summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/bitmap.h22
1 files changed, 17 insertions, 5 deletions
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 5f11fbdc27f8..1ee46f492267 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -302,12 +302,20 @@ static inline void bitmap_complement(unsigned long *dst, const unsigned long *sr
__bitmap_complement(dst, src, nbits);
}
+#ifdef __LITTLE_ENDIAN
+#define BITMAP_MEM_ALIGNMENT 8
+#else
+#define BITMAP_MEM_ALIGNMENT (8 * sizeof(unsigned long))
+#endif
+#define BITMAP_MEM_MASK (BITMAP_MEM_ALIGNMENT - 1)
+
static inline int bitmap_equal(const unsigned long *src1,
const unsigned long *src2, unsigned int nbits)
{
if (small_const_nbits(nbits))
return !((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits));
- if (__builtin_constant_p(nbits & 7) && IS_ALIGNED(nbits, 8))
+ if (__builtin_constant_p(nbits & BITMAP_MEM_MASK) &&
+ IS_ALIGNED(nbits, BITMAP_MEM_ALIGNMENT))
return !memcmp(src1, src2, nbits / 8);
return __bitmap_equal(src1, src2, nbits);
}
@@ -358,8 +366,10 @@ static __always_inline void bitmap_set(unsigned long *map, unsigned int start,
{
if (__builtin_constant_p(nbits) && nbits == 1)
__set_bit(start, map);
- else if (__builtin_constant_p(start & 7) && IS_ALIGNED(start, 8) &&
- __builtin_constant_p(nbits & 7) && IS_ALIGNED(nbits, 8))
+ else if (__builtin_constant_p(start & BITMAP_MEM_MASK) &&
+ IS_ALIGNED(start, BITMAP_MEM_ALIGNMENT) &&
+ __builtin_constant_p(nbits & BITMAP_MEM_MASK) &&
+ IS_ALIGNED(nbits, BITMAP_MEM_ALIGNMENT))
memset((char *)map + start / 8, 0xff, nbits / 8);
else
__bitmap_set(map, start, nbits);
@@ -370,8 +380,10 @@ static __always_inline void bitmap_clear(unsigned long *map, unsigned int start,
{
if (__builtin_constant_p(nbits) && nbits == 1)
__clear_bit(start, map);
- else if (__builtin_constant_p(start & 7) && IS_ALIGNED(start, 8) &&
- __builtin_constant_p(nbits & 7) && IS_ALIGNED(nbits, 8))
+ else if (__builtin_constant_p(start & BITMAP_MEM_MASK) &&
+ IS_ALIGNED(start, BITMAP_MEM_ALIGNMENT) &&
+ __builtin_constant_p(nbits & BITMAP_MEM_MASK) &&
+ IS_ALIGNED(nbits, BITMAP_MEM_ALIGNMENT))
memset((char *)map + start / 8, 0, nbits / 8);
else
__bitmap_clear(map, start, nbits);
repair2.c?h=bmm-3.12&id=d4bbf7e7759afc172e2bfbc5c416324590049cdd'>drivers/acpi/acpica/nsrepair2.c15
-rw-r--r--drivers/acpi/acpica/nsxfeval.c1
-rw-r--r--drivers/acpi/acpica/nsxfname.c1
-rw-r--r--drivers/acpi/acpica/nsxfobj.c1
-rw-r--r--drivers/acpi/acpica/rsxface.c1
-rw-r--r--drivers/acpi/acpica/tbinstal.c27
-rw-r--r--drivers/acpi/acpica/tbxface.c1
-rw-r--r--drivers/acpi/acpica/utdebug.c1
-rw-r--r--drivers/acpi/acpica/utdecode.c1
-rw-r--r--drivers/acpi/acpica/utglobal.c1
-rw-r--r--drivers/acpi/acpica/utxface.c1
-rw-r--r--drivers/acpi/acpica/utxferror.c1
-rw-r--r--drivers/acpi/apei/Kconfig11
-rw-r--r--drivers/acpi/apei/apei-base.c35
-rw-r--r--drivers/acpi/apei/apei-internal.h15
-rw-r--r--drivers/acpi/apei/einj.c43
-rw-r--r--drivers/acpi/apei/erst-dbg.c6
-rw-r--r--drivers/acpi/apei/erst.c40
-rw-r--r--drivers/acpi/apei/ghes.c451
-rw-r--r--drivers/acpi/apei/hest.c29
-rw-r--r--drivers/acpi/atomicio.c4
-rw-r--r--drivers/acpi/battery.c88
-rw-r--r--drivers/acpi/blacklist.c1
-rw-r--r--drivers/acpi/bus.c22
-rw-r--r--drivers/acpi/debugfs.c1
-rw-r--r--drivers/acpi/dock.c4
-rw-r--r--drivers/acpi/ec_sys.c3
-rw-r--r--drivers/acpi/event.c1
-rw-r--r--drivers/acpi/fan.c2
-rw-r--r--drivers/acpi/glue.c1
-rw-r--r--drivers/acpi/osl.c45
-rw-r--r--drivers/acpi/pci_irq.c58
-rw-r--r--drivers/acpi/pci_root.c3
-rw-r--r--drivers/acpi/proc.c1
-rw-r--r--drivers/acpi/processor_core.c1
-rw-r--r--drivers/acpi/processor_driver.c20
-rw-r--r--drivers/acpi/processor_idle.c266
-rw-r--r--drivers/acpi/processor_thermal.c2
-rw-r--r--drivers/acpi/sbs.c15
-rw-r--r--drivers/acpi/sbshc.c1
-rw-r--r--drivers/acpi/scan.c3
-rw-r--r--drivers/acpi/sleep.c48
-rw-r--r--drivers/acpi/sysfs.c18
-rw-r--r--drivers/acpi/thermal.c2
-rw-r--r--drivers/acpi/video.c9
-rw-r--r--drivers/acpi/video_detect.c1
-rw-r--r--drivers/amba/bus.c68
-rw-r--r--drivers/ata/Kconfig13
-rw-r--r--drivers/ata/Makefile2
-rw-r--r--drivers/ata/acard-ahci.c27
-rw-r--r--drivers/ata/ahci.c125
-rw-r--r--drivers/ata/ahci.h1
-rw-r--r--drivers/ata/ahci_platform.c55
-rw-r--r--drivers/ata/ata_generic.c5
-rw-r--r--drivers/ata/ata_piix.c197
-rw-r--r--drivers/ata/libahci.c152
-rw-r--r--drivers/ata/libata-acpi.c70
-rw-r--r--drivers/ata/libata-core.c403
-rw-r--r--drivers/ata/libata-eh.c170
-rw-r--r--drivers/ata/libata-pmp.c131
-rw-r--r--drivers/ata/libata-scsi.c65
-rw-r--r--drivers/ata/libata-sff.c173
-rw-r--r--drivers/ata/pata_acpi.c8
-rw-r--r--drivers/ata/pata_ali.c10
-rw-r--r--drivers/ata/pata_amd.c8
-rw-r--r--drivers/ata/pata_arasan_cf.c6
-rw-r--r--drivers/ata/pata_artop.c112
-rw-r--r--drivers/ata/pata_at91.c7
-rw-r--r--drivers/ata/pata_atiixp.c35
-rw-r--r--drivers/ata/pata_atp867x.c13
-rw-r--r--drivers/ata/pata_bf54x.c4
-rw-r--r--drivers/ata/pata_cmd64x.c44
-rw-r--r--drivers/ata/pata_cs5520.c3
-rw-r--r--drivers/ata/pata_cs5535.c16
-rw-r--r--drivers/ata/pata_efar.c27
-rw-r--r--drivers/ata/pata_hpt366.c35
-rw-r--r--drivers/ata/pata_hpt3x3.c4
-rw-r--r--drivers/ata/pata_icside.c4
-rw-r--r--drivers/ata/pata_imx.c253
-rw-r--r--drivers/ata/pata_it8213.c29
-rw-r--r--drivers/ata/pata_it821x.c16
-rw-r--r--drivers/ata/pata_ixp4xx_cf.c4
-rw-r--r--drivers/ata/pata_legacy.c131
-rw-r--r--drivers/ata/pata_macio.c10
-rw-r--r--drivers/ata/pata_mpc52xx.c2
-rw-r--r--drivers/ata/pata_mpiix.c4
-rw-r--r--drivers/ata/pata_netcell.c5
-rw-r--r--drivers/ata/pata_ns87410.c2
-rw-r--r--drivers/ata/pata_ns87415.c5
-rw-r--r--drivers/ata/pata_octeon_cf.c8
-rw-r--r--drivers/ata/pata_of_platform.c8
-rw-r--r--drivers/ata/pata_oldpiix.c5
-rw-r--r--drivers/ata/pata_opti.c4
-rw-r--r--drivers/ata/pata_optidma.c4
-rw-r--r--drivers/ata/pata_pcmcia.c5
-rw-r--r--drivers/ata/pata_pdc2027x.c36
-rw-r--r--drivers/ata/pata_platform.c2
-rw-r--r--drivers/ata/pata_qdi.c366
-rw-r--r--drivers/ata/pata_radisys.c5
-rw-r--r--drivers/ata/pata_rdc.c21
-rw-r--r--drivers/ata/pata_rz1000.c4
-rw-r--r--drivers/ata/pata_samsung_cf.c2
-rw-r--r--drivers/ata/pata_sc1200.c14
-rw-r--r--drivers/ata/pata_scc.c22
-rw-r--r--drivers/ata/pata_sch.c5
-rw-r--r--drivers/ata/pata_serverworks.c127
-rw-r--r--drivers/ata/pata_sil680.c98
-rw-r--r--drivers/ata/pata_sis.c159
-rw-r--r--drivers/ata/pata_sl82c105.c43
-rw-r--r--drivers/ata/pata_triflex.c4
-rw-r--r--drivers/ata/pata_via.c75
-rw-r--r--drivers/ata/pdc_adma.c10
-rw-r--r--drivers/ata/sata_dwc_460ex.c38
-rw-r--r--drivers/ata/sata_fsl.c43
-rw-r--r--drivers/ata/sata_inic162x.c31
-rw-r--r--drivers/ata/sata_mv.c110
-rw-r--r--drivers/ata/sata_nv.c79
-rw-r--r--drivers/ata/sata_promise.c6
-rw-r--r--drivers/ata/sata_qstor.c13
-rw-r--r--drivers/ata/sata_sil.c23
-rw-r--r--drivers/ata/sata_sil24.c47
-rw-r--r--drivers/ata/sata_sis.c28
-rw-r--r--drivers/ata/sata_svw.c4
-rw-r--r--drivers/ata/sata_sx4.c4
-rw-r--r--drivers/ata/sata_uli.c4
-rw-r--r--drivers/ata/sata_via.c42
-rw-r--r--drivers/ata/sata_vsc.c9
-rw-r--r--drivers/atm/ambassador.c13
-rw-r--r--drivers/atm/ambassador.h4
-rw-r--r--drivers/atm/atmtcp.c2
-rw-r--r--drivers/atm/eni.c7
-rw-r--r--drivers/atm/eni.h3
-rw-r--r--drivers/atm/firestream.c3
-rw-r--r--drivers/atm/fore200e.c2
-rw-r--r--drivers/atm/horizon.c3
-rw-r--r--drivers/atm/idt77252.c14
-rw-r--r--drivers/atm/iphase.c268
-rw-r--r--drivers/atm/iphase.h395
-rw-r--r--drivers/atm/lanai.c12
-rw-r--r--drivers/atm/nicstar.c2
-rw-r--r--drivers/atm/suni.c2
-rw-r--r--drivers/atm/uPD98402.c2
-rw-r--r--drivers/atm/zatm.c3
-rw-r--r--drivers/base/Kconfig66
-rw-r--r--drivers/base/Makefile2
-rw-r--r--drivers/base/base.h1
-rw-r--r--drivers/base/class.c17
-rw-r--r--drivers/base/core.c8
-rw-r--r--drivers/base/dd.c3
-rw-r--r--drivers/base/devres.c1
-rw-r--r--drivers/base/devtmpfs.c340
-rw-r--r--drivers/base/dma-coherent.c1
-rw-r--r--drivers/base/dma-mapping.c1
-rw-r--r--drivers/base/firmware_class.c11
-rw-r--r--drivers/base/hypervisor.c1
-rw-r--r--drivers/base/iommu.c124
-rw-r--r--drivers/base/memory.c65
-rw-r--r--drivers/base/node.c14
-rw-r--r--drivers/base/platform.c75
-rw-r--r--drivers/base/power/Makefile5
-rw-r--r--drivers/base/power/clock_ops.c315
-rw-r--r--drivers/base/power/common.c86
-rw-r--r--drivers/base/power/domain.c1328
-rw-r--r--drivers/base/power/generic_ops.c99
-rw-r--r--drivers/base/power/main.c109
-rw-r--r--drivers/base/power/opp.c49
-rw-r--r--drivers/base/power/power.h10
-rw-r--r--drivers/base/power/qos.c414
-rw-r--r--drivers/base/power/runtime.c232
-rw-r--r--drivers/base/power/sysfs.c9
-rw-r--r--drivers/base/power/trace.c3
-rw-r--r--drivers/base/power/wakeup.c5
-rw-r--r--drivers/base/regmap/Kconfig15
-rw-r--r--drivers/base/regmap/Makefile4
-rw-r--r--drivers/base/regmap/internal.h128
-rw-r--r--drivers/base/regmap/regcache-indexed.c64
-rw-r--r--drivers/base/regmap/regcache-lzo.c361
-rw-r--r--drivers/base/regmap/regcache-rbtree.c345
-rw-r--r--drivers/base/regmap/regcache.c402
-rw-r--r--drivers/base/regmap/regmap-debugfs.c209
-rw-r--r--drivers/base/regmap/regmap-i2c.c114
-rw-r--r--drivers/base/regmap/regmap-spi.c73
-rw-r--r--drivers/base/regmap/regmap.c589
-rw-r--r--drivers/base/transport_class.c1
-rw-r--r--drivers/bcma/Kconfig24
-rw-r--r--drivers/bcma/Makefile5
-rw-r--r--drivers/bcma/bcma_private.h27
-rw-r--r--drivers/bcma/core.c78
-rw-r--r--drivers/bcma/driver_chipcommon.c70
-rw-r--r--drivers/bcma/driver_chipcommon_pmu.c200
-rw-r--r--drivers/bcma/driver_mips.c256
-rw-r--r--drivers/bcma/driver_pci.c79
-rw-r--r--drivers/bcma/driver_pci_host.c14
-rw-r--r--drivers/bcma/host_pci.c54
-rw-r--r--drivers/bcma/host_soc.c183
-rw-r--r--drivers/bcma/main.c97
-rw-r--r--drivers/bcma/scan.c348
-rw-r--r--drivers/bcma/sprom.c186
-rw-r--r--drivers/block/Kconfig17
-rw-r--r--drivers/block/aoe/aoeblk.c15
-rw-r--r--drivers/block/aoe/aoechr.c1
-rw-r--r--drivers/block/brd.c4
-rw-r--r--drivers/block/cciss.c82
-rw-r--r--drivers/block/cciss.h3
-rw-r--r--drivers/block/cciss_scsi.c3
-rw-r--r--drivers/block/cpqarray.c2
-rw-r--r--drivers/block/drbd/drbd_bitmap.c5
-rw-r--r--drivers/block/drbd/drbd_int.h5
-rw-r--r--drivers/block/drbd/drbd_nl.c8
-rw-r--r--drivers/block/drbd/drbd_req.c8
-rw-r--r--drivers/block/floppy.c8
-rw-r--r--drivers/block/hd.c2
-rw-r--r--drivers/block/loop.c593
-rw-r--r--drivers/block/nbd.c69
-rw-r--r--drivers/block/paride/pg.c1
-rw-r--r--drivers/block/pktcdvd.c13
-rw-r--r--drivers/block/ps3disk.c1
-rw-r--r--drivers/block/ps3vram.c7
-rw-r--r--drivers/block/rbd.c50
-rw-r--r--drivers/block/swim3.c1
-rw-r--r--drivers/block/umem.c4
-rw-r--r--drivers/block/virtio_blk.c31
-rw-r--r--drivers/block/xen-blkback/blkback.c167
-rw-r--r--drivers/block/xen-blkback/common.h106
-rw-r--r--drivers/block/xen-blkback/xenbus.c138
-rw-r--r--drivers/block/xen-blkfront.c129
-rw-r--r--drivers/block/xsysace.c98
-rw-r--r--drivers/bluetooth/ath3k.c10
-rw-r--r--drivers/bluetooth/bcm203x.c12
-rw-r--r--drivers/bluetooth/bfusb.c13
-rw-r--r--drivers/bluetooth/btmrvl_main.c2
-rw-r--r--drivers/bluetooth/btmrvl_sdio.c5
-rw-r--r--drivers/bluetooth/btusb.c42
-rw-r--r--drivers/bluetooth/btwilink.c17
-rw-r--r--drivers/cdrom/cdrom.c8
-rw-r--r--drivers/char/Kconfig17
-rw-r--r--drivers/char/Makefile2
-rw-r--r--drivers/char/agp/backend.c3
-rw-r--r--drivers/char/agp/hp-agp.c6
-rw-r--r--drivers/char/agp/intel-agp.h7
-rw-r--r--drivers/char/agp/intel-gtt.c33
-rw-r--r--drivers/char/apm-emulation.c23
-rw-r--r--drivers/char/bsr.c2
-rw-r--r--drivers/char/generic_nvram.c4
-rw-r--r--drivers/char/hpet.c2
-rw-r--r--drivers/char/hw_random/Kconfig40
-rw-r--r--drivers/char/hw_random/Makefile2
-rw-r--r--drivers/char/hw_random/atmel-rng.c158
-rw-r--r--drivers/char/hw_random/core.c2
-rw-r--r--drivers/char/hw_random/n2-drv.c29
-rw-r--r--drivers/char/hw_random/n2rng.h2
-rw-r--r--drivers/char/hw_random/nomadik-rng.c3
-rw-r--r--drivers/char/hw_random/omap-rng.c6
-rw-r--r--drivers/char/hw_random/ppc4xx-rng.c156
-rw-r--r--drivers/char/hw_random/timeriomem-rng.c3
-rw-r--r--drivers/char/hw_random/virtio-rng.c1
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c35
-rw-r--r--drivers/char/mem.c1
-rw-r--r--drivers/char/msm_smd_pkt.c5
-rw-r--r--drivers/char/mspec.c2
-rw-r--r--drivers/char/nvram.c2
-rw-r--r--drivers/char/ps3flash.c14
-rw-r--r--drivers/char/ramoops.c102
-rw-r--r--drivers/char/random.c357
-rw-r--r--drivers/char/raw.c3
-rw-r--r--drivers/char/rtc.c7
-rw-r--r--drivers/char/scc.h613
-rw-r--r--drivers/char/tile-srom.c481
-rw-r--r--drivers/char/tpm/Kconfig1
-rw-r--r--drivers/char/tpm/tpm.c114
-rw-r--r--drivers/char/tpm/tpm.h7
-rw-r--r--drivers/char/tpm/tpm_nsc.c16
-rw-r--r--drivers/char/tpm/tpm_tis.c179
-rw-r--r--drivers/char/ttyprintk.c3
-rw-r--r--drivers/char/virtio_console.c121
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c2
-rw-r--r--drivers/clk/Kconfig3
-rw-r--r--drivers/clocksource/Kconfig27
-rw-r--r--drivers/clocksource/Makefile4
-rw-r--r--drivers/clocksource/clksrc-dbx500-prcmu.c106
-rw-r--r--drivers/clocksource/dw_apb_timer.c401
-rw-r--r--drivers/clocksource/i8253.c114
-rw-r--r--drivers/clocksource/sh_cmt.c35
-rw-r--r--drivers/clocksource/sh_mtu2.c1
-rw-r--r--drivers/clocksource/sh_tmu.c1
-rw-r--r--drivers/connector/cn_proc.c71
-rw-r--r--drivers/cpufreq/Kconfig10
-rw-r--r--drivers/cpufreq/Kconfig.arm32
-rw-r--r--drivers/cpufreq/Kconfig.powerpc7
-rw-r--r--drivers/cpufreq/Makefile12
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c2
-rw-r--r--drivers/cpufreq/cpufreq.c20
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c4
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c6
-rw-r--r--drivers/cpufreq/cpufreq_stats.c1
-rw-r--r--drivers/cpufreq/db8500-cpufreq.c40
-rw-r--r--drivers/cpufreq/e_powersaver.c135
-rw-r--r--drivers/cpufreq/exynos4210-cpufreq.c687
-rw-r--r--drivers/cpufreq/maple-cpufreq.c309
-rw-r--r--drivers/cpufreq/pcc-cpufreq.c3
-rw-r--r--drivers/cpufreq/s3c64xx-cpufreq.c274
-rw-r--r--drivers/cpufreq/s5pv210-cpufreq.c649
-rw-r--r--drivers/cpuidle/cpuidle.c139
-rw-r--r--drivers/cpuidle/cpuidle.h1
-rw-r--r--drivers/cpuidle/driver.c28
-rw-r--r--drivers/cpuidle/governor.c3
-rw-r--r--drivers/cpuidle/governors/ladder.c45
-rw-r--r--drivers/cpuidle/governors/menu.c32
-rw-r--r--drivers/cpuidle/sysfs.c22
-rw-r--r--drivers/crypto/Kconfig3
-rw-r--r--drivers/crypto/amcc/crypto4xx_core.c5
-rw-r--r--drivers/crypto/caam/caamalg.c1832
-rw-r--r--drivers/crypto/caam/compat.h1
-rw-r--r--drivers/crypto/caam/ctrl.c30
-rw-r--r--drivers/crypto/caam/desc_constr.h58
-rw-r--r--drivers/crypto/hifn_795x.c6
-rw-r--r--drivers/crypto/mv_cesa.c13
-rw-r--r--drivers/crypto/n2_core.c37
-rw-r--r--drivers/crypto/omap-sham.c180
-rw-r--r--drivers/crypto/padlock-aes.c4
-rw-r--r--drivers/crypto/picoxcell_crypto.c121
-rw-r--r--drivers/crypto/talitos.c65
-rw-r--r--drivers/dca/dca-core.c79
-rw-r--r--drivers/dca/dca-sysfs.c1
-rw-r--r--drivers/devfreq/Kconfig68
-rw-r--r--drivers/devfreq/Makefile5
-rw-r--r--drivers/devfreq/devfreq.c607
-rw-r--r--drivers/devfreq/governor.h24
-rw-r--r--drivers/devfreq/governor_performance.c29
-rw-r--r--drivers/devfreq/governor_powersave.c29
-rw-r--r--drivers/devfreq/governor_simpleondemand.c88
-rw-r--r--drivers/devfreq/governor_userspace.c116
-rw-r--r--drivers/dma/Kconfig10
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/TODO1
-rw-r--r--drivers/dma/amba-pl08x.c857
-rw-r--r--drivers/dma/at_hdmac.c168
-rw-r--r--drivers/dma/at_hdmac_regs.h24
-rw-r--r--drivers/dma/coh901318.c20
-rw-r--r--drivers/dma/dmaengine.c9
-rw-r--r--drivers/dma/dmatest.c24
-rw-r--r--drivers/dma/dw_dmac.c5
-rw-r--r--drivers/dma/ep93xx_dma.c1356
-rw-r--r--drivers/dma/imx-dma.c5
-rw-r--r--drivers/dma/imx-sdma.c141
-rw-r--r--drivers/dma/intel_mid_dma.c12
-rw-r--r--drivers/dma/ioat/dma_v3.c8
-rw-r--r--drivers/dma/ioat/pci.c11
-rw-r--r--drivers/dma/ipu/ipu_idmac.c73
-rw-r--r--drivers/dma/ipu/ipu_irq.c48
-rw-r--r--drivers/dma/mpc512x_dma.c1
-rw-r--r--drivers/dma/mv_xor.c4
-rw-r--r--drivers/dma/mxs-dma.c58
-rw-r--r--drivers/dma/pch_dma.c134
-rw-r--r--drivers/dma/pl330.c293
-rw-r--r--drivers/dma/shdma.c211
-rw-r--r--drivers/dma/shdma.h11
-rw-r--r--drivers/dma/ste_dma40.c314
-rw-r--r--drivers/dma/ste_dma40_ll.h3
-rw-r--r--drivers/dma/timb_dma.c5
-rw-r--r--drivers/edac/Kconfig16
-rw-r--r--drivers/edac/Makefile2
-rw-r--r--drivers/edac/amd64_edac.c37
-rw-r--r--drivers/edac/cell_edac.c2
-rw-r--r--drivers/edac/cpc925_edac.c67
-rw-r--r--drivers/edac/edac_core.h350
-rw-r--r--drivers/edac/edac_mce.c61
-rw-r--r--drivers/edac/edac_stub.c2
-rw-r--r--drivers/edac/i7300_edac.c51
-rw-r--r--drivers/edac/i7core_edac.c417
-rw-r--r--drivers/edac/mce_amd.c46
-rw-r--r--drivers/edac/mce_amd.h6
-rw-r--r--drivers/edac/mce_amd_inj.c1
-rw-r--r--drivers/edac/mpc85xx_edac.c18
-rw-r--r--drivers/edac/ppc4xx_edac.c2
-rw-r--r--drivers/edac/sb_edac.c1893
-rw-r--r--drivers/eisa/pci_eisa.c2
-rw-r--r--drivers/firewire/core-card.c2
-rw-r--r--drivers/firewire/core-cdev.c44
-rw-r--r--drivers/firewire/core-device.c17
-rw-r--r--drivers/firewire/core-iso.c1
-rw-r--r--drivers/firewire/core-topology.c2
-rw-r--r--drivers/firewire/core-transaction.c4
-rw-r--r--drivers/firewire/core.h2
-rw-r--r--drivers/firewire/net.c26
-rw-r--r--drivers/firewire/nosy.c2
-rw-r--r--drivers/firewire/ohci.c309
-rw-r--r--drivers/firewire/sbp2.c256
-rw-r--r--drivers/firmware/dmi_scan.c6
-rw-r--r--drivers/firmware/edd.c6
-rw-r--r--drivers/firmware/efivars.c244
-rw-r--r--drivers/firmware/google/gsmi.c22
-rw-r--r--drivers/firmware/iscsi_ibft.c14
-rw-r--r--drivers/firmware/sigma.c3
-rw-r--r--drivers/gpio/Kconfig108
-rw-r--r--drivers/gpio/Makefile100
-rw-r--r--drivers/gpio/gpio-74x164.c (renamed from drivers/gpio/74x164.c)34
-rw-r--r--drivers/gpio/gpio-ab8500.c (renamed from drivers/gpio/ab8500-gpio.c)3
-rw-r--r--drivers/gpio/gpio-adp5520.c (renamed from drivers/gpio/adp5520-gpio.c)0
-rw-r--r--drivers/gpio/gpio-adp5588.c (renamed from drivers/gpio/adp5588-gpio.c)0
-rw-r--r--drivers/gpio/gpio-bt8xx.c (renamed from drivers/gpio/bt8xxgpio.c)2
-rw-r--r--drivers/gpio/gpio-cs5535.c (renamed from drivers/gpio/cs5535-gpio.c)0
-rw-r--r--drivers/gpio/gpio-da9052.c277
-rw-r--r--drivers/gpio/gpio-davinci.c455
-rw-r--r--drivers/gpio/gpio-ep93xx.c426
-rw-r--r--drivers/gpio/gpio-exynos4.c386
-rw-r--r--drivers/gpio/gpio-generic.c (renamed from drivers/gpio/basic_mmio_gpio.c)21
-rw-r--r--drivers/gpio/gpio-it8761e.c (renamed from drivers/gpio/it8761e_gpio.c)2
-rw-r--r--drivers/gpio/gpio-janz-ttl.c (renamed from drivers/gpio/janz-ttl.c)0
-rw-r--r--drivers/gpio/gpio-ks8695.c319
-rw-r--r--drivers/gpio/gpio-langwell.c (renamed from drivers/gpio/langwell_gpio.c)31
-rw-r--r--drivers/gpio/gpio-lpc32xx.c446
-rw-r--r--drivers/gpio/gpio-max7300.c (renamed from drivers/gpio/max7300.c)2
-rw-r--r--drivers/gpio/gpio-max7301.c (renamed from drivers/gpio/max7301.c)2
-rw-r--r--drivers/gpio/gpio-max730x.c (renamed from drivers/gpio/max730x.c)2
-rw-r--r--drivers/gpio/gpio-max732x.c (renamed from drivers/gpio/max732x.c)2
-rw-r--r--drivers/gpio/gpio-mc33880.c (renamed from drivers/gpio/mc33880.c)3
-rw-r--r--drivers/gpio/gpio-mcp23s08.c (renamed from drivers/gpio/mcp23s08.c)292
-rw-r--r--drivers/gpio/gpio-ml-ioh.c (renamed from drivers/gpio/ml_ioh_gpio.c)257
-rw-r--r--drivers/gpio/gpio-mpc5200.c377
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c398
-rw-r--r--drivers/gpio/gpio-msm-v1.c636
-rw-r--r--drivers/gpio/gpio-msm-v2.c433
-rw-r--r--drivers/gpio/gpio-mxc.c479
-rw-r--r--drivers/gpio/gpio-mxs.c292
-rw-r--r--drivers/gpio/gpio-nomadik.c130
-rw-r--r--drivers/gpio/gpio-omap.c784
-rw-r--r--drivers/gpio/gpio-pca953x.c (renamed from drivers/gpio/pca953x.c)113
-rw-r--r--drivers/gpio/gpio-pcf857x.c (renamed from drivers/gpio/pcf857x.c)3
-rw-r--r--drivers/gpio/gpio-pch.c (renamed from drivers/gpio/pch_gpio.c)251
-rw-r--r--drivers/gpio/gpio-pl061.c (renamed from drivers/gpio/pl061.c)35
-rw-r--r--drivers/gpio/gpio-plat-samsung.c206
-rw-r--r--drivers/gpio/gpio-pxa.c338
-rw-r--r--drivers/gpio/gpio-rdc321x.c (renamed from drivers/gpio/rdc321x-gpio.c)0
-rw-r--r--drivers/gpio/gpio-s5pc100.c355
-rw-r--r--drivers/gpio/gpio-s5pv210.c288
-rw-r--r--drivers/gpio/gpio-sa1100.c63
-rw-r--r--drivers/gpio/gpio-samsung.c2712
-rw-r--r--drivers/gpio/gpio-sch.c (renamed from drivers/gpio/sch_gpio.c)2
-rw-r--r--drivers/gpio/gpio-stmpe.c (renamed from drivers/gpio/stmpe-gpio.c)0
-rw-r--r--drivers/gpio/gpio-sx150x.c (renamed from drivers/gpio/sx150x.c)0
-rw-r--r--drivers/gpio/gpio-tc3589x.c (renamed from drivers/gpio/tc3589x-gpio.c)0
-rw-r--r--drivers/gpio/gpio-tegra.c497
-rw-r--r--drivers/gpio/gpio-timberdale.c (renamed from drivers/gpio/timbgpio.c)2
-rw-r--r--drivers/gpio/gpio-tnetv107x.c205
-rw-r--r--drivers/gpio/gpio-tps65910.c (renamed from drivers/gpio/tps65910-gpio.c)2
-rw-r--r--drivers/gpio/gpio-tps65912.c156
-rw-r--r--drivers/gpio/gpio-twl4030.c (renamed from drivers/gpio/twl4030-gpio.c)2
-rw-r--r--drivers/gpio/gpio-u300.c1197
-rw-r--r--drivers/gpio/gpio-ucb1400.c (renamed from drivers/gpio/ucb1400_gpio.c)0
-rw-r--r--drivers/gpio/gpio-vr41xx.c (renamed from drivers/gpio/vr41xx_giu.c)2
-rw-r--r--drivers/gpio/gpio-vx855.c (renamed from drivers/gpio/vx855_gpio.c)0
-rw-r--r--drivers/gpio/gpio-wm831x.c (renamed from drivers/gpio/wm831x-gpio.c)3
-rw-r--r--drivers/gpio/gpio-wm8350.c (renamed from drivers/gpio/wm8350-gpiolib.c)2
-rw-r--r--drivers/gpio/gpio-wm8994.c (renamed from drivers/gpio/wm8994-gpio.c)2
-rw-r--r--drivers/gpio/gpio-xilinx.c (renamed from drivers/gpio/xilinx_gpio.c)1
-rw-r--r--drivers/gpu/drm/Kconfig6
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/ati_pcigart.c1
-rw-r--r--drivers/gpu/drm/drm_buffer.c1
-rw-r--r--drivers/gpu/drm/drm_bufs.c1
-rw-r--r--drivers/gpu/drm/drm_cache.c1
-rw-r--r--drivers/gpu/drm/drm_crtc.c19
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c23
-rw-r--r--drivers/gpu/drm/drm_debugfs.c16
-rw-r--r--drivers/gpu/drm/drm_dma.c1
-rw-r--r--drivers/gpu/drm/drm_drv.c5
-rw-r--r--drivers/gpu/drm/drm_edid.c209
-rw-r--r--drivers/gpu/drm/drm_encoder_slave.c2
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c2
-rw-r--r--drivers/gpu/drm/drm_fops.c1
-rw-r--r--drivers/gpu/drm/drm_gem.c135
-rw-r--r--drivers/gpu/drm/drm_hashtab.c1
-rw-r--r--drivers/gpu/drm/drm_ioc32.c1
-rw-r--r--drivers/gpu/drm/drm_irq.c58
-rw-r--r--drivers/gpu/drm/drm_memory.c1
-rw-r--r--drivers/gpu/drm/drm_mm.c1
-rw-r--r--drivers/gpu/drm/drm_modes.c88
-rw-r--r--drivers/gpu/drm/drm_pci.c1
-rw-r--r--drivers/gpu/drm/drm_platform.c6
-rw-r--r--drivers/gpu/drm/drm_proc.c4
-rw-r--r--drivers/gpu/drm/drm_scatter.c10
-rw-r--r--drivers/gpu/drm/drm_sman.c1
-rw-r--r--drivers/gpu/drm/drm_sysfs.c1
-rw-r--r--drivers/gpu/drm/drm_usb.c1
-rw-r--r--drivers/gpu/drm/drm_vm.c1
-rw-r--r--drivers/gpu/drm/exynos/Kconfig20
-rw-r--r--drivers/gpu/drm/exynos/Makefile11
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_buf.c110
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_buf.h53
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_connector.c293
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_connector.h34
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_core.c272
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c381
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.h38
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c244
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h254
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.c271
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.h45
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c265
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.h37
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c456
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.h37
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c811
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c415
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.h107
-rw-r--r--drivers/gpu/drm/i2c/ch7006_drv.c2
-rw-r--r--drivers/gpu/drm/i2c/sil164_drv.c2
-rw-r--r--drivers/gpu/drm/i810/i810_drv.c2
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7017.c2
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7xxx.c4
-rw-r--r--drivers/gpu/drm/i915/dvo_ivch.c6
-rw-r--r--drivers/gpu/drm/i915/dvo_sil164.c2
-rw-r--r--drivers/gpu/drm/i915/dvo_tfp410.c14
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c317
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c73
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c134
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h172
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c389
-rw-r--r--drivers/gpu/drm/i915/i915_gem_debug.c8
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.c2
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c3
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.c69
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c19
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c65
-rw-r--r--drivers/gpu/drm/i915/i915_mem.c14
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h191
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c23
-rw-r--r--drivers/gpu/drm/i915/i915_trace.h46
-rw-r--r--drivers/gpu/drm/i915/intel_acpi.c2
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c165
-rw-r--r--drivers/gpu/drm/i915/intel_bios.h22
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c20
-rw-r--r--drivers/gpu/drm/i915/intel_display.c1740
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c1069
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h57
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c185
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c9
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c98
-rw-r--r--drivers/gpu/drm/i915/intel_modes.c2
-rw-r--r--drivers/gpu/drm/i915/intel_opregion.c106
-rw-r--r--drivers/gpu/drm/i915/intel_overlay.c152
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c102
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c382
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h18
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c338
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo_regs.h558
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c104
-rw-r--r--drivers/gpu/drm/mga/mga_drv.c2
-rw-r--r--drivers/gpu/drm/mga/mga_warp.c1
-rw-r--r--drivers/gpu/drm/nouveau/Makefile9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_backlight.c169
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c347
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c236
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_channel.c184
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c88
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_crtc.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.c7
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c963
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.c26
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h199
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h27
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fb.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c26
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.c36
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c148
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.c60
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_irq.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c403
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mm.c88
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mm.h6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_notifier.c20
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_object.c100
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_perf.c118
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.c100
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.h15
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_reg.h11
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sgdma.c71
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c325
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_temp.c17
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vm.c61
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vm.h4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_volt.c46
-rw-r--r--drivers/gpu/drm/nouveau/nv04_crtc.c17
-rw-r--r--drivers/gpu/drm/nouveau/nv04_display.c23
-rw-r--r--drivers/gpu/drm/nouveau/nv04_graph.c22
-rw-r--r--drivers/gpu/drm/nouveau/nv04_instmem.c28
-rw-r--r--drivers/gpu/drm/nouveau/nv04_pm.c4
-rw-r--r--drivers/gpu/drm/nouveau/nv04_timer.c84
-rw-r--r--drivers/gpu/drm/nouveau/nv10_graph.c15
-rw-r--r--drivers/gpu/drm/nouveau/nv20_graph.c13
-rw-r--r--drivers/gpu/drm/nouveau/nv31_mpeg.c (renamed from drivers/gpu/drm/nouveau/nv40_mpeg.c)91
-rw-r--r--drivers/gpu/drm/nouveau/nv40_graph.c112
-rw-r--r--drivers/gpu/drm/nouveau/nv40_pm.c348
-rw-r--r--drivers/gpu/drm/nouveau/nv50_crtc.c24
-rw-r--r--drivers/gpu/drm/nouveau/nv50_cursor.c18
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c82
-rw-r--r--drivers/gpu/drm/nouveau/nv50_evo.c10
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fbcon.c10
-rw-r--r--drivers/gpu/drm/nouveau/nv50_gpio.c31
-rw-r--r--drivers/gpu/drm/nouveau/nv50_graph.c128
-rw-r--r--drivers/gpu/drm/nouveau/nv50_grctx.c13
-rw-r--r--drivers/gpu/drm/nouveau/nv50_instmem.c8
-rw-r--r--drivers/gpu/drm/nouveau/nv50_mpeg.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv50_pm.c8
-rw-r--r--drivers/gpu/drm/nouveau/nv50_sor.c40
-rw-r--r--drivers/gpu/drm/nouveau/nv50_vm.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv50_vram.c43
-rw-r--r--drivers/gpu/drm/nouveau/nv84_crypt.c2
-rw-r--r--drivers/gpu/drm/nouveau/nva3_copy.c2
-rw-r--r--drivers/gpu/drm/nouveau/nva3_pm.c402
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_copy.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fb.c95
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fbcon.c14
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fifo.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_graph.c348
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_graph.fuc400
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_graph.h25
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_grctx.c275
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_grgpc.fuc480
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h486
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_grhub.fuc811
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h838
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_instmem.c14
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_pm.c155
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_vm.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_vram.c60
-rw-r--r--drivers/gpu/drm/nouveau/nvd0_display.c1473
-rw-r--r--drivers/gpu/drm/r128/r128_cce.c1
-rw-r--r--drivers/gpu/drm/r128/r128_drv.c2
-rw-r--r--drivers/gpu/drm/radeon/Makefile3
-rw-r--r--drivers/gpu/drm/radeon/atom.c18
-rw-r--r--drivers/gpu/drm/radeon/atom.h1
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c69
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c84
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c2369
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c207
-rw-r--r--drivers/gpu/drm/radeon/evergreen_blit_kms.c371
-rw-r--r--drivers/gpu/drm/radeon/evergreen_cs.c183
-rw-r--r--drivers/gpu/drm/radeon/evergreend.h44
-rw-r--r--drivers/gpu/drm/radeon/ni.c96
-rw-r--r--drivers/gpu/drm/radeon/r100.c179
-rw-r--r--drivers/gpu/drm/radeon/r100_track.h110
-rw-r--r--drivers/gpu/drm/radeon/r200.c4
-rw-r--r--drivers/gpu/drm/radeon/r300.c115
-rw-r--r--drivers/gpu/drm/radeon/r300_cmdbuf.c2
-rw-r--r--drivers/gpu/drm/radeon/r600.c287
-rw-r--r--drivers/gpu/drm/radeon/r600_blit.c24
-rw-r--r--drivers/gpu/drm/radeon/r600_blit_kms.c361
-rw-r--r--drivers/gpu/drm/radeon/r600_cp.c25
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c79
-rw-r--r--drivers/gpu/drm/radeon/r600d.h22
-rw-r--r--drivers/gpu/drm/radeon/radeon.h239
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c32
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h21
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c308
-rw-r--r--drivers/gpu/drm/radeon/radeon_benchmark.c247
-rw-r--r--drivers/gpu/drm/radeon/radeon_bios.c21
-rw-r--r--drivers/gpu/drm/radeon/radeon_clocks.c7
-rw-r--r--drivers/gpu/drm/radeon/radeon_combios.c63
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c115
-rw-r--r--drivers/gpu/drm/radeon/radeon_cp.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c25
-rw-r--r--drivers/gpu/drm/radeon/radeon_cursor.c40
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c49
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c88
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon_encoders.c2123
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_gart.c75
-rw-r--r--drivers/gpu/drm/radeon/radeon_i2c.c76
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq_kms.c60
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c15
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_tv.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_mem.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h10
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c41
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.h42
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c21
-rw-r--r--drivers/gpu/drm/radeon/radeon_reg.h4
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c38
-rw-r--r--drivers/gpu/drm/radeon/radeon_state.c16
-rw-r--r--drivers/gpu/drm/radeon/radeon_test.c34
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c30
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/cayman2
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/evergreen3
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/r6001
-rw-r--r--drivers/gpu/drm/radeon/rs400.c8
-rw-r--r--drivers/gpu/drm/radeon/rs600.c28
-rw-r--r--drivers/gpu/drm/radeon/rv770.c140
-rw-r--r--drivers/gpu/drm/savage/savage_drv.c2
-rw-r--r--drivers/gpu/drm/sis/sis_drv.c2
-rw-r--r--drivers/gpu/drm/sis/sis_drv.h4
-rw-r--r--drivers/gpu/drm/tdfx/tdfx_drv.c2
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c26
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c4
-rw-r--r--drivers/gpu/drm/ttm/ttm_lock.c2
-rw-r--r--drivers/gpu/drm/ttm/ttm_object.c2
-rw-r--r--drivers/gpu/drm/ttm/ttm_page_alloc.c18
-rw-r--r--drivers/gpu/drm/ttm/ttm_tt.c1
-rw-r--r--drivers/gpu/drm/via/via_drv.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/Kconfig9
-rw-r--r--drivers/gpu/drm/vmwgfx/Makefile3
-rw-r--r--drivers/gpu/drm/vmwgfx/svga3d_reg.h259
-rw-r--r--drivers/gpu/drm/vmwgfx/svga_escape.h2
-rw-r--r--drivers/gpu/drm/vmwgfx/svga_overlay.h22
-rw-r--r--drivers/gpu/drm/vmwgfx/svga_reg.h304
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c73
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c322
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c279
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h229
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c870
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fb.c76
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c1135
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.h113
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c156
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c81
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c46
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c273
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_irq.c187
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c1174
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.h50
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c310
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_marker.c171
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c191
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c1043
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c537
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c5
-rw-r--r--drivers/gpu/vga/vgaarb.c65
-rw-r--r--drivers/hid/Kconfig87
-rw-r--r--drivers/hid/Makefile7
-rw-r--r--drivers/hid/hid-apple.c27
-rw-r--r--drivers/hid/hid-axff.c38
-rw-r--r--drivers/hid/hid-core.c102
-rw-r--r--drivers/hid/hid-debug.c6
-rw-r--r--drivers/hid/hid-dr.c1
-rw-r--r--drivers/hid/hid-emsff.c8
-rw-r--r--drivers/hid/hid-gaff.c1
-rw-r--r--drivers/hid/hid-holtekff.c241
-rw-r--r--drivers/hid/hid-ids.h47
-rw-r--r--drivers/hid/hid-input.c11
-rw-r--r--drivers/hid/hid-lg.c101
-rw-r--r--drivers/hid/hid-lg.h4
-rw-r--r--drivers/hid/hid-lg4ff.c403
-rw-r--r--drivers/hid/hid-lgff.c13
-rw-r--r--drivers/hid/hid-logitech-dj.c922
-rw-r--r--drivers/hid/hid-logitech-dj.h123
-rw-r--r--drivers/hid/hid-magicmouse.c73
-rw-r--r--drivers/hid/hid-microsoft.c28
-rw-r--r--drivers/hid/hid-multitouch.c50
-rw-r--r--drivers/hid/hid-picolcd.c3
-rw-r--r--drivers/hid/hid-pl.c1
-rw-r--r--drivers/hid/hid-primax.c117
-rw-r--r--drivers/hid/hid-prodikeys.c25
-rw-r--r--drivers/hid/hid-roccat-arvo.c21
-rw-r--r--drivers/hid/hid-roccat-arvo.h13
-rw-r--r--drivers/hid/hid-roccat-common.c21
-rw-r--r--drivers/hid/hid-roccat-common.h4
-rw-r--r--drivers/hid/hid-roccat-kone.c119
-rw-r--r--drivers/hid/hid-roccat-kone.h2
-rw-r--r--drivers/hid/hid-roccat-koneplus.c49
-rw-r--r--drivers/hid/hid-roccat-koneplus.h23
-rw-r--r--drivers/hid/hid-roccat-kovaplus.c42
-rw-r--r--drivers/hid/hid-roccat-kovaplus.h9
-rw-r--r--drivers/hid/hid-roccat-pyra.c48
-rw-r--r--drivers/hid/hid-roccat-pyra.h9
-rw-r--r--drivers/hid/hid-roccat.c19
-rw-r--r--drivers/hid/hid-sjoy.c76
-rw-r--r--drivers/hid/hid-sony.c35
-rw-r--r--drivers/hid/hid-speedlink.c89
-rw-r--r--drivers/hid/hid-tmff.c1
-rw-r--r--drivers/hid/hid-uclogic.c195
-rw-r--r--drivers/hid/hid-wacom.c101
-rw-r--r--drivers/hid/hid-wiimote.c1346
-rw-r--r--drivers/hid/hid-zpff.c1
-rw-r--r--drivers/hid/hid-zydacron.c4
-rw-r--r--drivers/hid/hidraw.c11
-rw-r--r--drivers/hid/usbhid/hid-core.c11
-rw-r--r--drivers/hid/usbhid/hid-quirks.c5
-rw-r--r--drivers/hid/usbhid/hiddev.c2
-rw-r--r--drivers/hv/Kconfig14
-rw-r--r--drivers/hv/Makefile7
-rw-r--r--drivers/hv/channel.c (renamed from drivers/staging/hv/channel.c)229
-rw-r--r--drivers/hv/channel_mgmt.c (renamed from drivers/staging/hv/channel_mgmt.c)198
-rw-r--r--drivers/hv/connection.c (renamed from drivers/staging/hv/connection.c)106
-rw-r--r--drivers/hv/hv.c (renamed from drivers/staging/hv/hv.c)29
-rw-r--r--drivers/hv/hv_kvp.c (renamed from drivers/staging/hv/hv_kvp.c)49
-rw-r--r--drivers/hv/hv_kvp.h (renamed from drivers/staging/hv/hv_kvp.h)2
-rw-r--r--drivers/hv/hv_util.c (renamed from drivers/staging/hv/hv_util.c)210
-rw-r--r--drivers/hv/hyperv_vmbus.h (renamed from drivers/staging/hv/hyperv_vmbus.h)19
-rw-r--r--drivers/hv/ring_buffer.c (renamed from drivers/staging/hv/ring_buffer.c)48
-rw-r--r--drivers/hv/vmbus_drv.c (renamed from drivers/staging/hv/vmbus_drv.c)586
-rw-r--r--drivers/hwmon/Kconfig189
-rw-r--r--drivers/hwmon/Makefile17
-rw-r--r--drivers/hwmon/ad7314.c186
-rw-r--r--drivers/hwmon/ad7414.c7
-rw-r--r--drivers/hwmon/ad7418.c27
-rw-r--r--drivers/hwmon/adm1275.c131
-rw-r--r--drivers/hwmon/ads1015.c21
-rw-r--r--drivers/hwmon/ads7828.c12
-rw-r--r--drivers/hwmon/asb100.c10
-rw-r--r--drivers/hwmon/asus_atk0110.c1
-rw-r--r--drivers/hwmon/coretemp.c178
-rw-r--r--drivers/hwmon/ds1621.c24
-rw-r--r--drivers/hwmon/ds620.c44
-rw-r--r--drivers/hwmon/exynos4_tmu.c524
-rw-r--r--drivers/hwmon/f71882fg.c231
-rw-r--r--drivers/hwmon/gl518sm.c4
-rw-r--r--drivers/hwmon/gl520sm.c6
-rw-r--r--drivers/hwmon/hwmon-vid.c42
-rw-r--r--drivers/hwmon/hwmon.c32
-rw-r--r--drivers/hwmon/i5k_amb.c42
-rw-r--r--drivers/hwmon/ibmaem.c120
-rw-r--r--drivers/hwmon/it87.c31
-rw-r--r--drivers/hwmon/jc42.c52
-rw-r--r--drivers/hwmon/lm73.c30
-rw-r--r--drivers/hwmon/lm75.c48
-rw-r--r--drivers/hwmon/lm77.c4
-rw-r--r--drivers/hwmon/lm78.c305
-rw-r--r--drivers/hwmon/lm90.c201
-rw-r--r--drivers/hwmon/lm92.c26
-rw-r--r--drivers/hwmon/lm95241.c31
-rw-r--r--drivers/hwmon/lm95245.c543
-rw-r--r--drivers/hwmon/max1111.c38
-rw-r--r--drivers/hwmon/max16065.c6
-rw-r--r--drivers/hwmon/max1668.c502
-rw-r--r--drivers/hwmon/mc13783-adc.c12
-rw-r--r--drivers/hwmon/ntc_thermistor.c452
-rw-r--r--drivers/hwmon/pmbus/Kconfig122
-rw-r--r--drivers/hwmon/pmbus/Makefile15
-rw-r--r--drivers/hwmon/pmbus/adm1275.c314
-rw-r--r--drivers/hwmon/pmbus/lm25066.c351
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c408
-rw-r--r--drivers/hwmon/pmbus/max16064.c (renamed from drivers/hwmon/max16064.c)60
-rw-r--r--drivers/hwmon/pmbus/max34440.c (renamed from drivers/hwmon/max34440.c)94
-rw-r--r--drivers/hwmon/pmbus/max8688.c (renamed from drivers/hwmon/max8688.c)76
-rw-r--r--drivers/hwmon/pmbus/pmbus.c (renamed from drivers/hwmon/pmbus.c)47
-rw-r--r--drivers/hwmon/pmbus/pmbus.h (renamed from drivers/hwmon/pmbus.h)68
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c (renamed from drivers/hwmon/pmbus_core.c)467
-rw-r--r--drivers/hwmon/pmbus/ucd9000.c (renamed from drivers/hwmon/ucd9000.c)19
-rw-r--r--drivers/hwmon/pmbus/ucd9200.c (renamed from drivers/hwmon/ucd9200.c)11
-rw-r--r--drivers/hwmon/pmbus/zl6100.c256
-rw-r--r--drivers/hwmon/sch5627.c334
-rw-r--r--drivers/hwmon/sch5636.c539
-rw-r--r--drivers/hwmon/sch56xx-common.c340
-rw-r--r--drivers/hwmon/sch56xx-common.h24
-rw-r--r--drivers/hwmon/sht15.c4
-rw-r--r--drivers/hwmon/sht21.c26
-rw-r--r--drivers/hwmon/smm665.c15
-rw-r--r--drivers/hwmon/smsc47b397.c13
-rw-r--r--drivers/hwmon/tmp102.c44
-rw-r--r--drivers/hwmon/ultra45_env.c1
-rw-r--r--drivers/hwmon/via-cputemp.c44
-rw-r--r--drivers/hwmon/w83627ehf.c465
-rw-r--r--drivers/hwmon/w83781d.c10
-rw-r--r--drivers/hwmon/w83791d.c4
-rw-r--r--drivers/hwspinlock/Kconfig27
-rw-r--r--drivers/hwspinlock/Makefile1
-rw-r--r--drivers/hwspinlock/hwspinlock_core.c204
-rw-r--r--drivers/hwspinlock/hwspinlock_internal.h40
-rw-r--r--drivers/hwspinlock/omap_hwspinlock.c127
-rw-r--r--drivers/hwspinlock/u8500_hsem.c197
-rw-r--r--drivers/i2c/Kconfig2
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c28
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c6
-rw-r--r--drivers/i2c/busses/Kconfig21
-rw-r--r--drivers/i2c/busses/Makefile5
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c61
-rw-r--r--drivers/i2c/busses/i2c-au1550.c282
-rw-r--r--drivers/i2c/busses/i2c-bfin-twi.c4
-rw-r--r--drivers/i2c/busses/i2c-cpm.c7
-rw-r--r--drivers/i2c/busses/i2c-designware-core.c (renamed from drivers/i2c/busses/i2c-designware.c)384
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h105
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c392
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c215
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c311
-rw-r--r--drivers/i2c/busses/i2c-highlander.c4
-rw-r--r--drivers/i2c/busses/i2c-imx.c46
-rw-r--r--drivers/i2c/busses/i2c-ixp2000.c2
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c96
-rw-r--r--drivers/i2c/busses/i2c-nuc900.c2
-rw-r--r--drivers/i2c/busses/i2c-omap.c157
-rw-r--r--drivers/i2c/busses/i2c-pmcmsp.c2
-rw-r--r--drivers/i2c/busses/i2c-pxa-pci.c6
-rw-r--r--drivers/i2c/busses/i2c-pxa.c7
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c130
-rw-r--r--drivers/i2c/busses/i2c-s6000.c5
-rw-r--r--drivers/i2c/busses/i2c-sh7760.c3
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c2
-rw-r--r--drivers/i2c/busses/i2c-stu300.c2
-rw-r--r--drivers/i2c/busses/i2c-tegra.c80
-rw-r--r--drivers/i2c/busses/scx200_acb.c6
-rw-r--r--drivers/i2c/i2c-boardinfo.c1
-rw-r--r--drivers/i2c/i2c-core.c9
-rw-r--r--drivers/i2c/i2c-dev.c2
-rw-r--r--drivers/ide/Kconfig32
-rw-r--r--drivers/ide/at91_ide.c2
-rw-r--r--drivers/ide/au1xxx-ide.c46
-rw-r--r--drivers/ide/buddha.c1
-rw-r--r--drivers/ide/cmd640.c1
-rw-r--r--drivers/ide/cy82c693.c6
-rw-r--r--drivers/ide/icside.c2
-rw-r--r--drivers/ide/ide-acpi.c1
-rw-r--r--drivers/ide/ide-atapi.c1
-rw-r--r--drivers/ide/ide-cd.c1
-rw-r--r--drivers/ide/ide-disk.c7
-rw-r--r--drivers/ide/ide-disk_proc.c1
-rw-r--r--drivers/ide/ide-dma-sff.c1
-rw-r--r--drivers/ide/ide-dma.c1
-rw-r--r--drivers/ide/ide-eh.c1
-rw-r--r--drivers/ide/ide-floppy.c1
-rw-r--r--drivers/ide/ide-floppy_proc.c1
-rw-r--r--drivers/ide/ide-io-std.c1
-rw-r--r--drivers/ide/ide-ioctls.c1
-rw-r--r--drivers/ide/ide-legacy.c1
-rw-r--r--drivers/ide/ide-lib.c1
-rw-r--r--drivers/ide/ide-pnp.c1
-rw-r--r--drivers/ide/ide-tape.c1
-rw-r--r--drivers/ide/ide-taskfile.c1
-rw-r--r--drivers/ide/ide-xfer-mode.c1
-rw-r--r--drivers/ide/ide_platform.c6
-rw-r--r--drivers/ide/macide.c1
-rw-r--r--drivers/ide/palm_bk3710.c2
-rw-r--r--drivers/ide/piix.c18
-rw-r--r--drivers/ide/pmac.c1
-rw-r--r--drivers/ide/q40ide.c1
-rw-r--r--drivers/ide/setup-pci.c1
-rw-r--r--drivers/ide/tc86c001.c1
-rw-r--r--drivers/ide/triflex.c16
-rw-r--r--drivers/ide/tx4939ide.c4
-rw-r--r--drivers/idle/intel_idle.c131
-rw-r--r--drivers/ieee802154/Makefile2
-rw-r--r--drivers/ieee802154/fakehard.c2
-rw-r--r--drivers/infiniband/core/addr.c8
-rw-r--r--drivers/infiniband/core/cache.c3
-rw-r--r--drivers/infiniband/core/cm.c64
-rw-r--r--drivers/infiniband/core/cm_msgs.h32
-rw-r--r--drivers/infiniband/core/cma.c152
-rw-r--r--drivers/infiniband/core/device.c6
-rw-r--r--drivers/infiniband/core/fmr_pool.c1
-rw-r--r--drivers/infiniband/core/iwcm.c1
-rw-r--r--drivers/infiniband/core/mad.c4
-rw-r--r--drivers/infiniband/core/multicast.c1
-rw-r--r--drivers/infiniband/core/netlink.c3
-rw-r--r--drivers/infiniband/core/packer.c1
-rw-r--r--drivers/infiniband/core/sysfs.c27
-rw-r--r--drivers/infiniband/core/ucm.c2
-rw-r--r--drivers/infiniband/core/ucma.c8
-rw-r--r--drivers/infiniband/core/ud_header.c1
-rw-r--r--drivers/infiniband/core/umem.c7
-rw-r--r--drivers/infiniband/core/user_mad.c5
-rw-r--r--drivers/infiniband/core/uverbs.h18
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c703
-rw-r--r--drivers/infiniband/core/uverbs_main.c30
-rw-r--r--drivers/infiniband/core/uverbs_marshall.c1
-rw-r--r--drivers/infiniband/core/verbs.c377
-rw-r--r--drivers/infiniband/hw/amso1100/c2.c12
-rw-r--r--drivers/infiniband/hw/amso1100/c2_ae.c5
-rw-r--r--drivers/infiniband/hw/amso1100/c2_intr.c5
-rw-r--r--drivers/infiniband/hw/amso1100/c2_provider.c14
-rw-r--r--drivers/infiniband/hw/cxgb3/Makefile2
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_cm.c32
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_ev.c6
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.c9
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.h1
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_qp.c14
-rw-r--r--drivers/infiniband/hw/cxgb4/Makefile2
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c511
-rw-r--r--drivers/infiniband/hw/cxgb4/cq.c3
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c41
-rw-r--r--drivers/infiniband/hw/cxgb4/ev.c10
-rw-r--r--drivers/infiniband/hw/cxgb4/iw_cxgb4.h23
-rw-r--r--drivers/infiniband/hw/cxgb4/mem.c2
-rw-r--r--drivers/infiniband/hw/cxgb4/provider.c8
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c42
-rw-r--r--drivers/infiniband/hw/cxgb4/resource.c9
-rw-r--r--drivers/infiniband/hw/ehca/ehca_eq.c4
-rw-r--r--drivers/infiniband/hw/ehca/ehca_qp.c3
-rw-r--r--drivers/infiniband/hw/ehca/ehca_tools.h2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_diag.c1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_driver.c1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_file_ops.c12
-rw-r--r--drivers/infiniband/hw/ipath/ipath_init_chip.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_mad.c198
-rw-r--r--drivers/infiniband/hw/ipath/ipath_srq.c5
-rw-r--r--drivers/infiniband/hw/ipath/ipath_sysfs.c1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_user_pages.c6
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.c1
-rw-r--r--drivers/infiniband/hw/mlx4/Kconfig3
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c68
-rw-r--r--drivers/infiniband/hw/mlx4/main.c127
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h14
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c141
-rw-r--r--drivers/infiniband/hw/mlx4/srq.c10
-rw-r--r--drivers/infiniband/hw/mthca/mthca_catas.c1
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cmd.c277
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cmd.h93
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cq.c15
-rw-r--r--drivers/infiniband/hw/mthca/mthca_eq.c43
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mad.c15
-rw-r--r--drivers/infiniband/hw/mthca/mthca_main.c175
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mcg.c101
-rw-r--r--drivers/infiniband/hw/mthca/mthca_memfree.c43
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mr.c37
-rw-r--r--drivers/infiniband/hw/mthca/mthca_provider.c82
-rw-r--r--drivers/infiniband/hw/mthca/mthca_qp.c49
-rw-r--r--drivers/infiniband/hw/mthca/mthca_reset.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_srq.c33
-rw-r--r--drivers/infiniband/hw/nes/Makefile2
-rw-r--r--drivers/infiniband/hw/nes/nes.c8
-rw-r--r--drivers/infiniband/hw/nes/nes.h17
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c1125
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.h75
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.c118
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.h39
-rw-r--r--drivers/infiniband/hw/nes/nes_mgt.c1162
-rw-r--r--drivers/infiniband/hw/nes/nes_mgt.h97
-rw-r--r--drivers/infiniband/hw/nes/nes_nic.c83
-rw-r--r--drivers/infiniband/hw/nes/nes_utils.c53
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c19
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.h12
-rw-r--r--drivers/infiniband/hw/qib/qib.h18
-rw-r--r--drivers/infiniband/hw/qib/qib_diag.c1
-rw-r--r--drivers/infiniband/hw/qib/qib_driver.c21
-rw-r--r--drivers/infiniband/hw/qib/qib_file_ops.c19
-rw-r--r--drivers/infiniband/hw/qib/qib_iba6120.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7220.c29
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7322.c208
-rw-r--r--drivers/infiniband/hw/qib/qib_init.c9
-rw-r--r--drivers/infiniband/hw/qib/qib_mad.c78
-rw-r--r--drivers/infiniband/hw/qib/qib_mad.h143
-rw-r--r--drivers/infiniband/hw/qib/qib_pcie.c9
-rw-r--r--drivers/infiniband/hw/qib/qib_qp.c90
-rw-r--r--drivers/infiniband/hw/qib/qib_qsfp.c25
-rw-r--r--drivers/infiniband/hw/qib/qib_qsfp.h3
-rw-r--r--drivers/infiniband/hw/qib/qib_rc.c46
-rw-r--r--drivers/infiniband/hw/qib/qib_ruc.c7
-rw-r--r--drivers/infiniband/hw/qib/qib_sd7220.c1
-rw-r--r--drivers/infiniband/hw/qib/qib_sdma.c1
-rw-r--r--drivers/infiniband/hw/qib/qib_srq.c5
-rw-r--r--drivers/infiniband/hw/qib/qib_sysfs.c13
-rw-r--r--drivers/infiniband/hw/qib/qib_tx.c1
-rw-r--r--drivers/infiniband/hw/qib/qib_uc.c25
-rw-r--r--drivers/infiniband/hw/qib/qib_user_pages.c4
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.c37
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.h5
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c13
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_fs.c8
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c24
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c45
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c21
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c102
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h7
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c35
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c33
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c4
-rw-r--r--drivers/input/Kconfig2
-rw-r--r--drivers/input/ff-core.c11
-rw-r--r--drivers/input/gameport/gameport.c2
-rw-r--r--drivers/input/input-compat.c1
-rw-r--r--drivers/input/input-mt.c2
-rw-r--r--drivers/input/input-polldev.c7
-rw-r--r--drivers/input/joystick/analog.c4
-rw-r--r--drivers/input/joystick/as5011.c1
-rw-r--r--drivers/input/joystick/xpad.c31
-rw-r--r--drivers/input/keyboard/Kconfig4
-rw-r--r--drivers/input/keyboard/adp5588-keys.c4
-rw-r--r--drivers/input/keyboard/adp5589-keys.c610
-rw-r--r--drivers/input/keyboard/atkbd.c4
-rw-r--r--drivers/input/keyboard/davinci_keyscan.c2
-rw-r--r--drivers/input/keyboard/ep93xx_keypad.c3
-rw-r--r--drivers/input/keyboard/gpio_keys.c168
-rw-r--r--drivers/input/keyboard/imx_keypad.c46
-rw-r--r--drivers/input/keyboard/jornada720_kbd.c2
-rw-r--r--drivers/input/keyboard/lm8323.c34
-rw-r--r--drivers/input/keyboard/matrix_keypad.c1
-rw-r--r--drivers/input/keyboard/mpr121_touchkey.c16
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c1
-rw-r--r--drivers/input/keyboard/omap-keypad.c2
-rw-r--r--drivers/input/keyboard/pmic8xxx-keypad.c8
-rw-r--r--drivers/input/keyboard/pxa27x_keypad.c2
-rw-r--r--drivers/input/keyboard/pxa930_rotary.c2
-rw-r--r--drivers/input/keyboard/qt1070.c2
-rw-r--r--drivers/input/keyboard/sh_keysc.c2
-rw-r--r--drivers/input/keyboard/tc3589x-keypad.c2
-rw-r--r--drivers/input/keyboard/tegra-kbc.c94
-rw-r--r--drivers/input/keyboard/tnetv107x-keypad.c3
-rw-r--r--drivers/input/keyboard/w90p910_keypad.c2
-rw-r--r--drivers/input/misc/Kconfig122
-rw-r--r--drivers/input/misc/Makefile11
-rw-r--r--drivers/input/misc/ad714x-i2c.c85
-rw-r--r--drivers/input/misc/ad714x-spi.c68
-rw-r--r--drivers/input/misc/ad714x.c117
-rw-r--r--drivers/input/misc/ad714x.h35
-rw-r--r--drivers/input/misc/adxl34x.c1
-rw-r--r--drivers/input/misc/ati_remote2.c1
-rw-r--r--drivers/input/misc/bfin_rotary.c1
-rw-r--r--drivers/input/misc/bma150.c691
-rw-r--r--drivers/input/misc/cm109.c2
-rw-r--r--drivers/input/misc/cma3000_d0x.c1
-rw-r--r--drivers/input/misc/dm355evm_keys.c1
-rw-r--r--drivers/input/misc/ixp4xx-beeper.c2
-rw-r--r--drivers/input/misc/kxtj9.c672
-rw-r--r--drivers/input/misc/mc13783-pwrbutton.c282
-rw-r--r--drivers/input/misc/mma8450.c264
-rw-r--r--drivers/input/misc/mpu3050.c376
-rw-r--r--drivers/input/misc/pcspkr.c9
-rw-r--r--drivers/input/misc/pm8xxx-vibrator.c296
-rw-r--r--drivers/input/misc/rotary_encoder.c2
-rw-r--r--drivers/input/misc/twl4030-vibra.c12
-rw-r--r--drivers/input/misc/twl6040-vibra.c430
-rw-r--r--drivers/input/misc/xen-kbdfront.c2
-rw-r--r--drivers/input/mouse/alps.c52
-rw-r--r--drivers/input/mouse/bcm5974.c60
-rw-r--r--drivers/input/mouse/elantech.c744
-rw-r--r--drivers/input/mouse/elantech.h57
-rw-r--r--drivers/input/mouse/gpio_mouse.c2
-rw-r--r--drivers/input/mouse/hgpk.c85
-rw-r--r--drivers/input/mouse/hgpk.h11
-rw-r--r--drivers/input/mouse/lifebook.c10
-rw-r--r--drivers/input/mouse/logips2pp.c16
-rw-r--r--drivers/input/mouse/psmouse-base.c67
-rw-r--r--drivers/input/mouse/psmouse.h25
-rw-r--r--drivers/input/mouse/pxa930_trkball.c3
-rw-r--r--drivers/input/mouse/sentelic.c14
-rw-r--r--drivers/input/mouse/synaptics.c661
-rw-r--r--drivers/input/mouse/synaptics.h45
-rw-r--r--drivers/input/mouse/synaptics_i2c.c4
-rw-r--r--drivers/input/serio/ams_delta_serio.c1
-rw-r--r--drivers/input/serio/at32psif.c2
-rw-r--r--drivers/input/serio/hp_sdc.c2
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h14
-rw-r--r--drivers/input/serio/libps2.c2
-rw-r--r--drivers/input/serio/sa1111ps2.c6
-rw-r--r--drivers/input/serio/serio_raw.c215
-rw-r--r--drivers/input/serio/xilinx_ps2.c2
-rw-r--r--drivers/input/sparse-keymap.c1
-rw-r--r--drivers/input/tablet/aiptek.c1
-rw-r--r--drivers/input/tablet/wacom.h10
-rw-r--r--drivers/input/tablet/wacom_sys.c383
-rw-r--r--drivers/input/tablet/wacom_wac.c154
-rw-r--r--drivers/input/touchscreen/Kconfig12
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/ad7877.c1
-rw-r--r--drivers/input/touchscreen/ad7879-i2c.c6
-rw-r--r--drivers/input/touchscreen/ad7879-spi.c1
-rw-r--r--drivers/input/touchscreen/ad7879.c5
-rw-r--r--drivers/input/touchscreen/ads7846.c16
-rw-r--r--drivers/input/touchscreen/atmel-wm97xx.c4
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c223
-rw-r--r--drivers/input/touchscreen/atmel_tsadcc.c2
-rw-r--r--drivers/input/touchscreen/bu21013_ts.c1
-rw-r--r--drivers/input/touchscreen/cy8ctmg110_ts.c8
-rw-r--r--drivers/input/touchscreen/h3600_ts_input.c4
-rw-r--r--drivers/input/touchscreen/hp680_ts_input.c2
-rw-r--r--drivers/input/touchscreen/intel-mid-touch.c15
-rw-r--r--drivers/input/touchscreen/jornada720_ts.c2
-rw-r--r--drivers/input/touchscreen/lpc32xx_ts.c2
-rw-r--r--drivers/input/touchscreen/mainstone-wm97xx.c6
-rw-r--r--drivers/input/touchscreen/max11801_ts.c3
-rw-r--r--drivers/input/touchscreen/mc13783_ts.c34
-rw-r--r--drivers/input/touchscreen/penmount.c199
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c2
-rw-r--r--drivers/input/touchscreen/tnetv107x-ts.c3
-rw-r--r--drivers/input/touchscreen/tsc2007.c200
-rw-r--r--drivers/input/touchscreen/tsc40.c184
-rw-r--r--drivers/input/touchscreen/w90p910_ts.c2
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c31
-rw-r--r--drivers/input/touchscreen/wm9705.c25
-rw-r--r--drivers/input/touchscreen/wm9712.c27
-rw-r--r--drivers/input/touchscreen/wm9713.c25
-rw-r--r--drivers/input/touchscreen/zylonite-wm97xx.c6
-rw-r--r--drivers/iommu/Kconfig134
-rw-r--r--drivers/iommu/Makefile9
-rw-r--r--drivers/iommu/amd_iommu.c2826
-rw-r--r--drivers/iommu/amd_iommu_init.c1574
-rw-r--r--drivers/iommu/amd_iommu_proto.h54
-rw-r--r--drivers/iommu/amd_iommu_types.h585
-rw-r--r--drivers/iommu/dmar.c (renamed from drivers/pci/dmar.c)248
-rw-r--r--drivers/iommu/intel-iommu.c (renamed from drivers/pci/intel-iommu.c)282
-rw-r--r--drivers/iommu/intr_remapping.c (renamed from drivers/pci/intr_remapping.c)94
-rw-r--r--drivers/iommu/intr_remapping.h (renamed from drivers/pci/intr_remapping.h)0
-rw-r--r--drivers/iommu/iommu.c188
-rw-r--r--drivers/iommu/iova.c (renamed from drivers/pci/iova.c)0
-rw-r--r--drivers/iommu/msm_iommu.c738
-rw-r--r--drivers/iommu/msm_iommu_dev.c422
-rw-r--r--drivers/iommu/omap-iommu-debug.c419
-rw-r--r--drivers/iommu/omap-iommu.c1245
-rw-r--r--drivers/iommu/omap-iovmm.c743
-rw-r--r--drivers/isdn/Kconfig2
-rw-r--r--drivers/isdn/capi/kcapi_proc.c1
-rw-r--r--drivers/isdn/gigaset/asyncdata.c1
-rw-r--r--drivers/isdn/gigaset/capi.c1
-rw-r--r--drivers/isdn/gigaset/dummyll.c1
-rw-r--r--drivers/isdn/gigaset/ev-layer.c1
-rw-r--r--drivers/isdn/gigaset/gigaset.h2
-rw-r--r--drivers/isdn/gigaset/i4l.c1
-rw-r--r--drivers/isdn/gigaset/interface.c1
-rw-r--r--drivers/isdn/hardware/mISDN/avmfritz.c1
-rw-r--r--drivers/isdn/hardware/mISDN/hfcmulti.c1
-rw-r--r--drivers/isdn/hardware/mISDN/hfcpci.c1
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNinfineon.c1
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNipac.c1
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNisar.c1
-rw-r--r--drivers/isdn/hardware/mISDN/netjet.c1
-rw-r--r--drivers/isdn/hardware/mISDN/speedfax.c1
-rw-r--r--drivers/isdn/hardware/mISDN/w6692.c1
-rw-r--r--drivers/isdn/hisax/callc.c4
-rw-r--r--drivers/isdn/hisax/hisax.h5
-rw-r--r--drivers/isdn/hisax/hisax_fcpcipnp.c1
-rw-r--r--drivers/isdn/hisax/isdnl1.h2
-rw-r--r--drivers/isdn/hisax/isdnl3.c2
-rw-r--r--drivers/isdn/hisax/l3dss1.c6
-rw-r--r--drivers/isdn/hisax/st5481_d.c4
-rw-r--r--drivers/isdn/i4l/isdn_bsdcomp.c2
-rw-r--r--drivers/isdn/i4l/isdn_common.c4
-rw-r--r--drivers/isdn/i4l/isdn_net.c8
-rw-r--r--drivers/isdn/i4l/isdn_tty.c3
-rw-r--r--drivers/isdn/mISDN/clock.c1
-rw-r--r--drivers/isdn/mISDN/dsp_audio.c1
-rw-r--r--drivers/isdn/mISDN/dsp_core.c3
-rw-r--r--drivers/isdn/mISDN/dsp_pipeline.c1
-rw-r--r--drivers/isdn/mISDN/l1oip_codec.c6
-rw-r--r--drivers/isdn/mISDN/socket.c1
-rw-r--r--drivers/leds/Kconfig42
-rw-r--r--drivers/leds/Makefile2
-rw-r--r--drivers/leds/dell-led.c1
-rw-r--r--drivers/leds/led-class.c2
-rw-r--r--drivers/leds/led-triggers.c5
-rw-r--r--drivers/leds/leds-88pm860x.c1
-rw-r--r--drivers/leds/leds-alix2.c239
-rw-r--r--drivers/leds/leds-ams-delta.c1
-rw-r--r--drivers/leds/leds-asic3.c36
-rw-r--r--drivers/leds/leds-atmel-pwm.c1
-rw-r--r--drivers/leds/leds-bd2802.c5
-rw-r--r--drivers/leds/leds-cobalt-raq.c1
-rw-r--r--drivers/leds/leds-fsg.c1
-rw-r--r--drivers/leds/leds-gpio.c9
-rw-r--r--drivers/leds/leds-hp6xx.c1
-rw-r--r--drivers/leds/leds-lm3530.c71
-rw-r--r--drivers/leds/leds-locomo.c1
-rw-r--r--drivers/leds/leds-lp5521.c28
-rw-r--r--drivers/leds/leds-lt3593.c1
-rw-r--r--drivers/leds/leds-mc13783.c60
-rw-r--r--drivers/leds/leds-net48xx.c1
-rw-r--r--drivers/leds/leds-net5501.c1
-rw-r--r--drivers/leds/leds-ns2.c1
-rw-r--r--drivers/leds/leds-renesas-tpu.c357
-rw-r--r--drivers/leds/leds-s3c24xx.c1
-rw-r--r--drivers/leds/leds-sunfire.c12
-rw-r--r--drivers/leds/leds-wm831x-status.c1
-rw-r--r--drivers/leds/leds-wm8350.c1
-rw-r--r--drivers/leds/leds-wrap.c1
-rw-r--r--drivers/leds/ledtrig-timer.c2
-rw-r--r--drivers/lguest/core.c18
-rw-r--r--drivers/lguest/interrupts_and_traps.c10
-rw-r--r--drivers/lguest/lg.h2
-rw-r--r--drivers/lguest/lguest_device.c38
-rw-r--r--drivers/lguest/lguest_user.c18
-rw-r--r--drivers/lguest/page_tables.c282
-rw-r--r--drivers/lguest/x86/core.c107
-rw-r--r--drivers/macintosh/nvram.c4
-rw-r--r--drivers/macintosh/via-macii.c2
-rw-r--r--drivers/macintosh/via-maciisi.c4
-rw-r--r--drivers/md/Kconfig41
-rw-r--r--drivers/md/Makefile4
-rw-r--r--drivers/md/bitmap.c317
-rw-r--r--drivers/md/bitmap.h17
-rw-r--r--drivers/md/dm-bufio.c1700
-rw-r--r--drivers/md/dm-bufio.h112
-rw-r--r--drivers/md/dm-crypt.c66
-rw-r--r--drivers/md/dm-exception-store.c1
-rw-r--r--drivers/md/dm-flakey.c272
-rw-r--r--drivers/md/dm-io.c29
-rw-r--r--drivers/md/dm-ioctl.c100
-rw-r--r--drivers/md/dm-kcopyd.c76
-rw-r--r--drivers/md/dm-log-userspace-base.c41
-rw-r--r--drivers/md/dm-log.c32
-rw-r--r--drivers/md/dm-mpath.c149
-rw-r--r--drivers/md/dm-path-selector.c1
-rw-r--r--drivers/md/dm-queue-length.c2
-rw-r--r--drivers/md/dm-raid.c680
-rw-r--r--drivers/md/dm-round-robin.c1
-rw-r--r--drivers/md/dm-service-time.c1
-rw-r--r--drivers/md/dm-snap-persistent.c81
-rw-r--r--drivers/md/dm-snap-transient.c1
-rw-r--r--drivers/md/dm-snap.c84
-rw-r--r--drivers/md/dm-table.c260
-rw-r--r--drivers/md/dm-thin-metadata.c1391
-rw-r--r--drivers/md/dm-thin-metadata.h156
-rw-r--r--drivers/md/dm-thin.c2428
-rw-r--r--drivers/md/dm-uevent.c1
-rw-r--r--drivers/md/dm.c121
-rw-r--r--drivers/md/dm.h4
-rw-r--r--drivers/md/faulty.c51
-rw-r--r--drivers/md/linear.c70
-rw-r--r--drivers/md/linear.h14
-rw-r--r--drivers/md/md.c1565
-rw-r--r--drivers/md/md.h247
-rw-r--r--drivers/md/multipath.c74
-rw-r--r--drivers/md/multipath.h10
-rw-r--r--drivers/md/persistent-data/Kconfig8
-rw-r--r--drivers/md/persistent-data/Makefile11
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c620
-rw-r--r--drivers/md/persistent-data/dm-block-manager.h123
-rw-r--r--drivers/md/persistent-data/dm-btree-internal.h137
-rw-r--r--drivers/md/persistent-data/dm-btree-remove.c566
-rw-r--r--drivers/md/persistent-data/dm-btree-spine.c244
-rw-r--r--drivers/md/persistent-data/dm-btree.c805
-rw-r--r--drivers/md/persistent-data/dm-btree.h145
-rw-r--r--drivers/md/persistent-data/dm-persistent-data-internal.h19
-rw-r--r--drivers/md/persistent-data/dm-space-map-checker.c438
-rw-r--r--drivers/md/persistent-data/dm-space-map-checker.h26
-rw-r--r--drivers/md/persistent-data/dm-space-map-common.c705
-rw-r--r--drivers/md/persistent-data/dm-space-map-common.h126
-rw-r--r--drivers/md/persistent-data/dm-space-map-disk.c335
-rw-r--r--drivers/md/persistent-data/dm-space-map-disk.h25
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.c596
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.h33
-rw-r--r--drivers/md/persistent-data/dm-space-map.h134
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.c400
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.h130
-rw-r--r--drivers/md/raid0.c212
-rw-r--r--drivers/md/raid0.h10
-rw-r--r--drivers/md/raid1.c1268
-rw-r--r--drivers/md/raid1.h99
-rw-r--r--drivers/md/raid10.c1451
-rw-r--r--drivers/md/raid10.h43
-rw-r--r--drivers/md/raid5.c1381
-rw-r--r--drivers/md/raid5.h117
-rw-r--r--drivers/media/Kconfig14
-rw-r--r--drivers/media/common/saa7146_core.c75
-rw-r--r--drivers/media/common/saa7146_fops.c119
-rw-r--r--drivers/media/common/saa7146_hlp.c15
-rw-r--r--drivers/media/common/saa7146_i2c.c60
-rw-r--r--drivers/media/common/saa7146_vbi.c48
-rw-r--r--drivers/media/common/saa7146_video.c184
-rw-r--r--drivers/media/common/tuners/Kconfig10
-rw-r--r--drivers/media/common/tuners/Makefile5
-rw-r--r--drivers/media/common/tuners/mt20xx.c24
-rw-r--r--drivers/media/common/tuners/mxl5005s.c22
-rw-r--r--drivers/media/common/tuners/tda18212.c31
-rw-r--r--drivers/media/common/tuners/tda18271-common.c32
-rw-r--r--drivers/media/common/tuners/tda18271-fe.c2
-rw-r--r--drivers/media/common/tuners/tda18271-priv.h39
-rw-r--r--drivers/media/common/tuners/tda827x.c8
-rw-r--r--drivers/media/common/tuners/tuner-types.c5
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c18
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.h1
-rw-r--r--drivers/media/common/tuners/xc4000.c1691
-rw-r--r--drivers/media/common/tuners/xc4000.h67
-rw-r--r--drivers/media/dvb/Kconfig4
-rw-r--r--drivers/media/dvb/Makefile3
-rw-r--r--drivers/media/dvb/b2c2/Makefile4
-rw-r--r--drivers/media/dvb/b2c2/flexcop-common.h1
-rw-r--r--drivers/media/dvb/bt8xx/Makefile8
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.c4
-rw-r--r--drivers/media/dvb/ddbridge/Kconfig18
-rw-r--r--drivers/media/dvb/ddbridge/Makefile14
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge-core.c1722
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge-regs.h151
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge.h187
-rw-r--r--drivers/media/dvb/dm1105/Makefile2
-rw-r--r--drivers/media/dvb/dm1105/dm1105.c1
-rw-r--r--drivers/media/dvb/dvb-core/Makefile4
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c106
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h1
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.c2
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.h21
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig29
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile16
-rw-r--r--drivers/media/dvb/dvb-usb/a800.c4
-rw-r--r--drivers/media/dvb/dvb-usb/af9005-fe.c2
-rw-r--r--drivers/media/dvb/dvb-usb/af9005-remote.c2
-rw-r--r--drivers/media/dvb/dvb-usb/af9005.c7
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c183
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.h1
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.c406
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.h17
-rw-r--r--drivers/media/dvb/dvb-usb/au6610.c9
-rw-r--r--drivers/media/dvb/dvb-usb/az6027.c26
-rw-r--r--drivers/media/dvb/dvb-usb/ce6230.c9
-rw-r--r--drivers/media/dvb/dvb-usb/cinergyT2-core.c5
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c142
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c99
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c549
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-common.c27
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mb.c31
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mc.c3
-rw-r--r--drivers/media/dvb/dvb-usb/digitv.c16
-rw-r--r--drivers/media/dvb/dvb-usb/dtt200u.c14
-rw-r--r--drivers/media/dvb/dvb-usb/dtv5100.c11
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-dvb.c153
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h9
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-init.c41
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-urb.c28
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb.h39
-rw-r--r--drivers/media/dvb/dvb-usb/dw2102.c115
-rw-r--r--drivers/media/dvb/dvb-usb/ec168.c9
-rw-r--r--drivers/media/dvb/dvb-usb/friio.c7
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.c9
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk-fe.c17
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.c5
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.h3
-rw-r--r--drivers/media/dvb/dvb-usb/it913x.c702
-rw-r--r--drivers/media/dvb/dvb-usb/lmedm04.c60
-rw-r--r--drivers/media/dvb/dvb-usb/m920x.c58
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-demod.c614
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-demod.h55
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-gpio.c763
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-gpio.h56
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-i2c.c850
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-i2c.h35
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-phy.c343
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-phy.h53
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-reg.h179
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-tuner.c476
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-tuner.h89
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.c1086
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.h158
-rw-r--r--drivers/media/dvb/dvb-usb/nova-t-usb2.c4
-rw-r--r--drivers/media/dvb/dvb-usb/opera1.c13
-rw-r--r--drivers/media/dvb/dvb-usb/pctv452e.c1079
-rw-r--r--drivers/media/dvb/dvb-usb/technisat-usb2.c32
-rw-r--r--drivers/media/dvb/dvb-usb/ttusb2.c407
-rw-r--r--drivers/media/dvb/dvb-usb/umt-010.c8
-rw-r--r--drivers/media/dvb/dvb-usb/usb-urb.c4
-rw-r--r--drivers/media/dvb/dvb-usb/vp702x.c5
-rw-r--r--drivers/media/dvb/dvb-usb/vp7045.c31
-rw-r--r--drivers/media/dvb/dvb-usb/vp7045.h3
-rw-r--r--drivers/media/dvb/firewire/firedtv-avc.c2
-rw-r--r--drivers/media/dvb/firewire/firedtv-ci.c34
-rw-r--r--drivers/media/dvb/frontends/Kconfig51
-rw-r--r--drivers/media/dvb/frontends/Makefile11
-rw-r--r--drivers/media/dvb/frontends/a8293.c184
-rw-r--r--drivers/media/dvb/frontends/a8293.h (renamed from drivers/media/common/tuners/tda18212_priv.h)39
-rw-r--r--drivers/media/dvb/frontends/au8522_decoder.c2
-rw-r--r--drivers/media/dvb/frontends/cx24113.c20
-rw-r--r--drivers/media/dvb/frontends/cx24116.c6
-rw-r--r--drivers/media/dvb/frontends/cxd2820r.h13
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_c.c1
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_core.c74
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_priv.h5
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_t.c1
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_t2.c1
-rw-r--r--drivers/media/dvb/frontends/dib0070.c37
-rw-r--r--drivers/media/dvb/frontends/dib0090.c70
-rw-r--r--drivers/media/dvb/frontends/dib3000.h2
-rw-r--r--drivers/media/dvb/frontends/dib3000mb.c2
-rw-r--r--drivers/media/dvb/frontends/dib7000m.c27
-rw-r--r--drivers/media/dvb/frontends/dib7000p.c39
-rw-r--r--drivers/media/dvb/frontends/dib8000.c72
-rw-r--r--drivers/media/dvb/frontends/dib9000.c167
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.c77
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.h1
-rw-r--r--drivers/media/dvb/frontends/drxd_hard.c33
-rw-r--r--drivers/media/dvb/frontends/drxk.h47
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.c6462
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.h348
-rw-r--r--drivers/media/dvb/frontends/drxk_map.h449
-rw-r--r--drivers/media/dvb/frontends/it913x-fe-priv.h336
-rw-r--r--drivers/media/dvb/frontends/it913x-fe.c839
-rw-r--r--drivers/media/dvb/frontends/it913x-fe.h196
-rw-r--r--drivers/media/dvb/frontends/itd1000.c25
-rw-r--r--drivers/media/dvb/frontends/lnbp22.c148
-rw-r--r--drivers/media/dvb/frontends/lnbp22.h57
-rw-r--r--drivers/media/dvb/frontends/nxt6000.c2
-rw-r--r--drivers/media/dvb/frontends/s5h1420.c12
-rw-r--r--drivers/media/dvb/frontends/stb0899_algo.c3
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.c6
-rw-r--r--drivers/media/dvb/frontends/stv0288.c29
-rw-r--r--drivers/media/dvb/frontends/stv090x.c35
-rw-r--r--drivers/media/dvb/frontends/tda10048.c37
-rw-r--r--drivers/media/dvb/frontends/tda10048.h8
-rw-r--r--drivers/media/dvb/frontends/tda10071.c1269
-rw-r--r--drivers/media/dvb/frontends/tda10071.h81
-rw-r--r--drivers/media/dvb/frontends/tda10071_priv.h122
-rw-r--r--drivers/media/dvb/frontends/tda18271c2dd.c1251
-rw-r--r--drivers/media/dvb/frontends/tda18271c2dd.h16
-rw-r--r--drivers/media/dvb/frontends/tda18271c2dd_maps.h814
-rw-r--r--drivers/media/dvb/mantis/Makefile2
-rw-r--r--drivers/media/dvb/mantis/hopper_cards.c6
-rw-r--r--drivers/media/dvb/mantis/mantis_ca.c1
-rw-r--r--drivers/media/dvb/mantis/mantis_cards.c6
-rw-r--r--drivers/media/dvb/mantis/mantis_common.h6
-rw-r--r--drivers/media/dvb/mantis/mantis_dma.c92
-rw-r--r--drivers/media/dvb/mantis/mantis_evm.c1
-rw-r--r--drivers/media/dvb/mantis/mantis_hif.c1
-rw-r--r--drivers/media/dvb/mantis/mantis_ioc.c1
-rw-r--r--drivers/media/dvb/mantis/mantis_pcmcia.c1
-rw-r--r--drivers/media/dvb/mantis/mantis_uart.c1
-rw-r--r--drivers/media/dvb/mantis/mantis_vp1034.c1
-rw-r--r--drivers/media/dvb/mantis/mantis_vp1041.c1
-rw-r--r--drivers/media/dvb/ngene/Kconfig2
-rw-r--r--drivers/media/dvb/ngene/Makefile8
-rw-r--r--drivers/media/dvb/ngene/ngene-cards.c182
-rw-r--r--drivers/media/dvb/ngene/ngene-core.c26
-rw-r--r--drivers/media/dvb/ngene/ngene-dvb.c46
-rw-r--r--drivers/media/dvb/ngene/ngene.h7
-rw-r--r--drivers/media/dvb/pluto2/Makefile2
-rw-r--r--drivers/media/dvb/pluto2/pluto2.c1
-rw-r--r--drivers/media/dvb/pt1/Makefile2
-rw-r--r--drivers/media/dvb/siano/Makefile4
-rw-r--r--drivers/media/dvb/siano/sms-cards.c1
-rw-r--r--drivers/media/dvb/siano/smscoreapi.c2
-rw-r--r--drivers/media/dvb/siano/smscoreapi.h1
-rw-r--r--drivers/media/dvb/siano/smsendian.c1
-rw-r--r--drivers/media/dvb/siano/smssdio.c1
-rw-r--r--drivers/media/dvb/siano/smsusb.c1
-rw-r--r--drivers/media/dvb/ttpci/Makefile4
-rw-r--r--drivers/media/dvb/ttpci/av7110_v4l.c32
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c47
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c1
-rw-r--r--drivers/media/dvb/ttpci/budget-core.c2
-rw-r--r--drivers/media/dvb/ttpci/budget.h1
-rw-r--r--drivers/media/dvb/ttpci/ttpci-eeprom.c29
-rw-r--r--drivers/media/dvb/ttpci/ttpci-eeprom.h1
-rw-r--r--drivers/media/dvb/ttusb-budget/Makefile2
-rw-r--r--drivers/media/dvb/ttusb-dec/Makefile2
-rw-r--r--drivers/media/media-device.c1
-rw-r--r--drivers/media/radio/Kconfig5
-rw-r--r--drivers/media/radio/Makefile2
-rw-r--r--drivers/media/radio/dsbr100.c7
-rw-r--r--drivers/media/radio/radio-aimslab.c5
-rw-r--r--drivers/media/radio/radio-aztech.c5
-rw-r--r--drivers/media/radio/radio-cadet.c5
-rw-r--r--drivers/media/radio/radio-gemtek.c7
-rw-r--r--drivers/media/radio/radio-maxiradio.c10
-rw-r--r--drivers/media/radio/radio-mr800.c6
-rw-r--r--drivers/media/radio/radio-rtrack2.c5
-rw-r--r--drivers/media/radio/radio-sf16fmi.c5
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c531
-rw-r--r--drivers/media/radio/radio-si4713.c4
-rw-r--r--drivers/media/radio/radio-tea5764.c12
-rw-r--r--drivers/media/radio/radio-terratec.c5
-rw-r--r--drivers/media/radio/radio-timb.c4
-rw-r--r--drivers/media/radio/radio-trust.c5
-rw-r--r--drivers/media/radio/radio-typhoon.c9
-rw-r--r--drivers/media/radio/radio-wl1273.c5
-rw-r--r--drivers/media/radio/radio-zoltrix.c5
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c4
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c8
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h1
-rw-r--r--drivers/media/radio/si4713-i2c.c5
-rw-r--r--drivers/media/radio/wl128x/fmdrv.h5
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.c13
-rw-r--r--drivers/media/rc/Kconfig34
-rw-r--r--drivers/media/rc/Makefile2
-rw-r--r--drivers/media/rc/ati_remote.c (renamed from drivers/input/misc/ati_remote.c)301
-rw-r--r--drivers/media/rc/ene_ir.c77
-rw-r--r--drivers/media/rc/ene_ir.h21
-rw-r--r--drivers/media/rc/imon.c36
-rw-r--r--drivers/media/rc/ir-jvc-decoder.c1
-rw-r--r--drivers/media/rc/ir-lirc-codec.c25
-rw-r--r--drivers/media/rc/ir-mce_kbd-decoder.c449
-rw-r--r--drivers/media/rc/ir-nec-decoder.c1
-rw-r--r--drivers/media/rc/ir-raw.c3
-rw-r--r--drivers/media/rc/ir-rc5-decoder.c1
-rw-r--r--drivers/media/rc/ir-rc5-sz-decoder.c1
-rw-r--r--drivers/media/rc/ir-rc6-decoder.c1
-rw-r--r--drivers/media/rc/ir-sony-decoder.c1
-rw-r--r--drivers/media/rc/ite-cir.c6
-rw-r--r--drivers/media/rc/keymaps/Makefile3
-rw-r--r--drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c1
-rw-r--r--drivers/media/rc/keymaps/rc-alink-dtu-m.c1
-rw-r--r--drivers/media/rc/keymaps/rc-anysee.c1
-rw-r--r--drivers/media/rc/keymaps/rc-apac-viewcomp.c1
-rw-r--r--drivers/media/rc/keymaps/rc-asus-pc39.c1
-rw-r--r--drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c1
-rw-r--r--drivers/media/rc/keymaps/rc-ati-x10.c104
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-a16d.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-cardbus.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-dvbt.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-m135a.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-rm-ks.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avertv-303.c1
-rw-r--r--drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c1
-rw-r--r--drivers/media/rc/keymaps/rc-behold-columbus.c1
-rw-r--r--drivers/media/rc/keymaps/rc-behold.c1
-rw-r--r--drivers/media/rc/keymaps/rc-budget-ci-old.c1
-rw-r--r--drivers/media/rc/keymaps/rc-cinergy-1400.c1
-rw-r--r--drivers/media/rc/keymaps/rc-cinergy.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dib0700-nec.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dib0700-rc5.c1
-rw-r--r--drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c1
-rw-r--r--drivers/media/rc/keymaps/rc-digittrade.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dm1105-nec.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c1
-rw-r--r--drivers/media/rc/keymaps/rc-em-terratec.c1
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv-fm53.c1
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv.c1
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv2.c1
-rw-r--r--drivers/media/rc/keymaps/rc-evga-indtube.c1
-rw-r--r--drivers/media/rc/keymaps/rc-eztv.c1
-rw-r--r--drivers/media/rc/keymaps/rc-flydvb.c1
-rw-r--r--drivers/media/rc/keymaps/rc-flyvideo.c1
-rw-r--r--drivers/media/rc/keymaps/rc-fusionhdtv-mce.c1
-rw-r--r--drivers/media/rc/keymaps/rc-gadmei-rm008z.c1
-rw-r--r--drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c1
-rw-r--r--drivers/media/rc/keymaps/rc-gotview7135.c1
-rw-r--r--drivers/media/rc/keymaps/rc-hauppauge.c1
-rw-r--r--drivers/media/rc/keymaps/rc-imon-mce.c1
-rw-r--r--drivers/media/rc/keymaps/rc-imon-pad.c1
-rw-r--r--drivers/media/rc/keymaps/rc-iodata-bctv7e.c1
-rw-r--r--drivers/media/rc/keymaps/rc-kaiomy.c1
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-315u.c1
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c1
-rw-r--r--drivers/media/rc/keymaps/rc-leadtek-y04g0051.c1
-rw-r--r--drivers/media/rc/keymaps/rc-lirc.c1
-rw-r--r--drivers/media/rc/keymaps/rc-lme2510.c1
-rw-r--r--drivers/media/rc/keymaps/rc-manli.c1
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10.c117
-rw-r--r--drivers/media/rc/keymaps/rc-msi-digivox-ii.c1
-rw-r--r--drivers/media/rc/keymaps/rc-msi-digivox-iii.c1
-rw-r--r--drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c1
-rw-r--r--drivers/media/rc/keymaps/rc-msi-tvanywhere.c1
-rw-r--r--drivers/media/rc/keymaps/rc-nebula.c1
-rw-r--r--drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c1
-rw-r--r--drivers/media/rc/keymaps/rc-norwood.c1
-rw-r--r--drivers/media/rc/keymaps/rc-npgtech.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pctv-sedna.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-color.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-grey.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c2
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-002t.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-mk12.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-new.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview.c1
-rw-r--r--drivers/media/rc/keymaps/rc-powercolor-real-angel.c1
-rw-r--r--drivers/media/rc/keymaps/rc-proteus-2309.c1
-rw-r--r--drivers/media/rc/keymaps/rc-purpletv.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pv951.c1
-rw-r--r--drivers/media/rc/keymaps/rc-rc6-mce.c4
-rw-r--r--drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c1
-rw-r--r--drivers/media/rc/keymaps/rc-snapstream-firefly.c107
-rw-r--r--drivers/media/rc/keymaps/rc-streamzap.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tbs-nec.c1
-rw-r--r--drivers/media/rc/keymaps/rc-technisat-usb2.c1
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c1
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-slim-2.c1
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-slim.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tevii-nec.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tivo.c1
-rw-r--r--drivers/media/rc/keymaps/rc-total-media-in-hand.c1
-rw-r--r--drivers/media/rc/keymaps/rc-trekstor.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tt-1500.c1
-rw-r--r--drivers/media/rc/keymaps/rc-twinhan1027.c1
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-m1f.c1
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-s350.c1
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-tv-pvr.c1
-rw-r--r--drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c1
-rw-r--r--drivers/media/rc/keymaps/rc-winfast.c1
-rw-r--r--drivers/media/rc/mceusb.c425
-rw-r--r--drivers/media/rc/nuvoton-cir.c59
-rw-r--r--drivers/media/rc/nuvoton-cir.h1
-rw-r--r--drivers/media/rc/rc-core-priv.h30
-rw-r--r--drivers/media/rc/rc-loopback.c13
-rw-r--r--drivers/media/rc/rc-main.c34
-rw-r--r--drivers/media/rc/redrat3.c70
-rw-r--r--drivers/media/rc/winbond-cir.c34
-rw-r--r--drivers/media/video/Kconfig105
-rw-r--r--drivers/media/video/Makefile19
-rw-r--r--drivers/media/video/adp1653.c506
-rw-r--r--drivers/media/video/adv7175.c62
-rw-r--r--drivers/media/video/ak881x.c1
-rw-r--r--drivers/media/video/arv.c5
-rw-r--r--drivers/media/video/atmel-isi.c1068
-rw-r--r--drivers/media/video/au0828/Makefile8
-rw-r--r--drivers/media/video/au0828/au0828-core.c1
-rw-r--r--drivers/media/video/au0828/au0828-video.c5
-rw-r--r--drivers/media/video/bt819.c2
-rw-r--r--drivers/media/video/bt8xx/Makefile6
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c249
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c304
-rw-r--r--drivers/media/video/bt8xx/bttv-gpio.c4
-rw-r--r--drivers/media/video/bt8xx/bttv-i2c.c56
-rw-r--r--drivers/media/video/bt8xx/bttv-input.c37
-rw-r--r--drivers/media/video/bt8xx/bttv-risc.c25
-rw-r--r--drivers/media/video/bt8xx/bttv-vbi.c9
-rw-r--r--drivers/media/video/bt8xx/bttvp.h21
-rw-r--r--drivers/media/video/bw-qcam.c4
-rw-r--r--drivers/media/video/c-qcam.c4
-rw-r--r--drivers/media/video/cafe_ccic-regs.h166
-rw-r--r--drivers/media/video/cafe_ccic.c2267
-rw-r--r--drivers/media/video/cpia2/cpia2.h5
-rw-r--r--drivers/media/video/cpia2/cpia2_usb.c1
-rw-r--r--drivers/media/video/cpia2/cpia2_v4l.c12
-rw-r--r--drivers/media/video/cx18/Makefile6
-rw-r--r--drivers/media/video/cx18/cx18-alsa-main.c1
-rw-r--r--drivers/media/video/cx18/cx18-driver.c2
-rw-r--r--drivers/media/video/cx18/cx18-driver.h6
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c2
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c19
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c2
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.h2
-rw-r--r--drivers/media/video/cx18/cx18-streams.c13
-rw-r--r--drivers/media/video/cx18/cx18-version.h8
-rw-r--r--drivers/media/video/cx231xx/Makefile10
-rw-r--r--drivers/media/video/cx231xx/cx231xx-avcore.c4
-rw-r--r--drivers/media/video/cx231xx/cx231xx-cards.c78
-rw-r--r--drivers/media/video/cx231xx/cx231xx-core.c4
-rw-r--r--drivers/media/video/cx231xx/cx231xx-video.c29
-rw-r--r--drivers/media/video/cx231xx/cx231xx.h5
-rw-r--r--drivers/media/video/cx23885/Kconfig2
-rw-r--r--drivers/media/video/cx23885/Makefile12
-rw-r--r--drivers/media/video/cx23885/altera-ci.c1
-rw-r--r--drivers/media/video/cx23885/cx23885-417.c1
-rw-r--r--drivers/media/video/cx23885/cx23885-alsa.c535
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c125
-rw-r--r--drivers/media/video/cx23885/cx23885-core.c121
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c25
-rw-r--r--drivers/media/video/cx23885/cx23885-i2c.c1
-rw-r--r--drivers/media/video/cx23885/cx23885-input.c6
-rw-r--r--drivers/media/video/cx23885/cx23885-reg.h3
-rw-r--r--drivers/media/video/cx23885/cx23885-vbi.c72
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c359
-rw-r--r--drivers/media/video/cx23885/cx23885.h60
-rw-r--r--drivers/media/video/cx23885/cx23888-ir.c12
-rw-r--r--drivers/media/video/cx25821/Kconfig (renamed from drivers/staging/cx25821/Kconfig)0
-rw-r--r--drivers/media/video/cx25821/Makefile (renamed from drivers/staging/cx25821/Makefile)0
-rw-r--r--drivers/media/video/cx25821/cx25821-alsa.c (renamed from drivers/staging/cx25821/cx25821-alsa.c)2
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.c (renamed from drivers/staging/cx25821/cx25821-audio-upstream.c)43
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.h (renamed from drivers/staging/cx25821/cx25821-audio-upstream.h)9
-rw-r--r--drivers/media/video/cx25821/cx25821-audio.h (renamed from drivers/staging/cx25821/cx25821-audio.h)6
-rw-r--r--drivers/media/video/cx25821/cx25821-biffuncs.h (renamed from drivers/staging/cx25821/cx25821-biffuncs.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-cards.c (renamed from drivers/staging/cx25821/cx25821-cards.c)26
-rw-r--r--drivers/media/video/cx25821/cx25821-core.c (renamed from drivers/staging/cx25821/cx25821-core.c)505
-rw-r--r--drivers/media/video/cx25821/cx25821-gpio.c (renamed from drivers/staging/cx25821/cx25821-gpio.c)1
-rw-r--r--drivers/media/video/cx25821/cx25821-i2c.c (renamed from drivers/staging/cx25821/cx25821-i2c.c)30
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-defines.h (renamed from drivers/staging/cx25821/cx25821-medusa-defines.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-reg.h (renamed from drivers/staging/cx25821/cx25821-medusa-reg.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.c (renamed from drivers/staging/cx25821/cx25821-medusa-video.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.h (renamed from drivers/staging/cx25821/cx25821-medusa-video.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-reg.h (renamed from drivers/staging/cx25821/cx25821-reg.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-sram.h (renamed from drivers/staging/cx25821/cx25821-sram.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.c (renamed from drivers/staging/cx25821/cx25821-video-upstream-ch2.c)81
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.h (renamed from drivers/staging/cx25821/cx25821-video-upstream-ch2.h)83
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.c (renamed from drivers/staging/cx25821/cx25821-video-upstream.c)18
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.h (renamed from drivers/staging/cx25821/cx25821-video-upstream.h)78
-rw-r--r--drivers/media/video/cx25821/cx25821-video.c (renamed from drivers/staging/cx25821/cx25821-video.c)1246
-rw-r--r--drivers/media/video/cx25821/cx25821-video.h (renamed from drivers/staging/cx25821/cx25821-video.h)79
-rw-r--r--drivers/media/video/cx25821/cx25821.h (renamed from drivers/staging/cx25821/cx25821.h)13
-rw-r--r--drivers/media/video/cx25840/Makefile2
-rw-r--r--drivers/media/video/cx25840/cx25840-audio.c10
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c19
-rw-r--r--drivers/media/video/cx25840/cx25840-ir.c13
-rw-r--r--drivers/media/video/cx88/Makefile8
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c19
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c20
-rw-r--r--drivers/media/video/cx88/cx88-cards.c150
-rw-r--r--drivers/media/video/cx88/cx88-core.c8
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c77
-rw-r--r--drivers/media/video/cx88/cx88-input.c4
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c35
-rw-r--r--drivers/media/video/cx88/cx88-video.c65
-rw-r--r--drivers/media/video/cx88/cx88.h7
-rw-r--r--drivers/media/video/davinci/Kconfig23
-rw-r--r--drivers/media/video/davinci/Makefile2
-rw-r--r--drivers/media/video/davinci/dm355_ccdc.c1
-rw-r--r--drivers/media/video/davinci/dm644x_ccdc.c1
-rw-r--r--drivers/media/video/davinci/vpbe.c864
-rw-r--r--drivers/media/video/davinci/vpbe_display.c1859
-rw-r--r--drivers/media/video/davinci/vpbe_osd.c1231
-rw-r--r--drivers/media/video/davinci/vpbe_osd_regs.h364
-rw-r--r--drivers/media/video/davinci/vpbe_venc.c566
-rw-r--r--drivers/media/video/davinci/vpbe_venc_regs.h177
-rw-r--r--drivers/media/video/davinci/vpif.c2
-rw-r--r--drivers/media/video/davinci/vpif_capture.c9
-rw-r--r--drivers/media/video/davinci/vpif_capture.h7
-rw-r--r--drivers/media/video/davinci/vpif_display.c9
-rw-r--r--drivers/media/video/davinci/vpif_display.h8
-rw-r--r--drivers/media/video/em28xx/Kconfig14
-rw-r--r--drivers/media/video/em28xx/Makefile14
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c251
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c308
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c129
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c243
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c17
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c7
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h1
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c72
-rw-r--r--drivers/media/video/em28xx/em28xx.h27
-rw-r--r--drivers/media/video/et61x251/et61x251.h67
-rw-r--r--drivers/media/video/et61x251/et61x251_core.c18
-rw-r--r--drivers/media/video/et61x251/et61x251_tas5130d1b.c2
-rw-r--r--drivers/media/video/fsl-viu.c10
-rw-r--r--drivers/media/video/gspca/Kconfig20
-rw-r--r--drivers/media/video/gspca/Makefile4
-rw-r--r--drivers/media/video/gspca/benq.c31
-rw-r--r--drivers/media/video/gspca/conex.c6
-rw-r--r--drivers/media/video/gspca/cpia1.c7
-rw-r--r--drivers/media/video/gspca/etoms.c6
-rw-r--r--drivers/media/video/gspca/finepix.c8
-rw-r--r--drivers/media/video/gspca/gl860/Makefile2
-rw-r--r--drivers/media/video/gspca/gl860/gl860.c8
-rw-r--r--drivers/media/video/gspca/gl860/gl860.h1
-rw-r--r--drivers/media/video/gspca/gspca.c302
-rw-r--r--drivers/media/video/gspca/gspca.h22
-rw-r--r--drivers/media/video/gspca/jeilinj.c20
-rw-r--r--drivers/media/video/gspca/kinect.c41
-rw-r--r--drivers/media/video/gspca/konica.c16
-rw-r--r--drivers/media/video/gspca/m5602/Makefile2
-rw-r--r--drivers/media/video/gspca/m5602/m5602_core.c9
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.c28
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov7660.c21
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.c19
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.c21
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.c35
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.c30
-rw-r--r--drivers/media/video/gspca/mars.c6
-rw-r--r--drivers/media/video/gspca/mr97310a.c24
-rw-r--r--drivers/media/video/gspca/nw80x.c9
-rw-r--r--drivers/media/video/gspca/ov519.c172
-rw-r--r--drivers/media/video/gspca/ov534.c12
-rw-r--r--drivers/media/video/gspca/ov534_9.c516
-rw-r--r--drivers/media/video/gspca/pac207.c14
-rw-r--r--drivers/media/video/gspca/pac7302.c15
-rw-r--r--drivers/media/video/gspca/pac7311.c15
-rw-r--r--drivers/media/video/gspca/se401.c780
-rw-r--r--drivers/media/video/gspca/se401.h90
-rw-r--r--drivers/media/video/gspca/sn9c2028.c14
-rw-r--r--drivers/media/video/gspca/sn9c20x.c76
-rw-r--r--drivers/media/video/gspca/sonixj.c51
-rw-r--r--drivers/media/video/gspca/spca1528.c34
-rw-r--r--drivers/media/video/gspca/spca500.c6
-rw-r--r--drivers/media/video/gspca/spca501.c4
-rw-r--r--drivers/media/video/gspca/spca505.c8
-rw-r--r--drivers/media/video/gspca/spca508.c6
-rw-r--r--drivers/media/video/gspca/spca561.c4
-rw-r--r--drivers/media/video/gspca/sq905.c17
-rw-r--r--drivers/media/video/gspca/sq905c.c10
-rw-r--r--drivers/media/video/gspca/sq930x.c21
-rw-r--r--drivers/media/video/gspca/stk014.c16
-rw-r--r--drivers/media/video/gspca/stv0680.c6
-rw-r--r--drivers/media/video/gspca/stv06xx/Makefile2
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.c18
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.h6
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c10
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c4
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_st6422.c4
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c32
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h56
-rw-r--r--drivers/media/video/gspca/sunplus.c13
-rw-r--r--drivers/media/video/gspca/t613.c14
-rw-r--r--drivers/media/video/gspca/topro.c4989
-rw-r--r--drivers/media/video/gspca/vc032x.c13
-rw-r--r--drivers/media/video/gspca/vicam.c12
-rw-r--r--drivers/media/video/gspca/w996Xcf.c8
-rw-r--r--drivers/media/video/gspca/xirlink_cit.c14
-rw-r--r--drivers/media/video/gspca/zc3xx.c15
-rw-r--r--drivers/media/video/hdpvr/Makefile4
-rw-r--r--drivers/media/video/hdpvr/hdpvr-core.c3
-rw-r--r--drivers/media/video/hdpvr/hdpvr-i2c.c1
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c2
-rw-r--r--drivers/media/video/hdpvr/hdpvr.h6
-rw-r--r--drivers/media/video/hexium_gemini.c45
-rw-r--r--drivers/media/video/hexium_orion.c39
-rw-r--r--drivers/media/video/imx074.c55
-rw-r--r--drivers/media/video/ivtv/Makefile8
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c2
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h1
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c19
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c20
-rw-r--r--drivers/media/video/ivtv/ivtv-version.h7
-rw-r--r--drivers/media/video/m5mols/m5mols_capture.c2
-rw-r--r--drivers/media/video/m5mols/m5mols_core.c8
-rw-r--r--drivers/media/video/marvell-ccic/Kconfig23
-rw-r--r--drivers/media/video/marvell-ccic/Makefile6
-rw-r--r--drivers/media/video/marvell-ccic/cafe-driver.c654
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.c1846
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.h323
-rw-r--r--drivers/media/video/marvell-ccic/mmp-driver.c341
-rw-r--r--drivers/media/video/mem2mem_testdev.c25
-rw-r--r--drivers/media/video/msp3400-driver.c20
-rw-r--r--drivers/media/video/msp3400-driver.h2
-rw-r--r--drivers/media/video/msp3400-kthreads.c86
-rw-r--r--drivers/media/video/mt9m001.c333
-rw-r--r--drivers/media/video/mt9m111.c585
-rw-r--r--drivers/media/video/mt9p031.c964
-rw-r--r--drivers/media/video/mt9t001.c836
-rw-r--r--drivers/media/video/mt9t031.c349
-rw-r--r--drivers/media/video/mt9t112.c273
-rw-r--r--drivers/media/video/mt9v011.c86
-rw-r--r--drivers/media/video/mt9v022.c448
-rw-r--r--drivers/media/video/mt9v032.c21
-rw-r--r--drivers/media/video/mx1_camera.c118
-rw-r--r--drivers/media/video/mx2_camera.c142
-rw-r--r--drivers/media/video/mx3_camera.c426
-rw-r--r--drivers/media/video/mxb.c81
-rw-r--r--drivers/media/video/noon010pc30.c264
-rw-r--r--drivers/media/video/omap/Kconfig7
-rw-r--r--drivers/media/video/omap/Makefile1
-rw-r--r--drivers/media/video/omap/omap_vout.c688
-rw-r--r--drivers/media/video/omap/omap_vout_vrfb.c390
-rw-r--r--drivers/media/video/omap/omap_vout_vrfb.h40
-rw-r--r--drivers/media/video/omap/omap_voutdef.h78
-rw-r--r--drivers/media/video/omap/omap_voutlib.c46
-rw-r--r--drivers/media/video/omap/omap_voutlib.h12
-rw-r--r--drivers/media/video/omap1_camera.c117
-rw-r--r--drivers/media/video/omap24xxcam.c15
-rw-r--r--drivers/media/video/omap3isp/Makefile4
-rw-r--r--drivers/media/video/omap3isp/isp.c55
-rw-r--r--drivers/media/video/omap3isp/isp.h84
-rw-r--r--drivers/media/video/omap3isp/ispccdc.c130
-rw-r--r--drivers/media/video/omap3isp/ispccp2.c150
-rw-r--r--drivers/media/video/omap3isp/ispccp2.h1
-rw-r--r--drivers/media/video/omap3isp/ispcsi2.c91
-rw-r--r--drivers/media/video/omap3isp/isph3a_aewb.c2
-rw-r--r--drivers/media/video/omap3isp/isph3a_af.c2
-rw-r--r--drivers/media/video/omap3isp/isphist.c2
-rw-r--r--drivers/media/video/omap3isp/isppreview.c419
-rw-r--r--drivers/media/video/omap3isp/isppreview.h9
-rw-r--r--drivers/media/video/omap3isp/ispqueue.c4
-rw-r--r--drivers/media/video/omap3isp/ispreg.h3
-rw-r--r--drivers/media/video/omap3isp/ispresizer.c104
-rw-r--r--drivers/media/video/omap3isp/ispstat.c66
-rw-r--r--drivers/media/video/omap3isp/ispstat.h2
-rw-r--r--drivers/media/video/omap3isp/ispvideo.c38
-rw-r--r--drivers/media/video/omap3isp/ispvideo.h4
-rw-r--r--drivers/media/video/ov2640.c185
-rw-r--r--drivers/media/video/ov5642.c1086
-rw-r--r--drivers/media/video/ov6650.c505
-rw-r--r--drivers/media/video/ov7670.c3
-rw-r--r--drivers/media/video/ov7670.h20
-rw-r--r--drivers/media/video/ov772x.c202
-rw-r--r--drivers/media/video/ov9640.c193
-rw-r--r--drivers/media/video/ov9640.h4
-rw-r--r--drivers/media/video/ov9740.c639
-rw-r--r--drivers/media/video/pms.c4
-rw-r--r--drivers/media/video/pvrusb2/Makefile8
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c8
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h3
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-main.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c18
-rw-r--r--drivers/media/video/pwc/Kconfig1
-rw-r--r--drivers/media/video/pwc/pwc-ctrl.c803
-rw-r--r--drivers/media/video/pwc/pwc-dec1.c28
-rw-r--r--drivers/media/video/pwc/pwc-dec1.h8
-rw-r--r--drivers/media/video/pwc/pwc-dec23.c22
-rw-r--r--drivers/media/video/pwc/pwc-dec23.h10
-rw-r--r--drivers/media/video/pwc/pwc-if.c1259
-rw-r--r--drivers/media/video/pwc/pwc-ioctl.h323
-rw-r--r--drivers/media/video/pwc/pwc-kiara.c1
-rw-r--r--drivers/media/video/pwc/pwc-misc.c4
-rw-r--r--drivers/media/video/pwc/pwc-uncompress.c17
-rw-r--r--drivers/media/video/pwc/pwc-uncompress.h40
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c1237
-rw-r--r--drivers/media/video/pwc/pwc.h409
-rw-r--r--drivers/media/video/pxa_camera.c232
-rw-r--r--drivers/media/video/rj54n1cb0c.c225
-rw-r--r--drivers/media/video/s2255drv.c35
-rw-r--r--drivers/media/video/s5k6aa.c1681
-rw-r--r--drivers/media/video/s5p-fimc/Makefile2
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c1468
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c1139
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h221
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.c858
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.h118
-rw-r--r--drivers/media/video/s5p-fimc/fimc-reg.c90
-rw-r--r--drivers/media/video/s5p-fimc/mipi-csis.c90
-rw-r--r--drivers/media/video/s5p-fimc/regs-fimc.h9
-rw-r--r--drivers/media/video/s5p-mfc/Makefile5
-rw-r--r--drivers/media/video/s5p-mfc/regs-mfc.h413
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc.c1273
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_cmd.c120
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_cmd.h30
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_common.h572
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c343
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h29
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_debug.h48
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.c1037
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.h23
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.c1830
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.h23
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_intr.c92
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_intr.h26
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_opr.c1397
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_opr.h91
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_pm.c117
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_pm.h24
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_shm.c47
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_shm.h91
-rw-r--r--drivers/media/video/s5p-tv/Kconfig76
-rw-r--r--drivers/media/video/s5p-tv/Makefile17
-rw-r--r--drivers/media/video/s5p-tv/hdmi_drv.c1043
-rw-r--r--drivers/media/video/s5p-tv/hdmiphy_drv.c188
-rw-r--r--drivers/media/video/s5p-tv/mixer.h352
-rw-r--r--drivers/media/video/s5p-tv/mixer_drv.c487
-rw-r--r--drivers/media/video/s5p-tv/mixer_grp_layer.c185
-rw-r--r--drivers/media/video/s5p-tv/mixer_reg.c548
-rw-r--r--drivers/media/video/s5p-tv/mixer_video.c1006
-rw-r--r--drivers/media/video/s5p-tv/mixer_vp_layer.c211
-rw-r--r--drivers/media/video/s5p-tv/regs-hdmi.h145
-rw-r--r--drivers/media/video/s5p-tv/regs-mixer.h122
-rw-r--r--drivers/media/video/s5p-tv/regs-sdo.h63
-rw-r--r--drivers/media/video/s5p-tv/regs-vp.h88
-rw-r--r--drivers/media/video/s5p-tv/sdo_drv.c480
-rw-r--r--drivers/media/video/saa7115.c57
-rw-r--r--drivers/media/video/saa7134/Makefile8
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c13
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c12
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c25
-rw-r--r--drivers/media/video/saa7134/saa7134-empress.c1
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c2
-rw-r--r--drivers/media/video/saa7134/saa7134.h15
-rw-r--r--drivers/media/video/saa7164/Makefile10
-rw-r--r--drivers/media/video/saa7164/saa7164-cards.c128
-rw-r--r--drivers/media/video/saa7164/saa7164-dvb.c2
-rw-r--r--drivers/media/video/saa7164/saa7164-encoder.c6
-rw-r--r--drivers/media/video/saa7164/saa7164-vbi.c6
-rw-r--r--drivers/media/video/saa7164/saa7164.h3
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c686
-rw-r--r--drivers/media/video/sh_mobile_csi2.c218
-rw-r--r--drivers/media/video/sh_vou.c4
-rw-r--r--drivers/media/video/sn9c102/sn9c102.h1
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c16
-rw-r--r--drivers/media/video/soc_camera.c542
-rw-r--r--drivers/media/video/soc_camera_platform.c53
-rw-r--r--drivers/media/video/soc_mediabus.c33
-rw-r--r--drivers/media/video/sr030pc30.c14
-rw-r--r--drivers/media/video/stk-webcam.c29
-rw-r--r--drivers/media/video/tcm825x.c1
-rw-r--r--drivers/media/video/tda7432.c5
-rw-r--r--drivers/media/video/timblogiw.c2
-rw-r--r--drivers/media/video/tlg2300/Makefile8
-rw-r--r--drivers/media/video/tlg2300/pd-common.h1
-rw-r--r--drivers/media/video/tlg2300/pd-dvb.c2
-rw-r--r--drivers/media/video/tlg2300/pd-main.c1
-rw-r--r--drivers/media/video/tlg2300/pd-radio.c2
-rw-r--r--drivers/media/video/tm6000/Kconfig (renamed from drivers/staging/tm6000/Kconfig)0
-rw-r--r--drivers/media/video/tm6000/Makefile (renamed from drivers/staging/tm6000/Makefile)0
-rw-r--r--drivers/media/video/tm6000/tm6000-alsa.c (renamed from drivers/staging/tm6000/tm6000-alsa.c)29
-rw-r--r--drivers/media/video/tm6000/tm6000-cards.c (renamed from drivers/staging/tm6000/tm6000-cards.c)51
-rw-r--r--drivers/media/video/tm6000/tm6000-core.c (renamed from drivers/staging/tm6000/tm6000-core.c)108
-rw-r--r--drivers/media/video/tm6000/tm6000-dvb.c (renamed from drivers/staging/tm6000/tm6000-dvb.c)28
-rw-r--r--drivers/media/video/tm6000/tm6000-i2c.c (renamed from drivers/staging/tm6000/tm6000-i2c.c)23
-rw-r--r--drivers/media/video/tm6000/tm6000-input.c (renamed from drivers/staging/tm6000/tm6000-input.c)5
-rw-r--r--drivers/media/video/tm6000/tm6000-regs.h (renamed from drivers/staging/tm6000/tm6000-regs.h)6
-rw-r--r--drivers/media/video/tm6000/tm6000-stds.c659
-rw-r--r--drivers/media/video/tm6000/tm6000-usb-isoc.h (renamed from drivers/staging/tm6000/tm6000-usb-isoc.h)2
-rw-r--r--drivers/media/video/tm6000/tm6000-video.c (renamed from drivers/staging/tm6000/tm6000-video.c)159
-rw-r--r--drivers/media/video/tm6000/tm6000.h (renamed from drivers/staging/tm6000/tm6000.h)14
-rw-r--r--drivers/media/video/tuner-core.c34
-rw-r--r--drivers/media/video/tvaudio.c9
-rw-r--r--drivers/media/video/tvp514x.c1
-rw-r--r--drivers/media/video/tvp5150.c1
-rw-r--r--drivers/media/video/tvp5150_reg.h17
-rw-r--r--drivers/media/video/tvp7002.c15
-rw-r--r--drivers/media/video/tw9910.c281
-rw-r--r--drivers/media/video/usbvision/Makefile4
-rw-r--r--drivers/media/video/usbvision/usbvision-cards.c1
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c12
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c12
-rw-r--r--drivers/media/video/uvc/uvc_driver.c27
-rw-r--r--drivers/media/video/uvc/uvc_entity.c2
-rw-r--r--drivers/media/video/uvc/uvc_queue.c2
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c60
-rw-r--r--drivers/media/video/uvc/uvc_video.c29
-rw-r--r--drivers/media/video/uvc/uvcvideo.h109
-rw-r--r--drivers/media/video/v4l2-common.c3
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c113
-rw-r--r--drivers/media/video/v4l2-ctrls.c909
-rw-r--r--drivers/media/video/v4l2-dev.c11
-rw-r--r--drivers/media/video/v4l2-device.c40
-rw-r--r--drivers/media/video/v4l2-event.c289
-rw-r--r--drivers/media/video/v4l2-fh.c24
-rw-r--r--drivers/media/video/v4l2-int-device.c1
-rw-r--r--drivers/media/video/v4l2-ioctl.c609
-rw-r--r--drivers/media/video/v4l2-mem2mem.c18
-rw-r--r--drivers/media/video/v4l2-subdev.c51
-rw-r--r--drivers/media/video/via-camera.c6
-rw-r--r--drivers/media/video/videobuf-dma-sg.c5
-rw-r--r--drivers/media/video/videobuf2-core.c596
-rw-r--r--drivers/media/video/videobuf2-dma-contig.c16
-rw-r--r--drivers/media/video/videobuf2-dma-sg.c14
-rw-r--r--drivers/media/video/videobuf2-memops.c13
-rw-r--r--drivers/media/video/vino.c5
-rw-r--r--drivers/media/video/vivi.c118
-rw-r--r--drivers/media/video/vpx3220.c2
-rw-r--r--drivers/media/video/w9966.c4
-rw-r--r--drivers/media/video/zoran/zoran.h4
-rw-r--r--drivers/media/video/zoran/zoran_card.c7
-rw-r--r--drivers/media/video/zoran/zoran_driver.c3
-rw-r--r--drivers/media/video/zr364xx.c9
-rw-r--r--drivers/memstick/core/memstick.c1
-rw-r--r--drivers/memstick/core/mspro_block.c1
-rw-r--r--drivers/memstick/host/jmb38x_ms.c1
-rw-r--r--drivers/memstick/host/tifm_ms.c1
-rw-r--r--drivers/message/fusion/mptbase.c92
-rw-r--r--drivers/message/fusion/mptbase.h72
-rw-r--r--drivers/message/fusion/mptsas.c47
-rw-r--r--drivers/message/fusion/mptscsih.c20
-rw-r--r--drivers/message/fusion/mptscsih.h1
-rw-r--r--drivers/message/i2o/i2o_scsi.c2
-rw-r--r--drivers/message/i2o/iop.c8
-rw-r--r--drivers/message/i2o/pci.c1
-rw-r--r--drivers/mfd/Kconfig126
-rw-r--r--drivers/mfd/Makefile15
-rw-r--r--drivers/mfd/aat2870-core.c535
-rw-r--r--drivers/mfd/ab3100-core.c3
-rw-r--r--drivers/mfd/ab3550-core.c1399
-rw-r--r--drivers/mfd/ab5500-core.c1440
-rw-r--r--drivers/mfd/ab5500-core.h87
-rw-r--r--drivers/mfd/ab5500-debugfs.c807
-rw-r--r--drivers/mfd/ab5500-debugfs.h22
-rw-r--r--drivers/mfd/ab8500-core.c264
-rw-r--r--drivers/mfd/ab8500-debugfs.c42
-rw-r--r--drivers/mfd/ab8500-gpadc.c56
-rw-r--r--drivers/mfd/ab8500-sysctrl.c1
-rw-r--r--drivers/mfd/abx500-core.c1
-rw-r--r--drivers/mfd/asic3.c27
-rw-r--r--drivers/mfd/da903x.c2
-rw-r--r--drivers/mfd/db5500-prcmu.c29
-rw-r--r--drivers/mfd/db8500-prcmu-regs.h166
-rw-r--r--drivers/mfd/db8500-prcmu.c629
-rw-r--r--drivers/mfd/dbx500-prcmu-regs.h (renamed from drivers/mfd/db5500-prcmu-regs.h)105
-rw-r--r--drivers/mfd/dm355evm_msp.c1
-rw-r--r--drivers/mfd/intel_msic.c502
-rw-r--r--drivers/mfd/jz4740-adc.c92
-rw-r--r--drivers/mfd/lpc_sch.c49
-rw-r--r--drivers/mfd/max8997-irq.c2
-rw-r--r--drivers/mfd/max8997.c33
-rw-r--r--drivers/mfd/max8998.c2
-rw-r--r--drivers/mfd/mc13xxx-core.c116
-rw-r--r--drivers/mfd/menelaus.c4
-rw-r--r--drivers/mfd/mfd-core.c1
-rw-r--r--drivers/mfd/omap-usb-host.c6
-rw-r--r--drivers/mfd/pcf50633-core.c114
-rw-r--r--drivers/mfd/pcf50633-irq.c1
-rw-r--r--drivers/mfd/stmpe.c2
-rw-r--r--drivers/mfd/stmpe.h1
-rw-r--r--drivers/mfd/tc3589x.c2
-rw-r--r--drivers/mfd/tc6387xb.c2
-rw-r--r--drivers/mfd/timberdale.c17
-rw-r--r--drivers/mfd/tmio_core.c1
-rw-r--r--drivers/mfd/tps65910-irq.c2
-rw-r--r--drivers/mfd/tps65910.c13
-rw-r--r--drivers/mfd/tps65911-comparator.c2
-rw-r--r--drivers/mfd/tps65912-core.c177
-rw-r--r--drivers/mfd/tps65912-i2c.c139
-rw-r--r--drivers/mfd/tps65912-irq.c224
-rw-r--r--drivers/mfd/tps65912-spi.c142
-rw-r--r--drivers/mfd/twl-core.c18
-rw-r--r--drivers/mfd/twl4030-audio.c277
-rw-r--r--drivers/mfd/twl4030-codec.c277
-rw-r--r--drivers/mfd/twl4030-irq.c342
-rw-r--r--drivers/mfd/twl4030-madc.c35
-rw-r--r--drivers/mfd/twl6030-irq.c76
-rw-r--r--drivers/mfd/twl6030-pwm.c2
-rw-r--r--drivers/mfd/twl6040-core.c639
-rw-r--r--drivers/mfd/twl6040-irq.c191
-rw-r--r--drivers/mfd/wl1273-core.c1
-rw-r--r--drivers/mfd/wm831x-auxadc.c299
-rw-r--r--drivers/mfd/wm831x-core.c694
-rw-r--r--drivers/mfd/wm831x-i2c.c76
-rw-r--r--drivers/mfd/wm831x-irq.c99
-rw-r--r--drivers/mfd/wm831x-spi.c204
-rw-r--r--drivers/mfd/wm8350-gpio.c4
-rw-r--r--drivers/mfd/wm8350-irq.c18
-rw-r--r--drivers/mfd/wm8400-core.c107
-rw-r--r--drivers/mfd/wm8994-core.c287
-rw-r--r--drivers/mfd/wm8994-irq.c12
-rw-r--r--drivers/misc/Kconfig31
-rw-r--r--drivers/misc/Makefile4
-rw-r--r--drivers/misc/ab8500-pwm.c3
-rw-r--r--drivers/misc/ad525x_dpot-i2c.c1
-rw-r--r--drivers/misc/ad525x_dpot.h2
-rw-r--r--drivers/misc/altera-stapl/Kconfig (renamed from drivers/staging/altera-stapl/Kconfig)0
-rw-r--r--drivers/misc/altera-stapl/Makefile (renamed from drivers/staging/altera-stapl/Makefile)0
-rw-r--r--drivers/misc/altera-stapl/altera-comp.c (renamed from drivers/staging/altera-stapl/altera-comp.c)0
-rw-r--r--drivers/misc/altera-stapl/altera-exprt.h (renamed from drivers/staging/altera-stapl/altera-exprt.h)0
-rw-r--r--drivers/misc/altera-stapl/altera-jtag.c (renamed from drivers/staging/altera-stapl/altera-jtag.c)2
-rw-r--r--drivers/misc/altera-stapl/altera-jtag.h (renamed from drivers/staging/altera-stapl/altera-jtag.h)0
-rw-r--r--drivers/misc/altera-stapl/altera-lpt.c (renamed from drivers/staging/altera-stapl/altera-lpt.c)0
-rw-r--r--drivers/misc/altera-stapl/altera.c (renamed from drivers/staging/altera-stapl/altera.c)38
-rw-r--r--drivers/misc/atmel-ssc.c3
-rw-r--r--drivers/misc/atmel_pwm.c2
-rw-r--r--drivers/misc/atmel_tclib.c1
-rw-r--r--drivers/misc/bh1780gli.c1
-rw-r--r--drivers/misc/carma/carma-fpga-program.c9
-rw-r--r--drivers/misc/carma/carma-fpga.c9
-rw-r--r--drivers/misc/cb710/core.c3
-rw-r--r--drivers/misc/eeprom/Kconfig25
-rw-r--r--drivers/misc/eeprom/Makefile2
-rw-r--r--drivers/misc/eeprom/digsy_mtc_eeprom.c85
-rw-r--r--drivers/misc/eeprom/eeprom_93xx46.c410
-rw-r--r--drivers/misc/fsa9480.c558
-rw-r--r--drivers/misc/kgdbts.c1
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.c403
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.h9
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_i2c.c45
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_spi.c2
-rw-r--r--drivers/misc/pch_phub.c100
-rw-r--r--drivers/misc/phantom.c2
-rw-r--r--drivers/misc/pti.c107
-rw-r--r--drivers/misc/sgi-gru/grukservices.c1
-rw-r--r--drivers/misc/spear13xx_pcie_gadget.c2
-rw-r--r--drivers/misc/ti-st/st_core.c10
-rw-r--r--drivers/misc/ti-st/st_kim.c34
-rw-r--r--drivers/misc/ti-st/st_ll.c19
-rw-r--r--drivers/misc/tifm_7xx1.c1
-rw-r--r--drivers/misc/tifm_core.c1
-rw-r--r--drivers/misc/vmw_balloon.c35
-rw-r--r--drivers/mmc/card/block.c890
-rw-r--r--drivers/mmc/card/mmc_test.c593
-rw-r--r--drivers/mmc/card/queue.c219
-rw-r--r--drivers/mmc/card/queue.h33
-rw-r--r--drivers/mmc/card/sdio_uart.c10
-rw-r--r--drivers/mmc/core/bus.c8
-rw-r--r--drivers/mmc/core/core.c655
-rw-r--r--drivers/mmc/core/core.h1
-rw-r--r--drivers/mmc/core/debugfs.c29
-rw-r--r--drivers/mmc/core/host.c24
-rw-r--r--drivers/mmc/core/host.h8
-rw-r--r--drivers/mmc/core/mmc.c367
-rw-r--r--drivers/mmc/core/mmc_ops.c42
-rw-r--r--drivers/mmc/core/mmc_ops.h1
-rw-r--r--drivers/mmc/core/quirks.c12
-rw-r--r--drivers/mmc/core/sd.c197
-rw-r--r--drivers/mmc/core/sd_ops.c9
-rw-r--r--drivers/mmc/core/sdio.c11
-rw-r--r--drivers/mmc/core/sdio_bus.c11
-rw-r--r--drivers/mmc/core/sdio_cis.c4
-rw-r--r--drivers/mmc/core/sdio_io.c1
-rw-r--r--drivers/mmc/core/sdio_irq.c7
-rw-r--r--drivers/mmc/core/sdio_ops.c9
-rw-r--r--drivers/mmc/host/Kconfig98
-rw-r--r--drivers/mmc/host/Makefile25
-rw-r--r--drivers/mmc/host/at91_mci.c9
-rw-r--r--drivers/mmc/host/at91_mci.h115
-rw-r--r--drivers/mmc/host/atmel-mci-regs.h220
-rw-r--r--drivers/mmc/host/atmel-mci.c882
-rw-r--r--drivers/mmc/host/au1xmmc.c98
-rw-r--r--drivers/mmc/host/davinci_mmc.c13
-rw-r--r--drivers/mmc/host/dw_mmc.c516
-rw-r--r--drivers/mmc/host/dw_mmc.h32
-rw-r--r--drivers/mmc/host/imxmmc.c2
-rw-r--r--drivers/mmc/host/mmc_spi.c1
-rw-r--r--drivers/mmc/host/mmci.c178
-rw-r--r--drivers/mmc/host/mmci.h8
-rw-r--r--drivers/mmc/host/msm_sdcc.c86
-rw-r--r--drivers/mmc/host/msm_sdcc.h6
-rw-r--r--drivers/mmc/host/mvsdio.c14
-rw-r--r--drivers/mmc/host/mxcmmc.c11
-rw-r--r--drivers/mmc/host/mxs-mmc.c31
-rw-r--r--drivers/mmc/host/omap.c2
-rw-r--r--drivers/mmc/host/omap_hsmmc.c699
-rw-r--r--drivers/mmc/host/pxamci.c2
-rw-r--r--drivers/mmc/host/s3cmci.c10
-rw-r--r--drivers/mmc/host/sdhci-cns3xxx.c44
-rw-r--r--drivers/mmc/host/sdhci-dove.c43
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c457
-rw-r--r--drivers/mmc/host/sdhci-of-core.c253
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c103
-rw-r--r--drivers/mmc/host/sdhci-of-hlwd.c68
-rw-r--r--drivers/mmc/host/sdhci-of.h42
-rw-r--r--drivers/mmc/host/sdhci-pci.c320
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c220
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h90
-rw-r--r--drivers/mmc/host/sdhci-pxa.c303
-rw-r--r--drivers/mmc/host/sdhci-pxav2.c245
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c291
-rw-r--r--drivers/mmc/host/sdhci-s3c.c46
-rw-r--r--drivers/mmc/host/sdhci-spear.c3
-rw-r--r--drivers/mmc/host/sdhci-tegra.c175
-rw-r--r--drivers/mmc/host/sdhci.c435
-rw-r--r--drivers/mmc/host/sdhci.h7
-rw-r--r--drivers/mmc/host/sdricoh_cs.c1
-rw-r--r--drivers/mmc/host/sh_mmcif.c48
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c135
-rw-r--r--drivers/mmc/host/tifm_sd.c19
-rw-r--r--drivers/mmc/host/tmio_mmc.c6
-rw-r--r--drivers/mmc/host/tmio_mmc.h61
-rw-r--r--drivers/mmc/host/tmio_mmc_dma.c8
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c308
-rw-r--r--drivers/mmc/host/via-sdmmc.c3
-rw-r--r--drivers/mmc/host/vub300.c4
-rw-r--r--drivers/mmc/host/wbsd.c22
-rw-r--r--drivers/mtd/Kconfig23
-rw-r--r--drivers/mtd/Makefile2
-rw-r--r--drivers/mtd/afs.c4
-rw-r--r--drivers/mtd/ar7part.c3
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c31
-rw-r--r--drivers/mtd/chips/fwh_lock.h3
-rw-r--r--drivers/mtd/chips/jedec_probe.c34
-rw-r--r--drivers/mtd/cmdlinepart.c8
-rw-r--r--drivers/mtd/devices/Kconfig13
-rw-r--r--drivers/mtd/devices/Makefile3
-rw-r--r--drivers/mtd/devices/doc2000.c17
-rw-r--r--drivers/mtd/devices/doc2001.c11
-rw-r--r--drivers/mtd/devices/doc2001plus.c11
-rw-r--r--drivers/mtd/devices/docecc.c2
-rw-r--r--drivers/mtd/devices/docg3.c1114
-rw-r--r--drivers/mtd/devices/docg3.h297
-rw-r--r--drivers/mtd/devices/docprobe.c5
-rw-r--r--drivers/mtd/devices/lart.c18
-rw-r--r--drivers/mtd/devices/m25p80.c92
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c91
-rw-r--r--drivers/mtd/devices/sst25l.c46
-rw-r--r--drivers/mtd/ftl.c40
-rw-r--r--drivers/mtd/inftlcore.c69
-rw-r--r--drivers/mtd/inftlmount.c116
-rw-r--r--drivers/mtd/lpddr/lpddr_cmds.c1
-rw-r--r--drivers/mtd/maps/Kconfig26
-rw-r--r--drivers/mtd/maps/Makefile2
-rw-r--r--drivers/mtd/maps/bcm963xx-flash.c1
-rw-r--r--drivers/mtd/maps/bfin-async-flash.c18
-rw-r--r--drivers/mtd/maps/ceiva.c341
-rw-r--r--drivers/mtd/maps/dc21285.c9
-rw-r--r--drivers/mtd/maps/edb7312.c134
-rw-r--r--drivers/mtd/maps/gpio-addr-flash.c16
-rw-r--r--drivers/mtd/maps/h720x-flash.c23
-rw-r--r--drivers/mtd/maps/impa7.c28
-rw-r--r--drivers/mtd/maps/intel_vr_nor.c7
-rw-r--r--drivers/mtd/maps/ixp2000.c22
-rw-r--r--drivers/mtd/maps/ixp4xx.c29
-rw-r--r--drivers/mtd/maps/lantiq-flash.c19
-rw-r--r--drivers/mtd/maps/latch-addr-flash.c24
-rw-r--r--drivers/mtd/maps/pcmciamtd.c124
-rw-r--r--drivers/mtd/maps/physmap.c38
-rw-r--r--drivers/mtd/maps/physmap_of.c80
-rw-r--r--drivers/mtd/maps/plat-ram.c23
-rw-r--r--drivers/mtd/maps/pxa2xx-flash.c22
-rw-r--r--drivers/mtd/maps/rbtx4939-flash.c24
-rw-r--r--drivers/mtd/maps/sa1100-flash.c30
-rw-r--r--drivers/mtd/maps/solutionengine.c30
-rw-r--r--drivers/mtd/maps/wr_sbc82xx_flash.c33
-rw-r--r--drivers/mtd/mtd_blkdevs.c2
-rw-r--r--drivers/mtd/mtdblock.c18
-rw-r--r--drivers/mtd/mtdblock_ro.c1
-rw-r--r--drivers/mtd/mtdchar.c166
-rw-r--r--drivers/mtd/mtdconcat.c10
-rw-r--r--drivers/mtd/mtdcore.c70
-rw-r--r--drivers/mtd/mtdcore.h3
-rw-r--r--drivers/mtd/mtdoops.c2
-rw-r--r--drivers/mtd/mtdpart.c62
-rw-r--r--drivers/mtd/mtdsuper.c21
-rw-r--r--drivers/mtd/mtdswap.c31
-rw-r--r--drivers/mtd/nand/Kconfig31
-rw-r--r--drivers/mtd/nand/Makefile2
-rw-r--r--drivers/mtd/nand/ams-delta.c2
-rw-r--r--drivers/mtd/nand/atmel_nand.c79
-rw-r--r--drivers/mtd/nand/au1550nd.c35
-rw-r--r--drivers/mtd/nand/autcpu12.c4
-rw-r--r--drivers/mtd/nand/bcm_umi_nand.c59
-rw-r--r--drivers/mtd/nand/cafe_nand.c22
-rw-r--r--drivers/mtd/nand/cmx270_nand.c24
-rw-r--r--drivers/mtd/nand/cs553x_nand.c15
-rw-r--r--drivers/mtd/nand/davinci_nand.c39
-rw-r--r--drivers/mtd/nand/denali.c6
-rw-r--r--drivers/mtd/nand/diskonchip.c9
-rw-r--r--drivers/mtd/nand/edb7312.c203
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c75
-rw-r--r--drivers/mtd/nand/fsl_upm.c16
-rw-r--r--drivers/mtd/nand/fsmc_nand.c77
-rw-r--r--drivers/mtd/nand/gpmi-nand/Makefile3
-rw-r--r--drivers/mtd/nand/gpmi-nand/bch-regs.h84
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-lib.c1057
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c1619
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.h273
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-regs.h172
-rw-r--r--drivers/mtd/nand/h1910.c19
-rw-r--r--drivers/mtd/nand/jz4740_nand.c18
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c24
-rw-r--r--drivers/mtd/nand/mxc_nand.c37
-rw-r--r--drivers/mtd/nand/nand_base.c1109
-rw-r--r--drivers/mtd/nand/nand_bbt.c693
-rw-r--r--drivers/mtd/nand/nand_bch.c4
-rw-r--r--drivers/mtd/nand/nand_ecc.c10
-rw-r--r--drivers/mtd/nand/nandsim.c4
-rw-r--r--drivers/mtd/nand/ndfc.c22
-rw-r--r--drivers/mtd/nand/nomadik_nand.c1
-rw-r--r--drivers/mtd/nand/nuc900_nand.c1
-rw-r--r--drivers/mtd/nand/omap2.c23
-rw-r--r--drivers/mtd/nand/orion_nand.c16
-rw-r--r--drivers/mtd/nand/pasemi_nand.c3
-rw-r--r--drivers/mtd/nand/plat_nand.c25
-rw-r--r--drivers/mtd/nand/ppchameleonevb.c47
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c471
-rw-r--r--drivers/mtd/nand/r852.c6
-rw-r--r--drivers/mtd/nand/rtc_from4.c5
-rw-r--r--drivers/mtd/nand/s3c2410.c27
-rw-r--r--drivers/mtd/nand/sharpsl.c13
-rw-r--r--drivers/mtd/nand/sm_common.c3
-rw-r--r--drivers/mtd/nand/socrates_nand.c28
-rw-r--r--drivers/mtd/nand/tmio_nand.c17
-rw-r--r--drivers/mtd/nand/txx9ndfmc.c8
-rw-r--r--drivers/mtd/nftlcore.c37
-rw-r--r--drivers/mtd/nftlmount.c26
-rw-r--r--drivers/mtd/ofpart.c112
-rw-r--r--drivers/mtd/onenand/generic.c14
-rw-r--r--drivers/mtd/onenand/omap2.c18
-rw-r--r--drivers/mtd/onenand/onenand_base.c114
-rw-r--r--drivers/mtd/onenand/onenand_bbt.c9
-rw-r--r--drivers/mtd/onenand/samsung.c13
-rw-r--r--drivers/mtd/redboot.c17
-rw-r--r--drivers/mtd/rfd_ftl.c1
-rw-r--r--drivers/mtd/sm_ftl.c26
-rw-r--r--drivers/mtd/ssfdc.c46
-rw-r--r--drivers/mtd/tests/mtd_oobtest.c33
-rw-r--r--drivers/mtd/tests/mtd_pagetest.c37
-rw-r--r--drivers/mtd/tests/mtd_readtest.c13
-rw-r--r--drivers/mtd/tests/mtd_speedtest.c17
-rw-r--r--drivers/mtd/tests/mtd_stresstest.c11
-rw-r--r--drivers/mtd/tests/mtd_subpagetest.c17
-rw-r--r--drivers/mtd/tests/mtd_torturetest.c11
-rw-r--r--drivers/mtd/ubi/build.c28
-rw-r--r--drivers/mtd/ubi/cdev.c10
-rw-r--r--drivers/mtd/ubi/debug.c269
-rw-r--r--drivers/mtd/ubi/debug.h115
-rw-r--r--drivers/mtd/ubi/eba.c2
-rw-r--r--drivers/mtd/ubi/io.c44
-rw-r--r--drivers/mtd/ubi/kapi.c2
-rw-r--r--drivers/mtd/ubi/misc.c2
-rw-r--r--drivers/mtd/ubi/scan.c6
-rw-r--r--drivers/mtd/ubi/ubi.h8
-rw-r--r--drivers/mtd/ubi/vmt.c3
-rw-r--r--drivers/mtd/ubi/vtbl.c20
-rw-r--r--drivers/mtd/ubi/wl.c42
-rw-r--r--drivers/net/Kconfig3390
-rw-r--r--drivers/net/Makefile328
-rw-r--r--drivers/net/appletalk/cops.c2
-rw-r--r--drivers/net/appletalk/ltpc.c10
-rw-r--r--drivers/net/arcnet/Kconfig14
-rw-r--r--drivers/net/arcnet/Makefile1
-rw-r--r--drivers/net/arcnet/arc-rimi.c1
-rw-r--r--drivers/net/arcnet/com20020-isa.c1
-rw-r--r--drivers/net/arcnet/com20020-pci.c1
-rw-r--r--drivers/net/arcnet/com20020.c3
-rw-r--r--drivers/net/arcnet/com20020_cs.c (renamed from drivers/net/pcmcia/com20020_cs.c)0
-rw-r--r--drivers/net/arcnet/com90io.c1
-rw-r--r--drivers/net/arcnet/com90xx.c1
-rw-r--r--drivers/net/ariadne.c858
-rw-r--r--drivers/net/arm/Kconfig74
-rw-r--r--drivers/net/arm/Makefile14
-rw-r--r--drivers/net/benet/Kconfig6
-rw-r--r--drivers/net/bna/bfa_ioc_ct.c516
-rw-r--r--drivers/net/bna/bfa_wc.h69
-rw-r--r--drivers/net/bna/bfi_ctreg.h646
-rw-r--r--drivers/net/bna/bfi_ll.h438
-rw-r--r--drivers/net/bna/bna_ctrl.c3077
-rw-r--r--drivers/net/bna/bna_hw.h1490
-rw-r--r--drivers/net/bna/bna_txrx.c4185
-rw-r--r--drivers/net/bnx2x/bnx2x_dump.h713
-rw-r--r--drivers/net/bnx2x/bnx2x_fw_defs.h531
-rw-r--r--drivers/net/bnx2x/bnx2x_hsi.h3732
-rw-r--r--drivers/net/bnx2x/bnx2x_init.h416
-rw-r--r--drivers/net/bonding/bond_3ad.c65
-rw-r--r--drivers/net/bonding/bond_3ad.h8
-rw-r--r--drivers/net/bonding/bond_alb.c17
-rw-r--r--drivers/net/bonding/bond_ipv6.c8
-rw-r--r--drivers/net/bonding/bond_main.c360
-rw-r--r--drivers/net/bonding/bond_procfs.c14
-rw-r--r--drivers/net/bonding/bond_sysfs.c220
-rw-r--r--drivers/net/bonding/bonding.h17
-rw-r--r--drivers/net/caif/Kconfig9
-rw-r--r--drivers/net/caif/Makefile3
-rw-r--r--drivers/net/caif/caif_hsi.c1285
-rw-r--r--drivers/net/caif/caif_serial.c2
-rw-r--r--drivers/net/caif/caif_shm_u5500.c1
-rw-r--r--drivers/net/caif/caif_shmcore.c2
-rw-r--r--drivers/net/caif/caif_spi.c5
-rw-r--r--drivers/net/caif/caif_spi_slave.c1
-rw-r--r--drivers/net/can/Kconfig8
-rw-r--r--drivers/net/can/at91_can.c368
-rw-r--r--drivers/net/can/bfin_can.c137
-rw-r--r--drivers/net/can/c_can/c_can.c2
-rw-r--r--drivers/net/can/c_can/c_can_platform.c2
-rw-r--r--drivers/net/can/dev.c16
-rw-r--r--drivers/net/can/flexcan.c189
-rw-r--r--drivers/net/can/janz-ican3.c1
-rw-r--r--drivers/net/can/mscan/mscan.c37
-rw-r--r--drivers/net/can/sja1000/Kconfig14
-rw-r--r--drivers/net/can/sja1000/Makefile2
-rw-r--r--drivers/net/can/sja1000/ems_pcmcia.c331
-rw-r--r--drivers/net/can/sja1000/peak_pci.c291
-rw-r--r--drivers/net/can/sja1000/plx_pci.c4
-rw-r--r--drivers/net/can/sja1000/sja1000.c2
-rw-r--r--drivers/net/can/sja1000/sja1000.h3
-rw-r--r--drivers/net/can/sja1000/sja1000_of_platform.c3
-rw-r--r--drivers/net/can/slcan.c67
-rw-r--r--drivers/net/can/softing/softing_fw.c1
-rw-r--r--drivers/net/can/softing/softing_main.c4
-rw-r--r--drivers/net/can/ti_hecc.c4
-rw-r--r--drivers/net/can/vcan.c2
-rw-r--r--drivers/net/cris/eth_v10.c2
-rw-r--r--drivers/net/dummy.c2
-rw-r--r--drivers/net/ethernet/3com/3c501.c (renamed from drivers/net/3c501.c)2
-rw-r--r--drivers/net/ethernet/3com/3c501.h (renamed from drivers/net/3c501.h)0
-rw-r--r--drivers/net/ethernet/3com/3c509.c (renamed from drivers/net/3c509.c)2
-rw-r--r--drivers/net/ethernet/3com/3c515.c (renamed from drivers/net/3c515.c)2
-rw-r--r--drivers/net/ethernet/3com/3c574_cs.c (renamed from drivers/net/pcmcia/3c574_cs.c)2
-rw-r--r--drivers/net/ethernet/3com/3c589_cs.c (renamed from drivers/net/pcmcia/3c589_cs.c)2
-rw-r--r--drivers/net/ethernet/3com/3c59x.c (renamed from drivers/net/3c59x.c)15
-rw-r--r--drivers/net/ethernet/3com/Kconfig122
-rw-r--r--drivers/net/ethernet/3com/Makefile11
-rw-r--r--drivers/net/ethernet/3com/typhoon.c (renamed from drivers/net/typhoon.c)17
-rw-r--r--drivers/net/ethernet/3com/typhoon.h (renamed from drivers/net/typhoon.h)0
-rw-r--r--drivers/net/ethernet/8390/3c503.c (renamed from drivers/net/3c503.c)3
-rw-r--r--drivers/net/ethernet/8390/3c503.h (renamed from drivers/net/3c503.h)0
-rw-r--r--drivers/net/ethernet/8390/8390.c (renamed from drivers/net/8390.c)2
-rw-r--r--drivers/net/ethernet/8390/8390.h (renamed from drivers/net/8390.h)1
-rw-r--r--drivers/net/ethernet/8390/8390p.c (renamed from drivers/net/8390p.c)2
-rw-r--r--drivers/net/ethernet/8390/Kconfig337
-rw-r--r--drivers/net/ethernet/8390/Makefile29
-rw-r--r--drivers/net/ethernet/8390/ac3200.c (renamed from drivers/net/ac3200.c)3
-rw-r--r--drivers/net/ethernet/8390/apne.c (renamed from drivers/net/apne.c)1
-rw-r--r--drivers/net/ethernet/8390/ax88796.c (renamed from drivers/net/ax88796.c)2
-rw-r--r--drivers/net/ethernet/8390/axnet_cs.c (renamed from drivers/net/pcmcia/axnet_cs.c)4
-rw-r--r--drivers/net/ethernet/8390/e2100.c (renamed from drivers/net/e2100.c)3
-rw-r--r--drivers/net/ethernet/8390/es3210.c (renamed from drivers/net/es3210.c)1
-rw-r--r--drivers/net/ethernet/8390/etherh.c (renamed from drivers/net/arm/etherh.c)4
-rw-r--r--drivers/net/ethernet/8390/hp-plus.c (renamed from drivers/net/hp-plus.c)3
-rw-r--r--drivers/net/ethernet/8390/hp.c (renamed from drivers/net/hp.c)1
-rw-r--r--drivers/net/ethernet/8390/hydra.c (renamed from drivers/net/hydra.c)2
-rw-r--r--drivers/net/ethernet/8390/lib8390.c (renamed from drivers/net/lib8390.c)290
-rw-r--r--drivers/net/ethernet/8390/lne390.c (renamed from drivers/net/lne390.c)1
-rw-r--r--drivers/net/ethernet/8390/mac8390.c (renamed from drivers/net/mac8390.c)2
-rw-r--r--drivers/net/ethernet/8390/ne-h8300.c (renamed from drivers/net/ne-h8300.c)2
-rw-r--r--drivers/net/ethernet/8390/ne.c (renamed from drivers/net/ne.c)0
-rw-r--r--drivers/net/ethernet/8390/ne2.c (renamed from drivers/net/ne2.c)0
-rw-r--r--drivers/net/ethernet/8390/ne2k-pci.c (renamed from drivers/net/ne2k-pci.c)2
-rw-r--r--drivers/net/ethernet/8390/ne3210.c (renamed from drivers/net/ne3210.c)0
-rw-r--r--drivers/net/ethernet/8390/pcnet_cs.c (renamed from drivers/net/pcmcia/pcnet_cs.c)4
-rw-r--r--drivers/net/ethernet/8390/smc-mca.c (renamed from drivers/net/smc-mca.c)3
-rw-r--r--drivers/net/ethernet/8390/smc-ultra.c (renamed from drivers/net/smc-ultra.c)3
-rw-r--r--drivers/net/ethernet/8390/smc-ultra32.c (renamed from drivers/net/smc-ultra32.c)2
-rw-r--r--drivers/net/ethernet/8390/stnic.c (renamed from drivers/net/stnic.c)0
-rw-r--r--drivers/net/ethernet/8390/wd.c (renamed from drivers/net/wd.c)2
-rw-r--r--drivers/net/ethernet/8390/zorro8390.c452
-rw-r--r--drivers/net/ethernet/Kconfig178
-rw-r--r--drivers/net/ethernet/Makefile75
-rw-r--r--drivers/net/ethernet/adaptec/Kconfig36
-rw-r--r--drivers/net/ethernet/adaptec/Makefile5
-rw-r--r--drivers/net/ethernet/adaptec/starfire.c (renamed from drivers/net/starfire.c)103
-rw-r--r--drivers/net/ethernet/adi/Kconfig69
-rw-r--r--drivers/net/ethernet/adi/Makefile5
-rw-r--r--drivers/net/ethernet/adi/bfin_mac.c (renamed from drivers/net/bfin_mac.c)2
-rw-r--r--drivers/net/ethernet/adi/bfin_mac.h (renamed from drivers/net/bfin_mac.h)0
-rw-r--r--drivers/net/ethernet/aeroflex/Kconfig11
-rw-r--r--drivers/net/ethernet/aeroflex/Makefile5
-rw-r--r--drivers/net/ethernet/aeroflex/greth.c (renamed from drivers/net/greth.c)34
-rw-r--r--drivers/net/ethernet/aeroflex/greth.h (renamed from drivers/net/greth.h)1
-rw-r--r--drivers/net/ethernet/alteon/Kconfig48
-rw-r--r--drivers/net/ethernet/alteon/Makefile5
-rw-r--r--drivers/net/ethernet/alteon/acenic.c (renamed from drivers/net/acenic.c)121
-rw-r--r--drivers/net/ethernet/alteon/acenic.h (renamed from drivers/net/acenic.h)14
-rw-r--r--drivers/net/ethernet/amd/7990.c (renamed from drivers/net/7990.c)9
-rw-r--r--drivers/net/ethernet/amd/7990.h (renamed from drivers/net/7990.h)0
-rw-r--r--drivers/net/ethernet/amd/Kconfig195
-rw-r--r--drivers/net/ethernet/amd/Makefile20
-rw-r--r--drivers/net/ethernet/amd/a2065.c (renamed from drivers/net/a2065.c)358
-rw-r--r--drivers/net/ethernet/amd/a2065.h (renamed from drivers/net/a2065.h)0
-rw-r--r--drivers/net/ethernet/amd/am79c961a.c (renamed from drivers/net/arm/am79c961a.c)19
-rw-r--r--drivers/net/ethernet/amd/am79c961a.h (renamed from drivers/net/arm/am79c961a.h)0
-rw-r--r--drivers/net/ethernet/amd/amd8111e.c (renamed from drivers/net/amd8111e.c)39
-rw-r--r--drivers/net/ethernet/amd/amd8111e.h (renamed from drivers/net/amd8111e.h)3
-rw-r--r--drivers/net/ethernet/amd/ariadne.c793
-rw-r--r--drivers/net/ethernet/amd/ariadne.h (renamed from drivers/net/ariadne.h)0
-rw-r--r--drivers/net/ethernet/amd/atarilance.c (renamed from drivers/net/atarilance.c)2
-rw-r--r--drivers/net/ethernet/amd/au1000_eth.c (renamed from drivers/net/au1000_eth.c)50
-rw-r--r--drivers/net/ethernet/amd/au1000_eth.h (renamed from drivers/net/au1000_eth.h)2
-rw-r--r--drivers/net/ethernet/amd/declance.c (renamed from drivers/net/declance.c)49
-rw-r--r--drivers/net/ethernet/amd/depca.c (renamed from drivers/net/depca.c)33
-rw-r--r--drivers/net/ethernet/amd/depca.h (renamed from drivers/net/depca.h)2
-rw-r--r--drivers/net/ethernet/amd/hplance.c (renamed from drivers/net/hplance.c)2
-rw-r--r--drivers/net/ethernet/amd/hplance.h (renamed from drivers/net/hplance.h)0
-rw-r--r--drivers/net/ethernet/amd/lance.c (renamed from drivers/net/lance.c)2
-rw-r--r--drivers/net/ethernet/amd/mvme147.c (renamed from drivers/net/mvme147.c)2
-rw-r--r--drivers/net/ethernet/amd/ni65.c (renamed from drivers/net/ni65.c)2
-rw-r--r--drivers/net/ethernet/amd/ni65.h (renamed from drivers/net/ni65.h)0
-rw-r--r--drivers/net/ethernet/amd/nmclan_cs.c (renamed from drivers/net/pcmcia/nmclan_cs.c)2
-rw-r--r--drivers/net/ethernet/amd/pcnet32.c (renamed from drivers/net/pcnet32.c)215
-rw-r--r--drivers/net/ethernet/amd/sun3lance.c (renamed from drivers/net/sun3lance.c)2
-rw-r--r--drivers/net/ethernet/amd/sunlance.c (renamed from drivers/net/sunlance.c)10
-rw-r--r--drivers/net/ethernet/apple/Kconfig65
-rw-r--r--drivers/net/ethernet/apple/Makefile7
-rw-r--r--drivers/net/ethernet/apple/bmac.c (renamed from drivers/net/bmac.c)11
-rw-r--r--drivers/net/ethernet/apple/bmac.h (renamed from drivers/net/bmac.h)0
-rw-r--r--drivers/net/ethernet/apple/mace.c (renamed from drivers/net/mace.c)3
-rw-r--r--drivers/net/ethernet/apple/mace.h (renamed from drivers/net/mace.h)0
-rw-r--r--drivers/net/ethernet/apple/macmace.c (renamed from drivers/net/macmace.c)13
-rw-r--r--drivers/net/ethernet/atheros/Kconfig70
-rw-r--r--drivers/net/ethernet/atheros/Makefile8
-rw-r--r--drivers/net/ethernet/atheros/atl1c/Makefile (renamed from drivers/net/atl1c/Makefile)0
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c.h (renamed from drivers/net/atl1c/atl1c.h)3
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c (renamed from drivers/net/atl1c/atl1c_ethtool.c)0
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_hw.c (renamed from drivers/net/atl1c/atl1c_hw.c)0
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_hw.h (renamed from drivers/net/atl1c/atl1c_hw.h)0
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_main.c (renamed from drivers/net/atl1c/atl1c_main.c)87
-rw-r--r--drivers/net/ethernet/atheros/atl1e/Makefile (renamed from drivers/net/atl1e/Makefile)0
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e.h (renamed from drivers/net/atl1e/atl1e.h)3
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c (renamed from drivers/net/atl1e/atl1e_ethtool.c)0
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_hw.c (renamed from drivers/net/atl1e/atl1e_hw.c)0
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_hw.h (renamed from drivers/net/atl1e/atl1e_hw.h)0
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_main.c (renamed from drivers/net/atl1e/atl1e_main.c)103
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_param.c (renamed from drivers/net/atl1e/atl1e_param.c)0
-rw-r--r--drivers/net/ethernet/atheros/atlx/Makefile (renamed from drivers/net/atlx/Makefile)0
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl1.c (renamed from drivers/net/atlx/atl1.c)50
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl1.h (renamed from drivers/net/atlx/atl1.h)1
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl2.c (renamed from drivers/net/atlx/atl2.c)85
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl2.h (renamed from drivers/net/atlx/atl2.h)5
-rw-r--r--drivers/net/ethernet/atheros/atlx/atlx.c (renamed from drivers/net/atlx/atlx.c)57
-rw-r--r--drivers/net/ethernet/atheros/atlx/atlx.h (renamed from drivers/net/atlx/atlx.h)0
-rw-r--r--drivers/net/ethernet/broadcom/Kconfig122
-rw-r--r--drivers/net/ethernet/broadcom/Makefile11
-rw-r--r--drivers/net/ethernet/broadcom/b44.c (renamed from drivers/net/b44.c)25
-rw-r--r--drivers/net/ethernet/broadcom/b44.h (renamed from drivers/net/b44.h)0
-rw-r--r--drivers/net/ethernet/broadcom/bcm63xx_enet.c (renamed from drivers/net/bcm63xx_enet.c)13
-rw-r--r--drivers/net/ethernet/broadcom/bcm63xx_enet.h (renamed from drivers/net/bcm63xx_enet.h)0
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.c (renamed from drivers/net/bnx2.c)191
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.h (renamed from drivers/net/bnx2.h)27
-rw-r--r--drivers/net/ethernet/broadcom/bnx2_fw.h (renamed from drivers/net/bnx2_fw.h)0
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/Makefile (renamed from drivers/net/bnx2x/Makefile)2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h (renamed from drivers/net/bnx2x/bnx2x.h)1242
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c (renamed from drivers/net/bnx2x/bnx2x_cmn.c)1590
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h (renamed from drivers/net/bnx2x/bnx2x_cmn.h)679
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c (renamed from drivers/net/bnx2x/bnx2x_dcb.c)884
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h (renamed from drivers/net/bnx2x/bnx2x_dcb.h)30
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h1156
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c (renamed from drivers/net/bnx2x/bnx2x_ethtool.c)777
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h410
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_file_hdr.h (renamed from drivers/net/bnx2x/bnx2x_fw_file_hdr.h)0
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h5133
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h567
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_init_ops.h (renamed from drivers/net/bnx2x/bnx2x_init_ops.h)194
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c (renamed from drivers/net/bnx2x/bnx2x_link.c)6662
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h (renamed from drivers/net/bnx2x/bnx2x_link.h)187
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c (renamed from drivers/net/bnx2x/bnx2x_main.c)6493
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h (renamed from drivers/net/bnx2x/bnx2x_reg.h)1006
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c5686
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h1297
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c (renamed from drivers/net/bnx2x/bnx2x_stats.c)929
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h (renamed from drivers/net/bnx2x/bnx2x_stats.h)228
-rw-r--r--drivers/net/ethernet/broadcom/cnic.c (renamed from drivers/net/cnic.c)447
-rw-r--r--drivers/net/ethernet/broadcom/cnic.h (renamed from drivers/net/cnic.h)56
-rw-r--r--drivers/net/ethernet/broadcom/cnic_defs.h (renamed from drivers/net/cnic_defs.h)5289
-rw-r--r--drivers/net/ethernet/broadcom/cnic_if.h (renamed from drivers/net/cnic_if.h)15
-rw-r--r--drivers/net/ethernet/broadcom/sb1250-mac.c (renamed from drivers/net/sb1250-mac.c)4
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c (renamed from drivers/net/tg3.c)2572
-rw-r--r--drivers/net/ethernet/broadcom/tg3.h (renamed from drivers/net/tg3.h)62
-rw-r--r--drivers/net/ethernet/brocade/Kconfig23
-rw-r--r--drivers/net/ethernet/brocade/Makefile5
-rw-r--r--drivers/net/ethernet/brocade/bna/Kconfig17
-rw-r--r--drivers/net/ethernet/brocade/bna/Makefile (renamed from drivers/net/bna/Makefile)5
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_cee.c (renamed from drivers/net/bna/bfa_cee.c)68
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_cee.h (renamed from drivers/net/bna/bfa_cee.h)3
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_cs.h (renamed from drivers/net/bna/bfa_sm.h)78
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_defs.h (renamed from drivers/net/bna/bfa_defs.h)69
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_defs_cna.h (renamed from drivers/net/bna/bfa_defs_cna.h)8
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_defs_mfg_comm.h (renamed from drivers/net/bna/bfa_defs_mfg_comm.h)89
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_defs_status.h (renamed from drivers/net/bna/bfa_defs_status.h)134
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_ioc.c (renamed from drivers/net/bna/bfa_ioc.c)610
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_ioc.h (renamed from drivers/net/bna/bfa_ioc.h)105
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_ioc_ct.c878
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_msgq.c669
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_msgq.h130
-rw-r--r--drivers/net/ethernet/brocade/bna/bfi.h (renamed from drivers/net/bna/bfi.h)273
-rw-r--r--drivers/net/ethernet/brocade/bna/bfi_cna.h (renamed from drivers/net/bna/bfi_cna.h)0
-rw-r--r--drivers/net/ethernet/brocade/bna/bfi_enet.h901
-rw-r--r--drivers/net/ethernet/brocade/bna/bfi_reg.h452
-rw-r--r--drivers/net/ethernet/brocade/bna/bna.h (renamed from drivers/net/bna/bna.h)363
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_enet.c2144
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_hw_defs.h422
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_tx_rx.c3798
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_types.h (renamed from drivers/net/bna/bna_types.h)700
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad.c (renamed from drivers/net/bna/bnad.c)1287
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad.h (renamed from drivers/net/bna/bnad.h)129
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad_ethtool.c (renamed from drivers/net/bna/bnad_ethtool.c)490
-rw-r--r--drivers/net/ethernet/brocade/bna/cna.h (renamed from drivers/net/bna/cna.h)46
-rw-r--r--drivers/net/ethernet/brocade/bna/cna_fwimg.c (renamed from drivers/net/bna/cna_fwimg.c)48
-rw-r--r--drivers/net/ethernet/cadence/Kconfig46
-rw-r--r--drivers/net/ethernet/cadence/Makefile6
-rw-r--r--drivers/net/ethernet/cadence/at91_ether.c (renamed from drivers/net/arm/at91_ether.c)5
-rw-r--r--drivers/net/ethernet/cadence/at91_ether.h (renamed from drivers/net/arm/at91_ether.h)0
-rw-r--r--drivers/net/ethernet/cadence/macb.c (renamed from drivers/net/macb.c)10
-rw-r--r--drivers/net/ethernet/cadence/macb.h (renamed from drivers/net/macb.h)0
-rw-r--r--drivers/net/ethernet/chelsio/Kconfig107
-rw-r--r--drivers/net/ethernet/chelsio/Makefile8
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/Makefile (renamed from drivers/net/chelsio/Makefile)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/common.h (renamed from drivers/net/chelsio/common.h)2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/cphy.h (renamed from drivers/net/chelsio/cphy.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/cpl5_cmd.h (renamed from drivers/net/chelsio/cpl5_cmd.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/cxgb2.c (renamed from drivers/net/chelsio/cxgb2.c)43
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/elmer0.h (renamed from drivers/net/chelsio/elmer0.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/espi.c (renamed from drivers/net/chelsio/espi.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/espi.h (renamed from drivers/net/chelsio/espi.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/fpga_defs.h (renamed from drivers/net/chelsio/fpga_defs.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/gmac.h (renamed from drivers/net/chelsio/gmac.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c (renamed from drivers/net/chelsio/mv88e1xxx.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.h (renamed from drivers/net/chelsio/mv88e1xxx.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/mv88x201x.c (renamed from drivers/net/chelsio/mv88x201x.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/my3126.c (renamed from drivers/net/chelsio/my3126.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/pm3393.c (renamed from drivers/net/chelsio/pm3393.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/regs.h (renamed from drivers/net/chelsio/regs.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/sge.c (renamed from drivers/net/chelsio/sge.c)31
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/sge.h (renamed from drivers/net/chelsio/sge.h)2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/subr.c (renamed from drivers/net/chelsio/subr.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/suni1x10gexp_regs.h (renamed from drivers/net/chelsio/suni1x10gexp_regs.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/tp.c (renamed from drivers/net/chelsio/tp.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/tp.h (renamed from drivers/net/chelsio/tp.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/vsc7326.c (renamed from drivers/net/chelsio/vsc7326.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/vsc7326_reg.h (renamed from drivers/net/chelsio/vsc7326_reg.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/Makefile (renamed from drivers/net/cxgb3/Makefile)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/adapter.h (renamed from drivers/net/cxgb3/adapter.h)2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/ael1002.c (renamed from drivers/net/cxgb3/ael1002.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/aq100x.c (renamed from drivers/net/cxgb3/aq100x.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/common.h (renamed from drivers/net/cxgb3/common.h)1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_ctl_defs.h (renamed from drivers/net/cxgb3/cxgb3_ctl_defs.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_defs.h (renamed from drivers/net/cxgb3/cxgb3_defs.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_ioctl.h (renamed from drivers/net/cxgb3/cxgb3_ioctl.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c (renamed from drivers/net/cxgb3/cxgb3_main.c)54
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c (renamed from drivers/net/cxgb3/cxgb3_offload.c)45
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.h (renamed from drivers/net/cxgb3/cxgb3_offload.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/firmware_exports.h (renamed from drivers/net/cxgb3/firmware_exports.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/l2t.c (renamed from drivers/net/cxgb3/l2t.c)16
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/l2t.h (renamed from drivers/net/cxgb3/l2t.h)18
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/mc5.c (renamed from drivers/net/cxgb3/mc5.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/regs.h (renamed from drivers/net/cxgb3/regs.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/sge.c (renamed from drivers/net/cxgb3/sge.c)51
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/sge_defs.h (renamed from drivers/net/cxgb3/sge_defs.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3_cpl.h (renamed from drivers/net/cxgb3/t3_cpl.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3_hw.c (renamed from drivers/net/cxgb3/t3_hw.c)11
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3cdev.h (renamed from drivers/net/cxgb3/t3cdev.h)2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/version.h (renamed from drivers/net/cxgb3/version.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/vsc8211.c (renamed from drivers/net/cxgb3/vsc8211.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/xgmac.c (renamed from drivers/net/cxgb3/xgmac.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/Makefile (renamed from drivers/net/cxgb4/Makefile)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4.h (renamed from drivers/net/cxgb4/cxgb4.h)3
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c (renamed from drivers/net/cxgb4/cxgb4_main.c)10
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h (renamed from drivers/net/cxgb4/cxgb4_uld.h)2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.c (renamed from drivers/net/cxgb4/l2t.c)7
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.h (renamed from drivers/net/cxgb4/l2t.h)2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c (renamed from drivers/net/cxgb4/sge.c)50
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c (renamed from drivers/net/cxgb4/t4_hw.c)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.h (renamed from drivers/net/cxgb4/t4_hw.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_msg.h (renamed from drivers/net/cxgb4/t4_msg.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_regs.h (renamed from drivers/net/cxgb4/t4_regs.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h (renamed from drivers/net/cxgb4/t4fw_api.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/Makefile (renamed from drivers/net/cxgb4vf/Makefile)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/adapter.h (renamed from drivers/net/cxgb4vf/adapter.h)4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c (renamed from drivers/net/cxgb4vf/cxgb4vf_main.c)70
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/sge.c (renamed from drivers/net/cxgb4vf/sge.c)128
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h (renamed from drivers/net/cxgb4vf/t4vf_common.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h (renamed from drivers/net/cxgb4vf/t4vf_defs.h)0
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c (renamed from drivers/net/cxgb4vf/t4vf_hw.c)1
-rw-r--r--drivers/net/ethernet/cirrus/Kconfig62
-rw-r--r--drivers/net/ethernet/cirrus/Makefile7
-rw-r--r--drivers/net/ethernet/cirrus/cs89x0.c (renamed from drivers/net/cs89x0.c)2
-rw-r--r--drivers/net/ethernet/cirrus/cs89x0.h (renamed from drivers/net/cs89x0.h)0
-rw-r--r--drivers/net/ethernet/cirrus/ep93xx_eth.c (renamed from drivers/net/arm/ep93xx_eth.c)1
-rw-r--r--drivers/net/ethernet/cirrus/mac89x0.c (renamed from drivers/net/mac89x0.c)2
-rw-r--r--drivers/net/ethernet/cisco/Kconfig23
-rw-r--r--drivers/net/ethernet/cisco/Makefile5
-rw-r--r--drivers/net/ethernet/cisco/enic/Kconfig9
-rw-r--r--drivers/net/ethernet/cisco/enic/Makefile (renamed from drivers/net/enic/Makefile)0
-rw-r--r--drivers/net/ethernet/cisco/enic/cq_desc.h (renamed from drivers/net/enic/cq_desc.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/cq_enet_desc.h (renamed from drivers/net/enic/cq_enet_desc.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/enic.h (renamed from drivers/net/enic/enic.h)17
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_dev.c (renamed from drivers/net/enic/enic_dev.c)11
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_dev.h (renamed from drivers/net/enic/enic_dev.h)20
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c (renamed from drivers/net/enic/enic_main.c)355
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_pp.c (renamed from drivers/net/enic/enic_pp.c)192
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_pp.h (renamed from drivers/net/enic/enic_pp.h)15
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_res.c (renamed from drivers/net/enic/enic_res.c)30
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_res.h (renamed from drivers/net/enic/enic_res.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/rq_enet_desc.h (renamed from drivers/net/enic/rq_enet_desc.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_cq.c (renamed from drivers/net/enic/vnic_cq.c)2
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_cq.h (renamed from drivers/net/enic/vnic_cq.h)1
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_dev.c (renamed from drivers/net/enic/vnic_dev.c)88
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_dev.h (renamed from drivers/net/enic/vnic_dev.h)7
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_devcmd.h (renamed from drivers/net/enic/vnic_devcmd.h)19
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_enet.h (renamed from drivers/net/enic/vnic_enet.h)11
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_intr.c (renamed from drivers/net/enic/vnic_intr.c)7
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_intr.h (renamed from drivers/net/enic/vnic_intr.h)6
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_nic.h (renamed from drivers/net/enic/vnic_nic.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_resource.h (renamed from drivers/net/enic/vnic_resource.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_rq.c (renamed from drivers/net/enic/vnic_rq.c)0
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_rq.h (renamed from drivers/net/enic/vnic_rq.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_rss.h (renamed from drivers/net/enic/vnic_rss.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_stats.h (renamed from drivers/net/enic/vnic_stats.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_vic.c (renamed from drivers/net/enic/vnic_vic.c)0
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_vic.h (renamed from drivers/net/enic/vnic_vic.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_wq.c (renamed from drivers/net/enic/vnic_wq.c)0
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_wq.h (renamed from drivers/net/enic/vnic_wq.h)0
-rw-r--r--drivers/net/ethernet/cisco/enic/wq_enet_desc.h (renamed from drivers/net/enic/wq_enet_desc.h)0
-rw-r--r--drivers/net/ethernet/davicom/Kconfig24
-rw-r--r--drivers/net/ethernet/davicom/Makefile5
-rw-r--r--drivers/net/ethernet/davicom/dm9000.c (renamed from drivers/net/dm9000.c)40
-rw-r--r--drivers/net/ethernet/davicom/dm9000.h (renamed from drivers/net/dm9000.h)0
-rw-r--r--drivers/net/ethernet/dec/Kconfig37
-rw-r--r--drivers/net/ethernet/dec/Makefile6
-rw-r--r--drivers/net/ethernet/dec/ewrk3.c (renamed from drivers/net/ewrk3.c)41
-rw-r--r--drivers/net/ethernet/dec/ewrk3.h (renamed from drivers/net/ewrk3.h)0
-rw-r--r--drivers/net/ethernet/dec/tulip/21142.c (renamed from drivers/net/tulip/21142.c)3
-rw-r--r--drivers/net/ethernet/dec/tulip/Kconfig (renamed from drivers/net/tulip/Kconfig)21
-rw-r--r--drivers/net/ethernet/dec/tulip/Makefile (renamed from drivers/net/tulip/Makefile)0
-rw-r--r--drivers/net/ethernet/dec/tulip/de2104x.c (renamed from drivers/net/tulip/de2104x.c)3
-rw-r--r--drivers/net/ethernet/dec/tulip/de4x5.c (renamed from drivers/net/tulip/de4x5.c)14
-rw-r--r--drivers/net/ethernet/dec/tulip/de4x5.h (renamed from drivers/net/tulip/de4x5.h)2
-rw-r--r--drivers/net/ethernet/dec/tulip/dmfe.c (renamed from drivers/net/tulip/dmfe.c)6
-rw-r--r--drivers/net/ethernet/dec/tulip/eeprom.c (renamed from drivers/net/tulip/eeprom.c)2
-rw-r--r--drivers/net/ethernet/dec/tulip/interrupt.c (renamed from drivers/net/tulip/interrupt.c)3
-rw-r--r--drivers/net/ethernet/dec/tulip/media.c (renamed from drivers/net/tulip/media.c)3
-rw-r--r--drivers/net/ethernet/dec/tulip/pnic.c (renamed from drivers/net/tulip/pnic.c)4
-rw-r--r--drivers/net/ethernet/dec/tulip/pnic2.c (renamed from drivers/net/tulip/pnic2.c)3
-rw-r--r--drivers/net/ethernet/dec/tulip/timer.c (renamed from drivers/net/tulip/timer.c)3
-rw-r--r--drivers/net/ethernet/dec/tulip/tulip.h (renamed from drivers/net/tulip/tulip.h)3
-rw-r--r--drivers/net/ethernet/dec/tulip/tulip_core.c (renamed from drivers/net/tulip/tulip_core.c)6
-rw-r--r--drivers/net/ethernet/dec/tulip/uli526x.c (renamed from drivers/net/tulip/uli526x.c)2
-rw-r--r--drivers/net/ethernet/dec/tulip/winbond-840.c (renamed from drivers/net/tulip/winbond-840.c)2
-rw-r--r--drivers/net/ethernet/dec/tulip/xircom_cb.c (renamed from drivers/net/tulip/xircom_cb.c)0
-rw-r--r--drivers/net/ethernet/dlink/Kconfig86
-rw-r--r--drivers/net/ethernet/dlink/Makefile8
-rw-r--r--drivers/net/ethernet/dlink/de600.c (renamed from drivers/net/de600.c)0
-rw-r--r--drivers/net/ethernet/dlink/de600.h (renamed from drivers/net/de600.h)0
-rw-r--r--drivers/net/ethernet/dlink/de620.c (renamed from drivers/net/de620.c)2
-rw-r--r--drivers/net/ethernet/dlink/de620.h (renamed from drivers/net/de620.h)0
-rw-r--r--drivers/net/ethernet/dlink/dl2k.c (renamed from drivers/net/dl2k.c)111
-rw-r--r--drivers/net/ethernet/dlink/dl2k.h (renamed from drivers/net/dl2k.h)110
-rw-r--r--drivers/net/ethernet/dlink/sundance.c (renamed from drivers/net/sundance.c)2
-rw-r--r--drivers/net/ethernet/dnet.c (renamed from drivers/net/dnet.c)3
-rw-r--r--drivers/net/ethernet/dnet.h (renamed from drivers/net/dnet.h)0
-rw-r--r--drivers/net/ethernet/emulex/Kconfig23
-rw-r--r--drivers/net/ethernet/emulex/Makefile5
-rw-r--r--drivers/net/ethernet/emulex/benet/Kconfig6
-rw-r--r--drivers/net/ethernet/emulex/benet/Makefile (renamed from drivers/net/benet/Makefile)0
-rw-r--r--drivers/net/ethernet/emulex/benet/be.h (renamed from drivers/net/benet/be.h)146
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c (renamed from drivers/net/benet/be_cmds.c)821
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.h (renamed from drivers/net/benet/be_cmds.h)133
-rw-r--r--drivers/net/ethernet/emulex/benet/be_ethtool.c (renamed from drivers/net/benet/be_ethtool.c)185
-rw-r--r--drivers/net/ethernet/emulex/benet/be_hw.h (renamed from drivers/net/benet/be_hw.h)23
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c (renamed from drivers/net/benet/be_main.c)1350
-rw-r--r--drivers/net/ethernet/ethoc.c (renamed from drivers/net/ethoc.c)8
-rw-r--r--drivers/net/ethernet/faraday/Kconfig40
-rw-r--r--drivers/net/ethernet/faraday/Makefile6
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.c1367
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.h246
-rw-r--r--drivers/net/ethernet/faraday/ftmac100.c (renamed from drivers/net/ftmac100.c)5
-rw-r--r--drivers/net/ethernet/faraday/ftmac100.h (renamed from drivers/net/ftmac100.h)0
-rw-r--r--drivers/net/ethernet/fealnx.c (renamed from drivers/net/fealnx.c)6
-rw-r--r--drivers/net/ethernet/freescale/Kconfig87
-rw-r--r--drivers/net/ethernet/freescale/Makefile18
-rw-r--r--drivers/net/ethernet/freescale/fec.c (renamed from drivers/net/fec.c)235
-rw-r--r--drivers/net/ethernet/freescale/fec.h (renamed from drivers/net/fec.h)0
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx.c (renamed from drivers/net/fec_mpc52xx.c)15
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx.h (renamed from drivers/net/fec_mpc52xx.h)0
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx_phy.c (renamed from drivers/net/fec_mpc52xx_phy.c)0
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/Kconfig (renamed from drivers/net/fs_enet/Kconfig)3
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/Makefile (renamed from drivers/net/fs_enet/Makefile)0
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fec.h (renamed from drivers/net/fs_enet/fec.h)0
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c (renamed from drivers/net/fs_enet/fs_enet-main.c)4
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fs_enet.h (renamed from drivers/net/fs_enet/fs_enet.h)0
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-fcc.c (renamed from drivers/net/fs_enet/mac-fcc.c)0
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-fec.c (renamed from drivers/net/fs_enet/mac-fec.c)0
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-scc.c (renamed from drivers/net/fs_enet/mac-scc.c)0
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c (renamed from drivers/net/fs_enet/mii-bitbang.c)4
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-fec.c (renamed from drivers/net/fs_enet/mii-fec.c)2
-rw-r--r--drivers/net/ethernet/freescale/fsl_pq_mdio.c (renamed from drivers/net/fsl_pq_mdio.c)0
-rw-r--r--drivers/net/ethernet/freescale/fsl_pq_mdio.h (renamed from drivers/net/fsl_pq_mdio.h)0
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c (renamed from drivers/net/gianfar.c)221
-rw-r--r--drivers/net/ethernet/freescale/gianfar.h (renamed from drivers/net/gianfar.h)60
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ethtool.c1764
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ptp.c (renamed from drivers/net/gianfar_ptp.c)11
-rw-r--r--drivers/net/ethernet/freescale/gianfar_sysfs.c (renamed from drivers/net/gianfar_sysfs.c)0
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.c (renamed from drivers/net/ucc_geth.c)20
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.h (renamed from drivers/net/ucc_geth.h)0
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth_ethtool.c (renamed from drivers/net/ucc_geth_ethtool.c)0
-rw-r--r--drivers/net/ethernet/fujitsu/Kconfig54
-rw-r--r--drivers/net/ethernet/fujitsu/Makefile7
-rw-r--r--drivers/net/ethernet/fujitsu/at1700.c (renamed from drivers/net/at1700.c)2
-rw-r--r--drivers/net/ethernet/fujitsu/eth16i.c (renamed from drivers/net/eth16i.c)2
-rw-r--r--drivers/net/ethernet/fujitsu/fmvj18x_cs.c (renamed from drivers/net/pcmcia/fmvj18x_cs.c)2
-rw-r--r--drivers/net/ethernet/hp/Kconfig32
-rw-r--r--drivers/net/ethernet/hp/Makefile5
-rw-r--r--drivers/net/ethernet/hp/hp100.c (renamed from drivers/net/hp100.c)22
-rw-r--r--drivers/net/ethernet/hp/hp100.h (renamed from drivers/net/hp100.h)0
-rw-r--r--drivers/net/ethernet/i825xx/3c505.c (renamed from drivers/net/3c505.c)8
-rw-r--r--drivers/net/ethernet/i825xx/3c505.h (renamed from drivers/net/3c505.h)0
-rw-r--r--drivers/net/ethernet/i825xx/3c507.c (renamed from drivers/net/3c507.c)0
-rw-r--r--drivers/net/ethernet/i825xx/3c523.c (renamed from drivers/net/3c523.c)2
-rw-r--r--drivers/net/ethernet/i825xx/3c523.h (renamed from drivers/net/3c523.h)0
-rw-r--r--drivers/net/ethernet/i825xx/3c527.c (renamed from drivers/net/3c527.c)2
-rw-r--r--drivers/net/ethernet/i825xx/3c527.h (renamed from drivers/net/3c527.h)0
-rw-r--r--drivers/net/ethernet/i825xx/82596.c (renamed from drivers/net/82596.c)2
-rw-r--r--drivers/net/ethernet/i825xx/Kconfig183
-rw-r--r--drivers/net/ethernet/i825xx/Makefile20
-rw-r--r--drivers/net/ethernet/i825xx/eepro.c (renamed from drivers/net/eepro.c)2
-rw-r--r--drivers/net/ethernet/i825xx/eexpress.c (renamed from drivers/net/eexpress.c)4
-rw-r--r--drivers/net/ethernet/i825xx/eexpress.h (renamed from drivers/net/eexpress.h)0
-rw-r--r--drivers/net/ethernet/i825xx/ether1.c (renamed from drivers/net/arm/ether1.c)2
-rw-r--r--drivers/net/ethernet/i825xx/ether1.h (renamed from drivers/net/arm/ether1.h)0
-rw-r--r--drivers/net/ethernet/i825xx/lasi_82596.c (renamed from drivers/net/lasi_82596.c)0
-rw-r--r--drivers/net/ethernet/i825xx/lib82596.c (renamed from drivers/net/lib82596.c)2
-rw-r--r--drivers/net/ethernet/i825xx/lp486e.c (renamed from drivers/net/lp486e.c)2
-rw-r--r--drivers/net/ethernet/i825xx/ni52.c (renamed from drivers/net/ni52.c)2
-rw-r--r--drivers/net/ethernet/i825xx/ni52.h (renamed from drivers/net/ni52.h)0
-rw-r--r--drivers/net/ethernet/i825xx/sni_82596.c (renamed from drivers/net/sni_82596.c)0
-rw-r--r--drivers/net/ethernet/i825xx/sun3_82586.c (renamed from drivers/net/sun3_82586.c)2
-rw-r--r--drivers/net/ethernet/i825xx/sun3_82586.h (renamed from drivers/net/sun3_82586.h)0
-rw-r--r--drivers/net/ethernet/i825xx/znet.c (renamed from drivers/net/znet.c)4
-rw-r--r--drivers/net/ethernet/ibm/Kconfig48
-rw-r--r--drivers/net/ethernet/ibm/Makefile8
-rw-r--r--drivers/net/ethernet/ibm/ehea/Makefile (renamed from drivers/net/ehea/Makefile)0
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea.h (renamed from drivers/net/ehea/ehea.h)22
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_ethtool.c (renamed from drivers/net/ehea/ehea_ethtool.c)33
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_hw.h (renamed from drivers/net/ehea/ehea_hw.h)25
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_main.c (renamed from drivers/net/ehea/ehea_main.c)584
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_phyp.c (renamed from drivers/net/ehea/ehea_phyp.c)0
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_phyp.h (renamed from drivers/net/ehea/ehea_phyp.h)0
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_qmr.c (renamed from drivers/net/ehea/ehea_qmr.c)2
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_qmr.h (renamed from drivers/net/ehea/ehea_qmr.h)2
-rw-r--r--drivers/net/ethernet/ibm/emac/Kconfig (renamed from drivers/net/ibm_newemac/Kconfig)40
-rw-r--r--drivers/net/ethernet/ibm/emac/Makefile11
-rw-r--r--drivers/net/ethernet/ibm/emac/core.c (renamed from drivers/net/ibm_newemac/core.c)57
-rw-r--r--drivers/net/ethernet/ibm/emac/core.h (renamed from drivers/net/ibm_newemac/core.h)16
-rw-r--r--drivers/net/ethernet/ibm/emac/debug.c (renamed from drivers/net/ibm_newemac/debug.c)0
-rw-r--r--drivers/net/ethernet/ibm/emac/debug.h (renamed from drivers/net/ibm_newemac/debug.h)2
-rw-r--r--drivers/net/ethernet/ibm/emac/emac.h (renamed from drivers/net/ibm_newemac/emac.h)19
-rw-r--r--drivers/net/ethernet/ibm/emac/mal.c (renamed from drivers/net/ibm_newemac/mal.c)6
-rw-r--r--drivers/net/ethernet/ibm/emac/mal.h (renamed from drivers/net/ibm_newemac/mal.h)4
-rw-r--r--drivers/net/ethernet/ibm/emac/phy.c (renamed from drivers/net/ibm_newemac/phy.c)7
-rw-r--r--drivers/net/ethernet/ibm/emac/phy.h (renamed from drivers/net/ibm_newemac/phy.h)0
-rw-r--r--drivers/net/ethernet/ibm/emac/rgmii.c (renamed from drivers/net/ibm_newemac/rgmii.c)0
-rw-r--r--drivers/net/ethernet/ibm/emac/rgmii.h (renamed from drivers/net/ibm_newemac/rgmii.h)4
-rw-r--r--drivers/net/ethernet/ibm/emac/tah.c (renamed from drivers/net/ibm_newemac/tah.c)0
-rw-r--r--drivers/net/ethernet/ibm/emac/tah.h (renamed from drivers/net/ibm_newemac/tah.h)4
-rw-r--r--drivers/net/ethernet/ibm/emac/zmii.c (renamed from drivers/net/ibm_newemac/zmii.c)0
-rw-r--r--drivers/net/ethernet/ibm/emac/zmii.h (renamed from drivers/net/ibm_newemac/zmii.h)4
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.c (renamed from drivers/net/ibmveth.c)78
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.h (renamed from drivers/net/ibmveth.h)0
-rw-r--r--drivers/net/ethernet/ibm/iseries_veth.c (renamed from drivers/net/iseries_veth.c)16
-rw-r--r--drivers/net/ethernet/icplus/Kconfig14
-rw-r--r--drivers/net/ethernet/icplus/Makefile5
-rw-r--r--drivers/net/ethernet/icplus/ipg.c (renamed from drivers/net/ipg.c)200
-rw-r--r--drivers/net/ethernet/icplus/ipg.h (renamed from drivers/net/ipg.h)0
-rw-r--r--drivers/net/ethernet/intel/Kconfig226
-rw-r--r--drivers/net/ethernet/intel/Makefile12
-rw-r--r--drivers/net/ethernet/intel/e100.c (renamed from drivers/net/e100.c)12
-rw-r--r--drivers/net/ethernet/intel/e1000/Makefile (renamed from drivers/net/e1000/Makefile)0
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000.h (renamed from drivers/net/e1000/e1000.h)16
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_ethtool.c (renamed from drivers/net/e1000/e1000_ethtool.c)79
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_hw.c (renamed from drivers/net/e1000/e1000_hw.c)38
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_hw.h (renamed from drivers/net/e1000/e1000_hw.h)0
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_main.c (renamed from drivers/net/e1000/e1000_main.c)422
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_osdep.h (renamed from drivers/net/e1000/e1000_osdep.h)0
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_param.c (renamed from drivers/net/e1000/e1000_param.c)0
-rw-r--r--drivers/net/ethernet/intel/e1000e/80003es2lan.c (renamed from drivers/net/e1000e/es2lan.c)19
-rw-r--r--drivers/net/ethernet/intel/e1000e/82571.c (renamed from drivers/net/e1000e/82571.c)36
-rw-r--r--drivers/net/ethernet/intel/e1000e/Makefile (renamed from drivers/net/e1000e/Makefile)2
-rw-r--r--drivers/net/ethernet/intel/e1000e/defines.h (renamed from drivers/net/e1000e/defines.h)0
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h (renamed from drivers/net/e1000e/e1000.h)91
-rw-r--r--drivers/net/ethernet/intel/e1000e/ethtool.c (renamed from drivers/net/e1000e/ethtool.c)114
-rw-r--r--drivers/net/ethernet/intel/e1000e/hw.h (renamed from drivers/net/e1000e/hw.h)21
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c (renamed from drivers/net/e1000e/ich8lan.c)335
-rw-r--r--drivers/net/ethernet/intel/e1000e/lib.c (renamed from drivers/net/e1000e/lib.c)12
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c (renamed from drivers/net/e1000e/netdev.c)525
-rw-r--r--drivers/net/ethernet/intel/e1000e/param.c (renamed from drivers/net/e1000e/param.c)1
-rw-r--r--drivers/net/ethernet/intel/e1000e/phy.c (renamed from drivers/net/e1000e/phy.c)354
-rw-r--r--drivers/net/ethernet/intel/igb/Makefile (renamed from drivers/net/igb/Makefile)2
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.c (renamed from drivers/net/igb/e1000_82575.c)65
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.h (renamed from drivers/net/igb/e1000_82575.h)6
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_defines.h (renamed from drivers/net/igb/e1000_defines.h)21
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_hw.h (renamed from drivers/net/igb/e1000_hw.h)2
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mac.c (renamed from drivers/net/igb/e1000_mac.c)77
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mac.h (renamed from drivers/net/igb/e1000_mac.h)3
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mbx.c (renamed from drivers/net/igb/e1000_mbx.c)4
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mbx.h (renamed from drivers/net/igb/e1000_mbx.h)2
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_nvm.c (renamed from drivers/net/igb/e1000_nvm.c)3
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_nvm.h (renamed from drivers/net/igb/e1000_nvm.h)2
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.c (renamed from drivers/net/igb/e1000_phy.c)10
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.h (renamed from drivers/net/igb/e1000_phy.h)2
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_regs.h (renamed from drivers/net/igb/e1000_regs.h)3
-rw-r--r--drivers/net/ethernet/intel/igb/igb.h (renamed from drivers/net/igb/igb.h)227
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c (renamed from drivers/net/igb/igb_ethtool.c)154
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c (renamed from drivers/net/igb/igb_main.c)2362
-rw-r--r--drivers/net/ethernet/intel/igbvf/Makefile (renamed from drivers/net/igbvf/Makefile)0
-rw-r--r--drivers/net/ethernet/intel/igbvf/defines.h (renamed from drivers/net/igbvf/defines.h)0
-rw-r--r--drivers/net/ethernet/intel/igbvf/ethtool.c (renamed from drivers/net/igbvf/ethtool.c)61
-rw-r--r--drivers/net/ethernet/intel/igbvf/igbvf.h (renamed from drivers/net/igbvf/igbvf.h)4
-rw-r--r--drivers/net/ethernet/intel/igbvf/mbx.c (renamed from drivers/net/igbvf/mbx.c)2
-rw-r--r--drivers/net/ethernet/intel/igbvf/mbx.h (renamed from drivers/net/igbvf/mbx.h)0
-rw-r--r--drivers/net/ethernet/intel/igbvf/netdev.c (renamed from drivers/net/igbvf/netdev.c)98
-rw-r--r--drivers/net/ethernet/intel/igbvf/regs.h (renamed from drivers/net/igbvf/regs.h)0
-rw-r--r--drivers/net/ethernet/intel/igbvf/vf.c (renamed from drivers/net/igbvf/vf.c)0
-rw-r--r--drivers/net/ethernet/intel/igbvf/vf.h (renamed from drivers/net/igbvf/vf.h)0
-rw-r--r--drivers/net/ethernet/intel/ixgb/Makefile (renamed from drivers/net/ixgb/Makefile)0
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb.h (renamed from drivers/net/ixgb/ixgb.h)2
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ee.c (renamed from drivers/net/ixgb/ixgb_ee.c)11
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ee.h (renamed from drivers/net/ixgb/ixgb_ee.h)4
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c (renamed from drivers/net/ixgb/ixgb_ethtool.c)102
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_hw.c (renamed from drivers/net/ixgb/ixgb_hw.c)4
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_hw.h (renamed from drivers/net/ixgb/ixgb_hw.h)4
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ids.h (renamed from drivers/net/ixgb/ixgb_ids.h)0
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_main.c (renamed from drivers/net/ixgb/ixgb_main.c)78
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_osdep.h (renamed from drivers/net/ixgb/ixgb_osdep.h)1
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_param.c (renamed from drivers/net/ixgb/ixgb_param.c)18
-rw-r--r--drivers/net/ethernet/intel/ixgbe/Makefile (renamed from drivers/net/ixgbe/Makefile)0
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe.h (renamed from drivers/net/ixgbe/ixgbe.h)177
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c (renamed from drivers/net/ixgbe/ixgbe_82598.c)88
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c (renamed from drivers/net/ixgbe/ixgbe_82599.c)653
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.c (renamed from drivers/net/ixgbe/ixgbe_common.c)419
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.h (renamed from drivers/net/ixgbe/ixgbe_common.h)7
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c (renamed from drivers/net/ixgbe/ixgbe_dcb.c)68
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h (renamed from drivers/net/ixgbe/ixgbe_dcb.h)14
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c (renamed from drivers/net/ixgbe/ixgbe_dcb_82598.c)52
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h (renamed from drivers/net/ixgbe/ixgbe_dcb_82598.h)3
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c (renamed from drivers/net/ixgbe/ixgbe_dcb_82599.c)175
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h (renamed from drivers/net/ixgbe/ixgbe_dcb_82599.h)16
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c (renamed from drivers/net/ixgbe/ixgbe_dcb_nl.c)272
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c (renamed from drivers/net/ixgbe/ixgbe_ethtool.c)893
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c (renamed from drivers/net/ixgbe/ixgbe_fcoe.c)324
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h (renamed from drivers/net/ixgbe/ixgbe_fcoe.h)14
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c (renamed from drivers/net/ixgbe/ixgbe_main.c)3447
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c (renamed from drivers/net/ixgbe/ixgbe_mbx.c)2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h (renamed from drivers/net/ixgbe/ixgbe_mbx.h)0
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c (renamed from drivers/net/ixgbe/ixgbe_phy.c)36
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h (renamed from drivers/net/ixgbe/ixgbe_phy.h)0
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c (renamed from drivers/net/ixgbe/ixgbe_sriov.c)273
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h (renamed from drivers/net/ixgbe/ixgbe_sriov.h)8
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_type.h (renamed from drivers/net/ixgbe/ixgbe_type.h)179
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c (renamed from drivers/net/ixgbe/ixgbe_x540.c)93
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/Makefile (renamed from drivers/net/ixgbevf/Makefile)0
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/defines.h (renamed from drivers/net/ixgbevf/defines.h)0
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ethtool.c (renamed from drivers/net/ixgbevf/ethtool.c)50
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf.h (renamed from drivers/net/ixgbevf/ixgbevf.h)16
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c (renamed from drivers/net/ixgbevf/ixgbevf_main.c)183
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/mbx.c (renamed from drivers/net/ixgbevf/mbx.c)2
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/mbx.h (renamed from drivers/net/ixgbevf/mbx.h)0
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/regs.h (renamed from drivers/net/ixgbevf/regs.h)0
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/vf.c (renamed from drivers/net/ixgbevf/vf.c)0
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/vf.h (renamed from drivers/net/ixgbevf/vf.h)0
-rw-r--r--drivers/net/ethernet/jme.c (renamed from drivers/net/jme.c)86
-rw-r--r--drivers/net/ethernet/jme.h (renamed from drivers/net/jme.h)8
-rw-r--r--drivers/net/ethernet/korina.c (renamed from drivers/net/korina.c)10
-rw-r--r--drivers/net/ethernet/lantiq_etop.c (renamed from drivers/net/lantiq_etop.c)4
-rw-r--r--drivers/net/ethernet/marvell/Kconfig111
-rw-r--r--drivers/net/ethernet/marvell/Makefile8
-rw-r--r--drivers/net/ethernet/marvell/mv643xx_eth.c (renamed from drivers/net/mv643xx_eth.c)31
-rw-r--r--drivers/net/ethernet/marvell/pxa168_eth.c (renamed from drivers/net/pxa168_eth.c)8
-rw-r--r--drivers/net/ethernet/marvell/skge.c (renamed from drivers/net/skge.c)191
-rw-r--r--drivers/net/ethernet/marvell/skge.h (renamed from drivers/net/skge.h)1
-rw-r--r--drivers/net/ethernet/marvell/sky2.c (renamed from drivers/net/sky2.c)382
-rw-r--r--drivers/net/ethernet/marvell/sky2.h (renamed from drivers/net/sky2.h)17
-rw-r--r--drivers/net/ethernet/mellanox/Kconfig23
-rw-r--r--drivers/net/ethernet/mellanox/Makefile5
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/Kconfig27
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/Makefile (renamed from drivers/net/mlx4/Makefile)0
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/alloc.c (renamed from drivers/net/mlx4/alloc.c)1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/catas.c (renamed from drivers/net/mlx4/catas.c)1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cmd.c (renamed from drivers/net/mlx4/cmd.c)1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cq.c (renamed from drivers/net/mlx4/cq.c)1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_cq.c (renamed from drivers/net/mlx4/en_cq.c)31
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c (renamed from drivers/net/mlx4/en_ethtool.c)23
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_main.c (renamed from drivers/net/mlx4/en_main.c)9
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c (renamed from drivers/net/mlx4/en_netdev.c)170
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_port.c (renamed from drivers/net/mlx4/en_port.c)53
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_port.h (renamed from drivers/net/mlx4/en_port.h)11
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_resources.c (renamed from drivers/net/mlx4/en_resources.c)0
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c (renamed from drivers/net/mlx4/en_rx.c)104
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_selftest.c (renamed from drivers/net/mlx4/en_selftest.c)3
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_tx.c (renamed from drivers/net/mlx4/en_tx.c)44
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/eq.c (renamed from drivers/net/mlx4/eq.c)3
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.c (renamed from drivers/net/mlx4/fw.c)47
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.h (renamed from drivers/net/mlx4/fw.h)10
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.c (renamed from drivers/net/mlx4/icm.c)0
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.h (renamed from drivers/net/mlx4/icm.h)0
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/intf.c (renamed from drivers/net/mlx4/intf.c)1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c (renamed from drivers/net/mlx4/main.c)100
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mcg.c (renamed from drivers/net/mlx4/mcg.c)18
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4.h (renamed from drivers/net/mlx4/mlx4.h)9
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h (renamed from drivers/net/mlx4/mlx4_en.h)32
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mr.c (renamed from drivers/net/mlx4/mr.c)3
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/pd.c (renamed from drivers/net/mlx4/pd.c)31
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/port.c (renamed from drivers/net/mlx4/port.c)105
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/profile.c (renamed from drivers/net/mlx4/profile.c)0
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/qp.c (renamed from drivers/net/mlx4/qp.c)4
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/reset.c (renamed from drivers/net/mlx4/reset.c)2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/sense.c (renamed from drivers/net/mlx4/sense.c)0
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/srq.c (renamed from drivers/net/mlx4/srq.c)21
-rw-r--r--drivers/net/ethernet/micrel/Kconfig69
-rw-r--r--drivers/net/ethernet/micrel/Makefile9
-rw-r--r--drivers/net/ethernet/micrel/ks8695net.c (renamed from drivers/net/arm/ks8695net.c)6
-rw-r--r--drivers/net/ethernet/micrel/ks8695net.h (renamed from drivers/net/arm/ks8695net.h)0
-rw-r--r--drivers/net/ethernet/micrel/ks8842.c (renamed from drivers/net/ks8842.c)1
-rw-r--r--drivers/net/ethernet/micrel/ks8851.c (renamed from drivers/net/ks8851.c)1
-rw-r--r--drivers/net/ethernet/micrel/ks8851.h (renamed from drivers/net/ks8851.h)0
-rw-r--r--drivers/net/ethernet/micrel/ks8851_mll.c (renamed from drivers/net/ks8851_mll.c)4
-rw-r--r--drivers/net/ethernet/micrel/ksz884x.c (renamed from drivers/net/ksz884x.c)8
-rw-r--r--drivers/net/ethernet/microchip/Kconfig38
-rw-r--r--drivers/net/ethernet/microchip/Makefile5
-rw-r--r--drivers/net/ethernet/microchip/enc28j60.c (renamed from drivers/net/enc28j60.c)2
-rw-r--r--drivers/net/ethernet/microchip/enc28j60_hw.h (renamed from drivers/net/enc28j60_hw.h)0
-rw-r--r--drivers/net/ethernet/mipsnet.c (renamed from drivers/net/mipsnet.c)3
-rw-r--r--drivers/net/ethernet/myricom/Kconfig47
-rw-r--r--drivers/net/ethernet/myricom/Makefile5
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/Makefile (renamed from drivers/net/myri10ge/Makefile)0
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge.c (renamed from drivers/net/myri10ge/myri10ge.c)299
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp.h (renamed from drivers/net/myri10ge/myri10ge_mcp.h)0
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp_gen_header.h (renamed from drivers/net/myri10ge/myri10ge_mcp_gen_header.h)2
-rw-r--r--drivers/net/ethernet/natsemi/Kconfig86
-rw-r--r--drivers/net/ethernet/natsemi/Makefile10
-rw-r--r--drivers/net/ethernet/natsemi/ibmlana.c (renamed from drivers/net/ibmlana.c)2
-rw-r--r--drivers/net/ethernet/natsemi/ibmlana.h (renamed from drivers/net/ibmlana.h)0
-rw-r--r--drivers/net/ethernet/natsemi/jazzsonic.c (renamed from drivers/net/jazzsonic.c)2
-rw-r--r--drivers/net/ethernet/natsemi/macsonic.c (renamed from drivers/net/macsonic.c)11
-rw-r--r--drivers/net/ethernet/natsemi/natsemi.c (renamed from drivers/net/natsemi.c)12
-rw-r--r--drivers/net/ethernet/natsemi/ns83820.c (renamed from drivers/net/ns83820.c)43
-rw-r--r--drivers/net/ethernet/natsemi/sonic.c (renamed from drivers/net/sonic.c)0
-rw-r--r--drivers/net/ethernet/natsemi/sonic.h (renamed from drivers/net/sonic.h)0
-rw-r--r--drivers/net/ethernet/natsemi/xtsonic.c (renamed from drivers/net/xtsonic.c)2
-rw-r--r--drivers/net/ethernet/neterion/Kconfig55
-rw-r--r--drivers/net/ethernet/neterion/Makefile6
-rw-r--r--drivers/net/ethernet/neterion/s2io-regs.h (renamed from drivers/net/s2io-regs.h)0
-rw-r--r--drivers/net/ethernet/neterion/s2io.c (renamed from drivers/net/s2io.c)112
-rw-r--r--drivers/net/ethernet/neterion/s2io.h (renamed from drivers/net/s2io.h)5
-rw-r--r--drivers/net/ethernet/neterion/vxge/Makefile (renamed from drivers/net/vxge/Makefile)0
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-config.c (renamed from drivers/net/vxge/vxge-config.c)68
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-config.h (renamed from drivers/net/vxge/vxge-config.h)6
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-ethtool.c (renamed from drivers/net/vxge/vxge-ethtool.c)0
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-ethtool.h (renamed from drivers/net/vxge/vxge-ethtool.h)0
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-main.c (renamed from drivers/net/vxge/vxge-main.c)190
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-main.h (renamed from drivers/net/vxge/vxge-main.h)53
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-reg.h (renamed from drivers/net/vxge/vxge-reg.h)0
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-traffic.c (renamed from drivers/net/vxge/vxge-traffic.c)18
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-traffic.h (renamed from drivers/net/vxge/vxge-traffic.h)0
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-version.h (renamed from drivers/net/vxge/vxge-version.h)0
-rw-r--r--drivers/net/ethernet/netx-eth.c (renamed from drivers/net/netx-eth.c)3
-rw-r--r--drivers/net/ethernet/nuvoton/Kconfig31
-rw-r--r--drivers/net/ethernet/nuvoton/Makefile5
-rw-r--r--drivers/net/ethernet/nuvoton/w90p910_ether.c (renamed from drivers/net/arm/w90p910_ether.c)2
-rw-r--r--drivers/net/ethernet/nvidia/Kconfig32
-rw-r--r--drivers/net/ethernet/nvidia/Makefile5
-rw-r--r--drivers/net/ethernet/nvidia/forcedeth.c (renamed from drivers/net/forcedeth.c)211
-rw-r--r--drivers/net/ethernet/octeon/Kconfig (renamed from drivers/net/octeon/Kconfig)6
-rw-r--r--drivers/net/ethernet/octeon/Makefile5
-rw-r--r--drivers/net/ethernet/octeon/octeon_mgmt.c (renamed from drivers/net/octeon/octeon_mgmt.c)6
-rw-r--r--drivers/net/ethernet/oki-semi/Kconfig23
-rw-r--r--drivers/net/ethernet/oki-semi/Makefile5
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/Kconfig22
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/Makefile (renamed from drivers/net/pch_gbe/Makefile)0
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h (renamed from drivers/net/pch_gbe/pch_gbe.h)12
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c (renamed from drivers/net/pch_gbe/pch_gbe_api.c)0
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.h (renamed from drivers/net/pch_gbe/pch_gbe_api.h)0
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c (renamed from drivers/net/pch_gbe/pch_gbe_ethtool.c)4
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c (renamed from drivers/net/pch_gbe/pch_gbe_main.c)350
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c (renamed from drivers/net/pch_gbe/pch_gbe_param.c)1
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c (renamed from drivers/net/pch_gbe/pch_gbe_phy.c)0
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.h (renamed from drivers/net/pch_gbe/pch_gbe_phy.h)0
-rw-r--r--drivers/net/ethernet/packetengines/Kconfig47
-rw-r--r--drivers/net/ethernet/packetengines/Makefile6
-rw-r--r--drivers/net/ethernet/packetengines/hamachi.c (renamed from drivers/net/hamachi.c)6
-rw-r--r--drivers/net/ethernet/packetengines/yellowfin.c (renamed from drivers/net/yellowfin.c)8
-rw-r--r--drivers/net/ethernet/pasemi/Kconfig30
-rw-r--r--drivers/net/ethernet/pasemi/Makefile5
-rw-r--r--drivers/net/ethernet/pasemi/pasemi_mac.c (renamed from drivers/net/pasemi_mac.c)15
-rw-r--r--drivers/net/ethernet/pasemi/pasemi_mac.h (renamed from drivers/net/pasemi_mac.h)0
-rw-r--r--drivers/net/ethernet/pasemi/pasemi_mac_ethtool.c (renamed from drivers/net/pasemi_mac_ethtool.c)0
-rw-r--r--drivers/net/ethernet/qlogic/Kconfig54
-rw-r--r--drivers/net/ethernet/qlogic/Makefile8
-rw-r--r--drivers/net/ethernet/qlogic/netxen/Makefile (renamed from drivers/net/netxen/Makefile)0
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic.h (renamed from drivers/net/netxen/netxen_nic.h)11
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c (renamed from drivers/net/netxen/netxen_nic_ctx.c)16
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c (renamed from drivers/net/netxen/netxen_nic_ethtool.c)3
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h (renamed from drivers/net/netxen/netxen_nic_hdr.h)0
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c (renamed from drivers/net/netxen/netxen_nic_hw.c)6
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.h (renamed from drivers/net/netxen/netxen_nic_hw.h)0
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c (renamed from drivers/net/netxen/netxen_nic_init.c)82
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c (renamed from drivers/net/netxen/netxen_nic_main.c)191
-rw-r--r--drivers/net/ethernet/qlogic/qla3xxx.c (renamed from drivers/net/qla3xxx.c)19
-rw-r--r--drivers/net/ethernet/qlogic/qla3xxx.h (renamed from drivers/net/qla3xxx.h)0
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/Makefile (renamed from drivers/net/qlcnic/Makefile)0
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic.h (renamed from drivers/net/qlcnic/qlcnic.h)74
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c (renamed from drivers/net/qlcnic/qlcnic_ctx.c)347
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c (renamed from drivers/net/qlcnic/qlcnic_ethtool.c)248
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h (renamed from drivers/net/qlcnic/qlcnic_hdr.h)6
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c (renamed from drivers/net/qlcnic/qlcnic_hw.c)78
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c (renamed from drivers/net/qlcnic/qlcnic_init.c)198
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c (renamed from drivers/net/qlcnic/qlcnic_main.c)265
-rw-r--r--drivers/net/ethernet/qlogic/qlge/Makefile (renamed from drivers/net/qlge/Makefile)0
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge.h (renamed from drivers/net/qlge/qlge.h)4
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_dbg.c (renamed from drivers/net/qlge/qlge_dbg.c)0
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c (renamed from drivers/net/qlge/qlge_ethtool.c)2
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_main.c (renamed from drivers/net/qlge/qlge_main.c)192
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_mpi.c (renamed from drivers/net/qlge/qlge_mpi.c)0
-rw-r--r--drivers/net/ethernet/racal/Kconfig33
-rw-r--r--drivers/net/ethernet/racal/Makefile5
-rw-r--r--drivers/net/ethernet/racal/ni5010.c (renamed from drivers/net/ni5010.c)2
-rw-r--r--drivers/net/ethernet/racal/ni5010.h (renamed from drivers/net/ni5010.h)0
-rw-r--r--drivers/net/ethernet/rdc/Kconfig35
-rw-r--r--drivers/net/ethernet/rdc/Makefile5
-rw-r--r--drivers/net/ethernet/rdc/r6040.c (renamed from drivers/net/r6040.c)23
-rw-r--r--drivers/net/ethernet/realtek/8139cp.c (renamed from drivers/net/8139cp.c)103
-rw-r--r--drivers/net/ethernet/realtek/8139too.c (renamed from drivers/net/8139too.c)3
-rw-r--r--drivers/net/ethernet/realtek/Kconfig118
-rw-r--r--drivers/net/ethernet/realtek/Makefile8
-rw-r--r--drivers/net/ethernet/realtek/atp.c (renamed from drivers/net/atp.c)2
-rw-r--r--drivers/net/ethernet/realtek/atp.h (renamed from drivers/net/atp.h)0
-rw-r--r--drivers/net/ethernet/realtek/r8169.c (renamed from drivers/net/r8169.c)1234
-rw-r--r--drivers/net/ethernet/renesas/Kconfig19
-rw-r--r--drivers/net/ethernet/renesas/Makefile5
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c (renamed from drivers/net/sh_eth.c)58
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.h (renamed from drivers/net/sh_eth.h)17
-rw-r--r--drivers/net/ethernet/s6gmac.c (renamed from drivers/net/s6gmac.c)0
-rw-r--r--drivers/net/ethernet/seeq/Kconfig47
-rw-r--r--drivers/net/ethernet/seeq/Makefile7
-rw-r--r--drivers/net/ethernet/seeq/ether3.c (renamed from drivers/net/arm/ether3.c)2
-rw-r--r--drivers/net/ethernet/seeq/ether3.h (renamed from drivers/net/arm/ether3.h)0
-rw-r--r--drivers/net/ethernet/seeq/seeq8005.c (renamed from drivers/net/seeq8005.c)2
-rw-r--r--drivers/net/ethernet/seeq/seeq8005.h (renamed from drivers/net/seeq8005.h)0
-rw-r--r--drivers/net/ethernet/seeq/sgiseeq.c (renamed from drivers/net/sgiseeq.c)3
-rw-r--r--drivers/net/ethernet/seeq/sgiseeq.h (renamed from drivers/net/sgiseeq.h)0
-rw-r--r--drivers/net/ethernet/sfc/Kconfig (renamed from drivers/net/sfc/Kconfig)11
-rw-r--r--drivers/net/ethernet/sfc/Makefile (renamed from drivers/net/sfc/Makefile)0
-rw-r--r--drivers/net/ethernet/sfc/bitfield.h (renamed from drivers/net/sfc/bitfield.h)0
-rw-r--r--drivers/net/ethernet/sfc/efx.c (renamed from drivers/net/sfc/efx.c)134
-rw-r--r--drivers/net/ethernet/sfc/efx.h (renamed from drivers/net/sfc/efx.h)11
-rw-r--r--drivers/net/ethernet/sfc/enum.h (renamed from drivers/net/sfc/enum.h)3
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c (renamed from drivers/net/sfc/ethtool.c)138
-rw-r--r--drivers/net/ethernet/sfc/falcon.c (renamed from drivers/net/sfc/falcon.c)56
-rw-r--r--drivers/net/ethernet/sfc/falcon_boards.c (renamed from drivers/net/sfc/falcon_boards.c)3
-rw-r--r--drivers/net/ethernet/sfc/falcon_xmac.c (renamed from drivers/net/sfc/falcon_xmac.c)0
-rw-r--r--drivers/net/ethernet/sfc/filter.c (renamed from drivers/net/sfc/filter.c)47
-rw-r--r--drivers/net/ethernet/sfc/filter.h (renamed from drivers/net/sfc/filter.h)0
-rw-r--r--drivers/net/ethernet/sfc/io.h (renamed from drivers/net/sfc/io.h)6
-rw-r--r--drivers/net/ethernet/sfc/mac.h (renamed from drivers/net/sfc/mac.h)0
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c (renamed from drivers/net/sfc/mcdi.c)46
-rw-r--r--drivers/net/ethernet/sfc/mcdi.h (renamed from drivers/net/sfc/mcdi.h)0
-rw-r--r--drivers/net/ethernet/sfc/mcdi_mac.c (renamed from drivers/net/sfc/mcdi_mac.c)0
-rw-r--r--drivers/net/ethernet/sfc/mcdi_pcol.h (renamed from drivers/net/sfc/mcdi_pcol.h)0
-rw-r--r--drivers/net/ethernet/sfc/mcdi_phy.c (renamed from drivers/net/sfc/mcdi_phy.c)0
-rw-r--r--drivers/net/ethernet/sfc/mdio_10g.c (renamed from drivers/net/sfc/mdio_10g.c)0
-rw-r--r--drivers/net/ethernet/sfc/mdio_10g.h (renamed from drivers/net/sfc/mdio_10g.h)0
-rw-r--r--drivers/net/ethernet/sfc/mtd.c (renamed from drivers/net/sfc/mtd.c)0
-rw-r--r--drivers/net/ethernet/sfc/net_driver.h (renamed from drivers/net/sfc/net_driver.h)12
-rw-r--r--drivers/net/ethernet/sfc/nic.c (renamed from drivers/net/sfc/nic.c)8
-rw-r--r--drivers/net/ethernet/sfc/nic.h (renamed from drivers/net/sfc/nic.h)5
-rw-r--r--drivers/net/ethernet/sfc/phy.h (renamed from drivers/net/sfc/phy.h)0
-rw-r--r--drivers/net/ethernet/sfc/qt202x_phy.c (renamed from drivers/net/sfc/qt202x_phy.c)0
-rw-r--r--drivers/net/ethernet/sfc/regs.h (renamed from drivers/net/sfc/regs.h)0
-rw-r--r--drivers/net/ethernet/sfc/rx.c (renamed from drivers/net/sfc/rx.c)5
-rw-r--r--drivers/net/ethernet/sfc/selftest.c (renamed from drivers/net/sfc/selftest.c)0
-rw-r--r--drivers/net/ethernet/sfc/selftest.h (renamed from drivers/net/sfc/selftest.h)0
-rw-r--r--drivers/net/ethernet/sfc/siena.c (renamed from drivers/net/sfc/siena.c)76
-rw-r--r--drivers/net/ethernet/sfc/spi.h (renamed from drivers/net/sfc/spi.h)0
-rw-r--r--drivers/net/ethernet/sfc/tenxpress.c (renamed from drivers/net/sfc/tenxpress.c)0
-rw-r--r--drivers/net/ethernet/sfc/tx.c (renamed from drivers/net/sfc/tx.c)21
-rw-r--r--drivers/net/ethernet/sfc/txc43128_phy.c (renamed from drivers/net/sfc/txc43128_phy.c)0
-rw-r--r--drivers/net/ethernet/sfc/workarounds.h (renamed from drivers/net/sfc/workarounds.h)2
-rw-r--r--drivers/net/ethernet/sgi/Kconfig36
-rw-r--r--drivers/net/ethernet/sgi/Makefile6
-rw-r--r--drivers/net/ethernet/sgi/ioc3-eth.c (renamed from drivers/net/ioc3-eth.c)11
-rw-r--r--drivers/net/ethernet/sgi/meth.c (renamed from drivers/net/meth.c)0
-rw-r--r--drivers/net/ethernet/sgi/meth.h (renamed from drivers/net/meth.h)0
-rw-r--r--drivers/net/ethernet/silan/Kconfig33
-rw-r--r--drivers/net/ethernet/silan/Makefile5
-rw-r--r--drivers/net/ethernet/silan/sc92031.c (renamed from drivers/net/sc92031.c)11
-rw-r--r--drivers/net/ethernet/sis/Kconfig53
-rw-r--r--drivers/net/ethernet/sis/Makefile6
-rw-r--r--drivers/net/ethernet/sis/sis190.c (renamed from drivers/net/sis190.c)15
-rw-r--r--drivers/net/ethernet/sis/sis900.c (renamed from drivers/net/sis900.c)6
-rw-r--r--drivers/net/ethernet/sis/sis900.h (renamed from drivers/net/sis900.h)0
-rw-r--r--drivers/net/ethernet/smsc/Kconfig136
-rw-r--r--drivers/net/ethernet/smsc/Makefile11
-rw-r--r--drivers/net/ethernet/smsc/epic100.c (renamed from drivers/net/epic100.c)6
-rw-r--r--drivers/net/ethernet/smsc/smc911x.c (renamed from drivers/net/smc911x.c)7
-rw-r--r--drivers/net/ethernet/smsc/smc911x.h (renamed from drivers/net/smc911x.h)0
-rw-r--r--drivers/net/ethernet/smsc/smc9194.c (renamed from drivers/net/smc9194.c)7
-rw-r--r--drivers/net/ethernet/smsc/smc9194.h (renamed from drivers/net/smc9194.h)0
-rw-r--r--drivers/net/ethernet/smsc/smc91c92_cs.c (renamed from drivers/net/pcmcia/smc91c92_cs.c)4
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c (renamed from drivers/net/smc91x.c)7
-rw-r--r--drivers/net/ethernet/smsc/smc91x.h (renamed from drivers/net/smc91x.h)0
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c (renamed from drivers/net/smsc911x.c)105
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.h (renamed from drivers/net/smsc911x.h)0
-rw-r--r--drivers/net/ethernet/smsc/smsc9420.c (renamed from drivers/net/smsc9420.c)6
-rw-r--r--drivers/net/ethernet/smsc/smsc9420.h (renamed from drivers/net/smsc9420.h)0
-rw-r--r--drivers/net/ethernet/stmicro/Kconfig23
-rw-r--r--drivers/net/ethernet/stmicro/Makefile5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig (renamed from drivers/net/stmmac/Kconfig)48
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile (renamed from drivers/net/stmmac/Makefile)5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/chain_mode.c137
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h (renamed from drivers/net/stmmac/common.h)97
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/descs.h (renamed from drivers/net/stmmac/descs.h)31
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/descs_com.h126
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100.h (renamed from drivers/net/stmmac/dwmac100.h)0
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h (renamed from drivers/net/stmmac/dwmac1000.h)0
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c (renamed from drivers/net/stmmac/dwmac1000_core.c)12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c (renamed from drivers/net/stmmac/dwmac1000_dma.c)18
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c (renamed from drivers/net/stmmac/dwmac100_core.c)13
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c (renamed from drivers/net/stmmac/dwmac100_dma.c)4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h (renamed from drivers/net/stmmac/dwmac_dma.h)1
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c (renamed from drivers/net/stmmac/dwmac_lib.c)0
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/enh_desc.c (renamed from drivers/net/stmmac/enh_desc.c)22
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/mmc.h131
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/mmc_core.c265
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/norm_desc.c (renamed from drivers/net/stmmac/norm_desc.c)52
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/ring_mode.c126
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h (renamed from drivers/net/stmmac/stmmac.h)19
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c (renamed from drivers/net/stmmac/stmmac_ethtool.c)164
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c (renamed from drivers/net/stmmac/stmmac_main.c)745
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c (renamed from drivers/net/stmmac/stmmac_mdio.c)84
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c (renamed from drivers/net/stmmac/stmmac_timer.c)0
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h (renamed from drivers/net/stmmac/stmmac_timer.h)0
-rw-r--r--drivers/net/ethernet/sun/Kconfig88
-rw-r--r--drivers/net/ethernet/sun/Makefile11
-rw-r--r--drivers/net/ethernet/sun/cassini.c (renamed from drivers/net/cassini.c)31
-rw-r--r--drivers/net/ethernet/sun/cassini.h (renamed from drivers/net/cassini.h)0
-rw-r--r--drivers/net/ethernet/sun/niu.c (renamed from drivers/net/niu.c)77
-rw-r--r--drivers/net/ethernet/sun/niu.h (renamed from drivers/net/niu.h)0
-rw-r--r--drivers/net/ethernet/sun/sunbmac.c (renamed from drivers/net/sunbmac.c)41
-rw-r--r--drivers/net/ethernet/sun/sunbmac.h (renamed from drivers/net/sunbmac.h)17
-rw-r--r--drivers/net/ethernet/sun/sungem.c (renamed from drivers/net/sungem.c)914
-rw-r--r--drivers/net/ethernet/sun/sungem.h (renamed from drivers/net/sungem.h)25
-rw-r--r--drivers/net/ethernet/sun/sunhme.c (renamed from drivers/net/sunhme.c)29
-rw-r--r--drivers/net/ethernet/sun/sunhme.h (renamed from drivers/net/sunhme.h)0
-rw-r--r--drivers/net/ethernet/sun/sunqe.c (renamed from drivers/net/sunqe.c)9
-rw-r--r--drivers/net/ethernet/sun/sunqe.h (renamed from drivers/net/sunqe.h)0
-rw-r--r--drivers/net/ethernet/sun/sunvnet.c (renamed from drivers/net/sunvnet.c)2
-rw-r--r--drivers/net/ethernet/sun/sunvnet.h (renamed from drivers/net/sunvnet.h)0
-rw-r--r--drivers/net/ethernet/tehuti/Kconfig27
-rw-r--r--drivers/net/ethernet/tehuti/Makefile5
-rw-r--r--drivers/net/ethernet/tehuti/tehuti.c (renamed from drivers/net/tehuti.c)57
-rw-r--r--drivers/net/ethernet/tehuti/tehuti.h (renamed from drivers/net/tehuti.h)1
-rw-r--r--drivers/net/ethernet/ti/Kconfig77
-rw-r--r--drivers/net/ethernet/ti/Makefile9
-rw-r--r--drivers/net/ethernet/ti/cpmac.c (renamed from drivers/net/cpmac.c)5
-rw-r--r--drivers/net/ethernet/ti/davinci_cpdma.c (renamed from drivers/net/davinci_cpdma.c)2
-rw-r--r--drivers/net/ethernet/ti/davinci_cpdma.h (renamed from drivers/net/davinci_cpdma.h)0
-rw-r--r--drivers/net/ethernet/ti/davinci_emac.c (renamed from drivers/net/davinci_emac.c)15
-rw-r--r--drivers/net/ethernet/ti/davinci_mdio.c (renamed from drivers/net/davinci_mdio.c)0
-rw-r--r--drivers/net/ethernet/ti/tlan.c (renamed from drivers/net/tlan.c)4
-rw-r--r--drivers/net/ethernet/ti/tlan.h (renamed from drivers/net/tlan.h)0
-rw-r--r--drivers/net/ethernet/tile/Kconfig15
-rw-r--r--drivers/net/ethernet/tile/Makefile (renamed from drivers/net/tile/Makefile)0
-rw-r--r--drivers/net/ethernet/tile/tilepro.c (renamed from drivers/net/tile/tilepro.c)4
-rw-r--r--drivers/net/ethernet/toshiba/Kconfig57
-rw-r--r--drivers/net/ethernet/toshiba/Makefile10
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_net.c (renamed from drivers/net/ps3_gelic_net.c)49
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_net.h (renamed from drivers/net/ps3_gelic_net.h)7
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_wireless.c (renamed from drivers/net/ps3_gelic_wireless.c)2
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_wireless.h (renamed from drivers/net/ps3_gelic_wireless.h)0
-rw-r--r--drivers/net/ethernet/toshiba/spider_net.c (renamed from drivers/net/spider_net.c)11
-rw-r--r--drivers/net/ethernet/toshiba/spider_net.h (renamed from drivers/net/spider_net.h)2
-rw-r--r--drivers/net/ethernet/toshiba/spider_net_ethtool.c (renamed from drivers/net/spider_net_ethtool.c)0
-rw-r--r--drivers/net/ethernet/toshiba/tc35815.c (renamed from drivers/net/tc35815.c)2
-rw-r--r--drivers/net/ethernet/tundra/Kconfig29
-rw-r--r--drivers/net/ethernet/tundra/Makefile5
-rw-r--r--drivers/net/ethernet/tundra/tsi108_eth.c (renamed from drivers/net/tsi108_eth.c)14
-rw-r--r--drivers/net/ethernet/tundra/tsi108_eth.h (renamed from drivers/net/tsi108_eth.h)0
-rw-r--r--drivers/net/ethernet/via/Kconfig59
-rw-r--r--drivers/net/ethernet/via/Makefile6
-rw-r--r--drivers/net/ethernet/via/via-rhine.c (renamed from drivers/net/via-rhine.c)2
-rw-r--r--drivers/net/ethernet/via/via-velocity.c (renamed from drivers/net/via-velocity.c)207
-rw-r--r--drivers/net/ethernet/via/via-velocity.h (renamed from drivers/net/via-velocity.h)2
-rw-r--r--drivers/net/ethernet/xilinx/Kconfig36
-rw-r--r--drivers/net/ethernet/xilinx/Makefile7
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac.h (renamed from drivers/net/ll_temac.h)0
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_main.c (renamed from drivers/net/ll_temac_main.c)49
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_mdio.c (renamed from drivers/net/ll_temac_mdio.c)0
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_emaclite.c (renamed from drivers/net/xilinx_emaclite.c)14
-rw-r--r--drivers/net/ethernet/xircom/Kconfig31
-rw-r--r--drivers/net/ethernet/xircom/Makefile5
-rw-r--r--drivers/net/ethernet/xircom/xirc2ps_cs.c (renamed from drivers/net/pcmcia/xirc2ps_cs.c)2
-rw-r--r--drivers/net/ethernet/xscale/Kconfig32
-rw-r--r--drivers/net/ethernet/xscale/Makefile6
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/Kconfig (renamed from drivers/net/ixp2000/Kconfig)2
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/Makefile (renamed from drivers/net/ixp2000/Makefile)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/caleb.c (renamed from drivers/net/ixp2000/caleb.c)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/caleb.h (renamed from drivers/net/ixp2000/caleb.h)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/enp2611.c (renamed from drivers/net/ixp2000/enp2611.c)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/ixp2400-msf.c (renamed from drivers/net/ixp2000/ixp2400-msf.c)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/ixp2400-msf.h (renamed from drivers/net/ixp2000/ixp2400-msf.h)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/ixp2400_rx.uc (renamed from drivers/net/ixp2000/ixp2400_rx.uc)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/ixp2400_rx.ucode (renamed from drivers/net/ixp2000/ixp2400_rx.ucode)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/ixp2400_tx.uc (renamed from drivers/net/ixp2000/ixp2400_tx.uc)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/ixp2400_tx.ucode (renamed from drivers/net/ixp2000/ixp2400_tx.ucode)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/ixpdev.c (renamed from drivers/net/ixp2000/ixpdev.c)1
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/ixpdev.h (renamed from drivers/net/ixp2000/ixpdev.h)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/ixpdev_priv.h (renamed from drivers/net/ixp2000/ixpdev_priv.h)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/pm3386.c (renamed from drivers/net/ixp2000/pm3386.c)0
-rw-r--r--drivers/net/ethernet/xscale/ixp2000/pm3386.h (renamed from drivers/net/ixp2000/pm3386.h)0
-rw-r--r--drivers/net/ethernet/xscale/ixp4xx_eth.c (renamed from drivers/net/arm/ixp4xx_eth.c)3
-rw-r--r--drivers/net/fddi/Kconfig77
-rw-r--r--drivers/net/fddi/Makefile6
-rw-r--r--drivers/net/fddi/defxx.c (renamed from drivers/net/defxx.c)2
-rw-r--r--drivers/net/fddi/defxx.h (renamed from drivers/net/defxx.h)0
-rw-r--r--drivers/net/fddi/skfp/Makefile (renamed from drivers/net/skfp/Makefile)0
-rw-r--r--drivers/net/fddi/skfp/cfm.c (renamed from drivers/net/skfp/cfm.c)0
-rw-r--r--drivers/net/fddi/skfp/drvfbi.c (renamed from drivers/net/skfp/drvfbi.c)0
-rw-r--r--drivers/net/fddi/skfp/ecm.c (renamed from drivers/net/skfp/ecm.c)0
-rw-r--r--drivers/net/fddi/skfp/ess.c (renamed from drivers/net/skfp/ess.c)0
-rw-r--r--drivers/net/fddi/skfp/fplustm.c (renamed from drivers/net/skfp/fplustm.c)0
-rw-r--r--drivers/net/fddi/skfp/h/cmtdef.h (renamed from drivers/net/skfp/h/cmtdef.h)4
-rw-r--r--drivers/net/fddi/skfp/h/fddi.h (renamed from drivers/net/skfp/h/fddi.h)0
-rw-r--r--drivers/net/fddi/skfp/h/fddimib.h (renamed from drivers/net/skfp/h/fddimib.h)0
-rw-r--r--drivers/net/fddi/skfp/h/fplustm.h (renamed from drivers/net/skfp/h/fplustm.h)0
-rw-r--r--drivers/net/fddi/skfp/h/hwmtm.h (renamed from drivers/net/skfp/h/hwmtm.h)2
-rw-r--r--drivers/net/fddi/skfp/h/mbuf.h (renamed from drivers/net/skfp/h/mbuf.h)0
-rw-r--r--drivers/net/fddi/skfp/h/osdef1st.h (renamed from drivers/net/skfp/h/osdef1st.h)0
-rw-r--r--drivers/net/fddi/skfp/h/sba.h (renamed from drivers/net/skfp/h/sba.h)4
-rw-r--r--drivers/net/fddi/skfp/h/sba_def.h (renamed from drivers/net/skfp/h/sba_def.h)0
-rw-r--r--drivers/net/fddi/skfp/h/skfbi.h (renamed from drivers/net/skfp/h/skfbi.h)0
-rw-r--r--drivers/net/fddi/skfp/h/skfbiinc.h (renamed from drivers/net/skfp/h/skfbiinc.h)2
-rw-r--r--drivers/net/fddi/skfp/h/smc.h (renamed from drivers/net/skfp/h/smc.h)14
-rw-r--r--drivers/net/fddi/skfp/h/smt.h (renamed from drivers/net/skfp/h/smt.h)0
-rw-r--r--drivers/net/fddi/skfp/h/smt_p.h (renamed from drivers/net/skfp/h/smt_p.h)0
-rw-r--r--drivers/net/fddi/skfp/h/smtstate.h (renamed from drivers/net/skfp/h/smtstate.h)0
-rw-r--r--drivers/net/fddi/skfp/h/supern_2.h (renamed from drivers/net/skfp/h/supern_2.h)0
-rw-r--r--drivers/net/fddi/skfp/h/targethw.h (renamed from drivers/net/skfp/h/targethw.h)6
-rw-r--r--drivers/net/fddi/skfp/h/targetos.h (renamed from drivers/net/skfp/h/targetos.h)2
-rw-r--r--drivers/net/fddi/skfp/h/types.h (renamed from drivers/net/skfp/h/types.h)0
-rw-r--r--drivers/net/fddi/skfp/hwmtm.c (renamed from drivers/net/skfp/hwmtm.c)0
-rw-r--r--drivers/net/fddi/skfp/hwt.c (renamed from drivers/net/skfp/hwt.c)0
-rw-r--r--drivers/net/fddi/skfp/pcmplc.c (renamed from drivers/net/skfp/pcmplc.c)0
-rw-r--r--drivers/net/fddi/skfp/pmf.c (renamed from drivers/net/skfp/pmf.c)0
-rw-r--r--drivers/net/fddi/skfp/queue.c (renamed from drivers/net/skfp/queue.c)0
-rw-r--r--drivers/net/fddi/skfp/rmt.c (renamed from drivers/net/skfp/rmt.c)0
-rw-r--r--drivers/net/fddi/skfp/skfddi.c (renamed from drivers/net/skfp/skfddi.c)2
-rw-r--r--drivers/net/fddi/skfp/smt.c (renamed from drivers/net/skfp/smt.c)0
-rw-r--r--drivers/net/fddi/skfp/smtdef.c (renamed from drivers/net/skfp/smtdef.c)0
-rw-r--r--drivers/net/fddi/skfp/smtinit.c (renamed from drivers/net/skfp/smtinit.c)0
-rw-r--r--drivers/net/fddi/skfp/smttimer.c (renamed from drivers/net/skfp/smttimer.c)0
-rw-r--r--drivers/net/fddi/skfp/srf.c (renamed from drivers/net/skfp/srf.c)0
-rw-r--r--drivers/net/gianfar_ethtool.c813
-rw-r--r--drivers/net/hamradio/6pack.c2
-rw-r--r--drivers/net/hamradio/baycom_ser_fdx.c1
-rw-r--r--drivers/net/hamradio/baycom_ser_hdx.c1
-rw-r--r--drivers/net/hamradio/dmascc.c2
-rw-r--r--drivers/net/hippi/Kconfig39
-rw-r--r--drivers/net/hippi/Makefile5
-rw-r--r--drivers/net/hippi/rrunner.c (renamed from drivers/net/rrunner.c)0
-rw-r--r--drivers/net/hippi/rrunner.h (renamed from drivers/net/rrunner.h)0
-rw-r--r--drivers/net/ibm_newemac/Makefile11
-rw-r--r--drivers/net/ifb.c59
-rw-r--r--drivers/net/irda/Kconfig4
-rw-r--r--drivers/net/irda/ali-ircc.c1
-rw-r--r--drivers/net/irda/donauboe.c1
-rw-r--r--drivers/net/irda/nsc-ircc.c1
-rw-r--r--drivers/net/irda/pxaficp_ir.c2
-rw-r--r--drivers/net/irda/sh_irda.c126
-rw-r--r--drivers/net/irda/sh_sir.c4
-rw-r--r--drivers/net/irda/sir_dev.c1
-rw-r--r--drivers/net/irda/smsc-ircc2.c21
-rw-r--r--drivers/net/irda/toim3232-sir.c2
-rw-r--r--drivers/net/irda/via-ircc.c1
-rw-r--r--drivers/net/irda/via-ircc.h2
-rw-r--r--drivers/net/irda/vlsi_ir.c1
-rw-r--r--drivers/net/irda/w83977af_ir.c1
-rw-r--r--drivers/net/macvlan.c46
-rw-r--r--drivers/net/macvtap.c291
-rw-r--r--drivers/net/myri_sbus.c1187
-rw-r--r--drivers/net/myri_sbus.h311
-rw-r--r--drivers/net/netconsole.c13
-rw-r--r--drivers/net/octeon/Makefile2
-rw-r--r--drivers/net/pci-skeleton.c1923
-rw-r--r--drivers/net/pcmcia/Kconfig123
-rw-r--r--drivers/net/pcmcia/Makefile16
-rw-r--r--drivers/net/phy/Kconfig2
-rw-r--r--drivers/net/phy/broadcom.c2
-rw-r--r--drivers/net/phy/dp83640.c223
-rw-r--r--drivers/net/phy/icplus.c102
-rw-r--r--drivers/net/phy/mdio-gpio.c2
-rw-r--r--drivers/net/phy/national.c17
-rw-r--r--drivers/net/phy/phy.c2
-rw-r--r--drivers/net/phy/phy_device.c4
-rw-r--r--drivers/net/phy/realtek.c1
-rw-r--r--drivers/net/phy/vitesse.c34
-rw-r--r--drivers/net/plip/Kconfig38
-rw-r--r--drivers/net/plip/Makefile5
-rw-r--r--drivers/net/plip/plip.c (renamed from drivers/net/plip.c)6
-rw-r--r--drivers/net/ppp/Kconfig175
-rw-r--r--drivers/net/ppp/Makefile13
-rw-r--r--drivers/net/ppp/bsd_comp.c (renamed from drivers/net/bsd_comp.c)2
-rw-r--r--drivers/net/ppp/ppp_async.c (renamed from drivers/net/ppp_async.c)1
-rw-r--r--drivers/net/ppp/ppp_deflate.c (renamed from drivers/net/ppp_deflate.c)0
-rw-r--r--drivers/net/ppp/ppp_generic.c (renamed from drivers/net/ppp_generic.c)9
-rw-r--r--drivers/net/ppp/ppp_mppe.c (renamed from drivers/net/ppp_mppe.c)0
-rw-r--r--drivers/net/ppp/ppp_mppe.h (renamed from drivers/net/ppp_mppe.h)0
-rw-r--r--drivers/net/ppp/ppp_synctty.c (renamed from drivers/net/ppp_synctty.c)1
-rw-r--r--drivers/net/ppp/pppoe.c (renamed from drivers/net/pppoe.c)3
-rw-r--r--drivers/net/ppp/pppox.c (renamed from drivers/net/pppox.c)0
-rw-r--r--drivers/net/ppp/pptp.c (renamed from drivers/net/pptp.c)23
-rw-r--r--drivers/net/rionet.c29
-rw-r--r--drivers/net/slip/Kconfig79
-rw-r--r--drivers/net/slip/Makefile6
-rw-r--r--drivers/net/slip/slhc.c (renamed from drivers/net/slhc.c)2
-rw-r--r--drivers/net/slip/slip.c (renamed from drivers/net/slip.c)68
-rw-r--r--drivers/net/slip/slip.h (renamed from drivers/net/slip.h)10
-rw-r--r--drivers/net/sungem_phy.c7
-rw-r--r--drivers/net/sungem_phy.h132
-rw-r--r--drivers/net/tokenring/3c359.c8
-rw-r--r--drivers/net/tokenring/Kconfig15
-rw-r--r--drivers/net/tokenring/Makefile21
-rw-r--r--drivers/net/tokenring/ibmtr.c5
-rw-r--r--drivers/net/tokenring/ibmtr_cs.c (renamed from drivers/net/pcmcia/ibmtr_cs.c)2
-rw-r--r--drivers/net/tokenring/lanstreamer.c2
-rw-r--r--drivers/net/tokenring/madgemc.c2
-rw-r--r--drivers/net/tokenring/olympic.c2
-rw-r--r--drivers/net/tokenring/smctr.c2
-rw-r--r--drivers/net/tokenring/tms380tr.c2
-rw-r--r--drivers/net/tun.c35
-rw-r--r--drivers/net/usb/Kconfig3
-rw-r--r--drivers/net/usb/asix.c469
-rw-r--r--drivers/net/usb/catc.c2
-rw-r--r--drivers/net/usb/cdc-phonet.c1
-rw-r--r--drivers/net/usb/cdc_ether.c2
-rw-r--r--drivers/net/usb/cdc_ncm.c192
-rw-r--r--drivers/net/usb/dm9601.c2
-rw-r--r--drivers/net/usb/hso.c7
-rw-r--r--drivers/net/usb/int51x1.c2
-rw-r--r--drivers/net/usb/ipheth.c20
-rw-r--r--drivers/net/usb/kalmia.c12
-rw-r--r--drivers/net/usb/kaweth.c2
-rw-r--r--drivers/net/usb/lg-vl600.c27
-rw-r--r--drivers/net/usb/mcs7830.c2
-rw-r--r--drivers/net/usb/pegasus.c2
-rw-r--r--drivers/net/usb/rtl8150.c114
-rw-r--r--drivers/net/usb/smsc75xx.c9
-rw-r--r--drivers/net/usb/smsc95xx.c2
-rw-r--r--drivers/net/usb/usbnet.c9
-rw-r--r--drivers/net/veth.c76
-rw-r--r--drivers/net/virtio_net.c135
-rw-r--r--drivers/net/vmxnet3/vmxnet3_drv.c175
-rw-r--r--drivers/net/vmxnet3/vmxnet3_ethtool.c55
-rw-r--r--drivers/net/vmxnet3/vmxnet3_int.h8
-rw-r--r--drivers/net/wan/c101.c30
-rw-r--r--drivers/net/wan/cosa.c226
-rw-r--r--drivers/net/wan/cycx_drv.c81
-rw-r--r--drivers/net/wan/cycx_main.c32
-rw-r--r--drivers/net/wan/cycx_x25.c177
-rw-r--r--drivers/net/wan/dlci.c22
-rw-r--r--drivers/net/wan/dscc4.c81
-rw-r--r--drivers/net/wan/farsync.c8
-rw-r--r--drivers/net/wan/hd64570.c8
-rw-r--r--drivers/net/wan/hd64572.c8
-rw-r--r--drivers/net/wan/hdlc.c16
-rw-r--r--drivers/net/wan/hdlc_cisco.c21
-rw-r--r--drivers/net/wan/hdlc_fr.c101
-rw-r--r--drivers/net/wan/hdlc_ppp.c23
-rw-r--r--drivers/net/wan/hdlc_x25.c28
-rw-r--r--drivers/net/wan/hostess_sv11.c11
-rw-r--r--drivers/net/wan/ixp4xx_hss.c28
-rw-r--r--drivers/net/wan/lapbether.c21
-rw-r--r--drivers/net/wan/lmc/lmc_var.h2
-rw-r--r--drivers/net/wan/n2.c42
-rw-r--r--drivers/net/wan/pc300_drv.c4
-rw-r--r--drivers/net/wan/pc300_tty.c2
-rw-r--r--drivers/net/wan/pc300too.c36
-rw-r--r--drivers/net/wan/pci200syn.c29
-rw-r--r--drivers/net/wan/sbni.c74
-rw-r--r--drivers/net/wan/sdla.c52
-rw-r--r--drivers/net/wan/sealevel.c11
-rw-r--r--drivers/net/wan/wanxl.c93
-rw-r--r--drivers/net/wan/x25_asy.c50
-rw-r--r--drivers/net/wan/z85230.c34
-rw-r--r--drivers/net/wimax/i2400m/control.c2
-rw-r--r--drivers/net/wimax/i2400m/debugfs.c1
-rw-r--r--drivers/net/wimax/i2400m/fw.c1
-rw-r--r--drivers/net/wimax/i2400m/i2400m.h2
-rw-r--r--drivers/net/wimax/i2400m/netdev.c1
-rw-r--r--drivers/net/wimax/i2400m/rx.c2
-rw-r--r--drivers/net/wimax/i2400m/sdio.c1
-rw-r--r--drivers/net/wimax/i2400m/tx.c1
-rw-r--r--drivers/net/wimax/i2400m/usb.c5
-rw-r--r--drivers/net/wireless/Kconfig1
-rw-r--r--drivers/net/wireless/Makefile5
-rw-r--r--drivers/net/wireless/adm8211.c5
-rw-r--r--drivers/net/wireless/airo.c5
-rw-r--r--drivers/net/wireless/at76c50x-usb.c18
-rw-r--r--drivers/net/wireless/ath/Kconfig3
-rw-r--r--drivers/net/wireless/ath/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath.h53
-rw-r--r--drivers/net/wireless/ath/ath5k/ahb.c50
-rw-r--r--drivers/net/wireless/ath/ath5k/ani.c90
-rw-r--r--drivers/net/wireless/ath/ath5k/ani.h4
-rw-r--r--drivers/net/wireless/ath/ath5k/ath5k.h486
-rw-r--r--drivers/net/wireless/ath/ath5k/attach.c71
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c1239
-rw-r--r--drivers/net/wireless/ath/ath5k/base.h263
-rw-r--r--drivers/net/wireless/ath/ath5k/caps.c51
-rw-r--r--drivers/net/wireless/ath/ath5k/debug.c439
-rw-r--r--drivers/net/wireless/ath/ath5k/debug.h25
-rw-r--r--drivers/net/wireless/ath/ath5k/desc.c11
-rw-r--r--drivers/net/wireless/ath/ath5k/desc.h4
-rw-r--r--drivers/net/wireless/ath/ath5k/dma.c21
-rw-r--r--drivers/net/wireless/ath/ath5k/eeprom.c60
-rw-r--r--drivers/net/wireless/ath/ath5k/eeprom.h12
-rw-r--r--drivers/net/wireless/ath/ath5k/gpio.c1
-rw-r--r--drivers/net/wireless/ath/ath5k/initvals.c17
-rw-r--r--drivers/net/wireless/ath/ath5k/led.c75
-rw-r--r--drivers/net/wireless/ath/ath5k/mac80211-ops.c274
-rw-r--r--drivers/net/wireless/ath/ath5k/pci.c56
-rw-r--r--drivers/net/wireless/ath/ath5k/pcu.c45
-rw-r--r--drivers/net/wireless/ath/ath5k/phy.c222
-rw-r--r--drivers/net/wireless/ath/ath5k/qcu.c17
-rw-r--r--drivers/net/wireless/ath/ath5k/reg.h89
-rw-r--r--drivers/net/wireless/ath/ath5k/reset.c166
-rw-r--r--drivers/net/wireless/ath/ath5k/rfbuffer.h4
-rw-r--r--drivers/net/wireless/ath/ath5k/rfgain.h8
-rw-r--r--drivers/net/wireless/ath/ath5k/rfkill.c67
-rw-r--r--drivers/net/wireless/ath/ath5k/sysfs.c46
-rw-r--r--drivers/net/wireless/ath/ath5k/trace.h24
-rw-r--r--drivers/net/wireless/ath/ath6kl/Kconfig15
-rw-r--r--drivers/net/wireless/ath/ath6kl/Makefile37
-rw-r--r--drivers/net/wireless/ath/ath6kl/bmi.c689
-rw-r--r--drivers/net/wireless/ath/ath6kl/bmi.h250
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c1916
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.h39
-rw-r--r--drivers/net/wireless/ath/ath6kl/common.h97
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.h637
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.c935
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.h138
-rw-r--r--drivers/net/wireless/ath/ath6kl/hif-ops.h77
-rw-r--r--drivers/net/wireless/ath/ath6kl/hif.h208
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc.c2478
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc.h607
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc_hif.c641
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc_hif.h92
-rw-r--r--drivers/net/wireless/ath/ath6kl/init.c1727
-rw-r--r--drivers/net/wireless/ath/ath6kl/main.c1477
-rw-r--r--drivers/net/wireless/ath/ath6kl/sdio.c950
-rw-r--r--drivers/net/wireless/ath/ath6kl/target.h364
-rw-r--r--drivers/net/wireless/ath/ath6kl/testmode.c167
-rw-r--r--drivers/net/wireless/ath/ath6kl/testmode.h36
-rw-r--r--drivers/net/wireless/ath/ath6kl/txrx.c1478
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.c3127
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.h2282
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath9k/ahb.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/ani.c18
-rw-r--r--drivers/net/wireless/ath/ath9k/ani.h6
-rw-r--r--drivers/net/wireless/ath/ath9k/ar5008_initvals.h131
-rw-r--r--drivers/net/wireless/ath/ath9k/ar5008_phy.c48
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9001_initvals.h266
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_calib.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_hw.c100
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_initvals.h3403
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_mac.c264
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_phy.c34
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h216
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_calib.c285
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.c369
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.h3
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_hw.c664
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_mac.c394
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_paprd.c152
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.c165
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.h149
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_rtt.c153
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_rtt.h28
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h1147
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h1080
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9462_1p0_initvals.h1833
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h1928
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9485_initvals.h10
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h1673
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h52
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c106
-rw-r--r--drivers/net/wireless/ath/ath9k/btcoex.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/calib.c19
-rw-r--r--drivers/net/wireless/ath/ath9k/calib.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/common.c32
-rw-r--r--drivers/net/wireless/ath/ath9k/common.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c573
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h69
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.h17
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_4k.c284
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_9287.c181
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_def.c253
-rw-r--r--drivers/net/wireless/ath/ath9k/gpio.c15
-rw-r--r--drivers/net/wireless/ath/ath9k/hif_usb.c12
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h3
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_beacon.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_debug.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_gpio.c10
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c30
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c42
-rw-r--r--drivers/net/wireless/ath/ath9k/hw-ops.h83
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c477
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h100
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c119
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c59
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h57
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c788
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c77
-rw-r--r--drivers/net/wireless/ath/ath9k/rc.c101
-rw-r--r--drivers/net/wireless/ath/ath9k/rc.h6
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c315
-rw-r--r--drivers/net/wireless/ath/ath9k/reg.h127
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c1213
-rw-r--r--drivers/net/wireless/ath/carl9170/Kconfig14
-rw-r--r--drivers/net/wireless/ath/carl9170/carl9170.h37
-rw-r--r--drivers/net/wireless/ath/carl9170/cmd.c34
-rw-r--r--drivers/net/wireless/ath/carl9170/cmd.h5
-rw-r--r--drivers/net/wireless/ath/carl9170/debug.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/fw.c6
-rw-r--r--drivers/net/wireless/ath/carl9170/fwcmd.h30
-rw-r--r--drivers/net/wireless/ath/carl9170/fwdesc.h21
-rw-r--r--drivers/net/wireless/ath/carl9170/hw.h56
-rw-r--r--drivers/net/wireless/ath/carl9170/led.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/mac.c129
-rw-r--r--drivers/net/wireless/ath/carl9170/main.c261
-rw-r--r--drivers/net/wireless/ath/carl9170/phy.c28
-rw-r--r--drivers/net/wireless/ath/carl9170/rx.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/tx.c301
-rw-r--r--drivers/net/wireless/ath/carl9170/usb.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/version.h6
-rw-r--r--drivers/net/wireless/ath/carl9170/wlan.h25
-rw-r--r--drivers/net/wireless/ath/debug.c1
-rw-r--r--drivers/net/wireless/ath/hw.c1
-rw-r--r--drivers/net/wireless/ath/key.c8
-rw-r--r--drivers/net/wireless/ath/main.c8
-rw-r--r--drivers/net/wireless/ath/regd.c3
-rw-r--r--drivers/net/wireless/ath/regd.h2
-rw-r--r--drivers/net/wireless/ath/regd_common.h2
-rw-r--r--drivers/net/wireless/atmel.c1
-rw-r--r--drivers/net/wireless/b43/Kconfig48
-rw-r--r--drivers/net/wireless/b43/Makefile5
-rw-r--r--drivers/net/wireless/b43/b43.h140
-rw-r--r--drivers/net/wireless/b43/bus.c257
-rw-r--r--drivers/net/wireless/b43/bus.h70
-rw-r--r--drivers/net/wireless/b43/debugfs.c2
-rw-r--r--drivers/net/wireless/b43/dma.c246
-rw-r--r--drivers/net/wireless/b43/dma.h20
-rw-r--r--drivers/net/wireless/b43/leds.c17
-rw-r--r--drivers/net/wireless/b43/lo.c8
-rw-r--r--drivers/net/wireless/b43/main.c813
-rw-r--r--drivers/net/wireless/b43/main.h4
-rw-r--r--drivers/net/wireless/b43/pcmcia.c3
-rw-r--r--drivers/net/wireless/b43/phy_a.c23
-rw-r--r--drivers/net/wireless/b43/phy_common.c54
-rw-r--r--drivers/net/wireless/b43/phy_common.h8
-rw-r--r--drivers/net/wireless/b43/phy_g.c94
-rw-r--r--drivers/net/wireless/b43/phy_ht.c618
-rw-r--r--drivers/net/wireless/b43/phy_ht.h65
-rw-r--r--drivers/net/wireless/b43/phy_lcn.c901
-rw-r--r--drivers/net/wireless/b43/phy_lcn.h31
-rw-r--r--drivers/net/wireless/b43/phy_lp.c137
-rw-r--r--drivers/net/wireless/b43/phy_n.c651
-rw-r--r--drivers/net/wireless/b43/phy_n.h4
-rw-r--r--drivers/net/wireless/b43/pio.c24
-rw-r--r--drivers/net/wireless/b43/radio_2055.c3
-rw-r--r--drivers/net/wireless/b43/radio_2055.h5
-rw-r--r--drivers/net/wireless/b43/radio_2056.c2
-rw-r--r--drivers/net/wireless/b43/radio_2056.h31
-rw-r--r--drivers/net/wireless/b43/radio_2059.c176
-rw-r--r--drivers/net/wireless/b43/radio_2059.h54
-rw-r--r--drivers/net/wireless/b43/rfkill.c11
-rw-r--r--drivers/net/wireless/b43/sdio.c12
-rw-r--r--drivers/net/wireless/b43/sysfs.c6
-rw-r--r--drivers/net/wireless/b43/tables.c2
-rw-r--r--drivers/net/wireless/b43/tables_lpphy.c17
-rw-r--r--drivers/net/wireless/b43/tables_nphy.c3
-rw-r--r--drivers/net/wireless/b43/tables_nphy.h8
-rw-r--r--drivers/net/wireless/b43/tables_phy_ht.c836
-rw-r--r--drivers/net/wireless/b43/tables_phy_ht.h26
-rw-r--r--drivers/net/wireless/b43/tables_phy_lcn.c724
-rw-r--r--drivers/net/wireless/b43/tables_phy_lcn.h24
-rw-r--r--drivers/net/wireless/b43/wa.c26
-rw-r--r--drivers/net/wireless/b43/xmit.c161
-rw-r--r--drivers/net/wireless/b43/xmit.h88
-rw-r--r--drivers/net/wireless/b43legacy/b43legacy.h25
-rw-r--r--drivers/net/wireless/b43legacy/debugfs.c2
-rw-r--r--drivers/net/wireless/b43legacy/dma.c387
-rw-r--r--drivers/net/wireless/b43legacy/dma.h109
-rw-r--r--drivers/net/wireless/b43legacy/ilt.c2
-rw-r--r--drivers/net/wireless/b43legacy/leds.c2
-rw-r--r--drivers/net/wireless/b43legacy/main.c25
-rw-r--r--drivers/net/wireless/b43legacy/main.h2
-rw-r--r--drivers/net/wireless/b43legacy/phy.c2
-rw-r--r--drivers/net/wireless/b43legacy/phy.h2
-rw-r--r--drivers/net/wireless/b43legacy/pio.c2
-rw-r--r--drivers/net/wireless/b43legacy/radio.c2
-rw-r--r--drivers/net/wireless/b43legacy/radio.h2
-rw-r--r--drivers/net/wireless/b43legacy/rfkill.c2
-rw-r--r--drivers/net/wireless/b43legacy/sysfs.c2
-rw-r--r--drivers/net/wireless/b43legacy/xmit.c4
-rw-r--r--drivers/net/wireless/brcm80211/Kconfig (renamed from drivers/staging/brcm80211/Kconfig)15
-rw-r--r--drivers/net/wireless/brcm80211/Makefile (renamed from drivers/staging/brcm80211/Makefile)5
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/Makefile (renamed from drivers/staging/brcm80211/brcmfmac/Makefile)31
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h (renamed from drivers/staging/brcm80211/brcmfmac/bcmchip.h)9
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c372
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c626
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd.h776
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h57
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c498
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c895
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h58
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c1356
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h60
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c4591
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h252
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c3868
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h (renamed from drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h)265
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/Makefile (renamed from drivers/staging/brcm80211/brcmsmac/Makefile)58
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/aiutils.c2079
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/aiutils.h (renamed from drivers/staging/brcm80211/brcmsmac/aiutils.h)422
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/ampdu.c (renamed from drivers/staging/brcm80211/brcmsmac/wlc_ampdu.c)892
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/ampdu.h (renamed from drivers/staging/brcm80211/brcmfmac/sdiovar.h)32
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/antsel.c (renamed from drivers/staging/brcm80211/brcmsmac/wlc_antsel.c)313
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/antsel.h (renamed from drivers/staging/brcm80211/brcmsmac/wlc_antsel.h)16
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c (renamed from drivers/staging/brcm80211/brcmsmac/wlc_alloc.h)11
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h92
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/channel.c (renamed from drivers/staging/brcm80211/brcmsmac/wlc_channel.c)1482
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/channel.h53
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/d11.h (renamed from drivers/staging/brcm80211/brcmsmac/d11.h)849
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/dma.c1426
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/dma.h120
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c (renamed from drivers/staging/brcm80211/brcmsmac/wl_mac80211.c)1900
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h108
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/main.c8775
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/main.h735
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/nicpci.c835
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/nicpci.h82
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/otp.c426
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/otp.h (renamed from drivers/staging/brcm80211/brcmsmac/wlc_types.h)31
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_cmn.c)1755
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h301
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_int.h)597
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_lcn.c)3844
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_lcn.h)12
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_n.c)15933
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_qmath.c)176
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_qmath.h)8
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_radio.h)6
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phyreg_n.h)14
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_lcn.c)1283
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_lcn.h)35
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_n.c)184
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h (renamed from drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_n.h)35
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c225
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h182
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/pmu.c458
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/pmu.h38
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/pub.h634
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/rate.c (renamed from drivers/staging/brcm80211/brcmsmac/wlc_rate.c)363
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/rate.h250
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/scb.h82
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/srom.c1298
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/srom.h (renamed from drivers/staging/brcm80211/include/bcmsrom.h)14
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/stf.c436
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/stf.h42
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/types.h352
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c109
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h58
-rw-r--r--drivers/net/wireless/brcm80211/brcmutil/Makefile (renamed from drivers/staging/brcm80211/util/Makefile)7
-rw-r--r--drivers/net/wireless/brcm80211/brcmutil/utils.c386
-rw-r--r--drivers/net/wireless/brcm80211/include/brcm_hw_ids.h59
-rw-r--r--drivers/net/wireless/brcm80211/include/brcmu_utils.h195
-rw-r--r--drivers/net/wireless/brcm80211/include/brcmu_wifi.h239
-rw-r--r--drivers/net/wireless/brcm80211/include/chipcommon.h284
-rw-r--r--drivers/net/wireless/brcm80211/include/defs.h104
-rw-r--r--drivers/net/wireless/brcm80211/include/soc.h90
-rw-r--r--drivers/net/wireless/hostap/hostap_80211_rx.c1
-rw-r--r--drivers/net/wireless/hostap/hostap_80211_tx.c1
-rw-r--r--drivers/net/wireless/hostap/hostap_ap.c2
-rw-r--r--drivers/net/wireless/hostap/hostap_cs.c2
-rw-r--r--drivers/net/wireless/hostap/hostap_info.c1
-rw-r--r--drivers/net/wireless/hostap/hostap_ioctl.c1
-rw-r--r--drivers/net/wireless/hostap/hostap_main.c7
-rw-r--r--drivers/net/wireless/hostap/hostap_proc.c1
-rw-r--r--drivers/net/wireless/hostap/hostap_wlan.h1
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.c27
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2200.c42
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2200.h1
-rw-r--r--drivers/net/wireless/ipw2x00/libipw_rx.c4
-rw-r--r--drivers/net/wireless/ipw2x00/libipw_tx.c2
-rw-r--r--drivers/net/wireless/ipw2x00/libipw_wx.c1
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-3945-led.c1
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-3945-rs.c14
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-3945.c11
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-4965-led.c1
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-4965-lib.c79
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-4965-rs.c4
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-4965-rx.c78
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-4965-tx.c5
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-4965.c19
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-commands.h11
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-core.c99
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-core.h23
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-debugfs.c193
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-dev.h61
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-devtrace.c3
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-devtrace.h60
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-eeprom.c1
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-hcmd.c2
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-helpers.h9
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-led.c1
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-rx.c22
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-scan.c93
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-sta.c1
-rw-r--r--drivers/net/wireless/iwlegacy/iwl-tx.c5
-rw-r--r--drivers/net/wireless/iwlegacy/iwl3945-base.c310
-rw-r--r--drivers/net/wireless/iwlegacy/iwl4965-base.c476
-rw-r--r--drivers/net/wireless/iwlwifi/Kconfig43
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile36
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-1000.c88
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-2000.c199
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c179
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c204
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-calib.c199
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-calib.h7
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c301
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c328
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-hw.h39
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-ict.c306
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-lib.c1766
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rs.c155
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rs.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rx.c (renamed from drivers/net/wireless/iwlwifi/iwl-rx.c)584
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rxon.c299
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-sta.c1327
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tt.c120
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tt.h1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tx.c1574
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-ucode.c315
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c3260
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.h287
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-bus.h201
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-cfg.h (renamed from drivers/net/wireless/iwlwifi/iwl-5000-hw.h)73
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-commands.h276
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c531
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h353
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-csr.h19
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debug.h60
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debugfs.c544
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h657
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom.c338
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom.h33
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fh.h22
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-hcmd.c291
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-helpers.h175
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.c192
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.h62
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-led.c27
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-pci.c568
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-power.c62
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-power.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h99
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-scan.c810
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-shared.h534
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.c820
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.h141
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sv-open.c208
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-testmode.h251
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h436
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c1435
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c1141
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie.c2003
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.c (renamed from drivers/net/wireless/iwlwifi/iwl-6000-hw.h)28
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h351
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-tx.c829
-rw-r--r--drivers/net/wireless/iwmc3200wifi/cfg80211.c6
-rw-r--r--drivers/net/wireless/iwmc3200wifi/commands.c1
-rw-r--r--drivers/net/wireless/iwmc3200wifi/debugfs.c1
-rw-r--r--drivers/net/wireless/iwmc3200wifi/fw.c2
-rw-r--r--drivers/net/wireless/iwmc3200wifi/main.c1
-rw-r--r--drivers/net/wireless/iwmc3200wifi/sdio.c1
-rw-r--r--drivers/net/wireless/libertas/README25
-rw-r--r--drivers/net/wireless/libertas/cfg.c148
-rw-r--r--drivers/net/wireless/libertas/cfg.h2
-rw-r--r--drivers/net/wireless/libertas/cmd.c52
-rw-r--r--drivers/net/wireless/libertas/cmd.h2
-rw-r--r--drivers/net/wireless/libertas/cmdresp.c7
-rw-r--r--drivers/net/wireless/libertas/debugfs.c2
-rw-r--r--drivers/net/wireless/libertas/decl.h6
-rw-r--r--drivers/net/wireless/libertas/dev.h32
-rw-r--r--drivers/net/wireless/libertas/ethtool.c2
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c313
-rw-r--r--drivers/net/wireless/libertas/if_spi.c15
-rw-r--r--drivers/net/wireless/libertas/if_usb.c36
-rw-r--r--drivers/net/wireless/libertas/main.c268
-rw-r--r--drivers/net/wireless/libertas/mesh.c1010
-rw-r--r--drivers/net/wireless/libertas/mesh.h58
-rw-r--r--drivers/net/wireless/libertas/rx.c3
-rw-r--r--drivers/net/wireless/libertas/tx.c5
-rw-r--r--drivers/net/wireless/libertas_tf/cmd.c2
-rw-r--r--drivers/net/wireless/libertas_tf/deb_defs.h2
-rw-r--r--drivers/net/wireless/libertas_tf/if_usb.c2
-rw-r--r--drivers/net/wireless/libertas_tf/main.c4
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c474
-rw-r--r--drivers/net/wireless/mac80211_hwsim.h133
-rw-r--r--drivers/net/wireless/mwifiex/11n.c33
-rw-r--r--drivers/net/wireless/mwifiex/11n.h14
-rw-r--r--drivers/net/wireless/mwifiex/11n_aggr.c55
-rw-r--r--drivers/net/wireless/mwifiex/11n_aggr.h1
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.c5
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.h5
-rw-r--r--drivers/net/wireless/mwifiex/Kconfig11
-rw-r--r--drivers/net/wireless/mwifiex/Makefile3
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c448
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.h3
-rw-r--r--drivers/net/wireless/mwifiex/cfp.c10
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c65
-rw-r--r--drivers/net/wireless/mwifiex/debugfs.c35
-rw-r--r--drivers/net/wireless/mwifiex/decl.h13
-rw-r--r--drivers/net/wireless/mwifiex/fw.h62
-rw-r--r--drivers/net/wireless/mwifiex/init.c96
-rw-r--r--drivers/net/wireless/mwifiex/ioctl.h6
-rw-r--r--drivers/net/wireless/mwifiex/join.c58
-rw-r--r--drivers/net/wireless/mwifiex/main.c308
-rw-r--r--drivers/net/wireless/mwifiex/main.h135
-rw-r--r--drivers/net/wireless/mwifiex/pcie.c1948
-rw-r--r--drivers/net/wireless/mwifiex/pcie.h148
-rw-r--r--drivers/net/wireless/mwifiex/scan.c1660
-rw-r--r--drivers/net/wireless/mwifiex/sdio.c113
-rw-r--r--drivers/net/wireless/mwifiex/sdio.h30
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c177
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c52
-rw-r--r--drivers/net/wireless/mwifiex/sta_event.c9
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c309
-rw-r--r--drivers/net/wireless/mwifiex/sta_rx.c2
-rw-r--r--drivers/net/wireless/mwifiex/sta_tx.c15
-rw-r--r--drivers/net/wireless/mwifiex/txrx.c47
-rw-r--r--drivers/net/wireless/mwifiex/util.c5
-rw-r--r--drivers/net/wireless/mwifiex/util.h9
-rw-r--r--drivers/net/wireless/mwifiex/wmm.c28
-rw-r--r--drivers/net/wireless/mwl8k.c23
-rw-r--r--drivers/net/wireless/orinoco/airport.c9
-rw-r--r--drivers/net/wireless/orinoco/cfg.c6
-rw-r--r--drivers/net/wireless/orinoco/fw.c8
-rw-r--r--drivers/net/wireless/orinoco/fw.h2
-rw-r--r--drivers/net/wireless/orinoco/hermes.c40
-rw-r--r--drivers/net/wireless/orinoco/hermes.h37
-rw-r--r--drivers/net/wireless/orinoco/hermes_dld.c8
-rw-r--r--drivers/net/wireless/orinoco/hermes_dld.h12
-rw-r--r--drivers/net/wireless/orinoco/hw.c48
-rw-r--r--drivers/net/wireless/orinoco/hw.h2
-rw-r--r--drivers/net/wireless/orinoco/main.c50
-rw-r--r--drivers/net/wireless/orinoco/mic.c8
-rw-r--r--drivers/net/wireless/orinoco/orinoco.h16
-rw-r--r--drivers/net/wireless/orinoco/orinoco_cs.c11
-rw-r--r--drivers/net/wireless/orinoco/orinoco_nortel.c3
-rw-r--r--drivers/net/wireless/orinoco/orinoco_pci.c4
-rw-r--r--drivers/net/wireless/orinoco/orinoco_plx.c6
-rw-r--r--drivers/net/wireless/orinoco/orinoco_tmd.c2
-rw-r--r--drivers/net/wireless/orinoco/orinoco_usb.c25
-rw-r--r--drivers/net/wireless/orinoco/spectrum_cs.c10
-rw-r--r--drivers/net/wireless/orinoco/wext.c15
-rw-r--r--drivers/net/wireless/p54/eeprom.c27
-rw-r--r--drivers/net/wireless/p54/fwio.c3
-rw-r--r--drivers/net/wireless/p54/main.c117
-rw-r--r--drivers/net/wireless/p54/p54.h18
-rw-r--r--drivers/net/wireless/p54/p54pci.c1
-rw-r--r--drivers/net/wireless/p54/p54pci.h1
-rw-r--r--drivers/net/wireless/p54/p54spi.c2
-rw-r--r--drivers/net/wireless/p54/p54usb.c1
-rw-r--r--drivers/net/wireless/p54/txrx.c73
-rw-r--r--drivers/net/wireless/prism54/islpci_dev.c1
-rw-r--r--drivers/net/wireless/prism54/islpci_dev.h1
-rw-r--r--drivers/net/wireless/prism54/islpci_hotplug.c1
-rw-r--r--drivers/net/wireless/ray_cs.c2
-rw-r--r--drivers/net/wireless/rndis_wlan.c4
-rw-r--r--drivers/net/wireless/rt2x00/Kconfig9
-rw-r--r--drivers/net/wireless/rt2x00/rt2400pci.c50
-rw-r--r--drivers/net/wireless/rt2x00/rt2500pci.c43
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.c1
-rw-r--r--drivers/net/wireless/rt2x00/rt2800.h19
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c626
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.h11
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c69
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c110
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h36
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00crypto.c6
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c57
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00lib.h6
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00mac.c72
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c120
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.h9
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.c32
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.c42
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.c9
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180/dev.c7
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187/dev.c6
-rw-r--r--drivers/net/wireless/rtlwifi/Kconfig17
-rw-r--r--drivers/net/wireless/rtlwifi/Makefile1
-rw-r--r--drivers/net/wireless/rtlwifi/base.c194
-rw-r--r--drivers/net/wireless/rtlwifi/base.h2
-rw-r--r--drivers/net/wireless/rtlwifi/cam.c9
-rw-r--r--drivers/net/wireless/rtlwifi/core.c37
-rw-r--r--drivers/net/wireless/rtlwifi/core.h2
-rw-r--r--drivers/net/wireless/rtlwifi/debug.c2
-rw-r--r--drivers/net/wireless/rtlwifi/debug.h5
-rw-r--r--drivers/net/wireless/rtlwifi/efuse.c15
-rw-r--r--drivers/net/wireless/rtlwifi/pci.c332
-rw-r--r--drivers/net/wireless/rtlwifi/pci.h42
-rw-r--r--drivers/net/wireless/rtlwifi/ps.c98
-rw-r--r--drivers/net/wireless/rtlwifi/ps.h3
-rw-r--r--drivers/net/wireless/rtlwifi/rc.c2
-rw-r--r--drivers/net/wireless/rtlwifi/regd.c18
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c5
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c8
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192c/main.c1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c13
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/def.h153
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/hw.c23
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/led.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/phy.c14
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/reg.h6
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/rf.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/sw.c78
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/trx.c128
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/trx.h6
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/def.h4
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/hw.c77
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/led.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/mac.c26
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/mac.h6
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/phy.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/rf.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/sw.c48
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/trx.c36
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/Makefile14
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/def.h273
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/dm.c1355
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/dm.h212
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/fw.c790
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/fw.h155
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/hw.c2328
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/hw.h66
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/led.c159
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/led.h38
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/phy.c3831
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/phy.h178
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/reg.h1313
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/rf.c628
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/rf.h44
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/sw.c437
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/sw.h37
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/table.c1690
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/table.h57
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/trx.c866
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/trx.h750
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/def.h41
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/dm.c7
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/fw.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/hw.c60
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/led.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/phy.c27
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/reg.h7
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/rf.c6
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/sw.c71
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/trx.c181
-rw-r--r--drivers/net/wireless/rtlwifi/usb.c54
-rw-r--r--drivers/net/wireless/rtlwifi/wifi.h64
-rw-r--r--drivers/net/wireless/wl1251/acx.c6
-rw-r--r--drivers/net/wireless/wl1251/cmd.c2
-rw-r--r--drivers/net/wireless/wl1251/cmd.h2
-rw-r--r--drivers/net/wireless/wl1251/main.c3
-rw-r--r--drivers/net/wireless/wl1251/sdio.c1
-rw-r--r--drivers/net/wireless/wl1251/spi.c1
-rw-r--r--drivers/net/wireless/wl1251/wl12xx_80211.h2
-rw-r--r--drivers/net/wireless/wl12xx/Kconfig12
-rw-r--r--drivers/net/wireless/wl12xx/Makefile6
-rw-r--r--drivers/net/wireless/wl12xx/acx.c445
-rw-r--r--drivers/net/wireless/wl12xx/acx.h433
-rw-r--r--drivers/net/wireless/wl12xx/boot.c56
-rw-r--r--drivers/net/wireless/wl12xx/boot.h3
-rw-r--r--drivers/net/wireless/wl12xx/cmd.c889
-rw-r--r--drivers/net/wireless/wl12xx/cmd.h410
-rw-r--r--drivers/net/wireless/wl12xx/conf.h452
-rw-r--r--drivers/net/wireless/wl12xx/debugfs.c216
-rw-r--r--drivers/net/wireless/wl12xx/event.c127
-rw-r--r--drivers/net/wireless/wl12xx/event.h71
-rw-r--r--drivers/net/wireless/wl12xx/ini.h3
-rw-r--r--drivers/net/wireless/wl12xx/init.c142
-rw-r--r--drivers/net/wireless/wl12xx/io.c7
-rw-r--r--drivers/net/wireless/wl12xx/io.h16
-rw-r--r--drivers/net/wireless/wl12xx/main.c2031
-rw-r--r--drivers/net/wireless/wl12xx/ps.c31
-rw-r--r--drivers/net/wireless/wl12xx/reg.h75
-rw-r--r--drivers/net/wireless/wl12xx/rx.c102
-rw-r--r--drivers/net/wireless/wl12xx/rx.h30
-rw-r--r--drivers/net/wireless/wl12xx/scan.c290
-rw-r--r--drivers/net/wireless/wl12xx/scan.h49
-rw-r--r--drivers/net/wireless/wl12xx/sdio.c87
-rw-r--r--drivers/net/wireless/wl12xx/sdio_test.c17
-rw-r--r--drivers/net/wireless/wl12xx/spi.c20
-rw-r--r--drivers/net/wireless/wl12xx/testmode.c52
-rw-r--r--drivers/net/wireless/wl12xx/tx.c346
-rw-r--r--drivers/net/wireless/wl12xx/tx.h54
-rw-r--r--drivers/net/wireless/wl12xx/wl12xx.h217
-rw-r--r--drivers/net/wireless/wl12xx/wl12xx_80211.h27
-rw-r--r--drivers/net/wireless/wl3501_cs.c2
-rw-r--r--drivers/net/wireless/zd1201.c2
-rw-r--r--drivers/net/wireless/zd1211rw/zd_chip.h2
-rw-r--r--drivers/net/wireless/zd1211rw/zd_def.h6
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.c120
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.h1
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.c130
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.h5
-rw-r--r--drivers/net/xen-netback/common.h11
-rw-r--r--drivers/net/xen-netback/interface.c4
-rw-r--r--drivers/net/xen-netback/netback.c139
-rw-r--r--drivers/net/xen-netfront.c107
-rw-r--r--drivers/net/zorro8390.c459
-rw-r--r--drivers/nfc/Kconfig35
-rw-r--r--drivers/nfc/Makefile4
-rw-r--r--drivers/nfc/nfcwilink.c343
-rw-r--r--drivers/nfc/pn533.c1623
-rw-r--r--drivers/of/Kconfig8
-rw-r--r--drivers/of/Makefile1
-rw-r--r--drivers/of/address.c20
-rw-r--r--drivers/of/base.c300
-rw-r--r--drivers/of/fdt.c13
-rw-r--r--drivers/of/gpio.c16
-rw-r--r--drivers/of/irq.c107
-rw-r--r--drivers/of/of_mdio.c1
-rw-r--r--drivers/of/of_net.c46
-rw-r--r--drivers/of/of_pci.c114
-rw-r--r--drivers/of/of_pci_irq.c93
-rw-r--r--drivers/of/of_spi.c1
-rw-r--r--drivers/of/pdt.c8
-rw-r--r--drivers/of/platform.c206
-rw-r--r--drivers/oprofile/event_buffer.c4
-rw-r--r--drivers/oprofile/oprofile_perf.c8
-rw-r--r--drivers/oprofile/oprofile_stats.h2
-rw-r--r--drivers/oprofile/oprofilefs.c6
-rw-r--r--drivers/parisc/ccio-dma.c1
-rw-r--r--drivers/parisc/sba_iommu.c17
-rw-r--r--drivers/parport/parport_ax88796.c2
-rw-r--r--drivers/parport/parport_pc.c57
-rw-r--r--drivers/parport/parport_serial.c229
-rw-r--r--drivers/pci/Kconfig38
-rw-r--r--drivers/pci/Makefile8
-rw-r--r--drivers/pci/ats.c439
-rw-r--r--drivers/pci/hotplug-pci.c1
-rw-r--r--drivers/pci/hotplug/acpi_pcihp.c6
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c140
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_core.c2
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c17
-rw-r--r--drivers/pci/hotplug/pciehp_acpi.c1
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c38
-rw-r--r--drivers/pci/hotplug/pcihp_slot.c48
-rw-r--r--drivers/pci/hotplug/rpadlpar_core.c3
-rw-r--r--drivers/pci/hotplug/shpchp_core.c4
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c4
-rw-r--r--drivers/pci/hotplug/shpchp_sysfs.c21
-rw-r--r--drivers/pci/htirq.c1
-rw-r--r--drivers/pci/ioapic.c1
-rw-r--r--drivers/pci/iov.c143
-rw-r--r--drivers/pci/irq.c1
-rw-r--r--drivers/pci/msi.c1
-rw-r--r--drivers/pci/of.c61
-rw-r--r--drivers/pci/pci-acpi.c6
-rw-r--r--drivers/pci/pci-driver.c18
-rw-r--r--drivers/pci/pci-label.c2
-rw-r--r--drivers/pci/pci-sysfs.c1
-rw-r--r--drivers/pci/pci.c141
-rw-r--r--drivers/pci/pci.h6
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c76
-rw-r--r--drivers/pci/pcie/aer/aerdrv_errprint.c3
-rw-r--r--drivers/pci/pcie/aspm.c2
-rw-r--r--drivers/pci/pcie/pme.c9
-rw-r--r--drivers/pci/probe.c247
-rw-r--r--drivers/pci/quirks.c109
-rw-r--r--drivers/pci/rom.c1
-rw-r--r--drivers/pci/setup-bus.c171
-rw-r--r--drivers/pci/setup-irq.c4
-rw-r--r--drivers/pci/setup-res.c156
-rw-r--r--drivers/pci/slot.c1
-rw-r--r--drivers/pci/vpd.c1
-rw-r--r--drivers/pci/xen-pcifront.c7
-rw-r--r--drivers/pcmcia/at91_cf.c7
-rw-r--r--drivers/pcmcia/db1xxx_ss.c1
-rw-r--r--drivers/pcmcia/electra_cf.c4
-rw-r--r--drivers/pcmcia/pxa2xx_balloon3.c12
-rw-r--r--drivers/pcmcia/pxa2xx_base.c1
-rw-r--r--drivers/pcmcia/pxa2xx_cm_x255.c12
-rw-r--r--drivers/pcmcia/pxa2xx_cm_x270.c12
-rw-r--r--drivers/pcmcia/pxa2xx_cm_x2xx.c3
-rw-r--r--drivers/pcmcia/pxa2xx_colibri.c11
-rw-r--r--drivers/pcmcia/pxa2xx_mainstone.c10
-rw-r--r--drivers/pcmcia/pxa2xx_palmld.c11
-rw-r--r--drivers/pcmcia/pxa2xx_palmtc.c11
-rw-r--r--drivers/pcmcia/pxa2xx_palmtx.c11
-rw-r--r--drivers/pcmcia/pxa2xx_sharpsl.c3
-rw-r--r--drivers/pcmcia/pxa2xx_stargate2.c34
-rw-r--r--drivers/pcmcia/pxa2xx_trizeps4.c4
-rw-r--r--drivers/pcmcia/pxa2xx_viper.c10
-rw-r--r--drivers/pcmcia/pxa2xx_vpac270.c6
-rw-r--r--drivers/pcmcia/rsrc_iodyn.c6
-rw-r--r--drivers/pcmcia/rsrc_nonstatic.c6
-rw-r--r--drivers/pcmcia/sa1100_simpad.c35
-rw-r--r--drivers/pcmcia/soc_common.c7
-rw-r--r--drivers/pinctrl/Kconfig35
-rw-r--r--drivers/pinctrl/Makefile8
-rw-r--r--drivers/pinctrl/core.c599
-rw-r--r--drivers/pinctrl/core.h71
-rw-r--r--drivers/pinctrl/pinmux-sirf.c1215
-rw-r--r--drivers/pinctrl/pinmux-u300.c1135
-rw-r--r--drivers/pinctrl/pinmux.c1190
-rw-r--r--drivers/pinctrl/pinmux.h47
-rw-r--r--drivers/platform/x86/Kconfig34
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/acer-wmi.c518
-rw-r--r--drivers/platform/x86/acerhdf.c13
-rw-r--r--drivers/platform/x86/asus-laptop.c387
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c27
-rw-r--r--drivers/platform/x86/asus-wmi.c245
-rw-r--r--drivers/platform/x86/asus-wmi.h7
-rw-r--r--drivers/platform/x86/dell-laptop.c85
-rw-r--r--drivers/platform/x86/dell-wmi.c10
-rw-r--r--drivers/platform/x86/eeepc-laptop.c2
-rw-r--r--drivers/platform/x86/eeepc-wmi.c27
-rw-r--r--drivers/platform/x86/hp_accel.c15
-rw-r--r--drivers/platform/x86/ideapad-laptop.c400
-rw-r--r--drivers/platform/x86/intel_ips.c4
-rw-r--r--drivers/platform/x86/intel_menlow.c2
-rw-r--r--drivers/platform/x86/intel_mid_thermal.c26
-rw-r--r--drivers/platform/x86/intel_rar_register.c4
-rw-r--r--drivers/platform/x86/intel_scu_ipc.c3
-rw-r--r--drivers/platform/x86/intel_scu_ipcutil.c8
-rw-r--r--drivers/platform/x86/msi-laptop.c10
-rw-r--r--drivers/platform/x86/msi-wmi.c2
-rw-r--r--drivers/platform/x86/samsung-laptop.c127
-rw-r--r--drivers/platform/x86/samsung-q10.c196
-rw-r--r--drivers/platform/x86/sony-laptop.c2
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c13
-rw-r--r--drivers/platform/x86/topstar-laptop.c1
-rw-r--r--drivers/platform/x86/toshiba_acpi.c641
-rw-r--r--drivers/platform/x86/wmi.c7
-rw-r--r--drivers/pnp/pnpacpi/core.c1
-rw-r--r--drivers/pnp/pnpacpi/rsparser.c40
-rw-r--r--drivers/pnp/pnpbios/rsparser.c12
-rw-r--r--drivers/power/Kconfig15
-rw-r--r--drivers/power/Makefile2
-rw-r--r--drivers/power/apm_power.c8
-rw-r--r--drivers/power/bq20z75.c103
-rw-r--r--drivers/power/ds2780_battery.c86
-rw-r--r--drivers/power/ds2782_battery.c4
-rw-r--r--drivers/power/gpio-charger.c2
-rw-r--r--drivers/power/max17042_battery.c176
-rw-r--r--drivers/power/max8903_charger.c17
-rw-r--r--drivers/power/max8997_charger.c208
-rw-r--r--drivers/power/max8998_charger.c220
-rw-r--r--drivers/power/power_supply_sysfs.c1
-rw-r--r--drivers/power/s3c_adc_battery.c3
-rw-r--r--drivers/power/twl4030_charger.c10
-rw-r--r--drivers/power/wm831x_backup.c12
-rw-r--r--drivers/power/wm831x_power.c26
-rw-r--r--drivers/pps/clients/Kconfig9
-rw-r--r--drivers/pps/clients/Makefile1
-rw-r--r--drivers/pps/clients/pps-gpio.c227
-rw-r--r--drivers/pps/clients/pps-ktimer.c12
-rw-r--r--drivers/pps/clients/pps_parport.c9
-rw-r--r--drivers/pps/kapi.c20
-rw-r--r--drivers/ps3/ps3-vuart.c2
-rw-r--r--drivers/ps3/ps3stor_lib.c3
-rw-r--r--drivers/ps3/sys-manager-core.c1
-rw-r--r--drivers/rapidio/Kconfig6
-rw-r--r--drivers/rapidio/Makefile1
-rw-r--r--drivers/rapidio/devices/Kconfig10
-rw-r--r--drivers/rapidio/devices/Makefile5
-rw-r--r--drivers/rapidio/devices/tsi721.c2360
-rw-r--r--drivers/rapidio/devices/tsi721.h766
-rw-r--r--drivers/rapidio/rio-scan.c9
-rw-r--r--drivers/rapidio/switches/idt_gen2.c1
-rw-r--r--drivers/regulator/88pm8607.c3
-rw-r--r--drivers/regulator/Kconfig24
-rw-r--r--drivers/regulator/Makefile3
-rw-r--r--drivers/regulator/aat2870-regulator.c233
-rw-r--r--drivers/regulator/ab8500.c1
-rw-r--r--drivers/regulator/bq24022.c1
-rw-r--r--drivers/regulator/core.c296
-rw-r--r--drivers/regulator/da903x.c1
-rw-r--r--drivers/regulator/db8500-prcmu.c3
-rw-r--r--drivers/regulator/dummy.c33
-rw-r--r--drivers/regulator/fixed.c1
-rw-r--r--drivers/regulator/gpio-regulator.c358
-rw-r--r--drivers/regulator/lp3971.c1
-rw-r--r--drivers/regulator/lp3972.c1
-rw-r--r--drivers/regulator/max8649.c2
-rw-r--r--drivers/regulator/max8925-regulator.c1
-rw-r--r--drivers/regulator/max8952.c3
-rw-r--r--drivers/regulator/max8997.c1
-rw-r--r--drivers/regulator/mc13783-regulator.c7
-rw-r--r--drivers/regulator/mc13892-regulator.c1
-rw-r--r--drivers/regulator/mc13xxx-regulator-core.c1
-rw-r--r--drivers/regulator/tps65023-regulator.c278
-rw-r--r--drivers/regulator/tps6507x-regulator.c8
-rw-r--r--drivers/regulator/tps6586x-regulator.c33
-rw-r--r--drivers/regulator/tps65910-regulator.c77
-rw-r--r--drivers/regulator/tps65912-regulator.c798
-rw-r--r--drivers/regulator/twl-regulator.c66
-rw-r--r--drivers/regulator/userspace-consumer.c1
-rw-r--r--drivers/regulator/virtual.c1
-rw-r--r--drivers/regulator/wm831x-dcdc.c126
-rw-r--r--drivers/regulator/wm831x-ldo.c25
-rw-r--r--drivers/regulator/wm8400-regulator.c1
-rw-r--r--drivers/regulator/wm8994-regulator.c17
-rw-r--r--drivers/rtc/Kconfig22
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/class.c97
-rw-r--r--drivers/rtc/interface.c56
-rw-r--r--drivers/rtc/rtc-at32ap700x.c2
-rw-r--r--drivers/rtc/rtc-cmos.c6
-rw-r--r--drivers/rtc/rtc-dm355evm.c1
-rw-r--r--drivers/rtc/rtc-ds1286.c2
-rw-r--r--drivers/rtc/rtc-ds1305.c1
-rw-r--r--drivers/rtc/rtc-ds1307.c27
-rw-r--r--drivers/rtc/rtc-ds1511.c3
-rw-r--r--drivers/rtc/rtc-ds1553.c1
-rw-r--r--drivers/rtc/rtc-ds1672.c1
-rw-r--r--drivers/rtc/rtc-ds1742.c3
-rw-r--r--drivers/rtc/rtc-em3027.c1
-rw-r--r--drivers/rtc/rtc-ep93xx.c16
-rw-r--r--drivers/rtc/rtc-imxdi.c1
-rw-r--r--drivers/rtc/rtc-isl12022.c1
-rw-r--r--drivers/rtc/rtc-lib.c2
-rw-r--r--drivers/rtc/rtc-m48t35.c2
-rw-r--r--drivers/rtc/rtc-m48t59.c2
-rw-r--r--drivers/rtc/rtc-mc13xxx.c6
-rw-r--r--drivers/rtc/rtc-mpc5121.c81
-rw-r--r--drivers/rtc/rtc-mrst.c24
-rw-r--r--drivers/rtc/rtc-mv.c1
-rw-r--r--drivers/rtc/rtc-omap.c2
-rw-r--r--drivers/rtc/rtc-pcf2123.c1
-rw-r--r--drivers/rtc/rtc-pcf8563.c1
-rw-r--r--drivers/rtc/rtc-pm8xxx.c550
-rw-r--r--drivers/rtc/rtc-puv3.c9
-rw-r--r--drivers/rtc/rtc-rs5c348.c1
-rw-r--r--drivers/rtc/rtc-rs5c372.c1
-rw-r--r--drivers/rtc/rtc-s3c.c137
-rw-r--r--drivers/rtc/rtc-stk17ta8.c1
-rw-r--r--drivers/rtc/rtc-stmp3xxx.c153
-rw-r--r--drivers/rtc/rtc-tegra.c2
-rw-r--r--drivers/rtc/rtc-twl.c62
-rw-r--r--drivers/rtc/rtc-tx4939.c1
-rw-r--r--drivers/rtc/rtc-vt8500.c6
-rw-r--r--drivers/rtc/rtc-x1205.c1
-rw-r--r--drivers/s390/block/dasd.c596
-rw-r--r--drivers/s390/block/dasd_eckd.c54
-rw-r--r--drivers/s390/block/dasd_eer.c2
-rw-r--r--drivers/s390/block/dasd_fba.c1
-rw-r--r--drivers/s390/block/dasd_int.h59
-rw-r--r--drivers/s390/block/dasd_ioctl.c48
-rw-r--r--drivers/s390/block/dasd_proc.c110
-rw-r--r--drivers/s390/block/dcssblk.c7
-rw-r--r--drivers/s390/block/xpram.c5
-rw-r--r--drivers/s390/char/Kconfig3
-rw-r--r--drivers/s390/char/con3215.c3
-rw-r--r--drivers/s390/char/fs3270.c1
-rw-r--r--drivers/s390/char/raw3270.c3
-rw-r--r--drivers/s390/char/sclp_async.c9
-rw-r--r--drivers/s390/char/sclp_cmd.c10
-rw-r--r--drivers/s390/char/sclp_cpi_sys.c1
-rw-r--r--drivers/s390/char/sclp_quiesce.c5
-rw-r--r--drivers/s390/char/tape_34xx.c1
-rw-r--r--drivers/s390/char/tape_3590.c1
-rw-r--r--drivers/s390/char/tape_core.c2
-rw-r--r--drivers/s390/char/vmcp.c1
-rw-r--r--drivers/s390/char/vmlogrdr.c2
-rw-r--r--drivers/s390/char/vmur.c4
-rw-r--r--drivers/s390/char/vmwatchdog.c4
-rw-r--r--drivers/s390/char/zcore.c23
-rw-r--r--drivers/s390/cio/blacklist.c3
-rw-r--r--drivers/s390/cio/ccwgroup.c333
-rw-r--r--drivers/s390/cio/ccwreq.c23
-rw-r--r--drivers/s390/cio/chp.c2
-rw-r--r--drivers/s390/cio/chsc_sch.c5
-rw-r--r--drivers/s390/cio/cio.c27
-rw-r--r--drivers/s390/cio/css.c8
-rw-r--r--drivers/s390/cio/css.h2
-rw-r--r--drivers/s390/cio/device.c13
-rw-r--r--drivers/s390/cio/device.h15
-rw-r--r--drivers/s390/cio/io_sch.h2
-rw-r--r--drivers/s390/cio/qdio.h52
-rw-r--r--drivers/s390/cio/qdio_debug.c27
-rw-r--r--drivers/s390/cio/qdio_main.c332
-rw-r--r--drivers/s390/cio/qdio_setup.c85
-rw-r--r--drivers/s390/cio/qdio_thinint.c153
-rw-r--r--drivers/s390/crypto/ap_bus.c121
-rw-r--r--drivers/s390/crypto/ap_bus.h22
-rw-r--r--drivers/s390/crypto/zcrypt_api.c2
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c2
-rw-r--r--drivers/s390/crypto/zcrypt_mono.c2
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.c2
-rw-r--r--drivers/s390/crypto/zcrypt_pcicc.c2
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c2
-rw-r--r--drivers/s390/kvm/kvm_virtio.c9
-rw-r--r--drivers/s390/net/Kconfig2
-rw-r--r--drivers/s390/net/claw.c3
-rw-r--r--drivers/s390/net/ctcm_main.c3
-rw-r--r--drivers/s390/net/ctcm_mpc.h1
-rw-r--r--drivers/s390/net/ctcm_sysfs.c2
-rw-r--r--drivers/s390/net/fsm.h2
-rw-r--r--drivers/s390/net/lcs.c13
-rw-r--r--drivers/s390/net/netiucv.c2
-rw-r--r--drivers/s390/net/qeth_core.h51
-rw-r--r--drivers/s390/net/qeth_core_main.c780
-rw-r--r--drivers/s390/net/qeth_l2_main.c4
-rw-r--r--drivers/s390/net/qeth_l3.h4
-rw-r--r--drivers/s390/net/qeth_l3_main.c218
-rw-r--r--drivers/s390/net/qeth_l3_sys.c114
-rw-r--r--drivers/s390/scsi/zfcp_aux.c1
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c1
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c37
-rw-r--r--drivers/s390/scsi/zfcp_def.h7
-rw-r--r--drivers/s390/scsi/zfcp_ext.h1
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c80
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c59
-rw-r--r--drivers/s390/scsi/zfcp_qdio.h66
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c18
-rw-r--r--drivers/sbus/char/display7seg.c2
-rw-r--r--drivers/scsi/3w-9xxx.c2
-rw-r--r--drivers/scsi/Kconfig29
-rw-r--r--drivers/scsi/Makefile3
-rw-r--r--drivers/scsi/a2091.c1
-rw-r--r--drivers/scsi/a3000.c1
-rw-r--r--drivers/scsi/aacraid/aachba.c1
-rw-r--r--drivers/scsi/aacraid/commsup.c2
-rw-r--r--drivers/scsi/aacraid/linit.c11
-rw-r--r--drivers/scsi/aacraid/src.c1
-rw-r--r--drivers/scsi/aha152x.c17
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic79xx3
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic7xxx3
-rw-r--r--drivers/scsi/aic94xx/aic94xx_dump.c2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_scb.c1
-rw-r--r--drivers/scsi/atari_NCR5380.c6
-rw-r--r--drivers/scsi/atari_scsi.c1
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.c20
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.h2
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c60
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.h2
-rw-r--r--drivers/scsi/be2iscsi/be_main.c312
-rw-r--r--drivers/scsi/be2iscsi/be_main.h9
-rw-r--r--drivers/scsi/bfa/Makefile2
-rw-r--r--drivers/scsi/bfa/bfa.h187
-rw-r--r--drivers/scsi/bfa/bfa_core.c912
-rw-r--r--drivers/scsi/bfa/bfa_defs.h754
-rw-r--r--drivers/scsi/bfa/bfa_defs_fcs.h27
-rw-r--r--drivers/scsi/bfa/bfa_defs_svc.h269
-rw-r--r--drivers/scsi/bfa/bfa_fc.h166
-rw-r--r--drivers/scsi/bfa/bfa_fcbuild.c49
-rw-r--r--drivers/scsi/bfa/bfa_fcbuild.h16
-rw-r--r--drivers/scsi/bfa/bfa_fcpim.c1214
-rw-r--r--drivers/scsi/bfa/bfa_fcpim.h134
-rw-r--r--drivers/scsi/bfa/bfa_fcs.c179
-rw-r--r--drivers/scsi/bfa/bfa_fcs.h32
-rw-r--r--drivers/scsi/bfa/bfa_fcs_fcpim.c47
-rw-r--r--drivers/scsi/bfa/bfa_fcs_lport.c403
-rw-r--r--drivers/scsi/bfa/bfa_fcs_rport.c170
-rw-r--r--drivers/scsi/bfa/bfa_hw_cb.c130
-rw-r--r--drivers/scsi/bfa/bfa_hw_ct.c114
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c3551
-rw-r--r--drivers/scsi/bfa/bfa_ioc.h655
-rw-r--r--drivers/scsi/bfa/bfa_ioc_cb.c69
-rw-r--r--drivers/scsi/bfa/bfa_ioc_ct.c516
-rw-r--r--drivers/scsi/bfa/bfa_modules.h30
-rw-r--r--drivers/scsi/bfa/bfa_port.c428
-rw-r--r--drivers/scsi/bfa/bfa_port.h62
-rw-r--r--drivers/scsi/bfa/bfa_svc.c1375
-rw-r--r--drivers/scsi/bfa/bfa_svc.h180
-rw-r--r--drivers/scsi/bfa/bfad.c303
-rw-r--r--drivers/scsi/bfa/bfad_attr.c53
-rw-r--r--drivers/scsi/bfa/bfad_bsg.c3235
-rw-r--r--drivers/scsi/bfa/bfad_bsg.h746
-rw-r--r--drivers/scsi/bfa/bfad_debugfs.c15
-rw-r--r--drivers/scsi/bfa/bfad_drv.h31
-rw-r--r--drivers/scsi/bfa/bfad_im.c60
-rw-r--r--drivers/scsi/bfa/bfad_im.h25
-rw-r--r--drivers/scsi/bfa/bfi.h657
-rw-r--r--drivers/scsi/bfa/bfi_cbreg.h305
-rw-r--r--drivers/scsi/bfa/bfi_ctreg.h636
-rw-r--r--drivers/scsi/bfa/bfi_ms.h159
-rw-r--r--drivers/scsi/bfa/bfi_reg.h450
-rw-r--r--drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h1162
-rw-r--r--drivers/scsi/bnx2fc/Kconfig3
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc.h129
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_constants.h139
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_debug.h16
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_els.c444
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c893
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_hwi.c849
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_io.c286
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_tgt.c92
-rw-r--r--drivers/scsi/bnx2i/57xx_iscsi_constants.h4
-rw-r--r--drivers/scsi/bnx2i/57xx_iscsi_hsi.h14
-rw-r--r--drivers/scsi/bnx2i/Kconfig3
-rw-r--r--drivers/scsi/bnx2i/bnx2i.h37
-rw-r--r--drivers/scsi/bnx2i/bnx2i_hwi.c217
-rw-r--r--drivers/scsi/bnx2i/bnx2i_init.c174
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c120
-rw-r--r--drivers/scsi/bnx2i/bnx2i_sysfs.c2
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/Kbuild2
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/Kconfig3
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/cxgb3i.c29
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/Kbuild2
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/Kconfig3
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/cxgb4i.c23
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.c83
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.h3
-rw-r--r--drivers/scsi/device_handler/scsi_dh.c72
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c109
-rw-r--r--drivers/scsi/device_handler/scsi_dh_emc.c1
-rw-r--r--drivers/scsi/device_handler/scsi_dh_hp_sw.c1
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c91
-rw-r--r--drivers/scsi/dpt/dpti_i2o.h2
-rw-r--r--drivers/scsi/fcoe/fcoe.c311
-rw-r--r--drivers/scsi/fcoe/fcoe.h11
-rw-r--r--drivers/scsi/fcoe/fcoe_transport.c108
-rw-r--r--drivers/scsi/fnic/fnic.h2
-rw-r--r--drivers/scsi/fnic/fnic_main.c21
-rw-r--r--drivers/scsi/fnic/fnic_scsi.c2
-rw-r--r--drivers/scsi/gvp11.c1
-rw-r--r--drivers/scsi/hosts.c9
-rw-r--r--drivers/scsi/hpsa.c302
-rw-r--r--drivers/scsi/hpsa.h12
-rw-r--r--drivers/scsi/hpsa_cmd.h5
-rw-r--r--drivers/scsi/ipr.c45
-rw-r--r--drivers/scsi/ipr.h5
-rw-r--r--drivers/scsi/isci/host.c105
-rw-r--r--drivers/scsi/isci/host.h18
-rw-r--r--drivers/scsi/isci/init.c52
-rw-r--r--drivers/scsi/isci/isci.h2
-rw-r--r--drivers/scsi/isci/phy.c26
-rw-r--r--drivers/scsi/isci/port.c148
-rw-r--r--drivers/scsi/isci/port.h6
-rw-r--r--drivers/scsi/isci/port_config.c2
-rw-r--r--drivers/scsi/isci/probe_roms.h4
-rw-r--r--drivers/scsi/isci/registers.h134
-rw-r--r--drivers/scsi/isci/remote_device.c109
-rw-r--r--drivers/scsi/isci/remote_device.h14
-rw-r--r--drivers/scsi/isci/request.c462
-rw-r--r--drivers/scsi/isci/request.h37
-rw-r--r--drivers/scsi/isci/sas.h2
-rw-r--r--drivers/scsi/isci/task.c721
-rw-r--r--drivers/scsi/isci/task.h54
-rw-r--r--drivers/scsi/isci/unsolicited_frame_control.c2
-rw-r--r--drivers/scsi/isci/unsolicited_frame_control.h2
-rw-r--r--drivers/scsi/iscsi_boot_sysfs.c31
-rw-r--r--drivers/scsi/iscsi_tcp.c145
-rw-r--r--drivers/scsi/libfc/fc_disc.c1
-rw-r--r--drivers/scsi/libfc/fc_elsct.c1
-rw-r--r--drivers/scsi/libfc/fc_exch.c111
-rw-r--r--drivers/scsi/libfc/fc_fcp.c22
-rw-r--r--drivers/scsi/libfc/fc_libfc.c1
-rw-r--r--drivers/scsi/libfc/fc_lport.c95
-rw-r--r--drivers/scsi/libfc/fc_npiv.c1
-rw-r--r--drivers/scsi/libfc/fc_rport.c29
-rw-r--r--drivers/scsi/libiscsi.c46
-rw-r--r--drivers/scsi/libiscsi_tcp.c15
-rw-r--r--drivers/scsi/libsas/sas_discover.c13
-rw-r--r--drivers/scsi/libsas/sas_expander.c52
-rw-r--r--drivers/scsi/libsas/sas_host_smp.c104
-rw-r--r--drivers/scsi/libsas/sas_init.c43
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c146
-rw-r--r--drivers/scsi/libsas/sas_task.c1
-rw-r--r--drivers/scsi/libsrp.c1
-rw-r--r--drivers/scsi/lpfc/lpfc.h35
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c227
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c89
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h16
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c3
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c1539
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.h125
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c138
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c229
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h15
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h330
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c372
-rw-r--r--drivers/scsi/lpfc/lpfc_logmsg.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c82
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c342
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c857
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h18
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h52
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c16
-rw-r--r--drivers/scsi/mac53c94.c1
-rw-r--r--drivers/scsi/mac_esp.c9
-rw-r--r--drivers/scsi/mac_scsi.c14
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.c3
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h22
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c255
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fp.c27
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.c193
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.h19
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2.h19
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h225
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_init.h6
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_ioc.h115
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c775
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.h145
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_config.c68
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.c50
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_debug.h2
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c917
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_transport.c26
-rw-r--r--drivers/scsi/mvsas/Kconfig9
-rw-r--r--drivers/scsi/mvsas/mv_64xx.c101
-rw-r--r--drivers/scsi/mvsas/mv_94xx.c541
-rw-r--r--drivers/scsi/mvsas/mv_94xx.h99
-rw-r--r--drivers/scsi/mvsas/mv_chips.h17
-rw-r--r--drivers/scsi/mvsas/mv_defs.h13
-rw-r--r--drivers/scsi/mvsas/mv_init.c210
-rw-r--r--drivers/scsi/mvsas/mv_sas.c493
-rw-r--r--drivers/scsi/mvsas/mv_sas.h110
-rw-r--r--drivers/scsi/mvumi.c2018
-rw-r--r--drivers/scsi/mvumi.h505
-rw-r--r--drivers/scsi/osd/Kconfig4
-rw-r--r--drivers/scsi/osd/osd_initiator.c1
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.c73
-rw-r--r--drivers/scsi/pm8001/pm8001_init.c4
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.c93
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.h4
-rw-r--r--drivers/scsi/pmcraid.c6
-rw-r--r--drivers/scsi/pmcraid.h1
-rw-r--r--drivers/scsi/ps3rom.c1
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c295
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.c593
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.h42
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c398
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.h187
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h11
-rw-r--r--drivers/scsi/qla2xxx/qla_dfs.c16
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h5
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h26
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c371
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c891
-rw-r--r--drivers/scsi/qla2xxx/qla_inline.h37
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c396
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c783
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c1218
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c162
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c1531
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.h255
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c830
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c275
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h2
-rw-r--r--drivers/scsi/qla4xxx/Kconfig3
-rw-r--r--drivers/scsi/qla4xxx/Makefile2
-rw-r--r--drivers/scsi/qla4xxx/ql4_attr.c76
-rw-r--r--drivers/scsi/qla4xxx/ql4_bsg.c513
-rw-r--r--drivers/scsi/qla4xxx/ql4_bsg.h19
-rw-r--r--drivers/scsi/qla4xxx/ql4_def.h214
-rw-r--r--drivers/scsi/qla4xxx/ql4_fw.h198
-rw-r--r--drivers/scsi/qla4xxx/ql4_glbl.h71
-rw-r--r--drivers/scsi/qla4xxx/ql4_init.c852
-rw-r--r--drivers/scsi/qla4xxx/ql4_iocb.c68
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c83
-rw-r--r--drivers/scsi/qla4xxx/ql4_mbx.c824
-rw-r--r--drivers/scsi/qla4xxx/ql4_nvram.c21
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.c17
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c2321
-rw-r--r--drivers/scsi/qla4xxx/ql4_version.h2
-rw-r--r--drivers/scsi/qlogicpti.c2
-rw-r--r--drivers/scsi/scsi_devinfo.c2
-rw-r--r--drivers/scsi/scsi_error.c12
-rw-r--r--drivers/scsi/scsi_lib.c16
-rw-r--r--drivers/scsi/scsi_lib_dma.c1
-rw-r--r--drivers/scsi/scsi_netlink.c1
-rw-r--r--drivers/scsi/scsi_pm.c9
-rw-r--r--drivers/scsi/scsi_scan.c5
-rw-r--r--drivers/scsi/scsi_sysfs.c38
-rw-r--r--drivers/scsi/scsi_tgt_if.c1
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c742
-rw-r--r--drivers/scsi/scsi_transport_sas.c10
-rw-r--r--drivers/scsi/scsi_transport_spi.c24
-rw-r--r--drivers/scsi/sd.c19
-rw-r--r--drivers/scsi/sd.h6
-rw-r--r--drivers/scsi/ses.c6
-rw-r--r--drivers/scsi/sg.c18
-rw-r--r--drivers/scsi/sr.c46
-rw-r--r--drivers/scsi/sr.h7
-rw-r--r--drivers/scsi/sr_ioctl.c1
-rw-r--r--drivers/scsi/st.c4
-rw-r--r--drivers/scsi/sun3_NCR5380.c98
-rw-r--r--drivers/scsi/sun3_scsi.c11
-rw-r--r--drivers/scsi/sun3_scsi_vme.c11
-rw-r--r--drivers/sfi/sfi_core.h3
-rw-r--r--drivers/sh/Makefile8
-rw-r--r--drivers/sh/clk/core.c123
-rw-r--r--drivers/sh/intc/chip.c12
-rw-r--r--drivers/sh/intc/core.c2
-rw-r--r--drivers/sh/intc/dynamic.c1
-rw-r--r--drivers/sh/intc/userimask.c1
-rw-r--r--drivers/sh/intc/virq.c1
-rw-r--r--drivers/sh/maple/maple.c1
-rw-r--r--drivers/sh/pfc.c29
-rw-r--r--drivers/sh/pm_runtime.c65
-rw-r--r--drivers/spi/Kconfig50
-rw-r--r--drivers/spi/Makefile111
-rw-r--r--drivers/spi/atmel_spi.h167
-rw-r--r--drivers/spi/spi-altera.c (renamed from drivers/spi/spi_altera.c)14
-rw-r--r--drivers/spi/spi-ath79.c (renamed from drivers/spi/ath79_spi.c)15
-rw-r--r--drivers/spi/spi-atmel.c (renamed from drivers/spi/atmel_spi.c)175
-rw-r--r--drivers/spi/spi-au1550.c (renamed from drivers/spi/au1550_spi.c)3
-rw-r--r--drivers/spi/spi-bfin-sport.c (renamed from drivers/spi/spi_bfin_sport.c)13
-rw-r--r--drivers/spi/spi-bfin5xx.c (renamed from drivers/spi/spi_bfin5xx.c)220
-rw-r--r--drivers/spi/spi-bitbang-txrx.h (renamed from drivers/spi/spi_bitbang_txrx.h)0
-rw-r--r--drivers/spi/spi-bitbang.c (renamed from drivers/spi/spi_bitbang.c)9
-rw-r--r--drivers/spi/spi-butterfly.c (renamed from drivers/spi/spi_butterfly.c)5
-rw-r--r--drivers/spi/spi-coldfire-qspi.c (renamed from drivers/spi/coldfire_qspi.c)16
-rw-r--r--drivers/spi/spi-davinci.c (renamed from drivers/spi/davinci_spi.c)20
-rw-r--r--drivers/spi/spi-dw-mid.c (renamed from drivers/spi/dw_spi_mid.c)13
-rw-r--r--drivers/spi/spi-dw-mmio.c (renamed from drivers/spi/dw_spi_mmio.c)19
-rw-r--r--drivers/spi/spi-dw-pci.c (renamed from drivers/spi/dw_spi_pci.c)5
-rw-r--r--drivers/spi/spi-dw.c (renamed from drivers/spi/dw_spi.c)71
-rw-r--r--drivers/spi/spi-dw.h (renamed from drivers/spi/dw_spi.h)98
-rw-r--r--drivers/spi/spi-ep93xx.c (renamed from drivers/spi/ep93xx_spi.c)324
-rw-r--r--drivers/spi/spi-fsl-espi.c (renamed from drivers/spi/spi_fsl_espi.c)15
-rw-r--r--drivers/spi/spi-fsl-lib.c (renamed from drivers/spi/spi_fsl_lib.c)2
-rw-r--r--drivers/spi/spi-fsl-lib.h (renamed from drivers/spi/spi_fsl_lib.h)0
-rw-r--r--drivers/spi/spi-fsl-spi.c (renamed from drivers/spi/spi_fsl_spi.c)33
-rw-r--r--drivers/spi/spi-gpio.c (renamed from drivers/spi/spi_gpio.c)28
-rw-r--r--drivers/spi/spi-imx.c (renamed from drivers/spi/spi_imx.c)482
-rw-r--r--drivers/spi/spi-lm70llp.c (renamed from drivers/spi/spi_lm70llp.c)4
-rw-r--r--drivers/spi/spi-mpc512x-psc.c (renamed from drivers/spi/mpc512x_psc_spi.c)13
-rw-r--r--drivers/spi/spi-mpc52xx-psc.c (renamed from drivers/spi/mpc52xx_psc_spi.c)13
-rw-r--r--drivers/spi/spi-mpc52xx.c (renamed from drivers/spi/mpc52xx_spi.c)14
-rw-r--r--drivers/spi/spi-nuc900.c (renamed from drivers/spi/spi_nuc900.c)19
-rw-r--r--drivers/spi/spi-oc-tiny.c (renamed from drivers/spi/spi_oc_tiny.c)14
-rw-r--r--drivers/spi/spi-omap-100k.c (renamed from drivers/spi/omap_spi_100k.c)0
-rw-r--r--drivers/spi/spi-omap-uwire.c (renamed from drivers/spi/omap_uwire.c)3
-rw-r--r--drivers/spi/spi-omap2-mcspi.c (renamed from drivers/spi/omap2_mcspi.c)17
-rw-r--r--drivers/spi/spi-orion.c (renamed from drivers/spi/orion_spi.c)9
-rw-r--r--drivers/spi/spi-pl022.c (renamed from drivers/spi/amba-pl022.c)362
-rw-r--r--drivers/spi/spi-ppc4xx.c (renamed from drivers/spi/spi_ppc4xx.c)17
-rw-r--r--drivers/spi/spi-pxa2xx-pci.c (renamed from drivers/spi/pxa2xx_spi_pci.c)1
-rw-r--r--drivers/spi/spi-pxa2xx.c (renamed from drivers/spi/pxa2xx_spi.c)0
-rw-r--r--drivers/spi/spi-s3c24xx-fiq.S (renamed from drivers/spi/spi_s3c24xx_fiq.S)2
-rw-r--r--drivers/spi/spi-s3c24xx-fiq.h (renamed from drivers/spi/spi_s3c24xx_fiq.h)0
-rw-r--r--drivers/spi/spi-s3c24xx.c (renamed from drivers/spi/spi_s3c24xx.c)27
-rw-r--r--drivers/spi/spi-s3c64xx.c (renamed from drivers/spi/spi_s3c64xx.c)178
-rw-r--r--drivers/spi/spi-sh-msiof.c (renamed from drivers/spi/spi_sh_msiof.c)16
-rw-r--r--drivers/spi/spi-sh-sci.c (renamed from drivers/spi/spi_sh_sci.c)16
-rw-r--r--drivers/spi/spi-sh.c (renamed from drivers/spi/spi_sh.c)15
-rw-r--r--drivers/spi/spi-stmp.c (renamed from drivers/spi/spi_stmp.c)13
-rw-r--r--drivers/spi/spi-tegra.c (renamed from drivers/spi/spi_tegra.c)37
-rw-r--r--drivers/spi/spi-ti-ssp.c (renamed from drivers/spi/ti-ssp-spi.c)14
-rw-r--r--drivers/spi/spi-tle62x0.c (renamed from drivers/spi/tle62x0.c)3
-rw-r--r--drivers/spi/spi-topcliff-pch.c (renamed from drivers/spi/spi_topcliff_pch.c)1213
-rw-r--r--drivers/spi/spi-txx9.c (renamed from drivers/spi/spi_txx9.c)3
-rw-r--r--drivers/spi/spi-xilinx.c (renamed from drivers/spi/xilinx_spi.c)13
-rw-r--r--drivers/spi/spi.c3
-rw-r--r--drivers/spi/spi_s3c24xx_gpio.c201
-rw-r--r--drivers/spi/spidev.c2
-rw-r--r--drivers/ssb/b43_pci_bridge.c3
-rw-r--r--drivers/ssb/driver_chipcommon.c3
-rw-r--r--drivers/ssb/driver_chipcommon_pmu.c7
-rw-r--r--drivers/ssb/driver_extif.c2
-rw-r--r--drivers/ssb/driver_gige.c16
-rw-r--r--drivers/ssb/driver_mipscore.c2
-rw-r--r--drivers/ssb/driver_pcicore.c31
-rw-r--r--drivers/ssb/embedded.c3
-rw-r--r--drivers/ssb/main.c59
-rw-r--r--drivers/ssb/pci.c11
-rw-r--r--drivers/ssb/pcihost_wrapper.c9
-rw-r--r--drivers/ssb/pcmcia.c2
-rw-r--r--drivers/ssb/scan.c5
-rw-r--r--drivers/ssb/sdio.c2
-rw-r--r--drivers/ssb/sprom.c2
-rw-r--r--drivers/staging/Kconfig38
-rw-r--r--drivers/staging/Makefile20
-rw-r--r--drivers/staging/altera-stapl/altera.h49
-rw-r--r--drivers/staging/ath6kl/Kconfig158
-rw-r--r--drivers/staging/ath6kl/Makefile122
-rw-r--r--drivers/staging/ath6kl/TODO25
-rw-r--r--drivers/staging/ath6kl/bmi/include/bmi_internal.h54
-rw-r--r--drivers/staging/ath6kl/bmi/src/bmi.c1010
-rw-r--r--drivers/staging/ath6kl/hif/common/hif_sdio_common.h87
-rw-r--r--drivers/staging/ath6kl/hif/sdio/linux_sdio/include/hif_internal.h131
-rw-r--r--drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif.c1273
-rw-r--r--drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c393
-rw-r--r--drivers/staging/ath6kl/htc2/AR6000/ar6k.c1479
-rw-r--r--drivers/staging/ath6kl/htc2/AR6000/ar6k.h401
-rw-r--r--drivers/staging/ath6kl/htc2/AR6000/ar6k_events.c783
-rw-r--r--drivers/staging/ath6kl/htc2/AR6000/ar6k_gmbox.c755
-rw-r--r--drivers/staging/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c1284
-rw-r--r--drivers/staging/ath6kl/htc2/htc.c575
-rw-r--r--drivers/staging/ath6kl/htc2/htc_debug.h38
-rw-r--r--drivers/staging/ath6kl/htc2/htc_internal.h211
-rw-r--r--drivers/staging/ath6kl/htc2/htc_recv.c1572
-rw-r--r--drivers/staging/ath6kl/htc2/htc_send.c1018
-rw-r--r--drivers/staging/ath6kl/htc2/htc_services.c450
-rw-r--r--drivers/staging/ath6kl/include/a_config.h31
-rw-r--r--drivers/staging/ath6kl/include/a_debug.h195
-rw-r--r--drivers/staging/ath6kl/include/a_drv.h32
-rw-r--r--drivers/staging/ath6kl/include/a_drv_api.h204
-rw-r--r--drivers/staging/ath6kl/include/a_osapi.h32
-rw-r--r--drivers/staging/ath6kl/include/aggr_recv_api.h140
-rw-r--r--drivers/staging/ath6kl/include/ar3kconfig.h65
-rw-r--r--drivers/staging/ath6kl/include/ar6000_api.h32
-rw-r--r--drivers/staging/ath6kl/include/ar6000_diag.h48
-rw-r--r--drivers/staging/ath6kl/include/ar6kap_common.h44
-rw-r--r--drivers/staging/ath6kl/include/athbtfilter.h135
-rw-r--r--drivers/staging/ath6kl/include/bmi.h134
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/AR6K_version.h52
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/addrs.h90
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/hw4.0/hw/apb_athr_wlan_map.h40
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/hw4.0/hw/apb_map.h40
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/hw4.0/hw/mbox_host_reg.h24
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/hw4.0/hw/mbox_reg.h552
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/hw4.0/hw/mbox_wlan_host_reg.h471
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/hw4.0/hw/mbox_wlan_reg.h589
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/hw4.0/hw/rtc_reg.h187
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/hw4.0/hw/rtc_wlan_reg.h162
-rw-r--r--drivers/staging/ath6kl/include/common/AR6002/hw4.0/hw/uart_reg.h40
-rw-r--r--drivers/staging/ath6kl/include/common/athdefs.h75
-rw-r--r--drivers/staging/ath6kl/include/common/bmi_msg.h233
-rw-r--r--drivers/staging/ath6kl/include/common/cnxmgmt.h36
-rw-r--r--drivers/staging/ath6kl/include/common/dbglog.h126
-rw-r--r--drivers/staging/ath6kl/include/common/dbglog_id.h558
-rw-r--r--drivers/staging/ath6kl/include/common/discovery.h75
-rw-r--r--drivers/staging/ath6kl/include/common/epping_test.h111
-rw-r--r--drivers/staging/ath6kl/include/common/gmboxif.h70
-rw-r--r--drivers/staging/ath6kl/include/common/gpio_reg.h9
-rw-r--r--drivers/staging/ath6kl/include/common/htc.h227
-rw-r--r--drivers/staging/ath6kl/include/common/htc_services.h52
-rw-r--r--drivers/staging/ath6kl/include/common/pkt_log.h45
-rw-r--r--drivers/staging/ath6kl/include/common/roaming.h41
-rw-r--r--drivers/staging/ath6kl/include/common/targaddrs.h395
-rw-r--r--drivers/staging/ath6kl/include/common/testcmd.h185
-rw-r--r--drivers/staging/ath6kl/include/common/tlpm.h38
-rw-r--r--drivers/staging/ath6kl/include/common/wlan_defs.h79
-rw-r--r--drivers/staging/ath6kl/include/common/wmi.h3220
-rw-r--r--drivers/staging/ath6kl/include/common/wmix.h271
-rw-r--r--drivers/staging/ath6kl/include/common_drv.h104
-rw-r--r--drivers/staging/ath6kl/include/dbglog_api.h52
-rw-r--r--drivers/staging/ath6kl/include/dl_list.h153
-rw-r--r--drivers/staging/ath6kl/include/dset_api.h65
-rw-r--r--drivers/staging/ath6kl/include/hci_transport_api.h259
-rw-r--r--drivers/staging/ath6kl/include/hif.h456
-rw-r--r--drivers/staging/ath6kl/include/host_version.h52
-rw-r--r--drivers/staging/ath6kl/include/htc_api.h575
-rw-r--r--drivers/staging/ath6kl/include/htc_packet.h227
-rw-r--r--drivers/staging/ath6kl/include/wlan_api.h128
-rw-r--r--drivers/staging/ath6kl/include/wmi_api.h441
-rw-r--r--drivers/staging/ath6kl/miscdrv/ar3kconfig.c565
-rw-r--r--drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsconfig.c572
-rw-r--r--drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsconfig.h75
-rw-r--r--drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsparser.c969
-rw-r--r--drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsparser.h113
-rw-r--r--drivers/staging/ath6kl/miscdrv/common_drv.c910
-rw-r--r--drivers/staging/ath6kl/miscdrv/credit_dist.c417
-rw-r--r--drivers/staging/ath6kl/miscdrv/miscdrv.h42
-rw-r--r--drivers/staging/ath6kl/os/linux/ar6000_drv.c6240
-rw-r--r--drivers/staging/ath6kl/os/linux/ar6000_pm.c626
-rw-r--r--drivers/staging/ath6kl/os/linux/ar6000_raw_if.c455
-rw-r--r--drivers/staging/ath6kl/os/linux/cfg80211.c1707
-rw-r--r--drivers/staging/ath6kl/os/linux/export_hci_transport.c124
-rw-r--r--drivers/staging/ath6kl/os/linux/hci_bridge.c1141
-rw-r--r--drivers/staging/ath6kl/os/linux/include/ar6000_drv.h774
-rw-r--r--drivers/staging/ath6kl/os/linux/include/ar6k_pal.h36
-rw-r--r--drivers/staging/ath6kl/os/linux/include/ar6xapi_linux.h190
-rw-r--r--drivers/staging/ath6kl/os/linux/include/athdrv_linux.h1217
-rw-r--r--drivers/staging/ath6kl/os/linux/include/cfg80211.h50
-rw-r--r--drivers/staging/ath6kl/os/linux/include/config_linux.h53
-rw-r--r--drivers/staging/ath6kl/os/linux/include/debug_linux.h50
-rw-r--r--drivers/staging/ath6kl/os/linux/include/export_hci_transport.h76
-rw-r--r--drivers/staging/ath6kl/os/linux/include/ieee80211_ioctl.h179
-rw-r--r--drivers/staging/ath6kl/os/linux/include/osapi_linux.h340
-rw-r--r--drivers/staging/ath6kl/os/linux/include/wlan_config.h108
-rw-r--r--drivers/staging/ath6kl/os/linux/include/wmi_filter_linux.h300
-rw-r--r--drivers/staging/ath6kl/os/linux/netbuf.c231
-rw-r--r--drivers/staging/ath6kl/reorder/aggr_rx_internal.h117
-rw-r--r--drivers/staging/ath6kl/reorder/rcv_aggr.c661
-rw-r--r--drivers/staging/ath6kl/wlan/include/ieee80211.h397
-rw-r--r--drivers/staging/ath6kl/wlan/include/ieee80211_node.h93
-rw-r--r--drivers/staging/ath6kl/wlan/src/wlan_node.c636
-rw-r--r--drivers/staging/ath6kl/wlan/src/wlan_recv_beacon.c199
-rw-r--r--drivers/staging/ath6kl/wlan/src/wlan_utils.c58
-rw-r--r--drivers/staging/ath6kl/wmi/wmi.c6444
-rw-r--r--drivers/staging/ath6kl/wmi/wmi_host.h102
-rw-r--r--drivers/staging/bcm/Bcmchar.c3171
-rw-r--r--drivers/staging/bcm/Debug.h2
-rw-r--r--drivers/staging/bcm/InterfaceDld.c456
-rw-r--r--drivers/staging/bcm/InterfaceInit.c39
-rw-r--r--drivers/staging/bcm/InterfaceMisc.c304
-rw-r--r--drivers/staging/bcm/InterfaceRx.c4
-rw-r--r--drivers/staging/bcm/Macros.h337
-rw-r--r--drivers/staging/bcm/Misc.c1843
-rw-r--r--drivers/staging/bcm/PHSModule.c24
-rw-r--r--drivers/staging/bcm/PHSModule.h2
-rw-r--r--drivers/staging/bcm/headers.h10
-rw-r--r--drivers/staging/bcm/nvm.c13
-rw-r--r--drivers/staging/bcm/sort.c77
-rw-r--r--drivers/staging/brcm80211/README64
-rw-r--r--drivers/staging/brcm80211/TODO15
-rw-r--r--drivers/staging/brcm80211/brcmfmac/README2
-rw-r--r--drivers/staging/brcm80211/brcmfmac/aiutils.c1
-rw-r--r--drivers/staging/brcm80211/brcmfmac/bcmcdc.h98
-rw-r--r--drivers/staging/brcm80211/brcmfmac/bcmsdbus.h113
-rw-r--r--drivers/staging/brcm80211/brcmfmac/bcmsdh.c631
-rw-r--r--drivers/staging/brcm80211/brcmfmac/bcmsdh_linux.c386
-rw-r--r--drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c1239
-rw-r--r--drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.h134
-rw-r--r--drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc_linux.c235
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd.h414
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd_bus.h82
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd_cdc.c474
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd_common.c1848
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd_custom_gpio.c158
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd_dbg.h103
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd_linux.c2862
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd_linux_sched.c25
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd_proto.h90
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd_sdio.c6390
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhdioctl.h100
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dngl_stats.h32
-rw-r--r--drivers/staging/brcm80211/brcmfmac/hndrte_armtrap.h75
-rw-r--r--drivers/staging/brcm80211/brcmfmac/hndrte_cons.h62
-rw-r--r--drivers/staging/brcm80211/brcmfmac/msgtrace.h61
-rw-r--r--drivers/staging/brcm80211/brcmfmac/sdioh.h63
-rw-r--r--drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c4428
-rw-r--r--drivers/staging/brcm80211/brcmfmac/wl_iw.c3693
-rw-r--r--drivers/staging/brcm80211/brcmfmac/wl_iw.h142
-rw-r--r--drivers/staging/brcm80211/brcmsmac/aiutils.c2054
-rw-r--r--drivers/staging/brcm80211/brcmsmac/bcmotp.c936
-rw-r--r--drivers/staging/brcm80211/brcmsmac/bcmsrom.c714
-rw-r--r--drivers/staging/brcm80211/brcmsmac/bcmsrom_tbl.h513
-rw-r--r--drivers/staging/brcm80211/brcmsmac/hnddma.c1756
-rw-r--r--drivers/staging/brcm80211/brcmsmac/nicpci.c836
-rw-r--r--drivers/staging/brcm80211/brcmsmac/nvram.c215
-rw-r--r--drivers/staging/brcm80211/brcmsmac/phy/phy_version.h36
-rw-r--r--drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_hal.h256
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wl_dbg.h92
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wl_export.h47
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wl_mac80211.h85
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wl_ucode.h49
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wl_ucode_loader.c111
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_alloc.c300
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_ampdu.h29
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_bmac.c3603
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_bmac.h178
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_bsscfg.h135
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_cfg.h280
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_channel.h120
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_key.h140
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_main.c7529
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_main.h939
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_phy_shim.c243
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_phy_shim.h112
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_pmu.c1929
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_pmu.h58
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_pub.h584
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_rate.h169
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_scb.h80
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_stf.c523
-rw-r--r--drivers/staging/brcm80211/brcmsmac/wlc_stf.h38
-rw-r--r--drivers/staging/brcm80211/include/aidmp.h374
-rw-r--r--drivers/staging/brcm80211/include/bcmdefs.h150
-rw-r--r--drivers/staging/brcm80211/include/bcmdevs.h124
-rw-r--r--drivers/staging/brcm80211/include/bcmnvram.h153
-rw-r--r--drivers/staging/brcm80211/include/bcmotp.h44
-rw-r--r--drivers/staging/brcm80211/include/bcmsdh.h205
-rw-r--r--drivers/staging/brcm80211/include/bcmsdpcm.h208
-rw-r--r--drivers/staging/brcm80211/include/bcmsrom_fmt.h367
-rw-r--r--drivers/staging/brcm80211/include/bcmutils.h500
-rw-r--r--drivers/staging/brcm80211/include/bcmwifi.h167
-rw-r--r--drivers/staging/brcm80211/include/hnddma.h226
-rw-r--r--drivers/staging/brcm80211/include/hndsoc.h199
-rw-r--r--drivers/staging/brcm80211/include/nicpci.h79
-rw-r--r--drivers/staging/brcm80211/include/pci_core.h122
-rw-r--r--drivers/staging/brcm80211/include/pcicfg.h50
-rw-r--r--drivers/staging/brcm80211/include/pcie_core.h299
-rw-r--r--drivers/staging/brcm80211/include/proto/802.11.h200
-rw-r--r--drivers/staging/brcm80211/include/proto/bcmeth.h44
-rw-r--r--drivers/staging/brcm80211/include/proto/bcmevent.h207
-rw-r--r--drivers/staging/brcm80211/include/sbchipc.h1588
-rw-r--r--drivers/staging/brcm80211/include/sbconfig.h272
-rw-r--r--drivers/staging/brcm80211/include/sbhnddma.h315
-rw-r--r--drivers/staging/brcm80211/include/sbsdio.h152
-rw-r--r--drivers/staging/brcm80211/include/sbsdpcmdev.h281
-rw-r--r--drivers/staging/brcm80211/include/sdio.h552
-rw-r--r--drivers/staging/brcm80211/include/wlioctl.h1365
-rw-r--r--drivers/staging/brcm80211/util/bcmutils.c796
-rw-r--r--drivers/staging/brcm80211/util/bcmwifi.c137
-rw-r--r--drivers/staging/comedi/Kconfig28
-rw-r--r--drivers/staging/comedi/comedi.h6
-rw-r--r--drivers/staging/comedi/comedi_fops.c20
-rw-r--r--drivers/staging/comedi/comedidev.h3
-rw-r--r--drivers/staging/comedi/drivers.c4
-rw-r--r--drivers/staging/comedi/drivers/Makefile2
-rw-r--r--drivers/staging/comedi/drivers/addi-data/addi_common.c2
-rw-r--r--drivers/staging/comedi/drivers/adl_pci9118.c1
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1723.c2
-rw-r--r--drivers/staging/comedi/drivers/adv_pci_dio.c2
-rw-r--r--drivers/staging/comedi/drivers/c6xdigio.c13
-rw-r--r--drivers/staging/comedi/drivers/cb_das16_cs.c5
-rw-r--r--drivers/staging/comedi/drivers/das1800.c28
-rw-r--r--drivers/staging/comedi/drivers/dt282x.c1
-rw-r--r--drivers/staging/comedi/drivers/dyna_pci10xx.c462
-rw-r--r--drivers/staging/comedi/drivers/ni_at_a2150.c1
-rw-r--r--drivers/staging/comedi/drivers/ni_atmio.c10
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_700.c10
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc.c57
-rw-r--r--drivers/staging/comedi/drivers/ni_pcidio.c2
-rw-r--r--drivers/staging/comedi/drivers/pcl812.c1
-rw-r--r--drivers/staging/comedi/drivers/pcl816.c1
-rw-r--r--drivers/staging/comedi/drivers/pcl818.c1
-rw-r--r--drivers/staging/comedi/drivers/pcmmio.c34
-rw-r--r--drivers/staging/comedi/drivers/rtd520.c22
-rw-r--r--drivers/staging/comedi/drivers/serial2002.c45
-rw-r--r--drivers/staging/comedi/drivers/unioxx5.c46
-rw-r--r--drivers/staging/comedi/drivers/usbdux.c7
-rw-r--r--drivers/staging/comedi/drivers/usbduxsigma.c2880
-rw-r--r--drivers/staging/crystalhd/bc_dts_defs.h4
-rw-r--r--drivers/staging/crystalhd/bc_dts_glob_lnx.h6
-rw-r--r--drivers/staging/crystalhd/bc_dts_types.h57
-rw-r--r--drivers/staging/crystalhd/crystalhd_cmds.h2
-rw-r--r--drivers/staging/crystalhd/crystalhd_fw_if.h532
-rw-r--r--drivers/staging/crystalhd/crystalhd_misc.h6
-rw-r--r--drivers/staging/cs5535_gpio/Kconfig11
-rw-r--r--drivers/staging/cs5535_gpio/Makefile1
-rw-r--r--drivers/staging/cs5535_gpio/TODO6
-rw-r--r--drivers/staging/cs5535_gpio/cs5535_gpio.c260
-rw-r--r--drivers/staging/cx25821/README6
-rw-r--r--drivers/staging/cx25821/cx25821-gpio.h2
-rw-r--r--drivers/staging/cxd2099/Kconfig11
-rw-r--r--drivers/staging/cxd2099/Makefile5
-rw-r--r--drivers/staging/cxt1e1/Kconfig3
-rw-r--r--drivers/staging/cxt1e1/linux.c7
-rw-r--r--drivers/staging/cxt1e1/sbecom_inline_linux.h65
-rw-r--r--drivers/staging/easycap/easycap_sound_oss.c954
-rw-r--r--drivers/staging/echo/echo.c11
-rw-r--r--drivers/staging/et131x/Kconfig12
-rw-r--r--drivers/staging/et131x/Makefile10
-rw-r--r--drivers/staging/et131x/README15
-rw-r--r--drivers/staging/et131x/et1310_address_map.h1483
-rw-r--r--drivers/staging/et131x/et1310_eeprom.c407
-rw-r--r--drivers/staging/et131x/et1310_mac.c621
-rw-r--r--drivers/staging/et131x/et1310_phy.c980
-rw-r--r--drivers/staging/et131x/et1310_phy.h511
-rw-r--r--drivers/staging/et131x/et1310_pm.c180
-rw-r--r--drivers/staging/et131x/et1310_rx.c1154
-rw-r--r--drivers/staging/et131x/et1310_rx.h243
-rw-r--r--drivers/staging/et131x/et1310_tx.c797
-rw-r--r--drivers/staging/et131x/et1310_tx.h150
-rw-r--r--drivers/staging/et131x/et131x.c5514
-rw-r--r--drivers/staging/et131x/et131x.h1721
-rw-r--r--drivers/staging/et131x/et131x_adapter.h248
-rw-r--r--drivers/staging/et131x/et131x_defs.h127
-rw-r--r--drivers/staging/et131x/et131x_initpci.c848
-rw-r--r--drivers/staging/et131x/et131x_isr.c480
-rw-r--r--drivers/staging/et131x/et131x_netdev.c704
-rw-r--r--drivers/staging/et131x/et131x_version.h74
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000.conf14
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000.h490
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c196
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_dev.h66
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c562
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c244
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c31
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_hw.c11
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_ioctl.h32
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_usb.h413
-rw-r--r--drivers/staging/ft1000/ft1000.h252
-rw-r--r--drivers/staging/generic_serial/Kconfig45
-rw-r--r--drivers/staging/generic_serial/Makefile6
-rw-r--r--drivers/staging/generic_serial/TODO6
-rw-r--r--drivers/staging/generic_serial/generic_serial.c844
-rw-r--r--drivers/staging/generic_serial/rio/Makefile12
-rw-r--r--drivers/staging/generic_serial/rio/board.h132
-rw-r--r--drivers/staging/generic_serial/rio/cirrus.h210
-rw-r--r--drivers/staging/generic_serial/rio/cmdblk.h53
-rw-r--r--drivers/staging/generic_serial/rio/cmdpkt.h177
-rw-r--r--drivers/staging/generic_serial/rio/daemon.h307
-rw-r--r--drivers/staging/generic_serial/rio/errors.h98
-rw-r--r--drivers/staging/generic_serial/rio/func.h143
-rw-r--r--drivers/staging/generic_serial/rio/host.h123
-rw-r--r--drivers/staging/generic_serial/rio/link.h96
-rw-r--r--drivers/staging/generic_serial/rio/linux_compat.h77
-rw-r--r--drivers/staging/generic_serial/rio/map.h98
-rw-r--r--drivers/staging/generic_serial/rio/param.h55
-rw-r--r--drivers/staging/generic_serial/rio/parmmap.h81
-rw-r--r--drivers/staging/generic_serial/rio/pci.h72
-rw-r--r--drivers/staging/generic_serial/rio/phb.h142
-rw-r--r--drivers/staging/generic_serial/rio/pkt.h77
-rw-r--r--drivers/staging/generic_serial/rio/port.h179
-rw-r--r--drivers/staging/generic_serial/rio/protsts.h110
-rw-r--r--drivers/staging/generic_serial/rio/rio.h208
-rw-r--r--drivers/staging/generic_serial/rio/rio_linux.c1204
-rw-r--r--drivers/staging/generic_serial/rio/rio_linux.h197
-rw-r--r--drivers/staging/generic_serial/rio/rioboard.h275
-rw-r--r--drivers/staging/generic_serial/rio/rioboot.c1113
-rw-r--r--drivers/staging/generic_serial/rio/riocmd.c939
-rw-r--r--drivers/staging/generic_serial/rio/rioctrl.c1504
-rw-r--r--drivers/staging/generic_serial/rio/riodrvr.h138
-rw-r--r--drivers/staging/generic_serial/rio/rioinfo.h92
-rw-r--r--drivers/staging/generic_serial/rio/rioinit.c421
-rw-r--r--drivers/staging/generic_serial/rio/riointr.c645
-rw-r--r--drivers/staging/generic_serial/rio/rioioctl.h57
-rw-r--r--drivers/staging/generic_serial/rio/rioparam.c663
-rw-r--r--drivers/staging/generic_serial/rio/rioroute.c1039
-rw-r--r--drivers/staging/generic_serial/rio/riospace.h154
-rw-r--r--drivers/staging/generic_serial/rio/riotable.c941
-rw-r--r--drivers/staging/generic_serial/rio/riotty.c654
-rw-r--r--drivers/staging/generic_serial/rio/route.h101
-rw-r--r--drivers/staging/generic_serial/rio/rup.h69
-rw-r--r--drivers/staging/generic_serial/rio/unixrup.h51
-rw-r--r--drivers/staging/generic_serial/ser_a2232.c831
-rw-r--r--drivers/staging/generic_serial/ser_a2232.h202
-rw-r--r--drivers/staging/generic_serial/ser_a2232fw.ax529
-rw-r--r--drivers/staging/generic_serial/ser_a2232fw.h306
-rw-r--r--drivers/staging/generic_serial/sx.c2894
-rw-r--r--drivers/staging/generic_serial/sx.h201
-rw-r--r--drivers/staging/generic_serial/sxboards.h206
-rw-r--r--drivers/staging/generic_serial/sxwindow.h393
-rw-r--r--drivers/staging/generic_serial/vme_scc.c1145
-rw-r--r--drivers/staging/gma500/Kconfig27
-rw-r--r--drivers/staging/gma500/Makefile48
-rw-r--r--drivers/staging/gma500/TODO29
-rw-r--r--drivers/staging/gma500/accel_2d.c (renamed from drivers/staging/gma500/psb_2d.c)300
-rw-r--r--drivers/staging/gma500/backlight.c49
-rw-r--r--drivers/staging/gma500/cdv_device.c350
-rw-r--r--drivers/staging/gma500/cdv_device.h36
-rw-r--r--drivers/staging/gma500/cdv_intel_crt.c326
-rw-r--r--drivers/staging/gma500/cdv_intel_display.c1508
-rw-r--r--drivers/staging/gma500/cdv_intel_hdmi.c376
-rw-r--r--drivers/staging/gma500/cdv_intel_lvds.c721
-rw-r--r--drivers/staging/gma500/displays/hdmi.h33
-rw-r--r--drivers/staging/gma500/displays/pyr_cmd.h34
-rw-r--r--drivers/staging/gma500/displays/pyr_vid.h34
-rw-r--r--drivers/staging/gma500/displays/tmd_cmd.h34
-rw-r--r--drivers/staging/gma500/displays/tmd_vid.h34
-rw-r--r--drivers/staging/gma500/displays/tpo_cmd.h35
-rw-r--r--drivers/staging/gma500/displays/tpo_vid.h33
-rw-r--r--drivers/staging/gma500/framebuffer.c (renamed from drivers/staging/gma500/psb_fb.c)596
-rw-r--r--drivers/staging/gma500/framebuffer.h (renamed from drivers/staging/gma500/psb_fb.h)11
-rw-r--r--drivers/staging/gma500/gem.c (renamed from drivers/staging/gma500/psb_gem.c)170
-rw-r--r--drivers/staging/gma500/gem_glue.c89
-rw-r--r--drivers/staging/gma500/gem_glue.h2
-rw-r--r--drivers/staging/gma500/gtt.c (renamed from drivers/staging/gma500/psb_gtt.c)316
-rw-r--r--drivers/staging/gma500/gtt.h (renamed from drivers/staging/gma500/psb_gtt.h)17
-rw-r--r--drivers/staging/gma500/intel_bios.c (renamed from drivers/staging/gma500/psb_intel_bios.c)25
-rw-r--r--drivers/staging/gma500/intel_bios.h (renamed from drivers/staging/gma500/psb_intel_bios.h)0
-rw-r--r--drivers/staging/gma500/intel_i2c.c (renamed from drivers/staging/gma500/psb_intel_i2c.c)1
-rw-r--r--drivers/staging/gma500/intel_opregion.c (renamed from drivers/staging/gma500/psb_intel_opregion.c)29
-rw-r--r--drivers/staging/gma500/mdfld_device.c714
-rw-r--r--drivers/staging/gma500/mdfld_dsi_dbi.c761
-rw-r--r--drivers/staging/gma500/mdfld_dsi_dbi.h173
-rw-r--r--drivers/staging/gma500/mdfld_dsi_dbi_dpu.c778
-rw-r--r--drivers/staging/gma500/mdfld_dsi_dbi_dpu.h154
-rw-r--r--drivers/staging/gma500/mdfld_dsi_dpi.c805
-rw-r--r--drivers/staging/gma500/mdfld_dsi_dpi.h78
-rw-r--r--drivers/staging/gma500/mdfld_dsi_output.c1014
-rw-r--r--drivers/staging/gma500/mdfld_dsi_output.h138
-rw-r--r--drivers/staging/gma500/mdfld_dsi_pkg_sender.c1484
-rw-r--r--drivers/staging/gma500/mdfld_dsi_pkg_sender.h184
-rw-r--r--drivers/staging/gma500/mdfld_intel_display.c1404
-rw-r--r--drivers/staging/gma500/mdfld_msic.h31
-rw-r--r--drivers/staging/gma500/mdfld_output.c171
-rw-r--r--drivers/staging/gma500/mdfld_output.h41
-rw-r--r--drivers/staging/gma500/mdfld_pyr_cmd.c558
-rw-r--r--drivers/staging/gma500/mdfld_tmd_vid.c206
-rw-r--r--drivers/staging/gma500/mdfld_tpo_cmd.c509
-rw-r--r--drivers/staging/gma500/mdfld_tpo_vid.c140
-rw-r--r--drivers/staging/gma500/medfield.h268
-rw-r--r--drivers/staging/gma500/mid_bios.c270
-rw-r--r--drivers/staging/gma500/mid_bios.h21
-rw-r--r--drivers/staging/gma500/mmu.c (renamed from drivers/staging/gma500/psb_mmu.c)0
-rw-r--r--drivers/staging/gma500/mrst.h73
-rw-r--r--drivers/staging/gma500/mrst_crtc.c47
-rw-r--r--drivers/staging/gma500/mrst_device.c634
-rw-r--r--drivers/staging/gma500/mrst_hdmi.c852
-rw-r--r--drivers/staging/gma500/mrst_hdmi_i2c.c328
-rw-r--r--drivers/staging/gma500/mrst_lvds.c92
-rw-r--r--drivers/staging/gma500/power.c (renamed from drivers/staging/gma500/psb_powermgmt.c)245
-rw-r--r--drivers/staging/gma500/power.h (renamed from drivers/staging/gma500/psb_powermgmt.h)6
-rw-r--r--drivers/staging/gma500/psb_bl.c227
-rw-r--r--drivers/staging/gma500/psb_device.c321
-rw-r--r--drivers/staging/gma500/psb_drm.h152
-rw-r--r--drivers/staging/gma500/psb_drv.c498
-rw-r--r--drivers/staging/gma500/psb_drv.h497
-rw-r--r--drivers/staging/gma500/psb_intel_display.c177
-rw-r--r--drivers/staging/gma500/psb_intel_display.h3
-rw-r--r--drivers/staging/gma500/psb_intel_drv.h24
-rw-r--r--drivers/staging/gma500/psb_intel_lvds.c203
-rw-r--r--drivers/staging/gma500/psb_intel_reg.h1144
-rw-r--r--drivers/staging/gma500/psb_intel_sdvo.c35
-rw-r--r--drivers/staging/gma500/psb_intel_sdvo_regs.h14
-rw-r--r--drivers/staging/gma500/psb_irq.c168
-rw-r--r--drivers/staging/gma500/psb_irq.h8
-rw-r--r--drivers/staging/gma500/psb_lid.c8
-rw-r--r--drivers/staging/gma500/psb_reg.h842
-rw-r--r--drivers/staging/hv/Kconfig35
-rw-r--r--drivers/staging/hv/Makefile10
-rw-r--r--drivers/staging/hv/TODO11
-rw-r--r--drivers/staging/hv/blkvsc_drv.c1022
-rw-r--r--drivers/staging/hv/hv_mouse.c717
-rw-r--r--drivers/staging/hv/hv_timesource.c102
-rw-r--r--drivers/staging/hv/hyperv.h944
-rw-r--r--drivers/staging/hv/hyperv_net.h21
-rw-r--r--drivers/staging/hv/hyperv_storage.h334
-rw-r--r--drivers/staging/hv/netvsc.c502
-rw-r--r--drivers/staging/hv/netvsc_drv.c212
-rw-r--r--drivers/staging/hv/rndis_filter.c203
-rw-r--r--drivers/staging/hv/storvsc.c564
-rw-r--r--drivers/staging/hv/storvsc_drv.c936
-rw-r--r--drivers/staging/hv/tools/hv_kvp_daemon.c494
-rw-r--r--drivers/staging/iio/Documentation/generic_buffer.c12
-rw-r--r--drivers/staging/iio/Documentation/iio_utils.h2
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio738
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a21
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad593330
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio-light8
-rw-r--r--drivers/staging/iio/Kconfig35
-rw-r--r--drivers/staging/iio/Makefile11
-rw-r--r--drivers/staging/iio/accel/Kconfig33
-rw-r--r--drivers/staging/iio/accel/Makefile12
-rw-r--r--drivers/staging/iio/accel/accel.h87
-rw-r--r--drivers/staging/iio/accel/adis16201.h18
-rw-r--r--drivers/staging/iio/accel/adis16201_core.c165
-rw-r--r--drivers/staging/iio/accel/adis16201_ring.c57
-rw-r--r--drivers/staging/iio/accel/adis16201_trigger.c25
-rw-r--r--drivers/staging/iio/accel/adis16203.h18
-rw-r--r--drivers/staging/iio/accel/adis16203_core.c125
-rw-r--r--drivers/staging/iio/accel/adis16203_ring.c48
-rw-r--r--drivers/staging/iio/accel/adis16203_trigger.c32
-rw-r--r--drivers/staging/iio/accel/adis16204.h18
-rw-r--r--drivers/staging/iio/accel/adis16204_core.c174
-rw-r--r--drivers/staging/iio/accel/adis16204_ring.c48
-rw-r--r--drivers/staging/iio/accel/adis16204_trigger.c26
-rw-r--r--drivers/staging/iio/accel/adis16209.h18
-rw-r--r--drivers/staging/iio/accel/adis16209_core.c125
-rw-r--r--drivers/staging/iio/accel/adis16209_ring.c51
-rw-r--r--drivers/staging/iio/accel/adis16209_trigger.c32
-rw-r--r--drivers/staging/iio/accel/adis16220.h14
-rw-r--r--drivers/staging/iio/accel/adis16220_core.c453
-rw-r--r--drivers/staging/iio/accel/adis16240.h18
-rw-r--r--drivers/staging/iio/accel/adis16240_core.c123
-rw-r--r--drivers/staging/iio/accel/adis16240_ring.c49
-rw-r--r--drivers/staging/iio/accel/adis16240_trigger.c32
-rw-r--r--drivers/staging/iio/accel/inclinometer.h25
-rw-r--r--drivers/staging/iio/accel/kxsd9.c352
-rw-r--r--drivers/staging/iio/accel/lis3l02dq.h26
-rw-r--r--drivers/staging/iio/accel/lis3l02dq_core.c116
-rw-r--r--drivers/staging/iio/accel/lis3l02dq_ring.c125
-rw-r--r--drivers/staging/iio/accel/sca3000.h5
-rw-r--r--drivers/staging/iio/accel/sca3000_core.c180
-rw-r--r--drivers/staging/iio/accel/sca3000_ring.c162
-rw-r--r--drivers/staging/iio/adc/Kconfig99
-rw-r--r--drivers/staging/iio/adc/Makefile16
-rw-r--r--drivers/staging/iio/adc/ad7150.c821
-rw-r--r--drivers/staging/iio/adc/ad7152.c595
-rw-r--r--drivers/staging/iio/adc/ad7192.c1179
-rw-r--r--drivers/staging/iio/adc/ad7192.h47
-rw-r--r--drivers/staging/iio/adc/ad7280a.c997
-rw-r--r--drivers/staging/iio/adc/ad7280a.h38
-rw-r--r--drivers/staging/iio/adc/ad7291.c1134
-rw-r--r--drivers/staging/iio/adc/ad7298.h6
-rw-r--r--drivers/staging/iio/adc/ad7298_core.c62
-rw-r--r--drivers/staging/iio/adc/ad7298_ring.c50
-rw-r--r--drivers/staging/iio/adc/ad7314.c291
-rw-r--r--drivers/staging/iio/adc/ad7476.h11
-rw-r--r--drivers/staging/iio/adc/ad7476_core.c99
-rw-r--r--drivers/staging/iio/adc/ad7476_ring.c58
-rw-r--r--drivers/staging/iio/adc/ad7606.h12
-rw-r--r--drivers/staging/iio/adc/ad7606_core.c277
-rw-r--r--drivers/staging/iio/adc/ad7606_par.c2
-rw-r--r--drivers/staging/iio/adc/ad7606_ring.c86
-rw-r--r--drivers/staging/iio/adc/ad7606_spi.c2
-rw-r--r--drivers/staging/iio/adc/ad7745.c683
-rw-r--r--drivers/staging/iio/adc/ad7780.c10
-rw-r--r--drivers/staging/iio/adc/ad7793.c1063
-rw-r--r--drivers/staging/iio/adc/ad7793.h107
-rw-r--r--drivers/staging/iio/adc/ad7816.c112
-rw-r--r--drivers/staging/iio/adc/ad7887.h10
-rw-r--r--drivers/staging/iio/adc/ad7887_core.c71
-rw-r--r--drivers/staging/iio/adc/ad7887_ring.c65
-rw-r--r--drivers/staging/iio/adc/ad799x.h4
-rw-r--r--drivers/staging/iio/adc/ad799x_core.c747
-rw-r--r--drivers/staging/iio/adc/ad799x_ring.c73
-rw-r--r--drivers/staging/iio/adc/adc.h42
-rw-r--r--drivers/staging/iio/adc/adt7310.c114
-rw-r--r--drivers/staging/iio/adc/adt7410.c106
-rw-r--r--drivers/staging/iio/adc/adt75.c668
-rw-r--r--drivers/staging/iio/adc/max1363.h8
-rw-r--r--drivers/staging/iio/adc/max1363_core.c332
-rw-r--r--drivers/staging/iio/adc/max1363_ring.c63
-rw-r--r--drivers/staging/iio/addac/Kconfig5
-rw-r--r--drivers/staging/iio/addac/adt7316-i2c.c3
-rw-r--r--drivers/staging/iio/addac/adt7316.c248
-rw-r--r--drivers/staging/iio/buffer_generic.h228
-rw-r--r--drivers/staging/iio/cdc/Kconfig36
-rw-r--r--drivers/staging/iio/cdc/Makefile7
-rw-r--r--drivers/staging/iio/cdc/ad7150.c676
-rw-r--r--drivers/staging/iio/cdc/ad7152.c559
-rw-r--r--drivers/staging/iio/cdc/ad7746.c807
-rw-r--r--drivers/staging/iio/cdc/ad7746.h29
-rw-r--r--drivers/staging/iio/chrdev.h54
-rw-r--r--drivers/staging/iio/dac/Kconfig38
-rw-r--r--drivers/staging/iio/dac/Makefile3
-rw-r--r--drivers/staging/iio/dac/ad5064.c463
-rw-r--r--drivers/staging/iio/dac/ad5360.c581
-rw-r--r--drivers/staging/iio/dac/ad5446.c126
-rw-r--r--drivers/staging/iio/dac/ad5446.h2
-rw-r--r--drivers/staging/iio/dac/ad5504.c141
-rw-r--r--drivers/staging/iio/dac/ad5504.h2
-rw-r--r--drivers/staging/iio/dac/ad5624r.h1
-rw-r--r--drivers/staging/iio/dac/ad5624r_spi.c91
-rw-r--r--drivers/staging/iio/dac/ad5686.c465
-rw-r--r--drivers/staging/iio/dac/ad5791.c246
-rw-r--r--drivers/staging/iio/dac/ad5791.h8
-rw-r--r--drivers/staging/iio/dac/dac.h2
-rw-r--r--drivers/staging/iio/dac/max517.c70
-rw-r--r--drivers/staging/iio/dds/Kconfig4
-rw-r--r--drivers/staging/iio/dds/ad5930.c39
-rw-r--r--drivers/staging/iio/dds/ad9832.c71
-rw-r--r--drivers/staging/iio/dds/ad9832.h2
-rw-r--r--drivers/staging/iio/dds/ad9834.c92
-rw-r--r--drivers/staging/iio/dds/ad9834.h2
-rw-r--r--drivers/staging/iio/dds/ad9850.c39
-rw-r--r--drivers/staging/iio/dds/ad9852.c41
-rw-r--r--drivers/staging/iio/dds/ad9910.c39
-rw-r--r--drivers/staging/iio/dds/ad9951.c39
-rw-r--r--drivers/staging/iio/gyro/Kconfig8
-rw-r--r--drivers/staging/iio/gyro/Makefile2
-rw-r--r--drivers/staging/iio/gyro/adis16060_core.c181
-rw-r--r--drivers/staging/iio/gyro/adis16080_core.c148
-rw-r--r--drivers/staging/iio/gyro/adis16130_core.c192
-rw-r--r--drivers/staging/iio/gyro/adis16260.h24
-rw-r--r--drivers/staging/iio/gyro/adis16260_core.c152
-rw-r--r--drivers/staging/iio/gyro/adis16260_ring.c49
-rw-r--r--drivers/staging/iio/gyro/adis16260_trigger.c30
-rw-r--r--drivers/staging/iio/gyro/adxrs450.h13
-rw-r--r--drivers/staging/iio/gyro/adxrs450_core.c330
-rw-r--r--drivers/staging/iio/gyro/gyro.h85
-rw-r--r--drivers/staging/iio/iio.h262
-rw-r--r--drivers/staging/iio/iio_core.h63
-rw-r--r--drivers/staging/iio/iio_core_trigger.h47
-rw-r--r--drivers/staging/iio/iio_dummy_evgen.c217
-rw-r--r--drivers/staging/iio/iio_dummy_evgen.h2
-rw-r--r--drivers/staging/iio/iio_simple_dummy.c545
-rw-r--r--drivers/staging/iio/iio_simple_dummy.h108
-rw-r--r--drivers/staging/iio/iio_simple_dummy_buffer.c207
-rw-r--r--drivers/staging/iio/iio_simple_dummy_events.c190
-rw-r--r--drivers/staging/iio/impedance-analyzer/Kconfig18
-rw-r--r--drivers/staging/iio/impedance-analyzer/Makefile5
-rw-r--r--drivers/staging/iio/impedance-analyzer/ad5933.c814
-rw-r--r--drivers/staging/iio/impedance-analyzer/ad5933.h28
-rw-r--r--drivers/staging/iio/imu/Kconfig16
-rw-r--r--drivers/staging/iio/imu/Makefile2
-rw-r--r--drivers/staging/iio/imu/adis16400.h9
-rw-r--r--drivers/staging/iio/imu/adis16400_core.c657
-rw-r--r--drivers/staging/iio/imu/adis16400_ring.c50
-rw-r--r--drivers/staging/iio/imu/adis16400_trigger.c17
-rw-r--r--drivers/staging/iio/industrialio-buffer.c636
-rw-r--r--drivers/staging/iio/industrialio-core.c1007
-rw-r--r--drivers/staging/iio/industrialio-ring.c596
-rw-r--r--drivers/staging/iio/industrialio-trigger.c184
-rw-r--r--drivers/staging/iio/kfifo_buf.c80
-rw-r--r--drivers/staging/iio/kfifo_buf.h16
-rw-r--r--drivers/staging/iio/light/Kconfig6
-rw-r--r--drivers/staging/iio/light/isl29018.c271
-rw-r--r--drivers/staging/iio/light/tsl2563.c176
-rw-r--r--drivers/staging/iio/light/tsl2583.c222
-rw-r--r--drivers/staging/iio/magnetometer/Kconfig4
-rw-r--r--drivers/staging/iio/magnetometer/ak8975.c311
-rw-r--r--drivers/staging/iio/magnetometer/hmc5843.c152
-rw-r--r--drivers/staging/iio/magnetometer/magnet.h31
-rw-r--r--drivers/staging/iio/meter/Kconfig8
-rw-r--r--drivers/staging/iio/meter/Makefile2
-rw-r--r--drivers/staging/iio/meter/ade7753.c107
-rw-r--r--drivers/staging/iio/meter/ade7753.h10
-rw-r--r--drivers/staging/iio/meter/ade7754.c103
-rw-r--r--drivers/staging/iio/meter/ade7754.h12
-rw-r--r--drivers/staging/iio/meter/ade7758.h13
-rw-r--r--drivers/staging/iio/meter/ade7758_core.c57
-rw-r--r--drivers/staging/iio/meter/ade7758_ring.c58
-rw-r--r--drivers/staging/iio/meter/ade7758_trigger.c17
-rw-r--r--drivers/staging/iio/meter/ade7759.c101
-rw-r--r--drivers/staging/iio/meter/ade7759.h12
-rw-r--r--drivers/staging/iio/meter/ade7854-i2c.c40
-rw-r--r--drivers/staging/iio/meter/ade7854-spi.c41
-rw-r--r--drivers/staging/iio/meter/ade7854.c77
-rw-r--r--drivers/staging/iio/meter/ade7854.h36
-rw-r--r--drivers/staging/iio/resolver/Kconfig36
-rw-r--r--drivers/staging/iio/resolver/Makefile2
-rw-r--r--drivers/staging/iio/resolver/ad2s1200.c188
-rw-r--r--drivers/staging/iio/resolver/ad2s120x.c311
-rw-r--r--drivers/staging/iio/resolver/ad2s1210.c923
-rw-r--r--drivers/staging/iio/resolver/ad2s1210.h17
-rw-r--r--drivers/staging/iio/resolver/ad2s90.c110
-rw-r--r--drivers/staging/iio/ring_generic.h288
-rw-r--r--drivers/staging/iio/ring_hw.h6
-rw-r--r--drivers/staging/iio/ring_sw.c69
-rw-r--r--drivers/staging/iio/ring_sw.h8
-rw-r--r--drivers/staging/iio/sysfs.h136
-rw-r--r--drivers/staging/iio/trigger.h117
-rw-r--r--drivers/staging/iio/trigger/iio-trig-bfin-timer.c6
-rw-r--r--drivers/staging/iio/trigger/iio-trig-gpio.c8
-rw-r--r--drivers/staging/iio/trigger/iio-trig-periodic-rtc.c7
-rw-r--r--drivers/staging/iio/trigger/iio-trig-sysfs.c13
-rw-r--r--drivers/staging/iio/trigger_consumer.h71
-rw-r--r--drivers/staging/intel_sst/intel_sst.c16
-rw-r--r--drivers/staging/intel_sst/intel_sst_app_interface.c4
-rw-r--r--drivers/staging/intel_sst/intel_sst_common.h2
-rw-r--r--drivers/staging/intel_sst/intel_sst_drv_interface.c11
-rw-r--r--drivers/staging/intel_sst/intel_sst_dsp.c2
-rw-r--r--drivers/staging/intel_sst/intelmid.c1
-rw-r--r--drivers/staging/intel_sst/intelmid_v2_control.c2
-rw-r--r--drivers/staging/keucr/Kconfig6
-rw-r--r--drivers/staging/keucr/Makefile2
-rw-r--r--drivers/staging/keucr/init.c71
-rw-r--r--drivers/staging/keucr/init.h773
-rw-r--r--drivers/staging/keucr/ms.c1034
-rw-r--r--drivers/staging/keucr/ms.h401
-rw-r--r--drivers/staging/keucr/msscsi.c344
-rw-r--r--drivers/staging/keucr/scsiglue.c5
-rw-r--r--drivers/staging/keucr/smil.h6
-rw-r--r--drivers/staging/keucr/smilsub.c1574
-rw-r--r--drivers/staging/keucr/smscsi.c117
-rw-r--r--drivers/staging/keucr/transport.c4
-rw-r--r--drivers/staging/keucr/transport.h46
-rw-r--r--drivers/staging/keucr/usb.c5
-rw-r--r--drivers/staging/keucr/usb.h3
-rw-r--r--drivers/staging/line6/audio.c1
-rw-r--r--drivers/staging/media/Kconfig37
-rw-r--r--drivers/staging/media/Makefile7
-rw-r--r--drivers/staging/media/as102/Kconfig7
-rw-r--r--drivers/staging/media/as102/Makefile6
-rw-r--r--drivers/staging/media/as102/as102_drv.c351
-rw-r--r--drivers/staging/media/as102/as102_drv.h142
-rw-r--r--drivers/staging/media/as102/as102_fe.c603
-rw-r--r--drivers/staging/media/as102/as102_fw.c251
-rw-r--r--drivers/staging/media/as102/as102_fw.h42
-rw-r--r--drivers/staging/media/as102/as102_usb_drv.c478
-rw-r--r--drivers/staging/media/as102/as102_usb_drv.h59
-rw-r--r--drivers/staging/media/as102/as10x_cmd.c452
-rw-r--r--drivers/staging/media/as102/as10x_cmd.h540
-rw-r--r--drivers/staging/media/as102/as10x_cmd_cfg.c215
-rw-r--r--drivers/staging/media/as102/as10x_cmd_stream.c223
-rw-r--r--drivers/staging/media/as102/as10x_handle.h58
-rw-r--r--drivers/staging/media/as102/as10x_types.h198
-rw-r--r--drivers/staging/media/cxd2099/Kconfig12
-rw-r--r--drivers/staging/media/cxd2099/Makefile5
-rw-r--r--drivers/staging/media/cxd2099/TODO (renamed from drivers/staging/cxd2099/TODO)0
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.c (renamed from drivers/staging/cxd2099/cxd2099.c)312
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.h (renamed from drivers/staging/cxd2099/cxd2099.h)18
-rw-r--r--drivers/staging/media/dt3155v4l/Kconfig (renamed from drivers/staging/dt3155v4l/Kconfig)10
-rw-r--r--drivers/staging/media/dt3155v4l/Makefile (renamed from drivers/staging/dt3155v4l/Makefile)0
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.c (renamed from drivers/staging/dt3155v4l/dt3155v4l.c)597
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.h (renamed from drivers/staging/dt3155v4l/dt3155v4l.h)26
-rw-r--r--drivers/staging/media/easycap/Kconfig (renamed from drivers/staging/easycap/Kconfig)32
-rw-r--r--drivers/staging/media/easycap/Makefile (renamed from drivers/staging/easycap/Makefile)4
-rw-r--r--drivers/staging/media/easycap/README (renamed from drivers/staging/easycap/README)28
-rw-r--r--drivers/staging/media/easycap/easycap.h (renamed from drivers/staging/easycap/easycap.h)57
-rw-r--r--drivers/staging/media/easycap/easycap_ioctl.c (renamed from drivers/staging/easycap/easycap_ioctl.c)49
-rw-r--r--drivers/staging/media/easycap/easycap_low.c (renamed from drivers/staging/easycap/easycap_low.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_main.c (renamed from drivers/staging/easycap/easycap_main.c)210
-rw-r--r--drivers/staging/media/easycap/easycap_settings.c (renamed from drivers/staging/easycap/easycap_settings.c)2
-rw-r--r--drivers/staging/media/easycap/easycap_sound.c (renamed from drivers/staging/easycap/easycap_sound.c)38
-rw-r--r--drivers/staging/media/easycap/easycap_testcard.c (renamed from drivers/staging/easycap/easycap_testcard.c)0
-rw-r--r--drivers/staging/media/go7007/Kconfig (renamed from drivers/staging/go7007/Kconfig)0
-rw-r--r--drivers/staging/media/go7007/Makefile (renamed from drivers/staging/go7007/Makefile)8
-rw-r--r--drivers/staging/media/go7007/README (renamed from drivers/staging/go7007/README)0
-rw-r--r--drivers/staging/media/go7007/go7007-driver.c (renamed from drivers/staging/go7007/go7007-driver.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-fw.c (renamed from drivers/staging/go7007/go7007-fw.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-i2c.c (renamed from drivers/staging/go7007/go7007-i2c.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-priv.h (renamed from drivers/staging/go7007/go7007-priv.h)0
-rw-r--r--drivers/staging/media/go7007/go7007-usb.c (renamed from drivers/staging/go7007/go7007-usb.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-v4l2.c (renamed from drivers/staging/go7007/go7007-v4l2.c)0
-rw-r--r--drivers/staging/media/go7007/go7007.h (renamed from drivers/staging/go7007/go7007.h)0
-rw-r--r--drivers/staging/media/go7007/go7007.txt (renamed from drivers/staging/go7007/go7007.txt)0
-rw-r--r--drivers/staging/media/go7007/s2250-board.c (renamed from drivers/staging/go7007/s2250-board.c)0
-rw-r--r--drivers/staging/media/go7007/s2250-loader.c (renamed from drivers/staging/go7007/s2250-loader.c)0
-rw-r--r--drivers/staging/media/go7007/s2250-loader.h (renamed from drivers/staging/go7007/s2250-loader.h)0
-rw-r--r--drivers/staging/media/go7007/saa7134-go7007.c (renamed from drivers/staging/go7007/saa7134-go7007.c)0
-rw-r--r--drivers/staging/media/go7007/snd-go7007.c (renamed from drivers/staging/go7007/snd-go7007.c)0
-rw-r--r--drivers/staging/media/go7007/wis-i2c.h (renamed from drivers/staging/go7007/wis-i2c.h)0
-rw-r--r--drivers/staging/media/go7007/wis-ov7640.c (renamed from drivers/staging/go7007/wis-ov7640.c)0
-rw-r--r--drivers/staging/media/go7007/wis-saa7113.c (renamed from drivers/staging/go7007/wis-saa7113.c)0
-rw-r--r--drivers/staging/media/go7007/wis-saa7115.c (renamed from drivers/staging/go7007/wis-saa7115.c)0
-rw-r--r--drivers/staging/media/go7007/wis-sony-tuner.c (renamed from drivers/staging/go7007/wis-sony-tuner.c)0
-rw-r--r--drivers/staging/media/go7007/wis-tw2804.c (renamed from drivers/staging/go7007/wis-tw2804.c)6
-rw-r--r--drivers/staging/media/go7007/wis-tw9903.c (renamed from drivers/staging/go7007/wis-tw9903.c)0
-rw-r--r--drivers/staging/media/go7007/wis-uda1342.c (renamed from drivers/staging/go7007/wis-uda1342.c)0
-rw-r--r--drivers/staging/media/lirc/Kconfig (renamed from drivers/staging/lirc/Kconfig)0
-rw-r--r--drivers/staging/media/lirc/Makefile (renamed from drivers/staging/lirc/Makefile)0
-rw-r--r--drivers/staging/media/lirc/TODO (renamed from drivers/staging/lirc/TODO)0
-rw-r--r--drivers/staging/media/lirc/TODO.lirc_zilog (renamed from drivers/staging/lirc/TODO.lirc_zilog)0
-rw-r--r--drivers/staging/media/lirc/lirc_bt829.c (renamed from drivers/staging/lirc/lirc_bt829.c)4
-rw-r--r--drivers/staging/media/lirc/lirc_ene0100.h (renamed from drivers/staging/lirc/lirc_ene0100.h)0
-rw-r--r--drivers/staging/media/lirc/lirc_igorplugusb.c (renamed from drivers/staging/lirc/lirc_igorplugusb.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_imon.c (renamed from drivers/staging/lirc/lirc_imon.c)6
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.c (renamed from drivers/staging/lirc/lirc_parallel.c)6
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.h (renamed from drivers/staging/lirc/lirc_parallel.h)0
-rw-r--r--drivers/staging/media/lirc/lirc_sasem.c (renamed from drivers/staging/lirc/lirc_sasem.c)46
-rw-r--r--drivers/staging/media/lirc/lirc_serial.c (renamed from drivers/staging/lirc/lirc_serial.c)2
-rw-r--r--drivers/staging/media/lirc/lirc_sir.c (renamed from drivers/staging/lirc/lirc_sir.c)2
-rw-r--r--drivers/staging/media/lirc/lirc_ttusbir.c (renamed from drivers/staging/lirc/lirc_ttusbir.c)1
-rw-r--r--drivers/staging/media/lirc/lirc_zilog.c (renamed from drivers/staging/lirc/lirc_zilog.c)2
-rw-r--r--drivers/staging/media/solo6x10/Kconfig (renamed from drivers/staging/solo6x10/Kconfig)0
-rw-r--r--drivers/staging/media/solo6x10/Makefile (renamed from drivers/staging/solo6x10/Makefile)0
-rw-r--r--drivers/staging/media/solo6x10/TODO (renamed from drivers/staging/solo6x10/TODO)0
-rw-r--r--drivers/staging/media/solo6x10/core.c (renamed from drivers/staging/solo6x10/core.c)1
-rw-r--r--drivers/staging/media/solo6x10/disp.c (renamed from drivers/staging/solo6x10/disp.c)0
-rw-r--r--drivers/staging/media/solo6x10/enc.c (renamed from drivers/staging/solo6x10/enc.c)1
-rw-r--r--drivers/staging/media/solo6x10/g723.c (renamed from drivers/staging/solo6x10/g723.c)2
-rw-r--r--drivers/staging/media/solo6x10/gpio.c (renamed from drivers/staging/solo6x10/gpio.c)0
-rw-r--r--drivers/staging/media/solo6x10/i2c.c (renamed from drivers/staging/solo6x10/i2c.c)0
-rw-r--r--drivers/staging/media/solo6x10/jpeg.h (renamed from drivers/staging/solo6x10/jpeg.h)0
-rw-r--r--drivers/staging/media/solo6x10/offsets.h (renamed from drivers/staging/solo6x10/offsets.h)0
-rw-r--r--drivers/staging/media/solo6x10/osd-font.h (renamed from drivers/staging/solo6x10/osd-font.h)0
-rw-r--r--drivers/staging/media/solo6x10/p2m.c (renamed from drivers/staging/solo6x10/p2m.c)1
-rw-r--r--drivers/staging/media/solo6x10/registers.h (renamed from drivers/staging/solo6x10/registers.h)0
-rw-r--r--drivers/staging/media/solo6x10/solo6x10.h (renamed from drivers/staging/solo6x10/solo6x10.h)3
-rw-r--r--drivers/staging/media/solo6x10/tw28.c (renamed from drivers/staging/solo6x10/tw28.c)0
-rw-r--r--drivers/staging/media/solo6x10/tw28.h (renamed from drivers/staging/solo6x10/tw28.h)0
-rw-r--r--drivers/staging/media/solo6x10/v4l2-enc.c (renamed from drivers/staging/solo6x10/v4l2-enc.c)0
-rw-r--r--drivers/staging/media/solo6x10/v4l2.c (renamed from drivers/staging/solo6x10/v4l2.c)0
-rw-r--r--drivers/staging/mei/Kconfig2
-rw-r--r--drivers/staging/mei/TODO10
-rw-r--r--drivers/staging/mei/init.c172
-rw-r--r--drivers/staging/mei/interface.c13
-rw-r--r--drivers/staging/mei/interface.h8
-rw-r--r--drivers/staging/mei/interrupt.c126
-rw-r--r--drivers/staging/mei/iorw.c32
-rw-r--r--drivers/staging/mei/main.c90
-rw-r--r--drivers/staging/mei/mei_dev.h143
-rw-r--r--drivers/staging/mei/wd.c254
-rw-r--r--drivers/staging/msm/Kconfig124
-rw-r--r--drivers/staging/msm/Makefile88
-rw-r--r--drivers/staging/msm/TODO3
-rw-r--r--drivers/staging/msm/ebi2_l2f.c569
-rw-r--r--drivers/staging/msm/ebi2_lcd.c250
-rw-r--r--drivers/staging/msm/ebi2_tmd20.c1122
-rw-r--r--drivers/staging/msm/hdmi_sii9022.c248
-rw-r--r--drivers/staging/msm/lcdc.c239
-rw-r--r--drivers/staging/msm/lcdc_external.c54
-rw-r--r--drivers/staging/msm/lcdc_gordon.c446
-rw-r--r--drivers/staging/msm/lcdc_panel.c88
-rw-r--r--drivers/staging/msm/lcdc_prism.c64
-rw-r--r--drivers/staging/msm/lcdc_sharp_wvga_pt.c290
-rw-r--r--drivers/staging/msm/lcdc_st15.c237
-rw-r--r--drivers/staging/msm/lcdc_toshiba_wvga_pt.c374
-rw-r--r--drivers/staging/msm/logo.c98
-rw-r--r--drivers/staging/msm/mddi.c375
-rw-r--r--drivers/staging/msm/mddi_ext.c320
-rw-r--r--drivers/staging/msm/mddi_ext_lcd.c91
-rw-r--r--drivers/staging/msm/mddi_prism.c114
-rw-r--r--drivers/staging/msm/mddi_sharp.c892
-rw-r--r--drivers/staging/msm/mddi_toshiba.c1741
-rw-r--r--drivers/staging/msm/mddi_toshiba.h36
-rw-r--r--drivers/staging/msm/mddi_toshiba_vga.c136
-rw-r--r--drivers/staging/msm/mddi_toshiba_wvga_pt.c64
-rw-r--r--drivers/staging/msm/mddihost.c377
-rw-r--r--drivers/staging/msm/mddihost.h207
-rw-r--r--drivers/staging/msm/mddihost_e.c63
-rw-r--r--drivers/staging/msm/mddihosti.c2239
-rw-r--r--drivers/staging/msm/mddihosti.h531
-rw-r--r--drivers/staging/msm/mdp.c1113
-rw-r--r--drivers/staging/msm/mdp.h679
-rw-r--r--drivers/staging/msm/mdp4.h336
-rw-r--r--drivers/staging/msm/mdp4_debugfs.c175
-rw-r--r--drivers/staging/msm/mdp4_overlay.c1259
-rw-r--r--drivers/staging/msm/mdp4_overlay_lcdc.c313
-rw-r--r--drivers/staging/msm/mdp4_overlay_mddi.c254
-rw-r--r--drivers/staging/msm/mdp4_util.c1686
-rw-r--r--drivers/staging/msm/mdp_cursor.c104
-rw-r--r--drivers/staging/msm/mdp_dma.c561
-rw-r--r--drivers/staging/msm/mdp_dma_lcdc.c379
-rw-r--r--drivers/staging/msm/mdp_dma_s.c139
-rw-r--r--drivers/staging/msm/mdp_dma_tv.c142
-rw-r--r--drivers/staging/msm/mdp_hw_init.c720
-rw-r--r--drivers/staging/msm/mdp_ppp.c1502
-rw-r--r--drivers/staging/msm/mdp_ppp_dq.c347
-rw-r--r--drivers/staging/msm/mdp_ppp_dq.h69
-rw-r--r--drivers/staging/msm/mdp_ppp_v20.c2486
-rw-r--r--drivers/staging/msm/mdp_ppp_v31.c828
-rw-r--r--drivers/staging/msm/mdp_vsync.c389
-rw-r--r--drivers/staging/msm/memory.c214
-rw-r--r--drivers/staging/msm/memory_ll.h61
-rw-r--r--drivers/staging/msm/msm_fb.c2354
-rw-r--r--drivers/staging/msm/msm_fb.h158
-rw-r--r--drivers/staging/msm/msm_fb_bl.c79
-rw-r--r--drivers/staging/msm/msm_fb_def.h181
-rw-r--r--drivers/staging/msm/msm_fb_panel.c136
-rw-r--r--drivers/staging/msm/msm_fb_panel.h129
-rw-r--r--drivers/staging/msm/msm_mdp.h245
-rw-r--r--drivers/staging/msm/staging-devices.c312
-rw-r--r--drivers/staging/msm/tv_ntsc.c163
-rw-r--r--drivers/staging/msm/tv_pal.c213
-rw-r--r--drivers/staging/msm/tvenc.c296
-rw-r--r--drivers/staging/msm/tvenc.h101
-rw-r--r--drivers/staging/nvec/Kconfig12
-rw-r--r--drivers/staging/nvec/Makefile1
-rw-r--r--drivers/staging/nvec/TODO16
-rw-r--r--drivers/staging/nvec/nvec-keytable.h225
-rw-r--r--drivers/staging/nvec/nvec.c959
-rw-r--r--drivers/staging/nvec/nvec.h217
-rw-r--r--drivers/staging/nvec/nvec_kbd.c95
-rw-r--r--drivers/staging/nvec/nvec_leds.c114
-rw-r--r--drivers/staging/nvec/nvec_power.c352
-rw-r--r--drivers/staging/nvec/nvec_ps2.c114
-rw-r--r--drivers/staging/octeon/ethernet-mdio.c27
-rw-r--r--drivers/staging/octeon/ethernet-rgmii.c34
-rw-r--r--drivers/staging/octeon/ethernet-rx.c29
-rw-r--r--drivers/staging/octeon/ethernet-sgmii.c14
-rw-r--r--drivers/staging/octeon/ethernet-spi.c1
-rw-r--r--drivers/staging/octeon/ethernet-tx.c15
-rw-r--r--drivers/staging/octeon/ethernet-util.h4
-rw-r--r--drivers/staging/octeon/ethernet-xaui.c22
-rw-r--r--drivers/staging/octeon/ethernet.c12
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon.c47
-rw-r--r--drivers/staging/panel/panel.c11
-rw-r--r--drivers/staging/pohmelfs/Kconfig8
-rw-r--r--drivers/staging/pohmelfs/crypto.c1
-rw-r--r--drivers/staging/pohmelfs/dir.c2
-rw-r--r--drivers/staging/pohmelfs/inode.c13
-rw-r--r--drivers/staging/pohmelfs/trans.c6
-rw-r--r--drivers/staging/quatech_usb2/quatech_usb2.c9
-rw-r--r--drivers/staging/rtl8187se/Makefile6
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211.h6
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211_crypt.c1
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_ccmp.c1
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_tkip.c1
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_wep.c1
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211_module.c1
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c23
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211_tx.c2
-rw-r--r--drivers/staging/rtl8187se/r8180.h1
-rw-r--r--drivers/staging/rtl8187se/r8180_core.c5
-rw-r--r--drivers/staging/rtl8187se/r8180_rtl8225z2.c4
-rw-r--r--drivers/staging/rtl8192e/Makefile67
-rw-r--r--drivers/staging/rtl8192e/TODO2
-rw-r--r--drivers/staging/rtl8192e/dot11d.c216
-rw-r--r--drivers/staging/rtl8192e/dot11d.h124
-rw-r--r--drivers/staging/rtl8192e/ieee80211/dot11d.c218
-rw-r--r--drivers/staging/rtl8192e/ieee80211/dot11d.h102
-rw-r--r--drivers/staging/rtl8192e/ieee80211/ieee80211.h2636
-rw-r--r--drivers/staging/rtl8192e/ieee80211/ieee80211_module.c353
-rw-r--r--drivers/staging/rtl8192e/ieee80211/ieee80211_rx.c2677
-rw-r--r--drivers/staging/rtl8192e/ieee80211/ieee80211_softmac.c3279
-rw-r--r--drivers/staging/rtl8192e/ieee80211/ieee80211_tx.c956
-rw-r--r--drivers/staging/rtl8192e/ieee80211/ieee80211_wx.c872
-rw-r--r--drivers/staging/rtl8192e/ieee80211/rtl819x_BA.h69
-rw-r--r--drivers/staging/rtl8192e/ieee80211/rtl819x_BAProc.c676
-rw-r--r--drivers/staging/rtl8192e/ieee80211/rtl819x_HT.h483
-rw-r--r--drivers/staging/rtl8192e/ieee80211/rtl819x_HTProc.c1732
-rw-r--r--drivers/staging/rtl8192e/ieee80211/rtl819x_Qos.h582
-rw-r--r--drivers/staging/rtl8192e/ieee80211/rtl819x_TS.h56
-rw-r--r--drivers/staging/rtl8192e/ieee80211/rtl819x_TSProc.c627
-rw-r--r--drivers/staging/rtl8192e/license339
-rw-r--r--drivers/staging/rtl8192e/r8180_93cx6.c141
-rw-r--r--drivers/staging/rtl8192e/r8180_93cx6.h41
-rw-r--r--drivers/staging/rtl8192e/r8190P_def.h410
-rw-r--r--drivers/staging/rtl8192e/r8190P_rtl8256.c306
-rw-r--r--drivers/staging/rtl8192e/r8190P_rtl8256.h31
-rw-r--r--drivers/staging/rtl8192e/r8190_rtl8256.c677
-rw-r--r--drivers/staging/rtl8192e/r8190_rtl8256.h29
-rw-r--r--drivers/staging/rtl8192e/r8192E.h1148
-rw-r--r--drivers/staging/rtl8192e/r8192E_cmdpkt.c418
-rw-r--r--drivers/staging/rtl8192e/r8192E_cmdpkt.h159
-rw-r--r--drivers/staging/rtl8192e/r8192E_core.c5033
-rw-r--r--drivers/staging/rtl8192e/r8192E_dev.c2395
-rw-r--r--drivers/staging/rtl8192e/r8192E_dev.h62
-rw-r--r--drivers/staging/rtl8192e/r8192E_dm.c2554
-rw-r--r--drivers/staging/rtl8192e/r8192E_dm.h228
-rw-r--r--drivers/staging/rtl8192e/r8192E_firmware.c348
-rw-r--r--drivers/staging/rtl8192e/r8192E_firmware.h73
-rw-r--r--drivers/staging/rtl8192e/r8192E_hw.h552
-rw-r--r--drivers/staging/rtl8192e/r8192E_hwimg.c3336
-rw-r--r--drivers/staging/rtl8192e/r8192E_hwimg.h51
-rw-r--r--drivers/staging/rtl8192e/r8192E_phy.c1637
-rw-r--r--drivers/staging/rtl8192e/r8192E_phy.h120
-rw-r--r--drivers/staging/rtl8192e/r8192E_phyreg.h852
-rw-r--r--drivers/staging/rtl8192e/r8192E_wx.c1163
-rw-r--r--drivers/staging/rtl8192e/r8192E_wx.h18
-rw-r--r--drivers/staging/rtl8192e/r8192_pm.c123
-rw-r--r--drivers/staging/rtl8192e/r8192_pm.h24
-rw-r--r--drivers/staging/rtl8192e/r819xE_cmdpkt.c444
-rw-r--r--drivers/staging/rtl8192e/r819xE_cmdpkt.h207
-rw-r--r--drivers/staging/rtl8192e/r819xE_firmware.c351
-rw-r--r--drivers/staging/rtl8192e/r819xE_phy.c2225
-rw-r--r--drivers/staging/rtl8192e/r819xE_phy.h131
-rw-r--r--drivers/staging/rtl8192e/rtl819x_BA.h77
-rw-r--r--drivers/staging/rtl8192e/rtl819x_BAProc.c566
-rw-r--r--drivers/staging/rtl8192e/rtl819x_HT.h475
-rw-r--r--drivers/staging/rtl8192e/rtl819x_HTProc.c1075
-rw-r--r--drivers/staging/rtl8192e/rtl819x_Qos.h444
-rw-r--r--drivers/staging/rtl8192e/rtl819x_TS.h73
-rw-r--r--drivers/staging/rtl8192e/rtl819x_TSProc.c548
-rw-r--r--drivers/staging/rtl8192e/rtl_cam.c304
-rw-r--r--drivers/staging/rtl8192e/rtl_cam.h46
-rw-r--r--drivers/staging/rtl8192e/rtl_core.c3198
-rw-r--r--drivers/staging/rtl8192e/rtl_core.h1124
-rw-r--r--drivers/staging/rtl8192e/rtl_crypto.h (renamed from drivers/staging/rtl8192e/ieee80211/rtl_crypto.h)105
-rw-r--r--drivers/staging/rtl8192e/rtl_debug.c1108
-rw-r--r--drivers/staging/rtl8192e/rtl_debug.h299
-rw-r--r--drivers/staging/rtl8192e/rtl_dm.c2995
-rw-r--r--drivers/staging/rtl8192e/rtl_dm.h324
-rw-r--r--drivers/staging/rtl8192e/rtl_eeprom.c139
-rw-r--r--drivers/staging/rtl8192e/rtl_eeprom.h29
-rw-r--r--drivers/staging/rtl8192e/rtl_ethtool.c53
-rw-r--r--drivers/staging/rtl8192e/rtl_pci.c97
-rw-r--r--drivers/staging/rtl8192e/rtl_pci.h104
-rw-r--r--drivers/staging/rtl8192e/rtl_pm.c136
-rw-r--r--drivers/staging/rtl8192e/rtl_pm.h35
-rw-r--r--drivers/staging/rtl8192e/rtl_ps.c310
-rw-r--r--drivers/staging/rtl8192e/rtl_ps.h47
-rw-r--r--drivers/staging/rtl8192e/rtl_wx.c1333
-rw-r--r--drivers/staging/rtl8192e/rtl_wx.h31
-rw-r--r--drivers/staging/rtl8192e/rtllib.h3144
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt.c (renamed from drivers/staging/rtl8192e/ieee80211/ieee80211_crypt.c)90
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt.h (renamed from drivers/staging/rtl8192e/ieee80211/ieee80211_crypt.h)32
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt_ccmp.c (renamed from drivers/staging/rtl8192e/ieee80211/ieee80211_crypt_ccmp.c)169
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt_tkip.c (renamed from drivers/staging/rtl8192e/ieee80211/ieee80211_crypt_tkip.c)309
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt_wep.c (renamed from drivers/staging/rtl8192e/ieee80211/ieee80211_crypt_wep.c)115
-rw-r--r--drivers/staging/rtl8192e/rtllib_endianfree.h160
-rw-r--r--drivers/staging/rtl8192e/rtllib_module.c289
-rw-r--r--drivers/staging/rtl8192e/rtllib_rx.c2720
-rw-r--r--drivers/staging/rtl8192e/rtllib_softmac.c3741
-rw-r--r--drivers/staging/rtl8192e/rtllib_softmac_wx.c (renamed from drivers/staging/rtl8192e/ieee80211/ieee80211_softmac_wx.c)381
-rw-r--r--drivers/staging/rtl8192e/rtllib_tx.c967
-rw-r--r--drivers/staging/rtl8192e/rtllib_wx.c876
-rw-r--r--drivers/staging/rtl8192u/ieee80211/Makefile109
-rw-r--r--drivers/staging/rtl8192u/ieee80211/compress.c4
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211.h6
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.c1
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c1
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_tkip.c1
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_wep.c1
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_module.c3
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c1
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c21
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_tx.c2
-rw-r--r--drivers/staging/rtl8192u/r8192U.h1
-rw-r--r--drivers/staging/rtl8192u/r8192U_core.c12
-rw-r--r--drivers/staging/rtl8192u/r819xU_firmware.c9
-rw-r--r--drivers/staging/rtl8712/Kconfig7
-rw-r--r--drivers/staging/rtl8712/basic_types.h25
-rw-r--r--drivers/staging/rtl8712/big_endian.h25
-rw-r--r--drivers/staging/rtl8712/drv_types.h34
-rw-r--r--drivers/staging/rtl8712/ethernet.h25
-rw-r--r--drivers/staging/rtl8712/generic.h25
-rw-r--r--drivers/staging/rtl8712/hal_init.c6
-rw-r--r--drivers/staging/rtl8712/ieee80211.c35
-rw-r--r--drivers/staging/rtl8712/ieee80211.h62
-rw-r--r--drivers/staging/rtl8712/if_ether.h25
-rw-r--r--drivers/staging/rtl8712/little_endian.h25
-rw-r--r--drivers/staging/rtl8712/mlme_osdep.h25
-rw-r--r--drivers/staging/rtl8712/mp_custom_oid.h25
-rw-r--r--drivers/staging/rtl8712/os_intfs.c33
-rw-r--r--drivers/staging/rtl8712/osdep_intf.h25
-rw-r--r--drivers/staging/rtl8712/osdep_service.h70
-rw-r--r--drivers/staging/rtl8712/recv_linux.c6
-rw-r--r--drivers/staging/rtl8712/recv_osdep.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_bitdef.h21
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmd.c18
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmd.h87
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_edcasetting_bitdef.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_edcasetting_regdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_efuse.c75
-rw-r--r--drivers/staging/rtl8712/rtl8712_event.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_gp_bitdef.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_gp_regdef.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_hal.h27
-rw-r--r--drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_led.c2
-rw-r--r--drivers/staging/rtl8712/rtl8712_macsetting_bitdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_macsetting_regdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_powersave_bitdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_powersave_regdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_ratectrl_bitdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_ratectrl_regdef.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_recv.c24
-rw-r--r--drivers/staging/rtl8712/rtl8712_recv.h31
-rw-r--r--drivers/staging/rtl8712/rtl8712_regdef.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_security_bitdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_spec.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_syscfg_bitdef.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_syscfg_regdef.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_timectrl_bitdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_timectrl_regdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_wmac_bitdef.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_wmac_regdef.h25
-rw-r--r--drivers/staging/rtl8712/rtl8712_xmit.c303
-rw-r--r--drivers/staging/rtl8712/rtl8712_xmit.h37
-rw-r--r--drivers/staging/rtl8712/rtl871x_byteorder.h19
-rw-r--r--drivers/staging/rtl8712/rtl871x_cmd.c172
-rw-r--r--drivers/staging/rtl8712/rtl871x_cmd.h77
-rw-r--r--drivers/staging/rtl8712/rtl871x_debug.h25
-rw-r--r--drivers/staging/rtl8712/rtl871x_eeprom.h19
-rw-r--r--drivers/staging/rtl8712/rtl871x_event.h25
-rw-r--r--drivers/staging/rtl8712/rtl871x_ht.h25
-rw-r--r--drivers/staging/rtl8712/rtl871x_io.h25
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_linux.c306
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_rtl.h25
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_set.c3
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_set.h27
-rw-r--r--drivers/staging/rtl8712/rtl871x_led.h25
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.c62
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.h28
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp.c26
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp.h26
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp_ioctl.h29
-rw-r--r--drivers/staging/rtl8712/rtl871x_pwrctrl.h25
-rw-r--r--drivers/staging/rtl8712/rtl871x_recv.c4
-rw-r--r--drivers/staging/rtl8712/rtl871x_rf.h25
-rw-r--r--drivers/staging/rtl8712/rtl871x_security.c15
-rw-r--r--drivers/staging/rtl8712/rtl871x_security.h25
-rw-r--r--drivers/staging/rtl8712/rtl871x_wlan_sme.h25
-rw-r--r--drivers/staging/rtl8712/rtl871x_xmit.c31
-rw-r--r--drivers/staging/rtl8712/rtl871x_xmit.h52
-rw-r--r--drivers/staging/rtl8712/sta_info.h29
-rw-r--r--drivers/staging/rtl8712/swab.h25
-rw-r--r--drivers/staging/rtl8712/usb_intf.c19
-rw-r--r--drivers/staging/rtl8712/usb_ops.h25
-rw-r--r--drivers/staging/rtl8712/usb_ops_linux.c29
-rw-r--r--drivers/staging/rtl8712/usb_osintf.h26
-rw-r--r--drivers/staging/rtl8712/usb_vendor_req.h25
-rw-r--r--drivers/staging/rtl8712/wifi.h32
-rw-r--r--drivers/staging/rtl8712/wlan_bssdef.h25
-rw-r--r--drivers/staging/rtl8712/xmit_linux.c27
-rw-r--r--drivers/staging/rtl8712/xmit_osdep.h26
-rw-r--r--drivers/staging/rts5139/Kconfig16
-rw-r--r--drivers/staging/rts5139/Makefile37
-rw-r--r--drivers/staging/rts5139/TODO5
-rw-r--r--drivers/staging/rts5139/debug.h46
-rw-r--r--drivers/staging/rts5139/ms.c4191
-rw-r--r--drivers/staging/rts5139/ms.h263
-rw-r--r--drivers/staging/rts5139/ms_mg.c642
-rw-r--r--drivers/staging/rts5139/ms_mg.h41
-rw-r--r--drivers/staging/rts5139/rts51x.c967
-rw-r--r--drivers/staging/rts5139/rts51x.h205
-rw-r--r--drivers/staging/rts5139/rts51x_card.c986
-rw-r--r--drivers/staging/rts5139/rts51x_card.h881
-rw-r--r--drivers/staging/rts5139/rts51x_chip.c1167
-rw-r--r--drivers/staging/rts5139/rts51x_chip.h904
-rw-r--r--drivers/staging/rts5139/rts51x_fop.c298
-rw-r--r--drivers/staging/rts5139/rts51x_fop.h62
-rw-r--r--drivers/staging/rts5139/rts51x_scsi.c2235
-rw-r--r--drivers/staging/rts5139/rts51x_scsi.h162
-rw-r--r--drivers/staging/rts5139/rts51x_sys.h54
-rw-r--r--drivers/staging/rts5139/rts51x_transport.c1000
-rw-r--r--drivers/staging/rts5139/rts51x_transport.h80
-rw-r--r--drivers/staging/rts5139/sd.c3400
-rw-r--r--drivers/staging/rts5139/sd.h304
-rw-r--r--drivers/staging/rts5139/sd_cprm.c1215
-rw-r--r--drivers/staging/rts5139/sd_cprm.h54
-rw-r--r--drivers/staging/rts5139/trace.h137
-rw-r--r--drivers/staging/rts5139/xd.c2255
-rw-r--r--drivers/staging/rts5139/xd.h193
-rw-r--r--drivers/staging/rts_pstor/Makefile2
-rw-r--r--drivers/staging/rts_pstor/ms.c3
-rw-r--r--drivers/staging/rts_pstor/rtsx.c171
-rw-r--r--drivers/staging/rts_pstor/rtsx.h20
-rw-r--r--drivers/staging/rts_pstor/rtsx_chip.c6
-rw-r--r--drivers/staging/rts_pstor/rtsx_scsi.c2
-rw-r--r--drivers/staging/rts_pstor/sd.c99
-rw-r--r--drivers/staging/rts_pstor/sd.h5
-rw-r--r--drivers/staging/rts_pstor/spi.c2
-rw-r--r--drivers/staging/sep/sep_driver.c13
-rw-r--r--drivers/staging/sep/sep_driver_config.h2
-rw-r--r--drivers/staging/serial/68360serial.c (renamed from drivers/tty/serial/68360serial.c)0
-rw-r--r--drivers/staging/serial/Kconfig16
-rw-r--r--drivers/staging/serial/Makefile1
-rw-r--r--drivers/staging/serial/TODO6
-rw-r--r--drivers/staging/serqt_usb2/serqt_usb2.c37
-rw-r--r--drivers/staging/slicoss/Kconfig2
-rw-r--r--drivers/staging/slicoss/slicoss.c2
-rw-r--r--drivers/staging/sm7xx/smtcfb.c2
-rw-r--r--drivers/staging/speakup/devsynth.c5
-rw-r--r--drivers/staging/speakup/main.c1
-rw-r--r--drivers/staging/speakup/speakup.h1
-rw-r--r--drivers/staging/speakup/speakup_soft.c9
-rw-r--r--drivers/staging/spectra/ffsport.c15
-rw-r--r--drivers/staging/spectra/lld_mtd.c6
-rw-r--r--drivers/staging/spectra/lld_nand.c18
-rw-r--r--drivers/staging/ste_rmi4/Makefile1
-rw-r--r--drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c32
-rw-r--r--drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c44
-rw-r--r--drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h2
-rw-r--r--drivers/staging/tidspbridge/core/dsp-clock.c1
-rw-r--r--drivers/staging/tidspbridge/gen/gh.c2
-rw-r--r--drivers/staging/tidspbridge/hw/hw_mmu.c2
-rw-r--r--drivers/staging/tidspbridge/include/dspbridge/host_os.h3
-rw-r--r--drivers/staging/tm6000/CARDLIST16
-rw-r--r--drivers/staging/tm6000/README22
-rw-r--r--drivers/staging/tm6000/TODO8
-rw-r--r--drivers/staging/tm6000/tm6000-stds.c679
-rw-r--r--drivers/staging/tty/Kconfig87
-rw-r--r--drivers/staging/tty/Makefile7
-rw-r--r--drivers/staging/tty/TODO6
-rw-r--r--drivers/staging/tty/cd1865.h263
-rw-r--r--drivers/staging/tty/digi1.h100
-rw-r--r--drivers/staging/tty/digiFep1.h136
-rw-r--r--drivers/staging/tty/digiPCI.h42
-rw-r--r--drivers/staging/tty/epca.c2784
-rw-r--r--drivers/staging/tty/epca.h158
-rw-r--r--drivers/staging/tty/epcaconfig.h7
-rw-r--r--drivers/staging/tty/ip2/Makefile8
-rw-r--r--drivers/staging/tty/ip2/i2cmd.c210
-rw-r--r--drivers/staging/tty/ip2/i2cmd.h630
-rw-r--r--drivers/staging/tty/ip2/i2ellis.c1403
-rw-r--r--drivers/staging/tty/ip2/i2ellis.h566
-rw-r--r--drivers/staging/tty/ip2/i2hw.h652
-rw-r--r--drivers/staging/tty/ip2/i2lib.c2214
-rw-r--r--drivers/staging/tty/ip2/i2lib.h351
-rw-r--r--drivers/staging/tty/ip2/i2pack.h364
-rw-r--r--drivers/staging/tty/ip2/ip2.h107
-rw-r--r--drivers/staging/tty/ip2/ip2ioctl.h35
-rw-r--r--drivers/staging/tty/ip2/ip2main.c3234
-rw-r--r--drivers/staging/tty/ip2/ip2trace.h42
-rw-r--r--drivers/staging/tty/ip2/ip2types.h57
-rw-r--r--drivers/staging/tty/istallion.c4507
-rw-r--r--drivers/staging/tty/riscom8.c1560
-rw-r--r--drivers/staging/tty/riscom8.h91
-rw-r--r--drivers/staging/tty/riscom8_reg.h254
-rw-r--r--drivers/staging/tty/serial167.c2489
-rw-r--r--drivers/staging/tty/specialix.c2368
-rw-r--r--drivers/staging/tty/specialix_io8.h140
-rw-r--r--drivers/staging/tty/stallion.c4651
-rw-r--r--drivers/staging/usbip/README1
-rw-r--r--drivers/staging/usbip/stub.h9
-rw-r--r--drivers/staging/usbip/stub_dev.c60
-rw-r--r--drivers/staging/usbip/stub_main.c194
-rw-r--r--drivers/staging/usbip/stub_rx.c11
-rw-r--r--drivers/staging/usbip/stub_tx.c15
-rw-r--r--drivers/staging/usbip/usbip_common.c36
-rw-r--r--drivers/staging/usbip/usbip_common.h236
-rw-r--r--drivers/staging/usbip/usbip_event.c1
-rw-r--r--drivers/staging/usbip/usbip_protocol.txt358
-rw-r--r--drivers/staging/usbip/userspace/AUTHORS1
-rw-r--r--drivers/staging/usbip/userspace/Makefile.am9
-rw-r--r--drivers/staging/usbip/userspace/README298
-rwxr-xr-xdrivers/staging/usbip/userspace/cleanup.sh10
-rw-r--r--drivers/staging/usbip/userspace/configure.ac26
-rw-r--r--drivers/staging/usbip/userspace/libsrc/Makefile.am8
-rw-r--r--drivers/staging/usbip/userspace/libsrc/stub_driver.c391
-rw-r--r--drivers/staging/usbip/userspace/libsrc/stub_driver.h36
-rw-r--r--drivers/staging/usbip/userspace/libsrc/usbip.h19
-rw-r--r--drivers/staging/usbip/userspace/libsrc/usbip_common.c28
-rw-r--r--drivers/staging/usbip/userspace/libsrc/usbip_common.h138
-rw-r--r--drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c401
-rw-r--r--drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h48
-rw-r--r--drivers/staging/usbip/userspace/libsrc/vhci_driver.c169
-rw-r--r--drivers/staging/usbip/userspace/libsrc/vhci_driver.h32
-rw-r--r--drivers/staging/usbip/userspace/src/Makefile.am17
-rw-r--r--drivers/staging/usbip/userspace/src/bind-driver.c643
-rw-r--r--drivers/staging/usbip/userspace/src/usbip.c817
-rw-r--r--drivers/staging/usbip/userspace/src/usbip.h39
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_attach.c230
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_bind.c277
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_detach.c103
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_list.c303
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_network.c198
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_network.h94
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_unbind.c186
-rw-r--r--drivers/staging/usbip/userspace/src/usbipd.c705
-rw-r--r--drivers/staging/usbip/userspace/src/utils.c283
-rw-r--r--drivers/staging/usbip/userspace/src/utils.h55
-rw-r--r--drivers/staging/usbip/userspace/usb.ids13209
-rw-r--r--drivers/staging/usbip/vhci.h16
-rw-r--r--drivers/staging/usbip/vhci_hcd.c59
-rw-r--r--drivers/staging/usbip/vhci_rx.c2
-rw-r--r--drivers/staging/usbip/vhci_sysfs.c6
-rw-r--r--drivers/staging/vme/boards/vme_vmivme7805.c4
-rw-r--r--drivers/staging/vme/bridges/vme_ca91cx42.c43
-rw-r--r--drivers/staging/vme/bridges/vme_tsi148.c26
-rw-r--r--drivers/staging/vme/devices/vme_user.c122
-rw-r--r--drivers/staging/vme/devices/vme_user.h8
-rw-r--r--drivers/staging/vme/vme.c349
-rw-r--r--drivers/staging/vme/vme.h52
-rw-r--r--drivers/staging/vme/vme_api.txt90
-rw-r--r--drivers/staging/vme/vme_bridge.h17
-rw-r--r--drivers/staging/vt6655/IEEE11h.c406
-rw-r--r--drivers/staging/vt6655/device_main.c12
-rw-r--r--drivers/staging/vt6655/dpc.c30
-rw-r--r--drivers/staging/vt6655/ioctl.c1092
-rw-r--r--drivers/staging/vt6655/wmgr.c49
-rw-r--r--drivers/staging/vt6655/wpactl.c4
-rw-r--r--drivers/staging/vt6656/dpc.c30
-rw-r--r--drivers/staging/vt6656/ioctl.c1040
-rw-r--r--drivers/staging/vt6656/main_usb.c13
-rw-r--r--drivers/staging/vt6656/wmgr.c49
-rw-r--r--drivers/staging/vt6656/wpactl.c18
-rw-r--r--drivers/staging/westbridge/Kconfig53
-rw-r--r--drivers/staging/westbridge/TODO7
-rw-r--r--drivers/staging/westbridge/astoria/Kconfig9
-rw-r--r--drivers/staging/westbridge/astoria/Makefile11
-rw-r--r--drivers/staging/westbridge/astoria/api/Makefile14
-rw-r--r--drivers/staging/westbridge/astoria/api/src/cyasdma.c1107
-rw-r--r--drivers/staging/westbridge/astoria/api/src/cyasintr.c143
-rw-r--r--drivers/staging/westbridge/astoria/api/src/cyaslep2pep.c358
-rw-r--r--drivers/staging/westbridge/astoria/api/src/cyaslowlevel.c1264
-rw-r--r--drivers/staging/westbridge/astoria/api/src/cyasmisc.c3488
-rw-r--r--drivers/staging/westbridge/astoria/api/src/cyasmtp.c1136
-rw-r--r--drivers/staging/westbridge/astoria/api/src/cyasstorage.c4125
-rw-r--r--drivers/staging/westbridge/astoria/api/src/cyasusb.c3740
-rw-r--r--drivers/staging/westbridge/astoria/arch/arm/mach-omap2/cyashalomap_kernel.c2441
-rw-r--r--drivers/staging/westbridge/astoria/arch/arm/plat-omap/include/mach/westbridge/cyashaldef.h55
-rw-r--r--drivers/staging/westbridge/astoria/arch/arm/plat-omap/include/mach/westbridge/westbridge-omap3-pnand-hal/cyashalomap_kernel.h319
-rw-r--r--drivers/staging/westbridge/astoria/arch/arm/plat-omap/include/mach/westbridge/westbridge-omap3-pnand-hal/cyasmemmap.h558
-rw-r--r--drivers/staging/westbridge/astoria/arch/arm/plat-omap/include/mach/westbridge/westbridge-omap3-pnand-hal/cyasomapdev_kernel.h72
-rw-r--r--drivers/staging/westbridge/astoria/block/Kconfig9
-rw-r--r--drivers/staging/westbridge/astoria/block/Makefile11
-rw-r--r--drivers/staging/westbridge/astoria/block/cyasblkdev_block.c1631
-rw-r--r--drivers/staging/westbridge/astoria/block/cyasblkdev_queue.c417
-rw-r--r--drivers/staging/westbridge/astoria/block/cyasblkdev_queue.h64
-rw-r--r--drivers/staging/westbridge/astoria/device/Kconfig9
-rw-r--r--drivers/staging/westbridge/astoria/device/Makefile23
-rw-r--r--drivers/staging/westbridge/astoria/device/cyasdevice.c409
-rw-r--r--drivers/staging/westbridge/astoria/gadget/Kconfig9
-rw-r--r--drivers/staging/westbridge/astoria/gadget/Makefile11
-rw-r--r--drivers/staging/westbridge/astoria/gadget/cyasgadget.c2177
-rw-r--r--drivers/staging/westbridge/astoria/gadget/cyasgadget.h193
-rw-r--r--drivers/staging/westbridge/astoria/gadget/cyasgadget_ioctl.h99
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyanerr.h418
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyanmedia.h59
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyanmisc.h614
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyanregs.h180
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyansdkversion.h30
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyanstorage.h419
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyantioch.h35
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyantypes.h31
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyanusb.h619
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyas_cplus_end.h11
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyas_cplus_start.h11
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyascast.h35
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasdevice.h1057
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasdma.h375
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyaserr.h1094
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyashal.h108
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyashalcb.h44
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyashaldoc.h800
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasintr.h104
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyaslep2pep.h36
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyaslowlevel.h366
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasmedia.h54
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasmisc.h1549
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasmisc_dep.h53
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasmtp.h646
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasprotocol.h3838
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasregs.h201
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasstorage.h2759
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasstorage_dep.h309
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyastoria.h36
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyastsdkversion.h30
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyastypes.h71
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasusb.h1862
-rw-r--r--drivers/staging/westbridge/astoria/include/linux/westbridge/cyasusb_dep.h224
-rw-r--r--drivers/staging/winbond/mds_s.h2
-rw-r--r--drivers/staging/winbond/phy_calibration.c470
-rw-r--r--drivers/staging/winbond/wb35reg_s.h2
-rw-r--r--drivers/staging/winbond/wbusb.c3
-rw-r--r--drivers/staging/wlags49_h2/Makefile26
-rw-r--r--drivers/staging/wlags49_h2/debug.h61
-rw-r--r--drivers/staging/wlags49_h2/hcf.c6428
-rw-r--r--drivers/staging/wlags49_h2/hcf.h10
-rw-r--r--drivers/staging/wlags49_h2/hcfcfg.h1586
-rw-r--r--drivers/staging/wlags49_h2/hcfdef.h857
-rw-r--r--drivers/staging/wlags49_h2/mdd.h34
-rw-r--r--drivers/staging/wlags49_h2/wl_cs.c115
-rw-r--r--drivers/staging/wlags49_h2/wl_internal.h23
-rw-r--r--drivers/staging/wlags49_h2/wl_main.c6
-rw-r--r--drivers/staging/wlags49_h2/wl_netdev.c4
-rw-r--r--drivers/staging/wlags49_h2/wl_sysfs.c18
-rw-r--r--drivers/staging/wlags49_h2/wl_version.h7
-rw-r--r--drivers/staging/wlags49_h2/wl_wext.c1184
-rw-r--r--drivers/staging/wlags49_h2/wl_wext.h8
-rw-r--r--drivers/staging/wlags49_h25/Makefile26
-rw-r--r--drivers/staging/wlan-ng/p80211netdev.c2
-rw-r--r--drivers/staging/wlan-ng/prism2fw.c4
-rw-r--r--drivers/staging/wlan-ng/prism2sta.c1
-rw-r--r--drivers/staging/xgifb/TODO1
-rw-r--r--drivers/staging/xgifb/XGI_main.h238
-rw-r--r--drivers/staging/xgifb/XGI_main_26.c1452
-rw-r--r--drivers/staging/xgifb/XGIfb.h48
-rw-r--r--drivers/staging/xgifb/vb_def.h605
-rw-r--r--drivers/staging/xgifb/vb_ext.c1
-rw-r--r--drivers/staging/xgifb/vb_ext.h20
-rw-r--r--drivers/staging/xgifb/vb_init.c258
-rw-r--r--drivers/staging/xgifb/vb_init.h2
-rw-r--r--drivers/staging/xgifb/vb_setmode.c1536
-rw-r--r--drivers/staging/xgifb/vb_struct.h97
-rw-r--r--drivers/staging/xgifb/vb_table.h1200
-rw-r--r--drivers/staging/xgifb/vb_util.c5
-rw-r--r--drivers/staging/xgifb/vgatypes.h11
-rw-r--r--drivers/staging/zcache/Makefile2
-rw-r--r--drivers/staging/zcache/tmem.c102
-rw-r--r--drivers/staging/zcache/tmem.h23
-rw-r--r--drivers/staging/zcache/zcache-main.c (renamed from drivers/staging/zcache/zcache.c)577
-rw-r--r--drivers/staging/zram/zram_drv.c554
-rw-r--r--drivers/staging/zram/zram_drv.h21
-rw-r--r--drivers/staging/zram/zram_sysfs.c22
-rw-r--r--drivers/target/Kconfig1
-rw-r--r--drivers/target/Makefile3
-rw-r--r--drivers/target/iscsi/Kconfig9
-rw-r--r--drivers/target/iscsi/Makefile20
-rw-r--r--drivers/target/iscsi/iscsi_target.c4534
-rw-r--r--drivers/target/iscsi/iscsi_target.h42
-rw-r--r--drivers/target/iscsi/iscsi_target_auth.c462
-rw-r--r--drivers/target/iscsi/iscsi_target_auth.h31
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.c1883
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.h7
-rw-r--r--drivers/target/iscsi/iscsi_target_core.h861
-rw-r--r--drivers/target/iscsi/iscsi_target_datain_values.c531
-rw-r--r--drivers/target/iscsi/iscsi_target_datain_values.h12
-rw-r--r--drivers/target/iscsi/iscsi_target_device.c87
-rw-r--r--drivers/target/iscsi/iscsi_target_device.h9
-rw-r--r--drivers/target/iscsi/iscsi_target_erl0.c1004
-rw-r--r--drivers/target/iscsi/iscsi_target_erl0.h15
-rw-r--r--drivers/target/iscsi/iscsi_target_erl1.c1299
-rw-r--r--drivers/target/iscsi/iscsi_target_erl1.h26
-rw-r--r--drivers/target/iscsi/iscsi_target_erl2.c439
-rw-r--r--drivers/target/iscsi/iscsi_target_erl2.h18
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c1222
-rw-r--r--drivers/target/iscsi/iscsi_target_login.h12
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.c1067
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.h17
-rw-r--r--drivers/target/iscsi/iscsi_target_nodeattrib.c263
-rw-r--r--drivers/target/iscsi/iscsi_target_nodeattrib.h14
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.c1894
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.h269
-rw-r--r--drivers/target/iscsi/iscsi_target_seq_pdu_list.c664
-rw-r--r--drivers/target/iscsi/iscsi_target_seq_pdu_list.h86
-rw-r--r--drivers/target/iscsi/iscsi_target_stat.c951
-rw-r--r--drivers/target/iscsi/iscsi_target_stat.h64
-rw-r--r--drivers/target/iscsi/iscsi_target_tmr.c849
-rw-r--r--drivers/target/iscsi/iscsi_target_tmr.h14
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c759
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.h41
-rw-r--r--drivers/target/iscsi/iscsi_target_tq.c551
-rw-r--r--drivers/target/iscsi/iscsi_target_tq.h88
-rw-r--r--drivers/target/iscsi/iscsi_target_util.c1607
-rw-r--r--drivers/target/iscsi/iscsi_target_util.h61
-rw-r--r--drivers/target/loopback/Kconfig6
-rw-r--r--drivers/target/loopback/tcm_loop.c240
-rw-r--r--drivers/target/loopback/tcm_loop.h6
-rw-r--r--drivers/target/target_core_alua.c457
-rw-r--r--drivers/target/target_core_alua.h4
-rw-r--r--drivers/target/target_core_cdb.c688
-rw-r--r--drivers/target/target_core_cdb.h14
-rw-r--r--drivers/target/target_core_configfs.c683
-rw-r--r--drivers/target/target_core_device.c880
-rw-r--r--drivers/target/target_core_fabric_configfs.c125
-rw-r--r--drivers/target/target_core_fabric_lib.c36
-rw-r--r--drivers/target/target_core_file.c198
-rw-r--r--drivers/target/target_core_file.h8
-rw-r--r--drivers/target/target_core_hba.c38
-rw-r--r--drivers/target/target_core_iblock.c426
-rw-r--r--drivers/target/target_core_iblock.h11
-rw-r--r--drivers/target/target_core_pr.c1178
-rw-r--r--drivers/target/target_core_pr.h9
-rw-r--r--drivers/target/target_core_pscsi.c532
-rw-r--r--drivers/target/target_core_pscsi.h5
-rw-r--r--drivers/target/target_core_rd.c522
-rw-r--r--drivers/target/target_core_rd.h6
-rw-r--r--drivers/target/target_core_scdb.c105
-rw-r--r--drivers/target/target_core_scdb.h10
-rw-r--r--drivers/target/target_core_stat.c113
-rw-r--r--drivers/target/target_core_tmr.c362
-rw-r--r--drivers/target/target_core_tpg.c271
-rw-r--r--drivers/target/target_core_transport.c4663
-rw-r--r--drivers/target/target_core_ua.c63
-rw-r--r--drivers/target/tcm_fc/Makefile17
-rw-r--r--drivers/target/tcm_fc/tcm_fc.h44
-rw-r--r--drivers/target/tcm_fc/tfc_cmd.c221
-rw-r--r--drivers/target/tcm_fc/tfc_conf.c74
-rw-r--r--drivers/target/tcm_fc/tfc_io.c243
-rw-r--r--drivers/target/tcm_fc/tfc_sess.c21
-rw-r--r--drivers/thermal/Kconfig8
-rw-r--r--drivers/thermal/thermal_sys.c146
-rw-r--r--drivers/tty/Kconfig42
-rw-r--r--drivers/tty/Makefile1
-rw-r--r--drivers/tty/amiserial.c12
-rw-r--r--drivers/tty/bfin_jtag_comm.c2
-rw-r--r--drivers/tty/cyclades.c14
-rw-r--r--drivers/tty/ehv_bytechan.c881
-rw-r--r--drivers/tty/hvc/Kconfig14
-rw-r--r--drivers/tty/hvc/Makefile4
-rw-r--r--drivers/tty/hvc/hvc_console.c72
-rw-r--r--drivers/tty/hvc/hvc_console.h4
-rw-r--r--drivers/tty/hvc/hvc_dcc.c2
-rw-r--r--drivers/tty/hvc/hvc_irq.c2
-rw-r--r--drivers/tty/hvc/hvc_iseries.c1
-rw-r--r--drivers/tty/hvc/hvc_opal.c425
-rw-r--r--drivers/tty/hvc/hvc_vio.c409
-rw-r--r--drivers/tty/hvc/hvcs.c10
-rw-r--r--drivers/tty/hvc/hvsi.c131
-rw-r--r--drivers/tty/hvc/hvsi_lib.c426
-rw-r--r--drivers/tty/isicom.c2
-rw-r--r--drivers/tty/moxa.c5
-rw-r--r--drivers/tty/mxser.c17
-rw-r--r--drivers/tty/n_gsm.c483
-rw-r--r--drivers/tty/n_tty.c2
-rw-r--r--drivers/tty/pty.c43
-rw-r--r--drivers/tty/rocket.c2
-rw-r--r--drivers/tty/serial/68328serial.c37
-rw-r--r--drivers/tty/serial/68328serial.h1
-rw-r--r--drivers/tty/serial/8250.c167
-rw-r--r--drivers/tty/serial/8250_dw.c194
-rw-r--r--drivers/tty/serial/8250_pci.c238
-rw-r--r--drivers/tty/serial/8250_pnp.c3
-rw-r--r--drivers/tty/serial/Kconfig63
-rw-r--r--drivers/tty/serial/Makefile6
-rw-r--r--drivers/tty/serial/altera_jtaguart.c7
-rw-r--r--drivers/tty/serial/altera_uart.c6
-rw-r--r--drivers/tty/serial/amba-pl010.c14
-rw-r--r--drivers/tty/serial/amba-pl011.c14
-rw-r--r--drivers/tty/serial/apbuart.c1
-rw-r--r--drivers/tty/serial/atmel_serial.c130
-rw-r--r--drivers/tty/serial/bfin_sport_uart.c2
-rw-r--r--drivers/tty/serial/bfin_uart.c (renamed from drivers/tty/serial/bfin_5xx.c)70
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_core.c1
-rw-r--r--drivers/tty/serial/crisv10.c44
-rw-r--r--drivers/tty/serial/dz.c3
-rw-r--r--drivers/tty/serial/icom.c2
-rw-r--r--drivers/tty/serial/ifx6x60.c1
-rw-r--r--drivers/tty/serial/imx.c179
-rw-r--r--drivers/tty/serial/ioc3_serial.c1
-rw-r--r--drivers/tty/serial/ioc4_serial.c1
-rw-r--r--drivers/tty/serial/jsm/jsm.h10
-rw-r--r--drivers/tty/serial/jsm/jsm_driver.c21
-rw-r--r--drivers/tty/serial/jsm/jsm_neo.c29
-rw-r--r--drivers/tty/serial/jsm/jsm_tty.c94
-rw-r--r--drivers/tty/serial/kgdboc.c1
-rw-r--r--drivers/tty/serial/lantiq.c10
-rw-r--r--drivers/tty/serial/m32r_sio.c3
-rw-r--r--drivers/tty/serial/max3100.c3
-rw-r--r--drivers/tty/serial/max3107-aava.c3
-rw-r--r--drivers/tty/serial/max3107.c5
-rw-r--r--drivers/tty/serial/mcf.c2
-rw-r--r--drivers/tty/serial/mfd.c53
-rw-r--r--drivers/tty/serial/mpc52xx_uart.c3
-rw-r--r--drivers/tty/serial/mrst_max3110.c119
-rw-r--r--drivers/tty/serial/mrst_max3110.h1
-rw-r--r--drivers/tty/serial/msm_serial.c32
-rw-r--r--drivers/tty/serial/msm_serial_hs.c2
-rw-r--r--drivers/tty/serial/mux.c2
-rw-r--r--drivers/tty/serial/nwpserial.c2
-rw-r--r--drivers/tty/serial/of_serial.c55
-rw-r--r--drivers/tty/serial/omap-serial.c9
-rw-r--r--drivers/tty/serial/pch_uart.c33
-rw-r--r--drivers/tty/serial/pxa.c22
-rw-r--r--drivers/tty/serial/s3c2400.c105
-rw-r--r--drivers/tty/serial/s3c2410.c2
-rw-r--r--drivers/tty/serial/s3c2412.c2
-rw-r--r--drivers/tty/serial/s3c2440.c2
-rw-r--r--drivers/tty/serial/s3c24a0.c117
-rw-r--r--drivers/tty/serial/s3c6400.c2
-rw-r--r--drivers/tty/serial/s5pv210.c11
-rw-r--r--drivers/tty/serial/samsung.c145
-rw-r--r--drivers/tty/serial/samsung.h20
-rw-r--r--drivers/tty/serial/sb1250-duart.c3
-rw-r--r--drivers/tty/serial/serial_core.c77
-rw-r--r--drivers/tty/serial/serial_ks8695.c9
-rw-r--r--drivers/tty/serial/serial_txx9.c2
-rw-r--r--drivers/tty/serial/sh-sci.c873
-rw-r--r--drivers/tty/serial/sh-sci.h434
-rw-r--r--drivers/tty/serial/sn_console.c3
-rw-r--r--drivers/tty/serial/sunsu.c2
-rw-r--r--drivers/tty/serial/timbuart.c3
-rw-r--r--drivers/tty/serial/uartlite.c5
-rw-r--r--drivers/tty/serial/ucc_uart.c6
-rw-r--r--drivers/tty/serial/vt8500_serial.c3
-rw-r--r--drivers/tty/serial/xilinx_uartps.c5
-rw-r--r--drivers/tty/serial/zs.c3
-rw-r--r--drivers/tty/synclink.c1
-rw-r--r--drivers/tty/synclinkmp.c6
-rw-r--r--drivers/tty/tty_io.c57
-rw-r--r--drivers/tty/tty_ioctl.c17
-rw-r--r--drivers/tty/tty_ldisc.c31
-rw-r--r--drivers/tty/tty_mutex.c12
-rw-r--r--drivers/tty/tty_port.c2
-rw-r--r--drivers/tty/vt/keyboard.c3
-rw-r--r--drivers/tty/vt/selection.c4
-rw-r--r--drivers/tty/vt/vc_screen.c1
-rw-r--r--drivers/tty/vt/vt.c2
-rw-r--r--drivers/uio/uio.c14
-rw-r--r--drivers/uio/uio_pci_generic.c5
-rw-r--r--drivers/uio/uio_pdrv.c3
-rw-r--r--drivers/uio/uio_pdrv_genirq.c46
-rw-r--r--drivers/usb/Kconfig17
-rw-r--r--drivers/usb/Makefile4
-rw-r--r--drivers/usb/atm/ueagle-atm.c38
-rw-r--r--drivers/usb/atm/usbatm.c4
-rw-r--r--drivers/usb/c67x00/c67x00-drv.c1
-rw-r--r--drivers/usb/class/cdc-acm.c19
-rw-r--r--drivers/usb/class/cdc-wdm.c8
-rw-r--r--drivers/usb/class/usblp.c7
-rw-r--r--drivers/usb/class/usbtmc.c7
-rw-r--r--drivers/usb/core/config.c118
-rw-r--r--drivers/usb/core/devices.c4
-rw-r--r--drivers/usb/core/devio.c57
-rw-r--r--drivers/usb/core/driver.c31
-rw-r--r--drivers/usb/core/endpoint.c2
-rw-r--r--drivers/usb/core/hcd-pci.c2
-rw-r--r--drivers/usb/core/hcd.c55
-rw-r--r--drivers/usb/core/hub.c332
-rw-r--r--drivers/usb/core/message.c2
-rw-r--r--drivers/usb/core/notify.c1
-rw-r--r--drivers/usb/core/quirks.c51
-rw-r--r--drivers/usb/core/sysfs.c59
-rw-r--r--drivers/usb/core/urb.c2
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/core/usb.h7
-rw-r--r--drivers/usb/dwc3/Kconfig25
-rw-r--r--drivers/usb/dwc3/Makefile36
-rw-r--r--drivers/usb/dwc3/core.c484
-rw-r--r--drivers/usb/dwc3/core.h768
-rw-r--r--drivers/usb/dwc3/debug.h50
-rw-r--r--drivers/usb/dwc3/debugfs.c441
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c401
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c219
-rw-r--r--drivers/usb/dwc3/ep0.c804
-rw-r--r--drivers/usb/dwc3/gadget.c2105
-rw-r--r--drivers/usb/dwc3/gadget.h211
-rw-r--r--drivers/usb/dwc3/io.h54
-rw-r--r--drivers/usb/gadget/Kconfig363
-rw-r--r--drivers/usb/gadget/Makefile6
-rw-r--r--drivers/usb/gadget/acm_ms.c256
-rw-r--r--drivers/usb/gadget/amd5536udc.c38
-rw-r--r--drivers/usb/gadget/amd5536udc.h9
-rw-r--r--drivers/usb/gadget/at91_udc.c53
-rw-r--r--drivers/usb/gadget/at91_udc.h10
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c41
-rw-r--r--drivers/usb/gadget/audio.c1
-rw-r--r--drivers/usb/gadget/cdc2.c11
-rw-r--r--drivers/usb/gadget/ci13xxx_msm.c14
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.c85
-rw-r--r--drivers/usb/gadget/composite.c367
-rw-r--r--drivers/usb/gadget/config.c34
-rw-r--r--drivers/usb/gadget/dbgp.c12
-rw-r--r--drivers/usb/gadget/dummy_hcd.c1173
-rw-r--r--drivers/usb/gadget/epautoconf.c144
-rw-r--r--drivers/usb/gadget/ether.c10
-rw-r--r--drivers/usb/gadget/f_acm.c50
-rw-r--r--drivers/usb/gadget/f_audio.c22
-rw-r--r--drivers/usb/gadget/f_ecm.c161
-rw-r--r--drivers/usb/gadget/f_eem.c99
-rw-r--r--drivers/usb/gadget/f_fs.c13
-rw-r--r--drivers/usb/gadget/f_hid.c38
-rw-r--r--drivers/usb/gadget/f_loopback.c81
-rw-r--r--drivers/usb/gadget/f_mass_storage.c231
-rw-r--r--drivers/usb/gadget/f_midi.c990
-rw-r--r--drivers/usb/gadget/f_ncm.c67
-rw-r--r--drivers/usb/gadget/f_obex.c42
-rw-r--r--drivers/usb/gadget/f_phonet.c31
-rw-r--r--drivers/usb/gadget/f_rndis.c162
-rw-r--r--drivers/usb/gadget/f_serial.c32
-rw-r--r--drivers/usb/gadget/f_sourcesink.c81
-rw-r--r--drivers/usb/gadget/f_subset.c104
-rw-r--r--drivers/usb/gadget/f_uvc.c9
-rw-r--r--drivers/usb/gadget/f_uvc.h1
-rw-r--r--drivers/usb/gadget/file_storage.c274
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.c22
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c84
-rw-r--r--drivers/usb/gadget/fusb300_udc.c229
-rw-r--r--drivers/usb/gadget/fusb300_udc.h10
-rw-r--r--drivers/usb/gadget/g_ffs.c10
-rw-r--r--drivers/usb/gadget/gadget_chips.h181
-rw-r--r--drivers/usb/gadget/gmidi.c1292
-rw-r--r--drivers/usb/gadget/goku_udc.c19
-rw-r--r--drivers/usb/gadget/hid.c10
-rw-r--r--drivers/usb/gadget/imx_udc.c24
-rw-r--r--drivers/usb/gadget/imx_udc.h5
-rw-r--r--drivers/usb/gadget/inode.c25
-rw-r--r--drivers/usb/gadget/langwell_udc.c165
-rw-r--r--drivers/usb/gadget/langwell_udc.h12
-rw-r--r--drivers/usb/gadget/m66592-udc.c78
-rw-r--r--drivers/usb/gadget/m66592-udc.h50
-rw-r--r--drivers/usb/gadget/mass_storage.c10
-rw-r--r--drivers/usb/gadget/multi.c10
-rw-r--r--drivers/usb/gadget/mv_udc.h26
-rw-r--r--drivers/usb/gadget/mv_udc_core.c579
-rw-r--r--drivers/usb/gadget/mv_udc_phy.c214
-rw-r--r--drivers/usb/gadget/ncm.c10
-rw-r--r--drivers/usb/gadget/ndis.h6
-rw-r--r--drivers/usb/gadget/net2272.c2718
-rw-r--r--drivers/usb/gadget/net2272.h601
-rw-r--r--drivers/usb/gadget/net2280.c149
-rw-r--r--drivers/usb/gadget/net2280.h9
-rw-r--r--drivers/usb/gadget/nokia.c1
-rw-r--r--drivers/usb/gadget/omap_udc.c40
-rw-r--r--drivers/usb/gadget/pch_udc.c41
-rw-r--r--drivers/usb/gadget/printer.c70
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c49
-rw-r--r--drivers/usb/gadget/pxa25x_udc.h17
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c35
-rw-r--r--drivers/usb/gadget/pxa27x_udc.h13
-rw-r--r--drivers/usb/gadget/r8a66597-udc.c603
-rw-r--r--drivers/usb/gadget/r8a66597-udc.h75
-rw-r--r--drivers/usb/gadget/s3c-hsotg.c29
-rw-r--r--drivers/usb/gadget/s3c-hsudc.c86
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c77
-rw-r--r--drivers/usb/gadget/s3c2410_udc.h10
-rw-r--r--drivers/usb/gadget/serial.c1
-rw-r--r--drivers/usb/gadget/storage_common.c187
-rw-r--r--drivers/usb/gadget/u_ether.c33
-rw-r--r--drivers/usb/gadget/u_ether.h13
-rw-r--r--drivers/usb/gadget/u_serial.c8
-rw-r--r--drivers/usb/gadget/u_serial.h2
-rw-r--r--drivers/usb/gadget/udc-core.c469
-rw-r--r--drivers/usb/gadget/uvc.h2
-rw-r--r--drivers/usb/gadget/uvc_queue.c3
-rw-r--r--drivers/usb/gadget/uvc_v4l2.c24
-rw-r--r--drivers/usb/gadget/uvc_video.c1
-rw-r--r--drivers/usb/gadget/webcam.c3
-rw-r--r--drivers/usb/gadget/zero.c10
-rw-r--r--drivers/usb/host/Kconfig28
-rw-r--r--drivers/usb/host/Makefile4
-rw-r--r--drivers/usb/host/alchemy-common.c337
-rw-r--r--drivers/usb/host/ehci-ath79.c4
-rw-r--r--drivers/usb/host/ehci-au1xxx.c81
-rw-r--r--drivers/usb/host/ehci-cns3xxx.c2
-rw-r--r--drivers/usb/host/ehci-dbg.c19
-rw-r--r--drivers/usb/host/ehci-fsl.c8
-rw-r--r--drivers/usb/host/ehci-grlib.c2
-rw-r--r--drivers/usb/host/ehci-hcd.c85
-rw-r--r--drivers/usb/host/ehci-hub.c107
-rw-r--r--drivers/usb/host/ehci-ixp4xx.c2
-rw-r--r--drivers/usb/host/ehci-msm.c20
-rw-r--r--drivers/usb/host/ehci-mxc.c3
-rw-r--r--drivers/usb/host/ehci-octeon.c4
-rw-r--r--drivers/usb/host/ehci-omap.c18
-rw-r--r--drivers/usb/host/ehci-orion.c2
-rw-r--r--drivers/usb/host/ehci-pci.c7
-rw-r--r--drivers/usb/host/ehci-pmcmsp.c10
-rw-r--r--drivers/usb/host/ehci-ppc-of.c2
-rw-r--r--drivers/usb/host/ehci-ps3.c2
-rw-r--r--drivers/usb/host/ehci-pxa168.c363
-rw-r--r--drivers/usb/host/ehci-q.c116
-rw-r--r--drivers/usb/host/ehci-s5p.c98
-rw-r--r--drivers/usb/host/ehci-sched.c73
-rw-r--r--drivers/usb/host/ehci-sh.c2
-rw-r--r--drivers/usb/host/ehci-spear.c2
-rw-r--r--drivers/usb/host/ehci-sysfs.c190
-rw-r--r--drivers/usb/host/ehci-tegra.c2
-rw-r--r--drivers/usb/host/ehci-vt8500.c2
-rw-r--r--drivers/usb/host/ehci-w90x900.c2
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c2
-rw-r--r--drivers/usb/host/ehci-xls.c161
-rw-r--r--drivers/usb/host/ehci.h30
-rw-r--r--drivers/usb/host/fhci-hcd.c11
-rw-r--r--drivers/usb/host/fhci-sched.c19
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c1
-rw-r--r--drivers/usb/host/imx21-hcd.c2
-rw-r--r--drivers/usb/host/isp116x-hcd.c2
-rw-r--r--drivers/usb/host/isp1362-hcd.c8
-rw-r--r--drivers/usb/host/isp1760-hcd.c421
-rw-r--r--drivers/usb/host/isp1760-hcd.h5
-rw-r--r--drivers/usb/host/isp1760-if.c81
-rw-r--r--drivers/usb/host/ohci-at91.c245
-rw-r--r--drivers/usb/host/ohci-ath79.c6
-rw-r--r--drivers/usb/host/ohci-au1xxx.c112
-rw-r--r--drivers/usb/host/ohci-cns3xxx.c2
-rw-r--r--drivers/usb/host/ohci-da8xx.c4
-rw-r--r--drivers/usb/host/ohci-ep93xx.c2
-rw-r--r--drivers/usb/host/ohci-hcd.c20
-rw-r--r--drivers/usb/host/ohci-hub.c5
-rw-r--r--drivers/usb/host/ohci-octeon.c4
-rw-r--r--drivers/usb/host/ohci-omap.c4
-rw-r--r--drivers/usb/host/ohci-omap3.c4
-rw-r--r--drivers/usb/host/ohci-pci.c26
-rw-r--r--drivers/usb/host/ohci-pnx4008.c4
-rw-r--r--drivers/usb/host/ohci-pnx8550.c2
-rw-r--r--drivers/usb/host/ohci-ppc-of.c4
-rw-r--r--drivers/usb/host/ohci-ppc-soc.c4
-rw-r--r--drivers/usb/host/ohci-ps3.c2
-rw-r--r--drivers/usb/host/ohci-pxa27x.c2
-rw-r--r--drivers/usb/host/ohci-q.c4
-rw-r--r--drivers/usb/host/ohci-s3c2410.c2
-rw-r--r--drivers/usb/host/ohci-sa1111.c4
-rw-r--r--drivers/usb/host/ohci-sh.c4
-rw-r--r--drivers/usb/host/ohci-sm501.c13
-rw-r--r--drivers/usb/host/ohci-spear.c2
-rw-r--r--drivers/usb/host/ohci-ssb.c4
-rw-r--r--drivers/usb/host/ohci-tmio.c8
-rw-r--r--drivers/usb/host/ohci-xls.c151
-rw-r--r--drivers/usb/host/ohci.h1
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c2
-rw-r--r--drivers/usb/host/pci-quirks.c74
-rw-r--r--drivers/usb/host/r8a66597-hcd.c10
-rw-r--r--drivers/usb/host/r8a66597.h38
-rw-r--r--drivers/usb/host/sl811-hcd.c2
-rw-r--r--drivers/usb/host/uhci-grlib.c2
-rw-r--r--drivers/usb/host/uhci-hcd.c66
-rw-r--r--drivers/usb/host/uhci-q.c6
-rw-r--r--drivers/usb/host/whci/debug.c1
-rw-r--r--drivers/usb/host/whci/hcd.c1
-rw-r--r--drivers/usb/host/whci/init.c2
-rw-r--r--drivers/usb/host/xhci-dbg.c22
-rw-r--r--drivers/usb/host/xhci-ext-caps.h6
-rw-r--r--drivers/usb/host/xhci-hub.c159
-rw-r--r--drivers/usb/host/xhci-mem.c402
-rw-r--r--drivers/usb/host/xhci-pci.c108
-rw-r--r--drivers/usb/host/xhci-ring.c244
-rw-r--r--drivers/usb/host/xhci.c1242
-rw-r--r--drivers/usb/host/xhci.h194
-rw-r--r--drivers/usb/image/microtek.c2
-rw-r--r--drivers/usb/misc/adutux.c14
-rw-r--r--drivers/usb/misc/appledisplay.c2
-rw-r--r--drivers/usb/misc/ftdi-elan.c30
-rw-r--r--drivers/usb/misc/idmouse.c2
-rw-r--r--drivers/usb/misc/iowarrior.c2
-rw-r--r--drivers/usb/misc/ldusb.c4
-rw-r--r--drivers/usb/misc/legousbtower.c4
-rw-r--r--drivers/usb/misc/usblcd.c129
-rw-r--r--drivers/usb/misc/usbled.c25
-rw-r--r--drivers/usb/misc/usbtest.c31
-rw-r--r--drivers/usb/mon/mon_bin.c3
-rw-r--r--drivers/usb/mon/mon_stat.c1
-rw-r--r--drivers/usb/mon/mon_text.c10
-rw-r--r--drivers/usb/musb/Kconfig81
-rw-r--r--drivers/usb/musb/Makefile4
-rw-r--r--drivers/usb/musb/am35x.c5
-rw-r--r--drivers/usb/musb/blackfin.c1
-rw-r--r--drivers/usb/musb/blackfin.h2
-rw-r--r--drivers/usb/musb/cppi_dma.c26
-rw-r--r--drivers/usb/musb/da8xx.c13
-rw-r--r--drivers/usb/musb/davinci.c7
-rw-r--r--drivers/usb/musb/musb_core.c138
-rw-r--r--drivers/usb/musb/musb_core.h82
-rw-r--r--drivers/usb/musb/musb_debugfs.c6
-rw-r--r--drivers/usb/musb/musb_gadget.c188
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c12
-rw-r--r--drivers/usb/musb/musb_host.c2
-rw-r--r--drivers/usb/musb/musb_host.h4
-rw-r--r--drivers/usb/musb/musb_regs.h6
-rw-r--r--drivers/usb/musb/musb_virthub.c6
-rw-r--r--drivers/usb/musb/musbhsdma.c2
-rw-r--r--drivers/usb/musb/omap2430.c15
-rw-r--r--drivers/usb/musb/tusb6010.c27
-rw-r--r--drivers/usb/musb/tusb6010_omap.c3
-rw-r--r--drivers/usb/musb/ux500_dma.c38
-rw-r--r--drivers/usb/otg/gpio_vbus.c1
-rw-r--r--drivers/usb/otg/isp1301_omap.c6
-rw-r--r--drivers/usb/otg/otg.c1
-rw-r--r--drivers/usb/otg/otg_fsm.c1
-rw-r--r--drivers/usb/otg/twl4030-usb.c2
-rw-r--r--drivers/usb/otg/twl6030-usb.c46
-rw-r--r--drivers/usb/otg/ulpi.c1
-rw-r--r--drivers/usb/renesas_usbhs/Kconfig15
-rw-r--r--drivers/usb/renesas_usbhs/Makefile10
-rw-r--r--drivers/usb/renesas_usbhs/common.c268
-rw-r--r--drivers/usb/renesas_usbhs/common.h104
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c1190
-rw-r--r--drivers/usb/renesas_usbhs/fifo.h102
-rw-r--r--drivers/usb/renesas_usbhs/mod.c81
-rw-r--r--drivers/usb/renesas_usbhs/mod.h61
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c879
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c1327
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c472
-rw-r--r--drivers/usb/renesas_usbhs/pipe.h70
-rw-r--r--drivers/usb/serial/Kconfig1
-rw-r--r--drivers/usb/serial/aircable.c1
-rw-r--r--drivers/usb/serial/ark3116.c10
-rw-r--r--drivers/usb/serial/digi_acceleport.c2
-rw-r--r--drivers/usb/serial/ftdi_sio.c133
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h13
-rw-r--r--drivers/usb/serial/garmin_gps.c2
-rw-r--r--drivers/usb/serial/io_edgeport.c4
-rw-r--r--drivers/usb/serial/ipw.c33
-rw-r--r--drivers/usb/serial/mos7720.c1
-rw-r--r--drivers/usb/serial/mos7840.c1
-rw-r--r--drivers/usb/serial/opticon.c2
-rw-r--r--drivers/usb/serial/option.c302
-rw-r--r--drivers/usb/serial/pl2303.c24
-rw-r--r--drivers/usb/serial/pl2303.h5
-rw-r--r--drivers/usb/serial/qcserial.c5
-rw-r--r--drivers/usb/serial/sierra.c2
-rw-r--r--drivers/usb/serial/symbolserial.c2
-rw-r--r--drivers/usb/serial/usb-serial.c8
-rw-r--r--drivers/usb/serial/usb_wwan.c2
-rw-r--r--drivers/usb/storage/Kconfig8
-rw-r--r--drivers/usb/storage/ene_ub6250.c1638
-rw-r--r--drivers/usb/storage/option_ms.c1
-rw-r--r--drivers/usb/storage/protocol.c8
-rw-r--r--drivers/usb/storage/realtek_cr.c607
-rw-r--r--drivers/usb/storage/sierra_ms.c1
-rw-r--r--drivers/usb/storage/transport.c35
-rw-r--r--drivers/usb/storage/uas.c1
-rw-r--r--drivers/usb/storage/unusual_devs.h10
-rw-r--r--drivers/usb/storage/usb.c16
-rw-r--r--drivers/usb/usb-common.c35
-rw-r--r--drivers/usb/usb-skeleton.c2
-rw-r--r--drivers/usb/wusbcore/cbaf.c4
-rw-r--r--drivers/usb/wusbcore/devconnect.c1
-rw-r--r--drivers/usb/wusbcore/mmc.c1
-rw-r--r--drivers/usb/wusbcore/rh.c1
-rw-r--r--drivers/usb/wusbcore/security.c1
-rw-r--r--drivers/usb/wusbcore/wa-hc.c3
-rw-r--r--drivers/usb/wusbcore/wa-rpipe.c3
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c8
-rw-r--r--drivers/uwb/est.c1
-rw-r--r--drivers/uwb/i1480/dfu/dfu.c1
-rw-r--r--drivers/uwb/ie.c1
-rw-r--r--drivers/uwb/lc-dev.c2
-rw-r--r--drivers/uwb/lc-rc.c1
-rw-r--r--drivers/uwb/neh.c1
-rw-r--r--drivers/uwb/pal.c1
-rw-r--r--drivers/uwb/radio.c1
-rw-r--r--drivers/uwb/reset.c1
-rw-r--r--drivers/uwb/rsv.c1
-rw-r--r--drivers/uwb/scan.c1
-rw-r--r--drivers/uwb/umc-bus.c1
-rw-r--r--drivers/uwb/umc-dev.c1
-rw-r--r--drivers/uwb/umc-drv.c1
-rw-r--r--drivers/uwb/uwb-internal.h1
-rw-r--r--drivers/uwb/uwbd.c2
-rw-r--r--drivers/uwb/whc-rc.c2
-rw-r--r--drivers/uwb/whci.c1
-rw-r--r--drivers/vhost/net.c91
-rw-r--r--drivers/vhost/test.c5
-rw-r--r--drivers/vhost/vhost.c213
-rw-r--r--drivers/vhost/vhost.h34
-rw-r--r--drivers/video/68328fb.c4
-rw-r--r--drivers/video/Kconfig38
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/acornfb.c5
-rw-r--r--drivers/video/amba-clcd.c9
-rw-r--r--drivers/video/arkfb.c9
-rw-r--r--drivers/video/atmel_lcdfb.c22
-rw-r--r--drivers/video/aty/atyfb_base.c7
-rw-r--r--drivers/video/aty/radeon_base.c10
-rw-r--r--drivers/video/au1100fb.c183
-rw-r--r--drivers/video/au1100fb.h6
-rw-r--r--drivers/video/au1200fb.c299
-rw-r--r--drivers/video/backlight/88pm860x_bl.c1
-rw-r--r--drivers/video/backlight/Kconfig15
-rw-r--r--drivers/video/backlight/Makefile2
-rw-r--r--drivers/video/backlight/aat2870_bl.c246
-rw-r--r--drivers/video/backlight/adp5520_bl.c1
-rw-r--r--drivers/video/backlight/adp8860_bl.c4
-rw-r--r--drivers/video/backlight/adp8870_bl.c5
-rw-r--r--drivers/video/backlight/ams369fg06.c647
-rw-r--r--drivers/video/backlight/backlight.c2
-rw-r--r--drivers/video/backlight/da903x_bl.c1
-rw-r--r--drivers/video/backlight/ep93xx_bl.c3
-rw-r--r--drivers/video/backlight/generic_bl.c4
-rw-r--r--drivers/video/backlight/l4f00242t03.c71
-rw-r--r--drivers/video/backlight/ld9040.c18
-rw-r--r--drivers/video/backlight/lms283gf05.c1
-rw-r--r--drivers/video/backlight/max8925_bl.c1
-rw-r--r--drivers/video/backlight/pwm_bl.c9
-rw-r--r--drivers/video/backlight/s6e63m0.c12
-rw-r--r--drivers/video/backlight/wm831x_bl.c1
-rw-r--r--drivers/video/bf54x-lq043fb.c2
-rw-r--r--drivers/video/bfin-lq035q1-fb.c2
-rw-r--r--drivers/video/bfin-t350mcqb-fb.c2
-rw-r--r--drivers/video/bfin_adv7393fb.c2
-rw-r--r--drivers/video/carminefb.c7
-rw-r--r--drivers/video/cobalt_lcdfb.c3
-rw-r--r--drivers/video/console/vgacon.c42
-rw-r--r--drivers/video/controlfb.c6
-rw-r--r--drivers/video/da8xx-fb.c199
-rw-r--r--drivers/video/ep93xx-fb.c5
-rw-r--r--drivers/video/fb-puv3.c4
-rw-r--r--drivers/video/fb_ddc.c3
-rw-r--r--drivers/video/fb_defio.c14
-rw-r--r--drivers/video/fb_notify.c1
-rw-r--r--drivers/video/fbmem.c3
-rw-r--r--drivers/video/fbmon.c21
-rw-r--r--drivers/video/fbsysfs.c3
-rw-r--r--drivers/video/fsl-diu-fb.c992
-rw-r--r--drivers/video/g364fb.c5
-rw-r--r--drivers/video/grvga.c579
-rw-r--r--drivers/video/gxt4500.c4
-rw-r--r--drivers/video/hgafb.c4
-rw-r--r--drivers/video/i810/i810.h2
-rw-r--r--drivers/video/igafb.c2
-rw-r--r--drivers/video/imsttfb.c2
-rw-r--r--drivers/video/intelfb/intelfbhw.c6
-rw-r--r--drivers/video/mb862xx/mb862xx-i2c.c3
-rw-r--r--drivers/video/mb862xx/mb862xxfbdrv.c11
-rw-r--r--drivers/video/mbx/mbxfb.c6
-rw-r--r--drivers/video/modedb.c444
-rw-r--r--drivers/video/msm/mddi.c2
-rw-r--r--drivers/video/msm/mdp.c10
-rw-r--r--drivers/video/msm/msm_fb.c7
-rw-r--r--drivers/video/mx3fb.c19
-rw-r--r--drivers/video/mxsfb.c1
-rw-r--r--drivers/video/neofb.c4
-rw-r--r--drivers/video/nuc900fb.c5
-rw-r--r--drivers/video/omap/Kconfig29
-rw-r--r--drivers/video/omap/Makefile8
-rw-r--r--drivers/video/omap/dispc.c1
-rw-r--r--drivers/video/omap/lcd_2430sdp.c203
-rw-r--r--drivers/video/omap/lcd_apollon.c136
-rw-r--r--drivers/video/omap/lcd_h3.c2
-rw-r--r--drivers/video/omap/lcd_h4.c117
-rw-r--r--drivers/video/omap/lcd_inn1610.c2
-rw-r--r--drivers/video/omap/lcd_ldp.c201
-rw-r--r--drivers/video/omap/lcd_mipid.c1
-rw-r--r--drivers/video/omap/lcd_omap3beagle.c130
-rw-r--r--drivers/video/omap/lcd_omap3evm.c193
-rw-r--r--drivers/video/omap/lcd_osk.c2
-rw-r--r--drivers/video/omap/lcd_overo.c180
-rw-r--r--drivers/video/omap/lcd_palmtt.c2
-rw-r--r--drivers/video/omap/omapfb_main.c1
-rw-r--r--drivers/video/omap2/displays/Kconfig28
-rw-r--r--drivers/video/omap2/displays/Makefile3
-rw-r--r--drivers/video/omap2/displays/panel-dvi.c363
-rw-r--r--drivers/video/omap2/displays/panel-generic-dpi.c113
-rw-r--r--drivers/video/omap2/displays/panel-n8x0.c747
-rw-r--r--drivers/video/omap2/displays/panel-picodlp.c594
-rw-r--r--drivers/video/omap2/displays/panel-picodlp.h288
-rw-r--r--drivers/video/omap2/displays/panel-taal.c180
-rw-r--r--drivers/video/omap2/dss/Kconfig14
-rw-r--r--drivers/video/omap2/dss/Makefile2
-rw-r--r--drivers/video/omap2/dss/core.c25
-rw-r--r--drivers/video/omap2/dss/dispc.c2054
-rw-r--r--drivers/video/omap2/dss/dispc.h57
-rw-r--r--drivers/video/omap2/dss/display.c88
-rw-r--r--drivers/video/omap2/dss/dpi.c102
-rw-r--r--drivers/video/omap2/dss/dsi.c1200
-rw-r--r--drivers/video/omap2/dss/dss.c592
-rw-r--r--drivers/video/omap2/dss/dss.h196
-rw-r--r--drivers/video/omap2/dss/dss_features.c158
-rw-r--r--drivers/video/omap2/dss/dss_features.h24
-rw-r--r--drivers/video/omap2/dss/hdmi.c1376
-rw-r--r--drivers/video/omap2/dss/hdmi_panel.c (renamed from drivers/video/omap2/dss/hdmi_omap4_panel.c)68
-rw-r--r--drivers/video/omap2/dss/manager.c498
-rw-r--r--drivers/video/omap2/dss/overlay.c147
-rw-r--r--drivers/video/omap2/dss/rfbi.c152
-rw-r--r--drivers/video/omap2/dss/sdi.c60
-rw-r--r--drivers/video/omap2/dss/ti_hdmi.h138
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c1239
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h (renamed from drivers/video/omap2/dss/hdmi.h)400
-rw-r--r--drivers/video/omap2/dss/venc.c174
-rw-r--r--drivers/video/omap2/omapfb/Kconfig2
-rw-r--r--drivers/video/omap2/omapfb/omapfb-ioctl.c73
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c300
-rw-r--r--drivers/video/omap2/omapfb/omapfb-sysfs.c40
-rw-r--r--drivers/video/omap2/omapfb/omapfb.h37
-rw-r--r--drivers/video/platinumfb.c9
-rw-r--r--drivers/video/pm2fb.c6
-rw-r--r--drivers/video/pm3fb.c6
-rw-r--r--drivers/video/pnx4008/sdum.c2
-rw-r--r--drivers/video/ps3fb.c2
-rw-r--r--drivers/video/pxa168fb.c2
-rw-r--r--drivers/video/pxa3xx-gcu.c4
-rw-r--r--drivers/video/pxafb.c12
-rw-r--r--drivers/video/s3c-fb.c117
-rw-r--r--drivers/video/s3c2410fb.c3
-rw-r--r--drivers/video/s3fb.c9
-rw-r--r--drivers/video/sa1100fb.c3
-rw-r--r--drivers/video/savage/savagefb.h2
-rw-r--r--drivers/video/savage/savagefb_accel.c1
-rw-r--r--drivers/video/savage/savagefb_driver.c16
-rw-r--r--drivers/video/sh_mipi_dsi.c1
-rw-r--r--drivers/video/sh_mobile_hdmi.c47
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c1165
-rw-r--r--drivers/video/sh_mobile_lcdcfb.h12
-rw-r--r--drivers/video/sh_mobile_meram.c208
-rw-r--r--drivers/video/sh_mobile_meram.h41
-rw-r--r--drivers/video/sis/sis_main.c30
-rw-r--r--drivers/video/skeletonfb.c2
-rw-r--r--drivers/video/sm501fb.c6
-rw-r--r--drivers/video/smscufx.c1994
-rw-r--r--drivers/video/tmiofb.c2
-rw-r--r--drivers/video/tridentfb.c4
-rw-r--r--drivers/video/udlfb.c188
-rw-r--r--drivers/video/valkyriefb.c2
-rw-r--r--drivers/video/vermilion/vermilion.h2
-rw-r--r--drivers/video/vfb.c4
-rw-r--r--drivers/video/vga16fb.c2
-rw-r--r--drivers/video/via/dvi.c34
-rw-r--r--drivers/video/via/dvi.h3
-rw-r--r--drivers/video/via/global.c2
-rw-r--r--drivers/video/via/global.h2
-rw-r--r--drivers/video/via/hw.c544
-rw-r--r--drivers/video/via/hw.h285
-rw-r--r--drivers/video/via/lcd.c53
-rw-r--r--drivers/video/via/lcd.h7
-rw-r--r--drivers/video/via/share.h27
-rw-r--r--drivers/video/via/via-core.c2
-rw-r--r--drivers/video/via/via-gpio.c1
-rw-r--r--drivers/video/via/via_modesetting.c104
-rw-r--r--drivers/video/via/via_modesetting.h23
-rw-r--r--drivers/video/via/viafbdev.c186
-rw-r--r--drivers/video/via/viamode.c60
-rw-r--r--drivers/video/via/viamode.h4
-rw-r--r--drivers/video/vt8500lcdfb.c6
-rw-r--r--drivers/video/vt8623fb.c9
-rw-r--r--drivers/video/w100fb.c1
-rw-r--r--drivers/video/xilinxfb.c1
-rw-r--r--drivers/virt/Kconfig32
-rw-r--r--drivers/virt/Makefile5
-rw-r--r--drivers/virt/fsl_hypervisor.c939
-rw-r--r--drivers/virtio/Kconfig14
-rw-r--r--drivers/virtio/Makefile1
-rw-r--r--drivers/virtio/virtio.c1
-rw-r--r--drivers/virtio/virtio_balloon.c1
-rw-r--r--drivers/virtio/virtio_mmio.c479
-rw-r--r--drivers/virtio/virtio_pci.c39
-rw-r--r--drivers/virtio/virtio_ring.c11
-rw-r--r--drivers/w1/masters/ds2490.c4
-rw-r--r--drivers/w1/masters/matrox_w1.c6
-rw-r--r--drivers/w1/slaves/w1_ds2408.c2
-rw-r--r--drivers/w1/slaves/w1_ds2760.c48
-rw-r--r--drivers/w1/slaves/w1_ds2780.c96
-rw-r--r--drivers/w1/slaves/w1_ds2780.h2
-rw-r--r--drivers/w1/slaves/w1_smem.c4
-rw-r--r--drivers/w1/slaves/w1_therm.c13
-rw-r--r--drivers/w1/w1.c6
-rw-r--r--drivers/w1/w1.h2
-rw-r--r--drivers/w1/w1_family.c3
-rw-r--r--drivers/w1/w1_family.h5
-rw-r--r--drivers/w1/w1_int.c5
-rw-r--r--drivers/w1/w1_int.h2
-rw-r--r--drivers/w1/w1_io.c7
-rw-r--r--drivers/w1/w1_log.h2
-rw-r--r--drivers/w1/w1_netlink.c2
-rw-r--r--drivers/w1/w1_netlink.h2
-rw-r--r--drivers/watchdog/Kconfig51
-rw-r--r--drivers/watchdog/Makefile9
-rw-r--r--drivers/watchdog/adx_wdt.c355
-rw-r--r--drivers/watchdog/at91sam9_wdt.c21
-rw-r--r--drivers/watchdog/at91sam9_wdt.h37
-rw-r--r--drivers/watchdog/bcm47xx_wdt.c27
-rw-r--r--drivers/watchdog/coh901327_wdt.c2
-rw-r--r--drivers/watchdog/dw_wdt.c376
-rw-r--r--drivers/watchdog/eurotechwdt.c2
-rw-r--r--drivers/watchdog/hpwdt.c130
-rw-r--r--drivers/watchdog/iTCO_wdt.c431
-rw-r--r--drivers/watchdog/imx2_wdt.c6
-rw-r--r--drivers/watchdog/intel_scu_watchdog.c2
-rw-r--r--drivers/watchdog/it8712f_wdt.c63
-rw-r--r--drivers/watchdog/it87_wdt.c168
-rw-r--r--drivers/watchdog/lantiq_wdt.c8
-rw-r--r--drivers/watchdog/mpcore_wdt.c26
-rw-r--r--drivers/watchdog/mtx-1_wdt.c4
-rw-r--r--drivers/watchdog/nv_tco.c8
-rw-r--r--drivers/watchdog/octeon-wdt-main.c2
-rw-r--r--drivers/watchdog/of_xilinx_wdt.c433
-rw-r--r--drivers/watchdog/pc87413_wdt.c96
-rw-r--r--drivers/watchdog/s3c2410_wdt.c190
-rw-r--r--drivers/watchdog/sb_wdog.c4
-rw-r--r--drivers/watchdog/sbc7240_wdt.c2
-rw-r--r--drivers/watchdog/sbc_epx_c3.c2
-rw-r--r--drivers/watchdog/sc520_wdt.c2
-rw-r--r--drivers/watchdog/sch311x_wdt.c5
-rw-r--r--drivers/watchdog/shwdt.c2
-rw-r--r--drivers/watchdog/smsc37b787_wdt.c2
-rw-r--r--drivers/watchdog/sp805_wdt.c5
-rw-r--r--drivers/watchdog/stmp3xxx_wdt.c1
-rw-r--r--drivers/watchdog/w83627hf_wdt.c33
-rw-r--r--drivers/watchdog/watchdog_core.c111
-rw-r--r--drivers/watchdog/watchdog_dev.c395
-rw-r--r--drivers/watchdog/watchdog_dev.h33
-rw-r--r--drivers/watchdog/wdt.c2
-rw-r--r--drivers/watchdog/wdt_pci.c2
-rw-r--r--drivers/watchdog/wm831x_wdt.c318
-rw-r--r--drivers/xen/Kconfig86
-rw-r--r--drivers/xen/Makefile8
-rw-r--r--drivers/xen/balloon.c215
-rw-r--r--drivers/xen/events.c105
-rw-r--r--drivers/xen/gntalloc.c4
-rw-r--r--drivers/xen/gntdev.c50
-rw-r--r--drivers/xen/grant-table.c10
-rw-r--r--drivers/xen/manage.c1
-rw-r--r--drivers/xen/pci.c107
-rw-r--r--drivers/xen/swiotlb-xen.c71
-rw-r--r--drivers/xen/tmem.c170
-rw-r--r--drivers/xen/xen-balloon.c17
-rw-r--r--drivers/xen/xen-pciback/Makefile7
-rw-r--r--drivers/xen/xen-pciback/conf_space.c438
-rw-r--r--drivers/xen/xen-pciback/conf_space.h126
-rw-r--r--drivers/xen/xen-pciback/conf_space_capability.c207
-rw-r--r--drivers/xen/xen-pciback/conf_space_header.c385
-rw-r--r--drivers/xen/xen-pciback/conf_space_quirks.c139
-rw-r--r--drivers/xen/xen-pciback/conf_space_quirks.h33
-rw-r--r--drivers/xen/xen-pciback/passthrough.c188
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c1371
-rw-r--r--drivers/xen/xen-pciback/pciback.h191
-rw-r--r--drivers/xen/xen-pciback/pciback_ops.c383
-rw-r--r--drivers/xen/xen-pciback/vpci.c254
-rw-r--r--drivers/xen/xen-pciback/xenbus.c745
-rw-r--r--drivers/xen/xen-selfballoon.c545
-rw-r--r--drivers/xen/xenbus/xenbus_client.c16
-rw-r--r--drivers/xen/xenbus/xenbus_comms.c4
-rw-r--r--drivers/xen/xenbus/xenbus_probe.c151
-rw-r--r--drivers/xen/xenbus/xenbus_probe.h5
-rw-r--r--drivers/xen/xenbus/xenbus_probe_backend.c12
-rw-r--r--drivers/xen/xenbus/xenbus_probe_frontend.c128
-rw-r--r--drivers/xen/xenbus/xenbus_xs.c17
-rw-r--r--drivers/zorro/proc.c1
-rw-r--r--drivers/zorro/zorro-driver.c8
-rw-r--r--drivers/zorro/zorro.c7
7011 files changed, 712719 insertions, 538033 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 3bb154d8c8cc..b5e6f243f749 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -56,6 +56,8 @@ source "drivers/pps/Kconfig"
source "drivers/ptp/Kconfig"
+source "drivers/pinctrl/Kconfig"
+
source "drivers/gpio/Kconfig"
source "drivers/w1/Kconfig"
@@ -94,8 +96,6 @@ source "drivers/memstick/Kconfig"
source "drivers/leds/Kconfig"
-source "drivers/nfc/Kconfig"
-
source "drivers/accessibility/Kconfig"
source "drivers/infiniband/Kconfig"
@@ -114,6 +114,8 @@ source "drivers/uio/Kconfig"
source "drivers/vlynq/Kconfig"
+source "drivers/virtio/Kconfig"
+
source "drivers/xen/Kconfig"
source "drivers/staging/Kconfig"
@@ -126,4 +128,12 @@ source "drivers/hwspinlock/Kconfig"
source "drivers/clocksource/Kconfig"
+source "drivers/iommu/Kconfig"
+
+source "drivers/virt/Kconfig"
+
+source "drivers/hv/Kconfig"
+
+source "drivers/devfreq/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 09f3232bcdcd..1b3142127bf5 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -5,6 +5,8 @@
# Rewritten to use lists instead of if-statements.
#
+# GPIO must come after pinctrl as gpios may need to mux pins etc
+obj-y += pinctrl/
obj-y += gpio/
obj-$(CONFIG_PCI) += pci/
obj-$(CONFIG_PARISC) += parisc/
@@ -122,3 +124,11 @@ obj-y += ieee802154/
obj-y += clk/
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
+obj-$(CONFIG_NFC) += nfc/
+obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
+
+# Virtualization drivers
+obj-$(CONFIG_VIRT_DRIVERS) += virt/
+obj-$(CONFIG_HYPERV) += hv/
+
+obj-$(CONFIG_PM_DEVFREQ) += devfreq/
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c
index 58c3f74bd84c..6512b20aeccd 100644
--- a/drivers/acpi/ac.c
+++ b/drivers/acpi/ac.c
@@ -89,7 +89,7 @@ struct acpi_ac {
unsigned long long state;
};
-#define to_acpi_ac(x) container_of(x, struct acpi_ac, charger);
+#define to_acpi_ac(x) container_of(x, struct acpi_ac, charger)
#ifdef CONFIG_ACPI_PROCFS_POWER
static const struct file_operations acpi_ac_fops = {
diff --git a/drivers/acpi/acpica/acconfig.h b/drivers/acpi/acpica/acconfig.h
index bc533dde16c4..f895a244ca7e 100644
--- a/drivers/acpi/acpica/acconfig.h
+++ b/drivers/acpi/acpica/acconfig.h
@@ -121,7 +121,7 @@
/* Maximum sleep allowed via Sleep() operator */
-#define ACPI_MAX_SLEEP 20000 /* Two seconds */
+#define ACPI_MAX_SLEEP 2000 /* Two seconds */
/******************************************************************************
*
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index 73863d86f022..76dc02f15574 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -126,6 +126,12 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_copy_dsdt_locally, FALSE);
*/
u8 ACPI_INIT_GLOBAL(acpi_gbl_truncate_io_addresses, FALSE);
+/*
+ * Disable runtime checking and repair of values returned by control methods.
+ * Use only if the repair is causing a problem on a particular machine.
+ */
+u8 ACPI_INIT_GLOBAL(acpi_gbl_disable_auto_repair, FALSE);
+
/* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */
struct acpi_table_fadt acpi_gbl_FADT;
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index c7f743ca395b..5552125d8340 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -357,6 +357,7 @@ struct acpi_predefined_data {
char *pathname;
const union acpi_predefined_info *predefined;
union acpi_operand_object *parent_package;
+ struct acpi_namespace_node *node;
u32 flags;
u8 node_flags;
};
diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h
index 94e73c97cf85..c445cca490ea 100644
--- a/drivers/acpi/acpica/acpredef.h
+++ b/drivers/acpi/acpica/acpredef.h
@@ -468,6 +468,7 @@ static const union acpi_predefined_info predefined_names[] =
{{"_SWS", 0, ACPI_RTYPE_INTEGER}},
{{"_TC1", 0, ACPI_RTYPE_INTEGER}},
{{"_TC2", 0, ACPI_RTYPE_INTEGER}},
+ {{"_TDL", 0, ACPI_RTYPE_INTEGER}},
{{"_TIP", 1, ACPI_RTYPE_INTEGER}},
{{"_TIV", 1, ACPI_RTYPE_INTEGER}},
{{"_TMP", 0, ACPI_RTYPE_INTEGER}},
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
index e1141402dbed..f4f523bf5939 100644
--- a/drivers/acpi/acpica/evxface.c
+++ b/drivers/acpi/acpica/evxface.c
@@ -41,6 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index c57b5c707a77..20516e599476 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -41,6 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "actables.h"
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c
index 52aaff3df562..f06a3ee356ba 100644
--- a/drivers/acpi/acpica/evxfgpe.c
+++ b/drivers/acpi/acpica/evxfgpe.c
@@ -41,6 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acevents.h"
diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
index 00cd95692a91..aee887e3ca5c 100644
--- a/drivers/acpi/acpica/evxfregn.c
+++ b/drivers/acpi/acpica/evxfregn.c
@@ -42,6 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c
index 55accb7018bb..cc70f3fdcdd1 100644
--- a/drivers/acpi/acpica/hwregs.c
+++ b/drivers/acpi/acpica/hwregs.c
@@ -269,16 +269,17 @@ acpi_status acpi_hw_clear_acpi_status(void)
status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS,
ACPI_BITMASK_ALL_FIXED_STATUS);
- if (ACPI_FAILURE(status)) {
- goto unlock_and_exit;
- }
+
+ acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags);
+
+ if (ACPI_FAILURE(status))
+ goto exit;
/* Clear the GPE Bits in all GPE registers in all GPE blocks */
status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL);
- unlock_and_exit:
- acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags);
+exit:
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
index 2ac28bbe8827..d52da3073650 100644
--- a/drivers/acpi/acpica/hwsleep.c
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -46,6 +46,7 @@
#include "accommon.h"
#include "actables.h"
#include <linux/tboot.h>
+#include <linux/module.h>
#define _COMPONENT ACPI_HARDWARE
ACPI_MODULE_NAME("hwsleep")
diff --git a/drivers/acpi/acpica/hwtimer.c b/drivers/acpi/acpica/hwtimer.c
index 9c8eb71a12fb..50d21c40b5c1 100644
--- a/drivers/acpi/acpica/hwtimer.c
+++ b/drivers/acpi/acpica/hwtimer.c
@@ -42,6 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c
index f75f81ad15c9..c2793a82f120 100644
--- a/drivers/acpi/acpica/hwxface.c
+++ b/drivers/acpi/acpica/hwxface.c
@@ -42,6 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c
index 9fb03fa8ffde..c845c8089f39 100644
--- a/drivers/acpi/acpica/nspredef.c
+++ b/drivers/acpi/acpica/nspredef.c
@@ -193,14 +193,20 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
}
/*
- * 1) We have a return value, but if one wasn't expected, just exit, this is
- * not a problem. For example, if the "Implicit Return" feature is
- * enabled, methods will always return a value.
+ * Return value validation and possible repair.
*
- * 2) If the return value can be of any type, then we cannot perform any
- * validation, exit.
+ * 1) Don't perform return value validation/repair if this feature
+ * has been disabled via a global option.
+ *
+ * 2) We have a return value, but if one wasn't expected, just exit,
+ * this is not a problem. For example, if the "Implicit Return"
+ * feature is enabled, methods will always return a value.
+ *
+ * 3) If the return value can be of any type, then we cannot perform
+ * any validation, just exit.
*/
- if ((!predefined->info.expected_btypes) ||
+ if (acpi_gbl_disable_auto_repair ||
+ (!predefined->info.expected_btypes) ||
(predefined->info.expected_btypes == ACPI_RTYPE_ALL)) {
goto cleanup;
}
@@ -212,6 +218,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
goto cleanup;
}
data->predefined = predefined;
+ data->node = node;
data->node_flags = node->flags;
data->pathname = pathname;
diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c
index 973883babee1..024c4f263f87 100644
--- a/drivers/acpi/acpica/nsrepair2.c
+++ b/drivers/acpi/acpica/nsrepair2.c
@@ -503,6 +503,21 @@ acpi_ns_repair_TSS(struct acpi_predefined_data *data,
{
union acpi_operand_object *return_object = *return_object_ptr;
acpi_status status;
+ struct acpi_namespace_node *node;
+
+ /*
+ * We can only sort the _TSS return package if there is no _PSS in the
+ * same scope. This is because if _PSS is present, the ACPI specification
+ * dictates that the _TSS Power Dissipation field is to be ignored, and
+ * therefore some BIOSs leave garbage values in the _TSS Power field(s).
+ * In this case, it is best to just return the _TSS package as-is.
+ * (May, 2011)
+ */
+ status =
+ acpi_ns_get_node(data->node, "^_PSS", ACPI_NS_NO_UPSEARCH, &node);
+ if (ACPI_SUCCESS(status)) {
+ return (AE_OK);
+ }
status = acpi_ns_check_sorted_list(data, return_object, 5, 1,
ACPI_SORT_DESCENDING,
diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c
index c53f0040e490..e7f016d1b226 100644
--- a/drivers/acpi/acpica/nsxfeval.c
+++ b/drivers/acpi/acpica/nsxfeval.c
@@ -42,6 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c
index 3fd4526f3dba..83bf93024303 100644
--- a/drivers/acpi/acpica/nsxfname.c
+++ b/drivers/acpi/acpica/nsxfname.c
@@ -42,6 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c
index db7660f8b869..57e6d825ed84 100644
--- a/drivers/acpi/acpica/nsxfobj.c
+++ b/drivers/acpi/acpica/nsxfobj.c
@@ -42,6 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c
index 2ff657a28f26..fe86b37b16ce 100644
--- a/drivers/acpi/acpica/rsxface.c
+++ b/drivers/acpi/acpica/rsxface.c
@@ -41,6 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c
index 48db0944ce4a..62365f6075dd 100644
--- a/drivers/acpi/acpica/tbinstal.c
+++ b/drivers/acpi/acpica/tbinstal.c
@@ -126,12 +126,29 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index)
}
/*
- * Originally, we checked the table signature for "SSDT" or "PSDT" here.
- * Next, we added support for OEMx tables, signature "OEM".
- * Valid tables were encountered with a null signature, so we've just
- * given up on validating the signature, since it seems to be a waste
- * of code. The original code was removed (05/2008).
+ * Validate the incoming table signature.
+ *
+ * 1) Originally, we checked the table signature for "SSDT" or "PSDT".
+ * 2) We added support for OEMx tables, signature "OEM".
+ * 3) Valid tables were encountered with a null signature, so we just
+ * gave up on validating the signature, (05/2008).
+ * 4) We encountered non-AML tables such as the MADT, which caused
+ * interpreter errors and kernel faults. So now, we once again allow
+ * only "SSDT", "OEMx", and now, also a null signature. (05/2011).
*/
+ if ((table_desc->pointer->signature[0] != 0x00) &&
+ (!ACPI_COMPARE_NAME(table_desc->pointer->signature, ACPI_SIG_SSDT))
+ && (ACPI_STRNCMP(table_desc->pointer->signature, "OEM", 3))) {
+ ACPI_ERROR((AE_INFO,
+ "Table has invalid signature [%4.4s] (0x%8.8X), must be SSDT or OEMx",
+ acpi_ut_valid_acpi_name(*(u32 *)table_desc->
+ pointer->
+ signature) ? table_desc->
+ pointer->signature : "????",
+ *(u32 *)table_desc->pointer->signature));
+
+ return_ACPI_STATUS(AE_BAD_SIGNATURE);
+ }
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c
index 4b7085dfc683..e7d13f5d3f2d 100644
--- a/drivers/acpi/acpica/tbxface.c
+++ b/drivers/acpi/acpica/tbxface.c
@@ -42,6 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c
index a9bcd816dc29..a1f8d7509e66 100644
--- a/drivers/acpi/acpica/utdebug.c
+++ b/drivers/acpi/acpica/utdebug.c
@@ -41,6 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c
index 97cb36f85ce9..8b087e2d64f4 100644
--- a/drivers/acpi/acpica/utdecode.c
+++ b/drivers/acpi/acpica/utdecode.c
@@ -41,6 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c
index 833a38a9c905..ffba0a39c3e8 100644
--- a/drivers/acpi/acpica/utglobal.c
+++ b/drivers/acpi/acpica/utglobal.c
@@ -43,6 +43,7 @@
#define DEFINE_ACPI_GLOBALS
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c
index 98ad125e14ff..420ebfe08c72 100644
--- a/drivers/acpi/acpica/utxface.c
+++ b/drivers/acpi/acpica/utxface.c
@@ -41,6 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acevents.h"
diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c
index 916ae097c43c..8d0245ec4315 100644
--- a/drivers/acpi/acpica/utxferror.c
+++ b/drivers/acpi/acpica/utxferror.c
@@ -41,6 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index f739a70b1c70..f0c1ce95a0ec 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -10,9 +10,11 @@ config ACPI_APEI
error injection.
config ACPI_APEI_GHES
- tristate "APEI Generic Hardware Error Source"
+ bool "APEI Generic Hardware Error Source"
depends on ACPI_APEI && X86
select ACPI_HED
+ select IRQ_WORK
+ select GENERIC_ALLOCATOR
help
Generic Hardware Error Source provides a way to report
platform hardware errors (such as that from chipset). It
@@ -30,6 +32,13 @@ config ACPI_APEI_PCIEAER
PCIe AER errors may be reported via APEI firmware first mode.
Turn on this option to enable the corresponding support.
+config ACPI_APEI_MEMORY_FAILURE
+ bool "APEI memory error recovering support"
+ depends on ACPI_APEI && MEMORY_FAILURE
+ help
+ Memory errors may be reported via APEI firmware first mode.
+ Turn on this option to enable the memory recovering support.
+
config ACPI_APEI_EINJ
tristate "APEI Error INJection (EINJ)"
depends on ACPI_APEI && DEBUG_FS
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c
index 4a904a4bf05f..61540360d5ce 100644
--- a/drivers/acpi/apei/apei-base.c
+++ b/drivers/acpi/apei/apei-base.c
@@ -157,9 +157,10 @@ EXPORT_SYMBOL_GPL(apei_exec_noop);
* Interpret the specified action. Go through whole action table,
* execute all instructions belong to the action.
*/
-int apei_exec_run(struct apei_exec_context *ctx, u8 action)
+int __apei_exec_run(struct apei_exec_context *ctx, u8 action,
+ bool optional)
{
- int rc;
+ int rc = -ENOENT;
u32 i, ip;
struct acpi_whea_header *entry;
apei_exec_ins_func_t run;
@@ -198,9 +199,9 @@ rewind:
goto rewind;
}
- return 0;
+ return !optional && rc < 0 ? rc : 0;
}
-EXPORT_SYMBOL_GPL(apei_exec_run);
+EXPORT_SYMBOL_GPL(__apei_exec_run);
typedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx,
struct acpi_whea_header *entry,
@@ -603,3 +604,29 @@ struct dentry *apei_get_debugfs_dir(void)
return dapei;
}
EXPORT_SYMBOL_GPL(apei_get_debugfs_dir);
+
+int apei_osc_setup(void)
+{
+ static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c";
+ acpi_handle handle;
+ u32 capbuf[3];
+ struct acpi_osc_context context = {
+ .uuid_str = whea_uuid_str,
+ .rev = 1,
+ .cap.length = sizeof(capbuf),
+ .cap.pointer = capbuf,
+ };
+
+ capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
+ capbuf[OSC_SUPPORT_TYPE] = 1;
+ capbuf[OSC_CONTROL_TYPE] = 0;
+
+ if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))
+ || ACPI_FAILURE(acpi_run_osc(handle, &context)))
+ return -EIO;
+ else {
+ kfree(context.ret.pointer);
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(apei_osc_setup);
diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h
index ef0581f2094d..f57050e7a5e7 100644
--- a/drivers/acpi/apei/apei-internal.h
+++ b/drivers/acpi/apei/apei-internal.h
@@ -50,7 +50,18 @@ static inline u64 apei_exec_ctx_get_output(struct apei_exec_context *ctx)
return ctx->value;
}
-int apei_exec_run(struct apei_exec_context *ctx, u8 action);
+int __apei_exec_run(struct apei_exec_context *ctx, u8 action, bool optional);
+
+static inline int apei_exec_run(struct apei_exec_context *ctx, u8 action)
+{
+ return __apei_exec_run(ctx, action, 0);
+}
+
+/* It is optional whether the firmware provides the action */
+static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 action)
+{
+ return __apei_exec_run(ctx, action, 1);
+}
/* Common instruction implementation */
@@ -113,4 +124,6 @@ void apei_estatus_print(const char *pfx,
const struct acpi_hest_generic_status *estatus);
int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus);
int apei_estatus_check(const struct acpi_hest_generic_status *estatus);
+
+int apei_osc_setup(void);
#endif
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c
index f74b2ea11f21..589b96c38704 100644
--- a/drivers/acpi/apei/einj.c
+++ b/drivers/acpi/apei/einj.c
@@ -46,7 +46,8 @@
* Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the
* EINJ table through an unpublished extension. Use with caution as
* most will ignore the parameter and make their own choice of address
- * for error injection.
+ * for error injection. This extension is used only if
+ * param_extension module parameter is specified.
*/
struct einj_parameter {
u64 type;
@@ -65,6 +66,9 @@ struct einj_parameter {
((struct acpi_whea_header *)((char *)(tab) + \
sizeof(struct acpi_table_einj)))
+static bool param_extension;
+module_param(param_extension, bool, 0);
+
static struct acpi_table_einj *einj_tab;
static struct apei_resources einj_resources;
@@ -285,7 +289,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
einj_exec_ctx_init(&ctx);
- rc = apei_exec_run(&ctx, ACPI_EINJ_BEGIN_OPERATION);
+ rc = apei_exec_run_optional(&ctx, ACPI_EINJ_BEGIN_OPERATION);
if (rc)
return rc;
apei_exec_ctx_set_input(&ctx, type);
@@ -323,7 +327,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
rc = __einj_error_trigger(trigger_paddr);
if (rc)
return rc;
- rc = apei_exec_run(&ctx, ACPI_EINJ_END_OPERATION);
+ rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
return rc;
}
@@ -489,14 +493,6 @@ static int __init einj_init(void)
einj_debug_dir, NULL, &error_type_fops);
if (!fentry)
goto err_cleanup;
- fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR,
- einj_debug_dir, &error_param1);
- if (!fentry)
- goto err_cleanup;
- fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR,
- einj_debug_dir, &error_param2);
- if (!fentry)
- goto err_cleanup;
fentry = debugfs_create_file("error_inject", S_IWUSR,
einj_debug_dir, NULL, &error_inject_fops);
if (!fentry)
@@ -513,12 +509,23 @@ static int __init einj_init(void)
rc = apei_exec_pre_map_gars(&ctx);
if (rc)
goto err_release;
- param_paddr = einj_get_parameter_address();
- if (param_paddr) {
- einj_param = ioremap(param_paddr, sizeof(*einj_param));
- rc = -ENOMEM;
- if (!einj_param)
- goto err_unmap;
+ if (param_extension) {
+ param_paddr = einj_get_parameter_address();
+ if (param_paddr) {
+ einj_param = ioremap(param_paddr, sizeof(*einj_param));
+ rc = -ENOMEM;
+ if (!einj_param)
+ goto err_unmap;
+ fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR,
+ einj_debug_dir, &error_param1);
+ if (!fentry)
+ goto err_unmap;
+ fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR,
+ einj_debug_dir, &error_param2);
+ if (!fentry)
+ goto err_unmap;
+ } else
+ pr_warn(EINJ_PFX "Parameter extension is not supported.\n");
}
pr_info(EINJ_PFX "Error INJection is initialized.\n");
@@ -526,6 +533,8 @@ static int __init einj_init(void)
return 0;
err_unmap:
+ if (einj_param)
+ iounmap(einj_param);
apei_exec_post_unmap_gars(&ctx);
err_release:
apei_resources_release(&einj_resources);
diff --git a/drivers/acpi/apei/erst-dbg.c b/drivers/acpi/apei/erst-dbg.c
index a4cfb64c86a1..903549df809b 100644
--- a/drivers/acpi/apei/erst-dbg.c
+++ b/drivers/acpi/apei/erst-dbg.c
@@ -33,7 +33,7 @@
#define ERST_DBG_PFX "ERST DBG: "
-#define ERST_DBG_RECORD_LEN_MAX 4096
+#define ERST_DBG_RECORD_LEN_MAX 0x4000
static void *erst_dbg_buf;
static unsigned int erst_dbg_buf_len;
@@ -213,6 +213,10 @@ static struct miscdevice erst_dbg_dev = {
static __init int erst_dbg_init(void)
{
+ if (erst_disable) {
+ pr_info(ERST_DBG_PFX "ERST support is disabled.\n");
+ return -ENODEV;
+ }
return misc_register(&erst_dbg_dev);
}
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index e6cef8e1b534..127408069ca7 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -642,7 +642,7 @@ static int __erst_write_to_storage(u64 offset)
int rc;
erst_exec_ctx_init(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_WRITE);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE);
if (rc)
return rc;
apei_exec_ctx_set_input(&ctx, offset);
@@ -666,7 +666,7 @@ static int __erst_write_to_storage(u64 offset)
if (rc)
return rc;
val = apei_exec_ctx_get_output(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_END);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
if (rc)
return rc;
@@ -681,7 +681,7 @@ static int __erst_read_from_storage(u64 record_id, u64 offset)
int rc;
erst_exec_ctx_init(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_READ);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ);
if (rc)
return rc;
apei_exec_ctx_set_input(&ctx, offset);
@@ -709,7 +709,7 @@ static int __erst_read_from_storage(u64 record_id, u64 offset)
if (rc)
return rc;
val = apei_exec_ctx_get_output(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_END);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
if (rc)
return rc;
@@ -724,7 +724,7 @@ static int __erst_clear_from_storage(u64 record_id)
int rc;
erst_exec_ctx_init(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_CLEAR);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR);
if (rc)
return rc;
apei_exec_ctx_set_input(&ctx, record_id);
@@ -748,7 +748,7 @@ static int __erst_clear_from_storage(u64 record_id)
if (rc)
return rc;
val = apei_exec_ctx_get_output(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_END);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
if (rc)
return rc;
@@ -932,8 +932,11 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
static int erst_open_pstore(struct pstore_info *psi);
static int erst_close_pstore(struct pstore_info *psi);
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
- struct timespec *time);
-static u64 erst_writer(enum pstore_type_id type, size_t size);
+ struct timespec *time, struct pstore_info *psi);
+static int erst_writer(enum pstore_type_id type, u64 *id, unsigned int part,
+ size_t size, struct pstore_info *psi);
+static int erst_clearer(enum pstore_type_id type, u64 id,
+ struct pstore_info *psi);
static struct pstore_info erst_info = {
.owner = THIS_MODULE,
@@ -942,7 +945,7 @@ static struct pstore_info erst_info = {
.close = erst_close_pstore,
.read = erst_reader,
.write = erst_writer,
- .erase = erst_clear
+ .erase = erst_clearer
};
#define CPER_CREATOR_PSTORE \
@@ -983,7 +986,7 @@ static int erst_close_pstore(struct pstore_info *psi)
}
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
- struct timespec *time)
+ struct timespec *time, struct pstore_info *psi)
{
int rc;
ssize_t len = 0;
@@ -1037,10 +1040,12 @@ out:
return (rc < 0) ? rc : (len - sizeof(*rcd));
}
-static u64 erst_writer(enum pstore_type_id type, size_t size)
+static int erst_writer(enum pstore_type_id type, u64 *id, unsigned int part,
+ size_t size, struct pstore_info *psi)
{
struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd));
+ int ret;
memset(rcd, 0, sizeof(*rcd));
memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
@@ -1075,9 +1080,16 @@ static u64 erst_writer(enum pstore_type_id type, size_t size)
}
rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
- erst_write(&rcd->hdr);
+ ret = erst_write(&rcd->hdr);
+ *id = rcd->hdr.record_id;
- return rcd->hdr.record_id;
+ return ret;
+}
+
+static int erst_clearer(enum pstore_type_id type, u64 id,
+ struct pstore_info *psi)
+{
+ return erst_clear(id);
}
static int __init erst_init(void)
@@ -1155,7 +1167,7 @@ static int __init erst_init(void)
goto err_release_erange;
buf = kmalloc(erst_erange.size, GFP_KERNEL);
- mutex_init(&erst_info.buf_mutex);
+ spin_lock_init(&erst_info.buf_lock);
if (buf) {
erst_info.buf = buf + sizeof(struct cper_pstore_record);
erst_info.bufsize = erst_erange.size -
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index f703b2881153..b8e08cb67a18 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -12,7 +12,7 @@
* For more information about Generic Hardware Error Source, please
* refer to ACPI Specification version 4.0, section 17.3.2.6
*
- * Copyright 2010 Intel Corp.
+ * Copyright 2010,2011 Intel Corp.
* Author: Huang Ying <ying.huang@intel.com>
*
* This program is free software; you can redistribute it and/or
@@ -42,17 +42,45 @@
#include <linux/mutex.h>
#include <linux/ratelimit.h>
#include <linux/vmalloc.h>
+#include <linux/irq_work.h>
+#include <linux/llist.h>
+#include <linux/genalloc.h>
#include <acpi/apei.h>
#include <acpi/atomicio.h>
#include <acpi/hed.h>
#include <asm/mce.h>
#include <asm/tlbflush.h>
+#include <asm/nmi.h>
#include "apei-internal.h"
#define GHES_PFX "GHES: "
#define GHES_ESTATUS_MAX_SIZE 65536
+#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536
+
+#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3
+
+/* This is just an estimation for memory pool allocation */
+#define GHES_ESTATUS_CACHE_AVG_SIZE 512
+
+#define GHES_ESTATUS_CACHES_SIZE 4
+
+#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL
+/* Prevent too many caches are allocated because of RCU */
+#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2)
+
+#define GHES_ESTATUS_CACHE_LEN(estatus_len) \
+ (sizeof(struct ghes_estatus_cache) + (estatus_len))
+#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \
+ ((struct acpi_hest_generic_status *) \
+ ((struct ghes_estatus_cache *)(estatus_cache) + 1))
+
+#define GHES_ESTATUS_NODE_LEN(estatus_len) \
+ (sizeof(struct ghes_estatus_node) + (estatus_len))
+#define GHES_ESTATUS_FROM_NODE(estatus_node) \
+ ((struct acpi_hest_generic_status *) \
+ ((struct ghes_estatus_node *)(estatus_node) + 1))
/*
* One struct ghes is created for each generic hardware error source.
@@ -77,6 +105,22 @@ struct ghes {
};
};
+struct ghes_estatus_node {
+ struct llist_node llnode;
+ struct acpi_hest_generic *generic;
+};
+
+struct ghes_estatus_cache {
+ u32 estatus_len;
+ atomic_t count;
+ struct acpi_hest_generic *generic;
+ unsigned long long time_in;
+ struct rcu_head rcu;
+};
+
+int ghes_disable;
+module_param_named(disable, ghes_disable, bool, 0);
+
static int ghes_panic_timeout __read_mostly = 30;
/*
@@ -121,6 +165,22 @@ static struct vm_struct *ghes_ioremap_area;
static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi);
static DEFINE_SPINLOCK(ghes_ioremap_lock_irq);
+/*
+ * printk is not safe in NMI context. So in NMI handler, we allocate
+ * required memory from lock-less memory allocator
+ * (ghes_estatus_pool), save estatus into it, put them into lock-less
+ * list (ghes_estatus_llist), then delay printk into IRQ context via
+ * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record
+ * required pool size by all NMI error source.
+ */
+static struct gen_pool *ghes_estatus_pool;
+static unsigned long ghes_estatus_pool_size_request;
+static struct llist_head ghes_estatus_llist;
+static struct irq_work ghes_proc_irq_work;
+
+struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
+static atomic_t ghes_estatus_cache_alloced;
+
static int ghes_ioremap_init(void)
{
ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
@@ -180,6 +240,55 @@ static void ghes_iounmap_irq(void __iomem *vaddr_ptr)
__flush_tlb_one(vaddr);
}
+static int ghes_estatus_pool_init(void)
+{
+ ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1);
+ if (!ghes_estatus_pool)
+ return -ENOMEM;
+ return 0;
+}
+
+static void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool,
+ struct gen_pool_chunk *chunk,
+ void *data)
+{
+ free_page(chunk->start_addr);
+}
+
+static void ghes_estatus_pool_exit(void)
+{
+ gen_pool_for_each_chunk(ghes_estatus_pool,
+ ghes_estatus_pool_free_chunk_page, NULL);
+ gen_pool_destroy(ghes_estatus_pool);
+}
+
+static int ghes_estatus_pool_expand(unsigned long len)
+{
+ unsigned long i, pages, size, addr;
+ int ret;
+
+ ghes_estatus_pool_size_request += PAGE_ALIGN(len);
+ size = gen_pool_size(ghes_estatus_pool);
+ if (size >= ghes_estatus_pool_size_request)
+ return 0;
+ pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE;
+ for (i = 0; i < pages; i++) {
+ addr = __get_free_page(GFP_KERNEL);
+ if (!addr)
+ return -ENOMEM;
+ ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ghes_estatus_pool_shrink(unsigned long len)
+{
+ ghes_estatus_pool_size_request -= PAGE_ALIGN(len);
+}
+
static struct ghes *ghes_new(struct acpi_hest_generic *generic)
{
struct ghes *ghes;
@@ -341,43 +450,196 @@ static void ghes_clear_estatus(struct ghes *ghes)
ghes->flags &= ~GHES_TO_CLEAR;
}
-static void ghes_do_proc(struct ghes *ghes)
+static void ghes_do_proc(const struct acpi_hest_generic_status *estatus)
{
- int sev, processed = 0;
+ int sev, sec_sev;
struct acpi_hest_generic_data *gdata;
- sev = ghes_severity(ghes->estatus->error_severity);
- apei_estatus_for_each_section(ghes->estatus, gdata) {
-#ifdef CONFIG_X86_MCE
+ sev = ghes_severity(estatus->error_severity);
+ apei_estatus_for_each_section(estatus, gdata) {
+ sec_sev = ghes_severity(gdata->error_severity);
if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
CPER_SEC_PLATFORM_MEM)) {
- apei_mce_report_mem_error(
- sev == GHES_SEV_CORRECTED,
- (struct cper_sec_mem_err *)(gdata+1));
- processed = 1;
- }
+ struct cper_sec_mem_err *mem_err;
+ mem_err = (struct cper_sec_mem_err *)(gdata+1);
+#ifdef CONFIG_X86_MCE
+ apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED,
+ mem_err);
#endif
+#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE
+ if (sev == GHES_SEV_RECOVERABLE &&
+ sec_sev == GHES_SEV_RECOVERABLE &&
+ mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) {
+ unsigned long pfn;
+ pfn = mem_err->physical_addr >> PAGE_SHIFT;
+ memory_failure_queue(pfn, 0, 0);
+ }
+#endif
+ }
}
}
-static void ghes_print_estatus(const char *pfx, struct ghes *ghes)
+static void __ghes_print_estatus(const char *pfx,
+ const struct acpi_hest_generic *generic,
+ const struct acpi_hest_generic_status *estatus)
{
- /* Not more than 2 messages every 5 seconds */
- static DEFINE_RATELIMIT_STATE(ratelimit, 5*HZ, 2);
-
if (pfx == NULL) {
- if (ghes_severity(ghes->estatus->error_severity) <=
+ if (ghes_severity(estatus->error_severity) <=
GHES_SEV_CORRECTED)
pfx = KERN_WARNING HW_ERR;
else
pfx = KERN_ERR HW_ERR;
}
- if (__ratelimit(&ratelimit)) {
- printk(
- "%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
- pfx, ghes->generic->header.source_id);
- apei_estatus_print(pfx, ghes->estatus);
+ printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
+ pfx, generic->header.source_id);
+ apei_estatus_print(pfx, estatus);
+}
+
+static int ghes_print_estatus(const char *pfx,
+ const struct acpi_hest_generic *generic,
+ const struct acpi_hest_generic_status *estatus)
+{
+ /* Not more than 2 messages every 5 seconds */
+ static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2);
+ static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2);
+ struct ratelimit_state *ratelimit;
+
+ if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED)
+ ratelimit = &ratelimit_corrected;
+ else
+ ratelimit = &ratelimit_uncorrected;
+ if (__ratelimit(ratelimit)) {
+ __ghes_print_estatus(pfx, generic, estatus);
+ return 1;
}
+ return 0;
+}
+
+/*
+ * GHES error status reporting throttle, to report more kinds of
+ * errors, instead of just most frequently occurred errors.
+ */
+static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus)
+{
+ u32 len;
+ int i, cached = 0;
+ unsigned long long now;
+ struct ghes_estatus_cache *cache;
+ struct acpi_hest_generic_status *cache_estatus;
+
+ len = apei_estatus_len(estatus);
+ rcu_read_lock();
+ for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
+ cache = rcu_dereference(ghes_estatus_caches[i]);
+ if (cache == NULL)
+ continue;
+ if (len != cache->estatus_len)
+ continue;
+ cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
+ if (memcmp(estatus, cache_estatus, len))
+ continue;
+ atomic_inc(&cache->count);
+ now = sched_clock();
+ if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC)
+ cached = 1;
+ break;
+ }
+ rcu_read_unlock();
+ return cached;
+}
+
+static struct ghes_estatus_cache *ghes_estatus_cache_alloc(
+ struct acpi_hest_generic *generic,
+ struct acpi_hest_generic_status *estatus)
+{
+ int alloced;
+ u32 len, cache_len;
+ struct ghes_estatus_cache *cache;
+ struct acpi_hest_generic_status *cache_estatus;
+
+ alloced = atomic_add_return(1, &ghes_estatus_cache_alloced);
+ if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) {
+ atomic_dec(&ghes_estatus_cache_alloced);
+ return NULL;
+ }
+ len = apei_estatus_len(estatus);
+ cache_len = GHES_ESTATUS_CACHE_LEN(len);
+ cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len);
+ if (!cache) {
+ atomic_dec(&ghes_estatus_cache_alloced);
+ return NULL;
+ }
+ cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
+ memcpy(cache_estatus, estatus, len);
+ cache->estatus_len = len;
+ atomic_set(&cache->count, 0);
+ cache->generic = generic;
+ cache->time_in = sched_clock();
+ return cache;
+}
+
+static void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
+{
+ u32 len;
+
+ len = apei_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
+ len = GHES_ESTATUS_CACHE_LEN(len);
+ gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
+ atomic_dec(&ghes_estatus_cache_alloced);
+}
+
+static void ghes_estatus_cache_rcu_free(struct rcu_head *head)
+{
+ struct ghes_estatus_cache *cache;
+
+ cache = container_of(head, struct ghes_estatus_cache, rcu);
+ ghes_estatus_cache_free(cache);
+}
+
+static void ghes_estatus_cache_add(
+ struct acpi_hest_generic *generic,
+ struct acpi_hest_generic_status *estatus)
+{
+ int i, slot = -1, count;
+ unsigned long long now, duration, period, max_period = 0;
+ struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
+
+ new_cache = ghes_estatus_cache_alloc(generic, estatus);
+ if (new_cache == NULL)
+ return;
+ rcu_read_lock();
+ now = sched_clock();
+ for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
+ cache = rcu_dereference(ghes_estatus_caches[i]);
+ if (cache == NULL) {
+ slot = i;
+ slot_cache = NULL;
+ break;
+ }
+ duration = now - cache->time_in;
+ if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
+ slot = i;
+ slot_cache = cache;
+ break;
+ }
+ count = atomic_read(&cache->count);
+ period = duration;
+ do_div(period, (count + 1));
+ if (period > max_period) {
+ max_period = period;
+ slot = i;
+ slot_cache = cache;
+ }
+ }
+ /* new_cache must be put into array after its contents are written */
+ smp_wmb();
+ if (slot != -1 && cmpxchg(ghes_estatus_caches + slot,
+ slot_cache, new_cache) == slot_cache) {
+ if (slot_cache)
+ call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free);
+ } else
+ ghes_estatus_cache_free(new_cache);
+ rcu_read_unlock();
}
static int ghes_proc(struct ghes *ghes)
@@ -387,9 +649,11 @@ static int ghes_proc(struct ghes *ghes)
rc = ghes_read_estatus(ghes, 0);
if (rc)
goto out;
- ghes_print_estatus(NULL, ghes);
- ghes_do_proc(ghes);
-
+ if (!ghes_estatus_cached(ghes->estatus)) {
+ if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus))
+ ghes_estatus_cache_add(ghes->generic, ghes->estatus);
+ }
+ ghes_do_proc(ghes->estatus);
out:
ghes_clear_estatus(ghes);
return 0;
@@ -447,15 +711,50 @@ static int ghes_notify_sci(struct notifier_block *this,
return ret;
}
-static int ghes_notify_nmi(struct notifier_block *this,
- unsigned long cmd, void *data)
+static void ghes_proc_in_irq(struct irq_work *irq_work)
+{
+ struct llist_node *llnode, *next, *tail = NULL;
+ struct ghes_estatus_node *estatus_node;
+ struct acpi_hest_generic *generic;
+ struct acpi_hest_generic_status *estatus;
+ u32 len, node_len;
+
+ /*
+ * Because the time order of estatus in list is reversed,
+ * revert it back to proper order.
+ */
+ llnode = llist_del_all(&ghes_estatus_llist);
+ while (llnode) {
+ next = llnode->next;
+ llnode->next = tail;
+ tail = llnode;
+ llnode = next;
+ }
+ llnode = tail;
+ while (llnode) {
+ next = llnode->next;
+ estatus_node = llist_entry(llnode, struct ghes_estatus_node,
+ llnode);
+ estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
+ len = apei_estatus_len(estatus);
+ node_len = GHES_ESTATUS_NODE_LEN(len);
+ ghes_do_proc(estatus);
+ if (!ghes_estatus_cached(estatus)) {
+ generic = estatus_node->generic;
+ if (ghes_print_estatus(NULL, generic, estatus))
+ ghes_estatus_cache_add(generic, estatus);
+ }
+ gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
+ node_len);
+ llnode = next;
+ }
+}
+
+static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
{
struct ghes *ghes, *ghes_global = NULL;
int sev, sev_global = -1;
- int ret = NOTIFY_DONE;
-
- if (cmd != DIE_NMI)
- return ret;
+ int ret = NMI_DONE;
raw_spin_lock(&ghes_nmi_lock);
list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
@@ -468,15 +767,16 @@ static int ghes_notify_nmi(struct notifier_block *this,
sev_global = sev;
ghes_global = ghes;
}
- ret = NOTIFY_STOP;
+ ret = NMI_HANDLED;
}
- if (ret == NOTIFY_DONE)
+ if (ret == NMI_DONE)
goto out;
if (sev_global >= GHES_SEV_PANIC) {
oops_begin();
- ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global);
+ __ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global->generic,
+ ghes_global->estatus);
/* reboot to log the error! */
if (panic_timeout == 0)
panic_timeout = ghes_panic_timeout;
@@ -484,12 +784,34 @@ static int ghes_notify_nmi(struct notifier_block *this,
}
list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ u32 len, node_len;
+ struct ghes_estatus_node *estatus_node;
+ struct acpi_hest_generic_status *estatus;
+#endif
if (!(ghes->flags & GHES_TO_CLEAR))
continue;
- /* Do not print estatus because printk is not NMI safe */
- ghes_do_proc(ghes);
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ if (ghes_estatus_cached(ghes->estatus))
+ goto next;
+ /* Save estatus for further processing in IRQ context */
+ len = apei_estatus_len(ghes->estatus);
+ node_len = GHES_ESTATUS_NODE_LEN(len);
+ estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool,
+ node_len);
+ if (estatus_node) {
+ estatus_node->generic = ghes->generic;
+ estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
+ memcpy(estatus, ghes->estatus, len);
+ llist_add(&estatus_node->llnode, &ghes_estatus_llist);
+ }
+next:
+#endif
ghes_clear_estatus(ghes);
}
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ irq_work_queue(&ghes_proc_irq_work);
+#endif
out:
raw_spin_unlock(&ghes_nmi_lock);
@@ -500,14 +822,26 @@ static struct notifier_block ghes_notifier_sci = {
.notifier_call = ghes_notify_sci,
};
-static struct notifier_block ghes_notifier_nmi = {
- .notifier_call = ghes_notify_nmi,
-};
+static unsigned long ghes_esource_prealloc_size(
+ const struct acpi_hest_generic *generic)
+{
+ unsigned long block_length, prealloc_records, prealloc_size;
+
+ block_length = min_t(unsigned long, generic->error_block_length,
+ GHES_ESTATUS_MAX_SIZE);
+ prealloc_records = max_t(unsigned long,
+ generic->records_to_preallocate, 1);
+ prealloc_size = min_t(unsigned long, block_length * prealloc_records,
+ GHES_ESOURCE_PREALLOC_MAX_SIZE);
+
+ return prealloc_size;
+}
static int __devinit ghes_probe(struct platform_device *ghes_dev)
{
struct acpi_hest_generic *generic;
struct ghes *ghes = NULL;
+ unsigned long len;
int rc = -EINVAL;
generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data;
@@ -573,9 +907,12 @@ static int __devinit ghes_probe(struct platform_device *ghes_dev)
mutex_unlock(&ghes_list_mutex);
break;
case ACPI_HEST_NOTIFY_NMI:
+ len = ghes_esource_prealloc_size(generic);
+ ghes_estatus_pool_expand(len);
mutex_lock(&ghes_list_mutex);
if (list_empty(&ghes_nmi))
- register_die_notifier(&ghes_notifier_nmi);
+ register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0,
+ "ghes");
list_add_rcu(&ghes->list, &ghes_nmi);
mutex_unlock(&ghes_list_mutex);
break;
@@ -597,6 +934,7 @@ static int __devexit ghes_remove(struct platform_device *ghes_dev)
{
struct ghes *ghes;
struct acpi_hest_generic *generic;
+ unsigned long len;
ghes = platform_get_drvdata(ghes_dev);
generic = ghes->generic;
@@ -620,13 +958,15 @@ static int __devexit ghes_remove(struct platform_device *ghes_dev)
mutex_lock(&ghes_list_mutex);
list_del_rcu(&ghes->list);
if (list_empty(&ghes_nmi))
- unregister_die_notifier(&ghes_notifier_nmi);
+ unregister_nmi_handler(NMI_LOCAL, "ghes");
mutex_unlock(&ghes_list_mutex);
/*
* To synchronize with NMI handler, ghes can only be
* freed after NMI handler finishes.
*/
synchronize_rcu();
+ len = ghes_esource_prealloc_size(generic);
+ ghes_estatus_pool_shrink(len);
break;
default:
BUG();
@@ -662,15 +1002,43 @@ static int __init ghes_init(void)
return -EINVAL;
}
+ if (ghes_disable) {
+ pr_info(GHES_PFX "GHES is not enabled!\n");
+ return -EINVAL;
+ }
+
+ init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
+
rc = ghes_ioremap_init();
if (rc)
goto err;
- rc = platform_driver_register(&ghes_platform_driver);
+ rc = ghes_estatus_pool_init();
if (rc)
goto err_ioremap_exit;
+ rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE *
+ GHES_ESTATUS_CACHE_ALLOCED_MAX);
+ if (rc)
+ goto err_pool_exit;
+
+ rc = platform_driver_register(&ghes_platform_driver);
+ if (rc)
+ goto err_pool_exit;
+
+ rc = apei_osc_setup();
+ if (rc == 0 && osc_sb_apei_support_acked)
+ pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n");
+ else if (rc == 0 && !osc_sb_apei_support_acked)
+ pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n");
+ else if (rc && osc_sb_apei_support_acked)
+ pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n");
+ else
+ pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n");
+
return 0;
+err_pool_exit:
+ ghes_estatus_pool_exit();
err_ioremap_exit:
ghes_ioremap_exit();
err:
@@ -680,6 +1048,7 @@ err:
static void __exit ghes_exit(void)
{
platform_driver_unregister(&ghes_platform_driver);
+ ghes_estatus_pool_exit();
ghes_ioremap_exit();
}
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
index abda3786a5d7..05fee06f4d6e 100644
--- a/drivers/acpi/apei/hest.c
+++ b/drivers/acpi/apei/hest.c
@@ -139,13 +139,23 @@ static int __init hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data)
{
struct platform_device *ghes_dev;
struct ghes_arr *ghes_arr = data;
- int rc;
+ int rc, i;
if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR)
return 0;
if (!((struct acpi_hest_generic *)hest_hdr)->enabled)
return 0;
+ for (i = 0; i < ghes_arr->count; i++) {
+ struct acpi_hest_header *hdr;
+ ghes_dev = ghes_arr->ghes_devs[i];
+ hdr = *(struct acpi_hest_header **)ghes_dev->dev.platform_data;
+ if (hdr->source_id == hest_hdr->source_id) {
+ pr_warning(FW_WARN HEST_PFX "Duplicated hardware error source ID: %d.\n",
+ hdr->source_id);
+ return -EIO;
+ }
+ }
ghes_dev = platform_device_alloc("GHES", hest_hdr->source_id);
if (!ghes_dev)
return -ENOMEM;
@@ -221,16 +231,17 @@ void __init acpi_hest_init(void)
goto err;
}
- rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
- if (rc)
- goto err;
-
- rc = hest_ghes_dev_register(ghes_count);
- if (!rc) {
- pr_info(HEST_PFX "Table parsing has been initialized.\n");
- return;
+ if (!ghes_disable) {
+ rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
+ if (rc)
+ goto err;
+ rc = hest_ghes_dev_register(ghes_count);
+ if (rc)
+ goto err;
}
+ pr_info(HEST_PFX "Table parsing has been initialized.\n");
+ return;
err:
hest_disable = 1;
}
diff --git a/drivers/acpi/atomicio.c b/drivers/acpi/atomicio.c
index 7489b89c300f..cfc0cc10af39 100644
--- a/drivers/acpi/atomicio.c
+++ b/drivers/acpi/atomicio.c
@@ -24,7 +24,7 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/io.h>
@@ -76,7 +76,7 @@ static void __iomem *__acpi_ioremap_fast(phys_addr_t paddr,
{
struct acpi_iomap *map;
- map = __acpi_find_iomap(paddr, size);
+ map = __acpi_find_iomap(paddr, size/8);
if (map)
return map->vaddr + (paddr - map->paddr);
else
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index fcc13ac0aa18..7711d94a0409 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -55,6 +55,9 @@
#define ACPI_BATTERY_NOTIFY_INFO 0x81
#define ACPI_BATTERY_NOTIFY_THRESHOLD 0x82
+/* Battery power unit: 0 means mW, 1 means mA */
+#define ACPI_BATTERY_POWER_UNIT_MA 1
+
#define _COMPONENT ACPI_BATTERY_COMPONENT
ACPI_MODULE_NAME("battery");
@@ -91,16 +94,12 @@ MODULE_DEVICE_TABLE(acpi, battery_device_ids);
enum {
ACPI_BATTERY_ALARM_PRESENT,
ACPI_BATTERY_XINFO_PRESENT,
- /* For buggy DSDTs that report negative 16-bit values for either
- * charging or discharging current and/or report 0 as 65536
- * due to bad math.
- */
- ACPI_BATTERY_QUIRK_SIGNED16_CURRENT,
ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
};
struct acpi_battery {
struct mutex lock;
+ struct mutex sysfs_lock;
struct power_supply bat;
struct acpi_device *device;
struct notifier_block pm_nb;
@@ -132,7 +131,7 @@ struct acpi_battery {
unsigned long flags;
};
-#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat);
+#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat)
inline int acpi_battery_present(struct acpi_battery *battery)
{
@@ -301,7 +300,8 @@ static enum power_supply_property energy_battery_props[] = {
#ifdef CONFIG_ACPI_PROCFS_POWER
inline char *acpi_battery_units(struct acpi_battery *battery)
{
- return (battery->power_unit)?"mA":"mW";
+ return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
+ "mA" : "mW";
}
#endif
@@ -461,9 +461,17 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
battery->update_time = jiffies;
kfree(buffer.pointer);
- if (test_bit(ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, &battery->flags) &&
- battery->rate_now != -1)
+ /* For buggy DSDTs that report negative 16-bit values for either
+ * charging or discharging current and/or report 0 as 65536
+ * due to bad math.
+ */
+ if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA &&
+ battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
+ (s16)(battery->rate_now) < 0) {
battery->rate_now = abs((s16)battery->rate_now);
+ printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate"
+ " invalid.\n");
+ }
if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
&& battery->capacity_now >= 0 && battery->capacity_now <= 100)
@@ -544,7 +552,7 @@ static int sysfs_add_battery(struct acpi_battery *battery)
{
int result;
- if (battery->power_unit) {
+ if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
battery->bat.properties = charge_battery_props;
battery->bat.num_properties =
ARRAY_SIZE(charge_battery_props);
@@ -566,18 +574,16 @@ static int sysfs_add_battery(struct acpi_battery *battery)
static void sysfs_remove_battery(struct acpi_battery *battery)
{
- if (!battery->bat.dev)
+ mutex_lock(&battery->sysfs_lock);
+ if (!battery->bat.dev) {
+ mutex_unlock(&battery->sysfs_lock);
return;
+ }
+
device_remove_file(battery->bat.dev, &alarm_attr);
power_supply_unregister(&battery->bat);
battery->bat.dev = NULL;
-}
-
-static void acpi_battery_quirks(struct acpi_battery *battery)
-{
- if (dmi_name_in_vendors("Acer") && battery->power_unit) {
- set_bit(ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, &battery->flags);
- }
+ mutex_unlock(&battery->sysfs_lock);
}
/*
@@ -592,7 +598,7 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
*
* Handle this correctly so that they won't break userspace.
*/
-static void acpi_battery_quirks2(struct acpi_battery *battery)
+static void acpi_battery_quirks(struct acpi_battery *battery)
{
if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
return ;
@@ -623,13 +629,15 @@ static int acpi_battery_update(struct acpi_battery *battery)
result = acpi_battery_get_info(battery);
if (result)
return result;
- acpi_battery_quirks(battery);
acpi_battery_init_alarm(battery);
}
- if (!battery->bat.dev)
- sysfs_add_battery(battery);
+ if (!battery->bat.dev) {
+ result = sysfs_add_battery(battery);
+ if (result)
+ return result;
+ }
result = acpi_battery_get_state(battery);
- acpi_battery_quirks2(battery);
+ acpi_battery_quirks(battery);
return result;
}
@@ -863,7 +871,7 @@ DECLARE_FILE_FUNCTIONS(alarm);
}, \
}
-static struct battery_file {
+static const struct battery_file {
struct file_operations ops;
mode_t mode;
const char *name;
@@ -948,9 +956,12 @@ static int battery_notify(struct notifier_block *nb,
struct acpi_battery *battery = container_of(nb, struct acpi_battery,
pm_nb);
switch (mode) {
+ case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
- sysfs_remove_battery(battery);
- sysfs_add_battery(battery);
+ if (battery->bat.dev) {
+ sysfs_remove_battery(battery);
+ sysfs_add_battery(battery);
+ }
break;
}
@@ -972,28 +983,38 @@ static int acpi_battery_add(struct acpi_device *device)
strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
device->driver_data = battery;
mutex_init(&battery->lock);
+ mutex_init(&battery->sysfs_lock);
if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle,
"_BIX", &handle)))
set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
- acpi_battery_update(battery);
+ result = acpi_battery_update(battery);
+ if (result)
+ goto fail;
#ifdef CONFIG_ACPI_PROCFS_POWER
result = acpi_battery_add_fs(device);
#endif
- if (!result) {
- printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
- ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
- device->status.battery_present ? "present" : "absent");
- } else {
+ if (result) {
#ifdef CONFIG_ACPI_PROCFS_POWER
acpi_battery_remove_fs(device);
#endif
- kfree(battery);
+ goto fail;
}
+ printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
+ ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
+ device->status.battery_present ? "present" : "absent");
+
battery->pm_nb.notifier_call = battery_notify;
register_pm_notifier(&battery->pm_nb);
return result;
+
+fail:
+ sysfs_remove_battery(battery);
+ mutex_destroy(&battery->lock);
+ mutex_destroy(&battery->sysfs_lock);
+ kfree(battery);
+ return result;
}
static int acpi_battery_remove(struct acpi_device *device, int type)
@@ -1009,6 +1030,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type)
#endif
sysfs_remove_battery(battery);
mutex_destroy(&battery->lock);
+ mutex_destroy(&battery->sysfs_lock);
kfree(battery);
return 0;
}
diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c
index af308d03f492..cb9629638def 100644
--- a/drivers/acpi/blacklist.c
+++ b/drivers/acpi/blacklist.c
@@ -28,7 +28,6 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index d1e06c182cdb..9ecec98bc76e 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -39,6 +39,7 @@
#include <linux/pci.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
+#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
@@ -519,6 +520,7 @@ out_kfree:
}
EXPORT_SYMBOL(acpi_run_osc);
+bool osc_sb_apei_support_acked;
static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48";
static void acpi_bus_osc_support(void)
{
@@ -541,11 +543,19 @@ static void acpi_bus_osc_support(void)
#if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE)
capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_PPC_OST_SUPPORT;
#endif
+
+ if (!ghes_disable)
+ capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_APEI_SUPPORT;
if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)))
return;
- if (ACPI_SUCCESS(acpi_run_osc(handle, &context)))
+ if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) {
+ u32 *capbuf_ret = context.ret.pointer;
+ if (context.ret.length > OSC_SUPPORT_TYPE)
+ osc_sb_apei_support_acked =
+ capbuf_ret[OSC_SUPPORT_TYPE] & OSC_SB_APEI_SUPPORT;
kfree(context.ret.pointer);
- /* do we need to check the returned cap? Sounds no */
+ }
+ /* do we need to check other returned cap? Sounds no */
}
/* --------------------------------------------------------------------------
@@ -901,10 +911,7 @@ void __init acpi_early_init(void)
}
#endif
- status =
- acpi_enable_subsystem(~
- (ACPI_NO_HARDWARE_INIT |
- ACPI_NO_ACPI_ENABLE));
+ status = acpi_enable_subsystem(~ACPI_NO_ACPI_ENABLE);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX "Unable to enable ACPI\n");
goto error0;
@@ -925,8 +932,7 @@ static int __init acpi_bus_init(void)
acpi_os_initialize1();
- status =
- acpi_enable_subsystem(ACPI_NO_HARDWARE_INIT | ACPI_NO_ACPI_ENABLE);
+ status = acpi_enable_subsystem(ACPI_NO_ACPI_ENABLE);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX
"Unable to start the ACPI Interpreter\n");
diff --git a/drivers/acpi/debugfs.c b/drivers/acpi/debugfs.c
index 182a9fc36355..b55d6a20dc0e 100644
--- a/drivers/acpi/debugfs.c
+++ b/drivers/acpi/debugfs.c
@@ -2,6 +2,7 @@
* debugfs.c - ACPI debugfs interface to userspace.
*/
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/debugfs.h>
#include <acpi/acpi_drivers.h>
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 1864ad3cf895..19a61136d848 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -77,7 +77,7 @@ struct dock_dependent_device {
struct list_head list;
struct list_head hotplug_list;
acpi_handle handle;
- struct acpi_dock_ops *ops;
+ const struct acpi_dock_ops *ops;
void *context;
};
@@ -589,7 +589,7 @@ EXPORT_SYMBOL_GPL(unregister_dock_notifier);
* the dock driver after _DCK is executed.
*/
int
-register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops,
+register_hotplug_dock_device(acpi_handle handle, const struct acpi_dock_ops *ops,
void *context)
{
struct dock_dependent_device *dd;
diff --git a/drivers/acpi/ec_sys.c b/drivers/acpi/ec_sys.c
index 05b44201a614..6c47ae9793a7 100644
--- a/drivers/acpi/ec_sys.c
+++ b/drivers/acpi/ec_sys.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/debugfs.h>
+#include <linux/module.h>
#include "internal.h"
MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
@@ -92,7 +93,7 @@ static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf,
return count;
}
-static struct file_operations acpi_ec_io_ops = {
+static const struct file_operations acpi_ec_io_ops = {
.owner = THIS_MODULE,
.open = acpi_ec_open_io,
.read = acpi_ec_read_io,
diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c
index 85d908993809..1442737cedec 100644
--- a/drivers/acpi/event.c
+++ b/drivers/acpi/event.c
@@ -7,6 +7,7 @@
*/
#include <linux/spinlock.h>
+#include <linux/export.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/poll.h>
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 467479f07c1f..0f0356ca1a9e 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -110,7 +110,7 @@ fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
return result;
}
-static struct thermal_cooling_device_ops fan_cooling_ops = {
+static const struct thermal_cooling_device_ops fan_cooling_ops = {
.get_max_state = fan_get_max_state,
.get_cur_state = fan_get_cur_state,
.set_cur_state = fan_set_cur_state,
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 7c47ed55e528..29a4a5c8ee00 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -6,6 +6,7 @@
*
* This file is released under the GPLv2.
*/
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/device.h>
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 52ca9649d769..f31c5c5f1b7e 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -80,7 +80,8 @@ static acpi_osd_handler acpi_irq_handler;
static void *acpi_irq_context;
static struct workqueue_struct *kacpid_wq;
static struct workqueue_struct *kacpi_notify_wq;
-static struct workqueue_struct *kacpi_hotplug_wq;
+struct workqueue_struct *kacpi_hotplug_wq;
+EXPORT_SYMBOL(kacpi_hotplug_wq);
struct acpi_res_list {
resource_size_t start;
@@ -155,7 +156,7 @@ static u32 acpi_osi_handler(acpi_string interface, u32 supported)
{
if (!strcmp("Linux", interface)) {
- printk(KERN_NOTICE FW_BUG PREFIX
+ printk_once(KERN_NOTICE FW_BUG PREFIX
"BIOS _OSI(Linux) query %s%s\n",
osi_linux.enable ? "honored" : "ignored",
osi_linux.cmdline ? " via cmdline" :
@@ -237,8 +238,23 @@ void acpi_os_vprintf(const char *fmt, va_list args)
#endif
}
+#ifdef CONFIG_KEXEC
+static unsigned long acpi_rsdp;
+static int __init setup_acpi_rsdp(char *arg)
+{
+ acpi_rsdp = simple_strtoul(arg, NULL, 16);
+ return 0;
+}
+early_param("acpi_rsdp", setup_acpi_rsdp);
+#endif
+
acpi_physical_address __init acpi_os_get_root_pointer(void)
{
+#ifdef CONFIG_KEXEC
+ if (acpi_rsdp)
+ return acpi_rsdp;
+#endif
+
if (efi_enabled) {
if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
return efi.acpi20;
@@ -1083,7 +1099,13 @@ struct osi_setup_entry {
bool enable;
};
-static struct osi_setup_entry __initdata osi_setup_entries[OSI_STRING_ENTRIES_MAX];
+static struct osi_setup_entry __initdata
+ osi_setup_entries[OSI_STRING_ENTRIES_MAX] = {
+ {"Module Device", true},
+ {"Processor Device", true},
+ {"3.0 _SCP Extensions", true},
+ {"Processor Aggregator Device", true},
+};
void __init acpi_osi_setup(char *str)
{
@@ -1333,23 +1355,6 @@ int acpi_resources_are_enforced(void)
EXPORT_SYMBOL(acpi_resources_are_enforced);
/*
- * Create and initialize a spinlock.
- */
-acpi_status
-acpi_os_create_lock(acpi_spinlock *out_handle)
-{
- spinlock_t *lock;
-
- lock = ACPI_ALLOCATE(sizeof(spinlock_t));
- if (!lock)
- return AE_NO_MEMORY;
- spin_lock_init(lock);
- *out_handle = lock;
-
- return AE_OK;
-}
-
-/*
* Deallocate the memory for a spinlock.
*/
void acpi_os_delete_lock(acpi_spinlock handle)
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index f907cfbfa13c..7f9eba9a0b02 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -303,6 +303,61 @@ void acpi_pci_irq_del_prt(struct pci_bus *bus)
/* --------------------------------------------------------------------------
PCI Interrupt Routing Support
-------------------------------------------------------------------------- */
+#ifdef CONFIG_X86_IO_APIC
+extern int noioapicquirk;
+extern int noioapicreroute;
+
+static int bridge_has_boot_interrupt_variant(struct pci_bus *bus)
+{
+ struct pci_bus *bus_it;
+
+ for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) {
+ if (!bus_it->self)
+ return 0;
+ if (bus_it->self->irq_reroute_variant)
+ return bus_it->self->irq_reroute_variant;
+ }
+ return 0;
+}
+
+/*
+ * Some chipsets (e.g. Intel 6700PXH) generate a legacy INTx when the IRQ
+ * entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel does
+ * during interrupt handling). When this INTx generation cannot be disabled,
+ * we reroute these interrupts to their legacy equivalent to get rid of
+ * spurious interrupts.
+ */
+static int acpi_reroute_boot_interrupt(struct pci_dev *dev,
+ struct acpi_prt_entry *entry)
+{
+ if (noioapicquirk || noioapicreroute) {
+ return 0;
+ } else {
+ switch (bridge_has_boot_interrupt_variant(dev->bus)) {
+ case 0:
+ /* no rerouting necessary */
+ return 0;
+ case INTEL_IRQ_REROUTE_VARIANT:
+ /*
+ * Remap according to INTx routing table in 6700PXH
+ * specs, intel order number 302628-002, section
+ * 2.15.2. Other chipsets (80332, ...) have the same
+ * mapping and are handled here as well.
+ */
+ dev_info(&dev->dev, "PCI IRQ %d -> rerouted to legacy "
+ "IRQ %d\n", entry->index,
+ (entry->index % 4) + 16);
+ entry->index = (entry->index % 4) + 16;
+ return 1;
+ default:
+ dev_warn(&dev->dev, "Cannot reroute IRQ %d to legacy "
+ "IRQ: unknown mapping\n", entry->index);
+ return -1;
+ }
+ }
+}
+#endif /* CONFIG_X86_IO_APIC */
+
static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
{
struct acpi_prt_entry *entry;
@@ -311,6 +366,9 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
entry = acpi_pci_irq_find_prt_entry(dev, pin);
if (entry) {
+#ifdef CONFIG_X86_IO_APIC
+ acpi_reroute_boot_interrupt(dev, entry);
+#endif /* CONFIG_X86_IO_APIC */
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %s[%c] _PRT entry\n",
pci_name(dev), pin_name(pin)));
return entry;
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index d06078d660ad..2672c798272f 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -485,7 +485,8 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
root->secondary.end = 0xFF;
printk(KERN_WARNING FW_BUG PREFIX
"no secondary bus range in _CRS\n");
- status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN, NULL, &bus);
+ status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN,
+ NULL, &bus);
if (ACPI_SUCCESS(status))
root->secondary.start = bus;
else if (status == AE_NOT_FOUND)
diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c
index f5f986991b52..251c7b6273a9 100644
--- a/drivers/acpi/proc.c
+++ b/drivers/acpi/proc.c
@@ -1,5 +1,6 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
+#include <linux/export.h>
#include <linux/suspend.h>
#include <linux/bcd.h>
#include <asm/uaccess.h>
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 02d2a4c9084d..3a0428e8435c 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -7,6 +7,7 @@
* Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
* - Added _PDC for platforms with Intel CPUs
*/
+#include <linux/export.h>
#include <linux/dmi.h>
#include <linux/slab.h>
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index a4e0f1ba6040..9d7bc9f6b6cc 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -426,7 +426,7 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb,
if (action == CPU_ONLINE && pr) {
acpi_processor_ppc_has_changed(pr, 0);
- acpi_processor_cst_has_changed(pr);
+ acpi_processor_hotplug(pr);
acpi_processor_reevaluate_tstate(pr, action);
acpi_processor_tstate_has_changed(pr);
}
@@ -503,8 +503,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
acpi_processor_get_throttling_info(pr);
acpi_processor_get_limit_info(pr);
-
- if (cpuidle_get_driver() == &acpi_idle_driver)
+ if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
acpi_processor_power_init(pr, device);
pr->cdev = thermal_cooling_device_register("Processor", device,
@@ -800,17 +799,9 @@ static int __init acpi_processor_init(void)
memset(&errata, 0, sizeof(errata));
- if (!cpuidle_register_driver(&acpi_idle_driver)) {
- printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n",
- acpi_idle_driver.name);
- } else {
- printk(KERN_DEBUG "ACPI: acpi_idle yielding to %s\n",
- cpuidle_get_driver()->name);
- }
-
result = acpi_bus_register_driver(&acpi_processor_driver);
if (result < 0)
- goto out_cpuidle;
+ return result;
acpi_processor_install_hotplug_notify();
@@ -821,11 +812,6 @@ static int __init acpi_processor_init(void)
acpi_processor_throttling_init();
return 0;
-
-out_cpuidle:
- cpuidle_unregister_driver(&acpi_idle_driver);
-
- return result;
}
static void __exit acpi_processor_exit(void)
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 431ab11c8c1b..0e8e2de2ed3e 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -37,7 +37,7 @@
#include <linux/dmi.h>
#include <linux/moduleparam.h>
#include <linux/sched.h> /* need_resched() */
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/clockchips.h>
#include <linux/cpuidle.h>
#include <linux/irqflags.h>
@@ -224,7 +224,6 @@ static void lapic_timer_state_broadcast(struct acpi_processor *pr,
/*
* Suspend / resume control
*/
-static int acpi_idle_suspend;
static u32 saved_bm_rld;
static void acpi_idle_bm_rld_save(void)
@@ -243,21 +242,13 @@ static void acpi_idle_bm_rld_restore(void)
int acpi_processor_suspend(struct acpi_device * device, pm_message_t state)
{
- if (acpi_idle_suspend == 1)
- return 0;
-
acpi_idle_bm_rld_save();
- acpi_idle_suspend = 1;
return 0;
}
int acpi_processor_resume(struct acpi_device * device)
{
- if (acpi_idle_suspend == 0)
- return 0;
-
acpi_idle_bm_rld_restore();
- acpi_idle_suspend = 0;
return 0;
}
@@ -741,66 +732,65 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx)
/**
* acpi_idle_enter_c1 - enters an ACPI C1 state-type
* @dev: the target CPU
- * @state: the state data
+ * @drv: cpuidle driver containing cpuidle state info
+ * @index: index of target state
*
* This is equivalent to the HALT instruction.
*/
static int acpi_idle_enter_c1(struct cpuidle_device *dev,
- struct cpuidle_state *state)
+ struct cpuidle_driver *drv, int index)
{
ktime_t kt1, kt2;
s64 idle_time;
struct acpi_processor *pr;
- struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
+ struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+ struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
pr = __this_cpu_read(processors);
+ dev->last_residency = 0;
if (unlikely(!pr))
- return 0;
+ return -EINVAL;
local_irq_disable();
- /* Do not access any ACPI IO ports in suspend path */
- if (acpi_idle_suspend) {
- local_irq_enable();
- cpu_relax();
- return 0;
- }
-
lapic_timer_state_broadcast(pr, cx, 1);
kt1 = ktime_get_real();
acpi_idle_do_entry(cx);
kt2 = ktime_get_real();
idle_time = ktime_to_us(ktime_sub(kt2, kt1));
+ /* Update device last_residency*/
+ dev->last_residency = (int)idle_time;
+
local_irq_enable();
cx->usage++;
lapic_timer_state_broadcast(pr, cx, 0);
- return idle_time;
+ return index;
}
/**
* acpi_idle_enter_simple - enters an ACPI state without BM handling
* @dev: the target CPU
- * @state: the state data
+ * @drv: cpuidle driver with cpuidle state information
+ * @index: the index of suggested state
*/
static int acpi_idle_enter_simple(struct cpuidle_device *dev,
- struct cpuidle_state *state)
+ struct cpuidle_driver *drv, int index)
{
struct acpi_processor *pr;
- struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
+ struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+ struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
ktime_t kt1, kt2;
s64 idle_time_ns;
s64 idle_time;
pr = __this_cpu_read(processors);
+ dev->last_residency = 0;
if (unlikely(!pr))
- return 0;
-
- if (acpi_idle_suspend)
- return(acpi_idle_enter_c1(dev, state));
+ return -EINVAL;
local_irq_disable();
@@ -815,7 +805,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
if (unlikely(need_resched())) {
current_thread_info()->status |= TS_POLLING;
local_irq_enable();
- return 0;
+ return -EINVAL;
}
}
@@ -837,6 +827,9 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
idle_time = idle_time_ns;
do_div(idle_time, NSEC_PER_USEC);
+ /* Update device last_residency*/
+ dev->last_residency = (int)idle_time;
+
/* Tell the scheduler how much we idled: */
sched_clock_idle_wakeup_event(idle_time_ns);
@@ -848,46 +841,46 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
lapic_timer_state_broadcast(pr, cx, 0);
cx->time += idle_time;
- return idle_time;
+ return index;
}
static int c3_cpu_count;
-static DEFINE_SPINLOCK(c3_lock);
+static DEFINE_RAW_SPINLOCK(c3_lock);
/**
* acpi_idle_enter_bm - enters C3 with proper BM handling
* @dev: the target CPU
- * @state: the state data
+ * @drv: cpuidle driver containing state data
+ * @index: the index of suggested state
*
* If BM is detected, the deepest non-C3 idle state is entered instead.
*/
static int acpi_idle_enter_bm(struct cpuidle_device *dev,
- struct cpuidle_state *state)
+ struct cpuidle_driver *drv, int index)
{
struct acpi_processor *pr;
- struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
+ struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+ struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
ktime_t kt1, kt2;
s64 idle_time_ns;
s64 idle_time;
pr = __this_cpu_read(processors);
+ dev->last_residency = 0;
if (unlikely(!pr))
- return 0;
-
- if (acpi_idle_suspend)
- return(acpi_idle_enter_c1(dev, state));
+ return -EINVAL;
if (!cx->bm_sts_skip && acpi_idle_bm_check()) {
- if (dev->safe_state) {
- dev->last_state = dev->safe_state;
- return dev->safe_state->enter(dev, dev->safe_state);
+ if (drv->safe_state_index >= 0) {
+ return drv->states[drv->safe_state_index].enter(dev,
+ drv, drv->safe_state_index);
} else {
local_irq_disable();
acpi_safe_halt();
local_irq_enable();
- return 0;
+ return -EINVAL;
}
}
@@ -904,7 +897,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
if (unlikely(need_resched())) {
current_thread_info()->status |= TS_POLLING;
local_irq_enable();
- return 0;
+ return -EINVAL;
}
}
@@ -930,12 +923,12 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
* without doing anything.
*/
if (pr->flags.bm_check && pr->flags.bm_control) {
- spin_lock(&c3_lock);
+ raw_spin_lock(&c3_lock);
c3_cpu_count++;
/* Disable bus master arbitration when all CPUs are in C3 */
if (c3_cpu_count == num_online_cpus())
acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1);
- spin_unlock(&c3_lock);
+ raw_spin_unlock(&c3_lock);
} else if (!pr->flags.bm_check) {
ACPI_FLUSH_CPU_CACHE();
}
@@ -944,16 +937,19 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
/* Re-enable bus master arbitration */
if (pr->flags.bm_check && pr->flags.bm_control) {
- spin_lock(&c3_lock);
+ raw_spin_lock(&c3_lock);
acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0);
c3_cpu_count--;
- spin_unlock(&c3_lock);
+ raw_spin_unlock(&c3_lock);
}
kt2 = ktime_get_real();
idle_time_ns = ktime_to_ns(ktime_sub(kt2, kt1));
idle_time = idle_time_ns;
do_div(idle_time, NSEC_PER_USEC);
+ /* Update device last_residency*/
+ dev->last_residency = (int)idle_time;
+
/* Tell the scheduler how much we idled: */
sched_clock_idle_wakeup_event(idle_time_ns);
@@ -965,7 +961,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
lapic_timer_state_broadcast(pr, cx, 0);
cx->time += idle_time;
- return idle_time;
+ return index;
}
struct cpuidle_driver acpi_idle_driver = {
@@ -974,14 +970,16 @@ struct cpuidle_driver acpi_idle_driver = {
};
/**
- * acpi_processor_setup_cpuidle - prepares and configures CPUIDLE
+ * acpi_processor_setup_cpuidle_cx - prepares and configures CPUIDLE
+ * device i.e. per-cpu data
+ *
* @pr: the ACPI processor
*/
-static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
+static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr)
{
int i, count = CPUIDLE_DRIVER_STATE_START;
struct acpi_processor_cx *cx;
- struct cpuidle_state *state;
+ struct cpuidle_state_usage *state_usage;
struct cpuidle_device *dev = &pr->power.dev;
if (!pr->flags.power_setup_done)
@@ -992,9 +990,62 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
}
dev->cpu = pr->id;
+
+ if (max_cstate == 0)
+ max_cstate = 1;
+
+ for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
+ cx = &pr->power.states[i];
+ state_usage = &dev->states_usage[count];
+
+ if (!cx->valid)
+ continue;
+
+#ifdef CONFIG_HOTPLUG_CPU
+ if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) &&
+ !pr->flags.has_cst &&
+ !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED))
+ continue;
+#endif
+
+ cpuidle_set_statedata(state_usage, cx);
+
+ count++;
+ if (count == CPUIDLE_STATE_MAX)
+ break;
+ }
+
+ dev->state_count = count;
+
+ if (!count)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * acpi_processor_setup_cpuidle states- prepares and configures cpuidle
+ * global state data i.e. idle routines
+ *
+ * @pr: the ACPI processor
+ */
+static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
+{
+ int i, count = CPUIDLE_DRIVER_STATE_START;
+ struct acpi_processor_cx *cx;
+ struct cpuidle_state *state;
+ struct cpuidle_driver *drv = &acpi_idle_driver;
+
+ if (!pr->flags.power_setup_done)
+ return -EINVAL;
+
+ if (pr->flags.power == 0)
+ return -EINVAL;
+
+ drv->safe_state_index = -1;
for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
- dev->states[i].name[0] = '\0';
- dev->states[i].desc[0] = '\0';
+ drv->states[i].name[0] = '\0';
+ drv->states[i].desc[0] = '\0';
}
if (max_cstate == 0)
@@ -1002,7 +1053,6 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
cx = &pr->power.states[i];
- state = &dev->states[count];
if (!cx->valid)
continue;
@@ -1013,8 +1063,8 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
!(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED))
continue;
#endif
- cpuidle_set_statedata(state, cx);
+ state = &drv->states[count];
snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i);
strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
state->exit_latency = cx->latency;
@@ -1027,13 +1077,13 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
state->flags |= CPUIDLE_FLAG_TIME_VALID;
state->enter = acpi_idle_enter_c1;
- dev->safe_state = state;
+ drv->safe_state_index = count;
break;
case ACPI_STATE_C2:
state->flags |= CPUIDLE_FLAG_TIME_VALID;
state->enter = acpi_idle_enter_simple;
- dev->safe_state = state;
+ drv->safe_state_index = count;
break;
case ACPI_STATE_C3:
@@ -1049,7 +1099,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
break;
}
- dev->state_count = count;
+ drv->state_count = count;
if (!count)
return -EINVAL;
@@ -1057,7 +1107,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
return 0;
}
-int acpi_processor_cst_has_changed(struct acpi_processor *pr)
+int acpi_processor_hotplug(struct acpi_processor *pr)
{
int ret = 0;
@@ -1078,7 +1128,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
cpuidle_disable_device(&pr->power.dev);
acpi_processor_get_power_info(pr);
if (pr->flags.power) {
- acpi_processor_setup_cpuidle(pr);
+ acpi_processor_setup_cpuidle_cx(pr);
ret = cpuidle_enable_device(&pr->power.dev);
}
cpuidle_resume_and_unlock();
@@ -1086,10 +1136,72 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
return ret;
}
+int acpi_processor_cst_has_changed(struct acpi_processor *pr)
+{
+ int cpu;
+ struct acpi_processor *_pr;
+
+ if (disabled_by_idle_boot_param())
+ return 0;
+
+ if (!pr)
+ return -EINVAL;
+
+ if (nocst)
+ return -ENODEV;
+
+ if (!pr->flags.power_setup_done)
+ return -ENODEV;
+
+ /*
+ * FIXME: Design the ACPI notification to make it once per
+ * system instead of once per-cpu. This condition is a hack
+ * to make the code that updates C-States be called once.
+ */
+
+ if (smp_processor_id() == 0 &&
+ cpuidle_get_driver() == &acpi_idle_driver) {
+
+ cpuidle_pause_and_lock();
+ /* Protect against cpu-hotplug */
+ get_online_cpus();
+
+ /* Disable all cpuidle devices */
+ for_each_online_cpu(cpu) {
+ _pr = per_cpu(processors, cpu);
+ if (!_pr || !_pr->flags.power_setup_done)
+ continue;
+ cpuidle_disable_device(&_pr->power.dev);
+ }
+
+ /* Populate Updated C-state information */
+ acpi_processor_setup_cpuidle_states(pr);
+
+ /* Enable all cpuidle devices */
+ for_each_online_cpu(cpu) {
+ _pr = per_cpu(processors, cpu);
+ if (!_pr || !_pr->flags.power_setup_done)
+ continue;
+ acpi_processor_get_power_info(_pr);
+ if (_pr->flags.power) {
+ acpi_processor_setup_cpuidle_cx(_pr);
+ cpuidle_enable_device(&_pr->power.dev);
+ }
+ }
+ put_online_cpus();
+ cpuidle_resume_and_unlock();
+ }
+
+ return 0;
+}
+
+static int acpi_processor_registered;
+
int __cpuinit acpi_processor_power_init(struct acpi_processor *pr,
struct acpi_device *device)
{
acpi_status status = 0;
+ int retval;
static int first_run;
if (disabled_by_idle_boot_param())
@@ -1126,9 +1238,26 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr,
* platforms that only support C1.
*/
if (pr->flags.power) {
- acpi_processor_setup_cpuidle(pr);
- if (cpuidle_register_device(&pr->power.dev))
- return -EIO;
+ /* Register acpi_idle_driver if not already registered */
+ if (!acpi_processor_registered) {
+ acpi_processor_setup_cpuidle_states(pr);
+ retval = cpuidle_register_driver(&acpi_idle_driver);
+ if (retval)
+ return retval;
+ printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n",
+ acpi_idle_driver.name);
+ }
+ /* Register per-cpu cpuidle_device. Cpuidle driver
+ * must already be registered before registering device
+ */
+ acpi_processor_setup_cpuidle_cx(pr);
+ retval = cpuidle_register_device(&pr->power.dev);
+ if (retval) {
+ if (acpi_processor_registered == 0)
+ cpuidle_unregister_driver(&acpi_idle_driver);
+ return retval;
+ }
+ acpi_processor_registered++;
}
return 0;
}
@@ -1139,8 +1268,13 @@ int acpi_processor_power_exit(struct acpi_processor *pr,
if (disabled_by_idle_boot_param())
return 0;
- cpuidle_unregister_device(&pr->power.dev);
- pr->flags.power_setup_done = 0;
+ if (pr->flags.power) {
+ cpuidle_unregister_device(&pr->power.dev);
+ acpi_processor_registered--;
+ if (acpi_processor_registered == 0)
+ cpuidle_unregister_driver(&acpi_idle_driver);
+ }
+ pr->flags.power_setup_done = 0;
return 0;
}
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
index 79cb65332894..870550d6a4bf 100644
--- a/drivers/acpi/processor_thermal.c
+++ b/drivers/acpi/processor_thermal.c
@@ -244,7 +244,7 @@ processor_set_cur_state(struct thermal_cooling_device *cdev,
return result;
}
-struct thermal_cooling_device_ops processor_cooling_ops = {
+const struct thermal_cooling_device_ops processor_cooling_ops = {
.get_max_state = processor_get_max_state,
.get_cur_state = processor_get_cur_state,
.set_cur_state = processor_set_cur_state,
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index 51ae3794ec7f..6e36d0c0057c 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -112,7 +112,7 @@ struct acpi_battery {
u8 have_sysfs_alarm:1;
};
-#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat);
+#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat)
struct acpi_sbs {
struct power_supply charger;
@@ -130,6 +130,9 @@ struct acpi_sbs {
#define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger)
+static int acpi_sbs_remove(struct acpi_device *device, int type);
+static int acpi_battery_get_state(struct acpi_battery *battery);
+
static inline int battery_scale(int log)
{
int scale = 1;
@@ -195,6 +198,8 @@ static int acpi_sbs_battery_get_property(struct power_supply *psy,
if ((!battery->present) && psp != POWER_SUPPLY_PROP_PRESENT)
return -ENODEV;
+
+ acpi_battery_get_state(battery);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (battery->rate_now < 0)
@@ -225,11 +230,17 @@ static int acpi_sbs_battery_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_POWER_NOW:
val->intval = abs(battery->rate_now) *
acpi_battery_ipscale(battery) * 1000;
+ val->intval *= (acpi_battery_mode(battery)) ?
+ (battery->voltage_now *
+ acpi_battery_vscale(battery) / 1000) : 1;
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
case POWER_SUPPLY_PROP_POWER_AVG:
val->intval = abs(battery->rate_avg) *
acpi_battery_ipscale(battery) * 1000;
+ val->intval *= (acpi_battery_mode(battery)) ?
+ (battery->voltage_now *
+ acpi_battery_vscale(battery) / 1000) : 1;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = battery->state_of_charge;
@@ -903,8 +914,6 @@ static void acpi_sbs_callback(void *context)
}
}
-static int acpi_sbs_remove(struct acpi_device *device, int type);
-
static int acpi_sbs_add(struct acpi_device *device)
{
struct acpi_sbs *sbs;
diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c
index f8be23b6c129..f8d2a472795c 100644
--- a/drivers/acpi/sbshc.c
+++ b/drivers/acpi/sbshc.c
@@ -13,6 +13,7 @@
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/interrupt.h>
#include "sbshc.h"
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 449c556274c0..8ab80bafe3f1 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1062,13 +1062,12 @@ static void acpi_add_id(struct acpi_device *device, const char *dev_id)
if (!id)
return;
- id->id = kmalloc(strlen(dev_id) + 1, GFP_KERNEL);
+ id->id = kstrdup(dev_id, GFP_KERNEL);
if (!id->id) {
kfree(id);
return;
}
- strcpy(id->id, dev_id);
list_add_tail(&id->list, &device->pnp.ids);
}
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 6c949602cbd1..6d9a3ab58db2 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -390,6 +390,22 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
},
{
.callback = init_nvs_nosave,
+ .ident = "Sony Vaio VGN-FW21E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21E"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VPCEB17FX",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB17FX"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
.ident = "Sony Vaio VGN-SR11M",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
@@ -428,6 +444,38 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "1000 Series"),
},
},
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "Asus A8N-SLI DELUXE",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI DELUXE"),
+ },
+ },
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "Asus A8N-SLI Premium",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI Premium"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VGN-SR26GN_P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR26GN_P"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VGN-FW520F",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW520F"),
+ },
+ },
{},
};
#endif /* CONFIG_SUSPEND */
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index 77255f250dbb..9f66181c814e 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -149,12 +149,12 @@ static int param_get_debug_level(char *buffer, const struct kernel_param *kp)
return result;
}
-static struct kernel_param_ops param_ops_debug_layer = {
+static const struct kernel_param_ops param_ops_debug_layer = {
.set = param_set_uint,
.get = param_get_debug_layer,
};
-static struct kernel_param_ops param_ops_debug_level = {
+static const struct kernel_param_ops param_ops_debug_level = {
.set = param_set_uint,
.get = param_get_debug_level,
};
@@ -706,11 +706,23 @@ static void __exit interrupt_stats_exit(void)
return;
}
+static ssize_t
+acpi_show_profile(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", acpi_gbl_FADT.preferred_profile);
+}
+
+static const struct device_attribute pm_profile_attr =
+ __ATTR(pm_profile, S_IRUGO, acpi_show_profile, NULL);
+
int __init acpi_sysfs_init(void)
{
int result;
result = acpi_tables_sysfs_init();
-
+ if (result)
+ return result;
+ result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr);
return result;
}
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 2607e17b520f..48fbc647b178 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -812,7 +812,7 @@ acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
thermal_zone_unbind_cooling_device);
}
-static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
+static const struct thermal_zone_device_ops acpi_thermal_zone_ops = {
.bind = acpi_thermal_bind_cooling_device,
.unbind = acpi_thermal_unbind_cooling_device,
.get_temp = thermal_get_temp,
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index db39e9e607d8..08a44b532f7c 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -46,7 +46,6 @@
#define PREFIX "ACPI: "
-#define ACPI_VIDEO_CLASS "video"
#define ACPI_VIDEO_BUS_NAME "Video Bus"
#define ACPI_VIDEO_DEVICE_NAME "Video Device"
#define ACPI_VIDEO_NOTIFY_SWITCH 0x80
@@ -308,7 +307,7 @@ video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long st
return acpi_video_device_lcd_set_level(video, level);
}
-static struct thermal_cooling_device_ops video_cooling_ops = {
+static const struct thermal_cooling_device_ops video_cooling_ops = {
.get_max_state = video_get_max_state,
.get_cur_state = video_get_cur_state,
.set_cur_state = video_set_cur_state,
@@ -1445,7 +1444,8 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch,
* most likely via hotkey. */
acpi_bus_generate_proc_event(device, event, 0);
- keycode = KEY_SWITCHVIDEOMODE;
+ if (!acpi_notifier_call_chain(device, event, 0))
+ keycode = KEY_SWITCHVIDEOMODE;
break;
case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video
@@ -1475,7 +1475,8 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
break;
}
- acpi_notifier_call_chain(device, event, 0);
+ if (event != ACPI_VIDEO_NOTIFY_SWITCH)
+ acpi_notifier_call_chain(device, event, 0);
if (keycode) {
input_report_key(input, keycode, 1);
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index 5af3479714f6..f3f0fe7e255a 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -33,6 +33,7 @@
*
*/
+#include <linux/export.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/pci.h>
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index d74926e0939e..bd230e801131 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -365,6 +365,40 @@ static int amba_pm_restore_noirq(struct device *dev)
#endif /* !CONFIG_HIBERNATE_CALLBACKS */
+#ifdef CONFIG_PM_RUNTIME
+/*
+ * Hooks to provide runtime PM of the pclk (bus clock). It is safe to
+ * enable/disable the bus clock at runtime PM suspend/resume as this
+ * does not result in loss of context. However, disabling vcore power
+ * would do, so we leave that to the driver.
+ */
+static int amba_pm_runtime_suspend(struct device *dev)
+{
+ struct amba_device *pcdev = to_amba_device(dev);
+ int ret = pm_generic_runtime_suspend(dev);
+
+ if (ret == 0 && dev->driver)
+ clk_disable(pcdev->pclk);
+
+ return ret;
+}
+
+static int amba_pm_runtime_resume(struct device *dev)
+{
+ struct amba_device *pcdev = to_amba_device(dev);
+ int ret;
+
+ if (dev->driver) {
+ ret = clk_enable(pcdev->pclk);
+ /* Failure is probably fatal to the system, but... */
+ if (ret)
+ return ret;
+ }
+
+ return pm_generic_runtime_resume(dev);
+}
+#endif
+
#ifdef CONFIG_PM
static const struct dev_pm_ops amba_pm = {
@@ -383,8 +417,8 @@ static const struct dev_pm_ops amba_pm = {
.poweroff_noirq = amba_pm_poweroff_noirq,
.restore_noirq = amba_pm_restore_noirq,
SET_RUNTIME_PM_OPS(
- pm_generic_runtime_suspend,
- pm_generic_runtime_resume,
+ amba_pm_runtime_suspend,
+ amba_pm_runtime_resume,
pm_generic_runtime_idle
)
};
@@ -426,9 +460,17 @@ static int amba_get_enable_pclk(struct amba_device *pcdev)
if (IS_ERR(pclk))
return PTR_ERR(pclk);
+ ret = clk_prepare(pclk);
+ if (ret) {
+ clk_put(pclk);
+ return ret;
+ }
+
ret = clk_enable(pclk);
- if (ret)
+ if (ret) {
+ clk_unprepare(pclk);
clk_put(pclk);
+ }
return ret;
}
@@ -438,6 +480,7 @@ static void amba_put_disable_pclk(struct amba_device *pcdev)
struct clk *pclk = pcdev->pclk;
clk_disable(pclk);
+ clk_unprepare(pclk);
clk_put(pclk);
}
@@ -494,10 +537,18 @@ static int amba_probe(struct device *dev)
if (ret)
break;
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
ret = pcdrv->probe(pcdev, id);
if (ret == 0)
break;
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+
amba_put_disable_pclk(pcdev);
amba_put_disable_vcore(pcdev);
} while (0);
@@ -509,7 +560,16 @@ static int amba_remove(struct device *dev)
{
struct amba_device *pcdev = to_amba_device(dev);
struct amba_driver *drv = to_amba_driver(dev->driver);
- int ret = drv->remove(pcdev);
+ int ret;
+
+ pm_runtime_get_sync(dev);
+ ret = drv->remove(pcdev);
+ pm_runtime_put_noidle(dev);
+
+ /* Undo the runtime PM settings in amba_probe() */
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
amba_put_disable_pclk(pcdev);
amba_put_disable_vcore(pcdev);
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 75afa75a515e..6bdedd7cca2c 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -313,6 +313,7 @@ config PATA_AMD
config PATA_ARASAN_CF
tristate "ARASAN CompactFlash PATA Controller Support"
+ depends on DMADEVICES
select DMA_ENGINE
help
Say Y here to support the ARASAN CompactFlash PATA controller
@@ -467,6 +468,15 @@ config PATA_ICSIDE
interface card. This is not required for ICS partition support.
If you are unsure, say N to this.
+config PATA_IMX
+ tristate "PATA support for Freescale iMX"
+ depends on ARCH_MXC
+ help
+ This option enables support for the PATA host available on Freescale
+ iMX SoCs.
+
+ If unsure, say N.
+
config PATA_IT8213
tristate "IT8213 PATA support (Experimental)"
depends on PCI && EXPERIMENTAL
@@ -810,7 +820,7 @@ config PATA_PLATFORM
config PATA_OF_PLATFORM
tristate "OpenFirmware platform device PATA support"
- depends on PATA_PLATFORM && PPC_OF
+ depends on PATA_PLATFORM && OF
help
This option enables support for generic directly connected ATA
devices commonly found on embedded systems with OpenFirmware
@@ -821,6 +831,7 @@ config PATA_OF_PLATFORM
config PATA_QDI
tristate "QDI VLB PATA support"
depends on ISA
+ select PATA_LEGACY
help
Support for QDI 6500 and 6580 PATA controllers on VESA local bus.
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 8ac64e1aa051..6ece5b7231a3 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_PATA_HPT37X) += pata_hpt37x.o
obj-$(CONFIG_PATA_HPT3X2N) += pata_hpt3x2n.o
obj-$(CONFIG_PATA_HPT3X3) += pata_hpt3x3.o
obj-$(CONFIG_PATA_ICSIDE) += pata_icside.o
+obj-$(CONFIG_PATA_IMX) += pata_imx.o
obj-$(CONFIG_PATA_IT8213) += pata_it8213.o
obj-$(CONFIG_PATA_IT821X) += pata_it821x.o
obj-$(CONFIG_PATA_JMICRON) += pata_jmicron.o
@@ -87,7 +88,6 @@ obj-$(CONFIG_PATA_PCMCIA) += pata_pcmcia.o
obj-$(CONFIG_PATA_PALMLD) += pata_palmld.o
obj-$(CONFIG_PATA_PLATFORM) += pata_platform.o
obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o
-obj-$(CONFIG_PATA_QDI) += pata_qdi.o
obj-$(CONFIG_PATA_RB532) += pata_rb532_cf.o
obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o
obj-$(CONFIG_PATA_SAMSUNG_CF) += pata_samsung_cf.o
diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c
index ae22be4157b5..3bc8c79bf2c7 100644
--- a/drivers/ata/acard-ahci.c
+++ b/drivers/ata/acard-ahci.c
@@ -135,8 +135,8 @@ static int acard_ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg
if (mesg.event & PM_EVENT_SUSPEND &&
hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
- dev_printk(KERN_ERR, &pdev->dev,
- "BIOS update required for suspend/resume\n");
+ dev_err(&pdev->dev,
+ "BIOS update required for suspend/resume\n");
return -EIO;
}
@@ -187,7 +187,7 @@ static int acard_ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac)
if (rc) {
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
+ dev_err(&pdev->dev,
"64-bit DMA enable failed\n");
return rc;
}
@@ -195,14 +195,13 @@ static int acard_ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac)
} else {
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit DMA enable failed\n");
+ dev_err(&pdev->dev, "32-bit DMA enable failed\n");
return rc;
}
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit consistent DMA enable failed\n");
+ dev_err(&pdev->dev,
+ "32-bit consistent DMA enable failed\n");
return rc;
}
}
@@ -343,14 +342,12 @@ static int acard_ahci_port_start(struct ata_port *ap)
if (cmd & PORT_CMD_FBSCP)
pp->fbs_supported = true;
else if (hpriv->flags & AHCI_HFLAG_YES_FBS) {
- dev_printk(KERN_INFO, dev,
- "port %d can do FBS, forcing FBSCP\n",
- ap->port_no);
+ dev_info(dev, "port %d can do FBS, forcing FBSCP\n",
+ ap->port_no);
pp->fbs_supported = true;
} else
- dev_printk(KERN_WARNING, dev,
- "port %d is not capable of FBS\n",
- ap->port_no);
+ dev_warn(dev, "port %d is not capable of FBS\n",
+ ap->port_no);
}
if (pp->fbs_supported) {
@@ -406,7 +403,6 @@ static int acard_ahci_port_start(struct ata_port *ap)
static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
unsigned int board_id = ent->driver_data;
struct ata_port_info pi = acard_ahci_port_info[board_id];
const struct ata_port_info *ppi[] = { &pi, NULL };
@@ -419,8 +415,7 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id
WARN_ON((int)ATA_MAX_QUEUE > AHCI_MAX_CMDS);
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* acquire resources */
rc = pcim_enable_device(pdev);
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 71afe0371311..cf26222a93c5 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -79,8 +79,6 @@ enum board_ids {
};
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
-static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
- unsigned long deadline);
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
@@ -104,12 +102,6 @@ static struct ata_port_operations ahci_p5wdh_ops = {
.hardreset = ahci_p5wdh_hardreset,
};
-static struct ata_port_operations ahci_sb600_ops = {
- .inherits = &ahci_ops,
- .softreset = ahci_sb600_softreset,
- .pmp_softreset = ahci_sb600_softreset,
-};
-
#define AHCI_HFLAGS(flags) .private_data = (void *)(flags)
static const struct ata_port_info ahci_port_info[] = {
@@ -188,7 +180,7 @@ static const struct ata_port_info ahci_port_info[] = {
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
- .port_ops = &ahci_sb600_ops,
+ .port_ops = &ahci_pmp_retry_srst_ops,
},
[board_ahci_sb700] = /* for SB700 and SB800 */
{
@@ -196,7 +188,7 @@ static const struct ata_port_info ahci_port_info[] = {
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
- .port_ops = &ahci_sb600_ops,
+ .port_ops = &ahci_pmp_retry_srst_ops,
},
[board_ahci_vt8251] =
{
@@ -267,6 +259,7 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x1e05), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e06), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e07), board_ahci }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x1e0e), board_ahci }, /* Panther Point RAID */
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
@@ -397,6 +390,9 @@ static const struct pci_device_id ahci_pci_tbl[] = {
/* Promise */
{ PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */
+ /* Asmedia */
+ { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1061 */
+
/* Generic, PCI class code for AHCI */
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci },
@@ -502,55 +498,6 @@ static void ahci_pci_init_controller(struct ata_host *host)
ahci_init_controller(host);
}
-static int ahci_sb600_check_ready(struct ata_link *link)
-{
- void __iomem *port_mmio = ahci_port_base(link->ap);
- u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
- u32 irq_status = readl(port_mmio + PORT_IRQ_STAT);
-
- /*
- * There is no need to check TFDATA if BAD PMP is found due to HW bug,
- * which can save timeout delay.
- */
- if (irq_status & PORT_IRQ_BAD_PMP)
- return -EIO;
-
- return ata_check_ready(status);
-}
-
-static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
- unsigned long deadline)
-{
- struct ata_port *ap = link->ap;
- void __iomem *port_mmio = ahci_port_base(ap);
- int pmp = sata_srst_pmp(link);
- int rc;
- u32 irq_sts;
-
- DPRINTK("ENTER\n");
-
- rc = ahci_do_softreset(link, class, pmp, deadline,
- ahci_sb600_check_ready);
-
- /*
- * Soft reset fails on some ATI chips with IPMS set when PMP
- * is enabled but SATA HDD/ODD is connected to SATA port,
- * do soft reset again to port 0.
- */
- if (rc == -EIO) {
- irq_sts = readl(port_mmio + PORT_IRQ_STAT);
- if (irq_sts & PORT_IRQ_BAD_PMP) {
- ata_link_printk(link, KERN_WARNING,
- "applying SB600 PMP SRST workaround "
- "and retrying\n");
- rc = ahci_do_softreset(link, class, 0, deadline,
- ahci_check_ready);
- }
- }
-
- return rc;
-}
-
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
@@ -629,8 +576,8 @@ static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
if (mesg.event & PM_EVENT_SUSPEND &&
hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
- dev_printk(KERN_ERR, &pdev->dev,
- "BIOS update required for suspend/resume\n");
+ dev_err(&pdev->dev,
+ "BIOS update required for suspend/resume\n");
return -EIO;
}
@@ -681,22 +628,21 @@ static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac)
if (rc) {
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "64-bit DMA enable failed\n");
+ dev_err(&pdev->dev,
+ "64-bit DMA enable failed\n");
return rc;
}
}
} else {
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit DMA enable failed\n");
+ dev_err(&pdev->dev, "32-bit DMA enable failed\n");
return rc;
}
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit consistent DMA enable failed\n");
+ dev_err(&pdev->dev,
+ "32-bit consistent DMA enable failed\n");
return rc;
}
}
@@ -759,8 +705,8 @@ static void ahci_p5wdh_workaround(struct ata_host *host)
dmi_check_system(sysids)) {
struct ata_port *ap = host->ports[1];
- dev_printk(KERN_INFO, &pdev->dev, "enabling ASUS P5W DH "
- "Deluxe on-board SIMG4726 workaround\n");
+ dev_info(&pdev->dev,
+ "enabling ASUS P5W DH Deluxe on-board SIMG4726 workaround\n");
ap->ops = &ahci_p5wdh_ops;
ap->link.flags |= ATA_LFLAG_NO_SRST | ATA_LFLAG_ASSUME_ATA;
@@ -811,6 +757,18 @@ static bool ahci_sb600_enable_64bit(struct pci_dev *pdev)
DMI_MATCH(DMI_BOARD_NAME, "MS-7376"),
},
},
+ /*
+ * All BIOS versions for the Asus M3A support 64bit DMA.
+ * (all release versions from 0301 to 1206 were tested)
+ */
+ {
+ .ident = "ASUS M3A",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR,
+ "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "M3A"),
+ },
+ },
{ }
};
const struct dmi_system_id *match;
@@ -831,14 +789,14 @@ static bool ahci_sb600_enable_64bit(struct pci_dev *pdev)
if (strcmp(buf, match->driver_data) >= 0)
goto enable_64bit;
else {
- dev_printk(KERN_WARNING, &pdev->dev, "%s: BIOS too old, "
- "forcing 32bit DMA, update BIOS\n", match->ident);
+ dev_warn(&pdev->dev,
+ "%s: BIOS too old, forcing 32bit DMA, update BIOS\n",
+ match->ident);
return false;
}
enable_64bit:
- dev_printk(KERN_WARNING, &pdev->dev, "%s: enabling 64bit DMA\n",
- match->ident);
+ dev_warn(&pdev->dev, "%s: enabling 64bit DMA\n", match->ident);
return true;
}
@@ -1041,9 +999,8 @@ static void ahci_gtf_filter_workaround(struct ata_host *host)
return;
filter = (unsigned long)dmi->driver_data;
- dev_printk(KERN_INFO, host->dev,
- "applying extra ACPI _GTF filter 0x%x for %s\n",
- filter, dmi->ident);
+ dev_info(host->dev, "applying extra ACPI _GTF filter 0x%x for %s\n",
+ filter, dmi->ident);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
@@ -1062,7 +1019,6 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
unsigned int board_id = ent->driver_data;
struct ata_port_info pi = ahci_port_info[board_id];
const struct ata_port_info *ppi[] = { &pi, NULL };
@@ -1075,8 +1031,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
WARN_ON((int)ATA_MAX_QUEUE > AHCI_MAX_CMDS);
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* The AHCI driver can only drive the SATA ports, the PATA driver
can drive them all so if both drivers are selected make sure
@@ -1099,8 +1054,8 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
* that for SAS drives they're out of luck.
*/
if (pdev->vendor == PCI_VENDOR_ID_PROMISE)
- dev_printk(KERN_INFO, &pdev->dev, "PDC42819 "
- "can only drive SATA devices with this driver\n");
+ dev_info(&pdev->dev,
+ "PDC42819 can only drive SATA devices with this driver\n");
/* acquire resources */
rc = pcim_enable_device(pdev);
@@ -1126,8 +1081,8 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
*/
pci_read_config_byte(pdev, ICH_MAP, &map);
if (map & 0x3) {
- dev_printk(KERN_INFO, &pdev->dev, "controller is in "
- "combined mode, can't enable AHCI mode\n");
+ dev_info(&pdev->dev,
+ "controller is in combined mode, can't enable AHCI mode\n");
return -ENODEV;
}
}
@@ -1184,8 +1139,8 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ahci_broken_suspend(pdev)) {
hpriv->flags |= AHCI_HFLAG_NO_SUSPEND;
- dev_printk(KERN_WARNING, &pdev->dev,
- "BIOS update required for suspend/resume\n");
+ dev_warn(&pdev->dev,
+ "BIOS update required for suspend/resume\n");
}
if (ahci_broken_online(pdev)) {
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 12c5282e7fca..b1750007c8dc 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -312,6 +312,7 @@ extern struct device_attribute *ahci_sdev_attrs[];
.sdev_attrs = ahci_sdev_attrs
extern struct ata_port_operations ahci_ops;
+extern struct ata_port_operations ahci_pmp_retry_srst_ops;
void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
u32 opts);
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 6fef1fa75c54..43b875810d1b 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -23,6 +23,41 @@
#include <linux/ahci_platform.h>
#include "ahci.h"
+enum ahci_type {
+ AHCI, /* standard platform ahci */
+ IMX53_AHCI, /* ahci on i.mx53 */
+};
+
+static struct platform_device_id ahci_devtype[] = {
+ {
+ .name = "ahci",
+ .driver_data = AHCI,
+ }, {
+ .name = "imx53-ahci",
+ .driver_data = IMX53_AHCI,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, ahci_devtype);
+
+
+static const struct ata_port_info ahci_port_info[] = {
+ /* by features */
+ [AHCI] = {
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_ops,
+ },
+ [IMX53_AHCI] = {
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_pmp_retry_srst_ops,
+ },
+};
+
static struct scsi_host_template ahci_platform_sht = {
AHCI_SHT("ahci_platform"),
};
@@ -30,13 +65,9 @@ static struct scsi_host_template ahci_platform_sht = {
static int __init ahci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct ahci_platform_data *pdata = dev->platform_data;
- struct ata_port_info pi = {
- .flags = AHCI_FLAG_COMMON,
- .pio_mask = ATA_PIO4,
- .udma_mask = ATA_UDMA6,
- .port_ops = &ahci_ops,
- };
+ struct ahci_platform_data *pdata = dev_get_platdata(dev);
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0];
const struct ata_port_info *ppi[] = { &pi, NULL };
struct ahci_host_priv *hpriv;
struct ata_host *host;
@@ -160,7 +191,7 @@ err0:
static int __devexit ahci_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct ahci_platform_data *pdata = dev->platform_data;
+ struct ahci_platform_data *pdata = dev_get_platdata(dev);
struct ata_host *host = dev_get_drvdata(dev);
ata_host_detach(host);
@@ -171,12 +202,20 @@ static int __devexit ahci_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id ahci_of_match[] = {
+ { .compatible = "calxeda,hb-ahci", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ahci_of_match);
+
static struct platform_driver ahci_driver = {
.remove = __devexit_p(ahci_remove),
.driver = {
.name = "ahci",
.owner = THIS_MODULE,
+ .of_match_table = ahci_of_match,
},
+ .id_table = ahci_devtype,
};
static int __init ahci_init(void)
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index 721d38bfa339..7df56ec31819 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -81,14 +81,13 @@ static int generic_set_mode(struct ata_link *link, struct ata_device **unused)
xfer_mask |= ata_xfer_mode2mask(XFER_MW_DMA_0);
}
- ata_dev_printk(dev, KERN_INFO, "configured for %s\n",
- name);
+ ata_dev_info(dev, "configured for %s\n", name);
dev->xfer_mode = ata_xfer_mask2mode(xfer_mask);
dev->xfer_shift = ata_xfer_mode2shift(dev->xfer_mode);
dev->flags &= ~ATA_DFLAG_PIO;
} else {
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ ata_dev_info(dev, "configured for PIO\n");
dev->xfer_mode = XFER_PIO_0;
dev->xfer_shift = ATA_SHIFT_PIO;
dev->flags |= ATA_DFLAG_PIO;
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index 6f6e7718b05c..69ac373c72ab 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -113,6 +113,8 @@ enum {
PIIX_PATA_FLAGS = ATA_FLAG_SLAVE_POSS,
PIIX_SATA_FLAGS = ATA_FLAG_SATA | PIIX_FLAG_CHECKINTR,
+ PIIX_FLAG_PIO16 = (1 << 30), /*support 16bit PIO only*/
+
PIIX_80C_PRI = (1 << 5) | (1 << 4),
PIIX_80C_SEC = (1 << 7) | (1 << 6),
@@ -147,6 +149,7 @@ enum piix_controller_ids {
ich8m_apple_sata, /* locks up on second port enable */
tolapai_sata,
piix_pata_vmw, /* PIIX4 for VMware, spurious DMA_ERR */
+ ich8_sata_snb,
};
struct piix_map_db {
@@ -177,6 +180,7 @@ static int piix_sidpr_scr_write(struct ata_link *link,
static int piix_sidpr_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
unsigned hints);
static bool piix_irq_check(struct ata_port *ap);
+static int piix_port_start(struct ata_port *ap);
#ifdef CONFIG_PM
static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
static int piix_pci_device_resume(struct pci_dev *pdev);
@@ -298,21 +302,21 @@ static const struct pci_device_id piix_pci_tbl[] = {
/* SATA Controller IDE (PCH) */
{ 0x8086, 0x3b2e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
/* SATA Controller IDE (CPT) */
- { 0x8086, 0x1c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ { 0x8086, 0x1c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb },
/* SATA Controller IDE (CPT) */
- { 0x8086, 0x1c01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ { 0x8086, 0x1c01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb },
/* SATA Controller IDE (CPT) */
{ 0x8086, 0x1c08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
/* SATA Controller IDE (CPT) */
{ 0x8086, 0x1c09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
/* SATA Controller IDE (PBG) */
- { 0x8086, 0x1d00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ { 0x8086, 0x1d00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb },
/* SATA Controller IDE (PBG) */
{ 0x8086, 0x1d08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
/* SATA Controller IDE (Panther Point) */
- { 0x8086, 0x1e00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ { 0x8086, 0x1e00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb },
/* SATA Controller IDE (Panther Point) */
- { 0x8086, 0x1e01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ { 0x8086, 0x1e01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb },
/* SATA Controller IDE (Panther Point) */
{ 0x8086, 0x1e08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
/* SATA Controller IDE (Panther Point) */
@@ -338,6 +342,7 @@ static struct scsi_host_template piix_sht = {
static struct ata_port_operations piix_sata_ops = {
.inherits = &ata_bmdma32_port_ops,
.sff_irq_check = piix_irq_check,
+ .port_start = piix_port_start,
};
static struct ata_port_operations piix_pata_ops = {
@@ -478,6 +483,7 @@ static const struct piix_map_db *piix_map_db_table[] = {
[ich8_2port_sata] = &ich8_2port_map_db,
[ich8m_apple_sata] = &ich8m_apple_map_db,
[tolapai_sata] = &tolapai_map_db,
+ [ich8_sata_snb] = &ich8_map_db,
};
static struct ata_port_info piix_port_info[] = {
@@ -606,6 +612,19 @@ static struct ata_port_info piix_port_info[] = {
.port_ops = &piix_vmw_ops,
},
+ /*
+ * some Sandybridge chipsets have broken 32 mode up to now,
+ * see https://bugzilla.kernel.org/show_bug.cgi?id=40592
+ */
+ [ich8_sata_snb] =
+ {
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR | PIIX_FLAG_PIO16,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &piix_sata_ops,
+ },
+
};
static struct pci_bits piix_enable_bits[] = {
@@ -649,6 +668,14 @@ static const struct ich_laptop ich_laptop[] = {
{ 0, }
};
+static int piix_port_start(struct ata_port *ap)
+{
+ if (!(ap->flags & PIIX_FLAG_PIO16))
+ ap->pflags |= ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE;
+
+ return ata_bmdma_port_start(ap);
+}
+
/**
* ich_pata_cable_detect - Probe host controller cable detect info
* @ap: Port for which cable detect info is desired
@@ -704,22 +731,11 @@ static int piix_pata_prereset(struct ata_link *link, unsigned long deadline)
static DEFINE_SPINLOCK(piix_lock);
-/**
- * piix_set_piomode - Initialize host controller PATA PIO timings
- * @ap: Port whose timings we are configuring
- * @adev: um
- *
- * Set PIO mode for device, in host controller PCI config space.
- *
- * LOCKING:
- * None (inherited from caller).
- */
-
-static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
+static void piix_set_timings(struct ata_port *ap, struct ata_device *adev,
+ u8 pio)
{
struct pci_dev *dev = to_pci_dev(ap->host->dev);
unsigned long flags;
- unsigned int pio = adev->pio_mode - XFER_PIO_0;
unsigned int is_slave = (adev->devno != 0);
unsigned int master_port= ap->port_no ? 0x42 : 0x40;
unsigned int slave_port = 0x44;
@@ -744,10 +760,16 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
control |= 1; /* TIME1 enable */
if (ata_pio_need_iordy(adev))
control |= 2; /* IE enable */
-
/* Intel specifies that the PPE functionality is for disk only */
if (adev->class == ATA_DEV_ATA)
control |= 4; /* PPE enable */
+ /*
+ * If the drive MWDMA is faster than it can do PIO then
+ * we must force PIO into PIO0
+ */
+ if (adev->pio_mode < XFER_PIO_0 + pio)
+ /* Enable DMA timing only */
+ control |= 8; /* PIO cycles in PIO0 */
spin_lock_irqsave(&piix_lock, flags);
@@ -759,8 +781,6 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
if (is_slave) {
/* clear TIME1|IE1|PPE1|DTE1 */
master_data &= 0xff0f;
- /* Enable SITRE (separate slave timing register) */
- master_data |= 0x4000;
/* enable PPE1, IE1 and TIME1 as needed */
master_data |= (control << 4);
pci_read_config_byte(dev, slave_port, &slave_data);
@@ -778,6 +798,9 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
(timings[pio][0] << 12) |
(timings[pio][1] << 8);
}
+
+ /* Enable SITRE (separate slave timing register) */
+ master_data |= 0x4000;
pci_write_config_word(dev, master_port, master_data);
if (is_slave)
pci_write_config_byte(dev, slave_port, slave_data);
@@ -795,6 +818,22 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
}
/**
+ * piix_set_piomode - Initialize host controller PATA PIO timings
+ * @ap: Port whose timings we are configuring
+ * @adev: Drive in question
+ *
+ * Set PIO mode for device, in host controller PCI config space.
+ *
+ * LOCKING:
+ * None (inherited from caller).
+ */
+
+static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+ piix_set_timings(ap, adev, adev->pio_mode - XFER_PIO_0);
+}
+
+/**
* do_pata_set_dmamode - Initialize host controller PATA PIO timings
* @ap: Port whose timings we are configuring
* @adev: Drive in question
@@ -810,31 +849,20 @@ static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, in
{
struct pci_dev *dev = to_pci_dev(ap->host->dev);
unsigned long flags;
- u8 master_port = ap->port_no ? 0x42 : 0x40;
- u16 master_data;
u8 speed = adev->dma_mode;
int devid = adev->devno + 2 * ap->port_no;
u8 udma_enable = 0;
- static const /* ISP RTC */
- u8 timings[][2] = { { 0, 0 },
- { 0, 0 },
- { 1, 0 },
- { 2, 1 },
- { 2, 3 }, };
-
- spin_lock_irqsave(&piix_lock, flags);
-
- pci_read_config_word(dev, master_port, &master_data);
- if (ap->udma_mask)
- pci_read_config_byte(dev, 0x48, &udma_enable);
-
if (speed >= XFER_UDMA_0) {
- unsigned int udma = adev->dma_mode - XFER_UDMA_0;
+ unsigned int udma = speed - XFER_UDMA_0;
u16 udma_timing;
u16 ideconf;
int u_clock, u_speed;
+ spin_lock_irqsave(&piix_lock, flags);
+
+ pci_read_config_byte(dev, 0x48, &udma_enable);
+
/*
* UDMA is handled by a combination of clock switching and
* selection of dividers
@@ -867,56 +895,21 @@ static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, in
performance (WR_PingPong_En) */
pci_write_config_word(dev, 0x54, ideconf);
}
+
+ pci_write_config_byte(dev, 0x48, udma_enable);
+
+ spin_unlock_irqrestore(&piix_lock, flags);
} else {
- /*
- * MWDMA is driven by the PIO timings. We must also enable
- * IORDY unconditionally along with TIME1. PPE has already
- * been set when the PIO timing was set.
- */
- unsigned int mwdma = adev->dma_mode - XFER_MW_DMA_0;
- unsigned int control;
- u8 slave_data;
+ /* MWDMA is driven by the PIO timings. */
+ unsigned int mwdma = speed - XFER_MW_DMA_0;
const unsigned int needed_pio[3] = {
XFER_PIO_0, XFER_PIO_3, XFER_PIO_4
};
int pio = needed_pio[mwdma] - XFER_PIO_0;
- control = 3; /* IORDY|TIME1 */
-
- /* If the drive MWDMA is faster than it can do PIO then
- we must force PIO into PIO0 */
-
- if (adev->pio_mode < needed_pio[mwdma])
- /* Enable DMA timing only */
- control |= 8; /* PIO cycles in PIO0 */
-
- if (adev->devno) { /* Slave */
- master_data &= 0xFF4F; /* Mask out IORDY|TIME1|DMAONLY */
- master_data |= control << 4;
- pci_read_config_byte(dev, 0x44, &slave_data);
- slave_data &= (ap->port_no ? 0x0f : 0xf0);
- /* Load the matching timing */
- slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << (ap->port_no ? 4 : 0);
- pci_write_config_byte(dev, 0x44, slave_data);
- } else { /* Master */
- master_data &= 0xCCF4; /* Mask out IORDY|TIME1|DMAONLY
- and master timing bits */
- master_data |= control;
- master_data |=
- (timings[pio][0] << 12) |
- (timings[pio][1] << 8);
- }
-
- if (ap->udma_mask)
- udma_enable &= ~(1 << devid);
-
- pci_write_config_word(dev, master_port, master_data);
+ /* XFER_PIO_0 is never used currently */
+ piix_set_timings(ap, adev, pio);
}
- /* Don't scribble on 0x48 if the controller does not support UDMA */
- if (ap->udma_mask)
- pci_write_config_byte(dev, 0x48, udma_enable);
-
- spin_unlock_irqrestore(&piix_lock, flags);
}
/**
@@ -1225,8 +1218,9 @@ static int piix_pci_device_resume(struct pci_dev *pdev)
*/
rc = pci_reenable_device(pdev);
if (rc)
- dev_printk(KERN_ERR, &pdev->dev, "failed to enable "
- "device after resume (%d)\n", rc);
+ dev_err(&pdev->dev,
+ "failed to enable device after resume (%d)\n",
+ rc);
} else
rc = ata_pci_device_do_resume(pdev);
@@ -1303,9 +1297,11 @@ static int __devinit piix_check_450nx_errata(struct pci_dev *ata_dev)
no_piix_dma = 2;
}
if (no_piix_dma)
- dev_printk(KERN_WARNING, &ata_dev->dev, "450NX errata present, disabling IDE DMA.\n");
- if (no_piix_dma == 2)
- dev_printk(KERN_WARNING, &ata_dev->dev, "A BIOS update may resolve this.\n");
+ dev_warn(&ata_dev->dev,
+ "450NX errata present, disabling IDE DMA%s\n",
+ no_piix_dma == 2 ? " - a BIOS update may resolve this"
+ : "");
+
return no_piix_dma;
}
@@ -1338,37 +1334,36 @@ static const int *__devinit piix_init_sata_map(struct pci_dev *pdev,
map = map_db->map[map_value & map_db->mask];
- dev_printk(KERN_INFO, &pdev->dev, "MAP [");
+ dev_info(&pdev->dev, "MAP [");
for (i = 0; i < 4; i++) {
switch (map[i]) {
case RV:
invalid_map = 1;
- printk(" XX");
+ pr_cont(" XX");
break;
case NA:
- printk(" --");
+ pr_cont(" --");
break;
case IDE:
WARN_ON((i & 1) || map[i + 1] != IDE);
pinfo[i / 2] = piix_port_info[ich_pata_100];
i++;
- printk(" IDE IDE");
+ pr_cont(" IDE IDE");
break;
default:
- printk(" P%d", map[i]);
+ pr_cont(" P%d", map[i]);
if (i & 1)
pinfo[i / 2].flags |= ATA_FLAG_SLAVE_POSS;
break;
}
}
- printk(" ]\n");
+ pr_cont(" ]\n");
if (invalid_map)
- dev_printk(KERN_ERR, &pdev->dev,
- "invalid MAP value %u\n", map_value);
+ dev_err(&pdev->dev, "invalid MAP value %u\n", map_value);
return map;
}
@@ -1398,8 +1393,8 @@ static bool piix_no_sidpr(struct ata_host *host)
if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == 0x2920 &&
pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG &&
pdev->subsystem_device == 0xb049) {
- dev_printk(KERN_WARNING, host->dev,
- "Samsung DB-P70 detected, disabling SIDPR\n");
+ dev_warn(host->dev,
+ "Samsung DB-P70 detected, disabling SIDPR\n");
return true;
}
@@ -1451,8 +1446,8 @@ static int __devinit piix_init_sidpr(struct ata_host *host)
piix_sidpr_scr_read(link0, SCR_CONTROL, &scontrol);
if ((scontrol & 0xf00) != 0x300) {
- dev_printk(KERN_INFO, host->dev, "SCR access via "
- "SIDPR is available but doesn't work\n");
+ dev_info(host->dev,
+ "SCR access via SIDPR is available but doesn't work\n");
return 0;
}
}
@@ -1501,8 +1496,7 @@ static void piix_iocfg_bit18_quirk(struct ata_host *host)
* affected systems.
*/
if (hpriv->saved_iocfg & (1 << 18)) {
- dev_printk(KERN_INFO, &pdev->dev,
- "applying IOCFG bit18 quirk\n");
+ dev_info(&pdev->dev, "applying IOCFG bit18 quirk\n");
pci_write_config_dword(pdev, PIIX_IOCFG,
hpriv->saved_iocfg & ~(1 << 18));
}
@@ -1561,7 +1555,6 @@ static bool piix_broken_system_poweroff(struct pci_dev *pdev)
static int __devinit piix_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- static int printed_version;
struct device *dev = &pdev->dev;
struct ata_port_info port_info[2];
const struct ata_port_info *ppi[] = { &port_info[0], &port_info[1] };
@@ -1571,9 +1564,7 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
struct piix_host_priv *hpriv;
int rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* no hotplugging support for later devices (FIXME) */
if (!in_module_init && ent->driver_data >= ich5_sata)
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 41223c7f0206..3c92dbd751e0 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -82,6 +82,8 @@ static void ahci_pmp_attach(struct ata_port *ap);
static void ahci_pmp_detach(struct ata_port *ap);
static int ahci_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
+static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline);
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static void ahci_postreset(struct ata_link *link, unsigned int *class);
@@ -178,6 +180,12 @@ struct ata_port_operations ahci_ops = {
};
EXPORT_SYMBOL_GPL(ahci_ops);
+struct ata_port_operations ahci_pmp_retry_srst_ops = {
+ .inherits = &ahci_ops,
+ .softreset = ahci_pmp_retry_softreset,
+};
+EXPORT_SYMBOL_GPL(ahci_pmp_retry_srst_ops);
+
int ahci_em_messages = 1;
EXPORT_SYMBOL_GPL(ahci_em_messages);
module_param(ahci_em_messages, int, 0444);
@@ -286,10 +294,10 @@ static ssize_t ahci_read_em_buffer(struct device *dev,
/* the count should not be larger than PAGE_SIZE */
if (count > PAGE_SIZE) {
if (printk_ratelimit())
- ata_port_printk(ap, KERN_WARNING,
- "EM read buffer size too large: "
- "buffer size %u, page size %lu\n",
- hpriv->em_buf_sz, PAGE_SIZE);
+ ata_port_warn(ap,
+ "EM read buffer size too large: "
+ "buffer size %u, page size %lu\n",
+ hpriv->em_buf_sz, PAGE_SIZE);
count = PAGE_SIZE;
}
@@ -410,51 +418,46 @@ void ahci_save_initial_config(struct device *dev,
/* some chips have errata preventing 64bit use */
if ((cap & HOST_CAP_64) && (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)) {
- dev_printk(KERN_INFO, dev,
- "controller can't do 64bit DMA, forcing 32bit\n");
+ dev_info(dev, "controller can't do 64bit DMA, forcing 32bit\n");
cap &= ~HOST_CAP_64;
}
if ((cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_NO_NCQ)) {
- dev_printk(KERN_INFO, dev,
- "controller can't do NCQ, turning off CAP_NCQ\n");
+ dev_info(dev, "controller can't do NCQ, turning off CAP_NCQ\n");
cap &= ~HOST_CAP_NCQ;
}
if (!(cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_YES_NCQ)) {
- dev_printk(KERN_INFO, dev,
- "controller can do NCQ, turning on CAP_NCQ\n");
+ dev_info(dev, "controller can do NCQ, turning on CAP_NCQ\n");
cap |= HOST_CAP_NCQ;
}
if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) {
- dev_printk(KERN_INFO, dev,
- "controller can't do PMP, turning off CAP_PMP\n");
+ dev_info(dev, "controller can't do PMP, turning off CAP_PMP\n");
cap &= ~HOST_CAP_PMP;
}
if ((cap & HOST_CAP_SNTF) && (hpriv->flags & AHCI_HFLAG_NO_SNTF)) {
- dev_printk(KERN_INFO, dev,
- "controller can't do SNTF, turning off CAP_SNTF\n");
+ dev_info(dev,
+ "controller can't do SNTF, turning off CAP_SNTF\n");
cap &= ~HOST_CAP_SNTF;
}
if (!(cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_YES_FBS)) {
- dev_printk(KERN_INFO, dev,
- "controller can do FBS, turning on CAP_FBS\n");
+ dev_info(dev, "controller can do FBS, turning on CAP_FBS\n");
cap |= HOST_CAP_FBS;
}
if (force_port_map && port_map != force_port_map) {
- dev_printk(KERN_INFO, dev, "forcing port_map 0x%x -> 0x%x\n",
- port_map, force_port_map);
+ dev_info(dev, "forcing port_map 0x%x -> 0x%x\n",
+ port_map, force_port_map);
port_map = force_port_map;
}
if (mask_port_map) {
- dev_printk(KERN_WARNING, dev, "masking port_map 0x%x -> 0x%x\n",
- port_map,
- port_map & mask_port_map);
+ dev_warn(dev, "masking port_map 0x%x -> 0x%x\n",
+ port_map,
+ port_map & mask_port_map);
port_map &= mask_port_map;
}
@@ -470,10 +473,9 @@ void ahci_save_initial_config(struct device *dev,
* port_map and let it be generated from n_ports.
*/
if (map_ports > ahci_nr_ports(cap)) {
- dev_printk(KERN_WARNING, dev,
- "implemented port map (0x%x) contains more "
- "ports than nr_ports (%u), using nr_ports\n",
- port_map, ahci_nr_ports(cap));
+ dev_warn(dev,
+ "implemented port map (0x%x) contains more ports than nr_ports (%u), using nr_ports\n",
+ port_map, ahci_nr_ports(cap));
port_map = 0;
}
}
@@ -481,8 +483,7 @@ void ahci_save_initial_config(struct device *dev,
/* fabricate port_map from cap.nr_ports */
if (!port_map) {
port_map = (1 << ahci_nr_ports(cap)) - 1;
- dev_printk(KERN_WARNING, dev,
- "forcing PORTS_IMPL to 0x%x\n", port_map);
+ dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map);
/* write the fixed up value to the PI register */
hpriv->saved_port_map = port_map;
@@ -822,8 +823,8 @@ int ahci_reset_controller(struct ata_host *host)
HOST_RESET, 10, 1000);
if (tmp & HOST_RESET) {
- dev_printk(KERN_ERR, host->dev,
- "controller reset failed (0x%x)\n", tmp);
+ dev_err(host->dev, "controller reset failed (0x%x)\n",
+ tmp);
return -EIO;
}
@@ -835,8 +836,7 @@ int ahci_reset_controller(struct ata_host *host)
*/
ahci_restore_initial_config(host);
} else
- dev_printk(KERN_INFO, host->dev,
- "skipping global host reset\n");
+ dev_info(host->dev, "skipping global host reset\n");
return 0;
}
@@ -1132,8 +1132,8 @@ static void ahci_dev_config(struct ata_device *dev)
if (hpriv->flags & AHCI_HFLAG_SECT255) {
dev->max_sectors = 255;
- ata_dev_printk(dev, KERN_INFO,
- "SB600 AHCI: limiting to 255 sectors per cmd\n");
+ ata_dev_info(dev,
+ "SB600 AHCI: limiting to 255 sectors per cmd\n");
}
}
@@ -1257,8 +1257,7 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class,
/* prepare for SRST (AHCI-1.1 10.4.1) */
rc = ahci_kick_engine(ap);
if (rc && rc != -EOPNOTSUPP)
- ata_link_printk(link, KERN_WARNING,
- "failed to reset engine (errno=%d)\n", rc);
+ ata_link_warn(link, "failed to reset engine (errno=%d)\n", rc);
ata_tf_init(link->device, &tf);
@@ -1291,8 +1290,7 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class,
* be trusted. Treat device readiness timeout as link
* offline.
*/
- ata_link_printk(link, KERN_INFO,
- "device not ready, treating as offline\n");
+ ata_link_info(link, "device not ready, treating as offline\n");
*class = ATA_DEV_NONE;
} else if (rc) {
/* link occupied, -ENODEV too is an error */
@@ -1305,7 +1303,7 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class,
return 0;
fail:
- ata_link_printk(link, KERN_ERR, "softreset failed (%s)\n", reason);
+ ata_link_err(link, "softreset failed (%s)\n", reason);
return rc;
}
@@ -1329,6 +1327,55 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class,
}
EXPORT_SYMBOL_GPL(ahci_do_softreset);
+static int ahci_bad_pmp_check_ready(struct ata_link *link)
+{
+ void __iomem *port_mmio = ahci_port_base(link->ap);
+ u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
+ u32 irq_status = readl(port_mmio + PORT_IRQ_STAT);
+
+ /*
+ * There is no need to check TFDATA if BAD PMP is found due to HW bug,
+ * which can save timeout delay.
+ */
+ if (irq_status & PORT_IRQ_BAD_PMP)
+ return -EIO;
+
+ return ata_check_ready(status);
+}
+
+int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ void __iomem *port_mmio = ahci_port_base(ap);
+ int pmp = sata_srst_pmp(link);
+ int rc;
+ u32 irq_sts;
+
+ DPRINTK("ENTER\n");
+
+ rc = ahci_do_softreset(link, class, pmp, deadline,
+ ahci_bad_pmp_check_ready);
+
+ /*
+ * Soft reset fails with IPMS set when PMP is enabled but
+ * SATA HDD/ODD is connected to SATA port, do soft reset
+ * again to port 0.
+ */
+ if (rc == -EIO) {
+ irq_sts = readl(port_mmio + PORT_IRQ_STAT);
+ if (irq_sts & PORT_IRQ_BAD_PMP) {
+ ata_link_printk(link, KERN_WARNING,
+ "applying PMP SRST workaround "
+ "and retrying\n");
+ rc = ahci_do_softreset(link, class, 0, deadline,
+ ahci_check_ready);
+ }
+ }
+
+ return rc;
+}
+
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
@@ -1474,8 +1521,7 @@ static void ahci_fbs_dec_intr(struct ata_port *ap)
}
if (fbs & PORT_FBS_DEC)
- dev_printk(KERN_ERR, ap->host->dev,
- "failed to clear device error\n");
+ dev_err(ap->host->dev, "failed to clear device error\n");
}
static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
@@ -1713,8 +1759,8 @@ irqreturn_t ahci_interrupt(int irq, void *dev_instance)
} else {
VPRINTK("port %u (no irq)\n", i);
if (ata_ratelimit())
- dev_printk(KERN_WARNING, host->dev,
- "interrupt on disabled port %u\n", i);
+ dev_warn(host->dev,
+ "interrupt on disabled port %u\n", i);
}
handled = 1;
@@ -1865,11 +1911,11 @@ static void ahci_enable_fbs(struct ata_port *ap)
writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS);
fbs = readl(port_mmio + PORT_FBS);
if (fbs & PORT_FBS_EN) {
- dev_printk(KERN_INFO, ap->host->dev, "FBS is enabled.\n");
+ dev_info(ap->host->dev, "FBS is enabled\n");
pp->fbs_enabled = true;
pp->fbs_last_dev = -1; /* initialization */
} else
- dev_printk(KERN_ERR, ap->host->dev, "Failed to enable FBS\n");
+ dev_err(ap->host->dev, "Failed to enable FBS\n");
ahci_start_engine(ap);
}
@@ -1897,9 +1943,9 @@ static void ahci_disable_fbs(struct ata_port *ap)
writel(fbs & ~PORT_FBS_EN, port_mmio + PORT_FBS);
fbs = readl(port_mmio + PORT_FBS);
if (fbs & PORT_FBS_EN)
- dev_printk(KERN_ERR, ap->host->dev, "Failed to disable FBS\n");
+ dev_err(ap->host->dev, "Failed to disable FBS\n");
else {
- dev_printk(KERN_INFO, ap->host->dev, "FBS is disabled.\n");
+ dev_info(ap->host->dev, "FBS is disabled\n");
pp->fbs_enabled = false;
}
@@ -1975,7 +2021,7 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
if (rc == 0)
ahci_power_down(ap);
else {
- ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc);
+ ata_port_err(ap, "%s (%d)\n", emsg, rc);
ahci_start_port(ap);
}
@@ -2003,14 +2049,12 @@ static int ahci_port_start(struct ata_port *ap)
if (cmd & PORT_CMD_FBSCP)
pp->fbs_supported = true;
else if (hpriv->flags & AHCI_HFLAG_YES_FBS) {
- dev_printk(KERN_INFO, dev,
- "port %d can do FBS, forcing FBSCP\n",
- ap->port_no);
+ dev_info(dev, "port %d can do FBS, forcing FBSCP\n",
+ ap->port_no);
pp->fbs_supported = true;
} else
- dev_printk(KERN_WARNING, dev,
- "port %d is not capable of FBS\n",
- ap->port_no);
+ dev_warn(dev, "port %d is not capable of FBS\n",
+ ap->port_no);
}
if (pp->fbs_supported) {
@@ -2072,7 +2116,7 @@ static void ahci_port_stop(struct ata_port *ap)
/* de-initialize port */
rc = ahci_deinit_port(ap, &emsg);
if (rc)
- ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc);
+ ata_port_warn(ap, "%s (%d)\n", emsg, rc);
}
void ahci_print_info(struct ata_host *host, const char *scc_s)
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index a791b8ce6294..bb7c5f1085cc 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -218,12 +218,12 @@ static void ata_acpi_dev_uevent(acpi_handle handle, u32 event, void *data)
ata_acpi_uevent(dev->link->ap, dev, event);
}
-static struct acpi_dock_ops ata_acpi_dev_dock_ops = {
+static const struct acpi_dock_ops ata_acpi_dev_dock_ops = {
.handler = ata_acpi_dev_notify_dock,
.uevent = ata_acpi_dev_uevent,
};
-static struct acpi_dock_ops ata_acpi_ap_dock_ops = {
+static const struct acpi_dock_ops ata_acpi_ap_dock_ops = {
.handler = ata_acpi_ap_notify_dock,
.uevent = ata_acpi_ap_uevent,
};
@@ -332,25 +332,22 @@ int ata_acpi_gtm(struct ata_port *ap, struct ata_acpi_gtm *gtm)
rc = -EINVAL;
if (ACPI_FAILURE(status)) {
- ata_port_printk(ap, KERN_ERR,
- "ACPI get timing mode failed (AE 0x%x)\n",
- status);
+ ata_port_err(ap, "ACPI get timing mode failed (AE 0x%x)\n",
+ status);
goto out_free;
}
out_obj = output.pointer;
if (out_obj->type != ACPI_TYPE_BUFFER) {
- ata_port_printk(ap, KERN_WARNING,
- "_GTM returned unexpected object type 0x%x\n",
- out_obj->type);
+ ata_port_warn(ap, "_GTM returned unexpected object type 0x%x\n",
+ out_obj->type);
goto out_free;
}
if (out_obj->buffer.length != sizeof(struct ata_acpi_gtm)) {
- ata_port_printk(ap, KERN_ERR,
- "_GTM returned invalid length %d\n",
- out_obj->buffer.length);
+ ata_port_err(ap, "_GTM returned invalid length %d\n",
+ out_obj->buffer.length);
goto out_free;
}
@@ -402,8 +399,8 @@ int ata_acpi_stm(struct ata_port *ap, const struct ata_acpi_gtm *stm)
if (status == AE_NOT_FOUND)
return -ENOENT;
if (ACPI_FAILURE(status)) {
- ata_port_printk(ap, KERN_ERR,
- "ACPI set timing mode failed (status=0x%x)\n", status);
+ ata_port_err(ap, "ACPI set timing mode failed (status=0x%x)\n",
+ status);
return -EINVAL;
}
return 0;
@@ -450,8 +447,8 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf)
output.pointer = NULL; /* ACPI-CA sets this; save/free it later */
if (ata_msg_probe(ap))
- ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER: port#: %d\n",
- __func__, ap->port_no);
+ ata_dev_dbg(dev, "%s: ENTER: port#: %d\n",
+ __func__, ap->port_no);
/* _GTF has no input parameters */
status = acpi_evaluate_object(dev->acpi_handle, "_GTF", NULL, &output);
@@ -459,9 +456,8 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf)
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND) {
- ata_dev_printk(dev, KERN_WARNING,
- "_GTF evaluation failed (AE 0x%x)\n",
- status);
+ ata_dev_warn(dev, "_GTF evaluation failed (AE 0x%x)\n",
+ status);
rc = -EINVAL;
}
goto out_free;
@@ -469,27 +465,24 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf)
if (!output.length || !output.pointer) {
if (ata_msg_probe(ap))
- ata_dev_printk(dev, KERN_DEBUG, "%s: Run _GTF: "
- "length or ptr is NULL (0x%llx, 0x%p)\n",
- __func__,
- (unsigned long long)output.length,
- output.pointer);
+ ata_dev_dbg(dev, "%s: Run _GTF: length or ptr is NULL (0x%llx, 0x%p)\n",
+ __func__,
+ (unsigned long long)output.length,
+ output.pointer);
rc = -EINVAL;
goto out_free;
}
if (out_obj->type != ACPI_TYPE_BUFFER) {
- ata_dev_printk(dev, KERN_WARNING,
- "_GTF unexpected object type 0x%x\n",
- out_obj->type);
+ ata_dev_warn(dev, "_GTF unexpected object type 0x%x\n",
+ out_obj->type);
rc = -EINVAL;
goto out_free;
}
if (out_obj->buffer.length % REGS_PER_GTF) {
- ata_dev_printk(dev, KERN_WARNING,
- "unexpected _GTF length (%d)\n",
- out_obj->buffer.length);
+ ata_dev_warn(dev, "unexpected _GTF length (%d)\n",
+ out_obj->buffer.length);
rc = -EINVAL;
goto out_free;
}
@@ -499,9 +492,8 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf)
if (gtf) {
*gtf = (void *)out_obj->buffer.pointer;
if (ata_msg_probe(ap))
- ata_dev_printk(dev, KERN_DEBUG,
- "%s: returning gtf=%p, gtf_count=%d\n",
- __func__, *gtf, rc);
+ ata_dev_dbg(dev, "%s: returning gtf=%p, gtf_count=%d\n",
+ __func__, *gtf, rc);
}
return rc;
@@ -811,8 +803,8 @@ static int ata_acpi_push_id(struct ata_device *dev)
union acpi_object in_params[1];
if (ata_msg_probe(ap))
- ata_dev_printk(dev, KERN_DEBUG, "%s: ix = %d, port#: %d\n",
- __func__, dev->devno, ap->port_no);
+ ata_dev_dbg(dev, "%s: ix = %d, port#: %d\n",
+ __func__, dev->devno, ap->port_no);
/* Give the drive Identify data to the drive via the _SDD method */
/* _SDD: set up input parameters */
@@ -832,8 +824,7 @@ static int ata_acpi_push_id(struct ata_device *dev)
return -ENOENT;
if (ACPI_FAILURE(status)) {
- ata_dev_printk(dev, KERN_WARNING,
- "ACPI _SDD failed (AE 0x%x)\n", status);
+ ata_dev_warn(dev, "ACPI _SDD failed (AE 0x%x)\n", status);
return -EIO;
}
@@ -983,8 +974,8 @@ int ata_acpi_on_devcfg(struct ata_device *dev)
if (nr_executed) {
rc = ata_dev_reread_id(dev, 0);
if (rc < 0) {
- ata_dev_printk(dev, KERN_ERR, "failed to IDENTIFY "
- "after ACPI commands\n");
+ ata_dev_err(dev,
+ "failed to IDENTIFY after ACPI commands\n");
return rc;
}
}
@@ -1002,8 +993,7 @@ int ata_acpi_on_devcfg(struct ata_device *dev)
return rc;
}
- ata_dev_printk(dev, KERN_WARNING,
- "ACPI: failed the second time, disabled\n");
+ ata_dev_warn(dev, "ACPI: failed the second time, disabled\n");
dev->acpi_handle = NULL;
/* We can safely continue if no _GTF command has been executed
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 000d03ae6653..c04ad68cb602 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -335,8 +335,7 @@ void ata_force_cbl(struct ata_port *ap)
continue;
ap->cbl = fe->param.cbl;
- ata_port_printk(ap, KERN_NOTICE,
- "FORCE: cable set to %s\n", fe->param.name);
+ ata_port_notice(ap, "FORCE: cable set to %s\n", fe->param.name);
return;
}
}
@@ -378,8 +377,7 @@ static void ata_force_link_limits(struct ata_link *link)
/* only honor the first spd limit */
if (!did_spd && fe->param.spd_limit) {
link->hw_sata_spd_limit = (1 << fe->param.spd_limit) - 1;
- ata_link_printk(link, KERN_NOTICE,
- "FORCE: PHY spd limit set to %s\n",
+ ata_link_notice(link, "FORCE: PHY spd limit set to %s\n",
fe->param.name);
did_spd = true;
}
@@ -387,7 +385,7 @@ static void ata_force_link_limits(struct ata_link *link)
/* let lflags stack */
if (fe->param.lflags) {
link->flags |= fe->param.lflags;
- ata_link_printk(link, KERN_NOTICE,
+ ata_link_notice(link,
"FORCE: link flag 0x%x forced -> 0x%x\n",
fe->param.lflags, link->flags);
}
@@ -442,8 +440,8 @@ static void ata_force_xfermask(struct ata_device *dev)
dev->pio_mask = pio_mask;
}
- ata_dev_printk(dev, KERN_NOTICE,
- "FORCE: xfer_mask set to %s\n", fe->param.name);
+ ata_dev_notice(dev, "FORCE: xfer_mask set to %s\n",
+ fe->param.name);
return;
}
}
@@ -486,8 +484,8 @@ static void ata_force_horkage(struct ata_device *dev)
dev->horkage |= fe->param.horkage_on;
dev->horkage &= ~fe->param.horkage_off;
- ata_dev_printk(dev, KERN_NOTICE,
- "FORCE: horkage modified (%s)\n", fe->param.name);
+ ata_dev_notice(dev, "FORCE: horkage modified (%s)\n",
+ fe->param.name);
}
}
@@ -711,8 +709,8 @@ u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev)
sect = tf->lbal;
if (!sect) {
- ata_dev_printk(dev, KERN_WARNING, "device reported "
- "invalid CHS sector 0\n");
+ ata_dev_warn(dev,
+ "device reported invalid CHS sector 0\n");
sect = 1; /* oh well */
}
@@ -1230,8 +1228,9 @@ static int ata_read_native_max_address(struct ata_device *dev, u64 *max_sectors)
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
if (err_mask) {
- ata_dev_printk(dev, KERN_WARNING, "failed to read native "
- "max address (err_mask=0x%x)\n", err_mask);
+ ata_dev_warn(dev,
+ "failed to read native max address (err_mask=0x%x)\n",
+ err_mask);
if (err_mask == AC_ERR_DEV && (tf.feature & ATA_ABORTED))
return -EACCES;
return -EIO;
@@ -1292,8 +1291,9 @@ static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors)
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
if (err_mask) {
- ata_dev_printk(dev, KERN_WARNING, "failed to set "
- "max address (err_mask=0x%x)\n", err_mask);
+ ata_dev_warn(dev,
+ "failed to set max address (err_mask=0x%x)\n",
+ err_mask);
if (err_mask == AC_ERR_DEV &&
(tf.feature & (ATA_ABORTED | ATA_IDNF)))
return -EACCES;
@@ -1336,8 +1336,8 @@ static int ata_hpa_resize(struct ata_device *dev)
* be unlocked, skip HPA resizing.
*/
if (rc == -EACCES || !unlock_hpa) {
- ata_dev_printk(dev, KERN_WARNING, "HPA support seems "
- "broken, skipping HPA handling\n");
+ ata_dev_warn(dev,
+ "HPA support seems broken, skipping HPA handling\n");
dev->horkage |= ATA_HORKAGE_BROKEN_HPA;
/* we can continue if device aborted the command */
@@ -1355,14 +1355,13 @@ static int ata_hpa_resize(struct ata_device *dev)
return 0;
if (native_sectors > sectors)
- ata_dev_printk(dev, KERN_INFO,
+ ata_dev_info(dev,
"HPA detected: current %llu, native %llu\n",
(unsigned long long)sectors,
(unsigned long long)native_sectors);
else if (native_sectors < sectors)
- ata_dev_printk(dev, KERN_WARNING,
- "native sectors (%llu) is smaller than "
- "sectors (%llu)\n",
+ ata_dev_warn(dev,
+ "native sectors (%llu) is smaller than sectors (%llu)\n",
(unsigned long long)native_sectors,
(unsigned long long)sectors);
return 0;
@@ -1372,10 +1371,10 @@ static int ata_hpa_resize(struct ata_device *dev)
rc = ata_set_max_sectors(dev, native_sectors);
if (rc == -EACCES) {
/* if device aborted the command, skip HPA resizing */
- ata_dev_printk(dev, KERN_WARNING, "device aborted resize "
- "(%llu -> %llu), skipping HPA handling\n",
- (unsigned long long)sectors,
- (unsigned long long)native_sectors);
+ ata_dev_warn(dev,
+ "device aborted resize (%llu -> %llu), skipping HPA handling\n",
+ (unsigned long long)sectors,
+ (unsigned long long)native_sectors);
dev->horkage |= ATA_HORKAGE_BROKEN_HPA;
return 0;
} else if (rc)
@@ -1384,14 +1383,14 @@ static int ata_hpa_resize(struct ata_device *dev)
/* re-read IDENTIFY data */
rc = ata_dev_reread_id(dev, 0);
if (rc) {
- ata_dev_printk(dev, KERN_ERR, "failed to re-read IDENTIFY "
- "data after HPA resizing\n");
+ ata_dev_err(dev,
+ "failed to re-read IDENTIFY data after HPA resizing\n");
return rc;
}
if (print_info) {
u64 new_sectors = ata_id_n_sectors(dev->id);
- ata_dev_printk(dev, KERN_INFO,
+ ata_dev_info(dev,
"HPA unlocked: %llu -> %llu, native %llu\n",
(unsigned long long)sectors,
(unsigned long long)new_sectors,
@@ -1655,8 +1654,8 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
ata_qc_complete(qc);
if (ata_msg_warn(ap))
- ata_dev_printk(dev, KERN_WARNING,
- "qc timeout (cmd 0x%x)\n", command);
+ ata_dev_warn(dev, "qc timeout (cmd 0x%x)\n",
+ command);
}
spin_unlock_irqrestore(ap->lock, flags);
@@ -1870,7 +1869,7 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
int rc;
if (ata_msg_ctl(ap))
- ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __func__);
+ ata_dev_dbg(dev, "%s: ENTER\n", __func__);
retry:
ata_tf_init(dev, &tf);
@@ -1909,14 +1908,13 @@ retry:
if (err_mask) {
if (err_mask & AC_ERR_NODEV_HINT) {
- ata_dev_printk(dev, KERN_DEBUG,
- "NODEV after polling detection\n");
+ ata_dev_dbg(dev, "NODEV after polling detection\n");
return -ENOENT;
}
if (is_semb) {
- ata_dev_printk(dev, KERN_INFO, "IDENTIFY failed on "
- "device w/ SEMB sig, disabled\n");
+ ata_dev_info(dev,
+ "IDENTIFY failed on device w/ SEMB sig, disabled\n");
/* SEMB is not supported yet */
*p_class = ATA_DEV_SEMB_UNSUP;
return 0;
@@ -1942,8 +1940,8 @@ retry:
* both flavors of IDENTIFYs which happens
* sometimes with phantom devices.
*/
- ata_dev_printk(dev, KERN_DEBUG,
- "both IDENTIFYs aborted, assuming NODEV\n");
+ ata_dev_dbg(dev,
+ "both IDENTIFYs aborted, assuming NODEV\n");
return -ENOENT;
}
@@ -1953,9 +1951,9 @@ retry:
}
if (dev->horkage & ATA_HORKAGE_DUMP_ID) {
- ata_dev_printk(dev, KERN_DEBUG, "dumping IDENTIFY data, "
- "class=%d may_fallback=%d tried_spinup=%d\n",
- class, may_fallback, tried_spinup);
+ ata_dev_dbg(dev, "dumping IDENTIFY data, "
+ "class=%d may_fallback=%d tried_spinup=%d\n",
+ class, may_fallback, tried_spinup);
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,
16, 2, id, ATA_ID_WORDS * sizeof(*id), true);
}
@@ -2034,8 +2032,8 @@ retry:
err_out:
if (ata_msg_warn(ap))
- ata_dev_printk(dev, KERN_WARNING, "failed to IDENTIFY "
- "(%s, err_mask=0x%x)\n", reason, err_mask);
+ ata_dev_warn(dev, "failed to IDENTIFY (%s, err_mask=0x%x)\n",
+ reason, err_mask);
return rc;
}
@@ -2065,9 +2063,8 @@ static int ata_do_link_spd_horkage(struct ata_device *dev)
* guaranteed by setting sata_spd_limit to target_limit above.
*/
if (plink->sata_spd > target) {
- ata_dev_printk(dev, KERN_INFO,
- "applying link speed limit horkage to %s\n",
- sata_spd_string(target));
+ ata_dev_info(dev, "applying link speed limit horkage to %s\n",
+ sata_spd_string(target));
return -EAGAIN;
}
return 0;
@@ -2110,8 +2107,9 @@ static int ata_dev_config_ncq(struct ata_device *dev,
err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_ENABLE,
SATA_FPDMA_AA);
if (err_mask) {
- ata_dev_printk(dev, KERN_ERR, "failed to enable AA"
- "(error_mask=0x%x)\n", err_mask);
+ ata_dev_err(dev,
+ "failed to enable AA (error_mask=0x%x)\n",
+ err_mask);
if (err_mask != AC_ERR_DEV) {
dev->horkage |= ATA_HORKAGE_BROKEN_FPDMA_AA;
return -EIO;
@@ -2154,31 +2152,28 @@ int ata_dev_configure(struct ata_device *dev)
int rc;
if (!ata_dev_enabled(dev) && ata_msg_info(ap)) {
- ata_dev_printk(dev, KERN_INFO, "%s: ENTER/EXIT -- nodev\n",
- __func__);
+ ata_dev_info(dev, "%s: ENTER/EXIT -- nodev\n", __func__);
return 0;
}
if (ata_msg_probe(ap))
- ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __func__);
+ ata_dev_dbg(dev, "%s: ENTER\n", __func__);
/* set horkage */
dev->horkage |= ata_dev_blacklisted(dev);
ata_force_horkage(dev);
if (dev->horkage & ATA_HORKAGE_DISABLE) {
- ata_dev_printk(dev, KERN_INFO,
- "unsupported device, disabling\n");
+ ata_dev_info(dev, "unsupported device, disabling\n");
ata_dev_disable(dev);
return 0;
}
if ((!atapi_enabled || (ap->flags & ATA_FLAG_NO_ATAPI)) &&
dev->class == ATA_DEV_ATAPI) {
- ata_dev_printk(dev, KERN_WARNING,
- "WARNING: ATAPI is %s, device ignored.\n",
- atapi_enabled ? "not supported with this driver"
- : "disabled");
+ ata_dev_warn(dev, "WARNING: ATAPI is %s, device ignored\n",
+ atapi_enabled ? "not supported with this driver"
+ : "disabled");
ata_dev_disable(dev);
return 0;
}
@@ -2199,12 +2194,12 @@ int ata_dev_configure(struct ata_device *dev)
/* print device capabilities */
if (ata_msg_probe(ap))
- ata_dev_printk(dev, KERN_DEBUG,
- "%s: cfg 49:%04x 82:%04x 83:%04x 84:%04x "
- "85:%04x 86:%04x 87:%04x 88:%04x\n",
- __func__,
- id[49], id[82], id[83], id[84],
- id[85], id[86], id[87], id[88]);
+ ata_dev_dbg(dev,
+ "%s: cfg 49:%04x 82:%04x 83:%04x 84:%04x "
+ "85:%04x 86:%04x 87:%04x 88:%04x\n",
+ __func__,
+ id[49], id[82], id[83], id[84],
+ id[85], id[86], id[87], id[88]);
/* initialize to-be-configured parameters */
dev->flags &= ~ATA_DFLAG_CFG_MASK;
@@ -2238,17 +2233,15 @@ int ata_dev_configure(struct ata_device *dev)
if (ata_id_is_cfa(id)) {
/* CPRM may make this media unusable */
if (id[ATA_ID_CFA_KEY_MGMT] & 1)
- ata_dev_printk(dev, KERN_WARNING,
- "supports DRM functions and may "
- "not be fully accessible.\n");
+ ata_dev_warn(dev,
+ "supports DRM functions and may not be fully accessible\n");
snprintf(revbuf, 7, "CFA");
} else {
snprintf(revbuf, 7, "ATA-%d", ata_id_major_version(id));
/* Warn the user if the device has TPM extensions */
if (ata_id_has_tpm(id))
- ata_dev_printk(dev, KERN_WARNING,
- "supports DRM functions and may "
- "not be fully accessible.\n");
+ ata_dev_warn(dev,
+ "supports DRM functions and may not be fully accessible\n");
}
dev->n_sectors = ata_id_n_sectors(id);
@@ -2285,12 +2278,11 @@ int ata_dev_configure(struct ata_device *dev)
/* print device info to dmesg */
if (ata_msg_drv(ap) && print_info) {
- ata_dev_printk(dev, KERN_INFO,
- "%s: %s, %s, max %s\n",
- revbuf, modelbuf, fwrevbuf,
- ata_mode_string(xfer_mask));
- ata_dev_printk(dev, KERN_INFO,
- "%Lu sectors, multi %u: %s %s\n",
+ ata_dev_info(dev, "%s: %s, %s, max %s\n",
+ revbuf, modelbuf, fwrevbuf,
+ ata_mode_string(xfer_mask));
+ ata_dev_info(dev,
+ "%llu sectors, multi %u: %s %s\n",
(unsigned long long)dev->n_sectors,
dev->multi_count, lba_desc, ncq_desc);
}
@@ -2311,15 +2303,14 @@ int ata_dev_configure(struct ata_device *dev)
/* print device info to dmesg */
if (ata_msg_drv(ap) && print_info) {
- ata_dev_printk(dev, KERN_INFO,
- "%s: %s, %s, max %s\n",
- revbuf, modelbuf, fwrevbuf,
- ata_mode_string(xfer_mask));
- ata_dev_printk(dev, KERN_INFO,
- "%Lu sectors, multi %u, CHS %u/%u/%u\n",
- (unsigned long long)dev->n_sectors,
- dev->multi_count, dev->cylinders,
- dev->heads, dev->sectors);
+ ata_dev_info(dev, "%s: %s, %s, max %s\n",
+ revbuf, modelbuf, fwrevbuf,
+ ata_mode_string(xfer_mask));
+ ata_dev_info(dev,
+ "%llu sectors, multi %u, CHS %u/%u/%u\n",
+ (unsigned long long)dev->n_sectors,
+ dev->multi_count, dev->cylinders,
+ dev->heads, dev->sectors);
}
}
@@ -2336,8 +2327,7 @@ int ata_dev_configure(struct ata_device *dev)
rc = atapi_cdb_len(id);
if ((rc < 12) || (rc > ATAPI_CDB_LEN)) {
if (ata_msg_warn(ap))
- ata_dev_printk(dev, KERN_WARNING,
- "unsupported CDB len\n");
+ ata_dev_warn(dev, "unsupported CDB len\n");
rc = -EINVAL;
goto err_out_nosup;
}
@@ -2358,9 +2348,9 @@ int ata_dev_configure(struct ata_device *dev)
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_ENABLE, SATA_AN);
if (err_mask)
- ata_dev_printk(dev, KERN_ERR,
- "failed to enable ATAPI AN "
- "(err_mask=0x%x)\n", err_mask);
+ ata_dev_err(dev,
+ "failed to enable ATAPI AN (err_mask=0x%x)\n",
+ err_mask);
else {
dev->flags |= ATA_DFLAG_AN;
atapi_an_string = ", ATAPI AN";
@@ -2379,12 +2369,12 @@ int ata_dev_configure(struct ata_device *dev)
/* print device info to dmesg */
if (ata_msg_drv(ap) && print_info)
- ata_dev_printk(dev, KERN_INFO,
- "ATAPI: %s, %s, max %s%s%s%s\n",
- modelbuf, fwrevbuf,
- ata_mode_string(xfer_mask),
- cdb_intr_string, atapi_an_string,
- dma_dir_string);
+ ata_dev_info(dev,
+ "ATAPI: %s, %s, max %s%s%s%s\n",
+ modelbuf, fwrevbuf,
+ ata_mode_string(xfer_mask),
+ cdb_intr_string, atapi_an_string,
+ dma_dir_string);
}
/* determine max_sectors */
@@ -2396,8 +2386,7 @@ int ata_dev_configure(struct ata_device *dev)
200 sectors */
if (ata_dev_knobble(dev)) {
if (ata_msg_drv(ap) && print_info)
- ata_dev_printk(dev, KERN_INFO,
- "applying bridge limits\n");
+ ata_dev_info(dev, "applying bridge limits\n");
dev->udma_mask &= ATA_UDMA5;
dev->max_sectors = ATA_MAX_SECTORS;
}
@@ -2423,26 +2412,23 @@ int ata_dev_configure(struct ata_device *dev)
bugs */
if (print_info) {
- ata_dev_printk(dev, KERN_WARNING,
+ ata_dev_warn(dev,
"Drive reports diagnostics failure. This may indicate a drive\n");
- ata_dev_printk(dev, KERN_WARNING,
+ ata_dev_warn(dev,
"fault or invalid emulation. Contact drive vendor for information.\n");
}
}
if ((dev->horkage & ATA_HORKAGE_FIRMWARE_WARN) && print_info) {
- ata_dev_printk(dev, KERN_WARNING, "WARNING: device requires "
- "firmware update to be fully functional.\n");
- ata_dev_printk(dev, KERN_WARNING, " contact the vendor "
- "or visit http://ata.wiki.kernel.org.\n");
+ ata_dev_warn(dev, "WARNING: device requires firmware update to be fully functional\n");
+ ata_dev_warn(dev, " contact the vendor or visit http://ata.wiki.kernel.org\n");
}
return 0;
err_out_nosup:
if (ata_msg_probe(ap))
- ata_dev_printk(dev, KERN_DEBUG,
- "%s: EXIT, err\n", __func__);
+ ata_dev_dbg(dev, "%s: EXIT, err\n", __func__);
return rc;
}
@@ -2663,13 +2649,11 @@ static void sata_print_link_status(struct ata_link *link)
if (ata_phys_link_online(link)) {
tmp = (sstatus >> 4) & 0xf;
- ata_link_printk(link, KERN_INFO,
- "SATA link up %s (SStatus %X SControl %X)\n",
- sata_spd_string(tmp), sstatus, scontrol);
+ ata_link_info(link, "SATA link up %s (SStatus %X SControl %X)\n",
+ sata_spd_string(tmp), sstatus, scontrol);
} else {
- ata_link_printk(link, KERN_INFO,
- "SATA link down (SStatus %X SControl %X)\n",
- sstatus, scontrol);
+ ata_link_info(link, "SATA link down (SStatus %X SControl %X)\n",
+ sstatus, scontrol);
}
}
@@ -2758,8 +2742,8 @@ int sata_down_spd_limit(struct ata_link *link, u32 spd_limit)
link->sata_spd_limit = mask;
- ata_link_printk(link, KERN_WARNING, "limiting SATA link speed to %s\n",
- sata_spd_string(fls(mask)));
+ ata_link_warn(link, "limiting SATA link speed to %s\n",
+ sata_spd_string(fls(mask)));
return 0;
}
@@ -2954,7 +2938,7 @@ int ata_timing_compute(struct ata_device *adev, unsigned short speed,
if (id[ATA_ID_FIELD_VALID] & 2) { /* EIDE drive */
memset(&p, 0, sizeof(p));
- if (speed >= XFER_PIO_0 && speed <= XFER_SW_DMA_0) {
+ if (speed >= XFER_PIO_0 && speed < XFER_SW_DMA_0) {
if (speed <= XFER_PIO_2)
p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO];
else if ((speed <= XFER_PIO_4) ||
@@ -3136,8 +3120,7 @@ int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel)
snprintf(buf, sizeof(buf), "%s",
ata_mode_string(xfer_mask));
- ata_dev_printk(dev, KERN_WARNING,
- "limiting speed to %s\n", buf);
+ ata_dev_warn(dev, "limiting speed to %s\n", buf);
}
ata_unpack_xfermask(xfer_mask, &dev->pio_mask, &dev->mwdma_mask,
@@ -3164,9 +3147,9 @@ static int ata_dev_set_mode(struct ata_device *dev)
dev_err_whine = " (SET_XFERMODE skipped)";
else {
if (nosetxfer)
- ata_dev_printk(dev, KERN_WARNING,
- "NOSETXFER but PATA detected - can't "
- "skip SETXFER, might malfunction\n");
+ ata_dev_warn(dev,
+ "NOSETXFER but PATA detected - can't "
+ "skip SETXFER, might malfunction\n");
err_mask = ata_dev_set_xfermode(dev);
}
@@ -3216,15 +3199,14 @@ static int ata_dev_set_mode(struct ata_device *dev)
DPRINTK("xfer_shift=%u, xfer_mode=0x%x\n",
dev->xfer_shift, (int)dev->xfer_mode);
- ata_dev_printk(dev, KERN_INFO, "configured for %s%s\n",
- ata_mode_string(ata_xfer_mode2mask(dev->xfer_mode)),
- dev_err_whine);
+ ata_dev_info(dev, "configured for %s%s\n",
+ ata_mode_string(ata_xfer_mode2mask(dev->xfer_mode)),
+ dev_err_whine);
return 0;
fail:
- ata_dev_printk(dev, KERN_ERR, "failed to set xfermode "
- "(err_mask=0x%x)\n", err_mask);
+ ata_dev_err(dev, "failed to set xfermode (err_mask=0x%x)\n", err_mask);
return -EIO;
}
@@ -3286,7 +3268,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
/* step 2: always set host PIO timings */
ata_for_each_dev(dev, link, ENABLED) {
if (dev->pio_mode == 0xff) {
- ata_dev_printk(dev, KERN_WARNING, "no PIO support\n");
+ ata_dev_warn(dev, "no PIO support\n");
rc = -EINVAL;
goto out;
}
@@ -3404,7 +3386,7 @@ int ata_wait_ready(struct ata_link *link, unsigned long deadline,
if (!warned && time_after(now, start + 5 * HZ) &&
(deadline - now > 3 * HZ)) {
- ata_link_printk(link, KERN_WARNING,
+ ata_link_warn(link,
"link is slow to respond, please be patient "
"(ready=%d)\n", tmp);
warned = 1;
@@ -3552,16 +3534,14 @@ int sata_link_resume(struct ata_link *link, const unsigned long *params,
} while ((scontrol & 0xf0f) != 0x300 && --tries);
if ((scontrol & 0xf0f) != 0x300) {
- ata_link_printk(link, KERN_ERR,
- "failed to resume link (SControl %X)\n",
- scontrol);
+ ata_link_warn(link, "failed to resume link (SControl %X)\n",
+ scontrol);
return 0;
}
if (tries < ATA_LINK_RESUME_TRIES)
- ata_link_printk(link, KERN_WARNING,
- "link resume succeeded after %d retries\n",
- ATA_LINK_RESUME_TRIES - tries);
+ ata_link_warn(link, "link resume succeeded after %d retries\n",
+ ATA_LINK_RESUME_TRIES - tries);
if ((rc = sata_link_debounce(link, params, deadline)))
return rc;
@@ -3678,8 +3658,9 @@ int ata_std_prereset(struct ata_link *link, unsigned long deadline)
rc = sata_link_resume(link, timing, deadline);
/* whine about phy resume failure but proceed */
if (rc && rc != -EOPNOTSUPP)
- ata_link_printk(link, KERN_WARNING, "failed to resume "
- "link for reset (errno=%d)\n", rc);
+ ata_link_warn(link,
+ "failed to resume link for reset (errno=%d)\n",
+ rc);
}
/* no point in trying softreset on offline link */
@@ -3795,8 +3776,7 @@ int sata_link_hardreset(struct ata_link *link, const unsigned long *timing,
/* online is set iff link is online && reset succeeded */
if (online)
*online = false;
- ata_link_printk(link, KERN_ERR,
- "COMRESET failed (errno=%d)\n", rc);
+ ata_link_err(link, "COMRESET failed (errno=%d)\n", rc);
}
DPRINTK("EXIT, rc=%d\n", rc);
return rc;
@@ -3880,8 +3860,8 @@ static int ata_dev_same_device(struct ata_device *dev, unsigned int new_class,
unsigned char serial[2][ATA_ID_SERNO_LEN + 1];
if (dev->class != new_class) {
- ata_dev_printk(dev, KERN_INFO, "class mismatch %d != %d\n",
- dev->class, new_class);
+ ata_dev_info(dev, "class mismatch %d != %d\n",
+ dev->class, new_class);
return 0;
}
@@ -3891,14 +3871,14 @@ static int ata_dev_same_device(struct ata_device *dev, unsigned int new_class,
ata_id_c_string(new_id, serial[1], ATA_ID_SERNO, sizeof(serial[1]));
if (strcmp(model[0], model[1])) {
- ata_dev_printk(dev, KERN_INFO, "model number mismatch "
- "'%s' != '%s'\n", model[0], model[1]);
+ ata_dev_info(dev, "model number mismatch '%s' != '%s'\n",
+ model[0], model[1]);
return 0;
}
if (strcmp(serial[0], serial[1])) {
- ata_dev_printk(dev, KERN_INFO, "serial number mismatch "
- "'%s' != '%s'\n", serial[0], serial[1]);
+ ata_dev_info(dev, "serial number mismatch '%s' != '%s'\n",
+ serial[0], serial[1]);
return 0;
}
@@ -3968,8 +3948,8 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
new_class != ATA_DEV_ATA &&
new_class != ATA_DEV_ATAPI &&
new_class != ATA_DEV_SEMB) {
- ata_dev_printk(dev, KERN_INFO, "class mismatch %u != %u\n",
- dev->class, new_class);
+ ata_dev_info(dev, "class mismatch %u != %u\n",
+ dev->class, new_class);
rc = -ENODEV;
goto fail;
}
@@ -3990,9 +3970,9 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
return 0;
/* n_sectors has changed */
- ata_dev_printk(dev, KERN_WARNING, "n_sectors mismatch %llu != %llu\n",
- (unsigned long long)n_sectors,
- (unsigned long long)dev->n_sectors);
+ ata_dev_warn(dev, "n_sectors mismatch %llu != %llu\n",
+ (unsigned long long)n_sectors,
+ (unsigned long long)dev->n_sectors);
/*
* Something could have caused HPA to be unlocked
@@ -4001,9 +3981,9 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
*/
if (dev->n_native_sectors == n_native_sectors &&
dev->n_sectors > n_sectors && dev->n_sectors == n_native_sectors) {
- ata_dev_printk(dev, KERN_WARNING,
- "new n_sectors matches native, probably "
- "late HPA unlock, n_sectors updated\n");
+ ata_dev_warn(dev,
+ "new n_sectors matches native, probably "
+ "late HPA unlock, n_sectors updated\n");
/* use the larger n_sectors */
return 0;
}
@@ -4017,9 +3997,9 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
if (dev->n_native_sectors == n_native_sectors &&
dev->n_sectors < n_sectors && n_sectors == n_native_sectors &&
!(dev->horkage & ATA_HORKAGE_BROKEN_HPA)) {
- ata_dev_printk(dev, KERN_WARNING,
- "old n_sectors matches native, probably "
- "late HPA lock, will try to unlock HPA\n");
+ ata_dev_warn(dev,
+ "old n_sectors matches native, probably "
+ "late HPA lock, will try to unlock HPA\n");
/* try unlocking HPA */
dev->flags |= ATA_DFLAG_UNLOCK_HPA;
rc = -EIO;
@@ -4030,7 +4010,7 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
dev->n_native_sectors = n_native_sectors;
dev->n_sectors = n_sectors;
fail:
- ata_dev_printk(dev, KERN_ERR, "revalidation failed (errno=%d)\n", rc);
+ ata_dev_err(dev, "revalidation failed (errno=%d)\n", rc);
return rc;
}
@@ -4358,15 +4338,15 @@ static void ata_dev_xfermask(struct ata_device *dev)
if (ata_dma_blacklisted(dev)) {
xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
- ata_dev_printk(dev, KERN_WARNING,
- "device is on DMA blacklist, disabling DMA\n");
+ ata_dev_warn(dev,
+ "device is on DMA blacklist, disabling DMA\n");
}
if ((host->flags & ATA_HOST_SIMPLEX) &&
host->simplex_claimed && host->simplex_claimed != ap) {
xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
- ata_dev_printk(dev, KERN_WARNING, "simplex DMA is claimed by "
- "other device, disabling DMA\n");
+ ata_dev_warn(dev,
+ "simplex DMA is claimed by other device, disabling DMA\n");
}
if (ap->flags & ATA_FLAG_NO_IORDY)
@@ -4386,8 +4366,8 @@ static void ata_dev_xfermask(struct ata_device *dev)
if (xfer_mask & (0xF8 << ATA_SHIFT_UDMA))
/* UDMA/44 or higher would be available */
if (cable_is_40wire(ap)) {
- ata_dev_printk(dev, KERN_WARNING,
- "limited to UDMA/33 due to 40-wire cable\n");
+ ata_dev_warn(dev,
+ "limited to UDMA/33 due to 40-wire cable\n");
xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
}
@@ -4954,8 +4934,8 @@ int ata_qc_complete_multiple(struct ata_port *ap, u32 qc_active)
done_mask = ap->qc_active ^ qc_active;
if (unlikely(done_mask & qc_active)) {
- ata_port_printk(ap, KERN_ERR, "illegal qc_active transition "
- "(%08x->%08x)\n", ap->qc_active, qc_active);
+ ata_port_err(ap, "illegal qc_active transition (%08x->%08x)\n",
+ ap->qc_active, qc_active);
return -EINVAL;
}
@@ -5847,9 +5827,9 @@ int ata_host_start(struct ata_host *host)
rc = ap->ops->port_start(ap);
if (rc) {
if (rc != -ENODEV)
- dev_printk(KERN_ERR, host->dev,
- "failed to start port %d "
- "(errno=%d)\n", i, rc);
+ dev_err(host->dev,
+ "failed to start port %d (errno=%d)\n",
+ i, rc);
goto err_out;
}
}
@@ -5971,8 +5951,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
/* host must have been started */
if (!(host->flags & ATA_HOST_STARTED)) {
- dev_printk(KERN_ERR, host->dev,
- "BUG: trying to register unstarted host\n");
+ dev_err(host->dev, "BUG: trying to register unstarted host\n");
WARN_ON(1);
return -EINVAL;
}
@@ -6023,14 +6002,13 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
ap->udma_mask);
if (!ata_port_is_dummy(ap)) {
- ata_port_printk(ap, KERN_INFO,
- "%cATA max %s %s\n",
- (ap->flags & ATA_FLAG_SATA) ? 'S' : 'P',
- ata_mode_string(xfer_mask),
- ap->link.eh_info.desc);
+ ata_port_info(ap, "%cATA max %s %s\n",
+ (ap->flags & ATA_FLAG_SATA) ? 'S' : 'P',
+ ata_mode_string(xfer_mask),
+ ap->link.eh_info.desc);
ata_ehi_clear_desc(&ap->link.eh_info);
} else
- ata_port_printk(ap, KERN_INFO, "DUMMY\n");
+ ata_port_info(ap, "DUMMY\n");
}
/* perform each probe asynchronously */
@@ -6242,8 +6220,8 @@ int ata_pci_device_do_resume(struct pci_dev *pdev)
rc = pcim_enable_device(pdev);
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "failed to enable device after resume (%d)\n", rc);
+ dev_err(&pdev->dev,
+ "failed to enable device after resume (%d)\n", rc);
return rc;
}
@@ -6600,6 +6578,82 @@ const struct ata_port_info ata_dummy_port_info = {
};
/*
+ * Utility print functions
+ */
+int ata_port_printk(const struct ata_port *ap, const char *level,
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ r = printk("%sata%u: %pV", level, ap->print_id, &vaf);
+
+ va_end(args);
+
+ return r;
+}
+EXPORT_SYMBOL(ata_port_printk);
+
+int ata_link_printk(const struct ata_link *link, const char *level,
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (sata_pmp_attached(link->ap) || link->ap->slave_link)
+ r = printk("%sata%u.%02u: %pV",
+ level, link->ap->print_id, link->pmp, &vaf);
+ else
+ r = printk("%sata%u: %pV",
+ level, link->ap->print_id, &vaf);
+
+ va_end(args);
+
+ return r;
+}
+EXPORT_SYMBOL(ata_link_printk);
+
+int ata_dev_printk(const struct ata_device *dev, const char *level,
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ r = printk("%sata%u.%02u: %pV",
+ level, dev->link->ap->print_id, dev->link->pmp + dev->devno,
+ &vaf);
+
+ va_end(args);
+
+ return r;
+}
+EXPORT_SYMBOL(ata_dev_printk);
+
+void ata_print_version(const struct device *dev, const char *version)
+{
+ dev_printk(KERN_DEBUG, dev, "version %s\n", version);
+}
+EXPORT_SYMBOL(ata_print_version);
+
+/*
* libata is essentially a library of internal helper functions for
* low-level ATA host controller drivers. As such, the API/ABI is
* likely to change as new drivers are added and updated.
@@ -6659,6 +6713,7 @@ EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
EXPORT_SYMBOL_GPL(ata_scsi_slave_destroy);
EXPORT_SYMBOL_GPL(ata_scsi_change_queue_depth);
+EXPORT_SYMBOL_GPL(__ata_change_queue_depth);
EXPORT_SYMBOL_GPL(sata_scr_valid);
EXPORT_SYMBOL_GPL(sata_scr_read);
EXPORT_SYMBOL_GPL(sata_scr_write);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 7f099d6e4e0b..a9b282038000 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -34,6 +34,7 @@
#include <linux/kernel.h>
#include <linux/blkdev.h>
+#include <linux/export.h>
#include <linux/pci.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
@@ -782,8 +783,9 @@ void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap)
spin_unlock_irqrestore(ap->lock, flags);
goto repeat;
}
- ata_port_printk(ap, KERN_ERR, "EH pending after %d "
- "tries, giving up\n", ATA_EH_MAX_TRIES);
+ ata_port_err(ap,
+ "EH pending after %d tries, giving up\n",
+ ATA_EH_MAX_TRIES);
ap->pflags &= ~ATA_PFLAG_EH_PENDING;
}
@@ -816,7 +818,7 @@ void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap)
schedule_delayed_work(&ap->hotplug_task, 0);
if (ap->pflags & ATA_PFLAG_RECOVERED)
- ata_port_printk(ap, KERN_INFO, "EH complete\n");
+ ata_port_info(ap, "EH complete\n");
ap->pflags &= ~(ATA_PFLAG_SCSI_HOTPLUG | ATA_PFLAG_RECOVERED);
@@ -1310,7 +1312,7 @@ void ata_dev_disable(struct ata_device *dev)
return;
if (ata_msg_drv(dev->link->ap))
- ata_dev_printk(dev, KERN_WARNING, "disabled\n");
+ ata_dev_warn(dev, "disabled\n");
ata_acpi_on_disable(dev);
ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO0 | ATA_DNXFER_QUIET);
dev->class++;
@@ -1515,8 +1517,8 @@ static int ata_eh_read_log_10h(struct ata_device *dev,
for (i = 0; i < ATA_SECT_SIZE; i++)
csum += buf[i];
if (csum)
- ata_dev_printk(dev, KERN_WARNING,
- "invalid checksum 0x%x on log page 10h\n", csum);
+ ata_dev_warn(dev, "invalid checksum 0x%x on log page 10h\n",
+ csum);
if (buf[0] & 0x80)
return -ENOENT;
@@ -1716,14 +1718,14 @@ void ata_eh_analyze_ncq_error(struct ata_link *link)
memset(&tf, 0, sizeof(tf));
rc = ata_eh_read_log_10h(dev, &tag, &tf);
if (rc) {
- ata_link_printk(link, KERN_ERR, "failed to read log page 10h "
- "(errno=%d)\n", rc);
+ ata_link_err(link, "failed to read log page 10h (errno=%d)\n",
+ rc);
return;
}
if (!(link->sactive & (1 << tag))) {
- ata_link_printk(link, KERN_ERR, "log page 10h reported "
- "inactive tag %d\n", tag);
+ ata_link_err(link, "log page 10h reported inactive tag %d\n",
+ tag);
return;
}
@@ -1988,8 +1990,7 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev,
(dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ |
ATA_DFLAG_NCQ_OFF)) == ATA_DFLAG_NCQ) {
dev->flags |= ATA_DFLAG_NCQ_OFF;
- ata_dev_printk(dev, KERN_WARNING,
- "NCQ disabled due to excessive errors\n");
+ ata_dev_warn(dev, "NCQ disabled due to excessive errors\n");
goto done;
}
@@ -2374,24 +2375,24 @@ static void ata_eh_link_report(struct ata_link *link)
ap->eh_tries);
if (ehc->i.dev) {
- ata_dev_printk(ehc->i.dev, KERN_ERR, "exception Emask 0x%x "
- "SAct 0x%x SErr 0x%x action 0x%x%s%s\n",
- ehc->i.err_mask, link->sactive, ehc->i.serror,
- ehc->i.action, frozen, tries_buf);
+ ata_dev_err(ehc->i.dev, "exception Emask 0x%x "
+ "SAct 0x%x SErr 0x%x action 0x%x%s%s\n",
+ ehc->i.err_mask, link->sactive, ehc->i.serror,
+ ehc->i.action, frozen, tries_buf);
if (desc)
- ata_dev_printk(ehc->i.dev, KERN_ERR, "%s\n", desc);
+ ata_dev_err(ehc->i.dev, "%s\n", desc);
} else {
- ata_link_printk(link, KERN_ERR, "exception Emask 0x%x "
- "SAct 0x%x SErr 0x%x action 0x%x%s%s\n",
- ehc->i.err_mask, link->sactive, ehc->i.serror,
- ehc->i.action, frozen, tries_buf);
+ ata_link_err(link, "exception Emask 0x%x "
+ "SAct 0x%x SErr 0x%x action 0x%x%s%s\n",
+ ehc->i.err_mask, link->sactive, ehc->i.serror,
+ ehc->i.action, frozen, tries_buf);
if (desc)
- ata_link_printk(link, KERN_ERR, "%s\n", desc);
+ ata_link_err(link, "%s\n", desc);
}
#ifdef CONFIG_ATA_VERBOSE_ERROR
if (ehc->i.serror)
- ata_link_printk(link, KERN_ERR,
+ ata_link_err(link,
"SError: { %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s}\n",
ehc->i.serror & SERR_DATA_RECOVERED ? "RecovData " : "",
ehc->i.serror & SERR_COMM_RECOVERED ? "RecovComm " : "",
@@ -2456,11 +2457,11 @@ static void ata_eh_link_report(struct ata_link *link)
} else {
const char *descr = ata_get_cmd_descript(cmd->command);
if (descr)
- ata_dev_printk(qc->dev, KERN_ERR,
- "failed command: %s\n", descr);
+ ata_dev_err(qc->dev, "failed command: %s\n",
+ descr);
}
- ata_dev_printk(qc->dev, KERN_ERR,
+ ata_dev_err(qc->dev,
"cmd %02x/%02x:%02x:%02x:%02x:%02x/%02x:%02x:%02x:%02x:%02x/%02x "
"tag %d%s\n %s"
"res %02x/%02x:%02x:%02x:%02x:%02x/%02x:%02x:%02x:%02x:%02x/%02x "
@@ -2481,11 +2482,9 @@ static void ata_eh_link_report(struct ata_link *link)
if (res->command & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ |
ATA_ERR)) {
if (res->command & ATA_BUSY)
- ata_dev_printk(qc->dev, KERN_ERR,
- "status: { Busy }\n");
+ ata_dev_err(qc->dev, "status: { Busy }\n");
else
- ata_dev_printk(qc->dev, KERN_ERR,
- "status: { %s%s%s%s}\n",
+ ata_dev_err(qc->dev, "status: { %s%s%s%s}\n",
res->command & ATA_DRDY ? "DRDY " : "",
res->command & ATA_DF ? "DF " : "",
res->command & ATA_DRQ ? "DRQ " : "",
@@ -2495,8 +2494,7 @@ static void ata_eh_link_report(struct ata_link *link)
if (cmd->command != ATA_CMD_PACKET &&
(res->feature & (ATA_ICRC | ATA_UNC | ATA_IDNF |
ATA_ABORTED)))
- ata_dev_printk(qc->dev, KERN_ERR,
- "error: { %s%s%s%s}\n",
+ ata_dev_err(qc->dev, "error: { %s%s%s%s}\n",
res->feature & ATA_ICRC ? "ICRC " : "",
res->feature & ATA_UNC ? "UNC " : "",
res->feature & ATA_IDNF ? "IDNF " : "",
@@ -2535,8 +2533,7 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset,
return reset(link, classes, deadline);
}
-static int ata_eh_followup_srst_needed(struct ata_link *link,
- int rc, const unsigned int *classes)
+static int ata_eh_followup_srst_needed(struct ata_link *link, int rc)
{
if ((link->flags & ATA_LFLAG_NO_SRST) || ata_link_offline(link))
return 0;
@@ -2650,8 +2647,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
if (rc) {
if (rc == -ENOENT) {
- ata_link_printk(link, KERN_DEBUG,
- "port disabled. ignoring.\n");
+ ata_link_dbg(link, "port disabled--ignoring\n");
ehc->i.action &= ~ATA_EH_RESET;
ata_for_each_dev(dev, link, ALL)
@@ -2659,8 +2655,9 @@ int ata_eh_reset(struct ata_link *link, int classify,
rc = 0;
} else
- ata_link_printk(link, KERN_ERR,
- "prereset failed (errno=%d)\n", rc);
+ ata_link_err(link,
+ "prereset failed (errno=%d)\n",
+ rc);
goto out;
}
@@ -2689,8 +2686,8 @@ int ata_eh_reset(struct ata_link *link, int classify,
if (reset) {
if (verbose)
- ata_link_printk(link, KERN_INFO, "%s resetting link\n",
- reset == softreset ? "soft" : "hard");
+ ata_link_info(link, "%s resetting link\n",
+ reset == softreset ? "soft" : "hard");
/* mark that this EH session started with reset */
ehc->last_reset = jiffies;
@@ -2710,8 +2707,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
int tmp;
if (verbose)
- ata_link_printk(slave, KERN_INFO,
- "hard resetting link\n");
+ ata_link_info(slave, "hard resetting link\n");
ata_eh_about_to_do(slave, NULL, ATA_EH_RESET);
tmp = ata_do_reset(slave, reset, classes, deadline,
@@ -2730,13 +2726,12 @@ int ata_eh_reset(struct ata_link *link, int classify,
/* perform follow-up SRST if necessary */
if (reset == hardreset &&
- ata_eh_followup_srst_needed(link, rc, classes)) {
+ ata_eh_followup_srst_needed(link, rc)) {
reset = softreset;
if (!reset) {
- ata_link_printk(link, KERN_ERR,
- "follow-up softreset required "
- "but no softreset available\n");
+ ata_link_err(link,
+ "follow-up softreset required but no softreset available\n");
failed_link = link;
rc = -EINVAL;
goto fail;
@@ -2751,8 +2746,8 @@ int ata_eh_reset(struct ata_link *link, int classify,
}
} else {
if (verbose)
- ata_link_printk(link, KERN_INFO, "no reset method "
- "available, skipping reset\n");
+ ata_link_info(link,
+ "no reset method available, skipping reset\n");
if (!(lflags & ATA_LFLAG_ASSUME_CLASS))
lflags |= ATA_LFLAG_ASSUME_ATA;
}
@@ -2830,36 +2825,35 @@ int ata_eh_reset(struct ata_link *link, int classify,
ata_for_each_dev(dev, link, ALL) {
if (ata_phys_link_online(ata_dev_phys_link(dev))) {
if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
- ata_dev_printk(dev, KERN_DEBUG, "link online "
- "but device misclassifed\n");
+ ata_dev_dbg(dev, "link online but device misclassified\n");
classes[dev->devno] = ATA_DEV_NONE;
nr_unknown++;
}
} else if (ata_phys_link_offline(ata_dev_phys_link(dev))) {
if (ata_class_enabled(classes[dev->devno]))
- ata_dev_printk(dev, KERN_DEBUG, "link offline, "
- "clearing class %d to NONE\n",
- classes[dev->devno]);
+ ata_dev_dbg(dev,
+ "link offline, clearing class %d to NONE\n",
+ classes[dev->devno]);
classes[dev->devno] = ATA_DEV_NONE;
} else if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
- ata_dev_printk(dev, KERN_DEBUG, "link status unknown, "
- "clearing UNKNOWN to NONE\n");
+ ata_dev_dbg(dev,
+ "link status unknown, clearing UNKNOWN to NONE\n");
classes[dev->devno] = ATA_DEV_NONE;
}
}
if (classify && nr_unknown) {
if (try < max_tries) {
- ata_link_printk(link, KERN_WARNING, "link online but "
- "%d devices misclassified, retrying\n",
- nr_unknown);
+ ata_link_warn(link,
+ "link online but %d devices misclassified, retrying\n",
+ nr_unknown);
failed_link = link;
rc = -EAGAIN;
goto fail;
}
- ata_link_printk(link, KERN_WARNING,
- "link online but %d devices misclassified, "
- "device detection might fail\n", nr_unknown);
+ ata_link_warn(link,
+ "link online but %d devices misclassified, "
+ "device detection might fail\n", nr_unknown);
}
/* reset successful, schedule revalidation */
@@ -2889,14 +2883,23 @@ int ata_eh_reset(struct ata_link *link, int classify,
sata_scr_read(link, SCR_STATUS, &sstatus))
rc = -ERESTART;
- if (rc == -ERESTART || try >= max_tries)
+ if (try >= max_tries) {
+ /*
+ * Thaw host port even if reset failed, so that the port
+ * can be retried on the next phy event. This risks
+ * repeated EH runs but seems to be a better tradeoff than
+ * shutting down a port after a botched hotplug attempt.
+ */
+ if (ata_is_host_link(link))
+ ata_eh_thaw_port(ap);
goto out;
+ }
now = jiffies;
if (time_before(now, deadline)) {
unsigned long delta = deadline - now;
- ata_link_printk(failed_link, KERN_WARNING,
+ ata_link_warn(failed_link,
"reset failed (errno=%d), retrying in %u secs\n",
rc, DIV_ROUND_UP(jiffies_to_msecs(delta), 1000));
@@ -2906,6 +2909,16 @@ int ata_eh_reset(struct ata_link *link, int classify,
ata_eh_acquire(ap);
}
+ /*
+ * While disks spinup behind PMP, some controllers fail sending SRST.
+ * They need to be reset - as well as the PMP - before retrying.
+ */
+ if (rc == -ERESTART) {
+ if (ata_is_host_link(link))
+ ata_eh_thaw_port(ap);
+ goto out;
+ }
+
if (try == max_tries - 1) {
sata_down_spd_limit(link, 0);
if (slave)
@@ -2987,7 +3000,7 @@ static void ata_eh_park_issue_cmd(struct ata_device *dev, int park)
tf.protocol |= ATA_PROT_NODATA;
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
if (park && (err_mask || tf.lbal != 0xc4)) {
- ata_dev_printk(dev, KERN_ERR, "head unload failed!\n");
+ ata_dev_err(dev, "head unload failed!\n");
ehc->unloaded_mask &= ~(1 << dev->devno);
}
}
@@ -3198,8 +3211,9 @@ static int atapi_eh_clear_ua(struct ata_device *dev)
err_mask = atapi_eh_tur(dev, &sense_key);
if (err_mask != 0 && err_mask != AC_ERR_DEV) {
- ata_dev_printk(dev, KERN_WARNING, "TEST_UNIT_READY "
- "failed (err_mask=0x%x)\n", err_mask);
+ ata_dev_warn(dev,
+ "TEST_UNIT_READY failed (err_mask=0x%x)\n",
+ err_mask);
return -EIO;
}
@@ -3208,14 +3222,14 @@ static int atapi_eh_clear_ua(struct ata_device *dev)
err_mask = atapi_eh_request_sense(dev, sense_buffer, sense_key);
if (err_mask) {
- ata_dev_printk(dev, KERN_WARNING, "failed to clear "
+ ata_dev_warn(dev, "failed to clear "
"UNIT ATTENTION (err_mask=0x%x)\n", err_mask);
return -EIO;
}
}
- ata_dev_printk(dev, KERN_WARNING,
- "UNIT ATTENTION persists after %d tries\n", ATA_EH_UA_TRIES);
+ ata_dev_warn(dev, "UNIT ATTENTION persists after %d tries\n",
+ ATA_EH_UA_TRIES);
return 0;
}
@@ -3266,7 +3280,7 @@ static int ata_eh_maybe_retry_flush(struct ata_device *dev)
tf.flags |= ATA_TFLAG_DEVICE;
tf.protocol = ATA_PROT_NODATA;
- ata_dev_printk(dev, KERN_WARNING, "retrying FLUSH 0x%x Emask 0x%x\n",
+ ata_dev_warn(dev, "retrying FLUSH 0x%x Emask 0x%x\n",
tf.command, qc->err_mask);
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
@@ -3281,7 +3295,7 @@ static int ata_eh_maybe_retry_flush(struct ata_device *dev)
*/
qc->scsicmd->allowed = max(qc->scsicmd->allowed, 1);
} else {
- ata_dev_printk(dev, KERN_WARNING, "FLUSH failed Emask 0x%x\n",
+ ata_dev_warn(dev, "FLUSH failed Emask 0x%x\n",
err_mask);
rc = -EIO;
@@ -3355,9 +3369,9 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_DISABLE, SATA_DIPM);
if (err_mask && err_mask != AC_ERR_DEV) {
- ata_dev_printk(dev, KERN_WARNING,
- "failed to disable DIPM, Emask 0x%x\n",
- err_mask);
+ ata_dev_warn(dev,
+ "failed to disable DIPM, Emask 0x%x\n",
+ err_mask);
rc = -EIO;
goto fail;
}
@@ -3399,7 +3413,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_ENABLE, SATA_DIPM);
if (err_mask && err_mask != AC_ERR_DEV) {
- ata_dev_printk(dev, KERN_WARNING,
+ ata_dev_warn(dev,
"failed to enable DIPM, Emask 0x%x\n",
err_mask);
rc = -EIO;
@@ -3418,8 +3432,7 @@ fail:
/* if no device or only one more chance is left, disable LPM */
if (!dev || ehc->tries[dev->devno] <= 2) {
- ata_link_printk(link, KERN_WARNING,
- "disabling LPM on the link\n");
+ ata_link_warn(link, "disabling LPM on the link\n");
link->flags |= ATA_LFLAG_NO_LPM;
}
if (r_failed_dev)
@@ -3690,8 +3703,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
rc = ata_eh_reset(link, ata_link_nr_vacant(link),
prereset, softreset, hardreset, postreset);
if (rc) {
- ata_link_printk(link, KERN_ERR,
- "reset failed, giving up\n");
+ ata_link_err(link, "reset failed, giving up\n");
goto out;
}
}
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c
index f06b7ea590d3..21b80c555c60 100644
--- a/drivers/ata/libata-pmp.c
+++ b/drivers/ata/libata-pmp.c
@@ -8,6 +8,7 @@
*/
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/libata.h>
#include <linux/slab.h>
#include "libata.h"
@@ -147,8 +148,8 @@ int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *r_val)
err_mask = sata_pmp_read(link, reg, r_val);
if (err_mask) {
- ata_link_printk(link, KERN_WARNING, "failed to read SCR %d "
- "(Emask=0x%x)\n", reg, err_mask);
+ ata_link_warn(link, "failed to read SCR %d (Emask=0x%x)\n",
+ reg, err_mask);
return -EIO;
}
return 0;
@@ -178,8 +179,8 @@ int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val)
err_mask = sata_pmp_write(link, reg, val);
if (err_mask) {
- ata_link_printk(link, KERN_WARNING, "failed to write SCR %d "
- "(Emask=0x%x)\n", reg, err_mask);
+ ata_link_warn(link, "failed to write SCR %d (Emask=0x%x)\n",
+ reg, err_mask);
return -EIO;
}
return 0;
@@ -231,8 +232,8 @@ static int sata_pmp_read_gscr(struct ata_device *dev, u32 *gscr)
err_mask = sata_pmp_read(dev->link, reg, &gscr[reg]);
if (err_mask) {
- ata_dev_printk(dev, KERN_ERR, "failed to read PMP "
- "GSCR[%d] (Emask=0x%x)\n", reg, err_mask);
+ ata_dev_err(dev, "failed to read PMP GSCR[%d] (Emask=0x%x)\n",
+ reg, err_mask);
return -EIO;
}
}
@@ -311,26 +312,25 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info)
}
if (print_info) {
- ata_dev_printk(dev, KERN_INFO, "Port Multiplier %s, "
- "0x%04x:0x%04x r%d, %d ports, feat 0x%x/0x%x\n",
- sata_pmp_spec_rev_str(gscr), vendor, devid,
- sata_pmp_gscr_rev(gscr),
- nr_ports, gscr[SATA_PMP_GSCR_FEAT_EN],
- gscr[SATA_PMP_GSCR_FEAT]);
+ ata_dev_info(dev, "Port Multiplier %s, "
+ "0x%04x:0x%04x r%d, %d ports, feat 0x%x/0x%x\n",
+ sata_pmp_spec_rev_str(gscr), vendor, devid,
+ sata_pmp_gscr_rev(gscr),
+ nr_ports, gscr[SATA_PMP_GSCR_FEAT_EN],
+ gscr[SATA_PMP_GSCR_FEAT]);
if (!(dev->flags & ATA_DFLAG_AN))
- ata_dev_printk(dev, KERN_INFO,
+ ata_dev_info(dev,
"Asynchronous notification not supported, "
- "hotplug won't\n work on fan-out "
- "ports. Use warm-plug instead.\n");
+ "hotplug won't work on fan-out ports. Use warm-plug instead.\n");
}
return 0;
fail:
- ata_dev_printk(dev, KERN_ERR,
- "failed to configure Port Multiplier (%s, Emask=0x%x)\n",
- reason, err_mask);
+ ata_dev_err(dev,
+ "failed to configure Port Multiplier (%s, Emask=0x%x)\n",
+ reason, err_mask);
return rc;
}
@@ -389,12 +389,9 @@ static void sata_pmp_quirks(struct ata_port *ap)
/* link reports offline after LPM */
link->flags |= ATA_LFLAG_NO_LPM;
- /* Class code report is unreliable and SRST
- * times out under certain configurations.
- */
+ /* Class code report is unreliable. */
if (link->pmp < 5)
- link->flags |= ATA_LFLAG_NO_SRST |
- ATA_LFLAG_ASSUME_ATA;
+ link->flags |= ATA_LFLAG_ASSUME_ATA;
/* port 5 is for SEMB device and it doesn't like SRST */
if (link->pmp == 5)
@@ -485,20 +482,17 @@ int sata_pmp_attach(struct ata_device *dev)
/* is it hanging off the right place? */
if (!sata_pmp_supported(ap)) {
- ata_dev_printk(dev, KERN_ERR,
- "host does not support Port Multiplier\n");
+ ata_dev_err(dev, "host does not support Port Multiplier\n");
return -EINVAL;
}
if (!ata_is_host_link(link)) {
- ata_dev_printk(dev, KERN_ERR,
- "Port Multipliers cannot be nested\n");
+ ata_dev_err(dev, "Port Multipliers cannot be nested\n");
return -EINVAL;
}
if (dev->devno) {
- ata_dev_printk(dev, KERN_ERR,
- "Port Multiplier must be the first device\n");
+ ata_dev_err(dev, "Port Multiplier must be the first device\n");
return -EINVAL;
}
@@ -517,8 +511,7 @@ int sata_pmp_attach(struct ata_device *dev)
rc = sata_pmp_init_links(ap, sata_pmp_gscr_ports(dev->gscr));
if (rc) {
- ata_dev_printk(dev, KERN_INFO,
- "failed to initialize PMP links\n");
+ ata_dev_info(dev, "failed to initialize PMP links\n");
goto fail;
}
@@ -562,7 +555,7 @@ static void sata_pmp_detach(struct ata_device *dev)
struct ata_link *tlink;
unsigned long flags;
- ata_dev_printk(dev, KERN_INFO, "Port Multiplier detaching\n");
+ ata_dev_info(dev, "Port Multiplier detaching\n");
WARN_ON(!ata_is_host_link(link) || dev->devno ||
link->pmp != SATA_PMP_CTRL_PORT);
@@ -609,23 +602,23 @@ static int sata_pmp_same_pmp(struct ata_device *dev, const u32 *new_gscr)
new_nr_ports = sata_pmp_gscr_ports(new_gscr);
if (old_vendor != new_vendor) {
- ata_dev_printk(dev, KERN_INFO, "Port Multiplier "
- "vendor mismatch '0x%x' != '0x%x'\n",
- old_vendor, new_vendor);
+ ata_dev_info(dev,
+ "Port Multiplier vendor mismatch '0x%x' != '0x%x'\n",
+ old_vendor, new_vendor);
return 0;
}
if (old_devid != new_devid) {
- ata_dev_printk(dev, KERN_INFO, "Port Multiplier "
- "device ID mismatch '0x%x' != '0x%x'\n",
- old_devid, new_devid);
+ ata_dev_info(dev,
+ "Port Multiplier device ID mismatch '0x%x' != '0x%x'\n",
+ old_devid, new_devid);
return 0;
}
if (old_nr_ports != new_nr_ports) {
- ata_dev_printk(dev, KERN_INFO, "Port Multiplier "
- "nr_ports mismatch '0x%x' != '0x%x'\n",
- old_nr_ports, new_nr_ports);
+ ata_dev_info(dev,
+ "Port Multiplier nr_ports mismatch '0x%x' != '0x%x'\n",
+ old_nr_ports, new_nr_ports);
return 0;
}
@@ -691,8 +684,7 @@ static int sata_pmp_revalidate(struct ata_device *dev, unsigned int new_class)
return 0;
fail:
- ata_dev_printk(dev, KERN_ERR,
- "PMP revalidation failed (errno=%d)\n", rc);
+ ata_dev_err(dev, "PMP revalidation failed (errno=%d)\n", rc);
DPRINTK("EXIT, rc=%d\n", rc);
return rc;
}
@@ -716,13 +708,14 @@ static int sata_pmp_revalidate_quick(struct ata_device *dev)
err_mask = sata_pmp_read(dev->link, SATA_PMP_GSCR_PROD_ID, &prod_id);
if (err_mask) {
- ata_dev_printk(dev, KERN_ERR, "failed to read PMP product ID "
- "(Emask=0x%x)\n", err_mask);
+ ata_dev_err(dev,
+ "failed to read PMP product ID (Emask=0x%x)\n",
+ err_mask);
return -EIO;
}
if (prod_id != dev->gscr[SATA_PMP_GSCR_PROD_ID]) {
- ata_dev_printk(dev, KERN_ERR, "PMP product ID mismatch\n");
+ ata_dev_err(dev, "PMP product ID mismatch\n");
/* something weird is going on, request full PMP recovery */
return -EIO;
}
@@ -777,8 +770,7 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
rc = ata_eh_reset(link, 0, prereset, softreset, hardreset,
postreset);
if (rc) {
- ata_link_printk(link, KERN_ERR,
- "failed to reset PMP, giving up\n");
+ ata_link_err(link, "failed to reset PMP, giving up\n");
goto fail;
}
@@ -819,9 +811,9 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
ehc->i.action |= ATA_EH_RESET;
goto retry;
} else {
- ata_dev_printk(dev, KERN_ERR, "failed to recover PMP "
- "after %d tries, giving up\n",
- ATA_EH_PMP_TRIES);
+ ata_dev_err(dev,
+ "failed to recover PMP after %d tries, giving up\n",
+ ATA_EH_PMP_TRIES);
goto fail;
}
}
@@ -867,8 +859,9 @@ static int sata_pmp_eh_handle_disabled_links(struct ata_port *ap)
/* unconditionally clear SError.N */
rc = sata_scr_write(link, SCR_ERROR, SERR_PHYRDY_CHG);
if (rc) {
- ata_link_printk(link, KERN_ERR, "failed to clear "
- "SError.N (errno=%d)\n", rc);
+ ata_link_err(link,
+ "failed to clear SError.N (errno=%d)\n",
+ rc);
return rc;
}
@@ -890,7 +883,7 @@ static int sata_pmp_handle_link_fail(struct ata_link *link, int *link_tries)
/* disable this link */
if (!(link->flags & ATA_LFLAG_DISABLED)) {
- ata_link_printk(link, KERN_WARNING,
+ ata_link_warn(link,
"failed to recover link after %d tries, disabling\n",
ATA_EH_PMP_LINK_TRIES);
@@ -974,7 +967,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
err_mask = sata_pmp_write(pmp_link, SATA_PMP_GSCR_FEAT_EN,
gscr[SATA_PMP_GSCR_FEAT_EN]);
if (err_mask) {
- ata_link_printk(pmp_link, KERN_WARNING,
+ ata_link_warn(pmp_link,
"failed to disable NOTIFY (err_mask=0x%x)\n",
err_mask);
goto pmp_fail;
@@ -1018,8 +1011,9 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
err_mask = sata_pmp_write(pmp_link, SATA_PMP_GSCR_FEAT_EN,
gscr[SATA_PMP_GSCR_FEAT_EN]);
if (err_mask) {
- ata_dev_printk(pmp_dev, KERN_ERR, "failed to write "
- "PMP_FEAT_EN (Emask=0x%x)\n", err_mask);
+ ata_dev_err(pmp_dev,
+ "failed to write PMP_FEAT_EN (Emask=0x%x)\n",
+ err_mask);
rc = -EIO;
goto pmp_fail;
}
@@ -1028,8 +1022,9 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
/* check GSCR_ERROR */
err_mask = sata_pmp_read(pmp_link, SATA_PMP_GSCR_ERROR, &gscr_error);
if (err_mask) {
- ata_dev_printk(pmp_dev, KERN_ERR, "failed to read "
- "PMP_GSCR_ERROR (Emask=0x%x)\n", err_mask);
+ ata_dev_err(pmp_dev,
+ "failed to read PMP_GSCR_ERROR (Emask=0x%x)\n",
+ err_mask);
rc = -EIO;
goto pmp_fail;
}
@@ -1043,17 +1038,16 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
ata_ehi_hotplugged(&link->eh_context.i);
cnt++;
} else {
- ata_link_printk(link, KERN_WARNING,
- "PHY status changed but maxed out on retries, "
- "giving up\n");
- ata_link_printk(link, KERN_WARNING,
- "Manully issue scan to resume this link\n");
+ ata_link_warn(link,
+ "PHY status changed but maxed out on retries, giving up\n");
+ ata_link_warn(link,
+ "Manually issue scan to resume this link\n");
}
}
if (cnt) {
- ata_port_printk(ap, KERN_INFO, "PMP SError.N set for some "
- "ports, repeating recovery\n");
+ ata_port_info(ap,
+ "PMP SError.N set for some ports, repeating recovery\n");
goto retry;
}
@@ -1081,9 +1075,8 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
goto retry;
}
- ata_port_printk(ap, KERN_ERR,
- "failed to recover PMP after %d tries, giving up\n",
- ATA_EH_PMP_TRIES);
+ ata_port_err(ap, "failed to recover PMP after %d tries, giving up\n",
+ ATA_EH_PMP_TRIES);
sata_pmp_detach(pmp_dev);
ata_dev_disable(pmp_dev);
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 927f968e99d9..2a5412e7e9c1 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -37,6 +37,7 @@
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/spinlock.h>
+#include <linux/export.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
@@ -1108,8 +1109,7 @@ static int ata_scsi_dev_config(struct scsi_device *sdev,
/* configure draining */
buf = kmalloc(ATAPI_MAX_DRAIN, q->bounce_gfp | GFP_KERNEL);
if (!buf) {
- ata_dev_printk(dev, KERN_ERR,
- "drain buffer allocation failed\n");
+ ata_dev_err(dev, "drain buffer allocation failed\n");
return -ENOMEM;
}
@@ -1127,7 +1127,7 @@ static int ata_scsi_dev_config(struct scsi_device *sdev,
* IDENTIFY_PACKET is executed as ATA_PROT_PIO.
*/
if (sdev->sector_size > PAGE_SIZE)
- ata_dev_printk(dev, KERN_WARNING,
+ ata_dev_warn(dev,
"sector_size=%u > PAGE_SIZE, PIO may malfunction\n",
sdev->sector_size);
@@ -1216,25 +1216,19 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev)
}
/**
- * ata_scsi_change_queue_depth - SCSI callback for queue depth config
+ * __ata_change_queue_depth - helper for ata_scsi_change_queue_depth
+ * @ap: ATA port to which the device change the queue depth
* @sdev: SCSI device to configure queue depth for
* @queue_depth: new queue depth
* @reason: calling context
*
- * This is libata standard hostt->change_queue_depth callback.
- * SCSI will call into this callback when user tries to set queue
- * depth via sysfs.
+ * libsas and libata have different approaches for associating a sdev to
+ * its ata_port.
*
- * LOCKING:
- * SCSI layer (we don't care)
- *
- * RETURNS:
- * Newly configured queue depth.
*/
-int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth,
- int reason)
+int __ata_change_queue_depth(struct ata_port *ap, struct scsi_device *sdev,
+ int queue_depth, int reason)
{
- struct ata_port *ap = ata_shost_to_port(sdev->host);
struct ata_device *dev;
unsigned long flags;
@@ -1270,6 +1264,30 @@ int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth,
}
/**
+ * ata_scsi_change_queue_depth - SCSI callback for queue depth config
+ * @sdev: SCSI device to configure queue depth for
+ * @queue_depth: new queue depth
+ * @reason: calling context
+ *
+ * This is libata standard hostt->change_queue_depth callback.
+ * SCSI will call into this callback when user tries to set queue
+ * depth via sysfs.
+ *
+ * LOCKING:
+ * SCSI layer (we don't care)
+ *
+ * RETURNS:
+ * Newly configured queue depth.
+ */
+int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+ int reason)
+{
+ struct ata_port *ap = ata_shost_to_port(sdev->host);
+
+ return __ata_change_queue_depth(ap, sdev, queue_depth, reason);
+}
+
+/**
* ata_scsi_start_stop_xlat - Translate SCSI START STOP UNIT command
* @qc: Storage for translated ATA taskfile
*
@@ -1784,8 +1802,7 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
if (cmd->sc_data_direction == DMA_FROM_DEVICE ||
cmd->sc_data_direction == DMA_TO_DEVICE) {
if (unlikely(scsi_bufflen(cmd) < 1)) {
- ata_dev_printk(dev, KERN_WARNING,
- "WARNING: zero len r/w req\n");
+ ata_dev_warn(dev, "WARNING: zero len r/w req\n");
goto err_did;
}
@@ -2969,9 +2986,8 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
* with the cached multi_count of libata
*/
if (multi_count != dev->multi_count)
- ata_dev_printk(dev, KERN_WARNING,
- "invalid multi_count %u ignored\n",
- multi_count);
+ ata_dev_warn(dev, "invalid multi_count %u ignored\n",
+ multi_count);
}
/*
@@ -3466,9 +3482,8 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
goto repeat;
}
- ata_port_printk(ap, KERN_ERR, "WARNING: synchronous SCSI scan "
- "failed without making any progress,\n"
- " switching to async\n");
+ ata_port_err(ap,
+ "WARNING: synchronous SCSI scan failed without making any progress, switching to async\n");
}
queue_delayed_work(system_long_wq, &ap->hotplug_task,
@@ -3550,8 +3565,8 @@ static void ata_scsi_remove_dev(struct ata_device *dev)
mutex_unlock(&ap->scsi_host->scan_mutex);
if (sdev) {
- ata_dev_printk(dev, KERN_INFO, "detaching (SCSI %s)\n",
- dev_name(&sdev->sdev_gendev));
+ ata_dev_info(dev, "detaching (SCSI %s)\n",
+ dev_name(&sdev->sdev_gendev));
scsi_remove_device(sdev);
scsi_device_put(sdev);
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index b1b926c55a72..4cadfa28f940 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -35,6 +35,7 @@
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/pci.h>
+#include <linux/module.h>
#include <linux/libata.h>
#include <linux/highmem.h>
@@ -227,9 +228,9 @@ int ata_sff_busy_sleep(struct ata_port *ap,
}
if (status != 0xff && (status & ATA_BUSY))
- ata_port_printk(ap, KERN_WARNING,
- "port is slow to respond, please be patient "
- "(Status 0x%x)\n", status);
+ ata_port_warn(ap,
+ "port is slow to respond, please be patient (Status 0x%x)\n",
+ status);
timeout = ata_deadline(timer_start, tmout);
while (status != 0xff && (status & ATA_BUSY) &&
@@ -242,9 +243,9 @@ int ata_sff_busy_sleep(struct ata_port *ap,
return -ENODEV;
if (status & ATA_BUSY) {
- ata_port_printk(ap, KERN_ERR, "port failed to respond "
- "(%lu secs, Status 0x%x)\n",
- DIV_ROUND_UP(tmout, 1000), status);
+ ata_port_err(ap,
+ "port failed to respond (%lu secs, Status 0x%x)\n",
+ DIV_ROUND_UP(tmout, 1000), status);
return -EBUSY;
}
@@ -350,8 +351,8 @@ static void ata_dev_select(struct ata_port *ap, unsigned int device,
unsigned int wait, unsigned int can_sleep)
{
if (ata_msg_probe(ap))
- ata_port_printk(ap, KERN_INFO, "ata_dev_select: ENTER, "
- "device %u, wait %u\n", device, wait);
+ ata_port_info(ap, "ata_dev_select: ENTER, device %u, wait %u\n",
+ device, wait);
if (wait)
ata_wait_idle(ap);
@@ -569,7 +570,7 @@ unsigned int ata_sff_data_xfer(struct ata_device *dev, unsigned char *buf,
/* Transfer trailing byte, if any. */
if (unlikely(buflen & 0x01)) {
- unsigned char pad[2];
+ unsigned char pad[2] = { };
/* Point buf to the tail of buffer */
buf += buflen - 1;
@@ -628,7 +629,7 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf,
/* Transfer trailing bytes, if any */
if (unlikely(slop)) {
- unsigned char pad[4];
+ unsigned char pad[4] = { };
/* Point buf to the tail of buffer */
buf += buflen - slop;
@@ -678,7 +679,7 @@ unsigned int ata_sff_data_xfer_noirq(struct ata_device *dev, unsigned char *buf,
unsigned int consumed;
local_irq_save(flags);
- consumed = ata_sff_data_xfer(dev, buf, buflen, rw);
+ consumed = ata_sff_data_xfer32(dev, buf, buflen, rw);
local_irq_restore(flags);
return consumed;
@@ -1333,9 +1334,10 @@ void ata_sff_flush_pio_task(struct ata_port *ap)
cancel_delayed_work_sync(&ap->sff_pio_task);
ap->hsm_task_state = HSM_ST_IDLE;
+ ap->sff_pio_task_link = NULL;
if (ata_msg_ctl(ap))
- ata_port_printk(ap, KERN_DEBUG, "%s: EXIT\n", __func__);
+ ata_port_dbg(ap, "%s: EXIT\n", __func__);
}
static void ata_sff_pio_task(struct work_struct *work)
@@ -1513,7 +1515,7 @@ static unsigned int ata_sff_idle_irq(struct ata_port *ap)
ap->ops->sff_check_status(ap);
if (ap->ops->sff_irq_clear)
ap->ops->sff_irq_clear(ap);
- ata_port_printk(ap, KERN_WARNING, "irq trap\n");
+ ata_port_warn(ap, "irq trap\n");
return 1;
}
#endif
@@ -1711,7 +1713,7 @@ void ata_sff_lost_interrupt(struct ata_port *ap)
/* There was a command running, we are no longer busy and we have
no interrupt. */
- ata_port_printk(ap, KERN_WARNING, "lost interrupt (Status 0x%x)\n",
+ ata_port_warn(ap, "lost interrupt (Status 0x%x)\n",
status);
/* Run the host interrupt logic as if the interrupt had not been
lost */
@@ -1798,8 +1800,9 @@ int ata_sff_prereset(struct ata_link *link, unsigned long deadline)
if (!ata_link_offline(link)) {
rc = ata_sff_wait_ready(link, deadline);
if (rc && rc != -ENODEV) {
- ata_link_printk(link, KERN_WARNING, "device not ready "
- "(errno=%d), forcing hardreset\n", rc);
+ ata_link_warn(link,
+ "device not ready (errno=%d), forcing hardreset\n",
+ rc);
ehc->i.action |= ATA_EH_HARDRESET;
}
}
@@ -2056,7 +2059,7 @@ int ata_sff_softreset(struct ata_link *link, unsigned int *classes,
rc = ata_bus_softreset(ap, devmask, deadline);
/* if link is occupied, -ENODEV too is an error */
if (rc && (rc != -ENODEV || sata_scr_valid(link))) {
- ata_link_printk(link, KERN_ERR, "SRST failed (errno=%d)\n", rc);
+ ata_link_err(link, "SRST failed (errno=%d)\n", rc);
return rc;
}
@@ -2170,8 +2173,7 @@ void ata_sff_drain_fifo(struct ata_queued_cmd *qc)
/* Can become DEBUG later */
if (count)
- ata_port_printk(ap, KERN_DEBUG,
- "drained %d bytes to clear DRQ.\n", count);
+ ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
}
EXPORT_SYMBOL_GPL(ata_sff_drain_fifo);
@@ -2316,9 +2318,9 @@ int ata_pci_sff_init_host(struct ata_host *host)
rc = pcim_iomap_regions(pdev, 0x3 << base,
dev_driver_string(gdev));
if (rc) {
- dev_printk(KERN_WARNING, gdev,
- "failed to request/iomap BARs for port %d "
- "(errno=%d)\n", i, rc);
+ dev_warn(gdev,
+ "failed to request/iomap BARs for port %d (errno=%d)\n",
+ i, rc);
if (rc == -EBUSY)
pcim_pin_device(pdev);
ap->ops = &ata_dummy_port_ops;
@@ -2340,7 +2342,7 @@ int ata_pci_sff_init_host(struct ata_host *host)
}
if (!mask) {
- dev_printk(KERN_ERR, gdev, "no available native port\n");
+ dev_err(gdev, "no available native port\n");
return -ENODEV;
}
@@ -2375,8 +2377,7 @@ int ata_pci_sff_prepare_host(struct pci_dev *pdev,
host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
if (!host) {
- dev_printk(KERN_ERR, &pdev->dev,
- "failed to allocate ATA host\n");
+ dev_err(&pdev->dev, "failed to allocate ATA host\n");
rc = -ENOMEM;
goto err_out;
}
@@ -2507,31 +2508,10 @@ static const struct ata_port_info *ata_sff_find_valid_pi(
return NULL;
}
-/**
- * ata_pci_sff_init_one - Initialize/register PIO-only PCI IDE controller
- * @pdev: Controller to be initialized
- * @ppi: array of port_info, must be enough for two ports
- * @sht: scsi_host_template to use when registering the host
- * @host_priv: host private_data
- * @hflag: host flags
- *
- * This is a helper function which can be called from a driver's
- * xxx_init_one() probe function if the hardware uses traditional
- * IDE taskfile registers and is PIO only.
- *
- * ASSUMPTION:
- * Nobody makes a single channel controller that appears solely as
- * the secondary legacy port on PCI.
- *
- * LOCKING:
- * Inherited from PCI layer (may sleep).
- *
- * RETURNS:
- * Zero on success, negative on errno-based value on error.
- */
-int ata_pci_sff_init_one(struct pci_dev *pdev,
- const struct ata_port_info * const *ppi,
- struct scsi_host_template *sht, void *host_priv, int hflag)
+static int ata_pci_init_one(struct pci_dev *pdev,
+ const struct ata_port_info * const *ppi,
+ struct scsi_host_template *sht, void *host_priv,
+ int hflags, bool bmdma)
{
struct device *dev = &pdev->dev;
const struct ata_port_info *pi;
@@ -2542,8 +2522,7 @@ int ata_pci_sff_init_one(struct pci_dev *pdev,
pi = ata_sff_find_valid_pi(ppi);
if (!pi) {
- dev_printk(KERN_ERR, &pdev->dev,
- "no valid port_info specified\n");
+ dev_err(&pdev->dev, "no valid port_info specified\n");
return -EINVAL;
}
@@ -2554,14 +2533,26 @@ int ata_pci_sff_init_one(struct pci_dev *pdev,
if (rc)
goto out;
- /* prepare and activate SFF host */
- rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+#ifdef CONFIG_ATA_BMDMA
+ if (bmdma)
+ /* prepare and activate BMDMA host */
+ rc = ata_pci_bmdma_prepare_host(pdev, ppi, &host);
+ else
+#endif
+ /* prepare and activate SFF host */
+ rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
if (rc)
goto out;
host->private_data = host_priv;
- host->flags |= hflag;
+ host->flags |= hflags;
- rc = ata_pci_sff_activate_host(host, ata_sff_interrupt, sht);
+#ifdef CONFIG_ATA_BMDMA
+ if (bmdma) {
+ pci_set_master(pdev);
+ rc = ata_pci_sff_activate_host(host, ata_bmdma_interrupt, sht);
+ } else
+#endif
+ rc = ata_pci_sff_activate_host(host, ata_sff_interrupt, sht);
out:
if (rc == 0)
devres_remove_group(&pdev->dev, NULL);
@@ -2570,6 +2561,35 @@ out:
return rc;
}
+
+/**
+ * ata_pci_sff_init_one - Initialize/register PIO-only PCI IDE controller
+ * @pdev: Controller to be initialized
+ * @ppi: array of port_info, must be enough for two ports
+ * @sht: scsi_host_template to use when registering the host
+ * @host_priv: host private_data
+ * @hflag: host flags
+ *
+ * This is a helper function which can be called from a driver's
+ * xxx_init_one() probe function if the hardware uses traditional
+ * IDE taskfile registers and is PIO only.
+ *
+ * ASSUMPTION:
+ * Nobody makes a single channel controller that appears solely as
+ * the secondary legacy port on PCI.
+ *
+ * LOCKING:
+ * Inherited from PCI layer (may sleep).
+ *
+ * RETURNS:
+ * Zero on success, negative on errno-based value on error.
+ */
+int ata_pci_sff_init_one(struct pci_dev *pdev,
+ const struct ata_port_info * const *ppi,
+ struct scsi_host_template *sht, void *host_priv, int hflag)
+{
+ return ata_pci_init_one(pdev, ppi, sht, host_priv, hflag, 0);
+}
EXPORT_SYMBOL_GPL(ata_pci_sff_init_one);
#endif /* CONFIG_PCI */
@@ -3164,8 +3184,7 @@ static void ata_bmdma_nodma(struct ata_host *host, const char *reason)
{
int i;
- dev_printk(KERN_ERR, host->dev, "BMDMA: %s, falling back to PIO\n",
- reason);
+ dev_err(host->dev, "BMDMA: %s, falling back to PIO\n", reason);
for (i = 0; i < 2; i++) {
host->ports[i]->mwdma_mask = 0;
@@ -3288,43 +3307,7 @@ int ata_pci_bmdma_init_one(struct pci_dev *pdev,
struct scsi_host_template *sht, void *host_priv,
int hflags)
{
- struct device *dev = &pdev->dev;
- const struct ata_port_info *pi;
- struct ata_host *host = NULL;
- int rc;
-
- DPRINTK("ENTER\n");
-
- pi = ata_sff_find_valid_pi(ppi);
- if (!pi) {
- dev_printk(KERN_ERR, &pdev->dev,
- "no valid port_info specified\n");
- return -EINVAL;
- }
-
- if (!devres_open_group(dev, NULL, GFP_KERNEL))
- return -ENOMEM;
-
- rc = pcim_enable_device(pdev);
- if (rc)
- goto out;
-
- /* prepare and activate BMDMA host */
- rc = ata_pci_bmdma_prepare_host(pdev, ppi, &host);
- if (rc)
- goto out;
- host->private_data = host_priv;
- host->flags |= hflags;
-
- pci_set_master(pdev);
- rc = ata_pci_sff_activate_host(host, ata_bmdma_interrupt, sht);
- out:
- if (rc == 0)
- devres_remove_group(&pdev->dev, NULL);
- else
- devres_release_group(&pdev->dev, NULL);
-
- return rc;
+ return ata_pci_init_one(pdev, ppi, sht, host_priv, hflags, 1);
}
EXPORT_SYMBOL_GPL(ata_pci_bmdma_init_one);
diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c
index 91949d997555..54145edf50e8 100644
--- a/drivers/ata/pata_acpi.c
+++ b/drivers/ata/pata_acpi.c
@@ -195,8 +195,6 @@ static int pacpi_port_start(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
struct pata_acpi *acpi;
- int ret;
-
if (ap->acpi_handle == NULL)
return -ENODEV;
@@ -205,11 +203,7 @@ static int pacpi_port_start(struct ata_port *ap)
return -ENOMEM;
acpi->mask[0] = pacpi_discover_modes(ap, &ap->link.device[0]);
acpi->mask[1] = pacpi_discover_modes(ap, &ap->link.device[1]);
- ret = ata_bmdma_port_start(ap);
- if (ret < 0)
- return ret;
-
- return ret;
+ return ata_bmdma_port_start(ap);
}
static struct scsi_host_template pacpi_sht = {
diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c
index 794ec6e3275d..61da0694aecd 100644
--- a/drivers/ata/pata_ali.c
+++ b/drivers/ata/pata_ali.c
@@ -56,7 +56,7 @@ static const struct dmi_system_id cable_dmi_table[] = {
},
},
{
- .ident = "Toshiba Satelite S1800-814",
+ .ident = "Toshiba Satellite S1800-814",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "S1800-814"),
@@ -287,10 +287,10 @@ static void ali_warn_atapi_dma(struct ata_device *adev)
int print_info = ehc->i.flags & ATA_EHI_PRINTINFO;
if (print_info && adev->class == ATA_DEV_ATAPI && !ali_atapi_dma) {
- ata_dev_printk(adev, KERN_WARNING,
- "WARNING: ATAPI DMA disabled for reliability issues. It can be enabled\n");
- ata_dev_printk(adev, KERN_WARNING,
- "WARNING: via pata_ali.atapi_dma modparam or corresponding sysfs node.\n");
+ ata_dev_warn(adev,
+ "WARNING: ATAPI DMA disabled for reliability issues. It can be enabled\n");
+ ata_dev_warn(adev,
+ "WARNING: via pata_ali.atapi_dma modparam or corresponding sysfs node.\n");
}
}
diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c
index b0975a5ad8c4..dc6b5dae0463 100644
--- a/drivers/ata/pata_amd.c
+++ b/drivers/ata/pata_amd.c
@@ -60,7 +60,7 @@ static void timing_setup(struct ata_port *ap, struct ata_device *adev, int offse
UT = T / 2;
if (ata_timing_compute(adev, speed, &at, T, UT) < 0) {
- dev_printk(KERN_ERR, &pdev->dev, "unknown mode %d.\n", speed);
+ dev_err(&pdev->dev, "unknown mode %d\n", speed);
return;
}
@@ -311,7 +311,7 @@ static unsigned long nv_mode_filter(struct ata_device *dev,
cable detection result */
limit |= ata_pack_xfermask(ATA_PIO4, ATA_MWDMA2, ATA_UDMA2);
- ata_port_printk(ap, KERN_DEBUG, "nv_mode_filter: 0x%lx&0x%lx->0x%lx, "
+ ata_port_dbg(ap, "nv_mode_filter: 0x%lx&0x%lx->0x%lx, "
"BIOS=0x%lx (0x%x) ACPI=0x%lx%s\n",
xfer_mask, limit, xfer_mask & limit, bios_limit,
saved_udma, acpi_limit, acpi_str);
@@ -530,14 +530,12 @@ static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
}
};
const struct ata_port_info *ppi[] = { NULL, NULL };
- static int printed_version;
int type = id->driver_data;
void *hpriv = NULL;
u8 fifo;
int rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c
index 719bb73a73e0..e8574bba3ee4 100644
--- a/drivers/ata/pata_arasan_cf.c
+++ b/drivers/ata/pata_arasan_cf.c
@@ -922,8 +922,7 @@ static int __devexit arasan_cf_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int arasan_cf_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct ata_host *host = dev_get_drvdata(dev);
struct arasan_cf_dev *acdev = host->ports[0]->private_data;
if (acdev->dma_chan) {
@@ -937,8 +936,7 @@ static int arasan_cf_suspend(struct device *dev)
static int arasan_cf_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct ata_host *host = dev_get_drvdata(dev);
struct arasan_cf_dev *acdev = host->ports[0]->private_data;
cf_init(acdev);
diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c
index 2215632e4b31..4b8b22efc00b 100644
--- a/drivers/ata/pata_artop.c
+++ b/drivers/ata/pata_artop.c
@@ -2,7 +2,7 @@
* pata_artop.c - ARTOP ATA controller driver
*
* (C) 2006 Red Hat
- * (C) 2007 Bartlomiej Zolnierkiewicz
+ * (C) 2007,2011 Bartlomiej Zolnierkiewicz
*
* Based in part on drivers/ide/pci/aec62xx.c
* Copyright (C) 1999-2002 Andre Hedrick <andre@linux-ide.org>
@@ -28,7 +28,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_artop"
-#define DRV_VERSION "0.4.5"
+#define DRV_VERSION "0.4.6"
/*
* The ARTOP has 33 Mhz and "over clocked" timing tables. Until we
@@ -39,31 +39,15 @@
static int clock = 0;
-static int artop6210_pre_reset(struct ata_link *link, unsigned long deadline)
-{
- struct ata_port *ap = link->ap;
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- const struct pci_bits artop_enable_bits[] = {
- { 0x4AU, 1U, 0x02UL, 0x02UL }, /* port 0 */
- { 0x4AU, 1U, 0x04UL, 0x04UL }, /* port 1 */
- };
-
- if (!pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no]))
- return -ENOENT;
-
- return ata_sff_prereset(link, deadline);
-}
-
/**
- * artop6260_pre_reset - check for 40/80 pin
+ * artop62x0_pre_reset - probe begin
* @link: link
* @deadline: deadline jiffies for the operation
*
- * The ARTOP hardware reports the cable detect bits in register 0x49.
* Nothing complicated needed here.
*/
-static int artop6260_pre_reset(struct ata_link *link, unsigned long deadline)
+static int artop62x0_pre_reset(struct ata_link *link, unsigned long deadline)
{
static const struct pci_bits artop_enable_bits[] = {
{ 0x4AU, 1U, 0x02UL, 0x02UL }, /* port 0 */
@@ -73,7 +57,7 @@ static int artop6260_pre_reset(struct ata_link *link, unsigned long deadline)
struct ata_port *ap = link->ap;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- /* Odd numbered device ids are the units with enable bits (the -R cards) */
+ /* Odd numbered device ids are the units with enable bits. */
if ((pdev->device & 1) &&
!pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no]))
return -ENOENT;
@@ -317,7 +301,7 @@ static struct ata_port_operations artop6210_ops = {
.cable_detect = ata_cable_40wire,
.set_piomode = artop6210_set_piomode,
.set_dmamode = artop6210_set_dmamode,
- .prereset = artop6210_pre_reset,
+ .prereset = artop62x0_pre_reset,
.qc_defer = artop6210_qc_defer,
};
@@ -326,9 +310,36 @@ static struct ata_port_operations artop6260_ops = {
.cable_detect = artop6260_cable_detect,
.set_piomode = artop6260_set_piomode,
.set_dmamode = artop6260_set_dmamode,
- .prereset = artop6260_pre_reset,
+ .prereset = artop62x0_pre_reset,
};
+static void atp8xx_fixup(struct pci_dev *pdev)
+{
+ if (pdev->device == 0x0005)
+ /* BIOS may have left us in UDMA, clear it before libata probe */
+ pci_write_config_byte(pdev, 0x54, 0);
+ else if (pdev->device == 0x0008 || pdev->device == 0x0009) {
+ u8 reg;
+
+ /* Mac systems come up with some registers not set as we
+ will need them */
+
+ /* Clear reset & test bits */
+ pci_read_config_byte(pdev, 0x49, &reg);
+ pci_write_config_byte(pdev, 0x49, reg & ~0x30);
+
+ /* PCI latency must be > 0x80 for burst mode, tweak it
+ * if required.
+ */
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &reg);
+ if (reg <= 0x80)
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x90);
+
+ /* Enable IRQ output and burst mode */
+ pci_read_config_byte(pdev, 0x4a, &reg);
+ pci_write_config_byte(pdev, 0x4a, (reg & ~0x01) | 0x80);
+ }
+}
/**
* artop_init_one - Register ARTOP ATA PCI device with kernel services
@@ -346,7 +357,6 @@ static struct ata_port_operations artop6260_ops = {
static int artop_init_one (struct pci_dev *pdev, const struct pci_device_id *id)
{
- static int printed_version;
static const struct ata_port_info info_6210 = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
@@ -378,50 +388,28 @@ static int artop_init_one (struct pci_dev *pdev, const struct pci_device_id *id)
const struct ata_port_info *ppi[] = { NULL, NULL };
int rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
return rc;
- if (id->driver_data == 0) { /* 6210 variant */
+ if (id->driver_data == 0) /* 6210 variant */
ppi[0] = &info_6210;
- /* BIOS may have left us in UDMA, clear it before libata probe */
- pci_write_config_byte(pdev, 0x54, 0);
- }
else if (id->driver_data == 1) /* 6260 */
ppi[0] = &info_626x;
else if (id->driver_data == 2) { /* 6280 or 6280 + fast */
unsigned long io = pci_resource_start(pdev, 4);
- u8 reg;
ppi[0] = &info_628x;
if (inb(io) & 0x10)
ppi[0] = &info_628x_fast;
- /* Mac systems come up with some registers not set as we
- will need them */
-
- /* Clear reset & test bits */
- pci_read_config_byte(pdev, 0x49, &reg);
- pci_write_config_byte(pdev, 0x49, reg & ~ 0x30);
-
- /* PCI latency must be > 0x80 for burst mode, tweak it
- * if required.
- */
- pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &reg);
- if (reg <= 0x80)
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x90);
-
- /* Enable IRQ output and burst mode */
- pci_read_config_byte(pdev, 0x4a, &reg);
- pci_write_config_byte(pdev, 0x4a, (reg & ~0x01) | 0x80);
-
}
BUG_ON(ppi[0] == NULL);
+ atp8xx_fixup(pdev);
+
return ata_pci_bmdma_init_one(pdev, ppi, &artop_sht, NULL, 0);
}
@@ -435,11 +423,32 @@ static const struct pci_device_id artop_pci_tbl[] = {
{ } /* terminate list */
};
+#ifdef CONFIG_PM
+static int atp8xx_reinit_one(struct pci_dev *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ int rc;
+
+ rc = ata_pci_device_do_resume(pdev);
+ if (rc)
+ return rc;
+
+ atp8xx_fixup(pdev);
+
+ ata_host_resume(host);
+ return 0;
+}
+#endif
+
static struct pci_driver artop_pci_driver = {
.name = DRV_NAME,
.id_table = artop_pci_tbl,
.probe = artop_init_one,
.remove = ata_pci_remove_one,
+#ifdef CONFIG_PM
+ .suspend = ata_pci_device_suspend,
+ .resume = atp8xx_reinit_one,
+#endif
};
static int __init artop_init(void)
@@ -455,9 +464,8 @@ static void __exit artop_exit(void)
module_init(artop_init);
module_exit(artop_exit);
-MODULE_AUTHOR("Alan Cox");
+MODULE_AUTHOR("Alan Cox, Bartlomiej Zolnierkiewicz");
MODULE_DESCRIPTION("SCSI low-level driver for ARTOP PATA");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, artop_pci_tbl);
MODULE_VERSION(DRV_VERSION);
-
diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c
index 960c72571395..a76f24a8e5db 100644
--- a/drivers/ata/pata_at91.c
+++ b/drivers/ata/pata_at91.c
@@ -30,7 +30,7 @@
#include <mach/at91sam9_smc.h>
#include <mach/board.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#define DRV_NAME "pata_at91"
#define DRV_VERSION "0.3"
@@ -414,10 +414,13 @@ static int __devinit pata_at91_probe(struct platform_device *pdev)
host->private_data = info;
- return ata_host_activate(host, irq ? gpio_to_irq(irq) : 0,
+ ret = ata_host_activate(host, irq ? gpio_to_irq(irq) : 0,
irq ? ata_sff_interrupt : NULL,
irq_flags, &pata_at91_sht);
+ if (!ret)
+ return 0;
+
err_put:
clk_put(info->mck);
return ret;
diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c
index 43755616dc5a..be1aa1486d39 100644
--- a/drivers/ata/pata_atiixp.c
+++ b/drivers/ata/pata_atiixp.c
@@ -49,6 +49,31 @@ static int atiixp_cable_detect(struct ata_port *ap)
static DEFINE_SPINLOCK(atiixp_lock);
/**
+ * atiixp_prereset - perform reset handling
+ * @link: ATA link
+ * @deadline: deadline jiffies for the operation
+ *
+ * Reset sequence checking enable bits to see which ports are
+ * active.
+ */
+
+static int atiixp_prereset(struct ata_link *link, unsigned long deadline)
+{
+ static const struct pci_bits atiixp_enable_bits[] = {
+ { 0x48, 1, 0x01, 0x00 },
+ { 0x48, 1, 0x08, 0x00 }
+ };
+
+ struct ata_port *ap = link->ap;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+
+ if (!pci_test_config_bits(pdev, &atiixp_enable_bits[ap->port_no]))
+ return -ENOENT;
+
+ return ata_sff_prereset(link, deadline);
+}
+
+/**
* atiixp_set_pio_timing - set initial PIO mode data
* @ap: ATA interface
* @adev: ATA device
@@ -221,6 +246,7 @@ static struct ata_port_operations atiixp_port_ops = {
.bmdma_start = atiixp_bmdma_start,
.bmdma_stop = atiixp_bmdma_stop,
+ .prereset = atiixp_prereset,
.cable_detect = atiixp_cable_detect,
.set_piomode = atiixp_set_piomode,
.set_dmamode = atiixp_set_dmamode,
@@ -235,16 +261,7 @@ static int atiixp_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
.udma_mask = ATA_UDMA5,
.port_ops = &atiixp_port_ops
};
- static const struct pci_bits atiixp_enable_bits[] = {
- { 0x48, 1, 0x01, 0x00 },
- { 0x48, 1, 0x08, 0x00 }
- };
const struct ata_port_info *ppi[] = { &info, &info };
- int i;
-
- for (i = 0; i < 2; i++)
- if (!pci_test_config_bits(pdev, &atiixp_enable_bits[i]))
- ppi[i] = &ata_dummy_port_info;
return ata_pci_bmdma_init_one(pdev, ppi, &atiixp_sht, NULL,
ATA_HOST_PARALLEL_SCAN);
diff --git a/drivers/ata/pata_atp867x.c b/drivers/ata/pata_atp867x.c
index 95295935dd95..3cfabb262af2 100644
--- a/drivers/ata/pata_atp867x.c
+++ b/drivers/ata/pata_atp867x.c
@@ -470,7 +470,7 @@ static int atp867x_ata_pci_sff_init_host(struct ata_host *host)
}
if (!mask) {
- dev_printk(KERN_ERR, gdev, "no available native port\n");
+ dev_err(gdev, "no available native port\n");
return -ENODEV;
}
@@ -487,7 +487,6 @@ static int atp867x_ata_pci_sff_init_host(struct ata_host *host)
static int atp867x_init_one(struct pci_dev *pdev,
const struct pci_device_id *id)
{
- static int printed_version;
static const struct ata_port_info info_867x = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
@@ -499,8 +498,7 @@ static int atp867x_init_one(struct pci_dev *pdev,
const struct ata_port_info *ppi[] = { &info_867x, NULL };
int rc;
- if (!printed_version++)
- dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
@@ -511,15 +509,14 @@ static int atp867x_init_one(struct pci_dev *pdev,
host = ata_host_alloc_pinfo(&pdev->dev, ppi, ATP867X_NUM_PORTS);
if (!host) {
- dev_printk(KERN_ERR, &pdev->dev,
- "failed to allocate ATA host\n");
+ dev_err(&pdev->dev, "failed to allocate ATA host\n");
rc = -ENOMEM;
goto err_out;
}
rc = atp867x_ata_pci_sff_init_host(host);
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev, "failed to init host\n");
+ dev_err(&pdev->dev, "failed to init host\n");
goto err_out;
}
@@ -528,7 +525,7 @@ static int atp867x_init_one(struct pci_dev *pdev,
rc = ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
IRQF_SHARED, &atp867x_sht);
if (rc)
- dev_printk(KERN_ERR, &pdev->dev, "failed to activate host\n");
+ dev_err(&pdev->dev, "failed to activate host\n");
err_out:
return rc;
diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c
index ea64967000ff..bd987bb082eb 100644
--- a/drivers/ata/pata_bf54x.c
+++ b/drivers/ata/pata_bf54x.c
@@ -1129,7 +1129,7 @@ static int bfin_softreset(struct ata_link *link, unsigned int *classes,
/* issue bus reset */
err_mask = bfin_bus_softreset(ap, devmask);
if (err_mask) {
- ata_port_printk(ap, KERN_ERR, "SRST failed (err_mask=0x%x)\n",
+ ata_port_err(ap, "SRST failed (err_mask=0x%x)\n",
err_mask);
return -EIO;
}
@@ -1382,7 +1382,7 @@ idle_irq:
#ifdef ATA_IRQ_TRAP
if ((ap->stats.idle_irq % 1000) == 0) {
ap->ops->irq_ack(ap, 0); /* debug trap */
- ata_port_printk(ap, KERN_WARNING, "irq trap\n");
+ ata_port_warn(ap, "irq trap\n");
return 1;
}
#endif
diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c
index 7bafc16cf5e0..e1fb39a74ce1 100644
--- a/drivers/ata/pata_cmd64x.c
+++ b/drivers/ata/pata_cmd64x.c
@@ -82,7 +82,7 @@ static int cmd648_cable_detect(struct ata_port *ap)
}
/**
- * cmd64x_set_piomode - set PIO and MWDMA timing
+ * cmd64x_set_timing - set PIO and MWDMA timing
* @ap: ATA interface
* @adev: ATA device
* @mode: mode
@@ -288,6 +288,22 @@ static struct ata_port_operations cmd648_port_ops = {
.cable_detect = cmd648_cable_detect,
};
+static void cmd64x_fixup(struct pci_dev *pdev)
+{
+ u8 mrdmode;
+
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
+ pci_read_config_byte(pdev, MRDMODE, &mrdmode);
+ mrdmode &= ~0x30; /* IRQ set up */
+ mrdmode |= 0x02; /* Memory read line enable */
+ pci_write_config_byte(pdev, MRDMODE, mrdmode);
+
+ /* PPC specific fixup copied from old driver */
+#ifdef CONFIG_PPC
+ pci_write_config_byte(pdev, UDIDETCR0, 0xF0);
+#endif
+}
+
static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
static const struct ata_port_info cmd_info[6] = {
@@ -336,7 +352,7 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
&cmd_info[id->driver_data],
NULL
};
- u8 mrdmode, reg;
+ u8 reg;
int rc;
struct pci_dev *bridge = pdev->bus->self;
/* mobility split bridges don't report enabled ports correctly */
@@ -368,11 +384,7 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
cntrl_ch0_ok = 0;
}
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
- pci_read_config_byte(pdev, MRDMODE, &mrdmode);
- mrdmode &= ~ 0x30; /* IRQ set up */
- mrdmode |= 0x02; /* Memory read line enable */
- pci_write_config_byte(pdev, MRDMODE, mrdmode);
+ cmd64x_fixup(pdev);
/* check for enabled ports */
pci_read_config_byte(pdev, CNTRL, &reg);
@@ -388,13 +400,6 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
ppi[1] = &ata_dummy_port_info;
}
- /* Force PIO 0 here.. */
-
- /* PPC specific fixup copied from old driver */
-#ifdef CONFIG_PPC
- pci_write_config_byte(pdev, UDIDETCR0, 0xF0);
-#endif
-
return ata_pci_bmdma_init_one(pdev, ppi, &cmd64x_sht, NULL, 0);
}
@@ -402,21 +407,14 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
static int cmd64x_reinit_one(struct pci_dev *pdev)
{
struct ata_host *host = dev_get_drvdata(&pdev->dev);
- u8 mrdmode;
int rc;
rc = ata_pci_device_do_resume(pdev);
if (rc)
return rc;
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
- pci_read_config_byte(pdev, MRDMODE, &mrdmode);
- mrdmode &= ~ 0x30; /* IRQ set up */
- mrdmode |= 0x02; /* Memory read line enable */
- pci_write_config_byte(pdev, MRDMODE, mrdmode);
-#ifdef CONFIG_PPC
- pci_write_config_byte(pdev, UDIDETCR0, 0xF0);
-#endif
+ cmd64x_fixup(pdev);
+
ata_host_resume(host);
return 0;
}
diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c
index e3254fcff0f1..9ddcddc66a20 100644
--- a/drivers/ata/pata_cs5520.c
+++ b/drivers/ata/pata_cs5520.c
@@ -149,8 +149,7 @@ static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_devi
ppi[1] = &pi;
if ((pcicfg & 0x40) == 0) {
- dev_printk(KERN_WARNING, &pdev->dev,
- "DMA mode disabled. Enabling.\n");
+ dev_warn(&pdev->dev, "DMA mode disabled. Enabling.\n");
pci_write_config_byte(pdev, 0x60, pcicfg | 0x40);
}
diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c
index 03a93186aa19..a0b4640125ae 100644
--- a/drivers/ata/pata_cs5535.c
+++ b/drivers/ata/pata_cs5535.c
@@ -38,7 +38,7 @@
#include <linux/libata.h>
#include <asm/msr.h>
-#define DRV_NAME "cs5535"
+#define DRV_NAME "pata_cs5535"
#define DRV_VERSION "0.2.12"
/*
@@ -67,8 +67,6 @@
#define CS5535_CABLE_DETECT 0x48
-#define CS5535_BAD_PIO(timings) ( (timings&~0x80000000UL)==0x00009172 )
-
/**
* cs5535_cable_detect - detect cable type
* @ap: Port to detect on
@@ -188,16 +186,6 @@ static int cs5535_init_one(struct pci_dev *dev, const struct pci_device_id *id)
};
const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info };
- u32 timings, dummy;
-
- /* Check the BIOS set the initial timing clock. If not set the
- timings for PIO0 */
- rdmsr(ATAC_CH0D0_PIO, timings, dummy);
- if (CS5535_BAD_PIO(timings))
- wrmsr(ATAC_CH0D0_PIO, 0xF7F4F7F4UL, 0);
- rdmsr(ATAC_CH0D1_PIO, timings, dummy);
- if (CS5535_BAD_PIO(timings))
- wrmsr(ATAC_CH0D1_PIO, 0xF7F4F7F4UL, 0);
return ata_pci_bmdma_init_one(dev, ppi, &cs5535_sht, NULL, 0);
}
@@ -230,7 +218,7 @@ static void __exit cs5535_exit(void)
}
MODULE_AUTHOR("Alan Cox, Jens Altmann, Wolfgan Zuleger, Alexander Kiausch");
-MODULE_DESCRIPTION("low-level driver for the NS/AMD 5530");
+MODULE_DESCRIPTION("low-level driver for the NS/AMD 5535");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, cs5535);
MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c
index a08834758ea2..f0243ed206f7 100644
--- a/drivers/ata/pata_efar.c
+++ b/drivers/ata/pata_efar.c
@@ -73,7 +73,7 @@ static DEFINE_SPINLOCK(efar_lock);
/**
* efar_set_piomode - Initialize host controller PATA PIO timings
* @ap: Port whose timings we are configuring
- * @adev: um
+ * @adev: Device to program
*
* Set PIO mode for device, in host controller PCI config space.
*
@@ -85,9 +85,9 @@ static void efar_set_piomode (struct ata_port *ap, struct ata_device *adev)
{
unsigned int pio = adev->pio_mode - XFER_PIO_0;
struct pci_dev *dev = to_pci_dev(ap->host->dev);
- unsigned int idetm_port= ap->port_no ? 0x42 : 0x40;
+ unsigned int master_port = ap->port_no ? 0x42 : 0x40;
unsigned long flags;
- u16 idetm_data;
+ u16 master_data;
u8 udma_enable;
int control = 0;
@@ -113,20 +113,20 @@ static void efar_set_piomode (struct ata_port *ap, struct ata_device *adev)
spin_lock_irqsave(&efar_lock, flags);
- pci_read_config_word(dev, idetm_port, &idetm_data);
+ pci_read_config_word(dev, master_port, &master_data);
/* Set PPE, IE, and TIME as appropriate */
if (adev->devno == 0) {
- idetm_data &= 0xCCF0;
- idetm_data |= control;
- idetm_data |= (timings[pio][0] << 12) |
+ master_data &= 0xCCF0;
+ master_data |= control;
+ master_data |= (timings[pio][0] << 12) |
(timings[pio][1] << 8);
} else {
int shift = 4 * ap->port_no;
u8 slave_data;
- idetm_data &= 0xFF0F;
- idetm_data |= (control << 4);
+ master_data &= 0xFF0F;
+ master_data |= (control << 4);
/* Slave timing in separate register */
pci_read_config_byte(dev, 0x44, &slave_data);
@@ -135,8 +135,8 @@ static void efar_set_piomode (struct ata_port *ap, struct ata_device *adev)
pci_write_config_byte(dev, 0x44, slave_data);
}
- idetm_data |= 0x4000; /* Ensure SITRE is set */
- pci_write_config_word(dev, idetm_port, idetm_data);
+ master_data |= 0x4000; /* Ensure SITRE is set */
+ pci_write_config_word(dev, master_port, master_data);
pci_read_config_byte(dev, 0x48, &udma_enable);
udma_enable &= ~(1 << (2 * ap->port_no + adev->devno));
@@ -263,7 +263,6 @@ static struct ata_port_operations efar_ops = {
static int efar_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
static const struct ata_port_info info = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
@@ -273,9 +272,7 @@ static int efar_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
};
const struct ata_port_info *ppi[] = { &info, &info };
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
return ata_pci_bmdma_init_one(pdev, ppi, &efar_sht, NULL,
ATA_HOST_PARALLEL_SCAN);
diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c
index 6c77d68dbd05..42cffd38910d 100644
--- a/drivers/ata/pata_hpt366.c
+++ b/drivers/ata/pata_hpt366.c
@@ -111,6 +111,28 @@ static const struct hpt_clock hpt366_25[] = {
{ 0, 0x01208585 }
};
+/**
+ * hpt36x_find_mode - find the hpt36x timing
+ * @ap: ATA port
+ * @speed: transfer mode
+ *
+ * Return the 32bit register programming information for this channel
+ * that matches the speed provided.
+ */
+
+static u32 hpt36x_find_mode(struct ata_port *ap, int speed)
+{
+ struct hpt_clock *clocks = ap->host->private_data;
+
+ while (clocks->xfer_mode) {
+ if (clocks->xfer_mode == speed)
+ return clocks->timing;
+ clocks++;
+ }
+ BUG();
+ return 0xffffffffU; /* silence compiler warning */
+}
+
static const char * const bad_ata33[] = {
"Maxtor 92720U8", "Maxtor 92040U6", "Maxtor 91360U4", "Maxtor 91020U3",
"Maxtor 90845U3", "Maxtor 90650U2",
@@ -210,10 +232,9 @@ static int hpt36x_cable_detect(struct ata_port *ap)
static void hpt366_set_mode(struct ata_port *ap, struct ata_device *adev,
u8 mode)
{
- struct hpt_clock *clocks = ap->host->private_data;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u32 addr = 0x40 + 4 * adev->devno;
- u32 mask, reg;
+ u32 mask, reg, t;
/* determine timing mask and find matching clock entry */
if (mode < XFER_MW_DMA_0)
@@ -223,13 +244,7 @@ static void hpt366_set_mode(struct ata_port *ap, struct ata_device *adev,
else
mask = 0x30070000;
- while (clocks->xfer_mode) {
- if (clocks->xfer_mode == mode)
- break;
- clocks++;
- }
- if (!clocks->xfer_mode)
- BUG();
+ t = hpt36x_find_mode(ap, mode);
/*
* Combine new mode bits with old config bits and disable
@@ -237,7 +252,7 @@ static void hpt366_set_mode(struct ata_port *ap, struct ata_device *adev,
* problems handling I/O errors later.
*/
pci_read_config_dword(pdev, addr, &reg);
- reg = ((reg & ~mask) | (clocks->timing & mask)) & ~0xc0000000;
+ reg = ((reg & ~mask) | (t & mask)) & ~0xc0000000;
pci_write_config_dword(pdev, addr, reg);
}
diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c
index 24d7df81546b..b3042dab08bb 100644
--- a/drivers/ata/pata_hpt3x3.c
+++ b/drivers/ata/pata_hpt3x3.c
@@ -185,7 +185,6 @@ static void hpt3x3_init_chipset(struct pci_dev *dev)
static int hpt3x3_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
- static int printed_version;
static const struct ata_port_info info = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
@@ -206,8 +205,7 @@ static int hpt3x3_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
hpt3x3_init_chipset(pdev);
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
if (!host)
diff --git a/drivers/ata/pata_icside.c b/drivers/ata/pata_icside.c
index 9f2889fe43b2..52e7e7b8c74f 100644
--- a/drivers/ata/pata_icside.c
+++ b/drivers/ata/pata_icside.c
@@ -210,8 +210,8 @@ static void pata_icside_set_dmamode(struct ata_port *ap, struct ata_device *adev
else
iomd_type = 'A', cycle = 562;
- ata_dev_printk(adev, KERN_INFO, "timings: act %dns rec %dns cyc %dns (%c)\n",
- t.active, t.recover, t.cycle, iomd_type);
+ ata_dev_info(adev, "timings: act %dns rec %dns cyc %dns (%c)\n",
+ t.active, t.recover, t.cycle, iomd_type);
state->port[ap->port_no].speed[adev->devno] = cycle;
}
diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c
new file mode 100644
index 000000000000..ca9d9caedfa3
--- /dev/null
+++ b/drivers/ata/pata_imx.c
@@ -0,0 +1,253 @@
+/*
+ * Freescale iMX PATA driver
+ *
+ * Copyright (C) 2011 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ * Based on pata_platform - Copyright (C) 2006 - 2007 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * TODO:
+ * - dmaengine support
+ * - check if timing stuff needed
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_host.h>
+#include <linux/ata.h>
+#include <linux/libata.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#define DRV_NAME "pata_imx"
+
+#define PATA_IMX_ATA_CONTROL 0x24
+#define PATA_IMX_ATA_CTRL_FIFO_RST_B (1<<7)
+#define PATA_IMX_ATA_CTRL_ATA_RST_B (1<<6)
+#define PATA_IMX_ATA_CTRL_IORDY_EN (1<<0)
+#define PATA_IMX_ATA_INT_EN 0x2C
+#define PATA_IMX_ATA_INTR_ATA_INTRQ2 (1<<3)
+#define PATA_IMX_DRIVE_DATA 0xA0
+#define PATA_IMX_DRIVE_CONTROL 0xD8
+
+struct pata_imx_priv {
+ struct clk *clk;
+ /* timings/interrupt/control regs */
+ u8 *host_regs;
+ u32 ata_ctl;
+};
+
+static int pata_imx_set_mode(struct ata_link *link, struct ata_device **unused)
+{
+ struct ata_device *dev;
+ struct ata_port *ap = link->ap;
+ struct pata_imx_priv *priv = ap->host->private_data;
+ u32 val;
+
+ ata_for_each_dev(dev, link, ENABLED) {
+ dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
+
+ val = __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL);
+ if (ata_pio_need_iordy(dev))
+ val |= PATA_IMX_ATA_CTRL_IORDY_EN;
+ else
+ val &= ~PATA_IMX_ATA_CTRL_IORDY_EN;
+ __raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL);
+
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ }
+ return 0;
+}
+
+static struct scsi_host_template pata_imx_sht = {
+ ATA_PIO_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations pata_imx_port_ops = {
+ .inherits = &ata_sff_port_ops,
+ .sff_data_xfer = ata_sff_data_xfer_noirq,
+ .cable_detect = ata_cable_unknown,
+ .set_mode = pata_imx_set_mode,
+};
+
+static void pata_imx_setup_port(struct ata_ioports *ioaddr)
+{
+ /* Fixup the port shift for platforms that need it */
+ ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << 2);
+ ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << 2);
+ ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << 2);
+ ioaddr->nsect_addr = ioaddr->cmd_addr + (ATA_REG_NSECT << 2);
+ ioaddr->lbal_addr = ioaddr->cmd_addr + (ATA_REG_LBAL << 2);
+ ioaddr->lbam_addr = ioaddr->cmd_addr + (ATA_REG_LBAM << 2);
+ ioaddr->lbah_addr = ioaddr->cmd_addr + (ATA_REG_LBAH << 2);
+ ioaddr->device_addr = ioaddr->cmd_addr + (ATA_REG_DEVICE << 2);
+ ioaddr->status_addr = ioaddr->cmd_addr + (ATA_REG_STATUS << 2);
+ ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << 2);
+}
+
+static int __devinit pata_imx_probe(struct platform_device *pdev)
+{
+ struct ata_host *host;
+ struct ata_port *ap;
+ struct pata_imx_priv *priv;
+ int irq = 0;
+ struct resource *io_res;
+
+ io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (io_res == NULL)
+ return -EINVAL;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return -EINVAL;
+
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct pata_imx_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(&pdev->dev, "Failed to get clock\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ clk_enable(priv->clk);
+
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ goto free_priv;
+
+ host->private_data = priv;
+ ap = host->ports[0];
+
+ ap->ops = &pata_imx_port_ops;
+ ap->pio_mask = ATA_PIO0;
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+
+ priv->host_regs = devm_ioremap(&pdev->dev, io_res->start,
+ resource_size(io_res));
+ if (!priv->host_regs) {
+ dev_err(&pdev->dev, "failed to map IO/CTL base\n");
+ goto free_priv;
+ }
+
+ ap->ioaddr.cmd_addr = priv->host_regs + PATA_IMX_DRIVE_DATA;
+ ap->ioaddr.ctl_addr = priv->host_regs + PATA_IMX_DRIVE_CONTROL;
+
+ ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
+
+ pata_imx_setup_port(&ap->ioaddr);
+
+ ata_port_desc(ap, "cmd 0x%llx ctl 0x%llx",
+ (unsigned long long)io_res->start + PATA_IMX_DRIVE_DATA,
+ (unsigned long long)io_res->start + PATA_IMX_DRIVE_CONTROL);
+
+ /* deassert resets */
+ __raw_writel(PATA_IMX_ATA_CTRL_FIFO_RST_B |
+ PATA_IMX_ATA_CTRL_ATA_RST_B,
+ priv->host_regs + PATA_IMX_ATA_CONTROL);
+ /* enable interrupts */
+ __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2,
+ priv->host_regs + PATA_IMX_ATA_INT_EN);
+
+ /* activate */
+ return ata_host_activate(host, irq, ata_sff_interrupt, 0,
+ &pata_imx_sht);
+
+free_priv:
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+ return -ENOMEM;
+}
+
+static int __devexit pata_imx_remove(struct platform_device *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct pata_imx_priv *priv = host->private_data;
+
+ ata_host_detach(host);
+
+ __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN);
+
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pata_imx_suspend(struct device *dev)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct pata_imx_priv *priv = host->private_data;
+ int ret;
+
+ ret = ata_host_suspend(host, PMSG_SUSPEND);
+ if (!ret) {
+ __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN);
+ priv->ata_ctl =
+ __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL);
+ clk_disable(priv->clk);
+ }
+
+ return ret;
+}
+
+static int pata_imx_resume(struct device *dev)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct pata_imx_priv *priv = host->private_data;
+
+ clk_enable(priv->clk);
+
+ __raw_writel(priv->ata_ctl, priv->host_regs + PATA_IMX_ATA_CONTROL);
+
+ __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2,
+ priv->host_regs + PATA_IMX_ATA_INT_EN);
+
+ ata_host_resume(host);
+
+ return 0;
+}
+
+static const struct dev_pm_ops pata_imx_pm_ops = {
+ .suspend = pata_imx_suspend,
+ .resume = pata_imx_resume,
+};
+#endif
+
+static struct platform_driver pata_imx_driver = {
+ .probe = pata_imx_probe,
+ .remove = __devexit_p(pata_imx_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &pata_imx_pm_ops,
+#endif
+ },
+};
+
+static int __init pata_imx_init(void)
+{
+ return platform_driver_register(&pata_imx_driver);
+}
+
+static void __exit pata_imx_exit(void)
+{
+ platform_driver_unregister(&pata_imx_driver);
+}
+module_init(pata_imx_init);
+module_exit(pata_imx_exit);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_DESCRIPTION("low-level driver for iMX PATA");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c
index 4d142a2ab8fd..cf9164d79f11 100644
--- a/drivers/ata/pata_it8213.c
+++ b/drivers/ata/pata_it8213.c
@@ -76,8 +76,8 @@ static void it8213_set_piomode (struct ata_port *ap, struct ata_device *adev)
{
unsigned int pio = adev->pio_mode - XFER_PIO_0;
struct pci_dev *dev = to_pci_dev(ap->host->dev);
- unsigned int idetm_port= ap->port_no ? 0x42 : 0x40;
- u16 idetm_data;
+ unsigned int master_port = ap->port_no ? 0x42 : 0x40;
+ u16 master_data;
int control = 0;
/*
@@ -100,19 +100,19 @@ static void it8213_set_piomode (struct ata_port *ap, struct ata_device *adev)
if (adev->class != ATA_DEV_ATA)
control |= 4; /* PPE */
- pci_read_config_word(dev, idetm_port, &idetm_data);
+ pci_read_config_word(dev, master_port, &master_data);
/* Set PPE, IE, and TIME as appropriate */
if (adev->devno == 0) {
- idetm_data &= 0xCCF0;
- idetm_data |= control;
- idetm_data |= (timings[pio][0] << 12) |
+ master_data &= 0xCCF0;
+ master_data |= control;
+ master_data |= (timings[pio][0] << 12) |
(timings[pio][1] << 8);
} else {
u8 slave_data;
- idetm_data &= 0xFF0F;
- idetm_data |= (control << 4);
+ master_data &= 0xFF0F;
+ master_data |= (control << 4);
/* Slave timing in separate register */
pci_read_config_byte(dev, 0x44, &slave_data);
@@ -121,8 +121,8 @@ static void it8213_set_piomode (struct ata_port *ap, struct ata_device *adev)
pci_write_config_byte(dev, 0x44, slave_data);
}
- idetm_data |= 0x4000; /* Ensure SITRE is set */
- pci_write_config_word(dev, idetm_port, idetm_data);
+ master_data |= 0x4000; /* Ensure SITRE is set */
+ pci_write_config_word(dev, master_port, master_data);
}
/**
@@ -163,7 +163,7 @@ static void it8213_set_dmamode (struct ata_port *ap, struct ata_device *adev)
/* Clocks follow the PIIX style */
u_speed = min(2 - (udma & 1), udma);
- if (udma == 5)
+ if (udma > 4)
u_clock = 0x1000; /* 100Mhz */
else if (udma > 2)
u_clock = 1; /* 66Mhz */
@@ -258,20 +258,17 @@ static struct ata_port_operations it8213_ops = {
static int it8213_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
static const struct ata_port_info info = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA12_ONLY,
- .udma_mask = ATA_UDMA4, /* FIXME: want UDMA 100? */
+ .udma_mask = ATA_UDMA6,
.port_ops = &it8213_ops,
};
/* Current IT8213 stuff is single port */
const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info };
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
return ata_pci_bmdma_init_one(pdev, ppi, &it8213_sht, NULL, 0);
}
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index 2d15f2548a10..62c5d00abd2e 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -473,12 +473,12 @@ static int it821x_smart_set_mode(struct ata_link *link, struct ata_device **unus
/* We do need the right mode information for DMA or PIO
and this comes from the current configuration flags */
if (ata_id_has_dma(dev->id)) {
- ata_dev_printk(dev, KERN_INFO, "configured for DMA\n");
+ ata_dev_info(dev, "configured for DMA\n");
dev->xfer_mode = XFER_MW_DMA_0;
dev->xfer_shift = ATA_SHIFT_MWDMA;
dev->flags &= ~ATA_DFLAG_PIO;
} else {
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ ata_dev_info(dev, "configured for PIO\n");
dev->xfer_mode = XFER_PIO_0;
dev->xfer_shift = ATA_SHIFT_PIO;
dev->flags |= ATA_DFLAG_PIO;
@@ -508,12 +508,12 @@ static void it821x_dev_config(struct ata_device *adev)
if (strstr(model_num, "Integrated Technology Express")) {
/* RAID mode */
- ata_dev_printk(adev, KERN_INFO, "%sRAID%d volume",
- adev->id[147]?"Bootable ":"",
- adev->id[129]);
+ ata_dev_info(adev, "%sRAID%d volume",
+ adev->id[147] ? "Bootable " : "",
+ adev->id[129]);
if (adev->id[129] != 1)
- printk("(%dK stripe)", adev->id[146]);
- printk(".\n");
+ pr_cont("(%dK stripe)", adev->id[146]);
+ pr_cont("\n");
}
/* This is a controller firmware triggered funny, don't
report the drive faulty! */
@@ -610,7 +610,7 @@ static void it821x_display_disk(int n, u8 *buf)
char *cbl = "(40 wire cable)";
static const char *types[5] = {
- "RAID0", "RAID1" "RAID 0+1", "JBOD", "DISK"
+ "RAID0", "RAID1", "RAID 0+1", "JBOD", "DISK"
};
if (buf[52] > 4) /* No Disk */
diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c
index f6b3f995f58a..15b64311fe0a 100644
--- a/drivers/ata/pata_ixp4xx_cf.c
+++ b/drivers/ata/pata_ixp4xx_cf.c
@@ -31,7 +31,7 @@ static int ixp4xx_set_mode(struct ata_link *link, struct ata_device **error)
struct ata_device *dev;
ata_for_each_dev(dev, link, ENABLED) {
- ata_dev_printk(dev, KERN_INFO, "configured for PIO0\n");
+ ata_dev_info(dev, "configured for PIO0\n");
dev->pio_mode = XFER_PIO_0;
dev->xfer_mode = XFER_PIO_0;
dev->xfer_shift = ATA_SHIFT_PIO;
@@ -181,7 +181,7 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev)
ixp4xx_setup_port(ap, data, cs0->start, cs1->start);
- dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* activate host */
return ata_host_activate(host, irq, ata_sff_interrupt, 0, &ixp4xx_sht);
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index 6bd9425ba5ab..35aca7d1a3eb 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -79,15 +79,6 @@ static int all;
module_param(all, int, 0444);
MODULE_PARM_DESC(all, "Grab all legacy port devices, even if PCI(0=off, 1=on)");
-struct legacy_data {
- unsigned long timing;
- u8 clock[2];
- u8 last;
- int fast;
- struct platform_device *platform_dev;
-
-};
-
enum controller {
BIOS = 0,
SNOOP = 1,
@@ -104,6 +95,14 @@ enum controller {
UNKNOWN = -1
};
+struct legacy_data {
+ unsigned long timing;
+ u8 clock[2];
+ u8 last;
+ int fast;
+ enum controller type;
+ struct platform_device *platform_dev;
+};
struct legacy_probe {
unsigned char *name;
@@ -137,11 +136,17 @@ static int ht6560a; /* HT 6560A on primary 1, second 2, both 3 */
static int ht6560b; /* HT 6560A on primary 1, second 2, both 3 */
static int opti82c611a; /* Opti82c611A on primary 1, sec 2, both 3 */
static int opti82c46x; /* Opti 82c465MV present(pri/sec autodetect) */
-static int qdi; /* Set to probe QDI controllers */
static int autospeed; /* Chip present which snoops speed changes */
static int pio_mask = ATA_PIO4; /* PIO range for autospeed devices */
static int iordy_mask = 0xFFFFFFFF; /* Use iordy if available */
+/* Set to probe QDI controllers */
+#ifdef CONFIG_PATA_QDI_MODULE
+static int qdi = 1;
+#else
+static int qdi;
+#endif
+
#ifdef CONFIG_PATA_WINBOND_VLB_MODULE
static int winbond = 1; /* Set to probe Winbond controllers,
give I/O port if non standard */
@@ -213,7 +218,7 @@ static int legacy_set_mode(struct ata_link *link, struct ata_device **unused)
struct ata_device *dev;
ata_for_each_dev(dev, link, ENABLED) {
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ ata_dev_info(dev, "configured for PIO\n");
dev->pio_mode = XFER_PIO_0;
dev->xfer_mode = XFER_PIO_0;
dev->xfer_shift = ATA_SHIFT_PIO;
@@ -631,40 +636,20 @@ static struct ata_port_operations opti82c46x_port_ops = {
.qc_issue = opti82c46x_qc_issue,
};
-static void qdi6500_set_piomode(struct ata_port *ap, struct ata_device *adev)
-{
- struct ata_timing t;
- struct legacy_data *ld_qdi = ap->host->private_data;
- int active, recovery;
- u8 timing;
-
- /* Get the timing data in cycles */
- ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000);
-
- if (ld_qdi->fast) {
- active = 8 - clamp_val(t.active, 1, 8);
- recovery = 18 - clamp_val(t.recover, 3, 18);
- } else {
- active = 9 - clamp_val(t.active, 2, 9);
- recovery = 15 - clamp_val(t.recover, 0, 15);
- }
- timing = (recovery << 4) | active | 0x08;
-
- ld_qdi->clock[adev->devno] = timing;
-
- outb(timing, ld_qdi->timing);
-}
-
/**
- * qdi6580dp_set_piomode - PIO setup for dual channel
+ * qdi65x0_set_piomode - PIO setup for QDI65x0
* @ap: Port
* @adev: Device
*
+ * In single channel mode the 6580 has one clock per device and we can
+ * avoid the requirement to clock switch. We also have to load the timing
+ * into the right clock according to whether we are master or slave.
+ *
* In dual channel mode the 6580 has one clock per channel and we have
* to software clockswitch in qc_issue.
*/
-static void qdi6580dp_set_piomode(struct ata_port *ap, struct ata_device *adev)
+static void qdi65x0_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
struct ata_timing t;
struct legacy_data *ld_qdi = ap->host->private_data;
@@ -682,47 +667,15 @@ static void qdi6580dp_set_piomode(struct ata_port *ap, struct ata_device *adev)
recovery = 15 - clamp_val(t.recover, 0, 15);
}
timing = (recovery << 4) | active | 0x08;
-
ld_qdi->clock[adev->devno] = timing;
- outb(timing, ld_qdi->timing + 2 * ap->port_no);
- /* Clear the FIFO */
- if (adev->class != ATA_DEV_ATA)
- outb(0x5F, (ld_qdi->timing & 0xFFF0) + 3);
-}
-
-/**
- * qdi6580_set_piomode - PIO setup for single channel
- * @ap: Port
- * @adev: Device
- *
- * In single channel mode the 6580 has one clock per device and we can
- * avoid the requirement to clock switch. We also have to load the timing
- * into the right clock according to whether we are master or slave.
- */
-
-static void qdi6580_set_piomode(struct ata_port *ap, struct ata_device *adev)
-{
- struct ata_timing t;
- struct legacy_data *ld_qdi = ap->host->private_data;
- int active, recovery;
- u8 timing;
-
- /* Get the timing data in cycles */
- ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000);
+ if (ld_qdi->type == QDI6580)
+ outb(timing, ld_qdi->timing + 2 * adev->devno);
+ else
+ outb(timing, ld_qdi->timing + 2 * ap->port_no);
- if (ld_qdi->fast) {
- active = 8 - clamp_val(t.active, 1, 8);
- recovery = 18 - clamp_val(t.recover, 3, 18);
- } else {
- active = 9 - clamp_val(t.active, 2, 9);
- recovery = 15 - clamp_val(t.recover, 0, 15);
- }
- timing = (recovery << 4) | active | 0x08;
- ld_qdi->clock[adev->devno] = timing;
- outb(timing, ld_qdi->timing + 2 * adev->devno);
/* Clear the FIFO */
- if (adev->class != ATA_DEV_ATA)
+ if (ld_qdi->type != QDI6500 && adev->class != ATA_DEV_ATA)
outb(0x5F, (ld_qdi->timing & 0xFFF0) + 3);
}
@@ -789,20 +742,20 @@ static int qdi_port(struct platform_device *dev,
static struct ata_port_operations qdi6500_port_ops = {
.inherits = &legacy_base_port_ops,
- .set_piomode = qdi6500_set_piomode,
+ .set_piomode = qdi65x0_set_piomode,
.qc_issue = qdi_qc_issue,
.sff_data_xfer = vlb32_data_xfer,
};
static struct ata_port_operations qdi6580_port_ops = {
.inherits = &legacy_base_port_ops,
- .set_piomode = qdi6580_set_piomode,
+ .set_piomode = qdi65x0_set_piomode,
.sff_data_xfer = vlb32_data_xfer,
};
static struct ata_port_operations qdi6580dp_port_ops = {
.inherits = &legacy_base_port_ops,
- .set_piomode = qdi6580dp_set_piomode,
+ .set_piomode = qdi65x0_set_piomode,
.qc_issue = qdi_qc_issue,
.sff_data_xfer = vlb32_data_xfer,
};
@@ -879,29 +832,29 @@ static struct ata_port_operations winbond_port_ops = {
};
static struct legacy_controller controllers[] = {
- {"BIOS", &legacy_port_ops, 0x1F,
+ {"BIOS", &legacy_port_ops, ATA_PIO4,
ATA_FLAG_NO_IORDY, 0, NULL },
- {"Snooping", &simple_port_ops, 0x1F,
+ {"Snooping", &simple_port_ops, ATA_PIO4,
0, 0, NULL },
- {"PDC20230", &pdc20230_port_ops, 0x7,
+ {"PDC20230", &pdc20230_port_ops, ATA_PIO2,
ATA_FLAG_NO_IORDY,
ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE, NULL },
- {"HT6560A", &ht6560a_port_ops, 0x07,
+ {"HT6560A", &ht6560a_port_ops, ATA_PIO2,
ATA_FLAG_NO_IORDY, 0, NULL },
- {"HT6560B", &ht6560b_port_ops, 0x1F,
+ {"HT6560B", &ht6560b_port_ops, ATA_PIO4,
ATA_FLAG_NO_IORDY, 0, NULL },
- {"OPTI82C611A", &opti82c611a_port_ops, 0x0F,
+ {"OPTI82C611A", &opti82c611a_port_ops, ATA_PIO3,
0, 0, NULL },
- {"OPTI82C46X", &opti82c46x_port_ops, 0x0F,
+ {"OPTI82C46X", &opti82c46x_port_ops, ATA_PIO3,
0, 0, NULL },
- {"QDI6500", &qdi6500_port_ops, 0x07,
+ {"QDI6500", &qdi6500_port_ops, ATA_PIO2,
ATA_FLAG_NO_IORDY,
ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE, qdi_port },
- {"QDI6580", &qdi6580_port_ops, 0x1F,
+ {"QDI6580", &qdi6580_port_ops, ATA_PIO4,
0, ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE, qdi_port },
- {"QDI6580DP", &qdi6580dp_port_ops, 0x1F,
+ {"QDI6580DP", &qdi6580dp_port_ops, ATA_PIO4,
0, ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE, qdi_port },
- {"W83759A", &winbond_port_ops, 0x1F,
+ {"W83759A", &winbond_port_ops, ATA_PIO4,
0, ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE,
winbond_port }
};
@@ -1022,6 +975,7 @@ static __init int legacy_init_one(struct legacy_probe *probe)
ctrl_addr = devm_ioport_map(&pdev->dev, io + 0x0206, 1);
if (!io_addr || !ctrl_addr)
goto fail;
+ ld->type = probe->type;
if (controller->setup)
if (controller->setup(pdev, probe, ld) < 0)
goto fail;
@@ -1306,6 +1260,7 @@ MODULE_AUTHOR("Alan Cox");
MODULE_DESCRIPTION("low-level driver for legacy ATA");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("pata_qdi");
MODULE_ALIAS("pata_winbond");
module_param(probe_all, int, 0);
diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c
index 46f589edccdb..b057e3fa44bc 100644
--- a/drivers/ata/pata_macio.c
+++ b/drivers/ata/pata_macio.c
@@ -772,8 +772,9 @@ static void pata_macio_reset_hw(struct pata_macio_priv *priv, int resume)
pci_restore_state(priv->pdev);
rc = pcim_enable_device(priv->pdev);
if (rc)
- dev_printk(KERN_ERR, &priv->pdev->dev,
- "Failed to enable device after resume (%d)\n", rc);
+ dev_err(&priv->pdev->dev,
+ "Failed to enable device after resume (%d)\n",
+ rc);
else
pci_set_master(priv->pdev);
}
@@ -812,7 +813,7 @@ static int pata_macio_slave_config(struct scsi_device *sdev)
blk_queue_update_dma_pad(sdev->request_queue, 31);
/* Tell the world about it */
- ata_dev_printk(dev, KERN_INFO, "OHare alignment limits applied\n");
+ ata_dev_info(dev, "OHare alignment limits applied\n");
return 0;
}
@@ -838,8 +839,7 @@ static int pata_macio_slave_config(struct scsi_device *sdev)
cmd | PCI_COMMAND_INVALIDATE);
/* Tell the world about it */
- ata_dev_printk(dev, KERN_INFO,
- "K2/Shasta alignment limits applied\n");
+ ata_dev_info(dev, "K2/Shasta alignment limits applied\n");
}
return 0;
diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
index 2fcac511d39c..3e1746314f22 100644
--- a/drivers/ata/pata_mpc52xx.c
+++ b/drivers/ata/pata_mpc52xx.c
@@ -780,7 +780,7 @@ mpc52xx_ata_probe(struct platform_device *op)
}
task_irq = bcom_get_task_irq(dmatsk);
- ret = request_irq(task_irq, &mpc52xx_ata_task_irq, IRQF_DISABLED,
+ ret = request_irq(task_irq, &mpc52xx_ata_task_irq, 0,
"ATA task", priv);
if (ret) {
dev_err(&op->dev, "error requesting DMA IRQ\n");
diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c
index d8d9c5807740..9dc16df84191 100644
--- a/drivers/ata/pata_mpiix.c
+++ b/drivers/ata/pata_mpiix.c
@@ -152,15 +152,13 @@ static struct ata_port_operations mpiix_port_ops = {
static int mpiix_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
/* Single threaded by the PCI probe logic */
- static int printed_version;
struct ata_host *host;
struct ata_port *ap;
void __iomem *cmd_addr, *ctl_addr;
u16 idetim;
int cmd, ctl, irq;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &dev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&dev->dev, DRV_VERSION);
host = ata_host_alloc(&dev->dev, 1);
if (!host)
diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c
index 3eb921c746a1..9979a43bc596 100644
--- a/drivers/ata/pata_netcell.c
+++ b/drivers/ata/pata_netcell.c
@@ -57,7 +57,6 @@ static struct ata_port_operations netcell_ops = {
static int netcell_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
static const struct ata_port_info info = {
.flags = ATA_FLAG_SLAVE_POSS,
/* Actually we don't really care about these as the
@@ -70,9 +69,7 @@ static int netcell_init_one (struct pci_dev *pdev, const struct pci_device_id *e
const struct ata_port_info *port_info[] = { &info, NULL };
int rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c
index 2110863bb3db..31d5986537a3 100644
--- a/drivers/ata/pata_ns87410.c
+++ b/drivers/ata/pata_ns87410.c
@@ -86,7 +86,7 @@ static void ns87410_set_piomode(struct ata_port *ap, struct ata_device *adev)
idefr &= ~0x04;
if (ata_timing_compute(adev, adev->pio_mode, &at, 30303, 1) < 0) {
- dev_printk(KERN_ERR, &pdev->dev, "unknown mode %d.\n", adev->pio_mode);
+ dev_err(&pdev->dev, "unknown mode %d\n", adev->pio_mode);
return;
}
diff --git a/drivers/ata/pata_ns87415.c b/drivers/ata/pata_ns87415.c
index 605f198f958c..f1d517bc5b49 100644
--- a/drivers/ata/pata_ns87415.c
+++ b/drivers/ata/pata_ns87415.c
@@ -350,7 +350,6 @@ static void ns87415_fixup(struct pci_dev *pdev)
static int ns87415_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
static const struct ata_port_info info = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
@@ -370,9 +369,7 @@ static int ns87415_init_one (struct pci_dev *pdev, const struct pci_device_id *e
if (PCI_SLOT(pdev->devfn) == 0x0E)
ppi[0] = &info87560;
#endif
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c
index 220ddc90608f..1d61d5d278fa 100644
--- a/drivers/ata/pata_octeon_cf.c
+++ b/drivers/ata/pata_octeon_cf.c
@@ -405,7 +405,7 @@ static int octeon_cf_softreset16(struct ata_link *link, unsigned int *classes,
rc = ata_sff_wait_after_reset(link, 1, deadline);
if (rc) {
- ata_link_printk(link, KERN_ERR, "SRST failed (errno=%d)\n", rc);
+ ata_link_err(link, "SRST failed (errno=%d)\n", rc);
return rc;
}
@@ -807,6 +807,7 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev)
irq_handler_t irq_handler = NULL;
void __iomem *base;
struct octeon_cf_port *cf_port;
+ char version[32];
res_cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -905,10 +906,11 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev)
ata_port_desc(ap, "cmd %p ctl %p", base, ap->ioaddr.ctl_addr);
- dev_info(&pdev->dev, "version " DRV_VERSION" %d bit%s.\n",
+ snprintf(version, sizeof(version), "%s %d bit%s",
+ DRV_VERSION,
(ocd->is16bit) ? 16 : 8,
(cs1) ? ", True IDE" : "");
-
+ ata_print_version_once(&pdev->dev, version);
return ata_host_activate(host, irq, irq_handler, 0, &octeon_cf_sht);
diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c
index f3054009bd25..2a472c5bb7db 100644
--- a/drivers/ata/pata_of_platform.c
+++ b/drivers/ata/pata_of_platform.c
@@ -11,6 +11,8 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/ata_platform.h>
@@ -50,18 +52,18 @@ static int __devinit pata_of_platform_probe(struct platform_device *ofdev)
}
ret = of_irq_to_resource(dn, 0, &irq_res);
- if (ret == NO_IRQ)
+ if (!ret)
irq_res.start = irq_res.end = 0;
else
irq_res.flags = 0;
prop = of_get_property(dn, "reg-shift", NULL);
if (prop)
- reg_shift = *prop;
+ reg_shift = be32_to_cpup(prop);
prop = of_get_property(dn, "pio-mode", NULL);
if (prop) {
- pio_mode = *prop;
+ pio_mode = be32_to_cpup(prop);
if (pio_mode > 6) {
dev_err(&ofdev->dev, "invalid pio-mode\n");
return -EINVAL;
diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c
index b811c1636204..98cdf50e4065 100644
--- a/drivers/ata/pata_oldpiix.c
+++ b/drivers/ata/pata_oldpiix.c
@@ -235,7 +235,6 @@ static struct ata_port_operations oldpiix_pata_ops = {
static int oldpiix_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
static const struct ata_port_info info = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
@@ -244,9 +243,7 @@ static int oldpiix_init_one (struct pci_dev *pdev, const struct pci_device_id *e
};
const struct ata_port_info *ppi[] = { &info, NULL };
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
return ata_pci_bmdma_init_one(pdev, ppi, &oldpiix_sht, NULL, 0);
}
diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c
index 00c5a02a94fc..accc033faf77 100644
--- a/drivers/ata/pata_opti.c
+++ b/drivers/ata/pata_opti.c
@@ -167,10 +167,8 @@ static int opti_init_one(struct pci_dev *dev, const struct pci_device_id *id)
.port_ops = &opti_port_ops
};
const struct ata_port_info *ppi[] = { &info, NULL };
- static int printed_version;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &dev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&dev->dev, DRV_VERSION);
return ata_pci_sff_init_one(dev, ppi, &opti_sht, NULL, 0);
}
diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c
index 0852cd07de08..77cb91408632 100644
--- a/drivers/ata/pata_optidma.c
+++ b/drivers/ata/pata_optidma.c
@@ -411,11 +411,9 @@ static int optidma_init_one(struct pci_dev *dev, const struct pci_device_id *id)
.port_ops = &optiplus_port_ops
};
const struct ata_port_info *ppi[] = { &info_82c700, NULL };
- static int printed_version;
int rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &dev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&dev->dev, DRV_VERSION);
rc = pcim_enable_device(dev);
if (rc)
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
index 021abe6d8527..a808ba03bd7f 100644
--- a/drivers/ata/pata_pcmcia.c
+++ b/drivers/ata/pata_pcmcia.c
@@ -68,7 +68,7 @@ static int pcmcia_set_mode(struct ata_link *link, struct ata_device **r_failed_d
the same vendor - check serial */
if (memcmp(master->id + ATA_ID_SERNO, slave->id + ATA_ID_SERNO,
ATA_ID_SERNO_LEN) == 0 && master->id[ATA_ID_SERNO] >> 8) {
- ata_dev_printk(slave, KERN_WARNING, "is a ghost device, ignoring.\n");
+ ata_dev_warn(slave, "is a ghost device, ignoring\n");
ata_dev_disable(slave);
}
}
@@ -142,8 +142,7 @@ static void pcmcia_8bit_drain_fifo(struct ata_queued_cmd *qc)
ioread8(ap->ioaddr.data_addr);
if (count)
- ata_port_printk(ap, KERN_WARNING, "drained %d bytes to clear DRQ.\n",
- count);
+ ata_port_warn(ap, "drained %d bytes to clear DRQ\n", count);
}
diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c
index 9765ace16921..7d63f24179c7 100644
--- a/drivers/ata/pata_pdc2027x.c
+++ b/drivers/ata/pata_pdc2027x.c
@@ -63,6 +63,7 @@ enum {
};
static int pdc2027x_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
+static int pdc2027x_reinit_one(struct pci_dev *pdev);
static int pdc2027x_prereset(struct ata_link *link, unsigned long deadline);
static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev);
static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev);
@@ -126,6 +127,10 @@ static struct pci_driver pdc2027x_pci_driver = {
.id_table = pdc2027x_pci_tbl,
.probe = pdc2027x_init_one,
.remove = ata_pci_remove_one,
+#ifdef CONFIG_PM
+ .suspend = ata_pci_device_suspend,
+ .resume = pdc2027x_reinit_one,
+#endif
};
static struct scsi_host_template pdc2027x_sht = {
@@ -655,7 +660,7 @@ static int pdc_hardware_init(struct ata_host *host, unsigned int board_idx)
*/
pll_clock = pdc_detect_pll_input_clock(host);
- dev_printk(KERN_INFO, host->dev, "PLL input clock %ld kHz\n", pll_clock/1000);
+ dev_info(host->dev, "PLL input clock %ld kHz\n", pll_clock/1000);
/* Adjust PLL control register */
pdc_adjust_pll(host, pll_clock, board_idx);
@@ -697,7 +702,6 @@ static void pdc_ata_setup_port(struct ata_ioports *port, void __iomem *base)
*/
static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
static const unsigned long cmd_offset[] = { 0x17c0, 0x15c0 };
static const unsigned long bmdma_offset[] = { 0x1000, 0x1008 };
unsigned int board_idx = (unsigned int) ent->driver_data;
@@ -707,8 +711,7 @@ static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_de
void __iomem *mmio_base;
int i, rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* alloc host */
host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
@@ -756,6 +759,31 @@ static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_de
IRQF_SHARED, &pdc2027x_sht);
}
+#ifdef CONFIG_PM
+static int pdc2027x_reinit_one(struct pci_dev *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ unsigned int board_idx;
+ int rc;
+
+ rc = ata_pci_device_do_resume(pdev);
+ if (rc)
+ return rc;
+
+ if (pdev->device == PCI_DEVICE_ID_PROMISE_20268 ||
+ pdev->device == PCI_DEVICE_ID_PROMISE_20270)
+ board_idx = PDC_UDMA_100;
+ else
+ board_idx = PDC_UDMA_133;
+
+ if (pdc_hardware_init(host, board_idx))
+ return -EIO;
+
+ ata_host_resume(host);
+ return 0;
+}
+#endif
+
/**
* pdc2027x_init - Called after this module is loaded into the kernel.
*/
diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
index 50400fa120fe..2067308f683f 100644
--- a/drivers/ata/pata_platform.c
+++ b/drivers/ata/pata_platform.c
@@ -39,7 +39,7 @@ static int pata_platform_set_mode(struct ata_link *link, struct ata_device **unu
dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
dev->xfer_shift = ATA_SHIFT_PIO;
dev->flags |= ATA_DFLAG_PIO;
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ ata_dev_info(dev, "configured for PIO\n");
}
return 0;
}
diff --git a/drivers/ata/pata_qdi.c b/drivers/ata/pata_qdi.c
deleted file mode 100644
index 45879dc6fa41..000000000000
--- a/drivers/ata/pata_qdi.c
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * pata_qdi.c - QDI VLB ATA controllers
- * (C) 2006 Red Hat
- *
- * This driver mostly exists as a proof of concept for non PCI devices under
- * libata. While the QDI6580 was 'neat' in 1993 it is no longer terribly
- * useful.
- *
- * Tuning code written from the documentation at
- * http://www.ryston.cz/petr/vlb/qd6500.html
- * http://www.ryston.cz/petr/vlb/qd6580.html
- *
- * Probe code based on drivers/ide/legacy/qd65xx.c
- * Rewritten from the work of Colten Edwards <pje120@cs.usask.ca> by
- * Samuel Thibault <samuel.thibault@ens-lyon.org>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/blkdev.h>
-#include <linux/delay.h>
-#include <scsi/scsi_host.h>
-#include <linux/libata.h>
-#include <linux/platform_device.h>
-
-#define DRV_NAME "pata_qdi"
-#define DRV_VERSION "0.3.1"
-
-#define NR_HOST 4 /* Two 6580s */
-
-struct qdi_data {
- unsigned long timing;
- u8 clock[2];
- u8 last;
- int fast;
- struct platform_device *platform_dev;
-
-};
-
-static struct ata_host *qdi_host[NR_HOST];
-static struct qdi_data qdi_data[NR_HOST];
-static int nr_qdi_host;
-
-#ifdef MODULE
-static int probe_qdi = 1;
-#else
-static int probe_qdi;
-#endif
-
-static void qdi6500_set_piomode(struct ata_port *ap, struct ata_device *adev)
-{
- struct ata_timing t;
- struct qdi_data *qdi = ap->host->private_data;
- int active, recovery;
- u8 timing;
-
- /* Get the timing data in cycles */
- ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000);
-
- if (qdi->fast) {
- active = 8 - clamp_val(t.active, 1, 8);
- recovery = 18 - clamp_val(t.recover, 3, 18);
- } else {
- active = 9 - clamp_val(t.active, 2, 9);
- recovery = 15 - clamp_val(t.recover, 0, 15);
- }
- timing = (recovery << 4) | active | 0x08;
-
- qdi->clock[adev->devno] = timing;
-
- outb(timing, qdi->timing);
-}
-
-static void qdi6580_set_piomode(struct ata_port *ap, struct ata_device *adev)
-{
- struct ata_timing t;
- struct qdi_data *qdi = ap->host->private_data;
- int active, recovery;
- u8 timing;
-
- /* Get the timing data in cycles */
- ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000);
-
- if (qdi->fast) {
- active = 8 - clamp_val(t.active, 1, 8);
- recovery = 18 - clamp_val(t.recover, 3, 18);
- } else {
- active = 9 - clamp_val(t.active, 2, 9);
- recovery = 15 - clamp_val(t.recover, 0, 15);
- }
- timing = (recovery << 4) | active | 0x08;
-
- qdi->clock[adev->devno] = timing;
-
- outb(timing, qdi->timing);
-
- /* Clear the FIFO */
- if (adev->class != ATA_DEV_ATA)
- outb(0x5F, (qdi->timing & 0xFFF0) + 3);
-}
-
-/**
- * qdi_qc_issue - command issue
- * @qc: command pending
- *
- * Called when the libata layer is about to issue a command. We wrap
- * this interface so that we can load the correct ATA timings.
- */
-
-static unsigned int qdi_qc_issue(struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
- struct ata_device *adev = qc->dev;
- struct qdi_data *qdi = ap->host->private_data;
-
- if (qdi->clock[adev->devno] != qdi->last) {
- if (adev->pio_mode) {
- qdi->last = qdi->clock[adev->devno];
- outb(qdi->clock[adev->devno], qdi->timing);
- }
- }
- return ata_sff_qc_issue(qc);
-}
-
-static unsigned int qdi_data_xfer(struct ata_device *dev, unsigned char *buf,
- unsigned int buflen, int rw)
-{
- if (ata_id_has_dword_io(dev->id)) {
- struct ata_port *ap = dev->link->ap;
- int slop = buflen & 3;
-
- if (rw == READ)
- ioread32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
- else
- iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
-
- if (unlikely(slop)) {
- __le32 pad;
- if (rw == READ) {
- pad = cpu_to_le32(ioread32(ap->ioaddr.data_addr));
- memcpy(buf + buflen - slop, &pad, slop);
- } else {
- memcpy(&pad, buf + buflen - slop, slop);
- iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr);
- }
- buflen += 4 - slop;
- }
- } else
- buflen = ata_sff_data_xfer(dev, buf, buflen, rw);
-
- return buflen;
-}
-
-static struct scsi_host_template qdi_sht = {
- ATA_PIO_SHT(DRV_NAME),
-};
-
-static struct ata_port_operations qdi6500_port_ops = {
- .inherits = &ata_sff_port_ops,
- .qc_issue = qdi_qc_issue,
- .sff_data_xfer = qdi_data_xfer,
- .cable_detect = ata_cable_40wire,
- .set_piomode = qdi6500_set_piomode,
-};
-
-static struct ata_port_operations qdi6580_port_ops = {
- .inherits = &qdi6500_port_ops,
- .set_piomode = qdi6580_set_piomode,
-};
-
-/**
- * qdi_init_one - attach a qdi interface
- * @type: Type to display
- * @io: I/O port start
- * @irq: interrupt line
- * @fast: True if on a > 33Mhz VLB
- *
- * Register an ISA bus IDE interface. Such interfaces are PIO and we
- * assume do not support IRQ sharing.
- */
-
-static __init int qdi_init_one(unsigned long port, int type, unsigned long io, int irq, int fast)
-{
- unsigned long ctl = io + 0x206;
- struct platform_device *pdev;
- struct ata_host *host;
- struct ata_port *ap;
- void __iomem *io_addr, *ctl_addr;
- int ret;
-
- /*
- * Fill in a probe structure first of all
- */
-
- pdev = platform_device_register_simple(DRV_NAME, nr_qdi_host, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
-
- ret = -ENOMEM;
- io_addr = devm_ioport_map(&pdev->dev, io, 8);
- ctl_addr = devm_ioport_map(&pdev->dev, ctl, 1);
- if (!io_addr || !ctl_addr)
- goto fail;
-
- ret = -ENOMEM;
- host = ata_host_alloc(&pdev->dev, 1);
- if (!host)
- goto fail;
- ap = host->ports[0];
-
- if (type == 6580) {
- ap->ops = &qdi6580_port_ops;
- ap->pio_mask = ATA_PIO4;
- ap->flags |= ATA_FLAG_SLAVE_POSS;
- } else {
- ap->ops = &qdi6500_port_ops;
- ap->pio_mask = ATA_PIO2; /* Actually PIO3 !IORDY is possible */
- ap->flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY;
- }
-
- ap->ioaddr.cmd_addr = io_addr;
- ap->ioaddr.altstatus_addr = ctl_addr;
- ap->ioaddr.ctl_addr = ctl_addr;
- ata_sff_std_ports(&ap->ioaddr);
-
- ata_port_desc(ap, "cmd %lx ctl %lx", io, ctl);
-
- /*
- * Hook in a private data structure per channel
- */
- ap->private_data = &qdi_data[nr_qdi_host];
-
- qdi_data[nr_qdi_host].timing = port;
- qdi_data[nr_qdi_host].fast = fast;
- qdi_data[nr_qdi_host].platform_dev = pdev;
-
- printk(KERN_INFO DRV_NAME": qd%d at 0x%lx.\n", type, io);
-
- /* activate */
- ret = ata_host_activate(host, irq, ata_sff_interrupt, 0, &qdi_sht);
- if (ret)
- goto fail;
-
- qdi_host[nr_qdi_host++] = dev_get_drvdata(&pdev->dev);
- return 0;
-
- fail:
- platform_device_unregister(pdev);
- return ret;
-}
-
-/**
- * qdi_init - attach qdi interfaces
- *
- * Attach qdi IDE interfaces by scanning the ports it may occupy.
- */
-
-static __init int qdi_init(void)
-{
- unsigned long flags;
- static const unsigned long qd_port[2] = { 0x30, 0xB0 };
- static const unsigned long ide_port[2] = { 0x170, 0x1F0 };
- static const int ide_irq[2] = { 14, 15 };
-
- int ct = 0;
- int i;
-
- if (probe_qdi == 0)
- return -ENODEV;
-
- /*
- * Check each possible QD65xx base address
- */
-
- for (i = 0; i < 2; i++) {
- unsigned long port = qd_port[i];
- u8 r, res;
-
-
- if (request_region(port, 2, "pata_qdi")) {
- /* Check for a card */
- local_irq_save(flags);
- r = inb_p(port);
- outb_p(0x19, port);
- res = inb_p(port);
- outb_p(r, port);
- local_irq_restore(flags);
-
- /* Fail */
- if (res == 0x19)
- {
- release_region(port, 2);
- continue;
- }
-
- /* Passes the presence test */
- r = inb_p(port + 1); /* Check port agrees with port set */
- if ((r & 2) >> 1 != i) {
- release_region(port, 2);
- continue;
- }
-
- /* Check card type */
- if ((r & 0xF0) == 0xC0) {
- /* QD6500: single channel */
- if (r & 8) {
- /* Disabled ? */
- release_region(port, 2);
- continue;
- }
- if (qdi_init_one(port, 6500, ide_port[r & 0x01], ide_irq[r & 0x01], r & 0x04) == 0)
- ct++;
- }
- if (((r & 0xF0) == 0xA0) || (r & 0xF0) == 0x50) {
- /* QD6580: dual channel */
- if (!request_region(port + 2 , 2, "pata_qdi"))
- {
- release_region(port, 2);
- continue;
- }
- res = inb(port + 3);
- if (res & 1) {
- /* Single channel mode */
- if (qdi_init_one(port, 6580, ide_port[r & 0x01], ide_irq[r & 0x01], r & 0x04) == 0)
- ct++;
- } else {
- /* Dual channel mode */
- if (qdi_init_one(port, 6580, 0x1F0, 14, r & 0x04) == 0)
- ct++;
- if (qdi_init_one(port + 2, 6580, 0x170, 15, r & 0x04) == 0)
- ct++;
- }
- }
- }
- }
- if (ct != 0)
- return 0;
- return -ENODEV;
-}
-
-static __exit void qdi_exit(void)
-{
- int i;
-
- for (i = 0; i < nr_qdi_host; i++) {
- ata_host_detach(qdi_host[i]);
- /* Free the control resource. The 6580 dual channel has the resources
- * claimed as a pair of 2 byte resources so we need no special cases...
- */
- release_region(qdi_data[i].timing, 2);
- platform_device_unregister(qdi_data[i].platform_dev);
- }
-}
-
-MODULE_AUTHOR("Alan Cox");
-MODULE_DESCRIPTION("low-level driver for qdi ATA");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRV_VERSION);
-
-module_init(qdi_init);
-module_exit(qdi_exit);
-
-module_param(probe_qdi, int, 0);
-
diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c
index 8574b31f1773..b2d3a2bb4e60 100644
--- a/drivers/ata/pata_radisys.c
+++ b/drivers/ata/pata_radisys.c
@@ -213,7 +213,6 @@ static struct ata_port_operations radisys_pata_ops = {
static int radisys_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
static const struct ata_port_info info = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
@@ -223,9 +222,7 @@ static int radisys_init_one (struct pci_dev *pdev, const struct pci_device_id *e
};
const struct ata_port_info *ppi[] = { &info, NULL };
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
return ata_pci_bmdma_init_one(pdev, ppi, &radisys_sht, NULL, 0);
}
diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c
index 5fbe9b166c69..e6a2dd7809c1 100644
--- a/drivers/ata/pata_rdc.c
+++ b/drivers/ata/pata_rdc.c
@@ -86,6 +86,8 @@ static int rdc_pata_prereset(struct ata_link *link, unsigned long deadline)
return ata_sff_prereset(link, deadline);
}
+static DEFINE_SPINLOCK(rdc_lock);
+
/**
* rdc_set_piomode - Initialize host controller PATA PIO timings
* @ap: Port whose timings we are configuring
@@ -101,6 +103,7 @@ static void rdc_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
unsigned int pio = adev->pio_mode - XFER_PIO_0;
struct pci_dev *dev = to_pci_dev(ap->host->dev);
+ unsigned long flags;
unsigned int is_slave = (adev->devno != 0);
unsigned int master_port= ap->port_no ? 0x42 : 0x40;
unsigned int slave_port = 0x44;
@@ -124,6 +127,8 @@ static void rdc_set_piomode(struct ata_port *ap, struct ata_device *adev)
if (adev->class == ATA_DEV_ATA)
control |= 4; /* PPE enable */
+ spin_lock_irqsave(&rdc_lock, flags);
+
/* PIO configuration clears DTE unconditionally. It will be
* programmed in set_dmamode which is guaranteed to be called
* after set_piomode if any DMA mode is available.
@@ -161,6 +166,8 @@ static void rdc_set_piomode(struct ata_port *ap, struct ata_device *adev)
pci_read_config_byte(dev, 0x48, &udma_enable);
udma_enable &= ~(1 << (2 * ap->port_no + adev->devno));
pci_write_config_byte(dev, 0x48, udma_enable);
+
+ spin_unlock_irqrestore(&rdc_lock, flags);
}
/**
@@ -177,6 +184,7 @@ static void rdc_set_piomode(struct ata_port *ap, struct ata_device *adev)
static void rdc_set_dmamode(struct ata_port *ap, struct ata_device *adev)
{
struct pci_dev *dev = to_pci_dev(ap->host->dev);
+ unsigned long flags;
u8 master_port = ap->port_no ? 0x42 : 0x40;
u16 master_data;
u8 speed = adev->dma_mode;
@@ -190,6 +198,8 @@ static void rdc_set_dmamode(struct ata_port *ap, struct ata_device *adev)
{ 2, 1 },
{ 2, 3 }, };
+ spin_lock_irqsave(&rdc_lock, flags);
+
pci_read_config_word(dev, master_port, &master_data);
pci_read_config_byte(dev, 0x48, &udma_enable);
@@ -271,6 +281,8 @@ static void rdc_set_dmamode(struct ata_port *ap, struct ata_device *adev)
pci_write_config_word(dev, master_port, master_data);
}
pci_write_config_byte(dev, 0x48, udma_enable);
+
+ spin_unlock_irqrestore(&rdc_lock, flags);
}
static struct ata_port_operations rdc_pata_ops = {
@@ -312,7 +324,6 @@ static struct scsi_host_template rdc_sht = {
static int __devinit rdc_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- static int printed_version;
struct device *dev = &pdev->dev;
struct ata_port_info port_info[2];
const struct ata_port_info *ppi[] = { &port_info[0], &port_info[1] };
@@ -321,9 +332,7 @@ static int __devinit rdc_init_one(struct pci_dev *pdev,
struct rdc_host_priv *hpriv;
int rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
port_info[0] = rdc_port_info;
port_info[1] = rdc_port_info;
@@ -378,6 +387,10 @@ static struct pci_driver rdc_pci_driver = {
.id_table = rdc_pci_tbl,
.probe = rdc_init_one,
.remove = rdc_remove_one,
+#ifdef CONFIG_PM
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
+#endif
};
diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c
index 4d04471794b6..aca321e1e6a2 100644
--- a/drivers/ata/pata_rz1000.c
+++ b/drivers/ata/pata_rz1000.c
@@ -44,7 +44,7 @@ static int rz1000_set_mode(struct ata_link *link, struct ata_device **unused)
dev->xfer_mode = XFER_PIO_0;
dev->xfer_shift = ATA_SHIFT_PIO;
dev->flags |= ATA_DFLAG_PIO;
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ ata_dev_info(dev, "configured for PIO\n");
}
return 0;
}
@@ -92,7 +92,7 @@ static int rz1000_init_one (struct pci_dev *pdev, const struct pci_device_id *en
};
const struct ata_port_info *ppi[] = { &info, NULL };
- printk_once(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
if (rz1000_fifo_disable(pdev) == 0)
return ata_pci_sff_init_one(pdev, ppi, &rz1000_sht, NULL, 0);
diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c
index c446ae6055a3..1b372c297195 100644
--- a/drivers/ata/pata_samsung_cf.c
+++ b/drivers/ata/pata_samsung_cf.c
@@ -376,7 +376,7 @@ static int pata_s3c_softreset(struct ata_link *link, unsigned int *classes,
rc = pata_s3c_bus_softreset(ap, deadline);
/* if link is occupied, -ENODEV too is an error */
if (rc && rc != -ENODEV) {
- ata_link_printk(link, KERN_ERR, "SRST failed (errno=%d)\n", rc);
+ ata_link_err(link, "SRST failed (errno=%d)\n", rc);
return rc;
}
diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c
index e2c18257adff..c0e603a84f7f 100644
--- a/drivers/ata/pata_sc1200.c
+++ b/drivers/ata/pata_sc1200.c
@@ -38,7 +38,7 @@
#include <scsi/scsi_host.h>
#include <linux/libata.h>
-#define DRV_NAME "sc1200"
+#define DRV_NAME "pata_sc1200"
#define DRV_VERSION "0.2.6"
#define SC1200_REV_A 0x00
@@ -86,10 +86,14 @@ static int sc1200_clock(void)
static void sc1200_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
static const u32 pio_timings[4][5] = {
- {0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010}, // format0 33Mhz
- {0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}, // format1, 33Mhz
- {0xfaa3f4f3, 0xc23232b2, 0x513101c1, 0x31213121, 0x10211021}, // format1, 48Mhz
- {0xfff4fff4, 0xf35353d3, 0x814102f1, 0x42314231, 0x11311131} // format1, 66Mhz
+ /* format0, 33Mhz */
+ { 0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010 },
+ /* format1, 33Mhz */
+ { 0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010 },
+ /* format1, 48Mhz */
+ { 0xfaa3f4f3, 0xc23232b2, 0x513101c1, 0x31213121, 0x10211021 },
+ /* format1, 66Mhz */
+ { 0xfff4fff4, 0xf35353d3, 0x814102f1, 0x42314231, 0x11311131 }
};
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c
index 88ea9b677b47..e265f835c95d 100644
--- a/drivers/ata/pata_scc.c
+++ b/drivers/ata/pata_scc.c
@@ -637,8 +637,7 @@ static int scc_softreset(struct ata_link *link, unsigned int *classes,
DPRINTK("about to softreset, devmask=%x\n", devmask);
err_mask = scc_bus_softreset(ap, devmask, deadline);
if (err_mask) {
- ata_port_printk(ap, KERN_ERR, "SRST failed (err_mask=0x%x)\n",
- err_mask);
+ ata_port_err(ap, "SRST failed (err_mask=0x%x)\n", err_mask);
return -EIO;
}
@@ -827,18 +826,6 @@ static unsigned int scc_data_xfer (struct ata_device *dev, unsigned char *buf,
}
/**
- * scc_pata_prereset - prepare for reset
- * @ap: ATA port to be reset
- * @deadline: deadline jiffies for the operation
- */
-
-static int scc_pata_prereset(struct ata_link *link, unsigned long deadline)
-{
- link->ap->cbl = ATA_CBL_PATA80;
- return ata_sff_prereset(link, deadline);
-}
-
-/**
* scc_postreset - standard postreset callback
* @ap: the target ata_port
* @classes: classes of attached devices
@@ -947,7 +934,7 @@ static struct ata_port_operations scc_pata_ops = {
.bmdma_status = scc_bmdma_status,
.sff_data_xfer = scc_data_xfer,
- .prereset = scc_pata_prereset,
+ .cable_detect = ata_cable_80wire,
.softreset = scc_softreset,
.postreset = scc_postreset,
@@ -1072,15 +1059,12 @@ static int scc_host_init(struct ata_host *host)
static int scc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
unsigned int board_idx = (unsigned int) ent->driver_data;
const struct ata_port_info *ppi[] = { &scc_port_info[board_idx], NULL };
struct ata_host *host;
int rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
host = ata_host_alloc_pinfo(&pdev->dev, ppi, 1);
if (!host)
diff --git a/drivers/ata/pata_sch.c b/drivers/ata/pata_sch.c
index e97b32f03a6e..7c78b9993627 100644
--- a/drivers/ata/pata_sch.c
+++ b/drivers/ata/pata_sch.c
@@ -172,12 +172,9 @@ static void sch_set_dmamode(struct ata_port *ap, struct ata_device *adev)
static int __devinit sch_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- static int printed_version;
const struct ata_port_info *ppi[] = { &sch_port_info, NULL };
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
return ata_pci_bmdma_init_one(pdev, ppi, &sch_sht, NULL, 0);
}
diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c
index 86dd714e3e1d..71eaf385e970 100644
--- a/drivers/ata/pata_serverworks.c
+++ b/drivers/ata/pata_serverworks.c
@@ -58,31 +58,15 @@ static const char *csb_bad_ata100[] = {
};
/**
- * dell_cable - Dell serverworks cable detection
+ * oem_cable - Dell/Sun serverworks cable detection
* @ap: ATA port to do cable detect
*
- * Dell hide the 40/80 pin select for their interfaces in the top two
- * bits of the subsystem ID.
+ * Dell PowerEdge and Sun Cobalt 'Alpine' hide the 40/80 pin select
+ * for their interfaces in the top two bits of the subsystem ID.
*/
-static int dell_cable(struct ata_port *ap) {
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-
- if (pdev->subsystem_device & (1 << (ap->port_no + 14)))
- return ATA_CBL_PATA80;
- return ATA_CBL_PATA40;
-}
-
-/**
- * sun_cable - Sun Cobalt 'Alpine' cable detection
- * @ap: ATA port to do cable select
- *
- * Cobalt CSB5 IDE hides the 40/80pin in the top two bits of the
- * subsystem ID the same as dell. We could use one function but we may
- * need to extend the Dell one in future
- */
-
-static int sun_cable(struct ata_port *ap) {
+static int oem_cable(struct ata_port *ap)
+{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
if (pdev->subsystem_device & (1 << (ap->port_no + 14)))
@@ -90,49 +74,21 @@ static int sun_cable(struct ata_port *ap) {
return ATA_CBL_PATA40;
}
-/**
- * osb4_cable - OSB4 cable detect
- * @ap: ATA port to check
- *
- * The OSB4 isn't UDMA66 capable so this is easy
- */
-
-static int osb4_cable(struct ata_port *ap) {
- return ATA_CBL_PATA40;
-}
-
-/**
- * csb_cable - CSB5/6 cable detect
- * @ap: ATA port to check
- *
- * Serverworks default arrangement is to use the drive side detection
- * only.
- */
-
-static int csb_cable(struct ata_port *ap) {
- return ATA_CBL_PATA_UNK;
-}
-
struct sv_cable_table {
int device;
int subvendor;
int (*cable_detect)(struct ata_port *ap);
};
-/*
- * Note that we don't copy the old serverworks code because the old
- * code contains obvious mistakes
- */
-
static struct sv_cable_table cable_detect[] = {
- { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_DELL, dell_cable },
- { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_VENDOR_ID_DELL, dell_cable },
- { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_SUN, sun_cable },
- { PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, osb4_cable },
- { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, csb_cable },
- { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, csb_cable },
- { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, csb_cable },
- { PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, csb_cable },
+ { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_DELL, oem_cable },
+ { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_VENDOR_ID_DELL, oem_cable },
+ { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_SUN, oem_cable },
+ { PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, ata_cable_40wire },
+ { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, ata_cable_unknown },
+ { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, ata_cable_unknown },
+ { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, ata_cable_unknown },
+ { PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, ata_cable_unknown },
{ }
};
@@ -393,6 +349,31 @@ static void serverworks_fixup_ht1000(struct pci_dev *pdev)
pci_write_config_byte(pdev, 0x5A, btr);
}
+static int serverworks_fixup(struct pci_dev *pdev)
+{
+ int rc = 0;
+
+ /* Force master latency timer to 64 PCI clocks */
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40);
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_SERVERWORKS_OSB4IDE:
+ rc = serverworks_fixup_osb4(pdev);
+ break;
+ case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE:
+ ata_pci_bmdma_clear_simplex(pdev);
+ /* fall through */
+ case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE:
+ case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2:
+ rc = serverworks_fixup_csb(pdev);
+ break;
+ case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE:
+ serverworks_fixup_ht1000(pdev);
+ break;
+ }
+
+ return rc;
+}
static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
@@ -430,13 +411,12 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id
if (rc)
return rc;
- /* Force master latency timer to 64 PCI clocks */
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40);
+ rc = serverworks_fixup(pdev);
/* OSB4 : South Bridge and IDE */
if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) {
/* Select non UDMA capable OSB4 if we can't do fixups */
- if ( serverworks_fixup_osb4(pdev) < 0)
+ if (rc < 0)
ppi[0] = &info[1];
}
/* setup CSB5/CSB6 : South Bridge and IDE option RAID */
@@ -446,19 +426,13 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id
/* If the returned btr is the newer revision then
select the right info block */
- if (serverworks_fixup_csb(pdev) == 3)
+ if (rc == 3)
ppi[0] = &info[3];
/* Is this the 3rd channel CSB6 IDE ? */
if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)
ppi[1] = &ata_dummy_port_info;
}
- /* setup HT1000E */
- else if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_HT1000IDE)
- serverworks_fixup_ht1000(pdev);
-
- if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE)
- ata_pci_bmdma_clear_simplex(pdev);
return ata_pci_bmdma_init_one(pdev, ppi, &serverworks_sht, NULL, 0);
}
@@ -473,24 +447,7 @@ static int serverworks_reinit_one(struct pci_dev *pdev)
if (rc)
return rc;
- /* Force master latency timer to 64 PCI clocks */
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40);
-
- switch (pdev->device) {
- case PCI_DEVICE_ID_SERVERWORKS_OSB4IDE:
- serverworks_fixup_osb4(pdev);
- break;
- case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE:
- ata_pci_bmdma_clear_simplex(pdev);
- /* fall through */
- case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE:
- case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2:
- serverworks_fixup_csb(pdev);
- break;
- case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE:
- serverworks_fixup_ht1000(pdev);
- break;
- }
+ (void)serverworks_fixup(pdev);
ata_host_resume(host);
return 0;
diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c
index 118787caa93f..b92eacf8dd3c 100644
--- a/drivers/ata/pata_sil680.c
+++ b/drivers/ata/pata_sil680.c
@@ -38,11 +38,12 @@
/**
* sil680_selreg - return register base
- * @hwif: interface
+ * @ap: ATA interface
* @r: config offset
*
- * Turn a config register offset into the right address in either
- * PCI space or MMIO space to access the control register in question
+ * Turn a config register offset into the right address in PCI space
+ * to access the control register in question.
+ *
* Thankfully this is a configuration operation so isn't performance
* criticial.
*/
@@ -56,12 +57,12 @@ static unsigned long sil680_selreg(struct ata_port *ap, int r)
/**
* sil680_seldev - return register base
- * @hwif: interface
+ * @ap: ATA interface
* @r: config offset
*
- * Turn a config register offset into the right address in either
- * PCI space or MMIO space to access the control register in question
- * including accounting for the unit shift.
+ * Turn a config register offset into the right address in PCI space
+ * to access the control register in question including accounting for
+ * the unit shift.
*/
static unsigned long sil680_seldev(struct ata_port *ap, struct ata_device *adev, int r)
@@ -81,7 +82,8 @@ static unsigned long sil680_seldev(struct ata_port *ap, struct ata_device *adev,
* space for us.
*/
-static int sil680_cable_detect(struct ata_port *ap) {
+static int sil680_cable_detect(struct ata_port *ap)
+{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
unsigned long addr = sil680_selreg(ap, 0);
u8 ata66;
@@ -93,7 +95,7 @@ static int sil680_cable_detect(struct ata_port *ap) {
}
/**
- * sil680_set_piomode - set initial PIO mode data
+ * sil680_set_piomode - set PIO mode data
* @ap: ATA interface
* @adev: ATA device
*
@@ -104,8 +106,12 @@ static int sil680_cable_detect(struct ata_port *ap) {
static void sil680_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
- static u16 speed_p[5] = { 0x328A, 0x2283, 0x1104, 0x10C3, 0x10C1 };
- static u16 speed_t[5] = { 0x328A, 0x2283, 0x1281, 0x10C3, 0x10C1 };
+ static const u16 speed_p[5] = {
+ 0x328A, 0x2283, 0x1104, 0x10C3, 0x10C1
+ };
+ static const u16 speed_t[5] = {
+ 0x328A, 0x2283, 0x1281, 0x10C3, 0x10C1
+ };
unsigned long tfaddr = sil680_selreg(ap, 0x02);
unsigned long addr = sil680_seldev(ap, adev, 0x04);
@@ -140,22 +146,23 @@ static void sil680_set_piomode(struct ata_port *ap, struct ata_device *adev)
}
/**
- * sil680_set_dmamode - set initial DMA mode data
+ * sil680_set_dmamode - set DMA mode data
* @ap: ATA interface
* @adev: ATA device
*
- * Program the MWDMA/UDMA modes for the sil680 k
- * chipset. The MWDMA mode values are pulled from a lookup table
+ * Program the MWDMA/UDMA modes for the sil680 chipset.
+ *
+ * The MWDMA mode values are pulled from a lookup table
* while the chipset uses mode number for UDMA.
*/
static void sil680_set_dmamode(struct ata_port *ap, struct ata_device *adev)
{
- static u8 ultra_table[2][7] = {
+ static const u8 ultra_table[2][7] = {
{ 0x0C, 0x07, 0x05, 0x04, 0x02, 0x01, 0xFF }, /* 100MHz */
{ 0x0F, 0x0B, 0x07, 0x05, 0x03, 0x02, 0x01 }, /* 133Mhz */
};
- static u16 dma_table[3] = { 0x2208, 0x10C2, 0x10C1 };
+ static const u16 dma_table[3] = { 0x2208, 0x10C2, 0x10C1 };
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
unsigned long ma = sil680_seldev(ap, adev, 0x08);
@@ -175,7 +182,7 @@ static void sil680_set_dmamode(struct ata_port *ap, struct ata_device *adev)
mode &= ~(0x03 << port_shift);
/* Extract scsc */
- scsc = (scsc & 0x30) ? 1: 0;
+ scsc = (scsc & 0x30) ? 1 : 0;
if (adev->dma_mode >= XFER_UDMA_0) {
multi = 0x10C1;
@@ -248,7 +255,7 @@ static u8 sil680_init_chip(struct pci_dev *pdev, int *try_mmio)
{
u8 tmpbyte = 0;
- /* FIXME: double check */
+ /* FIXME: double check */
pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
pdev->revision ? 1 : 255);
@@ -266,22 +273,22 @@ static u8 sil680_init_chip(struct pci_dev *pdev, int *try_mmio)
*try_mmio = (tmpbyte & 1) || pci_resource_start(pdev, 5);
#endif
- switch(tmpbyte & 0x30) {
- case 0x00:
- /* 133 clock attempt to force it on */
- pci_write_config_byte(pdev, 0x8A, tmpbyte|0x10);
- break;
- case 0x30:
- /* if clocking is disabled */
- /* 133 clock attempt to force it on */
- pci_write_config_byte(pdev, 0x8A, tmpbyte & ~0x20);
- break;
- case 0x10:
- /* 133 already */
- break;
- case 0x20:
- /* BIOS set PCI x2 clocking */
- break;
+ switch (tmpbyte & 0x30) {
+ case 0x00:
+ /* 133 clock attempt to force it on */
+ pci_write_config_byte(pdev, 0x8A, tmpbyte|0x10);
+ break;
+ case 0x30:
+ /* if clocking is disabled */
+ /* 133 clock attempt to force it on */
+ pci_write_config_byte(pdev, 0x8A, tmpbyte & ~0x20);
+ break;
+ case 0x10:
+ /* 133 already */
+ break;
+ case 0x20:
+ /* BIOS set PCI x2 clocking */
+ break;
}
pci_read_config_byte(pdev, 0x8A, &tmpbyte);
@@ -299,12 +306,19 @@ static u8 sil680_init_chip(struct pci_dev *pdev, int *try_mmio)
pci_write_config_dword(pdev, 0xB8, 0x43924392);
pci_write_config_dword(pdev, 0xBC, 0x40094009);
- switch(tmpbyte & 0x30) {
- case 0x00: printk(KERN_INFO "sil680: 100MHz clock.\n");break;
- case 0x10: printk(KERN_INFO "sil680: 133MHz clock.\n");break;
- case 0x20: printk(KERN_INFO "sil680: Using PCI clock.\n");break;
- /* This last case is _NOT_ ok */
- case 0x30: printk(KERN_ERR "sil680: Clock disabled ?\n");
+ switch (tmpbyte & 0x30) {
+ case 0x00:
+ printk(KERN_INFO "sil680: 100MHz clock.\n");
+ break;
+ case 0x10:
+ printk(KERN_INFO "sil680: 133MHz clock.\n");
+ break;
+ case 0x20:
+ printk(KERN_INFO "sil680: Using PCI clock.\n");
+ break;
+ /* This last case is _NOT_ ok */
+ case 0x30:
+ printk(KERN_ERR "sil680: Clock disabled ?\n");
}
return tmpbyte & 0x30;
}
@@ -327,13 +341,11 @@ static int __devinit sil680_init_one(struct pci_dev *pdev,
.port_ops = &sil680_port_ops
};
const struct ata_port_info *ppi[] = { &info, NULL };
- static int printed_version;
struct ata_host *host;
void __iomem *mmio_base;
int rc, try_mmio;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
index be08ff92db17..b0edc7de7b2d 100644
--- a/drivers/ata/pata_sis.c
+++ b/drivers/ata/pata_sis.c
@@ -55,7 +55,7 @@ static const struct sis_laptop sis_laptop[] = {
/* devid, subvendor, subdev */
{ 0x5513, 0x1043, 0x1107 }, /* ASUS A6K */
{ 0x5513, 0x1734, 0x105F }, /* FSC Amilo A1630 */
- { 0x5513, 0x1071, 0x8640 }, /* EasyNote K5305 */
+ { 0x5513, 0x1071, 0x8640 }, /* EasyNote K5305 */
/* end marker */
{ 0, }
};
@@ -76,7 +76,7 @@ static int sis_short_ata40(struct pci_dev *dev)
}
/**
- * sis_old_port_base - return PCI configuration base for dev
+ * sis_old_port_base - return PCI configuration base for dev
* @adev: device
*
* Returns the base of the PCI configuration registers for this port
@@ -85,11 +85,34 @@ static int sis_short_ata40(struct pci_dev *dev)
static int sis_old_port_base(struct ata_device *adev)
{
- return 0x40 + (4 * adev->link->ap->port_no) + (2 * adev->devno);
+ return 0x40 + (4 * adev->link->ap->port_no) + (2 * adev->devno);
}
/**
- * sis_133_cable_detect - check for 40/80 pin
+ * sis_port_base - return PCI configuration base for dev
+ * @adev: device
+ *
+ * Returns the base of the PCI configuration registers for this port
+ * number.
+ */
+
+static int sis_port_base(struct ata_device *adev)
+{
+ struct ata_port *ap = adev->link->ap;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ int port = 0x40;
+ u32 reg54;
+
+ /* If bit 30 is set then the registers are mapped at 0x70 not 0x40 */
+ pci_read_config_dword(pdev, 0x54, &reg54);
+ if (reg54 & 0x40000000)
+ port = 0x70;
+
+ return port + (8 * ap->port_no) + (4 * adev->devno);
+}
+
+/**
+ * sis_133_cable_detect - check for 40/80 pin
* @ap: Port
* @deadline: deadline jiffies for the operation
*
@@ -110,7 +133,7 @@ static int sis_133_cable_detect(struct ata_port *ap)
}
/**
- * sis_66_cable_detect - check for 40/80 pin
+ * sis_66_cable_detect - check for 40/80 pin
* @ap: Port
*
* Perform cable detection on the UDMA66, UDMA100 and early UDMA133
@@ -132,7 +155,7 @@ static int sis_66_cable_detect(struct ata_port *ap)
/**
- * sis_pre_reset - probe begin
+ * sis_pre_reset - probe begin
* @link: ATA link
* @deadline: deadline jiffies for the operation
*
@@ -160,7 +183,7 @@ static int sis_pre_reset(struct ata_link *link, unsigned long deadline)
/**
- * sis_set_fifo - Set RWP fifo bits for this device
+ * sis_set_fifo - Set RWP fifo bits for this device
* @ap: Port
* @adev: Device
*
@@ -203,13 +226,13 @@ static void sis_set_fifo(struct ata_port *ap, struct ata_device *adev)
static void sis_old_set_piomode (struct ata_port *ap, struct ata_device *adev)
{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
int port = sis_old_port_base(adev);
u8 t1, t2;
int speed = adev->pio_mode - XFER_PIO_0;
- const u8 active[] = { 0x00, 0x07, 0x04, 0x03, 0x01 };
- const u8 recovery[] = { 0x00, 0x06, 0x04, 0x03, 0x03 };
+ static const u8 active[] = { 0x00, 0x07, 0x04, 0x03, 0x01 };
+ static const u8 recovery[] = { 0x00, 0x06, 0x04, 0x03, 0x03 };
sis_set_fifo(ap, adev);
@@ -240,11 +263,11 @@ static void sis_old_set_piomode (struct ata_port *ap, struct ata_device *adev)
static void sis_100_set_piomode (struct ata_port *ap, struct ata_device *adev)
{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
int port = sis_old_port_base(adev);
int speed = adev->pio_mode - XFER_PIO_0;
- const u8 actrec[] = { 0x00, 0x67, 0x44, 0x33, 0x31 };
+ static const u8 actrec[] = { 0x00, 0x67, 0x44, 0x33, 0x31 };
sis_set_fifo(ap, adev);
@@ -265,20 +288,19 @@ static void sis_100_set_piomode (struct ata_port *ap, struct ata_device *adev)
static void sis_133_set_piomode (struct ata_port *ap, struct ata_device *adev)
{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- int port = 0x40;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ int port;
u32 t1;
- u32 reg54;
int speed = adev->pio_mode - XFER_PIO_0;
- const u32 timing133[] = {
+ static const u32 timing133[] = {
0x28269000, /* Recovery << 24 | Act << 16 | Ini << 12 */
0x0C266000,
0x04263000,
0x0C0A3000,
0x05093000
};
- const u32 timing100[] = {
+ static const u32 timing100[] = {
0x1E1C6000, /* Recovery << 24 | Act << 16 | Ini << 12 */
0x091C4000,
0x031C2000,
@@ -288,12 +310,7 @@ static void sis_133_set_piomode (struct ata_port *ap, struct ata_device *adev)
sis_set_fifo(ap, adev);
- /* If bit 14 is set then the registers are mapped at 0x70 not 0x40 */
- pci_read_config_dword(pdev, 0x54, &reg54);
- if (reg54 & 0x40000000)
- port = 0x70;
- port += 8 * ap->port_no + 4 * adev->devno;
-
+ port = sis_port_base(adev);
pci_read_config_dword(pdev, port, &t1);
t1 &= 0xC0C00FFF; /* Mask out timing */
@@ -319,13 +336,13 @@ static void sis_133_set_piomode (struct ata_port *ap, struct ata_device *adev)
static void sis_old_set_dmamode (struct ata_port *ap, struct ata_device *adev)
{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
int speed = adev->dma_mode - XFER_MW_DMA_0;
int drive_pci = sis_old_port_base(adev);
u16 timing;
- const u16 mwdma_bits[] = { 0x008, 0x302, 0x301 };
- const u16 udma_bits[] = { 0xE000, 0xC000, 0xA000 };
+ static const u16 mwdma_bits[] = { 0x008, 0x302, 0x301 };
+ static const u16 udma_bits[] = { 0xE000, 0xC000, 0xA000 };
pci_read_config_word(pdev, drive_pci, &timing);
@@ -358,14 +375,14 @@ static void sis_old_set_dmamode (struct ata_port *ap, struct ata_device *adev)
static void sis_66_set_dmamode (struct ata_port *ap, struct ata_device *adev)
{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
int speed = adev->dma_mode - XFER_MW_DMA_0;
int drive_pci = sis_old_port_base(adev);
u16 timing;
/* MWDMA 0-2 and UDMA 0-5 */
- const u16 mwdma_bits[] = { 0x008, 0x302, 0x301 };
- const u16 udma_bits[] = { 0xF000, 0xD000, 0xB000, 0xA000, 0x9000, 0x8000 };
+ static const u16 mwdma_bits[] = { 0x008, 0x302, 0x301 };
+ static const u16 udma_bits[] = { 0xF000, 0xD000, 0xB000, 0xA000, 0x9000, 0x8000 };
pci_read_config_word(pdev, drive_pci, &timing);
@@ -397,12 +414,12 @@ static void sis_66_set_dmamode (struct ata_port *ap, struct ata_device *adev)
static void sis_100_set_dmamode (struct ata_port *ap, struct ata_device *adev)
{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
int speed = adev->dma_mode - XFER_MW_DMA_0;
int drive_pci = sis_old_port_base(adev);
u8 timing;
- const u8 udma_bits[] = { 0x8B, 0x87, 0x85, 0x83, 0x82, 0x81};
+ static const u8 udma_bits[] = { 0x8B, 0x87, 0x85, 0x83, 0x82, 0x81};
pci_read_config_byte(pdev, drive_pci + 1, &timing);
@@ -431,7 +448,7 @@ static void sis_100_set_dmamode (struct ata_port *ap, struct ata_device *adev)
static void sis_133_early_set_dmamode (struct ata_port *ap, struct ata_device *adev)
{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
int speed = adev->dma_mode - XFER_MW_DMA_0;
int drive_pci = sis_old_port_base(adev);
u8 timing;
@@ -464,32 +481,34 @@ static void sis_133_early_set_dmamode (struct ata_port *ap, struct ata_device *a
static void sis_133_set_dmamode (struct ata_port *ap, struct ata_device *adev)
{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- int speed = adev->dma_mode - XFER_MW_DMA_0;
- int port = 0x40;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ int port;
u32 t1;
- u32 reg54;
-
- /* bits 4- cycle time 8 - cvs time */
- static const u32 timing_u100[] = { 0x6B0, 0x470, 0x350, 0x140, 0x120, 0x110, 0x000 };
- static const u32 timing_u133[] = { 0x9F0, 0x6A0, 0x470, 0x250, 0x230, 0x220, 0x210 };
-
- /* If bit 14 is set then the registers are mapped at 0x70 not 0x40 */
- pci_read_config_dword(pdev, 0x54, &reg54);
- if (reg54 & 0x40000000)
- port = 0x70;
- port += (8 * ap->port_no) + (4 * adev->devno);
+ port = sis_port_base(adev);
pci_read_config_dword(pdev, port, &t1);
if (adev->dma_mode < XFER_UDMA_0) {
+ /* Recovery << 24 | Act << 16 | Ini << 12, like PIO modes */
+ static const u32 timing_u100[] = { 0x19154000, 0x06072000, 0x04062000 };
+ static const u32 timing_u133[] = { 0x221C6000, 0x0C0A3000, 0x05093000 };
+ int speed = adev->dma_mode - XFER_MW_DMA_0;
+
+ t1 &= 0xC0C00FFF;
+ /* disable UDMA */
t1 &= ~0x00000004;
- /* FIXME: need data sheet to add MWDMA here. Also lacking on
- ide/pci driver */
+ if (t1 & 0x08)
+ t1 |= timing_u133[speed];
+ else
+ t1 |= timing_u100[speed];
} else {
- speed = adev->dma_mode - XFER_UDMA_0;
- /* if & 8 no UDMA133 - need info for ... */
+ /* bits 4- cycle time 8 - cvs time */
+ static const u32 timing_u100[] = { 0x6B0, 0x470, 0x350, 0x140, 0x120, 0x110, 0x000 };
+ static const u32 timing_u133[] = { 0x9F0, 0x6A0, 0x470, 0x250, 0x230, 0x220, 0x210 };
+ int speed = adev->dma_mode - XFER_UDMA_0;
+
t1 &= ~0x00000FF0;
+ /* enable UDMA */
t1 |= 0x00000004;
if (t1 & 0x08)
t1 |= timing_u133[speed];
@@ -499,6 +518,27 @@ static void sis_133_set_dmamode (struct ata_port *ap, struct ata_device *adev)
pci_write_config_dword(pdev, port, t1);
}
+/**
+ * sis_133_mode_filter - mode selection filter
+ * @adev: ATA device
+ *
+ * Block UDMA6 on devices that do not support it.
+ */
+
+static unsigned long sis_133_mode_filter(struct ata_device *adev, unsigned long mask)
+{
+ struct ata_port *ap = adev->link->ap;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ int port = sis_port_base(adev);
+ u32 t1;
+
+ pci_read_config_dword(pdev, port, &t1);
+ /* if ATA133 is disabled, mask it out */
+ if (!(t1 & 0x08))
+ mask &= ~(0xC0 << ATA_SHIFT_UDMA);
+ return mask;
+}
+
static struct scsi_host_template sis_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -520,6 +560,7 @@ static struct ata_port_operations sis_133_ops = {
.set_piomode = sis_133_set_piomode,
.set_dmamode = sis_133_set_dmamode,
.cable_detect = sis_133_cable_detect,
+ .mode_filter = sis_133_mode_filter,
};
static struct ata_port_operations sis_133_early_ops = {
@@ -588,7 +629,7 @@ static const struct ata_port_info sis_info100_early = {
static const struct ata_port_info sis_info133 = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
- /* No MWDMA */
+ .mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
.port_ops = &sis_133_ops,
};
@@ -669,7 +710,7 @@ static void sis_fixup(struct pci_dev *pdev, struct sis_chipset *sis)
* @pdev: PCI device to register
* @ent: Entry in sis_pci_tbl matching with @pdev
*
- * Called from kernel PCI layer. We probe for combined mode (sigh),
+ * Called from kernel PCI layer. We probe for combined mode (sigh),
* and then hand over control to libata, for it to do the rest.
*
* LOCKING:
@@ -681,7 +722,6 @@ static void sis_fixup(struct pci_dev *pdev, struct sis_chipset *sis)
static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
const struct ata_port_info *ppi[] = { NULL, NULL };
struct pci_dev *host = NULL;
struct sis_chipset *chipset = NULL;
@@ -735,9 +775,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
0x0, &sis_info100
};
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev,
- "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
@@ -772,17 +810,20 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
switch(trueid) {
case 0x5518: /* SIS 962/963 */
+ dev_info(&pdev->dev,
+ "SiS 962/963 MuTIOL IDE UDMA133 controller\n");
chipset = &sis133;
if ((idemisc & 0x40000000) == 0) {
pci_write_config_dword(pdev, 0x54, idemisc | 0x40000000);
- printk(KERN_INFO "SIS5513: Switching to 5513 register mapping\n");
+ dev_info(&pdev->dev,
+ "Switching to 5513 register mapping\n");
}
break;
case 0x0180: /* SIS 965/965L */
- chipset = &sis133;
+ chipset = &sis133;
break;
case 0x1180: /* SIS 966/966L */
- chipset = &sis133;
+ chipset = &sis133;
break;
}
}
diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c
index 7f5d020ed56c..24cf200dd1c9 100644
--- a/drivers/ata/pata_sl82c105.c
+++ b/drivers/ata/pata_sl82c105.c
@@ -1,6 +1,7 @@
/*
* pata_sl82c105.c - SL82C105 PATA for new ATA layer
* (C) 2005 Red Hat Inc
+ * (C) 2011 Bartlomiej Zolnierkiewicz
*
* Based in part on linux/drivers/ide/pci/sl82c105.c
* SL82C105/Winbond 553 IDE driver
@@ -289,6 +290,14 @@ static int sl82c105_bridge_revision(struct pci_dev *pdev)
return bridge->revision;
}
+static void sl82c105_fixup(struct pci_dev *pdev)
+{
+ u32 val;
+
+ pci_read_config_dword(pdev, 0x40, &val);
+ val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16;
+ pci_write_config_dword(pdev, 0x40, val);
+}
static int sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
@@ -306,7 +315,6 @@ static int sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id
/* for now use only the first port */
const struct ata_port_info *ppi[] = { &info_early,
NULL };
- u32 val;
int rev;
int rc;
@@ -317,19 +325,36 @@ static int sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id
rev = sl82c105_bridge_revision(dev);
if (rev == -1)
- dev_printk(KERN_WARNING, &dev->dev, "pata_sl82c105: Unable to find bridge, disabling DMA.\n");
+ dev_warn(&dev->dev,
+ "pata_sl82c105: Unable to find bridge, disabling DMA\n");
else if (rev <= 5)
- dev_printk(KERN_WARNING, &dev->dev, "pata_sl82c105: Early bridge revision, no DMA available.\n");
+ dev_warn(&dev->dev,
+ "pata_sl82c105: Early bridge revision, no DMA available\n");
else
ppi[0] = &info_dma;
- pci_read_config_dword(dev, 0x40, &val);
- val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16;
- pci_write_config_dword(dev, 0x40, val);
+ sl82c105_fixup(dev);
return ata_pci_bmdma_init_one(dev, ppi, &sl82c105_sht, NULL, 0);
}
+#ifdef CONFIG_PM
+static int sl82c105_reinit_one(struct pci_dev *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ int rc;
+
+ rc = ata_pci_device_do_resume(pdev);
+ if (rc)
+ return rc;
+
+ sl82c105_fixup(pdev);
+
+ ata_host_resume(host);
+ return 0;
+}
+#endif
+
static const struct pci_device_id sl82c105[] = {
{ PCI_VDEVICE(WINBOND, PCI_DEVICE_ID_WINBOND_82C105), },
@@ -340,7 +365,11 @@ static struct pci_driver sl82c105_pci_driver = {
.name = DRV_NAME,
.id_table = sl82c105,
.probe = sl82c105_init_one,
- .remove = ata_pci_remove_one
+ .remove = ata_pci_remove_one,
+#ifdef CONFIG_PM
+ .suspend = ata_pci_device_suspend,
+ .resume = sl82c105_reinit_one,
+#endif
};
static int __init sl82c105_init(void)
diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c
index b3e0c9432283..28da1c6becf1 100644
--- a/drivers/ata/pata_triflex.c
+++ b/drivers/ata/pata_triflex.c
@@ -196,10 +196,8 @@ static int triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id)
.port_ops = &triflex_port_ops
};
const struct ata_port_info *ppi[] = { &info, NULL };
- static int printed_version;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &dev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&dev->dev, DRV_VERSION);
return ata_pci_bmdma_init_one(dev, ppi, &triflex_sht, NULL, 0);
}
diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c
index ac8d7d97e408..255f336cd7ea 100644
--- a/drivers/ata/pata_via.c
+++ b/drivers/ata/pata_via.c
@@ -124,6 +124,17 @@ static const struct via_isa_bridge {
{ NULL }
};
+static const struct dmi_system_id no_atapi_dma_dmi_table[] = {
+ {
+ .ident = "AVERATEC 3200",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AVERATEC"),
+ DMI_MATCH(DMI_BOARD_NAME, "3200"),
+ },
+ },
+ { }
+};
+
struct via_port {
u8 cached_device;
};
@@ -350,11 +361,18 @@ static unsigned long via_mode_filter(struct ata_device *dev, unsigned long mask)
if (config->id == PCI_DEVICE_ID_VIA_82C586_0) {
ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
if (strcmp(model_num, "TS64GSSD25-M") == 0) {
- ata_dev_printk(dev, KERN_WARNING,
- "disabling UDMA mode due to reported lockups with this device.\n");
+ ata_dev_warn(dev,
+ "disabling UDMA mode due to reported lockups with this device\n");
mask &= ~ ATA_MASK_UDMA;
}
}
+
+ if (dev->class == ATA_DEV_ATAPI &&
+ dmi_check_system(no_atapi_dma_dmi_table)) {
+ ata_dev_warn(dev, "controller locks up on ATAPI DMA, forcing PIO\n");
+ mask &= ATA_MASK_PIO;
+ }
+
return mask;
}
@@ -491,6 +509,27 @@ static void via_config_fifo(struct pci_dev *pdev, unsigned int flags)
}
}
+static void via_fixup(struct pci_dev *pdev, const struct via_isa_bridge *config)
+{
+ u32 timing;
+
+ /* Initialise the FIFO for the enabled channels. */
+ via_config_fifo(pdev, config->flags);
+
+ if (config->udma_mask == ATA_UDMA4) {
+ /* The 66 MHz devices require we enable the clock */
+ pci_read_config_dword(pdev, 0x50, &timing);
+ timing |= 0x80008;
+ pci_write_config_dword(pdev, 0x50, timing);
+ }
+ if (config->flags & VIA_BAD_CLK66) {
+ /* Disable the 66MHz clock on problem devices */
+ pci_read_config_dword(pdev, 0x50, &timing);
+ timing &= ~0x80008;
+ pci_write_config_dword(pdev, 0x50, timing);
+ }
+}
+
/**
* via_init_one - discovery callback
* @pdev: PCI device
@@ -551,14 +590,11 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
const struct ata_port_info *ppi[] = { NULL, NULL };
struct pci_dev *isa;
const struct via_isa_bridge *config;
- static int printed_version;
u8 enable;
- u32 timing;
unsigned long flags = id->driver_data;
int rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
@@ -593,9 +629,6 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENODEV;
}
- /* Initialise the FIFO for the enabled channels. */
- via_config_fifo(pdev, config->flags);
-
/* Clock set up */
switch (config->udma_mask) {
case 0x00:
@@ -621,12 +654,7 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENODEV;
}
- if (config->flags & VIA_BAD_CLK66) {
- /* Disable the 66MHz clock on problem devices */
- pci_read_config_dword(pdev, 0x50, &timing);
- timing &= ~0x80008;
- pci_write_config_dword(pdev, 0x50, timing);
- }
+ via_fixup(pdev, config);
/* We have established the device type, now fire it up */
return ata_pci_bmdma_init_one(pdev, ppi, &via_sht, (void *)config, 0);
@@ -645,29 +673,14 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
static int via_reinit_one(struct pci_dev *pdev)
{
- u32 timing;
struct ata_host *host = dev_get_drvdata(&pdev->dev);
- const struct via_isa_bridge *config = host->private_data;
int rc;
rc = ata_pci_device_do_resume(pdev);
if (rc)
return rc;
- via_config_fifo(pdev, config->flags);
-
- if (config->udma_mask == ATA_UDMA4) {
- /* The 66 MHz devices require we enable the clock */
- pci_read_config_dword(pdev, 0x50, &timing);
- timing |= 0x80008;
- pci_write_config_dword(pdev, 0x50, timing);
- }
- if (config->flags & VIA_BAD_CLK66) {
- /* Disable the 66MHz clock on problem devices */
- pci_read_config_dword(pdev, 0x50, &timing);
- timing &= ~0x80008;
- pci_write_config_dword(pdev, 0x50, timing);
- }
+ via_fixup(pdev, host->private_data);
ata_host_resume(host);
return 0;
diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c
index 1111712b3d7d..04911d52f59d 100644
--- a/drivers/ata/pdc_adma.c
+++ b/drivers/ata/pdc_adma.c
@@ -596,14 +596,12 @@ static int adma_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base)
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit DMA enable failed\n");
+ dev_err(&pdev->dev, "32-bit DMA enable failed\n");
return rc;
}
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit consistent DMA enable failed\n");
+ dev_err(&pdev->dev, "32-bit consistent DMA enable failed\n");
return rc;
}
return 0;
@@ -612,15 +610,13 @@ static int adma_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base)
static int adma_ata_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- static int printed_version;
unsigned int board_idx = (unsigned int) ent->driver_data;
const struct ata_port_info *ppi[] = { &adma_port_info[board_idx], NULL };
struct ata_host *host;
void __iomem *mmio_base;
int rc, port_no;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* alloc host */
host = ata_host_alloc_pinfo(&pdev->dev, ppi, ADMA_PORTS);
diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c
index dc88a39e7db8..5c4237452f50 100644
--- a/drivers/ata/sata_dwc_460ex.c
+++ b/drivers/ata/sata_dwc_460ex.c
@@ -766,11 +766,15 @@ static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems,
static void dma_dwc_exit(struct sata_dwc_device *hsdev)
{
dev_dbg(host_pvt.dwc_dev, "%s:\n", __func__);
- if (host_pvt.sata_dma_regs)
+ if (host_pvt.sata_dma_regs) {
iounmap(host_pvt.sata_dma_regs);
+ host_pvt.sata_dma_regs = NULL;
+ }
- if (hsdev->irq_dma)
+ if (hsdev->irq_dma) {
free_irq(hsdev->irq_dma, hsdev);
+ hsdev->irq_dma = 0;
+ }
}
/*
@@ -1325,7 +1329,7 @@ static int sata_dwc_port_start(struct ata_port *ap)
dev_err(ap->dev, "%s: dma_alloc_coherent failed\n",
__func__);
err = -ENOMEM;
- goto CLEANUP;
+ goto CLEANUP_ALLOC;
}
}
@@ -1345,15 +1349,13 @@ static int sata_dwc_port_start(struct ata_port *ap)
/* Clear any error bits before libata starts issuing commands */
clear_serror();
ap->private_data = hsdevp;
+ dev_dbg(ap->dev, "%s: done\n", __func__);
+ return 0;
+CLEANUP_ALLOC:
+ kfree(hsdevp);
CLEANUP:
- if (err) {
- sata_dwc_port_stop(ap);
- dev_dbg(ap->dev, "%s: fail\n", __func__);
- } else {
- dev_dbg(ap->dev, "%s: done\n", __func__);
- }
-
+ dev_dbg(ap->dev, "%s: fail. ap->id = %d\n", __func__, ap->print_id);
return err;
}
@@ -1638,13 +1640,12 @@ static int sata_dwc_probe(struct platform_device *ofdev)
const struct ata_port_info *ppi[] = { &pi, NULL };
/* Allocate DWC SATA device */
- hsdev = kmalloc(sizeof(*hsdev), GFP_KERNEL);
+ hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL);
if (hsdev == NULL) {
dev_err(&ofdev->dev, "kmalloc failed for hsdev\n");
err = -ENOMEM;
- goto error_out;
+ goto error;
}
- memset(hsdev, 0, sizeof(*hsdev));
/* Ioremap SATA registers */
base = of_iomap(ofdev->dev.of_node, 0);
@@ -1652,7 +1653,7 @@ static int sata_dwc_probe(struct platform_device *ofdev)
dev_err(&ofdev->dev, "ioremap failed for SATA register"
" address\n");
err = -ENODEV;
- goto error_out;
+ goto error_kmalloc;
}
hsdev->reg_base = base;
dev_dbg(&ofdev->dev, "ioremap done for SATA register address\n");
@@ -1665,7 +1666,7 @@ static int sata_dwc_probe(struct platform_device *ofdev)
if (!host) {
dev_err(&ofdev->dev, "ata_host_alloc_pinfo failed\n");
err = -ENOMEM;
- goto error_out;
+ goto error_iomap;
}
host->private_data = hsdev;
@@ -1733,8 +1734,11 @@ error_out:
/* Free SATA DMA resources */
dma_dwc_exit(hsdev);
- if (base)
- iounmap(base);
+error_iomap:
+ iounmap(base);
+error_kmalloc:
+ kfree(hsdev);
+error:
return err;
}
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index 35a71d875d0e..78ae7b67b09e 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -346,12 +346,11 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc,
/* warn if each s/g element is not dword aligned */
if (sg_addr & 0x03)
- ata_port_printk(qc->ap, KERN_ERR,
- "s/g addr unaligned : 0x%llx\n",
- (unsigned long long)sg_addr);
+ ata_port_err(qc->ap, "s/g addr unaligned : 0x%llx\n",
+ (unsigned long long)sg_addr);
if (sg_len & 0x03)
- ata_port_printk(qc->ap, KERN_ERR,
- "s/g len unaligned : 0x%x\n", sg_len);
+ ata_port_err(qc->ap, "s/g len unaligned : 0x%x\n",
+ sg_len);
if (num_prde == (SATA_FSL_MAX_PRD_DIRECT - 1) &&
sg_next(sg) != NULL) {
@@ -661,8 +660,7 @@ static int sata_fsl_port_start(struct ata_port *ap)
sata_fsl_scr_write(&ap->link, SCR_CONTROL, temp);
sata_fsl_scr_read(&ap->link, SCR_CONTROL, &temp);
- dev_printk(KERN_WARNING, dev, "scr_control, speed limited to %x\n",
- temp);
+ dev_warn(dev, "scr_control, speed limited to %x\n", temp);
#endif
return 0;
@@ -740,8 +738,7 @@ try_offline_again:
1, 500);
if (temp & ONLINE) {
- ata_port_printk(ap, KERN_ERR,
- "Hardreset failed, not off-lined %d\n", i);
+ ata_port_err(ap, "Hardreset failed, not off-lined %d\n", i);
/*
* Try to offline controller atleast twice
@@ -777,8 +774,7 @@ try_offline_again:
temp = ata_wait_register(ap, hcr_base + HSTATUS, ONLINE, 0, 1, 500);
if (!(temp & ONLINE)) {
- ata_port_printk(ap, KERN_ERR,
- "Hardreset failed, not on-lined\n");
+ ata_port_err(ap, "Hardreset failed, not on-lined\n");
goto err;
}
@@ -794,9 +790,8 @@ try_offline_again:
temp = ata_wait_register(ap, hcr_base + HSTATUS, 0xFF, 0, 1, 500);
if ((!(temp & 0x10)) || ata_link_offline(link)) {
- ata_port_printk(ap, KERN_WARNING,
- "No Device OR PHYRDY change,Hstatus = 0x%x\n",
- ioread32(hcr_base + HSTATUS));
+ ata_port_warn(ap, "No Device OR PHYRDY change,Hstatus = 0x%x\n",
+ ioread32(hcr_base + HSTATUS));
*class = ATA_DEV_NONE;
return 0;
}
@@ -809,13 +804,12 @@ try_offline_again:
500, jiffies_to_msecs(deadline - start_jiffies));
if ((temp & 0xFF) != 0x18) {
- ata_port_printk(ap, KERN_WARNING, "No Signature Update\n");
+ ata_port_warn(ap, "No Signature Update\n");
*class = ATA_DEV_NONE;
goto do_followup_srst;
} else {
- ata_port_printk(ap, KERN_INFO,
- "Signature Update detected @ %d msecs\n",
- jiffies_to_msecs(jiffies - start_jiffies));
+ ata_port_info(ap, "Signature Update detected @ %d msecs\n",
+ jiffies_to_msecs(jiffies - start_jiffies));
*class = sata_fsl_dev_classify(ap);
return 0;
}
@@ -890,7 +884,7 @@ static int sata_fsl_softreset(struct ata_link *link, unsigned int *class,
temp = ata_wait_register(ap, CQ + hcr_base, 0x1, 0x1, 1, 5000);
if (temp & 0x1) {
- ata_port_printk(ap, KERN_WARNING, "ATA_SRST issue failed\n");
+ ata_port_warn(ap, "ATA_SRST issue failed\n");
DPRINTK("Softreset@5000,CQ=0x%x,CA=0x%x,CC=0x%x\n",
ioread32(CQ + hcr_base),
@@ -1202,8 +1196,7 @@ static irqreturn_t sata_fsl_interrupt(int irq, void *dev_instance)
if (ap) {
sata_fsl_host_intr(ap);
} else {
- dev_printk(KERN_WARNING, host->dev,
- "interrupt on disabled port 0\n");
+ dev_warn(host->dev, "interrupt on disabled port 0\n");
}
iowrite32(interrupt_enables, hcr_base + HSTATUS);
@@ -1317,8 +1310,7 @@ static int sata_fsl_probe(struct platform_device *ofdev)
struct ata_port_info pi = sata_fsl_port_info[0];
const struct ata_port_info *ppi[] = { &pi, NULL };
- dev_printk(KERN_INFO, &ofdev->dev,
- "Sata FSL Platform/CSB Driver init\n");
+ dev_info(&ofdev->dev, "Sata FSL Platform/CSB Driver init\n");
hcr_base = of_iomap(ofdev->dev.of_node, 0);
if (!hcr_base)
@@ -1347,7 +1339,7 @@ static int sata_fsl_probe(struct platform_device *ofdev)
irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
if (irq < 0) {
- dev_printk(KERN_ERR, &ofdev->dev, "invalid irq from platform\n");
+ dev_err(&ofdev->dev, "invalid irq from platform\n");
goto error_exit_with_cleanup;
}
host_priv->irq = irq;
@@ -1422,8 +1414,7 @@ static int sata_fsl_resume(struct platform_device *op)
ret = sata_fsl_init_controller(host);
if (ret) {
- dev_printk(KERN_ERR, &op->dev,
- "Error initialize hardware\n");
+ dev_err(&op->dev, "Error initializing hardware\n");
return ret;
}
diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c
index 83a44471b189..5c7d70c03bf0 100644
--- a/drivers/ata/sata_inic162x.c
+++ b/drivers/ata/sata_inic162x.c
@@ -396,9 +396,8 @@ static void inic_host_intr(struct ata_port *ap)
}
spurious:
- ata_port_printk(ap, KERN_WARNING, "unhandled interrupt: "
- "cmd=0x%x irq_stat=0x%x idma_stat=0x%x\n",
- qc ? qc->tf.command : 0xff, irq_stat, idma_stat);
+ ata_port_warn(ap, "unhandled interrupt: cmd=0x%x irq_stat=0x%x idma_stat=0x%x\n",
+ qc ? qc->tf.command : 0xff, irq_stat, idma_stat);
}
static irqreturn_t inic_interrupt(int irq, void *dev_instance)
@@ -619,8 +618,9 @@ static int inic_hardreset(struct ata_link *link, unsigned int *class,
rc = sata_link_resume(link, timing, deadline);
if (rc) {
- ata_link_printk(link, KERN_WARNING, "failed to resume "
- "link after reset (errno=%d)\n", rc);
+ ata_link_warn(link,
+ "failed to resume link after reset (errno=%d)\n",
+ rc);
return rc;
}
@@ -632,8 +632,9 @@ static int inic_hardreset(struct ata_link *link, unsigned int *class,
rc = ata_wait_after_reset(link, deadline, inic_check_ready);
/* link occupied, -ENODEV too is an error */
if (rc) {
- ata_link_printk(link, KERN_WARNING, "device not ready "
- "after hardreset (errno=%d)\n", rc);
+ ata_link_warn(link,
+ "device not ready after hardreset (errno=%d)\n",
+ rc);
return rc;
}
@@ -799,7 +800,6 @@ static int inic_pci_device_resume(struct pci_dev *pdev)
static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
const struct ata_port_info *ppi[] = { &inic_port_info, NULL };
struct ata_host *host;
struct inic_host_priv *hpriv;
@@ -807,8 +807,7 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
int mmio_bar;
int i, rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* alloc host */
host = ata_host_alloc_pinfo(&pdev->dev, ppi, NR_PORTS);
@@ -847,15 +846,13 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Set dma_mask. This devices doesn't support 64bit addressing. */
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit DMA enable failed\n");
+ dev_err(&pdev->dev, "32-bit DMA enable failed\n");
return rc;
}
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit consistent DMA enable failed\n");
+ dev_err(&pdev->dev, "32-bit consistent DMA enable failed\n");
return rc;
}
@@ -866,15 +863,13 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
*/
rc = pci_set_dma_max_seg_size(pdev, 65536 - 512);
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "failed to set the maximum segment size.\n");
+ dev_err(&pdev->dev, "failed to set the maximum segment size\n");
return rc;
}
rc = init_controller(hpriv->mmio_base, hpriv->cached_hctl);
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "failed to initialize controller\n");
+ dev_err(&pdev->dev, "failed to initialize controller\n");
return rc;
}
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index b52c0519ad0b..0b8b8b488ee8 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -1190,7 +1190,7 @@ static void mv_wait_for_edma_empty_idle(struct ata_port *ap)
break;
udelay(per_loop);
}
- /* ata_port_printk(ap, KERN_INFO, "%s: %u+ usecs\n", __func__, i); */
+ /* ata_port_info(ap, "%s: %u+ usecs\n", __func__, i); */
}
/**
@@ -1228,7 +1228,7 @@ static int mv_stop_edma(struct ata_port *ap)
pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN;
mv_wait_for_edma_empty_idle(ap);
if (mv_stop_edma_engine(port_mmio)) {
- ata_port_printk(ap, KERN_ERR, "Unable to stop eDMA\n");
+ ata_port_err(ap, "Unable to stop eDMA\n");
err = -EIO;
}
mv_edma_cfg(ap, 0, 0);
@@ -1382,7 +1382,7 @@ static void mv6_dev_config(struct ata_device *adev)
if (adev->flags & ATA_DFLAG_NCQ) {
if (sata_pmp_attached(adev->link->ap)) {
adev->flags &= ~ATA_DFLAG_NCQ;
- ata_dev_printk(adev, KERN_INFO,
+ ata_dev_info(adev,
"NCQ disabled for command-based switching\n");
}
}
@@ -2225,9 +2225,8 @@ static unsigned int mv_send_fis(struct ata_port *ap, u32 *fis, int nwords)
/* See if it worked */
if ((ifstat & 0x3000) != 0x1000) {
- ata_port_printk(ap, KERN_WARNING,
- "%s transmission error, ifstat=%08x\n",
- __func__, ifstat);
+ ata_port_warn(ap, "%s transmission error, ifstat=%08x\n",
+ __func__, ifstat);
return AC_ERR_OTHER;
}
return 0;
@@ -2342,9 +2341,9 @@ static unsigned int mv_qc_issue(struct ata_queued_cmd *qc)
*/
if (limit_warnings > 0 && (qc->nbytes / qc->sect_size) > 1) {
--limit_warnings;
- ata_link_printk(qc->dev->link, KERN_WARNING, DRV_NAME
- ": attempting PIO w/multiple DRQ: "
- "this may fail due to h/w errata\n");
+ ata_link_warn(qc->dev->link, DRV_NAME
+ ": attempting PIO w/multiple DRQ: "
+ "this may fail due to h/w errata\n");
}
/* drop through */
case ATA_PROT_NODATA:
@@ -2499,20 +2498,20 @@ static int mv_handle_fbs_ncq_dev_err(struct ata_port *ap)
}
failed_links = hweight16(new_map);
- ata_port_printk(ap, KERN_INFO, "%s: pmp_map=%04x qc_map=%04x "
- "failed_links=%d nr_active_links=%d\n",
- __func__, pp->delayed_eh_pmp_map,
- ap->qc_active, failed_links,
- ap->nr_active_links);
+ ata_port_info(ap,
+ "%s: pmp_map=%04x qc_map=%04x failed_links=%d nr_active_links=%d\n",
+ __func__, pp->delayed_eh_pmp_map,
+ ap->qc_active, failed_links,
+ ap->nr_active_links);
if (ap->nr_active_links <= failed_links && mv_req_q_empty(ap)) {
mv_process_crpb_entries(ap, pp);
mv_stop_edma(ap);
mv_eh_freeze(ap);
- ata_port_printk(ap, KERN_INFO, "%s: done\n", __func__);
+ ata_port_info(ap, "%s: done\n", __func__);
return 1; /* handled */
}
- ata_port_printk(ap, KERN_INFO, "%s: waiting\n", __func__);
+ ata_port_info(ap, "%s: waiting\n", __func__);
return 1; /* handled */
}
@@ -2554,9 +2553,8 @@ static int mv_handle_dev_err(struct ata_port *ap, u32 edma_err_cause)
* and we cannot handle it here.
*/
if (edma_err_cause & EDMA_ERR_SELF_DIS) {
- ata_port_printk(ap, KERN_WARNING,
- "%s: err_cause=0x%x pp_flags=0x%x\n",
- __func__, edma_err_cause, pp->pp_flags);
+ ata_port_warn(ap, "%s: err_cause=0x%x pp_flags=0x%x\n",
+ __func__, edma_err_cause, pp->pp_flags);
return 0; /* not handled */
}
return mv_handle_fbs_ncq_dev_err(ap);
@@ -2567,9 +2565,8 @@ static int mv_handle_dev_err(struct ata_port *ap, u32 edma_err_cause)
* and we cannot handle it here.
*/
if (!(edma_err_cause & EDMA_ERR_SELF_DIS)) {
- ata_port_printk(ap, KERN_WARNING,
- "%s: err_cause=0x%x pp_flags=0x%x\n",
- __func__, edma_err_cause, pp->pp_flags);
+ ata_port_warn(ap, "%s: err_cause=0x%x pp_flags=0x%x\n",
+ __func__, edma_err_cause, pp->pp_flags);
return 0; /* not handled */
}
return mv_handle_fbs_non_ncq_dev_err(ap);
@@ -2930,8 +2927,7 @@ static int mv_pci_error(struct ata_host *host, void __iomem *mmio)
err_cause = readl(mmio + hpriv->irq_cause_offset);
- dev_printk(KERN_ERR, host->dev, "PCI ERROR; PCI IRQ cause=0x%08x\n",
- err_cause);
+ dev_err(host->dev, "PCI ERROR; PCI IRQ cause=0x%08x\n", err_cause);
DPRINTK("All regs @ PCI error\n");
mv_dump_all_regs(mmio, -1, to_pci_dev(host->dev));
@@ -3760,8 +3756,8 @@ static int mv_chip_id(struct ata_host *host, unsigned int board_idx)
hp_flags |= MV_HP_ERRATA_50XXB2;
break;
default:
- dev_printk(KERN_WARNING, &pdev->dev,
- "Applying 50XXB2 workarounds to unknown rev\n");
+ dev_warn(&pdev->dev,
+ "Applying 50XXB2 workarounds to unknown rev\n");
hp_flags |= MV_HP_ERRATA_50XXB2;
break;
}
@@ -3780,8 +3776,8 @@ static int mv_chip_id(struct ata_host *host, unsigned int board_idx)
hp_flags |= MV_HP_ERRATA_50XXB2;
break;
default:
- dev_printk(KERN_WARNING, &pdev->dev,
- "Applying B2 workarounds to unknown rev\n");
+ dev_warn(&pdev->dev,
+ "Applying B2 workarounds to unknown rev\n");
hp_flags |= MV_HP_ERRATA_50XXB2;
break;
}
@@ -3801,8 +3797,8 @@ static int mv_chip_id(struct ata_host *host, unsigned int board_idx)
hp_flags |= MV_HP_ERRATA_60X1C0;
break;
default:
- dev_printk(KERN_WARNING, &pdev->dev,
- "Applying B2 workarounds to unknown rev\n");
+ dev_warn(&pdev->dev,
+ "Applying B2 workarounds to unknown rev\n");
hp_flags |= MV_HP_ERRATA_60X1B2;
break;
}
@@ -3851,8 +3847,8 @@ static int mv_chip_id(struct ata_host *host, unsigned int board_idx)
hp_flags |= MV_HP_ERRATA_60X1C0;
break;
default:
- dev_printk(KERN_WARNING, &pdev->dev,
- "Applying 60X1C0 workarounds to unknown rev\n");
+ dev_warn(&pdev->dev,
+ "Applying 60X1C0 workarounds to unknown rev\n");
hp_flags |= MV_HP_ERRATA_60X1C0;
break;
}
@@ -3867,8 +3863,7 @@ static int mv_chip_id(struct ata_host *host, unsigned int board_idx)
break;
default:
- dev_printk(KERN_ERR, host->dev,
- "BUG: invalid board index %u\n", board_idx);
+ dev_err(host->dev, "BUG: invalid board index %u\n", board_idx);
return 1;
}
@@ -4023,7 +4018,6 @@ static void mv_conf_mbus_windows(struct mv_host_priv *hpriv,
*/
static int mv_platform_probe(struct platform_device *pdev)
{
- static int printed_version;
const struct mv_sata_platform_data *mv_platform_data;
const struct ata_port_info *ppi[] =
{ &mv_port_info[chip_soc], NULL };
@@ -4032,8 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev)
struct resource *res;
int n_ports, rc;
- if (!printed_version++)
- dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/*
* Simple resource validation ..
@@ -4091,12 +4084,14 @@ static int mv_platform_probe(struct platform_device *pdev)
if (rc)
goto err;
- dev_printk(KERN_INFO, &pdev->dev,
- "slots %u ports %d\n", (unsigned)MV_MAX_Q_DEPTH,
- host->n_ports);
+ dev_info(&pdev->dev, "slots %u ports %d\n",
+ (unsigned)MV_MAX_Q_DEPTH, host->n_ports);
+
+ rc = ata_host_activate(host, platform_get_irq(pdev, 0), mv_interrupt,
+ IRQF_SHARED, &mv6_sht);
+ if (!rc)
+ return 0;
- return ata_host_activate(host, platform_get_irq(pdev, 0), mv_interrupt,
- IRQF_SHARED, &mv6_sht);
err:
#if defined(CONFIG_HAVE_CLK)
if (!IS_ERR(hpriv->clk)) {
@@ -4118,8 +4113,7 @@ err:
*/
static int __devexit mv_platform_remove(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
- struct ata_host *host = dev_get_drvdata(dev);
+ struct ata_host *host = platform_get_drvdata(pdev);
#if defined(CONFIG_HAVE_CLK)
struct mv_host_priv *hpriv = host->private_data;
#endif
@@ -4137,7 +4131,7 @@ static int __devexit mv_platform_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int mv_platform_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct ata_host *host = platform_get_drvdata(pdev);
if (host)
return ata_host_suspend(host, state);
else
@@ -4146,7 +4140,7 @@ static int mv_platform_suspend(struct platform_device *pdev, pm_message_t state)
static int mv_platform_resume(struct platform_device *pdev)
{
- struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct ata_host *host = platform_get_drvdata(pdev);
int ret;
if (host) {
@@ -4217,22 +4211,21 @@ static int pci_go_64(struct pci_dev *pdev)
if (rc) {
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "64-bit DMA enable failed\n");
+ dev_err(&pdev->dev,
+ "64-bit DMA enable failed\n");
return rc;
}
}
} else {
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit DMA enable failed\n");
+ dev_err(&pdev->dev, "32-bit DMA enable failed\n");
return rc;
}
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit consistent DMA enable failed\n");
+ dev_err(&pdev->dev,
+ "32-bit consistent DMA enable failed\n");
return rc;
}
}
@@ -4276,10 +4269,9 @@ static void mv_print_info(struct ata_host *host)
else
gen = "?";
- dev_printk(KERN_INFO, &pdev->dev,
- "Gen-%s %u slots %u ports %s mode IRQ via %s\n",
- gen, (unsigned)MV_MAX_Q_DEPTH, host->n_ports,
- scc_s, (MV_HP_FLAG_MSI & hpriv->hp_flags) ? "MSI" : "INTx");
+ dev_info(&pdev->dev, "Gen-%s %u slots %u ports %s mode IRQ via %s\n",
+ gen, (unsigned)MV_MAX_Q_DEPTH, host->n_ports,
+ scc_s, (MV_HP_FLAG_MSI & hpriv->hp_flags) ? "MSI" : "INTx");
}
/**
@@ -4293,15 +4285,13 @@ static void mv_print_info(struct ata_host *host)
static int mv_pci_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- static int printed_version;
unsigned int board_idx = (unsigned int)ent->driver_data;
const struct ata_port_info *ppi[] = { &mv_port_info[board_idx], NULL };
struct ata_host *host;
struct mv_host_priv *hpriv;
int n_ports, port, rc;
- if (!printed_version++)
- dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* allocate host */
n_ports = mv_get_hc_count(ppi[0]->flags) * MV_PORTS_PER_HC;
@@ -4365,7 +4355,7 @@ static int mv_pci_init_one(struct pci_dev *pdev,
#ifdef CONFIG_PM
static int mv_pci_device_resume(struct pci_dev *pdev)
{
- struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct ata_host *host = pci_get_drvdata(pdev);
int rc;
rc = ata_pci_device_do_resume(pdev);
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index f173ef3bfc10..e0bc9646a38e 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -620,9 +620,8 @@ static void nv_adma_register_mode(struct ata_port *ap)
count++;
}
if (count == 20)
- ata_port_printk(ap, KERN_WARNING,
- "timeout waiting for ADMA IDLE, stat=0x%hx\n",
- status);
+ ata_port_warn(ap, "timeout waiting for ADMA IDLE, stat=0x%hx\n",
+ status);
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
@@ -635,9 +634,9 @@ static void nv_adma_register_mode(struct ata_port *ap)
count++;
}
if (count == 20)
- ata_port_printk(ap, KERN_WARNING,
- "timeout waiting for ADMA LEGACY, stat=0x%hx\n",
- status);
+ ata_port_warn(ap,
+ "timeout waiting for ADMA LEGACY, stat=0x%hx\n",
+ status);
pp->flags |= NV_ADMA_PORT_REGISTER_MODE;
}
@@ -665,7 +664,7 @@ static void nv_adma_mode(struct ata_port *ap)
count++;
}
if (count == 20)
- ata_port_printk(ap, KERN_WARNING,
+ ata_port_warn(ap,
"timeout waiting for ADMA LEGACY clear and IDLE, stat=0x%hx\n",
status);
@@ -772,10 +771,10 @@ static int nv_adma_slave_config(struct scsi_device *sdev)
blk_queue_segment_boundary(sdev->request_queue, segment_boundary);
blk_queue_max_segments(sdev->request_queue, sg_tablesize);
- ata_port_printk(ap, KERN_INFO,
- "DMA mask 0x%llX, segment boundary 0x%lX, hw segs %hu\n",
- (unsigned long long)*ap->host->dev->dma_mask,
- segment_boundary, sg_tablesize);
+ ata_port_info(ap,
+ "DMA mask 0x%llX, segment boundary 0x%lX, hw segs %hu\n",
+ (unsigned long long)*ap->host->dev->dma_mask,
+ segment_boundary, sg_tablesize);
spin_unlock_irqrestore(ap->lock, flags);
@@ -1443,8 +1442,7 @@ static unsigned int nv_adma_qc_issue(struct ata_queued_cmd *qc)
existing commands. */
if (unlikely(qc->tf.protocol == ATA_PROT_NCQ &&
(qc->flags & ATA_QCFLAG_RESULT_TF))) {
- ata_dev_printk(qc->dev, KERN_ERR,
- "NCQ w/ RESULT_TF not allowed\n");
+ ata_dev_err(qc->dev, "NCQ w/ RESULT_TF not allowed\n");
return AC_ERR_SYSTEM;
}
@@ -1581,15 +1579,15 @@ static int nv_hardreset(struct ata_link *link, unsigned int *class,
int rc;
if (!(ehc->i.flags & ATA_EHI_QUIET))
- ata_link_printk(link, KERN_INFO, "nv: skipping "
- "hardreset on occupied port\n");
+ ata_link_info(link,
+ "nv: skipping hardreset on occupied port\n");
/* make sure the link is online */
rc = sata_link_resume(link, timing, deadline);
/* whine about phy resume failure but proceed */
if (rc && rc != -EOPNOTSUPP)
- ata_link_printk(link, KERN_WARNING, "failed to resume "
- "link (errno=%d)\n", rc);
+ ata_link_warn(link, "failed to resume link (errno=%d)\n",
+ rc);
}
/* device signature acquisition is unreliable */
@@ -1686,7 +1684,7 @@ static void nv_adma_error_handler(struct ata_port *ap)
u8 cpb_count = readb(mmio + NV_ADMA_CPB_COUNT);
u8 next_cpb_idx = readb(mmio + NV_ADMA_NEXT_CPB_IDX);
- ata_port_printk(ap, KERN_ERR,
+ ata_port_err(ap,
"EH in ADMA mode, notifier 0x%X "
"notifier_error 0x%X gen_ctl 0x%X status 0x%X "
"next cpb count 0x%X next cpb idx 0x%x\n",
@@ -1697,7 +1695,7 @@ static void nv_adma_error_handler(struct ata_port *ap)
struct nv_adma_cpb *cpb = &pp->cpb[i];
if ((ata_tag_valid(ap->link.active_tag) && i == ap->link.active_tag) ||
ap->link.sactive & (1 << i))
- ata_port_printk(ap, KERN_ERR,
+ ata_port_err(ap,
"CPB %d: ctl_flags 0x%x, resp_flags 0x%x\n",
i, cpb->ctl_flags, cpb->resp_flags);
}
@@ -1799,23 +1797,22 @@ static void nv_swncq_ncq_stop(struct ata_port *ap)
u32 sactive;
u32 done_mask;
- ata_port_printk(ap, KERN_ERR,
- "EH in SWNCQ mode,QC:qc_active 0x%X sactive 0x%X\n",
- ap->qc_active, ap->link.sactive);
- ata_port_printk(ap, KERN_ERR,
+ ata_port_err(ap, "EH in SWNCQ mode,QC:qc_active 0x%X sactive 0x%X\n",
+ ap->qc_active, ap->link.sactive);
+ ata_port_err(ap,
"SWNCQ:qc_active 0x%X defer_bits 0x%X last_issue_tag 0x%x\n "
"dhfis 0x%X dmafis 0x%X sdbfis 0x%X\n",
pp->qc_active, pp->defer_queue.defer_bits, pp->last_issue_tag,
pp->dhfis_bits, pp->dmafis_bits, pp->sdbfis_bits);
- ata_port_printk(ap, KERN_ERR, "ATA_REG 0x%X ERR_REG 0x%X\n",
- ap->ops->sff_check_status(ap),
- ioread8(ap->ioaddr.error_addr));
+ ata_port_err(ap, "ATA_REG 0x%X ERR_REG 0x%X\n",
+ ap->ops->sff_check_status(ap),
+ ioread8(ap->ioaddr.error_addr));
sactive = readl(pp->sactive_block);
done_mask = pp->qc_active ^ sactive;
- ata_port_printk(ap, KERN_ERR, "tag : dhfis dmafis sdbfis sacitve\n");
+ ata_port_err(ap, "tag : dhfis dmafis sdbfis sactive\n");
for (i = 0; i < ATA_MAX_QUEUE; i++) {
u8 err = 0;
if (pp->qc_active & (1 << i))
@@ -1825,13 +1822,13 @@ static void nv_swncq_ncq_stop(struct ata_port *ap)
else
continue;
- ata_port_printk(ap, KERN_ERR,
- "tag 0x%x: %01x %01x %01x %01x %s\n", i,
- (pp->dhfis_bits >> i) & 0x1,
- (pp->dmafis_bits >> i) & 0x1,
- (pp->sdbfis_bits >> i) & 0x1,
- (sactive >> i) & 0x1,
- (err ? "error! tag doesn't exit" : " "));
+ ata_port_err(ap,
+ "tag 0x%x: %01x %01x %01x %01x %s\n", i,
+ (pp->dhfis_bits >> i) & 0x1,
+ (pp->dmafis_bits >> i) & 0x1,
+ (pp->sdbfis_bits >> i) & 0x1,
+ (sactive >> i) & 0x1,
+ (err ? "error! tag doesn't exit" : " "));
}
nv_swncq_pp_reinit(ap);
@@ -1956,8 +1953,8 @@ static int nv_swncq_slave_config(struct scsi_device *sdev)
if (strncmp(model_num, "Maxtor", 6) == 0) {
ata_scsi_change_queue_depth(sdev, 1, SCSI_QDEPTH_DEFAULT);
- ata_dev_printk(dev, KERN_NOTICE,
- "Disabling SWNCQ mode (depth %x)\n", sdev->queue_depth);
+ ata_dev_notice(dev, "Disabling SWNCQ mode (depth %x)\n",
+ sdev->queue_depth);
}
return rc;
@@ -2356,7 +2353,6 @@ static irqreturn_t nv_swncq_interrupt(int irq, void *dev_instance)
static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
const struct ata_port_info *ppi[] = { NULL, NULL };
struct nv_pi_priv *ipriv;
struct ata_host *host;
@@ -2373,8 +2369,7 @@ static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (pci_resource_start(pdev, bar) == 0)
return -ENODEV;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
@@ -2382,10 +2377,10 @@ static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* determine type and allocate host */
if (type == CK804 && adma_enabled) {
- dev_printk(KERN_NOTICE, &pdev->dev, "Using ADMA mode\n");
+ dev_notice(&pdev->dev, "Using ADMA mode\n");
type = ADMA;
} else if (type == MCP5x && swncq_enabled) {
- dev_printk(KERN_NOTICE, &pdev->dev, "Using SWNCQ mode\n");
+ dev_notice(&pdev->dev, "Using SWNCQ mode\n");
type = SWNCQ;
}
@@ -2429,7 +2424,7 @@ static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
nv_swncq_host_init(host);
if (msi_enabled) {
- dev_printk(KERN_NOTICE, &pdev->dev, "Using MSI\n");
+ dev_notice(&pdev->dev, "Using MSI\n");
pci_enable_msi(pdev);
}
diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c
index a004b1e0ea6d..000fcc99e01d 100644
--- a/drivers/ata/sata_promise.c
+++ b/drivers/ata/sata_promise.c
@@ -1179,7 +1179,6 @@ static void pdc_host_init(struct ata_host *host)
static int pdc_ata_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- static int printed_version;
const struct ata_port_info *pi = &pdc_port_info[ent->driver_data];
const struct ata_port_info *ppi[PDC_MAX_PORTS];
struct ata_host *host;
@@ -1187,8 +1186,7 @@ static int pdc_ata_init_one(struct pci_dev *pdev,
int n_ports, i, rc;
int is_sataii_tx4;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* enable and acquire resources */
rc = pcim_enable_device(pdev);
@@ -1217,7 +1215,7 @@ static int pdc_ata_init_one(struct pci_dev *pdev,
host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
if (!host) {
- dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n");
+ dev_err(&pdev->dev, "failed to allocate host\n");
return -ENOMEM;
}
host->iomap = pcim_iomap_table(pdev);
diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c
index c5603265fa58..9d1a47bb21b3 100644
--- a/drivers/ata/sata_qstor.c
+++ b/drivers/ata/sata_qstor.c
@@ -563,21 +563,20 @@ static int qs_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base)
if (rc) {
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "64-bit DMA enable failed\n");
+ dev_err(&pdev->dev,
+ "64-bit DMA enable failed\n");
return rc;
}
}
} else {
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit DMA enable failed\n");
+ dev_err(&pdev->dev, "32-bit DMA enable failed\n");
return rc;
}
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
+ dev_err(&pdev->dev,
"32-bit consistent DMA enable failed\n");
return rc;
}
@@ -588,14 +587,12 @@ static int qs_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base)
static int qs_ata_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- static int printed_version;
unsigned int board_idx = (unsigned int) ent->driver_data;
const struct ata_port_info *ppi[] = { &qs_port_info[board_idx], NULL };
struct ata_host *host;
int rc, port_no;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* alloc host */
host = ata_host_alloc_pinfo(&pdev->dev, ppi, QS_PORTS);
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index b42edaaf3a53..9dfb40b8c2c9 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -438,7 +438,7 @@ static void sil_host_intr(struct ata_port *ap, u32 bmdma2)
u8 status;
if (unlikely(bmdma2 & SIL_DMA_SATA_IRQ)) {
- u32 serror;
+ u32 serror = 0xffffffff;
/* SIEN doesn't mask SATA IRQs on some 3112s. Those
* controllers continue to assert IRQ as long as
@@ -643,8 +643,8 @@ static void sil_dev_config(struct ata_device *dev)
((ap->flags & SIL_FLAG_MOD15WRITE) &&
(quirks & SIL_QUIRK_MOD15WRITE))) {
if (print_info)
- ata_dev_printk(dev, KERN_INFO, "applying Seagate "
- "errata fix (mod15write workaround)\n");
+ ata_dev_info(dev,
+ "applying Seagate errata fix (mod15write workaround)\n");
dev->max_sectors = 15;
return;
}
@@ -652,8 +652,8 @@ static void sil_dev_config(struct ata_device *dev)
/* limit to udma5 */
if (quirks & SIL_QUIRK_UDMA5MAX) {
if (print_info)
- ata_dev_printk(dev, KERN_INFO, "applying Maxtor "
- "errata fix %s\n", model_num);
+ ata_dev_info(dev, "applying Maxtor errata fix %s\n",
+ model_num);
dev->udma_mask &= ATA_UDMA5;
return;
}
@@ -676,8 +676,8 @@ static void sil_init_controller(struct ata_host *host)
writew(cls << 8 | cls,
mmio_base + sil_port[i].fifo_cfg);
} else
- dev_printk(KERN_WARNING, &pdev->dev,
- "cache line size not set. Driver may not function\n");
+ dev_warn(&pdev->dev,
+ "cache line size not set. Driver may not function\n");
/* Apply R_ERR on DMA activate FIS errata workaround */
if (host->ports[0]->flags & SIL_FLAG_RERR_ON_DMA_ACT) {
@@ -688,9 +688,8 @@ static void sil_init_controller(struct ata_host *host)
if ((tmp & 0x3) != 0x01)
continue;
if (!cnt)
- dev_printk(KERN_INFO, &pdev->dev,
- "Applying R_ERR on DMA activate "
- "FIS errata fix\n");
+ dev_info(&pdev->dev,
+ "Applying R_ERR on DMA activate FIS errata fix\n");
writel(tmp & ~0x3, mmio_base + sil_port[i].sfis_cfg);
cnt++;
}
@@ -733,7 +732,6 @@ static bool sil_broken_system_poweroff(struct pci_dev *pdev)
static int sil_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
int board_id = ent->driver_data;
struct ata_port_info pi = sil_port_info[board_id];
const struct ata_port_info *ppi[] = { &pi, NULL };
@@ -742,8 +740,7 @@ static int sil_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
int n_ports, rc;
unsigned int i;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* allocate host */
n_ports = 2;
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index 06c564e55051..1e9140626a83 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -268,7 +268,7 @@ union sil24_cmd_block {
struct sil24_atapi_block atapi;
};
-static struct sil24_cerr_info {
+static const struct sil24_cerr_info {
unsigned int err_mask, action;
const char *desc;
} sil24_cerr_db[] = {
@@ -694,7 +694,7 @@ static int sil24_softreset(struct ata_link *link, unsigned int *class,
return 0;
err:
- ata_link_printk(link, KERN_ERR, "softreset failed (%s)\n", reason);
+ ata_link_err(link, "softreset failed (%s)\n", reason);
return -EIO;
}
@@ -714,8 +714,8 @@ static int sil24_hardreset(struct ata_link *link, unsigned int *class,
* This happens often after PM DMA CS errata.
*/
if (pp->do_port_rst) {
- ata_port_printk(ap, KERN_WARNING, "controller in dubious "
- "state, performing PORT_RST\n");
+ ata_port_warn(ap,
+ "controller in dubious state, performing PORT_RST\n");
writel(PORT_CS_PORT_RST, port + PORT_CTRL_STAT);
ata_msleep(ap, 10);
@@ -773,7 +773,7 @@ static int sil24_hardreset(struct ata_link *link, unsigned int *class,
goto retry;
}
- ata_link_printk(link, KERN_ERR, "hardreset failed (%s)\n", reason);
+ ata_link_err(link, "hardreset failed (%s)\n", reason);
return -EIO;
}
@@ -925,7 +925,7 @@ static void sil24_pmp_attach(struct ata_port *ap)
if (sata_pmp_gscr_vendor(gscr) == 0x11ab &&
sata_pmp_gscr_devid(gscr) == 0x4140) {
- ata_port_printk(ap, KERN_INFO,
+ ata_port_info(ap,
"disabling NCQ support due to sil24-mv4140 quirk\n");
ap->flags &= ~ATA_FLAG_NCQ;
}
@@ -946,8 +946,7 @@ static int sil24_pmp_hardreset(struct ata_link *link, unsigned int *class,
rc = sil24_init_port(link->ap);
if (rc) {
- ata_link_printk(link, KERN_ERR,
- "hardreset failed (port not ready)\n");
+ ata_link_err(link, "hardreset failed (port not ready)\n");
return rc;
}
@@ -1020,7 +1019,7 @@ static void sil24_error_intr(struct ata_port *ap)
/* deal with command error */
if (irq_stat & PORT_IRQ_ERROR) {
- struct sil24_cerr_info *ci = NULL;
+ const struct sil24_cerr_info *ci = NULL;
unsigned int err_mask = 0, action = 0;
u32 context, cerr;
int pmp;
@@ -1141,8 +1140,8 @@ static inline void sil24_host_intr(struct ata_port *ap)
/* spurious interrupts are expected if PCIX_IRQ_WOC */
if (!(ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) && ata_ratelimit())
- ata_port_printk(ap, KERN_INFO, "spurious interrupt "
- "(slot_stat 0x%x active_tag %d sactive 0x%x)\n",
+ ata_port_info(ap,
+ "spurious interrupt (slot_stat 0x%x active_tag %d sactive 0x%x)\n",
slot_stat, ap->link.active_tag, ap->link.sactive);
}
@@ -1256,8 +1255,8 @@ static void sil24_init_controller(struct ata_host *host)
PORT_CS_PORT_RST,
PORT_CS_PORT_RST, 10, 100);
if (tmp & PORT_CS_PORT_RST)
- dev_printk(KERN_ERR, host->dev,
- "failed to clear port RST\n");
+ dev_err(host->dev,
+ "failed to clear port RST\n");
}
/* configure port */
@@ -1271,7 +1270,6 @@ static void sil24_init_controller(struct ata_host *host)
static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
extern int __MARKER__sil24_cmd_block_is_sized_wrongly;
- static int printed_version;
struct ata_port_info pi = sil24_port_info[ent->driver_data];
const struct ata_port_info *ppi[] = { &pi, NULL };
void __iomem * const *iomap;
@@ -1283,8 +1281,7 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (sizeof(union sil24_cmd_block) != PAGE_SIZE)
__MARKER__sil24_cmd_block_is_sized_wrongly = 1;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* acquire resources */
rc = pcim_enable_device(pdev);
@@ -1302,9 +1299,8 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (pi.flags & SIL24_FLAG_PCIX_IRQ_WOC) {
tmp = readl(iomap[SIL24_HOST_BAR] + HOST_CTRL);
if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL))
- dev_printk(KERN_INFO, &pdev->dev,
- "Applying completion IRQ loss on PCI-X "
- "errata fix\n");
+ dev_info(&pdev->dev,
+ "Applying completion IRQ loss on PCI-X errata fix\n");
else
pi.flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
}
@@ -1322,22 +1318,21 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc) {
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "64-bit DMA enable failed\n");
+ dev_err(&pdev->dev,
+ "64-bit DMA enable failed\n");
return rc;
}
}
} else {
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit DMA enable failed\n");
+ dev_err(&pdev->dev, "32-bit DMA enable failed\n");
return rc;
}
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit consistent DMA enable failed\n");
+ dev_err(&pdev->dev,
+ "32-bit consistent DMA enable failed\n");
return rc;
}
}
@@ -1350,7 +1345,7 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
sil24_init_controller(host);
if (sata_sil24_msi && !pci_enable_msi(pdev)) {
- dev_printk(KERN_INFO, &pdev->dev, "Using MSI\n");
+ dev_info(&pdev->dev, "Using MSI\n");
pci_intx(pdev, 0);
}
diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c
index cdcc13e9cf51..95ec435f0eb4 100644
--- a/drivers/ata/sata_sis.c
+++ b/drivers/ata/sata_sis.c
@@ -104,7 +104,7 @@ static const struct ata_port_info sis_port_info = {
};
MODULE_AUTHOR("Uwe Koziolek");
-MODULE_DESCRIPTION("low-level driver for Silicon Integratad Systems SATA controller");
+MODULE_DESCRIPTION("low-level driver for Silicon Integrated Systems SATA controller");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, sis_pci_tbl);
MODULE_VERSION(DRV_VERSION);
@@ -193,7 +193,6 @@ static int sis_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
struct ata_port_info pi = sis_port_info;
const struct ata_port_info *ppi[] = { &pi, &pi };
struct ata_host *host;
@@ -202,8 +201,7 @@ static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
u8 port2_start = 0x20;
int i, rc;
- if (!printed_version++)
- dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
@@ -241,12 +239,12 @@ static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
break;
}
if ((pmr & SIS_PMR_COMBINED) == 0) {
- dev_printk(KERN_INFO, &pdev->dev,
- "Detected SiS 180/181/964 chipset in SATA mode\n");
+ dev_info(&pdev->dev,
+ "Detected SiS 180/181/964 chipset in SATA mode\n");
port2_start = 64;
} else {
- dev_printk(KERN_INFO, &pdev->dev,
- "Detected SiS 180/181 chipset in combined mode\n");
+ dev_info(&pdev->dev,
+ "Detected SiS 180/181 chipset in combined mode\n");
port2_start = 0;
pi.flags |= ATA_FLAG_SLAVE_POSS;
}
@@ -256,24 +254,22 @@ static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
case 0x0183:
pci_read_config_dword(pdev, 0x6C, &val);
if (val & (1L << 31)) {
- dev_printk(KERN_INFO, &pdev->dev,
- "Detected SiS 182/965 chipset\n");
+ dev_info(&pdev->dev, "Detected SiS 182/965 chipset\n");
pi.flags |= ATA_FLAG_SLAVE_POSS;
} else {
- dev_printk(KERN_INFO, &pdev->dev,
- "Detected SiS 182/965L chipset\n");
+ dev_info(&pdev->dev, "Detected SiS 182/965L chipset\n");
}
break;
case 0x1182:
- dev_printk(KERN_INFO, &pdev->dev,
- "Detected SiS 1182/966/680 SATA controller\n");
+ dev_info(&pdev->dev,
+ "Detected SiS 1182/966/680 SATA controller\n");
pi.flags |= ATA_FLAG_SLAVE_POSS;
break;
case 0x1183:
- dev_printk(KERN_INFO, &pdev->dev,
- "Detected SiS 1183/966/966L/968/680 controller in PATA mode\n");
+ dev_info(&pdev->dev,
+ "Detected SiS 1183/966/966L/968/680 controller in PATA mode\n");
ppi[0] = &sis_info133_for_sata;
ppi[1] = &sis_info133_for_sata;
break;
diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c
index 35eabcf34568..c646118943ff 100644
--- a/drivers/ata/sata_svw.c
+++ b/drivers/ata/sata_svw.c
@@ -414,15 +414,13 @@ static void k2_sata_setup_port(struct ata_ioports *port, void __iomem *base)
static int k2_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
const struct ata_port_info *ppi[] =
{ &k2_port_info[ent->driver_data], NULL };
struct ata_host *host;
void __iomem *mmio_base;
int n_ports, i, rc, bar_pos;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* allocate host */
n_ports = 4;
diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c
index 8fd3b7252bda..cdaebbe3d184 100644
--- a/drivers/ata/sata_sx4.c
+++ b/drivers/ata/sata_sx4.c
@@ -1440,15 +1440,13 @@ static void pdc_20621_init(struct ata_host *host)
static int pdc_sata_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- static int printed_version;
const struct ata_port_info *ppi[] =
{ &pdc_port_info[ent->driver_data], NULL };
struct ata_host *host;
struct pdc_host_priv *hpriv;
int i, rc;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* allocate host */
host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4);
diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c
index 235be717a713..b54ebfcdda32 100644
--- a/drivers/ata/sata_uli.c
+++ b/drivers/ata/sata_uli.c
@@ -145,7 +145,6 @@ static int uli_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
const struct ata_port_info *ppi[] = { &uli_port_info, NULL };
unsigned int board_idx = (unsigned int) ent->driver_data;
struct ata_host *host;
@@ -154,8 +153,7 @@ static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
struct ata_ioports *ioaddr;
int n_ports, rc;
- if (!printed_version++)
- dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c
index 54434db15b12..f93e43b0ccd8 100644
--- a/drivers/ata/sata_via.c
+++ b/drivers/ata/sata_via.c
@@ -360,9 +360,9 @@ static int vt6420_prereset(struct ata_link *link, unsigned long deadline)
online = (sstatus & 0xf) == 0x3;
- ata_port_printk(ap, KERN_INFO,
- "SATA link %s 1.5 Gbps (SStatus %X SControl %X)\n",
- online ? "up" : "down", sstatus, scontrol);
+ ata_port_info(ap,
+ "SATA link %s 1.5 Gbps (SStatus %X SControl %X)\n",
+ online ? "up" : "down", sstatus, scontrol);
/* SStatus is read one more time */
svia_scr_read(link, SCR_STATUS, &sstatus);
@@ -469,7 +469,7 @@ static int vt6420_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
rc = pcim_iomap_regions(pdev, 1 << 5, DRV_NAME);
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev, "failed to iomap PCI BAR 5\n");
+ dev_err(&pdev->dev, "failed to iomap PCI BAR 5\n");
return rc;
}
@@ -488,14 +488,14 @@ static int vt6421_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
*r_host = host = ata_host_alloc_pinfo(&pdev->dev, ppi, ARRAY_SIZE(ppi));
if (!host) {
- dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n");
+ dev_err(&pdev->dev, "failed to allocate host\n");
return -ENOMEM;
}
rc = pcim_iomap_regions(pdev, 0x3f, DRV_NAME);
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev, "failed to request/iomap "
- "PCI BARs (errno=%d)\n", rc);
+ dev_err(&pdev->dev, "failed to request/iomap PCI BARs (errno=%d)\n",
+ rc);
return rc;
}
host->iomap = pcim_iomap_table(pdev);
@@ -526,7 +526,7 @@ static int vt8251_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
rc = pcim_iomap_regions(pdev, 1 << 5, DRV_NAME);
if (rc) {
- dev_printk(KERN_ERR, &pdev->dev, "failed to iomap PCI BAR 5\n");
+ dev_err(&pdev->dev, "failed to iomap PCI BAR 5\n");
return rc;
}
@@ -542,15 +542,14 @@ static void svia_configure(struct pci_dev *pdev, int board_id)
u8 tmp8;
pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8);
- dev_printk(KERN_INFO, &pdev->dev, "routed to hard irq line %d\n",
- (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f);
+ dev_info(&pdev->dev, "routed to hard irq line %d\n",
+ (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f);
/* make sure SATA channels are enabled */
pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8);
if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
- dev_printk(KERN_DEBUG, &pdev->dev,
- "enabling SATA channels (0x%x)\n",
- (int) tmp8);
+ dev_dbg(&pdev->dev, "enabling SATA channels (0x%x)\n",
+ (int)tmp8);
tmp8 |= ALL_PORTS;
pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8);
}
@@ -558,9 +557,8 @@ static void svia_configure(struct pci_dev *pdev, int board_id)
/* make sure interrupts for each channel sent to us */
pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8);
if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
- dev_printk(KERN_DEBUG, &pdev->dev,
- "enabling SATA channel interrupts (0x%x)\n",
- (int) tmp8);
+ dev_dbg(&pdev->dev, "enabling SATA channel interrupts (0x%x)\n",
+ (int) tmp8);
tmp8 |= ALL_PORTS;
pci_write_config_byte(pdev, SATA_INT_GATE, tmp8);
}
@@ -568,9 +566,9 @@ static void svia_configure(struct pci_dev *pdev, int board_id)
/* make sure native mode is enabled */
pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8);
if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) {
- dev_printk(KERN_DEBUG, &pdev->dev,
- "enabling SATA channel native mode (0x%x)\n",
- (int) tmp8);
+ dev_dbg(&pdev->dev,
+ "enabling SATA channel native mode (0x%x)\n",
+ (int) tmp8);
tmp8 |= NATIVE_MODE_ALL;
pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8);
}
@@ -606,15 +604,13 @@ static void svia_configure(struct pci_dev *pdev, int board_id)
static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static int printed_version;
unsigned int i;
int rc;
struct ata_host *host = NULL;
int board_id = (int) ent->driver_data;
const unsigned *bar_sizes;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
rc = pcim_enable_device(pdev);
if (rc)
@@ -628,7 +624,7 @@ static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
for (i = 0; i < ARRAY_SIZE(svia_bar_sizes); i++)
if ((pci_resource_start(pdev, i) == 0) ||
(pci_resource_len(pdev, i) < bar_sizes[i])) {
- dev_printk(KERN_ERR, &pdev->dev,
+ dev_err(&pdev->dev,
"invalid PCI BAR %u (sz 0x%llx, val 0x%llx)\n",
i,
(unsigned long long)pci_resource_start(pdev, i),
diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c
index 7c987371136e..6135a5288695 100644
--- a/drivers/ata/sata_vsc.c
+++ b/drivers/ata/sata_vsc.c
@@ -273,9 +273,8 @@ static irqreturn_t vsc_sata_interrupt(int irq, void *dev_instance)
if (unlikely(status == 0xffffffff || status == 0)) {
if (status)
- dev_printk(KERN_ERR, host->dev,
- ": IRQ status == 0xffffffff, "
- "PCI fault or device removal?\n");
+ dev_err(host->dev,
+ ": IRQ status == 0xffffffff, PCI fault or device removal?\n");
goto out;
}
@@ -347,14 +346,12 @@ static int __devinit vsc_sata_init_one(struct pci_dev *pdev,
.port_ops = &vsc_sata_ops,
};
const struct ata_port_info *ppi[] = { &pi, NULL };
- static int printed_version;
struct ata_host *host;
void __iomem *mmio_base;
int i, rc;
u8 cls;
- if (!printed_version++)
- dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ ata_print_version_once(&pdev->dev, DRV_VERSION);
/* allocate host */
host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4);
diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c
index a5fcb1eb862f..f8f41e0e8a8c 100644
--- a/drivers/atm/ambassador.c
+++ b/drivers/atm/ambassador.c
@@ -38,7 +38,7 @@
#include <linux/ihex.h>
#include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/io.h>
#include <asm/byteorder.h>
@@ -813,7 +813,7 @@ static void fill_rx_pool (amb_dev * dev, unsigned char pool,
return;
}
-// top up all RX pools (can also be called as a bottom half)
+// top up all RX pools
static void fill_rx_pools (amb_dev * dev) {
unsigned char pool;
@@ -872,11 +872,7 @@ static irqreturn_t interrupt_handler(int irq, void *dev_id) {
++irq_work;
if (irq_work) {
-#ifdef FILL_RX_POOLS_IN_BH
- schedule_work (&dev->bh);
-#else
fill_rx_pools (dev);
-#endif
PRINTD (DBG_IRQ, "work done: %u", irq_work);
} else {
@@ -2154,11 +2150,6 @@ static void setup_dev(amb_dev *dev, struct pci_dev *pci_dev)
dev->tx_avail = ATM_OC3_PCR;
dev->rx_avail = ATM_OC3_PCR;
-#ifdef FILL_RX_POOLS_IN_BH
- // initialise bottom half
- INIT_WORK(&dev->bh, (void (*)(void *)) fill_rx_pools, dev);
-#endif
-
// semaphore for txer/rxer modifications - we cannot use a
// spinlock as the critical region needs to switch processes
mutex_init(&dev->vcc_sf);
diff --git a/drivers/atm/ambassador.h b/drivers/atm/ambassador.h
index bd1c46a7ef49..aa9710556bd6 100644
--- a/drivers/atm/ambassador.h
+++ b/drivers/atm/ambassador.h
@@ -630,10 +630,6 @@ struct amb_dev {
u32 iobase;
u32 * membase;
-#ifdef FILL_RX_POOLS_IN_BH
- struct work_struct bh;
-#endif
-
amb_cq cq;
amb_txq txq;
amb_rxq rxq[NUM_RX_POOLS];
diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c
index 0b0625054a87..b22d71cac54c 100644
--- a/drivers/atm/atmtcp.c
+++ b/drivers/atm/atmtcp.c
@@ -11,7 +11,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
extern int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */
diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c
index 3230ea0df83c..956e9accb051 100644
--- a/drivers/atm/eni.c
+++ b/drivers/atm/eni.c
@@ -21,7 +21,7 @@
#include <linux/slab.h>
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/uaccess.h>
#include <asm/string.h>
#include <asm/byteorder.h>
@@ -1134,8 +1134,9 @@ DPRINTK("doing direct send\n"); /* @@@ well, this doesn't work anyway */
skb_headlen(skb));
else
put_dma(tx->index,eni_dev->dma,&j,(unsigned long)
- skb_shinfo(skb)->frags[i].page + skb_shinfo(skb)->frags[i].page_offset,
- skb_shinfo(skb)->frags[i].size);
+ skb_frag_page(&skb_shinfo(skb)->frags[i]) +
+ skb_shinfo(skb)->frags[i].page_offset,
+ skb_frag_size(&skb_shinfo(skb)->frags[i]));
}
if (skb->len & 3)
put_dma(tx->index,eni_dev->dma,&j,zeroes,4-(skb->len & 3));
diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h
index e4c9525e60b3..dc9a62cc2605 100644
--- a/drivers/atm/eni.h
+++ b/drivers/atm/eni.h
@@ -8,12 +8,13 @@
#include <linux/atm.h>
#include <linux/atmdev.h>
+#include <linux/interrupt.h>
#include <linux/sonet.h>
#include <linux/skbuff.h>
#include <linux/time.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "midway.h"
diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c
index ef7a658312a6..5072f8ac16fd 100644
--- a/drivers/atm/firestream.c
+++ b/drivers/atm/firestream.c
@@ -44,6 +44,7 @@
#include <linux/ioport.h> /* for request_region */
#include <linux/uio.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/capability.h>
#include <linux/bitops.h>
#include <linux/slab.h>
@@ -51,7 +52,7 @@
#include <asm/system.h>
#include <asm/string.h>
#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/uaccess.h>
#include <linux/wait.h>
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index bc9e702186dd..361f5aee3be1 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -44,7 +44,7 @@
#include <asm/dma.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#ifdef CONFIG_SBUS
#include <linux/of.h>
diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c
index d58e3fcb9db3..b81210330aca 100644
--- a/drivers/atm/horizon.c
+++ b/drivers/atm/horizon.c
@@ -38,13 +38,14 @@
#include <linux/delay.h>
#include <linux/uio.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/uaccess.h>
#include <asm/string.h>
#include <asm/byteorder.h>
diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c
index 1f8d724a18bf..1c052127548c 100644
--- a/drivers/atm/idt77252.c
+++ b/drivers/atm/idt77252.c
@@ -37,6 +37,7 @@
#include <linux/atm.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/wait.h>
#include <linux/jiffies.h>
@@ -45,7 +46,7 @@
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/byteorder.h>
#ifdef CONFIG_ATM_IDT77252_USE_SUNI
@@ -3415,27 +3416,28 @@ init_card(struct atm_dev *dev)
size = sizeof(struct vc_map *) * card->tct_size;
IPRINTK("%s: allocate %d byte for VC map.\n", card->name, size);
- if (NULL == (card->vcs = vmalloc(size))) {
+ card->vcs = vzalloc(size);
+ if (!card->vcs) {
printk("%s: memory allocation failure.\n", card->name);
deinit_card(card);
return -1;
}
- memset(card->vcs, 0, size);
size = sizeof(struct vc_map *) * card->scd_size;
IPRINTK("%s: allocate %d byte for SCD to VC mapping.\n",
card->name, size);
- if (NULL == (card->scd2vc = vmalloc(size))) {
+ card->scd2vc = vzalloc(size);
+ if (!card->scd2vc) {
printk("%s: memory allocation failure.\n", card->name);
deinit_card(card);
return -1;
}
- memset(card->scd2vc, 0, size);
size = sizeof(struct tst_info) * (card->tst_size - 2);
IPRINTK("%s: allocate %d byte for TST to VC mapping.\n",
card->name, size);
- if (NULL == (card->soft_tst = vmalloc(size))) {
+ card->soft_tst = vmalloc(size);
+ if (!card->soft_tst) {
printk("%s: memory allocation failure.\n", card->name);
deinit_card(card);
return -1;
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index dee4f01a64d8..3d0c2b0fed9c 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -53,11 +53,12 @@
#include <linux/delay.h>
#include <linux/uio.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/uaccess.h>
#include <asm/string.h>
#include <asm/byteorder.h>
@@ -817,127 +818,152 @@ static void ia_hw_type(IADEV *iadev) {
}
-static void IaFrontEndIntr(IADEV *iadev) {
- volatile IA_SUNI *suni;
- volatile ia_mb25_t *mb25;
- volatile suni_pm7345_t *suni_pm7345;
-
- if(iadev->phy_type & FE_25MBIT_PHY) {
- mb25 = (ia_mb25_t*)iadev->phy;
- iadev->carrier_detect = Boolean(mb25->mb25_intr_status & MB25_IS_GSB);
- } else if (iadev->phy_type & FE_DS3_PHY) {
- suni_pm7345 = (suni_pm7345_t *)iadev->phy;
- /* clear FRMR interrupts */
- (void) suni_pm7345->suni_ds3_frm_intr_stat;
- iadev->carrier_detect =
- Boolean(!(suni_pm7345->suni_ds3_frm_stat & SUNI_DS3_LOSV));
- } else if (iadev->phy_type & FE_E3_PHY ) {
- suni_pm7345 = (suni_pm7345_t *)iadev->phy;
- (void) suni_pm7345->suni_e3_frm_maint_intr_ind;
- iadev->carrier_detect =
- Boolean(!(suni_pm7345->suni_e3_frm_fram_intr_ind_stat&SUNI_E3_LOS));
- }
- else {
- suni = (IA_SUNI *)iadev->phy;
- (void) suni->suni_rsop_status;
- iadev->carrier_detect = Boolean(!(suni->suni_rsop_status & SUNI_LOSV));
- }
- if (iadev->carrier_detect)
- printk("IA: SUNI carrier detected\n");
- else
- printk("IA: SUNI carrier lost signal\n");
- return;
+static u32 ia_phy_read32(struct iadev_priv *ia, unsigned int reg)
+{
+ return readl(ia->phy + (reg >> 2));
+}
+
+static void ia_phy_write32(struct iadev_priv *ia, unsigned int reg, u32 val)
+{
+ writel(val, ia->phy + (reg >> 2));
+}
+
+static void ia_frontend_intr(struct iadev_priv *iadev)
+{
+ u32 status;
+
+ if (iadev->phy_type & FE_25MBIT_PHY) {
+ status = ia_phy_read32(iadev, MB25_INTR_STATUS);
+ iadev->carrier_detect = (status & MB25_IS_GSB) ? 1 : 0;
+ } else if (iadev->phy_type & FE_DS3_PHY) {
+ ia_phy_read32(iadev, SUNI_DS3_FRM_INTR_STAT);
+ status = ia_phy_read32(iadev, SUNI_DS3_FRM_STAT);
+ iadev->carrier_detect = (status & SUNI_DS3_LOSV) ? 0 : 1;
+ } else if (iadev->phy_type & FE_E3_PHY) {
+ ia_phy_read32(iadev, SUNI_E3_FRM_MAINT_INTR_IND);
+ status = ia_phy_read32(iadev, SUNI_E3_FRM_FRAM_INTR_IND_STAT);
+ iadev->carrier_detect = (status & SUNI_E3_LOS) ? 0 : 1;
+ } else {
+ status = ia_phy_read32(iadev, SUNI_RSOP_STATUS);
+ iadev->carrier_detect = (status & SUNI_LOSV) ? 0 : 1;
+ }
+
+ printk(KERN_INFO "IA: SUNI carrier %s\n",
+ iadev->carrier_detect ? "detected" : "lost signal");
}
-static void ia_mb25_init (IADEV *iadev)
+static void ia_mb25_init(struct iadev_priv *iadev)
{
- volatile ia_mb25_t *mb25 = (ia_mb25_t*)iadev->phy;
#if 0
mb25->mb25_master_ctrl = MB25_MC_DRIC | MB25_MC_DREC | MB25_MC_ENABLED;
#endif
- mb25->mb25_master_ctrl = MB25_MC_DRIC | MB25_MC_DREC;
- mb25->mb25_diag_control = 0;
- /*
- * Initialize carrier detect state
- */
- iadev->carrier_detect = Boolean(mb25->mb25_intr_status & MB25_IS_GSB);
- return;
-}
+ ia_phy_write32(iadev, MB25_MASTER_CTRL, MB25_MC_DRIC | MB25_MC_DREC);
+ ia_phy_write32(iadev, MB25_DIAG_CONTROL, 0);
+
+ iadev->carrier_detect =
+ (ia_phy_read32(iadev, MB25_INTR_STATUS) & MB25_IS_GSB) ? 1 : 0;
+}
-static void ia_suni_pm7345_init (IADEV *iadev)
+struct ia_reg {
+ u16 reg;
+ u16 val;
+};
+
+static void ia_phy_write(struct iadev_priv *iadev,
+ const struct ia_reg *regs, int len)
{
- volatile suni_pm7345_t *suni_pm7345 = (suni_pm7345_t *)iadev->phy;
- if (iadev->phy_type & FE_DS3_PHY)
- {
- iadev->carrier_detect =
- Boolean(!(suni_pm7345->suni_ds3_frm_stat & SUNI_DS3_LOSV));
- suni_pm7345->suni_ds3_frm_intr_enbl = 0x17;
- suni_pm7345->suni_ds3_frm_cfg = 1;
- suni_pm7345->suni_ds3_tran_cfg = 1;
- suni_pm7345->suni_config = 0;
- suni_pm7345->suni_splr_cfg = 0;
- suni_pm7345->suni_splt_cfg = 0;
- }
- else
- {
- iadev->carrier_detect =
- Boolean(!(suni_pm7345->suni_e3_frm_fram_intr_ind_stat & SUNI_E3_LOS));
- suni_pm7345->suni_e3_frm_fram_options = 0x4;
- suni_pm7345->suni_e3_frm_maint_options = 0x20;
- suni_pm7345->suni_e3_frm_fram_intr_enbl = 0x1d;
- suni_pm7345->suni_e3_frm_maint_intr_enbl = 0x30;
- suni_pm7345->suni_e3_tran_stat_diag_options = 0x0;
- suni_pm7345->suni_e3_tran_fram_options = 0x1;
- suni_pm7345->suni_config = SUNI_PM7345_E3ENBL;
- suni_pm7345->suni_splr_cfg = 0x41;
- suni_pm7345->suni_splt_cfg = 0x41;
- }
- /*
- * Enable RSOP loss of signal interrupt.
- */
- suni_pm7345->suni_intr_enbl = 0x28;
-
- /*
- * Clear error counters
- */
- suni_pm7345->suni_id_reset = 0;
-
- /*
- * Clear "PMCTST" in master test register.
- */
- suni_pm7345->suni_master_test = 0;
-
- suni_pm7345->suni_rxcp_ctrl = 0x2c;
- suni_pm7345->suni_rxcp_fctrl = 0x81;
-
- suni_pm7345->suni_rxcp_idle_pat_h1 =
- suni_pm7345->suni_rxcp_idle_pat_h2 =
- suni_pm7345->suni_rxcp_idle_pat_h3 = 0;
- suni_pm7345->suni_rxcp_idle_pat_h4 = 1;
-
- suni_pm7345->suni_rxcp_idle_mask_h1 = 0xff;
- suni_pm7345->suni_rxcp_idle_mask_h2 = 0xff;
- suni_pm7345->suni_rxcp_idle_mask_h3 = 0xff;
- suni_pm7345->suni_rxcp_idle_mask_h4 = 0xfe;
-
- suni_pm7345->suni_rxcp_cell_pat_h1 =
- suni_pm7345->suni_rxcp_cell_pat_h2 =
- suni_pm7345->suni_rxcp_cell_pat_h3 = 0;
- suni_pm7345->suni_rxcp_cell_pat_h4 = 1;
-
- suni_pm7345->suni_rxcp_cell_mask_h1 =
- suni_pm7345->suni_rxcp_cell_mask_h2 =
- suni_pm7345->suni_rxcp_cell_mask_h3 =
- suni_pm7345->suni_rxcp_cell_mask_h4 = 0xff;
-
- suni_pm7345->suni_txcp_ctrl = 0xa4;
- suni_pm7345->suni_txcp_intr_en_sts = 0x10;
- suni_pm7345->suni_txcp_idle_pat_h5 = 0x55;
-
- suni_pm7345->suni_config &= ~(SUNI_PM7345_LLB |
- SUNI_PM7345_CLB |
- SUNI_PM7345_DLB |
- SUNI_PM7345_PLB);
+ while (len--) {
+ ia_phy_write32(iadev, regs->reg, regs->val);
+ regs++;
+ }
+}
+
+static void ia_suni_pm7345_init_ds3(struct iadev_priv *iadev)
+{
+ static const struct ia_reg suni_ds3_init [] = {
+ { SUNI_DS3_FRM_INTR_ENBL, 0x17 },
+ { SUNI_DS3_FRM_CFG, 0x01 },
+ { SUNI_DS3_TRAN_CFG, 0x01 },
+ { SUNI_CONFIG, 0 },
+ { SUNI_SPLR_CFG, 0 },
+ { SUNI_SPLT_CFG, 0 }
+ };
+ u32 status;
+
+ status = ia_phy_read32(iadev, SUNI_DS3_FRM_STAT);
+ iadev->carrier_detect = (status & SUNI_DS3_LOSV) ? 0 : 1;
+
+ ia_phy_write(iadev, suni_ds3_init, ARRAY_SIZE(suni_ds3_init));
+}
+
+static void ia_suni_pm7345_init_e3(struct iadev_priv *iadev)
+{
+ static const struct ia_reg suni_e3_init [] = {
+ { SUNI_E3_FRM_FRAM_OPTIONS, 0x04 },
+ { SUNI_E3_FRM_MAINT_OPTIONS, 0x20 },
+ { SUNI_E3_FRM_FRAM_INTR_ENBL, 0x1d },
+ { SUNI_E3_FRM_MAINT_INTR_ENBL, 0x30 },
+ { SUNI_E3_TRAN_STAT_DIAG_OPTIONS, 0 },
+ { SUNI_E3_TRAN_FRAM_OPTIONS, 0x01 },
+ { SUNI_CONFIG, SUNI_PM7345_E3ENBL },
+ { SUNI_SPLR_CFG, 0x41 },
+ { SUNI_SPLT_CFG, 0x41 }
+ };
+ u32 status;
+
+ status = ia_phy_read32(iadev, SUNI_E3_FRM_FRAM_INTR_IND_STAT);
+ iadev->carrier_detect = (status & SUNI_E3_LOS) ? 0 : 1;
+ ia_phy_write(iadev, suni_e3_init, ARRAY_SIZE(suni_e3_init));
+}
+
+static void ia_suni_pm7345_init(struct iadev_priv *iadev)
+{
+ static const struct ia_reg suni_init [] = {
+ /* Enable RSOP loss of signal interrupt. */
+ { SUNI_INTR_ENBL, 0x28 },
+ /* Clear error counters. */
+ { SUNI_ID_RESET, 0 },
+ /* Clear "PMCTST" in master test register. */
+ { SUNI_MASTER_TEST, 0 },
+
+ { SUNI_RXCP_CTRL, 0x2c },
+ { SUNI_RXCP_FCTRL, 0x81 },
+
+ { SUNI_RXCP_IDLE_PAT_H1, 0 },
+ { SUNI_RXCP_IDLE_PAT_H2, 0 },
+ { SUNI_RXCP_IDLE_PAT_H3, 0 },
+ { SUNI_RXCP_IDLE_PAT_H4, 0x01 },
+
+ { SUNI_RXCP_IDLE_MASK_H1, 0xff },
+ { SUNI_RXCP_IDLE_MASK_H2, 0xff },
+ { SUNI_RXCP_IDLE_MASK_H3, 0xff },
+ { SUNI_RXCP_IDLE_MASK_H4, 0xfe },
+
+ { SUNI_RXCP_CELL_PAT_H1, 0 },
+ { SUNI_RXCP_CELL_PAT_H2, 0 },
+ { SUNI_RXCP_CELL_PAT_H3, 0 },
+ { SUNI_RXCP_CELL_PAT_H4, 0x01 },
+
+ { SUNI_RXCP_CELL_MASK_H1, 0xff },
+ { SUNI_RXCP_CELL_MASK_H2, 0xff },
+ { SUNI_RXCP_CELL_MASK_H3, 0xff },
+ { SUNI_RXCP_CELL_MASK_H4, 0xff },
+
+ { SUNI_TXCP_CTRL, 0xa4 },
+ { SUNI_TXCP_INTR_EN_STS, 0x10 },
+ { SUNI_TXCP_IDLE_PAT_H5, 0x55 }
+ };
+
+ if (iadev->phy_type & FE_DS3_PHY)
+ ia_suni_pm7345_init_ds3(iadev);
+ else
+ ia_suni_pm7345_init_e3(iadev);
+
+ ia_phy_write(iadev, suni_init, ARRAY_SIZE(suni_init));
+
+ ia_phy_write32(iadev, SUNI_CONFIG, ia_phy_read32(iadev, SUNI_CONFIG) &
+ ~(SUNI_PM7345_LLB | SUNI_PM7345_CLB |
+ SUNI_PM7345_DLB | SUNI_PM7345_PLB));
#ifdef __SNMP__
suni_pm7345->suni_rxcp_intr_en_sts |= SUNI_OOCDE;
#endif /* __SNMP__ */
@@ -1424,10 +1450,10 @@ static int rx_init(struct atm_dev *dev)
iadev->dma + IPHASE5575_RX_LIST_ADDR);
IF_INIT(printk("Tx Dle list addr: 0x%p value: 0x%0x\n",
iadev->dma+IPHASE5575_TX_LIST_ADDR,
- *(u32*)(iadev->dma+IPHASE5575_TX_LIST_ADDR));
+ readl(iadev->dma + IPHASE5575_TX_LIST_ADDR));
printk("Rx Dle list addr: 0x%p value: 0x%0x\n",
iadev->dma+IPHASE5575_RX_LIST_ADDR,
- *(u32*)(iadev->dma+IPHASE5575_RX_LIST_ADDR));)
+ readl(iadev->dma + IPHASE5575_RX_LIST_ADDR));)
writew(0xffff, iadev->reass_reg+REASS_MASK_REG);
writew(0, iadev->reass_reg+MODE_REG);
@@ -2207,7 +2233,7 @@ static irqreturn_t ia_int(int irq, void *dev_id)
if (status & STAT_DLERINT)
{
/* Clear this bit by writing a 1 to it. */
- *(u_int *)(iadev->reg+IPHASE5575_BUS_STATUS_REG) = STAT_DLERINT;
+ writel(STAT_DLERINT, iadev->reg + IPHASE5575_BUS_STATUS_REG);
rx_dle_intr(dev);
}
if (status & STAT_SEGINT)
@@ -2218,13 +2244,13 @@ static irqreturn_t ia_int(int irq, void *dev_id)
}
if (status & STAT_DLETINT)
{
- *(u_int *)(iadev->reg+IPHASE5575_BUS_STATUS_REG) = STAT_DLETINT;
+ writel(STAT_DLETINT, iadev->reg + IPHASE5575_BUS_STATUS_REG);
tx_dle_intr(dev);
}
if (status & (STAT_FEINT | STAT_ERRINT | STAT_MARKINT))
{
if (status & STAT_FEINT)
- IaFrontEndIntr(iadev);
+ ia_frontend_intr(iadev);
}
}
return IRQ_RETVAL(handled);
@@ -2555,7 +2581,7 @@ static int __devinit ia_start(struct atm_dev *dev)
goto err_free_rx;
}
/* Get iadev->carrier_detect status */
- IaFrontEndIntr(iadev);
+ ia_frontend_intr(iadev);
}
return 0;
@@ -2826,7 +2852,7 @@ static int ia_ioctl(struct atm_dev *dev, unsigned int cmd, void __user *arg)
case 0xb:
if (!capable(CAP_NET_ADMIN)) return -EPERM;
- IaFrontEndIntr(iadev);
+ ia_frontend_intr(iadev);
break;
case 0xa:
if (!capable(CAP_NET_ADMIN)) return -EPERM;
diff --git a/drivers/atm/iphase.h b/drivers/atm/iphase.h
index 077735e0e04b..6a0955e6d4fc 100644
--- a/drivers/atm/iphase.h
+++ b/drivers/atm/iphase.h
@@ -889,79 +889,71 @@ typedef struct ia_rtn_q {
} IARTN_Q;
#define SUNI_LOSV 0x04
-typedef struct {
- u32 suni_master_reset; /* SUNI Master Reset and Identity */
- u32 suni_master_config; /* SUNI Master Configuration */
- u32 suni_master_intr_stat; /* SUNI Master Interrupt Status */
- u32 suni_reserved1; /* Reserved */
- u32 suni_master_clk_monitor;/* SUNI Master Clock Monitor */
- u32 suni_master_control; /* SUNI Master Clock Monitor */
- u32 suni_reserved2[10]; /* Reserved */
-
- u32 suni_rsop_control; /* RSOP Control/Interrupt Enable */
- u32 suni_rsop_status; /* RSOP Status/Interrupt States */
- u32 suni_rsop_section_bip8l;/* RSOP Section BIP-8 LSB */
- u32 suni_rsop_section_bip8m;/* RSOP Section BIP-8 MSB */
-
- u32 suni_tsop_control; /* TSOP Control */
- u32 suni_tsop_diag; /* TSOP Disgnostics */
- u32 suni_tsop_reserved[2]; /* TSOP Reserved */
-
- u32 suni_rlop_cs; /* RLOP Control/Status */
- u32 suni_rlop_intr; /* RLOP Interrupt Enable/Status */
- u32 suni_rlop_line_bip24l; /* RLOP Line BIP-24 LSB */
- u32 suni_rlop_line_bip24; /* RLOP Line BIP-24 */
- u32 suni_rlop_line_bip24m; /* RLOP Line BIP-24 MSB */
- u32 suni_rlop_line_febel; /* RLOP Line FEBE LSB */
- u32 suni_rlop_line_febe; /* RLOP Line FEBE */
- u32 suni_rlop_line_febem; /* RLOP Line FEBE MSB */
-
- u32 suni_tlop_control; /* TLOP Control */
- u32 suni_tlop_disg; /* TLOP Disgnostics */
- u32 suni_tlop_reserved[14]; /* TLOP Reserved */
-
- u32 suni_rpop_cs; /* RPOP Status/Control */
- u32 suni_rpop_intr; /* RPOP Interrupt/Status */
- u32 suni_rpop_reserved; /* RPOP Reserved */
- u32 suni_rpop_intr_ena; /* RPOP Interrupt Enable */
- u32 suni_rpop_reserved1[3]; /* RPOP Reserved */
- u32 suni_rpop_path_sig; /* RPOP Path Signal Label */
- u32 suni_rpop_bip8l; /* RPOP Path BIP-8 LSB */
- u32 suni_rpop_bip8m; /* RPOP Path BIP-8 MSB */
- u32 suni_rpop_febel; /* RPOP Path FEBE LSB */
- u32 suni_rpop_febem; /* RPOP Path FEBE MSB */
- u32 suni_rpop_reserved2[4]; /* RPOP Reserved */
-
- u32 suni_tpop_cntrl_daig; /* TPOP Control/Disgnostics */
- u32 suni_tpop_pointer_ctrl; /* TPOP Pointer Control */
- u32 suni_tpop_sourcer_ctrl; /* TPOP Source Control */
- u32 suni_tpop_reserved1[2]; /* TPOP Reserved */
- u32 suni_tpop_arb_prtl; /* TPOP Arbitrary Pointer LSB */
- u32 suni_tpop_arb_prtm; /* TPOP Arbitrary Pointer MSB */
- u32 suni_tpop_reserved2; /* TPOP Reserved */
- u32 suni_tpop_path_sig; /* TPOP Path Signal Lable */
- u32 suni_tpop_path_status; /* TPOP Path Status */
- u32 suni_tpop_reserved3[6]; /* TPOP Reserved */
-
- u32 suni_racp_cs; /* RACP Control/Status */
- u32 suni_racp_intr; /* RACP Interrupt Enable/Status */
- u32 suni_racp_hdr_pattern; /* RACP Match Header Pattern */
- u32 suni_racp_hdr_mask; /* RACP Match Header Mask */
- u32 suni_racp_corr_hcs; /* RACP Correctable HCS Error Count */
- u32 suni_racp_uncorr_hcs; /* RACP Uncorrectable HCS Error Count */
- u32 suni_racp_reserved[10]; /* RACP Reserved */
-
- u32 suni_tacp_control; /* TACP Control */
- u32 suni_tacp_idle_hdr_pat; /* TACP Idle Cell Header Pattern */
- u32 suni_tacp_idle_pay_pay; /* TACP Idle Cell Payld Octet Pattern */
- u32 suni_tacp_reserved[5]; /* TACP Reserved */
-
- u32 suni_reserved3[24]; /* Reserved */
-
- u32 suni_master_test; /* SUNI Master Test */
- u32 suni_reserved_test; /* SUNI Reserved for Test */
-} IA_SUNI;
-
+enum ia_suni {
+ SUNI_MASTER_RESET = 0x000, /* SUNI Master Reset and Identity */
+ SUNI_MASTER_CONFIG = 0x004, /* SUNI Master Configuration */
+ SUNI_MASTER_INTR_STAT = 0x008, /* SUNI Master Interrupt Status */
+ SUNI_RESERVED1 = 0x00c, /* Reserved */
+ SUNI_MASTER_CLK_MONITOR = 0x010, /* SUNI Master Clock Monitor */
+ SUNI_MASTER_CONTROL = 0x014, /* SUNI Master Clock Monitor */
+ /* Reserved (10) */
+ SUNI_RSOP_CONTROL = 0x040, /* RSOP Control/Interrupt Enable */
+ SUNI_RSOP_STATUS = 0x044, /* RSOP Status/Interrupt States */
+ SUNI_RSOP_SECTION_BIP8L = 0x048, /* RSOP Section BIP-8 LSB */
+ SUNI_RSOP_SECTION_BIP8M = 0x04c, /* RSOP Section BIP-8 MSB */
+
+ SUNI_TSOP_CONTROL = 0x050, /* TSOP Control */
+ SUNI_TSOP_DIAG = 0x054, /* TSOP Disgnostics */
+ /* Reserved (2) */
+ SUNI_RLOP_CS = 0x060, /* RLOP Control/Status */
+ SUNI_RLOP_INTR = 0x064, /* RLOP Interrupt Enable/Status */
+ SUNI_RLOP_LINE_BIP24L = 0x068, /* RLOP Line BIP-24 LSB */
+ SUNI_RLOP_LINE_BIP24 = 0x06c, /* RLOP Line BIP-24 */
+ SUNI_RLOP_LINE_BIP24M = 0x070, /* RLOP Line BIP-24 MSB */
+ SUNI_RLOP_LINE_FEBEL = 0x074, /* RLOP Line FEBE LSB */
+ SUNI_RLOP_LINE_FEBE = 0x078, /* RLOP Line FEBE */
+ SUNI_RLOP_LINE_FEBEM = 0x07c, /* RLOP Line FEBE MSB */
+
+ SUNI_TLOP_CONTROL = 0x080, /* TLOP Control */
+ SUNI_TLOP_DISG = 0x084, /* TLOP Disgnostics */
+ /* Reserved (14) */
+ SUNI_RPOP_CS = 0x0c0, /* RPOP Status/Control */
+ SUNI_RPOP_INTR = 0x0c4, /* RPOP Interrupt/Status */
+ SUNI_RPOP_RESERVED = 0x0c8, /* RPOP Reserved */
+ SUNI_RPOP_INTR_ENA = 0x0cc, /* RPOP Interrupt Enable */
+ /* Reserved (3) */
+ SUNI_RPOP_PATH_SIG = 0x0dc, /* RPOP Path Signal Label */
+ SUNI_RPOP_BIP8L = 0x0e0, /* RPOP Path BIP-8 LSB */
+ SUNI_RPOP_BIP8M = 0x0e4, /* RPOP Path BIP-8 MSB */
+ SUNI_RPOP_FEBEL = 0x0e8, /* RPOP Path FEBE LSB */
+ SUNI_RPOP_FEBEM = 0x0ec, /* RPOP Path FEBE MSB */
+ /* Reserved (4) */
+ SUNI_TPOP_CNTRL_DAIG = 0x100, /* TPOP Control/Disgnostics */
+ SUNI_TPOP_POINTER_CTRL = 0x104, /* TPOP Pointer Control */
+ SUNI_TPOP_SOURCER_CTRL = 0x108, /* TPOP Source Control */
+ /* Reserved (2) */
+ SUNI_TPOP_ARB_PRTL = 0x114, /* TPOP Arbitrary Pointer LSB */
+ SUNI_TPOP_ARB_PRTM = 0x118, /* TPOP Arbitrary Pointer MSB */
+ SUNI_TPOP_RESERVED2 = 0x11c, /* TPOP Reserved */
+ SUNI_TPOP_PATH_SIG = 0x120, /* TPOP Path Signal Lable */
+ SUNI_TPOP_PATH_STATUS = 0x124, /* TPOP Path Status */
+ /* Reserved (6) */
+ SUNI_RACP_CS = 0x140, /* RACP Control/Status */
+ SUNI_RACP_INTR = 0x144, /* RACP Interrupt Enable/Status */
+ SUNI_RACP_HDR_PATTERN = 0x148, /* RACP Match Header Pattern */
+ SUNI_RACP_HDR_MASK = 0x14c, /* RACP Match Header Mask */
+ SUNI_RACP_CORR_HCS = 0x150, /* RACP Correctable HCS Error Count */
+ SUNI_RACP_UNCORR_HCS = 0x154, /* RACP Uncorrectable HCS Err Count */
+ /* Reserved (10) */
+ SUNI_TACP_CONTROL = 0x180, /* TACP Control */
+ SUNI_TACP_IDLE_HDR_PAT = 0x184, /* TACP Idle Cell Header Pattern */
+ SUNI_TACP_IDLE_PAY_PAY = 0x188, /* TACP Idle Cell Payld Octet Patrn */
+ /* Reserved (5) */
+ /* Reserved (24) */
+ /* FIXME: unused but name conflicts.
+ * SUNI_MASTER_TEST = 0x200, SUNI Master Test */
+ SUNI_RESERVED_TEST = 0x204 /* SUNI Reserved for Test */
+};
typedef struct _SUNI_STATS_
{
@@ -993,13 +985,11 @@ typedef struct _SUNI_STATS_
u32 racp_uchcs_count; // uncorrectable HCS error count
} IA_SUNI_STATS;
-typedef struct iadev_t {
+typedef struct iadev_priv {
/*-----base pointers into (i)chipSAR+ address space */
- u32 __iomem *phy; /* base pointer into phy(SUNI) */
- u32 __iomem *dma; /* base pointer into DMA control
- registers */
- u32 __iomem *reg; /* base pointer to SAR registers
- - Bus Interface Control Regs */
+ u32 __iomem *phy; /* Base pointer into phy (SUNI). */
+ u32 __iomem *dma; /* Base pointer into DMA control registers. */
+ u32 __iomem *reg; /* Base pointer to SAR registers. */
u32 __iomem *seg_reg; /* base pointer to segmentation engine
internal registers */
u32 __iomem *reass_reg; /* base pointer to reassemble engine
@@ -1071,14 +1061,14 @@ typedef struct iadev_t {
#define INPH_IA_VCC(v) ((struct ia_vcc *) (v)->dev_data)
/******************* IDT77105 25MB/s PHY DEFINE *****************************/
-typedef struct {
- u_int mb25_master_ctrl; /* Master control */
- u_int mb25_intr_status; /* Interrupt status */
- u_int mb25_diag_control; /* Diagnostic control */
- u_int mb25_led_hec; /* LED driver and HEC status/control */
- u_int mb25_low_byte_counter; /* Low byte counter */
- u_int mb25_high_byte_counter; /* High byte counter */
-} ia_mb25_t;
+enum ia_mb25 {
+ MB25_MASTER_CTRL = 0x00, /* Master control */
+ MB25_INTR_STATUS = 0x04, /* Interrupt status */
+ MB25_DIAG_CONTROL = 0x08, /* Diagnostic control */
+ MB25_LED_HEC = 0x0c, /* LED driver and HEC status/control */
+ MB25_LOW_BYTE_COUNTER = 0x10,
+ MB25_HIGH_BYTE_COUNTER = 0x14
+};
/*
* Master Control
@@ -1127,122 +1117,121 @@ typedef struct {
#define FE_E3_PHY 0x0090 /* E3 */
/*********************** SUNI_PM7345 PHY DEFINE HERE *********************/
-typedef struct _suni_pm7345_t
-{
- u_int suni_config; /* SUNI Configuration */
- u_int suni_intr_enbl; /* SUNI Interrupt Enable */
- u_int suni_intr_stat; /* SUNI Interrupt Status */
- u_int suni_control; /* SUNI Control */
- u_int suni_id_reset; /* SUNI Reset and Identity */
- u_int suni_data_link_ctrl;
- u_int suni_rboc_conf_intr_enbl;
- u_int suni_rboc_stat;
- u_int suni_ds3_frm_cfg;
- u_int suni_ds3_frm_intr_enbl;
- u_int suni_ds3_frm_intr_stat;
- u_int suni_ds3_frm_stat;
- u_int suni_rfdl_cfg;
- u_int suni_rfdl_enbl_stat;
- u_int suni_rfdl_stat;
- u_int suni_rfdl_data;
- u_int suni_pmon_chng;
- u_int suni_pmon_intr_enbl_stat;
- u_int suni_reserved1[0x13-0x11];
- u_int suni_pmon_lcv_evt_cnt_lsb;
- u_int suni_pmon_lcv_evt_cnt_msb;
- u_int suni_pmon_fbe_evt_cnt_lsb;
- u_int suni_pmon_fbe_evt_cnt_msb;
- u_int suni_pmon_sez_det_cnt_lsb;
- u_int suni_pmon_sez_det_cnt_msb;
- u_int suni_pmon_pe_evt_cnt_lsb;
- u_int suni_pmon_pe_evt_cnt_msb;
- u_int suni_pmon_ppe_evt_cnt_lsb;
- u_int suni_pmon_ppe_evt_cnt_msb;
- u_int suni_pmon_febe_evt_cnt_lsb;
- u_int suni_pmon_febe_evt_cnt_msb;
- u_int suni_ds3_tran_cfg;
- u_int suni_ds3_tran_diag;
- u_int suni_reserved2[0x23-0x21];
- u_int suni_xfdl_cfg;
- u_int suni_xfdl_intr_st;
- u_int suni_xfdl_xmit_data;
- u_int suni_xboc_code;
- u_int suni_splr_cfg;
- u_int suni_splr_intr_en;
- u_int suni_splr_intr_st;
- u_int suni_splr_status;
- u_int suni_splt_cfg;
- u_int suni_splt_cntl;
- u_int suni_splt_diag_g1;
- u_int suni_splt_f1;
- u_int suni_cppm_loc_meters;
- u_int suni_cppm_chng_of_cppm_perf_meter;
- u_int suni_cppm_b1_err_cnt_lsb;
- u_int suni_cppm_b1_err_cnt_msb;
- u_int suni_cppm_framing_err_cnt_lsb;
- u_int suni_cppm_framing_err_cnt_msb;
- u_int suni_cppm_febe_cnt_lsb;
- u_int suni_cppm_febe_cnt_msb;
- u_int suni_cppm_hcs_err_cnt_lsb;
- u_int suni_cppm_hcs_err_cnt_msb;
- u_int suni_cppm_idle_un_cell_cnt_lsb;
- u_int suni_cppm_idle_un_cell_cnt_msb;
- u_int suni_cppm_rcv_cell_cnt_lsb;
- u_int suni_cppm_rcv_cell_cnt_msb;
- u_int suni_cppm_xmit_cell_cnt_lsb;
- u_int suni_cppm_xmit_cell_cnt_msb;
- u_int suni_rxcp_ctrl;
- u_int suni_rxcp_fctrl;
- u_int suni_rxcp_intr_en_sts;
- u_int suni_rxcp_idle_pat_h1;
- u_int suni_rxcp_idle_pat_h2;
- u_int suni_rxcp_idle_pat_h3;
- u_int suni_rxcp_idle_pat_h4;
- u_int suni_rxcp_idle_mask_h1;
- u_int suni_rxcp_idle_mask_h2;
- u_int suni_rxcp_idle_mask_h3;
- u_int suni_rxcp_idle_mask_h4;
- u_int suni_rxcp_cell_pat_h1;
- u_int suni_rxcp_cell_pat_h2;
- u_int suni_rxcp_cell_pat_h3;
- u_int suni_rxcp_cell_pat_h4;
- u_int suni_rxcp_cell_mask_h1;
- u_int suni_rxcp_cell_mask_h2;
- u_int suni_rxcp_cell_mask_h3;
- u_int suni_rxcp_cell_mask_h4;
- u_int suni_rxcp_hcs_cs;
- u_int suni_rxcp_lcd_cnt_threshold;
- u_int suni_reserved3[0x57-0x54];
- u_int suni_txcp_ctrl;
- u_int suni_txcp_intr_en_sts;
- u_int suni_txcp_idle_pat_h1;
- u_int suni_txcp_idle_pat_h2;
- u_int suni_txcp_idle_pat_h3;
- u_int suni_txcp_idle_pat_h4;
- u_int suni_txcp_idle_pat_h5;
- u_int suni_txcp_idle_payload;
- u_int suni_e3_frm_fram_options;
- u_int suni_e3_frm_maint_options;
- u_int suni_e3_frm_fram_intr_enbl;
- u_int suni_e3_frm_fram_intr_ind_stat;
- u_int suni_e3_frm_maint_intr_enbl;
- u_int suni_e3_frm_maint_intr_ind;
- u_int suni_e3_frm_maint_stat;
- u_int suni_reserved4;
- u_int suni_e3_tran_fram_options;
- u_int suni_e3_tran_stat_diag_options;
- u_int suni_e3_tran_bip_8_err_mask;
- u_int suni_e3_tran_maint_adapt_options;
- u_int suni_ttb_ctrl;
- u_int suni_ttb_trail_trace_id_stat;
- u_int suni_ttb_ind_addr;
- u_int suni_ttb_ind_data;
- u_int suni_ttb_exp_payload_type;
- u_int suni_ttb_payload_type_ctrl_stat;
- u_int suni_pad5[0x7f-0x71];
- u_int suni_master_test;
- u_int suni_pad6[0xff-0x80];
-}suni_pm7345_t;
+enum suni_pm7345 {
+ SUNI_CONFIG = 0x000, /* SUNI Configuration */
+ SUNI_INTR_ENBL = 0x004, /* SUNI Interrupt Enable */
+ SUNI_INTR_STAT = 0x008, /* SUNI Interrupt Status */
+ SUNI_CONTROL = 0x00c, /* SUNI Control */
+ SUNI_ID_RESET = 0x010, /* SUNI Reset and Identity */
+ SUNI_DATA_LINK_CTRL = 0x014,
+ SUNI_RBOC_CONF_INTR_ENBL = 0x018,
+ SUNI_RBOC_STAT = 0x01c,
+ SUNI_DS3_FRM_CFG = 0x020,
+ SUNI_DS3_FRM_INTR_ENBL = 0x024,
+ SUNI_DS3_FRM_INTR_STAT = 0x028,
+ SUNI_DS3_FRM_STAT = 0x02c,
+ SUNI_RFDL_CFG = 0x030,
+ SUNI_RFDL_ENBL_STAT = 0x034,
+ SUNI_RFDL_STAT = 0x038,
+ SUNI_RFDL_DATA = 0x03c,
+ SUNI_PMON_CHNG = 0x040,
+ SUNI_PMON_INTR_ENBL_STAT = 0x044,
+ /* SUNI_RESERVED1 (0x13 - 0x11) */
+ SUNI_PMON_LCV_EVT_CNT_LSB = 0x050,
+ SUNI_PMON_LCV_EVT_CNT_MSB = 0x054,
+ SUNI_PMON_FBE_EVT_CNT_LSB = 0x058,
+ SUNI_PMON_FBE_EVT_CNT_MSB = 0x05c,
+ SUNI_PMON_SEZ_DET_CNT_LSB = 0x060,
+ SUNI_PMON_SEZ_DET_CNT_MSB = 0x064,
+ SUNI_PMON_PE_EVT_CNT_LSB = 0x068,
+ SUNI_PMON_PE_EVT_CNT_MSB = 0x06c,
+ SUNI_PMON_PPE_EVT_CNT_LSB = 0x070,
+ SUNI_PMON_PPE_EVT_CNT_MSB = 0x074,
+ SUNI_PMON_FEBE_EVT_CNT_LSB = 0x078,
+ SUNI_PMON_FEBE_EVT_CNT_MSB = 0x07c,
+ SUNI_DS3_TRAN_CFG = 0x080,
+ SUNI_DS3_TRAN_DIAG = 0x084,
+ /* SUNI_RESERVED2 (0x23 - 0x21) */
+ SUNI_XFDL_CFG = 0x090,
+ SUNI_XFDL_INTR_ST = 0x094,
+ SUNI_XFDL_XMIT_DATA = 0x098,
+ SUNI_XBOC_CODE = 0x09c,
+ SUNI_SPLR_CFG = 0x0a0,
+ SUNI_SPLR_INTR_EN = 0x0a4,
+ SUNI_SPLR_INTR_ST = 0x0a8,
+ SUNI_SPLR_STATUS = 0x0ac,
+ SUNI_SPLT_CFG = 0x0b0,
+ SUNI_SPLT_CNTL = 0x0b4,
+ SUNI_SPLT_DIAG_G1 = 0x0b8,
+ SUNI_SPLT_F1 = 0x0bc,
+ SUNI_CPPM_LOC_METERS = 0x0c0,
+ SUNI_CPPM_CHG_OF_CPPM_PERF_METR = 0x0c4,
+ SUNI_CPPM_B1_ERR_CNT_LSB = 0x0c8,
+ SUNI_CPPM_B1_ERR_CNT_MSB = 0x0cc,
+ SUNI_CPPM_FRAMING_ERR_CNT_LSB = 0x0d0,
+ SUNI_CPPM_FRAMING_ERR_CNT_MSB = 0x0d4,
+ SUNI_CPPM_FEBE_CNT_LSB = 0x0d8,
+ SUNI_CPPM_FEBE_CNT_MSB = 0x0dc,
+ SUNI_CPPM_HCS_ERR_CNT_LSB = 0x0e0,
+ SUNI_CPPM_HCS_ERR_CNT_MSB = 0x0e4,
+ SUNI_CPPM_IDLE_UN_CELL_CNT_LSB = 0x0e8,
+ SUNI_CPPM_IDLE_UN_CELL_CNT_MSB = 0x0ec,
+ SUNI_CPPM_RCV_CELL_CNT_LSB = 0x0f0,
+ SUNI_CPPM_RCV_CELL_CNT_MSB = 0x0f4,
+ SUNI_CPPM_XMIT_CELL_CNT_LSB = 0x0f8,
+ SUNI_CPPM_XMIT_CELL_CNT_MSB = 0x0fc,
+ SUNI_RXCP_CTRL = 0x100,
+ SUNI_RXCP_FCTRL = 0x104,
+ SUNI_RXCP_INTR_EN_STS = 0x108,
+ SUNI_RXCP_IDLE_PAT_H1 = 0x10c,
+ SUNI_RXCP_IDLE_PAT_H2 = 0x110,
+ SUNI_RXCP_IDLE_PAT_H3 = 0x114,
+ SUNI_RXCP_IDLE_PAT_H4 = 0x118,
+ SUNI_RXCP_IDLE_MASK_H1 = 0x11c,
+ SUNI_RXCP_IDLE_MASK_H2 = 0x120,
+ SUNI_RXCP_IDLE_MASK_H3 = 0x124,
+ SUNI_RXCP_IDLE_MASK_H4 = 0x128,
+ SUNI_RXCP_CELL_PAT_H1 = 0x12c,
+ SUNI_RXCP_CELL_PAT_H2 = 0x130,
+ SUNI_RXCP_CELL_PAT_H3 = 0x134,
+ SUNI_RXCP_CELL_PAT_H4 = 0x138,
+ SUNI_RXCP_CELL_MASK_H1 = 0x13c,
+ SUNI_RXCP_CELL_MASK_H2 = 0x140,
+ SUNI_RXCP_CELL_MASK_H3 = 0x144,
+ SUNI_RXCP_CELL_MASK_H4 = 0x148,
+ SUNI_RXCP_HCS_CS = 0x14c,
+ SUNI_RXCP_LCD_CNT_THRESHOLD = 0x150,
+ /* SUNI_RESERVED3 (0x57 - 0x54) */
+ SUNI_TXCP_CTRL = 0x160,
+ SUNI_TXCP_INTR_EN_STS = 0x164,
+ SUNI_TXCP_IDLE_PAT_H1 = 0x168,
+ SUNI_TXCP_IDLE_PAT_H2 = 0x16c,
+ SUNI_TXCP_IDLE_PAT_H3 = 0x170,
+ SUNI_TXCP_IDLE_PAT_H4 = 0x174,
+ SUNI_TXCP_IDLE_PAT_H5 = 0x178,
+ SUNI_TXCP_IDLE_PAYLOAD = 0x17c,
+ SUNI_E3_FRM_FRAM_OPTIONS = 0x180,
+ SUNI_E3_FRM_MAINT_OPTIONS = 0x184,
+ SUNI_E3_FRM_FRAM_INTR_ENBL = 0x188,
+ SUNI_E3_FRM_FRAM_INTR_IND_STAT = 0x18c,
+ SUNI_E3_FRM_MAINT_INTR_ENBL = 0x190,
+ SUNI_E3_FRM_MAINT_INTR_IND = 0x194,
+ SUNI_E3_FRM_MAINT_STAT = 0x198,
+ SUNI_RESERVED4 = 0x19c,
+ SUNI_E3_TRAN_FRAM_OPTIONS = 0x1a0,
+ SUNI_E3_TRAN_STAT_DIAG_OPTIONS = 0x1a4,
+ SUNI_E3_TRAN_BIP_8_ERR_MASK = 0x1a8,
+ SUNI_E3_TRAN_MAINT_ADAPT_OPTS = 0x1ac,
+ SUNI_TTB_CTRL = 0x1b0,
+ SUNI_TTB_TRAIL_TRACE_ID_STAT = 0x1b4,
+ SUNI_TTB_IND_ADDR = 0x1b8,
+ SUNI_TTB_IND_DATA = 0x1bc,
+ SUNI_TTB_EXP_PAYLOAD_TYPE = 0x1c0,
+ SUNI_TTB_PAYLOAD_TYPE_CTRL_STAT = 0x1c4,
+ /* SUNI_PAD5 (0x7f - 0x71) */
+ SUNI_MASTER_TEST = 0x200,
+ /* SUNI_PAD6 (0xff - 0x80) */
+};
#define SUNI_PM7345_T suni_pm7345_t
#define SUNI_PM7345 0x20 /* Suni chip type */
diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c
index 4e8ba56f75d3..f5569699f31c 100644
--- a/drivers/atm/lanai.c
+++ b/drivers/atm/lanai.c
@@ -1457,10 +1457,9 @@ static int __devinit vcc_table_allocate(struct lanai_dev *lanai)
return (lanai->vccs == NULL) ? -ENOMEM : 0;
#else
int bytes = (lanai->num_vci) * sizeof(struct lanai_vcc *);
- lanai->vccs = (struct lanai_vcc **) vmalloc(bytes);
+ lanai->vccs = vzalloc(bytes);
if (unlikely(lanai->vccs == NULL))
return -ENOMEM;
- memset(lanai->vccs, 0, bytes);
return 0;
#endif
}
@@ -1947,7 +1946,6 @@ static int __devinit lanai_pci_start(struct lanai_dev *lanai)
{
struct pci_dev *pci = lanai->pci;
int result;
- u16 w;
if (pci_enable_device(pci) != 0) {
printk(KERN_ERR DEV_LABEL "(itf %d): can't enable "
@@ -1965,13 +1963,7 @@ static int __devinit lanai_pci_start(struct lanai_dev *lanai)
"(itf %d): No suitable DMA available.\n", lanai->number);
return -EBUSY;
}
- result = pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &w);
- if (result != PCIBIOS_SUCCESSFUL) {
- printk(KERN_ERR DEV_LABEL "(itf %d): can't read "
- "PCI_SUBSYSTEM_ID: %d\n", lanai->number, result);
- return -EINVAL;
- }
- result = check_board_id_and_rev("PCI", w, NULL);
+ result = check_board_id_and_rev("PCI", pci->subsystem_device, NULL);
if (result != 0)
return result;
/* Set latency timer to zero as per lanai docs */
diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c
index 6b313ee9231b..1c70c45fa044 100644
--- a/drivers/atm/nicstar.c
+++ b/drivers/atm/nicstar.c
@@ -51,7 +51,7 @@
#include <linux/idr.h>
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "nicstar.h"
#ifdef CONFIG_ATM_NICSTAR_USE_SUNI
#include "suni.h"
diff --git a/drivers/atm/suni.c b/drivers/atm/suni.c
index 41c56eae4c81..90f1ccca9e52 100644
--- a/drivers/atm/suni.c
+++ b/drivers/atm/suni.c
@@ -25,7 +25,7 @@
#include <asm/system.h>
#include <asm/param.h>
#include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "suni.h"
diff --git a/drivers/atm/uPD98402.c b/drivers/atm/uPD98402.c
index c45ae0573bbd..5120a96b3a89 100644
--- a/drivers/atm/uPD98402.c
+++ b/drivers/atm/uPD98402.c
@@ -11,7 +11,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "uPD98402.h"
diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c
index 624917902b65..d889f56e8d8c 100644
--- a/drivers/atm/zatm.c
+++ b/drivers/atm/zatm.c
@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/uio.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/atm_zatm.h>
#include <linux/capability.h>
@@ -26,7 +27,7 @@
#include <asm/system.h>
#include <asm/string.h>
#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/uaccess.h>
#include "uPD98401.h"
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index d57e8d0fb823..21cf46f45245 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -16,6 +16,11 @@ config UEVENT_HELPER_PATH
that it creates a high system load, or on smaller systems
it is known to create out-of-memory situations during bootup.
+ To disable user space helper program execution at early boot
+ time specify an empty string here. This setting can be altered
+ via /proc/sys/kernel/hotplug or via /sys/kernel/uevent_helper
+ later at runtime.
+
config DEVTMPFS
bool "Maintain a devtmpfs filesystem to mount at /dev"
depends on HOTPLUG
@@ -65,17 +70,17 @@ config PREVENT_FIRMWARE_BUILD
default y
help
Say yes to avoid building firmware. Firmware is usually shipped
- with the driver, and only when updating the firmware a rebuild
- should be made.
- If unsure say Y here.
+ with the driver and only when updating the firmware should a
+ rebuild be made.
+ If unsure, say Y here.
config FW_LOADER
tristate "Userspace firmware loading support" if EXPERT
default y
---help---
- This option is provided for the case where no in-kernel-tree modules
- require userspace firmware loading support, but a module built outside
- the kernel tree does.
+ This option is provided for the case where none of the in-tree modules
+ require userspace firmware loading support, but a module built
+ out-of-tree does.
config FIRMWARE_IN_KERNEL
bool "Include in-kernel firmware blobs in kernel binary"
@@ -83,22 +88,22 @@ config FIRMWARE_IN_KERNEL
default y
help
The kernel source tree includes a number of firmware 'blobs'
- which are used by various drivers. The recommended way to
- use these is to run "make firmware_install" and to copy the
- resulting binary files created in usr/lib/firmware directory
- of the kernel tree to the /lib/firmware on your system so
+ that are used by various drivers. The recommended way to
+ use these is to run "make firmware_install", which, after
+ converting ihex files to binary, copies all of the needed
+ binary files in firmware/ to /lib/firmware/ on your system so
that they can be loaded by userspace helpers on request.
Enabling this option will build each required firmware blob
into the kernel directly, where request_firmware() will find
them without having to call out to userspace. This may be
- useful if your root file system requires a device which uses
- such firmware, and do not wish to use an initrd.
+ useful if your root file system requires a device that uses
+ such firmware and do not wish to use an initrd.
This single option controls the inclusion of firmware for
- every driver which uses request_firmware() and ships its
- firmware in the kernel source tree, to avoid a proliferation
- of 'Include firmware for xxx device' options.
+ every driver that uses request_firmware() and ships its
+ firmware in the kernel source tree, which avoids a
+ proliferation of 'Include firmware for xxx device' options.
Say 'N' and let firmware be loaded from userspace.
@@ -106,27 +111,27 @@ config EXTRA_FIRMWARE
string "External firmware blobs to build into the kernel binary"
depends on FW_LOADER
help
- This option allows firmware to be built into the kernel, for the
- cases where the user either cannot or doesn't want to provide it from
+ This option allows firmware to be built into the kernel for the case
+ where the user either cannot or doesn't want to provide it from
userspace at runtime (for example, when the firmware in question is
required for accessing the boot device, and the user doesn't want to
use an initrd).
- This option is a string, and takes the (space-separated) names of the
- firmware files -- the same names which appear in MODULE_FIRMWARE()
+ This option is a string and takes the (space-separated) names of the
+ firmware files -- the same names that appear in MODULE_FIRMWARE()
and request_firmware() in the source. These files should exist under
the directory specified by the EXTRA_FIRMWARE_DIR option, which is
- by default the firmware/ subdirectory of the kernel source tree.
+ by default the firmware subdirectory of the kernel source tree.
- So, for example, you might set CONFIG_EXTRA_FIRMWARE="usb8388.bin",
- copy the usb8388.bin file into the firmware/ directory, and build the
- kernel. Then any request_firmware("usb8388.bin") will be
- satisfied internally without needing to call out to userspace.
+ For example, you might set CONFIG_EXTRA_FIRMWARE="usb8388.bin", copy
+ the usb8388.bin file into the firmware directory, and build the kernel.
+ Then any request_firmware("usb8388.bin") will be satisfied internally
+ without needing to call out to userspace.
WARNING: If you include additional firmware files into your binary
- kernel image which are not available under the terms of the GPL,
+ kernel image that are not available under the terms of the GPL,
then it may be a violation of the GPL to distribute the resulting
- image -- since it combines both GPL and non-GPL work. You should
+ image since it combines both GPL and non-GPL work. You should
consult a lawyer of your own before distributing such an image.
config EXTRA_FIRMWARE_DIR
@@ -136,10 +141,9 @@ config EXTRA_FIRMWARE_DIR
help
This option controls the directory in which the kernel build system
looks for the firmware files listed in the EXTRA_FIRMWARE option.
- The default is the firmware/ directory in the kernel source tree,
- but by changing this option you can point it elsewhere, such as
- the /lib/firmware/ directory or another separate directory
- containing firmware files.
+ The default is firmware/ in the kernel source tree, but by changing
+ this option you can point it elsewhere, such as /lib/firmware/ or
+ some other directory containing the firmware files.
config DEBUG_DRIVER
bool "Driver Core verbose debug messages"
@@ -168,4 +172,6 @@ config SYS_HYPERVISOR
bool
default n
+source "drivers/base/regmap/Kconfig"
+
endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 4c5701c15f53..99a375ad2cc9 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -13,11 +13,11 @@ obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o
obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o
obj-$(CONFIG_SMP) += topology.o
-obj-$(CONFIG_IOMMU_API) += iommu.o
ifeq ($(CONFIG_SYSFS),y)
obj-$(CONFIG_MODULES) += module.o
endif
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
+obj-$(CONFIG_REGMAP) += regmap/
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/base.h b/drivers/base/base.h
index a34dca0ad041..21c1b96c34c6 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -1,3 +1,4 @@
+#include <linux/notifier.h>
/**
* struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
diff --git a/drivers/base/class.c b/drivers/base/class.c
index 4f1df2e8fd74..b80d91cc8c3a 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -47,6 +47,18 @@ static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr,
return ret;
}
+static const void *class_attr_namespace(struct kobject *kobj,
+ const struct attribute *attr)
+{
+ struct class_attribute *class_attr = to_class_attr(attr);
+ struct subsys_private *cp = to_subsys_private(kobj);
+ const void *ns = NULL;
+
+ if (class_attr->namespace)
+ ns = class_attr->namespace(cp->class, class_attr);
+ return ns;
+}
+
static void class_release(struct kobject *kobj)
{
struct subsys_private *cp = to_subsys_private(kobj);
@@ -72,8 +84,9 @@ static const struct kobj_ns_type_operations *class_child_ns_type(struct kobject
}
static const struct sysfs_ops class_sysfs_ops = {
- .show = class_attr_show,
- .store = class_attr_store,
+ .show = class_attr_show,
+ .store = class_attr_store,
+ .namespace = class_attr_namespace,
};
static struct kobj_type class_ktype = {
diff --git a/drivers/base/core.c b/drivers/base/core.c
index bc8729d603a7..d8b3d89db043 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -22,6 +22,7 @@
#include <linux/kallsyms.h>
#include <linux/mutex.h>
#include <linux/async.h>
+#include <linux/pm_runtime.h>
#include "base.h"
#include "power/power.h"
@@ -1742,6 +1743,8 @@ void device_shutdown(void)
*/
list_del_init(&dev->kobj.entry);
spin_unlock(&devices_kset->list_lock);
+ /* Disable all device's runtime power management */
+ pm_runtime_disable(dev);
if (dev->bus && dev->bus->shutdown) {
dev_dbg(dev, "shutdown\n");
@@ -1764,8 +1767,8 @@ void device_shutdown(void)
#ifdef CONFIG_PRINTK
-static int __dev_printk(const char *level, const struct device *dev,
- struct va_format *vaf)
+int __dev_printk(const char *level, const struct device *dev,
+ struct va_format *vaf)
{
if (!dev)
return printk("%s(NULL device *): %pV", level, vaf);
@@ -1773,6 +1776,7 @@ static int __dev_printk(const char *level, const struct device *dev,
return printk("%s%s %s: %pV",
level, dev_driver_string(dev), dev_name(dev), vaf);
}
+EXPORT_SYMBOL(__dev_printk);
int dev_printk(const char *level, const struct device *dev,
const char *fmt, ...)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 6658da743c3a..142e3d600f14 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -147,6 +147,9 @@ probe_failed:
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
+ } else {
+ pr_debug("%s: probe of %s rejects match %d\n",
+ drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index cf7a0c788052..65cd74832450 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -397,6 +397,7 @@ static int remove_nodes(struct device *dev,
static int release_nodes(struct device *dev, struct list_head *first,
struct list_head *end, unsigned long flags)
+ __releases(&dev->devres_lock)
{
LIST_HEAD(todo);
int cnt;
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 82bbb5967aa9..a4760e095ff5 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -21,12 +21,11 @@
#include <linux/fs.h>
#include <linux/shmem_fs.h>
#include <linux/ramfs.h>
-#include <linux/cred.h>
#include <linux/sched.h>
-#include <linux/init_task.h>
#include <linux/slab.h>
+#include <linux/kthread.h>
-static struct vfsmount *dev_mnt;
+static struct task_struct *thread;
#if defined CONFIG_DEVTMPFS_MOUNT
static int mount_dev = 1;
@@ -34,7 +33,16 @@ static int mount_dev = 1;
static int mount_dev;
#endif
-static DEFINE_MUTEX(dirlock);
+static DEFINE_SPINLOCK(req_lock);
+
+static struct req {
+ struct req *next;
+ struct completion done;
+ int err;
+ const char *name;
+ mode_t mode; /* 0 => delete */
+ struct device *dev;
+} *requests;
static int __init mount_param(char *str)
{
@@ -68,131 +76,152 @@ static inline int is_blockdev(struct device *dev)
static inline int is_blockdev(struct device *dev) { return 0; }
#endif
-static int dev_mkdir(const char *name, mode_t mode)
+int devtmpfs_create_node(struct device *dev)
{
- struct nameidata nd;
- struct dentry *dentry;
- int err;
+ const char *tmp = NULL;
+ struct req req;
- err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
- name, LOOKUP_PARENT, &nd);
- if (err)
- return err;
+ if (!thread)
+ return 0;
- dentry = lookup_create(&nd, 1);
- if (!IS_ERR(dentry)) {
- err = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
- if (!err)
- /* mark as kernel-created inode */
- dentry->d_inode->i_private = &dev_mnt;
- dput(dentry);
- } else {
- err = PTR_ERR(dentry);
- }
+ req.mode = 0;
+ req.name = device_get_devnode(dev, &req.mode, &tmp);
+ if (!req.name)
+ return -ENOMEM;
- mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
- path_put(&nd.path);
- return err;
+ if (req.mode == 0)
+ req.mode = 0600;
+ if (is_blockdev(dev))
+ req.mode |= S_IFBLK;
+ else
+ req.mode |= S_IFCHR;
+
+ req.dev = dev;
+
+ init_completion(&req.done);
+
+ spin_lock(&req_lock);
+ req.next = requests;
+ requests = &req;
+ spin_unlock(&req_lock);
+
+ wake_up_process(thread);
+ wait_for_completion(&req.done);
+
+ kfree(tmp);
+
+ return req.err;
}
-static int create_path(const char *nodepath)
+int devtmpfs_delete_node(struct device *dev)
{
- int err;
+ const char *tmp = NULL;
+ struct req req;
- mutex_lock(&dirlock);
- err = dev_mkdir(nodepath, 0755);
- if (err == -ENOENT) {
- char *path;
- char *s;
-
- /* parent directories do not exist, create them */
- path = kstrdup(nodepath, GFP_KERNEL);
- if (!path) {
- err = -ENOMEM;
- goto out;
- }
- s = path;
- for (;;) {
- s = strchr(s, '/');
- if (!s)
- break;
- s[0] = '\0';
- err = dev_mkdir(path, 0755);
- if (err && err != -EEXIST)
- break;
- s[0] = '/';
- s++;
- }
- kfree(path);
- }
-out:
- mutex_unlock(&dirlock);
- return err;
+ if (!thread)
+ return 0;
+
+ req.name = device_get_devnode(dev, NULL, &tmp);
+ if (!req.name)
+ return -ENOMEM;
+
+ req.mode = 0;
+ req.dev = dev;
+
+ init_completion(&req.done);
+
+ spin_lock(&req_lock);
+ req.next = requests;
+ requests = &req;
+ spin_unlock(&req_lock);
+
+ wake_up_process(thread);
+ wait_for_completion(&req.done);
+
+ kfree(tmp);
+ return req.err;
}
-int devtmpfs_create_node(struct device *dev)
+static int dev_mkdir(const char *name, mode_t mode)
{
- const char *tmp = NULL;
- const char *nodename;
- const struct cred *curr_cred;
- mode_t mode = 0;
- struct nameidata nd;
struct dentry *dentry;
+ struct path path;
int err;
- if (!dev_mnt)
- return 0;
+ dentry = kern_path_create(AT_FDCWD, name, &path, 1);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ err = vfs_mkdir(path.dentry->d_inode, dentry, mode);
+ if (!err)
+ /* mark as kernel-created inode */
+ dentry->d_inode->i_private = &thread;
+ dput(dentry);
+ mutex_unlock(&path.dentry->d_inode->i_mutex);
+ path_put(&path);
+ return err;
+}
- nodename = device_get_devnode(dev, &mode, &tmp);
- if (!nodename)
+static int create_path(const char *nodepath)
+{
+ char *path;
+ char *s;
+ int err = 0;
+
+ /* parent directories do not exist, create them */
+ path = kstrdup(nodepath, GFP_KERNEL);
+ if (!path)
return -ENOMEM;
- if (mode == 0)
- mode = 0600;
- if (is_blockdev(dev))
- mode |= S_IFBLK;
- else
- mode |= S_IFCHR;
+ s = path;
+ for (;;) {
+ s = strchr(s, '/');
+ if (!s)
+ break;
+ s[0] = '\0';
+ err = dev_mkdir(path, 0755);
+ if (err && err != -EEXIST)
+ break;
+ s[0] = '/';
+ s++;
+ }
+ kfree(path);
+ return err;
+}
- curr_cred = override_creds(&init_cred);
+static int handle_create(const char *nodename, mode_t mode, struct device *dev)
+{
+ struct dentry *dentry;
+ struct path path;
+ int err;
- err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
- nodename, LOOKUP_PARENT, &nd);
- if (err == -ENOENT) {
+ dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
+ if (dentry == ERR_PTR(-ENOENT)) {
create_path(nodename);
- err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
- nodename, LOOKUP_PARENT, &nd);
+ dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
}
- if (err)
- goto out;
-
- dentry = lookup_create(&nd, 0);
- if (!IS_ERR(dentry)) {
- err = vfs_mknod(nd.path.dentry->d_inode,
- dentry, mode, dev->devt);
- if (!err) {
- struct iattr newattrs;
-
- /* fixup possibly umasked mode */
- newattrs.ia_mode = mode;
- newattrs.ia_valid = ATTR_MODE;
- mutex_lock(&dentry->d_inode->i_mutex);
- notify_change(dentry, &newattrs);
- mutex_unlock(&dentry->d_inode->i_mutex);
-
- /* mark as kernel-created inode */
- dentry->d_inode->i_private = &dev_mnt;
- }
- dput(dentry);
- } else {
- err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ err = vfs_mknod(path.dentry->d_inode,
+ dentry, mode, dev->devt);
+ if (!err) {
+ struct iattr newattrs;
+
+ /* fixup possibly umasked mode */
+ newattrs.ia_mode = mode;
+ newattrs.ia_valid = ATTR_MODE;
+ mutex_lock(&dentry->d_inode->i_mutex);
+ notify_change(dentry, &newattrs);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+
+ /* mark as kernel-created inode */
+ dentry->d_inode->i_private = &thread;
}
+ dput(dentry);
- mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
- path_put(&nd.path);
-out:
- kfree(tmp);
- revert_creds(curr_cred);
+ mutex_unlock(&path.dentry->d_inode->i_mutex);
+ path_put(&path);
return err;
}
@@ -202,8 +231,7 @@ static int dev_rmdir(const char *name)
struct dentry *dentry;
int err;
- err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
- name, LOOKUP_PARENT, &nd);
+ err = kern_path_parent(name, &nd);
if (err)
return err;
@@ -211,7 +239,7 @@ static int dev_rmdir(const char *name)
dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
if (!IS_ERR(dentry)) {
if (dentry->d_inode) {
- if (dentry->d_inode->i_private == &dev_mnt)
+ if (dentry->d_inode->i_private == &thread)
err = vfs_rmdir(nd.path.dentry->d_inode,
dentry);
else
@@ -238,7 +266,6 @@ static int delete_path(const char *nodepath)
if (!path)
return -ENOMEM;
- mutex_lock(&dirlock);
for (;;) {
char *base;
@@ -250,7 +277,6 @@ static int delete_path(const char *nodepath)
if (err)
break;
}
- mutex_unlock(&dirlock);
kfree(path);
return err;
@@ -259,7 +285,7 @@ static int delete_path(const char *nodepath)
static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat)
{
/* did we create it */
- if (inode->i_private != &dev_mnt)
+ if (inode->i_private != &thread)
return 0;
/* does the dev_t match */
@@ -277,29 +303,17 @@ static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *sta
return 1;
}
-int devtmpfs_delete_node(struct device *dev)
+static int handle_remove(const char *nodename, struct device *dev)
{
- const char *tmp = NULL;
- const char *nodename;
- const struct cred *curr_cred;
struct nameidata nd;
struct dentry *dentry;
struct kstat stat;
int deleted = 1;
int err;
- if (!dev_mnt)
- return 0;
-
- nodename = device_get_devnode(dev, NULL, &tmp);
- if (!nodename)
- return -ENOMEM;
-
- curr_cred = override_creds(&init_cred);
- err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
- nodename, LOOKUP_PARENT, &nd);
+ err = kern_path_parent(nodename, &nd);
if (err)
- goto out;
+ return err;
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
@@ -337,9 +351,6 @@ int devtmpfs_delete_node(struct device *dev)
path_put(&nd.path);
if (deleted && strchr(nodename, '/'))
delete_path(nodename);
-out:
- kfree(tmp);
- revert_creds(curr_cred);
return err;
}
@@ -354,7 +365,7 @@ int devtmpfs_mount(const char *mntdir)
if (!mount_dev)
return 0;
- if (!dev_mnt)
+ if (!thread)
return 0;
err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL);
@@ -365,31 +376,80 @@ int devtmpfs_mount(const char *mntdir)
return err;
}
+static DECLARE_COMPLETION(setup_done);
+
+static int handle(const char *name, mode_t mode, struct device *dev)
+{
+ if (mode)
+ return handle_create(name, mode, dev);
+ else
+ return handle_remove(name, dev);
+}
+
+static int devtmpfsd(void *p)
+{
+ char options[] = "mode=0755";
+ int *err = p;
+ *err = sys_unshare(CLONE_NEWNS);
+ if (*err)
+ goto out;
+ *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options);
+ if (*err)
+ goto out;
+ sys_chdir("/.."); /* will traverse into overmounted root */
+ sys_chroot(".");
+ complete(&setup_done);
+ while (1) {
+ spin_lock(&req_lock);
+ while (requests) {
+ struct req *req = requests;
+ requests = NULL;
+ spin_unlock(&req_lock);
+ while (req) {
+ struct req *next = req->next;
+ req->err = handle(req->name, req->mode, req->dev);
+ complete(&req->done);
+ req = next;
+ }
+ spin_lock(&req_lock);
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock(&req_lock);
+ schedule();
+ __set_current_state(TASK_RUNNING);
+ }
+ return 0;
+out:
+ complete(&setup_done);
+ return *err;
+}
+
/*
* Create devtmpfs instance, driver-core devices will add their device
* nodes here.
*/
int __init devtmpfs_init(void)
{
- int err;
- struct vfsmount *mnt;
- char options[] = "mode=0755";
-
- err = register_filesystem(&dev_fs_type);
+ int err = register_filesystem(&dev_fs_type);
if (err) {
printk(KERN_ERR "devtmpfs: unable to register devtmpfs "
"type %i\n", err);
return err;
}
- mnt = kern_mount_data(&dev_fs_type, options);
- if (IS_ERR(mnt)) {
- err = PTR_ERR(mnt);
+ thread = kthread_run(devtmpfsd, &err, "kdevtmpfs");
+ if (!IS_ERR(thread)) {
+ wait_for_completion(&setup_done);
+ } else {
+ err = PTR_ERR(thread);
+ thread = NULL;
+ }
+
+ if (err) {
printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
unregister_filesystem(&dev_fs_type);
return err;
}
- dev_mnt = mnt;
printk(KERN_INFO "devtmpfs: initialized\n");
return 0;
diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c
index f369e2795985..bb0025c510b3 100644
--- a/drivers/base/dma-coherent.c
+++ b/drivers/base/dma-coherent.c
@@ -4,6 +4,7 @@
*/
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
struct dma_coherent_mem {
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c
index 763d59c1eb65..6f3676f1559f 100644
--- a/drivers/base/dma-mapping.c
+++ b/drivers/base/dma-mapping.c
@@ -8,6 +8,7 @@
*/
#include <linux/dma-mapping.h>
+#include <linux/export.h>
#include <linux/gfp.h>
/*
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index bbb03e6f7255..06ed6b4e7df5 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -521,11 +521,6 @@ static int _request_firmware(const struct firmware **firmware_p,
if (!firmware_p)
return -EINVAL;
- if (WARN_ON(usermodehelper_is_disabled())) {
- dev_err(device, "firmware: %s will not be loaded\n", name);
- return -EBUSY;
- }
-
*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
if (!firmware) {
dev_err(device, "%s: kmalloc(struct firmware) failed\n",
@@ -539,6 +534,12 @@ static int _request_firmware(const struct firmware **firmware_p,
return 0;
}
+ if (WARN_ON(usermodehelper_is_disabled())) {
+ dev_err(device, "firmware: %s will not be loaded\n", name);
+ retval = -EBUSY;
+ goto out;
+ }
+
if (uevent)
dev_dbg(device, "firmware: requesting %s\n", name);
diff --git a/drivers/base/hypervisor.c b/drivers/base/hypervisor.c
index 6428cba3aadd..4f8b741f4615 100644
--- a/drivers/base/hypervisor.c
+++ b/drivers/base/hypervisor.c
@@ -10,6 +10,7 @@
#include <linux/kobject.h>
#include <linux/device.h>
+#include <linux/export.h>
#include "base.h"
struct kobject *hypervisor_kobj;
diff --git a/drivers/base/iommu.c b/drivers/base/iommu.c
deleted file mode 100644
index 6e6b6a11b3ce..000000000000
--- a/drivers/base/iommu.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
- * Author: Joerg Roedel <joerg.roedel@amd.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- *
- * 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/bug.h>
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/iommu.h>
-
-static struct iommu_ops *iommu_ops;
-
-void register_iommu(struct iommu_ops *ops)
-{
- if (iommu_ops)
- BUG();
-
- iommu_ops = ops;
-}
-
-bool iommu_found(void)
-{
- return iommu_ops != NULL;
-}
-EXPORT_SYMBOL_GPL(iommu_found);
-
-struct iommu_domain *iommu_domain_alloc(void)
-{
- struct iommu_domain *domain;
- int ret;
-
- domain = kmalloc(sizeof(*domain), GFP_KERNEL);
- if (!domain)
- return NULL;
-
- ret = iommu_ops->domain_init(domain);
- if (ret)
- goto out_free;
-
- return domain;
-
-out_free:
- kfree(domain);
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(iommu_domain_alloc);
-
-void iommu_domain_free(struct iommu_domain *domain)
-{
- iommu_ops->domain_destroy(domain);
- kfree(domain);
-}
-EXPORT_SYMBOL_GPL(iommu_domain_free);
-
-int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
-{
- return iommu_ops->attach_dev(domain, dev);
-}
-EXPORT_SYMBOL_GPL(iommu_attach_device);
-
-void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
-{
- iommu_ops->detach_dev(domain, dev);
-}
-EXPORT_SYMBOL_GPL(iommu_detach_device);
-
-phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long iova)
-{
- return iommu_ops->iova_to_phys(domain, iova);
-}
-EXPORT_SYMBOL_GPL(iommu_iova_to_phys);
-
-int iommu_domain_has_cap(struct iommu_domain *domain,
- unsigned long cap)
-{
- return iommu_ops->domain_has_cap(domain, cap);
-}
-EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
-
-int iommu_map(struct iommu_domain *domain, unsigned long iova,
- phys_addr_t paddr, int gfp_order, int prot)
-{
- unsigned long invalid_mask;
- size_t size;
-
- size = 0x1000UL << gfp_order;
- invalid_mask = size - 1;
-
- BUG_ON((iova | paddr) & invalid_mask);
-
- return iommu_ops->map(domain, iova, paddr, gfp_order, prot);
-}
-EXPORT_SYMBOL_GPL(iommu_map);
-
-int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order)
-{
- unsigned long invalid_mask;
- size_t size;
-
- size = 0x1000UL << gfp_order;
- invalid_mask = size - 1;
-
- BUG_ON(iova & invalid_mask);
-
- return iommu_ops->unmap(domain, iova, gfp_order);
-}
-EXPORT_SYMBOL_GPL(iommu_unmap);
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 9f9b2359f718..8272d92d22c0 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -24,13 +24,12 @@
#include <linux/stat.h>
#include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/uaccess.h>
static DEFINE_MUTEX(mem_sysfs_mutex);
#define MEMORY_CLASS_NAME "memory"
-#define MIN_MEMORY_BLOCK_SIZE (1 << SECTION_SIZE_BITS)
static int sections_per_block;
@@ -225,13 +224,48 @@ int memory_isolate_notify(unsigned long val, void *v)
}
/*
+ * The probe routines leave the pages reserved, just as the bootmem code does.
+ * Make sure they're still that way.
+ */
+static bool pages_correctly_reserved(unsigned long start_pfn,
+ unsigned long nr_pages)
+{
+ int i, j;
+ struct page *page;
+ unsigned long pfn = start_pfn;
+
+ /*
+ * memmap between sections is not contiguous except with
+ * SPARSEMEM_VMEMMAP. We lookup the page once per section
+ * and assume memmap is contiguous within each section
+ */
+ for (i = 0; i < sections_per_block; i++, pfn += PAGES_PER_SECTION) {
+ if (WARN_ON_ONCE(!pfn_valid(pfn)))
+ return false;
+ page = pfn_to_page(pfn);
+
+ for (j = 0; j < PAGES_PER_SECTION; j++) {
+ if (PageReserved(page + j))
+ continue;
+
+ printk(KERN_WARNING "section number %ld page number %d "
+ "not reserved, was it already online?\n",
+ pfn_to_section_nr(pfn), j);
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
* MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is
* OK to have direct references to sparsemem variables in here.
*/
static int
memory_block_action(unsigned long phys_index, unsigned long action)
{
- int i;
unsigned long start_pfn, start_paddr;
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
struct page *first_page;
@@ -239,26 +273,13 @@ memory_block_action(unsigned long phys_index, unsigned long action)
first_page = pfn_to_page(phys_index << PFN_SECTION_SHIFT);
- /*
- * The probe routines leave the pages reserved, just
- * as the bootmem code does. Make sure they're still
- * that way.
- */
- if (action == MEM_ONLINE) {
- for (i = 0; i < nr_pages; i++) {
- if (PageReserved(first_page+i))
- continue;
-
- printk(KERN_WARNING "section number %ld page number %d "
- "not reserved, was it already online?\n",
- phys_index, i);
- return -EBUSY;
- }
- }
-
switch (action) {
case MEM_ONLINE:
start_pfn = page_to_pfn(first_page);
+
+ if (!pages_correctly_reserved(start_pfn, nr_pages))
+ return -EBUSY;
+
ret = online_pages(start_pfn, nr_pages);
break;
case MEM_OFFLINE:
@@ -381,9 +402,13 @@ memory_probe_store(struct class *class, struct class_attribute *attr,
u64 phys_addr;
int nid;
int i, ret;
+ unsigned long pages_per_block = PAGES_PER_SECTION * sections_per_block;
phys_addr = simple_strtoull(buf, NULL, 0);
+ if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1))
+ return -EINVAL;
+
for (i = 0; i < sections_per_block; i++) {
nid = memory_add_physaddr_to_nid(phys_addr);
ret = add_memory(nid, phys_addr,
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 793f796c4da3..5693ecee9a40 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -127,12 +127,13 @@ static ssize_t node_read_meminfo(struct sys_device * dev,
nid, K(node_page_state(nid, NR_WRITEBACK)),
nid, K(node_page_state(nid, NR_FILE_PAGES)),
nid, K(node_page_state(nid, NR_FILE_MAPPED)),
- nid, K(node_page_state(nid, NR_ANON_PAGES)
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ nid, K(node_page_state(nid, NR_ANON_PAGES)
+ node_page_state(nid, NR_ANON_TRANSPARENT_HUGEPAGES) *
- HPAGE_PMD_NR
+ HPAGE_PMD_NR),
+#else
+ nid, K(node_page_state(nid, NR_ANON_PAGES)),
#endif
- ),
nid, K(node_page_state(nid, NR_SHMEM)),
nid, node_page_state(nid, NR_KERNEL_STACK) *
THREAD_SIZE / 1024,
@@ -143,13 +144,14 @@ static ssize_t node_read_meminfo(struct sys_device * dev,
nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE) +
node_page_state(nid, NR_SLAB_UNRECLAIMABLE)),
nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE)),
- nid, K(node_page_state(nid, NR_SLAB_UNRECLAIMABLE))
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ nid, K(node_page_state(nid, NR_SLAB_UNRECLAIMABLE))
, nid,
K(node_page_state(nid, NR_ANON_TRANSPARENT_HUGEPAGES) *
- HPAGE_PMD_NR)
+ HPAGE_PMD_NR));
+#else
+ nid, K(node_page_state(nid, NR_SLAB_UNRECLAIMABLE)));
#endif
- );
n += hugetlb_report_node_meminfo(nid, buf + n);
return n;
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 6040717b62bb..7a24895543e7 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -32,6 +32,25 @@ struct device platform_bus = {
EXPORT_SYMBOL_GPL(platform_bus);
/**
+ * arch_setup_pdev_archdata - Allow manipulation of archdata before its used
+ * @pdev: platform device
+ *
+ * This is called before platform_device_add() such that any pdev_archdata may
+ * be setup before the platform_notifier is called. So if a user needs to
+ * manipulate any relevant information in the pdev_archdata they can do:
+ *
+ * platform_devic_alloc()
+ * ... manipulate ...
+ * platform_device_add()
+ *
+ * And if they don't care they can just call platform_device_register() and
+ * everything will just work out.
+ */
+void __weak arch_setup_pdev_archdata(struct platform_device *pdev)
+{
+}
+
+/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type
@@ -173,6 +192,7 @@ struct platform_device *platform_device_alloc(const char *name, int id)
pa->pdev.id = id;
device_initialize(&pa->pdev.dev);
pa->pdev.dev.release = platform_device_release;
+ arch_setup_pdev_archdata(&pa->pdev);
}
return pa ? &pa->pdev : NULL;
@@ -334,6 +354,7 @@ EXPORT_SYMBOL_GPL(platform_device_del);
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
+ arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_register);
@@ -354,52 +375,64 @@ void platform_device_unregister(struct platform_device *pdev)
EXPORT_SYMBOL_GPL(platform_device_unregister);
/**
- * platform_device_register_resndata - add a platform-level device with
+ * platform_device_register_full - add a platform-level device with
* resources and platform-specific data
*
- * @parent: parent device for the device we're adding
- * @name: base name of the device we're adding
- * @id: instance id
- * @res: set of resources that needs to be allocated for the device
- * @num: number of resources
- * @data: platform specific data for this platform device
- * @size: size of platform specific data
+ * @pdevinfo: data used to create device
*
* Returns &struct platform_device pointer on success, or ERR_PTR() on error.
*/
-struct platform_device *platform_device_register_resndata(
- struct device *parent,
- const char *name, int id,
- const struct resource *res, unsigned int num,
- const void *data, size_t size)
+struct platform_device *platform_device_register_full(
+ struct platform_device_info *pdevinfo)
{
int ret = -ENOMEM;
struct platform_device *pdev;
- pdev = platform_device_alloc(name, id);
+ pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id);
if (!pdev)
- goto err;
-
- pdev->dev.parent = parent;
+ goto err_alloc;
+
+ pdev->dev.parent = pdevinfo->parent;
+
+ if (pdevinfo->dma_mask) {
+ /*
+ * This memory isn't freed when the device is put,
+ * I don't have a nice idea for that though. Conceptually
+ * dma_mask in struct device should not be a pointer.
+ * See http://thread.gmane.org/gmane.linux.kernel.pci/9081
+ */
+ pdev->dev.dma_mask =
+ kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
+ if (!pdev->dev.dma_mask)
+ goto err;
+
+ *pdev->dev.dma_mask = pdevinfo->dma_mask;
+ pdev->dev.coherent_dma_mask = pdevinfo->dma_mask;
+ }
- ret = platform_device_add_resources(pdev, res, num);
+ ret = platform_device_add_resources(pdev,
+ pdevinfo->res, pdevinfo->num_res);
if (ret)
goto err;
- ret = platform_device_add_data(pdev, data, size);
+ ret = platform_device_add_data(pdev,
+ pdevinfo->data, pdevinfo->size_data);
if (ret)
goto err;
ret = platform_device_add(pdev);
if (ret) {
err:
+ kfree(pdev->dev.dma_mask);
+
+err_alloc:
platform_device_put(pdev);
return ERR_PTR(ret);
}
return pdev;
}
-EXPORT_SYMBOL_GPL(platform_device_register_resndata);
+EXPORT_SYMBOL_GPL(platform_device_register_full);
static int platform_drv_probe(struct device *_dev)
{
@@ -593,7 +626,7 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
return rc;
add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX,
- (pdev->id_entry) ? pdev->id_entry->name : pdev->name);
+ pdev->name);
return 0;
}
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 3647e114d0e7..81676dd17900 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,8 +1,9 @@
-obj-$(CONFIG_PM) += sysfs.o generic_ops.o
+obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
obj-$(CONFIG_PM_RUNTIME) += runtime.o
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
obj-$(CONFIG_PM_OPP) += opp.o
+obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o
obj-$(CONFIG_HAVE_CLK) += clock_ops.o
-ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG \ No newline at end of file
+ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index ad367c4139b1..428e55e012dc 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -10,17 +10,12 @@
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/pm.h>
-#include <linux/pm_runtime.h>
+#include <linux/pm_clock.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/err.h>
-#ifdef CONFIG_PM_RUNTIME
-
-struct pm_runtime_clk_data {
- struct list_head clock_list;
- struct mutex lock;
-};
+#ifdef CONFIG_PM
enum pce_status {
PCE_STATUS_NONE = 0,
@@ -36,25 +31,36 @@ struct pm_clock_entry {
enum pce_status status;
};
-static struct pm_runtime_clk_data *__to_prd(struct device *dev)
+/**
+ * pm_clk_acquire - Acquire a device clock.
+ * @dev: Device whose clock is to be acquired.
+ * @ce: PM clock entry corresponding to the clock.
+ */
+static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce)
{
- return dev ? dev->power.subsys_data : NULL;
+ ce->clk = clk_get(dev, ce->con_id);
+ if (IS_ERR(ce->clk)) {
+ ce->status = PCE_STATUS_ERROR;
+ } else {
+ ce->status = PCE_STATUS_ACQUIRED;
+ dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
+ }
}
/**
- * pm_runtime_clk_add - Start using a device clock for runtime PM.
- * @dev: Device whose clock is going to be used for runtime PM.
+ * pm_clk_add - Start using a device clock for power management.
+ * @dev: Device whose clock is going to be used for power management.
* @con_id: Connection ID of the clock.
*
* Add the clock represented by @con_id to the list of clocks used for
- * the runtime PM of @dev.
+ * the power management of @dev.
*/
-int pm_runtime_clk_add(struct device *dev, const char *con_id)
+int pm_clk_add(struct device *dev, const char *con_id)
{
- struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce;
- if (!prd)
+ if (!psd)
return -EINVAL;
ce = kzalloc(sizeof(*ce), GFP_KERNEL);
@@ -73,26 +79,23 @@ int pm_runtime_clk_add(struct device *dev, const char *con_id)
}
}
- mutex_lock(&prd->lock);
- list_add_tail(&ce->node, &prd->clock_list);
- mutex_unlock(&prd->lock);
+ pm_clk_acquire(dev, ce);
+
+ spin_lock_irq(&psd->lock);
+ list_add_tail(&ce->node, &psd->clock_list);
+ spin_unlock_irq(&psd->lock);
return 0;
}
/**
- * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
- * @ce: Runtime PM clock entry to destroy.
- *
- * This routine must be called under the mutex protecting the runtime PM list
- * of clocks corresponding the the @ce's device.
+ * __pm_clk_remove - Destroy PM clock entry.
+ * @ce: PM clock entry to destroy.
*/
-static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
+static void __pm_clk_remove(struct pm_clock_entry *ce)
{
if (!ce)
return;
- list_del(&ce->node);
-
if (ce->status < PCE_STATUS_ERROR) {
if (ce->status == PCE_STATUS_ENABLED)
clk_disable(ce->clk);
@@ -101,192 +104,188 @@ static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
clk_put(ce->clk);
}
- if (ce->con_id)
- kfree(ce->con_id);
-
+ kfree(ce->con_id);
kfree(ce);
}
/**
- * pm_runtime_clk_remove - Stop using a device clock for runtime PM.
- * @dev: Device whose clock should not be used for runtime PM any more.
+ * pm_clk_remove - Stop using a device clock for power management.
+ * @dev: Device whose clock should not be used for PM any more.
* @con_id: Connection ID of the clock.
*
* Remove the clock represented by @con_id from the list of clocks used for
- * the runtime PM of @dev.
+ * the power management of @dev.
*/
-void pm_runtime_clk_remove(struct device *dev, const char *con_id)
+void pm_clk_remove(struct device *dev, const char *con_id)
{
- struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce;
- if (!prd)
+ if (!psd)
return;
- mutex_lock(&prd->lock);
+ spin_lock_irq(&psd->lock);
- list_for_each_entry(ce, &prd->clock_list, node) {
- if (!con_id && !ce->con_id) {
- __pm_runtime_clk_remove(ce);
- break;
- } else if (!con_id || !ce->con_id) {
+ list_for_each_entry(ce, &psd->clock_list, node) {
+ if (!con_id && !ce->con_id)
+ goto remove;
+ else if (!con_id || !ce->con_id)
continue;
- } else if (!strcmp(con_id, ce->con_id)) {
- __pm_runtime_clk_remove(ce);
- break;
- }
+ else if (!strcmp(con_id, ce->con_id))
+ goto remove;
}
- mutex_unlock(&prd->lock);
+ spin_unlock_irq(&psd->lock);
+ return;
+
+ remove:
+ list_del(&ce->node);
+ spin_unlock_irq(&psd->lock);
+
+ __pm_clk_remove(ce);
}
/**
- * pm_runtime_clk_init - Initialize a device's list of runtime PM clocks.
- * @dev: Device to initialize the list of runtime PM clocks for.
+ * pm_clk_init - Initialize a device's list of power management clocks.
+ * @dev: Device to initialize the list of PM clocks for.
*
- * Allocate a struct pm_runtime_clk_data object, initialize its lock member and
- * make the @dev's power.subsys_data field point to it.
+ * Initialize the lock and clock_list members of the device's pm_subsys_data
+ * object.
*/
-int pm_runtime_clk_init(struct device *dev)
+void pm_clk_init(struct device *dev)
{
- struct pm_runtime_clk_data *prd;
-
- prd = kzalloc(sizeof(*prd), GFP_KERNEL);
- if (!prd) {
- dev_err(dev, "Not enough memory fo runtime PM data.\n");
- return -ENOMEM;
- }
+ struct pm_subsys_data *psd = dev_to_psd(dev);
+ if (psd)
+ INIT_LIST_HEAD(&psd->clock_list);
+}
- INIT_LIST_HEAD(&prd->clock_list);
- mutex_init(&prd->lock);
- dev->power.subsys_data = prd;
- return 0;
+/**
+ * pm_clk_create - Create and initialize a device's list of PM clocks.
+ * @dev: Device to create and initialize the list of PM clocks for.
+ *
+ * Allocate a struct pm_subsys_data object, initialize its lock and clock_list
+ * members and make the @dev's power.subsys_data field point to it.
+ */
+int pm_clk_create(struct device *dev)
+{
+ int ret = dev_pm_get_subsys_data(dev);
+ return ret < 0 ? ret : 0;
}
/**
- * pm_runtime_clk_destroy - Destroy a device's list of runtime PM clocks.
- * @dev: Device to destroy the list of runtime PM clocks for.
+ * pm_clk_destroy - Destroy a device's list of power management clocks.
+ * @dev: Device to destroy the list of PM clocks for.
*
* Clear the @dev's power.subsys_data field, remove the list of clock entries
- * from the struct pm_runtime_clk_data object pointed to by it before and free
+ * from the struct pm_subsys_data object pointed to by it before and free
* that object.
*/
-void pm_runtime_clk_destroy(struct device *dev)
+void pm_clk_destroy(struct device *dev)
{
- struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce, *c;
+ struct list_head list;
- if (!prd)
+ if (!psd)
return;
- dev->power.subsys_data = NULL;
+ INIT_LIST_HEAD(&list);
- mutex_lock(&prd->lock);
+ spin_lock_irq(&psd->lock);
- list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node)
- __pm_runtime_clk_remove(ce);
+ list_for_each_entry_safe_reverse(ce, c, &psd->clock_list, node)
+ list_move(&ce->node, &list);
- mutex_unlock(&prd->lock);
+ spin_unlock_irq(&psd->lock);
- kfree(prd);
-}
+ dev_pm_put_subsys_data(dev);
-/**
- * pm_runtime_clk_acquire - Acquire a device clock.
- * @dev: Device whose clock is to be acquired.
- * @con_id: Connection ID of the clock.
- */
-static void pm_runtime_clk_acquire(struct device *dev,
- struct pm_clock_entry *ce)
-{
- ce->clk = clk_get(dev, ce->con_id);
- if (IS_ERR(ce->clk)) {
- ce->status = PCE_STATUS_ERROR;
- } else {
- ce->status = PCE_STATUS_ACQUIRED;
- dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
+ list_for_each_entry_safe_reverse(ce, c, &list, node) {
+ list_del(&ce->node);
+ __pm_clk_remove(ce);
}
}
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_RUNTIME
+
/**
- * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
+ * pm_clk_suspend - Disable clocks in a device's PM clock list.
* @dev: Device to disable the clocks for.
*/
-int pm_runtime_clk_suspend(struct device *dev)
+int pm_clk_suspend(struct device *dev)
{
- struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce;
+ unsigned long flags;
dev_dbg(dev, "%s()\n", __func__);
- if (!prd)
+ if (!psd)
return 0;
- mutex_lock(&prd->lock);
-
- list_for_each_entry_reverse(ce, &prd->clock_list, node) {
- if (ce->status == PCE_STATUS_NONE)
- pm_runtime_clk_acquire(dev, ce);
+ spin_lock_irqsave(&psd->lock, flags);
+ list_for_each_entry_reverse(ce, &psd->clock_list, node) {
if (ce->status < PCE_STATUS_ERROR) {
- clk_disable(ce->clk);
+ if (ce->status == PCE_STATUS_ENABLED)
+ clk_disable(ce->clk);
ce->status = PCE_STATUS_ACQUIRED;
}
}
- mutex_unlock(&prd->lock);
+ spin_unlock_irqrestore(&psd->lock, flags);
return 0;
}
/**
- * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
+ * pm_clk_resume - Enable clocks in a device's PM clock list.
* @dev: Device to enable the clocks for.
*/
-int pm_runtime_clk_resume(struct device *dev)
+int pm_clk_resume(struct device *dev)
{
- struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce;
+ unsigned long flags;
dev_dbg(dev, "%s()\n", __func__);
- if (!prd)
+ if (!psd)
return 0;
- mutex_lock(&prd->lock);
-
- list_for_each_entry(ce, &prd->clock_list, node) {
- if (ce->status == PCE_STATUS_NONE)
- pm_runtime_clk_acquire(dev, ce);
+ spin_lock_irqsave(&psd->lock, flags);
+ list_for_each_entry(ce, &psd->clock_list, node) {
if (ce->status < PCE_STATUS_ERROR) {
clk_enable(ce->clk);
ce->status = PCE_STATUS_ENABLED;
}
}
- mutex_unlock(&prd->lock);
+ spin_unlock_irqrestore(&psd->lock, flags);
return 0;
}
/**
- * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * pm_clk_notify - Notify routine for device addition and removal.
* @nb: Notifier block object this function is a member of.
* @action: Operation being carried out by the caller.
* @data: Device the routine is being run for.
*
* For this function to work, @nb must be a member of an object of type
* struct pm_clk_notifier_block containing all of the requisite data.
- * Specifically, the pwr_domain member of that object is copied to the device's
- * pwr_domain field and its con_ids member is used to populate the device's list
- * of runtime PM clocks, depending on @action.
+ * Specifically, the pm_domain member of that object is copied to the device's
+ * pm_domain field and its con_ids member is used to populate the device's list
+ * of PM clocks, depending on @action.
*
- * If the device's pwr_domain field is already populated with a value different
+ * If the device's pm_domain field is already populated with a value different
* from the one stored in the struct pm_clk_notifier_block object, the function
* does nothing.
*/
-static int pm_runtime_clk_notify(struct notifier_block *nb,
+static int pm_clk_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct pm_clk_notifier_block *clknb;
@@ -300,28 +299,28 @@ static int pm_runtime_clk_notify(struct notifier_block *nb,
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
- if (dev->pwr_domain)
+ if (dev->pm_domain)
break;
- error = pm_runtime_clk_init(dev);
+ error = pm_clk_create(dev);
if (error)
break;
- dev->pwr_domain = clknb->pwr_domain;
+ dev->pm_domain = clknb->pm_domain;
if (clknb->con_ids[0]) {
for (con_id = clknb->con_ids; *con_id; con_id++)
- pm_runtime_clk_add(dev, *con_id);
+ pm_clk_add(dev, *con_id);
} else {
- pm_runtime_clk_add(dev, NULL);
+ pm_clk_add(dev, NULL);
}
break;
case BUS_NOTIFY_DEL_DEVICE:
- if (dev->pwr_domain != clknb->pwr_domain)
+ if (dev->pm_domain != clknb->pm_domain)
break;
- dev->pwr_domain = NULL;
- pm_runtime_clk_destroy(dev);
+ dev->pm_domain = NULL;
+ pm_clk_destroy(dev);
break;
}
@@ -330,6 +329,62 @@ static int pm_runtime_clk_notify(struct notifier_block *nb,
#else /* !CONFIG_PM_RUNTIME */
+#ifdef CONFIG_PM
+
+/**
+ * pm_clk_suspend - Disable clocks in a device's PM clock list.
+ * @dev: Device to disable the clocks for.
+ */
+int pm_clk_suspend(struct device *dev)
+{
+ struct pm_subsys_data *psd = dev_to_psd(dev);
+ struct pm_clock_entry *ce;
+ unsigned long flags;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ /* If there is no driver, the clocks are already disabled. */
+ if (!psd || !dev->driver)
+ return 0;
+
+ spin_lock_irqsave(&psd->lock, flags);
+
+ list_for_each_entry_reverse(ce, &psd->clock_list, node)
+ clk_disable(ce->clk);
+
+ spin_unlock_irqrestore(&psd->lock, flags);
+
+ return 0;
+}
+
+/**
+ * pm_clk_resume - Enable clocks in a device's PM clock list.
+ * @dev: Device to enable the clocks for.
+ */
+int pm_clk_resume(struct device *dev)
+{
+ struct pm_subsys_data *psd = dev_to_psd(dev);
+ struct pm_clock_entry *ce;
+ unsigned long flags;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ /* If there is no driver, the clocks should remain disabled. */
+ if (!psd || !dev->driver)
+ return 0;
+
+ spin_lock_irqsave(&psd->lock, flags);
+
+ list_for_each_entry(ce, &psd->clock_list, node)
+ clk_enable(ce->clk);
+
+ spin_unlock_irqrestore(&psd->lock, flags);
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
/**
* enable_clock - Enable a device clock.
* @dev: Device whose clock is to be enabled.
@@ -365,7 +420,7 @@ static void disable_clock(struct device *dev, const char *con_id)
}
/**
- * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * pm_clk_notify - Notify routine for device addition and removal.
* @nb: Notifier block object this function is a member of.
* @action: Operation being carried out by the caller.
* @data: Device the routine is being run for.
@@ -375,7 +430,7 @@ static void disable_clock(struct device *dev, const char *con_id)
* Specifically, the con_ids member of that object is used to enable or disable
* the device's clocks, depending on @action.
*/
-static int pm_runtime_clk_notify(struct notifier_block *nb,
+static int pm_clk_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct pm_clk_notifier_block *clknb;
@@ -411,21 +466,21 @@ static int pm_runtime_clk_notify(struct notifier_block *nb,
#endif /* !CONFIG_PM_RUNTIME */
/**
- * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks.
+ * pm_clk_add_notifier - Add bus type notifier for power management clocks.
* @bus: Bus type to add the notifier to.
* @clknb: Notifier to be added to the given bus type.
*
* The nb member of @clknb is not expected to be initialized and its
- * notifier_call member will be replaced with pm_runtime_clk_notify(). However,
+ * notifier_call member will be replaced with pm_clk_notify(). However,
* the remaining members of @clknb should be populated prior to calling this
* routine.
*/
-void pm_runtime_clk_add_notifier(struct bus_type *bus,
+void pm_clk_add_notifier(struct bus_type *bus,
struct pm_clk_notifier_block *clknb)
{
if (!bus || !clknb)
return;
- clknb->nb.notifier_call = pm_runtime_clk_notify;
+ clknb->nb.notifier_call = pm_clk_notify;
bus_register_notifier(bus, &clknb->nb);
}
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
new file mode 100644
index 000000000000..4af7c1cbf909
--- /dev/null
+++ b/drivers/base/power/common.c
@@ -0,0 +1,86 @@
+/*
+ * drivers/base/power/common.c - Common device power management code.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/pm_clock.h>
+
+/**
+ * dev_pm_get_subsys_data - Create or refcount power.subsys_data for device.
+ * @dev: Device to handle.
+ *
+ * If power.subsys_data is NULL, point it to a new object, otherwise increment
+ * its reference counter. Return 1 if a new object has been created, otherwise
+ * return 0 or error code.
+ */
+int dev_pm_get_subsys_data(struct device *dev)
+{
+ struct pm_subsys_data *psd;
+ int ret = 0;
+
+ psd = kzalloc(sizeof(*psd), GFP_KERNEL);
+ if (!psd)
+ return -ENOMEM;
+
+ spin_lock_irq(&dev->power.lock);
+
+ if (dev->power.subsys_data) {
+ dev->power.subsys_data->refcount++;
+ } else {
+ spin_lock_init(&psd->lock);
+ psd->refcount = 1;
+ dev->power.subsys_data = psd;
+ pm_clk_init(dev);
+ psd = NULL;
+ ret = 1;
+ }
+
+ spin_unlock_irq(&dev->power.lock);
+
+ /* kfree() verifies that its argument is nonzero. */
+ kfree(psd);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data);
+
+/**
+ * dev_pm_put_subsys_data - Drop reference to power.subsys_data.
+ * @dev: Device to handle.
+ *
+ * If the reference counter of power.subsys_data is zero after dropping the
+ * reference, power.subsys_data is removed. Return 1 if that happens or 0
+ * otherwise.
+ */
+int dev_pm_put_subsys_data(struct device *dev)
+{
+ struct pm_subsys_data *psd;
+ int ret = 0;
+
+ spin_lock_irq(&dev->power.lock);
+
+ psd = dev_to_psd(dev);
+ if (!psd) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (--psd->refcount == 0) {
+ dev->power.subsys_data = NULL;
+ kfree(psd);
+ ret = 1;
+ }
+
+ out:
+ spin_unlock_irq(&dev->power.lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
new file mode 100644
index 000000000000..6790cf7eba5a
--- /dev/null
+++ b/drivers/base/power/domain.c
@@ -0,0 +1,1328 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/suspend.h>
+
+static LIST_HEAD(gpd_list);
+static DEFINE_MUTEX(gpd_list_lock);
+
+#ifdef CONFIG_PM
+
+static struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+ if (IS_ERR_OR_NULL(dev->pm_domain))
+ return ERR_PTR(-EINVAL);
+
+ return pd_to_genpd(dev->pm_domain);
+}
+
+static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+ bool ret = false;
+
+ if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
+ ret = !!atomic_dec_and_test(&genpd->sd_count);
+
+ return ret;
+}
+
+static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
+{
+ atomic_inc(&genpd->sd_count);
+ smp_mb__after_atomic_inc();
+}
+
+static void genpd_acquire_lock(struct generic_pm_domain *genpd)
+{
+ DEFINE_WAIT(wait);
+
+ mutex_lock(&genpd->lock);
+ /*
+ * Wait for the domain to transition into either the active,
+ * or the power off state.
+ */
+ for (;;) {
+ prepare_to_wait(&genpd->status_wait_queue, &wait,
+ TASK_UNINTERRUPTIBLE);
+ if (genpd->status == GPD_STATE_ACTIVE
+ || genpd->status == GPD_STATE_POWER_OFF)
+ break;
+ mutex_unlock(&genpd->lock);
+
+ schedule();
+
+ mutex_lock(&genpd->lock);
+ }
+ finish_wait(&genpd->status_wait_queue, &wait);
+}
+
+static void genpd_release_lock(struct generic_pm_domain *genpd)
+{
+ mutex_unlock(&genpd->lock);
+}
+
+static void genpd_set_active(struct generic_pm_domain *genpd)
+{
+ if (genpd->resume_count == 0)
+ genpd->status = GPD_STATE_ACTIVE;
+}
+
+/**
+ * __pm_genpd_poweron - Restore power to a given PM domain and its masters.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its masters so that it is possible to
+ * resume a device belonging to it.
+ */
+int __pm_genpd_poweron(struct generic_pm_domain *genpd)
+ __releases(&genpd->lock) __acquires(&genpd->lock)
+{
+ struct gpd_link *link;
+ DEFINE_WAIT(wait);
+ int ret = 0;
+
+ /* If the domain's master is being waited for, we have to wait too. */
+ for (;;) {
+ prepare_to_wait(&genpd->status_wait_queue, &wait,
+ TASK_UNINTERRUPTIBLE);
+ if (genpd->status != GPD_STATE_WAIT_MASTER)
+ break;
+ mutex_unlock(&genpd->lock);
+
+ schedule();
+
+ mutex_lock(&genpd->lock);
+ }
+ finish_wait(&genpd->status_wait_queue, &wait);
+
+ if (genpd->status == GPD_STATE_ACTIVE
+ || (genpd->prepared_count > 0 && genpd->suspend_power_off))
+ return 0;
+
+ if (genpd->status != GPD_STATE_POWER_OFF) {
+ genpd_set_active(genpd);
+ return 0;
+ }
+
+ /*
+ * The list is guaranteed not to change while the loop below is being
+ * executed, unless one of the masters' .power_on() callbacks fiddles
+ * with it.
+ */
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ genpd_sd_counter_inc(link->master);
+ genpd->status = GPD_STATE_WAIT_MASTER;
+
+ mutex_unlock(&genpd->lock);
+
+ ret = pm_genpd_poweron(link->master);
+
+ mutex_lock(&genpd->lock);
+
+ /*
+ * The "wait for parent" status is guaranteed not to change
+ * while the master is powering on.
+ */
+ genpd->status = GPD_STATE_POWER_OFF;
+ wake_up_all(&genpd->status_wait_queue);
+ if (ret) {
+ genpd_sd_counter_dec(link->master);
+ goto err;
+ }
+ }
+
+ if (genpd->power_on) {
+ ret = genpd->power_on(genpd);
+ if (ret)
+ goto err;
+ }
+
+ genpd_set_active(genpd);
+
+ return 0;
+
+ err:
+ list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node)
+ genpd_sd_counter_dec(link->master);
+
+ return ret;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its masters.
+ * @genpd: PM domain to power up.
+ */
+int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+ int ret;
+
+ mutex_lock(&genpd->lock);
+ ret = __pm_genpd_poweron(genpd);
+ mutex_unlock(&genpd->lock);
+ return ret;
+}
+
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_RUNTIME
+
+/**
+ * __pm_genpd_save_device - Save the pre-suspend state of a device.
+ * @pdd: Domain data of the device to save the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static int __pm_genpd_save_device(struct pm_domain_data *pdd,
+ struct generic_pm_domain *genpd)
+ __releases(&genpd->lock) __acquires(&genpd->lock)
+{
+ struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
+ struct device *dev = pdd->dev;
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (gpd_data->need_restore)
+ return 0;
+
+ mutex_unlock(&genpd->lock);
+
+ if (drv && drv->pm && drv->pm->runtime_suspend) {
+ if (genpd->start_device)
+ genpd->start_device(dev);
+
+ ret = drv->pm->runtime_suspend(dev);
+
+ if (genpd->stop_device)
+ genpd->stop_device(dev);
+ }
+
+ mutex_lock(&genpd->lock);
+
+ if (!ret)
+ gpd_data->need_restore = true;
+
+ return ret;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @pdd: Domain data of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct pm_domain_data *pdd,
+ struct generic_pm_domain *genpd)
+ __releases(&genpd->lock) __acquires(&genpd->lock)
+{
+ struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
+ struct device *dev = pdd->dev;
+ struct device_driver *drv = dev->driver;
+
+ if (!gpd_data->need_restore)
+ return;
+
+ mutex_unlock(&genpd->lock);
+
+ if (drv && drv->pm && drv->pm->runtime_resume) {
+ if (genpd->start_device)
+ genpd->start_device(dev);
+
+ drv->pm->runtime_resume(dev);
+
+ if (genpd->stop_device)
+ genpd->stop_device(dev);
+ }
+
+ mutex_lock(&genpd->lock);
+
+ gpd_data->need_restore = false;
+}
+
+/**
+ * genpd_abort_poweroff - Check if a PM domain power off should be aborted.
+ * @genpd: PM domain to check.
+ *
+ * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
+ * a "power off" operation, which means that a "power on" has occured in the
+ * meantime, or if its resume_count field is different from zero, which means
+ * that one of its devices has been resumed in the meantime.
+ */
+static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
+{
+ return genpd->status == GPD_STATE_WAIT_MASTER
+ || genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
+}
+
+/**
+ * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
+ * @genpd: PM domait to power off.
+ *
+ * Queue up the execution of pm_genpd_poweroff() unless it's already been done
+ * before.
+ */
+void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
+{
+ if (!work_pending(&genpd->power_off_work))
+ queue_work(pm_wq, &genpd->power_off_work);
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+ __releases(&genpd->lock) __acquires(&genpd->lock)
+{
+ struct pm_domain_data *pdd;
+ struct gpd_link *link;
+ unsigned int not_suspended;
+ int ret = 0;
+
+ start:
+ /*
+ * Do not try to power off the domain in the following situations:
+ * (1) The domain is already in the "power off" state.
+ * (2) The domain is waiting for its master to power up.
+ * (3) One of the domain's devices is being resumed right now.
+ * (4) System suspend is in progress.
+ */
+ if (genpd->status == GPD_STATE_POWER_OFF
+ || genpd->status == GPD_STATE_WAIT_MASTER
+ || genpd->resume_count > 0 || genpd->prepared_count > 0)
+ return 0;
+
+ if (atomic_read(&genpd->sd_count) > 0)
+ return -EBUSY;
+
+ not_suspended = 0;
+ list_for_each_entry(pdd, &genpd->dev_list, list_node)
+ if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
+ || pdd->dev->power.irq_safe))
+ not_suspended++;
+
+ if (not_suspended > genpd->in_progress)
+ return -EBUSY;
+
+ if (genpd->poweroff_task) {
+ /*
+ * Another instance of pm_genpd_poweroff() is executing
+ * callbacks, so tell it to start over and return.
+ */
+ genpd->status = GPD_STATE_REPEAT;
+ return 0;
+ }
+
+ if (genpd->gov && genpd->gov->power_down_ok) {
+ if (!genpd->gov->power_down_ok(&genpd->domain))
+ return -EAGAIN;
+ }
+
+ genpd->status = GPD_STATE_BUSY;
+ genpd->poweroff_task = current;
+
+ list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
+ ret = atomic_read(&genpd->sd_count) == 0 ?
+ __pm_genpd_save_device(pdd, genpd) : -EBUSY;
+
+ if (genpd_abort_poweroff(genpd))
+ goto out;
+
+ if (ret) {
+ genpd_set_active(genpd);
+ goto out;
+ }
+
+ if (genpd->status == GPD_STATE_REPEAT) {
+ genpd->poweroff_task = NULL;
+ goto start;
+ }
+ }
+
+ if (genpd->power_off) {
+ if (atomic_read(&genpd->sd_count) > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /*
+ * If sd_count > 0 at this point, one of the subdomains hasn't
+ * managed to call pm_genpd_poweron() for the master yet after
+ * incrementing it. In that case pm_genpd_poweron() will wait
+ * for us to drop the lock, so we can call .power_off() and let
+ * the pm_genpd_poweron() restore power for us (this shouldn't
+ * happen very often).
+ */
+ ret = genpd->power_off(genpd);
+ if (ret == -EBUSY) {
+ genpd_set_active(genpd);
+ goto out;
+ }
+ }
+
+ genpd->status = GPD_STATE_POWER_OFF;
+
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ genpd_sd_counter_dec(link->master);
+ genpd_queue_power_off_work(link->master);
+ }
+
+ out:
+ genpd->poweroff_task = NULL;
+ wake_up_all(&genpd->status_wait_queue);
+ return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+ struct generic_pm_domain *genpd;
+
+ genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+ genpd_acquire_lock(genpd);
+ pm_genpd_poweroff(genpd);
+ genpd_release_lock(genpd);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ might_sleep_if(!genpd->dev_irq_safe);
+
+ if (genpd->stop_device) {
+ int ret = genpd->stop_device(dev);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * If power.irq_safe is set, this routine will be run with interrupts
+ * off, so it can't use mutexes.
+ */
+ if (dev->power.irq_safe)
+ return 0;
+
+ mutex_lock(&genpd->lock);
+ genpd->in_progress++;
+ pm_genpd_poweroff(genpd);
+ genpd->in_progress--;
+ mutex_unlock(&genpd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+ DEFINE_WAIT(wait);
+ int ret;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ might_sleep_if(!genpd->dev_irq_safe);
+
+ /* If power.irq_safe, the PM domain is never powered off. */
+ if (dev->power.irq_safe)
+ goto out;
+
+ mutex_lock(&genpd->lock);
+ ret = __pm_genpd_poweron(genpd);
+ if (ret) {
+ mutex_unlock(&genpd->lock);
+ return ret;
+ }
+ genpd->status = GPD_STATE_BUSY;
+ genpd->resume_count++;
+ for (;;) {
+ prepare_to_wait(&genpd->status_wait_queue, &wait,
+ TASK_UNINTERRUPTIBLE);
+ /*
+ * If current is the powering off task, we have been called
+ * reentrantly from one of the device callbacks, so we should
+ * not wait.
+ */
+ if (!genpd->poweroff_task || genpd->poweroff_task == current)
+ break;
+ mutex_unlock(&genpd->lock);
+
+ schedule();
+
+ mutex_lock(&genpd->lock);
+ }
+ finish_wait(&genpd->status_wait_queue, &wait);
+ __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
+ genpd->resume_count--;
+ genpd_set_active(genpd);
+ wake_up_all(&genpd->status_wait_queue);
+ mutex_unlock(&genpd->lock);
+
+ out:
+ if (genpd->start_device)
+ genpd->start_device(dev);
+
+ return 0;
+}
+
+/**
+ * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
+ */
+void pm_genpd_poweroff_unused(void)
+{
+ struct generic_pm_domain *genpd;
+
+ mutex_lock(&gpd_list_lock);
+
+ list_for_each_entry(genpd, &gpd_list, gpd_list_node)
+ genpd_queue_power_off_work(genpd);
+
+ mutex_unlock(&gpd_list_lock);
+}
+
+#else
+
+static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+
+#define pm_genpd_runtime_suspend NULL
+#define pm_genpd_runtime_resume NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so. Also, in that case propagate to its masters.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+ struct gpd_link *link;
+
+ if (genpd->status == GPD_STATE_POWER_OFF)
+ return;
+
+ if (genpd->suspended_count != genpd->device_count
+ || atomic_read(&genpd->sd_count) > 0)
+ return;
+
+ if (genpd->power_off)
+ genpd->power_off(genpd);
+
+ genpd->status = GPD_STATE_POWER_OFF;
+
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ genpd_sd_counter_dec(link->master);
+ pm_genpd_sync_poweroff(link->master);
+ }
+}
+
+/**
+ * resume_needed - Check whether to resume a device before system suspend.
+ * @dev: Device to check.
+ * @genpd: PM domain the device belongs to.
+ *
+ * There are two cases in which a device that can wake up the system from sleep
+ * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled
+ * to wake up the system and it has to remain active for this purpose while the
+ * system is in the sleep state and (2) if the device is not enabled to wake up
+ * the system from sleep states and it generally doesn't generate wakeup signals
+ * by itself (those signals are generated on its behalf by other parts of the
+ * system). In the latter case it may be necessary to reconfigure the device's
+ * wakeup settings during system suspend, because it may have been set up to
+ * signal remote wakeup from the system's working state as needed by runtime PM.
+ * Return 'true' in either of the above cases.
+ */
+static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
+{
+ bool active_wakeup;
+
+ if (!device_can_wakeup(dev))
+ return false;
+
+ active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
+ return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+ int ret;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ /*
+ * If a wakeup request is pending for the device, it should be woken up
+ * at this point and a system wakeup event should be reported if it's
+ * set up to wake up the system from sleep states.
+ */
+ pm_runtime_get_noresume(dev);
+ if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
+ pm_wakeup_event(dev, 0);
+
+ if (pm_wakeup_pending()) {
+ pm_runtime_put_sync(dev);
+ return -EBUSY;
+ }
+
+ if (resume_needed(dev, genpd))
+ pm_runtime_resume(dev);
+
+ genpd_acquire_lock(genpd);
+
+ if (genpd->prepared_count++ == 0)
+ genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
+
+ genpd_release_lock(genpd);
+
+ if (genpd->suspend_power_off) {
+ pm_runtime_put_noidle(dev);
+ return 0;
+ }
+
+ /*
+ * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
+ * so pm_genpd_poweron() will return immediately, but if the device
+ * is suspended (e.g. it's been stopped by .stop_device()), we need
+ * to make it operational.
+ */
+ pm_runtime_resume(dev);
+ __pm_runtime_disable(dev, false);
+
+ ret = pm_generic_prepare(dev);
+ if (ret) {
+ mutex_lock(&genpd->lock);
+
+ if (--genpd->prepared_count == 0)
+ genpd->suspend_power_off = false;
+
+ mutex_unlock(&genpd->lock);
+ pm_runtime_enable(dev);
+ }
+
+ pm_runtime_put_sync(dev);
+ return ret;
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+ int ret;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (genpd->suspend_power_off)
+ return 0;
+
+ ret = pm_generic_suspend_noirq(dev);
+ if (ret)
+ return ret;
+
+ if (dev->power.wakeup_path
+ && genpd->active_wakeup && genpd->active_wakeup(dev))
+ return 0;
+
+ if (genpd->stop_device)
+ genpd->stop_device(dev);
+
+ /*
+ * Since all of the "noirq" callbacks are executed sequentially, it is
+ * guaranteed that this function will never run twice in parallel for
+ * the same PM domain, so it is not necessary to use locking here.
+ */
+ genpd->suspended_count++;
+ pm_genpd_sync_poweroff(genpd);
+
+ return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (genpd->suspend_power_off)
+ return 0;
+
+ /*
+ * Since all of the "noirq" callbacks are executed sequentially, it is
+ * guaranteed that this function will never run twice in parallel for
+ * the same PM domain, so it is not necessary to use locking here.
+ */
+ pm_genpd_poweron(genpd);
+ genpd->suspended_count--;
+ if (genpd->start_device)
+ genpd->start_device(dev);
+
+ return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+ int ret;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (genpd->suspend_power_off)
+ return 0;
+
+ ret = pm_generic_freeze_noirq(dev);
+ if (ret)
+ return ret;
+
+ if (genpd->stop_device)
+ genpd->stop_device(dev);
+
+ return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (genpd->suspend_power_off)
+ return 0;
+
+ if (genpd->start_device)
+ genpd->start_device(dev);
+
+ return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+ int ret;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (genpd->suspend_power_off)
+ return 0;
+
+ ret = pm_generic_poweroff_noirq(dev);
+ if (ret)
+ return ret;
+
+ if (dev->power.wakeup_path
+ && genpd->active_wakeup && genpd->active_wakeup(dev))
+ return 0;
+
+ if (genpd->stop_device)
+ genpd->stop_device(dev);
+
+ /*
+ * Since all of the "noirq" callbacks are executed sequentially, it is
+ * guaranteed that this function will never run twice in parallel for
+ * the same PM domain, so it is not necessary to use locking here.
+ */
+ genpd->suspended_count++;
+ pm_genpd_sync_poweroff(genpd);
+
+ return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ /*
+ * Since all of the "noirq" callbacks are executed sequentially, it is
+ * guaranteed that this function will never run twice in parallel for
+ * the same PM domain, so it is not necessary to use locking here.
+ */
+ genpd->status = GPD_STATE_POWER_OFF;
+ if (genpd->suspend_power_off) {
+ /*
+ * The boot kernel might put the domain into the power on state,
+ * so make sure it really is powered off.
+ */
+ if (genpd->power_off)
+ genpd->power_off(genpd);
+ return 0;
+ }
+
+ pm_genpd_poweron(genpd);
+ genpd->suspended_count--;
+ if (genpd->start_device)
+ genpd->start_device(dev);
+
+ return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+ bool run_complete;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return;
+
+ mutex_lock(&genpd->lock);
+
+ run_complete = !genpd->suspend_power_off;
+ if (--genpd->prepared_count == 0)
+ genpd->suspend_power_off = false;
+
+ mutex_unlock(&genpd->lock);
+
+ if (run_complete) {
+ pm_generic_complete(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+ }
+}
+
+#else
+
+#define pm_genpd_prepare NULL
+#define pm_genpd_suspend NULL
+#define pm_genpd_suspend_noirq NULL
+#define pm_genpd_resume_noirq NULL
+#define pm_genpd_resume NULL
+#define pm_genpd_freeze NULL
+#define pm_genpd_freeze_noirq NULL
+#define pm_genpd_thaw_noirq NULL
+#define pm_genpd_thaw NULL
+#define pm_genpd_dev_poweroff_noirq NULL
+#define pm_genpd_dev_poweroff NULL
+#define pm_genpd_restore_noirq NULL
+#define pm_genpd_restore NULL
+#define pm_genpd_complete NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+ struct generic_pm_domain_data *gpd_data;
+ struct pm_domain_data *pdd;
+ int ret = 0;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+ return -EINVAL;
+
+ genpd_acquire_lock(genpd);
+
+ if (genpd->status == GPD_STATE_POWER_OFF) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (genpd->prepared_count > 0) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ list_for_each_entry(pdd, &genpd->dev_list, list_node)
+ if (pdd->dev == dev) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
+ if (!gpd_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ genpd->device_count++;
+
+ dev->pm_domain = &genpd->domain;
+ dev_pm_get_subsys_data(dev);
+ dev->power.subsys_data->domain_data = &gpd_data->base;
+ gpd_data->base.dev = dev;
+ gpd_data->need_restore = false;
+ list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
+
+ out:
+ genpd_release_lock(genpd);
+
+ return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+ struct device *dev)
+{
+ struct pm_domain_data *pdd;
+ int ret = -EINVAL;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+ return -EINVAL;
+
+ genpd_acquire_lock(genpd);
+
+ if (genpd->prepared_count > 0) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+ if (pdd->dev != dev)
+ continue;
+
+ list_del_init(&pdd->list_node);
+ pdd->dev = NULL;
+ dev_pm_put_subsys_data(dev);
+ dev->pm_domain = NULL;
+ kfree(to_gpd_data(pdd));
+
+ genpd->device_count--;
+
+ ret = 0;
+ break;
+ }
+
+ out:
+ genpd_release_lock(genpd);
+
+ return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+ struct generic_pm_domain *subdomain)
+{
+ struct gpd_link *link;
+ int ret = 0;
+
+ if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
+ return -EINVAL;
+
+ start:
+ genpd_acquire_lock(genpd);
+ mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
+
+ if (subdomain->status != GPD_STATE_POWER_OFF
+ && subdomain->status != GPD_STATE_ACTIVE) {
+ mutex_unlock(&subdomain->lock);
+ genpd_release_lock(genpd);
+ goto start;
+ }
+
+ if (genpd->status == GPD_STATE_POWER_OFF
+ && subdomain->status != GPD_STATE_POWER_OFF) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ if (link->slave == subdomain && link->master == genpd) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ link = kzalloc(sizeof(*link), GFP_KERNEL);
+ if (!link) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ link->master = genpd;
+ list_add_tail(&link->master_node, &genpd->master_links);
+ link->slave = subdomain;
+ list_add_tail(&link->slave_node, &subdomain->slave_links);
+ if (subdomain->status != GPD_STATE_POWER_OFF)
+ genpd_sd_counter_inc(genpd);
+
+ out:
+ mutex_unlock(&subdomain->lock);
+ genpd_release_lock(genpd);
+
+ return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @subdomain: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+ struct generic_pm_domain *subdomain)
+{
+ struct gpd_link *link;
+ int ret = -EINVAL;
+
+ if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
+ return -EINVAL;
+
+ start:
+ genpd_acquire_lock(genpd);
+
+ list_for_each_entry(link, &genpd->master_links, master_node) {
+ if (link->slave != subdomain)
+ continue;
+
+ mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
+
+ if (subdomain->status != GPD_STATE_POWER_OFF
+ && subdomain->status != GPD_STATE_ACTIVE) {
+ mutex_unlock(&subdomain->lock);
+ genpd_release_lock(genpd);
+ goto start;
+ }
+
+ list_del(&link->master_node);
+ list_del(&link->slave_node);
+ kfree(link);
+ if (subdomain->status != GPD_STATE_POWER_OFF)
+ genpd_sd_counter_dec(genpd);
+
+ mutex_unlock(&subdomain->lock);
+
+ ret = 0;
+ break;
+ }
+
+ genpd_release_lock(genpd);
+
+ return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+ struct dev_power_governor *gov, bool is_off)
+{
+ if (IS_ERR_OR_NULL(genpd))
+ return;
+
+ INIT_LIST_HEAD(&genpd->master_links);
+ INIT_LIST_HEAD(&genpd->slave_links);
+ INIT_LIST_HEAD(&genpd->dev_list);
+ mutex_init(&genpd->lock);
+ genpd->gov = gov;
+ INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+ genpd->in_progress = 0;
+ atomic_set(&genpd->sd_count, 0);
+ genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
+ init_waitqueue_head(&genpd->status_wait_queue);
+ genpd->poweroff_task = NULL;
+ genpd->resume_count = 0;
+ genpd->device_count = 0;
+ genpd->suspended_count = 0;
+ genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+ genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+ genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+ genpd->domain.ops.prepare = pm_genpd_prepare;
+ genpd->domain.ops.suspend = pm_genpd_suspend;
+ genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+ genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+ genpd->domain.ops.resume = pm_genpd_resume;
+ genpd->domain.ops.freeze = pm_genpd_freeze;
+ genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+ genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+ genpd->domain.ops.thaw = pm_genpd_thaw;
+ genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+ genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+ genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+ genpd->domain.ops.restore = pm_genpd_restore;
+ genpd->domain.ops.complete = pm_genpd_complete;
+ mutex_lock(&gpd_list_lock);
+ list_add(&genpd->gpd_list_node, &gpd_list);
+ mutex_unlock(&gpd_list_lock);
+}
diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c
index cb3bb368681c..265a0ee3b49e 100644
--- a/drivers/base/power/generic_ops.c
+++ b/drivers/base/power/generic_ops.c
@@ -8,6 +8,7 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/export.h>
#ifdef CONFIG_PM_RUNTIME
/**
@@ -94,12 +95,13 @@ int pm_generic_prepare(struct device *dev)
* __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
* @dev: Device to handle.
* @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
*
* If the device has not been suspended at run time, execute the
* suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
* return its error code. Otherwise, return zero.
*/
-static int __pm_generic_call(struct device *dev, int event)
+static int __pm_generic_call(struct device *dev, int event, bool noirq)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int (*callback)(struct device *);
@@ -109,16 +111,16 @@ static int __pm_generic_call(struct device *dev, int event)
switch (event) {
case PM_EVENT_SUSPEND:
- callback = pm->suspend;
+ callback = noirq ? pm->suspend_noirq : pm->suspend;
break;
case PM_EVENT_FREEZE:
- callback = pm->freeze;
+ callback = noirq ? pm->freeze_noirq : pm->freeze;
break;
case PM_EVENT_HIBERNATE:
- callback = pm->poweroff;
+ callback = noirq ? pm->poweroff_noirq : pm->poweroff;
break;
case PM_EVENT_THAW:
- callback = pm->thaw;
+ callback = noirq ? pm->thaw_noirq : pm->thaw;
break;
default:
callback = NULL;
@@ -129,42 +131,82 @@ static int __pm_generic_call(struct device *dev, int event)
}
/**
+ * pm_generic_suspend_noirq - Generic suspend_noirq callback for subsystems.
+ * @dev: Device to suspend.
+ */
+int pm_generic_suspend_noirq(struct device *dev)
+{
+ return __pm_generic_call(dev, PM_EVENT_SUSPEND, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
+
+/**
* pm_generic_suspend - Generic suspend callback for subsystems.
* @dev: Device to suspend.
*/
int pm_generic_suspend(struct device *dev)
{
- return __pm_generic_call(dev, PM_EVENT_SUSPEND);
+ return __pm_generic_call(dev, PM_EVENT_SUSPEND, false);
}
EXPORT_SYMBOL_GPL(pm_generic_suspend);
/**
+ * pm_generic_freeze_noirq - Generic freeze_noirq callback for subsystems.
+ * @dev: Device to freeze.
+ */
+int pm_generic_freeze_noirq(struct device *dev)
+{
+ return __pm_generic_call(dev, PM_EVENT_FREEZE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
+
+/**
* pm_generic_freeze - Generic freeze callback for subsystems.
* @dev: Device to freeze.
*/
int pm_generic_freeze(struct device *dev)
{
- return __pm_generic_call(dev, PM_EVENT_FREEZE);
+ return __pm_generic_call(dev, PM_EVENT_FREEZE, false);
}
EXPORT_SYMBOL_GPL(pm_generic_freeze);
/**
+ * pm_generic_poweroff_noirq - Generic poweroff_noirq callback for subsystems.
+ * @dev: Device to handle.
+ */
+int pm_generic_poweroff_noirq(struct device *dev)
+{
+ return __pm_generic_call(dev, PM_EVENT_HIBERNATE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
+
+/**
* pm_generic_poweroff - Generic poweroff callback for subsystems.
* @dev: Device to handle.
*/
int pm_generic_poweroff(struct device *dev)
{
- return __pm_generic_call(dev, PM_EVENT_HIBERNATE);
+ return __pm_generic_call(dev, PM_EVENT_HIBERNATE, false);
}
EXPORT_SYMBOL_GPL(pm_generic_poweroff);
/**
+ * pm_generic_thaw_noirq - Generic thaw_noirq callback for subsystems.
+ * @dev: Device to thaw.
+ */
+int pm_generic_thaw_noirq(struct device *dev)
+{
+ return __pm_generic_call(dev, PM_EVENT_THAW, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
+
+/**
* pm_generic_thaw - Generic thaw callback for subsystems.
* @dev: Device to thaw.
*/
int pm_generic_thaw(struct device *dev)
{
- return __pm_generic_call(dev, PM_EVENT_THAW);
+ return __pm_generic_call(dev, PM_EVENT_THAW, false);
}
EXPORT_SYMBOL_GPL(pm_generic_thaw);
@@ -172,12 +214,13 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
* __pm_generic_resume - Generic resume/restore callback for subsystems.
* @dev: Device to handle.
* @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
*
* Execute the resume/resotre callback provided by the @dev's driver, if
* defined. If it returns 0, change the device's runtime PM status to 'active'.
* Return the callback's error code.
*/
-static int __pm_generic_resume(struct device *dev, int event)
+static int __pm_generic_resume(struct device *dev, int event, bool noirq)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int (*callback)(struct device *);
@@ -188,10 +231,10 @@ static int __pm_generic_resume(struct device *dev, int event)
switch (event) {
case PM_EVENT_RESUME:
- callback = pm->resume;
+ callback = noirq ? pm->resume_noirq : pm->resume;
break;
case PM_EVENT_RESTORE:
- callback = pm->restore;
+ callback = noirq ? pm->restore_noirq : pm->restore;
break;
default:
callback = NULL;
@@ -202,7 +245,7 @@ static int __pm_generic_resume(struct device *dev, int event)
return 0;
ret = callback(dev);
- if (!ret && pm_runtime_enabled(dev)) {
+ if (!ret && !noirq && pm_runtime_enabled(dev)) {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -212,22 +255,42 @@ static int __pm_generic_resume(struct device *dev, int event)
}
/**
+ * pm_generic_resume_noirq - Generic resume_noirq callback for subsystems.
+ * @dev: Device to resume.
+ */
+int pm_generic_resume_noirq(struct device *dev)
+{
+ return __pm_generic_resume(dev, PM_EVENT_RESUME, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
+
+/**
* pm_generic_resume - Generic resume callback for subsystems.
* @dev: Device to resume.
*/
int pm_generic_resume(struct device *dev)
{
- return __pm_generic_resume(dev, PM_EVENT_RESUME);
+ return __pm_generic_resume(dev, PM_EVENT_RESUME, false);
}
EXPORT_SYMBOL_GPL(pm_generic_resume);
/**
+ * pm_generic_restore_noirq - Generic restore_noirq callback for subsystems.
+ * @dev: Device to restore.
+ */
+int pm_generic_restore_noirq(struct device *dev)
+{
+ return __pm_generic_resume(dev, PM_EVENT_RESTORE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
+
+/**
* pm_generic_restore - Generic restore callback for subsystems.
* @dev: Device to restore.
*/
int pm_generic_restore(struct device *dev)
{
- return __pm_generic_resume(dev, PM_EVENT_RESTORE);
+ return __pm_generic_resume(dev, PM_EVENT_RESTORE, false);
}
EXPORT_SYMBOL_GPL(pm_generic_restore);
@@ -256,11 +319,17 @@ struct dev_pm_ops generic_subsys_pm_ops = {
#ifdef CONFIG_PM_SLEEP
.prepare = pm_generic_prepare,
.suspend = pm_generic_suspend,
+ .suspend_noirq = pm_generic_suspend_noirq,
.resume = pm_generic_resume,
+ .resume_noirq = pm_generic_resume_noirq,
.freeze = pm_generic_freeze,
+ .freeze_noirq = pm_generic_freeze_noirq,
.thaw = pm_generic_thaw,
+ .thaw_noirq = pm_generic_thaw_noirq,
.poweroff = pm_generic_poweroff,
+ .poweroff_noirq = pm_generic_poweroff_noirq,
.restore = pm_generic_restore,
+ .restore_noirq = pm_generic_restore_noirq,
.complete = pm_generic_complete,
#endif
#ifdef CONFIG_PM_RUNTIME
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 06f09bf89cb2..c3d2dfcf438d 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -19,6 +19,7 @@
#include <linux/device.h>
#include <linux/kallsyms.h>
+#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
@@ -46,6 +47,7 @@ LIST_HEAD(dpm_prepared_list);
LIST_HEAD(dpm_suspended_list);
LIST_HEAD(dpm_noirq_list);
+struct suspend_stats suspend_stats;
static DEFINE_MUTEX(dpm_list_mtx);
static pm_message_t pm_transition;
@@ -65,6 +67,7 @@ void device_pm_init(struct device *dev)
spin_lock_init(&dev->power.lock);
pm_runtime_init(dev);
INIT_LIST_HEAD(&dev->power.entry);
+ dev->power.power_state = PMSG_INVALID;
}
/**
@@ -96,6 +99,7 @@ void device_pm_add(struct device *dev)
dev_warn(dev, "parent %s should not be sleeping\n",
dev_name(dev->parent));
list_add_tail(&dev->power.entry, &dpm_list);
+ dev_pm_qos_constraints_init(dev);
mutex_unlock(&dpm_list_mtx);
}
@@ -109,6 +113,7 @@ void device_pm_remove(struct device *dev)
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
complete_all(&dev->power.completion);
mutex_lock(&dpm_list_mtx);
+ dev_pm_qos_constraints_destroy(dev);
list_del_init(&dev->power.entry);
mutex_unlock(&dpm_list_mtx);
device_wakeup_disable(dev);
@@ -425,9 +430,9 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)
TRACE_DEVICE(dev);
TRACE_RESUME(0);
- if (dev->pwr_domain) {
+ if (dev->pm_domain) {
pm_dev_dbg(dev, state, "EARLY power domain ");
- error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ error = pm_noirq_op(dev, &dev->pm_domain->ops, state);
} else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "EARLY type ");
error = pm_noirq_op(dev, dev->type->pm, state);
@@ -464,8 +469,12 @@ void dpm_resume_noirq(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
error = device_resume_noirq(dev, state);
- if (error)
+ if (error) {
+ suspend_stats.failed_resume_noirq++;
+ dpm_save_failed_step(SUSPEND_RESUME_NOIRQ);
+ dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, " early", error);
+ }
mutex_lock(&dpm_list_mtx);
put_device(dev);
@@ -505,6 +514,7 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))
static int device_resume(struct device *dev, pm_message_t state, bool async)
{
int error = 0;
+ bool put = false;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
@@ -521,9 +531,12 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
if (!dev->power.is_suspended)
goto Unlock;
- if (dev->pwr_domain) {
+ pm_runtime_enable(dev);
+ put = true;
+
+ if (dev->pm_domain) {
pm_dev_dbg(dev, state, "power domain ");
- error = pm_op(dev, &dev->pwr_domain->ops, state);
+ error = pm_op(dev, &dev->pm_domain->ops, state);
goto End;
}
@@ -563,6 +576,10 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
complete_all(&dev->power.completion);
TRACE_RESUME(error);
+
+ if (put)
+ pm_runtime_put_sync(dev);
+
return error;
}
@@ -618,8 +635,12 @@ void dpm_resume(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
error = device_resume(dev, state, false);
- if (error)
+ if (error) {
+ suspend_stats.failed_resume++;
+ dpm_save_failed_step(SUSPEND_RESUME);
+ dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, "", error);
+ }
mutex_lock(&dpm_list_mtx);
}
@@ -641,10 +662,10 @@ static void device_complete(struct device *dev, pm_message_t state)
{
device_lock(dev);
- if (dev->pwr_domain) {
+ if (dev->pm_domain) {
pm_dev_dbg(dev, state, "completing power domain ");
- if (dev->pwr_domain->ops.complete)
- dev->pwr_domain->ops.complete(dev);
+ if (dev->pm_domain->ops.complete)
+ dev->pm_domain->ops.complete(dev);
} else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "completing type ");
if (dev->type->pm->complete)
@@ -744,9 +765,9 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
{
int error;
- if (dev->pwr_domain) {
+ if (dev->pm_domain) {
pm_dev_dbg(dev, state, "LATE power domain ");
- error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ error = pm_noirq_op(dev, &dev->pm_domain->ops, state);
if (error)
return error;
} else if (dev->type && dev->type->pm) {
@@ -794,6 +815,9 @@ int dpm_suspend_noirq(pm_message_t state)
mutex_lock(&dpm_list_mtx);
if (error) {
pm_dev_err(dev, state, " late", error);
+ suspend_stats.failed_suspend_noirq++;
+ dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
+ dpm_save_failed_dev(dev_name(dev));
put_device(dev);
break;
}
@@ -843,19 +867,25 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
int error = 0;
dpm_wait_for_children(dev, async);
- device_lock(dev);
if (async_error)
- goto Unlock;
+ return 0;
+
+ pm_runtime_get_noresume(dev);
+ if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
+ pm_wakeup_event(dev, 0);
if (pm_wakeup_pending()) {
+ pm_runtime_put_sync(dev);
async_error = -EBUSY;
- goto Unlock;
+ return 0;
}
- if (dev->pwr_domain) {
+ device_lock(dev);
+
+ if (dev->pm_domain) {
pm_dev_dbg(dev, state, "power domain ");
- error = pm_op(dev, &dev->pwr_domain->ops, state);
+ error = pm_op(dev, &dev->pm_domain->ops, state);
goto End;
}
@@ -888,14 +918,22 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
}
End:
- dev->power.is_suspended = !error;
+ if (!error) {
+ dev->power.is_suspended = true;
+ if (dev->power.wakeup_path
+ && dev->parent && !dev->parent->power.ignore_children)
+ dev->parent->power.wakeup_path = true;
+ }
- Unlock:
device_unlock(dev);
complete_all(&dev->power.completion);
- if (error)
+ if (error) {
+ pm_runtime_put_sync(dev);
async_error = error;
+ } else if (dev->power.is_suspended) {
+ __pm_runtime_disable(dev, false);
+ }
return error;
}
@@ -906,8 +944,10 @@ static void async_suspend(void *data, async_cookie_t cookie)
int error;
error = __device_suspend(dev, pm_transition, true);
- if (error)
+ if (error) {
+ dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, pm_transition, " async", error);
+ }
put_device(dev);
}
@@ -950,6 +990,7 @@ int dpm_suspend(pm_message_t state)
mutex_lock(&dpm_list_mtx);
if (error) {
pm_dev_err(dev, state, "", error);
+ dpm_save_failed_dev(dev_name(dev));
put_device(dev);
break;
}
@@ -963,7 +1004,10 @@ int dpm_suspend(pm_message_t state)
async_synchronize_full();
if (!error)
error = async_error;
- if (!error)
+ if (error) {
+ suspend_stats.failed_suspend++;
+ dpm_save_failed_step(SUSPEND_SUSPEND);
+ } else
dpm_show_time(starttime, state, NULL);
return error;
}
@@ -982,11 +1026,13 @@ static int device_prepare(struct device *dev, pm_message_t state)
device_lock(dev);
- if (dev->pwr_domain) {
+ dev->power.wakeup_path = device_may_wakeup(dev);
+
+ if (dev->pm_domain) {
pm_dev_dbg(dev, state, "preparing power domain ");
- if (dev->pwr_domain->ops.prepare)
- error = dev->pwr_domain->ops.prepare(dev);
- suspend_report_result(dev->pwr_domain->ops.prepare, error);
+ if (dev->pm_domain->ops.prepare)
+ error = dev->pm_domain->ops.prepare(dev);
+ suspend_report_result(dev->pm_domain->ops.prepare, error);
if (error)
goto End;
} else if (dev->type && dev->type->pm) {
@@ -1035,13 +1081,7 @@ int dpm_prepare(pm_message_t state)
get_device(dev);
mutex_unlock(&dpm_list_mtx);
- pm_runtime_get_noresume(dev);
- if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
- pm_wakeup_event(dev, 0);
-
- pm_runtime_put_sync(dev);
- error = pm_wakeup_pending() ?
- -EBUSY : device_prepare(dev, state);
+ error = device_prepare(dev, state);
mutex_lock(&dpm_list_mtx);
if (error) {
@@ -1077,7 +1117,10 @@ int dpm_suspend_start(pm_message_t state)
int error;
error = dpm_prepare(state);
- if (!error)
+ if (error) {
+ suspend_stats.failed_prepare++;
+ dpm_save_failed_step(SUSPEND_PREPARE);
+ } else
error = dpm_suspend(state);
return error;
}
diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
index 56a6899f5e9e..95706fa24c73 100644
--- a/drivers/base/power/opp.c
+++ b/drivers/base/power/opp.c
@@ -73,6 +73,7 @@ struct opp {
* RCU usage: nodes are not modified in the list of device_opp,
* however addition is possible and is secured by dev_opp_list_lock
* @dev: device pointer
+ * @head: notifier head to notify the OPP availability changes.
* @opp_list: list of opps
*
* This is an internal data structure maintaining the link to opps attached to
@@ -83,6 +84,7 @@ struct device_opp {
struct list_head node;
struct device *dev;
+ struct srcu_notifier_head head;
struct list_head opp_list;
};
@@ -404,6 +406,7 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
}
dev_opp->dev = dev;
+ srcu_init_notifier_head(&dev_opp->head);
INIT_LIST_HEAD(&dev_opp->opp_list);
/* Secure the device list modification */
@@ -428,6 +431,11 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
list_add_rcu(&new_opp->node, head);
mutex_unlock(&dev_opp_list_lock);
+ /*
+ * Notify the changes in the availability of the operable
+ * frequency/voltage list.
+ */
+ srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_ADD, new_opp);
return 0;
}
@@ -453,7 +461,7 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
static int opp_set_availability(struct device *dev, unsigned long freq,
bool availability_req)
{
- struct device_opp *tmp_dev_opp, *dev_opp = NULL;
+ struct device_opp *tmp_dev_opp, *dev_opp = ERR_PTR(-ENODEV);
struct opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
int r = 0;
@@ -504,6 +512,14 @@ static int opp_set_availability(struct device *dev, unsigned long freq,
mutex_unlock(&dev_opp_list_lock);
synchronize_rcu();
+ /* Notify the change of the OPP availability */
+ if (availability_req)
+ srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_ENABLE,
+ new_opp);
+ else
+ srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_DISABLE,
+ new_opp);
+
/* clean up old opp */
new_opp = opp;
goto out;
@@ -625,4 +641,35 @@ int opp_init_cpufreq_table(struct device *dev,
return 0;
}
+
+/**
+ * opp_free_cpufreq_table() - free the cpufreq table
+ * @dev: device for which we do this operation
+ * @table: table to free
+ *
+ * Free up the table allocated by opp_init_cpufreq_table
+ */
+void opp_free_cpufreq_table(struct device *dev,
+ struct cpufreq_frequency_table **table)
+{
+ if (!table)
+ return;
+
+ kfree(*table);
+ *table = NULL;
+}
#endif /* CONFIG_CPU_FREQ */
+
+/**
+ * opp_get_notifier() - find notifier_head of the device with opp
+ * @dev: device pointer used to lookup device OPPs.
+ */
+struct srcu_notifier_head *opp_get_notifier(struct device *dev)
+{
+ struct device_opp *dev_opp = find_device_opp(dev);
+
+ if (IS_ERR(dev_opp))
+ return ERR_CAST(dev_opp); /* matching type */
+
+ return &dev_opp->head;
+}
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index f2a25f18fde7..9bf62323aaf3 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -1,3 +1,5 @@
+#include <linux/pm_qos.h>
+
#ifdef CONFIG_PM_RUNTIME
extern void pm_runtime_init(struct device *dev);
@@ -35,15 +37,21 @@ extern void device_pm_move_last(struct device *);
static inline void device_pm_init(struct device *dev)
{
spin_lock_init(&dev->power.lock);
+ dev->power.power_state = PMSG_INVALID;
pm_runtime_init(dev);
}
+static inline void device_pm_add(struct device *dev)
+{
+ dev_pm_qos_constraints_init(dev);
+}
+
static inline void device_pm_remove(struct device *dev)
{
+ dev_pm_qos_constraints_destroy(dev);
pm_runtime_remove(dev);
}
-static inline void device_pm_add(struct device *dev) {}
static inline void device_pm_move_before(struct device *deva,
struct device *devb) {}
static inline void device_pm_move_after(struct device *deva,
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
new file mode 100644
index 000000000000..86de6c50fc41
--- /dev/null
+++ b/drivers/base/power/qos.c
@@ -0,0 +1,414 @@
+/*
+ * Devices PM QoS constraints management
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * This module exposes the interface to kernel space for specifying
+ * per-device PM QoS dependencies. It provides infrastructure for registration
+ * of:
+ *
+ * Dependents on a QoS value : register requests
+ * Watchers of QoS value : get notified when target QoS value changes
+ *
+ * This QoS design is best effort based. Dependents register their QoS needs.
+ * Watchers register to keep track of the current QoS needs of the system.
+ * Watchers can register different types of notification callbacks:
+ * . a per-device notification callback using the dev_pm_qos_*_notifier API.
+ * The notification chain data is stored in the per-device constraint
+ * data struct.
+ * . a system-wide notification callback using the dev_pm_qos_*_global_notifier
+ * API. The notification chain data is stored in a static variable.
+ *
+ * Note about the per-device constraint data struct allocation:
+ * . The per-device constraints data struct ptr is tored into the device
+ * dev_pm_info.
+ * . To minimize the data usage by the per-device constraints, the data struct
+ * is only allocated at the first call to dev_pm_qos_add_request.
+ * . The data is later free'd when the device is removed from the system.
+ * . A global mutex protects the constraints users from the data being
+ * allocated and free'd.
+ */
+
+#include <linux/pm_qos.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/export.h>
+
+
+static DEFINE_MUTEX(dev_pm_qos_mtx);
+
+static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
+
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+ struct pm_qos_constraints *c;
+ unsigned long flags;
+ s32 ret = 0;
+
+ spin_lock_irqsave(&dev->power.lock, flags);
+
+ c = dev->power.constraints;
+ if (c)
+ ret = pm_qos_read_value(c);
+
+ spin_unlock_irqrestore(&dev->power.lock, flags);
+
+ return ret;
+}
+
+/*
+ * apply_constraint
+ * @req: constraint request to apply
+ * @action: action to perform add/update/remove, of type enum pm_qos_req_action
+ * @value: defines the qos request
+ *
+ * Internal function to update the constraints list using the PM QoS core
+ * code and if needed call the per-device and the global notification
+ * callbacks
+ */
+static int apply_constraint(struct dev_pm_qos_request *req,
+ enum pm_qos_req_action action, int value)
+{
+ int ret, curr_value;
+
+ ret = pm_qos_update_target(req->dev->power.constraints,
+ &req->node, action, value);
+
+ if (ret) {
+ /* Call the global callbacks if needed */
+ curr_value = pm_qos_read_value(req->dev->power.constraints);
+ blocking_notifier_call_chain(&dev_pm_notifiers,
+ (unsigned long)curr_value,
+ req);
+ }
+
+ return ret;
+}
+
+/*
+ * dev_pm_qos_constraints_allocate
+ * @dev: device to allocate data for
+ *
+ * Called at the first call to add_request, for constraint data allocation
+ * Must be called with the dev_pm_qos_mtx mutex held
+ */
+static int dev_pm_qos_constraints_allocate(struct device *dev)
+{
+ struct pm_qos_constraints *c;
+ struct blocking_notifier_head *n;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+
+ n = kzalloc(sizeof(*n), GFP_KERNEL);
+ if (!n) {
+ kfree(c);
+ return -ENOMEM;
+ }
+ BLOCKING_INIT_NOTIFIER_HEAD(n);
+
+ plist_head_init(&c->list);
+ c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+ c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+ c->type = PM_QOS_MIN;
+ c->notifiers = n;
+
+ spin_lock_irq(&dev->power.lock);
+ dev->power.constraints = c;
+ spin_unlock_irq(&dev->power.lock);
+
+ return 0;
+}
+
+/**
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
+ * @dev: target device
+ *
+ * Called from the device PM subsystem during device insertion under
+ * device_pm_lock().
+ */
+void dev_pm_qos_constraints_init(struct device *dev)
+{
+ mutex_lock(&dev_pm_qos_mtx);
+ dev->power.constraints = NULL;
+ dev->power.power_state = PMSG_ON;
+ mutex_unlock(&dev_pm_qos_mtx);
+}
+
+/**
+ * dev_pm_qos_constraints_destroy
+ * @dev: target device
+ *
+ * Called from the device PM subsystem on device removal under device_pm_lock().
+ */
+void dev_pm_qos_constraints_destroy(struct device *dev)
+{
+ struct dev_pm_qos_request *req, *tmp;
+ struct pm_qos_constraints *c;
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ dev->power.power_state = PMSG_INVALID;
+ c = dev->power.constraints;
+ if (!c)
+ goto out;
+
+ /* Flush the constraints list for the device */
+ plist_for_each_entry_safe(req, tmp, &c->list, node) {
+ /*
+ * Update constraints list and call the notification
+ * callbacks if needed
+ */
+ apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+ memset(req, 0, sizeof(*req));
+ }
+
+ spin_lock_irq(&dev->power.lock);
+ dev->power.constraints = NULL;
+ spin_unlock_irq(&dev->power.lock);
+
+ kfree(c->notifiers);
+ kfree(c);
+
+ out:
+ mutex_unlock(&dev_pm_qos_mtx);
+}
+
+/**
+ * dev_pm_qos_add_request - inserts new qos request into the list
+ * @dev: target device for the constraint
+ * @req: pointer to a preallocated handle
+ * @value: defines the qos request
+ *
+ * This function inserts a new entry in the device constraints list of
+ * requested qos performance characteristics. It recomputes the aggregate
+ * QoS expectations of parameters and initializes the dev_pm_qos_request
+ * handle. Caller needs to save this handle for later use in updates and
+ * removal.
+ *
+ * Returns 1 if the aggregated constraint value has changed,
+ * 0 if the aggregated constraint value has not changed,
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
+ */
+int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
+ s32 value)
+{
+ int ret = 0;
+
+ if (!dev || !req) /*guard against callers passing in null */
+ return -EINVAL;
+
+ if (WARN(dev_pm_qos_request_active(req),
+ "%s() called for already added request\n", __func__))
+ return -EINVAL;
+
+ req->dev = dev;
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ if (!dev->power.constraints) {
+ if (dev->power.power_state.event == PM_EVENT_INVALID) {
+ /* The device has been removed from the system. */
+ req->dev = NULL;
+ ret = -ENODEV;
+ goto out;
+ } else {
+ /*
+ * Allocate the constraints data on the first call to
+ * add_request, i.e. only if the data is not already
+ * allocated and if the device has not been removed.
+ */
+ ret = dev_pm_qos_constraints_allocate(dev);
+ }
+ }
+
+ if (!ret)
+ ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
+
+ out:
+ mutex_unlock(&dev_pm_qos_mtx);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
+
+/**
+ * dev_pm_qos_update_request - modifies an existing qos request
+ * @req : handle to list element holding a dev_pm_qos request to use
+ * @new_value: defines the qos request
+ *
+ * Updates an existing dev PM qos request along with updating the
+ * target value.
+ *
+ * Attempts are made to make this code callable on hot code paths.
+ *
+ * Returns 1 if the aggregated constraint value has changed,
+ * 0 if the aggregated constraint value has not changed,
+ * -EINVAL in case of wrong parameters, -ENODEV if the device has been
+ * removed from the system
+ */
+int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
+ s32 new_value)
+{
+ int ret = 0;
+
+ if (!req) /*guard against callers passing in null */
+ return -EINVAL;
+
+ if (WARN(!dev_pm_qos_request_active(req),
+ "%s() called for unknown object\n", __func__))
+ return -EINVAL;
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ if (req->dev->power.constraints) {
+ if (new_value != req->node.prio)
+ ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
+ new_value);
+ } else {
+ /* Return if the device has been removed */
+ ret = -ENODEV;
+ }
+
+ mutex_unlock(&dev_pm_qos_mtx);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
+
+/**
+ * dev_pm_qos_remove_request - modifies an existing qos request
+ * @req: handle to request list element
+ *
+ * Will remove pm qos request from the list of constraints and
+ * recompute the current target value. Call this on slow code paths.
+ *
+ * Returns 1 if the aggregated constraint value has changed,
+ * 0 if the aggregated constraint value has not changed,
+ * -EINVAL in case of wrong parameters, -ENODEV if the device has been
+ * removed from the system
+ */
+int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
+{
+ int ret = 0;
+
+ if (!req) /*guard against callers passing in null */
+ return -EINVAL;
+
+ if (WARN(!dev_pm_qos_request_active(req),
+ "%s() called for unknown object\n", __func__))
+ return -EINVAL;
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ if (req->dev->power.constraints) {
+ ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
+ PM_QOS_DEFAULT_VALUE);
+ memset(req, 0, sizeof(*req));
+ } else {
+ /* Return if the device has been removed */
+ ret = -ENODEV;
+ }
+
+ mutex_unlock(&dev_pm_qos_mtx);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
+
+/**
+ * dev_pm_qos_add_notifier - sets notification entry for changes to target value
+ * of per-device PM QoS constraints
+ *
+ * @dev: target device for the constraint
+ * @notifier: notifier block managed by caller.
+ *
+ * Will register the notifier into a notification chain that gets called
+ * upon changes to the target value for the device.
+ */
+int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
+{
+ int retval = 0;
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ /* Silently return if the constraints object is not present. */
+ if (dev->power.constraints)
+ retval = blocking_notifier_chain_register(
+ dev->power.constraints->notifiers,
+ notifier);
+
+ mutex_unlock(&dev_pm_qos_mtx);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
+
+/**
+ * dev_pm_qos_remove_notifier - deletes notification for changes to target value
+ * of per-device PM QoS constraints
+ *
+ * @dev: target device for the constraint
+ * @notifier: notifier block to be removed.
+ *
+ * Will remove the notifier from the notification chain that gets called
+ * upon changes to the target value.
+ */
+int dev_pm_qos_remove_notifier(struct device *dev,
+ struct notifier_block *notifier)
+{
+ int retval = 0;
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ /* Silently return if the constraints object is not present. */
+ if (dev->power.constraints)
+ retval = blocking_notifier_chain_unregister(
+ dev->power.constraints->notifiers,
+ notifier);
+
+ mutex_unlock(&dev_pm_qos_mtx);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
+
+/**
+ * dev_pm_qos_add_global_notifier - sets notification entry for changes to
+ * target value of the PM QoS constraints for any device
+ *
+ * @notifier: notifier block managed by caller.
+ *
+ * Will register the notifier into a notification chain that gets called
+ * upon changes to the target value for any device.
+ */
+int dev_pm_qos_add_global_notifier(struct notifier_block *notifier)
+{
+ return blocking_notifier_chain_register(&dev_pm_notifiers, notifier);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier);
+
+/**
+ * dev_pm_qos_remove_global_notifier - deletes notification for changes to
+ * target value of PM QoS constraints for any device
+ *
+ * @notifier: notifier block to be removed.
+ *
+ * Will remove the notifier from the notification chain that gets called
+ * upon changes to the target value for any device.
+ */
+int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier)
+{
+ return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier);
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 0d4587b15c55..8c78443bca8f 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -1,5 +1,5 @@
/*
- * drivers/base/power/runtime.c - Helper functions for device run-time PM
+ * drivers/base/power/runtime.c - Helper functions for device runtime PM
*
* Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
* Copyright (C) 2010 Alan Stern <stern@rowland.harvard.edu>
@@ -8,7 +8,9 @@
*/
#include <linux/sched.h>
+#include <linux/export.h>
#include <linux/pm_runtime.h>
+#include <trace/events/rpm.h>
#include "power.h"
static int rpm_resume(struct device *dev, int rpmflags);
@@ -28,13 +30,10 @@ static int rpm_suspend(struct device *dev, int rpmflags);
void update_pm_runtime_accounting(struct device *dev)
{
unsigned long now = jiffies;
- int delta;
+ unsigned long delta;
delta = now - dev->power.accounting_timestamp;
- if (delta < 0)
- delta = 0;
-
dev->power.accounting_timestamp = now;
if (dev->power.disable_depth > 0)
@@ -135,8 +134,9 @@ static int rpm_check_suspend_allowed(struct device *dev)
if (dev->power.runtime_error)
retval = -EINVAL;
- else if (atomic_read(&dev->power.usage_count) > 0
- || dev->power.disable_depth > 0)
+ else if (dev->power.disable_depth > 0)
+ retval = -EACCES;
+ else if (atomic_read(&dev->power.usage_count) > 0)
retval = -EAGAIN;
else if (!pm_children_suspended(dev))
retval = -EBUSY;
@@ -154,11 +154,36 @@ static int rpm_check_suspend_allowed(struct device *dev)
}
/**
+ * __rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
+ */
+static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
+ __releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+ int retval;
+
+ if (dev->power.irq_safe)
+ spin_unlock(&dev->power.lock);
+ else
+ spin_unlock_irq(&dev->power.lock);
+
+ retval = cb(dev);
+
+ if (dev->power.irq_safe)
+ spin_lock(&dev->power.lock);
+ else
+ spin_lock_irq(&dev->power.lock);
+
+ return retval;
+}
+
+/**
* rpm_idle - Notify device bus type if the device can be suspended.
* @dev: Device to notify the bus type about.
* @rpmflags: Flag bits.
*
- * Check if the device's run-time PM status allows it to be suspended. If
+ * Check if the device's runtime PM status allows it to be suspended. If
* another idle notification has been started earlier, return immediately. If
* the RPM_ASYNC flag is set then queue an idle-notification request; otherwise
* run the ->runtime_idle() callback directly.
@@ -170,6 +195,7 @@ static int rpm_idle(struct device *dev, int rpmflags)
int (*callback)(struct device *);
int retval;
+ trace_rpm_idle(dev, rpmflags);
retval = rpm_check_suspend_allowed(dev);
if (retval < 0)
; /* Conditions are wrong. */
@@ -213,8 +239,8 @@ static int rpm_idle(struct device *dev, int rpmflags)
dev->power.idle_notification = true;
- if (dev->pwr_domain)
- callback = dev->pwr_domain->ops.runtime_idle;
+ if (dev->pm_domain)
+ callback = dev->pm_domain->ops.runtime_idle;
else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_idle;
else if (dev->class && dev->class->pm)
@@ -224,18 +250,14 @@ static int rpm_idle(struct device *dev, int rpmflags)
else
callback = NULL;
- if (callback) {
- spin_unlock_irq(&dev->power.lock);
-
- callback(dev);
-
- spin_lock_irq(&dev->power.lock);
- }
+ if (callback)
+ __rpm_callback(callback, dev);
dev->power.idle_notification = false;
wake_up_all(&dev->power.wait_queue);
out:
+ trace_rpm_return_int(dev, _THIS_IP_, retval);
return retval;
}
@@ -245,39 +267,36 @@ static int rpm_idle(struct device *dev, int rpmflags)
* @dev: Device to run the callback for.
*/
static int rpm_callback(int (*cb)(struct device *), struct device *dev)
- __releases(&dev->power.lock) __acquires(&dev->power.lock)
{
int retval;
if (!cb)
return -ENOSYS;
- if (dev->power.irq_safe) {
- retval = cb(dev);
- } else {
- spin_unlock_irq(&dev->power.lock);
+ retval = __rpm_callback(cb, dev);
- retval = cb(dev);
-
- spin_lock_irq(&dev->power.lock);
- }
dev->power.runtime_error = retval;
- return retval;
+ return retval != -EACCES ? retval : -EIO;
}
/**
- * rpm_suspend - Carry out run-time suspend of given device.
+ * rpm_suspend - Carry out runtime suspend of given device.
* @dev: Device to suspend.
* @rpmflags: Flag bits.
*
- * Check if the device's run-time PM status allows it to be suspended. If
- * another suspend has been started earlier, either return immediately or wait
- * for it to finish, depending on the RPM_NOWAIT and RPM_ASYNC flags. Cancel a
- * pending idle notification. If the RPM_ASYNC flag is set then queue a
- * suspend request; otherwise run the ->runtime_suspend() callback directly.
- * If a deferred resume was requested while the callback was running then carry
- * it out; otherwise send an idle notification for the device (if the suspend
- * failed) or for its parent (if the suspend succeeded).
+ * Check if the device's runtime PM status allows it to be suspended.
+ * Cancel a pending idle notification, autosuspend or suspend. If
+ * another suspend has been started earlier, either return immediately
+ * or wait for it to finish, depending on the RPM_NOWAIT and RPM_ASYNC
+ * flags. If the RPM_ASYNC flag is set then queue a suspend request;
+ * otherwise run the ->runtime_suspend() callback directly. When
+ * ->runtime_suspend succeeded, if a deferred resume was requested while
+ * the callback was running then carry it out, otherwise send an idle
+ * notification for its parent (if the suspend succeeded and both
+ * ignore_children of parent->power and irq_safe of dev->power are not set).
+ * If ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the RPM_AUTO
+ * flag is set and the next autosuspend-delay expiration time is in the
+ * future, schedule another autosuspend attempt.
*
* This function must be called under dev->power.lock with interrupts disabled.
*/
@@ -288,7 +307,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
struct device *parent = NULL;
int retval;
- dev_dbg(dev, "%s flags 0x%x\n", __func__, rpmflags);
+ trace_rpm_suspend(dev, rpmflags);
repeat:
retval = rpm_check_suspend_allowed(dev);
@@ -340,6 +359,15 @@ static int rpm_suspend(struct device *dev, int rpmflags)
goto out;
}
+ if (dev->power.irq_safe) {
+ spin_unlock(&dev->power.lock);
+
+ cpu_relax();
+
+ spin_lock(&dev->power.lock);
+ goto repeat;
+ }
+
/* Wait for the other suspend running in parallel with us. */
for (;;) {
prepare_to_wait(&dev->power.wait_queue, &wait,
@@ -374,8 +402,8 @@ static int rpm_suspend(struct device *dev, int rpmflags)
__update_runtime_status(dev, RPM_SUSPENDING);
- if (dev->pwr_domain)
- callback = dev->pwr_domain->ops.runtime_suspend;
+ if (dev->pm_domain)
+ callback = dev->pm_domain->ops.runtime_suspend;
else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_suspend;
else if (dev->class && dev->class->pm)
@@ -388,20 +416,32 @@ static int rpm_suspend(struct device *dev, int rpmflags)
retval = rpm_callback(callback, dev);
if (retval) {
__update_runtime_status(dev, RPM_ACTIVE);
- dev->power.deferred_resume = 0;
- if (retval == -EAGAIN || retval == -EBUSY)
+ dev->power.deferred_resume = false;
+ if (retval == -EAGAIN || retval == -EBUSY) {
dev->power.runtime_error = 0;
- else
+
+ /*
+ * If the callback routine failed an autosuspend, and
+ * if the last_busy time has been updated so that there
+ * is a new autosuspend expiration time, automatically
+ * reschedule another autosuspend.
+ */
+ if ((rpmflags & RPM_AUTO) &&
+ pm_runtime_autosuspend_expiration(dev) != 0)
+ goto repeat;
+ } else {
pm_runtime_cancel_pending(dev);
- } else {
+ }
+ wake_up_all(&dev->power.wait_queue);
+ goto out;
+ }
no_callback:
- __update_runtime_status(dev, RPM_SUSPENDED);
- pm_runtime_deactivate_timer(dev);
+ __update_runtime_status(dev, RPM_SUSPENDED);
+ pm_runtime_deactivate_timer(dev);
- if (dev->parent) {
- parent = dev->parent;
- atomic_add_unless(&parent->power.child_count, -1, 0);
- }
+ if (dev->parent) {
+ parent = dev->parent;
+ atomic_add_unless(&parent->power.child_count, -1, 0);
}
wake_up_all(&dev->power.wait_queue);
@@ -423,17 +463,17 @@ static int rpm_suspend(struct device *dev, int rpmflags)
}
out:
- dev_dbg(dev, "%s returns %d\n", __func__, retval);
+ trace_rpm_return_int(dev, _THIS_IP_, retval);
return retval;
}
/**
- * rpm_resume - Carry out run-time resume of given device.
+ * rpm_resume - Carry out runtime resume of given device.
* @dev: Device to resume.
* @rpmflags: Flag bits.
*
- * Check if the device's run-time PM status allows it to be resumed. Cancel
+ * Check if the device's runtime PM status allows it to be resumed. Cancel
* any scheduled or pending requests. If another resume has been started
* earlier, either return immediately or wait for it to finish, depending on the
* RPM_NOWAIT and RPM_ASYNC flags. Similarly, if there's a suspend running in
@@ -452,13 +492,13 @@ static int rpm_resume(struct device *dev, int rpmflags)
struct device *parent = NULL;
int retval = 0;
- dev_dbg(dev, "%s flags 0x%x\n", __func__, rpmflags);
+ trace_rpm_resume(dev, rpmflags);
repeat:
if (dev->power.runtime_error)
retval = -EINVAL;
else if (dev->power.disable_depth > 0)
- retval = -EAGAIN;
+ retval = -EACCES;
if (retval)
goto out;
@@ -489,6 +529,15 @@ static int rpm_resume(struct device *dev, int rpmflags)
goto out;
}
+ if (dev->power.irq_safe) {
+ spin_unlock(&dev->power.lock);
+
+ cpu_relax();
+
+ spin_lock(&dev->power.lock);
+ goto repeat;
+ }
+
/* Wait for the operation carried out in parallel with us. */
for (;;) {
prepare_to_wait(&dev->power.wait_queue, &wait,
@@ -550,7 +599,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
spin_lock(&parent->power.lock);
/*
- * We can resume if the parent's run-time PM is disabled or it
+ * We can resume if the parent's runtime PM is disabled or it
* is set to ignore children.
*/
if (!parent->power.disable_depth
@@ -573,8 +622,8 @@ static int rpm_resume(struct device *dev, int rpmflags)
__update_runtime_status(dev, RPM_RESUMING);
- if (dev->pwr_domain)
- callback = dev->pwr_domain->ops.runtime_resume;
+ if (dev->pm_domain)
+ callback = dev->pm_domain->ops.runtime_resume;
else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_resume;
else if (dev->class && dev->class->pm)
@@ -608,17 +657,17 @@ static int rpm_resume(struct device *dev, int rpmflags)
spin_lock_irq(&dev->power.lock);
}
- dev_dbg(dev, "%s returns %d\n", __func__, retval);
+ trace_rpm_return_int(dev, _THIS_IP_, retval);
return retval;
}
/**
- * pm_runtime_work - Universal run-time PM work function.
+ * pm_runtime_work - Universal runtime PM work function.
* @work: Work structure used for scheduling the execution of this function.
*
* Use @work to get the device object the work is to be done for, determine what
- * is to be done and execute the appropriate run-time PM function.
+ * is to be done and execute the appropriate runtime PM function.
*/
static void pm_runtime_work(struct work_struct *work)
{
@@ -717,7 +766,7 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay)
EXPORT_SYMBOL_GPL(pm_schedule_suspend);
/**
- * __pm_runtime_idle - Entry point for run-time idle operations.
+ * __pm_runtime_idle - Entry point for runtime idle operations.
* @dev: Device to send idle notification for.
* @rpmflags: Flag bits.
*
@@ -725,13 +774,16 @@ EXPORT_SYMBOL_GPL(pm_schedule_suspend);
* return immediately if it is larger than zero. Then carry out an idle
* notification, either synchronous or asynchronous.
*
- * This routine may be called in atomic context if the RPM_ASYNC flag is set.
+ * This routine may be called in atomic context if the RPM_ASYNC flag is set,
+ * or if pm_runtime_irq_safe() has been called.
*/
int __pm_runtime_idle(struct device *dev, int rpmflags)
{
unsigned long flags;
int retval;
+ might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
+
if (rpmflags & RPM_GET_PUT) {
if (!atomic_dec_and_test(&dev->power.usage_count))
return 0;
@@ -746,7 +798,7 @@ int __pm_runtime_idle(struct device *dev, int rpmflags)
EXPORT_SYMBOL_GPL(__pm_runtime_idle);
/**
- * __pm_runtime_suspend - Entry point for run-time put/suspend operations.
+ * __pm_runtime_suspend - Entry point for runtime put/suspend operations.
* @dev: Device to suspend.
* @rpmflags: Flag bits.
*
@@ -754,13 +806,16 @@ EXPORT_SYMBOL_GPL(__pm_runtime_idle);
* return immediately if it is larger than zero. Then carry out a suspend,
* either synchronous or asynchronous.
*
- * This routine may be called in atomic context if the RPM_ASYNC flag is set.
+ * This routine may be called in atomic context if the RPM_ASYNC flag is set,
+ * or if pm_runtime_irq_safe() has been called.
*/
int __pm_runtime_suspend(struct device *dev, int rpmflags)
{
unsigned long flags;
int retval;
+ might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
+
if (rpmflags & RPM_GET_PUT) {
if (!atomic_dec_and_test(&dev->power.usage_count))
return 0;
@@ -775,20 +830,23 @@ int __pm_runtime_suspend(struct device *dev, int rpmflags)
EXPORT_SYMBOL_GPL(__pm_runtime_suspend);
/**
- * __pm_runtime_resume - Entry point for run-time resume operations.
+ * __pm_runtime_resume - Entry point for runtime resume operations.
* @dev: Device to resume.
* @rpmflags: Flag bits.
*
* If the RPM_GET_PUT flag is set, increment the device's usage count. Then
* carry out a resume, either synchronous or asynchronous.
*
- * This routine may be called in atomic context if the RPM_ASYNC flag is set.
+ * This routine may be called in atomic context if the RPM_ASYNC flag is set,
+ * or if pm_runtime_irq_safe() has been called.
*/
int __pm_runtime_resume(struct device *dev, int rpmflags)
{
unsigned long flags;
int retval;
+ might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
+
if (rpmflags & RPM_GET_PUT)
atomic_inc(&dev->power.usage_count);
@@ -801,11 +859,11 @@ int __pm_runtime_resume(struct device *dev, int rpmflags)
EXPORT_SYMBOL_GPL(__pm_runtime_resume);
/**
- * __pm_runtime_set_status - Set run-time PM status of a device.
+ * __pm_runtime_set_status - Set runtime PM status of a device.
* @dev: Device to handle.
- * @status: New run-time PM status of the device.
+ * @status: New runtime PM status of the device.
*
- * If run-time PM of the device is disabled or its power.runtime_error field is
+ * If runtime PM of the device is disabled or its power.runtime_error field is
* different from zero, the status may be changed either to RPM_ACTIVE, or to
* RPM_SUSPENDED, as long as that reflects the actual state of the device.
* However, if the device has a parent and the parent is not active, and the
@@ -851,7 +909,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
/*
* It is invalid to put an active child under a parent that is
- * not active, has run-time PM enabled and the
+ * not active, has runtime PM enabled and the
* 'power.ignore_children' flag unset.
*/
if (!parent->power.disable_depth
@@ -885,7 +943,7 @@ EXPORT_SYMBOL_GPL(__pm_runtime_set_status);
* @dev: Device to handle.
*
* Flush all pending requests for the device from pm_wq and wait for all
- * run-time PM operations involving the device in progress to complete.
+ * runtime PM operations involving the device in progress to complete.
*
* Should be called under dev->power.lock with interrupts disabled.
*/
@@ -933,7 +991,7 @@ static void __pm_runtime_barrier(struct device *dev)
* Prevent the device from being suspended by incrementing its usage counter and
* if there's a pending resume request for the device, wake the device up.
* Next, make sure that all pending requests for the device have been flushed
- * from pm_wq and wait for all run-time PM operations involving the device in
+ * from pm_wq and wait for all runtime PM operations involving the device in
* progress to complete.
*
* Return value:
@@ -963,18 +1021,18 @@ int pm_runtime_barrier(struct device *dev)
EXPORT_SYMBOL_GPL(pm_runtime_barrier);
/**
- * __pm_runtime_disable - Disable run-time PM of a device.
+ * __pm_runtime_disable - Disable runtime PM of a device.
* @dev: Device to handle.
* @check_resume: If set, check if there's a resume request for the device.
*
* Increment power.disable_depth for the device and if was zero previously,
- * cancel all pending run-time PM requests for the device and wait for all
+ * cancel all pending runtime PM requests for the device and wait for all
* operations in progress to complete. The device can be either active or
- * suspended after its run-time PM has been disabled.
+ * suspended after its runtime PM has been disabled.
*
* If @check_resume is set and there's a resume request pending when
* __pm_runtime_disable() is called and power.disable_depth is zero, the
- * function will wake up the device before disabling its run-time PM.
+ * function will wake up the device before disabling its runtime PM.
*/
void __pm_runtime_disable(struct device *dev, bool check_resume)
{
@@ -987,7 +1045,7 @@ void __pm_runtime_disable(struct device *dev, bool check_resume)
/*
* Wake up the device if there's a resume request pending, because that
- * means there probably is some I/O to process and disabling run-time PM
+ * means there probably is some I/O to process and disabling runtime PM
* shouldn't prevent the device from processing the I/O.
*/
if (check_resume && dev->power.request_pending
@@ -1012,7 +1070,7 @@ void __pm_runtime_disable(struct device *dev, bool check_resume)
EXPORT_SYMBOL_GPL(__pm_runtime_disable);
/**
- * pm_runtime_enable - Enable run-time PM of a device.
+ * pm_runtime_enable - Enable runtime PM of a device.
* @dev: Device to handle.
*/
void pm_runtime_enable(struct device *dev)
@@ -1031,7 +1089,7 @@ void pm_runtime_enable(struct device *dev)
EXPORT_SYMBOL_GPL(pm_runtime_enable);
/**
- * pm_runtime_forbid - Block run-time PM of a device.
+ * pm_runtime_forbid - Block runtime PM of a device.
* @dev: Device to handle.
*
* Increase the device's usage count and clear its power.runtime_auto flag,
@@ -1054,7 +1112,7 @@ void pm_runtime_forbid(struct device *dev)
EXPORT_SYMBOL_GPL(pm_runtime_forbid);
/**
- * pm_runtime_allow - Unblock run-time PM of a device.
+ * pm_runtime_allow - Unblock runtime PM of a device.
* @dev: Device to handle.
*
* Decrease the device's usage count and set its power.runtime_auto flag.
@@ -1075,12 +1133,12 @@ void pm_runtime_allow(struct device *dev)
EXPORT_SYMBOL_GPL(pm_runtime_allow);
/**
- * pm_runtime_no_callbacks - Ignore run-time PM callbacks for a device.
+ * pm_runtime_no_callbacks - Ignore runtime PM callbacks for a device.
* @dev: Device to handle.
*
* Set the power.no_callbacks flag, which tells the PM core that this
- * device is power-managed through its parent and has no run-time PM
- * callbacks of its own. The run-time sysfs attributes will be removed.
+ * device is power-managed through its parent and has no runtime PM
+ * callbacks of its own. The runtime sysfs attributes will be removed.
*/
void pm_runtime_no_callbacks(struct device *dev)
{
@@ -1156,8 +1214,8 @@ static void update_autosuspend(struct device *dev, int old_delay, int old_use)
* @delay: Value of the new delay in milliseconds.
*
* Set the device's power.autosuspend_delay value. If it changes to negative
- * and the power.use_autosuspend flag is set, prevent run-time suspends. If it
- * changes the other way, allow run-time suspends.
+ * and the power.use_autosuspend flag is set, prevent runtime suspends. If it
+ * changes the other way, allow runtime suspends.
*/
void pm_runtime_set_autosuspend_delay(struct device *dev, int delay)
{
@@ -1177,7 +1235,7 @@ EXPORT_SYMBOL_GPL(pm_runtime_set_autosuspend_delay);
* @dev: Device to handle.
* @use: New value for use_autosuspend.
*
- * Set the device's power.use_autosuspend flag, and allow or prevent run-time
+ * Set the device's power.use_autosuspend flag, and allow or prevent runtime
* suspends as needed.
*/
void __pm_runtime_use_autosuspend(struct device *dev, bool use)
@@ -1194,7 +1252,7 @@ void __pm_runtime_use_autosuspend(struct device *dev, bool use)
EXPORT_SYMBOL_GPL(__pm_runtime_use_autosuspend);
/**
- * pm_runtime_init - Initialize run-time PM fields in given device object.
+ * pm_runtime_init - Initialize runtime PM fields in given device object.
* @dev: Device object to initialize.
*/
void pm_runtime_init(struct device *dev)
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index a9f5b8979611..adf41be0ea66 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -4,8 +4,9 @@
#include <linux/device.h>
#include <linux/string.h>
+#include <linux/export.h>
#include <linux/pm_runtime.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/jiffies.h>
#include "power.h"
@@ -116,12 +117,14 @@ static ssize_t control_store(struct device * dev, struct device_attribute *attr,
cp = memchr(buf, '\n', n);
if (cp)
len = cp - buf;
+ device_lock(dev);
if (len == sizeof ctrl_auto - 1 && strncmp(buf, ctrl_auto, len) == 0)
pm_runtime_allow(dev);
else if (len == sizeof ctrl_on - 1 && strncmp(buf, ctrl_on, len) == 0)
pm_runtime_forbid(dev);
else
- return -EINVAL;
+ n = -EINVAL;
+ device_unlock(dev);
return n;
}
@@ -205,7 +208,9 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev,
if (strict_strtol(buf, 10, &delay) != 0 || delay != (int) delay)
return -EINVAL;
+ device_lock(dev);
pm_runtime_set_autosuspend_delay(dev, delay);
+ device_unlock(dev);
return n;
}
diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c
index c80e138b62fe..d94a1f5121cf 100644
--- a/drivers/base/power/trace.c
+++ b/drivers/base/power/trace.c
@@ -8,6 +8,7 @@
*/
#include <linux/resume-trace.h>
+#include <linux/export.h>
#include <linux/rtc.h>
#include <asm/rtc.h>
@@ -112,7 +113,7 @@ static unsigned int read_magic_time(void)
unsigned int val;
get_rtc_time(&time);
- pr_info("Time: %2d:%02d:%02d Date: %02d/%02d/%02d\n",
+ pr_info("RTC time: %2d:%02d:%02d, date: %02d/%02d/%02d\n",
time.tm_hour, time.tm_min, time.tm_sec,
time.tm_mon + 1, time.tm_mday, time.tm_year % 100);
val = time.tm_year; /* 100 years */
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 84f7c7d5a098..caf995fb774b 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -10,6 +10,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/capability.h>
+#include <linux/export.h>
#include <linux/suspend.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
@@ -276,7 +277,9 @@ EXPORT_SYMBOL_GPL(device_set_wakeup_capable);
*
* By default, most devices should leave wakeup disabled. The exceptions are
* devices that everyone expects to be wakeup sources: keyboards, power buttons,
- * possibly network interfaces, etc.
+ * possibly network interfaces, etc. Also, devices that don't generate their
+ * own wakeup requests but merely forward requests from one bus to another
+ * (like PCI bridges) should have wakeup enabled by default.
*/
int device_init_wakeup(struct device *dev, bool enable)
{
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
new file mode 100644
index 000000000000..2fc6a66f39a4
--- /dev/null
+++ b/drivers/base/regmap/Kconfig
@@ -0,0 +1,15 @@
+# Generic register map support. There are no user servicable options here,
+# this is an API intended to be used by other kernel subsystems. These
+# subsystems should select the appropriate symbols.
+
+config REGMAP
+ default y if (REGMAP_I2C || REGMAP_SPI)
+ select LZO_COMPRESS
+ select LZO_DECOMPRESS
+ bool
+
+config REGMAP_I2C
+ tristate
+
+config REGMAP_SPI
+ tristate
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
new file mode 100644
index 000000000000..0573c8a9dacb
--- /dev/null
+++ b/drivers/base/regmap/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o regcache-lzo.o
+obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
+obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
+obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
new file mode 100644
index 000000000000..348ff02eb93e
--- /dev/null
+++ b/drivers/base/regmap/internal.h
@@ -0,0 +1,128 @@
+/*
+ * Register map access API internal header
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _REGMAP_INTERNAL_H
+#define _REGMAP_INTERNAL_H
+
+#include <linux/regmap.h>
+#include <linux/fs.h>
+
+struct regmap;
+struct regcache_ops;
+
+struct regmap_format {
+ size_t buf_size;
+ size_t reg_bytes;
+ size_t val_bytes;
+ void (*format_write)(struct regmap *map,
+ unsigned int reg, unsigned int val);
+ void (*format_reg)(void *buf, unsigned int reg);
+ void (*format_val)(void *buf, unsigned int val);
+ unsigned int (*parse_val)(void *buf);
+};
+
+struct regmap {
+ struct mutex lock;
+
+ struct device *dev; /* Device we do I/O on */
+ void *work_buf; /* Scratch buffer used to format I/O */
+ struct regmap_format format; /* Buffer format */
+ const struct regmap_bus *bus;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs;
+#endif
+
+ unsigned int max_register;
+ bool (*writeable_reg)(struct device *dev, unsigned int reg);
+ bool (*readable_reg)(struct device *dev, unsigned int reg);
+ bool (*volatile_reg)(struct device *dev, unsigned int reg);
+ bool (*precious_reg)(struct device *dev, unsigned int reg);
+
+ u8 read_flag_mask;
+ u8 write_flag_mask;
+
+ /* regcache specific members */
+ const struct regcache_ops *cache_ops;
+ enum regcache_type cache_type;
+
+ /* number of bytes in reg_defaults_raw */
+ unsigned int cache_size_raw;
+ /* number of bytes per word in reg_defaults_raw */
+ unsigned int cache_word_size;
+ /* number of entries in reg_defaults */
+ unsigned int num_reg_defaults;
+ /* number of entries in reg_defaults_raw */
+ unsigned int num_reg_defaults_raw;
+
+ /* if set, only the cache is modified not the HW */
+ unsigned int cache_only:1;
+ /* if set, only the HW is modified not the cache */
+ unsigned int cache_bypass:1;
+ /* if set, remember to free reg_defaults_raw */
+ unsigned int cache_free:1;
+
+ struct reg_default *reg_defaults;
+ const void *reg_defaults_raw;
+ void *cache;
+};
+
+struct regcache_ops {
+ const char *name;
+ enum regcache_type type;
+ int (*init)(struct regmap *map);
+ int (*exit)(struct regmap *map);
+ int (*read)(struct regmap *map, unsigned int reg, unsigned int *value);
+ int (*write)(struct regmap *map, unsigned int reg, unsigned int value);
+ int (*sync)(struct regmap *map);
+};
+
+bool regmap_writeable(struct regmap *map, unsigned int reg);
+bool regmap_readable(struct regmap *map, unsigned int reg);
+bool regmap_volatile(struct regmap *map, unsigned int reg);
+bool regmap_precious(struct regmap *map, unsigned int reg);
+
+int _regmap_write(struct regmap *map, unsigned int reg,
+ unsigned int val);
+
+#ifdef CONFIG_DEBUG_FS
+extern void regmap_debugfs_initcall(void);
+extern void regmap_debugfs_init(struct regmap *map);
+extern void regmap_debugfs_exit(struct regmap *map);
+#else
+static inline void regmap_debugfs_initcall(void) { }
+static inline void regmap_debugfs_init(struct regmap *map) { }
+static inline void regmap_debugfs_exit(struct regmap *map) { }
+#endif
+
+/* regcache core declarations */
+int regcache_init(struct regmap *map);
+void regcache_exit(struct regmap *map);
+int regcache_read(struct regmap *map,
+ unsigned int reg, unsigned int *value);
+int regcache_write(struct regmap *map,
+ unsigned int reg, unsigned int value);
+int regcache_sync(struct regmap *map);
+
+unsigned int regcache_get_val(const void *base, unsigned int idx,
+ unsigned int word_size);
+bool regcache_set_val(void *base, unsigned int idx,
+ unsigned int val, unsigned int word_size);
+int regcache_lookup_reg(struct regmap *map, unsigned int reg);
+int regcache_insert_reg(struct regmap *map, unsigned int reg,
+ unsigned int val);
+
+extern struct regcache_ops regcache_indexed_ops;
+extern struct regcache_ops regcache_rbtree_ops;
+extern struct regcache_ops regcache_lzo_ops;
+
+#endif
diff --git a/drivers/base/regmap/regcache-indexed.c b/drivers/base/regmap/regcache-indexed.c
new file mode 100644
index 000000000000..507731ad8ec1
--- /dev/null
+++ b/drivers/base/regmap/regcache-indexed.c
@@ -0,0 +1,64 @@
+/*
+ * Register cache access API - indexed caching support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+
+#include "internal.h"
+
+static int regcache_indexed_read(struct regmap *map, unsigned int reg,
+ unsigned int *value)
+{
+ int ret;
+
+ ret = regcache_lookup_reg(map, reg);
+ if (ret >= 0)
+ *value = map->reg_defaults[ret].def;
+
+ return ret;
+}
+
+static int regcache_indexed_write(struct regmap *map, unsigned int reg,
+ unsigned int value)
+{
+ int ret;
+
+ ret = regcache_lookup_reg(map, reg);
+ if (ret < 0)
+ return regcache_insert_reg(map, reg, value);
+ map->reg_defaults[ret].def = value;
+ return 0;
+}
+
+static int regcache_indexed_sync(struct regmap *map)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < map->num_reg_defaults; i++) {
+ ret = _regmap_write(map, map->reg_defaults[i].reg,
+ map->reg_defaults[i].def);
+ if (ret < 0)
+ return ret;
+ dev_dbg(map->dev, "Synced register %#x, value %#x\n",
+ map->reg_defaults[i].reg,
+ map->reg_defaults[i].def);
+ }
+ return 0;
+}
+
+struct regcache_ops regcache_indexed_ops = {
+ .type = REGCACHE_INDEXED,
+ .name = "indexed",
+ .read = regcache_indexed_read,
+ .write = regcache_indexed_write,
+ .sync = regcache_indexed_sync
+};
diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c
new file mode 100644
index 000000000000..066aeece3626
--- /dev/null
+++ b/drivers/base/regmap/regcache-lzo.c
@@ -0,0 +1,361 @@
+/*
+ * Register cache access API - LZO caching support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/lzo.h>
+
+#include "internal.h"
+
+struct regcache_lzo_ctx {
+ void *wmem;
+ void *dst;
+ const void *src;
+ size_t src_len;
+ size_t dst_len;
+ size_t decompressed_size;
+ unsigned long *sync_bmp;
+ int sync_bmp_nbits;
+};
+
+#define LZO_BLOCK_NUM 8
+static int regcache_lzo_block_count(void)
+{
+ return LZO_BLOCK_NUM;
+}
+
+static int regcache_lzo_prepare(struct regcache_lzo_ctx *lzo_ctx)
+{
+ lzo_ctx->wmem = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+ if (!lzo_ctx->wmem)
+ return -ENOMEM;
+ return 0;
+}
+
+static int regcache_lzo_compress(struct regcache_lzo_ctx *lzo_ctx)
+{
+ size_t compress_size;
+ int ret;
+
+ ret = lzo1x_1_compress(lzo_ctx->src, lzo_ctx->src_len,
+ lzo_ctx->dst, &compress_size, lzo_ctx->wmem);
+ if (ret != LZO_E_OK || compress_size > lzo_ctx->dst_len)
+ return -EINVAL;
+ lzo_ctx->dst_len = compress_size;
+ return 0;
+}
+
+static int regcache_lzo_decompress(struct regcache_lzo_ctx *lzo_ctx)
+{
+ size_t dst_len;
+ int ret;
+
+ dst_len = lzo_ctx->dst_len;
+ ret = lzo1x_decompress_safe(lzo_ctx->src, lzo_ctx->src_len,
+ lzo_ctx->dst, &dst_len);
+ if (ret != LZO_E_OK || dst_len != lzo_ctx->dst_len)
+ return -EINVAL;
+ return 0;
+}
+
+static int regcache_lzo_compress_cache_block(struct regmap *map,
+ struct regcache_lzo_ctx *lzo_ctx)
+{
+ int ret;
+
+ lzo_ctx->dst_len = lzo1x_worst_compress(PAGE_SIZE);
+ lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
+ if (!lzo_ctx->dst) {
+ lzo_ctx->dst_len = 0;
+ return -ENOMEM;
+ }
+
+ ret = regcache_lzo_compress(lzo_ctx);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int regcache_lzo_decompress_cache_block(struct regmap *map,
+ struct regcache_lzo_ctx *lzo_ctx)
+{
+ int ret;
+
+ lzo_ctx->dst_len = lzo_ctx->decompressed_size;
+ lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
+ if (!lzo_ctx->dst) {
+ lzo_ctx->dst_len = 0;
+ return -ENOMEM;
+ }
+
+ ret = regcache_lzo_decompress(lzo_ctx);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static inline int regcache_lzo_get_blkindex(struct regmap *map,
+ unsigned int reg)
+{
+ return (reg * map->cache_word_size) /
+ DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count());
+}
+
+static inline int regcache_lzo_get_blkpos(struct regmap *map,
+ unsigned int reg)
+{
+ return reg % (DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count()) /
+ map->cache_word_size);
+}
+
+static inline int regcache_lzo_get_blksize(struct regmap *map)
+{
+ return DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count());
+}
+
+static int regcache_lzo_init(struct regmap *map)
+{
+ struct regcache_lzo_ctx **lzo_blocks;
+ size_t bmp_size;
+ int ret, i, blksize, blkcount;
+ const char *p, *end;
+ unsigned long *sync_bmp;
+
+ ret = 0;
+
+ blkcount = regcache_lzo_block_count();
+ map->cache = kzalloc(blkcount * sizeof *lzo_blocks,
+ GFP_KERNEL);
+ if (!map->cache)
+ return -ENOMEM;
+ lzo_blocks = map->cache;
+
+ /*
+ * allocate a bitmap to be used when syncing the cache with
+ * the hardware. Each time a register is modified, the corresponding
+ * bit is set in the bitmap, so we know that we have to sync
+ * that register.
+ */
+ bmp_size = map->num_reg_defaults_raw;
+ sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof(long),
+ GFP_KERNEL);
+ if (!sync_bmp) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ bitmap_zero(sync_bmp, bmp_size);
+
+ /* allocate the lzo blocks and initialize them */
+ for (i = 0; i < blkcount; i++) {
+ lzo_blocks[i] = kzalloc(sizeof **lzo_blocks,
+ GFP_KERNEL);
+ if (!lzo_blocks[i]) {
+ kfree(sync_bmp);
+ ret = -ENOMEM;
+ goto err;
+ }
+ lzo_blocks[i]->sync_bmp = sync_bmp;
+ lzo_blocks[i]->sync_bmp_nbits = bmp_size;
+ /* alloc the working space for the compressed block */
+ ret = regcache_lzo_prepare(lzo_blocks[i]);
+ if (ret < 0)
+ goto err;
+ }
+
+ blksize = regcache_lzo_get_blksize(map);
+ p = map->reg_defaults_raw;
+ end = map->reg_defaults_raw + map->cache_size_raw;
+ /* compress the register map and fill the lzo blocks */
+ for (i = 0; i < blkcount; i++, p += blksize) {
+ lzo_blocks[i]->src = p;
+ if (p + blksize > end)
+ lzo_blocks[i]->src_len = end - p;
+ else
+ lzo_blocks[i]->src_len = blksize;
+ ret = regcache_lzo_compress_cache_block(map,
+ lzo_blocks[i]);
+ if (ret < 0)
+ goto err;
+ lzo_blocks[i]->decompressed_size =
+ lzo_blocks[i]->src_len;
+ }
+
+ return 0;
+err:
+ regcache_exit(map);
+ return ret;
+}
+
+static int regcache_lzo_exit(struct regmap *map)
+{
+ struct regcache_lzo_ctx **lzo_blocks;
+ int i, blkcount;
+
+ lzo_blocks = map->cache;
+ if (!lzo_blocks)
+ return 0;
+
+ blkcount = regcache_lzo_block_count();
+ /*
+ * the pointer to the bitmap used for syncing the cache
+ * is shared amongst all lzo_blocks. Ensure it is freed
+ * only once.
+ */
+ if (lzo_blocks[0])
+ kfree(lzo_blocks[0]->sync_bmp);
+ for (i = 0; i < blkcount; i++) {
+ if (lzo_blocks[i]) {
+ kfree(lzo_blocks[i]->wmem);
+ kfree(lzo_blocks[i]->dst);
+ }
+ /* each lzo_block is a pointer returned by kmalloc or NULL */
+ kfree(lzo_blocks[i]);
+ }
+ kfree(lzo_blocks);
+ map->cache = NULL;
+ return 0;
+}
+
+static int regcache_lzo_read(struct regmap *map,
+ unsigned int reg, unsigned int *value)
+{
+ struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
+ int ret, blkindex, blkpos;
+ size_t blksize, tmp_dst_len;
+ void *tmp_dst;
+
+ /* index of the compressed lzo block */
+ blkindex = regcache_lzo_get_blkindex(map, reg);
+ /* register index within the decompressed block */
+ blkpos = regcache_lzo_get_blkpos(map, reg);
+ /* size of the compressed block */
+ blksize = regcache_lzo_get_blksize(map);
+ lzo_blocks = map->cache;
+ lzo_block = lzo_blocks[blkindex];
+
+ /* save the pointer and length of the compressed block */
+ tmp_dst = lzo_block->dst;
+ tmp_dst_len = lzo_block->dst_len;
+
+ /* prepare the source to be the compressed block */
+ lzo_block->src = lzo_block->dst;
+ lzo_block->src_len = lzo_block->dst_len;
+
+ /* decompress the block */
+ ret = regcache_lzo_decompress_cache_block(map, lzo_block);
+ if (ret >= 0)
+ /* fetch the value from the cache */
+ *value = regcache_get_val(lzo_block->dst, blkpos,
+ map->cache_word_size);
+
+ kfree(lzo_block->dst);
+ /* restore the pointer and length of the compressed block */
+ lzo_block->dst = tmp_dst;
+ lzo_block->dst_len = tmp_dst_len;
+
+ return ret;
+}
+
+static int regcache_lzo_write(struct regmap *map,
+ unsigned int reg, unsigned int value)
+{
+ struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
+ int ret, blkindex, blkpos;
+ size_t blksize, tmp_dst_len;
+ void *tmp_dst;
+
+ /* index of the compressed lzo block */
+ blkindex = regcache_lzo_get_blkindex(map, reg);
+ /* register index within the decompressed block */
+ blkpos = regcache_lzo_get_blkpos(map, reg);
+ /* size of the compressed block */
+ blksize = regcache_lzo_get_blksize(map);
+ lzo_blocks = map->cache;
+ lzo_block = lzo_blocks[blkindex];
+
+ /* save the pointer and length of the compressed block */
+ tmp_dst = lzo_block->dst;
+ tmp_dst_len = lzo_block->dst_len;
+
+ /* prepare the source to be the compressed block */
+ lzo_block->src = lzo_block->dst;
+ lzo_block->src_len = lzo_block->dst_len;
+
+ /* decompress the block */
+ ret = regcache_lzo_decompress_cache_block(map, lzo_block);
+ if (ret < 0) {
+ kfree(lzo_block->dst);
+ goto out;
+ }
+
+ /* write the new value to the cache */
+ if (regcache_set_val(lzo_block->dst, blkpos, value,
+ map->cache_word_size)) {
+ kfree(lzo_block->dst);
+ goto out;
+ }
+
+ /* prepare the source to be the decompressed block */
+ lzo_block->src = lzo_block->dst;
+ lzo_block->src_len = lzo_block->dst_len;
+
+ /* compress the block */
+ ret = regcache_lzo_compress_cache_block(map, lzo_block);
+ if (ret < 0) {
+ kfree(lzo_block->dst);
+ kfree(lzo_block->src);
+ goto out;
+ }
+
+ /* set the bit so we know we have to sync this register */
+ set_bit(reg, lzo_block->sync_bmp);
+ kfree(tmp_dst);
+ kfree(lzo_block->src);
+ return 0;
+out:
+ lzo_block->dst = tmp_dst;
+ lzo_block->dst_len = tmp_dst_len;
+ return ret;
+}
+
+static int regcache_lzo_sync(struct regmap *map)
+{
+ struct regcache_lzo_ctx **lzo_blocks;
+ unsigned int val;
+ int i;
+ int ret;
+
+ lzo_blocks = map->cache;
+ for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) {
+ ret = regcache_read(map, i, &val);
+ if (ret)
+ return ret;
+ map->cache_bypass = 1;
+ ret = _regmap_write(map, i, val);
+ map->cache_bypass = 0;
+ if (ret)
+ return ret;
+ dev_dbg(map->dev, "Synced register %#x, value %#x\n",
+ i, val);
+ }
+
+ return 0;
+}
+
+struct regcache_ops regcache_lzo_ops = {
+ .type = REGCACHE_LZO,
+ .name = "lzo",
+ .init = regcache_lzo_init,
+ .exit = regcache_lzo_exit,
+ .read = regcache_lzo_read,
+ .write = regcache_lzo_write,
+ .sync = regcache_lzo_sync
+};
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
new file mode 100644
index 000000000000..e31498499b0f
--- /dev/null
+++ b/drivers/base/regmap/regcache-rbtree.c
@@ -0,0 +1,345 @@
+/*
+ * Register cache access API - rbtree caching support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/rbtree.h>
+
+#include "internal.h"
+
+static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
+ unsigned int value);
+
+struct regcache_rbtree_node {
+ /* the actual rbtree node holding this block */
+ struct rb_node node;
+ /* base register handled by this block */
+ unsigned int base_reg;
+ /* block of adjacent registers */
+ void *block;
+ /* number of registers available in the block */
+ unsigned int blklen;
+} __attribute__ ((packed));
+
+struct regcache_rbtree_ctx {
+ struct rb_root root;
+ struct regcache_rbtree_node *cached_rbnode;
+};
+
+static inline void regcache_rbtree_get_base_top_reg(
+ struct regcache_rbtree_node *rbnode,
+ unsigned int *base, unsigned int *top)
+{
+ *base = rbnode->base_reg;
+ *top = rbnode->base_reg + rbnode->blklen - 1;
+}
+
+static unsigned int regcache_rbtree_get_register(
+ struct regcache_rbtree_node *rbnode, unsigned int idx,
+ unsigned int word_size)
+{
+ return regcache_get_val(rbnode->block, idx, word_size);
+}
+
+static void regcache_rbtree_set_register(struct regcache_rbtree_node *rbnode,
+ unsigned int idx, unsigned int val,
+ unsigned int word_size)
+{
+ regcache_set_val(rbnode->block, idx, val, word_size);
+}
+
+static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
+ unsigned int reg)
+{
+ struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
+ struct rb_node *node;
+ struct regcache_rbtree_node *rbnode;
+ unsigned int base_reg, top_reg;
+
+ rbnode = rbtree_ctx->cached_rbnode;
+ if (rbnode) {
+ regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+ if (reg >= base_reg && reg <= top_reg)
+ return rbnode;
+ }
+
+ node = rbtree_ctx->root.rb_node;
+ while (node) {
+ rbnode = container_of(node, struct regcache_rbtree_node, node);
+ regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+ if (reg >= base_reg && reg <= top_reg) {
+ rbtree_ctx->cached_rbnode = rbnode;
+ return rbnode;
+ } else if (reg > top_reg) {
+ node = node->rb_right;
+ } else if (reg < base_reg) {
+ node = node->rb_left;
+ }
+ }
+
+ return NULL;
+}
+
+static int regcache_rbtree_insert(struct rb_root *root,
+ struct regcache_rbtree_node *rbnode)
+{
+ struct rb_node **new, *parent;
+ struct regcache_rbtree_node *rbnode_tmp;
+ unsigned int base_reg_tmp, top_reg_tmp;
+ unsigned int base_reg;
+
+ parent = NULL;
+ new = &root->rb_node;
+ while (*new) {
+ rbnode_tmp = container_of(*new, struct regcache_rbtree_node,
+ node);
+ /* base and top registers of the current rbnode */
+ regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp,
+ &top_reg_tmp);
+ /* base register of the rbnode to be added */
+ base_reg = rbnode->base_reg;
+ parent = *new;
+ /* if this register has already been inserted, just return */
+ if (base_reg >= base_reg_tmp &&
+ base_reg <= top_reg_tmp)
+ return 0;
+ else if (base_reg > top_reg_tmp)
+ new = &((*new)->rb_right);
+ else if (base_reg < base_reg_tmp)
+ new = &((*new)->rb_left);
+ }
+
+ /* insert the node into the rbtree */
+ rb_link_node(&rbnode->node, parent, new);
+ rb_insert_color(&rbnode->node, root);
+
+ return 1;
+}
+
+static int regcache_rbtree_init(struct regmap *map)
+{
+ struct regcache_rbtree_ctx *rbtree_ctx;
+ int i;
+ int ret;
+
+ map->cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
+ if (!map->cache)
+ return -ENOMEM;
+
+ rbtree_ctx = map->cache;
+ rbtree_ctx->root = RB_ROOT;
+ rbtree_ctx->cached_rbnode = NULL;
+
+ for (i = 0; i < map->num_reg_defaults; i++) {
+ ret = regcache_rbtree_write(map,
+ map->reg_defaults[i].reg,
+ map->reg_defaults[i].def);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_exit(map);
+ return ret;
+}
+
+static int regcache_rbtree_exit(struct regmap *map)
+{
+ struct rb_node *next;
+ struct regcache_rbtree_ctx *rbtree_ctx;
+ struct regcache_rbtree_node *rbtree_node;
+
+ /* if we've already been called then just return */
+ rbtree_ctx = map->cache;
+ if (!rbtree_ctx)
+ return 0;
+
+ /* free up the rbtree */
+ next = rb_first(&rbtree_ctx->root);
+ while (next) {
+ rbtree_node = rb_entry(next, struct regcache_rbtree_node, node);
+ next = rb_next(&rbtree_node->node);
+ rb_erase(&rbtree_node->node, &rbtree_ctx->root);
+ kfree(rbtree_node->block);
+ kfree(rbtree_node);
+ }
+
+ /* release the resources */
+ kfree(map->cache);
+ map->cache = NULL;
+
+ return 0;
+}
+
+static int regcache_rbtree_read(struct regmap *map,
+ unsigned int reg, unsigned int *value)
+{
+ struct regcache_rbtree_node *rbnode;
+ unsigned int reg_tmp;
+
+ rbnode = regcache_rbtree_lookup(map, reg);
+ if (rbnode) {
+ reg_tmp = reg - rbnode->base_reg;
+ *value = regcache_rbtree_get_register(rbnode, reg_tmp,
+ map->cache_word_size);
+ } else {
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+
+static int regcache_rbtree_insert_to_block(struct regcache_rbtree_node *rbnode,
+ unsigned int pos, unsigned int reg,
+ unsigned int value, unsigned int word_size)
+{
+ u8 *blk;
+
+ blk = krealloc(rbnode->block,
+ (rbnode->blklen + 1) * word_size, GFP_KERNEL);
+ if (!blk)
+ return -ENOMEM;
+
+ /* insert the register value in the correct place in the rbnode block */
+ memmove(blk + (pos + 1) * word_size,
+ blk + pos * word_size,
+ (rbnode->blklen - pos) * word_size);
+
+ /* update the rbnode block, its size and the base register */
+ rbnode->block = blk;
+ rbnode->blklen++;
+ if (!pos)
+ rbnode->base_reg = reg;
+
+ regcache_rbtree_set_register(rbnode, pos, value, word_size);
+ return 0;
+}
+
+static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
+ unsigned int value)
+{
+ struct regcache_rbtree_ctx *rbtree_ctx;
+ struct regcache_rbtree_node *rbnode, *rbnode_tmp;
+ struct rb_node *node;
+ unsigned int val;
+ unsigned int reg_tmp;
+ unsigned int pos;
+ int i;
+ int ret;
+
+ rbtree_ctx = map->cache;
+ /* if we can't locate it in the cached rbnode we'll have
+ * to traverse the rbtree looking for it.
+ */
+ rbnode = regcache_rbtree_lookup(map, reg);
+ if (rbnode) {
+ reg_tmp = reg - rbnode->base_reg;
+ val = regcache_rbtree_get_register(rbnode, reg_tmp,
+ map->cache_word_size);
+ if (val == value)
+ return 0;
+ regcache_rbtree_set_register(rbnode, reg_tmp, value,
+ map->cache_word_size);
+ } else {
+ /* look for an adjacent register to the one we are about to add */
+ for (node = rb_first(&rbtree_ctx->root); node;
+ node = rb_next(node)) {
+ rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node);
+ for (i = 0; i < rbnode_tmp->blklen; i++) {
+ reg_tmp = rbnode_tmp->base_reg + i;
+ if (abs(reg_tmp - reg) != 1)
+ continue;
+ /* decide where in the block to place our register */
+ if (reg_tmp + 1 == reg)
+ pos = i + 1;
+ else
+ pos = i;
+ ret = regcache_rbtree_insert_to_block(rbnode_tmp, pos,
+ reg, value,
+ map->cache_word_size);
+ if (ret)
+ return ret;
+ rbtree_ctx->cached_rbnode = rbnode_tmp;
+ return 0;
+ }
+ }
+ /* we did not manage to find a place to insert it in an existing
+ * block so create a new rbnode with a single register in its block.
+ * This block will get populated further if any other adjacent
+ * registers get modified in the future.
+ */
+ rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
+ if (!rbnode)
+ return -ENOMEM;
+ rbnode->blklen = 1;
+ rbnode->base_reg = reg;
+ rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
+ GFP_KERNEL);
+ if (!rbnode->block) {
+ kfree(rbnode);
+ return -ENOMEM;
+ }
+ regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size);
+ regcache_rbtree_insert(&rbtree_ctx->root, rbnode);
+ rbtree_ctx->cached_rbnode = rbnode;
+ }
+
+ return 0;
+}
+
+static int regcache_rbtree_sync(struct regmap *map)
+{
+ struct regcache_rbtree_ctx *rbtree_ctx;
+ struct rb_node *node;
+ struct regcache_rbtree_node *rbnode;
+ unsigned int regtmp;
+ unsigned int val;
+ int ret;
+ int i;
+
+ rbtree_ctx = map->cache;
+ for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
+ rbnode = rb_entry(node, struct regcache_rbtree_node, node);
+ for (i = 0; i < rbnode->blklen; i++) {
+ regtmp = rbnode->base_reg + i;
+ val = regcache_rbtree_get_register(rbnode, i,
+ map->cache_word_size);
+
+ /* Is this the hardware default? If so skip. */
+ ret = regcache_lookup_reg(map, i);
+ if (ret > 0 && val == map->reg_defaults[ret].def)
+ continue;
+
+ map->cache_bypass = 1;
+ ret = _regmap_write(map, regtmp, val);
+ map->cache_bypass = 0;
+ if (ret)
+ return ret;
+ dev_dbg(map->dev, "Synced register %#x, value %#x\n",
+ regtmp, val);
+ }
+ }
+
+ return 0;
+}
+
+struct regcache_ops regcache_rbtree_ops = {
+ .type = REGCACHE_RBTREE,
+ .name = "rbtree",
+ .init = regcache_rbtree_init,
+ .exit = regcache_rbtree_exit,
+ .read = regcache_rbtree_read,
+ .write = regcache_rbtree_write,
+ .sync = regcache_rbtree_sync
+};
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
new file mode 100644
index 000000000000..666f6f5011dc
--- /dev/null
+++ b/drivers/base/regmap/regcache.c
@@ -0,0 +1,402 @@
+/*
+ * Register cache access API
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <trace/events/regmap.h>
+#include <linux/bsearch.h>
+#include <linux/sort.h>
+
+#include "internal.h"
+
+static const struct regcache_ops *cache_types[] = {
+ &regcache_indexed_ops,
+ &regcache_rbtree_ops,
+ &regcache_lzo_ops,
+};
+
+static int regcache_hw_init(struct regmap *map)
+{
+ int i, j;
+ int ret;
+ int count;
+ unsigned int val;
+ void *tmp_buf;
+
+ if (!map->num_reg_defaults_raw)
+ return -EINVAL;
+
+ if (!map->reg_defaults_raw) {
+ dev_warn(map->dev, "No cache defaults, reading back from HW\n");
+ tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
+ if (!tmp_buf)
+ return -EINVAL;
+ ret = regmap_bulk_read(map, 0, tmp_buf,
+ map->num_reg_defaults_raw);
+ if (ret < 0) {
+ kfree(tmp_buf);
+ return ret;
+ }
+ map->reg_defaults_raw = tmp_buf;
+ map->cache_free = 1;
+ }
+
+ /* calculate the size of reg_defaults */
+ for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) {
+ val = regcache_get_val(map->reg_defaults_raw,
+ i, map->cache_word_size);
+ if (!val)
+ continue;
+ count++;
+ }
+
+ map->reg_defaults = kmalloc(count * sizeof(struct reg_default),
+ GFP_KERNEL);
+ if (!map->reg_defaults)
+ return -ENOMEM;
+
+ /* fill the reg_defaults */
+ map->num_reg_defaults = count;
+ for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
+ val = regcache_get_val(map->reg_defaults_raw,
+ i, map->cache_word_size);
+ if (!val)
+ continue;
+ map->reg_defaults[j].reg = i;
+ map->reg_defaults[j].def = val;
+ j++;
+ }
+
+ return 0;
+}
+
+int regcache_init(struct regmap *map)
+{
+ int ret;
+ int i;
+ void *tmp_buf;
+
+ if (map->cache_type == REGCACHE_NONE) {
+ map->cache_bypass = true;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cache_types); i++)
+ if (cache_types[i]->type == map->cache_type)
+ break;
+
+ if (i == ARRAY_SIZE(cache_types)) {
+ dev_err(map->dev, "Could not match compress type: %d\n",
+ map->cache_type);
+ return -EINVAL;
+ }
+
+ map->cache = NULL;
+ map->cache_ops = cache_types[i];
+
+ if (!map->cache_ops->read ||
+ !map->cache_ops->write ||
+ !map->cache_ops->name)
+ return -EINVAL;
+
+ /* We still need to ensure that the reg_defaults
+ * won't vanish from under us. We'll need to make
+ * a copy of it.
+ */
+ if (map->reg_defaults) {
+ if (!map->num_reg_defaults)
+ return -EINVAL;
+ tmp_buf = kmemdup(map->reg_defaults, map->num_reg_defaults *
+ sizeof(struct reg_default), GFP_KERNEL);
+ if (!tmp_buf)
+ return -ENOMEM;
+ map->reg_defaults = tmp_buf;
+ } else if (map->num_reg_defaults_raw) {
+ /* Some devices such as PMICs don't have cache defaults,
+ * we cope with this by reading back the HW registers and
+ * crafting the cache defaults by hand.
+ */
+ ret = regcache_hw_init(map);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (!map->max_register)
+ map->max_register = map->num_reg_defaults_raw;
+
+ if (map->cache_ops->init) {
+ dev_dbg(map->dev, "Initializing %s cache\n",
+ map->cache_ops->name);
+ return map->cache_ops->init(map);
+ }
+ return 0;
+}
+
+void regcache_exit(struct regmap *map)
+{
+ if (map->cache_type == REGCACHE_NONE)
+ return;
+
+ BUG_ON(!map->cache_ops);
+
+ kfree(map->reg_defaults);
+ if (map->cache_free)
+ kfree(map->reg_defaults_raw);
+
+ if (map->cache_ops->exit) {
+ dev_dbg(map->dev, "Destroying %s cache\n",
+ map->cache_ops->name);
+ map->cache_ops->exit(map);
+ }
+}
+
+/**
+ * regcache_read: Fetch the value of a given register from the cache.
+ *
+ * @map: map to configure.
+ * @reg: The register index.
+ * @value: The value to be returned.
+ *
+ * Return a negative value on failure, 0 on success.
+ */
+int regcache_read(struct regmap *map,
+ unsigned int reg, unsigned int *value)
+{
+ if (map->cache_type == REGCACHE_NONE)
+ return -ENOSYS;
+
+ BUG_ON(!map->cache_ops);
+
+ if (!regmap_readable(map, reg))
+ return -EIO;
+
+ if (!regmap_volatile(map, reg))
+ return map->cache_ops->read(map, reg, value);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(regcache_read);
+
+/**
+ * regcache_write: Set the value of a given register in the cache.
+ *
+ * @map: map to configure.
+ * @reg: The register index.
+ * @value: The new register value.
+ *
+ * Return a negative value on failure, 0 on success.
+ */
+int regcache_write(struct regmap *map,
+ unsigned int reg, unsigned int value)
+{
+ if (map->cache_type == REGCACHE_NONE)
+ return 0;
+
+ BUG_ON(!map->cache_ops);
+
+ if (!regmap_writeable(map, reg))
+ return -EIO;
+
+ if (!regmap_volatile(map, reg))
+ return map->cache_ops->write(map, reg, value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regcache_write);
+
+/**
+ * regcache_sync: Sync the register cache with the hardware.
+ *
+ * @map: map to configure.
+ *
+ * Any registers that should not be synced should be marked as
+ * volatile. In general drivers can choose not to use the provided
+ * syncing functionality if they so require.
+ *
+ * Return a negative value on failure, 0 on success.
+ */
+int regcache_sync(struct regmap *map)
+{
+ int ret = 0;
+ unsigned int val;
+ unsigned int i;
+ const char *name;
+ unsigned int bypass;
+
+ BUG_ON(!map->cache_ops);
+
+ mutex_lock(&map->lock);
+ /* Remember the initial bypass state */
+ bypass = map->cache_bypass;
+ dev_dbg(map->dev, "Syncing %s cache\n",
+ map->cache_ops->name);
+ name = map->cache_ops->name;
+ trace_regcache_sync(map->dev, name, "start");
+ if (map->cache_ops->sync) {
+ ret = map->cache_ops->sync(map);
+ } else {
+ for (i = 0; i < map->num_reg_defaults; i++) {
+ ret = regcache_read(map, i, &val);
+ if (ret < 0)
+ goto out;
+ map->cache_bypass = 1;
+ ret = _regmap_write(map, i, val);
+ map->cache_bypass = 0;
+ if (ret < 0)
+ goto out;
+ dev_dbg(map->dev, "Synced register %#x, value %#x\n",
+ map->reg_defaults[i].reg,
+ map->reg_defaults[i].def);
+ }
+
+ }
+out:
+ trace_regcache_sync(map->dev, name, "stop");
+ /* Restore the bypass state */
+ map->cache_bypass = bypass;
+ mutex_unlock(&map->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regcache_sync);
+
+/**
+ * regcache_cache_only: Put a register map into cache only mode
+ *
+ * @map: map to configure
+ * @cache_only: flag if changes should be written to the hardware
+ *
+ * When a register map is marked as cache only writes to the register
+ * map API will only update the register cache, they will not cause
+ * any hardware changes. This is useful for allowing portions of
+ * drivers to act as though the device were functioning as normal when
+ * it is disabled for power saving reasons.
+ */
+void regcache_cache_only(struct regmap *map, bool enable)
+{
+ mutex_lock(&map->lock);
+ WARN_ON(map->cache_bypass && enable);
+ map->cache_only = enable;
+ mutex_unlock(&map->lock);
+}
+EXPORT_SYMBOL_GPL(regcache_cache_only);
+
+/**
+ * regcache_cache_bypass: Put a register map into cache bypass mode
+ *
+ * @map: map to configure
+ * @cache_bypass: flag if changes should not be written to the hardware
+ *
+ * When a register map is marked with the cache bypass option, writes
+ * to the register map API will only update the hardware and not the
+ * the cache directly. This is useful when syncing the cache back to
+ * the hardware.
+ */
+void regcache_cache_bypass(struct regmap *map, bool enable)
+{
+ mutex_lock(&map->lock);
+ WARN_ON(map->cache_only && enable);
+ map->cache_bypass = enable;
+ mutex_unlock(&map->lock);
+}
+EXPORT_SYMBOL_GPL(regcache_cache_bypass);
+
+bool regcache_set_val(void *base, unsigned int idx,
+ unsigned int val, unsigned int word_size)
+{
+ switch (word_size) {
+ case 1: {
+ u8 *cache = base;
+ if (cache[idx] == val)
+ return true;
+ cache[idx] = val;
+ break;
+ }
+ case 2: {
+ u16 *cache = base;
+ if (cache[idx] == val)
+ return true;
+ cache[idx] = val;
+ break;
+ }
+ default:
+ BUG();
+ }
+ /* unreachable */
+ return false;
+}
+
+unsigned int regcache_get_val(const void *base, unsigned int idx,
+ unsigned int word_size)
+{
+ if (!base)
+ return -EINVAL;
+
+ switch (word_size) {
+ case 1: {
+ const u8 *cache = base;
+ return cache[idx];
+ }
+ case 2: {
+ const u16 *cache = base;
+ return cache[idx];
+ }
+ default:
+ BUG();
+ }
+ /* unreachable */
+ return -1;
+}
+
+static int regcache_default_cmp(const void *a, const void *b)
+{
+ const struct reg_default *_a = a;
+ const struct reg_default *_b = b;
+
+ return _a->reg - _b->reg;
+}
+
+int regcache_lookup_reg(struct regmap *map, unsigned int reg)
+{
+ struct reg_default key;
+ struct reg_default *r;
+
+ key.reg = reg;
+ key.def = 0;
+
+ r = bsearch(&key, map->reg_defaults, map->num_reg_defaults,
+ sizeof(struct reg_default), regcache_default_cmp);
+
+ if (r)
+ return r - map->reg_defaults;
+ else
+ return -ENOENT;
+}
+
+int regcache_insert_reg(struct regmap *map, unsigned int reg,
+ unsigned int val)
+{
+ void *tmp;
+
+ tmp = krealloc(map->reg_defaults,
+ (map->num_reg_defaults + 1) * sizeof(struct reg_default),
+ GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+ map->reg_defaults = tmp;
+ map->num_reg_defaults++;
+ map->reg_defaults[map->num_reg_defaults - 1].reg = reg;
+ map->reg_defaults[map->num_reg_defaults - 1].def = val;
+ sort(map->reg_defaults, map->num_reg_defaults,
+ sizeof(struct reg_default), regcache_default_cmp, NULL);
+ return 0;
+}
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
new file mode 100644
index 000000000000..6f397476e27c
--- /dev/null
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -0,0 +1,209 @@
+/*
+ * Register map access API - debugfs
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+#include "internal.h"
+
+static struct dentry *regmap_debugfs_root;
+
+/* Calculate the length of a fixed format */
+static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
+{
+ snprintf(buf, buf_size, "%x", max_val);
+ return strlen(buf);
+}
+
+static int regmap_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int reg_len, val_len, tot_len;
+ size_t buf_pos = 0;
+ loff_t p = 0;
+ ssize_t ret;
+ int i;
+ struct regmap *map = file->private_data;
+ char *buf;
+ unsigned int val;
+
+ if (*ppos < 0 || !count)
+ return -EINVAL;
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Calculate the length of a fixed format */
+ reg_len = regmap_calc_reg_len(map->max_register, buf, count);
+ val_len = 2 * map->format.val_bytes;
+ tot_len = reg_len + val_len + 3; /* : \n */
+
+ for (i = 0; i < map->max_register + 1; i++) {
+ if (!regmap_readable(map, i))
+ continue;
+
+ if (regmap_precious(map, i))
+ continue;
+
+ /* If we're in the region the user is trying to read */
+ if (p >= *ppos) {
+ /* ...but not beyond it */
+ if (buf_pos >= count - 1 - tot_len)
+ break;
+
+ /* Format the register */
+ snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
+ reg_len, i);
+ buf_pos += reg_len + 2;
+
+ /* Format the value, write all X if we can't read */
+ ret = regmap_read(map, i, &val);
+ if (ret == 0)
+ snprintf(buf + buf_pos, count - buf_pos,
+ "%.*x", val_len, val);
+ else
+ memset(buf + buf_pos, 'X', val_len);
+ buf_pos += 2 * map->format.val_bytes;
+
+ buf[buf_pos++] = '\n';
+ }
+ p += tot_len;
+ }
+
+ ret = buf_pos;
+
+ if (copy_to_user(user_buf, buf, buf_pos)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ *ppos += buf_pos;
+
+out:
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations regmap_map_fops = {
+ .open = regmap_open_file,
+ .read = regmap_map_read_file,
+ .llseek = default_llseek,
+};
+
+static ssize_t regmap_access_read_file(struct file *file,
+ char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ int reg_len, tot_len;
+ size_t buf_pos = 0;
+ loff_t p = 0;
+ ssize_t ret;
+ int i;
+ struct regmap *map = file->private_data;
+ char *buf;
+
+ if (*ppos < 0 || !count)
+ return -EINVAL;
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Calculate the length of a fixed format */
+ reg_len = regmap_calc_reg_len(map->max_register, buf, count);
+ tot_len = reg_len + 10; /* ': R W V P\n' */
+
+ for (i = 0; i < map->max_register + 1; i++) {
+ /* Ignore registers which are neither readable nor writable */
+ if (!regmap_readable(map, i) && !regmap_writeable(map, i))
+ continue;
+
+ /* If we're in the region the user is trying to read */
+ if (p >= *ppos) {
+ /* ...but not beyond it */
+ if (buf_pos >= count - 1 - tot_len)
+ break;
+
+ /* Format the register */
+ snprintf(buf + buf_pos, count - buf_pos,
+ "%.*x: %c %c %c %c\n",
+ reg_len, i,
+ regmap_readable(map, i) ? 'y' : 'n',
+ regmap_writeable(map, i) ? 'y' : 'n',
+ regmap_volatile(map, i) ? 'y' : 'n',
+ regmap_precious(map, i) ? 'y' : 'n');
+
+ buf_pos += tot_len;
+ }
+ p += tot_len;
+ }
+
+ ret = buf_pos;
+
+ if (copy_to_user(user_buf, buf, buf_pos)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ *ppos += buf_pos;
+
+out:
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations regmap_access_fops = {
+ .open = regmap_open_file,
+ .read = regmap_access_read_file,
+ .llseek = default_llseek,
+};
+
+void regmap_debugfs_init(struct regmap *map)
+{
+ map->debugfs = debugfs_create_dir(dev_name(map->dev),
+ regmap_debugfs_root);
+ if (!map->debugfs) {
+ dev_warn(map->dev, "Failed to create debugfs directory\n");
+ return;
+ }
+
+ if (map->max_register) {
+ debugfs_create_file("registers", 0400, map->debugfs,
+ map, &regmap_map_fops);
+ debugfs_create_file("access", 0400, map->debugfs,
+ map, &regmap_access_fops);
+ }
+}
+
+void regmap_debugfs_exit(struct regmap *map)
+{
+ debugfs_remove_recursive(map->debugfs);
+}
+
+void regmap_debugfs_initcall(void)
+{
+ regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
+ if (!regmap_debugfs_root) {
+ pr_warn("regmap: Failed to create debugfs root\n");
+ return;
+ }
+}
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
new file mode 100644
index 000000000000..38621ec87c05
--- /dev/null
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -0,0 +1,114 @@
+/*
+ * Register map access API - I2C support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+static int regmap_i2c_write(struct device *dev, const void *data, size_t count)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_master_send(i2c, data, count);
+ if (ret == count)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int regmap_i2c_gather_write(struct device *dev,
+ const void *reg, size_t reg_size,
+ const void *val, size_t val_size)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct i2c_msg xfer[2];
+ int ret;
+
+ /* If the I2C controller can't do a gather tell the core, it
+ * will substitute in a linear write for us.
+ */
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_PROTOCOL_MANGLING))
+ return -ENOTSUPP;
+
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = reg_size;
+ xfer[0].buf = (void *)reg;
+
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_NOSTART;
+ xfer[1].len = val_size;
+ xfer[1].buf = (void *)val;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret == 2)
+ return 0;
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int regmap_i2c_read(struct device *dev,
+ const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct i2c_msg xfer[2];
+ int ret;
+
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = reg_size;
+ xfer[0].buf = (void *)reg;
+
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = val_size;
+ xfer[1].buf = val;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret == 2)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static struct regmap_bus regmap_i2c = {
+ .write = regmap_i2c_write,
+ .gather_write = regmap_i2c_gather_write,
+ .read = regmap_i2c_read,
+};
+
+/**
+ * regmap_init_i2c(): Initialise register map
+ *
+ * @i2c: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_i2c(struct i2c_client *i2c,
+ const struct regmap_config *config)
+{
+ return regmap_init(&i2c->dev, &regmap_i2c, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_i2c);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c
new file mode 100644
index 000000000000..2560658de344
--- /dev/null
+++ b/drivers/base/regmap/regmap-spi.c
@@ -0,0 +1,73 @@
+/*
+ * Register map access API - SPI support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+static int regmap_spi_write(struct device *dev, const void *data, size_t count)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ return spi_write(spi, data, count);
+}
+
+static int regmap_spi_gather_write(struct device *dev,
+ const void *reg, size_t reg_len,
+ const void *val, size_t val_len)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct spi_message m;
+ struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, },
+ { .tx_buf = val, .len = val_len, }, };
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+
+ return spi_sync(spi, &m);
+}
+
+static int regmap_spi_read(struct device *dev,
+ const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ return spi_write_then_read(spi, reg, reg_size, val, val_size);
+}
+
+static struct regmap_bus regmap_spi = {
+ .write = regmap_spi_write,
+ .gather_write = regmap_spi_gather_write,
+ .read = regmap_spi_read,
+ .read_flag_mask = 0x80,
+};
+
+/**
+ * regmap_init_spi(): Initialise register map
+ *
+ * @spi: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_spi(struct spi_device *spi,
+ const struct regmap_config *config)
+{
+ return regmap_init(&spi->dev, &regmap_spi, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_spi);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
new file mode 100644
index 000000000000..bf441db1ee90
--- /dev/null
+++ b/drivers/base/regmap/regmap.c
@@ -0,0 +1,589 @@
+/*
+ * Register map access API
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/regmap.h>
+
+#include "internal.h"
+
+bool regmap_writeable(struct regmap *map, unsigned int reg)
+{
+ if (map->max_register && reg > map->max_register)
+ return false;
+
+ if (map->writeable_reg)
+ return map->writeable_reg(map->dev, reg);
+
+ return true;
+}
+
+bool regmap_readable(struct regmap *map, unsigned int reg)
+{
+ if (map->max_register && reg > map->max_register)
+ return false;
+
+ if (map->readable_reg)
+ return map->readable_reg(map->dev, reg);
+
+ return true;
+}
+
+bool regmap_volatile(struct regmap *map, unsigned int reg)
+{
+ if (map->max_register && reg > map->max_register)
+ return false;
+
+ if (map->volatile_reg)
+ return map->volatile_reg(map->dev, reg);
+
+ return true;
+}
+
+bool regmap_precious(struct regmap *map, unsigned int reg)
+{
+ if (map->max_register && reg > map->max_register)
+ return false;
+
+ if (map->precious_reg)
+ return map->precious_reg(map->dev, reg);
+
+ return false;
+}
+
+static void regmap_format_4_12_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ __be16 *out = map->work_buf;
+ *out = cpu_to_be16((reg << 12) | val);
+}
+
+static void regmap_format_7_9_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ __be16 *out = map->work_buf;
+ *out = cpu_to_be16((reg << 9) | val);
+}
+
+static void regmap_format_8(void *buf, unsigned int val)
+{
+ u8 *b = buf;
+
+ b[0] = val;
+}
+
+static void regmap_format_16(void *buf, unsigned int val)
+{
+ __be16 *b = buf;
+
+ b[0] = cpu_to_be16(val);
+}
+
+static unsigned int regmap_parse_8(void *buf)
+{
+ u8 *b = buf;
+
+ return b[0];
+}
+
+static unsigned int regmap_parse_16(void *buf)
+{
+ __be16 *b = buf;
+
+ b[0] = be16_to_cpu(b[0]);
+
+ return b[0];
+}
+
+/**
+ * regmap_init(): Initialise register map
+ *
+ * @dev: Device that will be interacted with
+ * @bus: Bus-specific callbacks to use with device
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap. This function should generally not be called
+ * directly, it should be called by bus-specific init functions.
+ */
+struct regmap *regmap_init(struct device *dev,
+ const struct regmap_bus *bus,
+ const struct regmap_config *config)
+{
+ struct regmap *map;
+ int ret = -EINVAL;
+
+ if (!bus || !config)
+ return NULL;
+
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (map == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ mutex_init(&map->lock);
+ map->format.buf_size = (config->reg_bits + config->val_bits) / 8;
+ map->format.reg_bytes = config->reg_bits / 8;
+ map->format.val_bytes = config->val_bits / 8;
+ map->dev = dev;
+ map->bus = bus;
+ map->max_register = config->max_register;
+ map->writeable_reg = config->writeable_reg;
+ map->readable_reg = config->readable_reg;
+ map->volatile_reg = config->volatile_reg;
+ map->precious_reg = config->precious_reg;
+ map->cache_type = config->cache_type;
+ map->reg_defaults = config->reg_defaults;
+ map->num_reg_defaults = config->num_reg_defaults;
+ map->num_reg_defaults_raw = config->num_reg_defaults_raw;
+ map->reg_defaults_raw = config->reg_defaults_raw;
+ map->cache_size_raw = (config->val_bits / 8) * config->num_reg_defaults_raw;
+ map->cache_word_size = config->val_bits / 8;
+
+ if (config->read_flag_mask || config->write_flag_mask) {
+ map->read_flag_mask = config->read_flag_mask;
+ map->write_flag_mask = config->write_flag_mask;
+ } else {
+ map->read_flag_mask = bus->read_flag_mask;
+ }
+
+ switch (config->reg_bits) {
+ case 4:
+ switch (config->val_bits) {
+ case 12:
+ map->format.format_write = regmap_format_4_12_write;
+ break;
+ default:
+ goto err_map;
+ }
+ break;
+
+ case 7:
+ switch (config->val_bits) {
+ case 9:
+ map->format.format_write = regmap_format_7_9_write;
+ break;
+ default:
+ goto err_map;
+ }
+ break;
+
+ case 8:
+ map->format.format_reg = regmap_format_8;
+ break;
+
+ case 16:
+ map->format.format_reg = regmap_format_16;
+ break;
+
+ default:
+ goto err_map;
+ }
+
+ switch (config->val_bits) {
+ case 8:
+ map->format.format_val = regmap_format_8;
+ map->format.parse_val = regmap_parse_8;
+ break;
+ case 16:
+ map->format.format_val = regmap_format_16;
+ map->format.parse_val = regmap_parse_16;
+ break;
+ }
+
+ if (!map->format.format_write &&
+ !(map->format.format_reg && map->format.format_val))
+ goto err_map;
+
+ map->work_buf = kmalloc(map->format.buf_size, GFP_KERNEL);
+ if (map->work_buf == NULL) {
+ ret = -ENOMEM;
+ goto err_map;
+ }
+
+ ret = regcache_init(map);
+ if (ret < 0)
+ goto err_map;
+
+ regmap_debugfs_init(map);
+
+ return map;
+
+err_map:
+ kfree(map);
+err:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(regmap_init);
+
+/**
+ * regmap_exit(): Free a previously allocated register map
+ */
+void regmap_exit(struct regmap *map)
+{
+ regcache_exit(map);
+ regmap_debugfs_exit(map);
+ kfree(map->work_buf);
+ kfree(map);
+}
+EXPORT_SYMBOL_GPL(regmap_exit);
+
+static int _regmap_raw_write(struct regmap *map, unsigned int reg,
+ const void *val, size_t val_len)
+{
+ u8 *u8 = map->work_buf;
+ void *buf;
+ int ret = -ENOTSUPP;
+ size_t len;
+ int i;
+
+ /* Check for unwritable registers before we start */
+ if (map->writeable_reg)
+ for (i = 0; i < val_len / map->format.val_bytes; i++)
+ if (!map->writeable_reg(map->dev, reg + i))
+ return -EINVAL;
+
+ map->format.format_reg(map->work_buf, reg);
+
+ u8[0] |= map->write_flag_mask;
+
+ trace_regmap_hw_write_start(map->dev, reg,
+ val_len / map->format.val_bytes);
+
+ /* If we're doing a single register write we can probably just
+ * send the work_buf directly, otherwise try to do a gather
+ * write.
+ */
+ if (val == map->work_buf + map->format.reg_bytes)
+ ret = map->bus->write(map->dev, map->work_buf,
+ map->format.reg_bytes + val_len);
+ else if (map->bus->gather_write)
+ ret = map->bus->gather_write(map->dev, map->work_buf,
+ map->format.reg_bytes,
+ val, val_len);
+
+ /* If that didn't work fall back on linearising by hand. */
+ if (ret == -ENOTSUPP) {
+ len = map->format.reg_bytes + val_len;
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf, map->work_buf, map->format.reg_bytes);
+ memcpy(buf + map->format.reg_bytes, val, val_len);
+ ret = map->bus->write(map->dev, buf, len);
+
+ kfree(buf);
+ }
+
+ trace_regmap_hw_write_done(map->dev, reg,
+ val_len / map->format.val_bytes);
+
+ return ret;
+}
+
+int _regmap_write(struct regmap *map, unsigned int reg,
+ unsigned int val)
+{
+ int ret;
+ BUG_ON(!map->format.format_write && !map->format.format_val);
+
+ if (!map->cache_bypass) {
+ ret = regcache_write(map, reg, val);
+ if (ret != 0)
+ return ret;
+ if (map->cache_only)
+ return 0;
+ }
+
+ trace_regmap_reg_write(map->dev, reg, val);
+
+ if (map->format.format_write) {
+ map->format.format_write(map, reg, val);
+
+ trace_regmap_hw_write_start(map->dev, reg, 1);
+
+ ret = map->bus->write(map->dev, map->work_buf,
+ map->format.buf_size);
+
+ trace_regmap_hw_write_done(map->dev, reg, 1);
+
+ return ret;
+ } else {
+ map->format.format_val(map->work_buf + map->format.reg_bytes,
+ val);
+ return _regmap_raw_write(map, reg,
+ map->work_buf + map->format.reg_bytes,
+ map->format.val_bytes);
+ }
+}
+
+/**
+ * regmap_write(): Write a value to a single register
+ *
+ * @map: Register map to write to
+ * @reg: Register to write to
+ * @val: Value to be written
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
+{
+ int ret;
+
+ mutex_lock(&map->lock);
+
+ ret = _regmap_write(map, reg, val);
+
+ mutex_unlock(&map->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_write);
+
+/**
+ * regmap_raw_write(): Write raw values to one or more registers
+ *
+ * @map: Register map to write to
+ * @reg: Initial register to write to
+ * @val: Block of data to be written, laid out for direct transmission to the
+ * device
+ * @val_len: Length of data pointed to by val.
+ *
+ * This function is intended to be used for things like firmware
+ * download where a large block of data needs to be transferred to the
+ * device. No formatting will be done on the data provided.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_raw_write(struct regmap *map, unsigned int reg,
+ const void *val, size_t val_len)
+{
+ int ret;
+
+ WARN_ON(map->cache_type != REGCACHE_NONE);
+
+ mutex_lock(&map->lock);
+
+ ret = _regmap_raw_write(map, reg, val, val_len);
+
+ mutex_unlock(&map->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_raw_write);
+
+static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
+ unsigned int val_len)
+{
+ u8 *u8 = map->work_buf;
+ int ret;
+
+ map->format.format_reg(map->work_buf, reg);
+
+ /*
+ * Some buses or devices flag reads by setting the high bits in the
+ * register addresss; since it's always the high bits for all
+ * current formats we can do this here rather than in
+ * formatting. This may break if we get interesting formats.
+ */
+ u8[0] |= map->read_flag_mask;
+
+ trace_regmap_hw_read_start(map->dev, reg,
+ val_len / map->format.val_bytes);
+
+ ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes,
+ val, val_len);
+
+ trace_regmap_hw_read_done(map->dev, reg,
+ val_len / map->format.val_bytes);
+
+ return ret;
+}
+
+static int _regmap_read(struct regmap *map, unsigned int reg,
+ unsigned int *val)
+{
+ int ret;
+
+ if (!map->format.parse_val)
+ return -EINVAL;
+
+ if (!map->cache_bypass) {
+ ret = regcache_read(map, reg, val);
+ if (ret == 0)
+ return 0;
+ }
+
+ if (map->cache_only)
+ return -EBUSY;
+
+ ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
+ if (ret == 0) {
+ *val = map->format.parse_val(map->work_buf);
+ trace_regmap_reg_read(map->dev, reg, *val);
+ }
+
+ return ret;
+}
+
+/**
+ * regmap_read(): Read a value from a single register
+ *
+ * @map: Register map to write to
+ * @reg: Register to be read from
+ * @val: Pointer to store read value
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
+{
+ int ret;
+
+ mutex_lock(&map->lock);
+
+ ret = _regmap_read(map, reg, val);
+
+ mutex_unlock(&map->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_read);
+
+/**
+ * regmap_raw_read(): Read raw data from the device
+ *
+ * @map: Register map to write to
+ * @reg: First register to be read from
+ * @val: Pointer to store read value
+ * @val_len: Size of data to read
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
+ size_t val_len)
+{
+ int ret;
+ int i;
+ bool vol = true;
+
+ for (i = 0; i < val_len / map->format.val_bytes; i++)
+ if (!regmap_volatile(map, reg + i))
+ vol = false;
+
+ WARN_ON(!vol && map->cache_type != REGCACHE_NONE);
+
+ mutex_lock(&map->lock);
+
+ ret = _regmap_raw_read(map, reg, val, val_len);
+
+ mutex_unlock(&map->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_raw_read);
+
+/**
+ * regmap_bulk_read(): Read multiple registers from the device
+ *
+ * @map: Register map to write to
+ * @reg: First register to be read from
+ * @val: Pointer to store read value, in native register size for device
+ * @val_count: Number of registers to read
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
+ size_t val_count)
+{
+ int ret, i;
+ size_t val_bytes = map->format.val_bytes;
+ bool vol = true;
+
+ if (!map->format.parse_val)
+ return -EINVAL;
+
+ /* Is this a block of volatile registers? */
+ for (i = 0; i < val_count; i++)
+ if (!regmap_volatile(map, reg + i))
+ vol = false;
+
+ if (vol || map->cache_type == REGCACHE_NONE) {
+ ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
+ if (ret != 0)
+ return ret;
+
+ for (i = 0; i < val_count * val_bytes; i += val_bytes)
+ map->format.parse_val(val + i);
+ } else {
+ for (i = 0; i < val_count; i++) {
+ ret = regmap_read(map, reg + i, val + (i * val_bytes));
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regmap_bulk_read);
+
+/**
+ * remap_update_bits: Perform a read/modify/write cycle on the register map
+ *
+ * @map: Register map to update
+ * @reg: Register to update
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ *
+ * Returns zero for success, a negative number on error.
+ */
+int regmap_update_bits(struct regmap *map, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ int ret;
+ unsigned int tmp;
+
+ mutex_lock(&map->lock);
+
+ ret = _regmap_read(map, reg, &tmp);
+ if (ret != 0)
+ goto out;
+
+ tmp &= ~mask;
+ tmp |= val & mask;
+
+ ret = _regmap_write(map, reg, tmp);
+
+out:
+ mutex_unlock(&map->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_update_bits);
+
+static int __init regmap_initcall(void)
+{
+ regmap_debugfs_initcall();
+
+ return 0;
+}
+postcore_initcall(regmap_initcall);
diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
index 84997efdb23d..f6c453c3816e 100644
--- a/drivers/base/transport_class.c
+++ b/drivers/base/transport_class.c
@@ -27,6 +27,7 @@
* transport class is framed entirely in terms of generic devices to
* allow it to be used by any physical HBA in the system.
*/
+#include <linux/export.h>
#include <linux/attribute_container.h>
#include <linux/transport_class.h>
diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
index 353781b5b78b..c1172dafdffa 100644
--- a/drivers/bcma/Kconfig
+++ b/drivers/bcma/Kconfig
@@ -13,6 +13,11 @@ config BCMA
Bus driver for Broadcom specific Advanced Microcontroller Bus
Architecture.
+# Support for Block-I/O. SELECT this from the driver that needs it.
+config BCMA_BLOCKIO
+ bool
+ depends on BCMA
+
config BCMA_HOST_PCI_POSSIBLE
bool
depends on BCMA && PCI = y
@@ -22,6 +27,25 @@ config BCMA_HOST_PCI
bool "Support for BCMA on PCI-host bus"
depends on BCMA_HOST_PCI_POSSIBLE
+config BCMA_DRIVER_PCI_HOSTMODE
+ bool "Driver for PCI core working in hostmode"
+ depends on BCMA && MIPS
+ help
+ PCI core hostmode operation (external PCI bus).
+
+config BCMA_HOST_SOC
+ bool
+ depends on BCMA_DRIVER_MIPS
+
+config BCMA_DRIVER_MIPS
+ bool "BCMA Broadcom MIPS core driver"
+ depends on BCMA && MIPS
+ help
+ Driver for the Broadcom MIPS core attached to Broadcom specific
+ Advanced Microcontroller Bus.
+
+ If unsure, say N
+
config BCMA_DEBUG
bool "BCMA debugging"
depends on BCMA
diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile
index 0d56245bcb79..82de24e5340c 100644
--- a/drivers/bcma/Makefile
+++ b/drivers/bcma/Makefile
@@ -1,7 +1,10 @@
-bcma-y += main.o scan.o core.o
+bcma-y += main.o scan.o core.o sprom.o
bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o
bcma-y += driver_pci.o
+bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o
+bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o
bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o
+bcma-$(CONFIG_BCMA_HOST_SOC) += host_soc.o
obj-$(CONFIG_BCMA) += bcma.o
ccflags-$(CONFIG_BCMA_DEBUG) := -DDEBUG
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 2f72e9c585fd..30a3085d3354 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -13,11 +13,30 @@
struct bcma_bus;
/* main.c */
-extern int bcma_bus_register(struct bcma_bus *bus);
-extern void bcma_bus_unregister(struct bcma_bus *bus);
+int bcma_bus_register(struct bcma_bus *bus);
+void bcma_bus_unregister(struct bcma_bus *bus);
+int __init bcma_bus_early_register(struct bcma_bus *bus,
+ struct bcma_device *core_cc,
+ struct bcma_device *core_mips);
/* scan.c */
int bcma_bus_scan(struct bcma_bus *bus);
+int __init bcma_bus_scan_early(struct bcma_bus *bus,
+ struct bcma_device_id *match,
+ struct bcma_device *core);
+void bcma_init_bus(struct bcma_bus *bus);
+
+/* sprom.c */
+int bcma_sprom_get(struct bcma_bus *bus);
+
+/* driver_chipcommon.c */
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
+
+/* driver_chipcommon_pmu.c */
+u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc);
+u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc);
#ifdef CONFIG_BCMA_HOST_PCI
/* host_pci.c */
@@ -25,4 +44,8 @@ extern int __init bcma_host_pci_init(void);
extern void __exit bcma_host_pci_exit(void);
#endif /* CONFIG_BCMA_HOST_PCI */
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
+#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
+
#endif
diff --git a/drivers/bcma/core.c b/drivers/bcma/core.c
index ced379f7b371..893f6e0c759f 100644
--- a/drivers/bcma/core.c
+++ b/drivers/bcma/core.c
@@ -6,6 +6,7 @@
*/
#include "bcma_private.h"
+#include <linux/export.h>
#include <linux/bcma/bcma.h>
bool bcma_core_is_enabled(struct bcma_device *core)
@@ -19,7 +20,7 @@ bool bcma_core_is_enabled(struct bcma_device *core)
}
EXPORT_SYMBOL_GPL(bcma_core_is_enabled);
-static void bcma_core_disable(struct bcma_device *core, u32 flags)
+void bcma_core_disable(struct bcma_device *core, u32 flags)
{
if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET)
return;
@@ -31,6 +32,7 @@ static void bcma_core_disable(struct bcma_device *core, u32 flags)
bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
udelay(1);
}
+EXPORT_SYMBOL_GPL(bcma_core_disable);
int bcma_core_enable(struct bcma_device *core, u32 flags)
{
@@ -49,3 +51,77 @@ int bcma_core_enable(struct bcma_device *core, u32 flags)
return 0;
}
EXPORT_SYMBOL_GPL(bcma_core_enable);
+
+void bcma_core_set_clockmode(struct bcma_device *core,
+ enum bcma_clkmode clkmode)
+{
+ u16 i;
+
+ WARN_ON(core->id.id != BCMA_CORE_CHIPCOMMON &&
+ core->id.id != BCMA_CORE_PCIE &&
+ core->id.id != BCMA_CORE_80211);
+
+ switch (clkmode) {
+ case BCMA_CLKMODE_FAST:
+ bcma_set32(core, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT);
+ udelay(64);
+ for (i = 0; i < 1500; i++) {
+ if (bcma_read32(core, BCMA_CLKCTLST) &
+ BCMA_CLKCTLST_HAVEHT) {
+ i = 0;
+ break;
+ }
+ udelay(10);
+ }
+ if (i)
+ pr_err("HT force timeout\n");
+ break;
+ case BCMA_CLKMODE_DYNAMIC:
+ pr_warn("Dynamic clockmode not supported yet!\n");
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(bcma_core_set_clockmode);
+
+void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on)
+{
+ u16 i;
+
+ WARN_ON(req & ~BCMA_CLKCTLST_EXTRESREQ);
+ WARN_ON(status & ~BCMA_CLKCTLST_EXTRESST);
+
+ if (on) {
+ bcma_set32(core, BCMA_CLKCTLST, req);
+ for (i = 0; i < 10000; i++) {
+ if ((bcma_read32(core, BCMA_CLKCTLST) & status) ==
+ status) {
+ i = 0;
+ break;
+ }
+ udelay(10);
+ }
+ if (i)
+ pr_err("PLL enable timeout\n");
+ } else {
+ pr_warn("Disabling PLL not supported yet!\n");
+ }
+}
+EXPORT_SYMBOL_GPL(bcma_core_pll_ctl);
+
+u32 bcma_core_dma_translation(struct bcma_device *core)
+{
+ switch (core->bus->hosttype) {
+ case BCMA_HOSTTYPE_SOC:
+ return 0;
+ case BCMA_HOSTTYPE_PCI:
+ if (bcma_aread32(core, BCMA_IOST) & BCMA_IOST_DMA64)
+ return BCMA_DMA_TRANSLATION_DMA64_CMT;
+ else
+ return BCMA_DMA_TRANSLATION_DMA32_CMT;
+ default:
+ pr_err("DMA translation unknown for host %d\n",
+ core->bus->hosttype);
+ }
+ return BCMA_DMA_TRANSLATION_NONE;
+}
+EXPORT_SYMBOL(bcma_core_dma_translation);
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index 606102256b44..e9f1b3fd252c 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -3,12 +3,13 @@
* ChipCommon core driver
*
* Copyright 2005, Broadcom Corporation
- * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include "bcma_private.h"
+#include <linux/export.h>
#include <linux/bcma/bcma.h>
static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
@@ -23,6 +24,12 @@ static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
{
+ u32 leddc_on = 10;
+ u32 leddc_off = 90;
+
+ if (cc->setup_done)
+ return;
+
if (cc->core->id.rev >= 11)
cc->status = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
cc->capabilities = bcma_cc_read32(cc, BCMA_CC_CAP);
@@ -38,6 +45,19 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
bcma_pmu_init(cc);
if (cc->capabilities & BCMA_CC_CAP_PCTL)
pr_err("Power control not implemented!\n");
+
+ if (cc->core->id.rev >= 16) {
+ if (cc->core->bus->sprom.leddc_on_time &&
+ cc->core->bus->sprom.leddc_off_time) {
+ leddc_on = cc->core->bus->sprom.leddc_on_time;
+ leddc_off = cc->core->bus->sprom.leddc_off_time;
+ }
+ bcma_cc_write32(cc, BCMA_CC_GPIOTIMER,
+ ((leddc_on << BCMA_CC_GPIOTIMER_ONTIME_SHIFT) |
+ (leddc_off << BCMA_CC_GPIOTIMER_OFFTIME_SHIFT)));
+ }
+
+ cc->setup_done = true;
}
/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
@@ -87,3 +107,51 @@ u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
return bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value);
}
+
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
+{
+ unsigned int irq;
+ u32 baud_base;
+ u32 i;
+ unsigned int ccrev = cc->core->id.rev;
+ struct bcma_serial_port *ports = cc->serial_ports;
+
+ if (ccrev >= 11 && ccrev != 15) {
+ /* Fixed ALP clock */
+ baud_base = bcma_pmu_alp_clock(cc);
+ if (ccrev >= 21) {
+ /* Turn off UART clock before switching clocksource. */
+ bcma_cc_write32(cc, BCMA_CC_CORECTL,
+ bcma_cc_read32(cc, BCMA_CC_CORECTL)
+ & ~BCMA_CC_CORECTL_UARTCLKEN);
+ }
+ /* Set the override bit so we don't divide it */
+ bcma_cc_write32(cc, BCMA_CC_CORECTL,
+ bcma_cc_read32(cc, BCMA_CC_CORECTL)
+ | BCMA_CC_CORECTL_UARTCLK0);
+ if (ccrev >= 21) {
+ /* Re-enable the UART clock. */
+ bcma_cc_write32(cc, BCMA_CC_CORECTL,
+ bcma_cc_read32(cc, BCMA_CC_CORECTL)
+ | BCMA_CC_CORECTL_UARTCLKEN);
+ }
+ } else {
+ pr_err("serial not supported on this device ccrev: 0x%x\n",
+ ccrev);
+ return;
+ }
+
+ irq = bcma_core_mips_irq(cc->core);
+
+ /* Determine the registers of the UARTs */
+ cc->nr_serial_ports = (cc->capabilities & BCMA_CC_CAP_NRUART);
+ for (i = 0; i < cc->nr_serial_ports; i++) {
+ ports[i].regs = cc->core->io_addr + BCMA_CC_UART0_DATA +
+ (i * 256);
+ ports[i].irq = irq;
+ ports[i].baud_base = baud_base;
+ ports[i].reg_shift = 0;
+ }
+}
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
index f44177a644c7..800163c8c2e7 100644
--- a/drivers/bcma/driver_chipcommon_pmu.c
+++ b/drivers/bcma/driver_chipcommon_pmu.c
@@ -2,29 +2,57 @@
* Broadcom specific AMBA
* ChipCommon Power Management Unit driver
*
- * Copyright 2009, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2009, Michael Buesch <m@bues.ch>
* Copyright 2007, Broadcom Corporation
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include "bcma_private.h"
+#include <linux/export.h>
#include <linux/bcma/bcma.h>
-static void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc,
- u32 offset, u32 mask, u32 set)
+static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset)
{
- u32 value;
+ bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+ bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+ return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA);
+}
- bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR);
+void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value)
+{
+ bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+ bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+ bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_pll_write);
+
+void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
+ u32 set)
+{
+ bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+ bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+ bcma_cc_maskset32(cc, BCMA_CC_PLLCTL_DATA, mask, set);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_pll_maskset);
+
+void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc,
+ u32 offset, u32 mask, u32 set)
+{
bcma_cc_write32(cc, BCMA_CC_CHIPCTL_ADDR, offset);
bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR);
- value = bcma_cc_read32(cc, BCMA_CC_CHIPCTL_DATA);
- value &= mask;
- value |= set;
- bcma_cc_write32(cc, BCMA_CC_CHIPCTL_DATA, value);
- bcma_cc_read32(cc, BCMA_CC_CHIPCTL_DATA);
+ bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL_DATA, mask, set);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_chipctl_maskset);
+
+void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
+ u32 set)
+{
+ bcma_cc_write32(cc, BCMA_CC_REGCTL_ADDR, offset);
+ bcma_cc_read32(cc, BCMA_CC_REGCTL_ADDR);
+ bcma_cc_maskset32(cc, BCMA_CC_REGCTL_DATA, mask, set);
}
+EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset);
static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
{
@@ -53,6 +81,7 @@ static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
max_msk = 0xFFFF;
break;
case 43224:
+ case 43225:
break;
default:
pr_err("PMU resource config unknown for device 0x%04X\n",
@@ -74,6 +103,7 @@ void bcma_pmu_swreg_init(struct bcma_drv_cc *cc)
case 0x4313:
case 0x4331:
case 43224:
+ case 43225:
break;
default:
pr_err("PMU switch/regulators init unknown for device "
@@ -81,6 +111,24 @@ void bcma_pmu_swreg_init(struct bcma_drv_cc *cc)
}
}
+/* Disable to allow reading SPROM. Don't know the adventages of enabling it. */
+void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable)
+{
+ struct bcma_bus *bus = cc->core->bus;
+ u32 val;
+
+ val = bcma_cc_read32(cc, BCMA_CC_CHIPCTL);
+ if (enable) {
+ val |= BCMA_CHIPCTL_4331_EXTPA_EN;
+ if (bus->chipinfo.pkg == 9 || bus->chipinfo.pkg == 11)
+ val |= BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5;
+ } else {
+ val &= ~BCMA_CHIPCTL_4331_EXTPA_EN;
+ val &= ~BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5;
+ }
+ bcma_cc_write32(cc, BCMA_CC_CHIPCTL, val);
+}
+
void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
{
struct bcma_bus *bus = cc->core->bus;
@@ -90,17 +138,19 @@ void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x7);
break;
case 0x4331:
- pr_err("Enabling Ext PA lines not implemented\n");
+ /* BCM4331 workaround is SPROM-related, we put it in sprom.c */
break;
case 43224:
if (bus->chipinfo.rev == 0) {
pr_err("Workarounds for 43224 rev 0 not fully "
"implemented\n");
- bcma_chipco_chipctl_maskset(cc, 0, ~0, 0xF0);
+ bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x00F000F0);
} else {
bcma_chipco_chipctl_maskset(cc, 0, ~0, 0xF0);
}
break;
+ case 43225:
+ break;
default:
pr_err("Workarounds unknown for device 0x%04X\n",
bus->chipinfo.id);
@@ -132,3 +182,129 @@ void bcma_pmu_init(struct bcma_drv_cc *cc)
bcma_pmu_swreg_init(cc);
bcma_pmu_workarounds(cc);
}
+
+u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ switch (bus->chipinfo.id) {
+ case 0x4716:
+ case 0x4748:
+ case 47162:
+ case 0x4313:
+ case 0x5357:
+ case 0x4749:
+ case 53572:
+ /* always 20Mhz */
+ return 20000 * 1000;
+ case 0x5356:
+ case 0x5300:
+ /* always 25Mhz */
+ return 25000 * 1000;
+ default:
+ pr_warn("No ALP clock specified for %04X device, "
+ "pmu rev. %d, using default %d Hz\n",
+ bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_ALP_CLOCK);
+ }
+ return BCMA_CC_PMU_ALP_CLOCK;
+}
+
+/* Find the output of the "m" pll divider given pll controls that start with
+ * pllreg "pll0" i.e. 12 for main 6 for phy, 0 for misc.
+ */
+static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m)
+{
+ u32 tmp, div, ndiv, p1, p2, fc;
+ struct bcma_bus *bus = cc->core->bus;
+
+ BUG_ON((pll0 & 3) || (pll0 > BCMA_CC_PMU4716_MAINPLL_PLL0));
+
+ BUG_ON(!m || m > 4);
+
+ if (bus->chipinfo.id == 0x5357 || bus->chipinfo.id == 0x4749) {
+ /* Detect failure in clock setting */
+ tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
+ if (tmp & 0x40000)
+ return 133 * 1000000;
+ }
+
+ tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_P1P2_OFF);
+ p1 = (tmp & BCMA_CC_PPL_P1_MASK) >> BCMA_CC_PPL_P1_SHIFT;
+ p2 = (tmp & BCMA_CC_PPL_P2_MASK) >> BCMA_CC_PPL_P2_SHIFT;
+
+ tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_M14_OFF);
+ div = (tmp >> ((m - 1) * BCMA_CC_PPL_MDIV_WIDTH)) &
+ BCMA_CC_PPL_MDIV_MASK;
+
+ tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_NM5_OFF);
+ ndiv = (tmp & BCMA_CC_PPL_NDIV_MASK) >> BCMA_CC_PPL_NDIV_SHIFT;
+
+ /* Do calculation in Mhz */
+ fc = bcma_pmu_alp_clock(cc) / 1000000;
+ fc = (p1 * ndiv * fc) / p2;
+
+ /* Return clock in Hertz */
+ return (fc / div) * 1000000;
+}
+
+/* query bus clock frequency for PMU-enabled chipcommon */
+u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ switch (bus->chipinfo.id) {
+ case 0x4716:
+ case 0x4748:
+ case 47162:
+ return bcma_pmu_clock(cc, BCMA_CC_PMU4716_MAINPLL_PLL0,
+ BCMA_CC_PMU5_MAINPLL_SSB);
+ case 0x5356:
+ return bcma_pmu_clock(cc, BCMA_CC_PMU5356_MAINPLL_PLL0,
+ BCMA_CC_PMU5_MAINPLL_SSB);
+ case 0x5357:
+ case 0x4749:
+ return bcma_pmu_clock(cc, BCMA_CC_PMU5357_MAINPLL_PLL0,
+ BCMA_CC_PMU5_MAINPLL_SSB);
+ case 0x5300:
+ return bcma_pmu_clock(cc, BCMA_CC_PMU4706_MAINPLL_PLL0,
+ BCMA_CC_PMU5_MAINPLL_SSB);
+ case 53572:
+ return 75000000;
+ default:
+ pr_warn("No backplane clock specified for %04X device, "
+ "pmu rev. %d, using default %d Hz\n",
+ bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_HT_CLOCK);
+ }
+ return BCMA_CC_PMU_HT_CLOCK;
+}
+
+/* query cpu clock frequency for PMU-enabled chipcommon */
+u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ if (bus->chipinfo.id == 53572)
+ return 300000000;
+
+ if (cc->pmu.rev >= 5) {
+ u32 pll;
+ switch (bus->chipinfo.id) {
+ case 0x5356:
+ pll = BCMA_CC_PMU5356_MAINPLL_PLL0;
+ break;
+ case 0x5357:
+ case 0x4749:
+ pll = BCMA_CC_PMU5357_MAINPLL_PLL0;
+ break;
+ default:
+ pll = BCMA_CC_PMU4716_MAINPLL_PLL0;
+ break;
+ }
+
+ /* TODO: if (bus->chipinfo.id == 0x5300)
+ return si_4706_pmu_clock(sih, osh, cc, PMU4706_MAINPLL_PLL0, PMU5_MAINPLL_CPU); */
+ return bcma_pmu_clock(cc, pll, BCMA_CC_PMU5_MAINPLL_CPU);
+ }
+
+ return bcma_pmu_get_clockcontrol(cc);
+}
diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c
new file mode 100644
index 000000000000..c3e9dff4224e
--- /dev/null
+++ b/drivers/bcma/driver_mips.c
@@ -0,0 +1,256 @@
+/*
+ * Broadcom specific AMBA
+ * Broadcom MIPS32 74K core driver
+ *
+ * Copyright 2009, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2010, Bernhard Loos <bernhardloos@googlemail.com>
+ * Copyright 2011, Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+
+/* The 47162a0 hangs when reading MIPS DMP registers registers */
+static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
+{
+ return dev->bus->chipinfo.id == 47162 && dev->bus->chipinfo.rev == 0 &&
+ dev->id.id == BCMA_CORE_MIPS_74K;
+}
+
+/* The 5357b0 hangs when reading USB20H DMP registers */
+static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev)
+{
+ return (dev->bus->chipinfo.id == 0x5357 ||
+ dev->bus->chipinfo.id == 0x4749) &&
+ dev->bus->chipinfo.pkg == 11 &&
+ dev->id.id == BCMA_CORE_USB20_HOST;
+}
+
+static inline u32 mips_read32(struct bcma_drv_mips *mcore,
+ u16 offset)
+{
+ return bcma_read32(mcore->core, offset);
+}
+
+static inline void mips_write32(struct bcma_drv_mips *mcore,
+ u16 offset,
+ u32 value)
+{
+ bcma_write32(mcore->core, offset, value);
+}
+
+static const u32 ipsflag_irq_mask[] = {
+ 0,
+ BCMA_MIPS_IPSFLAG_IRQ1,
+ BCMA_MIPS_IPSFLAG_IRQ2,
+ BCMA_MIPS_IPSFLAG_IRQ3,
+ BCMA_MIPS_IPSFLAG_IRQ4,
+};
+
+static const u32 ipsflag_irq_shift[] = {
+ 0,
+ BCMA_MIPS_IPSFLAG_IRQ1_SHIFT,
+ BCMA_MIPS_IPSFLAG_IRQ2_SHIFT,
+ BCMA_MIPS_IPSFLAG_IRQ3_SHIFT,
+ BCMA_MIPS_IPSFLAG_IRQ4_SHIFT,
+};
+
+static u32 bcma_core_mips_irqflag(struct bcma_device *dev)
+{
+ u32 flag;
+
+ if (bcma_core_mips_bcm47162a0_quirk(dev))
+ return dev->core_index;
+ if (bcma_core_mips_bcm5357b0_quirk(dev))
+ return dev->core_index;
+ flag = bcma_aread32(dev, BCMA_MIPS_OOBSELOUTA30);
+
+ return flag & 0x1F;
+}
+
+/* Get the MIPS IRQ assignment for a specified device.
+ * If unassigned, 0 is returned.
+ */
+unsigned int bcma_core_mips_irq(struct bcma_device *dev)
+{
+ struct bcma_device *mdev = dev->bus->drv_mips.core;
+ u32 irqflag;
+ unsigned int irq;
+
+ irqflag = bcma_core_mips_irqflag(dev);
+
+ for (irq = 1; irq <= 4; irq++)
+ if (bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)) &
+ (1 << irqflag))
+ return irq;
+
+ return 0;
+}
+EXPORT_SYMBOL(bcma_core_mips_irq);
+
+static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
+{
+ unsigned int oldirq = bcma_core_mips_irq(dev);
+ struct bcma_bus *bus = dev->bus;
+ struct bcma_device *mdev = bus->drv_mips.core;
+ u32 irqflag;
+
+ irqflag = bcma_core_mips_irqflag(dev);
+ BUG_ON(oldirq == 6);
+
+ dev->irq = irq + 2;
+
+ /* clear the old irq */
+ if (oldirq == 0)
+ bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
+ bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) &
+ ~(1 << irqflag));
+ else
+ bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq), 0);
+
+ /* assign the new one */
+ if (irq == 0) {
+ bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
+ bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) |
+ (1 << irqflag));
+ } else {
+ u32 oldirqflag = bcma_read32(mdev,
+ BCMA_MIPS_MIPS74K_INTMASK(irq));
+ if (oldirqflag) {
+ struct bcma_device *core;
+
+ /* backplane irq line is in use, find out who uses
+ * it and set user to irq 0
+ */
+ list_for_each_entry_reverse(core, &bus->cores, list) {
+ if ((1 << bcma_core_mips_irqflag(core)) ==
+ oldirqflag) {
+ bcma_core_mips_set_irq(core, 0);
+ break;
+ }
+ }
+ }
+ bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq),
+ 1 << irqflag);
+ }
+
+ pr_info("set_irq: core 0x%04x, irq %d => %d\n",
+ dev->id.id, oldirq + 2, irq + 2);
+}
+
+static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq)
+{
+ int i;
+ static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
+ printk(KERN_INFO KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id);
+ for (i = 0; i <= 6; i++)
+ printk(" %s%s", irq_name[i], i == irq ? "*" : " ");
+ printk("\n");
+}
+
+static void bcma_core_mips_dump_irq(struct bcma_bus *bus)
+{
+ struct bcma_device *core;
+
+ list_for_each_entry_reverse(core, &bus->cores, list) {
+ bcma_core_mips_print_irq(core, bcma_core_mips_irq(core));
+ }
+}
+
+u32 bcma_cpu_clock(struct bcma_drv_mips *mcore)
+{
+ struct bcma_bus *bus = mcore->core->bus;
+
+ if (bus->drv_cc.capabilities & BCMA_CC_CAP_PMU)
+ return bcma_pmu_get_clockcpu(&bus->drv_cc);
+
+ pr_err("No PMU available, need this to get the cpu clock\n");
+ return 0;
+}
+EXPORT_SYMBOL(bcma_cpu_clock);
+
+static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
+{
+ struct bcma_bus *bus = mcore->core->bus;
+
+ switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) {
+ case BCMA_CC_FLASHT_STSER:
+ case BCMA_CC_FLASHT_ATSER:
+ pr_err("Serial flash not supported.\n");
+ break;
+ case BCMA_CC_FLASHT_PARA:
+ pr_info("found parallel flash.\n");
+ bus->drv_cc.pflash.window = 0x1c000000;
+ bus->drv_cc.pflash.window_size = 0x02000000;
+
+ if ((bcma_read32(bus->drv_cc.core, BCMA_CC_FLASH_CFG) &
+ BCMA_CC_FLASH_CFG_DS) == 0)
+ bus->drv_cc.pflash.buswidth = 1;
+ else
+ bus->drv_cc.pflash.buswidth = 2;
+ break;
+ default:
+ pr_err("flash not supported.\n");
+ }
+}
+
+void bcma_core_mips_init(struct bcma_drv_mips *mcore)
+{
+ struct bcma_bus *bus;
+ struct bcma_device *core;
+ bus = mcore->core->bus;
+
+ pr_info("Initializing MIPS core...\n");
+
+ if (!mcore->setup_done)
+ mcore->assigned_irqs = 1;
+
+ /* Assign IRQs to all cores on the bus */
+ list_for_each_entry_reverse(core, &bus->cores, list) {
+ int mips_irq;
+ if (core->irq)
+ continue;
+
+ mips_irq = bcma_core_mips_irq(core);
+ if (mips_irq > 4)
+ core->irq = 0;
+ else
+ core->irq = mips_irq + 2;
+ if (core->irq > 5)
+ continue;
+ switch (core->id.id) {
+ case BCMA_CORE_PCI:
+ case BCMA_CORE_PCIE:
+ case BCMA_CORE_ETHERNET:
+ case BCMA_CORE_ETHERNET_GBIT:
+ case BCMA_CORE_MAC_GBIT:
+ case BCMA_CORE_80211:
+ case BCMA_CORE_USB20_HOST:
+ /* These devices get their own IRQ line if available,
+ * the rest goes on IRQ0
+ */
+ if (mcore->assigned_irqs <= 4)
+ bcma_core_mips_set_irq(core,
+ mcore->assigned_irqs++);
+ break;
+ }
+ }
+ pr_info("IRQ reconfiguration done\n");
+ bcma_core_mips_dump_irq(bus);
+
+ if (mcore->setup_done)
+ return;
+
+ bcma_chipco_serial_init(&bus->drv_cc);
+ bcma_core_mips_flash_detect(mcore);
+ mcore->setup_done = true;
+}
diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c
index e757e4e3c7e2..4fde6254f04e 100644
--- a/drivers/bcma/driver_pci.c
+++ b/drivers/bcma/driver_pci.c
@@ -3,12 +3,13 @@
* PCI Core
*
* Copyright 2005, Broadcom Corporation
- * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include "bcma_private.h"
+#include <linux/export.h>
#include <linux/bcma/bcma.h>
/**************************************************
@@ -157,7 +158,81 @@ static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
* Init.
**************************************************/
-void bcma_core_pci_init(struct bcma_drv_pci *pc)
+static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
{
bcma_pcicore_serdes_workaround(pc);
}
+
+static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
+{
+ struct bcma_bus *bus = pc->core->bus;
+ u16 chipid_top;
+
+ chipid_top = (bus->chipinfo.id & 0xFF00);
+ if (chipid_top != 0x4700 &&
+ chipid_top != 0x5300)
+ return false;
+
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+ if (bus->sprom.boardflags_lo & SSB_BFL_NOPCI)
+ return false;
+#endif /* CONFIG_SSB_DRIVER_PCICORE */
+
+#if 0
+ /* TODO: on BCMA we use address from EROM instead of magic formula */
+ u32 tmp;
+ return !mips_busprobe32(tmp, (bus->mmio +
+ (pc->core->core_index * BCMA_CORE_SIZE)));
+#endif
+
+ return true;
+}
+
+void bcma_core_pci_init(struct bcma_drv_pci *pc)
+{
+ if (pc->setup_done)
+ return;
+
+ if (bcma_core_pci_is_in_hostmode(pc)) {
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+ bcma_core_pci_hostmode_init(pc);
+#else
+ pr_err("Driver compiled without support for hostmode PCI\n");
+#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
+ } else {
+ bcma_core_pci_clientmode_init(pc);
+ }
+
+ pc->setup_done = true;
+}
+
+int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
+ bool enable)
+{
+ struct pci_dev *pdev = pc->core->bus->host_pci;
+ u32 coremask, tmp;
+ int err = 0;
+
+ if (core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
+ /* This bcma device is not on a PCI host-bus. So the IRQs are
+ * not routed through the PCI core.
+ * So we must not enable routing through the PCI core. */
+ goto out;
+ }
+
+ err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
+ if (err)
+ goto out;
+
+ coremask = BIT(core->core_index) << 8;
+ if (enable)
+ tmp |= coremask;
+ else
+ tmp &= ~coremask;
+
+ err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
+
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c
new file mode 100644
index 000000000000..eb332b75ce83
--- /dev/null
+++ b/drivers/bcma/driver_pci_host.c
@@ -0,0 +1,14 @@
+/*
+ * Broadcom specific AMBA
+ * PCI Core in hostmode
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/bcma/bcma.h>
+
+void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
+{
+ pr_err("No support for PCI core in hostmode yet\n");
+}
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 471a04013fe0..1b51d8b7ac80 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -9,6 +9,7 @@
#include <linux/slab.h>
#include <linux/bcma/bcma.h>
#include <linux/pci.h>
+#include <linux/module.h>
static void bcma_host_pci_switch_core(struct bcma_device *core)
{
@@ -65,6 +66,54 @@ static void bcma_host_pci_write32(struct bcma_device *core, u16 offset,
iowrite32(value, core->bus->mmio + offset);
}
+#ifdef CONFIG_BCMA_BLOCKIO
+void bcma_host_pci_block_read(struct bcma_device *core, void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ void __iomem *addr = core->bus->mmio + offset;
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ switch (reg_width) {
+ case sizeof(u8):
+ ioread8_rep(addr, buffer, count);
+ break;
+ case sizeof(u16):
+ WARN_ON(count & 1);
+ ioread16_rep(addr, buffer, count >> 1);
+ break;
+ case sizeof(u32):
+ WARN_ON(count & 3);
+ ioread32_rep(addr, buffer, count >> 2);
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+void bcma_host_pci_block_write(struct bcma_device *core, const void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ void __iomem *addr = core->bus->mmio + offset;
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ switch (reg_width) {
+ case sizeof(u8):
+ iowrite8_rep(addr, buffer, count);
+ break;
+ case sizeof(u16):
+ WARN_ON(count & 1);
+ iowrite16_rep(addr, buffer, count >> 1);
+ break;
+ case sizeof(u32):
+ WARN_ON(count & 3);
+ iowrite32_rep(addr, buffer, count >> 2);
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+#endif
+
static u32 bcma_host_pci_aread32(struct bcma_device *core, u16 offset)
{
if (core->bus->mapped_core != core)
@@ -87,6 +136,10 @@ const struct bcma_host_ops bcma_host_pci_ops = {
.write8 = bcma_host_pci_write8,
.write16 = bcma_host_pci_write16,
.write32 = bcma_host_pci_write32,
+#ifdef CONFIG_BCMA_BLOCKIO
+ .block_read = bcma_host_pci_block_read,
+ .block_write = bcma_host_pci_block_write,
+#endif
.aread32 = bcma_host_pci_aread32,
.awrite32 = bcma_host_pci_awrite32,
};
@@ -175,6 +228,7 @@ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
{ 0, },
};
diff --git a/drivers/bcma/host_soc.c b/drivers/bcma/host_soc.c
new file mode 100644
index 000000000000..3c381fb8f9c4
--- /dev/null
+++ b/drivers/bcma/host_soc.c
@@ -0,0 +1,183 @@
+/*
+ * Broadcom specific AMBA
+ * System on Chip (SoC) Host
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include "scan.h"
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_soc.h>
+
+static u8 bcma_host_soc_read8(struct bcma_device *core, u16 offset)
+{
+ return readb(core->io_addr + offset);
+}
+
+static u16 bcma_host_soc_read16(struct bcma_device *core, u16 offset)
+{
+ return readw(core->io_addr + offset);
+}
+
+static u32 bcma_host_soc_read32(struct bcma_device *core, u16 offset)
+{
+ return readl(core->io_addr + offset);
+}
+
+static void bcma_host_soc_write8(struct bcma_device *core, u16 offset,
+ u8 value)
+{
+ writeb(value, core->io_addr + offset);
+}
+
+static void bcma_host_soc_write16(struct bcma_device *core, u16 offset,
+ u16 value)
+{
+ writew(value, core->io_addr + offset);
+}
+
+static void bcma_host_soc_write32(struct bcma_device *core, u16 offset,
+ u32 value)
+{
+ writel(value, core->io_addr + offset);
+}
+
+#ifdef CONFIG_BCMA_BLOCKIO
+static void bcma_host_soc_block_read(struct bcma_device *core, void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ void __iomem *addr = core->io_addr + offset;
+
+ switch (reg_width) {
+ case sizeof(u8): {
+ u8 *buf = buffer;
+
+ while (count) {
+ *buf = __raw_readb(addr);
+ buf++;
+ count--;
+ }
+ break;
+ }
+ case sizeof(u16): {
+ __le16 *buf = buffer;
+
+ WARN_ON(count & 1);
+ while (count) {
+ *buf = (__force __le16)__raw_readw(addr);
+ buf++;
+ count -= 2;
+ }
+ break;
+ }
+ case sizeof(u32): {
+ __le32 *buf = buffer;
+
+ WARN_ON(count & 3);
+ while (count) {
+ *buf = (__force __le32)__raw_readl(addr);
+ buf++;
+ count -= 4;
+ }
+ break;
+ }
+ default:
+ WARN_ON(1);
+ }
+}
+
+static void bcma_host_soc_block_write(struct bcma_device *core,
+ const void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ void __iomem *addr = core->io_addr + offset;
+
+ switch (reg_width) {
+ case sizeof(u8): {
+ const u8 *buf = buffer;
+
+ while (count) {
+ __raw_writeb(*buf, addr);
+ buf++;
+ count--;
+ }
+ break;
+ }
+ case sizeof(u16): {
+ const __le16 *buf = buffer;
+
+ WARN_ON(count & 1);
+ while (count) {
+ __raw_writew((__force u16)(*buf), addr);
+ buf++;
+ count -= 2;
+ }
+ break;
+ }
+ case sizeof(u32): {
+ const __le32 *buf = buffer;
+
+ WARN_ON(count & 3);
+ while (count) {
+ __raw_writel((__force u32)(*buf), addr);
+ buf++;
+ count -= 4;
+ }
+ break;
+ }
+ default:
+ WARN_ON(1);
+ }
+}
+#endif /* CONFIG_BCMA_BLOCKIO */
+
+static u32 bcma_host_soc_aread32(struct bcma_device *core, u16 offset)
+{
+ return readl(core->io_wrap + offset);
+}
+
+static void bcma_host_soc_awrite32(struct bcma_device *core, u16 offset,
+ u32 value)
+{
+ writel(value, core->io_wrap + offset);
+}
+
+const struct bcma_host_ops bcma_host_soc_ops = {
+ .read8 = bcma_host_soc_read8,
+ .read16 = bcma_host_soc_read16,
+ .read32 = bcma_host_soc_read32,
+ .write8 = bcma_host_soc_write8,
+ .write16 = bcma_host_soc_write16,
+ .write32 = bcma_host_soc_write32,
+#ifdef CONFIG_BCMA_BLOCKIO
+ .block_read = bcma_host_soc_block_read,
+ .block_write = bcma_host_soc_block_write,
+#endif
+ .aread32 = bcma_host_soc_aread32,
+ .awrite32 = bcma_host_soc_awrite32,
+};
+
+int __init bcma_host_soc_register(struct bcma_soc *soc)
+{
+ struct bcma_bus *bus = &soc->bus;
+ int err;
+
+ /* iomap only first core. We have to read some register on this core
+ * to scan the bus.
+ */
+ bus->mmio = ioremap_nocache(BCMA_ADDR_BASE, BCMA_CORE_SIZE * 1);
+ if (!bus->mmio)
+ return -ENOMEM;
+
+ /* Host specific */
+ bus->hosttype = BCMA_HOSTTYPE_SOC;
+ bus->ops = &bcma_host_soc_ops;
+
+ /* Register */
+ err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips);
+ if (err)
+ iounmap(bus->mmio);
+
+ return err;
+}
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index be52344ed19d..70c84b951098 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -6,7 +6,9 @@
*/
#include "bcma_private.h"
+#include <linux/module.h>
#include <linux/bcma/bcma.h>
+#include <linux/slab.h>
MODULE_DESCRIPTION("Broadcom's specific AMBA driver");
MODULE_LICENSE("GPL");
@@ -14,6 +16,7 @@ MODULE_LICENSE("GPL");
static int bcma_bus_match(struct device *dev, struct device_driver *drv);
static int bcma_device_probe(struct device *dev);
static int bcma_device_remove(struct device *dev);
+static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env);
static ssize_t manuf_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -48,6 +51,7 @@ static struct bus_type bcma_bus_type = {
.match = bcma_bus_match,
.probe = bcma_device_probe,
.remove = bcma_device_remove,
+ .uevent = bcma_device_uevent,
.dev_attrs = bcma_device_attrs,
};
@@ -65,6 +69,10 @@ static struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid)
static void bcma_release_core_dev(struct device *dev)
{
struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+ if (core->io_addr)
+ iounmap(core->io_addr);
+ if (core->io_wrap)
+ iounmap(core->io_wrap);
kfree(core);
}
@@ -79,6 +87,7 @@ static int bcma_register_cores(struct bcma_bus *bus)
case BCMA_CORE_CHIPCOMMON:
case BCMA_CORE_PCI:
case BCMA_CORE_PCIE:
+ case BCMA_CORE_MIPS_74K:
continue;
}
@@ -89,8 +98,13 @@ static int bcma_register_cores(struct bcma_bus *bus)
switch (bus->hosttype) {
case BCMA_HOSTTYPE_PCI:
core->dev.parent = &bus->host_pci->dev;
+ core->dma_dev = &bus->host_pci->dev;
+ core->irq = bus->host_pci->irq;
+ break;
+ case BCMA_HOSTTYPE_SOC:
+ core->dev.dma_mask = &core->dev.coherent_dma_mask;
+ core->dma_dev = &core->dev;
break;
- case BCMA_HOSTTYPE_NONE:
case BCMA_HOSTTYPE_SDIO:
break;
}
@@ -137,6 +151,13 @@ int bcma_bus_register(struct bcma_bus *bus)
bcma_core_chipcommon_init(&bus->drv_cc);
}
+ /* Init MIPS core */
+ core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+ if (core) {
+ bus->drv_mips.core = core;
+ bcma_core_mips_init(&bus->drv_mips);
+ }
+
/* Init PCIE core */
core = bcma_find_core(bus, BCMA_CORE_PCIE);
if (core) {
@@ -144,6 +165,15 @@ int bcma_bus_register(struct bcma_bus *bus)
bcma_core_pci_init(&bus->drv_pci);
}
+ /* Try to get SPROM */
+ err = bcma_sprom_get(bus);
+ if (err == -ENOENT) {
+ pr_err("No SPROM available\n");
+ } else if (err) {
+ pr_err("Failed to get SPROM: %d\n", err);
+ return -ENOENT;
+ }
+
/* Register found cores */
bcma_register_cores(bus);
@@ -151,13 +181,64 @@ int bcma_bus_register(struct bcma_bus *bus)
return 0;
}
-EXPORT_SYMBOL_GPL(bcma_bus_register);
void bcma_bus_unregister(struct bcma_bus *bus)
{
bcma_unregister_cores(bus);
}
-EXPORT_SYMBOL_GPL(bcma_bus_unregister);
+
+int __init bcma_bus_early_register(struct bcma_bus *bus,
+ struct bcma_device *core_cc,
+ struct bcma_device *core_mips)
+{
+ int err;
+ struct bcma_device *core;
+ struct bcma_device_id match;
+
+ bcma_init_bus(bus);
+
+ match.manuf = BCMA_MANUF_BCM;
+ match.id = BCMA_CORE_CHIPCOMMON;
+ match.class = BCMA_CL_SIM;
+ match.rev = BCMA_ANY_REV;
+
+ /* Scan for chip common core */
+ err = bcma_bus_scan_early(bus, &match, core_cc);
+ if (err) {
+ pr_err("Failed to scan for common core: %d\n", err);
+ return -1;
+ }
+
+ match.manuf = BCMA_MANUF_MIPS;
+ match.id = BCMA_CORE_MIPS_74K;
+ match.class = BCMA_CL_SIM;
+ match.rev = BCMA_ANY_REV;
+
+ /* Scan for mips core */
+ err = bcma_bus_scan_early(bus, &match, core_mips);
+ if (err) {
+ pr_err("Failed to scan for mips core: %d\n", err);
+ return -1;
+ }
+
+ /* Init CC core */
+ core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
+ if (core) {
+ bus->drv_cc.core = core;
+ bcma_core_chipcommon_init(&bus->drv_cc);
+ }
+
+ /* Init MIPS core */
+ core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+ if (core) {
+ bus->drv_mips.core = core;
+ bcma_core_mips_init(&bus->drv_mips);
+ }
+
+ pr_info("Early bus registered\n");
+
+ return 0;
+}
int __bcma_driver_register(struct bcma_driver *drv, struct module *owner)
{
@@ -217,6 +298,16 @@ static int bcma_device_remove(struct device *dev)
return 0;
}
+static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+
+ return add_uevent_var(env,
+ "MODALIAS=bcma:m%04Xid%04Xrev%02Xcl%02X",
+ core->id.manuf, core->id.id,
+ core->id.rev, core->id.class);
+}
+
static int __init bcma_modinit(void)
{
int err;
diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c
index 40d7dcce8933..cad994857683 100644
--- a/drivers/bcma/scan.c
+++ b/drivers/bcma/scan.c
@@ -200,18 +200,162 @@ static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 **eromptr,
return addrl;
}
-int bcma_bus_scan(struct bcma_bus *bus)
+static struct bcma_device *bcma_find_core_by_index(struct bcma_bus *bus,
+ u16 index)
{
- u32 erombase;
- u32 __iomem *eromptr, *eromend;
+ struct bcma_device *core;
+ list_for_each_entry(core, &bus->cores, list) {
+ if (core->core_index == index)
+ return core;
+ }
+ return NULL;
+}
+
+static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
+ struct bcma_device_id *match, int core_num,
+ struct bcma_device *core)
+{
+ s32 tmp;
+ u8 i, j;
s32 cia, cib;
u8 ports[2], wrappers[2];
+ /* get CIs */
+ cia = bcma_erom_get_ci(bus, eromptr);
+ if (cia < 0) {
+ bcma_erom_push_ent(eromptr);
+ if (bcma_erom_is_end(bus, eromptr))
+ return -ESPIPE;
+ return -EILSEQ;
+ }
+ cib = bcma_erom_get_ci(bus, eromptr);
+ if (cib < 0)
+ return -EILSEQ;
+
+ /* parse CIs */
+ core->id.class = (cia & SCAN_CIA_CLASS) >> SCAN_CIA_CLASS_SHIFT;
+ core->id.id = (cia & SCAN_CIA_ID) >> SCAN_CIA_ID_SHIFT;
+ core->id.manuf = (cia & SCAN_CIA_MANUF) >> SCAN_CIA_MANUF_SHIFT;
+ ports[0] = (cib & SCAN_CIB_NMP) >> SCAN_CIB_NMP_SHIFT;
+ ports[1] = (cib & SCAN_CIB_NSP) >> SCAN_CIB_NSP_SHIFT;
+ wrappers[0] = (cib & SCAN_CIB_NMW) >> SCAN_CIB_NMW_SHIFT;
+ wrappers[1] = (cib & SCAN_CIB_NSW) >> SCAN_CIB_NSW_SHIFT;
+ core->id.rev = (cib & SCAN_CIB_REV) >> SCAN_CIB_REV_SHIFT;
+
+ if (((core->id.manuf == BCMA_MANUF_ARM) &&
+ (core->id.id == 0xFFF)) ||
+ (ports[1] == 0)) {
+ bcma_erom_skip_component(bus, eromptr);
+ return -ENXIO;
+ }
+
+ /* check if component is a core at all */
+ if (wrappers[0] + wrappers[1] == 0) {
+ /* we could save addrl of the router
+ if (cid == BCMA_CORE_OOB_ROUTER)
+ */
+ bcma_erom_skip_component(bus, eromptr);
+ return -ENXIO;
+ }
+
+ if (bcma_erom_is_bridge(bus, eromptr)) {
+ bcma_erom_skip_component(bus, eromptr);
+ return -ENXIO;
+ }
+
+ if (bcma_find_core_by_index(bus, core_num)) {
+ bcma_erom_skip_component(bus, eromptr);
+ return -ENODEV;
+ }
+
+ if (match && ((match->manuf != BCMA_ANY_MANUF &&
+ match->manuf != core->id.manuf) ||
+ (match->id != BCMA_ANY_ID && match->id != core->id.id) ||
+ (match->rev != BCMA_ANY_REV && match->rev != core->id.rev) ||
+ (match->class != BCMA_ANY_CLASS && match->class != core->id.class)
+ )) {
+ bcma_erom_skip_component(bus, eromptr);
+ return -ENODEV;
+ }
+
+ /* get & parse master ports */
+ for (i = 0; i < ports[0]; i++) {
+ s32 mst_port_d = bcma_erom_get_mst_port(bus, eromptr);
+ if (mst_port_d < 0)
+ return -EILSEQ;
+ }
+
+ /* get & parse slave ports */
+ for (i = 0; i < ports[1]; i++) {
+ for (j = 0; ; j++) {
+ tmp = bcma_erom_get_addr_desc(bus, eromptr,
+ SCAN_ADDR_TYPE_SLAVE, i);
+ if (tmp < 0) {
+ /* no more entries for port _i_ */
+ /* pr_debug("erom: slave port %d "
+ * "has %d descriptors\n", i, j); */
+ break;
+ } else {
+ if (i == 0 && j == 0)
+ core->addr = tmp;
+ }
+ }
+ }
+
+ /* get & parse master wrappers */
+ for (i = 0; i < wrappers[0]; i++) {
+ for (j = 0; ; j++) {
+ tmp = bcma_erom_get_addr_desc(bus, eromptr,
+ SCAN_ADDR_TYPE_MWRAP, i);
+ if (tmp < 0) {
+ /* no more entries for port _i_ */
+ /* pr_debug("erom: master wrapper %d "
+ * "has %d descriptors\n", i, j); */
+ break;
+ } else {
+ if (i == 0 && j == 0)
+ core->wrap = tmp;
+ }
+ }
+ }
+
+ /* get & parse slave wrappers */
+ for (i = 0; i < wrappers[1]; i++) {
+ u8 hack = (ports[1] == 1) ? 0 : 1;
+ for (j = 0; ; j++) {
+ tmp = bcma_erom_get_addr_desc(bus, eromptr,
+ SCAN_ADDR_TYPE_SWRAP, i + hack);
+ if (tmp < 0) {
+ /* no more entries for port _i_ */
+ /* pr_debug("erom: master wrapper %d "
+ * has %d descriptors\n", i, j); */
+ break;
+ } else {
+ if (wrappers[0] == 0 && !i && !j)
+ core->wrap = tmp;
+ }
+ }
+ }
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+ core->io_addr = ioremap_nocache(core->addr, BCMA_CORE_SIZE);
+ if (!core->io_addr)
+ return -ENOMEM;
+ core->io_wrap = ioremap_nocache(core->wrap, BCMA_CORE_SIZE);
+ if (!core->io_wrap) {
+ iounmap(core->io_addr);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+void bcma_init_bus(struct bcma_bus *bus)
+{
s32 tmp;
- u8 i, j;
- int err;
+ if (bus->init_done)
+ return;
INIT_LIST_HEAD(&bus->cores);
bus->nr_cores = 0;
@@ -222,9 +366,27 @@ int bcma_bus_scan(struct bcma_bus *bus)
bus->chipinfo.id = (tmp & BCMA_CC_ID_ID) >> BCMA_CC_ID_ID_SHIFT;
bus->chipinfo.rev = (tmp & BCMA_CC_ID_REV) >> BCMA_CC_ID_REV_SHIFT;
bus->chipinfo.pkg = (tmp & BCMA_CC_ID_PKG) >> BCMA_CC_ID_PKG_SHIFT;
+ bus->init_done = true;
+}
+
+int bcma_bus_scan(struct bcma_bus *bus)
+{
+ u32 erombase;
+ u32 __iomem *eromptr, *eromend;
+
+ int err, core_num = 0;
+
+ bcma_init_bus(bus);
erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
- eromptr = bus->mmio;
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+ eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
+ if (!eromptr)
+ return -ENOMEM;
+ } else {
+ eromptr = bus->mmio;
+ }
+
eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
bcma_scan_switch_core(bus, erombase);
@@ -236,125 +398,89 @@ int bcma_bus_scan(struct bcma_bus *bus)
INIT_LIST_HEAD(&core->list);
core->bus = bus;
- /* get CIs */
- cia = bcma_erom_get_ci(bus, &eromptr);
- if (cia < 0) {
- bcma_erom_push_ent(&eromptr);
- if (bcma_erom_is_end(bus, &eromptr))
- break;
- err= -EILSEQ;
- goto out;
- }
- cib = bcma_erom_get_ci(bus, &eromptr);
- if (cib < 0) {
- err= -EILSEQ;
- goto out;
- }
-
- /* parse CIs */
- core->id.class = (cia & SCAN_CIA_CLASS) >> SCAN_CIA_CLASS_SHIFT;
- core->id.id = (cia & SCAN_CIA_ID) >> SCAN_CIA_ID_SHIFT;
- core->id.manuf = (cia & SCAN_CIA_MANUF) >> SCAN_CIA_MANUF_SHIFT;
- ports[0] = (cib & SCAN_CIB_NMP) >> SCAN_CIB_NMP_SHIFT;
- ports[1] = (cib & SCAN_CIB_NSP) >> SCAN_CIB_NSP_SHIFT;
- wrappers[0] = (cib & SCAN_CIB_NMW) >> SCAN_CIB_NMW_SHIFT;
- wrappers[1] = (cib & SCAN_CIB_NSW) >> SCAN_CIB_NSW_SHIFT;
- core->id.rev = (cib & SCAN_CIB_REV) >> SCAN_CIB_REV_SHIFT;
-
- if (((core->id.manuf == BCMA_MANUF_ARM) &&
- (core->id.id == 0xFFF)) ||
- (ports[1] == 0)) {
- bcma_erom_skip_component(bus, &eromptr);
+ err = bcma_get_next_core(bus, &eromptr, NULL, core_num, core);
+ if (err == -ENODEV) {
+ core_num++;
continue;
- }
-
- /* check if component is a core at all */
- if (wrappers[0] + wrappers[1] == 0) {
- /* we could save addrl of the router
- if (cid == BCMA_CORE_OOB_ROUTER)
- */
- bcma_erom_skip_component(bus, &eromptr);
+ } else if (err == -ENXIO)
continue;
- }
+ else if (err == -ESPIPE)
+ break;
+ else if (err < 0)
+ return err;
- if (bcma_erom_is_bridge(bus, &eromptr)) {
- bcma_erom_skip_component(bus, &eromptr);
- continue;
- }
+ core->core_index = core_num++;
+ bus->nr_cores++;
- /* get & parse master ports */
- for (i = 0; i < ports[0]; i++) {
- u32 mst_port_d = bcma_erom_get_mst_port(bus, &eromptr);
- if (mst_port_d < 0) {
- err= -EILSEQ;
- goto out;
- }
- }
+ pr_info("Core %d found: %s "
+ "(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
+ core->core_index, bcma_device_name(&core->id),
+ core->id.manuf, core->id.id, core->id.rev,
+ core->id.class);
- /* get & parse slave ports */
- for (i = 0; i < ports[1]; i++) {
- for (j = 0; ; j++) {
- tmp = bcma_erom_get_addr_desc(bus, &eromptr,
- SCAN_ADDR_TYPE_SLAVE, i);
- if (tmp < 0) {
- /* no more entries for port _i_ */
- /* pr_debug("erom: slave port %d "
- * "has %d descriptors\n", i, j); */
- break;
- } else {
- if (i == 0 && j == 0)
- core->addr = tmp;
- }
- }
- }
+ list_add(&core->list, &bus->cores);
+ }
- /* get & parse master wrappers */
- for (i = 0; i < wrappers[0]; i++) {
- for (j = 0; ; j++) {
- tmp = bcma_erom_get_addr_desc(bus, &eromptr,
- SCAN_ADDR_TYPE_MWRAP, i);
- if (tmp < 0) {
- /* no more entries for port _i_ */
- /* pr_debug("erom: master wrapper %d "
- * "has %d descriptors\n", i, j); */
- break;
- } else {
- if (i == 0 && j == 0)
- core->wrap = tmp;
- }
- }
- }
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+ iounmap(eromptr);
- /* get & parse slave wrappers */
- for (i = 0; i < wrappers[1]; i++) {
- u8 hack = (ports[1] == 1) ? 0 : 1;
- for (j = 0; ; j++) {
- tmp = bcma_erom_get_addr_desc(bus, &eromptr,
- SCAN_ADDR_TYPE_SWRAP, i + hack);
- if (tmp < 0) {
- /* no more entries for port _i_ */
- /* pr_debug("erom: master wrapper %d "
- * has %d descriptors\n", i, j); */
- break;
- } else {
- if (wrappers[0] == 0 && !i && !j)
- core->wrap = tmp;
- }
- }
- }
+ return 0;
+}
+
+int __init bcma_bus_scan_early(struct bcma_bus *bus,
+ struct bcma_device_id *match,
+ struct bcma_device *core)
+{
+ u32 erombase;
+ u32 __iomem *eromptr, *eromend;
+
+ int err = -ENODEV;
+ int core_num = 0;
+
+ erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+ eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
+ if (!eromptr)
+ return -ENOMEM;
+ } else {
+ eromptr = bus->mmio;
+ }
+
+ eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
+
+ bcma_scan_switch_core(bus, erombase);
+
+ while (eromptr < eromend) {
+ memset(core, 0, sizeof(*core));
+ INIT_LIST_HEAD(&core->list);
+ core->bus = bus;
+ err = bcma_get_next_core(bus, &eromptr, match, core_num, core);
+ if (err == -ENODEV) {
+ core_num++;
+ continue;
+ } else if (err == -ENXIO)
+ continue;
+ else if (err == -ESPIPE)
+ break;
+ else if (err < 0)
+ return err;
+
+ core->core_index = core_num++;
+ bus->nr_cores++;
pr_info("Core %d found: %s "
"(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
- bus->nr_cores, bcma_device_name(&core->id),
+ core->core_index, bcma_device_name(&core->id),
core->id.manuf, core->id.id, core->id.rev,
core->id.class);
- core->core_index = bus->nr_cores++;
list_add(&core->list, &bus->cores);
- continue;
-out:
- return err;
+ err = 0;
+ break;
}
- return 0;
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+ iounmap(eromptr);
+
+ return err;
}
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
new file mode 100644
index 000000000000..d7292390d236
--- /dev/null
+++ b/drivers/bcma/sprom.c
@@ -0,0 +1,186 @@
+/*
+ * Broadcom specific AMBA
+ * SPROM reading
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#define SPOFF(offset) ((offset) / sizeof(u16))
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
+{
+ int i;
+ for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
+ sprom[i] = bcma_read16(bus->drv_cc.core,
+ offset + (i * 2));
+}
+
+/**************************************************
+ * Validation.
+ **************************************************/
+
+static inline u8 bcma_crc8(u8 crc, u8 data)
+{
+ /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */
+ static const u8 t[] = {
+ 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
+ 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
+ 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
+ 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
+ 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
+ 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
+ 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
+ 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
+ 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
+ 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
+ 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
+ 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
+ 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
+ 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
+ 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
+ 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
+ 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
+ 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
+ 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
+ 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
+ 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
+ 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
+ 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
+ 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
+ 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
+ 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
+ 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
+ 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
+ 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
+ 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
+ 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
+ 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
+ };
+ return t[crc ^ data];
+}
+
+static u8 bcma_sprom_crc(const u16 *sprom)
+{
+ int word;
+ u8 crc = 0xFF;
+
+ for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
+ crc = bcma_crc8(crc, sprom[word] & 0x00FF);
+ crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
+ }
+ crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
+ crc ^= 0xFF;
+
+ return crc;
+}
+
+static int bcma_sprom_check_crc(const u16 *sprom)
+{
+ u8 crc;
+ u8 expected_crc;
+ u16 tmp;
+
+ crc = bcma_sprom_crc(sprom);
+ tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
+ expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
+ if (crc != expected_crc)
+ return -EPROTO;
+
+ return 0;
+}
+
+static int bcma_sprom_valid(const u16 *sprom)
+{
+ u16 revision;
+ int err;
+
+ err = bcma_sprom_check_crc(sprom);
+ if (err)
+ return err;
+
+ revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
+ if (revision != 8 && revision != 9) {
+ pr_err("Unsupported SPROM revision: %d\n", revision);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+/**************************************************
+ * SPROM extraction.
+ **************************************************/
+
+static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
+{
+ u16 v;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
+ *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
+ }
+
+ bus->sprom.board_rev = sprom[SPOFF(SSB_SPROM8_BOARDREV)];
+
+ bus->sprom.boardflags_lo = sprom[SPOFF(SSB_SPROM8_BFLLO)];
+ bus->sprom.boardflags_hi = sprom[SPOFF(SSB_SPROM8_BFLHI)];
+ bus->sprom.boardflags2_lo = sprom[SPOFF(SSB_SPROM8_BFL2LO)];
+ bus->sprom.boardflags2_hi = sprom[SPOFF(SSB_SPROM8_BFL2HI)];
+
+ bus->sprom.country_code = sprom[SPOFF(SSB_SPROM8_CCODE)];
+}
+
+int bcma_sprom_get(struct bcma_bus *bus)
+{
+ u16 offset;
+ u16 *sprom;
+ int err = 0;
+
+ if (!bus->drv_cc.core)
+ return -EOPNOTSUPP;
+
+ if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
+ return -ENOENT;
+
+ sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
+ GFP_KERNEL);
+ if (!sprom)
+ return -ENOMEM;
+
+ if (bus->chipinfo.id == 0x4331)
+ bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
+
+ /* Most cards have SPROM moved by additional offset 0x30 (48 dwords).
+ * According to brcm80211 this applies to cards with PCIe rev >= 6
+ * TODO: understand this condition and use it */
+ offset = (bus->chipinfo.id == 0x4331) ? BCMA_CC_SPROM :
+ BCMA_CC_SPROM_PCIE6;
+ bcma_sprom_read(bus, offset, sprom);
+
+ if (bus->chipinfo.id == 0x4331)
+ bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
+
+ err = bcma_sprom_valid(sprom);
+ if (err)
+ goto out;
+
+ bcma_sprom_extract_r8(bus, sprom);
+
+out:
+ kfree(sprom);
+ return err;
+}
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 717d6e4e18d3..6f07ec1c2f58 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -256,6 +256,21 @@ config BLK_DEV_LOOP
Most users will answer N here.
+config BLK_DEV_LOOP_MIN_COUNT
+ int "Number of loop devices to pre-create at init time"
+ depends on BLK_DEV_LOOP
+ default 8
+ help
+ Static number of loop devices to be unconditionally pre-created
+ at init time.
+
+ This default value can be overwritten on the kernel command
+ line or with module-parameter loop.max_loop.
+
+ The historic default is 8. If a late 2011 version of losetup(8)
+ is used, it can be set to 0, since needed loop devices can be
+ dynamically allocated with the /dev/loop-control interface.
+
config BLK_DEV_CRYPTOLOOP
tristate "Cryptoloop Support"
select CRYPTO
@@ -471,7 +486,7 @@ config XEN_BLKDEV_FRONTEND
in another domain which drives the actual block device.
config XEN_BLKDEV_BACKEND
- tristate "Block-device backend driver"
+ tristate "Xen block-device backend driver"
depends on XEN_BACKEND
help
The block-device backend driver allows the kernel to export its
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
index 528f6318ded1..321de7b6c442 100644
--- a/drivers/block/aoe/aoeblk.c
+++ b/drivers/block/aoe/aoeblk.c
@@ -15,6 +15,7 @@
#include <linux/genhd.h>
#include <linux/netdevice.h>
#include <linux/mutex.h>
+#include <linux/export.h>
#include "aoe.h"
static DEFINE_MUTEX(aoeblk_mutex);
@@ -159,7 +160,7 @@ aoeblk_release(struct gendisk *disk, fmode_t mode)
return 0;
}
-static int
+static void
aoeblk_make_request(struct request_queue *q, struct bio *bio)
{
struct sk_buff_head queue;
@@ -172,25 +173,25 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio)
if (bio == NULL) {
printk(KERN_ERR "aoe: bio is NULL\n");
BUG();
- return 0;
+ return;
}
d = bio->bi_bdev->bd_disk->private_data;
if (d == NULL) {
printk(KERN_ERR "aoe: bd_disk->private_data is NULL\n");
BUG();
bio_endio(bio, -ENXIO);
- return 0;
+ return;
} else if (bio->bi_io_vec == NULL) {
printk(KERN_ERR "aoe: bi_io_vec is NULL\n");
BUG();
bio_endio(bio, -ENXIO);
- return 0;
+ return;
}
buf = mempool_alloc(d->bufpool, GFP_NOIO);
if (buf == NULL) {
printk(KERN_INFO "aoe: buf allocation failure\n");
bio_endio(bio, -ENOMEM);
- return 0;
+ return;
}
memset(buf, 0, sizeof(*buf));
INIT_LIST_HEAD(&buf->bufs);
@@ -211,7 +212,7 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio)
spin_unlock_irqrestore(&d->lock, flags);
mempool_free(buf, d->bufpool);
bio_endio(bio, -ENXIO);
- return 0;
+ return;
}
list_add_tail(&buf->bufs, &d->bufq);
@@ -222,8 +223,6 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio)
spin_unlock_irqrestore(&d->lock, flags);
aoenet_xmit(&queue);
-
- return 0;
}
static int
diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c
index 146296ca4965..5f8e39c43ae5 100644
--- a/drivers/block/aoe/aoechr.c
+++ b/drivers/block/aoe/aoechr.c
@@ -11,6 +11,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/skbuff.h>
+#include <linux/export.h>
#include "aoe.h"
enum {
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index dba1c32e1ddf..d22119d49e53 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -323,7 +323,7 @@ out:
return err;
}
-static int brd_make_request(struct request_queue *q, struct bio *bio)
+static void brd_make_request(struct request_queue *q, struct bio *bio)
{
struct block_device *bdev = bio->bi_bdev;
struct brd_device *brd = bdev->bd_disk->private_data;
@@ -359,8 +359,6 @@ static int brd_make_request(struct request_queue *q, struct bio *bio)
out:
bio_endio(bio, err);
-
- return 0;
}
#ifdef CONFIG_BLK_DEV_XIP
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 8f4ef656a1af..8004ac30a7a8 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -24,6 +24,7 @@
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/pci.h>
+#include <linux/pci-aspm.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
@@ -68,6 +69,10 @@ static int cciss_tape_cmds = 6;
module_param(cciss_tape_cmds, int, 0644);
MODULE_PARM_DESC(cciss_tape_cmds,
"number of commands to allocate for tape devices (default: 6)");
+static int cciss_simple_mode;
+module_param(cciss_simple_mode, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(cciss_simple_mode,
+ "Use 'simple mode' rather than 'performant mode'");
static DEFINE_MUTEX(cciss_mutex);
static struct proc_dir_entry *proc_cciss;
@@ -176,6 +181,7 @@ static void cciss_geometry_inquiry(ctlr_info_t *h, int logvol,
unsigned int block_size, InquiryData_struct *inq_buff,
drive_info_struct *drv);
static void __devinit cciss_interrupt_mode(ctlr_info_t *);
+static int __devinit cciss_enter_simple_mode(struct ctlr_info *h);
static void start_io(ctlr_info_t *h);
static int sendcmd_withirq(ctlr_info_t *h, __u8 cmd, void *buff, size_t size,
__u8 page_code, unsigned char scsi3addr[],
@@ -388,7 +394,7 @@ static void cciss_seq_show_header(struct seq_file *seq)
h->product_name,
(unsigned long)h->board_id,
h->firm_ver[0], h->firm_ver[1], h->firm_ver[2],
- h->firm_ver[3], (unsigned int)h->intr[PERF_MODE_INT],
+ h->firm_ver[3], (unsigned int)h->intr[h->intr_mode],
h->num_luns,
h->Qdepth, h->commands_outstanding,
h->maxQsinceinit, h->max_outstanding, h->maxSG);
@@ -636,6 +642,18 @@ static ssize_t host_store_rescan(struct device *dev,
}
static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan);
+static ssize_t host_show_transport_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ctlr_info *h = to_hba(dev);
+
+ return snprintf(buf, 20, "%s\n",
+ h->transMethod & CFGTBL_Trans_Performant ?
+ "performant" : "simple");
+}
+static DEVICE_ATTR(transport_mode, S_IRUGO, host_show_transport_mode, NULL);
+
static ssize_t dev_show_unique_id(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -808,6 +826,7 @@ static DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL);
static struct attribute *cciss_host_attrs[] = {
&dev_attr_rescan.attr,
&dev_attr_resettable.attr,
+ &dev_attr_transport_mode.attr,
NULL
};
@@ -3984,6 +4003,9 @@ static void __devinit cciss_put_controller_into_performant_mode(ctlr_info_t *h)
{
__u32 trans_support;
+ if (cciss_simple_mode)
+ return;
+
dev_dbg(&h->pdev->dev, "Trying to put board into Performant mode\n");
/* Attempt to put controller into performant mode if supported */
/* Does board support performant mode? */
@@ -4081,7 +4103,7 @@ static void __devinit cciss_interrupt_mode(ctlr_info_t *h)
default_int_mode:
#endif /* CONFIG_PCI_MSI */
/* if we get here we're going to use the default interrupt mode */
- h->intr[PERF_MODE_INT] = h->pdev->irq;
+ h->intr[h->intr_mode] = h->pdev->irq;
return;
}
@@ -4298,6 +4320,10 @@ static int __devinit cciss_pci_init(ctlr_info_t *h)
dev_warn(&h->pdev->dev, "controller appears to be disabled\n");
return -ENODEV;
}
+
+ pci_disable_link_state(h->pdev, PCIE_LINK_STATE_L0S |
+ PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
+
err = pci_enable_device(h->pdev);
if (err) {
dev_warn(&h->pdev->dev, "Unable to Enable PCI device\n");
@@ -4341,6 +4367,9 @@ static int __devinit cciss_pci_init(ctlr_info_t *h)
}
cciss_enable_scsi_prefetch(h);
cciss_p600_dma_prefetch_quirk(h);
+ err = cciss_enter_simple_mode(h);
+ if (err)
+ goto err_out_free_res;
cciss_put_controller_into_performant_mode(h);
return 0;
@@ -4533,6 +4562,13 @@ static int cciss_controller_hard_reset(struct pci_dev *pdev,
pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
pmcsr |= PCI_D0;
pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+
+ /*
+ * The P600 requires a small delay when changing states.
+ * Otherwise we may think the board did not reset and we bail.
+ * This for kdump only and is particular to the P600.
+ */
+ msleep(500);
}
return 0;
}
@@ -4843,20 +4879,20 @@ static int cciss_request_irq(ctlr_info_t *h,
irqreturn_t (*intxhandler)(int, void *))
{
if (h->msix_vector || h->msi_vector) {
- if (!request_irq(h->intr[PERF_MODE_INT], msixhandler,
+ if (!request_irq(h->intr[h->intr_mode], msixhandler,
IRQF_DISABLED, h->devname, h))
return 0;
dev_err(&h->pdev->dev, "Unable to get msi irq %d"
- " for %s\n", h->intr[PERF_MODE_INT],
+ " for %s\n", h->intr[h->intr_mode],
h->devname);
return -1;
}
- if (!request_irq(h->intr[PERF_MODE_INT], intxhandler,
+ if (!request_irq(h->intr[h->intr_mode], intxhandler,
IRQF_DISABLED, h->devname, h))
return 0;
dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n",
- h->intr[PERF_MODE_INT], h->devname);
+ h->intr[h->intr_mode], h->devname);
return -1;
}
@@ -4887,7 +4923,7 @@ static void cciss_undo_allocations_after_kdump_soft_reset(ctlr_info_t *h)
{
int ctlr = h->ctlr;
- free_irq(h->intr[PERF_MODE_INT], h);
+ free_irq(h->intr[h->intr_mode], h);
#ifdef CONFIG_PCI_MSI
if (h->msix_vector)
pci_disable_msix(h->pdev);
@@ -4953,6 +4989,7 @@ reinit_after_soft_reset:
h = hba[i];
h->pdev = pdev;
h->busy_initializing = 1;
+ h->intr_mode = cciss_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT;
INIT_LIST_HEAD(&h->cmpQ);
INIT_LIST_HEAD(&h->reqQ);
mutex_init(&h->busy_shutting_down);
@@ -5009,7 +5046,7 @@ reinit_after_soft_reset:
dev_info(&h->pdev->dev, "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n",
h->devname, pdev->device, pci_name(pdev),
- h->intr[PERF_MODE_INT], dac ? "" : " not");
+ h->intr[h->intr_mode], dac ? "" : " not");
if (cciss_allocate_cmd_pool(h))
goto clean4;
@@ -5056,7 +5093,7 @@ reinit_after_soft_reset:
spin_lock_irqsave(&h->lock, flags);
h->access.set_intr_mask(h, CCISS_INTR_OFF);
spin_unlock_irqrestore(&h->lock, flags);
- free_irq(h->intr[PERF_MODE_INT], h);
+ free_irq(h->intr[h->intr_mode], h);
rc = cciss_request_irq(h, cciss_msix_discard_completions,
cciss_intx_discard_completions);
if (rc) {
@@ -5126,6 +5163,7 @@ reinit_after_soft_reset:
h->cciss_max_sectors = 8192;
rebuild_lun_table(h, 1, 0);
+ cciss_engage_scsi(h);
h->busy_initializing = 0;
return 1;
@@ -5133,7 +5171,7 @@ clean4:
cciss_free_cmd_pool(h);
cciss_free_scatterlists(h);
cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds);
- free_irq(h->intr[PERF_MODE_INT], h);
+ free_irq(h->intr[h->intr_mode], h);
clean2:
unregister_blkdev(h->major, h->devname);
clean1:
@@ -5172,9 +5210,31 @@ static void cciss_shutdown(struct pci_dev *pdev)
if (return_code != IO_OK)
dev_warn(&h->pdev->dev, "Error flushing cache\n");
h->access.set_intr_mask(h, CCISS_INTR_OFF);
- free_irq(h->intr[PERF_MODE_INT], h);
+ free_irq(h->intr[h->intr_mode], h);
}
+static int __devinit cciss_enter_simple_mode(struct ctlr_info *h)
+{
+ u32 trans_support;
+
+ trans_support = readl(&(h->cfgtable->TransportSupport));
+ if (!(trans_support & SIMPLE_MODE))
+ return -ENOTSUPP;
+
+ h->max_commands = readl(&(h->cfgtable->CmdsOutMax));
+ writel(CFGTBL_Trans_Simple, &(h->cfgtable->HostWrite.TransportRequest));
+ writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
+ cciss_wait_for_mode_change_ack(h);
+ print_cfg_table(h);
+ if (!(readl(&(h->cfgtable->TransportActive)) & CFGTBL_Trans_Simple)) {
+ dev_warn(&h->pdev->dev, "unable to get board into simple mode\n");
+ return -ENODEV;
+ }
+ h->transMethod = CFGTBL_Trans_Simple;
+ return 0;
+}
+
+
static void __devexit cciss_remove_one(struct pci_dev *pdev)
{
ctlr_info_t *h;
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 16b4d58d84dd..7fda30e4a241 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -92,6 +92,7 @@ struct ctlr_info
unsigned int intr[4];
unsigned int msix_vector;
unsigned int msi_vector;
+ int intr_mode;
int cciss_max_sectors;
BYTE cciss_read;
BYTE cciss_write;
@@ -223,7 +224,7 @@ static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c)
h->ctlr, c->busaddr);
#endif /* CCISS_DEBUG */
writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
- readl(h->vaddr + SA5_REQUEST_PORT_OFFSET);
+ readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
h->commands_outstanding++;
if ( h->commands_outstanding > h->max_outstanding)
h->max_outstanding = h->commands_outstanding;
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index 696100241a6f..e820b68d2f6c 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -33,7 +33,7 @@
#include <linux/slab.h>
#include <linux/string.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -1720,5 +1720,6 @@ static int cciss_eh_abort_handler(struct scsi_cmnd *scsicmd)
/* If no tape support, then these become defined out of existence */
#define cciss_scsi_setup(cntl_num)
+#define cciss_engage_scsi(h)
#endif /* CONFIG_CISS_SCSI_TAPE */
diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
index b2fceb53e809..9125bbeacd4d 100644
--- a/drivers/block/cpqarray.c
+++ b/drivers/block/cpqarray.c
@@ -620,6 +620,7 @@ static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
}
vendor_id = pdev->vendor;
device_id = pdev->device;
+ revision = pdev->revision;
irq = pdev->irq;
for(i=0; i<6; i++)
@@ -632,7 +633,6 @@ static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
}
pci_read_config_word(pdev, PCI_COMMAND, &command);
- pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer);
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c
index 7b976296b564..912f585a760f 100644
--- a/drivers/block/drbd/drbd_bitmap.c
+++ b/drivers/block/drbd/drbd_bitmap.c
@@ -378,15 +378,14 @@ static struct page **bm_realloc_pages(struct drbd_bitmap *b, unsigned long want)
* thread. As we have no disk yet, we are not in the IO path,
* not even the IO path of the peer. */
bytes = sizeof(struct page *)*want;
- new_pages = kmalloc(bytes, GFP_KERNEL);
+ new_pages = kzalloc(bytes, GFP_KERNEL);
if (!new_pages) {
- new_pages = vmalloc(bytes);
+ new_pages = vzalloc(bytes);
if (!new_pages)
return NULL;
vmalloced = 1;
}
- memset(new_pages, 0, bytes);
if (want >= have) {
for (i = 0; i < have; i++)
new_pages[i] = old_pages[i];
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index ef2ceed3be4b..9cf20355ceec 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -28,7 +28,6 @@
#include <linux/compiler.h>
#include <linux/types.h>
-#include <linux/version.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/bitops.h>
@@ -928,7 +927,7 @@ struct drbd_md {
#define NL_INT64(pn,pr,member) __u64 member;
#define NL_BIT(pn,pr,member) unsigned member:1;
#define NL_STRING(pn,pr,member,len) unsigned char member[len]; int member ## _len;
-#include "linux/drbd_nl.h"
+#include <linux/drbd_nl.h>
struct drbd_backing_dev {
struct block_device *backing_bdev;
@@ -1507,7 +1506,7 @@ extern void drbd_free_mdev(struct drbd_conf *mdev);
extern int proc_details;
/* drbd_req */
-extern int drbd_make_request(struct request_queue *q, struct bio *bio);
+extern void drbd_make_request(struct request_queue *q, struct bio *bio);
extern int drbd_read_remote(struct drbd_conf *mdev, struct drbd_request *req);
extern int drbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bvm, struct bio_vec *bvec);
extern int is_valid_ar_handle(struct drbd_request *, sector_t);
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 515bcd948a43..af2a25049bce 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -94,7 +94,7 @@ static int name ## _from_tags(struct drbd_conf *mdev, \
arg->member ## _len = dlen; \
memcpy(arg->member, tags, min_t(size_t, dlen, len)); \
break;
-#include "linux/drbd_nl.h"
+#include <linux/drbd_nl.h>
/* Generate the struct to tag_list functions */
#define NL_PACKET(name, number, fields) \
@@ -129,7 +129,7 @@ name ## _to_tags(struct drbd_conf *mdev, \
put_unaligned(arg->member ## _len, tags++); \
memcpy(tags, arg->member, arg->member ## _len); \
tags = (unsigned short *)((char *)tags + arg->member ## _len);
-#include "linux/drbd_nl.h"
+#include <linux/drbd_nl.h>
void drbd_bcast_ev_helper(struct drbd_conf *mdev, char *helper_name);
void drbd_nl_send_reply(struct cn_msg *, int);
@@ -1829,10 +1829,10 @@ static int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *n
/* silently ignore cpu mask on UP kernel */
if (nr_cpu_ids > 1 && sc.cpu_mask[0] != 0) {
- err = __bitmap_parse(sc.cpu_mask, 32, 0,
+ err = bitmap_parse(sc.cpu_mask, 32,
cpumask_bits(new_cpu_mask), nr_cpu_ids);
if (err) {
- dev_warn(DEV, "__bitmap_parse() failed with %d\n", err);
+ dev_warn(DEV, "bitmap_parse() failed with %d\n", err);
retcode = ERR_CPU_MASK_PARSE;
goto fail;
}
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index 3424d675b769..4a0f314086e5 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -1073,7 +1073,7 @@ static int drbd_fail_request_early(struct drbd_conf *mdev, int is_write)
return 0;
}
-int drbd_make_request(struct request_queue *q, struct bio *bio)
+void drbd_make_request(struct request_queue *q, struct bio *bio)
{
unsigned int s_enr, e_enr;
struct drbd_conf *mdev = (struct drbd_conf *) q->queuedata;
@@ -1081,7 +1081,7 @@ int drbd_make_request(struct request_queue *q, struct bio *bio)
if (drbd_fail_request_early(mdev, bio_data_dir(bio) & WRITE)) {
bio_endio(bio, -EPERM);
- return 0;
+ return;
}
start_time = jiffies;
@@ -1100,7 +1100,8 @@ int drbd_make_request(struct request_queue *q, struct bio *bio)
if (likely(s_enr == e_enr)) {
inc_ap_bio(mdev, 1);
- return drbd_make_request_common(mdev, bio, start_time);
+ drbd_make_request_common(mdev, bio, start_time);
+ return;
}
/* can this bio be split generically?
@@ -1148,7 +1149,6 @@ int drbd_make_request(struct request_queue *q, struct bio *bio)
bio_pair_release(bp);
}
- return 0;
}
/* This is called by bio_add_page(). With this function we reduce
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 98de8f418676..9955a53733b2 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -4250,7 +4250,7 @@ static int __init floppy_init(void)
use_virtual_dma = can_use_virtual_dma & 1;
fdc_state[0].address = FDC1;
if (fdc_state[0].address == -1) {
- del_timer(&fd_timeout);
+ del_timer_sync(&fd_timeout);
err = -ENODEV;
goto out_unreg_region;
}
@@ -4261,7 +4261,7 @@ static int __init floppy_init(void)
fdc = 0; /* reset fdc in case of unexpected interrupt */
err = floppy_grab_irq_and_dma();
if (err) {
- del_timer(&fd_timeout);
+ del_timer_sync(&fd_timeout);
err = -EBUSY;
goto out_unreg_region;
}
@@ -4318,7 +4318,7 @@ static int __init floppy_init(void)
user_reset_fdc(-1, FD_RESET_ALWAYS, false);
}
fdc = 0;
- del_timer(&fd_timeout);
+ del_timer_sync(&fd_timeout);
current_drive = 0;
initialized = true;
if (have_no_fdc) {
@@ -4368,7 +4368,7 @@ out_unreg_blkdev:
unregister_blkdev(FLOPPY_MAJOR, "fd");
out_put_disk:
while (dr--) {
- del_timer(&motor_off_timer[dr]);
+ del_timer_sync(&motor_off_timer[dr]);
if (disks[dr]->queue)
blk_cleanup_queue(disks[dr]->queue);
put_disk(disks[dr]);
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index 007c630904c1..b52c9ca146fc 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -155,7 +155,7 @@ else \
#if (HD_DELAY > 0)
-#include <asm/i8253.h>
+#include <linux/i8253.h>
unsigned long last_req;
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 76c8da78212b..68b205a9338f 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -75,11 +75,13 @@
#include <linux/kthread.h>
#include <linux/splice.h>
#include <linux/sysfs.h>
+#include <linux/miscdevice.h>
+#include <linux/falloc.h>
#include <asm/uaccess.h>
-static LIST_HEAD(loop_devices);
-static DEFINE_MUTEX(loop_devices_mutex);
+static DEFINE_IDR(loop_index_idr);
+static DEFINE_MUTEX(loop_index_mutex);
static int max_part;
static int part_shift;
@@ -159,17 +161,19 @@ static struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = {
&xor_funcs
};
-static loff_t get_loop_size(struct loop_device *lo, struct file *file)
+static loff_t get_size(loff_t offset, loff_t sizelimit, struct file *file)
{
- loff_t size, offset, loopsize;
+ loff_t size, loopsize;
/* Compute loopsize in bytes */
size = i_size_read(file->f_mapping->host);
- offset = lo->lo_offset;
loopsize = size - offset;
- if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize)
- loopsize = lo->lo_sizelimit;
+ /* offset is beyond i_size, wierd but possible */
+ if (loopsize < 0)
+ return 0;
+ if (sizelimit > 0 && sizelimit < loopsize)
+ loopsize = sizelimit;
/*
* Unfortunately, if we want to do I/O on the device,
* the number of 512-byte sectors has to fit into a sector_t.
@@ -177,17 +181,25 @@ static loff_t get_loop_size(struct loop_device *lo, struct file *file)
return loopsize >> 9;
}
+static loff_t get_loop_size(struct loop_device *lo, struct file *file)
+{
+ return get_size(lo->lo_offset, lo->lo_sizelimit, file);
+}
+
static int
-figure_loop_size(struct loop_device *lo)
+figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit)
{
- loff_t size = get_loop_size(lo, lo->lo_backing_file);
+ loff_t size = get_size(offset, sizelimit, lo->lo_backing_file);
sector_t x = (sector_t)size;
if (unlikely((loff_t)x != size))
return -EFBIG;
-
+ if (lo->lo_offset != offset)
+ lo->lo_offset = offset;
+ if (lo->lo_sizelimit != sizelimit)
+ lo->lo_sizelimit = sizelimit;
set_capacity(lo->lo_disk, x);
- return 0;
+ return 0;
}
static inline int
@@ -203,74 +215,6 @@ lo_do_transfer(struct loop_device *lo, int cmd,
}
/**
- * do_lo_send_aops - helper for writing data to a loop device
- *
- * This is the fast version for backing filesystems which implement the address
- * space operations write_begin and write_end.
- */
-static int do_lo_send_aops(struct loop_device *lo, struct bio_vec *bvec,
- loff_t pos, struct page *unused)
-{
- struct file *file = lo->lo_backing_file; /* kudos to NFsckingS */
- struct address_space *mapping = file->f_mapping;
- pgoff_t index;
- unsigned offset, bv_offs;
- int len, ret;
-
- mutex_lock(&mapping->host->i_mutex);
- index = pos >> PAGE_CACHE_SHIFT;
- offset = pos & ((pgoff_t)PAGE_CACHE_SIZE - 1);
- bv_offs = bvec->bv_offset;
- len = bvec->bv_len;
- while (len > 0) {
- sector_t IV;
- unsigned size, copied;
- int transfer_result;
- struct page *page;
- void *fsdata;
-
- IV = ((sector_t)index << (PAGE_CACHE_SHIFT - 9))+(offset >> 9);
- size = PAGE_CACHE_SIZE - offset;
- if (size > len)
- size = len;
-
- ret = pagecache_write_begin(file, mapping, pos, size, 0,
- &page, &fsdata);
- if (ret)
- goto fail;
-
- file_update_time(file);
-
- transfer_result = lo_do_transfer(lo, WRITE, page, offset,
- bvec->bv_page, bv_offs, size, IV);
- copied = size;
- if (unlikely(transfer_result))
- copied = 0;
-
- ret = pagecache_write_end(file, mapping, pos, size, copied,
- page, fsdata);
- if (ret < 0 || ret != copied)
- goto fail;
-
- if (unlikely(transfer_result))
- goto fail;
-
- bv_offs += copied;
- len -= copied;
- offset = 0;
- index++;
- pos += copied;
- }
- ret = 0;
-out:
- mutex_unlock(&mapping->host->i_mutex);
- return ret;
-fail:
- ret = -1;
- goto out;
-}
-
-/**
* __do_lo_send_write - helper for writing data to a loop device
*
* This helper just factors out common code between do_lo_send_direct_write()
@@ -297,10 +241,8 @@ static int __do_lo_send_write(struct file *file,
/**
* do_lo_send_direct_write - helper for writing data to a loop device
*
- * This is the fast, non-transforming version for backing filesystems which do
- * not implement the address space operations write_begin and write_end.
- * It uses the write file operation which should be present on all writeable
- * filesystems.
+ * This is the fast, non-transforming version that does not need double
+ * buffering.
*/
static int do_lo_send_direct_write(struct loop_device *lo,
struct bio_vec *bvec, loff_t pos, struct page *page)
@@ -316,15 +258,9 @@ static int do_lo_send_direct_write(struct loop_device *lo,
/**
* do_lo_send_write - helper for writing data to a loop device
*
- * This is the slow, transforming version for filesystems which do not
- * implement the address space operations write_begin and write_end. It
- * uses the write file operation which should be present on all writeable
- * filesystems.
- *
- * Using fops->write is slower than using aops->{prepare,commit}_write in the
- * transforming case because we need to double buffer the data as we cannot do
- * the transformations in place as we do not have direct access to the
- * destination pages of the backing file.
+ * This is the slow, transforming version that needs to double buffer the
+ * data as it cannot do the transformations in place without having direct
+ * access to the destination pages of the backing file.
*/
static int do_lo_send_write(struct loop_device *lo, struct bio_vec *bvec,
loff_t pos, struct page *page)
@@ -350,17 +286,16 @@ static int lo_send(struct loop_device *lo, struct bio *bio, loff_t pos)
struct page *page = NULL;
int i, ret = 0;
- do_lo_send = do_lo_send_aops;
- if (!(lo->lo_flags & LO_FLAGS_USE_AOPS)) {
+ if (lo->transfer != transfer_none) {
+ page = alloc_page(GFP_NOIO | __GFP_HIGHMEM);
+ if (unlikely(!page))
+ goto fail;
+ kmap(page);
+ do_lo_send = do_lo_send_write;
+ } else {
do_lo_send = do_lo_send_direct_write;
- if (lo->transfer != transfer_none) {
- page = alloc_page(GFP_NOIO | __GFP_HIGHMEM);
- if (unlikely(!page))
- goto fail;
- kmap(page);
- do_lo_send = do_lo_send_write;
- }
}
+
bio_for_each_segment(bvec, bio, i) {
ret = do_lo_send(lo, bvec, pos, page);
if (ret < 0)
@@ -447,7 +382,8 @@ do_lo_receive(struct loop_device *lo,
if (retval < 0)
return retval;
-
+ if (retval != bvec->bv_len)
+ return -EIO;
return 0;
}
@@ -484,6 +420,29 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio)
}
}
+ /*
+ * We use punch hole to reclaim the free space used by the
+ * image a.k.a. discard. However we do support discard if
+ * encryption is enabled, because it may give an attacker
+ * useful information.
+ */
+ if (bio->bi_rw & REQ_DISCARD) {
+ struct file *file = lo->lo_backing_file;
+ int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
+
+ if ((!file->f_op->fallocate) ||
+ lo->lo_encrypt_key_size) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+ ret = file->f_op->fallocate(file, mode, pos,
+ bio->bi_size);
+ if (unlikely(ret && ret != -EINVAL &&
+ ret != -EOPNOTSUPP))
+ ret = -EIO;
+ goto out;
+ }
+
ret = lo_send(lo, bio, pos);
if ((bio->bi_rw & REQ_FUA) && !ret) {
@@ -514,7 +473,7 @@ static struct bio *loop_get_bio(struct loop_device *lo)
return bio_list_pop(&lo->lo_bio_list);
}
-static int loop_make_request(struct request_queue *q, struct bio *old_bio)
+static void loop_make_request(struct request_queue *q, struct bio *old_bio)
{
struct loop_device *lo = q->queuedata;
int rw = bio_rw(old_bio);
@@ -532,12 +491,11 @@ static int loop_make_request(struct request_queue *q, struct bio *old_bio)
loop_add_bio(lo, old_bio);
wake_up(&lo->lo_event);
spin_unlock_irq(&lo->lo_lock);
- return 0;
+ return;
out:
spin_unlock_irq(&lo->lo_lock);
bio_io_error(old_bio);
- return 0;
}
struct switch_request {
@@ -700,7 +658,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
goto out_putf;
fput(old_file);
- if (max_part > 0)
+ if (lo->lo_flags & LO_FLAGS_PARTSCAN)
ioctl_by_bdev(bdev, BLKRRPART, 0);
return 0;
@@ -722,17 +680,10 @@ static inline int is_loop_device(struct file *file)
static ssize_t loop_attr_show(struct device *dev, char *page,
ssize_t (*callback)(struct loop_device *, char *))
{
- struct loop_device *l, *lo = NULL;
-
- mutex_lock(&loop_devices_mutex);
- list_for_each_entry(l, &loop_devices, lo_list)
- if (disk_to_dev(l->lo_disk) == dev) {
- lo = l;
- break;
- }
- mutex_unlock(&loop_devices_mutex);
+ struct gendisk *disk = dev_to_disk(dev);
+ struct loop_device *lo = disk->private_data;
- return lo ? callback(lo, page) : -EIO;
+ return callback(lo, page);
}
#define LOOP_ATTR_RO(_name) \
@@ -750,10 +701,10 @@ static ssize_t loop_attr_backing_file_show(struct loop_device *lo, char *buf)
ssize_t ret;
char *p = NULL;
- mutex_lock(&lo->lo_ctl_mutex);
+ spin_lock_irq(&lo->lo_lock);
if (lo->lo_backing_file)
p = d_path(&lo->lo_backing_file->f_path, buf, PAGE_SIZE - 1);
- mutex_unlock(&lo->lo_ctl_mutex);
+ spin_unlock_irq(&lo->lo_lock);
if (IS_ERR_OR_NULL(p))
ret = PTR_ERR(p);
@@ -784,16 +735,25 @@ static ssize_t loop_attr_autoclear_show(struct loop_device *lo, char *buf)
return sprintf(buf, "%s\n", autoclear ? "1" : "0");
}
+static ssize_t loop_attr_partscan_show(struct loop_device *lo, char *buf)
+{
+ int partscan = (lo->lo_flags & LO_FLAGS_PARTSCAN);
+
+ return sprintf(buf, "%s\n", partscan ? "1" : "0");
+}
+
LOOP_ATTR_RO(backing_file);
LOOP_ATTR_RO(offset);
LOOP_ATTR_RO(sizelimit);
LOOP_ATTR_RO(autoclear);
+LOOP_ATTR_RO(partscan);
static struct attribute *loop_attrs[] = {
&loop_attr_backing_file.attr,
&loop_attr_offset.attr,
&loop_attr_sizelimit.attr,
&loop_attr_autoclear.attr,
+ &loop_attr_partscan.attr,
NULL,
};
@@ -814,6 +774,35 @@ static void loop_sysfs_exit(struct loop_device *lo)
&loop_attribute_group);
}
+static void loop_config_discard(struct loop_device *lo)
+{
+ struct file *file = lo->lo_backing_file;
+ struct inode *inode = file->f_mapping->host;
+ struct request_queue *q = lo->lo_queue;
+
+ /*
+ * We use punch hole to reclaim the free space used by the
+ * image a.k.a. discard. However we do support discard if
+ * encryption is enabled, because it may give an attacker
+ * useful information.
+ */
+ if ((!file->f_op->fallocate) ||
+ lo->lo_encrypt_key_size) {
+ q->limits.discard_granularity = 0;
+ q->limits.discard_alignment = 0;
+ q->limits.max_discard_sectors = 0;
+ q->limits.discard_zeroes_data = 0;
+ queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
+ return;
+ }
+
+ q->limits.discard_granularity = inode->i_sb->s_blocksize;
+ q->limits.discard_alignment = inode->i_sb->s_blocksize;
+ q->limits.max_discard_sectors = UINT_MAX >> 9;
+ q->limits.discard_zeroes_data = 1;
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+}
+
static int loop_set_fd(struct loop_device *lo, fmode_t mode,
struct block_device *bdev, unsigned int arg)
{
@@ -856,35 +845,23 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
mapping = file->f_mapping;
inode = mapping->host;
- if (!(file->f_mode & FMODE_WRITE))
- lo_flags |= LO_FLAGS_READ_ONLY;
-
error = -EINVAL;
- if (S_ISREG(inode->i_mode) || S_ISBLK(inode->i_mode)) {
- const struct address_space_operations *aops = mapping->a_ops;
-
- if (aops->write_begin)
- lo_flags |= LO_FLAGS_USE_AOPS;
- if (!(lo_flags & LO_FLAGS_USE_AOPS) && !file->f_op->write)
- lo_flags |= LO_FLAGS_READ_ONLY;
+ if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))
+ goto out_putf;
- lo_blocksize = S_ISBLK(inode->i_mode) ?
- inode->i_bdev->bd_block_size : PAGE_SIZE;
+ if (!(file->f_mode & FMODE_WRITE) || !(mode & FMODE_WRITE) ||
+ !file->f_op->write)
+ lo_flags |= LO_FLAGS_READ_ONLY;
- error = 0;
- } else {
- goto out_putf;
- }
+ lo_blocksize = S_ISBLK(inode->i_mode) ?
+ inode->i_bdev->bd_block_size : PAGE_SIZE;
+ error = -EFBIG;
size = get_loop_size(lo, file);
-
- if ((loff_t)(sector_t)size != size) {
- error = -EFBIG;
+ if ((loff_t)(sector_t)size != size)
goto out_putf;
- }
- if (!(mode & FMODE_WRITE))
- lo_flags |= LO_FLAGS_READ_ONLY;
+ error = 0;
set_device_ro(bdev, (lo_flags & LO_FLAGS_READ_ONLY) != 0);
@@ -926,7 +903,9 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
}
lo->lo_state = Lo_bound;
wake_up_process(lo->lo_thread);
- if (max_part > 0)
+ if (part_shift)
+ lo->lo_flags |= LO_FLAGS_PARTSCAN;
+ if (lo->lo_flags & LO_FLAGS_PARTSCAN)
ioctl_by_bdev(bdev, BLKRRPART, 0);
return 0;
@@ -987,10 +966,11 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer,
return err;
}
-static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
+static int loop_clr_fd(struct loop_device *lo)
{
struct file *filp = lo->lo_backing_file;
gfp_t gfp = lo->old_gfp_mask;
+ struct block_device *bdev = lo->lo_device;
if (lo->lo_state != Lo_bound)
return -ENXIO;
@@ -1007,7 +987,9 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
kthread_stop(lo->lo_thread);
+ spin_lock_irq(&lo->lo_lock);
lo->lo_backing_file = NULL;
+ spin_unlock_irq(&lo->lo_lock);
loop_release_xfer(lo);
lo->transfer = NULL;
@@ -1017,7 +999,6 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
lo->lo_offset = 0;
lo->lo_sizelimit = 0;
lo->lo_encrypt_key_size = 0;
- lo->lo_flags = 0;
lo->lo_thread = NULL;
memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
memset(lo->lo_crypt_name, 0, LO_NAME_SIZE);
@@ -1035,8 +1016,11 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
lo->lo_state = Lo_unbound;
/* This is safe: open() is still holding a reference. */
module_put(THIS_MODULE);
- if (max_part > 0 && bdev)
+ if (lo->lo_flags & LO_FLAGS_PARTSCAN && bdev)
ioctl_by_bdev(bdev, BLKRRPART, 0);
+ lo->lo_flags = 0;
+ if (!part_shift)
+ lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN;
mutex_unlock(&lo->lo_ctl_mutex);
/*
* Need not hold lo_ctl_mutex to fput backing file.
@@ -1085,11 +1069,10 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
if (lo->lo_offset != info->lo_offset ||
lo->lo_sizelimit != info->lo_sizelimit) {
- lo->lo_offset = info->lo_offset;
- lo->lo_sizelimit = info->lo_sizelimit;
- if (figure_loop_size(lo))
+ if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit))
return -EFBIG;
}
+ loop_config_discard(lo);
memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE);
memcpy(lo->lo_crypt_name, info->lo_crypt_name, LO_NAME_SIZE);
@@ -1105,6 +1088,13 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
(info->lo_flags & LO_FLAGS_AUTOCLEAR))
lo->lo_flags ^= LO_FLAGS_AUTOCLEAR;
+ if ((info->lo_flags & LO_FLAGS_PARTSCAN) &&
+ !(lo->lo_flags & LO_FLAGS_PARTSCAN)) {
+ lo->lo_flags |= LO_FLAGS_PARTSCAN;
+ lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN;
+ ioctl_by_bdev(lo->lo_device, BLKRRPART, 0);
+ }
+
lo->lo_encrypt_key_size = info->lo_encrypt_key_size;
lo->lo_init[0] = info->lo_init[0];
lo->lo_init[1] = info->lo_init[1];
@@ -1265,7 +1255,7 @@ static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev)
err = -ENXIO;
if (unlikely(lo->lo_state != Lo_bound))
goto out;
- err = figure_loop_size(lo);
+ err = figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit);
if (unlikely(err))
goto out;
sec = get_capacity(lo->lo_disk);
@@ -1298,18 +1288,24 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
break;
case LOOP_CLR_FD:
/* loop_clr_fd would have unlocked lo_ctl_mutex on success */
- err = loop_clr_fd(lo, bdev);
+ err = loop_clr_fd(lo);
if (!err)
goto out_unlocked;
break;
case LOOP_SET_STATUS:
- err = loop_set_status_old(lo, (struct loop_info __user *) arg);
+ err = -EPERM;
+ if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
+ err = loop_set_status_old(lo,
+ (struct loop_info __user *)arg);
break;
case LOOP_GET_STATUS:
err = loop_get_status_old(lo, (struct loop_info __user *) arg);
break;
case LOOP_SET_STATUS64:
- err = loop_set_status64(lo, (struct loop_info64 __user *) arg);
+ err = -EPERM;
+ if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
+ err = loop_set_status64(lo,
+ (struct loop_info64 __user *) arg);
break;
case LOOP_GET_STATUS64:
err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
@@ -1485,13 +1481,22 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode,
static int lo_open(struct block_device *bdev, fmode_t mode)
{
- struct loop_device *lo = bdev->bd_disk->private_data;
+ struct loop_device *lo;
+ int err = 0;
+
+ mutex_lock(&loop_index_mutex);
+ lo = bdev->bd_disk->private_data;
+ if (!lo) {
+ err = -ENXIO;
+ goto out;
+ }
mutex_lock(&lo->lo_ctl_mutex);
lo->lo_refcnt++;
mutex_unlock(&lo->lo_ctl_mutex);
-
- return 0;
+out:
+ mutex_unlock(&loop_index_mutex);
+ return err;
}
static int lo_release(struct gendisk *disk, fmode_t mode)
@@ -1509,7 +1514,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
* In autoclear mode, stop the loop thread
* and remove configuration after last close.
*/
- err = loop_clr_fd(lo, NULL);
+ err = loop_clr_fd(lo);
if (!err)
goto out_unlocked;
} else {
@@ -1557,40 +1562,71 @@ int loop_register_transfer(struct loop_func_table *funcs)
return 0;
}
+static int unregister_transfer_cb(int id, void *ptr, void *data)
+{
+ struct loop_device *lo = ptr;
+ struct loop_func_table *xfer = data;
+
+ mutex_lock(&lo->lo_ctl_mutex);
+ if (lo->lo_encryption == xfer)
+ loop_release_xfer(lo);
+ mutex_unlock(&lo->lo_ctl_mutex);
+ return 0;
+}
+
int loop_unregister_transfer(int number)
{
unsigned int n = number;
- struct loop_device *lo;
struct loop_func_table *xfer;
if (n == 0 || n >= MAX_LO_CRYPT || (xfer = xfer_funcs[n]) == NULL)
return -EINVAL;
xfer_funcs[n] = NULL;
-
- list_for_each_entry(lo, &loop_devices, lo_list) {
- mutex_lock(&lo->lo_ctl_mutex);
-
- if (lo->lo_encryption == xfer)
- loop_release_xfer(lo);
-
- mutex_unlock(&lo->lo_ctl_mutex);
- }
-
+ idr_for_each(&loop_index_idr, &unregister_transfer_cb, xfer);
return 0;
}
EXPORT_SYMBOL(loop_register_transfer);
EXPORT_SYMBOL(loop_unregister_transfer);
-static struct loop_device *loop_alloc(int i)
+static int loop_add(struct loop_device **l, int i)
{
struct loop_device *lo;
struct gendisk *disk;
+ int err;
lo = kzalloc(sizeof(*lo), GFP_KERNEL);
- if (!lo)
+ if (!lo) {
+ err = -ENOMEM;
goto out;
+ }
+
+ err = idr_pre_get(&loop_index_idr, GFP_KERNEL);
+ if (err < 0)
+ goto out_free_dev;
+
+ if (i >= 0) {
+ int m;
+
+ /* create specific i in the index */
+ err = idr_get_new_above(&loop_index_idr, lo, i, &m);
+ if (err >= 0 && i != m) {
+ idr_remove(&loop_index_idr, m);
+ err = -EEXIST;
+ }
+ } else if (i == -1) {
+ int m;
+
+ /* get next free nr */
+ err = idr_get_new(&loop_index_idr, lo, &m);
+ if (err >= 0)
+ i = m;
+ } else {
+ err = -EINVAL;
+ }
+ if (err < 0)
+ goto out_free_dev;
lo->lo_queue = blk_alloc_queue(GFP_KERNEL);
if (!lo->lo_queue)
@@ -1600,6 +1636,27 @@ static struct loop_device *loop_alloc(int i)
if (!disk)
goto out_free_queue;
+ /*
+ * Disable partition scanning by default. The in-kernel partition
+ * scanning can be requested individually per-device during its
+ * setup. Userspace can always add and remove partitions from all
+ * devices. The needed partition minors are allocated from the
+ * extended minor space, the main loop device numbers will continue
+ * to match the loop minors, regardless of the number of partitions
+ * used.
+ *
+ * If max_part is given, partition scanning is globally enabled for
+ * all loop devices. The minors for the main loop devices will be
+ * multiples of max_part.
+ *
+ * Note: Global-for-all-devices, set-only-at-init, read-only module
+ * parameteters like 'max_loop' and 'max_part' make things needlessly
+ * complicated, are too static, inflexible and may surprise
+ * userspace tools. Parameters like this in general should be avoided.
+ */
+ if (!part_shift)
+ disk->flags |= GENHD_FL_NO_PART_SCAN;
+ disk->flags |= GENHD_FL_EXT_DEVT;
mutex_init(&lo->lo_ctl_mutex);
lo->lo_number = i;
lo->lo_thread = NULL;
@@ -1611,81 +1668,158 @@ static struct loop_device *loop_alloc(int i)
disk->private_data = lo;
disk->queue = lo->lo_queue;
sprintf(disk->disk_name, "loop%d", i);
- return lo;
+ add_disk(disk);
+ *l = lo;
+ return lo->lo_number;
out_free_queue:
blk_cleanup_queue(lo->lo_queue);
out_free_dev:
kfree(lo);
out:
- return NULL;
+ return err;
}
-static void loop_free(struct loop_device *lo)
+static void loop_remove(struct loop_device *lo)
{
+ del_gendisk(lo->lo_disk);
blk_cleanup_queue(lo->lo_queue);
put_disk(lo->lo_disk);
- list_del(&lo->lo_list);
kfree(lo);
}
-static struct loop_device *loop_init_one(int i)
+static int find_free_cb(int id, void *ptr, void *data)
+{
+ struct loop_device *lo = ptr;
+ struct loop_device **l = data;
+
+ if (lo->lo_state == Lo_unbound) {
+ *l = lo;
+ return 1;
+ }
+ return 0;
+}
+
+static int loop_lookup(struct loop_device **l, int i)
{
struct loop_device *lo;
+ int ret = -ENODEV;
- list_for_each_entry(lo, &loop_devices, lo_list) {
- if (lo->lo_number == i)
- return lo;
+ if (i < 0) {
+ int err;
+
+ err = idr_for_each(&loop_index_idr, &find_free_cb, &lo);
+ if (err == 1) {
+ *l = lo;
+ ret = lo->lo_number;
+ }
+ goto out;
}
- lo = loop_alloc(i);
+ /* lookup and return a specific i */
+ lo = idr_find(&loop_index_idr, i);
if (lo) {
- add_disk(lo->lo_disk);
- list_add_tail(&lo->lo_list, &loop_devices);
+ *l = lo;
+ ret = lo->lo_number;
}
- return lo;
-}
-
-static void loop_del_one(struct loop_device *lo)
-{
- del_gendisk(lo->lo_disk);
- loop_free(lo);
+out:
+ return ret;
}
static struct kobject *loop_probe(dev_t dev, int *part, void *data)
{
struct loop_device *lo;
struct kobject *kobj;
+ int err;
- mutex_lock(&loop_devices_mutex);
- lo = loop_init_one(MINOR(dev) >> part_shift);
- kobj = lo ? get_disk(lo->lo_disk) : ERR_PTR(-ENOMEM);
- mutex_unlock(&loop_devices_mutex);
+ mutex_lock(&loop_index_mutex);
+ err = loop_lookup(&lo, MINOR(dev) >> part_shift);
+ if (err < 0)
+ err = loop_add(&lo, MINOR(dev) >> part_shift);
+ if (err < 0)
+ kobj = ERR_PTR(err);
+ else
+ kobj = get_disk(lo->lo_disk);
+ mutex_unlock(&loop_index_mutex);
*part = 0;
return kobj;
}
+static long loop_control_ioctl(struct file *file, unsigned int cmd,
+ unsigned long parm)
+{
+ struct loop_device *lo;
+ int ret = -ENOSYS;
+
+ mutex_lock(&loop_index_mutex);
+ switch (cmd) {
+ case LOOP_CTL_ADD:
+ ret = loop_lookup(&lo, parm);
+ if (ret >= 0) {
+ ret = -EEXIST;
+ break;
+ }
+ ret = loop_add(&lo, parm);
+ break;
+ case LOOP_CTL_REMOVE:
+ ret = loop_lookup(&lo, parm);
+ if (ret < 0)
+ break;
+ mutex_lock(&lo->lo_ctl_mutex);
+ if (lo->lo_state != Lo_unbound) {
+ ret = -EBUSY;
+ mutex_unlock(&lo->lo_ctl_mutex);
+ break;
+ }
+ if (lo->lo_refcnt > 0) {
+ ret = -EBUSY;
+ mutex_unlock(&lo->lo_ctl_mutex);
+ break;
+ }
+ lo->lo_disk->private_data = NULL;
+ mutex_unlock(&lo->lo_ctl_mutex);
+ idr_remove(&loop_index_idr, lo->lo_number);
+ loop_remove(lo);
+ break;
+ case LOOP_CTL_GET_FREE:
+ ret = loop_lookup(&lo, -1);
+ if (ret >= 0)
+ break;
+ ret = loop_add(&lo, -1);
+ }
+ mutex_unlock(&loop_index_mutex);
+
+ return ret;
+}
+
+static const struct file_operations loop_ctl_fops = {
+ .open = nonseekable_open,
+ .unlocked_ioctl = loop_control_ioctl,
+ .compat_ioctl = loop_control_ioctl,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+};
+
+static struct miscdevice loop_misc = {
+ .minor = LOOP_CTRL_MINOR,
+ .name = "loop-control",
+ .fops = &loop_ctl_fops,
+};
+
+MODULE_ALIAS_MISCDEV(LOOP_CTRL_MINOR);
+MODULE_ALIAS("devname:loop-control");
+
static int __init loop_init(void)
{
int i, nr;
unsigned long range;
- struct loop_device *lo, *next;
+ struct loop_device *lo;
+ int err;
- /*
- * loop module now has a feature to instantiate underlying device
- * structure on-demand, provided that there is an access dev node.
- * However, this will not work well with user space tool that doesn't
- * know about such "feature". In order to not break any existing
- * tool, we do the following:
- *
- * (1) if max_loop is specified, create that many upfront, and this
- * also becomes a hard limit.
- * (2) if max_loop is not specified, create 8 loop device on module
- * load, user can further extend loop device by create dev node
- * themselves and have kernel automatically instantiate actual
- * device on-demand.
- */
+ err = misc_register(&loop_misc);
+ if (err < 0)
+ return err;
part_shift = 0;
if (max_part > 0) {
@@ -1708,57 +1842,60 @@ static int __init loop_init(void)
if (max_loop > 1UL << (MINORBITS - part_shift))
return -EINVAL;
+ /*
+ * If max_loop is specified, create that many devices upfront.
+ * This also becomes a hard limit. If max_loop is not specified,
+ * create CONFIG_BLK_DEV_LOOP_MIN_COUNT loop devices at module
+ * init time. Loop devices can be requested on-demand with the
+ * /dev/loop-control interface, or be instantiated by accessing
+ * a 'dead' device node.
+ */
if (max_loop) {
nr = max_loop;
range = max_loop << part_shift;
} else {
- nr = 8;
+ nr = CONFIG_BLK_DEV_LOOP_MIN_COUNT;
range = 1UL << MINORBITS;
}
if (register_blkdev(LOOP_MAJOR, "loop"))
return -EIO;
- for (i = 0; i < nr; i++) {
- lo = loop_alloc(i);
- if (!lo)
- goto Enomem;
- list_add_tail(&lo->lo_list, &loop_devices);
- }
-
- /* point of no return */
-
- list_for_each_entry(lo, &loop_devices, lo_list)
- add_disk(lo->lo_disk);
-
blk_register_region(MKDEV(LOOP_MAJOR, 0), range,
THIS_MODULE, loop_probe, NULL, NULL);
+ /* pre-create number of devices given by config or max_loop */
+ mutex_lock(&loop_index_mutex);
+ for (i = 0; i < nr; i++)
+ loop_add(&lo, i);
+ mutex_unlock(&loop_index_mutex);
+
printk(KERN_INFO "loop: module loaded\n");
return 0;
+}
-Enomem:
- printk(KERN_INFO "loop: out of memory\n");
-
- list_for_each_entry_safe(lo, next, &loop_devices, lo_list)
- loop_free(lo);
+static int loop_exit_cb(int id, void *ptr, void *data)
+{
+ struct loop_device *lo = ptr;
- unregister_blkdev(LOOP_MAJOR, "loop");
- return -ENOMEM;
+ loop_remove(lo);
+ return 0;
}
static void __exit loop_exit(void)
{
unsigned long range;
- struct loop_device *lo, *next;
range = max_loop ? max_loop << part_shift : 1UL << MINORBITS;
- list_for_each_entry_safe(lo, next, &loop_devices, lo_list)
- loop_del_one(lo);
+ idr_for_each(&loop_index_idr, &loop_exit_cb, NULL);
+ idr_remove_all(&loop_index_idr);
+ idr_destroy(&loop_index_idr);
blk_unregister_region(MKDEV(LOOP_MAJOR, 0), range);
unregister_blkdev(LOOP_MAJOR, "loop");
+
+ misc_deregister(&loop_misc);
}
module_init(loop_init);
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index f533f3375e24..c3f0ee16594d 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -127,8 +127,7 @@ static void sock_shutdown(struct nbd_device *lo, int lock)
if (lock)
mutex_lock(&lo->tx_lock);
if (lo->sock) {
- printk(KERN_WARNING "%s: shutting down socket\n",
- lo->disk->disk_name);
+ dev_warn(disk_to_dev(lo->disk), "shutting down socket\n");
kernel_sock_shutdown(lo->sock, SHUT_RDWR);
lo->sock = NULL;
}
@@ -158,8 +157,9 @@ static int sock_xmit(struct nbd_device *lo, int send, void *buf, int size,
sigset_t blocked, oldset;
if (unlikely(!sock)) {
- printk(KERN_ERR "%s: Attempted %s on closed socket in sock_xmit\n",
- lo->disk->disk_name, (send ? "send" : "recv"));
+ dev_err(disk_to_dev(lo->disk),
+ "Attempted %s on closed socket in sock_xmit\n",
+ (send ? "send" : "recv"));
return -EINVAL;
}
@@ -250,8 +250,8 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req)
result = sock_xmit(lo, 1, &request, sizeof(request),
(nbd_cmd(req) == NBD_CMD_WRITE) ? MSG_MORE : 0);
if (result <= 0) {
- printk(KERN_ERR "%s: Send control failed (result %d)\n",
- lo->disk->disk_name, result);
+ dev_err(disk_to_dev(lo->disk),
+ "Send control failed (result %d)\n", result);
goto error_out;
}
@@ -270,8 +270,9 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req)
lo->disk->disk_name, req, bvec->bv_len);
result = sock_send_bvec(lo, bvec, flags);
if (result <= 0) {
- printk(KERN_ERR "%s: Send data failed (result %d)\n",
- lo->disk->disk_name, result);
+ dev_err(disk_to_dev(lo->disk),
+ "Send data failed (result %d)\n",
+ result);
goto error_out;
}
}
@@ -328,14 +329,13 @@ static struct request *nbd_read_stat(struct nbd_device *lo)
reply.magic = 0;
result = sock_xmit(lo, 0, &reply, sizeof(reply), MSG_WAITALL);
if (result <= 0) {
- printk(KERN_ERR "%s: Receive control failed (result %d)\n",
- lo->disk->disk_name, result);
+ dev_err(disk_to_dev(lo->disk),
+ "Receive control failed (result %d)\n", result);
goto harderror;
}
if (ntohl(reply.magic) != NBD_REPLY_MAGIC) {
- printk(KERN_ERR "%s: Wrong magic (0x%lx)\n",
- lo->disk->disk_name,
+ dev_err(disk_to_dev(lo->disk), "Wrong magic (0x%lx)\n",
(unsigned long)ntohl(reply.magic));
result = -EPROTO;
goto harderror;
@@ -347,15 +347,15 @@ static struct request *nbd_read_stat(struct nbd_device *lo)
if (result != -ENOENT)
goto harderror;
- printk(KERN_ERR "%s: Unexpected reply (%p)\n",
- lo->disk->disk_name, reply.handle);
+ dev_err(disk_to_dev(lo->disk), "Unexpected reply (%p)\n",
+ reply.handle);
result = -EBADR;
goto harderror;
}
if (ntohl(reply.error)) {
- printk(KERN_ERR "%s: Other side returned error (%d)\n",
- lo->disk->disk_name, ntohl(reply.error));
+ dev_err(disk_to_dev(lo->disk), "Other side returned error (%d)\n",
+ ntohl(reply.error));
req->errors++;
return req;
}
@@ -369,8 +369,8 @@ static struct request *nbd_read_stat(struct nbd_device *lo)
rq_for_each_segment(bvec, req, iter) {
result = sock_recv_bvec(lo, bvec);
if (result <= 0) {
- printk(KERN_ERR "%s: Receive data failed (result %d)\n",
- lo->disk->disk_name, result);
+ dev_err(disk_to_dev(lo->disk), "Receive data failed (result %d)\n",
+ result);
req->errors++;
return req;
}
@@ -405,10 +405,10 @@ static int nbd_do_it(struct nbd_device *lo)
BUG_ON(lo->magic != LO_MAGIC);
- lo->pid = current->pid;
- ret = sysfs_create_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr);
+ lo->pid = task_pid_nr(current);
+ ret = device_create_file(disk_to_dev(lo->disk), &pid_attr);
if (ret) {
- printk(KERN_ERR "nbd: sysfs_create_file failed!");
+ dev_err(disk_to_dev(lo->disk), "device_create_file failed!\n");
lo->pid = 0;
return ret;
}
@@ -416,7 +416,7 @@ static int nbd_do_it(struct nbd_device *lo)
while ((req = nbd_read_stat(lo)) != NULL)
nbd_end_request(req);
- sysfs_remove_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr);
+ device_remove_file(disk_to_dev(lo->disk), &pid_attr);
lo->pid = 0;
return 0;
}
@@ -457,8 +457,8 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req)
if (rq_data_dir(req) == WRITE) {
nbd_cmd(req) = NBD_CMD_WRITE;
if (lo->flags & NBD_READ_ONLY) {
- printk(KERN_ERR "%s: Write on read-only\n",
- lo->disk->disk_name);
+ dev_err(disk_to_dev(lo->disk),
+ "Write on read-only\n");
goto error_out;
}
}
@@ -468,16 +468,15 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req)
mutex_lock(&lo->tx_lock);
if (unlikely(!lo->sock)) {
mutex_unlock(&lo->tx_lock);
- printk(KERN_ERR "%s: Attempted send on closed socket\n",
- lo->disk->disk_name);
+ dev_err(disk_to_dev(lo->disk),
+ "Attempted send on closed socket\n");
goto error_out;
}
lo->active_req = req;
if (nbd_send_req(lo, req) != 0) {
- printk(KERN_ERR "%s: Request send failed\n",
- lo->disk->disk_name);
+ dev_err(disk_to_dev(lo->disk), "Request send failed\n");
req->errors++;
nbd_end_request(req);
} else {
@@ -549,8 +548,8 @@ static void do_nbd_request(struct request_queue *q)
BUG_ON(lo->magic != LO_MAGIC);
if (unlikely(!lo->sock)) {
- printk(KERN_ERR "%s: Attempted send on closed socket\n",
- lo->disk->disk_name);
+ dev_err(disk_to_dev(lo->disk),
+ "Attempted send on closed socket\n");
req->errors++;
nbd_end_request(req);
spin_lock_irq(q->queue_lock);
@@ -576,7 +575,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
case NBD_DISCONNECT: {
struct request sreq;
- printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name);
+ dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n");
blk_rq_init(NULL, &sreq);
sreq.cmd_type = REQ_TYPE_SPECIAL;
@@ -674,7 +673,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
file = lo->file;
lo->file = NULL;
nbd_clear_que(lo);
- printk(KERN_WARNING "%s: queue cleared\n", lo->disk->disk_name);
+ dev_warn(disk_to_dev(lo->disk), "queue cleared\n");
if (file)
fput(file);
lo->bytesize = 0;
@@ -694,8 +693,8 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
return 0;
case NBD_PRINT_DEBUG:
- printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n",
- bdev->bd_disk->disk_name,
+ dev_info(disk_to_dev(lo->disk),
+ "next = %p, prev = %p, head = %p\n",
lo->queue_head.next, lo->queue_head.prev,
&lo->queue_head);
return 0;
@@ -745,7 +744,7 @@ static int __init nbd_init(void)
BUILD_BUG_ON(sizeof(struct nbd_request) != 28);
if (max_part < 0) {
- printk(KERN_CRIT "nbd: max_part must be >= 0\n");
+ printk(KERN_ERR "nbd: max_part must be >= 0\n");
return -EINVAL;
}
diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c
index 6b9a2000d56a..a79fb4f7ff62 100644
--- a/drivers/block/paride/pg.c
+++ b/drivers/block/paride/pg.c
@@ -630,6 +630,7 @@ static ssize_t pg_read(struct file *filp, char __user *buf, size_t count, loff_t
if (dev->status & 0x10)
return -ETIME;
+ memset(&hdr, 0, sizeof(hdr));
hdr.magic = PG_MAGIC;
hdr.dlen = dev->dlen;
copy = 0;
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 07a382eaf0a8..a63b0a2b7805 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -1206,7 +1206,7 @@ static int pkt_start_recovery(struct packet_data *pkt)
if (!sb)
return 0;
- if (!sb->s_op || !sb->s_op->relocate_blocks)
+ if (!sb->s_op->relocate_blocks)
goto out;
old_block = pkt->sector / (CD_FRAMESIZE >> 9);
@@ -2444,7 +2444,7 @@ static void pkt_end_io_read_cloned(struct bio *bio, int err)
pkt_bio_finished(pd);
}
-static int pkt_make_request(struct request_queue *q, struct bio *bio)
+static void pkt_make_request(struct request_queue *q, struct bio *bio)
{
struct pktcdvd_device *pd;
char b[BDEVNAME_SIZE];
@@ -2473,7 +2473,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio)
cloned_bio->bi_end_io = pkt_end_io_read_cloned;
pd->stats.secs_r += bio->bi_size >> 9;
pkt_queue_bio(pd, cloned_bio);
- return 0;
+ return;
}
if (!test_bit(PACKET_WRITABLE, &pd->flags)) {
@@ -2509,7 +2509,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio)
pkt_make_request(q, &bp->bio1);
pkt_make_request(q, &bp->bio2);
bio_pair_release(bp);
- return 0;
+ return;
}
}
@@ -2533,7 +2533,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio)
}
spin_unlock(&pkt->lock);
spin_unlock(&pd->cdrw.active_list_lock);
- return 0;
+ return;
} else {
blocked_bio = 1;
}
@@ -2584,10 +2584,9 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio)
*/
wake_up(&pd->wqueue);
}
- return 0;
+ return;
end_io:
bio_io_error(bio);
- return 0;
}
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 8e1ce2e2916a..da0abc1838c1 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -21,6 +21,7 @@
#include <linux/ata.h>
#include <linux/blkdev.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <asm/lv1call.h>
#include <asm/ps3stor.h>
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index b3bdb8af89cf..f58cdcfb305f 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -10,6 +10,7 @@
#include <linux/blkdev.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
@@ -596,7 +597,7 @@ out:
return next;
}
-static int ps3vram_make_request(struct request_queue *q, struct bio *bio)
+static void ps3vram_make_request(struct request_queue *q, struct bio *bio)
{
struct ps3_system_bus_device *dev = q->queuedata;
struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
@@ -610,13 +611,11 @@ static int ps3vram_make_request(struct request_queue *q, struct bio *bio)
spin_unlock_irq(&priv->lock);
if (busy)
- return 0;
+ return;
do {
bio = ps3vram_do_bio(dev, bio);
} while (bio);
-
- return 0;
}
static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 1278098624e6..65cc424359b0 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -192,7 +192,7 @@ static ssize_t rbd_snap_add(struct device *dev,
const char *buf,
size_t count);
static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev,
- struct rbd_snap *snap);;
+ struct rbd_snap *snap);
static struct rbd_device *dev_to_rbd(struct device *dev)
@@ -260,7 +260,7 @@ static struct rbd_client *rbd_client_create(struct ceph_options *opt,
kref_init(&rbdc->kref);
INIT_LIST_HEAD(&rbdc->node);
- rbdc->client = ceph_create_client(opt, rbdc);
+ rbdc->client = ceph_create_client(opt, rbdc, 0, 0);
if (IS_ERR(rbdc->client))
goto out_rbdc;
opt = NULL; /* Now rbdc->client is responsible for opt */
@@ -630,6 +630,14 @@ static int rbd_get_num_segments(struct rbd_image_header *header,
}
/*
+ * returns the size of an object in the image
+ */
+static u64 rbd_obj_bytes(struct rbd_image_header *header)
+{
+ return 1 << header->obj_order;
+}
+
+/*
* bio helpers
*/
@@ -1253,6 +1261,35 @@ fail:
return ret;
}
+/*
+ * Request sync osd unwatch
+ */
+static int rbd_req_sync_unwatch(struct rbd_device *dev,
+ const char *obj)
+{
+ struct ceph_osd_req_op *ops;
+
+ int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0);
+ if (ret < 0)
+ return ret;
+
+ ops[0].watch.ver = 0;
+ ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie);
+ ops[0].watch.flag = 0;
+
+ ret = rbd_req_sync_op(dev, NULL,
+ CEPH_NOSNAP,
+ 0,
+ CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
+ ops,
+ 1, obj, 0, 0, NULL, NULL, NULL);
+
+ rbd_destroy_ops(ops);
+ ceph_osdc_cancel_event(dev->watch_event);
+ dev->watch_event = NULL;
+ return ret;
+}
+
struct rbd_notify_info {
struct rbd_device *dev;
};
@@ -1736,6 +1773,13 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
q = blk_init_queue(rbd_rq_fn, &rbd_dev->lock);
if (!q)
goto out_disk;
+
+ /* set io sizes to object size */
+ blk_queue_max_hw_sectors(q, rbd_obj_bytes(&rbd_dev->header) / 512ULL);
+ blk_queue_max_segment_size(q, rbd_obj_bytes(&rbd_dev->header));
+ blk_queue_io_min(q, rbd_obj_bytes(&rbd_dev->header));
+ blk_queue_io_opt(q, rbd_obj_bytes(&rbd_dev->header));
+
blk_queue_merge_bvec(q, rbd_merge_bvec);
disk->queue = q;
@@ -2290,7 +2334,7 @@ static void rbd_dev_release(struct device *dev)
ceph_osdc_unregister_linger_request(&rbd_dev->client->osdc,
rbd_dev->watch_request);
if (rbd_dev->watch_event)
- ceph_osdc_cancel_event(rbd_dev->watch_event);
+ rbd_req_sync_unwatch(rbd_dev, rbd_dev->obj_md_name);
rbd_put_client(rbd_dev);
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index 773bfa792777..ae3e167e17ad 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -1184,6 +1184,7 @@ static struct of_device_id swim3_match[] =
{
.compatible = "swim3"
},
+ { /* end of list */ }
};
static struct macio_driver swim3_driver =
diff --git a/drivers/block/umem.c b/drivers/block/umem.c
index 031ca720d926..aa2712060bfb 100644
--- a/drivers/block/umem.c
+++ b/drivers/block/umem.c
@@ -513,7 +513,7 @@ static void process_page(unsigned long data)
}
}
-static int mm_make_request(struct request_queue *q, struct bio *bio)
+static void mm_make_request(struct request_queue *q, struct bio *bio)
{
struct cardinfo *card = q->queuedata;
pr_debug("mm_make_request %llu %u\n",
@@ -525,7 +525,7 @@ static int mm_make_request(struct request_queue *q, struct bio *bio)
card->biotail = &bio->bi_next;
spin_unlock_irq(&card->lock);
- return 0;
+ return;
}
static irqreturn_t mm_interrupt(int irq, void *__card)
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 079c08808d8a..4d0b70adf5f7 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -3,15 +3,19 @@
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
+#include <linux/module.h>
#include <linux/virtio.h>
#include <linux/virtio_blk.h>
#include <linux/scatterlist.h>
#include <linux/string_helpers.h>
#include <scsi/scsi_cmnd.h>
+#include <linux/idr.h>
#define PART_BITS 4
-static int major, index;
+static int major;
+static DEFINE_IDA(vd_index_ida);
+
struct workqueue_struct *virtblk_wq;
struct virtio_blk
@@ -35,6 +39,9 @@ struct virtio_blk
/* What host tells us, plus 2 for header & tailer. */
unsigned int sg_elems;
+ /* Ida index - used to track minor number allocations. */
+ int index;
+
/* Scatterlist: can be too big for stack. */
struct scatterlist sg[/*sg_elems*/];
};
@@ -276,6 +283,11 @@ static int index_to_minor(int index)
return index << PART_BITS;
}
+static int minor_to_index(int minor)
+{
+ return minor >> PART_BITS;
+}
+
static ssize_t virtblk_serial_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -341,14 +353,17 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
{
struct virtio_blk *vblk;
struct request_queue *q;
- int err;
+ int err, index;
u64 cap;
u32 v, blk_size, sg_elems, opt_io_size;
u16 min_io_size;
u8 physical_block_exp, alignment_offset;
- if (index_to_minor(index) >= 1 << MINORBITS)
- return -ENOSPC;
+ err = ida_simple_get(&vd_index_ida, 0, minor_to_index(1 << MINORBITS),
+ GFP_KERNEL);
+ if (err < 0)
+ goto out;
+ index = err;
/* We need to know how many segments before we allocate. */
err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
@@ -365,7 +380,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
sizeof(vblk->sg[0]) * sg_elems, GFP_KERNEL);
if (!vblk) {
err = -ENOMEM;
- goto out;
+ goto out_free_index;
}
INIT_LIST_HEAD(&vblk->reqs);
@@ -421,7 +436,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
vblk->disk->private_data = vblk;
vblk->disk->fops = &virtblk_fops;
vblk->disk->driverfs_dev = &vdev->dev;
- index++;
+ vblk->index = index;
/* configure queue flush support */
if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH))
@@ -516,6 +531,8 @@ out_free_vq:
vdev->config->del_vqs(vdev);
out_free_vblk:
kfree(vblk);
+out_free_index:
+ ida_simple_remove(&vd_index_ida, index);
out:
return err;
}
@@ -523,6 +540,7 @@ out:
static void __devexit virtblk_remove(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
+ int index = vblk->index;
flush_work(&vblk->config_work);
@@ -538,6 +556,7 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
mempool_destroy(vblk->pool);
vdev->config->del_vqs(vdev);
kfree(vblk);
+ ida_simple_remove(&vd_index_ida, index);
}
static const struct virtio_device_id id_table[] = {
diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c
index 5cf2993a8338..15ec4db194d1 100644
--- a/drivers/block/xen-blkback/blkback.c
+++ b/drivers/block/xen-blkback/blkback.c
@@ -39,6 +39,9 @@
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/freezer.h>
+#include <linux/loop.h>
+#include <linux/falloc.h>
+#include <linux/fs.h>
#include <xen/events.h>
#include <xen/page.h>
@@ -258,13 +261,16 @@ irqreturn_t xen_blkif_be_int(int irq, void *dev_id)
static void print_stats(struct xen_blkif *blkif)
{
- pr_info("xen-blkback (%s): oo %3d | rd %4d | wr %4d | f %4d\n",
+ pr_info("xen-blkback (%s): oo %3d | rd %4d | wr %4d | f %4d"
+ " | ds %4d\n",
current->comm, blkif->st_oo_req,
- blkif->st_rd_req, blkif->st_wr_req, blkif->st_f_req);
+ blkif->st_rd_req, blkif->st_wr_req,
+ blkif->st_f_req, blkif->st_ds_req);
blkif->st_print = jiffies + msecs_to_jiffies(10 * 1000);
blkif->st_rd_req = 0;
blkif->st_wr_req = 0;
blkif->st_oo_req = 0;
+ blkif->st_ds_req = 0;
}
int xen_blkif_schedule(void *arg)
@@ -396,7 +402,7 @@ static int xen_blkbk_map(struct blkif_request *req,
continue;
ret = m2p_add_override(PFN_DOWN(map[i].dev_bus_addr),
- blkbk->pending_page(pending_req, i), false);
+ blkbk->pending_page(pending_req, i), NULL);
if (ret) {
pr_alert(DRV_PFX "Failed to install M2P override for %lx (ret: %d)\n",
(unsigned long)map[i].dev_bus_addr, ret);
@@ -410,6 +416,59 @@ static int xen_blkbk_map(struct blkif_request *req,
return ret;
}
+static void xen_blk_discard(struct xen_blkif *blkif, struct blkif_request *req)
+{
+ int err = 0;
+ int status = BLKIF_RSP_OKAY;
+ struct block_device *bdev = blkif->vbd.bdev;
+
+ if (blkif->blk_backend_type == BLKIF_BACKEND_PHY)
+ /* just forward the discard request */
+ err = blkdev_issue_discard(bdev,
+ req->u.discard.sector_number,
+ req->u.discard.nr_sectors,
+ GFP_KERNEL, 0);
+ else if (blkif->blk_backend_type == BLKIF_BACKEND_FILE) {
+ /* punch a hole in the backing file */
+ struct loop_device *lo = bdev->bd_disk->private_data;
+ struct file *file = lo->lo_backing_file;
+
+ if (file->f_op->fallocate)
+ err = file->f_op->fallocate(file,
+ FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
+ req->u.discard.sector_number << 9,
+ req->u.discard.nr_sectors << 9);
+ else
+ err = -EOPNOTSUPP;
+ } else
+ err = -EOPNOTSUPP;
+
+ if (err == -EOPNOTSUPP) {
+ pr_debug(DRV_PFX "discard op failed, not supported\n");
+ status = BLKIF_RSP_EOPNOTSUPP;
+ } else if (err)
+ status = BLKIF_RSP_ERROR;
+
+ make_response(blkif, req->id, req->operation, status);
+}
+
+static void xen_blk_drain_io(struct xen_blkif *blkif)
+{
+ atomic_set(&blkif->drain, 1);
+ do {
+ /* The initial value is one, and one refcnt taken at the
+ * start of the xen_blkif_schedule thread. */
+ if (atomic_read(&blkif->refcnt) <= 2)
+ break;
+ wait_for_completion_interruptible_timeout(
+ &blkif->drain_complete, HZ);
+
+ if (!atomic_read(&blkif->drain))
+ break;
+ } while (!kthread_should_stop());
+ atomic_set(&blkif->drain, 0);
+}
+
/*
* Completion callback on the bio's. Called as bh->b_end_io()
*/
@@ -422,6 +481,11 @@ static void __end_block_io_op(struct pending_req *pending_req, int error)
pr_debug(DRV_PFX "flush diskcache op failed, not supported\n");
xen_blkbk_flush_diskcache(XBT_NIL, pending_req->blkif->be, 0);
pending_req->status = BLKIF_RSP_EOPNOTSUPP;
+ } else if ((pending_req->operation == BLKIF_OP_WRITE_BARRIER) &&
+ (error == -EOPNOTSUPP)) {
+ pr_debug(DRV_PFX "write barrier op failed, not supported\n");
+ xen_blkbk_barrier(XBT_NIL, pending_req->blkif->be, 0);
+ pending_req->status = BLKIF_RSP_EOPNOTSUPP;
} else if (error) {
pr_debug(DRV_PFX "Buffer not up-to-date at end of operation,"
" error=%d\n", error);
@@ -438,6 +502,10 @@ static void __end_block_io_op(struct pending_req *pending_req, int error)
make_response(pending_req->blkif, pending_req->id,
pending_req->operation, pending_req->status);
xen_blkif_put(pending_req->blkif);
+ if (atomic_read(&pending_req->blkif->refcnt) <= 2) {
+ if (atomic_read(&pending_req->blkif->drain))
+ complete(&pending_req->blkif->drain_complete);
+ }
free_req(pending_req);
}
}
@@ -458,7 +526,8 @@ static void end_block_io_op(struct bio *bio, int error)
* (which has the sectors we want, number of them, grant references, etc),
* and transmute it to the block API to hand it over to the proper block disk.
*/
-static int do_block_io_op(struct xen_blkif *blkif)
+static int
+__do_block_io_op(struct xen_blkif *blkif)
{
union blkif_back_rings *blk_rings = &blkif->blk_rings;
struct blkif_request req;
@@ -515,6 +584,22 @@ static int do_block_io_op(struct xen_blkif *blkif)
return more_to_do;
}
+static int
+do_block_io_op(struct xen_blkif *blkif)
+{
+ union blkif_back_rings *blk_rings = &blkif->blk_rings;
+ int more_to_do;
+
+ do {
+ more_to_do = __do_block_io_op(blkif);
+ if (more_to_do)
+ break;
+
+ RING_FINAL_CHECK_FOR_REQUESTS(&blk_rings->common, more_to_do);
+ } while (more_to_do);
+
+ return more_to_do;
+}
/*
* Transmutation of the 'struct blkif_request' to a proper 'struct bio'
* and call the 'submit_bio' to pass it to the underlying storage.
@@ -531,6 +616,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
int i, nbio = 0;
int operation;
struct blk_plug plug;
+ bool drain = false;
switch (req->operation) {
case BLKIF_OP_READ:
@@ -541,11 +627,16 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
blkif->st_wr_req++;
operation = WRITE_ODIRECT;
break;
+ case BLKIF_OP_WRITE_BARRIER:
+ drain = true;
case BLKIF_OP_FLUSH_DISKCACHE:
blkif->st_f_req++;
operation = WRITE_FLUSH;
break;
- case BLKIF_OP_WRITE_BARRIER:
+ case BLKIF_OP_DISCARD:
+ blkif->st_ds_req++;
+ operation = REQ_DISCARD;
+ break;
default:
operation = 0; /* make gcc happy */
goto fail_response;
@@ -554,7 +645,8 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
/* Check that the number of segments is sane. */
nseg = req->nr_segments;
- if (unlikely(nseg == 0 && operation != WRITE_FLUSH) ||
+ if (unlikely(nseg == 0 && operation != WRITE_FLUSH &&
+ operation != REQ_DISCARD) ||
unlikely(nseg > BLKIF_MAX_SEGMENTS_PER_REQUEST)) {
pr_debug(DRV_PFX "Bad number of segments in request (%d)\n",
nseg);
@@ -603,16 +695,25 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
}
}
+ /* Wait on all outstanding I/O's and once that has been completed
+ * issue the WRITE_FLUSH.
+ */
+ if (drain)
+ xen_blk_drain_io(pending_req->blkif);
+
/*
* If we have failed at this point, we need to undo the M2P override,
* set gnttab_set_unmap_op on all of the grant references and perform
* the hypercall to unmap the grants - that is all done in
* xen_blkbk_unmap.
*/
- if (xen_blkbk_map(req, pending_req, seg))
+ if (operation != REQ_DISCARD && xen_blkbk_map(req, pending_req, seg))
goto fail_flush;
- /* This corresponding xen_blkif_put is done in __end_block_io_op */
+ /*
+ * This corresponding xen_blkif_put is done in __end_block_io_op, or
+ * below (in "!bio") if we are handling a BLKIF_OP_DISCARD.
+ */
xen_blkif_get(blkif);
for (i = 0; i < nseg; i++) {
@@ -636,18 +737,25 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
preq.sector_number += seg[i].nsec;
}
- /* This will be hit if the operation was a flush. */
+ /* This will be hit if the operation was a flush or discard. */
if (!bio) {
- BUG_ON(operation != WRITE_FLUSH);
+ BUG_ON(operation != WRITE_FLUSH && operation != REQ_DISCARD);
- bio = bio_alloc(GFP_KERNEL, 0);
- if (unlikely(bio == NULL))
- goto fail_put_bio;
+ if (operation == WRITE_FLUSH) {
+ bio = bio_alloc(GFP_KERNEL, 0);
+ if (unlikely(bio == NULL))
+ goto fail_put_bio;
- biolist[nbio++] = bio;
- bio->bi_bdev = preq.bdev;
- bio->bi_private = pending_req;
- bio->bi_end_io = end_block_io_op;
+ biolist[nbio++] = bio;
+ bio->bi_bdev = preq.bdev;
+ bio->bi_private = pending_req;
+ bio->bi_end_io = end_block_io_op;
+ } else if (operation == REQ_DISCARD) {
+ xen_blk_discard(blkif, req);
+ xen_blkif_put(blkif);
+ free_req(pending_req);
+ return 0;
+ }
}
/*
@@ -667,7 +775,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
if (operation == READ)
blkif->st_rd_sect += preq.nr_sects;
- else if (operation == WRITE || operation == WRITE_FLUSH)
+ else if (operation & WRITE)
blkif->st_wr_sect += preq.nr_sects;
return 0;
@@ -700,7 +808,6 @@ static void make_response(struct xen_blkif *blkif, u64 id,
struct blkif_response resp;
unsigned long flags;
union blkif_back_rings *blk_rings = &blkif->blk_rings;
- int more_to_do = 0;
int notify;
resp.id = id;
@@ -727,22 +834,7 @@ static void make_response(struct xen_blkif *blkif, u64 id,
}
blk_rings->common.rsp_prod_pvt++;
RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blk_rings->common, notify);
- if (blk_rings->common.rsp_prod_pvt == blk_rings->common.req_cons) {
- /*
- * Tail check for pending requests. Allows frontend to avoid
- * notifications if requests are already in flight (lower
- * overheads and promotes batching).
- */
- RING_FINAL_CHECK_FOR_REQUESTS(&blk_rings->common, more_to_do);
-
- } else if (RING_HAS_UNCONSUMED_REQUESTS(&blk_rings->common)) {
- more_to_do = 1;
- }
-
spin_unlock_irqrestore(&blkif->blk_ring_lock, flags);
-
- if (more_to_do)
- blkif_notify_work(blkif);
if (notify)
notify_remote_via_irq(blkif->irq);
}
@@ -763,9 +855,9 @@ static int __init xen_blkif_init(void)
mmap_pages = xen_blkif_reqs * BLKIF_MAX_SEGMENTS_PER_REQUEST;
- blkbk->pending_reqs = kmalloc(sizeof(blkbk->pending_reqs[0]) *
+ blkbk->pending_reqs = kzalloc(sizeof(blkbk->pending_reqs[0]) *
xen_blkif_reqs, GFP_KERNEL);
- blkbk->pending_grant_handles = kzalloc(sizeof(blkbk->pending_grant_handles[0]) *
+ blkbk->pending_grant_handles = kmalloc(sizeof(blkbk->pending_grant_handles[0]) *
mmap_pages, GFP_KERNEL);
blkbk->pending_pages = kzalloc(sizeof(blkbk->pending_pages[0]) *
mmap_pages, GFP_KERNEL);
@@ -788,8 +880,6 @@ static int __init xen_blkif_init(void)
if (rc)
goto failed_init;
- memset(blkbk->pending_reqs, 0, sizeof(blkbk->pending_reqs));
-
INIT_LIST_HEAD(&blkbk->pending_free);
spin_lock_init(&blkbk->pending_free_lock);
init_waitqueue_head(&blkbk->pending_free_wq);
@@ -824,3 +914,4 @@ static int __init xen_blkif_init(void)
module_init(xen_blkif_init);
MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("xen-backend:vbd");
diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h
index 9e40b283a468..dfb1b3a43a5d 100644
--- a/drivers/block/xen-blkback/common.h
+++ b/drivers/block/xen-blkback/common.h
@@ -27,7 +27,6 @@
#ifndef __XEN_BLKIF__BACKEND__COMMON_H__
#define __XEN_BLKIF__BACKEND__COMMON_H__
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
@@ -46,7 +45,7 @@
#define DRV_PFX "xen-blkback:"
#define DPRINTK(fmt, args...) \
- pr_debug(DRV_PFX "(%s:%d) " fmt ".\n", \
+ pr_debug(DRV_PFX "(%s:%d) " fmt ".\n", \
__func__, __LINE__, ##args)
@@ -63,13 +62,26 @@ struct blkif_common_response {
/* i386 protocol version */
#pragma pack(push, 4)
+
+struct blkif_x86_32_request_rw {
+ blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
+ struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+};
+
+struct blkif_x86_32_request_discard {
+ blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
+ uint64_t nr_sectors;
+};
+
struct blkif_x86_32_request {
uint8_t operation; /* BLKIF_OP_??? */
uint8_t nr_segments; /* number of segments */
blkif_vdev_t handle; /* only for read/write requests */
uint64_t id; /* private guest value, echoed in resp */
- blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
- struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+ union {
+ struct blkif_x86_32_request_rw rw;
+ struct blkif_x86_32_request_discard discard;
+ } u;
};
struct blkif_x86_32_response {
uint64_t id; /* copied from request */
@@ -79,13 +91,26 @@ struct blkif_x86_32_response {
#pragma pack(pop)
/* x86_64 protocol version */
+
+struct blkif_x86_64_request_rw {
+ blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
+ struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+};
+
+struct blkif_x86_64_request_discard {
+ blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
+ uint64_t nr_sectors;
+};
+
struct blkif_x86_64_request {
uint8_t operation; /* BLKIF_OP_??? */
uint8_t nr_segments; /* number of segments */
blkif_vdev_t handle; /* only for read/write requests */
uint64_t __attribute__((__aligned__(8))) id;
- blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
- struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+ union {
+ struct blkif_x86_64_request_rw rw;
+ struct blkif_x86_64_request_discard discard;
+ } u;
};
struct blkif_x86_64_response {
uint64_t __attribute__((__aligned__(8))) id;
@@ -113,6 +138,11 @@ enum blkif_protocol {
BLKIF_PROTOCOL_X86_64 = 3,
};
+enum blkif_backend_type {
+ BLKIF_BACKEND_PHY = 1,
+ BLKIF_BACKEND_FILE = 2,
+};
+
struct xen_vbd {
/* What the domain refers to this vbd as. */
blkif_vdev_t handle;
@@ -138,8 +168,9 @@ struct xen_blkif {
unsigned int irq;
/* Comms information. */
enum blkif_protocol blk_protocol;
+ enum blkif_backend_type blk_backend_type;
union blkif_back_rings blk_rings;
- struct vm_struct *blk_ring_area;
+ void *blk_ring;
/* The VBD attached to this interface. */
struct xen_vbd vbd;
/* Back pointer to the backend_info. */
@@ -149,6 +180,9 @@ struct xen_blkif {
atomic_t refcnt;
wait_queue_head_t wq;
+ /* for barrier (drain) requests */
+ struct completion drain_complete;
+ atomic_t drain;
/* One thread per one blkif. */
struct task_struct *xenblkd;
unsigned int waiting_reqs;
@@ -159,13 +193,11 @@ struct xen_blkif {
int st_wr_req;
int st_oo_req;
int st_f_req;
+ int st_ds_req;
int st_rd_sect;
int st_wr_sect;
wait_queue_head_t waiting_to_free;
-
- grant_handle_t shmem_handle;
- grant_ref_t shmem_ref;
};
@@ -182,7 +214,7 @@ struct xen_blkif {
struct phys_req {
unsigned short dev;
- unsigned short nr_sects;
+ blkif_sector_t nr_sects;
struct block_device *bdev;
blkif_sector_t sector_number;
};
@@ -196,6 +228,8 @@ int xen_blkif_schedule(void *arg);
int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt,
struct backend_info *be, int state);
+int xen_blkbk_barrier(struct xenbus_transaction xbt,
+ struct backend_info *be, int state);
struct xenbus_device *xen_blkbk_xenbus(struct backend_info *be);
static inline void blkif_get_x86_32_req(struct blkif_request *dst,
@@ -206,12 +240,25 @@ static inline void blkif_get_x86_32_req(struct blkif_request *dst,
dst->nr_segments = src->nr_segments;
dst->handle = src->handle;
dst->id = src->id;
- dst->u.rw.sector_number = src->sector_number;
- barrier();
- if (n > dst->nr_segments)
- n = dst->nr_segments;
- for (i = 0; i < n; i++)
- dst->u.rw.seg[i] = src->seg[i];
+ switch (src->operation) {
+ case BLKIF_OP_READ:
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_WRITE_BARRIER:
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ dst->u.rw.sector_number = src->u.rw.sector_number;
+ barrier();
+ if (n > dst->nr_segments)
+ n = dst->nr_segments;
+ for (i = 0; i < n; i++)
+ dst->u.rw.seg[i] = src->u.rw.seg[i];
+ break;
+ case BLKIF_OP_DISCARD:
+ dst->u.discard.sector_number = src->u.discard.sector_number;
+ dst->u.discard.nr_sectors = src->u.discard.nr_sectors;
+ break;
+ default:
+ break;
+ }
}
static inline void blkif_get_x86_64_req(struct blkif_request *dst,
@@ -222,12 +269,25 @@ static inline void blkif_get_x86_64_req(struct blkif_request *dst,
dst->nr_segments = src->nr_segments;
dst->handle = src->handle;
dst->id = src->id;
- dst->u.rw.sector_number = src->sector_number;
- barrier();
- if (n > dst->nr_segments)
- n = dst->nr_segments;
- for (i = 0; i < n; i++)
- dst->u.rw.seg[i] = src->seg[i];
+ switch (src->operation) {
+ case BLKIF_OP_READ:
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_WRITE_BARRIER:
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ dst->u.rw.sector_number = src->u.rw.sector_number;
+ barrier();
+ if (n > dst->nr_segments)
+ n = dst->nr_segments;
+ for (i = 0; i < n; i++)
+ dst->u.rw.seg[i] = src->u.rw.seg[i];
+ break;
+ case BLKIF_OP_DISCARD:
+ dst->u.discard.sector_number = src->u.discard.sector_number;
+ dst->u.discard.nr_sectors = src->u.discard.nr_sectors;
+ break;
+ default:
+ break;
+ }
}
#endif /* __XEN_BLKIF__BACKEND__COMMON_H__ */
diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c
index 6cc0db1bf522..f759ad4584c3 100644
--- a/drivers/block/xen-blkback/xenbus.c
+++ b/drivers/block/xen-blkback/xenbus.c
@@ -114,44 +114,14 @@ static struct xen_blkif *xen_blkif_alloc(domid_t domid)
spin_lock_init(&blkif->blk_ring_lock);
atomic_set(&blkif->refcnt, 1);
init_waitqueue_head(&blkif->wq);
+ init_completion(&blkif->drain_complete);
+ atomic_set(&blkif->drain, 0);
blkif->st_print = jiffies;
init_waitqueue_head(&blkif->waiting_to_free);
return blkif;
}
-static int map_frontend_page(struct xen_blkif *blkif, unsigned long shared_page)
-{
- struct gnttab_map_grant_ref op;
-
- gnttab_set_map_op(&op, (unsigned long)blkif->blk_ring_area->addr,
- GNTMAP_host_map, shared_page, blkif->domid);
-
- if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
- BUG();
-
- if (op.status) {
- DPRINTK("Grant table operation failure !\n");
- return op.status;
- }
-
- blkif->shmem_ref = shared_page;
- blkif->shmem_handle = op.handle;
-
- return 0;
-}
-
-static void unmap_frontend_page(struct xen_blkif *blkif)
-{
- struct gnttab_unmap_grant_ref op;
-
- gnttab_set_unmap_op(&op, (unsigned long)blkif->blk_ring_area->addr,
- GNTMAP_host_map, blkif->shmem_handle);
-
- if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
- BUG();
-}
-
static int xen_blkif_map(struct xen_blkif *blkif, unsigned long shared_page,
unsigned int evtchn)
{
@@ -161,35 +131,29 @@ static int xen_blkif_map(struct xen_blkif *blkif, unsigned long shared_page,
if (blkif->irq)
return 0;
- blkif->blk_ring_area = alloc_vm_area(PAGE_SIZE);
- if (!blkif->blk_ring_area)
- return -ENOMEM;
-
- err = map_frontend_page(blkif, shared_page);
- if (err) {
- free_vm_area(blkif->blk_ring_area);
+ err = xenbus_map_ring_valloc(blkif->be->dev, shared_page, &blkif->blk_ring);
+ if (err < 0)
return err;
- }
switch (blkif->blk_protocol) {
case BLKIF_PROTOCOL_NATIVE:
{
struct blkif_sring *sring;
- sring = (struct blkif_sring *)blkif->blk_ring_area->addr;
+ sring = (struct blkif_sring *)blkif->blk_ring;
BACK_RING_INIT(&blkif->blk_rings.native, sring, PAGE_SIZE);
break;
}
case BLKIF_PROTOCOL_X86_32:
{
struct blkif_x86_32_sring *sring_x86_32;
- sring_x86_32 = (struct blkif_x86_32_sring *)blkif->blk_ring_area->addr;
+ sring_x86_32 = (struct blkif_x86_32_sring *)blkif->blk_ring;
BACK_RING_INIT(&blkif->blk_rings.x86_32, sring_x86_32, PAGE_SIZE);
break;
}
case BLKIF_PROTOCOL_X86_64:
{
struct blkif_x86_64_sring *sring_x86_64;
- sring_x86_64 = (struct blkif_x86_64_sring *)blkif->blk_ring_area->addr;
+ sring_x86_64 = (struct blkif_x86_64_sring *)blkif->blk_ring;
BACK_RING_INIT(&blkif->blk_rings.x86_64, sring_x86_64, PAGE_SIZE);
break;
}
@@ -201,8 +165,7 @@ static int xen_blkif_map(struct xen_blkif *blkif, unsigned long shared_page,
xen_blkif_be_int, 0,
"blkif-backend", blkif);
if (err < 0) {
- unmap_frontend_page(blkif);
- free_vm_area(blkif->blk_ring_area);
+ xenbus_unmap_ring_vfree(blkif->be->dev, blkif->blk_ring);
blkif->blk_rings.common.sring = NULL;
return err;
}
@@ -228,8 +191,7 @@ static void xen_blkif_disconnect(struct xen_blkif *blkif)
}
if (blkif->blk_rings.common.sring) {
- unmap_frontend_page(blkif);
- free_vm_area(blkif->blk_ring_area);
+ xenbus_unmap_ring_vfree(blkif->be->dev, blkif->blk_ring);
blkif->blk_rings.common.sring = NULL;
}
}
@@ -272,6 +234,7 @@ VBD_SHOW(oo_req, "%d\n", be->blkif->st_oo_req);
VBD_SHOW(rd_req, "%d\n", be->blkif->st_rd_req);
VBD_SHOW(wr_req, "%d\n", be->blkif->st_wr_req);
VBD_SHOW(f_req, "%d\n", be->blkif->st_f_req);
+VBD_SHOW(ds_req, "%d\n", be->blkif->st_ds_req);
VBD_SHOW(rd_sect, "%d\n", be->blkif->st_rd_sect);
VBD_SHOW(wr_sect, "%d\n", be->blkif->st_wr_sect);
@@ -280,6 +243,7 @@ static struct attribute *xen_vbdstat_attrs[] = {
&dev_attr_rd_req.attr,
&dev_attr_wr_req.attr,
&dev_attr_f_req.attr,
+ &dev_attr_ds_req.attr,
&dev_attr_rd_sect.attr,
&dev_attr_wr_sect.attr,
NULL
@@ -419,6 +383,73 @@ int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt,
return err;
}
+int xen_blkbk_discard(struct xenbus_transaction xbt, struct backend_info *be)
+{
+ struct xenbus_device *dev = be->dev;
+ struct xen_blkif *blkif = be->blkif;
+ char *type;
+ int err;
+ int state = 0;
+
+ type = xenbus_read(XBT_NIL, dev->nodename, "type", NULL);
+ if (!IS_ERR(type)) {
+ if (strncmp(type, "file", 4) == 0) {
+ state = 1;
+ blkif->blk_backend_type = BLKIF_BACKEND_FILE;
+ }
+ if (strncmp(type, "phy", 3) == 0) {
+ struct block_device *bdev = be->blkif->vbd.bdev;
+ struct request_queue *q = bdev_get_queue(bdev);
+ if (blk_queue_discard(q)) {
+ err = xenbus_printf(xbt, dev->nodename,
+ "discard-granularity", "%u",
+ q->limits.discard_granularity);
+ if (err) {
+ xenbus_dev_fatal(dev, err,
+ "writing discard-granularity");
+ goto kfree;
+ }
+ err = xenbus_printf(xbt, dev->nodename,
+ "discard-alignment", "%u",
+ q->limits.discard_alignment);
+ if (err) {
+ xenbus_dev_fatal(dev, err,
+ "writing discard-alignment");
+ goto kfree;
+ }
+ state = 1;
+ blkif->blk_backend_type = BLKIF_BACKEND_PHY;
+ }
+ }
+ } else {
+ err = PTR_ERR(type);
+ xenbus_dev_fatal(dev, err, "reading type");
+ goto out;
+ }
+
+ err = xenbus_printf(xbt, dev->nodename, "feature-discard",
+ "%d", state);
+ if (err)
+ xenbus_dev_fatal(dev, err, "writing feature-discard");
+kfree:
+ kfree(type);
+out:
+ return err;
+}
+int xen_blkbk_barrier(struct xenbus_transaction xbt,
+ struct backend_info *be, int state)
+{
+ struct xenbus_device *dev = be->dev;
+ int err;
+
+ err = xenbus_printf(xbt, dev->nodename, "feature-barrier",
+ "%d", state);
+ if (err)
+ xenbus_dev_fatal(dev, err, "writing feature-barrier");
+
+ return err;
+}
+
/*
* Entry point to this code when a new device is created. Allocate the basic
* structures, and watch the store waiting for the hotplug scripts to tell us
@@ -590,7 +621,7 @@ static void frontend_changed(struct xenbus_device *dev,
/*
* Enforce precondition before potential leak point.
- * blkif_disconnect() is idempotent.
+ * xen_blkif_disconnect() is idempotent.
*/
xen_blkif_disconnect(be->blkif);
@@ -601,17 +632,17 @@ static void frontend_changed(struct xenbus_device *dev,
break;
case XenbusStateClosing:
- xen_blkif_disconnect(be->blkif);
xenbus_switch_state(dev, XenbusStateClosing);
break;
case XenbusStateClosed:
+ xen_blkif_disconnect(be->blkif);
xenbus_switch_state(dev, XenbusStateClosed);
if (xenbus_dev_is_online(dev))
break;
/* fall through if not online */
case XenbusStateUnknown:
- /* implies blkif_disconnect() via blkback_remove() */
+ /* implies xen_blkif_disconnect() via xen_blkbk_remove() */
device_unregister(&dev->dev);
break;
@@ -650,6 +681,11 @@ again:
if (err)
goto abort;
+ err = xen_blkbk_discard(xbt, be);
+
+ /* If we can't advertise it is OK. */
+ err = xen_blkbk_barrier(xbt, be, be->blkif->vbd.flush_support);
+
err = xenbus_printf(xbt, dev->nodename, "sectors", "%llu",
(unsigned long long)vbd_sz(&be->blkif->vbd));
if (err) {
@@ -684,7 +720,7 @@ again:
err = xenbus_switch_state(dev, XenbusStateConnected);
if (err)
- xenbus_dev_fatal(dev, err, "switching to Connected state",
+ xenbus_dev_fatal(dev, err, "%s: switching to Connected state",
dev->nodename);
return;
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index b536a9cef917..7b2ec5908413 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -98,6 +98,9 @@ struct blkfront_info
unsigned long shadow_free;
unsigned int feature_flush;
unsigned int flush_op;
+ unsigned int feature_discard;
+ unsigned int discard_granularity;
+ unsigned int discard_alignment;
int is_ready;
};
@@ -123,8 +126,8 @@ static DEFINE_SPINLOCK(minor_lock);
#define BLKIF_MINOR_EXT(dev) ((dev)&(~EXTENDED))
#define EMULATED_HD_DISK_MINOR_OFFSET (0)
#define EMULATED_HD_DISK_NAME_OFFSET (EMULATED_HD_DISK_MINOR_OFFSET / 256)
-#define EMULATED_SD_DISK_MINOR_OFFSET (EMULATED_HD_DISK_MINOR_OFFSET + (4 * 16))
-#define EMULATED_SD_DISK_NAME_OFFSET (EMULATED_HD_DISK_NAME_OFFSET + 4)
+#define EMULATED_SD_DISK_MINOR_OFFSET (0)
+#define EMULATED_SD_DISK_NAME_OFFSET (EMULATED_SD_DISK_MINOR_OFFSET / 256)
#define DEV_NAME "xvd" /* name in /dev */
@@ -302,29 +305,36 @@ static int blkif_queue_request(struct request *req)
ring_req->operation = info->flush_op;
}
- ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg);
- BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST);
+ if (unlikely(req->cmd_flags & REQ_DISCARD)) {
+ /* id, sector_number and handle are set above. */
+ ring_req->operation = BLKIF_OP_DISCARD;
+ ring_req->nr_segments = 0;
+ ring_req->u.discard.nr_sectors = blk_rq_sectors(req);
+ } else {
+ 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);
+ 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);
- gnttab_grant_foreign_access_ref(
- ref,
- info->xbdev->otherend_id,
- buffer_mfn,
- rq_data_dir(req) );
-
- info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn);
- ring_req->u.rw.seg[i] =
- (struct blkif_request_segment) {
- .gref = ref,
- .first_sect = fsect,
- .last_sect = lsect };
+ gnttab_grant_foreign_access_ref(
+ ref,
+ info->xbdev->otherend_id,
+ buffer_mfn,
+ rq_data_dir(req));
+
+ info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn);
+ ring_req->u.rw.seg[i] =
+ (struct blkif_request_segment) {
+ .gref = ref,
+ .first_sect = fsect,
+ .last_sect = lsect };
+ }
}
info->ring.req_prod_pvt++;
@@ -370,7 +380,9 @@ static void do_blkif_request(struct request_queue *rq)
blk_start_request(req);
- if (req->cmd_type != REQ_TYPE_FS) {
+ if ((req->cmd_type != REQ_TYPE_FS) ||
+ ((req->cmd_flags & (REQ_FLUSH | REQ_FUA)) &&
+ !info->flush_op)) {
__blk_end_request_all(req, -EIO);
continue;
}
@@ -399,6 +411,7 @@ wait:
static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
{
struct request_queue *rq;
+ struct blkfront_info *info = gd->private_data;
rq = blk_init_queue(do_blkif_request, &blkif_io_lock);
if (rq == NULL)
@@ -406,6 +419,13 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq);
+ if (info->feature_discard) {
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, rq);
+ blk_queue_max_discard_sectors(rq, get_capacity(gd));
+ rq->limits.discard_granularity = info->discard_granularity;
+ rq->limits.discard_alignment = info->discard_alignment;
+ }
+
/* Hard sector size and max sectors impersonate the equiv. hardware. */
blk_queue_logical_block_size(rq, sector_size);
blk_queue_max_hw_sectors(rq, 512);
@@ -529,7 +549,7 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
minor = BLKIF_MINOR_EXT(info->vdevice);
nr_parts = PARTS_PER_EXT_DISK;
offset = minor / nr_parts;
- if (xen_hvm_domain() && offset <= EMULATED_HD_DISK_NAME_OFFSET + 4)
+ if (xen_hvm_domain() && offset < EMULATED_HD_DISK_NAME_OFFSET + 4)
printk(KERN_WARNING "blkfront: vdevice 0x%x might conflict with "
"emulated IDE disks,\n\t choose an xvd device name"
"from xvde on\n", info->vdevice);
@@ -722,6 +742,17 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
error = (bret->status == BLKIF_RSP_OKAY) ? 0 : -EIO;
switch (bret->operation) {
+ case BLKIF_OP_DISCARD:
+ if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) {
+ struct request_queue *rq = info->rq;
+ printk(KERN_WARNING "blkfront: %s: discard op failed\n",
+ info->gd->disk_name);
+ error = -EOPNOTSUPP;
+ info->feature_discard = 0;
+ queue_flag_clear(QUEUE_FLAG_DISCARD, rq);
+ }
+ __blk_end_request_all(req, error);
+ break;
case BLKIF_OP_FLUSH_DISKCACHE:
case BLKIF_OP_WRITE_BARRIER:
if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) {
@@ -1098,6 +1129,33 @@ blkfront_closing(struct blkfront_info *info)
bdput(bdev);
}
+static void blkfront_setup_discard(struct blkfront_info *info)
+{
+ int err;
+ char *type;
+ unsigned int discard_granularity;
+ unsigned int discard_alignment;
+
+ type = xenbus_read(XBT_NIL, info->xbdev->otherend, "type", NULL);
+ if (IS_ERR(type))
+ return;
+
+ if (strncmp(type, "phy", 3) == 0) {
+ err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
+ "discard-granularity", "%u", &discard_granularity,
+ "discard-alignment", "%u", &discard_alignment,
+ NULL);
+ if (!err) {
+ info->feature_discard = 1;
+ info->discard_granularity = discard_granularity;
+ info->discard_alignment = discard_alignment;
+ }
+ } else if (strncmp(type, "file", 4) == 0)
+ info->feature_discard = 1;
+
+ kfree(type);
+}
+
/*
* Invoked when the backend is finally 'ready' (and has told produced
* the details about the physical device - #sectors, size, etc).
@@ -1108,7 +1166,7 @@ static void blkfront_connect(struct blkfront_info *info)
unsigned long sector_size;
unsigned int binfo;
int err;
- int barrier, flush;
+ int barrier, flush, discard;
switch (info->connected) {
case BLKIF_STATE_CONNECTED:
@@ -1178,7 +1236,14 @@ static void blkfront_connect(struct blkfront_info *info)
info->feature_flush = REQ_FLUSH;
info->flush_op = BLKIF_OP_FLUSH_DISKCACHE;
}
-
+
+ err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
+ "feature-discard", "%d", &discard,
+ NULL);
+
+ if (!err && discard)
+ blkfront_setup_discard(info);
+
err = xlvbd_alloc_gendisk(sectors, info, binfo, sector_size);
if (err) {
xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s",
@@ -1385,6 +1450,8 @@ static struct xenbus_driver blkfront = {
static int __init xlblk_init(void)
{
+ int ret;
+
if (!xen_domain())
return -ENODEV;
@@ -1394,7 +1461,13 @@ static int __init xlblk_init(void)
return -ENODEV;
}
- return xenbus_register_frontend(&blkfront);
+ ret = xenbus_register_frontend(&blkfront);
+ if (ret) {
+ unregister_blkdev(XENVBD_MAJOR, DEV_NAME);
+ return ret;
+ }
+
+ return 0;
}
module_init(xlblk_init);
diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c
index 6c7fd7db6dff..fb1975d82a73 100644
--- a/drivers/block/xsysace.c
+++ b/drivers/block/xsysace.c
@@ -1155,12 +1155,19 @@ static int __devinit ace_probe(struct platform_device *dev)
{
resource_size_t physaddr = 0;
int bus_width = ACE_BUS_WIDTH_16; /* FIXME: should not be hard coded */
- int id = dev->id;
+ u32 id = dev->id;
int irq = NO_IRQ;
int i;
dev_dbg(&dev->dev, "ace_probe(%p)\n", dev);
+ /* device id and bus width */
+ of_property_read_u32(dev->dev.of_node, "port-number", &id);
+ if (id < 0)
+ id = 0;
+ if (of_find_property(dev->dev.of_node, "8-bit", NULL))
+ bus_width = ACE_BUS_WIDTH_8;
+
for (i = 0; i < dev->num_resources; i++) {
if (dev->resource[i].flags & IORESOURCE_MEM)
physaddr = dev->resource[i].start;
@@ -1181,57 +1188,7 @@ static int __devexit ace_remove(struct platform_device *dev)
return 0;
}
-static struct platform_driver ace_platform_driver = {
- .probe = ace_probe,
- .remove = __devexit_p(ace_remove),
- .driver = {
- .owner = THIS_MODULE,
- .name = "xsysace",
- },
-};
-
-/* ---------------------------------------------------------------------
- * OF_Platform Bus Support
- */
-
#if defined(CONFIG_OF)
-static int __devinit ace_of_probe(struct platform_device *op)
-{
- struct resource res;
- resource_size_t physaddr;
- const u32 *id;
- int irq, bus_width, rc;
-
- /* device id */
- id = of_get_property(op->dev.of_node, "port-number", NULL);
-
- /* physaddr */
- rc = of_address_to_resource(op->dev.of_node, 0, &res);
- if (rc) {
- dev_err(&op->dev, "invalid address\n");
- return rc;
- }
- physaddr = res.start;
-
- /* irq */
- irq = irq_of_parse_and_map(op->dev.of_node, 0);
-
- /* bus width */
- bus_width = ACE_BUS_WIDTH_16;
- if (of_find_property(op->dev.of_node, "8-bit", NULL))
- bus_width = ACE_BUS_WIDTH_8;
-
- /* Call the bus-independent setup code */
- return ace_alloc(&op->dev, id ? be32_to_cpup(id) : 0,
- physaddr, irq, bus_width);
-}
-
-static int __devexit ace_of_remove(struct platform_device *op)
-{
- ace_free(&op->dev);
- return 0;
-}
-
/* Match table for of_platform binding */
static const struct of_device_id ace_of_match[] __devinitconst = {
{ .compatible = "xlnx,opb-sysace-1.00.b", },
@@ -1241,34 +1198,20 @@ static const struct of_device_id ace_of_match[] __devinitconst = {
{},
};
MODULE_DEVICE_TABLE(of, ace_of_match);
+#else /* CONFIG_OF */
+#define ace_of_match NULL
+#endif /* CONFIG_OF */
-static struct platform_driver ace_of_driver = {
- .probe = ace_of_probe,
- .remove = __devexit_p(ace_of_remove),
+static struct platform_driver ace_platform_driver = {
+ .probe = ace_probe,
+ .remove = __devexit_p(ace_remove),
.driver = {
- .name = "xsysace",
.owner = THIS_MODULE,
+ .name = "xsysace",
.of_match_table = ace_of_match,
},
};
-/* Registration helpers to keep the number of #ifdefs to a minimum */
-static inline int __init ace_of_register(void)
-{
- pr_debug("xsysace: registering OF binding\n");
- return platform_driver_register(&ace_of_driver);
-}
-
-static inline void __exit ace_of_unregister(void)
-{
- platform_driver_unregister(&ace_of_driver);
-}
-#else /* CONFIG_OF */
-/* CONFIG_OF not enabled; do nothing helpers */
-static inline int __init ace_of_register(void) { return 0; }
-static inline void __exit ace_of_unregister(void) { }
-#endif /* CONFIG_OF */
-
/* ---------------------------------------------------------------------
* Module init/exit routines
*/
@@ -1282,11 +1225,6 @@ static int __init ace_init(void)
goto err_blk;
}
- rc = ace_of_register();
- if (rc)
- goto err_of;
-
- pr_debug("xsysace: registering platform binding\n");
rc = platform_driver_register(&ace_platform_driver);
if (rc)
goto err_plat;
@@ -1295,21 +1233,17 @@ static int __init ace_init(void)
return 0;
err_plat:
- ace_of_unregister();
-err_of:
unregister_blkdev(ace_major, "xsysace");
err_blk:
printk(KERN_ERR "xsysace: registration failed; err=%i\n", rc);
return rc;
}
+module_init(ace_init);
static void __exit ace_exit(void)
{
pr_debug("Unregistering Xilinx SystemACE driver\n");
platform_driver_unregister(&ace_platform_driver);
- ace_of_unregister();
unregister_blkdev(ace_major, "xsysace");
}
-
-module_init(ace_init);
module_exit(ace_exit);
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 6bacef368fab..106beb194f3c 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -63,6 +63,7 @@ static struct usb_device_id ath3k_table[] = {
/* Atheros AR3011 with sflash firmware*/
{ USB_DEVICE(0x0CF3, 0x3002) },
{ USB_DEVICE(0x13d3, 0x3304) },
+ { USB_DEVICE(0x0930, 0x0215) },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03F0, 0x311D) },
@@ -104,7 +105,7 @@ static int ath3k_load_firmware(struct usb_device *udev,
pipe = usb_sndctrlpipe(udev, 0);
- send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC);
+ send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
if (!send_buf) {
BT_ERR("Can't allocate memory chunk for firmware");
return -ENOMEM;
@@ -175,7 +176,7 @@ static int ath3k_load_fwfile(struct usb_device *udev,
count = firmware->size;
- send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC);
+ send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
if (!send_buf) {
BT_ERR("Can't allocate memory chunk for firmware");
return -ENOMEM;
@@ -375,6 +376,11 @@ static int ath3k_probe(struct usb_interface *intf,
/* load patch and sysconfig files for AR3012 */
if (id->driver_info & BTUSB_ATH3012) {
+
+ /* New firmware with patch and sysconfig files already loaded */
+ if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001)
+ return -ENODEV;
+
ret = ath3k_load_patch(udev);
if (ret < 0) {
BT_ERR("Loading patch file failed");
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
index 8b1b643a519b..54952ab800b8 100644
--- a/drivers/bluetooth/bcm203x.c
+++ b/drivers/bluetooth/bcm203x.c
@@ -24,6 +24,7 @@
#include <linux/module.h>
+#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -65,6 +66,7 @@ struct bcm203x_data {
unsigned long state;
struct work_struct work;
+ atomic_t shutdown;
struct urb *urb;
unsigned char *buffer;
@@ -97,6 +99,7 @@ static void bcm203x_complete(struct urb *urb)
data->state = BCM203X_SELECT_MEMORY;
+ /* use workqueue to have a small delay */
schedule_work(&data->work);
break;
@@ -155,7 +158,10 @@ static void bcm203x_work(struct work_struct *work)
struct bcm203x_data *data =
container_of(work, struct bcm203x_data, work);
- if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ if (atomic_read(&data->shutdown))
+ return;
+
+ if (usb_submit_urb(data->urb, GFP_KERNEL) < 0)
BT_ERR("Can't submit URB");
}
@@ -243,6 +249,7 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id
usb_set_intfdata(intf, data);
+ /* use workqueue to have a small delay */
schedule_work(&data->work);
return 0;
@@ -254,6 +261,9 @@ static void bcm203x_disconnect(struct usb_interface *intf)
BT_DBG("intf %p", intf);
+ atomic_inc(&data->shutdown);
+ cancel_work_sync(&data->work);
+
usb_kill_urb(data->urb);
usb_set_intfdata(intf, NULL);
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 005919ab043c..61b591470a90 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -568,22 +568,23 @@ static int bfusb_load_firmware(struct bfusb_data *data,
BT_INFO("BlueFRITZ! USB loading firmware");
+ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_KERNEL);
+ if (!buf) {
+ BT_ERR("Can't allocate memory chunk for firmware");
+ return -ENOMEM;
+ }
+
pipe = usb_sndctrlpipe(data->udev, 0);
if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) {
BT_ERR("Can't change to loading configuration");
+ kfree(buf);
return -EBUSY;
}
data->udev->toggle[0] = data->udev->toggle[1] = 0;
- buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
- if (!buf) {
- BT_ERR("Can't allocate memory chunk for firmware");
- return -ENOMEM;
- }
-
pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
while (count) {
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index 548d1d9e4dda..a88a78c86162 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -18,6 +18,8 @@
* this warranty disclaimer.
**/
+#include <linux/module.h>
+
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 7f521d4ac657..9ef48167e2cf 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -23,6 +23,7 @@
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
+#include <linux/module.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -81,7 +82,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8787 = {
.io_port_2 = 0x7a,
};
-static const struct btmrvl_sdio_device btmrvl_sdio_sd6888 = {
+static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
.helper = "sd8688_helper.bin",
.firmware = "sd8688.bin",
.reg = &btmrvl_reg_8688,
@@ -98,7 +99,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
static const struct sdio_device_id btmrvl_sdio_ids[] = {
/* Marvell SD8688 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
- .driver_data = (unsigned long) &btmrvl_sdio_sd6888 },
+ .driver_data = (unsigned long) &btmrvl_sdio_sd8688 },
/* Marvell SD8787 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index c2de8951e3fb..fe4ebc375b3d 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -54,11 +54,15 @@ static struct usb_driver btusb_driver;
#define BTUSB_BCM92035 0x10
#define BTUSB_BROKEN_ISOC 0x20
#define BTUSB_WRONG_SCO_MTU 0x40
+#define BTUSB_ATH3012 0x80
static struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
+ /* Broadcom SoftSailing reporting vendor specific */
+ { USB_DEVICE(0x05ac, 0x21e1) },
+
/* Apple MacBookPro 7,1 */
{ USB_DEVICE(0x05ac, 0x8213) },
@@ -71,9 +75,15 @@ static struct usb_device_id btusb_table[] = {
/* Apple MacBookAir3,1, MacBookAir3,2 */
{ USB_DEVICE(0x05ac, 0x821b) },
+ /* Apple MacBookAir4,1 */
+ { USB_DEVICE(0x05ac, 0x821f) },
+
/* Apple MacBookPro8,2 */
{ USB_DEVICE(0x05ac, 0x821a) },
+ /* Apple MacMini5,1 */
+ { USB_DEVICE(0x05ac, 0x8281) },
+
/* AVM BlueFRITZ! USB v2.0 */
{ USB_DEVICE(0x057c, 0x3800) },
@@ -90,6 +100,9 @@ static struct usb_device_id btusb_table[] = {
/* Canyon CN-BTU1 with HID interfaces */
{ USB_DEVICE(0x0c10, 0x0000) },
+ /* Broadcom BCM20702A0 */
+ { USB_DEVICE(0x413c, 0x8197) },
+
{ } /* Terminating entry */
};
@@ -105,12 +118,13 @@ static struct usb_device_id blacklist_table[] = {
/* Atheros 3011 with sflash firmware */
{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
/* Atheros 3012 with sflash firmware */
- { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
@@ -255,7 +269,9 @@ static void btusb_intr_complete(struct urb *urb)
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
- if (err != -EPERM)
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected */
+ if (err != -EPERM && err != -ENODEV)
BT_ERR("%s urb %p failed to resubmit (%d)",
hdev->name, urb, -err);
usb_unanchor_urb(urb);
@@ -340,7 +356,9 @@ static void btusb_bulk_complete(struct urb *urb)
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
- if (err != -EPERM)
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected */
+ if (err != -EPERM && err != -ENODEV)
BT_ERR("%s urb %p failed to resubmit (%d)",
hdev->name, urb, -err);
usb_unanchor_urb(urb);
@@ -430,7 +448,9 @@ static void btusb_isoc_complete(struct urb *urb)
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
- if (err != -EPERM)
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected */
+ if (err != -EPERM && err != -ENODEV)
BT_ERR("%s urb %p failed to resubmit (%d)",
hdev->name, urb, -err);
usb_unanchor_urb(urb);
@@ -694,8 +714,7 @@ static int btusb_send_frame(struct sk_buff *skb)
break;
case HCI_ACLDATA_PKT:
- if (!data->bulk_tx_ep || (hdev->conn_hash.acl_num < 1 &&
- hdev->conn_hash.le_num < 1))
+ if (!data->bulk_tx_ep)
return -ENODEV;
urb = usb_alloc_urb(0, GFP_ATOMIC);
@@ -914,6 +933,15 @@ static int btusb_probe(struct usb_interface *intf,
if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER)
return -ENODEV;
+ if (id->driver_info & BTUSB_ATH3012) {
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ /* Old firmware would otherwise let ath3k driver load
+ * patch and sysconfig files */
+ if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001)
+ return -ENODEV;
+ }
+
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -1093,7 +1121,7 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
return 0;
spin_lock_irq(&data->txlock);
- if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
+ if (!(PMSG_IS_AUTO(message) && data->tx_in_flight)) {
set_bit(BTUSB_SUSPENDING, &data->flags);
spin_unlock_irq(&data->txlock);
} else {
diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c
index 65d27aff553a..b5f83b44a0cd 100644
--- a/drivers/bluetooth/btwilink.c
+++ b/drivers/bluetooth/btwilink.c
@@ -29,6 +29,7 @@
#include <net/bluetooth/hci.h>
#include <linux/ti_wilink_st.h>
+#include <linux/module.h>
/* Bluetooth Driver Version */
#define VERSION "1.0"
@@ -125,6 +126,13 @@ static long st_receive(void *priv_data, struct sk_buff *skb)
/* protocol structure registered with shared transport */
static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = {
{
+ .chnl_id = HCI_EVENT_PKT, /* HCI Events */
+ .hdr_len = sizeof(struct hci_event_hdr),
+ .offset_len_in_hdr = offsetof(struct hci_event_hdr, plen),
+ .len_size = 1, /* sizeof(plen) in struct hci_event_hdr */
+ .reserve = 8,
+ },
+ {
.chnl_id = HCI_ACLDATA_PKT, /* ACL */
.hdr_len = sizeof(struct hci_acl_hdr),
.offset_len_in_hdr = offsetof(struct hci_acl_hdr, dlen),
@@ -138,13 +146,6 @@ static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = {
.len_size = 1, /* sizeof(dlen) in struct hci_sco_hdr */
.reserve = 8,
},
- {
- .chnl_id = HCI_EVENT_PKT, /* HCI Events */
- .hdr_len = sizeof(struct hci_event_hdr),
- .offset_len_in_hdr = offsetof(struct hci_event_hdr, plen),
- .len_size = 1, /* sizeof(plen) in struct hci_event_hdr */
- .reserve = 8,
- },
};
/* Called from HCI core to initialize the device */
@@ -240,7 +241,7 @@ static int ti_st_close(struct hci_dev *hdev)
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0;
- for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
+ for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
err = st_unregister(&ti_st_proto[i]);
if (err)
BT_ERR("st_unregister(%d) failed with error %d",
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 75fb965b8f72..f997c27d79e2 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -1929,11 +1929,17 @@ static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s,
goto out;
s->manufact.len = buf[0] << 8 | buf[1];
- if (s->manufact.len < 0 || s->manufact.len > 2048) {
+ if (s->manufact.len < 0) {
cdinfo(CD_WARNING, "Received invalid manufacture info length"
" (%d)\n", s->manufact.len);
ret = -EIO;
} else {
+ if (s->manufact.len > 2048) {
+ cdinfo(CD_WARNING, "Received invalid manufacture info "
+ "length (%d): truncating to 2048\n",
+ s->manufact.len);
+ s->manufact.len = 2048;
+ }
memcpy(s->manufact.value, &buf[4], s->manufact.len);
}
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 49502bc5360a..43643033a3ae 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -298,7 +298,7 @@ if RTC_LIB=n
config RTC
tristate "Enhanced Real Time Clock Support (legacy PC RTC driver)"
depends on !PPC && !PARISC && !IA64 && !M68K && !SPARC && !FRV \
- && !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN
+ && !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN && !UML
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
@@ -346,7 +346,7 @@ config JS_RTC
config GEN_RTC
tristate "Generic /dev/rtc emulation"
- depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH && !AVR32 && !BLACKFIN
+ depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH && !AVR32 && !BLACKFIN && !UML
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
@@ -490,7 +490,7 @@ config SCx200_GPIO
config PC8736x_GPIO
tristate "NatSemi PC8736x GPIO Support"
- depends on X86_32
+ depends on X86_32 && !UML
default SCx200_GPIO # mostly N
select NSC_GPIO # needed for support routines
help
@@ -616,5 +616,16 @@ config MSM_SMD_PKT
Enables userspace clients to read and write to some packet SMD
ports via device interface for MSM chipset.
+config TILE_SROM
+ bool "Character-device access via hypervisor to the Tilera SPI ROM"
+ depends on TILE
+ default y
+ ---help---
+ This device provides character-level read-write access
+ to the SROM, typically via the "0", "1", and "2" devices
+ in /dev/srom/. The Tilera hypervisor makes the flash
+ device appear much like a simple EEPROM, and knows
+ how to partition a single ROM for multiple purposes.
+
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7a00672bd85d..32762ba769c2 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -63,3 +63,5 @@ obj-$(CONFIG_RAMOOPS) += ramoops.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
+
+obj-$(CONFIG_TILE_SROM) += tile-srom.o
diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c
index f27d0d0816d3..4b71647782d0 100644
--- a/drivers/char/agp/backend.c
+++ b/drivers/char/agp/backend.c
@@ -171,7 +171,7 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge)
}
got_gatt = 1;
- bridge->key_list = vmalloc(PAGE_SIZE * 4);
+ bridge->key_list = vzalloc(PAGE_SIZE * 4);
if (bridge->key_list == NULL) {
dev_err(&bridge->dev->dev,
"can't allocate memory for key lists\n");
@@ -181,7 +181,6 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge)
got_keylist = 1;
/* FIXME vmalloc'd memory not guaranteed contiguous */
- memset(bridge->key_list, 0, PAGE_SIZE * 4);
if (bridge->driver->configure()) {
dev_err(&bridge->dev->dev, "error configuring host chipset\n");
diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c
index 056b289a1e89..3695773ce7c3 100644
--- a/drivers/char/agp/hp-agp.c
+++ b/drivers/char/agp/hp-agp.c
@@ -336,7 +336,8 @@ hp_zx1_insert_memory (struct agp_memory *mem, off_t pg_start, int type)
off_t j, io_pg_start;
int io_pg_count;
- if (type != 0 || mem->type != 0) {
+ if (type != mem->type ||
+ agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) {
return -EINVAL;
}
@@ -380,7 +381,8 @@ hp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type)
struct _hp_private *hp = &hp_private;
int i, io_pg_start, io_pg_count;
- if (type != 0 || mem->type != 0) {
+ if (type != mem->type ||
+ agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) {
return -EINVAL;
}
diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h
index 999803ce10dc..5da67f165afa 100644
--- a/drivers/char/agp/intel-agp.h
+++ b/drivers/char/agp/intel-agp.h
@@ -90,9 +90,10 @@
#define G4x_GMCH_SIZE_MASK (0xf << 8)
#define G4x_GMCH_SIZE_1M (0x1 << 8)
#define G4x_GMCH_SIZE_2M (0x3 << 8)
-#define G4x_GMCH_SIZE_VT_1M (0x9 << 8)
-#define G4x_GMCH_SIZE_VT_1_5M (0xa << 8)
-#define G4x_GMCH_SIZE_VT_2M (0xc << 8)
+#define G4x_GMCH_SIZE_VT_EN (0x8 << 8)
+#define G4x_GMCH_SIZE_VT_1M (G4x_GMCH_SIZE_1M | G4x_GMCH_SIZE_VT_EN)
+#define G4x_GMCH_SIZE_VT_1_5M ((0x2 << 8) | G4x_GMCH_SIZE_VT_EN)
+#define G4x_GMCH_SIZE_VT_2M (G4x_GMCH_SIZE_2M | G4x_GMCH_SIZE_VT_EN)
#define GFX_FLSH_CNTL 0x2170 /* 915+ */
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index 85151019dde1..c92424ca1a55 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -30,10 +30,10 @@
/*
* If we have Intel graphics, we're not going to have anything other than
* an Intel IOMMU. So make the correct use of the PCI DMA API contingent
- * on the Intel IOMMU support (CONFIG_DMAR).
+ * on the Intel IOMMU support (CONFIG_INTEL_IOMMU).
* Only newer chipsets need to bother with this, of course.
*/
-#ifdef CONFIG_DMAR
+#ifdef CONFIG_INTEL_IOMMU
#define USE_PCI_DMA_API 1
#else
#define USE_PCI_DMA_API 0
@@ -923,6 +923,9 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
{
int ret = -EINVAL;
+ if (intel_private.base.do_idle_maps)
+ return -ENODEV;
+
if (intel_private.clear_fake_agp) {
int start = intel_private.base.stolen_size / PAGE_SIZE;
int end = intel_private.base.gtt_mappable_entries;
@@ -985,6 +988,9 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem,
if (mem->page_count == 0)
return 0;
+ if (intel_private.base.do_idle_maps)
+ return -ENODEV;
+
intel_gtt_clear_range(pg_start, mem->page_count);
if (intel_private.base.needs_dmar) {
@@ -1177,6 +1183,26 @@ static void gen6_cleanup(void)
{
}
+/* Certain Gen5 chipsets require require idling the GPU before
+ * unmapping anything from the GTT when VT-d is enabled.
+ */
+static inline int needs_idle_maps(void)
+{
+#ifdef CONFIG_INTEL_IOMMU
+ const unsigned short gpu_devid = intel_private.pcidev->device;
+ extern int intel_iommu_gfx_mapped;
+
+ /* Query intel_iommu to see if we need the workaround. Presumably that
+ * was loaded first.
+ */
+ if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB ||
+ gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) &&
+ intel_iommu_gfx_mapped)
+ return 1;
+#endif
+ return 0;
+}
+
static int i9xx_setup(void)
{
u32 reg_addr;
@@ -1211,6 +1237,9 @@ static int i9xx_setup(void)
intel_private.gtt_bus_addr = reg_addr + gtt_offset;
}
+ if (needs_idle_maps())
+ intel_private.base.do_idle_maps = 1;
+
intel_i9xx_setup_flush();
return 0;
diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c
index 548708c4b2b8..f4837a893dfa 100644
--- a/drivers/char/apm-emulation.c
+++ b/drivers/char/apm-emulation.c
@@ -40,10 +40,7 @@
#define APM_MINOR_DEV 134
/*
- * See Documentation/Config.help for the configuration options.
- *
- * Various options can be changed at boot time as follows:
- * (We allow underscores for compatibility with the modules code)
+ * One option can be changed at boot time as follows:
* apm=on/off enable/disable APM
*/
@@ -300,17 +297,13 @@ apm_ioctl(struct file *filp, u_int cmd, u_long arg)
/*
* Wait for the suspend/resume to complete. If there
* are pending acknowledges, we wait here for them.
+ * wait_event_freezable() is interruptible and pending
+ * signal can cause busy looping. We aren't doing
+ * anything critical, chill a bit on each iteration.
*/
- freezer_do_not_count();
-
- wait_event(apm_suspend_waitqueue,
- as->suspend_state == SUSPEND_DONE);
-
- /*
- * Since we are waiting until the suspend is done, the
- * try_to_freeze() in freezer_count() will not trigger
- */
- freezer_count();
+ while (wait_event_freezable(apm_suspend_waitqueue,
+ as->suspend_state == SUSPEND_DONE))
+ msleep(10);
break;
case SUSPEND_ACKTO:
as->suspend_result = -ETIMEDOUT;
@@ -606,7 +599,7 @@ static int apm_suspend_notifier(struct notifier_block *nb,
return NOTIFY_OK;
/* interrupted by signal */
- return NOTIFY_BAD;
+ return notifier_from_errno(err);
case PM_POST_SUSPEND:
/*
diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c
index cf39bc08ce08..0c688232aab3 100644
--- a/drivers/char/bsr.c
+++ b/drivers/char/bsr.c
@@ -212,7 +212,7 @@ static int bsr_add_node(struct device_node *bn)
cur->bsr_minor = i + total_bsr_devs;
cur->bsr_addr = res.start;
- cur->bsr_len = res.end - res.start + 1;
+ cur->bsr_len = resource_size(&res);
cur->bsr_bytes = bsr_bytes[i];
cur->bsr_stride = bsr_stride[i];
cur->bsr_dev = MKDEV(bsr_major, i + total_bsr_devs);
diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c
index 0e941b57482e..6c4f4b5a9dd3 100644
--- a/drivers/char/generic_nvram.c
+++ b/drivers/char/generic_nvram.c
@@ -34,12 +34,16 @@ static ssize_t nvram_len;
static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
{
switch (origin) {
+ case 0:
+ break;
case 1:
offset += file->f_pos;
break;
case 2:
offset += nvram_len;
break;
+ default:
+ offset = -1;
}
if (offset < 0)
return -EINVAL;
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 34d6a1cab8de..0833896cf6f2 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -952,7 +952,7 @@ int hpet_alloc(struct hpet_data *hdp)
#ifdef CONFIG_IA64
if (!hpet_clocksource) {
hpet_mctr = (void __iomem *)&hpetp->hp_hpet->hpet_mc;
- CLKSRC_FSYS_MMIO_SET(clocksource_hpet.fsys_mmio, hpet_mctr);
+ clocksource_hpet.archdata.fsys_mmio = hpet_mctr;
clocksource_register_hz(&clocksource_hpet, hpetp->hp_tick_freq);
hpetp->hp_clocksource = &clocksource_hpet;
hpet_clocksource = &clocksource_hpet;
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index a60043b3e409..0689bf6b0183 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -60,6 +60,19 @@ config HW_RANDOM_AMD
If unsure, say Y.
+config HW_RANDOM_ATMEL
+ tristate "Atmel Random Number Generator support"
+ depends on HW_RANDOM && ARCH_AT91SAM9G45
+ default HW_RANDOM
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Atmel AT91 devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atmel-rng.
+
+ If unsure, say Y.
+
config HW_RANDOM_GEODE
tristate "AMD Geode HW Random Number Generator support"
depends on HW_RANDOM && X86_32 && PCI
@@ -210,3 +223,30 @@ config HW_RANDOM_PICOXCELL
module will be called picoxcell-rng.
If unsure, say Y.
+
+config HW_RANDOM_PPC4XX
+ tristate "PowerPC 4xx generic true random number generator support"
+ depends on HW_RANDOM && PPC && 4xx
+ ---help---
+ This driver provides the kernel-side support for the TRNG hardware
+ found in the security function of some PowerPC 4xx SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ppc4xx-rng.
+
+ If unsure, say N.
+
+config UML_RANDOM
+ depends on UML
+ tristate "Hardware random number generator"
+ help
+ This option enables UML's "hardware" random number generator. It
+ attaches itself to the host's /dev/random, supplying as much entropy
+ as the host has, rather than the small amount the UML gets from its
+ own drivers. It registers itself as a standard hardware random number
+ generator, major 10, minor 183, and the canonical device name is
+ /dev/hwrng.
+ The way to make use of this is to install the rng-tools package
+ (check your distro, or download from
+ http://sourceforge.net/projects/gkernel/). rngd periodically reads
+ /dev/hwrng and injects the entropy into /dev/random.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 3db4eb8b19c0..b2ff5265a996 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -7,6 +7,7 @@ rng-core-y := core.o
obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
+obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o
obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o
n2-rng-y := n2-drv.o n2-asm.o
@@ -20,3 +21,4 @@ obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o
obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o
obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
+obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c
new file mode 100644
index 000000000000..241df2e76aba
--- /dev/null
+++ b/drivers/char/hw_random/atmel-rng.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2011 Peter Korsgaard <jacmet@sunsite.dk>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/hw_random.h>
+#include <linux/platform_device.h>
+
+#define TRNG_CR 0x00
+#define TRNG_ISR 0x1c
+#define TRNG_ODATA 0x50
+
+#define TRNG_KEY 0x524e4700 /* RNG */
+
+struct atmel_trng {
+ struct clk *clk;
+ void __iomem *base;
+ struct hwrng rng;
+};
+
+static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
+ u32 *data = buf;
+
+ /* data ready? */
+ if (readl(trng->base + TRNG_ODATA) & 1) {
+ *data = readl(trng->base + TRNG_ODATA);
+ return 4;
+ } else
+ return 0;
+}
+
+static int atmel_trng_probe(struct platform_device *pdev)
+{
+ struct atmel_trng *trng;
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
+ if (!trng)
+ return -ENOMEM;
+
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name))
+ return -EBUSY;
+
+ trng->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!trng->base)
+ return -EBUSY;
+
+ trng->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(trng->clk))
+ return PTR_ERR(trng->clk);
+
+ ret = clk_enable(trng->clk);
+ if (ret)
+ goto err_enable;
+
+ writel(TRNG_KEY | 1, trng->base + TRNG_CR);
+ trng->rng.name = pdev->name;
+ trng->rng.read = atmel_trng_read;
+
+ ret = hwrng_register(&trng->rng);
+ if (ret)
+ goto err_register;
+
+ platform_set_drvdata(pdev, trng);
+
+ return 0;
+
+err_register:
+ clk_disable(trng->clk);
+err_enable:
+ clk_put(trng->clk);
+
+ return ret;
+}
+
+static int __devexit atmel_trng_remove(struct platform_device *pdev)
+{
+ struct atmel_trng *trng = platform_get_drvdata(pdev);
+
+ hwrng_unregister(&trng->rng);
+
+ writel(TRNG_KEY, trng->base + TRNG_CR);
+ clk_disable(trng->clk);
+ clk_put(trng->clk);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int atmel_trng_suspend(struct device *dev)
+{
+ struct atmel_trng *trng = dev_get_drvdata(dev);
+
+ clk_disable(trng->clk);
+
+ return 0;
+}
+
+static int atmel_trng_resume(struct device *dev)
+{
+ struct atmel_trng *trng = dev_get_drvdata(dev);
+
+ return clk_enable(trng->clk);
+}
+
+static const struct dev_pm_ops atmel_trng_pm_ops = {
+ .suspend = atmel_trng_suspend,
+ .resume = atmel_trng_resume,
+};
+#endif /* CONFIG_PM */
+
+static struct platform_driver atmel_trng_driver = {
+ .probe = atmel_trng_probe,
+ .remove = __devexit_p(atmel_trng_remove),
+ .driver = {
+ .name = "atmel-trng",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &atmel_trng_pm_ops,
+#endif /* CONFIG_PM */
+ },
+};
+
+static int __init atmel_trng_init(void)
+{
+ return platform_driver_register(&atmel_trng_driver);
+}
+module_init(atmel_trng_init);
+
+static void __exit atmel_trng_exit(void)
+{
+ platform_driver_unregister(&atmel_trng_driver);
+}
+module_exit(atmel_trng_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
+MODULE_DESCRIPTION("Atmel true random number generator driver");
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 2016aad85203..1bafb40ec8a2 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -19,7 +19,7 @@
Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
Added generic RNG API
- Copyright 2006 Michael Buesch <mbuesch@freenet.de>
+ Copyright 2006 Michael Buesch <m@bues.ch>
Copyright 2005 (c) MontaVista Software, Inc.
Please read Documentation/hw_random.txt for details on use.
diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c
index ac6739e085e3..c3de70de00d4 100644
--- a/drivers/char/hw_random/n2-drv.c
+++ b/drivers/char/hw_random/n2-drv.c
@@ -1,6 +1,6 @@
/* n2-drv.c: Niagara-2 RNG driver.
*
- * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2008, 2011 David S. Miller <davem@davemloft.net>
*/
#include <linux/kernel.h>
@@ -22,8 +22,8 @@
#define DRV_MODULE_NAME "n2rng"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "0.1"
-#define DRV_MODULE_RELDATE "May 15, 2008"
+#define DRV_MODULE_VERSION "0.2"
+#define DRV_MODULE_RELDATE "July 27, 2011"
static char version[] __devinitdata =
DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
@@ -623,14 +623,14 @@ static const struct of_device_id n2rng_match[];
static int __devinit n2rng_probe(struct platform_device *op)
{
const struct of_device_id *match;
- int victoria_falls;
+ int multi_capable;
int err = -ENOMEM;
struct n2rng *np;
match = of_match_device(n2rng_match, &op->dev);
if (!match)
return -EINVAL;
- victoria_falls = (match->data != NULL);
+ multi_capable = (match->data != NULL);
n2rng_driver_version();
np = kzalloc(sizeof(*np), GFP_KERNEL);
@@ -640,8 +640,8 @@ static int __devinit n2rng_probe(struct platform_device *op)
INIT_DELAYED_WORK(&np->work, n2rng_work);
- if (victoria_falls)
- np->flags |= N2RNG_FLAG_VF;
+ if (multi_capable)
+ np->flags |= N2RNG_FLAG_MULTI;
err = -ENODEV;
np->hvapi_major = 2;
@@ -658,10 +658,10 @@ static int __devinit n2rng_probe(struct platform_device *op)
}
}
- if (np->flags & N2RNG_FLAG_VF) {
+ if (np->flags & N2RNG_FLAG_MULTI) {
if (np->hvapi_major < 2) {
- dev_err(&op->dev, "VF RNG requires HVAPI major "
- "version 2 or later, got %lu\n",
+ dev_err(&op->dev, "multi-unit-capable RNG requires "
+ "HVAPI major version 2 or later, got %lu\n",
np->hvapi_major);
goto out_hvapi_unregister;
}
@@ -688,8 +688,8 @@ static int __devinit n2rng_probe(struct platform_device *op)
goto out_free_units;
dev_info(&op->dev, "Found %s RNG, units: %d\n",
- ((np->flags & N2RNG_FLAG_VF) ?
- "Victoria Falls" : "Niagara2"),
+ ((np->flags & N2RNG_FLAG_MULTI) ?
+ "multi-unit-capable" : "single-unit"),
np->num_units);
np->hwrng.name = "n2rng";
@@ -751,6 +751,11 @@ static const struct of_device_id n2rng_match[] = {
.compatible = "SUNW,vf-rng",
.data = (void *) 1,
},
+ {
+ .name = "random-number-generator",
+ .compatible = "SUNW,kt-rng",
+ .data = (void *) 1,
+ },
{},
};
MODULE_DEVICE_TABLE(of, n2rng_match);
diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h
index 4bea07f30978..f244ac89087f 100644
--- a/drivers/char/hw_random/n2rng.h
+++ b/drivers/char/hw_random/n2rng.h
@@ -68,7 +68,7 @@ struct n2rng {
struct platform_device *op;
unsigned long flags;
-#define N2RNG_FLAG_VF 0x00000001 /* Victoria Falls RNG, else N2 */
+#define N2RNG_FLAG_MULTI 0x00000001 /* Multi-unit capable RNG */
#define N2RNG_FLAG_CONTROL 0x00000002 /* Operating in control domain */
#define N2RNG_FLAG_READY 0x00000008 /* Ready for hw-rng layer */
#define N2RNG_FLAG_SHUTDOWN 0x00000010 /* Driver unregistering */
diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c
index dd1d143eb8ea..52e08ca3ccd7 100644
--- a/drivers/char/hw_random/nomadik-rng.c
+++ b/drivers/char/hw_random/nomadik-rng.c
@@ -55,7 +55,7 @@ static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id)
ret = amba_request_regions(dev, dev->dev.init_name);
if (ret)
- return ret;
+ goto out_clk;
ret = -ENOMEM;
base = ioremap(dev->res.start, resource_size(&dev->res));
if (!base)
@@ -70,6 +70,7 @@ out_unmap:
iounmap(base);
out_release:
amba_release_regions(dev);
+out_clk:
clk_disable(rng_clk);
clk_put(rng_clk);
return ret;
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index 2cc755a64302..b757fac3cd1f 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -113,8 +113,10 @@ static int __devinit omap_rng_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOENT;
+ if (!res) {
+ ret = -ENOENT;
+ goto err_region;
+ }
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
ret = -EBUSY;
diff --git a/drivers/char/hw_random/ppc4xx-rng.c b/drivers/char/hw_random/ppc4xx-rng.c
new file mode 100644
index 000000000000..b8afa6a4ff67
--- /dev/null
+++ b/drivers/char/hw_random/ppc4xx-rng.c
@@ -0,0 +1,156 @@
+/*
+ * Generic PowerPC 44x RNG driver
+ *
+ * Copyright 2011 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/delay.h>
+#include <linux/of_platform.h>
+#include <asm/io.h>
+
+#define PPC4XX_TRNG_DEV_CTRL 0x60080
+
+#define PPC4XX_TRNGE 0x00020000
+#define PPC4XX_TRNG_CTRL 0x0008
+#define PPC4XX_TRNG_CTRL_DALM 0x20
+#define PPC4XX_TRNG_STAT 0x0004
+#define PPC4XX_TRNG_STAT_B 0x1
+#define PPC4XX_TRNG_DATA 0x0000
+
+#define MODULE_NAME "ppc4xx_rng"
+
+static int ppc4xx_rng_data_present(struct hwrng *rng, int wait)
+{
+ void __iomem *rng_regs = (void __iomem *) rng->priv;
+ int busy, i, present = 0;
+
+ for (i = 0; i < 20; i++) {
+ busy = (in_le32(rng_regs + PPC4XX_TRNG_STAT) & PPC4XX_TRNG_STAT_B);
+ if (!busy || !wait) {
+ present = 1;
+ break;
+ }
+ udelay(10);
+ }
+ return present;
+}
+
+static int ppc4xx_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ void __iomem *rng_regs = (void __iomem *) rng->priv;
+ *data = in_le32(rng_regs + PPC4XX_TRNG_DATA);
+ return 4;
+}
+
+static int ppc4xx_rng_enable(int enable)
+{
+ struct device_node *ctrl;
+ void __iomem *ctrl_reg;
+ int err = 0;
+ u32 val;
+
+ /* Find the main crypto device node and map it to turn the TRNG on */
+ ctrl = of_find_compatible_node(NULL, NULL, "amcc,ppc4xx-crypto");
+ if (!ctrl)
+ return -ENODEV;
+
+ ctrl_reg = of_iomap(ctrl, 0);
+ if (!ctrl_reg) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ val = in_le32(ctrl_reg + PPC4XX_TRNG_DEV_CTRL);
+
+ if (enable)
+ val |= PPC4XX_TRNGE;
+ else
+ val = val & ~PPC4XX_TRNGE;
+
+ out_le32(ctrl_reg + PPC4XX_TRNG_DEV_CTRL, val);
+ iounmap(ctrl_reg);
+
+out:
+ of_node_put(ctrl);
+
+ return err;
+}
+
+static struct hwrng ppc4xx_rng = {
+ .name = MODULE_NAME,
+ .data_present = ppc4xx_rng_data_present,
+ .data_read = ppc4xx_rng_data_read,
+};
+
+static int __devinit ppc4xx_rng_probe(struct platform_device *dev)
+{
+ void __iomem *rng_regs;
+ int err = 0;
+
+ rng_regs = of_iomap(dev->dev.of_node, 0);
+ if (!rng_regs)
+ return -ENODEV;
+
+ err = ppc4xx_rng_enable(1);
+ if (err)
+ return err;
+
+ out_le32(rng_regs + PPC4XX_TRNG_CTRL, PPC4XX_TRNG_CTRL_DALM);
+ ppc4xx_rng.priv = (unsigned long) rng_regs;
+
+ err = hwrng_register(&ppc4xx_rng);
+
+ return err;
+}
+
+static int __devexit ppc4xx_rng_remove(struct platform_device *dev)
+{
+ void __iomem *rng_regs = (void __iomem *) ppc4xx_rng.priv;
+
+ hwrng_unregister(&ppc4xx_rng);
+ ppc4xx_rng_enable(0);
+ iounmap(rng_regs);
+
+ return 0;
+}
+
+static struct of_device_id ppc4xx_rng_match[] = {
+ { .compatible = "ppc4xx-rng", },
+ { .compatible = "amcc,ppc460ex-rng", },
+ { .compatible = "amcc,ppc440epx-rng", },
+ {},
+};
+
+static struct platform_driver ppc4xx_rng_driver = {
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = ppc4xx_rng_match,
+ },
+ .probe = ppc4xx_rng_probe,
+ .remove = ppc4xx_rng_remove,
+};
+
+static int __init ppc4xx_rng_init(void)
+{
+ return platform_driver_register(&ppc4xx_rng_driver);
+}
+module_init(ppc4xx_rng_init);
+
+static void __exit ppc4xx_rng_exit(void)
+{
+ platform_driver_unregister(&ppc4xx_rng_driver);
+}
+module_exit(ppc4xx_rng_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Josh Boyer <jwboyer@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("HW RNG driver for PPC 4xx processors");
diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c
index a94e930575f2..a8428e6f64a9 100644
--- a/drivers/char/hw_random/timeriomem-rng.c
+++ b/drivers/char/hw_random/timeriomem-rng.c
@@ -100,8 +100,7 @@ static int __devinit timeriomem_rng_probe(struct platform_device *pdev)
timeriomem_rng_data = pdev->dev.platform_data;
- timeriomem_rng_data->address = ioremap(res->start,
- res->end - res->start + 1);
+ timeriomem_rng_data->address = ioremap(res->start, resource_size(res));
if (!timeriomem_rng_data->address)
return -EIO;
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index 75f1cbd61c17..fd699ccecf5b 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -23,6 +23,7 @@
#include <linux/spinlock.h>
#include <linux/virtio.h>
#include <linux/virtio_rng.h>
+#include <linux/module.h>
static struct virtqueue *vq;
static unsigned int data_avail;
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 320668f4c3aa..c2917ffad2c2 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -52,7 +52,7 @@
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/delay.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#ifdef CONFIG_X86
/*
@@ -65,6 +65,7 @@
* mechanism for it at that time.
*/
#include <asm/kdebug.h>
+#include <asm/nmi.h>
#define HAVE_DIE_NMI
#endif
@@ -1077,17 +1078,8 @@ static void ipmi_unregister_watchdog(int ipmi_intf)
#ifdef HAVE_DIE_NMI
static int
-ipmi_nmi(struct notifier_block *self, unsigned long val, void *data)
+ipmi_nmi(unsigned int val, struct pt_regs *regs)
{
- struct die_args *args = data;
-
- if (val != DIE_NMIUNKNOWN)
- return NOTIFY_OK;
-
- /* Hack, if it's a memory or I/O error, ignore it. */
- if (args->err & 0xc0)
- return NOTIFY_OK;
-
/*
* If we get here, it's an NMI that's not a memory or I/O
* error. We can't truly tell if it's from IPMI or not
@@ -1097,15 +1089,15 @@ ipmi_nmi(struct notifier_block *self, unsigned long val, void *data)
if (testing_nmi) {
testing_nmi = 2;
- return NOTIFY_STOP;
+ return NMI_HANDLED;
}
/* If we are not expecting a timeout, ignore it. */
if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE)
- return NOTIFY_OK;
+ return NMI_DONE;
if (preaction_val != WDOG_PRETIMEOUT_NMI)
- return NOTIFY_OK;
+ return NMI_DONE;
/*
* If no one else handled the NMI, we assume it was the IPMI
@@ -1120,12 +1112,8 @@ ipmi_nmi(struct notifier_block *self, unsigned long val, void *data)
panic(PFX "pre-timeout");
}
- return NOTIFY_STOP;
+ return NMI_HANDLED;
}
-
-static struct notifier_block ipmi_nmi_handler = {
- .notifier_call = ipmi_nmi
-};
#endif
static int wdog_reboot_handler(struct notifier_block *this,
@@ -1290,7 +1278,8 @@ static void check_parms(void)
}
}
if (do_nmi && !nmi_handler_registered) {
- rv = register_die_notifier(&ipmi_nmi_handler);
+ rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0,
+ "ipmi");
if (rv) {
printk(KERN_WARNING PFX
"Can't register nmi handler\n");
@@ -1298,7 +1287,7 @@ static void check_parms(void)
} else
nmi_handler_registered = 1;
} else if (!do_nmi && nmi_handler_registered) {
- unregister_die_notifier(&ipmi_nmi_handler);
+ unregister_nmi_handler(NMI_UNKNOWN, "ipmi");
nmi_handler_registered = 0;
}
#endif
@@ -1336,7 +1325,7 @@ static int __init ipmi_wdog_init(void)
if (rv) {
#ifdef HAVE_DIE_NMI
if (nmi_handler_registered)
- unregister_die_notifier(&ipmi_nmi_handler);
+ unregister_nmi_handler(NMI_UNKNOWN, "ipmi");
#endif
atomic_notifier_chain_unregister(&panic_notifier_list,
&wdog_panic_notifier);
@@ -1357,7 +1346,7 @@ static void __exit ipmi_wdog_exit(void)
#ifdef HAVE_DIE_NMI
if (nmi_handler_registered)
- unregister_die_notifier(&ipmi_nmi_handler);
+ unregister_nmi_handler(NMI_UNKNOWN, "ipmi");
#endif
atomic_notifier_chain_unregister(&panic_notifier_list,
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 8fc04b4f311f..145179033716 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -26,6 +26,7 @@
#include <linux/bootmem.h>
#include <linux/splice.h>
#include <linux/pfn.h>
+#include <linux/export.h>
#include <asm/uaccess.h>
#include <asm/io.h>
diff --git a/drivers/char/msm_smd_pkt.c b/drivers/char/msm_smd_pkt.c
index b6f8a65c9960..8eca55deb3a3 100644
--- a/drivers/char/msm_smd_pkt.c
+++ b/drivers/char/msm_smd_pkt.c
@@ -379,9 +379,8 @@ static int __init smd_pkt_init(void)
for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
GFP_KERNEL);
- if (IS_ERR(smd_pkt_devp[i])) {
- r = PTR_ERR(smd_pkt_devp[i]);
- pr_err("kmalloc() failed %d\n", r);
+ if (!smd_pkt_devp[i]) {
+ pr_err("kmalloc() failed\n");
goto clean_cdevs;
}
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
index 25d139c9dbed..5c0d96a820fa 100644
--- a/drivers/char/mspec.c
+++ b/drivers/char/mspec.c
@@ -46,7 +46,7 @@
#include <asm/page.h>
#include <asm/system.h>
#include <asm/pgtable.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/tlbflush.h>
#include <asm/uncached.h>
#include <asm/sn/addrs.h>
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index 166f1e7aaa7e..da3cfee782dc 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -224,6 +224,8 @@ static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
case 2:
offset += NVRAM_BYTES;
break;
+ default:
+ return -EINVAL;
}
return (offset >= 0) ? (file->f_pos = offset) : -EINVAL;
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index 85c004a518ee..6abdde4da2b7 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -22,6 +22,7 @@
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#include <linux/module.h>
#include <asm/lv1call.h>
#include <asm/ps3stor.h>
@@ -101,12 +102,16 @@ static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
mutex_lock(&file->f_mapping->host->i_mutex);
switch (origin) {
+ case 0:
+ break;
case 1:
offset += file->f_pos;
break;
case 2:
offset += dev->regions[dev->region_idx].size*dev->blk_size;
break;
+ default:
+ offset = -1;
}
if (offset < 0) {
res = -EINVAL;
@@ -305,9 +310,14 @@ static int ps3flash_flush(struct file *file, fl_owner_t id)
return ps3flash_writeback(ps3flash_dev);
}
-static int ps3flash_fsync(struct file *file, int datasync)
+static int ps3flash_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
- return ps3flash_writeback(ps3flash_dev);
+ struct inode *inode = file->f_path.dentry->d_inode;
+ int err;
+ mutex_lock(&inode->i_mutex);
+ err = ps3flash_writeback(ps3flash_dev);
+ mutex_unlock(&inode->i_mutex);
+ return err;
}
static irqreturn_t ps3flash_interrupt(int irq, void *data)
diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c
index 1a9f5f6d6ac5..7c7f42a1f880 100644
--- a/drivers/char/ramoops.c
+++ b/drivers/char/ramoops.c
@@ -19,18 +19,27 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/kmsg_dump.h>
#include <linux/time.h>
+#include <linux/err.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <linux/ramoops.h>
#define RAMOOPS_KERNMSG_HDR "===="
+#define MIN_MEM_SIZE 4096UL
-#define RECORD_SIZE 4096UL
+static ulong record_size = MIN_MEM_SIZE;
+module_param(record_size, ulong, 0400);
+MODULE_PARM_DESC(record_size,
+ "size of each dump done on oops/panic");
static ulong mem_address;
module_param(mem_address, ulong, 0400);
@@ -52,10 +61,15 @@ static struct ramoops_context {
void *virt_addr;
phys_addr_t phys_addr;
unsigned long size;
+ unsigned long record_size;
+ int dump_oops;
int count;
int max_count;
} oops_cxt;
+static struct platform_device *dummy;
+static struct ramoops_platform_data *dummy_data;
+
static void ramoops_do_dump(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
const char *s2, unsigned long l2)
@@ -74,13 +88,13 @@ static void ramoops_do_dump(struct kmsg_dumper *dumper,
return;
/* Only dump oopses if dump_oops is set */
- if (reason == KMSG_DUMP_OOPS && !dump_oops)
+ if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops)
return;
- buf = cxt->virt_addr + (cxt->count * RECORD_SIZE);
+ buf = cxt->virt_addr + (cxt->count * cxt->record_size);
buf_orig = buf;
- memset(buf, '\0', RECORD_SIZE);
+ memset(buf, '\0', cxt->record_size);
res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR);
buf += res;
do_gettimeofday(&timestamp);
@@ -88,8 +102,8 @@ static void ramoops_do_dump(struct kmsg_dumper *dumper,
buf += res;
hdr_size = buf - buf_orig;
- l2_cpy = min(l2, RECORD_SIZE - hdr_size);
- l1_cpy = min(l1, RECORD_SIZE - hdr_size - l2_cpy);
+ l2_cpy = min(l2, cxt->record_size - hdr_size);
+ l1_cpy = min(l1, cxt->record_size - hdr_size - l2_cpy);
s2_start = l2 - l2_cpy;
s1_start = l1 - l1_cpy;
@@ -106,44 +120,59 @@ static int __init ramoops_probe(struct platform_device *pdev)
struct ramoops_context *cxt = &oops_cxt;
int err = -EINVAL;
- if (pdata) {
- mem_size = pdata->mem_size;
- mem_address = pdata->mem_address;
+ if (!pdata->mem_size || !pdata->record_size) {
+ pr_err("The memory size and the record size must be "
+ "non-zero\n");
+ goto fail3;
}
- if (!mem_size) {
- printk(KERN_ERR "ramoops: invalid size specification");
+ rounddown_pow_of_two(pdata->mem_size);
+ rounddown_pow_of_two(pdata->record_size);
+
+ /* Check for the minimum memory size */
+ if (pdata->mem_size < MIN_MEM_SIZE &&
+ pdata->record_size < MIN_MEM_SIZE) {
+ pr_err("memory size too small, minium is %lu\n", MIN_MEM_SIZE);
goto fail3;
}
- rounddown_pow_of_two(mem_size);
-
- if (mem_size < RECORD_SIZE) {
- printk(KERN_ERR "ramoops: size too small");
+ if (pdata->mem_size < pdata->record_size) {
+ pr_err("The memory size must be larger than the "
+ "records size\n");
goto fail3;
}
- cxt->max_count = mem_size / RECORD_SIZE;
+ cxt->max_count = pdata->mem_size / pdata->record_size;
cxt->count = 0;
- cxt->size = mem_size;
- cxt->phys_addr = mem_address;
+ cxt->size = pdata->mem_size;
+ cxt->phys_addr = pdata->mem_address;
+ cxt->record_size = pdata->record_size;
+ cxt->dump_oops = pdata->dump_oops;
+ /*
+ * Update the module parameter variables as well so they are visible
+ * through /sys/module/ramoops/parameters/
+ */
+ mem_size = pdata->mem_size;
+ mem_address = pdata->mem_address;
+ record_size = pdata->record_size;
+ dump_oops = pdata->dump_oops;
if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) {
- printk(KERN_ERR "ramoops: request mem region failed");
+ pr_err("request mem region failed\n");
err = -EINVAL;
goto fail3;
}
cxt->virt_addr = ioremap(cxt->phys_addr, cxt->size);
if (!cxt->virt_addr) {
- printk(KERN_ERR "ramoops: ioremap failed");
+ pr_err("ioremap failed\n");
goto fail2;
}
cxt->dump.dump = ramoops_do_dump;
err = kmsg_dump_register(&cxt->dump);
if (err) {
- printk(KERN_ERR "ramoops: registering kmsg dumper failed");
+ pr_err("registering kmsg dumper failed\n");
goto fail1;
}
@@ -162,7 +191,7 @@ static int __exit ramoops_remove(struct platform_device *pdev)
struct ramoops_context *cxt = &oops_cxt;
if (kmsg_dump_unregister(&cxt->dump) < 0)
- printk(KERN_WARNING "ramoops: could not unregister kmsg_dumper");
+ pr_warn("could not unregister kmsg_dumper\n");
iounmap(cxt->virt_addr);
release_mem_region(cxt->phys_addr, cxt->size);
@@ -179,12 +208,39 @@ static struct platform_driver ramoops_driver = {
static int __init ramoops_init(void)
{
- return platform_driver_probe(&ramoops_driver, ramoops_probe);
+ int ret;
+ ret = platform_driver_probe(&ramoops_driver, ramoops_probe);
+ if (ret == -ENODEV) {
+ /*
+ * If we didn't find a platform device, we use module parameters
+ * building platform data on the fly.
+ */
+ pr_info("platform device not found, using module parameters\n");
+ dummy_data = kzalloc(sizeof(struct ramoops_platform_data),
+ GFP_KERNEL);
+ if (!dummy_data)
+ return -ENOMEM;
+ dummy_data->mem_size = mem_size;
+ dummy_data->mem_address = mem_address;
+ dummy_data->record_size = record_size;
+ dummy_data->dump_oops = dump_oops;
+ dummy = platform_create_bundle(&ramoops_driver, ramoops_probe,
+ NULL, 0, dummy_data,
+ sizeof(struct ramoops_platform_data));
+
+ if (IS_ERR(dummy))
+ ret = PTR_ERR(dummy);
+ else
+ ret = 0;
+ }
+
+ return ret;
}
static void __exit ramoops_exit(void)
{
platform_driver_unregister(&ramoops_driver);
+ kfree(dummy_data);
}
module_init(ramoops_init);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index d4ddeba56682..6035ab8d5ef7 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -932,7 +932,21 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
*/
void get_random_bytes(void *buf, int nbytes)
{
- extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0);
+ char *p = buf;
+
+ while (nbytes) {
+ unsigned long v;
+ int chunk = min(nbytes, (int)sizeof(unsigned long));
+
+ if (!arch_get_random_long(&v))
+ break;
+
+ memcpy(p, &v, chunk);
+ p += chunk;
+ nbytes -= chunk;
+ }
+
+ extract_entropy(&nonblocking_pool, p, nbytes, 0, 0);
}
EXPORT_SYMBOL(get_random_bytes);
@@ -1300,330 +1314,14 @@ ctl_table random_table[] = {
};
#endif /* CONFIG_SYSCTL */
-/********************************************************************
- *
- * Random functions for networking
- *
- ********************************************************************/
-
-/*
- * TCP initial sequence number picking. This uses the random number
- * generator to pick an initial secret value. This value is hashed
- * along with the TCP endpoint information to provide a unique
- * starting point for each pair of TCP endpoints. This defeats
- * attacks which rely on guessing the initial TCP sequence number.
- * This algorithm was suggested by Steve Bellovin.
- *
- * Using a very strong hash was taking an appreciable amount of the total
- * TCP connection establishment time, so this is a weaker hash,
- * compensated for by changing the secret periodically.
- */
-
-/* F, G and H are basic MD4 functions: selection, majority, parity */
-#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
-#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-
-/*
- * The generic round function. The application is so specific that
- * we don't bother protecting all the arguments with parens, as is generally
- * good macro practice, in favor of extra legibility.
- * Rotation is separate from addition to prevent recomputation
- */
-#define ROUND(f, a, b, c, d, x, s) \
- (a += f(b, c, d) + x, a = (a << s) | (a >> (32 - s)))
-#define K1 0
-#define K2 013240474631UL
-#define K3 015666365641UL
+static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-
-static __u32 twothirdsMD4Transform(__u32 const buf[4], __u32 const in[12])
+static int __init random_int_secret_init(void)
{
- __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
-
- /* Round 1 */
- ROUND(F, a, b, c, d, in[ 0] + K1, 3);
- ROUND(F, d, a, b, c, in[ 1] + K1, 7);
- ROUND(F, c, d, a, b, in[ 2] + K1, 11);
- ROUND(F, b, c, d, a, in[ 3] + K1, 19);
- ROUND(F, a, b, c, d, in[ 4] + K1, 3);
- ROUND(F, d, a, b, c, in[ 5] + K1, 7);
- ROUND(F, c, d, a, b, in[ 6] + K1, 11);
- ROUND(F, b, c, d, a, in[ 7] + K1, 19);
- ROUND(F, a, b, c, d, in[ 8] + K1, 3);
- ROUND(F, d, a, b, c, in[ 9] + K1, 7);
- ROUND(F, c, d, a, b, in[10] + K1, 11);
- ROUND(F, b, c, d, a, in[11] + K1, 19);
-
- /* Round 2 */
- ROUND(G, a, b, c, d, in[ 1] + K2, 3);
- ROUND(G, d, a, b, c, in[ 3] + K2, 5);
- ROUND(G, c, d, a, b, in[ 5] + K2, 9);
- ROUND(G, b, c, d, a, in[ 7] + K2, 13);
- ROUND(G, a, b, c, d, in[ 9] + K2, 3);
- ROUND(G, d, a, b, c, in[11] + K2, 5);
- ROUND(G, c, d, a, b, in[ 0] + K2, 9);
- ROUND(G, b, c, d, a, in[ 2] + K2, 13);
- ROUND(G, a, b, c, d, in[ 4] + K2, 3);
- ROUND(G, d, a, b, c, in[ 6] + K2, 5);
- ROUND(G, c, d, a, b, in[ 8] + K2, 9);
- ROUND(G, b, c, d, a, in[10] + K2, 13);
-
- /* Round 3 */
- ROUND(H, a, b, c, d, in[ 3] + K3, 3);
- ROUND(H, d, a, b, c, in[ 7] + K3, 9);
- ROUND(H, c, d, a, b, in[11] + K3, 11);
- ROUND(H, b, c, d, a, in[ 2] + K3, 15);
- ROUND(H, a, b, c, d, in[ 6] + K3, 3);
- ROUND(H, d, a, b, c, in[10] + K3, 9);
- ROUND(H, c, d, a, b, in[ 1] + K3, 11);
- ROUND(H, b, c, d, a, in[ 5] + K3, 15);
- ROUND(H, a, b, c, d, in[ 9] + K3, 3);
- ROUND(H, d, a, b, c, in[ 0] + K3, 9);
- ROUND(H, c, d, a, b, in[ 4] + K3, 11);
- ROUND(H, b, c, d, a, in[ 8] + K3, 15);
-
- return buf[1] + b; /* "most hashed" word */
- /* Alternative: return sum of all words? */
-}
-#endif
-
-#undef ROUND
-#undef F
-#undef G
-#undef H
-#undef K1
-#undef K2
-#undef K3
-
-/* This should not be decreased so low that ISNs wrap too fast. */
-#define REKEY_INTERVAL (300 * HZ)
-/*
- * Bit layout of the tcp sequence numbers (before adding current time):
- * bit 24-31: increased after every key exchange
- * bit 0-23: hash(source,dest)
- *
- * The implementation is similar to the algorithm described
- * in the Appendix of RFC 1185, except that
- * - it uses a 1 MHz clock instead of a 250 kHz clock
- * - it performs a rekey every 5 minutes, which is equivalent
- * to a (source,dest) tulple dependent forward jump of the
- * clock by 0..2^(HASH_BITS+1)
- *
- * Thus the average ISN wraparound time is 68 minutes instead of
- * 4.55 hours.
- *
- * SMP cleanup and lock avoidance with poor man's RCU.
- * Manfred Spraul <manfred@colorfullife.com>
- *
- */
-#define COUNT_BITS 8
-#define COUNT_MASK ((1 << COUNT_BITS) - 1)
-#define HASH_BITS 24
-#define HASH_MASK ((1 << HASH_BITS) - 1)
-
-static struct keydata {
- __u32 count; /* already shifted to the final position */
- __u32 secret[12];
-} ____cacheline_aligned ip_keydata[2];
-
-static unsigned int ip_cnt;
-
-static void rekey_seq_generator(struct work_struct *work);
-
-static DECLARE_DELAYED_WORK(rekey_work, rekey_seq_generator);
-
-/*
- * Lock avoidance:
- * The ISN generation runs lockless - it's just a hash over random data.
- * State changes happen every 5 minutes when the random key is replaced.
- * Synchronization is performed by having two copies of the hash function
- * state and rekey_seq_generator always updates the inactive copy.
- * The copy is then activated by updating ip_cnt.
- * The implementation breaks down if someone blocks the thread
- * that processes SYN requests for more than 5 minutes. Should never
- * happen, and even if that happens only a not perfectly compliant
- * ISN is generated, nothing fatal.
- */
-static void rekey_seq_generator(struct work_struct *work)
-{
- struct keydata *keyptr = &ip_keydata[1 ^ (ip_cnt & 1)];
-
- get_random_bytes(keyptr->secret, sizeof(keyptr->secret));
- keyptr->count = (ip_cnt & COUNT_MASK) << HASH_BITS;
- smp_wmb();
- ip_cnt++;
- schedule_delayed_work(&rekey_work,
- round_jiffies_relative(REKEY_INTERVAL));
-}
-
-static inline struct keydata *get_keyptr(void)
-{
- struct keydata *keyptr = &ip_keydata[ip_cnt & 1];
-
- smp_rmb();
-
- return keyptr;
-}
-
-static __init int seqgen_init(void)
-{
- rekey_seq_generator(NULL);
+ get_random_bytes(random_int_secret, sizeof(random_int_secret));
return 0;
}
-late_initcall(seqgen_init);
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-__u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr,
- __be16 sport, __be16 dport)
-{
- __u32 seq;
- __u32 hash[12];
- struct keydata *keyptr = get_keyptr();
-
- /* The procedure is the same as for IPv4, but addresses are longer.
- * Thus we must use twothirdsMD4Transform.
- */
-
- memcpy(hash, saddr, 16);
- hash[4] = ((__force u16)sport << 16) + (__force u16)dport;
- memcpy(&hash[5], keyptr->secret, sizeof(__u32) * 7);
-
- seq = twothirdsMD4Transform((const __u32 *)daddr, hash) & HASH_MASK;
- seq += keyptr->count;
-
- seq += ktime_to_ns(ktime_get_real());
-
- return seq;
-}
-EXPORT_SYMBOL(secure_tcpv6_sequence_number);
-#endif
-
-/* The code below is shamelessly stolen from secure_tcp_sequence_number().
- * All blames to Andrey V. Savochkin <saw@msu.ru>.
- */
-__u32 secure_ip_id(__be32 daddr)
-{
- struct keydata *keyptr;
- __u32 hash[4];
-
- keyptr = get_keyptr();
-
- /*
- * Pick a unique starting offset for each IP destination.
- * The dest ip address is placed in the starting vector,
- * which is then hashed with random data.
- */
- hash[0] = (__force __u32)daddr;
- hash[1] = keyptr->secret[9];
- hash[2] = keyptr->secret[10];
- hash[3] = keyptr->secret[11];
-
- return half_md4_transform(hash, keyptr->secret);
-}
-
-#ifdef CONFIG_INET
-
-__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport)
-{
- __u32 seq;
- __u32 hash[4];
- struct keydata *keyptr = get_keyptr();
-
- /*
- * Pick a unique starting offset for each TCP connection endpoints
- * (saddr, daddr, sport, dport).
- * Note that the words are placed into the starting vector, which is
- * then mixed with a partial MD4 over random data.
- */
- hash[0] = (__force u32)saddr;
- hash[1] = (__force u32)daddr;
- hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
- hash[3] = keyptr->secret[11];
-
- seq = half_md4_transform(hash, keyptr->secret) & HASH_MASK;
- seq += keyptr->count;
- /*
- * As close as possible to RFC 793, which
- * suggests using a 250 kHz clock.
- * Further reading shows this assumes 2 Mb/s networks.
- * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate.
- * For 10 Gb/s Ethernet, a 1 GHz clock should be ok, but
- * we also need to limit the resolution so that the u32 seq
- * overlaps less than one time per MSL (2 minutes).
- * Choosing a clock of 64 ns period is OK. (period of 274 s)
- */
- seq += ktime_to_ns(ktime_get_real()) >> 6;
-
- return seq;
-}
-
-/* Generate secure starting point for ephemeral IPV4 transport port search */
-u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
-{
- struct keydata *keyptr = get_keyptr();
- u32 hash[4];
-
- /*
- * Pick a unique starting offset for each ephemeral port search
- * (saddr, daddr, dport) and 48bits of random data.
- */
- hash[0] = (__force u32)saddr;
- hash[1] = (__force u32)daddr;
- hash[2] = (__force u32)dport ^ keyptr->secret[10];
- hash[3] = keyptr->secret[11];
-
- return half_md4_transform(hash, keyptr->secret);
-}
-EXPORT_SYMBOL_GPL(secure_ipv4_port_ephemeral);
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
- __be16 dport)
-{
- struct keydata *keyptr = get_keyptr();
- u32 hash[12];
-
- memcpy(hash, saddr, 16);
- hash[4] = (__force u32)dport;
- memcpy(&hash[5], keyptr->secret, sizeof(__u32) * 7);
-
- return twothirdsMD4Transform((const __u32 *)daddr, hash);
-}
-#endif
-
-#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
-/* Similar to secure_tcp_sequence_number but generate a 48 bit value
- * bit's 32-47 increase every key exchange
- * 0-31 hash(source, dest)
- */
-u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport)
-{
- u64 seq;
- __u32 hash[4];
- struct keydata *keyptr = get_keyptr();
-
- hash[0] = (__force u32)saddr;
- hash[1] = (__force u32)daddr;
- hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
- hash[3] = keyptr->secret[11];
-
- seq = half_md4_transform(hash, keyptr->secret);
- seq |= ((u64)keyptr->count) << (32 - HASH_BITS);
-
- seq += ktime_to_ns(ktime_get_real());
- seq &= (1ull << 48) - 1;
-
- return seq;
-}
-EXPORT_SYMBOL(secure_dccp_sequence_number);
-#endif
-
-#endif /* CONFIG_INET */
-
+late_initcall(random_int_secret_init);
/*
* Get a random word for internal kernel use only. Similar to urandom but
@@ -1631,17 +1329,20 @@ EXPORT_SYMBOL(secure_dccp_sequence_number);
* value is not cryptographically secure but for several uses the cost of
* depleting entropy is too high
*/
-DEFINE_PER_CPU(__u32 [4], get_random_int_hash);
+DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash);
unsigned int get_random_int(void)
{
- struct keydata *keyptr;
- __u32 *hash = get_cpu_var(get_random_int_hash);
- int ret;
+ __u32 *hash;
+ unsigned int ret;
- keyptr = get_keyptr();
- hash[0] += current->pid + jiffies + get_cycles();
+ if (arch_get_random_int(&ret))
+ return ret;
+
+ hash = get_cpu_var(get_random_int_hash);
- ret = half_md4_transform(hash, keyptr->secret);
+ hash[0] += current->pid + jiffies + get_cycles();
+ md5_transform(hash, random_int_secret);
+ ret = hash[0];
put_cpu_var(get_random_int_hash);
return ret;
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index b33e8ea314ed..b6de2c047145 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -324,13 +324,12 @@ static int __init raw_init(void)
max_raw_minors = MAX_RAW_MINORS;
}
- raw_devices = vmalloc(sizeof(struct raw_device_data) * max_raw_minors);
+ raw_devices = vzalloc(sizeof(struct raw_device_data) * max_raw_minors);
if (!raw_devices) {
printk(KERN_ERR "Not enough memory for raw device structures\n");
ret = -ENOMEM;
goto error;
}
- memset(raw_devices, 0, sizeof(struct raw_device_data) * max_raw_minors);
ret = register_chrdev_region(dev, max_raw_minors, "raw");
if (ret)
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index dfa8b3062fda..ccd124ab7ca7 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -80,6 +80,7 @@
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
#include <asm/current.h>
#include <asm/system.h>
@@ -1195,10 +1196,8 @@ static void rtc_dropped_irq(unsigned long data)
spin_unlock_irq(&rtc_lock);
- if (printk_ratelimit()) {
- printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n",
- freq);
- }
+ printk_ratelimited(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n",
+ freq);
/* Now we have new data */
wake_up_interruptible(&rtc_wait);
diff --git a/drivers/char/scc.h b/drivers/char/scc.h
deleted file mode 100644
index 341b1142bea8..000000000000
--- a/drivers/char/scc.h
+++ /dev/null
@@ -1,613 +0,0 @@
-/*
- * atari_SCC.h: Definitions for the Am8530 Serial Communications Controller
- *
- * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- *
- */
-
-
-#ifndef _SCC_H
-#define _SCC_H
-
-#include <linux/delay.h>
-
-/* Special configuration ioctls for the Atari SCC5380 Serial
- * Communications Controller
- */
-
-/* ioctl command codes */
-
-#define TIOCGATSCC 0x54c0 /* get SCC configuration */
-#define TIOCSATSCC 0x54c1 /* set SCC configuration */
-#define TIOCDATSCC 0x54c2 /* reset configuration to defaults */
-
-/* Clock sources */
-
-#define CLK_RTxC 0
-#define CLK_TRxC 1
-#define CLK_PCLK 2
-
-/* baud_bases for the common clocks in the Atari. These are the real
- * frequencies divided by 16.
- */
-
-#define SCC_BAUD_BASE_TIMC 19200 /* 0.3072 MHz from TT-MFP, Timer C */
-#define SCC_BAUD_BASE_BCLK 153600 /* 2.4576 MHz */
-#define SCC_BAUD_BASE_PCLK4 229500 /* 3.6720 MHz */
-#define SCC_BAUD_BASE_PCLK 503374 /* 8.0539763 MHz */
-#define SCC_BAUD_BASE_NONE 0 /* for not connected or unused
- * clock sources */
-
-/* The SCC clock configuration structure */
-
-struct scc_clock_config {
- unsigned RTxC_base; /* base_baud of RTxC */
- unsigned TRxC_base; /* base_baud of TRxC */
- unsigned PCLK_base; /* base_baud of PCLK, both channels! */
- struct {
- unsigned clksrc; /* CLK_RTxC, CLK_TRxC or CLK_PCLK */
- unsigned divisor; /* divisor for base baud, valid values:
- * see below */
- } baud_table[17]; /* For 50, 75, 110, 135, 150, 200, 300,
- * 600, 1200, 1800, 2400, 4800, 9600,
- * 19200, 38400, 57600 and 115200 bps.
- * The last two could be replaced by
- * other rates > 38400 if they're not
- * possible.
- */
-};
-
-/* The following divisors are valid:
- *
- * - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use
- * the BRG)
- *
- * - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible)
- *
- * - CLK_PCLK: >= 4 and even (no direct modes, only BRG)
- *
- */
-
-struct scc_port {
- struct gs_port gs;
- volatile unsigned char *ctrlp;
- volatile unsigned char *datap;
- int x_char; /* xon/xoff character */
- int c_dcd;
- int channel;
- struct scc_port *port_a; /* Reference to port A and B */
- struct scc_port *port_b; /* structs for reg access */
-};
-
-#define SCC_MAGIC 0x52696368
-
-/***********************************************************************/
-/* */
-/* Register Names */
-/* */
-/***********************************************************************/
-
-/* The SCC documentation gives no explicit names to the registers,
- * they're just called WR0..15 and RR0..15. To make the source code
- * better readable and make the transparent write reg read access (see
- * below) possible, I christen them here with self-invented names.
- * Note that (real) read registers are assigned numbers 16..31. WR7'
- * has number 33.
- */
-
-#define COMMAND_REG 0 /* wo */
-#define INT_AND_DMA_REG 1 /* wo */
-#define INT_VECTOR_REG 2 /* rw, common to both channels */
-#define RX_CTRL_REG 3 /* rw */
-#define AUX1_CTRL_REG 4 /* rw */
-#define TX_CTRL_REG 5 /* rw */
-#define SYNC_ADR_REG 6 /* wo */
-#define SYNC_CHAR_REG 7 /* wo */
-#define SDLC_OPTION_REG 33 /* wo */
-#define TX_DATA_REG 8 /* wo */
-#define MASTER_INT_CTRL 9 /* wo, common to both channels */
-#define AUX2_CTRL_REG 10 /* rw */
-#define CLK_CTRL_REG 11 /* wo */
-#define TIMER_LOW_REG 12 /* rw */
-#define TIMER_HIGH_REG 13 /* rw */
-#define DPLL_CTRL_REG 14 /* wo */
-#define INT_CTRL_REG 15 /* rw */
-
-#define STATUS_REG 16 /* ro */
-#define SPCOND_STATUS_REG 17 /* wo */
-/* RR2 is WR2 for Channel A, Channel B gives vector + current status: */
-#define CURR_VECTOR_REG 18 /* Ch. B only, Ch. A for rw */
-#define INT_PENDING_REG 19 /* Channel A only! */
-/* RR4 is WR4, if b6(MR7') == 1 */
-/* RR5 is WR5, if b6(MR7') == 1 */
-#define FS_FIFO_LOW_REG 22 /* ro */
-#define FS_FIFO_HIGH_REG 23 /* ro */
-#define RX_DATA_REG 24 /* ro */
-/* RR9 is WR3, if b6(MR7') == 1 */
-#define DPLL_STATUS_REG 26 /* ro */
-/* RR11 is WR10, if b6(MR7') == 1 */
-/* RR12 is WR12 */
-/* RR13 is WR13 */
-/* RR14 not present */
-/* RR15 is WR15 */
-
-
-/***********************************************************************/
-/* */
-/* Register Values */
-/* */
-/***********************************************************************/
-
-
-/* WR0: COMMAND_REG "CR" */
-
-#define CR_RX_CRC_RESET 0x40
-#define CR_TX_CRC_RESET 0x80
-#define CR_TX_UNDERRUN_RESET 0xc0
-
-#define CR_EXTSTAT_RESET 0x10
-#define CR_SEND_ABORT 0x18
-#define CR_ENAB_INT_NEXT_RX 0x20
-#define CR_TX_PENDING_RESET 0x28
-#define CR_ERROR_RESET 0x30
-#define CR_HIGHEST_IUS_RESET 0x38
-
-
-/* WR1: INT_AND_DMA_REG "IDR" */
-
-#define IDR_EXTSTAT_INT_ENAB 0x01
-#define IDR_TX_INT_ENAB 0x02
-#define IDR_PARERR_AS_SPCOND 0x04
-
-#define IDR_RX_INT_DISAB 0x00
-#define IDR_RX_INT_FIRST 0x08
-#define IDR_RX_INT_ALL 0x10
-#define IDR_RX_INT_SPCOND 0x18
-#define IDR_RX_INT_MASK 0x18
-
-#define IDR_WAITREQ_RX 0x20
-#define IDR_WAITREQ_IS_REQ 0x40
-#define IDR_WAITREQ_ENAB 0x80
-
-
-/* WR3: RX_CTRL_REG "RCR" */
-
-#define RCR_RX_ENAB 0x01
-#define RCR_DISCARD_SYNC_CHARS 0x02
-#define RCR_ADDR_SEARCH 0x04
-#define RCR_CRC_ENAB 0x08
-#define RCR_SEARCH_MODE 0x10
-#define RCR_AUTO_ENAB_MODE 0x20
-
-#define RCR_CHSIZE_MASK 0xc0
-#define RCR_CHSIZE_5 0x00
-#define RCR_CHSIZE_6 0x40
-#define RCR_CHSIZE_7 0x80
-#define RCR_CHSIZE_8 0xc0
-
-
-/* WR4: AUX1_CTRL_REG "A1CR" */
-
-#define A1CR_PARITY_MASK 0x03
-#define A1CR_PARITY_NONE 0x00
-#define A1CR_PARITY_ODD 0x01
-#define A1CR_PARITY_EVEN 0x03
-
-#define A1CR_MODE_MASK 0x0c
-#define A1CR_MODE_SYNCR 0x00
-#define A1CR_MODE_ASYNC_1 0x04
-#define A1CR_MODE_ASYNC_15 0x08
-#define A1CR_MODE_ASYNC_2 0x0c
-
-#define A1CR_SYNCR_MODE_MASK 0x30
-#define A1CR_SYNCR_MONOSYNC 0x00
-#define A1CR_SYNCR_BISYNC 0x10
-#define A1CR_SYNCR_SDLC 0x20
-#define A1CR_SYNCR_EXTCSYNC 0x30
-
-#define A1CR_CLKMODE_MASK 0xc0
-#define A1CR_CLKMODE_x1 0x00
-#define A1CR_CLKMODE_x16 0x40
-#define A1CR_CLKMODE_x32 0x80
-#define A1CR_CLKMODE_x64 0xc0
-
-
-/* WR5: TX_CTRL_REG "TCR" */
-
-#define TCR_TX_CRC_ENAB 0x01
-#define TCR_RTS 0x02
-#define TCR_USE_CRC_CCITT 0x00
-#define TCR_USE_CRC_16 0x04
-#define TCR_TX_ENAB 0x08
-#define TCR_SEND_BREAK 0x10
-
-#define TCR_CHSIZE_MASK 0x60
-#define TCR_CHSIZE_5 0x00
-#define TCR_CHSIZE_6 0x20
-#define TCR_CHSIZE_7 0x40
-#define TCR_CHSIZE_8 0x60
-
-#define TCR_DTR 0x80
-
-
-/* WR7': SLDC_OPTION_REG "SOR" */
-
-#define SOR_AUTO_TX_ENAB 0x01
-#define SOR_AUTO_EOM_RESET 0x02
-#define SOR_AUTO_RTS_MODE 0x04
-#define SOR_NRZI_DISAB_HIGH 0x08
-#define SOR_ALT_DTRREQ_TIMING 0x10
-#define SOR_READ_CRC_CHARS 0x20
-#define SOR_EXTENDED_REG_ACCESS 0x40
-
-
-/* WR9: MASTER_INT_CTRL "MIC" */
-
-#define MIC_VEC_INCL_STAT 0x01
-#define MIC_NO_VECTOR 0x02
-#define MIC_DISAB_LOWER_CHAIN 0x04
-#define MIC_MASTER_INT_ENAB 0x08
-#define MIC_STATUS_HIGH 0x10
-#define MIC_IGN_INTACK 0x20
-
-#define MIC_NO_RESET 0x00
-#define MIC_CH_A_RESET 0x40
-#define MIC_CH_B_RESET 0x80
-#define MIC_HARD_RESET 0xc0
-
-
-/* WR10: AUX2_CTRL_REG "A2CR" */
-
-#define A2CR_SYNC_6 0x01
-#define A2CR_LOOP_MODE 0x02
-#define A2CR_ABORT_ON_UNDERRUN 0x04
-#define A2CR_MARK_IDLE 0x08
-#define A2CR_GO_ACTIVE_ON_POLL 0x10
-
-#define A2CR_CODING_MASK 0x60
-#define A2CR_CODING_NRZ 0x00
-#define A2CR_CODING_NRZI 0x20
-#define A2CR_CODING_FM1 0x40
-#define A2CR_CODING_FM0 0x60
-
-#define A2CR_PRESET_CRC_1 0x80
-
-
-/* WR11: CLK_CTRL_REG "CCR" */
-
-#define CCR_TRxCOUT_MASK 0x03
-#define CCR_TRxCOUT_XTAL 0x00
-#define CCR_TRxCOUT_TXCLK 0x01
-#define CCR_TRxCOUT_BRG 0x02
-#define CCR_TRxCOUT_DPLL 0x03
-
-#define CCR_TRxC_OUTPUT 0x04
-
-#define CCR_TXCLK_MASK 0x18
-#define CCR_TXCLK_RTxC 0x00
-#define CCR_TXCLK_TRxC 0x08
-#define CCR_TXCLK_BRG 0x10
-#define CCR_TXCLK_DPLL 0x18
-
-#define CCR_RXCLK_MASK 0x60
-#define CCR_RXCLK_RTxC 0x00
-#define CCR_RXCLK_TRxC 0x20
-#define CCR_RXCLK_BRG 0x40
-#define CCR_RXCLK_DPLL 0x60
-
-#define CCR_RTxC_XTAL 0x80
-
-
-/* WR14: DPLL_CTRL_REG "DCR" */
-
-#define DCR_BRG_ENAB 0x01
-#define DCR_BRG_USE_PCLK 0x02
-#define DCR_DTRREQ_IS_REQ 0x04
-#define DCR_AUTO_ECHO 0x08
-#define DCR_LOCAL_LOOPBACK 0x10
-
-#define DCR_DPLL_EDGE_SEARCH 0x20
-#define DCR_DPLL_ERR_RESET 0x40
-#define DCR_DPLL_DISAB 0x60
-#define DCR_DPLL_CLK_BRG 0x80
-#define DCR_DPLL_CLK_RTxC 0xa0
-#define DCR_DPLL_FM 0xc0
-#define DCR_DPLL_NRZI 0xe0
-
-
-/* WR15: INT_CTRL_REG "ICR" */
-
-#define ICR_OPTIONREG_SELECT 0x01
-#define ICR_ENAB_BRG_ZERO_INT 0x02
-#define ICR_USE_FS_FIFO 0x04
-#define ICR_ENAB_DCD_INT 0x08
-#define ICR_ENAB_SYNC_INT 0x10
-#define ICR_ENAB_CTS_INT 0x20
-#define ICR_ENAB_UNDERRUN_INT 0x40
-#define ICR_ENAB_BREAK_INT 0x80
-
-
-/* RR0: STATUS_REG "SR" */
-
-#define SR_CHAR_AVAIL 0x01
-#define SR_BRG_ZERO 0x02
-#define SR_TX_BUF_EMPTY 0x04
-#define SR_DCD 0x08
-#define SR_SYNC_ABORT 0x10
-#define SR_CTS 0x20
-#define SR_TX_UNDERRUN 0x40
-#define SR_BREAK 0x80
-
-
-/* RR1: SPCOND_STATUS_REG "SCSR" */
-
-#define SCSR_ALL_SENT 0x01
-#define SCSR_RESIDUAL_MASK 0x0e
-#define SCSR_PARITY_ERR 0x10
-#define SCSR_RX_OVERRUN 0x20
-#define SCSR_CRC_FRAME_ERR 0x40
-#define SCSR_END_OF_FRAME 0x80
-
-
-/* RR3: INT_PENDING_REG "IPR" */
-
-#define IPR_B_EXTSTAT 0x01
-#define IPR_B_TX 0x02
-#define IPR_B_RX 0x04
-#define IPR_A_EXTSTAT 0x08
-#define IPR_A_TX 0x10
-#define IPR_A_RX 0x20
-
-
-/* RR7: FS_FIFO_HIGH_REG "FFHR" */
-
-#define FFHR_CNT_MASK 0x3f
-#define FFHR_IS_FROM_FIFO 0x40
-#define FFHR_FIFO_OVERRUN 0x80
-
-
-/* RR10: DPLL_STATUS_REG "DSR" */
-
-#define DSR_ON_LOOP 0x02
-#define DSR_ON_LOOP_SENDING 0x10
-#define DSR_TWO_CLK_MISSING 0x40
-#define DSR_ONE_CLK_MISSING 0x80
-
-/***********************************************************************/
-/* */
-/* Register Access */
-/* */
-/***********************************************************************/
-
-
-/* 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 st_mfp.par_dt_reg takes 600ns (sure?) and thus should be
- * quite right
- */
-
-#define scc_reg_delay() \
- do { \
- if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) \
- __asm__ __volatile__ ( " nop; nop"); \
- else if (MACH_IS_ATARI) \
- __asm__ __volatile__ ( "tstb %0" : : "g" (*_scc_del) : "cc" );\
- } while (0)
-
-static unsigned char scc_shadow[2][16];
-
-/* The following functions should relax the somehow complicated
- * register access of the SCC. _SCCwrite() stores all written values
- * (except for WR0 and WR8) in shadow registers for later recall. This
- * removes the burden of remembering written values as needed. The
- * extra work of storing the value doesn't count, since a delay is
- * needed after a SCC access anyway. Additionally, _SCCwrite() manages
- * writes to WR0 and WR8 differently, because these can be accessed
- * directly with less overhead. Another special case are WR7 and WR7'.
- * _SCCwrite automatically checks what of this registers is selected
- * and changes b0 of WR15 if needed.
- *
- * _SCCread() for standard read registers is straightforward, except
- * for RR2 (split into two "virtual" registers: one for the value
- * written to WR2 (from the shadow) and one for the vector including
- * status from RR2, Ch. B) and RR3. The latter must be read from
- * Channel A, because it reads as all zeros on Ch. B. RR0 and RR8 can
- * be accessed directly as before.
- *
- * The two inline function contain complicated switch statements. But
- * I rely on regno and final_delay being constants, so gcc can reduce
- * the whole stuff to just some assembler statements.
- *
- * _SCCwrite and _SCCread aren't intended to be used directly under
- * normal circumstances. The macros SCCread[_ND] and SCCwrite[_ND] are
- * for that purpose. They assume that a local variable 'port' is
- * declared and pointing to the port's scc_struct entry. The
- * variants with "_NB" appended should be used if no other SCC
- * accesses follow immediately (within 0.5 usecs). They just skip the
- * final delay nops.
- *
- * Please note that accesses to SCC registers should only take place
- * when interrupts are turned off (at least if SCC interrupts are
- * enabled). Otherwise, an interrupt could interfere with the
- * two-stage accessing process.
- *
- */
-
-
-static __inline__ void _SCCwrite(
- struct scc_port *port,
- unsigned char *shadow,
- volatile unsigned char *_scc_del,
- int regno,
- unsigned char val, int final_delay )
-{
- switch( regno ) {
-
- case COMMAND_REG:
- /* WR0 can be written directly without pointing */
- *port->ctrlp = val;
- break;
-
- case SYNC_CHAR_REG:
- /* For WR7, first set b0 of WR15 to 0, if needed */
- if (shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT) {
- *port->ctrlp = 15;
- shadow[INT_CTRL_REG] &= ~ICR_OPTIONREG_SELECT;
- scc_reg_delay();
- *port->ctrlp = shadow[INT_CTRL_REG];
- scc_reg_delay();
- }
- goto normal_case;
-
- case SDLC_OPTION_REG:
- /* For WR7', first set b0 of WR15 to 1, if needed */
- if (!(shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT)) {
- *port->ctrlp = 15;
- shadow[INT_CTRL_REG] |= ICR_OPTIONREG_SELECT;
- scc_reg_delay();
- *port->ctrlp = shadow[INT_CTRL_REG];
- scc_reg_delay();
- }
- *port->ctrlp = 7;
- shadow[8] = val; /* WR7' shadowed at WR8 */
- scc_reg_delay();
- *port->ctrlp = val;
- break;
-
- case TX_DATA_REG: /* WR8 */
- /* TX_DATA_REG can be accessed directly on some h/w */
- if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147)
- {
- *port->ctrlp = regno;
- scc_reg_delay();
- *port->ctrlp = val;
- }
- else
- *port->datap = val;
- break;
-
- case MASTER_INT_CTRL:
- *port->ctrlp = regno;
- val &= 0x3f; /* bits 6..7 are the reset commands */
- scc_shadow[0][regno] = val;
- scc_reg_delay();
- *port->ctrlp = val;
- break;
-
- case DPLL_CTRL_REG:
- *port->ctrlp = regno;
- val &= 0x1f; /* bits 5..7 are the DPLL commands */
- shadow[regno] = val;
- scc_reg_delay();
- *port->ctrlp = val;
- break;
-
- case 1 ... 6:
- case 10 ... 13:
- case 15:
- normal_case:
- *port->ctrlp = regno;
- shadow[regno] = val;
- scc_reg_delay();
- *port->ctrlp = val;
- break;
-
- default:
- printk( "Bad SCC write access to WR%d\n", regno );
- break;
-
- }
-
- if (final_delay)
- scc_reg_delay();
-}
-
-
-static __inline__ unsigned char _SCCread(
- struct scc_port *port,
- unsigned char *shadow,
- volatile unsigned char *_scc_del,
- int regno, int final_delay )
-{
- unsigned char rv;
-
- switch( regno ) {
-
- /* --- real read registers --- */
- case STATUS_REG:
- rv = *port->ctrlp;
- break;
-
- case INT_PENDING_REG:
- /* RR3: read only from Channel A! */
- port = port->port_a;
- goto normal_case;
-
- case RX_DATA_REG:
- /* RR8 can be accessed directly on some h/w */
- if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147)
- {
- *port->ctrlp = 8;
- scc_reg_delay();
- rv = *port->ctrlp;
- }
- else
- rv = *port->datap;
- break;
-
- case CURR_VECTOR_REG:
- /* RR2 (vector including status) from Ch. B */
- port = port->port_b;
- goto normal_case;
-
- /* --- reading write registers: access the shadow --- */
- case 1 ... 7:
- case 10 ... 15:
- return shadow[regno]; /* no final delay! */
-
- /* WR7' is special, because it is shadowed at the place of WR8 */
- case SDLC_OPTION_REG:
- return shadow[8]; /* no final delay! */
-
- /* WR9 is special too, because it is common for both channels */
- case MASTER_INT_CTRL:
- return scc_shadow[0][9]; /* no final delay! */
-
- default:
- printk( "Bad SCC read access to %cR%d\n", (regno & 16) ? 'R' : 'W',
- regno & ~16 );
- break;
-
- case SPCOND_STATUS_REG:
- case FS_FIFO_LOW_REG:
- case FS_FIFO_HIGH_REG:
- case DPLL_STATUS_REG:
- normal_case:
- *port->ctrlp = regno & 0x0f;
- scc_reg_delay();
- rv = *port->ctrlp;
- break;
-
- }
-
- if (final_delay)
- scc_reg_delay();
- return rv;
-}
-
-#define SCC_ACCESS_INIT(port) \
- unsigned char *_scc_shadow = &scc_shadow[port->channel][0]
-
-#define SCCwrite(reg,val) _SCCwrite(port,_scc_shadow,scc_del,(reg),(val),1)
-#define SCCwrite_NB(reg,val) _SCCwrite(port,_scc_shadow,scc_del,(reg),(val),0)
-#define SCCread(reg) _SCCread(port,_scc_shadow,scc_del,(reg),1)
-#define SCCread_NB(reg) _SCCread(port,_scc_shadow,scc_del,(reg),0)
-
-#define SCCmod(reg,and,or) SCCwrite((reg),(SCCread(reg)&(and))|(or))
-
-#endif /* _SCC_H */
diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c
new file mode 100644
index 000000000000..cf3ee008dca2
--- /dev/null
+++ b/drivers/char/tile-srom.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright 2011 Tilera 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, version 2.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ *
+ * SPI Flash ROM driver
+ *
+ * This source code is derived from code provided in "Linux Device
+ * Drivers, Third Edition", by Jonathan Corbet, Alessandro Rubini, and
+ * Greg Kroah-Hartman, published by O'Reilly Media, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/slab.h> /* kmalloc() */
+#include <linux/fs.h> /* everything... */
+#include <linux/errno.h> /* error codes */
+#include <linux/types.h> /* size_t */
+#include <linux/proc_fs.h>
+#include <linux/fcntl.h> /* O_ACCMODE */
+#include <linux/aio.h>
+#include <linux/pagemap.h>
+#include <linux/hugetlb.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <hv/hypervisor.h>
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <hv/drv_srom_intf.h>
+
+/*
+ * Size of our hypervisor I/O requests. We break up large transfers
+ * so that we don't spend large uninterrupted spans of time in the
+ * hypervisor. Erasing an SROM sector takes a significant fraction of
+ * a second, so if we allowed the user to, say, do one I/O to write the
+ * entire ROM, we'd get soft lockup timeouts, or worse.
+ */
+#define SROM_CHUNK_SIZE ((size_t)4096)
+
+/*
+ * When hypervisor is busy (e.g. erasing), poll the status periodically.
+ */
+
+/*
+ * Interval to poll the state in msec
+ */
+#define SROM_WAIT_TRY_INTERVAL 20
+
+/*
+ * Maximum times to poll the state
+ */
+#define SROM_MAX_WAIT_TRY_TIMES 1000
+
+struct srom_dev {
+ int hv_devhdl; /* Handle for hypervisor device */
+ u32 total_size; /* Size of this device */
+ u32 sector_size; /* Size of a sector */
+ u32 page_size; /* Size of a page */
+ struct mutex lock; /* Allow only one accessor at a time */
+};
+
+static int srom_major; /* Dynamic major by default */
+module_param(srom_major, int, 0);
+MODULE_AUTHOR("Tilera Corporation");
+MODULE_LICENSE("GPL");
+
+static int srom_devs; /* Number of SROM partitions */
+static struct cdev srom_cdev;
+static struct class *srom_class;
+static struct srom_dev *srom_devices;
+
+/*
+ * Handle calling the hypervisor and managing EAGAIN/EBUSY.
+ */
+
+static ssize_t _srom_read(int hv_devhdl, void *buf,
+ loff_t off, size_t count)
+{
+ int retval, retries = SROM_MAX_WAIT_TRY_TIMES;
+ for (;;) {
+ retval = hv_dev_pread(hv_devhdl, 0, (HV_VirtAddr)buf,
+ count, off);
+ if (retval >= 0)
+ return retval;
+ if (retval == HV_EAGAIN)
+ continue;
+ if (retval == HV_EBUSY && --retries > 0) {
+ msleep(SROM_WAIT_TRY_INTERVAL);
+ continue;
+ }
+ pr_err("_srom_read: error %d\n", retval);
+ return -EIO;
+ }
+}
+
+static ssize_t _srom_write(int hv_devhdl, const void *buf,
+ loff_t off, size_t count)
+{
+ int retval, retries = SROM_MAX_WAIT_TRY_TIMES;
+ for (;;) {
+ retval = hv_dev_pwrite(hv_devhdl, 0, (HV_VirtAddr)buf,
+ count, off);
+ if (retval >= 0)
+ return retval;
+ if (retval == HV_EAGAIN)
+ continue;
+ if (retval == HV_EBUSY && --retries > 0) {
+ msleep(SROM_WAIT_TRY_INTERVAL);
+ continue;
+ }
+ pr_err("_srom_write: error %d\n", retval);
+ return -EIO;
+ }
+}
+
+/**
+ * srom_open() - Device open routine.
+ * @inode: Inode for this device.
+ * @filp: File for this specific open of the device.
+ *
+ * Returns zero, or an error code.
+ */
+static int srom_open(struct inode *inode, struct file *filp)
+{
+ filp->private_data = &srom_devices[iminor(inode)];
+ return 0;
+}
+
+
+/**
+ * srom_release() - Device release routine.
+ * @inode: Inode for this device.
+ * @filp: File for this specific open of the device.
+ *
+ * Returns zero, or an error code.
+ */
+static int srom_release(struct inode *inode, struct file *filp)
+{
+ struct srom_dev *srom = filp->private_data;
+ char dummy;
+
+ /* Make sure we've flushed anything written to the ROM. */
+ mutex_lock(&srom->lock);
+ if (srom->hv_devhdl >= 0)
+ _srom_write(srom->hv_devhdl, &dummy, SROM_FLUSH_OFF, 1);
+ mutex_unlock(&srom->lock);
+
+ filp->private_data = NULL;
+
+ return 0;
+}
+
+
+/**
+ * srom_read() - Read data from the device.
+ * @filp: File for this specific open of the device.
+ * @buf: User's data buffer.
+ * @count: Number of bytes requested.
+ * @f_pos: File position.
+ *
+ * Returns number of bytes read, or an error code.
+ */
+static ssize_t srom_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int retval = 0;
+ void *kernbuf;
+ struct srom_dev *srom = filp->private_data;
+
+ kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL);
+ if (!kernbuf)
+ return -ENOMEM;
+
+ if (mutex_lock_interruptible(&srom->lock)) {
+ retval = -ERESTARTSYS;
+ kfree(kernbuf);
+ return retval;
+ }
+
+ while (count) {
+ int hv_retval;
+ int bytes_this_pass = min(count, SROM_CHUNK_SIZE);
+
+ hv_retval = _srom_read(srom->hv_devhdl, kernbuf,
+ *f_pos, bytes_this_pass);
+ if (hv_retval > 0) {
+ if (copy_to_user(buf, kernbuf, hv_retval) != 0) {
+ retval = -EFAULT;
+ break;
+ }
+ } else if (hv_retval <= 0) {
+ if (retval == 0)
+ retval = hv_retval;
+ break;
+ }
+
+ retval += hv_retval;
+ *f_pos += hv_retval;
+ buf += hv_retval;
+ count -= hv_retval;
+ }
+
+ mutex_unlock(&srom->lock);
+ kfree(kernbuf);
+
+ return retval;
+}
+
+/**
+ * srom_write() - Write data to the device.
+ * @filp: File for this specific open of the device.
+ * @buf: User's data buffer.
+ * @count: Number of bytes requested.
+ * @f_pos: File position.
+ *
+ * Returns number of bytes written, or an error code.
+ */
+static ssize_t srom_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int retval = 0;
+ void *kernbuf;
+ struct srom_dev *srom = filp->private_data;
+
+ kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL);
+ if (!kernbuf)
+ return -ENOMEM;
+
+ if (mutex_lock_interruptible(&srom->lock)) {
+ retval = -ERESTARTSYS;
+ kfree(kernbuf);
+ return retval;
+ }
+
+ while (count) {
+ int hv_retval;
+ int bytes_this_pass = min(count, SROM_CHUNK_SIZE);
+
+ if (copy_from_user(kernbuf, buf, bytes_this_pass) != 0) {
+ retval = -EFAULT;
+ break;
+ }
+
+ hv_retval = _srom_write(srom->hv_devhdl, kernbuf,
+ *f_pos, bytes_this_pass);
+ if (hv_retval <= 0) {
+ if (retval == 0)
+ retval = hv_retval;
+ break;
+ }
+
+ retval += hv_retval;
+ *f_pos += hv_retval;
+ buf += hv_retval;
+ count -= hv_retval;
+ }
+
+ mutex_unlock(&srom->lock);
+ kfree(kernbuf);
+
+ return retval;
+}
+
+/* Provide our own implementation so we can use srom->total_size. */
+loff_t srom_llseek(struct file *filp, loff_t offset, int origin)
+{
+ struct srom_dev *srom = filp->private_data;
+
+ if (mutex_lock_interruptible(&srom->lock))
+ return -ERESTARTSYS;
+
+ switch (origin) {
+ case SEEK_END:
+ offset += srom->total_size;
+ break;
+ case SEEK_CUR:
+ offset += filp->f_pos;
+ break;
+ }
+
+ if (offset < 0 || offset > srom->total_size) {
+ offset = -EINVAL;
+ } else {
+ filp->f_pos = offset;
+ filp->f_version = 0;
+ }
+
+ mutex_unlock(&srom->lock);
+
+ return offset;
+}
+
+static ssize_t total_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct srom_dev *srom = dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", srom->total_size);
+}
+
+static ssize_t sector_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct srom_dev *srom = dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", srom->sector_size);
+}
+
+static ssize_t page_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct srom_dev *srom = dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", srom->page_size);
+}
+
+static struct device_attribute srom_dev_attrs[] = {
+ __ATTR(total_size, S_IRUGO, total_show, NULL),
+ __ATTR(sector_size, S_IRUGO, sector_show, NULL),
+ __ATTR(page_size, S_IRUGO, page_show, NULL),
+ __ATTR_NULL
+};
+
+static char *srom_devnode(struct device *dev, mode_t *mode)
+{
+ *mode = S_IRUGO | S_IWUSR;
+ return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev));
+}
+
+/*
+ * The fops
+ */
+static const struct file_operations srom_fops = {
+ .owner = THIS_MODULE,
+ .llseek = srom_llseek,
+ .read = srom_read,
+ .write = srom_write,
+ .open = srom_open,
+ .release = srom_release,
+};
+
+/**
+ * srom_setup_minor() - Initialize per-minor information.
+ * @srom: Per-device SROM state.
+ * @index: Device to set up.
+ */
+static int srom_setup_minor(struct srom_dev *srom, int index)
+{
+ struct device *dev;
+ int devhdl = srom->hv_devhdl;
+
+ mutex_init(&srom->lock);
+
+ if (_srom_read(devhdl, &srom->total_size,
+ SROM_TOTAL_SIZE_OFF, sizeof(srom->total_size)) < 0)
+ return -EIO;
+ if (_srom_read(devhdl, &srom->sector_size,
+ SROM_SECTOR_SIZE_OFF, sizeof(srom->sector_size)) < 0)
+ return -EIO;
+ if (_srom_read(devhdl, &srom->page_size,
+ SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0)
+ return -EIO;
+
+ dev = device_create(srom_class, &platform_bus,
+ MKDEV(srom_major, index), srom, "%d", index);
+ return IS_ERR(dev) ? PTR_ERR(dev) : 0;
+}
+
+/** srom_init() - Initialize the driver's module. */
+static int srom_init(void)
+{
+ int result, i;
+ dev_t dev = MKDEV(srom_major, 0);
+
+ /*
+ * Start with a plausible number of partitions; the krealloc() call
+ * below will yield about log(srom_devs) additional allocations.
+ */
+ srom_devices = kzalloc(4 * sizeof(struct srom_dev), GFP_KERNEL);
+
+ /* Discover the number of srom partitions. */
+ for (i = 0; ; i++) {
+ int devhdl;
+ char buf[20];
+ struct srom_dev *new_srom_devices =
+ krealloc(srom_devices, (i+1) * sizeof(struct srom_dev),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!new_srom_devices) {
+ result = -ENOMEM;
+ goto fail_mem;
+ }
+ srom_devices = new_srom_devices;
+ sprintf(buf, "srom/0/%d", i);
+ devhdl = hv_dev_open((HV_VirtAddr)buf, 0);
+ if (devhdl < 0) {
+ if (devhdl != HV_ENODEV)
+ pr_notice("srom/%d: hv_dev_open failed: %d.\n",
+ i, devhdl);
+ break;
+ }
+ srom_devices[i].hv_devhdl = devhdl;
+ }
+ srom_devs = i;
+
+ /* Bail out early if we have no partitions at all. */
+ if (srom_devs == 0) {
+ result = -ENODEV;
+ goto fail_mem;
+ }
+
+ /* Register our major, and accept a dynamic number. */
+ if (srom_major)
+ result = register_chrdev_region(dev, srom_devs, "srom");
+ else {
+ result = alloc_chrdev_region(&dev, 0, srom_devs, "srom");
+ srom_major = MAJOR(dev);
+ }
+ if (result < 0)
+ goto fail_mem;
+
+ /* Register a character device. */
+ cdev_init(&srom_cdev, &srom_fops);
+ srom_cdev.owner = THIS_MODULE;
+ srom_cdev.ops = &srom_fops;
+ result = cdev_add(&srom_cdev, dev, srom_devs);
+ if (result < 0)
+ goto fail_chrdev;
+
+ /* Create a sysfs class. */
+ srom_class = class_create(THIS_MODULE, "srom");
+ if (IS_ERR(srom_class)) {
+ result = PTR_ERR(srom_class);
+ goto fail_cdev;
+ }
+ srom_class->dev_attrs = srom_dev_attrs;
+ srom_class->devnode = srom_devnode;
+
+ /* Do per-partition initialization */
+ for (i = 0; i < srom_devs; i++) {
+ result = srom_setup_minor(srom_devices + i, i);
+ if (result < 0)
+ goto fail_class;
+ }
+
+ return 0;
+
+fail_class:
+ for (i = 0; i < srom_devs; i++)
+ device_destroy(srom_class, MKDEV(srom_major, i));
+ class_destroy(srom_class);
+fail_cdev:
+ cdev_del(&srom_cdev);
+fail_chrdev:
+ unregister_chrdev_region(dev, srom_devs);
+fail_mem:
+ kfree(srom_devices);
+ return result;
+}
+
+/** srom_cleanup() - Clean up the driver's module. */
+static void srom_cleanup(void)
+{
+ int i;
+ for (i = 0; i < srom_devs; i++)
+ device_destroy(srom_class, MKDEV(srom_major, i));
+ class_destroy(srom_class);
+ cdev_del(&srom_cdev);
+ unregister_chrdev_region(MKDEV(srom_major, 0), srom_devs);
+ kfree(srom_devices);
+}
+
+module_init(srom_init);
+module_exit(srom_cleanup);
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index f6595aba4f0f..fa567f1158c2 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -43,6 +43,7 @@ config TCG_NSC
config TCG_ATMEL
tristate "Atmel TPM Interface"
+ depends on PPC64 || HAS_IOPORT
---help---
If you have a TPM security chip from Atmel say Yes and it
will be accessible from within Linux. To compile this driver
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index 7beb0e25f1e1..361a1dff8f77 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -383,6 +383,9 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
u32 count, ordinal;
unsigned long stop;
+ if (bufsiz > TPM_BUFSIZE)
+ bufsiz = TPM_BUFSIZE;
+
count = be32_to_cpu(*((__be32 *) (buf + 2)));
ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
if (count == 0)
@@ -534,6 +537,7 @@ void tpm_get_timeouts(struct tpm_chip *chip)
struct duration_t *duration_cap;
ssize_t rc;
u32 timeout;
+ unsigned int scale = 1;
tpm_cmd.header.in = tpm_getcap_header;
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
@@ -545,24 +549,30 @@ void tpm_get_timeouts(struct tpm_chip *chip)
if (rc)
goto duration;
- if (be32_to_cpu(tpm_cmd.header.out.length)
- != 4 * sizeof(u32))
- goto duration;
+ if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
+ be32_to_cpu(tpm_cmd.header.out.length)
+ != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
+ return;
timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
/* Don't overwrite default if value is 0 */
timeout = be32_to_cpu(timeout_cap->a);
+ if (timeout && timeout < 1000) {
+ /* timeouts in msec rather usec */
+ scale = 1000;
+ chip->vendor.timeout_adjusted = true;
+ }
if (timeout)
- chip->vendor.timeout_a = usecs_to_jiffies(timeout);
+ chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
timeout = be32_to_cpu(timeout_cap->b);
if (timeout)
- chip->vendor.timeout_b = usecs_to_jiffies(timeout);
+ chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
timeout = be32_to_cpu(timeout_cap->c);
if (timeout)
- chip->vendor.timeout_c = usecs_to_jiffies(timeout);
+ chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
timeout = be32_to_cpu(timeout_cap->d);
if (timeout)
- chip->vendor.timeout_d = usecs_to_jiffies(timeout);
+ chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
duration:
tpm_cmd.header.in = tpm_getcap_header;
@@ -575,23 +585,31 @@ duration:
if (rc)
return;
- if (be32_to_cpu(tpm_cmd.header.out.return_code)
- != 3 * sizeof(u32))
+ if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
+ be32_to_cpu(tpm_cmd.header.out.length)
+ != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32))
return;
+
duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
chip->vendor.duration[TPM_SHORT] =
usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
+ chip->vendor.duration[TPM_MEDIUM] =
+ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
+ chip->vendor.duration[TPM_LONG] =
+ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
* value wrong and apparently reports msecs rather than usecs. So we
* fix up the resulting too-small TPM_SHORT value to make things work.
+ * We also scale the TPM_MEDIUM and -_LONG values by 1000.
*/
- if (chip->vendor.duration[TPM_SHORT] < (HZ/100))
+ if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
chip->vendor.duration[TPM_SHORT] = HZ;
-
- chip->vendor.duration[TPM_MEDIUM] =
- usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
- chip->vendor.duration[TPM_LONG] =
- usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+ chip->vendor.duration[TPM_MEDIUM] *= 1000;
+ chip->vendor.duration[TPM_LONG] *= 1000;
+ chip->vendor.duration_adjusted = true;
+ dev_info(chip->dev, "Adjusting TPM timeout parameters.");
+ }
}
EXPORT_SYMBOL_GPL(tpm_get_timeouts);
@@ -600,7 +618,7 @@ void tpm_continue_selftest(struct tpm_chip *chip)
u8 data[] = {
0, 193, /* TPM_TAG_RQU_COMMAND */
0, 0, 0, 10, /* length */
- 0, 0, 0, 83, /* TPM_ORD_GetCapability */
+ 0, 0, 0, 83, /* TPM_ORD_ContinueSelfTest */
};
tpm_transmit(chip, data, sizeof(data));
@@ -863,18 +881,24 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
data = tpm_cmd.params.readpubek_out_buffer;
str +=
sprintf(str,
- "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
- "Sigscheme: %02X %02X\nParameters: %02X %02X %02X %02X"
- " %02X %02X %02X %02X %02X %02X %02X %02X\n"
- "Modulus length: %d\nModulus: \n",
- data[10], data[11], data[12], data[13], data[14],
- data[15], data[16], data[17], data[22], data[23],
- data[24], data[25], data[26], data[27], data[28],
- data[29], data[30], data[31], data[32], data[33],
- be32_to_cpu(*((__be32 *) (data + 34))));
+ "Algorithm: %02X %02X %02X %02X\n"
+ "Encscheme: %02X %02X\n"
+ "Sigscheme: %02X %02X\n"
+ "Parameters: %02X %02X %02X %02X "
+ "%02X %02X %02X %02X "
+ "%02X %02X %02X %02X\n"
+ "Modulus length: %d\n"
+ "Modulus:\n",
+ data[0], data[1], data[2], data[3],
+ data[4], data[5],
+ data[6], data[7],
+ data[12], data[13], data[14], data[15],
+ data[16], data[17], data[18], data[19],
+ data[20], data[21], data[22], data[23],
+ be32_to_cpu(*((__be32 *) (data + 24))));
for (i = 0; i < 256; i++) {
- str += sprintf(str, "%02X ", data[i + 38]);
+ str += sprintf(str, "%02X ", data[i + 28]);
if ((i + 1) % 16 == 0)
str += sprintf(str, "\n");
}
@@ -937,6 +961,38 @@ ssize_t tpm_show_caps_1_2(struct device * dev,
}
EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
+ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ if (chip->vendor.duration[TPM_LONG] == 0)
+ return 0;
+
+ return sprintf(buf, "%d %d %d [%s]\n",
+ jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
+ jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
+ jiffies_to_usecs(chip->vendor.duration[TPM_LONG]),
+ chip->vendor.duration_adjusted
+ ? "adjusted" : "original");
+}
+EXPORT_SYMBOL_GPL(tpm_show_durations);
+
+ssize_t tpm_show_timeouts(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d %d %d %d [%s]\n",
+ jiffies_to_usecs(chip->vendor.timeout_a),
+ jiffies_to_usecs(chip->vendor.timeout_b),
+ jiffies_to_usecs(chip->vendor.timeout_c),
+ jiffies_to_usecs(chip->vendor.timeout_d),
+ chip->vendor.timeout_adjusted
+ ? "adjusted" : "original");
+}
+EXPORT_SYMBOL_GPL(tpm_show_timeouts);
+
ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -1052,6 +1108,7 @@ ssize_t tpm_read(struct file *file, char __user *buf,
{
struct tpm_chip *chip = file->private_data;
ssize_t ret_size;
+ int rc;
del_singleshot_timer_sync(&chip->user_read_timer);
flush_work_sync(&chip->work);
@@ -1062,8 +1119,11 @@ ssize_t tpm_read(struct file *file, char __user *buf,
ret_size = size;
mutex_lock(&chip->buffer_mutex);
- if (copy_to_user(buf, chip->data_buffer, ret_size))
+ rc = copy_to_user(buf, chip->data_buffer, ret_size);
+ memset(chip->data_buffer, 0, ret_size);
+ if (rc)
ret_size = -EFAULT;
+
mutex_unlock(&chip->buffer_mutex);
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 72ddb031b69a..9c4163cfa3ce 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -56,6 +56,10 @@ extern ssize_t tpm_show_owned(struct device *, struct device_attribute *attr,
char *);
extern ssize_t tpm_show_temp_deactivated(struct device *,
struct device_attribute *attr, char *);
+extern ssize_t tpm_show_durations(struct device *,
+ struct device_attribute *attr, char *);
+extern ssize_t tpm_show_timeouts(struct device *,
+ struct device_attribute *attr, char *);
struct tpm_chip;
@@ -67,6 +71,7 @@ struct tpm_vendor_specific {
unsigned long base; /* TPM base address */
int irq;
+ int probed_irq;
int region_size;
int have_region;
@@ -81,7 +86,9 @@ struct tpm_vendor_specific {
struct list_head list;
int locality;
unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
+ bool timeout_adjusted;
unsigned long duration[3]; /* jiffies */
+ bool duration_adjusted;
wait_queue_head_t read_queue;
wait_queue_head_t int_queue;
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
index a605cb7dd898..4d2464871ada 100644
--- a/drivers/char/tpm/tpm_nsc.c
+++ b/drivers/char/tpm/tpm_nsc.c
@@ -330,12 +330,12 @@ static int __init init_nsc(void)
pdev->dev.driver = &nsc_drv.driver;
pdev->dev.release = tpm_nsc_remove;
- if ((rc = platform_device_register(pdev)) < 0)
- goto err_free_dev;
+ if ((rc = platform_device_add(pdev)) < 0)
+ goto err_put_dev;
if (request_region(base, 2, "tpm_nsc0") == NULL ) {
rc = -EBUSY;
- goto err_unreg_dev;
+ goto err_del_dev;
}
if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_nsc))) {
@@ -382,10 +382,10 @@ static int __init init_nsc(void)
err_rel_reg:
release_region(base, 2);
-err_unreg_dev:
- platform_device_unregister(pdev);
-err_free_dev:
- kfree(pdev);
+err_del_dev:
+ platform_device_del(pdev);
+err_put_dev:
+ platform_device_put(pdev);
err_unreg_drv:
platform_driver_unregister(&nsc_drv);
return rc;
@@ -396,8 +396,6 @@ static void __exit cleanup_nsc(void)
if (pdev) {
tpm_nsc_remove(&pdev->dev);
platform_device_unregister(pdev);
- kfree(pdev);
- pdev = NULL;
}
platform_driver_unregister(&nsc_drv);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index dd21df55689d..3f4051a7c5a7 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/acpi.h>
+#include <linux/freezer.h>
#include "tpm.h"
#define TPM_HEADER_SIZE 10
@@ -79,7 +80,7 @@ enum tis_defaults {
static LIST_HEAD(tis_chips);
static DEFINE_SPINLOCK(tis_lock);
-#ifdef CONFIG_ACPI
+#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
static int is_itpm(struct pnp_dev *dev)
{
struct acpi_device *acpi = pnp_acpi_device(dev);
@@ -93,7 +94,7 @@ static int is_itpm(struct pnp_dev *dev)
return 0;
}
#else
-static int is_itpm(struct pnp_dev *dev)
+static inline int is_itpm(struct pnp_dev *dev)
{
return 0;
}
@@ -120,7 +121,7 @@ static void release_locality(struct tpm_chip *chip, int l, int force)
static int request_locality(struct tpm_chip *chip, int l)
{
- unsigned long stop;
+ unsigned long stop, timeout;
long rc;
if (check_locality(chip, l) >= 0)
@@ -129,17 +130,25 @@ static int request_locality(struct tpm_chip *chip, int l)
iowrite8(TPM_ACCESS_REQUEST_USE,
chip->vendor.iobase + TPM_ACCESS(l));
+ stop = jiffies + chip->vendor.timeout_a;
+
if (chip->vendor.irq) {
+again:
+ timeout = stop - jiffies;
+ if ((long)timeout <= 0)
+ return -1;
rc = wait_event_interruptible_timeout(chip->vendor.int_queue,
(check_locality
(chip, l) >= 0),
- chip->vendor.timeout_a);
+ timeout);
if (rc > 0)
return l;
-
+ if (rc == -ERESTARTSYS && freezing(current)) {
+ clear_thread_flag(TIF_SIGPENDING);
+ goto again;
+ }
} else {
/* wait for burstcount */
- stop = jiffies + chip->vendor.timeout_a;
do {
if (check_locality(chip, l) >= 0)
return l;
@@ -196,15 +205,24 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
if ((status & mask) == mask)
return 0;
+ stop = jiffies + timeout;
+
if (chip->vendor.irq) {
+again:
+ timeout = stop - jiffies;
+ if ((long)timeout <= 0)
+ return -ETIME;
rc = wait_event_interruptible_timeout(*queue,
((tpm_tis_status
(chip) & mask) ==
mask), timeout);
if (rc > 0)
return 0;
+ if (rc == -ERESTARTSYS && freezing(current)) {
+ clear_thread_flag(TIF_SIGPENDING);
+ goto again;
+ }
} else {
- stop = jiffies + timeout;
do {
msleep(TPM_TIMEOUT);
status = tpm_tis_status(chip);
@@ -288,11 +306,10 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
* tpm.c can skip polling for the data to be available as the interrupt is
* waited for here
*/
-static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
{
int rc, status, burstcnt;
size_t count = 0;
- u32 ordinal;
if (request_locality(chip, 0) < 0)
return -EBUSY;
@@ -327,8 +344,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
/* write last byte */
iowrite8(buf[count],
- chip->vendor.iobase +
- TPM_DATA_FIFO(chip->vendor.locality));
+ chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
&chip->vendor.int_queue);
status = tpm_tis_status(chip);
@@ -337,6 +353,28 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
goto out_err;
}
+ return 0;
+
+out_err:
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality, 0);
+ return rc;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ int rc;
+ u32 ordinal;
+
+ rc = tpm_tis_send_data(chip, buf, len);
+ if (rc < 0)
+ return rc;
+
/* go and do it */
iowrite8(TPM_STS_GO,
chip->vendor.iobase + TPM_STS(chip->vendor.locality));
@@ -358,6 +396,47 @@ out_err:
return rc;
}
+/*
+ * Early probing for iTPM with STS_DATA_EXPECT flaw.
+ * Try sending command without itpm flag set and if that
+ * fails, repeat with itpm flag set.
+ */
+static int probe_itpm(struct tpm_chip *chip)
+{
+ int rc = 0;
+ u8 cmd_getticks[] = {
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a,
+ 0x00, 0x00, 0x00, 0xf1
+ };
+ size_t len = sizeof(cmd_getticks);
+ int rem_itpm = itpm;
+
+ itpm = 0;
+
+ rc = tpm_tis_send_data(chip, cmd_getticks, len);
+ if (rc == 0)
+ goto out;
+
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality, 0);
+
+ itpm = 1;
+
+ rc = tpm_tis_send_data(chip, cmd_getticks, len);
+ if (rc == 0) {
+ dev_info(chip->dev, "Detected an iTPM.\n");
+ rc = 1;
+ } else
+ rc = -EFAULT;
+
+out:
+ itpm = rem_itpm;
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality, 0);
+
+ return rc;
+}
+
static const struct file_operations tis_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -376,6 +455,8 @@ static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
NULL);
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
+static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
+static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
static struct attribute *tis_attrs[] = {
&dev_attr_pubek.attr,
@@ -385,7 +466,9 @@ static struct attribute *tis_attrs[] = {
&dev_attr_owned.attr,
&dev_attr_temp_deactivated.attr,
&dev_attr_caps.attr,
- &dev_attr_cancel.attr, NULL,
+ &dev_attr_cancel.attr,
+ &dev_attr_durations.attr,
+ &dev_attr_timeouts.attr, NULL,
};
static struct attribute_group tis_attr_grp = {
@@ -416,7 +499,7 @@ static irqreturn_t tis_int_probe(int irq, void *dev_id)
if (interrupt == 0)
return IRQ_NONE;
- chip->vendor.irq = irq;
+ chip->vendor.probed_irq = irq;
/* Clear interrupts handled with TPM_EOI */
iowrite32(interrupt,
@@ -464,7 +547,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
resource_size_t len, unsigned int irq)
{
u32 vendor, intfcaps, intmask;
- int rc, i;
+ int rc, i, irq_s, irq_e;
struct tpm_chip *chip;
if (!(chip = tpm_register_hardware(dev, &tpm_tis)))
@@ -493,6 +576,14 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
"1.2 TPM (device-id 0x%X, rev-id %d)\n",
vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
+ if (!itpm) {
+ itpm = probe_itpm(chip);
+ if (itpm < 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+ }
+
if (itpm)
dev_info(dev, "Intel iTPM workaround enabled\n");
@@ -522,6 +613,9 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
dev_dbg(dev, "\tData Avail Int Support\n");
+ /* get the timeouts before testing for irqs */
+ tpm_get_timeouts(chip);
+
/* INTERRUPT Setup */
init_waitqueue_head(&chip->vendor.read_queue);
init_waitqueue_head(&chip->vendor.int_queue);
@@ -540,13 +634,19 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
if (interrupts)
chip->vendor.irq = irq;
if (interrupts && !chip->vendor.irq) {
- chip->vendor.irq =
+ irq_s =
ioread8(chip->vendor.iobase +
TPM_INT_VECTOR(chip->vendor.locality));
+ if (irq_s) {
+ irq_e = irq_s;
+ } else {
+ irq_s = 3;
+ irq_e = 15;
+ }
- for (i = 3; i < 16 && chip->vendor.irq == 0; i++) {
+ for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) {
iowrite8(i, chip->vendor.iobase +
- TPM_INT_VECTOR(chip->vendor.locality));
+ TPM_INT_VECTOR(chip->vendor.locality));
if (request_irq
(i, tis_int_probe, IRQF_SHARED,
chip->vendor.miscdev.name, chip) != 0) {
@@ -568,9 +668,22 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
chip->vendor.iobase +
TPM_INT_ENABLE(chip->vendor.locality));
+ chip->vendor.probed_irq = 0;
+
/* Generate Interrupts */
tpm_gen_interrupt(chip);
+ chip->vendor.irq = chip->vendor.probed_irq;
+
+ /* free_irq will call into tis_int_probe;
+ clear all irqs we haven't seen while doing
+ tpm_gen_interrupt */
+ iowrite32(ioread32
+ (chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality)),
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
/* Turn off */
iowrite32(intmask,
chip->vendor.iobase +
@@ -609,7 +722,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
list_add(&chip->vendor.list, &tis_chips);
spin_unlock(&tis_lock);
- tpm_get_timeouts(chip);
tpm_continue_selftest(chip);
return 0;
@@ -619,6 +731,29 @@ out_err:
tpm_remove_hardware(chip->dev);
return rc;
}
+
+static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
+{
+ u32 intmask;
+
+ /* reenable interrupts that device may have lost or
+ BIOS/firmware may have disabled */
+ iowrite8(chip->vendor.irq, chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+
+ intmask =
+ ioread32(chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ intmask |= TPM_INTF_CMD_READY_INT
+ | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
+ | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
+
+ iowrite32(intmask,
+ chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
+}
+
+
#ifdef CONFIG_PNP
static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
const struct pnp_device_id *pnp_id)
@@ -650,6 +785,9 @@ static int tpm_tis_pnp_resume(struct pnp_dev *dev)
struct tpm_chip *chip = pnp_get_drvdata(dev);
int ret;
+ if (chip->vendor.irq)
+ tpm_tis_reenable_interrupts(chip);
+
ret = tpm_pm_resume(&dev->dev);
if (!ret)
tpm_continue_selftest(chip);
@@ -702,6 +840,11 @@ static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg)
static int tpm_tis_resume(struct platform_device *dev)
{
+ struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
+
+ if (chip->vendor.irq)
+ tpm_tis_reenable_interrupts(chip);
+
return tpm_pm_resume(&dev->dev);
}
static struct platform_driver tis_drv = {
diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c
index a1f68af4ccf4..eedd5474850c 100644
--- a/drivers/char/ttyprintk.c
+++ b/drivers/char/ttyprintk.c
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/serial.h>
#include <linux/tty.h>
+#include <linux/export.h>
struct ttyprintk_port {
struct tty_port port;
@@ -170,7 +171,7 @@ static const struct tty_operations ttyprintk_ops = {
.ioctl = tpk_ioctl,
};
-struct tty_port_operations null_ops = { };
+static struct tty_port_operations null_ops = { };
static struct tty_driver *ttyprintk_driver;
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index fb68b1295373..8e3c46d67cb3 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -19,8 +19,10 @@
*/
#include <linux/cdev.h>
#include <linux/debugfs.h>
+#include <linux/completion.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/freezer.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/list.h>
@@ -32,6 +34,7 @@
#include <linux/virtio_console.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
+#include <linux/module.h>
#include "../tty/hvc/hvc_console.h"
/*
@@ -73,6 +76,7 @@ struct ports_driver_data {
static struct ports_driver_data pdrvdata;
DEFINE_SPINLOCK(pdrvdata_lock);
+DECLARE_COMPLETION(early_console_added);
/* This struct holds information that's relevant only for console ports */
struct console {
@@ -151,6 +155,10 @@ struct ports_device {
int chr_major;
};
+struct port_stats {
+ unsigned long bytes_sent, bytes_received, bytes_discarded;
+};
+
/* This struct holds the per-port data */
struct port {
/* Next port in the list, head is in the ports_device */
@@ -179,6 +187,13 @@ struct port {
struct dentry *debugfs_file;
/*
+ * Keep count of the bytes sent, received and discarded for
+ * this port for accounting and debugging purposes. These
+ * counts are not reset across port open / close events.
+ */
+ struct port_stats stats;
+
+ /*
* The entries in this struct will be valid if this port is
* hooked up to an hvc console
*/
@@ -347,17 +362,19 @@ fail:
}
/* Callers should take appropriate locks */
-static void *get_inbuf(struct port *port)
+static struct port_buffer *get_inbuf(struct port *port)
{
struct port_buffer *buf;
- struct virtqueue *vq;
unsigned int len;
- vq = port->in_vq;
- buf = virtqueue_get_buf(vq, &len);
+ if (port->inbuf)
+ return port->inbuf;
+
+ buf = virtqueue_get_buf(port->in_vq, &len);
if (buf) {
buf->len = len;
buf->offset = 0;
+ port->stats.bytes_received += len;
}
return buf;
}
@@ -384,32 +401,27 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
static void discard_port_data(struct port *port)
{
struct port_buffer *buf;
- struct virtqueue *vq;
- unsigned int len;
- int ret;
+ unsigned int err;
if (!port->portdev) {
/* Device has been unplugged. vqs are already gone. */
return;
}
- vq = port->in_vq;
- if (port->inbuf)
- buf = port->inbuf;
- else
- buf = virtqueue_get_buf(vq, &len);
+ buf = get_inbuf(port);
- ret = 0;
+ err = 0;
while (buf) {
- if (add_inbuf(vq, buf) < 0) {
- ret++;
+ port->stats.bytes_discarded += buf->len - buf->offset;
+ if (add_inbuf(port->in_vq, buf) < 0) {
+ err++;
free_buf(buf);
}
- buf = virtqueue_get_buf(vq, &len);
+ port->inbuf = NULL;
+ buf = get_inbuf(port);
}
- port->inbuf = NULL;
- if (ret)
+ if (err)
dev_warn(port->dev, "Errors adding %d buffers back to vq\n",
- ret);
+ err);
}
static bool port_has_data(struct port *port)
@@ -417,18 +429,12 @@ static bool port_has_data(struct port *port)
unsigned long flags;
bool ret;
+ ret = false;
spin_lock_irqsave(&port->inbuf_lock, flags);
- if (port->inbuf) {
- ret = true;
- goto out;
- }
port->inbuf = get_inbuf(port);
- if (port->inbuf) {
+ if (port->inbuf)
ret = true;
- goto out;
- }
- ret = false;
-out:
+
spin_unlock_irqrestore(&port->inbuf_lock, flags);
return ret;
}
@@ -529,6 +535,8 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count,
cpu_relax();
done:
spin_unlock_irqrestore(&port->outvq_lock, flags);
+
+ port->stats.bytes_sent += in_count;
/*
* We're expected to return the amount of data we wrote -- all
* of it
@@ -633,8 +641,8 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
- ret = wait_event_interruptible(port->waitqueue,
- !will_read_block(port));
+ ret = wait_event_freezable(port->waitqueue,
+ !will_read_block(port));
if (ret < 0)
return ret;
}
@@ -677,8 +685,8 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
if (nonblock)
return -EAGAIN;
- ret = wait_event_interruptible(port->waitqueue,
- !will_write_block(port));
+ ret = wait_event_freezable(port->waitqueue,
+ !will_write_block(port));
if (ret < 0)
return ret;
}
@@ -1059,6 +1067,14 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf,
out_offset += snprintf(buf + out_offset, out_count - out_offset,
"outvq_full: %d\n", port->outvq_full);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "bytes_sent: %lu\n", port->stats.bytes_sent);
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "bytes_received: %lu\n",
+ port->stats.bytes_received);
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "bytes_discarded: %lu\n",
+ port->stats.bytes_discarded);
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
"is_console: %s\n",
is_console_port(port) ? "yes" : "no");
out_offset += snprintf(buf + out_offset, out_count - out_offset,
@@ -1143,6 +1159,7 @@ static int add_port(struct ports_device *portdev, u32 id)
port->cons.ws.ws_row = port->cons.ws.ws_col = 0;
port->host_connected = port->guest_connected = false;
+ port->stats = (struct port_stats) { 0 };
port->outvq_full = false;
@@ -1352,6 +1369,7 @@ static void handle_control_message(struct ports_device *portdev,
break;
init_port_console(port);
+ complete(&early_console_added);
/*
* Could remove the port here in case init fails - but
* have to notify the host first.
@@ -1394,6 +1412,13 @@ static void handle_control_message(struct ports_device *portdev,
break;
case VIRTIO_CONSOLE_PORT_NAME:
/*
+ * If we woke up after hibernation, we can get this
+ * again. Skip it in that case.
+ */
+ if (port->name)
+ break;
+
+ /*
* Skip the size of the header and the cpkt to get the size
* of the name that was sent
*/
@@ -1481,8 +1506,7 @@ static void in_intr(struct virtqueue *vq)
return;
spin_lock_irqsave(&port->inbuf_lock, flags);
- if (!port->inbuf)
- port->inbuf = get_inbuf(port);
+ port->inbuf = get_inbuf(port);
/*
* Don't queue up data when port is closed. This condition
@@ -1563,7 +1587,7 @@ static int init_vqs(struct ports_device *portdev)
portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *),
GFP_KERNEL);
if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs ||
- !portdev->out_vqs) {
+ !portdev->out_vqs) {
err = -ENOMEM;
goto free;
}
@@ -1648,6 +1672,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
struct ports_device *portdev;
int err;
bool multiport;
+ bool early = early_put_chars != NULL;
+
+ /* Ensure to read early_put_chars now */
+ barrier();
portdev = kmalloc(sizeof(*portdev), GFP_KERNEL);
if (!portdev) {
@@ -1675,13 +1703,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
multiport = false;
portdev->config.max_nr_ports = 1;
- if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) {
+ if (virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
+ offsetof(struct virtio_console_config,
+ max_nr_ports),
+ &portdev->config.max_nr_ports) == 0)
multiport = true;
- vdev->config->get(vdev, offsetof(struct virtio_console_config,
- max_nr_ports),
- &portdev->config.max_nr_ports,
- sizeof(portdev->config.max_nr_ports));
- }
err = init_vqs(portdev);
if (err < 0) {
@@ -1719,6 +1745,19 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
VIRTIO_CONSOLE_DEVICE_READY, 1);
+
+ /*
+ * If there was an early virtio console, assume that there are no
+ * other consoles. We need to wait until the hvc_alloc matches the
+ * hvc_instantiate, otherwise tty_open will complain, resulting in
+ * a "Warning: unable to open an initial console" boot failure.
+ * Without multiport this is done in add_port above. With multiport
+ * this might take some host<->guest communication - thus we have to
+ * wait.
+ */
+ if (multiport && early)
+ wait_for_completion(&early_console_added);
+
return 0;
free_vqs:
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index 39ccdeada791..e90e1c74fd4c 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -621,7 +621,7 @@ static int __devinit hwicap_setup(struct device *dev, int id,
drvdata->mem_start = regs_res->start;
drvdata->mem_end = regs_res->end;
- drvdata->mem_size = regs_res->end - regs_res->start + 1;
+ drvdata->mem_size = resource_size(regs_res);
if (!request_mem_region(drvdata->mem_start,
drvdata->mem_size, DRIVER_NAME)) {
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 4168c8896e16..35309274ad68 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -2,3 +2,6 @@
config CLKDEV_LOOKUP
bool
select HAVE_CLK
+
+config HAVE_MACH_CLKDEV
+ bool
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 96c921910469..999d6a03e436 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -1,5 +1,32 @@
config CLKSRC_I8253
bool
+config CLKEVT_I8253
+ bool
+
+config I8253_LOCK
+ bool
+
+config CLKBLD_I8253
+ def_bool y if CLKSRC_I8253 || CLKEVT_I8253 || I8253_LOCK
+
config CLKSRC_MMIO
bool
+
+config DW_APB_TIMER
+ bool
+
+config CLKSRC_DBX500_PRCMU
+ bool "Clocksource PRCMU Timer"
+ depends on UX500_SOC_DB5500 || UX500_SOC_DB8500
+ default y
+ help
+ Use the always on PRCMU Timer as clocksource
+
+config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
+ bool "Clocksource PRCMU Timer sched_clock"
+ depends on (CLKSRC_DBX500_PRCMU && !NOMADIK_MTU_SCHED_CLOCK)
+ select HAVE_SCHED_CLOCK
+ default y
+ help
+ Use the always on PRCMU Timer as sched_clock
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index b995942a5060..8d81a1d32653 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -6,5 +6,7 @@ obj-$(CONFIG_CS5535_CLOCK_EVENT_SRC) += cs5535-clockevt.o
obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o
obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o
obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
-obj-$(CONFIG_CLKSRC_I8253) += i8253.o
+obj-$(CONFIG_CLKBLD_I8253) += i8253.o
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
+obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
+obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o \ No newline at end of file
diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
new file mode 100644
index 000000000000..59feefe0e3e6
--- /dev/null
+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson
+ * Author: Sundar Iyer for ST-Ericsson
+ * sched_clock implementation is based on:
+ * plat-nomadik/timer.c Linus Walleij <linus.walleij@stericsson.com>
+ *
+ * DBx500-PRCMU Timer
+ * The PRCMU has 5 timers which are available in a always-on
+ * power domain. We use the Timer 4 for our always-on clock
+ * source on DB8500 and Timer 3 on DB5500.
+ */
+#include <linux/clockchips.h>
+#include <linux/clksrc-dbx500-prcmu.h>
+
+#include <asm/sched_clock.h>
+
+#include <mach/setup.h>
+#include <mach/hardware.h>
+
+#define RATE_32K 32768
+
+#define TIMER_MODE_CONTINOUS 0x1
+#define TIMER_DOWNCOUNT_VAL 0xffffffff
+
+#define PRCMU_TIMER_REF 0
+#define PRCMU_TIMER_DOWNCOUNT 0x4
+#define PRCMU_TIMER_MODE 0x8
+
+#define SCHED_CLOCK_MIN_WRAP 131072 /* 2^32 / 32768 */
+
+static void __iomem *clksrc_dbx500_timer_base;
+
+static cycle_t clksrc_dbx500_prcmu_read(struct clocksource *cs)
+{
+ u32 count, count2;
+
+ do {
+ count = readl(clksrc_dbx500_timer_base +
+ PRCMU_TIMER_DOWNCOUNT);
+ count2 = readl(clksrc_dbx500_timer_base +
+ PRCMU_TIMER_DOWNCOUNT);
+ } while (count2 != count);
+
+ /* Negate because the timer is a decrementing counter */
+ return ~count;
+}
+
+static struct clocksource clocksource_dbx500_prcmu = {
+ .name = "dbx500-prcmu-timer",
+ .rating = 300,
+ .read = clksrc_dbx500_prcmu_read,
+ .shift = 10,
+ .mask = CLOCKSOURCE_MASK(32),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+#ifdef CONFIG_CLKSRC_DBX500_PRCMU_SCHED_CLOCK
+static DEFINE_CLOCK_DATA(cd);
+
+unsigned long long notrace sched_clock(void)
+{
+ u32 cyc;
+
+ if (unlikely(!clksrc_dbx500_timer_base))
+ return 0;
+
+ cyc = clksrc_dbx500_prcmu_read(&clocksource_dbx500_prcmu);
+
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
+}
+
+static void notrace clksrc_dbx500_prcmu_update_sched_clock(void)
+{
+ u32 cyc = clksrc_dbx500_prcmu_read(&clocksource_dbx500_prcmu);
+ update_sched_clock(&cd, cyc, (u32)~0);
+}
+#endif
+
+void __init clksrc_dbx500_prcmu_init(void __iomem *base)
+{
+ clksrc_dbx500_timer_base = base;
+
+ /*
+ * The A9 sub system expects the timer to be configured as
+ * a continous looping timer.
+ * The PRCMU should configure it but if it for some reason
+ * don't we do it here.
+ */
+ if (readl(clksrc_dbx500_timer_base + PRCMU_TIMER_MODE) !=
+ TIMER_MODE_CONTINOUS) {
+ writel(TIMER_MODE_CONTINOUS,
+ clksrc_dbx500_timer_base + PRCMU_TIMER_MODE);
+ writel(TIMER_DOWNCOUNT_VAL,
+ clksrc_dbx500_timer_base + PRCMU_TIMER_REF);
+ }
+#ifdef CONFIG_CLKSRC_DBX500_PRCMU_SCHED_CLOCK
+ init_sched_clock(&cd, clksrc_dbx500_prcmu_update_sched_clock,
+ 32, RATE_32K);
+#endif
+ clocksource_calc_mult_shift(&clocksource_dbx500_prcmu,
+ RATE_32K, SCHED_CLOCK_MIN_WRAP);
+ clocksource_register(&clocksource_dbx500_prcmu);
+}
diff --git a/drivers/clocksource/dw_apb_timer.c b/drivers/clocksource/dw_apb_timer.c
new file mode 100644
index 000000000000..8c2a35f26d9b
--- /dev/null
+++ b/drivers/clocksource/dw_apb_timer.c
@@ -0,0 +1,401 @@
+/*
+ * (C) Copyright 2009 Intel Corporation
+ * Author: Jacob Pan (jacob.jun.pan@intel.com)
+ *
+ * Shared with ARM platforms, Jamie Iles, Picochip 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Support for the Synopsys DesignWare APB Timers.
+ */
+#include <linux/dw_apb_timer.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define APBT_MIN_PERIOD 4
+#define APBT_MIN_DELTA_USEC 200
+
+#define APBTMR_N_LOAD_COUNT 0x00
+#define APBTMR_N_CURRENT_VALUE 0x04
+#define APBTMR_N_CONTROL 0x08
+#define APBTMR_N_EOI 0x0c
+#define APBTMR_N_INT_STATUS 0x10
+
+#define APBTMRS_INT_STATUS 0xa0
+#define APBTMRS_EOI 0xa4
+#define APBTMRS_RAW_INT_STATUS 0xa8
+#define APBTMRS_COMP_VERSION 0xac
+
+#define APBTMR_CONTROL_ENABLE (1 << 0)
+/* 1: periodic, 0:free running. */
+#define APBTMR_CONTROL_MODE_PERIODIC (1 << 1)
+#define APBTMR_CONTROL_INT (1 << 2)
+
+static inline struct dw_apb_clock_event_device *
+ced_to_dw_apb_ced(struct clock_event_device *evt)
+{
+ return container_of(evt, struct dw_apb_clock_event_device, ced);
+}
+
+static inline struct dw_apb_clocksource *
+clocksource_to_dw_apb_clocksource(struct clocksource *cs)
+{
+ return container_of(cs, struct dw_apb_clocksource, cs);
+}
+
+static unsigned long apbt_readl(struct dw_apb_timer *timer, unsigned long offs)
+{
+ return readl(timer->base + offs);
+}
+
+static void apbt_writel(struct dw_apb_timer *timer, unsigned long val,
+ unsigned long offs)
+{
+ writel(val, timer->base + offs);
+}
+
+static void apbt_disable_int(struct dw_apb_timer *timer)
+{
+ unsigned long ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
+
+ ctrl |= APBTMR_CONTROL_INT;
+ apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
+}
+
+/**
+ * dw_apb_clockevent_pause() - stop the clock_event_device from running
+ *
+ * @dw_ced: The APB clock to stop generating events.
+ */
+void dw_apb_clockevent_pause(struct dw_apb_clock_event_device *dw_ced)
+{
+ disable_irq(dw_ced->timer.irq);
+ apbt_disable_int(&dw_ced->timer);
+}
+
+static void apbt_eoi(struct dw_apb_timer *timer)
+{
+ apbt_readl(timer, APBTMR_N_EOI);
+}
+
+static irqreturn_t dw_apb_clockevent_irq(int irq, void *data)
+{
+ struct clock_event_device *evt = data;
+ struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
+
+ if (!evt->event_handler) {
+ pr_info("Spurious APBT timer interrupt %d", irq);
+ return IRQ_NONE;
+ }
+
+ if (dw_ced->eoi)
+ dw_ced->eoi(&dw_ced->timer);
+
+ evt->event_handler(evt);
+ return IRQ_HANDLED;
+}
+
+static void apbt_enable_int(struct dw_apb_timer *timer)
+{
+ unsigned long ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
+ /* clear pending intr */
+ apbt_readl(timer, APBTMR_N_EOI);
+ ctrl &= ~APBTMR_CONTROL_INT;
+ apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
+}
+
+static void apbt_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ unsigned long ctrl;
+ unsigned long period;
+ struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
+
+ pr_debug("%s CPU %d mode=%d\n", __func__, first_cpu(*evt->cpumask),
+ mode);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ period = DIV_ROUND_UP(dw_ced->timer.freq, HZ);
+ ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
+ ctrl |= APBTMR_CONTROL_MODE_PERIODIC;
+ apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+ /*
+ * DW APB p. 46, have to disable timer before load counter,
+ * may cause sync problem.
+ */
+ ctrl &= ~APBTMR_CONTROL_ENABLE;
+ apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+ udelay(1);
+ pr_debug("Setting clock period %lu for HZ %d\n", period, HZ);
+ apbt_writel(&dw_ced->timer, period, APBTMR_N_LOAD_COUNT);
+ ctrl |= APBTMR_CONTROL_ENABLE;
+ apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
+ /*
+ * set free running mode, this mode will let timer reload max
+ * timeout which will give time (3min on 25MHz clock) to rearm
+ * the next event, therefore emulate the one-shot mode.
+ */
+ ctrl &= ~APBTMR_CONTROL_ENABLE;
+ ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC;
+
+ apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+ /* write again to set free running mode */
+ apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+
+ /*
+ * DW APB p. 46, load counter with all 1s before starting free
+ * running mode.
+ */
+ apbt_writel(&dw_ced->timer, ~0, APBTMR_N_LOAD_COUNT);
+ ctrl &= ~APBTMR_CONTROL_INT;
+ ctrl |= APBTMR_CONTROL_ENABLE;
+ apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+ break;
+
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
+ ctrl &= ~APBTMR_CONTROL_ENABLE;
+ apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+ break;
+
+ case CLOCK_EVT_MODE_RESUME:
+ apbt_enable_int(&dw_ced->timer);
+ break;
+ }
+}
+
+static int apbt_next_event(unsigned long delta,
+ struct clock_event_device *evt)
+{
+ unsigned long ctrl;
+ struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
+
+ /* Disable timer */
+ ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
+ ctrl &= ~APBTMR_CONTROL_ENABLE;
+ apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+ /* write new count */
+ apbt_writel(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT);
+ ctrl |= APBTMR_CONTROL_ENABLE;
+ apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+
+ return 0;
+}
+
+/**
+ * dw_apb_clockevent_init() - use an APB timer as a clock_event_device
+ *
+ * @cpu: The CPU the events will be targeted at.
+ * @name: The name used for the timer and the IRQ for it.
+ * @rating: The rating to give the timer.
+ * @base: I/O base for the timer registers.
+ * @irq: The interrupt number to use for the timer.
+ * @freq: The frequency that the timer counts at.
+ *
+ * This creates a clock_event_device for using with the generic clock layer
+ * but does not start and register it. This should be done with
+ * dw_apb_clockevent_register() as the next step. If this is the first time
+ * it has been called for a timer then the IRQ will be requested, if not it
+ * just be enabled to allow CPU hotplug to avoid repeatedly requesting and
+ * releasing the IRQ.
+ */
+struct dw_apb_clock_event_device *
+dw_apb_clockevent_init(int cpu, const char *name, unsigned rating,
+ void __iomem *base, int irq, unsigned long freq)
+{
+ struct dw_apb_clock_event_device *dw_ced =
+ kzalloc(sizeof(*dw_ced), GFP_KERNEL);
+ int err;
+
+ if (!dw_ced)
+ return NULL;
+
+ dw_ced->timer.base = base;
+ dw_ced->timer.irq = irq;
+ dw_ced->timer.freq = freq;
+
+ clockevents_calc_mult_shift(&dw_ced->ced, freq, APBT_MIN_PERIOD);
+ dw_ced->ced.max_delta_ns = clockevent_delta2ns(0x7fffffff,
+ &dw_ced->ced);
+ dw_ced->ced.min_delta_ns = clockevent_delta2ns(5000, &dw_ced->ced);
+ dw_ced->ced.cpumask = cpumask_of(cpu);
+ dw_ced->ced.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+ dw_ced->ced.set_mode = apbt_set_mode;
+ dw_ced->ced.set_next_event = apbt_next_event;
+ dw_ced->ced.irq = dw_ced->timer.irq;
+ dw_ced->ced.rating = rating;
+ dw_ced->ced.name = name;
+
+ dw_ced->irqaction.name = dw_ced->ced.name;
+ dw_ced->irqaction.handler = dw_apb_clockevent_irq;
+ dw_ced->irqaction.dev_id = &dw_ced->ced;
+ dw_ced->irqaction.irq = irq;
+ dw_ced->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL |
+ IRQF_NOBALANCING |
+ IRQF_DISABLED;
+
+ dw_ced->eoi = apbt_eoi;
+ err = setup_irq(irq, &dw_ced->irqaction);
+ if (err) {
+ pr_err("failed to request timer irq\n");
+ kfree(dw_ced);
+ dw_ced = NULL;
+ }
+
+ return dw_ced;
+}
+
+/**
+ * dw_apb_clockevent_resume() - resume a clock that has been paused.
+ *
+ * @dw_ced: The APB clock to resume.
+ */
+void dw_apb_clockevent_resume(struct dw_apb_clock_event_device *dw_ced)
+{
+ enable_irq(dw_ced->timer.irq);
+}
+
+/**
+ * dw_apb_clockevent_stop() - stop the clock_event_device and release the IRQ.
+ *
+ * @dw_ced: The APB clock to stop generating the events.
+ */
+void dw_apb_clockevent_stop(struct dw_apb_clock_event_device *dw_ced)
+{
+ free_irq(dw_ced->timer.irq, &dw_ced->ced);
+}
+
+/**
+ * dw_apb_clockevent_register() - register the clock with the generic layer
+ *
+ * @dw_ced: The APB clock to register as a clock_event_device.
+ */
+void dw_apb_clockevent_register(struct dw_apb_clock_event_device *dw_ced)
+{
+ apbt_writel(&dw_ced->timer, 0, APBTMR_N_CONTROL);
+ clockevents_register_device(&dw_ced->ced);
+ apbt_enable_int(&dw_ced->timer);
+}
+
+/**
+ * dw_apb_clocksource_start() - start the clocksource counting.
+ *
+ * @dw_cs: The clocksource to start.
+ *
+ * This is used to start the clocksource before registration and can be used
+ * to enable calibration of timers.
+ */
+void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs)
+{
+ /*
+ * start count down from 0xffff_ffff. this is done by toggling the
+ * enable bit then load initial load count to ~0.
+ */
+ unsigned long ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL);
+
+ ctrl &= ~APBTMR_CONTROL_ENABLE;
+ apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL);
+ apbt_writel(&dw_cs->timer, ~0, APBTMR_N_LOAD_COUNT);
+ /* enable, mask interrupt */
+ ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC;
+ ctrl |= (APBTMR_CONTROL_ENABLE | APBTMR_CONTROL_INT);
+ apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL);
+ /* read it once to get cached counter value initialized */
+ dw_apb_clocksource_read(dw_cs);
+}
+
+static cycle_t __apbt_read_clocksource(struct clocksource *cs)
+{
+ unsigned long current_count;
+ struct dw_apb_clocksource *dw_cs =
+ clocksource_to_dw_apb_clocksource(cs);
+
+ current_count = apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE);
+
+ return (cycle_t)~current_count;
+}
+
+static void apbt_restart_clocksource(struct clocksource *cs)
+{
+ struct dw_apb_clocksource *dw_cs =
+ clocksource_to_dw_apb_clocksource(cs);
+
+ dw_apb_clocksource_start(dw_cs);
+}
+
+/**
+ * dw_apb_clocksource_init() - use an APB timer as a clocksource.
+ *
+ * @rating: The rating to give the clocksource.
+ * @name: The name for the clocksource.
+ * @base: The I/O base for the timer registers.
+ * @freq: The frequency that the timer counts at.
+ *
+ * This creates a clocksource using an APB timer but does not yet register it
+ * with the clocksource system. This should be done with
+ * dw_apb_clocksource_register() as the next step.
+ */
+struct dw_apb_clocksource *
+dw_apb_clocksource_init(unsigned rating, const char *name, void __iomem *base,
+ unsigned long freq)
+{
+ struct dw_apb_clocksource *dw_cs = kzalloc(sizeof(*dw_cs), GFP_KERNEL);
+
+ if (!dw_cs)
+ return NULL;
+
+ dw_cs->timer.base = base;
+ dw_cs->timer.freq = freq;
+ dw_cs->cs.name = name;
+ dw_cs->cs.rating = rating;
+ dw_cs->cs.read = __apbt_read_clocksource;
+ dw_cs->cs.mask = CLOCKSOURCE_MASK(32);
+ dw_cs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+ dw_cs->cs.resume = apbt_restart_clocksource;
+
+ return dw_cs;
+}
+
+/**
+ * dw_apb_clocksource_register() - register the APB clocksource.
+ *
+ * @dw_cs: The clocksource to register.
+ */
+void dw_apb_clocksource_register(struct dw_apb_clocksource *dw_cs)
+{
+ clocksource_register_hz(&dw_cs->cs, dw_cs->timer.freq);
+}
+
+/**
+ * dw_apb_clocksource_read() - read the current value of a clocksource.
+ *
+ * @dw_cs: The clocksource to read.
+ */
+cycle_t dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs)
+{
+ return (cycle_t)~apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE);
+}
+
+/**
+ * dw_apb_clocksource_unregister() - unregister and free a clocksource.
+ *
+ * @dw_cs: The clocksource to unregister/free.
+ */
+void dw_apb_clocksource_unregister(struct dw_apb_clocksource *dw_cs)
+{
+ clocksource_unregister(&dw_cs->cs);
+
+ kfree(dw_cs);
+}
diff --git a/drivers/clocksource/i8253.c b/drivers/clocksource/i8253.c
index 225c1761b372..27c49e60b7d6 100644
--- a/drivers/clocksource/i8253.c
+++ b/drivers/clocksource/i8253.c
@@ -1,14 +1,25 @@
/*
* i8253 PIT clocksource
*/
-#include <linux/clocksource.h>
+#include <linux/clockchips.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/timex.h>
+#include <linux/module.h>
+#include <linux/i8253.h>
+#include <linux/smp.h>
-#include <asm/i8253.h>
+/*
+ * Protects access to I/O ports
+ *
+ * 0040-0043 : timer0, i8253 / i8254
+ * 0061-0061 : NMI Control Register which contains two speaker control bits.
+ */
+DEFINE_RAW_SPINLOCK(i8253_lock);
+EXPORT_SYMBOL(i8253_lock);
+#ifdef CONFIG_CLKSRC_I8253
/*
* Since the PIT overflows every tick, its not very useful
* to just read by itself. So use jiffies to emulate a free
@@ -37,15 +48,15 @@ static cycle_t i8253_read(struct clocksource *cs)
* count), it cannot be newer.
*/
jifs = jiffies;
- outb_pit(0x00, PIT_MODE); /* latch the count ASAP */
- count = inb_pit(PIT_CH0); /* read the latched count */
- count |= inb_pit(PIT_CH0) << 8;
+ outb_p(0x00, PIT_MODE); /* latch the count ASAP */
+ count = inb_p(PIT_CH0); /* read the latched count */
+ count |= inb_p(PIT_CH0) << 8;
/* VIA686a test code... reset the latch if count > max + 1 */
if (count > LATCH) {
- outb_pit(0x34, PIT_MODE);
- outb_pit(PIT_LATCH & 0xff, PIT_CH0);
- outb_pit(PIT_LATCH >> 8, PIT_CH0);
+ outb_p(0x34, PIT_MODE);
+ outb_p(PIT_LATCH & 0xff, PIT_CH0);
+ outb_p(PIT_LATCH >> 8, PIT_CH0);
count = PIT_LATCH - 1;
}
@@ -86,3 +97,90 @@ int __init clocksource_i8253_init(void)
{
return clocksource_register_hz(&i8253_cs, PIT_TICK_RATE);
}
+#endif
+
+#ifdef CONFIG_CLKEVT_I8253
+/*
+ * Initialize the PIT timer.
+ *
+ * This is also called after resume to bring the PIT into operation again.
+ */
+static void init_pit_timer(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ raw_spin_lock(&i8253_lock);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ /* binary, mode 2, LSB/MSB, ch 0 */
+ outb_p(0x34, PIT_MODE);
+ outb_p(LATCH & 0xff , PIT_CH0); /* LSB */
+ outb_p(LATCH >> 8 , PIT_CH0); /* MSB */
+ break;
+
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ if (evt->mode == CLOCK_EVT_MODE_PERIODIC ||
+ evt->mode == CLOCK_EVT_MODE_ONESHOT) {
+ outb_p(0x30, PIT_MODE);
+ outb_p(0, PIT_CH0);
+ outb_p(0, PIT_CH0);
+ }
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ /* One shot setup */
+ outb_p(0x38, PIT_MODE);
+ break;
+
+ case CLOCK_EVT_MODE_RESUME:
+ /* Nothing to do here */
+ break;
+ }
+ raw_spin_unlock(&i8253_lock);
+}
+
+/*
+ * Program the next event in oneshot mode
+ *
+ * Delta is given in PIT ticks
+ */
+static int pit_next_event(unsigned long delta, struct clock_event_device *evt)
+{
+ raw_spin_lock(&i8253_lock);
+ outb_p(delta & 0xff , PIT_CH0); /* LSB */
+ outb_p(delta >> 8 , PIT_CH0); /* MSB */
+ raw_spin_unlock(&i8253_lock);
+
+ return 0;
+}
+
+/*
+ * On UP the PIT can serve all of the possible timer functions. On SMP systems
+ * it can be solely used for the global tick.
+ */
+struct clock_event_device i8253_clockevent = {
+ .name = "pit",
+ .features = CLOCK_EVT_FEAT_PERIODIC,
+ .set_mode = init_pit_timer,
+ .set_next_event = pit_next_event,
+};
+
+/*
+ * Initialize the conversion factor and the min/max deltas of the clock event
+ * structure and register the clock event source with the framework.
+ */
+void __init clockevent_i8253_init(bool oneshot)
+{
+ if (oneshot)
+ i8253_clockevent.features |= CLOCK_EVT_FEAT_ONESHOT;
+ /*
+ * Start pit with the boot cpu mask. x86 might make it global
+ * when it is used as broadcast device later.
+ */
+ i8253_clockevent.cpumask = cpumask_of(smp_processor_id());
+
+ clockevents_config_and_register(&i8253_clockevent, PIT_TICK_RATE,
+ 0xF, 0x7FFF);
+}
+#endif
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index dc7c033ef587..ca09bc421ddb 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -26,10 +26,12 @@
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/err.h>
+#include <linux/delay.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/sh_timer.h>
#include <linux/slab.h>
+#include <linux/module.h>
struct sh_cmt_priv {
void __iomem *mapbase;
@@ -150,13 +152,13 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)
static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
{
- int ret;
+ int k, ret;
/* enable clock */
ret = clk_enable(p->clk);
if (ret) {
dev_err(&p->pdev->dev, "cannot enable clock\n");
- return ret;
+ goto err0;
}
/* make sure channel is disabled */
@@ -174,9 +176,38 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
sh_cmt_write(p, CMCOR, 0xffffffff);
sh_cmt_write(p, CMCNT, 0);
+ /*
+ * According to the sh73a0 user's manual, as CMCNT can be operated
+ * only by the RCLK (Pseudo 32 KHz), there's one restriction on
+ * modifying CMCNT register; two RCLK cycles are necessary before
+ * this register is either read or any modification of the value
+ * it holds is reflected in the LSI's actual operation.
+ *
+ * While at it, we're supposed to clear out the CMCNT as of this
+ * moment, so make sure it's processed properly here. This will
+ * take RCLKx2 at maximum.
+ */
+ for (k = 0; k < 100; k++) {
+ if (!sh_cmt_read(p, CMCNT))
+ break;
+ udelay(1);
+ }
+
+ if (sh_cmt_read(p, CMCNT)) {
+ dev_err(&p->pdev->dev, "cannot clear CMCNT\n");
+ ret = -ETIMEDOUT;
+ goto err1;
+ }
+
/* enable channel */
sh_cmt_start_stop_ch(p, 1);
return 0;
+ err1:
+ /* stop clock */
+ clk_disable(p->clk);
+
+ err0:
+ return ret;
}
static void sh_cmt_disable(struct sh_cmt_priv *p)
diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c
index 40630cb98237..db8d5955bad4 100644
--- a/drivers/clocksource/sh_mtu2.c
+++ b/drivers/clocksource/sh_mtu2.c
@@ -30,6 +30,7 @@
#include <linux/clockchips.h>
#include <linux/sh_timer.h>
#include <linux/slab.h>
+#include <linux/module.h>
struct sh_mtu2_priv {
void __iomem *mapbase;
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c
index 808135768617..079e96ad44e8 100644
--- a/drivers/clocksource/sh_tmu.c
+++ b/drivers/clocksource/sh_tmu.c
@@ -31,6 +31,7 @@
#include <linux/clockchips.h>
#include <linux/sh_timer.h>
#include <linux/slab.h>
+#include <linux/module.h>
struct sh_tmu_priv {
void __iomem *mapbase;
diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index 2b46a7efa0ac..77e1e6cd66ce 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -28,7 +28,9 @@
#include <linux/init.h>
#include <linux/connector.h>
#include <linux/gfp.h>
-#include <asm/atomic.h>
+#include <linux/ptrace.h>
+#include <linux/atomic.h>
+
#include <asm/unaligned.h>
#include <linux/cn_proc.h>
@@ -55,6 +57,7 @@ void proc_fork_connector(struct task_struct *task)
struct proc_event *ev;
__u8 buffer[CN_PROC_MSG_SIZE];
struct timespec ts;
+ struct task_struct *parent;
if (atomic_read(&proc_event_num_listeners) < 1)
return;
@@ -65,8 +68,11 @@ void proc_fork_connector(struct task_struct *task)
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
ev->what = PROC_EVENT_FORK;
- ev->event_data.fork.parent_pid = task->real_parent->pid;
- ev->event_data.fork.parent_tgid = task->real_parent->tgid;
+ rcu_read_lock();
+ parent = rcu_dereference(task->real_parent);
+ ev->event_data.fork.parent_pid = parent->pid;
+ ev->event_data.fork.parent_tgid = parent->tgid;
+ rcu_read_unlock();
ev->event_data.fork.child_pid = task->pid;
ev->event_data.fork.child_tgid = task->tgid;
@@ -166,6 +172,65 @@ void proc_sid_connector(struct task_struct *task)
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}
+void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
+{
+ struct cn_msg *msg;
+ struct proc_event *ev;
+ struct timespec ts;
+ __u8 buffer[CN_PROC_MSG_SIZE];
+
+ if (atomic_read(&proc_event_num_listeners) < 1)
+ return;
+
+ msg = (struct cn_msg *)buffer;
+ ev = (struct proc_event *)msg->data;
+ get_seq(&msg->seq, &ev->cpu);
+ ktime_get_ts(&ts); /* get high res monotonic timestamp */
+ put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+ ev->what = PROC_EVENT_PTRACE;
+ ev->event_data.ptrace.process_pid = task->pid;
+ ev->event_data.ptrace.process_tgid = task->tgid;
+ if (ptrace_id == PTRACE_ATTACH) {
+ ev->event_data.ptrace.tracer_pid = current->pid;
+ ev->event_data.ptrace.tracer_tgid = current->tgid;
+ } else if (ptrace_id == PTRACE_DETACH) {
+ ev->event_data.ptrace.tracer_pid = 0;
+ ev->event_data.ptrace.tracer_tgid = 0;
+ } else
+ return;
+
+ memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
+ msg->ack = 0; /* not used */
+ msg->len = sizeof(*ev);
+ cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+}
+
+void proc_comm_connector(struct task_struct *task)
+{
+ struct cn_msg *msg;
+ struct proc_event *ev;
+ struct timespec ts;
+ __u8 buffer[CN_PROC_MSG_SIZE];
+
+ if (atomic_read(&proc_event_num_listeners) < 1)
+ return;
+
+ msg = (struct cn_msg *)buffer;
+ ev = (struct proc_event *)msg->data;
+ get_seq(&msg->seq, &ev->cpu);
+ ktime_get_ts(&ts); /* get high res monotonic timestamp */
+ put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+ ev->what = PROC_EVENT_COMM;
+ ev->event_data.comm.process_pid = task->pid;
+ ev->event_data.comm.process_tgid = task->tgid;
+ get_task_comm(ev->event_data.comm.comm, task);
+
+ memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
+ msg->ack = 0; /* not used */
+ msg->len = sizeof(*ev);
+ cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+}
+
void proc_exit_connector(struct task_struct *task)
{
struct cn_msg *msg;
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 9fb84853d8e3..e24a2a1b6666 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -184,5 +184,15 @@ depends on X86
source "drivers/cpufreq/Kconfig.x86"
endmenu
+menu "ARM CPU frequency scaling drivers"
+depends on ARM
+source "drivers/cpufreq/Kconfig.arm"
+endmenu
+
+menu "PowerPC CPU frequency scaling drivers"
+depends on PPC32 || PPC64
+source "drivers/cpufreq/Kconfig.powerpc"
+endmenu
+
endif
endmenu
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
new file mode 100644
index 000000000000..72a0044c1baa
--- /dev/null
+++ b/drivers/cpufreq/Kconfig.arm
@@ -0,0 +1,32 @@
+#
+# ARM CPU Frequency scaling drivers
+#
+
+config ARM_S3C64XX_CPUFREQ
+ bool "Samsung S3C64XX"
+ depends on CPU_S3C6410
+ default y
+ help
+ This adds the CPUFreq driver for Samsung S3C6410 SoC.
+
+ If in doubt, say N.
+
+config ARM_S5PV210_CPUFREQ
+ bool "Samsung S5PV210 and S5PC110"
+ depends on CPU_S5PV210
+ default y
+ help
+ This adds the CPUFreq driver for Samsung S5PV210 and
+ S5PC110 SoCs.
+
+ If in doubt, say N.
+
+config ARM_EXYNOS4210_CPUFREQ
+ bool "Samsung EXYNOS4210"
+ depends on CPU_EXYNOS4210
+ default y
+ help
+ This adds the CPUFreq driver for Samsung EXYNOS4210
+ SoC (S5PV310 or S5PC210).
+
+ If in doubt, say N.
diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc
new file mode 100644
index 000000000000..e76992f79683
--- /dev/null
+++ b/drivers/cpufreq/Kconfig.powerpc
@@ -0,0 +1,7 @@
+config CPU_FREQ_MAPLE
+ bool "Support for Maple 970FX Evaluation Board"
+ depends on PPC_MAPLE
+ select CPU_FREQ_TABLE
+ help
+ This adds support for frequency switching on Maple 970FX
+ Evaluation Board and compatible boards (IBM JS2x blades).
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index e2fc2d21fa61..a48bc02cd765 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -13,7 +13,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o
# CPUfreq cross-arch helpers
obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o
-##################################################################################d
+##################################################################################
# x86 drivers.
# Link order matters. K8 is preferred to ACPI because of firmware bugs in early
# K8 systems. ACPI is preferred to all other hardware-specific drivers.
@@ -37,7 +37,13 @@ obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o
obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o
obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
-##################################################################################d
-
+##################################################################################
# ARM SoC drivers
obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o
+obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
+obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
+obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o
+
+##################################################################################
+# PowerPC platform drivers
+obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 596d5dd32f41..56c6c6b4eb4d 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -655,7 +655,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
acpi_processor_notify_smm(THIS_MODULE);
/* Check for APERF/MPERF support in hardware */
- if (cpu_has(c, X86_FEATURE_APERFMPERF))
+ if (boot_cpu_has(X86_FEATURE_APERFMPERF))
acpi_cpufreq_driver.getavg = cpufreq_get_measured_perf;
pr_debug("CPU%u - ACPI performance management activated.\n", cpu);
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 0a5bea9e3585..987a165ede26 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1199,6 +1199,26 @@ unsigned int cpufreq_quick_get(unsigned int cpu)
}
EXPORT_SYMBOL(cpufreq_quick_get);
+/**
+ * cpufreq_quick_get_max - get the max reported CPU frequency for this CPU
+ * @cpu: CPU number
+ *
+ * Just return the max possible frequency for a given CPU.
+ */
+unsigned int cpufreq_quick_get_max(unsigned int cpu)
+{
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+ unsigned int ret_freq = 0;
+
+ if (policy) {
+ ret_freq = policy->max;
+ cpufreq_cpu_put(policy);
+ }
+
+ return ret_freq;
+}
+EXPORT_SYMBOL(cpufreq_quick_get_max);
+
static unsigned int __cpufreq_get(unsigned int cpu)
{
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 33b56e5c5c14..c97b468ee9f7 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -120,10 +120,12 @@ static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu,
static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall)
{
- u64 idle_time = get_cpu_idle_time_us(cpu, wall);
+ u64 idle_time = get_cpu_idle_time_us(cpu, NULL);
if (idle_time == -1ULL)
return get_cpu_idle_time_jiffy(cpu, wall);
+ else
+ idle_time += get_cpu_iowait_time_us(cpu, wall);
return idle_time;
}
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 891360edecdd..fa8af4ebb1d6 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -144,10 +144,12 @@ static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu,
static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall)
{
- u64 idle_time = get_cpu_idle_time_us(cpu, wall);
+ u64 idle_time = get_cpu_idle_time_us(cpu, NULL);
if (idle_time == -1ULL)
return get_cpu_idle_time_jiffy(cpu, wall);
+ else
+ idle_time += get_cpu_iowait_time_us(cpu, wall);
return idle_time;
}
@@ -725,7 +727,7 @@ static int __init cpufreq_gov_dbs_init(void)
dbs_tuners_ins.down_differential =
MICRO_FREQUENCY_DOWN_DIFFERENTIAL;
/*
- * In no_hz/micro accounting case we set the minimum frequency
+ * In nohz/micro accounting case we set the minimum frequency
* not depending on HZ, but fixed (very low). The deferred
* timer might skip some samples if idle/sleeping as needed.
*/
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index faf7c5217848..c5072a91e848 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -15,6 +15,7 @@
#include <linux/cpu.h>
#include <linux/sysfs.h>
#include <linux/cpufreq.h>
+#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/percpu.h>
#include <linux/kobject.h>
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c
index d90456a809f9..f5002015d82e 100644
--- a/drivers/cpufreq/db8500-cpufreq.c
+++ b/drivers/cpufreq/db8500-cpufreq.c
@@ -12,30 +12,35 @@
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/mfd/db8500-prcmu.h>
+#include <linux/mfd/dbx500-prcmu.h>
#include <mach/id.h>
static struct cpufreq_frequency_table freq_table[] = {
[0] = {
.index = 0,
- .frequency = 300000,
+ .frequency = 200000,
},
[1] = {
.index = 1,
- .frequency = 600000,
+ .frequency = 300000,
},
[2] = {
- /* Used for MAX_OPP, if available */
.index = 2,
- .frequency = CPUFREQ_TABLE_END,
+ .frequency = 600000,
},
[3] = {
+ /* Used for MAX_OPP, if available */
.index = 3,
.frequency = CPUFREQ_TABLE_END,
},
+ [4] = {
+ .index = 4,
+ .frequency = CPUFREQ_TABLE_END,
+ },
};
static enum arm_opp idx2opp[] = {
+ ARM_EXTCLK,
ARM_50_OPP,
ARM_100_OPP,
ARM_MAX_OPP
@@ -72,13 +77,13 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy,
freqs.old = policy->cur;
freqs.new = freq_table[idx].frequency;
- freqs.cpu = policy->cpu;
if (freqs.old == freqs.new)
return 0;
/* pre-change notification */
- cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ for_each_cpu(freqs.cpu, policy->cpus)
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
/* request the PRCM unit for opp change */
if (prcmu_set_arm_opp(idx2opp[idx])) {
@@ -87,7 +92,8 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy,
}
/* post change notification */
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ for_each_cpu(freqs.cpu, policy->cpus)
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return 0;
}
@@ -103,17 +109,19 @@ static unsigned int db8500_cpufreq_getspeed(unsigned int cpu)
static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
{
- int res;
- int i;
+ int i, res;
BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
- if (cpu_is_u8500v2() && !prcmu_is_u8400()) {
- freq_table[0].frequency = 400000;
- freq_table[1].frequency = 800000;
+ if (!prcmu_is_u8400()) {
+ freq_table[1].frequency = 400000;
+ freq_table[2].frequency = 800000;
if (prcmu_has_arm_maxopp())
- freq_table[2].frequency = 1000000;
+ freq_table[3].frequency = 1000000;
}
+ pr_info("db8500-cpufreq : Available frequencies:\n");
+ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
+ pr_info(" %d Mhz\n", freq_table[i].frequency/1000);
/* get policy fields based on the table */
res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
@@ -127,10 +135,6 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
policy->min = policy->cpuinfo.min_freq;
policy->max = policy->cpuinfo.max_freq;
policy->cur = db8500_cpufreq_getspeed(policy->cpu);
-
- for (i = 0; freq_table[i].frequency != policy->cur; i++)
- ;
-
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
/*
diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c
index 35a257dd4bb7..4bd6815d317b 100644
--- a/drivers/cpufreq/e_powersaver.c
+++ b/drivers/cpufreq/e_powersaver.c
@@ -19,6 +19,11 @@
#include <asm/msr.h>
#include <asm/tsc.h>
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#endif
+
#define EPS_BRAND_C7M 0
#define EPS_BRAND_C7 1
#define EPS_BRAND_EDEN 2
@@ -27,11 +32,59 @@
struct eps_cpu_data {
u32 fsb;
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+ u32 bios_limit;
+#endif
struct cpufreq_frequency_table freq_table[];
};
static struct eps_cpu_data *eps_cpu[NR_CPUS];
+/* Module parameters */
+static int freq_failsafe_off;
+static int voltage_failsafe_off;
+static int set_max_voltage;
+
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+static int ignore_acpi_limit;
+
+static struct acpi_processor_performance *eps_acpi_cpu_perf;
+
+/* Minimum necessary to get acpi_processor_get_bios_limit() working */
+static int eps_acpi_init(void)
+{
+ eps_acpi_cpu_perf = kzalloc(sizeof(struct acpi_processor_performance),
+ GFP_KERNEL);
+ if (!eps_acpi_cpu_perf)
+ return -ENOMEM;
+
+ if (!zalloc_cpumask_var(&eps_acpi_cpu_perf->shared_cpu_map,
+ GFP_KERNEL)) {
+ kfree(eps_acpi_cpu_perf);
+ eps_acpi_cpu_perf = NULL;
+ return -ENOMEM;
+ }
+
+ if (acpi_processor_register_performance(eps_acpi_cpu_perf, 0)) {
+ free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
+ kfree(eps_acpi_cpu_perf);
+ eps_acpi_cpu_perf = NULL;
+ return -EIO;
+ }
+ return 0;
+}
+
+static int eps_acpi_exit(struct cpufreq_policy *policy)
+{
+ if (eps_acpi_cpu_perf) {
+ acpi_processor_unregister_performance(eps_acpi_cpu_perf, 0);
+ free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
+ kfree(eps_acpi_cpu_perf);
+ eps_acpi_cpu_perf = NULL;
+ }
+ return 0;
+}
+#endif
static unsigned int eps_get(unsigned int cpu)
{
@@ -164,6 +217,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
int k, step, voltage;
int ret;
int states;
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+ unsigned int limit;
+#endif
if (policy->cpu != 0)
return -ENODEV;
@@ -244,11 +300,62 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
return -EINVAL;
if (current_voltage > 0x1f || max_voltage > 0x1f)
return -EINVAL;
- if (max_voltage < min_voltage)
+ if (max_voltage < min_voltage
+ || current_voltage < min_voltage
+ || current_voltage > max_voltage)
return -EINVAL;
+ /* Check for systems using underclocked CPU */
+ if (!freq_failsafe_off && max_multiplier != current_multiplier) {
+ printk(KERN_INFO "eps: Your processor is running at different "
+ "frequency then its maximum. Aborting.\n");
+ printk(KERN_INFO "eps: You can use freq_failsafe_off option "
+ "to disable this check.\n");
+ return -EINVAL;
+ }
+ if (!voltage_failsafe_off && max_voltage != current_voltage) {
+ printk(KERN_INFO "eps: Your processor is running at different "
+ "voltage then its maximum. Aborting.\n");
+ printk(KERN_INFO "eps: You can use voltage_failsafe_off "
+ "option to disable this check.\n");
+ return -EINVAL;
+ }
+
/* Calc FSB speed */
fsb = cpu_khz / current_multiplier;
+
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+ /* Check for ACPI processor speed limit */
+ if (!ignore_acpi_limit && !eps_acpi_init()) {
+ if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) {
+ printk(KERN_INFO "eps: ACPI limit %u.%uGHz\n",
+ limit/1000000,
+ (limit%1000000)/10000);
+ eps_acpi_exit(policy);
+ /* Check if max_multiplier is in BIOS limits */
+ if (limit && max_multiplier * fsb > limit) {
+ printk(KERN_INFO "eps: Aborting.\n");
+ return -EINVAL;
+ }
+ }
+ }
+#endif
+
+ /* Allow user to set lower maximum voltage then that reported
+ * by processor */
+ if (brand == EPS_BRAND_C7M && set_max_voltage) {
+ u32 v;
+
+ /* Change mV to something hardware can use */
+ v = (set_max_voltage - 700) / 16;
+ /* Check if voltage is within limits */
+ if (v >= min_voltage && v <= max_voltage) {
+ printk(KERN_INFO "eps: Setting %dmV as maximum.\n",
+ v * 16 + 700);
+ max_voltage = v;
+ }
+ }
+
/* Calc number of p-states supported */
if (brand == EPS_BRAND_C7M)
states = max_multiplier - min_multiplier + 1;
@@ -265,6 +372,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
/* Copy basic values */
centaur->fsb = fsb;
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+ centaur->bios_limit = limit;
+#endif
/* Fill frequency and MSR value table */
f_table = &centaur->freq_table[0];
@@ -303,17 +413,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
static int eps_cpu_exit(struct cpufreq_policy *policy)
{
unsigned int cpu = policy->cpu;
- struct eps_cpu_data *centaur;
- u32 lo, hi;
- if (eps_cpu[cpu] == NULL)
- return -ENODEV;
- centaur = eps_cpu[cpu];
-
- /* Get max frequency */
- rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
- /* Set max frequency */
- eps_set_state(centaur, cpu, hi & 0xffff);
/* Bye */
cpufreq_frequency_table_put_attr(policy->cpu);
kfree(eps_cpu[cpu]);
@@ -359,6 +459,19 @@ static void __exit eps_exit(void)
cpufreq_unregister_driver(&eps_driver);
}
+/* Allow user to overclock his machine or to change frequency to higher after
+ * unloading module */
+module_param(freq_failsafe_off, int, 0644);
+MODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check");
+module_param(voltage_failsafe_off, int, 0644);
+MODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check");
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+module_param(ignore_acpi_limit, int, 0644);
+MODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit");
+#endif
+module_param(set_max_voltage, int, 0644);
+MODULE_PARM_DESC(set_max_voltage, "Set maximum CPU voltage (mV) C7-M only");
+
MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c
new file mode 100644
index 000000000000..ab9741fab92e
--- /dev/null
+++ b/drivers/cpufreq/exynos4210-cpufreq.c
@@ -0,0 +1,687 @@
+/*
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * EXYNOS4 - CPU frequency scaling support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/cpufreq.h>
+#include <linux/notifier.h>
+#include <linux/suspend.h>
+
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+#include <mach/regs-mem.h>
+
+#include <plat/clock.h>
+#include <plat/pm.h>
+
+static struct clk *cpu_clk;
+static struct clk *moutcore;
+static struct clk *mout_mpll;
+static struct clk *mout_apll;
+
+static struct regulator *arm_regulator;
+static struct regulator *int_regulator;
+
+static struct cpufreq_freqs freqs;
+static unsigned int memtype;
+
+static unsigned int locking_frequency;
+static bool frequency_locked;
+static DEFINE_MUTEX(cpufreq_lock);
+
+enum exynos4_memory_type {
+ DDR2 = 4,
+ LPDDR2,
+ DDR3,
+};
+
+enum cpufreq_level_index {
+ L0, L1, L2, L3, CPUFREQ_LEVEL_END,
+};
+
+static struct cpufreq_frequency_table exynos4_freq_table[] = {
+ {L0, 1000*1000},
+ {L1, 800*1000},
+ {L2, 400*1000},
+ {L3, 100*1000},
+ {0, CPUFREQ_TABLE_END},
+};
+
+static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END][7] = {
+ /*
+ * Clock divider value for following
+ * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH,
+ * DIVATB, DIVPCLK_DBG, DIVAPLL }
+ */
+
+ /* ARM L0: 1000MHz */
+ { 0, 3, 7, 3, 3, 0, 1 },
+
+ /* ARM L1: 800MHz */
+ { 0, 3, 7, 3, 3, 0, 1 },
+
+ /* ARM L2: 400MHz */
+ { 0, 1, 3, 1, 3, 0, 1 },
+
+ /* ARM L3: 100MHz */
+ { 0, 0, 1, 0, 3, 1, 1 },
+};
+
+static unsigned int clkdiv_cpu1[CPUFREQ_LEVEL_END][2] = {
+ /*
+ * Clock divider value for following
+ * { DIVCOPY, DIVHPM }
+ */
+
+ /* ARM L0: 1000MHz */
+ { 3, 0 },
+
+ /* ARM L1: 800MHz */
+ { 3, 0 },
+
+ /* ARM L2: 400MHz */
+ { 3, 0 },
+
+ /* ARM L3: 100MHz */
+ { 3, 0 },
+};
+
+static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END][8] = {
+ /*
+ * Clock divider value for following
+ * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD
+ * DIVDMCP, DIVCOPY2, DIVCORE_TIMERS }
+ */
+
+ /* DMC L0: 400MHz */
+ { 3, 1, 1, 1, 1, 1, 3, 1 },
+
+ /* DMC L1: 400MHz */
+ { 3, 1, 1, 1, 1, 1, 3, 1 },
+
+ /* DMC L2: 266.7MHz */
+ { 7, 1, 1, 2, 1, 1, 3, 1 },
+
+ /* DMC L3: 200MHz */
+ { 7, 1, 1, 3, 1, 1, 3, 1 },
+};
+
+static unsigned int clkdiv_top[CPUFREQ_LEVEL_END][5] = {
+ /*
+ * Clock divider value for following
+ * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND }
+ */
+
+ /* ACLK200 L0: 200MHz */
+ { 3, 7, 4, 5, 1 },
+
+ /* ACLK200 L1: 200MHz */
+ { 3, 7, 4, 5, 1 },
+
+ /* ACLK200 L2: 160MHz */
+ { 4, 7, 5, 7, 1 },
+
+ /* ACLK200 L3: 133.3MHz */
+ { 5, 7, 7, 7, 1 },
+};
+
+static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END][2] = {
+ /*
+ * Clock divider value for following
+ * { DIVGDL/R, DIVGPL/R }
+ */
+
+ /* ACLK_GDL/R L0: 200MHz */
+ { 3, 1 },
+
+ /* ACLK_GDL/R L1: 200MHz */
+ { 3, 1 },
+
+ /* ACLK_GDL/R L2: 160MHz */
+ { 4, 1 },
+
+ /* ACLK_GDL/R L3: 133.3MHz */
+ { 5, 1 },
+};
+
+struct cpufreq_voltage_table {
+ unsigned int index; /* any */
+ unsigned int arm_volt; /* uV */
+ unsigned int int_volt;
+};
+
+static struct cpufreq_voltage_table exynos4_volt_table[CPUFREQ_LEVEL_END] = {
+ {
+ .index = L0,
+ .arm_volt = 1200000,
+ .int_volt = 1100000,
+ }, {
+ .index = L1,
+ .arm_volt = 1100000,
+ .int_volt = 1100000,
+ }, {
+ .index = L2,
+ .arm_volt = 1000000,
+ .int_volt = 1000000,
+ }, {
+ .index = L3,
+ .arm_volt = 900000,
+ .int_volt = 1000000,
+ },
+};
+
+static unsigned int exynos4_apll_pms_table[CPUFREQ_LEVEL_END] = {
+ /* APLL FOUT L0: 1000MHz */
+ ((250 << 16) | (6 << 8) | 1),
+
+ /* APLL FOUT L1: 800MHz */
+ ((200 << 16) | (6 << 8) | 1),
+
+ /* APLL FOUT L2 : 400MHz */
+ ((200 << 16) | (6 << 8) | 2),
+
+ /* APLL FOUT L3: 100MHz */
+ ((200 << 16) | (6 << 8) | 4),
+};
+
+static int exynos4_verify_speed(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, exynos4_freq_table);
+}
+
+static unsigned int exynos4_getspeed(unsigned int cpu)
+{
+ return clk_get_rate(cpu_clk) / 1000;
+}
+
+static void exynos4_set_clkdiv(unsigned int div_index)
+{
+ unsigned int tmp;
+
+ /* Change Divider - CPU0 */
+
+ tmp = __raw_readl(S5P_CLKDIV_CPU);
+
+ tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | S5P_CLKDIV_CPU0_COREM0_MASK |
+ S5P_CLKDIV_CPU0_COREM1_MASK | S5P_CLKDIV_CPU0_PERIPH_MASK |
+ S5P_CLKDIV_CPU0_ATB_MASK | S5P_CLKDIV_CPU0_PCLKDBG_MASK |
+ S5P_CLKDIV_CPU0_APLL_MASK);
+
+ tmp |= ((clkdiv_cpu0[div_index][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) |
+ (clkdiv_cpu0[div_index][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) |
+ (clkdiv_cpu0[div_index][2] << S5P_CLKDIV_CPU0_COREM1_SHIFT) |
+ (clkdiv_cpu0[div_index][3] << S5P_CLKDIV_CPU0_PERIPH_SHIFT) |
+ (clkdiv_cpu0[div_index][4] << S5P_CLKDIV_CPU0_ATB_SHIFT) |
+ (clkdiv_cpu0[div_index][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) |
+ (clkdiv_cpu0[div_index][6] << S5P_CLKDIV_CPU0_APLL_SHIFT));
+
+ __raw_writel(tmp, S5P_CLKDIV_CPU);
+
+ do {
+ tmp = __raw_readl(S5P_CLKDIV_STATCPU);
+ } while (tmp & 0x1111111);
+
+ /* Change Divider - CPU1 */
+
+ tmp = __raw_readl(S5P_CLKDIV_CPU1);
+
+ tmp &= ~((0x7 << 4) | 0x7);
+
+ tmp |= ((clkdiv_cpu1[div_index][0] << 4) |
+ (clkdiv_cpu1[div_index][1] << 0));
+
+ __raw_writel(tmp, S5P_CLKDIV_CPU1);
+
+ do {
+ tmp = __raw_readl(S5P_CLKDIV_STATCPU1);
+ } while (tmp & 0x11);
+
+ /* Change Divider - DMC0 */
+
+ tmp = __raw_readl(S5P_CLKDIV_DMC0);
+
+ tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK | S5P_CLKDIV_DMC0_ACPPCLK_MASK |
+ S5P_CLKDIV_DMC0_DPHY_MASK | S5P_CLKDIV_DMC0_DMC_MASK |
+ S5P_CLKDIV_DMC0_DMCD_MASK | S5P_CLKDIV_DMC0_DMCP_MASK |
+ S5P_CLKDIV_DMC0_COPY2_MASK | S5P_CLKDIV_DMC0_CORETI_MASK);
+
+ tmp |= ((clkdiv_dmc0[div_index][0] << S5P_CLKDIV_DMC0_ACP_SHIFT) |
+ (clkdiv_dmc0[div_index][1] << S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) |
+ (clkdiv_dmc0[div_index][2] << S5P_CLKDIV_DMC0_DPHY_SHIFT) |
+ (clkdiv_dmc0[div_index][3] << S5P_CLKDIV_DMC0_DMC_SHIFT) |
+ (clkdiv_dmc0[div_index][4] << S5P_CLKDIV_DMC0_DMCD_SHIFT) |
+ (clkdiv_dmc0[div_index][5] << S5P_CLKDIV_DMC0_DMCP_SHIFT) |
+ (clkdiv_dmc0[div_index][6] << S5P_CLKDIV_DMC0_COPY2_SHIFT) |
+ (clkdiv_dmc0[div_index][7] << S5P_CLKDIV_DMC0_CORETI_SHIFT));
+
+ __raw_writel(tmp, S5P_CLKDIV_DMC0);
+
+ do {
+ tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0);
+ } while (tmp & 0x11111111);
+
+ /* Change Divider - TOP */
+
+ tmp = __raw_readl(S5P_CLKDIV_TOP);
+
+ tmp &= ~(S5P_CLKDIV_TOP_ACLK200_MASK | S5P_CLKDIV_TOP_ACLK100_MASK |
+ S5P_CLKDIV_TOP_ACLK160_MASK | S5P_CLKDIV_TOP_ACLK133_MASK |
+ S5P_CLKDIV_TOP_ONENAND_MASK);
+
+ tmp |= ((clkdiv_top[div_index][0] << S5P_CLKDIV_TOP_ACLK200_SHIFT) |
+ (clkdiv_top[div_index][1] << S5P_CLKDIV_TOP_ACLK100_SHIFT) |
+ (clkdiv_top[div_index][2] << S5P_CLKDIV_TOP_ACLK160_SHIFT) |
+ (clkdiv_top[div_index][3] << S5P_CLKDIV_TOP_ACLK133_SHIFT) |
+ (clkdiv_top[div_index][4] << S5P_CLKDIV_TOP_ONENAND_SHIFT));
+
+ __raw_writel(tmp, S5P_CLKDIV_TOP);
+
+ do {
+ tmp = __raw_readl(S5P_CLKDIV_STAT_TOP);
+ } while (tmp & 0x11111);
+
+ /* Change Divider - LEFTBUS */
+
+ tmp = __raw_readl(S5P_CLKDIV_LEFTBUS);
+
+ tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);
+
+ tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) |
+ (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT));
+
+ __raw_writel(tmp, S5P_CLKDIV_LEFTBUS);
+
+ do {
+ tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS);
+ } while (tmp & 0x11);
+
+ /* Change Divider - RIGHTBUS */
+
+ tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS);
+
+ tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);
+
+ tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) |
+ (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT));
+
+ __raw_writel(tmp, S5P_CLKDIV_RIGHTBUS);
+
+ do {
+ tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS);
+ } while (tmp & 0x11);
+}
+
+static void exynos4_set_apll(unsigned int index)
+{
+ unsigned int tmp;
+
+ /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
+ clk_set_parent(moutcore, mout_mpll);
+
+ do {
+ tmp = (__raw_readl(S5P_CLKMUX_STATCPU)
+ >> S5P_CLKSRC_CPU_MUXCORE_SHIFT);
+ tmp &= 0x7;
+ } while (tmp != 0x2);
+
+ /* 2. Set APLL Lock time */
+ __raw_writel(S5P_APLL_LOCKTIME, S5P_APLL_LOCK);
+
+ /* 3. Change PLL PMS values */
+ tmp = __raw_readl(S5P_APLL_CON0);
+ tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
+ tmp |= exynos4_apll_pms_table[index];
+ __raw_writel(tmp, S5P_APLL_CON0);
+
+ /* 4. wait_lock_time */
+ do {
+ tmp = __raw_readl(S5P_APLL_CON0);
+ } while (!(tmp & (0x1 << S5P_APLLCON0_LOCKED_SHIFT)));
+
+ /* 5. MUX_CORE_SEL = APLL */
+ clk_set_parent(moutcore, mout_apll);
+
+ do {
+ tmp = __raw_readl(S5P_CLKMUX_STATCPU);
+ tmp &= S5P_CLKMUX_STATCPU_MUXCORE_MASK;
+ } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT));
+}
+
+static void exynos4_set_frequency(unsigned int old_index, unsigned int new_index)
+{
+ unsigned int tmp;
+
+ if (old_index > new_index) {
+ /* The frequency changing to L0 needs to change apll */
+ if (freqs.new == exynos4_freq_table[L0].frequency) {
+ /* 1. Change the system clock divider values */
+ exynos4_set_clkdiv(new_index);
+
+ /* 2. Change the apll m,p,s value */
+ exynos4_set_apll(new_index);
+ } else {
+ /* 1. Change the system clock divider values */
+ exynos4_set_clkdiv(new_index);
+
+ /* 2. Change just s value in apll m,p,s value */
+ tmp = __raw_readl(S5P_APLL_CON0);
+ tmp &= ~(0x7 << 0);
+ tmp |= (exynos4_apll_pms_table[new_index] & 0x7);
+ __raw_writel(tmp, S5P_APLL_CON0);
+ }
+ }
+
+ else if (old_index < new_index) {
+ /* The frequency changing from L0 needs to change apll */
+ if (freqs.old == exynos4_freq_table[L0].frequency) {
+ /* 1. Change the apll m,p,s value */
+ exynos4_set_apll(new_index);
+
+ /* 2. Change the system clock divider values */
+ exynos4_set_clkdiv(new_index);
+ } else {
+ /* 1. Change just s value in apll m,p,s value */
+ tmp = __raw_readl(S5P_APLL_CON0);
+ tmp &= ~(0x7 << 0);
+ tmp |= (exynos4_apll_pms_table[new_index] & 0x7);
+ __raw_writel(tmp, S5P_APLL_CON0);
+
+ /* 2. Change the system clock divider values */
+ exynos4_set_clkdiv(new_index);
+ }
+ }
+}
+
+static int exynos4_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int index, old_index;
+ unsigned int arm_volt, int_volt;
+ int err = -EINVAL;
+
+ freqs.old = exynos4_getspeed(policy->cpu);
+
+ mutex_lock(&cpufreq_lock);
+
+ if (frequency_locked && target_freq != locking_frequency) {
+ err = -EAGAIN;
+ goto out;
+ }
+
+ if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
+ freqs.old, relation, &old_index))
+ goto out;
+
+ if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
+ target_freq, relation, &index))
+ goto out;
+
+ err = 0;
+
+ freqs.new = exynos4_freq_table[index].frequency;
+ freqs.cpu = policy->cpu;
+
+ if (freqs.new == freqs.old)
+ goto out;
+
+ /* get the voltage value */
+ arm_volt = exynos4_volt_table[index].arm_volt;
+ int_volt = exynos4_volt_table[index].int_volt;
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ /* control regulator */
+ if (freqs.new > freqs.old) {
+ /* Voltage up */
+ regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
+ regulator_set_voltage(int_regulator, int_volt, int_volt);
+ }
+
+ /* Clock Configuration Procedure */
+ exynos4_set_frequency(old_index, index);
+
+ /* control regulator */
+ if (freqs.new < freqs.old) {
+ /* Voltage down */
+ regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
+ regulator_set_voltage(int_regulator, int_volt, int_volt);
+ }
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+out:
+ mutex_unlock(&cpufreq_lock);
+ return err;
+}
+
+#ifdef CONFIG_PM
+/*
+ * These suspend/resume are used as syscore_ops, it is already too
+ * late to set regulator voltages at this stage.
+ */
+static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+
+static int exynos4_cpufreq_resume(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+#endif
+
+/**
+ * exynos4_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume
+ * context
+ * @notifier
+ * @pm_event
+ * @v
+ *
+ * While frequency_locked == true, target() ignores every frequency but
+ * locking_frequency. The locking_frequency value is the initial frequency,
+ * which is set by the bootloader. In order to eliminate possible
+ * inconsistency in clock values, we save and restore frequencies during
+ * suspend and resume and block CPUFREQ activities. Note that the standard
+ * suspend/resume cannot be used as they are too deep (syscore_ops) for
+ * regulator actions.
+ */
+static int exynos4_cpufreq_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event, void *v)
+{
+ struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */
+ static unsigned int saved_frequency;
+ unsigned int temp;
+
+ mutex_lock(&cpufreq_lock);
+ switch (pm_event) {
+ case PM_SUSPEND_PREPARE:
+ if (frequency_locked)
+ goto out;
+ frequency_locked = true;
+
+ if (locking_frequency) {
+ saved_frequency = exynos4_getspeed(0);
+
+ mutex_unlock(&cpufreq_lock);
+ exynos4_target(policy, locking_frequency,
+ CPUFREQ_RELATION_H);
+ mutex_lock(&cpufreq_lock);
+ }
+
+ break;
+ case PM_POST_SUSPEND:
+
+ if (saved_frequency) {
+ /*
+ * While frequency_locked, only locking_frequency
+ * is valid for target(). In order to use
+ * saved_frequency while keeping frequency_locked,
+ * we temporarly overwrite locking_frequency.
+ */
+ temp = locking_frequency;
+ locking_frequency = saved_frequency;
+
+ mutex_unlock(&cpufreq_lock);
+ exynos4_target(policy, locking_frequency,
+ CPUFREQ_RELATION_H);
+ mutex_lock(&cpufreq_lock);
+
+ locking_frequency = temp;
+ }
+
+ frequency_locked = false;
+ break;
+ }
+out:
+ mutex_unlock(&cpufreq_lock);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block exynos4_cpufreq_nb = {
+ .notifier_call = exynos4_cpufreq_pm_notifier,
+};
+
+static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu);
+
+ cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu);
+
+ /* set the transition latency value */
+ policy->cpuinfo.transition_latency = 100000;
+
+ /*
+ * EXYNOS4 multi-core processors has 2 cores
+ * that the frequency cannot be set independently.
+ * Each cpu is bound to the same speed.
+ * So the affected cpu is all of the cpus.
+ */
+ cpumask_setall(policy->cpus);
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table);
+ if (ret)
+ return ret;
+
+ cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu);
+
+ return 0;
+}
+
+static int exynos4_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ return 0;
+}
+
+static struct freq_attr *exynos4_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver exynos4_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = exynos4_verify_speed,
+ .target = exynos4_target,
+ .get = exynos4_getspeed,
+ .init = exynos4_cpufreq_cpu_init,
+ .exit = exynos4_cpufreq_cpu_exit,
+ .name = "exynos4_cpufreq",
+ .attr = exynos4_cpufreq_attr,
+#ifdef CONFIG_PM
+ .suspend = exynos4_cpufreq_suspend,
+ .resume = exynos4_cpufreq_resume,
+#endif
+};
+
+static int __init exynos4_cpufreq_init(void)
+{
+ cpu_clk = clk_get(NULL, "armclk");
+ if (IS_ERR(cpu_clk))
+ return PTR_ERR(cpu_clk);
+
+ locking_frequency = exynos4_getspeed(0);
+
+ moutcore = clk_get(NULL, "moutcore");
+ if (IS_ERR(moutcore))
+ goto out;
+
+ mout_mpll = clk_get(NULL, "mout_mpll");
+ if (IS_ERR(mout_mpll))
+ goto out;
+
+ mout_apll = clk_get(NULL, "mout_apll");
+ if (IS_ERR(mout_apll))
+ goto out;
+
+ arm_regulator = regulator_get(NULL, "vdd_arm");
+ if (IS_ERR(arm_regulator)) {
+ printk(KERN_ERR "failed to get resource %s\n", "vdd_arm");
+ goto out;
+ }
+
+ int_regulator = regulator_get(NULL, "vdd_int");
+ if (IS_ERR(int_regulator)) {
+ printk(KERN_ERR "failed to get resource %s\n", "vdd_int");
+ goto out;
+ }
+
+ /*
+ * Check DRAM type.
+ * Because DVFS level is different according to DRAM type.
+ */
+ memtype = __raw_readl(S5P_VA_DMC0 + S5P_DMC0_MEMCON_OFFSET);
+ memtype = (memtype >> S5P_DMC0_MEMTYPE_SHIFT);
+ memtype &= S5P_DMC0_MEMTYPE_MASK;
+
+ if ((memtype < DDR2) && (memtype > DDR3)) {
+ printk(KERN_ERR "%s: wrong memtype= 0x%x\n", __func__, memtype);
+ goto out;
+ } else {
+ printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype);
+ }
+
+ register_pm_notifier(&exynos4_cpufreq_nb);
+
+ return cpufreq_register_driver(&exynos4_driver);
+
+out:
+ if (!IS_ERR(cpu_clk))
+ clk_put(cpu_clk);
+
+ if (!IS_ERR(moutcore))
+ clk_put(moutcore);
+
+ if (!IS_ERR(mout_mpll))
+ clk_put(mout_mpll);
+
+ if (!IS_ERR(mout_apll))
+ clk_put(mout_apll);
+
+ if (!IS_ERR(arm_regulator))
+ regulator_put(arm_regulator);
+
+ if (!IS_ERR(int_regulator))
+ regulator_put(int_regulator);
+
+ printk(KERN_ERR "%s: failed initialization\n", __func__);
+
+ return -EINVAL;
+}
+late_initcall(exynos4_cpufreq_init);
diff --git a/drivers/cpufreq/maple-cpufreq.c b/drivers/cpufreq/maple-cpufreq.c
new file mode 100644
index 000000000000..89b178a3f849
--- /dev/null
+++ b/drivers/cpufreq/maple-cpufreq.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2011 Dmitry Eremin-Solenikov
+ * Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ * and Markus Demleitner <msdemlei@cl.uni-heidelberg.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This driver adds basic cpufreq support for SMU & 970FX based G5 Macs,
+ * that is iMac G5 and latest single CPU desktop.
+ */
+
+#undef DEBUG
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/time.h>
+#include <linux/of.h>
+
+#define DBG(fmt...) pr_debug(fmt)
+
+/* see 970FX user manual */
+
+#define SCOM_PCR 0x0aa001 /* PCR scom addr */
+
+#define PCR_HILO_SELECT 0x80000000U /* 1 = PCR, 0 = PCRH */
+#define PCR_SPEED_FULL 0x00000000U /* 1:1 speed value */
+#define PCR_SPEED_HALF 0x00020000U /* 1:2 speed value */
+#define PCR_SPEED_QUARTER 0x00040000U /* 1:4 speed value */
+#define PCR_SPEED_MASK 0x000e0000U /* speed mask */
+#define PCR_SPEED_SHIFT 17
+#define PCR_FREQ_REQ_VALID 0x00010000U /* freq request valid */
+#define PCR_VOLT_REQ_VALID 0x00008000U /* volt request valid */
+#define PCR_TARGET_TIME_MASK 0x00006000U /* target time */
+#define PCR_STATLAT_MASK 0x00001f00U /* STATLAT value */
+#define PCR_SNOOPLAT_MASK 0x000000f0U /* SNOOPLAT value */
+#define PCR_SNOOPACC_MASK 0x0000000fU /* SNOOPACC value */
+
+#define SCOM_PSR 0x408001 /* PSR scom addr */
+/* warning: PSR is a 64 bits register */
+#define PSR_CMD_RECEIVED 0x2000000000000000U /* command received */
+#define PSR_CMD_COMPLETED 0x1000000000000000U /* command completed */
+#define PSR_CUR_SPEED_MASK 0x0300000000000000U /* current speed */
+#define PSR_CUR_SPEED_SHIFT (56)
+
+/*
+ * The G5 only supports two frequencies (Quarter speed is not supported)
+ */
+#define CPUFREQ_HIGH 0
+#define CPUFREQ_LOW 1
+
+static struct cpufreq_frequency_table maple_cpu_freqs[] = {
+ {CPUFREQ_HIGH, 0},
+ {CPUFREQ_LOW, 0},
+ {0, CPUFREQ_TABLE_END},
+};
+
+static struct freq_attr *maple_cpu_freqs_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+/* Power mode data is an array of the 32 bits PCR values to use for
+ * the various frequencies, retrieved from the device-tree
+ */
+static int maple_pmode_cur;
+
+static DEFINE_MUTEX(maple_switch_mutex);
+
+static const u32 *maple_pmode_data;
+static int maple_pmode_max;
+
+/*
+ * SCOM based frequency switching for 970FX rev3
+ */
+static int maple_scom_switch_freq(int speed_mode)
+{
+ unsigned long flags;
+ int to;
+
+ local_irq_save(flags);
+
+ /* Clear PCR high */
+ scom970_write(SCOM_PCR, 0);
+ /* Clear PCR low */
+ scom970_write(SCOM_PCR, PCR_HILO_SELECT | 0);
+ /* Set PCR low */
+ scom970_write(SCOM_PCR, PCR_HILO_SELECT |
+ maple_pmode_data[speed_mode]);
+
+ /* Wait for completion */
+ for (to = 0; to < 10; to++) {
+ unsigned long psr = scom970_read(SCOM_PSR);
+
+ if ((psr & PSR_CMD_RECEIVED) == 0 &&
+ (((psr >> PSR_CUR_SPEED_SHIFT) ^
+ (maple_pmode_data[speed_mode] >> PCR_SPEED_SHIFT)) & 0x3)
+ == 0)
+ break;
+ if (psr & PSR_CMD_COMPLETED)
+ break;
+ udelay(100);
+ }
+
+ local_irq_restore(flags);
+
+ maple_pmode_cur = speed_mode;
+ ppc_proc_freq = maple_cpu_freqs[speed_mode].frequency * 1000ul;
+
+ return 0;
+}
+
+static int maple_scom_query_freq(void)
+{
+ unsigned long psr = scom970_read(SCOM_PSR);
+ int i;
+
+ for (i = 0; i <= maple_pmode_max; i++)
+ if ((((psr >> PSR_CUR_SPEED_SHIFT) ^
+ (maple_pmode_data[i] >> PCR_SPEED_SHIFT)) & 0x3) == 0)
+ break;
+ return i;
+}
+
+/*
+ * Common interface to the cpufreq core
+ */
+
+static int maple_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, maple_cpu_freqs);
+}
+
+static int maple_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq, unsigned int relation)
+{
+ unsigned int newstate = 0;
+ struct cpufreq_freqs freqs;
+ int rc;
+
+ if (cpufreq_frequency_table_target(policy, maple_cpu_freqs,
+ target_freq, relation, &newstate))
+ return -EINVAL;
+
+ if (maple_pmode_cur == newstate)
+ return 0;
+
+ mutex_lock(&maple_switch_mutex);
+
+ freqs.old = maple_cpu_freqs[maple_pmode_cur].frequency;
+ freqs.new = maple_cpu_freqs[newstate].frequency;
+ freqs.cpu = 0;
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ rc = maple_scom_switch_freq(newstate);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ mutex_unlock(&maple_switch_mutex);
+
+ return rc;
+}
+
+static unsigned int maple_cpufreq_get_speed(unsigned int cpu)
+{
+ return maple_cpu_freqs[maple_pmode_cur].frequency;
+}
+
+static int maple_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+ policy->cpuinfo.transition_latency = 12000;
+ policy->cur = maple_cpu_freqs[maple_scom_query_freq()].frequency;
+ /* secondary CPUs are tied to the primary one by the
+ * cpufreq core if in the secondary policy we tell it that
+ * it actually must be one policy together with all others. */
+ cpumask_copy(policy->cpus, cpu_online_mask);
+ cpufreq_frequency_table_get_attr(maple_cpu_freqs, policy->cpu);
+
+ return cpufreq_frequency_table_cpuinfo(policy,
+ maple_cpu_freqs);
+}
+
+
+static struct cpufreq_driver maple_cpufreq_driver = {
+ .name = "maple",
+ .owner = THIS_MODULE,
+ .flags = CPUFREQ_CONST_LOOPS,
+ .init = maple_cpufreq_cpu_init,
+ .verify = maple_cpufreq_verify,
+ .target = maple_cpufreq_target,
+ .get = maple_cpufreq_get_speed,
+ .attr = maple_cpu_freqs_attr,
+};
+
+static int __init maple_cpufreq_init(void)
+{
+ struct device_node *cpus;
+ struct device_node *cpunode;
+ unsigned int psize;
+ unsigned long max_freq;
+ const u32 *valp;
+ u32 pvr_hi;
+ int rc = -ENODEV;
+
+ /*
+ * Behave here like powermac driver which checks machine compatibility
+ * to ease merging of two drivers in future.
+ */
+ if (!of_machine_is_compatible("Momentum,Maple") &&
+ !of_machine_is_compatible("Momentum,Apache"))
+ return 0;
+
+ cpus = of_find_node_by_path("/cpus");
+ if (cpus == NULL) {
+ DBG("No /cpus node !\n");
+ return -ENODEV;
+ }
+
+ /* Get first CPU node */
+ for (cpunode = NULL;
+ (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) {
+ const u32 *reg = of_get_property(cpunode, "reg", NULL);
+ if (reg == NULL || (*reg) != 0)
+ continue;
+ if (!strcmp(cpunode->type, "cpu"))
+ break;
+ }
+ if (cpunode == NULL) {
+ printk(KERN_ERR "cpufreq: Can't find any CPU 0 node\n");
+ goto bail_cpus;
+ }
+
+ /* Check 970FX for now */
+ /* we actually don't care on which CPU to access PVR */
+ pvr_hi = PVR_VER(mfspr(SPRN_PVR));
+ if (pvr_hi != 0x3c && pvr_hi != 0x44) {
+ printk(KERN_ERR "cpufreq: Unsupported CPU version (%x)\n",
+ pvr_hi);
+ goto bail_noprops;
+ }
+
+ /* Look for the powertune data in the device-tree */
+ /*
+ * On Maple this property is provided by PIBS in dual-processor config,
+ * not provided by PIBS in CPU0 config and also not provided by SLOF,
+ * so YMMV
+ */
+ maple_pmode_data = of_get_property(cpunode, "power-mode-data", &psize);
+ if (!maple_pmode_data) {
+ DBG("No power-mode-data !\n");
+ goto bail_noprops;
+ }
+ maple_pmode_max = psize / sizeof(u32) - 1;
+
+ /*
+ * From what I see, clock-frequency is always the maximal frequency.
+ * The current driver can not slew sysclk yet, so we really only deal
+ * with powertune steps for now. We also only implement full freq and
+ * half freq in this version. So far, I haven't yet seen a machine
+ * supporting anything else.
+ */
+ valp = of_get_property(cpunode, "clock-frequency", NULL);
+ if (!valp)
+ return -ENODEV;
+ max_freq = (*valp)/1000;
+ maple_cpu_freqs[0].frequency = max_freq;
+ maple_cpu_freqs[1].frequency = max_freq/2;
+
+ /* Force apply current frequency to make sure everything is in
+ * sync (voltage is right for example). Firmware may leave us with
+ * a strange setting ...
+ */
+ msleep(10);
+ maple_pmode_cur = -1;
+ maple_scom_switch_freq(maple_scom_query_freq());
+
+ printk(KERN_INFO "Registering Maple CPU frequency driver\n");
+ printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n",
+ maple_cpu_freqs[1].frequency/1000,
+ maple_cpu_freqs[0].frequency/1000,
+ maple_cpu_freqs[maple_pmode_cur].frequency/1000);
+
+ rc = cpufreq_register_driver(&maple_cpufreq_driver);
+
+ of_node_put(cpunode);
+ of_node_put(cpus);
+
+ return rc;
+
+bail_noprops:
+ of_node_put(cpunode);
+bail_cpus:
+ of_node_put(cpus);
+
+ return rc;
+}
+
+module_init(maple_cpufreq_init);
+
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c
index 7b0603eb0129..cdc02ac8f41a 100644
--- a/drivers/cpufreq/pcc-cpufreq.c
+++ b/drivers/cpufreq/pcc-cpufreq.c
@@ -261,6 +261,9 @@ static int pcc_get_offset(int cpu)
pr = per_cpu(processors, cpu);
pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
+ if (!pr)
+ return -ENODEV;
+
status = acpi_evaluate_object(pr->handle, "PCCP", NULL, &buffer);
if (ACPI_FAILURE(status))
return -ENODEV;
diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c
new file mode 100644
index 000000000000..3475f65aeec6
--- /dev/null
+++ b/drivers/cpufreq/s3c64xx-cpufreq.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * S3C64xx CPUfreq Support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+
+static struct clk *armclk;
+static struct regulator *vddarm;
+static unsigned long regulator_latency;
+
+#ifdef CONFIG_CPU_S3C6410
+struct s3c64xx_dvfs {
+ unsigned int vddarm_min;
+ unsigned int vddarm_max;
+};
+
+static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = {
+ [0] = { 1000000, 1150000 },
+ [1] = { 1050000, 1150000 },
+ [2] = { 1100000, 1150000 },
+ [3] = { 1200000, 1350000 },
+ [4] = { 1300000, 1350000 },
+};
+
+static struct cpufreq_frequency_table s3c64xx_freq_table[] = {
+ { 0, 66000 },
+ { 0, 100000 },
+ { 0, 133000 },
+ { 1, 200000 },
+ { 1, 222000 },
+ { 1, 266000 },
+ { 2, 333000 },
+ { 2, 400000 },
+ { 2, 532000 },
+ { 2, 533000 },
+ { 3, 667000 },
+ { 4, 800000 },
+ { 0, CPUFREQ_TABLE_END },
+};
+#endif
+
+static int s3c64xx_cpufreq_verify_speed(struct cpufreq_policy *policy)
+{
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ return cpufreq_frequency_table_verify(policy, s3c64xx_freq_table);
+}
+
+static unsigned int s3c64xx_cpufreq_get_speed(unsigned int cpu)
+{
+ if (cpu != 0)
+ return 0;
+
+ return clk_get_rate(armclk) / 1000;
+}
+
+static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ int ret;
+ unsigned int i;
+ struct cpufreq_freqs freqs;
+ struct s3c64xx_dvfs *dvfs;
+
+ ret = cpufreq_frequency_table_target(policy, s3c64xx_freq_table,
+ target_freq, relation, &i);
+ if (ret != 0)
+ return ret;
+
+ freqs.cpu = 0;
+ freqs.old = clk_get_rate(armclk) / 1000;
+ freqs.new = s3c64xx_freq_table[i].frequency;
+ freqs.flags = 0;
+ dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].index];
+
+ if (freqs.old == freqs.new)
+ return 0;
+
+ pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new);
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+#ifdef CONFIG_REGULATOR
+ if (vddarm && freqs.new > freqs.old) {
+ ret = regulator_set_voltage(vddarm,
+ dvfs->vddarm_min,
+ dvfs->vddarm_max);
+ if (ret != 0) {
+ pr_err("cpufreq: Failed to set VDDARM for %dkHz: %d\n",
+ freqs.new, ret);
+ goto err;
+ }
+ }
+#endif
+
+ ret = clk_set_rate(armclk, freqs.new * 1000);
+ if (ret < 0) {
+ pr_err("cpufreq: Failed to set rate %dkHz: %d\n",
+ freqs.new, ret);
+ goto err;
+ }
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+#ifdef CONFIG_REGULATOR
+ if (vddarm && freqs.new < freqs.old) {
+ ret = regulator_set_voltage(vddarm,
+ dvfs->vddarm_min,
+ dvfs->vddarm_max);
+ if (ret != 0) {
+ pr_err("cpufreq: Failed to set VDDARM for %dkHz: %d\n",
+ freqs.new, ret);
+ goto err_clk;
+ }
+ }
+#endif
+
+ pr_debug("cpufreq: Set actual frequency %lukHz\n",
+ clk_get_rate(armclk) / 1000);
+
+ return 0;
+
+err_clk:
+ if (clk_set_rate(armclk, freqs.old * 1000) < 0)
+ pr_err("Failed to restore original clock rate\n");
+err:
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return ret;
+}
+
+#ifdef CONFIG_REGULATOR
+static void __init s3c64xx_cpufreq_config_regulator(void)
+{
+ int count, v, i, found;
+ struct cpufreq_frequency_table *freq;
+ struct s3c64xx_dvfs *dvfs;
+
+ count = regulator_count_voltages(vddarm);
+ if (count < 0) {
+ pr_err("cpufreq: Unable to check supported voltages\n");
+ }
+
+ freq = s3c64xx_freq_table;
+ while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) {
+ if (freq->frequency == CPUFREQ_ENTRY_INVALID)
+ continue;
+
+ dvfs = &s3c64xx_dvfs_table[freq->index];
+ found = 0;
+
+ for (i = 0; i < count; i++) {
+ v = regulator_list_voltage(vddarm, i);
+ if (v >= dvfs->vddarm_min && v <= dvfs->vddarm_max)
+ found = 1;
+ }
+
+ if (!found) {
+ pr_debug("cpufreq: %dkHz unsupported by regulator\n",
+ freq->frequency);
+ freq->frequency = CPUFREQ_ENTRY_INVALID;
+ }
+
+ freq++;
+ }
+
+ /* Guess based on having to do an I2C/SPI write; in future we
+ * will be able to query the regulator performance here. */
+ regulator_latency = 1 * 1000 * 1000;
+}
+#endif
+
+static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
+{
+ int ret;
+ struct cpufreq_frequency_table *freq;
+
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ if (s3c64xx_freq_table == NULL) {
+ pr_err("cpufreq: No frequency information for this CPU\n");
+ return -ENODEV;
+ }
+
+ armclk = clk_get(NULL, "armclk");
+ if (IS_ERR(armclk)) {
+ pr_err("cpufreq: Unable to obtain ARMCLK: %ld\n",
+ PTR_ERR(armclk));
+ return PTR_ERR(armclk);
+ }
+
+#ifdef CONFIG_REGULATOR
+ vddarm = regulator_get(NULL, "vddarm");
+ if (IS_ERR(vddarm)) {
+ ret = PTR_ERR(vddarm);
+ pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret);
+ pr_err("cpufreq: Only frequency scaling available\n");
+ vddarm = NULL;
+ } else {
+ s3c64xx_cpufreq_config_regulator();
+ }
+#endif
+
+ freq = s3c64xx_freq_table;
+ while (freq->frequency != CPUFREQ_TABLE_END) {
+ unsigned long r;
+
+ /* Check for frequencies we can generate */
+ r = clk_round_rate(armclk, freq->frequency * 1000);
+ r /= 1000;
+ if (r != freq->frequency) {
+ pr_debug("cpufreq: %dkHz unsupported by clock\n",
+ freq->frequency);
+ freq->frequency = CPUFREQ_ENTRY_INVALID;
+ }
+
+ /* If we have no regulator then assume startup
+ * frequency is the maximum we can support. */
+ if (!vddarm && freq->frequency > s3c64xx_cpufreq_get_speed(0))
+ freq->frequency = CPUFREQ_ENTRY_INVALID;
+
+ freq++;
+ }
+
+ policy->cur = clk_get_rate(armclk) / 1000;
+
+ /* Datasheet says PLL stabalisation time (if we were to use
+ * the PLLs, which we don't currently) is ~300us worst case,
+ * but add some fudge.
+ */
+ policy->cpuinfo.transition_latency = (500 * 1000) + regulator_latency;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, s3c64xx_freq_table);
+ if (ret != 0) {
+ pr_err("cpufreq: Failed to configure frequency table: %d\n",
+ ret);
+ regulator_put(vddarm);
+ clk_put(armclk);
+ }
+
+ return ret;
+}
+
+static struct cpufreq_driver s3c64xx_cpufreq_driver = {
+ .owner = THIS_MODULE,
+ .flags = 0,
+ .verify = s3c64xx_cpufreq_verify_speed,
+ .target = s3c64xx_cpufreq_set_target,
+ .get = s3c64xx_cpufreq_get_speed,
+ .init = s3c64xx_cpufreq_driver_init,
+ .name = "s3c",
+};
+
+static int __init s3c64xx_cpufreq_init(void)
+{
+ return cpufreq_register_driver(&s3c64xx_cpufreq_driver);
+}
+module_init(s3c64xx_cpufreq_init);
diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c
new file mode 100644
index 000000000000..a484aaea9809
--- /dev/null
+++ b/drivers/cpufreq/s5pv210-cpufreq.c
@@ -0,0 +1,649 @@
+/*
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * CPU frequency scaling for S5PC110/S5PV210
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/cpufreq.h>
+#include <linux/reboot.h>
+#include <linux/regulator/consumer.h>
+#include <linux/suspend.h>
+
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+
+static struct clk *cpu_clk;
+static struct clk *dmc0_clk;
+static struct clk *dmc1_clk;
+static struct cpufreq_freqs freqs;
+static DEFINE_MUTEX(set_freq_lock);
+
+/* APLL M,P,S values for 1G/800Mhz */
+#define APLL_VAL_1000 ((1 << 31) | (125 << 16) | (3 << 8) | 1)
+#define APLL_VAL_800 ((1 << 31) | (100 << 16) | (3 << 8) | 1)
+
+/* Use 800MHz when entering sleep mode */
+#define SLEEP_FREQ (800 * 1000)
+
+/*
+ * relation has an additional symantics other than the standard of cpufreq
+ * DISALBE_FURTHER_CPUFREQ: disable further access to target
+ * ENABLE_FURTUER_CPUFREQ: enable access to target
+ */
+enum cpufreq_access {
+ DISABLE_FURTHER_CPUFREQ = 0x10,
+ ENABLE_FURTHER_CPUFREQ = 0x20,
+};
+
+static bool no_cpufreq_access;
+
+/*
+ * DRAM configurations to calculate refresh counter for changing
+ * frequency of memory.
+ */
+struct dram_conf {
+ unsigned long freq; /* HZ */
+ unsigned long refresh; /* DRAM refresh counter * 1000 */
+};
+
+/* DRAM configuration (DMC0 and DMC1) */
+static struct dram_conf s5pv210_dram_conf[2];
+
+enum perf_level {
+ L0, L1, L2, L3, L4,
+};
+
+enum s5pv210_mem_type {
+ LPDDR = 0x1,
+ LPDDR2 = 0x2,
+ DDR2 = 0x4,
+};
+
+enum s5pv210_dmc_port {
+ DMC0 = 0,
+ DMC1,
+};
+
+static struct cpufreq_frequency_table s5pv210_freq_table[] = {
+ {L0, 1000*1000},
+ {L1, 800*1000},
+ {L2, 400*1000},
+ {L3, 200*1000},
+ {L4, 100*1000},
+ {0, CPUFREQ_TABLE_END},
+};
+
+static struct regulator *arm_regulator;
+static struct regulator *int_regulator;
+
+struct s5pv210_dvs_conf {
+ int arm_volt; /* uV */
+ int int_volt; /* uV */
+};
+
+static const int arm_volt_max = 1350000;
+static const int int_volt_max = 1250000;
+
+static struct s5pv210_dvs_conf dvs_conf[] = {
+ [L0] = {
+ .arm_volt = 1250000,
+ .int_volt = 1100000,
+ },
+ [L1] = {
+ .arm_volt = 1200000,
+ .int_volt = 1100000,
+ },
+ [L2] = {
+ .arm_volt = 1050000,
+ .int_volt = 1100000,
+ },
+ [L3] = {
+ .arm_volt = 950000,
+ .int_volt = 1100000,
+ },
+ [L4] = {
+ .arm_volt = 950000,
+ .int_volt = 1000000,
+ },
+};
+
+static u32 clkdiv_val[5][11] = {
+ /*
+ * Clock divider value for following
+ * { APLL, A2M, HCLK_MSYS, PCLK_MSYS,
+ * HCLK_DSYS, PCLK_DSYS, HCLK_PSYS, PCLK_PSYS,
+ * ONEDRAM, MFC, G3D }
+ */
+
+ /* L0 : [1000/200/100][166/83][133/66][200/200] */
+ {0, 4, 4, 1, 3, 1, 4, 1, 3, 0, 0},
+
+ /* L1 : [800/200/100][166/83][133/66][200/200] */
+ {0, 3, 3, 1, 3, 1, 4, 1, 3, 0, 0},
+
+ /* L2 : [400/200/100][166/83][133/66][200/200] */
+ {1, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
+
+ /* L3 : [200/200/100][166/83][133/66][200/200] */
+ {3, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
+
+ /* L4 : [100/100/100][83/83][66/66][100/100] */
+ {7, 7, 0, 0, 7, 0, 9, 0, 7, 0, 0},
+};
+
+/*
+ * This function set DRAM refresh counter
+ * accoriding to operating frequency of DRAM
+ * ch: DMC port number 0 or 1
+ * freq: Operating frequency of DRAM(KHz)
+ */
+static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq)
+{
+ unsigned long tmp, tmp1;
+ void __iomem *reg = NULL;
+
+ if (ch == DMC0) {
+ reg = (S5P_VA_DMC0 + 0x30);
+ } else if (ch == DMC1) {
+ reg = (S5P_VA_DMC1 + 0x30);
+ } else {
+ printk(KERN_ERR "Cannot find DMC port\n");
+ return;
+ }
+
+ /* Find current DRAM frequency */
+ tmp = s5pv210_dram_conf[ch].freq;
+
+ do_div(tmp, freq);
+
+ tmp1 = s5pv210_dram_conf[ch].refresh;
+
+ do_div(tmp1, tmp);
+
+ __raw_writel(tmp1, reg);
+}
+
+static int s5pv210_verify_speed(struct cpufreq_policy *policy)
+{
+ if (policy->cpu)
+ return -EINVAL;
+
+ return cpufreq_frequency_table_verify(policy, s5pv210_freq_table);
+}
+
+static unsigned int s5pv210_getspeed(unsigned int cpu)
+{
+ if (cpu)
+ return 0;
+
+ return clk_get_rate(cpu_clk) / 1000;
+}
+
+static int s5pv210_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned long reg;
+ unsigned int index, priv_index;
+ unsigned int pll_changing = 0;
+ unsigned int bus_speed_changing = 0;
+ int arm_volt, int_volt;
+ int ret = 0;
+
+ mutex_lock(&set_freq_lock);
+
+ if (relation & ENABLE_FURTHER_CPUFREQ)
+ no_cpufreq_access = false;
+
+ if (no_cpufreq_access) {
+#ifdef CONFIG_PM_VERBOSE
+ pr_err("%s:%d denied access to %s as it is disabled"
+ "temporarily\n", __FILE__, __LINE__, __func__);
+#endif
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (relation & DISABLE_FURTHER_CPUFREQ)
+ no_cpufreq_access = true;
+
+ relation &= ~(ENABLE_FURTHER_CPUFREQ | DISABLE_FURTHER_CPUFREQ);
+
+ freqs.old = s5pv210_getspeed(0);
+
+ if (cpufreq_frequency_table_target(policy, s5pv210_freq_table,
+ target_freq, relation, &index)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ freqs.new = s5pv210_freq_table[index].frequency;
+ freqs.cpu = 0;
+
+ if (freqs.new == freqs.old)
+ goto exit;
+
+ /* Finding current running level index */
+ if (cpufreq_frequency_table_target(policy, s5pv210_freq_table,
+ freqs.old, relation, &priv_index)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ arm_volt = dvs_conf[index].arm_volt;
+ int_volt = dvs_conf[index].int_volt;
+
+ if (freqs.new > freqs.old) {
+ ret = regulator_set_voltage(arm_regulator,
+ arm_volt, arm_volt_max);
+ if (ret)
+ goto exit;
+
+ ret = regulator_set_voltage(int_regulator,
+ int_volt, int_volt_max);
+ if (ret)
+ goto exit;
+ }
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ /* Check if there need to change PLL */
+ if ((index == L0) || (priv_index == L0))
+ pll_changing = 1;
+
+ /* Check if there need to change System bus clock */
+ if ((index == L4) || (priv_index == L4))
+ bus_speed_changing = 1;
+
+ if (bus_speed_changing) {
+ /*
+ * Reconfigure DRAM refresh counter value for minimum
+ * temporary clock while changing divider.
+ * expected clock is 83Mhz : 7.8usec/(1/83Mhz) = 0x287
+ */
+ if (pll_changing)
+ s5pv210_set_refresh(DMC1, 83000);
+ else
+ s5pv210_set_refresh(DMC1, 100000);
+
+ s5pv210_set_refresh(DMC0, 83000);
+ }
+
+ /*
+ * APLL should be changed in this level
+ * APLL -> MPLL(for stable transition) -> APLL
+ * Some clock source's clock API are not prepared.
+ * Do not use clock API in below code.
+ */
+ if (pll_changing) {
+ /*
+ * 1. Temporary Change divider for MFC and G3D
+ * SCLKA2M(200/1=200)->(200/4=50)Mhz
+ */
+ reg = __raw_readl(S5P_CLK_DIV2);
+ reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
+ reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) |
+ (3 << S5P_CLKDIV2_MFC_SHIFT);
+ __raw_writel(reg, S5P_CLK_DIV2);
+
+ /* For MFC, G3D dividing */
+ do {
+ reg = __raw_readl(S5P_CLKDIV_STAT0);
+ } while (reg & ((1 << 16) | (1 << 17)));
+
+ /*
+ * 2. Change SCLKA2M(200Mhz)to SCLKMPLL in MFC_MUX, G3D MUX
+ * (200/4=50)->(667/4=166)Mhz
+ */
+ reg = __raw_readl(S5P_CLK_SRC2);
+ reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
+ reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) |
+ (1 << S5P_CLKSRC2_MFC_SHIFT);
+ __raw_writel(reg, S5P_CLK_SRC2);
+
+ do {
+ reg = __raw_readl(S5P_CLKMUX_STAT1);
+ } while (reg & ((1 << 7) | (1 << 3)));
+
+ /*
+ * 3. DMC1 refresh count for 133Mhz if (index == L4) is
+ * true refresh counter is already programed in upper
+ * code. 0x287@83Mhz
+ */
+ if (!bus_speed_changing)
+ s5pv210_set_refresh(DMC1, 133000);
+
+ /* 4. SCLKAPLL -> SCLKMPLL */
+ reg = __raw_readl(S5P_CLK_SRC0);
+ reg &= ~(S5P_CLKSRC0_MUX200_MASK);
+ reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT);
+ __raw_writel(reg, S5P_CLK_SRC0);
+
+ do {
+ reg = __raw_readl(S5P_CLKMUX_STAT0);
+ } while (reg & (0x1 << 18));
+
+ }
+
+ /* Change divider */
+ reg = __raw_readl(S5P_CLK_DIV0);
+
+ reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK |
+ S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK |
+ S5P_CLKDIV0_HCLK166_MASK | S5P_CLKDIV0_PCLK83_MASK |
+ S5P_CLKDIV0_HCLK133_MASK | S5P_CLKDIV0_PCLK66_MASK);
+
+ reg |= ((clkdiv_val[index][0] << S5P_CLKDIV0_APLL_SHIFT) |
+ (clkdiv_val[index][1] << S5P_CLKDIV0_A2M_SHIFT) |
+ (clkdiv_val[index][2] << S5P_CLKDIV0_HCLK200_SHIFT) |
+ (clkdiv_val[index][3] << S5P_CLKDIV0_PCLK100_SHIFT) |
+ (clkdiv_val[index][4] << S5P_CLKDIV0_HCLK166_SHIFT) |
+ (clkdiv_val[index][5] << S5P_CLKDIV0_PCLK83_SHIFT) |
+ (clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) |
+ (clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT));
+
+ __raw_writel(reg, S5P_CLK_DIV0);
+
+ do {
+ reg = __raw_readl(S5P_CLKDIV_STAT0);
+ } while (reg & 0xff);
+
+ /* ARM MCS value changed */
+ reg = __raw_readl(S5P_ARM_MCS_CON);
+ reg &= ~0x3;
+ if (index >= L3)
+ reg |= 0x3;
+ else
+ reg |= 0x1;
+
+ __raw_writel(reg, S5P_ARM_MCS_CON);
+
+ if (pll_changing) {
+ /* 5. Set Lock time = 30us*24Mhz = 0x2cf */
+ __raw_writel(0x2cf, S5P_APLL_LOCK);
+
+ /*
+ * 6. Turn on APLL
+ * 6-1. Set PMS values
+ * 6-2. Wait untile the PLL is locked
+ */
+ if (index == L0)
+ __raw_writel(APLL_VAL_1000, S5P_APLL_CON);
+ else
+ __raw_writel(APLL_VAL_800, S5P_APLL_CON);
+
+ do {
+ reg = __raw_readl(S5P_APLL_CON);
+ } while (!(reg & (0x1 << 29)));
+
+ /*
+ * 7. Change souce clock from SCLKMPLL(667Mhz)
+ * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX
+ * (667/4=166)->(200/4=50)Mhz
+ */
+ reg = __raw_readl(S5P_CLK_SRC2);
+ reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
+ reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) |
+ (0 << S5P_CLKSRC2_MFC_SHIFT);
+ __raw_writel(reg, S5P_CLK_SRC2);
+
+ do {
+ reg = __raw_readl(S5P_CLKMUX_STAT1);
+ } while (reg & ((1 << 7) | (1 << 3)));
+
+ /*
+ * 8. Change divider for MFC and G3D
+ * (200/4=50)->(200/1=200)Mhz
+ */
+ reg = __raw_readl(S5P_CLK_DIV2);
+ reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
+ reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) |
+ (clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT);
+ __raw_writel(reg, S5P_CLK_DIV2);
+
+ /* For MFC, G3D dividing */
+ do {
+ reg = __raw_readl(S5P_CLKDIV_STAT0);
+ } while (reg & ((1 << 16) | (1 << 17)));
+
+ /* 9. Change MPLL to APLL in MSYS_MUX */
+ reg = __raw_readl(S5P_CLK_SRC0);
+ reg &= ~(S5P_CLKSRC0_MUX200_MASK);
+ reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT);
+ __raw_writel(reg, S5P_CLK_SRC0);
+
+ do {
+ reg = __raw_readl(S5P_CLKMUX_STAT0);
+ } while (reg & (0x1 << 18));
+
+ /*
+ * 10. DMC1 refresh counter
+ * L4 : DMC1 = 100Mhz 7.8us/(1/100) = 0x30c
+ * Others : DMC1 = 200Mhz 7.8us/(1/200) = 0x618
+ */
+ if (!bus_speed_changing)
+ s5pv210_set_refresh(DMC1, 200000);
+ }
+
+ /*
+ * L4 level need to change memory bus speed, hence onedram clock divier
+ * and memory refresh parameter should be changed
+ */
+ if (bus_speed_changing) {
+ reg = __raw_readl(S5P_CLK_DIV6);
+ reg &= ~S5P_CLKDIV6_ONEDRAM_MASK;
+ reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT);
+ __raw_writel(reg, S5P_CLK_DIV6);
+
+ do {
+ reg = __raw_readl(S5P_CLKDIV_STAT1);
+ } while (reg & (1 << 15));
+
+ /* Reconfigure DRAM refresh counter value */
+ if (index != L4) {
+ /*
+ * DMC0 : 166Mhz
+ * DMC1 : 200Mhz
+ */
+ s5pv210_set_refresh(DMC0, 166000);
+ s5pv210_set_refresh(DMC1, 200000);
+ } else {
+ /*
+ * DMC0 : 83Mhz
+ * DMC1 : 100Mhz
+ */
+ s5pv210_set_refresh(DMC0, 83000);
+ s5pv210_set_refresh(DMC1, 100000);
+ }
+ }
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ if (freqs.new < freqs.old) {
+ regulator_set_voltage(int_regulator,
+ int_volt, int_volt_max);
+
+ regulator_set_voltage(arm_regulator,
+ arm_volt, arm_volt_max);
+ }
+
+ printk(KERN_DEBUG "Perf changed[L%d]\n", index);
+
+exit:
+ mutex_unlock(&set_freq_lock);
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int s5pv210_cpufreq_suspend(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+
+static int s5pv210_cpufreq_resume(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+#endif
+
+static int check_mem_type(void __iomem *dmc_reg)
+{
+ unsigned long val;
+
+ val = __raw_readl(dmc_reg + 0x4);
+ val = (val & (0xf << 8));
+
+ return val >> 8;
+}
+
+static int __init s5pv210_cpu_init(struct cpufreq_policy *policy)
+{
+ unsigned long mem_type;
+ int ret;
+
+ cpu_clk = clk_get(NULL, "armclk");
+ if (IS_ERR(cpu_clk))
+ return PTR_ERR(cpu_clk);
+
+ dmc0_clk = clk_get(NULL, "sclk_dmc0");
+ if (IS_ERR(dmc0_clk)) {
+ ret = PTR_ERR(dmc0_clk);
+ goto out_dmc0;
+ }
+
+ dmc1_clk = clk_get(NULL, "hclk_msys");
+ if (IS_ERR(dmc1_clk)) {
+ ret = PTR_ERR(dmc1_clk);
+ goto out_dmc1;
+ }
+
+ if (policy->cpu != 0) {
+ ret = -EINVAL;
+ goto out_dmc1;
+ }
+
+ /*
+ * check_mem_type : This driver only support LPDDR & LPDDR2.
+ * other memory type is not supported.
+ */
+ mem_type = check_mem_type(S5P_VA_DMC0);
+
+ if ((mem_type != LPDDR) && (mem_type != LPDDR2)) {
+ printk(KERN_ERR "CPUFreq doesn't support this memory type\n");
+ ret = -EINVAL;
+ goto out_dmc1;
+ }
+
+ /* Find current refresh counter and frequency each DMC */
+ s5pv210_dram_conf[0].refresh = (__raw_readl(S5P_VA_DMC0 + 0x30) * 1000);
+ s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk);
+
+ s5pv210_dram_conf[1].refresh = (__raw_readl(S5P_VA_DMC1 + 0x30) * 1000);
+ s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk);
+
+ policy->cur = policy->min = policy->max = s5pv210_getspeed(0);
+
+ cpufreq_frequency_table_get_attr(s5pv210_freq_table, policy->cpu);
+
+ policy->cpuinfo.transition_latency = 40000;
+
+ return cpufreq_frequency_table_cpuinfo(policy, s5pv210_freq_table);
+
+out_dmc1:
+ clk_put(dmc0_clk);
+out_dmc0:
+ clk_put(cpu_clk);
+ return ret;
+}
+
+static int s5pv210_cpufreq_notifier_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ int ret;
+
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
+ DISABLE_FURTHER_CPUFREQ);
+ if (ret < 0)
+ return NOTIFY_BAD;
+
+ return NOTIFY_OK;
+ case PM_POST_RESTORE:
+ case PM_POST_SUSPEND:
+ cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
+ ENABLE_FURTHER_CPUFREQ);
+
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int s5pv210_cpufreq_reboot_notifier_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ int ret;
+
+ ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
+ DISABLE_FURTHER_CPUFREQ);
+ if (ret < 0)
+ return NOTIFY_BAD;
+
+ return NOTIFY_DONE;
+}
+
+static struct cpufreq_driver s5pv210_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = s5pv210_verify_speed,
+ .target = s5pv210_target,
+ .get = s5pv210_getspeed,
+ .init = s5pv210_cpu_init,
+ .name = "s5pv210",
+#ifdef CONFIG_PM
+ .suspend = s5pv210_cpufreq_suspend,
+ .resume = s5pv210_cpufreq_resume,
+#endif
+};
+
+static struct notifier_block s5pv210_cpufreq_notifier = {
+ .notifier_call = s5pv210_cpufreq_notifier_event,
+};
+
+static struct notifier_block s5pv210_cpufreq_reboot_notifier = {
+ .notifier_call = s5pv210_cpufreq_reboot_notifier_event,
+};
+
+static int __init s5pv210_cpufreq_init(void)
+{
+ arm_regulator = regulator_get(NULL, "vddarm");
+ if (IS_ERR(arm_regulator)) {
+ pr_err("failed to get regulator vddarm");
+ return PTR_ERR(arm_regulator);
+ }
+
+ int_regulator = regulator_get(NULL, "vddint");
+ if (IS_ERR(int_regulator)) {
+ pr_err("failed to get regulator vddint");
+ regulator_put(arm_regulator);
+ return PTR_ERR(int_regulator);
+ }
+
+ register_pm_notifier(&s5pv210_cpufreq_notifier);
+ register_reboot_notifier(&s5pv210_cpufreq_reboot_notifier);
+
+ return cpufreq_register_driver(&s5pv210_driver);
+}
+
+late_initcall(s5pv210_cpufreq_init);
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index bf5092455a8f..06ce2680d00d 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -12,11 +12,12 @@
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/notifier.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/cpu.h>
#include <linux/cpuidle.h>
#include <linux/ktime.h>
#include <linux/hrtimer.h>
+#include <linux/module.h>
#include <trace/events/power.h>
#include "cpuidle.h"
@@ -25,9 +26,19 @@ DEFINE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
DEFINE_MUTEX(cpuidle_lock);
LIST_HEAD(cpuidle_detected_devices);
-static void (*pm_idle_old)(void);
static int enabled_devices;
+static int off __read_mostly;
+static int initialized __read_mostly;
+
+int cpuidle_disabled(void)
+{
+ return off;
+}
+void disable_cpuidle(void)
+{
+ off = 1;
+}
#if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT)
static void cpuidle_kick_cpus(void)
@@ -46,25 +57,24 @@ static int __cpuidle_register_device(struct cpuidle_device *dev);
* cpuidle_idle_call - the main idle loop
*
* NOTE: no locks or semaphores should be used here
+ * return non-zero on failure
*/
-static void cpuidle_idle_call(void)
+int cpuidle_idle_call(void)
{
struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
+ struct cpuidle_driver *drv = cpuidle_get_driver();
struct cpuidle_state *target_state;
- int next_state;
+ int next_state, entered_state;
+
+ if (off)
+ return -ENODEV;
+
+ if (!initialized)
+ return -ENODEV;
/* check if the device is ready */
- if (!dev || !dev->enabled) {
- if (pm_idle_old)
- pm_idle_old();
- else
-#if defined(CONFIG_ARCH_HAS_DEFAULT_IDLE)
- default_idle();
-#else
- local_irq_enable();
-#endif
- return;
- }
+ if (!dev || !dev->enabled)
+ return -EBUSY;
#if 0
/* shows regressions, re-enable for 2.6.29 */
@@ -75,45 +85,38 @@ static void cpuidle_idle_call(void)
hrtimer_peek_ahead_timers();
#endif
- /*
- * Call the device's prepare function before calling the
- * governor's select function. ->prepare gives the device's
- * cpuidle driver a chance to update any dynamic information
- * of its cpuidle states for the current idle period, e.g.
- * state availability, latencies, residencies, etc.
- */
- if (dev->prepare)
- dev->prepare(dev);
-
/* ask the governor for the next state */
- next_state = cpuidle_curr_governor->select(dev);
+ next_state = cpuidle_curr_governor->select(drv, dev);
if (need_resched()) {
local_irq_enable();
- return;
+ return 0;
}
- target_state = &dev->states[next_state];
-
- /* enter the state and update stats */
- dev->last_state = target_state;
+ target_state = &drv->states[next_state];
trace_power_start(POWER_CSTATE, next_state, dev->cpu);
trace_cpu_idle(next_state, dev->cpu);
- dev->last_residency = target_state->enter(dev, target_state);
+ entered_state = target_state->enter(dev, drv, next_state);
trace_power_end(dev->cpu);
trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu);
- if (dev->last_state)
- target_state = dev->last_state;
-
- target_state->time += (unsigned long long)dev->last_residency;
- target_state->usage++;
+ if (entered_state >= 0) {
+ /* Update cpuidle counters */
+ /* This can be moved to within driver enter routine
+ * but that results in multiple copies of same code.
+ */
+ dev->states_usage[entered_state].time +=
+ (unsigned long long)dev->last_residency;
+ dev->states_usage[entered_state].usage++;
+ }
/* give the governor an opportunity to reflect on the outcome */
if (cpuidle_curr_governor->reflect)
- cpuidle_curr_governor->reflect(dev);
+ cpuidle_curr_governor->reflect(dev, entered_state);
+
+ return 0;
}
/**
@@ -121,10 +124,10 @@ static void cpuidle_idle_call(void)
*/
void cpuidle_install_idle_handler(void)
{
- if (enabled_devices && (pm_idle != cpuidle_idle_call)) {
+ if (enabled_devices) {
/* Make sure all changes finished before we switch to new idle */
smp_wmb();
- pm_idle = cpuidle_idle_call;
+ initialized = 1;
}
}
@@ -133,8 +136,8 @@ void cpuidle_install_idle_handler(void)
*/
void cpuidle_uninstall_idle_handler(void)
{
- if (enabled_devices && pm_idle_old && (pm_idle != pm_idle_old)) {
- pm_idle = pm_idle_old;
+ if (enabled_devices) {
+ initialized = 0;
cpuidle_kick_cpus();
}
}
@@ -162,11 +165,11 @@ void cpuidle_resume_and_unlock(void)
EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock);
#ifdef CONFIG_ARCH_HAS_CPU_RELAX
-static int poll_idle(struct cpuidle_device *dev, struct cpuidle_state *st)
+static int poll_idle(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
{
ktime_t t1, t2;
s64 diff;
- int ret;
t1 = ktime_get();
local_irq_enable();
@@ -178,15 +181,14 @@ static int poll_idle(struct cpuidle_device *dev, struct cpuidle_state *st)
if (diff > INT_MAX)
diff = INT_MAX;
- ret = (int) diff;
- return ret;
+ dev->last_residency = (int) diff;
+
+ return index;
}
-static void poll_idle_init(struct cpuidle_device *dev)
+static void poll_idle_init(struct cpuidle_driver *drv)
{
- struct cpuidle_state *state = &dev->states[0];
-
- cpuidle_set_statedata(state, NULL);
+ struct cpuidle_state *state = &drv->states[0];
snprintf(state->name, CPUIDLE_NAME_LEN, "POLL");
snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE");
@@ -197,7 +199,7 @@ static void poll_idle_init(struct cpuidle_device *dev)
state->enter = poll_idle;
}
#else
-static void poll_idle_init(struct cpuidle_device *dev) {}
+static void poll_idle_init(struct cpuidle_driver *drv) {}
#endif /* CONFIG_ARCH_HAS_CPU_RELAX */
/**
@@ -224,21 +226,20 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
return ret;
}
- poll_idle_init(dev);
+ poll_idle_init(cpuidle_get_driver());
if ((ret = cpuidle_add_state_sysfs(dev)))
return ret;
if (cpuidle_curr_governor->enable &&
- (ret = cpuidle_curr_governor->enable(dev)))
+ (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev)))
goto fail_sysfs;
for (i = 0; i < dev->state_count; i++) {
- dev->states[i].usage = 0;
- dev->states[i].time = 0;
+ dev->states_usage[i].usage = 0;
+ dev->states_usage[i].time = 0;
}
dev->last_residency = 0;
- dev->last_state = NULL;
smp_wmb();
@@ -272,7 +273,7 @@ void cpuidle_disable_device(struct cpuidle_device *dev)
dev->enabled = 0;
if (cpuidle_curr_governor->disable)
- cpuidle_curr_governor->disable(dev);
+ cpuidle_curr_governor->disable(cpuidle_get_driver(), dev);
cpuidle_remove_state_sysfs(dev);
enabled_devices--;
@@ -300,26 +301,6 @@ static int __cpuidle_register_device(struct cpuidle_device *dev)
init_completion(&dev->kobj_unregister);
- /*
- * cpuidle driver should set the dev->power_specified bit
- * before registering the device if the driver provides
- * power_usage numbers.
- *
- * For those devices whose ->power_specified is not set,
- * we fill in power_usage with decreasing values as the
- * cpuidle code has an implicit assumption that state Cn
- * uses less power than C(n-1).
- *
- * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned
- * an power value of -1. So we use -2, -3, etc, for other
- * c-states.
- */
- if (!dev->power_specified) {
- int i;
- for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++)
- dev->states[i].power_usage = -1 - i;
- }
-
per_cpu(cpuidle_devices, dev->cpu) = dev;
list_add(&dev->device_list, &cpuidle_detected_devices);
if ((ret = cpuidle_add_sysfs(sys_dev))) {
@@ -427,7 +408,8 @@ static int __init cpuidle_init(void)
{
int ret;
- pm_idle_old = pm_idle;
+ if (cpuidle_disabled())
+ return -ENODEV;
ret = cpuidle_add_class_sysfs(&cpu_sysdev_class);
if (ret)
@@ -438,4 +420,5 @@ static int __init cpuidle_init(void)
return 0;
}
+module_param(off, int, 0444);
core_initcall(cpuidle_init);
diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h
index 33e50d556f17..38c3fd8b9d76 100644
--- a/drivers/cpuidle/cpuidle.h
+++ b/drivers/cpuidle/cpuidle.h
@@ -13,6 +13,7 @@ extern struct list_head cpuidle_governors;
extern struct list_head cpuidle_detected_devices;
extern struct mutex cpuidle_lock;
extern spinlock_t cpuidle_driver_lock;
+extern int cpuidle_disabled(void);
/* idle loop */
extern void cpuidle_install_idle_handler(void);
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index fd1601e3d125..284d7af5a9c8 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -17,6 +17,30 @@
static struct cpuidle_driver *cpuidle_curr_driver;
DEFINE_SPINLOCK(cpuidle_driver_lock);
+static void __cpuidle_register_driver(struct cpuidle_driver *drv)
+{
+ int i;
+ /*
+ * cpuidle driver should set the drv->power_specified bit
+ * before registering if the driver provides
+ * power_usage numbers.
+ *
+ * If power_specified is not set,
+ * we fill in power_usage with decreasing values as the
+ * cpuidle code has an implicit assumption that state Cn
+ * uses less power than C(n-1).
+ *
+ * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned
+ * an power value of -1. So we use -2, -3, etc, for other
+ * c-states.
+ */
+ if (!drv->power_specified) {
+ for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++)
+ drv->states[i].power_usage = -1 - i;
+ }
+}
+
+
/**
* cpuidle_register_driver - registers a driver
* @drv: the driver
@@ -26,11 +50,15 @@ int cpuidle_register_driver(struct cpuidle_driver *drv)
if (!drv)
return -EINVAL;
+ if (cpuidle_disabled())
+ return -ENODEV;
+
spin_lock(&cpuidle_driver_lock);
if (cpuidle_curr_driver) {
spin_unlock(&cpuidle_driver_lock);
return -EBUSY;
}
+ __cpuidle_register_driver(drv);
cpuidle_curr_driver = drv;
spin_unlock(&cpuidle_driver_lock);
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index 724c164d31c9..ea2f8e7aa24a 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -81,6 +81,9 @@ int cpuidle_register_governor(struct cpuidle_governor *gov)
if (!gov || !gov->select)
return -EINVAL;
+ if (cpuidle_disabled())
+ return -ENODEV;
+
mutex_lock(&cpuidle_lock);
if (__cpuidle_find_governor(gov->name) == NULL) {
ret = 0;
diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c
index 12c98900dcf8..b6a09ea859b1 100644
--- a/drivers/cpuidle/governors/ladder.c
+++ b/drivers/cpuidle/governors/ladder.c
@@ -14,8 +14,8 @@
#include <linux/kernel.h>
#include <linux/cpuidle.h>
-#include <linux/pm_qos_params.h>
-#include <linux/moduleparam.h>
+#include <linux/pm_qos.h>
+#include <linux/module.h>
#include <linux/jiffies.h>
#include <asm/io.h>
@@ -60,9 +60,11 @@ static inline void ladder_do_selection(struct ladder_device *ldev,
/**
* ladder_select_state - selects the next state to enter
+ * @drv: cpuidle driver
* @dev: the CPU
*/
-static int ladder_select_state(struct cpuidle_device *dev)
+static int ladder_select_state(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
{
struct ladder_device *ldev = &__get_cpu_var(ladder_devices);
struct ladder_device_state *last_state;
@@ -77,15 +79,17 @@ static int ladder_select_state(struct cpuidle_device *dev)
last_state = &ldev->states[last_idx];
- if (dev->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID)
- last_residency = cpuidle_get_last_residency(dev) - dev->states[last_idx].exit_latency;
+ if (drv->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID) {
+ last_residency = cpuidle_get_last_residency(dev) - \
+ drv->states[last_idx].exit_latency;
+ }
else
last_residency = last_state->threshold.promotion_time + 1;
/* consider promotion */
- if (last_idx < dev->state_count - 1 &&
+ if (last_idx < drv->state_count - 1 &&
last_residency > last_state->threshold.promotion_time &&
- dev->states[last_idx + 1].exit_latency <= latency_req) {
+ drv->states[last_idx + 1].exit_latency <= latency_req) {
last_state->stats.promotion_count++;
last_state->stats.demotion_count = 0;
if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) {
@@ -96,11 +100,11 @@ static int ladder_select_state(struct cpuidle_device *dev)
/* consider demotion */
if (last_idx > CPUIDLE_DRIVER_STATE_START &&
- dev->states[last_idx].exit_latency > latency_req) {
+ drv->states[last_idx].exit_latency > latency_req) {
int i;
for (i = last_idx - 1; i > CPUIDLE_DRIVER_STATE_START; i--) {
- if (dev->states[i].exit_latency <= latency_req)
+ if (drv->states[i].exit_latency <= latency_req)
break;
}
ladder_do_selection(ldev, last_idx, i);
@@ -123,9 +127,11 @@ static int ladder_select_state(struct cpuidle_device *dev)
/**
* ladder_enable_device - setup for the governor
+ * @drv: cpuidle driver
* @dev: the CPU
*/
-static int ladder_enable_device(struct cpuidle_device *dev)
+static int ladder_enable_device(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
{
int i;
struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu);
@@ -134,8 +140,8 @@ static int ladder_enable_device(struct cpuidle_device *dev)
ldev->last_state_idx = CPUIDLE_DRIVER_STATE_START;
- for (i = 0; i < dev->state_count; i++) {
- state = &dev->states[i];
+ for (i = 0; i < drv->state_count; i++) {
+ state = &drv->states[i];
lstate = &ldev->states[i];
lstate->stats.promotion_count = 0;
@@ -144,7 +150,7 @@ static int ladder_enable_device(struct cpuidle_device *dev)
lstate->threshold.promotion_count = PROMOTION_COUNT;
lstate->threshold.demotion_count = DEMOTION_COUNT;
- if (i < dev->state_count - 1)
+ if (i < drv->state_count - 1)
lstate->threshold.promotion_time = state->exit_latency;
if (i > 0)
lstate->threshold.demotion_time = state->exit_latency;
@@ -153,11 +159,24 @@ static int ladder_enable_device(struct cpuidle_device *dev)
return 0;
}
+/**
+ * ladder_reflect - update the correct last_state_idx
+ * @dev: the CPU
+ * @index: the index of actual state entered
+ */
+static void ladder_reflect(struct cpuidle_device *dev, int index)
+{
+ struct ladder_device *ldev = &__get_cpu_var(ladder_devices);
+ if (index > 0)
+ ldev->last_state_idx = index;
+}
+
static struct cpuidle_governor ladder_governor = {
.name = "ladder",
.rating = 10,
.enable = ladder_enable_device,
.select = ladder_select_state,
+ .reflect = ladder_reflect,
.owner = THIS_MODULE,
};
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index c47f3d09c1ee..ad0952601ae2 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -12,13 +12,14 @@
#include <linux/kernel.h>
#include <linux/cpuidle.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/time.h>
#include <linux/ktime.h>
#include <linux/hrtimer.h>
#include <linux/tick.h>
#include <linux/sched.h>
#include <linux/math64.h>
+#include <linux/module.h>
#define BUCKETS 12
#define INTERVALS 8
@@ -182,7 +183,7 @@ static inline int performance_multiplier(void)
static DEFINE_PER_CPU(struct menu_device, menu_devices);
-static void menu_update(struct cpuidle_device *dev);
+static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev);
/* This implements DIV_ROUND_CLOSEST but avoids 64 bit division */
static u64 div_round64(u64 dividend, u32 divisor)
@@ -228,9 +229,10 @@ static void detect_repeating_patterns(struct menu_device *data)
/**
* menu_select - selects the next idle state to enter
+ * @drv: cpuidle driver containing state data
* @dev: the CPU
*/
-static int menu_select(struct cpuidle_device *dev)
+static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
struct menu_device *data = &__get_cpu_var(menu_devices);
int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
@@ -240,7 +242,7 @@ static int menu_select(struct cpuidle_device *dev)
struct timespec t;
if (data->needs_update) {
- menu_update(dev);
+ menu_update(drv, dev);
data->needs_update = 0;
}
@@ -285,11 +287,9 @@ static int menu_select(struct cpuidle_device *dev)
* Find the idle state with the lowest power while satisfying
* our constraints.
*/
- for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) {
- struct cpuidle_state *s = &dev->states[i];
+ for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
+ struct cpuidle_state *s = &drv->states[i];
- if (s->flags & CPUIDLE_FLAG_IGNORE)
- continue;
if (s->target_residency > data->predicted_us)
continue;
if (s->exit_latency > latency_req)
@@ -310,26 +310,30 @@ static int menu_select(struct cpuidle_device *dev)
/**
* menu_reflect - records that data structures need update
* @dev: the CPU
+ * @index: the index of actual entered state
*
* NOTE: it's important to be fast here because this operation will add to
* the overall exit latency.
*/
-static void menu_reflect(struct cpuidle_device *dev)
+static void menu_reflect(struct cpuidle_device *dev, int index)
{
struct menu_device *data = &__get_cpu_var(menu_devices);
- data->needs_update = 1;
+ data->last_state_idx = index;
+ if (index >= 0)
+ data->needs_update = 1;
}
/**
* menu_update - attempts to guess what happened after entry
+ * @drv: cpuidle driver containing state data
* @dev: the CPU
*/
-static void menu_update(struct cpuidle_device *dev)
+static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
struct menu_device *data = &__get_cpu_var(menu_devices);
int last_idx = data->last_state_idx;
unsigned int last_idle_us = cpuidle_get_last_residency(dev);
- struct cpuidle_state *target = &dev->states[last_idx];
+ struct cpuidle_state *target = &drv->states[last_idx];
unsigned int measured_us;
u64 new_factor;
@@ -383,9 +387,11 @@ static void menu_update(struct cpuidle_device *dev)
/**
* menu_enable_device - scans a CPU's states and does setup
+ * @drv: cpuidle driver
* @dev: the CPU
*/
-static int menu_enable_device(struct cpuidle_device *dev)
+static int menu_enable_device(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
{
struct menu_device *data = &per_cpu(menu_devices, dev->cpu);
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index be7917ec40c9..1e756e160dca 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -216,7 +216,8 @@ static struct kobj_type ktype_cpuidle = {
struct cpuidle_state_attr {
struct attribute attr;
- ssize_t (*show)(struct cpuidle_state *, char *);
+ ssize_t (*show)(struct cpuidle_state *, \
+ struct cpuidle_state_usage *, char *);
ssize_t (*store)(struct cpuidle_state *, const char *, size_t);
};
@@ -224,19 +225,22 @@ struct cpuidle_state_attr {
static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL)
#define define_show_state_function(_name) \
-static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \
+static ssize_t show_state_##_name(struct cpuidle_state *state, \
+ struct cpuidle_state_usage *state_usage, char *buf) \
{ \
return sprintf(buf, "%u\n", state->_name);\
}
#define define_show_state_ull_function(_name) \
-static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \
+static ssize_t show_state_##_name(struct cpuidle_state *state, \
+ struct cpuidle_state_usage *state_usage, char *buf) \
{ \
- return sprintf(buf, "%llu\n", state->_name);\
+ return sprintf(buf, "%llu\n", state_usage->_name);\
}
#define define_show_state_str_function(_name) \
-static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \
+static ssize_t show_state_##_name(struct cpuidle_state *state, \
+ struct cpuidle_state_usage *state_usage, char *buf) \
{ \
if (state->_name[0] == '\0')\
return sprintf(buf, "<null>\n");\
@@ -269,16 +273,18 @@ static struct attribute *cpuidle_state_default_attrs[] = {
#define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj)
#define kobj_to_state(k) (kobj_to_state_obj(k)->state)
+#define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage)
#define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr)
static ssize_t cpuidle_state_show(struct kobject * kobj,
struct attribute * attr ,char * buf)
{
int ret = -EIO;
struct cpuidle_state *state = kobj_to_state(kobj);
+ struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj);
struct cpuidle_state_attr * cattr = attr_to_stateattr(attr);
if (cattr->show)
- ret = cattr->show(state, buf);
+ ret = cattr->show(state, state_usage, buf);
return ret;
}
@@ -316,13 +322,15 @@ int cpuidle_add_state_sysfs(struct cpuidle_device *device)
{
int i, ret = -ENOMEM;
struct cpuidle_state_kobj *kobj;
+ struct cpuidle_driver *drv = cpuidle_get_driver();
/* state statistics */
for (i = 0; i < device->state_count; i++) {
kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL);
if (!kobj)
goto error_state;
- kobj->state = &device->states[i];
+ kobj->state = &drv->states[i];
+ kobj->state_usage = &device->states_usage[i];
init_completion(&kobj->kobj_unregister);
ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, &device->kobj,
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index e0b25de1e339..6d16b4b0d7a0 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -200,6 +200,7 @@ config CRYPTO_DEV_HIFN_795X
select CRYPTO_BLKCIPHER
select HW_RANDOM if CRYPTO_DEV_HIFN_795X_RNG
depends on PCI
+ depends on !ARCH_DMA_ADDR_T_64BIT
help
This option allows you to have support for HIFN 795x crypto adapters.
@@ -266,7 +267,7 @@ config CRYPTO_DEV_OMAP_AES
config CRYPTO_DEV_PICOXCELL
tristate "Support for picoXcell IPSEC and Layer2 crypto engines"
- depends on ARCH_PICOXCELL
+ depends on ARCH_PICOXCELL && HAVE_CLK
select CRYPTO_AES
select CRYPTO_AUTHENC
select CRYPTO_ALGAPI
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
index 18912521a7a5..1d103f997dc2 100644
--- a/drivers/crypto/amcc/crypto4xx_core.c
+++ b/drivers/crypto/amcc/crypto4xx_core.c
@@ -51,6 +51,7 @@ static void crypto4xx_hw_init(struct crypto4xx_device *dev)
union ce_io_threshold io_threshold;
u32 rand_num;
union ce_pe_dma_cfg pe_dma_cfg;
+ u32 device_ctrl;
writel(PPC4XX_BYTE_ORDER, dev->ce_base + CRYPTO4XX_BYTE_ORDER_CFG);
/* setup pe dma, include reset sg, pdr and pe, then release reset */
@@ -84,7 +85,9 @@ static void crypto4xx_hw_init(struct crypto4xx_device *dev)
writel(ring_size.w, dev->ce_base + CRYPTO4XX_RING_SIZE);
ring_ctrl.w = 0;
writel(ring_ctrl.w, dev->ce_base + CRYPTO4XX_RING_CTRL);
- writel(PPC4XX_DC_3DES_EN, dev->ce_base + CRYPTO4XX_DEVICE_CTRL);
+ device_ctrl = readl(dev->ce_base + CRYPTO4XX_DEVICE_CTRL);
+ device_ctrl |= PPC4XX_DC_3DES_EN;
+ writel(device_ctrl, dev->ce_base + CRYPTO4XX_DEVICE_CTRL);
writel(dev->gdr_pa, dev->ce_base + CRYPTO4XX_GATH_RING_BASE);
writel(dev->sdr_pa, dev->ce_base + CRYPTO4XX_SCAT_RING_BASE);
part_ring_size.w = 0;
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 676d957c22b0..4159265b453b 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -62,10 +62,22 @@
#define CAAM_MAX_IV_LENGTH 16
/* length of descriptors text */
-#define DESC_AEAD_SHARED_TEXT_LEN 4
-#define DESC_AEAD_ENCRYPT_TEXT_LEN 21
-#define DESC_AEAD_DECRYPT_TEXT_LEN 24
-#define DESC_AEAD_GIVENCRYPT_TEXT_LEN 27
+#define DESC_JOB_IO_LEN (CAAM_CMD_SZ * 3 + CAAM_PTR_SZ * 3)
+
+#define DESC_AEAD_BASE (4 * CAAM_CMD_SZ)
+#define DESC_AEAD_ENC_LEN (DESC_AEAD_BASE + 16 * CAAM_CMD_SZ)
+#define DESC_AEAD_DEC_LEN (DESC_AEAD_BASE + 21 * CAAM_CMD_SZ)
+#define DESC_AEAD_GIVENC_LEN (DESC_AEAD_ENC_LEN + 7 * CAAM_CMD_SZ)
+
+#define DESC_ABLKCIPHER_BASE (3 * CAAM_CMD_SZ)
+#define DESC_ABLKCIPHER_ENC_LEN (DESC_ABLKCIPHER_BASE + \
+ 20 * CAAM_CMD_SZ)
+#define DESC_ABLKCIPHER_DEC_LEN (DESC_ABLKCIPHER_BASE + \
+ 15 * CAAM_CMD_SZ)
+
+#define DESC_MAX_USED_BYTES (DESC_AEAD_GIVENC_LEN + \
+ CAAM_MAX_KEY_SIZE)
+#define DESC_MAX_USED_LEN (DESC_MAX_USED_BYTES / CAAM_CMD_SZ)
#ifdef DEBUG
/* for print_hex_dumps with line references */
@@ -76,30 +88,366 @@
#define debug(format, arg...)
#endif
+/* Set DK bit in class 1 operation if shared */
+static inline void append_dec_op1(u32 *desc, u32 type)
+{
+ u32 *jump_cmd, *uncond_jump_cmd;
+
+ jump_cmd = append_jump(desc, JUMP_TEST_ALL | JUMP_COND_SHRD);
+ append_operation(desc, type | OP_ALG_AS_INITFINAL |
+ OP_ALG_DECRYPT);
+ uncond_jump_cmd = append_jump(desc, JUMP_TEST_ALL);
+ set_jump_tgt_here(desc, jump_cmd);
+ append_operation(desc, type | OP_ALG_AS_INITFINAL |
+ OP_ALG_DECRYPT | OP_ALG_AAI_DK);
+ set_jump_tgt_here(desc, uncond_jump_cmd);
+}
+
+/*
+ * Wait for completion of class 1 key loading before allowing
+ * error propagation
+ */
+static inline void append_dec_shr_done(u32 *desc)
+{
+ u32 *jump_cmd;
+
+ jump_cmd = append_jump(desc, JUMP_CLASS_CLASS1 | JUMP_TEST_ALL);
+ set_jump_tgt_here(desc, jump_cmd);
+ append_cmd(desc, SET_OK_PROP_ERRORS | CMD_LOAD);
+}
+
+/*
+ * For aead functions, read payload and write payload,
+ * both of which are specified in req->src and req->dst
+ */
+static inline void aead_append_src_dst(u32 *desc, u32 msg_type)
+{
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH |
+ KEY_VLF | msg_type | FIFOLD_TYPE_LASTBOTH);
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF);
+}
+
+/*
+ * For aead encrypt and decrypt, read iv for both classes
+ */
+static inline void aead_append_ld_iv(u32 *desc, int ivsize)
+{
+ append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT |
+ LDST_CLASS_1_CCB | ivsize);
+ append_move(desc, MOVE_SRC_CLASS1CTX | MOVE_DEST_CLASS2INFIFO | ivsize);
+}
+
+/*
+ * For ablkcipher encrypt and decrypt, read from req->src and
+ * write to req->dst
+ */
+static inline void ablkcipher_append_src_dst(u32 *desc)
+{
+ append_math_add(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ); \
+ append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ); \
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | \
+ KEY_VLF | FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST1); \
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF); \
+}
+
+/*
+ * If all data, including src (with assoc and iv) or dst (with iv only) are
+ * contiguous
+ */
+#define GIV_SRC_CONTIG 1
+#define GIV_DST_CONTIG (1 << 1)
+
/*
* per-session context
*/
struct caam_ctx {
struct device *jrdev;
- u32 *sh_desc;
- dma_addr_t shared_desc_phys;
+ u32 sh_desc_enc[DESC_MAX_USED_LEN];
+ u32 sh_desc_dec[DESC_MAX_USED_LEN];
+ u32 sh_desc_givenc[DESC_MAX_USED_LEN];
+ dma_addr_t sh_desc_enc_dma;
+ dma_addr_t sh_desc_dec_dma;
+ dma_addr_t sh_desc_givenc_dma;
u32 class1_alg_type;
u32 class2_alg_type;
u32 alg_op;
- u8 *key;
- dma_addr_t key_phys;
+ u8 key[CAAM_MAX_KEY_SIZE];
+ dma_addr_t key_dma;
unsigned int enckeylen;
unsigned int split_key_len;
unsigned int split_key_pad_len;
unsigned int authsize;
};
-static int aead_authenc_setauthsize(struct crypto_aead *authenc,
+static void append_key_aead(u32 *desc, struct caam_ctx *ctx,
+ int keys_fit_inline)
+{
+ if (keys_fit_inline) {
+ append_key_as_imm(desc, ctx->key, ctx->split_key_pad_len,
+ ctx->split_key_len, CLASS_2 |
+ KEY_DEST_MDHA_SPLIT | KEY_ENC);
+ append_key_as_imm(desc, (void *)ctx->key +
+ ctx->split_key_pad_len, ctx->enckeylen,
+ ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
+ } else {
+ append_key(desc, ctx->key_dma, ctx->split_key_len, CLASS_2 |
+ KEY_DEST_MDHA_SPLIT | KEY_ENC);
+ append_key(desc, ctx->key_dma + ctx->split_key_pad_len,
+ ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
+ }
+}
+
+static void init_sh_desc_key_aead(u32 *desc, struct caam_ctx *ctx,
+ int keys_fit_inline)
+{
+ u32 *key_jump_cmd;
+
+ init_sh_desc(desc, HDR_SHARE_WAIT);
+
+ /* Skip if already shared */
+ key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_SHRD);
+
+ append_key_aead(desc, ctx, keys_fit_inline);
+
+ set_jump_tgt_here(desc, key_jump_cmd);
+
+ /* Propagate errors from shared to job descriptor */
+ append_cmd(desc, SET_OK_PROP_ERRORS | CMD_LOAD);
+}
+
+static int aead_set_sh_desc(struct crypto_aead *aead)
+{
+ struct aead_tfm *tfm = &aead->base.crt_aead;
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ bool keys_fit_inline = 0;
+ u32 *key_jump_cmd, *jump_cmd;
+ u32 geniv, moveiv;
+ u32 *desc;
+
+ if (!ctx->enckeylen || !ctx->authsize)
+ return 0;
+
+ /*
+ * Job Descriptor and Shared Descriptors
+ * must all fit into the 64-word Descriptor h/w Buffer
+ */
+ if (DESC_AEAD_ENC_LEN + DESC_JOB_IO_LEN +
+ ctx->split_key_pad_len + ctx->enckeylen <=
+ CAAM_DESC_BYTES_MAX)
+ keys_fit_inline = 1;
+
+ /* aead_encrypt shared descriptor */
+ desc = ctx->sh_desc_enc;
+
+ init_sh_desc_key_aead(desc, ctx, keys_fit_inline);
+
+ /* Class 2 operation */
+ append_operation(desc, ctx->class2_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* cryptlen = seqoutlen - authsize */
+ append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
+
+ /* assoclen + cryptlen = seqinlen - ivsize */
+ append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, tfm->ivsize);
+
+ /* assoclen + cryptlen = (assoclen + cryptlen) - cryptlen */
+ append_math_sub(desc, VARSEQINLEN, REG2, REG3, CAAM_CMD_SZ);
+
+ /* read assoc before reading payload */
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
+ KEY_VLF);
+ aead_append_ld_iv(desc, tfm->ivsize);
+
+ /* Class 1 operation */
+ append_operation(desc, ctx->class1_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* Read and write cryptlen bytes */
+ append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+ append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
+ aead_append_src_dst(desc, FIFOLD_TYPE_MSG1OUT2);
+
+ /* Write ICV */
+ append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB |
+ LDST_SRCDST_BYTE_CONTEXT);
+
+ ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead enc shdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+
+ /*
+ * Job Descriptor and Shared Descriptors
+ * must all fit into the 64-word Descriptor h/w Buffer
+ */
+ if (DESC_AEAD_DEC_LEN + DESC_JOB_IO_LEN +
+ ctx->split_key_pad_len + ctx->enckeylen <=
+ CAAM_DESC_BYTES_MAX)
+ keys_fit_inline = 1;
+
+ desc = ctx->sh_desc_dec;
+
+ /* aead_decrypt shared descriptor */
+ init_sh_desc(desc, HDR_SHARE_WAIT);
+
+ /* Skip if already shared */
+ key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_SHRD);
+
+ append_key_aead(desc, ctx, keys_fit_inline);
+
+ /* Only propagate error immediately if shared */
+ jump_cmd = append_jump(desc, JUMP_TEST_ALL);
+ set_jump_tgt_here(desc, key_jump_cmd);
+ append_cmd(desc, SET_OK_PROP_ERRORS | CMD_LOAD);
+ set_jump_tgt_here(desc, jump_cmd);
+
+ /* Class 2 operation */
+ append_operation(desc, ctx->class2_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT | OP_ALG_ICV_ON);
+
+ /* assoclen + cryptlen = seqinlen - ivsize */
+ append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
+ ctx->authsize + tfm->ivsize)
+ /* assoclen = (assoclen + cryptlen) - cryptlen */
+ append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
+ append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
+
+ /* read assoc before reading payload */
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
+ KEY_VLF);
+
+ aead_append_ld_iv(desc, tfm->ivsize);
+
+ append_dec_op1(desc, ctx->class1_alg_type);
+
+ /* Read and write cryptlen bytes */
+ append_math_add(desc, VARSEQINLEN, ZERO, REG2, CAAM_CMD_SZ);
+ append_math_add(desc, VARSEQOUTLEN, ZERO, REG2, CAAM_CMD_SZ);
+ aead_append_src_dst(desc, FIFOLD_TYPE_MSG);
+
+ /* Load ICV */
+ append_seq_fifo_load(desc, ctx->authsize, FIFOLD_CLASS_CLASS2 |
+ FIFOLD_TYPE_LAST2 | FIFOLD_TYPE_ICV);
+ append_dec_shr_done(desc);
+
+ ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead dec shdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+
+ /*
+ * Job Descriptor and Shared Descriptors
+ * must all fit into the 64-word Descriptor h/w Buffer
+ */
+ if (DESC_AEAD_GIVENC_LEN + DESC_JOB_IO_LEN +
+ ctx->split_key_pad_len + ctx->enckeylen <=
+ CAAM_DESC_BYTES_MAX)
+ keys_fit_inline = 1;
+
+ /* aead_givencrypt shared descriptor */
+ desc = ctx->sh_desc_givenc;
+
+ init_sh_desc_key_aead(desc, ctx, keys_fit_inline);
+
+ /* Generate IV */
+ geniv = NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DEST_DECO |
+ NFIFOENTRY_DTYPE_MSG | NFIFOENTRY_LC1 |
+ NFIFOENTRY_PTYPE_RND | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
+ append_load_imm_u32(desc, geniv, LDST_CLASS_IND_CCB |
+ LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
+ append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
+ append_move(desc, MOVE_SRC_INFIFO |
+ MOVE_DEST_CLASS1CTX | (tfm->ivsize << MOVE_LEN_SHIFT));
+ append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
+
+ /* Copy IV to class 1 context */
+ append_move(desc, MOVE_SRC_CLASS1CTX |
+ MOVE_DEST_OUTFIFO | (tfm->ivsize << MOVE_LEN_SHIFT));
+
+ /* Return to encryption */
+ append_operation(desc, ctx->class2_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* ivsize + cryptlen = seqoutlen - authsize */
+ append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
+
+ /* assoclen = seqinlen - (ivsize + cryptlen) */
+ append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
+
+ /* read assoc before reading payload */
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
+ KEY_VLF);
+
+ /* Copy iv from class 1 ctx to class 2 fifo*/
+ moveiv = NFIFOENTRY_STYPE_OFIFO | NFIFOENTRY_DEST_CLASS2 |
+ NFIFOENTRY_DTYPE_MSG | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
+ append_load_imm_u32(desc, moveiv, LDST_CLASS_IND_CCB |
+ LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
+ append_load_imm_u32(desc, tfm->ivsize, LDST_CLASS_2_CCB |
+ LDST_SRCDST_WORD_DATASZ_REG | LDST_IMM);
+
+ /* Class 1 operation */
+ append_operation(desc, ctx->class1_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* Will write ivsize + cryptlen */
+ append_math_add(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
+
+ /* Not need to reload iv */
+ append_seq_fifo_load(desc, tfm->ivsize,
+ FIFOLD_CLASS_SKIP);
+
+ /* Will read cryptlen */
+ append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
+ aead_append_src_dst(desc, FIFOLD_TYPE_MSG1OUT2);
+
+ /* Write ICV */
+ append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB |
+ LDST_SRCDST_BYTE_CONTEXT);
+
+ ctx->sh_desc_givenc_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_givenc_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead givenc shdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+
+ return 0;
+}
+
+static int aead_setauthsize(struct crypto_aead *authenc,
unsigned int authsize)
{
struct caam_ctx *ctx = crypto_aead_ctx(authenc);
ctx->authsize = authsize;
+ aead_set_sh_desc(authenc);
return 0;
}
@@ -117,6 +465,7 @@ static void split_key_done(struct device *dev, u32 *desc, u32 err,
#ifdef DEBUG
dev_err(dev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
#endif
+
if (err) {
char tmp[CAAM_ERROR_STR_MAX];
@@ -220,73 +569,7 @@ static u32 gen_split_key(struct caam_ctx *ctx, const u8 *key_in, u32 authkeylen)
return ret;
}
-static int build_sh_desc_ipsec(struct caam_ctx *ctx)
-{
- struct device *jrdev = ctx->jrdev;
- u32 *sh_desc;
- u32 *jump_cmd;
- bool keys_fit_inline = 0;
-
- /*
- * largest Job Descriptor and its Shared Descriptor
- * must both fit into the 64-word Descriptor h/w Buffer
- */
- if ((DESC_AEAD_GIVENCRYPT_TEXT_LEN +
- DESC_AEAD_SHARED_TEXT_LEN) * CAAM_CMD_SZ +
- ctx->split_key_pad_len + ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
- keys_fit_inline = 1;
-
- /* build shared descriptor for this session */
- sh_desc = kmalloc(CAAM_CMD_SZ * DESC_AEAD_SHARED_TEXT_LEN +
- (keys_fit_inline ?
- ctx->split_key_pad_len + ctx->enckeylen :
- CAAM_PTR_SZ * 2), GFP_DMA | GFP_KERNEL);
- if (!sh_desc) {
- dev_err(jrdev, "could not allocate shared descriptor\n");
- return -ENOMEM;
- }
-
- init_sh_desc(sh_desc, HDR_SAVECTX | HDR_SHARE_SERIAL);
-
- jump_cmd = append_jump(sh_desc, CLASS_BOTH | JUMP_TEST_ALL |
- JUMP_COND_SHRD | JUMP_COND_SELF);
-
- /*
- * process keys, starting with class 2/authentication.
- */
- if (keys_fit_inline) {
- append_key_as_imm(sh_desc, ctx->key, ctx->split_key_pad_len,
- ctx->split_key_len,
- CLASS_2 | KEY_DEST_MDHA_SPLIT | KEY_ENC);
-
- append_key_as_imm(sh_desc, (void *)ctx->key +
- ctx->split_key_pad_len, ctx->enckeylen,
- ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
- } else {
- append_key(sh_desc, ctx->key_phys, ctx->split_key_len, CLASS_2 |
- KEY_DEST_MDHA_SPLIT | KEY_ENC);
- append_key(sh_desc, ctx->key_phys + ctx->split_key_pad_len,
- ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
- }
-
- /* update jump cmd now that we are at the jump target */
- set_jump_tgt_here(sh_desc, jump_cmd);
-
- ctx->shared_desc_phys = dma_map_single(jrdev, sh_desc,
- desc_bytes(sh_desc),
- DMA_TO_DEVICE);
- if (dma_mapping_error(jrdev, ctx->shared_desc_phys)) {
- dev_err(jrdev, "unable to map shared descriptor\n");
- kfree(sh_desc);
- return -ENOMEM;
- }
-
- ctx->sh_desc = sh_desc;
-
- return 0;
-}
-
-static int aead_authenc_setkey(struct crypto_aead *aead,
+static int aead_setkey(struct crypto_aead *aead,
const u8 *key, unsigned int keylen)
{
/* Sizes for MDHA pads (*not* keys): MD5, SHA1, 224, 256, 384, 512 */
@@ -326,27 +609,19 @@ static int aead_authenc_setkey(struct crypto_aead *aead,
print_hex_dump(KERN_ERR, "key in @"xstr(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
#endif
- ctx->key = kmalloc(ctx->split_key_pad_len + enckeylen,
- GFP_KERNEL | GFP_DMA);
- if (!ctx->key) {
- dev_err(jrdev, "could not allocate key output memory\n");
- return -ENOMEM;
- }
ret = gen_split_key(ctx, key, authkeylen);
if (ret) {
- kfree(ctx->key);
goto badkey;
}
/* postpend encryption key to auth split key */
memcpy(ctx->key + ctx->split_key_pad_len, key + authkeylen, enckeylen);
- ctx->key_phys = dma_map_single(jrdev, ctx->key, ctx->split_key_pad_len +
+ ctx->key_dma = dma_map_single(jrdev, ctx->key, ctx->split_key_pad_len +
enckeylen, DMA_TO_DEVICE);
- if (dma_mapping_error(jrdev, ctx->key_phys)) {
+ if (dma_mapping_error(jrdev, ctx->key_dma)) {
dev_err(jrdev, "unable to map key i/o memory\n");
- kfree(ctx->key);
return -ENOMEM;
}
#ifdef DEBUG
@@ -357,11 +632,10 @@ static int aead_authenc_setkey(struct crypto_aead *aead,
ctx->enckeylen = enckeylen;
- ret = build_sh_desc_ipsec(ctx);
+ ret = aead_set_sh_desc(aead);
if (ret) {
- dma_unmap_single(jrdev, ctx->key_phys, ctx->split_key_pad_len +
+ dma_unmap_single(jrdev, ctx->key_dma, ctx->split_key_pad_len +
enckeylen, DMA_TO_DEVICE);
- kfree(ctx->key);
}
return ret;
@@ -370,6 +644,119 @@ badkey:
return -EINVAL;
}
+static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct caam_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher);
+ struct ablkcipher_tfm *tfm = &ablkcipher->base.crt_ablkcipher;
+ struct device *jrdev = ctx->jrdev;
+ int ret = 0;
+ u32 *key_jump_cmd, *jump_cmd;
+ u32 *desc;
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "key in @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
+#endif
+
+ memcpy(ctx->key, key, keylen);
+ ctx->key_dma = dma_map_single(jrdev, ctx->key, keylen,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->key_dma)) {
+ dev_err(jrdev, "unable to map key i/o memory\n");
+ return -ENOMEM;
+ }
+ ctx->enckeylen = keylen;
+
+ /* ablkcipher_encrypt shared descriptor */
+ desc = ctx->sh_desc_enc;
+ init_sh_desc(desc, HDR_SHARE_WAIT);
+ /* Skip if already shared */
+ key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_SHRD);
+
+ /* Load class1 key only */
+ append_key_as_imm(desc, (void *)ctx->key, ctx->enckeylen,
+ ctx->enckeylen, CLASS_1 |
+ KEY_DEST_CLASS_REG);
+
+ set_jump_tgt_here(desc, key_jump_cmd);
+
+ /* Propagate errors from shared to job descriptor */
+ append_cmd(desc, SET_OK_PROP_ERRORS | CMD_LOAD);
+
+ /* Load iv */
+ append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT |
+ LDST_CLASS_1_CCB | tfm->ivsize);
+
+ /* Load operation */
+ append_operation(desc, ctx->class1_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* Perform operation */
+ ablkcipher_append_src_dst(desc);
+
+ ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ablkcipher enc shdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+ /* ablkcipher_decrypt shared descriptor */
+ desc = ctx->sh_desc_dec;
+
+ init_sh_desc(desc, HDR_SHARE_WAIT);
+ /* Skip if already shared */
+ key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_SHRD);
+
+ /* Load class1 key only */
+ append_key_as_imm(desc, (void *)ctx->key, ctx->enckeylen,
+ ctx->enckeylen, CLASS_1 |
+ KEY_DEST_CLASS_REG);
+
+ /* For aead, only propagate error immediately if shared */
+ jump_cmd = append_jump(desc, JUMP_TEST_ALL);
+ set_jump_tgt_here(desc, key_jump_cmd);
+ append_cmd(desc, SET_OK_PROP_ERRORS | CMD_LOAD);
+ set_jump_tgt_here(desc, jump_cmd);
+
+ /* load IV */
+ append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT |
+ LDST_CLASS_1_CCB | tfm->ivsize);
+
+ /* Choose operation */
+ append_dec_op1(desc, ctx->class1_alg_type);
+
+ /* Perform operation */
+ ablkcipher_append_src_dst(desc);
+
+ /* Wait for key to load before allowing propagating error */
+ append_dec_shr_done(desc);
+
+ ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ablkcipher dec shdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+
+ return ret;
+}
+
struct link_tbl_entry {
u64 ptr;
u32 len;
@@ -379,64 +766,109 @@ struct link_tbl_entry {
};
/*
- * ipsec_esp_edesc - s/w-extended ipsec_esp descriptor
+ * aead_edesc - s/w-extended aead descriptor
+ * @assoc_nents: number of segments in associated data (SPI+Seq) scatterlist
* @src_nents: number of segments in input scatterlist
* @dst_nents: number of segments in output scatterlist
- * @assoc_nents: number of segments in associated data (SPI+Seq) scatterlist
+ * @iv_dma: dma address of iv for checking continuity and link table
* @desc: h/w descriptor (variable length; must not exceed MAX_CAAM_DESCSIZE)
* @link_tbl_bytes: length of dma mapped link_tbl space
* @link_tbl_dma: bus physical mapped address of h/w link table
* @hw_desc: the h/w job descriptor followed by any referenced link tables
*/
-struct ipsec_esp_edesc {
+struct aead_edesc {
int assoc_nents;
int src_nents;
int dst_nents;
+ dma_addr_t iv_dma;
int link_tbl_bytes;
dma_addr_t link_tbl_dma;
struct link_tbl_entry *link_tbl;
u32 hw_desc[0];
};
-static void ipsec_esp_unmap(struct device *dev,
- struct ipsec_esp_edesc *edesc,
- struct aead_request *areq)
-{
- dma_unmap_sg(dev, areq->assoc, edesc->assoc_nents, DMA_TO_DEVICE);
+/*
+ * ablkcipher_edesc - s/w-extended ablkcipher descriptor
+ * @src_nents: number of segments in input scatterlist
+ * @dst_nents: number of segments in output scatterlist
+ * @iv_dma: dma address of iv for checking continuity and link table
+ * @desc: h/w descriptor (variable length; must not exceed MAX_CAAM_DESCSIZE)
+ * @link_tbl_bytes: length of dma mapped link_tbl space
+ * @link_tbl_dma: bus physical mapped address of h/w link table
+ * @hw_desc: the h/w job descriptor followed by any referenced link tables
+ */
+struct ablkcipher_edesc {
+ int src_nents;
+ int dst_nents;
+ dma_addr_t iv_dma;
+ int link_tbl_bytes;
+ dma_addr_t link_tbl_dma;
+ struct link_tbl_entry *link_tbl;
+ u32 hw_desc[0];
+};
- if (unlikely(areq->dst != areq->src)) {
- dma_unmap_sg(dev, areq->src, edesc->src_nents,
- DMA_TO_DEVICE);
- dma_unmap_sg(dev, areq->dst, edesc->dst_nents,
- DMA_FROM_DEVICE);
+static void caam_unmap(struct device *dev, struct scatterlist *src,
+ struct scatterlist *dst, int src_nents, int dst_nents,
+ dma_addr_t iv_dma, int ivsize, dma_addr_t link_tbl_dma,
+ int link_tbl_bytes)
+{
+ if (unlikely(dst != src)) {
+ dma_unmap_sg(dev, src, src_nents, DMA_TO_DEVICE);
+ dma_unmap_sg(dev, dst, dst_nents, DMA_FROM_DEVICE);
} else {
- dma_unmap_sg(dev, areq->src, edesc->src_nents,
- DMA_BIDIRECTIONAL);
+ dma_unmap_sg(dev, src, src_nents, DMA_BIDIRECTIONAL);
}
- if (edesc->link_tbl_bytes)
- dma_unmap_single(dev, edesc->link_tbl_dma,
- edesc->link_tbl_bytes,
+ if (iv_dma)
+ dma_unmap_single(dev, iv_dma, ivsize, DMA_TO_DEVICE);
+ if (link_tbl_bytes)
+ dma_unmap_single(dev, link_tbl_dma, link_tbl_bytes,
DMA_TO_DEVICE);
}
-/*
- * ipsec_esp descriptor callbacks
- */
-static void ipsec_esp_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
+static void aead_unmap(struct device *dev,
+ struct aead_edesc *edesc,
+ struct aead_request *req)
+{
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ int ivsize = crypto_aead_ivsize(aead);
+
+ dma_unmap_sg(dev, req->assoc, edesc->assoc_nents, DMA_TO_DEVICE);
+
+ caam_unmap(dev, req->src, req->dst,
+ edesc->src_nents, edesc->dst_nents,
+ edesc->iv_dma, ivsize, edesc->link_tbl_dma,
+ edesc->link_tbl_bytes);
+}
+
+static void ablkcipher_unmap(struct device *dev,
+ struct ablkcipher_edesc *edesc,
+ struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+
+ caam_unmap(dev, req->src, req->dst,
+ edesc->src_nents, edesc->dst_nents,
+ edesc->iv_dma, ivsize, edesc->link_tbl_dma,
+ edesc->link_tbl_bytes);
+}
+
+static void aead_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
void *context)
{
- struct aead_request *areq = context;
- struct ipsec_esp_edesc *edesc;
+ struct aead_request *req = context;
+ struct aead_edesc *edesc;
#ifdef DEBUG
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
- int ivsize = crypto_aead_ivsize(aead);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ int ivsize = crypto_aead_ivsize(aead);
dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
#endif
- edesc = (struct ipsec_esp_edesc *)((char *)desc -
- offsetof(struct ipsec_esp_edesc, hw_desc));
+
+ edesc = (struct aead_edesc *)((char *)desc -
+ offsetof(struct aead_edesc, hw_desc));
if (err) {
char tmp[CAAM_ERROR_STR_MAX];
@@ -444,39 +876,50 @@ static void ipsec_esp_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err));
}
- ipsec_esp_unmap(jrdev, edesc, areq);
+ aead_unmap(jrdev, edesc, req);
#ifdef DEBUG
print_hex_dump(KERN_ERR, "assoc @"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->assoc),
- areq->assoclen , 1);
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->assoc),
+ req->assoclen , 1);
print_hex_dump(KERN_ERR, "dstiv @"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->src) - ivsize,
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src) - ivsize,
edesc->src_nents ? 100 : ivsize, 1);
print_hex_dump(KERN_ERR, "dst @"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->src),
- edesc->src_nents ? 100 : areq->cryptlen +
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->src_nents ? 100 : req->cryptlen +
ctx->authsize + 4, 1);
#endif
kfree(edesc);
- aead_request_complete(areq, err);
+ aead_request_complete(req, err);
}
-static void ipsec_esp_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
+static void aead_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
void *context)
{
- struct aead_request *areq = context;
- struct ipsec_esp_edesc *edesc;
+ struct aead_request *req = context;
+ struct aead_edesc *edesc;
#ifdef DEBUG
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ int ivsize = crypto_aead_ivsize(aead);
dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
#endif
- edesc = (struct ipsec_esp_edesc *)((char *)desc -
- offsetof(struct ipsec_esp_edesc, hw_desc));
+
+ edesc = (struct aead_edesc *)((char *)desc -
+ offsetof(struct aead_edesc, hw_desc));
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "dstiv @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, req->iv,
+ ivsize, 1);
+ print_hex_dump(KERN_ERR, "dst @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->dst),
+ req->cryptlen, 1);
+#endif
if (err) {
char tmp[CAAM_ERROR_STR_MAX];
@@ -484,7 +927,7 @@ static void ipsec_esp_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err));
}
- ipsec_esp_unmap(jrdev, edesc, areq);
+ aead_unmap(jrdev, edesc, req);
/*
* verify hw auth check passed else return -EBADMSG
@@ -495,255 +938,413 @@ static void ipsec_esp_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
#ifdef DEBUG
print_hex_dump(KERN_ERR, "iphdrout@"xstr(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4,
- ((char *)sg_virt(areq->assoc) - sizeof(struct iphdr)),
- sizeof(struct iphdr) + areq->assoclen +
- ((areq->cryptlen > 1500) ? 1500 : areq->cryptlen) +
+ ((char *)sg_virt(req->assoc) - sizeof(struct iphdr)),
+ sizeof(struct iphdr) + req->assoclen +
+ ((req->cryptlen > 1500) ? 1500 : req->cryptlen) +
ctx->authsize + 36, 1);
if (!err && edesc->link_tbl_bytes) {
- struct scatterlist *sg = sg_last(areq->src, edesc->src_nents);
+ struct scatterlist *sg = sg_last(req->src, edesc->src_nents);
print_hex_dump(KERN_ERR, "sglastout@"xstr(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(sg),
sg->length + ctx->authsize + 16, 1);
}
#endif
+
kfree(edesc);
- aead_request_complete(areq, err);
+ aead_request_complete(req, err);
+}
+
+static void ablkcipher_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
+ void *context)
+{
+ struct ablkcipher_request *req = context;
+ struct ablkcipher_edesc *edesc;
+#ifdef DEBUG
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+
+ dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+#endif
+
+ edesc = (struct ablkcipher_edesc *)((char *)desc -
+ offsetof(struct ablkcipher_edesc, hw_desc));
+
+ if (err) {
+ char tmp[CAAM_ERROR_STR_MAX];
+
+ dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err));
+ }
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "dstiv @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, req->info,
+ edesc->src_nents > 1 ? 100 : ivsize, 1);
+ print_hex_dump(KERN_ERR, "dst @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->dst_nents > 1 ? 100 : req->nbytes, 1);
+#endif
+
+ ablkcipher_unmap(jrdev, edesc, req);
+ kfree(edesc);
+
+ ablkcipher_request_complete(req, err);
+}
+
+static void ablkcipher_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
+ void *context)
+{
+ struct ablkcipher_request *req = context;
+ struct ablkcipher_edesc *edesc;
+#ifdef DEBUG
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+
+ dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+#endif
+
+ edesc = (struct ablkcipher_edesc *)((char *)desc -
+ offsetof(struct ablkcipher_edesc, hw_desc));
+ if (err) {
+ char tmp[CAAM_ERROR_STR_MAX];
+
+ dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err));
+ }
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "dstiv @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, req->info,
+ ivsize, 1);
+ print_hex_dump(KERN_ERR, "dst @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->dst_nents > 1 ? 100 : req->nbytes, 1);
+#endif
+
+ ablkcipher_unmap(jrdev, edesc, req);
+ kfree(edesc);
+
+ ablkcipher_request_complete(req, err);
+}
+
+static void sg_to_link_tbl_one(struct link_tbl_entry *link_tbl_ptr,
+ dma_addr_t dma, u32 len, u32 offset)
+{
+ link_tbl_ptr->ptr = dma;
+ link_tbl_ptr->len = len;
+ link_tbl_ptr->reserved = 0;
+ link_tbl_ptr->buf_pool_id = 0;
+ link_tbl_ptr->offset = offset;
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "link_tbl_ptr@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, link_tbl_ptr,
+ sizeof(struct link_tbl_entry), 1);
+#endif
}
/*
* convert scatterlist to h/w link table format
- * scatterlist must have been previously dma mapped
+ * but does not have final bit; instead, returns last entry
*/
-static void sg_to_link_tbl(struct scatterlist *sg, int sg_count,
- struct link_tbl_entry *link_tbl_ptr, u32 offset)
+static struct link_tbl_entry *sg_to_link_tbl(struct scatterlist *sg,
+ int sg_count, struct link_tbl_entry
+ *link_tbl_ptr, u32 offset)
{
while (sg_count) {
- link_tbl_ptr->ptr = sg_dma_address(sg);
- link_tbl_ptr->len = sg_dma_len(sg);
- link_tbl_ptr->reserved = 0;
- link_tbl_ptr->buf_pool_id = 0;
- link_tbl_ptr->offset = offset;
+ sg_to_link_tbl_one(link_tbl_ptr, sg_dma_address(sg),
+ sg_dma_len(sg), offset);
link_tbl_ptr++;
sg = sg_next(sg);
sg_count--;
}
+ return link_tbl_ptr - 1;
+}
- /* set Final bit (marks end of link table) */
- link_tbl_ptr--;
+/*
+ * convert scatterlist to h/w link table format
+ * scatterlist must have been previously dma mapped
+ */
+static void sg_to_link_tbl_last(struct scatterlist *sg, int sg_count,
+ struct link_tbl_entry *link_tbl_ptr, u32 offset)
+{
+ link_tbl_ptr = sg_to_link_tbl(sg, sg_count, link_tbl_ptr, offset);
link_tbl_ptr->len |= 0x40000000;
}
/*
- * fill in and submit ipsec_esp job descriptor
+ * Fill in aead job descriptor
*/
-static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
- u32 encrypt,
- void (*callback) (struct device *dev, u32 *desc,
- u32 err, void *context))
+static void init_aead_job(u32 *sh_desc, dma_addr_t ptr,
+ struct aead_edesc *edesc,
+ struct aead_request *req,
+ bool all_contig, bool encrypt)
{
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
- struct device *jrdev = ctx->jrdev;
- u32 *desc = edesc->hw_desc, options;
- int ret, sg_count, assoc_sg_count;
int ivsize = crypto_aead_ivsize(aead);
int authsize = ctx->authsize;
- dma_addr_t ptr, dst_dma, src_dma;
-#ifdef DEBUG
- u32 *sh_desc = ctx->sh_desc;
+ u32 *desc = edesc->hw_desc;
+ u32 out_options = 0, in_options;
+ dma_addr_t dst_dma, src_dma;
+ int len, link_tbl_index = 0;
+#ifdef DEBUG
debug("assoclen %d cryptlen %d authsize %d\n",
- areq->assoclen, areq->cryptlen, authsize);
+ req->assoclen, req->cryptlen, authsize);
print_hex_dump(KERN_ERR, "assoc @"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->assoc),
- areq->assoclen , 1);
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->assoc),
+ req->assoclen , 1);
print_hex_dump(KERN_ERR, "presciv@"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->src) - ivsize,
+ DUMP_PREFIX_ADDRESS, 16, 4, req->iv,
edesc->src_nents ? 100 : ivsize, 1);
print_hex_dump(KERN_ERR, "src @"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->src),
- edesc->src_nents ? 100 : areq->cryptlen + authsize, 1);
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->src_nents ? 100 : req->cryptlen, 1);
print_hex_dump(KERN_ERR, "shrdesc@"xstr(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, sh_desc,
desc_bytes(sh_desc), 1);
#endif
- assoc_sg_count = dma_map_sg(jrdev, areq->assoc, edesc->assoc_nents ?: 1,
- DMA_TO_DEVICE);
- if (areq->src == areq->dst)
- sg_count = dma_map_sg(jrdev, areq->src, edesc->src_nents ? : 1,
- DMA_BIDIRECTIONAL);
- else
- sg_count = dma_map_sg(jrdev, areq->src, edesc->src_nents ? : 1,
- DMA_TO_DEVICE);
- /* start auth operation */
- append_operation(desc, ctx->class2_alg_type | OP_ALG_AS_INITFINAL |
- (encrypt ? : OP_ALG_ICV_ON));
+ len = desc_len(sh_desc);
+ init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE);
- /* Load FIFO with data for Class 2 CHA */
- options = FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG;
- if (!edesc->assoc_nents) {
- ptr = sg_dma_address(areq->assoc);
+ if (all_contig) {
+ src_dma = sg_dma_address(req->assoc);
+ in_options = 0;
} else {
- sg_to_link_tbl(areq->assoc, edesc->assoc_nents,
- edesc->link_tbl, 0);
- ptr = edesc->link_tbl_dma;
- options |= LDST_SGF;
+ src_dma = edesc->link_tbl_dma;
+ link_tbl_index += (edesc->assoc_nents ? : 1) + 1 +
+ (edesc->src_nents ? : 1);
+ in_options = LDST_SGF;
}
- append_fifo_load(desc, ptr, areq->assoclen, options);
-
- /* copy iv from cipher/class1 input context to class2 infifo */
- append_move(desc, MOVE_SRC_CLASS1CTX | MOVE_DEST_CLASS2INFIFO | ivsize);
-
- if (!encrypt) {
- u32 *jump_cmd, *uncond_jump_cmd;
-
- /* JUMP if shared */
- jump_cmd = append_jump(desc, JUMP_TEST_ALL | JUMP_COND_SHRD);
+ if (encrypt)
+ append_seq_in_ptr(desc, src_dma, req->assoclen + ivsize +
+ req->cryptlen - authsize, in_options);
+ else
+ append_seq_in_ptr(desc, src_dma, req->assoclen + ivsize +
+ req->cryptlen, in_options);
- /* start class 1 (cipher) operation, non-shared version */
- append_operation(desc, ctx->class1_alg_type |
- OP_ALG_AS_INITFINAL);
+ if (likely(req->src == req->dst)) {
+ if (all_contig) {
+ dst_dma = sg_dma_address(req->src);
+ } else {
+ dst_dma = src_dma + sizeof(struct link_tbl_entry) *
+ ((edesc->assoc_nents ? : 1) + 1);
+ out_options = LDST_SGF;
+ }
+ } else {
+ if (!edesc->dst_nents) {
+ dst_dma = sg_dma_address(req->dst);
+ } else {
+ dst_dma = edesc->link_tbl_dma +
+ link_tbl_index *
+ sizeof(struct link_tbl_entry);
+ out_options = LDST_SGF;
+ }
+ }
+ if (encrypt)
+ append_seq_out_ptr(desc, dst_dma, req->cryptlen, out_options);
+ else
+ append_seq_out_ptr(desc, dst_dma, req->cryptlen - authsize,
+ out_options);
+}
- uncond_jump_cmd = append_jump(desc, 0);
+/*
+ * Fill in aead givencrypt job descriptor
+ */
+static void init_aead_giv_job(u32 *sh_desc, dma_addr_t ptr,
+ struct aead_edesc *edesc,
+ struct aead_request *req,
+ int contig)
+{
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ int ivsize = crypto_aead_ivsize(aead);
+ int authsize = ctx->authsize;
+ u32 *desc = edesc->hw_desc;
+ u32 out_options = 0, in_options;
+ dma_addr_t dst_dma, src_dma;
+ int len, link_tbl_index = 0;
- set_jump_tgt_here(desc, jump_cmd);
+#ifdef DEBUG
+ debug("assoclen %d cryptlen %d authsize %d\n",
+ req->assoclen, req->cryptlen, authsize);
+ print_hex_dump(KERN_ERR, "assoc @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->assoc),
+ req->assoclen , 1);
+ print_hex_dump(KERN_ERR, "presciv@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, req->iv, ivsize, 1);
+ print_hex_dump(KERN_ERR, "src @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->src_nents > 1 ? 100 : req->cryptlen, 1);
+ print_hex_dump(KERN_ERR, "shrdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sh_desc,
+ desc_bytes(sh_desc), 1);
+#endif
- /* start class 1 (cipher) operation, shared version */
- append_operation(desc, ctx->class1_alg_type |
- OP_ALG_AS_INITFINAL | OP_ALG_AAI_DK);
- set_jump_tgt_here(desc, uncond_jump_cmd);
- } else
- append_operation(desc, ctx->class1_alg_type |
- OP_ALG_AS_INITFINAL | encrypt);
+ len = desc_len(sh_desc);
+ init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE);
- /* load payload & instruct to class2 to snoop class 1 if encrypting */
- options = 0;
- if (!edesc->src_nents) {
- src_dma = sg_dma_address(areq->src);
+ if (contig & GIV_SRC_CONTIG) {
+ src_dma = sg_dma_address(req->assoc);
+ in_options = 0;
} else {
- sg_to_link_tbl(areq->src, edesc->src_nents, edesc->link_tbl +
- edesc->assoc_nents, 0);
- src_dma = edesc->link_tbl_dma + edesc->assoc_nents *
- sizeof(struct link_tbl_entry);
- options |= LDST_SGF;
+ src_dma = edesc->link_tbl_dma;
+ link_tbl_index += edesc->assoc_nents + 1 + edesc->src_nents;
+ in_options = LDST_SGF;
}
- append_seq_in_ptr(desc, src_dma, areq->cryptlen + authsize, options);
- append_seq_fifo_load(desc, areq->cryptlen, FIFOLD_CLASS_BOTH |
- FIFOLD_TYPE_LASTBOTH |
- (encrypt ? FIFOLD_TYPE_MSG1OUT2
- : FIFOLD_TYPE_MSG));
-
- /* specify destination */
- if (areq->src == areq->dst) {
- dst_dma = src_dma;
+ append_seq_in_ptr(desc, src_dma, req->assoclen + ivsize +
+ req->cryptlen - authsize, in_options);
+
+ if (contig & GIV_DST_CONTIG) {
+ dst_dma = edesc->iv_dma;
} else {
- sg_count = dma_map_sg(jrdev, areq->dst, edesc->dst_nents ? : 1,
- DMA_FROM_DEVICE);
- if (!edesc->dst_nents) {
- dst_dma = sg_dma_address(areq->dst);
- options = 0;
+ if (likely(req->src == req->dst)) {
+ dst_dma = src_dma + sizeof(struct link_tbl_entry) *
+ edesc->assoc_nents;
+ out_options = LDST_SGF;
} else {
- sg_to_link_tbl(areq->dst, edesc->dst_nents,
- edesc->link_tbl + edesc->assoc_nents +
- edesc->src_nents, 0);
- dst_dma = edesc->link_tbl_dma + (edesc->assoc_nents +
- edesc->src_nents) *
+ dst_dma = edesc->link_tbl_dma +
+ link_tbl_index *
sizeof(struct link_tbl_entry);
- options = LDST_SGF;
+ out_options = LDST_SGF;
}
}
- append_seq_out_ptr(desc, dst_dma, areq->cryptlen + authsize, options);
- append_seq_fifo_store(desc, areq->cryptlen, FIFOST_TYPE_MESSAGE_DATA);
- /* ICV */
- if (encrypt)
- append_seq_store(desc, authsize, LDST_CLASS_2_CCB |
- LDST_SRCDST_BYTE_CONTEXT);
- else
- append_seq_fifo_load(desc, authsize, FIFOLD_CLASS_CLASS2 |
- FIFOLD_TYPE_LAST2 | FIFOLD_TYPE_ICV);
+ append_seq_out_ptr(desc, dst_dma, ivsize + req->cryptlen, out_options);
+}
+
+/*
+ * Fill in ablkcipher job descriptor
+ */
+static void init_ablkcipher_job(u32 *sh_desc, dma_addr_t ptr,
+ struct ablkcipher_edesc *edesc,
+ struct ablkcipher_request *req,
+ bool iv_contig)
+{
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+ u32 *desc = edesc->hw_desc;
+ u32 out_options = 0, in_options;
+ dma_addr_t dst_dma, src_dma;
+ int len, link_tbl_index = 0;
#ifdef DEBUG
- debug("job_desc_len %d\n", desc_len(desc));
- print_hex_dump(KERN_ERR, "jobdesc@"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc) , 1);
- print_hex_dump(KERN_ERR, "jdlinkt@"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, edesc->link_tbl,
- edesc->link_tbl_bytes, 1);
+ print_hex_dump(KERN_ERR, "presciv@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, req->info,
+ ivsize, 1);
+ print_hex_dump(KERN_ERR, "src @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->src_nents ? 100 : req->nbytes, 1);
#endif
- ret = caam_jr_enqueue(jrdev, desc, callback, areq);
- if (!ret)
- ret = -EINPROGRESS;
- else {
- ipsec_esp_unmap(jrdev, edesc, areq);
- kfree(edesc);
+ len = desc_len(sh_desc);
+ init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE);
+
+ if (iv_contig) {
+ src_dma = edesc->iv_dma;
+ in_options = 0;
+ } else {
+ src_dma = edesc->link_tbl_dma;
+ link_tbl_index += (iv_contig ? 0 : 1) + edesc->src_nents;
+ in_options = LDST_SGF;
}
+ append_seq_in_ptr(desc, src_dma, req->nbytes + ivsize, in_options);
- return ret;
+ if (likely(req->src == req->dst)) {
+ if (!edesc->src_nents && iv_contig) {
+ dst_dma = sg_dma_address(req->src);
+ } else {
+ dst_dma = edesc->link_tbl_dma +
+ sizeof(struct link_tbl_entry);
+ out_options = LDST_SGF;
+ }
+ } else {
+ if (!edesc->dst_nents) {
+ dst_dma = sg_dma_address(req->dst);
+ } else {
+ dst_dma = edesc->link_tbl_dma +
+ link_tbl_index * sizeof(struct link_tbl_entry);
+ out_options = LDST_SGF;
+ }
+ }
+ append_seq_out_ptr(desc, dst_dma, req->nbytes, out_options);
}
/*
* derive number of elements in scatterlist
*/
-static int sg_count(struct scatterlist *sg_list, int nbytes, int *chained)
+static int sg_count(struct scatterlist *sg_list, int nbytes)
{
struct scatterlist *sg = sg_list;
int sg_nents = 0;
- *chained = 0;
while (nbytes > 0) {
sg_nents++;
nbytes -= sg->length;
if (!sg_is_last(sg) && (sg + 1)->length == 0)
- *chained = 1;
+ BUG(); /* Not support chaining */
sg = scatterwalk_sg_next(sg);
}
+ if (likely(sg_nents == 1))
+ return 0;
+
return sg_nents;
}
/*
- * allocate and map the ipsec_esp extended descriptor
+ * allocate and map the aead extended descriptor
*/
-static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
- int desc_bytes)
+static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
+ int desc_bytes, bool *all_contig_ptr)
{
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
- gfp_t flags = areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
- GFP_ATOMIC;
- int assoc_nents, src_nents, dst_nents = 0, chained, link_tbl_bytes;
- struct ipsec_esp_edesc *edesc;
-
- assoc_nents = sg_count(areq->assoc, areq->assoclen, &chained);
- BUG_ON(chained);
- if (likely(assoc_nents == 1))
- assoc_nents = 0;
-
- src_nents = sg_count(areq->src, areq->cryptlen + ctx->authsize,
- &chained);
- BUG_ON(chained);
- if (src_nents == 1)
- src_nents = 0;
-
- if (unlikely(areq->dst != areq->src)) {
- dst_nents = sg_count(areq->dst, areq->cryptlen + ctx->authsize,
- &chained);
- BUG_ON(chained);
- if (dst_nents == 1)
- dst_nents = 0;
+ gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ int assoc_nents, src_nents, dst_nents = 0;
+ struct aead_edesc *edesc;
+ dma_addr_t iv_dma = 0;
+ int sgc;
+ bool all_contig = true;
+ int ivsize = crypto_aead_ivsize(aead);
+ int link_tbl_index, link_tbl_len = 0, link_tbl_bytes;
+
+ assoc_nents = sg_count(req->assoc, req->assoclen);
+ src_nents = sg_count(req->src, req->cryptlen);
+
+ if (unlikely(req->dst != req->src))
+ dst_nents = sg_count(req->dst, req->cryptlen);
+
+ sgc = dma_map_sg(jrdev, req->assoc, assoc_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ if (likely(req->src == req->dst)) {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ } else {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_TO_DEVICE);
+ sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1,
+ DMA_FROM_DEVICE);
}
- link_tbl_bytes = (assoc_nents + src_nents + dst_nents) *
- sizeof(struct link_tbl_entry);
- debug("link_tbl_bytes %d\n", link_tbl_bytes);
+ /* Check if data are contiguous */
+ iv_dma = dma_map_single(jrdev, req->iv, ivsize, DMA_TO_DEVICE);
+ if (assoc_nents || sg_dma_address(req->assoc) + req->assoclen !=
+ iv_dma || src_nents || iv_dma + ivsize !=
+ sg_dma_address(req->src)) {
+ all_contig = false;
+ assoc_nents = assoc_nents ? : 1;
+ src_nents = src_nents ? : 1;
+ link_tbl_len = assoc_nents + 1 + src_nents;
+ }
+ link_tbl_len += dst_nents;
+
+ link_tbl_bytes = link_tbl_len * sizeof(struct link_tbl_entry);
/* allocate space for base edesc and hw desc commands, link tables */
- edesc = kmalloc(sizeof(struct ipsec_esp_edesc) + desc_bytes +
+ edesc = kmalloc(sizeof(struct aead_edesc) + desc_bytes +
link_tbl_bytes, GFP_DMA | flags);
if (!edesc) {
dev_err(jrdev, "could not allocate extended descriptor\n");
@@ -753,142 +1354,450 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
edesc->assoc_nents = assoc_nents;
edesc->src_nents = src_nents;
edesc->dst_nents = dst_nents;
- edesc->link_tbl = (void *)edesc + sizeof(struct ipsec_esp_edesc) +
+ edesc->iv_dma = iv_dma;
+ edesc->link_tbl_bytes = link_tbl_bytes;
+ edesc->link_tbl = (void *)edesc + sizeof(struct aead_edesc) +
desc_bytes;
edesc->link_tbl_dma = dma_map_single(jrdev, edesc->link_tbl,
link_tbl_bytes, DMA_TO_DEVICE);
- edesc->link_tbl_bytes = link_tbl_bytes;
+ *all_contig_ptr = all_contig;
+
+ link_tbl_index = 0;
+ if (!all_contig) {
+ sg_to_link_tbl(req->assoc,
+ (assoc_nents ? : 1),
+ edesc->link_tbl +
+ link_tbl_index, 0);
+ link_tbl_index += assoc_nents ? : 1;
+ sg_to_link_tbl_one(edesc->link_tbl + link_tbl_index,
+ iv_dma, ivsize, 0);
+ link_tbl_index += 1;
+ sg_to_link_tbl_last(req->src,
+ (src_nents ? : 1),
+ edesc->link_tbl +
+ link_tbl_index, 0);
+ link_tbl_index += src_nents ? : 1;
+ }
+ if (dst_nents) {
+ sg_to_link_tbl_last(req->dst, dst_nents,
+ edesc->link_tbl + link_tbl_index, 0);
+ }
return edesc;
}
-static int aead_authenc_encrypt(struct aead_request *areq)
+static int aead_encrypt(struct aead_request *req)
{
- struct ipsec_esp_edesc *edesc;
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+ struct aead_edesc *edesc;
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
- int ivsize = crypto_aead_ivsize(aead);
+ bool all_contig;
u32 *desc;
- dma_addr_t iv_dma;
+ int ret = 0;
+
+ req->cryptlen += ctx->authsize;
/* allocate extended descriptor */
- edesc = ipsec_esp_edesc_alloc(areq, DESC_AEAD_ENCRYPT_TEXT_LEN *
- CAAM_CMD_SZ);
+ edesc = aead_edesc_alloc(req, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &all_contig);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
- desc = edesc->hw_desc;
-
- /* insert shared descriptor pointer */
- init_job_desc_shared(desc, ctx->shared_desc_phys,
- desc_len(ctx->sh_desc), HDR_SHARE_DEFER);
-
- iv_dma = dma_map_single(jrdev, areq->iv, ivsize, DMA_TO_DEVICE);
- /* check dma error */
+ /* Create and submit job descriptor */
+ init_aead_job(ctx->sh_desc_enc, ctx->sh_desc_enc_dma, edesc, req,
+ all_contig, true);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead jobdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
- append_load(desc, iv_dma, ivsize,
- LDST_CLASS_1_CCB | LDST_SRCDST_BYTE_CONTEXT);
+ desc = edesc->hw_desc;
+ ret = caam_jr_enqueue(jrdev, desc, aead_encrypt_done, req);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ aead_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
- return ipsec_esp(edesc, areq, OP_ALG_ENCRYPT, ipsec_esp_encrypt_done);
+ return ret;
}
-static int aead_authenc_decrypt(struct aead_request *req)
+static int aead_decrypt(struct aead_request *req)
{
+ struct aead_edesc *edesc;
struct crypto_aead *aead = crypto_aead_reqtfm(req);
- int ivsize = crypto_aead_ivsize(aead);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
- struct ipsec_esp_edesc *edesc;
+ bool all_contig;
u32 *desc;
- dma_addr_t iv_dma;
-
- req->cryptlen -= ctx->authsize;
+ int ret = 0;
/* allocate extended descriptor */
- edesc = ipsec_esp_edesc_alloc(req, DESC_AEAD_DECRYPT_TEXT_LEN *
- CAAM_CMD_SZ);
+ edesc = aead_edesc_alloc(req, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &all_contig);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "dec src@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ req->cryptlen, 1);
+#endif
+
+ /* Create and submit job descriptor*/
+ init_aead_job(ctx->sh_desc_dec,
+ ctx->sh_desc_dec_dma, edesc, req, all_contig, false);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead jobdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
+
desc = edesc->hw_desc;
+ ret = caam_jr_enqueue(jrdev, desc, aead_decrypt_done, req);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ aead_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
- /* insert shared descriptor pointer */
- init_job_desc_shared(desc, ctx->shared_desc_phys,
- desc_len(ctx->sh_desc), HDR_SHARE_DEFER);
+ return ret;
+}
- iv_dma = dma_map_single(jrdev, req->iv, ivsize, DMA_TO_DEVICE);
- /* check dma error */
+/*
+ * allocate and map the aead extended descriptor for aead givencrypt
+ */
+static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request
+ *greq, int desc_bytes,
+ u32 *contig_ptr)
+{
+ struct aead_request *req = &greq->areq;
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ int assoc_nents, src_nents, dst_nents = 0;
+ struct aead_edesc *edesc;
+ dma_addr_t iv_dma = 0;
+ int sgc;
+ u32 contig = GIV_SRC_CONTIG | GIV_DST_CONTIG;
+ int ivsize = crypto_aead_ivsize(aead);
+ int link_tbl_index, link_tbl_len = 0, link_tbl_bytes;
+
+ assoc_nents = sg_count(req->assoc, req->assoclen);
+ src_nents = sg_count(req->src, req->cryptlen);
- append_load(desc, iv_dma, ivsize,
- LDST_CLASS_1_CCB | LDST_SRCDST_BYTE_CONTEXT);
+ if (unlikely(req->dst != req->src))
+ dst_nents = sg_count(req->dst, req->cryptlen);
- return ipsec_esp(edesc, req, !OP_ALG_ENCRYPT, ipsec_esp_decrypt_done);
+ sgc = dma_map_sg(jrdev, req->assoc, assoc_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ if (likely(req->src == req->dst)) {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ } else {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_TO_DEVICE);
+ sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1,
+ DMA_FROM_DEVICE);
+ }
+
+ /* Check if data are contiguous */
+ iv_dma = dma_map_single(jrdev, greq->giv, ivsize, DMA_TO_DEVICE);
+ if (assoc_nents || sg_dma_address(req->assoc) + req->assoclen !=
+ iv_dma || src_nents || iv_dma + ivsize != sg_dma_address(req->src))
+ contig &= ~GIV_SRC_CONTIG;
+ if (dst_nents || iv_dma + ivsize != sg_dma_address(req->dst))
+ contig &= ~GIV_DST_CONTIG;
+ if (unlikely(req->src != req->dst)) {
+ dst_nents = dst_nents ? : 1;
+ link_tbl_len += 1;
+ }
+ if (!(contig & GIV_SRC_CONTIG)) {
+ assoc_nents = assoc_nents ? : 1;
+ src_nents = src_nents ? : 1;
+ link_tbl_len += assoc_nents + 1 + src_nents;
+ if (likely(req->src == req->dst))
+ contig &= ~GIV_DST_CONTIG;
+ }
+ link_tbl_len += dst_nents;
+
+ link_tbl_bytes = link_tbl_len * sizeof(struct link_tbl_entry);
+
+ /* allocate space for base edesc and hw desc commands, link tables */
+ edesc = kmalloc(sizeof(struct aead_edesc) + desc_bytes +
+ link_tbl_bytes, GFP_DMA | flags);
+ if (!edesc) {
+ dev_err(jrdev, "could not allocate extended descriptor\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ edesc->assoc_nents = assoc_nents;
+ edesc->src_nents = src_nents;
+ edesc->dst_nents = dst_nents;
+ edesc->iv_dma = iv_dma;
+ edesc->link_tbl_bytes = link_tbl_bytes;
+ edesc->link_tbl = (void *)edesc + sizeof(struct aead_edesc) +
+ desc_bytes;
+ edesc->link_tbl_dma = dma_map_single(jrdev, edesc->link_tbl,
+ link_tbl_bytes, DMA_TO_DEVICE);
+ *contig_ptr = contig;
+
+ link_tbl_index = 0;
+ if (!(contig & GIV_SRC_CONTIG)) {
+ sg_to_link_tbl(req->assoc, assoc_nents,
+ edesc->link_tbl +
+ link_tbl_index, 0);
+ link_tbl_index += assoc_nents;
+ sg_to_link_tbl_one(edesc->link_tbl + link_tbl_index,
+ iv_dma, ivsize, 0);
+ link_tbl_index += 1;
+ sg_to_link_tbl_last(req->src, src_nents,
+ edesc->link_tbl +
+ link_tbl_index, 0);
+ link_tbl_index += src_nents;
+ }
+ if (unlikely(req->src != req->dst && !(contig & GIV_DST_CONTIG))) {
+ sg_to_link_tbl_one(edesc->link_tbl + link_tbl_index,
+ iv_dma, ivsize, 0);
+ link_tbl_index += 1;
+ sg_to_link_tbl_last(req->dst, dst_nents,
+ edesc->link_tbl + link_tbl_index, 0);
+ }
+
+ return edesc;
}
-static int aead_authenc_givencrypt(struct aead_givcrypt_request *req)
+static int aead_givencrypt(struct aead_givcrypt_request *areq)
{
- struct aead_request *areq = &req->areq;
- struct ipsec_esp_edesc *edesc;
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+ struct aead_request *req = &areq->areq;
+ struct aead_edesc *edesc;
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
- int ivsize = crypto_aead_ivsize(aead);
- dma_addr_t iv_dma;
+ u32 contig;
u32 *desc;
+ int ret = 0;
- iv_dma = dma_map_single(jrdev, req->giv, ivsize, DMA_FROM_DEVICE);
-
- debug("%s: giv %p\n", __func__, req->giv);
+ req->cryptlen += ctx->authsize;
/* allocate extended descriptor */
- edesc = ipsec_esp_edesc_alloc(areq, DESC_AEAD_GIVENCRYPT_TEXT_LEN *
- CAAM_CMD_SZ);
+ edesc = aead_giv_edesc_alloc(areq, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &contig);
+
if (IS_ERR(edesc))
return PTR_ERR(edesc);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "giv src@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ req->cryptlen, 1);
+#endif
+
+ /* Create and submit job descriptor*/
+ init_aead_giv_job(ctx->sh_desc_givenc,
+ ctx->sh_desc_givenc_dma, edesc, req, contig);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead jobdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
+
desc = edesc->hw_desc;
+ ret = caam_jr_enqueue(jrdev, desc, aead_encrypt_done, req);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ aead_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
- /* insert shared descriptor pointer */
- init_job_desc_shared(desc, ctx->shared_desc_phys,
- desc_len(ctx->sh_desc), HDR_SHARE_DEFER);
+ return ret;
+}
- /*
- * LOAD IMM Info FIFO
- * to DECO, Last, Padding, Random, Message, 16 bytes
- */
- append_load_imm_u32(desc, NFIFOENTRY_DEST_DECO | NFIFOENTRY_LC1 |
- NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DTYPE_MSG |
- NFIFOENTRY_PTYPE_RND | ivsize,
- LDST_SRCDST_WORD_INFO_FIFO);
+/*
+ * allocate and map the ablkcipher extended descriptor for ablkcipher
+ */
+static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request
+ *req, int desc_bytes,
+ bool *iv_contig_out)
+{
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ struct caam_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher);
+ struct device *jrdev = ctx->jrdev;
+ gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ?
+ GFP_KERNEL : GFP_ATOMIC;
+ int src_nents, dst_nents = 0, link_tbl_bytes;
+ struct ablkcipher_edesc *edesc;
+ dma_addr_t iv_dma = 0;
+ bool iv_contig = false;
+ int sgc;
+ int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+ int link_tbl_index;
+
+ src_nents = sg_count(req->src, req->nbytes);
+
+ if (unlikely(req->dst != req->src))
+ dst_nents = sg_count(req->dst, req->nbytes);
+
+ if (likely(req->src == req->dst)) {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ } else {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_TO_DEVICE);
+ sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1,
+ DMA_FROM_DEVICE);
+ }
/*
- * disable info fifo entries since the above serves as the entry
- * this way, the MOVE command won't generate an entry.
- * Note that this isn't required in more recent versions of
- * SEC as a MOVE that doesn't do info FIFO entries is available.
+ * Check if iv can be contiguous with source and destination.
+ * If so, include it. If not, create scatterlist.
*/
- append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
+ iv_dma = dma_map_single(jrdev, req->info, ivsize, DMA_TO_DEVICE);
+ if (!src_nents && iv_dma + ivsize == sg_dma_address(req->src))
+ iv_contig = true;
+ else
+ src_nents = src_nents ? : 1;
+ link_tbl_bytes = ((iv_contig ? 0 : 1) + src_nents + dst_nents) *
+ sizeof(struct link_tbl_entry);
- /* MOVE DECO Alignment -> C1 Context 16 bytes */
- append_move(desc, MOVE_SRC_INFIFO | MOVE_DEST_CLASS1CTX | ivsize);
+ /* allocate space for base edesc and hw desc commands, link tables */
+ edesc = kmalloc(sizeof(struct ablkcipher_edesc) + desc_bytes +
+ link_tbl_bytes, GFP_DMA | flags);
+ if (!edesc) {
+ dev_err(jrdev, "could not allocate extended descriptor\n");
+ return ERR_PTR(-ENOMEM);
+ }
- /* re-enable info fifo entries */
- append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
+ edesc->src_nents = src_nents;
+ edesc->dst_nents = dst_nents;
+ edesc->link_tbl_bytes = link_tbl_bytes;
+ edesc->link_tbl = (void *)edesc + sizeof(struct ablkcipher_edesc) +
+ desc_bytes;
+
+ link_tbl_index = 0;
+ if (!iv_contig) {
+ sg_to_link_tbl_one(edesc->link_tbl, iv_dma, ivsize, 0);
+ sg_to_link_tbl_last(req->src, src_nents,
+ edesc->link_tbl + 1, 0);
+ link_tbl_index += 1 + src_nents;
+ }
+
+ if (unlikely(dst_nents)) {
+ sg_to_link_tbl_last(req->dst, dst_nents,
+ edesc->link_tbl + link_tbl_index, 0);
+ }
+
+ edesc->link_tbl_dma = dma_map_single(jrdev, edesc->link_tbl,
+ link_tbl_bytes, DMA_TO_DEVICE);
+ edesc->iv_dma = iv_dma;
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ablkcipher link_tbl@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->link_tbl,
+ link_tbl_bytes, 1);
+#endif
+
+ *iv_contig_out = iv_contig;
+ return edesc;
+}
+
+static int ablkcipher_encrypt(struct ablkcipher_request *req)
+{
+ struct ablkcipher_edesc *edesc;
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ struct caam_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher);
+ struct device *jrdev = ctx->jrdev;
+ bool iv_contig;
+ u32 *desc;
+ int ret = 0;
+
+ /* allocate extended descriptor */
+ edesc = ablkcipher_edesc_alloc(req, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &iv_contig);
+ if (IS_ERR(edesc))
+ return PTR_ERR(edesc);
- /* MOVE C1 Context -> OFIFO 16 bytes */
- append_move(desc, MOVE_SRC_CLASS1CTX | MOVE_DEST_OUTFIFO | ivsize);
+ /* Create and submit job descriptor*/
+ init_ablkcipher_job(ctx->sh_desc_enc,
+ ctx->sh_desc_enc_dma, edesc, req, iv_contig);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ablkcipher jobdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
+ desc = edesc->hw_desc;
+ ret = caam_jr_enqueue(jrdev, desc, ablkcipher_encrypt_done, req);
- append_fifo_store(desc, iv_dma, ivsize, FIFOST_TYPE_MESSAGE_DATA);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ ablkcipher_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
- return ipsec_esp(edesc, areq, OP_ALG_ENCRYPT, ipsec_esp_encrypt_done);
+ return ret;
}
+static int ablkcipher_decrypt(struct ablkcipher_request *req)
+{
+ struct ablkcipher_edesc *edesc;
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ struct caam_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher);
+ struct device *jrdev = ctx->jrdev;
+ bool iv_contig;
+ u32 *desc;
+ int ret = 0;
+
+ /* allocate extended descriptor */
+ edesc = ablkcipher_edesc_alloc(req, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &iv_contig);
+ if (IS_ERR(edesc))
+ return PTR_ERR(edesc);
+
+ /* Create and submit job descriptor*/
+ init_ablkcipher_job(ctx->sh_desc_dec,
+ ctx->sh_desc_dec_dma, edesc, req, iv_contig);
+ desc = edesc->hw_desc;
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ablkcipher jobdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
+
+ ret = caam_jr_enqueue(jrdev, desc, ablkcipher_decrypt_done, req);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ ablkcipher_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
+
+ return ret;
+}
+
+#define template_aead template_u.aead
+#define template_ablkcipher template_u.ablkcipher
struct caam_alg_template {
char name[CRYPTO_MAX_ALG_NAME];
char driver_name[CRYPTO_MAX_ALG_NAME];
unsigned int blocksize;
- struct aead_alg aead;
+ u32 type;
+ union {
+ struct ablkcipher_alg ablkcipher;
+ struct aead_alg aead;
+ struct blkcipher_alg blkcipher;
+ struct cipher_alg cipher;
+ struct compress_alg compress;
+ struct rng_alg rng;
+ } template_u;
u32 class1_alg_type;
u32 class2_alg_type;
u32 alg_op;
@@ -900,12 +1809,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha1),cbc(aes))",
.driver_name = "authenc-hmac-sha1-cbc-aes-caam",
.blocksize = AES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA1_DIGEST_SIZE,
@@ -918,12 +1828,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha256),cbc(aes))",
.driver_name = "authenc-hmac-sha256-cbc-aes-caam",
.blocksize = AES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA256_DIGEST_SIZE,
@@ -937,12 +1848,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha512),cbc(aes))",
.driver_name = "authenc-hmac-sha512-cbc-aes-caam",
.blocksize = AES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA512_DIGEST_SIZE,
@@ -956,12 +1868,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha1),cbc(des3_ede))",
.driver_name = "authenc-hmac-sha1-cbc-des3_ede-caam",
.blocksize = DES3_EDE_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA1_DIGEST_SIZE,
@@ -974,12 +1887,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha256),cbc(des3_ede))",
.driver_name = "authenc-hmac-sha256-cbc-des3_ede-caam",
.blocksize = DES3_EDE_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA256_DIGEST_SIZE,
@@ -993,12 +1907,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha512),cbc(des3_ede))",
.driver_name = "authenc-hmac-sha512-cbc-des3_ede-caam",
.blocksize = DES3_EDE_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA512_DIGEST_SIZE,
@@ -1012,12 +1927,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha1),cbc(des))",
.driver_name = "authenc-hmac-sha1-cbc-des-caam",
.blocksize = DES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA1_DIGEST_SIZE,
@@ -1030,12 +1946,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha256),cbc(des))",
.driver_name = "authenc-hmac-sha256-cbc-des-caam",
.blocksize = DES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA256_DIGEST_SIZE,
@@ -1049,12 +1966,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha512),cbc(des))",
.driver_name = "authenc-hmac-sha512-cbc-des-caam",
.blocksize = DES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA512_DIGEST_SIZE,
@@ -1064,6 +1982,55 @@ static struct caam_alg_template driver_algs[] = {
OP_ALG_AAI_HMAC_PRECOMP,
.alg_op = OP_ALG_ALGSEL_SHA512 | OP_ALG_AAI_HMAC,
},
+ /* ablkcipher descriptor */
+ {
+ .name = "cbc(aes)",
+ .driver_name = "cbc-aes-caam",
+ .blocksize = AES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_ablkcipher = {
+ .setkey = ablkcipher_setkey,
+ .encrypt = ablkcipher_encrypt,
+ .decrypt = ablkcipher_decrypt,
+ .geniv = "eseqiv",
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CBC,
+ },
+ {
+ .name = "cbc(des3_ede)",
+ .driver_name = "cbc-3des-caam",
+ .blocksize = DES3_EDE_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_ablkcipher = {
+ .setkey = ablkcipher_setkey,
+ .encrypt = ablkcipher_encrypt,
+ .decrypt = ablkcipher_decrypt,
+ .geniv = "eseqiv",
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ },
+ .class1_alg_type = OP_ALG_ALGSEL_3DES | OP_ALG_AAI_CBC,
+ },
+ {
+ .name = "cbc(des)",
+ .driver_name = "cbc-des-caam",
+ .blocksize = DES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_ablkcipher = {
+ .setkey = ablkcipher_setkey,
+ .encrypt = ablkcipher_encrypt,
+ .decrypt = ablkcipher_decrypt,
+ .geniv = "eseqiv",
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ },
+ .class1_alg_type = OP_ALG_ALGSEL_DES | OP_ALG_AAI_CBC,
+ }
};
struct caam_crypto_alg {
@@ -1102,16 +2069,19 @@ static void caam_cra_exit(struct crypto_tfm *tfm)
{
struct caam_ctx *ctx = crypto_tfm_ctx(tfm);
- if (!dma_mapping_error(ctx->jrdev, ctx->shared_desc_phys))
- dma_unmap_single(ctx->jrdev, ctx->shared_desc_phys,
- desc_bytes(ctx->sh_desc), DMA_TO_DEVICE);
- kfree(ctx->sh_desc);
-
- if (!dma_mapping_error(ctx->jrdev, ctx->key_phys))
- dma_unmap_single(ctx->jrdev, ctx->key_phys,
- ctx->split_key_pad_len + ctx->enckeylen,
+ if (ctx->sh_desc_enc_dma &&
+ !dma_mapping_error(ctx->jrdev, ctx->sh_desc_enc_dma))
+ dma_unmap_single(ctx->jrdev, ctx->sh_desc_enc_dma,
+ desc_bytes(ctx->sh_desc_enc), DMA_TO_DEVICE);
+ if (ctx->sh_desc_dec_dma &&
+ !dma_mapping_error(ctx->jrdev, ctx->sh_desc_dec_dma))
+ dma_unmap_single(ctx->jrdev, ctx->sh_desc_dec_dma,
+ desc_bytes(ctx->sh_desc_dec), DMA_TO_DEVICE);
+ if (ctx->sh_desc_givenc_dma &&
+ !dma_mapping_error(ctx->jrdev, ctx->sh_desc_givenc_dma))
+ dma_unmap_single(ctx->jrdev, ctx->sh_desc_givenc_dma,
+ desc_bytes(ctx->sh_desc_givenc),
DMA_TO_DEVICE);
- kfree(ctx->key);
}
static void __exit caam_algapi_exit(void)
@@ -1175,12 +2145,20 @@ static struct caam_crypto_alg *caam_alg_alloc(struct device *ctrldev,
alg->cra_init = caam_cra_init;
alg->cra_exit = caam_cra_exit;
alg->cra_priority = CAAM_CRA_PRIORITY;
- alg->cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC;
alg->cra_blocksize = template->blocksize;
alg->cra_alignmask = 0;
- alg->cra_type = &crypto_aead_type;
alg->cra_ctxsize = sizeof(struct caam_ctx);
- alg->cra_u.aead = template->aead;
+ alg->cra_flags = CRYPTO_ALG_ASYNC | template->type;
+ switch (template->type) {
+ case CRYPTO_ALG_TYPE_ABLKCIPHER:
+ alg->cra_type = &crypto_ablkcipher_type;
+ alg->cra_ablkcipher = template->template_ablkcipher;
+ break;
+ case CRYPTO_ALG_TYPE_AEAD:
+ alg->cra_type = &crypto_aead_type;
+ alg->cra_aead = template->template_aead;
+ break;
+ }
t_alg->class1_alg_type = template->class1_alg_type;
t_alg->class2_alg_type = template->class2_alg_type;
diff --git a/drivers/crypto/caam/compat.h b/drivers/crypto/caam/compat.h
index 950450346f70..d38f2afaa966 100644
--- a/drivers/crypto/caam/compat.h
+++ b/drivers/crypto/caam/compat.h
@@ -31,5 +31,6 @@
#include <crypto/aead.h>
#include <crypto/authenc.h>
#include <crypto/scatterwalk.h>
+#include <crypto/internal/skcipher.h>
#endif /* !defined(CAAM_COMPAT_H) */
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index 9009713a3c2e..73988bb7322a 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -52,9 +52,11 @@ static int caam_probe(struct platform_device *pdev)
struct caam_ctrl __iomem *ctrl;
struct caam_full __iomem *topregs;
struct caam_drv_private *ctrlpriv;
- struct caam_perfmon *perfmon;
struct caam_deco **deco;
u32 deconum;
+#ifdef CONFIG_DEBUG_FS
+ struct caam_perfmon *perfmon;
+#endif
ctrlpriv = kzalloc(sizeof(struct caam_drv_private), GFP_KERNEL);
if (!ctrlpriv)
@@ -164,52 +166,52 @@ static int caam_probe(struct platform_device *pdev)
/* Controller-level - performance monitor counters */
ctrlpriv->ctl_rq_dequeued =
debugfs_create_u64("rq_dequeued",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->req_dequeued);
ctrlpriv->ctl_ob_enc_req =
debugfs_create_u64("ob_rq_encrypted",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ob_enc_req);
ctrlpriv->ctl_ib_dec_req =
debugfs_create_u64("ib_rq_decrypted",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ib_dec_req);
ctrlpriv->ctl_ob_enc_bytes =
debugfs_create_u64("ob_bytes_encrypted",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ob_enc_bytes);
ctrlpriv->ctl_ob_prot_bytes =
debugfs_create_u64("ob_bytes_protected",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ob_prot_bytes);
ctrlpriv->ctl_ib_dec_bytes =
debugfs_create_u64("ib_bytes_decrypted",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ib_dec_bytes);
ctrlpriv->ctl_ib_valid_bytes =
debugfs_create_u64("ib_bytes_validated",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ib_valid_bytes);
/* Controller level - global status values */
ctrlpriv->ctl_faultaddr =
debugfs_create_u64("fault_addr",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->faultaddr);
ctrlpriv->ctl_faultdetail =
debugfs_create_u32("fault_detail",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->faultdetail);
ctrlpriv->ctl_faultstatus =
debugfs_create_u32("fault_status",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->status);
/* Internal covering keys (useful in non-secure mode only) */
ctrlpriv->ctl_kek_wrap.data = &ctrlpriv->ctrl->kek[0];
ctrlpriv->ctl_kek_wrap.size = KEK_KEY_SIZE * sizeof(u32);
ctrlpriv->ctl_kek = debugfs_create_blob("kek",
- S_IFCHR | S_IRUSR |
+ S_IRUSR |
S_IRGRP | S_IROTH,
ctrlpriv->ctl,
&ctrlpriv->ctl_kek_wrap);
@@ -217,7 +219,7 @@ static int caam_probe(struct platform_device *pdev)
ctrlpriv->ctl_tkek_wrap.data = &ctrlpriv->ctrl->tkek[0];
ctrlpriv->ctl_tkek_wrap.size = KEK_KEY_SIZE * sizeof(u32);
ctrlpriv->ctl_tkek = debugfs_create_blob("tkek",
- S_IFCHR | S_IRUSR |
+ S_IRUSR |
S_IRGRP | S_IROTH,
ctrlpriv->ctl,
&ctrlpriv->ctl_tkek_wrap);
@@ -225,7 +227,7 @@ static int caam_probe(struct platform_device *pdev)
ctrlpriv->ctl_tdsk_wrap.data = &ctrlpriv->ctrl->tdsk[0];
ctrlpriv->ctl_tdsk_wrap.size = KEK_KEY_SIZE * sizeof(u32);
ctrlpriv->ctl_tdsk = debugfs_create_blob("tdsk",
- S_IFCHR | S_IRUSR |
+ S_IRUSR |
S_IRGRP | S_IROTH,
ctrlpriv->ctl,
&ctrlpriv->ctl_tdsk_wrap);
diff --git a/drivers/crypto/caam/desc_constr.h b/drivers/crypto/caam/desc_constr.h
index 46915800c26f..0991323cf3fd 100644
--- a/drivers/crypto/caam/desc_constr.h
+++ b/drivers/crypto/caam/desc_constr.h
@@ -9,7 +9,7 @@
#define IMMEDIATE (1 << 23)
#define CAAM_CMD_SZ sizeof(u32)
#define CAAM_PTR_SZ sizeof(dma_addr_t)
-#define CAAM_DESC_BYTES_MAX (CAAM_CMD_SZ * 64)
+#define CAAM_DESC_BYTES_MAX (CAAM_CMD_SZ * MAX_CAAM_DESCSIZE)
#ifdef DEBUG
#define PRINT_POS do { printk(KERN_DEBUG "%02d: %s\n", desc_len(desc),\
@@ -18,6 +18,9 @@
#define PRINT_POS
#endif
+#define SET_OK_PROP_ERRORS (IMMEDIATE | LDST_CLASS_DECO | \
+ LDST_SRCDST_WORD_DECOCTRL | \
+ (LDOFF_CHG_SHARE_OK_PROP << LDST_OFFSET_SHIFT))
#define DISABLE_AUTO_INFO_FIFO (IMMEDIATE | LDST_CLASS_DECO | \
LDST_SRCDST_WORD_DECOCTRL | \
(LDOFF_DISABLE_AUTO_NFIFO << LDST_OFFSET_SHIFT))
@@ -203,3 +206,56 @@ static inline void append_##cmd##_imm_##type(u32 *desc, type immediate, \
append_cmd(desc, immediate); \
}
APPEND_CMD_RAW_IMM(load, LOAD, u32);
+
+/*
+ * Append math command. Only the last part of destination and source need to
+ * be specified
+ */
+#define APPEND_MATH(op, desc, dest, src_0, src_1, len) \
+append_cmd(desc, CMD_MATH | MATH_FUN_##op | MATH_DEST_##dest | \
+ MATH_SRC0_##src_0 | MATH_SRC1_##src_1 | (u32) (len & MATH_LEN_MASK));
+
+#define append_math_add(desc, dest, src0, src1, len) \
+ APPEND_MATH(ADD, desc, dest, src0, src1, len)
+#define append_math_sub(desc, dest, src0, src1, len) \
+ APPEND_MATH(SUB, desc, dest, src0, src1, len)
+#define append_math_add_c(desc, dest, src0, src1, len) \
+ APPEND_MATH(ADDC, desc, dest, src0, src1, len)
+#define append_math_sub_b(desc, dest, src0, src1, len) \
+ APPEND_MATH(SUBB, desc, dest, src0, src1, len)
+#define append_math_and(desc, dest, src0, src1, len) \
+ APPEND_MATH(AND, desc, dest, src0, src1, len)
+#define append_math_or(desc, dest, src0, src1, len) \
+ APPEND_MATH(OR, desc, dest, src0, src1, len)
+#define append_math_xor(desc, dest, src0, src1, len) \
+ APPEND_MATH(XOR, desc, dest, src0, src1, len)
+#define append_math_lshift(desc, dest, src0, src1, len) \
+ APPEND_MATH(LSHIFT, desc, dest, src0, src1, len)
+#define append_math_rshift(desc, dest, src0, src1, len) \
+ APPEND_MATH(RSHIFT, desc, dest, src0, src1, len)
+
+/* Exactly one source is IMM. Data is passed in as u32 value */
+#define APPEND_MATH_IMM_u32(op, desc, dest, src_0, src_1, data) \
+do { \
+ APPEND_MATH(op, desc, dest, src_0, src_1, CAAM_CMD_SZ); \
+ append_cmd(desc, data); \
+} while (0);
+
+#define append_math_add_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(ADD, desc, dest, src0, src1, data)
+#define append_math_sub_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(SUB, desc, dest, src0, src1, data)
+#define append_math_add_c_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(ADDC, desc, dest, src0, src1, data)
+#define append_math_sub_b_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(SUBB, desc, dest, src0, src1, data)
+#define append_math_and_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(AND, desc, dest, src0, src1, data)
+#define append_math_or_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(OR, desc, dest, src0, src1, data)
+#define append_math_xor_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(XOR, desc, dest, src0, src1, data)
+#define append_math_lshift_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(LSHIFT, desc, dest, src0, src1, data)
+#define append_math_rshift_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(RSHIFT, desc, dest, src0, src1, data)
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index a84250a5dd51..fe765f49de58 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -2744,10 +2744,8 @@ static int __init hifn_init(void)
unsigned int freq;
int err;
- if (sizeof(dma_addr_t) > 4) {
- printk(KERN_INFO "HIFN supports only 32-bit addresses.\n");
- return -EINVAL;
- }
+ /* HIFN supports only 32-bit addresses */
+ BUILD_BUG_ON(sizeof(dma_addr_t) != 4);
if (strncmp(hifn_pll_ref, "ext", 3) &&
strncmp(hifn_pll_ref, "pci", 3)) {
diff --git a/drivers/crypto/mv_cesa.c b/drivers/crypto/mv_cesa.c
index 3cf303ee3fe3..dcd8babae9eb 100644
--- a/drivers/crypto/mv_cesa.c
+++ b/drivers/crypto/mv_cesa.c
@@ -15,6 +15,7 @@
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <crypto/internal/hash.h>
#include <crypto/sha.h>
@@ -342,11 +343,13 @@ static void mv_process_hash_current(int first_block)
else
op.config |= CFG_MID_FRAG;
- writel(req_ctx->state[0], cpg->reg + DIGEST_INITIAL_VAL_A);
- writel(req_ctx->state[1], cpg->reg + DIGEST_INITIAL_VAL_B);
- writel(req_ctx->state[2], cpg->reg + DIGEST_INITIAL_VAL_C);
- writel(req_ctx->state[3], cpg->reg + DIGEST_INITIAL_VAL_D);
- writel(req_ctx->state[4], cpg->reg + DIGEST_INITIAL_VAL_E);
+ if (first_block) {
+ writel(req_ctx->state[0], cpg->reg + DIGEST_INITIAL_VAL_A);
+ writel(req_ctx->state[1], cpg->reg + DIGEST_INITIAL_VAL_B);
+ writel(req_ctx->state[2], cpg->reg + DIGEST_INITIAL_VAL_C);
+ writel(req_ctx->state[3], cpg->reg + DIGEST_INITIAL_VAL_D);
+ writel(req_ctx->state[4], cpg->reg + DIGEST_INITIAL_VAL_E);
+ }
}
memcpy(cpg->sram + SRAM_CONFIG, &op, sizeof(struct sec_accel_config));
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index 2e5b2044c96f..8944dabc0e3c 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1,6 +1,6 @@
/* n2_core.c: Niagara2 Stream Processing Unit (SPU) crypto support.
*
- * Copyright (C) 2010 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2010, 2011 David S. Miller <davem@davemloft.net>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -31,8 +31,8 @@
#include "n2_core.h"
#define DRV_MODULE_NAME "n2_crypto"
-#define DRV_MODULE_VERSION "0.1"
-#define DRV_MODULE_RELDATE "April 29, 2010"
+#define DRV_MODULE_VERSION "0.2"
+#define DRV_MODULE_RELDATE "July 28, 2011"
static char version[] __devinitdata =
DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
@@ -1006,9 +1006,9 @@ static int n2_do_ecb(struct ablkcipher_request *req, bool encrypt)
spin_unlock_irqrestore(&qp->lock, flags);
+out:
put_cpu();
-out:
n2_chunk_complete(req, NULL);
return err;
}
@@ -1096,9 +1096,9 @@ static int n2_do_chaining(struct ablkcipher_request *req, bool encrypt)
spin_unlock_irqrestore(&qp->lock, flags);
+out:
put_cpu();
-out:
n2_chunk_complete(req, err ? NULL : final_iv_addr);
return err;
}
@@ -1823,22 +1823,17 @@ static int spu_mdesc_scan(struct mdesc_handle *mdesc, struct platform_device *de
static int __devinit get_irq_props(struct mdesc_handle *mdesc, u64 node,
struct spu_mdesc_info *ip)
{
- const u64 *intr, *ino;
- int intr_len, ino_len;
+ const u64 *ino;
+ int ino_len;
int i;
- intr = mdesc_get_property(mdesc, node, "intr", &intr_len);
- if (!intr)
- return -ENODEV;
-
ino = mdesc_get_property(mdesc, node, "ino", &ino_len);
- if (!ino)
+ if (!ino) {
+ printk("NO 'ino'\n");
return -ENODEV;
+ }
- if (intr_len != ino_len)
- return -EINVAL;
-
- ip->num_intrs = intr_len / sizeof(u64);
+ ip->num_intrs = ino_len / sizeof(u64);
ip->ino_table = kzalloc((sizeof(struct ino_blob) *
ip->num_intrs),
GFP_KERNEL);
@@ -1847,7 +1842,7 @@ static int __devinit get_irq_props(struct mdesc_handle *mdesc, u64 node,
for (i = 0; i < ip->num_intrs; i++) {
struct ino_blob *b = &ip->ino_table[i];
- b->intr = intr[i];
+ b->intr = i + 1;
b->ino = ino[i];
}
@@ -2204,6 +2199,10 @@ static struct of_device_id n2_crypto_match[] = {
.name = "n2cp",
.compatible = "SUNW,vf-cwq",
},
+ {
+ .name = "n2cp",
+ .compatible = "SUNW,kt-cwq",
+ },
{},
};
@@ -2228,6 +2227,10 @@ static struct of_device_id n2_mau_match[] = {
.name = "ncp",
.compatible = "SUNW,vf-mau",
},
+ {
+ .name = "ncp",
+ .compatible = "SUNW,kt-mau",
+ },
{},
};
diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c
index ba8f1ea84c5e..6399a8f1938a 100644
--- a/drivers/crypto/omap-sham.c
+++ b/drivers/crypto/omap-sham.c
@@ -72,17 +72,20 @@
#define DEFAULT_TIMEOUT_INTERVAL HZ
-#define FLAGS_FINUP 0x0002
-#define FLAGS_FINAL 0x0004
-#define FLAGS_SG 0x0008
-#define FLAGS_SHA1 0x0010
-#define FLAGS_DMA_ACTIVE 0x0020
-#define FLAGS_OUTPUT_READY 0x0040
-#define FLAGS_INIT 0x0100
-#define FLAGS_CPU 0x0200
-#define FLAGS_HMAC 0x0400
-#define FLAGS_ERROR 0x0800
-#define FLAGS_BUSY 0x1000
+/* mostly device flags */
+#define FLAGS_BUSY 0
+#define FLAGS_FINAL 1
+#define FLAGS_DMA_ACTIVE 2
+#define FLAGS_OUTPUT_READY 3
+#define FLAGS_INIT 4
+#define FLAGS_CPU 5
+#define FLAGS_DMA_READY 6
+/* context flags */
+#define FLAGS_FINUP 16
+#define FLAGS_SG 17
+#define FLAGS_SHA1 18
+#define FLAGS_HMAC 19
+#define FLAGS_ERROR 20
#define OP_UPDATE 1
#define OP_FINAL 2
@@ -144,7 +147,6 @@ struct omap_sham_dev {
int dma;
int dma_lch;
struct tasklet_struct done_task;
- struct tasklet_struct queue_task;
unsigned long flags;
struct crypto_queue queue;
@@ -223,7 +225,7 @@ static void omap_sham_copy_ready_hash(struct ahash_request *req)
if (!hash)
return;
- if (likely(ctx->flags & FLAGS_SHA1)) {
+ if (likely(ctx->flags & BIT(FLAGS_SHA1))) {
/* SHA1 results are in big endian */
for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++)
hash[i] = be32_to_cpu(in[i]);
@@ -238,7 +240,7 @@ static int omap_sham_hw_init(struct omap_sham_dev *dd)
{
clk_enable(dd->iclk);
- if (!(dd->flags & FLAGS_INIT)) {
+ if (!test_bit(FLAGS_INIT, &dd->flags)) {
omap_sham_write_mask(dd, SHA_REG_MASK,
SHA_REG_MASK_SOFTRESET, SHA_REG_MASK_SOFTRESET);
@@ -246,7 +248,7 @@ static int omap_sham_hw_init(struct omap_sham_dev *dd)
SHA_REG_SYSSTATUS_RESETDONE))
return -ETIMEDOUT;
- dd->flags |= FLAGS_INIT;
+ set_bit(FLAGS_INIT, &dd->flags);
dd->err = 0;
}
@@ -269,7 +271,7 @@ static void omap_sham_write_ctrl(struct omap_sham_dev *dd, size_t length,
* Setting ALGO_CONST only for the first iteration
* and CLOSE_HASH only for the last one.
*/
- if (ctx->flags & FLAGS_SHA1)
+ if (ctx->flags & BIT(FLAGS_SHA1))
val |= SHA_REG_CTRL_ALGO;
if (!ctx->digcnt)
val |= SHA_REG_CTRL_ALGO_CONST;
@@ -301,7 +303,9 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf,
return -ETIMEDOUT;
if (final)
- ctx->flags |= FLAGS_FINAL; /* catch last interrupt */
+ set_bit(FLAGS_FINAL, &dd->flags); /* catch last interrupt */
+
+ set_bit(FLAGS_CPU, &dd->flags);
len32 = DIV_ROUND_UP(length, sizeof(u32));
@@ -334,9 +338,9 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr,
ctx->digcnt += length;
if (final)
- ctx->flags |= FLAGS_FINAL; /* catch last interrupt */
+ set_bit(FLAGS_FINAL, &dd->flags); /* catch last interrupt */
- dd->flags |= FLAGS_DMA_ACTIVE;
+ set_bit(FLAGS_DMA_ACTIVE, &dd->flags);
omap_start_dma(dd->dma_lch);
@@ -392,7 +396,7 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd,
return -EINVAL;
}
- ctx->flags &= ~FLAGS_SG;
+ ctx->flags &= ~BIT(FLAGS_SG);
/* next call does not fail... so no unmap in the case of error */
return omap_sham_xmit_dma(dd, ctx->dma_addr, length, final);
@@ -406,7 +410,7 @@ static int omap_sham_update_dma_slow(struct omap_sham_dev *dd)
omap_sham_append_sg(ctx);
- final = (ctx->flags & FLAGS_FINUP) && !ctx->total;
+ final = (ctx->flags & BIT(FLAGS_FINUP)) && !ctx->total;
dev_dbg(dd->dev, "slow: bufcnt: %u, digcnt: %d, final: %d\n",
ctx->bufcnt, ctx->digcnt, final);
@@ -452,7 +456,7 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd)
length = min(ctx->total, sg->length);
if (sg_is_last(sg)) {
- if (!(ctx->flags & FLAGS_FINUP)) {
+ if (!(ctx->flags & BIT(FLAGS_FINUP))) {
/* not last sg must be SHA1_MD5_BLOCK_SIZE aligned */
tail = length & (SHA1_MD5_BLOCK_SIZE - 1);
/* without finup() we need one block to close hash */
@@ -467,12 +471,12 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd)
return -EINVAL;
}
- ctx->flags |= FLAGS_SG;
+ ctx->flags |= BIT(FLAGS_SG);
ctx->total -= length;
ctx->offset = length; /* offset where to start slow */
- final = (ctx->flags & FLAGS_FINUP) && !ctx->total;
+ final = (ctx->flags & BIT(FLAGS_FINUP)) && !ctx->total;
/* next call does not fail... so no unmap in the case of error */
return omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final);
@@ -495,7 +499,7 @@ static int omap_sham_update_dma_stop(struct omap_sham_dev *dd)
struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
omap_stop_dma(dd->dma_lch);
- if (ctx->flags & FLAGS_SG) {
+ if (ctx->flags & BIT(FLAGS_SG)) {
dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE);
if (ctx->sg->length == ctx->offset) {
ctx->sg = sg_next(ctx->sg);
@@ -537,18 +541,18 @@ static int omap_sham_init(struct ahash_request *req)
crypto_ahash_digestsize(tfm));
if (crypto_ahash_digestsize(tfm) == SHA1_DIGEST_SIZE)
- ctx->flags |= FLAGS_SHA1;
+ ctx->flags |= BIT(FLAGS_SHA1);
ctx->bufcnt = 0;
ctx->digcnt = 0;
ctx->buflen = BUFLEN;
- if (tctx->flags & FLAGS_HMAC) {
+ if (tctx->flags & BIT(FLAGS_HMAC)) {
struct omap_sham_hmac_ctx *bctx = tctx->base;
memcpy(ctx->buffer, bctx->ipad, SHA1_MD5_BLOCK_SIZE);
ctx->bufcnt = SHA1_MD5_BLOCK_SIZE;
- ctx->flags |= FLAGS_HMAC;
+ ctx->flags |= BIT(FLAGS_HMAC);
}
return 0;
@@ -562,9 +566,9 @@ static int omap_sham_update_req(struct omap_sham_dev *dd)
int err;
dev_dbg(dd->dev, "update_req: total: %u, digcnt: %d, finup: %d\n",
- ctx->total, ctx->digcnt, (ctx->flags & FLAGS_FINUP) != 0);
+ ctx->total, ctx->digcnt, (ctx->flags & BIT(FLAGS_FINUP)) != 0);
- if (ctx->flags & FLAGS_CPU)
+ if (ctx->flags & BIT(FLAGS_CPU))
err = omap_sham_update_cpu(dd);
else
err = omap_sham_update_dma_start(dd);
@@ -624,7 +628,7 @@ static int omap_sham_finish(struct ahash_request *req)
if (ctx->digcnt) {
omap_sham_copy_ready_hash(req);
- if (ctx->flags & FLAGS_HMAC)
+ if (ctx->flags & BIT(FLAGS_HMAC))
err = omap_sham_finish_hmac(req);
}
@@ -639,18 +643,23 @@ static void omap_sham_finish_req(struct ahash_request *req, int err)
struct omap_sham_dev *dd = ctx->dd;
if (!err) {
- omap_sham_copy_hash(ctx->dd->req, 1);
- if (ctx->flags & FLAGS_FINAL)
+ omap_sham_copy_hash(req, 1);
+ if (test_bit(FLAGS_FINAL, &dd->flags))
err = omap_sham_finish(req);
} else {
- ctx->flags |= FLAGS_ERROR;
+ ctx->flags |= BIT(FLAGS_ERROR);
}
+ /* atomic operation is not needed here */
+ dd->flags &= ~(BIT(FLAGS_BUSY) | BIT(FLAGS_FINAL) | BIT(FLAGS_CPU) |
+ BIT(FLAGS_DMA_READY) | BIT(FLAGS_OUTPUT_READY));
clk_disable(dd->iclk);
- dd->flags &= ~FLAGS_BUSY;
if (req->base.complete)
req->base.complete(&req->base, err);
+
+ /* handle new request */
+ tasklet_schedule(&dd->done_task);
}
static int omap_sham_handle_queue(struct omap_sham_dev *dd,
@@ -658,21 +667,20 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd,
{
struct crypto_async_request *async_req, *backlog;
struct omap_sham_reqctx *ctx;
- struct ahash_request *prev_req;
unsigned long flags;
int err = 0, ret = 0;
spin_lock_irqsave(&dd->lock, flags);
if (req)
ret = ahash_enqueue_request(&dd->queue, req);
- if (dd->flags & FLAGS_BUSY) {
+ if (test_bit(FLAGS_BUSY, &dd->flags)) {
spin_unlock_irqrestore(&dd->lock, flags);
return ret;
}
backlog = crypto_get_backlog(&dd->queue);
async_req = crypto_dequeue_request(&dd->queue);
if (async_req)
- dd->flags |= FLAGS_BUSY;
+ set_bit(FLAGS_BUSY, &dd->flags);
spin_unlock_irqrestore(&dd->lock, flags);
if (!async_req)
@@ -682,16 +690,12 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd,
backlog->complete(backlog, -EINPROGRESS);
req = ahash_request_cast(async_req);
-
- prev_req = dd->req;
dd->req = req;
-
ctx = ahash_request_ctx(req);
dev_dbg(dd->dev, "handling new req, op: %lu, nbytes: %d\n",
ctx->op, req->nbytes);
-
err = omap_sham_hw_init(dd);
if (err)
goto err1;
@@ -712,18 +716,16 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd,
if (ctx->op == OP_UPDATE) {
err = omap_sham_update_req(dd);
- if (err != -EINPROGRESS && (ctx->flags & FLAGS_FINUP))
+ if (err != -EINPROGRESS && (ctx->flags & BIT(FLAGS_FINUP)))
/* no final() after finup() */
err = omap_sham_final_req(dd);
} else if (ctx->op == OP_FINAL) {
err = omap_sham_final_req(dd);
}
err1:
- if (err != -EINPROGRESS) {
+ if (err != -EINPROGRESS)
/* done_task will not finish it, so do it here */
omap_sham_finish_req(req, err);
- tasklet_schedule(&dd->queue_task);
- }
dev_dbg(dd->dev, "exit, err: %d\n", err);
@@ -752,7 +754,7 @@ static int omap_sham_update(struct ahash_request *req)
ctx->sg = req->src;
ctx->offset = 0;
- if (ctx->flags & FLAGS_FINUP) {
+ if (ctx->flags & BIT(FLAGS_FINUP)) {
if ((ctx->digcnt + ctx->bufcnt + ctx->total) < 9) {
/*
* OMAP HW accel works only with buffers >= 9
@@ -765,7 +767,7 @@ static int omap_sham_update(struct ahash_request *req)
/*
* faster to use CPU for short transfers
*/
- ctx->flags |= FLAGS_CPU;
+ ctx->flags |= BIT(FLAGS_CPU);
}
} else if (ctx->bufcnt + ctx->total < ctx->buflen) {
omap_sham_append_sg(ctx);
@@ -802,9 +804,9 @@ static int omap_sham_final(struct ahash_request *req)
{
struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
- ctx->flags |= FLAGS_FINUP;
+ ctx->flags |= BIT(FLAGS_FINUP);
- if (ctx->flags & FLAGS_ERROR)
+ if (ctx->flags & BIT(FLAGS_ERROR))
return 0; /* uncompleted hash is not needed */
/* OMAP HW accel works only with buffers >= 9 */
@@ -823,7 +825,7 @@ static int omap_sham_finup(struct ahash_request *req)
struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
int err1, err2;
- ctx->flags |= FLAGS_FINUP;
+ ctx->flags |= BIT(FLAGS_FINUP);
err1 = omap_sham_update(req);
if (err1 == -EINPROGRESS || err1 == -EBUSY)
@@ -895,7 +897,7 @@ static int omap_sham_cra_init_alg(struct crypto_tfm *tfm, const char *alg_base)
if (alg_base) {
struct omap_sham_hmac_ctx *bctx = tctx->base;
- tctx->flags |= FLAGS_HMAC;
+ tctx->flags |= BIT(FLAGS_HMAC);
bctx->shash = crypto_alloc_shash(alg_base, 0,
CRYPTO_ALG_NEED_FALLBACK);
if (IS_ERR(bctx->shash)) {
@@ -932,7 +934,7 @@ static void omap_sham_cra_exit(struct crypto_tfm *tfm)
crypto_free_shash(tctx->fallback);
tctx->fallback = NULL;
- if (tctx->flags & FLAGS_HMAC) {
+ if (tctx->flags & BIT(FLAGS_HMAC)) {
struct omap_sham_hmac_ctx *bctx = tctx->base;
crypto_free_shash(bctx->shash);
}
@@ -1036,51 +1038,46 @@ static struct ahash_alg algs[] = {
static void omap_sham_done_task(unsigned long data)
{
struct omap_sham_dev *dd = (struct omap_sham_dev *)data;
- struct ahash_request *req = dd->req;
- struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
- int ready = 0, err = 0;
+ int err = 0;
- if (ctx->flags & FLAGS_OUTPUT_READY) {
- ctx->flags &= ~FLAGS_OUTPUT_READY;
- ready = 1;
+ if (!test_bit(FLAGS_BUSY, &dd->flags)) {
+ omap_sham_handle_queue(dd, NULL);
+ return;
}
- if (dd->flags & FLAGS_DMA_ACTIVE) {
- dd->flags &= ~FLAGS_DMA_ACTIVE;
- omap_sham_update_dma_stop(dd);
- if (!dd->err)
+ if (test_bit(FLAGS_CPU, &dd->flags)) {
+ if (test_and_clear_bit(FLAGS_OUTPUT_READY, &dd->flags))
+ goto finish;
+ } else if (test_bit(FLAGS_DMA_READY, &dd->flags)) {
+ if (test_and_clear_bit(FLAGS_DMA_ACTIVE, &dd->flags)) {
+ omap_sham_update_dma_stop(dd);
+ if (dd->err) {
+ err = dd->err;
+ goto finish;
+ }
+ }
+ if (test_and_clear_bit(FLAGS_OUTPUT_READY, &dd->flags)) {
+ /* hash or semi-hash ready */
+ clear_bit(FLAGS_DMA_READY, &dd->flags);
err = omap_sham_update_dma_start(dd);
+ if (err != -EINPROGRESS)
+ goto finish;
+ }
}
- err = dd->err ? : err;
-
- if (err != -EINPROGRESS && (ready || err)) {
- dev_dbg(dd->dev, "update done: err: %d\n", err);
- /* finish curent request */
- omap_sham_finish_req(req, err);
- /* start new request */
- omap_sham_handle_queue(dd, NULL);
- }
-}
-
-static void omap_sham_queue_task(unsigned long data)
-{
- struct omap_sham_dev *dd = (struct omap_sham_dev *)data;
+ return;
- omap_sham_handle_queue(dd, NULL);
+finish:
+ dev_dbg(dd->dev, "update done: err: %d\n", err);
+ /* finish curent request */
+ omap_sham_finish_req(dd->req, err);
}
static irqreturn_t omap_sham_irq(int irq, void *dev_id)
{
struct omap_sham_dev *dd = dev_id;
- struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
-
- if (!ctx) {
- dev_err(dd->dev, "unknown interrupt.\n");
- return IRQ_HANDLED;
- }
- if (unlikely(ctx->flags & FLAGS_FINAL))
+ if (unlikely(test_bit(FLAGS_FINAL, &dd->flags)))
/* final -> allow device to go to power-saving mode */
omap_sham_write_mask(dd, SHA_REG_CTRL, 0, SHA_REG_CTRL_LENGTH);
@@ -1088,8 +1085,12 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id)
SHA_REG_CTRL_OUTPUT_READY);
omap_sham_read(dd, SHA_REG_CTRL);
- ctx->flags |= FLAGS_OUTPUT_READY;
- dd->err = 0;
+ if (!test_bit(FLAGS_BUSY, &dd->flags)) {
+ dev_warn(dd->dev, "Interrupt when no active requests.\n");
+ return IRQ_HANDLED;
+ }
+
+ set_bit(FLAGS_OUTPUT_READY, &dd->flags);
tasklet_schedule(&dd->done_task);
return IRQ_HANDLED;
@@ -1102,9 +1103,10 @@ static void omap_sham_dma_callback(int lch, u16 ch_status, void *data)
if (ch_status != OMAP_DMA_BLOCK_IRQ) {
pr_err("omap-sham DMA error status: 0x%hx\n", ch_status);
dd->err = -EIO;
- dd->flags &= ~FLAGS_INIT; /* request to re-initialize */
+ clear_bit(FLAGS_INIT, &dd->flags);/* request to re-initialize */
}
+ set_bit(FLAGS_DMA_READY, &dd->flags);
tasklet_schedule(&dd->done_task);
}
@@ -1151,7 +1153,6 @@ static int __devinit omap_sham_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&dd->list);
spin_lock_init(&dd->lock);
tasklet_init(&dd->done_task, omap_sham_done_task, (unsigned long)dd);
- tasklet_init(&dd->queue_task, omap_sham_queue_task, (unsigned long)dd);
crypto_init_queue(&dd->queue, OMAP_SHAM_QUEUE_LENGTH);
dd->irq = -1;
@@ -1260,7 +1261,6 @@ static int __devexit omap_sham_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(algs); i++)
crypto_unregister_ahash(&algs[i]);
tasklet_kill(&dd->done_task);
- tasklet_kill(&dd->queue_task);
iounmap(dd->io_base);
clk_put(dd->iclk);
omap_sham_dma_cleanup(dd);
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index db33d300aa23..29b9469f8378 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -508,10 +508,8 @@ static int __init padlock_init(void)
int ret;
struct cpuinfo_x86 *c = &cpu_data(0);
- if (!cpu_has_xcrypt) {
- printk(KERN_NOTICE PFX "VIA PadLock not detected.\n");
+ if (!cpu_has_xcrypt)
return -ENODEV;
- }
if (!cpu_has_xcrypt_enabled) {
printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
diff --git a/drivers/crypto/picoxcell_crypto.c b/drivers/crypto/picoxcell_crypto.c
index 230b5b8cda1f..a2b553eabbdb 100644
--- a/drivers/crypto/picoxcell_crypto.c
+++ b/drivers/crypto/picoxcell_crypto.c
@@ -34,6 +34,7 @@
#include <linux/io.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/rtnetlink.h>
@@ -1241,8 +1242,8 @@ static void spacc_spacc_complete(unsigned long data)
spin_unlock_irqrestore(&engine->hw_lock, flags);
list_for_each_entry_safe(req, tmp, &completed, list) {
- req->complete(req);
list_del(&req->list);
+ req->complete(req);
}
}
@@ -1657,10 +1658,33 @@ static struct spacc_alg l2_engine_algs[] = {
},
};
-static int __devinit spacc_probe(struct platform_device *pdev,
- unsigned max_ctxs, size_t cipher_pg_sz,
- size_t hash_pg_sz, size_t fifo_sz,
- struct spacc_alg *algs, size_t num_algs)
+#ifdef CONFIG_OF
+static const struct of_device_id spacc_of_id_table[] = {
+ { .compatible = "picochip,spacc-ipsec" },
+ { .compatible = "picochip,spacc-l2" },
+ {}
+};
+#else /* CONFIG_OF */
+#define spacc_of_id_table NULL
+#endif /* CONFIG_OF */
+
+static bool spacc_is_compatible(struct platform_device *pdev,
+ const char *spacc_type)
+{
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
+
+ if (platid && !strcmp(platid->name, spacc_type))
+ return true;
+
+#ifdef CONFIG_OF
+ if (of_device_is_compatible(pdev->dev.of_node, spacc_type))
+ return true;
+#endif /* CONFIG_OF */
+
+ return false;
+}
+
+static int __devinit spacc_probe(struct platform_device *pdev)
{
int i, err, ret = -EINVAL;
struct resource *mem, *irq;
@@ -1669,13 +1693,25 @@ static int __devinit spacc_probe(struct platform_device *pdev,
if (!engine)
return -ENOMEM;
- engine->max_ctxs = max_ctxs;
- engine->cipher_pg_sz = cipher_pg_sz;
- engine->hash_pg_sz = hash_pg_sz;
- engine->fifo_sz = fifo_sz;
- engine->algs = algs;
- engine->num_algs = num_algs;
- engine->name = dev_name(&pdev->dev);
+ if (spacc_is_compatible(pdev, "picochip,spacc-ipsec")) {
+ engine->max_ctxs = SPACC_CRYPTO_IPSEC_MAX_CTXS;
+ engine->cipher_pg_sz = SPACC_CRYPTO_IPSEC_CIPHER_PG_SZ;
+ engine->hash_pg_sz = SPACC_CRYPTO_IPSEC_HASH_PG_SZ;
+ engine->fifo_sz = SPACC_CRYPTO_IPSEC_FIFO_SZ;
+ engine->algs = ipsec_engine_algs;
+ engine->num_algs = ARRAY_SIZE(ipsec_engine_algs);
+ } else if (spacc_is_compatible(pdev, "picochip,spacc-l2")) {
+ engine->max_ctxs = SPACC_CRYPTO_L2_MAX_CTXS;
+ engine->cipher_pg_sz = SPACC_CRYPTO_L2_CIPHER_PG_SZ;
+ engine->hash_pg_sz = SPACC_CRYPTO_L2_HASH_PG_SZ;
+ engine->fifo_sz = SPACC_CRYPTO_L2_FIFO_SZ;
+ engine->algs = l2_engine_algs;
+ engine->num_algs = ARRAY_SIZE(l2_engine_algs);
+ } else {
+ return -EINVAL;
+ }
+
+ engine->name = dev_name(&pdev->dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -1711,7 +1747,7 @@ static int __devinit spacc_probe(struct platform_device *pdev,
spin_lock_init(&engine->hw_lock);
- engine->clk = clk_get(&pdev->dev, NULL);
+ engine->clk = clk_get(&pdev->dev, "ref");
if (IS_ERR(engine->clk)) {
dev_info(&pdev->dev, "clk unavailable\n");
device_remove_file(&pdev->dev, &dev_attr_stat_irq_thresh);
@@ -1800,72 +1836,33 @@ static int __devexit spacc_remove(struct platform_device *pdev)
return 0;
}
-static int __devinit ipsec_probe(struct platform_device *pdev)
-{
- return spacc_probe(pdev, SPACC_CRYPTO_IPSEC_MAX_CTXS,
- SPACC_CRYPTO_IPSEC_CIPHER_PG_SZ,
- SPACC_CRYPTO_IPSEC_HASH_PG_SZ,
- SPACC_CRYPTO_IPSEC_FIFO_SZ, ipsec_engine_algs,
- ARRAY_SIZE(ipsec_engine_algs));
-}
-
-static struct platform_driver ipsec_driver = {
- .probe = ipsec_probe,
- .remove = __devexit_p(spacc_remove),
- .driver = {
- .name = "picoxcell-ipsec",
-#ifdef CONFIG_PM
- .pm = &spacc_pm_ops,
-#endif /* CONFIG_PM */
- },
+static const struct platform_device_id spacc_id_table[] = {
+ { "picochip,spacc-ipsec", },
+ { "picochip,spacc-l2", },
};
-static int __devinit l2_probe(struct platform_device *pdev)
-{
- return spacc_probe(pdev, SPACC_CRYPTO_L2_MAX_CTXS,
- SPACC_CRYPTO_L2_CIPHER_PG_SZ,
- SPACC_CRYPTO_L2_HASH_PG_SZ, SPACC_CRYPTO_L2_FIFO_SZ,
- l2_engine_algs, ARRAY_SIZE(l2_engine_algs));
-}
-
-static struct platform_driver l2_driver = {
- .probe = l2_probe,
+static struct platform_driver spacc_driver = {
+ .probe = spacc_probe,
.remove = __devexit_p(spacc_remove),
.driver = {
- .name = "picoxcell-l2",
+ .name = "picochip,spacc",
#ifdef CONFIG_PM
.pm = &spacc_pm_ops,
#endif /* CONFIG_PM */
+ .of_match_table = spacc_of_id_table,
},
+ .id_table = spacc_id_table,
};
static int __init spacc_init(void)
{
- int ret = platform_driver_register(&ipsec_driver);
- if (ret) {
- pr_err("failed to register ipsec spacc driver");
- goto out;
- }
-
- ret = platform_driver_register(&l2_driver);
- if (ret) {
- pr_err("failed to register l2 spacc driver");
- goto l2_failed;
- }
-
- return 0;
-
-l2_failed:
- platform_driver_unregister(&ipsec_driver);
-out:
- return ret;
+ return platform_driver_register(&spacc_driver);
}
module_init(spacc_init);
static void __exit spacc_exit(void)
{
- platform_driver_unregister(&ipsec_driver);
- platform_driver_unregister(&l2_driver);
+ platform_driver_unregister(&spacc_driver);
}
module_exit(spacc_exit);
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 854e2632f9a6..dbe76b5df9cf 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -1,7 +1,7 @@
/*
* talitos - Freescale Integrated Security Engine (SEC) device driver
*
- * Copyright (c) 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright (c) 2008-2011 Freescale Semiconductor, Inc.
*
* Scatterlist Crypto API glue code copied from files with the following:
* Copyright (c) 2006-2007 Herbert Xu <herbert@gondor.apana.org.au>
@@ -282,6 +282,7 @@ static int init_device(struct device *dev)
/**
* talitos_submit - submits a descriptor to the device for processing
* @dev: the SEC device to be used
+ * @ch: the SEC device channel to be used
* @desc: the descriptor to be processed by the device
* @callback: whom to call when processing is complete
* @context: a handle for use by caller (optional)
@@ -290,7 +291,7 @@ static int init_device(struct device *dev)
* callback must check err and feedback in descriptor header
* for device processing status.
*/
-static int talitos_submit(struct device *dev, struct talitos_desc *desc,
+static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
void (*callback)(struct device *dev,
struct talitos_desc *desc,
void *context, int error),
@@ -298,15 +299,9 @@ static int talitos_submit(struct device *dev, struct talitos_desc *desc,
{
struct talitos_private *priv = dev_get_drvdata(dev);
struct talitos_request *request;
- unsigned long flags, ch;
+ unsigned long flags;
int head;
- /* select done notification */
- desc->hdr |= DESC_HDR_DONE_NOTIFY;
-
- /* emulate SEC's round-robin channel fifo polling scheme */
- ch = atomic_inc_return(&priv->last_chan) & (priv->num_channels - 1);
-
spin_lock_irqsave(&priv->chan[ch].head_lock, flags);
if (!atomic_inc_not_zero(&priv->chan[ch].submit_count)) {
@@ -421,7 +416,7 @@ static void talitos_done(unsigned long data)
/*
* locate current (offending) descriptor
*/
-static struct talitos_desc *current_desc(struct device *dev, int ch)
+static u32 current_desc_hdr(struct device *dev, int ch)
{
struct talitos_private *priv = dev_get_drvdata(dev);
int tail = priv->chan[ch].tail;
@@ -433,23 +428,25 @@ static struct talitos_desc *current_desc(struct device *dev, int ch)
tail = (tail + 1) & (priv->fifo_len - 1);
if (tail == priv->chan[ch].tail) {
dev_err(dev, "couldn't locate current descriptor\n");
- return NULL;
+ return 0;
}
}
- return priv->chan[ch].fifo[tail].desc;
+ return priv->chan[ch].fifo[tail].desc->hdr;
}
/*
* user diagnostics; report root cause of error based on execution unit status
*/
-static void report_eu_error(struct device *dev, int ch,
- struct talitos_desc *desc)
+static void report_eu_error(struct device *dev, int ch, u32 desc_hdr)
{
struct talitos_private *priv = dev_get_drvdata(dev);
int i;
- switch (desc->hdr & DESC_HDR_SEL0_MASK) {
+ if (!desc_hdr)
+ desc_hdr = in_be32(priv->reg + TALITOS_DESCBUF(ch));
+
+ switch (desc_hdr & DESC_HDR_SEL0_MASK) {
case DESC_HDR_SEL0_AFEU:
dev_err(dev, "AFEUISR 0x%08x_%08x\n",
in_be32(priv->reg + TALITOS_AFEUISR),
@@ -493,7 +490,7 @@ static void report_eu_error(struct device *dev, int ch,
break;
}
- switch (desc->hdr & DESC_HDR_SEL1_MASK) {
+ switch (desc_hdr & DESC_HDR_SEL1_MASK) {
case DESC_HDR_SEL1_MDEUA:
case DESC_HDR_SEL1_MDEUB:
dev_err(dev, "MDEUISR 0x%08x_%08x\n",
@@ -555,7 +552,7 @@ static void talitos_error(unsigned long data, u32 isr, u32 isr_lo)
if (v_lo & TALITOS_CCPSR_LO_IEU)
dev_err(dev, "invalid execution unit error\n");
if (v_lo & TALITOS_CCPSR_LO_EU)
- report_eu_error(dev, ch, current_desc(dev, ch));
+ report_eu_error(dev, ch, current_desc_hdr(dev, ch));
if (v_lo & TALITOS_CCPSR_LO_GB)
dev_err(dev, "gather boundary error\n");
if (v_lo & TALITOS_CCPSR_LO_GRL)
@@ -706,6 +703,7 @@ static void talitos_unregister_rng(struct device *dev)
struct talitos_ctx {
struct device *dev;
+ int ch;
__be32 desc_hdr_template;
u8 key[TALITOS_MAX_KEY_SIZE];
u8 iv[TALITOS_MAX_IV_LENGTH];
@@ -1117,7 +1115,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv, 0,
DMA_FROM_DEVICE);
- ret = talitos_submit(dev, desc, callback, areq);
+ ret = talitos_submit(dev, ctx->ch, desc, callback, areq);
if (ret != -EINPROGRESS) {
ipsec_esp_unmap(dev, edesc, areq);
kfree(edesc);
@@ -1382,22 +1380,11 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *cipher,
const u8 *key, unsigned int keylen)
{
struct talitos_ctx *ctx = crypto_ablkcipher_ctx(cipher);
- struct ablkcipher_alg *alg = crypto_ablkcipher_alg(cipher);
-
- if (keylen > TALITOS_MAX_KEY_SIZE)
- goto badkey;
-
- if (keylen < alg->min_keysize || keylen > alg->max_keysize)
- goto badkey;
memcpy(&ctx->key, key, keylen);
ctx->keylen = keylen;
return 0;
-
-badkey:
- crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
- return -EINVAL;
}
static void common_nonsnoop_unmap(struct device *dev,
@@ -1433,7 +1420,6 @@ static void ablkcipher_done(struct device *dev,
static int common_nonsnoop(struct talitos_edesc *edesc,
struct ablkcipher_request *areq,
- u8 *giv,
void (*callback) (struct device *dev,
struct talitos_desc *desc,
void *context, int error))
@@ -1453,7 +1439,7 @@ static int common_nonsnoop(struct talitos_edesc *edesc,
/* cipher iv */
ivsize = crypto_ablkcipher_ivsize(cipher);
- map_single_talitos_ptr(dev, &desc->ptr[1], ivsize, giv ?: areq->info, 0,
+ map_single_talitos_ptr(dev, &desc->ptr[1], ivsize, areq->info, 0,
DMA_TO_DEVICE);
/* cipher key */
@@ -1524,7 +1510,7 @@ static int common_nonsnoop(struct talitos_edesc *edesc,
to_talitos_ptr(&desc->ptr[6], 0);
desc->ptr[6].j_extent = 0;
- ret = talitos_submit(dev, desc, callback, areq);
+ ret = talitos_submit(dev, ctx->ch, desc, callback, areq);
if (ret != -EINPROGRESS) {
common_nonsnoop_unmap(dev, edesc, areq);
kfree(edesc);
@@ -1556,7 +1542,7 @@ static int ablkcipher_encrypt(struct ablkcipher_request *areq)
/* set encrypt */
edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_MODE0_ENCRYPT;
- return common_nonsnoop(edesc, areq, NULL, ablkcipher_done);
+ return common_nonsnoop(edesc, areq, ablkcipher_done);
}
static int ablkcipher_decrypt(struct ablkcipher_request *areq)
@@ -1572,7 +1558,7 @@ static int ablkcipher_decrypt(struct ablkcipher_request *areq)
edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
- return common_nonsnoop(edesc, areq, NULL, ablkcipher_done);
+ return common_nonsnoop(edesc, areq, ablkcipher_done);
}
static void common_nonsnoop_hash_unmap(struct device *dev,
@@ -1703,7 +1689,7 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
/* last DWORD empty */
desc->ptr[6] = zero_entry;
- ret = talitos_submit(dev, desc, callback, areq);
+ ret = talitos_submit(dev, ctx->ch, desc, callback, areq);
if (ret != -EINPROGRESS) {
common_nonsnoop_hash_unmap(dev, edesc, areq);
kfree(edesc);
@@ -2244,6 +2230,7 @@ static int talitos_cra_init(struct crypto_tfm *tfm)
struct crypto_alg *alg = tfm->__crt_alg;
struct talitos_crypto_alg *talitos_alg;
struct talitos_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct talitos_private *priv;
if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_AHASH)
talitos_alg = container_of(__crypto_ahash_alg(alg),
@@ -2256,9 +2243,17 @@ static int talitos_cra_init(struct crypto_tfm *tfm)
/* update context with ptr to dev */
ctx->dev = talitos_alg->dev;
+ /* assign SEC channel to tfm in round-robin fashion */
+ priv = dev_get_drvdata(ctx->dev);
+ ctx->ch = atomic_inc_return(&priv->last_chan) &
+ (priv->num_channels - 1);
+
/* copy descriptor header template value */
ctx->desc_hdr_template = talitos_alg->algt.desc_hdr_template;
+ /* select done notification */
+ ctx->desc_hdr_template |= DESC_HDR_DONE_NOTIFY;
+
return 0;
}
diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c
index 4abd089a094f..bc6f5faa1e9e 100644
--- a/drivers/dca/dca-core.c
+++ b/drivers/dca/dca-core.c
@@ -28,6 +28,7 @@
#include <linux/device.h>
#include <linux/dca.h>
#include <linux/slab.h>
+#include <linux/module.h>
#define DCA_VERSION "1.12.1"
@@ -35,7 +36,7 @@ MODULE_VERSION(DCA_VERSION);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Intel Corporation");
-static DEFINE_SPINLOCK(dca_lock);
+static DEFINE_RAW_SPINLOCK(dca_lock);
static LIST_HEAD(dca_domains);
@@ -101,10 +102,10 @@ static void unregister_dca_providers(void)
INIT_LIST_HEAD(&unregistered_providers);
- spin_lock_irqsave(&dca_lock, flags);
+ raw_spin_lock_irqsave(&dca_lock, flags);
if (list_empty(&dca_domains)) {
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
return;
}
@@ -116,7 +117,7 @@ static void unregister_dca_providers(void)
dca_free_domain(domain);
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
list_for_each_entry_safe(dca, _dca, &unregistered_providers, node) {
dca_sysfs_remove_provider(dca);
@@ -144,13 +145,8 @@ static struct dca_domain *dca_get_domain(struct device *dev)
domain = dca_find_domain(rc);
if (!domain) {
- if (dca_provider_ioat_ver_3_0(dev) && !list_empty(&dca_domains)) {
+ if (dca_provider_ioat_ver_3_0(dev) && !list_empty(&dca_domains))
dca_providers_blocked = 1;
- } else {
- domain = dca_allocate_domain(rc);
- if (domain)
- list_add(&domain->node, &dca_domains);
- }
}
return domain;
@@ -198,19 +194,19 @@ int dca_add_requester(struct device *dev)
if (!dev)
return -EFAULT;
- spin_lock_irqsave(&dca_lock, flags);
+ raw_spin_lock_irqsave(&dca_lock, flags);
/* check if the requester has not been added already */
dca = dca_find_provider_by_dev(dev);
if (dca) {
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
return -EEXIST;
}
pci_rc = dca_pci_rc_from_dev(dev);
domain = dca_find_domain(pci_rc);
if (!domain) {
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
return -ENODEV;
}
@@ -220,17 +216,17 @@ int dca_add_requester(struct device *dev)
break;
}
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
if (slot < 0)
return slot;
err = dca_sysfs_add_req(dca, dev, slot);
if (err) {
- spin_lock_irqsave(&dca_lock, flags);
+ raw_spin_lock_irqsave(&dca_lock, flags);
if (dca == dca_find_provider_by_dev(dev))
dca->ops->remove_requester(dca, dev);
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
return err;
}
@@ -251,14 +247,14 @@ int dca_remove_requester(struct device *dev)
if (!dev)
return -EFAULT;
- spin_lock_irqsave(&dca_lock, flags);
+ raw_spin_lock_irqsave(&dca_lock, flags);
dca = dca_find_provider_by_dev(dev);
if (!dca) {
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
return -ENODEV;
}
slot = dca->ops->remove_requester(dca, dev);
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
if (slot < 0)
return slot;
@@ -280,16 +276,16 @@ u8 dca_common_get_tag(struct device *dev, int cpu)
u8 tag;
unsigned long flags;
- spin_lock_irqsave(&dca_lock, flags);
+ raw_spin_lock_irqsave(&dca_lock, flags);
dca = dca_find_provider_by_dev(dev);
if (!dca) {
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
return -ENODEV;
}
tag = dca->ops->get_tag(dca, dev, cpu);
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
return tag;
}
@@ -360,36 +356,51 @@ int register_dca_provider(struct dca_provider *dca, struct device *dev)
{
int err;
unsigned long flags;
- struct dca_domain *domain;
+ struct dca_domain *domain, *newdomain = NULL;
- spin_lock_irqsave(&dca_lock, flags);
+ raw_spin_lock_irqsave(&dca_lock, flags);
if (dca_providers_blocked) {
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
return -ENODEV;
}
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
err = dca_sysfs_add_provider(dca, dev);
if (err)
return err;
- spin_lock_irqsave(&dca_lock, flags);
+ raw_spin_lock_irqsave(&dca_lock, flags);
domain = dca_get_domain(dev);
if (!domain) {
+ struct pci_bus *rc;
+
if (dca_providers_blocked) {
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
dca_sysfs_remove_provider(dca);
unregister_dca_providers();
- } else {
- spin_unlock_irqrestore(&dca_lock, flags);
+ return -ENODEV;
+ }
+
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
+ rc = dca_pci_rc_from_dev(dev);
+ newdomain = dca_allocate_domain(rc);
+ if (!newdomain)
+ return -ENODEV;
+ raw_spin_lock_irqsave(&dca_lock, flags);
+ /* Recheck, we might have raced after dropping the lock */
+ domain = dca_get_domain(dev);
+ if (!domain) {
+ domain = newdomain;
+ newdomain = NULL;
+ list_add(&domain->node, &dca_domains);
}
- return -ENODEV;
}
list_add(&dca->node, &domain->dca_providers);
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
blocking_notifier_call_chain(&dca_provider_chain,
DCA_PROVIDER_ADD, NULL);
+ kfree(newdomain);
return 0;
}
EXPORT_SYMBOL_GPL(register_dca_provider);
@@ -407,7 +418,7 @@ void unregister_dca_provider(struct dca_provider *dca, struct device *dev)
blocking_notifier_call_chain(&dca_provider_chain,
DCA_PROVIDER_REMOVE, NULL);
- spin_lock_irqsave(&dca_lock, flags);
+ raw_spin_lock_irqsave(&dca_lock, flags);
list_del(&dca->node);
@@ -416,7 +427,7 @@ void unregister_dca_provider(struct dca_provider *dca, struct device *dev)
if (list_empty(&domain->dca_providers))
dca_free_domain(domain);
- spin_unlock_irqrestore(&dca_lock, flags);
+ raw_spin_unlock_irqrestore(&dca_lock, flags);
dca_sysfs_remove_provider(dca);
}
diff --git a/drivers/dca/dca-sysfs.c b/drivers/dca/dca-sysfs.c
index 5e8f335e6f6e..591b6597c00a 100644
--- a/drivers/dca/dca-sysfs.c
+++ b/drivers/dca/dca-sysfs.c
@@ -27,6 +27,7 @@
#include <linux/err.h>
#include <linux/dca.h>
#include <linux/gfp.h>
+#include <linux/export.h>
static struct class *dca_class;
static struct idr dca_idr;
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
new file mode 100644
index 000000000000..8f0491037080
--- /dev/null
+++ b/drivers/devfreq/Kconfig
@@ -0,0 +1,68 @@
+menuconfig PM_DEVFREQ
+ bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support"
+ help
+ A device may have a list of frequencies and voltages available.
+ devfreq, a generic DVFS framework can be registered for a device
+ in order to let the governor provided to devfreq choose an
+ operating frequency based on the device driver's policy.
+
+ Each device may have its own governor and policy. Devfreq can
+ reevaluate the device state periodically and/or based on the
+ notification to "nb", a notifier block, of devfreq.
+
+ Like some CPUs with CPUfreq, a device may have multiple clocks.
+ However, because the clock frequencies of a single device are
+ determined by the single device's state, an instance of devfreq
+ is attached to a single device and returns a "representative"
+ clock frequency of the device, which is also attached
+ to a device by 1-to-1. The device registering devfreq takes the
+ responsiblity to "interpret" the representative frequency and
+ to set its every clock accordingly with the "target" callback
+ given to devfreq.
+
+ When OPP is used with the devfreq device, it is recommended to
+ register devfreq's nb to the OPP's notifier head. If OPP is
+ used with the devfreq device, you may use OPP helper
+ functions defined in devfreq.h.
+
+if PM_DEVFREQ
+
+comment "DEVFREQ Governors"
+
+config DEVFREQ_GOV_SIMPLE_ONDEMAND
+ bool "Simple Ondemand"
+ help
+ Chooses frequency based on the recent load on the device. Works
+ similar as ONDEMAND governor of CPUFREQ does. A device with
+ Simple-Ondemand should be able to provide busy/total counter
+ values that imply the usage rate. A device may provide tuned
+ values to the governor with data field at devfreq_add_device().
+
+config DEVFREQ_GOV_PERFORMANCE
+ bool "Performance"
+ help
+ Sets the frequency at the maximum available frequency.
+ This governor always returns UINT_MAX as frequency so that
+ the DEVFREQ framework returns the highest frequency available
+ at any time.
+
+config DEVFREQ_GOV_POWERSAVE
+ bool "Powersave"
+ help
+ Sets the frequency at the minimum available frequency.
+ This governor always returns 0 as frequency so that
+ the DEVFREQ framework returns the lowest frequency available
+ at any time.
+
+config DEVFREQ_GOV_USERSPACE
+ bool "Userspace"
+ help
+ Sets the frequency at the user specified one.
+ This governor returns the user configured frequency if there
+ has been an input to /sys/devices/.../power/devfreq_set_freq.
+ Otherwise, the governor does not change the frequnecy
+ given at the initialization.
+
+comment "DEVFREQ Drivers"
+
+endif # PM_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
new file mode 100644
index 000000000000..4564a89e970a
--- /dev/null
+++ b/drivers/devfreq/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_PM_DEVFREQ) += devfreq.o
+obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o
+obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o
+obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
+obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
new file mode 100644
index 000000000000..59d24e9cb8c5
--- /dev/null
+++ b/drivers/devfreq/devfreq.c
@@ -0,0 +1,607 @@
+/*
+ * devfreq: Generic Dynamic Voltage and Frequency Scaling (DVFS) Framework
+ * for Non-CPU Devices.
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/opp.h>
+#include <linux/devfreq.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/printk.h>
+#include <linux/hrtimer.h>
+#include "governor.h"
+
+struct class *devfreq_class;
+
+/*
+ * devfreq_work periodically monitors every registered device.
+ * The minimum polling interval is one jiffy. The polling interval is
+ * determined by the minimum polling period among all polling devfreq
+ * devices. The resolution of polling interval is one jiffy.
+ */
+static bool polling;
+static struct workqueue_struct *devfreq_wq;
+static struct delayed_work devfreq_work;
+
+/* wait removing if this is to be removed */
+static struct devfreq *wait_remove_device;
+
+/* The list of all device-devfreq */
+static LIST_HEAD(devfreq_list);
+static DEFINE_MUTEX(devfreq_list_lock);
+
+/**
+ * find_device_devfreq() - find devfreq struct using device pointer
+ * @dev: device pointer used to lookup device devfreq.
+ *
+ * Search the list of device devfreqs and return the matched device's
+ * devfreq info. devfreq_list_lock should be held by the caller.
+ */
+static struct devfreq *find_device_devfreq(struct device *dev)
+{
+ struct devfreq *tmp_devfreq;
+
+ if (unlikely(IS_ERR_OR_NULL(dev))) {
+ pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+ WARN(!mutex_is_locked(&devfreq_list_lock),
+ "devfreq_list_lock must be locked.");
+
+ list_for_each_entry(tmp_devfreq, &devfreq_list, node) {
+ if (tmp_devfreq->dev.parent == dev)
+ return tmp_devfreq;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+
+/**
+ * update_devfreq() - Reevaluate the device and configure frequency.
+ * @devfreq: the devfreq instance.
+ *
+ * Note: Lock devfreq->lock before calling update_devfreq
+ * This function is exported for governors.
+ */
+int update_devfreq(struct devfreq *devfreq)
+{
+ unsigned long freq;
+ int err = 0;
+
+ if (!mutex_is_locked(&devfreq->lock)) {
+ WARN(true, "devfreq->lock must be locked by the caller.\n");
+ return -EINVAL;
+ }
+
+ /* Reevaluate the proper frequency */
+ err = devfreq->governor->get_target_freq(devfreq, &freq);
+ if (err)
+ return err;
+
+ err = devfreq->profile->target(devfreq->dev.parent, &freq);
+ if (err)
+ return err;
+
+ devfreq->previous_freq = freq;
+ return err;
+}
+
+/**
+ * devfreq_notifier_call() - Notify that the device frequency requirements
+ * has been changed out of devfreq framework.
+ * @nb the notifier_block (supposed to be devfreq->nb)
+ * @type not used
+ * @devp not used
+ *
+ * Called by a notifier that uses devfreq->nb.
+ */
+static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
+ void *devp)
+{
+ struct devfreq *devfreq = container_of(nb, struct devfreq, nb);
+ int ret;
+
+ mutex_lock(&devfreq->lock);
+ ret = update_devfreq(devfreq);
+ mutex_unlock(&devfreq->lock);
+
+ return ret;
+}
+
+/**
+ * _remove_devfreq() - Remove devfreq from the device.
+ * @devfreq: the devfreq struct
+ * @skip: skip calling device_unregister().
+ *
+ * Note that the caller should lock devfreq->lock before calling
+ * this. _remove_devfreq() will unlock it and free devfreq
+ * internally. devfreq_list_lock should be locked by the caller
+ * as well (not relased at return)
+ *
+ * Lock usage:
+ * devfreq->lock: locked before call.
+ * unlocked at return (and freed)
+ * devfreq_list_lock: locked before call.
+ * kept locked at return.
+ * if devfreq is centrally polled.
+ *
+ * Freed memory:
+ * devfreq
+ */
+static void _remove_devfreq(struct devfreq *devfreq, bool skip)
+{
+ if (!mutex_is_locked(&devfreq->lock)) {
+ WARN(true, "devfreq->lock must be locked by the caller.\n");
+ return;
+ }
+ if (!devfreq->governor->no_central_polling &&
+ !mutex_is_locked(&devfreq_list_lock)) {
+ WARN(true, "devfreq_list_lock must be locked by the caller.\n");
+ return;
+ }
+
+ if (devfreq->being_removed)
+ return;
+
+ devfreq->being_removed = true;
+
+ if (devfreq->profile->exit)
+ devfreq->profile->exit(devfreq->dev.parent);
+
+ if (devfreq->governor->exit)
+ devfreq->governor->exit(devfreq);
+
+ if (!skip && get_device(&devfreq->dev)) {
+ device_unregister(&devfreq->dev);
+ put_device(&devfreq->dev);
+ }
+
+ if (!devfreq->governor->no_central_polling)
+ list_del(&devfreq->node);
+
+ mutex_unlock(&devfreq->lock);
+ mutex_destroy(&devfreq->lock);
+
+ kfree(devfreq);
+}
+
+/**
+ * devfreq_dev_release() - Callback for struct device to release the device.
+ * @dev: the devfreq device
+ *
+ * This calls _remove_devfreq() if _remove_devfreq() is not called.
+ * Note that devfreq_dev_release() could be called by _remove_devfreq() as
+ * well as by others unregistering the device.
+ */
+static void devfreq_dev_release(struct device *dev)
+{
+ struct devfreq *devfreq = to_devfreq(dev);
+ bool central_polling = !devfreq->governor->no_central_polling;
+
+ /*
+ * If devfreq_dev_release() was called by device_unregister() of
+ * _remove_devfreq(), we cannot mutex_lock(&devfreq->lock) and
+ * being_removed is already set. This also partially checks the case
+ * where devfreq_dev_release() is called from a thread other than
+ * the one called _remove_devfreq(); however, this case is
+ * dealt completely with another following being_removed check.
+ *
+ * Because being_removed is never being
+ * unset, we do not need to worry about race conditions on
+ * being_removed.
+ */
+ if (devfreq->being_removed)
+ return;
+
+ if (central_polling)
+ mutex_lock(&devfreq_list_lock);
+
+ mutex_lock(&devfreq->lock);
+
+ /*
+ * Check being_removed flag again for the case where
+ * devfreq_dev_release() was called in a thread other than the one
+ * possibly called _remove_devfreq().
+ */
+ if (devfreq->being_removed) {
+ mutex_unlock(&devfreq->lock);
+ goto out;
+ }
+
+ /* devfreq->lock is unlocked and removed in _removed_devfreq() */
+ _remove_devfreq(devfreq, true);
+
+out:
+ if (central_polling)
+ mutex_unlock(&devfreq_list_lock);
+}
+
+/**
+ * devfreq_monitor() - Periodically poll devfreq objects.
+ * @work: the work struct used to run devfreq_monitor periodically.
+ *
+ */
+static void devfreq_monitor(struct work_struct *work)
+{
+ static unsigned long last_polled_at;
+ struct devfreq *devfreq, *tmp;
+ int error;
+ unsigned long jiffies_passed;
+ unsigned long next_jiffies = ULONG_MAX, now = jiffies;
+ struct device *dev;
+
+ /* Initially last_polled_at = 0, polling every device at bootup */
+ jiffies_passed = now - last_polled_at;
+ last_polled_at = now;
+ if (jiffies_passed == 0)
+ jiffies_passed = 1;
+
+ mutex_lock(&devfreq_list_lock);
+ list_for_each_entry_safe(devfreq, tmp, &devfreq_list, node) {
+ mutex_lock(&devfreq->lock);
+ dev = devfreq->dev.parent;
+
+ /* Do not remove tmp for a while */
+ wait_remove_device = tmp;
+
+ if (devfreq->governor->no_central_polling ||
+ devfreq->next_polling == 0) {
+ mutex_unlock(&devfreq->lock);
+ continue;
+ }
+ mutex_unlock(&devfreq_list_lock);
+
+ /*
+ * Reduce more next_polling if devfreq_wq took an extra
+ * delay. (i.e., CPU has been idled.)
+ */
+ if (devfreq->next_polling <= jiffies_passed) {
+ error = update_devfreq(devfreq);
+
+ /* Remove a devfreq with an error. */
+ if (error && error != -EAGAIN) {
+
+ dev_err(dev, "Due to update_devfreq error(%d), devfreq(%s) is removed from the device\n",
+ error, devfreq->governor->name);
+
+ /*
+ * Unlock devfreq before locking the list
+ * in order to avoid deadlock with
+ * find_device_devfreq or others
+ */
+ mutex_unlock(&devfreq->lock);
+ mutex_lock(&devfreq_list_lock);
+ /* Check if devfreq is already removed */
+ if (IS_ERR(find_device_devfreq(dev)))
+ continue;
+ mutex_lock(&devfreq->lock);
+ /* This unlocks devfreq->lock and free it */
+ _remove_devfreq(devfreq, false);
+ continue;
+ }
+ devfreq->next_polling = devfreq->polling_jiffies;
+ } else {
+ devfreq->next_polling -= jiffies_passed;
+ }
+
+ if (devfreq->next_polling)
+ next_jiffies = (next_jiffies > devfreq->next_polling) ?
+ devfreq->next_polling : next_jiffies;
+
+ mutex_unlock(&devfreq->lock);
+ mutex_lock(&devfreq_list_lock);
+ }
+ wait_remove_device = NULL;
+ mutex_unlock(&devfreq_list_lock);
+
+ if (next_jiffies > 0 && next_jiffies < ULONG_MAX) {
+ polling = true;
+ queue_delayed_work(devfreq_wq, &devfreq_work, next_jiffies);
+ } else {
+ polling = false;
+ }
+}
+
+/**
+ * devfreq_add_device() - Add devfreq feature to the device
+ * @dev: the device to add devfreq feature.
+ * @profile: device-specific profile to run devfreq.
+ * @governor: the policy to choose frequency.
+ * @data: private data for the governor. The devfreq framework does not
+ * touch this value.
+ */
+struct devfreq *devfreq_add_device(struct device *dev,
+ struct devfreq_dev_profile *profile,
+ const struct devfreq_governor *governor,
+ void *data)
+{
+ struct devfreq *devfreq;
+ int err = 0;
+
+ if (!dev || !profile || !governor) {
+ dev_err(dev, "%s: Invalid parameters.\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+
+ if (!governor->no_central_polling) {
+ mutex_lock(&devfreq_list_lock);
+ devfreq = find_device_devfreq(dev);
+ mutex_unlock(&devfreq_list_lock);
+ if (!IS_ERR(devfreq)) {
+ dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.\n", __func__);
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL);
+ if (!devfreq) {
+ dev_err(dev, "%s: Unable to create devfreq for the device\n",
+ __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ mutex_init(&devfreq->lock);
+ mutex_lock(&devfreq->lock);
+ devfreq->dev.parent = dev;
+ devfreq->dev.class = devfreq_class;
+ devfreq->dev.release = devfreq_dev_release;
+ devfreq->profile = profile;
+ devfreq->governor = governor;
+ devfreq->previous_freq = profile->initial_freq;
+ devfreq->data = data;
+ devfreq->next_polling = devfreq->polling_jiffies
+ = msecs_to_jiffies(devfreq->profile->polling_ms);
+ devfreq->nb.notifier_call = devfreq_notifier_call;
+
+ dev_set_name(&devfreq->dev, dev_name(dev));
+ err = device_register(&devfreq->dev);
+ if (err) {
+ put_device(&devfreq->dev);
+ goto err_dev;
+ }
+
+ if (governor->init)
+ err = governor->init(devfreq);
+ if (err)
+ goto err_init;
+
+ mutex_unlock(&devfreq->lock);
+
+ if (governor->no_central_polling)
+ goto out;
+
+ mutex_lock(&devfreq_list_lock);
+
+ list_add(&devfreq->node, &devfreq_list);
+
+ if (devfreq_wq && devfreq->next_polling && !polling) {
+ polling = true;
+ queue_delayed_work(devfreq_wq, &devfreq_work,
+ devfreq->next_polling);
+ }
+ mutex_unlock(&devfreq_list_lock);
+ goto out;
+err_init:
+ device_unregister(&devfreq->dev);
+err_dev:
+ mutex_unlock(&devfreq->lock);
+ kfree(devfreq);
+out:
+ if (err)
+ return ERR_PTR(err);
+ else
+ return devfreq;
+}
+
+/**
+ * devfreq_remove_device() - Remove devfreq feature from a device.
+ * @devfreq the devfreq instance to be removed
+ */
+int devfreq_remove_device(struct devfreq *devfreq)
+{
+ bool central_polling;
+
+ if (!devfreq)
+ return -EINVAL;
+
+ central_polling = !devfreq->governor->no_central_polling;
+
+ if (central_polling) {
+ mutex_lock(&devfreq_list_lock);
+ while (wait_remove_device == devfreq) {
+ mutex_unlock(&devfreq_list_lock);
+ schedule();
+ mutex_lock(&devfreq_list_lock);
+ }
+ }
+
+ mutex_lock(&devfreq->lock);
+ _remove_devfreq(devfreq, false); /* it unlocks devfreq->lock */
+
+ if (central_polling)
+ mutex_unlock(&devfreq_list_lock);
+
+ return 0;
+}
+
+static ssize_t show_governor(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_devfreq(dev)->governor->name);
+}
+
+static ssize_t show_freq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%lu\n", to_devfreq(dev)->previous_freq);
+}
+
+static ssize_t show_polling_interval(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", to_devfreq(dev)->profile->polling_ms);
+}
+
+static ssize_t store_polling_interval(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct devfreq *df = to_devfreq(dev);
+ unsigned int value;
+ int ret;
+
+ ret = sscanf(buf, "%u", &value);
+ if (ret != 1)
+ goto out;
+
+ mutex_lock(&df->lock);
+ df->profile->polling_ms = value;
+ df->next_polling = df->polling_jiffies
+ = msecs_to_jiffies(value);
+ mutex_unlock(&df->lock);
+
+ ret = count;
+
+ if (df->governor->no_central_polling)
+ goto out;
+
+ mutex_lock(&devfreq_list_lock);
+ if (df->next_polling > 0 && !polling) {
+ polling = true;
+ queue_delayed_work(devfreq_wq, &devfreq_work,
+ df->next_polling);
+ }
+ mutex_unlock(&devfreq_list_lock);
+out:
+ return ret;
+}
+
+static ssize_t show_central_polling(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n",
+ !to_devfreq(dev)->governor->no_central_polling);
+}
+
+static struct device_attribute devfreq_attrs[] = {
+ __ATTR(governor, S_IRUGO, show_governor, NULL),
+ __ATTR(cur_freq, S_IRUGO, show_freq, NULL),
+ __ATTR(central_polling, S_IRUGO, show_central_polling, NULL),
+ __ATTR(polling_interval, S_IRUGO | S_IWUSR, show_polling_interval,
+ store_polling_interval),
+ { },
+};
+
+/**
+ * devfreq_start_polling() - Initialize data structure for devfreq framework and
+ * start polling registered devfreq devices.
+ */
+static int __init devfreq_start_polling(void)
+{
+ mutex_lock(&devfreq_list_lock);
+ polling = false;
+ devfreq_wq = create_freezable_workqueue("devfreq_wq");
+ INIT_DELAYED_WORK_DEFERRABLE(&devfreq_work, devfreq_monitor);
+ mutex_unlock(&devfreq_list_lock);
+
+ devfreq_monitor(&devfreq_work.work);
+ return 0;
+}
+late_initcall(devfreq_start_polling);
+
+static int __init devfreq_init(void)
+{
+ devfreq_class = class_create(THIS_MODULE, "devfreq");
+ if (IS_ERR(devfreq_class)) {
+ pr_err("%s: couldn't create class\n", __FILE__);
+ return PTR_ERR(devfreq_class);
+ }
+ devfreq_class->dev_attrs = devfreq_attrs;
+ return 0;
+}
+subsys_initcall(devfreq_init);
+
+static void __exit devfreq_exit(void)
+{
+ class_destroy(devfreq_class);
+}
+module_exit(devfreq_exit);
+
+/*
+ * The followings are helper functions for devfreq user device drivers with
+ * OPP framework.
+ */
+
+/**
+ * devfreq_recommended_opp() - Helper function to get proper OPP for the
+ * freq value given to target callback.
+ * @dev The devfreq user device. (parent of devfreq)
+ * @freq The frequency given to target function
+ *
+ */
+struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq)
+{
+ struct opp *opp = opp_find_freq_ceil(dev, freq);
+
+ if (opp == ERR_PTR(-ENODEV))
+ opp = opp_find_freq_floor(dev, freq);
+ return opp;
+}
+
+/**
+ * devfreq_register_opp_notifier() - Helper function to get devfreq notified
+ * for any changes in the OPP availability
+ * changes
+ * @dev The devfreq user device. (parent of devfreq)
+ * @devfreq The devfreq object.
+ */
+int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)
+{
+ struct srcu_notifier_head *nh = opp_get_notifier(dev);
+
+ if (IS_ERR(nh))
+ return PTR_ERR(nh);
+ return srcu_notifier_chain_register(nh, &devfreq->nb);
+}
+
+/**
+ * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq
+ * notified for any changes in the OPP
+ * availability changes anymore.
+ * @dev The devfreq user device. (parent of devfreq)
+ * @devfreq The devfreq object.
+ *
+ * At exit() callback of devfreq_dev_profile, this must be included if
+ * devfreq_recommended_opp is used.
+ */
+int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
+{
+ struct srcu_notifier_head *nh = opp_get_notifier(dev);
+
+ if (IS_ERR(nh))
+ return PTR_ERR(nh);
+ return srcu_notifier_chain_unregister(nh, &devfreq->nb);
+}
+
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_DESCRIPTION("devfreq class support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h
new file mode 100644
index 000000000000..ea7f13c58ded
--- /dev/null
+++ b/drivers/devfreq/governor.h
@@ -0,0 +1,24 @@
+/*
+ * governor.h - internal header for devfreq governors.
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This header is for devfreq governors in drivers/devfreq/
+ */
+
+#ifndef _GOVERNOR_H
+#define _GOVERNOR_H
+
+#include <linux/devfreq.h>
+
+#define to_devfreq(DEV) container_of((DEV), struct devfreq, dev)
+
+/* Caution: devfreq->lock must be locked before calling update_devfreq */
+extern int update_devfreq(struct devfreq *devfreq);
+
+#endif /* _GOVERNOR_H */
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
new file mode 100644
index 000000000000..c0596b291761
--- /dev/null
+++ b/drivers/devfreq/governor_performance.c
@@ -0,0 +1,29 @@
+/*
+ * linux/drivers/devfreq/governor_performance.c
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/devfreq.h>
+
+static int devfreq_performance_func(struct devfreq *df,
+ unsigned long *freq)
+{
+ /*
+ * target callback should be able to get floor value as
+ * said in devfreq.h
+ */
+ *freq = UINT_MAX;
+ return 0;
+}
+
+const struct devfreq_governor devfreq_performance = {
+ .name = "performance",
+ .get_target_freq = devfreq_performance_func,
+ .no_central_polling = true,
+};
diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c
new file mode 100644
index 000000000000..2483a85a266f
--- /dev/null
+++ b/drivers/devfreq/governor_powersave.c
@@ -0,0 +1,29 @@
+/*
+ * linux/drivers/devfreq/governor_powersave.c
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/devfreq.h>
+
+static int devfreq_powersave_func(struct devfreq *df,
+ unsigned long *freq)
+{
+ /*
+ * target callback should be able to get ceiling value as
+ * said in devfreq.h
+ */
+ *freq = 0;
+ return 0;
+}
+
+const struct devfreq_governor devfreq_powersave = {
+ .name = "powersave",
+ .get_target_freq = devfreq_powersave_func,
+ .no_central_polling = true,
+};
diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c
new file mode 100644
index 000000000000..efad8dcf9028
--- /dev/null
+++ b/drivers/devfreq/governor_simpleondemand.c
@@ -0,0 +1,88 @@
+/*
+ * linux/drivers/devfreq/governor_simpleondemand.c
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/devfreq.h>
+#include <linux/math64.h>
+
+/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
+#define DFSO_UPTHRESHOLD (90)
+#define DFSO_DOWNDIFFERENCTIAL (5)
+static int devfreq_simple_ondemand_func(struct devfreq *df,
+ unsigned long *freq)
+{
+ struct devfreq_dev_status stat;
+ int err = df->profile->get_dev_status(df->dev.parent, &stat);
+ unsigned long long a, b;
+ unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
+ unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
+ struct devfreq_simple_ondemand_data *data = df->data;
+
+ if (err)
+ return err;
+
+ if (data) {
+ if (data->upthreshold)
+ dfso_upthreshold = data->upthreshold;
+ if (data->downdifferential)
+ dfso_downdifferential = data->downdifferential;
+ }
+ if (dfso_upthreshold > 100 ||
+ dfso_upthreshold < dfso_downdifferential)
+ return -EINVAL;
+
+ /* Assume MAX if it is going to be divided by zero */
+ if (stat.total_time == 0) {
+ *freq = UINT_MAX;
+ return 0;
+ }
+
+ /* Prevent overflow */
+ if (stat.busy_time >= (1 << 24) || stat.total_time >= (1 << 24)) {
+ stat.busy_time >>= 7;
+ stat.total_time >>= 7;
+ }
+
+ /* Set MAX if it's busy enough */
+ if (stat.busy_time * 100 >
+ stat.total_time * dfso_upthreshold) {
+ *freq = UINT_MAX;
+ return 0;
+ }
+
+ /* Set MAX if we do not know the initial frequency */
+ if (stat.current_frequency == 0) {
+ *freq = UINT_MAX;
+ return 0;
+ }
+
+ /* Keep the current frequency */
+ if (stat.busy_time * 100 >
+ stat.total_time * (dfso_upthreshold - dfso_downdifferential)) {
+ *freq = stat.current_frequency;
+ return 0;
+ }
+
+ /* Set the desired frequency based on the load */
+ a = stat.busy_time;
+ a *= stat.current_frequency;
+ b = div_u64(a, stat.total_time);
+ b *= 100;
+ b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
+ *freq = (unsigned long) b;
+
+ return 0;
+}
+
+const struct devfreq_governor devfreq_simple_ondemand = {
+ .name = "simple_ondemand",
+ .get_target_freq = devfreq_simple_ondemand_func,
+};
diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c
new file mode 100644
index 000000000000..4f8b563da782
--- /dev/null
+++ b/drivers/devfreq/governor_userspace.c
@@ -0,0 +1,116 @@
+/*
+ * linux/drivers/devfreq/governor_simpleondemand.c
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/devfreq.h>
+#include <linux/pm.h>
+#include <linux/mutex.h>
+#include "governor.h"
+
+struct userspace_data {
+ unsigned long user_frequency;
+ bool valid;
+};
+
+static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq)
+{
+ struct userspace_data *data = df->data;
+
+ if (!data->valid)
+ *freq = df->previous_freq; /* No user freq specified yet */
+ else
+ *freq = data->user_frequency;
+ return 0;
+}
+
+static ssize_t store_freq(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct devfreq *devfreq = to_devfreq(dev);
+ struct userspace_data *data;
+ unsigned long wanted;
+ int err = 0;
+
+
+ mutex_lock(&devfreq->lock);
+ data = devfreq->data;
+
+ sscanf(buf, "%lu", &wanted);
+ data->user_frequency = wanted;
+ data->valid = true;
+ err = update_devfreq(devfreq);
+ if (err == 0)
+ err = count;
+ mutex_unlock(&devfreq->lock);
+ return err;
+}
+
+static ssize_t show_freq(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct devfreq *devfreq = to_devfreq(dev);
+ struct userspace_data *data;
+ int err = 0;
+
+ mutex_lock(&devfreq->lock);
+ data = devfreq->data;
+
+ if (data->valid)
+ err = sprintf(buf, "%lu\n", data->user_frequency);
+ else
+ err = sprintf(buf, "undefined\n");
+ mutex_unlock(&devfreq->lock);
+ return err;
+}
+
+static DEVICE_ATTR(set_freq, 0644, show_freq, store_freq);
+static struct attribute *dev_entries[] = {
+ &dev_attr_set_freq.attr,
+ NULL,
+};
+static struct attribute_group dev_attr_group = {
+ .name = "userspace",
+ .attrs = dev_entries,
+};
+
+static int userspace_init(struct devfreq *devfreq)
+{
+ int err = 0;
+ struct userspace_data *data = kzalloc(sizeof(struct userspace_data),
+ GFP_KERNEL);
+
+ if (!data) {
+ err = -ENOMEM;
+ goto out;
+ }
+ data->valid = false;
+ devfreq->data = data;
+
+ err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group);
+out:
+ return err;
+}
+
+static void userspace_exit(struct devfreq *devfreq)
+{
+ sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
+ kfree(devfreq->data);
+ devfreq->data = NULL;
+}
+
+const struct devfreq_governor devfreq_userspace = {
+ .name = "userspace",
+ .get_target_freq = devfreq_userspace_func,
+ .init = userspace_init,
+ .exit = userspace_exit,
+ .no_central_polling = true,
+};
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 25cf327cd1cb..ab8f469f5cf8 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -193,7 +193,8 @@ config ARCH_HAS_ASYNC_TX_FIND_CHANNEL
config PL330_DMA
tristate "DMA API Driver for PL330"
select DMA_ENGINE
- depends on PL330
+ depends on ARM_AMBA
+ select PL330
help
Select if your platform has one or more PL330 DMACs.
You need to provide platform specific settings via
@@ -237,6 +238,13 @@ config MXS_DMA
Support the MXS DMA engine. This engine including APBH-DMA
and APBX-DMA is integrated into Freescale i.MX23/28 chips.
+config EP93XX_DMA
+ bool "Cirrus Logic EP93xx DMA support"
+ depends on ARCH_EP93XX
+ select DMA_ENGINE
+ help
+ Enable support for the Cirrus Logic EP93xx M2P/M2M DMA controller.
+
config DMA_ENGINE
bool
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 836095ab3c5c..30cf3b1f0c5c 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
obj-$(CONFIG_PL330_DMA) += pl330.o
obj-$(CONFIG_PCH_DMA) += pch_dma.o
obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
+obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
diff --git a/drivers/dma/TODO b/drivers/dma/TODO
index a4af8589330c..734ed0206cd5 100644
--- a/drivers/dma/TODO
+++ b/drivers/dma/TODO
@@ -9,6 +9,5 @@ TODO for slave dma
- mxs-dma.c
- dw_dmac
- intel_mid_dma
- - ste_dma40
4. Check other subsystems for dma drivers and merge/move to dmaengine
5. Remove dma_slave_config's dma direction.
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index e6d7228b1479..b7cbd1ab1db1 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -66,31 +66,29 @@
* after the final transfer signalled by LBREQ or LSREQ. The DMAC
* will then move to the next LLI entry.
*
- * Only the former works sanely with scatter lists, so we only implement
- * the DMAC flow control method. However, peripherals which use the LBREQ
- * and LSREQ signals (eg, MMCI) are unable to use this mode, which through
- * these hardware restrictions prevents them from using scatter DMA.
- *
* Global TODO:
* - Break out common code from arch/arm/mach-s3c64xx and share
*/
-#include <linux/device.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/dmapool.h>
-#include <linux/dmaengine.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl08x.h>
#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/seq_file.h>
-
+#include <linux/slab.h>
#include <asm/hardware/pl080.h>
#define DRIVER_NAME "pl08xdmac"
+static struct amba_driver pl08x_amba_driver;
+
/**
* struct vendor_data - vendor-specific config parameters for PL08x derivatives
* @channels: the number of channels available in this variant
@@ -125,7 +123,8 @@ struct pl08x_lli {
* @phy_chans: array of data for the physical channels
* @pool: a pool for the LLI descriptors
* @pool_ctr: counter of LLIs in the pool
- * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI fetches
+ * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI
+ * fetches
* @mem_buses: set to indicate memory transfers on AHB2.
* @lock: a spinlock for this struct
*/
@@ -148,22 +147,10 @@ struct pl08x_driver_data {
* PL08X specific defines
*/
-/*
- * Memory boundaries: the manual for PL08x says that the controller
- * cannot read past a 1KiB boundary, so these defines are used to
- * create transfer LLIs that do not cross such boundaries.
- */
-#define PL08X_BOUNDARY_SHIFT (10) /* 1KB 0x400 */
-#define PL08X_BOUNDARY_SIZE (1 << PL08X_BOUNDARY_SHIFT)
-
-/* Minimum period between work queue runs */
-#define PL08X_WQ_PERIODMIN 20
-
/* Size (bytes) of each LLI buffer allocated for one transfer */
# define PL08X_LLI_TSFR_SIZE 0x2000
/* Maximum times we call dma_pool_alloc on this pool without freeing */
-#define PL08X_MAX_ALLOCS 0x40
#define MAX_NUM_TSFR_LLIS (PL08X_LLI_TSFR_SIZE/sizeof(struct pl08x_lli))
#define PL08X_ALIGN 8
@@ -275,7 +262,6 @@ static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)
writel(val, ch->base + PL080_CH_CONFIG);
}
-
/*
* pl08x_terminate_phy_chan() stops the channel, clears the FIFO and
* clears any pending interrupt status. This should not be used for
@@ -366,7 +352,9 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
if (!list_empty(&plchan->pend_list)) {
struct pl08x_txd *txdi;
list_for_each_entry(txdi, &plchan->pend_list, node) {
- bytes += txdi->len;
+ struct pl08x_sg *dsg;
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ bytes += dsg->len;
}
}
@@ -410,6 +398,7 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
return NULL;
}
+ pm_runtime_get_sync(&pl08x->adev->dev);
return ch;
}
@@ -423,6 +412,8 @@ static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x,
/* Stop the channel and clear its interrupts */
pl08x_terminate_phy_chan(pl08x, ch);
+ pm_runtime_put(&pl08x->adev->dev);
+
/* Mark it as free */
ch->serving = NULL;
spin_unlock_irqrestore(&ch->lock, flags);
@@ -495,43 +486,37 @@ static inline u32 pl08x_cctl_bits(u32 cctl, u8 srcwidth, u8 dstwidth,
struct pl08x_lli_build_data {
struct pl08x_txd *txd;
- struct pl08x_driver_data *pl08x;
struct pl08x_bus_data srcbus;
struct pl08x_bus_data dstbus;
size_t remainder;
+ u32 lli_bus;
};
/*
- * Autoselect a master bus to use for the transfer this prefers the
- * destination bus if both available if fixed address on one bus the
- * other will be chosen
+ * Autoselect a master bus to use for the transfer. Slave will be the chosen as
+ * victim in case src & dest are not similarly aligned. i.e. If after aligning
+ * masters address with width requirements of transfer (by sending few byte by
+ * byte data), slave is still not aligned, then its width will be reduced to
+ * BYTE.
+ * - prefers the destination bus if both available
+ * - prefers bus with fixed address (i.e. peripheral)
*/
static void pl08x_choose_master_bus(struct pl08x_lli_build_data *bd,
struct pl08x_bus_data **mbus, struct pl08x_bus_data **sbus, u32 cctl)
{
if (!(cctl & PL080_CONTROL_DST_INCR)) {
- *mbus = &bd->srcbus;
- *sbus = &bd->dstbus;
- } else if (!(cctl & PL080_CONTROL_SRC_INCR)) {
*mbus = &bd->dstbus;
*sbus = &bd->srcbus;
+ } else if (!(cctl & PL080_CONTROL_SRC_INCR)) {
+ *mbus = &bd->srcbus;
+ *sbus = &bd->dstbus;
} else {
- if (bd->dstbus.buswidth == 4) {
- *mbus = &bd->dstbus;
- *sbus = &bd->srcbus;
- } else if (bd->srcbus.buswidth == 4) {
- *mbus = &bd->srcbus;
- *sbus = &bd->dstbus;
- } else if (bd->dstbus.buswidth == 2) {
+ if (bd->dstbus.buswidth >= bd->srcbus.buswidth) {
*mbus = &bd->dstbus;
*sbus = &bd->srcbus;
- } else if (bd->srcbus.buswidth == 2) {
+ } else {
*mbus = &bd->srcbus;
*sbus = &bd->dstbus;
- } else {
- /* bd->srcbus.buswidth == 1 */
- *mbus = &bd->dstbus;
- *sbus = &bd->srcbus;
}
}
}
@@ -550,9 +535,9 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
llis_va[num_llis].cctl = cctl;
llis_va[num_llis].src = bd->srcbus.addr;
llis_va[num_llis].dst = bd->dstbus.addr;
- llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli);
- if (bd->pl08x->lli_buses & PL08X_AHB2)
- llis_va[num_llis].lli |= PL080_LLI_LM_AHB2;
+ llis_va[num_llis].lli = llis_bus + (num_llis + 1) *
+ sizeof(struct pl08x_lli);
+ llis_va[num_llis].lli |= bd->lli_bus;
if (cctl & PL080_CONTROL_SRC_INCR)
bd->srcbus.addr += len;
@@ -564,16 +549,12 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
bd->remainder -= len;
}
-/*
- * Return number of bytes to fill to boundary, or len.
- * This calculation works for any value of addr.
- */
-static inline size_t pl08x_pre_boundary(u32 addr, size_t len)
+static inline void prep_byte_width_lli(struct pl08x_lli_build_data *bd,
+ u32 *cctl, u32 len, int num_llis, size_t *total_bytes)
{
- size_t boundary_len = PL08X_BOUNDARY_SIZE -
- (addr & (PL08X_BOUNDARY_SIZE - 1));
-
- return min(boundary_len, len);
+ *cctl = pl08x_cctl_bits(*cctl, 1, 1, len);
+ pl08x_fill_lli_for_desc(bd, num_llis, len, *cctl);
+ (*total_bytes) += len;
}
/*
@@ -587,13 +568,12 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
struct pl08x_bus_data *mbus, *sbus;
struct pl08x_lli_build_data bd;
int num_llis = 0;
- u32 cctl;
- size_t max_bytes_per_lli;
- size_t total_bytes = 0;
+ u32 cctl, early_bytes = 0;
+ size_t max_bytes_per_lli, total_bytes;
struct pl08x_lli *llis_va;
+ struct pl08x_sg *dsg;
- txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT,
- &txd->llis_bus);
+ txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT, &txd->llis_bus);
if (!txd->llis_va) {
dev_err(&pl08x->adev->dev, "%s no memory for llis\n", __func__);
return 0;
@@ -601,13 +581,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
pl08x->pool_ctr++;
- /* Get the default CCTL */
- cctl = txd->cctl;
-
bd.txd = txd;
- bd.pl08x = pl08x;
- bd.srcbus.addr = txd->src_addr;
- bd.dstbus.addr = txd->dst_addr;
+ bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0;
+ cctl = txd->cctl;
/* Find maximum width of the source bus */
bd.srcbus.maxwidth =
@@ -619,215 +595,179 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
pl08x_get_bytes_for_cctl((cctl & PL080_CONTROL_DWIDTH_MASK) >>
PL080_CONTROL_DWIDTH_SHIFT);
- /* Set up the bus widths to the maximum */
- bd.srcbus.buswidth = bd.srcbus.maxwidth;
- bd.dstbus.buswidth = bd.dstbus.maxwidth;
- dev_vdbg(&pl08x->adev->dev,
- "%s source bus is %d bytes wide, dest bus is %d bytes wide\n",
- __func__, bd.srcbus.buswidth, bd.dstbus.buswidth);
+ list_for_each_entry(dsg, &txd->dsg_list, node) {
+ total_bytes = 0;
+ cctl = txd->cctl;
+ bd.srcbus.addr = dsg->src_addr;
+ bd.dstbus.addr = dsg->dst_addr;
+ bd.remainder = dsg->len;
+ bd.srcbus.buswidth = bd.srcbus.maxwidth;
+ bd.dstbus.buswidth = bd.dstbus.maxwidth;
- /*
- * Bytes transferred == tsize * MIN(buswidths), not max(buswidths)
- */
- max_bytes_per_lli = min(bd.srcbus.buswidth, bd.dstbus.buswidth) *
- PL080_CONTROL_TRANSFER_SIZE_MASK;
- dev_vdbg(&pl08x->adev->dev,
- "%s max bytes per lli = %zu\n",
- __func__, max_bytes_per_lli);
+ pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl);
- /* We need to count this down to zero */
- bd.remainder = txd->len;
- dev_vdbg(&pl08x->adev->dev,
- "%s remainder = %zu\n",
- __func__, bd.remainder);
+ dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu\n",
+ bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "",
+ bd.srcbus.buswidth,
+ bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "",
+ bd.dstbus.buswidth,
+ bd.remainder);
+ dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n",
+ mbus == &bd.srcbus ? "src" : "dst",
+ sbus == &bd.srcbus ? "src" : "dst");
- /*
- * Choose bus to align to
- * - prefers destination bus if both available
- * - if fixed address on one bus chooses other
- */
- pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl);
+ /*
+ * Zero length is only allowed if all these requirements are
+ * met:
+ * - flow controller is peripheral.
+ * - src.addr is aligned to src.width
+ * - dst.addr is aligned to dst.width
+ *
+ * sg_len == 1 should be true, as there can be two cases here:
+ *
+ * - Memory addresses are contiguous and are not scattered.
+ * Here, Only one sg will be passed by user driver, with
+ * memory address and zero length. We pass this to controller
+ * and after the transfer it will receive the last burst
+ * request from peripheral and so transfer finishes.
+ *
+ * - Memory addresses are scattered and are not contiguous.
+ * Here, Obviously as DMA controller doesn't know when a lli's
+ * transfer gets over, it can't load next lli. So in this
+ * case, there has to be an assumption that only one lli is
+ * supported. Thus, we can't have scattered addresses.
+ */
+ if (!bd.remainder) {
+ u32 fc = (txd->ccfg & PL080_CONFIG_FLOW_CONTROL_MASK) >>
+ PL080_CONFIG_FLOW_CONTROL_SHIFT;
+ if (!((fc >= PL080_FLOW_SRC2DST_DST) &&
+ (fc <= PL080_FLOW_SRC2DST_SRC))) {
+ dev_err(&pl08x->adev->dev, "%s sg len can't be zero",
+ __func__);
+ return 0;
+ }
- if (txd->len < mbus->buswidth) {
- /* Less than a bus width available - send as single bytes */
- while (bd.remainder) {
- dev_vdbg(&pl08x->adev->dev,
- "%s single byte LLIs for a transfer of "
- "less than a bus width (remain 0x%08x)\n",
- __func__, bd.remainder);
- cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
- pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
- total_bytes++;
- }
- } else {
- /* Make one byte LLIs until master bus is aligned */
- while ((mbus->addr) % (mbus->buswidth)) {
- dev_vdbg(&pl08x->adev->dev,
- "%s adjustment lli for less than bus width "
- "(remain 0x%08x)\n",
- __func__, bd.remainder);
- cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
- pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
- total_bytes++;
+ if ((bd.srcbus.addr % bd.srcbus.buswidth) ||
+ (bd.srcbus.addr % bd.srcbus.buswidth)) {
+ dev_err(&pl08x->adev->dev,
+ "%s src & dst address must be aligned to src"
+ " & dst width if peripheral is flow controller",
+ __func__);
+ return 0;
+ }
+
+ cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth,
+ bd.dstbus.buswidth, 0);
+ pl08x_fill_lli_for_desc(&bd, num_llis++, 0, cctl);
+ break;
}
/*
- * Master now aligned
- * - if slave is not then we must set its width down
+ * Send byte by byte for following cases
+ * - Less than a bus width available
+ * - until master bus is aligned
*/
- if (sbus->addr % sbus->buswidth) {
- dev_dbg(&pl08x->adev->dev,
- "%s set down bus width to one byte\n",
- __func__);
+ if (bd.remainder < mbus->buswidth)
+ early_bytes = bd.remainder;
+ else if ((mbus->addr) % (mbus->buswidth)) {
+ early_bytes = mbus->buswidth - (mbus->addr) %
+ (mbus->buswidth);
+ if ((bd.remainder - early_bytes) < mbus->buswidth)
+ early_bytes = bd.remainder;
+ }
- sbus->buswidth = 1;
+ if (early_bytes) {
+ dev_vdbg(&pl08x->adev->dev,
+ "%s byte width LLIs (remain 0x%08x)\n",
+ __func__, bd.remainder);
+ prep_byte_width_lli(&bd, &cctl, early_bytes, num_llis++,
+ &total_bytes);
}
- /*
- * Make largest possible LLIs until less than one bus
- * width left
- */
- while (bd.remainder > (mbus->buswidth - 1)) {
- size_t lli_len, target_len, tsize, odd_bytes;
+ if (bd.remainder) {
+ /*
+ * Master now aligned
+ * - if slave is not then we must set its width down
+ */
+ if (sbus->addr % sbus->buswidth) {
+ dev_dbg(&pl08x->adev->dev,
+ "%s set down bus width to one byte\n",
+ __func__);
+
+ sbus->buswidth = 1;
+ }
/*
- * If enough left try to send max possible,
- * otherwise try to send the remainder
+ * Bytes transferred = tsize * src width, not
+ * MIN(buswidths)
*/
- target_len = min(bd.remainder, max_bytes_per_lli);
+ max_bytes_per_lli = bd.srcbus.buswidth *
+ PL080_CONTROL_TRANSFER_SIZE_MASK;
+ dev_vdbg(&pl08x->adev->dev,
+ "%s max bytes per lli = %zu\n",
+ __func__, max_bytes_per_lli);
/*
- * Set bus lengths for incrementing buses to the
- * number of bytes which fill to next memory boundary,
- * limiting on the target length calculated above.
+ * Make largest possible LLIs until less than one bus
+ * width left
*/
- if (cctl & PL080_CONTROL_SRC_INCR)
- bd.srcbus.fill_bytes =
- pl08x_pre_boundary(bd.srcbus.addr,
- target_len);
- else
- bd.srcbus.fill_bytes = target_len;
-
- if (cctl & PL080_CONTROL_DST_INCR)
- bd.dstbus.fill_bytes =
- pl08x_pre_boundary(bd.dstbus.addr,
- target_len);
- else
- bd.dstbus.fill_bytes = target_len;
-
- /* Find the nearest */
- lli_len = min(bd.srcbus.fill_bytes,
- bd.dstbus.fill_bytes);
-
- BUG_ON(lli_len > bd.remainder);
-
- if (lli_len <= 0) {
- dev_err(&pl08x->adev->dev,
- "%s lli_len is %zu, <= 0\n",
- __func__, lli_len);
- return 0;
- }
+ while (bd.remainder > (mbus->buswidth - 1)) {
+ size_t lli_len, tsize, width;
- if (lli_len == target_len) {
- /*
- * Can send what we wanted.
- * Maintain alignment
- */
- lli_len = (lli_len/mbus->buswidth) *
- mbus->buswidth;
- odd_bytes = 0;
- } else {
/*
- * So now we know how many bytes to transfer
- * to get to the nearest boundary. The next
- * LLI will past the boundary. However, we
- * may be working to a boundary on the slave
- * bus. We need to ensure the master stays
- * aligned, and that we are working in
- * multiples of the bus widths.
+ * If enough left try to send max possible,
+ * otherwise try to send the remainder
*/
- odd_bytes = lli_len % mbus->buswidth;
- lli_len -= odd_bytes;
+ lli_len = min(bd.remainder, max_bytes_per_lli);
- }
-
- if (lli_len) {
/*
- * Check against minimum bus alignment:
- * Calculate actual transfer size in relation
- * to bus width an get a maximum remainder of
- * the smallest bus width - 1
+ * Check against maximum bus alignment:
+ * Calculate actual transfer size in relation to
+ * bus width an get a maximum remainder of the
+ * highest bus width - 1
*/
- /* FIXME: use round_down()? */
- tsize = lli_len / min(mbus->buswidth,
- sbus->buswidth);
- lli_len = tsize * min(mbus->buswidth,
- sbus->buswidth);
-
- if (target_len != lli_len) {
- dev_vdbg(&pl08x->adev->dev,
- "%s can't send what we want. Desired 0x%08zx, lli of 0x%08zx bytes in txd of 0x%08zx\n",
- __func__, target_len, lli_len, txd->len);
- }
-
- cctl = pl08x_cctl_bits(cctl,
- bd.srcbus.buswidth,
- bd.dstbus.buswidth,
- tsize);
+ width = max(mbus->buswidth, sbus->buswidth);
+ lli_len = (lli_len / width) * width;
+ tsize = lli_len / bd.srcbus.buswidth;
dev_vdbg(&pl08x->adev->dev,
- "%s fill lli with single lli chunk of size 0x%08zx (remainder 0x%08zx)\n",
+ "%s fill lli with single lli chunk of "
+ "size 0x%08zx (remainder 0x%08zx)\n",
__func__, lli_len, bd.remainder);
+
+ cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth,
+ bd.dstbus.buswidth, tsize);
pl08x_fill_lli_for_desc(&bd, num_llis++,
- lli_len, cctl);
+ lli_len, cctl);
total_bytes += lli_len;
}
-
- if (odd_bytes) {
- /*
- * Creep past the boundary, maintaining
- * master alignment
- */
- int j;
- for (j = 0; (j < mbus->buswidth)
- && (bd.remainder); j++) {
- cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
- dev_vdbg(&pl08x->adev->dev,
- "%s align with boundary, single byte (remain 0x%08zx)\n",
- __func__, bd.remainder);
- pl08x_fill_lli_for_desc(&bd,
- num_llis++, 1, cctl);
- total_bytes++;
- }
+ /*
+ * Send any odd bytes
+ */
+ if (bd.remainder) {
+ dev_vdbg(&pl08x->adev->dev,
+ "%s align with boundary, send odd bytes (remain %zu)\n",
+ __func__, bd.remainder);
+ prep_byte_width_lli(&bd, &cctl, bd.remainder,
+ num_llis++, &total_bytes);
}
}
- /*
- * Send any odd bytes
- */
- while (bd.remainder) {
- cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
- dev_vdbg(&pl08x->adev->dev,
- "%s align with boundary, single odd byte (remain %zu)\n",
- __func__, bd.remainder);
- pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
- total_bytes++;
+ if (total_bytes != dsg->len) {
+ dev_err(&pl08x->adev->dev,
+ "%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n",
+ __func__, total_bytes, dsg->len);
+ return 0;
}
- }
- if (total_bytes != txd->len) {
- dev_err(&pl08x->adev->dev,
- "%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n",
- __func__, total_bytes, txd->len);
- return 0;
- }
- if (num_llis >= MAX_NUM_TSFR_LLIS) {
- dev_err(&pl08x->adev->dev,
- "%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n",
- __func__, (u32) MAX_NUM_TSFR_LLIS);
- return 0;
+ if (num_llis >= MAX_NUM_TSFR_LLIS) {
+ dev_err(&pl08x->adev->dev,
+ "%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n",
+ __func__, (u32) MAX_NUM_TSFR_LLIS);
+ return 0;
+ }
}
llis_va = txd->llis_va;
@@ -840,15 +780,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
{
int i;
+ dev_vdbg(&pl08x->adev->dev,
+ "%-3s %-9s %-10s %-10s %-10s %s\n",
+ "lli", "", "csrc", "cdst", "clli", "cctl");
for (i = 0; i < num_llis; i++) {
dev_vdbg(&pl08x->adev->dev,
- "lli %d @%p: csrc=0x%08x, cdst=0x%08x, cctl=0x%08x, clli=0x%08x\n",
- i,
- &llis_va[i],
- llis_va[i].src,
- llis_va[i].dst,
- llis_va[i].cctl,
- llis_va[i].lli
+ "%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ i, &llis_va[i], llis_va[i].src,
+ llis_va[i].dst, llis_va[i].lli, llis_va[i].cctl
);
}
}
@@ -861,11 +800,19 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
static void pl08x_free_txd(struct pl08x_driver_data *pl08x,
struct pl08x_txd *txd)
{
+ struct pl08x_sg *dsg, *_dsg;
+
/* Free the LLI */
- dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus);
+ if (txd->llis_va)
+ dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus);
pl08x->pool_ctr--;
+ list_for_each_entry_safe(dsg, _dsg, &txd->dsg_list, node) {
+ list_del(&dsg->node);
+ kfree(dsg);
+ }
+
kfree(txd);
}
@@ -922,9 +869,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
* need, but for slaves the physical signals may be muxed!
* Can the platform allow us to use this channel?
*/
- if (plchan->slave &&
- ch->signal < 0 &&
- pl08x->pd->get_signal) {
+ if (plchan->slave && pl08x->pd->get_signal) {
ret = pl08x->pd->get_signal(plchan);
if (ret < 0) {
dev_dbg(&pl08x->adev->dev,
@@ -1013,10 +958,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(
* If slaves are relying on interrupts to signal completion this function
* must not be called with interrupts disabled.
*/
-static enum dma_status
-pl08x_dma_tx_status(struct dma_chan *chan,
- dma_cookie_t cookie,
- struct dma_tx_state *txstate)
+static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie, struct dma_tx_state *txstate)
{
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
dma_cookie_t last_used;
@@ -1054,64 +997,105 @@ pl08x_dma_tx_status(struct dma_chan *chan,
/* PrimeCell DMA extension */
struct burst_table {
- int burstwords;
+ u32 burstwords;
u32 reg;
};
static const struct burst_table burst_sizes[] = {
{
.burstwords = 256,
- .reg = (PL080_BSIZE_256 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_256 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_256,
},
{
.burstwords = 128,
- .reg = (PL080_BSIZE_128 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_128 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_128,
},
{
.burstwords = 64,
- .reg = (PL080_BSIZE_64 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_64 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_64,
},
{
.burstwords = 32,
- .reg = (PL080_BSIZE_32 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_32 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_32,
},
{
.burstwords = 16,
- .reg = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_16,
},
{
.burstwords = 8,
- .reg = (PL080_BSIZE_8 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_8 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_8,
},
{
.burstwords = 4,
- .reg = (PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_4,
},
{
- .burstwords = 1,
- .reg = (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .burstwords = 0,
+ .reg = PL080_BSIZE_1,
},
};
+/*
+ * Given the source and destination available bus masks, select which
+ * will be routed to each port. We try to have source and destination
+ * on separate ports, but always respect the allowable settings.
+ */
+static u32 pl08x_select_bus(u8 src, u8 dst)
+{
+ u32 cctl = 0;
+
+ if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
+ cctl |= PL080_CONTROL_DST_AHB2;
+ if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
+ cctl |= PL080_CONTROL_SRC_AHB2;
+
+ return cctl;
+}
+
+static u32 pl08x_cctl(u32 cctl)
+{
+ cctl &= ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
+ PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
+ PL080_CONTROL_PROT_MASK);
+
+ /* Access the cell in privileged mode, non-bufferable, non-cacheable */
+ return cctl | PL080_CONTROL_PROT_SYS;
+}
+
+static u32 pl08x_width(enum dma_slave_buswidth width)
+{
+ switch (width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ return PL080_WIDTH_8BIT;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ return PL080_WIDTH_16BIT;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ return PL080_WIDTH_32BIT;
+ default:
+ return ~0;
+ }
+}
+
+static u32 pl08x_burst(u32 maxburst)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
+ if (burst_sizes[i].burstwords <= maxburst)
+ break;
+
+ return burst_sizes[i].reg;
+}
+
static int dma_set_runtime_config(struct dma_chan *chan,
struct dma_slave_config *config)
{
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
struct pl08x_driver_data *pl08x = plchan->host;
- struct pl08x_channel_data *cd = plchan->cd;
enum dma_slave_buswidth addr_width;
- dma_addr_t addr;
- u32 maxburst;
+ u32 width, burst, maxburst;
u32 cctl = 0;
- int i;
if (!plchan->slave)
return -EINVAL;
@@ -1119,11 +1103,9 @@ static int dma_set_runtime_config(struct dma_chan *chan,
/* Transfer direction */
plchan->runtime_direction = config->direction;
if (config->direction == DMA_TO_DEVICE) {
- addr = config->dst_addr;
addr_width = config->dst_addr_width;
maxburst = config->dst_maxburst;
} else if (config->direction == DMA_FROM_DEVICE) {
- addr = config->src_addr;
addr_width = config->src_addr_width;
maxburst = config->src_maxburst;
} else {
@@ -1132,46 +1114,40 @@ static int dma_set_runtime_config(struct dma_chan *chan,
return -EINVAL;
}
- switch (addr_width) {
- case DMA_SLAVE_BUSWIDTH_1_BYTE:
- cctl |= (PL080_WIDTH_8BIT << PL080_CONTROL_SWIDTH_SHIFT) |
- (PL080_WIDTH_8BIT << PL080_CONTROL_DWIDTH_SHIFT);
- break;
- case DMA_SLAVE_BUSWIDTH_2_BYTES:
- cctl |= (PL080_WIDTH_16BIT << PL080_CONTROL_SWIDTH_SHIFT) |
- (PL080_WIDTH_16BIT << PL080_CONTROL_DWIDTH_SHIFT);
- break;
- case DMA_SLAVE_BUSWIDTH_4_BYTES:
- cctl |= (PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT) |
- (PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT);
- break;
- default:
+ width = pl08x_width(addr_width);
+ if (width == ~0) {
dev_err(&pl08x->adev->dev,
"bad runtime_config: alien address width\n");
return -EINVAL;
}
+ cctl |= width << PL080_CONTROL_SWIDTH_SHIFT;
+ cctl |= width << PL080_CONTROL_DWIDTH_SHIFT;
+
/*
- * Now decide on a maxburst:
* If this channel will only request single transfers, set this
* down to ONE element. Also select one element if no maxburst
* is specified.
*/
- if (plchan->cd->single || maxburst == 0) {
- cctl |= (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT);
+ if (plchan->cd->single)
+ maxburst = 1;
+
+ burst = pl08x_burst(maxburst);
+ cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT;
+ cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT;
+
+ if (plchan->runtime_direction == DMA_FROM_DEVICE) {
+ plchan->src_addr = config->src_addr;
+ plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR |
+ pl08x_select_bus(plchan->cd->periph_buses,
+ pl08x->mem_buses);
} else {
- for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
- if (burst_sizes[i].burstwords <= maxburst)
- break;
- cctl |= burst_sizes[i].reg;
+ plchan->dst_addr = config->dst_addr;
+ plchan->dst_cctl = pl08x_cctl(cctl) | PL080_CONTROL_SRC_INCR |
+ pl08x_select_bus(pl08x->mem_buses,
+ plchan->cd->periph_buses);
}
- plchan->runtime_addr = addr;
-
- /* Modify the default channel data to fit PrimeCell request */
- cd->cctl = cctl;
-
dev_dbg(&pl08x->adev->dev,
"configured channel %s (%s) for %s, data width %d, "
"maxburst %d words, LE, CCTL=0x%08x\n",
@@ -1225,7 +1201,9 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
num_llis = pl08x_fill_llis_for_desc(pl08x, txd);
if (!num_llis) {
- kfree(txd);
+ spin_lock_irqsave(&plchan->lock, flags);
+ pl08x_free_txd(pl08x, txd);
+ spin_unlock_irqrestore(&plchan->lock, flags);
return -EINVAL;
}
@@ -1270,33 +1248,17 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
return 0;
}
-/*
- * Given the source and destination available bus masks, select which
- * will be routed to each port. We try to have source and destination
- * on separate ports, but always respect the allowable settings.
- */
-static u32 pl08x_select_bus(struct pl08x_driver_data *pl08x, u8 src, u8 dst)
-{
- u32 cctl = 0;
-
- if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
- cctl |= PL080_CONTROL_DST_AHB2;
- if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
- cctl |= PL080_CONTROL_SRC_AHB2;
-
- return cctl;
-}
-
static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan,
unsigned long flags)
{
- struct pl08x_txd *txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT);
+ struct pl08x_txd *txd = kzalloc(sizeof(*txd), GFP_NOWAIT);
if (txd) {
dma_async_tx_descriptor_init(&txd->tx, &plchan->chan);
txd->tx.flags = flags;
txd->tx.tx_submit = pl08x_tx_submit;
INIT_LIST_HEAD(&txd->node);
+ INIT_LIST_HEAD(&txd->dsg_list);
/* Always enable error and terminal interrupts */
txd->ccfg = PL080_CONFIG_ERR_IRQ_MASK |
@@ -1315,6 +1277,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_txd *txd;
+ struct pl08x_sg *dsg;
int ret;
txd = pl08x_get_txd(plchan, flags);
@@ -1324,10 +1287,19 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
return NULL;
}
+ dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
+ if (!dsg) {
+ pl08x_free_txd(pl08x, txd);
+ dev_err(&pl08x->adev->dev, "%s no memory for pl080 sg\n",
+ __func__);
+ return NULL;
+ }
+ list_add_tail(&dsg->node, &txd->dsg_list);
+
txd->direction = DMA_NONE;
- txd->src_addr = src;
- txd->dst_addr = dest;
- txd->len = len;
+ dsg->src_addr = src;
+ dsg->dst_addr = dest;
+ dsg->len = len;
/* Set platform data for m2m */
txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
@@ -1338,8 +1310,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
txd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
if (pl08x->vd->dualmaster)
- txd->cctl |= pl08x_select_bus(pl08x,
- pl08x->mem_buses, pl08x->mem_buses);
+ txd->cctl |= pl08x_select_bus(pl08x->mem_buses,
+ pl08x->mem_buses);
ret = pl08x_prep_channel_resources(plchan, txd);
if (ret)
@@ -1356,20 +1328,13 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_txd *txd;
- u8 src_buses, dst_buses;
- int ret;
-
- /*
- * Current implementation ASSUMES only one sg
- */
- if (sg_len != 1) {
- dev_err(&pl08x->adev->dev, "%s prepared too long sglist\n",
- __func__);
- BUG();
- }
+ struct pl08x_sg *dsg;
+ struct scatterlist *sg;
+ dma_addr_t slave_addr;
+ int ret, tmp;
dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n",
- __func__, sgl->length, plchan->name);
+ __func__, sgl->length, plchan->name);
txd = pl08x_get_txd(plchan, flags);
if (!txd) {
@@ -1388,43 +1353,48 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
* channel target address dynamically at runtime.
*/
txd->direction = direction;
- txd->len = sgl->length;
-
- txd->cctl = plchan->cd->cctl &
- ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
- PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
- PL080_CONTROL_PROT_MASK);
-
- /* Access the cell in privileged mode, non-bufferable, non-cacheable */
- txd->cctl |= PL080_CONTROL_PROT_SYS;
if (direction == DMA_TO_DEVICE) {
- txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
- txd->cctl |= PL080_CONTROL_SRC_INCR;
- txd->src_addr = sgl->dma_address;
- if (plchan->runtime_addr)
- txd->dst_addr = plchan->runtime_addr;
- else
- txd->dst_addr = plchan->cd->addr;
- src_buses = pl08x->mem_buses;
- dst_buses = plchan->cd->periph_buses;
+ txd->cctl = plchan->dst_cctl;
+ slave_addr = plchan->dst_addr;
} else if (direction == DMA_FROM_DEVICE) {
- txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
- txd->cctl |= PL080_CONTROL_DST_INCR;
- if (plchan->runtime_addr)
- txd->src_addr = plchan->runtime_addr;
- else
- txd->src_addr = plchan->cd->addr;
- txd->dst_addr = sgl->dma_address;
- src_buses = plchan->cd->periph_buses;
- dst_buses = pl08x->mem_buses;
+ txd->cctl = plchan->src_cctl;
+ slave_addr = plchan->src_addr;
} else {
+ pl08x_free_txd(pl08x, txd);
dev_err(&pl08x->adev->dev,
"%s direction unsupported\n", __func__);
return NULL;
}
- txd->cctl |= pl08x_select_bus(pl08x, src_buses, dst_buses);
+ if (plchan->cd->device_fc)
+ tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER_PER :
+ PL080_FLOW_PER2MEM_PER;
+ else
+ tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER :
+ PL080_FLOW_PER2MEM;
+
+ txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+
+ for_each_sg(sgl, sg, sg_len, tmp) {
+ dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
+ if (!dsg) {
+ pl08x_free_txd(pl08x, txd);
+ dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n",
+ __func__);
+ return NULL;
+ }
+ list_add_tail(&dsg->node, &txd->dsg_list);
+
+ dsg->len = sg_dma_len(sg);
+ if (direction == DMA_TO_DEVICE) {
+ dsg->src_addr = sg_phys(sg);
+ dsg->dst_addr = slave_addr;
+ } else {
+ dsg->src_addr = slave_addr;
+ dsg->dst_addr = sg_phys(sg);
+ }
+ }
ret = pl08x_prep_channel_resources(plchan, txd);
if (ret)
@@ -1499,9 +1469,15 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
bool pl08x_filter_id(struct dma_chan *chan, void *chan_id)
{
- struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ struct pl08x_dma_chan *plchan;
char *name = chan_id;
+ /* Reject channels for devices not bound to this driver */
+ if (chan->device->dev->driver != &pl08x_amba_driver.drv)
+ return false;
+
+ plchan = to_pl08x_chan(chan);
+
/* Check that the channel is not taken! */
if (!strcmp(plchan->name, name))
return true;
@@ -1517,34 +1493,34 @@ bool pl08x_filter_id(struct dma_chan *chan, void *chan_id)
*/
static void pl08x_ensure_on(struct pl08x_driver_data *pl08x)
{
- u32 val;
-
- val = readl(pl08x->base + PL080_CONFIG);
- val &= ~(PL080_CONFIG_M2_BE | PL080_CONFIG_M1_BE | PL080_CONFIG_ENABLE);
- /* We implicitly clear bit 1 and that means little-endian mode */
- val |= PL080_CONFIG_ENABLE;
- writel(val, pl08x->base + PL080_CONFIG);
+ writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG);
}
static void pl08x_unmap_buffers(struct pl08x_txd *txd)
{
struct device *dev = txd->tx.chan->device->dev;
+ struct pl08x_sg *dsg;
if (!(txd->tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
if (txd->tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
- dma_unmap_single(dev, txd->src_addr, txd->len,
- DMA_TO_DEVICE);
- else
- dma_unmap_page(dev, txd->src_addr, txd->len,
- DMA_TO_DEVICE);
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_single(dev, dsg->src_addr, dsg->len,
+ DMA_TO_DEVICE);
+ else {
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_page(dev, dsg->src_addr, dsg->len,
+ DMA_TO_DEVICE);
+ }
}
if (!(txd->tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
if (txd->tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
- dma_unmap_single(dev, txd->dst_addr, txd->len,
- DMA_FROM_DEVICE);
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_single(dev, dsg->dst_addr, dsg->len,
+ DMA_FROM_DEVICE);
else
- dma_unmap_page(dev, txd->dst_addr, txd->len,
- DMA_FROM_DEVICE);
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_page(dev, dsg->dst_addr, dsg->len,
+ DMA_FROM_DEVICE);
}
}
@@ -1599,8 +1575,8 @@ static void pl08x_tasklet(unsigned long data)
*/
list_for_each_entry(waiting, &pl08x->memcpy.channels,
chan.device_node) {
- if (waiting->state == PL08X_CHAN_WAITING &&
- waiting->waiting != NULL) {
+ if (waiting->state == PL08X_CHAN_WAITING &&
+ waiting->waiting != NULL) {
int ret;
/* This should REALLY not fail now */
@@ -1640,50 +1616,64 @@ static void pl08x_tasklet(unsigned long data)
static irqreturn_t pl08x_irq(int irq, void *dev)
{
struct pl08x_driver_data *pl08x = dev;
- u32 mask = 0;
- u32 val;
- int i;
-
- val = readl(pl08x->base + PL080_ERR_STATUS);
- if (val) {
- /* An error interrupt (on one or more channels) */
- dev_err(&pl08x->adev->dev,
- "%s error interrupt, register value 0x%08x\n",
- __func__, val);
- /*
- * Simply clear ALL PL08X error interrupts,
- * regardless of channel and cause
- * FIXME: should be 0x00000003 on PL081 really.
- */
- writel(0x000000FF, pl08x->base + PL080_ERR_CLEAR);
+ u32 mask = 0, err, tc, i;
+
+ /* check & clear - ERR & TC interrupts */
+ err = readl(pl08x->base + PL080_ERR_STATUS);
+ if (err) {
+ dev_err(&pl08x->adev->dev, "%s error interrupt, register value 0x%08x\n",
+ __func__, err);
+ writel(err, pl08x->base + PL080_ERR_CLEAR);
}
- val = readl(pl08x->base + PL080_INT_STATUS);
+ tc = readl(pl08x->base + PL080_INT_STATUS);
+ if (tc)
+ writel(tc, pl08x->base + PL080_TC_CLEAR);
+
+ if (!err && !tc)
+ return IRQ_NONE;
+
for (i = 0; i < pl08x->vd->channels; i++) {
- if ((1 << i) & val) {
+ if (((1 << i) & err) || ((1 << i) & tc)) {
/* Locate physical channel */
struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i];
struct pl08x_dma_chan *plchan = phychan->serving;
+ if (!plchan) {
+ dev_err(&pl08x->adev->dev,
+ "%s Error TC interrupt on unused channel: 0x%08x\n",
+ __func__, i);
+ continue;
+ }
+
/* Schedule tasklet on this channel */
tasklet_schedule(&plchan->tasklet);
-
mask |= (1 << i);
}
}
- /* Clear only the terminal interrupts on channels we processed */
- writel(mask, pl08x->base + PL080_TC_CLEAR);
return mask ? IRQ_HANDLED : IRQ_NONE;
}
+static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
+{
+ u32 cctl = pl08x_cctl(chan->cd->cctl);
+
+ chan->slave = true;
+ chan->name = chan->cd->bus_id;
+ chan->src_addr = chan->cd->addr;
+ chan->dst_addr = chan->cd->addr;
+ chan->src_cctl = cctl | PL080_CONTROL_DST_INCR |
+ pl08x_select_bus(chan->cd->periph_buses, chan->host->mem_buses);
+ chan->dst_cctl = cctl | PL080_CONTROL_SRC_INCR |
+ pl08x_select_bus(chan->host->mem_buses, chan->cd->periph_buses);
+}
+
/*
* Initialise the DMAC memcpy/slave channels.
* Make a local wrapper to hold required data
*/
static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
- struct dma_device *dmadev,
- unsigned int channels,
- bool slave)
+ struct dma_device *dmadev, unsigned int channels, bool slave)
{
struct pl08x_dma_chan *chan;
int i;
@@ -1696,7 +1686,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
* to cope with that situation.
*/
for (i = 0; i < channels; i++) {
- chan = kzalloc(sizeof(struct pl08x_dma_chan), GFP_KERNEL);
+ chan = kzalloc(sizeof(*chan), GFP_KERNEL);
if (!chan) {
dev_err(&pl08x->adev->dev,
"%s no memory for channel\n", __func__);
@@ -1707,9 +1697,8 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
chan->state = PL08X_CHAN_IDLE;
if (slave) {
- chan->slave = true;
- chan->name = pl08x->pd->slave_channels[i].bus_id;
chan->cd = &pl08x->pd->slave_channels[i];
+ pl08x_dma_slave_init(chan);
} else {
chan->cd = &pl08x->pd->memcpy_channel;
chan->name = kasprintf(GFP_KERNEL, "memcpy%d", i);
@@ -1725,7 +1714,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
kfree(chan);
continue;
}
- dev_info(&pl08x->adev->dev,
+ dev_dbg(&pl08x->adev->dev,
"initialize virtual channel \"%s\"\n",
chan->name);
@@ -1834,9 +1823,9 @@ static const struct file_operations pl08x_debugfs_operations = {
static void init_pl08x_debugfs(struct pl08x_driver_data *pl08x)
{
/* Expose a simple debugfs interface to view all clocks */
- (void) debugfs_create_file(dev_name(&pl08x->adev->dev), S_IFREG | S_IRUGO,
- NULL, pl08x,
- &pl08x_debugfs_operations);
+ (void) debugfs_create_file(dev_name(&pl08x->adev->dev),
+ S_IFREG | S_IRUGO, NULL, pl08x,
+ &pl08x_debugfs_operations);
}
#else
@@ -1857,12 +1846,15 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
return ret;
/* Create the driver state holder */
- pl08x = kzalloc(sizeof(struct pl08x_driver_data), GFP_KERNEL);
+ pl08x = kzalloc(sizeof(*pl08x), GFP_KERNEL);
if (!pl08x) {
ret = -ENOMEM;
goto out_no_pl08x;
}
+ pm_runtime_set_active(&adev->dev);
+ pm_runtime_enable(&adev->dev);
+
/* Initialize memcpy engine */
dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask);
pl08x->memcpy.dev = &adev->dev;
@@ -1936,7 +1928,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
}
/* Initialize physical channels */
- pl08x->phy_chans = kmalloc((vd->channels * sizeof(struct pl08x_phy_chan)),
+ pl08x->phy_chans = kmalloc((vd->channels * sizeof(*pl08x->phy_chans)),
GFP_KERNEL);
if (!pl08x->phy_chans) {
dev_err(&adev->dev, "%s failed to allocate "
@@ -1953,9 +1945,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
spin_lock_init(&ch->lock);
ch->serving = NULL;
ch->signal = -1;
- dev_info(&adev->dev,
- "physical channel %d is %s\n", i,
- pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE");
+ dev_dbg(&adev->dev, "physical channel %d is %s\n",
+ i, pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE");
}
/* Register as many memcpy channels as there are physical channels */
@@ -1971,8 +1962,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
/* Register slave channels */
ret = pl08x_dma_init_virtual_channels(pl08x, &pl08x->slave,
- pl08x->pd->num_slave_channels,
- true);
+ pl08x->pd->num_slave_channels, true);
if (ret <= 0) {
dev_warn(&pl08x->adev->dev,
"%s failed to enumerate slave channels - %d\n",
@@ -2002,6 +1992,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
dev_info(&pl08x->adev->dev, "DMA: PL%03x rev%u at 0x%08llx irq %d\n",
amba_part(adev), amba_rev(adev),
(unsigned long long)adev->res.start, adev->irq[0]);
+
+ pm_runtime_put(&adev->dev);
return 0;
out_no_slave_reg:
@@ -2020,6 +2012,9 @@ out_no_ioremap:
dma_pool_destroy(pl08x->pool);
out_no_lli_pool:
out_no_platdata:
+ pm_runtime_put(&adev->dev);
+ pm_runtime_disable(&adev->dev);
+
kfree(pl08x);
out_no_pl08x:
amba_release_regions(adev);
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 36144f88d718..fcfa0a8b5c59 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -107,10 +107,11 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
{
struct at_desc *desc, *_desc;
struct at_desc *ret = NULL;
+ unsigned long flags;
unsigned int i = 0;
LIST_HEAD(tmp_list);
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
list_for_each_entry_safe(desc, _desc, &atchan->free_list, desc_node) {
i++;
if (async_tx_test_ack(&desc->txd)) {
@@ -121,7 +122,7 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
dev_dbg(chan2dev(&atchan->chan_common),
"desc %p not ACKed\n", desc);
}
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
dev_vdbg(chan2dev(&atchan->chan_common),
"scanned %u descriptors on freelist\n", i);
@@ -129,9 +130,9 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
if (!ret) {
ret = atc_alloc_descriptor(&atchan->chan_common, GFP_ATOMIC);
if (ret) {
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
atchan->descs_allocated++;
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
} else {
dev_err(chan2dev(&atchan->chan_common),
"not enough descriptors available\n");
@@ -150,8 +151,9 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc)
{
if (desc) {
struct at_desc *child;
+ unsigned long flags;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
list_for_each_entry(child, &desc->tx_list, desc_node)
dev_vdbg(chan2dev(&atchan->chan_common),
"moving child desc %p to freelist\n",
@@ -160,7 +162,7 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc)
dev_vdbg(chan2dev(&atchan->chan_common),
"moving desc %p to freelist\n", desc);
list_add(&desc->desc_node, &atchan->free_list);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
}
}
@@ -299,7 +301,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc)
/* for cyclic transfers,
* no need to replay callback function while stopping */
- if (!test_bit(ATC_IS_CYCLIC, &atchan->status)) {
+ if (!atc_chan_is_cyclic(atchan)) {
dma_async_tx_callback callback = txd->callback;
void *param = txd->callback_param;
@@ -471,16 +473,17 @@ static void atc_handle_cyclic(struct at_dma_chan *atchan)
static void atc_tasklet(unsigned long data)
{
struct at_dma_chan *atchan = (struct at_dma_chan *)data;
+ unsigned long flags;
- spin_lock(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
if (test_and_clear_bit(ATC_IS_ERROR, &atchan->status))
atc_handle_error(atchan);
- else if (test_bit(ATC_IS_CYCLIC, &atchan->status))
+ else if (atc_chan_is_cyclic(atchan))
atc_handle_cyclic(atchan);
else
atc_advance_work(atchan);
- spin_unlock(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
}
static irqreturn_t at_dma_interrupt(int irq, void *dev_id)
@@ -539,8 +542,9 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx)
struct at_desc *desc = txd_to_at_desc(tx);
struct at_dma_chan *atchan = to_at_dma_chan(tx->chan);
dma_cookie_t cookie;
+ unsigned long flags;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
cookie = atc_assign_cookie(atchan, desc);
if (list_empty(&atchan->active_list)) {
@@ -554,7 +558,7 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx)
list_add_tail(&desc->desc_node, &atchan->queue);
}
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
return cookie;
}
@@ -927,28 +931,29 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
struct at_dma_chan *atchan = to_at_dma_chan(chan);
struct at_dma *atdma = to_at_dma(chan->device);
int chan_id = atchan->chan_common.chan_id;
+ unsigned long flags;
LIST_HEAD(list);
dev_vdbg(chan2dev(chan), "atc_control (%d)\n", cmd);
if (cmd == DMA_PAUSE) {
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
dma_writel(atdma, CHER, AT_DMA_SUSP(chan_id));
set_bit(ATC_IS_PAUSED, &atchan->status);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
} else if (cmd == DMA_RESUME) {
- if (!test_bit(ATC_IS_PAUSED, &atchan->status))
+ if (!atc_chan_is_paused(atchan))
return 0;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
dma_writel(atdma, CHDR, AT_DMA_RES(chan_id));
clear_bit(ATC_IS_PAUSED, &atchan->status);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
} else if (cmd == DMA_TERMINATE_ALL) {
struct at_desc *desc, *_desc;
/*
@@ -957,7 +962,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
* channel. We still have to poll the channel enable bit due
* to AHB/HSB limitations.
*/
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
/* disabling channel: must also remove suspend state */
dma_writel(atdma, CHDR, AT_DMA_RES(chan_id) | atchan->mask);
@@ -978,7 +983,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
/* if channel dedicated to cyclic operations, free it */
clear_bit(ATC_IS_CYCLIC, &atchan->status);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
} else {
return -ENXIO;
}
@@ -1004,9 +1009,10 @@ atc_tx_status(struct dma_chan *chan,
struct at_dma_chan *atchan = to_at_dma_chan(chan);
dma_cookie_t last_used;
dma_cookie_t last_complete;
+ unsigned long flags;
enum dma_status ret;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
last_complete = atchan->completed_cookie;
last_used = chan->cookie;
@@ -1021,7 +1027,7 @@ atc_tx_status(struct dma_chan *chan,
ret = dma_async_is_complete(cookie, last_complete, last_used);
}
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
if (ret != DMA_SUCCESS)
dma_set_tx_state(txstate, last_complete, last_used,
@@ -1029,7 +1035,7 @@ atc_tx_status(struct dma_chan *chan,
else
dma_set_tx_state(txstate, last_complete, last_used, 0);
- if (test_bit(ATC_IS_PAUSED, &atchan->status))
+ if (atc_chan_is_paused(atchan))
ret = DMA_PAUSED;
dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n",
@@ -1046,18 +1052,19 @@ atc_tx_status(struct dma_chan *chan,
static void atc_issue_pending(struct dma_chan *chan)
{
struct at_dma_chan *atchan = to_at_dma_chan(chan);
+ unsigned long flags;
dev_vdbg(chan2dev(chan), "issue_pending\n");
/* Not needed for cyclic transfers */
- if (test_bit(ATC_IS_CYCLIC, &atchan->status))
+ if (atc_chan_is_cyclic(atchan))
return;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
if (!atc_chan_is_enabled(atchan)) {
atc_advance_work(atchan);
}
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
}
/**
@@ -1073,6 +1080,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan)
struct at_dma *atdma = to_at_dma(chan->device);
struct at_desc *desc;
struct at_dma_slave *atslave;
+ unsigned long flags;
int i;
u32 cfg;
LIST_HEAD(tmp_list);
@@ -1116,11 +1124,11 @@ static int atc_alloc_chan_resources(struct dma_chan *chan)
list_add_tail(&desc->desc_node, &tmp_list);
}
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
atchan->descs_allocated = i;
list_splice(&tmp_list, &atchan->free_list);
atchan->completed_cookie = chan->cookie = 1;
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
/* channel parameters */
channel_writel(atchan, CFG, cfg);
@@ -1216,7 +1224,7 @@ static int __init at_dma_probe(struct platform_device *pdev)
atdma->dma_common.cap_mask = pdata->cap_mask;
atdma->all_chan_mask = (1 << pdata->nr_channels) - 1;
- size = io->end - io->start + 1;
+ size = resource_size(io);
if (!request_mem_region(io->start, size, pdev->dev.driver->name)) {
err = -EBUSY;
goto err_kfree;
@@ -1260,12 +1268,11 @@ static int __init at_dma_probe(struct platform_device *pdev)
/* initialize channels related values */
INIT_LIST_HEAD(&atdma->dma_common.channels);
- for (i = 0; i < pdata->nr_channels; i++, atdma->dma_common.chancnt++) {
+ for (i = 0; i < pdata->nr_channels; i++) {
struct at_dma_chan *atchan = &atdma->chan[i];
atchan->chan_common.device = &atdma->dma_common;
atchan->chan_common.cookie = atchan->completed_cookie = 1;
- atchan->chan_common.chan_id = i;
list_add_tail(&atchan->chan_common.device_node,
&atdma->dma_common.channels);
@@ -1293,22 +1300,20 @@ static int __init at_dma_probe(struct platform_device *pdev)
if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask))
atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy;
- if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask))
+ if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) {
atdma->dma_common.device_prep_slave_sg = atc_prep_slave_sg;
-
- if (dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask))
+ /* controller can do slave DMA: can trigger cyclic transfers */
+ dma_cap_set(DMA_CYCLIC, atdma->dma_common.cap_mask);
atdma->dma_common.device_prep_dma_cyclic = atc_prep_dma_cyclic;
-
- if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ||
- dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask))
atdma->dma_common.device_control = atc_control;
+ }
dma_writel(atdma, EN, AT_DMA_ENABLE);
dev_info(&pdev->dev, "Atmel AHB DMA Controller ( %s%s), %d channels\n",
dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask) ? "cpy " : "",
dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ? "slave " : "",
- atdma->dma_common.chancnt);
+ pdata->nr_channels);
dma_async_device_register(&atdma->dma_common);
@@ -1362,7 +1367,7 @@ static int __exit at_dma_remove(struct platform_device *pdev)
atdma->regs = NULL;
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(io->start, io->end - io->start + 1);
+ release_mem_region(io->start, resource_size(io));
kfree(atdma);
@@ -1377,27 +1382,112 @@ static void at_dma_shutdown(struct platform_device *pdev)
clk_disable(atdma->clk);
}
+static int at_dma_prepare(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct at_dma *atdma = platform_get_drvdata(pdev);
+ struct dma_chan *chan, *_chan;
+
+ list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels,
+ device_node) {
+ struct at_dma_chan *atchan = to_at_dma_chan(chan);
+ /* wait for transaction completion (except in cyclic case) */
+ if (atc_chan_is_enabled(atchan) && !atc_chan_is_cyclic(atchan))
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static void atc_suspend_cyclic(struct at_dma_chan *atchan)
+{
+ struct dma_chan *chan = &atchan->chan_common;
+
+ /* Channel should be paused by user
+ * do it anyway even if it is not done already */
+ if (!atc_chan_is_paused(atchan)) {
+ dev_warn(chan2dev(chan),
+ "cyclic channel not paused, should be done by channel user\n");
+ atc_control(chan, DMA_PAUSE, 0);
+ }
+
+ /* now preserve additional data for cyclic operations */
+ /* next descriptor address in the cyclic list */
+ atchan->save_dscr = channel_readl(atchan, DSCR);
+
+ vdbg_dump_regs(atchan);
+}
+
static int at_dma_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct at_dma *atdma = platform_get_drvdata(pdev);
+ struct dma_chan *chan, *_chan;
- at_dma_off(platform_get_drvdata(pdev));
+ /* preserve data */
+ list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels,
+ device_node) {
+ struct at_dma_chan *atchan = to_at_dma_chan(chan);
+
+ if (atc_chan_is_cyclic(atchan))
+ atc_suspend_cyclic(atchan);
+ atchan->save_cfg = channel_readl(atchan, CFG);
+ }
+ atdma->save_imr = dma_readl(atdma, EBCIMR);
+
+ /* disable DMA controller */
+ at_dma_off(atdma);
clk_disable(atdma->clk);
return 0;
}
+static void atc_resume_cyclic(struct at_dma_chan *atchan)
+{
+ struct at_dma *atdma = to_at_dma(atchan->chan_common.device);
+
+ /* restore channel status for cyclic descriptors list:
+ * next descriptor in the cyclic list at the time of suspend */
+ channel_writel(atchan, SADDR, 0);
+ channel_writel(atchan, DADDR, 0);
+ channel_writel(atchan, CTRLA, 0);
+ channel_writel(atchan, CTRLB, 0);
+ channel_writel(atchan, DSCR, atchan->save_dscr);
+ dma_writel(atdma, CHER, atchan->mask);
+
+ /* channel pause status should be removed by channel user
+ * We cannot take the initiative to do it here */
+
+ vdbg_dump_regs(atchan);
+}
+
static int at_dma_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct at_dma *atdma = platform_get_drvdata(pdev);
+ struct dma_chan *chan, *_chan;
+ /* bring back DMA controller */
clk_enable(atdma->clk);
dma_writel(atdma, EN, AT_DMA_ENABLE);
+
+ /* clear any pending interrupt */
+ while (dma_readl(atdma, EBCISR))
+ cpu_relax();
+
+ /* restore saved data */
+ dma_writel(atdma, EBCIER, atdma->save_imr);
+ list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels,
+ device_node) {
+ struct at_dma_chan *atchan = to_at_dma_chan(chan);
+
+ channel_writel(atchan, CFG, atchan->save_cfg);
+ if (atc_chan_is_cyclic(atchan))
+ atc_resume_cyclic(atchan);
+ }
return 0;
}
static const struct dev_pm_ops at_dma_dev_pm_ops = {
+ .prepare = at_dma_prepare,
.suspend_noirq = at_dma_suspend_noirq,
.resume_noirq = at_dma_resume_noirq,
};
diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h
index 087dbf1dd39c..aa4c9aebab7c 100644
--- a/drivers/dma/at_hdmac_regs.h
+++ b/drivers/dma/at_hdmac_regs.h
@@ -204,6 +204,9 @@ enum atc_status {
* @status: transmit status information from irq/prep* functions
* to tasklet (use atomic operations)
* @tasklet: bottom half to finish transaction work
+ * @save_cfg: configuration register that is saved on suspend/resume cycle
+ * @save_dscr: for cyclic operations, preserve next descriptor address in
+ * the cyclic list on suspend/resume cycle
* @lock: serializes enqueue/dequeue operations to descriptors lists
* @completed_cookie: identifier for the most recently completed operation
* @active_list: list of descriptors dmaengine is being running on
@@ -218,6 +221,8 @@ struct at_dma_chan {
u8 mask;
unsigned long status;
struct tasklet_struct tasklet;
+ u32 save_cfg;
+ u32 save_dscr;
spinlock_t lock;
@@ -248,6 +253,7 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan)
* @chan_common: common dmaengine dma_device object members
* @ch_regs: memory mapped register base
* @clk: dma controller clock
+ * @save_imr: interrupt mask register that is saved on suspend/resume cycle
* @all_chan_mask: all channels availlable in a mask
* @dma_desc_pool: base of DMA descriptor region (DMA address)
* @chan: channels table to store at_dma_chan structures
@@ -256,6 +262,7 @@ struct at_dma {
struct dma_device dma_common;
void __iomem *regs;
struct clk *clk;
+ u32 save_imr;
u8 all_chan_mask;
@@ -355,6 +362,23 @@ static inline int atc_chan_is_enabled(struct at_dma_chan *atchan)
return !!(dma_readl(atdma, CHSR) & atchan->mask);
}
+/**
+ * atc_chan_is_paused - test channel pause/resume status
+ * @atchan: channel we want to test status
+ */
+static inline int atc_chan_is_paused(struct at_dma_chan *atchan)
+{
+ return test_bit(ATC_IS_PAUSED, &atchan->status);
+}
+
+/**
+ * atc_chan_is_cyclic - test if given channel has cyclic property set
+ * @atchan: channel we want to test status
+ */
+static inline int atc_chan_is_cyclic(struct at_dma_chan *atchan)
+{
+ return test_bit(ATC_IS_CYCLIC, &atchan->status);
+}
/**
* set_desc_eol - set end-of-link to descriptor so it will end transfer
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c
index af8c0b5ed70f..4234f416ef11 100644
--- a/drivers/dma/coh901318.c
+++ b/drivers/dma/coh901318.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h> /* everything... */
+#include <linux/scatterlist.h>
#include <linux/slab.h> /* kmalloc() */
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
@@ -40,6 +41,8 @@ struct coh901318_desc {
struct coh901318_lli *lli;
enum dma_data_direction dir;
unsigned long flags;
+ u32 head_config;
+ u32 head_ctrl;
};
struct coh901318_base {
@@ -660,6 +663,9 @@ static struct coh901318_desc *coh901318_queue_start(struct coh901318_chan *cohc)
coh901318_desc_submit(cohc, cohd);
+ /* Program the transaction head */
+ coh901318_set_conf(cohc, cohd->head_config);
+ coh901318_set_ctrl(cohc, cohd->head_ctrl);
coh901318_prep_linked_list(cohc, cohd->lli);
/* start dma job on this channel */
@@ -1090,8 +1096,6 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
} else
goto err_direction;
- coh901318_set_conf(cohc, config);
-
/* The dma only supports transmitting packages up to
* MAX_DMA_PACKET_SIZE. Calculate to total number of
* dma elemts required to send the entire sg list
@@ -1128,16 +1132,18 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (ret)
goto err_lli_fill;
- /*
- * Set the default ctrl for the channel to the one from the lli,
- * things may have changed due to odd buffer alignment etc.
- */
- coh901318_set_ctrl(cohc, lli->control);
COH_DBG(coh901318_list_print(cohc, lli));
/* Pick a descriptor to handle this transfer */
cohd = coh901318_desc_get(cohc);
+ cohd->head_config = config;
+ /*
+ * Set the default head ctrl for the channel to the one from the
+ * lli, things may have changed due to odd buffer alignment
+ * etc.
+ */
+ cohd->head_ctrl = lli->control;
cohd->dir = direction;
cohd->flags = flags;
cohd->desc.tx_submit = coh901318_tx_submit;
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 8bcb15fb959d..b48967b499da 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -45,6 +45,7 @@
* See Documentation/dmaengine.txt for more details
*/
+#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
@@ -61,9 +62,9 @@
#include <linux/slab.h>
static DEFINE_MUTEX(dma_list_mutex);
+static DEFINE_IDR(dma_idr);
static LIST_HEAD(dma_device_list);
static long dmaengine_ref_count;
-static struct idr dma_idr;
/* --- sysfs implementation --- */
@@ -509,8 +510,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v
dma_chan_name(chan));
list_del_rcu(&device->global_node);
} else if (err)
- pr_err("dmaengine: failed to get %s: (%d)\n",
- dma_chan_name(chan), err);
+ pr_debug("dmaengine: failed to get %s: (%d)\n",
+ dma_chan_name(chan), err);
else
break;
if (--device->privatecnt == 0)
@@ -1049,8 +1050,6 @@ EXPORT_SYMBOL_GPL(dma_run_dependencies);
static int __init dma_bus_init(void)
{
- idr_init(&dma_idr);
- mutex_init(&dma_list_mutex);
return class_register(&dma_devclass);
}
arch_initcall(dma_bus_init);
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index b4f5c32b6a47..eb1d8641cf5c 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -8,7 +8,9 @@
* published by the Free Software Foundation.
*/
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
+#include <linux/freezer.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/module.h>
@@ -250,6 +252,7 @@ static int dmatest_func(void *data)
int i;
thread_name = current->comm;
+ set_freezable_with_signal();
ret = -ENOMEM;
@@ -304,7 +307,8 @@ static int dmatest_func(void *data)
dma_addr_t dma_srcs[src_cnt];
dma_addr_t dma_dsts[dst_cnt];
struct completion cmp;
- unsigned long tmo = msecs_to_jiffies(timeout);
+ unsigned long start, tmo, end = 0 /* compiler... */;
+ bool reload = true;
u8 align = 0;
total_tests++;
@@ -403,7 +407,17 @@ static int dmatest_func(void *data)
}
dma_async_issue_pending(chan);
- tmo = wait_for_completion_timeout(&cmp, tmo);
+ do {
+ start = jiffies;
+ if (reload)
+ end = start + msecs_to_jiffies(timeout);
+ else if (end <= start)
+ end = start + 1;
+ tmo = wait_for_completion_interruptible_timeout(&cmp,
+ end - start);
+ reload = try_to_freeze();
+ } while (tmo == -ERESTARTSYS);
+
status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
if (tmo == 0) {
@@ -476,6 +490,8 @@ err_srcs:
pr_notice("%s: terminating after %u tests, %u failures (status %d)\n",
thread_name, total_tests, failed_tests, ret);
+ /* terminate all transfers on specified channels */
+ chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
if (iterations > 0)
while (!kthread_should_stop()) {
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit);
@@ -498,6 +514,10 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc)
list_del(&thread->node);
kfree(thread);
}
+
+ /* terminate all transfers on specified channels */
+ dtc->chan->device->device_control(dtc->chan, DMA_TERMINATE_ALL, 0);
+
kfree(dtc);
}
diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c
index 4d180ca9a1d8..9bfd6d360718 100644
--- a/drivers/dma/dw_dmac.c
+++ b/drivers/dma/dw_dmac.c
@@ -1407,12 +1407,11 @@ static int __init dw_probe(struct platform_device *pdev)
dw->all_chan_mask = (1 << pdata->nr_channels) - 1;
INIT_LIST_HEAD(&dw->dma.channels);
- for (i = 0; i < pdata->nr_channels; i++, dw->dma.chancnt++) {
+ for (i = 0; i < pdata->nr_channels; i++) {
struct dw_dma_chan *dwc = &dw->chan[i];
dwc->chan.device = &dw->dma;
dwc->chan.cookie = dwc->completed = 1;
- dwc->chan.chan_id = i;
if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING)
list_add_tail(&dwc->chan.device_node,
&dw->dma.channels);
@@ -1468,7 +1467,7 @@ static int __init dw_probe(struct platform_device *pdev)
dma_writel(dw, CFG, DW_CFG_DMA_EN);
printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n",
- dev_name(&pdev->dev), dw->dma.chancnt);
+ dev_name(&pdev->dev), pdata->nr_channels);
dma_async_device_register(&dw->dma);
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
new file mode 100644
index 000000000000..b47e2b803faf
--- /dev/null
+++ b/drivers/dma/ep93xx_dma.c
@@ -0,0 +1,1356 @@
+/*
+ * Driver for the Cirrus Logic EP93xx DMA Controller
+ *
+ * Copyright (C) 2011 Mika Westerberg
+ *
+ * DMA M2P implementation is based on the original
+ * arch/arm/mach-ep93xx/dma-m2p.c which has following copyrights:
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Copyright (C) 2006 Applied Data Systems
+ * Copyright (C) 2009 Ryan Mallon <rmallon@gmail.com>
+ *
+ * This driver is based on dw_dmac and amba-pl08x drivers.
+ *
+ * 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/clk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <mach/dma.h>
+
+/* M2P registers */
+#define M2P_CONTROL 0x0000
+#define M2P_CONTROL_STALLINT BIT(0)
+#define M2P_CONTROL_NFBINT BIT(1)
+#define M2P_CONTROL_CH_ERROR_INT BIT(3)
+#define M2P_CONTROL_ENABLE BIT(4)
+#define M2P_CONTROL_ICE BIT(6)
+
+#define M2P_INTERRUPT 0x0004
+#define M2P_INTERRUPT_STALL BIT(0)
+#define M2P_INTERRUPT_NFB BIT(1)
+#define M2P_INTERRUPT_ERROR BIT(3)
+
+#define M2P_PPALLOC 0x0008
+#define M2P_STATUS 0x000c
+
+#define M2P_MAXCNT0 0x0020
+#define M2P_BASE0 0x0024
+#define M2P_MAXCNT1 0x0030
+#define M2P_BASE1 0x0034
+
+#define M2P_STATE_IDLE 0
+#define M2P_STATE_STALL 1
+#define M2P_STATE_ON 2
+#define M2P_STATE_NEXT 3
+
+/* M2M registers */
+#define M2M_CONTROL 0x0000
+#define M2M_CONTROL_DONEINT BIT(2)
+#define M2M_CONTROL_ENABLE BIT(3)
+#define M2M_CONTROL_START BIT(4)
+#define M2M_CONTROL_DAH BIT(11)
+#define M2M_CONTROL_SAH BIT(12)
+#define M2M_CONTROL_PW_SHIFT 9
+#define M2M_CONTROL_PW_8 (0 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_PW_16 (1 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_PW_32 (2 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_PW_MASK (3 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_TM_SHIFT 13
+#define M2M_CONTROL_TM_TX (1 << M2M_CONTROL_TM_SHIFT)
+#define M2M_CONTROL_TM_RX (2 << M2M_CONTROL_TM_SHIFT)
+#define M2M_CONTROL_RSS_SHIFT 22
+#define M2M_CONTROL_RSS_SSPRX (1 << M2M_CONTROL_RSS_SHIFT)
+#define M2M_CONTROL_RSS_SSPTX (2 << M2M_CONTROL_RSS_SHIFT)
+#define M2M_CONTROL_RSS_IDE (3 << M2M_CONTROL_RSS_SHIFT)
+#define M2M_CONTROL_NO_HDSK BIT(24)
+#define M2M_CONTROL_PWSC_SHIFT 25
+
+#define M2M_INTERRUPT 0x0004
+#define M2M_INTERRUPT_DONEINT BIT(1)
+
+#define M2M_BCR0 0x0010
+#define M2M_BCR1 0x0014
+#define M2M_SAR_BASE0 0x0018
+#define M2M_SAR_BASE1 0x001c
+#define M2M_DAR_BASE0 0x002c
+#define M2M_DAR_BASE1 0x0030
+
+#define DMA_MAX_CHAN_BYTES 0xffff
+#define DMA_MAX_CHAN_DESCRIPTORS 32
+
+struct ep93xx_dma_engine;
+
+/**
+ * struct ep93xx_dma_desc - EP93xx specific transaction descriptor
+ * @src_addr: source address of the transaction
+ * @dst_addr: destination address of the transaction
+ * @size: size of the transaction (in bytes)
+ * @complete: this descriptor is completed
+ * @txd: dmaengine API descriptor
+ * @tx_list: list of linked descriptors
+ * @node: link used for putting this into a channel queue
+ */
+struct ep93xx_dma_desc {
+ u32 src_addr;
+ u32 dst_addr;
+ size_t size;
+ bool complete;
+ struct dma_async_tx_descriptor txd;
+ struct list_head tx_list;
+ struct list_head node;
+};
+
+/**
+ * struct ep93xx_dma_chan - an EP93xx DMA M2P/M2M channel
+ * @chan: dmaengine API channel
+ * @edma: pointer to to the engine device
+ * @regs: memory mapped registers
+ * @irq: interrupt number of the channel
+ * @clk: clock used by this channel
+ * @tasklet: channel specific tasklet used for callbacks
+ * @lock: lock protecting the fields following
+ * @flags: flags for the channel
+ * @buffer: which buffer to use next (0/1)
+ * @last_completed: last completed cookie value
+ * @active: flattened chain of descriptors currently being processed
+ * @queue: pending descriptors which are handled next
+ * @free_list: list of free descriptors which can be used
+ * @runtime_addr: physical address currently used as dest/src (M2M only). This
+ * is set via %DMA_SLAVE_CONFIG before slave operation is
+ * prepared
+ * @runtime_ctrl: M2M runtime values for the control register.
+ *
+ * As EP93xx DMA controller doesn't support real chained DMA descriptors we
+ * will have slightly different scheme here: @active points to a head of
+ * flattened DMA descriptor chain.
+ *
+ * @queue holds pending transactions. These are linked through the first
+ * descriptor in the chain. When a descriptor is moved to the @active queue,
+ * the first and chained descriptors are flattened into a single list.
+ *
+ * @chan.private holds pointer to &struct ep93xx_dma_data which contains
+ * necessary channel configuration information. For memcpy channels this must
+ * be %NULL.
+ */
+struct ep93xx_dma_chan {
+ struct dma_chan chan;
+ const struct ep93xx_dma_engine *edma;
+ void __iomem *regs;
+ int irq;
+ struct clk *clk;
+ struct tasklet_struct tasklet;
+ /* protects the fields following */
+ spinlock_t lock;
+ unsigned long flags;
+/* Channel is configured for cyclic transfers */
+#define EP93XX_DMA_IS_CYCLIC 0
+
+ int buffer;
+ dma_cookie_t last_completed;
+ struct list_head active;
+ struct list_head queue;
+ struct list_head free_list;
+ u32 runtime_addr;
+ u32 runtime_ctrl;
+};
+
+/**
+ * struct ep93xx_dma_engine - the EP93xx DMA engine instance
+ * @dma_dev: holds the dmaengine device
+ * @m2m: is this an M2M or M2P device
+ * @hw_setup: method which sets the channel up for operation
+ * @hw_shutdown: shuts the channel down and flushes whatever is left
+ * @hw_submit: pushes active descriptor(s) to the hardware
+ * @hw_interrupt: handle the interrupt
+ * @num_channels: number of channels for this instance
+ * @channels: array of channels
+ *
+ * There is one instance of this struct for the M2P channels and one for the
+ * M2M channels. hw_xxx() methods are used to perform operations which are
+ * different on M2M and M2P channels. These methods are called with channel
+ * lock held and interrupts disabled so they cannot sleep.
+ */
+struct ep93xx_dma_engine {
+ struct dma_device dma_dev;
+ bool m2m;
+ int (*hw_setup)(struct ep93xx_dma_chan *);
+ void (*hw_shutdown)(struct ep93xx_dma_chan *);
+ void (*hw_submit)(struct ep93xx_dma_chan *);
+ int (*hw_interrupt)(struct ep93xx_dma_chan *);
+#define INTERRUPT_UNKNOWN 0
+#define INTERRUPT_DONE 1
+#define INTERRUPT_NEXT_BUFFER 2
+
+ size_t num_channels;
+ struct ep93xx_dma_chan channels[];
+};
+
+static inline struct device *chan2dev(struct ep93xx_dma_chan *edmac)
+{
+ return &edmac->chan.dev->device;
+}
+
+static struct ep93xx_dma_chan *to_ep93xx_dma_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct ep93xx_dma_chan, chan);
+}
+
+/**
+ * ep93xx_dma_set_active - set new active descriptor chain
+ * @edmac: channel
+ * @desc: head of the new active descriptor chain
+ *
+ * Sets @desc to be the head of the new active descriptor chain. This is the
+ * chain which is processed next. The active list must be empty before calling
+ * this function.
+ *
+ * Called with @edmac->lock held and interrupts disabled.
+ */
+static void ep93xx_dma_set_active(struct ep93xx_dma_chan *edmac,
+ struct ep93xx_dma_desc *desc)
+{
+ BUG_ON(!list_empty(&edmac->active));
+
+ list_add_tail(&desc->node, &edmac->active);
+
+ /* Flatten the @desc->tx_list chain into @edmac->active list */
+ while (!list_empty(&desc->tx_list)) {
+ struct ep93xx_dma_desc *d = list_first_entry(&desc->tx_list,
+ struct ep93xx_dma_desc, node);
+
+ /*
+ * We copy the callback parameters from the first descriptor
+ * to all the chained descriptors. This way we can call the
+ * callback without having to find out the first descriptor in
+ * the chain. Useful for cyclic transfers.
+ */
+ d->txd.callback = desc->txd.callback;
+ d->txd.callback_param = desc->txd.callback_param;
+
+ list_move_tail(&d->node, &edmac->active);
+ }
+}
+
+/* Called with @edmac->lock held and interrupts disabled */
+static struct ep93xx_dma_desc *
+ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac)
+{
+ return list_first_entry(&edmac->active, struct ep93xx_dma_desc, node);
+}
+
+/**
+ * ep93xx_dma_advance_active - advances to the next active descriptor
+ * @edmac: channel
+ *
+ * Function advances active descriptor to the next in the @edmac->active and
+ * returns %true if we still have descriptors in the chain to process.
+ * Otherwise returns %false.
+ *
+ * When the channel is in cyclic mode always returns %true.
+ *
+ * Called with @edmac->lock held and interrupts disabled.
+ */
+static bool ep93xx_dma_advance_active(struct ep93xx_dma_chan *edmac)
+{
+ list_rotate_left(&edmac->active);
+
+ if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags))
+ return true;
+
+ /*
+ * If txd.cookie is set it means that we are back in the first
+ * descriptor in the chain and hence done with it.
+ */
+ return !ep93xx_dma_get_active(edmac)->txd.cookie;
+}
+
+/*
+ * M2P DMA implementation
+ */
+
+static void m2p_set_control(struct ep93xx_dma_chan *edmac, u32 control)
+{
+ writel(control, edmac->regs + M2P_CONTROL);
+ /*
+ * EP93xx User's Guide states that we must perform a dummy read after
+ * write to the control register.
+ */
+ readl(edmac->regs + M2P_CONTROL);
+}
+
+static int m2p_hw_setup(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_data *data = edmac->chan.private;
+ u32 control;
+
+ writel(data->port & 0xf, edmac->regs + M2P_PPALLOC);
+
+ control = M2P_CONTROL_CH_ERROR_INT | M2P_CONTROL_ICE
+ | M2P_CONTROL_ENABLE;
+ m2p_set_control(edmac, control);
+
+ return 0;
+}
+
+static inline u32 m2p_channel_state(struct ep93xx_dma_chan *edmac)
+{
+ return (readl(edmac->regs + M2P_STATUS) >> 4) & 0x3;
+}
+
+static void m2p_hw_shutdown(struct ep93xx_dma_chan *edmac)
+{
+ u32 control;
+
+ control = readl(edmac->regs + M2P_CONTROL);
+ control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
+ m2p_set_control(edmac, control);
+
+ while (m2p_channel_state(edmac) >= M2P_STATE_ON)
+ cpu_relax();
+
+ m2p_set_control(edmac, 0);
+
+ while (m2p_channel_state(edmac) == M2P_STATE_STALL)
+ cpu_relax();
+}
+
+static void m2p_fill_desc(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac);
+ u32 bus_addr;
+
+ if (ep93xx_dma_chan_direction(&edmac->chan) == DMA_TO_DEVICE)
+ bus_addr = desc->src_addr;
+ else
+ bus_addr = desc->dst_addr;
+
+ if (edmac->buffer == 0) {
+ writel(desc->size, edmac->regs + M2P_MAXCNT0);
+ writel(bus_addr, edmac->regs + M2P_BASE0);
+ } else {
+ writel(desc->size, edmac->regs + M2P_MAXCNT1);
+ writel(bus_addr, edmac->regs + M2P_BASE1);
+ }
+
+ edmac->buffer ^= 1;
+}
+
+static void m2p_hw_submit(struct ep93xx_dma_chan *edmac)
+{
+ u32 control = readl(edmac->regs + M2P_CONTROL);
+
+ m2p_fill_desc(edmac);
+ control |= M2P_CONTROL_STALLINT;
+
+ if (ep93xx_dma_advance_active(edmac)) {
+ m2p_fill_desc(edmac);
+ control |= M2P_CONTROL_NFBINT;
+ }
+
+ m2p_set_control(edmac, control);
+}
+
+static int m2p_hw_interrupt(struct ep93xx_dma_chan *edmac)
+{
+ u32 irq_status = readl(edmac->regs + M2P_INTERRUPT);
+ u32 control;
+
+ if (irq_status & M2P_INTERRUPT_ERROR) {
+ struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac);
+
+ /* Clear the error interrupt */
+ writel(1, edmac->regs + M2P_INTERRUPT);
+
+ /*
+ * It seems that there is no easy way of reporting errors back
+ * to client so we just report the error here and continue as
+ * usual.
+ *
+ * Revisit this when there is a mechanism to report back the
+ * errors.
+ */
+ dev_err(chan2dev(edmac),
+ "DMA transfer failed! Details:\n"
+ "\tcookie : %d\n"
+ "\tsrc_addr : 0x%08x\n"
+ "\tdst_addr : 0x%08x\n"
+ "\tsize : %zu\n",
+ desc->txd.cookie, desc->src_addr, desc->dst_addr,
+ desc->size);
+ }
+
+ switch (irq_status & (M2P_INTERRUPT_STALL | M2P_INTERRUPT_NFB)) {
+ case M2P_INTERRUPT_STALL:
+ /* Disable interrupts */
+ control = readl(edmac->regs + M2P_CONTROL);
+ control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
+ m2p_set_control(edmac, control);
+
+ return INTERRUPT_DONE;
+
+ case M2P_INTERRUPT_NFB:
+ if (ep93xx_dma_advance_active(edmac))
+ m2p_fill_desc(edmac);
+
+ return INTERRUPT_NEXT_BUFFER;
+ }
+
+ return INTERRUPT_UNKNOWN;
+}
+
+/*
+ * M2M DMA implementation
+ *
+ * For the M2M transfers we don't use NFB at all. This is because it simply
+ * doesn't work well with memcpy transfers. When you submit both buffers it is
+ * extremely unlikely that you get an NFB interrupt, but it instead reports
+ * DONE interrupt and both buffers are already transferred which means that we
+ * weren't able to update the next buffer.
+ *
+ * So for now we "simulate" NFB by just submitting buffer after buffer
+ * without double buffering.
+ */
+
+static int m2m_hw_setup(struct ep93xx_dma_chan *edmac)
+{
+ const struct ep93xx_dma_data *data = edmac->chan.private;
+ u32 control = 0;
+
+ if (!data) {
+ /* This is memcpy channel, nothing to configure */
+ writel(control, edmac->regs + M2M_CONTROL);
+ return 0;
+ }
+
+ switch (data->port) {
+ case EP93XX_DMA_SSP:
+ /*
+ * This was found via experimenting - anything less than 5
+ * causes the channel to perform only a partial transfer which
+ * leads to problems since we don't get DONE interrupt then.
+ */
+ control = (5 << M2M_CONTROL_PWSC_SHIFT);
+ control |= M2M_CONTROL_NO_HDSK;
+
+ if (data->direction == DMA_TO_DEVICE) {
+ control |= M2M_CONTROL_DAH;
+ control |= M2M_CONTROL_TM_TX;
+ control |= M2M_CONTROL_RSS_SSPTX;
+ } else {
+ control |= M2M_CONTROL_SAH;
+ control |= M2M_CONTROL_TM_RX;
+ control |= M2M_CONTROL_RSS_SSPRX;
+ }
+ break;
+
+ case EP93XX_DMA_IDE:
+ /*
+ * This IDE part is totally untested. Values below are taken
+ * from the EP93xx Users's Guide and might not be correct.
+ */
+ control |= M2M_CONTROL_NO_HDSK;
+ control |= M2M_CONTROL_RSS_IDE;
+ control |= M2M_CONTROL_PW_16;
+
+ if (data->direction == DMA_TO_DEVICE) {
+ /* Worst case from the UG */
+ control = (3 << M2M_CONTROL_PWSC_SHIFT);
+ control |= M2M_CONTROL_DAH;
+ control |= M2M_CONTROL_TM_TX;
+ } else {
+ control = (2 << M2M_CONTROL_PWSC_SHIFT);
+ control |= M2M_CONTROL_SAH;
+ control |= M2M_CONTROL_TM_RX;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ writel(control, edmac->regs + M2M_CONTROL);
+ return 0;
+}
+
+static void m2m_hw_shutdown(struct ep93xx_dma_chan *edmac)
+{
+ /* Just disable the channel */
+ writel(0, edmac->regs + M2M_CONTROL);
+}
+
+static void m2m_fill_desc(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac);
+
+ if (edmac->buffer == 0) {
+ writel(desc->src_addr, edmac->regs + M2M_SAR_BASE0);
+ writel(desc->dst_addr, edmac->regs + M2M_DAR_BASE0);
+ writel(desc->size, edmac->regs + M2M_BCR0);
+ } else {
+ writel(desc->src_addr, edmac->regs + M2M_SAR_BASE1);
+ writel(desc->dst_addr, edmac->regs + M2M_DAR_BASE1);
+ writel(desc->size, edmac->regs + M2M_BCR1);
+ }
+
+ edmac->buffer ^= 1;
+}
+
+static void m2m_hw_submit(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_data *data = edmac->chan.private;
+ u32 control = readl(edmac->regs + M2M_CONTROL);
+
+ /*
+ * Since we allow clients to configure PW (peripheral width) we always
+ * clear PW bits here and then set them according what is given in
+ * the runtime configuration.
+ */
+ control &= ~M2M_CONTROL_PW_MASK;
+ control |= edmac->runtime_ctrl;
+
+ m2m_fill_desc(edmac);
+ control |= M2M_CONTROL_DONEINT;
+
+ /*
+ * Now we can finally enable the channel. For M2M channel this must be
+ * done _after_ the BCRx registers are programmed.
+ */
+ control |= M2M_CONTROL_ENABLE;
+ writel(control, edmac->regs + M2M_CONTROL);
+
+ if (!data) {
+ /*
+ * For memcpy channels the software trigger must be asserted
+ * in order to start the memcpy operation.
+ */
+ control |= M2M_CONTROL_START;
+ writel(control, edmac->regs + M2M_CONTROL);
+ }
+}
+
+static int m2m_hw_interrupt(struct ep93xx_dma_chan *edmac)
+{
+ u32 control;
+
+ if (!(readl(edmac->regs + M2M_INTERRUPT) & M2M_INTERRUPT_DONEINT))
+ return INTERRUPT_UNKNOWN;
+
+ /* Clear the DONE bit */
+ writel(0, edmac->regs + M2M_INTERRUPT);
+
+ /* Disable interrupts and the channel */
+ control = readl(edmac->regs + M2M_CONTROL);
+ control &= ~(M2M_CONTROL_DONEINT | M2M_CONTROL_ENABLE);
+ writel(control, edmac->regs + M2M_CONTROL);
+
+ /*
+ * Since we only get DONE interrupt we have to find out ourselves
+ * whether there still is something to process. So we try to advance
+ * the chain an see whether it succeeds.
+ */
+ if (ep93xx_dma_advance_active(edmac)) {
+ edmac->edma->hw_submit(edmac);
+ return INTERRUPT_NEXT_BUFFER;
+ }
+
+ return INTERRUPT_DONE;
+}
+
+/*
+ * DMA engine API implementation
+ */
+
+static struct ep93xx_dma_desc *
+ep93xx_dma_desc_get(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_desc *desc, *_desc;
+ struct ep93xx_dma_desc *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ list_for_each_entry_safe(desc, _desc, &edmac->free_list, node) {
+ if (async_tx_test_ack(&desc->txd)) {
+ list_del_init(&desc->node);
+
+ /* Re-initialize the descriptor */
+ desc->src_addr = 0;
+ desc->dst_addr = 0;
+ desc->size = 0;
+ desc->complete = false;
+ desc->txd.cookie = 0;
+ desc->txd.callback = NULL;
+ desc->txd.callback_param = NULL;
+
+ ret = desc;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&edmac->lock, flags);
+ return ret;
+}
+
+static void ep93xx_dma_desc_put(struct ep93xx_dma_chan *edmac,
+ struct ep93xx_dma_desc *desc)
+{
+ if (desc) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ list_splice_init(&desc->tx_list, &edmac->free_list);
+ list_add(&desc->node, &edmac->free_list);
+ spin_unlock_irqrestore(&edmac->lock, flags);
+ }
+}
+
+/**
+ * ep93xx_dma_advance_work - start processing the next pending transaction
+ * @edmac: channel
+ *
+ * If we have pending transactions queued and we are currently idling, this
+ * function takes the next queued transaction from the @edmac->queue and
+ * pushes it to the hardware for execution.
+ */
+static void ep93xx_dma_advance_work(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_desc *new;
+ unsigned long flags;
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ if (!list_empty(&edmac->active) || list_empty(&edmac->queue)) {
+ spin_unlock_irqrestore(&edmac->lock, flags);
+ return;
+ }
+
+ /* Take the next descriptor from the pending queue */
+ new = list_first_entry(&edmac->queue, struct ep93xx_dma_desc, node);
+ list_del_init(&new->node);
+
+ ep93xx_dma_set_active(edmac, new);
+
+ /* Push it to the hardware */
+ edmac->edma->hw_submit(edmac);
+ spin_unlock_irqrestore(&edmac->lock, flags);
+}
+
+static void ep93xx_dma_unmap_buffers(struct ep93xx_dma_desc *desc)
+{
+ struct device *dev = desc->txd.chan->device->dev;
+
+ if (!(desc->txd.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
+ if (desc->txd.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
+ dma_unmap_single(dev, desc->src_addr, desc->size,
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_page(dev, desc->src_addr, desc->size,
+ DMA_TO_DEVICE);
+ }
+ if (!(desc->txd.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
+ if (desc->txd.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
+ dma_unmap_single(dev, desc->dst_addr, desc->size,
+ DMA_FROM_DEVICE);
+ else
+ dma_unmap_page(dev, desc->dst_addr, desc->size,
+ DMA_FROM_DEVICE);
+ }
+}
+
+static void ep93xx_dma_tasklet(unsigned long data)
+{
+ struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data;
+ struct ep93xx_dma_desc *desc, *d;
+ dma_async_tx_callback callback;
+ void *callback_param;
+ LIST_HEAD(list);
+
+ spin_lock_irq(&edmac->lock);
+ desc = ep93xx_dma_get_active(edmac);
+ if (desc->complete) {
+ edmac->last_completed = desc->txd.cookie;
+ list_splice_init(&edmac->active, &list);
+ }
+ spin_unlock_irq(&edmac->lock);
+
+ /* Pick up the next descriptor from the queue */
+ ep93xx_dma_advance_work(edmac);
+
+ callback = desc->txd.callback;
+ callback_param = desc->txd.callback_param;
+
+ /* Now we can release all the chained descriptors */
+ list_for_each_entry_safe(desc, d, &list, node) {
+ /*
+ * For the memcpy channels the API requires us to unmap the
+ * buffers unless requested otherwise.
+ */
+ if (!edmac->chan.private)
+ ep93xx_dma_unmap_buffers(desc);
+
+ ep93xx_dma_desc_put(edmac, desc);
+ }
+
+ if (callback)
+ callback(callback_param);
+}
+
+static irqreturn_t ep93xx_dma_interrupt(int irq, void *dev_id)
+{
+ struct ep93xx_dma_chan *edmac = dev_id;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ spin_lock(&edmac->lock);
+
+ switch (edmac->edma->hw_interrupt(edmac)) {
+ case INTERRUPT_DONE:
+ ep93xx_dma_get_active(edmac)->complete = true;
+ tasklet_schedule(&edmac->tasklet);
+ break;
+
+ case INTERRUPT_NEXT_BUFFER:
+ if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags))
+ tasklet_schedule(&edmac->tasklet);
+ break;
+
+ default:
+ dev_warn(chan2dev(edmac), "unknown interrupt!\n");
+ ret = IRQ_NONE;
+ break;
+ }
+
+ spin_unlock(&edmac->lock);
+ return ret;
+}
+
+/**
+ * ep93xx_dma_tx_submit - set the prepared descriptor(s) to be executed
+ * @tx: descriptor to be executed
+ *
+ * Function will execute given descriptor on the hardware or if the hardware
+ * is busy, queue the descriptor to be executed later on. Returns cookie which
+ * can be used to poll the status of the descriptor.
+ */
+static dma_cookie_t ep93xx_dma_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(tx->chan);
+ struct ep93xx_dma_desc *desc;
+ dma_cookie_t cookie;
+ unsigned long flags;
+
+ spin_lock_irqsave(&edmac->lock, flags);
+
+ cookie = edmac->chan.cookie;
+
+ if (++cookie < 0)
+ cookie = 1;
+
+ desc = container_of(tx, struct ep93xx_dma_desc, txd);
+
+ edmac->chan.cookie = cookie;
+ desc->txd.cookie = cookie;
+
+ /*
+ * If nothing is currently prosessed, we push this descriptor
+ * directly to the hardware. Otherwise we put the descriptor
+ * to the pending queue.
+ */
+ if (list_empty(&edmac->active)) {
+ ep93xx_dma_set_active(edmac, desc);
+ edmac->edma->hw_submit(edmac);
+ } else {
+ list_add_tail(&desc->node, &edmac->queue);
+ }
+
+ spin_unlock_irqrestore(&edmac->lock, flags);
+ return cookie;
+}
+
+/**
+ * ep93xx_dma_alloc_chan_resources - allocate resources for the channel
+ * @chan: channel to allocate resources
+ *
+ * Function allocates necessary resources for the given DMA channel and
+ * returns number of allocated descriptors for the channel. Negative errno
+ * is returned in case of failure.
+ */
+static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct ep93xx_dma_data *data = chan->private;
+ const char *name = dma_chan_name(chan);
+ int ret, i;
+
+ /* Sanity check the channel parameters */
+ if (!edmac->edma->m2m) {
+ if (!data)
+ return -EINVAL;
+ if (data->port < EP93XX_DMA_I2S1 ||
+ data->port > EP93XX_DMA_IRDA)
+ return -EINVAL;
+ if (data->direction != ep93xx_dma_chan_direction(chan))
+ return -EINVAL;
+ } else {
+ if (data) {
+ switch (data->port) {
+ case EP93XX_DMA_SSP:
+ case EP93XX_DMA_IDE:
+ if (data->direction != DMA_TO_DEVICE &&
+ data->direction != DMA_FROM_DEVICE)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (data && data->name)
+ name = data->name;
+
+ ret = clk_enable(edmac->clk);
+ if (ret)
+ return ret;
+
+ ret = request_irq(edmac->irq, ep93xx_dma_interrupt, 0, name, edmac);
+ if (ret)
+ goto fail_clk_disable;
+
+ spin_lock_irq(&edmac->lock);
+ edmac->last_completed = 1;
+ edmac->chan.cookie = 1;
+ ret = edmac->edma->hw_setup(edmac);
+ spin_unlock_irq(&edmac->lock);
+
+ if (ret)
+ goto fail_free_irq;
+
+ for (i = 0; i < DMA_MAX_CHAN_DESCRIPTORS; i++) {
+ struct ep93xx_dma_desc *desc;
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc) {
+ dev_warn(chan2dev(edmac), "not enough descriptors\n");
+ break;
+ }
+
+ INIT_LIST_HEAD(&desc->tx_list);
+
+ dma_async_tx_descriptor_init(&desc->txd, chan);
+ desc->txd.flags = DMA_CTRL_ACK;
+ desc->txd.tx_submit = ep93xx_dma_tx_submit;
+
+ ep93xx_dma_desc_put(edmac, desc);
+ }
+
+ return i;
+
+fail_free_irq:
+ free_irq(edmac->irq, edmac);
+fail_clk_disable:
+ clk_disable(edmac->clk);
+
+ return ret;
+}
+
+/**
+ * ep93xx_dma_free_chan_resources - release resources for the channel
+ * @chan: channel
+ *
+ * Function releases all the resources allocated for the given channel.
+ * The channel must be idle when this is called.
+ */
+static void ep93xx_dma_free_chan_resources(struct dma_chan *chan)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct ep93xx_dma_desc *desc, *d;
+ unsigned long flags;
+ LIST_HEAD(list);
+
+ BUG_ON(!list_empty(&edmac->active));
+ BUG_ON(!list_empty(&edmac->queue));
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ edmac->edma->hw_shutdown(edmac);
+ edmac->runtime_addr = 0;
+ edmac->runtime_ctrl = 0;
+ edmac->buffer = 0;
+ list_splice_init(&edmac->free_list, &list);
+ spin_unlock_irqrestore(&edmac->lock, flags);
+
+ list_for_each_entry_safe(desc, d, &list, node)
+ kfree(desc);
+
+ clk_disable(edmac->clk);
+ free_irq(edmac->irq, edmac);
+}
+
+/**
+ * ep93xx_dma_prep_dma_memcpy - prepare a memcpy DMA operation
+ * @chan: channel
+ * @dest: destination bus address
+ * @src: source bus address
+ * @len: size of the transaction
+ * @flags: flags for the descriptor
+ *
+ * Returns a valid DMA descriptor or %NULL in case of failure.
+ */
+static struct dma_async_tx_descriptor *
+ep93xx_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
+ dma_addr_t src, size_t len, unsigned long flags)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct ep93xx_dma_desc *desc, *first;
+ size_t bytes, offset;
+
+ first = NULL;
+ for (offset = 0; offset < len; offset += bytes) {
+ desc = ep93xx_dma_desc_get(edmac);
+ if (!desc) {
+ dev_warn(chan2dev(edmac), "couln't get descriptor\n");
+ goto fail;
+ }
+
+ bytes = min_t(size_t, len - offset, DMA_MAX_CHAN_BYTES);
+
+ desc->src_addr = src + offset;
+ desc->dst_addr = dest + offset;
+ desc->size = bytes;
+
+ if (!first)
+ first = desc;
+ else
+ list_add_tail(&desc->node, &first->tx_list);
+ }
+
+ first->txd.cookie = -EBUSY;
+ first->txd.flags = flags;
+
+ return &first->txd;
+fail:
+ ep93xx_dma_desc_put(edmac, first);
+ return NULL;
+}
+
+/**
+ * ep93xx_dma_prep_slave_sg - prepare a slave DMA operation
+ * @chan: channel
+ * @sgl: list of buffers to transfer
+ * @sg_len: number of entries in @sgl
+ * @dir: direction of tha DMA transfer
+ * @flags: flags for the descriptor
+ *
+ * Returns a valid DMA descriptor or %NULL in case of failure.
+ */
+static struct dma_async_tx_descriptor *
+ep93xx_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_data_direction dir,
+ unsigned long flags)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct ep93xx_dma_desc *desc, *first;
+ struct scatterlist *sg;
+ int i;
+
+ if (!edmac->edma->m2m && dir != ep93xx_dma_chan_direction(chan)) {
+ dev_warn(chan2dev(edmac),
+ "channel was configured with different direction\n");
+ return NULL;
+ }
+
+ if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) {
+ dev_warn(chan2dev(edmac),
+ "channel is already used for cyclic transfers\n");
+ return NULL;
+ }
+
+ first = NULL;
+ for_each_sg(sgl, sg, sg_len, i) {
+ size_t sg_len = sg_dma_len(sg);
+
+ if (sg_len > DMA_MAX_CHAN_BYTES) {
+ dev_warn(chan2dev(edmac), "too big transfer size %d\n",
+ sg_len);
+ goto fail;
+ }
+
+ desc = ep93xx_dma_desc_get(edmac);
+ if (!desc) {
+ dev_warn(chan2dev(edmac), "couln't get descriptor\n");
+ goto fail;
+ }
+
+ if (dir == DMA_TO_DEVICE) {
+ desc->src_addr = sg_dma_address(sg);
+ desc->dst_addr = edmac->runtime_addr;
+ } else {
+ desc->src_addr = edmac->runtime_addr;
+ desc->dst_addr = sg_dma_address(sg);
+ }
+ desc->size = sg_len;
+
+ if (!first)
+ first = desc;
+ else
+ list_add_tail(&desc->node, &first->tx_list);
+ }
+
+ first->txd.cookie = -EBUSY;
+ first->txd.flags = flags;
+
+ return &first->txd;
+
+fail:
+ ep93xx_dma_desc_put(edmac, first);
+ return NULL;
+}
+
+/**
+ * ep93xx_dma_prep_dma_cyclic - prepare a cyclic DMA operation
+ * @chan: channel
+ * @dma_addr: DMA mapped address of the buffer
+ * @buf_len: length of the buffer (in bytes)
+ * @period_len: lenght of a single period
+ * @dir: direction of the operation
+ *
+ * Prepares a descriptor for cyclic DMA operation. This means that once the
+ * descriptor is submitted, we will be submitting in a @period_len sized
+ * buffers and calling callback once the period has been elapsed. Transfer
+ * terminates only when client calls dmaengine_terminate_all() for this
+ * channel.
+ *
+ * Returns a valid DMA descriptor or %NULL in case of failure.
+ */
+static struct dma_async_tx_descriptor *
+ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
+ size_t buf_len, size_t period_len,
+ enum dma_data_direction dir)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct ep93xx_dma_desc *desc, *first;
+ size_t offset = 0;
+
+ if (!edmac->edma->m2m && dir != ep93xx_dma_chan_direction(chan)) {
+ dev_warn(chan2dev(edmac),
+ "channel was configured with different direction\n");
+ return NULL;
+ }
+
+ if (test_and_set_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) {
+ dev_warn(chan2dev(edmac),
+ "channel is already used for cyclic transfers\n");
+ return NULL;
+ }
+
+ if (period_len > DMA_MAX_CHAN_BYTES) {
+ dev_warn(chan2dev(edmac), "too big period length %d\n",
+ period_len);
+ return NULL;
+ }
+
+ /* Split the buffer into period size chunks */
+ first = NULL;
+ for (offset = 0; offset < buf_len; offset += period_len) {
+ desc = ep93xx_dma_desc_get(edmac);
+ if (!desc) {
+ dev_warn(chan2dev(edmac), "couln't get descriptor\n");
+ goto fail;
+ }
+
+ if (dir == DMA_TO_DEVICE) {
+ desc->src_addr = dma_addr + offset;
+ desc->dst_addr = edmac->runtime_addr;
+ } else {
+ desc->src_addr = edmac->runtime_addr;
+ desc->dst_addr = dma_addr + offset;
+ }
+
+ desc->size = period_len;
+
+ if (!first)
+ first = desc;
+ else
+ list_add_tail(&desc->node, &first->tx_list);
+ }
+
+ first->txd.cookie = -EBUSY;
+
+ return &first->txd;
+
+fail:
+ ep93xx_dma_desc_put(edmac, first);
+ return NULL;
+}
+
+/**
+ * ep93xx_dma_terminate_all - terminate all transactions
+ * @edmac: channel
+ *
+ * Stops all DMA transactions. All descriptors are put back to the
+ * @edmac->free_list and callbacks are _not_ called.
+ */
+static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_desc *desc, *_d;
+ unsigned long flags;
+ LIST_HEAD(list);
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ /* First we disable and flush the DMA channel */
+ edmac->edma->hw_shutdown(edmac);
+ clear_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags);
+ list_splice_init(&edmac->active, &list);
+ list_splice_init(&edmac->queue, &list);
+ /*
+ * We then re-enable the channel. This way we can continue submitting
+ * the descriptors by just calling ->hw_submit() again.
+ */
+ edmac->edma->hw_setup(edmac);
+ spin_unlock_irqrestore(&edmac->lock, flags);
+
+ list_for_each_entry_safe(desc, _d, &list, node)
+ ep93xx_dma_desc_put(edmac, desc);
+
+ return 0;
+}
+
+static int ep93xx_dma_slave_config(struct ep93xx_dma_chan *edmac,
+ struct dma_slave_config *config)
+{
+ enum dma_slave_buswidth width;
+ unsigned long flags;
+ u32 addr, ctrl;
+
+ if (!edmac->edma->m2m)
+ return -EINVAL;
+
+ switch (config->direction) {
+ case DMA_FROM_DEVICE:
+ width = config->src_addr_width;
+ addr = config->src_addr;
+ break;
+
+ case DMA_TO_DEVICE:
+ width = config->dst_addr_width;
+ addr = config->dst_addr;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ ctrl = 0;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ ctrl = M2M_CONTROL_PW_16;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ ctrl = M2M_CONTROL_PW_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ edmac->runtime_addr = addr;
+ edmac->runtime_ctrl = ctrl;
+ spin_unlock_irqrestore(&edmac->lock, flags);
+
+ return 0;
+}
+
+/**
+ * ep93xx_dma_control - manipulate all pending operations on a channel
+ * @chan: channel
+ * @cmd: control command to perform
+ * @arg: optional argument
+ *
+ * Controls the channel. Function returns %0 in case of success or negative
+ * error in case of failure.
+ */
+static int ep93xx_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct dma_slave_config *config;
+
+ switch (cmd) {
+ case DMA_TERMINATE_ALL:
+ return ep93xx_dma_terminate_all(edmac);
+
+ case DMA_SLAVE_CONFIG:
+ config = (struct dma_slave_config *)arg;
+ return ep93xx_dma_slave_config(edmac, config);
+
+ default:
+ break;
+ }
+
+ return -ENOSYS;
+}
+
+/**
+ * ep93xx_dma_tx_status - check if a transaction is completed
+ * @chan: channel
+ * @cookie: transaction specific cookie
+ * @state: state of the transaction is stored here if given
+ *
+ * This function can be used to query state of a given transaction.
+ */
+static enum dma_status ep93xx_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *state)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ dma_cookie_t last_used, last_completed;
+ enum dma_status ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ last_used = chan->cookie;
+ last_completed = edmac->last_completed;
+ spin_unlock_irqrestore(&edmac->lock, flags);
+
+ ret = dma_async_is_complete(cookie, last_completed, last_used);
+ dma_set_tx_state(state, last_completed, last_used, 0);
+
+ return ret;
+}
+
+/**
+ * ep93xx_dma_issue_pending - push pending transactions to the hardware
+ * @chan: channel
+ *
+ * When this function is called, all pending transactions are pushed to the
+ * hardware and executed.
+ */
+static void ep93xx_dma_issue_pending(struct dma_chan *chan)
+{
+ ep93xx_dma_advance_work(to_ep93xx_dma_chan(chan));
+}
+
+static int __init ep93xx_dma_probe(struct platform_device *pdev)
+{
+ struct ep93xx_dma_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct ep93xx_dma_engine *edma;
+ struct dma_device *dma_dev;
+ size_t edma_size;
+ int ret, i;
+
+ edma_size = pdata->num_channels * sizeof(struct ep93xx_dma_chan);
+ edma = kzalloc(sizeof(*edma) + edma_size, GFP_KERNEL);
+ if (!edma)
+ return -ENOMEM;
+
+ dma_dev = &edma->dma_dev;
+ edma->m2m = platform_get_device_id(pdev)->driver_data;
+ edma->num_channels = pdata->num_channels;
+
+ INIT_LIST_HEAD(&dma_dev->channels);
+ for (i = 0; i < pdata->num_channels; i++) {
+ const struct ep93xx_dma_chan_data *cdata = &pdata->channels[i];
+ struct ep93xx_dma_chan *edmac = &edma->channels[i];
+
+ edmac->chan.device = dma_dev;
+ edmac->regs = cdata->base;
+ edmac->irq = cdata->irq;
+ edmac->edma = edma;
+
+ edmac->clk = clk_get(NULL, cdata->name);
+ if (IS_ERR(edmac->clk)) {
+ dev_warn(&pdev->dev, "failed to get clock for %s\n",
+ cdata->name);
+ continue;
+ }
+
+ spin_lock_init(&edmac->lock);
+ INIT_LIST_HEAD(&edmac->active);
+ INIT_LIST_HEAD(&edmac->queue);
+ INIT_LIST_HEAD(&edmac->free_list);
+ tasklet_init(&edmac->tasklet, ep93xx_dma_tasklet,
+ (unsigned long)edmac);
+
+ list_add_tail(&edmac->chan.device_node,
+ &dma_dev->channels);
+ }
+
+ dma_cap_zero(dma_dev->cap_mask);
+ dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
+ dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask);
+
+ dma_dev->dev = &pdev->dev;
+ dma_dev->device_alloc_chan_resources = ep93xx_dma_alloc_chan_resources;
+ dma_dev->device_free_chan_resources = ep93xx_dma_free_chan_resources;
+ dma_dev->device_prep_slave_sg = ep93xx_dma_prep_slave_sg;
+ dma_dev->device_prep_dma_cyclic = ep93xx_dma_prep_dma_cyclic;
+ dma_dev->device_control = ep93xx_dma_control;
+ dma_dev->device_issue_pending = ep93xx_dma_issue_pending;
+ dma_dev->device_tx_status = ep93xx_dma_tx_status;
+
+ dma_set_max_seg_size(dma_dev->dev, DMA_MAX_CHAN_BYTES);
+
+ if (edma->m2m) {
+ dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
+ dma_dev->device_prep_dma_memcpy = ep93xx_dma_prep_dma_memcpy;
+
+ edma->hw_setup = m2m_hw_setup;
+ edma->hw_shutdown = m2m_hw_shutdown;
+ edma->hw_submit = m2m_hw_submit;
+ edma->hw_interrupt = m2m_hw_interrupt;
+ } else {
+ dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask);
+
+ edma->hw_setup = m2p_hw_setup;
+ edma->hw_shutdown = m2p_hw_shutdown;
+ edma->hw_submit = m2p_hw_submit;
+ edma->hw_interrupt = m2p_hw_interrupt;
+ }
+
+ ret = dma_async_device_register(dma_dev);
+ if (unlikely(ret)) {
+ for (i = 0; i < edma->num_channels; i++) {
+ struct ep93xx_dma_chan *edmac = &edma->channels[i];
+ if (!IS_ERR_OR_NULL(edmac->clk))
+ clk_put(edmac->clk);
+ }
+ kfree(edma);
+ } else {
+ dev_info(dma_dev->dev, "EP93xx M2%s DMA ready\n",
+ edma->m2m ? "M" : "P");
+ }
+
+ return ret;
+}
+
+static struct platform_device_id ep93xx_dma_driver_ids[] = {
+ { "ep93xx-dma-m2p", 0 },
+ { "ep93xx-dma-m2m", 1 },
+ { },
+};
+
+static struct platform_driver ep93xx_dma_driver = {
+ .driver = {
+ .name = "ep93xx-dma",
+ },
+ .id_table = ep93xx_dma_driver_ids,
+};
+
+static int __init ep93xx_dma_module_init(void)
+{
+ return platform_driver_probe(&ep93xx_dma_driver, ep93xx_dma_probe);
+}
+subsys_initcall(ep93xx_dma_module_init);
+
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
+MODULE_DESCRIPTION("EP93xx DMA driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index e18eaabe92b9..4be55f9bb6c1 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -14,6 +14,7 @@
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
@@ -23,6 +24,7 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
+#include <linux/module.h>
#include <asm/irq.h>
#include <mach/dma-v1.h>
@@ -135,7 +137,8 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
if (ret)
return ret;
- imx_dma_config_burstlen(imxdmac->imxdma_channel, imxdmac->watermark_level);
+ imx_dma_config_burstlen(imxdmac->imxdma_channel,
+ imxdmac->watermark_level * imxdmac->word_size);
return 0;
default:
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index b6d1455fa936..f993955a640c 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -18,6 +18,7 @@
*/
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
@@ -32,6 +33,9 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
#include <asm/irq.h>
#include <mach/sdma.h>
@@ -65,8 +69,8 @@
#define SDMA_ONCE_RTB 0x060
#define SDMA_XTRIG_CONF1 0x070
#define SDMA_XTRIG_CONF2 0x074
-#define SDMA_CHNENBL0_V2 0x200
-#define SDMA_CHNENBL0_V1 0x080
+#define SDMA_CHNENBL0_IMX35 0x200
+#define SDMA_CHNENBL0_IMX31 0x080
#define SDMA_CHNPRI_0 0x100
/*
@@ -299,21 +303,47 @@ struct sdma_firmware_header {
u32 ram_code_size;
};
+enum sdma_devtype {
+ IMX31_SDMA, /* runs on i.mx31 */
+ IMX35_SDMA, /* runs on i.mx35 and later */
+};
+
struct sdma_engine {
struct device *dev;
struct device_dma_parameters dma_parms;
struct sdma_channel channel[MAX_DMA_CHANNELS];
struct sdma_channel_control *channel_control;
void __iomem *regs;
- unsigned int version;
+ enum sdma_devtype devtype;
unsigned int num_events;
struct sdma_context_data *context;
dma_addr_t context_phys;
struct dma_device dma_device;
struct clk *clk;
+ struct mutex channel_0_lock;
struct sdma_script_start_addrs *script_addrs;
};
+static struct platform_device_id sdma_devtypes[] = {
+ {
+ .name = "imx31-sdma",
+ .driver_data = IMX31_SDMA,
+ }, {
+ .name = "imx35-sdma",
+ .driver_data = IMX35_SDMA,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, sdma_devtypes);
+
+static const struct of_device_id sdma_dt_ids[] = {
+ { .compatible = "fsl,imx31-sdma", .data = &sdma_devtypes[IMX31_SDMA], },
+ { .compatible = "fsl,imx35-sdma", .data = &sdma_devtypes[IMX35_SDMA], },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sdma_dt_ids);
+
#define SDMA_H_CONFIG_DSPDMA (1 << 12) /* indicates if the DSPDMA is used */
#define SDMA_H_CONFIG_RTD_PINS (1 << 11) /* indicates if Real-Time Debug pins are enabled */
#define SDMA_H_CONFIG_ACR (1 << 4) /* indicates if AHB freq /core freq = 2 or 1 */
@@ -321,8 +351,8 @@ struct sdma_engine {
static inline u32 chnenbl_ofs(struct sdma_engine *sdma, unsigned int event)
{
- u32 chnenbl0 = (sdma->version == 2 ? SDMA_CHNENBL0_V2 : SDMA_CHNENBL0_V1);
-
+ u32 chnenbl0 = (sdma->devtype == IMX31_SDMA ? SDMA_CHNENBL0_IMX31 :
+ SDMA_CHNENBL0_IMX35);
return chnenbl0 + event * 4;
}
@@ -388,11 +418,15 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
dma_addr_t buf_phys;
int ret;
+ mutex_lock(&sdma->channel_0_lock);
+
buf_virt = dma_alloc_coherent(NULL,
size,
&buf_phys, GFP_KERNEL);
- if (!buf_virt)
- return -ENOMEM;
+ if (!buf_virt) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
bd0->mode.command = C0_SETPM;
bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD;
@@ -406,6 +440,9 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
dma_free_coherent(NULL, size, buf_virt, buf_phys);
+err_out:
+ mutex_unlock(&sdma->channel_0_lock);
+
return ret;
}
@@ -629,6 +666,8 @@ static int sdma_load_context(struct sdma_channel *sdmac)
dev_dbg(sdma->dev, "event_mask0 = 0x%08x\n", sdmac->event_mask0);
dev_dbg(sdma->dev, "event_mask1 = 0x%08x\n", sdmac->event_mask1);
+ mutex_lock(&sdma->channel_0_lock);
+
memset(context, 0, sizeof(*context));
context->channel_state.pc = load_address;
@@ -649,6 +688,8 @@ static int sdma_load_context(struct sdma_channel *sdmac)
ret = sdma_run_channel(&sdma->channel[0]);
+ mutex_unlock(&sdma->channel_0_lock);
+
return ret;
}
@@ -1104,26 +1145,17 @@ static void sdma_add_scripts(struct sdma_engine *sdma,
saddr_arr[i] = addr_arr[i];
}
-static int __init sdma_get_firmware(struct sdma_engine *sdma,
- const char *cpu_name, int to_version)
+static void sdma_load_firmware(const struct firmware *fw, void *context)
{
- const struct firmware *fw;
- char *fwname;
+ struct sdma_engine *sdma = context;
const struct sdma_firmware_header *header;
- int ret;
const struct sdma_script_start_addrs *addr;
unsigned short *ram_code;
- fwname = kasprintf(GFP_KERNEL, "sdma-%s-to%d.bin", cpu_name, to_version);
- if (!fwname)
- return -ENOMEM;
-
- ret = request_firmware(&fw, fwname, sdma->dev);
- if (ret) {
- kfree(fwname);
- return ret;
+ if (!fw) {
+ dev_err(sdma->dev, "firmware not found\n");
+ return;
}
- kfree(fwname);
if (fw->size < sizeof(*header))
goto err_firmware;
@@ -1153,6 +1185,16 @@ static int __init sdma_get_firmware(struct sdma_engine *sdma,
err_firmware:
release_firmware(fw);
+}
+
+static int __init sdma_get_firmware(struct sdma_engine *sdma,
+ const char *fw_name)
+{
+ int ret;
+
+ ret = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_HOTPLUG, fw_name, sdma->dev,
+ GFP_KERNEL, sdma, sdma_load_firmware);
return ret;
}
@@ -1162,15 +1204,16 @@ static int __init sdma_init(struct sdma_engine *sdma)
int i, ret;
dma_addr_t ccb_phys;
- switch (sdma->version) {
- case 1:
+ switch (sdma->devtype) {
+ case IMX31_SDMA:
sdma->num_events = 32;
break;
- case 2:
+ case IMX35_SDMA:
sdma->num_events = 48;
break;
default:
- dev_err(sdma->dev, "Unknown version %d. aborting\n", sdma->version);
+ dev_err(sdma->dev, "Unknown sdma type %d. aborting\n",
+ sdma->devtype);
return -ENODEV;
}
@@ -1239,22 +1282,29 @@ err_dma_alloc:
static int __init sdma_probe(struct platform_device *pdev)
{
+ const struct of_device_id *of_id =
+ of_match_device(sdma_dt_ids, &pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
+ const char *fw_name;
int ret;
int irq;
struct resource *iores;
struct sdma_platform_data *pdata = pdev->dev.platform_data;
int i;
struct sdma_engine *sdma;
+ s32 *saddr_arr;
sdma = kzalloc(sizeof(*sdma), GFP_KERNEL);
if (!sdma)
return -ENOMEM;
+ mutex_init(&sdma->channel_0_lock);
+
sdma->dev = &pdev->dev;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- if (!iores || irq < 0 || !pdata) {
+ if (!iores || irq < 0) {
ret = -EINVAL;
goto err_irq;
}
@@ -1281,10 +1331,19 @@ static int __init sdma_probe(struct platform_device *pdev)
goto err_request_irq;
sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL);
- if (!sdma->script_addrs)
+ if (!sdma->script_addrs) {
+ ret = -ENOMEM;
goto err_alloc;
+ }
+
+ /* initially no scripts available */
+ saddr_arr = (s32 *)sdma->script_addrs;
+ for (i = 0; i < SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; i++)
+ saddr_arr[i] = -EINVAL;
- sdma->version = pdata->sdma_version;
+ if (of_id)
+ pdev->id_entry = of_id->data;
+ sdma->devtype = pdev->id_entry->driver_data;
dma_cap_set(DMA_SLAVE, sdma->dma_device.cap_mask);
dma_cap_set(DMA_CYCLIC, sdma->dma_device.cap_mask);
@@ -1314,10 +1373,30 @@ static int __init sdma_probe(struct platform_device *pdev)
if (ret)
goto err_init;
- if (pdata->script_addrs)
+ if (pdata && pdata->script_addrs)
sdma_add_scripts(sdma, pdata->script_addrs);
- sdma_get_firmware(sdma, pdata->cpu_name, pdata->to_version);
+ if (pdata) {
+ sdma_get_firmware(sdma, pdata->fw_name);
+ } else {
+ /*
+ * Because that device tree does not encode ROM script address,
+ * the RAM script in firmware is mandatory for device tree
+ * probe, otherwise it fails.
+ */
+ ret = of_property_read_string(np, "fsl,sdma-ram-script-name",
+ &fw_name);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get firmware name\n");
+ goto err_init;
+ }
+
+ ret = sdma_get_firmware(sdma, fw_name);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get firmware\n");
+ goto err_init;
+ }
+ }
sdma->dma_device.dev = &pdev->dev;
@@ -1365,7 +1444,9 @@ static int __exit sdma_remove(struct platform_device *pdev)
static struct platform_driver sdma_driver = {
.driver = {
.name = "imx-sdma",
+ .of_match_table = sdma_dt_ids,
},
+ .id_table = sdma_devtypes,
.remove = __exit_p(sdma_remove),
};
diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c
index f653517ef744..19a0c64d45d3 100644
--- a/drivers/dma/intel_mid_dma.c
+++ b/drivers/dma/intel_mid_dma.c
@@ -27,6 +27,7 @@
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <linux/intel_mid_dma.h>
+#include <linux/module.h>
#define MAX_CHAN 4 /*max ch across controllers*/
#include "intel_mid_dma_regs.h"
@@ -115,16 +116,15 @@ DMAC1 interrupt Functions*/
/**
* dmac1_mask_periphral_intr - mask the periphral interrupt
- * @midc: dma channel for which masking is required
+ * @mid: dma device for which masking is required
*
* Masks the DMA periphral interrupt
* this is valid for DMAC1 family controllers only
* This controller should have periphral mask registers already mapped
*/
-static void dmac1_mask_periphral_intr(struct intel_mid_dma_chan *midc)
+static void dmac1_mask_periphral_intr(struct middma_device *mid)
{
u32 pimr;
- struct middma_device *mid = to_middma_device(midc->chan.device);
if (mid->pimr_mask) {
pimr = readl(mid->mask_reg + LNW_PERIPHRAL_MASK);
@@ -184,7 +184,6 @@ static void enable_dma_interrupt(struct intel_mid_dma_chan *midc)
static void disable_dma_interrupt(struct intel_mid_dma_chan *midc)
{
/*Check LPE PISR, make sure fwd is disabled*/
- dmac1_mask_periphral_intr(midc);
iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_BLOCK);
iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_TFR);
iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_ERR);
@@ -1114,7 +1113,6 @@ static int mid_setup_dma(struct pci_dev *pdev)
midch->chan.device = &dma->common;
midch->chan.cookie = 1;
- midch->chan.chan_id = i;
midch->ch_id = dma->chan_base + i;
pr_debug("MDMA:Init CH %d, ID %d\n", i, midch->ch_id);
@@ -1150,7 +1148,6 @@ static int mid_setup_dma(struct pci_dev *pdev)
dma_cap_set(DMA_SLAVE, dma->common.cap_mask);
dma_cap_set(DMA_PRIVATE, dma->common.cap_mask);
dma->common.dev = &pdev->dev;
- dma->common.chancnt = dma->max_chan;
dma->common.device_alloc_chan_resources =
intel_mid_dma_alloc_chan_resources;
@@ -1350,8 +1347,8 @@ int dma_suspend(struct pci_dev *pci, pm_message_t state)
if (device->ch[i].in_use)
return -EAGAIN;
}
+ dmac1_mask_periphral_intr(device);
device->state = SUSPENDED;
- pci_set_drvdata(pci, device);
pci_save_state(pci);
pci_disable_device(pci);
pci_set_power_state(pci, PCI_D3hot);
@@ -1380,7 +1377,6 @@ int dma_resume(struct pci_dev *pci)
}
device->state = RUNNING;
iowrite32(REG_BIT0, device->dma_base + DMA_CFG);
- pci_set_drvdata(pci, device);
return 0;
}
diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c
index d845dc4b7103..f519c93a61e7 100644
--- a/drivers/dma/ioat/dma_v3.c
+++ b/drivers/dma/ioat/dma_v3.c
@@ -73,10 +73,10 @@
/* provide a lookup table for setting the source address in the base or
* extended descriptor of an xor or pq descriptor
*/
-static const u8 xor_idx_to_desc __read_mostly = 0xd0;
-static const u8 xor_idx_to_field[] __read_mostly = { 1, 4, 5, 6, 7, 0, 1, 2 };
-static const u8 pq_idx_to_desc __read_mostly = 0xf8;
-static const u8 pq_idx_to_field[] __read_mostly = { 1, 4, 5, 0, 1, 2, 4, 5 };
+static const u8 xor_idx_to_desc = 0xe0;
+static const u8 xor_idx_to_field[] = { 1, 4, 5, 6, 7, 0, 1, 2 };
+static const u8 pq_idx_to_desc = 0xf8;
+static const u8 pq_idx_to_field[] = { 1, 4, 5, 0, 1, 2, 4, 5 };
static dma_addr_t xor_get_src(struct ioat_raw_descriptor *descs[2], int idx)
{
diff --git a/drivers/dma/ioat/pci.c b/drivers/dma/ioat/pci.c
index fab37d1cf48d..5e3a40f79945 100644
--- a/drivers/dma/ioat/pci.c
+++ b/drivers/dma/ioat/pci.c
@@ -72,6 +72,17 @@ static struct pci_device_id ioat_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF8) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF9) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB0) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB1) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB2) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB3) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB4) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB5) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB6) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB7) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB8) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB9) },
+
{ 0, }
};
MODULE_DEVICE_TABLE(pci, ioat_pci_tbl);
diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c
index c1a125e7d1df..0e5ef33f90a1 100644
--- a/drivers/dma/ipu/ipu_idmac.c
+++ b/drivers/dma/ipu/ipu_idmac.c
@@ -9,6 +9,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/err.h>
@@ -20,6 +21,7 @@
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/module.h>
#include <mach/ipu.h>
@@ -1306,6 +1308,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) {
callback = descnew->txd.callback;
callback_param = descnew->txd.callback_param;
+ list_del_init(&descnew->list);
spin_unlock(&ichan->lock);
if (callback)
callback(callback_param);
@@ -1427,39 +1430,58 @@ static int __idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
{
struct idmac_channel *ichan = to_idmac_chan(chan);
struct idmac *idmac = to_idmac(chan->device);
+ struct ipu *ipu = to_ipu(idmac);
+ struct list_head *list, *tmp;
unsigned long flags;
int i;
- /* Only supports DMA_TERMINATE_ALL */
- if (cmd != DMA_TERMINATE_ALL)
- return -ENXIO;
+ switch (cmd) {
+ case DMA_PAUSE:
+ spin_lock_irqsave(&ipu->lock, flags);
+ ipu_ic_disable_task(ipu, chan->chan_id);
- ipu_disable_channel(idmac, ichan,
- ichan->status >= IPU_CHANNEL_ENABLED);
+ /* Return all descriptors into "prepared" state */
+ list_for_each_safe(list, tmp, &ichan->queue)
+ list_del_init(list);
- tasklet_disable(&to_ipu(idmac)->tasklet);
+ ichan->sg[0] = NULL;
+ ichan->sg[1] = NULL;
- /* ichan->queue is modified in ISR, have to spinlock */
- spin_lock_irqsave(&ichan->lock, flags);
- list_splice_init(&ichan->queue, &ichan->free_list);
+ spin_unlock_irqrestore(&ipu->lock, flags);
- if (ichan->desc)
- for (i = 0; i < ichan->n_tx_desc; i++) {
- struct idmac_tx_desc *desc = ichan->desc + i;
- if (list_empty(&desc->list))
- /* Descriptor was prepared, but not submitted */
- list_add(&desc->list, &ichan->free_list);
+ ichan->status = IPU_CHANNEL_INITIALIZED;
+ break;
+ case DMA_TERMINATE_ALL:
+ ipu_disable_channel(idmac, ichan,
+ ichan->status >= IPU_CHANNEL_ENABLED);
- async_tx_clear_ack(&desc->txd);
- }
+ tasklet_disable(&ipu->tasklet);
- ichan->sg[0] = NULL;
- ichan->sg[1] = NULL;
- spin_unlock_irqrestore(&ichan->lock, flags);
+ /* ichan->queue is modified in ISR, have to spinlock */
+ spin_lock_irqsave(&ichan->lock, flags);
+ list_splice_init(&ichan->queue, &ichan->free_list);
- tasklet_enable(&to_ipu(idmac)->tasklet);
+ if (ichan->desc)
+ for (i = 0; i < ichan->n_tx_desc; i++) {
+ struct idmac_tx_desc *desc = ichan->desc + i;
+ if (list_empty(&desc->list))
+ /* Descriptor was prepared, but not submitted */
+ list_add(&desc->list, &ichan->free_list);
- ichan->status = IPU_CHANNEL_INITIALIZED;
+ async_tx_clear_ack(&desc->txd);
+ }
+
+ ichan->sg[0] = NULL;
+ ichan->sg[1] = NULL;
+ spin_unlock_irqrestore(&ichan->lock, flags);
+
+ tasklet_enable(&ipu->tasklet);
+
+ ichan->status = IPU_CHANNEL_INITIALIZED;
+ break;
+ default:
+ return -ENOSYS;
+ }
return 0;
}
@@ -1662,7 +1684,6 @@ static void __exit ipu_idmac_exit(struct ipu *ipu)
struct idmac_channel *ichan = ipu->channel + i;
idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL, 0);
- idmac_prep_slave_sg(&ichan->dma_chan, NULL, 0, DMA_NONE, 0);
}
dma_async_device_unregister(&idmac->dma);
@@ -1705,16 +1726,14 @@ static int __init ipu_probe(struct platform_device *pdev)
ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base);
/* Remap IPU common registers */
- ipu_data.reg_ipu = ioremap(mem_ipu->start,
- mem_ipu->end - mem_ipu->start + 1);
+ ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu));
if (!ipu_data.reg_ipu) {
ret = -ENOMEM;
goto err_ioremap_ipu;
}
/* Remap Image Converter and Image DMA Controller registers */
- ipu_data.reg_ic = ioremap(mem_ic->start,
- mem_ic->end - mem_ic->start + 1);
+ ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic));
if (!ipu_data.reg_ic) {
ret = -ENOMEM;
goto err_ioremap_ic;
diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c
index ab8a4eff072a..a71f55e72be9 100644
--- a/drivers/dma/ipu/ipu_irq.c
+++ b/drivers/dma/ipu/ipu_irq.c
@@ -81,7 +81,7 @@ static struct ipu_irq_map irq_map[CONFIG_MX3_IPU_IRQS];
/* Protects allocations from the above array of maps */
static DEFINE_MUTEX(map_lock);
/* Protects register accesses and individual mappings */
-static DEFINE_SPINLOCK(bank_lock);
+static DEFINE_RAW_SPINLOCK(bank_lock);
static struct ipu_irq_map *src2map(unsigned int src)
{
@@ -101,11 +101,11 @@ static void ipu_irq_unmask(struct irq_data *d)
uint32_t reg;
unsigned long lock_flags;
- spin_lock_irqsave(&bank_lock, lock_flags);
+ raw_spin_lock_irqsave(&bank_lock, lock_flags);
bank = map->bank;
if (!bank) {
- spin_unlock_irqrestore(&bank_lock, lock_flags);
+ raw_spin_unlock_irqrestore(&bank_lock, lock_flags);
pr_err("IPU: %s(%u) - unmapped!\n", __func__, d->irq);
return;
}
@@ -114,7 +114,7 @@ static void ipu_irq_unmask(struct irq_data *d)
reg |= (1UL << (map->source & 31));
ipu_write_reg(bank->ipu, reg, bank->control);
- spin_unlock_irqrestore(&bank_lock, lock_flags);
+ raw_spin_unlock_irqrestore(&bank_lock, lock_flags);
}
static void ipu_irq_mask(struct irq_data *d)
@@ -124,11 +124,11 @@ static void ipu_irq_mask(struct irq_data *d)
uint32_t reg;
unsigned long lock_flags;
- spin_lock_irqsave(&bank_lock, lock_flags);
+ raw_spin_lock_irqsave(&bank_lock, lock_flags);
bank = map->bank;
if (!bank) {
- spin_unlock_irqrestore(&bank_lock, lock_flags);
+ raw_spin_unlock_irqrestore(&bank_lock, lock_flags);
pr_err("IPU: %s(%u) - unmapped!\n", __func__, d->irq);
return;
}
@@ -137,7 +137,7 @@ static void ipu_irq_mask(struct irq_data *d)
reg &= ~(1UL << (map->source & 31));
ipu_write_reg(bank->ipu, reg, bank->control);
- spin_unlock_irqrestore(&bank_lock, lock_flags);
+ raw_spin_unlock_irqrestore(&bank_lock, lock_flags);
}
static void ipu_irq_ack(struct irq_data *d)
@@ -146,17 +146,17 @@ static void ipu_irq_ack(struct irq_data *d)
struct ipu_irq_bank *bank;
unsigned long lock_flags;
- spin_lock_irqsave(&bank_lock, lock_flags);
+ raw_spin_lock_irqsave(&bank_lock, lock_flags);
bank = map->bank;
if (!bank) {
- spin_unlock_irqrestore(&bank_lock, lock_flags);
+ raw_spin_unlock_irqrestore(&bank_lock, lock_flags);
pr_err("IPU: %s(%u) - unmapped!\n", __func__, d->irq);
return;
}
ipu_write_reg(bank->ipu, 1UL << (map->source & 31), bank->status);
- spin_unlock_irqrestore(&bank_lock, lock_flags);
+ raw_spin_unlock_irqrestore(&bank_lock, lock_flags);
}
/**
@@ -172,11 +172,11 @@ bool ipu_irq_status(unsigned int irq)
unsigned long lock_flags;
bool ret;
- spin_lock_irqsave(&bank_lock, lock_flags);
+ raw_spin_lock_irqsave(&bank_lock, lock_flags);
bank = map->bank;
ret = bank && ipu_read_reg(bank->ipu, bank->status) &
(1UL << (map->source & 31));
- spin_unlock_irqrestore(&bank_lock, lock_flags);
+ raw_spin_unlock_irqrestore(&bank_lock, lock_flags);
return ret;
}
@@ -213,10 +213,10 @@ int ipu_irq_map(unsigned int source)
if (irq_map[i].source < 0) {
unsigned long lock_flags;
- spin_lock_irqsave(&bank_lock, lock_flags);
+ raw_spin_lock_irqsave(&bank_lock, lock_flags);
irq_map[i].source = source;
irq_map[i].bank = irq_bank + source / 32;
- spin_unlock_irqrestore(&bank_lock, lock_flags);
+ raw_spin_unlock_irqrestore(&bank_lock, lock_flags);
ret = irq_map[i].irq;
pr_debug("IPU: mapped source %u to IRQ %u\n",
@@ -252,10 +252,10 @@ int ipu_irq_unmap(unsigned int source)
pr_debug("IPU: unmapped source %u from IRQ %u\n",
source, irq_map[i].irq);
- spin_lock_irqsave(&bank_lock, lock_flags);
+ raw_spin_lock_irqsave(&bank_lock, lock_flags);
irq_map[i].source = -EINVAL;
irq_map[i].bank = NULL;
- spin_unlock_irqrestore(&bank_lock, lock_flags);
+ raw_spin_unlock_irqrestore(&bank_lock, lock_flags);
ret = 0;
break;
@@ -276,7 +276,7 @@ static void ipu_irq_err(unsigned int irq, struct irq_desc *desc)
for (i = IPU_IRQ_NR_FN_BANKS; i < IPU_IRQ_NR_BANKS; i++) {
struct ipu_irq_bank *bank = irq_bank + i;
- spin_lock(&bank_lock);
+ raw_spin_lock(&bank_lock);
status = ipu_read_reg(ipu, bank->status);
/*
* Don't think we have to clear all interrupts here, they will
@@ -284,18 +284,18 @@ static void ipu_irq_err(unsigned int irq, struct irq_desc *desc)
* might want to clear unhandled interrupts after the loop...
*/
status &= ipu_read_reg(ipu, bank->control);
- spin_unlock(&bank_lock);
+ raw_spin_unlock(&bank_lock);
while ((line = ffs(status))) {
struct ipu_irq_map *map;
line--;
status &= ~(1UL << line);
- spin_lock(&bank_lock);
+ raw_spin_lock(&bank_lock);
map = src2map(32 * i + line);
if (map)
irq = map->irq;
- spin_unlock(&bank_lock);
+ raw_spin_unlock(&bank_lock);
if (!map) {
pr_err("IPU: Interrupt on unmapped source %u bank %d\n",
@@ -317,22 +317,22 @@ static void ipu_irq_fn(unsigned int irq, struct irq_desc *desc)
for (i = 0; i < IPU_IRQ_NR_FN_BANKS; i++) {
struct ipu_irq_bank *bank = irq_bank + i;
- spin_lock(&bank_lock);
+ raw_spin_lock(&bank_lock);
status = ipu_read_reg(ipu, bank->status);
/* Not clearing all interrupts, see above */
status &= ipu_read_reg(ipu, bank->control);
- spin_unlock(&bank_lock);
+ raw_spin_unlock(&bank_lock);
while ((line = ffs(status))) {
struct ipu_irq_map *map;
line--;
status &= ~(1UL << line);
- spin_lock(&bank_lock);
+ raw_spin_lock(&bank_lock);
map = src2map(32 * i + line);
if (map)
irq = map->irq;
- spin_unlock(&bank_lock);
+ raw_spin_unlock(&bank_lock);
if (!map) {
pr_err("IPU: Interrupt on unmapped source %u bank %d\n",
diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c
index b9bae94f2015..8ba4edc6185e 100644
--- a/drivers/dma/mpc512x_dma.c
+++ b/drivers/dma/mpc512x_dma.c
@@ -741,7 +741,6 @@ static int __devinit mpc_dma_probe(struct platform_device *op)
mchan = &mdma->channels[i];
mchan->chan.device = dma;
- mchan->chan.chan_id = i;
mchan->chan.cookie = 1;
mchan->completed_cookie = mchan->chan.cookie;
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 954e334e01bb..9a353c2216d0 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -1305,7 +1305,7 @@ static int mv_xor_shared_probe(struct platform_device *pdev)
return -ENODEV;
msp->xor_base = devm_ioremap(&pdev->dev, res->start,
- res->end - res->start + 1);
+ resource_size(res));
if (!msp->xor_base)
return -EBUSY;
@@ -1314,7 +1314,7 @@ static int mv_xor_shared_probe(struct platform_device *pdev)
return -ENODEV;
msp->xor_high_base = devm_ioremap(&pdev->dev, res->start,
- res->end - res->start + 1);
+ resource_size(res));
if (!msp->xor_high_base)
return -EBUSY;
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
index 88aad4f54002..b4588bdd98bb 100644
--- a/drivers/dma/mxs-dma.c
+++ b/drivers/dma/mxs-dma.c
@@ -130,6 +130,23 @@ struct mxs_dma_engine {
struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS];
};
+static inline void mxs_dma_clkgate(struct mxs_dma_chan *mxs_chan, int enable)
+{
+ struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
+ int chan_id = mxs_chan->chan.chan_id;
+ int set_clr = enable ? MXS_CLR_ADDR : MXS_SET_ADDR;
+
+ /* enable apbh channel clock */
+ if (dma_is_apbh()) {
+ if (apbh_is_old())
+ writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
+ mxs_dma->base + HW_APBHX_CTRL0 + set_clr);
+ else
+ writel(1 << chan_id,
+ mxs_dma->base + HW_APBHX_CTRL0 + set_clr);
+ }
+}
+
static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan)
{
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
@@ -148,38 +165,21 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan)
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int chan_id = mxs_chan->chan.chan_id;
+ /* clkgate needs to be enabled before writing other registers */
+ mxs_dma_clkgate(mxs_chan, 1);
+
/* set cmd_addr up */
writel(mxs_chan->ccw_phys,
mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id));
- /* enable apbh channel clock */
- if (dma_is_apbh()) {
- if (apbh_is_old())
- writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
- mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
- else
- writel(1 << chan_id,
- mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
- }
-
/* write 1 to SEMA to kick off the channel */
writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(chan_id));
}
static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan)
{
- struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
- int chan_id = mxs_chan->chan.chan_id;
-
/* disable apbh channel clock */
- if (dma_is_apbh()) {
- if (apbh_is_old())
- writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
- mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
- else
- writel(1 << chan_id,
- mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
- }
+ mxs_dma_clkgate(mxs_chan, 0);
mxs_chan->status = DMA_SUCCESS;
}
@@ -327,16 +327,21 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
memset(mxs_chan->ccw, 0, PAGE_SIZE);
- ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
- 0, "mxs-dma", mxs_dma);
- if (ret)
- goto err_irq;
+ if (mxs_chan->chan_irq != NO_IRQ) {
+ ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
+ 0, "mxs-dma", mxs_dma);
+ if (ret)
+ goto err_irq;
+ }
ret = clk_enable(mxs_dma->clk);
if (ret)
goto err_clk;
+ /* clkgate needs to be enabled for reset to finish */
+ mxs_dma_clkgate(mxs_chan, 1);
mxs_dma_reset_chan(mxs_chan);
+ mxs_dma_clkgate(mxs_chan, 0);
dma_async_tx_descriptor_init(&mxs_chan->desc, chan);
mxs_chan->desc.tx_submit = mxs_dma_tx_submit;
@@ -535,6 +540,7 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
switch (cmd) {
case DMA_TERMINATE_ALL:
mxs_dma_disable_chan(mxs_chan);
+ mxs_dma_reset_chan(mxs_chan);
break;
case DMA_PAUSE:
mxs_dma_pause_chan(mxs_chan);
@@ -707,6 +713,8 @@ static struct platform_device_id mxs_dma_type[] = {
}, {
.name = "mxs-dma-apbx",
.driver_data = MXS_DMA_APBX,
+ }, {
+ /* end of list */
}
};
diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c
index ff5b38f9d45b..a6d0e3dbed07 100644
--- a/drivers/dma/pch_dma.c
+++ b/drivers/dma/pch_dma.c
@@ -45,7 +45,8 @@
#define DMA_STATUS_MASK_BITS 0x3
#define DMA_STATUS_SHIFT_BITS 16
#define DMA_STATUS_IRQ(x) (0x1 << (x))
-#define DMA_STATUS_ERR(x) (0x1 << ((x) + 8))
+#define DMA_STATUS0_ERR(x) (0x1 << ((x) + 8))
+#define DMA_STATUS2_ERR(x) (0x1 << (x))
#define DMA_DESC_WIDTH_SHIFT_BITS 12
#define DMA_DESC_WIDTH_1_BYTE (0x3 << DMA_DESC_WIDTH_SHIFT_BITS)
@@ -59,7 +60,10 @@
#define DMA_DESC_FOLLOW_WITHOUT_IRQ 0x2
#define DMA_DESC_FOLLOW_WITH_IRQ 0x3
-#define MAX_CHAN_NR 8
+#define MAX_CHAN_NR 12
+
+#define DMA_MASK_CTL0_MODE 0x33333333
+#define DMA_MASK_CTL2_MODE 0x00003333
static unsigned int init_nr_desc_per_channel = 64;
module_param(init_nr_desc_per_channel, uint, 0644);
@@ -133,6 +137,7 @@ struct pch_dma {
#define PCH_DMA_CTL3 0x0C
#define PCH_DMA_STS0 0x10
#define PCH_DMA_STS1 0x14
+#define PCH_DMA_STS2 0x18
#define dma_readl(pd, name) \
readl((pd)->membase + PCH_DMA_##name)
@@ -183,13 +188,19 @@ static void pdc_enable_irq(struct dma_chan *chan, int enable)
{
struct pch_dma *pd = to_pd(chan->device);
u32 val;
+ int pos;
+
+ if (chan->chan_id < 8)
+ pos = chan->chan_id;
+ else
+ pos = chan->chan_id + 8;
val = dma_readl(pd, CTL2);
if (enable)
- val |= 0x1 << chan->chan_id;
+ val |= 0x1 << pos;
else
- val &= ~(0x1 << chan->chan_id);
+ val &= ~(0x1 << pos);
dma_writel(pd, CTL2, val);
@@ -202,10 +213,17 @@ static void pdc_set_dir(struct dma_chan *chan)
struct pch_dma_chan *pd_chan = to_pd_chan(chan);
struct pch_dma *pd = to_pd(chan->device);
u32 val;
+ u32 mask_mode;
+ u32 mask_ctl;
if (chan->chan_id < 8) {
val = dma_readl(pd, CTL0);
+ mask_mode = DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * chan->chan_id);
+ mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+ val &= mask_mode;
if (pd_chan->dir == DMA_TO_DEVICE)
val |= 0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +
DMA_CTL0_DIR_SHIFT_BITS);
@@ -213,18 +231,24 @@ static void pdc_set_dir(struct dma_chan *chan)
val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +
DMA_CTL0_DIR_SHIFT_BITS));
+ val |= mask_ctl;
dma_writel(pd, CTL0, val);
} else {
int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
val = dma_readl(pd, CTL3);
+ mask_mode = DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * ch);
+ mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * ch));
+ val &= mask_mode;
if (pd_chan->dir == DMA_TO_DEVICE)
val |= 0x1 << (DMA_CTL0_BITS_PER_CH * ch +
DMA_CTL0_DIR_SHIFT_BITS);
else
val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * ch +
DMA_CTL0_DIR_SHIFT_BITS));
-
+ val |= mask_ctl;
dma_writel(pd, CTL3, val);
}
@@ -236,33 +260,37 @@ static void pdc_set_mode(struct dma_chan *chan, u32 mode)
{
struct pch_dma *pd = to_pd(chan->device);
u32 val;
+ u32 mask_ctl;
+ u32 mask_dir;
if (chan->chan_id < 8) {
+ mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+ mask_dir = 1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +\
+ DMA_CTL0_DIR_SHIFT_BITS);
val = dma_readl(pd, CTL0);
-
- val &= ~(DMA_CTL0_MODE_MASK_BITS <<
- (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+ val &= mask_dir;
val |= mode << (DMA_CTL0_BITS_PER_CH * chan->chan_id);
-
+ val |= mask_ctl;
dma_writel(pd, CTL0, val);
} else {
int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
-
+ mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * ch));
+ mask_dir = 1 << (DMA_CTL0_BITS_PER_CH * ch +\
+ DMA_CTL0_DIR_SHIFT_BITS);
val = dma_readl(pd, CTL3);
-
- val &= ~(DMA_CTL0_MODE_MASK_BITS <<
- (DMA_CTL0_BITS_PER_CH * ch));
+ val &= mask_dir;
val |= mode << (DMA_CTL0_BITS_PER_CH * ch);
-
+ val |= mask_ctl;
dma_writel(pd, CTL3, val);
-
}
dev_dbg(chan2dev(chan), "pdc_set_mode: chan %d -> %x\n",
chan->chan_id, val);
}
-static u32 pdc_get_status(struct pch_dma_chan *pd_chan)
+static u32 pdc_get_status0(struct pch_dma_chan *pd_chan)
{
struct pch_dma *pd = to_pd(pd_chan->chan.device);
u32 val;
@@ -272,9 +300,27 @@ static u32 pdc_get_status(struct pch_dma_chan *pd_chan)
DMA_STATUS_BITS_PER_CH * pd_chan->chan.chan_id));
}
+static u32 pdc_get_status2(struct pch_dma_chan *pd_chan)
+{
+ struct pch_dma *pd = to_pd(pd_chan->chan.device);
+ u32 val;
+
+ val = dma_readl(pd, STS2);
+ return DMA_STATUS_MASK_BITS & (val >> (DMA_STATUS_SHIFT_BITS +
+ DMA_STATUS_BITS_PER_CH * (pd_chan->chan.chan_id - 8)));
+}
+
static bool pdc_is_idle(struct pch_dma_chan *pd_chan)
{
- if (pdc_get_status(pd_chan) == DMA_STATUS_IDLE)
+ u32 sts;
+
+ if (pd_chan->chan.chan_id < 8)
+ sts = pdc_get_status0(pd_chan);
+ else
+ sts = pdc_get_status2(pd_chan);
+
+
+ if (sts == DMA_STATUS_IDLE)
return true;
else
return false;
@@ -495,11 +541,11 @@ static int pd_alloc_chan_resources(struct dma_chan *chan)
list_add_tail(&desc->desc_node, &tmp_list);
}
- spin_lock_bh(&pd_chan->lock);
+ spin_lock_irq(&pd_chan->lock);
list_splice(&tmp_list, &pd_chan->free_list);
pd_chan->descs_allocated = i;
pd_chan->completed_cookie = chan->cookie = 1;
- spin_unlock_bh(&pd_chan->lock);
+ spin_unlock_irq(&pd_chan->lock);
pdc_enable_irq(chan, 1);
@@ -517,10 +563,10 @@ static void pd_free_chan_resources(struct dma_chan *chan)
BUG_ON(!list_empty(&pd_chan->active_list));
BUG_ON(!list_empty(&pd_chan->queue));
- spin_lock_bh(&pd_chan->lock);
+ spin_lock_irq(&pd_chan->lock);
list_splice_init(&pd_chan->free_list, &tmp_list);
pd_chan->descs_allocated = 0;
- spin_unlock_bh(&pd_chan->lock);
+ spin_unlock_irq(&pd_chan->lock);
list_for_each_entry_safe(desc, _d, &tmp_list, desc_node)
pci_pool_free(pd->pool, desc, desc->txd.phys);
@@ -536,10 +582,10 @@ static enum dma_status pd_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
dma_cookie_t last_completed;
int ret;
- spin_lock_bh(&pd_chan->lock);
+ spin_lock_irq(&pd_chan->lock);
last_completed = pd_chan->completed_cookie;
last_used = chan->cookie;
- spin_unlock_bh(&pd_chan->lock);
+ spin_unlock_irq(&pd_chan->lock);
ret = dma_async_is_complete(cookie, last_completed, last_used);
@@ -654,7 +700,7 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
if (cmd != DMA_TERMINATE_ALL)
return -ENXIO;
- spin_lock_bh(&pd_chan->lock);
+ spin_lock_irq(&pd_chan->lock);
pdc_set_mode(&pd_chan->chan, DMA_CTL0_DISABLE);
@@ -664,7 +710,7 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
list_for_each_entry_safe(desc, _d, &list, desc_node)
pdc_chain_complete(pd_chan, desc);
- spin_unlock_bh(&pd_chan->lock);
+ spin_unlock_irq(&pd_chan->lock);
return 0;
}
@@ -693,30 +739,45 @@ static irqreturn_t pd_irq(int irq, void *devid)
struct pch_dma *pd = (struct pch_dma *)devid;
struct pch_dma_chan *pd_chan;
u32 sts0;
+ u32 sts2;
int i;
- int ret = IRQ_NONE;
+ int ret0 = IRQ_NONE;
+ int ret2 = IRQ_NONE;
sts0 = dma_readl(pd, STS0);
+ sts2 = dma_readl(pd, STS2);
dev_dbg(pd->dma.dev, "pd_irq sts0: %x\n", sts0);
for (i = 0; i < pd->dma.chancnt; i++) {
pd_chan = &pd->channels[i];
- if (sts0 & DMA_STATUS_IRQ(i)) {
- if (sts0 & DMA_STATUS_ERR(i))
- set_bit(0, &pd_chan->err_status);
+ if (i < 8) {
+ if (sts0 & DMA_STATUS_IRQ(i)) {
+ if (sts0 & DMA_STATUS0_ERR(i))
+ set_bit(0, &pd_chan->err_status);
- tasklet_schedule(&pd_chan->tasklet);
- ret = IRQ_HANDLED;
- }
+ tasklet_schedule(&pd_chan->tasklet);
+ ret0 = IRQ_HANDLED;
+ }
+ } else {
+ if (sts2 & DMA_STATUS_IRQ(i - 8)) {
+ if (sts2 & DMA_STATUS2_ERR(i))
+ set_bit(0, &pd_chan->err_status);
+ tasklet_schedule(&pd_chan->tasklet);
+ ret2 = IRQ_HANDLED;
+ }
+ }
}
/* clear interrupt bits in status register */
- dma_writel(pd, STS0, sts0);
+ if (ret0)
+ dma_writel(pd, STS0, sts0);
+ if (ret2)
+ dma_writel(pd, STS2, sts2);
- return ret;
+ return ret0 | ret2;
}
#ifdef CONFIG_PM
@@ -811,8 +872,7 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev,
int i;
nr_channels = id->driver_data;
- pd = kzalloc(sizeof(struct pch_dma)+
- sizeof(struct pch_dma_chan) * nr_channels, GFP_KERNEL);
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
if (!pd)
return -ENOMEM;
@@ -865,7 +925,6 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev,
}
pd->dma.dev = &pdev->dev;
- pd->dma.chancnt = nr_channels;
INIT_LIST_HEAD(&pd->dma.channels);
@@ -874,7 +933,6 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev,
pd_chan->chan.device = &pd->dma;
pd_chan->chan.cookie = 1;
- pd_chan->chan.chan_id = i;
pd_chan->membase = &regs->desc[i];
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 6abe1ec1f2ce..571041477ab2 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -17,6 +17,8 @@
#include <linux/interrupt.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl330.h>
+#include <linux/pm_runtime.h>
+#include <linux/scatterlist.h>
#define NR_DEFAULT_DESC 16
@@ -68,6 +70,14 @@ struct dma_pl330_chan {
* NULL if the channel is available to be acquired.
*/
void *pl330_chid;
+
+ /* For D-to-M and M-to-D channels */
+ int burst_sz; /* the peripheral fifo width */
+ int burst_len; /* the number of burst */
+ dma_addr_t fifo_addr;
+
+ /* for cyclic capability */
+ bool cyclic;
};
struct dma_pl330_dmac {
@@ -82,7 +92,9 @@ struct dma_pl330_dmac {
spinlock_t pool_lock;
/* Peripheral channels connected to this DMAC */
- struct dma_pl330_chan peripherals[0]; /* keep at end */
+ struct dma_pl330_chan *peripherals; /* keep at end */
+
+ struct clk *clk;
};
struct dma_pl330_desc {
@@ -152,6 +164,31 @@ static inline void free_desc_list(struct list_head *list)
spin_unlock_irqrestore(&pdmac->pool_lock, flags);
}
+static inline void handle_cyclic_desc_list(struct list_head *list)
+{
+ struct dma_pl330_desc *desc;
+ struct dma_pl330_chan *pch;
+ unsigned long flags;
+
+ if (list_empty(list))
+ return;
+
+ list_for_each_entry(desc, list, node) {
+ dma_async_tx_callback callback;
+
+ /* Change status to reload it */
+ desc->status = PREP;
+ pch = desc->pchan;
+ callback = desc->txd.callback;
+ if (callback)
+ callback(desc->txd.callback_param);
+ }
+
+ spin_lock_irqsave(&pch->lock, flags);
+ list_splice_tail_init(list, &pch->work_list);
+ spin_unlock_irqrestore(&pch->lock, flags);
+}
+
static inline void fill_queue(struct dma_pl330_chan *pch)
{
struct dma_pl330_desc *desc;
@@ -205,7 +242,10 @@ static void pl330_tasklet(unsigned long data)
spin_unlock_irqrestore(&pch->lock, flags);
- free_desc_list(&list);
+ if (pch->cyclic)
+ handle_cyclic_desc_list(&list);
+ else
+ free_desc_list(&list);
}
static void dma_pl330_rqcb(void *token, enum pl330_op_err err)
@@ -236,6 +276,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
spin_lock_irqsave(&pch->lock, flags);
pch->completed = chan->cookie = 1;
+ pch->cyclic = false;
pch->pl330_chid = pl330_request_channel(&pdmac->pif);
if (!pch->pl330_chid) {
@@ -253,25 +294,52 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg)
{
struct dma_pl330_chan *pch = to_pchan(chan);
- struct dma_pl330_desc *desc;
+ struct dma_pl330_desc *desc, *_dt;
unsigned long flags;
+ struct dma_pl330_dmac *pdmac = pch->dmac;
+ struct dma_slave_config *slave_config;
+ LIST_HEAD(list);
- /* Only supports DMA_TERMINATE_ALL */
- if (cmd != DMA_TERMINATE_ALL)
- return -ENXIO;
-
- spin_lock_irqsave(&pch->lock, flags);
-
- /* FLUSH the PL330 Channel thread */
- pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
+ switch (cmd) {
+ case DMA_TERMINATE_ALL:
+ spin_lock_irqsave(&pch->lock, flags);
- /* Mark all desc done */
- list_for_each_entry(desc, &pch->work_list, node)
- desc->status = DONE;
+ /* FLUSH the PL330 Channel thread */
+ pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
- spin_unlock_irqrestore(&pch->lock, flags);
+ /* Mark all desc done */
+ list_for_each_entry_safe(desc, _dt, &pch->work_list , node) {
+ desc->status = DONE;
+ pch->completed = desc->txd.cookie;
+ list_move_tail(&desc->node, &list);
+ }
- pl330_tasklet((unsigned long) pch);
+ list_splice_tail_init(&list, &pdmac->desc_pool);
+ spin_unlock_irqrestore(&pch->lock, flags);
+ break;
+ case DMA_SLAVE_CONFIG:
+ slave_config = (struct dma_slave_config *)arg;
+
+ if (slave_config->direction == DMA_TO_DEVICE) {
+ if (slave_config->dst_addr)
+ pch->fifo_addr = slave_config->dst_addr;
+ if (slave_config->dst_addr_width)
+ pch->burst_sz = __ffs(slave_config->dst_addr_width);
+ if (slave_config->dst_maxburst)
+ pch->burst_len = slave_config->dst_maxburst;
+ } else if (slave_config->direction == DMA_FROM_DEVICE) {
+ if (slave_config->src_addr)
+ pch->fifo_addr = slave_config->src_addr;
+ if (slave_config->src_addr_width)
+ pch->burst_sz = __ffs(slave_config->src_addr_width);
+ if (slave_config->src_maxburst)
+ pch->burst_len = slave_config->src_maxburst;
+ }
+ break;
+ default:
+ dev_err(pch->dmac->pif.dev, "Not supported command.\n");
+ return -ENXIO;
+ }
return 0;
}
@@ -288,6 +356,9 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
pl330_release_channel(pch->pl330_chid);
pch->pl330_chid = NULL;
+ if (pch->cyclic)
+ list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);
+
spin_unlock_irqrestore(&pch->lock, flags);
}
@@ -451,8 +522,13 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
desc->txd.cookie = 0;
async_tx_ack(&desc->txd);
- desc->req.rqtype = peri->rqtype;
- desc->req.peri = peri->peri_id;
+ if (peri) {
+ desc->req.rqtype = peri->rqtype;
+ desc->req.peri = pch->chan.chan_id;
+ } else {
+ desc->req.rqtype = MEMTOMEM;
+ desc->req.peri = 0;
+ }
dma_async_tx_descriptor_init(&desc->txd, &pch->chan);
@@ -519,6 +595,51 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
return burst_len;
}
+static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
+ struct dma_chan *chan, dma_addr_t dma_addr, size_t len,
+ size_t period_len, enum dma_data_direction direction)
+{
+ struct dma_pl330_desc *desc;
+ struct dma_pl330_chan *pch = to_pchan(chan);
+ dma_addr_t dst;
+ dma_addr_t src;
+
+ desc = pl330_get_desc(pch);
+ if (!desc) {
+ dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n",
+ __func__, __LINE__);
+ return NULL;
+ }
+
+ switch (direction) {
+ case DMA_TO_DEVICE:
+ desc->rqcfg.src_inc = 1;
+ desc->rqcfg.dst_inc = 0;
+ src = dma_addr;
+ dst = pch->fifo_addr;
+ break;
+ case DMA_FROM_DEVICE:
+ desc->rqcfg.src_inc = 0;
+ desc->rqcfg.dst_inc = 1;
+ src = pch->fifo_addr;
+ dst = dma_addr;
+ break;
+ default:
+ dev_err(pch->dmac->pif.dev, "%s:%d Invalid dma direction\n",
+ __func__, __LINE__);
+ return NULL;
+ }
+
+ desc->rqcfg.brst_size = pch->burst_sz;
+ desc->rqcfg.brst_len = 1;
+
+ pch->cyclic = true;
+
+ fill_px(&desc->px, dst, src, period_len);
+
+ return &desc->txd;
+}
+
static struct dma_async_tx_descriptor *
pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
dma_addr_t src, size_t len, unsigned long flags)
@@ -529,10 +650,10 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
struct pl330_info *pi;
int burst;
- if (unlikely(!pch || !len || !peri))
+ if (unlikely(!pch || !len))
return NULL;
- if (peri->rqtype != MEMTOMEM)
+ if (peri && peri->rqtype != MEMTOMEM)
return NULL;
pi = &pch->dmac->pif;
@@ -574,10 +695,10 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct dma_pl330_peri *peri = chan->private;
struct scatterlist *sg;
unsigned long flags;
- int i, burst_size;
+ int i;
dma_addr_t addr;
- if (unlikely(!pch || !sgl || !sg_len))
+ if (unlikely(!pch || !sgl || !sg_len || !peri))
return NULL;
/* Make sure the direction is consistent */
@@ -590,8 +711,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
return NULL;
}
- addr = peri->fifo_addr;
- burst_size = peri->burst_sz;
+ addr = pch->fifo_addr;
first = NULL;
@@ -639,7 +759,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
sg_dma_address(sg), addr, sg_dma_len(sg));
}
- desc->rqcfg.brst_size = burst_size;
+ desc->rqcfg.brst_size = pch->burst_sz;
desc->rqcfg.brst_len = 1;
}
@@ -666,17 +786,12 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
struct dma_device *pd;
struct resource *res;
int i, ret, irq;
+ int num_chan;
pdat = adev->dev.platform_data;
- if (!pdat || !pdat->nr_valid_peri) {
- dev_err(&adev->dev, "platform data missing\n");
- return -ENODEV;
- }
-
/* Allocate a new DMAC and its Channels */
- pdmac = kzalloc(pdat->nr_valid_peri * sizeof(*pch)
- + sizeof(*pdmac), GFP_KERNEL);
+ pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL);
if (!pdmac) {
dev_err(&adev->dev, "unable to allocate mem\n");
return -ENOMEM;
@@ -685,7 +800,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pi = &pdmac->pif;
pi->dev = &adev->dev;
pi->pl330_data = NULL;
- pi->mcbufsz = pdat->mcbuf_sz;
+ pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0;
res = &adev->res;
request_mem_region(res->start, resource_size(res), "dma-pl330");
@@ -696,6 +811,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto probe_err1;
}
+ pdmac->clk = clk_get(&adev->dev, "dma");
+ if (IS_ERR(pdmac->clk)) {
+ dev_err(&adev->dev, "Cannot get operation clock.\n");
+ ret = -EINVAL;
+ goto probe_err1;
+ }
+
+ amba_set_drvdata(adev, pdmac);
+
+#ifdef CONFIG_PM_RUNTIME
+ /* to use the runtime PM helper functions */
+ pm_runtime_enable(&adev->dev);
+
+ /* enable the power domain */
+ if (pm_runtime_get_sync(&adev->dev)) {
+ dev_err(&adev->dev, "failed to get runtime pm\n");
+ ret = -ENODEV;
+ goto probe_err1;
+ }
+#else
+ /* enable dma clk */
+ clk_enable(pdmac->clk);
+#endif
+
irq = adev->irq[0];
ret = request_irq(irq, pl330_irq_handler, 0,
dev_name(&adev->dev), pi);
@@ -717,33 +856,40 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
INIT_LIST_HEAD(&pd->channels);
/* Initialize channel parameters */
- for (i = 0; i < pdat->nr_valid_peri; i++) {
- struct dma_pl330_peri *peri = &pdat->peri[i];
- pch = &pdmac->peripherals[i];
+ num_chan = max(pdat ? pdat->nr_valid_peri : 0, (u8)pi->pcfg.num_chan);
+ pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
- switch (peri->rqtype) {
- case MEMTOMEM:
+ for (i = 0; i < num_chan; i++) {
+ pch = &pdmac->peripherals[i];
+ if (pdat) {
+ struct dma_pl330_peri *peri = &pdat->peri[i];
+
+ switch (peri->rqtype) {
+ case MEMTOMEM:
+ dma_cap_set(DMA_MEMCPY, pd->cap_mask);
+ break;
+ case MEMTODEV:
+ case DEVTOMEM:
+ dma_cap_set(DMA_SLAVE, pd->cap_mask);
+ dma_cap_set(DMA_CYCLIC, pd->cap_mask);
+ break;
+ default:
+ dev_err(&adev->dev, "DEVTODEV Not Supported\n");
+ continue;
+ }
+ pch->chan.private = peri;
+ } else {
dma_cap_set(DMA_MEMCPY, pd->cap_mask);
- break;
- case MEMTODEV:
- case DEVTOMEM:
- dma_cap_set(DMA_SLAVE, pd->cap_mask);
- break;
- default:
- dev_err(&adev->dev, "DEVTODEV Not Supported\n");
- continue;
+ pch->chan.private = NULL;
}
INIT_LIST_HEAD(&pch->work_list);
spin_lock_init(&pch->lock);
pch->pl330_chid = NULL;
- pch->chan.private = peri;
pch->chan.device = pd;
- pch->chan.chan_id = i;
pch->dmac = pdmac;
/* Add the channel to the DMAC list */
- pd->chancnt++;
list_add_tail(&pch->chan.device_node, &pd->channels);
}
@@ -752,6 +898,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
pd->device_free_chan_resources = pl330_free_chan_resources;
pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
+ pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
pd->device_tx_status = pl330_tx_status;
pd->device_prep_slave_sg = pl330_prep_slave_sg;
pd->device_control = pl330_control;
@@ -763,8 +910,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto probe_err4;
}
- amba_set_drvdata(adev, pdmac);
-
dev_info(&adev->dev,
"Loaded driver for PL330 DMAC-%d\n", adev->periphid);
dev_info(&adev->dev,
@@ -825,6 +970,13 @@ static int __devexit pl330_remove(struct amba_device *adev)
res = &adev->res;
release_mem_region(res->start, resource_size(res));
+#ifdef CONFIG_PM_RUNTIME
+ pm_runtime_put(&adev->dev);
+ pm_runtime_disable(&adev->dev);
+#else
+ clk_disable(pdmac->clk);
+#endif
+
kfree(pdmac);
return 0;
@@ -838,10 +990,49 @@ static struct amba_id pl330_ids[] = {
{ 0, 0 },
};
+#ifdef CONFIG_PM_RUNTIME
+static int pl330_runtime_suspend(struct device *dev)
+{
+ struct dma_pl330_dmac *pdmac = dev_get_drvdata(dev);
+
+ if (!pdmac) {
+ dev_err(dev, "failed to get dmac\n");
+ return -ENODEV;
+ }
+
+ clk_disable(pdmac->clk);
+
+ return 0;
+}
+
+static int pl330_runtime_resume(struct device *dev)
+{
+ struct dma_pl330_dmac *pdmac = dev_get_drvdata(dev);
+
+ if (!pdmac) {
+ dev_err(dev, "failed to get dmac\n");
+ return -ENODEV;
+ }
+
+ clk_enable(pdmac->clk);
+
+ return 0;
+}
+#else
+#define pl330_runtime_suspend NULL
+#define pl330_runtime_resume NULL
+#endif /* CONFIG_PM_RUNTIME */
+
+static const struct dev_pm_ops pl330_pm_ops = {
+ .runtime_suspend = pl330_runtime_suspend,
+ .runtime_resume = pl330_runtime_resume,
+};
+
static struct amba_driver pl330_driver = {
.drv = {
.owner = THIS_MODULE,
.name = "dma-pl330",
+ .pm = &pl330_pm_ops,
},
.id_table = pl330_ids,
.probe = pl330_probe,
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index 028330044201..81809c2b46ab 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -70,12 +70,36 @@ static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg)
static u16 dmaor_read(struct sh_dmae_device *shdev)
{
- return __raw_readw(shdev->chan_reg + DMAOR / sizeof(u32));
+ u32 __iomem *addr = shdev->chan_reg + DMAOR / sizeof(u32);
+
+ if (shdev->pdata->dmaor_is_32bit)
+ return __raw_readl(addr);
+ else
+ return __raw_readw(addr);
}
static void dmaor_write(struct sh_dmae_device *shdev, u16 data)
{
- __raw_writew(data, shdev->chan_reg + DMAOR / sizeof(u32));
+ u32 __iomem *addr = shdev->chan_reg + DMAOR / sizeof(u32);
+
+ if (shdev->pdata->dmaor_is_32bit)
+ __raw_writel(data, addr);
+ else
+ __raw_writew(data, addr);
+}
+
+static void chcr_write(struct sh_dmae_chan *sh_dc, u32 data)
+{
+ struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
+
+ __raw_writel(data, sh_dc->base + shdev->chcr_offset / sizeof(u32));
+}
+
+static u32 chcr_read(struct sh_dmae_chan *sh_dc)
+{
+ struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
+
+ return __raw_readl(sh_dc->base + shdev->chcr_offset / sizeof(u32));
}
/*
@@ -120,7 +144,7 @@ static int sh_dmae_rst(struct sh_dmae_device *shdev)
static bool dmae_is_busy(struct sh_dmae_chan *sh_chan)
{
- u32 chcr = sh_dmae_readl(sh_chan, CHCR);
+ u32 chcr = chcr_read(sh_chan);
if ((chcr & (CHCR_DE | CHCR_TE)) == CHCR_DE)
return true; /* working */
@@ -130,8 +154,7 @@ static bool dmae_is_busy(struct sh_dmae_chan *sh_chan)
static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr)
{
- struct sh_dmae_device *shdev = container_of(sh_chan->common.device,
- struct sh_dmae_device, common);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_dmae_pdata *pdata = shdev->pdata;
int cnt = ((chcr & pdata->ts_low_mask) >> pdata->ts_low_shift) |
((chcr & pdata->ts_high_mask) >> pdata->ts_high_shift);
@@ -144,8 +167,7 @@ static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr)
static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size)
{
- struct sh_dmae_device *shdev = container_of(sh_chan->common.device,
- struct sh_dmae_device, common);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_dmae_pdata *pdata = shdev->pdata;
int i;
@@ -169,18 +191,23 @@ static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw)
static void dmae_start(struct sh_dmae_chan *sh_chan)
{
- u32 chcr = sh_dmae_readl(sh_chan, CHCR);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
+ u32 chcr = chcr_read(sh_chan);
+
+ if (shdev->pdata->needs_tend_set)
+ sh_dmae_writel(sh_chan, 0xFFFFFFFF, TEND);
- chcr |= CHCR_DE | CHCR_IE;
- sh_dmae_writel(sh_chan, chcr & ~CHCR_TE, CHCR);
+ chcr |= CHCR_DE | shdev->chcr_ie_bit;
+ chcr_write(sh_chan, chcr & ~CHCR_TE);
}
static void dmae_halt(struct sh_dmae_chan *sh_chan)
{
- u32 chcr = sh_dmae_readl(sh_chan, CHCR);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
+ u32 chcr = chcr_read(sh_chan);
- chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
- sh_dmae_writel(sh_chan, chcr, CHCR);
+ chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit);
+ chcr_write(sh_chan, chcr);
}
static void dmae_init(struct sh_dmae_chan *sh_chan)
@@ -192,7 +219,7 @@ static void dmae_init(struct sh_dmae_chan *sh_chan)
u32 chcr = DM_INC | SM_INC | 0x400 | log2size_to_chcr(sh_chan,
LOG2_DEFAULT_XFER_SIZE);
sh_chan->xmit_shift = calc_xmit_shift(sh_chan, chcr);
- sh_dmae_writel(sh_chan, chcr, CHCR);
+ chcr_write(sh_chan, chcr);
}
static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val)
@@ -202,23 +229,25 @@ static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val)
return -EBUSY;
sh_chan->xmit_shift = calc_xmit_shift(sh_chan, val);
- sh_dmae_writel(sh_chan, val, CHCR);
+ chcr_write(sh_chan, val);
return 0;
}
static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
{
- struct sh_dmae_device *shdev = container_of(sh_chan->common.device,
- struct sh_dmae_device, common);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_dmae_pdata *pdata = shdev->pdata;
const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id];
u16 __iomem *addr = shdev->dmars;
- int shift = chan_pdata->dmars_bit;
+ unsigned int shift = chan_pdata->dmars_bit;
if (dmae_is_busy(sh_chan))
return -EBUSY;
+ if (pdata->no_dmars)
+ return 0;
+
/* in the case of a missing DMARS resource use first memory window */
if (!addr)
addr = (u16 __iomem *)shdev->chan_reg;
@@ -230,14 +259,23 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
return 0;
}
+static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan);
+
static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx)
{
struct sh_desc *desc = tx_to_sh_desc(tx), *chunk, *last = desc, *c;
struct sh_dmae_chan *sh_chan = to_sh_chan(tx->chan);
+ struct sh_dmae_slave *param = tx->chan->private;
dma_async_tx_callback callback = tx->callback;
dma_cookie_t cookie;
+ bool power_up;
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irq(&sh_chan->desc_lock);
+
+ if (list_empty(&sh_chan->ld_queue))
+ power_up = true;
+ else
+ power_up = false;
cookie = sh_chan->common.cookie;
cookie++;
@@ -273,7 +311,38 @@ static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx)
tx->cookie, &last->async_tx, sh_chan->id,
desc->hw.sar, desc->hw.tcr, desc->hw.dar);
- spin_unlock_bh(&sh_chan->desc_lock);
+ if (power_up) {
+ sh_chan->pm_state = DMAE_PM_BUSY;
+
+ pm_runtime_get(sh_chan->dev);
+
+ spin_unlock_irq(&sh_chan->desc_lock);
+
+ pm_runtime_barrier(sh_chan->dev);
+
+ spin_lock_irq(&sh_chan->desc_lock);
+
+ /* Have we been reset, while waiting? */
+ if (sh_chan->pm_state != DMAE_PM_ESTABLISHED) {
+ dev_dbg(sh_chan->dev, "Bring up channel %d\n",
+ sh_chan->id);
+ if (param) {
+ const struct sh_dmae_slave_config *cfg =
+ param->config;
+
+ dmae_set_dmars(sh_chan, cfg->mid_rid);
+ dmae_set_chcr(sh_chan, cfg->chcr);
+ } else {
+ dmae_init(sh_chan);
+ }
+
+ if (sh_chan->pm_state == DMAE_PM_PENDING)
+ sh_chan_xfer_ld_queue(sh_chan);
+ sh_chan->pm_state = DMAE_PM_ESTABLISHED;
+ }
+ }
+
+ spin_unlock_irq(&sh_chan->desc_lock);
return cookie;
}
@@ -296,9 +365,7 @@ static struct sh_desc *sh_dmae_get_desc(struct sh_dmae_chan *sh_chan)
static const struct sh_dmae_slave_config *sh_dmae_find_slave(
struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *param)
{
- struct dma_device *dma_dev = sh_chan->common.device;
- struct sh_dmae_device *shdev = container_of(dma_dev,
- struct sh_dmae_device, common);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_dmae_pdata *pdata = shdev->pdata;
int i;
@@ -319,8 +386,6 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan)
struct sh_dmae_slave *param = chan->private;
int ret;
- pm_runtime_get_sync(sh_chan->dev);
-
/*
* This relies on the guarantee from dmaengine that alloc_chan_resources
* never runs concurrently with itself or free_chan_resources.
@@ -340,31 +405,20 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan)
}
param->config = cfg;
-
- dmae_set_dmars(sh_chan, cfg->mid_rid);
- dmae_set_chcr(sh_chan, cfg->chcr);
- } else {
- dmae_init(sh_chan);
}
- spin_lock_bh(&sh_chan->desc_lock);
while (sh_chan->descs_allocated < NR_DESCS_PER_CHANNEL) {
- spin_unlock_bh(&sh_chan->desc_lock);
desc = kzalloc(sizeof(struct sh_desc), GFP_KERNEL);
- if (!desc) {
- spin_lock_bh(&sh_chan->desc_lock);
+ if (!desc)
break;
- }
dma_async_tx_descriptor_init(&desc->async_tx,
&sh_chan->common);
desc->async_tx.tx_submit = sh_dmae_tx_submit;
desc->mark = DESC_IDLE;
- spin_lock_bh(&sh_chan->desc_lock);
list_add(&desc->node, &sh_chan->ld_free);
sh_chan->descs_allocated++;
}
- spin_unlock_bh(&sh_chan->desc_lock);
if (!sh_chan->descs_allocated) {
ret = -ENOMEM;
@@ -378,7 +432,7 @@ edescalloc:
clear_bit(param->slave_id, sh_dmae_slave_used);
etestused:
efindslave:
- pm_runtime_put(sh_chan->dev);
+ chan->private = NULL;
return ret;
}
@@ -390,7 +444,6 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan)
struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
struct sh_desc *desc, *_desc;
LIST_HEAD(list);
- int descs = sh_chan->descs_allocated;
/* Protect against ISR */
spin_lock_irq(&sh_chan->desc_lock);
@@ -410,15 +463,12 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan)
chan->private = NULL;
}
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irq(&sh_chan->desc_lock);
list_splice_init(&sh_chan->ld_free, &list);
sh_chan->descs_allocated = 0;
- spin_unlock_bh(&sh_chan->desc_lock);
-
- if (descs > 0)
- pm_runtime_put(sh_chan->dev);
+ spin_unlock_irq(&sh_chan->desc_lock);
list_for_each_entry_safe(desc, _desc, &list, node)
kfree(desc);
@@ -507,6 +557,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c
struct sh_desc *first = NULL, *new = NULL /* compiler... */;
LIST_HEAD(tx_list);
int chunks = 0;
+ unsigned long irq_flags;
int i;
if (!sg_len)
@@ -517,7 +568,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c
(SH_DMA_TCR_MAX + 1);
/* Have to lock the whole loop to protect against concurrent release */
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irqsave(&sh_chan->desc_lock, irq_flags);
/*
* Chaining:
@@ -563,7 +614,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c
/* Put them back on the free list, so, they don't get lost */
list_splice_tail(&tx_list, &sh_chan->ld_free);
- spin_unlock_bh(&sh_chan->desc_lock);
+ spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags);
return &first->async_tx;
@@ -572,7 +623,7 @@ err_get_desc:
new->mark = DESC_IDLE;
list_splice(&tx_list, &sh_chan->ld_free);
- spin_unlock_bh(&sh_chan->desc_lock);
+ spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags);
return NULL;
}
@@ -634,6 +685,7 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg)
{
struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
+ unsigned long flags;
/* Only supports DMA_TERMINATE_ALL */
if (cmd != DMA_TERMINATE_ALL)
@@ -642,7 +694,7 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
if (!chan)
return -EINVAL;
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irqsave(&sh_chan->desc_lock, flags);
dmae_halt(sh_chan);
if (!list_empty(&sh_chan->ld_queue)) {
@@ -651,9 +703,8 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
struct sh_desc, node);
desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) <<
sh_chan->xmit_shift;
-
}
- spin_unlock_bh(&sh_chan->desc_lock);
+ spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
sh_dmae_chan_ld_cleanup(sh_chan, true);
@@ -668,8 +719,9 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all
dma_cookie_t cookie = 0;
dma_async_tx_callback callback = NULL;
void *param = NULL;
+ unsigned long flags;
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irqsave(&sh_chan->desc_lock, flags);
list_for_each_entry_safe(desc, _desc, &sh_chan->ld_queue, node) {
struct dma_async_tx_descriptor *tx = &desc->async_tx;
@@ -735,7 +787,13 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all
async_tx_test_ack(&desc->async_tx)) || all) {
/* Remove from ld_queue list */
desc->mark = DESC_IDLE;
+
list_move(&desc->node, &sh_chan->ld_free);
+
+ if (list_empty(&sh_chan->ld_queue)) {
+ dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id);
+ pm_runtime_put(sh_chan->dev);
+ }
}
}
@@ -746,7 +804,7 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all
*/
sh_chan->completed_cookie = sh_chan->common.cookie;
- spin_unlock_bh(&sh_chan->desc_lock);
+ spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
if (callback)
callback(param);
@@ -765,16 +823,14 @@ static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all)
;
}
+/* Called under spin_lock_irq(&sh_chan->desc_lock) */
static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan)
{
struct sh_desc *desc;
- spin_lock_bh(&sh_chan->desc_lock);
/* DMA work check */
- if (dmae_is_busy(sh_chan)) {
- spin_unlock_bh(&sh_chan->desc_lock);
+ if (dmae_is_busy(sh_chan))
return;
- }
/* Find the first not transferred descriptor */
list_for_each_entry(desc, &sh_chan->ld_queue, node)
@@ -787,14 +843,18 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan)
dmae_start(sh_chan);
break;
}
-
- spin_unlock_bh(&sh_chan->desc_lock);
}
static void sh_dmae_memcpy_issue_pending(struct dma_chan *chan)
{
struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
- sh_chan_xfer_ld_queue(sh_chan);
+
+ spin_lock_irq(&sh_chan->desc_lock);
+ if (sh_chan->pm_state == DMAE_PM_ESTABLISHED)
+ sh_chan_xfer_ld_queue(sh_chan);
+ else
+ sh_chan->pm_state = DMAE_PM_PENDING;
+ spin_unlock_irq(&sh_chan->desc_lock);
}
static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
@@ -805,6 +865,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
dma_cookie_t last_used;
dma_cookie_t last_complete;
enum dma_status status;
+ unsigned long flags;
sh_dmae_chan_ld_cleanup(sh_chan, false);
@@ -815,7 +876,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
BUG_ON(last_complete < 0);
dma_set_tx_state(txstate, last_complete, last_used, 0);
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irqsave(&sh_chan->desc_lock, flags);
status = dma_async_is_complete(cookie, last_complete, last_used);
@@ -833,7 +894,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
}
}
- spin_unlock_bh(&sh_chan->desc_lock);
+ spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
return status;
}
@@ -846,7 +907,7 @@ static irqreturn_t sh_dmae_interrupt(int irq, void *data)
spin_lock(&sh_chan->desc_lock);
- chcr = sh_dmae_readl(sh_chan, CHCR);
+ chcr = chcr_read(sh_chan);
if (chcr & CHCR_TE) {
/* DMA stop */
@@ -886,6 +947,12 @@ static bool sh_dmae_reset(struct sh_dmae_device *shdev)
list_splice_init(&sh_chan->ld_queue, &dl);
+ if (!list_empty(&dl)) {
+ dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id);
+ pm_runtime_put(sh_chan->dev);
+ }
+ sh_chan->pm_state = DMAE_PM_ESTABLISHED;
+
spin_unlock(&sh_chan->desc_lock);
/* Complete all */
@@ -926,7 +993,7 @@ static void dmae_do_tasklet(unsigned long data)
u32 sar_buf = sh_dmae_readl(sh_chan, SAR);
u32 dar_buf = sh_dmae_readl(sh_chan, DAR);
- spin_lock(&sh_chan->desc_lock);
+ spin_lock_irq(&sh_chan->desc_lock);
list_for_each_entry(desc, &sh_chan->ld_queue, node) {
if (desc->mark == DESC_SUBMITTED &&
((desc->direction == DMA_FROM_DEVICE &&
@@ -939,10 +1006,10 @@ static void dmae_do_tasklet(unsigned long data)
break;
}
}
- spin_unlock(&sh_chan->desc_lock);
-
/* Next desc */
sh_chan_xfer_ld_queue(sh_chan);
+ spin_unlock_irq(&sh_chan->desc_lock);
+
sh_dmae_chan_ld_cleanup(sh_chan, false);
}
@@ -1010,7 +1077,9 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id,
return -ENOMEM;
}
- /* copy struct dma_device */
+ new_sh_chan->pm_state = DMAE_PM_ESTABLISHED;
+
+ /* reference struct dma_device */
new_sh_chan->common.device = &shdev->common;
new_sh_chan->dev = shdev->common.dev;
@@ -1144,6 +1213,16 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
/* platform data */
shdev->pdata = pdata;
+ if (pdata->chcr_offset)
+ shdev->chcr_offset = pdata->chcr_offset;
+ else
+ shdev->chcr_offset = CHCR;
+
+ if (pdata->chcr_ie_bit)
+ shdev->chcr_ie_bit = pdata->chcr_ie_bit;
+ else
+ shdev->chcr_ie_bit = CHCR_IE;
+
platform_set_drvdata(pdev, shdev);
pm_runtime_enable(&pdev->dev);
diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h
index 5ae9fc512180..2b55a276dc5b 100644
--- a/drivers/dma/shdma.h
+++ b/drivers/dma/shdma.h
@@ -23,6 +23,12 @@
struct device;
+enum dmae_pm_state {
+ DMAE_PM_ESTABLISHED,
+ DMAE_PM_BUSY,
+ DMAE_PM_PENDING,
+};
+
struct sh_dmae_chan {
dma_cookie_t completed_cookie; /* The maximum cookie completed */
spinlock_t desc_lock; /* Descriptor operation lock */
@@ -38,6 +44,7 @@ struct sh_dmae_chan {
u32 __iomem *base;
char dev_id[16]; /* unique name per DMAC of channel */
int pm_error;
+ enum dmae_pm_state pm_state;
};
struct sh_dmae_device {
@@ -47,10 +54,14 @@ struct sh_dmae_device {
struct list_head node;
u32 __iomem *chan_reg;
u16 __iomem *dmars;
+ unsigned int chcr_offset;
+ u32 chcr_ie_bit;
};
#define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, common)
#define to_sh_desc(lh) container_of(lh, struct sh_desc, node)
#define tx_to_sh_desc(tx) container_of(tx, struct sh_desc, async_tx)
+#define to_sh_dev(chan) container_of(chan->common.device,\
+ struct sh_dmae_device, common)
#endif /* __DMA_SHDMA_H */
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index 8f222d4db7de..13259cad0ceb 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -6,13 +6,16 @@
* License terms: GNU General Public License (GPL) version 2
*/
+#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/amba/bus.h>
#include <plat/ste_dma40.h>
@@ -44,9 +47,6 @@
#define D40_ALLOC_PHY (1 << 30)
#define D40_ALLOC_LOG_FREE 0
-/* Hardware designer of the block */
-#define D40_HW_DESIGNER 0x8
-
/**
* enum 40_command - The different commands and/or statuses.
*
@@ -175,8 +175,10 @@ struct d40_base;
* @tasklet: Tasklet that gets scheduled from interrupt context to complete a
* transfer and call client callback.
* @client: Cliented owned descriptor list.
+ * @pending_queue: Submitted jobs, to be issued by issue_pending()
* @active: Active descriptor.
* @queue: Queued jobs.
+ * @prepare_queue: Prepared jobs.
* @dma_cfg: The client configuration of this dma channel.
* @configured: whether the dma_cfg configuration is valid
* @base: Pointer to the device instance struct.
@@ -185,6 +187,8 @@ struct d40_base;
* @log_def: Default logical channel settings.
* @lcla: Space for one dst src pair for logical channel transfers.
* @lcpa: Pointer to dst and src lcpa settings.
+ * @runtime_addr: runtime configured address.
+ * @runtime_direction: runtime configured direction.
*
* This struct can either "be" a logical or a physical channel.
*/
@@ -199,8 +203,10 @@ struct d40_chan {
struct dma_chan chan;
struct tasklet_struct tasklet;
struct list_head client;
+ struct list_head pending_queue;
struct list_head active;
struct list_head queue;
+ struct list_head prepare_queue;
struct stedma40_chan_cfg dma_cfg;
bool configured;
struct d40_base *base;
@@ -475,7 +481,6 @@ static struct d40_desc *d40_desc_get(struct d40_chan *d40c)
list_for_each_entry_safe(d, _d, &d40c->client, node)
if (async_tx_test_ack(&d->txd)) {
- d40_pool_lli_free(d40c, d);
d40_desc_remove(d);
desc = d;
memset(desc, 0, sizeof(*desc));
@@ -642,9 +647,25 @@ static struct d40_desc *d40_first_active_get(struct d40_chan *d40c)
return d;
}
+/* remove desc from current queue and add it to the pending_queue */
static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc)
{
- list_add_tail(&desc->node, &d40c->queue);
+ d40_desc_remove(desc);
+ desc->is_in_client_list = false;
+ list_add_tail(&desc->node, &d40c->pending_queue);
+}
+
+static struct d40_desc *d40_first_pending(struct d40_chan *d40c)
+{
+ struct d40_desc *d;
+
+ if (list_empty(&d40c->pending_queue))
+ return NULL;
+
+ d = list_first_entry(&d40c->pending_queue,
+ struct d40_desc,
+ node);
+ return d;
}
static struct d40_desc *d40_first_queued(struct d40_chan *d40c)
@@ -788,6 +809,7 @@ done:
static void d40_term_all(struct d40_chan *d40c)
{
struct d40_desc *d40d;
+ struct d40_desc *_d;
/* Release active descriptors */
while ((d40d = d40_first_active_get(d40c))) {
@@ -801,6 +823,26 @@ static void d40_term_all(struct d40_chan *d40c)
d40_desc_free(d40c, d40d);
}
+ /* Release pending descriptors */
+ while ((d40d = d40_first_pending(d40c))) {
+ d40_desc_remove(d40d);
+ d40_desc_free(d40c, d40d);
+ }
+
+ /* Release client owned descriptors */
+ if (!list_empty(&d40c->client))
+ list_for_each_entry_safe(d40d, _d, &d40c->client, node) {
+ d40_desc_remove(d40d);
+ d40_desc_free(d40c, d40d);
+ }
+
+ /* Release descriptors in prepare queue */
+ if (!list_empty(&d40c->prepare_queue))
+ list_for_each_entry_safe(d40d, _d,
+ &d40c->prepare_queue, node) {
+ d40_desc_remove(d40d);
+ d40_desc_free(d40c, d40d);
+ }
d40c->pending_tx = 0;
d40c->busy = false;
@@ -1188,7 +1230,6 @@ static void dma_tasklet(unsigned long data)
if (!d40d->cyclic) {
if (async_tx_test_ack(&d40d->txd)) {
- d40_pool_lli_free(d40c, d40d);
d40_desc_remove(d40d);
d40_desc_free(d40c, d40d);
} else {
@@ -1575,21 +1616,10 @@ static int d40_free_dma(struct d40_chan *d40c)
u32 event;
struct d40_phy_res *phy = d40c->phy_chan;
bool is_src;
- struct d40_desc *d;
- struct d40_desc *_d;
-
/* Terminate all queued and active transfers */
d40_term_all(d40c);
- /* Release client owned descriptors */
- if (!list_empty(&d40c->client))
- list_for_each_entry_safe(d, _d, &d40c->client, node) {
- d40_pool_lli_free(d40c, d);
- d40_desc_remove(d);
- d40_desc_free(d40c, d);
- }
-
if (phy == NULL) {
chan_err(d40c, "phy == null\n");
return -EINVAL;
@@ -1891,6 +1921,12 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src,
goto err;
}
+ /*
+ * add descriptor to the prepare queue in order to be able
+ * to free them later in terminate_all
+ */
+ list_add_tail(&desc->node, &chan->prepare_queue);
+
spin_unlock_irqrestore(&chan->lock, flags);
return &desc->txd;
@@ -2091,7 +2127,7 @@ dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
struct scatterlist *sg;
int i;
- sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_KERNEL);
+ sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_NOWAIT);
for (i = 0; i < periods; i++) {
sg_dma_address(&sg[i]) = dma_addr;
sg_dma_len(&sg[i]) = period_len;
@@ -2151,24 +2187,87 @@ static void d40_issue_pending(struct dma_chan *chan)
spin_lock_irqsave(&d40c->lock, flags);
- /* Busy means that pending jobs are already being processed */
+ list_splice_tail_init(&d40c->pending_queue, &d40c->queue);
+
+ /* Busy means that queued jobs are already being processed */
if (!d40c->busy)
(void) d40_queue_start(d40c);
spin_unlock_irqrestore(&d40c->lock, flags);
}
+static int
+dma40_config_to_halfchannel(struct d40_chan *d40c,
+ struct stedma40_half_channel_info *info,
+ enum dma_slave_buswidth width,
+ u32 maxburst)
+{
+ enum stedma40_periph_data_width addr_width;
+ int psize;
+
+ switch (width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ addr_width = STEDMA40_BYTE_WIDTH;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ addr_width = STEDMA40_HALFWORD_WIDTH;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ addr_width = STEDMA40_WORD_WIDTH;
+ break;
+ case DMA_SLAVE_BUSWIDTH_8_BYTES:
+ addr_width = STEDMA40_DOUBLEWORD_WIDTH;
+ break;
+ default:
+ dev_err(d40c->base->dev,
+ "illegal peripheral address width "
+ "requested (%d)\n",
+ width);
+ return -EINVAL;
+ }
+
+ if (chan_is_logical(d40c)) {
+ if (maxburst >= 16)
+ psize = STEDMA40_PSIZE_LOG_16;
+ else if (maxburst >= 8)
+ psize = STEDMA40_PSIZE_LOG_8;
+ else if (maxburst >= 4)
+ psize = STEDMA40_PSIZE_LOG_4;
+ else
+ psize = STEDMA40_PSIZE_LOG_1;
+ } else {
+ if (maxburst >= 16)
+ psize = STEDMA40_PSIZE_PHY_16;
+ else if (maxburst >= 8)
+ psize = STEDMA40_PSIZE_PHY_8;
+ else if (maxburst >= 4)
+ psize = STEDMA40_PSIZE_PHY_4;
+ else
+ psize = STEDMA40_PSIZE_PHY_1;
+ }
+
+ info->data_width = addr_width;
+ info->psize = psize;
+ info->flow_ctrl = STEDMA40_NO_FLOW_CTRL;
+
+ return 0;
+}
+
/* Runtime reconfiguration extension */
-static void d40_set_runtime_config(struct dma_chan *chan,
- struct dma_slave_config *config)
+static int d40_set_runtime_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
{
struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
struct stedma40_chan_cfg *cfg = &d40c->dma_cfg;
- enum dma_slave_buswidth config_addr_width;
+ enum dma_slave_buswidth src_addr_width, dst_addr_width;
dma_addr_t config_addr;
- u32 config_maxburst;
- enum stedma40_periph_data_width addr_width;
- int psize;
+ u32 src_maxburst, dst_maxburst;
+ int ret;
+
+ src_addr_width = config->src_addr_width;
+ src_maxburst = config->src_maxburst;
+ dst_addr_width = config->dst_addr_width;
+ dst_maxburst = config->dst_maxburst;
if (config->direction == DMA_FROM_DEVICE) {
dma_addr_t dev_addr_rx =
@@ -2187,8 +2286,11 @@ static void d40_set_runtime_config(struct dma_chan *chan,
cfg->dir);
cfg->dir = STEDMA40_PERIPH_TO_MEM;
- config_addr_width = config->src_addr_width;
- config_maxburst = config->src_maxburst;
+ /* Configure the memory side */
+ if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ dst_addr_width = src_addr_width;
+ if (dst_maxburst == 0)
+ dst_maxburst = src_maxburst;
} else if (config->direction == DMA_TO_DEVICE) {
dma_addr_t dev_addr_tx =
@@ -2207,68 +2309,39 @@ static void d40_set_runtime_config(struct dma_chan *chan,
cfg->dir);
cfg->dir = STEDMA40_MEM_TO_PERIPH;
- config_addr_width = config->dst_addr_width;
- config_maxburst = config->dst_maxburst;
-
+ /* Configure the memory side */
+ if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ src_addr_width = dst_addr_width;
+ if (src_maxburst == 0)
+ src_maxburst = dst_maxburst;
} else {
dev_err(d40c->base->dev,
"unrecognized channel direction %d\n",
config->direction);
- return;
+ return -EINVAL;
}
- switch (config_addr_width) {
- case DMA_SLAVE_BUSWIDTH_1_BYTE:
- addr_width = STEDMA40_BYTE_WIDTH;
- break;
- case DMA_SLAVE_BUSWIDTH_2_BYTES:
- addr_width = STEDMA40_HALFWORD_WIDTH;
- break;
- case DMA_SLAVE_BUSWIDTH_4_BYTES:
- addr_width = STEDMA40_WORD_WIDTH;
- break;
- case DMA_SLAVE_BUSWIDTH_8_BYTES:
- addr_width = STEDMA40_DOUBLEWORD_WIDTH;
- break;
- default:
+ if (src_maxburst * src_addr_width != dst_maxburst * dst_addr_width) {
dev_err(d40c->base->dev,
- "illegal peripheral address width "
- "requested (%d)\n",
- config->src_addr_width);
- return;
+ "src/dst width/maxburst mismatch: %d*%d != %d*%d\n",
+ src_maxburst,
+ src_addr_width,
+ dst_maxburst,
+ dst_addr_width);
+ return -EINVAL;
}
- if (chan_is_logical(d40c)) {
- if (config_maxburst >= 16)
- psize = STEDMA40_PSIZE_LOG_16;
- else if (config_maxburst >= 8)
- psize = STEDMA40_PSIZE_LOG_8;
- else if (config_maxburst >= 4)
- psize = STEDMA40_PSIZE_LOG_4;
- else
- psize = STEDMA40_PSIZE_LOG_1;
- } else {
- if (config_maxburst >= 16)
- psize = STEDMA40_PSIZE_PHY_16;
- else if (config_maxburst >= 8)
- psize = STEDMA40_PSIZE_PHY_8;
- else if (config_maxburst >= 4)
- psize = STEDMA40_PSIZE_PHY_4;
- else if (config_maxburst >= 2)
- psize = STEDMA40_PSIZE_PHY_2;
- else
- psize = STEDMA40_PSIZE_PHY_1;
- }
+ ret = dma40_config_to_halfchannel(d40c, &cfg->src_info,
+ src_addr_width,
+ src_maxburst);
+ if (ret)
+ return ret;
- /* Set up all the endpoint configs */
- cfg->src_info.data_width = addr_width;
- cfg->src_info.psize = psize;
- cfg->src_info.big_endian = false;
- cfg->src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
- cfg->dst_info.data_width = addr_width;
- cfg->dst_info.psize = psize;
- cfg->dst_info.big_endian = false;
- cfg->dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
+ ret = dma40_config_to_halfchannel(d40c, &cfg->dst_info,
+ dst_addr_width,
+ dst_maxburst);
+ if (ret)
+ return ret;
/* Fill in register values */
if (chan_is_logical(d40c))
@@ -2281,12 +2354,14 @@ static void d40_set_runtime_config(struct dma_chan *chan,
d40c->runtime_addr = config_addr;
d40c->runtime_direction = config->direction;
dev_dbg(d40c->base->dev,
- "configured channel %s for %s, data width %d, "
- "maxburst %d bytes, LE, no flow control\n",
+ "configured channel %s for %s, data width %d/%d, "
+ "maxburst %d/%d elements, LE, no flow control\n",
dma_chan_name(chan),
(config->direction == DMA_FROM_DEVICE) ? "RX" : "TX",
- config_addr_width,
- config_maxburst);
+ src_addr_width, dst_addr_width,
+ src_maxburst, dst_maxburst);
+
+ return 0;
}
static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -2307,9 +2382,8 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
case DMA_RESUME:
return d40_resume(d40c);
case DMA_SLAVE_CONFIG:
- d40_set_runtime_config(chan,
+ return d40_set_runtime_config(chan,
(struct dma_slave_config *) arg);
- return 0;
default:
break;
}
@@ -2340,7 +2414,9 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
INIT_LIST_HEAD(&d40c->active);
INIT_LIST_HEAD(&d40c->queue);
+ INIT_LIST_HEAD(&d40c->pending_queue);
INIT_LIST_HEAD(&d40c->client);
+ INIT_LIST_HEAD(&d40c->prepare_queue);
tasklet_init(&d40c->tasklet, dma_tasklet,
(unsigned long) d40c);
@@ -2501,25 +2577,6 @@ static int __init d40_phy_res_init(struct d40_base *base)
static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
{
- static const struct d40_reg_val dma_id_regs[] = {
- /* Peripheral Id */
- { .reg = D40_DREG_PERIPHID0, .val = 0x0040},
- { .reg = D40_DREG_PERIPHID1, .val = 0x0000},
- /*
- * D40_DREG_PERIPHID2 Depends on HW revision:
- * DB8500ed has 0x0008,
- * ? has 0x0018,
- * DB8500v1 has 0x0028
- * DB8500v2 has 0x0038
- */
- { .reg = D40_DREG_PERIPHID3, .val = 0x0000},
-
- /* PCell Id */
- { .reg = D40_DREG_CELLID0, .val = 0x000d},
- { .reg = D40_DREG_CELLID1, .val = 0x00f0},
- { .reg = D40_DREG_CELLID2, .val = 0x0005},
- { .reg = D40_DREG_CELLID3, .val = 0x00b1}
- };
struct stedma40_platform_data *plat_data;
struct clk *clk = NULL;
void __iomem *virtbase = NULL;
@@ -2528,8 +2585,9 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
int num_log_chans = 0;
int num_phy_chans;
int i;
- u32 val;
- u32 rev;
+ u32 pid;
+ u32 cid;
+ u8 rev;
clk = clk_get(&pdev->dev, NULL);
@@ -2553,32 +2611,32 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
if (!virtbase)
goto failure;
- /* HW version check */
- for (i = 0; i < ARRAY_SIZE(dma_id_regs); i++) {
- if (dma_id_regs[i].val !=
- readl(virtbase + dma_id_regs[i].reg)) {
- d40_err(&pdev->dev,
- "Unknown hardware! Expected 0x%x at 0x%x but got 0x%x\n",
- dma_id_regs[i].val,
- dma_id_regs[i].reg,
- readl(virtbase + dma_id_regs[i].reg));
- goto failure;
- }
- }
-
- /* Get silicon revision and designer */
- val = readl(virtbase + D40_DREG_PERIPHID2);
+ /* This is just a regular AMBA PrimeCell ID actually */
+ for (pid = 0, i = 0; i < 4; i++)
+ pid |= (readl(virtbase + resource_size(res) - 0x20 + 4 * i)
+ & 255) << (i * 8);
+ for (cid = 0, i = 0; i < 4; i++)
+ cid |= (readl(virtbase + resource_size(res) - 0x10 + 4 * i)
+ & 255) << (i * 8);
- if ((val & D40_DREG_PERIPHID2_DESIGNER_MASK) !=
- D40_HW_DESIGNER) {
+ if (cid != AMBA_CID) {
+ d40_err(&pdev->dev, "Unknown hardware! No PrimeCell ID\n");
+ goto failure;
+ }
+ if (AMBA_MANF_BITS(pid) != AMBA_VENDOR_ST) {
d40_err(&pdev->dev, "Unknown designer! Got %x wanted %x\n",
- val & D40_DREG_PERIPHID2_DESIGNER_MASK,
- D40_HW_DESIGNER);
+ AMBA_MANF_BITS(pid),
+ AMBA_VENDOR_ST);
goto failure;
}
-
- rev = (val & D40_DREG_PERIPHID2_REV_MASK) >>
- D40_DREG_PERIPHID2_REV_POS;
+ /*
+ * HW revision:
+ * DB8500ed has revision 0
+ * ? has revision 1
+ * DB8500v1 has revision 2
+ * DB8500v2 has revision 3
+ */
+ rev = AMBA_REV_BITS(pid);
/* The number of physical channels on this HW */
num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4;
diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h
index 195ee65ee7f3..b44c455158de 100644
--- a/drivers/dma/ste_dma40_ll.h
+++ b/drivers/dma/ste_dma40_ll.h
@@ -184,9 +184,6 @@
#define D40_DREG_PERIPHID0 0xFE0
#define D40_DREG_PERIPHID1 0xFE4
#define D40_DREG_PERIPHID2 0xFE8
-#define D40_DREG_PERIPHID2_REV_POS 4
-#define D40_DREG_PERIPHID2_REV_MASK (0xf << D40_DREG_PERIPHID2_REV_POS)
-#define D40_DREG_PERIPHID2_DESIGNER_MASK 0xf
#define D40_DREG_PERIPHID3 0xFEC
#define D40_DREG_CELLID0 0xFF0
#define D40_DREG_CELLID1 0xFF4
diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c
index f69f90a61873..a4a398f2ef61 100644
--- a/drivers/dma/timb_dma.c
+++ b/drivers/dma/timb_dma.c
@@ -753,7 +753,7 @@ static int __devinit td_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&td->dma.channels);
- for (i = 0; i < pdata->nr_channels; i++, td->dma.chancnt++) {
+ for (i = 0; i < pdata->nr_channels; i++) {
struct timb_dma_chan *td_chan = &td->channels[i];
struct timb_dma_platform_data_channel *pchan =
pdata->channels + i;
@@ -762,12 +762,11 @@ static int __devinit td_probe(struct platform_device *pdev)
if ((i % 2) == pchan->rx) {
dev_err(&pdev->dev, "Wrong channel configuration\n");
err = -EINVAL;
- goto err_tasklet_kill;
+ goto err_free_irq;
}
td_chan->chan.device = &td->dma;
td_chan->chan.cookie = 1;
- td_chan->chan.chan_id = i;
spin_lock_init(&td_chan->lock);
INIT_LIST_HEAD(&td_chan->active_list);
INIT_LIST_HEAD(&td_chan->queue);
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index af1a17d42bd7..5948a2194f50 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -41,7 +41,7 @@ config EDAC_DEBUG
config EDAC_DECODE_MCE
tristate "Decode MCEs in human-readable form (only on AMD for now)"
- depends on CPU_SUP_AMD && X86_MCE
+ depends on CPU_SUP_AMD && X86_MCE_AMD
default y
---help---
Enable this option if you want to decode Machine Check Exceptions
@@ -71,9 +71,6 @@ config EDAC_MM_EDAC
occurred so that a particular failing memory module can be
replaced. If unsure, select 'Y'.
-config EDAC_MCE
- bool
-
config EDAC_AMD64
tristate "AMD64 (Opteron, Athlon64) K8, F10h"
depends on EDAC_MM_EDAC && AMD_NB && X86_64 && EDAC_DECODE_MCE
@@ -173,8 +170,7 @@ config EDAC_I5400
config EDAC_I7CORE
tristate "Intel i7 Core (Nehalem) processors"
- depends on EDAC_MM_EDAC && PCI && X86
- select EDAC_MCE
+ depends on EDAC_MM_EDAC && PCI && X86 && X86_MCE_INTEL
help
Support for error detection and correction the Intel
i7 Core (Nehalem) Integrated Memory Controller that exists on
@@ -216,6 +212,14 @@ config EDAC_I7300
Support for error detection and correction the Intel
Clarksboro MCH (Intel 7300 chipset).
+config EDAC_SBRIDGE
+ tristate "Intel Sandy-Bridge Integrated MC"
+ depends on EDAC_MM_EDAC && PCI && X86_64 && X86_MCE_INTEL
+ depends on EXPERIMENTAL
+ help
+ Support for error detection and correction the Intel
+ Sandy Bridge Integrated Memory Controller.
+
config EDAC_MPC85XX
tristate "Freescale MPC83xx / MPC85xx"
depends on EDAC_MM_EDAC && FSL_SOC && (PPC_83xx || PPC_85xx)
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 3e239133e29e..196a63dd37c5 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -8,7 +8,6 @@
obj-$(CONFIG_EDAC) := edac_stub.o
obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o
-obj-$(CONFIG_EDAC_MCE) += edac_mce.o
edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o edac_pci_sysfs.o
edac_core-y += edac_module.o edac_device_sysfs.o
@@ -29,6 +28,7 @@ obj-$(CONFIG_EDAC_I5100) += i5100_edac.o
obj-$(CONFIG_EDAC_I5400) += i5400_edac.o
obj-$(CONFIG_EDAC_I7300) += i7300_edac.o
obj-$(CONFIG_EDAC_I7CORE) += i7core_edac.o
+obj-$(CONFIG_EDAC_SBRIDGE) += sb_edac.o
obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o
obj-$(CONFIG_EDAC_E752X) += e752x_edac.o
obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 9a8bebcf6b17..c9eee6d33e9a 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -114,10 +114,22 @@ static int f10_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
}
+/*
+ * Select DCT to which PCI cfg accesses are routed
+ */
+static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
+{
+ u32 reg = 0;
+
+ amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
+ reg &= 0xfffffffe;
+ reg |= dct;
+ amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
+}
+
static int f15_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
const char *func)
{
- u32 reg = 0;
u8 dct = 0;
if (addr >= 0x140 && addr <= 0x1a0) {
@@ -125,10 +137,7 @@ static int f15_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
addr -= 0x100;
}
- amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
- reg &= 0xfffffffe;
- reg |= dct;
- amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
+ f15h_select_dct(pvt, dct);
return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
}
@@ -198,6 +207,10 @@ static int amd64_set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
if (boot_cpu_data.x86 == 0xf)
min_scrubrate = 0x0;
+ /* F15h Erratum #505 */
+ if (boot_cpu_data.x86 == 0x15)
+ f15h_select_dct(pvt, 0);
+
return __amd64_set_scrub_rate(pvt->F3, bw, min_scrubrate);
}
@@ -207,6 +220,10 @@ static int amd64_get_scrub_rate(struct mem_ctl_info *mci)
u32 scrubval = 0;
int i, retval = -EINVAL;
+ /* F15h Erratum #505 */
+ if (boot_cpu_data.x86 == 0x15)
+ f15h_select_dct(pvt, 0);
+
amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
scrubval = scrubval & 0x001F;
@@ -751,10 +768,10 @@ static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
* Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
* are ECC capable.
*/
-static enum edac_type amd64_determine_edac_cap(struct amd64_pvt *pvt)
+static unsigned long amd64_determine_edac_cap(struct amd64_pvt *pvt)
{
u8 bit;
- enum dev_type edac_cap = EDAC_FLAG_NONE;
+ unsigned long edac_cap = EDAC_FLAG_NONE;
bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= K8_REV_F)
? 19
@@ -1953,11 +1970,9 @@ static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci,
amd64_handle_ue(mci, m);
}
-void amd64_decode_bus_error(int node_id, struct mce *m, u32 nbcfg)
+void amd64_decode_bus_error(int node_id, struct mce *m)
{
- struct mem_ctl_info *mci = mcis[node_id];
-
- __amd64_decode_bus_error(mci, m);
+ __amd64_decode_bus_error(mcis[node_id], m);
}
/*
diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c
index db1df59ae2b6..9a6a274e6925 100644
--- a/drivers/edac/cell_edac.c
+++ b/drivers/edac/cell_edac.c
@@ -140,7 +140,7 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
if (of_node_to_nid(np) != priv->node)
continue;
csrow->first_page = r.start >> PAGE_SHIFT;
- csrow->nr_pages = (r.end - r.start + 1) >> PAGE_SHIFT;
+ csrow->nr_pages = resource_size(&r) >> PAGE_SHIFT;
csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
csrow->mtype = MEM_XDR;
csrow->edac_mode = EDAC_SECDED;
diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c
index a687a0d16962..a774c0ddaf5b 100644
--- a/drivers/edac/cpc925_edac.c
+++ b/drivers/edac/cpc925_edac.c
@@ -90,6 +90,7 @@ enum apimask_bits {
ECC_MASK_ENABLE = (APIMASK_ECC_UE_H | APIMASK_ECC_CE_H |
APIMASK_ECC_UE_L | APIMASK_ECC_CE_L),
};
+#define APIMASK_ADI(n) CPC925_BIT(((n)+1))
/************************************************************
* Processor Interface Exception Register (APIEXCP)
@@ -581,16 +582,73 @@ static void cpc925_mc_check(struct mem_ctl_info *mci)
}
/******************** CPU err device********************************/
+static u32 cpc925_cpu_mask_disabled(void)
+{
+ struct device_node *cpus;
+ struct device_node *cpunode = NULL;
+ static u32 mask = 0;
+
+ /* use cached value if available */
+ if (mask != 0)
+ return mask;
+
+ mask = APIMASK_ADI0 | APIMASK_ADI1;
+
+ cpus = of_find_node_by_path("/cpus");
+ if (cpus == NULL) {
+ cpc925_printk(KERN_DEBUG, "No /cpus node !\n");
+ return 0;
+ }
+
+ while ((cpunode = of_get_next_child(cpus, cpunode)) != NULL) {
+ const u32 *reg = of_get_property(cpunode, "reg", NULL);
+
+ if (strcmp(cpunode->type, "cpu")) {
+ cpc925_printk(KERN_ERR, "Not a cpu node in /cpus: %s\n", cpunode->name);
+ continue;
+ }
+
+ if (reg == NULL || *reg > 2) {
+ cpc925_printk(KERN_ERR, "Bad reg value at %s\n", cpunode->full_name);
+ continue;
+ }
+
+ mask &= ~APIMASK_ADI(*reg);
+ }
+
+ if (mask != (APIMASK_ADI0 | APIMASK_ADI1)) {
+ /* We assume that each CPU sits on it's own PI and that
+ * for present CPUs the reg property equals to the PI
+ * interface id */
+ cpc925_printk(KERN_WARNING,
+ "Assuming PI id is equal to CPU MPIC id!\n");
+ }
+
+ of_node_put(cpunode);
+ of_node_put(cpus);
+
+ return mask;
+}
+
/* Enable CPU Errors detection */
static void cpc925_cpu_init(struct cpc925_dev_info *dev_info)
{
u32 apimask;
+ u32 cpumask;
apimask = __raw_readl(dev_info->vbase + REG_APIMASK_OFFSET);
- if ((apimask & CPU_MASK_ENABLE) == 0) {
- apimask |= CPU_MASK_ENABLE;
- __raw_writel(apimask, dev_info->vbase + REG_APIMASK_OFFSET);
+
+ cpumask = cpc925_cpu_mask_disabled();
+ if (apimask & cpumask) {
+ cpc925_printk(KERN_WARNING, "CPU(s) not present, "
+ "but enabled in APIMASK, disabling\n");
+ apimask &= ~cpumask;
}
+
+ if ((apimask & CPU_MASK_ENABLE) == 0)
+ apimask |= CPU_MASK_ENABLE;
+
+ __raw_writel(apimask, dev_info->vbase + REG_APIMASK_OFFSET);
}
/* Disable CPU Errors detection */
@@ -622,6 +680,9 @@ static void cpc925_cpu_check(struct edac_device_ctl_info *edac_dev)
if ((apiexcp & CPU_EXCP_DETECTED) == 0)
return;
+ if ((apiexcp & ~cpc925_cpu_mask_disabled()) == 0)
+ return;
+
apimask = __raw_readl(dev_info->vbase + REG_APIMASK_OFFSET);
cpc925_printk(KERN_INFO, "Processor Interface Fault\n"
"Processor Interface register dump:\n");
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h
index 55b8278bb172..fe90cd4a7ebc 100644
--- a/drivers/edac/edac_core.h
+++ b/drivers/edac/edac_core.h
@@ -34,11 +34,10 @@
#include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <linux/workqueue.h>
+#include <linux/edac.h>
-#define EDAC_MC_LABEL_LEN 31
#define EDAC_DEVICE_NAME_LEN 31
#define EDAC_ATTRIB_VALUE_LEN 15
-#define MC_PROC_NAME_MAX_LEN 7
#if PAGE_SHIFT < 20
#define PAGES_TO_MiB(pages) ((pages) >> (20 - PAGE_SHIFT))
@@ -101,353 +100,6 @@ extern int edac_debug_level;
#define edac_dev_name(dev) (dev)->dev_name
-/* memory devices */
-enum dev_type {
- DEV_UNKNOWN = 0,
- DEV_X1,
- DEV_X2,
- DEV_X4,
- DEV_X8,
- DEV_X16,
- DEV_X32, /* Do these parts exist? */
- DEV_X64 /* Do these parts exist? */
-};
-
-#define DEV_FLAG_UNKNOWN BIT(DEV_UNKNOWN)
-#define DEV_FLAG_X1 BIT(DEV_X1)
-#define DEV_FLAG_X2 BIT(DEV_X2)
-#define DEV_FLAG_X4 BIT(DEV_X4)
-#define DEV_FLAG_X8 BIT(DEV_X8)
-#define DEV_FLAG_X16 BIT(DEV_X16)
-#define DEV_FLAG_X32 BIT(DEV_X32)
-#define DEV_FLAG_X64 BIT(DEV_X64)
-
-/* memory types */
-enum mem_type {
- MEM_EMPTY = 0, /* Empty csrow */
- MEM_RESERVED, /* Reserved csrow type */
- MEM_UNKNOWN, /* Unknown csrow type */
- MEM_FPM, /* Fast page mode */
- MEM_EDO, /* Extended data out */
- MEM_BEDO, /* Burst Extended data out */
- MEM_SDR, /* Single data rate SDRAM */
- MEM_RDR, /* Registered single data rate SDRAM */
- MEM_DDR, /* Double data rate SDRAM */
- MEM_RDDR, /* Registered Double data rate SDRAM */
- MEM_RMBS, /* Rambus DRAM */
- MEM_DDR2, /* DDR2 RAM */
- MEM_FB_DDR2, /* fully buffered DDR2 */
- MEM_RDDR2, /* Registered DDR2 RAM */
- MEM_XDR, /* Rambus XDR */
- MEM_DDR3, /* DDR3 RAM */
- MEM_RDDR3, /* Registered DDR3 RAM */
-};
-
-#define MEM_FLAG_EMPTY BIT(MEM_EMPTY)
-#define MEM_FLAG_RESERVED BIT(MEM_RESERVED)
-#define MEM_FLAG_UNKNOWN BIT(MEM_UNKNOWN)
-#define MEM_FLAG_FPM BIT(MEM_FPM)
-#define MEM_FLAG_EDO BIT(MEM_EDO)
-#define MEM_FLAG_BEDO BIT(MEM_BEDO)
-#define MEM_FLAG_SDR BIT(MEM_SDR)
-#define MEM_FLAG_RDR BIT(MEM_RDR)
-#define MEM_FLAG_DDR BIT(MEM_DDR)
-#define MEM_FLAG_RDDR BIT(MEM_RDDR)
-#define MEM_FLAG_RMBS BIT(MEM_RMBS)
-#define MEM_FLAG_DDR2 BIT(MEM_DDR2)
-#define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2)
-#define MEM_FLAG_RDDR2 BIT(MEM_RDDR2)
-#define MEM_FLAG_XDR BIT(MEM_XDR)
-#define MEM_FLAG_DDR3 BIT(MEM_DDR3)
-#define MEM_FLAG_RDDR3 BIT(MEM_RDDR3)
-
-/* chipset Error Detection and Correction capabilities and mode */
-enum edac_type {
- EDAC_UNKNOWN = 0, /* Unknown if ECC is available */
- EDAC_NONE, /* Doesn't support ECC */
- EDAC_RESERVED, /* Reserved ECC type */
- EDAC_PARITY, /* Detects parity errors */
- EDAC_EC, /* Error Checking - no correction */
- EDAC_SECDED, /* Single bit error correction, Double detection */
- EDAC_S2ECD2ED, /* Chipkill x2 devices - do these exist? */
- EDAC_S4ECD4ED, /* Chipkill x4 devices */
- EDAC_S8ECD8ED, /* Chipkill x8 devices */
- EDAC_S16ECD16ED, /* Chipkill x16 devices */
-};
-
-#define EDAC_FLAG_UNKNOWN BIT(EDAC_UNKNOWN)
-#define EDAC_FLAG_NONE BIT(EDAC_NONE)
-#define EDAC_FLAG_PARITY BIT(EDAC_PARITY)
-#define EDAC_FLAG_EC BIT(EDAC_EC)
-#define EDAC_FLAG_SECDED BIT(EDAC_SECDED)
-#define EDAC_FLAG_S2ECD2ED BIT(EDAC_S2ECD2ED)
-#define EDAC_FLAG_S4ECD4ED BIT(EDAC_S4ECD4ED)
-#define EDAC_FLAG_S8ECD8ED BIT(EDAC_S8ECD8ED)
-#define EDAC_FLAG_S16ECD16ED BIT(EDAC_S16ECD16ED)
-
-/* scrubbing capabilities */
-enum scrub_type {
- SCRUB_UNKNOWN = 0, /* Unknown if scrubber is available */
- SCRUB_NONE, /* No scrubber */
- SCRUB_SW_PROG, /* SW progressive (sequential) scrubbing */
- SCRUB_SW_SRC, /* Software scrub only errors */
- SCRUB_SW_PROG_SRC, /* Progressive software scrub from an error */
- SCRUB_SW_TUNABLE, /* Software scrub frequency is tunable */
- SCRUB_HW_PROG, /* HW progressive (sequential) scrubbing */
- SCRUB_HW_SRC, /* Hardware scrub only errors */
- SCRUB_HW_PROG_SRC, /* Progressive hardware scrub from an error */
- SCRUB_HW_TUNABLE /* Hardware scrub frequency is tunable */
-};
-
-#define SCRUB_FLAG_SW_PROG BIT(SCRUB_SW_PROG)
-#define SCRUB_FLAG_SW_SRC BIT(SCRUB_SW_SRC)
-#define SCRUB_FLAG_SW_PROG_SRC BIT(SCRUB_SW_PROG_SRC)
-#define SCRUB_FLAG_SW_TUN BIT(SCRUB_SW_SCRUB_TUNABLE)
-#define SCRUB_FLAG_HW_PROG BIT(SCRUB_HW_PROG)
-#define SCRUB_FLAG_HW_SRC BIT(SCRUB_HW_SRC)
-#define SCRUB_FLAG_HW_PROG_SRC BIT(SCRUB_HW_PROG_SRC)
-#define SCRUB_FLAG_HW_TUN BIT(SCRUB_HW_TUNABLE)
-
-/* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */
-
-/* EDAC internal operation states */
-#define OP_ALLOC 0x100
-#define OP_RUNNING_POLL 0x201
-#define OP_RUNNING_INTERRUPT 0x202
-#define OP_RUNNING_POLL_INTR 0x203
-#define OP_OFFLINE 0x300
-
-/*
- * There are several things to be aware of that aren't at all obvious:
- *
- *
- * SOCKETS, SOCKET SETS, BANKS, ROWS, CHIP-SELECT ROWS, CHANNELS, etc..
- *
- * These are some of the many terms that are thrown about that don't always
- * mean what people think they mean (Inconceivable!). In the interest of
- * creating a common ground for discussion, terms and their definitions
- * will be established.
- *
- * Memory devices: The individual chip on a memory stick. These devices
- * commonly output 4 and 8 bits each. Grouping several
- * of these in parallel provides 64 bits which is common
- * for a memory stick.
- *
- * Memory Stick: A printed circuit board that aggregates multiple
- * memory devices in parallel. This is the atomic
- * memory component that is purchaseable by Joe consumer
- * and loaded into a memory socket.
- *
- * Socket: A physical connector on the motherboard that accepts
- * a single memory stick.
- *
- * Channel: Set of memory devices on a memory stick that must be
- * grouped in parallel with one or more additional
- * channels from other memory sticks. This parallel
- * grouping of the output from multiple channels are
- * necessary for the smallest granularity of memory access.
- * Some memory controllers are capable of single channel -
- * which means that memory sticks can be loaded
- * individually. Other memory controllers are only
- * capable of dual channel - which means that memory
- * sticks must be loaded as pairs (see "socket set").
- *
- * Chip-select row: All of the memory devices that are selected together.
- * for a single, minimum grain of memory access.
- * This selects all of the parallel memory devices across
- * all of the parallel channels. Common chip-select rows
- * for single channel are 64 bits, for dual channel 128
- * bits.
- *
- * Single-Ranked stick: A Single-ranked stick has 1 chip-select row of memory.
- * Motherboards commonly drive two chip-select pins to
- * a memory stick. A single-ranked stick, will occupy
- * only one of those rows. The other will be unused.
- *
- * Double-Ranked stick: A double-ranked stick has two chip-select rows which
- * access different sets of memory devices. The two
- * rows cannot be accessed concurrently.
- *
- * Double-sided stick: DEPRECATED TERM, see Double-Ranked stick.
- * A double-sided stick has two chip-select rows which
- * access different sets of memory devices. The two
- * rows cannot be accessed concurrently. "Double-sided"
- * is irrespective of the memory devices being mounted
- * on both sides of the memory stick.
- *
- * Socket set: All of the memory sticks that are required for
- * a single memory access or all of the memory sticks
- * spanned by a chip-select row. A single socket set
- * has two chip-select rows and if double-sided sticks
- * are used these will occupy those chip-select rows.
- *
- * Bank: This term is avoided because it is unclear when
- * needing to distinguish between chip-select rows and
- * socket sets.
- *
- * Controller pages:
- *
- * Physical pages:
- *
- * Virtual pages:
- *
- *
- * STRUCTURE ORGANIZATION AND CHOICES
- *
- *
- *
- * PS - I enjoyed writing all that about as much as you enjoyed reading it.
- */
-
-struct channel_info {
- int chan_idx; /* channel index */
- u32 ce_count; /* Correctable Errors for this CHANNEL */
- char label[EDAC_MC_LABEL_LEN + 1]; /* DIMM label on motherboard */
- struct csrow_info *csrow; /* the parent */
-};
-
-struct csrow_info {
- unsigned long first_page; /* first page number in dimm */
- unsigned long last_page; /* last page number in dimm */
- unsigned long page_mask; /* used for interleaving -
- * 0UL for non intlv
- */
- u32 nr_pages; /* number of pages in csrow */
- u32 grain; /* granularity of reported error in bytes */
- int csrow_idx; /* the chip-select row */
- enum dev_type dtype; /* memory device type */
- u32 ue_count; /* Uncorrectable Errors for this csrow */
- u32 ce_count; /* Correctable Errors for this csrow */
- enum mem_type mtype; /* memory csrow type */
- enum edac_type edac_mode; /* EDAC mode for this csrow */
- struct mem_ctl_info *mci; /* the parent */
-
- struct kobject kobj; /* sysfs kobject for this csrow */
-
- /* channel information for this csrow */
- u32 nr_channels;
- struct channel_info *channels;
-};
-
-struct mcidev_sysfs_group {
- const char *name; /* group name */
- const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */
-};
-
-struct mcidev_sysfs_group_kobj {
- struct list_head list; /* list for all instances within a mc */
-
- struct kobject kobj; /* kobj for the group */
-
- const struct mcidev_sysfs_group *grp; /* group description table */
- struct mem_ctl_info *mci; /* the parent */
-};
-
-/* mcidev_sysfs_attribute structure
- * used for driver sysfs attributes and in mem_ctl_info
- * sysfs top level entries
- */
-struct mcidev_sysfs_attribute {
- /* It should use either attr or grp */
- struct attribute attr;
- const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */
-
- /* Ops for show/store values at the attribute - not used on group */
- ssize_t (*show)(struct mem_ctl_info *,char *);
- ssize_t (*store)(struct mem_ctl_info *, const char *,size_t);
-};
-
-/* MEMORY controller information structure
- */
-struct mem_ctl_info {
- struct list_head link; /* for global list of mem_ctl_info structs */
-
- struct module *owner; /* Module owner of this control struct */
-
- unsigned long mtype_cap; /* memory types supported by mc */
- unsigned long edac_ctl_cap; /* Mem controller EDAC capabilities */
- unsigned long edac_cap; /* configuration capabilities - this is
- * closely related to edac_ctl_cap. The
- * difference is that the controller may be
- * capable of s4ecd4ed which would be listed
- * in edac_ctl_cap, but if channels aren't
- * capable of s4ecd4ed then the edac_cap would
- * not have that capability.
- */
- unsigned long scrub_cap; /* chipset scrub capabilities */
- enum scrub_type scrub_mode; /* current scrub mode */
-
- /* Translates sdram memory scrub rate given in bytes/sec to the
- internal representation and configures whatever else needs
- to be configured.
- */
- int (*set_sdram_scrub_rate) (struct mem_ctl_info * mci, u32 bw);
-
- /* Get the current sdram memory scrub rate from the internal
- representation and converts it to the closest matching
- bandwidth in bytes/sec.
- */
- int (*get_sdram_scrub_rate) (struct mem_ctl_info * mci);
-
-
- /* pointer to edac checking routine */
- void (*edac_check) (struct mem_ctl_info * mci);
-
- /*
- * Remaps memory pages: controller pages to physical pages.
- * For most MC's, this will be NULL.
- */
- /* FIXME - why not send the phys page to begin with? */
- unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci,
- unsigned long page);
- int mc_idx;
- int nr_csrows;
- struct csrow_info *csrows;
- /*
- * FIXME - what about controllers on other busses? - IDs must be
- * unique. dev pointer should be sufficiently unique, but
- * BUS:SLOT.FUNC numbers may not be unique.
- */
- struct device *dev;
- const char *mod_name;
- const char *mod_ver;
- const char *ctl_name;
- const char *dev_name;
- char proc_name[MC_PROC_NAME_MAX_LEN + 1];
- void *pvt_info;
- u32 ue_noinfo_count; /* Uncorrectable Errors w/o info */
- u32 ce_noinfo_count; /* Correctable Errors w/o info */
- u32 ue_count; /* Total Uncorrectable Errors for this MC */
- u32 ce_count; /* Total Correctable Errors for this MC */
- unsigned long start_time; /* mci load start time (in jiffies) */
-
- struct completion complete;
-
- /* edac sysfs device control */
- struct kobject edac_mci_kobj;
-
- /* list for all grp instances within a mc */
- struct list_head grp_kobj_list;
-
- /* Additional top controller level attributes, but specified
- * by the low level driver.
- *
- * Set by the low level driver to provide attributes at the
- * controller level, same level as 'ue_count' and 'ce_count' above.
- * An array of structures, NULL terminated
- *
- * If attributes are desired, then set to array of attributes
- * If no attributes are desired, leave NULL
- */
- const struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes;
-
- /* work struct for this MC */
- struct delayed_work work;
-
- /* the internal state of this controller instance */
- int op_state;
-};
-
/*
* The following are the structures to provide for a generic
* or abstract 'edac_device'. This set of structures and the
diff --git a/drivers/edac/edac_mce.c b/drivers/edac/edac_mce.c
deleted file mode 100644
index 9ccdc5b140e7..000000000000
--- a/drivers/edac/edac_mce.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Provides edac interface to mcelog events
- *
- * This file may be distributed under the terms of the
- * GNU General Public License version 2.
- *
- * Copyright (c) 2009 by:
- * Mauro Carvalho Chehab <mchehab@redhat.com>
- *
- * Red Hat Inc. http://www.redhat.com
- */
-
-#include <linux/module.h>
-#include <linux/edac_mce.h>
-#include <asm/mce.h>
-
-int edac_mce_enabled;
-EXPORT_SYMBOL_GPL(edac_mce_enabled);
-
-
-/*
- * Extension interface
- */
-
-static LIST_HEAD(edac_mce_list);
-static DEFINE_MUTEX(edac_mce_lock);
-
-int edac_mce_register(struct edac_mce *edac_mce)
-{
- mutex_lock(&edac_mce_lock);
- list_add_tail(&edac_mce->list, &edac_mce_list);
- mutex_unlock(&edac_mce_lock);
- return 0;
-}
-EXPORT_SYMBOL(edac_mce_register);
-
-void edac_mce_unregister(struct edac_mce *edac_mce)
-{
- mutex_lock(&edac_mce_lock);
- list_del(&edac_mce->list);
- mutex_unlock(&edac_mce_lock);
-}
-EXPORT_SYMBOL(edac_mce_unregister);
-
-int edac_mce_parse(struct mce *mce)
-{
- struct edac_mce *edac_mce;
-
- list_for_each_entry(edac_mce, &edac_mce_list, list) {
- if (edac_mce->check_error(edac_mce->priv, mce))
- return 1;
- }
-
- /* Nobody queued the error */
- return 0;
-}
-EXPORT_SYMBOL_GPL(edac_mce_parse);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
-MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
-MODULE_DESCRIPTION("EDAC Driver for mcelog captured errors");
diff --git a/drivers/edac/edac_stub.c b/drivers/edac/edac_stub.c
index aab970760b75..86ad2eee1201 100644
--- a/drivers/edac/edac_stub.c
+++ b/drivers/edac/edac_stub.c
@@ -14,7 +14,7 @@
*/
#include <linux/module.h>
#include <linux/edac.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/edac.h>
int edac_op_state = EDAC_OPSTATE_INVAL;
diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c
index a76fe8366b68..6104dba380b6 100644
--- a/drivers/edac/i7300_edac.c
+++ b/drivers/edac/i7300_edac.c
@@ -372,7 +372,7 @@ static const char *get_err_from_table(const char *table[], int size, int pos)
static void i7300_process_error_global(struct mem_ctl_info *mci)
{
struct i7300_pvt *pvt;
- u32 errnum, value;
+ u32 errnum, error_reg;
unsigned long errors;
const char *specific;
bool is_fatal;
@@ -381,9 +381,9 @@ static void i7300_process_error_global(struct mem_ctl_info *mci)
/* read in the 1st FATAL error register */
pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
- FERR_GLOBAL_HI, &value);
- if (unlikely(value)) {
- errors = value;
+ FERR_GLOBAL_HI, &error_reg);
+ if (unlikely(error_reg)) {
+ errors = error_reg;
errnum = find_first_bit(&errors,
ARRAY_SIZE(ferr_global_hi_name));
specific = GET_ERR_FROM_TABLE(ferr_global_hi_name, errnum);
@@ -391,15 +391,15 @@ static void i7300_process_error_global(struct mem_ctl_info *mci)
/* Clear the error bit */
pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
- FERR_GLOBAL_HI, value);
+ FERR_GLOBAL_HI, error_reg);
goto error_global;
}
pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
- FERR_GLOBAL_LO, &value);
- if (unlikely(value)) {
- errors = value;
+ FERR_GLOBAL_LO, &error_reg);
+ if (unlikely(error_reg)) {
+ errors = error_reg;
errnum = find_first_bit(&errors,
ARRAY_SIZE(ferr_global_lo_name));
specific = GET_ERR_FROM_TABLE(ferr_global_lo_name, errnum);
@@ -407,7 +407,7 @@ static void i7300_process_error_global(struct mem_ctl_info *mci)
/* Clear the error bit */
pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
- FERR_GLOBAL_LO, value);
+ FERR_GLOBAL_LO, error_reg);
goto error_global;
}
@@ -427,7 +427,7 @@ error_global:
static void i7300_process_fbd_error(struct mem_ctl_info *mci)
{
struct i7300_pvt *pvt;
- u32 errnum, value;
+ u32 errnum, value, error_reg;
u16 val16;
unsigned branch, channel, bank, rank, cas, ras;
u32 syndrome;
@@ -440,14 +440,14 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
/* read in the 1st FATAL error register */
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
- FERR_FAT_FBD, &value);
- if (unlikely(value & FERR_FAT_FBD_ERR_MASK)) {
- errors = value & FERR_FAT_FBD_ERR_MASK ;
+ FERR_FAT_FBD, &error_reg);
+ if (unlikely(error_reg & FERR_FAT_FBD_ERR_MASK)) {
+ errors = error_reg & FERR_FAT_FBD_ERR_MASK ;
errnum = find_first_bit(&errors,
ARRAY_SIZE(ferr_fat_fbd_name));
specific = GET_ERR_FROM_TABLE(ferr_fat_fbd_name, errnum);
+ branch = (GET_FBD_FAT_IDX(error_reg) == 2) ? 1 : 0;
- branch = (GET_FBD_FAT_IDX(value) == 2) ? 1 : 0;
pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map,
NRECMEMA, &val16);
bank = NRECMEMA_BANK(val16);
@@ -455,11 +455,14 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
NRECMEMB, &value);
-
is_wr = NRECMEMB_IS_WR(value);
cas = NRECMEMB_CAS(value);
ras = NRECMEMB_RAS(value);
+ /* Clean the error register */
+ pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+ FERR_FAT_FBD, error_reg);
+
snprintf(pvt->tmp_prt_buffer, PAGE_SIZE,
"FATAL (Branch=%d DRAM-Bank=%d %s "
"RAS=%d CAS=%d Err=0x%lx (%s))",
@@ -476,21 +479,17 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
/* read in the 1st NON-FATAL error register */
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
- FERR_NF_FBD, &value);
- if (unlikely(value & FERR_NF_FBD_ERR_MASK)) {
- errors = value & FERR_NF_FBD_ERR_MASK;
+ FERR_NF_FBD, &error_reg);
+ if (unlikely(error_reg & FERR_NF_FBD_ERR_MASK)) {
+ errors = error_reg & FERR_NF_FBD_ERR_MASK;
errnum = find_first_bit(&errors,
ARRAY_SIZE(ferr_nf_fbd_name));
specific = GET_ERR_FROM_TABLE(ferr_nf_fbd_name, errnum);
-
- /* Clear the error bit */
- pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
- FERR_GLOBAL_LO, value);
+ branch = (GET_FBD_FAT_IDX(error_reg) == 2) ? 1 : 0;
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
REDMEMA, &syndrome);
- branch = (GET_FBD_FAT_IDX(value) == 2) ? 1 : 0;
pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map,
RECMEMA, &val16);
bank = RECMEMA_BANK(val16);
@@ -498,18 +497,20 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
RECMEMB, &value);
-
is_wr = RECMEMB_IS_WR(value);
cas = RECMEMB_CAS(value);
ras = RECMEMB_RAS(value);
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
REDMEMB, &value);
-
channel = (branch << 1);
if (IS_SECOND_CH(value))
channel++;
+ /* Clear the error bit */
+ pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+ FERR_NF_FBD, error_reg);
+
/* Form out message */
snprintf(pvt->tmp_prt_buffer, PAGE_SIZE,
"Corrected error (Branch=%d, Channel %d), "
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 04f1e7ce02b1..70ad8923f1d7 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -31,11 +31,13 @@
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/dmi.h>
#include <linux/edac.h>
#include <linux/mmzone.h>
-#include <linux/edac_mce.h>
#include <linux/smp.h>
+#include <asm/mce.h>
#include <asm/processor.h>
+#include <asm/div64.h>
#include "edac_core.h"
@@ -78,6 +80,8 @@ MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
/* OFFSETS for Device 0 Function 0 */
#define MC_CFG_CONTROL 0x90
+ #define MC_CFG_UNLOCK 0x02
+ #define MC_CFG_LOCK 0x00
/* OFFSETS for Device 3 Function 0 */
@@ -98,6 +102,15 @@ MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
#define DIMM0_COR_ERR(r) ((r) & 0x7fff)
/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
+#define MC_SSRCONTROL 0x48
+ #define SSR_MODE_DISABLE 0x00
+ #define SSR_MODE_ENABLE 0x01
+ #define SSR_MODE_MASK 0x03
+
+#define MC_SCRUB_CONTROL 0x4c
+ #define STARTSCRUB (1 << 24)
+ #define SCRUBINTERVAL_MASK 0xffffff
+
#define MC_COR_ECC_CNT_0 0x80
#define MC_COR_ECC_CNT_1 0x84
#define MC_COR_ECC_CNT_2 0x88
@@ -253,10 +266,7 @@ struct i7core_pvt {
unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
- unsigned int is_registered;
-
- /* mcelog glue */
- struct edac_mce edac_mce;
+ bool is_registered, enable_scrub;
/* Fifo double buffers */
struct mce mce_entry[MCE_LOG_LEN];
@@ -268,6 +278,9 @@ struct i7core_pvt {
/* Count indicator to show errors not got */
unsigned mce_overrun;
+ /* DCLK Frequency used for computing scrub rate */
+ int dclk_freq;
+
/* Struct to control EDAC polling */
struct edac_pci_ctl_info *i7core_pci;
};
@@ -281,8 +294,7 @@ static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
/* Memory controller */
{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
-
- /* Exists only for RDIMM */
+ /* Exists only for RDIMM */
{ PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
{ PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
@@ -303,6 +315,16 @@ static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
+
+ /* Generic Non-core registers */
+ /*
+ * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
+ * On Xeon 55xx, however, it has a different id (8086:2c40). So,
+ * the probing code needs to test for the other address in case of
+ * failure of this one
+ */
+ { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
+
};
static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
@@ -319,6 +341,12 @@ static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
{ PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
{ PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
{ PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) },
+
+ /*
+ * This is the PCI device has an alternate address on some
+ * processors like Core i7 860
+ */
+ { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) },
};
static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
@@ -346,6 +374,10 @@ static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) },
+
+ /* Generic Non-core registers */
+ { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) },
+
};
#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
@@ -714,6 +746,10 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
csr->edac_mode = mode;
csr->mtype = mtype;
+ snprintf(csr->channels[0].label,
+ sizeof(csr->channels[0].label),
+ "CPU#%uChannel#%u_DIMM#%u",
+ pvt->i7core_dev->socket, i, j);
csrow++;
}
@@ -731,7 +767,7 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
debugf1("\t\t%#x\t%#x\t%#x\n",
(value[j] >> 27) & 0x1,
(value[j] >> 24) & 0x7,
- (value[j] && ((1 << 24) - 1)));
+ (value[j] & ((1 << 24) - 1)));
}
return 0;
@@ -1324,6 +1360,20 @@ static int i7core_get_onedevice(struct pci_dev **prev,
pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
dev_descr->dev_id, *prev);
+ /*
+ * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
+ * is at addr 8086:2c40, instead of 8086:2c41. So, we need
+ * to probe for the alternate address in case of failure
+ */
+ if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
+
+ if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev)
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
+ *prev);
+
if (!pdev) {
if (*prev) {
*prev = pdev;
@@ -1444,8 +1494,10 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
struct i7core_pvt *pvt = mci->pvt_info;
struct pci_dev *pdev;
int i, func, slot;
+ char *family;
- pvt->is_registered = 0;
+ pvt->is_registered = false;
+ pvt->enable_scrub = false;
for (i = 0; i < i7core_dev->n_devs; i++) {
pdev = i7core_dev->pdev[i];
if (!pdev)
@@ -1461,9 +1513,37 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
if (unlikely(func > MAX_CHAN_FUNC))
goto error;
pvt->pci_ch[slot - 4][func] = pdev;
- } else if (!slot && !func)
+ } else if (!slot && !func) {
pvt->pci_noncore = pdev;
- else
+
+ /* Detect the processor family */
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_INTEL_I7_NONCORE:
+ family = "Xeon 35xx/ i7core";
+ pvt->enable_scrub = false;
+ break;
+ case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT:
+ family = "i7-800/i5-700";
+ pvt->enable_scrub = false;
+ break;
+ case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE:
+ family = "Xeon 34xx";
+ pvt->enable_scrub = false;
+ break;
+ case PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT:
+ family = "Xeon 55xx";
+ pvt->enable_scrub = true;
+ break;
+ case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2:
+ family = "Xeon 56xx / i7-900";
+ pvt->enable_scrub = true;
+ break;
+ default:
+ family = "unknown";
+ pvt->enable_scrub = false;
+ }
+ debugf0("Detected a processor type %s\n", family);
+ } else
goto error;
debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
@@ -1472,7 +1552,7 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
if (PCI_SLOT(pdev->devfn) == 3 &&
PCI_FUNC(pdev->devfn) == 2)
- pvt->is_registered = 1;
+ pvt->is_registered = true;
}
return 0;
@@ -1670,7 +1750,7 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci,
char *type, *optype, *err, *msg;
unsigned long error = m->status & 0x1ff0000l;
u32 optypenum = (m->status >> 4) & 0x07;
- u32 core_err_cnt = (m->status >> 38) && 0x7fff;
+ u32 core_err_cnt = (m->status >> 38) & 0x7fff;
u32 dimm = (m->misc >> 16) & 0x3;
u32 channel = (m->misc >> 18) & 0x3;
u32 syndrome = m->misc >> 32;
@@ -1826,33 +1906,43 @@ check_ce_error:
* WARNING: As this routine should be called at NMI time, extra care should
* be taken to avoid deadlocks, and to be as fast as possible.
*/
-static int i7core_mce_check_error(void *priv, struct mce *mce)
+static int i7core_mce_check_error(struct notifier_block *nb, unsigned long val,
+ void *data)
{
- struct mem_ctl_info *mci = priv;
- struct i7core_pvt *pvt = mci->pvt_info;
+ struct mce *mce = (struct mce *)data;
+ struct i7core_dev *i7_dev;
+ struct mem_ctl_info *mci;
+ struct i7core_pvt *pvt;
+
+ i7_dev = get_i7core_dev(mce->socketid);
+ if (!i7_dev)
+ return NOTIFY_BAD;
+
+ mci = i7_dev->mci;
+ pvt = mci->pvt_info;
/*
* Just let mcelog handle it if the error is
* outside the memory controller
*/
if (((mce->status & 0xffff) >> 7) != 1)
- return 0;
+ return NOTIFY_DONE;
/* Bank 8 registers are the only ones that we know how to handle */
if (mce->bank != 8)
- return 0;
+ return NOTIFY_DONE;
#ifdef CONFIG_SMP
/* Only handle if it is the right mc controller */
- if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket)
- return 0;
+ if (mce->socketid != pvt->i7core_dev->socket)
+ return NOTIFY_DONE;
#endif
smp_rmb();
if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
smp_wmb();
pvt->mce_overrun++;
- return 0;
+ return NOTIFY_DONE;
}
/* Copy memory error at the ringbuffer */
@@ -1865,7 +1955,240 @@ static int i7core_mce_check_error(void *priv, struct mce *mce)
i7core_check_error(mci);
/* Advise mcelog that the errors were handled */
- return 1;
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block i7_mce_dec = {
+ .notifier_call = i7core_mce_check_error,
+};
+
+struct memdev_dmi_entry {
+ u8 type;
+ u8 length;
+ u16 handle;
+ u16 phys_mem_array_handle;
+ u16 mem_err_info_handle;
+ u16 total_width;
+ u16 data_width;
+ u16 size;
+ u8 form;
+ u8 device_set;
+ u8 device_locator;
+ u8 bank_locator;
+ u8 memory_type;
+ u16 type_detail;
+ u16 speed;
+ u8 manufacturer;
+ u8 serial_number;
+ u8 asset_tag;
+ u8 part_number;
+ u8 attributes;
+ u32 extended_size;
+ u16 conf_mem_clk_speed;
+} __attribute__((__packed__));
+
+
+/*
+ * Decode the DRAM Clock Frequency, be paranoid, make sure that all
+ * memory devices show the same speed, and if they don't then consider
+ * all speeds to be invalid.
+ */
+static void decode_dclk(const struct dmi_header *dh, void *_dclk_freq)
+{
+ int *dclk_freq = _dclk_freq;
+ u16 dmi_mem_clk_speed;
+
+ if (*dclk_freq == -1)
+ return;
+
+ if (dh->type == DMI_ENTRY_MEM_DEVICE) {
+ struct memdev_dmi_entry *memdev_dmi_entry =
+ (struct memdev_dmi_entry *)dh;
+ unsigned long conf_mem_clk_speed_offset =
+ (unsigned long)&memdev_dmi_entry->conf_mem_clk_speed -
+ (unsigned long)&memdev_dmi_entry->type;
+ unsigned long speed_offset =
+ (unsigned long)&memdev_dmi_entry->speed -
+ (unsigned long)&memdev_dmi_entry->type;
+
+ /* Check that a DIMM is present */
+ if (memdev_dmi_entry->size == 0)
+ return;
+
+ /*
+ * Pick the configured speed if it's available, otherwise
+ * pick the DIMM speed, or we don't have a speed.
+ */
+ if (memdev_dmi_entry->length > conf_mem_clk_speed_offset) {
+ dmi_mem_clk_speed =
+ memdev_dmi_entry->conf_mem_clk_speed;
+ } else if (memdev_dmi_entry->length > speed_offset) {
+ dmi_mem_clk_speed = memdev_dmi_entry->speed;
+ } else {
+ *dclk_freq = -1;
+ return;
+ }
+
+ if (*dclk_freq == 0) {
+ /* First pass, speed was 0 */
+ if (dmi_mem_clk_speed > 0) {
+ /* Set speed if a valid speed is read */
+ *dclk_freq = dmi_mem_clk_speed;
+ } else {
+ /* Otherwise we don't have a valid speed */
+ *dclk_freq = -1;
+ }
+ } else if (*dclk_freq > 0 &&
+ *dclk_freq != dmi_mem_clk_speed) {
+ /*
+ * If we have a speed, check that all DIMMS are the same
+ * speed, otherwise set the speed as invalid.
+ */
+ *dclk_freq = -1;
+ }
+ }
+}
+
+/*
+ * The default DCLK frequency is used as a fallback if we
+ * fail to find anything reliable in the DMI. The value
+ * is taken straight from the datasheet.
+ */
+#define DEFAULT_DCLK_FREQ 800
+
+static int get_dclk_freq(void)
+{
+ int dclk_freq = 0;
+
+ dmi_walk(decode_dclk, (void *)&dclk_freq);
+
+ if (dclk_freq < 1)
+ return DEFAULT_DCLK_FREQ;
+
+ return dclk_freq;
+}
+
+/*
+ * set_sdram_scrub_rate This routine sets byte/sec bandwidth scrub rate
+ * to hardware according to SCRUBINTERVAL formula
+ * found in datasheet.
+ */
+static int set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 new_bw)
+{
+ struct i7core_pvt *pvt = mci->pvt_info;
+ struct pci_dev *pdev;
+ u32 dw_scrub;
+ u32 dw_ssr;
+
+ /* Get data from the MC register, function 2 */
+ pdev = pvt->pci_mcr[2];
+ if (!pdev)
+ return -ENODEV;
+
+ pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &dw_scrub);
+
+ if (new_bw == 0) {
+ /* Prepare to disable petrol scrub */
+ dw_scrub &= ~STARTSCRUB;
+ /* Stop the patrol scrub engine */
+ write_and_test(pdev, MC_SCRUB_CONTROL,
+ dw_scrub & ~SCRUBINTERVAL_MASK);
+
+ /* Get current status of scrub rate and set bit to disable */
+ pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
+ dw_ssr &= ~SSR_MODE_MASK;
+ dw_ssr |= SSR_MODE_DISABLE;
+ } else {
+ const int cache_line_size = 64;
+ const u32 freq_dclk_mhz = pvt->dclk_freq;
+ unsigned long long scrub_interval;
+ /*
+ * Translate the desired scrub rate to a register value and
+ * program the corresponding register value.
+ */
+ scrub_interval = (unsigned long long)freq_dclk_mhz *
+ cache_line_size * 1000000;
+ do_div(scrub_interval, new_bw);
+
+ if (!scrub_interval || scrub_interval > SCRUBINTERVAL_MASK)
+ return -EINVAL;
+
+ dw_scrub = SCRUBINTERVAL_MASK & scrub_interval;
+
+ /* Start the patrol scrub engine */
+ pci_write_config_dword(pdev, MC_SCRUB_CONTROL,
+ STARTSCRUB | dw_scrub);
+
+ /* Get current status of scrub rate and set bit to enable */
+ pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
+ dw_ssr &= ~SSR_MODE_MASK;
+ dw_ssr |= SSR_MODE_ENABLE;
+ }
+ /* Disable or enable scrubbing */
+ pci_write_config_dword(pdev, MC_SSRCONTROL, dw_ssr);
+
+ return new_bw;
+}
+
+/*
+ * get_sdram_scrub_rate This routine convert current scrub rate value
+ * into byte/sec bandwidth accourding to
+ * SCRUBINTERVAL formula found in datasheet.
+ */
+static int get_sdram_scrub_rate(struct mem_ctl_info *mci)
+{
+ struct i7core_pvt *pvt = mci->pvt_info;
+ struct pci_dev *pdev;
+ const u32 cache_line_size = 64;
+ const u32 freq_dclk_mhz = pvt->dclk_freq;
+ unsigned long long scrub_rate;
+ u32 scrubval;
+
+ /* Get data from the MC register, function 2 */
+ pdev = pvt->pci_mcr[2];
+ if (!pdev)
+ return -ENODEV;
+
+ /* Get current scrub control data */
+ pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &scrubval);
+
+ /* Mask highest 8-bits to 0 */
+ scrubval &= SCRUBINTERVAL_MASK;
+ if (!scrubval)
+ return 0;
+
+ /* Calculate scrub rate value into byte/sec bandwidth */
+ scrub_rate = (unsigned long long)freq_dclk_mhz *
+ 1000000 * cache_line_size;
+ do_div(scrub_rate, scrubval);
+ return (int)scrub_rate;
+}
+
+static void enable_sdram_scrub_setting(struct mem_ctl_info *mci)
+{
+ struct i7core_pvt *pvt = mci->pvt_info;
+ u32 pci_lock;
+
+ /* Unlock writes to pci registers */
+ pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
+ pci_lock &= ~0x3;
+ pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
+ pci_lock | MC_CFG_UNLOCK);
+
+ mci->set_sdram_scrub_rate = set_sdram_scrub_rate;
+ mci->get_sdram_scrub_rate = get_sdram_scrub_rate;
+}
+
+static void disable_sdram_scrub_setting(struct mem_ctl_info *mci)
+{
+ struct i7core_pvt *pvt = mci->pvt_info;
+ u32 pci_lock;
+
+ /* Lock writes to pci registers */
+ pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
+ pci_lock &= ~0x3;
+ pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
+ pci_lock | MC_CFG_LOCK);
}
static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
@@ -1874,7 +2197,8 @@ static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
&pvt->i7core_dev->pdev[0]->dev,
EDAC_MOD_STR);
if (unlikely(!pvt->i7core_pci))
- pr_warn("Unable to setup PCI error report via EDAC\n");
+ i7core_printk(KERN_WARNING,
+ "Unable to setup PCI error report via EDAC\n");
}
static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
@@ -1906,8 +2230,11 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
__func__, mci, &i7core_dev->pdev[0]->dev);
- /* Disable MCE NMI handler */
- edac_mce_unregister(&pvt->edac_mce);
+ /* Disable scrubrate setting */
+ if (pvt->enable_scrub)
+ disable_sdram_scrub_setting(mci);
+
+ atomic_notifier_chain_unregister(&x86_mce_decoder_chain, &i7_mce_dec);
/* Disable EDAC polling */
i7core_pci_ctl_release(pvt);
@@ -1979,6 +2306,10 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
/* Set the function pointer to an actual operation function */
mci->edac_check = i7core_check_error;
+ /* Enable scrubrate setting */
+ if (pvt->enable_scrub)
+ enable_sdram_scrub_setting(mci);
+
/* add this new MC control structure to EDAC's list of MCs */
if (unlikely(edac_mc_add_mc(mci))) {
debugf0("MC: " __FILE__
@@ -2002,21 +2333,13 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
/* allocating generic PCI control info */
i7core_pci_ctl_create(pvt);
- /* Registers on edac_mce in order to receive memory errors */
- pvt->edac_mce.priv = mci;
- pvt->edac_mce.check_error = i7core_mce_check_error;
- rc = edac_mce_register(&pvt->edac_mce);
- if (unlikely(rc < 0)) {
- debugf0("MC: " __FILE__
- ": %s(): failed edac_mce_register()\n", __func__);
- goto fail1;
- }
+ /* DCLK for scrub rate setting */
+ pvt->dclk_freq = get_dclk_freq();
+
+ atomic_notifier_chain_register(&x86_mce_decoder_chain, &i7_mce_dec);
return 0;
-fail1:
- i7core_pci_ctl_release(pvt);
- edac_mc_del_mc(mci->dev);
fail0:
kfree(mci->ctl_name);
edac_mc_free(mci);
@@ -2035,7 +2358,7 @@ fail0:
static int __devinit i7core_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
- int rc;
+ int rc, count = 0;
struct i7core_dev *i7core_dev;
/* get the pci devices we want to reserve for our use */
@@ -2055,12 +2378,28 @@ static int __devinit i7core_probe(struct pci_dev *pdev,
goto fail0;
list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
+ count++;
rc = i7core_register_mci(i7core_dev);
if (unlikely(rc < 0))
goto fail1;
}
- i7core_printk(KERN_INFO, "Driver loaded.\n");
+ /*
+ * Nehalem-EX uses a different memory controller. However, as the
+ * memory controller is not visible on some Nehalem/Nehalem-EP, we
+ * need to indirectly probe via a X58 PCI device. The same devices
+ * are found on (some) Nehalem-EX. So, on those machines, the
+ * probe routine needs to return -ENODEV, as the actual Memory
+ * Controller registers won't be detected.
+ */
+ if (!count) {
+ rc = -ENODEV;
+ goto fail1;
+ }
+
+ i7core_printk(KERN_INFO,
+ "Driver loaded, %d memory controller(s) found.\n",
+ count);
mutex_unlock(&i7core_edac_lock);
return 0;
diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c
index 795cfbc0bf50..d0864d9c38ad 100644
--- a/drivers/edac/mce_amd.c
+++ b/drivers/edac/mce_amd.c
@@ -9,7 +9,7 @@ static u8 xec_mask = 0xf;
static u8 nb_err_cpumask = 0xf;
static bool report_gart_errors;
-static void (*nb_bus_decoder)(int node_id, struct mce *m, u32 nbcfg);
+static void (*nb_bus_decoder)(int node_id, struct mce *m);
void amd_report_gart_errors(bool v)
{
@@ -17,13 +17,13 @@ void amd_report_gart_errors(bool v)
}
EXPORT_SYMBOL_GPL(amd_report_gart_errors);
-void amd_register_ecc_decoder(void (*f)(int, struct mce *, u32))
+void amd_register_ecc_decoder(void (*f)(int, struct mce *))
{
nb_bus_decoder = f;
}
EXPORT_SYMBOL_GPL(amd_register_ecc_decoder);
-void amd_unregister_ecc_decoder(void (*f)(int, struct mce *, u32))
+void amd_unregister_ecc_decoder(void (*f)(int, struct mce *))
{
if (nb_bus_decoder) {
WARN_ON(nb_bus_decoder != f);
@@ -592,31 +592,14 @@ static bool nb_noop_mce(u16 ec, u8 xec)
return false;
}
-void amd_decode_nb_mce(int node_id, struct mce *m, u32 nbcfg)
+void amd_decode_nb_mce(struct mce *m)
{
struct cpuinfo_x86 *c = &boot_cpu_data;
- u16 ec = EC(m->status);
- u8 xec = XEC(m->status, 0x1f);
- u32 nbsh = (u32)(m->status >> 32);
- int core = -1;
-
- pr_emerg(HW_ERR "Northbridge Error (node %d", node_id);
-
- /* F10h, revD can disable ErrCpu[3:0] through ErrCpuVal */
- if (c->x86 == 0x10 && c->x86_model > 7) {
- if (nbsh & NBSH_ERR_CPU_VAL)
- core = nbsh & nb_err_cpumask;
- } else {
- u8 assoc_cpus = nbsh & nb_err_cpumask;
-
- if (assoc_cpus > 0)
- core = fls(assoc_cpus) - 1;
- }
+ int node_id = amd_get_nb_id(m->extcpu);
+ u16 ec = EC(m->status);
+ u8 xec = XEC(m->status, 0x1f);
- if (core >= 0)
- pr_cont(", core %d): ", core);
- else
- pr_cont("): ");
+ pr_emerg(HW_ERR "Northbridge Error (node %d): ", node_id);
switch (xec) {
case 0x2:
@@ -648,7 +631,7 @@ void amd_decode_nb_mce(int node_id, struct mce *m, u32 nbcfg)
if (c->x86 == 0xf || c->x86 == 0x10 || c->x86 == 0x15)
if ((xec == 0x8 || xec == 0x0) && nb_bus_decoder)
- nb_bus_decoder(node_id, m, nbcfg);
+ nb_bus_decoder(node_id, m);
return;
@@ -764,13 +747,13 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
{
struct mce *m = (struct mce *)data;
struct cpuinfo_x86 *c = &boot_cpu_data;
- int node, ecc;
+ int ecc;
if (amd_filter_mce(m))
return NOTIFY_STOP;
- pr_emerg(HW_ERR "MC%d_STATUS[%s|%s|%s|%s|%s",
- m->bank,
+ pr_emerg(HW_ERR "CPU:%d\tMC%d_STATUS[%s|%s|%s|%s|%s",
+ m->extcpu, m->bank,
((m->status & MCI_STATUS_OVER) ? "Over" : "-"),
((m->status & MCI_STATUS_UC) ? "UE" : "CE"),
((m->status & MCI_STATUS_MISCV) ? "MiscV" : "-"),
@@ -789,6 +772,8 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
pr_cont("]: 0x%016llx\n", m->status);
+ if (m->status & MCI_STATUS_ADDRV)
+ pr_emerg(HW_ERR "\tMC%d_ADDR: 0x%016llx\n", m->bank, m->addr);
switch (m->bank) {
case 0:
@@ -811,8 +796,7 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
break;
case 4:
- node = amd_get_nb_id(m->extcpu);
- amd_decode_nb_mce(node, m, 0);
+ amd_decode_nb_mce(m);
break;
case 5:
diff --git a/drivers/edac/mce_amd.h b/drivers/edac/mce_amd.h
index 795a3206acf5..0106747e240c 100644
--- a/drivers/edac/mce_amd.h
+++ b/drivers/edac/mce_amd.h
@@ -86,9 +86,9 @@ struct amd_decoder_ops {
};
void amd_report_gart_errors(bool);
-void amd_register_ecc_decoder(void (*f)(int, struct mce *, u32));
-void amd_unregister_ecc_decoder(void (*f)(int, struct mce *, u32));
-void amd_decode_nb_mce(int, struct mce *, u32);
+void amd_register_ecc_decoder(void (*f)(int, struct mce *));
+void amd_unregister_ecc_decoder(void (*f)(int, struct mce *));
+void amd_decode_nb_mce(struct mce *);
int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data);
#endif /* _EDAC_MCE_AMD_H */
diff --git a/drivers/edac/mce_amd_inj.c b/drivers/edac/mce_amd_inj.c
index a4987e03f59e..73c3e26a0bce 100644
--- a/drivers/edac/mce_amd_inj.c
+++ b/drivers/edac/mce_amd_inj.c
@@ -13,6 +13,7 @@
#include <linux/kobject.h>
#include <linux/sysdev.h>
#include <linux/edac.h>
+#include <linux/module.h>
#include <asm/mce.h>
#include "mce_amd.h"
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index 38ab8e2cd7f4..73464a62adf7 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -538,15 +538,15 @@ static int __devinit mpc85xx_l2_err_probe(struct platform_device *op)
/* we only need the error registers */
r.start += 0xe00;
- if (!devm_request_mem_region(&op->dev, r.start,
- r.end - r.start + 1, pdata->name)) {
+ if (!devm_request_mem_region(&op->dev, r.start, resource_size(&r),
+ pdata->name)) {
printk(KERN_ERR "%s: Error while requesting mem region\n",
__func__);
res = -EBUSY;
goto err;
}
- pdata->l2_vbase = devm_ioremap(&op->dev, r.start, r.end - r.start + 1);
+ pdata->l2_vbase = devm_ioremap(&op->dev, r.start, resource_size(&r));
if (!pdata->l2_vbase) {
printk(KERN_ERR "%s: Unable to setup L2 err regs\n", __func__);
res = -ENOMEM;
@@ -854,11 +854,11 @@ static void mpc85xx_mc_check(struct mem_ctl_info *mci)
mpc85xx_mc_printk(mci, KERN_ERR, "PFN out of range!\n");
if (err_detect & DDR_EDE_SBE)
- edac_mc_handle_ce(mci, pfn, err_addr & PAGE_MASK,
+ edac_mc_handle_ce(mci, pfn, err_addr & ~PAGE_MASK,
syndrome, row_index, 0, mci->ctl_name);
if (err_detect & DDR_EDE_MBE)
- edac_mc_handle_ue(mci, pfn, err_addr & PAGE_MASK,
+ edac_mc_handle_ue(mci, pfn, err_addr & ~PAGE_MASK,
row_index, mci->ctl_name);
out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect);
@@ -987,15 +987,15 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
goto err;
}
- if (!devm_request_mem_region(&op->dev, r.start,
- r.end - r.start + 1, pdata->name)) {
+ if (!devm_request_mem_region(&op->dev, r.start, resource_size(&r),
+ pdata->name)) {
printk(KERN_ERR "%s: Error while requesting mem region\n",
__func__);
res = -EBUSY;
goto err;
}
- pdata->mc_vbase = devm_ioremap(&op->dev, r.start, r.end - r.start + 1);
+ pdata->mc_vbase = devm_ioremap(&op->dev, r.start, resource_size(&r));
if (!pdata->mc_vbase) {
printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__);
res = -ENOMEM;
@@ -1128,7 +1128,7 @@ static struct of_device_id mpc85xx_mc_err_of_match[] = {
{ .compatible = "fsl,p1020-memory-controller", },
{ .compatible = "fsl,p1021-memory-controller", },
{ .compatible = "fsl,p2020-memory-controller", },
- { .compatible = "fsl,p4080-memory-controller", },
+ { .compatible = "fsl,qoriq-memory-controller", },
{},
};
MODULE_DEVICE_TABLE(of, mpc85xx_mc_err_of_match);
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index 0de7d8770891..38400963e245 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -205,7 +205,7 @@ static struct platform_driver ppc4xx_edac_driver = {
.remove = ppc4xx_edac_remove,
.driver = {
.owner = THIS_MODULE,
- .name = PPC4XX_EDAC_MODULE_NAME
+ .name = PPC4XX_EDAC_MODULE_NAME,
.of_match_table = ppc4xx_edac_match,
},
};
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
new file mode 100644
index 000000000000..7a402bfbee7d
--- /dev/null
+++ b/drivers/edac/sb_edac.c
@@ -0,0 +1,1893 @@
+/* Intel Sandy Bridge -EN/-EP/-EX Memory Controller kernel module
+ *
+ * This driver supports the memory controllers found on the Intel
+ * processor family Sandy Bridge.
+ *
+ * This file may be distributed under the terms of the
+ * GNU General Public License version 2 only.
+ *
+ * Copyright (c) 2011 by:
+ * Mauro Carvalho Chehab <mchehab@redhat.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/edac.h>
+#include <linux/mmzone.h>
+#include <linux/smp.h>
+#include <linux/bitmap.h>
+#include <asm/processor.h>
+#include <asm/mce.h>
+
+#include "edac_core.h"
+
+/* Static vars */
+static LIST_HEAD(sbridge_edac_list);
+static DEFINE_MUTEX(sbridge_edac_lock);
+static int probed;
+
+/*
+ * Alter this version for the module when modifications are made
+ */
+#define SBRIDGE_REVISION " Ver: 1.0.0 "
+#define EDAC_MOD_STR "sbridge_edac"
+
+/*
+ * Debug macros
+ */
+#define sbridge_printk(level, fmt, arg...) \
+ edac_printk(level, "sbridge", fmt, ##arg)
+
+#define sbridge_mc_printk(mci, level, fmt, arg...) \
+ edac_mc_chipset_printk(mci, level, "sbridge", fmt, ##arg)
+
+/*
+ * Get a bit field at register value <v>, from bit <lo> to bit <hi>
+ */
+#define GET_BITFIELD(v, lo, hi) \
+ (((v) & ((1ULL << ((hi) - (lo) + 1)) - 1) << (lo)) >> (lo))
+
+/*
+ * sbridge Memory Controller Registers
+ */
+
+/*
+ * FIXME: For now, let's order by device function, as it makes
+ * easier for driver's development proccess. This table should be
+ * moved to pci_id.h when submitted upstream
+ */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_SAD0 0x3cf4 /* 12.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_SAD1 0x3cf6 /* 12.7 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_BR 0x3cf5 /* 13.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0 0x3ca0 /* 14.0 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA 0x3ca8 /* 15.0 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_RAS 0x3c71 /* 15.1 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0 0x3caa /* 15.2 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD1 0x3cab /* 15.3 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD2 0x3cac /* 15.4 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD3 0x3cad /* 15.5 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO 0x3cb8 /* 17.0 */
+
+ /*
+ * Currently, unused, but will be needed in the future
+ * implementations, as they hold the error counters
+ */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR0 0x3c72 /* 16.2 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR1 0x3c73 /* 16.3 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR2 0x3c76 /* 16.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR3 0x3c77 /* 16.7 */
+
+/* Devices 12 Function 6, Offsets 0x80 to 0xcc */
+static const u32 dram_rule[] = {
+ 0x80, 0x88, 0x90, 0x98, 0xa0,
+ 0xa8, 0xb0, 0xb8, 0xc0, 0xc8,
+};
+#define MAX_SAD ARRAY_SIZE(dram_rule)
+
+#define SAD_LIMIT(reg) ((GET_BITFIELD(reg, 6, 25) << 26) | 0x3ffffff)
+#define DRAM_ATTR(reg) GET_BITFIELD(reg, 2, 3)
+#define INTERLEAVE_MODE(reg) GET_BITFIELD(reg, 1, 1)
+#define DRAM_RULE_ENABLE(reg) GET_BITFIELD(reg, 0, 0)
+
+static char *get_dram_attr(u32 reg)
+{
+ switch(DRAM_ATTR(reg)) {
+ case 0:
+ return "DRAM";
+ case 1:
+ return "MMCFG";
+ case 2:
+ return "NXM";
+ default:
+ return "unknown";
+ }
+}
+
+static const u32 interleave_list[] = {
+ 0x84, 0x8c, 0x94, 0x9c, 0xa4,
+ 0xac, 0xb4, 0xbc, 0xc4, 0xcc,
+};
+#define MAX_INTERLEAVE ARRAY_SIZE(interleave_list)
+
+#define SAD_PKG0(reg) GET_BITFIELD(reg, 0, 2)
+#define SAD_PKG1(reg) GET_BITFIELD(reg, 3, 5)
+#define SAD_PKG2(reg) GET_BITFIELD(reg, 8, 10)
+#define SAD_PKG3(reg) GET_BITFIELD(reg, 11, 13)
+#define SAD_PKG4(reg) GET_BITFIELD(reg, 16, 18)
+#define SAD_PKG5(reg) GET_BITFIELD(reg, 19, 21)
+#define SAD_PKG6(reg) GET_BITFIELD(reg, 24, 26)
+#define SAD_PKG7(reg) GET_BITFIELD(reg, 27, 29)
+
+static inline int sad_pkg(u32 reg, int interleave)
+{
+ switch (interleave) {
+ case 0:
+ return SAD_PKG0(reg);
+ case 1:
+ return SAD_PKG1(reg);
+ case 2:
+ return SAD_PKG2(reg);
+ case 3:
+ return SAD_PKG3(reg);
+ case 4:
+ return SAD_PKG4(reg);
+ case 5:
+ return SAD_PKG5(reg);
+ case 6:
+ return SAD_PKG6(reg);
+ case 7:
+ return SAD_PKG7(reg);
+ default:
+ return -EINVAL;
+ }
+}
+
+/* Devices 12 Function 7 */
+
+#define TOLM 0x80
+#define TOHM 0x84
+
+#define GET_TOLM(reg) ((GET_BITFIELD(reg, 0, 3) << 28) | 0x3ffffff)
+#define GET_TOHM(reg) ((GET_BITFIELD(reg, 0, 20) << 25) | 0x3ffffff)
+
+/* Device 13 Function 6 */
+
+#define SAD_TARGET 0xf0
+
+#define SOURCE_ID(reg) GET_BITFIELD(reg, 9, 11)
+
+#define SAD_CONTROL 0xf4
+
+#define NODE_ID(reg) GET_BITFIELD(reg, 0, 2)
+
+/* Device 14 function 0 */
+
+static const u32 tad_dram_rule[] = {
+ 0x40, 0x44, 0x48, 0x4c,
+ 0x50, 0x54, 0x58, 0x5c,
+ 0x60, 0x64, 0x68, 0x6c,
+};
+#define MAX_TAD ARRAY_SIZE(tad_dram_rule)
+
+#define TAD_LIMIT(reg) ((GET_BITFIELD(reg, 12, 31) << 26) | 0x3ffffff)
+#define TAD_SOCK(reg) GET_BITFIELD(reg, 10, 11)
+#define TAD_CH(reg) GET_BITFIELD(reg, 8, 9)
+#define TAD_TGT3(reg) GET_BITFIELD(reg, 6, 7)
+#define TAD_TGT2(reg) GET_BITFIELD(reg, 4, 5)
+#define TAD_TGT1(reg) GET_BITFIELD(reg, 2, 3)
+#define TAD_TGT0(reg) GET_BITFIELD(reg, 0, 1)
+
+/* Device 15, function 0 */
+
+#define MCMTR 0x7c
+
+#define IS_ECC_ENABLED(mcmtr) GET_BITFIELD(mcmtr, 2, 2)
+#define IS_LOCKSTEP_ENABLED(mcmtr) GET_BITFIELD(mcmtr, 1, 1)
+#define IS_CLOSE_PG(mcmtr) GET_BITFIELD(mcmtr, 0, 0)
+
+/* Device 15, function 1 */
+
+#define RASENABLES 0xac
+#define IS_MIRROR_ENABLED(reg) GET_BITFIELD(reg, 0, 0)
+
+/* Device 15, functions 2-5 */
+
+static const int mtr_regs[] = {
+ 0x80, 0x84, 0x88,
+};
+
+#define RANK_DISABLE(mtr) GET_BITFIELD(mtr, 16, 19)
+#define IS_DIMM_PRESENT(mtr) GET_BITFIELD(mtr, 14, 14)
+#define RANK_CNT_BITS(mtr) GET_BITFIELD(mtr, 12, 13)
+#define RANK_WIDTH_BITS(mtr) GET_BITFIELD(mtr, 2, 4)
+#define COL_WIDTH_BITS(mtr) GET_BITFIELD(mtr, 0, 1)
+
+static const u32 tad_ch_nilv_offset[] = {
+ 0x90, 0x94, 0x98, 0x9c,
+ 0xa0, 0xa4, 0xa8, 0xac,
+ 0xb0, 0xb4, 0xb8, 0xbc,
+};
+#define CHN_IDX_OFFSET(reg) GET_BITFIELD(reg, 28, 29)
+#define TAD_OFFSET(reg) (GET_BITFIELD(reg, 6, 25) << 26)
+
+static const u32 rir_way_limit[] = {
+ 0x108, 0x10c, 0x110, 0x114, 0x118,
+};
+#define MAX_RIR_RANGES ARRAY_SIZE(rir_way_limit)
+
+#define IS_RIR_VALID(reg) GET_BITFIELD(reg, 31, 31)
+#define RIR_WAY(reg) GET_BITFIELD(reg, 28, 29)
+#define RIR_LIMIT(reg) ((GET_BITFIELD(reg, 1, 10) << 29)| 0x1fffffff)
+
+#define MAX_RIR_WAY 8
+
+static const u32 rir_offset[MAX_RIR_RANGES][MAX_RIR_WAY] = {
+ { 0x120, 0x124, 0x128, 0x12c, 0x130, 0x134, 0x138, 0x13c },
+ { 0x140, 0x144, 0x148, 0x14c, 0x150, 0x154, 0x158, 0x15c },
+ { 0x160, 0x164, 0x168, 0x16c, 0x170, 0x174, 0x178, 0x17c },
+ { 0x180, 0x184, 0x188, 0x18c, 0x190, 0x194, 0x198, 0x19c },
+ { 0x1a0, 0x1a4, 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc },
+};
+
+#define RIR_RNK_TGT(reg) GET_BITFIELD(reg, 16, 19)
+#define RIR_OFFSET(reg) GET_BITFIELD(reg, 2, 14)
+
+/* Device 16, functions 2-7 */
+
+/*
+ * FIXME: Implement the error count reads directly
+ */
+
+static const u32 correrrcnt[] = {
+ 0x104, 0x108, 0x10c, 0x110,
+};
+
+#define RANK_ODD_OV(reg) GET_BITFIELD(reg, 31, 31)
+#define RANK_ODD_ERR_CNT(reg) GET_BITFIELD(reg, 16, 30)
+#define RANK_EVEN_OV(reg) GET_BITFIELD(reg, 15, 15)
+#define RANK_EVEN_ERR_CNT(reg) GET_BITFIELD(reg, 0, 14)
+
+static const u32 correrrthrsld[] = {
+ 0x11c, 0x120, 0x124, 0x128,
+};
+
+#define RANK_ODD_ERR_THRSLD(reg) GET_BITFIELD(reg, 16, 30)
+#define RANK_EVEN_ERR_THRSLD(reg) GET_BITFIELD(reg, 0, 14)
+
+
+/* Device 17, function 0 */
+
+#define RANK_CFG_A 0x0328
+
+#define IS_RDIMM_ENABLED(reg) GET_BITFIELD(reg, 11, 11)
+
+/*
+ * sbridge structs
+ */
+
+#define NUM_CHANNELS 4
+#define MAX_DIMMS 3 /* Max DIMMS per channel */
+
+struct sbridge_info {
+ u32 mcmtr;
+};
+
+struct sbridge_channel {
+ u32 ranks;
+ u32 dimms;
+};
+
+struct pci_id_descr {
+ int dev;
+ int func;
+ int dev_id;
+ int optional;
+};
+
+struct pci_id_table {
+ const struct pci_id_descr *descr;
+ int n_devs;
+};
+
+struct sbridge_dev {
+ struct list_head list;
+ u8 bus, mc;
+ u8 node_id, source_id;
+ struct pci_dev **pdev;
+ int n_devs;
+ struct mem_ctl_info *mci;
+};
+
+struct sbridge_pvt {
+ struct pci_dev *pci_ta, *pci_ddrio, *pci_ras;
+ struct pci_dev *pci_sad0, *pci_sad1, *pci_ha0;
+ struct pci_dev *pci_br;
+ struct pci_dev *pci_tad[NUM_CHANNELS];
+
+ struct sbridge_dev *sbridge_dev;
+
+ struct sbridge_info info;
+ struct sbridge_channel channel[NUM_CHANNELS];
+
+ int csrow_map[NUM_CHANNELS][MAX_DIMMS];
+
+ /* Memory type detection */
+ bool is_mirrored, is_lockstep, is_close_pg;
+
+ /* Fifo double buffers */
+ struct mce mce_entry[MCE_LOG_LEN];
+ struct mce mce_outentry[MCE_LOG_LEN];
+
+ /* Fifo in/out counters */
+ unsigned mce_in, mce_out;
+
+ /* Count indicator to show errors not got */
+ unsigned mce_overrun;
+
+ /* Memory description */
+ u64 tolm, tohm;
+};
+
+#define PCI_DESCR(device, function, device_id) \
+ .dev = (device), \
+ .func = (function), \
+ .dev_id = (device_id)
+
+static const struct pci_id_descr pci_dev_descr_sbridge[] = {
+ /* Processor Home Agent */
+ { PCI_DESCR(14, 0, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0) },
+
+ /* Memory controller */
+ { PCI_DESCR(15, 0, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA) },
+ { PCI_DESCR(15, 1, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_RAS) },
+ { PCI_DESCR(15, 2, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0) },
+ { PCI_DESCR(15, 3, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD1) },
+ { PCI_DESCR(15, 4, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD2) },
+ { PCI_DESCR(15, 5, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD3) },
+ { PCI_DESCR(17, 0, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO) },
+
+ /* System Address Decoder */
+ { PCI_DESCR(12, 6, PCI_DEVICE_ID_INTEL_SBRIDGE_SAD0) },
+ { PCI_DESCR(12, 7, PCI_DEVICE_ID_INTEL_SBRIDGE_SAD1) },
+
+ /* Broadcast Registers */
+ { PCI_DESCR(13, 6, PCI_DEVICE_ID_INTEL_SBRIDGE_BR) },
+};
+
+#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
+static const struct pci_id_table pci_dev_descr_sbridge_table[] = {
+ PCI_ID_TABLE_ENTRY(pci_dev_descr_sbridge),
+ {0,} /* 0 terminated list. */
+};
+
+/*
+ * pci_device_id table for which devices we are looking for
+ */
+static const struct pci_device_id sbridge_pci_tbl[] __devinitdata = {
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA)},
+ {0,} /* 0 terminated list. */
+};
+
+
+/****************************************************************************
+ Anciliary status routines
+ ****************************************************************************/
+
+static inline int numrank(u32 mtr)
+{
+ int ranks = (1 << RANK_CNT_BITS(mtr));
+
+ if (ranks > 4) {
+ debugf0("Invalid number of ranks: %d (max = 4) raw value = %x (%04x)",
+ ranks, (unsigned int)RANK_CNT_BITS(mtr), mtr);
+ return -EINVAL;
+ }
+
+ return ranks;
+}
+
+static inline int numrow(u32 mtr)
+{
+ int rows = (RANK_WIDTH_BITS(mtr) + 12);
+
+ if (rows < 13 || rows > 18) {
+ debugf0("Invalid number of rows: %d (should be between 14 and 17) raw value = %x (%04x)",
+ rows, (unsigned int)RANK_WIDTH_BITS(mtr), mtr);
+ return -EINVAL;
+ }
+
+ return 1 << rows;
+}
+
+static inline int numcol(u32 mtr)
+{
+ int cols = (COL_WIDTH_BITS(mtr) + 10);
+
+ if (cols > 12) {
+ debugf0("Invalid number of cols: %d (max = 4) raw value = %x (%04x)",
+ cols, (unsigned int)COL_WIDTH_BITS(mtr), mtr);
+ return -EINVAL;
+ }
+
+ return 1 << cols;
+}
+
+static struct sbridge_dev *get_sbridge_dev(u8 bus)
+{
+ struct sbridge_dev *sbridge_dev;
+
+ list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) {
+ if (sbridge_dev->bus == bus)
+ return sbridge_dev;
+ }
+
+ return NULL;
+}
+
+static struct sbridge_dev *alloc_sbridge_dev(u8 bus,
+ const struct pci_id_table *table)
+{
+ struct sbridge_dev *sbridge_dev;
+
+ sbridge_dev = kzalloc(sizeof(*sbridge_dev), GFP_KERNEL);
+ if (!sbridge_dev)
+ return NULL;
+
+ sbridge_dev->pdev = kzalloc(sizeof(*sbridge_dev->pdev) * table->n_devs,
+ GFP_KERNEL);
+ if (!sbridge_dev->pdev) {
+ kfree(sbridge_dev);
+ return NULL;
+ }
+
+ sbridge_dev->bus = bus;
+ sbridge_dev->n_devs = table->n_devs;
+ list_add_tail(&sbridge_dev->list, &sbridge_edac_list);
+
+ return sbridge_dev;
+}
+
+static void free_sbridge_dev(struct sbridge_dev *sbridge_dev)
+{
+ list_del(&sbridge_dev->list);
+ kfree(sbridge_dev->pdev);
+ kfree(sbridge_dev);
+}
+
+/****************************************************************************
+ Memory check routines
+ ****************************************************************************/
+static struct pci_dev *get_pdev_slot_func(u8 bus, unsigned slot,
+ unsigned func)
+{
+ struct sbridge_dev *sbridge_dev = get_sbridge_dev(bus);
+ int i;
+
+ if (!sbridge_dev)
+ return NULL;
+
+ for (i = 0; i < sbridge_dev->n_devs; i++) {
+ if (!sbridge_dev->pdev[i])
+ continue;
+
+ if (PCI_SLOT(sbridge_dev->pdev[i]->devfn) == slot &&
+ PCI_FUNC(sbridge_dev->pdev[i]->devfn) == func) {
+ debugf1("Associated %02x.%02x.%d with %p\n",
+ bus, slot, func, sbridge_dev->pdev[i]);
+ return sbridge_dev->pdev[i];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * sbridge_get_active_channels() - gets the number of channels and csrows
+ * bus: Device bus
+ * @channels: Number of channels that will be returned
+ * @csrows: Number of csrows found
+ *
+ * Since EDAC core needs to know in advance the number of available channels
+ * and csrows, in order to allocate memory for csrows/channels, it is needed
+ * to run two similar steps. At the first step, implemented on this function,
+ * it checks the number of csrows/channels present at one socket, identified
+ * by the associated PCI bus.
+ * this is used in order to properly allocate the size of mci components.
+ * Note: one csrow is one dimm.
+ */
+static int sbridge_get_active_channels(const u8 bus, unsigned *channels,
+ unsigned *csrows)
+{
+ struct pci_dev *pdev = NULL;
+ int i, j;
+ u32 mcmtr;
+
+ *channels = 0;
+ *csrows = 0;
+
+ pdev = get_pdev_slot_func(bus, 15, 0);
+ if (!pdev) {
+ sbridge_printk(KERN_ERR, "Couldn't find PCI device "
+ "%2x.%02d.%d!!!\n",
+ bus, 15, 0);
+ return -ENODEV;
+ }
+
+ pci_read_config_dword(pdev, MCMTR, &mcmtr);
+ if (!IS_ECC_ENABLED(mcmtr)) {
+ sbridge_printk(KERN_ERR, "ECC is disabled. Aborting\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ u32 mtr;
+
+ /* Device 15 functions 2 - 5 */
+ pdev = get_pdev_slot_func(bus, 15, 2 + i);
+ if (!pdev) {
+ sbridge_printk(KERN_ERR, "Couldn't find PCI device "
+ "%2x.%02d.%d!!!\n",
+ bus, 15, 2 + i);
+ return -ENODEV;
+ }
+ (*channels)++;
+
+ for (j = 0; j < ARRAY_SIZE(mtr_regs); j++) {
+ pci_read_config_dword(pdev, mtr_regs[j], &mtr);
+ debugf1("Bus#%02x channel #%d MTR%d = %x\n", bus, i, j, mtr);
+ if (IS_DIMM_PRESENT(mtr))
+ (*csrows)++;
+ }
+ }
+
+ debugf0("Number of active channels: %d, number of active dimms: %d\n",
+ *channels, *csrows);
+
+ return 0;
+}
+
+static int get_dimm_config(const struct mem_ctl_info *mci)
+{
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ struct csrow_info *csr;
+ int i, j, banks, ranks, rows, cols, size, npages;
+ int csrow = 0;
+ unsigned long last_page = 0;
+ u32 reg;
+ enum edac_type mode;
+ enum mem_type mtype;
+
+ pci_read_config_dword(pvt->pci_br, SAD_TARGET, &reg);
+ pvt->sbridge_dev->source_id = SOURCE_ID(reg);
+
+ pci_read_config_dword(pvt->pci_br, SAD_CONTROL, &reg);
+ pvt->sbridge_dev->node_id = NODE_ID(reg);
+ debugf0("mc#%d: Node ID: %d, source ID: %d\n",
+ pvt->sbridge_dev->mc,
+ pvt->sbridge_dev->node_id,
+ pvt->sbridge_dev->source_id);
+
+ pci_read_config_dword(pvt->pci_ras, RASENABLES, &reg);
+ if (IS_MIRROR_ENABLED(reg)) {
+ debugf0("Memory mirror is enabled\n");
+ pvt->is_mirrored = true;
+ } else {
+ debugf0("Memory mirror is disabled\n");
+ pvt->is_mirrored = false;
+ }
+
+ pci_read_config_dword(pvt->pci_ta, MCMTR, &pvt->info.mcmtr);
+ if (IS_LOCKSTEP_ENABLED(pvt->info.mcmtr)) {
+ debugf0("Lockstep is enabled\n");
+ mode = EDAC_S8ECD8ED;
+ pvt->is_lockstep = true;
+ } else {
+ debugf0("Lockstep is disabled\n");
+ mode = EDAC_S4ECD4ED;
+ pvt->is_lockstep = false;
+ }
+ if (IS_CLOSE_PG(pvt->info.mcmtr)) {
+ debugf0("address map is on closed page mode\n");
+ pvt->is_close_pg = true;
+ } else {
+ debugf0("address map is on open page mode\n");
+ pvt->is_close_pg = false;
+ }
+
+ pci_read_config_dword(pvt->pci_ta, RANK_CFG_A, &reg);
+ if (IS_RDIMM_ENABLED(reg)) {
+ /* FIXME: Can also be LRDIMM */
+ debugf0("Memory is registered\n");
+ mtype = MEM_RDDR3;
+ } else {
+ debugf0("Memory is unregistered\n");
+ mtype = MEM_DDR3;
+ }
+
+ /* On all supported DDR3 DIMM types, there are 8 banks available */
+ banks = 8;
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ u32 mtr;
+
+ for (j = 0; j < ARRAY_SIZE(mtr_regs); j++) {
+ pci_read_config_dword(pvt->pci_tad[i],
+ mtr_regs[j], &mtr);
+ debugf4("Channel #%d MTR%d = %x\n", i, j, mtr);
+ if (IS_DIMM_PRESENT(mtr)) {
+ pvt->channel[i].dimms++;
+
+ ranks = numrank(mtr);
+ rows = numrow(mtr);
+ cols = numcol(mtr);
+
+ /* DDR3 has 8 I/O banks */
+ size = (rows * cols * banks * ranks) >> (20 - 3);
+ npages = MiB_TO_PAGES(size);
+
+ debugf0("mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
+ pvt->sbridge_dev->mc, i, j,
+ size, npages,
+ banks, ranks, rows, cols);
+ csr = &mci->csrows[csrow];
+
+ csr->first_page = last_page;
+ csr->last_page = last_page + npages - 1;
+ csr->page_mask = 0UL; /* Unused */
+ csr->nr_pages = npages;
+ csr->grain = 32;
+ csr->csrow_idx = csrow;
+ csr->dtype = (banks == 8) ? DEV_X8 : DEV_X4;
+ csr->ce_count = 0;
+ csr->ue_count = 0;
+ csr->mtype = mtype;
+ csr->edac_mode = mode;
+ csr->nr_channels = 1;
+ csr->channels[0].chan_idx = i;
+ csr->channels[0].ce_count = 0;
+ pvt->csrow_map[i][j] = csrow;
+ snprintf(csr->channels[0].label,
+ sizeof(csr->channels[0].label),
+ "CPU_SrcID#%u_Channel#%u_DIMM#%u",
+ pvt->sbridge_dev->source_id, i, j);
+ last_page += npages;
+ csrow++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void get_memory_layout(const struct mem_ctl_info *mci)
+{
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ int i, j, k, n_sads, n_tads, sad_interl;
+ u32 reg;
+ u64 limit, prv = 0;
+ u64 tmp_mb;
+ u32 rir_way;
+
+ /*
+ * Step 1) Get TOLM/TOHM ranges
+ */
+
+ /* Address range is 32:28 */
+ pci_read_config_dword(pvt->pci_sad1, TOLM,
+ &reg);
+ pvt->tolm = GET_TOLM(reg);
+ tmp_mb = (1 + pvt->tolm) >> 20;
+
+ debugf0("TOLM: %Lu.%03Lu GB (0x%016Lx)\n",
+ tmp_mb / 1000, tmp_mb % 1000, (u64)pvt->tolm);
+
+ /* Address range is already 45:25 */
+ pci_read_config_dword(pvt->pci_sad1, TOHM,
+ &reg);
+ pvt->tohm = GET_TOHM(reg);
+ tmp_mb = (1 + pvt->tohm) >> 20;
+
+ debugf0("TOHM: %Lu.%03Lu GB (0x%016Lx)",
+ tmp_mb / 1000, tmp_mb % 1000, (u64)pvt->tohm);
+
+ /*
+ * Step 2) Get SAD range and SAD Interleave list
+ * TAD registers contain the interleave wayness. However, it
+ * seems simpler to just discover it indirectly, with the
+ * algorithm bellow.
+ */
+ prv = 0;
+ for (n_sads = 0; n_sads < MAX_SAD; n_sads++) {
+ /* SAD_LIMIT Address range is 45:26 */
+ pci_read_config_dword(pvt->pci_sad0, dram_rule[n_sads],
+ &reg);
+ limit = SAD_LIMIT(reg);
+
+ if (!DRAM_RULE_ENABLE(reg))
+ continue;
+
+ if (limit <= prv)
+ break;
+
+ tmp_mb = (limit + 1) >> 20;
+ debugf0("SAD#%d %s up to %Lu.%03Lu GB (0x%016Lx) %s reg=0x%08x\n",
+ n_sads,
+ get_dram_attr(reg),
+ tmp_mb / 1000, tmp_mb % 1000,
+ ((u64)tmp_mb) << 20L,
+ INTERLEAVE_MODE(reg) ? "Interleave: 8:6" : "Interleave: [8:6]XOR[18:16]",
+ reg);
+ prv = limit;
+
+ pci_read_config_dword(pvt->pci_sad0, interleave_list[n_sads],
+ &reg);
+ sad_interl = sad_pkg(reg, 0);
+ for (j = 0; j < 8; j++) {
+ if (j > 0 && sad_interl == sad_pkg(reg, j))
+ break;
+
+ debugf0("SAD#%d, interleave #%d: %d\n",
+ n_sads, j, sad_pkg(reg, j));
+ }
+ }
+
+ /*
+ * Step 3) Get TAD range
+ */
+ prv = 0;
+ for (n_tads = 0; n_tads < MAX_TAD; n_tads++) {
+ pci_read_config_dword(pvt->pci_ha0, tad_dram_rule[n_tads],
+ &reg);
+ limit = TAD_LIMIT(reg);
+ if (limit <= prv)
+ break;
+ tmp_mb = (limit + 1) >> 20;
+
+ debugf0("TAD#%d: up to %Lu.%03Lu GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
+ n_tads, tmp_mb / 1000, tmp_mb % 1000,
+ ((u64)tmp_mb) << 20L,
+ (u32)TAD_SOCK(reg),
+ (u32)TAD_CH(reg),
+ (u32)TAD_TGT0(reg),
+ (u32)TAD_TGT1(reg),
+ (u32)TAD_TGT2(reg),
+ (u32)TAD_TGT3(reg),
+ reg);
+ prv = tmp_mb;
+ }
+
+ /*
+ * Step 4) Get TAD offsets, per each channel
+ */
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if (!pvt->channel[i].dimms)
+ continue;
+ for (j = 0; j < n_tads; j++) {
+ pci_read_config_dword(pvt->pci_tad[i],
+ tad_ch_nilv_offset[j],
+ &reg);
+ tmp_mb = TAD_OFFSET(reg) >> 20;
+ debugf0("TAD CH#%d, offset #%d: %Lu.%03Lu GB (0x%016Lx), reg=0x%08x\n",
+ i, j,
+ tmp_mb / 1000, tmp_mb % 1000,
+ ((u64)tmp_mb) << 20L,
+ reg);
+ }
+ }
+
+ /*
+ * Step 6) Get RIR Wayness/Limit, per each channel
+ */
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if (!pvt->channel[i].dimms)
+ continue;
+ for (j = 0; j < MAX_RIR_RANGES; j++) {
+ pci_read_config_dword(pvt->pci_tad[i],
+ rir_way_limit[j],
+ &reg);
+
+ if (!IS_RIR_VALID(reg))
+ continue;
+
+ tmp_mb = RIR_LIMIT(reg) >> 20;
+ rir_way = 1 << RIR_WAY(reg);
+ debugf0("CH#%d RIR#%d, limit: %Lu.%03Lu GB (0x%016Lx), way: %d, reg=0x%08x\n",
+ i, j,
+ tmp_mb / 1000, tmp_mb % 1000,
+ ((u64)tmp_mb) << 20L,
+ rir_way,
+ reg);
+
+ for (k = 0; k < rir_way; k++) {
+ pci_read_config_dword(pvt->pci_tad[i],
+ rir_offset[j][k],
+ &reg);
+ tmp_mb = RIR_OFFSET(reg) << 6;
+
+ debugf0("CH#%d RIR#%d INTL#%d, offset %Lu.%03Lu GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
+ i, j, k,
+ tmp_mb / 1000, tmp_mb % 1000,
+ ((u64)tmp_mb) << 20L,
+ (u32)RIR_RNK_TGT(reg),
+ reg);
+ }
+ }
+ }
+}
+
+struct mem_ctl_info *get_mci_for_node_id(u8 node_id)
+{
+ struct sbridge_dev *sbridge_dev;
+
+ list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) {
+ if (sbridge_dev->node_id == node_id)
+ return sbridge_dev->mci;
+ }
+ return NULL;
+}
+
+static int get_memory_error_data(struct mem_ctl_info *mci,
+ u64 addr,
+ u8 *socket,
+ long *channel_mask,
+ u8 *rank,
+ char *area_type)
+{
+ struct mem_ctl_info *new_mci;
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ char msg[256];
+ int n_rir, n_sads, n_tads, sad_way, sck_xch;
+ int sad_interl, idx, base_ch;
+ int interleave_mode;
+ unsigned sad_interleave[MAX_INTERLEAVE];
+ u32 reg;
+ u8 ch_way,sck_way;
+ u32 tad_offset;
+ u32 rir_way;
+ u64 ch_addr, offset, limit, prv = 0;
+
+
+ /*
+ * Step 0) Check if the address is at special memory ranges
+ * The check bellow is probably enough to fill all cases where
+ * the error is not inside a memory, except for the legacy
+ * range (e. g. VGA addresses). It is unlikely, however, that the
+ * memory controller would generate an error on that range.
+ */
+ if ((addr > (u64) pvt->tolm) && (addr < (1L << 32))) {
+ sprintf(msg, "Error at TOLM area, on addr 0x%08Lx", addr);
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ if (addr >= (u64)pvt->tohm) {
+ sprintf(msg, "Error at MMIOH area, on addr 0x%016Lx", addr);
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+
+ /*
+ * Step 1) Get socket
+ */
+ for (n_sads = 0; n_sads < MAX_SAD; n_sads++) {
+ pci_read_config_dword(pvt->pci_sad0, dram_rule[n_sads],
+ &reg);
+
+ if (!DRAM_RULE_ENABLE(reg))
+ continue;
+
+ limit = SAD_LIMIT(reg);
+ if (limit <= prv) {
+ sprintf(msg, "Can't discover the memory socket");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ if (addr <= limit)
+ break;
+ prv = limit;
+ }
+ if (n_sads == MAX_SAD) {
+ sprintf(msg, "Can't discover the memory socket");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ area_type = get_dram_attr(reg);
+ interleave_mode = INTERLEAVE_MODE(reg);
+
+ pci_read_config_dword(pvt->pci_sad0, interleave_list[n_sads],
+ &reg);
+ sad_interl = sad_pkg(reg, 0);
+ for (sad_way = 0; sad_way < 8; sad_way++) {
+ if (sad_way > 0 && sad_interl == sad_pkg(reg, sad_way))
+ break;
+ sad_interleave[sad_way] = sad_pkg(reg, sad_way);
+ debugf0("SAD interleave #%d: %d\n",
+ sad_way, sad_interleave[sad_way]);
+ }
+ debugf0("mc#%d: Error detected on SAD#%d: address 0x%016Lx < 0x%016Lx, Interleave [%d:6]%s\n",
+ pvt->sbridge_dev->mc,
+ n_sads,
+ addr,
+ limit,
+ sad_way + 7,
+ INTERLEAVE_MODE(reg) ? "" : "XOR[18:16]");
+ if (interleave_mode)
+ idx = ((addr >> 6) ^ (addr >> 16)) & 7;
+ else
+ idx = (addr >> 6) & 7;
+ switch (sad_way) {
+ case 1:
+ idx = 0;
+ break;
+ case 2:
+ idx = idx & 1;
+ break;
+ case 4:
+ idx = idx & 3;
+ break;
+ case 8:
+ break;
+ default:
+ sprintf(msg, "Can't discover socket interleave");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ *socket = sad_interleave[idx];
+ debugf0("SAD interleave index: %d (wayness %d) = CPU socket %d\n",
+ idx, sad_way, *socket);
+
+ /*
+ * Move to the proper node structure, in order to access the
+ * right PCI registers
+ */
+ new_mci = get_mci_for_node_id(*socket);
+ if (!new_mci) {
+ sprintf(msg, "Struct for socket #%u wasn't initialized",
+ *socket);
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ mci = new_mci;
+ pvt = mci->pvt_info;
+
+ /*
+ * Step 2) Get memory channel
+ */
+ prv = 0;
+ for (n_tads = 0; n_tads < MAX_TAD; n_tads++) {
+ pci_read_config_dword(pvt->pci_ha0, tad_dram_rule[n_tads],
+ &reg);
+ limit = TAD_LIMIT(reg);
+ if (limit <= prv) {
+ sprintf(msg, "Can't discover the memory channel");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ if (addr <= limit)
+ break;
+ prv = limit;
+ }
+ ch_way = TAD_CH(reg) + 1;
+ sck_way = TAD_SOCK(reg) + 1;
+ /*
+ * FIXME: Is it right to always use channel 0 for offsets?
+ */
+ pci_read_config_dword(pvt->pci_tad[0],
+ tad_ch_nilv_offset[n_tads],
+ &tad_offset);
+
+ if (ch_way == 3)
+ idx = addr >> 6;
+ else
+ idx = addr >> (6 + sck_way);
+ idx = idx % ch_way;
+
+ /*
+ * FIXME: Shouldn't we use CHN_IDX_OFFSET() here, when ch_way == 3 ???
+ */
+ switch (idx) {
+ case 0:
+ base_ch = TAD_TGT0(reg);
+ break;
+ case 1:
+ base_ch = TAD_TGT1(reg);
+ break;
+ case 2:
+ base_ch = TAD_TGT2(reg);
+ break;
+ case 3:
+ base_ch = TAD_TGT3(reg);
+ break;
+ default:
+ sprintf(msg, "Can't discover the TAD target");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ *channel_mask = 1 << base_ch;
+
+ if (pvt->is_mirrored) {
+ *channel_mask |= 1 << ((base_ch + 2) % 4);
+ switch(ch_way) {
+ case 2:
+ case 4:
+ sck_xch = 1 << sck_way * (ch_way >> 1);
+ break;
+ default:
+ sprintf(msg, "Invalid mirror set. Can't decode addr");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ } else
+ sck_xch = (1 << sck_way) * ch_way;
+
+ if (pvt->is_lockstep)
+ *channel_mask |= 1 << ((base_ch + 1) % 4);
+
+ offset = TAD_OFFSET(tad_offset);
+
+ debugf0("TAD#%d: address 0x%016Lx < 0x%016Lx, socket interleave %d, channel interleave %d (offset 0x%08Lx), index %d, base ch: %d, ch mask: 0x%02lx\n",
+ n_tads,
+ addr,
+ limit,
+ (u32)TAD_SOCK(reg),
+ ch_way,
+ offset,
+ idx,
+ base_ch,
+ *channel_mask);
+
+ /* Calculate channel address */
+ /* Remove the TAD offset */
+
+ if (offset > addr) {
+ sprintf(msg, "Can't calculate ch addr: TAD offset 0x%08Lx is too high for addr 0x%08Lx!",
+ offset, addr);
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ addr -= offset;
+ /* Store the low bits [0:6] of the addr */
+ ch_addr = addr & 0x7f;
+ /* Remove socket wayness and remove 6 bits */
+ addr >>= 6;
+ addr /= sck_xch;
+#if 0
+ /* Divide by channel way */
+ addr = addr / ch_way;
+#endif
+ /* Recover the last 6 bits */
+ ch_addr |= addr << 6;
+
+ /*
+ * Step 3) Decode rank
+ */
+ for (n_rir = 0; n_rir < MAX_RIR_RANGES; n_rir++) {
+ pci_read_config_dword(pvt->pci_tad[base_ch],
+ rir_way_limit[n_rir],
+ &reg);
+
+ if (!IS_RIR_VALID(reg))
+ continue;
+
+ limit = RIR_LIMIT(reg);
+
+ debugf0("RIR#%d, limit: %Lu.%03Lu GB (0x%016Lx), way: %d\n",
+ n_rir,
+ (limit >> 20) / 1000, (limit >> 20) % 1000,
+ limit,
+ 1 << RIR_WAY(reg));
+ if (ch_addr <= limit)
+ break;
+ }
+ if (n_rir == MAX_RIR_RANGES) {
+ sprintf(msg, "Can't discover the memory rank for ch addr 0x%08Lx",
+ ch_addr);
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ rir_way = RIR_WAY(reg);
+ if (pvt->is_close_pg)
+ idx = (ch_addr >> 6);
+ else
+ idx = (ch_addr >> 13); /* FIXME: Datasheet says to shift by 15 */
+ idx %= 1 << rir_way;
+
+ pci_read_config_dword(pvt->pci_tad[base_ch],
+ rir_offset[n_rir][idx],
+ &reg);
+ *rank = RIR_RNK_TGT(reg);
+
+ debugf0("RIR#%d: channel address 0x%08Lx < 0x%08Lx, RIR interleave %d, index %d\n",
+ n_rir,
+ ch_addr,
+ limit,
+ rir_way,
+ idx);
+
+ return 0;
+}
+
+/****************************************************************************
+ Device initialization routines: put/get, init/exit
+ ****************************************************************************/
+
+/*
+ * sbridge_put_all_devices 'put' all the devices that we have
+ * reserved via 'get'
+ */
+static void sbridge_put_devices(struct sbridge_dev *sbridge_dev)
+{
+ int i;
+
+ debugf0(__FILE__ ": %s()\n", __func__);
+ for (i = 0; i < sbridge_dev->n_devs; i++) {
+ struct pci_dev *pdev = sbridge_dev->pdev[i];
+ if (!pdev)
+ continue;
+ debugf0("Removing dev %02x:%02x.%d\n",
+ pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+ pci_dev_put(pdev);
+ }
+}
+
+static void sbridge_put_all_devices(void)
+{
+ struct sbridge_dev *sbridge_dev, *tmp;
+
+ list_for_each_entry_safe(sbridge_dev, tmp, &sbridge_edac_list, list) {
+ sbridge_put_devices(sbridge_dev);
+ free_sbridge_dev(sbridge_dev);
+ }
+}
+
+/*
+ * sbridge_get_all_devices Find and perform 'get' operation on the MCH's
+ * device/functions we want to reference for this driver
+ *
+ * Need to 'get' device 16 func 1 and func 2
+ */
+static int sbridge_get_onedevice(struct pci_dev **prev,
+ u8 *num_mc,
+ const struct pci_id_table *table,
+ const unsigned devno)
+{
+ struct sbridge_dev *sbridge_dev;
+ const struct pci_id_descr *dev_descr = &table->descr[devno];
+
+ struct pci_dev *pdev = NULL;
+ u8 bus = 0;
+
+ sbridge_printk(KERN_INFO,
+ "Seeking for: dev %02x.%d PCI ID %04x:%04x\n",
+ dev_descr->dev, dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ dev_descr->dev_id, *prev);
+
+ if (!pdev) {
+ if (*prev) {
+ *prev = pdev;
+ return 0;
+ }
+
+ if (dev_descr->optional)
+ return 0;
+
+ if (devno == 0)
+ return -ENODEV;
+
+ sbridge_printk(KERN_INFO,
+ "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
+ dev_descr->dev, dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+
+ /* End of list, leave */
+ return -ENODEV;
+ }
+ bus = pdev->bus->number;
+
+ sbridge_dev = get_sbridge_dev(bus);
+ if (!sbridge_dev) {
+ sbridge_dev = alloc_sbridge_dev(bus, table);
+ if (!sbridge_dev) {
+ pci_dev_put(pdev);
+ return -ENOMEM;
+ }
+ (*num_mc)++;
+ }
+
+ if (sbridge_dev->pdev[devno]) {
+ sbridge_printk(KERN_ERR,
+ "Duplicated device for "
+ "dev %02x:%d.%d PCI ID %04x:%04x\n",
+ bus, dev_descr->dev, dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+ pci_dev_put(pdev);
+ return -ENODEV;
+ }
+
+ sbridge_dev->pdev[devno] = pdev;
+
+ /* Sanity check */
+ if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
+ PCI_FUNC(pdev->devfn) != dev_descr->func)) {
+ sbridge_printk(KERN_ERR,
+ "Device PCI ID %04x:%04x "
+ "has dev %02x:%d.%d instead of dev %02x:%02x.%d\n",
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
+ bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ bus, dev_descr->dev, dev_descr->func);
+ return -ENODEV;
+ }
+
+ /* Be sure that the device is enabled */
+ if (unlikely(pci_enable_device(pdev) < 0)) {
+ sbridge_printk(KERN_ERR,
+ "Couldn't enable "
+ "dev %02x:%d.%d PCI ID %04x:%04x\n",
+ bus, dev_descr->dev, dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+ return -ENODEV;
+ }
+
+ debugf0("Detected dev %02x:%d.%d PCI ID %04x:%04x\n",
+ bus, dev_descr->dev,
+ dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+
+ /*
+ * As stated on drivers/pci/search.c, the reference count for
+ * @from is always decremented if it is not %NULL. So, as we need
+ * to get all devices up to null, we need to do a get for the device
+ */
+ pci_dev_get(pdev);
+
+ *prev = pdev;
+
+ return 0;
+}
+
+static int sbridge_get_all_devices(u8 *num_mc)
+{
+ int i, rc;
+ struct pci_dev *pdev = NULL;
+ const struct pci_id_table *table = pci_dev_descr_sbridge_table;
+
+ while (table && table->descr) {
+ for (i = 0; i < table->n_devs; i++) {
+ pdev = NULL;
+ do {
+ rc = sbridge_get_onedevice(&pdev, num_mc,
+ table, i);
+ if (rc < 0) {
+ if (i == 0) {
+ i = table->n_devs;
+ break;
+ }
+ sbridge_put_all_devices();
+ return -ENODEV;
+ }
+ } while (pdev);
+ }
+ table++;
+ }
+
+ return 0;
+}
+
+static int mci_bind_devs(struct mem_ctl_info *mci,
+ struct sbridge_dev *sbridge_dev)
+{
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ struct pci_dev *pdev;
+ int i, func, slot;
+
+ for (i = 0; i < sbridge_dev->n_devs; i++) {
+ pdev = sbridge_dev->pdev[i];
+ if (!pdev)
+ continue;
+ slot = PCI_SLOT(pdev->devfn);
+ func = PCI_FUNC(pdev->devfn);
+ switch (slot) {
+ case 12:
+ switch (func) {
+ case 6:
+ pvt->pci_sad0 = pdev;
+ break;
+ case 7:
+ pvt->pci_sad1 = pdev;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case 13:
+ switch (func) {
+ case 6:
+ pvt->pci_br = pdev;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case 14:
+ switch (func) {
+ case 0:
+ pvt->pci_ha0 = pdev;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case 15:
+ switch (func) {
+ case 0:
+ pvt->pci_ta = pdev;
+ break;
+ case 1:
+ pvt->pci_ras = pdev;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ pvt->pci_tad[func - 2] = pdev;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case 17:
+ switch (func) {
+ case 0:
+ pvt->pci_ddrio = pdev;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ default:
+ goto error;
+ }
+
+ debugf0("Associated PCI %02x.%02d.%d with dev = %p\n",
+ sbridge_dev->bus,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ pdev);
+ }
+
+ /* Check if everything were registered */
+ if (!pvt->pci_sad0 || !pvt->pci_sad1 || !pvt->pci_ha0 ||
+ !pvt-> pci_tad || !pvt->pci_ras || !pvt->pci_ta ||
+ !pvt->pci_ddrio)
+ goto enodev;
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if (!pvt->pci_tad[i])
+ goto enodev;
+ }
+ return 0;
+
+enodev:
+ sbridge_printk(KERN_ERR, "Some needed devices are missing\n");
+ return -ENODEV;
+
+error:
+ sbridge_printk(KERN_ERR, "Device %d, function %d "
+ "is out of the expected range\n",
+ slot, func);
+ return -EINVAL;
+}
+
+/****************************************************************************
+ Error check routines
+ ****************************************************************************/
+
+/*
+ * While Sandy Bridge has error count registers, SMI BIOS read values from
+ * and resets the counters. So, they are not reliable for the OS to read
+ * from them. So, we have no option but to just trust on whatever MCE is
+ * telling us about the errors.
+ */
+static void sbridge_mce_output_error(struct mem_ctl_info *mci,
+ const struct mce *m)
+{
+ struct mem_ctl_info *new_mci;
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ char *type, *optype, *msg, *recoverable_msg;
+ bool ripv = GET_BITFIELD(m->mcgstatus, 0, 0);
+ bool overflow = GET_BITFIELD(m->status, 62, 62);
+ bool uncorrected_error = GET_BITFIELD(m->status, 61, 61);
+ bool recoverable = GET_BITFIELD(m->status, 56, 56);
+ u32 core_err_cnt = GET_BITFIELD(m->status, 38, 52);
+ u32 mscod = GET_BITFIELD(m->status, 16, 31);
+ u32 errcode = GET_BITFIELD(m->status, 0, 15);
+ u32 channel = GET_BITFIELD(m->status, 0, 3);
+ u32 optypenum = GET_BITFIELD(m->status, 4, 6);
+ long channel_mask, first_channel;
+ u8 rank, socket;
+ int csrow, rc, dimm;
+ char *area_type = "Unknown";
+
+ if (ripv)
+ type = "NON_FATAL";
+ else
+ type = "FATAL";
+
+ /*
+ * According with Table 15-9 of the Intel Archictecture spec vol 3A,
+ * memory errors should fit in this mask:
+ * 000f 0000 1mmm cccc (binary)
+ * where:
+ * f = Correction Report Filtering Bit. If 1, subsequent errors
+ * won't be shown
+ * mmm = error type
+ * cccc = channel
+ * If the mask doesn't match, report an error to the parsing logic
+ */
+ if (! ((errcode & 0xef80) == 0x80)) {
+ optype = "Can't parse: it is not a mem";
+ } else {
+ switch (optypenum) {
+ case 0:
+ optype = "generic undef request";
+ break;
+ case 1:
+ optype = "memory read";
+ break;
+ case 2:
+ optype = "memory write";
+ break;
+ case 3:
+ optype = "addr/cmd";
+ break;
+ case 4:
+ optype = "memory scrubbing";
+ break;
+ default:
+ optype = "reserved";
+ break;
+ }
+ }
+
+ rc = get_memory_error_data(mci, m->addr, &socket,
+ &channel_mask, &rank, area_type);
+ if (rc < 0)
+ return;
+ new_mci = get_mci_for_node_id(socket);
+ if (!new_mci) {
+ edac_mc_handle_ce_no_info(mci, "Error: socket got corrupted!");
+ return;
+ }
+ mci = new_mci;
+ pvt = mci->pvt_info;
+
+ first_channel = find_first_bit(&channel_mask, NUM_CHANNELS);
+
+ if (rank < 4)
+ dimm = 0;
+ else if (rank < 8)
+ dimm = 1;
+ else
+ dimm = 2;
+
+ csrow = pvt->csrow_map[first_channel][dimm];
+
+ if (uncorrected_error && recoverable)
+ recoverable_msg = " recoverable";
+ else
+ recoverable_msg = "";
+
+ /*
+ * FIXME: What should we do with "channel" information on mcelog?
+ * Probably, we can just discard it, as the channel information
+ * comes from the get_memory_error_data() address decoding
+ */
+ msg = kasprintf(GFP_ATOMIC,
+ "%d %s error(s): %s on %s area %s%s: cpu=%d Err=%04x:%04x (ch=%d), "
+ "addr = 0x%08llx => socket=%d, Channel=%ld(mask=%ld), rank=%d\n",
+ core_err_cnt,
+ area_type,
+ optype,
+ type,
+ recoverable_msg,
+ overflow ? "OVERFLOW" : "",
+ m->cpu,
+ mscod, errcode,
+ channel, /* 1111b means not specified */
+ (long long) m->addr,
+ socket,
+ first_channel, /* This is the real channel on SB */
+ channel_mask,
+ rank);
+
+ debugf0("%s", msg);
+
+ /* Call the helper to output message */
+ if (uncorrected_error)
+ edac_mc_handle_fbd_ue(mci, csrow, 0, 0, msg);
+ else
+ edac_mc_handle_fbd_ce(mci, csrow, 0, msg);
+
+ kfree(msg);
+}
+
+/*
+ * sbridge_check_error Retrieve and process errors reported by the
+ * hardware. Called by the Core module.
+ */
+static void sbridge_check_error(struct mem_ctl_info *mci)
+{
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ int i;
+ unsigned count = 0;
+ struct mce *m;
+
+ /*
+ * MCE first step: Copy all mce errors into a temporary buffer
+ * We use a double buffering here, to reduce the risk of
+ * loosing an error.
+ */
+ smp_rmb();
+ count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
+ % MCE_LOG_LEN;
+ if (!count)
+ return;
+
+ m = pvt->mce_outentry;
+ if (pvt->mce_in + count > MCE_LOG_LEN) {
+ unsigned l = MCE_LOG_LEN - pvt->mce_in;
+
+ memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
+ smp_wmb();
+ pvt->mce_in = 0;
+ count -= l;
+ m += l;
+ }
+ memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
+ smp_wmb();
+ pvt->mce_in += count;
+
+ smp_rmb();
+ if (pvt->mce_overrun) {
+ sbridge_printk(KERN_ERR, "Lost %d memory errors\n",
+ pvt->mce_overrun);
+ smp_wmb();
+ pvt->mce_overrun = 0;
+ }
+
+ /*
+ * MCE second step: parse errors and display
+ */
+ for (i = 0; i < count; i++)
+ sbridge_mce_output_error(mci, &pvt->mce_outentry[i]);
+}
+
+/*
+ * sbridge_mce_check_error Replicates mcelog routine to get errors
+ * This routine simply queues mcelog errors, and
+ * return. The error itself should be handled later
+ * by sbridge_check_error.
+ * WARNING: As this routine should be called at NMI time, extra care should
+ * be taken to avoid deadlocks, and to be as fast as possible.
+ */
+static int sbridge_mce_check_error(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct mce *mce = (struct mce *)data;
+ struct mem_ctl_info *mci;
+ struct sbridge_pvt *pvt;
+
+ mci = get_mci_for_node_id(mce->socketid);
+ if (!mci)
+ return NOTIFY_BAD;
+ pvt = mci->pvt_info;
+
+ /*
+ * Just let mcelog handle it if the error is
+ * outside the memory controller. A memory error
+ * is indicated by bit 7 = 1 and bits = 8-11,13-15 = 0.
+ * bit 12 has an special meaning.
+ */
+ if ((mce->status & 0xefff) >> 7 != 1)
+ return NOTIFY_DONE;
+
+ printk("sbridge: HANDLING MCE MEMORY ERROR\n");
+
+ printk("CPU %d: Machine Check Exception: %Lx Bank %d: %016Lx\n",
+ mce->extcpu, mce->mcgstatus, mce->bank, mce->status);
+ printk("TSC %llx ", mce->tsc);
+ printk("ADDR %llx ", mce->addr);
+ printk("MISC %llx ", mce->misc);
+
+ printk("PROCESSOR %u:%x TIME %llu SOCKET %u APIC %x\n",
+ mce->cpuvendor, mce->cpuid, mce->time,
+ mce->socketid, mce->apicid);
+
+#ifdef CONFIG_SMP
+ /* Only handle if it is the right mc controller */
+ if (cpu_data(mce->cpu).phys_proc_id != pvt->sbridge_dev->mc)
+ return NOTIFY_DONE;
+#endif
+
+ smp_rmb();
+ if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
+ smp_wmb();
+ pvt->mce_overrun++;
+ return NOTIFY_DONE;
+ }
+
+ /* Copy memory error at the ringbuffer */
+ memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
+ smp_wmb();
+ pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
+
+ /* Handle fatal errors immediately */
+ if (mce->mcgstatus & 1)
+ sbridge_check_error(mci);
+
+ /* Advice mcelog that the error were handled */
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block sbridge_mce_dec = {
+ .notifier_call = sbridge_mce_check_error,
+};
+
+/****************************************************************************
+ EDAC register/unregister logic
+ ****************************************************************************/
+
+static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev)
+{
+ struct mem_ctl_info *mci = sbridge_dev->mci;
+ struct sbridge_pvt *pvt;
+
+ if (unlikely(!mci || !mci->pvt_info)) {
+ debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
+ __func__, &sbridge_dev->pdev[0]->dev);
+
+ sbridge_printk(KERN_ERR, "Couldn't find mci handler\n");
+ return;
+ }
+
+ pvt = mci->pvt_info;
+
+ debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
+ __func__, mci, &sbridge_dev->pdev[0]->dev);
+
+ atomic_notifier_chain_unregister(&x86_mce_decoder_chain,
+ &sbridge_mce_dec);
+
+ /* Remove MC sysfs nodes */
+ edac_mc_del_mc(mci->dev);
+
+ debugf1("%s: free mci struct\n", mci->ctl_name);
+ kfree(mci->ctl_name);
+ edac_mc_free(mci);
+ sbridge_dev->mci = NULL;
+}
+
+static int sbridge_register_mci(struct sbridge_dev *sbridge_dev)
+{
+ struct mem_ctl_info *mci;
+ struct sbridge_pvt *pvt;
+ int rc, channels, csrows;
+
+ /* Check the number of active and not disabled channels */
+ rc = sbridge_get_active_channels(sbridge_dev->bus, &channels, &csrows);
+ if (unlikely(rc < 0))
+ return rc;
+
+ /* allocate a new MC control structure */
+ mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, sbridge_dev->mc);
+ if (unlikely(!mci))
+ return -ENOMEM;
+
+ debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
+ __func__, mci, &sbridge_dev->pdev[0]->dev);
+
+ pvt = mci->pvt_info;
+ memset(pvt, 0, sizeof(*pvt));
+
+ /* Associate sbridge_dev and mci for future usage */
+ pvt->sbridge_dev = sbridge_dev;
+ sbridge_dev->mci = mci;
+
+ mci->mtype_cap = MEM_FLAG_DDR3;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE;
+ mci->edac_cap = EDAC_FLAG_NONE;
+ mci->mod_name = "sbridge_edac.c";
+ mci->mod_ver = SBRIDGE_REVISION;
+ mci->ctl_name = kasprintf(GFP_KERNEL, "Sandy Bridge Socket#%d", mci->mc_idx);
+ mci->dev_name = pci_name(sbridge_dev->pdev[0]);
+ mci->ctl_page_to_phys = NULL;
+
+ /* Set the function pointer to an actual operation function */
+ mci->edac_check = sbridge_check_error;
+
+ /* Store pci devices at mci for faster access */
+ rc = mci_bind_devs(mci, sbridge_dev);
+ if (unlikely(rc < 0))
+ goto fail0;
+
+ /* Get dimm basic config and the memory layout */
+ get_dimm_config(mci);
+ get_memory_layout(mci);
+
+ /* record ptr to the generic device */
+ mci->dev = &sbridge_dev->pdev[0]->dev;
+
+ /* add this new MC control structure to EDAC's list of MCs */
+ if (unlikely(edac_mc_add_mc(mci))) {
+ debugf0("MC: " __FILE__
+ ": %s(): failed edac_mc_add_mc()\n", __func__);
+ rc = -EINVAL;
+ goto fail0;
+ }
+
+ atomic_notifier_chain_register(&x86_mce_decoder_chain,
+ &sbridge_mce_dec);
+ return 0;
+
+fail0:
+ kfree(mci->ctl_name);
+ edac_mc_free(mci);
+ sbridge_dev->mci = NULL;
+ return rc;
+}
+
+/*
+ * sbridge_probe Probe for ONE instance of device to see if it is
+ * present.
+ * return:
+ * 0 for FOUND a device
+ * < 0 for error code
+ */
+
+static int __devinit sbridge_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int rc;
+ u8 mc, num_mc = 0;
+ struct sbridge_dev *sbridge_dev;
+
+ /* get the pci devices we want to reserve for our use */
+ mutex_lock(&sbridge_edac_lock);
+
+ /*
+ * All memory controllers are allocated at the first pass.
+ */
+ if (unlikely(probed >= 1)) {
+ mutex_unlock(&sbridge_edac_lock);
+ return -ENODEV;
+ }
+ probed++;
+
+ rc = sbridge_get_all_devices(&num_mc);
+ if (unlikely(rc < 0))
+ goto fail0;
+ mc = 0;
+
+ list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) {
+ debugf0("Registering MC#%d (%d of %d)\n", mc, mc + 1, num_mc);
+ sbridge_dev->mc = mc++;
+ rc = sbridge_register_mci(sbridge_dev);
+ if (unlikely(rc < 0))
+ goto fail1;
+ }
+
+ sbridge_printk(KERN_INFO, "Driver loaded.\n");
+
+ mutex_unlock(&sbridge_edac_lock);
+ return 0;
+
+fail1:
+ list_for_each_entry(sbridge_dev, &sbridge_edac_list, list)
+ sbridge_unregister_mci(sbridge_dev);
+
+ sbridge_put_all_devices();
+fail0:
+ mutex_unlock(&sbridge_edac_lock);
+ return rc;
+}
+
+/*
+ * sbridge_remove destructor for one instance of device
+ *
+ */
+static void __devexit sbridge_remove(struct pci_dev *pdev)
+{
+ struct sbridge_dev *sbridge_dev;
+
+ debugf0(__FILE__ ": %s()\n", __func__);
+
+ /*
+ * we have a trouble here: pdev value for removal will be wrong, since
+ * it will point to the X58 register used to detect that the machine
+ * is a Nehalem or upper design. However, due to the way several PCI
+ * devices are grouped together to provide MC functionality, we need
+ * to use a different method for releasing the devices
+ */
+
+ mutex_lock(&sbridge_edac_lock);
+
+ if (unlikely(!probed)) {
+ mutex_unlock(&sbridge_edac_lock);
+ return;
+ }
+
+ list_for_each_entry(sbridge_dev, &sbridge_edac_list, list)
+ sbridge_unregister_mci(sbridge_dev);
+
+ /* Release PCI resources */
+ sbridge_put_all_devices();
+
+ probed--;
+
+ mutex_unlock(&sbridge_edac_lock);
+}
+
+MODULE_DEVICE_TABLE(pci, sbridge_pci_tbl);
+
+/*
+ * sbridge_driver pci_driver structure for this module
+ *
+ */
+static struct pci_driver sbridge_driver = {
+ .name = "sbridge_edac",
+ .probe = sbridge_probe,
+ .remove = __devexit_p(sbridge_remove),
+ .id_table = sbridge_pci_tbl,
+};
+
+/*
+ * sbridge_init Module entry function
+ * Try to initialize this module for its devices
+ */
+static int __init sbridge_init(void)
+{
+ int pci_rc;
+
+ debugf2("MC: " __FILE__ ": %s()\n", __func__);
+
+ /* Ensure that the OPSTATE is set correctly for POLL or NMI */
+ opstate_init();
+
+ pci_rc = pci_register_driver(&sbridge_driver);
+
+ if (pci_rc >= 0)
+ return 0;
+
+ sbridge_printk(KERN_ERR, "Failed to register device with error %d.\n",
+ pci_rc);
+
+ return pci_rc;
+}
+
+/*
+ * sbridge_exit() Module exit function
+ * Unregister the driver
+ */
+static void __exit sbridge_exit(void)
+{
+ debugf2("MC: " __FILE__ ": %s()\n", __func__);
+ pci_unregister_driver(&sbridge_driver);
+}
+
+module_init(sbridge_init);
+module_exit(sbridge_exit);
+
+module_param(edac_op_state, int, 0444);
+MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
+MODULE_DESCRIPTION("MC Driver for Intel Sandy Bridge memory controllers - "
+ SBRIDGE_REVISION);
diff --git a/drivers/eisa/pci_eisa.c b/drivers/eisa/pci_eisa.c
index 0dd0f633b18d..cdae207028a7 100644
--- a/drivers/eisa/pci_eisa.c
+++ b/drivers/eisa/pci_eisa.c
@@ -51,7 +51,7 @@ static struct pci_device_id pci_eisa_pci_tbl[] = {
{ 0, }
};
-static struct pci_driver pci_eisa_driver = {
+static struct pci_driver __refdata pci_eisa_driver = {
.name = "pci_eisa",
.id_table = pci_eisa_pci_tbl,
.probe = pci_eisa_init,
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index 29d2423fae6d..85661b060ed7 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -32,7 +32,7 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/byteorder.h>
#include "core.h"
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index b1c11775839c..4799393247c8 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -216,15 +216,33 @@ struct inbound_phy_packet_event {
struct fw_cdev_event_phy_packet phy_packet;
};
-static inline void __user *u64_to_uptr(__u64 value)
+#ifdef CONFIG_COMPAT
+static void __user *u64_to_uptr(u64 value)
+{
+ if (is_compat_task())
+ return compat_ptr(value);
+ else
+ return (void __user *)(unsigned long)value;
+}
+
+static u64 uptr_to_u64(void __user *ptr)
+{
+ if (is_compat_task())
+ return ptr_to_compat(ptr);
+ else
+ return (u64)(unsigned long)ptr;
+}
+#else
+static inline void __user *u64_to_uptr(u64 value)
{
return (void __user *)(unsigned long)value;
}
-static inline __u64 uptr_to_u64(void __user *ptr)
+static inline u64 uptr_to_u64(void __user *ptr)
{
- return (__u64)(unsigned long)ptr;
+ return (u64)(unsigned long)ptr;
}
+#endif /* CONFIG_COMPAT */
static int fw_device_op_open(struct inode *inode, struct file *file)
{
@@ -253,14 +271,11 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
init_waitqueue_head(&client->wait);
init_waitqueue_head(&client->tx_flush_wait);
INIT_LIST_HEAD(&client->phy_receiver_link);
+ INIT_LIST_HEAD(&client->link);
kref_init(&client->kref);
file->private_data = client;
- mutex_lock(&device->client_list_mutex);
- list_add_tail(&client->link, &device->client_list);
- mutex_unlock(&device->client_list_mutex);
-
return nonseekable_open(inode, file);
}
@@ -451,15 +466,20 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
if (ret != 0)
return -EFAULT;
+ mutex_lock(&client->device->client_list_mutex);
+
client->bus_reset_closure = a->bus_reset_closure;
if (a->bus_reset != 0) {
fill_bus_reset_event(&bus_reset, client);
- if (copy_to_user(u64_to_uptr(a->bus_reset),
- &bus_reset, sizeof(bus_reset)))
- return -EFAULT;
+ ret = copy_to_user(u64_to_uptr(a->bus_reset),
+ &bus_reset, sizeof(bus_reset));
}
+ if (ret == 0 && list_empty(&client->link))
+ list_add_tail(&client->link, &client->device->client_list);
- return 0;
+ mutex_unlock(&client->device->client_list_mutex);
+
+ return ret ? -EFAULT : 0;
}
static int add_client_resource(struct client *client,
@@ -1583,7 +1603,7 @@ static int dispatch_ioctl(struct client *client,
if (_IOC_TYPE(cmd) != '#' ||
_IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) ||
_IOC_SIZE(cmd) > sizeof(buffer))
- return -EINVAL;
+ return -ENOTTY;
if (_IOC_DIR(cmd) == _IOC_READ)
memset(&buffer, 0, _IOC_SIZE(cmd));
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c
index 95a471401892..f3b890da1e87 100644
--- a/drivers/firewire/core-device.c
+++ b/drivers/firewire/core-device.c
@@ -38,7 +38,7 @@
#include <linux/string.h>
#include <linux/workqueue.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/byteorder.h>
#include <asm/system.h>
@@ -455,15 +455,20 @@ static struct device_attribute fw_device_attributes[] = {
static int read_rom(struct fw_device *device,
int generation, int index, u32 *data)
{
- int rcode;
+ u64 offset = (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + index * 4;
+ int i, rcode;
/* device->node_id, accessed below, must not be older than generation */
smp_rmb();
- rcode = fw_run_transaction(device->card, TCODE_READ_QUADLET_REQUEST,
- device->node_id, generation, device->max_speed,
- (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + index * 4,
- data, 4);
+ for (i = 10; i < 100; i += 10) {
+ rcode = fw_run_transaction(device->card,
+ TCODE_READ_QUADLET_REQUEST, device->node_id,
+ generation, device->max_speed, offset, data, 4);
+ if (rcode != RCODE_BUSY)
+ break;
+ msleep(i);
+ }
be32_to_cpus(data);
return rcode;
diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c
index 57c3973093ad..0f90e0071875 100644
--- a/drivers/firewire/core-iso.c
+++ b/drivers/firewire/core-iso.c
@@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <asm/byteorder.h>
diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c
index 193ed9233144..94d3b494ddfb 100644
--- a/drivers/firewire/core-topology.c
+++ b/drivers/firewire/core-topology.c
@@ -29,7 +29,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/byteorder.h>
#include <asm/system.h>
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index 334b82a3542c..855ab3f5936f 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -1046,8 +1046,8 @@ static void update_split_timeout(struct fw_card *card)
cycles = card->split_timeout_hi * 8000 + (card->split_timeout_lo >> 19);
- cycles = max(cycles, 800u); /* minimum as per the spec */
- cycles = min(cycles, 3u * 8000u); /* maximum OHCI timeout */
+ /* minimum per IEEE 1394, maximum which doesn't overflow OHCI */
+ cycles = clamp(cycles, 800u, 3u * 8000u);
card->split_timeout_cycles = cycles;
card->split_timeout_jiffies = DIV_ROUND_UP(cycles * HZ, 8000);
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index 0fe4e4e6eda7..b45be5767529 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -9,7 +9,7 @@
#include <linux/slab.h>
#include <linux/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
struct device;
struct fw_card;
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index b9762d07198d..a20f45b1e7e5 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -7,6 +7,7 @@
*/
#include <linux/bug.h>
+#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/ethtool.h>
@@ -73,7 +74,7 @@ struct rfc2734_arp {
__be32 fifo_lo; /* lo 32bits of sender's FIFO addr */
__be32 sip; /* Sender's IP Address */
__be32 tip; /* IP Address of requested hw addr */
-} __attribute__((packed));
+} __packed;
/* This header format is specific to this driver implementation. */
#define FWNET_ALEN 8
@@ -81,7 +82,7 @@ struct rfc2734_arp {
struct fwnet_header {
u8 h_dest[FWNET_ALEN]; /* destination address */
__be16 h_proto; /* packet type ID field */
-} __attribute__((packed));
+} __packed;
/* IPv4 and IPv6 encapsulation header */
struct rfc2734_header {
@@ -261,16 +262,16 @@ static int fwnet_header_rebuild(struct sk_buff *skb)
}
static int fwnet_header_cache(const struct neighbour *neigh,
- struct hh_cache *hh)
+ struct hh_cache *hh, __be16 type)
{
struct net_device *net;
struct fwnet_header *h;
- if (hh->hh_type == cpu_to_be16(ETH_P_802_3))
+ if (type == cpu_to_be16(ETH_P_802_3))
return -1;
net = neigh->dev;
h = (struct fwnet_header *)((u8 *)hh->hh_data + 16 - sizeof(*h));
- h->h_proto = hh->hh_type;
+ h->h_proto = type;
memcpy(h->h_dest, neigh->ha, net->addr_len);
hh->hh_len = FWNET_HLEN;
@@ -501,11 +502,7 @@ static struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev,
static unsigned fwnet_max_payload(unsigned max_rec, unsigned speed)
{
max_rec = min(max_rec, speed + 8);
- max_rec = min(max_rec, 0xbU); /* <= 4096 */
- if (max_rec < 8) {
- fw_notify("max_rec %x out of range\n", max_rec);
- max_rec = 8;
- }
+ max_rec = clamp(max_rec, 8U, 11U); /* 512...4096 */
return (1 << (max_rec + 1)) - RFC2374_FRAG_HDR_SIZE;
}
@@ -1124,17 +1121,12 @@ static int fwnet_broadcast_start(struct fwnet_device *dev)
unsigned u;
if (dev->local_fifo == FWNET_NO_FIFO_ADDR) {
- /* outside OHCI posted write area? */
- static const struct fw_address_region region = {
- .start = 0xffff00000000ULL,
- .end = CSR_REGISTER_BASE,
- };
-
dev->handler.length = 4096;
dev->handler.address_callback = fwnet_receive_packet;
dev->handler.callback_data = dev;
- retval = fw_core_add_address_handler(&dev->handler, &region);
+ retval = fw_core_add_address_handler(&dev->handler,
+ &fw_high_memory_region);
if (retval < 0)
goto failed_initial;
diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c
index 0618145376ad..763626b739d1 100644
--- a/drivers/firewire/nosy.c
+++ b/drivers/firewire/nosy.c
@@ -37,7 +37,7 @@
#include <linux/uaccess.h>
#include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/byteorder.h>
#include "nosy.h"
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index ebb897329c1e..6628feaa7622 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -42,6 +42,7 @@
#include <linux/string.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
#include <asm/byteorder.h>
#include <asm/page.h>
@@ -125,6 +126,7 @@ struct context {
struct fw_ohci *ohci;
u32 regs;
int total_allocation;
+ u32 current_bus;
bool running;
bool flushing;
@@ -226,7 +228,7 @@ struct fw_ohci {
__le32 *self_id_cpu;
dma_addr_t self_id_bus;
- struct tasklet_struct bus_reset_tasklet;
+ struct work_struct bus_reset_work;
u32 self_id_buffer[512];
};
@@ -253,7 +255,6 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card)
#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8
#define OHCI1394_REGISTER_SIZE 0x800
-#define OHCI_LOOP_COUNT 500
#define OHCI1394_PCI_HCI_Control 0x40
#define SELF_ID_BUF_SIZE 0x800
#define OHCI_TCODE_PHY_PACKET 0x0e
@@ -264,6 +265,8 @@ static char ohci_driver_name[] = KBUILD_MODNAME;
#define PCI_DEVICE_ID_AGERE_FW643 0x5901
#define PCI_DEVICE_ID_JMICRON_JMB38X_FW 0x2380
#define PCI_DEVICE_ID_TI_TSB12LV22 0x8009
+#define PCI_DEVICE_ID_TI_TSB12LV26 0x8020
+#define PCI_DEVICE_ID_TI_TSB82AA2 0x8025
#define PCI_VENDOR_ID_PINNACLE_SYSTEMS 0x11bd
#define QUIRK_CYCLE_TIMER 1
@@ -271,6 +274,7 @@ static char ohci_driver_name[] = KBUILD_MODNAME;
#define QUIRK_BE_HEADERS 4
#define QUIRK_NO_1394A 8
#define QUIRK_NO_MSI 16
+#define QUIRK_TI_SLLZ059 32
/* In case of multiple matches in ohci_quirks[], only the first one is used. */
static const struct {
@@ -291,12 +295,21 @@ static const struct {
{PCI_VENDOR_ID_NEC, PCI_ANY_ID, PCI_ANY_ID,
QUIRK_CYCLE_TIMER},
+ {PCI_VENDOR_ID_O2, PCI_ANY_ID, PCI_ANY_ID,
+ QUIRK_NO_MSI},
+
{PCI_VENDOR_ID_RICOH, PCI_ANY_ID, PCI_ANY_ID,
QUIRK_CYCLE_TIMER},
{PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB12LV22, PCI_ANY_ID,
QUIRK_CYCLE_TIMER | QUIRK_RESET_PACKET | QUIRK_NO_1394A},
+ {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB12LV26, PCI_ANY_ID,
+ QUIRK_RESET_PACKET | QUIRK_TI_SLLZ059},
+
+ {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB82AA2, PCI_ANY_ID,
+ QUIRK_RESET_PACKET | QUIRK_TI_SLLZ059},
+
{PCI_VENDOR_ID_TI, PCI_ANY_ID, PCI_ANY_ID,
QUIRK_RESET_PACKET},
@@ -313,6 +326,7 @@ MODULE_PARM_DESC(quirks, "Chip quirks (default = 0"
", AR/selfID endianess = " __stringify(QUIRK_BE_HEADERS)
", no 1394a enhancements = " __stringify(QUIRK_NO_1394A)
", disable MSI = " __stringify(QUIRK_NO_MSI)
+ ", TI SLLZ059 erratum = " __stringify(QUIRK_TI_SLLZ059)
")");
#define OHCI_PARAM_DEBUG_AT_AR 1
@@ -514,6 +528,12 @@ static inline void flush_writes(const struct fw_ohci *ohci)
reg_read(ohci, OHCI1394_Version);
}
+/*
+ * Beware! read_phy_reg(), write_phy_reg(), update_phy_reg(), and
+ * read_paged_phy_reg() require the caller to hold ohci->phy_reg_mutex.
+ * In other words, only use ohci_read_phy_reg() and ohci_update_phy_reg()
+ * directly. Exceptions are intrinsically serialized contexts like pci_probe.
+ */
static int read_phy_reg(struct fw_ohci *ohci, int addr)
{
u32 val;
@@ -522,6 +542,9 @@ static int read_phy_reg(struct fw_ohci *ohci, int addr)
reg_write(ohci, OHCI1394_PhyControl, OHCI1394_PhyControl_Read(addr));
for (i = 0; i < 3 + 100; i++) {
val = reg_read(ohci, OHCI1394_PhyControl);
+ if (!~val)
+ return -ENODEV; /* Card was ejected. */
+
if (val & OHCI1394_PhyControl_ReadDone)
return OHCI1394_PhyControl_ReadData(val);
@@ -545,6 +568,9 @@ static int write_phy_reg(const struct fw_ohci *ohci, int addr, u32 val)
OHCI1394_PhyControl_Write(addr, val));
for (i = 0; i < 3 + 100; i++) {
val = reg_read(ohci, OHCI1394_PhyControl);
+ if (!~val)
+ return -ENODEV; /* Card was ejected. */
+
if (!(val & OHCI1394_PhyControl_WritePending))
return 0;
@@ -630,7 +656,6 @@ static void ar_context_link_page(struct ar_context *ctx, unsigned int index)
ctx->last_buffer_index = index;
reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
- flush_writes(ctx->ohci);
}
static void ar_context_release(struct ar_context *ctx)
@@ -846,7 +871,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
*
* Alas some chips sometimes emit bus reset packets with a
* wrong generation. We set the correct generation for these
- * at a slightly incorrect time (in bus_reset_tasklet).
+ * at a slightly incorrect time (in bus_reset_work).
*/
if (evt == OHCI1394_evt_bus_reset) {
if (!(ohci->quirks & QUIRK_RESET_PACKET))
@@ -1002,7 +1027,6 @@ static void ar_context_run(struct ar_context *ctx)
reg_write(ctx->ohci, COMMAND_PTR(ctx->regs), ctx->descriptors_bus | 1);
reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN);
- flush_writes(ctx->ohci);
}
static struct descriptor *find_branch_descriptor(struct descriptor *d, int z)
@@ -1034,6 +1058,7 @@ static void context_tasklet(unsigned long data)
address = le32_to_cpu(last->branch_address);
z = address & 0xf;
address &= ~0xf;
+ ctx->current_bus = address;
/* If the branch address points to a buffer outside of the
* current buffer, advance to the next buffer. */
@@ -1202,14 +1227,14 @@ static void context_stop(struct context *ctx)
reg_write(ctx->ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN);
ctx->running = false;
- flush_writes(ctx->ohci);
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < 1000; i++) {
reg = reg_read(ctx->ohci, CONTROL_SET(ctx->regs));
if ((reg & CONTEXT_ACTIVE) == 0)
return;
- mdelay(1);
+ if (i)
+ udelay(10);
}
fw_error("Error: DMA context still active (0x%08x)\n", reg);
}
@@ -1346,12 +1371,10 @@ static int at_context_queue_packet(struct context *ctx,
context_append(ctx, d, z, 4 - z);
- if (ctx->running) {
+ if (ctx->running)
reg_write(ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
- flush_writes(ohci);
- } else {
+ else
context_run(ctx, 0);
- }
return 0;
}
@@ -1703,9 +1726,94 @@ static u32 update_bus_time(struct fw_ohci *ohci)
return ohci->bus_time | cycle_time_seconds;
}
-static void bus_reset_tasklet(unsigned long data)
+static int get_status_for_port(struct fw_ohci *ohci, int port_index)
+{
+ int reg;
+
+ mutex_lock(&ohci->phy_reg_mutex);
+ reg = write_phy_reg(ohci, 7, port_index);
+ if (reg >= 0)
+ reg = read_phy_reg(ohci, 8);
+ mutex_unlock(&ohci->phy_reg_mutex);
+ if (reg < 0)
+ return reg;
+
+ switch (reg & 0x0f) {
+ case 0x06:
+ return 2; /* is child node (connected to parent node) */
+ case 0x0e:
+ return 3; /* is parent node (connected to child node) */
+ }
+ return 1; /* not connected */
+}
+
+static int get_self_id_pos(struct fw_ohci *ohci, u32 self_id,
+ int self_id_count)
{
- struct fw_ohci *ohci = (struct fw_ohci *)data;
+ int i;
+ u32 entry;
+
+ for (i = 0; i < self_id_count; i++) {
+ entry = ohci->self_id_buffer[i];
+ if ((self_id & 0xff000000) == (entry & 0xff000000))
+ return -1;
+ if ((self_id & 0xff000000) < (entry & 0xff000000))
+ return i;
+ }
+ return i;
+}
+
+/*
+ * TI TSB82AA2B and TSB12LV26 do not receive the selfID of a locally
+ * attached TSB41BA3D phy; see http://www.ti.com/litv/pdf/sllz059.
+ * Construct the selfID from phy register contents.
+ * FIXME: How to determine the selfID.i flag?
+ */
+static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count)
+{
+ int reg, i, pos, status;
+ /* link active 1, speed 3, bridge 0, contender 1, more packets 0 */
+ u32 self_id = 0x8040c800;
+
+ reg = reg_read(ohci, OHCI1394_NodeID);
+ if (!(reg & OHCI1394_NodeID_idValid)) {
+ fw_notify("node ID not valid, new bus reset in progress\n");
+ return -EBUSY;
+ }
+ self_id |= ((reg & 0x3f) << 24); /* phy ID */
+
+ reg = ohci_read_phy_reg(&ohci->card, 4);
+ if (reg < 0)
+ return reg;
+ self_id |= ((reg & 0x07) << 8); /* power class */
+
+ reg = ohci_read_phy_reg(&ohci->card, 1);
+ if (reg < 0)
+ return reg;
+ self_id |= ((reg & 0x3f) << 16); /* gap count */
+
+ for (i = 0; i < 3; i++) {
+ status = get_status_for_port(ohci, i);
+ if (status < 0)
+ return status;
+ self_id |= ((status & 0x3) << (6 - (i * 2)));
+ }
+
+ pos = get_self_id_pos(ohci, self_id, self_id_count);
+ if (pos >= 0) {
+ memmove(&(ohci->self_id_buffer[pos+1]),
+ &(ohci->self_id_buffer[pos]),
+ (self_id_count - pos) * sizeof(*ohci->self_id_buffer));
+ ohci->self_id_buffer[pos] = self_id;
+ self_id_count++;
+ }
+ return self_id_count;
+}
+
+static void bus_reset_work(struct work_struct *work)
+{
+ struct fw_ohci *ohci =
+ container_of(work, struct fw_ohci, bus_reset_work);
int self_id_count, i, j, reg;
int generation, new_generation;
unsigned long flags;
@@ -1743,21 +1851,50 @@ static void bus_reset_tasklet(unsigned long data)
* bit extra to get the actual number of self IDs.
*/
self_id_count = (reg >> 3) & 0xff;
- if (self_id_count == 0 || self_id_count > 252) {
+
+ if (self_id_count > 252) {
fw_notify("inconsistent self IDs\n");
return;
}
+
generation = (cond_le32_to_cpu(ohci->self_id_cpu[0]) >> 16) & 0xff;
rmb();
for (i = 1, j = 0; j < self_id_count; i += 2, j++) {
if (ohci->self_id_cpu[i] != ~ohci->self_id_cpu[i + 1]) {
- fw_notify("inconsistent self IDs\n");
- return;
+ /*
+ * If the invalid data looks like a cycle start packet,
+ * it's likely to be the result of the cycle master
+ * having a wrong gap count. In this case, the self IDs
+ * so far are valid and should be processed so that the
+ * bus manager can then correct the gap count.
+ */
+ if (cond_le32_to_cpu(ohci->self_id_cpu[i])
+ == 0xffff008f) {
+ fw_notify("ignoring spurious self IDs\n");
+ self_id_count = j;
+ break;
+ } else {
+ fw_notify("inconsistent self IDs\n");
+ return;
+ }
}
ohci->self_id_buffer[j] =
cond_le32_to_cpu(ohci->self_id_cpu[i]);
}
+
+ if (ohci->quirks & QUIRK_TI_SLLZ059) {
+ self_id_count = find_and_insert_self_id(ohci, self_id_count);
+ if (self_id_count < 0) {
+ fw_notify("could not construct local self ID\n");
+ return;
+ }
+ }
+
+ if (self_id_count == 0) {
+ fw_notify("inconsistent self IDs\n");
+ return;
+ }
rmb();
/*
@@ -1877,7 +2014,7 @@ static irqreturn_t irq_handler(int irq, void *data)
log_irqs(event);
if (event & OHCI1394_selfIDComplete)
- tasklet_schedule(&ohci->bus_reset_tasklet);
+ queue_work(fw_workqueue, &ohci->bus_reset_work);
if (event & OHCI1394_RQPkt)
tasklet_schedule(&ohci->ar_request_ctx.tasklet);
@@ -1924,7 +2061,8 @@ static irqreturn_t irq_handler(int irq, void *data)
reg_read(ohci, OHCI1394_PostedWriteAddressLo);
reg_write(ohci, OHCI1394_IntEventClear,
OHCI1394_postedWriteErr);
- fw_error("PCI posted write error\n");
+ if (printk_ratelimit())
+ fw_error("PCI posted write error\n");
}
if (unlikely(event & OHCI1394_cycleTooLong)) {
@@ -1960,14 +2098,18 @@ static irqreturn_t irq_handler(int irq, void *data)
static int software_reset(struct fw_ohci *ohci)
{
+ u32 val;
int i;
reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);
+ for (i = 0; i < 500; i++) {
+ val = reg_read(ohci, OHCI1394_HCControlSet);
+ if (!~val)
+ return -ENODEV; /* Card was ejected. */
- for (i = 0; i < OHCI_LOOP_COUNT; i++) {
- if ((reg_read(ohci, OHCI1394_HCControlSet) &
- OHCI1394_HCControl_softReset) == 0)
+ if (!(val & OHCI1394_HCControl_softReset))
return 0;
+
msleep(1);
}
@@ -2034,6 +2176,28 @@ static int configure_1394a_enhancements(struct fw_ohci *ohci)
return 0;
}
+static int probe_tsb41ba3d(struct fw_ohci *ohci)
+{
+ /* TI vendor ID = 0x080028, TSB41BA3D product ID = 0x833005 (sic) */
+ static const u8 id[] = { 0x08, 0x00, 0x28, 0x83, 0x30, 0x05, };
+ int reg, i;
+
+ reg = read_phy_reg(ohci, 2);
+ if (reg < 0)
+ return reg;
+ if ((reg & PHY_EXTENDED_REGISTERS) != PHY_EXTENDED_REGISTERS)
+ return 0;
+
+ for (i = ARRAY_SIZE(id) - 1; i >= 0; i--) {
+ reg = read_paged_phy_reg(ohci, 1, i + 10);
+ if (reg < 0)
+ return reg;
+ if (reg != id[i])
+ return 0;
+ }
+ return 1;
+}
+
static int ohci_enable(struct fw_card *card,
const __be32 *config_rom, size_t length)
{
@@ -2071,6 +2235,16 @@ static int ohci_enable(struct fw_card *card,
return -EIO;
}
+ if (ohci->quirks & QUIRK_TI_SLLZ059) {
+ ret = probe_tsb41ba3d(ohci);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ fw_notify("local TSB41BA3D phy\n");
+ else
+ ohci->quirks &= ~QUIRK_TI_SLLZ059;
+ }
+
reg_write(ohci, OHCI1394_HCControlClear,
OHCI1394_HCControl_noByteSwapData);
@@ -2168,8 +2342,13 @@ static int ohci_enable(struct fw_card *card,
ohci_driver_name, ohci)) {
fw_error("Failed to allocate interrupt %d.\n", dev->irq);
pci_disable_msi(dev);
- dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
- ohci->config_rom, ohci->config_rom_bus);
+
+ if (config_rom) {
+ dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ ohci->next_config_rom,
+ ohci->next_config_rom_bus);
+ ohci->next_config_rom = NULL;
+ }
return -EIO;
}
@@ -2197,7 +2376,9 @@ static int ohci_enable(struct fw_card *card,
OHCI1394_LinkControl_rcvPhyPkt);
ar_context_run(&ohci->ar_request_ctx);
- ar_context_run(&ohci->ar_response_ctx); /* also flushes writes */
+ ar_context_run(&ohci->ar_response_ctx);
+
+ flush_writes(ohci);
/* We are ready to go, reset bus to finish initialization. */
fw_schedule_bus_reset(&ohci->card, false, true);
@@ -2239,7 +2420,7 @@ static int ohci_set_config_rom(struct fw_card *card,
* then set up the real values for the two registers.
*
* We use ohci->lock to avoid racing with the code that sets
- * ohci->next_config_rom to NULL (see bus_reset_tasklet).
+ * ohci->next_config_rom to NULL (see bus_reset_work).
*/
next_config_rom =
@@ -2518,6 +2699,7 @@ static int handle_ir_packet_per_buffer(struct context *context,
struct iso_context *ctx =
container_of(context, struct iso_context, context);
struct descriptor *pd;
+ u32 buffer_dma;
__le32 *ir_header;
void *p;
@@ -2528,6 +2710,16 @@ static int handle_ir_packet_per_buffer(struct context *context,
/* Descriptor(s) not done yet, stop iteration */
return 0;
+ while (!(d->control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS))) {
+ d++;
+ buffer_dma = le32_to_cpu(d->data_address);
+ dma_sync_single_range_for_cpu(context->ohci->card.device,
+ buffer_dma & PAGE_MASK,
+ buffer_dma & ~PAGE_MASK,
+ le16_to_cpu(d->req_count),
+ DMA_FROM_DEVICE);
+ }
+
p = last + 1;
copy_iso_headers(ctx, p);
@@ -2550,11 +2742,19 @@ static int handle_ir_buffer_fill(struct context *context,
{
struct iso_context *ctx =
container_of(context, struct iso_context, context);
+ u32 buffer_dma;
if (!last->transfer_status)
/* Descriptor(s) not done yet, stop iteration */
return 0;
+ buffer_dma = le32_to_cpu(last->data_address);
+ dma_sync_single_range_for_cpu(context->ohci->card.device,
+ buffer_dma & PAGE_MASK,
+ buffer_dma & ~PAGE_MASK,
+ le16_to_cpu(last->req_count),
+ DMA_FROM_DEVICE);
+
if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS)
ctx->base.callback.mc(&ctx->base,
le32_to_cpu(last->data_address) +
@@ -2565,6 +2765,43 @@ static int handle_ir_buffer_fill(struct context *context,
return 1;
}
+static inline void sync_it_packet_for_cpu(struct context *context,
+ struct descriptor *pd)
+{
+ __le16 control;
+ u32 buffer_dma;
+
+ /* only packets beginning with OUTPUT_MORE* have data buffers */
+ if (pd->control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS))
+ return;
+
+ /* skip over the OUTPUT_MORE_IMMEDIATE descriptor */
+ pd += 2;
+
+ /*
+ * If the packet has a header, the first OUTPUT_MORE/LAST descriptor's
+ * data buffer is in the context program's coherent page and must not
+ * be synced.
+ */
+ if ((le32_to_cpu(pd->data_address) & PAGE_MASK) ==
+ (context->current_bus & PAGE_MASK)) {
+ if (pd->control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS))
+ return;
+ pd++;
+ }
+
+ do {
+ buffer_dma = le32_to_cpu(pd->data_address);
+ dma_sync_single_range_for_cpu(context->ohci->card.device,
+ buffer_dma & PAGE_MASK,
+ buffer_dma & ~PAGE_MASK,
+ le16_to_cpu(pd->req_count),
+ DMA_TO_DEVICE);
+ control = pd->control;
+ pd++;
+ } while (!(control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS)));
+}
+
static int handle_it_packet(struct context *context,
struct descriptor *d,
struct descriptor *last)
@@ -2581,6 +2818,8 @@ static int handle_it_packet(struct context *context,
/* Descriptor(s) not done yet, stop iteration */
return 0;
+ sync_it_packet_for_cpu(context, d);
+
i = ctx->header_length;
if (i + 4 < PAGE_SIZE) {
/* Present this value as big-endian to match the receive code */
@@ -2950,6 +3189,10 @@ static int queue_iso_transmit(struct iso_context *ctx,
page_bus = page_private(buffer->pages[page]);
pd[i].data_address = cpu_to_le32(page_bus + offset);
+ dma_sync_single_range_for_device(ctx->context.ohci->card.device,
+ page_bus, offset, length,
+ DMA_TO_DEVICE);
+
payload_index += length;
}
@@ -2974,6 +3217,7 @@ static int queue_iso_packet_per_buffer(struct iso_context *ctx,
struct fw_iso_buffer *buffer,
unsigned long payload)
{
+ struct device *device = ctx->context.ohci->card.device;
struct descriptor *d, *pd;
dma_addr_t d_bus, page_bus;
u32 z, header_z, rest;
@@ -3028,6 +3272,10 @@ static int queue_iso_packet_per_buffer(struct iso_context *ctx,
page_bus = page_private(buffer->pages[page]);
pd->data_address = cpu_to_le32(page_bus + offset);
+ dma_sync_single_range_for_device(device, page_bus,
+ offset, length,
+ DMA_FROM_DEVICE);
+
offset = (offset + length) & ~PAGE_MASK;
rest -= length;
if (offset == 0)
@@ -3087,6 +3335,10 @@ static int queue_iso_buffer_fill(struct iso_context *ctx,
page_bus = page_private(buffer->pages[page]);
d->data_address = cpu_to_le32(page_bus + offset);
+ dma_sync_single_range_for_device(ctx->context.ohci->card.device,
+ page_bus, offset, length,
+ DMA_FROM_DEVICE);
+
rest -= length;
offset = 0;
page++;
@@ -3129,7 +3381,6 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base)
&container_of(base, struct iso_context, base)->context;
reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
- flush_writes(ctx->ohci);
}
static const struct fw_card_driver ohci_driver = {
@@ -3219,8 +3470,7 @@ static int __devinit pci_probe(struct pci_dev *dev,
spin_lock_init(&ohci->lock);
mutex_init(&ohci->phy_reg_mutex);
- tasklet_init(&ohci->bus_reset_tasklet,
- bus_reset_tasklet, (unsigned long)ohci);
+ INIT_WORK(&ohci->bus_reset_work, bus_reset_work);
err = pci_request_region(dev, 0, ohci_driver_name);
if (err) {
@@ -3362,6 +3612,7 @@ static void pci_remove(struct pci_dev *dev)
ohci = pci_get_drvdata(dev);
reg_write(ohci, OHCI1394_IntMaskClear, ~0);
flush_writes(ohci);
+ cancel_work_sync(&ohci->bus_reset_work);
fw_core_remove_card(&ohci->card);
/*
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c
index 41841a3e3f99..68375bc3aef6 100644
--- a/drivers/firewire/sbp2.c
+++ b/drivers/firewire/sbp2.c
@@ -154,12 +154,16 @@ struct sbp2_logical_unit {
bool blocked;
};
+static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay)
+{
+ queue_delayed_work(fw_workqueue, &lu->work, delay);
+}
+
/*
* We create one struct sbp2_target per IEEE 1212 Unit Directory
* and one struct Scsi_Host per sbp2_target.
*/
struct sbp2_target {
- struct kref kref;
struct fw_unit *unit;
const char *bus_id;
struct list_head lu_list;
@@ -772,71 +776,6 @@ static int sbp2_lun2int(u16 lun)
return scsilun_to_int(&eight_bytes_lun);
}
-static void sbp2_release_target(struct kref *kref)
-{
- struct sbp2_target *tgt = container_of(kref, struct sbp2_target, kref);
- struct sbp2_logical_unit *lu, *next;
- struct Scsi_Host *shost =
- container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
- struct scsi_device *sdev;
- struct fw_device *device = target_device(tgt);
-
- /* prevent deadlocks */
- sbp2_unblock(tgt);
-
- list_for_each_entry_safe(lu, next, &tgt->lu_list, link) {
- sdev = scsi_device_lookup(shost, 0, 0, sbp2_lun2int(lu->lun));
- if (sdev) {
- scsi_remove_device(sdev);
- scsi_device_put(sdev);
- }
- if (lu->login_id != INVALID_LOGIN_ID) {
- int generation, node_id;
- /*
- * tgt->node_id may be obsolete here if we failed
- * during initial login or after a bus reset where
- * the topology changed.
- */
- generation = device->generation;
- smp_rmb(); /* node_id vs. generation */
- node_id = device->node_id;
- sbp2_send_management_orb(lu, node_id, generation,
- SBP2_LOGOUT_REQUEST,
- lu->login_id, NULL);
- }
- fw_core_remove_address_handler(&lu->address_handler);
- list_del(&lu->link);
- kfree(lu);
- }
- scsi_remove_host(shost);
- fw_notify("released %s, target %d:0:0\n", tgt->bus_id, shost->host_no);
-
- fw_unit_put(tgt->unit);
- scsi_host_put(shost);
- fw_device_put(device);
-}
-
-static void sbp2_target_get(struct sbp2_target *tgt)
-{
- kref_get(&tgt->kref);
-}
-
-static void sbp2_target_put(struct sbp2_target *tgt)
-{
- kref_put(&tgt->kref, sbp2_release_target);
-}
-
-/*
- * Always get the target's kref when scheduling work on one its units.
- * Each workqueue job is responsible to call sbp2_target_put() upon return.
- */
-static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay)
-{
- sbp2_target_get(lu->tgt);
- if (!queue_delayed_work(fw_workqueue, &lu->work, delay))
- sbp2_target_put(lu->tgt);
-}
-
/*
* Write retransmit retry values into the BUSY_TIMEOUT register.
* - The single-phase retry protocol is supported by all SBP-2 devices, but the
@@ -877,7 +816,7 @@ static void sbp2_login(struct work_struct *work)
int generation, node_id, local_node_id;
if (fw_device_is_shutdown(device))
- goto out;
+ return;
generation = device->generation;
smp_rmb(); /* node IDs must not be older than generation */
@@ -899,7 +838,7 @@ static void sbp2_login(struct work_struct *work)
/* Let any waiting I/O fail from now on. */
sbp2_unblock(lu->tgt);
}
- goto out;
+ return;
}
tgt->node_id = node_id;
@@ -925,7 +864,8 @@ static void sbp2_login(struct work_struct *work)
if (lu->has_sdev) {
sbp2_cancel_orbs(lu);
sbp2_conditionally_unblock(lu);
- goto out;
+
+ return;
}
if (lu->tgt->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY)
@@ -957,7 +897,8 @@ static void sbp2_login(struct work_struct *work)
lu->has_sdev = true;
scsi_device_put(sdev);
sbp2_allow_block(lu);
- goto out;
+
+ return;
out_logout_login:
smp_rmb(); /* generation may have changed */
@@ -971,8 +912,57 @@ static void sbp2_login(struct work_struct *work)
* lu->work already. Reset the work from reconnect to login.
*/
PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
- out:
- sbp2_target_put(tgt);
+}
+
+static void sbp2_reconnect(struct work_struct *work)
+{
+ struct sbp2_logical_unit *lu =
+ container_of(work, struct sbp2_logical_unit, work.work);
+ struct sbp2_target *tgt = lu->tgt;
+ struct fw_device *device = target_device(tgt);
+ int generation, node_id, local_node_id;
+
+ if (fw_device_is_shutdown(device))
+ return;
+
+ generation = device->generation;
+ smp_rmb(); /* node IDs must not be older than generation */
+ node_id = device->node_id;
+ local_node_id = device->card->node_id;
+
+ if (sbp2_send_management_orb(lu, node_id, generation,
+ SBP2_RECONNECT_REQUEST,
+ lu->login_id, NULL) < 0) {
+ /*
+ * If reconnect was impossible even though we are in the
+ * current generation, fall back and try to log in again.
+ *
+ * We could check for "Function rejected" status, but
+ * looking at the bus generation as simpler and more general.
+ */
+ smp_rmb(); /* get current card generation */
+ if (generation == device->card->generation ||
+ lu->retries++ >= 5) {
+ fw_error("%s: failed to reconnect\n", tgt->bus_id);
+ lu->retries = 0;
+ PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
+ }
+ sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
+
+ return;
+ }
+
+ tgt->node_id = node_id;
+ tgt->address_high = local_node_id << 16;
+ smp_wmb(); /* node IDs must not be older than generation */
+ lu->generation = generation;
+
+ fw_notify("%s: reconnected to LUN %04x (%d retries)\n",
+ tgt->bus_id, lu->lun, lu->retries);
+
+ sbp2_agent_reset(lu);
+ sbp2_cancel_orbs(lu);
+ sbp2_conditionally_unblock(lu);
}
static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry)
@@ -1120,6 +1110,7 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model,
}
static struct scsi_host_template scsi_driver_template;
+static int sbp2_remove(struct device *dev);
static int sbp2_probe(struct device *dev)
{
@@ -1141,7 +1132,6 @@ static int sbp2_probe(struct device *dev)
tgt = (struct sbp2_target *)shost->hostdata;
dev_set_drvdata(&unit->device, tgt);
tgt->unit = unit;
- kref_init(&tgt->kref);
INIT_LIST_HEAD(&tgt->lu_list);
tgt->bus_id = dev_name(&unit->device);
tgt->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
@@ -1154,9 +1144,6 @@ static int sbp2_probe(struct device *dev)
if (scsi_add_host(shost, &unit->device) < 0)
goto fail_shost_put;
- fw_device_get(device);
- fw_unit_get(unit);
-
/* implicit directory ID */
tgt->directory_id = ((unit->directory - device->config_rom) * 4
+ CSR_CONFIG_ROM) & 0xffffff;
@@ -1166,7 +1153,7 @@ static int sbp2_probe(struct device *dev)
if (sbp2_scan_unit_dir(tgt, unit->directory, &model,
&firmware_revision) < 0)
- goto fail_tgt_put;
+ goto fail_remove;
sbp2_clamp_management_orb_timeout(tgt);
sbp2_init_workarounds(tgt, model, firmware_revision);
@@ -1177,16 +1164,17 @@ static int sbp2_probe(struct device *dev)
* specifies the max payload size as 2 ^ (max_payload + 2), so
* if we set this to max_speed + 7, we get the right value.
*/
- tgt->max_payload = min(device->max_speed + 7, 10U);
- tgt->max_payload = min(tgt->max_payload, device->card->max_receive - 1);
+ tgt->max_payload = min3(device->max_speed + 7, 10U,
+ device->card->max_receive - 1);
/* Do the login in a workqueue so we can easily reschedule retries. */
list_for_each_entry(lu, &tgt->lu_list, link)
sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
+
return 0;
- fail_tgt_put:
- sbp2_target_put(tgt);
+ fail_remove:
+ sbp2_remove(dev);
return -ENOMEM;
fail_shost_put:
@@ -1194,67 +1182,6 @@ static int sbp2_probe(struct device *dev)
return -ENOMEM;
}
-static int sbp2_remove(struct device *dev)
-{
- struct fw_unit *unit = fw_unit(dev);
- struct sbp2_target *tgt = dev_get_drvdata(&unit->device);
-
- sbp2_target_put(tgt);
- return 0;
-}
-
-static void sbp2_reconnect(struct work_struct *work)
-{
- struct sbp2_logical_unit *lu =
- container_of(work, struct sbp2_logical_unit, work.work);
- struct sbp2_target *tgt = lu->tgt;
- struct fw_device *device = target_device(tgt);
- int generation, node_id, local_node_id;
-
- if (fw_device_is_shutdown(device))
- goto out;
-
- generation = device->generation;
- smp_rmb(); /* node IDs must not be older than generation */
- node_id = device->node_id;
- local_node_id = device->card->node_id;
-
- if (sbp2_send_management_orb(lu, node_id, generation,
- SBP2_RECONNECT_REQUEST,
- lu->login_id, NULL) < 0) {
- /*
- * If reconnect was impossible even though we are in the
- * current generation, fall back and try to log in again.
- *
- * We could check for "Function rejected" status, but
- * looking at the bus generation as simpler and more general.
- */
- smp_rmb(); /* get current card generation */
- if (generation == device->card->generation ||
- lu->retries++ >= 5) {
- fw_error("%s: failed to reconnect\n", tgt->bus_id);
- lu->retries = 0;
- PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
- }
- sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
- goto out;
- }
-
- tgt->node_id = node_id;
- tgt->address_high = local_node_id << 16;
- smp_wmb(); /* node IDs must not be older than generation */
- lu->generation = generation;
-
- fw_notify("%s: reconnected to LUN %04x (%d retries)\n",
- tgt->bus_id, lu->lun, lu->retries);
-
- sbp2_agent_reset(lu);
- sbp2_cancel_orbs(lu);
- sbp2_conditionally_unblock(lu);
- out:
- sbp2_target_put(tgt);
-}
-
static void sbp2_update(struct fw_unit *unit)
{
struct sbp2_target *tgt = dev_get_drvdata(&unit->device);
@@ -1273,6 +1200,51 @@ static void sbp2_update(struct fw_unit *unit)
}
}
+static int sbp2_remove(struct device *dev)
+{
+ struct fw_unit *unit = fw_unit(dev);
+ struct fw_device *device = fw_parent_device(unit);
+ struct sbp2_target *tgt = dev_get_drvdata(&unit->device);
+ struct sbp2_logical_unit *lu, *next;
+ struct Scsi_Host *shost =
+ container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
+ struct scsi_device *sdev;
+
+ /* prevent deadlocks */
+ sbp2_unblock(tgt);
+
+ list_for_each_entry_safe(lu, next, &tgt->lu_list, link) {
+ cancel_delayed_work_sync(&lu->work);
+ sdev = scsi_device_lookup(shost, 0, 0, sbp2_lun2int(lu->lun));
+ if (sdev) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ }
+ if (lu->login_id != INVALID_LOGIN_ID) {
+ int generation, node_id;
+ /*
+ * tgt->node_id may be obsolete here if we failed
+ * during initial login or after a bus reset where
+ * the topology changed.
+ */
+ generation = device->generation;
+ smp_rmb(); /* node_id vs. generation */
+ node_id = device->node_id;
+ sbp2_send_management_orb(lu, node_id, generation,
+ SBP2_LOGOUT_REQUEST,
+ lu->login_id, NULL);
+ }
+ fw_core_remove_address_handler(&lu->address_handler);
+ list_del(&lu->link);
+ kfree(lu);
+ }
+ scsi_remove_host(shost);
+ fw_notify("released %s, target %d:0:0\n", tgt->bus_id, shost->host_no);
+
+ scsi_host_put(shost);
+ return 0;
+}
+
#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e
#define SBP2_SW_VERSION_ENTRY 0x00010483
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index bcb1126e3d00..153980be4ee6 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -585,14 +585,12 @@ int dmi_name_in_serial(const char *str)
}
/**
- * dmi_name_in_vendors - Check if string is anywhere in the DMI vendor information.
+ * dmi_name_in_vendors - Check if string is in the DMI system or board vendor name
* @str: Case sensitive Name
*/
int dmi_name_in_vendors(const char *str)
{
- static int fields[] = { DMI_BIOS_VENDOR, DMI_BIOS_VERSION, DMI_SYS_VENDOR,
- DMI_PRODUCT_NAME, DMI_PRODUCT_VERSION, DMI_BOARD_VENDOR,
- DMI_BOARD_NAME, DMI_BOARD_VERSION, DMI_NONE };
+ static int fields[] = { DMI_SYS_VENDOR, DMI_BOARD_VENDOR, DMI_NONE };
int i;
for (i = 0; fields[i] != DMI_NONE; i++) {
int f = fields[i];
diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c
index f1b7f659d3c9..e22957665808 100644
--- a/drivers/firmware/edd.c
+++ b/drivers/firmware/edd.c
@@ -151,7 +151,8 @@ edd_show_host_bus(struct edd_device *edev, char *buf)
p += scnprintf(p, left, "\tbase_address: %x\n",
info->params.interface_path.isa.base_address);
} else if (!strncmp(info->params.host_bus_type, "PCIX", 4) ||
- !strncmp(info->params.host_bus_type, "PCI", 3)) {
+ !strncmp(info->params.host_bus_type, "PCI", 3) ||
+ !strncmp(info->params.host_bus_type, "XPRS", 4)) {
p += scnprintf(p, left,
"\t%02x:%02x.%d channel: %u\n",
info->params.interface_path.pci.bus,
@@ -159,7 +160,6 @@ edd_show_host_bus(struct edd_device *edev, char *buf)
info->params.interface_path.pci.function,
info->params.interface_path.pci.channel);
} else if (!strncmp(info->params.host_bus_type, "IBND", 4) ||
- !strncmp(info->params.host_bus_type, "XPRS", 4) ||
!strncmp(info->params.host_bus_type, "HTPT", 4)) {
p += scnprintf(p, left,
"\tTBD: %llx\n",
@@ -668,7 +668,7 @@ edd_get_pci_dev(struct edd_device *edev)
{
struct edd_info *info = edd_dev_get_info(edev);
- if (edd_dev_is_type(edev, "PCI")) {
+ if (edd_dev_is_type(edev, "PCI") || edd_dev_is_type(edev, "XPRS")) {
return pci_get_bus_and_slot(info->params.interface_path.pci.bus,
PCI_DEVFN(info->params.interface_path.pci.slot,
info->params.interface_path.pci.
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 5f29aafd4462..8370f72d87ff 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -78,6 +78,7 @@
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/slab.h>
+#include <linux/pstore.h>
#include <asm/uaccess.h>
@@ -89,6 +90,8 @@ MODULE_DESCRIPTION("sysfs interface to EFI Variables");
MODULE_LICENSE("GPL");
MODULE_VERSION(EFIVARS_VERSION);
+#define DUMP_NAME_LEN 52
+
/*
* The maximum size of VariableName + Data = 1024
* Therefore, it's reasonable to save that much
@@ -119,6 +122,10 @@ struct efivar_attribute {
ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
};
+#define PSTORE_EFI_ATTRIBUTES \
+ (EFI_VARIABLE_NON_VOLATILE | \
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | \
+ EFI_VARIABLE_RUNTIME_ACCESS)
#define EFIVAR_ATTR(_name, _mode, _show, _store) \
struct efivar_attribute efivar_attr_##_name = { \
@@ -141,38 +148,72 @@ efivar_create_sysfs_entry(struct efivars *efivars,
/* Return the number of unicode characters in data */
static unsigned long
-utf8_strlen(efi_char16_t *data, unsigned long maxlength)
+utf16_strnlen(efi_char16_t *s, size_t maxlength)
{
unsigned long length = 0;
- while (*data++ != 0 && length < maxlength)
+ while (*s++ != 0 && length < maxlength)
length++;
return length;
}
+static inline unsigned long
+utf16_strlen(efi_char16_t *s)
+{
+ return utf16_strnlen(s, ~0UL);
+}
+
/*
* Return the number of bytes is the length of this string
* Note: this is NOT the same as the number of unicode characters
*/
static inline unsigned long
-utf8_strsize(efi_char16_t *data, unsigned long maxlength)
+utf16_strsize(efi_char16_t *data, unsigned long maxlength)
{
- return utf8_strlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
+ return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
+}
+
+static inline int
+utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len)
+{
+ while (1) {
+ if (len == 0)
+ return 0;
+ if (*a < *b)
+ return -1;
+ if (*a > *b)
+ return 1;
+ if (*a == 0) /* implies *b == 0 */
+ return 0;
+ a++;
+ b++;
+ len--;
+ }
}
static efi_status_t
-get_var_data(struct efivars *efivars, struct efi_variable *var)
+get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
{
efi_status_t status;
- spin_lock(&efivars->lock);
var->DataSize = 1024;
status = efivars->ops->get_variable(var->VariableName,
&var->VendorGuid,
&var->Attributes,
&var->DataSize,
var->Data);
+ return status;
+}
+
+static efi_status_t
+get_var_data(struct efivars *efivars, struct efi_variable *var)
+{
+ efi_status_t status;
+
+ spin_lock(&efivars->lock);
+ status = get_var_data_locked(efivars, var);
spin_unlock(&efivars->lock);
+
if (status != EFI_SUCCESS) {
printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
status);
@@ -387,12 +428,181 @@ static struct kobj_type efivar_ktype = {
.default_attrs = def_attrs,
};
+static struct pstore_info efi_pstore_info;
+
static inline void
efivar_unregister(struct efivar_entry *var)
{
kobject_put(&var->kobj);
}
+#ifdef CONFIG_PSTORE
+
+static int efi_pstore_open(struct pstore_info *psi)
+{
+ struct efivars *efivars = psi->data;
+
+ spin_lock(&efivars->lock);
+ efivars->walk_entry = list_first_entry(&efivars->list,
+ struct efivar_entry, list);
+ return 0;
+}
+
+static int efi_pstore_close(struct pstore_info *psi)
+{
+ struct efivars *efivars = psi->data;
+
+ spin_unlock(&efivars->lock);
+ return 0;
+}
+
+static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
+ struct timespec *timespec, struct pstore_info *psi)
+{
+ efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
+ struct efivars *efivars = psi->data;
+ char name[DUMP_NAME_LEN];
+ int i;
+ unsigned int part, size;
+ unsigned long time;
+
+ while (&efivars->walk_entry->list != &efivars->list) {
+ if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid,
+ vendor)) {
+ for (i = 0; i < DUMP_NAME_LEN; i++) {
+ name[i] = efivars->walk_entry->var.VariableName[i];
+ }
+ if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) {
+ *id = part;
+ timespec->tv_sec = time;
+ timespec->tv_nsec = 0;
+ get_var_data_locked(efivars, &efivars->walk_entry->var);
+ size = efivars->walk_entry->var.DataSize;
+ memcpy(psi->buf, efivars->walk_entry->var.Data, size);
+ efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
+ struct efivar_entry, list);
+ return size;
+ }
+ }
+ efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
+ struct efivar_entry, list);
+ }
+ return 0;
+}
+
+static int efi_pstore_write(enum pstore_type_id type, u64 *id,
+ unsigned int part, size_t size, struct pstore_info *psi)
+{
+ char name[DUMP_NAME_LEN];
+ char stub_name[DUMP_NAME_LEN];
+ efi_char16_t efi_name[DUMP_NAME_LEN];
+ efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
+ struct efivars *efivars = psi->data;
+ struct efivar_entry *entry, *found = NULL;
+ int i, ret = 0;
+
+ sprintf(stub_name, "dump-type%u-%u-", type, part);
+ sprintf(name, "%s%lu", stub_name, get_seconds());
+
+ spin_lock(&efivars->lock);
+
+ for (i = 0; i < DUMP_NAME_LEN; i++)
+ efi_name[i] = stub_name[i];
+
+ /*
+ * Clean up any entries with the same name
+ */
+
+ list_for_each_entry(entry, &efivars->list, list) {
+ get_var_data_locked(efivars, &entry->var);
+
+ if (efi_guidcmp(entry->var.VendorGuid, vendor))
+ continue;
+ if (utf16_strncmp(entry->var.VariableName, efi_name,
+ utf16_strlen(efi_name)))
+ continue;
+ /* Needs to be a prefix */
+ if (entry->var.VariableName[utf16_strlen(efi_name)] == 0)
+ continue;
+
+ /* found */
+ found = entry;
+ efivars->ops->set_variable(entry->var.VariableName,
+ &entry->var.VendorGuid,
+ PSTORE_EFI_ATTRIBUTES,
+ 0, NULL);
+ }
+
+ if (found)
+ list_del(&found->list);
+
+ for (i = 0; i < DUMP_NAME_LEN; i++)
+ efi_name[i] = name[i];
+
+ efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
+ size, psi->buf);
+
+ spin_unlock(&efivars->lock);
+
+ if (found)
+ efivar_unregister(found);
+
+ if (size)
+ ret = efivar_create_sysfs_entry(efivars,
+ utf16_strsize(efi_name,
+ DUMP_NAME_LEN * 2),
+ efi_name, &vendor);
+
+ *id = part;
+ return ret;
+};
+
+static int efi_pstore_erase(enum pstore_type_id type, u64 id,
+ struct pstore_info *psi)
+{
+ efi_pstore_write(type, &id, (unsigned int)id, 0, psi);
+
+ return 0;
+}
+#else
+static int efi_pstore_open(struct pstore_info *psi)
+{
+ return 0;
+}
+
+static int efi_pstore_close(struct pstore_info *psi)
+{
+ return 0;
+}
+
+static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
+ struct timespec *time, struct pstore_info *psi)
+{
+ return -1;
+}
+
+static int efi_pstore_write(enum pstore_type_id type, u64 *id,
+ unsigned int part, size_t size, struct pstore_info *psi)
+{
+ return 0;
+}
+
+static int efi_pstore_erase(enum pstore_type_id type, u64 id,
+ struct pstore_info *psi)
+{
+ return 0;
+}
+#endif
+
+static struct pstore_info efi_pstore_info = {
+ .owner = THIS_MODULE,
+ .name = "efi",
+ .open = efi_pstore_open,
+ .close = efi_pstore_close,
+ .read = efi_pstore_read,
+ .write = efi_pstore_write,
+ .erase = efi_pstore_erase,
+};
static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
@@ -414,8 +624,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
* Does this variable already exist?
*/
list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
- strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
- strsize2 = utf8_strsize(new_var->VariableName, 1024);
+ strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
+ strsize2 = utf16_strsize(new_var->VariableName, 1024);
if (strsize1 == strsize2 &&
!memcmp(&(search_efivar->var.VariableName),
new_var->VariableName, strsize1) &&
@@ -447,8 +657,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
/* Create the entry in sysfs. Locking is not required here */
status = efivar_create_sysfs_entry(efivars,
- utf8_strsize(new_var->VariableName,
- 1024),
+ utf16_strsize(new_var->VariableName,
+ 1024),
new_var->VariableName,
&new_var->VendorGuid);
if (status) {
@@ -477,8 +687,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
* Does this variable already exist?
*/
list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
- strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
- strsize2 = utf8_strsize(del_var->VariableName, 1024);
+ strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
+ strsize2 = utf16_strsize(del_var->VariableName, 1024);
if (strsize1 == strsize2 &&
!memcmp(&(search_efivar->var.VariableName),
del_var->VariableName, strsize1) &&
@@ -763,6 +973,16 @@ int register_efivars(struct efivars *efivars,
if (error)
unregister_efivars(efivars);
+ efivars->efi_pstore_info = efi_pstore_info;
+
+ efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
+ if (efivars->efi_pstore_info.buf) {
+ efivars->efi_pstore_info.bufsize = 1024;
+ efivars->efi_pstore_info.data = efivars;
+ spin_lock_init(&efivars->efi_pstore_info.buf_lock);
+ pstore_register(&efivars->efi_pstore_info);
+ }
+
out:
kfree(variable_name);
diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c
index fa7f0b3e81dd..c4e7c59d1c63 100644
--- a/drivers/firmware/google/gsmi.c
+++ b/drivers/firmware/google/gsmi.c
@@ -27,6 +27,7 @@
#include <linux/kdebug.h>
#include <linux/reboot.h>
#include <linux/efi.h>
+#include <linux/module.h>
#define GSMI_SHUTDOWN_CLEAN 0 /* Clean Shutdown */
/* TODO(mikew@google.com): Tie in HARDLOCKUP_DETECTOR with NMIWDT */
@@ -420,7 +421,7 @@ static efi_status_t gsmi_get_next_variable(unsigned long *name_size,
static efi_status_t gsmi_set_variable(efi_char16_t *name,
efi_guid_t *vendor,
- unsigned long attr,
+ u32 attr,
unsigned long data_size,
void *data)
{
@@ -869,8 +870,6 @@ static __init int gsmi_init(void)
goto out_err;
}
- printk(KERN_INFO "gsmi version " DRIVER_VERSION " loaded\n");
-
/* Register in the firmware directory */
ret = -ENOMEM;
gsmi_kobj = kobject_create_and_add("gsmi", firmware_kobj);
@@ -890,12 +889,13 @@ static __init int gsmi_init(void)
ret = sysfs_create_files(gsmi_kobj, gsmi_attrs);
if (ret) {
printk(KERN_INFO "gsmi: Failed to add attrs");
- goto out_err;
+ goto out_remove_bin_file;
}
- if (register_efivars(&efivars, &efivar_ops, gsmi_kobj)) {
+ ret = register_efivars(&efivars, &efivar_ops, gsmi_kobj);
+ if (ret) {
printk(KERN_INFO "gsmi: Failed to register efivars\n");
- goto out_err;
+ goto out_remove_sysfs_files;
}
register_reboot_notifier(&gsmi_reboot_notifier);
@@ -903,9 +903,15 @@ static __init int gsmi_init(void)
atomic_notifier_chain_register(&panic_notifier_list,
&gsmi_panic_notifier);
+ printk(KERN_INFO "gsmi version " DRIVER_VERSION " loaded\n");
+
return 0;
- out_err:
+out_remove_sysfs_files:
+ sysfs_remove_files(gsmi_kobj, gsmi_attrs);
+out_remove_bin_file:
+ sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr);
+out_err:
kobject_put(gsmi_kobj);
gsmi_buf_free(gsmi_dev.param_buf);
gsmi_buf_free(gsmi_dev.data_buf);
@@ -925,6 +931,8 @@ static void __exit gsmi_exit(void)
&gsmi_panic_notifier);
unregister_efivars(&efivars);
+ sysfs_remove_files(gsmi_kobj, gsmi_attrs);
+ sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr);
kobject_put(gsmi_kobj);
gsmi_buf_free(gsmi_dev.param_buf);
gsmi_buf_free(gsmi_dev.data_buf);
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c
index ce33f4626957..c811cb107904 100644
--- a/drivers/firmware/iscsi_ibft.c
+++ b/drivers/firmware/iscsi_ibft.c
@@ -566,6 +566,11 @@ static mode_t __init ibft_check_initiator_for(void *data, int type)
return rc;
}
+static void ibft_kobj_release(void *data)
+{
+ kfree(data);
+}
+
/*
* Helper function for ibft_register_kobjects.
*/
@@ -595,7 +600,8 @@ static int __init ibft_create_kobject(struct acpi_table_ibft *header,
boot_kobj = iscsi_boot_create_initiator(boot_kset, hdr->index,
ibft_kobj,
ibft_attr_show_initiator,
- ibft_check_initiator_for);
+ ibft_check_initiator_for,
+ ibft_kobj_release);
if (!boot_kobj) {
rc = -ENOMEM;
goto free_ibft_obj;
@@ -610,7 +616,8 @@ static int __init ibft_create_kobject(struct acpi_table_ibft *header,
boot_kobj = iscsi_boot_create_ethernet(boot_kset, hdr->index,
ibft_kobj,
ibft_attr_show_nic,
- ibft_check_nic_for);
+ ibft_check_nic_for,
+ ibft_kobj_release);
if (!boot_kobj) {
rc = -ENOMEM;
goto free_ibft_obj;
@@ -625,7 +632,8 @@ static int __init ibft_create_kobject(struct acpi_table_ibft *header,
boot_kobj = iscsi_boot_create_target(boot_kset, hdr->index,
ibft_kobj,
ibft_attr_show_target,
- ibft_check_tgt_for);
+ ibft_check_tgt_for,
+ ibft_kobj_release);
if (!boot_kobj) {
rc = -ENOMEM;
goto free_ibft_obj;
diff --git a/drivers/firmware/sigma.c b/drivers/firmware/sigma.c
index c19cd2c39fa6..f10fc521951b 100644
--- a/drivers/firmware/sigma.c
+++ b/drivers/firmware/sigma.c
@@ -11,6 +11,7 @@
#include <linux/firmware.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
+#include <linux/module.h>
#include <linux/sigma.h>
/* Return: 0==OK, <0==error, =1 ==no more actions */
@@ -113,3 +114,5 @@ int process_sigma_firmware(struct i2c_client *client, const char *name)
return ret;
}
EXPORT_SYMBOL(process_sigma_firmware);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 2967002a9f82..8482a23887dc 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -63,44 +63,77 @@ config GPIO_SYSFS
Kernel drivers may also request that a particular GPIO be
exported to userspace; this can be useful when debugging.
+config GPIO_GENERIC
+ tristate
+
# put drivers in the right section, in alphabetical order
+config GPIO_DA9052
+ tristate "Dialog DA9052 GPIO"
+ depends on PMIC_DA9052
+ help
+ Say yes here to enable the GPIO driver for the DA9052 chip.
+
config GPIO_MAX730X
tristate
comment "Memory mapped GPIO drivers:"
-config GPIO_BASIC_MMIO_CORE
- tristate
+config GPIO_GENERIC_PLATFORM
+ tristate "Generic memory-mapped GPIO controller support (MMIO platform device)"
+ select GPIO_GENERIC
help
- Provides core functionality for basic memory-mapped GPIO controllers.
-
-config GPIO_BASIC_MMIO
- tristate "Basic memory-mapped GPIO controllers support"
- select GPIO_BASIC_MMIO_CORE
- help
- Say yes here to support basic memory-mapped GPIO controllers.
+ Say yes here to support basic platform_device memory-mapped GPIO controllers.
config GPIO_IT8761E
tristate "IT8761E GPIO support"
help
Say yes here to support GPIO functionality of IT8761E super I/O chip.
-config GPIO_EXYNOS4
+config GPIO_EP93XX
def_bool y
- depends on CPU_EXYNOS4210
+ depends on ARCH_EP93XX
+ select GPIO_GENERIC
-config GPIO_PLAT_SAMSUNG
+config GPIO_MPC5200
def_bool y
- depends on SAMSUNG_GPIOLIB_4BIT
+ depends on PPC_MPC52xx
+
+config GPIO_MPC8XXX
+ bool "MPC512x/MPC8xxx GPIO support"
+ depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \
+ FSL_SOC_BOOKE || PPC_86xx
+ help
+ Say Y here if you're going to use hardware that connects to the
+ MPC512x/831x/834x/837x/8572/8610 GPIOs.
+
+config GPIO_MSM_V1
+ tristate "Qualcomm MSM GPIO v1"
+ depends on GPIOLIB && ARCH_MSM
+ help
+ Say yes here to support the GPIO interface on ARM v6 based
+ Qualcomm MSM chips. Most of the pins on the MSM can be
+ selected for GPIO, and are controlled by this driver.
-config GPIO_S5PC100
+config GPIO_MSM_V2
+ tristate "Qualcomm MSM GPIO v2"
+ depends on GPIOLIB && ARCH_MSM
+ help
+ Say yes here to support the GPIO interface on ARM v7 based
+ Qualcomm MSM chips. Most of the pins on the MSM can be
+ selected for GPIO, and are controlled by this driver.
+
+config GPIO_MXC
def_bool y
- depends on CPU_S5PC100
+ depends on ARCH_MXC
+ select GPIO_GENERIC
+ select GENERIC_IRQ_CHIP
-config GPIO_S5PV210
+config GPIO_MXS
def_bool y
- depends on CPU_S5PV210
+ depends on ARCH_MXS
+ select GPIO_GENERIC
+ select GENERIC_IRQ_CHIP
config GPIO_PL061
bool "PrimeCell PL061 GPIO support"
@@ -137,12 +170,18 @@ config GPIO_SCH
The Intel Tunnel Creek processor has 5 GPIOs powered by the
core power rail and 9 from suspend power supply.
- This driver can also be built as a module. If so, the module
- will be called sch-gpio.
+config GPIO_U300
+ bool "ST-Ericsson U300 COH 901 335/571 GPIO"
+ depends on GPIOLIB && ARCH_U300
+ help
+ Say yes here to support GPIO interface on ST-Ericsson U300.
+ The names of the two IP block variants supported are
+ COH 901 335 and COH 901 571/3. They contain 3, 5 or 7
+ ports of 8 GPIO pins each.
config GPIO_VX855
tristate "VIA VX855/VX875 GPIO"
- depends on MFD_SUPPORT && PCI
+ depends on PCI
select MFD_CORE
select MFD_VX855
help
@@ -202,9 +241,6 @@ config GPIO_PCA953X
16 bits: pca9535, pca9539, pca9555, tca6416
- This driver can also be built as a module. If so, the module
- will be called pca953x.
-
config GPIO_PCA953X_IRQ
bool "Interrupt controller support for PCA953x"
depends on GPIO_PCA953X=y
@@ -261,6 +297,12 @@ config GPIO_TC3589X
This enables support for the GPIOs found on the TC3589X
I/O Expander.
+config GPIO_TPS65912
+ tristate "TI TPS65912 GPIO"
+ depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
+ help
+ This driver supports TPS65912 gpio chip
+
config GPIO_TWL4030
tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
depends on TWL4030_CORE
@@ -296,17 +338,12 @@ config GPIO_ADP5520
This option enables support for on-chip GPIO found
on Analog Devices ADP5520 PMICs.
- To compile this driver as a module, choose M here: the module will
- be called adp5520-gpio.
-
config GPIO_ADP5588
tristate "ADP5588 I2C GPIO expander"
depends on I2C
help
This option enables support for 18 GPIOs found
on Analog Devices ADP5588 GPIO Expanders.
- To compile this driver as a module, choose M here: the module will be
- called adp5588-gpio.
config GPIO_ADP5588_IRQ
bool "Interrupt controller support for ADP5588"
@@ -352,6 +389,7 @@ config GPIO_LANGWELL
config GPIO_PCH
tristate "Intel EG20T PCH / OKI SEMICONDUCTOR ML7223 IOH GPIO"
depends on PCI && X86
+ select GENERIC_IRQ_CHIP
help
This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff
which is an IOH(Input/Output Hub) for x86 embedded processor.
@@ -366,6 +404,7 @@ config GPIO_PCH
config GPIO_ML_IOH
tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
depends on PCI
+ select GENERIC_IRQ_CHIP
help
ML7213 is companion chip for Intel Atom E6xx series.
This driver can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/Output
@@ -381,7 +420,6 @@ config GPIO_TIMBERDALE
config GPIO_RDC321X
tristate "RDC R-321x GPIO support"
depends on PCI
- select MFD_SUPPORT
select MFD_CORE
select MFD_RDC321X
help
@@ -398,10 +436,11 @@ config GPIO_MAX7301
GPIO driver for Maxim MAX7301 SPI-based GPIO expander.
config GPIO_MCP23S08
- tristate "Microchip MCP23Sxx I/O expander"
- depends on SPI_MASTER
+ tristate "Microchip MCP23xxx I/O expander"
+ depends on SPI_MASTER || I2C
help
- SPI driver for Microchip MCP23S08/MPC23S17 I/O expanders.
+ SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
+ I/O expanders.
This provides a GPIO interface supporting inputs and outputs.
config GPIO_MC33880
@@ -428,9 +467,6 @@ config GPIO_UCB1400
This enables support for the Philips UCB1400 GPIO pins.
The UCB1400 is an AC97 audio codec.
- To compile this driver as a module, choose M here: the
- module will be called ucb1400_gpio.
-
comment "MODULbus GPIO expanders:"
config GPIO_JANZ_TTL
@@ -441,7 +477,7 @@ config GPIO_JANZ_TTL
This driver provides support for driving the pins in output
mode only. Input mode is not supported.
-config AB8500_GPIO
+config GPIO_AB8500
bool "ST-Ericsson AB8500 Mixed Signal Circuit gpio functions"
depends on AB8500_CORE && BROKEN
help
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index b605f8ec6fbe..dbcb0bcfd8da 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -4,47 +4,61 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
obj-$(CONFIG_GPIOLIB) += gpiolib.o
-obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o
-obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o
-obj-$(CONFIG_GPIO_BASIC_MMIO_CORE) += basic_mmio_gpio.o
-obj-$(CONFIG_GPIO_BASIC_MMIO) += basic_mmio_gpio.o
-obj-$(CONFIG_GPIO_EXYNOS4) += gpio-exynos4.o
-obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o
-obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o
-obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o
-obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o
-obj-$(CONFIG_GPIO_MAX730X) += max730x.o
-obj-$(CONFIG_GPIO_MAX7300) += max7300.o
-obj-$(CONFIG_GPIO_MAX7301) += max7301.o
-obj-$(CONFIG_GPIO_MAX732X) += max732x.o
-obj-$(CONFIG_GPIO_MC33880) += mc33880.o
-obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
-obj-$(CONFIG_GPIO_74X164) += 74x164.o
-obj-$(CONFIG_ARCH_OMAP) += gpio-omap.o
-obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
-obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
-obj-$(CONFIG_GPIO_PCH) += pch_gpio.o
-obj-$(CONFIG_GPIO_PL061) += pl061.o
-obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o
-obj-$(CONFIG_GPIO_TC3589X) += tc3589x-gpio.o
-obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
-obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
-obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o
-obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o
-obj-$(CONFIG_GPIO_CS5535) += cs5535-gpio.o
-obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
-obj-$(CONFIG_GPIO_IT8761E) += it8761e_gpio.o
-obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
-obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
-obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o
-obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o
-obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
-obj-$(CONFIG_MACH_U300) += gpio-u300.o
+# Device drivers. Generally keep list sorted alphabetically
+obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
+
+obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
+obj-$(CONFIG_GPIO_AB8500) += gpio-ab8500.o
+obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
+obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
+obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
+obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
+obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
+obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o
+obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
+obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o
+obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
+obj-$(CONFIG_MACH_KS8695) += gpio-ks8695.o
+obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o
+obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
+obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
+obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
+obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o
+obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o
+obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o
+obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o
+obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o
+obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o
+obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o
+obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o
+obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o
+obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
+obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
obj-$(CONFIG_PLAT_NOMADIK) += gpio-nomadik.o
-obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
-obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
-obj-$(CONFIG_GPIO_SX150X) += sx150x.o
-obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o
-obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o
-obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o
-obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o
+obj-$(CONFIG_ARCH_OMAP) += gpio-omap.o
+obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o
+obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o
+obj-$(CONFIG_GPIO_PCH) += gpio-pch.o
+obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
+obj-$(CONFIG_PLAT_PXA) += gpio-pxa.o
+obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
+obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o
+obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
+obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
+obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
+obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
+obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
+obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
+obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
+obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o
+obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
+obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
+obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
+obj-$(CONFIG_MACH_U300) += gpio-u300.o
+obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
+obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
+obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
+obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
+obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
+obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
+obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
diff --git a/drivers/gpio/74x164.c b/drivers/gpio/gpio-74x164.c
index 84e070219839..a31ad6f5d910 100644
--- a/drivers/gpio/74x164.c
+++ b/drivers/gpio/gpio-74x164.c
@@ -15,9 +15,7 @@
#include <linux/spi/74x164.h>
#include <linux/gpio.h>
#include <linux/slab.h>
-
-#define GEN_74X164_GPIO_COUNT 8
-
+#include <linux/module.h>
struct gen_74x164_chip {
struct spi_device *spi;
@@ -26,9 +24,7 @@ struct gen_74x164_chip {
u8 port_config;
};
-static void gen_74x164_set_value(struct gpio_chip *, unsigned, int);
-
-static struct gen_74x164_chip *gpio_to_chip(struct gpio_chip *gc)
+static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
{
return container_of(gc, struct gen_74x164_chip, gpio_chip);
}
@@ -39,16 +35,9 @@ static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
&chip->port_config, sizeof(chip->port_config));
}
-static int gen_74x164_direction_output(struct gpio_chip *gc,
- unsigned offset, int val)
-{
- gen_74x164_set_value(gc, offset, val);
- return 0;
-}
-
static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
{
- struct gen_74x164_chip *chip = gpio_to_chip(gc);
+ struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
int ret;
mutex_lock(&chip->lock);
@@ -61,7 +50,7 @@ static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
static void gen_74x164_set_value(struct gpio_chip *gc,
unsigned offset, int val)
{
- struct gen_74x164_chip *chip = gpio_to_chip(gc);
+ struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
mutex_lock(&chip->lock);
if (val)
@@ -73,6 +62,13 @@ static void gen_74x164_set_value(struct gpio_chip *gc,
mutex_unlock(&chip->lock);
}
+static int gen_74x164_direction_output(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ gen_74x164_set_value(gc, offset, val);
+ return 0;
+}
+
static int __devinit gen_74x164_probe(struct spi_device *spi)
{
struct gen_74x164_chip *chip;
@@ -104,12 +100,12 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
chip->spi = spi;
- chip->gpio_chip.label = GEN_74X164_DRIVER_NAME,
- chip->gpio_chip.direction_output = gen_74x164_direction_output;
+ chip->gpio_chip.label = spi->modalias;
+ chip->gpio_chip.direction_output = gen_74x164_direction_output;
chip->gpio_chip.get = gen_74x164_get_value;
chip->gpio_chip.set = gen_74x164_set_value;
chip->gpio_chip.base = pdata->base;
- chip->gpio_chip.ngpio = GEN_74X164_GPIO_COUNT;
+ chip->gpio_chip.ngpio = 8;
chip->gpio_chip.can_sleep = 1;
chip->gpio_chip.dev = &spi->dev;
chip->gpio_chip.owner = THIS_MODULE;
@@ -157,7 +153,7 @@ static int __devexit gen_74x164_remove(struct spi_device *spi)
static struct spi_driver gen_74x164_driver = {
.driver = {
- .name = GEN_74X164_DRIVER_NAME,
+ .name = "74x164",
.owner = THIS_MODULE,
},
.probe = gen_74x164_probe,
diff --git a/drivers/gpio/ab8500-gpio.c b/drivers/gpio/gpio-ab8500.c
index 970053c89ff7..050c05d91896 100644
--- a/drivers/gpio/ab8500-gpio.c
+++ b/drivers/gpio/gpio-ab8500.c
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/err.h>
#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
@@ -517,5 +516,5 @@ module_exit(ab8500_gpio_exit);
MODULE_AUTHOR("BIBEK BASU <bibek.basu@stericsson.com>");
MODULE_DESCRIPTION("Driver allows to use AB8500 unused pins to be used as GPIO");
-MODULE_ALIAS("AB8500 GPIO driver");
+MODULE_ALIAS("platform:ab8500-gpio");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/adp5520-gpio.c b/drivers/gpio/gpio-adp5520.c
index 9f2781537001..9f2781537001 100644
--- a/drivers/gpio/adp5520-gpio.c
+++ b/drivers/gpio/gpio-adp5520.c
diff --git a/drivers/gpio/adp5588-gpio.c b/drivers/gpio/gpio-adp5588.c
index 3525ad918771..3525ad918771 100644
--- a/drivers/gpio/adp5588-gpio.c
+++ b/drivers/gpio/gpio-adp5588.c
diff --git a/drivers/gpio/bt8xxgpio.c b/drivers/gpio/gpio-bt8xx.c
index aa4f09ad3ced..ec57936aef62 100644
--- a/drivers/gpio/bt8xxgpio.c
+++ b/drivers/gpio/gpio-bt8xx.c
@@ -2,7 +2,7 @@
bt8xx GPIO abuser
- Copyright (C) 2008 Michael Buesch <mb@bu3sch.de>
+ Copyright (C) 2008 Michael Buesch <m@bues.ch>
Please do _only_ contact the people listed _above_ with issues related to this driver.
All the other people listed below are not related to this driver. Their names
diff --git a/drivers/gpio/cs5535-gpio.c b/drivers/gpio/gpio-cs5535.c
index 6e16cba56ad2..6e16cba56ad2 100644
--- a/drivers/gpio/cs5535-gpio.c
+++ b/drivers/gpio/gpio-cs5535.c
diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c
new file mode 100644
index 000000000000..038f5eb8b13d
--- /dev/null
+++ b/drivers/gpio/gpio-da9052.c
@@ -0,0 +1,277 @@
+/*
+ * GPIO Driver for Dialog DA9052 PMICs.
+ *
+ * Copyright(c) 2011 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/syscalls.h>
+#include <linux/seq_file.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/pdata.h>
+#include <linux/mfd/da9052/gpio.h>
+
+#define DA9052_INPUT 1
+#define DA9052_OUTPUT_OPENDRAIN 2
+#define DA9052_OUTPUT_PUSHPULL 3
+
+#define DA9052_SUPPLY_VDD_IO1 0
+
+#define DA9052_DEBOUNCING_OFF 0
+#define DA9052_DEBOUNCING_ON 1
+
+#define DA9052_OUTPUT_LOWLEVEL 0
+
+#define DA9052_ACTIVE_LOW 0
+#define DA9052_ACTIVE_HIGH 1
+
+#define DA9052_GPIO_MAX_PORTS_PER_REGISTER 8
+#define DA9052_GPIO_SHIFT_COUNT(no) (no%8)
+#define DA9052_GPIO_MASK_UPPER_NIBBLE 0xF0
+#define DA9052_GPIO_MASK_LOWER_NIBBLE 0x0F
+#define DA9052_GPIO_NIBBLE_SHIFT 4
+
+struct da9052_gpio {
+ struct da9052 *da9052;
+ struct gpio_chip gp;
+};
+
+static inline struct da9052_gpio *to_da9052_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct da9052_gpio, gp);
+}
+
+static unsigned char da9052_gpio_port_odd(unsigned offset)
+{
+ return offset % 2;
+}
+
+static int da9052_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct da9052_gpio *gpio = to_da9052_gpio(gc);
+ int da9052_port_direction = 0;
+ int ret;
+
+ ret = da9052_reg_read(gpio->da9052,
+ DA9052_GPIO_0_1_REG + (offset >> 1));
+ if (ret < 0)
+ return ret;
+
+ if (da9052_gpio_port_odd(offset)) {
+ da9052_port_direction = ret & DA9052_GPIO_ODD_PORT_PIN;
+ da9052_port_direction >>= 4;
+ } else {
+ da9052_port_direction = ret & DA9052_GPIO_EVEN_PORT_PIN;
+ }
+
+ switch (da9052_port_direction) {
+ case DA9052_INPUT:
+ if (offset < DA9052_GPIO_MAX_PORTS_PER_REGISTER)
+ ret = da9052_reg_read(gpio->da9052,
+ DA9052_STATUS_C_REG);
+ else
+ ret = da9052_reg_read(gpio->da9052,
+ DA9052_STATUS_D_REG);
+ if (ret < 0)
+ return ret;
+ if (ret & (1 << DA9052_GPIO_SHIFT_COUNT(offset)))
+ return 1;
+ else
+ return 0;
+ case DA9052_OUTPUT_PUSHPULL:
+ if (da9052_gpio_port_odd(offset))
+ return ret & DA9052_GPIO_ODD_PORT_MODE;
+ else
+ return ret & DA9052_GPIO_EVEN_PORT_MODE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void da9052_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+ struct da9052_gpio *gpio = to_da9052_gpio(gc);
+ unsigned char register_value = 0;
+ int ret;
+
+ if (da9052_gpio_port_odd(offset)) {
+ if (value) {
+ register_value = DA9052_GPIO_ODD_PORT_MODE;
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_ODD_PORT_MODE,
+ register_value);
+ if (ret != 0)
+ dev_err(gpio->da9052->dev,
+ "Failed to updated gpio odd reg,%d",
+ ret);
+ }
+ } else {
+ if (value) {
+ register_value = DA9052_GPIO_EVEN_PORT_MODE;
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_EVEN_PORT_MODE,
+ register_value);
+ if (ret != 0)
+ dev_err(gpio->da9052->dev,
+ "Failed to updated gpio even reg,%d",
+ ret);
+ }
+ }
+}
+
+static int da9052_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct da9052_gpio *gpio = to_da9052_gpio(gc);
+ unsigned char register_value;
+ int ret;
+
+ /* Format: function - 2 bits type - 1 bit mode - 1 bit */
+ register_value = DA9052_INPUT | DA9052_ACTIVE_LOW << 2 |
+ DA9052_DEBOUNCING_ON << 3;
+
+ if (da9052_gpio_port_odd(offset))
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_MASK_UPPER_NIBBLE,
+ (register_value <<
+ DA9052_GPIO_NIBBLE_SHIFT));
+ else
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_MASK_LOWER_NIBBLE,
+ register_value);
+
+ return ret;
+}
+
+static int da9052_gpio_direction_output(struct gpio_chip *gc,
+ unsigned offset, int value)
+{
+ struct da9052_gpio *gpio = to_da9052_gpio(gc);
+ unsigned char register_value;
+ int ret;
+
+ /* Format: Function - 2 bits Type - 1 bit Mode - 1 bit */
+ register_value = DA9052_OUTPUT_PUSHPULL | DA9052_SUPPLY_VDD_IO1 << 2 |
+ value << 3;
+
+ if (da9052_gpio_port_odd(offset))
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_MASK_UPPER_NIBBLE,
+ (register_value <<
+ DA9052_GPIO_NIBBLE_SHIFT));
+ else
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_MASK_LOWER_NIBBLE,
+ register_value);
+
+ return ret;
+}
+
+static int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset)
+{
+ struct da9052_gpio *gpio = to_da9052_gpio(gc);
+ struct da9052 *da9052 = gpio->da9052;
+
+ return da9052->irq_base + DA9052_IRQ_GPI0 + offset;
+}
+
+static struct gpio_chip reference_gp __devinitdata = {
+ .label = "da9052-gpio",
+ .owner = THIS_MODULE,
+ .get = da9052_gpio_get,
+ .set = da9052_gpio_set,
+ .direction_input = da9052_gpio_direction_input,
+ .direction_output = da9052_gpio_direction_output,
+ .to_irq = da9052_gpio_to_irq,
+ .can_sleep = 1;
+ .ngpio = 16;
+ .base = -1;
+};
+
+static int __devinit da9052_gpio_probe(struct platform_device *pdev)
+{
+ struct da9052_gpio *gpio;
+ struct da9052_pdata *pdata;
+ int ret;
+
+ gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
+ if (gpio == NULL)
+ return -ENOMEM;
+
+ gpio->da9052 = dev_get_drvdata(pdev->dev.parent);
+ pdata = gpio->da9052->dev->platform_data;
+
+ gpio->gp = reference_gp;
+ if (pdata && pdata->gpio_base)
+ gpio->gp.base = pdata->gpio_base;
+
+ ret = gpiochip_add(&gpio->gp);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ goto err_mem;
+ }
+
+ platform_set_drvdata(pdev, gpio);
+
+ return 0;
+
+err_mem:
+ kfree(gpio);
+ return ret;
+}
+
+static int __devexit da9052_gpio_remove(struct platform_device *pdev)
+{
+ struct da9052_gpio *gpio = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = gpiochip_remove(&gpio->gp);
+ if (ret == 0)
+ kfree(gpio);
+
+ return ret;
+}
+
+static struct platform_driver da9052_gpio_driver = {
+ .probe = da9052_gpio_probe,
+ .remove = __devexit_p(da9052_gpio_remove),
+ .driver = {
+ .name = "da9052-gpio",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init da9052_gpio_init(void)
+{
+ return platform_driver_register(&da9052_gpio_driver);
+}
+module_init(da9052_gpio_init);
+
+static void __exit da9052_gpio_exit(void)
+{
+ return platform_driver_unregister(&da9052_gpio_driver);
+}
+module_exit(da9052_gpio_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9052 GPIO Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-gpio");
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
new file mode 100644
index 000000000000..df0d59570a84
--- /dev/null
+++ b/drivers/gpio/gpio-davinci.c
@@ -0,0 +1,455 @@
+/*
+ * TI DaVinci GPIO Support
+ *
+ * Copyright (c) 2006-2007 David Brownell
+ * Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.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/gpio.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <asm/mach/irq.h>
+
+struct davinci_gpio_regs {
+ u32 dir;
+ u32 out_data;
+ u32 set_data;
+ u32 clr_data;
+ u32 in_data;
+ u32 set_rising;
+ u32 clr_rising;
+ u32 set_falling;
+ u32 clr_falling;
+ u32 intstat;
+};
+
+#define chip2controller(chip) \
+ container_of(chip, struct davinci_gpio_controller, chip)
+
+static struct davinci_gpio_controller chips[DIV_ROUND_UP(DAVINCI_N_GPIO, 32)];
+static void __iomem *gpio_base;
+
+static struct davinci_gpio_regs __iomem __init *gpio2regs(unsigned gpio)
+{
+ void __iomem *ptr;
+
+ if (gpio < 32 * 1)
+ ptr = gpio_base + 0x10;
+ else if (gpio < 32 * 2)
+ ptr = gpio_base + 0x38;
+ else if (gpio < 32 * 3)
+ ptr = gpio_base + 0x60;
+ else if (gpio < 32 * 4)
+ ptr = gpio_base + 0x88;
+ else if (gpio < 32 * 5)
+ ptr = gpio_base + 0xb0;
+ else
+ ptr = NULL;
+ return ptr;
+}
+
+static inline struct davinci_gpio_regs __iomem *irq2regs(int irq)
+{
+ struct davinci_gpio_regs __iomem *g;
+
+ g = (__force struct davinci_gpio_regs __iomem *)irq_get_chip_data(irq);
+
+ return g;
+}
+
+static int __init davinci_gpio_irq_setup(void);
+
+/*--------------------------------------------------------------------------*/
+
+/* board setup code *MUST* setup pinmux and enable the GPIO clock. */
+static inline int __davinci_direction(struct gpio_chip *chip,
+ unsigned offset, bool out, int value)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+ struct davinci_gpio_regs __iomem *g = d->regs;
+ unsigned long flags;
+ u32 temp;
+ u32 mask = 1 << offset;
+
+ spin_lock_irqsave(&d->lock, flags);
+ temp = __raw_readl(&g->dir);
+ if (out) {
+ temp &= ~mask;
+ __raw_writel(mask, value ? &g->set_data : &g->clr_data);
+ } else {
+ temp |= mask;
+ }
+ __raw_writel(temp, &g->dir);
+ spin_unlock_irqrestore(&d->lock, flags);
+
+ return 0;
+}
+
+static int davinci_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ return __davinci_direction(chip, offset, false, 0);
+}
+
+static int
+davinci_direction_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+ return __davinci_direction(chip, offset, true, value);
+}
+
+/*
+ * Read the pin's value (works even if it's set up as output);
+ * returns zero/nonzero.
+ *
+ * Note that changes are synched to the GPIO clock, so reading values back
+ * right after you've set them may give old values.
+ */
+static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+ struct davinci_gpio_regs __iomem *g = d->regs;
+
+ return (1 << offset) & __raw_readl(&g->in_data);
+}
+
+/*
+ * Assuming the pin is muxed as a gpio output, set its output value.
+ */
+static void
+davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+ struct davinci_gpio_regs __iomem *g = d->regs;
+
+ __raw_writel((1 << offset), value ? &g->set_data : &g->clr_data);
+}
+
+static int __init davinci_gpio_setup(void)
+{
+ int i, base;
+ unsigned ngpio;
+ struct davinci_soc_info *soc_info = &davinci_soc_info;
+ struct davinci_gpio_regs *regs;
+
+ if (soc_info->gpio_type != GPIO_TYPE_DAVINCI)
+ return 0;
+
+ /*
+ * The gpio banks conceptually expose a segmented bitmap,
+ * and "ngpio" is one more than the largest zero-based
+ * bit index that's valid.
+ */
+ ngpio = soc_info->gpio_num;
+ if (ngpio == 0) {
+ pr_err("GPIO setup: how many GPIOs?\n");
+ return -EINVAL;
+ }
+
+ if (WARN_ON(DAVINCI_N_GPIO < ngpio))
+ ngpio = DAVINCI_N_GPIO;
+
+ gpio_base = ioremap(soc_info->gpio_base, SZ_4K);
+ if (WARN_ON(!gpio_base))
+ return -ENOMEM;
+
+ for (i = 0, base = 0; base < ngpio; i++, base += 32) {
+ chips[i].chip.label = "DaVinci";
+
+ chips[i].chip.direction_input = davinci_direction_in;
+ chips[i].chip.get = davinci_gpio_get;
+ chips[i].chip.direction_output = davinci_direction_out;
+ chips[i].chip.set = davinci_gpio_set;
+
+ chips[i].chip.base = base;
+ chips[i].chip.ngpio = ngpio - base;
+ if (chips[i].chip.ngpio > 32)
+ chips[i].chip.ngpio = 32;
+
+ spin_lock_init(&chips[i].lock);
+
+ regs = gpio2regs(base);
+ chips[i].regs = regs;
+ chips[i].set_data = &regs->set_data;
+ chips[i].clr_data = &regs->clr_data;
+ chips[i].in_data = &regs->in_data;
+
+ gpiochip_add(&chips[i].chip);
+ }
+
+ soc_info->gpio_ctlrs = chips;
+ soc_info->gpio_ctlrs_num = DIV_ROUND_UP(ngpio, 32);
+
+ davinci_gpio_irq_setup();
+ return 0;
+}
+pure_initcall(davinci_gpio_setup);
+
+/*--------------------------------------------------------------------------*/
+/*
+ * We expect irqs will normally be set up as input pins, but they can also be
+ * used as output pins ... which is convenient for testing.
+ *
+ * NOTE: The first few GPIOs also have direct INTC hookups in addition
+ * to their GPIOBNK0 irq, with a bit less overhead.
+ *
+ * All those INTC hookups (direct, plus several IRQ banks) can also
+ * serve as EDMA event triggers.
+ */
+
+static void gpio_irq_disable(struct irq_data *d)
+{
+ struct davinci_gpio_regs __iomem *g = irq2regs(d->irq);
+ u32 mask = (u32) irq_data_get_irq_handler_data(d);
+
+ __raw_writel(mask, &g->clr_falling);
+ __raw_writel(mask, &g->clr_rising);
+}
+
+static void gpio_irq_enable(struct irq_data *d)
+{
+ struct davinci_gpio_regs __iomem *g = irq2regs(d->irq);
+ u32 mask = (u32) irq_data_get_irq_handler_data(d);
+ unsigned status = irqd_get_trigger_type(d);
+
+ status &= IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
+ if (!status)
+ status = IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
+
+ if (status & IRQ_TYPE_EDGE_FALLING)
+ __raw_writel(mask, &g->set_falling);
+ if (status & IRQ_TYPE_EDGE_RISING)
+ __raw_writel(mask, &g->set_rising);
+}
+
+static int gpio_irq_type(struct irq_data *d, unsigned trigger)
+{
+ if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct irq_chip gpio_irqchip = {
+ .name = "GPIO",
+ .irq_enable = gpio_irq_enable,
+ .irq_disable = gpio_irq_disable,
+ .irq_set_type = gpio_irq_type,
+ .flags = IRQCHIP_SET_TYPE_MASKED,
+};
+
+static void
+gpio_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ struct davinci_gpio_regs __iomem *g;
+ u32 mask = 0xffff;
+ struct davinci_gpio_controller *d;
+
+ d = (struct davinci_gpio_controller *)irq_desc_get_handler_data(desc);
+ g = (struct davinci_gpio_regs __iomem *)d->regs;
+
+ /* we only care about one bank */
+ if (irq & 1)
+ mask <<= 16;
+
+ /* temporarily mask (level sensitive) parent IRQ */
+ desc->irq_data.chip->irq_mask(&desc->irq_data);
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+ while (1) {
+ u32 status;
+ int n;
+ int res;
+
+ /* ack any irqs */
+ status = __raw_readl(&g->intstat) & mask;
+ if (!status)
+ break;
+ __raw_writel(status, &g->intstat);
+
+ /* now demux them to the right lowlevel handler */
+ n = d->irq_base;
+ if (irq & 1) {
+ n += 16;
+ status >>= 16;
+ }
+
+ while (status) {
+ res = ffs(status);
+ n += res;
+ generic_handle_irq(n - 1);
+ status >>= res;
+ }
+ }
+ desc->irq_data.chip->irq_unmask(&desc->irq_data);
+ /* now it may re-trigger */
+}
+
+static int gpio_to_irq_banked(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+
+ if (d->irq_base >= 0)
+ return d->irq_base + offset;
+ else
+ return -ENODEV;
+}
+
+static int gpio_to_irq_unbanked(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_soc_info *soc_info = &davinci_soc_info;
+
+ /* NOTE: we assume for now that only irqs in the first gpio_chip
+ * can provide direct-mapped IRQs to AINTC (up to 32 GPIOs).
+ */
+ if (offset < soc_info->gpio_unbanked)
+ return soc_info->gpio_irq + offset;
+ else
+ return -ENODEV;
+}
+
+static int gpio_irq_type_unbanked(struct irq_data *d, unsigned trigger)
+{
+ struct davinci_gpio_regs __iomem *g = irq2regs(d->irq);
+ u32 mask = (u32) irq_data_get_irq_handler_data(d);
+
+ if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ return -EINVAL;
+
+ __raw_writel(mask, (trigger & IRQ_TYPE_EDGE_FALLING)
+ ? &g->set_falling : &g->clr_falling);
+ __raw_writel(mask, (trigger & IRQ_TYPE_EDGE_RISING)
+ ? &g->set_rising : &g->clr_rising);
+
+ return 0;
+}
+
+/*
+ * NOTE: for suspend/resume, probably best to make a platform_device with
+ * suspend_late/resume_resume calls hooking into results of the set_wake()
+ * calls ... so if no gpios are wakeup events the clock can be disabled,
+ * with outputs left at previously set levels, and so that VDD3P3V.IOPWDN0
+ * (dm6446) can be set appropriately for GPIOV33 pins.
+ */
+
+static int __init davinci_gpio_irq_setup(void)
+{
+ unsigned gpio, irq, bank;
+ struct clk *clk;
+ u32 binten = 0;
+ unsigned ngpio, bank_irq;
+ struct davinci_soc_info *soc_info = &davinci_soc_info;
+ struct davinci_gpio_regs __iomem *g;
+
+ ngpio = soc_info->gpio_num;
+
+ bank_irq = soc_info->gpio_irq;
+ if (bank_irq == 0) {
+ printk(KERN_ERR "Don't know first GPIO bank IRQ.\n");
+ return -EINVAL;
+ }
+
+ clk = clk_get(NULL, "gpio");
+ if (IS_ERR(clk)) {
+ printk(KERN_ERR "Error %ld getting gpio clock?\n",
+ PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+ clk_enable(clk);
+
+ /* Arrange gpio_to_irq() support, handling either direct IRQs or
+ * banked IRQs. Having GPIOs in the first GPIO bank use direct
+ * IRQs, while the others use banked IRQs, would need some setup
+ * tweaks to recognize hardware which can do that.
+ */
+ for (gpio = 0, bank = 0; gpio < ngpio; bank++, gpio += 32) {
+ chips[bank].chip.to_irq = gpio_to_irq_banked;
+ chips[bank].irq_base = soc_info->gpio_unbanked
+ ? -EINVAL
+ : (soc_info->intc_irq_num + gpio);
+ }
+
+ /*
+ * AINTC can handle direct/unbanked IRQs for GPIOs, with the GPIO
+ * controller only handling trigger modes. We currently assume no
+ * IRQ mux conflicts; gpio_irq_type_unbanked() is only for GPIOs.
+ */
+ if (soc_info->gpio_unbanked) {
+ static struct irq_chip gpio_irqchip_unbanked;
+
+ /* pass "bank 0" GPIO IRQs to AINTC */
+ chips[0].chip.to_irq = gpio_to_irq_unbanked;
+ binten = BIT(0);
+
+ /* AINTC handles mask/unmask; GPIO handles triggering */
+ irq = bank_irq;
+ gpio_irqchip_unbanked = *irq_get_chip(irq);
+ gpio_irqchip_unbanked.name = "GPIO-AINTC";
+ gpio_irqchip_unbanked.irq_set_type = gpio_irq_type_unbanked;
+
+ /* default trigger: both edges */
+ g = gpio2regs(0);
+ __raw_writel(~0, &g->set_falling);
+ __raw_writel(~0, &g->set_rising);
+
+ /* set the direct IRQs up to use that irqchip */
+ for (gpio = 0; gpio < soc_info->gpio_unbanked; gpio++, irq++) {
+ irq_set_chip(irq, &gpio_irqchip_unbanked);
+ irq_set_handler_data(irq, (void *)__gpio_mask(gpio));
+ irq_set_chip_data(irq, (__force void *)g);
+ irq_set_status_flags(irq, IRQ_TYPE_EDGE_BOTH);
+ }
+
+ goto done;
+ }
+
+ /*
+ * Or, AINTC can handle IRQs for banks of 16 GPIO IRQs, which we
+ * then chain through our own handler.
+ */
+ for (gpio = 0, irq = gpio_to_irq(0), bank = 0;
+ gpio < ngpio;
+ bank++, bank_irq++) {
+ unsigned i;
+
+ /* disabled by default, enabled only as needed */
+ g = gpio2regs(gpio);
+ __raw_writel(~0, &g->clr_falling);
+ __raw_writel(~0, &g->clr_rising);
+
+ /* set up all irqs in this bank */
+ irq_set_chained_handler(bank_irq, gpio_irq_handler);
+
+ /*
+ * Each chip handles 32 gpios, and each irq bank consists of 16
+ * gpio irqs. Pass the irq bank's corresponding controller to
+ * the chained irq handler.
+ */
+ irq_set_handler_data(bank_irq, &chips[gpio / 32]);
+
+ for (i = 0; i < 16 && gpio < ngpio; i++, irq++, gpio++) {
+ irq_set_chip(irq, &gpio_irqchip);
+ irq_set_chip_data(irq, (__force void *)g);
+ irq_set_handler_data(irq, (void *)__gpio_mask(gpio));
+ irq_set_handler(irq, handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ binten |= BIT(bank);
+ }
+
+done:
+ /* BINTEN -- per-bank interrupt enable. genirq would also let these
+ * bits be set/cleared dynamically.
+ */
+ __raw_writel(binten, gpio_base + 0x08);
+
+ printk(KERN_INFO "DaVinci: %d gpio irqs\n", irq - gpio_to_irq(0));
+
+ return 0;
+}
diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c
new file mode 100644
index 000000000000..1c0fc3756cb1
--- /dev/null
+++ b/drivers/gpio/gpio-ep93xx.c
@@ -0,0 +1,426 @@
+/*
+ * Generic EP93xx GPIO handling
+ *
+ * Copyright (c) 2008 Ryan Mallon
+ * Copyright (c) 2011 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on code originally from:
+ * linux/arch/arm/mach-ep93xx/core.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/basic_mmio_gpio.h>
+
+#include <mach/hardware.h>
+#include <mach/gpio-ep93xx.h>
+
+#define irq_to_gpio(irq) ((irq) - gpio_to_irq(0))
+
+struct ep93xx_gpio {
+ void __iomem *mmio_base;
+ struct bgpio_chip bgc[8];
+};
+
+/*************************************************************************
+ * Interrupt handling for EP93xx on-chip GPIOs
+ *************************************************************************/
+static unsigned char gpio_int_unmasked[3];
+static unsigned char gpio_int_enabled[3];
+static unsigned char gpio_int_type1[3];
+static unsigned char gpio_int_type2[3];
+static unsigned char gpio_int_debounce[3];
+
+/* Port ordering is: A B F */
+static const u8 int_type1_register_offset[3] = { 0x90, 0xac, 0x4c };
+static const u8 int_type2_register_offset[3] = { 0x94, 0xb0, 0x50 };
+static const u8 eoi_register_offset[3] = { 0x98, 0xb4, 0x54 };
+static const u8 int_en_register_offset[3] = { 0x9c, 0xb8, 0x58 };
+static const u8 int_debounce_register_offset[3] = { 0xa8, 0xc4, 0x64 };
+
+static void ep93xx_gpio_update_int_params(unsigned port)
+{
+ BUG_ON(port > 2);
+
+ __raw_writeb(0, EP93XX_GPIO_REG(int_en_register_offset[port]));
+
+ __raw_writeb(gpio_int_type2[port],
+ EP93XX_GPIO_REG(int_type2_register_offset[port]));
+
+ __raw_writeb(gpio_int_type1[port],
+ EP93XX_GPIO_REG(int_type1_register_offset[port]));
+
+ __raw_writeb(gpio_int_unmasked[port] & gpio_int_enabled[port],
+ EP93XX_GPIO_REG(int_en_register_offset[port]));
+}
+
+static inline void ep93xx_gpio_int_mask(unsigned line)
+{
+ gpio_int_unmasked[line >> 3] &= ~(1 << (line & 7));
+}
+
+static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable)
+{
+ int line = irq_to_gpio(irq);
+ int port = line >> 3;
+ int port_mask = 1 << (line & 7);
+
+ if (enable)
+ gpio_int_debounce[port] |= port_mask;
+ else
+ gpio_int_debounce[port] &= ~port_mask;
+
+ __raw_writeb(gpio_int_debounce[port],
+ EP93XX_GPIO_REG(int_debounce_register_offset[port]));
+}
+
+static void ep93xx_gpio_ab_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ unsigned char status;
+ int i;
+
+ status = __raw_readb(EP93XX_GPIO_A_INT_STATUS);
+ for (i = 0; i < 8; i++) {
+ if (status & (1 << i)) {
+ int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_A(0)) + i;
+ generic_handle_irq(gpio_irq);
+ }
+ }
+
+ status = __raw_readb(EP93XX_GPIO_B_INT_STATUS);
+ for (i = 0; i < 8; i++) {
+ if (status & (1 << i)) {
+ int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_B(0)) + i;
+ generic_handle_irq(gpio_irq);
+ }
+ }
+}
+
+static void ep93xx_gpio_f_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ /*
+ * map discontiguous hw irq range to continuous sw irq range:
+ *
+ * IRQ_EP93XX_GPIO{0..7}MUX -> gpio_to_irq(EP93XX_GPIO_LINE_F({0..7})
+ */
+ int port_f_idx = ((irq + 1) & 7) ^ 4; /* {19..22,47..50} -> {0..7} */
+ int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_F(0)) + port_f_idx;
+
+ generic_handle_irq(gpio_irq);
+}
+
+static void ep93xx_gpio_irq_ack(struct irq_data *d)
+{
+ int line = irq_to_gpio(d->irq);
+ int port = line >> 3;
+ int port_mask = 1 << (line & 7);
+
+ if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) {
+ gpio_int_type2[port] ^= port_mask; /* switch edge direction */
+ ep93xx_gpio_update_int_params(port);
+ }
+
+ __raw_writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port]));
+}
+
+static void ep93xx_gpio_irq_mask_ack(struct irq_data *d)
+{
+ int line = irq_to_gpio(d->irq);
+ int port = line >> 3;
+ int port_mask = 1 << (line & 7);
+
+ if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH)
+ gpio_int_type2[port] ^= port_mask; /* switch edge direction */
+
+ gpio_int_unmasked[port] &= ~port_mask;
+ ep93xx_gpio_update_int_params(port);
+
+ __raw_writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port]));
+}
+
+static void ep93xx_gpio_irq_mask(struct irq_data *d)
+{
+ int line = irq_to_gpio(d->irq);
+ int port = line >> 3;
+
+ gpio_int_unmasked[port] &= ~(1 << (line & 7));
+ ep93xx_gpio_update_int_params(port);
+}
+
+static void ep93xx_gpio_irq_unmask(struct irq_data *d)
+{
+ int line = irq_to_gpio(d->irq);
+ int port = line >> 3;
+
+ gpio_int_unmasked[port] |= 1 << (line & 7);
+ ep93xx_gpio_update_int_params(port);
+}
+
+/*
+ * gpio_int_type1 controls whether the interrupt is level (0) or
+ * edge (1) triggered, while gpio_int_type2 controls whether it
+ * triggers on low/falling (0) or high/rising (1).
+ */
+static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
+{
+ const int gpio = irq_to_gpio(d->irq);
+ const int port = gpio >> 3;
+ const int port_mask = 1 << (gpio & 7);
+ irq_flow_handler_t handler;
+
+ gpio_direction_input(gpio);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ gpio_int_type1[port] |= port_mask;
+ gpio_int_type2[port] |= port_mask;
+ handler = handle_edge_irq;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ gpio_int_type1[port] |= port_mask;
+ gpio_int_type2[port] &= ~port_mask;
+ handler = handle_edge_irq;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ gpio_int_type1[port] &= ~port_mask;
+ gpio_int_type2[port] |= port_mask;
+ handler = handle_level_irq;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ gpio_int_type1[port] &= ~port_mask;
+ gpio_int_type2[port] &= ~port_mask;
+ handler = handle_level_irq;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ gpio_int_type1[port] |= port_mask;
+ /* set initial polarity based on current input level */
+ if (gpio_get_value(gpio))
+ gpio_int_type2[port] &= ~port_mask; /* falling */
+ else
+ gpio_int_type2[port] |= port_mask; /* rising */
+ handler = handle_edge_irq;
+ break;
+ default:
+ pr_err("failed to set irq type %d for gpio %d\n", type, gpio);
+ return -EINVAL;
+ }
+
+ __irq_set_handler_locked(d->irq, handler);
+
+ gpio_int_enabled[port] |= port_mask;
+
+ ep93xx_gpio_update_int_params(port);
+
+ return 0;
+}
+
+static struct irq_chip ep93xx_gpio_irq_chip = {
+ .name = "GPIO",
+ .irq_ack = ep93xx_gpio_irq_ack,
+ .irq_mask_ack = ep93xx_gpio_irq_mask_ack,
+ .irq_mask = ep93xx_gpio_irq_mask,
+ .irq_unmask = ep93xx_gpio_irq_unmask,
+ .irq_set_type = ep93xx_gpio_irq_type,
+};
+
+static void ep93xx_gpio_init_irq(void)
+{
+ int gpio_irq;
+
+ for (gpio_irq = gpio_to_irq(0);
+ gpio_irq <= gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); ++gpio_irq) {
+ irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip,
+ handle_level_irq);
+ set_irq_flags(gpio_irq, IRQF_VALID);
+ }
+
+ irq_set_chained_handler(IRQ_EP93XX_GPIO_AB,
+ ep93xx_gpio_ab_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO0MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO1MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO2MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO3MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO4MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO5MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO6MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO7MUX,
+ ep93xx_gpio_f_irq_handler);
+}
+
+
+/*************************************************************************
+ * gpiolib interface for EP93xx on-chip GPIOs
+ *************************************************************************/
+struct ep93xx_gpio_bank {
+ const char *label;
+ int data;
+ int dir;
+ int base;
+ bool has_debounce;
+};
+
+#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _debounce) \
+ { \
+ .label = _label, \
+ .data = _data, \
+ .dir = _dir, \
+ .base = _base, \
+ .has_debounce = _debounce, \
+ }
+
+static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = {
+ EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true),
+ EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true),
+ EP93XX_GPIO_BANK("C", 0x08, 0x18, 40, false),
+ EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24, false),
+ EP93XX_GPIO_BANK("E", 0x20, 0x24, 32, false),
+ EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, true),
+ EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48, false),
+ EP93XX_GPIO_BANK("H", 0x40, 0x44, 56, false),
+};
+
+static int ep93xx_gpio_set_debounce(struct gpio_chip *chip,
+ unsigned offset, unsigned debounce)
+{
+ int gpio = chip->base + offset;
+ int irq = gpio_to_irq(gpio);
+
+ if (irq < 0)
+ return -EINVAL;
+
+ ep93xx_gpio_int_debounce(irq, debounce ? true : false);
+
+ return 0;
+}
+
+/*
+ * Map GPIO A0..A7 (0..7) to irq 64..71,
+ * B0..B7 (7..15) to irq 72..79, and
+ * F0..F7 (16..24) to irq 80..87.
+ */
+static int ep93xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ int gpio = chip->base + offset;
+
+ if (gpio > EP93XX_GPIO_LINE_MAX_IRQ)
+ return -EINVAL;
+
+ return 64 + gpio;
+}
+
+static int ep93xx_gpio_add_bank(struct bgpio_chip *bgc, struct device *dev,
+ void __iomem *mmio_base, struct ep93xx_gpio_bank *bank)
+{
+ void __iomem *data = mmio_base + bank->data;
+ void __iomem *dir = mmio_base + bank->dir;
+ int err;
+
+ err = bgpio_init(bgc, dev, 1, data, NULL, NULL, dir, NULL, false);
+ if (err)
+ return err;
+
+ bgc->gc.label = bank->label;
+ bgc->gc.base = bank->base;
+
+ if (bank->has_debounce) {
+ bgc->gc.set_debounce = ep93xx_gpio_set_debounce;
+ bgc->gc.to_irq = ep93xx_gpio_to_irq;
+ }
+
+ return gpiochip_add(&bgc->gc);
+}
+
+static int __devinit ep93xx_gpio_probe(struct platform_device *pdev)
+{
+ struct ep93xx_gpio *ep93xx_gpio;
+ struct resource *res;
+ void __iomem *mmio;
+ int i;
+ int ret;
+
+ ep93xx_gpio = kzalloc(sizeof(*ep93xx_gpio), GFP_KERNEL);
+ if (!ep93xx_gpio)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENXIO;
+ goto exit_free;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ ret = -EBUSY;
+ goto exit_free;
+ }
+
+ mmio = ioremap(res->start, resource_size(res));
+ if (!mmio) {
+ ret = -ENXIO;
+ goto exit_release;
+ }
+ ep93xx_gpio->mmio_base = mmio;
+
+ /* Default all ports to GPIO */
+ ep93xx_devcfg_set_bits(EP93XX_SYSCON_DEVCFG_KEYS |
+ EP93XX_SYSCON_DEVCFG_GONK |
+ EP93XX_SYSCON_DEVCFG_EONIDE |
+ EP93XX_SYSCON_DEVCFG_GONIDE |
+ EP93XX_SYSCON_DEVCFG_HONIDE);
+
+ for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) {
+ struct bgpio_chip *bgc = &ep93xx_gpio->bgc[i];
+ struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i];
+
+ if (ep93xx_gpio_add_bank(bgc, &pdev->dev, mmio, bank))
+ dev_warn(&pdev->dev, "Unable to add gpio bank %s\n",
+ bank->label);
+ }
+
+ ep93xx_gpio_init_irq();
+
+ return 0;
+
+exit_release:
+ release_mem_region(res->start, resource_size(res));
+exit_free:
+ kfree(ep93xx_gpio);
+ dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, ret);
+ return ret;
+}
+
+static struct platform_driver ep93xx_gpio_driver = {
+ .driver = {
+ .name = "gpio-ep93xx",
+ .owner = THIS_MODULE,
+ },
+ .probe = ep93xx_gpio_probe,
+};
+
+static int __init ep93xx_gpio_init(void)
+{
+ return platform_driver_register(&ep93xx_gpio_driver);
+}
+postcore_initcall(ep93xx_gpio_init);
+
+MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com> "
+ "H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_DESCRIPTION("EP93XX GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-exynos4.c b/drivers/gpio/gpio-exynos4.c
deleted file mode 100644
index 9029835112e7..000000000000
--- a/drivers/gpio/gpio-exynos4.c
+++ /dev/null
@@ -1,386 +0,0 @@
-/* linux/arch/arm/mach-exynos4/gpiolib.c
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * EXYNOS4 - GPIOlib support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/kernel.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-
-#include <mach/map.h>
-
-#include <plat/gpio-core.h>
-#include <plat/gpio-cfg.h>
-#include <plat/gpio-cfg-helpers.h>
-
-int s3c_gpio_setpull_exynos4(struct s3c_gpio_chip *chip,
- unsigned int off, s3c_gpio_pull_t pull)
-{
- if (pull == S3C_GPIO_PULL_UP)
- pull = 3;
-
- return s3c_gpio_setpull_updown(chip, off, pull);
-}
-
-s3c_gpio_pull_t s3c_gpio_getpull_exynos4(struct s3c_gpio_chip *chip,
- unsigned int off)
-{
- s3c_gpio_pull_t pull;
-
- pull = s3c_gpio_getpull_updown(chip, off);
- if (pull == 3)
- pull = S3C_GPIO_PULL_UP;
-
- return pull;
-}
-
-static struct s3c_gpio_cfg gpio_cfg = {
- .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
- .set_pull = s3c_gpio_setpull_exynos4,
- .get_pull = s3c_gpio_getpull_exynos4,
-};
-
-static struct s3c_gpio_cfg gpio_cfg_noint = {
- .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
- .set_pull = s3c_gpio_setpull_exynos4,
- .get_pull = s3c_gpio_getpull_exynos4,
-};
-
-/*
- * Following are the gpio banks in v310.
- *
- * The 'config' member when left to NULL, is initialized to the default
- * structure gpio_cfg in the init function below.
- *
- * The 'base' member is also initialized in the init function below.
- * Note: The initialization of 'base' member of s3c_gpio_chip structure
- * uses the above macro and depends on the banks being listed in order here.
- */
-static struct s3c_gpio_chip exynos4_gpio_part1_4bit[] = {
- {
- .chip = {
- .base = EXYNOS4_GPA0(0),
- .ngpio = EXYNOS4_GPIO_A0_NR,
- .label = "GPA0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPA1(0),
- .ngpio = EXYNOS4_GPIO_A1_NR,
- .label = "GPA1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPB(0),
- .ngpio = EXYNOS4_GPIO_B_NR,
- .label = "GPB",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPC0(0),
- .ngpio = EXYNOS4_GPIO_C0_NR,
- .label = "GPC0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPC1(0),
- .ngpio = EXYNOS4_GPIO_C1_NR,
- .label = "GPC1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPD0(0),
- .ngpio = EXYNOS4_GPIO_D0_NR,
- .label = "GPD0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPD1(0),
- .ngpio = EXYNOS4_GPIO_D1_NR,
- .label = "GPD1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPE0(0),
- .ngpio = EXYNOS4_GPIO_E0_NR,
- .label = "GPE0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPE1(0),
- .ngpio = EXYNOS4_GPIO_E1_NR,
- .label = "GPE1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPE2(0),
- .ngpio = EXYNOS4_GPIO_E2_NR,
- .label = "GPE2",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPE3(0),
- .ngpio = EXYNOS4_GPIO_E3_NR,
- .label = "GPE3",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPE4(0),
- .ngpio = EXYNOS4_GPIO_E4_NR,
- .label = "GPE4",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPF0(0),
- .ngpio = EXYNOS4_GPIO_F0_NR,
- .label = "GPF0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPF1(0),
- .ngpio = EXYNOS4_GPIO_F1_NR,
- .label = "GPF1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPF2(0),
- .ngpio = EXYNOS4_GPIO_F2_NR,
- .label = "GPF2",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPF3(0),
- .ngpio = EXYNOS4_GPIO_F3_NR,
- .label = "GPF3",
- },
- },
-};
-
-static struct s3c_gpio_chip exynos4_gpio_part2_4bit[] = {
- {
- .chip = {
- .base = EXYNOS4_GPJ0(0),
- .ngpio = EXYNOS4_GPIO_J0_NR,
- .label = "GPJ0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPJ1(0),
- .ngpio = EXYNOS4_GPIO_J1_NR,
- .label = "GPJ1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPK0(0),
- .ngpio = EXYNOS4_GPIO_K0_NR,
- .label = "GPK0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPK1(0),
- .ngpio = EXYNOS4_GPIO_K1_NR,
- .label = "GPK1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPK2(0),
- .ngpio = EXYNOS4_GPIO_K2_NR,
- .label = "GPK2",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPK3(0),
- .ngpio = EXYNOS4_GPIO_K3_NR,
- .label = "GPK3",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPL0(0),
- .ngpio = EXYNOS4_GPIO_L0_NR,
- .label = "GPL0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPL1(0),
- .ngpio = EXYNOS4_GPIO_L1_NR,
- .label = "GPL1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPL2(0),
- .ngpio = EXYNOS4_GPIO_L2_NR,
- .label = "GPL2",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = EXYNOS4_GPY0(0),
- .ngpio = EXYNOS4_GPIO_Y0_NR,
- .label = "GPY0",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = EXYNOS4_GPY1(0),
- .ngpio = EXYNOS4_GPIO_Y1_NR,
- .label = "GPY1",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = EXYNOS4_GPY2(0),
- .ngpio = EXYNOS4_GPIO_Y2_NR,
- .label = "GPY2",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = EXYNOS4_GPY3(0),
- .ngpio = EXYNOS4_GPIO_Y3_NR,
- .label = "GPY3",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = EXYNOS4_GPY4(0),
- .ngpio = EXYNOS4_GPIO_Y4_NR,
- .label = "GPY4",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = EXYNOS4_GPY5(0),
- .ngpio = EXYNOS4_GPIO_Y5_NR,
- .label = "GPY5",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = EXYNOS4_GPY6(0),
- .ngpio = EXYNOS4_GPIO_Y6_NR,
- .label = "GPY6",
- },
- }, {
- .base = (S5P_VA_GPIO2 + 0xC00),
- .config = &gpio_cfg_noint,
- .irq_base = IRQ_EINT(0),
- .chip = {
- .base = EXYNOS4_GPX0(0),
- .ngpio = EXYNOS4_GPIO_X0_NR,
- .label = "GPX0",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .base = (S5P_VA_GPIO2 + 0xC20),
- .config = &gpio_cfg_noint,
- .irq_base = IRQ_EINT(8),
- .chip = {
- .base = EXYNOS4_GPX1(0),
- .ngpio = EXYNOS4_GPIO_X1_NR,
- .label = "GPX1",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .base = (S5P_VA_GPIO2 + 0xC40),
- .config = &gpio_cfg_noint,
- .irq_base = IRQ_EINT(16),
- .chip = {
- .base = EXYNOS4_GPX2(0),
- .ngpio = EXYNOS4_GPIO_X2_NR,
- .label = "GPX2",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .base = (S5P_VA_GPIO2 + 0xC60),
- .config = &gpio_cfg_noint,
- .irq_base = IRQ_EINT(24),
- .chip = {
- .base = EXYNOS4_GPX3(0),
- .ngpio = EXYNOS4_GPIO_X3_NR,
- .label = "GPX3",
- .to_irq = samsung_gpiolib_to_irq,
- },
- },
-};
-
-static struct s3c_gpio_chip exynos4_gpio_part3_4bit[] = {
- {
- .chip = {
- .base = EXYNOS4_GPZ(0),
- .ngpio = EXYNOS4_GPIO_Z_NR,
- .label = "GPZ",
- },
- },
-};
-
-static __init int exynos4_gpiolib_init(void)
-{
- struct s3c_gpio_chip *chip;
- int i;
- int group = 0;
- int nr_chips;
-
- /* GPIO part 1 */
-
- chip = exynos4_gpio_part1_4bit;
- nr_chips = ARRAY_SIZE(exynos4_gpio_part1_4bit);
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (chip->config == NULL) {
- chip->config = &gpio_cfg;
- /* Assign the GPIO interrupt group */
- chip->group = group++;
- }
- if (chip->base == NULL)
- chip->base = S5P_VA_GPIO1 + (i) * 0x20;
- }
-
- samsung_gpiolib_add_4bit_chips(exynos4_gpio_part1_4bit, nr_chips);
-
- /* GPIO part 2 */
-
- chip = exynos4_gpio_part2_4bit;
- nr_chips = ARRAY_SIZE(exynos4_gpio_part2_4bit);
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (chip->config == NULL) {
- chip->config = &gpio_cfg;
- /* Assign the GPIO interrupt group */
- chip->group = group++;
- }
- if (chip->base == NULL)
- chip->base = S5P_VA_GPIO2 + (i) * 0x20;
- }
-
- samsung_gpiolib_add_4bit_chips(exynos4_gpio_part2_4bit, nr_chips);
-
- /* GPIO part 3 */
-
- chip = exynos4_gpio_part3_4bit;
- nr_chips = ARRAY_SIZE(exynos4_gpio_part3_4bit);
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (chip->config == NULL) {
- chip->config = &gpio_cfg;
- /* Assign the GPIO interrupt group */
- chip->group = group++;
- }
- if (chip->base == NULL)
- chip->base = S5P_VA_GPIO3 + (i) * 0x20;
- }
-
- samsung_gpiolib_add_4bit_chips(exynos4_gpio_part3_4bit, nr_chips);
- s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS);
- s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS);
-
- return 0;
-}
-core_initcall(exynos4_gpiolib_init);
diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/gpio-generic.c
index 8152e9f516b0..4e24436b0f82 100644
--- a/drivers/gpio/basic_mmio_gpio.c
+++ b/drivers/gpio/gpio-generic.c
@@ -1,5 +1,5 @@
/*
- * Driver for basic memory-mapped GPIO controllers.
+ * Generic driver for memory-mapped GPIO controllers.
*
* Copyright 2008 MontaVista Software, Inc.
* Copyright 2008,2010 Anton Vorontsov <cbouatmailru@gmail.com>
@@ -351,7 +351,7 @@ static int bgpio_setup_direction(struct bgpio_chip *bgc,
return 0;
}
-int __devexit bgpio_remove(struct bgpio_chip *bgc)
+int bgpio_remove(struct bgpio_chip *bgc)
{
int err = gpiochip_remove(&bgc->gc);
@@ -361,15 +361,10 @@ int __devexit bgpio_remove(struct bgpio_chip *bgc)
}
EXPORT_SYMBOL_GPL(bgpio_remove);
-int __devinit bgpio_init(struct bgpio_chip *bgc,
- struct device *dev,
- unsigned long sz,
- void __iomem *dat,
- void __iomem *set,
- void __iomem *clr,
- void __iomem *dirout,
- void __iomem *dirin,
- bool big_endian)
+int bgpio_init(struct bgpio_chip *bgc, struct device *dev,
+ unsigned long sz, void __iomem *dat, void __iomem *set,
+ void __iomem *clr, void __iomem *dirout, void __iomem *dirin,
+ bool big_endian)
{
int ret;
@@ -404,7 +399,7 @@ int __devinit bgpio_init(struct bgpio_chip *bgc,
}
EXPORT_SYMBOL_GPL(bgpio_init);
-#ifdef CONFIG_GPIO_BASIC_MMIO
+#ifdef CONFIG_GPIO_GENERIC_PLATFORM
static void __iomem *bgpio_map(struct platform_device *pdev,
const char *name,
@@ -541,7 +536,7 @@ static void __exit bgpio_platform_exit(void)
}
module_exit(bgpio_platform_exit);
-#endif /* CONFIG_GPIO_BASIC_MMIO */
+#endif /* CONFIG_GPIO_GENERIC_PLATFORM */
MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers");
MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
diff --git a/drivers/gpio/it8761e_gpio.c b/drivers/gpio/gpio-it8761e.c
index 48fc43c4bdd1..278b81317010 100644
--- a/drivers/gpio/it8761e_gpio.c
+++ b/drivers/gpio/gpio-it8761e.c
@@ -1,5 +1,5 @@
/*
- * it8761_gpio.c - GPIO interface for IT8761E Super I/O chip
+ * GPIO interface for IT8761E Super I/O chip
*
* Author: Denis Turischev <denis@compulab.co.il>
*
diff --git a/drivers/gpio/janz-ttl.c b/drivers/gpio/gpio-janz-ttl.c
index 813ac077e5d7..813ac077e5d7 100644
--- a/drivers/gpio/janz-ttl.c
+++ b/drivers/gpio/gpio-janz-ttl.c
diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c
new file mode 100644
index 000000000000..a3ac66ea364b
--- /dev/null
+++ b/drivers/gpio/gpio-ks8695.c
@@ -0,0 +1,319 @@
+/*
+ * arch/arm/mach-ks8695/gpio.c
+ *
+ * Copyright (C) 2006 Andrew Victor
+ * Updated to GPIOLIB, Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/gpio.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/mach/irq.h>
+
+#include <mach/regs-gpio.h>
+#include <mach/gpio-ks8695.h>
+
+/*
+ * Configure a GPIO line for either GPIO function, or its internal
+ * function (Interrupt, Timer, etc).
+ */
+static void ks8695_gpio_mode(unsigned int pin, short gpio)
+{
+ unsigned int enable[] = { IOPC_IOEINT0EN, IOPC_IOEINT1EN, IOPC_IOEINT2EN, IOPC_IOEINT3EN, IOPC_IOTIM0EN, IOPC_IOTIM1EN };
+ unsigned long x, flags;
+
+ if (pin > KS8695_GPIO_5) /* only GPIO 0..5 have internal functions */
+ return;
+
+ local_irq_save(flags);
+
+ x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC);
+ if (gpio) /* GPIO: set bit to 0 */
+ x &= ~enable[pin];
+ else /* Internal function: set bit to 1 */
+ x |= enable[pin];
+ __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPC);
+
+ local_irq_restore(flags);
+}
+
+
+static unsigned short gpio_irq[] = { KS8695_IRQ_EXTERN0, KS8695_IRQ_EXTERN1, KS8695_IRQ_EXTERN2, KS8695_IRQ_EXTERN3 };
+
+/*
+ * Configure GPIO pin as external interrupt source.
+ */
+int ks8695_gpio_interrupt(unsigned int pin, unsigned int type)
+{
+ unsigned long x, flags;
+
+ if (pin > KS8695_GPIO_3) /* only GPIO 0..3 can generate IRQ */
+ return -EINVAL;
+
+ local_irq_save(flags);
+
+ /* set pin as input */
+ x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM);
+ x &= ~IOPM(pin);
+ __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM);
+
+ local_irq_restore(flags);
+
+ /* Set IRQ triggering type */
+ irq_set_irq_type(gpio_irq[pin], type);
+
+ /* enable interrupt mode */
+ ks8695_gpio_mode(pin, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL(ks8695_gpio_interrupt);
+
+
+
+/* .... Generic GPIO interface .............................................. */
+
+/*
+ * Configure the GPIO line as an input.
+ */
+static int ks8695_gpio_direction_input(struct gpio_chip *gc, unsigned int pin)
+{
+ unsigned long x, flags;
+
+ if (pin > KS8695_GPIO_15)
+ return -EINVAL;
+
+ /* set pin to GPIO mode */
+ ks8695_gpio_mode(pin, 1);
+
+ local_irq_save(flags);
+
+ /* set pin as input */
+ x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM);
+ x &= ~IOPM(pin);
+ __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+
+/*
+ * Configure the GPIO line as an output, with default state.
+ */
+static int ks8695_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int pin, int state)
+{
+ unsigned long x, flags;
+
+ if (pin > KS8695_GPIO_15)
+ return -EINVAL;
+
+ /* set pin to GPIO mode */
+ ks8695_gpio_mode(pin, 1);
+
+ local_irq_save(flags);
+
+ /* set line state */
+ x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
+ if (state)
+ x |= IOPD(pin);
+ else
+ x &= ~IOPD(pin);
+ __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD);
+
+ /* set pin as output */
+ x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM);
+ x |= IOPM(pin);
+ __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+
+/*
+ * Set the state of an output GPIO line.
+ */
+static void ks8695_gpio_set_value(struct gpio_chip *gc,
+ unsigned int pin, int state)
+{
+ unsigned long x, flags;
+
+ if (pin > KS8695_GPIO_15)
+ return;
+
+ local_irq_save(flags);
+
+ /* set output line state */
+ x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
+ if (state)
+ x |= IOPD(pin);
+ else
+ x &= ~IOPD(pin);
+ __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD);
+
+ local_irq_restore(flags);
+}
+
+
+/*
+ * Read the state of a GPIO line.
+ */
+static int ks8695_gpio_get_value(struct gpio_chip *gc, unsigned int pin)
+{
+ unsigned long x;
+
+ if (pin > KS8695_GPIO_15)
+ return -EINVAL;
+
+ x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
+ return (x & IOPD(pin)) != 0;
+}
+
+
+/*
+ * Map GPIO line to IRQ number.
+ */
+static int ks8695_gpio_to_irq(struct gpio_chip *gc, unsigned int pin)
+{
+ if (pin > KS8695_GPIO_3) /* only GPIO 0..3 can generate IRQ */
+ return -EINVAL;
+
+ return gpio_irq[pin];
+}
+
+/*
+ * Map IRQ number to GPIO line.
+ */
+int irq_to_gpio(unsigned int irq)
+{
+ if ((irq < KS8695_IRQ_EXTERN0) || (irq > KS8695_IRQ_EXTERN3))
+ return -EINVAL;
+
+ return (irq - KS8695_IRQ_EXTERN0);
+}
+EXPORT_SYMBOL(irq_to_gpio);
+
+/* GPIOLIB interface */
+
+static struct gpio_chip ks8695_gpio_chip = {
+ .label = "KS8695",
+ .direction_input = ks8695_gpio_direction_input,
+ .direction_output = ks8695_gpio_direction_output,
+ .get = ks8695_gpio_get_value,
+ .set = ks8695_gpio_set_value,
+ .to_irq = ks8695_gpio_to_irq,
+ .base = 0,
+ .ngpio = 16,
+ .can_sleep = 0,
+};
+
+/* Register the GPIOs */
+void ks8695_register_gpios(void)
+{
+ if (gpiochip_add(&ks8695_gpio_chip))
+ printk(KERN_ERR "Unable to register core GPIOs\n");
+}
+
+/* .... Debug interface ..................................................... */
+
+#ifdef CONFIG_DEBUG_FS
+
+static int ks8695_gpio_show(struct seq_file *s, void *unused)
+{
+ unsigned int enable[] = { IOPC_IOEINT0EN, IOPC_IOEINT1EN, IOPC_IOEINT2EN, IOPC_IOEINT3EN, IOPC_IOTIM0EN, IOPC_IOTIM1EN };
+ unsigned int intmask[] = { IOPC_IOEINT0TM, IOPC_IOEINT1TM, IOPC_IOEINT2TM, IOPC_IOEINT3TM };
+ unsigned long mode, ctrl, data;
+ int i;
+
+ mode = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM);
+ ctrl = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC);
+ data = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
+
+ seq_printf(s, "Pin\tI/O\tFunction\tState\n\n");
+
+ for (i = KS8695_GPIO_0; i <= KS8695_GPIO_15 ; i++) {
+ seq_printf(s, "%i:\t", i);
+
+ seq_printf(s, "%s\t", (mode & IOPM(i)) ? "Output" : "Input");
+
+ if (i <= KS8695_GPIO_3) {
+ if (ctrl & enable[i]) {
+ seq_printf(s, "EXT%i ", i);
+
+ switch ((ctrl & intmask[i]) >> (4 * i)) {
+ case IOPC_TM_LOW:
+ seq_printf(s, "(Low)"); break;
+ case IOPC_TM_HIGH:
+ seq_printf(s, "(High)"); break;
+ case IOPC_TM_RISING:
+ seq_printf(s, "(Rising)"); break;
+ case IOPC_TM_FALLING:
+ seq_printf(s, "(Falling)"); break;
+ case IOPC_TM_EDGE:
+ seq_printf(s, "(Edges)"); break;
+ }
+ }
+ else
+ seq_printf(s, "GPIO\t");
+ }
+ else if (i <= KS8695_GPIO_5) {
+ if (ctrl & enable[i])
+ seq_printf(s, "TOUT%i\t", i - KS8695_GPIO_4);
+ else
+ seq_printf(s, "GPIO\t");
+ }
+ else
+ seq_printf(s, "GPIO\t");
+
+ seq_printf(s, "\t");
+
+ seq_printf(s, "%i\n", (data & IOPD(i)) ? 1 : 0);
+ }
+ return 0;
+}
+
+static int ks8695_gpio_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ks8695_gpio_show, NULL);
+}
+
+static const struct file_operations ks8695_gpio_operations = {
+ .open = ks8695_gpio_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init ks8695_gpio_debugfs_init(void)
+{
+ /* /sys/kernel/debug/ks8695_gpio */
+ (void) debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL, &ks8695_gpio_operations);
+ return 0;
+}
+postcore_initcall(ks8695_gpio_debugfs_init);
+
+#endif
diff --git a/drivers/gpio/langwell_gpio.c b/drivers/gpio/gpio-langwell.c
index 644ba1255d3c..00692e89ef87 100644
--- a/drivers/gpio/langwell_gpio.c
+++ b/drivers/gpio/gpio-langwell.c
@@ -1,4 +1,6 @@
-/* langwell_gpio.c Moorestown platform Langwell chip GPIO driver
+/*
+ * Moorestown platform Langwell chip GPIO driver
+ *
* Copyright (c) 2008 - 2009, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify
@@ -57,6 +59,7 @@ enum GPIO_REG {
GRER, /* rising edge detect */
GFER, /* falling edge detect */
GEDR, /* edge detect result */
+ GAFR, /* alt function */
};
struct lnw_gpio {
@@ -79,6 +82,31 @@ static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset,
return ptr;
}
+static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset,
+ enum GPIO_REG reg_type)
+{
+ struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip);
+ unsigned nreg = chip->ngpio / 32;
+ u8 reg = offset / 16;
+ void __iomem *ptr;
+
+ ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4);
+ return ptr;
+}
+
+static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ void __iomem *gafr = gpio_reg_2bit(chip, offset, GAFR);
+ u32 value = readl(gafr);
+ int shift = (offset % 16) << 1, af = (value >> shift) & 3;
+
+ if (af) {
+ value &= ~(3 << shift);
+ writel(value, gafr);
+ }
+ return 0;
+}
+
static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset)
{
void __iomem *gplr = gpio_reg(chip, offset, GPLR);
@@ -319,6 +347,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
lnw->reg_base = base;
lnw->irq_base = irq_base;
lnw->chip.label = dev_name(&pdev->dev);
+ lnw->chip.request = lnw_gpio_request;
lnw->chip.direction_input = lnw_gpio_direction_input;
lnw->chip.direction_output = lnw_gpio_direction_output;
lnw->chip.get = lnw_gpio_get;
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
new file mode 100644
index 000000000000..5b6948081f8f
--- /dev/null
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -0,0 +1,446 @@
+/*
+ * arch/arm/mach-lpc32xx/gpiolib.c
+ *
+ * Author: Kevin Wells <kevin.wells@nxp.com>
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+
+#include <mach/hardware.h>
+#include <mach/platform.h>
+#include <mach/gpio-lpc32xx.h>
+
+#define LPC32XX_GPIO_P3_INP_STATE _GPREG(0x000)
+#define LPC32XX_GPIO_P3_OUTP_SET _GPREG(0x004)
+#define LPC32XX_GPIO_P3_OUTP_CLR _GPREG(0x008)
+#define LPC32XX_GPIO_P3_OUTP_STATE _GPREG(0x00C)
+#define LPC32XX_GPIO_P2_DIR_SET _GPREG(0x010)
+#define LPC32XX_GPIO_P2_DIR_CLR _GPREG(0x014)
+#define LPC32XX_GPIO_P2_DIR_STATE _GPREG(0x018)
+#define LPC32XX_GPIO_P2_INP_STATE _GPREG(0x01C)
+#define LPC32XX_GPIO_P2_OUTP_SET _GPREG(0x020)
+#define LPC32XX_GPIO_P2_OUTP_CLR _GPREG(0x024)
+#define LPC32XX_GPIO_P2_MUX_SET _GPREG(0x028)
+#define LPC32XX_GPIO_P2_MUX_CLR _GPREG(0x02C)
+#define LPC32XX_GPIO_P2_MUX_STATE _GPREG(0x030)
+#define LPC32XX_GPIO_P0_INP_STATE _GPREG(0x040)
+#define LPC32XX_GPIO_P0_OUTP_SET _GPREG(0x044)
+#define LPC32XX_GPIO_P0_OUTP_CLR _GPREG(0x048)
+#define LPC32XX_GPIO_P0_OUTP_STATE _GPREG(0x04C)
+#define LPC32XX_GPIO_P0_DIR_SET _GPREG(0x050)
+#define LPC32XX_GPIO_P0_DIR_CLR _GPREG(0x054)
+#define LPC32XX_GPIO_P0_DIR_STATE _GPREG(0x058)
+#define LPC32XX_GPIO_P1_INP_STATE _GPREG(0x060)
+#define LPC32XX_GPIO_P1_OUTP_SET _GPREG(0x064)
+#define LPC32XX_GPIO_P1_OUTP_CLR _GPREG(0x068)
+#define LPC32XX_GPIO_P1_OUTP_STATE _GPREG(0x06C)
+#define LPC32XX_GPIO_P1_DIR_SET _GPREG(0x070)
+#define LPC32XX_GPIO_P1_DIR_CLR _GPREG(0x074)
+#define LPC32XX_GPIO_P1_DIR_STATE _GPREG(0x078)
+
+#define GPIO012_PIN_TO_BIT(x) (1 << (x))
+#define GPIO3_PIN_TO_BIT(x) (1 << ((x) + 25))
+#define GPO3_PIN_TO_BIT(x) (1 << (x))
+#define GPIO012_PIN_IN_SEL(x, y) (((x) >> (y)) & 1)
+#define GPIO3_PIN_IN_SHIFT(x) ((x) == 5 ? 24 : 10 + (x))
+#define GPIO3_PIN_IN_SEL(x, y) ((x) >> GPIO3_PIN_IN_SHIFT(y))
+#define GPIO3_PIN5_IN_SEL(x) (((x) >> 24) & 1)
+#define GPI3_PIN_IN_SEL(x, y) (((x) >> (y)) & 1)
+
+struct gpio_regs {
+ void __iomem *inp_state;
+ void __iomem *outp_set;
+ void __iomem *outp_clr;
+ void __iomem *dir_set;
+ void __iomem *dir_clr;
+};
+
+/*
+ * GPIO names
+ */
+static const char *gpio_p0_names[LPC32XX_GPIO_P0_MAX] = {
+ "p0.0", "p0.1", "p0.2", "p0.3",
+ "p0.4", "p0.5", "p0.6", "p0.7"
+};
+
+static const char *gpio_p1_names[LPC32XX_GPIO_P1_MAX] = {
+ "p1.0", "p1.1", "p1.2", "p1.3",
+ "p1.4", "p1.5", "p1.6", "p1.7",
+ "p1.8", "p1.9", "p1.10", "p1.11",
+ "p1.12", "p1.13", "p1.14", "p1.15",
+ "p1.16", "p1.17", "p1.18", "p1.19",
+ "p1.20", "p1.21", "p1.22", "p1.23",
+};
+
+static const char *gpio_p2_names[LPC32XX_GPIO_P2_MAX] = {
+ "p2.0", "p2.1", "p2.2", "p2.3",
+ "p2.4", "p2.5", "p2.6", "p2.7",
+ "p2.8", "p2.9", "p2.10", "p2.11",
+ "p2.12"
+};
+
+static const char *gpio_p3_names[LPC32XX_GPIO_P3_MAX] = {
+ "gpi000", "gpio01", "gpio02", "gpio03",
+ "gpio04", "gpio05"
+};
+
+static const char *gpi_p3_names[LPC32XX_GPI_P3_MAX] = {
+ "gpi00", "gpi01", "gpi02", "gpi03",
+ "gpi04", "gpi05", "gpi06", "gpi07",
+ "gpi08", "gpi09", NULL, NULL,
+ NULL, NULL, NULL, "gpi15",
+ "gpi16", "gpi17", "gpi18", "gpi19",
+ "gpi20", "gpi21", "gpi22", "gpi23",
+ "gpi24", "gpi25", "gpi26", "gpi27"
+};
+
+static const char *gpo_p3_names[LPC32XX_GPO_P3_MAX] = {
+ "gpo00", "gpo01", "gpo02", "gpo03",
+ "gpo04", "gpo05", "gpo06", "gpo07",
+ "gpo08", "gpo09", "gpo10", "gpo11",
+ "gpo12", "gpo13", "gpo14", "gpo15",
+ "gpo16", "gpo17", "gpo18", "gpo19",
+ "gpo20", "gpo21", "gpo22", "gpo23"
+};
+
+static struct gpio_regs gpio_grp_regs_p0 = {
+ .inp_state = LPC32XX_GPIO_P0_INP_STATE,
+ .outp_set = LPC32XX_GPIO_P0_OUTP_SET,
+ .outp_clr = LPC32XX_GPIO_P0_OUTP_CLR,
+ .dir_set = LPC32XX_GPIO_P0_DIR_SET,
+ .dir_clr = LPC32XX_GPIO_P0_DIR_CLR,
+};
+
+static struct gpio_regs gpio_grp_regs_p1 = {
+ .inp_state = LPC32XX_GPIO_P1_INP_STATE,
+ .outp_set = LPC32XX_GPIO_P1_OUTP_SET,
+ .outp_clr = LPC32XX_GPIO_P1_OUTP_CLR,
+ .dir_set = LPC32XX_GPIO_P1_DIR_SET,
+ .dir_clr = LPC32XX_GPIO_P1_DIR_CLR,
+};
+
+static struct gpio_regs gpio_grp_regs_p2 = {
+ .inp_state = LPC32XX_GPIO_P2_INP_STATE,
+ .outp_set = LPC32XX_GPIO_P2_OUTP_SET,
+ .outp_clr = LPC32XX_GPIO_P2_OUTP_CLR,
+ .dir_set = LPC32XX_GPIO_P2_DIR_SET,
+ .dir_clr = LPC32XX_GPIO_P2_DIR_CLR,
+};
+
+static struct gpio_regs gpio_grp_regs_p3 = {
+ .inp_state = LPC32XX_GPIO_P3_INP_STATE,
+ .outp_set = LPC32XX_GPIO_P3_OUTP_SET,
+ .outp_clr = LPC32XX_GPIO_P3_OUTP_CLR,
+ .dir_set = LPC32XX_GPIO_P2_DIR_SET,
+ .dir_clr = LPC32XX_GPIO_P2_DIR_CLR,
+};
+
+struct lpc32xx_gpio_chip {
+ struct gpio_chip chip;
+ struct gpio_regs *gpio_grp;
+};
+
+static inline struct lpc32xx_gpio_chip *to_lpc32xx_gpio(
+ struct gpio_chip *gpc)
+{
+ return container_of(gpc, struct lpc32xx_gpio_chip, chip);
+}
+
+static void __set_gpio_dir_p012(struct lpc32xx_gpio_chip *group,
+ unsigned pin, int input)
+{
+ if (input)
+ __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ group->gpio_grp->dir_clr);
+ else
+ __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ group->gpio_grp->dir_set);
+}
+
+static void __set_gpio_dir_p3(struct lpc32xx_gpio_chip *group,
+ unsigned pin, int input)
+{
+ u32 u = GPIO3_PIN_TO_BIT(pin);
+
+ if (input)
+ __raw_writel(u, group->gpio_grp->dir_clr);
+ else
+ __raw_writel(u, group->gpio_grp->dir_set);
+}
+
+static void __set_gpio_level_p012(struct lpc32xx_gpio_chip *group,
+ unsigned pin, int high)
+{
+ if (high)
+ __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ group->gpio_grp->outp_set);
+ else
+ __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ group->gpio_grp->outp_clr);
+}
+
+static void __set_gpio_level_p3(struct lpc32xx_gpio_chip *group,
+ unsigned pin, int high)
+{
+ u32 u = GPIO3_PIN_TO_BIT(pin);
+
+ if (high)
+ __raw_writel(u, group->gpio_grp->outp_set);
+ else
+ __raw_writel(u, group->gpio_grp->outp_clr);
+}
+
+static void __set_gpo_level_p3(struct lpc32xx_gpio_chip *group,
+ unsigned pin, int high)
+{
+ if (high)
+ __raw_writel(GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_set);
+ else
+ __raw_writel(GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_clr);
+}
+
+static int __get_gpio_state_p012(struct lpc32xx_gpio_chip *group,
+ unsigned pin)
+{
+ return GPIO012_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state),
+ pin);
+}
+
+static int __get_gpio_state_p3(struct lpc32xx_gpio_chip *group,
+ unsigned pin)
+{
+ int state = __raw_readl(group->gpio_grp->inp_state);
+
+ /*
+ * P3 GPIO pin input mapping is not contiguous, GPIOP3-0..4 is mapped
+ * to bits 10..14, while GPIOP3-5 is mapped to bit 24.
+ */
+ return GPIO3_PIN_IN_SEL(state, pin);
+}
+
+static int __get_gpi_state_p3(struct lpc32xx_gpio_chip *group,
+ unsigned pin)
+{
+ return GPI3_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state), pin);
+}
+
+/*
+ * GENERIC_GPIO primitives.
+ */
+static int lpc32xx_gpio_dir_input_p012(struct gpio_chip *chip,
+ unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_dir_p012(group, pin, 1);
+
+ return 0;
+}
+
+static int lpc32xx_gpio_dir_input_p3(struct gpio_chip *chip,
+ unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_dir_p3(group, pin, 1);
+
+ return 0;
+}
+
+static int lpc32xx_gpio_dir_in_always(struct gpio_chip *chip,
+ unsigned pin)
+{
+ return 0;
+}
+
+static int lpc32xx_gpio_get_value_p012(struct gpio_chip *chip, unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ return __get_gpio_state_p012(group, pin);
+}
+
+static int lpc32xx_gpio_get_value_p3(struct gpio_chip *chip, unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ return __get_gpio_state_p3(group, pin);
+}
+
+static int lpc32xx_gpi_get_value(struct gpio_chip *chip, unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ return __get_gpi_state_p3(group, pin);
+}
+
+static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_dir_p012(group, pin, 0);
+
+ return 0;
+}
+
+static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_dir_p3(group, pin, 0);
+
+ return 0;
+}
+
+static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ return 0;
+}
+
+static void lpc32xx_gpio_set_value_p012(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_level_p012(group, pin, value);
+}
+
+static void lpc32xx_gpio_set_value_p3(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_level_p3(group, pin, value);
+}
+
+static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpo_level_p3(group, pin, value);
+}
+
+static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin)
+{
+ if (pin < chip->ngpio)
+ return 0;
+
+ return -EINVAL;
+}
+
+static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = {
+ {
+ .chip = {
+ .label = "gpio_p0",
+ .direction_input = lpc32xx_gpio_dir_input_p012,
+ .get = lpc32xx_gpio_get_value_p012,
+ .direction_output = lpc32xx_gpio_dir_output_p012,
+ .set = lpc32xx_gpio_set_value_p012,
+ .request = lpc32xx_gpio_request,
+ .base = LPC32XX_GPIO_P0_GRP,
+ .ngpio = LPC32XX_GPIO_P0_MAX,
+ .names = gpio_p0_names,
+ .can_sleep = 0,
+ },
+ .gpio_grp = &gpio_grp_regs_p0,
+ },
+ {
+ .chip = {
+ .label = "gpio_p1",
+ .direction_input = lpc32xx_gpio_dir_input_p012,
+ .get = lpc32xx_gpio_get_value_p012,
+ .direction_output = lpc32xx_gpio_dir_output_p012,
+ .set = lpc32xx_gpio_set_value_p012,
+ .request = lpc32xx_gpio_request,
+ .base = LPC32XX_GPIO_P1_GRP,
+ .ngpio = LPC32XX_GPIO_P1_MAX,
+ .names = gpio_p1_names,
+ .can_sleep = 0,
+ },
+ .gpio_grp = &gpio_grp_regs_p1,
+ },
+ {
+ .chip = {
+ .label = "gpio_p2",
+ .direction_input = lpc32xx_gpio_dir_input_p012,
+ .get = lpc32xx_gpio_get_value_p012,
+ .direction_output = lpc32xx_gpio_dir_output_p012,
+ .set = lpc32xx_gpio_set_value_p012,
+ .request = lpc32xx_gpio_request,
+ .base = LPC32XX_GPIO_P2_GRP,
+ .ngpio = LPC32XX_GPIO_P2_MAX,
+ .names = gpio_p2_names,
+ .can_sleep = 0,
+ },
+ .gpio_grp = &gpio_grp_regs_p2,
+ },
+ {
+ .chip = {
+ .label = "gpio_p3",
+ .direction_input = lpc32xx_gpio_dir_input_p3,
+ .get = lpc32xx_gpio_get_value_p3,
+ .direction_output = lpc32xx_gpio_dir_output_p3,
+ .set = lpc32xx_gpio_set_value_p3,
+ .request = lpc32xx_gpio_request,
+ .base = LPC32XX_GPIO_P3_GRP,
+ .ngpio = LPC32XX_GPIO_P3_MAX,
+ .names = gpio_p3_names,
+ .can_sleep = 0,
+ },
+ .gpio_grp = &gpio_grp_regs_p3,
+ },
+ {
+ .chip = {
+ .label = "gpi_p3",
+ .direction_input = lpc32xx_gpio_dir_in_always,
+ .get = lpc32xx_gpi_get_value,
+ .request = lpc32xx_gpio_request,
+ .base = LPC32XX_GPI_P3_GRP,
+ .ngpio = LPC32XX_GPI_P3_MAX,
+ .names = gpi_p3_names,
+ .can_sleep = 0,
+ },
+ .gpio_grp = &gpio_grp_regs_p3,
+ },
+ {
+ .chip = {
+ .label = "gpo_p3",
+ .direction_output = lpc32xx_gpio_dir_out_always,
+ .set = lpc32xx_gpo_set_value,
+ .request = lpc32xx_gpio_request,
+ .base = LPC32XX_GPO_P3_GRP,
+ .ngpio = LPC32XX_GPO_P3_MAX,
+ .names = gpo_p3_names,
+ .can_sleep = 0,
+ },
+ .gpio_grp = &gpio_grp_regs_p3,
+ },
+};
+
+void __init lpc32xx_gpio_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lpc32xx_gpiochip); i++)
+ gpiochip_add(&lpc32xx_gpiochip[i].chip);
+}
diff --git a/drivers/gpio/max7300.c b/drivers/gpio/gpio-max7300.c
index 962f661c18c7..a5ca0ab1b372 100644
--- a/drivers/gpio/max7300.c
+++ b/drivers/gpio/gpio-max7300.c
@@ -1,6 +1,4 @@
/*
- * drivers/gpio/max7300.c
- *
* Copyright (C) 2009 Wolfram Sang, Pengutronix
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/gpio/max7301.c b/drivers/gpio/gpio-max7301.c
index 92a100ddef6b..741acfcbe761 100644
--- a/drivers/gpio/max7301.c
+++ b/drivers/gpio/gpio-max7301.c
@@ -1,6 +1,4 @@
/*
- * drivers/gpio/max7301.c
- *
* Copyright (C) 2006 Juergen Beisert, Pengutronix
* Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix
* Copyright (C) 2009 Wolfram Sang, Pengutronix
diff --git a/drivers/gpio/max730x.c b/drivers/gpio/gpio-max730x.c
index 94ce773f95f8..05e2dac60b3b 100644
--- a/drivers/gpio/max730x.c
+++ b/drivers/gpio/gpio-max730x.c
@@ -1,6 +1,4 @@
/**
- * drivers/gpio/max7301.c
- *
* Copyright (C) 2006 Juergen Beisert, Pengutronix
* Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix
* Copyright (C) 2009 Wolfram Sang, Pengutronix
diff --git a/drivers/gpio/max732x.c b/drivers/gpio/gpio-max732x.c
index ad6951edc16c..9504120812a5 100644
--- a/drivers/gpio/max732x.c
+++ b/drivers/gpio/gpio-max732x.c
@@ -1,5 +1,5 @@
/*
- * max732x.c - I2C Port Expander with 8/16 I/O
+ * MAX732x I2C Port Expander with 8/16 I/O
*
* Copyright (C) 2007 Marvell International Ltd.
* Copyright (C) 2008 Jack Ren <jack.ren@marvell.com>
diff --git a/drivers/gpio/mc33880.c b/drivers/gpio/gpio-mc33880.c
index 4ec797593bdb..2de57ce5feb6 100644
--- a/drivers/gpio/mc33880.c
+++ b/drivers/gpio/gpio-mc33880.c
@@ -1,5 +1,5 @@
/*
- * mc33880.c MC33880 high-side/low-side switch GPIO driver
+ * MC33880 high-side/low-side switch GPIO driver
* Copyright (c) 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
@@ -26,6 +26,7 @@
#include <linux/spi/mc33880.h>
#include <linux/gpio.h>
#include <linux/slab.h>
+#include <linux/module.h>
#define DRIVER_NAME "mc33880"
diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
index 40e076083ec0..c5d83a8a91c2 100644
--- a/drivers/gpio/mcp23s08.c
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -1,12 +1,13 @@
/*
- * mcp23s08.c - SPI gpio expander driver
+ * MCP23S08 SPI/GPIO gpio expander driver
*/
#include <linux/kernel.h>
#include <linux/device.h>
-#include <linux/workqueue.h>
#include <linux/mutex.h>
+#include <linux/module.h>
#include <linux/gpio.h>
+#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/spi/mcp23s08.h>
#include <linux/slab.h>
@@ -17,13 +18,13 @@
*/
#define MCP_TYPE_S08 0
#define MCP_TYPE_S17 1
+#define MCP_TYPE_008 2
+#define MCP_TYPE_017 3
/* Registers are all 8 bits wide.
*
* The mcp23s17 has twice as many bits, and can be configured to work
* with either 16 bit registers or with two adjacent 8 bit banks.
- *
- * Also, there are I2C versions of both chips.
*/
#define MCP_IODIR 0x00 /* init/reset: all ones */
#define MCP_IPOL 0x01
@@ -51,7 +52,6 @@ struct mcp23s08_ops {
};
struct mcp23s08 {
- struct spi_device *spi;
u8 addr;
u16 cache[11];
@@ -60,9 +60,8 @@ struct mcp23s08 {
struct gpio_chip chip;
- struct work_struct work;
-
const struct mcp23s08_ops *ops;
+ void *data; /* ops specific data */
};
/* A given spi_device can represent up to eight mcp23sxx chips
@@ -76,6 +75,74 @@ struct mcp23s08_driver_data {
struct mcp23s08 chip[];
};
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_I2C
+
+static int mcp23008_read(struct mcp23s08 *mcp, unsigned reg)
+{
+ return i2c_smbus_read_byte_data(mcp->data, reg);
+}
+
+static int mcp23008_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
+{
+ return i2c_smbus_write_byte_data(mcp->data, reg, val);
+}
+
+static int
+mcp23008_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
+{
+ while (n--) {
+ int ret = mcp23008_read(mcp, reg++);
+ if (ret < 0)
+ return ret;
+ *vals++ = ret;
+ }
+
+ return 0;
+}
+
+static int mcp23017_read(struct mcp23s08 *mcp, unsigned reg)
+{
+ return i2c_smbus_read_word_data(mcp->data, reg << 1);
+}
+
+static int mcp23017_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
+{
+ return i2c_smbus_write_word_data(mcp->data, reg << 1, val);
+}
+
+static int
+mcp23017_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
+{
+ while (n--) {
+ int ret = mcp23017_read(mcp, reg++);
+ if (ret < 0)
+ return ret;
+ *vals++ = ret;
+ }
+
+ return 0;
+}
+
+static const struct mcp23s08_ops mcp23008_ops = {
+ .read = mcp23008_read,
+ .write = mcp23008_write,
+ .read_regs = mcp23008_read_regs,
+};
+
+static const struct mcp23s08_ops mcp23017_ops = {
+ .read = mcp23017_read,
+ .write = mcp23017_write,
+ .read_regs = mcp23017_read_regs,
+};
+
+#endif /* CONFIG_I2C */
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_SPI_MASTER
+
static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
{
u8 tx[2], rx[1];
@@ -83,7 +150,7 @@ static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
tx[0] = mcp->addr | 0x01;
tx[1] = reg;
- status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx);
+ status = spi_write_then_read(mcp->data, tx, sizeof tx, rx, sizeof rx);
return (status < 0) ? status : rx[0];
}
@@ -94,7 +161,7 @@ static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
tx[0] = mcp->addr;
tx[1] = reg;
tx[2] = val;
- return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
+ return spi_write_then_read(mcp->data, tx, sizeof tx, NULL, 0);
}
static int
@@ -109,7 +176,7 @@ mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
tx[1] = reg;
tmp = (u8 *)vals;
- status = spi_write_then_read(mcp->spi, tx, sizeof tx, tmp, n);
+ status = spi_write_then_read(mcp->data, tx, sizeof tx, tmp, n);
if (status >= 0) {
while (n--)
vals[n] = tmp[n]; /* expand to 16bit */
@@ -124,7 +191,7 @@ static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg)
tx[0] = mcp->addr | 0x01;
tx[1] = reg << 1;
- status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx);
+ status = spi_write_then_read(mcp->data, tx, sizeof tx, rx, sizeof rx);
return (status < 0) ? status : (rx[0] | (rx[1] << 8));
}
@@ -136,7 +203,7 @@ static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
tx[1] = reg << 1;
tx[2] = val;
tx[3] = val >> 8;
- return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
+ return spi_write_then_read(mcp->data, tx, sizeof tx, NULL, 0);
}
static int
@@ -150,7 +217,7 @@ mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
tx[0] = mcp->addr | 0x01;
tx[1] = reg << 1;
- status = spi_write_then_read(mcp->spi, tx, sizeof tx,
+ status = spi_write_then_read(mcp->data, tx, sizeof tx,
(u8 *)vals, n * 2);
if (status >= 0) {
while (n--)
@@ -172,6 +239,7 @@ static const struct mcp23s08_ops mcp23s17_ops = {
.read_regs = mcp23s17_read_regs,
};
+#endif /* CONFIG_SPI_MASTER */
/*----------------------------------------------------------------------*/
@@ -299,17 +367,16 @@ done:
/*----------------------------------------------------------------------*/
-static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
+static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
+ void *data, unsigned addr,
unsigned type, unsigned base, unsigned pullups)
{
- struct mcp23s08_driver_data *data = spi_get_drvdata(spi);
- struct mcp23s08 *mcp = data->mcp[addr];
- int status;
+ int status;
mutex_init(&mcp->lock);
- mcp->spi = spi;
- mcp->addr = 0x40 | (addr << 1);
+ mcp->data = data;
+ mcp->addr = addr;
mcp->chip.direction_input = mcp23s08_direction_input;
mcp->chip.get = mcp23s08_get;
@@ -317,18 +384,43 @@ static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
mcp->chip.set = mcp23s08_set;
mcp->chip.dbg_show = mcp23s08_dbg_show;
- if (type == MCP_TYPE_S17) {
+ switch (type) {
+#ifdef CONFIG_SPI_MASTER
+ case MCP_TYPE_S08:
+ mcp->ops = &mcp23s08_ops;
+ mcp->chip.ngpio = 8;
+ mcp->chip.label = "mcp23s08";
+ break;
+
+ case MCP_TYPE_S17:
mcp->ops = &mcp23s17_ops;
mcp->chip.ngpio = 16;
mcp->chip.label = "mcp23s17";
- } else {
- mcp->ops = &mcp23s08_ops;
+ break;
+#endif /* CONFIG_SPI_MASTER */
+
+#ifdef CONFIG_I2C
+ case MCP_TYPE_008:
+ mcp->ops = &mcp23008_ops;
mcp->chip.ngpio = 8;
- mcp->chip.label = "mcp23s08";
+ mcp->chip.label = "mcp23008";
+ break;
+
+ case MCP_TYPE_017:
+ mcp->ops = &mcp23017_ops;
+ mcp->chip.ngpio = 16;
+ mcp->chip.label = "mcp23017";
+ break;
+#endif /* CONFIG_I2C */
+
+ default:
+ dev_err(dev, "invalid device type (%d)\n", type);
+ return -EINVAL;
}
+
mcp->chip.base = base;
mcp->chip.can_sleep = 1;
- mcp->chip.dev = &spi->dev;
+ mcp->chip.dev = dev;
mcp->chip.owner = THIS_MODULE;
/* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
@@ -374,11 +466,98 @@ static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
status = gpiochip_add(&mcp->chip);
fail:
if (status < 0)
- dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n",
- addr, status);
+ dev_dbg(dev, "can't setup chip %d, --> %d\n",
+ addr, status);
return status;
}
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_I2C
+
+static int __devinit mcp230xx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mcp23s08_platform_data *pdata;
+ struct mcp23s08 *mcp;
+ int status;
+
+ pdata = client->dev.platform_data;
+ if (!pdata || !gpio_is_valid(pdata->base)) {
+ dev_dbg(&client->dev, "invalid or missing platform data\n");
+ return -EINVAL;
+ }
+
+ mcp = kzalloc(sizeof *mcp, GFP_KERNEL);
+ if (!mcp)
+ return -ENOMEM;
+
+ status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr,
+ id->driver_data, pdata->base,
+ pdata->chip[0].pullups);
+ if (status)
+ goto fail;
+
+ i2c_set_clientdata(client, mcp);
+
+ return 0;
+
+fail:
+ kfree(mcp);
+
+ return status;
+}
+
+static int __devexit mcp230xx_remove(struct i2c_client *client)
+{
+ struct mcp23s08 *mcp = i2c_get_clientdata(client);
+ int status;
+
+ status = gpiochip_remove(&mcp->chip);
+ if (status == 0)
+ kfree(mcp);
+
+ return status;
+}
+
+static const struct i2c_device_id mcp230xx_id[] = {
+ { "mcp23008", MCP_TYPE_008 },
+ { "mcp23017", MCP_TYPE_017 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, mcp230xx_id);
+
+static struct i2c_driver mcp230xx_driver = {
+ .driver = {
+ .name = "mcp230xx",
+ .owner = THIS_MODULE,
+ },
+ .probe = mcp230xx_probe,
+ .remove = __devexit_p(mcp230xx_remove),
+ .id_table = mcp230xx_id,
+};
+
+static int __init mcp23s08_i2c_init(void)
+{
+ return i2c_add_driver(&mcp230xx_driver);
+}
+
+static void mcp23s08_i2c_exit(void)
+{
+ i2c_del_driver(&mcp230xx_driver);
+}
+
+#else
+
+static int __init mcp23s08_i2c_init(void) { return 0; }
+static void mcp23s08_i2c_exit(void) { }
+
+#endif /* CONFIG_I2C */
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_SPI_MASTER
+
static int mcp23s08_probe(struct spi_device *spi)
{
struct mcp23s08_platform_data *pdata;
@@ -421,7 +600,8 @@ static int mcp23s08_probe(struct spi_device *spi)
continue;
chips--;
data->mcp[addr] = &data->chip[chips];
- status = mcp23s08_probe_one(spi, addr, type, base,
+ status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi,
+ 0x40 | (addr << 1), type, base,
pdata->chip[addr].pullups);
if (status < 0)
goto fail;
@@ -435,14 +615,6 @@ static int mcp23s08_probe(struct spi_device *spi)
* handled here...
*/
- if (pdata->setup) {
- status = pdata->setup(spi,
- pdata->base, data->ngpio,
- pdata->context);
- if (status < 0)
- dev_dbg(&spi->dev, "setup --> %d\n", status);
- }
-
return 0;
fail:
@@ -462,20 +634,9 @@ fail:
static int mcp23s08_remove(struct spi_device *spi)
{
struct mcp23s08_driver_data *data = spi_get_drvdata(spi);
- struct mcp23s08_platform_data *pdata = spi->dev.platform_data;
unsigned addr;
int status = 0;
- if (pdata->teardown) {
- status = pdata->teardown(spi,
- pdata->base, data->ngpio,
- pdata->context);
- if (status < 0) {
- dev_err(&spi->dev, "%s --> %d\n", "teardown", status);
- return status;
- }
- }
-
for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
int tmp;
@@ -510,20 +671,53 @@ static struct spi_driver mcp23s08_driver = {
},
};
+static int __init mcp23s08_spi_init(void)
+{
+ return spi_register_driver(&mcp23s08_driver);
+}
+
+static void mcp23s08_spi_exit(void)
+{
+ spi_unregister_driver(&mcp23s08_driver);
+}
+
+#else
+
+static int __init mcp23s08_spi_init(void) { return 0; }
+static void mcp23s08_spi_exit(void) { }
+
+#endif /* CONFIG_SPI_MASTER */
+
/*----------------------------------------------------------------------*/
static int __init mcp23s08_init(void)
{
- return spi_register_driver(&mcp23s08_driver);
+ int ret;
+
+ ret = mcp23s08_spi_init();
+ if (ret)
+ goto spi_fail;
+
+ ret = mcp23s08_i2c_init();
+ if (ret)
+ goto i2c_fail;
+
+ return 0;
+
+ i2c_fail:
+ mcp23s08_spi_exit();
+ spi_fail:
+ return ret;
}
-/* register after spi postcore initcall and before
+/* register after spi/i2c postcore initcall and before
* subsys initcalls that may rely on these GPIOs
*/
subsys_initcall(mcp23s08_init);
static void __exit mcp23s08_exit(void)
{
- spi_unregister_driver(&mcp23s08_driver);
+ mcp23s08_spi_exit();
+ mcp23s08_i2c_exit();
}
module_exit(mcp23s08_exit);
diff --git a/drivers/gpio/ml_ioh_gpio.c b/drivers/gpio/gpio-ml-ioh.c
index 1bc621ac3536..ea8e73869250 100644
--- a/drivers/gpio/ml_ioh_gpio.c
+++ b/drivers/gpio/gpio-ml-ioh.c
@@ -14,10 +14,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#define IOH_EDGE_FALLING 0
+#define IOH_EDGE_RISING BIT(0)
+#define IOH_LEVEL_L BIT(1)
+#define IOH_LEVEL_H (BIT(0) | BIT(1))
+#define IOH_EDGE_BOTH BIT(2)
+#define IOH_IM_MASK (BIT(0) | BIT(1) | BIT(2))
+
+#define IOH_IRQ_BASE 0
#define PCI_VENDOR_ID_ROHM 0x10DB
@@ -46,12 +58,22 @@ struct ioh_regs {
/**
* struct ioh_gpio_reg_data - The register store data.
+ * @ien_reg To store contents of interrupt enable register.
+ * @imask_reg: To store contents of interrupt mask regist
* @po_reg: To store contents of PO register.
* @pm_reg: To store contents of PM register.
+ * @im0_reg: To store contents of interrupt mode regist0
+ * @im1_reg: To store contents of interrupt mode regist1
+ * @use_sel_reg: To store contents of GPIO_USE_SEL0~3
*/
struct ioh_gpio_reg_data {
+ u32 ien_reg;
+ u32 imask_reg;
u32 po_reg;
u32 pm_reg;
+ u32 im0_reg;
+ u32 im1_reg;
+ u32 use_sel_reg;
};
/**
@@ -62,7 +84,11 @@ struct ioh_gpio_reg_data {
* @gpio: Data for GPIO infrastructure.
* @ioh_gpio_reg: Memory mapped Register data is saved here
* when suspend.
+ * @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM
* @ch: Indicate GPIO channel
+ * @irq_base: Save base of IRQ number for interrupt
+ * @spinlock: Used for register access protection in
+ * interrupt context ioh_irq_type and PM;
*/
struct ioh_gpio {
void __iomem *base;
@@ -70,8 +96,11 @@ struct ioh_gpio {
struct device *dev;
struct gpio_chip gpio;
struct ioh_gpio_reg_data ioh_gpio_reg;
+ u32 gpio_use_sel;
struct mutex lock;
int ch;
+ int irq_base;
+ spinlock_t spinlock;
};
static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12};
@@ -145,8 +174,25 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
*/
static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
{
- chip->ioh_gpio_reg.po_reg = ioread32(&chip->reg->regs[chip->ch].po);
- chip->ioh_gpio_reg.pm_reg = ioread32(&chip->reg->regs[chip->ch].pm);
+ int i;
+
+ for (i = 0; i < 8; i ++, chip++) {
+ chip->ioh_gpio_reg.po_reg =
+ ioread32(&chip->reg->regs[chip->ch].po);
+ chip->ioh_gpio_reg.pm_reg =
+ ioread32(&chip->reg->regs[chip->ch].pm);
+ chip->ioh_gpio_reg.ien_reg =
+ ioread32(&chip->reg->regs[chip->ch].ien);
+ chip->ioh_gpio_reg.imask_reg =
+ ioread32(&chip->reg->regs[chip->ch].imask);
+ chip->ioh_gpio_reg.im0_reg =
+ ioread32(&chip->reg->regs[chip->ch].im_0);
+ chip->ioh_gpio_reg.im1_reg =
+ ioread32(&chip->reg->regs[chip->ch].im_1);
+ if (i < 4)
+ chip->ioh_gpio_reg.use_sel_reg =
+ ioread32(&chip->reg->ioh_sel_reg[i]);
+ }
}
/*
@@ -154,13 +200,34 @@ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
*/
static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip)
{
- /* to store contents of PO register */
- iowrite32(chip->ioh_gpio_reg.po_reg, &chip->reg->regs[chip->ch].po);
- /* to store contents of PM register */
- iowrite32(chip->ioh_gpio_reg.pm_reg, &chip->reg->regs[chip->ch].pm);
+ int i;
+
+ for (i = 0; i < 8; i ++, chip++) {
+ iowrite32(chip->ioh_gpio_reg.po_reg,
+ &chip->reg->regs[chip->ch].po);
+ iowrite32(chip->ioh_gpio_reg.pm_reg,
+ &chip->reg->regs[chip->ch].pm);
+ iowrite32(chip->ioh_gpio_reg.ien_reg,
+ &chip->reg->regs[chip->ch].ien);
+ iowrite32(chip->ioh_gpio_reg.imask_reg,
+ &chip->reg->regs[chip->ch].imask);
+ iowrite32(chip->ioh_gpio_reg.im0_reg,
+ &chip->reg->regs[chip->ch].im_0);
+ iowrite32(chip->ioh_gpio_reg.im1_reg,
+ &chip->reg->regs[chip->ch].im_1);
+ if (i < 4)
+ iowrite32(chip->ioh_gpio_reg.use_sel_reg,
+ &chip->reg->ioh_sel_reg[i]);
+ }
}
#endif
+static int ioh_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+{
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+ return chip->irq_base + offset;
+}
+
static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port)
{
struct gpio_chip *gpio = &chip->gpio;
@@ -175,16 +242,148 @@ static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port)
gpio->base = -1;
gpio->ngpio = num_port;
gpio->can_sleep = 0;
+ gpio->to_irq = ioh_gpio_to_irq;
+}
+
+static int ioh_irq_type(struct irq_data *d, unsigned int type)
+{
+ u32 im;
+ u32 *im_reg;
+ u32 ien;
+ u32 im_pos;
+ int ch;
+ unsigned long flags;
+ u32 val;
+ int irq = d->irq;
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct ioh_gpio *chip = gc->private;
+
+ ch = irq - chip->irq_base;
+ if (irq <= chip->irq_base + 7) {
+ im_reg = &chip->reg->regs[chip->ch].im_0;
+ im_pos = ch;
+ } else {
+ im_reg = &chip->reg->regs[chip->ch].im_1;
+ im_pos = ch - 8;
+ }
+ dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d type=%d\n",
+ __func__, irq, type, ch, im_pos, type);
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ val = IOH_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ val = IOH_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ val = IOH_EDGE_BOTH;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ val = IOH_LEVEL_H;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ val = IOH_LEVEL_L;
+ break;
+ case IRQ_TYPE_PROBE:
+ goto end;
+ default:
+ dev_warn(chip->dev, "%s: unknown type(%dd)",
+ __func__, type);
+ goto end;
+ }
+
+ /* Set interrupt mode */
+ im = ioread32(im_reg) & ~(IOH_IM_MASK << (im_pos * 4));
+ iowrite32(im | (val << (im_pos * 4)), im_reg);
+
+ /* iclr */
+ iowrite32(BIT(ch), &chip->reg->regs[chip->ch].iclr);
+
+ /* IMASKCLR */
+ iowrite32(BIT(ch), &chip->reg->regs[chip->ch].imaskclr);
+
+ /* Enable interrupt */
+ ien = ioread32(&chip->reg->regs[chip->ch].ien);
+ iowrite32(ien | BIT(ch), &chip->reg->regs[chip->ch].ien);
+end:
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+
+static void ioh_irq_unmask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct ioh_gpio *chip = gc->private;
+
+ iowrite32(1 << (d->irq - chip->irq_base),
+ &chip->reg->regs[chip->ch].imaskclr);
+}
+
+static void ioh_irq_mask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct ioh_gpio *chip = gc->private;
+
+ iowrite32(1 << (d->irq - chip->irq_base),
+ &chip->reg->regs[chip->ch].imask);
+}
+
+static irqreturn_t ioh_gpio_handler(int irq, void *dev_id)
+{
+ struct ioh_gpio *chip = dev_id;
+ u32 reg_val;
+ int i, j;
+ int ret = IRQ_NONE;
+
+ for (i = 0; i < 8; i++) {
+ reg_val = ioread32(&chip->reg->regs[i].istatus);
+ for (j = 0; j < num_ports[i]; j++) {
+ if (reg_val & BIT(j)) {
+ dev_dbg(chip->dev,
+ "%s:[%d]:irq=%d status=0x%x\n",
+ __func__, j, irq, reg_val);
+ iowrite32(BIT(j),
+ &chip->reg->regs[chip->ch].iclr);
+ generic_handle_irq(chip->irq_base + j);
+ ret = IRQ_HANDLED;
+ }
+ }
+ }
+ return ret;
+}
+
+static __devinit void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip,
+ unsigned int irq_start, unsigned int num)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("ioh_gpio", 1, irq_start, chip->base,
+ handle_simple_irq);
+ gc->private = chip;
+ ct = gc->chip_types;
+
+ ct->chip.irq_mask = ioh_irq_mask;
+ ct->chip.irq_unmask = ioh_irq_unmask;
+ ct->chip.irq_set_type = ioh_irq_type;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
+ IRQ_NOREQUEST | IRQ_NOPROBE, 0);
}
static int __devinit ioh_gpio_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int ret;
- int i;
+ int i, j;
struct ioh_gpio *chip;
void __iomem *base;
void __iomem *chip_save;
+ int irq_base;
ret = pci_enable_device(pdev);
if (ret) {
@@ -228,12 +427,43 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev,
}
chip = chip_save;
+ for (j = 0; j < 8; j++, chip++) {
+ irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j],
+ NUMA_NO_NODE);
+ if (irq_base < 0) {
+ dev_warn(&pdev->dev,
+ "ml_ioh_gpio: Failed to get IRQ base num\n");
+ chip->irq_base = -1;
+ goto err_irq_alloc_descs;
+ }
+ chip->irq_base = irq_base;
+ ioh_gpio_alloc_generic_chip(chip, irq_base, num_ports[j]);
+ }
+
+ chip = chip_save;
+ ret = request_irq(pdev->irq, ioh_gpio_handler,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (ret != 0) {
+ dev_err(&pdev->dev,
+ "%s request_irq failed\n", __func__);
+ goto err_request_irq;
+ }
+
pci_set_drvdata(pdev, chip);
return 0;
+err_request_irq:
+ chip = chip_save;
+err_irq_alloc_descs:
+ while (--j >= 0) {
+ chip--;
+ irq_free_descs(chip->irq_base, num_ports[j]);
+ }
+
+ chip = chip_save;
err_gpiochip_add:
- for (; i != 0; i--) {
+ while (--i >= 0) {
chip--;
ret = gpiochip_remove(&chip->gpio);
if (ret)
@@ -264,7 +494,11 @@ static void __devexit ioh_gpio_remove(struct pci_dev *pdev)
void __iomem *chip_save;
chip_save = chip;
+
+ free_irq(pdev->irq, chip);
+
for (i = 0; i < 8; i++, chip++) {
+ irq_free_descs(chip->irq_base, num_ports[i]);
err = gpiochip_remove(&chip->gpio);
if (err)
dev_err(&pdev->dev, "Failed gpiochip_remove\n");
@@ -282,9 +516,11 @@ static int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
{
s32 ret;
struct ioh_gpio *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
+ spin_lock_irqsave(&chip->spinlock, flags);
ioh_gpio_save_reg_conf(chip);
- ioh_gpio_restore_reg_conf(chip);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
ret = pci_save_state(pdev);
if (ret) {
@@ -304,6 +540,7 @@ static int ioh_gpio_resume(struct pci_dev *pdev)
{
s32 ret;
struct ioh_gpio *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
ret = pci_enable_wake(pdev, PCI_D0, 0);
@@ -315,9 +552,11 @@ static int ioh_gpio_resume(struct pci_dev *pdev)
}
pci_restore_state(pdev);
+ spin_lock_irqsave(&chip->spinlock, flags);
iowrite32(0x01, &chip->reg->srst);
iowrite32(0x00, &chip->reg->srst);
ioh_gpio_restore_reg_conf(chip);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
}
diff --git a/drivers/gpio/gpio-mpc5200.c b/drivers/gpio/gpio-mpc5200.c
new file mode 100644
index 000000000000..2c7cef367fc0
--- /dev/null
+++ b/drivers/gpio/gpio-mpc5200.c
@@ -0,0 +1,377 @@
+/*
+ * MPC52xx gpio driver
+ *
+ * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/of.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+
+#include <asm/gpio.h>
+#include <asm/mpc52xx.h>
+#include <sysdev/fsl_soc.h>
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+struct mpc52xx_gpiochip {
+ struct of_mm_gpio_chip mmchip;
+ unsigned int shadow_dvo;
+ unsigned int shadow_gpioe;
+ unsigned int shadow_ddr;
+};
+
+/*
+ * GPIO LIB API implementation for wakeup GPIOs.
+ *
+ * There's a maximum of 8 wakeup GPIOs. Which of these are available
+ * for use depends on your board setup.
+ *
+ * 0 -> GPIO_WKUP_7
+ * 1 -> GPIO_WKUP_6
+ * 2 -> PSC6_1
+ * 3 -> PSC6_0
+ * 4 -> ETH_17
+ * 5 -> PSC3_9
+ * 6 -> PSC2_4
+ * 7 -> PSC1_4
+ *
+ */
+static int mpc52xx_wkup_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+ unsigned int ret;
+
+ ret = (in_8(&regs->wkup_ival) >> (7 - gpio)) & 1;
+
+ pr_debug("%s: gpio: %d ret: %d\n", __func__, gpio, ret);
+
+ return ret;
+}
+
+static inline void
+__mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+
+ if (val)
+ chip->shadow_dvo |= 1 << (7 - gpio);
+ else
+ chip->shadow_dvo &= ~(1 << (7 - gpio));
+
+ out_8(&regs->wkup_dvo, chip->shadow_dvo);
+}
+
+static void
+mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ __mpc52xx_wkup_gpio_set(gc, gpio, val);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
+}
+
+static int mpc52xx_wkup_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* set the direction */
+ chip->shadow_ddr &= ~(1 << (7 - gpio));
+ out_8(&regs->wkup_ddr, chip->shadow_ddr);
+
+ /* and enable the pin */
+ chip->shadow_gpioe |= 1 << (7 - gpio);
+ out_8(&regs->wkup_gpioe, chip->shadow_gpioe);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+
+static int
+mpc52xx_wkup_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ __mpc52xx_wkup_gpio_set(gc, gpio, val);
+
+ /* Then set direction */
+ chip->shadow_ddr |= 1 << (7 - gpio);
+ out_8(&regs->wkup_ddr, chip->shadow_ddr);
+
+ /* Finally enable the pin */
+ chip->shadow_gpioe |= 1 << (7 - gpio);
+ out_8(&regs->wkup_gpioe, chip->shadow_gpioe);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
+
+ return 0;
+}
+
+static int __devinit mpc52xx_wkup_gpiochip_probe(struct platform_device *ofdev)
+{
+ struct mpc52xx_gpiochip *chip;
+ struct mpc52xx_gpio_wkup __iomem *regs;
+ struct gpio_chip *gc;
+ int ret;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ gc = &chip->mmchip.gc;
+
+ gc->ngpio = 8;
+ gc->direction_input = mpc52xx_wkup_gpio_dir_in;
+ gc->direction_output = mpc52xx_wkup_gpio_dir_out;
+ gc->get = mpc52xx_wkup_gpio_get;
+ gc->set = mpc52xx_wkup_gpio_set;
+
+ ret = of_mm_gpiochip_add(ofdev->dev.of_node, &chip->mmchip);
+ if (ret)
+ return ret;
+
+ regs = chip->mmchip.regs;
+ chip->shadow_gpioe = in_8(&regs->wkup_gpioe);
+ chip->shadow_ddr = in_8(&regs->wkup_ddr);
+ chip->shadow_dvo = in_8(&regs->wkup_dvo);
+
+ return 0;
+}
+
+static int mpc52xx_gpiochip_remove(struct platform_device *ofdev)
+{
+ return -EBUSY;
+}
+
+static const struct of_device_id mpc52xx_wkup_gpiochip_match[] = {
+ { .compatible = "fsl,mpc5200-gpio-wkup", },
+ {}
+};
+
+static struct platform_driver mpc52xx_wkup_gpiochip_driver = {
+ .driver = {
+ .name = "mpc5200-gpio-wkup",
+ .owner = THIS_MODULE,
+ .of_match_table = mpc52xx_wkup_gpiochip_match,
+ },
+ .probe = mpc52xx_wkup_gpiochip_probe,
+ .remove = mpc52xx_gpiochip_remove,
+};
+
+/*
+ * GPIO LIB API implementation for simple GPIOs
+ *
+ * There's a maximum of 32 simple GPIOs. Which of these are available
+ * for use depends on your board setup.
+ * The numbering reflects the bit numbering in the port registers:
+ *
+ * 0..1 > reserved
+ * 2..3 > IRDA
+ * 4..7 > ETHR
+ * 8..11 > reserved
+ * 12..15 > USB
+ * 16..17 > reserved
+ * 18..23 > PSC3
+ * 24..27 > PSC2
+ * 28..31 > PSC1
+ */
+static int mpc52xx_simple_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+ unsigned int ret;
+
+ ret = (in_be32(&regs->simple_ival) >> (31 - gpio)) & 1;
+
+ return ret;
+}
+
+static inline void
+__mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+
+ if (val)
+ chip->shadow_dvo |= 1 << (31 - gpio);
+ else
+ chip->shadow_dvo &= ~(1 << (31 - gpio));
+ out_be32(&regs->simple_dvo, chip->shadow_dvo);
+}
+
+static void
+mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ __mpc52xx_simple_gpio_set(gc, gpio, val);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
+}
+
+static int mpc52xx_simple_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* set the direction */
+ chip->shadow_ddr &= ~(1 << (31 - gpio));
+ out_be32(&regs->simple_ddr, chip->shadow_ddr);
+
+ /* and enable the pin */
+ chip->shadow_gpioe |= 1 << (31 - gpio);
+ out_be32(&regs->simple_gpioe, chip->shadow_gpioe);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+
+static int
+mpc52xx_simple_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* First set initial value */
+ __mpc52xx_simple_gpio_set(gc, gpio, val);
+
+ /* Then set direction */
+ chip->shadow_ddr |= 1 << (31 - gpio);
+ out_be32(&regs->simple_ddr, chip->shadow_ddr);
+
+ /* Finally enable the pin */
+ chip->shadow_gpioe |= 1 << (31 - gpio);
+ out_be32(&regs->simple_gpioe, chip->shadow_gpioe);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
+
+ return 0;
+}
+
+static int __devinit mpc52xx_simple_gpiochip_probe(struct platform_device *ofdev)
+{
+ struct mpc52xx_gpiochip *chip;
+ struct gpio_chip *gc;
+ struct mpc52xx_gpio __iomem *regs;
+ int ret;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ gc = &chip->mmchip.gc;
+
+ gc->ngpio = 32;
+ gc->direction_input = mpc52xx_simple_gpio_dir_in;
+ gc->direction_output = mpc52xx_simple_gpio_dir_out;
+ gc->get = mpc52xx_simple_gpio_get;
+ gc->set = mpc52xx_simple_gpio_set;
+
+ ret = of_mm_gpiochip_add(ofdev->dev.of_node, &chip->mmchip);
+ if (ret)
+ return ret;
+
+ regs = chip->mmchip.regs;
+ chip->shadow_gpioe = in_be32(&regs->simple_gpioe);
+ chip->shadow_ddr = in_be32(&regs->simple_ddr);
+ chip->shadow_dvo = in_be32(&regs->simple_dvo);
+
+ return 0;
+}
+
+static const struct of_device_id mpc52xx_simple_gpiochip_match[] = {
+ { .compatible = "fsl,mpc5200-gpio", },
+ {}
+};
+
+static struct platform_driver mpc52xx_simple_gpiochip_driver = {
+ .driver = {
+ .name = "mpc5200-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = mpc52xx_simple_gpiochip_match,
+ },
+ .probe = mpc52xx_simple_gpiochip_probe,
+ .remove = mpc52xx_gpiochip_remove,
+};
+
+static int __init mpc52xx_gpio_init(void)
+{
+ if (platform_driver_register(&mpc52xx_wkup_gpiochip_driver))
+ printk(KERN_ERR "Unable to register wakeup GPIO driver\n");
+
+ if (platform_driver_register(&mpc52xx_simple_gpiochip_driver))
+ printk(KERN_ERR "Unable to register simple GPIO driver\n");
+
+ return 0;
+}
+
+
+/* Make sure we get initialised before anyone else tries to use us */
+subsys_initcall(mpc52xx_gpio_init);
+
+/* No exit call at the moment as we cannot unregister of gpio chips */
+
+MODULE_DESCRIPTION("Freescale MPC52xx gpio driver");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
new file mode 100644
index 000000000000..ec3fcf0a7e12
--- /dev/null
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -0,0 +1,398 @@
+/*
+ * GPIOs on MPC512x/8349/8572/8610 and compatible
+ *
+ * Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+
+#define MPC8XXX_GPIO_PINS 32
+
+#define GPIO_DIR 0x00
+#define GPIO_ODR 0x04
+#define GPIO_DAT 0x08
+#define GPIO_IER 0x0c
+#define GPIO_IMR 0x10
+#define GPIO_ICR 0x14
+#define GPIO_ICR2 0x18
+
+struct mpc8xxx_gpio_chip {
+ struct of_mm_gpio_chip mm_gc;
+ spinlock_t lock;
+
+ /*
+ * shadowed data register to be able to clear/set output pins in
+ * open drain mode safely
+ */
+ u32 data;
+ struct irq_host *irq;
+ void *of_dev_id_data;
+};
+
+static inline u32 mpc8xxx_gpio2mask(unsigned int gpio)
+{
+ return 1u << (MPC8XXX_GPIO_PINS - 1 - gpio);
+}
+
+static inline struct mpc8xxx_gpio_chip *
+to_mpc8xxx_gpio_chip(struct of_mm_gpio_chip *mm)
+{
+ return container_of(mm, struct mpc8xxx_gpio_chip, mm_gc);
+}
+
+static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+
+ mpc8xxx_gc->data = in_be32(mm->regs + GPIO_DAT);
+}
+
+/* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs
+ * defined as output cannot be determined by reading GPDAT register,
+ * so we use shadow data register instead. The status of input pins
+ * is determined by reading GPDAT register.
+ */
+static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ u32 val;
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+
+ val = in_be32(mm->regs + GPIO_DAT) & ~in_be32(mm->regs + GPIO_DIR);
+
+ return (val | mpc8xxx_gc->data) & mpc8xxx_gpio2mask(gpio);
+}
+
+static int mpc8xxx_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+
+ return in_be32(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio);
+}
+
+static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ if (val)
+ mpc8xxx_gc->data |= mpc8xxx_gpio2mask(gpio);
+ else
+ mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(gpio);
+
+ out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data);
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+}
+
+static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio));
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+
+ return 0;
+}
+
+static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+ unsigned long flags;
+
+ mpc8xxx_gpio_set(gc, gpio, val);
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio));
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+
+ return 0;
+}
+
+static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+
+ if (mpc8xxx_gc->irq && offset < MPC8XXX_GPIO_PINS)
+ return irq_create_mapping(mpc8xxx_gc->irq, offset);
+ else
+ return -ENXIO;
+}
+
+static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned int mask;
+
+ mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->regs + GPIO_IMR);
+ if (mask)
+ generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq,
+ 32 - ffs(mask)));
+ chip->irq_eoi(&desc->irq_data);
+}
+
+static void mpc8xxx_irq_unmask(struct irq_data *d)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+}
+
+static void mpc8xxx_irq_mask(struct irq_data *d)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+}
+
+static void mpc8xxx_irq_ack(struct irq_data *d)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+
+ out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+}
+
+static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned long flags;
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_FALLING:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ setbits32(mm->regs + GPIO_ICR,
+ mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrbits32(mm->regs + GPIO_ICR,
+ mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned long gpio = irqd_to_hwirq(d);
+ void __iomem *reg;
+ unsigned int shift;
+ unsigned long flags;
+
+ if (gpio < 16) {
+ reg = mm->regs + GPIO_ICR;
+ shift = (15 - gpio) * 2;
+ } else {
+ reg = mm->regs + GPIO_ICR2;
+ shift = (15 - (gpio % 16)) * 2;
+ }
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_LEVEL_LOW:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrsetbits_be32(reg, 3 << shift, 2 << shift);
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_LEVEL_HIGH:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrsetbits_be32(reg, 3 << shift, 1 << shift);
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrbits32(reg, 3 << shift);
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct irq_chip mpc8xxx_irq_chip = {
+ .name = "mpc8xxx-gpio",
+ .irq_unmask = mpc8xxx_irq_unmask,
+ .irq_mask = mpc8xxx_irq_mask,
+ .irq_ack = mpc8xxx_irq_ack,
+ .irq_set_type = mpc8xxx_irq_set_type,
+};
+
+static int mpc8xxx_gpio_irq_map(struct irq_host *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = h->host_data;
+
+ if (mpc8xxx_gc->of_dev_id_data)
+ mpc8xxx_irq_chip.irq_set_type = mpc8xxx_gc->of_dev_id_data;
+
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_chip_and_handler(virq, &mpc8xxx_irq_chip, handle_level_irq);
+ irq_set_irq_type(virq, IRQ_TYPE_NONE);
+
+ return 0;
+}
+
+static int mpc8xxx_gpio_irq_xlate(struct irq_host *h, struct device_node *ct,
+ const u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_flags)
+
+{
+ /* interrupt sense values coming from the device tree equal either
+ * EDGE_FALLING or EDGE_BOTH
+ */
+ *out_hwirq = intspec[0];
+ *out_flags = intspec[1];
+
+ return 0;
+}
+
+static struct irq_host_ops mpc8xxx_gpio_irq_ops = {
+ .map = mpc8xxx_gpio_irq_map,
+ .xlate = mpc8xxx_gpio_irq_xlate,
+};
+
+static struct of_device_id mpc8xxx_gpio_ids[] __initdata = {
+ { .compatible = "fsl,mpc8349-gpio", },
+ { .compatible = "fsl,mpc8572-gpio", },
+ { .compatible = "fsl,mpc8610-gpio", },
+ { .compatible = "fsl,mpc5121-gpio", .data = mpc512x_irq_set_type, },
+ { .compatible = "fsl,pq3-gpio", },
+ { .compatible = "fsl,qoriq-gpio", },
+ {}
+};
+
+static void __init mpc8xxx_add_controller(struct device_node *np)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc;
+ struct of_mm_gpio_chip *mm_gc;
+ struct gpio_chip *gc;
+ const struct of_device_id *id;
+ unsigned hwirq;
+ int ret;
+
+ mpc8xxx_gc = kzalloc(sizeof(*mpc8xxx_gc), GFP_KERNEL);
+ if (!mpc8xxx_gc) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ spin_lock_init(&mpc8xxx_gc->lock);
+
+ mm_gc = &mpc8xxx_gc->mm_gc;
+ gc = &mm_gc->gc;
+
+ mm_gc->save_regs = mpc8xxx_gpio_save_regs;
+ gc->ngpio = MPC8XXX_GPIO_PINS;
+ gc->direction_input = mpc8xxx_gpio_dir_in;
+ gc->direction_output = mpc8xxx_gpio_dir_out;
+ if (of_device_is_compatible(np, "fsl,mpc8572-gpio"))
+ gc->get = mpc8572_gpio_get;
+ else
+ gc->get = mpc8xxx_gpio_get;
+ gc->set = mpc8xxx_gpio_set;
+ gc->to_irq = mpc8xxx_gpio_to_irq;
+
+ ret = of_mm_gpiochip_add(np, mm_gc);
+ if (ret)
+ goto err;
+
+ hwirq = irq_of_parse_and_map(np, 0);
+ if (hwirq == NO_IRQ)
+ goto skip_irq;
+
+ mpc8xxx_gc->irq =
+ irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, MPC8XXX_GPIO_PINS,
+ &mpc8xxx_gpio_irq_ops, MPC8XXX_GPIO_PINS);
+ if (!mpc8xxx_gc->irq)
+ goto skip_irq;
+
+ id = of_match_node(mpc8xxx_gpio_ids, np);
+ if (id)
+ mpc8xxx_gc->of_dev_id_data = id->data;
+
+ mpc8xxx_gc->irq->host_data = mpc8xxx_gc;
+
+ /* ack and mask all irqs */
+ out_be32(mm_gc->regs + GPIO_IER, 0xffffffff);
+ out_be32(mm_gc->regs + GPIO_IMR, 0);
+
+ irq_set_handler_data(hwirq, mpc8xxx_gc);
+ irq_set_chained_handler(hwirq, mpc8xxx_gpio_irq_cascade);
+
+skip_irq:
+ return;
+
+err:
+ pr_err("%s: registration failed with status %d\n",
+ np->full_name, ret);
+ kfree(mpc8xxx_gc);
+
+ return;
+}
+
+static int __init mpc8xxx_add_gpiochips(void)
+{
+ struct device_node *np;
+
+ for_each_matching_node(np, mpc8xxx_gpio_ids)
+ mpc8xxx_add_controller(np);
+
+ return 0;
+}
+arch_initcall(mpc8xxx_add_gpiochips);
diff --git a/drivers/gpio/gpio-msm-v1.c b/drivers/gpio/gpio-msm-v1.c
new file mode 100644
index 000000000000..52a4d4286eba
--- /dev/null
+++ b/drivers/gpio/gpio-msm-v1.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <mach/cpu.h>
+#include <mach/msm_gpiomux.h>
+#include <mach/msm_iomap.h>
+
+/* see 80-VA736-2 Rev C pp 695-751
+**
+** These are actually the *shadow* gpio registers, since the
+** real ones (which allow full access) are only available to the
+** ARM9 side of the world.
+**
+** Since the _BASE need to be page-aligned when we're mapping them
+** to virtual addresses, adjust for the additional offset in these
+** macros.
+*/
+
+#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + (off))
+#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off))
+#define MSM_GPIO1_SHADOW_REG(off) (MSM_GPIO1_BASE + 0x800 + (off))
+#define MSM_GPIO2_SHADOW_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off))
+
+/*
+ * MSM7X00 registers
+ */
+/* output value */
+#define MSM7X00_GPIO_OUT_0 MSM_GPIO1_SHADOW_REG(0x00) /* gpio 15-0 */
+#define MSM7X00_GPIO_OUT_1 MSM_GPIO2_SHADOW_REG(0x00) /* gpio 42-16 */
+#define MSM7X00_GPIO_OUT_2 MSM_GPIO1_SHADOW_REG(0x04) /* gpio 67-43 */
+#define MSM7X00_GPIO_OUT_3 MSM_GPIO1_SHADOW_REG(0x08) /* gpio 94-68 */
+#define MSM7X00_GPIO_OUT_4 MSM_GPIO1_SHADOW_REG(0x0C) /* gpio 106-95 */
+#define MSM7X00_GPIO_OUT_5 MSM_GPIO1_SHADOW_REG(0x50) /* gpio 107-121 */
+
+/* same pin map as above, output enable */
+#define MSM7X00_GPIO_OE_0 MSM_GPIO1_SHADOW_REG(0x10)
+#define MSM7X00_GPIO_OE_1 MSM_GPIO2_SHADOW_REG(0x08)
+#define MSM7X00_GPIO_OE_2 MSM_GPIO1_SHADOW_REG(0x14)
+#define MSM7X00_GPIO_OE_3 MSM_GPIO1_SHADOW_REG(0x18)
+#define MSM7X00_GPIO_OE_4 MSM_GPIO1_SHADOW_REG(0x1C)
+#define MSM7X00_GPIO_OE_5 MSM_GPIO1_SHADOW_REG(0x54)
+
+/* same pin map as above, input read */
+#define MSM7X00_GPIO_IN_0 MSM_GPIO1_SHADOW_REG(0x34)
+#define MSM7X00_GPIO_IN_1 MSM_GPIO2_SHADOW_REG(0x20)
+#define MSM7X00_GPIO_IN_2 MSM_GPIO1_SHADOW_REG(0x38)
+#define MSM7X00_GPIO_IN_3 MSM_GPIO1_SHADOW_REG(0x3C)
+#define MSM7X00_GPIO_IN_4 MSM_GPIO1_SHADOW_REG(0x40)
+#define MSM7X00_GPIO_IN_5 MSM_GPIO1_SHADOW_REG(0x44)
+
+/* same pin map as above, 1=edge 0=level interrup */
+#define MSM7X00_GPIO_INT_EDGE_0 MSM_GPIO1_SHADOW_REG(0x60)
+#define MSM7X00_GPIO_INT_EDGE_1 MSM_GPIO2_SHADOW_REG(0x50)
+#define MSM7X00_GPIO_INT_EDGE_2 MSM_GPIO1_SHADOW_REG(0x64)
+#define MSM7X00_GPIO_INT_EDGE_3 MSM_GPIO1_SHADOW_REG(0x68)
+#define MSM7X00_GPIO_INT_EDGE_4 MSM_GPIO1_SHADOW_REG(0x6C)
+#define MSM7X00_GPIO_INT_EDGE_5 MSM_GPIO1_SHADOW_REG(0xC0)
+
+/* same pin map as above, 1=positive 0=negative */
+#define MSM7X00_GPIO_INT_POS_0 MSM_GPIO1_SHADOW_REG(0x70)
+#define MSM7X00_GPIO_INT_POS_1 MSM_GPIO2_SHADOW_REG(0x58)
+#define MSM7X00_GPIO_INT_POS_2 MSM_GPIO1_SHADOW_REG(0x74)
+#define MSM7X00_GPIO_INT_POS_3 MSM_GPIO1_SHADOW_REG(0x78)
+#define MSM7X00_GPIO_INT_POS_4 MSM_GPIO1_SHADOW_REG(0x7C)
+#define MSM7X00_GPIO_INT_POS_5 MSM_GPIO1_SHADOW_REG(0xBC)
+
+/* same pin map as above, interrupt enable */
+#define MSM7X00_GPIO_INT_EN_0 MSM_GPIO1_SHADOW_REG(0x80)
+#define MSM7X00_GPIO_INT_EN_1 MSM_GPIO2_SHADOW_REG(0x60)
+#define MSM7X00_GPIO_INT_EN_2 MSM_GPIO1_SHADOW_REG(0x84)
+#define MSM7X00_GPIO_INT_EN_3 MSM_GPIO1_SHADOW_REG(0x88)
+#define MSM7X00_GPIO_INT_EN_4 MSM_GPIO1_SHADOW_REG(0x8C)
+#define MSM7X00_GPIO_INT_EN_5 MSM_GPIO1_SHADOW_REG(0xB8)
+
+/* same pin map as above, write 1 to clear interrupt */
+#define MSM7X00_GPIO_INT_CLEAR_0 MSM_GPIO1_SHADOW_REG(0x90)
+#define MSM7X00_GPIO_INT_CLEAR_1 MSM_GPIO2_SHADOW_REG(0x68)
+#define MSM7X00_GPIO_INT_CLEAR_2 MSM_GPIO1_SHADOW_REG(0x94)
+#define MSM7X00_GPIO_INT_CLEAR_3 MSM_GPIO1_SHADOW_REG(0x98)
+#define MSM7X00_GPIO_INT_CLEAR_4 MSM_GPIO1_SHADOW_REG(0x9C)
+#define MSM7X00_GPIO_INT_CLEAR_5 MSM_GPIO1_SHADOW_REG(0xB4)
+
+/* same pin map as above, 1=interrupt pending */
+#define MSM7X00_GPIO_INT_STATUS_0 MSM_GPIO1_SHADOW_REG(0xA0)
+#define MSM7X00_GPIO_INT_STATUS_1 MSM_GPIO2_SHADOW_REG(0x70)
+#define MSM7X00_GPIO_INT_STATUS_2 MSM_GPIO1_SHADOW_REG(0xA4)
+#define MSM7X00_GPIO_INT_STATUS_3 MSM_GPIO1_SHADOW_REG(0xA8)
+#define MSM7X00_GPIO_INT_STATUS_4 MSM_GPIO1_SHADOW_REG(0xAC)
+#define MSM7X00_GPIO_INT_STATUS_5 MSM_GPIO1_SHADOW_REG(0xB0)
+
+/*
+ * QSD8X50 registers
+ */
+/* output value */
+#define QSD8X50_GPIO_OUT_0 MSM_GPIO1_SHADOW_REG(0x00) /* gpio 15-0 */
+#define QSD8X50_GPIO_OUT_1 MSM_GPIO2_SHADOW_REG(0x00) /* gpio 42-16 */
+#define QSD8X50_GPIO_OUT_2 MSM_GPIO1_SHADOW_REG(0x04) /* gpio 67-43 */
+#define QSD8X50_GPIO_OUT_3 MSM_GPIO1_SHADOW_REG(0x08) /* gpio 94-68 */
+#define QSD8X50_GPIO_OUT_4 MSM_GPIO1_SHADOW_REG(0x0C) /* gpio 103-95 */
+#define QSD8X50_GPIO_OUT_5 MSM_GPIO1_SHADOW_REG(0x10) /* gpio 121-104 */
+#define QSD8X50_GPIO_OUT_6 MSM_GPIO1_SHADOW_REG(0x14) /* gpio 152-122 */
+#define QSD8X50_GPIO_OUT_7 MSM_GPIO1_SHADOW_REG(0x18) /* gpio 164-153 */
+
+/* same pin map as above, output enable */
+#define QSD8X50_GPIO_OE_0 MSM_GPIO1_SHADOW_REG(0x20)
+#define QSD8X50_GPIO_OE_1 MSM_GPIO2_SHADOW_REG(0x08)
+#define QSD8X50_GPIO_OE_2 MSM_GPIO1_SHADOW_REG(0x24)
+#define QSD8X50_GPIO_OE_3 MSM_GPIO1_SHADOW_REG(0x28)
+#define QSD8X50_GPIO_OE_4 MSM_GPIO1_SHADOW_REG(0x2C)
+#define QSD8X50_GPIO_OE_5 MSM_GPIO1_SHADOW_REG(0x30)
+#define QSD8X50_GPIO_OE_6 MSM_GPIO1_SHADOW_REG(0x34)
+#define QSD8X50_GPIO_OE_7 MSM_GPIO1_SHADOW_REG(0x38)
+
+/* same pin map as above, input read */
+#define QSD8X50_GPIO_IN_0 MSM_GPIO1_SHADOW_REG(0x50)
+#define QSD8X50_GPIO_IN_1 MSM_GPIO2_SHADOW_REG(0x20)
+#define QSD8X50_GPIO_IN_2 MSM_GPIO1_SHADOW_REG(0x54)
+#define QSD8X50_GPIO_IN_3 MSM_GPIO1_SHADOW_REG(0x58)
+#define QSD8X50_GPIO_IN_4 MSM_GPIO1_SHADOW_REG(0x5C)
+#define QSD8X50_GPIO_IN_5 MSM_GPIO1_SHADOW_REG(0x60)
+#define QSD8X50_GPIO_IN_6 MSM_GPIO1_SHADOW_REG(0x64)
+#define QSD8X50_GPIO_IN_7 MSM_GPIO1_SHADOW_REG(0x68)
+
+/* same pin map as above, 1=edge 0=level interrup */
+#define QSD8X50_GPIO_INT_EDGE_0 MSM_GPIO1_SHADOW_REG(0x70)
+#define QSD8X50_GPIO_INT_EDGE_1 MSM_GPIO2_SHADOW_REG(0x50)
+#define QSD8X50_GPIO_INT_EDGE_2 MSM_GPIO1_SHADOW_REG(0x74)
+#define QSD8X50_GPIO_INT_EDGE_3 MSM_GPIO1_SHADOW_REG(0x78)
+#define QSD8X50_GPIO_INT_EDGE_4 MSM_GPIO1_SHADOW_REG(0x7C)
+#define QSD8X50_GPIO_INT_EDGE_5 MSM_GPIO1_SHADOW_REG(0x80)
+#define QSD8X50_GPIO_INT_EDGE_6 MSM_GPIO1_SHADOW_REG(0x84)
+#define QSD8X50_GPIO_INT_EDGE_7 MSM_GPIO1_SHADOW_REG(0x88)
+
+/* same pin map as above, 1=positive 0=negative */
+#define QSD8X50_GPIO_INT_POS_0 MSM_GPIO1_SHADOW_REG(0x90)
+#define QSD8X50_GPIO_INT_POS_1 MSM_GPIO2_SHADOW_REG(0x58)
+#define QSD8X50_GPIO_INT_POS_2 MSM_GPIO1_SHADOW_REG(0x94)
+#define QSD8X50_GPIO_INT_POS_3 MSM_GPIO1_SHADOW_REG(0x98)
+#define QSD8X50_GPIO_INT_POS_4 MSM_GPIO1_SHADOW_REG(0x9C)
+#define QSD8X50_GPIO_INT_POS_5 MSM_GPIO1_SHADOW_REG(0xA0)
+#define QSD8X50_GPIO_INT_POS_6 MSM_GPIO1_SHADOW_REG(0xA4)
+#define QSD8X50_GPIO_INT_POS_7 MSM_GPIO1_SHADOW_REG(0xA8)
+
+/* same pin map as above, interrupt enable */
+#define QSD8X50_GPIO_INT_EN_0 MSM_GPIO1_SHADOW_REG(0xB0)
+#define QSD8X50_GPIO_INT_EN_1 MSM_GPIO2_SHADOW_REG(0x60)
+#define QSD8X50_GPIO_INT_EN_2 MSM_GPIO1_SHADOW_REG(0xB4)
+#define QSD8X50_GPIO_INT_EN_3 MSM_GPIO1_SHADOW_REG(0xB8)
+#define QSD8X50_GPIO_INT_EN_4 MSM_GPIO1_SHADOW_REG(0xBC)
+#define QSD8X50_GPIO_INT_EN_5 MSM_GPIO1_SHADOW_REG(0xC0)
+#define QSD8X50_GPIO_INT_EN_6 MSM_GPIO1_SHADOW_REG(0xC4)
+#define QSD8X50_GPIO_INT_EN_7 MSM_GPIO1_SHADOW_REG(0xC8)
+
+/* same pin map as above, write 1 to clear interrupt */
+#define QSD8X50_GPIO_INT_CLEAR_0 MSM_GPIO1_SHADOW_REG(0xD0)
+#define QSD8X50_GPIO_INT_CLEAR_1 MSM_GPIO2_SHADOW_REG(0x68)
+#define QSD8X50_GPIO_INT_CLEAR_2 MSM_GPIO1_SHADOW_REG(0xD4)
+#define QSD8X50_GPIO_INT_CLEAR_3 MSM_GPIO1_SHADOW_REG(0xD8)
+#define QSD8X50_GPIO_INT_CLEAR_4 MSM_GPIO1_SHADOW_REG(0xDC)
+#define QSD8X50_GPIO_INT_CLEAR_5 MSM_GPIO1_SHADOW_REG(0xE0)
+#define QSD8X50_GPIO_INT_CLEAR_6 MSM_GPIO1_SHADOW_REG(0xE4)
+#define QSD8X50_GPIO_INT_CLEAR_7 MSM_GPIO1_SHADOW_REG(0xE8)
+
+/* same pin map as above, 1=interrupt pending */
+#define QSD8X50_GPIO_INT_STATUS_0 MSM_GPIO1_SHADOW_REG(0xF0)
+#define QSD8X50_GPIO_INT_STATUS_1 MSM_GPIO2_SHADOW_REG(0x70)
+#define QSD8X50_GPIO_INT_STATUS_2 MSM_GPIO1_SHADOW_REG(0xF4)
+#define QSD8X50_GPIO_INT_STATUS_3 MSM_GPIO1_SHADOW_REG(0xF8)
+#define QSD8X50_GPIO_INT_STATUS_4 MSM_GPIO1_SHADOW_REG(0xFC)
+#define QSD8X50_GPIO_INT_STATUS_5 MSM_GPIO1_SHADOW_REG(0x100)
+#define QSD8X50_GPIO_INT_STATUS_6 MSM_GPIO1_SHADOW_REG(0x104)
+#define QSD8X50_GPIO_INT_STATUS_7 MSM_GPIO1_SHADOW_REG(0x108)
+
+/*
+ * MSM7X30 registers
+ */
+/* output value */
+#define MSM7X30_GPIO_OUT_0 MSM_GPIO1_REG(0x00) /* gpio 15-0 */
+#define MSM7X30_GPIO_OUT_1 MSM_GPIO2_REG(0x00) /* gpio 43-16 */
+#define MSM7X30_GPIO_OUT_2 MSM_GPIO1_REG(0x04) /* gpio 67-44 */
+#define MSM7X30_GPIO_OUT_3 MSM_GPIO1_REG(0x08) /* gpio 94-68 */
+#define MSM7X30_GPIO_OUT_4 MSM_GPIO1_REG(0x0C) /* gpio 106-95 */
+#define MSM7X30_GPIO_OUT_5 MSM_GPIO1_REG(0x50) /* gpio 133-107 */
+#define MSM7X30_GPIO_OUT_6 MSM_GPIO1_REG(0xC4) /* gpio 150-134 */
+#define MSM7X30_GPIO_OUT_7 MSM_GPIO1_REG(0x214) /* gpio 181-151 */
+
+/* same pin map as above, output enable */
+#define MSM7X30_GPIO_OE_0 MSM_GPIO1_REG(0x10)
+#define MSM7X30_GPIO_OE_1 MSM_GPIO2_REG(0x08)
+#define MSM7X30_GPIO_OE_2 MSM_GPIO1_REG(0x14)
+#define MSM7X30_GPIO_OE_3 MSM_GPIO1_REG(0x18)
+#define MSM7X30_GPIO_OE_4 MSM_GPIO1_REG(0x1C)
+#define MSM7X30_GPIO_OE_5 MSM_GPIO1_REG(0x54)
+#define MSM7X30_GPIO_OE_6 MSM_GPIO1_REG(0xC8)
+#define MSM7X30_GPIO_OE_7 MSM_GPIO1_REG(0x218)
+
+/* same pin map as above, input read */
+#define MSM7X30_GPIO_IN_0 MSM_GPIO1_REG(0x34)
+#define MSM7X30_GPIO_IN_1 MSM_GPIO2_REG(0x20)
+#define MSM7X30_GPIO_IN_2 MSM_GPIO1_REG(0x38)
+#define MSM7X30_GPIO_IN_3 MSM_GPIO1_REG(0x3C)
+#define MSM7X30_GPIO_IN_4 MSM_GPIO1_REG(0x40)
+#define MSM7X30_GPIO_IN_5 MSM_GPIO1_REG(0x44)
+#define MSM7X30_GPIO_IN_6 MSM_GPIO1_REG(0xCC)
+#define MSM7X30_GPIO_IN_7 MSM_GPIO1_REG(0x21C)
+
+/* same pin map as above, 1=edge 0=level interrup */
+#define MSM7X30_GPIO_INT_EDGE_0 MSM_GPIO1_REG(0x60)
+#define MSM7X30_GPIO_INT_EDGE_1 MSM_GPIO2_REG(0x50)
+#define MSM7X30_GPIO_INT_EDGE_2 MSM_GPIO1_REG(0x64)
+#define MSM7X30_GPIO_INT_EDGE_3 MSM_GPIO1_REG(0x68)
+#define MSM7X30_GPIO_INT_EDGE_4 MSM_GPIO1_REG(0x6C)
+#define MSM7X30_GPIO_INT_EDGE_5 MSM_GPIO1_REG(0xC0)
+#define MSM7X30_GPIO_INT_EDGE_6 MSM_GPIO1_REG(0xD0)
+#define MSM7X30_GPIO_INT_EDGE_7 MSM_GPIO1_REG(0x240)
+
+/* same pin map as above, 1=positive 0=negative */
+#define MSM7X30_GPIO_INT_POS_0 MSM_GPIO1_REG(0x70)
+#define MSM7X30_GPIO_INT_POS_1 MSM_GPIO2_REG(0x58)
+#define MSM7X30_GPIO_INT_POS_2 MSM_GPIO1_REG(0x74)
+#define MSM7X30_GPIO_INT_POS_3 MSM_GPIO1_REG(0x78)
+#define MSM7X30_GPIO_INT_POS_4 MSM_GPIO1_REG(0x7C)
+#define MSM7X30_GPIO_INT_POS_5 MSM_GPIO1_REG(0xBC)
+#define MSM7X30_GPIO_INT_POS_6 MSM_GPIO1_REG(0xD4)
+#define MSM7X30_GPIO_INT_POS_7 MSM_GPIO1_REG(0x228)
+
+/* same pin map as above, interrupt enable */
+#define MSM7X30_GPIO_INT_EN_0 MSM_GPIO1_REG(0x80)
+#define MSM7X30_GPIO_INT_EN_1 MSM_GPIO2_REG(0x60)
+#define MSM7X30_GPIO_INT_EN_2 MSM_GPIO1_REG(0x84)
+#define MSM7X30_GPIO_INT_EN_3 MSM_GPIO1_REG(0x88)
+#define MSM7X30_GPIO_INT_EN_4 MSM_GPIO1_REG(0x8C)
+#define MSM7X30_GPIO_INT_EN_5 MSM_GPIO1_REG(0xB8)
+#define MSM7X30_GPIO_INT_EN_6 MSM_GPIO1_REG(0xD8)
+#define MSM7X30_GPIO_INT_EN_7 MSM_GPIO1_REG(0x22C)
+
+/* same pin map as above, write 1 to clear interrupt */
+#define MSM7X30_GPIO_INT_CLEAR_0 MSM_GPIO1_REG(0x90)
+#define MSM7X30_GPIO_INT_CLEAR_1 MSM_GPIO2_REG(0x68)
+#define MSM7X30_GPIO_INT_CLEAR_2 MSM_GPIO1_REG(0x94)
+#define MSM7X30_GPIO_INT_CLEAR_3 MSM_GPIO1_REG(0x98)
+#define MSM7X30_GPIO_INT_CLEAR_4 MSM_GPIO1_REG(0x9C)
+#define MSM7X30_GPIO_INT_CLEAR_5 MSM_GPIO1_REG(0xB4)
+#define MSM7X30_GPIO_INT_CLEAR_6 MSM_GPIO1_REG(0xDC)
+#define MSM7X30_GPIO_INT_CLEAR_7 MSM_GPIO1_REG(0x230)
+
+/* same pin map as above, 1=interrupt pending */
+#define MSM7X30_GPIO_INT_STATUS_0 MSM_GPIO1_REG(0xA0)
+#define MSM7X30_GPIO_INT_STATUS_1 MSM_GPIO2_REG(0x70)
+#define MSM7X30_GPIO_INT_STATUS_2 MSM_GPIO1_REG(0xA4)
+#define MSM7X30_GPIO_INT_STATUS_3 MSM_GPIO1_REG(0xA8)
+#define MSM7X30_GPIO_INT_STATUS_4 MSM_GPIO1_REG(0xAC)
+#define MSM7X30_GPIO_INT_STATUS_5 MSM_GPIO1_REG(0xB0)
+#define MSM7X30_GPIO_INT_STATUS_6 MSM_GPIO1_REG(0xE0)
+#define MSM7X30_GPIO_INT_STATUS_7 MSM_GPIO1_REG(0x234)
+
+#define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0)
+
+#define MSM_GPIO_BANK(soc, bank, first, last) \
+ { \
+ .regs = { \
+ .out = soc##_GPIO_OUT_##bank, \
+ .in = soc##_GPIO_IN_##bank, \
+ .int_status = soc##_GPIO_INT_STATUS_##bank, \
+ .int_clear = soc##_GPIO_INT_CLEAR_##bank, \
+ .int_en = soc##_GPIO_INT_EN_##bank, \
+ .int_edge = soc##_GPIO_INT_EDGE_##bank, \
+ .int_pos = soc##_GPIO_INT_POS_##bank, \
+ .oe = soc##_GPIO_OE_##bank, \
+ }, \
+ .chip = { \
+ .base = (first), \
+ .ngpio = (last) - (first) + 1, \
+ .get = msm_gpio_get, \
+ .set = msm_gpio_set, \
+ .direction_input = msm_gpio_direction_input, \
+ .direction_output = msm_gpio_direction_output, \
+ .to_irq = msm_gpio_to_irq, \
+ .request = msm_gpio_request, \
+ .free = msm_gpio_free, \
+ } \
+ }
+
+#define MSM_GPIO_BROKEN_INT_CLEAR 1
+
+struct msm_gpio_regs {
+ void __iomem *out;
+ void __iomem *in;
+ void __iomem *int_status;
+ void __iomem *int_clear;
+ void __iomem *int_en;
+ void __iomem *int_edge;
+ void __iomem *int_pos;
+ void __iomem *oe;
+};
+
+struct msm_gpio_chip {
+ spinlock_t lock;
+ struct gpio_chip chip;
+ struct msm_gpio_regs regs;
+#if MSM_GPIO_BROKEN_INT_CLEAR
+ unsigned int_status_copy;
+#endif
+ unsigned int both_edge_detect;
+ unsigned int int_enable[2]; /* 0: awake, 1: sleep */
+};
+
+static int msm_gpio_write(struct msm_gpio_chip *msm_chip,
+ unsigned offset, unsigned on)
+{
+ unsigned mask = BIT(offset);
+ unsigned val;
+
+ val = readl(msm_chip->regs.out);
+ if (on)
+ writel(val | mask, msm_chip->regs.out);
+ else
+ writel(val & ~mask, msm_chip->regs.out);
+ return 0;
+}
+
+static void msm_gpio_update_both_edge_detect(struct msm_gpio_chip *msm_chip)
+{
+ int loop_limit = 100;
+ unsigned pol, val, val2, intstat;
+ do {
+ val = readl(msm_chip->regs.in);
+ pol = readl(msm_chip->regs.int_pos);
+ pol = (pol & ~msm_chip->both_edge_detect) |
+ (~val & msm_chip->both_edge_detect);
+ writel(pol, msm_chip->regs.int_pos);
+ intstat = readl(msm_chip->regs.int_status);
+ val2 = readl(msm_chip->regs.in);
+ if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0)
+ return;
+ } while (loop_limit-- > 0);
+ printk(KERN_ERR "msm_gpio_update_both_edge_detect, "
+ "failed to reach stable state %x != %x\n", val, val2);
+}
+
+static int msm_gpio_clear_detect_status(struct msm_gpio_chip *msm_chip,
+ unsigned offset)
+{
+ unsigned bit = BIT(offset);
+
+#if MSM_GPIO_BROKEN_INT_CLEAR
+ /* Save interrupts that already triggered before we loose them. */
+ /* Any interrupt that triggers between the read of int_status */
+ /* and the write to int_clear will still be lost though. */
+ msm_chip->int_status_copy |= readl(msm_chip->regs.int_status);
+ msm_chip->int_status_copy &= ~bit;
+#endif
+ writel(bit, msm_chip->regs.int_clear);
+ msm_gpio_update_both_edge_detect(msm_chip);
+ return 0;
+}
+
+static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct msm_gpio_chip *msm_chip;
+ unsigned long irq_flags;
+
+ msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ writel(readl(msm_chip->regs.oe) & ~BIT(offset), msm_chip->regs.oe);
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+ return 0;
+}
+
+static int
+msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct msm_gpio_chip *msm_chip;
+ unsigned long irq_flags;
+
+ msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ msm_gpio_write(msm_chip, offset, value);
+ writel(readl(msm_chip->regs.oe) | BIT(offset), msm_chip->regs.oe);
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+ return 0;
+}
+
+static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct msm_gpio_chip *msm_chip;
+
+ msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+ return (readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0;
+}
+
+static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct msm_gpio_chip *msm_chip;
+ unsigned long irq_flags;
+
+ msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ msm_gpio_write(msm_chip, offset, value);
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+}
+
+static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return MSM_GPIO_TO_INT(chip->base + offset);
+}
+
+#ifdef CONFIG_MSM_GPIOMUX
+static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return msm_gpiomux_get(chip->base + offset);
+}
+
+static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ msm_gpiomux_put(chip->base + offset);
+}
+#else
+#define msm_gpio_request NULL
+#define msm_gpio_free NULL
+#endif
+
+static struct msm_gpio_chip *msm_gpio_chips;
+static int msm_gpio_count;
+
+static struct msm_gpio_chip msm_gpio_chips_msm7x01[] = {
+ MSM_GPIO_BANK(MSM7X00, 0, 0, 15),
+ MSM_GPIO_BANK(MSM7X00, 1, 16, 42),
+ MSM_GPIO_BANK(MSM7X00, 2, 43, 67),
+ MSM_GPIO_BANK(MSM7X00, 3, 68, 94),
+ MSM_GPIO_BANK(MSM7X00, 4, 95, 106),
+ MSM_GPIO_BANK(MSM7X00, 5, 107, 121),
+};
+
+static struct msm_gpio_chip msm_gpio_chips_msm7x30[] = {
+ MSM_GPIO_BANK(MSM7X30, 0, 0, 15),
+ MSM_GPIO_BANK(MSM7X30, 1, 16, 43),
+ MSM_GPIO_BANK(MSM7X30, 2, 44, 67),
+ MSM_GPIO_BANK(MSM7X30, 3, 68, 94),
+ MSM_GPIO_BANK(MSM7X30, 4, 95, 106),
+ MSM_GPIO_BANK(MSM7X30, 5, 107, 133),
+ MSM_GPIO_BANK(MSM7X30, 6, 134, 150),
+ MSM_GPIO_BANK(MSM7X30, 7, 151, 181),
+};
+
+static struct msm_gpio_chip msm_gpio_chips_qsd8x50[] = {
+ MSM_GPIO_BANK(QSD8X50, 0, 0, 15),
+ MSM_GPIO_BANK(QSD8X50, 1, 16, 42),
+ MSM_GPIO_BANK(QSD8X50, 2, 43, 67),
+ MSM_GPIO_BANK(QSD8X50, 3, 68, 94),
+ MSM_GPIO_BANK(QSD8X50, 4, 95, 103),
+ MSM_GPIO_BANK(QSD8X50, 5, 104, 121),
+ MSM_GPIO_BANK(QSD8X50, 6, 122, 152),
+ MSM_GPIO_BANK(QSD8X50, 7, 153, 164),
+};
+
+static void msm_gpio_irq_ack(struct irq_data *d)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ msm_gpio_clear_detect_status(msm_chip,
+ d->irq - gpio_to_irq(msm_chip->chip.base));
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+}
+
+static void msm_gpio_irq_mask(struct irq_data *d)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+ unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
+
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ /* level triggered interrupts are also latched */
+ if (!(readl(msm_chip->regs.int_edge) & BIT(offset)))
+ msm_gpio_clear_detect_status(msm_chip, offset);
+ msm_chip->int_enable[0] &= ~BIT(offset);
+ writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+}
+
+static void msm_gpio_irq_unmask(struct irq_data *d)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+ unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
+
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ /* level triggered interrupts are also latched */
+ if (!(readl(msm_chip->regs.int_edge) & BIT(offset)))
+ msm_gpio_clear_detect_status(msm_chip, offset);
+ msm_chip->int_enable[0] |= BIT(offset);
+ writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+}
+
+static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+ unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
+
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+
+ if (on)
+ msm_chip->int_enable[1] |= BIT(offset);
+ else
+ msm_chip->int_enable[1] &= ~BIT(offset);
+
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+ return 0;
+}
+
+static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+ unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
+ unsigned val, mask = BIT(offset);
+
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ val = readl(msm_chip->regs.int_edge);
+ if (flow_type & IRQ_TYPE_EDGE_BOTH) {
+ writel(val | mask, msm_chip->regs.int_edge);
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+ } else {
+ writel(val & ~mask, msm_chip->regs.int_edge);
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+ }
+ if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
+ msm_chip->both_edge_detect |= mask;
+ msm_gpio_update_both_edge_detect(msm_chip);
+ } else {
+ msm_chip->both_edge_detect &= ~mask;
+ val = readl(msm_chip->regs.int_pos);
+ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH))
+ writel(val | mask, msm_chip->regs.int_pos);
+ else
+ writel(val & ~mask, msm_chip->regs.int_pos);
+ }
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+ return 0;
+}
+
+static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int i, j, mask;
+ unsigned val;
+
+ for (i = 0; i < msm_gpio_count; i++) {
+ struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i];
+ val = readl(msm_chip->regs.int_status);
+ val &= msm_chip->int_enable[0];
+ while (val) {
+ mask = val & -val;
+ j = fls(mask) - 1;
+ /* printk("%s %08x %08x bit %d gpio %d irq %d\n",
+ __func__, v, m, j, msm_chip->chip.start + j,
+ FIRST_GPIO_IRQ + msm_chip->chip.start + j); */
+ val &= ~mask;
+ generic_handle_irq(FIRST_GPIO_IRQ +
+ msm_chip->chip.base + j);
+ }
+ }
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+}
+
+static struct irq_chip msm_gpio_irq_chip = {
+ .name = "msmgpio",
+ .irq_ack = msm_gpio_irq_ack,
+ .irq_mask = msm_gpio_irq_mask,
+ .irq_unmask = msm_gpio_irq_unmask,
+ .irq_set_wake = msm_gpio_irq_set_wake,
+ .irq_set_type = msm_gpio_irq_set_type,
+};
+
+static int __init msm_init_gpio(void)
+{
+ int i, j = 0;
+
+ if (cpu_is_msm7x01()) {
+ msm_gpio_chips = msm_gpio_chips_msm7x01;
+ msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_msm7x01);
+ } else if (cpu_is_msm7x30()) {
+ msm_gpio_chips = msm_gpio_chips_msm7x30;
+ msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_msm7x30);
+ } else if (cpu_is_qsd8x50()) {
+ msm_gpio_chips = msm_gpio_chips_qsd8x50;
+ msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_qsd8x50);
+ } else {
+ return 0;
+ }
+
+ for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) {
+ if (i - FIRST_GPIO_IRQ >=
+ msm_gpio_chips[j].chip.base +
+ msm_gpio_chips[j].chip.ngpio)
+ j++;
+ irq_set_chip_data(i, &msm_gpio_chips[j]);
+ irq_set_chip_and_handler(i, &msm_gpio_irq_chip,
+ handle_edge_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+
+ for (i = 0; i < msm_gpio_count; i++) {
+ spin_lock_init(&msm_gpio_chips[i].lock);
+ writel(0, msm_gpio_chips[i].regs.int_en);
+ gpiochip_add(&msm_gpio_chips[i].chip);
+ }
+
+ irq_set_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler);
+ irq_set_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler);
+ irq_set_irq_wake(INT_GPIO_GROUP1, 1);
+ irq_set_irq_wake(INT_GPIO_GROUP2, 2);
+ return 0;
+}
+
+postcore_initcall(msm_init_gpio);
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
new file mode 100644
index 000000000000..5cb1227d69cf
--- /dev/null
+++ b/drivers/gpio/gpio-msm-v2.c
@@ -0,0 +1,433 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <asm/mach/irq.h>
+
+#include <mach/msm_gpiomux.h>
+#include <mach/msm_iomap.h>
+
+/* Bits of interest in the GPIO_IN_OUT register.
+ */
+enum {
+ GPIO_IN = 0,
+ GPIO_OUT = 1
+};
+
+/* Bits of interest in the GPIO_INTR_STATUS register.
+ */
+enum {
+ INTR_STATUS = 0,
+};
+
+/* Bits of interest in the GPIO_CFG register.
+ */
+enum {
+ GPIO_OE = 9,
+};
+
+/* Bits of interest in the GPIO_INTR_CFG register.
+ * When a GPIO triggers, two separate decisions are made, controlled
+ * by two separate flags.
+ *
+ * - First, INTR_RAW_STATUS_EN controls whether or not the GPIO_INTR_STATUS
+ * register for that GPIO will be updated to reflect the triggering of that
+ * gpio. If this bit is 0, this register will not be updated.
+ * - Second, INTR_ENABLE controls whether an interrupt is triggered.
+ *
+ * If INTR_ENABLE is set and INTR_RAW_STATUS_EN is NOT set, an interrupt
+ * can be triggered but the status register will not reflect it.
+ */
+enum {
+ INTR_ENABLE = 0,
+ INTR_POL_CTL = 1,
+ INTR_DECT_CTL = 2,
+ INTR_RAW_STATUS_EN = 3,
+};
+
+/* Codes of interest in GPIO_INTR_CFG_SU.
+ */
+enum {
+ TARGET_PROC_SCORPION = 4,
+ TARGET_PROC_NONE = 7,
+};
+
+
+#define GPIO_INTR_CFG_SU(gpio) (MSM_TLMM_BASE + 0x0400 + (0x04 * (gpio)))
+#define GPIO_CONFIG(gpio) (MSM_TLMM_BASE + 0x1000 + (0x10 * (gpio)))
+#define GPIO_IN_OUT(gpio) (MSM_TLMM_BASE + 0x1004 + (0x10 * (gpio)))
+#define GPIO_INTR_CFG(gpio) (MSM_TLMM_BASE + 0x1008 + (0x10 * (gpio)))
+#define GPIO_INTR_STATUS(gpio) (MSM_TLMM_BASE + 0x100c + (0x10 * (gpio)))
+
+/**
+ * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure
+ *
+ * @enabled_irqs: a bitmap used to optimize the summary-irq handler. By
+ * keeping track of which gpios are unmasked as irq sources, we avoid
+ * having to do readl calls on hundreds of iomapped registers each time
+ * the summary interrupt fires in order to locate the active interrupts.
+ *
+ * @wake_irqs: a bitmap for tracking which interrupt lines are enabled
+ * as wakeup sources. When the device is suspended, interrupts which are
+ * not wakeup sources are disabled.
+ *
+ * @dual_edge_irqs: a bitmap used to track which irqs are configured
+ * as dual-edge, as this is not supported by the hardware and requires
+ * some special handling in the driver.
+ */
+struct msm_gpio_dev {
+ struct gpio_chip gpio_chip;
+ DECLARE_BITMAP(enabled_irqs, NR_GPIO_IRQS);
+ DECLARE_BITMAP(wake_irqs, NR_GPIO_IRQS);
+ DECLARE_BITMAP(dual_edge_irqs, NR_GPIO_IRQS);
+};
+
+static DEFINE_SPINLOCK(tlmm_lock);
+
+static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip)
+{
+ return container_of(chip, struct msm_gpio_dev, gpio_chip);
+}
+
+static inline void set_gpio_bits(unsigned n, void __iomem *reg)
+{
+ writel(readl(reg) | n, reg);
+}
+
+static inline void clear_gpio_bits(unsigned n, void __iomem *reg)
+{
+ writel(readl(reg) & ~n, reg);
+}
+
+static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return readl(GPIO_IN_OUT(offset)) & BIT(GPIO_IN);
+}
+
+static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ writel(val ? BIT(GPIO_OUT) : 0, GPIO_IN_OUT(offset));
+}
+
+static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&tlmm_lock, irq_flags);
+ clear_gpio_bits(BIT(GPIO_OE), GPIO_CONFIG(offset));
+ spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+ return 0;
+}
+
+static int msm_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset,
+ int val)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&tlmm_lock, irq_flags);
+ msm_gpio_set(chip, offset, val);
+ set_gpio_bits(BIT(GPIO_OE), GPIO_CONFIG(offset));
+ spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+ return 0;
+}
+
+static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return msm_gpiomux_get(chip->base + offset);
+}
+
+static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ msm_gpiomux_put(chip->base + offset);
+}
+
+static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return MSM_GPIO_TO_INT(chip->base + offset);
+}
+
+static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
+{
+ return irq - MSM_GPIO_TO_INT(chip->base);
+}
+
+static struct msm_gpio_dev msm_gpio = {
+ .gpio_chip = {
+ .base = 0,
+ .ngpio = NR_GPIO_IRQS,
+ .direction_input = msm_gpio_direction_input,
+ .direction_output = msm_gpio_direction_output,
+ .get = msm_gpio_get,
+ .set = msm_gpio_set,
+ .to_irq = msm_gpio_to_irq,
+ .request = msm_gpio_request,
+ .free = msm_gpio_free,
+ },
+};
+
+/* For dual-edge interrupts in software, since the hardware has no
+ * such support:
+ *
+ * At appropriate moments, this function may be called to flip the polarity
+ * settings of both-edge irq lines to try and catch the next edge.
+ *
+ * The attempt is considered successful if:
+ * - the status bit goes high, indicating that an edge was caught, or
+ * - the input value of the gpio doesn't change during the attempt.
+ * If the value changes twice during the process, that would cause the first
+ * test to fail but would force the second, as two opposite
+ * transitions would cause a detection no matter the polarity setting.
+ *
+ * The do-loop tries to sledge-hammer closed the timing hole between
+ * the initial value-read and the polarity-write - if the line value changes
+ * during that window, an interrupt is lost, the new polarity setting is
+ * incorrect, and the first success test will fail, causing a retry.
+ *
+ * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
+ */
+static void msm_gpio_update_dual_edge_pos(unsigned gpio)
+{
+ int loop_limit = 100;
+ unsigned val, val2, intstat;
+
+ do {
+ val = readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN);
+ if (val)
+ clear_gpio_bits(BIT(INTR_POL_CTL), GPIO_INTR_CFG(gpio));
+ else
+ set_gpio_bits(BIT(INTR_POL_CTL), GPIO_INTR_CFG(gpio));
+ val2 = readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN);
+ intstat = readl(GPIO_INTR_STATUS(gpio)) & BIT(INTR_STATUS);
+ if (intstat || val == val2)
+ return;
+ } while (loop_limit-- > 0);
+ pr_err("dual-edge irq failed to stabilize, "
+ "interrupts dropped. %#08x != %#08x\n",
+ val, val2);
+}
+
+static void msm_gpio_irq_ack(struct irq_data *d)
+{
+ int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
+
+ writel(BIT(INTR_STATUS), GPIO_INTR_STATUS(gpio));
+ if (test_bit(gpio, msm_gpio.dual_edge_irqs))
+ msm_gpio_update_dual_edge_pos(gpio);
+}
+
+static void msm_gpio_irq_mask(struct irq_data *d)
+{
+ int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&tlmm_lock, irq_flags);
+ writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
+ clear_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
+ __clear_bit(gpio, msm_gpio.enabled_irqs);
+ spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+}
+
+static void msm_gpio_irq_unmask(struct irq_data *d)
+{
+ int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&tlmm_lock, irq_flags);
+ __set_bit(gpio, msm_gpio.enabled_irqs);
+ set_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
+ writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
+ spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+}
+
+static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
+ unsigned long irq_flags;
+ uint32_t bits;
+
+ spin_lock_irqsave(&tlmm_lock, irq_flags);
+
+ bits = readl(GPIO_INTR_CFG(gpio));
+
+ if (flow_type & IRQ_TYPE_EDGE_BOTH) {
+ bits |= BIT(INTR_DECT_CTL);
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+ if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
+ __set_bit(gpio, msm_gpio.dual_edge_irqs);
+ else
+ __clear_bit(gpio, msm_gpio.dual_edge_irqs);
+ } else {
+ bits &= ~BIT(INTR_DECT_CTL);
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+ __clear_bit(gpio, msm_gpio.dual_edge_irqs);
+ }
+
+ if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
+ bits |= BIT(INTR_POL_CTL);
+ else
+ bits &= ~BIT(INTR_POL_CTL);
+
+ writel(bits, GPIO_INTR_CFG(gpio));
+
+ if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
+ msm_gpio_update_dual_edge_pos(gpio);
+
+ spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+
+ return 0;
+}
+
+/*
+ * When the summary IRQ is raised, any number of GPIO lines may be high.
+ * It is the job of the summary handler to find all those GPIO lines
+ * which have been set as summary IRQ lines and which are triggered,
+ * and to call their interrupt handlers.
+ */
+static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ unsigned long i;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+
+ for (i = find_first_bit(msm_gpio.enabled_irqs, NR_GPIO_IRQS);
+ i < NR_GPIO_IRQS;
+ i = find_next_bit(msm_gpio.enabled_irqs, NR_GPIO_IRQS, i + 1)) {
+ if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS))
+ generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
+ i));
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
+
+ if (on) {
+ if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS))
+ irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 1);
+ set_bit(gpio, msm_gpio.wake_irqs);
+ } else {
+ clear_bit(gpio, msm_gpio.wake_irqs);
+ if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS))
+ irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 0);
+ }
+
+ return 0;
+}
+
+static struct irq_chip msm_gpio_irq_chip = {
+ .name = "msmgpio",
+ .irq_mask = msm_gpio_irq_mask,
+ .irq_unmask = msm_gpio_irq_unmask,
+ .irq_ack = msm_gpio_irq_ack,
+ .irq_set_type = msm_gpio_irq_set_type,
+ .irq_set_wake = msm_gpio_irq_set_wake,
+};
+
+static int __devinit msm_gpio_probe(struct platform_device *dev)
+{
+ int i, irq, ret;
+
+ bitmap_zero(msm_gpio.enabled_irqs, NR_GPIO_IRQS);
+ bitmap_zero(msm_gpio.wake_irqs, NR_GPIO_IRQS);
+ bitmap_zero(msm_gpio.dual_edge_irqs, NR_GPIO_IRQS);
+ msm_gpio.gpio_chip.label = dev->name;
+ ret = gpiochip_add(&msm_gpio.gpio_chip);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) {
+ irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
+ irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
+ handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ irq_set_chained_handler(TLMM_SCSS_SUMMARY_IRQ,
+ msm_summary_irq_handler);
+ return 0;
+}
+
+static int __devexit msm_gpio_remove(struct platform_device *dev)
+{
+ int ret = gpiochip_remove(&msm_gpio.gpio_chip);
+
+ if (ret < 0)
+ return ret;
+
+ irq_set_handler(TLMM_SCSS_SUMMARY_IRQ, NULL);
+
+ return 0;
+}
+
+static struct platform_driver msm_gpio_driver = {
+ .probe = msm_gpio_probe,
+ .remove = __devexit_p(msm_gpio_remove),
+ .driver = {
+ .name = "msmgpio",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device msm_device_gpio = {
+ .name = "msmgpio",
+ .id = -1,
+};
+
+static int __init msm_gpio_init(void)
+{
+ int rc;
+
+ rc = platform_driver_register(&msm_gpio_driver);
+ if (!rc) {
+ rc = platform_device_register(&msm_device_gpio);
+ if (rc)
+ platform_driver_unregister(&msm_gpio_driver);
+ }
+
+ return rc;
+}
+
+static void __exit msm_gpio_exit(void)
+{
+ platform_device_unregister(&msm_device_gpio);
+ platform_driver_unregister(&msm_gpio_driver);
+}
+
+postcore_initcall(msm_gpio_init);
+module_exit(msm_gpio_exit);
+
+MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
+MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:msmgpio");
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
new file mode 100644
index 000000000000..e79147634573
--- /dev/null
+++ b/drivers/gpio/gpio-mxc.c
@@ -0,0 +1,479 @@
+/*
+ * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ *
+ * Based on code from Freescale,
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <asm-generic/bug.h>
+#include <asm/mach/irq.h>
+
+#define irq_to_gpio(irq) ((irq) - MXC_GPIO_IRQ_START)
+
+enum mxc_gpio_hwtype {
+ IMX1_GPIO, /* runs on i.mx1 */
+ IMX21_GPIO, /* runs on i.mx21 and i.mx27 */
+ IMX31_GPIO, /* runs on all other i.mx */
+};
+
+/* device type dependent stuff */
+struct mxc_gpio_hwdata {
+ unsigned dr_reg;
+ unsigned gdir_reg;
+ unsigned psr_reg;
+ unsigned icr1_reg;
+ unsigned icr2_reg;
+ unsigned imr_reg;
+ unsigned isr_reg;
+ unsigned low_level;
+ unsigned high_level;
+ unsigned rise_edge;
+ unsigned fall_edge;
+};
+
+struct mxc_gpio_port {
+ struct list_head node;
+ void __iomem *base;
+ int irq;
+ int irq_high;
+ int virtual_irq_start;
+ struct bgpio_chip bgc;
+ u32 both_edges;
+};
+
+static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
+ .dr_reg = 0x1c,
+ .gdir_reg = 0x00,
+ .psr_reg = 0x24,
+ .icr1_reg = 0x28,
+ .icr2_reg = 0x2c,
+ .imr_reg = 0x30,
+ .isr_reg = 0x34,
+ .low_level = 0x03,
+ .high_level = 0x02,
+ .rise_edge = 0x00,
+ .fall_edge = 0x01,
+};
+
+static struct mxc_gpio_hwdata imx31_gpio_hwdata = {
+ .dr_reg = 0x00,
+ .gdir_reg = 0x04,
+ .psr_reg = 0x08,
+ .icr1_reg = 0x0c,
+ .icr2_reg = 0x10,
+ .imr_reg = 0x14,
+ .isr_reg = 0x18,
+ .low_level = 0x00,
+ .high_level = 0x01,
+ .rise_edge = 0x02,
+ .fall_edge = 0x03,
+};
+
+static enum mxc_gpio_hwtype mxc_gpio_hwtype;
+static struct mxc_gpio_hwdata *mxc_gpio_hwdata;
+
+#define GPIO_DR (mxc_gpio_hwdata->dr_reg)
+#define GPIO_GDIR (mxc_gpio_hwdata->gdir_reg)
+#define GPIO_PSR (mxc_gpio_hwdata->psr_reg)
+#define GPIO_ICR1 (mxc_gpio_hwdata->icr1_reg)
+#define GPIO_ICR2 (mxc_gpio_hwdata->icr2_reg)
+#define GPIO_IMR (mxc_gpio_hwdata->imr_reg)
+#define GPIO_ISR (mxc_gpio_hwdata->isr_reg)
+
+#define GPIO_INT_LOW_LEV (mxc_gpio_hwdata->low_level)
+#define GPIO_INT_HIGH_LEV (mxc_gpio_hwdata->high_level)
+#define GPIO_INT_RISE_EDGE (mxc_gpio_hwdata->rise_edge)
+#define GPIO_INT_FALL_EDGE (mxc_gpio_hwdata->fall_edge)
+#define GPIO_INT_NONE 0x4
+
+static struct platform_device_id mxc_gpio_devtype[] = {
+ {
+ .name = "imx1-gpio",
+ .driver_data = IMX1_GPIO,
+ }, {
+ .name = "imx21-gpio",
+ .driver_data = IMX21_GPIO,
+ }, {
+ .name = "imx31-gpio",
+ .driver_data = IMX31_GPIO,
+ }, {
+ /* sentinel */
+ }
+};
+
+static const struct of_device_id mxc_gpio_dt_ids[] = {
+ { .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
+ { .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
+ { .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
+ { /* sentinel */ }
+};
+
+/*
+ * MX2 has one interrupt *for all* gpio ports. The list is used
+ * to save the references to all ports, so that mx2_gpio_irq_handler
+ * can walk through all interrupt status registers.
+ */
+static LIST_HEAD(mxc_gpio_ports);
+
+/* Note: This driver assumes 32 GPIOs are handled in one register */
+
+static int gpio_set_irq_type(struct irq_data *d, u32 type)
+{
+ u32 gpio = irq_to_gpio(d->irq);
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mxc_gpio_port *port = gc->private;
+ u32 bit, val;
+ int edge;
+ void __iomem *reg = port->base;
+
+ port->both_edges &= ~(1 << (gpio & 31));
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ edge = GPIO_INT_RISE_EDGE;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ edge = GPIO_INT_FALL_EDGE;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ val = gpio_get_value(gpio);
+ if (val) {
+ edge = GPIO_INT_LOW_LEV;
+ pr_debug("mxc: set GPIO %d to low trigger\n", gpio);
+ } else {
+ edge = GPIO_INT_HIGH_LEV;
+ pr_debug("mxc: set GPIO %d to high trigger\n", gpio);
+ }
+ port->both_edges |= 1 << (gpio & 31);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ edge = GPIO_INT_LOW_LEV;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ edge = GPIO_INT_HIGH_LEV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
+ bit = gpio & 0xf;
+ val = readl(reg) & ~(0x3 << (bit << 1));
+ writel(val | (edge << (bit << 1)), reg);
+ writel(1 << (gpio & 0x1f), port->base + GPIO_ISR);
+
+ return 0;
+}
+
+static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
+{
+ void __iomem *reg = port->base;
+ u32 bit, val;
+ int edge;
+
+ reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
+ bit = gpio & 0xf;
+ val = readl(reg);
+ edge = (val >> (bit << 1)) & 3;
+ val &= ~(0x3 << (bit << 1));
+ if (edge == GPIO_INT_HIGH_LEV) {
+ edge = GPIO_INT_LOW_LEV;
+ pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
+ } else if (edge == GPIO_INT_LOW_LEV) {
+ edge = GPIO_INT_HIGH_LEV;
+ pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
+ } else {
+ pr_err("mxc: invalid configuration for GPIO %d: %x\n",
+ gpio, edge);
+ return;
+ }
+ writel(val | (edge << (bit << 1)), reg);
+}
+
+/* handle 32 interrupts in one status register */
+static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
+{
+ u32 gpio_irq_no_base = port->virtual_irq_start;
+
+ while (irq_stat != 0) {
+ int irqoffset = fls(irq_stat) - 1;
+
+ if (port->both_edges & (1 << irqoffset))
+ mxc_flip_edge(port, irqoffset);
+
+ generic_handle_irq(gpio_irq_no_base + irqoffset);
+
+ irq_stat &= ~(1 << irqoffset);
+ }
+}
+
+/* MX1 and MX3 has one interrupt *per* gpio port */
+static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 irq_stat;
+ struct mxc_gpio_port *port = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
+
+ irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);
+
+ mxc_gpio_irq_handler(port, irq_stat);
+
+ chained_irq_exit(chip, desc);
+}
+
+/* MX2 has one interrupt *for all* gpio ports */
+static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 irq_msk, irq_stat;
+ struct mxc_gpio_port *port;
+
+ /* walk through all interrupt status registers */
+ list_for_each_entry(port, &mxc_gpio_ports, node) {
+ irq_msk = readl(port->base + GPIO_IMR);
+ if (!irq_msk)
+ continue;
+
+ irq_stat = readl(port->base + GPIO_ISR) & irq_msk;
+ if (irq_stat)
+ mxc_gpio_irq_handler(port, irq_stat);
+ }
+}
+
+/*
+ * Set interrupt number "irq" in the GPIO as a wake-up source.
+ * While system is running, all registered GPIO interrupts need to have
+ * wake-up enabled. When system is suspended, only selected GPIO interrupts
+ * need to have wake-up enabled.
+ * @param irq interrupt source number
+ * @param enable enable as wake-up if equal to non-zero
+ * @return This function returns 0 on success.
+ */
+static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
+{
+ u32 gpio = irq_to_gpio(d->irq);
+ u32 gpio_idx = gpio & 0x1F;
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mxc_gpio_port *port = gc->private;
+
+ if (enable) {
+ if (port->irq_high && (gpio_idx >= 16))
+ enable_irq_wake(port->irq_high);
+ else
+ enable_irq_wake(port->irq);
+ } else {
+ if (port->irq_high && (gpio_idx >= 16))
+ disable_irq_wake(port->irq_high);
+ else
+ disable_irq_wake(port->irq);
+ }
+
+ return 0;
+}
+
+static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("gpio-mxc", 1, port->virtual_irq_start,
+ port->base, handle_level_irq);
+ gc->private = port;
+
+ ct = gc->chip_types;
+ ct->chip.irq_ack = irq_gc_ack_set_bit;
+ ct->chip.irq_mask = irq_gc_mask_clr_bit;
+ ct->chip.irq_unmask = irq_gc_mask_set_bit;
+ ct->chip.irq_set_type = gpio_set_irq_type;
+ ct->chip.irq_set_wake = gpio_set_wake_irq;
+ ct->regs.ack = GPIO_ISR;
+ ct->regs.mask = GPIO_IMR;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK,
+ IRQ_NOREQUEST, 0);
+}
+
+static void __devinit mxc_gpio_get_hw(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(mxc_gpio_dt_ids, &pdev->dev);
+ enum mxc_gpio_hwtype hwtype;
+
+ if (of_id)
+ pdev->id_entry = of_id->data;
+ hwtype = pdev->id_entry->driver_data;
+
+ if (mxc_gpio_hwtype) {
+ /*
+ * The driver works with a reasonable presupposition,
+ * that is all gpio ports must be the same type when
+ * running on one soc.
+ */
+ BUG_ON(mxc_gpio_hwtype != hwtype);
+ return;
+ }
+
+ if (hwtype == IMX31_GPIO)
+ mxc_gpio_hwdata = &imx31_gpio_hwdata;
+ else
+ mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata;
+
+ mxc_gpio_hwtype = hwtype;
+}
+
+static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct mxc_gpio_port *port =
+ container_of(bgc, struct mxc_gpio_port, bgc);
+
+ return port->virtual_irq_start + offset;
+}
+
+static int __devinit mxc_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct mxc_gpio_port *port;
+ struct resource *iores;
+ int err;
+
+ mxc_gpio_get_hw(pdev);
+
+ port = kzalloc(sizeof(struct mxc_gpio_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iores) {
+ err = -ENODEV;
+ goto out_kfree;
+ }
+
+ if (!request_mem_region(iores->start, resource_size(iores),
+ pdev->name)) {
+ err = -EBUSY;
+ goto out_kfree;
+ }
+
+ port->base = ioremap(iores->start, resource_size(iores));
+ if (!port->base) {
+ err = -ENOMEM;
+ goto out_release_mem;
+ }
+
+ port->irq_high = platform_get_irq(pdev, 1);
+ port->irq = platform_get_irq(pdev, 0);
+ if (port->irq < 0) {
+ err = -EINVAL;
+ goto out_iounmap;
+ }
+
+ /* disable the interrupt and clear the status */
+ writel(0, port->base + GPIO_IMR);
+ writel(~0, port->base + GPIO_ISR);
+
+ if (mxc_gpio_hwtype == IMX21_GPIO) {
+ /* setup one handler for all GPIO interrupts */
+ if (pdev->id == 0)
+ irq_set_chained_handler(port->irq,
+ mx2_gpio_irq_handler);
+ } else {
+ /* setup one handler for each entry */
+ irq_set_chained_handler(port->irq, mx3_gpio_irq_handler);
+ irq_set_handler_data(port->irq, port);
+ if (port->irq_high > 0) {
+ /* setup handler for GPIO 16 to 31 */
+ irq_set_chained_handler(port->irq_high,
+ mx3_gpio_irq_handler);
+ irq_set_handler_data(port->irq_high, port);
+ }
+ }
+
+ err = bgpio_init(&port->bgc, &pdev->dev, 4,
+ port->base + GPIO_PSR,
+ port->base + GPIO_DR, NULL,
+ port->base + GPIO_GDIR, NULL, false);
+ if (err)
+ goto out_iounmap;
+
+ port->bgc.gc.to_irq = mxc_gpio_to_irq;
+ port->bgc.gc.base = pdev->id * 32;
+ port->bgc.dir = port->bgc.read_reg(port->bgc.reg_dir);
+ port->bgc.data = port->bgc.read_reg(port->bgc.reg_set);
+
+ err = gpiochip_add(&port->bgc.gc);
+ if (err)
+ goto out_bgpio_remove;
+
+ /*
+ * In dt case, we use gpio number range dynamically
+ * allocated by gpio core.
+ */
+ port->virtual_irq_start = MXC_GPIO_IRQ_START + (np ? port->bgc.gc.base :
+ pdev->id * 32);
+
+ /* gpio-mxc can be a generic irq chip */
+ mxc_gpio_init_gc(port);
+
+ list_add_tail(&port->node, &mxc_gpio_ports);
+
+ return 0;
+
+out_bgpio_remove:
+ bgpio_remove(&port->bgc);
+out_iounmap:
+ iounmap(port->base);
+out_release_mem:
+ release_mem_region(iores->start, resource_size(iores));
+out_kfree:
+ kfree(port);
+ dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
+ return err;
+}
+
+static struct platform_driver mxc_gpio_driver = {
+ .driver = {
+ .name = "gpio-mxc",
+ .owner = THIS_MODULE,
+ .of_match_table = mxc_gpio_dt_ids,
+ },
+ .probe = mxc_gpio_probe,
+ .id_table = mxc_gpio_devtype,
+};
+
+static int __init gpio_mxc_init(void)
+{
+ return platform_driver_register(&mxc_gpio_driver);
+}
+postcore_initcall(gpio_mxc_init);
+
+MODULE_AUTHOR("Freescale Semiconductor, "
+ "Daniel Mack <danielncaiaq.de>, "
+ "Juergen Beisert <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("Freescale MXC GPIO");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
new file mode 100644
index 000000000000..385c58e8405b
--- /dev/null
+++ b/drivers/gpio/gpio-mxs.c
@@ -0,0 +1,292 @@
+/*
+ * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ *
+ * Based on code from Freescale,
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/module.h>
+#include <mach/mxs.h>
+
+#define MXS_SET 0x4
+#define MXS_CLR 0x8
+
+#define PINCTRL_DOUT(n) ((cpu_is_mx23() ? 0x0500 : 0x0700) + (n) * 0x10)
+#define PINCTRL_DIN(n) ((cpu_is_mx23() ? 0x0600 : 0x0900) + (n) * 0x10)
+#define PINCTRL_DOE(n) ((cpu_is_mx23() ? 0x0700 : 0x0b00) + (n) * 0x10)
+#define PINCTRL_PIN2IRQ(n) ((cpu_is_mx23() ? 0x0800 : 0x1000) + (n) * 0x10)
+#define PINCTRL_IRQEN(n) ((cpu_is_mx23() ? 0x0900 : 0x1100) + (n) * 0x10)
+#define PINCTRL_IRQLEV(n) ((cpu_is_mx23() ? 0x0a00 : 0x1200) + (n) * 0x10)
+#define PINCTRL_IRQPOL(n) ((cpu_is_mx23() ? 0x0b00 : 0x1300) + (n) * 0x10)
+#define PINCTRL_IRQSTAT(n) ((cpu_is_mx23() ? 0x0c00 : 0x1400) + (n) * 0x10)
+
+#define GPIO_INT_FALL_EDGE 0x0
+#define GPIO_INT_LOW_LEV 0x1
+#define GPIO_INT_RISE_EDGE 0x2
+#define GPIO_INT_HIGH_LEV 0x3
+#define GPIO_INT_LEV_MASK (1 << 0)
+#define GPIO_INT_POL_MASK (1 << 1)
+
+#define irq_to_gpio(irq) ((irq) - MXS_GPIO_IRQ_START)
+
+struct mxs_gpio_port {
+ void __iomem *base;
+ int id;
+ int irq;
+ int virtual_irq_start;
+ struct bgpio_chip bgc;
+};
+
+/* Note: This driver assumes 32 GPIOs are handled in one register */
+
+static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
+{
+ u32 gpio = irq_to_gpio(d->irq);
+ u32 pin_mask = 1 << (gpio & 31);
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mxs_gpio_port *port = gc->private;
+ void __iomem *pin_addr;
+ int edge;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ edge = GPIO_INT_RISE_EDGE;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ edge = GPIO_INT_FALL_EDGE;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ edge = GPIO_INT_LOW_LEV;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ edge = GPIO_INT_HIGH_LEV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set level or edge */
+ pin_addr = port->base + PINCTRL_IRQLEV(port->id);
+ if (edge & GPIO_INT_LEV_MASK)
+ writel(pin_mask, pin_addr + MXS_SET);
+ else
+ writel(pin_mask, pin_addr + MXS_CLR);
+
+ /* set polarity */
+ pin_addr = port->base + PINCTRL_IRQPOL(port->id);
+ if (edge & GPIO_INT_POL_MASK)
+ writel(pin_mask, pin_addr + MXS_SET);
+ else
+ writel(pin_mask, pin_addr + MXS_CLR);
+
+ writel(1 << (gpio & 0x1f),
+ port->base + PINCTRL_IRQSTAT(port->id) + MXS_CLR);
+
+ return 0;
+}
+
+/* MXS has one interrupt *per* gpio port */
+static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 irq_stat;
+ struct mxs_gpio_port *port = irq_get_handler_data(irq);
+ u32 gpio_irq_no_base = port->virtual_irq_start;
+
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+
+ irq_stat = readl(port->base + PINCTRL_IRQSTAT(port->id)) &
+ readl(port->base + PINCTRL_IRQEN(port->id));
+
+ while (irq_stat != 0) {
+ int irqoffset = fls(irq_stat) - 1;
+ generic_handle_irq(gpio_irq_no_base + irqoffset);
+ irq_stat &= ~(1 << irqoffset);
+ }
+}
+
+/*
+ * Set interrupt number "irq" in the GPIO as a wake-up source.
+ * While system is running, all registered GPIO interrupts need to have
+ * wake-up enabled. When system is suspended, only selected GPIO interrupts
+ * need to have wake-up enabled.
+ * @param irq interrupt source number
+ * @param enable enable as wake-up if equal to non-zero
+ * @return This function returns 0 on success.
+ */
+static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mxs_gpio_port *port = gc->private;
+
+ if (enable)
+ enable_irq_wake(port->irq);
+ else
+ disable_irq_wake(port->irq);
+
+ return 0;
+}
+
+static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("gpio-mxs", 1, port->virtual_irq_start,
+ port->base, handle_level_irq);
+ gc->private = port;
+
+ ct = gc->chip_types;
+ ct->chip.irq_ack = irq_gc_ack_set_bit;
+ ct->chip.irq_mask = irq_gc_mask_clr_bit;
+ ct->chip.irq_unmask = irq_gc_mask_set_bit;
+ ct->chip.irq_set_type = mxs_gpio_set_irq_type;
+ ct->chip.irq_set_wake = mxs_gpio_set_wake_irq;
+ ct->regs.ack = PINCTRL_IRQSTAT(port->id) + MXS_CLR;
+ ct->regs.mask = PINCTRL_IRQEN(port->id);
+
+ irq_setup_generic_chip(gc, IRQ_MSK(32), 0, IRQ_NOREQUEST, 0);
+}
+
+static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct mxs_gpio_port *port =
+ container_of(bgc, struct mxs_gpio_port, bgc);
+
+ return port->virtual_irq_start + offset;
+}
+
+static int __devinit mxs_gpio_probe(struct platform_device *pdev)
+{
+ static void __iomem *base;
+ struct mxs_gpio_port *port;
+ struct resource *iores = NULL;
+ int err;
+
+ port = kzalloc(sizeof(struct mxs_gpio_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->id = pdev->id;
+ port->virtual_irq_start = MXS_GPIO_IRQ_START + port->id * 32;
+
+ /*
+ * map memory region only once, as all the gpio ports
+ * share the same one
+ */
+ if (!base) {
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iores) {
+ err = -ENODEV;
+ goto out_kfree;
+ }
+
+ if (!request_mem_region(iores->start, resource_size(iores),
+ pdev->name)) {
+ err = -EBUSY;
+ goto out_kfree;
+ }
+
+ base = ioremap(iores->start, resource_size(iores));
+ if (!base) {
+ err = -ENOMEM;
+ goto out_release_mem;
+ }
+ }
+ port->base = base;
+
+ port->irq = platform_get_irq(pdev, 0);
+ if (port->irq < 0) {
+ err = -EINVAL;
+ goto out_iounmap;
+ }
+
+ /*
+ * select the pin interrupt functionality but initially
+ * disable the interrupts
+ */
+ writel(~0U, port->base + PINCTRL_PIN2IRQ(port->id));
+ writel(0, port->base + PINCTRL_IRQEN(port->id));
+
+ /* clear address has to be used to clear IRQSTAT bits */
+ writel(~0U, port->base + PINCTRL_IRQSTAT(port->id) + MXS_CLR);
+
+ /* gpio-mxs can be a generic irq chip */
+ mxs_gpio_init_gc(port);
+
+ /* setup one handler for each entry */
+ irq_set_chained_handler(port->irq, mxs_gpio_irq_handler);
+ irq_set_handler_data(port->irq, port);
+
+ err = bgpio_init(&port->bgc, &pdev->dev, 4,
+ port->base + PINCTRL_DIN(port->id),
+ port->base + PINCTRL_DOUT(port->id), NULL,
+ port->base + PINCTRL_DOE(port->id), NULL, false);
+ if (err)
+ goto out_iounmap;
+
+ port->bgc.gc.to_irq = mxs_gpio_to_irq;
+ port->bgc.gc.base = port->id * 32;
+
+ err = gpiochip_add(&port->bgc.gc);
+ if (err)
+ goto out_bgpio_remove;
+
+ return 0;
+
+out_bgpio_remove:
+ bgpio_remove(&port->bgc);
+out_iounmap:
+ if (iores)
+ iounmap(port->base);
+out_release_mem:
+ if (iores)
+ release_mem_region(iores->start, resource_size(iores));
+out_kfree:
+ kfree(port);
+ dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
+ return err;
+}
+
+static struct platform_driver mxs_gpio_driver = {
+ .driver = {
+ .name = "gpio-mxs",
+ .owner = THIS_MODULE,
+ },
+ .probe = mxs_gpio_probe,
+};
+
+static int __init mxs_gpio_init(void)
+{
+ return platform_driver_register(&mxs_gpio_driver);
+}
+postcore_initcall(mxs_gpio_init);
+
+MODULE_AUTHOR("Freescale Semiconductor, "
+ "Daniel Mack <danielncaiaq.de>, "
+ "Juergen Beisert <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("Freescale MXS GPIO");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c
index 2c212c732d76..1ebedfb6d46d 100644
--- a/drivers/gpio/gpio-nomadik.c
+++ b/drivers/gpio/gpio-nomadik.c
@@ -27,8 +27,9 @@
#include <asm/mach/irq.h>
#include <plat/pincfg.h>
+#include <plat/gpio-nomadik.h>
#include <mach/hardware.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
/*
* The GPIO module in the Nomadik family of Systems-on-Chip is an
@@ -58,7 +59,6 @@ struct nmk_gpio_chip {
u32 rwimsc;
u32 fwimsc;
u32 slpm;
- u32 enabled;
u32 pull_up;
};
@@ -276,6 +276,8 @@ static void nmk_gpio_glitch_slpm_init(unsigned int *slpm)
if (!chip)
break;
+ clk_enable(chip->clk);
+
slpm[i] = readl(chip->addr + NMK_GPIO_SLPC);
writel(temp, chip->addr + NMK_GPIO_SLPC);
}
@@ -292,6 +294,8 @@ static void nmk_gpio_glitch_slpm_restore(unsigned int *slpm)
break;
writel(slpm[i], chip->addr + NMK_GPIO_SLPC);
+
+ clk_disable(chip->clk);
}
}
@@ -336,10 +340,12 @@ static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep)
break;
}
+ clk_enable(nmk_chip->clk);
spin_lock(&nmk_chip->lock);
__nmk_config_pin(nmk_chip, pin - nmk_chip->chip.base,
cfgs[i], sleep, glitch ? slpm : NULL);
spin_unlock(&nmk_chip->lock);
+ clk_disable(nmk_chip->clk);
}
if (glitch)
@@ -424,6 +430,7 @@ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode)
if (!nmk_chip)
return -EINVAL;
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
spin_lock(&nmk_chip->lock);
@@ -431,6 +438,7 @@ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode)
spin_unlock(&nmk_chip->lock);
spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
@@ -457,9 +465,11 @@ int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull)
if (!nmk_chip)
return -EINVAL;
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_chip->lock, flags);
__nmk_gpio_set_pull(nmk_chip, gpio - nmk_chip->chip.base, pull);
spin_unlock_irqrestore(&nmk_chip->lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
@@ -483,9 +493,11 @@ int nmk_gpio_set_mode(int gpio, int gpio_mode)
if (!nmk_chip)
return -EINVAL;
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_chip->lock, flags);
__nmk_gpio_set_mode(nmk_chip, gpio - nmk_chip->chip.base, gpio_mode);
spin_unlock_irqrestore(&nmk_chip->lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
@@ -502,9 +514,13 @@ int nmk_gpio_get_mode(int gpio)
bit = 1 << (gpio - nmk_chip->chip.base);
+ clk_enable(nmk_chip->clk);
+
afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit;
bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit;
+ clk_disable(nmk_chip->clk);
+
return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0);
}
EXPORT_SYMBOL(nmk_gpio_get_mode);
@@ -525,7 +541,10 @@ static void nmk_gpio_irq_ack(struct irq_data *d)
nmk_chip = irq_data_get_irq_chip_data(d);
if (!nmk_chip)
return;
+
+ clk_enable(nmk_chip->clk);
writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC);
+ clk_disable(nmk_chip->clk);
}
enum nmk_gpio_irq_type {
@@ -586,11 +605,7 @@ static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable)
if (!nmk_chip)
return -EINVAL;
- if (enable)
- nmk_chip->enabled |= bitmask;
- else
- nmk_chip->enabled &= ~bitmask;
-
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
spin_lock(&nmk_chip->lock);
@@ -601,6 +616,7 @@ static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable)
spin_unlock(&nmk_chip->lock);
spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
@@ -628,10 +644,11 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
return -EINVAL;
bitmask = nmk_gpio_get_bitmask(gpio);
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
spin_lock(&nmk_chip->lock);
- if (!(nmk_chip->enabled & bitmask))
+ if (irqd_irq_disabled(d))
__nmk_gpio_set_wake(nmk_chip, gpio, on);
if (on)
@@ -641,13 +658,15 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
spin_unlock(&nmk_chip->lock);
spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
- bool enabled, wake = irqd_is_wakeup_set(d);
+ bool enabled = !irqd_irq_disabled(d);
+ bool wake = irqd_is_wakeup_set(d);
int gpio;
struct nmk_gpio_chip *nmk_chip;
unsigned long flags;
@@ -664,8 +683,7 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type)
if (type & IRQ_TYPE_LEVEL_LOW)
return -EINVAL;
- enabled = nmk_chip->enabled & bitmask;
-
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_chip->lock, flags);
if (enabled)
@@ -689,10 +707,28 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type)
__nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, true);
spin_unlock_irqrestore(&nmk_chip->lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
+static unsigned int nmk_gpio_irq_startup(struct irq_data *d)
+{
+ struct nmk_gpio_chip *nmk_chip = irq_data_get_irq_chip_data(d);
+
+ clk_enable(nmk_chip->clk);
+ nmk_gpio_irq_unmask(d);
+ return 0;
+}
+
+static void nmk_gpio_irq_shutdown(struct irq_data *d)
+{
+ struct nmk_gpio_chip *nmk_chip = irq_data_get_irq_chip_data(d);
+
+ nmk_gpio_irq_mask(d);
+ clk_disable(nmk_chip->clk);
+}
+
static struct irq_chip nmk_gpio_irq_chip = {
.name = "Nomadik-GPIO",
.irq_ack = nmk_gpio_irq_ack,
@@ -700,6 +736,8 @@ static struct irq_chip nmk_gpio_irq_chip = {
.irq_unmask = nmk_gpio_irq_unmask,
.irq_set_type = nmk_gpio_irq_set_type,
.irq_set_wake = nmk_gpio_irq_set_wake,
+ .irq_startup = nmk_gpio_irq_startup,
+ .irq_shutdown = nmk_gpio_irq_shutdown,
};
static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc,
@@ -726,7 +764,11 @@ static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc,
static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
struct nmk_gpio_chip *nmk_chip = irq_get_handler_data(irq);
- u32 status = readl(nmk_chip->addr + NMK_GPIO_IS);
+ u32 status;
+
+ clk_enable(nmk_chip->clk);
+ status = readl(nmk_chip->addr + NMK_GPIO_IS);
+ clk_disable(nmk_chip->clk);
__nmk_gpio_irq_handler(irq, desc, status);
}
@@ -772,7 +814,12 @@ static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned offset)
struct nmk_gpio_chip *nmk_chip =
container_of(chip, struct nmk_gpio_chip, chip);
+ clk_enable(nmk_chip->clk);
+
writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC);
+
+ clk_disable(nmk_chip->clk);
+
return 0;
}
@@ -781,8 +828,15 @@ static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned offset)
struct nmk_gpio_chip *nmk_chip =
container_of(chip, struct nmk_gpio_chip, chip);
u32 bit = 1 << offset;
+ int value;
+
+ clk_enable(nmk_chip->clk);
- return (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0;
+ value = (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0;
+
+ clk_disable(nmk_chip->clk);
+
+ return value;
}
static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset,
@@ -791,7 +845,11 @@ static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset,
struct nmk_gpio_chip *nmk_chip =
container_of(chip, struct nmk_gpio_chip, chip);
+ clk_enable(nmk_chip->clk);
+
__nmk_gpio_set_output(nmk_chip, offset, val);
+
+ clk_disable(nmk_chip->clk);
}
static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset,
@@ -800,8 +858,12 @@ static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset,
struct nmk_gpio_chip *nmk_chip =
container_of(chip, struct nmk_gpio_chip, chip);
+ clk_enable(nmk_chip->clk);
+
__nmk_gpio_make_output(nmk_chip, offset, val);
+ clk_disable(nmk_chip->clk);
+
return 0;
}
@@ -832,6 +894,8 @@ static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
[NMK_GPIO_ALT_C] = "altC",
};
+ clk_enable(nmk_chip->clk);
+
for (i = 0; i < chip->ngpio; i++, gpio++) {
const char *label = gpiochip_is_requested(chip, i);
bool pull;
@@ -876,6 +940,8 @@ static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
seq_printf(s, "\n");
}
+
+ clk_disable(nmk_chip->clk);
}
#else
@@ -893,6 +959,34 @@ static struct gpio_chip nmk_gpio_template = {
.can_sleep = 0,
};
+void nmk_gpio_clocks_enable(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_BANKS; i++) {
+ struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
+
+ if (!chip)
+ continue;
+
+ clk_enable(chip->clk);
+ }
+}
+
+void nmk_gpio_clocks_disable(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_BANKS; i++) {
+ struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
+
+ if (!chip)
+ continue;
+
+ clk_disable(chip->clk);
+ }
+}
+
/*
* Called from the suspend/resume path to only keep the real wakeup interrupts
* (those that have had set_irq_wake() called on them) as wakeup interrupts,
@@ -912,6 +1006,8 @@ void nmk_gpio_wakeups_suspend(void)
if (!chip)
break;
+ clk_enable(chip->clk);
+
chip->rwimsc = readl(chip->addr + NMK_GPIO_RWIMSC);
chip->fwimsc = readl(chip->addr + NMK_GPIO_FWIMSC);
@@ -926,6 +1022,8 @@ void nmk_gpio_wakeups_suspend(void)
/* 0 -> wakeup enable */
writel(~chip->real_wake, chip->addr + NMK_GPIO_SLPC);
}
+
+ clk_disable(chip->clk);
}
}
@@ -939,11 +1037,15 @@ void nmk_gpio_wakeups_resume(void)
if (!chip)
break;
+ clk_enable(chip->clk);
+
writel(chip->rwimsc, chip->addr + NMK_GPIO_RWIMSC);
writel(chip->fwimsc, chip->addr + NMK_GPIO_FWIMSC);
if (chip->sleepmode)
writel(chip->slpm, chip->addr + NMK_GPIO_SLPC);
+
+ clk_disable(chip->clk);
}
}
@@ -1010,8 +1112,6 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
goto out_release;
}
- clk_enable(clk);
-
nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL);
if (!nmk_chip) {
ret = -ENOMEM;
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 35bebde23e83..0b0562979171 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -25,7 +25,7 @@
#include <mach/hardware.h>
#include <asm/irq.h>
#include <mach/irqs.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#include <asm/mach/irq.h>
struct gpio_bank {
@@ -34,8 +34,8 @@ struct gpio_bank {
u16 irq;
u16 virtual_irq_start;
int method;
-#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS)
u32 suspend_wakeup;
+#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS)
u32 saved_wakeup;
#endif
u32 non_wakeup_gpios;
@@ -54,6 +54,11 @@ struct gpio_bank {
struct device *dev;
bool dbck_flag;
int stride;
+ u32 width;
+
+ void (*set_dataout)(struct gpio_bank *bank, int gpio, int enable);
+
+ struct omap_gpio_reg_offs *regs;
};
#ifdef CONFIG_ARCH_OMAP3
@@ -79,121 +84,18 @@ static struct omap3_gpio_regs gpio_context[OMAP34XX_NR_GPIOS];
*/
static struct gpio_bank *gpio_bank;
-static int bank_width;
-
/* TODO: Analyze removing gpio_bank_count usage from driver code */
int gpio_bank_count;
-static inline struct gpio_bank *get_gpio_bank(int gpio)
-{
- if (cpu_is_omap15xx()) {
- if (OMAP_GPIO_IS_MPUIO(gpio))
- return &gpio_bank[0];
- return &gpio_bank[1];
- }
- if (cpu_is_omap16xx()) {
- if (OMAP_GPIO_IS_MPUIO(gpio))
- return &gpio_bank[0];
- return &gpio_bank[1 + (gpio >> 4)];
- }
- if (cpu_is_omap7xx()) {
- if (OMAP_GPIO_IS_MPUIO(gpio))
- return &gpio_bank[0];
- return &gpio_bank[1 + (gpio >> 5)];
- }
- if (cpu_is_omap24xx())
- return &gpio_bank[gpio >> 5];
- if (cpu_is_omap34xx() || cpu_is_omap44xx())
- return &gpio_bank[gpio >> 5];
- BUG();
- return NULL;
-}
-
-static inline int get_gpio_index(int gpio)
-{
- if (cpu_is_omap7xx())
- return gpio & 0x1f;
- if (cpu_is_omap24xx())
- return gpio & 0x1f;
- if (cpu_is_omap34xx() || cpu_is_omap44xx())
- return gpio & 0x1f;
- return gpio & 0x0f;
-}
-
-static inline int gpio_valid(int gpio)
-{
- if (gpio < 0)
- return -1;
- if (cpu_class_is_omap1() && OMAP_GPIO_IS_MPUIO(gpio)) {
- if (gpio >= OMAP_MAX_GPIO_LINES + 16)
- return -1;
- return 0;
- }
- if (cpu_is_omap15xx() && gpio < 16)
- return 0;
- if ((cpu_is_omap16xx()) && gpio < 64)
- return 0;
- if (cpu_is_omap7xx() && gpio < 192)
- return 0;
- if (cpu_is_omap2420() && gpio < 128)
- return 0;
- if (cpu_is_omap2430() && gpio < 160)
- return 0;
- if ((cpu_is_omap34xx() || cpu_is_omap44xx()) && gpio < 192)
- return 0;
- return -1;
-}
-
-static int check_gpio(int gpio)
-{
- if (unlikely(gpio_valid(gpio) < 0)) {
- printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio);
- dump_stack();
- return -1;
- }
- return 0;
-}
+#define GPIO_INDEX(bank, gpio) (gpio % bank->width)
+#define GPIO_BIT(bank, gpio) (1 << GPIO_INDEX(bank, gpio))
static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
{
void __iomem *reg = bank->base;
u32 l;
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP1
- case METHOD_MPUIO:
- reg += OMAP_MPUIO_IO_CNTL / bank->stride;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP15XX
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_DIR_CONTROL;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_GPIO_1610:
- reg += OMAP1610_GPIO_DIRECTION;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
- case METHOD_GPIO_7XX:
- reg += OMAP7XX_GPIO_DIR_CONTROL;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- case METHOD_GPIO_24XX:
- reg += OMAP24XX_GPIO_OE;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP4)
- case METHOD_GPIO_44XX:
- reg += OMAP4_GPIO_OE;
- break;
-#endif
- default:
- WARN_ON(1);
- return;
- }
+ reg += bank->regs->direction;
l = __raw_readl(reg);
if (is_input)
l |= 1 << gpio;
@@ -202,174 +104,61 @@ static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
__raw_writel(l, reg);
}
-static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable)
+
+/* set data out value using dedicate set/clear register */
+static void _set_gpio_dataout_reg(struct gpio_bank *bank, int gpio, int enable)
{
void __iomem *reg = bank->base;
- u32 l = 0;
+ u32 l = GPIO_BIT(bank, gpio);
+
+ if (enable)
+ reg += bank->regs->set_dataout;
+ else
+ reg += bank->regs->clr_dataout;
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP1
- case METHOD_MPUIO:
- reg += OMAP_MPUIO_OUTPUT / bank->stride;
- l = __raw_readl(reg);
- if (enable)
- l |= 1 << gpio;
- else
- l &= ~(1 << gpio);
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP15XX
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_DATA_OUTPUT;
- l = __raw_readl(reg);
- if (enable)
- l |= 1 << gpio;
- else
- l &= ~(1 << gpio);
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_GPIO_1610:
- if (enable)
- reg += OMAP1610_GPIO_SET_DATAOUT;
- else
- reg += OMAP1610_GPIO_CLEAR_DATAOUT;
- l = 1 << gpio;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
- case METHOD_GPIO_7XX:
- reg += OMAP7XX_GPIO_DATA_OUTPUT;
- l = __raw_readl(reg);
- if (enable)
- l |= 1 << gpio;
- else
- l &= ~(1 << gpio);
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- case METHOD_GPIO_24XX:
- if (enable)
- reg += OMAP24XX_GPIO_SETDATAOUT;
- else
- reg += OMAP24XX_GPIO_CLEARDATAOUT;
- l = 1 << gpio;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP4
- case METHOD_GPIO_44XX:
- if (enable)
- reg += OMAP4_GPIO_SETDATAOUT;
- else
- reg += OMAP4_GPIO_CLEARDATAOUT;
- l = 1 << gpio;
- break;
-#endif
- default:
- WARN_ON(1);
- return;
- }
+ __raw_writel(l, reg);
+}
+
+/* set data out value using mask register */
+static void _set_gpio_dataout_mask(struct gpio_bank *bank, int gpio, int enable)
+{
+ void __iomem *reg = bank->base + bank->regs->dataout;
+ u32 gpio_bit = GPIO_BIT(bank, gpio);
+ u32 l;
+
+ l = __raw_readl(reg);
+ if (enable)
+ l |= gpio_bit;
+ else
+ l &= ~gpio_bit;
__raw_writel(l, reg);
}
static int _get_gpio_datain(struct gpio_bank *bank, int gpio)
{
- void __iomem *reg;
+ void __iomem *reg = bank->base + bank->regs->datain;
- if (check_gpio(gpio) < 0)
- return -EINVAL;
- reg = bank->base;
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP1
- case METHOD_MPUIO:
- reg += OMAP_MPUIO_INPUT_LATCH / bank->stride;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP15XX
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_DATA_INPUT;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_GPIO_1610:
- reg += OMAP1610_GPIO_DATAIN;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
- case METHOD_GPIO_7XX:
- reg += OMAP7XX_GPIO_DATA_INPUT;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- case METHOD_GPIO_24XX:
- reg += OMAP24XX_GPIO_DATAIN;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP4
- case METHOD_GPIO_44XX:
- reg += OMAP4_GPIO_DATAIN;
- break;
-#endif
- default:
- return -EINVAL;
- }
- return (__raw_readl(reg)
- & (1 << get_gpio_index(gpio))) != 0;
+ return (__raw_readl(reg) & GPIO_BIT(bank, gpio)) != 0;
}
static int _get_gpio_dataout(struct gpio_bank *bank, int gpio)
{
- void __iomem *reg;
+ void __iomem *reg = bank->base + bank->regs->dataout;
- if (check_gpio(gpio) < 0)
- return -EINVAL;
- reg = bank->base;
+ return (__raw_readl(reg) & GPIO_BIT(bank, gpio)) != 0;
+}
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP1
- case METHOD_MPUIO:
- reg += OMAP_MPUIO_OUTPUT / bank->stride;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP15XX
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_DATA_OUTPUT;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_GPIO_1610:
- reg += OMAP1610_GPIO_DATAOUT;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
- case METHOD_GPIO_7XX:
- reg += OMAP7XX_GPIO_DATA_OUTPUT;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- case METHOD_GPIO_24XX:
- reg += OMAP24XX_GPIO_DATAOUT;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP4
- case METHOD_GPIO_44XX:
- reg += OMAP4_GPIO_DATAOUT;
- break;
-#endif
- default:
- return -EINVAL;
- }
+static inline void _gpio_rmw(void __iomem *base, u32 reg, u32 mask, bool set)
+{
+ int l = __raw_readl(base + reg);
- return (__raw_readl(reg) & (1 << get_gpio_index(gpio))) != 0;
-}
+ if (set)
+ l |= mask;
+ else
+ l &= ~mask;
-#define MOD_REG_BIT(reg, bit_mask, set) \
-do { \
- int l = __raw_readl(base + reg); \
- if (set) l |= bit_mask; \
- else l &= ~bit_mask; \
- __raw_writel(l, base + reg); \
-} while(0)
+ __raw_writel(l, base + reg);
+}
/**
* _set_gpio_debounce - low level gpio debounce time
@@ -383,7 +172,7 @@ do { \
static void _set_gpio_debounce(struct gpio_bank *bank, unsigned gpio,
unsigned debounce)
{
- void __iomem *reg = bank->base;
+ void __iomem *reg;
u32 val;
u32 l;
@@ -397,21 +186,12 @@ static void _set_gpio_debounce(struct gpio_bank *bank, unsigned gpio,
else
debounce = (debounce / 0x1f) - 1;
- l = 1 << get_gpio_index(gpio);
-
- if (bank->method == METHOD_GPIO_44XX)
- reg += OMAP4_GPIO_DEBOUNCINGTIME;
- else
- reg += OMAP24XX_GPIO_DEBOUNCE_VAL;
+ l = GPIO_BIT(bank, gpio);
+ reg = bank->base + bank->regs->debounce;
__raw_writel(debounce, reg);
- reg = bank->base;
- if (bank->method == METHOD_GPIO_44XX)
- reg += OMAP4_GPIO_DEBOUNCENABLE;
- else
- reg += OMAP24XX_GPIO_DEBOUNCE_EN;
-
+ reg = bank->base + bank->regs->debounce_en;
val = __raw_readl(reg);
if (debounce) {
@@ -434,28 +214,28 @@ static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio,
u32 gpio_bit = 1 << gpio;
if (cpu_is_omap44xx()) {
- MOD_REG_BIT(OMAP4_GPIO_LEVELDETECT0, gpio_bit,
- trigger & IRQ_TYPE_LEVEL_LOW);
- MOD_REG_BIT(OMAP4_GPIO_LEVELDETECT1, gpio_bit,
- trigger & IRQ_TYPE_LEVEL_HIGH);
- MOD_REG_BIT(OMAP4_GPIO_RISINGDETECT, gpio_bit,
- trigger & IRQ_TYPE_EDGE_RISING);
- MOD_REG_BIT(OMAP4_GPIO_FALLINGDETECT, gpio_bit,
- trigger & IRQ_TYPE_EDGE_FALLING);
+ _gpio_rmw(base, OMAP4_GPIO_LEVELDETECT0, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_LOW);
+ _gpio_rmw(base, OMAP4_GPIO_LEVELDETECT1, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_HIGH);
+ _gpio_rmw(base, OMAP4_GPIO_RISINGDETECT, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_RISING);
+ _gpio_rmw(base, OMAP4_GPIO_FALLINGDETECT, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_FALLING);
} else {
- MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT0, gpio_bit,
- trigger & IRQ_TYPE_LEVEL_LOW);
- MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT1, gpio_bit,
- trigger & IRQ_TYPE_LEVEL_HIGH);
- MOD_REG_BIT(OMAP24XX_GPIO_RISINGDETECT, gpio_bit,
- trigger & IRQ_TYPE_EDGE_RISING);
- MOD_REG_BIT(OMAP24XX_GPIO_FALLINGDETECT, gpio_bit,
- trigger & IRQ_TYPE_EDGE_FALLING);
+ _gpio_rmw(base, OMAP24XX_GPIO_LEVELDETECT0, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_LOW);
+ _gpio_rmw(base, OMAP24XX_GPIO_LEVELDETECT1, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_HIGH);
+ _gpio_rmw(base, OMAP24XX_GPIO_RISINGDETECT, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_RISING);
+ _gpio_rmw(base, OMAP24XX_GPIO_FALLINGDETECT, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_FALLING);
}
if (likely(!(bank->non_wakeup_gpios & gpio_bit))) {
if (cpu_is_omap44xx()) {
- MOD_REG_BIT(OMAP4_GPIO_IRQWAKEN0, gpio_bit,
- trigger != 0);
+ _gpio_rmw(base, OMAP4_GPIO_IRQWAKEN0, gpio_bit,
+ trigger != 0);
} else {
/*
* GPIO wakeup request can only be generated on edge
@@ -629,9 +409,6 @@ static int gpio_irq_type(struct irq_data *d, unsigned type)
else
gpio = d->irq - IH_GPIO_BASE;
- if (check_gpio(gpio) < 0)
- return -EINVAL;
-
if (type & ~IRQ_TYPE_SENSE_MASK)
return -EINVAL;
@@ -642,7 +419,7 @@ static int gpio_irq_type(struct irq_data *d, unsigned type)
bank = irq_data_get_irq_chip_data(d);
spin_lock_irqsave(&bank->lock, flags);
- retval = _set_gpio_triggering(bank, get_gpio_index(gpio), type);
+ retval = _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), type);
spin_unlock_irqrestore(&bank->lock, flags);
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
@@ -657,195 +434,81 @@ static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
{
void __iomem *reg = bank->base;
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP1
- case METHOD_MPUIO:
- /* MPUIO irqstatus is reset by reading the status register,
- * so do nothing here */
- return;
-#endif
-#ifdef CONFIG_ARCH_OMAP15XX
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_INT_STATUS;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_GPIO_1610:
- reg += OMAP1610_GPIO_IRQSTATUS1;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
- case METHOD_GPIO_7XX:
- reg += OMAP7XX_GPIO_INT_STATUS;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- case METHOD_GPIO_24XX:
- reg += OMAP24XX_GPIO_IRQSTATUS1;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP4)
- case METHOD_GPIO_44XX:
- reg += OMAP4_GPIO_IRQSTATUS0;
- break;
-#endif
- default:
- WARN_ON(1);
- return;
- }
+ reg += bank->regs->irqstatus;
__raw_writel(gpio_mask, reg);
/* Workaround for clearing DSP GPIO interrupts to allow retention */
- if (cpu_is_omap24xx() || cpu_is_omap34xx())
- reg = bank->base + OMAP24XX_GPIO_IRQSTATUS2;
- else if (cpu_is_omap44xx())
- reg = bank->base + OMAP4_GPIO_IRQSTATUS1;
-
- if (cpu_is_omap24xx() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
+ if (bank->regs->irqstatus2) {
+ reg = bank->base + bank->regs->irqstatus2;
__raw_writel(gpio_mask, reg);
+ }
/* Flush posted write for the irq status to avoid spurious interrupts */
__raw_readl(reg);
- }
}
static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio)
{
- _clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio));
+ _clear_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
}
static u32 _get_gpio_irqbank_mask(struct gpio_bank *bank)
{
void __iomem *reg = bank->base;
- int inv = 0;
u32 l;
- u32 mask;
-
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP1
- case METHOD_MPUIO:
- reg += OMAP_MPUIO_GPIO_MASKIT / bank->stride;
- mask = 0xffff;
- inv = 1;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP15XX
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_INT_MASK;
- mask = 0xffff;
- inv = 1;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_GPIO_1610:
- reg += OMAP1610_GPIO_IRQENABLE1;
- mask = 0xffff;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
- case METHOD_GPIO_7XX:
- reg += OMAP7XX_GPIO_INT_MASK;
- mask = 0xffffffff;
- inv = 1;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- case METHOD_GPIO_24XX:
- reg += OMAP24XX_GPIO_IRQENABLE1;
- mask = 0xffffffff;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP4)
- case METHOD_GPIO_44XX:
- reg += OMAP4_GPIO_IRQSTATUSSET0;
- mask = 0xffffffff;
- break;
-#endif
- default:
- WARN_ON(1);
- return 0;
- }
+ u32 mask = (1 << bank->width) - 1;
+ reg += bank->regs->irqenable;
l = __raw_readl(reg);
- if (inv)
+ if (bank->regs->irqenable_inv)
l = ~l;
l &= mask;
return l;
}
-static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable)
+static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
{
void __iomem *reg = bank->base;
u32 l;
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP1
- case METHOD_MPUIO:
- reg += OMAP_MPUIO_GPIO_MASKIT / bank->stride;
- l = __raw_readl(reg);
- if (enable)
- l &= ~(gpio_mask);
- else
- l |= gpio_mask;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP15XX
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_INT_MASK;
+ if (bank->regs->set_irqenable) {
+ reg += bank->regs->set_irqenable;
+ l = gpio_mask;
+ } else {
+ reg += bank->regs->irqenable;
l = __raw_readl(reg);
- if (enable)
- l &= ~(gpio_mask);
+ if (bank->regs->irqenable_inv)
+ l &= ~gpio_mask;
else
l |= gpio_mask;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_GPIO_1610:
- if (enable)
- reg += OMAP1610_GPIO_SET_IRQENABLE1;
- else
- reg += OMAP1610_GPIO_CLEAR_IRQENABLE1;
+ }
+
+ __raw_writel(l, reg);
+}
+
+static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
+{
+ void __iomem *reg = bank->base;
+ u32 l;
+
+ if (bank->regs->clr_irqenable) {
+ reg += bank->regs->clr_irqenable;
l = gpio_mask;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
- case METHOD_GPIO_7XX:
- reg += OMAP7XX_GPIO_INT_MASK;
+ } else {
+ reg += bank->regs->irqenable;
l = __raw_readl(reg);
- if (enable)
- l &= ~(gpio_mask);
- else
+ if (bank->regs->irqenable_inv)
l |= gpio_mask;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- case METHOD_GPIO_24XX:
- if (enable)
- reg += OMAP24XX_GPIO_SETIRQENABLE1;
- else
- reg += OMAP24XX_GPIO_CLEARIRQENABLE1;
- l = gpio_mask;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP4
- case METHOD_GPIO_44XX:
- if (enable)
- reg += OMAP4_GPIO_IRQSTATUSSET0;
else
- reg += OMAP4_GPIO_IRQSTATUSCLR0;
- l = gpio_mask;
- break;
-#endif
- default:
- WARN_ON(1);
- return;
+ l &= ~gpio_mask;
}
+
__raw_writel(l, reg);
}
static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable)
{
- _enable_gpio_irqbank(bank, 1 << get_gpio_index(gpio), enable);
+ _enable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
}
/*
@@ -858,50 +521,32 @@ static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int ena
*/
static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
{
- unsigned long uninitialized_var(flags);
+ u32 gpio_bit = GPIO_BIT(bank, gpio);
+ unsigned long flags;
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_MPUIO:
- case METHOD_GPIO_1610:
- spin_lock_irqsave(&bank->lock, flags);
- if (enable)
- bank->suspend_wakeup |= (1 << gpio);
- else
- bank->suspend_wakeup &= ~(1 << gpio);
- spin_unlock_irqrestore(&bank->lock, flags);
- return 0;
-#endif
-#ifdef CONFIG_ARCH_OMAP2PLUS
- case METHOD_GPIO_24XX:
- case METHOD_GPIO_44XX:
- if (bank->non_wakeup_gpios & (1 << gpio)) {
- printk(KERN_ERR "Unable to modify wakeup on "
- "non-wakeup GPIO%d\n",
- (bank - gpio_bank) * 32 + gpio);
- return -EINVAL;
- }
- spin_lock_irqsave(&bank->lock, flags);
- if (enable)
- bank->suspend_wakeup |= (1 << gpio);
- else
- bank->suspend_wakeup &= ~(1 << gpio);
- spin_unlock_irqrestore(&bank->lock, flags);
- return 0;
-#endif
- default:
- printk(KERN_ERR "Can't enable GPIO wakeup for method %i\n",
- bank->method);
+ if (bank->non_wakeup_gpios & gpio_bit) {
+ dev_err(bank->dev,
+ "Unable to modify wakeup on non-wakeup GPIO%d\n", gpio);
return -EINVAL;
}
+
+ spin_lock_irqsave(&bank->lock, flags);
+ if (enable)
+ bank->suspend_wakeup |= gpio_bit;
+ else
+ bank->suspend_wakeup &= ~gpio_bit;
+
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ return 0;
}
static void _reset_gpio(struct gpio_bank *bank, int gpio)
{
- _set_gpio_direction(bank, get_gpio_index(gpio), 1);
+ _set_gpio_direction(bank, GPIO_INDEX(bank, gpio), 1);
_set_gpio_irqenable(bank, gpio, 0);
_clear_gpio_irqstatus(bank, gpio);
- _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE);
+ _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), IRQ_TYPE_NONE);
}
/* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
@@ -911,10 +556,8 @@ static int gpio_wake_enable(struct irq_data *d, unsigned int enable)
struct gpio_bank *bank;
int retval;
- if (check_gpio(gpio) < 0)
- return -ENODEV;
bank = irq_data_get_irq_chip_data(d);
- retval = _set_gpio_wakeup(bank, get_gpio_index(gpio), enable);
+ retval = _set_gpio_wakeup(bank, gpio, enable);
return retval;
}
@@ -1030,31 +673,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
chained_irq_enter(chip, desc);
bank = irq_get_handler_data(irq);
-#ifdef CONFIG_ARCH_OMAP1
- if (bank->method == METHOD_MPUIO)
- isr_reg = bank->base +
- OMAP_MPUIO_GPIO_INT / bank->stride;
-#endif
-#ifdef CONFIG_ARCH_OMAP15XX
- if (bank->method == METHOD_GPIO_1510)
- isr_reg = bank->base + OMAP1510_GPIO_INT_STATUS;
-#endif
-#if defined(CONFIG_ARCH_OMAP16XX)
- if (bank->method == METHOD_GPIO_1610)
- isr_reg = bank->base + OMAP1610_GPIO_IRQSTATUS1;
-#endif
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
- if (bank->method == METHOD_GPIO_7XX)
- isr_reg = bank->base + OMAP7XX_GPIO_INT_STATUS;
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- if (bank->method == METHOD_GPIO_24XX)
- isr_reg = bank->base + OMAP24XX_GPIO_IRQSTATUS1;
-#endif
-#if defined(CONFIG_ARCH_OMAP4)
- if (bank->method == METHOD_GPIO_44XX)
- isr_reg = bank->base + OMAP4_GPIO_IRQSTATUS0;
-#endif
+ isr_reg = bank->base + bank->regs->irqstatus;
if (WARN_ON(!isr_reg))
goto exit;
@@ -1076,9 +695,9 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
/* clear edge sensitive interrupts before handler(s) are
called so that we don't miss any interrupt occurred while
executing them */
- _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 0);
+ _disable_gpio_irqbank(bank, isr_saved & ~level_mask);
_clear_gpio_irqbank(bank, isr_saved & ~level_mask);
- _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 1);
+ _enable_gpio_irqbank(bank, isr_saved & ~level_mask);
/* if there is only edge sensitive GPIO pin interrupts
configured, we could unmask GPIO bank interrupt immediately */
@@ -1094,7 +713,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
gpio_irq = bank->virtual_irq_start;
for (; isr != 0; isr >>= 1, gpio_irq++) {
- gpio_index = get_gpio_index(irq_to_gpio(gpio_irq));
+ gpio_index = GPIO_INDEX(bank, irq_to_gpio(gpio_irq));
if (!(isr & 1))
continue;
@@ -1150,7 +769,7 @@ static void gpio_mask_irq(struct irq_data *d)
spin_lock_irqsave(&bank->lock, flags);
_set_gpio_irqenable(bank, gpio, 0);
- _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE);
+ _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), IRQ_TYPE_NONE);
spin_unlock_irqrestore(&bank->lock, flags);
}
@@ -1158,13 +777,13 @@ static void gpio_unmask_irq(struct irq_data *d)
{
unsigned int gpio = d->irq - IH_GPIO_BASE;
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
- unsigned int irq_mask = 1 << get_gpio_index(gpio);
+ unsigned int irq_mask = GPIO_BIT(bank, gpio);
u32 trigger = irqd_get_trigger_type(d);
unsigned long flags;
spin_lock_irqsave(&bank->lock, flags);
if (trigger)
- _set_gpio_triggering(bank, get_gpio_index(gpio), trigger);
+ _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), trigger);
/* For level-triggered GPIOs, the clearing must be done after
* the HW source is cleared, thus after the handler has run */
@@ -1191,45 +810,8 @@ static struct irq_chip gpio_irq_chip = {
#ifdef CONFIG_ARCH_OMAP1
-/* MPUIO uses the always-on 32k clock */
-
-static void mpuio_ack_irq(struct irq_data *d)
-{
- /* The ISR is reset automatically, so do nothing here. */
-}
-
-static void mpuio_mask_irq(struct irq_data *d)
-{
- unsigned int gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE);
- struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
-
- _set_gpio_irqenable(bank, gpio, 0);
-}
-
-static void mpuio_unmask_irq(struct irq_data *d)
-{
- unsigned int gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE);
- struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
-
- _set_gpio_irqenable(bank, gpio, 1);
-}
-
-static struct irq_chip mpuio_irq_chip = {
- .name = "MPUIO",
- .irq_ack = mpuio_ack_irq,
- .irq_mask = mpuio_mask_irq,
- .irq_unmask = mpuio_unmask_irq,
- .irq_set_type = gpio_irq_type,
-#ifdef CONFIG_ARCH_OMAP16XX
- /* REVISIT: assuming only 16xx supports MPUIO wake events */
- .irq_set_wake = gpio_wake_enable,
-#endif
-};
-
-
#define bank_is_mpuio(bank) ((bank)->method == METHOD_MPUIO)
-
#ifdef CONFIG_ARCH_OMAP16XX
#include <linux/platform_device.h>
@@ -1289,7 +871,7 @@ static struct platform_device omap_mpuio_device = {
static inline void mpuio_init(void)
{
- struct gpio_bank *bank = get_gpio_bank(OMAP_MPUIO(0));
+ struct gpio_bank *bank = &gpio_bank[0];
platform_set_drvdata(&omap_mpuio_device, bank);
if (platform_driver_register(&omap_mpuio_driver) == 0)
@@ -1302,8 +884,6 @@ static inline void mpuio_init(void) {}
#else
-extern struct irq_chip mpuio_irq_chip;
-
#define bank_is_mpuio(bank) 0
static inline void mpuio_init(void) {}
@@ -1329,31 +909,8 @@ static int gpio_input(struct gpio_chip *chip, unsigned offset)
static int gpio_is_input(struct gpio_bank *bank, int mask)
{
- void __iomem *reg = bank->base;
+ void __iomem *reg = bank->base + bank->regs->direction;
- switch (bank->method) {
- case METHOD_MPUIO:
- reg += OMAP_MPUIO_IO_CNTL / bank->stride;
- break;
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_DIR_CONTROL;
- break;
- case METHOD_GPIO_1610:
- reg += OMAP1610_GPIO_DIRECTION;
- break;
- case METHOD_GPIO_7XX:
- reg += OMAP7XX_GPIO_DIR_CONTROL;
- break;
- case METHOD_GPIO_24XX:
- reg += OMAP24XX_GPIO_OE;
- break;
- case METHOD_GPIO_44XX:
- reg += OMAP4_GPIO_OE;
- break;
- default:
- WARN_ONCE(1, "gpio_is_input: incorrect OMAP GPIO method");
- return -EINVAL;
- }
return __raw_readl(reg) & mask;
}
@@ -1365,9 +922,9 @@ static int gpio_get(struct gpio_chip *chip, unsigned offset)
u32 mask;
gpio = chip->base + offset;
- bank = get_gpio_bank(gpio);
+ bank = container_of(chip, struct gpio_bank, chip);
reg = bank->base;
- mask = 1 << get_gpio_index(gpio);
+ mask = GPIO_BIT(bank, gpio);
if (gpio_is_input(bank, mask))
return _get_gpio_datain(bank, gpio);
@@ -1382,7 +939,7 @@ static int gpio_output(struct gpio_chip *chip, unsigned offset, int value)
bank = container_of(chip, struct gpio_bank, chip);
spin_lock_irqsave(&bank->lock, flags);
- _set_gpio_dataout(bank, offset, value);
+ bank->set_dataout(bank, offset, value);
_set_gpio_direction(bank, offset, 0);
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
@@ -1416,7 +973,7 @@ static void gpio_set(struct gpio_chip *chip, unsigned offset, int value)
bank = container_of(chip, struct gpio_bank, chip);
spin_lock_irqsave(&bank->lock, flags);
- _set_gpio_dataout(bank, offset, value);
+ bank->set_dataout(bank, offset, value);
spin_unlock_irqrestore(&bank->lock, flags);
}
@@ -1432,19 +989,17 @@ static int gpio_2irq(struct gpio_chip *chip, unsigned offset)
static void __init omap_gpio_show_rev(struct gpio_bank *bank)
{
+ static bool called;
u32 rev;
- if (cpu_is_omap16xx() && !(bank->method != METHOD_MPUIO))
- rev = __raw_readw(bank->base + OMAP1610_GPIO_REVISION);
- else if (cpu_is_omap24xx() || cpu_is_omap34xx())
- rev = __raw_readl(bank->base + OMAP24XX_GPIO_REVISION);
- else if (cpu_is_omap44xx())
- rev = __raw_readl(bank->base + OMAP4_GPIO_REVISION);
- else
+ if (called || bank->regs->revision == USHRT_MAX)
return;
- printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n",
+ rev = __raw_readw(bank->base + bank->regs->revision);
+ pr_info("OMAP GPIO hardware version %d.%d\n",
(rev >> 4) & 0x0f, rev & 0x0f);
+
+ called = true;
}
/* This lock class tells lockdep that GPIO irqs are in a different
@@ -1526,6 +1081,35 @@ static void omap_gpio_mod_init(struct gpio_bank *bank, int id)
}
}
+static __init void
+omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start,
+ unsigned int num)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("MPUIO", 1, irq_start, bank->base,
+ handle_simple_irq);
+ if (!gc) {
+ dev_err(bank->dev, "Memory alloc failed for gc\n");
+ return;
+ }
+
+ ct = gc->chip_types;
+
+ /* NOTE: No ack required, reading IRQ status clears it. */
+ ct->chip.irq_mask = irq_gc_mask_set_bit;
+ ct->chip.irq_unmask = irq_gc_mask_clr_bit;
+ ct->chip.irq_set_type = gpio_irq_type;
+ /* REVISIT: assuming only 16xx supports MPUIO wake events */
+ if (cpu_is_omap16xx())
+ ct->chip.irq_set_wake = gpio_wake_enable,
+
+ ct->regs.mask = OMAP_MPUIO_GPIO_INT / bank->stride;
+ irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
+ IRQ_NOREQUEST | IRQ_NOPROBE, 0);
+}
+
static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
{
int j;
@@ -1553,22 +1137,23 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
} else {
bank->chip.label = "gpio";
bank->chip.base = gpio;
- gpio += bank_width;
+ gpio += bank->width;
}
- bank->chip.ngpio = bank_width;
+ bank->chip.ngpio = bank->width;
gpiochip_add(&bank->chip);
for (j = bank->virtual_irq_start;
- j < bank->virtual_irq_start + bank_width; j++) {
+ j < bank->virtual_irq_start + bank->width; j++) {
irq_set_lockdep_class(j, &gpio_lock_class);
irq_set_chip_data(j, bank);
- if (bank_is_mpuio(bank))
- irq_set_chip(j, &mpuio_irq_chip);
- else
+ if (bank_is_mpuio(bank)) {
+ omap_mpuio_alloc_gc(bank, j, bank->width);
+ } else {
irq_set_chip(j, &gpio_irq_chip);
- irq_set_handler(j, handle_simple_irq);
- set_irq_flags(j, IRQF_VALID);
+ irq_set_handler(j, handle_simple_irq);
+ set_irq_flags(j, IRQF_VALID);
+ }
}
irq_set_chained_handler(bank->irq, gpio_irq_handler);
irq_set_handler_data(bank->irq, bank);
@@ -1610,7 +1195,14 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
bank->dev = &pdev->dev;
bank->dbck_flag = pdata->dbck_flag;
bank->stride = pdata->bank_stride;
- bank_width = pdata->bank_width;
+ bank->width = pdata->bank_width;
+
+ bank->regs = pdata->regs;
+
+ if (bank->regs->set_dataout && bank->regs->clr_dataout)
+ bank->set_dataout = _set_gpio_dataout_reg;
+ else
+ bank->set_dataout = _set_gpio_dataout_mask;
spin_lock_init(&bank->lock);
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/gpio-pca953x.c
index 0451d7ac94ac..d3f3e8f54561 100644
--- a/drivers/gpio/pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -1,5 +1,5 @@
/*
- * pca953x.c - 4/8/16 bit I/O ports
+ * PCA953x 4/8/16 bit I/O ports
*
* Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
* Copyright (C) 2007 Marvell International Ltd.
@@ -21,7 +21,6 @@
#include <linux/slab.h>
#ifdef CONFIG_OF_GPIO
#include <linux/of_platform.h>
-#include <linux/of_gpio.h>
#endif
#define PCA953X_INPUT 0
@@ -85,7 +84,6 @@ struct pca953x_chip {
#endif
struct i2c_client *client;
- struct pca953x_platform_data *dyn_pdata;
struct gpio_chip gpio_chip;
const char *const *names;
int chip_type;
@@ -437,7 +435,7 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid)
do {
level = __ffs(pending);
- generic_handle_irq(level + chip->irq_base);
+ handle_nested_irq(level + chip->irq_base);
pending &= ~(1 << level);
} while (pending);
@@ -446,13 +444,13 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid)
}
static int pca953x_irq_setup(struct pca953x_chip *chip,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id,
+ int irq_base)
{
struct i2c_client *client = chip->client;
- struct pca953x_platform_data *pdata = client->dev.platform_data;
int ret, offset = 0;
- if (pdata->irq_base != -1
+ if (irq_base != -1
&& (id->driver_data & PCA_INT)) {
int lvl;
@@ -474,15 +472,19 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
* this purpose.
*/
chip->irq_stat &= chip->reg_direction;
- chip->irq_base = pdata->irq_base;
mutex_init(&chip->irq_lock);
+ chip->irq_base = irq_alloc_descs(-1, irq_base, chip->gpio_chip.ngpio, -1);
+ if (chip->irq_base < 0)
+ goto out_failed;
+
for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) {
int irq = lvl + chip->irq_base;
+ irq_clear_status_flags(irq, IRQ_NOREQUEST);
irq_set_chip_data(irq, chip);
- irq_set_chip_and_handler(irq, &pca953x_irq_chip,
- handle_simple_irq);
+ irq_set_chip(irq, &pca953x_irq_chip);
+ irq_set_nested_thread(irq, true);
#ifdef CONFIG_ARM
set_irq_flags(irq, IRQF_VALID);
#else
@@ -493,8 +495,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
ret = request_threaded_irq(client->irq,
NULL,
pca953x_irq_handler,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
dev_name(&client->dev), chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
@@ -514,17 +515,19 @@ out_failed:
static void pca953x_irq_teardown(struct pca953x_chip *chip)
{
- if (chip->irq_base != -1)
+ if (chip->irq_base != -1) {
+ irq_free_descs(chip->irq_base, chip->gpio_chip.ngpio);
free_irq(chip->client->irq, chip);
+ }
}
#else /* CONFIG_GPIO_PCA953X_IRQ */
static int pca953x_irq_setup(struct pca953x_chip *chip,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id,
+ int irq_base)
{
struct i2c_client *client = chip->client;
- struct pca953x_platform_data *pdata = client->dev.platform_data;
- if (pdata->irq_base != -1 && (id->driver_data & PCA_INT))
+ if (irq_base != -1 && (id->driver_data & PCA_INT))
dev_warn(&client->dev, "interrupt support not compiled in\n");
return 0;
@@ -541,46 +544,40 @@ static void pca953x_irq_teardown(struct pca953x_chip *chip)
#ifdef CONFIG_OF_GPIO
/*
* Translate OpenFirmware node properties into platform_data
+ * WARNING: This is DEPRECATED and will be removed eventually!
*/
-static struct pca953x_platform_data *
-pca953x_get_alt_pdata(struct i2c_client *client)
+static void
+pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, int *invert)
{
- struct pca953x_platform_data *pdata;
struct device_node *node;
const __be32 *val;
int size;
node = client->dev.of_node;
if (node == NULL)
- return NULL;
+ return;
- pdata = kzalloc(sizeof(struct pca953x_platform_data), GFP_KERNEL);
- if (pdata == NULL) {
- dev_err(&client->dev, "Unable to allocate platform_data\n");
- return NULL;
- }
-
- pdata->gpio_base = -1;
+ *gpio_base = -1;
val = of_get_property(node, "linux,gpio-base", &size);
+ WARN(val, "%s: device-tree property 'linux,gpio-base' is deprecated!", __func__);
if (val) {
if (size != sizeof(*val))
dev_warn(&client->dev, "%s: wrong linux,gpio-base\n",
node->full_name);
else
- pdata->gpio_base = be32_to_cpup(val);
+ *gpio_base = be32_to_cpup(val);
}
val = of_get_property(node, "polarity", NULL);
+ WARN(val, "%s: device-tree property 'polarity' is deprecated!", __func__);
if (val)
- pdata->invert = *val;
-
- return pdata;
+ *invert = *val;
}
#else
-static struct pca953x_platform_data *
-pca953x_get_alt_pdata(struct i2c_client *client)
+static void
+pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, int *invert)
{
- return NULL;
+ *gpio_base = -1;
}
#endif
@@ -599,9 +596,6 @@ static int __devinit device_pca953x_init(struct pca953x_chip *chip, int invert)
/* set platform specific polarity inversion */
ret = pca953x_write_reg(chip, PCA953X_INVERT, invert);
- if (ret)
- goto out;
- return 0;
out:
return ret;
}
@@ -642,33 +636,30 @@ static int __devinit pca953x_probe(struct i2c_client *client,
{
struct pca953x_platform_data *pdata;
struct pca953x_chip *chip;
- int ret = 0;
+ int irq_base=0, invert=0;
+ int ret;
chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
pdata = client->dev.platform_data;
- if (pdata == NULL) {
- pdata = pca953x_get_alt_pdata(client);
- /*
- * Unlike normal platform_data, this is allocated
- * dynamically and must be freed in the driver
- */
- chip->dyn_pdata = pdata;
- }
-
- if (pdata == NULL) {
- dev_dbg(&client->dev, "no platform data\n");
- ret = -EINVAL;
- goto out_failed;
+ if (pdata) {
+ irq_base = pdata->irq_base;
+ chip->gpio_start = pdata->gpio_base;
+ invert = pdata->invert;
+ chip->names = pdata->names;
+ } else {
+ pca953x_get_alt_pdata(client, &chip->gpio_start, &invert);
+#ifdef CONFIG_OF_GPIO
+ /* If I2C node has no interrupts property, disable GPIO interrupts */
+ if (of_find_property(client->dev.of_node, "interrupts", NULL) == NULL)
+ irq_base = -1;
+#endif
}
chip->client = client;
- chip->gpio_start = pdata->gpio_base;
-
- chip->names = pdata->names;
chip->chip_type = id->driver_data & (PCA953X_TYPE | PCA957X_TYPE);
mutex_init(&chip->i2c_lock);
@@ -679,13 +670,13 @@ static int __devinit pca953x_probe(struct i2c_client *client,
pca953x_setup_gpio(chip, id->driver_data & PCA_GPIO_MASK);
if (chip->chip_type == PCA953X_TYPE)
- device_pca953x_init(chip, pdata->invert);
- else if (chip->chip_type == PCA957X_TYPE)
- device_pca957x_init(chip, pdata->invert);
+ ret = device_pca953x_init(chip, invert);
else
+ ret = device_pca957x_init(chip, invert);
+ if (ret)
goto out_failed;
- ret = pca953x_irq_setup(chip, id);
+ ret = pca953x_irq_setup(chip, id, irq_base);
if (ret)
goto out_failed;
@@ -693,7 +684,7 @@ static int __devinit pca953x_probe(struct i2c_client *client,
if (ret)
goto out_failed_irq;
- if (pdata->setup) {
+ if (pdata && pdata->setup) {
ret = pdata->setup(client, chip->gpio_chip.base,
chip->gpio_chip.ngpio, pdata->context);
if (ret < 0)
@@ -706,7 +697,6 @@ static int __devinit pca953x_probe(struct i2c_client *client,
out_failed_irq:
pca953x_irq_teardown(chip);
out_failed:
- kfree(chip->dyn_pdata);
kfree(chip);
return ret;
}
@@ -717,7 +707,7 @@ static int pca953x_remove(struct i2c_client *client)
struct pca953x_chip *chip = i2c_get_clientdata(client);
int ret = 0;
- if (pdata->teardown) {
+ if (pdata && pdata->teardown) {
ret = pdata->teardown(client, chip->gpio_chip.base,
chip->gpio_chip.ngpio, pdata->context);
if (ret < 0) {
@@ -735,7 +725,6 @@ static int pca953x_remove(struct i2c_client *client)
}
pca953x_irq_teardown(chip);
- kfree(chip->dyn_pdata);
kfree(chip);
return 0;
}
diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 879b473aab5a..3e1f1ecd07be 100644
--- a/drivers/gpio/pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -1,5 +1,5 @@
/*
- * pcf857x - driver for pcf857x, pca857x, and pca967x I2C GPIO expanders
+ * Driver for pcf857x, pca857x, and pca967x I2C GPIO expanders
*
* Copyright (C) 2007 David Brownell
*
@@ -23,6 +23,7 @@
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/i2c/pcf857x.h>
+#include <linux/module.h>
static const struct i2c_device_id pcf857x_id[] = {
diff --git a/drivers/gpio/pch_gpio.c b/drivers/gpio/gpio-pch.c
index 36919e77c495..a6008e123d04 100644
--- a/drivers/gpio/pch_gpio.c
+++ b/drivers/gpio/gpio-pch.c
@@ -14,12 +14,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
-#define PCH_GPIO_ALL_PINS 0xfff /* Mask for GPIO pins 0 to 11 */
-#define GPIO_NUM_PINS 12 /* Specifies number of GPIO PINS GPIO0-GPIO11 */
+#define PCH_EDGE_FALLING 0
+#define PCH_EDGE_RISING BIT(0)
+#define PCH_LEVEL_L BIT(1)
+#define PCH_LEVEL_H (BIT(0) | BIT(1))
+#define PCH_EDGE_BOTH BIT(2)
+#define PCH_IM_MASK (BIT(0) | BIT(1) | BIT(2))
+
+#define PCH_IRQ_BASE 24
struct pch_regs {
u32 ien;
@@ -33,18 +42,43 @@ struct pch_regs {
u32 pm;
u32 im0;
u32 im1;
- u32 reserved[4];
+ u32 reserved[3];
+ u32 gpio_use_sel;
u32 reset;
};
+enum pch_type_t {
+ INTEL_EG20T_PCH,
+ OKISEMI_ML7223m_IOH, /* OKISEMI ML7223 IOH PCIe Bus-m */
+ OKISEMI_ML7223n_IOH /* OKISEMI ML7223 IOH PCIe Bus-n */
+};
+
+/* Specifies number of GPIO PINS */
+static int gpio_pins[] = {
+ [INTEL_EG20T_PCH] = 12,
+ [OKISEMI_ML7223m_IOH] = 8,
+ [OKISEMI_ML7223n_IOH] = 8,
+};
+
/**
* struct pch_gpio_reg_data - The register store data.
+ * @ien_reg: To store contents of IEN register.
+ * @imask_reg: To store contents of IMASK register.
* @po_reg: To store contents of PO register.
* @pm_reg: To store contents of PM register.
+ * @im0_reg: To store contents of IM0 register.
+ * @im1_reg: To store contents of IM1 register.
+ * @gpio_use_sel_reg : To store contents of GPIO_USE_SEL register.
+ * (Only ML7223 Bus-n)
*/
struct pch_gpio_reg_data {
+ u32 ien_reg;
+ u32 imask_reg;
u32 po_reg;
u32 pm_reg;
+ u32 im0_reg;
+ u32 im1_reg;
+ u32 gpio_use_sel_reg;
};
/**
@@ -55,6 +89,12 @@ struct pch_gpio_reg_data {
* @gpio: Data for GPIO infrastructure.
* @pch_gpio_reg: Memory mapped Register data is saved here
* when suspend.
+ * @lock: Used for register access protection
+ * @irq_base: Save base of IRQ number for interrupt
+ * @ioh: IOH ID
+ * @spinlock: Used for register access protection in
+ * interrupt context pch_irq_mask,
+ * pch_irq_unmask and pch_irq_type;
*/
struct pch_gpio {
void __iomem *base;
@@ -63,6 +103,9 @@ struct pch_gpio {
struct gpio_chip gpio;
struct pch_gpio_reg_data pch_gpio_reg;
struct mutex lock;
+ int irq_base;
+ enum pch_type_t ioh;
+ spinlock_t spinlock;
};
static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
@@ -96,7 +139,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
u32 reg_val;
mutex_lock(&chip->lock);
- pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS;
+ pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
pm |= (1 << nr);
iowrite32(pm, &chip->reg->pm);
@@ -118,7 +161,7 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
u32 pm;
mutex_lock(&chip->lock);
- pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; /*bits 0-11*/
+ pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
pm &= ~(1 << nr);
iowrite32(pm, &chip->reg->pm);
mutex_unlock(&chip->lock);
@@ -131,8 +174,16 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
*/
static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
{
+ chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien);
+ chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask);
chip->pch_gpio_reg.po_reg = ioread32(&chip->reg->po);
chip->pch_gpio_reg.pm_reg = ioread32(&chip->reg->pm);
+ chip->pch_gpio_reg.im0_reg = ioread32(&chip->reg->im0);
+ if (chip->ioh == INTEL_EG20T_PCH)
+ chip->pch_gpio_reg.im1_reg = ioread32(&chip->reg->im1);
+ if (chip->ioh == OKISEMI_ML7223n_IOH)
+ chip->pch_gpio_reg.gpio_use_sel_reg =\
+ ioread32(&chip->reg->gpio_use_sel);
}
/*
@@ -140,10 +191,24 @@ static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
*/
static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
{
+ iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien);
+ iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask);
/* to store contents of PO register */
iowrite32(chip->pch_gpio_reg.po_reg, &chip->reg->po);
/* to store contents of PM register */
iowrite32(chip->pch_gpio_reg.pm_reg, &chip->reg->pm);
+ iowrite32(chip->pch_gpio_reg.im0_reg, &chip->reg->im0);
+ if (chip->ioh == INTEL_EG20T_PCH)
+ iowrite32(chip->pch_gpio_reg.im1_reg, &chip->reg->im1);
+ if (chip->ioh == OKISEMI_ML7223n_IOH)
+ iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg,
+ &chip->reg->gpio_use_sel);
+}
+
+static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+{
+ struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
+ return chip->irq_base + offset;
}
static void pch_gpio_setup(struct pch_gpio *chip)
@@ -158,8 +223,132 @@ static void pch_gpio_setup(struct pch_gpio *chip)
gpio->set = pch_gpio_set;
gpio->dbg_show = NULL;
gpio->base = -1;
- gpio->ngpio = GPIO_NUM_PINS;
+ gpio->ngpio = gpio_pins[chip->ioh];
gpio->can_sleep = 0;
+ gpio->to_irq = pch_gpio_to_irq;
+}
+
+static int pch_irq_type(struct irq_data *d, unsigned int type)
+{
+ u32 im;
+ u32 *im_reg;
+ u32 ien;
+ u32 im_pos;
+ int ch;
+ unsigned long flags;
+ u32 val;
+ int irq = d->irq;
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct pch_gpio *chip = gc->private;
+
+ ch = irq - chip->irq_base;
+ if (irq <= chip->irq_base + 7) {
+ im_reg = &chip->reg->im0;
+ im_pos = ch;
+ } else {
+ im_reg = &chip->reg->im1;
+ im_pos = ch - 8;
+ }
+ dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d\n",
+ __func__, irq, type, ch, im_pos);
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ val = PCH_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ val = PCH_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ val = PCH_EDGE_BOTH;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ val = PCH_LEVEL_H;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ val = PCH_LEVEL_L;
+ break;
+ case IRQ_TYPE_PROBE:
+ goto end;
+ default:
+ dev_warn(chip->dev, "%s: unknown type(%dd)",
+ __func__, type);
+ goto end;
+ }
+
+ /* Set interrupt mode */
+ im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4));
+ iowrite32(im | (val << (im_pos * 4)), im_reg);
+
+ /* iclr */
+ iowrite32(BIT(ch), &chip->reg->iclr);
+
+ /* IMASKCLR */
+ iowrite32(BIT(ch), &chip->reg->imaskclr);
+
+ /* Enable interrupt */
+ ien = ioread32(&chip->reg->ien);
+ iowrite32(ien | BIT(ch), &chip->reg->ien);
+end:
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+
+static void pch_irq_unmask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct pch_gpio *chip = gc->private;
+
+ iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imaskclr);
+}
+
+static void pch_irq_mask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct pch_gpio *chip = gc->private;
+
+ iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imask);
+}
+
+static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
+{
+ struct pch_gpio *chip = dev_id;
+ u32 reg_val = ioread32(&chip->reg->istatus);
+ int i;
+ int ret = IRQ_NONE;
+
+ for (i = 0; i < gpio_pins[chip->ioh]; i++) {
+ if (reg_val & BIT(i)) {
+ dev_dbg(chip->dev, "%s:[%d]:irq=%d status=0x%x\n",
+ __func__, i, irq, reg_val);
+ iowrite32(BIT(i), &chip->reg->iclr);
+ generic_handle_irq(chip->irq_base + i);
+ ret = IRQ_HANDLED;
+ }
+ }
+ return ret;
+}
+
+static __devinit void pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
+ unsigned int irq_start, unsigned int num)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("pch_gpio", 1, irq_start, chip->base,
+ handle_simple_irq);
+ gc->private = chip;
+ ct = gc->chip_types;
+
+ ct->chip.irq_mask = pch_irq_mask;
+ ct->chip.irq_unmask = pch_irq_unmask;
+ ct->chip.irq_set_type = pch_irq_type;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
+ IRQ_NOREQUEST | IRQ_NOPROBE, 0);
}
static int __devinit pch_gpio_probe(struct pci_dev *pdev,
@@ -167,6 +356,7 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev,
{
s32 ret;
struct pch_gpio *chip;
+ int irq_base;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
@@ -192,6 +382,13 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev,
goto err_iomap;
}
+ if (pdev->device == 0x8803)
+ chip->ioh = INTEL_EG20T_PCH;
+ else if (pdev->device == 0x8014)
+ chip->ioh = OKISEMI_ML7223m_IOH;
+ else if (pdev->device == 0x8043)
+ chip->ioh = OKISEMI_ML7223n_IOH;
+
chip->reg = chip->base;
pci_set_drvdata(pdev, chip);
mutex_init(&chip->lock);
@@ -202,8 +399,36 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev,
goto err_gpiochip_add;
}
+ irq_base = irq_alloc_descs(-1, 0, gpio_pins[chip->ioh], NUMA_NO_NODE);
+ if (irq_base < 0) {
+ dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n");
+ chip->irq_base = -1;
+ goto end;
+ }
+ chip->irq_base = irq_base;
+
+ ret = request_irq(pdev->irq, pch_gpio_handler,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (ret != 0) {
+ dev_err(&pdev->dev,
+ "%s request_irq failed\n", __func__);
+ goto err_request_irq;
+ }
+
+ pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]);
+
+ /* Initialize interrupt ien register */
+ iowrite32(0, &chip->reg->ien);
+end:
return 0;
+err_request_irq:
+ irq_free_descs(irq_base, gpio_pins[chip->ioh]);
+
+ ret = gpiochip_remove(&chip->gpio);
+ if (ret)
+ dev_err(&pdev->dev, "%s gpiochip_remove failed\n", __func__);
+
err_gpiochip_add:
pci_iounmap(pdev, chip->base);
@@ -224,6 +449,12 @@ static void __devexit pch_gpio_remove(struct pci_dev *pdev)
int err;
struct pch_gpio *chip = pci_get_drvdata(pdev);
+ if (chip->irq_base != -1) {
+ free_irq(pdev->irq, chip);
+
+ irq_free_descs(chip->irq_base, gpio_pins[chip->ioh]);
+ }
+
err = gpiochip_remove(&chip->gpio);
if (err)
dev_err(&pdev->dev, "Failed gpiochip_remove\n");
@@ -239,9 +470,11 @@ static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
{
s32 ret;
struct pch_gpio *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
+ spin_lock_irqsave(&chip->spinlock, flags);
pch_gpio_save_reg_conf(chip);
- pch_gpio_restore_reg_conf(chip);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
ret = pci_save_state(pdev);
if (ret) {
@@ -261,6 +494,7 @@ static int pch_gpio_resume(struct pci_dev *pdev)
{
s32 ret;
struct pch_gpio *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
ret = pci_enable_wake(pdev, PCI_D0, 0);
@@ -272,9 +506,11 @@ static int pch_gpio_resume(struct pci_dev *pdev)
}
pci_restore_state(pdev);
+ spin_lock_irqsave(&chip->spinlock, flags);
iowrite32(0x01, &chip->reg->reset);
iowrite32(0x00, &chip->reg->reset);
pch_gpio_restore_reg_conf(chip);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
}
@@ -287,6 +523,7 @@ static int pch_gpio_resume(struct pci_dev *pdev)
static DEFINE_PCI_DEVICE_TABLE(pch_gpio_pcidev_id) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) },
{ PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8043) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id);
diff --git a/drivers/gpio/pl061.c b/drivers/gpio/gpio-pl061.c
index 6fcb28cdd862..093c90bd3c1d 100644
--- a/drivers/gpio/pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -1,7 +1,5 @@
/*
- * linux/drivers/gpio/pl061.c
- *
- * Copyright (C) 2008, 2009 Provigent Ltd.
+ * Copyright (C) 2008, 2009 Provigent Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -120,7 +118,7 @@ static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
- if (chip->irq_base == (unsigned) -1)
+ if (chip->irq_base == NO_IRQ)
return -EINVAL;
return chip->irq_base + offset;
@@ -248,6 +246,18 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
if (chip == NULL)
return -ENOMEM;
+ pdata = dev->dev.platform_data;
+ if (pdata) {
+ chip->gc.base = pdata->gpio_base;
+ chip->irq_base = pdata->irq_base;
+ } else if (dev->dev.of_node) {
+ chip->gc.base = -1;
+ chip->irq_base = NO_IRQ;
+ } else {
+ ret = -ENODEV;
+ goto free_mem;
+ }
+
if (!request_mem_region(dev->res.start,
resource_size(&dev->res), "pl061")) {
ret = -EBUSY;
@@ -269,14 +279,11 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
chip->gc.get = pl061_get_value;
chip->gc.set = pl061_set_value;
chip->gc.to_irq = pl061_to_irq;
- chip->gc.base = pdata->gpio_base;
chip->gc.ngpio = PL061_GPIO_NR;
chip->gc.label = dev_name(&dev->dev);
chip->gc.dev = &dev->dev;
chip->gc.owner = THIS_MODULE;
- chip->irq_base = pdata->irq_base;
-
ret = gpiochip_add(&chip->gc);
if (ret)
goto iounmap;
@@ -285,7 +292,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
* irq_chip support
*/
- if (chip->irq_base == (unsigned) -1)
+ if (chip->irq_base == NO_IRQ)
return 0;
writeb(0, chip->base + GPIOIE); /* disable irqs */
@@ -309,11 +316,13 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
list_add(&chip->list, chip_list);
for (i = 0; i < PL061_GPIO_NR; i++) {
- if (pdata->directions & (1 << i))
- pl061_direction_output(&chip->gc, i,
- pdata->values & (1 << i));
- else
- pl061_direction_input(&chip->gc, i);
+ if (pdata) {
+ if (pdata->directions & (1 << i))
+ pl061_direction_output(&chip->gc, i,
+ pdata->values & (1 << i));
+ else
+ pl061_direction_input(&chip->gc, i);
+ }
irq_set_chip_and_handler(i + chip->irq_base, &pl061_irqchip,
handle_simple_irq);
diff --git a/drivers/gpio/gpio-plat-samsung.c b/drivers/gpio/gpio-plat-samsung.c
deleted file mode 100644
index ea37c0461788..000000000000
--- a/drivers/gpio/gpio-plat-samsung.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/* arch/arm/plat-samsung/gpiolib.c
- *
- * Copyright 2008 Openmoko, Inc.
- * Copyright 2008 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- * http://armlinux.simtec.co.uk/
- *
- * Copyright (c) 2009 Samsung Electronics Co., Ltd.
- * http://www.samsung.com/
- *
- * SAMSUNG - GPIOlib support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-#include <plat/gpio-core.h>
-#include <plat/gpio-cfg.h>
-#include <plat/gpio-cfg-helpers.h>
-
-#ifndef DEBUG_GPIO
-#define gpio_dbg(x...) do { } while (0)
-#else
-#define gpio_dbg(x...) printk(KERN_DEBUG x)
-#endif
-
-/* The samsung_gpiolib_4bit routines are to control the gpio banks where
- * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the
- * following example:
- *
- * base + 0x00: Control register, 4 bits per gpio
- * gpio n: 4 bits starting at (4*n)
- * 0000 = input, 0001 = output, others mean special-function
- * base + 0x04: Data register, 1 bit per gpio
- * bit n: data bit n
- *
- * Note, since the data register is one bit per gpio and is at base + 0x4
- * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of
- * the output.
-*/
-
-static int samsung_gpiolib_4bit_input(struct gpio_chip *chip,
- unsigned int offset)
-{
- struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
- void __iomem *base = ourchip->base;
- unsigned long con;
-
- con = __raw_readl(base + GPIOCON_OFF);
- con &= ~(0xf << con_4bit_shift(offset));
- __raw_writel(con, base + GPIOCON_OFF);
-
- gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con);
-
- return 0;
-}
-
-static int samsung_gpiolib_4bit_output(struct gpio_chip *chip,
- unsigned int offset, int value)
-{
- struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
- void __iomem *base = ourchip->base;
- unsigned long con;
- unsigned long dat;
-
- con = __raw_readl(base + GPIOCON_OFF);
- con &= ~(0xf << con_4bit_shift(offset));
- con |= 0x1 << con_4bit_shift(offset);
-
- dat = __raw_readl(base + GPIODAT_OFF);
-
- if (value)
- dat |= 1 << offset;
- else
- dat &= ~(1 << offset);
-
- __raw_writel(dat, base + GPIODAT_OFF);
- __raw_writel(con, base + GPIOCON_OFF);
- __raw_writel(dat, base + GPIODAT_OFF);
-
- gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
-
- return 0;
-}
-
-/* The next set of routines are for the case where the GPIO configuration
- * registers are 4 bits per GPIO but there is more than one register (the
- * bank has more than 8 GPIOs.
- *
- * This case is the similar to the 4 bit case, but the registers are as
- * follows:
- *
- * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs)
- * gpio n: 4 bits starting at (4*n)
- * 0000 = input, 0001 = output, others mean special-function
- * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs)
- * gpio n: 4 bits starting at (4*n)
- * 0000 = input, 0001 = output, others mean special-function
- * base + 0x08: Data register, 1 bit per gpio
- * bit n: data bit n
- *
- * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we
- * store the 'base + 0x4' address so that these routines see the data
- * register at ourchip->base + 0x04.
- */
-
-static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip,
- unsigned int offset)
-{
- struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
- void __iomem *base = ourchip->base;
- void __iomem *regcon = base;
- unsigned long con;
-
- if (offset > 7)
- offset -= 8;
- else
- regcon -= 4;
-
- con = __raw_readl(regcon);
- con &= ~(0xf << con_4bit_shift(offset));
- __raw_writel(con, regcon);
-
- gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con);
-
- return 0;
-}
-
-static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip,
- unsigned int offset, int value)
-{
- struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
- void __iomem *base = ourchip->base;
- void __iomem *regcon = base;
- unsigned long con;
- unsigned long dat;
- unsigned con_offset = offset;
-
- if (con_offset > 7)
- con_offset -= 8;
- else
- regcon -= 4;
-
- con = __raw_readl(regcon);
- con &= ~(0xf << con_4bit_shift(con_offset));
- con |= 0x1 << con_4bit_shift(con_offset);
-
- dat = __raw_readl(base + GPIODAT_OFF);
-
- if (value)
- dat |= 1 << offset;
- else
- dat &= ~(1 << offset);
-
- __raw_writel(dat, base + GPIODAT_OFF);
- __raw_writel(con, regcon);
- __raw_writel(dat, base + GPIODAT_OFF);
-
- gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
-
- return 0;
-}
-
-void __init samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip)
-{
- chip->chip.direction_input = samsung_gpiolib_4bit_input;
- chip->chip.direction_output = samsung_gpiolib_4bit_output;
- chip->pm = __gpio_pm(&s3c_gpio_pm_4bit);
-}
-
-void __init samsung_gpiolib_add_4bit2(struct s3c_gpio_chip *chip)
-{
- chip->chip.direction_input = samsung_gpiolib_4bit2_input;
- chip->chip.direction_output = samsung_gpiolib_4bit2_output;
- chip->pm = __gpio_pm(&s3c_gpio_pm_4bit);
-}
-
-void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip,
- int nr_chips)
-{
- for (; nr_chips > 0; nr_chips--, chip++) {
- samsung_gpiolib_add_4bit(chip);
- s3c_gpiolib_add(chip);
- }
-}
-
-void __init samsung_gpiolib_add_4bit2_chips(struct s3c_gpio_chip *chip,
- int nr_chips)
-{
- for (; nr_chips > 0; nr_chips--, chip++) {
- samsung_gpiolib_add_4bit2(chip);
- s3c_gpiolib_add(chip);
- }
-}
-
-void __init samsung_gpiolib_add_2bit_chips(struct s3c_gpio_chip *chip,
- int nr_chips)
-{
- for (; nr_chips > 0; nr_chips--, chip++)
- s3c_gpiolib_add(chip);
-}
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
new file mode 100644
index 000000000000..ee137712f9db
--- /dev/null
+++ b/drivers/gpio/gpio-pxa.c
@@ -0,0 +1,338 @@
+/*
+ * linux/arch/arm/plat-pxa/gpio.c
+ *
+ * Generic PXA GPIO handling
+ *
+ * Author: Nicolas Pitre
+ * Created: Jun 15, 2001
+ * Copyright: MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/syscore_ops.h>
+#include <linux/slab.h>
+
+#include <mach/gpio-pxa.h>
+
+int pxa_last_gpio;
+
+struct pxa_gpio_chip {
+ struct gpio_chip chip;
+ void __iomem *regbase;
+ char label[10];
+
+ unsigned long irq_mask;
+ unsigned long irq_edge_rise;
+ unsigned long irq_edge_fall;
+
+#ifdef CONFIG_PM
+ unsigned long saved_gplr;
+ unsigned long saved_gpdr;
+ unsigned long saved_grer;
+ unsigned long saved_gfer;
+#endif
+};
+
+static DEFINE_SPINLOCK(gpio_lock);
+static struct pxa_gpio_chip *pxa_gpio_chips;
+
+#define for_each_gpio_chip(i, c) \
+ for (i = 0, c = &pxa_gpio_chips[0]; i <= pxa_last_gpio; i += 32, c++)
+
+static inline void __iomem *gpio_chip_base(struct gpio_chip *c)
+{
+ return container_of(c, struct pxa_gpio_chip, chip)->regbase;
+}
+
+static inline struct pxa_gpio_chip *gpio_to_pxachip(unsigned gpio)
+{
+ return &pxa_gpio_chips[gpio_to_bank(gpio)];
+}
+
+static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ void __iomem *base = gpio_chip_base(chip);
+ uint32_t value, mask = 1 << offset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ value = __raw_readl(base + GPDR_OFFSET);
+ if (__gpio_is_inverted(chip->base + offset))
+ value |= mask;
+ else
+ value &= ~mask;
+ __raw_writel(value, base + GPDR_OFFSET);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return 0;
+}
+
+static int pxa_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ void __iomem *base = gpio_chip_base(chip);
+ uint32_t tmp, mask = 1 << offset;
+ unsigned long flags;
+
+ __raw_writel(mask, base + (value ? GPSR_OFFSET : GPCR_OFFSET));
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ tmp = __raw_readl(base + GPDR_OFFSET);
+ if (__gpio_is_inverted(chip->base + offset))
+ tmp &= ~mask;
+ else
+ tmp |= mask;
+ __raw_writel(tmp, base + GPDR_OFFSET);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return 0;
+}
+
+static int pxa_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return __raw_readl(gpio_chip_base(chip) + GPLR_OFFSET) & (1 << offset);
+}
+
+static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ __raw_writel(1 << offset, gpio_chip_base(chip) +
+ (value ? GPSR_OFFSET : GPCR_OFFSET));
+}
+
+static int __init pxa_init_gpio_chip(int gpio_end)
+{
+ int i, gpio, nbanks = gpio_to_bank(gpio_end) + 1;
+ struct pxa_gpio_chip *chips;
+
+ chips = kzalloc(nbanks * sizeof(struct pxa_gpio_chip), GFP_KERNEL);
+ if (chips == NULL) {
+ pr_err("%s: failed to allocate GPIO chips\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0, gpio = 0; i < nbanks; i++, gpio += 32) {
+ struct gpio_chip *c = &chips[i].chip;
+
+ sprintf(chips[i].label, "gpio-%d", i);
+ chips[i].regbase = GPIO_BANK(i);
+
+ c->base = gpio;
+ c->label = chips[i].label;
+
+ c->direction_input = pxa_gpio_direction_input;
+ c->direction_output = pxa_gpio_direction_output;
+ c->get = pxa_gpio_get;
+ c->set = pxa_gpio_set;
+
+ /* number of GPIOs on last bank may be less than 32 */
+ c->ngpio = (gpio + 31 > gpio_end) ? (gpio_end - gpio + 1) : 32;
+ gpiochip_add(c);
+ }
+ pxa_gpio_chips = chips;
+ return 0;
+}
+
+/* Update only those GRERx and GFERx edge detection register bits if those
+ * bits are set in c->irq_mask
+ */
+static inline void update_edge_detect(struct pxa_gpio_chip *c)
+{
+ uint32_t grer, gfer;
+
+ grer = __raw_readl(c->regbase + GRER_OFFSET) & ~c->irq_mask;
+ gfer = __raw_readl(c->regbase + GFER_OFFSET) & ~c->irq_mask;
+ grer |= c->irq_edge_rise & c->irq_mask;
+ gfer |= c->irq_edge_fall & c->irq_mask;
+ __raw_writel(grer, c->regbase + GRER_OFFSET);
+ __raw_writel(gfer, c->regbase + GFER_OFFSET);
+}
+
+static int pxa_gpio_irq_type(struct irq_data *d, unsigned int type)
+{
+ struct pxa_gpio_chip *c;
+ int gpio = irq_to_gpio(d->irq);
+ unsigned long gpdr, mask = GPIO_bit(gpio);
+
+ c = gpio_to_pxachip(gpio);
+
+ if (type == IRQ_TYPE_PROBE) {
+ /* Don't mess with enabled GPIOs using preconfigured edges or
+ * GPIOs set to alternate function or to output during probe
+ */
+ if ((c->irq_edge_rise | c->irq_edge_fall) & GPIO_bit(gpio))
+ return 0;
+
+ if (__gpio_is_occupied(gpio))
+ return 0;
+
+ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ }
+
+ gpdr = __raw_readl(c->regbase + GPDR_OFFSET);
+
+ if (__gpio_is_inverted(gpio))
+ __raw_writel(gpdr | mask, c->regbase + GPDR_OFFSET);
+ else
+ __raw_writel(gpdr & ~mask, c->regbase + GPDR_OFFSET);
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ c->irq_edge_rise |= mask;
+ else
+ c->irq_edge_rise &= ~mask;
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ c->irq_edge_fall |= mask;
+ else
+ c->irq_edge_fall &= ~mask;
+
+ update_edge_detect(c);
+
+ pr_debug("%s: IRQ%d (GPIO%d) - edge%s%s\n", __func__, d->irq, gpio,
+ ((type & IRQ_TYPE_EDGE_RISING) ? " rising" : ""),
+ ((type & IRQ_TYPE_EDGE_FALLING) ? " falling" : ""));
+ return 0;
+}
+
+static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct pxa_gpio_chip *c;
+ int loop, gpio, gpio_base, n;
+ unsigned long gedr;
+
+ do {
+ loop = 0;
+ for_each_gpio_chip(gpio, c) {
+ gpio_base = c->chip.base;
+
+ gedr = __raw_readl(c->regbase + GEDR_OFFSET);
+ gedr = gedr & c->irq_mask;
+ __raw_writel(gedr, c->regbase + GEDR_OFFSET);
+
+ n = find_first_bit(&gedr, BITS_PER_LONG);
+ while (n < BITS_PER_LONG) {
+ loop = 1;
+
+ generic_handle_irq(gpio_to_irq(gpio_base + n));
+ n = find_next_bit(&gedr, BITS_PER_LONG, n + 1);
+ }
+ }
+ } while (loop);
+}
+
+static void pxa_ack_muxed_gpio(struct irq_data *d)
+{
+ int gpio = irq_to_gpio(d->irq);
+ struct pxa_gpio_chip *c = gpio_to_pxachip(gpio);
+
+ __raw_writel(GPIO_bit(gpio), c->regbase + GEDR_OFFSET);
+}
+
+static void pxa_mask_muxed_gpio(struct irq_data *d)
+{
+ int gpio = irq_to_gpio(d->irq);
+ struct pxa_gpio_chip *c = gpio_to_pxachip(gpio);
+ uint32_t grer, gfer;
+
+ c->irq_mask &= ~GPIO_bit(gpio);
+
+ grer = __raw_readl(c->regbase + GRER_OFFSET) & ~GPIO_bit(gpio);
+ gfer = __raw_readl(c->regbase + GFER_OFFSET) & ~GPIO_bit(gpio);
+ __raw_writel(grer, c->regbase + GRER_OFFSET);
+ __raw_writel(gfer, c->regbase + GFER_OFFSET);
+}
+
+static void pxa_unmask_muxed_gpio(struct irq_data *d)
+{
+ int gpio = irq_to_gpio(d->irq);
+ struct pxa_gpio_chip *c = gpio_to_pxachip(gpio);
+
+ c->irq_mask |= GPIO_bit(gpio);
+ update_edge_detect(c);
+}
+
+static struct irq_chip pxa_muxed_gpio_chip = {
+ .name = "GPIO",
+ .irq_ack = pxa_ack_muxed_gpio,
+ .irq_mask = pxa_mask_muxed_gpio,
+ .irq_unmask = pxa_unmask_muxed_gpio,
+ .irq_set_type = pxa_gpio_irq_type,
+};
+
+void __init pxa_init_gpio(int mux_irq, int start, int end, set_wake_t fn)
+{
+ struct pxa_gpio_chip *c;
+ int gpio, irq;
+
+ pxa_last_gpio = end;
+
+ /* Initialize GPIO chips */
+ pxa_init_gpio_chip(end);
+
+ /* clear all GPIO edge detects */
+ for_each_gpio_chip(gpio, c) {
+ __raw_writel(0, c->regbase + GFER_OFFSET);
+ __raw_writel(0, c->regbase + GRER_OFFSET);
+ __raw_writel(~0,c->regbase + GEDR_OFFSET);
+ }
+
+ for (irq = gpio_to_irq(start); irq <= gpio_to_irq(end); irq++) {
+ irq_set_chip_and_handler(irq, &pxa_muxed_gpio_chip,
+ handle_edge_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+
+ /* Install handler for GPIO>=2 edge detect interrupts */
+ irq_set_chained_handler(mux_irq, pxa_gpio_demux_handler);
+ pxa_muxed_gpio_chip.irq_set_wake = fn;
+}
+
+#ifdef CONFIG_PM
+static int pxa_gpio_suspend(void)
+{
+ struct pxa_gpio_chip *c;
+ int gpio;
+
+ for_each_gpio_chip(gpio, c) {
+ c->saved_gplr = __raw_readl(c->regbase + GPLR_OFFSET);
+ c->saved_gpdr = __raw_readl(c->regbase + GPDR_OFFSET);
+ c->saved_grer = __raw_readl(c->regbase + GRER_OFFSET);
+ c->saved_gfer = __raw_readl(c->regbase + GFER_OFFSET);
+
+ /* Clear GPIO transition detect bits */
+ __raw_writel(0xffffffff, c->regbase + GEDR_OFFSET);
+ }
+ return 0;
+}
+
+static void pxa_gpio_resume(void)
+{
+ struct pxa_gpio_chip *c;
+ int gpio;
+
+ for_each_gpio_chip(gpio, c) {
+ /* restore level with set/clear */
+ __raw_writel( c->saved_gplr, c->regbase + GPSR_OFFSET);
+ __raw_writel(~c->saved_gplr, c->regbase + GPCR_OFFSET);
+
+ __raw_writel(c->saved_grer, c->regbase + GRER_OFFSET);
+ __raw_writel(c->saved_gfer, c->regbase + GFER_OFFSET);
+ __raw_writel(c->saved_gpdr, c->regbase + GPDR_OFFSET);
+ }
+}
+#else
+#define pxa_gpio_suspend NULL
+#define pxa_gpio_resume NULL
+#endif
+
+struct syscore_ops pxa_gpio_syscore_ops = {
+ .suspend = pxa_gpio_suspend,
+ .resume = pxa_gpio_resume,
+};
diff --git a/drivers/gpio/rdc321x-gpio.c b/drivers/gpio/gpio-rdc321x.c
index 2762698e0204..2762698e0204 100644
--- a/drivers/gpio/rdc321x-gpio.c
+++ b/drivers/gpio/gpio-rdc321x.c
diff --git a/drivers/gpio/gpio-s5pc100.c b/drivers/gpio/gpio-s5pc100.c
deleted file mode 100644
index 2842394b28b5..000000000000
--- a/drivers/gpio/gpio-s5pc100.c
+++ /dev/null
@@ -1,355 +0,0 @@
-/* linux/arch/arm/mach-s5pc100/gpiolib.c
- *
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Copyright 2009 Samsung Electronics Co
- * Kyungmin Park <kyungmin.park@samsung.com>
- *
- * S5PC100 - GPIOlib support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-
-#include <mach/map.h>
-#include <mach/regs-gpio.h>
-
-#include <plat/gpio-core.h>
-#include <plat/gpio-cfg.h>
-#include <plat/gpio-cfg-helpers.h>
-
-/* S5PC100 GPIO bank summary:
- *
- * Bank GPIOs Style INT Type
- * A0 8 4Bit GPIO_INT0
- * A1 5 4Bit GPIO_INT1
- * B 8 4Bit GPIO_INT2
- * C 5 4Bit GPIO_INT3
- * D 7 4Bit GPIO_INT4
- * E0 8 4Bit GPIO_INT5
- * E1 6 4Bit GPIO_INT6
- * F0 8 4Bit GPIO_INT7
- * F1 8 4Bit GPIO_INT8
- * F2 8 4Bit GPIO_INT9
- * F3 4 4Bit GPIO_INT10
- * G0 8 4Bit GPIO_INT11
- * G1 3 4Bit GPIO_INT12
- * G2 7 4Bit GPIO_INT13
- * G3 7 4Bit GPIO_INT14
- * H0 8 4Bit WKUP_INT
- * H1 8 4Bit WKUP_INT
- * H2 8 4Bit WKUP_INT
- * H3 8 4Bit WKUP_INT
- * I 8 4Bit GPIO_INT15
- * J0 8 4Bit GPIO_INT16
- * J1 5 4Bit GPIO_INT17
- * J2 8 4Bit GPIO_INT18
- * J3 8 4Bit GPIO_INT19
- * J4 4 4Bit GPIO_INT20
- * K0 8 4Bit None
- * K1 6 4Bit None
- * K2 8 4Bit None
- * K3 8 4Bit None
- * L0 8 4Bit None
- * L1 8 4Bit None
- * L2 8 4Bit None
- * L3 8 4Bit None
- */
-
-static struct s3c_gpio_cfg gpio_cfg = {
- .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
- .set_pull = s3c_gpio_setpull_updown,
- .get_pull = s3c_gpio_getpull_updown,
-};
-
-static struct s3c_gpio_cfg gpio_cfg_eint = {
- .cfg_eint = 0xf,
- .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
- .set_pull = s3c_gpio_setpull_updown,
- .get_pull = s3c_gpio_getpull_updown,
-};
-
-static struct s3c_gpio_cfg gpio_cfg_noint = {
- .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
- .set_pull = s3c_gpio_setpull_updown,
- .get_pull = s3c_gpio_getpull_updown,
-};
-
-/*
- * GPIO bank's base address given the index of the bank in the
- * list of all gpio banks.
- */
-#define S5PC100_BANK_BASE(bank_nr) (S5P_VA_GPIO + ((bank_nr) * 0x20))
-
-/*
- * Following are the gpio banks in S5PC100.
- *
- * The 'config' member when left to NULL, is initialized to the default
- * structure gpio_cfg in the init function below.
- *
- * The 'base' member is also initialized in the init function below.
- * Note: The initialization of 'base' member of s3c_gpio_chip structure
- * uses the above macro and depends on the banks being listed in order here.
- */
-static struct s3c_gpio_chip s5pc100_gpio_chips[] = {
- {
- .chip = {
- .base = S5PC100_GPA0(0),
- .ngpio = S5PC100_GPIO_A0_NR,
- .label = "GPA0",
- },
- }, {
- .chip = {
- .base = S5PC100_GPA1(0),
- .ngpio = S5PC100_GPIO_A1_NR,
- .label = "GPA1",
- },
- }, {
- .chip = {
- .base = S5PC100_GPB(0),
- .ngpio = S5PC100_GPIO_B_NR,
- .label = "GPB",
- },
- }, {
- .chip = {
- .base = S5PC100_GPC(0),
- .ngpio = S5PC100_GPIO_C_NR,
- .label = "GPC",
- },
- }, {
- .chip = {
- .base = S5PC100_GPD(0),
- .ngpio = S5PC100_GPIO_D_NR,
- .label = "GPD",
- },
- }, {
- .chip = {
- .base = S5PC100_GPE0(0),
- .ngpio = S5PC100_GPIO_E0_NR,
- .label = "GPE0",
- },
- }, {
- .chip = {
- .base = S5PC100_GPE1(0),
- .ngpio = S5PC100_GPIO_E1_NR,
- .label = "GPE1",
- },
- }, {
- .chip = {
- .base = S5PC100_GPF0(0),
- .ngpio = S5PC100_GPIO_F0_NR,
- .label = "GPF0",
- },
- }, {
- .chip = {
- .base = S5PC100_GPF1(0),
- .ngpio = S5PC100_GPIO_F1_NR,
- .label = "GPF1",
- },
- }, {
- .chip = {
- .base = S5PC100_GPF2(0),
- .ngpio = S5PC100_GPIO_F2_NR,
- .label = "GPF2",
- },
- }, {
- .chip = {
- .base = S5PC100_GPF3(0),
- .ngpio = S5PC100_GPIO_F3_NR,
- .label = "GPF3",
- },
- }, {
- .chip = {
- .base = S5PC100_GPG0(0),
- .ngpio = S5PC100_GPIO_G0_NR,
- .label = "GPG0",
- },
- }, {
- .chip = {
- .base = S5PC100_GPG1(0),
- .ngpio = S5PC100_GPIO_G1_NR,
- .label = "GPG1",
- },
- }, {
- .chip = {
- .base = S5PC100_GPG2(0),
- .ngpio = S5PC100_GPIO_G2_NR,
- .label = "GPG2",
- },
- }, {
- .chip = {
- .base = S5PC100_GPG3(0),
- .ngpio = S5PC100_GPIO_G3_NR,
- .label = "GPG3",
- },
- }, {
- .chip = {
- .base = S5PC100_GPI(0),
- .ngpio = S5PC100_GPIO_I_NR,
- .label = "GPI",
- },
- }, {
- .chip = {
- .base = S5PC100_GPJ0(0),
- .ngpio = S5PC100_GPIO_J0_NR,
- .label = "GPJ0",
- },
- }, {
- .chip = {
- .base = S5PC100_GPJ1(0),
- .ngpio = S5PC100_GPIO_J1_NR,
- .label = "GPJ1",
- },
- }, {
- .chip = {
- .base = S5PC100_GPJ2(0),
- .ngpio = S5PC100_GPIO_J2_NR,
- .label = "GPJ2",
- },
- }, {
- .chip = {
- .base = S5PC100_GPJ3(0),
- .ngpio = S5PC100_GPIO_J3_NR,
- .label = "GPJ3",
- },
- }, {
- .chip = {
- .base = S5PC100_GPJ4(0),
- .ngpio = S5PC100_GPIO_J4_NR,
- .label = "GPJ4",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PC100_GPK0(0),
- .ngpio = S5PC100_GPIO_K0_NR,
- .label = "GPK0",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PC100_GPK1(0),
- .ngpio = S5PC100_GPIO_K1_NR,
- .label = "GPK1",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PC100_GPK2(0),
- .ngpio = S5PC100_GPIO_K2_NR,
- .label = "GPK2",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PC100_GPK3(0),
- .ngpio = S5PC100_GPIO_K3_NR,
- .label = "GPK3",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PC100_GPL0(0),
- .ngpio = S5PC100_GPIO_L0_NR,
- .label = "GPL0",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PC100_GPL1(0),
- .ngpio = S5PC100_GPIO_L1_NR,
- .label = "GPL1",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PC100_GPL2(0),
- .ngpio = S5PC100_GPIO_L2_NR,
- .label = "GPL2",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PC100_GPL3(0),
- .ngpio = S5PC100_GPIO_L3_NR,
- .label = "GPL3",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PC100_GPL4(0),
- .ngpio = S5PC100_GPIO_L4_NR,
- .label = "GPL4",
- },
- }, {
- .base = (S5P_VA_GPIO + 0xC00),
- .config = &gpio_cfg_eint,
- .irq_base = IRQ_EINT(0),
- .chip = {
- .base = S5PC100_GPH0(0),
- .ngpio = S5PC100_GPIO_H0_NR,
- .label = "GPH0",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .base = (S5P_VA_GPIO + 0xC20),
- .config = &gpio_cfg_eint,
- .irq_base = IRQ_EINT(8),
- .chip = {
- .base = S5PC100_GPH1(0),
- .ngpio = S5PC100_GPIO_H1_NR,
- .label = "GPH1",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .base = (S5P_VA_GPIO + 0xC40),
- .config = &gpio_cfg_eint,
- .irq_base = IRQ_EINT(16),
- .chip = {
- .base = S5PC100_GPH2(0),
- .ngpio = S5PC100_GPIO_H2_NR,
- .label = "GPH2",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .base = (S5P_VA_GPIO + 0xC60),
- .config = &gpio_cfg_eint,
- .irq_base = IRQ_EINT(24),
- .chip = {
- .base = S5PC100_GPH3(0),
- .ngpio = S5PC100_GPIO_H3_NR,
- .label = "GPH3",
- .to_irq = samsung_gpiolib_to_irq,
- },
- },
-};
-
-static __init int s5pc100_gpiolib_init(void)
-{
- struct s3c_gpio_chip *chip = s5pc100_gpio_chips;
- int nr_chips = ARRAY_SIZE(s5pc100_gpio_chips);
- int gpioint_group = 0;
- int i;
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (chip->config == NULL) {
- chip->config = &gpio_cfg;
- chip->group = gpioint_group++;
- }
- if (chip->base == NULL)
- chip->base = S5PC100_BANK_BASE(i);
- }
-
- samsung_gpiolib_add_4bit_chips(s5pc100_gpio_chips, nr_chips);
- s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR);
-
- return 0;
-}
-core_initcall(s5pc100_gpiolib_init);
diff --git a/drivers/gpio/gpio-s5pv210.c b/drivers/gpio/gpio-s5pv210.c
deleted file mode 100644
index 1ba20a703e05..000000000000
--- a/drivers/gpio/gpio-s5pv210.c
+++ /dev/null
@@ -1,288 +0,0 @@
-/* linux/arch/arm/mach-s5pv210/gpiolib.c
- *
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- * http://www.samsung.com/
- *
- * S5PV210 - GPIOlib support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-#include <plat/gpio-core.h>
-#include <plat/gpio-cfg.h>
-#include <plat/gpio-cfg-helpers.h>
-#include <mach/map.h>
-
-static struct s3c_gpio_cfg gpio_cfg = {
- .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
- .set_pull = s3c_gpio_setpull_updown,
- .get_pull = s3c_gpio_getpull_updown,
-};
-
-static struct s3c_gpio_cfg gpio_cfg_noint = {
- .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
- .set_pull = s3c_gpio_setpull_updown,
- .get_pull = s3c_gpio_getpull_updown,
-};
-
-/* GPIO bank's base address given the index of the bank in the
- * list of all gpio banks.
- */
-#define S5PV210_BANK_BASE(bank_nr) (S5P_VA_GPIO + ((bank_nr) * 0x20))
-
-/*
- * Following are the gpio banks in v210.
- *
- * The 'config' member when left to NULL, is initialized to the default
- * structure gpio_cfg in the init function below.
- *
- * The 'base' member is also initialized in the init function below.
- * Note: The initialization of 'base' member of s3c_gpio_chip structure
- * uses the above macro and depends on the banks being listed in order here.
- */
-static struct s3c_gpio_chip s5pv210_gpio_4bit[] = {
- {
- .chip = {
- .base = S5PV210_GPA0(0),
- .ngpio = S5PV210_GPIO_A0_NR,
- .label = "GPA0",
- },
- }, {
- .chip = {
- .base = S5PV210_GPA1(0),
- .ngpio = S5PV210_GPIO_A1_NR,
- .label = "GPA1",
- },
- }, {
- .chip = {
- .base = S5PV210_GPB(0),
- .ngpio = S5PV210_GPIO_B_NR,
- .label = "GPB",
- },
- }, {
- .chip = {
- .base = S5PV210_GPC0(0),
- .ngpio = S5PV210_GPIO_C0_NR,
- .label = "GPC0",
- },
- }, {
- .chip = {
- .base = S5PV210_GPC1(0),
- .ngpio = S5PV210_GPIO_C1_NR,
- .label = "GPC1",
- },
- }, {
- .chip = {
- .base = S5PV210_GPD0(0),
- .ngpio = S5PV210_GPIO_D0_NR,
- .label = "GPD0",
- },
- }, {
- .chip = {
- .base = S5PV210_GPD1(0),
- .ngpio = S5PV210_GPIO_D1_NR,
- .label = "GPD1",
- },
- }, {
- .chip = {
- .base = S5PV210_GPE0(0),
- .ngpio = S5PV210_GPIO_E0_NR,
- .label = "GPE0",
- },
- }, {
- .chip = {
- .base = S5PV210_GPE1(0),
- .ngpio = S5PV210_GPIO_E1_NR,
- .label = "GPE1",
- },
- }, {
- .chip = {
- .base = S5PV210_GPF0(0),
- .ngpio = S5PV210_GPIO_F0_NR,
- .label = "GPF0",
- },
- }, {
- .chip = {
- .base = S5PV210_GPF1(0),
- .ngpio = S5PV210_GPIO_F1_NR,
- .label = "GPF1",
- },
- }, {
- .chip = {
- .base = S5PV210_GPF2(0),
- .ngpio = S5PV210_GPIO_F2_NR,
- .label = "GPF2",
- },
- }, {
- .chip = {
- .base = S5PV210_GPF3(0),
- .ngpio = S5PV210_GPIO_F3_NR,
- .label = "GPF3",
- },
- }, {
- .chip = {
- .base = S5PV210_GPG0(0),
- .ngpio = S5PV210_GPIO_G0_NR,
- .label = "GPG0",
- },
- }, {
- .chip = {
- .base = S5PV210_GPG1(0),
- .ngpio = S5PV210_GPIO_G1_NR,
- .label = "GPG1",
- },
- }, {
- .chip = {
- .base = S5PV210_GPG2(0),
- .ngpio = S5PV210_GPIO_G2_NR,
- .label = "GPG2",
- },
- }, {
- .chip = {
- .base = S5PV210_GPG3(0),
- .ngpio = S5PV210_GPIO_G3_NR,
- .label = "GPG3",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PV210_GPI(0),
- .ngpio = S5PV210_GPIO_I_NR,
- .label = "GPI",
- },
- }, {
- .chip = {
- .base = S5PV210_GPJ0(0),
- .ngpio = S5PV210_GPIO_J0_NR,
- .label = "GPJ0",
- },
- }, {
- .chip = {
- .base = S5PV210_GPJ1(0),
- .ngpio = S5PV210_GPIO_J1_NR,
- .label = "GPJ1",
- },
- }, {
- .chip = {
- .base = S5PV210_GPJ2(0),
- .ngpio = S5PV210_GPIO_J2_NR,
- .label = "GPJ2",
- },
- }, {
- .chip = {
- .base = S5PV210_GPJ3(0),
- .ngpio = S5PV210_GPIO_J3_NR,
- .label = "GPJ3",
- },
- }, {
- .chip = {
- .base = S5PV210_GPJ4(0),
- .ngpio = S5PV210_GPIO_J4_NR,
- .label = "GPJ4",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PV210_MP01(0),
- .ngpio = S5PV210_GPIO_MP01_NR,
- .label = "MP01",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PV210_MP02(0),
- .ngpio = S5PV210_GPIO_MP02_NR,
- .label = "MP02",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PV210_MP03(0),
- .ngpio = S5PV210_GPIO_MP03_NR,
- .label = "MP03",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PV210_MP04(0),
- .ngpio = S5PV210_GPIO_MP04_NR,
- .label = "MP04",
- },
- }, {
- .config = &gpio_cfg_noint,
- .chip = {
- .base = S5PV210_MP05(0),
- .ngpio = S5PV210_GPIO_MP05_NR,
- .label = "MP05",
- },
- }, {
- .base = (S5P_VA_GPIO + 0xC00),
- .config = &gpio_cfg_noint,
- .irq_base = IRQ_EINT(0),
- .chip = {
- .base = S5PV210_GPH0(0),
- .ngpio = S5PV210_GPIO_H0_NR,
- .label = "GPH0",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .base = (S5P_VA_GPIO + 0xC20),
- .config = &gpio_cfg_noint,
- .irq_base = IRQ_EINT(8),
- .chip = {
- .base = S5PV210_GPH1(0),
- .ngpio = S5PV210_GPIO_H1_NR,
- .label = "GPH1",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .base = (S5P_VA_GPIO + 0xC40),
- .config = &gpio_cfg_noint,
- .irq_base = IRQ_EINT(16),
- .chip = {
- .base = S5PV210_GPH2(0),
- .ngpio = S5PV210_GPIO_H2_NR,
- .label = "GPH2",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .base = (S5P_VA_GPIO + 0xC60),
- .config = &gpio_cfg_noint,
- .irq_base = IRQ_EINT(24),
- .chip = {
- .base = S5PV210_GPH3(0),
- .ngpio = S5PV210_GPIO_H3_NR,
- .label = "GPH3",
- .to_irq = samsung_gpiolib_to_irq,
- },
- },
-};
-
-static __init int s5pv210_gpiolib_init(void)
-{
- struct s3c_gpio_chip *chip = s5pv210_gpio_4bit;
- int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit);
- int gpioint_group = 0;
- int i = 0;
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (chip->config == NULL) {
- chip->config = &gpio_cfg;
- chip->group = gpioint_group++;
- }
- if (chip->base == NULL)
- chip->base = S5PV210_BANK_BASE(i);
- }
-
- samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);
- s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR);
-
- return 0;
-}
-core_initcall(s5pv210_gpiolib_init);
diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c
new file mode 100644
index 000000000000..b6c1f6d80649
--- /dev/null
+++ b/drivers/gpio/gpio-sa1100.c
@@ -0,0 +1,63 @@
+/*
+ * linux/arch/arm/mach-sa1100/gpio.c
+ *
+ * Generic SA-1100 GPIO handling
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <mach/hardware.h>
+
+static int sa1100_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return GPLR & GPIO_GPIO(offset);
+}
+
+static void sa1100_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ if (value)
+ GPSR = GPIO_GPIO(offset);
+ else
+ GPCR = GPIO_GPIO(offset);
+}
+
+static int sa1100_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ GPDR &= ~GPIO_GPIO(offset);
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int sa1100_direction_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ sa1100_gpio_set(chip, offset, value);
+ GPDR |= GPIO_GPIO(offset);
+ local_irq_restore(flags);
+ return 0;
+}
+
+static struct gpio_chip sa1100_gpio_chip = {
+ .label = "gpio",
+ .direction_input = sa1100_direction_input,
+ .direction_output = sa1100_direction_output,
+ .set = sa1100_gpio_set,
+ .get = sa1100_gpio_get,
+ .base = 0,
+ .ngpio = GPIO_MAX + 1,
+};
+
+void __init sa1100_init_gpio(void)
+{
+ gpiochip_add(&sa1100_gpio_chip);
+}
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
new file mode 100644
index 000000000000..866251852719
--- /dev/null
+++ b/drivers/gpio/gpio-samsung.c
@@ -0,0 +1,2712 @@
+/*
+ * Copyright (c) 2009-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * SAMSUNG - GPIOlib support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/sysdev.h>
+#include <linux/ioport.h>
+
+#include <asm/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+#include <mach/regs-gpio.h>
+
+#include <plat/cpu.h>
+#include <plat/gpio-core.h>
+#include <plat/gpio-cfg.h>
+#include <plat/gpio-cfg-helpers.h>
+#include <plat/gpio-fns.h>
+#include <plat/pm.h>
+
+#ifndef DEBUG_GPIO
+#define gpio_dbg(x...) do { } while (0)
+#else
+#define gpio_dbg(x...) printk(KERN_DEBUG x)
+#endif
+
+int samsung_gpio_setpull_updown(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull)
+{
+ void __iomem *reg = chip->base + 0x08;
+ int shift = off * 2;
+ u32 pup;
+
+ pup = __raw_readl(reg);
+ pup &= ~(3 << shift);
+ pup |= pull << shift;
+ __raw_writel(pup, reg);
+
+ return 0;
+}
+
+samsung_gpio_pull_t samsung_gpio_getpull_updown(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ void __iomem *reg = chip->base + 0x08;
+ int shift = off * 2;
+ u32 pup = __raw_readl(reg);
+
+ pup >>= shift;
+ pup &= 0x3;
+
+ return (__force samsung_gpio_pull_t)pup;
+}
+
+int s3c2443_gpio_setpull(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull)
+{
+ switch (pull) {
+ case S3C_GPIO_PULL_NONE:
+ pull = 0x01;
+ break;
+ case S3C_GPIO_PULL_UP:
+ pull = 0x00;
+ break;
+ case S3C_GPIO_PULL_DOWN:
+ pull = 0x02;
+ break;
+ }
+ return samsung_gpio_setpull_updown(chip, off, pull);
+}
+
+samsung_gpio_pull_t s3c2443_gpio_getpull(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ samsung_gpio_pull_t pull;
+
+ pull = samsung_gpio_getpull_updown(chip, off);
+
+ switch (pull) {
+ case 0x00:
+ pull = S3C_GPIO_PULL_UP;
+ break;
+ case 0x01:
+ case 0x03:
+ pull = S3C_GPIO_PULL_NONE;
+ break;
+ case 0x02:
+ pull = S3C_GPIO_PULL_DOWN;
+ break;
+ }
+
+ return pull;
+}
+
+static int s3c24xx_gpio_setpull_1(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull,
+ samsung_gpio_pull_t updown)
+{
+ void __iomem *reg = chip->base + 0x08;
+ u32 pup = __raw_readl(reg);
+
+ if (pull == updown)
+ pup &= ~(1 << off);
+ else if (pull == S3C_GPIO_PULL_NONE)
+ pup |= (1 << off);
+ else
+ return -EINVAL;
+
+ __raw_writel(pup, reg);
+ return 0;
+}
+
+static samsung_gpio_pull_t s3c24xx_gpio_getpull_1(struct samsung_gpio_chip *chip,
+ unsigned int off,
+ samsung_gpio_pull_t updown)
+{
+ void __iomem *reg = chip->base + 0x08;
+ u32 pup = __raw_readl(reg);
+
+ pup &= (1 << off);
+ return pup ? S3C_GPIO_PULL_NONE : updown;
+}
+
+samsung_gpio_pull_t s3c24xx_gpio_getpull_1up(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ return s3c24xx_gpio_getpull_1(chip, off, S3C_GPIO_PULL_UP);
+}
+
+int s3c24xx_gpio_setpull_1up(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull)
+{
+ return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_UP);
+}
+
+samsung_gpio_pull_t s3c24xx_gpio_getpull_1down(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ return s3c24xx_gpio_getpull_1(chip, off, S3C_GPIO_PULL_DOWN);
+}
+
+int s3c24xx_gpio_setpull_1down(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull)
+{
+ return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_DOWN);
+}
+
+static int exynos4_gpio_setpull(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull)
+{
+ if (pull == S3C_GPIO_PULL_UP)
+ pull = 3;
+
+ return samsung_gpio_setpull_updown(chip, off, pull);
+}
+
+static samsung_gpio_pull_t exynos4_gpio_getpull(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ samsung_gpio_pull_t pull;
+
+ pull = samsung_gpio_getpull_updown(chip, off);
+
+ if (pull == 3)
+ pull = S3C_GPIO_PULL_UP;
+
+ return pull;
+}
+
+/*
+ * samsung_gpio_setcfg_2bit - Samsung 2bit style GPIO configuration.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @cfg: The configuration value to set.
+ *
+ * This helper deal with the GPIO cases where the control register
+ * has two bits of configuration per gpio, which have the following
+ * functions:
+ * 00 = input
+ * 01 = output
+ * 1x = special function
+ */
+
+static int samsung_gpio_setcfg_2bit(struct samsung_gpio_chip *chip,
+ unsigned int off, unsigned int cfg)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = off * 2;
+ u32 con;
+
+ if (samsung_gpio_is_cfg_special(cfg)) {
+ cfg &= 0xf;
+ if (cfg > 3)
+ return -EINVAL;
+
+ cfg <<= shift;
+ }
+
+ con = __raw_readl(reg);
+ con &= ~(0x3 << shift);
+ con |= cfg;
+ __raw_writel(con, reg);
+
+ return 0;
+}
+
+/*
+ * samsung_gpio_getcfg_2bit - Samsung 2bit style GPIO configuration read.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ *
+ * The reverse of samsung_gpio_setcfg_2bit(). Will return a value whicg
+ * could be directly passed back to samsung_gpio_setcfg_2bit(), from the
+ * S3C_GPIO_SPECIAL() macro.
+ */
+
+static unsigned int samsung_gpio_getcfg_2bit(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ u32 con;
+
+ con = __raw_readl(chip->base);
+ con >>= off * 2;
+ con &= 3;
+
+ /* this conversion works for IN and OUT as well as special mode */
+ return S3C_GPIO_SPECIAL(con);
+}
+
+/*
+ * samsung_gpio_setcfg_4bit - Samsung 4bit single register GPIO config.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @cfg: The configuration value to set.
+ *
+ * This helper deal with the GPIO cases where the control register has 4 bits
+ * of control per GPIO, generally in the form of:
+ * 0000 = Input
+ * 0001 = Output
+ * others = Special functions (dependent on bank)
+ *
+ * Note, since the code to deal with the case where there are two control
+ * registers instead of one, we do not have a separate set of functions for
+ * each case.
+ */
+
+static int samsung_gpio_setcfg_4bit(struct samsung_gpio_chip *chip,
+ unsigned int off, unsigned int cfg)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = (off & 7) * 4;
+ u32 con;
+
+ if (off < 8 && chip->chip.ngpio > 8)
+ reg -= 4;
+
+ if (samsung_gpio_is_cfg_special(cfg)) {
+ cfg &= 0xf;
+ cfg <<= shift;
+ }
+
+ con = __raw_readl(reg);
+ con &= ~(0xf << shift);
+ con |= cfg;
+ __raw_writel(con, reg);
+
+ return 0;
+}
+
+/*
+ * samsung_gpio_getcfg_4bit - Samsung 4bit single register GPIO config read.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ *
+ * The reverse of samsung_gpio_setcfg_4bit(), turning a gpio configuration
+ * register setting into a value the software can use, such as could be passed
+ * to samsung_gpio_setcfg_4bit().
+ *
+ * @sa samsung_gpio_getcfg_2bit
+ */
+
+static unsigned samsung_gpio_getcfg_4bit(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = (off & 7) * 4;
+ u32 con;
+
+ if (off < 8 && chip->chip.ngpio > 8)
+ reg -= 4;
+
+ con = __raw_readl(reg);
+ con >>= shift;
+ con &= 0xf;
+
+ /* this conversion works for IN and OUT as well as special mode */
+ return S3C_GPIO_SPECIAL(con);
+}
+
+#ifdef CONFIG_PLAT_S3C24XX
+/*
+ * s3c24xx_gpio_setcfg_abank - S3C24XX style GPIO configuration (Bank A)
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @cfg: The configuration value to set.
+ *
+ * This helper deal with the GPIO cases where the control register
+ * has one bit of configuration for the gpio, where setting the bit
+ * means the pin is in special function mode and unset means output.
+ */
+
+static int s3c24xx_gpio_setcfg_abank(struct samsung_gpio_chip *chip,
+ unsigned int off, unsigned int cfg)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = off;
+ u32 con;
+
+ if (samsung_gpio_is_cfg_special(cfg)) {
+ cfg &= 0xf;
+
+ /* Map output to 0, and SFN2 to 1 */
+ cfg -= 1;
+ if (cfg > 1)
+ return -EINVAL;
+
+ cfg <<= shift;
+ }
+
+ con = __raw_readl(reg);
+ con &= ~(0x1 << shift);
+ con |= cfg;
+ __raw_writel(con, reg);
+
+ return 0;
+}
+
+/*
+ * s3c24xx_gpio_getcfg_abank - S3C24XX style GPIO configuration read (Bank A)
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ *
+ * The reverse of s3c24xx_gpio_setcfg_abank() turning an GPIO into a usable
+ * GPIO configuration value.
+ *
+ * @sa samsung_gpio_getcfg_2bit
+ * @sa samsung_gpio_getcfg_4bit
+ */
+
+static unsigned s3c24xx_gpio_getcfg_abank(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ u32 con;
+
+ con = __raw_readl(chip->base);
+ con >>= off;
+ con &= 1;
+ con++;
+
+ return S3C_GPIO_SFN(con);
+}
+#endif
+
+#if defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450)
+static int s5p64x0_gpio_setcfg_rbank(struct samsung_gpio_chip *chip,
+ unsigned int off, unsigned int cfg)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift;
+ u32 con;
+
+ switch (off) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ shift = (off & 7) * 4;
+ reg -= 4;
+ break;
+ case 6:
+ shift = ((off + 1) & 7) * 4;
+ reg -= 4;
+ default:
+ shift = ((off + 1) & 7) * 4;
+ break;
+ }
+
+ if (samsung_gpio_is_cfg_special(cfg)) {
+ cfg &= 0xf;
+ cfg <<= shift;
+ }
+
+ con = __raw_readl(reg);
+ con &= ~(0xf << shift);
+ con |= cfg;
+ __raw_writel(con, reg);
+
+ return 0;
+}
+#endif
+
+static void __init samsung_gpiolib_set_cfg(struct samsung_gpio_cfg *chipcfg,
+ int nr_chips)
+{
+ for (; nr_chips > 0; nr_chips--, chipcfg++) {
+ if (!chipcfg->set_config)
+ chipcfg->set_config = samsung_gpio_setcfg_4bit;
+ if (!chipcfg->get_config)
+ chipcfg->get_config = samsung_gpio_getcfg_4bit;
+ if (!chipcfg->set_pull)
+ chipcfg->set_pull = samsung_gpio_setpull_updown;
+ if (!chipcfg->get_pull)
+ chipcfg->get_pull = samsung_gpio_getpull_updown;
+ }
+}
+
+struct samsung_gpio_cfg s3c24xx_gpiocfg_default = {
+ .set_config = samsung_gpio_setcfg_2bit,
+ .get_config = samsung_gpio_getcfg_2bit,
+};
+
+#ifdef CONFIG_PLAT_S3C24XX
+static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = {
+ .set_config = s3c24xx_gpio_setcfg_abank,
+ .get_config = s3c24xx_gpio_getcfg_abank,
+};
+#endif
+
+static struct samsung_gpio_cfg exynos4_gpio_cfg = {
+ .set_pull = exynos4_gpio_setpull,
+ .get_pull = exynos4_gpio_getpull,
+ .set_config = samsung_gpio_setcfg_4bit,
+ .get_config = samsung_gpio_getcfg_4bit,
+};
+
+#if defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450)
+static struct samsung_gpio_cfg s5p64x0_gpio_cfg_rbank = {
+ .cfg_eint = 0x3,
+ .set_config = s5p64x0_gpio_setcfg_rbank,
+ .get_config = samsung_gpio_getcfg_4bit,
+ .set_pull = samsung_gpio_setpull_updown,
+ .get_pull = samsung_gpio_getpull_updown,
+};
+#endif
+
+static struct samsung_gpio_cfg samsung_gpio_cfgs[] = {
+ {
+ .cfg_eint = 0x0,
+ }, {
+ .cfg_eint = 0x3,
+ }, {
+ .cfg_eint = 0x7,
+ }, {
+ .cfg_eint = 0xF,
+ }, {
+ .cfg_eint = 0x0,
+ .set_config = samsung_gpio_setcfg_2bit,
+ .get_config = samsung_gpio_getcfg_2bit,
+ }, {
+ .cfg_eint = 0x2,
+ .set_config = samsung_gpio_setcfg_2bit,
+ .get_config = samsung_gpio_getcfg_2bit,
+ }, {
+ .cfg_eint = 0x3,
+ .set_config = samsung_gpio_setcfg_2bit,
+ .get_config = samsung_gpio_getcfg_2bit,
+ }, {
+ .set_config = samsung_gpio_setcfg_2bit,
+ .get_config = samsung_gpio_getcfg_2bit,
+ }, {
+ .set_pull = exynos4_gpio_setpull,
+ .get_pull = exynos4_gpio_getpull,
+ }, {
+ .cfg_eint = 0x3,
+ .set_pull = exynos4_gpio_setpull,
+ .get_pull = exynos4_gpio_getpull,
+ }
+};
+
+/*
+ * Default routines for controlling GPIO, based on the original S3C24XX
+ * GPIO functions which deal with the case where each gpio bank of the
+ * chip is as following:
+ *
+ * base + 0x00: Control register, 2 bits per gpio
+ * gpio n: 2 bits starting at (2*n)
+ * 00 = input, 01 = output, others mean special-function
+ * base + 0x04: Data register, 1 bit per gpio
+ * bit n: data bit n
+*/
+
+static int samsung_gpiolib_2bit_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long con;
+
+ samsung_gpio_lock(ourchip, flags);
+
+ con = __raw_readl(base + 0x00);
+ con &= ~(3 << (offset * 2));
+
+ __raw_writel(con, base + 0x00);
+
+ samsung_gpio_unlock(ourchip, flags);
+ return 0;
+}
+
+static int samsung_gpiolib_2bit_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long dat;
+ unsigned long con;
+
+ samsung_gpio_lock(ourchip, flags);
+
+ dat = __raw_readl(base + 0x04);
+ dat &= ~(1 << offset);
+ if (value)
+ dat |= 1 << offset;
+ __raw_writel(dat, base + 0x04);
+
+ con = __raw_readl(base + 0x00);
+ con &= ~(3 << (offset * 2));
+ con |= 1 << (offset * 2);
+
+ __raw_writel(con, base + 0x00);
+ __raw_writel(dat, base + 0x04);
+
+ samsung_gpio_unlock(ourchip, flags);
+ return 0;
+}
+
+/*
+ * The samsung_gpiolib_4bit routines are to control the gpio banks where
+ * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the
+ * following example:
+ *
+ * base + 0x00: Control register, 4 bits per gpio
+ * gpio n: 4 bits starting at (4*n)
+ * 0000 = input, 0001 = output, others mean special-function
+ * base + 0x04: Data register, 1 bit per gpio
+ * bit n: data bit n
+ *
+ * Note, since the data register is one bit per gpio and is at base + 0x4
+ * we can use samsung_gpiolib_get and samsung_gpiolib_set to change the
+ * state of the output.
+ */
+
+static int samsung_gpiolib_4bit_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long con;
+
+ con = __raw_readl(base + GPIOCON_OFF);
+ con &= ~(0xf << con_4bit_shift(offset));
+ __raw_writel(con, base + GPIOCON_OFF);
+
+ gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con);
+
+ return 0;
+}
+
+static int samsung_gpiolib_4bit_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long con;
+ unsigned long dat;
+
+ con = __raw_readl(base + GPIOCON_OFF);
+ con &= ~(0xf << con_4bit_shift(offset));
+ con |= 0x1 << con_4bit_shift(offset);
+
+ dat = __raw_readl(base + GPIODAT_OFF);
+
+ if (value)
+ dat |= 1 << offset;
+ else
+ dat &= ~(1 << offset);
+
+ __raw_writel(dat, base + GPIODAT_OFF);
+ __raw_writel(con, base + GPIOCON_OFF);
+ __raw_writel(dat, base + GPIODAT_OFF);
+
+ gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
+
+ return 0;
+}
+
+/*
+ * The next set of routines are for the case where the GPIO configuration
+ * registers are 4 bits per GPIO but there is more than one register (the
+ * bank has more than 8 GPIOs.
+ *
+ * This case is the similar to the 4 bit case, but the registers are as
+ * follows:
+ *
+ * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs)
+ * gpio n: 4 bits starting at (4*n)
+ * 0000 = input, 0001 = output, others mean special-function
+ * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs)
+ * gpio n: 4 bits starting at (4*n)
+ * 0000 = input, 0001 = output, others mean special-function
+ * base + 0x08: Data register, 1 bit per gpio
+ * bit n: data bit n
+ *
+ * To allow us to use the samsung_gpiolib_get and samsung_gpiolib_set
+ * routines we store the 'base + 0x4' address so that these routines see
+ * the data register at ourchip->base + 0x04.
+ */
+
+static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ void __iomem *regcon = base;
+ unsigned long con;
+
+ if (offset > 7)
+ offset -= 8;
+ else
+ regcon -= 4;
+
+ con = __raw_readl(regcon);
+ con &= ~(0xf << con_4bit_shift(offset));
+ __raw_writel(con, regcon);
+
+ gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con);
+
+ return 0;
+}
+
+static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ void __iomem *regcon = base;
+ unsigned long con;
+ unsigned long dat;
+ unsigned con_offset = offset;
+
+ if (con_offset > 7)
+ con_offset -= 8;
+ else
+ regcon -= 4;
+
+ con = __raw_readl(regcon);
+ con &= ~(0xf << con_4bit_shift(con_offset));
+ con |= 0x1 << con_4bit_shift(con_offset);
+
+ dat = __raw_readl(base + GPIODAT_OFF);
+
+ if (value)
+ dat |= 1 << offset;
+ else
+ dat &= ~(1 << offset);
+
+ __raw_writel(dat, base + GPIODAT_OFF);
+ __raw_writel(con, regcon);
+ __raw_writel(dat, base + GPIODAT_OFF);
+
+ gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
+
+ return 0;
+}
+
+#ifdef CONFIG_PLAT_S3C24XX
+/* The next set of routines are for the case of s3c24xx bank a */
+
+static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset)
+{
+ return -EINVAL;
+}
+
+static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long dat;
+ unsigned long con;
+
+ local_irq_save(flags);
+
+ con = __raw_readl(base + 0x00);
+ dat = __raw_readl(base + 0x04);
+
+ dat &= ~(1 << offset);
+ if (value)
+ dat |= 1 << offset;
+
+ __raw_writel(dat, base + 0x04);
+
+ con &= ~(1 << offset);
+
+ __raw_writel(con, base + 0x00);
+ __raw_writel(dat, base + 0x04);
+
+ local_irq_restore(flags);
+ return 0;
+}
+#endif
+
+/* The next set of routines are for the case of s5p64x0 bank r */
+
+static int s5p64x0_gpiolib_rbank_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ void __iomem *regcon = base;
+ unsigned long con;
+ unsigned long flags;
+
+ switch (offset) {
+ case 6:
+ offset += 1;
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ regcon -= 4;
+ break;
+ default:
+ offset -= 7;
+ break;
+ }
+
+ samsung_gpio_lock(ourchip, flags);
+
+ con = __raw_readl(regcon);
+ con &= ~(0xf << con_4bit_shift(offset));
+ __raw_writel(con, regcon);
+
+ samsung_gpio_unlock(ourchip, flags);
+
+ return 0;
+}
+
+static int s5p64x0_gpiolib_rbank_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ void __iomem *regcon = base;
+ unsigned long con;
+ unsigned long dat;
+ unsigned long flags;
+ unsigned con_offset = offset;
+
+ switch (con_offset) {
+ case 6:
+ con_offset += 1;
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ regcon -= 4;
+ break;
+ default:
+ con_offset -= 7;
+ break;
+ }
+
+ samsung_gpio_lock(ourchip, flags);
+
+ con = __raw_readl(regcon);
+ con &= ~(0xf << con_4bit_shift(con_offset));
+ con |= 0x1 << con_4bit_shift(con_offset);
+
+ dat = __raw_readl(base + GPIODAT_OFF);
+ if (value)
+ dat |= 1 << offset;
+ else
+ dat &= ~(1 << offset);
+
+ __raw_writel(con, regcon);
+ __raw_writel(dat, base + GPIODAT_OFF);
+
+ samsung_gpio_unlock(ourchip, flags);
+
+ return 0;
+}
+
+static void samsung_gpiolib_set(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long dat;
+
+ samsung_gpio_lock(ourchip, flags);
+
+ dat = __raw_readl(base + 0x04);
+ dat &= ~(1 << offset);
+ if (value)
+ dat |= 1 << offset;
+ __raw_writel(dat, base + 0x04);
+
+ samsung_gpio_unlock(ourchip, flags);
+}
+
+static int samsung_gpiolib_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ unsigned long val;
+
+ val = __raw_readl(ourchip->base + 0x04);
+ val >>= offset;
+ val &= 1;
+
+ return val;
+}
+
+/*
+ * CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios
+ * for use with the configuration calls, and other parts of the s3c gpiolib
+ * support code.
+ *
+ * Not all s3c support code will need this, as some configurations of cpu
+ * may only support one or two different configuration options and have an
+ * easy gpio to samsung_gpio_chip mapping function. If this is the case, then
+ * the machine support file should provide its own samsung_gpiolib_getchip()
+ * and any other necessary functions.
+ */
+
+#ifdef CONFIG_S3C_GPIO_TRACK
+struct samsung_gpio_chip *s3c_gpios[S3C_GPIO_END];
+
+static __init void s3c_gpiolib_track(struct samsung_gpio_chip *chip)
+{
+ unsigned int gpn;
+ int i;
+
+ gpn = chip->chip.base;
+ for (i = 0; i < chip->chip.ngpio; i++, gpn++) {
+ BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios));
+ s3c_gpios[gpn] = chip;
+ }
+}
+#endif /* CONFIG_S3C_GPIO_TRACK */
+
+/*
+ * samsung_gpiolib_add() - add the Samsung gpio_chip.
+ * @chip: The chip to register
+ *
+ * This is a wrapper to gpiochip_add() that takes our specific gpio chip
+ * information and makes the necessary alterations for the platform and
+ * notes the information for use with the configuration systems and any
+ * other parts of the system.
+ */
+
+static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip)
+{
+ struct gpio_chip *gc = &chip->chip;
+ int ret;
+
+ BUG_ON(!chip->base);
+ BUG_ON(!gc->label);
+ BUG_ON(!gc->ngpio);
+
+ spin_lock_init(&chip->lock);
+
+ if (!gc->direction_input)
+ gc->direction_input = samsung_gpiolib_2bit_input;
+ if (!gc->direction_output)
+ gc->direction_output = samsung_gpiolib_2bit_output;
+ if (!gc->set)
+ gc->set = samsung_gpiolib_set;
+ if (!gc->get)
+ gc->get = samsung_gpiolib_get;
+
+#ifdef CONFIG_PM
+ if (chip->pm != NULL) {
+ if (!chip->pm->save || !chip->pm->resume)
+ printk(KERN_ERR "gpio: %s has missing PM functions\n",
+ gc->label);
+ } else
+ printk(KERN_ERR "gpio: %s has no PM function\n", gc->label);
+#endif
+
+ /* gpiochip_add() prints own failure message on error. */
+ ret = gpiochip_add(gc);
+ if (ret >= 0)
+ s3c_gpiolib_track(chip);
+}
+
+static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip,
+ int nr_chips, void __iomem *base)
+{
+ int i;
+ struct gpio_chip *gc = &chip->chip;
+
+ for (i = 0 ; i < nr_chips; i++, chip++) {
+ /* skip banks not present on SoC */
+ if (chip->chip.base >= S3C_GPIO_END)
+ continue;
+
+ if (!chip->config)
+ chip->config = &s3c24xx_gpiocfg_default;
+ if (!chip->pm)
+ chip->pm = __gpio_pm(&samsung_gpio_pm_2bit);
+ if ((base != NULL) && (chip->base == NULL))
+ chip->base = base + ((i) * 0x10);
+
+ if (!gc->direction_input)
+ gc->direction_input = samsung_gpiolib_2bit_input;
+ if (!gc->direction_output)
+ gc->direction_output = samsung_gpiolib_2bit_output;
+
+ samsung_gpiolib_add(chip);
+ }
+}
+
+static void __init samsung_gpiolib_add_2bit_chips(struct samsung_gpio_chip *chip,
+ int nr_chips, void __iomem *base,
+ unsigned int offset)
+{
+ int i;
+
+ for (i = 0 ; i < nr_chips; i++, chip++) {
+ chip->chip.direction_input = samsung_gpiolib_2bit_input;
+ chip->chip.direction_output = samsung_gpiolib_2bit_output;
+
+ if (!chip->config)
+ chip->config = &samsung_gpio_cfgs[7];
+ if (!chip->pm)
+ chip->pm = __gpio_pm(&samsung_gpio_pm_2bit);
+ if ((base != NULL) && (chip->base == NULL))
+ chip->base = base + ((i) * offset);
+
+ samsung_gpiolib_add(chip);
+ }
+}
+
+/*
+ * samsung_gpiolib_add_4bit_chips - 4bit single register GPIO config.
+ * @chip: The gpio chip that is being configured.
+ * @nr_chips: The no of chips (gpio ports) for the GPIO being configured.
+ *
+ * This helper deal with the GPIO cases where the control register has 4 bits
+ * of control per GPIO, generally in the form of:
+ * 0000 = Input
+ * 0001 = Output
+ * others = Special functions (dependent on bank)
+ *
+ * Note, since the code to deal with the case where there are two control
+ * registers instead of one, we do not have a separate set of function
+ * (samsung_gpiolib_add_4bit2_chips)for each case.
+ */
+
+static void __init samsung_gpiolib_add_4bit_chips(struct samsung_gpio_chip *chip,
+ int nr_chips, void __iomem *base)
+{
+ int i;
+
+ for (i = 0 ; i < nr_chips; i++, chip++) {
+ chip->chip.direction_input = samsung_gpiolib_4bit_input;
+ chip->chip.direction_output = samsung_gpiolib_4bit_output;
+
+ if (!chip->config)
+ chip->config = &samsung_gpio_cfgs[2];
+ if (!chip->pm)
+ chip->pm = __gpio_pm(&samsung_gpio_pm_4bit);
+ if ((base != NULL) && (chip->base == NULL))
+ chip->base = base + ((i) * 0x20);
+
+ samsung_gpiolib_add(chip);
+ }
+}
+
+static void __init samsung_gpiolib_add_4bit2_chips(struct samsung_gpio_chip *chip,
+ int nr_chips)
+{
+ for (; nr_chips > 0; nr_chips--, chip++) {
+ chip->chip.direction_input = samsung_gpiolib_4bit2_input;
+ chip->chip.direction_output = samsung_gpiolib_4bit2_output;
+
+ if (!chip->config)
+ chip->config = &samsung_gpio_cfgs[2];
+ if (!chip->pm)
+ chip->pm = __gpio_pm(&samsung_gpio_pm_4bit);
+
+ samsung_gpiolib_add(chip);
+ }
+}
+
+static void __init s5p64x0_gpiolib_add_rbank(struct samsung_gpio_chip *chip,
+ int nr_chips)
+{
+ for (; nr_chips > 0; nr_chips--, chip++) {
+ chip->chip.direction_input = s5p64x0_gpiolib_rbank_input;
+ chip->chip.direction_output = s5p64x0_gpiolib_rbank_output;
+
+ if (!chip->pm)
+ chip->pm = __gpio_pm(&samsung_gpio_pm_4bit);
+
+ samsung_gpiolib_add(chip);
+ }
+}
+
+int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct samsung_gpio_chip *samsung_chip = container_of(chip, struct samsung_gpio_chip, chip);
+
+ return samsung_chip->irq_base + offset;
+}
+
+#ifdef CONFIG_PLAT_S3C24XX
+static int s3c24xx_gpiolib_fbank_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ if (offset < 4)
+ return IRQ_EINT0 + offset;
+
+ if (offset < 8)
+ return IRQ_EINT4 + offset - 4;
+
+ return -EINVAL;
+}
+#endif
+
+#ifdef CONFIG_PLAT_S3C64XX
+static int s3c64xx_gpiolib_mbank_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+ return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO;
+}
+
+static int s3c64xx_gpiolib_lbank_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+ return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO;
+}
+#endif
+
+struct samsung_gpio_chip s3c24xx_gpios[] = {
+#ifdef CONFIG_PLAT_S3C24XX
+ {
+ .config = &s3c24xx_gpiocfg_banka,
+ .chip = {
+ .base = S3C2410_GPA(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOA",
+ .ngpio = 24,
+ .direction_input = s3c24xx_gpiolib_banka_input,
+ .direction_output = s3c24xx_gpiolib_banka_output,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPB(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOB",
+ .ngpio = 16,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPC(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOC",
+ .ngpio = 16,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPD(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOD",
+ .ngpio = 16,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPE(0),
+ .label = "GPIOE",
+ .owner = THIS_MODULE,
+ .ngpio = 16,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPF(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOF",
+ .ngpio = 8,
+ .to_irq = s3c24xx_gpiolib_fbank_to_irq,
+ },
+ }, {
+ .irq_base = IRQ_EINT8,
+ .chip = {
+ .base = S3C2410_GPG(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOG",
+ .ngpio = 16,
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPH(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOH",
+ .ngpio = 11,
+ },
+ },
+ /* GPIOS for the S3C2443 and later devices. */
+ {
+ .base = S3C2440_GPJCON,
+ .chip = {
+ .base = S3C2410_GPJ(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOJ",
+ .ngpio = 16,
+ },
+ }, {
+ .base = S3C2443_GPKCON,
+ .chip = {
+ .base = S3C2410_GPK(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOK",
+ .ngpio = 16,
+ },
+ }, {
+ .base = S3C2443_GPLCON,
+ .chip = {
+ .base = S3C2410_GPL(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOL",
+ .ngpio = 15,
+ },
+ }, {
+ .base = S3C2443_GPMCON,
+ .chip = {
+ .base = S3C2410_GPM(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOM",
+ .ngpio = 2,
+ },
+ },
+#endif
+};
+
+/*
+ * GPIO bank summary:
+ *
+ * Bank GPIOs Style SlpCon ExtInt Group
+ * A 8 4Bit Yes 1
+ * B 7 4Bit Yes 1
+ * C 8 4Bit Yes 2
+ * D 5 4Bit Yes 3
+ * E 5 4Bit Yes None
+ * F 16 2Bit Yes 4 [1]
+ * G 7 4Bit Yes 5
+ * H 10 4Bit[2] Yes 6
+ * I 16 2Bit Yes None
+ * J 12 2Bit Yes None
+ * K 16 4Bit[2] No None
+ * L 15 4Bit[2] No None
+ * M 6 4Bit No IRQ_EINT
+ * N 16 2Bit No IRQ_EINT
+ * O 16 2Bit Yes 7
+ * P 15 2Bit Yes 8
+ * Q 9 2Bit Yes 9
+ *
+ * [1] BANKF pins 14,15 do not form part of the external interrupt sources
+ * [2] BANK has two control registers, GPxCON0 and GPxCON1
+ */
+
+static struct samsung_gpio_chip s3c64xx_gpios_4bit[] = {
+#ifdef CONFIG_PLAT_S3C64XX
+ {
+ .chip = {
+ .base = S3C64XX_GPA(0),
+ .ngpio = S3C64XX_GPIO_A_NR,
+ .label = "GPA",
+ },
+ }, {
+ .chip = {
+ .base = S3C64XX_GPB(0),
+ .ngpio = S3C64XX_GPIO_B_NR,
+ .label = "GPB",
+ },
+ }, {
+ .chip = {
+ .base = S3C64XX_GPC(0),
+ .ngpio = S3C64XX_GPIO_C_NR,
+ .label = "GPC",
+ },
+ }, {
+ .chip = {
+ .base = S3C64XX_GPD(0),
+ .ngpio = S3C64XX_GPIO_D_NR,
+ .label = "GPD",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[0],
+ .chip = {
+ .base = S3C64XX_GPE(0),
+ .ngpio = S3C64XX_GPIO_E_NR,
+ .label = "GPE",
+ },
+ }, {
+ .base = S3C64XX_GPG_BASE,
+ .chip = {
+ .base = S3C64XX_GPG(0),
+ .ngpio = S3C64XX_GPIO_G_NR,
+ .label = "GPG",
+ },
+ }, {
+ .base = S3C64XX_GPM_BASE,
+ .config = &samsung_gpio_cfgs[1],
+ .chip = {
+ .base = S3C64XX_GPM(0),
+ .ngpio = S3C64XX_GPIO_M_NR,
+ .label = "GPM",
+ .to_irq = s3c64xx_gpiolib_mbank_to_irq,
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip s3c64xx_gpios_4bit2[] = {
+#ifdef CONFIG_PLAT_S3C64XX
+ {
+ .base = S3C64XX_GPH_BASE + 0x4,
+ .chip = {
+ .base = S3C64XX_GPH(0),
+ .ngpio = S3C64XX_GPIO_H_NR,
+ .label = "GPH",
+ },
+ }, {
+ .base = S3C64XX_GPK_BASE + 0x4,
+ .config = &samsung_gpio_cfgs[0],
+ .chip = {
+ .base = S3C64XX_GPK(0),
+ .ngpio = S3C64XX_GPIO_K_NR,
+ .label = "GPK",
+ },
+ }, {
+ .base = S3C64XX_GPL_BASE + 0x4,
+ .config = &samsung_gpio_cfgs[1],
+ .chip = {
+ .base = S3C64XX_GPL(0),
+ .ngpio = S3C64XX_GPIO_L_NR,
+ .label = "GPL",
+ .to_irq = s3c64xx_gpiolib_lbank_to_irq,
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip s3c64xx_gpios_2bit[] = {
+#ifdef CONFIG_PLAT_S3C64XX
+ {
+ .base = S3C64XX_GPF_BASE,
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S3C64XX_GPF(0),
+ .ngpio = S3C64XX_GPIO_F_NR,
+ .label = "GPF",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[7],
+ .chip = {
+ .base = S3C64XX_GPI(0),
+ .ngpio = S3C64XX_GPIO_I_NR,
+ .label = "GPI",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[7],
+ .chip = {
+ .base = S3C64XX_GPJ(0),
+ .ngpio = S3C64XX_GPIO_J_NR,
+ .label = "GPJ",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S3C64XX_GPO(0),
+ .ngpio = S3C64XX_GPIO_O_NR,
+ .label = "GPO",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S3C64XX_GPP(0),
+ .ngpio = S3C64XX_GPIO_P_NR,
+ .label = "GPP",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S3C64XX_GPQ(0),
+ .ngpio = S3C64XX_GPIO_Q_NR,
+ .label = "GPQ",
+ },
+ }, {
+ .base = S3C64XX_GPN_BASE,
+ .irq_base = IRQ_EINT(0),
+ .config = &samsung_gpio_cfgs[5],
+ .chip = {
+ .base = S3C64XX_GPN(0),
+ .ngpio = S3C64XX_GPIO_N_NR,
+ .label = "GPN",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ },
+#endif
+};
+
+/*
+ * S5P6440 GPIO bank summary:
+ *
+ * Bank GPIOs Style SlpCon ExtInt Group
+ * A 6 4Bit Yes 1
+ * B 7 4Bit Yes 1
+ * C 8 4Bit Yes 2
+ * F 2 2Bit Yes 4 [1]
+ * G 7 4Bit Yes 5
+ * H 10 4Bit[2] Yes 6
+ * I 16 2Bit Yes None
+ * J 12 2Bit Yes None
+ * N 16 2Bit No IRQ_EINT
+ * P 8 2Bit Yes 8
+ * R 15 4Bit[2] Yes 8
+ */
+
+static struct samsung_gpio_chip s5p6440_gpios_4bit[] = {
+#ifdef CONFIG_CPU_S5P6440
+ {
+ .chip = {
+ .base = S5P6440_GPA(0),
+ .ngpio = S5P6440_GPIO_A_NR,
+ .label = "GPA",
+ },
+ }, {
+ .chip = {
+ .base = S5P6440_GPB(0),
+ .ngpio = S5P6440_GPIO_B_NR,
+ .label = "GPB",
+ },
+ }, {
+ .chip = {
+ .base = S5P6440_GPC(0),
+ .ngpio = S5P6440_GPIO_C_NR,
+ .label = "GPC",
+ },
+ }, {
+ .base = S5P64X0_GPG_BASE,
+ .chip = {
+ .base = S5P6440_GPG(0),
+ .ngpio = S5P6440_GPIO_G_NR,
+ .label = "GPG",
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip s5p6440_gpios_4bit2[] = {
+#ifdef CONFIG_CPU_S5P6440
+ {
+ .base = S5P64X0_GPH_BASE + 0x4,
+ .chip = {
+ .base = S5P6440_GPH(0),
+ .ngpio = S5P6440_GPIO_H_NR,
+ .label = "GPH",
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip s5p6440_gpios_rbank[] = {
+#ifdef CONFIG_CPU_S5P6440
+ {
+ .base = S5P64X0_GPR_BASE + 0x4,
+ .config = &s5p64x0_gpio_cfg_rbank,
+ .chip = {
+ .base = S5P6440_GPR(0),
+ .ngpio = S5P6440_GPIO_R_NR,
+ .label = "GPR",
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip s5p6440_gpios_2bit[] = {
+#ifdef CONFIG_CPU_S5P6440
+ {
+ .base = S5P64X0_GPF_BASE,
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S5P6440_GPF(0),
+ .ngpio = S5P6440_GPIO_F_NR,
+ .label = "GPF",
+ },
+ }, {
+ .base = S5P64X0_GPI_BASE,
+ .config = &samsung_gpio_cfgs[4],
+ .chip = {
+ .base = S5P6440_GPI(0),
+ .ngpio = S5P6440_GPIO_I_NR,
+ .label = "GPI",
+ },
+ }, {
+ .base = S5P64X0_GPJ_BASE,
+ .config = &samsung_gpio_cfgs[4],
+ .chip = {
+ .base = S5P6440_GPJ(0),
+ .ngpio = S5P6440_GPIO_J_NR,
+ .label = "GPJ",
+ },
+ }, {
+ .base = S5P64X0_GPN_BASE,
+ .config = &samsung_gpio_cfgs[5],
+ .chip = {
+ .base = S5P6440_GPN(0),
+ .ngpio = S5P6440_GPIO_N_NR,
+ .label = "GPN",
+ },
+ }, {
+ .base = S5P64X0_GPP_BASE,
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S5P6440_GPP(0),
+ .ngpio = S5P6440_GPIO_P_NR,
+ .label = "GPP",
+ },
+ },
+#endif
+};
+
+/*
+ * S5P6450 GPIO bank summary:
+ *
+ * Bank GPIOs Style SlpCon ExtInt Group
+ * A 6 4Bit Yes 1
+ * B 7 4Bit Yes 1
+ * C 8 4Bit Yes 2
+ * D 8 4Bit Yes None
+ * F 2 2Bit Yes None
+ * G 14 4Bit[2] Yes 5
+ * H 10 4Bit[2] Yes 6
+ * I 16 2Bit Yes None
+ * J 12 2Bit Yes None
+ * K 5 4Bit Yes None
+ * N 16 2Bit No IRQ_EINT
+ * P 11 2Bit Yes 8
+ * Q 14 2Bit Yes None
+ * R 15 4Bit[2] Yes None
+ * S 8 2Bit Yes None
+ *
+ * [1] BANKF pins 14,15 do not form part of the external interrupt sources
+ * [2] BANK has two control registers, GPxCON0 and GPxCON1
+ */
+
+static struct samsung_gpio_chip s5p6450_gpios_4bit[] = {
+#ifdef CONFIG_CPU_S5P6450
+ {
+ .chip = {
+ .base = S5P6450_GPA(0),
+ .ngpio = S5P6450_GPIO_A_NR,
+ .label = "GPA",
+ },
+ }, {
+ .chip = {
+ .base = S5P6450_GPB(0),
+ .ngpio = S5P6450_GPIO_B_NR,
+ .label = "GPB",
+ },
+ }, {
+ .chip = {
+ .base = S5P6450_GPC(0),
+ .ngpio = S5P6450_GPIO_C_NR,
+ .label = "GPC",
+ },
+ }, {
+ .chip = {
+ .base = S5P6450_GPD(0),
+ .ngpio = S5P6450_GPIO_D_NR,
+ .label = "GPD",
+ },
+ }, {
+ .base = S5P6450_GPK_BASE,
+ .chip = {
+ .base = S5P6450_GPK(0),
+ .ngpio = S5P6450_GPIO_K_NR,
+ .label = "GPK",
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip s5p6450_gpios_4bit2[] = {
+#ifdef CONFIG_CPU_S5P6450
+ {
+ .base = S5P64X0_GPG_BASE + 0x4,
+ .chip = {
+ .base = S5P6450_GPG(0),
+ .ngpio = S5P6450_GPIO_G_NR,
+ .label = "GPG",
+ },
+ }, {
+ .base = S5P64X0_GPH_BASE + 0x4,
+ .chip = {
+ .base = S5P6450_GPH(0),
+ .ngpio = S5P6450_GPIO_H_NR,
+ .label = "GPH",
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip s5p6450_gpios_rbank[] = {
+#ifdef CONFIG_CPU_S5P6450
+ {
+ .base = S5P64X0_GPR_BASE + 0x4,
+ .config = &s5p64x0_gpio_cfg_rbank,
+ .chip = {
+ .base = S5P6450_GPR(0),
+ .ngpio = S5P6450_GPIO_R_NR,
+ .label = "GPR",
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip s5p6450_gpios_2bit[] = {
+#ifdef CONFIG_CPU_S5P6450
+ {
+ .base = S5P64X0_GPF_BASE,
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S5P6450_GPF(0),
+ .ngpio = S5P6450_GPIO_F_NR,
+ .label = "GPF",
+ },
+ }, {
+ .base = S5P64X0_GPI_BASE,
+ .config = &samsung_gpio_cfgs[4],
+ .chip = {
+ .base = S5P6450_GPI(0),
+ .ngpio = S5P6450_GPIO_I_NR,
+ .label = "GPI",
+ },
+ }, {
+ .base = S5P64X0_GPJ_BASE,
+ .config = &samsung_gpio_cfgs[4],
+ .chip = {
+ .base = S5P6450_GPJ(0),
+ .ngpio = S5P6450_GPIO_J_NR,
+ .label = "GPJ",
+ },
+ }, {
+ .base = S5P64X0_GPN_BASE,
+ .config = &samsung_gpio_cfgs[5],
+ .chip = {
+ .base = S5P6450_GPN(0),
+ .ngpio = S5P6450_GPIO_N_NR,
+ .label = "GPN",
+ },
+ }, {
+ .base = S5P64X0_GPP_BASE,
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S5P6450_GPP(0),
+ .ngpio = S5P6450_GPIO_P_NR,
+ .label = "GPP",
+ },
+ }, {
+ .base = S5P6450_GPQ_BASE,
+ .config = &samsung_gpio_cfgs[5],
+ .chip = {
+ .base = S5P6450_GPQ(0),
+ .ngpio = S5P6450_GPIO_Q_NR,
+ .label = "GPQ",
+ },
+ }, {
+ .base = S5P6450_GPS_BASE,
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S5P6450_GPS(0),
+ .ngpio = S5P6450_GPIO_S_NR,
+ .label = "GPS",
+ },
+ },
+#endif
+};
+
+/*
+ * S5PC100 GPIO bank summary:
+ *
+ * Bank GPIOs Style INT Type
+ * A0 8 4Bit GPIO_INT0
+ * A1 5 4Bit GPIO_INT1
+ * B 8 4Bit GPIO_INT2
+ * C 5 4Bit GPIO_INT3
+ * D 7 4Bit GPIO_INT4
+ * E0 8 4Bit GPIO_INT5
+ * E1 6 4Bit GPIO_INT6
+ * F0 8 4Bit GPIO_INT7
+ * F1 8 4Bit GPIO_INT8
+ * F2 8 4Bit GPIO_INT9
+ * F3 4 4Bit GPIO_INT10
+ * G0 8 4Bit GPIO_INT11
+ * G1 3 4Bit GPIO_INT12
+ * G2 7 4Bit GPIO_INT13
+ * G3 7 4Bit GPIO_INT14
+ * H0 8 4Bit WKUP_INT
+ * H1 8 4Bit WKUP_INT
+ * H2 8 4Bit WKUP_INT
+ * H3 8 4Bit WKUP_INT
+ * I 8 4Bit GPIO_INT15
+ * J0 8 4Bit GPIO_INT16
+ * J1 5 4Bit GPIO_INT17
+ * J2 8 4Bit GPIO_INT18
+ * J3 8 4Bit GPIO_INT19
+ * J4 4 4Bit GPIO_INT20
+ * K0 8 4Bit None
+ * K1 6 4Bit None
+ * K2 8 4Bit None
+ * K3 8 4Bit None
+ * L0 8 4Bit None
+ * L1 8 4Bit None
+ * L2 8 4Bit None
+ * L3 8 4Bit None
+ */
+
+static struct samsung_gpio_chip s5pc100_gpios_4bit[] = {
+#ifdef CONFIG_CPU_S5PC100
+ {
+ .chip = {
+ .base = S5PC100_GPA0(0),
+ .ngpio = S5PC100_GPIO_A0_NR,
+ .label = "GPA0",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPA1(0),
+ .ngpio = S5PC100_GPIO_A1_NR,
+ .label = "GPA1",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPB(0),
+ .ngpio = S5PC100_GPIO_B_NR,
+ .label = "GPB",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPC(0),
+ .ngpio = S5PC100_GPIO_C_NR,
+ .label = "GPC",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPD(0),
+ .ngpio = S5PC100_GPIO_D_NR,
+ .label = "GPD",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPE0(0),
+ .ngpio = S5PC100_GPIO_E0_NR,
+ .label = "GPE0",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPE1(0),
+ .ngpio = S5PC100_GPIO_E1_NR,
+ .label = "GPE1",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPF0(0),
+ .ngpio = S5PC100_GPIO_F0_NR,
+ .label = "GPF0",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPF1(0),
+ .ngpio = S5PC100_GPIO_F1_NR,
+ .label = "GPF1",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPF2(0),
+ .ngpio = S5PC100_GPIO_F2_NR,
+ .label = "GPF2",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPF3(0),
+ .ngpio = S5PC100_GPIO_F3_NR,
+ .label = "GPF3",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPG0(0),
+ .ngpio = S5PC100_GPIO_G0_NR,
+ .label = "GPG0",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPG1(0),
+ .ngpio = S5PC100_GPIO_G1_NR,
+ .label = "GPG1",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPG2(0),
+ .ngpio = S5PC100_GPIO_G2_NR,
+ .label = "GPG2",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPG3(0),
+ .ngpio = S5PC100_GPIO_G3_NR,
+ .label = "GPG3",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPI(0),
+ .ngpio = S5PC100_GPIO_I_NR,
+ .label = "GPI",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPJ0(0),
+ .ngpio = S5PC100_GPIO_J0_NR,
+ .label = "GPJ0",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPJ1(0),
+ .ngpio = S5PC100_GPIO_J1_NR,
+ .label = "GPJ1",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPJ2(0),
+ .ngpio = S5PC100_GPIO_J2_NR,
+ .label = "GPJ2",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPJ3(0),
+ .ngpio = S5PC100_GPIO_J3_NR,
+ .label = "GPJ3",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPJ4(0),
+ .ngpio = S5PC100_GPIO_J4_NR,
+ .label = "GPJ4",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPK0(0),
+ .ngpio = S5PC100_GPIO_K0_NR,
+ .label = "GPK0",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPK1(0),
+ .ngpio = S5PC100_GPIO_K1_NR,
+ .label = "GPK1",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPK2(0),
+ .ngpio = S5PC100_GPIO_K2_NR,
+ .label = "GPK2",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPK3(0),
+ .ngpio = S5PC100_GPIO_K3_NR,
+ .label = "GPK3",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPL0(0),
+ .ngpio = S5PC100_GPIO_L0_NR,
+ .label = "GPL0",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPL1(0),
+ .ngpio = S5PC100_GPIO_L1_NR,
+ .label = "GPL1",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPL2(0),
+ .ngpio = S5PC100_GPIO_L2_NR,
+ .label = "GPL2",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPL3(0),
+ .ngpio = S5PC100_GPIO_L3_NR,
+ .label = "GPL3",
+ },
+ }, {
+ .chip = {
+ .base = S5PC100_GPL4(0),
+ .ngpio = S5PC100_GPIO_L4_NR,
+ .label = "GPL4",
+ },
+ }, {
+ .base = (S5P_VA_GPIO + 0xC00),
+ .irq_base = IRQ_EINT(0),
+ .chip = {
+ .base = S5PC100_GPH0(0),
+ .ngpio = S5PC100_GPIO_H0_NR,
+ .label = "GPH0",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .base = (S5P_VA_GPIO + 0xC20),
+ .irq_base = IRQ_EINT(8),
+ .chip = {
+ .base = S5PC100_GPH1(0),
+ .ngpio = S5PC100_GPIO_H1_NR,
+ .label = "GPH1",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .base = (S5P_VA_GPIO + 0xC40),
+ .irq_base = IRQ_EINT(16),
+ .chip = {
+ .base = S5PC100_GPH2(0),
+ .ngpio = S5PC100_GPIO_H2_NR,
+ .label = "GPH2",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .base = (S5P_VA_GPIO + 0xC60),
+ .irq_base = IRQ_EINT(24),
+ .chip = {
+ .base = S5PC100_GPH3(0),
+ .ngpio = S5PC100_GPIO_H3_NR,
+ .label = "GPH3",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ },
+#endif
+};
+
+/*
+ * Followings are the gpio banks in S5PV210/S5PC110
+ *
+ * The 'config' member when left to NULL, is initialized to the default
+ * structure samsung_gpio_cfgs[3] in the init function below.
+ *
+ * The 'base' member is also initialized in the init function below.
+ * Note: The initialization of 'base' member of samsung_gpio_chip structure
+ * uses the above macro and depends on the banks being listed in order here.
+ */
+
+static struct samsung_gpio_chip s5pv210_gpios_4bit[] = {
+#ifdef CONFIG_CPU_S5PV210
+ {
+ .chip = {
+ .base = S5PV210_GPA0(0),
+ .ngpio = S5PV210_GPIO_A0_NR,
+ .label = "GPA0",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPA1(0),
+ .ngpio = S5PV210_GPIO_A1_NR,
+ .label = "GPA1",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPB(0),
+ .ngpio = S5PV210_GPIO_B_NR,
+ .label = "GPB",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPC0(0),
+ .ngpio = S5PV210_GPIO_C0_NR,
+ .label = "GPC0",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPC1(0),
+ .ngpio = S5PV210_GPIO_C1_NR,
+ .label = "GPC1",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPD0(0),
+ .ngpio = S5PV210_GPIO_D0_NR,
+ .label = "GPD0",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPD1(0),
+ .ngpio = S5PV210_GPIO_D1_NR,
+ .label = "GPD1",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPE0(0),
+ .ngpio = S5PV210_GPIO_E0_NR,
+ .label = "GPE0",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPE1(0),
+ .ngpio = S5PV210_GPIO_E1_NR,
+ .label = "GPE1",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPF0(0),
+ .ngpio = S5PV210_GPIO_F0_NR,
+ .label = "GPF0",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPF1(0),
+ .ngpio = S5PV210_GPIO_F1_NR,
+ .label = "GPF1",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPF2(0),
+ .ngpio = S5PV210_GPIO_F2_NR,
+ .label = "GPF2",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPF3(0),
+ .ngpio = S5PV210_GPIO_F3_NR,
+ .label = "GPF3",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPG0(0),
+ .ngpio = S5PV210_GPIO_G0_NR,
+ .label = "GPG0",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPG1(0),
+ .ngpio = S5PV210_GPIO_G1_NR,
+ .label = "GPG1",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPG2(0),
+ .ngpio = S5PV210_GPIO_G2_NR,
+ .label = "GPG2",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPG3(0),
+ .ngpio = S5PV210_GPIO_G3_NR,
+ .label = "GPG3",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPI(0),
+ .ngpio = S5PV210_GPIO_I_NR,
+ .label = "GPI",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPJ0(0),
+ .ngpio = S5PV210_GPIO_J0_NR,
+ .label = "GPJ0",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPJ1(0),
+ .ngpio = S5PV210_GPIO_J1_NR,
+ .label = "GPJ1",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPJ2(0),
+ .ngpio = S5PV210_GPIO_J2_NR,
+ .label = "GPJ2",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPJ3(0),
+ .ngpio = S5PV210_GPIO_J3_NR,
+ .label = "GPJ3",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_GPJ4(0),
+ .ngpio = S5PV210_GPIO_J4_NR,
+ .label = "GPJ4",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_MP01(0),
+ .ngpio = S5PV210_GPIO_MP01_NR,
+ .label = "MP01",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_MP02(0),
+ .ngpio = S5PV210_GPIO_MP02_NR,
+ .label = "MP02",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_MP03(0),
+ .ngpio = S5PV210_GPIO_MP03_NR,
+ .label = "MP03",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_MP04(0),
+ .ngpio = S5PV210_GPIO_MP04_NR,
+ .label = "MP04",
+ },
+ }, {
+ .chip = {
+ .base = S5PV210_MP05(0),
+ .ngpio = S5PV210_GPIO_MP05_NR,
+ .label = "MP05",
+ },
+ }, {
+ .base = (S5P_VA_GPIO + 0xC00),
+ .irq_base = IRQ_EINT(0),
+ .chip = {
+ .base = S5PV210_GPH0(0),
+ .ngpio = S5PV210_GPIO_H0_NR,
+ .label = "GPH0",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .base = (S5P_VA_GPIO + 0xC20),
+ .irq_base = IRQ_EINT(8),
+ .chip = {
+ .base = S5PV210_GPH1(0),
+ .ngpio = S5PV210_GPIO_H1_NR,
+ .label = "GPH1",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .base = (S5P_VA_GPIO + 0xC40),
+ .irq_base = IRQ_EINT(16),
+ .chip = {
+ .base = S5PV210_GPH2(0),
+ .ngpio = S5PV210_GPIO_H2_NR,
+ .label = "GPH2",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .base = (S5P_VA_GPIO + 0xC60),
+ .irq_base = IRQ_EINT(24),
+ .chip = {
+ .base = S5PV210_GPH3(0),
+ .ngpio = S5PV210_GPIO_H3_NR,
+ .label = "GPH3",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ },
+#endif
+};
+
+/*
+ * Followings are the gpio banks in EXYNOS4210
+ *
+ * The 'config' member when left to NULL, is initialized to the default
+ * structure samsung_gpio_cfgs[3] in the init function below.
+ *
+ * The 'base' member is also initialized in the init function below.
+ * Note: The initialization of 'base' member of samsung_gpio_chip structure
+ * uses the above macro and depends on the banks being listed in order here.
+ */
+
+static struct samsung_gpio_chip exynos4_gpios_1[] = {
+#ifdef CONFIG_ARCH_EXYNOS4
+ {
+ .chip = {
+ .base = EXYNOS4_GPA0(0),
+ .ngpio = EXYNOS4_GPIO_A0_NR,
+ .label = "GPA0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPA1(0),
+ .ngpio = EXYNOS4_GPIO_A1_NR,
+ .label = "GPA1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPB(0),
+ .ngpio = EXYNOS4_GPIO_B_NR,
+ .label = "GPB",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPC0(0),
+ .ngpio = EXYNOS4_GPIO_C0_NR,
+ .label = "GPC0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPC1(0),
+ .ngpio = EXYNOS4_GPIO_C1_NR,
+ .label = "GPC1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPD0(0),
+ .ngpio = EXYNOS4_GPIO_D0_NR,
+ .label = "GPD0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPD1(0),
+ .ngpio = EXYNOS4_GPIO_D1_NR,
+ .label = "GPD1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPE0(0),
+ .ngpio = EXYNOS4_GPIO_E0_NR,
+ .label = "GPE0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPE1(0),
+ .ngpio = EXYNOS4_GPIO_E1_NR,
+ .label = "GPE1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPE2(0),
+ .ngpio = EXYNOS4_GPIO_E2_NR,
+ .label = "GPE2",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPE3(0),
+ .ngpio = EXYNOS4_GPIO_E3_NR,
+ .label = "GPE3",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPE4(0),
+ .ngpio = EXYNOS4_GPIO_E4_NR,
+ .label = "GPE4",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPF0(0),
+ .ngpio = EXYNOS4_GPIO_F0_NR,
+ .label = "GPF0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPF1(0),
+ .ngpio = EXYNOS4_GPIO_F1_NR,
+ .label = "GPF1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPF2(0),
+ .ngpio = EXYNOS4_GPIO_F2_NR,
+ .label = "GPF2",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPF3(0),
+ .ngpio = EXYNOS4_GPIO_F3_NR,
+ .label = "GPF3",
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip exynos4_gpios_2[] = {
+#ifdef CONFIG_ARCH_EXYNOS4
+ {
+ .chip = {
+ .base = EXYNOS4_GPJ0(0),
+ .ngpio = EXYNOS4_GPIO_J0_NR,
+ .label = "GPJ0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPJ1(0),
+ .ngpio = EXYNOS4_GPIO_J1_NR,
+ .label = "GPJ1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPK0(0),
+ .ngpio = EXYNOS4_GPIO_K0_NR,
+ .label = "GPK0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPK1(0),
+ .ngpio = EXYNOS4_GPIO_K1_NR,
+ .label = "GPK1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPK2(0),
+ .ngpio = EXYNOS4_GPIO_K2_NR,
+ .label = "GPK2",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPK3(0),
+ .ngpio = EXYNOS4_GPIO_K3_NR,
+ .label = "GPK3",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPL0(0),
+ .ngpio = EXYNOS4_GPIO_L0_NR,
+ .label = "GPL0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPL1(0),
+ .ngpio = EXYNOS4_GPIO_L1_NR,
+ .label = "GPL1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS4_GPL2(0),
+ .ngpio = EXYNOS4_GPIO_L2_NR,
+ .label = "GPL2",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[8],
+ .chip = {
+ .base = EXYNOS4_GPY0(0),
+ .ngpio = EXYNOS4_GPIO_Y0_NR,
+ .label = "GPY0",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[8],
+ .chip = {
+ .base = EXYNOS4_GPY1(0),
+ .ngpio = EXYNOS4_GPIO_Y1_NR,
+ .label = "GPY1",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[8],
+ .chip = {
+ .base = EXYNOS4_GPY2(0),
+ .ngpio = EXYNOS4_GPIO_Y2_NR,
+ .label = "GPY2",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[8],
+ .chip = {
+ .base = EXYNOS4_GPY3(0),
+ .ngpio = EXYNOS4_GPIO_Y3_NR,
+ .label = "GPY3",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[8],
+ .chip = {
+ .base = EXYNOS4_GPY4(0),
+ .ngpio = EXYNOS4_GPIO_Y4_NR,
+ .label = "GPY4",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[8],
+ .chip = {
+ .base = EXYNOS4_GPY5(0),
+ .ngpio = EXYNOS4_GPIO_Y5_NR,
+ .label = "GPY5",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[8],
+ .chip = {
+ .base = EXYNOS4_GPY6(0),
+ .ngpio = EXYNOS4_GPIO_Y6_NR,
+ .label = "GPY6",
+ },
+ }, {
+ .base = (S5P_VA_GPIO2 + 0xC00),
+ .config = &samsung_gpio_cfgs[9],
+ .irq_base = IRQ_EINT(0),
+ .chip = {
+ .base = EXYNOS4_GPX0(0),
+ .ngpio = EXYNOS4_GPIO_X0_NR,
+ .label = "GPX0",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .base = (S5P_VA_GPIO2 + 0xC20),
+ .config = &samsung_gpio_cfgs[9],
+ .irq_base = IRQ_EINT(8),
+ .chip = {
+ .base = EXYNOS4_GPX1(0),
+ .ngpio = EXYNOS4_GPIO_X1_NR,
+ .label = "GPX1",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .base = (S5P_VA_GPIO2 + 0xC40),
+ .config = &samsung_gpio_cfgs[9],
+ .irq_base = IRQ_EINT(16),
+ .chip = {
+ .base = EXYNOS4_GPX2(0),
+ .ngpio = EXYNOS4_GPIO_X2_NR,
+ .label = "GPX2",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .base = (S5P_VA_GPIO2 + 0xC60),
+ .config = &samsung_gpio_cfgs[9],
+ .irq_base = IRQ_EINT(24),
+ .chip = {
+ .base = EXYNOS4_GPX3(0),
+ .ngpio = EXYNOS4_GPIO_X3_NR,
+ .label = "GPX3",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip exynos4_gpios_3[] = {
+#ifdef CONFIG_ARCH_EXYNOS4
+ {
+ .chip = {
+ .base = EXYNOS4_GPZ(0),
+ .ngpio = EXYNOS4_GPIO_Z_NR,
+ .label = "GPZ",
+ },
+ },
+#endif
+};
+
+/* TODO: cleanup soc_is_* */
+static __init int samsung_gpiolib_init(void)
+{
+ struct samsung_gpio_chip *chip;
+ int i, nr_chips;
+ int group = 0;
+
+ samsung_gpiolib_set_cfg(samsung_gpio_cfgs, ARRAY_SIZE(samsung_gpio_cfgs));
+
+ if (soc_is_s3c24xx()) {
+ s3c24xx_gpiolib_add_chips(s3c24xx_gpios,
+ ARRAY_SIZE(s3c24xx_gpios), S3C24XX_VA_GPIO);
+ } else if (soc_is_s3c64xx()) {
+ samsung_gpiolib_add_2bit_chips(s3c64xx_gpios_2bit,
+ ARRAY_SIZE(s3c64xx_gpios_2bit),
+ S3C64XX_VA_GPIO + 0xE0, 0x20);
+ samsung_gpiolib_add_4bit_chips(s3c64xx_gpios_4bit,
+ ARRAY_SIZE(s3c64xx_gpios_4bit),
+ S3C64XX_VA_GPIO);
+ samsung_gpiolib_add_4bit2_chips(s3c64xx_gpios_4bit2,
+ ARRAY_SIZE(s3c64xx_gpios_4bit2));
+ } else if (soc_is_s5p6440()) {
+ samsung_gpiolib_add_2bit_chips(s5p6440_gpios_2bit,
+ ARRAY_SIZE(s5p6440_gpios_2bit), NULL, 0x0);
+ samsung_gpiolib_add_4bit_chips(s5p6440_gpios_4bit,
+ ARRAY_SIZE(s5p6440_gpios_4bit), S5P_VA_GPIO);
+ samsung_gpiolib_add_4bit2_chips(s5p6440_gpios_4bit2,
+ ARRAY_SIZE(s5p6440_gpios_4bit2));
+ s5p64x0_gpiolib_add_rbank(s5p6440_gpios_rbank,
+ ARRAY_SIZE(s5p6440_gpios_rbank));
+ } else if (soc_is_s5p6450()) {
+ samsung_gpiolib_add_2bit_chips(s5p6450_gpios_2bit,
+ ARRAY_SIZE(s5p6450_gpios_2bit), NULL, 0x0);
+ samsung_gpiolib_add_4bit_chips(s5p6450_gpios_4bit,
+ ARRAY_SIZE(s5p6450_gpios_4bit), S5P_VA_GPIO);
+ samsung_gpiolib_add_4bit2_chips(s5p6450_gpios_4bit2,
+ ARRAY_SIZE(s5p6450_gpios_4bit2));
+ s5p64x0_gpiolib_add_rbank(s5p6450_gpios_rbank,
+ ARRAY_SIZE(s5p6450_gpios_rbank));
+ } else if (soc_is_s5pc100()) {
+ group = 0;
+ chip = s5pc100_gpios_4bit;
+ nr_chips = ARRAY_SIZE(s5pc100_gpios_4bit);
+
+ for (i = 0; i < nr_chips; i++, chip++) {
+ if (!chip->config) {
+ chip->config = &samsung_gpio_cfgs[3];
+ chip->group = group++;
+ }
+ }
+ samsung_gpiolib_add_4bit_chips(s5pc100_gpios_4bit, nr_chips, S5P_VA_GPIO);
+#if defined(CONFIG_CPU_S5PC100) && defined(CONFIG_S5P_GPIO_INT)
+ s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR);
+#endif
+ } else if (soc_is_s5pv210()) {
+ group = 0;
+ chip = s5pv210_gpios_4bit;
+ nr_chips = ARRAY_SIZE(s5pv210_gpios_4bit);
+
+ for (i = 0; i < nr_chips; i++, chip++) {
+ if (!chip->config) {
+ chip->config = &samsung_gpio_cfgs[3];
+ chip->group = group++;
+ }
+ }
+ samsung_gpiolib_add_4bit_chips(s5pv210_gpios_4bit, nr_chips, S5P_VA_GPIO);
+#if defined(CONFIG_CPU_S5PV210) && defined(CONFIG_S5P_GPIO_INT)
+ s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR);
+#endif
+ } else if (soc_is_exynos4210()) {
+ group = 0;
+
+ /* gpio part1 */
+ chip = exynos4_gpios_1;
+ nr_chips = ARRAY_SIZE(exynos4_gpios_1);
+
+ for (i = 0; i < nr_chips; i++, chip++) {
+ if (!chip->config) {
+ chip->config = &exynos4_gpio_cfg;
+ chip->group = group++;
+ }
+ }
+ samsung_gpiolib_add_4bit_chips(exynos4_gpios_1, nr_chips, S5P_VA_GPIO1);
+
+ /* gpio part2 */
+ chip = exynos4_gpios_2;
+ nr_chips = ARRAY_SIZE(exynos4_gpios_2);
+
+ for (i = 0; i < nr_chips; i++, chip++) {
+ if (!chip->config) {
+ chip->config = &exynos4_gpio_cfg;
+ chip->group = group++;
+ }
+ }
+ samsung_gpiolib_add_4bit_chips(exynos4_gpios_2, nr_chips, S5P_VA_GPIO2);
+
+ /* gpio part3 */
+ chip = exynos4_gpios_3;
+ nr_chips = ARRAY_SIZE(exynos4_gpios_3);
+
+ for (i = 0; i < nr_chips; i++, chip++) {
+ if (!chip->config) {
+ chip->config = &exynos4_gpio_cfg;
+ chip->group = group++;
+ }
+ }
+ samsung_gpiolib_add_4bit_chips(exynos4_gpios_3, nr_chips, S5P_VA_GPIO3);
+
+#if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_S5P_GPIO_INT)
+ s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS);
+ s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS);
+#endif
+ } else {
+ WARN(1, "Unknown SoC in gpio-samsung, no GPIOs added\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+core_initcall(samsung_gpiolib_init);
+
+int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned long flags;
+ int offset;
+ int ret;
+
+ if (!chip)
+ return -EINVAL;
+
+ offset = pin - chip->chip.base;
+
+ samsung_gpio_lock(chip, flags);
+ ret = samsung_gpio_do_setcfg(chip, offset, config);
+ samsung_gpio_unlock(chip, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(s3c_gpio_cfgpin);
+
+int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr,
+ unsigned int cfg)
+{
+ int ret;
+
+ for (; nr > 0; nr--, start++) {
+ ret = s3c_gpio_cfgpin(start, cfg);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(s3c_gpio_cfgpin_range);
+
+int s3c_gpio_cfgall_range(unsigned int start, unsigned int nr,
+ unsigned int cfg, samsung_gpio_pull_t pull)
+{
+ int ret;
+
+ for (; nr > 0; nr--, start++) {
+ s3c_gpio_setpull(start, pull);
+ ret = s3c_gpio_cfgpin(start, cfg);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(s3c_gpio_cfgall_range);
+
+unsigned s3c_gpio_getcfg(unsigned int pin)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned long flags;
+ unsigned ret = 0;
+ int offset;
+
+ if (chip) {
+ offset = pin - chip->chip.base;
+
+ samsung_gpio_lock(chip, flags);
+ ret = samsung_gpio_do_getcfg(chip, offset);
+ samsung_gpio_unlock(chip, flags);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(s3c_gpio_getcfg);
+
+int s3c_gpio_setpull(unsigned int pin, samsung_gpio_pull_t pull)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned long flags;
+ int offset, ret;
+
+ if (!chip)
+ return -EINVAL;
+
+ offset = pin - chip->chip.base;
+
+ samsung_gpio_lock(chip, flags);
+ ret = samsung_gpio_do_setpull(chip, offset, pull);
+ samsung_gpio_unlock(chip, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(s3c_gpio_setpull);
+
+samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned long flags;
+ int offset;
+ u32 pup = 0;
+
+ if (chip) {
+ offset = pin - chip->chip.base;
+
+ samsung_gpio_lock(chip, flags);
+ pup = samsung_gpio_do_getpull(chip, offset);
+ samsung_gpio_unlock(chip, flags);
+ }
+
+ return (__force samsung_gpio_pull_t)pup;
+}
+EXPORT_SYMBOL(s3c_gpio_getpull);
+
+/* gpiolib wrappers until these are totally eliminated */
+
+void s3c2410_gpio_pullup(unsigned int pin, unsigned int to)
+{
+ int ret;
+
+ WARN_ON(to); /* should be none of these left */
+
+ if (!to) {
+ /* if pull is enabled, try first with up, and if that
+ * fails, try using down */
+
+ ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP);
+ if (ret)
+ s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN);
+ } else {
+ s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE);
+ }
+}
+EXPORT_SYMBOL(s3c2410_gpio_pullup);
+
+void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
+{
+ /* do this via gpiolib until all users removed */
+
+ gpio_request(pin, "temporary");
+ gpio_set_value(pin, to);
+ gpio_free(pin);
+}
+EXPORT_SYMBOL(s3c2410_gpio_setpin);
+
+unsigned int s3c2410_gpio_getpin(unsigned int pin)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned long offs = pin - chip->chip.base;
+
+ return __raw_readl(chip->base + 0x04) & (1 << offs);
+}
+EXPORT_SYMBOL(s3c2410_gpio_getpin);
+
+#ifdef CONFIG_S5P_GPIO_DRVSTR
+s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned int off;
+ void __iomem *reg;
+ int shift;
+ u32 drvstr;
+
+ if (!chip)
+ return -EINVAL;
+
+ off = pin - chip->chip.base;
+ shift = off * 2;
+ reg = chip->base + 0x0C;
+
+ drvstr = __raw_readl(reg);
+ drvstr = drvstr >> shift;
+ drvstr &= 0x3;
+
+ return (__force s5p_gpio_drvstr_t)drvstr;
+}
+EXPORT_SYMBOL(s5p_gpio_get_drvstr);
+
+int s5p_gpio_set_drvstr(unsigned int pin, s5p_gpio_drvstr_t drvstr)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned int off;
+ void __iomem *reg;
+ int shift;
+ u32 tmp;
+
+ if (!chip)
+ return -EINVAL;
+
+ off = pin - chip->chip.base;
+ shift = off * 2;
+ reg = chip->base + 0x0C;
+
+ tmp = __raw_readl(reg);
+ tmp &= ~(0x3 << shift);
+ tmp |= drvstr << shift;
+
+ __raw_writel(tmp, reg);
+
+ return 0;
+}
+EXPORT_SYMBOL(s5p_gpio_set_drvstr);
+#endif /* CONFIG_S5P_GPIO_DRVSTR */
+
+#ifdef CONFIG_PLAT_S3C24XX
+unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change)
+{
+ unsigned long flags;
+ unsigned long misccr;
+
+ local_irq_save(flags);
+ misccr = __raw_readl(S3C24XX_MISCCR);
+ misccr &= ~clear;
+ misccr ^= change;
+ __raw_writel(misccr, S3C24XX_MISCCR);
+ local_irq_restore(flags);
+
+ return misccr;
+}
+EXPORT_SYMBOL(s3c2410_modify_misccr);
+#endif
diff --git a/drivers/gpio/sch_gpio.c b/drivers/gpio/gpio-sch.c
index 56060421cdff..163515845494 100644
--- a/drivers/gpio/sch_gpio.c
+++ b/drivers/gpio/gpio-sch.c
@@ -1,5 +1,5 @@
/*
- * sch_gpio.c - GPIO interface for Intel Poulsbo SCH
+ * GPIO interface for Intel Poulsbo SCH
*
* Copyright (c) 2010 CompuLab Ltd
* Author: Denis Turischev <denis@compulab.co.il>
diff --git a/drivers/gpio/stmpe-gpio.c b/drivers/gpio/gpio-stmpe.c
index 4c980b573328..4c980b573328 100644
--- a/drivers/gpio/stmpe-gpio.c
+++ b/drivers/gpio/gpio-stmpe.c
diff --git a/drivers/gpio/sx150x.c b/drivers/gpio/gpio-sx150x.c
index a4f73534394e..a4f73534394e 100644
--- a/drivers/gpio/sx150x.c
+++ b/drivers/gpio/gpio-sx150x.c
diff --git a/drivers/gpio/tc3589x-gpio.c b/drivers/gpio/gpio-tc3589x.c
index 2a82e8999a42..2a82e8999a42 100644
--- a/drivers/gpio/tc3589x-gpio.c
+++ b/drivers/gpio/gpio-tc3589x.c
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
new file mode 100644
index 000000000000..61044c889f7f
--- /dev/null
+++ b/drivers/gpio/gpio-tegra.c
@@ -0,0 +1,497 @@
+/*
+ * arch/arm/mach-tegra/gpio.c
+ *
+ * Copyright (c) 2010 Google, Inc
+ *
+ * Author:
+ * Erik Gilling <konkers@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+#include <asm/mach/irq.h>
+
+#include <mach/gpio-tegra.h>
+#include <mach/iomap.h>
+#include <mach/suspend.h>
+
+#define GPIO_BANK(x) ((x) >> 5)
+#define GPIO_PORT(x) (((x) >> 3) & 0x3)
+#define GPIO_BIT(x) ((x) & 0x7)
+
+#define GPIO_REG(x) (GPIO_BANK(x) * 0x80 + GPIO_PORT(x) * 4)
+
+#define GPIO_CNF(x) (GPIO_REG(x) + 0x00)
+#define GPIO_OE(x) (GPIO_REG(x) + 0x10)
+#define GPIO_OUT(x) (GPIO_REG(x) + 0X20)
+#define GPIO_IN(x) (GPIO_REG(x) + 0x30)
+#define GPIO_INT_STA(x) (GPIO_REG(x) + 0x40)
+#define GPIO_INT_ENB(x) (GPIO_REG(x) + 0x50)
+#define GPIO_INT_LVL(x) (GPIO_REG(x) + 0x60)
+#define GPIO_INT_CLR(x) (GPIO_REG(x) + 0x70)
+
+#define GPIO_MSK_CNF(x) (GPIO_REG(x) + 0x800)
+#define GPIO_MSK_OE(x) (GPIO_REG(x) + 0x810)
+#define GPIO_MSK_OUT(x) (GPIO_REG(x) + 0X820)
+#define GPIO_MSK_INT_STA(x) (GPIO_REG(x) + 0x840)
+#define GPIO_MSK_INT_ENB(x) (GPIO_REG(x) + 0x850)
+#define GPIO_MSK_INT_LVL(x) (GPIO_REG(x) + 0x860)
+
+#define GPIO_INT_LVL_MASK 0x010101
+#define GPIO_INT_LVL_EDGE_RISING 0x000101
+#define GPIO_INT_LVL_EDGE_FALLING 0x000100
+#define GPIO_INT_LVL_EDGE_BOTH 0x010100
+#define GPIO_INT_LVL_LEVEL_HIGH 0x000001
+#define GPIO_INT_LVL_LEVEL_LOW 0x000000
+
+struct tegra_gpio_bank {
+ int bank;
+ int irq;
+ spinlock_t lvl_lock[4];
+#ifdef CONFIG_PM
+ u32 cnf[4];
+ u32 out[4];
+ u32 oe[4];
+ u32 int_enb[4];
+ u32 int_lvl[4];
+#endif
+};
+
+
+static void __iomem *regs;
+static struct tegra_gpio_bank tegra_gpio_banks[7];
+
+static inline void tegra_gpio_writel(u32 val, u32 reg)
+{
+ __raw_writel(val, regs + reg);
+}
+
+static inline u32 tegra_gpio_readl(u32 reg)
+{
+ return __raw_readl(regs + reg);
+}
+
+static int tegra_gpio_compose(int bank, int port, int bit)
+{
+ return (bank << 5) | ((port & 0x3) << 3) | (bit & 0x7);
+}
+
+static void tegra_gpio_mask_write(u32 reg, int gpio, int value)
+{
+ u32 val;
+
+ val = 0x100 << GPIO_BIT(gpio);
+ if (value)
+ val |= 1 << GPIO_BIT(gpio);
+ tegra_gpio_writel(val, reg);
+}
+
+void tegra_gpio_enable(int gpio)
+{
+ tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 1);
+}
+
+void tegra_gpio_disable(int gpio)
+{
+ tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 0);
+}
+
+static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ tegra_gpio_mask_write(GPIO_MSK_OUT(offset), offset, value);
+}
+
+static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1;
+}
+
+static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 0);
+ return 0;
+}
+
+static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ tegra_gpio_set(chip, offset, value);
+ tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 1);
+ return 0;
+}
+
+static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return TEGRA_GPIO_TO_IRQ(offset);
+}
+
+static struct gpio_chip tegra_gpio_chip = {
+ .label = "tegra-gpio",
+ .direction_input = tegra_gpio_direction_input,
+ .get = tegra_gpio_get,
+ .direction_output = tegra_gpio_direction_output,
+ .set = tegra_gpio_set,
+ .to_irq = tegra_gpio_to_irq,
+ .base = 0,
+ .ngpio = TEGRA_NR_GPIOS,
+};
+
+static void tegra_gpio_irq_ack(struct irq_data *d)
+{
+ int gpio = d->irq - INT_GPIO_BASE;
+
+ tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio));
+}
+
+static void tegra_gpio_irq_mask(struct irq_data *d)
+{
+ int gpio = d->irq - INT_GPIO_BASE;
+
+ tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 0);
+}
+
+static void tegra_gpio_irq_unmask(struct irq_data *d)
+{
+ int gpio = d->irq - INT_GPIO_BASE;
+
+ tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 1);
+}
+
+static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ int gpio = d->irq - INT_GPIO_BASE;
+ struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ int port = GPIO_PORT(gpio);
+ int lvl_type;
+ int val;
+ unsigned long flags;
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ lvl_type = GPIO_INT_LVL_EDGE_RISING;
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ lvl_type = GPIO_INT_LVL_EDGE_FALLING;
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ lvl_type = GPIO_INT_LVL_EDGE_BOTH;
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ lvl_type = GPIO_INT_LVL_LEVEL_HIGH;
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ lvl_type = GPIO_INT_LVL_LEVEL_LOW;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&bank->lvl_lock[port], flags);
+
+ val = tegra_gpio_readl(GPIO_INT_LVL(gpio));
+ val &= ~(GPIO_INT_LVL_MASK << GPIO_BIT(gpio));
+ val |= lvl_type << GPIO_BIT(gpio);
+ tegra_gpio_writel(val, GPIO_INT_LVL(gpio));
+
+ spin_unlock_irqrestore(&bank->lvl_lock[port], flags);
+
+ if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+ else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+
+ return 0;
+}
+
+static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct tegra_gpio_bank *bank;
+ int port;
+ int pin;
+ int unmasked = 0;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+
+ bank = irq_get_handler_data(irq);
+
+ for (port = 0; port < 4; port++) {
+ int gpio = tegra_gpio_compose(bank->bank, port, 0);
+ unsigned long sta = tegra_gpio_readl(GPIO_INT_STA(gpio)) &
+ tegra_gpio_readl(GPIO_INT_ENB(gpio));
+ u32 lvl = tegra_gpio_readl(GPIO_INT_LVL(gpio));
+
+ for_each_set_bit(pin, &sta, 8) {
+ tegra_gpio_writel(1 << pin, GPIO_INT_CLR(gpio));
+
+ /* if gpio is edge triggered, clear condition
+ * before executing the hander so that we don't
+ * miss edges
+ */
+ if (lvl & (0x100 << pin)) {
+ unmasked = 1;
+ chained_irq_exit(chip, desc);
+ }
+
+ generic_handle_irq(gpio_to_irq(gpio + pin));
+ }
+ }
+
+ if (!unmasked)
+ chained_irq_exit(chip, desc);
+
+}
+
+#ifdef CONFIG_PM
+void tegra_gpio_resume(void)
+{
+ unsigned long flags;
+ int b;
+ int p;
+
+ local_irq_save(flags);
+
+ for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
+ struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
+
+ for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
+ unsigned int gpio = (b<<5) | (p<<3);
+ tegra_gpio_writel(bank->cnf[p], GPIO_CNF(gpio));
+ tegra_gpio_writel(bank->out[p], GPIO_OUT(gpio));
+ tegra_gpio_writel(bank->oe[p], GPIO_OE(gpio));
+ tegra_gpio_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio));
+ tegra_gpio_writel(bank->int_enb[p], GPIO_INT_ENB(gpio));
+ }
+ }
+
+ local_irq_restore(flags);
+}
+
+void tegra_gpio_suspend(void)
+{
+ unsigned long flags;
+ int b;
+ int p;
+
+ local_irq_save(flags);
+ for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
+ struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
+
+ for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
+ unsigned int gpio = (b<<5) | (p<<3);
+ bank->cnf[p] = tegra_gpio_readl(GPIO_CNF(gpio));
+ bank->out[p] = tegra_gpio_readl(GPIO_OUT(gpio));
+ bank->oe[p] = tegra_gpio_readl(GPIO_OE(gpio));
+ bank->int_enb[p] = tegra_gpio_readl(GPIO_INT_ENB(gpio));
+ bank->int_lvl[p] = tegra_gpio_readl(GPIO_INT_LVL(gpio));
+ }
+ }
+ local_irq_restore(flags);
+}
+
+static int tegra_gpio_wake_enable(struct irq_data *d, unsigned int enable)
+{
+ struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ return irq_set_irq_wake(bank->irq, enable);
+}
+#endif
+
+static struct irq_chip tegra_gpio_irq_chip = {
+ .name = "GPIO",
+ .irq_ack = tegra_gpio_irq_ack,
+ .irq_mask = tegra_gpio_irq_mask,
+ .irq_unmask = tegra_gpio_irq_unmask,
+ .irq_set_type = tegra_gpio_irq_set_type,
+#ifdef CONFIG_PM
+ .irq_set_wake = tegra_gpio_wake_enable,
+#endif
+};
+
+
+/* This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key gpio_lock_class;
+
+static int __devinit tegra_gpio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct tegra_gpio_bank *bank;
+ int gpio;
+ int i;
+ int j;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) {
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+ if (!res) {
+ dev_err(&pdev->dev, "Missing IRQ resource\n");
+ return -ENODEV;
+ }
+
+ bank = &tegra_gpio_banks[i];
+ bank->bank = i;
+ bank->irq = res->start;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Missing MEM resource\n");
+ return -ENODEV;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res),
+ dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev, "Couldn't request MEM resource\n");
+ return -ENODEV;
+ }
+
+ regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!regs) {
+ dev_err(&pdev->dev, "Couldn't ioremap regs\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < 7; i++) {
+ for (j = 0; j < 4; j++) {
+ int gpio = tegra_gpio_compose(i, j, 0);
+ tegra_gpio_writel(0x00, GPIO_INT_ENB(gpio));
+ }
+ }
+
+#ifdef CONFIG_OF_GPIO
+ tegra_gpio_chip.of_node = pdev->dev.of_node;
+#endif
+
+ gpiochip_add(&tegra_gpio_chip);
+
+ for (gpio = 0; gpio < TEGRA_NR_GPIOS; gpio++) {
+ int irq = TEGRA_GPIO_TO_IRQ(gpio);
+ /* No validity check; all Tegra GPIOs are valid IRQs */
+
+ bank = &tegra_gpio_banks[GPIO_BANK(gpio)];
+
+ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_data(irq, bank);
+ irq_set_chip_and_handler(irq, &tegra_gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) {
+ bank = &tegra_gpio_banks[i];
+
+ irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler);
+ irq_set_handler_data(bank->irq, bank);
+
+ for (j = 0; j < 4; j++)
+ spin_lock_init(&bank->lvl_lock[j]);
+ }
+
+ return 0;
+}
+
+static struct of_device_id tegra_gpio_of_match[] __devinitdata = {
+ { .compatible = "nvidia,tegra20-gpio", },
+ { },
+};
+
+static struct platform_driver tegra_gpio_driver = {
+ .driver = {
+ .name = "tegra-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = tegra_gpio_of_match,
+ },
+ .probe = tegra_gpio_probe,
+};
+
+static int __init tegra_gpio_init(void)
+{
+ return platform_driver_register(&tegra_gpio_driver);
+}
+postcore_initcall(tegra_gpio_init);
+
+void __init tegra_gpio_config(struct tegra_gpio_table *table, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ int gpio = table[i].gpio;
+
+ if (table[i].enable)
+ tegra_gpio_enable(gpio);
+ else
+ tegra_gpio_disable(gpio);
+ }
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static int dbg_gpio_show(struct seq_file *s, void *unused)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < 7; i++) {
+ for (j = 0; j < 4; j++) {
+ int gpio = tegra_gpio_compose(i, j, 0);
+ seq_printf(s,
+ "%d:%d %02x %02x %02x %02x %02x %02x %06x\n",
+ i, j,
+ tegra_gpio_readl(GPIO_CNF(gpio)),
+ tegra_gpio_readl(GPIO_OE(gpio)),
+ tegra_gpio_readl(GPIO_OUT(gpio)),
+ tegra_gpio_readl(GPIO_IN(gpio)),
+ tegra_gpio_readl(GPIO_INT_STA(gpio)),
+ tegra_gpio_readl(GPIO_INT_ENB(gpio)),
+ tegra_gpio_readl(GPIO_INT_LVL(gpio)));
+ }
+ }
+ return 0;
+}
+
+static int dbg_gpio_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dbg_gpio_show, &inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+ .open = dbg_gpio_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_gpio_debuginit(void)
+{
+ (void) debugfs_create_file("tegra_gpio", S_IRUGO,
+ NULL, NULL, &debug_fops);
+ return 0;
+}
+late_initcall(tegra_gpio_debuginit);
+#endif
diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/gpio-timberdale.c
index 0265872e57d1..c593bd46bfb6 100644
--- a/drivers/gpio/timbgpio.c
+++ b/drivers/gpio/gpio-timberdale.c
@@ -1,5 +1,5 @@
/*
- * timbgpio.c timberdale FPGA GPIO driver
+ * Timberdale FPGA GPIO driver
* Copyright (c) 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/gpio/gpio-tnetv107x.c b/drivers/gpio/gpio-tnetv107x.c
new file mode 100644
index 000000000000..3fa3e2867e19
--- /dev/null
+++ b/drivers/gpio/gpio-tnetv107x.c
@@ -0,0 +1,205 @@
+/*
+ * Texas Instruments TNETV107X GPIO Controller
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+
+#include <mach/common.h>
+#include <mach/tnetv107x.h>
+
+struct tnetv107x_gpio_regs {
+ u32 idver;
+ u32 data_in[3];
+ u32 data_out[3];
+ u32 direction[3];
+ u32 enable[3];
+};
+
+#define gpio_reg_index(gpio) ((gpio) >> 5)
+#define gpio_reg_bit(gpio) BIT((gpio) & 0x1f)
+
+#define gpio_reg_rmw(reg, mask, val) \
+ __raw_writel((__raw_readl(reg) & ~(mask)) | (val), (reg))
+
+#define gpio_reg_set_bit(reg, gpio) \
+ gpio_reg_rmw((reg) + gpio_reg_index(gpio), 0, gpio_reg_bit(gpio))
+
+#define gpio_reg_clear_bit(reg, gpio) \
+ gpio_reg_rmw((reg) + gpio_reg_index(gpio), gpio_reg_bit(gpio), 0)
+
+#define gpio_reg_get_bit(reg, gpio) \
+ (__raw_readl((reg) + gpio_reg_index(gpio)) & gpio_reg_bit(gpio))
+
+#define chip2controller(chip) \
+ container_of(chip, struct davinci_gpio_controller, chip)
+
+#define TNETV107X_GPIO_CTLRS DIV_ROUND_UP(TNETV107X_N_GPIO, 32)
+
+static struct davinci_gpio_controller chips[TNETV107X_GPIO_CTLRS];
+
+static int tnetv107x_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *ctlr = chip2controller(chip);
+ struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
+ unsigned gpio = chip->base + offset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+
+ gpio_reg_set_bit(regs->enable, gpio);
+
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+
+ return 0;
+}
+
+static void tnetv107x_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *ctlr = chip2controller(chip);
+ struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
+ unsigned gpio = chip->base + offset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+
+ gpio_reg_clear_bit(regs->enable, gpio);
+
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+}
+
+static int tnetv107x_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *ctlr = chip2controller(chip);
+ struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
+ unsigned gpio = chip->base + offset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+
+ gpio_reg_set_bit(regs->direction, gpio);
+
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+
+ return 0;
+}
+
+static int tnetv107x_gpio_dir_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct davinci_gpio_controller *ctlr = chip2controller(chip);
+ struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
+ unsigned gpio = chip->base + offset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+
+ if (value)
+ gpio_reg_set_bit(regs->data_out, gpio);
+ else
+ gpio_reg_clear_bit(regs->data_out, gpio);
+
+ gpio_reg_clear_bit(regs->direction, gpio);
+
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+
+ return 0;
+}
+
+static int tnetv107x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *ctlr = chip2controller(chip);
+ struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
+ unsigned gpio = chip->base + offset;
+ int ret;
+
+ ret = gpio_reg_get_bit(regs->data_in, gpio);
+
+ return ret ? 1 : 0;
+}
+
+static void tnetv107x_gpio_set(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct davinci_gpio_controller *ctlr = chip2controller(chip);
+ struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
+ unsigned gpio = chip->base + offset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+
+ if (value)
+ gpio_reg_set_bit(regs->data_out, gpio);
+ else
+ gpio_reg_clear_bit(regs->data_out, gpio);
+
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+}
+
+static int __init tnetv107x_gpio_setup(void)
+{
+ int i, base;
+ unsigned ngpio;
+ struct davinci_soc_info *soc_info = &davinci_soc_info;
+ struct tnetv107x_gpio_regs *regs;
+ struct davinci_gpio_controller *ctlr;
+
+ if (soc_info->gpio_type != GPIO_TYPE_TNETV107X)
+ return 0;
+
+ ngpio = soc_info->gpio_num;
+ if (ngpio == 0) {
+ pr_err("GPIO setup: how many GPIOs?\n");
+ return -EINVAL;
+ }
+
+ if (WARN_ON(TNETV107X_N_GPIO < ngpio))
+ ngpio = TNETV107X_N_GPIO;
+
+ regs = ioremap(soc_info->gpio_base, SZ_4K);
+ if (WARN_ON(!regs))
+ return -EINVAL;
+
+ for (i = 0, base = 0; base < ngpio; i++, base += 32) {
+ ctlr = &chips[i];
+
+ ctlr->chip.label = "tnetv107x";
+ ctlr->chip.can_sleep = 0;
+ ctlr->chip.base = base;
+ ctlr->chip.ngpio = ngpio - base;
+ if (ctlr->chip.ngpio > 32)
+ ctlr->chip.ngpio = 32;
+
+ ctlr->chip.request = tnetv107x_gpio_request;
+ ctlr->chip.free = tnetv107x_gpio_free;
+ ctlr->chip.direction_input = tnetv107x_gpio_dir_in;
+ ctlr->chip.get = tnetv107x_gpio_get;
+ ctlr->chip.direction_output = tnetv107x_gpio_dir_out;
+ ctlr->chip.set = tnetv107x_gpio_set;
+
+ spin_lock_init(&ctlr->lock);
+
+ ctlr->regs = regs;
+ ctlr->set_data = &regs->data_out[i];
+ ctlr->clr_data = &regs->data_out[i];
+ ctlr->in_data = &regs->data_in[i];
+
+ gpiochip_add(&ctlr->chip);
+ }
+
+ soc_info->gpio_ctlrs = chips;
+ soc_info->gpio_ctlrs_num = DIV_ROUND_UP(ngpio, 32);
+ return 0;
+}
+pure_initcall(tnetv107x_gpio_setup);
diff --git a/drivers/gpio/tps65910-gpio.c b/drivers/gpio/gpio-tps65910.c
index 15097ca616d6..b9c1c297669e 100644
--- a/drivers/gpio/tps65910-gpio.c
+++ b/drivers/gpio/gpio-tps65910.c
@@ -1,5 +1,5 @@
/*
- * tps65910-gpio.c -- TI TPS6591x
+ * TI TPS6591x GPIO driver
*
* Copyright 2010 Texas Instruments Inc.
*
diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c
new file mode 100644
index 000000000000..79e66c002350
--- /dev/null
+++ b/drivers/gpio/gpio-tps65912.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/mfd/tps65912.h>
+
+struct tps65912_gpio_data {
+ struct tps65912 *tps65912;
+ struct gpio_chip gpio_chip;
+};
+
+static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+ int val;
+
+ val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset);
+
+ if (val & GPIO_STS_MASK)
+ return 1;
+
+ return 0;
+}
+
+static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+
+ if (value)
+ tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_SET_MASK);
+ else
+ tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_SET_MASK);
+}
+
+static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+
+ /* Set the initial value */
+ tps65912_gpio_set(gc, offset, value);
+
+ return tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_CFG_MASK);
+}
+
+static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+
+ return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_CFG_MASK);
+
+}
+
+static struct gpio_chip template_chip = {
+ .label = "tps65912",
+ .owner = THIS_MODULE,
+ .direction_input = tps65912_gpio_input,
+ .direction_output = tps65912_gpio_output,
+ .get = tps65912_gpio_get,
+ .set = tps65912_gpio_set,
+ .can_sleep = 1,
+ .ngpio = 5,
+ .base = -1,
+};
+
+static int __devinit tps65912_gpio_probe(struct platform_device *pdev)
+{
+ struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
+ struct tps65912_board *pdata = tps65912->dev->platform_data;
+ struct tps65912_gpio_data *tps65912_gpio;
+ int ret;
+
+ tps65912_gpio = kzalloc(sizeof(*tps65912_gpio), GFP_KERNEL);
+ if (tps65912_gpio == NULL)
+ return -ENOMEM;
+
+ tps65912_gpio->tps65912 = tps65912;
+ tps65912_gpio->gpio_chip = template_chip;
+ tps65912_gpio->gpio_chip.dev = &pdev->dev;
+ if (pdata && pdata->gpio_base)
+ tps65912_gpio->gpio_chip.base = pdata->gpio_base;
+
+ ret = gpiochip_add(&tps65912_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, tps65912_gpio);
+
+ return ret;
+
+err:
+ kfree(tps65912_gpio);
+ return ret;
+}
+
+static int __devexit tps65912_gpio_remove(struct platform_device *pdev)
+{
+ struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = gpiochip_remove(&tps65912_gpio->gpio_chip);
+ if (ret == 0)
+ kfree(tps65912_gpio);
+
+ return ret;
+}
+
+static struct platform_driver tps65912_gpio_driver = {
+ .driver = {
+ .name = "tps65912-gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65912_gpio_probe,
+ .remove = __devexit_p(tps65912_gpio_remove),
+};
+
+static int __init tps65912_gpio_init(void)
+{
+ return platform_driver_register(&tps65912_gpio_driver);
+}
+subsys_initcall(tps65912_gpio_init);
+
+static void __exit tps65912_gpio_exit(void)
+{
+ platform_driver_unregister(&tps65912_gpio_driver);
+}
+module_exit(tps65912_gpio_exit);
+
+MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("GPIO interface for TPS65912 PMICs");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65912-gpio");
diff --git a/drivers/gpio/twl4030-gpio.c b/drivers/gpio/gpio-twl4030.c
index 57635ac35a73..b8b4f228757c 100644
--- a/drivers/gpio/twl4030-gpio.c
+++ b/drivers/gpio/gpio-twl4030.c
@@ -1,5 +1,5 @@
/*
- * twl4030_gpio.c -- access to GPIOs on TWL4030/TPS659x0 chips
+ * Access to GPIOs on TWL4030/TPS659x0 chips
*
* Copyright (C) 2006-2007 Texas Instruments, Inc.
* Copyright (C) 2006 MontaVista Software, Inc.
diff --git a/drivers/gpio/gpio-u300.c b/drivers/gpio/gpio-u300.c
index d92790140fe5..4035778852b0 100644
--- a/drivers/gpio/gpio-u300.c
+++ b/drivers/gpio/gpio-u300.c
@@ -1,21 +1,17 @@
/*
+ * U300 GPIO module.
*
- * arch/arm/mach-u300/gpio.c
- *
- *
- * Copyright (C) 2007-2009 ST-Ericsson AB
+ * Copyright (C) 2007-2011 ST-Ericsson AB
* License terms: GNU General Public License (GPL) version 2
- * U300 GPIO module.
* This can driver either of the two basic GPIO cores
* available in the U300 platforms:
* COH 901 335 - Used in DB3150 (U300 1.0) and DB3200 (U330 1.0)
* COH 901 571/3 - Used in DB3210 (U365 2.0) and DB3350 (U335 1.0)
- * Notice that you also have inline macros in <asm-arch/gpio.h>
- * Author: Linus Walleij <linus.walleij@stericsson.com>
+ * Author: Linus Walleij <linus.walleij@linaro.org>
* Author: Jonas Aaberg <jonas.aberg@stericsson.com>
- *
*/
#include <linux/module.h>
+#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
@@ -24,677 +20,898 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <mach/gpio-u300.h>
+
+/*
+ * Bias modes for U300 GPIOs
+ *
+ * GPIO_U300_CONFIG_BIAS_UNKNOWN: this bias mode is not known to us
+ * GPIO_U300_CONFIG_BIAS_FLOAT: no specific bias, the GPIO will float or state
+ * is not controlled by software
+ * GPIO_U300_CONFIG_BIAS_PULL_UP: the GPIO will be pulled up (usually with high
+ * impedance to VDD)
+ */
+#define GPIO_U300_CONFIG_BIAS_UNKNOWN 0x1000
+#define GPIO_U300_CONFIG_BIAS_FLOAT 0x1001
+#define GPIO_U300_CONFIG_BIAS_PULL_UP 0x1002
+
+/*
+ * Drive modes for U300 GPIOs (output)
+ *
+ * GPIO_U300_CONFIG_DRIVE_PUSH_PULL: the GPIO will be driven actively high and
+ * low, this is the most typical case and is typically achieved with two
+ * active transistors on the output
+ * GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN: the GPIO will be driven with open drain
+ * (open collector) which means it is usually wired with other output
+ * ports which are then pulled up with an external resistor
+ * GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE: the GPIO will be driven with open drain
+ * (open emitter) which is the same as open drain mutatis mutandis but
+ * pulled to ground
+ */
+#define GPIO_U300_CONFIG_DRIVE_PUSH_PULL 0x2000
+#define GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN 0x2001
+#define GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE 0x2002
-/* Reference to GPIO block clock */
-static struct clk *clk;
+/*
+ * Register definitions for COH 901 335 variant
+ */
+#define U300_335_PORT_STRIDE (0x1C)
+/* Port X Pin Data Register 32bit, this is both input and output (R/W) */
+#define U300_335_PXPDIR (0x00)
+#define U300_335_PXPDOR (0x00)
+/* Port X Pin Config Register 32bit (R/W) */
+#define U300_335_PXPCR (0x04)
+/* This register layout is the same in both blocks */
+#define U300_GPIO_PXPCR_ALL_PINS_MODE_MASK (0x0000FFFFUL)
+#define U300_GPIO_PXPCR_PIN_MODE_MASK (0x00000003UL)
+#define U300_GPIO_PXPCR_PIN_MODE_SHIFT (0x00000002UL)
+#define U300_GPIO_PXPCR_PIN_MODE_INPUT (0x00000000UL)
+#define U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL (0x00000001UL)
+#define U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_DRAIN (0x00000002UL)
+#define U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_SOURCE (0x00000003UL)
+/* Port X Interrupt Event Register 32bit (R/W) */
+#define U300_335_PXIEV (0x08)
+/* Port X Interrupt Enable Register 32bit (R/W) */
+#define U300_335_PXIEN (0x0C)
+/* Port X Interrupt Force Register 32bit (R/W) */
+#define U300_335_PXIFR (0x10)
+/* Port X Interrupt Config Register 32bit (R/W) */
+#define U300_335_PXICR (0x14)
+/* This register layout is the same in both blocks */
+#define U300_GPIO_PXICR_ALL_IRQ_CONFIG_MASK (0x000000FFUL)
+#define U300_GPIO_PXICR_IRQ_CONFIG_MASK (0x00000001UL)
+#define U300_GPIO_PXICR_IRQ_CONFIG_FALLING_EDGE (0x00000000UL)
+#define U300_GPIO_PXICR_IRQ_CONFIG_RISING_EDGE (0x00000001UL)
+/* Port X Pull-up Enable Register 32bit (R/W) */
+#define U300_335_PXPER (0x18)
+/* This register layout is the same in both blocks */
+#define U300_GPIO_PXPER_ALL_PULL_UP_DISABLE_MASK (0x000000FFUL)
+#define U300_GPIO_PXPER_PULL_UP_DISABLE (0x00000001UL)
+/* Control Register 32bit (R/W) */
+#define U300_335_CR (0x54)
+#define U300_335_CR_BLOCK_CLOCK_ENABLE (0x00000001UL)
-/* Memory resource */
-static struct resource *memres;
-static void __iomem *virtbase;
-static struct device *gpiodev;
+/*
+ * Register definitions for COH 901 571 / 3 variant
+ */
+#define U300_571_PORT_STRIDE (0x30)
+/*
+ * Control Register 32bit (R/W)
+ * bit 15-9 (mask 0x0000FE00) contains the number of cores. 8*cores
+ * gives the number of GPIO pins.
+ * bit 8-2 (mask 0x000001FC) contains the core version ID.
+ */
+#define U300_571_CR (0x00)
+#define U300_571_CR_SYNC_SEL_ENABLE (0x00000002UL)
+#define U300_571_CR_BLOCK_CLKRQ_ENABLE (0x00000001UL)
+/*
+ * These registers have the same layout and function as the corresponding
+ * COH 901 335 registers, just at different offset.
+ */
+#define U300_571_PXPDIR (0x04)
+#define U300_571_PXPDOR (0x08)
+#define U300_571_PXPCR (0x0C)
+#define U300_571_PXPER (0x10)
+#define U300_571_PXIEV (0x14)
+#define U300_571_PXIEN (0x18)
+#define U300_571_PXIFR (0x1C)
+#define U300_571_PXICR (0x20)
+
+/* 8 bits per port, no version has more than 7 ports */
+#define U300_GPIO_PINS_PER_PORT 8
+#define U300_GPIO_MAX (U300_GPIO_PINS_PER_PORT * 7)
+
+struct u300_gpio {
+ struct gpio_chip chip;
+ struct list_head port_list;
+ struct clk *clk;
+ struct resource *memres;
+ void __iomem *base;
+ struct device *dev;
+ int irq_base;
+ u32 stride;
+ /* Register offsets */
+ u32 pcr;
+ u32 dor;
+ u32 dir;
+ u32 per;
+ u32 icr;
+ u32 ien;
+ u32 iev;
+};
struct u300_gpio_port {
- const char *name;
+ struct list_head node;
+ struct u300_gpio *gpio;
+ char name[8];
int irq;
int number;
+ u8 toggle_edge_mode;
};
+/*
+ * Macro to expand to read a specific register found in the "gpio"
+ * struct. It requires the struct u300_gpio *gpio variable to exist in
+ * its context. It calculates the port offset from the given pin
+ * offset, muliplies by the port stride and adds the register offset
+ * so it provides a pointer to the desired register.
+ */
+#define U300_PIN_REG(pin, reg) \
+ (gpio->base + (pin >> 3) * gpio->stride + gpio->reg)
-static struct u300_gpio_port gpio_ports[] = {
- {
- .name = "gpio0",
- .number = 0,
- },
- {
- .name = "gpio1",
- .number = 1,
- },
- {
- .name = "gpio2",
- .number = 2,
- },
-#ifdef U300_COH901571_3
- {
- .name = "gpio3",
- .number = 3,
- },
- {
- .name = "gpio4",
- .number = 4,
- },
-#ifdef CONFIG_MACH_U300_BS335
- {
- .name = "gpio5",
- .number = 5,
- },
- {
- .name = "gpio6",
- .number = 6,
- },
-#endif
-#endif
+/*
+ * Provides a bitmask for a specific gpio pin inside an 8-bit GPIO
+ * register.
+ */
+#define U300_PIN_BIT(pin) \
+ (1 << (pin & 0x07))
+struct u300_gpio_confdata {
+ u16 bias_mode;
+ bool output;
+ int outval;
};
+/* BS335 has seven ports of 8 bits each = GPIO pins 0..55 */
+#define BS335_GPIO_NUM_PORTS 7
+/* BS365 has five ports of 8 bits each = GPIO pins 0..39 */
+#define BS365_GPIO_NUM_PORTS 5
-#ifdef U300_COH901571_3
+#define U300_FLOATING_INPUT { \
+ .bias_mode = GPIO_U300_CONFIG_BIAS_FLOAT, \
+ .output = false, \
+}
-/* Default input value */
-#define DEFAULT_OUTPUT_LOW 0
-#define DEFAULT_OUTPUT_HIGH 1
+#define U300_PULL_UP_INPUT { \
+ .bias_mode = GPIO_U300_CONFIG_BIAS_PULL_UP, \
+ .output = false, \
+}
-/* GPIO Pull-Up status */
-#define DISABLE_PULL_UP 0
-#define ENABLE_PULL_UP 1
+#define U300_OUTPUT_LOW { \
+ .output = true, \
+ .outval = 0, \
+}
-#define GPIO_NOT_USED 0
-#define GPIO_IN 1
-#define GPIO_OUT 2
+#define U300_OUTPUT_HIGH { \
+ .output = true, \
+ .outval = 1, \
+}
-struct u300_gpio_configuration_data {
- unsigned char pin_usage;
- unsigned char default_output_value;
- unsigned char pull_up;
-};
/* Initial configuration */
-const struct u300_gpio_configuration_data
-u300_gpio_config[U300_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = {
-#ifdef CONFIG_MACH_U300_BS335
+static const struct __initdata u300_gpio_confdata
+bs335_gpio_config[BS335_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = {
/* Port 0, pins 0-7 */
{
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_HIGH, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}
+ U300_FLOATING_INPUT,
+ U300_OUTPUT_HIGH,
+ U300_FLOATING_INPUT,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
},
/* Port 1, pins 0-7 */
{
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_HIGH, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
+ U300_PULL_UP_INPUT,
+ U300_FLOATING_INPUT,
+ U300_OUTPUT_HIGH,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
},
/* Port 2, pins 0-7 */
{
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_OUTPUT_LOW,
+ U300_PULL_UP_INPUT,
+ U300_OUTPUT_LOW,
+ U300_PULL_UP_INPUT,
},
/* Port 3, pins 0-7 */
{
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}
+ U300_PULL_UP_INPUT,
+ U300_OUTPUT_LOW,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
},
/* Port 4, pins 0-7 */
{
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
},
/* Port 5, pins 0-7 */
{
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
},
/* Port 6, pind 0-7 */
{
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
}
-#endif
+};
-#ifdef CONFIG_MACH_U300_BS365
+static const struct __initdata u300_gpio_confdata
+bs365_gpio_config[BS365_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = {
/* Port 0, pins 0-7 */
{
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}
+ U300_FLOATING_INPUT,
+ U300_OUTPUT_LOW,
+ U300_FLOATING_INPUT,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
+ U300_PULL_UP_INPUT,
+ U300_FLOATING_INPUT,
},
/* Port 1, pins 0-7 */
{
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_HIGH, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}
+ U300_OUTPUT_LOW,
+ U300_FLOATING_INPUT,
+ U300_OUTPUT_LOW,
+ U300_FLOATING_INPUT,
+ U300_FLOATING_INPUT,
+ U300_OUTPUT_HIGH,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
},
/* Port 2, pins 0-7 */
{
- {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}
+ U300_FLOATING_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
},
/* Port 3, pins 0-7 */
{
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
},
/* Port 4, pins 0-7 */
{
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
+ U300_PULL_UP_INPUT,
/* These 4 pins doesn't exist on DB3210 */
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP},
- {GPIO_OUT, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
+ U300_OUTPUT_LOW,
}
-#endif
};
-#endif
-
-/* No users == we can power down GPIO */
-static int gpio_users;
-
-struct gpio_struct {
- int (*callback)(void *);
- void *data;
- int users;
-};
-
-static struct gpio_struct gpio_pin[U300_GPIO_MAX];
-
-/*
- * Let drivers register callback in order to get notified when there is
- * an interrupt on the gpio pin
+/**
+ * to_u300_gpio() - get the pointer to u300_gpio
+ * @chip: the gpio chip member of the structure u300_gpio
*/
-int gpio_register_callback(unsigned gpio, int (*func)(void *arg), void *data)
+static inline struct u300_gpio *to_u300_gpio(struct gpio_chip *chip)
{
- if (gpio_pin[gpio].callback)
- dev_warn(gpiodev, "%s: WARNING: callback already "
- "registered for gpio pin#%d\n", __func__, gpio);
- gpio_pin[gpio].callback = func;
- gpio_pin[gpio].data = data;
-
- return 0;
+ return container_of(chip, struct u300_gpio, chip);
}
-EXPORT_SYMBOL(gpio_register_callback);
-int gpio_unregister_callback(unsigned gpio)
+static int u300_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- if (!gpio_pin[gpio].callback)
- dev_warn(gpiodev, "%s: WARNING: callback already "
- "unregistered for gpio pin#%d\n", __func__, gpio);
- gpio_pin[gpio].callback = NULL;
- gpio_pin[gpio].data = NULL;
+ struct u300_gpio *gpio = to_u300_gpio(chip);
- return 0;
+ return readl(U300_PIN_REG(offset, dir)) & U300_PIN_BIT(offset);
}
-EXPORT_SYMBOL(gpio_unregister_callback);
-/* Non-zero means valid */
-int gpio_is_valid(int number)
+static void u300_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
- if (number >= 0 &&
- number < (U300_GPIO_NUM_PORTS * U300_GPIO_PINS_PER_PORT))
- return 1;
- return 0;
-}
-EXPORT_SYMBOL(gpio_is_valid);
+ struct u300_gpio *gpio = to_u300_gpio(chip);
+ unsigned long flags;
+ u32 val;
-int gpio_request(unsigned gpio, const char *label)
-{
- if (gpio_pin[gpio].users)
- return -EINVAL;
- else
- gpio_pin[gpio].users++;
+ local_irq_save(flags);
- gpio_users++;
+ val = readl(U300_PIN_REG(offset, dor));
+ if (value)
+ writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, dor));
+ else
+ writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, dor));
- return 0;
+ local_irq_restore(flags);
}
-EXPORT_SYMBOL(gpio_request);
-void gpio_free(unsigned gpio)
+static int u300_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
- gpio_users--;
- gpio_pin[gpio].users--;
- if (unlikely(gpio_pin[gpio].users < 0)) {
- dev_warn(gpiodev, "warning: gpio#%d release mismatch\n",
- gpio);
- gpio_pin[gpio].users = 0;
- }
+ struct u300_gpio *gpio = to_u300_gpio(chip);
+ unsigned long flags;
+ u32 val;
- return;
+ local_irq_save(flags);
+ val = readl(U300_PIN_REG(offset, pcr));
+ /* Mask out this pin, note 2 bits per setting */
+ val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((offset & 0x07) << 1));
+ writel(val, U300_PIN_REG(offset, pcr));
+ local_irq_restore(flags);
+ return 0;
}
-EXPORT_SYMBOL(gpio_free);
-/* This returns zero or nonzero */
-int gpio_get_value(unsigned gpio)
+static int u300_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
{
- return readl(virtbase + U300_GPIO_PXPDIR +
- PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING) & (1 << (gpio & 0x07));
-}
-EXPORT_SYMBOL(gpio_get_value);
-
-/*
- * We hope that the compiler will optimize away the unused branch
- * in case "value" is a constant
- */
-void gpio_set_value(unsigned gpio, int value)
-{
- u32 val;
+ struct u300_gpio *gpio = to_u300_gpio(chip);
unsigned long flags;
+ u32 oldmode;
+ u32 val;
local_irq_save(flags);
- if (value) {
- /* set */
- val = readl(virtbase + U300_GPIO_PXPDOR +
- PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING)
- & (1 << (gpio & 0x07));
- writel(val | (1 << (gpio & 0x07)), virtbase +
- U300_GPIO_PXPDOR +
- PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING);
- } else {
- /* clear */
- val = readl(virtbase + U300_GPIO_PXPDOR +
- PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING)
- & (1 << (gpio & 0x07));
- writel(val & ~(1 << (gpio & 0x07)), virtbase +
- U300_GPIO_PXPDOR +
- PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING);
+ val = readl(U300_PIN_REG(offset, pcr));
+ /*
+ * Drive mode must be set by the special mode set function, set
+ * push/pull mode by default if no mode has been selected.
+ */
+ oldmode = val & (U300_GPIO_PXPCR_PIN_MODE_MASK <<
+ ((offset & 0x07) << 1));
+ /* mode = 0 means input, else some mode is already set */
+ if (oldmode == 0) {
+ val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK <<
+ ((offset & 0x07) << 1));
+ val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL
+ << ((offset & 0x07) << 1));
+ writel(val, U300_PIN_REG(offset, pcr));
}
+ u300_gpio_set(chip, offset, value);
local_irq_restore(flags);
+ return 0;
+}
+
+static int u300_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct u300_gpio *gpio = to_u300_gpio(chip);
+ int retirq = gpio->irq_base + offset;
+
+ dev_dbg(gpio->dev, "request IRQ for GPIO %d, return %d\n", offset,
+ retirq);
+ return retirq;
}
-EXPORT_SYMBOL(gpio_set_value);
-int gpio_direction_input(unsigned gpio)
+static int u300_gpio_config(struct gpio_chip *chip, unsigned offset,
+ u16 param, unsigned long *data)
{
+ struct u300_gpio *gpio = to_u300_gpio(chip);
unsigned long flags;
u32 val;
- if (gpio > U300_GPIO_MAX)
- return -EINVAL;
-
local_irq_save(flags);
- val = readl(virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
- /* Mask out this pin*/
- val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((gpio & 0x07) << 1));
- /* This is not needed since it sets the bits to zero.*/
- /* val |= (U300_GPIO_PXPCR_PIN_MODE_INPUT << (gpio*2)); */
- writel(val, virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
+ switch (param) {
+ case GPIO_U300_CONFIG_BIAS_UNKNOWN:
+ case GPIO_U300_CONFIG_BIAS_FLOAT:
+ val = readl(U300_PIN_REG(offset, per));
+ writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, per));
+ break;
+ case GPIO_U300_CONFIG_BIAS_PULL_UP:
+ val = readl(U300_PIN_REG(offset, per));
+ writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, per));
+ break;
+ case GPIO_U300_CONFIG_DRIVE_PUSH_PULL:
+ val = readl(U300_PIN_REG(offset, pcr));
+ val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK
+ << ((offset & 0x07) << 1));
+ val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL
+ << ((offset & 0x07) << 1));
+ writel(val, U300_PIN_REG(offset, pcr));
+ break;
+ case GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN:
+ val = readl(U300_PIN_REG(offset, pcr));
+ val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK
+ << ((offset & 0x07) << 1));
+ val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_DRAIN
+ << ((offset & 0x07) << 1));
+ writel(val, U300_PIN_REG(offset, pcr));
+ break;
+ case GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE:
+ val = readl(U300_PIN_REG(offset, pcr));
+ val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK
+ << ((offset & 0x07) << 1));
+ val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_SOURCE
+ << ((offset & 0x07) << 1));
+ writel(val, U300_PIN_REG(offset, pcr));
+ break;
+ default:
+ local_irq_restore(flags);
+ dev_err(gpio->dev, "illegal configuration requested\n");
+ return -EINVAL;
+ }
local_irq_restore(flags);
return 0;
}
-EXPORT_SYMBOL(gpio_direction_input);
-int gpio_direction_output(unsigned gpio, int value)
+static struct gpio_chip u300_gpio_chip = {
+ .label = "u300-gpio-chip",
+ .owner = THIS_MODULE,
+ .get = u300_gpio_get,
+ .set = u300_gpio_set,
+ .direction_input = u300_gpio_direction_input,
+ .direction_output = u300_gpio_direction_output,
+ .to_irq = u300_gpio_to_irq,
+};
+
+static void u300_toggle_trigger(struct u300_gpio *gpio, unsigned offset)
{
- unsigned long flags;
u32 val;
- if (gpio > U300_GPIO_MAX)
- return -EINVAL;
-
- local_irq_save(flags);
- val = readl(virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
- /* Mask out this pin */
- val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((gpio & 0x07) << 1));
- /*
- * FIXME: configure for push/pull, open drain or open source per pin
- * in setup. The current driver will only support push/pull.
- */
- val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL
- << ((gpio & 0x07) << 1));
- writel(val, virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
- gpio_set_value(gpio, value);
- local_irq_restore(flags);
- return 0;
+ val = readl(U300_PIN_REG(offset, icr));
+ /* Set mode depending on state */
+ if (u300_gpio_get(&gpio->chip, offset)) {
+ /* High now, let's trigger on falling edge next then */
+ writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, icr));
+ dev_dbg(gpio->dev, "next IRQ on falling edge on pin %d\n",
+ offset);
+ } else {
+ /* Low now, let's trigger on rising edge next then */
+ writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, icr));
+ dev_dbg(gpio->dev, "next IRQ on rising edge on pin %d\n",
+ offset);
+ }
}
-EXPORT_SYMBOL(gpio_direction_output);
-/*
- * Enable an IRQ, edge is rising edge (!= 0) or falling edge (==0).
- */
-void enable_irq_on_gpio_pin(unsigned gpio, int edge)
+static int u300_gpio_irq_type(struct irq_data *d, unsigned trigger)
{
+ struct u300_gpio_port *port = irq_data_get_irq_chip_data(d);
+ struct u300_gpio *gpio = port->gpio;
+ int offset = d->irq - gpio->irq_base;
u32 val;
- unsigned long flags;
- local_irq_save(flags);
- val = readl(virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
- val |= (1 << (gpio & 0x07));
- writel(val, virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
- val = readl(virtbase + U300_GPIO_PXICR + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
- if (edge)
- val |= (1 << (gpio & 0x07));
- else
- val &= ~(1 << (gpio & 0x07));
- writel(val, virtbase + U300_GPIO_PXICR + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
- local_irq_restore(flags);
+ if ((trigger & IRQF_TRIGGER_RISING) &&
+ (trigger & IRQF_TRIGGER_FALLING)) {
+ /*
+ * The GPIO block can only trigger on falling OR rising edges,
+ * not both. So we need to toggle the mode whenever the pin
+ * goes from one state to the other with a special state flag
+ */
+ dev_dbg(gpio->dev,
+ "trigger on both rising and falling edge on pin %d\n",
+ offset);
+ port->toggle_edge_mode |= U300_PIN_BIT(offset);
+ u300_toggle_trigger(gpio, offset);
+ } else if (trigger & IRQF_TRIGGER_RISING) {
+ dev_dbg(gpio->dev, "trigger on rising edge on pin %d\n",
+ offset);
+ val = readl(U300_PIN_REG(offset, icr));
+ writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, icr));
+ port->toggle_edge_mode &= ~U300_PIN_BIT(offset);
+ } else if (trigger & IRQF_TRIGGER_FALLING) {
+ dev_dbg(gpio->dev, "trigger on falling edge on pin %d\n",
+ offset);
+ val = readl(U300_PIN_REG(offset, icr));
+ writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, icr));
+ port->toggle_edge_mode &= ~U300_PIN_BIT(offset);
+ }
+
+ return 0;
}
-EXPORT_SYMBOL(enable_irq_on_gpio_pin);
-void disable_irq_on_gpio_pin(unsigned gpio)
+static void u300_gpio_irq_enable(struct irq_data *d)
{
+ struct u300_gpio_port *port = irq_data_get_irq_chip_data(d);
+ struct u300_gpio *gpio = port->gpio;
+ int offset = d->irq - gpio->irq_base;
u32 val;
unsigned long flags;
local_irq_save(flags);
- val = readl(virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
- val &= ~(1 << (gpio & 0x07));
- writel(val, virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
+ val = readl(U300_PIN_REG(offset, ien));
+ writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, ien));
local_irq_restore(flags);
}
-EXPORT_SYMBOL(disable_irq_on_gpio_pin);
-/* Enable (value == 0) or disable (value == 1) internal pullup */
-void gpio_pullup(unsigned gpio, int value)
+static void u300_gpio_irq_disable(struct irq_data *d)
{
+ struct u300_gpio_port *port = irq_data_get_irq_chip_data(d);
+ struct u300_gpio *gpio = port->gpio;
+ int offset = d->irq - gpio->irq_base;
u32 val;
unsigned long flags;
local_irq_save(flags);
- if (value) {
- val = readl(virtbase + U300_GPIO_PXPER + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
- writel(val | (1 << (gpio & 0x07)), virtbase + U300_GPIO_PXPER +
- PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING);
- } else {
- val = readl(virtbase + U300_GPIO_PXPER + PIN_TO_PORT(gpio) *
- U300_GPIO_PORTX_SPACING);
- writel(val & ~(1 << (gpio & 0x07)), virtbase + U300_GPIO_PXPER +
- PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING);
- }
+ val = readl(U300_PIN_REG(offset, ien));
+ writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, ien));
local_irq_restore(flags);
}
-EXPORT_SYMBOL(gpio_pullup);
-static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
+static struct irq_chip u300_gpio_irqchip = {
+ .name = "u300-gpio-irqchip",
+ .irq_enable = u300_gpio_irq_enable,
+ .irq_disable = u300_gpio_irq_disable,
+ .irq_set_type = u300_gpio_irq_type,
+
+};
+
+static void u300_gpio_irq_handler(unsigned irq, struct irq_desc *desc)
{
- struct u300_gpio_port *port = dev_id;
- u32 val;
- int pin;
+ struct u300_gpio_port *port = irq_get_handler_data(irq);
+ struct u300_gpio *gpio = port->gpio;
+ int pinoffset = port->number << 3; /* get the right stride */
+ unsigned long val;
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
/* Read event register */
- val = readl(virtbase + U300_GPIO_PXIEV + port->number *
- U300_GPIO_PORTX_SPACING);
- /* Mask with enable register */
- val &= readl(virtbase + U300_GPIO_PXIEV + port->number *
- U300_GPIO_PORTX_SPACING);
+ val = readl(U300_PIN_REG(pinoffset, iev));
/* Mask relevant bits */
- val &= U300_GPIO_PXIEV_ALL_IRQ_EVENT_MASK;
+ val &= 0xFFU; /* 8 bits per port */
/* ACK IRQ (clear event) */
- writel(val, virtbase + U300_GPIO_PXIEV + port->number *
- U300_GPIO_PORTX_SPACING);
- /* Print message */
- while (val != 0) {
- unsigned gpio;
-
- pin = __ffs(val);
- /* mask off this pin */
- val &= ~(1 << pin);
- gpio = (port->number << 3) + pin;
-
- if (gpio_pin[gpio].callback)
- (void)gpio_pin[gpio].callback(gpio_pin[gpio].data);
- else
- dev_dbg(gpiodev, "stray GPIO IRQ on line %d\n",
- gpio);
+ writel(val, U300_PIN_REG(pinoffset, iev));
+
+ /* Call IRQ handler */
+ if (val != 0) {
+ int irqoffset;
+
+ for_each_set_bit(irqoffset, &val, U300_GPIO_PINS_PER_PORT) {
+ int pin_irq = gpio->irq_base + (port->number << 3)
+ + irqoffset;
+ int offset = pinoffset + irqoffset;
+
+ dev_dbg(gpio->dev, "GPIO IRQ %d on pin %d\n",
+ pin_irq, offset);
+ generic_handle_irq(pin_irq);
+ /*
+ * Triggering IRQ on both rising and falling edge
+ * needs mockery
+ */
+ if (port->toggle_edge_mode & U300_PIN_BIT(offset))
+ u300_toggle_trigger(gpio, offset);
+ }
}
- return IRQ_HANDLED;
+
+ desc->irq_data.chip->irq_unmask(&desc->irq_data);
}
-static void gpio_set_initial_values(void)
+static void __init u300_gpio_init_pin(struct u300_gpio *gpio,
+ int offset,
+ const struct u300_gpio_confdata *conf)
{
-#ifdef U300_COH901571_3
- int i, j;
- unsigned long flags;
- u32 val;
+ /* Set mode: input or output */
+ if (conf->output) {
+ u300_gpio_direction_output(&gpio->chip, offset, conf->outval);
- /* Write default values to all pins */
- for (i = 0; i < U300_GPIO_NUM_PORTS; i++) {
- val = 0;
- for (j = 0; j < 8; j++)
- val |= (u32) (u300_gpio_config[i][j].default_output_value != DEFAULT_OUTPUT_LOW) << j;
- local_irq_save(flags);
- writel(val, virtbase + U300_GPIO_PXPDOR + i * U300_GPIO_PORTX_SPACING);
- local_irq_restore(flags);
+ /* Deactivate bias mode for output */
+ u300_gpio_config(&gpio->chip, offset,
+ GPIO_U300_CONFIG_BIAS_FLOAT,
+ NULL);
+
+ /* Set drive mode for output */
+ u300_gpio_config(&gpio->chip, offset,
+ GPIO_U300_CONFIG_DRIVE_PUSH_PULL, NULL);
+
+ dev_dbg(gpio->dev, "set up pin %d as output, value: %d\n",
+ offset, conf->outval);
+ } else {
+ u300_gpio_direction_input(&gpio->chip, offset);
+
+ /* Always set output low on input pins */
+ u300_gpio_set(&gpio->chip, offset, 0);
+
+ /* Set bias mode for input */
+ u300_gpio_config(&gpio->chip, offset, conf->bias_mode, NULL);
+
+ dev_dbg(gpio->dev, "set up pin %d as input, bias: %04x\n",
+ offset, conf->bias_mode);
}
+}
- /*
- * Put all pins that are set to either 'GPIO_OUT' or 'GPIO_NOT_USED'
- * to output and 'GPIO_IN' to input for each port. And initialize
- * default value on outputs.
- */
- for (i = 0; i < U300_GPIO_NUM_PORTS; i++) {
- for (j = 0; j < U300_GPIO_PINS_PER_PORT; j++) {
- local_irq_save(flags);
- val = readl(virtbase + U300_GPIO_PXPCR +
- i * U300_GPIO_PORTX_SPACING);
- /* Mask out this pin */
- val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << (j << 1));
-
- if (u300_gpio_config[i][j].pin_usage != GPIO_IN)
- val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL << (j << 1));
- writel(val, virtbase + U300_GPIO_PXPCR +
- i * U300_GPIO_PORTX_SPACING);
- local_irq_restore(flags);
+static void __init u300_gpio_init_coh901571(struct u300_gpio *gpio,
+ struct u300_gpio_platform *plat)
+{
+ int i, j;
+
+ /* Write default config and values to all pins */
+ for (i = 0; i < plat->ports; i++) {
+ for (j = 0; j < 8; j++) {
+ const struct u300_gpio_confdata *conf;
+ int offset = (i*8) + j;
+
+ if (plat->variant == U300_GPIO_COH901571_3_BS335)
+ conf = &bs335_gpio_config[i][j];
+ else if (plat->variant == U300_GPIO_COH901571_3_BS365)
+ conf = &bs365_gpio_config[i][j];
+ else
+ break;
+
+ u300_gpio_init_pin(gpio, offset, conf);
}
}
+}
- /* Enable or disable the internal pull-ups in the GPIO ASIC block */
- for (i = 0; i < U300_GPIO_MAX; i++) {
- val = 0;
- for (j = 0; j < 8; j++)
- val |= (u32)((u300_gpio_config[i][j].pull_up == DISABLE_PULL_UP) << j);
- local_irq_save(flags);
- writel(val, virtbase + U300_GPIO_PXPER + i * U300_GPIO_PORTX_SPACING);
- local_irq_restore(flags);
+static inline void u300_gpio_free_ports(struct u300_gpio *gpio)
+{
+ struct u300_gpio_port *port;
+ struct list_head *p, *n;
+
+ list_for_each_safe(p, n, &gpio->port_list) {
+ port = list_entry(p, struct u300_gpio_port, node);
+ list_del(&port->node);
+ free_irq(port->irq, port);
+ kfree(port);
}
-#endif
}
-static int __init gpio_probe(struct platform_device *pdev)
+static int __init u300_gpio_probe(struct platform_device *pdev)
{
- u32 val;
+ struct u300_gpio_platform *plat = dev_get_platdata(&pdev->dev);
+ struct u300_gpio *gpio;
int err = 0;
+ int portno;
+ u32 val;
+ u32 ifr;
int i;
- int num_irqs;
- gpiodev = &pdev->dev;
- memset(gpio_pin, 0, sizeof(gpio_pin));
+ gpio = kzalloc(sizeof(struct u300_gpio), GFP_KERNEL);
+ if (gpio == NULL) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ gpio->chip = u300_gpio_chip;
+ gpio->chip.ngpio = plat->ports * U300_GPIO_PINS_PER_PORT;
+ gpio->irq_base = plat->gpio_irq_base;
+ gpio->chip.dev = &pdev->dev;
+ gpio->chip.base = plat->gpio_base;
+ gpio->dev = &pdev->dev;
/* Get GPIO clock */
- clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- err = PTR_ERR(clk);
- dev_err(gpiodev, "could not get GPIO clock\n");
+ gpio->clk = clk_get(gpio->dev, NULL);
+ if (IS_ERR(gpio->clk)) {
+ err = PTR_ERR(gpio->clk);
+ dev_err(gpio->dev, "could not get GPIO clock\n");
goto err_no_clk;
}
- err = clk_enable(clk);
+ err = clk_enable(gpio->clk);
if (err) {
- dev_err(gpiodev, "could not enable GPIO clock\n");
+ dev_err(gpio->dev, "could not enable GPIO clock\n");
goto err_no_clk_enable;
}
- memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!memres)
+ gpio->memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!gpio->memres) {
+ dev_err(gpio->dev, "could not get GPIO memory resource\n");
+ err = -ENODEV;
goto err_no_resource;
+ }
- if (request_mem_region(memres->start, memres->end - memres->start, "GPIO Controller")
- == NULL) {
+ if (!request_mem_region(gpio->memres->start,
+ resource_size(gpio->memres),
+ "GPIO Controller")) {
err = -ENODEV;
goto err_no_ioregion;
}
- virtbase = ioremap(memres->start, resource_size(memres));
- if (!virtbase) {
+ gpio->base = ioremap(gpio->memres->start, resource_size(gpio->memres));
+ if (!gpio->base) {
err = -ENOMEM;
goto err_no_ioremap;
}
- dev_info(gpiodev, "remapped 0x%08x to %p\n",
- memres->start, virtbase);
-
-#ifdef U300_COH901335
- dev_info(gpiodev, "initializing GPIO Controller COH 901 335\n");
- /* Turn on the GPIO block */
- writel(U300_GPIO_CR_BLOCK_CLOCK_ENABLE, virtbase + U300_GPIO_CR);
-#endif
-
-#ifdef U300_COH901571_3
- dev_info(gpiodev, "initializing GPIO Controller COH 901 571/3\n");
- val = readl(virtbase + U300_GPIO_CR);
- dev_info(gpiodev, "COH901571/3 block version: %d, " \
- "number of cores: %d\n",
- ((val & 0x0000FE00) >> 9),
- ((val & 0x000001FC) >> 2));
- writel(U300_GPIO_CR_BLOCK_CLKRQ_ENABLE, virtbase + U300_GPIO_CR);
-#endif
-
- gpio_set_initial_values();
-
- for (num_irqs = 0 ; num_irqs < U300_GPIO_NUM_PORTS; num_irqs++) {
-
- gpio_ports[num_irqs].irq =
- platform_get_irq_byname(pdev,
- gpio_ports[num_irqs].name);
-
- err = request_irq(gpio_ports[num_irqs].irq,
- gpio_irq_handler, IRQF_DISABLED,
- gpio_ports[num_irqs].name,
- &gpio_ports[num_irqs]);
- if (err) {
- dev_err(gpiodev, "cannot allocate IRQ for %s!\n",
- gpio_ports[num_irqs].name);
- goto err_no_irq;
+
+ if (plat->variant == U300_GPIO_COH901335) {
+ dev_info(gpio->dev,
+ "initializing GPIO Controller COH 901 335\n");
+ gpio->stride = U300_335_PORT_STRIDE;
+ gpio->pcr = U300_335_PXPCR;
+ gpio->dor = U300_335_PXPDOR;
+ gpio->dir = U300_335_PXPDIR;
+ gpio->per = U300_335_PXPER;
+ gpio->icr = U300_335_PXICR;
+ gpio->ien = U300_335_PXIEN;
+ gpio->iev = U300_335_PXIEV;
+ ifr = U300_335_PXIFR;
+
+ /* Turn on the GPIO block */
+ writel(U300_335_CR_BLOCK_CLOCK_ENABLE,
+ gpio->base + U300_335_CR);
+ } else if (plat->variant == U300_GPIO_COH901571_3_BS335 ||
+ plat->variant == U300_GPIO_COH901571_3_BS365) {
+ dev_info(gpio->dev,
+ "initializing GPIO Controller COH 901 571/3\n");
+ gpio->stride = U300_571_PORT_STRIDE;
+ gpio->pcr = U300_571_PXPCR;
+ gpio->dor = U300_571_PXPDOR;
+ gpio->dir = U300_571_PXPDIR;
+ gpio->per = U300_571_PXPER;
+ gpio->icr = U300_571_PXICR;
+ gpio->ien = U300_571_PXIEN;
+ gpio->iev = U300_571_PXIEV;
+ ifr = U300_571_PXIFR;
+
+ val = readl(gpio->base + U300_571_CR);
+ dev_info(gpio->dev, "COH901571/3 block version: %d, " \
+ "number of cores: %d totalling %d pins\n",
+ ((val & 0x000001FC) >> 2),
+ ((val & 0x0000FE00) >> 9),
+ ((val & 0x0000FE00) >> 9) * 8);
+ writel(U300_571_CR_BLOCK_CLKRQ_ENABLE,
+ gpio->base + U300_571_CR);
+ u300_gpio_init_coh901571(gpio, plat);
+ } else {
+ dev_err(gpio->dev, "unknown block variant\n");
+ err = -ENODEV;
+ goto err_unknown_variant;
+ }
+
+ /* Add each port with its IRQ separately */
+ INIT_LIST_HEAD(&gpio->port_list);
+ for (portno = 0 ; portno < plat->ports; portno++) {
+ struct u300_gpio_port *port =
+ kmalloc(sizeof(struct u300_gpio_port), GFP_KERNEL);
+
+ if (!port) {
+ dev_err(gpio->dev, "out of memory\n");
+ err = -ENOMEM;
+ goto err_no_port;
}
- /* Turns off PortX_irq_force */
- writel(0x0, virtbase + U300_GPIO_PXIFR +
- num_irqs * U300_GPIO_PORTX_SPACING);
+
+ snprintf(port->name, 8, "gpio%d", portno);
+ port->number = portno;
+ port->gpio = gpio;
+
+ port->irq = platform_get_irq_byname(pdev,
+ port->name);
+
+ dev_dbg(gpio->dev, "register IRQ %d for %s\n", port->irq,
+ port->name);
+
+ irq_set_chained_handler(port->irq, u300_gpio_irq_handler);
+ irq_set_handler_data(port->irq, port);
+
+ /* For each GPIO pin set the unique IRQ handler */
+ for (i = 0; i < U300_GPIO_PINS_PER_PORT; i++) {
+ int irqno = gpio->irq_base + (portno << 3) + i;
+
+ dev_dbg(gpio->dev, "handler for IRQ %d on %s\n",
+ irqno, port->name);
+ irq_set_chip_and_handler(irqno, &u300_gpio_irqchip,
+ handle_simple_irq);
+ set_irq_flags(irqno, IRQF_VALID);
+ irq_set_chip_data(irqno, port);
+ }
+
+ /* Turns off irq force (test register) for this port */
+ writel(0x0, gpio->base + portno * gpio->stride + ifr);
+
+ list_add_tail(&port->node, &gpio->port_list);
}
+ dev_dbg(gpio->dev, "initialized %d GPIO ports\n", portno);
+
+ err = gpiochip_add(&gpio->chip);
+ if (err) {
+ dev_err(gpio->dev, "unable to add gpiochip: %d\n", err);
+ goto err_no_chip;
+ }
+
+ platform_set_drvdata(pdev, gpio);
return 0;
- err_no_irq:
- for (i = 0; i < num_irqs; i++)
- free_irq(gpio_ports[i].irq, &gpio_ports[i]);
- iounmap(virtbase);
- err_no_ioremap:
- release_mem_region(memres->start, memres->end - memres->start);
- err_no_ioregion:
- err_no_resource:
- clk_disable(clk);
- err_no_clk_enable:
- clk_put(clk);
- err_no_clk:
- dev_info(gpiodev, "module ERROR:%d\n", err);
+err_no_chip:
+err_no_port:
+ u300_gpio_free_ports(gpio);
+err_unknown_variant:
+ iounmap(gpio->base);
+err_no_ioremap:
+ release_mem_region(gpio->memres->start, resource_size(gpio->memres));
+err_no_ioregion:
+err_no_resource:
+ clk_disable(gpio->clk);
+err_no_clk_enable:
+ clk_put(gpio->clk);
+err_no_clk:
+ kfree(gpio);
+ dev_info(&pdev->dev, "module ERROR:%d\n", err);
return err;
}
-static int __exit gpio_remove(struct platform_device *pdev)
+static int __exit u300_gpio_remove(struct platform_device *pdev)
{
- int i;
+ struct u300_gpio_platform *plat = dev_get_platdata(&pdev->dev);
+ struct u300_gpio *gpio = platform_get_drvdata(pdev);
+ int err;
/* Turn off the GPIO block */
- writel(0x00000000U, virtbase + U300_GPIO_CR);
- for (i = 0 ; i < U300_GPIO_NUM_PORTS; i++)
- free_irq(gpio_ports[i].irq, &gpio_ports[i]);
- iounmap(virtbase);
- release_mem_region(memres->start, memres->end - memres->start);
- clk_disable(clk);
- clk_put(clk);
+ if (plat->variant == U300_GPIO_COH901335)
+ writel(0x00000000U, gpio->base + U300_335_CR);
+ if (plat->variant == U300_GPIO_COH901571_3_BS335 ||
+ plat->variant == U300_GPIO_COH901571_3_BS365)
+ writel(0x00000000U, gpio->base + U300_571_CR);
+
+ err = gpiochip_remove(&gpio->chip);
+ if (err < 0) {
+ dev_err(gpio->dev, "unable to remove gpiochip: %d\n", err);
+ return err;
+ }
+ u300_gpio_free_ports(gpio);
+ iounmap(gpio->base);
+ release_mem_region(gpio->memres->start,
+ resource_size(gpio->memres));
+ clk_disable(gpio->clk);
+ clk_put(gpio->clk);
+ platform_set_drvdata(pdev, NULL);
+ kfree(gpio);
return 0;
}
-static struct platform_driver gpio_driver = {
+static struct platform_driver u300_gpio_driver = {
.driver = {
.name = "u300-gpio",
},
- .remove = __exit_p(gpio_remove),
+ .remove = __exit_p(u300_gpio_remove),
};
static int __init u300_gpio_init(void)
{
- return platform_driver_probe(&gpio_driver, gpio_probe);
+ return platform_driver_probe(&u300_gpio_driver, u300_gpio_probe);
}
static void __exit u300_gpio_exit(void)
{
- platform_driver_unregister(&gpio_driver);
+ platform_driver_unregister(&u300_gpio_driver);
}
arch_initcall(u300_gpio_init);
module_exit(u300_gpio_exit);
MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
-
-#ifdef U300_COH901571_3
-MODULE_DESCRIPTION("ST-Ericsson AB COH 901 571/3 GPIO driver");
-#endif
-
-#ifdef U300_COH901335
-MODULE_DESCRIPTION("ST-Ericsson AB COH 901 335 GPIO driver");
-#endif
-
+MODULE_DESCRIPTION("ST-Ericsson AB COH 901 335/COH 901 571/3 GPIO driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/ucb1400_gpio.c b/drivers/gpio/gpio-ucb1400.c
index 50e6bd1392ce..50e6bd1392ce 100644
--- a/drivers/gpio/ucb1400_gpio.c
+++ b/drivers/gpio/gpio-ucb1400.c
diff --git a/drivers/gpio/vr41xx_giu.c b/drivers/gpio/gpio-vr41xx.c
index a365be040b36..98723cb9ac68 100644
--- a/drivers/gpio/vr41xx_giu.c
+++ b/drivers/gpio/gpio-vr41xx.c
@@ -518,7 +518,7 @@ static int __devinit giu_probe(struct platform_device *pdev)
if (!res)
return -EBUSY;
- giu_base = ioremap(res->start, res->end - res->start + 1);
+ giu_base = ioremap(res->start, resource_size(res));
if (!giu_base)
return -ENOMEM;
diff --git a/drivers/gpio/vx855_gpio.c b/drivers/gpio/gpio-vx855.c
index ef5aabd8b8b7..ef5aabd8b8b7 100644
--- a/drivers/gpio/vx855_gpio.c
+++ b/drivers/gpio/gpio-vx855.c
diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/gpio-wm831x.c
index 309644cf4d9b..deb949e75ec1 100644
--- a/drivers/gpio/wm831x-gpio.c
+++ b/drivers/gpio/gpio-wm831x.c
@@ -1,5 +1,5 @@
/*
- * wm831x-gpio.c -- gpiolib support for Wolfson WM831x PMICs
+ * gpiolib support for Wolfson WM831x PMICs
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
@@ -180,6 +180,7 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
break;
case WM831X_GPIO_PULL_UP:
pull = "pullup";
+ break;
default:
pull = "INVALID PULL";
break;
diff --git a/drivers/gpio/wm8350-gpiolib.c b/drivers/gpio/gpio-wm8350.c
index 359999290f55..a06af5154838 100644
--- a/drivers/gpio/wm8350-gpiolib.c
+++ b/drivers/gpio/gpio-wm8350.c
@@ -1,5 +1,5 @@
/*
- * wm835x-gpiolib.c -- gpiolib support for Wolfson WM835x PMICs
+ * gpiolib support for Wolfson WM835x PMICs
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
diff --git a/drivers/gpio/wm8994-gpio.c b/drivers/gpio/gpio-wm8994.c
index c822baacd8fc..96198f3fab73 100644
--- a/drivers/gpio/wm8994-gpio.c
+++ b/drivers/gpio/gpio-wm8994.c
@@ -1,5 +1,5 @@
/*
- * wm8994-gpio.c -- gpiolib support for Wolfson WM8994
+ * gpiolib support for Wolfson WM8994
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/gpio-xilinx.c
index 846fbd5e31bf..0ce6ac9898b1 100644
--- a/drivers/gpio/xilinx_gpio.c
+++ b/drivers/gpio/gpio-xilinx.c
@@ -14,6 +14,7 @@
#include <linux/init.h>
#include <linux/errno.h>
+#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index b493663c7ba7..1368826ef284 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -9,7 +9,6 @@ menuconfig DRM
depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && MMU
select I2C
select I2C_ALGOBIT
- select SLOW_WORK
help
Kernel-level support for the Direct Rendering Infrastructure (DRI)
introduced in XFree86 4.0. If you say Y here, you need to select
@@ -96,6 +95,7 @@ config DRM_I915
select FB_CFB_IMAGEBLIT
# i915 depends on ACPI_VIDEO when ACPI is enabled
# but for select to work, need to select ACPI_VIDEO's dependencies, ick
+ select BACKLIGHT_LCD_SUPPORT if ACPI
select BACKLIGHT_CLASS_DEVICE if ACPI
select VIDEO_OUTPUT_CONTROL if ACPI
select INPUT if ACPI
@@ -158,3 +158,7 @@ config DRM_SAVAGE
help
Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister
chipset. If M is selected the module will be called savage.
+
+source "drivers/gpu/drm/exynos/Kconfig"
+
+source "drivers/gpu/drm/vmwgfx/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 89cf05a72d1c..c0496f660707 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -35,4 +35,5 @@ obj-$(CONFIG_DRM_SAVAGE)+= savage/
obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
+obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-y += i2c/
diff --git a/drivers/gpu/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c
index 1c3649242208..9afe495c12c7 100644
--- a/drivers/gpu/drm/ati_pcigart.c
+++ b/drivers/gpu/drm/ati_pcigart.c
@@ -31,6 +31,7 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include <linux/export.h>
#include "drmP.h"
# define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */
diff --git a/drivers/gpu/drm/drm_buffer.c b/drivers/gpu/drm/drm_buffer.c
index 529a0dbe9fc6..08ccefedb327 100644
--- a/drivers/gpu/drm/drm_buffer.c
+++ b/drivers/gpu/drm/drm_buffer.c
@@ -32,6 +32,7 @@
* Pauli Nieminen <suokkos-at-gmail-dot-com>
*/
+#include <linux/export.h>
#include "drm_buffer.h"
/**
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index 61e1ef90d4e5..30372f7b2d45 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -36,6 +36,7 @@
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/log2.h>
+#include <linux/export.h>
#include <asm/shmparam.h>
#include "drmP.h"
diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c
index 0e3bd5b54b78..592865381c6e 100644
--- a/drivers/gpu/drm/drm_cache.c
+++ b/drivers/gpu/drm/drm_cache.c
@@ -28,6 +28,7 @@
* Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
*/
+#include <linux/export.h>
#include "drmP.h"
#if defined(CONFIG_X86)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 82db18506662..8323fc389840 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -31,6 +31,7 @@
*/
#include <linux/list.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "drm.h"
#include "drmP.h"
#include "drm_crtc.h"
@@ -162,6 +163,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] =
{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B", 0 },
{ DRM_MODE_CONNECTOR_TV, "TV", 0 },
{ DRM_MODE_CONNECTOR_eDP, "eDP", 0 },
+ { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual", 0},
};
static struct drm_prop_enum_list drm_encoder_enum_list[] =
@@ -170,6 +172,7 @@ static struct drm_prop_enum_list drm_encoder_enum_list[] =
{ DRM_MODE_ENCODER_TMDS, "TMDS" },
{ DRM_MODE_ENCODER_LVDS, "LVDS" },
{ DRM_MODE_ENCODER_TVDAC, "TV" },
+ { DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
};
char *drm_get_encoder_name(struct drm_encoder *encoder)
@@ -463,8 +466,10 @@ void drm_connector_init(struct drm_device *dev,
list_add_tail(&connector->head, &dev->mode_config.connector_list);
dev->mode_config.num_connector++;
- drm_connector_attach_property(connector,
- dev->mode_config.edid_property, 0);
+ if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
+ drm_connector_attach_property(connector,
+ dev->mode_config.edid_property,
+ 0);
drm_connector_attach_property(connector,
dev->mode_config.dpms_property, 0);
@@ -499,6 +504,7 @@ void drm_connector_cleanup(struct drm_connector *connector)
mutex_lock(&dev->mode_config.mutex);
drm_mode_object_put(dev, &connector->base);
list_del(&connector->head);
+ dev->mode_config.num_connector--;
mutex_unlock(&dev->mode_config.mutex);
}
EXPORT_SYMBOL(drm_connector_cleanup);
@@ -529,6 +535,7 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
mutex_lock(&dev->mode_config.mutex);
drm_mode_object_put(dev, &encoder->base);
list_del(&encoder->head);
+ dev->mode_config.num_encoder--;
mutex_unlock(&dev->mode_config.mutex);
}
EXPORT_SYMBOL(drm_encoder_cleanup);
@@ -1866,6 +1873,10 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
}
if (num_clips && clips_ptr) {
+ if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) {
+ ret = -EINVAL;
+ goto out_err1;
+ }
clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
if (!clips) {
ret = -ENOMEM;
@@ -2111,8 +2122,10 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
property->num_values = num_values;
INIT_LIST_HEAD(&property->enum_blob_list);
- if (name)
+ if (name) {
strncpy(property->name, name, DRM_PROP_NAME_LEN);
+ property->name[DRM_PROP_NAME_LEN-1] = '\0';
+ }
list_add_tail(&property->head, &dev->mode_config.property_list);
return property;
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 92369655dca3..3969f7553fe7 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -29,6 +29,9 @@
* Jesse Barnes <jesse.barnes@intel.com>
*/
+#include <linux/export.h>
+#include <linux/moduleparam.h>
+
#include "drmP.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
@@ -372,11 +375,13 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
encoder_funcs = encoder->helper_private;
if (!(ret = encoder_funcs->mode_fixup(encoder, mode,
adjusted_mode))) {
+ DRM_DEBUG_KMS("Encoder fixup failed\n");
goto done;
}
}
if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) {
+ DRM_DEBUG_KMS("CRTC fixup failed\n");
goto done;
}
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
@@ -479,6 +484,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
struct drm_connector *save_connectors, *connector;
int count = 0, ro, fail = 0;
struct drm_crtc_helper_funcs *crtc_funcs;
+ struct drm_mode_set save_set;
int ret = 0;
int i;
@@ -551,6 +557,12 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
save_connectors[count++] = *connector;
}
+ save_set.crtc = set->crtc;
+ save_set.mode = &set->crtc->mode;
+ save_set.x = set->crtc->x;
+ save_set.y = set->crtc->y;
+ save_set.fb = set->crtc->fb;
+
/* We should be able to check here if the fb has the same properties
* and then just flip_or_move it */
if (set->crtc->fb != set->fb) {
@@ -560,6 +572,11 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
mode_changed = true;
} else if (set->fb == NULL) {
mode_changed = true;
+ } else if (set->fb->depth != set->crtc->fb->depth) {
+ mode_changed = true;
+ } else if (set->fb->bits_per_pixel !=
+ set->crtc->fb->bits_per_pixel) {
+ mode_changed = true;
} else
fb_changed = true;
}
@@ -711,6 +728,12 @@ fail:
*connector = save_connectors[count++];
}
+ /* Try to restore the config */
+ if (mode_changed &&
+ !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x,
+ save_set.y, save_set.fb))
+ DRM_ERROR("failed to restore config after modeset failure\n");
+
kfree(save_connectors);
kfree(save_encoders);
kfree(save_crtcs);
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 9d8c892d07c9..1c7a1c0d3edd 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -33,6 +33,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "drmP.h"
#if defined(CONFIG_DEBUG_FS)
@@ -90,7 +91,6 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count,
struct drm_device *dev = minor->dev;
struct dentry *ent;
struct drm_info_node *tmp;
- char name[64];
int i, ret;
for (i = 0; i < count; i++) {
@@ -109,7 +109,7 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count,
root, tmp, &drm_debugfs_fops);
if (!ent) {
DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/%s\n",
- name, files[i].name);
+ root->d_name.name, files[i].name);
kfree(tmp);
ret = -1;
goto fail;
@@ -118,7 +118,10 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count,
tmp->minor = minor;
tmp->dent = ent;
tmp->info_ent = &files[i];
- list_add(&(tmp->list), &(minor->debugfs_nodes.list));
+
+ mutex_lock(&minor->debugfs_lock);
+ list_add(&tmp->list, &minor->debugfs_list);
+ mutex_unlock(&minor->debugfs_lock);
}
return 0;
@@ -146,7 +149,8 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id,
char name[64];
int ret;
- INIT_LIST_HEAD(&minor->debugfs_nodes.list);
+ INIT_LIST_HEAD(&minor->debugfs_list);
+ mutex_init(&minor->debugfs_lock);
sprintf(name, "%d", minor_id);
minor->debugfs_root = debugfs_create_dir(name, root);
if (!minor->debugfs_root) {
@@ -192,8 +196,9 @@ int drm_debugfs_remove_files(struct drm_info_list *files, int count,
struct drm_info_node *tmp;
int i;
+ mutex_lock(&minor->debugfs_lock);
for (i = 0; i < count; i++) {
- list_for_each_safe(pos, q, &minor->debugfs_nodes.list) {
+ list_for_each_safe(pos, q, &minor->debugfs_list) {
tmp = list_entry(pos, struct drm_info_node, list);
if (tmp->info_ent == &files[i]) {
debugfs_remove(tmp->dent);
@@ -202,6 +207,7 @@ int drm_debugfs_remove_files(struct drm_info_list *files, int count,
}
}
}
+ mutex_unlock(&minor->debugfs_lock);
return 0;
}
EXPORT_SYMBOL(drm_debugfs_remove_files);
diff --git a/drivers/gpu/drm/drm_dma.c b/drivers/gpu/drm/drm_dma.c
index 252cbd74df0e..cfb4e333ec0f 100644
--- a/drivers/gpu/drm/drm_dma.c
+++ b/drivers/gpu/drm/drm_dma.c
@@ -33,6 +33,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
+#include <linux/export.h>
#include "drmP.h"
/**
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 93a112d45c1a..40c187c60f44 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -48,6 +48,7 @@
#include <linux/debugfs.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "drmP.h"
#include "drm_core.h"
@@ -124,7 +125,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, 0),
+ DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0),
@@ -438,6 +439,8 @@ long drm_ioctl(struct file *filp,
goto err_i1;
}
}
+ if (asize > usize)
+ memset(kdata + usize, 0, asize - usize);
}
if (cmd & IOC_IN) {
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 09292193dafe..3e927ce7557d 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -30,6 +30,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/export.h>
#include "drmP.h"
#include "drm_edid.h"
#include "drm_edid_modes.h"
@@ -127,6 +128,23 @@ static const u8 edid_header[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
};
+ /*
+ * Sanity check the header of the base EDID block. Return 8 if the header
+ * is perfect, down to 0 if it's totally wrong.
+ */
+int drm_edid_header_is_valid(const u8 *raw_edid)
+{
+ int i, score = 0;
+
+ for (i = 0; i < sizeof(edid_header); i++)
+ if (raw_edid[i] == edid_header[i])
+ score++;
+
+ return score;
+}
+EXPORT_SYMBOL(drm_edid_header_is_valid);
+
+
/*
* Sanity check the EDID block (base or extension). Return 0 if the block
* doesn't check out, or 1 if it's valid.
@@ -139,12 +157,7 @@ drm_edid_block_valid(u8 *raw_edid)
struct edid *edid = (struct edid *)raw_edid;
if (raw_edid[0] == 0x00) {
- int score = 0;
-
- for (i = 0; i < sizeof(edid_header); i++)
- if (raw_edid[i] == edid_header[i])
- score++;
-
+ int score = drm_edid_header_is_valid(raw_edid);
if (score == 8) ;
else if (score >= 6) {
DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
@@ -185,8 +198,8 @@ drm_edid_block_valid(u8 *raw_edid)
bad:
if (raw_edid) {
printk(KERN_ERR "Raw EDID:\n");
- print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH);
- printk(KERN_ERR "\n");
+ print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1,
+ raw_edid, EDID_LENGTH, false);
}
return 0;
}
@@ -1307,6 +1320,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
#define HDMI_IDENTIFIER 0x000C03
#define AUDIO_BLOCK 0x01
#define VENDOR_BLOCK 0x03
+#define SPEAKER_BLOCK 0x04
#define EDID_BASIC_AUDIO (1 << 6)
/**
@@ -1335,6 +1349,176 @@ u8 *drm_find_cea_extension(struct edid *edid)
}
EXPORT_SYMBOL(drm_find_cea_extension);
+static void
+parse_hdmi_vsdb(struct drm_connector *connector, uint8_t *db)
+{
+ connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */
+
+ connector->dvi_dual = db[6] & 1;
+ connector->max_tmds_clock = db[7] * 5;
+
+ connector->latency_present[0] = db[8] >> 7;
+ connector->latency_present[1] = (db[8] >> 6) & 1;
+ connector->video_latency[0] = db[9];
+ connector->audio_latency[0] = db[10];
+ connector->video_latency[1] = db[11];
+ connector->audio_latency[1] = db[12];
+
+ DRM_LOG_KMS("HDMI: DVI dual %d, "
+ "max TMDS clock %d, "
+ "latency present %d %d, "
+ "video latency %d %d, "
+ "audio latency %d %d\n",
+ connector->dvi_dual,
+ connector->max_tmds_clock,
+ (int) connector->latency_present[0],
+ (int) connector->latency_present[1],
+ connector->video_latency[0],
+ connector->video_latency[1],
+ connector->audio_latency[0],
+ connector->audio_latency[1]);
+}
+
+static void
+monitor_name(struct detailed_timing *t, void *data)
+{
+ if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME)
+ *(u8 **)data = t->data.other_data.data.str.str;
+}
+
+/**
+ * drm_edid_to_eld - build ELD from EDID
+ * @connector: connector corresponding to the HDMI/DP sink
+ * @edid: EDID to parse
+ *
+ * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver.
+ * Some ELD fields are left to the graphics driver caller:
+ * - Conn_Type
+ * - HDCP
+ * - Port_ID
+ */
+void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
+{
+ uint8_t *eld = connector->eld;
+ u8 *cea;
+ u8 *name;
+ u8 *db;
+ int sad_count = 0;
+ int mnl;
+ int dbl;
+
+ memset(eld, 0, sizeof(connector->eld));
+
+ cea = drm_find_cea_extension(edid);
+ if (!cea) {
+ DRM_DEBUG_KMS("ELD: no CEA Extension found\n");
+ return;
+ }
+
+ name = NULL;
+ drm_for_each_detailed_block((u8 *)edid, monitor_name, &name);
+ for (mnl = 0; name && mnl < 13; mnl++) {
+ if (name[mnl] == 0x0a)
+ break;
+ eld[20 + mnl] = name[mnl];
+ }
+ eld[4] = (cea[1] << 5) | mnl;
+ DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20);
+
+ eld[0] = 2 << 3; /* ELD version: 2 */
+
+ eld[16] = edid->mfg_id[0];
+ eld[17] = edid->mfg_id[1];
+ eld[18] = edid->prod_code[0];
+ eld[19] = edid->prod_code[1];
+
+ for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
+ dbl = db[0] & 0x1f;
+
+ switch ((db[0] & 0xe0) >> 5) {
+ case AUDIO_BLOCK: /* Audio Data Block, contains SADs */
+ sad_count = dbl / 3;
+ memcpy(eld + 20 + mnl, &db[1], dbl);
+ break;
+ case SPEAKER_BLOCK: /* Speaker Allocation Data Block */
+ eld[7] = db[1];
+ break;
+ case VENDOR_BLOCK:
+ /* HDMI Vendor-Specific Data Block */
+ if (db[1] == 0x03 && db[2] == 0x0c && db[3] == 0)
+ parse_hdmi_vsdb(connector, db);
+ break;
+ default:
+ break;
+ }
+ }
+ eld[5] |= sad_count << 4;
+ eld[2] = (20 + mnl + sad_count * 3 + 3) / 4;
+
+ DRM_DEBUG_KMS("ELD size %d, SAD count %d\n", (int)eld[2], sad_count);
+}
+EXPORT_SYMBOL(drm_edid_to_eld);
+
+/**
+ * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond
+ * @connector: connector associated with the HDMI/DP sink
+ * @mode: the display mode
+ */
+int drm_av_sync_delay(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+ int a, v;
+
+ if (!connector->latency_present[0])
+ return 0;
+ if (!connector->latency_present[1])
+ i = 0;
+
+ a = connector->audio_latency[i];
+ v = connector->video_latency[i];
+
+ /*
+ * HDMI/DP sink doesn't support audio or video?
+ */
+ if (a == 255 || v == 255)
+ return 0;
+
+ /*
+ * Convert raw EDID values to millisecond.
+ * Treat unknown latency as 0ms.
+ */
+ if (a)
+ a = min(2 * (a - 1), 500);
+ if (v)
+ v = min(2 * (v - 1), 500);
+
+ return max(v - a, 0);
+}
+EXPORT_SYMBOL(drm_av_sync_delay);
+
+/**
+ * drm_select_eld - select one ELD from multiple HDMI/DP sinks
+ * @encoder: the encoder just changed display mode
+ * @mode: the adjusted display mode
+ *
+ * It's possible for one encoder to be associated with multiple HDMI/DP sinks.
+ * The policy is now hard coded to simply use the first HDMI/DP sink's ELD.
+ */
+struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
+ struct drm_display_mode *mode)
+{
+ struct drm_connector *connector;
+ struct drm_device *dev = encoder->dev;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ if (connector->encoder == encoder && connector->eld[0])
+ return connector;
+
+ return NULL;
+}
+EXPORT_SYMBOL(drm_select_eld);
+
/**
* drm_detect_hdmi_monitor - detect whether monitor is hdmi.
* @edid: monitor EDID information
@@ -1439,6 +1623,8 @@ EXPORT_SYMBOL(drm_detect_monitor_audio);
static void drm_add_display_info(struct edid *edid,
struct drm_display_info *info)
{
+ u8 *edid_ext;
+
info->width_mm = edid->width_cm * 10;
info->height_mm = edid->height_cm * 10;
@@ -1483,6 +1669,13 @@ static void drm_add_display_info(struct edid *edid,
info->color_formats = DRM_COLOR_FORMAT_YCRCB444;
if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB422)
info->color_formats = DRM_COLOR_FORMAT_YCRCB422;
+
+ /* Get data from CEA blocks if present */
+ edid_ext = drm_find_cea_extension(edid);
+ if (!edid_ext)
+ return;
+
+ info->cea_rev = edid_ext[1];
}
/**
diff --git a/drivers/gpu/drm/drm_encoder_slave.c b/drivers/gpu/drm/drm_encoder_slave.c
index d62c064fbaa0..fb943551060e 100644
--- a/drivers/gpu/drm/drm_encoder_slave.c
+++ b/drivers/gpu/drm/drm_encoder_slave.c
@@ -24,6 +24,8 @@
*
*/
+#include <linux/module.h>
+
#include "drm_encoder_slave.h"
/**
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 802b61ac3139..80fe39d98b0c 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -31,6 +31,7 @@
#include <linux/sysrq.h>
#include <linux/slab.h>
#include <linux/fb.h>
+#include <linux/module.h>
#include "drmP.h"
#include "drm_crtc.h"
#include "drm_fb_helper.h"
@@ -256,7 +257,6 @@ int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
{
printk(KERN_ERR "panic occurred, switching back to text console\n");
return drm_fb_helper_force_kernel_mode();
- return 0;
}
EXPORT_SYMBOL(drm_fb_helper_panic);
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 2ec7d48fc4a8..4911e1d1dcf2 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -37,6 +37,7 @@
#include "drmP.h"
#include <linux/poll.h>
#include <linux/slab.h>
+#include <linux/module.h>
/* from BKL pushdown: note that nothing else serializes idr_find() */
DEFINE_MUTEX(drm_global_mutex);
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 4012fe423460..396e60ce8114 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -129,7 +129,7 @@ drm_gem_destroy(struct drm_device *dev)
}
/**
- * Initialize an already allocate GEM object of the specified size with
+ * Initialize an already allocated GEM object of the specified size with
* shmfs backing store.
*/
int drm_gem_object_init(struct drm_device *dev,
@@ -151,6 +151,27 @@ int drm_gem_object_init(struct drm_device *dev,
EXPORT_SYMBOL(drm_gem_object_init);
/**
+ * Initialize an already allocated GEM object of the specified size with
+ * no GEM provided backing store. Instead the caller is responsible for
+ * backing the object and handling it.
+ */
+int drm_gem_private_object_init(struct drm_device *dev,
+ struct drm_gem_object *obj, size_t size)
+{
+ BUG_ON((size & (PAGE_SIZE - 1)) != 0);
+
+ obj->dev = dev;
+ obj->filp = NULL;
+
+ kref_init(&obj->refcount);
+ atomic_set(&obj->handle_count, 0);
+ obj->size = size;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_gem_private_object_init);
+
+/**
* Allocate a GEM object of the specified size with shmfs backing store
*/
struct drm_gem_object *
@@ -211,6 +232,8 @@ drm_gem_handle_delete(struct drm_file *filp, u32 handle)
idr_remove(&filp->object_idr, handle);
spin_unlock(&filp->table_lock);
+ if (dev->driver->gem_close_object)
+ dev->driver->gem_close_object(obj, filp);
drm_gem_object_handle_unreference_unlocked(obj);
return 0;
@@ -227,7 +250,8 @@ drm_gem_handle_create(struct drm_file *file_priv,
struct drm_gem_object *obj,
u32 *handlep)
{
- int ret;
+ struct drm_device *dev = obj->dev;
+ int ret;
/*
* Get the user-visible handle using idr.
@@ -248,10 +272,107 @@ again:
return ret;
drm_gem_object_handle_reference(obj);
+
+ if (dev->driver->gem_open_object) {
+ ret = dev->driver->gem_open_object(obj, file_priv);
+ if (ret) {
+ drm_gem_handle_delete(file_priv, *handlep);
+ return ret;
+ }
+ }
+
return 0;
}
EXPORT_SYMBOL(drm_gem_handle_create);
+
+/**
+ * drm_gem_free_mmap_offset - release a fake mmap offset for an object
+ * @obj: obj in question
+ *
+ * This routine frees fake offsets allocated by drm_gem_create_mmap_offset().
+ */
+void
+drm_gem_free_mmap_offset(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ struct drm_gem_mm *mm = dev->mm_private;
+ struct drm_map_list *list = &obj->map_list;
+
+ drm_ht_remove_item(&mm->offset_hash, &list->hash);
+ drm_mm_put_block(list->file_offset_node);
+ kfree(list->map);
+ list->map = NULL;
+}
+EXPORT_SYMBOL(drm_gem_free_mmap_offset);
+
+/**
+ * drm_gem_create_mmap_offset - create a fake mmap offset for an object
+ * @obj: obj in question
+ *
+ * GEM memory mapping works by handing back to userspace a fake mmap offset
+ * it can use in a subsequent mmap(2) call. The DRM core code then looks
+ * up the object based on the offset and sets up the various memory mapping
+ * structures.
+ *
+ * This routine allocates and attaches a fake offset for @obj.
+ */
+int
+drm_gem_create_mmap_offset(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_local_map *map;
+ int ret = 0;
+
+ /* Set the object up for mmap'ing */
+ list = &obj->map_list;
+ list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
+ if (!list->map)
+ return -ENOMEM;
+
+ map = list->map;
+ map->type = _DRM_GEM;
+ map->size = obj->size;
+ map->handle = obj;
+
+ /* Get a DRM GEM mmap offset allocated... */
+ list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
+ obj->size / PAGE_SIZE, 0, 0);
+
+ if (!list->file_offset_node) {
+ DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
+ ret = -ENOSPC;
+ goto out_free_list;
+ }
+
+ list->file_offset_node = drm_mm_get_block(list->file_offset_node,
+ obj->size / PAGE_SIZE, 0);
+ if (!list->file_offset_node) {
+ ret = -ENOMEM;
+ goto out_free_list;
+ }
+
+ list->hash.key = list->file_offset_node->start;
+ ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
+ if (ret) {
+ DRM_ERROR("failed to add to map hash\n");
+ goto out_free_mm;
+ }
+
+ return 0;
+
+out_free_mm:
+ drm_mm_put_block(list->file_offset_node);
+out_free_list:
+ kfree(list->map);
+ list->map = NULL;
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_gem_create_mmap_offset);
+
/** Returns a reference to the object named by the handle. */
struct drm_gem_object *
drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp,
@@ -402,7 +523,12 @@ drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
static int
drm_gem_object_release_handle(int id, void *ptr, void *data)
{
+ struct drm_file *file_priv = data;
struct drm_gem_object *obj = ptr;
+ struct drm_device *dev = obj->dev;
+
+ if (dev->driver->gem_close_object)
+ dev->driver->gem_close_object(obj, file_priv);
drm_gem_object_handle_unreference_unlocked(obj);
@@ -418,7 +544,7 @@ void
drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
{
idr_for_each(&file_private->object_idr,
- &drm_gem_object_release_handle, NULL);
+ &drm_gem_object_release_handle, file_private);
idr_remove_all(&file_private->object_idr);
idr_destroy(&file_private->object_idr);
@@ -427,7 +553,8 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
void
drm_gem_object_release(struct drm_gem_object *obj)
{
- fput(obj->filp);
+ if (obj->filp)
+ fput(obj->filp);
}
EXPORT_SYMBOL(drm_gem_object_release);
diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c
index e3a75688f3cd..68dc8744b630 100644
--- a/drivers/gpu/drm/drm_hashtab.c
+++ b/drivers/gpu/drm/drm_hashtab.c
@@ -36,6 +36,7 @@
#include "drm_hashtab.h"
#include <linux/hash.h>
#include <linux/slab.h>
+#include <linux/export.h>
int drm_ht_create(struct drm_open_hash *ht, unsigned int order)
{
diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c
index 4a058c7af6c0..ddd70db45f76 100644
--- a/drivers/gpu/drm/drm_ioc32.c
+++ b/drivers/gpu/drm/drm_ioc32.c
@@ -29,6 +29,7 @@
*/
#include <linux/compat.h>
#include <linux/ratelimit.h>
+#include <linux/export.h>
#include "drmP.h"
#include "drm_core.h"
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 2022a5c966bb..44a5d0ad8b7c 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <linux/vgaarb.h>
+#include <linux/export.h>
/* Access macro for slots in vblank timestamp ringbuffer. */
#define vblanktimestamp(dev, crtc, count) ( \
@@ -109,10 +110,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
/* Prevent vblank irq processing while disabling vblank irqs,
* so no updates of timestamps or count can happen after we've
* disabled. Needed to prevent races in case of delayed irq's.
- * Disable preemption, so vblank_time_lock is held as short as
- * possible, even under a kernel with PREEMPT_RT patches.
*/
- preempt_disable();
spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
dev->driver->disable_vblank(dev, crtc);
@@ -163,7 +161,6 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
clear_vblank_timestamps(dev, crtc);
spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
- preempt_enable();
}
static void vblank_disable_fn(unsigned long arg)
@@ -291,11 +288,14 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state)
if (!dev->irq_enabled)
return;
- if (state)
- dev->driver->irq_uninstall(dev);
- else {
- dev->driver->irq_preinstall(dev);
- dev->driver->irq_postinstall(dev);
+ if (state) {
+ if (dev->driver->irq_uninstall)
+ dev->driver->irq_uninstall(dev);
+ } else {
+ if (dev->driver->irq_preinstall)
+ dev->driver->irq_preinstall(dev);
+ if (dev->driver->irq_postinstall)
+ dev->driver->irq_postinstall(dev);
}
}
@@ -338,7 +338,8 @@ int drm_irq_install(struct drm_device *dev)
DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev));
/* Before installing handler */
- dev->driver->irq_preinstall(dev);
+ if (dev->driver->irq_preinstall)
+ dev->driver->irq_preinstall(dev);
/* Install handler */
if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
@@ -363,11 +364,16 @@ int drm_irq_install(struct drm_device *dev)
vga_client_register(dev->pdev, (void *)dev, drm_irq_vgaarb_nokms, NULL);
/* After installing handler */
- ret = dev->driver->irq_postinstall(dev);
+ if (dev->driver->irq_postinstall)
+ ret = dev->driver->irq_postinstall(dev);
+
if (ret < 0) {
mutex_lock(&dev->struct_mutex);
dev->irq_enabled = 0;
mutex_unlock(&dev->struct_mutex);
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ vga_client_register(dev->pdev, NULL, NULL, NULL);
+ free_irq(drm_dev_to_irq(dev), dev);
}
return ret;
@@ -397,13 +403,16 @@ int drm_irq_uninstall(struct drm_device *dev)
/*
* Wake up any waiters so they don't hang.
*/
- spin_lock_irqsave(&dev->vbl_lock, irqflags);
- for (i = 0; i < dev->num_crtcs; i++) {
- DRM_WAKEUP(&dev->vbl_queue[i]);
- dev->vblank_enabled[i] = 0;
- dev->last_vblank[i] = dev->driver->get_vblank_counter(dev, i);
+ if (dev->num_crtcs) {
+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
+ for (i = 0; i < dev->num_crtcs; i++) {
+ DRM_WAKEUP(&dev->vbl_queue[i]);
+ dev->vblank_enabled[i] = 0;
+ dev->last_vblank[i] =
+ dev->driver->get_vblank_counter(dev, i);
+ }
+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
}
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
if (!irq_enabled)
return -EINVAL;
@@ -413,7 +422,8 @@ int drm_irq_uninstall(struct drm_device *dev)
if (!drm_core_check_feature(dev, DRIVER_MODESET))
vga_client_register(dev->pdev, NULL, NULL, NULL);
- dev->driver->irq_uninstall(dev);
+ if (dev->driver->irq_uninstall)
+ dev->driver->irq_uninstall(dev);
free_irq(drm_dev_to_irq(dev), dev);
@@ -875,10 +885,6 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
spin_lock_irqsave(&dev->vbl_lock, irqflags);
/* Going from 0->1 means we have to enable interrupts again */
if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) {
- /* Disable preemption while holding vblank_time_lock. Do
- * it explicitely to guard against PREEMPT_RT kernel.
- */
- preempt_disable();
spin_lock_irqsave(&dev->vblank_time_lock, irqflags2);
if (!dev->vblank_enabled[crtc]) {
/* Enable vblank irqs under vblank_time_lock protection.
@@ -898,7 +904,6 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
}
}
spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags2);
- preempt_enable();
} else {
if (!dev->vblank_enabled[crtc]) {
atomic_dec(&dev->vblank_refcount[crtc]);
@@ -1114,6 +1119,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
trace_drm_vblank_event_delivered(current->pid, pipe,
vblwait->request.sequence);
} else {
+ /* drm_handle_vblank_events will call drm_vblank_put */
list_add_tail(&e->base.link, &dev->vblank_event_list);
vblwait->reply.sequence = vblwait->request.sequence;
}
@@ -1194,8 +1200,12 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
goto done;
}
- if (flags & _DRM_VBLANK_EVENT)
+ if (flags & _DRM_VBLANK_EVENT) {
+ /* must hold on to the vblank ref until the event fires
+ * drm_vblank_put will be called asynchronously
+ */
return drm_queue_vblank_event(dev, crtc, vblwait, file_priv);
+ }
if ((flags & _DRM_VBLANK_NEXTONMISS) &&
(seq - vblwait->request.sequence) <= (1<<23)) {
diff --git a/drivers/gpu/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c
index c9b805000a11..c8b6b66d428d 100644
--- a/drivers/gpu/drm/drm_memory.c
+++ b/drivers/gpu/drm/drm_memory.c
@@ -34,6 +34,7 @@
*/
#include <linux/highmem.h>
+#include <linux/export.h>
#include "drmP.h"
/**
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 959186cbf328..961fb54f4266 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -45,6 +45,7 @@
#include "drm_mm.h"
#include <linux/slab.h>
#include <linux/seq_file.h>
+#include <linux/export.h>
#define MM_UNUSED_TARGET 4
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index c2d32f20e2fb..fb8e46b4e8bc 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -32,6 +32,7 @@
#include <linux/list.h>
#include <linux/list_sort.h>
+#include <linux/export.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
@@ -994,9 +995,10 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
{
const char *name;
unsigned int namelen;
- int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
+ bool res_specified = false, bpp_specified = false, refresh_specified = false;
unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
- int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
+ bool yres_specified = false, cvt = false, rb = false;
+ bool interlace = false, margins = false, was_digit = false;
int i;
enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
@@ -1015,54 +1017,65 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
for (i = namelen-1; i >= 0; i--) {
switch (name[i]) {
case '@':
- namelen = i;
if (!refresh_specified && !bpp_specified &&
- !yres_specified) {
+ !yres_specified && !cvt && !rb && was_digit) {
refresh = simple_strtol(&name[i+1], NULL, 10);
- refresh_specified = 1;
- if (cvt || rb)
- cvt = 0;
+ refresh_specified = true;
+ was_digit = false;
} else
goto done;
break;
case '-':
- namelen = i;
- if (!bpp_specified && !yres_specified) {
+ if (!bpp_specified && !yres_specified && !cvt &&
+ !rb && was_digit) {
bpp = simple_strtol(&name[i+1], NULL, 10);
- bpp_specified = 1;
- if (cvt || rb)
- cvt = 0;
+ bpp_specified = true;
+ was_digit = false;
} else
goto done;
break;
case 'x':
- if (!yres_specified) {
+ if (!yres_specified && was_digit) {
yres = simple_strtol(&name[i+1], NULL, 10);
- yres_specified = 1;
+ yres_specified = true;
+ was_digit = false;
} else
goto done;
case '0' ... '9':
+ was_digit = true;
break;
case 'M':
- if (!yres_specified)
- cvt = 1;
+ if (yres_specified || cvt || was_digit)
+ goto done;
+ cvt = true;
break;
case 'R':
- if (cvt)
- rb = 1;
+ if (yres_specified || cvt || rb || was_digit)
+ goto done;
+ rb = true;
break;
case 'm':
- if (!cvt)
- margins = 1;
+ if (cvt || yres_specified || was_digit)
+ goto done;
+ margins = true;
break;
case 'i':
- if (!cvt)
- interlace = 1;
+ if (cvt || yres_specified || was_digit)
+ goto done;
+ interlace = true;
break;
case 'e':
+ if (yres_specified || bpp_specified || refresh_specified ||
+ was_digit || (force != DRM_FORCE_UNSPECIFIED))
+ goto done;
+
force = DRM_FORCE_ON;
break;
case 'D':
+ if (yres_specified || bpp_specified || refresh_specified ||
+ was_digit || (force != DRM_FORCE_UNSPECIFIED))
+ goto done;
+
if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
(connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
force = DRM_FORCE_ON;
@@ -1070,17 +1083,37 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
force = DRM_FORCE_ON_DIGITAL;
break;
case 'd':
+ if (yres_specified || bpp_specified || refresh_specified ||
+ was_digit || (force != DRM_FORCE_UNSPECIFIED))
+ goto done;
+
force = DRM_FORCE_OFF;
break;
default:
goto done;
}
}
+
if (i < 0 && yres_specified) {
- xres = simple_strtol(name, NULL, 10);
- res_specified = 1;
+ char *ch;
+ xres = simple_strtol(name, &ch, 10);
+ if ((ch != NULL) && (*ch == 'x'))
+ res_specified = true;
+ else
+ i = ch - name;
+ } else if (!yres_specified && was_digit) {
+ /* catch mode that begins with digits but has no 'x' */
+ i = 0;
}
done:
+ if (i >= 0) {
+ printk(KERN_WARNING
+ "parse error at position %i in video mode '%s'\n",
+ i, name);
+ mode->specified = false;
+ return false;
+ }
+
if (res_specified) {
mode->specified = true;
mode->xres = xres;
@@ -1096,9 +1129,10 @@ done:
mode->bpp_specified = true;
mode->bpp = bpp;
}
- mode->rb = rb ? true : false;
- mode->cvt = cvt ? true : false;
- mode->interlace = interlace ? true : false;
+ mode->rb = rb;
+ mode->cvt = cvt;
+ mode->interlace = interlace;
+ mode->margins = margins;
mode->force = force;
return true;
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index b6a19cb07caf..d4d10b7880cf 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -39,6 +39,7 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
+#include <linux/export.h>
#include "drmP.h"
/**********************************************************************/
diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c
index 7223f06d8e58..ae9db5e2b27c 100644
--- a/drivers/gpu/drm/drm_platform.c
+++ b/drivers/gpu/drm/drm_platform.c
@@ -25,6 +25,7 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+#include <linux/export.h>
#include "drmP.h"
/**
@@ -123,14 +124,15 @@ static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *mas
{
int len, ret;
- master->unique_len = 10 + strlen(dev->platformdev->name);
+ master->unique_len = 13 + strlen(dev->platformdev->name);
+ master->unique_size = master->unique_len;
master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
if (master->unique == NULL)
return -ENOMEM;
len = snprintf(master->unique, master->unique_len,
- "platform:%s", dev->platformdev->name);
+ "platform:%s:%02d", dev->platformdev->name, dev->platformdev->id);
if (len > master->unique_len) {
DRM_ERROR("Unique buffer overflowed\n");
diff --git a/drivers/gpu/drm/drm_proc.c b/drivers/gpu/drm/drm_proc.c
index 9e5b07efebb7..fff87221f9e9 100644
--- a/drivers/gpu/drm/drm_proc.c
+++ b/drivers/gpu/drm/drm_proc.c
@@ -39,6 +39,7 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "drmP.h"
/***************************************************
@@ -95,7 +96,6 @@ int drm_proc_create_files(struct drm_info_list *files, int count,
struct drm_device *dev = minor->dev;
struct proc_dir_entry *ent;
struct drm_info_node *tmp;
- char name[64];
int i, ret;
for (i = 0; i < count; i++) {
@@ -118,7 +118,7 @@ int drm_proc_create_files(struct drm_info_list *files, int count,
&drm_proc_fops, tmp);
if (!ent) {
DRM_ERROR("Cannot create /proc/dri/%s/%s\n",
- name, files[i].name);
+ root->name, files[i].name);
list_del(&tmp->list);
kfree(tmp);
ret = -1;
diff --git a/drivers/gpu/drm/drm_scatter.c b/drivers/gpu/drm/drm_scatter.c
index d15e09b0ae0b..7525e0311e59 100644
--- a/drivers/gpu/drm/drm_scatter.c
+++ b/drivers/gpu/drm/drm_scatter.c
@@ -83,30 +83,26 @@ int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request)
if (dev->sg)
return -EINVAL;
- entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
- memset(entry, 0, sizeof(*entry));
pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
DRM_DEBUG("size=%ld pages=%ld\n", request->size, pages);
entry->pages = pages;
- entry->pagelist = kmalloc(pages * sizeof(*entry->pagelist), GFP_KERNEL);
+ entry->pagelist = kcalloc(pages, sizeof(*entry->pagelist), GFP_KERNEL);
if (!entry->pagelist) {
kfree(entry);
return -ENOMEM;
}
- memset(entry->pagelist, 0, pages * sizeof(*entry->pagelist));
-
- entry->busaddr = kmalloc(pages * sizeof(*entry->busaddr), GFP_KERNEL);
+ entry->busaddr = kcalloc(pages, sizeof(*entry->busaddr), GFP_KERNEL);
if (!entry->busaddr) {
kfree(entry->pagelist);
kfree(entry);
return -ENOMEM;
}
- memset((void *)entry->busaddr, 0, pages * sizeof(*entry->busaddr));
entry->virtual = drm_vmalloc_dma(pages << PAGE_SHIFT);
if (!entry->virtual) {
diff --git a/drivers/gpu/drm/drm_sman.c b/drivers/gpu/drm/drm_sman.c
index 34664587a74e..cebce45f4429 100644
--- a/drivers/gpu/drm/drm_sman.c
+++ b/drivers/gpu/drm/drm_sman.c
@@ -36,6 +36,7 @@
* Thomas Hellström <thomas-at-tungstengraphics-dot-com>
*/
+#include <linux/export.h>
#include "drm_sman.h"
struct drm_owner_item {
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 2eee8e016b38..0f9ef9bf6730 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -16,6 +16,7 @@
#include <linux/kdev_t.h>
#include <linux/gfp.h>
#include <linux/err.h>
+#include <linux/export.h>
#include "drm_sysfs.h"
#include "drm_core.h"
diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c
index 206d2300d873..445003f4dc93 100644
--- a/drivers/gpu/drm/drm_usb.c
+++ b/drivers/gpu/drm/drm_usb.c
@@ -1,5 +1,6 @@
#include "drmP.h"
#include <linux/usb.h>
+#include <linux/export.h>
#ifdef CONFIG_USB
int drm_get_usb_dev(struct usb_interface *interface,
diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
index 5db96d45fc71..8c03eaf41448 100644
--- a/drivers/gpu/drm/drm_vm.c
+++ b/drivers/gpu/drm/drm_vm.c
@@ -34,6 +34,7 @@
*/
#include "drmP.h"
+#include <linux/export.h>
#if defined(__ia64__)
#include <linux/efi.h>
#include <linux/slab.h>
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
new file mode 100644
index 000000000000..847466aab435
--- /dev/null
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -0,0 +1,20 @@
+config DRM_EXYNOS
+ tristate "DRM Support for Samsung SoC EXYNOS Series"
+ depends on DRM && PLAT_SAMSUNG
+ default n
+ select DRM_KMS_HELPER
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+ help
+ Choose this option if you have a Samsung SoC EXYNOS chipset.
+ If M is selected the module will be called exynosdrm.
+
+config DRM_EXYNOS_FIMD
+ tristate "Exynos DRM FIMD"
+ depends on DRM_EXYNOS
+ default n
+ help
+ Choose this option if you want to use Exynos FIMD for DRM.
+ If M is selected, the module will be called exynos_drm_fimd
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
new file mode 100644
index 000000000000..0496d3ff2683
--- /dev/null
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the drm device driver. This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos
+exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
+ exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \
+ exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o
+
+obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
+obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c
new file mode 100644
index 000000000000..6f8afea94fc9
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c
@@ -0,0 +1,110 @@
+/* exynos_drm_buf.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Author: Inki Dae <inki.dae@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm.h"
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_buf.h"
+
+static DEFINE_MUTEX(exynos_drm_buf_lock);
+
+static int lowlevel_buffer_allocate(struct drm_device *dev,
+ struct exynos_drm_buf_entry *entry)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ entry->vaddr = dma_alloc_writecombine(dev->dev, entry->size,
+ (dma_addr_t *)&entry->paddr, GFP_KERNEL);
+ if (!entry->paddr) {
+ DRM_ERROR("failed to allocate buffer.\n");
+ return -ENOMEM;
+ }
+
+ DRM_DEBUG_KMS("allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n",
+ (unsigned int)entry->vaddr, entry->paddr, entry->size);
+
+ return 0;
+}
+
+static void lowlevel_buffer_deallocate(struct drm_device *dev,
+ struct exynos_drm_buf_entry *entry)
+{
+ DRM_DEBUG_KMS("%s.\n", __FILE__);
+
+ if (entry->paddr && entry->vaddr && entry->size)
+ dma_free_writecombine(dev->dev, entry->size, entry->vaddr,
+ entry->paddr);
+ else
+ DRM_DEBUG_KMS("entry data is null.\n");
+}
+
+struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev,
+ unsigned int size)
+{
+ struct exynos_drm_buf_entry *entry;
+
+ DRM_DEBUG_KMS("%s.\n", __FILE__);
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ DRM_ERROR("failed to allocate exynos_drm_buf_entry.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ entry->size = size;
+
+ /*
+ * allocate memory region with size and set the memory information
+ * to vaddr and paddr of a entry object.
+ */
+ if (lowlevel_buffer_allocate(dev, entry) < 0) {
+ kfree(entry);
+ entry = NULL;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return entry;
+}
+
+void exynos_drm_buf_destroy(struct drm_device *dev,
+ struct exynos_drm_buf_entry *entry)
+{
+ DRM_DEBUG_KMS("%s.\n", __FILE__);
+
+ if (!entry) {
+ DRM_DEBUG_KMS("entry is null.\n");
+ return;
+ }
+
+ lowlevel_buffer_deallocate(dev, entry);
+
+ kfree(entry);
+ entry = NULL;
+}
+
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC DRM Buffer Management Module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.h b/drivers/gpu/drm/exynos/exynos_drm_buf.h
new file mode 100644
index 000000000000..045d59eab01a
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_buf.h
@@ -0,0 +1,53 @@
+/* exynos_drm_buf.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Author: Inki Dae <inki.dae@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_BUF_H_
+#define _EXYNOS_DRM_BUF_H_
+
+/*
+ * exynos drm buffer entry structure.
+ *
+ * @paddr: physical address of allocated memory.
+ * @vaddr: kernel virtual address of allocated memory.
+ * @size: size of allocated memory.
+ */
+struct exynos_drm_buf_entry {
+ dma_addr_t paddr;
+ void __iomem *vaddr;
+ unsigned int size;
+};
+
+/* allocate physical memory. */
+struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev,
+ unsigned int size);
+
+/* get physical memory information of a drm framebuffer. */
+struct exynos_drm_buf_entry *exynos_drm_fb_get_buf(struct drm_framebuffer *fb);
+
+/* remove allocated physical memory. */
+void exynos_drm_buf_destroy(struct drm_device *dev,
+ struct exynos_drm_buf_entry *entry);
+
+#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c
new file mode 100644
index 000000000000..985d9e768728
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_encoder.h"
+
+#define MAX_EDID 256
+#define to_exynos_connector(x) container_of(x, struct exynos_drm_connector,\
+ drm_connector)
+
+struct exynos_drm_connector {
+ struct drm_connector drm_connector;
+};
+
+/* convert exynos_video_timings to drm_display_mode */
+static inline void
+convert_to_display_mode(struct drm_display_mode *mode,
+ struct fb_videomode *timing)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ mode->clock = timing->pixclock / 1000;
+
+ mode->hdisplay = timing->xres;
+ mode->hsync_start = mode->hdisplay + timing->left_margin;
+ mode->hsync_end = mode->hsync_start + timing->hsync_len;
+ mode->htotal = mode->hsync_end + timing->right_margin;
+
+ mode->vdisplay = timing->yres;
+ mode->vsync_start = mode->vdisplay + timing->upper_margin;
+ mode->vsync_end = mode->vsync_start + timing->vsync_len;
+ mode->vtotal = mode->vsync_end + timing->lower_margin;
+}
+
+/* convert drm_display_mode to exynos_video_timings */
+static inline void
+convert_to_video_timing(struct fb_videomode *timing,
+ struct drm_display_mode *mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ memset(timing, 0, sizeof(*timing));
+
+ timing->pixclock = mode->clock * 1000;
+ timing->refresh = mode->vrefresh;
+
+ timing->xres = mode->hdisplay;
+ timing->left_margin = mode->hsync_start - mode->hdisplay;
+ timing->hsync_len = mode->hsync_end - mode->hsync_start;
+ timing->right_margin = mode->htotal - mode->hsync_end;
+
+ timing->yres = mode->vdisplay;
+ timing->upper_margin = mode->vsync_start - mode->vdisplay;
+ timing->vsync_len = mode->vsync_end - mode->vsync_start;
+ timing->lower_margin = mode->vtotal - mode->vsync_end;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ timing->vmode = FB_VMODE_INTERLACED;
+ else
+ timing->vmode = FB_VMODE_NONINTERLACED;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ timing->vmode |= FB_VMODE_DOUBLE;
+}
+
+static int exynos_drm_connector_get_modes(struct drm_connector *connector)
+{
+ struct exynos_drm_manager *manager =
+ exynos_drm_get_manager(connector->encoder);
+ struct exynos_drm_display *display = manager->display;
+ unsigned int count;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!display) {
+ DRM_DEBUG_KMS("display is null.\n");
+ return 0;
+ }
+
+ /*
+ * if get_edid() exists then get_edid() callback of hdmi side
+ * is called to get edid data through i2c interface else
+ * get timing from the FIMD driver(display controller).
+ *
+ * P.S. in case of lcd panel, count is always 1 if success
+ * because lcd panel has only one mode.
+ */
+ if (display->get_edid) {
+ int ret;
+ void *edid;
+
+ edid = kzalloc(MAX_EDID, GFP_KERNEL);
+ if (!edid) {
+ DRM_ERROR("failed to allocate edid\n");
+ return 0;
+ }
+
+ ret = display->get_edid(manager->dev, connector,
+ edid, MAX_EDID);
+ if (ret < 0) {
+ DRM_ERROR("failed to get edid data.\n");
+ kfree(edid);
+ edid = NULL;
+ return 0;
+ }
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ count = drm_add_edid_modes(connector, edid);
+
+ kfree(connector->display_info.raw_edid);
+ connector->display_info.raw_edid = edid;
+ } else {
+ struct drm_display_mode *mode = drm_mode_create(connector->dev);
+ struct fb_videomode *timing;
+
+ if (display->get_timing)
+ timing = display->get_timing(manager->dev);
+ else {
+ drm_mode_destroy(connector->dev, mode);
+ return 0;
+ }
+
+ convert_to_display_mode(mode, timing);
+
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ count = 1;
+ }
+
+ return count;
+}
+
+static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct exynos_drm_manager *manager =
+ exynos_drm_get_manager(connector->encoder);
+ struct exynos_drm_display *display = manager->display;
+ struct fb_videomode timing;
+ int ret = MODE_BAD;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ convert_to_video_timing(&timing, mode);
+
+ if (display && display->check_timing)
+ if (!display->check_timing(manager->dev, (void *)&timing))
+ ret = MODE_OK;
+
+ return ret;
+}
+
+struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return connector->encoder;
+}
+
+static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
+ .get_modes = exynos_drm_connector_get_modes,
+ .mode_valid = exynos_drm_connector_mode_valid,
+ .best_encoder = exynos_drm_best_encoder,
+};
+
+/* get detection status of display device. */
+static enum drm_connector_status
+exynos_drm_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct exynos_drm_manager *manager =
+ exynos_drm_get_manager(connector->encoder);
+ struct exynos_drm_display *display = manager->display;
+ enum drm_connector_status status = connector_status_disconnected;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (display && display->is_connected) {
+ if (display->is_connected(manager->dev))
+ status = connector_status_connected;
+ else
+ status = connector_status_disconnected;
+ }
+
+ return status;
+}
+
+static void exynos_drm_connector_destroy(struct drm_connector *connector)
+{
+ struct exynos_drm_connector *exynos_connector =
+ to_exynos_connector(connector);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(exynos_connector);
+}
+
+static struct drm_connector_funcs exynos_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = exynos_drm_connector_detect,
+ .destroy = exynos_drm_connector_destroy,
+};
+
+struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
+ struct drm_encoder *encoder)
+{
+ struct exynos_drm_connector *exynos_connector;
+ struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+ struct drm_connector *connector;
+ int type;
+ int err;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL);
+ if (!exynos_connector) {
+ DRM_ERROR("failed to allocate connector\n");
+ return NULL;
+ }
+
+ connector = &exynos_connector->drm_connector;
+
+ switch (manager->display->type) {
+ case EXYNOS_DISPLAY_TYPE_HDMI:
+ type = DRM_MODE_CONNECTOR_HDMIA;
+ break;
+ default:
+ type = DRM_MODE_CONNECTOR_Unknown;
+ break;
+ }
+
+ drm_connector_init(dev, connector, &exynos_connector_funcs, type);
+ drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
+
+ err = drm_sysfs_connector_add(connector);
+ if (err)
+ goto err_connector;
+
+ connector->encoder = encoder;
+ err = drm_mode_connector_attach_encoder(connector, encoder);
+ if (err) {
+ DRM_ERROR("failed to attach a connector to a encoder\n");
+ goto err_sysfs;
+ }
+
+ DRM_DEBUG_KMS("connector has been created\n");
+
+ return connector;
+
+err_sysfs:
+ drm_sysfs_connector_remove(connector);
+err_connector:
+ drm_connector_cleanup(connector);
+ kfree(exynos_connector);
+ return NULL;
+}
+
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC DRM Connector Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.h b/drivers/gpu/drm/exynos/exynos_drm_connector.h
new file mode 100644
index 000000000000..1c7b2b5b579c
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_CONNECTOR_H_
+#define _EXYNOS_DRM_CONNECTOR_H_
+
+struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
+ struct drm_encoder *encoder);
+
+#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
new file mode 100644
index 000000000000..661a03571d0c
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -0,0 +1,272 @@
+/* exynos_drm_core.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Author:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "exynos_drm_drv.h"
+#include "exynos_drm_encoder.h"
+#include "exynos_drm_connector.h"
+#include "exynos_drm_fbdev.h"
+
+static DEFINE_MUTEX(exynos_drm_mutex);
+static LIST_HEAD(exynos_drm_subdrv_list);
+static struct drm_device *drm_dev;
+
+static int exynos_drm_subdrv_probe(struct drm_device *dev,
+ struct exynos_drm_subdrv *subdrv)
+{
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (subdrv->probe) {
+ int ret;
+
+ /*
+ * this probe callback would be called by sub driver
+ * after setting of all resources to this sub driver,
+ * such as clock, irq and register map are done or by load()
+ * of exynos drm driver.
+ *
+ * P.S. note that this driver is considered for modularization.
+ */
+ ret = subdrv->probe(dev, subdrv->manager.dev);
+ if (ret)
+ return ret;
+ }
+
+ /* create and initialize a encoder for this sub driver. */
+ encoder = exynos_drm_encoder_create(dev, &subdrv->manager,
+ (1 << MAX_CRTC) - 1);
+ if (!encoder) {
+ DRM_ERROR("failed to create encoder\n");
+ return -EFAULT;
+ }
+
+ /*
+ * create and initialize a connector for this sub driver and
+ * attach the encoder created above to the connector.
+ */
+ connector = exynos_drm_connector_create(dev, encoder);
+ if (!connector) {
+ DRM_ERROR("failed to create connector\n");
+ encoder->funcs->destroy(encoder);
+ return -EFAULT;
+ }
+
+ subdrv->encoder = encoder;
+ subdrv->connector = connector;
+
+ return 0;
+}
+
+static void exynos_drm_subdrv_remove(struct drm_device *dev,
+ struct exynos_drm_subdrv *subdrv)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (subdrv->remove)
+ subdrv->remove(dev);
+
+ if (subdrv->encoder) {
+ struct drm_encoder *encoder = subdrv->encoder;
+ encoder->funcs->destroy(encoder);
+ subdrv->encoder = NULL;
+ }
+
+ if (subdrv->connector) {
+ struct drm_connector *connector = subdrv->connector;
+ connector->funcs->destroy(connector);
+ subdrv->connector = NULL;
+ }
+}
+
+int exynos_drm_device_register(struct drm_device *dev)
+{
+ struct exynos_drm_subdrv *subdrv, *n;
+ int err;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (!dev)
+ return -EINVAL;
+
+ if (drm_dev) {
+ DRM_ERROR("Already drm device were registered\n");
+ return -EBUSY;
+ }
+
+ mutex_lock(&exynos_drm_mutex);
+ list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
+ err = exynos_drm_subdrv_probe(dev, subdrv);
+ if (err) {
+ DRM_DEBUG("exynos drm subdrv probe failed.\n");
+ list_del(&subdrv->list);
+ }
+ }
+
+ drm_dev = dev;
+ mutex_unlock(&exynos_drm_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(exynos_drm_device_register);
+
+int exynos_drm_device_unregister(struct drm_device *dev)
+{
+ struct exynos_drm_subdrv *subdrv;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (!dev || dev != drm_dev) {
+ WARN(1, "Unexpected drm device unregister!\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&exynos_drm_mutex);
+ list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list)
+ exynos_drm_subdrv_remove(dev, subdrv);
+
+ drm_dev = NULL;
+ mutex_unlock(&exynos_drm_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
+
+static int exynos_drm_mode_group_reinit(struct drm_device *dev)
+{
+ struct drm_mode_group *group = &dev->primary->mode_group;
+ uint32_t *id_list = group->id_list;
+ int ret;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ ret = drm_mode_group_init_legacy_group(dev, group);
+ if (ret < 0)
+ return ret;
+
+ kfree(id_list);
+ return 0;
+}
+
+int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
+{
+ int err;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (!subdrv)
+ return -EINVAL;
+
+ mutex_lock(&exynos_drm_mutex);
+ if (drm_dev) {
+ err = exynos_drm_subdrv_probe(drm_dev, subdrv);
+ if (err) {
+ DRM_ERROR("failed to probe exynos drm subdrv\n");
+ mutex_unlock(&exynos_drm_mutex);
+ return err;
+ }
+
+ /*
+ * if any specific driver such as fimd or hdmi driver called
+ * exynos_drm_subdrv_register() later than drm_load(),
+ * the fb helper should be re-initialized and re-configured.
+ */
+ err = exynos_drm_fbdev_reinit(drm_dev);
+ if (err) {
+ DRM_ERROR("failed to reinitialize exynos drm fbdev\n");
+ exynos_drm_subdrv_remove(drm_dev, subdrv);
+ mutex_unlock(&exynos_drm_mutex);
+ return err;
+ }
+
+ err = exynos_drm_mode_group_reinit(drm_dev);
+ if (err) {
+ DRM_ERROR("failed to reinitialize mode group\n");
+ exynos_drm_fbdev_fini(drm_dev);
+ exynos_drm_subdrv_remove(drm_dev, subdrv);
+ mutex_unlock(&exynos_drm_mutex);
+ return err;
+ }
+ }
+
+ subdrv->drm_dev = drm_dev;
+
+ list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
+ mutex_unlock(&exynos_drm_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
+
+int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
+{
+ int ret = -EFAULT;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (!subdrv) {
+ DRM_DEBUG("Unexpected exynos drm subdrv unregister!\n");
+ return ret;
+ }
+
+ mutex_lock(&exynos_drm_mutex);
+ if (drm_dev) {
+ exynos_drm_subdrv_remove(drm_dev, subdrv);
+ list_del(&subdrv->list);
+
+ /*
+ * fb helper should be updated once a sub driver is released
+ * to re-configure crtc and connector and also to re-setup
+ * drm framebuffer.
+ */
+ ret = exynos_drm_fbdev_reinit(drm_dev);
+ if (ret < 0) {
+ DRM_ERROR("failed fb helper reinit.\n");
+ goto fail;
+ }
+
+ ret = exynos_drm_mode_group_reinit(drm_dev);
+ if (ret < 0) {
+ DRM_ERROR("failed drm mode group reinit.\n");
+ goto fail;
+ }
+ }
+
+fail:
+ mutex_unlock(&exynos_drm_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
+
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC DRM Core Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
new file mode 100644
index 000000000000..9337e5e2dbb6
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -0,0 +1,381 @@
+/* exynos_drm_crtc.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_fb.h"
+#include "exynos_drm_encoder.h"
+#include "exynos_drm_buf.h"
+
+#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\
+ drm_crtc)
+
+/*
+ * Exynos specific crtc postion structure.
+ *
+ * @fb_x: offset x on a framebuffer to be displyed
+ * - the unit is screen coordinates.
+ * @fb_y: offset y on a framebuffer to be displayed
+ * - the unit is screen coordinates.
+ * @crtc_x: offset x on hardware screen.
+ * @crtc_y: offset y on hardware screen.
+ * @crtc_w: width of hardware screen.
+ * @crtc_h: height of hardware screen.
+ */
+struct exynos_drm_crtc_pos {
+ unsigned int fb_x;
+ unsigned int fb_y;
+ unsigned int crtc_x;
+ unsigned int crtc_y;
+ unsigned int crtc_w;
+ unsigned int crtc_h;
+};
+
+/*
+ * Exynos specific crtc structure.
+ *
+ * @drm_crtc: crtc object.
+ * @overlay: contain information common to display controller and hdmi and
+ * contents of this overlay object would be copied to sub driver size.
+ * @pipe: a crtc index created at load() with a new crtc object creation
+ * and the crtc object would be set to private->crtc array
+ * to get a crtc object corresponding to this pipe from private->crtc
+ * array when irq interrupt occured. the reason of using this pipe is that
+ * drm framework doesn't support multiple irq yet.
+ * we can refer to the crtc to current hardware interrupt occured through
+ * this pipe value.
+ */
+struct exynos_drm_crtc {
+ struct drm_crtc drm_crtc;
+ struct exynos_drm_overlay overlay;
+ unsigned int pipe;
+};
+
+static void exynos_drm_crtc_apply(struct drm_crtc *crtc)
+{
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct exynos_drm_overlay *overlay = &exynos_crtc->overlay;
+
+ exynos_drm_fn_encoder(crtc, overlay,
+ exynos_drm_encoder_crtc_mode_set);
+ exynos_drm_fn_encoder(crtc, NULL, exynos_drm_encoder_crtc_commit);
+}
+
+static int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
+ struct drm_framebuffer *fb,
+ struct drm_display_mode *mode,
+ struct exynos_drm_crtc_pos *pos)
+{
+ struct exynos_drm_buf_entry *entry;
+ unsigned int actual_w;
+ unsigned int actual_h;
+
+ entry = exynos_drm_fb_get_buf(fb);
+ if (!entry) {
+ DRM_LOG_KMS("entry is null.\n");
+ return -EFAULT;
+ }
+
+ overlay->paddr = entry->paddr;
+ overlay->vaddr = entry->vaddr;
+
+ DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n",
+ (unsigned long)overlay->vaddr,
+ (unsigned long)overlay->paddr);
+
+ actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w);
+ actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h);
+
+ /* set drm framebuffer data. */
+ overlay->fb_x = pos->fb_x;
+ overlay->fb_y = pos->fb_y;
+ overlay->fb_width = fb->width;
+ overlay->fb_height = fb->height;
+ overlay->bpp = fb->bits_per_pixel;
+ overlay->pitch = fb->pitch;
+
+ /* set overlay range to be displayed. */
+ overlay->crtc_x = pos->crtc_x;
+ overlay->crtc_y = pos->crtc_y;
+ overlay->crtc_width = actual_w;
+ overlay->crtc_height = actual_h;
+
+ /* set drm mode data. */
+ overlay->mode_width = mode->hdisplay;
+ overlay->mode_height = mode->vdisplay;
+ overlay->refresh = mode->vrefresh;
+ overlay->scan_flag = mode->flags;
+
+ DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
+ overlay->crtc_x, overlay->crtc_y,
+ overlay->crtc_width, overlay->crtc_height);
+
+ return 0;
+}
+
+static int exynos_drm_crtc_update(struct drm_crtc *crtc)
+{
+ struct exynos_drm_crtc *exynos_crtc;
+ struct exynos_drm_overlay *overlay;
+ struct exynos_drm_crtc_pos pos;
+ struct drm_display_mode *mode = &crtc->mode;
+ struct drm_framebuffer *fb = crtc->fb;
+
+ if (!mode || !fb)
+ return -EINVAL;
+
+ exynos_crtc = to_exynos_crtc(crtc);
+ overlay = &exynos_crtc->overlay;
+
+ memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos));
+
+ /* it means the offset of framebuffer to be displayed. */
+ pos.fb_x = crtc->x;
+ pos.fb_y = crtc->y;
+
+ /* OSD position to be displayed. */
+ pos.crtc_x = 0;
+ pos.crtc_y = 0;
+ pos.crtc_w = fb->width - crtc->x;
+ pos.crtc_h = fb->height - crtc->y;
+
+ return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos);
+}
+
+static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO */
+}
+
+static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* drm framework doesn't check NULL. */
+}
+
+static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* drm framework doesn't check NULL. */
+}
+
+static bool
+exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* drm framework doesn't check NULL */
+ return true;
+}
+
+static int
+exynos_drm_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)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ mode = adjusted_mode;
+
+ return exynos_drm_crtc_update(crtc);
+}
+
+static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ ret = exynos_drm_crtc_update(crtc);
+ if (ret)
+ return ret;
+
+ exynos_drm_crtc_apply(crtc);
+
+ return ret;
+}
+
+static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+ /* drm framework doesn't check NULL */
+}
+
+static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
+ .dpms = exynos_drm_crtc_dpms,
+ .prepare = exynos_drm_crtc_prepare,
+ .commit = exynos_drm_crtc_commit,
+ .mode_fixup = exynos_drm_crtc_mode_fixup,
+ .mode_set = exynos_drm_crtc_mode_set,
+ .mode_set_base = exynos_drm_crtc_mode_set_base,
+ .load_lut = exynos_drm_crtc_load_lut,
+};
+
+static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event)
+{
+ struct drm_device *dev = crtc->dev;
+ struct exynos_drm_private *dev_priv = dev->dev_private;
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct drm_framebuffer *old_fb = crtc->fb;
+ int ret = -EINVAL;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ mutex_lock(&dev->struct_mutex);
+
+ if (event) {
+ /*
+ * the pipe from user always is 0 so we can set pipe number
+ * of current owner to event.
+ */
+ event->pipe = exynos_crtc->pipe;
+
+ list_add_tail(&event->base.link,
+ &dev_priv->pageflip_event_list);
+
+ ret = drm_vblank_get(dev, exynos_crtc->pipe);
+ if (ret) {
+ DRM_DEBUG("failed to acquire vblank counter\n");
+ list_del(&event->base.link);
+
+ goto out;
+ }
+
+ crtc->fb = fb;
+ ret = exynos_drm_crtc_update(crtc);
+ if (ret) {
+ crtc->fb = old_fb;
+ drm_vblank_put(dev, exynos_crtc->pipe);
+ list_del(&event->base.link);
+
+ goto out;
+ }
+
+ /*
+ * the values related to a buffer of the drm framebuffer
+ * to be applied should be set at here. because these values
+ * first, are set to shadow registers and then to
+ * real registers at vsync front porch period.
+ */
+ exynos_drm_crtc_apply(crtc);
+ }
+out:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct exynos_drm_private *private = crtc->dev->dev_private;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ private->crtc[exynos_crtc->pipe] = NULL;
+
+ drm_crtc_cleanup(crtc);
+ kfree(exynos_crtc);
+}
+
+static struct drm_crtc_funcs exynos_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .page_flip = exynos_drm_crtc_page_flip,
+ .destroy = exynos_drm_crtc_destroy,
+};
+
+struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev,
+ struct drm_crtc *crtc)
+{
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+
+ return &exynos_crtc->overlay;
+}
+
+int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
+{
+ struct exynos_drm_crtc *exynos_crtc;
+ struct exynos_drm_private *private = dev->dev_private;
+ struct drm_crtc *crtc;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
+ if (!exynos_crtc) {
+ DRM_ERROR("failed to allocate exynos crtc\n");
+ return -ENOMEM;
+ }
+
+ exynos_crtc->pipe = nr;
+ crtc = &exynos_crtc->drm_crtc;
+
+ private->crtc[nr] = crtc;
+
+ drm_crtc_init(dev, crtc, &exynos_crtc_funcs);
+ drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
+
+ return 0;
+}
+
+int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
+{
+ struct exynos_drm_private *private = dev->dev_private;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
+ exynos_drm_enable_vblank);
+
+ return 0;
+}
+
+void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
+{
+ struct exynos_drm_private *private = dev->dev_private;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
+ exynos_drm_disable_vblank);
+}
+
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC DRM CRTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
new file mode 100644
index 000000000000..c584042d6d2c
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
@@ -0,0 +1,38 @@
+/* exynos_drm_crtc.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_CRTC_H_
+#define _EXYNOS_DRM_CRTC_H_
+
+struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev,
+ struct drm_crtc *crtc);
+int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr);
+int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
+void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
+
+#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
new file mode 100644
index 000000000000..83810cbe3c17
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm.h"
+
+#include <drm/exynos_drm.h>
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_crtc.h"
+#include "exynos_drm_fbdev.h"
+#include "exynos_drm_fb.h"
+#include "exynos_drm_gem.h"
+
+#define DRIVER_NAME "exynos-drm"
+#define DRIVER_DESC "Samsung SoC DRM"
+#define DRIVER_DATE "20110530"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
+{
+ struct exynos_drm_private *private;
+ int ret;
+ int nr;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL);
+ if (!private) {
+ DRM_ERROR("failed to allocate private\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&private->pageflip_event_list);
+ dev->dev_private = (void *)private;
+
+ drm_mode_config_init(dev);
+
+ exynos_drm_mode_config_init(dev);
+
+ /*
+ * EXYNOS4 is enough to have two CRTCs and each crtc would be used
+ * without dependency of hardware.
+ */
+ for (nr = 0; nr < MAX_CRTC; nr++) {
+ ret = exynos_drm_crtc_create(dev, nr);
+ if (ret)
+ goto err_crtc;
+ }
+
+ ret = drm_vblank_init(dev, MAX_CRTC);
+ if (ret)
+ goto err_crtc;
+
+ /*
+ * probe sub drivers such as display controller and hdmi driver,
+ * that were registered at probe() of platform driver
+ * to the sub driver and create encoder and connector for them.
+ */
+ ret = exynos_drm_device_register(dev);
+ if (ret)
+ goto err_vblank;
+
+ /*
+ * create and configure fb helper and also exynos specific
+ * fbdev object.
+ */
+ ret = exynos_drm_fbdev_init(dev);
+ if (ret) {
+ DRM_ERROR("failed to initialize drm fbdev\n");
+ goto err_drm_device;
+ }
+
+ return 0;
+
+err_drm_device:
+ exynos_drm_device_unregister(dev);
+err_vblank:
+ drm_vblank_cleanup(dev);
+err_crtc:
+ drm_mode_config_cleanup(dev);
+ kfree(private);
+
+ return ret;
+}
+
+static int exynos_drm_unload(struct drm_device *dev)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ exynos_drm_fbdev_fini(dev);
+ exynos_drm_device_unregister(dev);
+ drm_vblank_cleanup(dev);
+ drm_mode_config_cleanup(dev);
+ kfree(dev->dev_private);
+
+ dev->dev_private = NULL;
+
+ return 0;
+}
+
+static void exynos_drm_preclose(struct drm_device *dev,
+ struct drm_file *file_priv)
+{
+ struct exynos_drm_private *dev_priv = dev->dev_private;
+
+ /*
+ * drm framework frees all events at release time,
+ * so private event list should be cleared.
+ */
+ if (!list_empty(&dev_priv->pageflip_event_list))
+ INIT_LIST_HEAD(&dev_priv->pageflip_event_list);
+}
+
+static void exynos_drm_lastclose(struct drm_device *dev)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ exynos_drm_fbdev_restore_mode(dev);
+}
+
+static struct vm_operations_struct exynos_drm_gem_vm_ops = {
+ .fault = exynos_drm_gem_fault,
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static struct drm_ioctl_desc exynos_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl,
+ DRM_UNLOCKED | DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP_OFFSET,
+ exynos_drm_gem_map_offset_ioctl, DRM_UNLOCKED |
+ DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP,
+ exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
+};
+
+static struct drm_driver exynos_drm_driver = {
+ .driver_features = DRIVER_HAVE_IRQ | DRIVER_BUS_PLATFORM |
+ DRIVER_MODESET | DRIVER_GEM,
+ .load = exynos_drm_load,
+ .unload = exynos_drm_unload,
+ .preclose = exynos_drm_preclose,
+ .lastclose = exynos_drm_lastclose,
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = exynos_drm_crtc_enable_vblank,
+ .disable_vblank = exynos_drm_crtc_disable_vblank,
+ .gem_init_object = exynos_drm_gem_init_object,
+ .gem_free_object = exynos_drm_gem_free_object,
+ .gem_vm_ops = &exynos_drm_gem_vm_ops,
+ .dumb_create = exynos_drm_gem_dumb_create,
+ .dumb_map_offset = exynos_drm_gem_dumb_map_offset,
+ .dumb_destroy = exynos_drm_gem_dumb_destroy,
+ .ioctls = exynos_ioctls,
+ .fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = exynos_drm_gem_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+ .release = drm_release,
+ },
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+static int exynos_drm_platform_probe(struct platform_device *pdev)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls);
+
+ return drm_platform_init(&exynos_drm_driver, pdev);
+}
+
+static int exynos_drm_platform_remove(struct platform_device *pdev)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ drm_platform_exit(&exynos_drm_driver, pdev);
+
+ return 0;
+}
+
+static struct platform_driver exynos_drm_platform_driver = {
+ .probe = exynos_drm_platform_probe,
+ .remove = __devexit_p(exynos_drm_platform_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init exynos_drm_init(void)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ return platform_driver_register(&exynos_drm_platform_driver);
+}
+
+static void __exit exynos_drm_exit(void)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ platform_driver_unregister(&exynos_drm_platform_driver);
+}
+
+module_init(exynos_drm_init);
+module_exit(exynos_drm_exit);
+
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
new file mode 100644
index 000000000000..c03683f2ae72
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -0,0 +1,254 @@
+/* exynos_drm_drv.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_DRV_H_
+#define _EXYNOS_DRM_DRV_H_
+
+#include "drm.h"
+
+#define MAX_CRTC 2
+
+struct drm_device;
+struct exynos_drm_overlay;
+struct drm_connector;
+
+/* this enumerates display type. */
+enum exynos_drm_output_type {
+ EXYNOS_DISPLAY_TYPE_NONE,
+ /* RGB or CPU Interface. */
+ EXYNOS_DISPLAY_TYPE_LCD,
+ /* HDMI Interface. */
+ EXYNOS_DISPLAY_TYPE_HDMI,
+};
+
+/*
+ * Exynos drm overlay ops structure.
+ *
+ * @mode_set: copy drm overlay info to hw specific overlay info.
+ * @commit: apply hardware specific overlay data to registers.
+ * @disable: disable hardware specific overlay.
+ */
+struct exynos_drm_overlay_ops {
+ void (*mode_set)(struct device *subdrv_dev,
+ struct exynos_drm_overlay *overlay);
+ void (*commit)(struct device *subdrv_dev);
+ void (*disable)(struct device *subdrv_dev);
+};
+
+/*
+ * Exynos drm common overlay structure.
+ *
+ * @fb_x: offset x on a framebuffer to be displayed.
+ * - the unit is screen coordinates.
+ * @fb_y: offset y on a framebuffer to be displayed.
+ * - the unit is screen coordinates.
+ * @fb_width: width of a framebuffer.
+ * @fb_height: height of a framebuffer.
+ * @crtc_x: offset x on hardware screen.
+ * @crtc_y: offset y on hardware screen.
+ * @crtc_width: window width to be displayed (hardware screen).
+ * @crtc_height: window height to be displayed (hardware screen).
+ * @mode_width: width of screen mode.
+ * @mode_height: height of screen mode.
+ * @refresh: refresh rate.
+ * @scan_flag: interlace or progressive way.
+ * (it could be DRM_MODE_FLAG_*)
+ * @bpp: pixel size.(in bit)
+ * @paddr: bus(accessed by dma) physical memory address to this overlay
+ * and this is physically continuous.
+ * @vaddr: virtual memory addresss to this overlay.
+ * @default_win: a window to be enabled.
+ * @color_key: color key on or off.
+ * @index_color: if using color key feature then this value would be used
+ * as index color.
+ * @local_path: in case of lcd type, local path mode on or off.
+ * @transparency: transparency on or off.
+ * @activated: activated or not.
+ *
+ * this structure is common to exynos SoC and its contents would be copied
+ * to hardware specific overlay info.
+ */
+struct exynos_drm_overlay {
+ unsigned int fb_x;
+ unsigned int fb_y;
+ unsigned int fb_width;
+ unsigned int fb_height;
+ unsigned int crtc_x;
+ unsigned int crtc_y;
+ unsigned int crtc_width;
+ unsigned int crtc_height;
+ unsigned int mode_width;
+ unsigned int mode_height;
+ unsigned int refresh;
+ unsigned int scan_flag;
+ unsigned int bpp;
+ unsigned int pitch;
+ dma_addr_t paddr;
+ void __iomem *vaddr;
+
+ bool default_win;
+ bool color_key;
+ unsigned int index_color;
+ bool local_path;
+ bool transparency;
+ bool activated;
+};
+
+/*
+ * Exynos DRM Display Structure.
+ * - this structure is common to analog tv, digital tv and lcd panel.
+ *
+ * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
+ * @is_connected: check for that display is connected or not.
+ * @get_edid: get edid modes from display driver.
+ * @get_timing: get timing object from display driver.
+ * @check_timing: check if timing is valid or not.
+ * @power_on: display device on or off.
+ */
+struct exynos_drm_display {
+ enum exynos_drm_output_type type;
+ bool (*is_connected)(struct device *dev);
+ int (*get_edid)(struct device *dev, struct drm_connector *connector,
+ u8 *edid, int len);
+ void *(*get_timing)(struct device *dev);
+ int (*check_timing)(struct device *dev, void *timing);
+ int (*power_on)(struct device *dev, int mode);
+};
+
+/*
+ * Exynos drm manager ops
+ *
+ * @mode_set: convert drm_display_mode to hw specific display mode and
+ * would be called by encoder->mode_set().
+ * @commit: set current hw specific display mode to hw.
+ * @enable_vblank: specific driver callback for enabling vblank interrupt.
+ * @disable_vblank: specific driver callback for disabling vblank interrupt.
+ */
+struct exynos_drm_manager_ops {
+ void (*mode_set)(struct device *subdrv_dev, void *mode);
+ void (*commit)(struct device *subdrv_dev);
+ int (*enable_vblank)(struct device *subdrv_dev);
+ void (*disable_vblank)(struct device *subdrv_dev);
+};
+
+/*
+ * Exynos drm common manager structure.
+ *
+ * @dev: pointer to device object for subdrv device driver.
+ * sub drivers such as display controller or hdmi driver,
+ * have their own device object.
+ * @ops: pointer to callbacks for exynos drm specific framebuffer.
+ * these callbacks should be set by specific drivers such fimd
+ * or hdmi driver and are used to control hardware global registers.
+ * @overlay_ops: pointer to callbacks for exynos drm specific framebuffer.
+ * these callbacks should be set by specific drivers such fimd
+ * or hdmi driver and are used to control hardware overlay reigsters.
+ * @display: pointer to callbacks for exynos drm specific framebuffer.
+ * these callbacks should be set by specific drivers such fimd
+ * or hdmi driver and are used to control display devices such as
+ * analog tv, digital tv and lcd panel and also get timing data for them.
+ */
+struct exynos_drm_manager {
+ struct device *dev;
+ int pipe;
+ struct exynos_drm_manager_ops *ops;
+ struct exynos_drm_overlay_ops *overlay_ops;
+ struct exynos_drm_display *display;
+};
+
+/*
+ * Exynos drm private structure.
+ */
+struct exynos_drm_private {
+ struct drm_fb_helper *fb_helper;
+
+ /* list head for new event to be added. */
+ struct list_head pageflip_event_list;
+
+ /*
+ * created crtc object would be contained at this array and
+ * this array is used to be aware of which crtc did it request vblank.
+ */
+ struct drm_crtc *crtc[MAX_CRTC];
+};
+
+/*
+ * Exynos drm sub driver structure.
+ *
+ * @list: sub driver has its own list object to register to exynos drm driver.
+ * @drm_dev: pointer to drm_device and this pointer would be set
+ * when sub driver calls exynos_drm_subdrv_register().
+ * @probe: this callback would be called by exynos drm driver after
+ * subdrv is registered to it.
+ * @remove: this callback is used to release resources created
+ * by probe callback.
+ * @manager: subdrv has its own manager to control a hardware appropriately
+ * and we can access a hardware drawing on this manager.
+ * @encoder: encoder object owned by this sub driver.
+ * @connector: connector object owned by this sub driver.
+ */
+struct exynos_drm_subdrv {
+ struct list_head list;
+ struct drm_device *drm_dev;
+
+ int (*probe)(struct drm_device *drm_dev, struct device *dev);
+ void (*remove)(struct drm_device *dev);
+
+ struct exynos_drm_manager manager;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+};
+
+/*
+ * this function calls a probe callback registered to sub driver list and
+ * create its own encoder and connector and then set drm_device object
+ * to global one.
+ */
+int exynos_drm_device_register(struct drm_device *dev);
+/*
+ * this function calls a remove callback registered to sub driver list and
+ * destroy its own encoder and connetor.
+ */
+int exynos_drm_device_unregister(struct drm_device *dev);
+
+/*
+ * this function would be called by sub drivers such as display controller
+ * or hdmi driver to register this sub driver object to exynos drm driver
+ * and when a sub driver is registered to exynos drm driver a probe callback
+ * of the sub driver is called and creates its own encoder and connector
+ * and then fb helper and drm mode group would be re-initialized.
+ */
+int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
+
+/*
+ * this function removes subdrv list from exynos drm driver and fb helper
+ * and drm mode group would be re-initialized.
+ */
+int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
+
+#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
new file mode 100644
index 000000000000..7cf6fa86a67e
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -0,0 +1,271 @@
+/* exynos_drm_encoder.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_crtc.h"
+#include "exynos_drm_encoder.h"
+
+#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\
+ drm_encoder)
+
+/*
+ * exynos specific encoder structure.
+ *
+ * @drm_encoder: encoder object.
+ * @manager: specific encoder has its own manager to control a hardware
+ * appropriately and we can access a hardware drawing on this manager.
+ */
+struct exynos_drm_encoder {
+ struct drm_encoder drm_encoder;
+ struct exynos_drm_manager *manager;
+};
+
+static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_connector *connector;
+ struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+
+ DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder == encoder) {
+ struct exynos_drm_display *display = manager->display;
+
+ if (display && display->power_on)
+ display->power_on(manager->dev, mode);
+ }
+ }
+}
+
+static bool
+exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* drm framework doesn't check NULL. */
+
+ return true;
+}
+
+static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_connector *connector;
+ struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+ struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev,
+ encoder->crtc);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ mode = adjusted_mode;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder == encoder) {
+ if (manager_ops && manager_ops->mode_set)
+ manager_ops->mode_set(manager->dev, mode);
+
+ if (overlay_ops && overlay_ops->mode_set)
+ overlay_ops->mode_set(manager->dev, overlay);
+ }
+ }
+}
+
+static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* drm framework doesn't check NULL. */
+}
+
+static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
+{
+ struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+ struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (manager_ops && manager_ops->commit)
+ manager_ops->commit(manager->dev);
+
+ if (overlay_ops && overlay_ops->commit)
+ overlay_ops->commit(manager->dev);
+}
+
+static struct drm_crtc *
+exynos_drm_encoder_get_crtc(struct drm_encoder *encoder)
+{
+ return encoder->crtc;
+}
+
+static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
+ .dpms = exynos_drm_encoder_dpms,
+ .mode_fixup = exynos_drm_encoder_mode_fixup,
+ .mode_set = exynos_drm_encoder_mode_set,
+ .prepare = exynos_drm_encoder_prepare,
+ .commit = exynos_drm_encoder_commit,
+ .get_crtc = exynos_drm_encoder_get_crtc,
+};
+
+static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct exynos_drm_encoder *exynos_encoder =
+ to_exynos_encoder(encoder);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_encoder->manager->pipe = -1;
+
+ drm_encoder_cleanup(encoder);
+ encoder->dev->mode_config.num_encoder--;
+ kfree(exynos_encoder);
+}
+
+static struct drm_encoder_funcs exynos_encoder_funcs = {
+ .destroy = exynos_drm_encoder_destroy,
+};
+
+struct drm_encoder *
+exynos_drm_encoder_create(struct drm_device *dev,
+ struct exynos_drm_manager *manager,
+ unsigned int possible_crtcs)
+{
+ struct drm_encoder *encoder;
+ struct exynos_drm_encoder *exynos_encoder;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!manager || !possible_crtcs)
+ return NULL;
+
+ if (!manager->dev)
+ return NULL;
+
+ exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL);
+ if (!exynos_encoder) {
+ DRM_ERROR("failed to allocate encoder\n");
+ return NULL;
+ }
+
+ exynos_encoder->manager = manager;
+ encoder = &exynos_encoder->drm_encoder;
+ encoder->possible_crtcs = possible_crtcs;
+
+ DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+ drm_encoder_init(dev, encoder, &exynos_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
+ drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs);
+
+ DRM_DEBUG_KMS("encoder has been created\n");
+
+ return encoder;
+}
+
+struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder)
+{
+ return to_exynos_encoder(encoder)->manager;
+}
+
+void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
+ void (*fn)(struct drm_encoder *, void *))
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_encoder *encoder;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc != crtc)
+ continue;
+
+ fn(encoder, data);
+ }
+}
+
+void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data)
+{
+ struct exynos_drm_manager *manager =
+ to_exynos_encoder(encoder)->manager;
+ struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ int crtc = *(int *)data;
+
+ if (manager->pipe == -1)
+ manager->pipe = crtc;
+
+ if (manager_ops->enable_vblank)
+ manager_ops->enable_vblank(manager->dev);
+}
+
+void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
+{
+ struct exynos_drm_manager *manager =
+ to_exynos_encoder(encoder)->manager;
+ struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ int crtc = *(int *)data;
+
+ if (manager->pipe == -1)
+ manager->pipe = crtc;
+
+ if (manager_ops->disable_vblank)
+ manager_ops->disable_vblank(manager->dev);
+}
+
+void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
+{
+ struct exynos_drm_manager *manager =
+ to_exynos_encoder(encoder)->manager;
+ struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+
+ overlay_ops->commit(manager->dev);
+}
+
+void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data)
+{
+ struct exynos_drm_manager *manager =
+ to_exynos_encoder(encoder)->manager;
+ struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ struct exynos_drm_overlay *overlay = data;
+
+ overlay_ops->mode_set(manager->dev, overlay);
+}
+
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC DRM Encoder Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
new file mode 100644
index 000000000000..5ecd645d06a9
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_ENCODER_H_
+#define _EXYNOS_DRM_ENCODER_H_
+
+struct exynos_drm_manager;
+
+struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
+ struct exynos_drm_manager *mgr,
+ unsigned int possible_crtcs);
+struct exynos_drm_manager *
+exynos_drm_get_manager(struct drm_encoder *encoder);
+void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
+ void (*fn)(struct drm_encoder *, void *));
+void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data);
+void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data);
+
+#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
new file mode 100644
index 000000000000..48d29cfd5240
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -0,0 +1,265 @@
+/* exynos_drm_fb.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+#include "exynos_drm_fb.h"
+#include "exynos_drm_buf.h"
+#include "exynos_drm_gem.h"
+
+#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb)
+
+/*
+ * exynos specific framebuffer structure.
+ *
+ * @fb: drm framebuffer obejct.
+ * @exynos_gem_obj: exynos specific gem object containing a gem object.
+ * @entry: pointer to exynos drm buffer entry object.
+ * - containing only the information to physically continuous memory
+ * region allocated at default framebuffer creation.
+ */
+struct exynos_drm_fb {
+ struct drm_framebuffer fb;
+ struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_buf_entry *entry;
+};
+
+static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+ struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ drm_framebuffer_cleanup(fb);
+
+ /*
+ * default framebuffer has no gem object so
+ * a buffer of the default framebuffer should be released at here.
+ */
+ if (!exynos_fb->exynos_gem_obj && exynos_fb->entry)
+ exynos_drm_buf_destroy(fb->dev, exynos_fb->entry);
+
+ kfree(exynos_fb);
+ exynos_fb = NULL;
+}
+
+static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle)
+{
+ struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return drm_gem_handle_create(file_priv,
+ &exynos_fb->exynos_gem_obj->base, handle);
+}
+
+static int exynos_drm_fb_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file_priv, unsigned flags,
+ unsigned color, struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO */
+
+ return 0;
+}
+
+static struct drm_framebuffer_funcs exynos_drm_fb_funcs = {
+ .destroy = exynos_drm_fb_destroy,
+ .create_handle = exynos_drm_fb_create_handle,
+ .dirty = exynos_drm_fb_dirty,
+};
+
+static struct drm_framebuffer *
+exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev,
+ struct drm_mode_fb_cmd *mode_cmd)
+{
+ struct exynos_drm_fb *exynos_fb;
+ struct drm_framebuffer *fb;
+ struct exynos_drm_gem_obj *exynos_gem_obj = NULL;
+ struct drm_gem_object *obj;
+ unsigned int size;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ mode_cmd->pitch = max(mode_cmd->pitch,
+ mode_cmd->width * (mode_cmd->bpp >> 3));
+
+ DRM_LOG_KMS("drm fb create(%dx%d)\n",
+ mode_cmd->width, mode_cmd->height);
+
+ exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL);
+ if (!exynos_fb) {
+ DRM_ERROR("failed to allocate exynos drm framebuffer.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ fb = &exynos_fb->fb;
+ ret = drm_framebuffer_init(dev, fb, &exynos_drm_fb_funcs);
+ if (ret) {
+ DRM_ERROR("failed to initialize framebuffer.\n");
+ goto err_init;
+ }
+
+ DRM_LOG_KMS("create: fb id: %d\n", fb->base.id);
+
+ size = mode_cmd->pitch * mode_cmd->height;
+
+ /*
+ * mode_cmd->handle could be NULL at booting time or
+ * with user request. if NULL, a new buffer or a gem object
+ * would be allocated.
+ */
+ if (!mode_cmd->handle) {
+ if (!file_priv) {
+ struct exynos_drm_buf_entry *entry;
+
+ /*
+ * in case that file_priv is NULL, it allocates
+ * only buffer and this buffer would be used
+ * for default framebuffer.
+ */
+ entry = exynos_drm_buf_create(dev, size);
+ if (IS_ERR(entry)) {
+ ret = PTR_ERR(entry);
+ goto err_buffer;
+ }
+
+ exynos_fb->entry = entry;
+
+ DRM_LOG_KMS("default fb: paddr = 0x%lx, size = 0x%x\n",
+ (unsigned long)entry->paddr, size);
+
+ goto out;
+ } else {
+ exynos_gem_obj = exynos_drm_gem_create(file_priv, dev,
+ size,
+ &mode_cmd->handle);
+ if (IS_ERR(exynos_gem_obj)) {
+ ret = PTR_ERR(exynos_gem_obj);
+ goto err_buffer;
+ }
+ }
+ } else {
+ obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ goto err_buffer;
+ }
+
+ exynos_gem_obj = to_exynos_gem_obj(obj);
+
+ drm_gem_object_unreference_unlocked(obj);
+ }
+
+ /*
+ * if got a exynos_gem_obj from either a handle or
+ * a new creation then exynos_fb->exynos_gem_obj is NULL
+ * so that default framebuffer has no its own gem object,
+ * only its own buffer object.
+ */
+ exynos_fb->entry = exynos_gem_obj->entry;
+
+ DRM_LOG_KMS("paddr = 0x%lx, size = 0x%x, gem object = 0x%x\n",
+ (unsigned long)exynos_fb->entry->paddr, size,
+ (unsigned int)&exynos_gem_obj->base);
+
+out:
+ exynos_fb->exynos_gem_obj = exynos_gem_obj;
+
+ drm_helper_mode_fill_fb_struct(fb, mode_cmd);
+
+ return fb;
+
+err_buffer:
+ drm_framebuffer_cleanup(fb);
+
+err_init:
+ kfree(exynos_fb);
+
+ return ERR_PTR(ret);
+}
+
+struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev,
+ struct drm_file *file_priv,
+ struct drm_mode_fb_cmd *mode_cmd)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return exynos_drm_fb_init(file_priv, dev, mode_cmd);
+}
+
+struct exynos_drm_buf_entry *exynos_drm_fb_get_buf(struct drm_framebuffer *fb)
+{
+ struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
+ struct exynos_drm_buf_entry *entry;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ entry = exynos_fb->entry;
+ if (!entry)
+ return NULL;
+
+ DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n",
+ (unsigned long)entry->vaddr,
+ (unsigned long)entry->paddr);
+
+ return entry;
+}
+
+static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
+ .fb_create = exynos_drm_fb_create,
+};
+
+void exynos_drm_mode_config_init(struct drm_device *dev)
+{
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ /*
+ * set max width and height as default value(4096x4096).
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+
+ dev->mode_config.funcs = &exynos_drm_mode_config_funcs;
+}
+
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC DRM FB Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h
new file mode 100644
index 000000000000..eb35931d302c
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_FB_H_
+#define _EXYNOS_DRM_FB_H
+
+struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev,
+ struct drm_file *filp,
+ struct drm_mode_fb_cmd *mode_cmd);
+
+void exynos_drm_mode_config_init(struct drm_device *dev);
+
+#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
new file mode 100644
index 000000000000..1f4b3d1a7713
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -0,0 +1,456 @@
+/* exynos_drm_fbdev.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_fb_helper.h"
+#include "drm_crtc_helper.h"
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_fb.h"
+#include "exynos_drm_buf.h"
+
+#define MAX_CONNECTOR 4
+#define PREFERRED_BPP 32
+
+#define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\
+ drm_fb_helper)
+
+struct exynos_drm_fbdev {
+ struct drm_fb_helper drm_fb_helper;
+ struct drm_framebuffer *fb;
+};
+
+static int exynos_drm_fbdev_set_par(struct fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->var;
+
+ switch (var->bits_per_pixel) {
+ case 32:
+ case 24:
+ case 18:
+ case 16:
+ case 12:
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ break;
+ case 1:
+ info->fix.visual = FB_VISUAL_MONO01;
+ break;
+ default:
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+ }
+
+ info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
+
+ return drm_fb_helper_set_par(info);
+}
+
+
+static struct fb_ops exynos_drm_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = exynos_drm_fbdev_set_par,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
+ struct drm_framebuffer *fb,
+ unsigned int fb_width,
+ unsigned int fb_height)
+{
+ struct fb_info *fbi = helper->fbdev;
+ struct drm_device *dev = helper->dev;
+ struct exynos_drm_fbdev *exynos_fb = to_exynos_fbdev(helper);
+ struct exynos_drm_buf_entry *entry;
+ unsigned int size = fb_width * fb_height * (fb->bits_per_pixel >> 3);
+ unsigned long offset;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_fb->fb = fb;
+
+ drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
+ drm_fb_helper_fill_var(fbi, helper, fb_width, fb_height);
+
+ entry = exynos_drm_fb_get_buf(fb);
+ if (!entry) {
+ DRM_LOG_KMS("entry is null.\n");
+ return -EFAULT;
+ }
+
+ offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
+ offset += fbi->var.yoffset * fb->pitch;
+
+ dev->mode_config.fb_base = entry->paddr;
+ fbi->screen_base = entry->vaddr + offset;
+ fbi->fix.smem_start = entry->paddr + offset;
+ fbi->screen_size = size;
+ fbi->fix.smem_len = size;
+
+ return 0;
+}
+
+static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
+ struct drm_device *dev = helper->dev;
+ struct fb_info *fbi;
+ struct drm_mode_fb_cmd mode_cmd = { 0 };
+ struct platform_device *pdev = dev->platformdev;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
+ sizes->surface_width, sizes->surface_height,
+ sizes->surface_bpp);
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.bpp = sizes->surface_bpp;
+ mode_cmd.depth = sizes->surface_depth;
+
+ mutex_lock(&dev->struct_mutex);
+
+ fbi = framebuffer_alloc(0, &pdev->dev);
+ if (!fbi) {
+ DRM_ERROR("failed to allocate fb info.\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd);
+ if (IS_ERR_OR_NULL(exynos_fbdev->fb)) {
+ DRM_ERROR("failed to create drm framebuffer.\n");
+ ret = PTR_ERR(exynos_fbdev->fb);
+ goto out;
+ }
+
+ helper->fb = exynos_fbdev->fb;
+ helper->fbdev = fbi;
+
+ fbi->par = helper;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->fbops = &exynos_drm_fb_ops;
+
+ ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+ if (ret) {
+ DRM_ERROR("failed to allocate cmap.\n");
+ goto out;
+ }
+
+ ret = exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width,
+ sizes->fb_height);
+ if (ret < 0)
+ fb_dealloc_cmap(&fbi->cmap);
+
+/*
+ * if failed, all resources allocated above would be released by
+ * drm_mode_config_cleanup() when drm_load() had been called prior
+ * to any specific driver such as fimd or hdmi driver.
+ */
+out:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+static bool
+exynos_drm_fbdev_is_samefb(struct drm_framebuffer *fb,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ if (fb->width != sizes->surface_width)
+ return false;
+ if (fb->height != sizes->surface_height)
+ return false;
+ if (fb->bits_per_pixel != sizes->surface_bpp)
+ return false;
+ if (fb->depth != sizes->surface_depth)
+ return false;
+
+ return true;
+}
+
+static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct drm_device *dev = helper->dev;
+ struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
+ struct drm_framebuffer *fb = exynos_fbdev->fb;
+ struct drm_mode_fb_cmd mode_cmd = { 0 };
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (helper->fb != fb) {
+ DRM_ERROR("drm framebuffer is different\n");
+ return -EINVAL;
+ }
+
+ if (exynos_drm_fbdev_is_samefb(fb, sizes))
+ return 0;
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.bpp = sizes->surface_bpp;
+ mode_cmd.depth = sizes->surface_depth;
+
+ if (fb->funcs->destroy)
+ fb->funcs->destroy(fb);
+
+ exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd);
+ if (IS_ERR(exynos_fbdev->fb)) {
+ DRM_ERROR("failed to allocate fb.\n");
+ return PTR_ERR(exynos_fbdev->fb);
+ }
+
+ helper->fb = exynos_fbdev->fb;
+ return exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width,
+ sizes->fb_height);
+}
+
+static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ int ret = 0;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!helper->fb) {
+ ret = exynos_drm_fbdev_create(helper, sizes);
+ if (ret < 0) {
+ DRM_ERROR("failed to create fbdev.\n");
+ return ret;
+ }
+
+ /*
+ * fb_helper expects a value more than 1 if succeed
+ * because register_framebuffer() should be called.
+ */
+ ret = 1;
+ } else {
+ ret = exynos_drm_fbdev_recreate(helper, sizes);
+ if (ret < 0) {
+ DRM_ERROR("failed to reconfigure fbdev\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
+ .fb_probe = exynos_drm_fbdev_probe,
+};
+
+int exynos_drm_fbdev_init(struct drm_device *dev)
+{
+ struct exynos_drm_fbdev *fbdev;
+ struct exynos_drm_private *private = dev->dev_private;
+ struct drm_fb_helper *helper;
+ unsigned int num_crtc;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
+ return 0;
+
+ fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
+ if (!fbdev) {
+ DRM_ERROR("failed to allocate drm fbdev.\n");
+ return -ENOMEM;
+ }
+
+ private->fb_helper = helper = &fbdev->drm_fb_helper;
+ helper->funcs = &exynos_drm_fb_helper_funcs;
+
+ num_crtc = dev->mode_config.num_crtc;
+
+ ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
+ if (ret < 0) {
+ DRM_ERROR("failed to initialize drm fb helper.\n");
+ goto err_init;
+ }
+
+ ret = drm_fb_helper_single_add_all_connectors(helper);
+ if (ret < 0) {
+ DRM_ERROR("failed to register drm_fb_helper_connector.\n");
+ goto err_setup;
+
+ }
+
+ ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
+ if (ret < 0) {
+ DRM_ERROR("failed to set up hw configuration.\n");
+ goto err_setup;
+ }
+
+ return 0;
+
+err_setup:
+ drm_fb_helper_fini(helper);
+
+err_init:
+ private->fb_helper = NULL;
+ kfree(fbdev);
+
+ return ret;
+}
+
+static void exynos_drm_fbdev_destroy(struct drm_device *dev,
+ struct drm_fb_helper *fb_helper)
+{
+ struct drm_framebuffer *fb;
+
+ /* release drm framebuffer and real buffer */
+ if (fb_helper->fb && fb_helper->fb->funcs) {
+ fb = fb_helper->fb;
+ if (fb && fb->funcs->destroy)
+ fb->funcs->destroy(fb);
+ }
+
+ /* release linux framebuffer */
+ if (fb_helper->fbdev) {
+ struct fb_info *info;
+ int ret;
+
+ info = fb_helper->fbdev;
+ ret = unregister_framebuffer(info);
+ if (ret < 0)
+ DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
+
+ if (info->cmap.len)
+ fb_dealloc_cmap(&info->cmap);
+
+ framebuffer_release(info);
+ }
+
+ drm_fb_helper_fini(fb_helper);
+}
+
+void exynos_drm_fbdev_fini(struct drm_device *dev)
+{
+ struct exynos_drm_private *private = dev->dev_private;
+ struct exynos_drm_fbdev *fbdev;
+
+ if (!private || !private->fb_helper)
+ return;
+
+ fbdev = to_exynos_fbdev(private->fb_helper);
+
+ exynos_drm_fbdev_destroy(dev, private->fb_helper);
+ kfree(fbdev);
+ private->fb_helper = NULL;
+}
+
+void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
+{
+ struct exynos_drm_private *private = dev->dev_private;
+
+ if (!private || !private->fb_helper)
+ return;
+
+ drm_fb_helper_restore_fbdev_mode(private->fb_helper);
+}
+
+int exynos_drm_fbdev_reinit(struct drm_device *dev)
+{
+ struct exynos_drm_private *private = dev->dev_private;
+ struct drm_fb_helper *fb_helper;
+ int ret;
+
+ if (!private)
+ return -EINVAL;
+
+ /*
+ * if all sub drivers were unloaded then num_connector is 0
+ * so at this time, the framebuffers also should be destroyed.
+ */
+ if (!dev->mode_config.num_connector) {
+ exynos_drm_fbdev_fini(dev);
+ return 0;
+ }
+
+ fb_helper = private->fb_helper;
+
+ if (fb_helper) {
+ drm_fb_helper_fini(fb_helper);
+
+ ret = drm_fb_helper_init(dev, fb_helper,
+ dev->mode_config.num_crtc, MAX_CONNECTOR);
+ if (ret < 0) {
+ DRM_ERROR("failed to initialize drm fb helper\n");
+ return ret;
+ }
+
+ ret = drm_fb_helper_single_add_all_connectors(fb_helper);
+ if (ret < 0) {
+ DRM_ERROR("failed to add fb helper to connectors\n");
+ goto err;
+ }
+
+ ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP);
+ if (ret < 0) {
+ DRM_ERROR("failed to set up hw configuration.\n");
+ goto err;
+ }
+ } else {
+ /*
+ * if drm_load() failed whem drm load() was called prior
+ * to specific drivers, fb_helper must be NULL and so
+ * this fuction should be called again to re-initialize and
+ * re-configure the fb helper. it means that this function
+ * has been called by the specific drivers.
+ */
+ ret = exynos_drm_fbdev_init(dev);
+ }
+
+ return ret;
+
+err:
+ /*
+ * if drm_load() failed when drm load() was called prior
+ * to specific drivers, the fb_helper must be NULL and so check it.
+ */
+ if (fb_helper)
+ drm_fb_helper_fini(fb_helper);
+
+ return ret;
+}
+
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.h b/drivers/gpu/drm/exynos/exynos_drm_fbdev.h
new file mode 100644
index 000000000000..ccfce8a1a451
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_FBDEV_H_
+#define _EXYNOS_DRM_FBDEV_H_
+
+int exynos_drm_fbdev_init(struct drm_device *dev);
+int exynos_drm_fbdev_reinit(struct drm_device *dev);
+void exynos_drm_fbdev_fini(struct drm_device *dev);
+void exynos_drm_fbdev_restore_mode(struct drm_device *dev);
+
+#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
new file mode 100644
index 000000000000..4659c88cdd9b
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -0,0 +1,811 @@
+/* exynos_drm_fimd.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Authors:
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Inki Dae <inki.dae@samsung.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 "drmP.h"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <drm/exynos_drm.h>
+#include <plat/regs-fb-v4.h>
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_fbdev.h"
+#include "exynos_drm_crtc.h"
+
+/*
+ * FIMD is stand for Fully Interactive Mobile Display and
+ * as a display controller, it transfers contents drawn on memory
+ * to a LCD Panel through Display Interfaces such as RGB or
+ * CPU Interface.
+ */
+
+/* position control register for hardware window 0, 2 ~ 4.*/
+#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
+#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16)
+/* size control register for hardware window 0. */
+#define VIDOSD_C_SIZE_W0 (VIDOSD_BASE + 0x08)
+/* alpha control register for hardware window 1 ~ 4. */
+#define VIDOSD_C(win) (VIDOSD_BASE + 0x18 + (win) * 16)
+/* size control register for hardware window 1 ~ 4. */
+#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16)
+
+#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
+#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
+#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
+
+/* color key control register for hardware window 1 ~ 4. */
+#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + (x * 8))
+/* color key value register for hardware window 1 ~ 4. */
+#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + (x * 8))
+
+/* FIMD has totally five hardware windows. */
+#define WINDOWS_NR 5
+
+#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev))
+
+struct fimd_win_data {
+ unsigned int offset_x;
+ unsigned int offset_y;
+ unsigned int ovl_width;
+ unsigned int ovl_height;
+ unsigned int fb_width;
+ unsigned int fb_height;
+ unsigned int bpp;
+ dma_addr_t paddr;
+ void __iomem *vaddr;
+ unsigned int buf_offsize;
+ unsigned int line_size; /* bytes */
+};
+
+struct fimd_context {
+ struct exynos_drm_subdrv subdrv;
+ int irq;
+ struct drm_crtc *crtc;
+ struct clk *bus_clk;
+ struct clk *lcd_clk;
+ struct resource *regs_res;
+ void __iomem *regs;
+ struct fimd_win_data win_data[WINDOWS_NR];
+ unsigned int clkdiv;
+ unsigned int default_win;
+ unsigned long irq_flags;
+ u32 vidcon0;
+ u32 vidcon1;
+
+ struct fb_videomode *timing;
+};
+
+static bool fimd_display_is_connected(struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return true;
+}
+
+static void *fimd_get_timing(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return ctx->timing;
+}
+
+static int fimd_check_timing(struct device *dev, void *timing)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return 0;
+}
+
+static int fimd_display_power_on(struct device *dev, int mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return 0;
+}
+
+static struct exynos_drm_display fimd_display = {
+ .type = EXYNOS_DISPLAY_TYPE_LCD,
+ .is_connected = fimd_display_is_connected,
+ .get_timing = fimd_get_timing,
+ .check_timing = fimd_check_timing,
+ .power_on = fimd_display_power_on,
+};
+
+static void fimd_commit(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fb_videomode *timing = ctx->timing;
+ u32 val;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* setup polarity values from machine code. */
+ writel(ctx->vidcon1, ctx->regs + VIDCON1);
+
+ /* setup vertical timing values. */
+ val = VIDTCON0_VBPD(timing->upper_margin - 1) |
+ VIDTCON0_VFPD(timing->lower_margin - 1) |
+ VIDTCON0_VSPW(timing->vsync_len - 1);
+ writel(val, ctx->regs + VIDTCON0);
+
+ /* setup horizontal timing values. */
+ val = VIDTCON1_HBPD(timing->left_margin - 1) |
+ VIDTCON1_HFPD(timing->right_margin - 1) |
+ VIDTCON1_HSPW(timing->hsync_len - 1);
+ writel(val, ctx->regs + VIDTCON1);
+
+ /* setup horizontal and vertical display size. */
+ val = VIDTCON2_LINEVAL(timing->yres - 1) |
+ VIDTCON2_HOZVAL(timing->xres - 1);
+ writel(val, ctx->regs + VIDTCON2);
+
+ /* setup clock source, clock divider, enable dma. */
+ val = ctx->vidcon0;
+ val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
+
+ if (ctx->clkdiv > 1)
+ val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
+ else
+ val &= ~VIDCON0_CLKDIR; /* 1:1 clock */
+
+ /*
+ * fields of register with prefix '_F' would be updated
+ * at vsync(same as dma start)
+ */
+ val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
+ writel(val, ctx->regs + VIDCON0);
+}
+
+static int fimd_enable_vblank(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ u32 val;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!test_and_set_bit(0, &ctx->irq_flags)) {
+ val = readl(ctx->regs + VIDINTCON0);
+
+ val |= VIDINTCON0_INT_ENABLE;
+ val |= VIDINTCON0_INT_FRAME;
+
+ val &= ~VIDINTCON0_FRAMESEL0_MASK;
+ val |= VIDINTCON0_FRAMESEL0_VSYNC;
+ val &= ~VIDINTCON0_FRAMESEL1_MASK;
+ val |= VIDINTCON0_FRAMESEL1_NONE;
+
+ writel(val, ctx->regs + VIDINTCON0);
+ }
+
+ return 0;
+}
+
+static void fimd_disable_vblank(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ u32 val;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (test_and_clear_bit(0, &ctx->irq_flags)) {
+ val = readl(ctx->regs + VIDINTCON0);
+
+ val &= ~VIDINTCON0_INT_FRAME;
+ val &= ~VIDINTCON0_INT_ENABLE;
+
+ writel(val, ctx->regs + VIDINTCON0);
+ }
+}
+
+static struct exynos_drm_manager_ops fimd_manager_ops = {
+ .commit = fimd_commit,
+ .enable_vblank = fimd_enable_vblank,
+ .disable_vblank = fimd_disable_vblank,
+};
+
+static void fimd_win_mode_set(struct device *dev,
+ struct exynos_drm_overlay *overlay)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ unsigned long offset;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!overlay) {
+ dev_err(dev, "overlay is NULL\n");
+ return;
+ }
+
+ offset = overlay->fb_x * (overlay->bpp >> 3);
+ offset += overlay->fb_y * overlay->pitch;
+
+ DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
+
+ win_data = &ctx->win_data[ctx->default_win];
+
+ win_data->offset_x = overlay->crtc_x;
+ win_data->offset_y = overlay->crtc_y;
+ win_data->ovl_width = overlay->crtc_width;
+ win_data->ovl_height = overlay->crtc_height;
+ win_data->fb_width = overlay->fb_width;
+ win_data->fb_height = overlay->fb_height;
+ win_data->paddr = overlay->paddr + offset;
+ win_data->vaddr = overlay->vaddr + offset;
+ win_data->bpp = overlay->bpp;
+ win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
+ (overlay->bpp >> 3);
+ win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
+
+ DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
+ win_data->offset_x, win_data->offset_y);
+ DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+ win_data->ovl_width, win_data->ovl_height);
+ DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n",
+ (unsigned long)win_data->paddr,
+ (unsigned long)win_data->vaddr);
+ DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
+ overlay->fb_width, overlay->crtc_width);
+}
+
+static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data = &ctx->win_data[win];
+ unsigned long val;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ val = WINCONx_ENWIN;
+
+ switch (win_data->bpp) {
+ case 1:
+ val |= WINCON0_BPPMODE_1BPP;
+ val |= WINCONx_BITSWP;
+ val |= WINCONx_BURSTLEN_4WORD;
+ break;
+ case 2:
+ val |= WINCON0_BPPMODE_2BPP;
+ val |= WINCONx_BITSWP;
+ val |= WINCONx_BURSTLEN_8WORD;
+ break;
+ case 4:
+ val |= WINCON0_BPPMODE_4BPP;
+ val |= WINCONx_BITSWP;
+ val |= WINCONx_BURSTLEN_8WORD;
+ break;
+ case 8:
+ val |= WINCON0_BPPMODE_8BPP_PALETTE;
+ val |= WINCONx_BURSTLEN_8WORD;
+ val |= WINCONx_BYTSWP;
+ break;
+ case 16:
+ val |= WINCON0_BPPMODE_16BPP_565;
+ val |= WINCONx_HAWSWP;
+ val |= WINCONx_BURSTLEN_16WORD;
+ break;
+ case 24:
+ val |= WINCON0_BPPMODE_24BPP_888;
+ val |= WINCONx_WSWP;
+ val |= WINCONx_BURSTLEN_16WORD;
+ break;
+ case 32:
+ val |= WINCON1_BPPMODE_28BPP_A4888
+ | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
+ val |= WINCONx_WSWP;
+ val |= WINCONx_BURSTLEN_16WORD;
+ break;
+ default:
+ DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
+
+ val |= WINCON0_BPPMODE_24BPP_888;
+ val |= WINCONx_WSWP;
+ val |= WINCONx_BURSTLEN_16WORD;
+ break;
+ }
+
+ DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
+
+ writel(val, ctx->regs + WINCON(win));
+}
+
+static void fimd_win_set_colkey(struct device *dev, unsigned int win)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ unsigned int keycon0 = 0, keycon1 = 0;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
+ WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
+
+ keycon1 = WxKEYCON1_COLVAL(0xffffffff);
+
+ writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
+ writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
+}
+
+static void fimd_win_commit(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ int win = ctx->default_win;
+ unsigned long val, alpha, size;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ win_data = &ctx->win_data[win];
+
+ /*
+ * SHADOWCON register is used for enabling timing.
+ *
+ * for example, once only width value of a register is set,
+ * if the dma is started then fimd hardware could malfunction so
+ * with protect window setting, the register fields with prefix '_F'
+ * wouldn't be updated at vsync also but updated once unprotect window
+ * is set.
+ */
+
+ /* protect windows */
+ val = readl(ctx->regs + SHADOWCON);
+ val |= SHADOWCON_WINx_PROTECT(win);
+ writel(val, ctx->regs + SHADOWCON);
+
+ /* buffer start address */
+ val = win_data->paddr;
+ writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
+
+ /* buffer end address */
+ size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
+ val = win_data->paddr + size;
+ writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
+
+ DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
+ (unsigned long)win_data->paddr, val, size);
+ DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+ win_data->ovl_width, win_data->ovl_height);
+
+ /* buffer size */
+ val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
+ VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size);
+ writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
+
+ /* OSD position */
+ val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
+ VIDOSDxA_TOPLEFT_Y(win_data->offset_y);
+ writel(val, ctx->regs + VIDOSD_A(win));
+
+ val = VIDOSDxB_BOTRIGHT_X(win_data->offset_x +
+ win_data->ovl_width - 1) |
+ VIDOSDxB_BOTRIGHT_Y(win_data->offset_y +
+ win_data->ovl_height - 1);
+ writel(val, ctx->regs + VIDOSD_B(win));
+
+ DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
+ win_data->offset_x, win_data->offset_y,
+ win_data->offset_x + win_data->ovl_width - 1,
+ win_data->offset_y + win_data->ovl_height - 1);
+
+ /* hardware window 0 doesn't support alpha channel. */
+ if (win != 0) {
+ /* OSD alpha */
+ alpha = VIDISD14C_ALPHA1_R(0xf) |
+ VIDISD14C_ALPHA1_G(0xf) |
+ VIDISD14C_ALPHA1_B(0xf);
+
+ writel(alpha, ctx->regs + VIDOSD_C(win));
+ }
+
+ /* OSD size */
+ if (win != 3 && win != 4) {
+ u32 offset = VIDOSD_D(win);
+ if (win == 0)
+ offset = VIDOSD_C_SIZE_W0;
+ val = win_data->ovl_width * win_data->ovl_height;
+ writel(val, ctx->regs + offset);
+
+ DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
+ }
+
+ fimd_win_set_pixfmt(dev, win);
+
+ /* hardware window 0 doesn't support color key. */
+ if (win != 0)
+ fimd_win_set_colkey(dev, win);
+
+ /* Enable DMA channel and unprotect windows */
+ val = readl(ctx->regs + SHADOWCON);
+ val |= SHADOWCON_CHx_ENABLE(win);
+ val &= ~SHADOWCON_WINx_PROTECT(win);
+ writel(val, ctx->regs + SHADOWCON);
+}
+
+static void fimd_win_disable(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ int win = ctx->default_win;
+ u32 val;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ win_data = &ctx->win_data[win];
+
+ /* protect windows */
+ val = readl(ctx->regs + SHADOWCON);
+ val |= SHADOWCON_WINx_PROTECT(win);
+ writel(val, ctx->regs + SHADOWCON);
+
+ /* wincon */
+ val = readl(ctx->regs + WINCON(win));
+ val &= ~WINCONx_ENWIN;
+ writel(val, ctx->regs + WINCON(win));
+
+ /* unprotect windows */
+ val = readl(ctx->regs + SHADOWCON);
+ val &= ~SHADOWCON_CHx_ENABLE(win);
+ val &= ~SHADOWCON_WINx_PROTECT(win);
+ writel(val, ctx->regs + SHADOWCON);
+}
+
+static struct exynos_drm_overlay_ops fimd_overlay_ops = {
+ .mode_set = fimd_win_mode_set,
+ .commit = fimd_win_commit,
+ .disable = fimd_win_disable,
+};
+
+static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc)
+{
+ struct exynos_drm_private *dev_priv = drm_dev->dev_private;
+ struct drm_pending_vblank_event *e, *t;
+ struct timeval now;
+ unsigned long flags;
+ bool is_checked = false;
+
+ spin_lock_irqsave(&drm_dev->event_lock, flags);
+
+ list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
+ base.link) {
+ /* if event's pipe isn't same as crtc then ignore it. */
+ if (crtc != e->pipe)
+ continue;
+
+ is_checked = true;
+
+ do_gettimeofday(&now);
+ e->event.sequence = 0;
+ e->event.tv_sec = now.tv_sec;
+ e->event.tv_usec = now.tv_usec;
+
+ list_move_tail(&e->base.link, &e->base.file_priv->event_list);
+ wake_up_interruptible(&e->base.file_priv->event_wait);
+ }
+
+ if (is_checked)
+ drm_vblank_put(drm_dev, crtc);
+
+ spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+}
+
+static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
+{
+ struct fimd_context *ctx = (struct fimd_context *)dev_id;
+ struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
+ struct drm_device *drm_dev = subdrv->drm_dev;
+ struct exynos_drm_manager *manager = &subdrv->manager;
+ u32 val;
+
+ val = readl(ctx->regs + VIDINTCON1);
+
+ if (val & VIDINTCON1_INT_FRAME)
+ /* VSYNC interrupt */
+ writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
+
+ drm_handle_vblank(drm_dev, manager->pipe);
+ fimd_finish_pageflip(drm_dev, manager->pipe);
+
+ return IRQ_HANDLED;
+}
+
+static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = 1, we can use the vblank feature.
+ *
+ * P.S. note that we wouldn't use drm irq handler but
+ * just specific driver own one instead because
+ * drm framework supports only one irq handler.
+ */
+ drm_dev->irq_enabled = 1;
+
+ /*
+ * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(drm_vblank_put function was called)
+ */
+ drm_dev->vblank_disable_allowed = 1;
+
+ return 0;
+}
+
+static void fimd_subdrv_remove(struct drm_device *drm_dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+}
+
+static int fimd_calc_clkdiv(struct fimd_context *ctx,
+ struct fb_videomode *timing)
+{
+ unsigned long clk = clk_get_rate(ctx->lcd_clk);
+ u32 retrace;
+ u32 clkdiv;
+ u32 best_framerate = 0;
+ u32 framerate;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ retrace = timing->left_margin + timing->hsync_len +
+ timing->right_margin + timing->xres;
+ retrace *= timing->upper_margin + timing->vsync_len +
+ timing->lower_margin + timing->yres;
+
+ /* default framerate is 60Hz */
+ if (!timing->refresh)
+ timing->refresh = 60;
+
+ clk /= retrace;
+
+ for (clkdiv = 1; clkdiv < 0x100; clkdiv++) {
+ int tmp;
+
+ /* get best framerate */
+ framerate = clk / clkdiv;
+ tmp = timing->refresh - framerate;
+ if (tmp < 0) {
+ best_framerate = framerate;
+ continue;
+ } else {
+ if (!best_framerate)
+ best_framerate = framerate;
+ else if (tmp < (best_framerate - framerate))
+ best_framerate = framerate;
+ break;
+ }
+ }
+
+ return clkdiv;
+}
+
+static void fimd_clear_win(struct fimd_context *ctx, int win)
+{
+ u32 val;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ writel(0, ctx->regs + WINCON(win));
+ writel(0, ctx->regs + VIDOSD_A(win));
+ writel(0, ctx->regs + VIDOSD_B(win));
+ writel(0, ctx->regs + VIDOSD_C(win));
+
+ if (win == 1 || win == 2)
+ writel(0, ctx->regs + VIDOSD_D(win));
+
+ val = readl(ctx->regs + SHADOWCON);
+ val &= ~SHADOWCON_WINx_PROTECT(win);
+ writel(val, ctx->regs + SHADOWCON);
+}
+
+static int __devinit fimd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fimd_context *ctx;
+ struct exynos_drm_subdrv *subdrv;
+ struct exynos_drm_fimd_pdata *pdata;
+ struct fb_videomode *timing;
+ struct resource *res;
+ int win;
+ int ret = -EINVAL;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(dev, "no platform data specified\n");
+ return -EINVAL;
+ }
+
+ timing = &pdata->timing;
+ if (!timing) {
+ dev_err(dev, "timing is null.\n");
+ return -EINVAL;
+ }
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->bus_clk = clk_get(dev, "fimd");
+ if (IS_ERR(ctx->bus_clk)) {
+ dev_err(dev, "failed to get bus clock\n");
+ ret = PTR_ERR(ctx->bus_clk);
+ goto err_clk_get;
+ }
+
+ clk_enable(ctx->bus_clk);
+
+ ctx->lcd_clk = clk_get(dev, "sclk_fimd");
+ if (IS_ERR(ctx->lcd_clk)) {
+ dev_err(dev, "failed to get lcd clock\n");
+ ret = PTR_ERR(ctx->lcd_clk);
+ goto err_bus_clk;
+ }
+
+ clk_enable(ctx->lcd_clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to find registers\n");
+ ret = -ENOENT;
+ goto err_clk;
+ }
+
+ ctx->regs_res = request_mem_region(res->start, resource_size(res),
+ dev_name(dev));
+ if (!ctx->regs_res) {
+ dev_err(dev, "failed to claim register region\n");
+ ret = -ENOENT;
+ goto err_clk;
+ }
+
+ ctx->regs = ioremap(res->start, resource_size(res));
+ if (!ctx->regs) {
+ dev_err(dev, "failed to map registers\n");
+ ret = -ENXIO;
+ goto err_req_region_io;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "irq request failed.\n");
+ goto err_req_region_irq;
+ }
+
+ ctx->irq = res->start;
+
+ for (win = 0; win < WINDOWS_NR; win++)
+ fimd_clear_win(ctx, win);
+
+ ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx);
+ if (ret < 0) {
+ dev_err(dev, "irq request failed.\n");
+ goto err_req_irq;
+ }
+
+ ctx->clkdiv = fimd_calc_clkdiv(ctx, timing);
+ ctx->vidcon0 = pdata->vidcon0;
+ ctx->vidcon1 = pdata->vidcon1;
+ ctx->default_win = pdata->default_win;
+ ctx->timing = timing;
+
+ timing->pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv;
+
+ DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n",
+ timing->pixclock, ctx->clkdiv);
+
+ subdrv = &ctx->subdrv;
+
+ subdrv->probe = fimd_subdrv_probe;
+ subdrv->remove = fimd_subdrv_remove;
+ subdrv->manager.pipe = -1;
+ subdrv->manager.ops = &fimd_manager_ops;
+ subdrv->manager.overlay_ops = &fimd_overlay_ops;
+ subdrv->manager.display = &fimd_display;
+ subdrv->manager.dev = dev;
+
+ platform_set_drvdata(pdev, ctx);
+ exynos_drm_subdrv_register(subdrv);
+
+ return 0;
+
+err_req_irq:
+err_req_region_irq:
+ iounmap(ctx->regs);
+
+err_req_region_io:
+ release_resource(ctx->regs_res);
+ kfree(ctx->regs_res);
+
+err_clk:
+ clk_disable(ctx->lcd_clk);
+ clk_put(ctx->lcd_clk);
+
+err_bus_clk:
+ clk_disable(ctx->bus_clk);
+ clk_put(ctx->bus_clk);
+
+err_clk_get:
+ kfree(ctx);
+ return ret;
+}
+
+static int __devexit fimd_remove(struct platform_device *pdev)
+{
+ struct fimd_context *ctx = platform_get_drvdata(pdev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_drm_subdrv_unregister(&ctx->subdrv);
+
+ clk_disable(ctx->lcd_clk);
+ clk_disable(ctx->bus_clk);
+ clk_put(ctx->lcd_clk);
+ clk_put(ctx->bus_clk);
+
+ iounmap(ctx->regs);
+ release_resource(ctx->regs_res);
+ kfree(ctx->regs_res);
+ free_irq(ctx->irq, ctx);
+
+ kfree(ctx);
+
+ return 0;
+}
+
+static struct platform_driver fimd_driver = {
+ .probe = fimd_probe,
+ .remove = __devexit_p(fimd_remove),
+ .driver = {
+ .name = "exynos4-fb",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init fimd_init(void)
+{
+ return platform_driver_register(&fimd_driver);
+}
+
+static void __exit fimd_exit(void)
+{
+ platform_driver_unregister(&fimd_driver);
+}
+
+module_init(fimd_init);
+module_exit(fimd_exit);
+
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_DESCRIPTION("Samsung DRM FIMD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
new file mode 100644
index 000000000000..a8e7a88906ed
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -0,0 +1,415 @@
+/* exynos_drm_gem.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Author: Inki Dae <inki.dae@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm.h"
+
+#include <drm/exynos_drm.h>
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_gem.h"
+#include "exynos_drm_buf.h"
+
+static unsigned int convert_to_vm_err_msg(int msg)
+{
+ unsigned int out_msg;
+
+ switch (msg) {
+ case 0:
+ case -ERESTARTSYS:
+ case -EINTR:
+ out_msg = VM_FAULT_NOPAGE;
+ break;
+
+ case -ENOMEM:
+ out_msg = VM_FAULT_OOM;
+ break;
+
+ default:
+ out_msg = VM_FAULT_SIGBUS;
+ break;
+ }
+
+ return out_msg;
+}
+
+static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
+}
+
+struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv,
+ struct drm_device *dev, unsigned int size,
+ unsigned int *handle)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_buf_entry *entry;
+ struct drm_gem_object *obj;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ size = roundup(size, PAGE_SIZE);
+
+ exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
+ if (!exynos_gem_obj) {
+ DRM_ERROR("failed to allocate exynos gem object.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* allocate the new buffer object and memory region. */
+ entry = exynos_drm_buf_create(dev, size);
+ if (!entry) {
+ kfree(exynos_gem_obj);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ exynos_gem_obj->entry = entry;
+
+ obj = &exynos_gem_obj->base;
+
+ ret = drm_gem_object_init(dev, obj, size);
+ if (ret < 0) {
+ DRM_ERROR("failed to initailize gem object.\n");
+ goto err_obj_init;
+ }
+
+ DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
+
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret < 0) {
+ DRM_ERROR("failed to allocate mmap offset.\n");
+ goto err_create_mmap_offset;
+ }
+
+ /*
+ * allocate a id of idr table where the obj is registered
+ * and handle has the id what user can see.
+ */
+ ret = drm_gem_handle_create(file_priv, obj, handle);
+ if (ret)
+ goto err_handle_create;
+
+ DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_unreference_unlocked(obj);
+
+ return exynos_gem_obj;
+
+err_handle_create:
+ drm_gem_free_mmap_offset(obj);
+
+err_create_mmap_offset:
+ drm_gem_object_release(obj);
+
+err_obj_init:
+ exynos_drm_buf_destroy(dev, exynos_gem_obj->entry);
+
+ kfree(exynos_gem_obj);
+
+ return ERR_PTR(ret);
+}
+
+int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_exynos_gem_create *args = data;
+ struct exynos_drm_gem_obj *exynos_gem_obj;
+
+ DRM_DEBUG_KMS("%s : size = 0x%x\n", __FILE__, args->size);
+
+ exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size,
+ &args->handle);
+ if (IS_ERR(exynos_gem_obj))
+ return PTR_ERR(exynos_gem_obj);
+
+ return 0;
+}
+
+int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_exynos_gem_map_off *args = data;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
+ args->handle, (unsigned long)args->offset);
+
+ if (!(dev->driver->driver_features & DRIVER_GEM)) {
+ DRM_ERROR("does not support GEM.\n");
+ return -ENODEV;
+ }
+
+ return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle,
+ &args->offset);
+}
+
+static int exynos_drm_gem_mmap_buffer(struct file *filp,
+ struct vm_area_struct *vma)
+{
+ struct drm_gem_object *obj = filp->private_data;
+ struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+ struct exynos_drm_buf_entry *entry;
+ unsigned long pfn, vm_size;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ vma->vm_flags |= (VM_IO | VM_RESERVED);
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_file = filp;
+
+ vm_size = vma->vm_end - vma->vm_start;
+ /*
+ * a entry contains information to physically continuous memory
+ * allocated by user request or at framebuffer creation.
+ */
+ entry = exynos_gem_obj->entry;
+
+ /* check if user-requested size is valid. */
+ if (vm_size > entry->size)
+ return -EINVAL;
+
+ /*
+ * get page frame number to physical memory to be mapped
+ * to user space.
+ */
+ pfn = exynos_gem_obj->entry->paddr >> PAGE_SHIFT;
+
+ DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
+
+ if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
+ vma->vm_page_prot)) {
+ DRM_ERROR("failed to remap pfn range.\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static const struct file_operations exynos_drm_gem_fops = {
+ .mmap = exynos_drm_gem_mmap_buffer,
+};
+
+int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_exynos_gem_mmap *args = data;
+ struct drm_gem_object *obj;
+ unsigned int addr;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!(dev->driver->driver_features & DRIVER_GEM)) {
+ DRM_ERROR("does not support GEM.\n");
+ return -ENODEV;
+ }
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ return -EINVAL;
+ }
+
+ obj->filp->f_op = &exynos_drm_gem_fops;
+ obj->filp->private_data = obj;
+
+ down_write(&current->mm->mmap_sem);
+ addr = do_mmap(obj->filp, 0, args->size,
+ PROT_READ | PROT_WRITE, MAP_SHARED, 0);
+ up_write(&current->mm->mmap_sem);
+
+ drm_gem_object_unreference_unlocked(obj);
+
+ if (IS_ERR((void *)addr))
+ return PTR_ERR((void *)addr);
+
+ args->mapped = addr;
+
+ DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
+
+ return 0;
+}
+
+int exynos_drm_gem_init_object(struct drm_gem_object *obj)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return 0;
+}
+
+void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ DRM_DEBUG_KMS("handle count = %d\n",
+ atomic_read(&gem_obj->handle_count));
+
+ if (gem_obj->map_list.map)
+ drm_gem_free_mmap_offset(gem_obj);
+
+ /* release file pointer to gem object. */
+ drm_gem_object_release(gem_obj);
+
+ exynos_gem_obj = to_exynos_gem_obj(gem_obj);
+
+ exynos_drm_buf_destroy(gem_obj->dev, exynos_gem_obj->entry);
+
+ kfree(exynos_gem_obj);
+}
+
+int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * alocate memory to be used for framebuffer.
+ * - this callback would be called by user application
+ * with DRM_IOCTL_MODE_CREATE_DUMB command.
+ */
+
+ args->pitch = args->width * args->bpp >> 3;
+ args->size = args->pitch * args->height;
+
+ exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size,
+ &args->handle);
+ if (IS_ERR(exynos_gem_obj))
+ return PTR_ERR(exynos_gem_obj);
+
+ return 0;
+}
+
+int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle, uint64_t *offset)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct drm_gem_object *obj;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ mutex_lock(&dev->struct_mutex);
+
+ /*
+ * get offset of memory allocated for drm framebuffer.
+ * - this callback would be called by user application
+ * with DRM_IOCTL_MODE_MAP_DUMB command.
+ */
+
+ obj = drm_gem_object_lookup(dev, file_priv, handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
+ }
+
+ exynos_gem_obj = to_exynos_gem_obj(obj);
+
+ *offset = get_gem_mmap_offset(&exynos_gem_obj->base);
+
+ drm_gem_object_unreference(obj);
+
+ DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct drm_gem_object *obj = vma->vm_private_data;
+ struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+ struct drm_device *dev = obj->dev;
+ unsigned long pfn;
+ pgoff_t page_offset;
+ int ret;
+
+ page_offset = ((unsigned long)vmf->virtual_address -
+ vma->vm_start) >> PAGE_SHIFT;
+
+ mutex_lock(&dev->struct_mutex);
+
+ pfn = (exynos_gem_obj->entry->paddr >> PAGE_SHIFT) + page_offset;
+
+ ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return convert_to_vm_err_msg(ret);
+}
+
+int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* set vm_area_struct. */
+ ret = drm_gem_mmap(filp, vma);
+ if (ret < 0) {
+ DRM_ERROR("failed to mmap.\n");
+ return ret;
+ }
+
+ vma->vm_flags &= ~VM_PFNMAP;
+ vma->vm_flags |= VM_MIXEDMAP;
+
+ return ret;
+}
+
+
+int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *dev, unsigned int handle)
+{
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * obj->refcount and obj->handle_count are decreased and
+ * if both them are 0 then exynos_drm_gem_free_object()
+ * would be called by callback to release resources.
+ */
+ ret = drm_gem_handle_delete(file_priv, handle);
+ if (ret < 0) {
+ DRM_ERROR("failed to delete drm_gem_handle.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC DRM GEM Module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h
new file mode 100644
index 000000000000..e5fc0148277b
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h
@@ -0,0 +1,107 @@
+/* exynos_drm_gem.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authoer: Inki Dae <inki.dae@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_GEM_H_
+#define _EXYNOS_DRM_GEM_H_
+
+#define to_exynos_gem_obj(x) container_of(x,\
+ struct exynos_drm_gem_obj, base)
+
+/*
+ * exynos drm buffer structure.
+ *
+ * @base: a gem object.
+ * - a new handle to this gem object would be created
+ * by drm_gem_handle_create().
+ * @entry: pointer to exynos drm buffer entry object.
+ * - containing the information to physically
+ * continuous memory region allocated by user request
+ * or at framebuffer creation.
+ *
+ * P.S. this object would be transfered to user as kms_bo.handle so
+ * user can access the buffer through kms_bo.handle.
+ */
+struct exynos_drm_gem_obj {
+ struct drm_gem_object base;
+ struct exynos_drm_buf_entry *entry;
+};
+
+/* create a new buffer and get a new gem handle. */
+struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv,
+ struct drm_device *dev, unsigned int size,
+ unsigned int *handle);
+
+/*
+ * request gem object creation and buffer allocation as the size
+ * that it is calculated with framebuffer information such as width,
+ * height and bpp.
+ */
+int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/* get buffer offset to map to user space. */
+int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/* unmap a buffer from user space. */
+int exynos_drm_gem_munmap_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/* initialize gem object. */
+int exynos_drm_gem_init_object(struct drm_gem_object *obj);
+
+/* free gem object. */
+void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj);
+
+/* create memory region for drm framebuffer. */
+int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev, struct drm_mode_create_dumb *args);
+
+/* map memory region for drm framebuffer to user space. */
+int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle, uint64_t *offset);
+
+/* page fault handler and mmap fault address(virtual) to physical memory. */
+int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+
+/*
+ * mmap the physically continuous memory that a gem object contains
+ * to user space.
+ */
+int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/* set vm_flags and we can change the vm attribute to other one at here. */
+int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+
+/*
+ * destroy memory region allocated.
+ * - a gem handle and physical memory region pointed by a gem object
+ * would be released by drm_gem_handle_delete().
+ */
+int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *dev, unsigned int handle);
+
+#endif
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
index 08792a740f18..07d55df6623e 100644
--- a/drivers/gpu/drm/i2c/ch7006_drv.c
+++ b/drivers/gpu/drm/i2c/ch7006_drv.c
@@ -24,6 +24,8 @@
*
*/
+#include <linux/module.h>
+
#include "ch7006_priv.h"
/* DRM encoder functions */
diff --git a/drivers/gpu/drm/i2c/sil164_drv.c b/drivers/gpu/drm/i2c/sil164_drv.c
index 0b6773290c08..b7d45ab4ba69 100644
--- a/drivers/gpu/drm/i2c/sil164_drv.c
+++ b/drivers/gpu/drm/i2c/sil164_drv.c
@@ -24,6 +24,8 @@
*
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "drm_crtc_helper.h"
#include "drm_encoder_slave.h"
diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c
index 6f98d059f68a..d4266bdf6fb4 100644
--- a/drivers/gpu/drm/i810/i810_drv.c
+++ b/drivers/gpu/drm/i810/i810_drv.c
@@ -30,6 +30,8 @@
* Gareth Hughes <gareth@valinux.com>
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "drm.h"
#include "i810_drm.h"
diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c
index d3e8c540f778..1ca799a1e1fc 100644
--- a/drivers/gpu/drm/i915/dvo_ch7017.c
+++ b/drivers/gpu/drm/i915/dvo_ch7017.c
@@ -227,7 +227,7 @@ static bool ch7017_init(struct intel_dvo_device *dvo,
default:
DRM_DEBUG_KMS("ch701x not detected, got %d: from %s "
"slave %d.\n",
- val, adapter->name,dvo->slave_addr);
+ val, adapter->name, dvo->slave_addr);
goto fail;
}
diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c
index 7eaa94e4ff06..4a036600e806 100644
--- a/drivers/gpu/drm/i915/dvo_ch7xxx.c
+++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c
@@ -111,7 +111,7 @@ static char *ch7xxx_get_id(uint8_t vid)
/** Reads an 8 bit register */
static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
- struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
u8 out_buf[2];
u8 in_buf[2];
@@ -303,7 +303,7 @@ static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
for (i = 0; i < CH7xxx_NUM_REGS; i++) {
uint8_t val;
- if ((i % 8) == 0 )
+ if ((i % 8) == 0)
DRM_LOG_KMS("\n %02X: ", i);
ch7xxx_readb(dvo, i, &val);
DRM_LOG_KMS("%02X ", val);
diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c
index a12ed9414cc7..04f2893d5e3c 100644
--- a/drivers/gpu/drm/i915/dvo_ivch.c
+++ b/drivers/gpu/drm/i915/dvo_ivch.c
@@ -344,8 +344,8 @@ static void ivch_mode_set(struct intel_dvo_device *dvo,
(adjusted_mode->hdisplay - 1)) >> 2;
y_ratio = (((mode->vdisplay - 1) << 16) /
(adjusted_mode->vdisplay - 1)) >> 2;
- ivch_write (dvo, VR42, x_ratio);
- ivch_write (dvo, VR41, y_ratio);
+ ivch_write(dvo, VR42, x_ratio);
+ ivch_write(dvo, VR41, y_ratio);
} else {
vr01 &= ~VR01_PANEL_FIT_ENABLE;
vr40 &= ~VR40_CLOCK_GATING_ENABLE;
@@ -410,7 +410,7 @@ static void ivch_destroy(struct intel_dvo_device *dvo)
}
}
-struct intel_dvo_dev_ops ivch_ops= {
+struct intel_dvo_dev_ops ivch_ops = {
.init = ivch_init,
.dpms = ivch_dpms,
.mode_valid = ivch_mode_valid,
diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c
index e4b4091df942..a0b13a6f619d 100644
--- a/drivers/gpu/drm/i915/dvo_sil164.c
+++ b/drivers/gpu/drm/i915/dvo_sil164.c
@@ -104,7 +104,7 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
- struct sil164_priv *sil= dvo->dev_priv;
+ struct sil164_priv *sil = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
uint8_t out_buf[2];
struct i2c_msg msg = {
diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c
index 8ab2855bb544..aa2cd3ec54aa 100644
--- a/drivers/gpu/drm/i915/dvo_tfp410.c
+++ b/drivers/gpu/drm/i915/dvo_tfp410.c
@@ -56,7 +56,7 @@
#define TFP410_CTL_2_MDI (1<<0)
#define TFP410_CTL_3 0x0A
-#define TFP410_CTL_3_DK_MASK (0x7<<5)
+#define TFP410_CTL_3_DK_MASK (0x7<<5)
#define TFP410_CTL_3_DK (1<<5)
#define TFP410_CTL_3_DKEN (1<<4)
#define TFP410_CTL_3_CTL_MASK (0x7<<1)
@@ -225,12 +225,12 @@ static void tfp410_mode_set(struct intel_dvo_device *dvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- /* As long as the basics are set up, since we don't have clock dependencies
- * in the mode setup, we can just leave the registers alone and everything
- * will work fine.
- */
- /* don't do much */
- return;
+ /* As long as the basics are set up, since we don't have clock dependencies
+ * in the mode setup, we can just leave the registers alone and everything
+ * will work fine.
+ */
+ /* don't do much */
+ return;
}
/* set the tfp410 power state */
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 0a893f7400fa..d09a6e02dc95 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -29,6 +29,7 @@
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "drmP.h"
#include "drm.h"
#include "intel_drv.h"
@@ -98,12 +99,12 @@ static const char *get_pin_flag(struct drm_i915_gem_object *obj)
static const char *get_tiling_flag(struct drm_i915_gem_object *obj)
{
- switch (obj->tiling_mode) {
- default:
- case I915_TILING_NONE: return " ";
- case I915_TILING_X: return "X";
- case I915_TILING_Y: return "Y";
- }
+ switch (obj->tiling_mode) {
+ default:
+ case I915_TILING_NONE: return " ";
+ case I915_TILING_X: return "X";
+ case I915_TILING_Y: return "Y";
+ }
}
static const char *cache_level_str(int type)
@@ -217,7 +218,7 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
++mappable_count; \
} \
} \
-} while(0)
+} while (0)
static int i915_gem_object_info(struct seq_file *m, void* data)
{
@@ -499,7 +500,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
seq_printf(m, "Interrupts received: %d\n",
atomic_read(&dev_priv->irq_received));
for (i = 0; i < I915_NUM_RINGS; i++) {
- if (IS_GEN6(dev)) {
+ if (IS_GEN6(dev) || IS_GEN7(dev)) {
seq_printf(m, "Graphics Interrupt mask (%s): %08x\n",
dev_priv->ring[i].name,
I915_READ_IMR(&dev_priv->ring[i]));
@@ -635,11 +636,16 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data)
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
+ int ret;
ring = &dev_priv->ring[(uintptr_t)node->info_ent->data];
if (ring->size == 0)
return 0;
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
seq_printf(m, "Ring %s:\n", ring->name);
seq_printf(m, " Head : %08x\n", I915_READ_HEAD(ring) & HEAD_ADDR);
seq_printf(m, " Tail : %08x\n", I915_READ_TAIL(ring) & TAIL_ADDR);
@@ -653,6 +659,8 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data)
seq_printf(m, " Control : %08x\n", I915_READ_CTL(ring));
seq_printf(m, " Start : %08x\n", I915_READ_START(ring));
+ mutex_unlock(&dev->struct_mutex);
+
return 0;
}
@@ -841,7 +849,16 @@ static int i915_rstdby_delays(struct seq_file *m, void *unused)
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
- u16 crstanddelay = I915_READ16(CRSTANDVID);
+ u16 crstanddelay;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ crstanddelay = I915_READ16(CRSTANDVID);
+
+ mutex_unlock(&dev->struct_mutex);
seq_printf(m, "w/ctx: %d, w/o ctx: %d\n", (crstanddelay >> 8) & 0x3f, (crstanddelay & 0x3f));
@@ -865,7 +882,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
MEMSTAT_VID_SHIFT);
seq_printf(m, "Current P-state: %d\n",
(rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
- } else if (IS_GEN6(dev)) {
+ } else if (IS_GEN6(dev) || IS_GEN7(dev)) {
u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS);
u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
@@ -939,7 +956,11 @@ static int i915_delayfreq_table(struct seq_file *m, void *unused)
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
u32 delayfreq;
- int i;
+ int ret, i;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
for (i = 0; i < 16; i++) {
delayfreq = I915_READ(PXVFREQ_BASE + i * 4);
@@ -947,6 +968,8 @@ static int i915_delayfreq_table(struct seq_file *m, void *unused)
(delayfreq & PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT);
}
+ mutex_unlock(&dev->struct_mutex);
+
return 0;
}
@@ -961,13 +984,19 @@ static int i915_inttoext_table(struct seq_file *m, void *unused)
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
u32 inttoext;
- int i;
+ int ret, i;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
for (i = 1; i <= 32; i++) {
inttoext = I915_READ(INTTOEXT_BASE_ILK + i * 4);
seq_printf(m, "INTTOEXT%02d: 0x%08x\n", i, inttoext);
}
+ mutex_unlock(&dev->struct_mutex);
+
return 0;
}
@@ -976,9 +1005,19 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
- u32 rgvmodectl = I915_READ(MEMMODECTL);
- u32 rstdbyctl = I915_READ(RSTDBYCTL);
- u16 crstandvid = I915_READ16(CRSTANDVID);
+ u32 rgvmodectl, rstdbyctl;
+ u16 crstandvid;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ rgvmodectl = I915_READ(MEMMODECTL);
+ rstdbyctl = I915_READ(RSTDBYCTL);
+ crstandvid = I915_READ16(CRSTANDVID);
+
+ mutex_unlock(&dev->struct_mutex);
seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ?
"yes" : "no");
@@ -1123,14 +1162,59 @@ static int i915_emon_status(struct seq_file *m, void *unused)
return 0;
}
+static int i915_ring_freq_table(struct seq_file *m, void *unused)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ int ret;
+ int gpu_freq, ia_freq;
+
+ if (!(IS_GEN6(dev) || IS_GEN7(dev))) {
+ seq_printf(m, "unsupported on this chipset\n");
+ return 0;
+ }
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ seq_printf(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\n");
+
+ for (gpu_freq = dev_priv->min_delay; gpu_freq <= dev_priv->max_delay;
+ gpu_freq++) {
+ I915_WRITE(GEN6_PCODE_DATA, gpu_freq);
+ I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
+ GEN6_PCODE_READ_MIN_FREQ_TABLE);
+ if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
+ GEN6_PCODE_READY) == 0, 10)) {
+ DRM_ERROR("pcode read of freq table timed out\n");
+ continue;
+ }
+ ia_freq = I915_READ(GEN6_PCODE_DATA);
+ seq_printf(m, "%d\t\t%d\n", gpu_freq * 50, ia_freq * 100);
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
static int i915_gfxec(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
seq_printf(m, "GFXEC: %ld\n", (unsigned long)I915_READ(0x112f4));
+ mutex_unlock(&dev->struct_mutex);
+
return 0;
}
@@ -1255,12 +1339,12 @@ i915_wedged_read(struct file *filp,
char buf[80];
int len;
- len = snprintf(buf, sizeof (buf),
+ len = snprintf(buf, sizeof(buf),
"wedged : %d\n",
atomic_read(&dev_priv->mm.wedged));
- if (len > sizeof (buf))
- len = sizeof (buf);
+ if (len > sizeof(buf))
+ len = sizeof(buf);
return simple_read_from_buffer(ubuf, max, ppos, buf, len);
}
@@ -1276,7 +1360,7 @@ i915_wedged_write(struct file *filp,
int val = 1;
if (cnt > 0) {
- if (cnt > sizeof (buf) - 1)
+ if (cnt > sizeof(buf) - 1)
return -EINVAL;
if (copy_from_user(buf, ubuf, cnt))
@@ -1300,6 +1384,155 @@ static const struct file_operations i915_wedged_fops = {
.llseek = default_llseek,
};
+static int
+i915_max_freq_open(struct inode *inode,
+ struct file *filp)
+{
+ filp->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t
+i915_max_freq_read(struct file *filp,
+ char __user *ubuf,
+ size_t max,
+ loff_t *ppos)
+{
+ struct drm_device *dev = filp->private_data;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ char buf[80];
+ int len;
+
+ len = snprintf(buf, sizeof(buf),
+ "max freq: %d\n", dev_priv->max_delay * 50);
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+}
+
+static ssize_t
+i915_max_freq_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt,
+ loff_t *ppos)
+{
+ struct drm_device *dev = filp->private_data;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ char buf[20];
+ int val = 1;
+
+ if (cnt > 0) {
+ if (cnt > sizeof(buf) - 1)
+ return -EINVAL;
+
+ if (copy_from_user(buf, ubuf, cnt))
+ return -EFAULT;
+ buf[cnt] = 0;
+
+ val = simple_strtoul(buf, NULL, 0);
+ }
+
+ DRM_DEBUG_DRIVER("Manually setting max freq to %d\n", val);
+
+ /*
+ * Turbo will still be enabled, but won't go above the set value.
+ */
+ dev_priv->max_delay = val / 50;
+
+ gen6_set_rps(dev, val / 50);
+
+ return cnt;
+}
+
+static const struct file_operations i915_max_freq_fops = {
+ .owner = THIS_MODULE,
+ .open = i915_max_freq_open,
+ .read = i915_max_freq_read,
+ .write = i915_max_freq_write,
+ .llseek = default_llseek,
+};
+
+static int
+i915_cache_sharing_open(struct inode *inode,
+ struct file *filp)
+{
+ filp->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t
+i915_cache_sharing_read(struct file *filp,
+ char __user *ubuf,
+ size_t max,
+ loff_t *ppos)
+{
+ struct drm_device *dev = filp->private_data;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ char buf[80];
+ u32 snpcr;
+ int len;
+
+ mutex_lock(&dev_priv->dev->struct_mutex);
+ snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
+ mutex_unlock(&dev_priv->dev->struct_mutex);
+
+ len = snprintf(buf, sizeof(buf),
+ "%d\n", (snpcr & GEN6_MBC_SNPCR_MASK) >>
+ GEN6_MBC_SNPCR_SHIFT);
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+}
+
+static ssize_t
+i915_cache_sharing_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt,
+ loff_t *ppos)
+{
+ struct drm_device *dev = filp->private_data;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ char buf[20];
+ u32 snpcr;
+ int val = 1;
+
+ if (cnt > 0) {
+ if (cnt > sizeof(buf) - 1)
+ return -EINVAL;
+
+ if (copy_from_user(buf, ubuf, cnt))
+ return -EFAULT;
+ buf[cnt] = 0;
+
+ val = simple_strtoul(buf, NULL, 0);
+ }
+
+ if (val < 0 || val > 3)
+ return -EINVAL;
+
+ DRM_DEBUG_DRIVER("Manually setting uncore sharing to %d\n", val);
+
+ /* Update the cache sharing policy here as well */
+ snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
+ snpcr &= ~GEN6_MBC_SNPCR_MASK;
+ snpcr |= (val << GEN6_MBC_SNPCR_SHIFT);
+ I915_WRITE(GEN6_MBCUNIT_SNPCR, snpcr);
+
+ return cnt;
+}
+
+static const struct file_operations i915_cache_sharing_fops = {
+ .owner = THIS_MODULE,
+ .open = i915_cache_sharing_open,
+ .read = i915_cache_sharing_read,
+ .write = i915_cache_sharing_write,
+ .llseek = default_llseek,
+};
+
/* As the drm_debugfs_init() routines are called before dev->dev_private is
* allocated we need to hook into the minor for release. */
static int
@@ -1318,7 +1551,10 @@ drm_add_fake_info_node(struct drm_minor *minor,
node->minor = minor;
node->dent = ent;
node->info_ent = (void *) key;
- list_add(&node->list, &minor->debugfs_nodes.list);
+
+ mutex_lock(&minor->debugfs_lock);
+ list_add(&node->list, &minor->debugfs_list);
+ mutex_unlock(&minor->debugfs_lock);
return 0;
}
@@ -1399,6 +1635,36 @@ static int i915_forcewake_create(struct dentry *root, struct drm_minor *minor)
return drm_add_fake_info_node(minor, ent, &i915_forcewake_fops);
}
+static int i915_max_freq_create(struct dentry *root, struct drm_minor *minor)
+{
+ struct drm_device *dev = minor->dev;
+ struct dentry *ent;
+
+ ent = debugfs_create_file("i915_max_freq",
+ S_IRUGO | S_IWUSR,
+ root, dev,
+ &i915_max_freq_fops);
+ if (IS_ERR(ent))
+ return PTR_ERR(ent);
+
+ return drm_add_fake_info_node(minor, ent, &i915_max_freq_fops);
+}
+
+static int i915_cache_sharing_create(struct dentry *root, struct drm_minor *minor)
+{
+ struct drm_device *dev = minor->dev;
+ struct dentry *ent;
+
+ ent = debugfs_create_file("i915_cache_sharing",
+ S_IRUGO | S_IWUSR,
+ root, dev,
+ &i915_cache_sharing_fops);
+ if (IS_ERR(ent))
+ return PTR_ERR(ent);
+
+ return drm_add_fake_info_node(minor, ent, &i915_cache_sharing_fops);
+}
+
static struct drm_info_list i915_debugfs_list[] = {
{"i915_capabilities", i915_capabilities, 0},
{"i915_gem_objects", i915_gem_object_info, 0},
@@ -1430,6 +1696,7 @@ static struct drm_info_list i915_debugfs_list[] = {
{"i915_inttoext_table", i915_inttoext_table, 0},
{"i915_drpc_info", i915_drpc_info, 0},
{"i915_emon_status", i915_emon_status, 0},
+ {"i915_ring_freq_table", i915_ring_freq_table, 0},
{"i915_gfxec", i915_gfxec, 0},
{"i915_fbc_status", i915_fbc_status, 0},
{"i915_sr_status", i915_sr_status, 0},
@@ -1451,6 +1718,12 @@ int i915_debugfs_init(struct drm_minor *minor)
ret = i915_forcewake_create(minor->debugfs_root, minor);
if (ret)
return ret;
+ ret = i915_max_freq_create(minor->debugfs_root, minor);
+ if (ret)
+ return ret;
+ ret = i915_cache_sharing_create(minor->debugfs_root, minor);
+ if (ret)
+ return ret;
return drm_debugfs_create_files(i915_debugfs_list,
I915_DEBUGFS_ENTRIES,
@@ -1465,6 +1738,10 @@ void i915_debugfs_cleanup(struct drm_minor *minor)
1, minor);
drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops,
1, minor);
+ drm_debugfs_remove_files((struct drm_info_list *) &i915_max_freq_fops,
+ 1, minor);
+ drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops,
+ 1, minor);
}
#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index e1787022d6c8..a9533c54c93c 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -41,6 +41,7 @@
#include <linux/pnp.h>
#include <linux/vga_switcheroo.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <acpi/video.h>
static void i915_write_hws_pga(struct drm_device *dev)
@@ -61,7 +62,6 @@ static void i915_write_hws_pga(struct drm_device *dev)
static int i915_init_phys_hws(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = LP_RING(dev_priv);
/* Program Hardware Status Page */
dev_priv->status_page_dmah =
@@ -71,10 +71,9 @@ static int i915_init_phys_hws(struct drm_device *dev)
DRM_ERROR("Can not allocate hardware status page\n");
return -ENOMEM;
}
- ring->status_page.page_addr =
- (void __force __iomem *)dev_priv->status_page_dmah->vaddr;
- memset_io(ring->status_page.page_addr, 0, PAGE_SIZE);
+ memset_io((void __force __iomem *)dev_priv->status_page_dmah->vaddr,
+ 0, PAGE_SIZE);
i915_write_hws_pga(dev);
@@ -886,7 +885,7 @@ static int i915_get_bridge_dev(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
+ dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
if (!dev_priv->bridge_dev) {
DRM_ERROR("bridge device not found\n");
return -1;
@@ -1073,6 +1072,9 @@ static void i915_setup_compression(struct drm_device *dev, int size)
unsigned long cfb_base;
unsigned long ll_base = 0;
+ /* Just in case the BIOS is doing something questionable. */
+ intel_disable_fbc(dev);
+
compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0);
if (compressed_fb)
compressed_fb = drm_mm_get_block(compressed_fb, size, 4096);
@@ -1099,7 +1101,6 @@ static void i915_setup_compression(struct drm_device *dev, int size)
dev_priv->cfb_size = size;
- intel_disable_fbc(dev);
dev_priv->compressed_fb = compressed_fb;
if (HAS_PCH_SPLIT(dev))
I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start);
@@ -1730,10 +1731,10 @@ static DEFINE_SPINLOCK(mchdev_lock);
*/
unsigned long i915_read_mch_val(void)
{
- struct drm_i915_private *dev_priv;
+ struct drm_i915_private *dev_priv;
unsigned long chipset_val, graphics_val, ret = 0;
- spin_lock(&mchdev_lock);
+ spin_lock(&mchdev_lock);
if (!i915_mch_dev)
goto out_unlock;
dev_priv = i915_mch_dev;
@@ -1744,9 +1745,9 @@ unsigned long i915_read_mch_val(void)
ret = chipset_val + graphics_val;
out_unlock:
- spin_unlock(&mchdev_lock);
+ spin_unlock(&mchdev_lock);
- return ret;
+ return ret;
}
EXPORT_SYMBOL_GPL(i915_read_mch_val);
@@ -1757,10 +1758,10 @@ EXPORT_SYMBOL_GPL(i915_read_mch_val);
*/
bool i915_gpu_raise(void)
{
- struct drm_i915_private *dev_priv;
+ struct drm_i915_private *dev_priv;
bool ret = true;
- spin_lock(&mchdev_lock);
+ spin_lock(&mchdev_lock);
if (!i915_mch_dev) {
ret = false;
goto out_unlock;
@@ -1771,9 +1772,9 @@ bool i915_gpu_raise(void)
dev_priv->max_delay--;
out_unlock:
- spin_unlock(&mchdev_lock);
+ spin_unlock(&mchdev_lock);
- return ret;
+ return ret;
}
EXPORT_SYMBOL_GPL(i915_gpu_raise);
@@ -1785,10 +1786,10 @@ EXPORT_SYMBOL_GPL(i915_gpu_raise);
*/
bool i915_gpu_lower(void)
{
- struct drm_i915_private *dev_priv;
+ struct drm_i915_private *dev_priv;
bool ret = true;
- spin_lock(&mchdev_lock);
+ spin_lock(&mchdev_lock);
if (!i915_mch_dev) {
ret = false;
goto out_unlock;
@@ -1799,9 +1800,9 @@ bool i915_gpu_lower(void)
dev_priv->max_delay++;
out_unlock:
- spin_unlock(&mchdev_lock);
+ spin_unlock(&mchdev_lock);
- return ret;
+ return ret;
}
EXPORT_SYMBOL_GPL(i915_gpu_lower);
@@ -1812,10 +1813,10 @@ EXPORT_SYMBOL_GPL(i915_gpu_lower);
*/
bool i915_gpu_busy(void)
{
- struct drm_i915_private *dev_priv;
+ struct drm_i915_private *dev_priv;
bool ret = false;
- spin_lock(&mchdev_lock);
+ spin_lock(&mchdev_lock);
if (!i915_mch_dev)
goto out_unlock;
dev_priv = i915_mch_dev;
@@ -1823,9 +1824,9 @@ bool i915_gpu_busy(void)
ret = dev_priv->busy;
out_unlock:
- spin_unlock(&mchdev_lock);
+ spin_unlock(&mchdev_lock);
- return ret;
+ return ret;
}
EXPORT_SYMBOL_GPL(i915_gpu_busy);
@@ -1837,10 +1838,10 @@ EXPORT_SYMBOL_GPL(i915_gpu_busy);
*/
bool i915_gpu_turbo_disable(void)
{
- struct drm_i915_private *dev_priv;
+ struct drm_i915_private *dev_priv;
bool ret = true;
- spin_lock(&mchdev_lock);
+ spin_lock(&mchdev_lock);
if (!i915_mch_dev) {
ret = false;
goto out_unlock;
@@ -1853,9 +1854,9 @@ bool i915_gpu_turbo_disable(void)
ret = false;
out_unlock:
- spin_unlock(&mchdev_lock);
+ spin_unlock(&mchdev_lock);
- return ret;
+ return ret;
}
EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable);
@@ -1943,12 +1944,12 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (!dev_priv->mm.gtt) {
DRM_ERROR("Failed to initialize GTT\n");
ret = -ENODEV;
- goto out_iomapfree;
+ goto out_rmmap;
}
agp_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT;
- dev_priv->mm.gtt_mapping =
+ dev_priv->mm.gtt_mapping =
io_mapping_create_wc(dev->agp->base, agp_size);
if (dev_priv->mm.gtt_mapping == NULL) {
ret = -EIO;
@@ -1987,7 +1988,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (dev_priv->wq == NULL) {
DRM_ERROR("Failed to create our workqueue.\n");
ret = -ENOMEM;
- goto out_iomapfree;
+ goto out_mtrrfree;
}
/* enable GEM by default */
@@ -2035,7 +2036,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
spin_lock_init(&dev_priv->error_lock);
spin_lock_init(&dev_priv->rps_lock);
- if (IS_MOBILE(dev) || !IS_GEN2(dev))
+ if (IS_IVYBRIDGE(dev))
+ dev_priv->num_pipe = 3;
+ else if (IS_MOBILE(dev) || !IS_GEN2(dev))
dev_priv->num_pipe = 2;
else
dev_priv->num_pipe = 1;
@@ -2074,13 +2077,21 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
return 0;
out_gem_unload:
+ if (dev_priv->mm.inactive_shrinker.shrink)
+ unregister_shrinker(&dev_priv->mm.inactive_shrinker);
+
if (dev->pdev->msi_enabled)
pci_disable_msi(dev->pdev);
intel_teardown_gmbus(dev);
intel_teardown_mchbar(dev);
destroy_workqueue(dev_priv->wq);
-out_iomapfree:
+out_mtrrfree:
+ if (dev_priv->mm.gtt_mtrr >= 0) {
+ mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base,
+ dev->agp->agp_info.aper_size * 1024 * 1024);
+ dev_priv->mm.gtt_mtrr = -1;
+ }
io_mapping_free(dev_priv->mm.gtt_mapping);
out_rmmap:
pci_iounmap(dev->pdev, dev_priv->regs);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 013d304455b9..15bfa9145d2b 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -35,46 +35,79 @@
#include "intel_drv.h"
#include <linux/console.h>
+#include <linux/module.h>
#include "drm_crtc_helper.h"
-static int i915_modeset = -1;
+static int i915_modeset __read_mostly = -1;
module_param_named(modeset, i915_modeset, int, 0400);
+MODULE_PARM_DESC(modeset,
+ "Use kernel modesetting [KMS] (0=DRM_I915_KMS from .config, "
+ "1=on, -1=force vga console preference [default])");
-unsigned int i915_fbpercrtc = 0;
+unsigned int i915_fbpercrtc __always_unused = 0;
module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
-int i915_panel_ignore_lid = 0;
+int i915_panel_ignore_lid __read_mostly = 0;
module_param_named(panel_ignore_lid, i915_panel_ignore_lid, int, 0600);
+MODULE_PARM_DESC(panel_ignore_lid,
+ "Override lid status (0=autodetect [default], 1=lid open, "
+ "-1=lid closed)");
-unsigned int i915_powersave = 1;
+unsigned int i915_powersave __read_mostly = 1;
module_param_named(powersave, i915_powersave, int, 0600);
+MODULE_PARM_DESC(powersave,
+ "Enable powersavings, fbc, downclocking, etc. (default: true)");
-unsigned int i915_semaphores = 0;
+unsigned int i915_semaphores __read_mostly = 0;
module_param_named(semaphores, i915_semaphores, int, 0600);
+MODULE_PARM_DESC(semaphores,
+ "Use semaphores for inter-ring sync (default: false)");
-unsigned int i915_enable_rc6 = 1;
+unsigned int i915_enable_rc6 __read_mostly = 0;
module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600);
+MODULE_PARM_DESC(i915_enable_rc6,
+ "Enable power-saving render C-state 6 (default: true)");
-unsigned int i915_enable_fbc = 0;
+int i915_enable_fbc __read_mostly = -1;
module_param_named(i915_enable_fbc, i915_enable_fbc, int, 0600);
+MODULE_PARM_DESC(i915_enable_fbc,
+ "Enable frame buffer compression for power savings "
+ "(default: -1 (use per-chip default))");
-unsigned int i915_lvds_downclock = 0;
+unsigned int i915_lvds_downclock __read_mostly = 0;
module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400);
+MODULE_PARM_DESC(lvds_downclock,
+ "Use panel (LVDS/eDP) downclocking for power savings "
+ "(default: false)");
-unsigned int i915_panel_use_ssc = 1;
+int i915_panel_use_ssc __read_mostly = -1;
module_param_named(lvds_use_ssc, i915_panel_use_ssc, int, 0600);
+MODULE_PARM_DESC(lvds_use_ssc,
+ "Use Spread Spectrum Clock with panels [LVDS/eDP] "
+ "(default: auto from VBT)");
-int i915_vbt_sdvo_panel_type = -1;
+int i915_vbt_sdvo_panel_type __read_mostly = -1;
module_param_named(vbt_sdvo_panel_type, i915_vbt_sdvo_panel_type, int, 0600);
+MODULE_PARM_DESC(vbt_sdvo_panel_type,
+ "Override selection of SDVO panel mode in the VBT "
+ "(default: auto)");
-static bool i915_try_reset = true;
+static bool i915_try_reset __read_mostly = true;
module_param_named(reset, i915_try_reset, bool, 0600);
+MODULE_PARM_DESC(reset, "Attempt GPU resets (default: true)");
+
+bool i915_enable_hangcheck __read_mostly = true;
+module_param_named(enable_hangcheck, i915_enable_hangcheck, bool, 0644);
+MODULE_PARM_DESC(enable_hangcheck,
+ "Periodically check GPU activity for detecting hangs. "
+ "WARNING: Disabling this can cause system wide hangs. "
+ "(default: true)");
static struct drm_driver driver;
extern int intel_agp_enabled;
#define INTEL_VGA_DEVICE(id, info) { \
- .class = PCI_CLASS_DISPLAY_VGA << 8, \
+ .class = PCI_BASE_CLASS_DISPLAY << 16, \
.class_mask = 0xff0000, \
.vendor = 0x8086, \
.device = id, \
@@ -262,7 +295,7 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
#define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00
#define INTEL_PCH_PPT_DEVICE_ID_TYPE 0x1e00
-void intel_detect_pch (struct drm_device *dev)
+void intel_detect_pch(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct pci_dev *pch;
@@ -345,12 +378,17 @@ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
{
- int loop = 500;
- u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
- while (fifo < 20 && loop--) {
- udelay(10);
- fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
+ if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES) {
+ int loop = 500;
+ u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
+ while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) {
+ udelay(10);
+ fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
+ }
+ WARN_ON(loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES);
+ dev_priv->gt_fifo_count = fifo;
}
+ dev_priv->gt_fifo_count--;
}
static int i915_drm_freeze(struct drm_device *dev)
@@ -434,6 +472,9 @@ static int i915_drm_thaw(struct drm_device *dev)
error = i915_gem_init_ringbuffer(dev);
mutex_unlock(&dev->struct_mutex);
+ if (HAS_PCH_SPLIT(dev))
+ ironlake_init_pch_refclk(dev);
+
drm_mode_config_reset(dev);
drm_irq_install(dev);
@@ -577,6 +618,7 @@ int i915_reset(struct drm_device *dev, u8 flags)
if (get_seconds() - dev_priv->last_gpu_reset < 5) {
DRM_ERROR("GPU hanging too fast, declaring wedged!\n");
} else switch (INTEL_INFO(dev)->gen) {
+ case 7:
case 6:
ret = gen6_do_reset(dev, flags);
/* If reset with a user forcewake, try to restore */
@@ -732,12 +774,12 @@ static int i915_pm_poweroff(struct device *dev)
}
static const struct dev_pm_ops i915_pm_ops = {
- .suspend = i915_pm_suspend,
- .resume = i915_pm_resume,
- .freeze = i915_pm_freeze,
- .thaw = i915_pm_thaw,
- .poweroff = i915_pm_poweroff,
- .restore = i915_pm_resume,
+ .suspend = i915_pm_suspend,
+ .resume = i915_pm_resume,
+ .freeze = i915_pm_freeze,
+ .thaw = i915_pm_thaw,
+ .poweroff = i915_pm_poweroff,
+ .restore = i915_pm_resume,
};
static struct vm_operations_struct i915_gem_vm_ops = {
@@ -747,8 +789,8 @@ static struct vm_operations_struct i915_gem_vm_ops = {
};
static struct drm_driver driver = {
- /* don't use mtrr's here, the Xserver or user space app should
- * deal with them for intel hardware.
+ /* Don't use MTRRs here; the Xserver or userspace app should
+ * deal with them for Intel hardware.
*/
.driver_features =
DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/
@@ -857,3 +899,43 @@ module_exit(i915_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL and additional rights");
+
+/* We give fast paths for the really cool registers */
+#define NEEDS_FORCE_WAKE(dev_priv, reg) \
+ (((dev_priv)->info->gen >= 6) && \
+ ((reg) < 0x40000) && \
+ ((reg) != FORCEWAKE))
+
+#define __i915_read(x, y) \
+u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \
+ u##x val = 0; \
+ if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
+ gen6_gt_force_wake_get(dev_priv); \
+ val = read##y(dev_priv->regs + reg); \
+ gen6_gt_force_wake_put(dev_priv); \
+ } else { \
+ val = read##y(dev_priv->regs + reg); \
+ } \
+ trace_i915_reg_rw(false, reg, val, sizeof(val)); \
+ return val; \
+}
+
+__i915_read(8, b)
+__i915_read(16, w)
+__i915_read(32, l)
+__i915_read(64, q)
+#undef __i915_read
+
+#define __i915_write(x, y) \
+void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \
+ trace_i915_reg_rw(true, reg, val, sizeof(val)); \
+ if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
+ __gen6_gt_wait_for_fifo(dev_priv); \
+ } \
+ write##y(val, dev_priv->regs + reg); \
+}
+__i915_write(8, b)
+__i915_write(16, w)
+__i915_write(32, l)
+__i915_write(64, q)
+#undef __i915_write
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index f245c588ae95..4a9c1b979804 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -36,6 +36,7 @@
#include <linux/io-mapping.h>
#include <linux/i2c.h>
#include <drm/intel-gtt.h>
+#include <linux/backlight.h>
/* General customization:
*/
@@ -125,6 +126,9 @@ struct drm_i915_master_private {
struct _drm_i915_sarea *sarea_priv;
};
#define I915_FENCE_REG_NONE -1
+#define I915_MAX_NUM_FENCES 16
+/* 16 fences + sign bit for FENCE_REG_NONE */
+#define I915_MAX_NUM_FENCE_BITS 5
struct drm_i915_fence_reg {
struct list_head lru_list;
@@ -138,7 +142,6 @@ struct sdvo_device_mapping {
u8 slave_addr;
u8 dvo_wiring;
u8 i2c_pin;
- u8 i2c_speed;
u8 ddc_pin;
};
@@ -168,7 +171,7 @@ struct drm_i915_error_state {
u32 instdone1;
u32 seqno;
u64 bbaddr;
- u64 fence[16];
+ u64 fence[I915_MAX_NUM_FENCES];
struct timeval time;
struct drm_i915_error_object {
int page_count;
@@ -182,7 +185,7 @@ struct drm_i915_error_state {
u32 gtt_offset;
u32 read_domains;
u32 write_domain;
- s32 fence_reg:5;
+ s32 fence_reg:I915_MAX_NUM_FENCE_BITS;
s32 pinned:2;
u32 tiling:2;
u32 dirty:1;
@@ -208,12 +211,16 @@ struct drm_i915_display_funcs {
struct drm_display_mode *adjusted_mode,
int x, int y,
struct drm_framebuffer *old_fb);
+ void (*write_eld)(struct drm_connector *connector,
+ struct drm_crtc *crtc);
void (*fdi_link_train)(struct drm_crtc *crtc);
void (*init_clock_gating)(struct drm_device *dev);
void (*init_pch_clock_gating)(struct drm_device *dev);
int (*queue_flip)(struct drm_device *dev, struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_i915_gem_object *obj);
+ int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+ int x, int y);
/* clock updates for mode set */
/* cursor updates */
/* render clock increase/decrease */
@@ -223,26 +230,26 @@ struct drm_i915_display_funcs {
struct intel_device_info {
u8 gen;
- u8 is_mobile : 1;
- u8 is_i85x : 1;
- u8 is_i915g : 1;
- u8 is_i945gm : 1;
- u8 is_g33 : 1;
- u8 need_gfx_hws : 1;
- u8 is_g4x : 1;
- u8 is_pineview : 1;
- u8 is_broadwater : 1;
- u8 is_crestline : 1;
- u8 is_ivybridge : 1;
- u8 has_fbc : 1;
- u8 has_pipe_cxsr : 1;
- u8 has_hotplug : 1;
- u8 cursor_needs_physical : 1;
- u8 has_overlay : 1;
- u8 overlay_needs_physical : 1;
- u8 supports_tv : 1;
- u8 has_bsd_ring : 1;
- u8 has_blt_ring : 1;
+ u8 is_mobile:1;
+ u8 is_i85x:1;
+ u8 is_i915g:1;
+ u8 is_i945gm:1;
+ u8 is_g33:1;
+ u8 need_gfx_hws:1;
+ u8 is_g4x:1;
+ u8 is_pineview:1;
+ u8 is_broadwater:1;
+ u8 is_crestline:1;
+ u8 is_ivybridge:1;
+ u8 has_fbc:1;
+ u8 has_pipe_cxsr:1;
+ u8 has_hotplug:1;
+ u8 cursor_needs_physical:1;
+ u8 has_overlay:1;
+ u8 overlay_needs_physical:1;
+ u8 supports_tv:1;
+ u8 has_bsd_ring:1;
+ u8 has_blt_ring:1;
};
enum no_fbc_reason {
@@ -262,8 +269,10 @@ enum intel_pch {
};
#define QUIRK_PIPEA_FORCE (1<<0)
+#define QUIRK_LVDS_SSC_DISABLE (1<<1)
struct intel_fbdev;
+struct intel_fbc_work;
typedef struct drm_i915_private {
struct drm_device *dev;
@@ -274,6 +283,7 @@ typedef struct drm_i915_private {
int relative_constants_mode;
void __iomem *regs;
+ u32 gt_fifo_count;
struct intel_gmbus {
struct i2c_adapter adapter;
@@ -328,11 +338,10 @@ typedef struct drm_i915_private {
uint32_t last_instdone1;
unsigned long cfb_size;
- unsigned long cfb_pitch;
- unsigned long cfb_offset;
- int cfb_fence;
- int cfb_plane;
+ unsigned int cfb_fb;
+ enum plane cfb_plane;
int cfb_y;
+ struct intel_fbc_work *fbc_work;
struct intel_opregion opregion;
@@ -342,7 +351,6 @@ typedef struct drm_i915_private {
/* LVDS info */
int backlight_level; /* restore backlight to this value */
bool backlight_enabled;
- struct drm_display_mode *panel_fixed_mode;
struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
@@ -352,6 +360,7 @@ typedef struct drm_i915_private {
unsigned int lvds_vbt:1;
unsigned int int_crt_support:1;
unsigned int lvds_use_ssc:1;
+ unsigned int display_clock_mode:1;
int lvds_ssc_freq;
struct {
int rate;
@@ -369,7 +378,7 @@ typedef struct drm_i915_private {
struct notifier_block lid_notifier;
int crt_ddc_pin;
- struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
+ struct drm_i915_fence_reg fence_regs[I915_MAX_NUM_FENCES]; /* assume 965 */
int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
int num_fence_regs; /* 8 on pre-965, 16 otherwise */
@@ -500,7 +509,7 @@ typedef struct drm_i915_private {
u8 saveAR[21];
u8 saveDACMASK;
u8 saveCR[37];
- uint64_t saveFENCE[16];
+ uint64_t saveFENCE[I915_MAX_NUM_FENCES];
u32 saveCURACNTR;
u32 saveCURAPOS;
u32 saveCURABASE;
@@ -540,6 +549,7 @@ typedef struct drm_i915_private {
u32 savePIPEB_LINK_M1;
u32 savePIPEB_LINK_N1;
u32 saveMCHBAR_RENDER_STANDBY;
+ u32 savePCH_PORT_HOTPLUG;
struct {
/** Bridge to intel-gtt-ko */
@@ -666,10 +676,9 @@ typedef struct drm_i915_private {
unsigned int lvds_border_bits;
/* Panel fitter placement and size for Ironlake+ */
u32 pch_pf_pos, pch_pf_size;
- int panel_t3, panel_t12;
- struct drm_crtc *plane_to_crtc_mapping[2];
- struct drm_crtc *pipe_to_crtc_mapping[2];
+ struct drm_crtc *plane_to_crtc_mapping[3];
+ struct drm_crtc *pipe_to_crtc_mapping[3];
wait_queue_head_t pending_flip_queue;
bool flip_pending_is_done;
@@ -685,6 +694,7 @@ typedef struct drm_i915_private {
int child_dev_num;
struct child_device_config *child_dev;
struct drm_connector *int_lvds_connector;
+ struct drm_connector *int_edp_connector;
bool mchbar_need_disable;
@@ -718,6 +728,8 @@ typedef struct drm_i915_private {
/* list of fbdev register on this device */
struct intel_fbdev *fbdev;
+ struct backlight_device *backlight;
+
struct drm_property *broadcast_rgb_property;
struct drm_property *force_audio_property;
@@ -750,39 +762,37 @@ struct drm_i915_gem_object {
* (has pending rendering), and is not set if it's on inactive (ready
* to be unbound).
*/
- unsigned int active : 1;
+ unsigned int active:1;
/**
* This is set if the object has been written to since last bound
* to the GTT
*/
- unsigned int dirty : 1;
+ unsigned int dirty:1;
/**
* This is set if the object has been written to since the last
* GPU flush.
*/
- unsigned int pending_gpu_write : 1;
+ unsigned int pending_gpu_write:1;
/**
* Fence register bits (if any) for this object. Will be set
* as needed when mapped into the GTT.
* Protected by dev->struct_mutex.
- *
- * Size: 4 bits for 16 fences + sign (for FENCE_REG_NONE)
*/
- signed int fence_reg : 5;
+ signed int fence_reg:I915_MAX_NUM_FENCE_BITS;
/**
* Advice: are the backing pages purgeable?
*/
- unsigned int madv : 2;
+ unsigned int madv:2;
/**
* Current tiling mode for the object.
*/
- unsigned int tiling_mode : 2;
- unsigned int tiling_changed : 1;
+ unsigned int tiling_mode:2;
+ unsigned int tiling_changed:1;
/** How many users have pinned this object in GTT space. The following
* users can each hold at most one reference: pwrite/pread, pin_ioctl
@@ -793,22 +803,22 @@ struct drm_i915_gem_object {
*
* In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3
* bits with absolutely no headroom. So use 4 bits. */
- unsigned int pin_count : 4;
+ unsigned int pin_count:4;
#define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf
/**
* Is the object at the current location in the gtt mappable and
* fenceable? Used to avoid costly recalculations.
*/
- unsigned int map_and_fenceable : 1;
+ unsigned int map_and_fenceable:1;
/**
* Whether the current gtt mapping needs to be mappable (and isn't just
* mappable by accident). Track pin and fault separate for a more
* accurate mappable working set.
*/
- unsigned int fault_mappable : 1;
- unsigned int pin_mappable : 1;
+ unsigned int fault_mappable:1;
+ unsigned int pin_mappable:1;
/*
* Is the GPU currently using a fence to access this buffer,
@@ -985,15 +995,16 @@ struct drm_i915_file_private {
extern struct drm_ioctl_desc i915_ioctls[];
extern int i915_max_ioctl;
-extern unsigned int i915_fbpercrtc;
-extern int i915_panel_ignore_lid;
-extern unsigned int i915_powersave;
-extern unsigned int i915_semaphores;
-extern unsigned int i915_lvds_downclock;
-extern unsigned int i915_panel_use_ssc;
-extern int i915_vbt_sdvo_panel_type;
-extern unsigned int i915_enable_rc6;
-extern unsigned int i915_enable_fbc;
+extern unsigned int i915_fbpercrtc __always_unused;
+extern int i915_panel_ignore_lid __read_mostly;
+extern unsigned int i915_powersave __read_mostly;
+extern unsigned int i915_semaphores __read_mostly;
+extern unsigned int i915_lvds_downclock __read_mostly;
+extern int i915_panel_use_ssc __read_mostly;
+extern int i915_vbt_sdvo_panel_type __read_mostly;
+extern unsigned int i915_enable_rc6 __read_mostly;
+extern int i915_enable_fbc __read_mostly;
+extern bool i915_enable_hangcheck __read_mostly;
extern int i915_suspend(struct drm_device *dev, pm_message_t state);
extern int i915_resume(struct drm_device *dev);
@@ -1046,7 +1057,7 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
void
i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
-void intel_enable_asle (struct drm_device *dev);
+void intel_enable_asle(struct drm_device *dev);
#ifdef CONFIG_DEBUG_FS
extern void i915_destroy_error_state(struct drm_device *dev);
@@ -1136,7 +1147,7 @@ int i915_gem_dumb_create(struct drm_file *file_priv,
int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev,
uint32_t handle, uint64_t *offset);
int i915_gem_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev,
- uint32_t handle);
+ uint32_t handle);
/**
* Returns true if seq1 is later than seq2.
*/
@@ -1163,7 +1174,7 @@ void i915_gem_clflush_object(struct drm_i915_gem_object *obj);
int __must_check i915_gem_object_set_domain(struct drm_i915_gem_object *obj,
uint32_t read_domains,
uint32_t write_domain);
-int __must_check i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj);
+int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj);
int __must_check i915_gem_init_ringbuffer(struct drm_device *dev);
void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
void i915_gem_do_init(struct drm_device *dev,
@@ -1182,7 +1193,8 @@ int __must_check
i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj,
bool write);
int __must_check
-i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj,
+i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
+ u32 alignment,
struct intel_ring_buffer *pipelined);
int i915_gem_attach_phys_object(struct drm_device *dev,
struct drm_i915_gem_object *obj,
@@ -1194,11 +1206,18 @@ void i915_gem_free_all_phys_object(struct drm_device *dev);
void i915_gem_release(struct drm_device *dev, struct drm_file *file);
uint32_t
-i915_gem_get_unfenced_gtt_alignment(struct drm_i915_gem_object *obj);
+i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev,
+ uint32_t size,
+ int tiling_mode);
+
+int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
+ enum i915_cache_level cache_level);
/* i915_gem_gtt.c */
void i915_gem_restore_gtt_mappings(struct drm_device *dev);
int __must_check i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj);
+void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj,
+ enum i915_cache_level cache_level);
void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj);
/* i915_gem_evict.c */
@@ -1280,17 +1299,14 @@ extern void intel_modeset_init(struct drm_device *dev);
extern void intel_modeset_gem_init(struct drm_device *dev);
extern void intel_modeset_cleanup(struct drm_device *dev);
extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
-extern void i8xx_disable_fbc(struct drm_device *dev);
-extern void g4x_disable_fbc(struct drm_device *dev);
-extern void ironlake_disable_fbc(struct drm_device *dev);
-extern void intel_disable_fbc(struct drm_device *dev);
-extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval);
extern bool intel_fbc_enabled(struct drm_device *dev);
+extern void intel_disable_fbc(struct drm_device *dev);
extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
+extern void ironlake_init_pch_refclk(struct drm_device *dev);
extern void ironlake_enable_rc6(struct drm_device *dev);
extern void gen6_set_rps(struct drm_device *dev, u8 val);
-extern void intel_detect_pch (struct drm_device *dev);
-extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
+extern void intel_detect_pch(struct drm_device *dev);
+extern int intel_trans_dp_port_sel(struct drm_crtc *crtc);
/* overlay */
#ifdef CONFIG_DEBUG_FS
@@ -1340,18 +1356,7 @@ void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv);
((reg) != FORCEWAKE))
#define __i915_read(x, y) \
-static inline u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \
- u##x val = 0; \
- if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
- gen6_gt_force_wake_get(dev_priv); \
- val = read##y(dev_priv->regs + reg); \
- gen6_gt_force_wake_put(dev_priv); \
- } else { \
- val = read##y(dev_priv->regs + reg); \
- } \
- trace_i915_reg_rw(false, reg, val, sizeof(val)); \
- return val; \
-}
+ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg);
__i915_read(8, b)
__i915_read(16, w)
@@ -1360,13 +1365,8 @@ __i915_read(64, q)
#undef __i915_read
#define __i915_write(x, y) \
-static inline void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \
- trace_i915_reg_rw(true, reg, val, sizeof(val)); \
- if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
- __gen6_gt_wait_for_fifo(dev_priv); \
- } \
- write##y(val, dev_priv->regs + reg); \
-}
+ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val);
+
__i915_write(8, b)
__i915_write(16, w)
__i915_write(32, l)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 5c0d1247f453..8359dc777041 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -179,7 +179,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
mutex_unlock(&dev->struct_mutex);
args->aper_size = dev_priv->mm.gtt_total;
- args->aper_available_size = args->aper_size -pinned;
+ args->aper_available_size = args->aper_size - pinned;
return 0;
}
@@ -195,6 +195,8 @@ i915_gem_create(struct drm_file *file,
u32 handle;
size = roundup(size, PAGE_SIZE);
+ if (size == 0)
+ return -EINVAL;
/* Allocate the new object */
obj = i915_gem_alloc_object(dev, size);
@@ -800,11 +802,11 @@ i915_gem_shmem_pwrite_fast(struct drm_device *dev,
if (IS_ERR(page))
return PTR_ERR(page);
- vaddr = kmap_atomic(page, KM_USER0);
+ vaddr = kmap_atomic(page);
ret = __copy_from_user_inatomic(vaddr + page_offset,
user_data,
page_length);
- kunmap_atomic(vaddr, KM_USER0);
+ kunmap_atomic(vaddr);
set_page_dirty(page);
mark_page_accessed(page);
@@ -1265,74 +1267,6 @@ out:
}
/**
- * i915_gem_create_mmap_offset - create a fake mmap offset for an object
- * @obj: obj in question
- *
- * GEM memory mapping works by handing back to userspace a fake mmap offset
- * it can use in a subsequent mmap(2) call. The DRM core code then looks
- * up the object based on the offset and sets up the various memory mapping
- * structures.
- *
- * This routine allocates and attaches a fake offset for @obj.
- */
-static int
-i915_gem_create_mmap_offset(struct drm_i915_gem_object *obj)
-{
- struct drm_device *dev = obj->base.dev;
- struct drm_gem_mm *mm = dev->mm_private;
- struct drm_map_list *list;
- struct drm_local_map *map;
- int ret = 0;
-
- /* Set the object up for mmap'ing */
- list = &obj->base.map_list;
- list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
- if (!list->map)
- return -ENOMEM;
-
- map = list->map;
- map->type = _DRM_GEM;
- map->size = obj->base.size;
- map->handle = obj;
-
- /* Get a DRM GEM mmap offset allocated... */
- list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
- obj->base.size / PAGE_SIZE,
- 0, 0);
- if (!list->file_offset_node) {
- DRM_ERROR("failed to allocate offset for bo %d\n",
- obj->base.name);
- ret = -ENOSPC;
- goto out_free_list;
- }
-
- list->file_offset_node = drm_mm_get_block(list->file_offset_node,
- obj->base.size / PAGE_SIZE,
- 0);
- if (!list->file_offset_node) {
- ret = -ENOMEM;
- goto out_free_list;
- }
-
- list->hash.key = list->file_offset_node->start;
- ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
- if (ret) {
- DRM_ERROR("failed to add to map hash\n");
- goto out_free_mm;
- }
-
- return 0;
-
-out_free_mm:
- drm_mm_put_block(list->file_offset_node);
-out_free_list:
- kfree(list->map);
- list->map = NULL;
-
- return ret;
-}
-
-/**
* i915_gem_release_mmap - remove physical page mappings
* @obj: obj in question
*
@@ -1360,39 +1294,25 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
obj->fault_mappable = false;
}
-static void
-i915_gem_free_mmap_offset(struct drm_i915_gem_object *obj)
-{
- struct drm_device *dev = obj->base.dev;
- struct drm_gem_mm *mm = dev->mm_private;
- struct drm_map_list *list = &obj->base.map_list;
-
- drm_ht_remove_item(&mm->offset_hash, &list->hash);
- drm_mm_put_block(list->file_offset_node);
- kfree(list->map);
- list->map = NULL;
-}
-
static uint32_t
-i915_gem_get_gtt_size(struct drm_i915_gem_object *obj)
+i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode)
{
- struct drm_device *dev = obj->base.dev;
- uint32_t size;
+ uint32_t gtt_size;
if (INTEL_INFO(dev)->gen >= 4 ||
- obj->tiling_mode == I915_TILING_NONE)
- return obj->base.size;
+ tiling_mode == I915_TILING_NONE)
+ return size;
/* Previous chips need a power-of-two fence region when tiling */
if (INTEL_INFO(dev)->gen == 3)
- size = 1024*1024;
+ gtt_size = 1024*1024;
else
- size = 512*1024;
+ gtt_size = 512*1024;
- while (size < obj->base.size)
- size <<= 1;
+ while (gtt_size < size)
+ gtt_size <<= 1;
- return size;
+ return gtt_size;
}
/**
@@ -1403,59 +1323,52 @@ i915_gem_get_gtt_size(struct drm_i915_gem_object *obj)
* potential fence register mapping.
*/
static uint32_t
-i915_gem_get_gtt_alignment(struct drm_i915_gem_object *obj)
+i915_gem_get_gtt_alignment(struct drm_device *dev,
+ uint32_t size,
+ int tiling_mode)
{
- struct drm_device *dev = obj->base.dev;
-
/*
* Minimum alignment is 4k (GTT page size), but might be greater
* if a fence register is needed for the object.
*/
if (INTEL_INFO(dev)->gen >= 4 ||
- obj->tiling_mode == I915_TILING_NONE)
+ tiling_mode == I915_TILING_NONE)
return 4096;
/*
* Previous chips need to be aligned to the size of the smallest
* fence register that can contain the object.
*/
- return i915_gem_get_gtt_size(obj);
+ return i915_gem_get_gtt_size(dev, size, tiling_mode);
}
/**
* i915_gem_get_unfenced_gtt_alignment - return required GTT alignment for an
* unfenced object
- * @obj: object to check
+ * @dev: the device
+ * @size: size of the object
+ * @tiling_mode: tiling mode of the object
*
* Return the required GTT alignment for an object, only taking into account
* unfenced tiled surface requirements.
*/
uint32_t
-i915_gem_get_unfenced_gtt_alignment(struct drm_i915_gem_object *obj)
+i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev,
+ uint32_t size,
+ int tiling_mode)
{
- struct drm_device *dev = obj->base.dev;
- int tile_height;
-
/*
* Minimum alignment is 4k (GTT page size) for sane hw.
*/
if (INTEL_INFO(dev)->gen >= 4 || IS_G33(dev) ||
- obj->tiling_mode == I915_TILING_NONE)
+ tiling_mode == I915_TILING_NONE)
return 4096;
- /*
- * Older chips need unfenced tiled buffers to be aligned to the left
- * edge of an even tile row (where tile rows are counted as if the bo is
- * placed in a fenced gtt region).
+ /* Previous hardware however needs to be aligned to a power-of-two
+ * tile height. The simplest method for determining this is to reuse
+ * the power-of-tile object size.
*/
- if (IS_GEN2(dev))
- tile_height = 16;
- else if (obj->tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev))
- tile_height = 32;
- else
- tile_height = 8;
-
- return tile_height * obj->stride * 2;
+ return i915_gem_get_gtt_size(dev, size, tiling_mode);
}
int
@@ -1483,7 +1396,7 @@ i915_gem_mmap_gtt(struct drm_file *file,
if (obj->base.size > dev_priv->mm.gtt_mappable_end) {
ret = -E2BIG;
- goto unlock;
+ goto out;
}
if (obj->madv != I915_MADV_WILLNEED) {
@@ -1493,7 +1406,7 @@ i915_gem_mmap_gtt(struct drm_file *file,
}
if (!obj->base.map_list.map) {
- ret = i915_gem_create_mmap_offset(obj);
+ ret = drm_gem_create_mmap_offset(&obj->base);
if (ret)
goto out;
}
@@ -1565,7 +1478,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
obj->pages[i] = page;
}
- if (obj->tiling_mode != I915_TILING_NONE)
+ if (i915_gem_object_needs_bit17_swizzle(obj))
i915_gem_object_do_bit_17_swizzle(obj);
return 0;
@@ -1587,7 +1500,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
BUG_ON(obj->madv == __I915_MADV_PURGED);
- if (obj->tiling_mode != I915_TILING_NONE)
+ if (i915_gem_object_needs_bit17_swizzle(obj))
i915_gem_object_save_bit_17_swizzle(obj);
if (obj->madv == I915_MADV_DONTNEED)
@@ -1771,8 +1684,11 @@ i915_add_request(struct intel_ring_buffer *ring,
ring->outstanding_lazy_request = false;
if (!dev_priv->mm.suspended) {
- mod_timer(&dev_priv->hangcheck_timer,
- jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
+ if (i915_enable_hangcheck) {
+ mod_timer(&dev_priv->hangcheck_timer,
+ jiffies +
+ msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
+ }
if (was_empty)
queue_delayed_work(dev_priv->wq,
&dev_priv->mm.retire_work, HZ);
@@ -1829,7 +1745,7 @@ static void i915_gem_reset_fences(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < dev_priv->num_fence_regs; i++) {
struct drm_i915_fence_reg *reg = &dev_priv->fence_regs[i];
struct drm_i915_gem_object *obj = reg->obj;
@@ -1861,7 +1777,7 @@ void i915_gem_reset(struct drm_device *dev)
* lost bo to the inactive list.
*/
while (!list_empty(&dev_priv->mm.flushing_list)) {
- obj= list_first_entry(&dev_priv->mm.flushing_list,
+ obj = list_first_entry(&dev_priv->mm.flushing_list,
struct drm_i915_gem_object,
mm_list);
@@ -1927,7 +1843,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
while (!list_empty(&ring->active_list)) {
struct drm_i915_gem_object *obj;
- obj= list_first_entry(&ring->active_list,
+ obj = list_first_entry(&ring->active_list,
struct drm_i915_gem_object,
ring_list);
@@ -2143,6 +2059,30 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj)
return 0;
}
+static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj)
+{
+ u32 old_write_domain, old_read_domains;
+
+ /* Act a barrier for all accesses through the GTT */
+ mb();
+
+ /* Force a pagefault for domain tracking on next user access */
+ i915_gem_release_mmap(obj);
+
+ if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0)
+ return;
+
+ old_read_domains = obj->base.read_domains;
+ old_write_domain = obj->base.write_domain;
+
+ obj->base.read_domains &= ~I915_GEM_DOMAIN_GTT;
+ obj->base.write_domain &= ~I915_GEM_DOMAIN_GTT;
+
+ trace_i915_gem_object_change_domain(obj,
+ old_read_domains,
+ old_write_domain);
+}
+
/**
* Unbinds an object from the GTT aperture.
*/
@@ -2159,23 +2099,28 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
return -EINVAL;
}
- /* blow away mappings if mapped through GTT */
- i915_gem_release_mmap(obj);
-
- /* Move the object to the CPU domain to ensure that
- * any possible CPU writes while it's not in the GTT
- * are flushed when we go to remap it. This will
- * also ensure that all pending GPU writes are finished
- * before we unbind.
- */
- ret = i915_gem_object_set_to_cpu_domain(obj, 1);
+ ret = i915_gem_object_finish_gpu(obj);
if (ret == -ERESTARTSYS)
return ret;
/* Continue on if we fail due to EIO, the GPU is hung so we
* should be safe and we need to cleanup or else we might
* cause memory corruption through use-after-free.
*/
+
+ i915_gem_object_finish_gtt(obj);
+
+ /* Move the object to the CPU domain to ensure that
+ * any possible CPU writes while it's not in the GTT
+ * are flushed when we go to remap it.
+ */
+ if (ret == 0)
+ ret = i915_gem_object_set_to_cpu_domain(obj, 1);
+ if (ret == -ERESTARTSYS)
+ return ret;
if (ret) {
+ /* In the event of a disaster, abandon all caches and
+ * hope for the best.
+ */
i915_gem_clflush_object(obj);
obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU;
}
@@ -2248,14 +2193,8 @@ int
i915_gpu_idle(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- bool lists_empty;
int ret, i;
- lists_empty = (list_empty(&dev_priv->mm.flushing_list) &&
- list_empty(&dev_priv->mm.active_list));
- if (lists_empty)
- return 0;
-
/* Flush everything onto the inactive list. */
for (i = 0; i < I915_NUM_RINGS; i++) {
ret = i915_ring_idle(&dev_priv->ring[i]);
@@ -2744,9 +2683,16 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
return -EINVAL;
}
- fence_size = i915_gem_get_gtt_size(obj);
- fence_alignment = i915_gem_get_gtt_alignment(obj);
- unfenced_alignment = i915_gem_get_unfenced_gtt_alignment(obj);
+ fence_size = i915_gem_get_gtt_size(dev,
+ obj->base.size,
+ obj->tiling_mode);
+ fence_alignment = i915_gem_get_gtt_alignment(dev,
+ obj->base.size,
+ obj->tiling_mode);
+ unfenced_alignment =
+ i915_gem_get_unfenced_gtt_alignment(dev,
+ obj->base.size,
+ obj->tiling_mode);
if (alignment == 0)
alignment = map_and_fenceable ? fence_alignment :
@@ -2851,7 +2797,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
fenceable =
obj->gtt_space->size == fence_size &&
- (obj->gtt_space->start & (fence_alignment -1)) == 0;
+ (obj->gtt_space->start & (fence_alignment - 1)) == 0;
mappable =
obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end;
@@ -2997,51 +2943,139 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
return 0;
}
+int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
+ enum i915_cache_level cache_level)
+{
+ int ret;
+
+ if (obj->cache_level == cache_level)
+ return 0;
+
+ if (obj->pin_count) {
+ DRM_DEBUG("can not change the cache level of pinned objects\n");
+ return -EBUSY;
+ }
+
+ if (obj->gtt_space) {
+ ret = i915_gem_object_finish_gpu(obj);
+ if (ret)
+ return ret;
+
+ i915_gem_object_finish_gtt(obj);
+
+ /* Before SandyBridge, you could not use tiling or fence
+ * registers with snooped memory, so relinquish any fences
+ * currently pointing to our region in the aperture.
+ */
+ if (INTEL_INFO(obj->base.dev)->gen < 6) {
+ ret = i915_gem_object_put_fence(obj);
+ if (ret)
+ return ret;
+ }
+
+ i915_gem_gtt_rebind_object(obj, cache_level);
+ }
+
+ if (cache_level == I915_CACHE_NONE) {
+ u32 old_read_domains, old_write_domain;
+
+ /* If we're coming from LLC cached, then we haven't
+ * actually been tracking whether the data is in the
+ * CPU cache or not, since we only allow one bit set
+ * in obj->write_domain and have been skipping the clflushes.
+ * Just set it to the CPU cache for now.
+ */
+ WARN_ON(obj->base.write_domain & ~I915_GEM_DOMAIN_CPU);
+ WARN_ON(obj->base.read_domains & ~I915_GEM_DOMAIN_CPU);
+
+ old_read_domains = obj->base.read_domains;
+ old_write_domain = obj->base.write_domain;
+
+ obj->base.read_domains = I915_GEM_DOMAIN_CPU;
+ obj->base.write_domain = I915_GEM_DOMAIN_CPU;
+
+ trace_i915_gem_object_change_domain(obj,
+ old_read_domains,
+ old_write_domain);
+ }
+
+ obj->cache_level = cache_level;
+ return 0;
+}
+
/*
- * Prepare buffer for display plane. Use uninterruptible for possible flush
- * wait, as in modesetting process we're not supposed to be interrupted.
+ * Prepare buffer for display plane (scanout, cursors, etc).
+ * Can be called from an uninterruptible phase (modesetting) and allows
+ * any flushes to be pipelined (for pageflips).
+ *
+ * For the display plane, we want to be in the GTT but out of any write
+ * domains. So in many ways this looks like set_to_gtt_domain() apart from the
+ * ability to pipeline the waits, pinning and any additional subtleties
+ * that may differentiate the display plane from ordinary buffers.
*/
int
-i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj,
+i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
+ u32 alignment,
struct intel_ring_buffer *pipelined)
{
- uint32_t old_read_domains;
+ u32 old_read_domains, old_write_domain;
int ret;
- /* Not valid to be called on unbound objects. */
- if (obj->gtt_space == NULL)
- return -EINVAL;
-
ret = i915_gem_object_flush_gpu_write_domain(obj);
if (ret)
return ret;
-
- /* Currently, we are always called from an non-interruptible context. */
if (pipelined != obj->ring) {
ret = i915_gem_object_wait_rendering(obj);
- if (ret)
+ if (ret == -ERESTARTSYS)
return ret;
}
+ /* The display engine is not coherent with the LLC cache on gen6. As
+ * a result, we make sure that the pinning that is about to occur is
+ * done with uncached PTEs. This is lowest common denominator for all
+ * chipsets.
+ *
+ * However for gen6+, we could do better by using the GFDT bit instead
+ * of uncaching, which would allow us to flush all the LLC-cached data
+ * with that bit in the PTE to main memory with just one PIPE_CONTROL.
+ */
+ ret = i915_gem_object_set_cache_level(obj, I915_CACHE_NONE);
+ if (ret)
+ return ret;
+
+ /* As the user may map the buffer once pinned in the display plane
+ * (e.g. libkms for the bootup splash), we have to ensure that we
+ * always use map_and_fenceable for all scanout buffers.
+ */
+ ret = i915_gem_object_pin(obj, alignment, true);
+ if (ret)
+ return ret;
+
i915_gem_object_flush_cpu_write_domain(obj);
+ old_write_domain = obj->base.write_domain;
old_read_domains = obj->base.read_domains;
+
+ /* It should now be out of any other write domains, and we can update
+ * the domain values for our changes.
+ */
+ BUG_ON((obj->base.write_domain & ~I915_GEM_DOMAIN_GTT) != 0);
obj->base.read_domains |= I915_GEM_DOMAIN_GTT;
trace_i915_gem_object_change_domain(obj,
old_read_domains,
- obj->base.write_domain);
+ old_write_domain);
return 0;
}
int
-i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj)
+i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj)
{
int ret;
- if (!obj->active)
+ if ((obj->base.read_domains & I915_GEM_GPU_DOMAINS) == 0)
return 0;
if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) {
@@ -3050,6 +3084,9 @@ i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj)
return ret;
}
+ /* Ensure that we invalidate the GPU's caches and TLBs. */
+ obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS;
+
return i915_gem_object_wait_rendering(obj);
}
@@ -3475,9 +3512,11 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
* so emit a request to do so.
*/
request = kzalloc(sizeof(*request), GFP_KERNEL);
- if (request)
- ret = i915_add_request(obj->ring, NULL,request);
- else
+ if (request) {
+ ret = i915_add_request(obj->ring, NULL, request);
+ if (ret)
+ kfree(request);
+ } else
ret = -ENOMEM;
}
@@ -3501,7 +3540,7 @@ int
i915_gem_throttle_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- return i915_gem_ring_throttle(dev, file_priv);
+ return i915_gem_ring_throttle(dev, file_priv);
}
int
@@ -3576,7 +3615,23 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
obj->base.write_domain = I915_GEM_DOMAIN_CPU;
obj->base.read_domains = I915_GEM_DOMAIN_CPU;
- obj->cache_level = I915_CACHE_NONE;
+ if (IS_GEN6(dev) || IS_GEN7(dev)) {
+ /* On Gen6, we can have the GPU use the LLC (the CPU
+ * cache) for about a 10% performance improvement
+ * compared to uncached. Graphics requests other than
+ * display scanout are coherent with the CPU in
+ * accessing this cache. This means in this mode we
+ * don't need to clflush on the CPU side, and on the
+ * GPU side we only need to flush internal caches to
+ * get data visible to the CPU.
+ *
+ * However, we maintain the display planes as UC, and so
+ * need to rebind when first used as such.
+ */
+ obj->cache_level = I915_CACHE_LLC;
+ } else
+ obj->cache_level = I915_CACHE_NONE;
+
obj->base.driver_private = NULL;
obj->fence_reg = I915_FENCE_REG_NONE;
INIT_LIST_HEAD(&obj->mm_list);
@@ -3614,7 +3669,7 @@ static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj)
trace_i915_gem_object_destroy(obj);
if (obj->base.map_list.map)
- i915_gem_free_mmap_offset(obj);
+ drm_gem_free_mmap_offset(&obj->base);
drm_gem_object_release(&obj->base);
i915_gem_info_remove_obj(dev_priv, obj->base.size);
@@ -3824,7 +3879,7 @@ i915_gem_load(struct drm_device *dev)
INIT_LIST_HEAD(&dev_priv->mm.gtt_list);
for (i = 0; i < I915_NUM_RINGS; i++)
init_ring_lists(&dev_priv->ring[i]);
- for (i = 0; i < 16; i++)
+ for (i = 0; i < I915_MAX_NUM_FENCES; i++)
INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list);
INIT_DELAYED_WORK(&dev_priv->mm.retire_work,
i915_gem_retire_work_handler);
diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c
index 8da1899bd24f..cc93cac242d6 100644
--- a/drivers/gpu/drm/i915/i915_gem_debug.c
+++ b/drivers/gpu/drm/i915/i915_gem_debug.c
@@ -72,7 +72,7 @@ i915_verify_lists(struct drm_device *dev)
break;
} else if (!obj->active ||
(obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0 ||
- list_empty(&obj->gpu_write_list)){
+ list_empty(&obj->gpu_write_list)) {
DRM_ERROR("invalid flushing %p (a %d w %x gwl %d)\n",
obj,
obj->active,
@@ -157,7 +157,7 @@ i915_gem_object_check_coherency(struct drm_i915_gem_object *obj, int handle)
for (page = 0; page < obj->size / PAGE_SIZE; page++) {
int i;
- backing_map = kmap_atomic(obj->pages[page], KM_USER0);
+ backing_map = kmap_atomic(obj->pages[page]);
if (backing_map == NULL) {
DRM_ERROR("failed to map backing page\n");
@@ -181,13 +181,13 @@ i915_gem_object_check_coherency(struct drm_i915_gem_object *obj, int handle)
}
}
}
- kunmap_atomic(backing_map, KM_USER0);
+ kunmap_atomic(backing_map);
backing_map = NULL;
}
out:
if (backing_map != NULL)
- kunmap_atomic(backing_map, KM_USER0);
+ kunmap_atomic(backing_map);
iounmap(gtt_mapping);
/* give syslog time to catch up */
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index da05a2692a75..ead5d00f91b0 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -122,7 +122,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
goto found;
}
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
- if (! obj->base.write_domain || obj->pin_count)
+ if (!obj->base.write_domain || obj->pin_count)
continue;
if (mark_free(obj, &unwind_list))
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 4934cf84c320..3693e83a97f3 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -784,7 +784,8 @@ i915_gem_execbuffer_sync_rings(struct drm_i915_gem_object *obj,
}
from->sync_seqno[idx] = seqno;
- return intel_ring_sync(to, from, seqno - 1);
+
+ return to->sync_to(to, from, seqno - 1);
}
static int
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index e46b645773cf..6042c5e6d278 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -49,6 +49,28 @@ static unsigned int cache_level_to_agp_type(struct drm_device *dev,
}
}
+static bool do_idling(struct drm_i915_private *dev_priv)
+{
+ bool ret = dev_priv->mm.interruptible;
+
+ if (unlikely(dev_priv->mm.gtt->do_idle_maps)) {
+ dev_priv->mm.interruptible = false;
+ if (i915_gpu_idle(dev_priv->dev)) {
+ DRM_ERROR("Couldn't idle GPU\n");
+ /* Wait a bit, in hopes it avoids the hang */
+ udelay(10);
+ }
+ }
+
+ return ret;
+}
+
+static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible)
+{
+ if (unlikely(dev_priv->mm.gtt->do_idle_maps))
+ dev_priv->mm.interruptible = interruptible;
+}
+
void i915_gem_restore_gtt_mappings(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -59,24 +81,8 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
(dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE);
list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
- unsigned int agp_type =
- cache_level_to_agp_type(dev, obj->cache_level);
-
i915_gem_clflush_object(obj);
-
- if (dev_priv->mm.gtt->needs_dmar) {
- BUG_ON(!obj->sg_list);
-
- intel_gtt_insert_sg_entries(obj->sg_list,
- obj->num_sg,
- obj->gtt_space->start >> PAGE_SHIFT,
- agp_type);
- } else
- intel_gtt_insert_pages(obj->gtt_space->start
- >> PAGE_SHIFT,
- obj->base.size >> PAGE_SHIFT,
- obj->pages,
- agp_type);
+ i915_gem_gtt_rebind_object(obj, obj->cache_level);
}
intel_gtt_chipset_flush();
@@ -110,8 +116,35 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj)
return 0;
}
+void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj,
+ enum i915_cache_level cache_level)
+{
+ struct drm_device *dev = obj->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned int agp_type = cache_level_to_agp_type(dev, cache_level);
+
+ if (dev_priv->mm.gtt->needs_dmar) {
+ BUG_ON(!obj->sg_list);
+
+ intel_gtt_insert_sg_entries(obj->sg_list,
+ obj->num_sg,
+ obj->gtt_space->start >> PAGE_SHIFT,
+ agp_type);
+ } else
+ intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT,
+ obj->base.size >> PAGE_SHIFT,
+ obj->pages,
+ agp_type);
+}
+
void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
{
+ struct drm_device *dev = obj->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ bool interruptible;
+
+ interruptible = do_idling(dev_priv);
+
intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT);
@@ -119,4 +152,6 @@ void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
intel_gtt_unmap_memory(obj->sg_list, obj->num_sg);
obj->sg_list = NULL;
}
+
+ undo_idling(dev_priv, interruptible);
}
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index 82d70fd9e933..31d334d9d9da 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -92,7 +92,10 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
- if (INTEL_INFO(dev)->gen >= 5) {
+ if (INTEL_INFO(dev)->gen >= 6) {
+ swizzle_x = I915_BIT_6_SWIZZLE_NONE;
+ swizzle_y = I915_BIT_6_SWIZZLE_NONE;
+ } else if (IS_GEN5(dev)) {
/* On Ironlake whatever DRAM config, GPU always do
* same swizzling setup.
*/
@@ -348,7 +351,9 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
/* Rebind if we need a change of alignment */
if (!obj->map_and_fenceable) {
u32 unfenced_alignment =
- i915_gem_get_unfenced_gtt_alignment(obj);
+ i915_gem_get_unfenced_gtt_alignment(dev,
+ obj->base.size,
+ args->tiling_mode);
if (obj->gtt_offset & (unfenced_alignment - 1))
ret = i915_gem_object_unbind(obj);
}
@@ -438,14 +443,9 @@ i915_gem_swizzle_page(struct page *page)
void
i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj)
{
- struct drm_device *dev = obj->base.dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
int page_count = obj->base.size >> PAGE_SHIFT;
int i;
- if (dev_priv->mm.bit_6_swizzle_x != I915_BIT_6_SWIZZLE_9_10_17)
- return;
-
if (obj->bit_17 == NULL)
return;
@@ -462,14 +462,9 @@ i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj)
void
i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj)
{
- struct drm_device *dev = obj->base.dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
int page_count = obj->base.size >> PAGE_SHIFT;
int i;
- if (dev_priv->mm.bit_6_swizzle_x != I915_BIT_6_SWIZZLE_9_10_17)
- return;
-
if (obj->bit_17 == NULL) {
obj->bit_17 = kmalloc(BITS_TO_LONGS(page_count) *
sizeof(long), GFP_KERNEL);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 3b03f85ea627..b40004b55977 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -306,12 +306,15 @@ static void i915_hotplug_work_func(struct work_struct *work)
struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *encoder;
+ mutex_lock(&mode_config->mutex);
DRM_DEBUG_KMS("running encoder hotplug functions\n");
list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
if (encoder->hot_plug)
encoder->hot_plug(encoder);
+ mutex_unlock(&mode_config->mutex);
+
/* Just fire off a uevent and let userspace tell us what to do */
drm_helper_hpd_irq_event(dev);
}
@@ -361,10 +364,12 @@ static void notify_ring(struct drm_device *dev,
ring->irq_seqno = seqno;
wake_up_all(&ring->irq_queue);
-
- dev_priv->hangcheck_count = 0;
- mod_timer(&dev_priv->hangcheck_timer,
- jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
+ if (i915_enable_hangcheck) {
+ dev_priv->hangcheck_count = 0;
+ mod_timer(&dev_priv->hangcheck_timer,
+ jiffies +
+ msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
+ }
}
static void gen6_pm_rps_work(struct work_struct *work)
@@ -378,6 +383,7 @@ static void gen6_pm_rps_work(struct work_struct *work)
pm_iir = dev_priv->pm_iir;
dev_priv->pm_iir = 0;
pm_imr = I915_READ(GEN6_PMIMR);
+ I915_WRITE(GEN6_PMIMR, 0);
spin_unlock_irq(&dev_priv->rps_lock);
if (!pm_iir)
@@ -415,7 +421,6 @@ static void gen6_pm_rps_work(struct work_struct *work)
* an *extremely* unlikely race with gen6_rps_enable() that is prevented
* by holding struct_mutex for the duration of the write.
*/
- I915_WRITE(GEN6_PMIMR, pm_imr & ~pm_iir);
mutex_unlock(&dev_priv->dev->struct_mutex);
}
@@ -531,8 +536,9 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS)
unsigned long flags;
spin_lock_irqsave(&dev_priv->rps_lock, flags);
WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n");
- I915_WRITE(GEN6_PMIMR, pm_iir);
dev_priv->pm_iir |= pm_iir;
+ I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir);
+ POSTING_READ(GEN6_PMIMR);
spin_unlock_irqrestore(&dev_priv->rps_lock, flags);
queue_work(dev_priv->wq, &dev_priv->rps_work);
}
@@ -644,8 +650,9 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
unsigned long flags;
spin_lock_irqsave(&dev_priv->rps_lock, flags);
WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n");
- I915_WRITE(GEN6_PMIMR, pm_iir);
dev_priv->pm_iir |= pm_iir;
+ I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir);
+ POSTING_READ(GEN6_PMIMR);
spin_unlock_irqrestore(&dev_priv->rps_lock, flags);
queue_work(dev_priv->wq, &dev_priv->rps_work);
}
@@ -706,7 +713,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
page_count = src->base.size / PAGE_SIZE;
- dst = kmalloc(sizeof(*dst) + page_count * sizeof (u32 *), GFP_ATOMIC);
+ dst = kmalloc(sizeof(*dst) + page_count * sizeof(u32 *), GFP_ATOMIC);
if (dst == NULL)
return NULL;
@@ -817,6 +824,7 @@ static void i915_gem_record_fences(struct drm_device *dev,
/* Fences */
switch (INTEL_INFO(dev)->gen) {
+ case 7:
case 6:
for (i = 0; i < 16; i++)
error->fence[i] = I915_READ64(FENCE_REG_SANDYBRIDGE_0 + (i * 8));
@@ -1488,7 +1496,7 @@ static int ironlake_enable_vblank(struct drm_device *dev, int pipe)
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
ironlake_enable_display_irq(dev_priv, (pipe == 0) ?
- DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
+ DE_PIPEA_VBLANK : DE_PIPEB_VBLANK);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
return 0;
@@ -1536,7 +1544,7 @@ static void ironlake_disable_vblank(struct drm_device *dev, int pipe)
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
ironlake_disable_display_irq(dev_priv, (pipe == 0) ?
- DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
+ DE_PIPEA_VBLANK : DE_PIPEB_VBLANK);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
@@ -1664,6 +1672,9 @@ void i915_hangcheck_elapsed(unsigned long data)
uint32_t acthd, instdone, instdone1;
bool err = false;
+ if (!i915_enable_hangcheck)
+ return;
+
/* If all work is done then ACTHD clearly hasn't advanced. */
if (i915_hangcheck_ring_idle(&dev_priv->ring[RCS], &err) &&
i915_hangcheck_ring_idle(&dev_priv->ring[VCS], &err) &&
@@ -1769,6 +1780,26 @@ static void ironlake_irq_preinstall(struct drm_device *dev)
POSTING_READ(SDEIER);
}
+/*
+ * Enable digital hotplug on the PCH, and configure the DP short pulse
+ * duration to 2ms (which is the minimum in the Display Port spec)
+ *
+ * This register is the same on all known PCH chips.
+ */
+
+static void ironlake_enable_pch_hotplug(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ u32 hotplug;
+
+ hotplug = I915_READ(PCH_PORT_HOTPLUG);
+ hotplug &= ~(PORTD_PULSE_DURATION_MASK|PORTC_PULSE_DURATION_MASK|PORTB_PULSE_DURATION_MASK);
+ hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms;
+ hotplug |= PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms;
+ hotplug |= PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms;
+ I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
+}
+
static int ironlake_irq_postinstall(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -1831,6 +1862,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
I915_WRITE(SDEIER, hotplug_mask);
POSTING_READ(SDEIER);
+ ironlake_enable_pch_hotplug(dev);
+
if (IS_IRONLAKE_M(dev)) {
/* Clear & enable PCU event interrupts */
I915_WRITE(DEIIR, DE_PCU_EVENT);
@@ -1888,6 +1921,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
I915_WRITE(SDEIER, hotplug_mask);
POSTING_READ(SDEIER);
+ ironlake_enable_pch_hotplug(dev);
+
return 0;
}
@@ -2012,6 +2047,10 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
I915_WRITE(GTIMR, 0xffffffff);
I915_WRITE(GTIER, 0x0);
I915_WRITE(GTIIR, I915_READ(GTIIR));
+
+ I915_WRITE(SDEIMR, 0xffffffff);
+ I915_WRITE(SDEIER, 0x0);
+ I915_WRITE(SDEIIR, I915_READ(SDEIIR));
}
static void i915_driver_irq_uninstall(struct drm_device * dev)
@@ -2050,8 +2089,10 @@ void intel_irq_init(struct drm_device *dev)
dev->driver->get_vblank_counter = gm45_get_vblank_counter;
}
-
- dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
+ else
+ dev->driver->get_vblank_timestamp = NULL;
dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
if (IS_IVYBRIDGE(dev)) {
diff --git a/drivers/gpu/drm/i915/i915_mem.c b/drivers/gpu/drm/i915/i915_mem.c
index 83b7b81bb2b8..cc8f6d49cf20 100644
--- a/drivers/gpu/drm/i915/i915_mem.c
+++ b/drivers/gpu/drm/i915/i915_mem.c
@@ -202,7 +202,7 @@ static int init_heap(struct mem_block **heap, int start, int size)
blocks->next = blocks->prev = *heap;
memset(*heap, 0, sizeof(**heap));
- (*heap)->file_priv = (struct drm_file *) - 1;
+ (*heap)->file_priv = (struct drm_file *) -1;
(*heap)->next = (*heap)->prev = blocks;
return 0;
}
@@ -359,19 +359,19 @@ int i915_mem_init_heap(struct drm_device *dev, void *data,
return init_heap(heap, initheap->start, initheap->size);
}
-int i915_mem_destroy_heap( struct drm_device *dev, void *data,
- struct drm_file *file_priv )
+int i915_mem_destroy_heap(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = dev->dev_private;
drm_i915_mem_destroy_heap_t *destroyheap = data;
struct mem_block **heap;
- if ( !dev_priv ) {
- DRM_ERROR( "called with no initialization\n" );
+ if (!dev_priv) {
+ DRM_ERROR("called with no initialization\n");
return -EINVAL;
}
- heap = get_heap( dev_priv, destroyheap->region );
+ heap = get_heap(dev_priv, destroyheap->region);
if (!heap) {
DRM_ERROR("get_heap failed");
return -EFAULT;
@@ -382,6 +382,6 @@ int i915_mem_destroy_heap( struct drm_device *dev, void *data,
return -EFAULT;
}
- i915_mem_takedown( heap );
+ i915_mem_takedown(heap);
return 0;
}
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 5d5def756c9e..b080cc824001 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -78,6 +78,14 @@
#define GRDOM_RENDER (1<<2)
#define GRDOM_MEDIA (3<<2)
+#define GEN6_MBCUNIT_SNPCR 0x900c /* for LLC config */
+#define GEN6_MBC_SNPCR_SHIFT 21
+#define GEN6_MBC_SNPCR_MASK (3<<21)
+#define GEN6_MBC_SNPCR_MAX (0<<21)
+#define GEN6_MBC_SNPCR_MED (1<<21)
+#define GEN6_MBC_SNPCR_LOW (2<<21)
+#define GEN6_MBC_SNPCR_MIN (3<<21) /* only 1/16th of the cache is shared */
+
#define GEN6_GDRST 0x941c
#define GEN6_GRDOM_FULL (1 << 0)
#define GEN6_GRDOM_RENDER (1 << 1)
@@ -148,7 +156,7 @@
#define MI_SUSPEND_FLUSH MI_INSTR(0x0b, 0)
#define MI_SUSPEND_FLUSH_EN (1<<0)
#define MI_REPORT_HEAD MI_INSTR(0x07, 0)
-#define MI_OVERLAY_FLIP MI_INSTR(0x11,0)
+#define MI_OVERLAY_FLIP MI_INSTR(0x11, 0)
#define MI_OVERLAY_CONTINUE (0x0<<21)
#define MI_OVERLAY_ON (0x1<<21)
#define MI_OVERLAY_OFF (0x2<<21)
@@ -186,6 +194,13 @@
#define MI_SEMAPHORE_UPDATE (1<<21)
#define MI_SEMAPHORE_COMPARE (1<<20)
#define MI_SEMAPHORE_REGISTER (1<<18)
+#define MI_SEMAPHORE_SYNC_RV (2<<16)
+#define MI_SEMAPHORE_SYNC_RB (0<<16)
+#define MI_SEMAPHORE_SYNC_VR (0<<16)
+#define MI_SEMAPHORE_SYNC_VB (2<<16)
+#define MI_SEMAPHORE_SYNC_BR (2<<16)
+#define MI_SEMAPHORE_SYNC_BV (0<<16)
+#define MI_SEMAPHORE_SYNC_INVALID (1<<0)
/*
* 3D instructions used by the kernel
*/
@@ -227,16 +242,22 @@
#define ASYNC_FLIP (1<<22)
#define DISPLAY_PLANE_A (0<<20)
#define DISPLAY_PLANE_B (1<<20)
-#define GFX_OP_PIPE_CONTROL ((0x3<<29)|(0x3<<27)|(0x2<<24)|2)
-#define PIPE_CONTROL_QW_WRITE (1<<14)
-#define PIPE_CONTROL_DEPTH_STALL (1<<13)
-#define PIPE_CONTROL_WC_FLUSH (1<<12)
-#define PIPE_CONTROL_IS_FLUSH (1<<11) /* MBZ on Ironlake */
-#define PIPE_CONTROL_TC_FLUSH (1<<10) /* GM45+ only */
-#define PIPE_CONTROL_ISP_DIS (1<<9)
-#define PIPE_CONTROL_NOTIFY (1<<8)
+#define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|(len-2))
+#define PIPE_CONTROL_CS_STALL (1<<20)
+#define PIPE_CONTROL_QW_WRITE (1<<14)
+#define PIPE_CONTROL_DEPTH_STALL (1<<13)
+#define PIPE_CONTROL_WRITE_FLUSH (1<<12)
+#define PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH (1<<12) /* gen6+ */
+#define PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE (1<<11) /* MBZ on Ironlake */
+#define PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE (1<<10) /* GM45+ only */
+#define PIPE_CONTROL_INDIRECT_STATE_DISABLE (1<<9)
+#define PIPE_CONTROL_NOTIFY (1<<8)
+#define PIPE_CONTROL_VF_CACHE_INVALIDATE (1<<4)
+#define PIPE_CONTROL_CONST_CACHE_INVALIDATE (1<<3)
+#define PIPE_CONTROL_STATE_CACHE_INVALIDATE (1<<2)
+#define PIPE_CONTROL_STALL_AT_SCOREBOARD (1<<1)
+#define PIPE_CONTROL_DEPTH_CACHE_FLUSH (1<<0)
#define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */
-#define PIPE_CONTROL_STALL_EN (1<<1) /* in addr word, Ironlake+ only */
/*
@@ -288,6 +309,12 @@
#define RING_CTL(base) ((base)+0x3c)
#define RING_SYNC_0(base) ((base)+0x40)
#define RING_SYNC_1(base) ((base)+0x44)
+#define GEN6_RVSYNC (RING_SYNC_0(RENDER_RING_BASE))
+#define GEN6_RBSYNC (RING_SYNC_1(RENDER_RING_BASE))
+#define GEN6_VRSYNC (RING_SYNC_1(GEN6_BSD_RING_BASE))
+#define GEN6_VBSYNC (RING_SYNC_0(GEN6_BSD_RING_BASE))
+#define GEN6_BRSYNC (RING_SYNC_0(BLT_RING_BASE))
+#define GEN6_BVSYNC (RING_SYNC_1(BLT_RING_BASE))
#define RING_MAX_IDLE(base) ((base)+0x54)
#define RING_HWS_PGA(base) ((base)+0x80)
#define RING_HWS_PGA_GEN6(base) ((base)+0x2080)
@@ -367,6 +394,7 @@
# define MI_FLUSH_ENABLE (1 << 11)
#define GFX_MODE 0x02520
+#define GFX_MODE_GEN7 0x0229c
#define GFX_RUN_LIST_ENABLE (1<<15)
#define GFX_TLB_INVALIDATE_ALWAYS (1<<13)
#define GFX_SURFACE_FAULT_ENABLE (1<<12)
@@ -374,6 +402,9 @@
#define GFX_PSMI_GRANULARITY (1<<10)
#define GFX_PPGTT_ENABLE (1<<9)
+#define GFX_MODE_ENABLE(bit) (((bit) << 16) | (bit))
+#define GFX_MODE_DISABLE(bit) (((bit) << 16) | (0))
+
#define SCPD0 0x0209c /* 915+ only */
#define IER 0x020a0
#define IIR 0x020a4
@@ -458,7 +489,7 @@
/* Enables non-sequential data reads through arbiter
*/
-#define MI_ARB_DUAL_DATA_PHASE_DISABLE (1 << 9)
+#define MI_ARB_DUAL_DATA_PHASE_DISABLE (1 << 9)
/* Disable FSB snooping of cacheable write cycles from binner/render
* command stream
@@ -579,6 +610,7 @@
#define DPFC_CTL_PLANEA (0<<30)
#define DPFC_CTL_PLANEB (1<<30)
#define DPFC_CTL_FENCE_EN (1<<29)
+#define DPFC_CTL_PERSISTENT_MODE (1<<25)
#define DPFC_SR_EN (1<<10)
#define DPFC_CTL_LIMIT_1X (0<<6)
#define DPFC_CTL_LIMIT_2X (1<<6)
@@ -613,7 +645,7 @@
#define ILK_DISPLAY_CHICKEN1 0x42000
#define ILK_FBCQ_DIS (1<<22)
-#define ILK_PABSTRETCH_DIS (1<<21)
+#define ILK_PABSTRETCH_DIS (1<<21)
/*
@@ -1309,6 +1341,7 @@
#define ADPA_PIPE_SELECT_MASK (1<<30)
#define ADPA_PIPE_A_SELECT 0
#define ADPA_PIPE_B_SELECT (1<<30)
+#define ADPA_PIPE_SELECT(pipe) ((pipe) << 30)
#define ADPA_USE_VGA_HVPOLARITY (1<<15)
#define ADPA_SETS_HVPOLARITY 0
#define ADPA_VSYNC_CNTL_DISABLE (1<<11)
@@ -1451,6 +1484,7 @@
/* Selects pipe B for LVDS data. Must be set on pre-965. */
#define LVDS_PIPEB_SELECT (1 << 30)
#define LVDS_PIPE_MASK (1 << 30)
+#define LVDS_PIPE(pipe) ((pipe) << 30)
/* LVDS dithering flag on 965/g4x platform */
#define LVDS_ENABLE_DITHER (1 << 25)
/* LVDS sync polarity flags. Set to invert (i.e. negative) */
@@ -1490,9 +1524,6 @@
#define LVDS_B0B3_POWER_DOWN (0 << 2)
#define LVDS_B0B3_POWER_UP (3 << 2)
-#define LVDS_PIPE_ENABLED(V, P) \
- (((V) & (LVDS_PIPE_MASK | LVDS_PORT_EN)) == ((P) << 30 | LVDS_PORT_EN))
-
/* Video Data Island Packet control */
#define VIDEO_DIP_DATA 0x61178
#define VIDEO_DIP_CTL 0x61170
@@ -1505,6 +1536,7 @@
#define VIDEO_DIP_SELECT_AVI (0 << 19)
#define VIDEO_DIP_SELECT_VENDOR (1 << 19)
#define VIDEO_DIP_SELECT_SPD (3 << 19)
+#define VIDEO_DIP_SELECT_MASK (3 << 19)
#define VIDEO_DIP_FREQ_ONCE (0 << 16)
#define VIDEO_DIP_FREQ_VSYNC (1 << 16)
#define VIDEO_DIP_FREQ_2VSYNC (2 << 16)
@@ -1521,12 +1553,21 @@
*/
#define PP_READY (1 << 30)
#define PP_SEQUENCE_NONE (0 << 28)
-#define PP_SEQUENCE_ON (1 << 28)
-#define PP_SEQUENCE_OFF (2 << 28)
-#define PP_SEQUENCE_MASK 0x30000000
+#define PP_SEQUENCE_POWER_UP (1 << 28)
+#define PP_SEQUENCE_POWER_DOWN (2 << 28)
+#define PP_SEQUENCE_MASK (3 << 28)
+#define PP_SEQUENCE_SHIFT 28
#define PP_CYCLE_DELAY_ACTIVE (1 << 27)
-#define PP_SEQUENCE_STATE_ON_IDLE (1 << 3)
#define PP_SEQUENCE_STATE_MASK 0x0000000f
+#define PP_SEQUENCE_STATE_OFF_IDLE (0x0 << 0)
+#define PP_SEQUENCE_STATE_OFF_S0_1 (0x1 << 0)
+#define PP_SEQUENCE_STATE_OFF_S0_2 (0x2 << 0)
+#define PP_SEQUENCE_STATE_OFF_S0_3 (0x3 << 0)
+#define PP_SEQUENCE_STATE_ON_IDLE (0x8 << 0)
+#define PP_SEQUENCE_STATE_ON_S1_0 (0x9 << 0)
+#define PP_SEQUENCE_STATE_ON_S1_2 (0xa << 0)
+#define PP_SEQUENCE_STATE_ON_S1_3 (0xb << 0)
+#define PP_SEQUENCE_STATE_RESET (0xf << 0)
#define PP_CONTROL 0x61204
#define POWER_TARGET_ON (1 << 0)
#define PP_ON_DELAYS 0x61208
@@ -2083,9 +2124,6 @@
#define DP_PIPEB_SELECT (1 << 30)
#define DP_PIPE_MASK (1 << 30)
-#define DP_PIPE_ENABLED(V, P) \
- (((V) & (DP_PIPE_MASK | DP_PORT_EN)) == ((P) << 30 | DP_PORT_EN))
-
/* Link training mode - select a suitable mode for each stage */
#define DP_LINK_TRAIN_PAT_1 (0 << 28)
#define DP_LINK_TRAIN_PAT_2 (1 << 28)
@@ -2348,7 +2386,7 @@
#define DSPFW1 0x70034
#define DSPFW_SR_SHIFT 23
-#define DSPFW_SR_MASK (0x1ff<<23)
+#define DSPFW_SR_MASK (0x1ff<<23)
#define DSPFW_CURSORB_SHIFT 16
#define DSPFW_CURSORB_MASK (0x3f<<16)
#define DSPFW_PLANEB_SHIFT 8
@@ -2406,6 +2444,7 @@
#define WM0_PIPE_CURSOR_MASK (0x1f)
#define WM0_PIPEB_ILK 0x45104
+#define WM0_PIPEC_IVB 0x45200
#define WM1_LP_ILK 0x45108
#define WM1_LP_SR_EN (1<<31)
#define WM1_LP_LATENCY_SHIFT 24
@@ -2544,10 +2583,18 @@
#define _CURBBASE 0x700c4
#define _CURBPOS 0x700c8
+#define _CURBCNTR_IVB 0x71080
+#define _CURBBASE_IVB 0x71084
+#define _CURBPOS_IVB 0x71088
+
#define CURCNTR(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR)
#define CURBASE(pipe) _PIPE(pipe, _CURABASE, _CURBBASE)
#define CURPOS(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS)
+#define CURCNTR_IVB(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR_IVB)
+#define CURBASE_IVB(pipe) _PIPE(pipe, _CURABASE, _CURBBASE_IVB)
+#define CURPOS_IVB(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS_IVB)
+
/* Display A control */
#define _DSPACNTR 0x70180
#define DISPLAY_PLANE_ENABLE (1<<31)
@@ -2893,12 +2940,13 @@
#define SDEIER 0xc400c
/* digital port hotplug */
-#define PCH_PORT_HOTPLUG 0xc4030
+#define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */
#define PORTD_HOTPLUG_ENABLE (1 << 20)
#define PORTD_PULSE_DURATION_2ms (0)
#define PORTD_PULSE_DURATION_4_5ms (1 << 18)
#define PORTD_PULSE_DURATION_6ms (2 << 18)
#define PORTD_PULSE_DURATION_100ms (3 << 18)
+#define PORTD_PULSE_DURATION_MASK (3 << 18)
#define PORTD_HOTPLUG_NO_DETECT (0)
#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16)
#define PORTD_HOTPLUG_LONG_DETECT (1 << 17)
@@ -2907,6 +2955,7 @@
#define PORTC_PULSE_DURATION_4_5ms (1 << 10)
#define PORTC_PULSE_DURATION_6ms (2 << 10)
#define PORTC_PULSE_DURATION_100ms (3 << 10)
+#define PORTC_PULSE_DURATION_MASK (3 << 10)
#define PORTC_HOTPLUG_NO_DETECT (0)
#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8)
#define PORTC_HOTPLUG_LONG_DETECT (1 << 9)
@@ -2915,6 +2964,7 @@
#define PORTB_PULSE_DURATION_4_5ms (1 << 2)
#define PORTB_PULSE_DURATION_6ms (2 << 2)
#define PORTB_PULSE_DURATION_100ms (3 << 2)
+#define PORTB_PULSE_DURATION_MASK (3 << 2)
#define PORTB_HOTPLUG_NO_DETECT (0)
#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0)
#define PORTB_HOTPLUG_LONG_DETECT (1 << 1)
@@ -2935,15 +2985,15 @@
#define _PCH_DPLL_A 0xc6014
#define _PCH_DPLL_B 0xc6018
-#define PCH_DPLL(pipe) _PIPE(pipe, _PCH_DPLL_A, _PCH_DPLL_B)
+#define PCH_DPLL(pipe) (pipe == 0 ? _PCH_DPLL_A : _PCH_DPLL_B)
#define _PCH_FPA0 0xc6040
#define FP_CB_TUNE (0x3<<22)
#define _PCH_FPA1 0xc6044
#define _PCH_FPB0 0xc6048
#define _PCH_FPB1 0xc604c
-#define PCH_FP0(pipe) _PIPE(pipe, _PCH_FPA0, _PCH_FPB0)
-#define PCH_FP1(pipe) _PIPE(pipe, _PCH_FPA1, _PCH_FPB1)
+#define PCH_FP0(pipe) (pipe == 0 ? _PCH_FPA0 : _PCH_FPB0)
+#define PCH_FP1(pipe) (pipe == 0 ? _PCH_FPA1 : _PCH_FPB1)
#define PCH_DPLL_TEST 0xc606c
@@ -3023,6 +3073,20 @@
#define _TRANSA_DP_LINK_M2 0xe0048
#define _TRANSA_DP_LINK_N2 0xe004c
+/* Per-transcoder DIP controls */
+
+#define _VIDEO_DIP_CTL_A 0xe0200
+#define _VIDEO_DIP_DATA_A 0xe0208
+#define _VIDEO_DIP_GCP_A 0xe0210
+
+#define _VIDEO_DIP_CTL_B 0xe1200
+#define _VIDEO_DIP_DATA_B 0xe1208
+#define _VIDEO_DIP_GCP_B 0xe1210
+
+#define TVIDEO_DIP_CTL(pipe) _PIPE(pipe, _VIDEO_DIP_CTL_A, _VIDEO_DIP_CTL_B)
+#define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B)
+#define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B)
+
#define _TRANS_HTOTAL_B 0xe1000
#define _TRANS_HBLANK_B 0xe1004
#define _TRANS_HSYNC_B 0xe1008
@@ -3075,6 +3139,16 @@
#define TRANS_6BPC (2<<5)
#define TRANS_12BPC (3<<5)
+#define _TRANSA_CHICKEN2 0xf0064
+#define _TRANSB_CHICKEN2 0xf1064
+#define TRANS_CHICKEN2(pipe) _PIPE(pipe, _TRANSA_CHICKEN2, _TRANSB_CHICKEN2)
+#define TRANS_AUTOTRAIN_GEN_STALL_DIS (1<<31)
+
+#define SOUTH_CHICKEN1 0xc2000
+#define FDIA_PHASE_SYNC_SHIFT_OVR 19
+#define FDIA_PHASE_SYNC_SHIFT_EN 18
+#define FDI_PHASE_SYNC_OVR(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_OVR - ((pipe) * 2)))
+#define FDI_PHASE_SYNC_EN(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_EN - ((pipe) * 2)))
#define SOUTH_CHICKEN2 0xc2004
#define DPLS_EDP_PPS_FIX_DIS (1<<0)
@@ -3133,6 +3207,7 @@
#define FDI_LINK_TRAIN_NONE_IVB (3<<8)
/* both Tx and Rx */
+#define FDI_COMPOSITE_SYNC (1<<11)
#define FDI_LINK_TRAIN_AUTO (1<<10)
#define FDI_SCRAMBLING_ENABLE (0<<7)
#define FDI_SCRAMBLING_DISABLE (1<<7)
@@ -3225,14 +3300,12 @@
#define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17)
#define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16)
-#define ADPA_PIPE_ENABLED(V, P) \
- (((V) & (ADPA_TRANS_SELECT_MASK | ADPA_DAC_ENABLE)) == ((P) << 30 | ADPA_DAC_ENABLE))
-
/* or SDVOB */
#define HDMIB 0xe1140
#define PORT_ENABLE (1 << 31)
#define TRANSCODER_A (0)
#define TRANSCODER_B (1 << 30)
+#define TRANSCODER(pipe) ((pipe) << 30)
#define TRANSCODER_MASK (1 << 30)
#define COLOR_FORMAT_8bpc (0)
#define COLOR_FORMAT_12bpc (3 << 26)
@@ -3249,9 +3322,6 @@
#define HSYNC_ACTIVE_HIGH (1 << 3)
#define PORT_DETECTED (1 << 2)
-#define HDMI_PIPE_ENABLED(V, P) \
- (((V) & (TRANSCODER_MASK | PORT_ENABLE)) == ((P) << 30 | PORT_ENABLE))
-
/* PCH SDVOB multiplex with HDMIB */
#define PCH_SDVOB HDMIB
@@ -3279,15 +3349,35 @@
#define PCH_PP_STATUS 0xc7200
#define PCH_PP_CONTROL 0xc7204
#define PANEL_UNLOCK_REGS (0xabcd << 16)
+#define PANEL_UNLOCK_MASK (0xffff << 16)
#define EDP_FORCE_VDD (1 << 3)
#define EDP_BLC_ENABLE (1 << 2)
#define PANEL_POWER_RESET (1 << 1)
#define PANEL_POWER_OFF (0 << 0)
#define PANEL_POWER_ON (1 << 0)
#define PCH_PP_ON_DELAYS 0xc7208
+#define PANEL_PORT_SELECT_MASK (3 << 30)
+#define PANEL_PORT_SELECT_LVDS (0 << 30)
+#define PANEL_PORT_SELECT_DPA (1 << 30)
#define EDP_PANEL (1 << 30)
+#define PANEL_PORT_SELECT_DPC (2 << 30)
+#define PANEL_PORT_SELECT_DPD (3 << 30)
+#define PANEL_POWER_UP_DELAY_MASK (0x1fff0000)
+#define PANEL_POWER_UP_DELAY_SHIFT 16
+#define PANEL_LIGHT_ON_DELAY_MASK (0x1fff)
+#define PANEL_LIGHT_ON_DELAY_SHIFT 0
+
#define PCH_PP_OFF_DELAYS 0xc720c
+#define PANEL_POWER_DOWN_DELAY_MASK (0x1fff0000)
+#define PANEL_POWER_DOWN_DELAY_SHIFT 16
+#define PANEL_LIGHT_OFF_DELAY_MASK (0x1fff)
+#define PANEL_LIGHT_OFF_DELAY_SHIFT 0
+
#define PCH_PP_DIVISOR 0xc7210
+#define PP_REFERENCE_DIVIDER_MASK (0xffffff00)
+#define PP_REFERENCE_DIVIDER_SHIFT 8
+#define PANEL_POWER_CYCLE_DELAY_MASK (0x1f)
+#define PANEL_POWER_CYCLE_DELAY_SHIFT 0
#define PCH_DP_B 0xe4100
#define PCH_DPB_AUX_CH_CTL 0xe4110
@@ -3318,6 +3408,7 @@
#define PORT_TRANS_B_SEL_CPT (1<<29)
#define PORT_TRANS_C_SEL_CPT (2<<29)
#define PORT_TRANS_SEL_MASK (3<<29)
+#define PORT_TRANS_SEL_CPT(pipe) ((pipe) << 29)
#define TRANS_DP_CTL_A 0xe0300
#define TRANS_DP_CTL_B 0xe1300
@@ -3360,6 +3451,11 @@
#define FORCEWAKE_ACK 0x130090
#define GT_FIFO_FREE_ENTRIES 0x120008
+#define GT_FIFO_NUM_RESERVED_ENTRIES 20
+
+#define GEN6_UCGCTL2 0x9404
+# define GEN6_RCPBUNIT_CLOCK_GATE_DISABLE (1 << 12)
+# define GEN6_RCCUNIT_CLOCK_GATE_DISABLE (1 << 11)
#define GEN6_RPNSWREQ 0xA008
#define GEN6_TURBO_DISABLE (1<<31)
@@ -3434,7 +3530,34 @@
#define GEN6_PCODE_MAILBOX 0x138124
#define GEN6_PCODE_READY (1<<31)
#define GEN6_READ_OC_PARAMS 0xc
-#define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x9
+#define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x8
+#define GEN6_PCODE_READ_MIN_FREQ_TABLE 0x9
#define GEN6_PCODE_DATA 0x138128
+#define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8
+
+#define G4X_AUD_VID_DID 0x62020
+#define INTEL_AUDIO_DEVCL 0x808629FB
+#define INTEL_AUDIO_DEVBLC 0x80862801
+#define INTEL_AUDIO_DEVCTG 0x80862802
+
+#define G4X_AUD_CNTL_ST 0x620B4
+#define G4X_ELDV_DEVCL_DEVBLC (1 << 13)
+#define G4X_ELDV_DEVCTG (1 << 14)
+#define G4X_ELD_ADDR (0xf << 5)
+#define G4X_ELD_ACK (1 << 4)
+#define G4X_HDMIW_HDMIEDID 0x6210C
+
+#define GEN5_HDMIW_HDMIEDID_A 0xE2050
+#define GEN5_AUD_CNTL_ST_A 0xE20B4
+#define GEN5_ELD_BUFFER_SIZE (0x1f << 10)
+#define GEN5_ELD_ADDRESS (0x1f << 5)
+#define GEN5_ELD_ACK (1 << 4)
+#define GEN5_AUD_CNTL_ST2 0xE20C0
+#define GEN5_ELD_VALIDB (1 << 0)
+#define GEN5_CP_READYB (1 << 1)
+
+#define GEN7_HDMIW_HDMIEDID_A 0xE5050
+#define GEN7_AUD_CNTRL_ST_A 0xE50B4
+#define GEN7_AUD_CNTRL_ST2 0xE50C0
#endif /* _I915_REG_H_ */
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index 5257cfc34c35..7886e4fb60e3 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -60,7 +60,7 @@ static void i915_save_palette(struct drm_device *dev, enum pipe pipe)
else
array = dev_priv->save_palette_b;
- for(i = 0; i < 256; i++)
+ for (i = 0; i < 256; i++)
array[i] = I915_READ(reg + (i << 2));
}
@@ -82,7 +82,7 @@ static void i915_restore_palette(struct drm_device *dev, enum pipe pipe)
else
array = dev_priv->save_palette_b;
- for(i = 0; i < 256; i++)
+ for (i = 0; i < 256; i++)
I915_WRITE(reg + (i << 2), array[i]);
}
@@ -370,6 +370,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
/* Fences */
switch (INTEL_INFO(dev)->gen) {
+ case 7:
case 6:
for (i = 0; i < 16; i++)
dev_priv->saveFENCE[i] = I915_READ64(FENCE_REG_SANDYBRIDGE_0 + (i * 8));
@@ -404,6 +405,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
/* Fences */
switch (INTEL_INFO(dev)->gen) {
+ case 7:
case 6:
for (i = 0; i < 16; i++)
I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (i * 8), dev_priv->saveFENCE[i]);
@@ -760,15 +762,13 @@ static void i915_restore_display(struct drm_device *dev)
/* FIXME: restore TV & SDVO state */
/* only restore FBC info on the platform that supports FBC*/
+ intel_disable_fbc(dev);
if (I915_HAS_FBC(dev)) {
if (HAS_PCH_SPLIT(dev)) {
- ironlake_disable_fbc(dev);
I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE);
} else if (IS_GM45(dev)) {
- g4x_disable_fbc(dev);
I915_WRITE(DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE);
} else {
- i8xx_disable_fbc(dev);
I915_WRITE(FBC_CFB_BASE, dev_priv->saveFBC_CFB_BASE);
I915_WRITE(FBC_LL_BASE, dev_priv->saveFBC_LL_BASE);
I915_WRITE(FBC_CONTROL2, dev_priv->saveFBC_CONTROL2);
@@ -814,6 +814,7 @@ int i915_save_state(struct drm_device *dev)
dev_priv->saveFDI_RXB_IMR = I915_READ(_FDI_RXB_IMR);
dev_priv->saveMCHBAR_RENDER_STANDBY =
I915_READ(RSTDBYCTL);
+ dev_priv->savePCH_PORT_HOTPLUG = I915_READ(PCH_PORT_HOTPLUG);
} else {
dev_priv->saveIER = I915_READ(IER);
dev_priv->saveIMR = I915_READ(IMR);
@@ -865,29 +866,33 @@ int i915_restore_state(struct drm_device *dev)
I915_WRITE(GTIMR, dev_priv->saveGTIMR);
I915_WRITE(_FDI_RXA_IMR, dev_priv->saveFDI_RXA_IMR);
I915_WRITE(_FDI_RXB_IMR, dev_priv->saveFDI_RXB_IMR);
+ I915_WRITE(PCH_PORT_HOTPLUG, dev_priv->savePCH_PORT_HOTPLUG);
} else {
I915_WRITE(IER, dev_priv->saveIER);
I915_WRITE(IMR, dev_priv->saveIMR);
}
mutex_unlock(&dev->struct_mutex);
- intel_init_clock_gating(dev);
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ intel_init_clock_gating(dev);
if (IS_IRONLAKE_M(dev)) {
ironlake_enable_drps(dev);
intel_init_emon(dev);
}
- if (IS_GEN6(dev))
+ if (IS_GEN6(dev)) {
gen6_enable_rps(dev_priv);
+ gen6_update_ring_freq(dev_priv);
+ }
mutex_lock(&dev->struct_mutex);
/* Cache mode state */
- I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
+ I915_WRITE(CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
/* Memory arbitration state */
- I915_WRITE (MI_ARB_STATE, dev_priv->saveMI_ARB_STATE | 0xffff0000);
+ I915_WRITE(MI_ARB_STATE, dev_priv->saveMI_ARB_STATE | 0xffff0000);
for (i = 0; i < 16; i++) {
I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF0[i]);
diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h
index d623fefbfaca..dac7bba4d9da 100644
--- a/drivers/gpu/drm/i915/i915_trace.h
+++ b/drivers/gpu/drm/i915/i915_trace.h
@@ -385,29 +385,29 @@ TRACE_EVENT(i915_flip_complete,
);
TRACE_EVENT(i915_reg_rw,
- TP_PROTO(bool write, u32 reg, u64 val, int len),
-
- TP_ARGS(write, reg, val, len),
-
- TP_STRUCT__entry(
- __field(u64, val)
- __field(u32, reg)
- __field(u16, write)
- __field(u16, len)
- ),
-
- TP_fast_assign(
- __entry->val = (u64)val;
- __entry->reg = reg;
- __entry->write = write;
- __entry->len = len;
- ),
-
- TP_printk("%s reg=0x%x, len=%d, val=(0x%x, 0x%x)",
- __entry->write ? "write" : "read",
- __entry->reg, __entry->len,
- (u32)(__entry->val & 0xffffffff),
- (u32)(__entry->val >> 32))
+ TP_PROTO(bool write, u32 reg, u64 val, int len),
+
+ TP_ARGS(write, reg, val, len),
+
+ TP_STRUCT__entry(
+ __field(u64, val)
+ __field(u32, reg)
+ __field(u16, write)
+ __field(u16, len)
+ ),
+
+ TP_fast_assign(
+ __entry->val = (u64)val;
+ __entry->reg = reg;
+ __entry->write = write;
+ __entry->len = len;
+ ),
+
+ TP_printk("%s reg=0x%x, len=%d, val=(0x%x, 0x%x)",
+ __entry->write ? "write" : "read",
+ __entry->reg, __entry->len,
+ (u32)(__entry->val & 0xffffffff),
+ (u32)(__entry->val >> 32))
);
#endif /* _I915_TRACE_H_ */
diff --git a/drivers/gpu/drm/i915/intel_acpi.c b/drivers/gpu/drm/i915/intel_acpi.c
index 2cb8e0b9f1ee..cb912106d1a2 100644
--- a/drivers/gpu/drm/i915/intel_acpi.c
+++ b/drivers/gpu/drm/i915/intel_acpi.c
@@ -64,7 +64,7 @@ static int intel_dsm(acpi_handle handle, int func, int arg)
case ACPI_TYPE_BUFFER:
if (obj->buffer.length == 4) {
- result =(obj->buffer.pointer[0] |
+ result = (obj->buffer.pointer[0] |
(obj->buffer.pointer[1] << 8) |
(obj->buffer.pointer[2] << 16) |
(obj->buffer.pointer[3] << 24));
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 927442a11925..63880e2e5cfd 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -1,5 +1,5 @@
/*
- * Copyright © 2006 Intel Corporation
+ * Copyright © 2006 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -74,7 +74,7 @@ get_blocksize(void *p)
static void
fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode,
- struct lvds_dvo_timing *dvo_timing)
+ const struct lvds_dvo_timing *dvo_timing)
{
panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
dvo_timing->hactive_lo;
@@ -115,20 +115,75 @@ fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode,
drm_mode_set_name(panel_fixed_mode);
}
+static bool
+lvds_dvo_timing_equal_size(const struct lvds_dvo_timing *a,
+ const struct lvds_dvo_timing *b)
+{
+ if (a->hactive_hi != b->hactive_hi ||
+ a->hactive_lo != b->hactive_lo)
+ return false;
+
+ if (a->hsync_off_hi != b->hsync_off_hi ||
+ a->hsync_off_lo != b->hsync_off_lo)
+ return false;
+
+ if (a->hsync_pulse_width != b->hsync_pulse_width)
+ return false;
+
+ if (a->hblank_hi != b->hblank_hi ||
+ a->hblank_lo != b->hblank_lo)
+ return false;
+
+ if (a->vactive_hi != b->vactive_hi ||
+ a->vactive_lo != b->vactive_lo)
+ return false;
+
+ if (a->vsync_off != b->vsync_off)
+ return false;
+
+ if (a->vsync_pulse_width != b->vsync_pulse_width)
+ return false;
+
+ if (a->vblank_hi != b->vblank_hi ||
+ a->vblank_lo != b->vblank_lo)
+ return false;
+
+ return true;
+}
+
+static const struct lvds_dvo_timing *
+get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data,
+ const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs,
+ int index)
+{
+ /*
+ * the size of fp_timing varies on the different platform.
+ * So calculate the DVO timing relative offset in LVDS data
+ * entry to get the DVO timing entry
+ */
+
+ int lfp_data_size =
+ lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset -
+ lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset;
+ int dvo_timing_offset =
+ lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset -
+ lvds_lfp_data_ptrs->ptr[0].fp_timing_offset;
+ char *entry = (char *)lvds_lfp_data->data + lfp_data_size * index;
+
+ return (struct lvds_dvo_timing *)(entry + dvo_timing_offset);
+}
+
/* Try to find integrated panel data */
static void
parse_lfp_panel_data(struct drm_i915_private *dev_priv,
struct bdb_header *bdb)
{
- struct bdb_lvds_options *lvds_options;
- struct bdb_lvds_lfp_data *lvds_lfp_data;
- struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
- struct bdb_lvds_lfp_data_entry *entry;
- struct lvds_dvo_timing *dvo_timing;
+ const struct bdb_lvds_options *lvds_options;
+ const struct bdb_lvds_lfp_data *lvds_lfp_data;
+ const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
+ const struct lvds_dvo_timing *panel_dvo_timing;
struct drm_display_mode *panel_fixed_mode;
- int lfp_data_size, dvo_timing_offset;
- int i, temp_downclock;
- struct drm_display_mode *temp_mode;
+ int i, downclock;
lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
if (!lvds_options)
@@ -150,75 +205,44 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
dev_priv->lvds_vbt = 1;
- lfp_data_size = lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset -
- lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset;
- entry = (struct bdb_lvds_lfp_data_entry *)
- ((uint8_t *)lvds_lfp_data->data + (lfp_data_size *
- lvds_options->panel_type));
- dvo_timing_offset = lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset -
- lvds_lfp_data_ptrs->ptr[0].fp_timing_offset;
-
- /*
- * the size of fp_timing varies on the different platform.
- * So calculate the DVO timing relative offset in LVDS data
- * entry to get the DVO timing entry
- */
- dvo_timing = (struct lvds_dvo_timing *)
- ((unsigned char *)entry + dvo_timing_offset);
+ panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
+ lvds_lfp_data_ptrs,
+ lvds_options->panel_type);
panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
if (!panel_fixed_mode)
return;
- fill_detail_timing_data(panel_fixed_mode, dvo_timing);
+ fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing);
dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n");
drm_mode_debug_printmodeline(panel_fixed_mode);
- temp_mode = kzalloc(sizeof(*temp_mode), GFP_KERNEL);
- temp_downclock = panel_fixed_mode->clock;
/*
- * enumerate the LVDS panel timing info entry in VBT to check whether
- * the LVDS downclock is found.
+ * Iterate over the LVDS panel timing info to find the lowest clock
+ * for the native resolution.
*/
+ downclock = panel_dvo_timing->clock;
for (i = 0; i < 16; i++) {
- entry = (struct bdb_lvds_lfp_data_entry *)
- ((uint8_t *)lvds_lfp_data->data + (lfp_data_size * i));
- dvo_timing = (struct lvds_dvo_timing *)
- ((unsigned char *)entry + dvo_timing_offset);
-
- fill_detail_timing_data(temp_mode, dvo_timing);
-
- if (temp_mode->hdisplay == panel_fixed_mode->hdisplay &&
- temp_mode->hsync_start == panel_fixed_mode->hsync_start &&
- temp_mode->hsync_end == panel_fixed_mode->hsync_end &&
- temp_mode->htotal == panel_fixed_mode->htotal &&
- temp_mode->vdisplay == panel_fixed_mode->vdisplay &&
- temp_mode->vsync_start == panel_fixed_mode->vsync_start &&
- temp_mode->vsync_end == panel_fixed_mode->vsync_end &&
- temp_mode->vtotal == panel_fixed_mode->vtotal &&
- temp_mode->clock < temp_downclock) {
- /*
- * downclock is already found. But we expect
- * to find the lower downclock.
- */
- temp_downclock = temp_mode->clock;
- }
- /* clear it to zero */
- memset(temp_mode, 0, sizeof(*temp_mode));
+ const struct lvds_dvo_timing *dvo_timing;
+
+ dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
+ lvds_lfp_data_ptrs,
+ i);
+ if (lvds_dvo_timing_equal_size(dvo_timing, panel_dvo_timing) &&
+ dvo_timing->clock < downclock)
+ downclock = dvo_timing->clock;
}
- kfree(temp_mode);
- if (temp_downclock < panel_fixed_mode->clock &&
- i915_lvds_downclock) {
+
+ if (downclock < panel_dvo_timing->clock && i915_lvds_downclock) {
dev_priv->lvds_downclock_avail = 1;
- dev_priv->lvds_downclock = temp_downclock;
+ dev_priv->lvds_downclock = downclock * 10;
DRM_DEBUG_KMS("LVDS downclock is found in VBT. "
"Normal Clock %dKHz, downclock %dKHz\n",
- temp_downclock, panel_fixed_mode->clock);
+ panel_fixed_mode->clock, 10*downclock);
}
- return;
}
/* Try to find sdvo panel data */
@@ -285,6 +309,13 @@ parse_general_features(struct drm_i915_private *dev_priv,
dev_priv->lvds_use_ssc = general->enable_ssc;
dev_priv->lvds_ssc_freq =
intel_bios_ssc_frequency(dev, general->ssc_freq);
+ dev_priv->display_clock_mode = general->display_clock_mode;
+ DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d display_clock_mode %d\n",
+ dev_priv->int_tv_support,
+ dev_priv->int_crt_support,
+ dev_priv->lvds_use_ssc,
+ dev_priv->lvds_ssc_freq,
+ dev_priv->display_clock_mode);
}
}
@@ -357,7 +388,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
if (p_child->dvo_port != DEVICE_PORT_DVOB &&
p_child->dvo_port != DEVICE_PORT_DVOC) {
/* skip the incorrect SDVO port */
- DRM_DEBUG_KMS("Incorrect SDVO port. Skip it \n");
+ DRM_DEBUG_KMS("Incorrect SDVO port. Skip it\n");
continue;
}
DRM_DEBUG_KMS("the SDVO device with slave addr %2x is found on"
@@ -372,15 +403,13 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
p_mapping->dvo_wiring = p_child->dvo_wiring;
p_mapping->ddc_pin = p_child->ddc_pin;
p_mapping->i2c_pin = p_child->i2c_pin;
- p_mapping->i2c_speed = p_child->i2c_speed;
p_mapping->initialized = 1;
- DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d, i2c_speed=%d\n",
+ DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d\n",
p_mapping->dvo_port,
p_mapping->slave_addr,
p_mapping->dvo_wiring,
p_mapping->ddc_pin,
- p_mapping->i2c_pin,
- p_mapping->i2c_speed);
+ p_mapping->i2c_pin);
} else {
DRM_DEBUG_KMS("Maybe one SDVO port is shared by "
"two SDVO device.\n");
@@ -540,7 +569,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
count++;
}
if (!count) {
- DRM_DEBUG_KMS("no child dev is parsed from VBT \n");
+ DRM_DEBUG_KMS("no child dev is parsed from VBT\n");
return;
}
dev_priv->child_dev = kzalloc(sizeof(*p_child) * count, GFP_KERNEL);
@@ -586,7 +615,7 @@ init_vbt_defaults(struct drm_i915_private *dev_priv)
/* Default to using SSC */
dev_priv->lvds_use_ssc = 1;
dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1);
- DRM_DEBUG("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq);
+ DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq);
/* eDP data */
dev_priv->edp.bpp = 18;
@@ -615,7 +644,7 @@ intel_parse_bios(struct drm_device *dev)
if (dev_priv->opregion.vbt) {
struct vbt_header *vbt = dev_priv->opregion.vbt;
if (memcmp(vbt->signature, "$VBT", 4) == 0) {
- DRM_DEBUG_DRIVER("Using VBT from OpRegion: %20s\n",
+ DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n",
vbt->signature);
bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset);
} else
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
index 5f8e4edcbbb9..8af3735e27c6 100644
--- a/drivers/gpu/drm/i915/intel_bios.h
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -1,5 +1,5 @@
/*
- * Copyright © 2006 Intel Corporation
+ * Copyright © 2006 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -120,7 +120,9 @@ struct bdb_general_features {
u8 ssc_freq:1;
u8 enable_lfp_on_override:1;
u8 disable_ssc_ddt:1;
- u8 rsvd8:3; /* finish byte */
+ u8 rsvd7:1;
+ u8 display_clock_mode:1;
+ u8 rsvd8:1; /* finish byte */
/* bits 3 */
u8 disable_smooth_vision:1;
@@ -133,7 +135,10 @@ struct bdb_general_features {
/* bits 5 */
u8 int_crt_support:1;
u8 int_tv_support:1;
- u8 rsvd11:6; /* finish byte */
+ u8 int_efp_support:1;
+ u8 dp_ssc_enb:1; /* PCH attached eDP supports SSC */
+ u8 dp_ssc_freq:1; /* SSC freq for PCH attached eDP */
+ u8 rsvd11:3; /* finish byte */
} __attribute__((packed));
/* pre-915 */
@@ -197,8 +202,7 @@ struct bdb_general_features {
struct child_device_config {
u16 handle;
u16 device_type;
- u8 i2c_speed;
- u8 rsvd[9];
+ u8 device_id[10]; /* ascii string */
u16 addin_offset;
u8 dvo_port; /* See Device_PORT_* above */
u8 i2c_pin;
@@ -240,7 +244,7 @@ struct bdb_general_definitions {
* And the device num is related with the size of general definition
* block. It is obtained by using the following formula:
* number = (block_size - sizeof(bdb_general_definitions))/
- * sizeof(child_device_config);
+ * sizeof(child_device_config);
*/
struct child_device_config devices[0];
} __attribute__((packed));
@@ -446,11 +450,11 @@ struct bdb_driver_features {
#define EDP_VSWING_1_2V 3
struct edp_power_seq {
- u16 t3;
- u16 t7;
+ u16 t1_t3;
+ u16 t8;
u16 t9;
u16 t10;
- u16 t12;
+ u16 t11_t12;
} __attribute__ ((packed));
struct edp_link_params {
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 0979d8877880..fee0ad02c6d0 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -69,7 +69,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
temp &= ~ADPA_DAC_ENABLE;
- switch(mode) {
+ switch (mode) {
case DRM_MODE_DPMS_ON:
temp |= ADPA_DAC_ENABLE;
break;
@@ -152,17 +152,13 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
adpa |= ADPA_VSYNC_ACTIVE_HIGH;
- if (intel_crtc->pipe == 0) {
- if (HAS_PCH_CPT(dev))
- adpa |= PORT_TRANS_A_SEL_CPT;
- else
- adpa |= ADPA_PIPE_A_SELECT;
- } else {
- if (HAS_PCH_CPT(dev))
- adpa |= PORT_TRANS_B_SEL_CPT;
- else
- adpa |= ADPA_PIPE_B_SELECT;
- }
+ /* For CPT allow 3 pipe config, for others just use A or B */
+ if (HAS_PCH_CPT(dev))
+ adpa |= PORT_TRANS_SEL_CPT(intel_crtc->pipe);
+ else if (intel_crtc->pipe == 0)
+ adpa |= ADPA_PIPE_A_SELECT;
+ else
+ adpa |= ADPA_PIPE_B_SELECT;
if (!HAS_PCH_SPLIT(dev))
I915_WRITE(BCLRPAT(intel_crtc->pipe), 0);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 21b6f93fe919..e77a863a3833 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -24,12 +24,14 @@
* Eric Anholt <eric@anholt.net>
*/
+#include <linux/cpufreq.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vgaarb.h>
+#include <drm/drm_edid.h>
#include "drmP.h"
#include "intel_drv.h"
#include "i915_drm.h"
@@ -41,39 +43,39 @@
#define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
-bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
+bool intel_pipe_has_type(struct drm_crtc *crtc, int type);
static void intel_update_watermarks(struct drm_device *dev);
static void intel_increase_pllclock(struct drm_crtc *crtc);
static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
typedef struct {
- /* given values */
- int n;
- int m1, m2;
- int p1, p2;
- /* derived values */
- int dot;
- int vco;
- int m;
- int p;
+ /* given values */
+ int n;
+ int m1, m2;
+ int p1, p2;
+ /* derived values */
+ int dot;
+ int vco;
+ int m;
+ int p;
} intel_clock_t;
typedef struct {
- int min, max;
+ int min, max;
} intel_range_t;
typedef struct {
- int dot_limit;
- int p2_slow, p2_fast;
+ int dot_limit;
+ int p2_slow, p2_fast;
} intel_p2_t;
#define INTEL_P2_NUM 2
typedef struct intel_limit intel_limit_t;
struct intel_limit {
- intel_range_t dot, vco, n, m, m1, m2, p, p1;
- intel_p2_t p2;
- bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
- int, int, intel_clock_t *);
+ intel_range_t dot, vco, n, m, m1, m2, p, p1;
+ intel_p2_t p2;
+ bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
+ int, int, intel_clock_t *);
};
/* FDI */
@@ -104,56 +106,56 @@ intel_fdi_link_freq(struct drm_device *dev)
}
static const intel_limit_t intel_limits_i8xx_dvo = {
- .dot = { .min = 25000, .max = 350000 },
- .vco = { .min = 930000, .max = 1400000 },
- .n = { .min = 3, .max = 16 },
- .m = { .min = 96, .max = 140 },
- .m1 = { .min = 18, .max = 26 },
- .m2 = { .min = 6, .max = 16 },
- .p = { .min = 4, .max = 128 },
- .p1 = { .min = 2, .max = 33 },
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 930000, .max = 1400000 },
+ .n = { .min = 3, .max = 16 },
+ .m = { .min = 96, .max = 140 },
+ .m1 = { .min = 18, .max = 26 },
+ .m2 = { .min = 6, .max = 16 },
+ .p = { .min = 4, .max = 128 },
+ .p1 = { .min = 2, .max = 33 },
.p2 = { .dot_limit = 165000,
.p2_slow = 4, .p2_fast = 2 },
.find_pll = intel_find_best_PLL,
};
static const intel_limit_t intel_limits_i8xx_lvds = {
- .dot = { .min = 25000, .max = 350000 },
- .vco = { .min = 930000, .max = 1400000 },
- .n = { .min = 3, .max = 16 },
- .m = { .min = 96, .max = 140 },
- .m1 = { .min = 18, .max = 26 },
- .m2 = { .min = 6, .max = 16 },
- .p = { .min = 4, .max = 128 },
- .p1 = { .min = 1, .max = 6 },
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 930000, .max = 1400000 },
+ .n = { .min = 3, .max = 16 },
+ .m = { .min = 96, .max = 140 },
+ .m1 = { .min = 18, .max = 26 },
+ .m2 = { .min = 6, .max = 16 },
+ .p = { .min = 4, .max = 128 },
+ .p1 = { .min = 1, .max = 6 },
.p2 = { .dot_limit = 165000,
.p2_slow = 14, .p2_fast = 7 },
.find_pll = intel_find_best_PLL,
};
static const intel_limit_t intel_limits_i9xx_sdvo = {
- .dot = { .min = 20000, .max = 400000 },
- .vco = { .min = 1400000, .max = 2800000 },
- .n = { .min = 1, .max = 6 },
- .m = { .min = 70, .max = 120 },
- .m1 = { .min = 10, .max = 22 },
- .m2 = { .min = 5, .max = 9 },
- .p = { .min = 5, .max = 80 },
- .p1 = { .min = 1, .max = 8 },
+ .dot = { .min = 20000, .max = 400000 },
+ .vco = { .min = 1400000, .max = 2800000 },
+ .n = { .min = 1, .max = 6 },
+ .m = { .min = 70, .max = 120 },
+ .m1 = { .min = 10, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 5, .max = 80 },
+ .p1 = { .min = 1, .max = 8 },
.p2 = { .dot_limit = 200000,
.p2_slow = 10, .p2_fast = 5 },
.find_pll = intel_find_best_PLL,
};
static const intel_limit_t intel_limits_i9xx_lvds = {
- .dot = { .min = 20000, .max = 400000 },
- .vco = { .min = 1400000, .max = 2800000 },
- .n = { .min = 1, .max = 6 },
- .m = { .min = 70, .max = 120 },
- .m1 = { .min = 10, .max = 22 },
- .m2 = { .min = 5, .max = 9 },
- .p = { .min = 7, .max = 98 },
- .p1 = { .min = 1, .max = 8 },
+ .dot = { .min = 20000, .max = 400000 },
+ .vco = { .min = 1400000, .max = 2800000 },
+ .n = { .min = 1, .max = 6 },
+ .m = { .min = 70, .max = 120 },
+ .m1 = { .min = 10, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 7, .max = 98 },
+ .p1 = { .min = 1, .max = 8 },
.p2 = { .dot_limit = 112000,
.p2_slow = 14, .p2_fast = 7 },
.find_pll = intel_find_best_PLL,
@@ -221,44 +223,44 @@ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
};
static const intel_limit_t intel_limits_g4x_display_port = {
- .dot = { .min = 161670, .max = 227000 },
- .vco = { .min = 1750000, .max = 3500000},
- .n = { .min = 1, .max = 2 },
- .m = { .min = 97, .max = 108 },
- .m1 = { .min = 0x10, .max = 0x12 },
- .m2 = { .min = 0x05, .max = 0x06 },
- .p = { .min = 10, .max = 20 },
- .p1 = { .min = 1, .max = 2},
- .p2 = { .dot_limit = 0,
+ .dot = { .min = 161670, .max = 227000 },
+ .vco = { .min = 1750000, .max = 3500000},
+ .n = { .min = 1, .max = 2 },
+ .m = { .min = 97, .max = 108 },
+ .m1 = { .min = 0x10, .max = 0x12 },
+ .m2 = { .min = 0x05, .max = 0x06 },
+ .p = { .min = 10, .max = 20 },
+ .p1 = { .min = 1, .max = 2},
+ .p2 = { .dot_limit = 0,
.p2_slow = 10, .p2_fast = 10 },
- .find_pll = intel_find_pll_g4x_dp,
+ .find_pll = intel_find_pll_g4x_dp,
};
static const intel_limit_t intel_limits_pineview_sdvo = {
- .dot = { .min = 20000, .max = 400000},
- .vco = { .min = 1700000, .max = 3500000 },
+ .dot = { .min = 20000, .max = 400000},
+ .vco = { .min = 1700000, .max = 3500000 },
/* Pineview's Ncounter is a ring counter */
- .n = { .min = 3, .max = 6 },
- .m = { .min = 2, .max = 256 },
+ .n = { .min = 3, .max = 6 },
+ .m = { .min = 2, .max = 256 },
/* Pineview only has one combined m divider, which we treat as m2. */
- .m1 = { .min = 0, .max = 0 },
- .m2 = { .min = 0, .max = 254 },
- .p = { .min = 5, .max = 80 },
- .p1 = { .min = 1, .max = 8 },
+ .m1 = { .min = 0, .max = 0 },
+ .m2 = { .min = 0, .max = 254 },
+ .p = { .min = 5, .max = 80 },
+ .p1 = { .min = 1, .max = 8 },
.p2 = { .dot_limit = 200000,
.p2_slow = 10, .p2_fast = 5 },
.find_pll = intel_find_best_PLL,
};
static const intel_limit_t intel_limits_pineview_lvds = {
- .dot = { .min = 20000, .max = 400000 },
- .vco = { .min = 1700000, .max = 3500000 },
- .n = { .min = 3, .max = 6 },
- .m = { .min = 2, .max = 256 },
- .m1 = { .min = 0, .max = 0 },
- .m2 = { .min = 0, .max = 254 },
- .p = { .min = 7, .max = 112 },
- .p1 = { .min = 1, .max = 8 },
+ .dot = { .min = 20000, .max = 400000 },
+ .vco = { .min = 1700000, .max = 3500000 },
+ .n = { .min = 3, .max = 6 },
+ .m = { .min = 2, .max = 256 },
+ .m1 = { .min = 0, .max = 0 },
+ .m2 = { .min = 0, .max = 254 },
+ .p = { .min = 7, .max = 112 },
+ .p1 = { .min = 1, .max = 8 },
.p2 = { .dot_limit = 112000,
.p2_slow = 14, .p2_fast = 14 },
.find_pll = intel_find_best_PLL,
@@ -320,7 +322,7 @@ static const intel_limit_t intel_limits_ironlake_single_lvds_100m = {
.m1 = { .min = 12, .max = 22 },
.m2 = { .min = 5, .max = 9 },
.p = { .min = 28, .max = 112 },
- .p1 = { .min = 2,.max = 8 },
+ .p1 = { .min = 2, .max = 8 },
.p2 = { .dot_limit = 225000,
.p2_slow = 14, .p2_fast = 14 },
.find_pll = intel_g4x_find_best_PLL,
@@ -334,24 +336,24 @@ static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
.m1 = { .min = 12, .max = 22 },
.m2 = { .min = 5, .max = 9 },
.p = { .min = 14, .max = 42 },
- .p1 = { .min = 2,.max = 6 },
+ .p1 = { .min = 2, .max = 6 },
.p2 = { .dot_limit = 225000,
.p2_slow = 7, .p2_fast = 7 },
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_ironlake_display_port = {
- .dot = { .min = 25000, .max = 350000 },
- .vco = { .min = 1760000, .max = 3510000},
- .n = { .min = 1, .max = 2 },
- .m = { .min = 81, .max = 90 },
- .m1 = { .min = 12, .max = 22 },
- .m2 = { .min = 5, .max = 9 },
- .p = { .min = 10, .max = 20 },
- .p1 = { .min = 1, .max = 2},
- .p2 = { .dot_limit = 0,
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 1760000, .max = 3510000},
+ .n = { .min = 1, .max = 2 },
+ .m = { .min = 81, .max = 90 },
+ .m1 = { .min = 12, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 10, .max = 20 },
+ .p1 = { .min = 1, .max = 2},
+ .p2 = { .dot_limit = 0,
.p2_slow = 10, .p2_fast = 10 },
- .find_pll = intel_find_pll_ironlake_dp,
+ .find_pll = intel_find_pll_ironlake_dp,
};
static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
@@ -403,7 +405,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
limit = &intel_limits_g4x_hdmi;
} else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
limit = &intel_limits_g4x_sdvo;
- } else if (intel_pipe_has_type (crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+ } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
limit = &intel_limits_g4x_display_port;
} else /* The option is for other outputs */
limit = &intel_limits_i9xx_sdvo;
@@ -487,26 +489,26 @@ static bool intel_PLL_is_valid(struct drm_device *dev,
const intel_clock_t *clock)
{
if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
- INTELPllInvalid ("p1 out of range\n");
+ INTELPllInvalid("p1 out of range\n");
if (clock->p < limit->p.min || limit->p.max < clock->p)
- INTELPllInvalid ("p out of range\n");
+ INTELPllInvalid("p out of range\n");
if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
- INTELPllInvalid ("m2 out of range\n");
+ INTELPllInvalid("m2 out of range\n");
if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
- INTELPllInvalid ("m1 out of range\n");
+ INTELPllInvalid("m1 out of range\n");
if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev))
- INTELPllInvalid ("m1 <= m2\n");
+ INTELPllInvalid("m1 <= m2\n");
if (clock->m < limit->m.min || limit->m.max < clock->m)
- INTELPllInvalid ("m out of range\n");
+ INTELPllInvalid("m out of range\n");
if (clock->n < limit->n.min || limit->n.max < clock->n)
- INTELPllInvalid ("n out of range\n");
+ INTELPllInvalid("n out of range\n");
if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
- INTELPllInvalid ("vco out of range\n");
+ INTELPllInvalid("vco out of range\n");
/* XXX: We may need to be checking "Dot clock" depending on the multiplier,
* connector, etc., rather than just a single range.
*/
if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
- INTELPllInvalid ("dot out of range\n");
+ INTELPllInvalid("dot out of range\n");
return true;
}
@@ -541,7 +543,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
clock.p2 = limit->p2.p2_fast;
}
- memset (best_clock, 0, sizeof (*best_clock));
+ memset(best_clock, 0, sizeof(*best_clock));
for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
clock.m1++) {
@@ -801,6 +803,19 @@ static void assert_pch_pll(struct drm_i915_private *dev_priv,
u32 val;
bool cur_state;
+ if (HAS_PCH_CPT(dev_priv->dev)) {
+ u32 pch_dpll;
+
+ pch_dpll = I915_READ(PCH_DPLL_SEL);
+
+ /* Make sure the selected PLL is enabled to the transcoder */
+ WARN(!((pch_dpll >> (4 * pipe)) & 8),
+ "transcoder %d PLL not enabled\n", pipe);
+
+ /* Convert the transcoder pipe number to a pll pipe number */
+ pipe = (pch_dpll >> (4 * pipe)) & 1;
+ }
+
reg = PCH_DPLL(pipe);
val = I915_READ(reg);
cur_state = !!(val & DPLL_VCO_ENABLE);
@@ -877,7 +892,7 @@ static void assert_panel_unlocked(struct drm_i915_private *dev_priv,
int pp_reg, lvds_reg;
u32 val;
enum pipe panel_pipe = PIPE_A;
- bool locked = locked;
+ bool locked = true;
if (HAS_PCH_SPLIT(dev_priv->dev)) {
pp_reg = PCH_PP_CONTROL;
@@ -979,11 +994,76 @@ static void assert_transcoder_disabled(struct drm_i915_private *dev_priv,
pipe_name(pipe));
}
+static bool dp_pipe_enabled(struct drm_i915_private *dev_priv,
+ enum pipe pipe, u32 port_sel, u32 val)
+{
+ if ((val & DP_PORT_EN) == 0)
+ return false;
+
+ if (HAS_PCH_CPT(dev_priv->dev)) {
+ u32 trans_dp_ctl_reg = TRANS_DP_CTL(pipe);
+ u32 trans_dp_ctl = I915_READ(trans_dp_ctl_reg);
+ if ((trans_dp_ctl & TRANS_DP_PORT_SEL_MASK) != port_sel)
+ return false;
+ } else {
+ if ((val & DP_PIPE_MASK) != (pipe << 30))
+ return false;
+ }
+ return true;
+}
+
+static bool hdmi_pipe_enabled(struct drm_i915_private *dev_priv,
+ enum pipe pipe, u32 val)
+{
+ if ((val & PORT_ENABLE) == 0)
+ return false;
+
+ if (HAS_PCH_CPT(dev_priv->dev)) {
+ if ((val & PORT_TRANS_SEL_MASK) != PORT_TRANS_SEL_CPT(pipe))
+ return false;
+ } else {
+ if ((val & TRANSCODER_MASK) != TRANSCODER(pipe))
+ return false;
+ }
+ return true;
+}
+
+static bool lvds_pipe_enabled(struct drm_i915_private *dev_priv,
+ enum pipe pipe, u32 val)
+{
+ if ((val & LVDS_PORT_EN) == 0)
+ return false;
+
+ if (HAS_PCH_CPT(dev_priv->dev)) {
+ if ((val & PORT_TRANS_SEL_MASK) != PORT_TRANS_SEL_CPT(pipe))
+ return false;
+ } else {
+ if ((val & LVDS_PIPE_MASK) != LVDS_PIPE(pipe))
+ return false;
+ }
+ return true;
+}
+
+static bool adpa_pipe_enabled(struct drm_i915_private *dev_priv,
+ enum pipe pipe, u32 val)
+{
+ if ((val & ADPA_DAC_ENABLE) == 0)
+ return false;
+ if (HAS_PCH_CPT(dev_priv->dev)) {
+ if ((val & PORT_TRANS_SEL_MASK) != PORT_TRANS_SEL_CPT(pipe))
+ return false;
+ } else {
+ if ((val & ADPA_PIPE_SELECT_MASK) != ADPA_PIPE_SELECT(pipe))
+ return false;
+ }
+ return true;
+}
+
static void assert_pch_dp_disabled(struct drm_i915_private *dev_priv,
- enum pipe pipe, int reg)
+ enum pipe pipe, int reg, u32 port_sel)
{
u32 val = I915_READ(reg);
- WARN(DP_PIPE_ENABLED(val, pipe),
+ WARN(dp_pipe_enabled(dev_priv, pipe, port_sel, val),
"PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n",
reg, pipe_name(pipe));
}
@@ -992,7 +1072,7 @@ static void assert_pch_hdmi_disabled(struct drm_i915_private *dev_priv,
enum pipe pipe, int reg)
{
u32 val = I915_READ(reg);
- WARN(HDMI_PIPE_ENABLED(val, pipe),
+ WARN(hdmi_pipe_enabled(dev_priv, val, pipe),
"PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n",
reg, pipe_name(pipe));
}
@@ -1003,19 +1083,19 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
int reg;
u32 val;
- assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B);
- assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C);
- assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_D);
+ assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B);
+ assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C);
+ assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D);
reg = PCH_ADPA;
val = I915_READ(reg);
- WARN(ADPA_PIPE_ENABLED(val, pipe),
+ WARN(adpa_pipe_enabled(dev_priv, val, pipe),
"PCH VGA enabled on transcoder %c, should be disabled\n",
pipe_name(pipe));
reg = PCH_LVDS;
val = I915_READ(reg);
- WARN(LVDS_PIPE_ENABLED(val, pipe),
+ WARN(lvds_pipe_enabled(dev_priv, val, pipe),
"PCH LVDS enabled on transcoder %c, should be disabled\n",
pipe_name(pipe));
@@ -1105,6 +1185,9 @@ static void intel_enable_pch_pll(struct drm_i915_private *dev_priv,
int reg;
u32 val;
+ if (pipe > 1)
+ return;
+
/* PCH only available on ILK+ */
BUG_ON(dev_priv->info->gen < 5);
@@ -1125,6 +1208,9 @@ static void intel_disable_pch_pll(struct drm_i915_private *dev_priv,
int reg;
u32 val;
+ if (pipe > 1)
+ return;
+
/* PCH only available on ILK+ */
BUG_ON(dev_priv->info->gen < 5);
@@ -1157,12 +1243,15 @@ static void intel_enable_transcoder(struct drm_i915_private *dev_priv,
reg = TRANSCONF(pipe);
val = I915_READ(reg);
- /*
- * make the BPC in transcoder be consistent with
- * that in pipeconf reg.
- */
- val &= ~PIPE_BPC_MASK;
- val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK;
+
+ if (HAS_PCH_IBX(dev_priv->dev)) {
+ /*
+ * make the BPC in transcoder be consistent with
+ * that in pipeconf reg.
+ */
+ val &= ~PIPE_BPC_MASK;
+ val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK;
+ }
I915_WRITE(reg, val | TRANS_ENABLE);
if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100))
DRM_ERROR("failed to enable transcoder %d\n", pipe);
@@ -1187,7 +1276,7 @@ static void intel_disable_transcoder(struct drm_i915_private *dev_priv,
I915_WRITE(reg, val);
/* wait for PCH transcoder off, transcoder state */
if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50))
- DRM_ERROR("failed to disable transcoder\n");
+ DRM_ERROR("failed to disable transcoder %d\n", pipe);
}
/**
@@ -1272,6 +1361,17 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv,
intel_wait_for_pipe_off(dev_priv->dev, pipe);
}
+/*
+ * Plane regs are double buffered, going from enabled->disabled needs a
+ * trigger in order to latch. The display address reg provides this.
+ */
+static void intel_flush_display_plane(struct drm_i915_private *dev_priv,
+ enum plane plane)
+{
+ I915_WRITE(DSPADDR(plane), I915_READ(DSPADDR(plane)));
+ I915_WRITE(DSPSURF(plane), I915_READ(DSPSURF(plane)));
+}
+
/**
* intel_enable_plane - enable a display plane on a given pipe
* @dev_priv: i915 private structure
@@ -1295,20 +1395,10 @@ static void intel_enable_plane(struct drm_i915_private *dev_priv,
return;
I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE);
+ intel_flush_display_plane(dev_priv, plane);
intel_wait_for_vblank(dev_priv->dev, pipe);
}
-/*
- * Plane regs are double buffered, going from enabled->disabled needs a
- * trigger in order to latch. The display address reg provides this.
- */
-static void intel_flush_display_plane(struct drm_i915_private *dev_priv,
- enum plane plane)
-{
- u32 reg = DSPADDR(plane);
- I915_WRITE(reg, I915_READ(reg));
-}
-
/**
* intel_disable_plane - disable a display plane
* @dev_priv: i915 private structure
@@ -1334,19 +1424,24 @@ static void intel_disable_plane(struct drm_i915_private *dev_priv,
}
static void disable_pch_dp(struct drm_i915_private *dev_priv,
- enum pipe pipe, int reg)
+ enum pipe pipe, int reg, u32 port_sel)
{
u32 val = I915_READ(reg);
- if (DP_PIPE_ENABLED(val, pipe))
+ if (dp_pipe_enabled(dev_priv, pipe, port_sel, val)) {
+ DRM_DEBUG_KMS("Disabling pch dp %x on pipe %d\n", reg, pipe);
I915_WRITE(reg, val & ~DP_PORT_EN);
+ }
}
static void disable_pch_hdmi(struct drm_i915_private *dev_priv,
enum pipe pipe, int reg)
{
u32 val = I915_READ(reg);
- if (HDMI_PIPE_ENABLED(val, pipe))
+ if (hdmi_pipe_enabled(dev_priv, val, pipe)) {
+ DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n",
+ reg, pipe);
I915_WRITE(reg, val & ~PORT_ENABLE);
+ }
}
/* Disable any ports connected to this transcoder */
@@ -1358,18 +1453,19 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv,
val = I915_READ(PCH_PP_CONTROL);
I915_WRITE(PCH_PP_CONTROL, val | PANEL_UNLOCK_REGS);
- disable_pch_dp(dev_priv, pipe, PCH_DP_B);
- disable_pch_dp(dev_priv, pipe, PCH_DP_C);
- disable_pch_dp(dev_priv, pipe, PCH_DP_D);
+ disable_pch_dp(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B);
+ disable_pch_dp(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C);
+ disable_pch_dp(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D);
reg = PCH_ADPA;
val = I915_READ(reg);
- if (ADPA_PIPE_ENABLED(val, pipe))
+ if (adpa_pipe_enabled(dev_priv, val, pipe))
I915_WRITE(reg, val & ~ADPA_DAC_ENABLE);
reg = PCH_LVDS;
val = I915_READ(reg);
- if (LVDS_PIPE_ENABLED(val, pipe)) {
+ if (lvds_pipe_enabled(dev_priv, val, pipe)) {
+ DRM_DEBUG_KMS("disable lvds on pipe %d val 0x%08x\n", pipe, val);
I915_WRITE(reg, val & ~LVDS_PORT_EN);
POSTING_READ(reg);
udelay(100);
@@ -1380,6 +1476,28 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv,
disable_pch_hdmi(dev_priv, pipe, HDMID);
}
+static void i8xx_disable_fbc(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 fbc_ctl;
+
+ /* Disable compression */
+ fbc_ctl = I915_READ(FBC_CONTROL);
+ if ((fbc_ctl & FBC_CTL_EN) == 0)
+ return;
+
+ fbc_ctl &= ~FBC_CTL_EN;
+ I915_WRITE(FBC_CONTROL, fbc_ctl);
+
+ /* Wait for compressing bit to clear */
+ if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) {
+ DRM_DEBUG_KMS("FBC idle timed out\n");
+ return;
+ }
+
+ DRM_DEBUG_KMS("disabled FBC\n");
+}
+
static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
{
struct drm_device *dev = crtc->dev;
@@ -1388,36 +1506,25 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj = intel_fb->obj;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int cfb_pitch;
int plane, i;
u32 fbc_ctl, fbc_ctl2;
- if (fb->pitch == dev_priv->cfb_pitch &&
- obj->fence_reg == dev_priv->cfb_fence &&
- intel_crtc->plane == dev_priv->cfb_plane &&
- I915_READ(FBC_CONTROL) & FBC_CTL_EN)
- return;
-
- i8xx_disable_fbc(dev);
-
- dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
-
- if (fb->pitch < dev_priv->cfb_pitch)
- dev_priv->cfb_pitch = fb->pitch;
+ cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
+ if (fb->pitch < cfb_pitch)
+ cfb_pitch = fb->pitch;
/* FBC_CTL wants 64B units */
- dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
- dev_priv->cfb_fence = obj->fence_reg;
- dev_priv->cfb_plane = intel_crtc->plane;
- plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
+ cfb_pitch = (cfb_pitch / 64) - 1;
+ plane = intel_crtc->plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
/* Clear old tags */
for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++)
I915_WRITE(FBC_TAG + (i * 4), 0);
/* Set it up... */
- fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | plane;
- if (obj->tiling_mode != I915_TILING_NONE)
- fbc_ctl2 |= FBC_CTL_CPU_FENCE;
+ fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE;
+ fbc_ctl2 |= plane;
I915_WRITE(FBC_CONTROL2, fbc_ctl2);
I915_WRITE(FBC_FENCE_OFF, crtc->y);
@@ -1425,36 +1532,13 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
if (IS_I945GM(dev))
fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */
- fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
+ fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
- if (obj->tiling_mode != I915_TILING_NONE)
- fbc_ctl |= dev_priv->cfb_fence;
+ fbc_ctl |= obj->fence_reg;
I915_WRITE(FBC_CONTROL, fbc_ctl);
- DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ",
- dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane);
-}
-
-void i8xx_disable_fbc(struct drm_device *dev)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- u32 fbc_ctl;
-
- /* Disable compression */
- fbc_ctl = I915_READ(FBC_CONTROL);
- if ((fbc_ctl & FBC_CTL_EN) == 0)
- return;
-
- fbc_ctl &= ~FBC_CTL_EN;
- I915_WRITE(FBC_CONTROL, fbc_ctl);
-
- /* Wait for compressing bit to clear */
- if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) {
- DRM_DEBUG_KMS("FBC idle timed out\n");
- return;
- }
-
- DRM_DEBUG_KMS("disabled FBC\n");
+ DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ",
+ cfb_pitch, crtc->y, intel_crtc->plane);
}
static bool i8xx_fbc_enabled(struct drm_device *dev)
@@ -1476,30 +1560,9 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
unsigned long stall_watermark = 200;
u32 dpfc_ctl;
- dpfc_ctl = I915_READ(DPFC_CONTROL);
- if (dpfc_ctl & DPFC_CTL_EN) {
- if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
- dev_priv->cfb_fence == obj->fence_reg &&
- dev_priv->cfb_plane == intel_crtc->plane &&
- dev_priv->cfb_y == crtc->y)
- return;
-
- I915_WRITE(DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN);
- intel_wait_for_vblank(dev, intel_crtc->pipe);
- }
-
- dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
- dev_priv->cfb_fence = obj->fence_reg;
- dev_priv->cfb_plane = intel_crtc->plane;
- dev_priv->cfb_y = crtc->y;
-
dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X;
- if (obj->tiling_mode != I915_TILING_NONE) {
- dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence;
- I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
- } else {
- I915_WRITE(DPFC_CHICKEN, ~DPFC_HT_MODIFY);
- }
+ dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg;
+ I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
(stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
@@ -1512,7 +1575,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
}
-void g4x_disable_fbc(struct drm_device *dev)
+static void g4x_disable_fbc(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 dpfc_ctl;
@@ -1567,32 +1630,12 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
u32 dpfc_ctl;
dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
- if (dpfc_ctl & DPFC_CTL_EN) {
- if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
- dev_priv->cfb_fence == obj->fence_reg &&
- dev_priv->cfb_plane == intel_crtc->plane &&
- dev_priv->cfb_offset == obj->gtt_offset &&
- dev_priv->cfb_y == crtc->y)
- return;
-
- I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN);
- intel_wait_for_vblank(dev, intel_crtc->pipe);
- }
-
- dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
- dev_priv->cfb_fence = obj->fence_reg;
- dev_priv->cfb_plane = intel_crtc->plane;
- dev_priv->cfb_offset = obj->gtt_offset;
- dev_priv->cfb_y = crtc->y;
-
dpfc_ctl &= DPFC_RESERVED;
dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X);
- if (obj->tiling_mode != I915_TILING_NONE) {
- dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence);
- I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
- } else {
- I915_WRITE(ILK_DPFC_CHICKEN, ~DPFC_HT_MODIFY);
- }
+ /* Set persistent mode for front-buffer rendering, ala X. */
+ dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE;
+ dpfc_ctl |= (DPFC_CTL_FENCE_EN | obj->fence_reg);
+ I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
(stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
@@ -1604,7 +1647,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
if (IS_GEN6(dev)) {
I915_WRITE(SNB_DPFC_CTL_SA,
- SNB_CPU_FENCE_ENABLE | dev_priv->cfb_fence);
+ SNB_CPU_FENCE_ENABLE | obj->fence_reg);
I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y);
sandybridge_blit_fbc_update(dev);
}
@@ -1612,7 +1655,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
}
-void ironlake_disable_fbc(struct drm_device *dev)
+static void ironlake_disable_fbc(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 dpfc_ctl;
@@ -1644,24 +1687,109 @@ bool intel_fbc_enabled(struct drm_device *dev)
return dev_priv->display.fbc_enabled(dev);
}
-void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+static void intel_fbc_work_fn(struct work_struct *__work)
+{
+ struct intel_fbc_work *work =
+ container_of(to_delayed_work(__work),
+ struct intel_fbc_work, work);
+ struct drm_device *dev = work->crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ mutex_lock(&dev->struct_mutex);
+ if (work == dev_priv->fbc_work) {
+ /* Double check that we haven't switched fb without cancelling
+ * the prior work.
+ */
+ if (work->crtc->fb == work->fb) {
+ dev_priv->display.enable_fbc(work->crtc,
+ work->interval);
+
+ dev_priv->cfb_plane = to_intel_crtc(work->crtc)->plane;
+ dev_priv->cfb_fb = work->crtc->fb->base.id;
+ dev_priv->cfb_y = work->crtc->y;
+ }
+
+ dev_priv->fbc_work = NULL;
+ }
+ mutex_unlock(&dev->struct_mutex);
+
+ kfree(work);
+}
+
+static void intel_cancel_fbc_work(struct drm_i915_private *dev_priv)
+{
+ if (dev_priv->fbc_work == NULL)
+ return;
+
+ DRM_DEBUG_KMS("cancelling pending FBC enable\n");
+
+ /* Synchronisation is provided by struct_mutex and checking of
+ * dev_priv->fbc_work, so we can perform the cancellation
+ * entirely asynchronously.
+ */
+ if (cancel_delayed_work(&dev_priv->fbc_work->work))
+ /* tasklet was killed before being run, clean up */
+ kfree(dev_priv->fbc_work);
+
+ /* Mark the work as no longer wanted so that if it does
+ * wake-up (because the work was already running and waiting
+ * for our mutex), it will discover that is no longer
+ * necessary to run.
+ */
+ dev_priv->fbc_work = NULL;
+}
+
+static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
{
- struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+ struct intel_fbc_work *work;
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
if (!dev_priv->display.enable_fbc)
return;
- dev_priv->display.enable_fbc(crtc, interval);
+ intel_cancel_fbc_work(dev_priv);
+
+ work = kzalloc(sizeof *work, GFP_KERNEL);
+ if (work == NULL) {
+ dev_priv->display.enable_fbc(crtc, interval);
+ return;
+ }
+
+ work->crtc = crtc;
+ work->fb = crtc->fb;
+ work->interval = interval;
+ INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn);
+
+ dev_priv->fbc_work = work;
+
+ DRM_DEBUG_KMS("scheduling delayed FBC enable\n");
+
+ /* Delay the actual enabling to let pageflipping cease and the
+ * display to settle before starting the compression. Note that
+ * this delay also serves a second purpose: it allows for a
+ * vblank to pass after disabling the FBC before we attempt
+ * to modify the control registers.
+ *
+ * A more complicated solution would involve tracking vblanks
+ * following the termination of the page-flipping sequence
+ * and indeed performing the enable as a co-routine and not
+ * waiting synchronously upon the vblank.
+ */
+ schedule_delayed_work(&work->work, msecs_to_jiffies(50));
}
void intel_disable_fbc(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ intel_cancel_fbc_work(dev_priv);
+
if (!dev_priv->display.disable_fbc)
return;
dev_priv->display.disable_fbc(dev);
+ dev_priv->cfb_plane = -1;
}
/**
@@ -1691,6 +1819,7 @@ static void intel_update_fbc(struct drm_device *dev)
struct drm_framebuffer *fb;
struct intel_framebuffer *intel_fb;
struct drm_i915_gem_object *obj;
+ int enable_fbc;
DRM_DEBUG_KMS("\n");
@@ -1731,8 +1860,15 @@ static void intel_update_fbc(struct drm_device *dev)
intel_fb = to_intel_framebuffer(fb);
obj = intel_fb->obj;
- if (!i915_enable_fbc) {
- DRM_DEBUG_KMS("fbc disabled per module param (default off)\n");
+ enable_fbc = i915_enable_fbc;
+ if (enable_fbc < 0) {
+ DRM_DEBUG_KMS("fbc set to per-chip default\n");
+ enable_fbc = 1;
+ if (INTEL_INFO(dev)->gen <= 5)
+ enable_fbc = 0;
+ }
+ if (!enable_fbc) {
+ DRM_DEBUG_KMS("fbc disabled per module param\n");
dev_priv->no_fbc_reason = FBC_MODULE_PARAM;
goto out_disable;
}
@@ -1760,8 +1896,13 @@ static void intel_update_fbc(struct drm_device *dev)
dev_priv->no_fbc_reason = FBC_BAD_PLANE;
goto out_disable;
}
- if (obj->tiling_mode != I915_TILING_X) {
- DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n");
+
+ /* The use of a CPU fence is mandatory in order to detect writes
+ * by the CPU to the scanout and trigger updates to the FBC.
+ */
+ if (obj->tiling_mode != I915_TILING_X ||
+ obj->fence_reg == I915_FENCE_REG_NONE) {
+ DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n");
dev_priv->no_fbc_reason = FBC_NOT_TILED;
goto out_disable;
}
@@ -1770,6 +1911,44 @@ static void intel_update_fbc(struct drm_device *dev)
if (in_dbg_master())
goto out_disable;
+ /* If the scanout has not changed, don't modify the FBC settings.
+ * Note that we make the fundamental assumption that the fb->obj
+ * cannot be unpinned (and have its GTT offset and fence revoked)
+ * without first being decoupled from the scanout and FBC disabled.
+ */
+ if (dev_priv->cfb_plane == intel_crtc->plane &&
+ dev_priv->cfb_fb == fb->base.id &&
+ dev_priv->cfb_y == crtc->y)
+ return;
+
+ if (intel_fbc_enabled(dev)) {
+ /* We update FBC along two paths, after changing fb/crtc
+ * configuration (modeswitching) and after page-flipping
+ * finishes. For the latter, we know that not only did
+ * we disable the FBC at the start of the page-flip
+ * sequence, but also more than one vblank has passed.
+ *
+ * For the former case of modeswitching, it is possible
+ * to switch between two FBC valid configurations
+ * instantaneously so we do need to disable the FBC
+ * before we can modify its control registers. We also
+ * have to wait for the next vblank for that to take
+ * effect. However, since we delay enabling FBC we can
+ * assume that a vblank has passed since disabling and
+ * that we can safely alter the registers in the deferred
+ * callback.
+ *
+ * In the scenario that we go from a valid to invalid
+ * and then back to valid FBC configuration we have
+ * no strict enforcement that a vblank occurred since
+ * disabling the FBC. However, along all current pipe
+ * disabling paths we do need to wait for a vblank at
+ * some point. And we wait before enabling FBC anyway.
+ */
+ DRM_DEBUG_KMS("disabling active FBC for update\n");
+ intel_disable_fbc(dev);
+ }
+
intel_enable_fbc(crtc, 500);
return;
@@ -1812,14 +1991,10 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev,
}
dev_priv->mm.interruptible = false;
- ret = i915_gem_object_pin(obj, alignment, true);
+ ret = i915_gem_object_pin_to_display_plane(obj, alignment, pipelined);
if (ret)
goto err_interruptible;
- ret = i915_gem_object_set_to_display_plane(obj, pipelined);
- if (ret)
- goto err_unpin;
-
/* Install a fence for tiled scan-out. Pre-i965 always needs a
* fence, whereas 965+ only requires a fence if using
* framebuffer compression. For simplicity, we always install
@@ -1841,10 +2016,8 @@ err_interruptible:
return ret;
}
-/* Assume fb object is pinned & idle & fenced and just update base pointers */
-static int
-intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
- int x, int y, enum mode_set_atomic state)
+static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+ int x, int y)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1887,7 +2060,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
break;
default:
- DRM_ERROR("Unknown color depth\n");
+ DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel);
return -EINVAL;
}
if (INTEL_INFO(dev)->gen >= 4) {
@@ -1897,10 +2070,6 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
dspcntr &= ~DISPPLANE_TILED;
}
- if (HAS_PCH_SPLIT(dev))
- /* must disable */
- dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
-
I915_WRITE(reg, dspcntr);
Start = obj->gtt_offset;
@@ -1917,6 +2086,100 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
I915_WRITE(DSPADDR(plane), Start + Offset);
POSTING_READ(reg);
+ return 0;
+}
+
+static int ironlake_update_plane(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int x, int y)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_framebuffer *intel_fb;
+ struct drm_i915_gem_object *obj;
+ int plane = intel_crtc->plane;
+ unsigned long Start, Offset;
+ u32 dspcntr;
+ u32 reg;
+
+ switch (plane) {
+ case 0:
+ case 1:
+ case 2:
+ break;
+ default:
+ DRM_ERROR("Can't update plane %d in SAREA\n", plane);
+ return -EINVAL;
+ }
+
+ intel_fb = to_intel_framebuffer(fb);
+ obj = intel_fb->obj;
+
+ reg = DSPCNTR(plane);
+ dspcntr = I915_READ(reg);
+ /* Mask out pixel format bits in case we change it */
+ dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
+ switch (fb->bits_per_pixel) {
+ case 8:
+ dspcntr |= DISPPLANE_8BPP;
+ break;
+ case 16:
+ if (fb->depth != 16)
+ return -EINVAL;
+
+ dspcntr |= DISPPLANE_16BPP;
+ break;
+ case 24:
+ case 32:
+ if (fb->depth == 24)
+ dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+ else if (fb->depth == 30)
+ dspcntr |= DISPPLANE_32BPP_30BIT_NO_ALPHA;
+ else
+ return -EINVAL;
+ break;
+ default:
+ DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ if (obj->tiling_mode != I915_TILING_NONE)
+ dspcntr |= DISPPLANE_TILED;
+ else
+ dspcntr &= ~DISPPLANE_TILED;
+
+ /* must disable */
+ dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
+
+ I915_WRITE(reg, dspcntr);
+
+ Start = obj->gtt_offset;
+ Offset = y * fb->pitch + x * (fb->bits_per_pixel / 8);
+
+ DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n",
+ Start, Offset, x, y, fb->pitch);
+ I915_WRITE(DSPSTRIDE(plane), fb->pitch);
+ I915_WRITE(DSPSURF(plane), Start);
+ I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
+ I915_WRITE(DSPADDR(plane), Offset);
+ POSTING_READ(reg);
+
+ return 0;
+}
+
+/* Assume fb object is pinned & idle & fenced and just update base pointers */
+static int
+intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+ int x, int y, enum mode_set_atomic state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ ret = dev_priv->display.update_plane(crtc, fb, x, y);
+ if (ret)
+ return ret;
+
intel_update_fbc(dev);
intel_increase_pllclock(crtc);
@@ -1934,7 +2197,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
/* no fb bound */
if (!crtc->fb) {
- DRM_DEBUG_KMS("No FB bound\n");
+ DRM_ERROR("No FB bound\n");
return 0;
}
@@ -1942,7 +2205,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
case 0:
case 1:
break;
+ case 2:
+ if (IS_IVYBRIDGE(dev))
+ break;
+ /* fall through otherwise */
default:
+ DRM_ERROR("no plane for crtc\n");
return -EINVAL;
}
@@ -1952,6 +2220,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
NULL);
if (ret != 0) {
mutex_unlock(&dev->struct_mutex);
+ DRM_ERROR("pin & fence failed\n");
return ret;
}
@@ -1971,7 +2240,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
* This should only fail upon a hung GPU, in which case we
* can safely continue.
*/
- ret = i915_gem_object_flush_gpu(obj);
+ ret = i915_gem_object_finish_gpu(obj);
(void) ret;
}
@@ -1980,6 +2249,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
if (ret) {
i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
mutex_unlock(&dev->struct_mutex);
+ DRM_ERROR("failed to update base address\n");
return ret;
}
@@ -2086,6 +2356,18 @@ static void intel_fdi_normal_train(struct drm_crtc *crtc)
FDI_FE_ERRC_ENABLE);
}
+static void cpt_phase_pointer_enable(struct drm_device *dev, int pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 flags = I915_READ(SOUTH_CHICKEN1);
+
+ flags |= FDI_PHASE_SYNC_OVR(pipe);
+ I915_WRITE(SOUTH_CHICKEN1, flags); /* once to unlock... */
+ flags |= FDI_PHASE_SYNC_EN(pipe);
+ I915_WRITE(SOUTH_CHICKEN1, flags); /* then again to enable */
+ POSTING_READ(SOUTH_CHICKEN1);
+}
+
/* The FDI link training functions for ILK/Ibexpeak. */
static void ironlake_fdi_link_train(struct drm_crtc *crtc)
{
@@ -2183,7 +2465,7 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc)
}
-static const int snb_b_fdi_train_param [] = {
+static const int snb_b_fdi_train_param[] = {
FDI_LINK_TRAIN_400MV_0DB_SNB_B,
FDI_LINK_TRAIN_400MV_6DB_SNB_B,
FDI_LINK_TRAIN_600MV_3_5DB_SNB_B,
@@ -2236,7 +2518,10 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
POSTING_READ(reg);
udelay(150);
- for (i = 0; i < 4; i++ ) {
+ if (HAS_PCH_CPT(dev))
+ cpt_phase_pointer_enable(dev, pipe);
+
+ for (i = 0; i < 4; i++) {
reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
@@ -2285,7 +2570,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
POSTING_READ(reg);
udelay(150);
- for (i = 0; i < 4; i++ ) {
+ for (i = 0; i < 4; i++) {
reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
@@ -2340,6 +2625,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
temp |= FDI_LINK_TRAIN_PATTERN_1_IVB;
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
+ temp |= FDI_COMPOSITE_SYNC;
I915_WRITE(reg, temp | FDI_TX_ENABLE);
reg = FDI_RX_CTL(pipe);
@@ -2347,12 +2633,16 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
temp &= ~FDI_LINK_TRAIN_AUTO;
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
+ temp |= FDI_COMPOSITE_SYNC;
I915_WRITE(reg, temp | FDI_RX_ENABLE);
POSTING_READ(reg);
udelay(150);
- for (i = 0; i < 4; i++ ) {
+ if (HAS_PCH_CPT(dev))
+ cpt_phase_pointer_enable(dev, pipe);
+
+ for (i = 0; i < 4; i++) {
reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
@@ -2394,7 +2684,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
POSTING_READ(reg);
udelay(150);
- for (i = 0; i < 4; i++ ) {
+ for (i = 0; i < 4; i++) {
reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
@@ -2461,6 +2751,17 @@ static void ironlake_fdi_pll_enable(struct drm_crtc *crtc)
}
}
+static void cpt_phase_pointer_disable(struct drm_device *dev, int pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 flags = I915_READ(SOUTH_CHICKEN1);
+
+ flags &= ~(FDI_PHASE_SYNC_EN(pipe));
+ I915_WRITE(SOUTH_CHICKEN1, flags); /* once to disable... */
+ flags &= ~(FDI_PHASE_SYNC_OVR(pipe));
+ I915_WRITE(SOUTH_CHICKEN1, flags); /* then again to lock */
+ POSTING_READ(SOUTH_CHICKEN1);
+}
static void ironlake_fdi_disable(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
@@ -2490,6 +2791,8 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc)
I915_WRITE(FDI_RX_CHICKEN(pipe),
I915_READ(FDI_RX_CHICKEN(pipe) &
~FDI_RX_PHASE_SYNC_POINTER_EN));
+ } else if (HAS_PCH_CPT(dev)) {
+ cpt_phase_pointer_disable(dev, pipe);
}
/* still set train pattern 1 */
@@ -2590,7 +2893,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
- u32 reg, temp;
+ u32 reg, temp, transc_sel;
/* For PCH output, training FDI link */
dev_priv->display.fdi_link_train(crtc);
@@ -2598,12 +2901,21 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
intel_enable_pch_pll(dev_priv, pipe);
if (HAS_PCH_CPT(dev)) {
+ transc_sel = intel_crtc->use_pll_a ? TRANSC_DPLLA_SEL :
+ TRANSC_DPLLB_SEL;
+
/* Be sure PCH DPLL SEL is set */
temp = I915_READ(PCH_DPLL_SEL);
- if (pipe == 0 && (temp & TRANSA_DPLL_ENABLE) == 0)
+ if (pipe == 0) {
+ temp &= ~(TRANSA_DPLLB_SEL);
temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
- else if (pipe == 1 && (temp & TRANSB_DPLL_ENABLE) == 0)
+ } else if (pipe == 1) {
+ temp &= ~(TRANSB_DPLLB_SEL);
temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
+ } else if (pipe == 2) {
+ temp &= ~(TRANSC_DPLLB_SEL);
+ temp |= (TRANSC_DPLL_ENABLE | transc_sel);
+ }
I915_WRITE(PCH_DPLL_SEL, temp);
}
@@ -2621,7 +2933,9 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
/* For PCH DP, enable TRANS_DP_CTL */
if (HAS_PCH_CPT(dev) &&
- intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+ (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
+ intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) {
+ u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) >> 5;
reg = TRANS_DP_CTL(pipe);
temp = I915_READ(reg);
temp &= ~(TRANS_DP_PORT_SEL_MASK |
@@ -2629,7 +2943,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
TRANS_DP_BPC_MASK);
temp |= (TRANS_DP_OUTPUT_ENABLE |
TRANS_DP_ENH_FRAMING);
- temp |= TRANS_DP_8BPC;
+ temp |= bpc << 9; /* same format but at 11:9 */
if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC)
temp |= TRANS_DP_HSYNC_ACTIVE_HIGH;
@@ -2658,6 +2972,24 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
intel_enable_transcoder(dev_priv, pipe);
}
+void intel_cpt_verify_modeset(struct drm_device *dev, int pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int dslreg = PIPEDSL(pipe), tc2reg = TRANS_CHICKEN2(pipe);
+ u32 temp;
+
+ temp = I915_READ(dslreg);
+ udelay(500);
+ if (wait_for(I915_READ(dslreg) != temp, 5)) {
+ /* Without this, mode sets may fail silently on FDI */
+ I915_WRITE(tc2reg, TRANS_AUTOTRAIN_GEN_STALL_DIS);
+ udelay(250);
+ I915_WRITE(tc2reg, 0);
+ if (wait_for(I915_READ(dslreg) != temp, 5))
+ DRM_ERROR("mode set failed: pipe %d stuck\n", pipe);
+ }
+}
+
static void ironlake_crtc_enable(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
@@ -2699,14 +3031,18 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size);
}
+ /*
+ * On ILK+ LUT must be loaded before the pipe is running but with
+ * clocks enabled
+ */
+ intel_crtc_load_lut(crtc);
+
intel_enable_pipe(dev_priv, pipe, is_pch_port);
intel_enable_plane(dev_priv, plane, pipe);
if (is_pch_port)
ironlake_pch_enable(crtc);
- intel_crtc_load_lut(crtc);
-
mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
mutex_unlock(&dev->struct_mutex);
@@ -2732,9 +3068,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
intel_disable_plane(dev_priv, plane, pipe);
- if (dev_priv->cfb_plane == plane &&
- dev_priv->display.disable_fbc)
- dev_priv->display.disable_fbc(dev);
+ if (dev_priv->cfb_plane == plane)
+ intel_disable_fbc(dev);
intel_disable_pipe(dev_priv, pipe);
@@ -2765,13 +3100,13 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
temp = I915_READ(PCH_DPLL_SEL);
switch (pipe) {
case 0:
- temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
+ temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL);
break;
case 1:
temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
break;
case 2:
- /* FIXME: manage transcoder PLLs? */
+ /* C shares PLL A or B */
temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL);
break;
default:
@@ -2781,7 +3116,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
}
/* disable PCH DPLL */
- intel_disable_pch_pll(dev_priv, pipe);
+ if (!intel_crtc->no_pll)
+ intel_disable_pch_pll(dev_priv, pipe);
/* Switch from PCDclk to Rawclk */
reg = FDI_RX_CTL(pipe);
@@ -2898,9 +3234,8 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
intel_crtc_dpms_overlay(intel_crtc, false);
intel_crtc_update_cursor(crtc, false);
- if (dev_priv->cfb_plane == plane &&
- dev_priv->display.disable_fbc)
- dev_priv->display.disable_fbc(dev);
+ if (dev_priv->cfb_plane == plane)
+ intel_disable_fbc(dev);
intel_disable_plane(dev_priv, plane, pipe);
intel_disable_pipe(dev_priv, pipe);
@@ -3014,18 +3349,25 @@ static void ironlake_crtc_commit(struct drm_crtc *crtc)
ironlake_crtc_enable(crtc);
}
-void intel_encoder_prepare (struct drm_encoder *encoder)
+void intel_encoder_prepare(struct drm_encoder *encoder)
{
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
/* lvds has its own version of prepare see intel_lvds_prepare */
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
}
-void intel_encoder_commit (struct drm_encoder *encoder)
+void intel_encoder_commit(struct drm_encoder *encoder)
{
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ struct drm_device *dev = encoder->dev;
+ struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+ struct intel_crtc *intel_crtc = to_intel_crtc(intel_encoder->base.crtc);
+
/* lvds has its own version of commit see intel_lvds_commit */
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+
+ if (HAS_PCH_CPT(dev))
+ intel_cpt_verify_modeset(dev, intel_crtc->pipe);
}
void intel_encoder_destroy(struct drm_encoder *encoder)
@@ -4199,6 +4541,20 @@ static void sandybridge_update_wm(struct drm_device *dev)
enabled |= 2;
}
+ /* IVB has 3 pipes */
+ if (IS_IVYBRIDGE(dev) &&
+ g4x_compute_wm0(dev, 2,
+ &sandybridge_display_wm_info, latency,
+ &sandybridge_cursor_wm_info, latency,
+ &plane_wm, &cursor_wm)) {
+ I915_WRITE(WM0_PIPEC_IVB,
+ (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
+ DRM_DEBUG_KMS("FIFO watermarks For pipe C -"
+ " plane %d, cursor: %d\n",
+ plane_wm, cursor_wm);
+ enabled |= 3;
+ }
+
/*
* Calculate and update the self-refresh watermark only when one
* display plane is used.
@@ -4305,7 +4661,141 @@ static void intel_update_watermarks(struct drm_device *dev)
static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
{
- return dev_priv->lvds_use_ssc && i915_panel_use_ssc;
+ if (i915_panel_use_ssc >= 0)
+ return i915_panel_use_ssc != 0;
+ return dev_priv->lvds_use_ssc
+ && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
+}
+
+/**
+ * intel_choose_pipe_bpp_dither - figure out what color depth the pipe should send
+ * @crtc: CRTC structure
+ *
+ * A pipe may be connected to one or more outputs. Based on the depth of the
+ * attached framebuffer, choose a good color depth to use on the pipe.
+ *
+ * If possible, match the pipe depth to the fb depth. In some cases, this
+ * isn't ideal, because the connected output supports a lesser or restricted
+ * set of depths. Resolve that here:
+ * LVDS typically supports only 6bpc, so clamp down in that case
+ * HDMI supports only 8bpc or 12bpc, so clamp to 8bpc with dither for 10bpc
+ * Displays may support a restricted set as well, check EDID and clamp as
+ * appropriate.
+ *
+ * RETURNS:
+ * Dithering requirement (i.e. false if display bpc and pipe bpc match,
+ * true if they don't match).
+ */
+static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
+ unsigned int *pipe_bpp)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ unsigned int display_bpc = UINT_MAX, bpc;
+
+ /* Walk the encoders & connectors on this crtc, get min bpc */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ if (intel_encoder->type == INTEL_OUTPUT_LVDS) {
+ unsigned int lvds_bpc;
+
+ if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) ==
+ LVDS_A3_POWER_UP)
+ lvds_bpc = 8;
+ else
+ lvds_bpc = 6;
+
+ if (lvds_bpc < display_bpc) {
+ DRM_DEBUG_KMS("clamping display bpc (was %d) to LVDS (%d)\n", display_bpc, lvds_bpc);
+ display_bpc = lvds_bpc;
+ }
+ continue;
+ }
+
+ if (intel_encoder->type == INTEL_OUTPUT_EDP) {
+ /* Use VBT settings if we have an eDP panel */
+ unsigned int edp_bpc = dev_priv->edp.bpp / 3;
+
+ if (edp_bpc < display_bpc) {
+ DRM_DEBUG_KMS("clamping display bpc (was %d) to eDP (%d)\n", display_bpc, edp_bpc);
+ display_bpc = edp_bpc;
+ }
+ continue;
+ }
+
+ /* Not one of the known troublemakers, check the EDID */
+ list_for_each_entry(connector, &dev->mode_config.connector_list,
+ head) {
+ if (connector->encoder != encoder)
+ continue;
+
+ /* Don't use an invalid EDID bpc value */
+ if (connector->display_info.bpc &&
+ connector->display_info.bpc < display_bpc) {
+ DRM_DEBUG_KMS("clamping display bpc (was %d) to EDID reported max of %d\n", display_bpc, connector->display_info.bpc);
+ display_bpc = connector->display_info.bpc;
+ }
+ }
+
+ /*
+ * HDMI is either 12 or 8, so if the display lets 10bpc sneak
+ * through, clamp it down. (Note: >12bpc will be caught below.)
+ */
+ if (intel_encoder->type == INTEL_OUTPUT_HDMI) {
+ if (display_bpc > 8 && display_bpc < 12) {
+ DRM_DEBUG_KMS("forcing bpc to 12 for HDMI\n");
+ display_bpc = 12;
+ } else {
+ DRM_DEBUG_KMS("forcing bpc to 8 for HDMI\n");
+ display_bpc = 8;
+ }
+ }
+ }
+
+ /*
+ * We could just drive the pipe at the highest bpc all the time and
+ * enable dithering as needed, but that costs bandwidth. So choose
+ * the minimum value that expresses the full color range of the fb but
+ * also stays within the max display bpc discovered above.
+ */
+
+ switch (crtc->fb->depth) {
+ case 8:
+ bpc = 8; /* since we go through a colormap */
+ break;
+ case 15:
+ case 16:
+ bpc = 6; /* min is 18bpp */
+ break;
+ case 24:
+ bpc = 8;
+ break;
+ case 30:
+ bpc = 10;
+ break;
+ case 48:
+ bpc = 12;
+ break;
+ default:
+ DRM_DEBUG("unsupported depth, assuming 24 bits\n");
+ bpc = min((unsigned int)8, display_bpc);
+ break;
+ }
+
+ display_bpc = min(display_bpc, bpc);
+
+ DRM_DEBUG_KMS("setting pipe bpc to %d (max display bpc %d)\n",
+ bpc, display_bpc);
+
+ *pipe_bpp = display_bpc * 3;
+
+ return display_bpc != bpc;
}
static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
@@ -4696,6 +5186,156 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
return ret;
}
+/*
+ * Initialize reference clocks when the driver loads
+ */
+void ironlake_init_pch_refclk(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct intel_encoder *encoder;
+ u32 temp;
+ bool has_lvds = false;
+ bool has_cpu_edp = false;
+ bool has_pch_edp = false;
+ bool has_panel = false;
+ bool has_ck505 = false;
+ bool can_ssc = false;
+
+ /* We need to take the global config into account */
+ list_for_each_entry(encoder, &mode_config->encoder_list,
+ base.head) {
+ switch (encoder->type) {
+ case INTEL_OUTPUT_LVDS:
+ has_panel = true;
+ has_lvds = true;
+ break;
+ case INTEL_OUTPUT_EDP:
+ has_panel = true;
+ if (intel_encoder_is_pch_edp(&encoder->base))
+ has_pch_edp = true;
+ else
+ has_cpu_edp = true;
+ break;
+ }
+ }
+
+ if (HAS_PCH_IBX(dev)) {
+ has_ck505 = dev_priv->display_clock_mode;
+ can_ssc = has_ck505;
+ } else {
+ has_ck505 = false;
+ can_ssc = true;
+ }
+
+ DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d has_ck505 %d\n",
+ has_panel, has_lvds, has_pch_edp, has_cpu_edp,
+ has_ck505);
+
+ /* Ironlake: try to setup display ref clock before DPLL
+ * enabling. This is only under driver's control after
+ * PCH B stepping, previous chipset stepping should be
+ * ignoring this setting.
+ */
+ temp = I915_READ(PCH_DREF_CONTROL);
+ /* Always enable nonspread source */
+ temp &= ~DREF_NONSPREAD_SOURCE_MASK;
+
+ if (has_ck505)
+ temp |= DREF_NONSPREAD_CK505_ENABLE;
+ else
+ temp |= DREF_NONSPREAD_SOURCE_ENABLE;
+
+ if (has_panel) {
+ temp &= ~DREF_SSC_SOURCE_MASK;
+ temp |= DREF_SSC_SOURCE_ENABLE;
+
+ /* SSC must be turned on before enabling the CPU output */
+ if (intel_panel_use_ssc(dev_priv) && can_ssc) {
+ DRM_DEBUG_KMS("Using SSC on panel\n");
+ temp |= DREF_SSC1_ENABLE;
+ }
+
+ /* Get SSC going before enabling the outputs */
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+
+ temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+
+ /* Enable CPU source on CPU attached eDP */
+ if (has_cpu_edp) {
+ if (intel_panel_use_ssc(dev_priv) && can_ssc) {
+ DRM_DEBUG_KMS("Using SSC on eDP\n");
+ temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
+ }
+ else
+ temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
+ } else
+ temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
+
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+ } else {
+ DRM_DEBUG_KMS("Disabling SSC entirely\n");
+
+ temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+
+ /* Turn off CPU output */
+ temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
+
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+
+ /* Turn off the SSC source */
+ temp &= ~DREF_SSC_SOURCE_MASK;
+ temp |= DREF_SSC_SOURCE_DISABLE;
+
+ /* Turn off SSC1 */
+ temp &= ~ DREF_SSC1_ENABLE;
+
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+ }
+}
+
+static int ironlake_get_refclk(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_encoder *encoder;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct intel_encoder *edp_encoder = NULL;
+ int num_connectors = 0;
+ bool is_lvds = false;
+
+ list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
+ if (encoder->base.crtc != crtc)
+ continue;
+
+ switch (encoder->type) {
+ case INTEL_OUTPUT_LVDS:
+ is_lvds = true;
+ break;
+ case INTEL_OUTPUT_EDP:
+ edp_encoder = encoder;
+ break;
+ }
+ num_connectors++;
+ }
+
+ if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
+ DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
+ dev_priv->lvds_ssc_freq);
+ return dev_priv->lvds_ssc_freq * 1000;
+ }
+
+ return 120000;
+}
+
static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
@@ -4720,7 +5360,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
struct fdi_m_n m_n = {0};
u32 temp;
u32 lvds_sync = 0;
- int target_clock, pixel_multiplier, lane, link_bw, bpp, factor;
+ int target_clock, pixel_multiplier, lane, link_bw, factor;
+ unsigned int pipe_bpp;
+ bool dither;
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
if (encoder->base.crtc != crtc)
@@ -4753,16 +5395,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
num_connectors++;
}
- if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
- refclk = dev_priv->lvds_ssc_freq * 1000;
- DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
- refclk / 1000);
- } else {
- refclk = 96000;
- if (!has_edp_encoder ||
- intel_encoder_is_pch_edp(&has_edp_encoder->base))
- refclk = 120000; /* 120Mhz refclk */
- }
+ refclk = ironlake_get_refclk(crtc);
/*
* Returns a set of divisors for the desired target clock with the given
@@ -4847,56 +5480,38 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
/* determine panel color depth */
temp = I915_READ(PIPECONF(pipe));
temp &= ~PIPE_BPC_MASK;
- if (is_lvds) {
- /* the BPC will be 6 if it is 18-bit LVDS panel */
- if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
- temp |= PIPE_8BPC;
- else
- temp |= PIPE_6BPC;
- } else if (has_edp_encoder) {
- switch (dev_priv->edp.bpp/3) {
- case 8:
- temp |= PIPE_8BPC;
- break;
- case 10:
- temp |= PIPE_10BPC;
- break;
- case 6:
- temp |= PIPE_6BPC;
- break;
- case 12:
- temp |= PIPE_12BPC;
- break;
- }
- } else
- temp |= PIPE_8BPC;
- I915_WRITE(PIPECONF(pipe), temp);
-
- switch (temp & PIPE_BPC_MASK) {
- case PIPE_8BPC:
- bpp = 24;
+ dither = intel_choose_pipe_bpp_dither(crtc, &pipe_bpp);
+ switch (pipe_bpp) {
+ case 18:
+ temp |= PIPE_6BPC;
break;
- case PIPE_10BPC:
- bpp = 30;
+ case 24:
+ temp |= PIPE_8BPC;
break;
- case PIPE_6BPC:
- bpp = 18;
+ case 30:
+ temp |= PIPE_10BPC;
break;
- case PIPE_12BPC:
- bpp = 36;
+ case 36:
+ temp |= PIPE_12BPC;
break;
default:
- DRM_ERROR("unknown pipe bpc value\n");
- bpp = 24;
+ WARN(1, "intel_choose_pipe_bpp returned invalid value %d\n",
+ pipe_bpp);
+ temp |= PIPE_8BPC;
+ pipe_bpp = 24;
+ break;
}
+ intel_crtc->bpp = pipe_bpp;
+ I915_WRITE(PIPECONF(pipe), temp);
+
if (!lane) {
/*
* Account for spread spectrum to avoid
* oversubscribing the link. Max center spread
* is 2.5%; use 5% for safety's sake.
*/
- u32 bps = target_clock * bpp * 21 / 20;
+ u32 bps = target_clock * intel_crtc->bpp * 21 / 20;
lane = bps / (link_bw * 8) + 1;
}
@@ -4904,51 +5519,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
if (pixel_multiplier > 1)
link_bw *= pixel_multiplier;
- ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
-
- /* Ironlake: try to setup display ref clock before DPLL
- * enabling. This is only under driver's control after
- * PCH B stepping, previous chipset stepping should be
- * ignoring this setting.
- */
- temp = I915_READ(PCH_DREF_CONTROL);
- /* Always enable nonspread source */
- temp &= ~DREF_NONSPREAD_SOURCE_MASK;
- temp |= DREF_NONSPREAD_SOURCE_ENABLE;
- temp &= ~DREF_SSC_SOURCE_MASK;
- temp |= DREF_SSC_SOURCE_ENABLE;
- I915_WRITE(PCH_DREF_CONTROL, temp);
-
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
-
- if (has_edp_encoder) {
- if (intel_panel_use_ssc(dev_priv)) {
- temp |= DREF_SSC1_ENABLE;
- I915_WRITE(PCH_DREF_CONTROL, temp);
-
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
- }
- temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
-
- /* Enable CPU source on CPU attached eDP */
- if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
- if (intel_panel_use_ssc(dev_priv))
- temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
- else
- temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
- } else {
- /* Enable SSC on PCH eDP if needed */
- if (intel_panel_use_ssc(dev_priv)) {
- DRM_ERROR("enabling SSC on PCH\n");
- temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
- }
- }
- I915_WRITE(PCH_DREF_CONTROL, temp);
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
- }
+ ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw,
+ &m_n);
fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
if (has_reduced_clock)
@@ -4965,7 +5537,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
} else if (is_sdvo && is_tv)
factor = 20;
- if (clock.m1 < factor * clock.n)
+ if (clock.m < factor * clock.n)
fp |= FP_CB_TUNE;
dpll = 0;
@@ -5021,39 +5593,32 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
/* Set up the display plane register */
dspcntr = DISPPLANE_GAMMA_ENABLE;
- DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+ DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe);
drm_mode_debug_printmodeline(mode);
/* PCH eDP needs FDI, but CPU eDP does not */
- if (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
- I915_WRITE(PCH_FP0(pipe), fp);
- I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE);
-
- POSTING_READ(PCH_DPLL(pipe));
- udelay(150);
- }
+ if (!intel_crtc->no_pll) {
+ if (!has_edp_encoder ||
+ intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+ I915_WRITE(PCH_FP0(pipe), fp);
+ I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE);
- /* enable transcoder DPLL */
- if (HAS_PCH_CPT(dev)) {
- temp = I915_READ(PCH_DPLL_SEL);
- switch (pipe) {
- case 0:
- temp |= TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL;
- break;
- case 1:
- temp |= TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL;
- break;
- case 2:
- /* FIXME: manage transcoder PLLs? */
- temp |= TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL;
- break;
- default:
- BUG();
+ POSTING_READ(PCH_DPLL(pipe));
+ udelay(150);
+ }
+ } else {
+ if (dpll == (I915_READ(PCH_DPLL(0)) & 0x7fffffff) &&
+ fp == I915_READ(PCH_FP0(0))) {
+ intel_crtc->use_pll_a = true;
+ DRM_DEBUG_KMS("using pipe a dpll\n");
+ } else if (dpll == (I915_READ(PCH_DPLL(1)) & 0x7fffffff) &&
+ fp == I915_READ(PCH_FP0(1))) {
+ intel_crtc->use_pll_a = false;
+ DRM_DEBUG_KMS("using pipe b dpll\n");
+ } else {
+ DRM_DEBUG_KMS("no matching PLL configuration for pipe 2\n");
+ return -EINVAL;
}
- I915_WRITE(PCH_DPLL_SEL, temp);
-
- POSTING_READ(PCH_DPLL_SEL);
- udelay(150);
}
/* The LVDS pin pair needs to be on before the DPLLs are enabled.
@@ -5063,17 +5628,13 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
if (is_lvds) {
temp = I915_READ(PCH_LVDS);
temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
- if (pipe == 1) {
- if (HAS_PCH_CPT(dev))
- temp |= PORT_TRANS_B_SEL_CPT;
- else
- temp |= LVDS_PIPEB_SELECT;
- } else {
- if (HAS_PCH_CPT(dev))
- temp &= ~PORT_TRANS_SEL_MASK;
- else
- temp &= ~LVDS_PIPEB_SELECT;
- }
+ if (HAS_PCH_CPT(dev))
+ temp |= PORT_TRANS_SEL_CPT(pipe);
+ else if (pipe == 1)
+ temp |= LVDS_PIPEB_SELECT;
+ else
+ temp &= ~LVDS_PIPEB_SELECT;
+
/* set the corresponsding LVDS_BORDER bit */
temp |= dev_priv->lvds_border_bits;
/* Set the B0-B3 data pairs corresponding to whether we're going to
@@ -5107,14 +5668,12 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(PCH_LVDS, temp);
}
- /* set the dithering flag and clear for anything other than a panel. */
pipeconf &= ~PIPECONF_DITHER_EN;
pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
- if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
+ if ((is_lvds && dev_priv->lvds_dither) || dither) {
pipeconf |= PIPECONF_DITHER_EN;
- pipeconf |= PIPECONF_DITHER_TYPE_ST1;
+ pipeconf |= PIPECONF_DITHER_TYPE_SP;
}
-
if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
intel_dp_set_m_n(crtc, mode, adjusted_mode);
} else {
@@ -5125,8 +5684,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(TRANSDPLINK_N1(pipe), 0);
}
- if (!has_edp_encoder ||
- intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+ if (!intel_crtc->no_pll &&
+ (!has_edp_encoder ||
+ intel_encoder_is_pch_edp(&has_edp_encoder->base))) {
I915_WRITE(PCH_DPLL(pipe), dpll);
/* Wait for the clocks to stabilize. */
@@ -5142,18 +5702,20 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
}
intel_crtc->lowfreq_avail = false;
- if (is_lvds && has_reduced_clock && i915_powersave) {
- I915_WRITE(PCH_FP1(pipe), fp2);
- intel_crtc->lowfreq_avail = true;
- if (HAS_PIPE_CXSR(dev)) {
- DRM_DEBUG_KMS("enabling CxSR downclocking\n");
- pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
- }
- } else {
- I915_WRITE(PCH_FP1(pipe), fp);
- if (HAS_PIPE_CXSR(dev)) {
- DRM_DEBUG_KMS("disabling CxSR downclocking\n");
- pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
+ if (!intel_crtc->no_pll) {
+ if (is_lvds && has_reduced_clock && i915_powersave) {
+ I915_WRITE(PCH_FP1(pipe), fp2);
+ intel_crtc->lowfreq_avail = true;
+ if (HAS_PIPE_CXSR(dev)) {
+ DRM_DEBUG_KMS("enabling CxSR downclocking\n");
+ pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
+ }
+ } else {
+ I915_WRITE(PCH_FP1(pipe), fp);
+ if (HAS_PIPE_CXSR(dev)) {
+ DRM_DEBUG_KMS("disabling CxSR downclocking\n");
+ pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
+ }
}
}
@@ -5245,9 +5807,136 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
drm_vblank_post_modeset(dev, pipe);
+ intel_crtc->dpms_mode = DRM_MODE_DPMS_ON;
+
return ret;
}
+static void g4x_write_eld(struct drm_connector *connector,
+ struct drm_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ uint8_t *eld = connector->eld;
+ uint32_t eldv;
+ uint32_t len;
+ uint32_t i;
+
+ i = I915_READ(G4X_AUD_VID_DID);
+
+ if (i == INTEL_AUDIO_DEVBLC || i == INTEL_AUDIO_DEVCL)
+ eldv = G4X_ELDV_DEVCL_DEVBLC;
+ else
+ eldv = G4X_ELDV_DEVCTG;
+
+ i = I915_READ(G4X_AUD_CNTL_ST);
+ i &= ~(eldv | G4X_ELD_ADDR);
+ len = (i >> 9) & 0x1f; /* ELD buffer size */
+ I915_WRITE(G4X_AUD_CNTL_ST, i);
+
+ if (!eld[0])
+ return;
+
+ len = min_t(uint8_t, eld[2], len);
+ DRM_DEBUG_DRIVER("ELD size %d\n", len);
+ for (i = 0; i < len; i++)
+ I915_WRITE(G4X_HDMIW_HDMIEDID, *((uint32_t *)eld + i));
+
+ i = I915_READ(G4X_AUD_CNTL_ST);
+ i |= eldv;
+ I915_WRITE(G4X_AUD_CNTL_ST, i);
+}
+
+static void ironlake_write_eld(struct drm_connector *connector,
+ struct drm_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ uint8_t *eld = connector->eld;
+ uint32_t eldv;
+ uint32_t i;
+ int len;
+ int hdmiw_hdmiedid;
+ int aud_cntl_st;
+ int aud_cntrl_st2;
+
+ if (IS_IVYBRIDGE(connector->dev)) {
+ hdmiw_hdmiedid = GEN7_HDMIW_HDMIEDID_A;
+ aud_cntl_st = GEN7_AUD_CNTRL_ST_A;
+ aud_cntrl_st2 = GEN7_AUD_CNTRL_ST2;
+ } else {
+ hdmiw_hdmiedid = GEN5_HDMIW_HDMIEDID_A;
+ aud_cntl_st = GEN5_AUD_CNTL_ST_A;
+ aud_cntrl_st2 = GEN5_AUD_CNTL_ST2;
+ }
+
+ i = to_intel_crtc(crtc)->pipe;
+ hdmiw_hdmiedid += i * 0x100;
+ aud_cntl_st += i * 0x100;
+
+ DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i));
+
+ i = I915_READ(aud_cntl_st);
+ i = (i >> 29) & 0x3; /* DIP_Port_Select, 0x1 = PortB */
+ if (!i) {
+ DRM_DEBUG_DRIVER("Audio directed to unknown port\n");
+ /* operate blindly on all ports */
+ eldv = GEN5_ELD_VALIDB;
+ eldv |= GEN5_ELD_VALIDB << 4;
+ eldv |= GEN5_ELD_VALIDB << 8;
+ } else {
+ DRM_DEBUG_DRIVER("ELD on port %c\n", 'A' + i);
+ eldv = GEN5_ELD_VALIDB << ((i - 1) * 4);
+ }
+
+ i = I915_READ(aud_cntrl_st2);
+ i &= ~eldv;
+ I915_WRITE(aud_cntrl_st2, i);
+
+ if (!eld[0])
+ return;
+
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+ DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
+ eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */
+ }
+
+ i = I915_READ(aud_cntl_st);
+ i &= ~GEN5_ELD_ADDRESS;
+ I915_WRITE(aud_cntl_st, i);
+
+ len = min_t(uint8_t, eld[2], 21); /* 84 bytes of hw ELD buffer */
+ DRM_DEBUG_DRIVER("ELD size %d\n", len);
+ for (i = 0; i < len; i++)
+ I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i));
+
+ i = I915_READ(aud_cntrl_st2);
+ i |= eldv;
+ I915_WRITE(aud_cntrl_st2, i);
+}
+
+void intel_write_eld(struct drm_encoder *encoder,
+ struct drm_display_mode *mode)
+{
+ struct drm_crtc *crtc = encoder->crtc;
+ struct drm_connector *connector;
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ connector = drm_select_eld(encoder, mode);
+ if (!connector)
+ return;
+
+ DRM_DEBUG_DRIVER("ELD on [CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
+ connector->base.id,
+ drm_get_connector_name(connector),
+ connector->encoder->base.id,
+ drm_get_encoder_name(connector->encoder));
+
+ connector->eld[6] = drm_av_sync_delay(connector, mode) / 2;
+
+ if (dev_priv->display.write_eld)
+ dev_priv->display.write_eld(connector, crtc);
+}
+
/** Loads the palette/gamma unit for the CRTC with the prepared values */
void intel_crtc_load_lut(struct drm_crtc *crtc)
{
@@ -5329,6 +6018,31 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
I915_WRITE(CURBASE(pipe), base);
}
+static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ bool visible = base != 0;
+
+ if (intel_crtc->cursor_visible != visible) {
+ uint32_t cntl = I915_READ(CURCNTR_IVB(pipe));
+ if (base) {
+ cntl &= ~CURSOR_MODE;
+ cntl |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
+ } else {
+ cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
+ cntl |= CURSOR_MODE_DISABLE;
+ }
+ I915_WRITE(CURCNTR_IVB(pipe), cntl);
+
+ intel_crtc->cursor_visible = visible;
+ }
+ /* and commit changes on next vblank */
+ I915_WRITE(CURBASE_IVB(pipe), base);
+}
+
/* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */
static void intel_crtc_update_cursor(struct drm_crtc *crtc,
bool on)
@@ -5376,11 +6090,16 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
if (!visible && !intel_crtc->cursor_visible)
return;
- I915_WRITE(CURPOS(pipe), pos);
- if (IS_845G(dev) || IS_I865G(dev))
- i845_update_cursor(crtc, base);
- else
- i9xx_update_cursor(crtc, base);
+ if (IS_IVYBRIDGE(dev)) {
+ I915_WRITE(CURPOS_IVB(pipe), pos);
+ ivb_update_cursor(crtc, base);
+ } else {
+ I915_WRITE(CURPOS(pipe), pos);
+ if (IS_845G(dev) || IS_I865G(dev))
+ i845_update_cursor(crtc, base);
+ else
+ i9xx_update_cursor(crtc, base);
+ }
if (visible)
intel_mark_busy(dev, to_intel_framebuffer(crtc->fb)->obj);
@@ -5434,21 +6153,15 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
goto fail_locked;
}
- ret = i915_gem_object_pin(obj, PAGE_SIZE, true);
- if (ret) {
- DRM_ERROR("failed to pin cursor bo\n");
- goto fail_locked;
- }
-
- ret = i915_gem_object_set_to_gtt_domain(obj, 0);
+ ret = i915_gem_object_pin_to_display_plane(obj, 0, NULL);
if (ret) {
DRM_ERROR("failed to move cursor bo into the GTT\n");
- goto fail_unpin;
+ goto fail_locked;
}
ret = i915_gem_object_put_fence(obj);
if (ret) {
- DRM_ERROR("failed to move cursor bo into the GTT\n");
+ DRM_ERROR("failed to release fence for cursor");
goto fail_unpin;
}
@@ -6151,6 +6864,7 @@ static void intel_unpin_work_fn(struct work_struct *__work)
drm_gem_object_unreference(&work->pending_flip_obj->base);
drm_gem_object_unreference(&work->old_fb_obj->base);
+ intel_update_fbc(work->dev);
mutex_unlock(&work->dev->struct_mutex);
kfree(work);
}
@@ -6515,6 +7229,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
if (ret)
goto cleanup_pending;
+ intel_disable_fbc(dev);
mutex_unlock(&dev->struct_mutex);
trace_i915_flip_request(intel_crtc->plane, obj);
@@ -6643,8 +7358,11 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
intel_crtc_reset(&intel_crtc->base);
intel_crtc->active = true; /* force the pipe off on setup_init_config */
+ intel_crtc->bpp = 24; /* default for pre-Ironlake */
if (HAS_PCH_SPLIT(dev)) {
+ if (pipe == 2 && IS_IVYBRIDGE(dev))
+ intel_crtc->no_pll = true;
intel_helper_funcs.prepare = ironlake_crtc_prepare;
intel_helper_funcs.commit = ironlake_crtc_commit;
} else {
@@ -6822,10 +7540,11 @@ static void intel_setup_outputs(struct drm_device *dev)
intel_encoder_clones(dev, encoder->clone_mask);
}
- intel_panel_setup_backlight(dev);
-
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
+
+ if (HAS_PCH_SPLIT(dev))
+ ironlake_init_pch_refclk(dev);
}
static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
@@ -6869,6 +7588,11 @@ int intel_framebuffer_init(struct drm_device *dev,
switch (mode_cmd->bpp) {
case 8:
case 16:
+ /* Only pre-ILK can handle 5:5:5 */
+ if (mode_cmd->depth == 15 && !HAS_PCH_SPLIT(dev))
+ return -EINVAL;
+ break;
+
case 24:
case 32:
break;
@@ -7065,6 +7789,10 @@ void gen6_disable_rps(struct drm_device *dev)
I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
I915_WRITE(GEN6_PMIER, 0);
+ /* Complete PM interrupt masking here doesn't race with the rps work
+ * item again unmasking PM interrupts because that is using a different
+ * register (PMIMR) to mask PM interrupts. The only risk is in leaving
+ * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */
spin_lock_irq(&dev_priv->rps_lock);
dev_priv->pm_iir = 0;
@@ -7283,6 +8011,59 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
mutex_unlock(&dev_priv->dev->struct_mutex);
}
+void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
+{
+ int min_freq = 15;
+ int gpu_freq, ia_freq, max_ia_freq;
+ int scaling_factor = 180;
+
+ max_ia_freq = cpufreq_quick_get_max(0);
+ /*
+ * Default to measured freq if none found, PCU will ensure we don't go
+ * over
+ */
+ if (!max_ia_freq)
+ max_ia_freq = tsc_khz;
+
+ /* Convert from kHz to MHz */
+ max_ia_freq /= 1000;
+
+ mutex_lock(&dev_priv->dev->struct_mutex);
+
+ /*
+ * For each potential GPU frequency, load a ring frequency we'd like
+ * to use for memory access. We do this by specifying the IA frequency
+ * the PCU should use as a reference to determine the ring frequency.
+ */
+ for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay;
+ gpu_freq--) {
+ int diff = dev_priv->max_delay - gpu_freq;
+
+ /*
+ * For GPU frequencies less than 750MHz, just use the lowest
+ * ring freq.
+ */
+ if (gpu_freq < min_freq)
+ ia_freq = 800;
+ else
+ ia_freq = max_ia_freq - ((diff * scaling_factor) / 2);
+ ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100);
+
+ I915_WRITE(GEN6_PCODE_DATA,
+ (ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) |
+ gpu_freq);
+ I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
+ GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
+ if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
+ GEN6_PCODE_READY) == 0, 10)) {
+ DRM_ERROR("pcode write of freq table timed out\n");
+ continue;
+ }
+ }
+
+ mutex_unlock(&dev_priv->dev->struct_mutex);
+}
+
static void ironlake_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -7368,6 +8149,20 @@ static void gen6_init_clock_gating(struct drm_device *dev)
I915_WRITE(WM2_LP_ILK, 0);
I915_WRITE(WM1_LP_ILK, 0);
+ /* According to the BSpec vol1g, bit 12 (RCPBUNIT) clock
+ * gating disable must be set. Failure to set it results in
+ * flickering pixels due to Z write ordering failures after
+ * some amount of runtime in the Mesa "fire" demo, and Unigine
+ * Sanctuary and Tropics, and apparently anything else with
+ * alpha test or pixel discard.
+ *
+ * According to the spec, bit 11 (RCCUNIT) must also be set,
+ * but we didn't debug actual testcases to find it out.
+ */
+ I915_WRITE(GEN6_UCGCTL2,
+ GEN6_RCPBUNIT_CLOCK_GATE_DISABLE |
+ GEN6_RCCUNIT_CLOCK_GATE_DISABLE);
+
/*
* According to the spec the following bits should be
* set in order to enable memory self-refresh and fbc:
@@ -7388,10 +8183,12 @@ static void gen6_init_clock_gating(struct drm_device *dev)
ILK_DPARB_CLK_GATE |
ILK_DPFD_CLK_GATE);
- for_each_pipe(pipe)
+ for_each_pipe(pipe) {
I915_WRITE(DSPCNTR(pipe),
I915_READ(DSPCNTR(pipe)) |
DISPPLANE_TRICKLE_FEED_DISABLE);
+ intel_flush_display_plane(dev_priv, pipe);
+ }
}
static void ivybridge_init_clock_gating(struct drm_device *dev)
@@ -7408,10 +8205,12 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE);
- for_each_pipe(pipe)
+ for_each_pipe(pipe) {
I915_WRITE(DSPCNTR(pipe),
I915_READ(DSPCNTR(pipe)) |
DISPPLANE_TRICKLE_FEED_DISABLE);
+ intel_flush_display_plane(dev_priv, pipe);
+ }
}
static void g4x_init_clock_gating(struct drm_device *dev)
@@ -7494,6 +8293,7 @@ static void ibx_init_clock_gating(struct drm_device *dev)
static void cpt_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ int pipe;
/*
* On Ibex Peak and Cougar Point, we need to disable clock
@@ -7503,6 +8303,9 @@ static void cpt_init_clock_gating(struct drm_device *dev)
I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) |
DPLS_EDP_PPS_FIX_DIS);
+ /* Without this, mode sets may fail silently on FDI */
+ for_each_pipe(pipe)
+ I915_WRITE(TRANS_CHICKEN2(pipe), TRANS_AUTOTRAIN_GEN_STALL_DIS);
}
static void ironlake_teardown_rc6(struct drm_device *dev)
@@ -7639,9 +8442,11 @@ static void intel_init_display(struct drm_device *dev)
if (HAS_PCH_SPLIT(dev)) {
dev_priv->display.dpms = ironlake_crtc_dpms;
dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set;
+ dev_priv->display.update_plane = ironlake_update_plane;
} else {
dev_priv->display.dpms = i9xx_crtc_dpms;
dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
+ dev_priv->display.update_plane = i9xx_update_plane;
}
if (I915_HAS_FBC(dev)) {
@@ -7662,7 +8467,7 @@ static void intel_init_display(struct drm_device *dev)
}
/* Returns the core display clock speed */
- if (IS_I945G(dev) || (IS_G33(dev) && ! IS_PINEVIEW_M(dev)))
+ if (IS_I945G(dev) || (IS_G33(dev) && !IS_PINEVIEW_M(dev)))
dev_priv->display.get_display_clock_speed =
i945_get_display_clock_speed;
else if (IS_I915G(dev))
@@ -7701,6 +8506,7 @@ static void intel_init_display(struct drm_device *dev)
}
dev_priv->display.fdi_link_train = ironlake_fdi_link_train;
dev_priv->display.init_clock_gating = ironlake_init_clock_gating;
+ dev_priv->display.write_eld = ironlake_write_eld;
} else if (IS_GEN6(dev)) {
if (SNB_READ_WM0_LATENCY()) {
dev_priv->display.update_wm = sandybridge_update_wm;
@@ -7711,6 +8517,7 @@ static void intel_init_display(struct drm_device *dev)
}
dev_priv->display.fdi_link_train = gen6_fdi_link_train;
dev_priv->display.init_clock_gating = gen6_init_clock_gating;
+ dev_priv->display.write_eld = ironlake_write_eld;
} else if (IS_IVYBRIDGE(dev)) {
/* FIXME: detect B0+ stepping and use auto training */
dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
@@ -7722,7 +8529,7 @@ static void intel_init_display(struct drm_device *dev)
dev_priv->display.update_wm = NULL;
}
dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
-
+ dev_priv->display.write_eld = ironlake_write_eld;
} else
dev_priv->display.update_wm = NULL;
} else if (IS_PINEVIEW(dev)) {
@@ -7733,7 +8540,7 @@ static void intel_init_display(struct drm_device *dev)
DRM_INFO("failed to find known CxSR latency "
"(found ddr%s fsb freq %d, mem freq %d), "
"disabling CxSR\n",
- (dev_priv->is_ddr3 == 1) ? "3": "2",
+ (dev_priv->is_ddr3 == 1) ? "3" : "2",
dev_priv->fsb_freq, dev_priv->mem_freq);
/* Disable CxSR and never update its watermark again */
pineview_disable_cxsr(dev);
@@ -7742,6 +8549,7 @@ static void intel_init_display(struct drm_device *dev)
dev_priv->display.update_wm = pineview_update_wm;
dev_priv->display.init_clock_gating = gen3_init_clock_gating;
} else if (IS_G4X(dev)) {
+ dev_priv->display.write_eld = g4x_write_eld;
dev_priv->display.update_wm = g4x_update_wm;
dev_priv->display.init_clock_gating = g4x_init_clock_gating;
} else if (IS_GEN4(dev)) {
@@ -7802,7 +8610,7 @@ static void intel_init_display(struct drm_device *dev)
* resume, or other times. This quirk makes sure that's the case for
* affected systems.
*/
-static void quirk_pipea_force (struct drm_device *dev)
+static void quirk_pipea_force(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -7810,6 +8618,15 @@ static void quirk_pipea_force (struct drm_device *dev)
DRM_DEBUG_DRIVER("applying pipe a force quirk\n");
}
+/*
+ * Some machines (Lenovo U160) do not work with SSC on LVDS for some reason
+ */
+static void quirk_ssc_force_disable(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ dev_priv->quirks |= QUIRK_LVDS_SSC_DISABLE;
+}
+
struct intel_quirk {
int device;
int subsystem_vendor;
@@ -7821,7 +8638,7 @@ struct intel_quirk intel_quirks[] = {
/* HP Compaq 2730p needs pipe A force quirk (LP: #291555) */
{ 0x2a42, 0x103c, 0x30eb, quirk_pipea_force },
/* HP Mini needs pipe A force quirk (LP: #322104) */
- { 0x27ae,0x103c, 0x361a, quirk_pipea_force },
+ { 0x27ae, 0x103c, 0x361a, quirk_pipea_force },
/* Thinkpad R31 needs pipe A force quirk */
{ 0x3577, 0x1014, 0x0505, quirk_pipea_force },
@@ -7838,6 +8655,12 @@ struct intel_quirk intel_quirks[] = {
/* 855 & before need to leave pipe A & dpll A up */
{ 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
{ 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
+
+ /* Lenovo U160 cannot use SSC on LVDS */
+ { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
+
+ /* Sony Vaio Y cannot use SSC on LVDS */
+ { 0x0046, 0x104d, 0x9076, quirk_ssc_force_disable },
};
static void intel_init_quirks(struct drm_device *dev)
@@ -7926,8 +8749,10 @@ void intel_modeset_init(struct drm_device *dev)
intel_init_emon(dev);
}
- if (IS_GEN6(dev))
+ if (IS_GEN6(dev) || IS_GEN7(dev)) {
gen6_enable_rps(dev_priv);
+ gen6_update_ring_freq(dev_priv);
+ }
INIT_WORK(&dev_priv->idle_work, intel_idle_update);
setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
@@ -7963,12 +8788,11 @@ void intel_modeset_cleanup(struct drm_device *dev)
intel_increase_pllclock(crtc);
}
- if (dev_priv->display.disable_fbc)
- dev_priv->display.disable_fbc(dev);
+ intel_disable_fbc(dev);
if (IS_IRONLAKE_M(dev))
ironlake_disable_drps(dev);
- if (IS_GEN6(dev))
+ if (IS_GEN6(dev) || IS_GEN7(dev))
gen6_disable_rps(dev);
if (IS_IRONLAKE_M(dev))
@@ -7980,6 +8804,10 @@ void intel_modeset_cleanup(struct drm_device *dev)
* enqueue unpin/hotplug work. */
drm_irq_uninstall(dev);
cancel_work_sync(&dev_priv->hotplug_work);
+ cancel_work_sync(&dev_priv->rps_work);
+
+ /* flush any delayed tasks or pending work */
+ flush_scheduled_work();
/* Shut off idle work before the crtcs get freed. */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -8062,7 +8890,7 @@ struct intel_display_error_state {
struct intel_display_error_state *
intel_display_capture_error_state(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_display_error_state *error;
int i;
@@ -8078,7 +8906,7 @@ intel_display_capture_error_state(struct drm_device *dev)
error->plane[i].control = I915_READ(DSPCNTR(i));
error->plane[i].stride = I915_READ(DSPSTRIDE(i));
error->plane[i].size = I915_READ(DSPSIZE(i));
- error->plane[i].pos= I915_READ(DSPPOS(i));
+ error->plane[i].pos = I915_READ(DSPPOS(i));
error->plane[i].addr = I915_READ(DSPADDR(i));
if (INTEL_INFO(dev)->gen >= 4) {
error->plane[i].surface = I915_READ(DSPSURF(i));
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 391b55f1cc74..4d0358fad937 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -27,6 +27,7 @@
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
@@ -36,7 +37,7 @@
#include "i915_drv.h"
#include "drm_dp_helper.h"
-
+#define DP_RECEIVER_CAP_SIZE 0xf
#define DP_LINK_STATUS_SIZE 6
#define DP_LINK_CHECK_TIMEOUT (10 * 1000)
@@ -53,12 +54,19 @@ struct intel_dp {
int dpms_mode;
uint8_t link_bw;
uint8_t lane_count;
- uint8_t dpcd[4];
+ uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
struct i2c_adapter adapter;
struct i2c_algo_dp_aux_data algo;
bool is_pch_edp;
uint8_t train_set[4];
- uint8_t link_status[DP_LINK_STATUS_SIZE];
+ int panel_power_up_delay;
+ int panel_power_down_delay;
+ int panel_power_cycle_delay;
+ int backlight_on_delay;
+ int backlight_off_delay;
+ struct drm_display_mode *panel_fixed_mode; /* for eDP */
+ struct delayed_work panel_vdd_work;
+ bool want_panel_vdd;
};
/**
@@ -86,6 +94,17 @@ static bool is_pch_edp(struct intel_dp *intel_dp)
return intel_dp->is_pch_edp;
}
+/**
+ * is_cpu_edp - is the port on the CPU and attached to an eDP panel?
+ * @intel_dp: DP struct
+ *
+ * Returns true if the given DP struct corresponds to a CPU eDP port.
+ */
+static bool is_cpu_edp(struct intel_dp *intel_dp)
+{
+ return is_edp(intel_dp) && !is_pch_edp(intel_dp);
+}
+
static struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
{
return container_of(encoder, struct intel_dp, base.base);
@@ -121,7 +140,7 @@ static void intel_dp_complete_link_train(struct intel_dp *intel_dp);
static void intel_dp_link_down(struct intel_dp *intel_dp);
void
-intel_edp_link_config (struct intel_encoder *intel_encoder,
+intel_edp_link_config(struct intel_encoder *intel_encoder,
int *lane_num, int *link_bw)
{
struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base);
@@ -136,16 +155,12 @@ intel_edp_link_config (struct intel_encoder *intel_encoder,
static int
intel_dp_max_lane_count(struct intel_dp *intel_dp)
{
- int max_lane_count = 4;
-
- if (intel_dp->dpcd[0] >= 0x11) {
- max_lane_count = intel_dp->dpcd[2] & 0x1f;
- switch (max_lane_count) {
- case 1: case 2: case 4:
- break;
- default:
- max_lane_count = 4;
- }
+ int max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f;
+ switch (max_lane_count) {
+ case 1: case 2: case 4:
+ break;
+ default:
+ max_lane_count = 4;
}
return max_lane_count;
}
@@ -153,7 +168,7 @@ intel_dp_max_lane_count(struct intel_dp *intel_dp)
static int
intel_dp_max_link_bw(struct intel_dp *intel_dp)
{
- int max_link_bw = intel_dp->dpcd[1];
+ int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
switch (max_link_bw) {
case DP_LINK_BW_1_62:
@@ -175,16 +190,34 @@ intel_dp_link_clock(uint8_t link_bw)
return 162000;
}
-/* I think this is a fiction */
+/*
+ * The units on the numbers in the next two are... bizarre. Examples will
+ * make it clearer; this one parallels an example in the eDP spec.
+ *
+ * intel_dp_max_data_rate for one lane of 2.7GHz evaluates as:
+ *
+ * 270000 * 1 * 8 / 10 == 216000
+ *
+ * The actual data capacity of that configuration is 2.16Gbit/s, so the
+ * units are decakilobits. ->clock in a drm_display_mode is in kilohertz -
+ * or equivalently, kilopixels per second - so for 1680x1050R it'd be
+ * 119000. At 18bpp that's 2142000 kilobits per second.
+ *
+ * Thus the strange-looking division by 10 in intel_dp_link_required, to
+ * get the result in decakilobits instead of kilobits.
+ */
+
static int
-intel_dp_link_required(struct drm_device *dev, struct intel_dp *intel_dp, int pixel_clock)
+intel_dp_link_required(struct intel_dp *intel_dp, int pixel_clock)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = intel_dp->base.base.crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int bpp = 24;
- if (is_edp(intel_dp))
- return (pixel_clock * dev_priv->edp.bpp + 7) / 8;
- else
- return pixel_clock * 3;
+ if (intel_crtc)
+ bpp = intel_crtc->bpp;
+
+ return (pixel_clock * bpp + 9) / 10;
}
static int
@@ -198,24 +231,19 @@ intel_dp_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
- struct drm_device *dev = connector->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp));
int max_lanes = intel_dp_max_lane_count(intel_dp);
- if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) {
- if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay)
+ if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) {
+ if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay)
return MODE_PANEL;
- if (mode->vdisplay > dev_priv->panel_fixed_mode->vdisplay)
+ if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay)
return MODE_PANEL;
}
- /* only refuse the mode on non eDP since we have seen some weird eDP panels
- which are outside spec tolerances but somehow work by magic */
- if (!is_edp(intel_dp) &&
- (intel_dp_link_required(connector->dev, intel_dp, mode->clock)
- > intel_dp_max_data_rate(max_link_clock, max_lanes)))
+ if (intel_dp_link_required(intel_dp, mode->clock)
+ > intel_dp_max_data_rate(max_link_clock, max_lanes))
return MODE_CLOCK_HIGH;
if (mode->clock < 10000)
@@ -277,6 +305,38 @@ intel_hrawclk(struct drm_device *dev)
}
}
+static bool ironlake_edp_have_panel_power(struct intel_dp *intel_dp)
+{
+ struct drm_device *dev = intel_dp->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ return (I915_READ(PCH_PP_STATUS) & PP_ON) != 0;
+}
+
+static bool ironlake_edp_have_panel_vdd(struct intel_dp *intel_dp)
+{
+ struct drm_device *dev = intel_dp->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ return (I915_READ(PCH_PP_CONTROL) & EDP_FORCE_VDD) != 0;
+}
+
+static void
+intel_dp_check_edp(struct intel_dp *intel_dp)
+{
+ struct drm_device *dev = intel_dp->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (!is_edp(intel_dp))
+ return;
+ if (!ironlake_edp_have_panel_power(intel_dp) && !ironlake_edp_have_panel_vdd(intel_dp)) {
+ WARN(1, "eDP powered off while attempting aux channel communication.\n");
+ DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n",
+ I915_READ(PCH_PP_STATUS),
+ I915_READ(PCH_PP_CONTROL));
+ }
+}
+
static int
intel_dp_aux_ch(struct intel_dp *intel_dp,
uint8_t *send, int send_bytes,
@@ -293,6 +353,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
uint32_t aux_clock_divider;
int try, precharge;
+ intel_dp_check_edp(intel_dp);
/* The clock divider is based off the hrawclk,
* and would like to run at 2MHz. So, take the
* hrawclk value and divide by 2 and use that
@@ -300,7 +361,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
* Note that PCH attached eDP panels should use a 125MHz input
* clock divider.
*/
- if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) {
+ if (is_cpu_edp(intel_dp)) {
if (IS_GEN6(dev))
aux_clock_divider = 200; /* SNB eDP input clock at 400Mhz */
else
@@ -315,9 +376,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
else
precharge = 5;
- if (I915_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) {
- DRM_ERROR("dp_aux_ch not started status 0x%08x\n",
- I915_READ(ch_ctl));
+ /* Try to wait for any previous AUX channel activity */
+ for (try = 0; try < 3; try++) {
+ status = I915_READ(ch_ctl);
+ if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
+ break;
+ msleep(1);
+ }
+
+ if (try == 3) {
+ WARN(1, "dp_aux_ch not started status 0x%08x\n",
+ I915_READ(ch_ctl));
return -EBUSY;
}
@@ -327,7 +396,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
for (i = 0; i < send_bytes; i += 4)
I915_WRITE(ch_data + i,
pack_aux(send + i, send_bytes - i));
-
+
/* Send the command and wait for it to complete */
I915_WRITE(ch_ctl,
DP_AUX_CH_CTL_SEND_BUSY |
@@ -344,7 +413,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
break;
udelay(100);
}
-
+
/* Clear done status and any errors */
I915_WRITE(ch_ctl,
status |
@@ -380,7 +449,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT);
if (recv_bytes > recv_size)
recv_bytes = recv_size;
-
+
for (i = 0; i < recv_bytes; i += 4)
unpack_aux(I915_READ(ch_data + i),
recv + i, recv_bytes - i);
@@ -398,6 +467,7 @@ intel_dp_aux_native_write(struct intel_dp *intel_dp,
int msg_bytes;
uint8_t ack;
+ intel_dp_check_edp(intel_dp);
if (send_bytes > 16)
return -1;
msg[0] = AUX_NATIVE_WRITE << 4;
@@ -440,6 +510,7 @@ intel_dp_aux_native_read(struct intel_dp *intel_dp,
uint8_t ack;
int ret;
+ intel_dp_check_edp(intel_dp);
msg[0] = AUX_NATIVE_READ << 4;
msg[1] = address >> 8;
msg[2] = address & 0xff;
@@ -483,6 +554,7 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
int reply_bytes;
int ret;
+ intel_dp_check_edp(intel_dp);
/* Set up the command byte */
if (mode & MODE_I2C_READ)
msg[0] = AUX_I2C_READ << 4;
@@ -563,24 +635,32 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
return -EREMOTEIO;
}
+static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp);
+static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
+
static int
intel_dp_i2c_init(struct intel_dp *intel_dp,
struct intel_connector *intel_connector, const char *name)
{
+ int ret;
+
DRM_DEBUG_KMS("i2c_init %s\n", name);
intel_dp->algo.running = false;
intel_dp->algo.address = 0;
intel_dp->algo.aux_ch = intel_dp_i2c_aux_ch;
- memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter));
+ memset(&intel_dp->adapter, '\0', sizeof(intel_dp->adapter));
intel_dp->adapter.owner = THIS_MODULE;
intel_dp->adapter.class = I2C_CLASS_DDC;
- strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
+ strncpy(intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0';
intel_dp->adapter.algo_data = &intel_dp->algo;
intel_dp->adapter.dev.parent = &intel_connector->base.kdev;
- return i2c_dp_aux_add_bus(&intel_dp->adapter);
+ ironlake_edp_panel_vdd_on(intel_dp);
+ ret = i2c_dp_aux_add_bus(&intel_dp->adapter);
+ ironlake_edp_panel_vdd_off(intel_dp, false);
+ return ret;
}
static bool
@@ -588,29 +668,28 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
int lane_count, clock;
int max_lane_count = intel_dp_max_lane_count(intel_dp);
int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0;
static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };
- if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) {
- intel_fixed_panel_mode(dev_priv->panel_fixed_mode, adjusted_mode);
+ if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) {
+ intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode);
intel_pch_panel_fitting(dev, DRM_MODE_SCALE_FULLSCREEN,
mode, adjusted_mode);
/*
* the mode->clock is used to calculate the Data&Link M/N
* of the pipe. For the eDP the fixed clock should be used.
*/
- mode->clock = dev_priv->panel_fixed_mode->clock;
+ mode->clock = intel_dp->panel_fixed_mode->clock;
}
for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
for (clock = 0; clock <= max_clock; clock++) {
int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count);
- if (intel_dp_link_required(encoder->dev, intel_dp, mode->clock)
+ if (intel_dp_link_required(intel_dp, mode->clock)
<= link_avail) {
intel_dp->link_bw = bws[clock];
intel_dp->lane_count = lane_count;
@@ -624,19 +703,6 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
}
}
- if (is_edp(intel_dp)) {
- /* okay we failed just pick the highest */
- intel_dp->lane_count = max_lane_count;
- intel_dp->link_bw = bws[max_clock];
- adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw);
- DRM_DEBUG_KMS("Force picking display port link bw %02x lane "
- "count %d clock %d\n",
- intel_dp->link_bw, intel_dp->lane_count,
- adjusted_mode->clock);
-
- return true;
- }
-
return false;
}
@@ -682,7 +748,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_encoder *encoder;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int lane_count = 4, bpp = 24;
+ int lane_count = 4;
struct intel_dp_m_n m_n;
int pipe = intel_crtc->pipe;
@@ -696,13 +762,11 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
continue;
intel_dp = enc_to_intel_dp(encoder);
- if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT) {
+ if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT ||
+ intel_dp->base.type == INTEL_OUTPUT_EDP)
+ {
lane_count = intel_dp->lane_count;
break;
- } else if (is_edp(intel_dp)) {
- lane_count = dev_priv->edp.lanes;
- bpp = dev_priv->edp.bpp;
- break;
}
}
@@ -711,7 +775,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
* the number of bytes_per_pixel post-LUT, which we always
* set up for 8-bits of R/G/B, or 3 bytes total.
*/
- intel_dp_compute_m_n(bpp, lane_count,
+ intel_dp_compute_m_n(intel_crtc->bpp, lane_count,
mode->clock, adjusted_mode->clock, &m_n);
if (HAS_PCH_SPLIT(dev)) {
@@ -731,27 +795,52 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
}
}
+static void ironlake_edp_pll_on(struct drm_encoder *encoder);
+static void ironlake_edp_pll_off(struct drm_encoder *encoder);
+
static void
intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct drm_crtc *crtc = intel_dp->base.base.crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
- intel_dp->DP |= intel_dp->color_range;
+ /* Turn on the eDP PLL if needed */
+ if (is_edp(intel_dp)) {
+ if (!is_pch_edp(intel_dp))
+ ironlake_edp_pll_on(encoder);
+ else
+ ironlake_edp_pll_off(encoder);
+ }
+
+ /*
+ * There are three kinds of DP registers:
+ *
+ * IBX PCH
+ * CPU
+ * CPT PCH
+ *
+ * IBX PCH and CPU are the same for almost everything,
+ * except that the CPU DP PLL is configured in this
+ * register
+ *
+ * CPT PCH is quite different, having many bits moved
+ * to the TRANS_DP_CTL register instead. That
+ * configuration happens (oddly) in ironlake_pch_enable
+ */
- if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
- intel_dp->DP |= DP_SYNC_HS_HIGH;
- if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
- intel_dp->DP |= DP_SYNC_VS_HIGH;
+ /* Preserve the BIOS-computed detected bit. This is
+ * supposed to be read-only.
+ */
+ intel_dp->DP = I915_READ(intel_dp->output_reg) & DP_DETECTED;
+ intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
- if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
- intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
- else
- intel_dp->DP |= DP_LINK_TRAIN_OFF;
+ /* Handle DP bits in common between all three register formats */
+
+ intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
switch (intel_dp->lane_count) {
case 1:
@@ -764,133 +853,279 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
intel_dp->DP |= DP_PORT_WIDTH_4;
break;
}
- if (intel_dp->has_audio)
+ if (intel_dp->has_audio) {
+ DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n",
+ pipe_name(intel_crtc->pipe));
intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE;
-
+ intel_write_eld(encoder, adjusted_mode);
+ }
memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
intel_dp->link_configuration[0] = intel_dp->link_bw;
intel_dp->link_configuration[1] = intel_dp->lane_count;
-
+ intel_dp->link_configuration[8] = DP_SET_ANSI_8B10B;
/*
* Check for DPCD version > 1.1 and enhanced framing support
*/
- if (intel_dp->dpcd[0] >= 0x11 && (intel_dp->dpcd[2] & DP_ENHANCED_FRAME_CAP)) {
+ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
+ (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) {
intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
- intel_dp->DP |= DP_ENHANCED_FRAMING;
}
- /* CPT DP's pipe select is decided in TRANS_DP_CTL */
- if (intel_crtc->pipe == 1 && !HAS_PCH_CPT(dev))
- intel_dp->DP |= DP_PIPEB_SELECT;
+ /* Split out the IBX/CPU vs CPT settings */
- if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) {
- /* don't miss out required setting for eDP */
- intel_dp->DP |= DP_PLL_ENABLE;
- if (adjusted_mode->clock < 200000)
- intel_dp->DP |= DP_PLL_FREQ_160MHZ;
- else
- intel_dp->DP |= DP_PLL_FREQ_270MHZ;
+ if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) {
+ intel_dp->DP |= intel_dp->color_range;
+
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ intel_dp->DP |= DP_SYNC_HS_HIGH;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ intel_dp->DP |= DP_SYNC_VS_HIGH;
+ intel_dp->DP |= DP_LINK_TRAIN_OFF;
+
+ if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN)
+ intel_dp->DP |= DP_ENHANCED_FRAMING;
+
+ if (intel_crtc->pipe == 1)
+ intel_dp->DP |= DP_PIPEB_SELECT;
+
+ if (is_cpu_edp(intel_dp)) {
+ /* don't miss out required setting for eDP */
+ intel_dp->DP |= DP_PLL_ENABLE;
+ if (adjusted_mode->clock < 200000)
+ intel_dp->DP |= DP_PLL_FREQ_160MHZ;
+ else
+ intel_dp->DP |= DP_PLL_FREQ_270MHZ;
+ }
+ } else {
+ intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
+ }
+}
+
+#define IDLE_ON_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK)
+#define IDLE_ON_VALUE (PP_ON | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_ON_IDLE)
+
+#define IDLE_OFF_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK)
+#define IDLE_OFF_VALUE (0 | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE)
+
+#define IDLE_CYCLE_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK)
+#define IDLE_CYCLE_VALUE (0 | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE)
+
+static void ironlake_wait_panel_status(struct intel_dp *intel_dp,
+ u32 mask,
+ u32 value)
+{
+ struct drm_device *dev = intel_dp->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ DRM_DEBUG_KMS("mask %08x value %08x status %08x control %08x\n",
+ mask, value,
+ I915_READ(PCH_PP_STATUS),
+ I915_READ(PCH_PP_CONTROL));
+
+ if (_wait_for((I915_READ(PCH_PP_STATUS) & mask) == value, 5000, 10)) {
+ DRM_ERROR("Panel status timeout: status %08x control %08x\n",
+ I915_READ(PCH_PP_STATUS),
+ I915_READ(PCH_PP_CONTROL));
}
}
+static void ironlake_wait_panel_on(struct intel_dp *intel_dp)
+{
+ DRM_DEBUG_KMS("Wait for panel power on\n");
+ ironlake_wait_panel_status(intel_dp, IDLE_ON_MASK, IDLE_ON_VALUE);
+}
+
+static void ironlake_wait_panel_off(struct intel_dp *intel_dp)
+{
+ DRM_DEBUG_KMS("Wait for panel power off time\n");
+ ironlake_wait_panel_status(intel_dp, IDLE_OFF_MASK, IDLE_OFF_VALUE);
+}
+
+static void ironlake_wait_panel_power_cycle(struct intel_dp *intel_dp)
+{
+ DRM_DEBUG_KMS("Wait for panel power cycle\n");
+ ironlake_wait_panel_status(intel_dp, IDLE_CYCLE_MASK, IDLE_CYCLE_VALUE);
+}
+
+
+/* Read the current pp_control value, unlocking the register if it
+ * is locked
+ */
+
+static u32 ironlake_get_pp_control(struct drm_i915_private *dev_priv)
+{
+ u32 control = I915_READ(PCH_PP_CONTROL);
+
+ control &= ~PANEL_UNLOCK_MASK;
+ control |= PANEL_UNLOCK_REGS;
+ return control;
+}
+
static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
- /*
- * If the panel wasn't on, make sure there's not a currently
- * active PP sequence before enabling AUX VDD.
- */
- if (!(I915_READ(PCH_PP_STATUS) & PP_ON))
- msleep(dev_priv->panel_t3);
+ if (!is_edp(intel_dp))
+ return;
+ DRM_DEBUG_KMS("Turn eDP VDD on\n");
+
+ WARN(intel_dp->want_panel_vdd,
+ "eDP VDD already requested on\n");
+
+ intel_dp->want_panel_vdd = true;
- pp = I915_READ(PCH_PP_CONTROL);
+ if (ironlake_edp_have_panel_vdd(intel_dp)) {
+ DRM_DEBUG_KMS("eDP VDD already on\n");
+ return;
+ }
+
+ if (!ironlake_edp_have_panel_power(intel_dp))
+ ironlake_wait_panel_power_cycle(intel_dp);
+
+ pp = ironlake_get_pp_control(dev_priv);
pp |= EDP_FORCE_VDD;
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
+ DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n",
+ I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL));
+
+ /*
+ * If the panel wasn't on, delay before accessing aux channel
+ */
+ if (!ironlake_edp_have_panel_power(intel_dp)) {
+ DRM_DEBUG_KMS("eDP was not running\n");
+ msleep(intel_dp->panel_power_up_delay);
+ }
}
-static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp)
+static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
- pp = I915_READ(PCH_PP_CONTROL);
- pp &= ~EDP_FORCE_VDD;
- I915_WRITE(PCH_PP_CONTROL, pp);
- POSTING_READ(PCH_PP_CONTROL);
+ if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) {
+ pp = ironlake_get_pp_control(dev_priv);
+ pp &= ~EDP_FORCE_VDD;
+ I915_WRITE(PCH_PP_CONTROL, pp);
+ POSTING_READ(PCH_PP_CONTROL);
+
+ /* Make sure sequencer is idle before allowing subsequent activity */
+ DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n",
+ I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL));
- /* Make sure sequencer is idle before allowing subsequent activity */
- msleep(dev_priv->panel_t12);
+ msleep(intel_dp->panel_power_down_delay);
+ }
}
-/* Returns true if the panel was already on when called */
-static bool ironlake_edp_panel_on (struct intel_dp *intel_dp)
+static void ironlake_panel_vdd_work(struct work_struct *__work)
+{
+ struct intel_dp *intel_dp = container_of(to_delayed_work(__work),
+ struct intel_dp, panel_vdd_work);
+ struct drm_device *dev = intel_dp->base.base.dev;
+
+ mutex_lock(&dev->mode_config.mutex);
+ ironlake_panel_vdd_off_sync(intel_dp);
+ mutex_unlock(&dev->mode_config.mutex);
+}
+
+static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
+{
+ if (!is_edp(intel_dp))
+ return;
+
+ DRM_DEBUG_KMS("Turn eDP VDD off %d\n", intel_dp->want_panel_vdd);
+ WARN(!intel_dp->want_panel_vdd, "eDP VDD not forced on");
+
+ intel_dp->want_panel_vdd = false;
+
+ if (sync) {
+ ironlake_panel_vdd_off_sync(intel_dp);
+ } else {
+ /*
+ * Queue the timer to fire a long
+ * time from now (relative to the power down delay)
+ * to keep the panel power up across a sequence of operations
+ */
+ schedule_delayed_work(&intel_dp->panel_vdd_work,
+ msecs_to_jiffies(intel_dp->panel_power_cycle_delay * 5));
+ }
+}
+
+static void ironlake_edp_panel_on(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_STATE_ON_IDLE;
+ u32 pp;
- if (I915_READ(PCH_PP_STATUS) & PP_ON)
- return true;
+ if (!is_edp(intel_dp))
+ return;
- pp = I915_READ(PCH_PP_CONTROL);
+ DRM_DEBUG_KMS("Turn eDP power on\n");
- /* ILK workaround: disable reset around power sequence */
- pp &= ~PANEL_POWER_RESET;
- I915_WRITE(PCH_PP_CONTROL, pp);
- POSTING_READ(PCH_PP_CONTROL);
+ if (ironlake_edp_have_panel_power(intel_dp)) {
+ DRM_DEBUG_KMS("eDP power already on\n");
+ return;
+ }
- pp |= PANEL_UNLOCK_REGS | POWER_TARGET_ON;
- I915_WRITE(PCH_PP_CONTROL, pp);
- POSTING_READ(PCH_PP_CONTROL);
+ ironlake_wait_panel_power_cycle(intel_dp);
- if (wait_for((I915_READ(PCH_PP_STATUS) & idle_on_mask) == idle_on_mask,
- 5000))
- DRM_ERROR("panel on wait timed out: 0x%08x\n",
- I915_READ(PCH_PP_STATUS));
+ pp = ironlake_get_pp_control(dev_priv);
+ if (IS_GEN5(dev)) {
+ /* ILK workaround: disable reset around power sequence */
+ pp &= ~PANEL_POWER_RESET;
+ I915_WRITE(PCH_PP_CONTROL, pp);
+ POSTING_READ(PCH_PP_CONTROL);
+ }
+
+ pp |= POWER_TARGET_ON;
+ if (!IS_GEN5(dev))
+ pp |= PANEL_POWER_RESET;
- pp |= PANEL_POWER_RESET; /* restore panel reset bit */
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
- return false;
+ ironlake_wait_panel_on(intel_dp);
+
+ if (IS_GEN5(dev)) {
+ pp |= PANEL_POWER_RESET; /* restore panel reset bit */
+ I915_WRITE(PCH_PP_CONTROL, pp);
+ POSTING_READ(PCH_PP_CONTROL);
+ }
}
-static void ironlake_edp_panel_off (struct drm_device *dev)
+static void ironlake_edp_panel_off(struct intel_dp *intel_dp)
{
+ struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 pp, idle_off_mask = PP_ON | PP_SEQUENCE_MASK |
- PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK;
-
- pp = I915_READ(PCH_PP_CONTROL);
+ u32 pp;
- /* ILK workaround: disable reset around power sequence */
- pp &= ~PANEL_POWER_RESET;
- I915_WRITE(PCH_PP_CONTROL, pp);
- POSTING_READ(PCH_PP_CONTROL);
+ if (!is_edp(intel_dp))
+ return;
- pp &= ~POWER_TARGET_ON;
- I915_WRITE(PCH_PP_CONTROL, pp);
- POSTING_READ(PCH_PP_CONTROL);
+ DRM_DEBUG_KMS("Turn eDP power off\n");
- if (wait_for((I915_READ(PCH_PP_STATUS) & idle_off_mask) == 0, 5000))
- DRM_ERROR("panel off wait timed out: 0x%08x\n",
- I915_READ(PCH_PP_STATUS));
+ WARN(intel_dp->want_panel_vdd, "Cannot turn power off while VDD is on\n");
- pp |= PANEL_POWER_RESET; /* restore panel reset bit */
+ pp = ironlake_get_pp_control(dev_priv);
+ pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD | PANEL_POWER_RESET | EDP_BLC_ENABLE);
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
+
+ ironlake_wait_panel_off(intel_dp);
}
-static void ironlake_edp_backlight_on (struct drm_device *dev)
+static void ironlake_edp_backlight_on(struct intel_dp *intel_dp)
{
+ struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
+ if (!is_edp(intel_dp))
+ return;
+
DRM_DEBUG_KMS("\n");
/*
* If we enable the backlight right away following a panel power
@@ -898,21 +1133,28 @@ static void ironlake_edp_backlight_on (struct drm_device *dev)
* link. So delay a bit to make sure the image is solid before
* allowing it to appear.
*/
- msleep(300);
- pp = I915_READ(PCH_PP_CONTROL);
+ msleep(intel_dp->backlight_on_delay);
+ pp = ironlake_get_pp_control(dev_priv);
pp |= EDP_BLC_ENABLE;
I915_WRITE(PCH_PP_CONTROL, pp);
+ POSTING_READ(PCH_PP_CONTROL);
}
-static void ironlake_edp_backlight_off (struct drm_device *dev)
+static void ironlake_edp_backlight_off(struct intel_dp *intel_dp)
{
+ struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
+ if (!is_edp(intel_dp))
+ return;
+
DRM_DEBUG_KMS("\n");
- pp = I915_READ(PCH_PP_CONTROL);
+ pp = ironlake_get_pp_control(dev_priv);
pp &= ~EDP_BLC_ENABLE;
I915_WRITE(PCH_PP_CONTROL, pp);
+ POSTING_READ(PCH_PP_CONTROL);
+ msleep(intel_dp->backlight_off_delay);
}
static void ironlake_edp_pll_on(struct drm_encoder *encoder)
@@ -942,41 +1184,72 @@ static void ironlake_edp_pll_off(struct drm_encoder *encoder)
udelay(200);
}
+/* If the sink supports it, try to set the power state appropriately */
+static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
+{
+ int ret, i;
+
+ /* Should have a valid DPCD by this point */
+ if (intel_dp->dpcd[DP_DPCD_REV] < 0x11)
+ return;
+
+ if (mode != DRM_MODE_DPMS_ON) {
+ ret = intel_dp_aux_native_write_1(intel_dp, DP_SET_POWER,
+ DP_SET_POWER_D3);
+ if (ret != 1)
+ DRM_DEBUG_DRIVER("failed to write sink power state\n");
+ } else {
+ /*
+ * When turning on, we need to retry for 1ms to give the sink
+ * time to wake up.
+ */
+ for (i = 0; i < 3; i++) {
+ ret = intel_dp_aux_native_write_1(intel_dp,
+ DP_SET_POWER,
+ DP_SET_POWER_D0);
+ if (ret == 1)
+ break;
+ msleep(1);
+ }
+ }
+}
+
static void intel_dp_prepare(struct drm_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
- struct drm_device *dev = encoder->dev;
- if (is_edp(intel_dp)) {
- ironlake_edp_backlight_off(dev);
- ironlake_edp_panel_off(dev);
- if (!is_pch_edp(intel_dp))
- ironlake_edp_pll_on(encoder);
- else
- ironlake_edp_pll_off(encoder);
- }
+ ironlake_edp_backlight_off(intel_dp);
+ ironlake_edp_panel_off(intel_dp);
+
+ /* Wake up the sink first */
+ ironlake_edp_panel_vdd_on(intel_dp);
+ intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
intel_dp_link_down(intel_dp);
+ ironlake_edp_panel_vdd_off(intel_dp, false);
+
+ /* Make sure the panel is off before trying to
+ * change the mode
+ */
}
static void intel_dp_commit(struct drm_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct drm_device *dev = encoder->dev;
+ struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc);
- if (is_edp(intel_dp))
- ironlake_edp_panel_vdd_on(intel_dp);
-
+ ironlake_edp_panel_vdd_on(intel_dp);
+ intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
intel_dp_start_link_train(intel_dp);
-
- if (is_edp(intel_dp)) {
- ironlake_edp_panel_on(intel_dp);
- ironlake_edp_panel_vdd_off(intel_dp);
- }
-
+ ironlake_edp_panel_on(intel_dp);
+ ironlake_edp_panel_vdd_off(intel_dp, true);
intel_dp_complete_link_train(intel_dp);
+ ironlake_edp_backlight_on(intel_dp);
+
+ intel_dp->dpms_mode = DRM_MODE_DPMS_ON;
- if (is_edp(intel_dp))
- ironlake_edp_backlight_on(dev);
+ if (HAS_PCH_CPT(dev))
+ intel_cpt_verify_modeset(dev, intel_crtc->pipe);
}
static void
@@ -988,45 +1261,70 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
uint32_t dp_reg = I915_READ(intel_dp->output_reg);
if (mode != DRM_MODE_DPMS_ON) {
- if (is_edp(intel_dp))
- ironlake_edp_backlight_off(dev);
+ ironlake_edp_backlight_off(intel_dp);
+ ironlake_edp_panel_off(intel_dp);
+
+ ironlake_edp_panel_vdd_on(intel_dp);
+ intel_dp_sink_dpms(intel_dp, mode);
intel_dp_link_down(intel_dp);
- if (is_edp(intel_dp))
- ironlake_edp_panel_off(dev);
- if (is_edp(intel_dp) && !is_pch_edp(intel_dp))
+ ironlake_edp_panel_vdd_off(intel_dp, false);
+
+ if (is_cpu_edp(intel_dp))
ironlake_edp_pll_off(encoder);
} else {
- if (is_edp(intel_dp))
- ironlake_edp_panel_vdd_on(intel_dp);
+ if (is_cpu_edp(intel_dp))
+ ironlake_edp_pll_on(encoder);
+
+ ironlake_edp_panel_vdd_on(intel_dp);
+ intel_dp_sink_dpms(intel_dp, mode);
if (!(dp_reg & DP_PORT_EN)) {
intel_dp_start_link_train(intel_dp);
- if (is_edp(intel_dp)) {
- ironlake_edp_panel_on(intel_dp);
- ironlake_edp_panel_vdd_off(intel_dp);
- }
+ ironlake_edp_panel_on(intel_dp);
+ ironlake_edp_panel_vdd_off(intel_dp, true);
intel_dp_complete_link_train(intel_dp);
- }
- if (is_edp(intel_dp))
- ironlake_edp_backlight_on(dev);
+ } else
+ ironlake_edp_panel_vdd_off(intel_dp, false);
+ ironlake_edp_backlight_on(intel_dp);
}
intel_dp->dpms_mode = mode;
}
/*
+ * Native read with retry for link status and receiver capability reads for
+ * cases where the sink may still be asleep.
+ */
+static bool
+intel_dp_aux_native_read_retry(struct intel_dp *intel_dp, uint16_t address,
+ uint8_t *recv, int recv_bytes)
+{
+ int ret, i;
+
+ /*
+ * Sinks are *supposed* to come up within 1ms from an off state,
+ * but we're also supposed to retry 3 times per the spec.
+ */
+ for (i = 0; i < 3; i++) {
+ ret = intel_dp_aux_native_read(intel_dp, address, recv,
+ recv_bytes);
+ if (ret == recv_bytes)
+ return true;
+ msleep(1);
+ }
+
+ return false;
+}
+
+/*
* Fetch AUX CH registers 0x202 - 0x207 which contain
* link status information
*/
static bool
-intel_dp_get_link_status(struct intel_dp *intel_dp)
+intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
{
- int ret;
-
- ret = intel_dp_aux_native_read(intel_dp,
- DP_LANE0_1_STATUS,
- intel_dp->link_status, DP_LINK_STATUS_SIZE);
- if (ret != DP_LINK_STATUS_SIZE)
- return false;
- return true;
+ return intel_dp_aux_native_read_retry(intel_dp,
+ DP_LANE0_1_STATUS,
+ link_status,
+ DP_LINK_STATUS_SIZE);
}
static uint8_t
@@ -1037,27 +1335,25 @@ intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
}
static uint8_t
-intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
+intel_get_adjust_request_voltage(uint8_t adjust_request[2],
int lane)
{
- int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
int s = ((lane & 1) ?
DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
- uint8_t l = intel_dp_link_status(link_status, i);
+ uint8_t l = adjust_request[lane>>1];
return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
}
static uint8_t
-intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
+intel_get_adjust_request_pre_emphasis(uint8_t adjust_request[2],
int lane)
{
- int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
int s = ((lane & 1) ?
DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
- uint8_t l = intel_dp_link_status(link_status, i);
+ uint8_t l = adjust_request[lane>>1];
return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
}
@@ -1080,6 +1376,7 @@ static char *link_train_names[] = {
* a maximum voltage of 800mV and a maximum pre-emphasis of 6dB
*/
#define I830_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_800
+#define I830_DP_VOLTAGE_MAX_CPT DP_TRAIN_VOLTAGE_SWING_1200
static uint8_t
intel_dp_pre_emphasis_max(uint8_t voltage_swing)
@@ -1098,15 +1395,18 @@ intel_dp_pre_emphasis_max(uint8_t voltage_swing)
}
static void
-intel_get_adjust_train(struct intel_dp *intel_dp)
+intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
{
+ struct drm_device *dev = intel_dp->base.base.dev;
uint8_t v = 0;
uint8_t p = 0;
int lane;
+ uint8_t *adjust_request = link_status + (DP_ADJUST_REQUEST_LANE0_1 - DP_LANE0_1_STATUS);
+ int voltage_max;
for (lane = 0; lane < intel_dp->lane_count; lane++) {
- uint8_t this_v = intel_get_adjust_request_voltage(intel_dp->link_status, lane);
- uint8_t this_p = intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane);
+ uint8_t this_v = intel_get_adjust_request_voltage(adjust_request, lane);
+ uint8_t this_p = intel_get_adjust_request_pre_emphasis(adjust_request, lane);
if (this_v > v)
v = this_v;
@@ -1114,8 +1414,12 @@ intel_get_adjust_train(struct intel_dp *intel_dp)
p = this_p;
}
- if (v >= I830_DP_VOLTAGE_MAX)
- v = I830_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
+ if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp))
+ voltage_max = I830_DP_VOLTAGE_MAX_CPT;
+ else
+ voltage_max = I830_DP_VOLTAGE_MAX;
+ if (v >= voltage_max)
+ v = voltage_max | DP_TRAIN_MAX_SWING_REACHED;
if (p >= intel_dp_pre_emphasis_max(v))
p = intel_dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
@@ -1125,7 +1429,7 @@ intel_get_adjust_train(struct intel_dp *intel_dp)
}
static uint32_t
-intel_dp_signal_levels(uint8_t train_set, int lane_count)
+intel_dp_signal_levels(uint8_t train_set)
{
uint32_t signal_levels = 0;
@@ -1194,9 +1498,8 @@ static uint8_t
intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
int lane)
{
- int i = DP_LANE0_1_STATUS + (lane >> 1);
int s = (lane & 1) * 4;
- uint8_t l = intel_dp_link_status(link_status, i);
+ uint8_t l = link_status[lane>>1];
return (l >> s) & 0xf;
}
@@ -1221,18 +1524,18 @@ intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count
DP_LANE_CHANNEL_EQ_DONE|\
DP_LANE_SYMBOL_LOCKED)
static bool
-intel_channel_eq_ok(struct intel_dp *intel_dp)
+intel_channel_eq_ok(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
{
uint8_t lane_align;
uint8_t lane_status;
int lane;
- lane_align = intel_dp_link_status(intel_dp->link_status,
+ lane_align = intel_dp_link_status(link_status,
DP_LANE_ALIGN_STATUS_UPDATED);
if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
return false;
for (lane = 0; lane < intel_dp->lane_count; lane++) {
- lane_status = intel_get_lane_status(intel_dp->link_status, lane);
+ lane_status = intel_get_lane_status(link_status, lane);
if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS)
return false;
}
@@ -1257,8 +1560,9 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
ret = intel_dp_aux_native_write(intel_dp,
DP_TRAINING_LANE0_SET,
- intel_dp->train_set, 4);
- if (ret != 4)
+ intel_dp->train_set,
+ intel_dp->lane_count);
+ if (ret != intel_dp->lane_count)
return false;
return true;
@@ -1274,14 +1578,20 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
int i;
uint8_t voltage;
bool clock_recovery = false;
- int tries;
+ int voltage_tries, loop_tries;
u32 reg;
uint32_t DP = intel_dp->DP;
- /* Enable output, wait for it to become active */
- I915_WRITE(intel_dp->output_reg, intel_dp->DP);
- POSTING_READ(intel_dp->output_reg);
- intel_wait_for_vblank(dev, intel_crtc->pipe);
+ /*
+ * On CPT we have to enable the port in training pattern 1, which
+ * will happen below in intel_dp_set_link_train. Otherwise, enable
+ * the port and wait for it to become active.
+ */
+ if (!HAS_PCH_CPT(dev)) {
+ I915_WRITE(intel_dp->output_reg, intel_dp->DP);
+ POSTING_READ(intel_dp->output_reg);
+ intel_wait_for_vblank(dev, intel_crtc->pipe);
+ }
/* Write the link configuration data */
intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
@@ -1289,40 +1599,48 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
DP_LINK_CONFIGURATION_SIZE);
DP |= DP_PORT_EN;
- if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
+ if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp))
DP &= ~DP_LINK_TRAIN_MASK_CPT;
else
DP &= ~DP_LINK_TRAIN_MASK;
memset(intel_dp->train_set, 0, 4);
voltage = 0xff;
- tries = 0;
+ voltage_tries = 0;
+ loop_tries = 0;
clock_recovery = false;
for (;;) {
/* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
+ uint8_t link_status[DP_LINK_STATUS_SIZE];
uint32_t signal_levels;
- if (IS_GEN6(dev) && is_edp(intel_dp)) {
+
+ if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) {
signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
} else {
- signal_levels = intel_dp_signal_levels(intel_dp->train_set[0], intel_dp->lane_count);
+ signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]);
+ DRM_DEBUG_KMS("training pattern 1 signal levels %08x\n", signal_levels);
DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
}
- if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
+ if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp))
reg = DP | DP_LINK_TRAIN_PAT_1_CPT;
else
reg = DP | DP_LINK_TRAIN_PAT_1;
if (!intel_dp_set_link_train(intel_dp, reg,
- DP_TRAINING_PATTERN_1))
+ DP_TRAINING_PATTERN_1 |
+ DP_LINK_SCRAMBLING_DISABLE))
break;
/* Set training pattern 1 */
udelay(100);
- if (!intel_dp_get_link_status(intel_dp))
+ if (!intel_dp_get_link_status(intel_dp, link_status)) {
+ DRM_ERROR("failed to get link status\n");
break;
+ }
- if (intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
+ if (intel_clock_recovery_ok(link_status, intel_dp->lane_count)) {
+ DRM_DEBUG_KMS("clock recovery OK\n");
clock_recovery = true;
break;
}
@@ -1331,20 +1649,30 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
for (i = 0; i < intel_dp->lane_count; i++)
if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
break;
- if (i == intel_dp->lane_count)
- break;
+ if (i == intel_dp->lane_count) {
+ ++loop_tries;
+ if (loop_tries == 5) {
+ DRM_DEBUG_KMS("too many full retries, give up\n");
+ break;
+ }
+ memset(intel_dp->train_set, 0, 4);
+ voltage_tries = 0;
+ continue;
+ }
/* Check to see if we've tried the same voltage 5 times */
if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
- ++tries;
- if (tries == 5)
+ ++voltage_tries;
+ if (voltage_tries == 5) {
+ DRM_DEBUG_KMS("too many voltage retries, give up\n");
break;
+ }
} else
- tries = 0;
+ voltage_tries = 0;
voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
/* Compute new intel_dp->train_set as requested by target */
- intel_get_adjust_train(intel_dp);
+ intel_get_adjust_train(intel_dp, link_status);
}
intel_dp->DP = DP;
@@ -1367,6 +1695,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
for (;;) {
/* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
uint32_t signal_levels;
+ uint8_t link_status[DP_LINK_STATUS_SIZE];
if (cr_tries > 5) {
DRM_ERROR("failed to train DP, aborting\n");
@@ -1374,36 +1703,37 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
break;
}
- if (IS_GEN6(dev) && is_edp(intel_dp)) {
+ if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) {
signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
} else {
- signal_levels = intel_dp_signal_levels(intel_dp->train_set[0], intel_dp->lane_count);
+ signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]);
DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
}
- if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
+ if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp))
reg = DP | DP_LINK_TRAIN_PAT_2_CPT;
else
reg = DP | DP_LINK_TRAIN_PAT_2;
/* channel eq pattern */
if (!intel_dp_set_link_train(intel_dp, reg,
- DP_TRAINING_PATTERN_2))
+ DP_TRAINING_PATTERN_2 |
+ DP_LINK_SCRAMBLING_DISABLE))
break;
udelay(400);
- if (!intel_dp_get_link_status(intel_dp))
+ if (!intel_dp_get_link_status(intel_dp, link_status))
break;
/* Make sure clock is still ok */
- if (!intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
+ if (!intel_clock_recovery_ok(link_status, intel_dp->lane_count)) {
intel_dp_start_link_train(intel_dp);
cr_tries++;
continue;
}
- if (intel_channel_eq_ok(intel_dp)) {
+ if (intel_channel_eq_ok(intel_dp, link_status)) {
channel_eq = true;
break;
}
@@ -1418,11 +1748,11 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
}
/* Compute new intel_dp->train_set as requested by target */
- intel_get_adjust_train(intel_dp);
+ intel_get_adjust_train(intel_dp, link_status);
++tries;
}
- if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
+ if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp))
reg = DP | DP_LINK_TRAIN_OFF_CPT;
else
reg = DP | DP_LINK_TRAIN_OFF;
@@ -1452,7 +1782,7 @@ intel_dp_link_down(struct intel_dp *intel_dp)
udelay(100);
}
- if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) {
+ if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp)) {
DP &= ~DP_LINK_TRAIN_MASK_CPT;
I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT);
} else {
@@ -1463,8 +1793,12 @@ intel_dp_link_down(struct intel_dp *intel_dp)
msleep(17);
- if (is_edp(intel_dp))
- DP |= DP_LINK_TRAIN_OFF;
+ if (is_edp(intel_dp)) {
+ if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp))
+ DP |= DP_LINK_TRAIN_OFF_CPT;
+ else
+ DP |= DP_LINK_TRAIN_OFF;
+ }
if (!HAS_PCH_CPT(dev) &&
I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) {
@@ -1501,6 +1835,40 @@ intel_dp_link_down(struct intel_dp *intel_dp)
I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN);
POSTING_READ(intel_dp->output_reg);
+ msleep(intel_dp->panel_power_down_delay);
+}
+
+static bool
+intel_dp_get_dpcd(struct intel_dp *intel_dp)
+{
+ if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd,
+ sizeof(intel_dp->dpcd)) &&
+ (intel_dp->dpcd[DP_DPCD_REV] != 0)) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector)
+{
+ int ret;
+
+ ret = intel_dp_aux_native_read_retry(intel_dp,
+ DP_DEVICE_SERVICE_IRQ_VECTOR,
+ sink_irq_vector, 1);
+ if (!ret)
+ return false;
+
+ return true;
+}
+
+static void
+intel_dp_handle_test_request(struct intel_dp *intel_dp)
+{
+ /* NAK by default */
+ intel_dp_aux_native_write_1(intel_dp, DP_TEST_RESPONSE, DP_TEST_ACK);
}
/*
@@ -1515,21 +1883,58 @@ intel_dp_link_down(struct intel_dp *intel_dp)
static void
intel_dp_check_link_status(struct intel_dp *intel_dp)
{
+ u8 sink_irq_vector;
+ u8 link_status[DP_LINK_STATUS_SIZE];
+
+ if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON)
+ return;
+
if (!intel_dp->base.base.crtc)
return;
- if (!intel_dp_get_link_status(intel_dp)) {
+ /* Try to read receiver status if the link appears to be up */
+ if (!intel_dp_get_link_status(intel_dp, link_status)) {
intel_dp_link_down(intel_dp);
return;
}
- if (!intel_channel_eq_ok(intel_dp)) {
+ /* Now read the DPCD to see if it's actually running */
+ if (!intel_dp_get_dpcd(intel_dp)) {
+ intel_dp_link_down(intel_dp);
+ return;
+ }
+
+ /* Try to read the source of the interrupt */
+ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
+ intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) {
+ /* Clear interrupt source */
+ intel_dp_aux_native_write_1(intel_dp,
+ DP_DEVICE_SERVICE_IRQ_VECTOR,
+ sink_irq_vector);
+
+ if (sink_irq_vector & DP_AUTOMATED_TEST_REQUEST)
+ intel_dp_handle_test_request(intel_dp);
+ if (sink_irq_vector & (DP_CP_IRQ | DP_SINK_SPECIFIC_IRQ))
+ DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n");
+ }
+
+ if (!intel_channel_eq_ok(intel_dp, link_status)) {
+ DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n",
+ drm_get_encoder_name(&intel_dp->base.base));
intel_dp_start_link_train(intel_dp);
intel_dp_complete_link_train(intel_dp);
}
}
static enum drm_connector_status
+intel_dp_detect_dpcd(struct intel_dp *intel_dp)
+{
+ if (intel_dp_get_dpcd(intel_dp))
+ return connector_status_connected;
+ return connector_status_disconnected;
+}
+
+static enum drm_connector_status
ironlake_dp_detect(struct intel_dp *intel_dp)
{
enum drm_connector_status status;
@@ -1542,17 +1947,7 @@ ironlake_dp_detect(struct intel_dp *intel_dp)
return status;
}
- status = connector_status_disconnected;
- if (intel_dp_aux_native_read(intel_dp,
- 0x000, intel_dp->dpcd,
- sizeof (intel_dp->dpcd))
- == sizeof(intel_dp->dpcd)) {
- if (intel_dp->dpcd[0] != 0)
- status = connector_status_connected;
- }
- DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0],
- intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]);
- return status;
+ return intel_dp_detect_dpcd(intel_dp);
}
static enum drm_connector_status
@@ -1560,7 +1955,6 @@ g4x_dp_detect(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- enum drm_connector_status status;
uint32_t temp, bit;
switch (intel_dp->output_reg) {
@@ -1582,17 +1976,34 @@ g4x_dp_detect(struct intel_dp *intel_dp)
if ((temp & bit) == 0)
return connector_status_disconnected;
- status = connector_status_disconnected;
- if (intel_dp_aux_native_read(intel_dp, 0x000, intel_dp->dpcd,
- sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd))
- {
- if (intel_dp->dpcd[0] != 0)
- status = connector_status_connected;
- }
+ return intel_dp_detect_dpcd(intel_dp);
+}
+
+static struct edid *
+intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
+{
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct edid *edid;
- return status;
+ ironlake_edp_panel_vdd_on(intel_dp);
+ edid = drm_get_edid(connector, adapter);
+ ironlake_edp_panel_vdd_off(intel_dp, false);
+ return edid;
}
+static int
+intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *adapter)
+{
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ int ret;
+
+ ironlake_edp_panel_vdd_on(intel_dp);
+ ret = intel_ddc_get_modes(connector, adapter);
+ ironlake_edp_panel_vdd_off(intel_dp, false);
+ return ret;
+}
+
+
/**
* Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
*
@@ -1613,13 +2024,19 @@ intel_dp_detect(struct drm_connector *connector, bool force)
status = ironlake_dp_detect(intel_dp);
else
status = g4x_dp_detect(intel_dp);
+
+ DRM_DEBUG_KMS("DPCD: %02hx%02hx%02hx%02hx%02hx%02hx%02hx%02hx\n",
+ intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2],
+ intel_dp->dpcd[3], intel_dp->dpcd[4], intel_dp->dpcd[5],
+ intel_dp->dpcd[6], intel_dp->dpcd[7]);
+
if (status != connector_status_connected)
return status;
if (intel_dp->force_audio) {
intel_dp->has_audio = intel_dp->force_audio > 0;
} else {
- edid = drm_get_edid(connector, &intel_dp->adapter);
+ edid = intel_dp_get_edid(connector, &intel_dp->adapter);
if (edid) {
intel_dp->has_audio = drm_detect_monitor_audio(edid);
connector->display_info.raw_edid = NULL;
@@ -1640,28 +2057,36 @@ static int intel_dp_get_modes(struct drm_connector *connector)
/* We should parse the EDID data and find out if it has an audio sink
*/
- ret = intel_ddc_get_modes(connector, &intel_dp->adapter);
+ ret = intel_dp_get_edid_modes(connector, &intel_dp->adapter);
if (ret) {
- if (is_edp(intel_dp) && !dev_priv->panel_fixed_mode) {
+ if (is_edp(intel_dp) && !intel_dp->panel_fixed_mode) {
struct drm_display_mode *newmode;
list_for_each_entry(newmode, &connector->probed_modes,
head) {
- if (newmode->type & DRM_MODE_TYPE_PREFERRED) {
- dev_priv->panel_fixed_mode =
+ if ((newmode->type & DRM_MODE_TYPE_PREFERRED)) {
+ intel_dp->panel_fixed_mode =
drm_mode_duplicate(dev, newmode);
break;
}
}
}
-
return ret;
}
/* if eDP has no EDID, try to use fixed panel mode from VBT */
if (is_edp(intel_dp)) {
- if (dev_priv->panel_fixed_mode != NULL) {
+ /* initialize panel mode from VBT if available for eDP */
+ if (intel_dp->panel_fixed_mode == NULL && dev_priv->lfp_lvds_vbt_mode != NULL) {
+ intel_dp->panel_fixed_mode =
+ drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
+ if (intel_dp->panel_fixed_mode) {
+ intel_dp->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ }
+ }
+ if (intel_dp->panel_fixed_mode) {
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
+ mode = drm_mode_duplicate(dev, intel_dp->panel_fixed_mode);
drm_mode_probed_add(connector, mode);
return 1;
}
@@ -1676,7 +2101,7 @@ intel_dp_detect_audio(struct drm_connector *connector)
struct edid *edid;
bool has_audio = false;
- edid = drm_get_edid(connector, &intel_dp->adapter);
+ edid = intel_dp_get_edid(connector, &intel_dp->adapter);
if (edid) {
has_audio = drm_detect_monitor_audio(edid);
@@ -1743,8 +2168,13 @@ done:
}
static void
-intel_dp_destroy (struct drm_connector *connector)
+intel_dp_destroy(struct drm_connector *connector)
{
+ struct drm_device *dev = connector->dev;
+
+ if (intel_dpd_is_edp(dev))
+ intel_panel_destroy_backlight(dev);
+
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(connector);
@@ -1756,6 +2186,10 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
i2c_del_adapter(&intel_dp->adapter);
drm_encoder_cleanup(encoder);
+ if (is_edp(intel_dp)) {
+ cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
+ ironlake_panel_vdd_off_sync(intel_dp);
+ }
kfree(intel_dp);
}
@@ -1790,13 +2224,12 @@ intel_dp_hot_plug(struct intel_encoder *intel_encoder)
{
struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base);
- if (intel_dp->dpms_mode == DRM_MODE_DPMS_ON)
- intel_dp_check_link_status(intel_dp);
+ intel_dp_check_link_status(intel_dp);
}
/* Return which DP Port should be selected for Transcoder DP control */
int
-intel_trans_dp_port_sel (struct drm_crtc *crtc)
+intel_trans_dp_port_sel(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_mode_config *mode_config = &dev->mode_config;
@@ -1809,7 +2242,8 @@ intel_trans_dp_port_sel (struct drm_crtc *crtc)
continue;
intel_dp = enc_to_intel_dp(encoder);
- if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT)
+ if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT ||
+ intel_dp->base.type == INTEL_OUTPUT_EDP)
return intel_dp->output_reg;
}
@@ -1893,10 +2327,13 @@ intel_dp_init(struct drm_device *dev, int output_reg)
else if (output_reg == DP_D || output_reg == PCH_DP_D)
intel_encoder->clone_mask = (1 << INTEL_DP_D_CLONE_BIT);
- if (is_edp(intel_dp))
+ if (is_edp(intel_dp)) {
intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT);
+ INIT_DELAYED_WORK(&intel_dp->panel_vdd_work,
+ ironlake_panel_vdd_work);
+ }
- intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
+ intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
connector->interlace_allowed = true;
connector->doublescan_allowed = 0;
@@ -1932,30 +2369,63 @@ intel_dp_init(struct drm_device *dev, int output_reg)
break;
}
- intel_dp_i2c_init(intel_dp, intel_connector, name);
-
/* Cache some DPCD data in the eDP case */
if (is_edp(intel_dp)) {
- int ret;
- u32 pp_on, pp_div;
+ bool ret;
+ struct edp_power_seq cur, vbt;
+ u32 pp_on, pp_off, pp_div;
pp_on = I915_READ(PCH_PP_ON_DELAYS);
+ pp_off = I915_READ(PCH_PP_OFF_DELAYS);
pp_div = I915_READ(PCH_PP_DIVISOR);
- /* Get T3 & T12 values (note: VESA not bspec terminology) */
- dev_priv->panel_t3 = (pp_on & 0x1fff0000) >> 16;
- dev_priv->panel_t3 /= 10; /* t3 in 100us units */
- dev_priv->panel_t12 = pp_div & 0xf;
- dev_priv->panel_t12 *= 100; /* t12 in 100ms units */
+ /* Pull timing values out of registers */
+ cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >>
+ PANEL_POWER_UP_DELAY_SHIFT;
+
+ cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >>
+ PANEL_LIGHT_ON_DELAY_SHIFT;
+
+ cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >>
+ PANEL_LIGHT_OFF_DELAY_SHIFT;
+
+ cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >>
+ PANEL_POWER_DOWN_DELAY_SHIFT;
+
+ cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >>
+ PANEL_POWER_CYCLE_DELAY_SHIFT) * 1000;
+
+ DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
+ cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12);
+
+ vbt = dev_priv->edp.pps;
+
+ DRM_DEBUG_KMS("vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
+ vbt.t1_t3, vbt.t8, vbt.t9, vbt.t10, vbt.t11_t12);
+
+#define get_delay(field) ((max(cur.field, vbt.field) + 9) / 10)
+
+ intel_dp->panel_power_up_delay = get_delay(t1_t3);
+ intel_dp->backlight_on_delay = get_delay(t8);
+ intel_dp->backlight_off_delay = get_delay(t9);
+ intel_dp->panel_power_down_delay = get_delay(t10);
+ intel_dp->panel_power_cycle_delay = get_delay(t11_t12);
+
+ DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n",
+ intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay,
+ intel_dp->panel_power_cycle_delay);
+
+ DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n",
+ intel_dp->backlight_on_delay, intel_dp->backlight_off_delay);
ironlake_edp_panel_vdd_on(intel_dp);
- ret = intel_dp_aux_native_read(intel_dp, DP_DPCD_REV,
- intel_dp->dpcd,
- sizeof(intel_dp->dpcd));
- ironlake_edp_panel_vdd_off(intel_dp);
- if (ret == sizeof(intel_dp->dpcd)) {
- if (intel_dp->dpcd[0] >= 0x11)
- dev_priv->no_aux_handshake = intel_dp->dpcd[3] &
+ ret = intel_dp_get_dpcd(intel_dp);
+ ironlake_edp_panel_vdd_off(intel_dp, false);
+
+ if (ret) {
+ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
+ dev_priv->no_aux_handshake =
+ intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
DP_NO_AUX_HANDSHAKE_LINK_TRAINING;
} else {
/* if this fails, presume the device is a ghost */
@@ -1966,18 +2436,13 @@ intel_dp_init(struct drm_device *dev, int output_reg)
}
}
+ intel_dp_i2c_init(intel_dp, intel_connector, name);
+
intel_encoder->hot_plug = intel_dp_hot_plug;
if (is_edp(intel_dp)) {
- /* initialize panel mode from VBT if available for eDP */
- if (dev_priv->lfp_lvds_vbt_mode) {
- dev_priv->panel_fixed_mode =
- drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
- if (dev_priv->panel_fixed_mode) {
- dev_priv->panel_fixed_mode->type |=
- DRM_MODE_TYPE_PREFERRED;
- }
- }
+ dev_priv->int_edp_connector = connector;
+ intel_panel_setup_backlight(dev);
}
intel_dp_add_properties(intel_dp, connector);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 9ffa61eb4d7e..bd9a604b73da 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -34,7 +34,7 @@
#define _wait_for(COND, MS, W) ({ \
unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
int ret__ = 0; \
- while (! (COND)) { \
+ while (!(COND)) { \
if (time_after(jiffies, timeout__)) { \
ret__ = -ETIMEDOUT; \
break; \
@@ -49,10 +49,10 @@
#define MSLEEP(x) do { \
if (in_dbg_master()) \
- mdelay(x); \
+ mdelay(x); \
else \
msleep(x); \
-} while(0)
+} while (0)
#define KHz(x) (1000*x)
#define MHz(x) KHz(1000*x)
@@ -170,6 +170,10 @@ struct intel_crtc {
int16_t cursor_x, cursor_y;
int16_t cursor_width, cursor_height;
bool cursor_visible;
+ unsigned int bpp;
+
+ bool no_pll; /* tertiary pipe for IVB */
+ bool use_pll_a;
};
#define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
@@ -177,10 +181,28 @@ struct intel_crtc {
#define to_intel_encoder(x) container_of(x, struct intel_encoder, base)
#define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)
+#define DIP_HEADER_SIZE 5
+
#define DIP_TYPE_AVI 0x82
#define DIP_VERSION_AVI 0x2
#define DIP_LEN_AVI 13
+#define DIP_TYPE_SPD 0x83
+#define DIP_VERSION_SPD 0x1
+#define DIP_LEN_SPD 25
+#define DIP_SPD_UNKNOWN 0
+#define DIP_SPD_DSTB 0x1
+#define DIP_SPD_DVDP 0x2
+#define DIP_SPD_DVHS 0x3
+#define DIP_SPD_HDDVR 0x4
+#define DIP_SPD_DVC 0x5
+#define DIP_SPD_DSC 0x6
+#define DIP_SPD_VCD 0x7
+#define DIP_SPD_GAME 0x8
+#define DIP_SPD_PC 0x9
+#define DIP_SPD_BD 0xa
+#define DIP_SPD_SCD 0xb
+
struct dip_infoframe {
uint8_t type; /* HB0 */
uint8_t ver; /* HB1 */
@@ -205,6 +227,11 @@ struct dip_infoframe {
uint16_t left_bar_end;
uint16_t right_bar_start;
} avi;
+ struct {
+ uint8_t vn[8];
+ uint8_t pd[16];
+ uint8_t sdi;
+ } spd;
uint8_t payload[27];
} __attribute__ ((packed)) body;
} __attribute__((packed));
@@ -233,6 +260,13 @@ struct intel_unpin_work {
bool enable_stall_check;
};
+struct intel_fbc_work {
+ struct delayed_work work;
+ struct drm_crtc *crtc;
+ struct drm_framebuffer *fb;
+ int interval;
+};
+
int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus);
@@ -253,7 +287,7 @@ void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
extern bool intel_dpd_is_edp(struct drm_device *dev);
-extern void intel_edp_link_config (struct intel_encoder *, int *, int *);
+extern void intel_edp_link_config(struct intel_encoder *, int *, int *);
extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder);
/* intel_panel.c */
@@ -266,14 +300,15 @@ extern void intel_pch_panel_fitting(struct drm_device *dev,
extern u32 intel_panel_get_max_backlight(struct drm_device *dev);
extern u32 intel_panel_get_backlight(struct drm_device *dev);
extern void intel_panel_set_backlight(struct drm_device *dev, u32 level);
-extern void intel_panel_setup_backlight(struct drm_device *dev);
+extern int intel_panel_setup_backlight(struct drm_device *dev);
extern void intel_panel_enable_backlight(struct drm_device *dev);
extern void intel_panel_disable_backlight(struct drm_device *dev);
+extern void intel_panel_destroy_backlight(struct drm_device *dev);
extern enum drm_connector_status intel_panel_detect(struct drm_device *dev);
extern void intel_crtc_load_lut(struct drm_crtc *crtc);
-extern void intel_encoder_prepare (struct drm_encoder *encoder);
-extern void intel_encoder_commit (struct drm_encoder *encoder);
+extern void intel_encoder_prepare(struct drm_encoder *encoder);
+extern void intel_encoder_commit(struct drm_encoder *encoder);
extern void intel_encoder_destroy(struct drm_encoder *encoder);
static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector)
@@ -305,9 +340,6 @@ extern void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
struct drm_connector *connector,
struct intel_load_detect_pipe *old);
-extern struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB);
-extern int intel_sdvo_supports_hotplug(struct drm_connector *connector);
-extern void intel_sdvo_set_hotplug(struct drm_connector *connector, int enable);
extern void intelfb_restore(void);
extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
u16 blue, int regno);
@@ -317,6 +349,7 @@ extern void intel_enable_clock_gating(struct drm_device *dev);
extern void ironlake_enable_drps(struct drm_device *dev);
extern void ironlake_disable_drps(struct drm_device *dev);
extern void gen6_enable_rps(struct drm_i915_private *dev_priv);
+extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv);
extern void gen6_disable_rps(struct drm_device *dev);
extern void intel_init_emon(struct drm_device *dev);
@@ -347,4 +380,8 @@ extern void intel_fb_output_poll_changed(struct drm_device *dev);
extern void intel_fb_restore_mode(struct drm_device *dev);
extern void intel_init_clock_gating(struct drm_device *dev);
+extern void intel_write_eld(struct drm_encoder *encoder,
+ struct drm_display_mode *mode);
+extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe);
+
#endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index aa0a8e83142e..d4f5a0b2120d 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -45,6 +45,8 @@ struct intel_hdmi {
bool has_hdmi_sink;
bool has_audio;
int force_audio;
+ void (*write_infoframe)(struct drm_encoder *encoder,
+ struct dip_infoframe *frame);
};
static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
@@ -58,37 +60,69 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)
struct intel_hdmi, base);
}
-void intel_dip_infoframe_csum(struct dip_infoframe *avi_if)
+void intel_dip_infoframe_csum(struct dip_infoframe *frame)
{
- uint8_t *data = (uint8_t *)avi_if;
+ uint8_t *data = (uint8_t *)frame;
uint8_t sum = 0;
unsigned i;
- avi_if->checksum = 0;
- avi_if->ecc = 0;
+ frame->checksum = 0;
+ frame->ecc = 0;
- for (i = 0; i < sizeof(*avi_if); i++)
+ for (i = 0; i < frame->len + DIP_HEADER_SIZE; i++)
sum += data[i];
- avi_if->checksum = 0x100 - sum;
+ frame->checksum = 0x100 - sum;
}
-static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+static u32 intel_infoframe_index(struct dip_infoframe *frame)
{
- struct dip_infoframe avi_if = {
- .type = DIP_TYPE_AVI,
- .ver = DIP_VERSION_AVI,
- .len = DIP_LEN_AVI,
- };
- uint32_t *data = (uint32_t *)&avi_if;
+ u32 flags = 0;
+
+ switch (frame->type) {
+ case DIP_TYPE_AVI:
+ flags |= VIDEO_DIP_SELECT_AVI;
+ break;
+ case DIP_TYPE_SPD:
+ flags |= VIDEO_DIP_SELECT_SPD;
+ break;
+ default:
+ DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
+ break;
+ }
+
+ return flags;
+}
+
+static u32 intel_infoframe_flags(struct dip_infoframe *frame)
+{
+ u32 flags = 0;
+
+ switch (frame->type) {
+ case DIP_TYPE_AVI:
+ flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC;
+ break;
+ case DIP_TYPE_SPD:
+ flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_VSYNC;
+ break;
+ default:
+ DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
+ break;
+ }
+
+ return flags;
+}
+
+static void i9xx_write_infoframe(struct drm_encoder *encoder,
+ struct dip_infoframe *frame)
+{
+ uint32_t *data = (uint32_t *)frame;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
- u32 port;
- unsigned i;
+ u32 port, flags, val = I915_READ(VIDEO_DIP_CTL);
+ unsigned i, len = DIP_HEADER_SIZE + frame->len;
- if (!intel_hdmi->has_hdmi_sink)
- return;
/* XXX first guess at handling video port, is this corrent? */
if (intel_hdmi->sdvox_reg == SDVOB)
@@ -98,18 +132,87 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
else
return;
- I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
- VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC);
+ flags = intel_infoframe_index(frame);
+
+ val &= ~VIDEO_DIP_SELECT_MASK;
- intel_dip_infoframe_csum(&avi_if);
- for (i = 0; i < sizeof(avi_if); i += 4) {
+ I915_WRITE(VIDEO_DIP_CTL, val | port | flags);
+
+ for (i = 0; i < len; i += 4) {
I915_WRITE(VIDEO_DIP_DATA, *data);
data++;
}
- I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
- VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC |
- VIDEO_DIP_ENABLE_AVI);
+ flags |= intel_infoframe_flags(frame);
+
+ I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
+}
+
+static void ironlake_write_infoframe(struct drm_encoder *encoder,
+ struct dip_infoframe *frame)
+{
+ uint32_t *data = (uint32_t *)frame;
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+ unsigned i, len = DIP_HEADER_SIZE + frame->len;
+ u32 flags, val = I915_READ(reg);
+
+ intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+ flags = intel_infoframe_index(frame);
+
+ val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
+
+ I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
+
+ for (i = 0; i < len; i += 4) {
+ I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
+ data++;
+ }
+
+ flags |= intel_infoframe_flags(frame);
+
+ I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
+}
+static void intel_set_infoframe(struct drm_encoder *encoder,
+ struct dip_infoframe *frame)
+{
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+ if (!intel_hdmi->has_hdmi_sink)
+ return;
+
+ intel_dip_infoframe_csum(frame);
+ intel_hdmi->write_infoframe(encoder, frame);
+}
+
+static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+{
+ struct dip_infoframe avi_if = {
+ .type = DIP_TYPE_AVI,
+ .ver = DIP_VERSION_AVI,
+ .len = DIP_LEN_AVI,
+ };
+
+ intel_set_infoframe(encoder, &avi_if);
+}
+
+static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
+{
+ struct dip_infoframe spd_if;
+
+ memset(&spd_if, 0, sizeof(spd_if));
+ spd_if.type = DIP_TYPE_SPD;
+ spd_if.ver = DIP_VERSION_SPD;
+ spd_if.len = DIP_LEN_SPD;
+ strcpy(spd_if.body.spd.vn, "Intel");
+ strcpy(spd_if.body.spd.pd, "Integrated gfx");
+ spd_if.body.spd.sdi = DIP_SPD_PC;
+
+ intel_set_infoframe(encoder, &spd_if);
}
static void intel_hdmi_mode_set(struct drm_encoder *encoder,
@@ -124,32 +227,40 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
u32 sdvox;
sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE;
- sdvox |= intel_hdmi->color_range;
+ if (!HAS_PCH_SPLIT(dev))
+ sdvox |= intel_hdmi->color_range;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
sdvox |= SDVO_VSYNC_ACTIVE_HIGH;
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
sdvox |= SDVO_HSYNC_ACTIVE_HIGH;
+ if (intel_crtc->bpp > 24)
+ sdvox |= COLOR_FORMAT_12bpc;
+ else
+ sdvox |= COLOR_FORMAT_8bpc;
+
/* Required on CPT */
if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
sdvox |= HDMI_MODE_SELECT;
if (intel_hdmi->has_audio) {
+ DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
+ pipe_name(intel_crtc->pipe));
sdvox |= SDVO_AUDIO_ENABLE;
sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC;
+ intel_write_eld(encoder, adjusted_mode);
}
- if (intel_crtc->pipe == 1) {
- if (HAS_PCH_CPT(dev))
- sdvox |= PORT_TRANS_B_SEL_CPT;
- else
- sdvox |= SDVO_PIPE_B_SELECT;
- }
+ if (HAS_PCH_CPT(dev))
+ sdvox |= PORT_TRANS_SEL_CPT(intel_crtc->pipe);
+ else if (intel_crtc->pipe == 1)
+ sdvox |= SDVO_PIPE_B_SELECT;
I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
POSTING_READ(intel_hdmi->sdvox_reg);
intel_hdmi_set_avi_infoframe(encoder);
+ intel_hdmi_set_spd_infoframe(encoder);
}
static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
@@ -375,6 +486,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
struct intel_encoder *intel_encoder;
struct intel_connector *intel_connector;
struct intel_hdmi *intel_hdmi;
+ int i;
intel_hdmi = kzalloc(sizeof(struct intel_hdmi), GFP_KERNEL);
if (!intel_hdmi)
@@ -400,7 +512,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
connector->polled = DRM_CONNECTOR_POLL_HPD;
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
- intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
+ intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
/* Set up the DDC bus. */
if (sdvox_reg == SDVOB) {
@@ -427,6 +539,15 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
intel_hdmi->sdvox_reg = sdvox_reg;
+ if (!HAS_PCH_SPLIT(dev)) {
+ intel_hdmi->write_infoframe = i9xx_write_infoframe;
+ I915_WRITE(VIDEO_DIP_CTL, 0);
+ } else {
+ intel_hdmi->write_infoframe = ironlake_write_infoframe;
+ for_each_pipe(i)
+ I915_WRITE(TVIDEO_DIP_CTL(i), 0);
+ }
+
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
intel_hdmi_add_properties(intel_hdmi, connector);
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index d98cee60b602..d30ccccb9d73 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -28,6 +28,7 @@
*/
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
+#include <linux/export.h>
#include "drmP.h"
#include "drm.h"
#include "intel_drv.h"
@@ -422,13 +423,7 @@ void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed)
{
struct intel_gmbus *bus = to_intel_gmbus(adapter);
- /* speed:
- * 0x0 = 100 KHz
- * 0x1 = 50 KHz
- * 0x2 = 400 KHz
- * 0x3 = 1000 Khz
- */
- bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | (speed << 8);
+ bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | speed;
}
void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit)
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index b28f7bd9f88a..42f165a520de 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -72,14 +72,16 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds)
{
struct drm_device *dev = intel_lvds->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 ctl_reg, lvds_reg;
+ u32 ctl_reg, lvds_reg, stat_reg;
if (HAS_PCH_SPLIT(dev)) {
ctl_reg = PCH_PP_CONTROL;
lvds_reg = PCH_LVDS;
+ stat_reg = PCH_PP_STATUS;
} else {
ctl_reg = PP_CONTROL;
lvds_reg = LVDS;
+ stat_reg = PP_STATUS;
}
I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN);
@@ -94,17 +96,16 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds)
DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
intel_lvds->pfit_control,
intel_lvds->pfit_pgm_ratios);
- if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) {
- DRM_ERROR("timed out waiting for panel to power off\n");
- } else {
- I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios);
- I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control);
- intel_lvds->pfit_dirty = false;
- }
+
+ I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios);
+ I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control);
+ intel_lvds->pfit_dirty = false;
}
I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
POSTING_READ(lvds_reg);
+ if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000))
+ DRM_ERROR("timed out waiting for panel to power on\n");
intel_panel_enable_backlight(dev);
}
@@ -113,24 +114,25 @@ static void intel_lvds_disable(struct intel_lvds *intel_lvds)
{
struct drm_device *dev = intel_lvds->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 ctl_reg, lvds_reg;
+ u32 ctl_reg, lvds_reg, stat_reg;
if (HAS_PCH_SPLIT(dev)) {
ctl_reg = PCH_PP_CONTROL;
lvds_reg = PCH_LVDS;
+ stat_reg = PCH_PP_STATUS;
} else {
ctl_reg = PP_CONTROL;
lvds_reg = LVDS;
+ stat_reg = PP_STATUS;
}
intel_panel_disable_backlight(dev);
I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON);
+ if (wait_for((I915_READ(stat_reg) & PP_ON) == 0, 1000))
+ DRM_ERROR("timed out waiting for panel to power off\n");
if (intel_lvds->pfit_control) {
- if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000))
- DRM_ERROR("timed out waiting for panel to power off\n");
-
I915_WRITE(PFIT_CONTROL, 0);
intel_lvds->pfit_dirty = true;
}
@@ -398,53 +400,21 @@ out:
static void intel_lvds_prepare(struct drm_encoder *encoder)
{
- struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
- /* We try to do the minimum that is necessary in order to unlock
- * the registers for mode setting.
- *
- * On Ironlake, this is quite simple as we just set the unlock key
- * and ignore all subtleties. (This may cause some issues...)
- *
+ /*
* Prior to Ironlake, we must disable the pipe if we want to adjust
* the panel fitter. However at all other times we can just reset
* the registers regardless.
*/
-
- if (HAS_PCH_SPLIT(dev)) {
- I915_WRITE(PCH_PP_CONTROL,
- I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
- } else if (intel_lvds->pfit_dirty) {
- I915_WRITE(PP_CONTROL,
- (I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS)
- & ~POWER_TARGET_ON);
- } else {
- I915_WRITE(PP_CONTROL,
- I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
- }
+ if (!HAS_PCH_SPLIT(encoder->dev) && intel_lvds->pfit_dirty)
+ intel_lvds_disable(intel_lvds);
}
static void intel_lvds_commit(struct drm_encoder *encoder)
{
- struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
- /* Undo any unlocking done in prepare to prevent accidental
- * adjustment of the registers.
- */
- if (HAS_PCH_SPLIT(dev)) {
- u32 val = I915_READ(PCH_PP_CONTROL);
- if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS)
- I915_WRITE(PCH_PP_CONTROL, val & 0x3);
- } else {
- u32 val = I915_READ(PP_CONTROL);
- if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS)
- I915_WRITE(PP_CONTROL, val & 0x3);
- }
-
/* Always do a full power on as we do not know what state
* we were left in.
*/
@@ -582,6 +552,8 @@ static void intel_lvds_destroy(struct drm_connector *connector)
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ intel_panel_destroy_backlight(dev);
+
if (dev_priv->lid_notifier.notifier_call)
acpi_lid_notifier_unregister(&dev_priv->lid_notifier);
drm_sysfs_connector_remove(connector);
@@ -690,6 +662,14 @@ static const struct dmi_system_id intel_no_lvds[] = {
},
{
.callback = intel_no_lvds_dmi_callback,
+ .ident = "Dell OptiPlex FX170",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"),
+ },
+ },
+ {
+ .callback = intel_no_lvds_dmi_callback,
.ident = "AOpen Mini PC",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "AOpen"),
@@ -908,9 +888,11 @@ bool intel_lvds_init(struct drm_device *dev)
intel_encoder->type = INTEL_OUTPUT_LVDS;
intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT);
- intel_encoder->crtc_mask = (1 << 1);
- if (INTEL_INFO(dev)->gen >= 5)
- intel_encoder->crtc_mask |= (1 << 0);
+ if (HAS_PCH_SPLIT(dev))
+ intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+ else
+ intel_encoder->crtc_mask = (1 << 1);
+
drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs);
drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
@@ -1032,6 +1014,19 @@ out:
pwm = I915_READ(BLC_PWM_PCH_CTL1);
pwm |= PWM_PCH_ENABLE;
I915_WRITE(BLC_PWM_PCH_CTL1, pwm);
+ /*
+ * Unlock registers and just
+ * leave them unlocked
+ */
+ I915_WRITE(PCH_PP_CONTROL,
+ I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
+ } else {
+ /*
+ * Unlock registers and just
+ * leave them unlocked
+ */
+ I915_WRITE(PP_CONTROL,
+ I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
}
dev_priv->lid_notifier.notifier_call = intel_lid_notify;
if (acpi_lid_notifier_register(&dev_priv->lid_notifier)) {
@@ -1041,6 +1036,9 @@ out:
/* keep the LVDS connector */
dev_priv->int_lvds_connector = connector;
drm_sysfs_connector_add(connector);
+
+ intel_panel_setup_backlight(dev);
+
return true;
failed:
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
index 3b26a3ba02dd..be2c6fe07d12 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/fb.h>
+#include <drm/drm_edid.h>
#include "drmP.h"
#include "intel_drv.h"
#include "i915_drv.h"
@@ -74,6 +75,7 @@ int intel_ddc_get_modes(struct drm_connector *connector,
if (edid) {
drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
+ drm_edid_to_eld(connector, edid);
connector->display_info.raw_edid = NULL;
kfree(edid);
}
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index d2c710422908..289140bc83cb 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -51,61 +51,61 @@
#define MBOX_ASLE (1<<2)
struct opregion_header {
- u8 signature[16];
- u32 size;
- u32 opregion_ver;
- u8 bios_ver[32];
- u8 vbios_ver[16];
- u8 driver_ver[16];
- u32 mboxes;
- u8 reserved[164];
+ u8 signature[16];
+ u32 size;
+ u32 opregion_ver;
+ u8 bios_ver[32];
+ u8 vbios_ver[16];
+ u8 driver_ver[16];
+ u32 mboxes;
+ u8 reserved[164];
} __attribute__((packed));
/* OpRegion mailbox #1: public ACPI methods */
struct opregion_acpi {
- u32 drdy; /* driver readiness */
- u32 csts; /* notification status */
- u32 cevt; /* current event */
- u8 rsvd1[20];
- u32 didl[8]; /* supported display devices ID list */
- u32 cpdl[8]; /* currently presented display list */
- u32 cadl[8]; /* currently active display list */
- u32 nadl[8]; /* next active devices list */
- u32 aslp; /* ASL sleep time-out */
- u32 tidx; /* toggle table index */
- u32 chpd; /* current hotplug enable indicator */
- u32 clid; /* current lid state*/
- u32 cdck; /* current docking state */
- u32 sxsw; /* Sx state resume */
- u32 evts; /* ASL supported events */
- u32 cnot; /* current OS notification */
- u32 nrdy; /* driver status */
- u8 rsvd2[60];
+ u32 drdy; /* driver readiness */
+ u32 csts; /* notification status */
+ u32 cevt; /* current event */
+ u8 rsvd1[20];
+ u32 didl[8]; /* supported display devices ID list */
+ u32 cpdl[8]; /* currently presented display list */
+ u32 cadl[8]; /* currently active display list */
+ u32 nadl[8]; /* next active devices list */
+ u32 aslp; /* ASL sleep time-out */
+ u32 tidx; /* toggle table index */
+ u32 chpd; /* current hotplug enable indicator */
+ u32 clid; /* current lid state*/
+ u32 cdck; /* current docking state */
+ u32 sxsw; /* Sx state resume */
+ u32 evts; /* ASL supported events */
+ u32 cnot; /* current OS notification */
+ u32 nrdy; /* driver status */
+ u8 rsvd2[60];
} __attribute__((packed));
/* OpRegion mailbox #2: SWSCI */
struct opregion_swsci {
- u32 scic; /* SWSCI command|status|data */
- u32 parm; /* command parameters */
- u32 dslp; /* driver sleep time-out */
- u8 rsvd[244];
+ u32 scic; /* SWSCI command|status|data */
+ u32 parm; /* command parameters */
+ u32 dslp; /* driver sleep time-out */
+ u8 rsvd[244];
} __attribute__((packed));
/* OpRegion mailbox #3: ASLE */
struct opregion_asle {
- u32 ardy; /* driver readiness */
- u32 aslc; /* ASLE interrupt command */
- u32 tche; /* technology enabled indicator */
- u32 alsi; /* current ALS illuminance reading */
- u32 bclp; /* backlight brightness to set */
- u32 pfit; /* panel fitting state */
- u32 cblv; /* current brightness level */
- u16 bclm[20]; /* backlight level duty cycle mapping table */
- u32 cpfm; /* current panel fitting mode */
- u32 epfm; /* enabled panel fitting modes */
- u8 plut[74]; /* panel LUT and identifier */
- u32 pfmb; /* PWM freq and min brightness */
- u8 rsvd[102];
+ u32 ardy; /* driver readiness */
+ u32 aslc; /* ASLE interrupt command */
+ u32 tche; /* technology enabled indicator */
+ u32 alsi; /* current ALS illuminance reading */
+ u32 bclp; /* backlight brightness to set */
+ u32 pfit; /* panel fitting state */
+ u32 cblv; /* current brightness level */
+ u16 bclm[20]; /* backlight level duty cycle mapping table */
+ u32 cpfm; /* current panel fitting mode */
+ u32 epfm; /* enabled panel fitting modes */
+ u8 plut[74]; /* panel LUT and identifier */
+ u32 pfmb; /* PWM freq and min brightness */
+ u8 rsvd[102];
} __attribute__((packed));
/* ASLE irq request bits */
@@ -227,7 +227,6 @@ void intel_opregion_asle_intr(struct drm_device *dev)
asle->aslc = asle_stat;
}
-/* Only present on Ironlake+ */
void intel_opregion_gse_intr(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -297,19 +296,26 @@ static int intel_opregion_video_event(struct notifier_block *nb,
/* The only video events relevant to opregion are 0x80. These indicate
either a docking event, lid switch or display switch request. In
Linux, these are handled by the dock, button and video drivers.
- We might want to fix the video driver to be opregion-aware in
- future, but right now we just indicate to the firmware that the
- request has been handled */
+ */
struct opregion_acpi *acpi;
+ struct acpi_bus_event *event = data;
+ int ret = NOTIFY_OK;
+
+ if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0)
+ return NOTIFY_DONE;
if (!system_opregion)
return NOTIFY_DONE;
acpi = system_opregion->acpi;
+
+ if (event->type == 0x80 && !(acpi->cevt & 0x1))
+ ret = NOTIFY_BAD;
+
acpi->csts = 0;
- return NOTIFY_OK;
+ return ret;
}
static struct notifier_block intel_opregion_notifier = {
@@ -355,7 +361,7 @@ static void intel_didl_outputs(struct drm_device *dev)
list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {
if (i >= 8) {
- dev_printk (KERN_ERR, &dev->pdev->dev,
+ dev_printk(KERN_ERR, &dev->pdev->dev,
"More than 8 outputs detected\n");
return;
}
@@ -381,7 +387,7 @@ blind_set:
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
int output_type = ACPI_OTHER_OUTPUT;
if (i >= 8) {
- dev_printk (KERN_ERR, &dev->pdev->dev,
+ dev_printk(KERN_ERR, &dev->pdev->dev,
"More than 8 outputs detected\n");
return;
}
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index 9e2959bc91cd..cdf17d4cc1f7 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -117,57 +117,57 @@
/* memory bufferd overlay registers */
struct overlay_registers {
- u32 OBUF_0Y;
- u32 OBUF_1Y;
- u32 OBUF_0U;
- u32 OBUF_0V;
- u32 OBUF_1U;
- u32 OBUF_1V;
- u32 OSTRIDE;
- u32 YRGB_VPH;
- u32 UV_VPH;
- u32 HORZ_PH;
- u32 INIT_PHS;
- u32 DWINPOS;
- u32 DWINSZ;
- u32 SWIDTH;
- u32 SWIDTHSW;
- u32 SHEIGHT;
- u32 YRGBSCALE;
- u32 UVSCALE;
- u32 OCLRC0;
- u32 OCLRC1;
- u32 DCLRKV;
- u32 DCLRKM;
- u32 SCLRKVH;
- u32 SCLRKVL;
- u32 SCLRKEN;
- u32 OCONFIG;
- u32 OCMD;
- u32 RESERVED1; /* 0x6C */
- u32 OSTART_0Y;
- u32 OSTART_1Y;
- u32 OSTART_0U;
- u32 OSTART_0V;
- u32 OSTART_1U;
- u32 OSTART_1V;
- u32 OTILEOFF_0Y;
- u32 OTILEOFF_1Y;
- u32 OTILEOFF_0U;
- u32 OTILEOFF_0V;
- u32 OTILEOFF_1U;
- u32 OTILEOFF_1V;
- u32 FASTHSCALE; /* 0xA0 */
- u32 UVSCALEV; /* 0xA4 */
- u32 RESERVEDC[(0x200 - 0xA8) / 4]; /* 0xA8 - 0x1FC */
- u16 Y_VCOEFS[N_VERT_Y_TAPS * N_PHASES]; /* 0x200 */
- u16 RESERVEDD[0x100 / 2 - N_VERT_Y_TAPS * N_PHASES];
- u16 Y_HCOEFS[N_HORIZ_Y_TAPS * N_PHASES]; /* 0x300 */
- u16 RESERVEDE[0x200 / 2 - N_HORIZ_Y_TAPS * N_PHASES];
- u16 UV_VCOEFS[N_VERT_UV_TAPS * N_PHASES]; /* 0x500 */
- u16 RESERVEDF[0x100 / 2 - N_VERT_UV_TAPS * N_PHASES];
- u16 UV_HCOEFS[N_HORIZ_UV_TAPS * N_PHASES]; /* 0x600 */
- u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES];
+ u32 OBUF_0Y;
+ u32 OBUF_1Y;
+ u32 OBUF_0U;
+ u32 OBUF_0V;
+ u32 OBUF_1U;
+ u32 OBUF_1V;
+ u32 OSTRIDE;
+ u32 YRGB_VPH;
+ u32 UV_VPH;
+ u32 HORZ_PH;
+ u32 INIT_PHS;
+ u32 DWINPOS;
+ u32 DWINSZ;
+ u32 SWIDTH;
+ u32 SWIDTHSW;
+ u32 SHEIGHT;
+ u32 YRGBSCALE;
+ u32 UVSCALE;
+ u32 OCLRC0;
+ u32 OCLRC1;
+ u32 DCLRKV;
+ u32 DCLRKM;
+ u32 SCLRKVH;
+ u32 SCLRKVL;
+ u32 SCLRKEN;
+ u32 OCONFIG;
+ u32 OCMD;
+ u32 RESERVED1; /* 0x6C */
+ u32 OSTART_0Y;
+ u32 OSTART_1Y;
+ u32 OSTART_0U;
+ u32 OSTART_0V;
+ u32 OSTART_1U;
+ u32 OSTART_1V;
+ u32 OTILEOFF_0Y;
+ u32 OTILEOFF_1Y;
+ u32 OTILEOFF_0U;
+ u32 OTILEOFF_0V;
+ u32 OTILEOFF_1U;
+ u32 OTILEOFF_1V;
+ u32 FASTHSCALE; /* 0xA0 */
+ u32 UVSCALEV; /* 0xA4 */
+ u32 RESERVEDC[(0x200 - 0xA8) / 4]; /* 0xA8 - 0x1FC */
+ u16 Y_VCOEFS[N_VERT_Y_TAPS * N_PHASES]; /* 0x200 */
+ u16 RESERVEDD[0x100 / 2 - N_VERT_Y_TAPS * N_PHASES];
+ u16 Y_HCOEFS[N_HORIZ_Y_TAPS * N_PHASES]; /* 0x300 */
+ u16 RESERVEDE[0x200 / 2 - N_HORIZ_Y_TAPS * N_PHASES];
+ u16 UV_VCOEFS[N_VERT_UV_TAPS * N_PHASES]; /* 0x500 */
+ u16 RESERVEDF[0x100 / 2 - N_VERT_UV_TAPS * N_PHASES];
+ u16 UV_HCOEFS[N_HORIZ_UV_TAPS * N_PHASES]; /* 0x600 */
+ u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES];
};
struct intel_overlay {
@@ -192,7 +192,7 @@ struct intel_overlay {
static struct overlay_registers *
intel_overlay_map_regs(struct intel_overlay *overlay)
{
- drm_i915_private_t *dev_priv = overlay->dev->dev_private;
+ drm_i915_private_t *dev_priv = overlay->dev->dev_private;
struct overlay_registers *regs;
if (OVERLAY_NEEDS_PHYSICAL(overlay->dev))
@@ -264,7 +264,7 @@ i830_activate_pipe_a(struct drm_device *dev)
mode = drm_mode_duplicate(dev, &vesa_640x480);
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
- if(!drm_crtc_helper_set_mode(&crtc->base, mode,
+ if (!drm_crtc_helper_set_mode(&crtc->base, mode,
crtc->base.x, crtc->base.y,
crtc->base.fb))
return 0;
@@ -332,7 +332,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
bool load_polyphase_filter)
{
struct drm_device *dev = overlay->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_request *request;
u32 flip_addr = overlay->flip_addr;
u32 tmp;
@@ -359,7 +359,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
}
OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
OUT_RING(flip_addr);
- ADVANCE_LP_RING();
+ ADVANCE_LP_RING();
ret = i915_add_request(LP_RING(dev_priv), NULL, request);
if (ret) {
@@ -583,7 +583,7 @@ static u32 calc_swidthsw(struct drm_device *dev, u32 offset, u32 width)
ret = ((offset + width + mask) >> shift) - (offset >> shift);
if (!IS_GEN2(dev))
ret <<= 1;
- ret -=1;
+ ret -= 1;
return ret << 2;
}
@@ -773,14 +773,10 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
if (ret != 0)
return ret;
- ret = i915_gem_object_pin(new_bo, PAGE_SIZE, true);
+ ret = i915_gem_object_pin_to_display_plane(new_bo, 0, NULL);
if (ret != 0)
return ret;
- ret = i915_gem_object_set_to_gtt_domain(new_bo, 0);
- if (ret != 0)
- goto out_unpin;
-
ret = i915_gem_object_put_fence(new_bo);
if (ret)
goto out_unpin;
@@ -821,7 +817,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
regs->SWIDTHSW = calc_swidthsw(overlay->dev,
params->offset_Y, tmp_width);
regs->SHEIGHT = params->src_h;
- regs->OBUF_0Y = new_bo->gtt_offset + params-> offset_Y;
+ regs->OBUF_0Y = new_bo->gtt_offset + params->offset_Y;
regs->OSTRIDE = params->stride_Y;
if (params->format & I915_OVERLAY_YUV_PLANAR) {
@@ -921,7 +917,7 @@ static void update_pfit_vscale_ratio(struct intel_overlay *overlay)
* line with the intel documentation for the i965
*/
if (INTEL_INFO(dev)->gen >= 4) {
- /* on i965 use the PGM reg to read out the autoscaler values */
+ /* on i965 use the PGM reg to read out the autoscaler values */
ratio = I915_READ(PFIT_PGM_RATIOS) >> PFIT_VERT_SCALE_SHIFT_965;
} else {
if (pfit_control & VERT_AUTO_SCALE)
@@ -1102,7 +1098,7 @@ static int intel_panel_fitter_pipe(struct drm_device *dev)
}
int intel_overlay_put_image(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+ struct drm_file *file_priv)
{
struct drm_intel_overlay_put_image *put_image_rec = data;
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -1305,10 +1301,10 @@ static int check_gamma(struct drm_intel_overlay_attrs *attrs)
}
int intel_overlay_attrs(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+ struct drm_file *file_priv)
{
struct drm_intel_overlay_attrs *attrs = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_overlay *overlay;
struct overlay_registers *regs;
int ret;
@@ -1397,7 +1393,7 @@ out_unlock:
void intel_setup_overlay(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_overlay *overlay;
struct drm_i915_gem_object *reg_bo;
struct overlay_registers *regs;
@@ -1425,24 +1421,24 @@ void intel_setup_overlay(struct drm_device *dev)
ret = i915_gem_attach_phys_object(dev, reg_bo,
I915_GEM_PHYS_OVERLAY_REGS,
PAGE_SIZE);
- if (ret) {
- DRM_ERROR("failed to attach phys overlay regs\n");
- goto out_free_bo;
- }
+ if (ret) {
+ DRM_ERROR("failed to attach phys overlay regs\n");
+ goto out_free_bo;
+ }
overlay->flip_addr = reg_bo->phys_obj->handle->busaddr;
} else {
ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true);
if (ret) {
- DRM_ERROR("failed to pin overlay register bo\n");
- goto out_free_bo;
- }
+ DRM_ERROR("failed to pin overlay register bo\n");
+ goto out_free_bo;
+ }
overlay->flip_addr = reg_bo->gtt_offset;
ret = i915_gem_object_set_to_gtt_domain(reg_bo, true);
if (ret) {
- DRM_ERROR("failed to move overlay register bo into the GTT\n");
- goto out_unpin_bo;
- }
+ DRM_ERROR("failed to move overlay register bo into the GTT\n");
+ goto out_unpin_bo;
+ }
}
/* init all values */
@@ -1529,7 +1525,7 @@ static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay,
struct intel_overlay_error_state *
intel_overlay_capture_error_state(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_overlay *overlay = dev_priv->overlay;
struct intel_overlay_error_state *error;
struct overlay_registers __iomem *regs;
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index a06ff07a4d3b..21f60b7d69a3 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -83,11 +83,15 @@ intel_pch_panel_fitting(struct drm_device *dev,
u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
if (scaled_width > scaled_height) { /* pillar */
width = scaled_height / mode->vdisplay;
+ if (width & 1)
+ width++;
x = (adjusted_mode->hdisplay - width + 1) / 2;
y = 0;
height = adjusted_mode->vdisplay;
} else if (scaled_width < scaled_height) { /* letter */
height = scaled_width / mode->hdisplay;
+ if (height & 1)
+ height++;
y = (adjusted_mode->vdisplay - height + 1) / 2;
x = 0;
width = adjusted_mode->hdisplay;
@@ -202,7 +206,7 @@ u32 intel_panel_get_backlight(struct drm_device *dev)
if (IS_PINEVIEW(dev))
val >>= 1;
- if (is_backlight_combination_mode(dev)){
+ if (is_backlight_combination_mode(dev)) {
u8 lbpc;
val &= ~1;
@@ -222,7 +226,7 @@ static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
I915_WRITE(BLC_PWM_CPU_CTL, val | level);
}
-void intel_panel_set_backlight(struct drm_device *dev, u32 level)
+static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 tmp;
@@ -232,7 +236,7 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level)
if (HAS_PCH_SPLIT(dev))
return intel_pch_panel_set_backlight(dev, level);
- if (is_backlight_combination_mode(dev)){
+ if (is_backlight_combination_mode(dev)) {
u32 max = intel_panel_get_max_backlight(dev);
u8 lbpc;
@@ -250,16 +254,21 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level)
I915_WRITE(BLC_PWM_CTL, tmp | level);
}
-void intel_panel_disable_backlight(struct drm_device *dev)
+void intel_panel_set_backlight(struct drm_device *dev, u32 level)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- if (dev_priv->backlight_enabled) {
- dev_priv->backlight_level = intel_panel_get_backlight(dev);
- dev_priv->backlight_enabled = false;
- }
+ dev_priv->backlight_level = level;
+ if (dev_priv->backlight_enabled)
+ intel_panel_actually_set_backlight(dev, level);
+}
+
+void intel_panel_disable_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
- intel_panel_set_backlight(dev, 0);
+ dev_priv->backlight_enabled = false;
+ intel_panel_actually_set_backlight(dev, 0);
}
void intel_panel_enable_backlight(struct drm_device *dev)
@@ -269,11 +278,11 @@ void intel_panel_enable_backlight(struct drm_device *dev)
if (dev_priv->backlight_level == 0)
dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
- intel_panel_set_backlight(dev, dev_priv->backlight_level);
dev_priv->backlight_enabled = true;
+ intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
}
-void intel_panel_setup_backlight(struct drm_device *dev)
+static void intel_panel_init_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -305,3 +314,74 @@ intel_panel_detect(struct drm_device *dev)
return connector_status_unknown;
}
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+static int intel_panel_update_status(struct backlight_device *bd)
+{
+ struct drm_device *dev = bl_get_data(bd);
+ intel_panel_set_backlight(dev, bd->props.brightness);
+ return 0;
+}
+
+static int intel_panel_get_brightness(struct backlight_device *bd)
+{
+ struct drm_device *dev = bl_get_data(bd);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ return dev_priv->backlight_level;
+}
+
+static const struct backlight_ops intel_panel_bl_ops = {
+ .update_status = intel_panel_update_status,
+ .get_brightness = intel_panel_get_brightness,
+};
+
+int intel_panel_setup_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct backlight_properties props;
+ struct drm_connector *connector;
+
+ intel_panel_init_backlight(dev);
+
+ if (dev_priv->int_lvds_connector)
+ connector = dev_priv->int_lvds_connector;
+ else if (dev_priv->int_edp_connector)
+ connector = dev_priv->int_edp_connector;
+ else
+ return -ENODEV;
+
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = intel_panel_get_max_backlight(dev);
+ dev_priv->backlight =
+ backlight_device_register("intel_backlight",
+ &connector->kdev, dev,
+ &intel_panel_bl_ops, &props);
+
+ if (IS_ERR(dev_priv->backlight)) {
+ DRM_ERROR("Failed to register backlight: %ld\n",
+ PTR_ERR(dev_priv->backlight));
+ dev_priv->backlight = NULL;
+ return -ENODEV;
+ }
+ dev_priv->backlight->props.brightness = intel_panel_get_backlight(dev);
+ return 0;
+}
+
+void intel_panel_destroy_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ if (dev_priv->backlight)
+ backlight_device_unregister(dev_priv->backlight);
+}
+#else
+int intel_panel_setup_backlight(struct drm_device *dev)
+{
+ intel_panel_init_backlight(dev);
+ return 0;
+}
+
+void intel_panel_destroy_backlight(struct drm_device *dev)
+{
+ return;
+}
+#endif
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 95c4b1429935..ca70e2f10445 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -34,6 +34,16 @@
#include "i915_trace.h"
#include "intel_drv.h"
+/*
+ * 965+ support PIPE_CONTROL commands, which provide finer grained control
+ * over cache flushing.
+ */
+struct pipe_control {
+ struct drm_i915_gem_object *obj;
+ volatile u32 *cpu_page;
+ u32 gtt_offset;
+};
+
static inline int ring_space(struct intel_ring_buffer *ring)
{
int space = (ring->head & HEAD_ADDR) - (ring->tail + 8);
@@ -123,6 +133,118 @@ render_ring_flush(struct intel_ring_buffer *ring,
return 0;
}
+/**
+ * Emits a PIPE_CONTROL with a non-zero post-sync operation, for
+ * implementing two workarounds on gen6. From section 1.4.7.1
+ * "PIPE_CONTROL" of the Sandy Bridge PRM volume 2 part 1:
+ *
+ * [DevSNB-C+{W/A}] Before any depth stall flush (including those
+ * produced by non-pipelined state commands), software needs to first
+ * send a PIPE_CONTROL with no bits set except Post-Sync Operation !=
+ * 0.
+ *
+ * [Dev-SNB{W/A}]: Before a PIPE_CONTROL with Write Cache Flush Enable
+ * =1, a PIPE_CONTROL with any non-zero post-sync-op is required.
+ *
+ * And the workaround for these two requires this workaround first:
+ *
+ * [Dev-SNB{W/A}]: Pipe-control with CS-stall bit set must be sent
+ * BEFORE the pipe-control with a post-sync op and no write-cache
+ * flushes.
+ *
+ * And this last workaround is tricky because of the requirements on
+ * that bit. From section 1.4.7.2.3 "Stall" of the Sandy Bridge PRM
+ * volume 2 part 1:
+ *
+ * "1 of the following must also be set:
+ * - Render Target Cache Flush Enable ([12] of DW1)
+ * - Depth Cache Flush Enable ([0] of DW1)
+ * - Stall at Pixel Scoreboard ([1] of DW1)
+ * - Depth Stall ([13] of DW1)
+ * - Post-Sync Operation ([13] of DW1)
+ * - Notify Enable ([8] of DW1)"
+ *
+ * The cache flushes require the workaround flush that triggered this
+ * one, so we can't use it. Depth stall would trigger the same.
+ * Post-sync nonzero is what triggered this second workaround, so we
+ * can't use that one either. Notify enable is IRQs, which aren't
+ * really our business. That leaves only stall at scoreboard.
+ */
+static int
+intel_emit_post_sync_nonzero_flush(struct intel_ring_buffer *ring)
+{
+ struct pipe_control *pc = ring->private;
+ u32 scratch_addr = pc->gtt_offset + 128;
+ int ret;
+
+
+ ret = intel_ring_begin(ring, 6);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5));
+ intel_ring_emit(ring, PIPE_CONTROL_CS_STALL |
+ PIPE_CONTROL_STALL_AT_SCOREBOARD);
+ intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */
+ intel_ring_emit(ring, 0); /* low dword */
+ intel_ring_emit(ring, 0); /* high dword */
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_advance(ring);
+
+ ret = intel_ring_begin(ring, 6);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5));
+ intel_ring_emit(ring, PIPE_CONTROL_QW_WRITE);
+ intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */
+ intel_ring_emit(ring, 0);
+ intel_ring_emit(ring, 0);
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_advance(ring);
+
+ return 0;
+}
+
+static int
+gen6_render_ring_flush(struct intel_ring_buffer *ring,
+ u32 invalidate_domains, u32 flush_domains)
+{
+ u32 flags = 0;
+ struct pipe_control *pc = ring->private;
+ u32 scratch_addr = pc->gtt_offset + 128;
+ int ret;
+
+ /* Force SNB workarounds for PIPE_CONTROL flushes */
+ intel_emit_post_sync_nonzero_flush(ring);
+
+ /* Just flush everything. Experiments have shown that reducing the
+ * number of bits based on the write domains has little performance
+ * impact.
+ */
+ flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
+ flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
+ flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
+
+ ret = intel_ring_begin(ring, 6);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5));
+ intel_ring_emit(ring, flags);
+ intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT);
+ intel_ring_emit(ring, 0); /* lower dword */
+ intel_ring_emit(ring, 0); /* uppwer dword */
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_advance(ring);
+
+ return 0;
+}
+
static void ring_write_tail(struct intel_ring_buffer *ring,
u32 value)
{
@@ -206,16 +328,6 @@ static int init_ring_common(struct intel_ring_buffer *ring)
return 0;
}
-/*
- * 965+ support PIPE_CONTROL commands, which provide finer grained control
- * over cache flushing.
- */
-struct pipe_control {
- struct drm_i915_gem_object *obj;
- volatile u32 *cpu_page;
- u32 gtt_offset;
-};
-
static int
init_pipe_control(struct intel_ring_buffer *ring)
{
@@ -236,7 +348,8 @@ init_pipe_control(struct intel_ring_buffer *ring)
ret = -ENOMEM;
goto err;
}
- obj->cache_level = I915_CACHE_LLC;
+
+ i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
ret = i915_gem_object_pin(obj, 4096, true);
if (ret)
@@ -289,10 +402,13 @@ static int init_render_ring(struct intel_ring_buffer *ring)
if (IS_GEN6(dev) || IS_GEN7(dev))
mode |= MI_FLUSH_ENABLE << 16 | MI_FLUSH_ENABLE;
I915_WRITE(MI_MODE, mode);
+ if (IS_GEN7(dev))
+ I915_WRITE(GFX_MODE_GEN7,
+ GFX_MODE_DISABLE(GFX_TLB_INVALIDATE_ALWAYS) |
+ GFX_MODE_ENABLE(GFX_REPLAY_MODE));
}
- if (INTEL_INFO(dev)->gen >= 6) {
- } else if (IS_GEN5(dev)) {
+ if (INTEL_INFO(dev)->gen >= 5) {
ret = init_pipe_control(ring);
if (ret)
return ret;
@@ -310,83 +426,131 @@ static void render_ring_cleanup(struct intel_ring_buffer *ring)
}
static void
-update_semaphore(struct intel_ring_buffer *ring, int i, u32 seqno)
+update_mboxes(struct intel_ring_buffer *ring,
+ u32 seqno,
+ u32 mmio_offset)
{
- struct drm_device *dev = ring->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- int id;
-
- /*
- * cs -> 1 = vcs, 0 = bcs
- * vcs -> 1 = bcs, 0 = cs,
- * bcs -> 1 = cs, 0 = vcs.
- */
- id = ring - dev_priv->ring;
- id += 2 - i;
- id %= 3;
-
- intel_ring_emit(ring,
- MI_SEMAPHORE_MBOX |
- MI_SEMAPHORE_REGISTER |
- MI_SEMAPHORE_UPDATE);
+ intel_ring_emit(ring, MI_SEMAPHORE_MBOX |
+ MI_SEMAPHORE_GLOBAL_GTT |
+ MI_SEMAPHORE_REGISTER |
+ MI_SEMAPHORE_UPDATE);
intel_ring_emit(ring, seqno);
- intel_ring_emit(ring,
- RING_SYNC_0(dev_priv->ring[id].mmio_base) + 4*i);
+ intel_ring_emit(ring, mmio_offset);
}
+/**
+ * gen6_add_request - Update the semaphore mailbox registers
+ *
+ * @ring - ring that is adding a request
+ * @seqno - return seqno stuck into the ring
+ *
+ * Update the mailbox registers in the *other* rings with the current seqno.
+ * This acts like a signal in the canonical semaphore.
+ */
static int
gen6_add_request(struct intel_ring_buffer *ring,
- u32 *result)
+ u32 *seqno)
{
- u32 seqno;
+ u32 mbox1_reg;
+ u32 mbox2_reg;
int ret;
ret = intel_ring_begin(ring, 10);
if (ret)
return ret;
- seqno = i915_gem_get_seqno(ring->dev);
- update_semaphore(ring, 0, seqno);
- update_semaphore(ring, 1, seqno);
+ mbox1_reg = ring->signal_mbox[0];
+ mbox2_reg = ring->signal_mbox[1];
+ *seqno = i915_gem_get_seqno(ring->dev);
+
+ update_mboxes(ring, *seqno, mbox1_reg);
+ update_mboxes(ring, *seqno, mbox2_reg);
intel_ring_emit(ring, MI_STORE_DWORD_INDEX);
intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
- intel_ring_emit(ring, seqno);
+ intel_ring_emit(ring, *seqno);
intel_ring_emit(ring, MI_USER_INTERRUPT);
intel_ring_advance(ring);
- *result = seqno;
return 0;
}
-int
-intel_ring_sync(struct intel_ring_buffer *ring,
- struct intel_ring_buffer *to,
+/**
+ * intel_ring_sync - sync the waiter to the signaller on seqno
+ *
+ * @waiter - ring that is waiting
+ * @signaller - ring which has, or will signal
+ * @seqno - seqno which the waiter will block on
+ */
+static int
+intel_ring_sync(struct intel_ring_buffer *waiter,
+ struct intel_ring_buffer *signaller,
+ int ring,
u32 seqno)
{
int ret;
+ u32 dw1 = MI_SEMAPHORE_MBOX |
+ MI_SEMAPHORE_COMPARE |
+ MI_SEMAPHORE_REGISTER;
- ret = intel_ring_begin(ring, 4);
+ ret = intel_ring_begin(waiter, 4);
if (ret)
return ret;
- intel_ring_emit(ring,
- MI_SEMAPHORE_MBOX |
- MI_SEMAPHORE_REGISTER |
- intel_ring_sync_index(ring, to) << 17 |
- MI_SEMAPHORE_COMPARE);
- intel_ring_emit(ring, seqno);
- intel_ring_emit(ring, 0);
- intel_ring_emit(ring, MI_NOOP);
- intel_ring_advance(ring);
+ intel_ring_emit(waiter, dw1 | signaller->semaphore_register[ring]);
+ intel_ring_emit(waiter, seqno);
+ intel_ring_emit(waiter, 0);
+ intel_ring_emit(waiter, MI_NOOP);
+ intel_ring_advance(waiter);
return 0;
}
+/* VCS->RCS (RVSYNC) or BCS->RCS (RBSYNC) */
+int
+render_ring_sync_to(struct intel_ring_buffer *waiter,
+ struct intel_ring_buffer *signaller,
+ u32 seqno)
+{
+ WARN_ON(signaller->semaphore_register[RCS] == MI_SEMAPHORE_SYNC_INVALID);
+ return intel_ring_sync(waiter,
+ signaller,
+ RCS,
+ seqno);
+}
+
+/* RCS->VCS (VRSYNC) or BCS->VCS (VBSYNC) */
+int
+gen6_bsd_ring_sync_to(struct intel_ring_buffer *waiter,
+ struct intel_ring_buffer *signaller,
+ u32 seqno)
+{
+ WARN_ON(signaller->semaphore_register[VCS] == MI_SEMAPHORE_SYNC_INVALID);
+ return intel_ring_sync(waiter,
+ signaller,
+ VCS,
+ seqno);
+}
+
+/* RCS->BCS (BRSYNC) or VCS->BCS (BVSYNC) */
+int
+gen6_blt_ring_sync_to(struct intel_ring_buffer *waiter,
+ struct intel_ring_buffer *signaller,
+ u32 seqno)
+{
+ WARN_ON(signaller->semaphore_register[BCS] == MI_SEMAPHORE_SYNC_INVALID);
+ return intel_ring_sync(waiter,
+ signaller,
+ BCS,
+ seqno);
+}
+
+
+
#define PIPE_CONTROL_FLUSH(ring__, addr__) \
do { \
- intel_ring_emit(ring__, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | \
- PIPE_CONTROL_DEPTH_STALL | 2); \
+ intel_ring_emit(ring__, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE | \
+ PIPE_CONTROL_DEPTH_STALL); \
intel_ring_emit(ring__, (addr__) | PIPE_CONTROL_GLOBAL_GTT); \
intel_ring_emit(ring__, 0); \
intel_ring_emit(ring__, 0); \
@@ -414,8 +578,9 @@ pc_render_add_request(struct intel_ring_buffer *ring,
if (ret)
return ret;
- intel_ring_emit(ring, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE |
- PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH);
+ intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE |
+ PIPE_CONTROL_WRITE_FLUSH |
+ PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE);
intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT);
intel_ring_emit(ring, seqno);
intel_ring_emit(ring, 0);
@@ -430,8 +595,9 @@ pc_render_add_request(struct intel_ring_buffer *ring,
PIPE_CONTROL_FLUSH(ring, scratch_addr);
scratch_addr += 128;
PIPE_CONTROL_FLUSH(ring, scratch_addr);
- intel_ring_emit(ring, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE |
- PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH |
+ intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE |
+ PIPE_CONTROL_WRITE_FLUSH |
+ PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE |
PIPE_CONTROL_NOTIFY);
intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT);
intel_ring_emit(ring, seqno);
@@ -776,7 +942,8 @@ static int init_status_page(struct intel_ring_buffer *ring)
ret = -ENOMEM;
goto err;
}
- obj->cache_level = I915_CACHE_LLC;
+
+ i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
ret = i915_gem_object_pin(obj, 4096, true);
if (ret != 0) {
@@ -1020,7 +1187,12 @@ static const struct intel_ring_buffer render_ring = {
.irq_get = render_ring_get_irq,
.irq_put = render_ring_put_irq,
.dispatch_execbuffer = render_ring_dispatch_execbuffer,
- .cleanup = render_ring_cleanup,
+ .cleanup = render_ring_cleanup,
+ .sync_to = render_ring_sync_to,
+ .semaphore_register = {MI_SEMAPHORE_SYNC_INVALID,
+ MI_SEMAPHORE_SYNC_RV,
+ MI_SEMAPHORE_SYNC_RB},
+ .signal_mbox = {GEN6_VRSYNC, GEN6_BRSYNC},
};
/* ring buffer for bit-stream decoder */
@@ -1044,23 +1216,23 @@ static const struct intel_ring_buffer bsd_ring = {
static void gen6_bsd_ring_write_tail(struct intel_ring_buffer *ring,
u32 value)
{
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
+ drm_i915_private_t *dev_priv = ring->dev->dev_private;
/* Every tail move must follow the sequence below */
- I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL,
- GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK |
- GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_DISABLE);
- I915_WRITE(GEN6_BSD_RNCID, 0x0);
-
- if (wait_for((I915_READ(GEN6_BSD_SLEEP_PSMI_CONTROL) &
- GEN6_BSD_SLEEP_PSMI_CONTROL_IDLE_INDICATOR) == 0,
- 50))
- DRM_ERROR("timed out waiting for IDLE Indicator\n");
-
- I915_WRITE_TAIL(ring, value);
- I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL,
- GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK |
- GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE);
+ I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL,
+ GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK |
+ GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_DISABLE);
+ I915_WRITE(GEN6_BSD_RNCID, 0x0);
+
+ if (wait_for((I915_READ(GEN6_BSD_SLEEP_PSMI_CONTROL) &
+ GEN6_BSD_SLEEP_PSMI_CONTROL_IDLE_INDICATOR) == 0,
+ 50))
+ DRM_ERROR("timed out waiting for IDLE Indicator\n");
+
+ I915_WRITE_TAIL(ring, value);
+ I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL,
+ GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK |
+ GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE);
}
static int gen6_ring_flush(struct intel_ring_buffer *ring,
@@ -1088,18 +1260,18 @@ static int
gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
u32 offset, u32 len)
{
- int ret;
+ int ret;
- ret = intel_ring_begin(ring, 2);
- if (ret)
- return ret;
+ ret = intel_ring_begin(ring, 2);
+ if (ret)
+ return ret;
- intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965);
- /* bit0-7 is the length on GEN6+ */
- intel_ring_emit(ring, offset);
- intel_ring_advance(ring);
+ intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965);
+ /* bit0-7 is the length on GEN6+ */
+ intel_ring_emit(ring, offset);
+ intel_ring_advance(ring);
- return 0;
+ return 0;
}
static bool
@@ -1148,6 +1320,11 @@ static const struct intel_ring_buffer gen6_bsd_ring = {
.irq_get = gen6_bsd_ring_get_irq,
.irq_put = gen6_bsd_ring_put_irq,
.dispatch_execbuffer = gen6_ring_dispatch_execbuffer,
+ .sync_to = gen6_bsd_ring_sync_to,
+ .semaphore_register = {MI_SEMAPHORE_SYNC_VR,
+ MI_SEMAPHORE_SYNC_INVALID,
+ MI_SEMAPHORE_SYNC_VB},
+ .signal_mbox = {GEN6_RVSYNC, GEN6_BVSYNC},
};
/* Blitter support (SandyBridge+) */
@@ -1266,19 +1443,24 @@ static void blt_ring_cleanup(struct intel_ring_buffer *ring)
}
static const struct intel_ring_buffer gen6_blt_ring = {
- .name = "blt ring",
- .id = RING_BLT,
- .mmio_base = BLT_RING_BASE,
- .size = 32 * PAGE_SIZE,
- .init = blt_ring_init,
- .write_tail = ring_write_tail,
- .flush = blt_ring_flush,
- .add_request = gen6_add_request,
- .get_seqno = ring_get_seqno,
- .irq_get = blt_ring_get_irq,
- .irq_put = blt_ring_put_irq,
- .dispatch_execbuffer = gen6_ring_dispatch_execbuffer,
- .cleanup = blt_ring_cleanup,
+ .name = "blt ring",
+ .id = RING_BLT,
+ .mmio_base = BLT_RING_BASE,
+ .size = 32 * PAGE_SIZE,
+ .init = blt_ring_init,
+ .write_tail = ring_write_tail,
+ .flush = blt_ring_flush,
+ .add_request = gen6_add_request,
+ .get_seqno = ring_get_seqno,
+ .irq_get = blt_ring_get_irq,
+ .irq_put = blt_ring_put_irq,
+ .dispatch_execbuffer = gen6_ring_dispatch_execbuffer,
+ .cleanup = blt_ring_cleanup,
+ .sync_to = gen6_blt_ring_sync_to,
+ .semaphore_register = {MI_SEMAPHORE_SYNC_BR,
+ MI_SEMAPHORE_SYNC_BV,
+ MI_SEMAPHORE_SYNC_INVALID},
+ .signal_mbox = {GEN6_RBSYNC, GEN6_VBSYNC},
};
int intel_init_render_ring_buffer(struct drm_device *dev)
@@ -1289,6 +1471,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
*ring = render_ring;
if (INTEL_INFO(dev)->gen >= 6) {
ring->add_request = gen6_add_request;
+ ring->flush = gen6_render_ring_flush;
ring->irq_get = gen6_render_ring_get_irq;
ring->irq_put = gen6_render_ring_put_irq;
} else if (IS_GEN5(dev)) {
@@ -1319,6 +1502,9 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
ring->get_seqno = pc_render_get_seqno;
}
+ if (!I915_NEED_GFX_HWS(dev))
+ ring->status_page.page_addr = dev_priv->status_page_dmah->vaddr;
+
ring->dev = dev;
INIT_LIST_HEAD(&ring->active_list);
INIT_LIST_HEAD(&ring->request_list);
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index c0e0ee63fbf4..68281c96c558 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -2,10 +2,10 @@
#define _INTEL_RINGBUFFER_H_
enum {
- RCS = 0x0,
- VCS,
- BCS,
- I915_NUM_RINGS,
+ RCS = 0x0,
+ VCS,
+ BCS,
+ I915_NUM_RINGS,
};
struct intel_hw_status_page {
@@ -75,7 +75,12 @@ struct intel_ring_buffer {
int (*dispatch_execbuffer)(struct intel_ring_buffer *ring,
u32 offset, u32 length);
void (*cleanup)(struct intel_ring_buffer *ring);
+ int (*sync_to)(struct intel_ring_buffer *ring,
+ struct intel_ring_buffer *to,
+ u32 seqno);
+ u32 semaphore_register[3]; /*our mbox written by others */
+ u32 signal_mbox[2]; /* mboxes this ring signals to */
/**
* List of objects currently involved in rendering from the
* ringbuffer.
@@ -165,7 +170,7 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring);
int __must_check intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n);
static inline int intel_wait_ring_idle(struct intel_ring_buffer *ring)
{
- return intel_wait_ring_buffer(ring, ring->space - 8);
+ return intel_wait_ring_buffer(ring, ring->size - 8);
}
int __must_check intel_ring_begin(struct intel_ring_buffer *ring, int n);
@@ -180,9 +185,6 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring,
void intel_ring_advance(struct intel_ring_buffer *ring);
u32 intel_ring_get_seqno(struct intel_ring_buffer *ring);
-int intel_ring_sync(struct intel_ring_buffer *ring,
- struct intel_ring_buffer *to,
- u32 seqno);
int intel_init_render_ring_buffer(struct drm_device *dev);
int intel_init_bsd_ring_buffer(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 30fe554d8936..3003fb25aefd 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -28,6 +28,7 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/export.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
@@ -43,7 +44,7 @@
#define SDVO_TV_MASK (SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_SVID0)
#define SDVO_OUTPUT_MASK (SDVO_TMDS_MASK | SDVO_RGB_MASK | SDVO_LVDS_MASK |\
- SDVO_TV_MASK)
+ SDVO_TV_MASK)
#define IS_TV(c) (c->output_flag & SDVO_TV_MASK)
#define IS_TMDS(c) (c->output_flag & SDVO_TMDS_MASK)
@@ -92,6 +93,11 @@ struct intel_sdvo {
*/
uint16_t attached_output;
+ /*
+ * Hotplug activation bits for this device
+ */
+ uint8_t hotplug_active[2];
+
/**
* This is used to select the color range of RBG outputs in HDMI mode.
* It is only valid when using TMDS encoding and 8 bit per color mode.
@@ -283,117 +289,117 @@ static const struct _sdvo_cmd_name {
u8 cmd;
const char *name;
} sdvo_cmd_names[] = {
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS),
-
- /* Add the op code for SDVO enhancements */
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HPOS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HPOS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HPOS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_VPOS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_VPOS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_VPOS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SATURATION),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SATURATION),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SATURATION),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HUE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HUE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HUE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_CONTRAST),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CONTRAST),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTRAST),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_BRIGHTNESS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_BRIGHTNESS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_BRIGHTNESS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_H),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_H),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_H),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_V),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_V),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_V),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_ADAPTIVE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_ADAPTIVE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_ADAPTIVE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_2D),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_2D),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_2D),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SHARPNESS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SHARPNESS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SHARPNESS),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DOT_CRAWL),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DOT_CRAWL),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_CHROMA_FILTER),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_CHROMA_FILTER),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_CHROMA_FILTER),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_LUMA_FILTER),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_LUMA_FILTER),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_LUMA_FILTER),
-
- /* HDMI op code */
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA),
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS),
+
+ /* Add the op code for SDVO enhancements */
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HPOS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HPOS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HPOS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_VPOS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_VPOS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_VPOS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SATURATION),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SATURATION),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SATURATION),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HUE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HUE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HUE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_CONTRAST),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CONTRAST),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTRAST),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_BRIGHTNESS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_BRIGHTNESS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_BRIGHTNESS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_H),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_H),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_H),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_V),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_V),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_V),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_ADAPTIVE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_ADAPTIVE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_ADAPTIVE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_2D),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_2D),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_2D),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SHARPNESS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SHARPNESS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SHARPNESS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DOT_CRAWL),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DOT_CRAWL),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_CHROMA_FILTER),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_CHROMA_FILTER),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_CHROMA_FILTER),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_LUMA_FILTER),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_LUMA_FILTER),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_LUMA_FILTER),
+
+ /* HDMI op code */
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA),
};
#define IS_SDVOB(reg) (reg == SDVOB || reg == PCH_SDVOB)
@@ -1208,81 +1214,26 @@ static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct in
return true;
}
-/* No use! */
-#if 0
-struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB)
-{
- struct drm_connector *connector = NULL;
- struct intel_sdvo *iout = NULL;
- struct intel_sdvo *sdvo;
-
- /* find the sdvo connector */
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- iout = to_intel_sdvo(connector);
-
- if (iout->type != INTEL_OUTPUT_SDVO)
- continue;
-
- sdvo = iout->dev_priv;
-
- if (sdvo->sdvo_reg == SDVOB && sdvoB)
- return connector;
-
- if (sdvo->sdvo_reg == SDVOC && !sdvoB)
- return connector;
-
- }
-
- return NULL;
-}
-
-int intel_sdvo_supports_hotplug(struct drm_connector *connector)
+static int intel_sdvo_supports_hotplug(struct intel_sdvo *intel_sdvo)
{
u8 response[2];
- u8 status;
- struct intel_sdvo *intel_sdvo;
- DRM_DEBUG_KMS("\n");
-
- if (!connector)
- return 0;
-
- intel_sdvo = to_intel_sdvo(connector);
return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT,
&response, 2) && response[0];
}
-void intel_sdvo_set_hotplug(struct drm_connector *connector, int on)
+static void intel_sdvo_enable_hotplug(struct intel_encoder *encoder)
{
- u8 response[2];
- u8 status;
- struct intel_sdvo *intel_sdvo = to_intel_sdvo(connector);
-
- intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0);
- intel_sdvo_read_response(intel_sdvo, &response, 2);
-
- if (on) {
- intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0);
- status = intel_sdvo_read_response(intel_sdvo, &response, 2);
-
- intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
- } else {
- response[0] = 0;
- response[1] = 0;
- intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
- }
+ struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
- intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0);
- intel_sdvo_read_response(intel_sdvo, &response, 2);
+ intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &intel_sdvo->hotplug_active, 2);
}
-#endif
static bool
intel_sdvo_multifunc_encoder(struct intel_sdvo *intel_sdvo)
{
/* Is there more than one type of output? */
- int caps = intel_sdvo->caps.output_flags & 0xf;
- return caps & -caps;
+ return hweight16(intel_sdvo->caps.output_flags) > 1;
}
static struct edid *
@@ -1303,7 +1254,7 @@ intel_sdvo_get_analog_edid(struct drm_connector *connector)
}
enum drm_connector_status
-intel_sdvo_hdmi_sink_detect(struct drm_connector *connector)
+intel_sdvo_tmds_sink_detect(struct drm_connector *connector)
{
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
enum drm_connector_status status;
@@ -1398,7 +1349,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
if ((intel_sdvo_connector->output_flag & response) == 0)
ret = connector_status_disconnected;
else if (IS_TMDS(intel_sdvo_connector))
- ret = intel_sdvo_hdmi_sink_detect(connector);
+ ret = intel_sdvo_tmds_sink_detect(connector);
else {
struct edid *edid;
@@ -1945,7 +1896,7 @@ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv,
struct intel_sdvo *sdvo, u32 reg)
{
struct sdvo_device_mapping *mapping;
- u8 pin, speed;
+ u8 pin;
if (IS_SDVOB(reg))
mapping = &dev_priv->sdvo_mappings[0];
@@ -1953,18 +1904,16 @@ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv,
mapping = &dev_priv->sdvo_mappings[1];
pin = GMBUS_PORT_DPB;
- speed = GMBUS_RATE_1MHZ >> 8;
- if (mapping->initialized) {
+ if (mapping->initialized)
pin = mapping->i2c_pin;
- speed = mapping->i2c_speed;
- }
if (pin < GMBUS_NUM_PORTS) {
sdvo->i2c = &dev_priv->gmbus[pin].adapter;
- intel_gmbus_set_speed(sdvo->i2c, speed);
+ intel_gmbus_set_speed(sdvo->i2c, GMBUS_RATE_1MHZ);
intel_gmbus_force_bit(sdvo->i2c, true);
- } else
+ } else {
sdvo->i2c = &dev_priv->gmbus[GMBUS_PORT_DPB].adapter;
+ }
}
static bool
@@ -2045,6 +1994,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
{
struct drm_encoder *encoder = &intel_sdvo->base.base;
struct drm_connector *connector;
+ struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
struct intel_connector *intel_connector;
struct intel_sdvo_connector *intel_sdvo_connector;
@@ -2062,7 +2012,17 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
intel_connector = &intel_sdvo_connector->base;
connector = &intel_connector->base;
- connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+ if (intel_sdvo_supports_hotplug(intel_sdvo) & (1 << device)) {
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ intel_sdvo->hotplug_active[0] |= 1 << device;
+ /* Some SDVO devices have one-shot hotplug interrupts.
+ * Ensure that they get re-enabled when an interrupt happens.
+ */
+ intel_encoder->hot_plug = intel_sdvo_enable_hotplug;
+ intel_sdvo_enable_hotplug(intel_encoder);
+ }
+ else
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
connector->connector_type = DRM_MODE_CONNECTOR_DVID;
@@ -2244,7 +2204,7 @@ intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, uint16_t flags)
bytes[0], bytes[1]);
return false;
}
- intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1);
+ intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
return true;
}
@@ -2313,7 +2273,7 @@ static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo,
DRM_DEBUG_KMS(#name ": max %d, default %d, current %d\n", \
data_value[0], data_value[1], response); \
} \
-} while(0)
+} while (0)
static bool
intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo,
@@ -2480,7 +2440,7 @@ static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo,
if (IS_TV(intel_sdvo_connector))
return intel_sdvo_create_enhance_property_tv(intel_sdvo, intel_sdvo_connector, enhancements.reply);
- else if(IS_LVDS(intel_sdvo_connector))
+ else if (IS_LVDS(intel_sdvo_connector))
return intel_sdvo_create_enhance_property_lvds(intel_sdvo, intel_sdvo_connector, enhancements.reply);
else
return true;
@@ -2569,6 +2529,14 @@ bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg)
if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps))
goto err;
+ /* Set up hotplug command - note paranoia about contents of reply.
+ * We assume that the hardware is in a sane state, and only touch
+ * the bits we think we understand.
+ */
+ intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ACTIVE_HOT_PLUG,
+ &intel_sdvo->hotplug_active, 2);
+ intel_sdvo->hotplug_active[0] &= ~0x3;
+
if (intel_sdvo_output_setup(intel_sdvo,
intel_sdvo->caps.output_flags) != true) {
DRM_DEBUG_KMS("SDVO output failed to setup on SDVO%c\n",
diff --git a/drivers/gpu/drm/i915/intel_sdvo_regs.h b/drivers/gpu/drm/i915/intel_sdvo_regs.h
index 4f4e23bc2d16..4aa6f343e49a 100644
--- a/drivers/gpu/drm/i915/intel_sdvo_regs.h
+++ b/drivers/gpu/drm/i915/intel_sdvo_regs.h
@@ -46,63 +46,63 @@
#define SDVO_OUTPUT_LAST (14)
struct intel_sdvo_caps {
- u8 vendor_id;
- u8 device_id;
- u8 device_rev_id;
- u8 sdvo_version_major;
- u8 sdvo_version_minor;
- unsigned int sdvo_inputs_mask:2;
- unsigned int smooth_scaling:1;
- unsigned int sharp_scaling:1;
- unsigned int up_scaling:1;
- unsigned int down_scaling:1;
- unsigned int stall_support:1;
- unsigned int pad:1;
- u16 output_flags;
+ u8 vendor_id;
+ u8 device_id;
+ u8 device_rev_id;
+ u8 sdvo_version_major;
+ u8 sdvo_version_minor;
+ unsigned int sdvo_inputs_mask:2;
+ unsigned int smooth_scaling:1;
+ unsigned int sharp_scaling:1;
+ unsigned int up_scaling:1;
+ unsigned int down_scaling:1;
+ unsigned int stall_support:1;
+ unsigned int pad:1;
+ u16 output_flags;
} __attribute__((packed));
/** This matches the EDID DTD structure, more or less */
struct intel_sdvo_dtd {
- struct {
- u16 clock; /**< pixel clock, in 10kHz units */
- u8 h_active; /**< lower 8 bits (pixels) */
- u8 h_blank; /**< lower 8 bits (pixels) */
- u8 h_high; /**< upper 4 bits each h_active, h_blank */
- u8 v_active; /**< lower 8 bits (lines) */
- u8 v_blank; /**< lower 8 bits (lines) */
- u8 v_high; /**< upper 4 bits each v_active, v_blank */
- } part1;
-
- struct {
- u8 h_sync_off; /**< lower 8 bits, from hblank start */
- u8 h_sync_width; /**< lower 8 bits (pixels) */
- /** lower 4 bits each vsync offset, vsync width */
- u8 v_sync_off_width;
- /**
- * 2 high bits of hsync offset, 2 high bits of hsync width,
- * bits 4-5 of vsync offset, and 2 high bits of vsync width.
- */
- u8 sync_off_width_high;
- u8 dtd_flags;
- u8 sdvo_flags;
- /** bits 6-7 of vsync offset at bits 6-7 */
- u8 v_sync_off_high;
- u8 reserved;
- } part2;
+ struct {
+ u16 clock; /**< pixel clock, in 10kHz units */
+ u8 h_active; /**< lower 8 bits (pixels) */
+ u8 h_blank; /**< lower 8 bits (pixels) */
+ u8 h_high; /**< upper 4 bits each h_active, h_blank */
+ u8 v_active; /**< lower 8 bits (lines) */
+ u8 v_blank; /**< lower 8 bits (lines) */
+ u8 v_high; /**< upper 4 bits each v_active, v_blank */
+ } part1;
+
+ struct {
+ u8 h_sync_off; /**< lower 8 bits, from hblank start */
+ u8 h_sync_width; /**< lower 8 bits (pixels) */
+ /** lower 4 bits each vsync offset, vsync width */
+ u8 v_sync_off_width;
+ /**
+ * 2 high bits of hsync offset, 2 high bits of hsync width,
+ * bits 4-5 of vsync offset, and 2 high bits of vsync width.
+ */
+ u8 sync_off_width_high;
+ u8 dtd_flags;
+ u8 sdvo_flags;
+ /** bits 6-7 of vsync offset at bits 6-7 */
+ u8 v_sync_off_high;
+ u8 reserved;
+ } part2;
} __attribute__((packed));
struct intel_sdvo_pixel_clock_range {
- u16 min; /**< pixel clock, in 10kHz units */
- u16 max; /**< pixel clock, in 10kHz units */
+ u16 min; /**< pixel clock, in 10kHz units */
+ u16 max; /**< pixel clock, in 10kHz units */
} __attribute__((packed));
struct intel_sdvo_preferred_input_timing_args {
- u16 clock;
- u16 width;
- u16 height;
- u8 interlace:1;
- u8 scaled:1;
- u8 pad:6;
+ u16 clock;
+ u16 width;
+ u16 height;
+ u8 interlace:1;
+ u8 scaled:1;
+ u8 pad:6;
} __attribute__((packed));
/* I2C registers for SDVO */
@@ -154,9 +154,9 @@ struct intel_sdvo_preferred_input_timing_args {
*/
#define SDVO_CMD_GET_TRAINED_INPUTS 0x03
struct intel_sdvo_get_trained_inputs_response {
- unsigned int input0_trained:1;
- unsigned int input1_trained:1;
- unsigned int pad:6;
+ unsigned int input0_trained:1;
+ unsigned int input1_trained:1;
+ unsigned int pad:6;
} __attribute__((packed));
/** Returns a struct intel_sdvo_output_flags of active outputs. */
@@ -177,7 +177,7 @@ struct intel_sdvo_get_trained_inputs_response {
*/
#define SDVO_CMD_GET_IN_OUT_MAP 0x06
struct intel_sdvo_in_out_map {
- u16 in0, in1;
+ u16 in0, in1;
};
/**
@@ -210,10 +210,10 @@ struct intel_sdvo_in_out_map {
#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f
struct intel_sdvo_get_interrupt_event_source_response {
- u16 interrupt_status;
- unsigned int ambient_light_interrupt:1;
- unsigned int hdmi_audio_encrypt_change:1;
- unsigned int pad:6;
+ u16 interrupt_status;
+ unsigned int ambient_light_interrupt:1;
+ unsigned int hdmi_audio_encrypt_change:1;
+ unsigned int pad:6;
} __attribute__((packed));
/**
@@ -225,8 +225,8 @@ struct intel_sdvo_get_interrupt_event_source_response {
*/
#define SDVO_CMD_SET_TARGET_INPUT 0x10
struct intel_sdvo_set_target_input_args {
- unsigned int target_1:1;
- unsigned int pad:7;
+ unsigned int target_1:1;
+ unsigned int pad:7;
} __attribute__((packed));
/**
@@ -314,57 +314,57 @@ struct intel_sdvo_set_target_input_args {
#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27
/** 6 bytes of bit flags for TV formats shared by all TV format functions */
struct intel_sdvo_tv_format {
- unsigned int ntsc_m:1;
- unsigned int ntsc_j:1;
- unsigned int ntsc_443:1;
- unsigned int pal_b:1;
- unsigned int pal_d:1;
- unsigned int pal_g:1;
- unsigned int pal_h:1;
- unsigned int pal_i:1;
-
- unsigned int pal_m:1;
- unsigned int pal_n:1;
- unsigned int pal_nc:1;
- unsigned int pal_60:1;
- unsigned int secam_b:1;
- unsigned int secam_d:1;
- unsigned int secam_g:1;
- unsigned int secam_k:1;
-
- unsigned int secam_k1:1;
- unsigned int secam_l:1;
- unsigned int secam_60:1;
- unsigned int hdtv_std_smpte_240m_1080i_59:1;
- unsigned int hdtv_std_smpte_240m_1080i_60:1;
- unsigned int hdtv_std_smpte_260m_1080i_59:1;
- unsigned int hdtv_std_smpte_260m_1080i_60:1;
- unsigned int hdtv_std_smpte_274m_1080i_50:1;
-
- unsigned int hdtv_std_smpte_274m_1080i_59:1;
- unsigned int hdtv_std_smpte_274m_1080i_60:1;
- unsigned int hdtv_std_smpte_274m_1080p_23:1;
- unsigned int hdtv_std_smpte_274m_1080p_24:1;
- unsigned int hdtv_std_smpte_274m_1080p_25:1;
- unsigned int hdtv_std_smpte_274m_1080p_29:1;
- unsigned int hdtv_std_smpte_274m_1080p_30:1;
- unsigned int hdtv_std_smpte_274m_1080p_50:1;
-
- unsigned int hdtv_std_smpte_274m_1080p_59:1;
- unsigned int hdtv_std_smpte_274m_1080p_60:1;
- unsigned int hdtv_std_smpte_295m_1080i_50:1;
- unsigned int hdtv_std_smpte_295m_1080p_50:1;
- unsigned int hdtv_std_smpte_296m_720p_59:1;
- unsigned int hdtv_std_smpte_296m_720p_60:1;
- unsigned int hdtv_std_smpte_296m_720p_50:1;
- unsigned int hdtv_std_smpte_293m_480p_59:1;
-
- unsigned int hdtv_std_smpte_170m_480i_59:1;
- unsigned int hdtv_std_iturbt601_576i_50:1;
- unsigned int hdtv_std_iturbt601_576p_50:1;
- unsigned int hdtv_std_eia_7702a_480i_60:1;
- unsigned int hdtv_std_eia_7702a_480p_60:1;
- unsigned int pad:3;
+ unsigned int ntsc_m:1;
+ unsigned int ntsc_j:1;
+ unsigned int ntsc_443:1;
+ unsigned int pal_b:1;
+ unsigned int pal_d:1;
+ unsigned int pal_g:1;
+ unsigned int pal_h:1;
+ unsigned int pal_i:1;
+
+ unsigned int pal_m:1;
+ unsigned int pal_n:1;
+ unsigned int pal_nc:1;
+ unsigned int pal_60:1;
+ unsigned int secam_b:1;
+ unsigned int secam_d:1;
+ unsigned int secam_g:1;
+ unsigned int secam_k:1;
+
+ unsigned int secam_k1:1;
+ unsigned int secam_l:1;
+ unsigned int secam_60:1;
+ unsigned int hdtv_std_smpte_240m_1080i_59:1;
+ unsigned int hdtv_std_smpte_240m_1080i_60:1;
+ unsigned int hdtv_std_smpte_260m_1080i_59:1;
+ unsigned int hdtv_std_smpte_260m_1080i_60:1;
+ unsigned int hdtv_std_smpte_274m_1080i_50:1;
+
+ unsigned int hdtv_std_smpte_274m_1080i_59:1;
+ unsigned int hdtv_std_smpte_274m_1080i_60:1;
+ unsigned int hdtv_std_smpte_274m_1080p_23:1;
+ unsigned int hdtv_std_smpte_274m_1080p_24:1;
+ unsigned int hdtv_std_smpte_274m_1080p_25:1;
+ unsigned int hdtv_std_smpte_274m_1080p_29:1;
+ unsigned int hdtv_std_smpte_274m_1080p_30:1;
+ unsigned int hdtv_std_smpte_274m_1080p_50:1;
+
+ unsigned int hdtv_std_smpte_274m_1080p_59:1;
+ unsigned int hdtv_std_smpte_274m_1080p_60:1;
+ unsigned int hdtv_std_smpte_295m_1080i_50:1;
+ unsigned int hdtv_std_smpte_295m_1080p_50:1;
+ unsigned int hdtv_std_smpte_296m_720p_59:1;
+ unsigned int hdtv_std_smpte_296m_720p_60:1;
+ unsigned int hdtv_std_smpte_296m_720p_50:1;
+ unsigned int hdtv_std_smpte_293m_480p_59:1;
+
+ unsigned int hdtv_std_smpte_170m_480i_59:1;
+ unsigned int hdtv_std_iturbt601_576i_50:1;
+ unsigned int hdtv_std_iturbt601_576p_50:1;
+ unsigned int hdtv_std_eia_7702a_480i_60:1;
+ unsigned int hdtv_std_eia_7702a_480p_60:1;
+ unsigned int pad:3;
} __attribute__((packed));
#define SDVO_CMD_GET_TV_FORMAT 0x28
@@ -374,53 +374,53 @@ struct intel_sdvo_tv_format {
/** Returns the resolutiosn that can be used with the given TV format */
#define SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT 0x83
struct intel_sdvo_sdtv_resolution_request {
- unsigned int ntsc_m:1;
- unsigned int ntsc_j:1;
- unsigned int ntsc_443:1;
- unsigned int pal_b:1;
- unsigned int pal_d:1;
- unsigned int pal_g:1;
- unsigned int pal_h:1;
- unsigned int pal_i:1;
-
- unsigned int pal_m:1;
- unsigned int pal_n:1;
- unsigned int pal_nc:1;
- unsigned int pal_60:1;
- unsigned int secam_b:1;
- unsigned int secam_d:1;
- unsigned int secam_g:1;
- unsigned int secam_k:1;
-
- unsigned int secam_k1:1;
- unsigned int secam_l:1;
- unsigned int secam_60:1;
- unsigned int pad:5;
+ unsigned int ntsc_m:1;
+ unsigned int ntsc_j:1;
+ unsigned int ntsc_443:1;
+ unsigned int pal_b:1;
+ unsigned int pal_d:1;
+ unsigned int pal_g:1;
+ unsigned int pal_h:1;
+ unsigned int pal_i:1;
+
+ unsigned int pal_m:1;
+ unsigned int pal_n:1;
+ unsigned int pal_nc:1;
+ unsigned int pal_60:1;
+ unsigned int secam_b:1;
+ unsigned int secam_d:1;
+ unsigned int secam_g:1;
+ unsigned int secam_k:1;
+
+ unsigned int secam_k1:1;
+ unsigned int secam_l:1;
+ unsigned int secam_60:1;
+ unsigned int pad:5;
} __attribute__((packed));
struct intel_sdvo_sdtv_resolution_reply {
- unsigned int res_320x200:1;
- unsigned int res_320x240:1;
- unsigned int res_400x300:1;
- unsigned int res_640x350:1;
- unsigned int res_640x400:1;
- unsigned int res_640x480:1;
- unsigned int res_704x480:1;
- unsigned int res_704x576:1;
-
- unsigned int res_720x350:1;
- unsigned int res_720x400:1;
- unsigned int res_720x480:1;
- unsigned int res_720x540:1;
- unsigned int res_720x576:1;
- unsigned int res_768x576:1;
- unsigned int res_800x600:1;
- unsigned int res_832x624:1;
-
- unsigned int res_920x766:1;
- unsigned int res_1024x768:1;
- unsigned int res_1280x1024:1;
- unsigned int pad:5;
+ unsigned int res_320x200:1;
+ unsigned int res_320x240:1;
+ unsigned int res_400x300:1;
+ unsigned int res_640x350:1;
+ unsigned int res_640x400:1;
+ unsigned int res_640x480:1;
+ unsigned int res_704x480:1;
+ unsigned int res_704x576:1;
+
+ unsigned int res_720x350:1;
+ unsigned int res_720x400:1;
+ unsigned int res_720x480:1;
+ unsigned int res_720x540:1;
+ unsigned int res_720x576:1;
+ unsigned int res_768x576:1;
+ unsigned int res_800x600:1;
+ unsigned int res_832x624:1;
+
+ unsigned int res_920x766:1;
+ unsigned int res_1024x768:1;
+ unsigned int res_1280x1024:1;
+ unsigned int pad:5;
} __attribute__((packed));
/* Get supported resolution with squire pixel aspect ratio that can be
@@ -428,90 +428,90 @@ struct intel_sdvo_sdtv_resolution_reply {
#define SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT 0x85
struct intel_sdvo_hdtv_resolution_request {
- unsigned int hdtv_std_smpte_240m_1080i_59:1;
- unsigned int hdtv_std_smpte_240m_1080i_60:1;
- unsigned int hdtv_std_smpte_260m_1080i_59:1;
- unsigned int hdtv_std_smpte_260m_1080i_60:1;
- unsigned int hdtv_std_smpte_274m_1080i_50:1;
- unsigned int hdtv_std_smpte_274m_1080i_59:1;
- unsigned int hdtv_std_smpte_274m_1080i_60:1;
- unsigned int hdtv_std_smpte_274m_1080p_23:1;
-
- unsigned int hdtv_std_smpte_274m_1080p_24:1;
- unsigned int hdtv_std_smpte_274m_1080p_25:1;
- unsigned int hdtv_std_smpte_274m_1080p_29:1;
- unsigned int hdtv_std_smpte_274m_1080p_30:1;
- unsigned int hdtv_std_smpte_274m_1080p_50:1;
- unsigned int hdtv_std_smpte_274m_1080p_59:1;
- unsigned int hdtv_std_smpte_274m_1080p_60:1;
- unsigned int hdtv_std_smpte_295m_1080i_50:1;
-
- unsigned int hdtv_std_smpte_295m_1080p_50:1;
- unsigned int hdtv_std_smpte_296m_720p_59:1;
- unsigned int hdtv_std_smpte_296m_720p_60:1;
- unsigned int hdtv_std_smpte_296m_720p_50:1;
- unsigned int hdtv_std_smpte_293m_480p_59:1;
- unsigned int hdtv_std_smpte_170m_480i_59:1;
- unsigned int hdtv_std_iturbt601_576i_50:1;
- unsigned int hdtv_std_iturbt601_576p_50:1;
-
- unsigned int hdtv_std_eia_7702a_480i_60:1;
- unsigned int hdtv_std_eia_7702a_480p_60:1;
- unsigned int pad:6;
+ unsigned int hdtv_std_smpte_240m_1080i_59:1;
+ unsigned int hdtv_std_smpte_240m_1080i_60:1;
+ unsigned int hdtv_std_smpte_260m_1080i_59:1;
+ unsigned int hdtv_std_smpte_260m_1080i_60:1;
+ unsigned int hdtv_std_smpte_274m_1080i_50:1;
+ unsigned int hdtv_std_smpte_274m_1080i_59:1;
+ unsigned int hdtv_std_smpte_274m_1080i_60:1;
+ unsigned int hdtv_std_smpte_274m_1080p_23:1;
+
+ unsigned int hdtv_std_smpte_274m_1080p_24:1;
+ unsigned int hdtv_std_smpte_274m_1080p_25:1;
+ unsigned int hdtv_std_smpte_274m_1080p_29:1;
+ unsigned int hdtv_std_smpte_274m_1080p_30:1;
+ unsigned int hdtv_std_smpte_274m_1080p_50:1;
+ unsigned int hdtv_std_smpte_274m_1080p_59:1;
+ unsigned int hdtv_std_smpte_274m_1080p_60:1;
+ unsigned int hdtv_std_smpte_295m_1080i_50:1;
+
+ unsigned int hdtv_std_smpte_295m_1080p_50:1;
+ unsigned int hdtv_std_smpte_296m_720p_59:1;
+ unsigned int hdtv_std_smpte_296m_720p_60:1;
+ unsigned int hdtv_std_smpte_296m_720p_50:1;
+ unsigned int hdtv_std_smpte_293m_480p_59:1;
+ unsigned int hdtv_std_smpte_170m_480i_59:1;
+ unsigned int hdtv_std_iturbt601_576i_50:1;
+ unsigned int hdtv_std_iturbt601_576p_50:1;
+
+ unsigned int hdtv_std_eia_7702a_480i_60:1;
+ unsigned int hdtv_std_eia_7702a_480p_60:1;
+ unsigned int pad:6;
} __attribute__((packed));
struct intel_sdvo_hdtv_resolution_reply {
- unsigned int res_640x480:1;
- unsigned int res_800x600:1;
- unsigned int res_1024x768:1;
- unsigned int res_1280x960:1;
- unsigned int res_1400x1050:1;
- unsigned int res_1600x1200:1;
- unsigned int res_1920x1440:1;
- unsigned int res_2048x1536:1;
-
- unsigned int res_2560x1920:1;
- unsigned int res_3200x2400:1;
- unsigned int res_3840x2880:1;
- unsigned int pad1:5;
-
- unsigned int res_848x480:1;
- unsigned int res_1064x600:1;
- unsigned int res_1280x720:1;
- unsigned int res_1360x768:1;
- unsigned int res_1704x960:1;
- unsigned int res_1864x1050:1;
- unsigned int res_1920x1080:1;
- unsigned int res_2128x1200:1;
-
- unsigned int res_2560x1400:1;
- unsigned int res_2728x1536:1;
- unsigned int res_3408x1920:1;
- unsigned int res_4264x2400:1;
- unsigned int res_5120x2880:1;
- unsigned int pad2:3;
-
- unsigned int res_768x480:1;
- unsigned int res_960x600:1;
- unsigned int res_1152x720:1;
- unsigned int res_1124x768:1;
- unsigned int res_1536x960:1;
- unsigned int res_1680x1050:1;
- unsigned int res_1728x1080:1;
- unsigned int res_1920x1200:1;
-
- unsigned int res_2304x1440:1;
- unsigned int res_2456x1536:1;
- unsigned int res_3072x1920:1;
- unsigned int res_3840x2400:1;
- unsigned int res_4608x2880:1;
- unsigned int pad3:3;
-
- unsigned int res_1280x1024:1;
- unsigned int pad4:7;
-
- unsigned int res_1280x768:1;
- unsigned int pad5:7;
+ unsigned int res_640x480:1;
+ unsigned int res_800x600:1;
+ unsigned int res_1024x768:1;
+ unsigned int res_1280x960:1;
+ unsigned int res_1400x1050:1;
+ unsigned int res_1600x1200:1;
+ unsigned int res_1920x1440:1;
+ unsigned int res_2048x1536:1;
+
+ unsigned int res_2560x1920:1;
+ unsigned int res_3200x2400:1;
+ unsigned int res_3840x2880:1;
+ unsigned int pad1:5;
+
+ unsigned int res_848x480:1;
+ unsigned int res_1064x600:1;
+ unsigned int res_1280x720:1;
+ unsigned int res_1360x768:1;
+ unsigned int res_1704x960:1;
+ unsigned int res_1864x1050:1;
+ unsigned int res_1920x1080:1;
+ unsigned int res_2128x1200:1;
+
+ unsigned int res_2560x1400:1;
+ unsigned int res_2728x1536:1;
+ unsigned int res_3408x1920:1;
+ unsigned int res_4264x2400:1;
+ unsigned int res_5120x2880:1;
+ unsigned int pad2:3;
+
+ unsigned int res_768x480:1;
+ unsigned int res_960x600:1;
+ unsigned int res_1152x720:1;
+ unsigned int res_1124x768:1;
+ unsigned int res_1536x960:1;
+ unsigned int res_1680x1050:1;
+ unsigned int res_1728x1080:1;
+ unsigned int res_1920x1200:1;
+
+ unsigned int res_2304x1440:1;
+ unsigned int res_2456x1536:1;
+ unsigned int res_3072x1920:1;
+ unsigned int res_3840x2400:1;
+ unsigned int res_4608x2880:1;
+ unsigned int pad3:3;
+
+ unsigned int res_1280x1024:1;
+ unsigned int pad4:7;
+
+ unsigned int res_1280x768:1;
+ unsigned int pad5:7;
} __attribute__((packed));
/* Get supported power state returns info for encoder and monitor, rely on
@@ -539,25 +539,25 @@ struct intel_sdvo_hdtv_resolution_reply {
* The high fields are bits 8:9 of the 10-bit values.
*/
struct sdvo_panel_power_sequencing {
- u8 t0;
- u8 t1;
- u8 t2;
- u8 t3;
- u8 t4;
-
- unsigned int t0_high:2;
- unsigned int t1_high:2;
- unsigned int t2_high:2;
- unsigned int t3_high:2;
-
- unsigned int t4_high:2;
- unsigned int pad:6;
+ u8 t0;
+ u8 t1;
+ u8 t2;
+ u8 t3;
+ u8 t4;
+
+ unsigned int t0_high:2;
+ unsigned int t1_high:2;
+ unsigned int t2_high:2;
+ unsigned int t3_high:2;
+
+ unsigned int t4_high:2;
+ unsigned int pad:6;
} __attribute__((packed));
#define SDVO_CMD_GET_MAX_BACKLIGHT_LEVEL 0x30
struct sdvo_max_backlight_reply {
- u8 max_value;
- u8 default_value;
+ u8 max_value;
+ u8 default_value;
} __attribute__((packed));
#define SDVO_CMD_GET_BACKLIGHT_LEVEL 0x31
@@ -565,16 +565,16 @@ struct sdvo_max_backlight_reply {
#define SDVO_CMD_GET_AMBIENT_LIGHT 0x33
struct sdvo_get_ambient_light_reply {
- u16 trip_low;
- u16 trip_high;
- u16 value;
+ u16 trip_low;
+ u16 trip_high;
+ u16 value;
} __attribute__((packed));
#define SDVO_CMD_SET_AMBIENT_LIGHT 0x34
struct sdvo_set_ambient_light_reply {
- u16 trip_low;
- u16 trip_high;
- unsigned int enable:1;
- unsigned int pad:7;
+ u16 trip_low;
+ u16 trip_high;
+ unsigned int enable:1;
+ unsigned int pad:7;
} __attribute__((packed));
/* Set display power state */
@@ -586,23 +586,23 @@ struct sdvo_set_ambient_light_reply {
#define SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS 0x84
struct intel_sdvo_enhancements_reply {
- unsigned int flicker_filter:1;
- unsigned int flicker_filter_adaptive:1;
- unsigned int flicker_filter_2d:1;
- unsigned int saturation:1;
- unsigned int hue:1;
- unsigned int brightness:1;
- unsigned int contrast:1;
- unsigned int overscan_h:1;
-
- unsigned int overscan_v:1;
- unsigned int hpos:1;
- unsigned int vpos:1;
- unsigned int sharpness:1;
- unsigned int dot_crawl:1;
- unsigned int dither:1;
- unsigned int tv_chroma_filter:1;
- unsigned int tv_luma_filter:1;
+ unsigned int flicker_filter:1;
+ unsigned int flicker_filter_adaptive:1;
+ unsigned int flicker_filter_2d:1;
+ unsigned int saturation:1;
+ unsigned int hue:1;
+ unsigned int brightness:1;
+ unsigned int contrast:1;
+ unsigned int overscan_h:1;
+
+ unsigned int overscan_v:1;
+ unsigned int hpos:1;
+ unsigned int vpos:1;
+ unsigned int sharpness:1;
+ unsigned int dot_crawl:1;
+ unsigned int dither:1;
+ unsigned int tv_chroma_filter:1;
+ unsigned int tv_luma_filter:1;
} __attribute__((packed));
/* Picture enhancement limits below are dependent on the current TV format,
@@ -623,8 +623,8 @@ struct intel_sdvo_enhancements_reply {
#define SDVO_CMD_GET_MAX_TV_CHROMA_FILTER 0x74
#define SDVO_CMD_GET_MAX_TV_LUMA_FILTER 0x77
struct intel_sdvo_enhancement_limits_reply {
- u16 max_value;
- u16 default_value;
+ u16 max_value;
+ u16 default_value;
} __attribute__((packed));
#define SDVO_CMD_GET_LVDS_PANEL_INFORMATION 0x7f
@@ -665,8 +665,8 @@ struct intel_sdvo_enhancement_limits_reply {
#define SDVO_CMD_GET_TV_LUMA_FILTER 0x78
#define SDVO_CMD_SET_TV_LUMA_FILTER 0x79
struct intel_sdvo_enhancements_arg {
- u16 value;
-}__attribute__((packed));
+ u16 value;
+} __attribute__((packed));
#define SDVO_CMD_GET_DOT_CRAWL 0x70
#define SDVO_CMD_SET_DOT_CRAWL 0x71
@@ -717,7 +717,7 @@ struct intel_sdvo_enhancements_arg {
#define SDVO_CMD_GET_AUDIO_TX_INFO 0x9c
#define SDVO_NEED_TO_STALL (1 << 7)
-struct intel_sdvo_encode{
- u8 dvi_rev;
- u8 hdmi_rev;
+struct intel_sdvo_encode {
+ u8 dvi_rev;
+ u8 hdmi_rev;
} __attribute__ ((packed));
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 113e4e7264cd..f3c6a9a8b081 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -194,10 +194,10 @@ static const u32 filter_table[] = {
*
* if (f >= 1) {
* exp = 0x7;
- * mant = 1 << 8;
+ * mant = 1 << 8;
* } else {
* for (exp = 0; exp < 3 && f < 0.5; exp++)
- * f *= 2.0;
+ * f *= 2.0;
* mant = (f * (1 << 9) + 0.5);
* if (mant >= (1 << 9))
* mant = (1 << 9) - 1;
@@ -430,7 +430,7 @@ static const struct tv_mode tv_modes[] = {
.vsync_start_f1 = 6, .vsync_start_f2 = 7,
.vsync_len = 6,
- .veq_ena = true, .veq_start_f1 = 0,
+ .veq_ena = true, .veq_start_f1 = 0,
.veq_start_f2 = 1, .veq_len = 18,
.vi_end_f1 = 20, .vi_end_f2 = 21,
@@ -472,7 +472,7 @@ static const struct tv_mode tv_modes[] = {
.vsync_start_f1 = 6, .vsync_start_f2 = 7,
.vsync_len = 6,
- .veq_ena = true, .veq_start_f1 = 0,
+ .veq_ena = true, .veq_start_f1 = 0,
.veq_start_f2 = 1, .veq_len = 18,
.vi_end_f1 = 20, .vi_end_f2 = 21,
@@ -515,7 +515,7 @@ static const struct tv_mode tv_modes[] = {
.vsync_start_f1 = 6, .vsync_start_f2 = 7,
.vsync_len = 6,
- .veq_ena = true, .veq_start_f1 = 0,
+ .veq_ena = true, .veq_start_f1 = 0,
.veq_start_f2 = 1, .veq_len = 18,
.vi_end_f1 = 20, .vi_end_f2 = 21,
@@ -558,7 +558,7 @@ static const struct tv_mode tv_modes[] = {
.vsync_start_f1 = 6, .vsync_start_f2 = 7,
.vsync_len = 6,
- .veq_ena = true, .veq_start_f1 = 0,
+ .veq_ena = true, .veq_start_f1 = 0,
.veq_start_f2 = 1, .veq_len = 18,
.vi_end_f1 = 20, .vi_end_f2 = 21,
@@ -602,14 +602,14 @@ static const struct tv_mode tv_modes[] = {
.vsync_start_f1 = 6, .vsync_start_f2 = 7,
.vsync_len = 6,
- .veq_ena = true, .veq_start_f1 = 0,
+ .veq_ena = true, .veq_start_f1 = 0,
.veq_start_f2 = 1, .veq_len = 18,
.vi_end_f1 = 24, .vi_end_f2 = 25,
.nbr_end = 286,
.burst_ena = true,
- .hburst_start = 73, .hburst_len = 34,
+ .hburst_start = 73, .hburst_len = 34,
.vburst_start_f1 = 8, .vburst_end_f1 = 285,
.vburst_start_f2 = 8, .vburst_end_f2 = 286,
.vburst_start_f3 = 9, .vburst_end_f3 = 286,
@@ -646,7 +646,7 @@ static const struct tv_mode tv_modes[] = {
.vsync_start_f1 = 5, .vsync_start_f2 = 6,
.vsync_len = 5,
- .veq_ena = true, .veq_start_f1 = 0,
+ .veq_ena = true, .veq_start_f1 = 0,
.veq_start_f2 = 1, .veq_len = 15,
.vi_end_f1 = 24, .vi_end_f2 = 25,
@@ -675,7 +675,7 @@ static const struct tv_mode tv_modes[] = {
},
{
.name = "480p@59.94Hz",
- .clock = 107520,
+ .clock = 107520,
.refresh = 59940,
.oversample = TV_OVERSAMPLE_4X,
.component_only = 1,
@@ -683,7 +683,7 @@ static const struct tv_mode tv_modes[] = {
.hsync_end = 64, .hblank_end = 122,
.hblank_start = 842, .htotal = 857,
- .progressive = true,.trilevel_sync = false,
+ .progressive = true, .trilevel_sync = false,
.vsync_start_f1 = 12, .vsync_start_f2 = 12,
.vsync_len = 12,
@@ -699,7 +699,7 @@ static const struct tv_mode tv_modes[] = {
},
{
.name = "480p@60Hz",
- .clock = 107520,
+ .clock = 107520,
.refresh = 60000,
.oversample = TV_OVERSAMPLE_4X,
.component_only = 1,
@@ -707,7 +707,7 @@ static const struct tv_mode tv_modes[] = {
.hsync_end = 64, .hblank_end = 122,
.hblank_start = 842, .htotal = 856,
- .progressive = true,.trilevel_sync = false,
+ .progressive = true, .trilevel_sync = false,
.vsync_start_f1 = 12, .vsync_start_f2 = 12,
.vsync_len = 12,
@@ -723,7 +723,7 @@ static const struct tv_mode tv_modes[] = {
},
{
.name = "576p",
- .clock = 107520,
+ .clock = 107520,
.refresh = 50000,
.oversample = TV_OVERSAMPLE_4X,
.component_only = 1,
@@ -755,7 +755,7 @@ static const struct tv_mode tv_modes[] = {
.hsync_end = 80, .hblank_end = 300,
.hblank_start = 1580, .htotal = 1649,
- .progressive = true, .trilevel_sync = true,
+ .progressive = true, .trilevel_sync = true,
.vsync_start_f1 = 10, .vsync_start_f2 = 10,
.vsync_len = 10,
@@ -779,7 +779,7 @@ static const struct tv_mode tv_modes[] = {
.hsync_end = 80, .hblank_end = 300,
.hblank_start = 1580, .htotal = 1651,
- .progressive = true, .trilevel_sync = true,
+ .progressive = true, .trilevel_sync = true,
.vsync_start_f1 = 10, .vsync_start_f2 = 10,
.vsync_len = 10,
@@ -803,7 +803,7 @@ static const struct tv_mode tv_modes[] = {
.hsync_end = 80, .hblank_end = 300,
.hblank_start = 1580, .htotal = 1979,
- .progressive = true, .trilevel_sync = true,
+ .progressive = true, .trilevel_sync = true,
.vsync_start_f1 = 10, .vsync_start_f2 = 10,
.vsync_len = 10,
@@ -828,12 +828,12 @@ static const struct tv_mode tv_modes[] = {
.hsync_end = 88, .hblank_end = 235,
.hblank_start = 2155, .htotal = 2639,
- .progressive = false, .trilevel_sync = true,
+ .progressive = false, .trilevel_sync = true,
.vsync_start_f1 = 4, .vsync_start_f2 = 5,
.vsync_len = 10,
- .veq_ena = true, .veq_start_f1 = 4,
+ .veq_ena = true, .veq_start_f1 = 4,
.veq_start_f2 = 4, .veq_len = 10,
@@ -854,12 +854,12 @@ static const struct tv_mode tv_modes[] = {
.hsync_end = 88, .hblank_end = 235,
.hblank_start = 2155, .htotal = 2199,
- .progressive = false, .trilevel_sync = true,
+ .progressive = false, .trilevel_sync = true,
.vsync_start_f1 = 4, .vsync_start_f2 = 5,
.vsync_len = 10,
- .veq_ena = true, .veq_start_f1 = 4,
+ .veq_ena = true, .veq_start_f1 = 4,
.veq_start_f2 = 4, .veq_len = 10,
@@ -880,16 +880,16 @@ static const struct tv_mode tv_modes[] = {
.hsync_end = 88, .hblank_end = 235,
.hblank_start = 2155, .htotal = 2201,
- .progressive = false, .trilevel_sync = true,
+ .progressive = false, .trilevel_sync = true,
.vsync_start_f1 = 4, .vsync_start_f2 = 5,
.vsync_len = 10,
.veq_ena = true, .veq_start_f1 = 4,
- .veq_start_f2 = 4, .veq_len = 10,
+ .veq_start_f2 = 4, .veq_len = 10,
- .vi_end_f1 = 21, .vi_end_f2 = 22,
+ .vi_end_f1 = 21, .vi_end_f2 = 22,
.nbr_end = 539,
.burst_ena = false,
@@ -916,7 +916,7 @@ intel_tv_dpms(struct drm_encoder *encoder, int mode)
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- switch(mode) {
+ switch (mode) {
case DRM_MODE_DPMS_ON:
I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
break;
@@ -933,7 +933,7 @@ intel_tv_mode_lookup(const char *tv_format)
{
int i;
- for (i = 0; i < sizeof(tv_modes) / sizeof (tv_modes[0]); i++) {
+ for (i = 0; i < sizeof(tv_modes) / sizeof(tv_modes[0]); i++) {
const struct tv_mode *tv_mode = &tv_modes[i];
if (!strcmp(tv_format, tv_mode->name))
@@ -1128,7 +1128,7 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
if (color_conversion) {
I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
color_conversion->gy);
- I915_WRITE(TV_CSC_Y2,(color_conversion->by << 16) |
+ I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
color_conversion->ay);
I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
color_conversion->gu);
@@ -1232,10 +1232,12 @@ static const struct drm_display_mode reported_modes[] = {
* \return false if TV is disconnected.
*/
static int
-intel_tv_detect_type (struct intel_tv *intel_tv,
+intel_tv_detect_type(struct intel_tv *intel_tv,
struct drm_connector *connector)
{
struct drm_encoder *encoder = &intel_tv->base.base;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
@@ -1258,6 +1260,10 @@ intel_tv_detect_type (struct intel_tv *intel_tv,
/* Poll for TV detection */
tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK);
tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
+ if (intel_crtc->pipe == 1)
+ tv_ctl |= TV_ENC_PIPEB_SELECT;
+ else
+ tv_ctl &= ~TV_ENC_PIPEB_SELECT;
tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
tv_dac |= (TVDAC_STATE_CHG_EN |
@@ -1277,26 +1283,26 @@ intel_tv_detect_type (struct intel_tv *intel_tv,
to_intel_crtc(intel_tv->base.base.crtc)->pipe);
type = -1;
- if (wait_for((tv_dac = I915_READ(TV_DAC)) & TVDAC_STATE_CHG, 20) == 0) {
- DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
- /*
- * A B C
- * 0 1 1 Composite
- * 1 0 X svideo
- * 0 0 0 Component
- */
- if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
- DRM_DEBUG_KMS("Detected Composite TV connection\n");
- type = DRM_MODE_CONNECTOR_Composite;
- } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
- DRM_DEBUG_KMS("Detected S-Video TV connection\n");
- type = DRM_MODE_CONNECTOR_SVIDEO;
- } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
- DRM_DEBUG_KMS("Detected Component TV connection\n");
- type = DRM_MODE_CONNECTOR_Component;
- } else {
- DRM_DEBUG_KMS("Unrecognised TV connection\n");
- }
+ tv_dac = I915_READ(TV_DAC);
+ DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
+ /*
+ * A B C
+ * 0 1 1 Composite
+ * 1 0 X svideo
+ * 0 0 0 Component
+ */
+ if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
+ DRM_DEBUG_KMS("Detected Composite TV connection\n");
+ type = DRM_MODE_CONNECTOR_Composite;
+ } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
+ DRM_DEBUG_KMS("Detected S-Video TV connection\n");
+ type = DRM_MODE_CONNECTOR_SVIDEO;
+ } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
+ DRM_DEBUG_KMS("Detected Component TV connection\n");
+ type = DRM_MODE_CONNECTOR_Component;
+ } else {
+ DRM_DEBUG_KMS("Unrecognised TV connection\n");
+ type = -1;
}
I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
@@ -1480,7 +1486,7 @@ intel_tv_get_modes(struct drm_connector *connector)
}
static void
-intel_tv_destroy (struct drm_connector *connector)
+intel_tv_destroy(struct drm_connector *connector)
{
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c
index 42d31874edf2..33daa29eea66 100644
--- a/drivers/gpu/drm/mga/mga_drv.c
+++ b/drivers/gpu/drm/mga/mga_drv.c
@@ -29,6 +29,8 @@
* Gareth Hughes <gareth@valinux.com>
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "drm.h"
#include "mga_drm.h"
diff --git a/drivers/gpu/drm/mga/mga_warp.c b/drivers/gpu/drm/mga/mga_warp.c
index f172bd5c257f..722a91b69b0c 100644
--- a/drivers/gpu/drm/mga/mga_warp.c
+++ b/drivers/gpu/drm/mga/mga_warp.c
@@ -30,6 +30,7 @@
#include <linux/firmware.h>
#include <linux/ihex.h>
#include <linux/platform_device.h>
+#include <linux/module.h>
#include "drmP.h"
#include "drm.h"
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 0583677e4581..35ef5b1e3566 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -21,16 +21,17 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nv40_grctx.o nv50_grctx.o nvc0_grctx.o \
nv84_crypt.o \
nva3_copy.o nvc0_copy.o \
- nv40_mpeg.o nv50_mpeg.o \
+ nv31_mpeg.o nv50_mpeg.o \
nv04_instmem.o nv50_instmem.o nvc0_instmem.o \
- nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \
- nv50_cursor.o nv50_display.o \
nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
nv04_crtc.o nv04_display.o nv04_cursor.o \
+ nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \
+ nv50_cursor.o nv50_display.o \
+ nvd0_display.o \
nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \
nv10_gpio.o nv50_gpio.o \
nv50_calc.o \
- nv04_pm.o nv50_pm.o nva3_pm.o \
+ nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \
nv50_vram.o nvc0_vram.o \
nv50_vm.o nvc0_vm.o
diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c
index 00a55dfdba82..fa22b28e8777 100644
--- a/drivers/gpu/drm/nouveau/nouveau_backlight.c
+++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c
@@ -37,8 +37,10 @@
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#include "nouveau_reg.h"
+#include "nouveau_encoder.h"
-static int nv40_get_intensity(struct backlight_device *bd)
+static int
+nv40_get_intensity(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)
@@ -47,7 +49,8 @@ static int nv40_get_intensity(struct backlight_device *bd)
return val;
}
-static int nv40_set_intensity(struct backlight_device *bd)
+static int
+nv40_set_intensity(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
int val = bd->props.brightness;
@@ -65,30 +68,8 @@ static const struct backlight_ops nv40_bl_ops = {
.update_status = nv40_set_intensity,
};
-static int nv50_get_intensity(struct backlight_device *bd)
-{
- struct drm_device *dev = bl_get_data(bd);
-
- return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT);
-}
-
-static int nv50_set_intensity(struct backlight_device *bd)
-{
- struct drm_device *dev = bl_get_data(bd);
- int val = bd->props.brightness;
-
- nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT,
- val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE);
- return 0;
-}
-
-static const struct backlight_ops nv50_bl_ops = {
- .options = BL_CORE_SUSPENDRESUME,
- .get_brightness = nv50_get_intensity,
- .update_status = nv50_set_intensity,
-};
-
-static int nouveau_nv40_backlight_init(struct drm_connector *connector)
+static int
+nv40_backlight_init(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -113,34 +94,129 @@ static int nouveau_nv40_backlight_init(struct drm_connector *connector)
return 0;
}
-static int nouveau_nv50_backlight_init(struct drm_connector *connector)
+static int
+nv50_get_intensity(struct backlight_device *bd)
+{
+ struct nouveau_encoder *nv_encoder = bl_get_data(bd);
+ struct drm_device *dev = nv_encoder->base.base.dev;
+ int or = nv_encoder->or;
+ u32 div = 1025;
+ u32 val;
+
+ val = nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(or));
+ val &= NV50_PDISP_SOR_PWM_CTL_VAL;
+ return ((val * 100) + (div / 2)) / div;
+}
+
+static int
+nv50_set_intensity(struct backlight_device *bd)
+{
+ struct nouveau_encoder *nv_encoder = bl_get_data(bd);
+ struct drm_device *dev = nv_encoder->base.base.dev;
+ int or = nv_encoder->or;
+ u32 div = 1025;
+ u32 val = (bd->props.brightness * div) / 100;
+
+ nv_wr32(dev, NV50_PDISP_SOR_PWM_CTL(or),
+ NV50_PDISP_SOR_PWM_CTL_NEW | val);
+ return 0;
+}
+
+static const struct backlight_ops nv50_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = nv50_get_intensity,
+ .update_status = nv50_set_intensity,
+};
+
+static int
+nva3_get_intensity(struct backlight_device *bd)
+{
+ struct nouveau_encoder *nv_encoder = bl_get_data(bd);
+ struct drm_device *dev = nv_encoder->base.base.dev;
+ int or = nv_encoder->or;
+ u32 div, val;
+
+ div = nv_rd32(dev, NV50_PDISP_SOR_PWM_DIV(or));
+ val = nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(or));
+ val &= NVA3_PDISP_SOR_PWM_CTL_VAL;
+ if (div && div >= val)
+ return ((val * 100) + (div / 2)) / div;
+
+ return 100;
+}
+
+static int
+nva3_set_intensity(struct backlight_device *bd)
+{
+ struct nouveau_encoder *nv_encoder = bl_get_data(bd);
+ struct drm_device *dev = nv_encoder->base.base.dev;
+ int or = nv_encoder->or;
+ u32 div, val;
+
+ div = nv_rd32(dev, NV50_PDISP_SOR_PWM_DIV(or));
+ val = (bd->props.brightness * div) / 100;
+ if (div) {
+ nv_wr32(dev, NV50_PDISP_SOR_PWM_CTL(or), val |
+ NV50_PDISP_SOR_PWM_CTL_NEW |
+ NVA3_PDISP_SOR_PWM_CTL_UNK);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const struct backlight_ops nva3_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = nva3_get_intensity,
+ .update_status = nva3_set_intensity,
+};
+
+static int
+nv50_backlight_init(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_encoder *nv_encoder;
struct backlight_properties props;
struct backlight_device *bd;
+ const struct backlight_ops *ops;
+
+ nv_encoder = find_encoder(connector, OUTPUT_LVDS);
+ if (!nv_encoder) {
+ nv_encoder = find_encoder(connector, OUTPUT_DP);
+ if (!nv_encoder)
+ return -ENODEV;
+ }
- if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT))
+ if (!nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or)))
return 0;
+ if (dev_priv->chipset <= 0xa0 ||
+ dev_priv->chipset == 0xaa ||
+ dev_priv->chipset == 0xac)
+ ops = &nv50_bl_ops;
+ else
+ ops = &nva3_bl_ops;
+
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
- props.max_brightness = 1025;
- bd = backlight_device_register("nv_backlight", &connector->kdev, dev,
- &nv50_bl_ops, &props);
+ props.max_brightness = 100;
+ bd = backlight_device_register("nv_backlight", &connector->kdev,
+ nv_encoder, ops, &props);
if (IS_ERR(bd))
return PTR_ERR(bd);
dev_priv->backlight = bd;
- bd->props.brightness = nv50_get_intensity(bd);
+ bd->props.brightness = bd->ops->get_brightness(bd);
backlight_update_status(bd);
return 0;
}
-int nouveau_backlight_init(struct drm_connector *connector)
+int
+nouveau_backlight_init(struct drm_device *dev)
{
- struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct drm_connector *connector;
#ifdef CONFIG_ACPI
if (acpi_video_backlight_support()) {
@@ -150,21 +226,28 @@ int nouveau_backlight_init(struct drm_connector *connector)
}
#endif
- switch (dev_priv->card_type) {
- case NV_40:
- return nouveau_nv40_backlight_init(connector);
- case NV_50:
- return nouveau_nv50_backlight_init(connector);
- default:
- break;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
+ connector->connector_type != DRM_MODE_CONNECTOR_eDP)
+ continue;
+
+ switch (dev_priv->card_type) {
+ case NV_40:
+ return nv40_backlight_init(connector);
+ case NV_50:
+ return nv50_backlight_init(connector);
+ default:
+ break;
+ }
}
+
return 0;
}
-void nouveau_backlight_exit(struct drm_connector *connector)
+void
+nouveau_backlight_exit(struct drm_device *dev)
{
- struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->backlight) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 729d5fd7c88d..5fc201b49d30 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -135,13 +135,14 @@ static void load_vbios_pramin(struct drm_device *dev, uint8_t *data)
int i;
if (dev_priv->card_type >= NV_50) {
- uint32_t vbios_vram = (nv_rd32(dev, 0x619f04) & ~0xff) << 8;
-
- if (!vbios_vram)
- vbios_vram = (nv_rd32(dev, 0x1700) << 16) + 0xf0000;
+ u64 addr = (u64)(nv_rd32(dev, 0x619f04) & 0xffffff00) << 8;
+ if (!addr) {
+ addr = (u64)nv_rd32(dev, 0x1700) << 16;
+ addr += 0xf0000;
+ }
old_bar0_pramin = nv_rd32(dev, 0x1700);
- nv_wr32(dev, 0x1700, vbios_vram >> 16);
+ nv_wr32(dev, 0x1700, addr >> 16);
}
/* bail if no rom signature */
@@ -295,6 +296,11 @@ munge_reg(struct nvbios *bios, uint32_t reg)
if (dev_priv->card_type < NV_50)
return reg;
+ if (reg & 0x80000000) {
+ BUG_ON(bios->display.crtc < 0);
+ reg += bios->display.crtc * 0x800;
+ }
+
if (reg & 0x40000000) {
BUG_ON(!dcbent);
@@ -303,7 +309,7 @@ munge_reg(struct nvbios *bios, uint32_t reg)
reg += 0x00000080;
}
- reg &= ~0x60000000;
+ reg &= ~0xe0000000;
return reg;
}
@@ -634,10 +640,9 @@ static int
nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t reg0 = nv_rd32(dev, reg + 0);
- uint32_t reg1 = nv_rd32(dev, reg + 4);
struct nouveau_pll_vals pll;
struct pll_lims pll_limits;
+ u32 ctrl, mask, coef;
int ret;
ret = get_pll_limits(dev, reg, &pll_limits);
@@ -648,15 +653,20 @@ nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk)
if (!clk)
return -ERANGE;
- reg0 = (reg0 & 0xfff8ffff) | (pll.log2P << 16);
- reg1 = (reg1 & 0xffff0000) | (pll.N1 << 8) | pll.M1;
-
- if (dev_priv->vbios.execute) {
- still_alive();
- nv_wr32(dev, reg + 4, reg1);
- nv_wr32(dev, reg + 0, reg0);
+ coef = pll.N1 << 8 | pll.M1;
+ ctrl = pll.log2P << 16;
+ mask = 0x00070000;
+ if (reg == 0x004008) {
+ mask |= 0x01f80000;
+ ctrl |= (pll_limits.log2p_bias << 19);
+ ctrl |= (pll.log2P << 22);
}
+ if (!dev_priv->vbios.execute)
+ return 0;
+
+ nv_mask(dev, reg + 0, mask, ctrl);
+ nv_wr32(dev, reg + 4, coef);
return 0;
}
@@ -1173,22 +1183,19 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
*
*/
- struct bit_displayport_encoder_table *dpe = NULL;
struct dcb_entry *dcb = bios->display.output;
struct drm_device *dev = bios->dev;
uint8_t cond = bios->data[offset + 1];
- int dummy;
+ uint8_t *table, *entry;
BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond);
if (!iexec->execute)
return 3;
- dpe = nouveau_bios_dp_table(dev, dcb, &dummy);
- if (!dpe) {
- NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset);
+ table = nouveau_dp_bios_data(dev, dcb, &entry);
+ if (!table)
return 3;
- }
switch (cond) {
case 0:
@@ -1202,7 +1209,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
break;
case 1:
case 2:
- if (!(dpe->unknown & cond))
+ if (!(entry[5] & cond))
iexec->execute = false;
break;
case 5:
@@ -3220,6 +3227,49 @@ init_8d(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
return 1;
}
+static void
+init_gpio_unknv50(struct nvbios *bios, struct dcb_gpio_entry *gpio)
+{
+ const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
+ u32 r, s, v;
+
+ /* Not a clue, needs de-magicing */
+ r = nv50_gpio_ctl[gpio->line >> 4];
+ s = (gpio->line & 0x0f);
+ v = bios_rd32(bios, r) & ~(0x00010001 << s);
+ switch ((gpio->entry & 0x06000000) >> 25) {
+ case 1:
+ v |= (0x00000001 << s);
+ break;
+ case 2:
+ v |= (0x00010000 << s);
+ break;
+ default:
+ break;
+ }
+
+ bios_wr32(bios, r, v);
+}
+
+static void
+init_gpio_unknvd0(struct nvbios *bios, struct dcb_gpio_entry *gpio)
+{
+ u32 v, i;
+
+ v = bios_rd32(bios, 0x00d610 + (gpio->line * 4));
+ v &= 0xffffff00;
+ v |= (gpio->entry & 0x00ff0000) >> 16;
+ bios_wr32(bios, 0x00d610 + (gpio->line * 4), v);
+
+ i = (gpio->entry & 0x1f000000) >> 24;
+ if (i) {
+ v = bios_rd32(bios, 0x00d640 + ((i - 1) * 4));
+ v &= 0xffffff00;
+ v |= gpio->line;
+ bios_wr32(bios, 0x00d640 + ((i - 1) * 4), v);
+ }
+}
+
static int
init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
{
@@ -3234,7 +3284,6 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
- const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
int i;
if (dev_priv->card_type < NV_50) {
@@ -3247,33 +3296,20 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
for (i = 0; i < bios->dcb.gpio.entries; i++) {
struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i];
- uint32_t r, s, v;
BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry);
BIOSLOG(bios, "0x%04X: set gpio 0x%02x, state %d\n",
offset, gpio->tag, gpio->state_default);
- if (bios->execute)
- pgpio->set(bios->dev, gpio->tag, gpio->state_default);
- /* The NVIDIA binary driver doesn't appear to actually do
- * any of this, my VBIOS does however.
- */
- /* Not a clue, needs de-magicing */
- r = nv50_gpio_ctl[gpio->line >> 4];
- s = (gpio->line & 0x0f);
- v = bios_rd32(bios, r) & ~(0x00010001 << s);
- switch ((gpio->entry & 0x06000000) >> 25) {
- case 1:
- v |= (0x00000001 << s);
- break;
- case 2:
- v |= (0x00010000 << s);
- break;
- default:
- break;
- }
- bios_wr32(bios, r, v);
+ if (!bios->execute)
+ continue;
+
+ pgpio->set(bios->dev, gpio->tag, gpio->state_default);
+ if (dev_priv->card_type < NV_D0)
+ init_gpio_unknv50(bios, gpio);
+ else
+ init_gpio_unknvd0(bios, gpio);
}
return 1;
@@ -3736,6 +3772,10 @@ parse_init_table(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
int count = 0, i, ret;
uint8_t id;
+ /* catch NULL script pointers */
+ if (offset == 0)
+ return 0;
+
/*
* Loop until INIT_DONE causes us to break out of the loop
* (or until offset > bios length just in case... )
@@ -4388,86 +4428,37 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b
return 0;
}
-static uint8_t *
-bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
- uint16_t record, int record_len, int record_nr,
- bool match_link)
+/* BIT 'U'/'d' table encoder subtables have hashes matching them to
+ * a particular set of encoders.
+ *
+ * This function returns true if a particular DCB entry matches.
+ */
+bool
+bios_encoder_match(struct dcb_entry *dcb, u32 hash)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nvbios *bios = &dev_priv->vbios;
- uint32_t entry;
- uint16_t table;
- int i, v;
+ if ((hash & 0x000000f0) != (dcb->location << 4))
+ return false;
+ if ((hash & 0x0000000f) != dcb->type)
+ return false;
+ if (!(hash & (dcb->or << 16)))
+ return false;
- switch (dcbent->type) {
+ switch (dcb->type) {
case OUTPUT_TMDS:
case OUTPUT_LVDS:
case OUTPUT_DP:
- break;
- default:
- match_link = false;
- break;
- }
-
- for (i = 0; i < record_nr; i++, record += record_len) {
- table = ROM16(bios->data[record]);
- if (!table)
- continue;
- entry = ROM32(bios->data[table]);
-
- if (match_link) {
- v = (entry & 0x00c00000) >> 22;
- if (!(v & dcbent->sorconf.link))
- continue;
+ if (hash & 0x00c00000) {
+ if (!(hash & (dcb->sorconf.link << 22)))
+ return false;
}
-
- v = (entry & 0x000f0000) >> 16;
- if (!(v & dcbent->or))
- continue;
-
- v = (entry & 0x000000f0) >> 4;
- if (v != dcbent->location)
- continue;
-
- v = (entry & 0x0000000f);
- if (v != dcbent->type)
- continue;
-
- return &bios->data[table];
- }
-
- return NULL;
-}
-
-void *
-nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
- int *length)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nvbios *bios = &dev_priv->vbios;
- uint8_t *table;
-
- if (!bios->display.dp_table_ptr) {
- NV_ERROR(dev, "No pointer to DisplayPort table\n");
- return NULL;
- }
- table = &bios->data[bios->display.dp_table_ptr];
-
- if (table[0] != 0x20 && table[0] != 0x21) {
- NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n",
- table[0]);
- return NULL;
+ default:
+ return true;
}
-
- *length = table[4];
- return bios_output_config_match(dev, dcbent,
- bios->display.dp_table_ptr + table[1],
- table[2], table[3], table[0] >= 0x21);
}
int
-nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
- uint32_t sub, int pxclk)
+nouveau_bios_run_display_table(struct drm_device *dev, u16 type, int pclk,
+ struct dcb_entry *dcbent, int crtc)
{
/*
* The display script table is located by the BIT 'U' table.
@@ -4497,7 +4488,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
uint8_t *table = &bios->data[bios->display.script_table_ptr];
uint8_t *otable = NULL;
uint16_t script;
- int i = 0;
+ int i;
if (!bios->display.script_table_ptr) {
NV_ERROR(dev, "No pointer to output script table\n");
@@ -4549,30 +4540,33 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
NV_DEBUG_KMS(dev, "Searching for output entry for %d %d %d\n",
dcbent->type, dcbent->location, dcbent->or);
- otable = bios_output_config_match(dev, dcbent, table[1] +
- bios->display.script_table_ptr,
- table[2], table[3], table[0] >= 0x21);
+ for (i = 0; i < table[3]; i++) {
+ otable = ROMPTR(bios, table[table[1] + (i * table[2])]);
+ if (otable && bios_encoder_match(dcbent, ROM32(otable[0])))
+ break;
+ }
+
if (!otable) {
NV_DEBUG_KMS(dev, "failed to match any output table\n");
return 1;
}
- if (pxclk < -2 || pxclk > 0) {
+ if (pclk < -2 || pclk > 0) {
/* Try to find matching script table entry */
for (i = 0; i < otable[5]; i++) {
- if (ROM16(otable[table[4] + i*6]) == sub)
+ if (ROM16(otable[table[4] + i*6]) == type)
break;
}
if (i == otable[5]) {
NV_ERROR(dev, "Table 0x%04x not found for %d/%d, "
"using first\n",
- sub, dcbent->type, dcbent->or);
+ type, dcbent->type, dcbent->or);
i = 0;
}
}
- if (pxclk == 0) {
+ if (pclk == 0) {
script = ROM16(otable[6]);
if (!script) {
NV_DEBUG_KMS(dev, "output script 0 not found\n");
@@ -4580,9 +4574,9 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
}
NV_DEBUG_KMS(dev, "0x%04X: parsing output script 0\n", script);
- nouveau_bios_run_init_table(dev, script, dcbent);
+ nouveau_bios_run_init_table(dev, script, dcbent, crtc);
} else
- if (pxclk == -1) {
+ if (pclk == -1) {
script = ROM16(otable[8]);
if (!script) {
NV_DEBUG_KMS(dev, "output script 1 not found\n");
@@ -4590,9 +4584,9 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
}
NV_DEBUG_KMS(dev, "0x%04X: parsing output script 1\n", script);
- nouveau_bios_run_init_table(dev, script, dcbent);
+ nouveau_bios_run_init_table(dev, script, dcbent, crtc);
} else
- if (pxclk == -2) {
+ if (pclk == -2) {
if (table[4] >= 12)
script = ROM16(otable[10]);
else
@@ -4603,31 +4597,31 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
}
NV_DEBUG_KMS(dev, "0x%04X: parsing output script 2\n", script);
- nouveau_bios_run_init_table(dev, script, dcbent);
+ nouveau_bios_run_init_table(dev, script, dcbent, crtc);
} else
- if (pxclk > 0) {
+ if (pclk > 0) {
script = ROM16(otable[table[4] + i*6 + 2]);
if (script)
- script = clkcmptable(bios, script, pxclk);
+ script = clkcmptable(bios, script, pclk);
if (!script) {
NV_DEBUG_KMS(dev, "clock script 0 not found\n");
return 1;
}
NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 0\n", script);
- nouveau_bios_run_init_table(dev, script, dcbent);
+ nouveau_bios_run_init_table(dev, script, dcbent, crtc);
} else
- if (pxclk < 0) {
+ if (pclk < 0) {
script = ROM16(otable[table[4] + i*6 + 4]);
if (script)
- script = clkcmptable(bios, script, -pxclk);
+ script = clkcmptable(bios, script, -pclk);
if (!script) {
NV_DEBUG_KMS(dev, "clock script 1 not found\n");
return 1;
}
NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 1\n", script);
- nouveau_bios_run_init_table(dev, script, dcbent);
+ nouveau_bios_run_init_table(dev, script, dcbent, crtc);
}
return 0;
@@ -5186,7 +5180,7 @@ static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, st
load_table_ptr = ROM16(bios->data[bitentry->offset]);
if (load_table_ptr == 0x0) {
- NV_ERROR(dev, "Pointer to BIT loadval table invalid\n");
+ NV_DEBUG(dev, "Pointer to BIT loadval table invalid\n");
return -EINVAL;
}
@@ -5477,14 +5471,6 @@ parse_bit_U_tbl_entry(struct drm_device *dev, struct nvbios *bios,
return 0;
}
-static int
-parse_bit_displayport_tbl_entry(struct drm_device *dev, struct nvbios *bios,
- struct bit_entry *bitentry)
-{
- bios->display.dp_table_ptr = ROM16(bios->data[bitentry->offset]);
- return 0;
-}
-
struct bit_table {
const char id;
int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *);
@@ -5558,7 +5544,6 @@ parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset)
parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds));
parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds));
parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U));
- parse_bit_table(bios, bitoffset, &BIT_TABLE('d', displayport));
return 0;
}
@@ -5883,9 +5868,15 @@ parse_dcb_gpio_table(struct nvbios *bios)
}
e->line = (e->entry & 0x0000001f) >> 0;
- e->state_default = (e->entry & 0x01000000) >> 24;
- e->state[0] = (e->entry & 0x18000000) >> 27;
- e->state[1] = (e->entry & 0x60000000) >> 29;
+ if (gpio[0] == 0x40) {
+ e->state_default = (e->entry & 0x01000000) >> 24;
+ e->state[0] = (e->entry & 0x18000000) >> 27;
+ e->state[1] = (e->entry & 0x60000000) >> 29;
+ } else {
+ e->state_default = (e->entry & 0x00000080) >> 7;
+ e->state[0] = (entry[4] >> 4) & 3;
+ e->state[1] = (entry[4] >> 6) & 3;
+ }
}
}
@@ -5965,6 +5956,12 @@ apply_dcb_connector_quirks(struct nvbios *bios, int idx)
if (cte->type == DCB_CONNECTOR_HDMI_1)
cte->type = DCB_CONNECTOR_DVI_I;
}
+
+ /* Gigabyte GV-NX86T512H */
+ if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) {
+ if (cte->type == DCB_CONNECTOR_HDMI_1)
+ cte->type = DCB_CONNECTOR_DVI_I;
+ }
}
static const u8 hpd_gpio[16] = {
@@ -6149,7 +6146,14 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
}
case OUTPUT_DP:
entry->dpconf.sor.link = (conf & 0x00000030) >> 4;
- entry->dpconf.link_bw = (conf & 0x00e00000) >> 21;
+ switch ((conf & 0x00e00000) >> 21) {
+ case 0:
+ entry->dpconf.link_bw = 162000;
+ break;
+ default:
+ entry->dpconf.link_bw = 270000;
+ break;
+ }
switch ((conf & 0x0f000000) >> 24) {
case 0xf:
entry->dpconf.link_nr = 4;
@@ -6377,6 +6381,37 @@ apply_dcb_encoder_quirks(struct drm_device *dev, int idx, u32 *conn, u32 *conf)
}
}
+ /* Some other twisted XFX board (rhbz#694914)
+ *
+ * The DVI/VGA encoder combo that's supposed to represent the
+ * DVI-I connector actually point at two different ones, and
+ * the HDMI connector ends up paired with the VGA instead.
+ *
+ * Connector table is missing anything for VGA at all, pointing it
+ * an invalid conntab entry 2 so we figure it out ourself.
+ */
+ if (nv_match_device(dev, 0x0615, 0x1682, 0x2605)) {
+ if (idx == 0) {
+ *conn = 0x02002300; /* VGA, connector 2 */
+ *conf = 0x00000028;
+ } else
+ if (idx == 1) {
+ *conn = 0x01010312; /* DVI, connector 0 */
+ *conf = 0x00020030;
+ } else
+ if (idx == 2) {
+ *conn = 0x04020310; /* VGA, connector 0 */
+ *conf = 0x00000028;
+ } else
+ if (idx == 3) {
+ *conn = 0x02021322; /* HDMI, connector 1 */
+ *conf = 0x00020010;
+ } else {
+ *conn = 0x0000000e; /* EOL */
+ *conf = 0x00000000;
+ }
+ }
+
return true;
}
@@ -6731,7 +6766,7 @@ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
void
nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
- struct dcb_entry *dcbent)
+ struct dcb_entry *dcbent, int crtc)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->vbios;
@@ -6739,11 +6774,22 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
spin_lock_bh(&bios->lock);
bios->display.output = dcbent;
+ bios->display.crtc = crtc;
parse_init_table(bios, table, &iexec);
bios->display.output = NULL;
spin_unlock_bh(&bios->lock);
}
+void
+nouveau_bios_init_exec(struct drm_device *dev, uint16_t table)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ struct init_exec iexec = { true, false };
+
+ parse_init_table(bios, table, &iexec);
+}
+
static bool NVInitVBIOS(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -6825,9 +6871,8 @@ nouveau_run_vbios_init(struct drm_device *dev)
if (dev_priv->card_type >= NV_50) {
for (i = 0; i < bios->dcb.entries; i++) {
- nouveau_bios_run_display_table(dev,
- &bios->dcb.entry[i],
- 0, 0);
+ nouveau_bios_run_display_table(dev, 0, 0,
+ &bios->dcb.entry[i], -1);
}
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
index 050c314119df..8adb69e4a6b1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -289,8 +289,8 @@ struct nvbios {
struct {
struct dcb_entry *output;
+ int crtc;
uint16_t script_table_ptr;
- uint16_t dp_table_ptr;
} display;
struct {
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index 2ad49cbf7c8b..7cc37e690860 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -49,16 +49,12 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
DRM_ERROR("bo %p still attached to GEM object\n", bo);
nv10_mem_put_tile_region(dev, nvbo->tile, NULL);
- if (nvbo->vma.node) {
- nouveau_vm_unmap(&nvbo->vma);
- nouveau_vm_put(&nvbo->vma);
- }
kfree(nvbo);
}
static void
nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags,
- int *align, int *size, int *page_shift)
+ int *align, int *size)
{
struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
@@ -82,67 +78,51 @@ nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags,
}
}
} else {
- if (likely(dev_priv->chan_vm)) {
- if (!(flags & TTM_PL_FLAG_TT) && *size > 256 * 1024)
- *page_shift = dev_priv->chan_vm->lpg_shift;
- else
- *page_shift = dev_priv->chan_vm->spg_shift;
- } else {
- *page_shift = 12;
- }
-
- *size = roundup(*size, (1 << *page_shift));
- *align = max((1 << *page_shift), *align);
+ *size = roundup(*size, (1 << nvbo->page_shift));
+ *align = max((1 << nvbo->page_shift), *align);
}
*size = roundup(*size, PAGE_SIZE);
}
int
-nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
- int size, int align, uint32_t flags, uint32_t tile_mode,
- uint32_t tile_flags, struct nouveau_bo **pnvbo)
+nouveau_bo_new(struct drm_device *dev, int size, int align,
+ uint32_t flags, uint32_t tile_mode, uint32_t tile_flags,
+ struct nouveau_bo **pnvbo)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_bo *nvbo;
- int ret = 0, page_shift = 0;
+ int ret;
nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL);
if (!nvbo)
return -ENOMEM;
INIT_LIST_HEAD(&nvbo->head);
INIT_LIST_HEAD(&nvbo->entry);
+ INIT_LIST_HEAD(&nvbo->vma_list);
nvbo->tile_mode = tile_mode;
nvbo->tile_flags = tile_flags;
nvbo->bo.bdev = &dev_priv->ttm.bdev;
- nouveau_bo_fixup_align(nvbo, flags, &align, &size, &page_shift);
- align >>= PAGE_SHIFT;
-
- if (dev_priv->chan_vm) {
- ret = nouveau_vm_get(dev_priv->chan_vm, size, page_shift,
- NV_MEM_ACCESS_RW, &nvbo->vma);
- if (ret) {
- kfree(nvbo);
- return ret;
- }
+ nvbo->page_shift = 12;
+ if (dev_priv->bar1_vm) {
+ if (!(flags & TTM_PL_FLAG_TT) && size > 256 * 1024)
+ nvbo->page_shift = dev_priv->bar1_vm->lpg_shift;
}
+ nouveau_bo_fixup_align(nvbo, flags, &align, &size);
nvbo->bo.mem.num_pages = size >> PAGE_SHIFT;
nouveau_bo_placement_set(nvbo, flags, 0);
- nvbo->channel = chan;
ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size,
- ttm_bo_type_device, &nvbo->placement, align, 0,
- false, NULL, size, nouveau_bo_del_ttm);
+ ttm_bo_type_device, &nvbo->placement,
+ align >> PAGE_SHIFT, 0, false, NULL, size,
+ nouveau_bo_del_ttm);
if (ret) {
/* ttm will call nouveau_bo_del_ttm if it fails.. */
return ret;
}
- nvbo->channel = NULL;
- if (nvbo->vma.node)
- nvbo->bo.offset = nvbo->vma.offset;
*pnvbo = nvbo;
return 0;
}
@@ -168,7 +148,7 @@ set_placement_range(struct nouveau_bo *nvbo, uint32_t type)
if (dev_priv->card_type == NV_10 &&
nvbo->tile_mode && (type & TTM_PL_FLAG_VRAM) &&
- nvbo->bo.mem.num_pages < vram_pages / 2) {
+ nvbo->bo.mem.num_pages < vram_pages / 4) {
/*
* Make sure that the color and depth buffers are handled
* by independent memory controller units. Up to a 9x
@@ -312,8 +292,6 @@ nouveau_bo_validate(struct nouveau_bo *nvbo, bool interruptible,
if (ret)
return ret;
- if (nvbo->vma.node)
- nvbo->bo.offset = nvbo->vma.offset;
return 0;
}
@@ -440,7 +418,6 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
TTM_MEMTYPE_FLAG_CMA;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
- man->gpu_offset = dev_priv->gart_info.aper_base;
break;
default:
NV_ERROR(dev, "Unknown GART type: %d\n",
@@ -501,19 +478,12 @@ static int
nvc0_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,
struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem)
{
- struct nouveau_mem *old_node = old_mem->mm_node;
- struct nouveau_mem *new_node = new_mem->mm_node;
- struct nouveau_bo *nvbo = nouveau_bo(bo);
+ struct nouveau_mem *node = old_mem->mm_node;
+ u64 src_offset = node->vma[0].offset;
+ u64 dst_offset = node->vma[1].offset;
u32 page_count = new_mem->num_pages;
- u64 src_offset, dst_offset;
int ret;
- src_offset = old_node->tmp_vma.offset;
- if (new_node->tmp_vma.node)
- dst_offset = new_node->tmp_vma.offset;
- else
- dst_offset = nvbo->vma.offset;
-
page_count = new_mem->num_pages;
while (page_count) {
int line_count = (page_count > 2047) ? 2047 : page_count;
@@ -547,19 +517,13 @@ static int
nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,
struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem)
{
- struct nouveau_mem *old_node = old_mem->mm_node;
- struct nouveau_mem *new_node = new_mem->mm_node;
+ struct nouveau_mem *node = old_mem->mm_node;
struct nouveau_bo *nvbo = nouveau_bo(bo);
u64 length = (new_mem->num_pages << PAGE_SHIFT);
- u64 src_offset, dst_offset;
+ u64 src_offset = node->vma[0].offset;
+ u64 dst_offset = node->vma[1].offset;
int ret;
- src_offset = old_node->tmp_vma.offset;
- if (new_node->tmp_vma.node)
- dst_offset = new_node->tmp_vma.offset;
- else
- dst_offset = nvbo->vma.offset;
-
while (length) {
u32 amount, stride, height;
@@ -695,6 +659,27 @@ nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,
}
static int
+nouveau_vma_getmap(struct nouveau_channel *chan, struct nouveau_bo *nvbo,
+ struct ttm_mem_reg *mem, struct nouveau_vma *vma)
+{
+ struct nouveau_mem *node = mem->mm_node;
+ int ret;
+
+ ret = nouveau_vm_get(chan->vm, mem->num_pages << PAGE_SHIFT,
+ node->page_shift, NV_MEM_ACCESS_RO, vma);
+ if (ret)
+ return ret;
+
+ if (mem->mem_type == TTM_PL_VRAM)
+ nouveau_vm_map(vma, node);
+ else
+ nouveau_vm_map_sg(vma, 0, mem->num_pages << PAGE_SHIFT,
+ node, node->pages);
+
+ return 0;
+}
+
+static int
nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
bool no_wait_reserve, bool no_wait_gpu,
struct ttm_mem_reg *new_mem)
@@ -711,31 +696,20 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
mutex_lock_nested(&chan->mutex, NOUVEAU_KCHANNEL_MUTEX);
}
- /* create temporary vma for old memory, this will get cleaned
- * up after ttm destroys the ttm_mem_reg
+ /* create temporary vmas for the transfer and attach them to the
+ * old nouveau_mem node, these will get cleaned up after ttm has
+ * destroyed the ttm_mem_reg
*/
if (dev_priv->card_type >= NV_50) {
struct nouveau_mem *node = old_mem->mm_node;
- if (!node->tmp_vma.node) {
- u32 page_shift = nvbo->vma.node->type;
- if (old_mem->mem_type == TTM_PL_TT)
- page_shift = nvbo->vma.vm->spg_shift;
-
- ret = nouveau_vm_get(chan->vm,
- old_mem->num_pages << PAGE_SHIFT,
- page_shift, NV_MEM_ACCESS_RO,
- &node->tmp_vma);
- if (ret)
- goto out;
- }
- if (old_mem->mem_type == TTM_PL_VRAM)
- nouveau_vm_map(&node->tmp_vma, node);
- else {
- nouveau_vm_map_sg(&node->tmp_vma, 0,
- old_mem->num_pages << PAGE_SHIFT,
- node, node->pages);
- }
+ ret = nouveau_vma_getmap(chan, nvbo, old_mem, &node->vma[0]);
+ if (ret)
+ goto out;
+
+ ret = nouveau_vma_getmap(chan, nvbo, new_mem, &node->vma[1]);
+ if (ret)
+ goto out;
}
if (dev_priv->card_type < NV_50)
@@ -762,7 +736,6 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
bool no_wait_reserve, bool no_wait_gpu,
struct ttm_mem_reg *new_mem)
{
- struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
struct ttm_placement placement;
struct ttm_mem_reg tmp_mem;
@@ -782,23 +755,7 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
if (ret)
goto out;
- if (dev_priv->card_type >= NV_50) {
- struct nouveau_bo *nvbo = nouveau_bo(bo);
- struct nouveau_mem *node = tmp_mem.mm_node;
- struct nouveau_vma *vma = &nvbo->vma;
- if (vma->node->type != vma->vm->spg_shift)
- vma = &node->tmp_vma;
- nouveau_vm_map_sg(vma, 0, tmp_mem.num_pages << PAGE_SHIFT,
- node, node->pages);
- }
-
ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_reserve, no_wait_gpu, &tmp_mem);
-
- if (dev_priv->card_type >= NV_50) {
- struct nouveau_bo *nvbo = nouveau_bo(bo);
- nouveau_vm_unmap(&nvbo->vma);
- }
-
if (ret)
goto out;
@@ -844,30 +801,22 @@ out:
static void
nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem)
{
- struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
struct nouveau_mem *node = new_mem->mm_node;
struct nouveau_bo *nvbo = nouveau_bo(bo);
- struct nouveau_vma *vma = &nvbo->vma;
- struct nouveau_vm *vm = vma->vm;
-
- if (dev_priv->card_type < NV_50)
- return;
-
- switch (new_mem->mem_type) {
- case TTM_PL_VRAM:
- nouveau_vm_map(vma, node);
- break;
- case TTM_PL_TT:
- if (vma->node->type != vm->spg_shift) {
+ struct nouveau_vma *vma;
+
+ list_for_each_entry(vma, &nvbo->vma_list, head) {
+ if (new_mem->mem_type == TTM_PL_VRAM) {
+ nouveau_vm_map(vma, new_mem->mm_node);
+ } else
+ if (new_mem->mem_type == TTM_PL_TT &&
+ nvbo->page_shift == vma->vm->spg_shift) {
+ nouveau_vm_map_sg(vma, 0, new_mem->
+ num_pages << PAGE_SHIFT,
+ node, node->pages);
+ } else {
nouveau_vm_unmap(vma);
- vma = &node->tmp_vma;
}
- nouveau_vm_map_sg(vma, 0, new_mem->num_pages << PAGE_SHIFT,
- node, node->pages);
- break;
- default:
- nouveau_vm_unmap(&nvbo->vma);
- break;
}
}
@@ -1007,7 +956,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
break;
}
- if (dev_priv->card_type == NV_C0)
+ if (dev_priv->card_type >= NV_C0)
page_shift = node->page_shift;
else
page_shift = 12;
@@ -1113,3 +1062,54 @@ struct ttm_bo_driver nouveau_bo_driver = {
.io_mem_free = &nouveau_ttm_io_mem_free,
};
+struct nouveau_vma *
+nouveau_bo_vma_find(struct nouveau_bo *nvbo, struct nouveau_vm *vm)
+{
+ struct nouveau_vma *vma;
+ list_for_each_entry(vma, &nvbo->vma_list, head) {
+ if (vma->vm == vm)
+ return vma;
+ }
+
+ return NULL;
+}
+
+int
+nouveau_bo_vma_add(struct nouveau_bo *nvbo, struct nouveau_vm *vm,
+ struct nouveau_vma *vma)
+{
+ const u32 size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
+ struct nouveau_mem *node = nvbo->bo.mem.mm_node;
+ int ret;
+
+ ret = nouveau_vm_get(vm, size, nvbo->page_shift,
+ NV_MEM_ACCESS_RW, vma);
+ if (ret)
+ return ret;
+
+ if (nvbo->bo.mem.mem_type == TTM_PL_VRAM)
+ nouveau_vm_map(vma, nvbo->bo.mem.mm_node);
+ else
+ if (nvbo->bo.mem.mem_type == TTM_PL_TT)
+ nouveau_vm_map_sg(vma, 0, size, node, node->pages);
+
+ list_add_tail(&vma->head, &nvbo->vma_list);
+ vma->refcount = 1;
+ return 0;
+}
+
+void
+nouveau_bo_vma_del(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
+{
+ if (vma->node) {
+ if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM) {
+ spin_lock(&nvbo->bo.bdev->fence_lock);
+ ttm_bo_wait(&nvbo->bo, false, false, false);
+ spin_unlock(&nvbo->bo.bdev->fence_lock);
+ nouveau_vm_unmap(vma);
+ }
+
+ nouveau_vm_put(vma);
+ list_del(&vma->head);
+ }
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
index a7583a8ddb01..bb6ec9ef8676 100644
--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
@@ -27,40 +27,63 @@
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#include "nouveau_dma.h"
+#include "nouveau_ramht.h"
static int
-nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
+nouveau_channel_pushbuf_init(struct nouveau_channel *chan)
{
+ u32 mem = nouveau_vram_pushbuf ? TTM_PL_FLAG_VRAM : TTM_PL_FLAG_TT;
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_bo *pb = chan->pushbuf_bo;
- struct nouveau_gpuobj *pushbuf = NULL;
- int ret = 0;
+ int ret;
+
+ /* allocate buffer object */
+ ret = nouveau_bo_new(dev, 65536, 0, mem, 0, 0, &chan->pushbuf_bo);
+ if (ret)
+ goto out;
+
+ ret = nouveau_bo_pin(chan->pushbuf_bo, mem);
+ if (ret)
+ goto out;
+
+ ret = nouveau_bo_map(chan->pushbuf_bo);
+ if (ret)
+ goto out;
+ /* create DMA object covering the entire memtype where the push
+ * buffer resides, userspace can submit its own push buffers from
+ * anywhere within the same memtype.
+ */
+ chan->pushbuf_base = chan->pushbuf_bo->bo.offset;
if (dev_priv->card_type >= NV_50) {
+ ret = nouveau_bo_vma_add(chan->pushbuf_bo, chan->vm,
+ &chan->pushbuf_vma);
+ if (ret)
+ goto out;
+
if (dev_priv->card_type < NV_C0) {
ret = nouveau_gpuobj_dma_new(chan,
NV_CLASS_DMA_IN_MEMORY, 0,
(1ULL << 40),
NV_MEM_ACCESS_RO,
NV_MEM_TARGET_VM,
- &pushbuf);
+ &chan->pushbuf);
}
- chan->pushbuf_base = pb->bo.offset;
+ chan->pushbuf_base = chan->pushbuf_vma.offset;
} else
- if (pb->bo.mem.mem_type == TTM_PL_TT) {
+ if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_TT) {
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
dev_priv->gart_info.aper_size,
NV_MEM_ACCESS_RO,
- NV_MEM_TARGET_GART, &pushbuf);
- chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
+ NV_MEM_TARGET_GART,
+ &chan->pushbuf);
} else
if (dev_priv->card_type != NV_04) {
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
dev_priv->fb_available_size,
NV_MEM_ACCESS_RO,
- NV_MEM_TARGET_VRAM, &pushbuf);
- chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
+ NV_MEM_TARGET_VRAM,
+ &chan->pushbuf);
} else {
/* NV04 cmdbuf hack, from original ddx.. not sure of it's
* exact reason for existing :) PCI access to cmdbuf in
@@ -70,47 +93,22 @@ nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
pci_resource_start(dev->pdev, 1),
dev_priv->fb_available_size,
NV_MEM_ACCESS_RO,
- NV_MEM_TARGET_PCI, &pushbuf);
- chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
- }
-
- nouveau_gpuobj_ref(pushbuf, &chan->pushbuf);
- nouveau_gpuobj_ref(NULL, &pushbuf);
- return ret;
-}
-
-static struct nouveau_bo *
-nouveau_channel_user_pushbuf_alloc(struct drm_device *dev)
-{
- struct nouveau_bo *pushbuf = NULL;
- int location, ret;
-
- if (nouveau_vram_pushbuf)
- location = TTM_PL_FLAG_VRAM;
- else
- location = TTM_PL_FLAG_TT;
-
- ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, &pushbuf);
- if (ret) {
- NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret);
- return NULL;
+ NV_MEM_TARGET_PCI,
+ &chan->pushbuf);
}
- ret = nouveau_bo_pin(pushbuf, location);
+out:
if (ret) {
- NV_ERROR(dev, "error pinning DMA push buffer: %d\n", ret);
- nouveau_bo_ref(NULL, &pushbuf);
- return NULL;
- }
-
- ret = nouveau_bo_map(pushbuf);
- if (ret) {
- nouveau_bo_unpin(pushbuf);
- nouveau_bo_ref(NULL, &pushbuf);
- return NULL;
+ NV_ERROR(dev, "error initialising pushbuf: %d\n", ret);
+ nouveau_bo_vma_del(chan->pushbuf_bo, &chan->pushbuf_vma);
+ nouveau_gpuobj_ref(NULL, &chan->pushbuf);
+ if (chan->pushbuf_bo) {
+ nouveau_bo_unmap(chan->pushbuf_bo);
+ nouveau_bo_ref(NULL, &chan->pushbuf_bo);
+ }
}
- return pushbuf;
+ return 0;
}
/* allocates and initializes a fifo for user space consumption */
@@ -121,6 +119,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+ struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
struct nouveau_channel *chan;
unsigned long flags;
int ret;
@@ -159,20 +158,16 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
INIT_LIST_HEAD(&chan->nvsw.vbl_wait);
INIT_LIST_HEAD(&chan->nvsw.flip);
INIT_LIST_HEAD(&chan->fence.pending);
+ spin_lock_init(&chan->fence.lock);
- /* Allocate DMA push buffer */
- chan->pushbuf_bo = nouveau_channel_user_pushbuf_alloc(dev);
- if (!chan->pushbuf_bo) {
- ret = -ENOMEM;
- NV_ERROR(dev, "pushbuf %d\n", ret);
+ /* setup channel's memory and vm */
+ ret = nouveau_gpuobj_channel_init(chan, vram_handle, gart_handle);
+ if (ret) {
+ NV_ERROR(dev, "gpuobj %d\n", ret);
nouveau_channel_put(&chan);
return ret;
}
- nouveau_dma_pre_init(chan);
- chan->user_put = 0x40;
- chan->user_get = 0x44;
-
/* Allocate space for per-channel fixed notifier memory */
ret = nouveau_notifier_init_channel(chan);
if (ret) {
@@ -181,21 +176,17 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
return ret;
}
- /* Setup channel's default objects */
- ret = nouveau_gpuobj_channel_init(chan, vram_handle, gart_handle);
+ /* Allocate DMA push buffer */
+ ret = nouveau_channel_pushbuf_init(chan);
if (ret) {
- NV_ERROR(dev, "gpuobj %d\n", ret);
+ NV_ERROR(dev, "pushbuf %d\n", ret);
nouveau_channel_put(&chan);
return ret;
}
- /* Create a dma object for the push buffer */
- ret = nouveau_channel_pushbuf_ctxdma_init(chan);
- if (ret) {
- NV_ERROR(dev, "pbctxdma %d\n", ret);
- nouveau_channel_put(&chan);
- return ret;
- }
+ nouveau_dma_pre_init(chan);
+ chan->user_put = 0x40;
+ chan->user_get = 0x44;
/* disable the fifo caches */
pfifo->reassign(dev, false);
@@ -220,6 +211,11 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
nouveau_debugfs_channel_init(chan);
NV_DEBUG(dev, "channel %d initialised\n", chan->id);
+ if (fpriv) {
+ spin_lock(&fpriv->lock);
+ list_add(&chan->list, &fpriv->channels);
+ spin_unlock(&fpriv->lock);
+ }
*chan_ret = chan;
return 0;
}
@@ -236,29 +232,23 @@ nouveau_channel_get_unlocked(struct nouveau_channel *ref)
}
struct nouveau_channel *
-nouveau_channel_get(struct drm_device *dev, struct drm_file *file_priv, int id)
+nouveau_channel_get(struct drm_file *file_priv, int id)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
struct nouveau_channel *chan;
- unsigned long flags;
-
- if (unlikely(id < 0 || id >= NOUVEAU_MAX_CHANNEL_NR))
- return ERR_PTR(-EINVAL);
-
- spin_lock_irqsave(&dev_priv->channels.lock, flags);
- chan = nouveau_channel_get_unlocked(dev_priv->channels.ptr[id]);
- spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
- if (unlikely(!chan))
- return ERR_PTR(-EINVAL);
-
- if (unlikely(file_priv && chan->file_priv != file_priv)) {
- nouveau_channel_put_unlocked(&chan);
- return ERR_PTR(-EINVAL);
+ spin_lock(&fpriv->lock);
+ list_for_each_entry(chan, &fpriv->channels, list) {
+ if (chan->id == id) {
+ chan = nouveau_channel_get_unlocked(chan);
+ spin_unlock(&fpriv->lock);
+ mutex_lock(&chan->mutex);
+ return chan;
+ }
}
+ spin_unlock(&fpriv->lock);
- mutex_lock(&chan->mutex);
- return chan;
+ return ERR_PTR(-EINVAL);
}
void
@@ -312,12 +302,14 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan)
/* destroy any resources the channel owned */
nouveau_gpuobj_ref(NULL, &chan->pushbuf);
if (chan->pushbuf_bo) {
+ nouveau_bo_vma_del(chan->pushbuf_bo, &chan->pushbuf_vma);
nouveau_bo_unmap(chan->pushbuf_bo);
nouveau_bo_unpin(chan->pushbuf_bo);
nouveau_bo_ref(NULL, &chan->pushbuf_bo);
}
- nouveau_gpuobj_channel_takedown(chan);
+ nouveau_ramht_ref(NULL, &chan->ramht, chan);
nouveau_notifier_takedown_channel(chan);
+ nouveau_gpuobj_channel_takedown(chan);
nouveau_channel_ref(NULL, pchan);
}
@@ -383,10 +375,11 @@ nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv)
NV_DEBUG(dev, "clearing FIFO enables from file_priv\n");
for (i = 0; i < engine->fifo.channels; i++) {
- chan = nouveau_channel_get(dev, file_priv, i);
+ chan = nouveau_channel_get(file_priv, i);
if (IS_ERR(chan))
continue;
+ list_del(&chan->list);
atomic_dec(&chan->users);
nouveau_channel_put(&chan);
}
@@ -419,13 +412,17 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
return ret;
init->channel = chan->id;
- if (chan->dma.ib_max)
- init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
- NOUVEAU_GEM_DOMAIN_GART;
- else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM)
+ if (nouveau_vram_pushbuf == 0) {
+ if (chan->dma.ib_max)
+ init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
+ NOUVEAU_GEM_DOMAIN_GART;
+ else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM)
+ init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
+ else
+ init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
+ } else {
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
- else
- init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
+ }
if (dev_priv->card_type < NV_C0) {
init->subchan[0].handle = NvM2MF;
@@ -459,10 +456,11 @@ nouveau_ioctl_fifo_free(struct drm_device *dev, void *data,
struct drm_nouveau_channel_free *req = data;
struct nouveau_channel *chan;
- chan = nouveau_channel_get(dev, file_priv, req->channel);
+ chan = nouveau_channel_get(file_priv, req->channel);
if (IS_ERR(chan))
return PTR_ERR(chan);
+ list_del(&chan->list);
atomic_dec(&chan->users);
nouveau_channel_put(&chan);
return 0;
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 1595d0b6e815..cea6696b1906 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -39,8 +39,8 @@
static void nouveau_connector_hotplug(void *, int);
-static struct nouveau_encoder *
-find_encoder_by_type(struct drm_connector *connector, int type)
+struct nouveau_encoder *
+find_encoder(struct drm_connector *connector, int type)
{
struct drm_device *dev = connector->dev;
struct nouveau_encoder *nv_encoder;
@@ -116,10 +116,6 @@ nouveau_connector_destroy(struct drm_connector *connector)
nouveau_connector_hotplug, connector);
}
- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
- connector->connector_type == DRM_MODE_CONNECTOR_eDP)
- nouveau_backlight_exit(connector);
-
kfree(nv_connector->edid);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
@@ -170,8 +166,8 @@ nouveau_connector_of_detect(struct drm_connector *connector)
struct device_node *cn, *dn = pci_device_to_OF_node(dev->pdev);
if (!dn ||
- !((nv_encoder = find_encoder_by_type(connector, OUTPUT_TMDS)) ||
- (nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG))))
+ !((nv_encoder = find_encoder(connector, OUTPUT_TMDS)) ||
+ (nv_encoder = find_encoder(connector, OUTPUT_ANALOG))))
return NULL;
for_each_child_of_node(dn, cn) {
@@ -233,6 +229,7 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
struct drm_device *dev = connector->dev;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = NULL;
+ struct nouveau_encoder *nv_partner;
struct nouveau_i2c_chan *i2c;
int type;
@@ -266,19 +263,22 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
* same i2c channel so the value returned from ddc_detect
* isn't necessarily correct.
*/
- if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
+ nv_partner = NULL;
+ if (nv_encoder->dcb->type == OUTPUT_TMDS)
+ nv_partner = find_encoder(connector, OUTPUT_ANALOG);
+ if (nv_encoder->dcb->type == OUTPUT_ANALOG)
+ nv_partner = find_encoder(connector, OUTPUT_TMDS);
+
+ if (nv_partner && ((nv_encoder->dcb->type == OUTPUT_ANALOG &&
+ nv_partner->dcb->type == OUTPUT_TMDS) ||
+ (nv_encoder->dcb->type == OUTPUT_TMDS &&
+ nv_partner->dcb->type == OUTPUT_ANALOG))) {
if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
type = OUTPUT_TMDS;
else
type = OUTPUT_ANALOG;
- nv_encoder = find_encoder_by_type(connector, type);
- if (!nv_encoder) {
- NV_ERROR(dev, "Detected %d encoder on %s, "
- "but no object!\n", type,
- drm_get_connector_name(connector));
- return connector_status_disconnected;
- }
+ nv_encoder = find_encoder(connector, type);
}
nouveau_connector_set_encoder(connector, nv_encoder);
@@ -292,9 +292,9 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
}
detect_analog:
- nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG);
+ nv_encoder = find_encoder(connector, OUTPUT_ANALOG);
if (!nv_encoder && !nouveau_tv_disable)
- nv_encoder = find_encoder_by_type(connector, OUTPUT_TV);
+ nv_encoder = find_encoder(connector, OUTPUT_TV);
if (nv_encoder && force) {
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
struct drm_encoder_helper_funcs *helper =
@@ -327,7 +327,7 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
nv_connector->edid = NULL;
}
- nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
+ nv_encoder = find_encoder(connector, OUTPUT_LVDS);
if (!nv_encoder)
return connector_status_disconnected;
@@ -405,7 +405,7 @@ nouveau_connector_force(struct drm_connector *connector)
} else
type = OUTPUT_ANY;
- nv_encoder = find_encoder_by_type(connector, type);
+ nv_encoder = find_encoder(connector, type);
if (!nv_encoder) {
NV_ERROR(connector->dev, "can't find encoder to force %s on!\n",
drm_get_connector_name(connector));
@@ -708,12 +708,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
case OUTPUT_TV:
return get_slave_funcs(encoder)->mode_valid(encoder, mode);
case OUTPUT_DP:
- if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7)
- max_clock = nv_encoder->dp.link_nr * 270000;
- else
- max_clock = nv_encoder->dp.link_nr * 162000;
-
- clock = clock * nouveau_connector_bpp(connector) / 8;
+ max_clock = nv_encoder->dp.link_nr;
+ max_clock *= nv_encoder->dp.link_bw;
+ clock = clock * nouveau_connector_bpp(connector) / 10;
break;
default:
BUG_ON(1);
@@ -867,7 +864,6 @@ nouveau_connector_create(struct drm_device *dev, int index)
dev->mode_config.scaling_mode_property,
nv_connector->scaling_mode);
}
- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
/* fall-through */
case DCB_CONNECTOR_TV_0:
case DCB_CONNECTOR_TV_1:
@@ -884,27 +880,20 @@ nouveau_connector_create(struct drm_device *dev, int index)
dev->mode_config.dithering_mode_property,
nv_connector->use_dithering ?
DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF);
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) {
- if (dev_priv->card_type >= NV_50)
- connector->polled = DRM_CONNECTOR_POLL_HPD;
- else
- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
- }
break;
}
- if (pgpio->irq_register) {
+ if (nv_connector->dcb->gpio_tag != 0xff && pgpio->irq_register) {
pgpio->irq_register(dev, nv_connector->dcb->gpio_tag,
nouveau_connector_hotplug, connector);
+
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ } else {
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT;
}
drm_sysfs_connector_add(connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
- connector->connector_type == DRM_MODE_CONNECTOR_eDP)
- nouveau_backlight_init(connector);
-
dcb->drm = connector;
return dcb->drm;
@@ -921,22 +910,13 @@ nouveau_connector_hotplug(void *data, int plugged)
struct drm_connector *connector = data;
struct drm_device *dev = connector->dev;
- NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un",
- drm_get_connector_name(connector));
-
- if (connector->encoder && connector->encoder->crtc &&
- connector->encoder->crtc->enabled) {
- struct nouveau_encoder *nv_encoder = nouveau_encoder(connector->encoder);
- struct drm_encoder_helper_funcs *helper =
- connector->encoder->helper_private;
+ NV_DEBUG(dev, "%splugged %s\n", plugged ? "" : "un",
+ drm_get_connector_name(connector));
- if (nv_encoder->dcb->type == OUTPUT_DP) {
- if (plugged)
- helper->dpms(connector->encoder, DRM_MODE_DPMS_ON);
- else
- helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF);
- }
- }
+ if (plugged)
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
+ else
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
drm_helper_hpd_irq_event(dev);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h
index cb1ce2a09162..bf8e1289953d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_crtc.h
+++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h
@@ -82,14 +82,13 @@ static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc)
}
int nv50_crtc_create(struct drm_device *dev, int index);
-int nv50_cursor_init(struct nouveau_crtc *);
-void nv50_cursor_fini(struct nouveau_crtc *);
int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv,
uint32_t buffer_handle, uint32_t width,
uint32_t height);
int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y);
int nv04_cursor_init(struct nouveau_crtc *);
+int nv50_cursor_init(struct nouveau_crtc *);
struct nouveau_connector *
nouveau_crtc_connector_get(struct nouveau_crtc *crtc);
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index eb514ea29377..ddbabefb4273 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -105,9 +105,12 @@ nouveau_framebuffer_init(struct drm_device *dev,
if (dev_priv->chipset == 0x50)
nv_fb->r_format |= (tile_flags << 8);
- if (!tile_flags)
- nv_fb->r_pitch = 0x00100000 | fb->pitch;
- else {
+ if (!tile_flags) {
+ if (dev_priv->card_type < NV_D0)
+ nv_fb->r_pitch = 0x00100000 | fb->pitch;
+ else
+ nv_fb->r_pitch = 0x01000000 | fb->pitch;
+ } else {
u32 mode = nvbo->tile_mode;
if (dev_priv->card_type >= NV_C0)
mode >>= 4;
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
index 568caedd7216..00bc6eaad558 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -167,8 +167,13 @@ nv50_dma_push(struct nouveau_channel *chan, struct nouveau_bo *bo,
int delta, int length)
{
struct nouveau_bo *pb = chan->pushbuf_bo;
- uint64_t offset = bo->bo.offset + delta;
+ struct nouveau_vma *vma;
int ip = (chan->dma.ib_put * 2) + chan->dma.ib_base;
+ u64 offset;
+
+ vma = nouveau_bo_vma_find(bo, chan->vm);
+ BUG_ON(!vma);
+ offset = vma->offset + delta;
BUG_ON(chan->dma.ib_free < 1);
nouveau_bo_wr32(pb, ip++, lower_32_bits(offset));
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 7beb82a0315d..de5efe71fefd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -28,418 +28,619 @@
#include "nouveau_i2c.h"
#include "nouveau_connector.h"
#include "nouveau_encoder.h"
+#include "nouveau_crtc.h"
+
+/******************************************************************************
+ * aux channel util functions
+ *****************************************************************************/
+#define AUX_DBG(fmt, args...) do { \
+ if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_AUXCH) { \
+ NV_PRINTK(KERN_DEBUG, dev, "AUXCH(%d): " fmt, ch, ##args); \
+ } \
+} while (0)
+#define AUX_ERR(fmt, args...) NV_ERROR(dev, "AUXCH(%d): " fmt, ch, ##args)
+
+static void
+auxch_fini(struct drm_device *dev, int ch)
+{
+ nv_mask(dev, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000);
+}
static int
-auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
+auxch_init(struct drm_device *dev, int ch)
{
- struct drm_device *dev = encoder->dev;
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct nouveau_i2c_chan *auxch;
- int ret;
-
- auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
- if (!auxch)
- return -ENODEV;
-
- ret = nouveau_dp_auxch(auxch, 9, address, buf, size);
- if (ret)
- return ret;
+ const u32 unksel = 1; /* nfi which to use, or if it matters.. */
+ const u32 ureq = unksel ? 0x00100000 : 0x00200000;
+ const u32 urep = unksel ? 0x01000000 : 0x02000000;
+ u32 ctrl, timeout;
+
+ /* wait up to 1ms for any previous transaction to be done... */
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("begin idle timeout 0x%08x", ctrl);
+ return -EBUSY;
+ }
+ } while (ctrl & 0x03010000);
+
+ /* set some magic, and wait up to 1ms for it to appear */
+ nv_mask(dev, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq);
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("magic wait 0x%08x\n", ctrl);
+ auxch_fini(dev, ch);
+ return -EBUSY;
+ }
+ } while ((ctrl & 0x03000000) != urep);
return 0;
}
static int
-auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
+auxch_tx(struct drm_device *dev, int ch, u8 type, u32 addr, u8 *data, u8 size)
{
- struct drm_device *dev = encoder->dev;
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct nouveau_i2c_chan *auxch;
- int ret;
+ u32 ctrl, stat, timeout, retries;
+ u32 xbuf[4] = {};
+ int ret, i;
- auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
- if (!auxch)
- return -ENODEV;
+ AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
- ret = nouveau_dp_auxch(auxch, 8, address, buf, size);
- return ret;
-}
+ ret = auxch_init(dev, ch);
+ if (ret)
+ goto out;
-static int
-nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd)
-{
- struct drm_device *dev = encoder->dev;
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- uint32_t tmp;
- int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
-
- tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
- tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED |
- NV50_SOR_DP_CTRL_LANE_MASK);
- tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16;
- if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN)
- tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED;
- nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
-
- return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1);
-}
+ stat = nv_rd32(dev, 0x00e4e8 + (ch * 0x50));
+ if (!(stat & 0x10000000)) {
+ AUX_DBG("sink not detected\n");
+ ret = -ENXIO;
+ goto out;
+ }
-static int
-nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd)
-{
- struct drm_device *dev = encoder->dev;
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- uint32_t tmp;
- int reg = 0x614300 + (nv_encoder->or * 0x800);
+ if (!(type & 1)) {
+ memcpy(xbuf, data, size);
+ for (i = 0; i < 16; i += 4) {
+ AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
+ nv_wr32(dev, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]);
+ }
+ }
- tmp = nv_rd32(dev, reg);
- tmp &= 0xfff3ffff;
- if (cmd == DP_LINK_BW_2_7)
- tmp |= 0x00040000;
- nv_wr32(dev, reg, tmp);
+ ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50));
+ ctrl &= ~0x0001f0ff;
+ ctrl |= type << 12;
+ ctrl |= size - 1;
+ nv_wr32(dev, 0x00e4e0 + (ch * 0x50), addr);
+
+ /* retry transaction a number of times on failure... */
+ ret = -EREMOTEIO;
+ for (retries = 0; retries < 32; retries++) {
+ /* reset, and delay a while if this is a retry */
+ nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
+ nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
+ if (retries)
+ udelay(400);
+
+ /* transaction request, wait up to 1ms for it to complete */
+ nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl);
+
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("tx req timeout 0x%08x\n", ctrl);
+ goto out;
+ }
+ } while (ctrl & 0x00010000);
- return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1);
-}
+ /* read status, and check if transaction completed ok */
+ stat = nv_mask(dev, 0x00e4e8 + (ch * 0x50), 0, 0);
+ if (!(stat & 0x000f0f00)) {
+ ret = 0;
+ break;
+ }
-static int
-nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern)
-{
- struct drm_device *dev = encoder->dev;
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- uint32_t tmp;
- uint8_t cmd;
- int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
- int ret;
+ AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
+ }
- tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
- tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN;
- tmp |= (pattern << 24);
- nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
+ if (type & 1) {
+ for (i = 0; i < 16; i += 4) {
+ xbuf[i / 4] = nv_rd32(dev, 0x00e4d0 + (ch * 0x50) + i);
+ AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
+ }
+ memcpy(data, xbuf, size);
+ }
- ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1);
- if (ret)
- return ret;
- cmd &= ~DP_TRAINING_PATTERN_MASK;
- cmd |= (pattern & DP_TRAINING_PATTERN_MASK);
- return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1);
+out:
+ auxch_fini(dev, ch);
+ return ret;
}
-static int
-nouveau_dp_max_voltage_swing(struct drm_encoder *encoder)
+static u32
+dp_link_bw_get(struct drm_device *dev, int or, int link)
{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct bit_displayport_encoder_table_entry *dpse;
- struct bit_displayport_encoder_table *dpe;
- int i, dpe_headerlen, max_vs = 0;
-
- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
- if (!dpe)
- return false;
- dpse = (void *)((char *)dpe + dpe_headerlen);
+ u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800));
+ if (!(ctrl & 0x000c0000))
+ return 162000;
+ return 270000;
+}
- for (i = 0; i < dpe_headerlen; i++, dpse++) {
- if (dpse->vs_level > max_vs)
- max_vs = dpse->vs_level;
+static int
+dp_lane_count_get(struct drm_device *dev, int or, int link)
+{
+ u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
+ switch (ctrl & 0x000f0000) {
+ case 0x00010000: return 1;
+ case 0x00030000: return 2;
+ default:
+ return 4;
}
-
- return max_vs;
}
-static int
-nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs)
+void
+nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct bit_displayport_encoder_table_entry *dpse;
- struct bit_displayport_encoder_table *dpe;
- int i, dpe_headerlen, max_pre = 0;
+ const u32 symbol = 100000;
+ int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
+ int TU, VTUi, VTUf, VTUa;
+ u64 link_data_rate, link_ratio, unk;
+ u32 best_diff = 64 * symbol;
+ u32 link_nr, link_bw, r;
+
+ /* calculate packed data rate for each lane */
+ link_nr = dp_lane_count_get(dev, or, link);
+ link_data_rate = (clk * bpp / 8) / link_nr;
+
+ /* calculate ratio of packed data rate to link symbol rate */
+ link_bw = dp_link_bw_get(dev, or, link);
+ link_ratio = link_data_rate * symbol;
+ r = do_div(link_ratio, link_bw);
+
+ for (TU = 64; TU >= 32; TU--) {
+ /* calculate average number of valid symbols in each TU */
+ u32 tu_valid = link_ratio * TU;
+ u32 calc, diff;
+
+ /* find a hw representation for the fraction.. */
+ VTUi = tu_valid / symbol;
+ calc = VTUi * symbol;
+ diff = tu_valid - calc;
+ if (diff) {
+ if (diff >= (symbol / 2)) {
+ VTUf = symbol / (symbol - diff);
+ if (symbol - (VTUf * diff))
+ VTUf++;
+
+ if (VTUf <= 15) {
+ VTUa = 1;
+ calc += symbol - (symbol / VTUf);
+ } else {
+ VTUa = 0;
+ VTUf = 1;
+ calc += symbol;
+ }
+ } else {
+ VTUa = 0;
+ VTUf = min((int)(symbol / diff), 15);
+ calc += symbol / VTUf;
+ }
- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
- if (!dpe)
- return false;
- dpse = (void *)((char *)dpe + dpe_headerlen);
+ diff = calc - tu_valid;
+ } else {
+ /* no remainder, but the hw doesn't like the fractional
+ * part to be zero. decrement the integer part and
+ * have the fraction add a whole symbol back
+ */
+ VTUa = 0;
+ VTUf = 1;
+ VTUi--;
+ }
- for (i = 0; i < dpe_headerlen; i++, dpse++) {
- if (dpse->vs_level != vs)
- continue;
+ if (diff < best_diff) {
+ best_diff = diff;
+ bestTU = TU;
+ bestVTUa = VTUa;
+ bestVTUf = VTUf;
+ bestVTUi = VTUi;
+ if (diff == 0)
+ break;
+ }
+ }
- if (dpse->pre_level > max_pre)
- max_pre = dpse->pre_level;
+ if (!bestTU) {
+ NV_ERROR(dev, "DP: unable to find suitable config\n");
+ return;
}
- return max_pre;
+ /* XXX close to vbios numbers, but not right */
+ unk = (symbol - link_ratio) * bestTU;
+ unk *= link_ratio;
+ r = do_div(unk, symbol);
+ r = do_div(unk, symbol);
+ unk += 6;
+
+ nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2);
+ nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 |
+ bestVTUf << 16 |
+ bestVTUi << 8 |
+ unk);
}
-static bool
-nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config)
+u8 *
+nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct bit_displayport_encoder_table *dpe;
- int ret, i, dpe_headerlen, vs = 0, pre = 0;
- uint8_t request[2];
-
- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
- if (!dpe)
- return false;
-
- ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2);
- if (ret)
- return false;
-
- NV_DEBUG_KMS(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]);
-
- /* Keep all lanes at the same level.. */
- for (i = 0; i < nv_encoder->dp.link_nr; i++) {
- int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf;
- int lane_vs = lane_req & 3;
- int lane_pre = (lane_req >> 2) & 3;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ struct bit_entry d;
+ u8 *table;
+ int i;
+
+ if (bit_table(dev, 'd', &d)) {
+ NV_ERROR(dev, "BIT 'd' table not found\n");
+ return NULL;
+ }
- if (lane_vs > vs)
- vs = lane_vs;
- if (lane_pre > pre)
- pre = lane_pre;
+ if (d.version != 1) {
+ NV_ERROR(dev, "BIT 'd' table version %d unknown\n", d.version);
+ return NULL;
}
- if (vs >= nouveau_dp_max_voltage_swing(encoder)) {
- vs = nouveau_dp_max_voltage_swing(encoder);
- vs |= 4;
+ table = ROMPTR(bios, d.data[0]);
+ if (!table) {
+ NV_ERROR(dev, "displayport table pointer invalid\n");
+ return NULL;
}
- if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) {
- pre = nouveau_dp_max_pre_emphasis(encoder, vs & 3);
- pre |= 4;
+ switch (table[0]) {
+ case 0x20:
+ case 0x21:
+ case 0x30:
+ break;
+ default:
+ NV_ERROR(dev, "displayport table 0x%02x unknown\n", table[0]);
+ return NULL;
}
- /* Update the configuration for all lanes.. */
- for (i = 0; i < nv_encoder->dp.link_nr; i++)
- config[i] = (pre << 3) | vs;
+ for (i = 0; i < table[3]; i++) {
+ *entry = ROMPTR(bios, table[table[1] + (i * table[2])]);
+ if (*entry && bios_encoder_match(dcb, ROM32((*entry)[0])))
+ return table;
+ }
- return true;
+ NV_ERROR(dev, "displayport encoder table not found\n");
+ return NULL;
}
-static bool
-nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct bit_displayport_encoder_table_entry *dpse;
- struct bit_displayport_encoder_table *dpe;
- int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
- int dpe_headerlen, ret, i;
+/******************************************************************************
+ * link training
+ *****************************************************************************/
+struct dp_state {
+ struct dcb_entry *dcb;
+ u8 *table;
+ u8 *entry;
+ int auxch;
+ int crtc;
+ int or;
+ int link;
+ u8 *dpcd;
+ int link_nr;
+ u32 link_bw;
+ u8 stat[6];
+ u8 conf[4];
+};
- NV_DEBUG_KMS(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n",
- config[0], config[1], config[2], config[3]);
+static void
+dp_set_link_config(struct drm_device *dev, struct dp_state *dp)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int or = dp->or, link = dp->link;
+ u8 *entry, sink[2];
+ u32 dp_ctrl;
+ u16 script;
+
+ NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
+
+ /* set selected link rate on source */
+ switch (dp->link_bw) {
+ case 270000:
+ nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00040000);
+ sink[0] = DP_LINK_BW_2_7;
+ break;
+ default:
+ nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00000000);
+ sink[0] = DP_LINK_BW_1_62;
+ break;
+ }
- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
- if (!dpe)
- return false;
- dpse = (void *)((char *)dpe + dpe_headerlen);
+ /* offset +0x0a of each dp encoder table entry is a pointer to another
+ * table, that has (among other things) pointers to more scripts that
+ * need to be executed, this time depending on link speed.
+ */
+ entry = ROMPTR(&dev_priv->vbios, dp->entry[10]);
+ if (entry) {
+ if (dp->table[0] < 0x30) {
+ while (dp->link_bw < (ROM16(entry[0]) * 10))
+ entry += 4;
+ script = ROM16(entry[2]);
+ } else {
+ while (dp->link_bw < (entry[0] * 27000))
+ entry += 3;
+ script = ROM16(entry[1]);
+ }
- for (i = 0; i < dpe->record_nr; i++, dpse++) {
- if (dpse->vs_level == (config[0] & 3) &&
- dpse->pre_level == ((config[0] >> 3) & 3))
- break;
+ nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc);
}
- BUG_ON(i == dpe->record_nr);
-
- for (i = 0; i < nv_encoder->dp.link_nr; i++) {
- const int shift[4] = { 16, 8, 0, 24 };
- uint32_t mask = 0xff << shift[i];
- uint32_t reg0, reg1, reg2;
-
- reg0 = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask;
- reg0 |= (dpse->reg0 << shift[i]);
- reg1 = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask;
- reg1 |= (dpse->reg1 << shift[i]);
- reg2 = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff;
- reg2 |= (dpse->reg2 << 8);
- nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0);
- nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1);
- nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2);
+
+ /* configure lane count on the source */
+ dp_ctrl = ((1 << dp->link_nr) - 1) << 16;
+ sink[1] = dp->link_nr;
+ if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) {
+ dp_ctrl |= 0x00004000;
+ sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
}
- ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4);
- if (ret)
- return false;
+ nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl);
- return true;
+ /* inform the sink of the new configuration */
+ auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2);
}
-bool
-nouveau_dp_link_train(struct drm_encoder *encoder)
+static void
+dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp)
{
- struct drm_device *dev = encoder->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct nouveau_connector *nv_connector;
- struct bit_displayport_encoder_table *dpe;
- int dpe_headerlen;
- uint8_t config[4], status[3];
- bool cr_done, cr_max_vs, eq_done, hpd_state;
- int ret = 0, i, tries, voltage;
+ u8 sink_tp;
- NV_DEBUG_KMS(dev, "link training!!\n");
+ NV_DEBUG_KMS(dev, "training pattern %d\n", tp);
- nv_connector = nouveau_encoder_connector_get(nv_encoder);
- if (!nv_connector)
- return false;
+ nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24);
- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
- if (!dpe) {
- NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or);
- return false;
- }
+ auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
+ sink_tp &= ~DP_TRAINING_PATTERN_MASK;
+ sink_tp |= tp;
+ auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
+}
- /* disable hotplug detect, this flips around on some panels during
- * link training.
- */
- hpd_state = pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false);
+static const u8 nv50_lane_map[] = { 16, 8, 0, 24 };
+static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 };
+
+static int
+dp_link_train_commit(struct drm_device *dev, struct dp_state *dp)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 mask = 0, drv = 0, pre = 0, unk = 0;
+ const u8 *shifts;
+ int link = dp->link;
+ int or = dp->or;
+ int i;
+
+ if (dev_priv->chipset != 0xaf)
+ shifts = nv50_lane_map;
+ else
+ shifts = nvaf_lane_map;
+
+ for (i = 0; i < dp->link_nr; i++) {
+ u8 *conf = dp->entry + dp->table[4];
+ u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
+ u8 lpre = (lane & 0x0c) >> 2;
+ u8 lvsw = (lane & 0x03) >> 0;
+
+ mask |= 0xff << shifts[i];
+ unk |= 1 << (shifts[i] >> 3);
+
+ dp->conf[i] = (lpre << 3) | lvsw;
+ if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200)
+ dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED;
+ if (lpre == DP_TRAIN_PRE_EMPHASIS_9_5)
+ dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+ NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]);
+
+ if (dp->table[0] < 0x30) {
+ u8 *last = conf + (dp->entry[4] * dp->table[5]);
+ while (lvsw != conf[0] || lpre != conf[1]) {
+ conf += dp->table[5];
+ if (conf >= last)
+ return -EINVAL;
+ }
+
+ conf += 2;
+ } else {
+ /* no lookup table anymore, set entries for each
+ * combination of voltage swing and pre-emphasis
+ * level allowed by the DP spec.
+ */
+ switch (lvsw) {
+ case 0: lpre += 0; break;
+ case 1: lpre += 4; break;
+ case 2: lpre += 7; break;
+ case 3: lpre += 9; break;
+ }
+
+ conf = conf + (lpre * dp->table[5]);
+ conf++;
+ }
- if (dpe->script0) {
- NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or);
- nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0),
- nv_encoder->dcb);
+ drv |= conf[0] << shifts[i];
+ pre |= conf[1] << shifts[i];
+ unk = (unk & ~0x0000ff00) | (conf[2] << 8);
}
-train:
- cr_done = eq_done = false;
+ nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv);
+ nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre);
+ nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk);
- /* set link configuration */
- NV_DEBUG_KMS(dev, "\tbegin train: bw %d, lanes %d\n",
- nv_encoder->dp.link_bw, nv_encoder->dp.link_nr);
+ return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4);
+}
- ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw);
- if (ret)
- return false;
+static int
+dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay)
+{
+ int ret;
- config[0] = nv_encoder->dp.link_nr;
- if (nv_encoder->dp.dpcd_version >= 0x11 &&
- nv_encoder->dp.enhanced_frame)
- config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+ udelay(delay);
- ret = nouveau_dp_lane_count_set(encoder, config[0]);
+ ret = auxch_tx(dev, dp->auxch, 9, DP_LANE0_1_STATUS, dp->stat, 6);
if (ret)
- return false;
+ return ret;
- /* clock recovery */
- NV_DEBUG_KMS(dev, "\tbegin cr\n");
- ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1);
- if (ret)
- goto stop;
+ NV_DEBUG_KMS(dev, "status %02x %02x %02x %02x %02x %02x\n",
+ dp->stat[0], dp->stat[1], dp->stat[2], dp->stat[3],
+ dp->stat[4], dp->stat[5]);
+ return 0;
+}
- tries = 0;
- voltage = -1;
- memset(config, 0x00, sizeof(config));
- for (;;) {
- if (!nouveau_dp_link_train_commit(encoder, config))
- break;
+static int
+dp_link_train_cr(struct drm_device *dev, struct dp_state *dp)
+{
+ bool cr_done = false, abort = false;
+ int voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+ int tries = 0, i;
- udelay(100);
+ dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_1);
- ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2);
- if (ret)
+ do {
+ if (dp_link_train_commit(dev, dp) ||
+ dp_link_train_update(dev, dp, 100))
break;
- NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n",
- status[0], status[1]);
cr_done = true;
- cr_max_vs = false;
- for (i = 0; i < nv_encoder->dp.link_nr; i++) {
- int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf;
-
+ for (i = 0; i < dp->link_nr; i++) {
+ u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
if (!(lane & DP_LANE_CR_DONE)) {
cr_done = false;
- if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED)
- cr_max_vs = true;
+ if (dp->conf[i] & DP_TRAIN_MAX_SWING_REACHED)
+ abort = true;
break;
}
}
- if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) {
- voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+ if ((dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) {
+ voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
tries = 0;
}
+ } while (!cr_done && !abort && ++tries < 5);
- if (cr_done || cr_max_vs || (++tries == 5))
- break;
-
- if (!nouveau_dp_link_train_adjust(encoder, config))
- break;
- }
-
- if (!cr_done)
- goto stop;
+ return cr_done ? 0 : -1;
+}
- /* channel equalisation */
- NV_DEBUG_KMS(dev, "\tbegin eq\n");
- ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2);
- if (ret)
- goto stop;
+static int
+dp_link_train_eq(struct drm_device *dev, struct dp_state *dp)
+{
+ bool eq_done, cr_done = true;
+ int tries = 0, i;
- for (tries = 0; tries <= 5; tries++) {
- udelay(400);
+ dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_2);
- ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3);
- if (ret)
+ do {
+ if (dp_link_train_update(dev, dp, 400))
break;
- NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n",
- status[0], status[1]);
- eq_done = true;
- if (!(status[2] & DP_INTERLANE_ALIGN_DONE))
- eq_done = false;
-
- for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) {
- int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf;
-
- if (!(lane & DP_LANE_CR_DONE)) {
+ eq_done = !!(dp->stat[2] & DP_INTERLANE_ALIGN_DONE);
+ for (i = 0; i < dp->link_nr && eq_done; i++) {
+ u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
+ if (!(lane & DP_LANE_CR_DONE))
cr_done = false;
- break;
- }
-
if (!(lane & DP_LANE_CHANNEL_EQ_DONE) ||
- !(lane & DP_LANE_SYMBOL_LOCKED)) {
+ !(lane & DP_LANE_SYMBOL_LOCKED))
eq_done = false;
- break;
- }
}
- if (eq_done || !cr_done)
+ if (dp_link_train_commit(dev, dp))
break;
+ } while (!eq_done && cr_done && ++tries <= 5);
- if (!nouveau_dp_link_train_adjust(encoder, config) ||
- !nouveau_dp_link_train_commit(encoder, config))
- break;
- }
+ return eq_done ? 0 : -1;
+}
-stop:
- /* end link training */
- ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_DISABLE);
- if (ret)
+bool
+nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
+{
+ struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
+ struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+ struct nouveau_connector *nv_connector =
+ nouveau_encoder_connector_get(nv_encoder);
+ struct drm_device *dev = encoder->dev;
+ struct nouveau_i2c_chan *auxch;
+ const u32 bw_list[] = { 270000, 162000, 0 };
+ const u32 *link_bw = bw_list;
+ struct dp_state dp;
+
+ auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+ if (!auxch)
return false;
- /* retry at a lower setting, if possible */
- if (!ret && !(eq_done && cr_done)) {
- NV_DEBUG_KMS(dev, "\twe failed\n");
- if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62) {
- NV_DEBUG_KMS(dev, "retry link training at low rate\n");
- nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
- goto train;
- }
+ dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry);
+ if (!dp.table)
+ return -EINVAL;
+
+ dp.dcb = nv_encoder->dcb;
+ dp.crtc = nv_crtc->index;
+ dp.auxch = auxch->rd;
+ dp.or = nv_encoder->or;
+ dp.link = !(nv_encoder->dcb->sorconf.link & 1);
+ dp.dpcd = nv_encoder->dp.dpcd;
+
+ /* some sinks toggle hotplug in response to some of the actions
+ * we take during link training (DP_SET_POWER is one), we need
+ * to ignore them for the moment to avoid races.
+ */
+ pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false);
+
+ /* enable down-spreading, if possible */
+ if (dp.table[1] >= 16) {
+ u16 script = ROM16(dp.entry[14]);
+ if (nv_encoder->dp.dpcd[3] & 1)
+ script = ROM16(dp.entry[12]);
+
+ nouveau_bios_run_init_table(dev, script, dp.dcb, dp.crtc);
}
- if (dpe->script1) {
- NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or);
- nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1),
- nv_encoder->dcb);
+ /* execute pre-train script from vbios */
+ nouveau_bios_run_init_table(dev, ROM16(dp.entry[6]), dp.dcb, dp.crtc);
+
+ /* start off at highest link rate supported by encoder and display */
+ while (*link_bw > nv_encoder->dp.link_bw)
+ link_bw++;
+
+ while (link_bw[0]) {
+ /* find minimum required lane count at this link rate */
+ dp.link_nr = nv_encoder->dp.link_nr;
+ while ((dp.link_nr >> 1) * link_bw[0] > datarate)
+ dp.link_nr >>= 1;
+
+ /* drop link rate to minimum with this lane count */
+ while ((link_bw[1] * dp.link_nr) > datarate)
+ link_bw++;
+ dp.link_bw = link_bw[0];
+
+ /* program selected link configuration */
+ dp_set_link_config(dev, &dp);
+
+ /* attempt to train the link at this configuration */
+ memset(dp.stat, 0x00, sizeof(dp.stat));
+ if (!dp_link_train_cr(dev, &dp) &&
+ !dp_link_train_eq(dev, &dp))
+ break;
+
+ /* retry at lower rate */
+ link_bw++;
}
- /* re-enable hotplug detect */
- pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, hpd_state);
+ /* finish link training */
+ dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE);
- return eq_done;
+ /* execute post-train script from vbios */
+ nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc);
+
+ /* re-enable hotplug detect */
+ pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true);
+ return true;
}
bool
@@ -447,31 +648,34 @@ nouveau_dp_detect(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
- uint8_t dpcd[4];
+ struct nouveau_i2c_chan *auxch;
+ u8 *dpcd = nv_encoder->dp.dpcd;
int ret;
- ret = auxch_rd(encoder, 0x0000, dpcd, 4);
- if (ret)
+ auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+ if (!auxch)
return false;
- NV_DEBUG_KMS(dev, "encoder: link_bw %d, link_nr %d\n"
- "display: link_bw %d, link_nr %d version 0x%02x\n",
- nv_encoder->dcb->dpconf.link_bw,
- nv_encoder->dcb->dpconf.link_nr,
- dpcd[1], dpcd[2] & 0x0f, dpcd[0]);
+ ret = auxch_tx(dev, auxch->rd, 9, DP_DPCD_REV, dpcd, 8);
+ if (ret)
+ return false;
- nv_encoder->dp.dpcd_version = dpcd[0];
+ nv_encoder->dp.link_bw = 27000 * dpcd[1];
+ nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK;
- nv_encoder->dp.link_bw = dpcd[1];
- if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62 &&
- !nv_encoder->dcb->dpconf.link_bw)
- nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
+ NV_DEBUG_KMS(dev, "display: %dx%d dpcd 0x%02x\n",
+ nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, dpcd[0]);
+ NV_DEBUG_KMS(dev, "encoder: %dx%d\n",
+ nv_encoder->dcb->dpconf.link_nr,
+ nv_encoder->dcb->dpconf.link_bw);
- nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK;
- if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr)
+ if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr)
nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr;
+ if (nv_encoder->dcb->dpconf.link_bw < nv_encoder->dp.link_bw)
+ nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw;
- nv_encoder->dp.enhanced_frame = (dpcd[2] & DP_ENHANCED_FRAME_CAP);
+ NV_DEBUG_KMS(dev, "maximum: %dx%d\n",
+ nv_encoder->dp.link_nr, nv_encoder->dp.link_bw);
return true;
}
@@ -480,105 +684,13 @@ int
nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
uint8_t *data, int data_nr)
{
- struct drm_device *dev = auxch->dev;
- uint32_t tmp, ctrl, stat = 0, data32[4] = {};
- int ret = 0, i, index = auxch->rd;
-
- NV_DEBUG_KMS(dev, "ch %d cmd %d addr 0x%x len %d\n", index, cmd, addr, data_nr);
-
- tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
- nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp | 0x00100000);
- tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
- if (!(tmp & 0x01000000)) {
- NV_ERROR(dev, "expected bit 24 == 1, got 0x%08x\n", tmp);
- ret = -EIO;
- goto out;
- }
-
- for (i = 0; i < 3; i++) {
- tmp = nv_rd32(dev, NV50_AUXCH_STAT(auxch->rd));
- if (tmp & NV50_AUXCH_STAT_STATE_READY)
- break;
- udelay(100);
- }
-
- if (i == 3) {
- ret = -EBUSY;
- goto out;
- }
-
- if (!(cmd & 1)) {
- memcpy(data32, data, data_nr);
- for (i = 0; i < 4; i++) {
- NV_DEBUG_KMS(dev, "wr %d: 0x%08x\n", i, data32[i]);
- nv_wr32(dev, NV50_AUXCH_DATA_OUT(index, i), data32[i]);
- }
- }
-
- nv_wr32(dev, NV50_AUXCH_ADDR(index), addr);
- ctrl = nv_rd32(dev, NV50_AUXCH_CTRL(index));
- ctrl &= ~(NV50_AUXCH_CTRL_CMD | NV50_AUXCH_CTRL_LEN);
- ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT);
- ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT);
-
- for (i = 0; i < 16; i++) {
- nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000);
- nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl);
- nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x00010000);
- if (!nv_wait(dev, NV50_AUXCH_CTRL(index),
- 0x00010000, 0x00000000)) {
- NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n",
- nv_rd32(dev, NV50_AUXCH_CTRL(index)));
- ret = -EBUSY;
- goto out;
- }
-
- udelay(400);
-
- stat = nv_rd32(dev, NV50_AUXCH_STAT(index));
- if ((stat & NV50_AUXCH_STAT_REPLY_AUX) !=
- NV50_AUXCH_STAT_REPLY_AUX_DEFER)
- break;
- }
-
- if (i == 16) {
- NV_ERROR(dev, "auxch DEFER too many times, bailing\n");
- ret = -EREMOTEIO;
- goto out;
- }
-
- if (cmd & 1) {
- if ((stat & NV50_AUXCH_STAT_COUNT) != data_nr) {
- ret = -EREMOTEIO;
- goto out;
- }
-
- for (i = 0; i < 4; i++) {
- data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i));
- NV_DEBUG_KMS(dev, "rd %d: 0x%08x\n", i, data32[i]);
- }
- memcpy(data, data32, data_nr);
- }
-
-out:
- tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
- nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp & ~0x00100000);
- tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
- if (tmp & 0x01000000) {
- NV_ERROR(dev, "expected bit 24 == 0, got 0x%08x\n", tmp);
- ret = -EIO;
- }
-
- udelay(400);
-
- return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY);
+ return auxch_tx(auxch->dev, auxch->rd, cmd, addr, data, data_nr);
}
static int
nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap;
- struct drm_device *dev = auxch->dev;
struct i2c_msg *msg = msgs;
int ret, mcnt = num;
@@ -602,19 +714,6 @@ nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (ret < 0)
return ret;
- switch (ret & NV50_AUXCH_STAT_REPLY_I2C) {
- case NV50_AUXCH_STAT_REPLY_I2C_ACK:
- break;
- case NV50_AUXCH_STAT_REPLY_I2C_NACK:
- return -EREMOTEIO;
- case NV50_AUXCH_STAT_REPLY_I2C_DEFER:
- udelay(100);
- continue;
- default:
- NV_ERROR(dev, "bad auxch reply: 0x%08x\n", ret);
- return -EREMOTEIO;
- }
-
ptr += cnt;
remaining -= cnt;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
index 02c6f37d8bd7..9f7bb1295262 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
@@ -23,6 +23,7 @@
*/
#include <linux/console.h>
+#include <linux/module.h>
#include "drmP.h"
#include "drm.h"
@@ -41,7 +42,7 @@ int nouveau_agpmode = -1;
module_param_named(agpmode, nouveau_agpmode, int, 0400);
MODULE_PARM_DESC(modeset, "Enable kernel modesetting");
-static int nouveau_modeset = -1; /* kms */
+int nouveau_modeset = -1;
module_param_named(modeset, nouveau_modeset, int, 0400);
MODULE_PARM_DESC(vbios, "Override default VBIOS location");
@@ -73,7 +74,7 @@ int nouveau_ignorelid = 0;
module_param_named(ignorelid, nouveau_ignorelid, int, 0400);
MODULE_PARM_DESC(noaccel, "Disable all acceleration");
-int nouveau_noaccel = 0;
+int nouveau_noaccel = -1;
module_param_named(noaccel, nouveau_noaccel, int, 0400);
MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration");
@@ -119,6 +120,10 @@ MODULE_PARM_DESC(msi, "Enable MSI (default: off)\n");
int nouveau_msi;
module_param_named(msi, nouveau_msi, int, 0400);
+MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)\n");
+int nouveau_ctxfw;
+module_param_named(ctxfw, nouveau_ctxfw, int, 0400);
+
int nouveau_fbpercrtc;
#if 0
module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400);
@@ -210,10 +215,13 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
pfifo->unload_context(dev);
for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) {
- if (dev_priv->eng[e]) {
- ret = dev_priv->eng[e]->fini(dev, e);
- if (ret)
- goto out_abort;
+ if (!dev_priv->eng[e])
+ continue;
+
+ ret = dev_priv->eng[e]->fini(dev, e, true);
+ if (ret) {
+ NV_ERROR(dev, "... engine %d failed: %d\n", i, ret);
+ goto out_abort;
}
}
@@ -354,7 +362,7 @@ nouveau_pci_resume(struct pci_dev *pdev)
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
- u32 offset = nv_crtc->cursor.nvbo->bo.mem.start << PAGE_SHIFT;
+ u32 offset = nv_crtc->cursor.nvbo->bo.offset;
nv_crtc->cursor.set_offset(nv_crtc, offset);
nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x,
@@ -389,7 +397,9 @@ static struct drm_driver driver = {
.firstopen = nouveau_firstopen,
.lastclose = nouveau_lastclose,
.unload = nouveau_unload,
+ .open = nouveau_open,
.preclose = nouveau_preclose,
+ .postclose = nouveau_postclose,
#if defined(CONFIG_DRM_NOUVEAU_DEBUG)
.debugfs_init = nouveau_debugfs_init,
.debugfs_cleanup = nouveau_debugfs_takedown,
@@ -420,6 +430,8 @@ static struct drm_driver driver = {
.gem_init_object = nouveau_gem_object_new,
.gem_free_object = nouveau_gem_object_del,
+ .gem_open_object = nouveau_gem_object_open,
+ .gem_close_object = nouveau_gem_object_close,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 9c56331941e2..29837da1098b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -46,9 +46,17 @@
#include "ttm/ttm_module.h"
struct nouveau_fpriv {
- struct ttm_object_file *tfile;
+ spinlock_t lock;
+ struct list_head channels;
+ struct nouveau_vm *vm;
};
+static inline struct nouveau_fpriv *
+nouveau_fpriv(struct drm_file *file_priv)
+{
+ return file_priv ? file_priv->driver_priv : NULL;
+}
+
#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
#include "nouveau_drm.h"
@@ -69,7 +77,7 @@ struct nouveau_mem {
struct drm_device *dev;
struct nouveau_vma bar_vma;
- struct nouveau_vma tmp_vma;
+ struct nouveau_vma vma[2];
u8 page_shift;
struct drm_mm_node *tag;
@@ -107,7 +115,8 @@ struct nouveau_bo {
struct nouveau_channel *channel;
- struct nouveau_vma vma;
+ struct list_head vma_list;
+ unsigned page_shift;
uint32_t tile_mode;
uint32_t tile_flags;
@@ -176,9 +185,10 @@ struct nouveau_gpuobj {
uint32_t flags;
u32 size;
- u32 pinst;
- u32 cinst;
- u64 vinst;
+ u32 pinst; /* PRAMIN BAR offset */
+ u32 cinst; /* Channel offset */
+ u64 vinst; /* VRAM address */
+ u64 linst; /* VM address */
uint32_t engine;
uint32_t class;
@@ -201,6 +211,7 @@ enum nouveau_channel_mutex_class {
struct nouveau_channel {
struct drm_device *dev;
+ struct list_head list;
int id;
/* references to the channel data structure */
@@ -228,15 +239,18 @@ struct nouveau_channel {
uint32_t sequence;
uint32_t sequence_ack;
atomic_t last_sequence_irq;
+ struct nouveau_vma vma;
} fence;
/* DMA push buffer */
struct nouveau_gpuobj *pushbuf;
struct nouveau_bo *pushbuf_bo;
+ struct nouveau_vma pushbuf_vma;
uint32_t pushbuf_base;
/* Notifier memory */
struct nouveau_bo *notifier_bo;
+ struct nouveau_vma notifier_vma;
struct drm_mm notifier_heap;
/* PFIFO context */
@@ -278,6 +292,7 @@ struct nouveau_channel {
uint32_t sw_subchannel[8];
+ struct nouveau_vma dispc_vma[2];
struct {
struct nouveau_gpuobj *vblsem;
uint32_t vblsem_head;
@@ -297,7 +312,7 @@ struct nouveau_channel {
struct nouveau_exec_engine {
void (*destroy)(struct drm_device *, int engine);
int (*init)(struct drm_device *, int engine);
- int (*fini)(struct drm_device *, int engine);
+ int (*fini)(struct drm_device *, int engine, bool suspend);
int (*context_new)(struct nouveau_channel *, int engine);
void (*context_del)(struct nouveau_channel *, int engine);
int (*object_new)(struct nouveau_channel *, int engine,
@@ -314,7 +329,8 @@ struct nouveau_instmem_engine {
int (*suspend)(struct drm_device *dev);
void (*resume)(struct drm_device *dev);
- int (*get)(struct nouveau_gpuobj *, u32 size, u32 align);
+ int (*get)(struct nouveau_gpuobj *, struct nouveau_channel *,
+ u32 size, u32 align);
void (*put)(struct nouveau_gpuobj *);
int (*map)(struct nouveau_gpuobj *);
void (*unmap)(struct nouveau_gpuobj *);
@@ -398,12 +414,13 @@ struct nouveau_gpio_engine {
};
struct nouveau_pm_voltage_level {
- u8 voltage;
- u8 vid;
+ u32 voltage; /* microvolts */
+ u8 vid;
};
struct nouveau_pm_voltage {
bool supported;
+ u8 version;
u8 vid_mask;
struct nouveau_pm_voltage_level *level;
@@ -412,17 +429,48 @@ struct nouveau_pm_voltage {
struct nouveau_pm_memtiming {
int id;
- u32 reg_100220;
- u32 reg_100224;
- u32 reg_100228;
- u32 reg_10022c;
- u32 reg_100230;
- u32 reg_100234;
- u32 reg_100238;
- u32 reg_10023c;
- u32 reg_100240;
+ u32 reg_0; /* 0x10f290 on Fermi, 0x100220 for older */
+ u32 reg_1;
+ u32 reg_2;
+ u32 reg_3;
+ u32 reg_4;
+ u32 reg_5;
+ u32 reg_6;
+ u32 reg_7;
+ u32 reg_8;
+ /* To be written to 0x1002c0 */
+ u8 CL;
+ u8 WR;
};
+struct nouveau_pm_tbl_header{
+ u8 version;
+ u8 header_len;
+ u8 entry_cnt;
+ u8 entry_len;
+};
+
+struct nouveau_pm_tbl_entry{
+ u8 tWR;
+ u8 tUNK_1;
+ u8 tCL;
+ u8 tRP; /* Byte 3 */
+ u8 empty_4;
+ u8 tRAS; /* Byte 5 */
+ u8 empty_6;
+ u8 tRFC; /* Byte 7 */
+ u8 empty_8;
+ u8 tRC; /* Byte 9 */
+ u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
+ u8 empty_15,empty_16,empty_17;
+ u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
+};
+
+/* nouveau_mem.c */
+void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
+ struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
+ struct nouveau_pm_memtiming *timing);
+
#define NOUVEAU_PM_MAX_LEVEL 8
struct nouveau_pm_level {
struct device_attribute dev_attr;
@@ -432,11 +480,19 @@ struct nouveau_pm_level {
u32 core;
u32 memory;
u32 shader;
- u32 unk05;
- u32 unk0a;
-
- u8 voltage;
- u8 fanspeed;
+ u32 rop;
+ u32 copy;
+ u32 daemon;
+ u32 vdec;
+ u32 unk05; /* nv50:nva3, roughly.. */
+ u32 unka0; /* nva3:nvc0 */
+ u32 hub01; /* nvc0- */
+ u32 hub06; /* nvc0- */
+ u32 hub07; /* nvc0- */
+
+ u32 volt_min; /* microvolts */
+ u32 volt_max;
+ u8 fanspeed;
u16 memscript;
struct nouveau_pm_memtiming *timing;
@@ -445,9 +501,9 @@ struct nouveau_pm_level {
struct nouveau_pm_temp_sensor_constants {
u16 offset_constant;
s16 offset_mult;
- u16 offset_div;
- u16 slope_mult;
- u16 slope_div;
+ s16 offset_div;
+ s16 slope_mult;
+ s16 slope_div;
};
struct nouveau_pm_threshold_temp {
@@ -480,6 +536,11 @@ struct nouveau_pm_engine {
void *(*clock_pre)(struct drm_device *, struct nouveau_pm_level *,
u32 id, int khz);
void (*clock_set)(struct drm_device *, void *);
+
+ int (*clocks_get)(struct drm_device *, struct nouveau_pm_level *);
+ void *(*clocks_pre)(struct drm_device *, struct nouveau_pm_level *);
+ void (*clocks_set)(struct drm_device *, void *);
+
int (*voltage_get)(struct drm_device *);
int (*voltage_set)(struct drm_device *, int voltage);
int (*fanspeed_get)(struct drm_device *);
@@ -488,7 +549,10 @@ struct nouveau_pm_engine {
};
struct nouveau_vram_engine {
+ struct nouveau_mm mm;
+
int (*init)(struct drm_device *);
+ void (*takedown)(struct drm_device *dev);
int (*get)(struct drm_device *, u64, u32 align, u32 size_nc,
u32 type, struct nouveau_mem **);
void (*put)(struct drm_device *, struct nouveau_mem **);
@@ -604,17 +668,19 @@ enum nouveau_card_type {
NV_40 = 0x40,
NV_50 = 0x50,
NV_C0 = 0xc0,
+ NV_D0 = 0xd0
};
struct drm_nouveau_private {
struct drm_device *dev;
+ bool noaccel;
/* the card type, takes NV_* as values */
enum nouveau_card_type card_type;
/* exact chipset, derived from NV_PMC_BOOT_0 */
int chipset;
- int stepping;
int flags;
+ u32 crystal;
void __iomem *mmio;
@@ -700,9 +766,7 @@ struct drm_nouveau_private {
/* VRAM/fb configuration */
uint64_t vram_size;
uint64_t vram_sys_base;
- u32 vram_rblock_size;
- uint64_t fb_phys;
uint64_t fb_available_size;
uint64_t fb_mappable_pages;
uint64_t fb_aper_free;
@@ -765,6 +829,7 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo)
}
/* nouveau_drv.c */
+extern int nouveau_modeset;
extern int nouveau_agpmode;
extern int nouveau_duallink;
extern int nouveau_uscript_lvds;
@@ -784,12 +849,15 @@ extern int nouveau_override_conntype;
extern char *nouveau_perflvl;
extern int nouveau_perflvl_wr;
extern int nouveau_msi;
+extern int nouveau_ctxfw;
extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state);
extern int nouveau_pci_resume(struct pci_dev *pdev);
/* nouveau_state.c */
+extern int nouveau_open(struct drm_device *, struct drm_file *);
extern void nouveau_preclose(struct drm_device *dev, struct drm_file *);
+extern void nouveau_postclose(struct drm_device *, struct drm_file *);
extern int nouveau_load(struct drm_device *, unsigned long flags);
extern int nouveau_firstopen(struct drm_device *);
extern void nouveau_lastclose(struct drm_device *);
@@ -802,6 +870,8 @@ extern bool nouveau_wait_eq(struct drm_device *, uint64_t timeout,
uint32_t reg, uint32_t mask, uint32_t val);
extern bool nouveau_wait_ne(struct drm_device *, uint64_t timeout,
uint32_t reg, uint32_t mask, uint32_t val);
+extern bool nouveau_wait_cb(struct drm_device *, u64 timeout,
+ bool (*cond)(void *), void *);
extern bool nouveau_wait_for_idle(struct drm_device *);
extern int nouveau_card_init(struct drm_device *);
@@ -847,7 +917,7 @@ extern int nouveau_channel_alloc(struct drm_device *dev,
extern struct nouveau_channel *
nouveau_channel_get_unlocked(struct nouveau_channel *);
extern struct nouveau_channel *
-nouveau_channel_get(struct drm_device *, struct drm_file *, int id);
+nouveau_channel_get(struct drm_file *, int id);
extern void nouveau_channel_put_unlocked(struct nouveau_channel **);
extern void nouveau_channel_put(struct nouveau_channel **);
extern void nouveau_channel_ref(struct nouveau_channel *chan,
@@ -984,15 +1054,15 @@ static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector
/* nouveau_backlight.c */
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
-extern int nouveau_backlight_init(struct drm_connector *);
-extern void nouveau_backlight_exit(struct drm_connector *);
+extern int nouveau_backlight_init(struct drm_device *);
+extern void nouveau_backlight_exit(struct drm_device *);
#else
-static inline int nouveau_backlight_init(struct drm_connector *dev)
+static inline int nouveau_backlight_init(struct drm_device *dev)
{
return 0;
}
-static inline void nouveau_backlight_exit(struct drm_connector *dev) { }
+static inline void nouveau_backlight_exit(struct drm_device *dev) { }
#endif
/* nouveau_bios.c */
@@ -1000,7 +1070,8 @@ extern int nouveau_bios_init(struct drm_device *);
extern void nouveau_bios_takedown(struct drm_device *dev);
extern int nouveau_run_vbios_init(struct drm_device *);
extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table,
- struct dcb_entry *);
+ struct dcb_entry *, int crtc);
+extern void nouveau_bios_init_exec(struct drm_device *, uint16_t table);
extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *,
enum dcb_gpio_tag);
extern struct dcb_connector_table_entry *
@@ -1008,11 +1079,8 @@ nouveau_bios_connector_entry(struct drm_device *, int index);
extern u32 get_pll_register(struct drm_device *, enum pll_types);
extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
struct pll_lims *);
-extern int nouveau_bios_run_display_table(struct drm_device *,
- struct dcb_entry *,
- uint32_t script, int pxclk);
-extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *,
- int *length);
+extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk,
+ struct dcb_entry *, int crtc);
extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *);
extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *);
extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk,
@@ -1021,6 +1089,7 @@ extern int run_tmds_table(struct drm_device *, struct dcb_entry *,
int head, int pxclk);
extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head,
enum LVDS_script, int pxclk);
+bool bios_encoder_match(struct dcb_entry *, u32 hash);
/* nouveau_ttm.c */
int nouveau_ttm_global_init(struct drm_nouveau_private *);
@@ -1031,7 +1100,9 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
uint8_t *data, int data_nr);
bool nouveau_dp_detect(struct drm_encoder *);
-bool nouveau_dp_link_train(struct drm_encoder *);
+bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate);
+void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32);
+u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **);
/* nv04_fb.c */
extern int nv04_fb_init(struct drm_device *);
@@ -1120,7 +1191,6 @@ extern int nvc0_fifo_unload_context(struct drm_device *);
/* nv04_graph.c */
extern int nv04_graph_create(struct drm_device *);
-extern void nv04_graph_fifo_access(struct drm_device *, bool);
extern int nv04_graph_object_new(struct nouveau_channel *, int, u32, u16);
extern int nv04_graph_mthd_page_flip(struct nouveau_channel *chan,
u32 class, u32 mthd, u32 data);
@@ -1158,8 +1228,8 @@ extern int nva3_copy_create(struct drm_device *dev);
/* nvc0_copy.c */
extern int nvc0_copy_create(struct drm_device *dev, int engine);
-/* nv40_mpeg.c */
-extern int nv40_mpeg_create(struct drm_device *dev);
+/* nv31_mpeg.c */
+extern int nv31_mpeg_create(struct drm_device *dev);
/* nv50_mpeg.c */
extern int nv50_mpeg_create(struct drm_device *dev);
@@ -1169,7 +1239,8 @@ extern int nv04_instmem_init(struct drm_device *);
extern void nv04_instmem_takedown(struct drm_device *);
extern int nv04_instmem_suspend(struct drm_device *);
extern void nv04_instmem_resume(struct drm_device *);
-extern int nv04_instmem_get(struct nouveau_gpuobj *, u32 size, u32 align);
+extern int nv04_instmem_get(struct nouveau_gpuobj *, struct nouveau_channel *,
+ u32 size, u32 align);
extern void nv04_instmem_put(struct nouveau_gpuobj *);
extern int nv04_instmem_map(struct nouveau_gpuobj *);
extern void nv04_instmem_unmap(struct nouveau_gpuobj *);
@@ -1180,7 +1251,8 @@ extern int nv50_instmem_init(struct drm_device *);
extern void nv50_instmem_takedown(struct drm_device *);
extern int nv50_instmem_suspend(struct drm_device *);
extern void nv50_instmem_resume(struct drm_device *);
-extern int nv50_instmem_get(struct nouveau_gpuobj *, u32 size, u32 align);
+extern int nv50_instmem_get(struct nouveau_gpuobj *, struct nouveau_channel *,
+ u32 size, u32 align);
extern void nv50_instmem_put(struct nouveau_gpuobj *);
extern int nv50_instmem_map(struct nouveau_gpuobj *);
extern void nv50_instmem_unmap(struct nouveau_gpuobj *);
@@ -1242,15 +1314,19 @@ extern int nv04_display_create(struct drm_device *);
extern int nv04_display_init(struct drm_device *);
extern void nv04_display_destroy(struct drm_device *);
+/* nvd0_display.c */
+extern int nvd0_display_create(struct drm_device *);
+extern int nvd0_display_init(struct drm_device *);
+extern void nvd0_display_destroy(struct drm_device *);
+
/* nv04_crtc.c */
extern int nv04_crtc_create(struct drm_device *, int index);
/* nouveau_bo.c */
extern struct ttm_bo_driver nouveau_bo_driver;
-extern int nouveau_bo_new(struct drm_device *, struct nouveau_channel *,
- int size, int align, uint32_t flags,
- uint32_t tile_mode, uint32_t tile_flags,
- struct nouveau_bo **);
+extern int nouveau_bo_new(struct drm_device *, int size, int align,
+ uint32_t flags, uint32_t tile_mode,
+ uint32_t tile_flags, struct nouveau_bo **);
extern int nouveau_bo_pin(struct nouveau_bo *, uint32_t flags);
extern int nouveau_bo_unpin(struct nouveau_bo *);
extern int nouveau_bo_map(struct nouveau_bo *);
@@ -1265,6 +1341,12 @@ extern void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *);
extern int nouveau_bo_validate(struct nouveau_bo *, bool interruptible,
bool no_wait_reserve, bool no_wait_gpu);
+extern struct nouveau_vma *
+nouveau_bo_vma_find(struct nouveau_bo *, struct nouveau_vm *);
+extern int nouveau_bo_vma_add(struct nouveau_bo *, struct nouveau_vm *,
+ struct nouveau_vma *);
+extern void nouveau_bo_vma_del(struct nouveau_bo *, struct nouveau_vma *);
+
/* nouveau_fence.c */
struct nouveau_fence;
extern int nouveau_fence_init(struct drm_device *);
@@ -1310,12 +1392,14 @@ static inline struct nouveau_fence *nouveau_fence_ref(struct nouveau_fence *obj)
}
/* nouveau_gem.c */
-extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *,
- int size, int align, uint32_t domain,
- uint32_t tile_mode, uint32_t tile_flags,
- struct nouveau_bo **);
+extern int nouveau_gem_new(struct drm_device *, int size, int align,
+ uint32_t domain, uint32_t tile_mode,
+ uint32_t tile_flags, struct nouveau_bo **);
extern int nouveau_gem_object_new(struct drm_gem_object *);
extern void nouveau_gem_object_del(struct drm_gem_object *);
+extern int nouveau_gem_object_open(struct drm_gem_object *, struct drm_file *);
+extern void nouveau_gem_object_close(struct drm_gem_object *,
+ struct drm_file *);
extern int nouveau_gem_ioctl_new(struct drm_device *, void *,
struct drm_file *);
extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *,
@@ -1344,6 +1428,8 @@ int nv50_gpio_init(struct drm_device *dev);
void nv50_gpio_fini(struct drm_device *dev);
int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
+int nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
+int nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
int nv50_gpio_irq_register(struct drm_device *, enum dcb_gpio_tag,
void (*)(void *, int), void *);
void nv50_gpio_irq_unregister(struct drm_device *, enum dcb_gpio_tag,
@@ -1418,6 +1504,8 @@ static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val)
nouveau_wait_eq(dev, 2000000000ULL, (reg), (mask), (val))
#define nv_wait_ne(dev, reg, mask, val) \
nouveau_wait_ne(dev, 2000000000ULL, (reg), (mask), (val))
+#define nv_wait_cb(dev, func, data) \
+ nouveau_wait_cb(dev, 2000000000ULL, (func), (data))
/* PRAMIN access */
static inline u32 nv_ri32(struct drm_device *dev, unsigned offset)
@@ -1484,6 +1572,7 @@ enum {
NOUVEAU_REG_DEBUG_RMVIO = 0x80,
NOUVEAU_REG_DEBUG_VGAATTR = 0x100,
NOUVEAU_REG_DEBUG_EVO = 0x200,
+ NOUVEAU_REG_DEBUG_AUXCH = 0x400
};
#define NV_REG_DEBUG(type, dev, fmt, arg...) do { \
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
index ae69b61d93db..e5d6e3faff3d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -49,17 +49,17 @@ struct nouveau_encoder {
union {
struct {
- int mc_unknown;
- uint32_t unk0;
- uint32_t unk1;
- int dpcd_version;
+ u8 dpcd[8];
int link_nr;
int link_bw;
- bool enhanced_frame;
+ u32 datarate;
} dp;
};
};
+struct nouveau_encoder *
+find_encoder(struct drm_connector *connector, int type);
+
static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc)
{
struct drm_encoder_slave *slave = to_encoder_slave(enc);
@@ -83,21 +83,4 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
int nv50_dac_create(struct drm_connector *, struct dcb_entry *);
-struct bit_displayport_encoder_table {
- uint32_t match;
- uint8_t record_nr;
- uint8_t unknown;
- uint16_t script0;
- uint16_t script1;
- uint16_t unknown_table;
-} __attribute__ ((packed));
-
-struct bit_displayport_encoder_table_entry {
- uint8_t vs_level;
- uint8_t pre_level;
- uint8_t reg0;
- uint8_t reg1;
- uint8_t reg2;
-} __attribute__ ((packed));
-
#endif /* __NOUVEAU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h
index a3a88ad00f86..95c843e684bb 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fb.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fb.h
@@ -30,6 +30,7 @@
struct nouveau_framebuffer {
struct drm_framebuffer base;
struct nouveau_bo *nvbo;
+ struct nouveau_vma vma;
u32 r_dma;
u32 r_format;
u32 r_pitch;
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 39aee6d4daf8..3a4cc32b9e44 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -279,6 +279,7 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
struct fb_info *info;
struct drm_framebuffer *fb;
struct nouveau_framebuffer *nouveau_fb;
+ struct nouveau_channel *chan;
struct nouveau_bo *nvbo;
struct drm_mode_fb_cmd mode_cmd;
struct pci_dev *pdev = dev->pdev;
@@ -296,8 +297,8 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
size = mode_cmd.pitch * mode_cmd.height;
size = roundup(size, PAGE_SIZE);
- ret = nouveau_gem_new(dev, dev_priv->channel, size, 0,
- NOUVEAU_GEM_DOMAIN_VRAM, 0, 0x0000, &nvbo);
+ ret = nouveau_gem_new(dev, size, 0, NOUVEAU_GEM_DOMAIN_VRAM,
+ 0, 0x0000, &nvbo);
if (ret) {
NV_ERROR(dev, "failed to allocate framebuffer\n");
goto out;
@@ -318,6 +319,15 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
goto out;
}
+ chan = nouveau_nofbaccel ? NULL : dev_priv->channel;
+ if (chan && dev_priv->card_type >= NV_50) {
+ ret = nouveau_bo_vma_add(nvbo, chan->vm, &nfbdev->nouveau_fb.vma);
+ if (ret) {
+ NV_ERROR(dev, "failed to map fb into chan: %d\n", ret);
+ chan = NULL;
+ }
+ }
+
mutex_lock(&dev->struct_mutex);
info = framebuffer_alloc(0, device);
@@ -448,6 +458,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *nfbdev)
if (nouveau_fb->nvbo) {
nouveau_bo_unmap(nouveau_fb->nvbo);
+ nouveau_bo_vma_del(nouveau_fb->nvbo, &nouveau_fb->vma);
drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem);
nouveau_fb->nvbo = NULL;
}
@@ -476,6 +487,7 @@ int nouveau_fbcon_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fbdev *nfbdev;
+ int preferred_bpp;
int ret;
nfbdev = kzalloc(sizeof(struct nouveau_fbdev), GFP_KERNEL);
@@ -494,7 +506,15 @@ int nouveau_fbcon_init(struct drm_device *dev)
}
drm_fb_helper_single_add_all_connectors(&nfbdev->helper);
- drm_fb_helper_initial_config(&nfbdev->helper, 32);
+
+ if (dev_priv->vram_size <= 32 * 1024 * 1024)
+ preferred_bpp = 8;
+ else if (dev_priv->vram_size <= 64 * 1024 * 1024)
+ preferred_bpp = 16;
+ else
+ preferred_bpp = 32;
+
+ drm_fb_helper_initial_config(&nfbdev->helper, preferred_bpp);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 7347075ca5b8..2f6daae68b9d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -336,6 +336,7 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct nouveau_fence *fence = NULL;
+ u64 offset = chan->fence.vma.offset + sema->mem->start;
int ret;
if (dev_priv->chipset < 0x84) {
@@ -345,13 +346,10 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 3);
OUT_RING (chan, NvSema);
- OUT_RING (chan, sema->mem->start);
+ OUT_RING (chan, offset);
OUT_RING (chan, 1);
} else
if (dev_priv->chipset < 0xc0) {
- struct nouveau_vma *vma = &dev_priv->fence.bo->vma;
- u64 offset = vma->offset + sema->mem->start;
-
ret = RING_SPACE(chan, 7);
if (ret)
return ret;
@@ -364,9 +362,6 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
OUT_RING (chan, 1);
OUT_RING (chan, 1); /* ACQUIRE_EQ */
} else {
- struct nouveau_vma *vma = &dev_priv->fence.bo->vma;
- u64 offset = vma->offset + sema->mem->start;
-
ret = RING_SPACE(chan, 5);
if (ret)
return ret;
@@ -394,6 +389,7 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct nouveau_fence *fence = NULL;
+ u64 offset = chan->fence.vma.offset + sema->mem->start;
int ret;
if (dev_priv->chipset < 0x84) {
@@ -403,14 +399,11 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 2);
OUT_RING (chan, NvSema);
- OUT_RING (chan, sema->mem->start);
+ OUT_RING (chan, offset);
BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_RELEASE, 1);
OUT_RING (chan, 1);
} else
if (dev_priv->chipset < 0xc0) {
- struct nouveau_vma *vma = &dev_priv->fence.bo->vma;
- u64 offset = vma->offset + sema->mem->start;
-
ret = RING_SPACE(chan, 7);
if (ret)
return ret;
@@ -423,9 +416,6 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
OUT_RING (chan, 1);
OUT_RING (chan, 2); /* RELEASE */
} else {
- struct nouveau_vma *vma = &dev_priv->fence.bo->vma;
- u64 offset = vma->offset + sema->mem->start;
-
ret = RING_SPACE(chan, 5);
if (ret)
return ret;
@@ -529,7 +519,7 @@ nouveau_fence_channel_init(struct nouveau_channel *chan)
if (USE_SEMA(dev) && dev_priv->chipset < 0x84) {
struct ttm_mem_reg *mem = &dev_priv->fence.bo->bo.mem;
- ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
+ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_FROM_MEMORY,
mem->start << PAGE_SHIFT,
mem->size, NV_MEM_ACCESS_RW,
NV_MEM_TARGET_VRAM, &obj);
@@ -540,10 +530,15 @@ nouveau_fence_channel_init(struct nouveau_channel *chan)
nouveau_gpuobj_ref(NULL, &obj);
if (ret)
return ret;
+ } else
+ if (USE_SEMA(dev)) {
+ /* map fence bo into channel's vm */
+ ret = nouveau_bo_vma_add(dev_priv->fence.bo, chan->vm,
+ &chan->fence.vma);
+ if (ret)
+ return ret;
}
- INIT_LIST_HEAD(&chan->fence.pending);
- spin_lock_init(&chan->fence.lock);
atomic_set(&chan->fence.last_sequence_irq, 0);
return 0;
}
@@ -551,10 +546,10 @@ nouveau_fence_channel_init(struct nouveau_channel *chan)
void
nouveau_fence_channel_fini(struct nouveau_channel *chan)
{
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct nouveau_fence *tmp, *fence;
spin_lock(&chan->fence.lock);
-
list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
fence->signalled = true;
list_del(&fence->entry);
@@ -564,8 +559,9 @@ nouveau_fence_channel_fini(struct nouveau_channel *chan)
kref_put(&fence->refcount, nouveau_fence_del);
}
-
spin_unlock(&chan->fence.lock);
+
+ nouveau_bo_vma_del(dev_priv->fence.bo, &chan->fence.vma);
}
int
@@ -577,7 +573,7 @@ nouveau_fence_init(struct drm_device *dev)
/* Create a shared VRAM heap for cross-channel sync. */
if (USE_SEMA(dev)) {
- ret = nouveau_bo_new(dev, NULL, size, 0, TTM_PL_FLAG_VRAM,
+ ret = nouveau_bo_new(dev, size, 0, TTM_PL_FLAG_VRAM,
0, 0, &dev_priv->fence.bo);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index b52e46018245..5f0bc57fdaab 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -60,9 +60,71 @@ nouveau_gem_object_del(struct drm_gem_object *gem)
}
int
-nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan,
- int size, int align, uint32_t domain, uint32_t tile_mode,
- uint32_t tile_flags, struct nouveau_bo **pnvbo)
+nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
+{
+ struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
+ struct nouveau_bo *nvbo = nouveau_gem_object(gem);
+ struct nouveau_vma *vma;
+ int ret;
+
+ if (!fpriv->vm)
+ return 0;
+
+ ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+ if (ret)
+ return ret;
+
+ vma = nouveau_bo_vma_find(nvbo, fpriv->vm);
+ if (!vma) {
+ vma = kzalloc(sizeof(*vma), GFP_KERNEL);
+ if (!vma) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = nouveau_bo_vma_add(nvbo, fpriv->vm, vma);
+ if (ret) {
+ kfree(vma);
+ goto out;
+ }
+ } else {
+ vma->refcount++;
+ }
+
+out:
+ ttm_bo_unreserve(&nvbo->bo);
+ return ret;
+}
+
+void
+nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
+{
+ struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
+ struct nouveau_bo *nvbo = nouveau_gem_object(gem);
+ struct nouveau_vma *vma;
+ int ret;
+
+ if (!fpriv->vm)
+ return;
+
+ ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+ if (ret)
+ return;
+
+ vma = nouveau_bo_vma_find(nvbo, fpriv->vm);
+ if (vma) {
+ if (--vma->refcount == 0) {
+ nouveau_bo_vma_del(nvbo, vma);
+ kfree(vma);
+ }
+ }
+ ttm_bo_unreserve(&nvbo->bo);
+}
+
+int
+nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain,
+ uint32_t tile_mode, uint32_t tile_flags,
+ struct nouveau_bo **pnvbo)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_bo *nvbo;
@@ -76,7 +138,7 @@ nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan,
if (!flags || domain & NOUVEAU_GEM_DOMAIN_CPU)
flags |= TTM_PL_FLAG_SYSTEM;
- ret = nouveau_bo_new(dev, chan, size, align, flags, tile_mode,
+ ret = nouveau_bo_new(dev, size, align, flags, tile_mode,
tile_flags, pnvbo);
if (ret)
return ret;
@@ -103,17 +165,28 @@ nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan,
}
static int
-nouveau_gem_info(struct drm_gem_object *gem, struct drm_nouveau_gem_info *rep)
+nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
+ struct drm_nouveau_gem_info *rep)
{
+ struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
struct nouveau_bo *nvbo = nouveau_gem_object(gem);
+ struct nouveau_vma *vma;
if (nvbo->bo.mem.mem_type == TTM_PL_TT)
rep->domain = NOUVEAU_GEM_DOMAIN_GART;
else
rep->domain = NOUVEAU_GEM_DOMAIN_VRAM;
- rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
rep->offset = nvbo->bo.offset;
+ if (fpriv->vm) {
+ vma = nouveau_bo_vma_find(nvbo, fpriv->vm);
+ if (!vma)
+ return -EINVAL;
+
+ rep->offset = vma->offset;
+ }
+
+ rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
rep->map_handle = nvbo->bo.addr_space_offset;
rep->tile_mode = nvbo->tile_mode;
rep->tile_flags = nvbo->tile_flags;
@@ -127,7 +200,6 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_nouveau_gem_new *req = data;
struct nouveau_bo *nvbo = NULL;
- struct nouveau_channel *chan = NULL;
int ret = 0;
if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL))
@@ -138,28 +210,21 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
return -EINVAL;
}
- if (req->channel_hint) {
- chan = nouveau_channel_get(dev, file_priv, req->channel_hint);
- if (IS_ERR(chan))
- return PTR_ERR(chan);
- }
-
- ret = nouveau_gem_new(dev, chan, req->info.size, req->align,
+ ret = nouveau_gem_new(dev, req->info.size, req->align,
req->info.domain, req->info.tile_mode,
req->info.tile_flags, &nvbo);
- if (chan)
- nouveau_channel_put(&chan);
if (ret)
return ret;
- ret = nouveau_gem_info(nvbo->gem, &req->info);
- if (ret)
- goto out;
-
ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle);
+ if (ret == 0) {
+ ret = nouveau_gem_info(file_priv, nvbo->gem, &req->info);
+ if (ret)
+ drm_gem_handle_delete(file_priv, req->info.handle);
+ }
+
/* drop reference from allocate - handle holds it now */
drm_gem_object_unreference_unlocked(nvbo->gem);
-out:
return ret;
}
@@ -318,6 +383,7 @@ static int
validate_list(struct nouveau_channel *chan, struct list_head *list,
struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr)
{
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct drm_nouveau_gem_pushbuf_bo __user *upbbo =
(void __force __user *)(uintptr_t)user_pbbo_ptr;
struct drm_device *dev = chan->dev;
@@ -356,24 +422,26 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
return ret;
}
- if (nvbo->bo.offset == b->presumed.offset &&
- ((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
- b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
- (nvbo->bo.mem.mem_type == TTM_PL_TT &&
- b->presumed.domain & NOUVEAU_GEM_DOMAIN_GART)))
- continue;
+ if (dev_priv->card_type < NV_50) {
+ if (nvbo->bo.offset == b->presumed.offset &&
+ ((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
+ b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
+ (nvbo->bo.mem.mem_type == TTM_PL_TT &&
+ b->presumed.domain & NOUVEAU_GEM_DOMAIN_GART)))
+ continue;
- if (nvbo->bo.mem.mem_type == TTM_PL_TT)
- b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART;
- else
- b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM;
- b->presumed.offset = nvbo->bo.offset;
- b->presumed.valid = 0;
- relocs++;
-
- if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index].presumed,
- &b->presumed, sizeof(b->presumed)))
- return -EFAULT;
+ if (nvbo->bo.mem.mem_type == TTM_PL_TT)
+ b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART;
+ else
+ b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM;
+ b->presumed.offset = nvbo->bo.offset;
+ b->presumed.valid = 0;
+ relocs++;
+
+ if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index].presumed,
+ &b->presumed, sizeof(b->presumed)))
+ return -EFAULT;
+ }
}
return relocs;
@@ -548,7 +616,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
struct nouveau_fence *fence = NULL;
int i, j, ret = 0, do_reloc = 0;
- chan = nouveau_channel_get(dev, file_priv, req->channel);
+ chan = nouveau_channel_get(file_priv, req->channel);
if (IS_ERR(chan))
return PTR_ERR(chan);
@@ -782,7 +850,7 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data,
if (!gem)
return -ENOENT;
- ret = nouveau_gem_info(gem, req);
+ ret = nouveau_gem_info(file_priv, gem, req);
drm_gem_object_unreference_unlocked(gem);
return ret;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index cb389d014326..d39b2202b197 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -22,6 +22,8 @@
* Authors: Ben Skeggs
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_i2c.h"
@@ -107,6 +109,13 @@ nv4e_i2c_getsda(void *data)
return !!((nv_rd32(dev, i2c->rd) >> 16) & 8);
}
+static const uint32_t nv50_i2c_port[] = {
+ 0x00e138, 0x00e150, 0x00e168, 0x00e180,
+ 0x00e254, 0x00e274, 0x00e764, 0x00e780,
+ 0x00e79c, 0x00e7b8
+};
+#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port)
+
static int
nv50_i2c_getscl(void *data)
{
@@ -130,28 +139,32 @@ static void
nv50_i2c_setscl(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
- struct drm_device *dev = i2c->dev;
- nv_wr32(dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0));
+ nv_wr32(i2c->dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0));
}
static void
nv50_i2c_setsda(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
- struct drm_device *dev = i2c->dev;
- nv_wr32(dev, i2c->wr,
- (nv_rd32(dev, i2c->rd) & 1) | 4 | (state ? 2 : 0));
+ nv_mask(i2c->dev, i2c->wr, 0x00000006, 4 | (state ? 2 : 0));
i2c->data = state;
}
-static const uint32_t nv50_i2c_port[] = {
- 0x00e138, 0x00e150, 0x00e168, 0x00e180,
- 0x00e254, 0x00e274, 0x00e764, 0x00e780,
- 0x00e79c, 0x00e7b8
-};
-#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port)
+static int
+nvd0_i2c_getscl(void *data)
+{
+ struct nouveau_i2c_chan *i2c = data;
+ return !!(nv_rd32(i2c->dev, i2c->rd) & 0x10);
+}
+
+static int
+nvd0_i2c_getsda(void *data)
+{
+ struct nouveau_i2c_chan *i2c = data;
+ return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20);
+}
int
nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
@@ -163,7 +176,8 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
if (entry->chan)
return -EEXIST;
- if (dev_priv->card_type >= NV_50 && entry->read >= NV50_I2C_PORTS) {
+ if (dev_priv->card_type >= NV_50 &&
+ dev_priv->card_type <= NV_C0 && entry->read >= NV50_I2C_PORTS) {
NV_ERROR(dev, "unknown i2c port %d\n", entry->read);
return -EINVAL;
}
@@ -192,10 +206,17 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
case 5:
i2c->bit.setsda = nv50_i2c_setsda;
i2c->bit.setscl = nv50_i2c_setscl;
- i2c->bit.getsda = nv50_i2c_getsda;
- i2c->bit.getscl = nv50_i2c_getscl;
- i2c->rd = nv50_i2c_port[entry->read];
- i2c->wr = i2c->rd;
+ if (dev_priv->card_type < NV_D0) {
+ i2c->bit.getsda = nv50_i2c_getsda;
+ i2c->bit.getscl = nv50_i2c_getscl;
+ i2c->rd = nv50_i2c_port[entry->read];
+ i2c->wr = i2c->rd;
+ } else {
+ i2c->bit.getsda = nvd0_i2c_getsda;
+ i2c->bit.getscl = nvd0_i2c_getscl;
+ i2c->rd = 0x00d014 + (entry->read * 0x20);
+ i2c->wr = i2c->rd;
+ }
break;
case 6:
i2c->rd = entry->read;
@@ -267,7 +288,10 @@ nouveau_i2c_find(struct drm_device *dev, int index)
val = 0xe001;
}
- nv_wr32(dev, reg, (nv_rd32(dev, reg) & ~0xf003) | val);
+ /* nfi, but neither auxch or i2c work if it's 1 */
+ nv_mask(dev, reg + 0x0c, 0x00000001, 0x00000000);
+ /* nfi, but switches auxch vs normal i2c */
+ nv_mask(dev, reg + 0x00, 0x0000f003, val);
}
if (!i2c->chan && nouveau_i2c_init(dev, i2c, index))
@@ -309,7 +333,7 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what,
NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, index);
- for (i = 0; info[i].addr; i++) {
+ for (i = 0; i2c && info[i].addr; i++) {
if (nouveau_probe_i2c_addr(i2c, info[i].addr) &&
(!match || match(i2c, &info[i]))) {
NV_INFO(dev, "Detected %s: %s\n", what, info[i].type);
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
index 2ba7265bc967..868c7fd74854 100644
--- a/drivers/gpu/drm/nouveau/nouveau_irq.c
+++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
@@ -79,7 +79,7 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
int i;
stat = nv_rd32(dev, NV03_PMC_INTR_0);
- if (!stat)
+ if (stat == 0 || stat == ~0)
return IRQ_NONE;
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 5ee14d216ce8..36bec4807701 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -397,7 +397,7 @@ nouveau_mem_vram_init(struct drm_device *dev)
if (pci_dma_supported(dev->pdev, DMA_BIT_MASK(40)))
dma_bits = 40;
} else
- if (0 && drm_pci_device_is_pcie(dev) &&
+ if (0 && pci_is_pcie(dev->pdev) &&
dev_priv->chipset > 0x40 &&
dev_priv->chipset != 0x45) {
if (pci_dma_supported(dev->pdev, DMA_BIT_MASK(39)))
@@ -408,8 +408,6 @@ nouveau_mem_vram_init(struct drm_device *dev)
if (ret)
return ret;
- dev_priv->fb_phys = pci_resource_start(dev->pdev, 1);
-
ret = nouveau_ttm_global_init(dev_priv);
if (ret)
return ret;
@@ -423,38 +421,6 @@ nouveau_mem_vram_init(struct drm_device *dev)
return ret;
}
- /* reserve space at end of VRAM for PRAMIN */
- if (dev_priv->card_type >= NV_50) {
- dev_priv->ramin_rsvd_vram = 1 * 1024 * 1024;
- } else
- if (dev_priv->card_type >= NV_40) {
- u32 vs = hweight8((nv_rd32(dev, 0x001540) & 0x0000ff00) >> 8);
- u32 rsvd;
-
- /* estimate grctx size, the magics come from nv40_grctx.c */
- if (dev_priv->chipset == 0x40) rsvd = 0x6aa0 * vs;
- else if (dev_priv->chipset < 0x43) rsvd = 0x4f00 * vs;
- else if (nv44_graph_class(dev)) rsvd = 0x4980 * vs;
- else rsvd = 0x4a40 * vs;
- rsvd += 16 * 1024;
- rsvd *= dev_priv->engine.fifo.channels;
-
- /* pciegart table */
- if (drm_pci_device_is_pcie(dev))
- rsvd += 512 * 1024;
-
- /* object storage */
- rsvd += 512 * 1024;
-
- dev_priv->ramin_rsvd_vram = round_up(rsvd, 4096);
- } else {
- dev_priv->ramin_rsvd_vram = 512 * 1024;
- }
-
- ret = dev_priv->engine.vram.init(dev);
- if (ret)
- return ret;
-
NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20));
if (dev_priv->vram_sys_base) {
NV_INFO(dev, "Stolen system memory at: 0x%010llx\n",
@@ -479,7 +445,7 @@ nouveau_mem_vram_init(struct drm_device *dev)
}
if (dev_priv->card_type < NV_50) {
- ret = nouveau_bo_new(dev, NULL, 256*1024, 0, TTM_PL_FLAG_VRAM,
+ ret = nouveau_bo_new(dev, 256*1024, 0, TTM_PL_FLAG_VRAM,
0, 0, &dev_priv->vga_ram);
if (ret == 0)
ret = nouveau_bo_pin(dev_priv->vga_ram,
@@ -536,35 +502,146 @@ nouveau_mem_gart_init(struct drm_device *dev)
return 0;
}
+/* XXX: For now a dummy. More samples required, possibly even a card
+ * Called from nouveau_perf.c */
+void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
+ struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
+ struct nouveau_pm_memtiming *timing) {
+
+ NV_DEBUG(dev,"Timing entry format unknown, please contact nouveau developers");
+}
+
+void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
+ struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
+ struct nouveau_pm_memtiming *timing) {
+
+ timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP);
+
+ /* XXX: I don't trust the -1's and +1's... they must come
+ * from somewhere! */
+ timing->reg_1 = (e->tWR + 2 + magic_number) << 24 |
+ 1 << 16 |
+ (e->tUNK_1 + 2 + magic_number) << 8 |
+ (e->tCL + 2 - magic_number);
+ timing->reg_2 = (magic_number << 24 | e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10);
+ timing->reg_2 |= 0x20200000;
+
+ NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", timing->id,
+ timing->reg_0, timing->reg_1,timing->reg_2);
+}
+
+void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct nouveau_pm_tbl_header *hdr,
+ struct nouveau_pm_tbl_entry *e, uint8_t magic_number,struct nouveau_pm_memtiming *timing) {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ uint8_t unk18 = 1,
+ unk19 = 1,
+ unk20 = 0,
+ unk21 = 0;
+
+ switch (min(hdr->entry_len, (u8) 22)) {
+ case 22:
+ unk21 = e->tUNK_21;
+ case 21:
+ unk20 = e->tUNK_20;
+ case 20:
+ unk19 = e->tUNK_19;
+ case 19:
+ unk18 = e->tUNK_18;
+ break;
+ }
+
+ timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP);
+
+ /* XXX: I don't trust the -1's and +1's... they must come
+ * from somewhere! */
+ timing->reg_1 = (e->tWR + unk19 + 1 + magic_number) << 24 |
+ max(unk18, (u8) 1) << 16 |
+ (e->tUNK_1 + unk19 + 1 + magic_number) << 8;
+ if (dev_priv->chipset == 0xa8) {
+ timing->reg_1 |= (e->tCL - 1);
+ } else {
+ timing->reg_1 |= (e->tCL + 2 - magic_number);
+ }
+ timing->reg_2 = (e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10);
+
+ timing->reg_5 = (e->tRAS << 24 | e->tRC);
+ timing->reg_5 += max(e->tUNK_10, e->tUNK_11) << 16;
+
+ if (P->version == 1) {
+ timing->reg_2 |= magic_number << 24;
+ timing->reg_3 = (0x14 + e->tCL) << 24 |
+ 0x16 << 16 |
+ (e->tCL - 1) << 8 |
+ (e->tCL - 1);
+ timing->reg_4 = (nv_rd32(dev,0x10022c) & 0xffff0000) | e->tUNK_13 << 8 | e->tUNK_13;
+ timing->reg_5 |= (e->tCL + 2) << 8;
+ timing->reg_7 = 0x4000202 | (e->tCL - 1) << 16;
+ } else {
+ timing->reg_2 |= (unk19 - 1) << 24;
+ /* XXX: reg_10022c for recentish cards pretty much unknown*/
+ timing->reg_3 = e->tCL - 1;
+ timing->reg_4 = (unk20 << 24 | unk21 << 16 |
+ e->tUNK_13 << 8 | e->tUNK_13);
+ /* XXX: +6? */
+ timing->reg_5 |= (unk19 + 6) << 8;
+
+ /* XXX: reg_10023c currently unknown
+ * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
+ timing->reg_7 = 0x202;
+ }
+
+ NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", timing->id,
+ timing->reg_0, timing->reg_1,
+ timing->reg_2, timing->reg_3);
+ NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n",
+ timing->reg_4, timing->reg_5,
+ timing->reg_6, timing->reg_7);
+ NV_DEBUG(dev, " 240: %08x\n", timing->reg_8);
+}
+
+void nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
+ struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) {
+ timing->reg_0 = (e->tRC << 24 | (e->tRFC & 0x7f) << 17 | e->tRAS << 8 | e->tRP);
+ timing->reg_1 = (nv_rd32(dev,0x10f294) & 0xff000000) | (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | (e->tCL & 0x0f);
+ timing->reg_2 = (nv_rd32(dev,0x10f298) & 0xff0000ff) | e->tWR << 16 | e->tUNK_1 << 8;
+ timing->reg_3 = e->tUNK_20 << 9 | e->tUNK_13;
+ timing->reg_4 = (nv_rd32(dev,0x10f2a0) & 0xfff000ff) | e->tUNK_12 << 15;
+ NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", timing->id,
+ timing->reg_0, timing->reg_1,
+ timing->reg_2, timing->reg_3);
+ NV_DEBUG(dev, " 2a0: %08x %08x %08x %08x\n",
+ timing->reg_4, timing->reg_5,
+ timing->reg_6, timing->reg_7);
+}
+
+/**
+ * Processes the Memory Timing BIOS table, stores generated
+ * register values
+ * @pre init scripts were run, memtiming regs are initialized
+ */
void
nouveau_mem_timing_init(struct drm_device *dev)
{
- /* cards < NVC0 only */
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct nouveau_pm_memtimings *memtimings = &pm->memtimings;
struct nvbios *bios = &dev_priv->vbios;
struct bit_entry P;
- u8 tUNK_0, tUNK_1, tUNK_2;
- u8 tRP; /* Byte 3 */
- u8 tRAS; /* Byte 5 */
- u8 tRFC; /* Byte 7 */
- u8 tRC; /* Byte 9 */
- u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
- u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
- u8 magic_number = 0; /* Yeah... sorry*/
- u8 *mem = NULL, *entry;
- int i, recordlen, entries;
+ struct nouveau_pm_tbl_header *hdr = NULL;
+ uint8_t magic_number;
+ u8 *entry;
+ int i;
if (bios->type == NVBIOS_BIT) {
if (bit_table(dev, 'P', &P))
return;
if (P.version == 1)
- mem = ROMPTR(bios, P.data[4]);
+ hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[4]);
else
if (P.version == 2)
- mem = ROMPTR(bios, P.data[8]);
+ hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[8]);
else {
NV_WARN(dev, "unknown mem for BIT P %d\n", P.version);
}
@@ -573,150 +650,56 @@ nouveau_mem_timing_init(struct drm_device *dev)
return;
}
- if (!mem) {
+ if (!hdr) {
NV_DEBUG(dev, "memory timing table pointer invalid\n");
return;
}
- if (mem[0] != 0x10) {
- NV_WARN(dev, "memory timing table 0x%02x unknown\n", mem[0]);
+ if (hdr->version != 0x10) {
+ NV_WARN(dev, "memory timing table 0x%02x unknown\n", hdr->version);
return;
}
/* validate record length */
- entries = mem[2];
- recordlen = mem[3];
- if (recordlen < 15) {
- NV_ERROR(dev, "mem timing table length unknown: %d\n", mem[3]);
+ if (hdr->entry_len < 15) {
+ NV_ERROR(dev, "mem timing table length unknown: %d\n", hdr->entry_len);
return;
}
/* parse vbios entries into common format */
memtimings->timing =
- kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL);
+ kcalloc(hdr->entry_cnt, sizeof(*memtimings->timing), GFP_KERNEL);
if (!memtimings->timing)
return;
/* Get "some number" from the timing reg for NV_40 and NV_50
- * Used in calculations later */
- if (dev_priv->card_type >= NV_40 && dev_priv->chipset < 0x98) {
+ * Used in calculations later... source unknown */
+ magic_number = 0;
+ if (P.version == 1) {
magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24;
}
- entry = mem + mem[1];
- for (i = 0; i < entries; i++, entry += recordlen) {
+ entry = (u8*) hdr + hdr->header_len;
+ for (i = 0; i < hdr->entry_cnt; i++, entry += hdr->entry_len) {
struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i];
if (entry[0] == 0)
continue;
- tUNK_18 = 1;
- tUNK_19 = 1;
- tUNK_20 = 0;
- tUNK_21 = 0;
- switch (min(recordlen, 22)) {
- case 22:
- tUNK_21 = entry[21];
- case 21:
- tUNK_20 = entry[20];
- case 20:
- tUNK_19 = entry[19];
- case 19:
- tUNK_18 = entry[18];
- default:
- tUNK_0 = entry[0];
- tUNK_1 = entry[1];
- tUNK_2 = entry[2];
- tRP = entry[3];
- tRAS = entry[5];
- tRFC = entry[7];
- tRC = entry[9];
- tUNK_10 = entry[10];
- tUNK_11 = entry[11];
- tUNK_12 = entry[12];
- tUNK_13 = entry[13];
- tUNK_14 = entry[14];
- break;
- }
-
- timing->reg_100220 = (tRC << 24 | tRFC << 16 | tRAS << 8 | tRP);
-
- /* XXX: I don't trust the -1's and +1's... they must come
- * from somewhere! */
- timing->reg_100224 = (tUNK_0 + tUNK_19 + 1 + magic_number) << 24 |
- max(tUNK_18, (u8) 1) << 16 |
- (tUNK_1 + tUNK_19 + 1 + magic_number) << 8;
- if (dev_priv->chipset == 0xa8) {
- timing->reg_100224 |= (tUNK_2 - 1);
- } else {
- timing->reg_100224 |= (tUNK_2 + 2 - magic_number);
- }
-
- timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10);
- if (dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa)
- timing->reg_100228 |= (tUNK_19 - 1) << 24;
- else
- timing->reg_100228 |= magic_number << 24;
-
- if (dev_priv->card_type == NV_40) {
- /* NV40: don't know what the rest of the regs are..
- * And don't need to know either */
- timing->reg_100228 |= 0x20200000;
- } else if (dev_priv->card_type >= NV_50) {
- if (dev_priv->chipset < 0x98 ||
- (dev_priv->chipset == 0x98 &&
- dev_priv->stepping <= 0xa1)) {
- timing->reg_10022c = (0x14 + tUNK_2) << 24 |
- 0x16 << 16 |
- (tUNK_2 - 1) << 8 |
- (tUNK_2 - 1);
- } else {
- /* XXX: reg_10022c for recentish cards */
- timing->reg_10022c = tUNK_2 - 1;
- }
-
- timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
- tUNK_13 << 8 | tUNK_13);
-
- timing->reg_100234 = (tRAS << 24 | tRC);
- timing->reg_100234 += max(tUNK_10, tUNK_11) << 16;
-
- if (dev_priv->chipset < 0x98 ||
- (dev_priv->chipset == 0x98 &&
- dev_priv->stepping <= 0xa1)) {
- timing->reg_100234 |= (tUNK_2 + 2) << 8;
- } else {
- /* XXX: +6? */
- timing->reg_100234 |= (tUNK_19 + 6) << 8;
- }
-
- /* XXX; reg_100238
- * reg_100238: 0x00?????? */
- timing->reg_10023c = 0x202;
- if (dev_priv->chipset < 0x98 ||
- (dev_priv->chipset == 0x98 &&
- dev_priv->stepping <= 0xa1)) {
- timing->reg_10023c |= 0x4000000 | (tUNK_2 - 1) << 16;
- } else {
- /* XXX: reg_10023c
- * currently unknown
- * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
- }
-
- /* XXX: reg_100240? */
- }
timing->id = i;
-
- NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i,
- timing->reg_100220, timing->reg_100224,
- timing->reg_100228, timing->reg_10022c);
- NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n",
- timing->reg_100230, timing->reg_100234,
- timing->reg_100238, timing->reg_10023c);
- NV_DEBUG(dev, " 240: %08x\n", timing->reg_100240);
+ timing->WR = entry[0];
+ timing->CL = entry[2];
+
+ if(dev_priv->card_type <= NV_40) {
+ nv40_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]);
+ } else if(dev_priv->card_type == NV_50){
+ nv50_mem_timing_entry(dev,&P,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]);
+ } else if(dev_priv->card_type == NV_C0) {
+ nvc0_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,&pm->memtimings.timing[i]);
+ }
}
- memtimings->nr_timing = entries;
- memtimings->supported = (dev_priv->chipset <= 0x98);
+ memtimings->nr_timing = hdr->entry_cnt;
+ memtimings->supported = P.version == 1;
}
void
@@ -725,41 +708,38 @@ nouveau_mem_timing_fini(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_memtimings *mem = &dev_priv->engine.pm.memtimings;
- kfree(mem->timing);
+ if(mem->timing) {
+ kfree(mem->timing);
+ mem->timing = NULL;
+ }
}
static int
-nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long p_size)
+nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long psize)
{
- struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev);
- struct nouveau_mm *mm;
- u64 size, block, rsvd;
- int ret;
-
- rsvd = (256 * 1024); /* vga memory */
- size = (p_size << PAGE_SHIFT) - rsvd;
- block = dev_priv->vram_rblock_size;
-
- ret = nouveau_mm_init(&mm, rsvd >> 12, size >> 12, block >> 12);
- if (ret)
- return ret;
-
- man->priv = mm;
+ /* nothing to do */
return 0;
}
static int
nouveau_vram_manager_fini(struct ttm_mem_type_manager *man)
{
- struct nouveau_mm *mm = man->priv;
- int ret;
+ /* nothing to do */
+ return 0;
+}
- ret = nouveau_mm_fini(&mm);
- if (ret)
- return ret;
+static inline void
+nouveau_mem_node_cleanup(struct nouveau_mem *node)
+{
+ if (node->vma[0].node) {
+ nouveau_vm_unmap(&node->vma[0]);
+ nouveau_vm_put(&node->vma[0]);
+ }
- man->priv = NULL;
- return 0;
+ if (node->vma[1].node) {
+ nouveau_vm_unmap(&node->vma[1]);
+ nouveau_vm_put(&node->vma[1]);
+ }
}
static void
@@ -768,14 +748,9 @@ nouveau_vram_manager_del(struct ttm_mem_type_manager *man,
{
struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev);
struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
- struct nouveau_mem *node = mem->mm_node;
struct drm_device *dev = dev_priv->dev;
- if (node->tmp_vma.node) {
- nouveau_vm_unmap(&node->tmp_vma);
- nouveau_vm_put(&node->tmp_vma);
- }
-
+ nouveau_mem_node_cleanup(mem->mm_node);
vram->put(dev, (struct nouveau_mem **)&mem->mm_node);
}
@@ -794,7 +769,7 @@ nouveau_vram_manager_new(struct ttm_mem_type_manager *man,
int ret;
if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG)
- size_nc = 1 << nvbo->vma.node->type;
+ size_nc = 1 << nvbo->page_shift;
ret = vram->get(dev, mem->num_pages << PAGE_SHIFT,
mem->page_alignment << PAGE_SHIFT, size_nc,
@@ -804,9 +779,7 @@ nouveau_vram_manager_new(struct ttm_mem_type_manager *man,
return (ret == -ENOSPC) ? 0 : ret;
}
- node->page_shift = 12;
- if (nvbo->vma.node)
- node->page_shift = nvbo->vma.node->type;
+ node->page_shift = nvbo->page_shift;
mem->mm_node = node;
mem->start = node->offset >> PAGE_SHIFT;
@@ -862,15 +835,9 @@ static void
nouveau_gart_manager_del(struct ttm_mem_type_manager *man,
struct ttm_mem_reg *mem)
{
- struct nouveau_mem *node = mem->mm_node;
-
- if (node->tmp_vma.node) {
- nouveau_vm_unmap(&node->tmp_vma);
- nouveau_vm_put(&node->tmp_vma);
- }
-
+ nouveau_mem_node_cleanup(mem->mm_node);
+ kfree(mem->mm_node);
mem->mm_node = NULL;
- kfree(node);
}
static int
@@ -880,11 +847,7 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man,
struct ttm_mem_reg *mem)
{
struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
- struct nouveau_bo *nvbo = nouveau_bo(bo);
- struct nouveau_vma *vma = &nvbo->vma;
- struct nouveau_vm *vm = vma->vm;
struct nouveau_mem *node;
- int ret;
if (unlikely((mem->num_pages << PAGE_SHIFT) >=
dev_priv->gart_info.aper_size))
@@ -893,24 +856,8 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man,
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return -ENOMEM;
+ node->page_shift = 12;
- /* This node must be for evicting large-paged VRAM
- * to system memory. Due to a nv50 limitation of
- * not being able to mix large/small pages within
- * the same PDE, we need to create a temporary
- * small-paged VMA for the eviction.
- */
- if (vma->node->type != vm->spg_shift) {
- ret = nouveau_vm_get(vm, (u64)vma->node->length << 12,
- vm->spg_shift, NV_MEM_ACCESS_RW,
- &node->tmp_vma);
- if (ret) {
- kfree(node);
- return ret;
- }
- }
-
- node->page_shift = nvbo->vma.node->type;
mem->mm_node = node;
mem->start = 0;
return 0;
diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.c b/drivers/gpu/drm/nouveau/nouveau_mm.c
index 7609756b6faf..b29ffb3d1408 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mm.c
@@ -27,7 +27,7 @@
#include "nouveau_mm.h"
static inline void
-region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a)
+region_put(struct nouveau_mm *mm, struct nouveau_mm_node *a)
{
list_del(&a->nl_entry);
list_del(&a->fl_entry);
@@ -35,7 +35,7 @@ region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a)
}
static struct nouveau_mm_node *
-region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size)
+region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
{
struct nouveau_mm_node *b;
@@ -57,33 +57,33 @@ region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size)
return b;
}
-#define node(root, dir) ((root)->nl_entry.dir == &rmm->nodes) ? NULL : \
+#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
void
-nouveau_mm_put(struct nouveau_mm *rmm, struct nouveau_mm_node *this)
+nouveau_mm_put(struct nouveau_mm *mm, struct nouveau_mm_node *this)
{
struct nouveau_mm_node *prev = node(this, prev);
struct nouveau_mm_node *next = node(this, next);
- list_add(&this->fl_entry, &rmm->free);
+ list_add(&this->fl_entry, &mm->free);
this->type = 0;
if (prev && prev->type == 0) {
prev->length += this->length;
- region_put(rmm, this);
+ region_put(mm, this);
this = prev;
}
if (next && next->type == 0) {
next->offset = this->offset;
next->length += this->length;
- region_put(rmm, this);
+ region_put(mm, this);
}
}
int
-nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
+nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc,
u32 align, struct nouveau_mm_node **pnode)
{
struct nouveau_mm_node *prev, *this, *next;
@@ -92,17 +92,17 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
u32 splitoff;
u32 s, e;
- list_for_each_entry(this, &rmm->free, fl_entry) {
+ list_for_each_entry(this, &mm->free, fl_entry) {
e = this->offset + this->length;
s = this->offset;
prev = node(this, prev);
if (prev && prev->type != type)
- s = roundup(s, rmm->block_size);
+ s = roundup(s, mm->block_size);
next = node(this, next);
if (next && next->type != type)
- e = rounddown(e, rmm->block_size);
+ e = rounddown(e, mm->block_size);
s = (s + align_mask) & ~align_mask;
e &= ~align_mask;
@@ -110,10 +110,10 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
continue;
splitoff = s - this->offset;
- if (splitoff && !region_split(rmm, this, splitoff))
+ if (splitoff && !region_split(mm, this, splitoff))
return -ENOMEM;
- this = region_split(rmm, this, min(size, e - s));
+ this = region_split(mm, this, min(size, e - s));
if (!this)
return -ENOMEM;
@@ -127,45 +127,49 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
}
int
-nouveau_mm_init(struct nouveau_mm **prmm, u32 offset, u32 length, u32 block)
+nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
{
- struct nouveau_mm *rmm;
- struct nouveau_mm_node *heap;
+ struct nouveau_mm_node *node;
+
+ if (block) {
+ mutex_init(&mm->mutex);
+ INIT_LIST_HEAD(&mm->nodes);
+ INIT_LIST_HEAD(&mm->free);
+ mm->block_size = block;
+ mm->heap_nodes = 0;
+ }
- heap = kzalloc(sizeof(*heap), GFP_KERNEL);
- if (!heap)
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
return -ENOMEM;
- heap->offset = roundup(offset, block);
- heap->length = rounddown(offset + length, block) - heap->offset;
+ node->offset = roundup(offset, mm->block_size);
+ node->length = rounddown(offset + length, mm->block_size) - node->offset;
- rmm = kzalloc(sizeof(*rmm), GFP_KERNEL);
- if (!rmm) {
- kfree(heap);
- return -ENOMEM;
- }
- rmm->block_size = block;
- mutex_init(&rmm->mutex);
- INIT_LIST_HEAD(&rmm->nodes);
- INIT_LIST_HEAD(&rmm->free);
- list_add(&heap->nl_entry, &rmm->nodes);
- list_add(&heap->fl_entry, &rmm->free);
-
- *prmm = rmm;
+ list_add_tail(&node->nl_entry, &mm->nodes);
+ list_add_tail(&node->fl_entry, &mm->free);
+ mm->heap_nodes++;
return 0;
}
int
-nouveau_mm_fini(struct nouveau_mm **prmm)
+nouveau_mm_fini(struct nouveau_mm *mm)
{
- struct nouveau_mm *rmm = *prmm;
- struct nouveau_mm_node *heap =
- list_first_entry(&rmm->nodes, struct nouveau_mm_node, nl_entry);
-
- if (!list_is_singular(&rmm->nodes))
- return -EBUSY;
+ struct nouveau_mm_node *node, *heap =
+ list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry);
+ int nodes = 0;
+
+ list_for_each_entry(node, &mm->nodes, nl_entry) {
+ if (nodes++ == mm->heap_nodes) {
+ printk(KERN_ERR "nouveau_mm in use at destroy time!\n");
+ list_for_each_entry(node, &mm->nodes, nl_entry) {
+ printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n",
+ node->type, node->offset, node->length);
+ }
+ WARN_ON(1);
+ return -EBUSY;
+ }
+ }
kfree(heap);
- kfree(rmm);
- *prmm = NULL;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.h b/drivers/gpu/drm/nouveau/nouveau_mm.h
index 1f7483aae9a4..57a600c35c95 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_mm.h
@@ -42,16 +42,18 @@ struct nouveau_mm {
struct mutex mutex;
u32 block_size;
+ int heap_nodes;
};
-int nouveau_mm_init(struct nouveau_mm **, u32 offset, u32 length, u32 block);
-int nouveau_mm_fini(struct nouveau_mm **);
+int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block);
+int nouveau_mm_fini(struct nouveau_mm *);
int nouveau_mm_pre(struct nouveau_mm *);
int nouveau_mm_get(struct nouveau_mm *, int type, u32 size, u32 size_nc,
u32 align, struct nouveau_mm_node **);
void nouveau_mm_put(struct nouveau_mm *, struct nouveau_mm_node *);
int nv50_vram_init(struct drm_device *);
+void nv50_vram_fini(struct drm_device *);
int nv50_vram_new(struct drm_device *, u64 size, u32 align, u32 size_nc,
u32 memtype, struct nouveau_mem **);
void nv50_vram_del(struct drm_device *, struct nouveau_mem **);
diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c
index 5b39718ae1f8..6abdbe6530a7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_notifier.c
+++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c
@@ -34,6 +34,7 @@ int
nouveau_notifier_init_channel(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_bo *ntfy = NULL;
uint32_t flags, ttmpl;
int ret;
@@ -46,7 +47,7 @@ nouveau_notifier_init_channel(struct nouveau_channel *chan)
ttmpl = TTM_PL_FLAG_TT;
}
- ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, flags, 0, 0, &ntfy);
+ ret = nouveau_gem_new(dev, PAGE_SIZE, 0, flags, 0, 0, &ntfy);
if (ret)
return ret;
@@ -58,14 +59,22 @@ nouveau_notifier_init_channel(struct nouveau_channel *chan)
if (ret)
goto out_err;
+ if (dev_priv->card_type >= NV_50) {
+ ret = nouveau_bo_vma_add(ntfy, chan->vm, &chan->notifier_vma);
+ if (ret)
+ goto out_err;
+ }
+
ret = drm_mm_init(&chan->notifier_heap, 0, ntfy->bo.mem.size);
if (ret)
goto out_err;
chan->notifier_bo = ntfy;
out_err:
- if (ret)
+ if (ret) {
+ nouveau_bo_vma_del(ntfy, &chan->notifier_vma);
drm_gem_object_unreference_unlocked(ntfy->gem);
+ }
return ret;
}
@@ -78,6 +87,7 @@ nouveau_notifier_takedown_channel(struct nouveau_channel *chan)
if (!chan->notifier_bo)
return;
+ nouveau_bo_vma_del(chan->notifier_bo, &chan->notifier_vma);
nouveau_bo_unmap(chan->notifier_bo);
mutex_lock(&dev->struct_mutex);
nouveau_bo_unpin(chan->notifier_bo);
@@ -122,10 +132,10 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
target = NV_MEM_TARGET_VRAM;
else
target = NV_MEM_TARGET_GART;
- offset = chan->notifier_bo->bo.mem.start << PAGE_SHIFT;
+ offset = chan->notifier_bo->bo.offset;
} else {
target = NV_MEM_TARGET_VM;
- offset = chan->notifier_bo->vma.offset;
+ offset = chan->notifier_vma.offset;
}
offset += mem->start;
@@ -183,7 +193,7 @@ nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data,
if (unlikely(dev_priv->card_type >= NV_C0))
return -EINVAL;
- chan = nouveau_channel_get(dev, file_priv, na->channel);
+ chan = nouveau_channel_get(file_priv, na->channel);
if (IS_ERR(chan))
return PTR_ERR(chan);
diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c
index 8f97016f5b26..02222c540aee 100644
--- a/drivers/gpu/drm/nouveau/nouveau_object.c
+++ b/drivers/gpu/drm/nouveau/nouveau_object.c
@@ -125,7 +125,7 @@ nouveau_gpuobj_mthd_call2(struct drm_device *dev, int chid,
int ret = -EINVAL;
spin_lock_irqsave(&dev_priv->channels.lock, flags);
- if (chid > 0 && chid < dev_priv->engine.fifo.channels)
+ if (chid >= 0 && chid < dev_priv->engine.fifo.channels)
chan = dev_priv->channels.ptr[chid];
if (chan)
ret = nouveau_gpuobj_mthd_call(chan, class, mthd, data);
@@ -191,7 +191,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
spin_unlock(&dev_priv->ramin_lock);
- if (chan) {
+ if (!(flags & NVOBJ_FLAG_VM) && chan) {
ramin = drm_mm_search_free(&chan->ramin_heap, size, align, 0);
if (ramin)
ramin = drm_mm_get_block(ramin, size, align);
@@ -208,7 +208,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
gpuobj->vinst = ramin->start + chan->ramin->vinst;
gpuobj->node = ramin;
} else {
- ret = instmem->get(gpuobj, size, align);
+ ret = instmem->get(gpuobj, chan, size, align);
if (ret) {
nouveau_gpuobj_ref(NULL, &gpuobj);
return ret;
@@ -690,36 +690,69 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
return 0;
}
-int
-nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
- uint32_t vram_h, uint32_t tt_h)
+static int
+nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm)
{
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct drm_device *dev = chan->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj *vram = NULL, *tt = NULL;
+ struct nouveau_gpuobj *pgd = NULL;
+ struct nouveau_vm_pgd *vpgd;
int ret, i;
- NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h);
-
- if (dev_priv->card_type == NV_C0) {
- struct nouveau_vm *vm = dev_priv->chan_vm;
- struct nouveau_vm_pgd *vpgd;
+ ret = nouveau_gpuobj_new(dev, NULL, 4096, 0x1000, 0, &chan->ramin);
+ if (ret)
+ return ret;
- ret = nouveau_gpuobj_new(dev, NULL, 4096, 0x1000, 0,
- &chan->ramin);
+ /* create page directory for this vm if none currently exists,
+ * will be destroyed automagically when last reference to the
+ * vm is removed
+ */
+ if (list_empty(&vm->pgd_list)) {
+ ret = nouveau_gpuobj_new(dev, NULL, 65536, 0x1000, 0, &pgd);
if (ret)
return ret;
+ }
+ nouveau_vm_ref(vm, &chan->vm, pgd);
+ nouveau_gpuobj_ref(NULL, &pgd);
+
+ /* point channel at vm's page directory */
+ vpgd = list_first_entry(&vm->pgd_list, struct nouveau_vm_pgd, head);
+ nv_wo32(chan->ramin, 0x0200, lower_32_bits(vpgd->obj->vinst));
+ nv_wo32(chan->ramin, 0x0204, upper_32_bits(vpgd->obj->vinst));
+ nv_wo32(chan->ramin, 0x0208, 0xffffffff);
+ nv_wo32(chan->ramin, 0x020c, 0x000000ff);
+
+ /* map display semaphore buffers into channel's vm */
+ if (dev_priv->card_type >= NV_D0)
+ return 0;
- nouveau_vm_ref(vm, &chan->vm, NULL);
+ for (i = 0; i < 2; i++) {
+ struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i];
- vpgd = list_first_entry(&vm->pgd_list, struct nouveau_vm_pgd, head);
- nv_wo32(chan->ramin, 0x0200, lower_32_bits(vpgd->obj->vinst));
- nv_wo32(chan->ramin, 0x0204, upper_32_bits(vpgd->obj->vinst));
- nv_wo32(chan->ramin, 0x0208, 0xffffffff);
- nv_wo32(chan->ramin, 0x020c, 0x000000ff);
- return 0;
+ ret = nouveau_bo_vma_add(dispc->sem.bo, chan->vm,
+ &chan->dispc_vma[i]);
+ if (ret)
+ return ret;
}
+ return 0;
+}
+
+int
+nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
+ uint32_t vram_h, uint32_t tt_h)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_fpriv *fpriv = nouveau_fpriv(chan->file_priv);
+ struct nouveau_vm *vm = fpriv ? fpriv->vm : dev_priv->chan_vm;
+ struct nouveau_gpuobj *vram = NULL, *tt = NULL;
+ int ret, i;
+
+ NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h);
+ if (dev_priv->card_type >= NV_C0)
+ return nvc0_gpuobj_channel_init(chan, vm);
+
/* Allocate a chunk of memory for per-channel object storage */
ret = nouveau_gpuobj_channel_init_pramin(chan);
if (ret) {
@@ -731,7 +764,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
* - Allocate per-channel page-directory
* - Link with shared channel VM
*/
- if (dev_priv->chan_vm) {
+ if (vm) {
u32 pgd_offs = (dev_priv->chipset == 0x50) ? 0x1400 : 0x0200;
u64 vm_vinst = chan->ramin->vinst + pgd_offs;
u32 vm_pinst = chan->ramin->pinst;
@@ -744,7 +777,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
if (ret)
return ret;
- nouveau_vm_ref(dev_priv->chan_vm, &chan->vm, chan->vm_pd);
+ nouveau_vm_ref(vm, &chan->vm, chan->vm_pd);
}
/* RAMHT */
@@ -764,11 +797,11 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
return ret;
/* dma objects for display sync channel semaphore blocks */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
struct nouveau_gpuobj *sem = NULL;
struct nv50_display_crtc *dispc =
&nv50_display(dev)->crtc[i];
- u64 offset = dispc->sem.bo->bo.mem.start << PAGE_SHIFT;
+ u64 offset = dispc->sem.bo->bo.offset;
ret = nouveau_gpuobj_dma_new(chan, 0x3d, offset, 0xfff,
NV_MEM_ACCESS_RW,
@@ -841,10 +874,19 @@ void
nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int i;
NV_DEBUG(dev, "ch%d\n", chan->id);
- nouveau_ramht_ref(NULL, &chan->ramht, chan);
+ if (dev_priv->card_type >= NV_50 && dev_priv->card_type <= NV_C0) {
+ struct nv50_display *disp = nv50_display(dev);
+
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ struct nv50_display_crtc *dispc = &disp->crtc[i];
+ nouveau_bo_vma_del(dispc->sem.bo, &chan->dispc_vma[i]);
+ }
+ }
nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd);
nouveau_gpuobj_ref(NULL, &chan->vm_pd);
@@ -909,7 +951,7 @@ int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data,
if (init->handle == ~0)
return -EINVAL;
- chan = nouveau_channel_get(dev, file_priv, init->channel);
+ chan = nouveau_channel_get(file_priv, init->channel);
if (IS_ERR(chan))
return PTR_ERR(chan);
@@ -936,7 +978,7 @@ int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data,
struct nouveau_channel *chan;
int ret;
- chan = nouveau_channel_get(dev, file_priv, objfree->channel);
+ chan = nouveau_channel_get(file_priv, objfree->channel);
if (IS_ERR(chan))
return PTR_ERR(chan);
diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c
index ef9dec0e6f8b..33d03fbf00df 100644
--- a/drivers/gpu/drm/nouveau/nouveau_perf.c
+++ b/drivers/gpu/drm/nouveau/nouveau_perf.c
@@ -127,13 +127,57 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P,
entry += ramcfg * recordlen;
if (entry[1] >= pm->memtimings.nr_timing) {
- NV_WARN(dev, "timingset %d does not exist\n", entry[1]);
+ if (entry[1] != 0xff)
+ NV_WARN(dev, "timingset %d does not exist\n", entry[1]);
return NULL;
}
return &pm->memtimings.timing[entry[1]];
}
+static void
+nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P,
+ struct nouveau_pm_level *perflvl)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ u8 *vmap;
+ int id;
+
+ id = perflvl->volt_min;
+ perflvl->volt_min = 0;
+
+ /* boards using voltage table version <0x40 store the voltage
+ * level directly in the perflvl entry as a multiple of 10mV
+ */
+ if (dev_priv->engine.pm.voltage.version < 0x40) {
+ perflvl->volt_min = id * 10000;
+ perflvl->volt_max = perflvl->volt_min;
+ return;
+ }
+
+ /* on newer ones, the perflvl stores an index into yet another
+ * vbios table containing a min/max voltage value for the perflvl
+ */
+ if (P->version != 2 || P->length < 34) {
+ NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n",
+ P->version, P->length);
+ return;
+ }
+
+ vmap = ROMPTR(bios, P->data[32]);
+ if (!vmap) {
+ NV_DEBUG(dev, "volt map table pointer invalid\n");
+ return;
+ }
+
+ if (id < vmap[3]) {
+ vmap += vmap[1] + (vmap[2] * id);
+ perflvl->volt_min = ROM32(vmap[0]);
+ perflvl->volt_max = ROM32(vmap[4]);
+ }
+}
+
void
nouveau_perf_init(struct drm_device *dev)
{
@@ -141,6 +185,8 @@ nouveau_perf_init(struct drm_device *dev)
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct nvbios *bios = &dev_priv->vbios;
struct bit_entry P;
+ struct nouveau_pm_memtimings *memtimings = &pm->memtimings;
+ struct nouveau_pm_tbl_header mt_hdr;
u8 version, headerlen, recordlen, entries;
u8 *perf, *entry;
int vid, i;
@@ -188,6 +234,22 @@ nouveau_perf_init(struct drm_device *dev)
}
entry = perf + headerlen;
+
+ /* For version 0x15, initialize memtiming table */
+ if(version == 0x15) {
+ memtimings->timing =
+ kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL);
+ if (!memtimings->timing) {
+ NV_WARN(dev,"Could not allocate memtiming table\n");
+ return;
+ }
+
+ mt_hdr.entry_cnt = entries;
+ mt_hdr.entry_len = 14;
+ mt_hdr.version = version;
+ mt_hdr.header_len = 4;
+ }
+
for (i = 0; i < entries; i++) {
struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
@@ -203,7 +265,8 @@ nouveau_perf_init(struct drm_device *dev)
case 0x13:
case 0x15:
perflvl->fanspeed = entry[55];
- perflvl->voltage = (recordlen > 56) ? entry[56] : 0;
+ if (recordlen > 56)
+ perflvl->volt_min = entry[56];
perflvl->core = ROM32(entry[1]) * 10;
perflvl->memory = ROM32(entry[5]) * 20;
break;
@@ -211,9 +274,10 @@ nouveau_perf_init(struct drm_device *dev)
case 0x23:
case 0x24:
perflvl->fanspeed = entry[4];
- perflvl->voltage = entry[5];
- perflvl->core = ROM16(entry[6]) * 1000;
-
+ perflvl->volt_min = entry[5];
+ perflvl->shader = ROM16(entry[6]) * 1000;
+ perflvl->core = perflvl->shader;
+ perflvl->core += (signed char)entry[8] * 1000;
if (dev_priv->chipset == 0x49 ||
dev_priv->chipset == 0x4b)
perflvl->memory = ROM16(entry[11]) * 1000;
@@ -223,7 +287,7 @@ nouveau_perf_init(struct drm_device *dev)
break;
case 0x25:
perflvl->fanspeed = entry[4];
- perflvl->voltage = entry[5];
+ perflvl->volt_min = entry[5];
perflvl->core = ROM16(entry[6]) * 1000;
perflvl->shader = ROM16(entry[10]) * 1000;
perflvl->memory = ROM16(entry[12]) * 1000;
@@ -232,7 +296,7 @@ nouveau_perf_init(struct drm_device *dev)
perflvl->memscript = ROM16(entry[2]);
case 0x35:
perflvl->fanspeed = entry[6];
- perflvl->voltage = entry[7];
+ perflvl->volt_min = entry[7];
perflvl->core = ROM16(entry[8]) * 1000;
perflvl->shader = ROM16(entry[10]) * 1000;
perflvl->memory = ROM16(entry[12]) * 1000;
@@ -240,30 +304,34 @@ nouveau_perf_init(struct drm_device *dev)
perflvl->unk05 = ROM16(entry[16]) * 1000;
break;
case 0x40:
-#define subent(n) entry[perf[2] + ((n) * perf[3])]
+#define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000
perflvl->fanspeed = 0; /*XXX*/
- perflvl->voltage = entry[2];
+ perflvl->volt_min = entry[2];
if (dev_priv->card_type == NV_50) {
- perflvl->core = ROM16(subent(0)) & 0xfff;
- perflvl->shader = ROM16(subent(1)) & 0xfff;
- perflvl->memory = ROM16(subent(2)) & 0xfff;
+ perflvl->core = subent(0);
+ perflvl->shader = subent(1);
+ perflvl->memory = subent(2);
+ perflvl->vdec = subent(3);
+ perflvl->unka0 = subent(4);
} else {
- perflvl->shader = ROM16(subent(3)) & 0xfff;
+ perflvl->hub06 = subent(0);
+ perflvl->hub01 = subent(1);
+ perflvl->copy = subent(2);
+ perflvl->shader = subent(3);
+ perflvl->rop = subent(4);
+ perflvl->memory = subent(5);
+ perflvl->vdec = subent(6);
+ perflvl->daemon = subent(10);
+ perflvl->hub07 = subent(11);
perflvl->core = perflvl->shader / 2;
- perflvl->unk0a = ROM16(subent(4)) & 0xfff;
- perflvl->memory = ROM16(subent(5)) & 0xfff;
}
-
- perflvl->core *= 1000;
- perflvl->shader *= 1000;
- perflvl->memory *= 1000;
- perflvl->unk0a *= 1000;
break;
}
/* make sure vid is valid */
- if (pm->voltage.supported && perflvl->voltage) {
- vid = nouveau_volt_vid_lookup(dev, perflvl->voltage);
+ nouveau_perf_voltage(dev, &P, perflvl);
+ if (pm->voltage.supported && perflvl->volt_min) {
+ vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min);
if (vid < 0) {
NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i);
entry += recordlen;
@@ -272,7 +340,11 @@ nouveau_perf_init(struct drm_device *dev)
}
/* get the corresponding memory timings */
- if (version > 0x15) {
+ if (version == 0x15) {
+ memtimings->timing[i].id = i;
+ nv30_mem_timing_entry(dev,&mt_hdr,(struct nouveau_pm_tbl_entry*) &entry[41],0,&memtimings->timing[i]);
+ perflvl->timing = &memtimings->timing[i];
+ } else if (version > 0x15) {
/* last 3 args are for < 0x40, ignored for >= 0x40 */
perflvl->timing =
nouveau_perf_timing(dev, &P,
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
index da8d994d5e8a..a539fd257921 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -64,18 +64,26 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
if (perflvl == pm->cur)
return 0;
- if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) {
- ret = pm->voltage_set(dev, perflvl->voltage);
+ if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) {
+ ret = pm->voltage_set(dev, perflvl->volt_min);
if (ret) {
NV_ERROR(dev, "voltage_set %d failed: %d\n",
- perflvl->voltage, ret);
+ perflvl->volt_min, ret);
}
}
- nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
- nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
- nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
- nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
+ if (pm->clocks_pre) {
+ void *state = pm->clocks_pre(dev, perflvl);
+ if (IS_ERR(state))
+ return PTR_ERR(state);
+ pm->clocks_set(dev, state);
+ } else
+ if (pm->clock_set) {
+ nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
+ nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
+ nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
+ nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
+ }
pm->cur = perflvl;
return 0;
@@ -92,9 +100,6 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
if (nouveau_perflvl_wr != 7777)
return -EPERM;
- if (!pm->clock_set)
- return -EINVAL;
-
if (!strncmp(profile, "boot", 4))
perflvl = &pm->boot;
else {
@@ -123,31 +128,37 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
int ret;
- if (!pm->clock_get)
- return -EINVAL;
-
memset(perflvl, 0, sizeof(*perflvl));
- ret = pm->clock_get(dev, PLL_CORE);
- if (ret > 0)
- perflvl->core = ret;
+ if (pm->clocks_get) {
+ ret = pm->clocks_get(dev, perflvl);
+ if (ret)
+ return ret;
+ } else
+ if (pm->clock_get) {
+ ret = pm->clock_get(dev, PLL_CORE);
+ if (ret > 0)
+ perflvl->core = ret;
- ret = pm->clock_get(dev, PLL_MEMORY);
- if (ret > 0)
- perflvl->memory = ret;
+ ret = pm->clock_get(dev, PLL_MEMORY);
+ if (ret > 0)
+ perflvl->memory = ret;
- ret = pm->clock_get(dev, PLL_SHADER);
- if (ret > 0)
- perflvl->shader = ret;
+ ret = pm->clock_get(dev, PLL_SHADER);
+ if (ret > 0)
+ perflvl->shader = ret;
- ret = pm->clock_get(dev, PLL_UNK05);
- if (ret > 0)
- perflvl->unk05 = ret;
+ ret = pm->clock_get(dev, PLL_UNK05);
+ if (ret > 0)
+ perflvl->unk05 = ret;
+ }
if (pm->voltage.supported && pm->voltage_get) {
ret = pm->voltage_get(dev);
- if (ret > 0)
- perflvl->voltage = ret;
+ if (ret > 0) {
+ perflvl->volt_min = ret;
+ perflvl->volt_max = ret;
+ }
}
return 0;
@@ -156,7 +167,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
static void
nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
{
- char c[16], s[16], v[16], f[16], t[16];
+ char c[16], s[16], v[32], f[16], t[16], m[16];
c[0] = '\0';
if (perflvl->core)
@@ -166,9 +177,19 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
if (perflvl->shader)
snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
+ m[0] = '\0';
+ if (perflvl->memory)
+ snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
+
v[0] = '\0';
- if (perflvl->voltage)
- snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10);
+ if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
+ snprintf(v, sizeof(v), " voltage %dmV-%dmV",
+ perflvl->volt_min / 1000, perflvl->volt_max / 1000);
+ } else
+ if (perflvl->volt_min) {
+ snprintf(v, sizeof(v), " voltage %dmV",
+ perflvl->volt_min / 1000);
+ }
f[0] = '\0';
if (perflvl->fanspeed)
@@ -178,8 +199,7 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
if (perflvl->timing)
snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
- snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000,
- c, s, v, f, t);
+ snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f);
}
static ssize_t
@@ -190,7 +210,7 @@ nouveau_pm_get_perflvl_info(struct device *d,
char *ptr = buf;
int len = PAGE_SIZE;
- snprintf(ptr, len, "%d: ", perflvl->id);
+ snprintf(ptr, len, "%d:", perflvl->id);
ptr += strlen(buf);
len -= strlen(buf);
@@ -211,9 +231,9 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
if (!pm->cur)
snprintf(ptr, len, "setting: boot\n");
else if (pm->cur == &pm->boot)
- snprintf(ptr, len, "setting: boot\nc: ");
+ snprintf(ptr, len, "setting: boot\nc:");
else
- snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id);
+ snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
ptr += strlen(buf);
len -= strlen(buf);
@@ -292,7 +312,7 @@ nouveau_sysfs_fini(struct drm_device *dev)
}
}
-#ifdef CONFIG_HWMON
+#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
static ssize_t
nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
{
@@ -409,7 +429,7 @@ static const struct attribute_group hwmon_attrgroup = {
static int
nouveau_hwmon_init(struct drm_device *dev)
{
-#ifdef CONFIG_HWMON
+#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct device *hwmon_dev;
@@ -442,7 +462,7 @@ nouveau_hwmon_init(struct drm_device *dev)
static void
nouveau_hwmon_fini(struct drm_device *dev)
{
-#ifdef CONFIG_HWMON
+#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
@@ -488,7 +508,7 @@ nouveau_pm_init(struct drm_device *dev)
NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
for (i = 0; i < pm->nr_perflvl; i++) {
nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
- NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info);
+ NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info);
}
/* determine current ("boot") performance level */
@@ -498,7 +518,7 @@ nouveau_pm_init(struct drm_device *dev)
pm->cur = &pm->boot;
nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
- NV_INFO(dev, "c: %s", info);
+ NV_INFO(dev, "c:%s", info);
}
/* switch performance levels now if requested */
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h
index 4a9838ddacec..8ac02cdd03a1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.h
@@ -52,6 +52,11 @@ void *nv04_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
u32 id, int khz);
void nv04_pm_clock_set(struct drm_device *, void *);
+/* nv40_pm.c */
+int nv40_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
+void *nv40_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
+void nv40_pm_clocks_set(struct drm_device *, void *);
+
/* nv50_pm.c */
int nv50_pm_clock_get(struct drm_device *, u32 id);
void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
@@ -59,10 +64,12 @@ void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
void nv50_pm_clock_set(struct drm_device *, void *);
/* nva3_pm.c */
-int nva3_pm_clock_get(struct drm_device *, u32 id);
-void *nva3_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
- u32 id, int khz);
-void nva3_pm_clock_set(struct drm_device *, void *);
+int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
+void *nva3_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
+void nva3_pm_clocks_set(struct drm_device *, void *);
+
+/* nvc0_pm.c */
+int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
/* nouveau_temp.c */
void nouveau_temp_init(struct drm_device *dev);
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
index f18cdfc3400f..43a96b99e180 100644
--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
@@ -826,9 +826,12 @@
#define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE 0x00030000
#define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED 0x00080000
#define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT 0x10000000
-#define NV50_PDISPLAY_SOR_BACKLIGHT 0x0061c084
-#define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000
-#define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff
+#define NV50_PDISP_SOR_PWM_DIV(i) (0x0061c080 + (i) * 0x800)
+#define NV50_PDISP_SOR_PWM_CTL(i) (0x0061c084 + (i) * 0x800)
+#define NV50_PDISP_SOR_PWM_CTL_NEW 0x80000000
+#define NVA3_PDISP_SOR_PWM_CTL_UNK 0x40000000
+#define NV50_PDISP_SOR_PWM_CTL_VAL 0x000007ff
+#define NVA3_PDISP_SOR_PWM_CTL_VAL 0x00ffffff
#define NV50_SOR_DP_CTRL(i, l) (0x0061c10c + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_CTRL_ENABLED 0x00000001
#define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000
@@ -843,7 +846,7 @@
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000
#define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80)
-#define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80)
+#define NV50_SOR_DP_SCFG(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80)
#define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000)
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
index 82fad914e648..b75258a9fe44 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
@@ -12,8 +12,8 @@ struct nouveau_sgdma_be {
struct drm_device *dev;
dma_addr_t *pages;
- bool *ttm_alloced;
unsigned nr_pages;
+ bool unmap_pages;
u64 offset;
bool bound;
@@ -26,40 +26,28 @@ nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
struct drm_device *dev = nvbe->dev;
+ int i;
NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages);
- if (nvbe->pages)
- return -EINVAL;
-
- nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL);
- if (!nvbe->pages)
- return -ENOMEM;
+ nvbe->pages = dma_addrs;
+ nvbe->nr_pages = num_pages;
+ nvbe->unmap_pages = true;
- nvbe->ttm_alloced = kmalloc(sizeof(bool) * num_pages, GFP_KERNEL);
- if (!nvbe->ttm_alloced)
- return -ENOMEM;
+ /* this code path isn't called and is incorrect anyways */
+ if (0) { /* dma_addrs[0] != DMA_ERROR_CODE) { */
+ nvbe->unmap_pages = false;
+ return 0;
+ }
- nvbe->nr_pages = 0;
- while (num_pages--) {
- /* this code path isn't called and is incorrect anyways */
- if (0) { /*dma_addrs[nvbe->nr_pages] != DMA_ERROR_CODE)*/
- nvbe->pages[nvbe->nr_pages] =
- dma_addrs[nvbe->nr_pages];
- nvbe->ttm_alloced[nvbe->nr_pages] = true;
- } else {
- nvbe->pages[nvbe->nr_pages] =
- pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0,
- PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
- if (pci_dma_mapping_error(dev->pdev,
- nvbe->pages[nvbe->nr_pages])) {
- be->func->clear(be);
- return -EFAULT;
- }
- nvbe->ttm_alloced[nvbe->nr_pages] = false;
+ for (i = 0; i < num_pages; i++) {
+ nvbe->pages[i] = pci_map_page(dev->pdev, pages[i], 0,
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ if (pci_dma_mapping_error(dev->pdev, nvbe->pages[i])) {
+ nvbe->nr_pages = --i;
+ be->func->clear(be);
+ return -EFAULT;
}
-
- nvbe->nr_pages++;
}
return 0;
@@ -69,25 +57,16 @@ static void
nouveau_sgdma_clear(struct ttm_backend *be)
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
- struct drm_device *dev;
-
- if (nvbe && nvbe->pages) {
- dev = nvbe->dev;
- NV_DEBUG(dev, "\n");
+ struct drm_device *dev = nvbe->dev;
- if (nvbe->bound)
- be->func->unbind(be);
+ if (nvbe->bound)
+ be->func->unbind(be);
+ if (nvbe->unmap_pages) {
while (nvbe->nr_pages--) {
- if (!nvbe->ttm_alloced[nvbe->nr_pages])
- pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages],
+ pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages],
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
}
- kfree(nvbe->pages);
- kfree(nvbe->ttm_alloced);
- nvbe->pages = NULL;
- nvbe->ttm_alloced = NULL;
- nvbe->nr_pages = 0;
}
}
@@ -126,7 +105,7 @@ nv04_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++, pte++) {
nv_wo32(gpuobj, (pte * 4) + 0, offset_l | 3);
- dma_offset += NV_CTXDMA_PAGE_SIZE;
+ offset_l += NV_CTXDMA_PAGE_SIZE;
}
}
@@ -429,7 +408,7 @@ nouveau_sgdma_init(struct drm_device *dev)
u32 aper_size, align;
int ret;
- if (dev_priv->card_type >= NV_40 && drm_pci_device_is_pcie(dev))
+ if (dev_priv->card_type >= NV_40 && pci_is_pcie(dev->pdev))
aper_size = 512 * 1024 * 1024;
else
aper_size = 64 * 1024 * 1024;
@@ -458,7 +437,7 @@ nouveau_sgdma_init(struct drm_device *dev)
dev_priv->gart_info.type = NOUVEAU_GART_HW;
dev_priv->gart_info.func = &nv50_sgdma_backend;
} else
- if (0 && drm_pci_device_is_pcie(dev) &&
+ if (0 && pci_is_pcie(dev->pdev) &&
dev_priv->chipset > 0x40 && dev_priv->chipset != 0x45) {
if (nv44_graph_class(dev)) {
dev_priv->gart_info.func = &nv44_sgdma_backend;
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 731acea865b5..d8831ab42bb9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -91,6 +91,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clock_pre = nv04_pm_clock_pre;
engine->pm.clock_set = nv04_pm_clock_set;
engine->vram.init = nouveau_mem_detect;
+ engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
case 0x10:
@@ -139,6 +140,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clock_pre = nv04_pm_clock_pre;
engine->pm.clock_set = nv04_pm_clock_set;
engine->vram.init = nouveau_mem_detect;
+ engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
case 0x20:
@@ -187,6 +189,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clock_pre = nv04_pm_clock_pre;
engine->pm.clock_set = nv04_pm_clock_set;
engine->vram.init = nouveau_mem_detect;
+ engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
case 0x30:
@@ -237,6 +240,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
engine->vram.init = nouveau_mem_detect;
+ engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
case 0x40:
@@ -282,19 +286,20 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.get = nv10_gpio_get;
engine->gpio.set = nv10_gpio_set;
engine->gpio.irq_enable = NULL;
- engine->pm.clock_get = nv04_pm_clock_get;
- engine->pm.clock_pre = nv04_pm_clock_pre;
- engine->pm.clock_set = nv04_pm_clock_set;
+ engine->pm.clocks_get = nv40_pm_clocks_get;
+ engine->pm.clocks_pre = nv40_pm_clocks_pre;
+ engine->pm.clocks_set = nv40_pm_clocks_set;
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
engine->pm.temp_get = nv40_temp_get;
engine->vram.init = nouveau_mem_detect;
+ engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
case 0x50:
case 0x80: /* gotta love NVIDIA's consistency.. */
case 0x90:
- case 0xA0:
+ case 0xa0:
engine->instmem.init = nv50_instmem_init;
engine->instmem.takedown = nv50_instmem_takedown;
engine->instmem.suspend = nv50_instmem_suspend;
@@ -354,9 +359,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clock_set = nv50_pm_clock_set;
break;
default:
- engine->pm.clock_get = nva3_pm_clock_get;
- engine->pm.clock_pre = nva3_pm_clock_pre;
- engine->pm.clock_set = nva3_pm_clock_set;
+ engine->pm.clocks_get = nva3_pm_clocks_get;
+ engine->pm.clocks_pre = nva3_pm_clocks_pre;
+ engine->pm.clocks_set = nva3_pm_clocks_set;
break;
}
engine->pm.voltage_get = nouveau_voltage_gpio_get;
@@ -366,11 +371,12 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
else
engine->pm.temp_get = nv40_temp_get;
engine->vram.init = nv50_vram_init;
+ engine->vram.takedown = nv50_vram_fini;
engine->vram.get = nv50_vram_new;
engine->vram.put = nv50_vram_del;
engine->vram.flags_valid = nv50_vram_flags_valid;
break;
- case 0xC0:
+ case 0xc0:
engine->instmem.init = nvc0_instmem_init;
engine->instmem.takedown = nvc0_instmem_takedown;
engine->instmem.suspend = nvc0_instmem_suspend;
@@ -411,15 +417,78 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.irq_unregister = nv50_gpio_irq_unregister;
engine->gpio.irq_enable = nv50_gpio_irq_enable;
engine->vram.init = nvc0_vram_init;
+ engine->vram.takedown = nv50_vram_fini;
engine->vram.get = nvc0_vram_new;
engine->vram.put = nv50_vram_del;
engine->vram.flags_valid = nvc0_vram_flags_valid;
+ engine->pm.temp_get = nv84_temp_get;
+ engine->pm.clocks_get = nvc0_pm_clocks_get;
+ engine->pm.voltage_get = nouveau_voltage_gpio_get;
+ engine->pm.voltage_set = nouveau_voltage_gpio_set;
+ break;
+ case 0xd0:
+ engine->instmem.init = nvc0_instmem_init;
+ engine->instmem.takedown = nvc0_instmem_takedown;
+ engine->instmem.suspend = nvc0_instmem_suspend;
+ engine->instmem.resume = nvc0_instmem_resume;
+ engine->instmem.get = nv50_instmem_get;
+ engine->instmem.put = nv50_instmem_put;
+ engine->instmem.map = nv50_instmem_map;
+ engine->instmem.unmap = nv50_instmem_unmap;
+ engine->instmem.flush = nv84_instmem_flush;
+ engine->mc.init = nv50_mc_init;
+ engine->mc.takedown = nv50_mc_takedown;
+ engine->timer.init = nv04_timer_init;
+ engine->timer.read = nv04_timer_read;
+ engine->timer.takedown = nv04_timer_takedown;
+ engine->fb.init = nvc0_fb_init;
+ engine->fb.takedown = nvc0_fb_takedown;
+ engine->fifo.channels = 128;
+ engine->fifo.init = nvc0_fifo_init;
+ engine->fifo.takedown = nvc0_fifo_takedown;
+ engine->fifo.disable = nvc0_fifo_disable;
+ engine->fifo.enable = nvc0_fifo_enable;
+ engine->fifo.reassign = nvc0_fifo_reassign;
+ engine->fifo.channel_id = nvc0_fifo_channel_id;
+ engine->fifo.create_context = nvc0_fifo_create_context;
+ engine->fifo.destroy_context = nvc0_fifo_destroy_context;
+ engine->fifo.load_context = nvc0_fifo_load_context;
+ engine->fifo.unload_context = nvc0_fifo_unload_context;
+ engine->display.early_init = nouveau_stub_init;
+ engine->display.late_takedown = nouveau_stub_takedown;
+ engine->display.create = nvd0_display_create;
+ engine->display.init = nvd0_display_init;
+ engine->display.destroy = nvd0_display_destroy;
+ engine->gpio.init = nv50_gpio_init;
+ engine->gpio.takedown = nouveau_stub_takedown;
+ engine->gpio.get = nvd0_gpio_get;
+ engine->gpio.set = nvd0_gpio_set;
+ engine->gpio.irq_register = nv50_gpio_irq_register;
+ engine->gpio.irq_unregister = nv50_gpio_irq_unregister;
+ engine->gpio.irq_enable = nv50_gpio_irq_enable;
+ engine->vram.init = nvc0_vram_init;
+ engine->vram.takedown = nv50_vram_fini;
+ engine->vram.get = nvc0_vram_new;
+ engine->vram.put = nv50_vram_del;
+ engine->vram.flags_valid = nvc0_vram_flags_valid;
+ engine->pm.clocks_get = nvc0_pm_clocks_get;
+ engine->pm.voltage_get = nouveau_voltage_gpio_get;
+ engine->pm.voltage_set = nouveau_voltage_gpio_set;
break;
default:
NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
return 1;
}
+ /* headless mode */
+ if (nouveau_modeset == 2) {
+ engine->display.early_init = nouveau_stub_init;
+ engine->display.late_takedown = nouveau_stub_takedown;
+ engine->display.create = nouveau_stub_init;
+ engine->display.init = nouveau_stub_init;
+ engine->display.destroy = nouveau_stub_takedown;
+ }
+
return 0;
}
@@ -441,21 +510,6 @@ nouveau_vga_set_decode(void *priv, bool state)
return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
}
-static int
-nouveau_card_init_channel(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- int ret;
-
- ret = nouveau_channel_alloc(dev, &dev_priv->channel,
- (struct drm_file *)-2, NvDmaFB, NvDmaTT);
- if (ret)
- return ret;
-
- mutex_unlock(&dev_priv->channel->mutex);
- return 0;
-}
-
static void nouveau_switcheroo_set_state(struct pci_dev *pdev,
enum vga_switcheroo_state state)
{
@@ -525,9 +579,17 @@ nouveau_card_init(struct drm_device *dev)
if (ret)
goto out_display_early;
+ /* workaround an odd issue on nvc1 by disabling the device's
+ * nosnoop capability. hopefully won't cause issues until a
+ * better fix is found - assuming there is one...
+ */
+ if (dev_priv->chipset == 0xc1) {
+ nv_mask(dev, 0x00088080, 0x00000800, 0x00000000);
+ }
+
nouveau_pm_init(dev);
- ret = nouveau_mem_vram_init(dev);
+ ret = engine->vram.init(dev);
if (ret)
goto out_bios;
@@ -539,10 +601,14 @@ nouveau_card_init(struct drm_device *dev)
if (ret)
goto out_gpuobj;
- ret = nouveau_mem_gart_init(dev);
+ ret = nouveau_mem_vram_init(dev);
if (ret)
goto out_instmem;
+ ret = nouveau_mem_gart_init(dev);
+ if (ret)
+ goto out_ttmvram;
+
/* PMC */
ret = engine->mc.init(dev);
if (ret)
@@ -563,7 +629,7 @@ nouveau_card_init(struct drm_device *dev)
if (ret)
goto out_timer;
- if (!nouveau_noaccel) {
+ if (!dev_priv->noaccel) {
switch (dev_priv->card_type) {
case NV_04:
nv04_graph_create(dev);
@@ -618,8 +684,11 @@ nouveau_card_init(struct drm_device *dev)
break;
}
- if (dev_priv->card_type == NV_40)
- nv40_mpeg_create(dev);
+ if (dev_priv->card_type == NV_40 ||
+ dev_priv->chipset == 0x31 ||
+ dev_priv->chipset == 0x34 ||
+ dev_priv->chipset == 0x36)
+ nv31_mpeg_create(dev);
else
if (dev_priv->card_type == NV_50 &&
(dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0))
@@ -639,50 +708,78 @@ nouveau_card_init(struct drm_device *dev)
goto out_engine;
}
- ret = engine->display.create(dev);
+ ret = nouveau_irq_init(dev);
if (ret)
goto out_fifo;
- ret = drm_vblank_init(dev, nv_two_heads(dev) ? 2 : 1);
- if (ret)
- goto out_vblank;
+ /* initialise general modesetting */
+ drm_mode_config_init(dev);
+ drm_mode_create_scaling_mode_property(dev);
+ drm_mode_create_dithering_property(dev);
+ dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
+ dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1);
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+ if (dev_priv->card_type < NV_10) {
+ dev->mode_config.max_width = 2048;
+ dev->mode_config.max_height = 2048;
+ } else
+ if (dev_priv->card_type < NV_50) {
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+ } else {
+ dev->mode_config.max_width = 8192;
+ dev->mode_config.max_height = 8192;
+ }
- ret = nouveau_irq_init(dev);
+ ret = engine->display.create(dev);
if (ret)
- goto out_vblank;
+ goto out_irq;
- /* what about PVIDEO/PCRTC/PRAMDAC etc? */
+ nouveau_backlight_init(dev);
if (dev_priv->eng[NVOBJ_ENGINE_GR]) {
ret = nouveau_fence_init(dev);
if (ret)
- goto out_irq;
+ goto out_disp;
- ret = nouveau_card_init_channel(dev);
+ ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL,
+ NvDmaFB, NvDmaTT);
if (ret)
goto out_fence;
+
+ mutex_unlock(&dev_priv->channel->mutex);
+ }
+
+ if (dev->mode_config.num_crtc) {
+ ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+ if (ret)
+ goto out_chan;
+
+ nouveau_fbcon_init(dev);
+ drm_kms_helper_poll_init(dev);
}
- nouveau_fbcon_init(dev);
- drm_kms_helper_poll_init(dev);
return 0;
+out_chan:
+ nouveau_channel_put_unlocked(&dev_priv->channel);
out_fence:
nouveau_fence_fini(dev);
+out_disp:
+ nouveau_backlight_exit(dev);
+ engine->display.destroy(dev);
out_irq:
nouveau_irq_fini(dev);
-out_vblank:
- drm_vblank_cleanup(dev);
- engine->display.destroy(dev);
out_fifo:
- if (!nouveau_noaccel)
+ if (!dev_priv->noaccel)
engine->fifo.takedown(dev);
out_engine:
- if (!nouveau_noaccel) {
+ if (!dev_priv->noaccel) {
for (e = e - 1; e >= 0; e--) {
if (!dev_priv->eng[e])
continue;
- dev_priv->eng[e]->fini(dev, e);
+ dev_priv->eng[e]->fini(dev, e, false);
dev_priv->eng[e]->destroy(dev,e );
}
}
@@ -696,12 +793,14 @@ out_mc:
engine->mc.takedown(dev);
out_gart:
nouveau_mem_gart_fini(dev);
+out_ttmvram:
+ nouveau_mem_vram_fini(dev);
out_instmem:
engine->instmem.takedown(dev);
out_gpuobj:
nouveau_gpuobj_takedown(dev);
out_vram:
- nouveau_mem_vram_fini(dev);
+ engine->vram.takedown(dev);
out_bios:
nouveau_pm_fini(dev);
nouveau_bios_takedown(dev);
@@ -718,16 +817,26 @@ static void nouveau_card_takedown(struct drm_device *dev)
struct nouveau_engine *engine = &dev_priv->engine;
int e;
+ if (dev->mode_config.num_crtc) {
+ drm_kms_helper_poll_fini(dev);
+ nouveau_fbcon_fini(dev);
+ drm_vblank_cleanup(dev);
+ }
+
if (dev_priv->channel) {
- nouveau_fence_fini(dev);
nouveau_channel_put_unlocked(&dev_priv->channel);
+ nouveau_fence_fini(dev);
}
- if (!nouveau_noaccel) {
+ nouveau_backlight_exit(dev);
+ engine->display.destroy(dev);
+ drm_mode_config_cleanup(dev);
+
+ if (!dev_priv->noaccel) {
engine->fifo.takedown(dev);
for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) {
if (dev_priv->eng[e]) {
- dev_priv->eng[e]->fini(dev, e);
+ dev_priv->eng[e]->fini(dev, e, false);
dev_priv->eng[e]->destroy(dev,e );
}
}
@@ -748,13 +857,13 @@ static void nouveau_card_takedown(struct drm_device *dev)
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
mutex_unlock(&dev->struct_mutex);
nouveau_mem_gart_fini(dev);
+ nouveau_mem_vram_fini(dev);
engine->instmem.takedown(dev);
nouveau_gpuobj_takedown(dev);
- nouveau_mem_vram_fini(dev);
+ engine->vram.takedown(dev);
nouveau_irq_fini(dev);
- drm_vblank_cleanup(dev);
nouveau_pm_fini(dev);
nouveau_bios_takedown(dev);
@@ -762,6 +871,41 @@ static void nouveau_card_takedown(struct drm_device *dev)
vga_client_register(dev->pdev, NULL, NULL, NULL);
}
+int
+nouveau_open(struct drm_device *dev, struct drm_file *file_priv)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_fpriv *fpriv;
+ int ret;
+
+ fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
+ if (unlikely(!fpriv))
+ return -ENOMEM;
+
+ spin_lock_init(&fpriv->lock);
+ INIT_LIST_HEAD(&fpriv->channels);
+
+ if (dev_priv->card_type == NV_50) {
+ ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0020000000ULL,
+ &fpriv->vm);
+ if (ret) {
+ kfree(fpriv);
+ return ret;
+ }
+ } else
+ if (dev_priv->card_type >= NV_C0) {
+ ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0008000000ULL,
+ &fpriv->vm);
+ if (ret) {
+ kfree(fpriv);
+ return ret;
+ }
+ }
+
+ file_priv->driver_priv = fpriv;
+ return 0;
+}
+
/* here a client dies, release the stuff that was allocated for its
* file_priv */
void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv)
@@ -769,6 +913,14 @@ void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv)
nouveau_channel_cleanup(dev, file_priv);
}
+void
+nouveau_postclose(struct drm_device *dev, struct drm_file *file_priv)
+{
+ struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
+ nouveau_vm_ref(NULL, &fpriv->vm, NULL);
+ kfree(fpriv);
+}
+
/* first module load, setup the mmio/fb mapping */
/* KMS: we need mmio at load time, not when the first drm client opens. */
int nouveau_firstopen(struct drm_device *dev)
@@ -844,7 +996,7 @@ static int nouveau_remove_conflicting_drivers(struct drm_device *dev)
int nouveau_load(struct drm_device *dev, unsigned long flags)
{
struct drm_nouveau_private *dev_priv;
- uint32_t reg0;
+ uint32_t reg0, strap;
resource_size_t mmio_start_offs;
int ret;
@@ -888,13 +1040,11 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
/* Time to determine the card architecture */
reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
- dev_priv->stepping = 0; /* XXX: add stepping for pre-NV10? */
/* We're dealing with >=NV10 */
if ((reg0 & 0x0f000000) > 0) {
/* Bit 27-20 contain the architecture in hex */
dev_priv->chipset = (reg0 & 0xff00000) >> 20;
- dev_priv->stepping = (reg0 & 0xff);
/* NV04 or NV05 */
} else if ((reg0 & 0xff00fff0) == 0x20004000) {
if (reg0 & 0x00f00000)
@@ -924,6 +1074,9 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
case 0xc0:
dev_priv->card_type = NV_C0;
break;
+ case 0xd0:
+ dev_priv->card_type = NV_D0;
+ break;
default:
NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0);
ret = -EINVAL;
@@ -933,6 +1086,43 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
dev_priv->card_type, reg0);
+ /* determine frequency of timing crystal */
+ strap = nv_rd32(dev, 0x101000);
+ if ( dev_priv->chipset < 0x17 ||
+ (dev_priv->chipset >= 0x20 && dev_priv->chipset <= 0x25))
+ strap &= 0x00000040;
+ else
+ strap &= 0x00400040;
+
+ switch (strap) {
+ case 0x00000000: dev_priv->crystal = 13500; break;
+ case 0x00000040: dev_priv->crystal = 14318; break;
+ case 0x00400000: dev_priv->crystal = 27000; break;
+ case 0x00400040: dev_priv->crystal = 25000; break;
+ }
+
+ NV_DEBUG(dev, "crystal freq: %dKHz\n", dev_priv->crystal);
+
+ /* Determine whether we'll attempt acceleration or not, some
+ * cards are disabled by default here due to them being known
+ * non-functional, or never been tested due to lack of hw.
+ */
+ dev_priv->noaccel = !!nouveau_noaccel;
+ if (nouveau_noaccel == -1) {
+ switch (dev_priv->chipset) {
+#if 0
+ case 0xXX: /* known broken */
+ NV_INFO(dev, "acceleration disabled by default, pass "
+ "noaccel=0 to force enable\n");
+ dev_priv->noaccel = true;
+ break;
+#endif
+ default:
+ dev_priv->noaccel = false;
+ break;
+ }
+ }
+
ret = nouveau_remove_conflicting_drivers(dev);
if (ret)
goto err_mmio;
@@ -948,7 +1138,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
ioremap(pci_resource_start(dev->pdev, ramin_bar),
dev_priv->ramin_size);
if (!dev_priv->ramin) {
- NV_ERROR(dev, "Failed to PRAMIN BAR");
+ NV_ERROR(dev, "Failed to map PRAMIN BAR\n");
ret = -ENOMEM;
goto err_mmio;
}
@@ -997,11 +1187,7 @@ void nouveau_lastclose(struct drm_device *dev)
int nouveau_unload(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_engine *engine = &dev_priv->engine;
- drm_kms_helper_poll_fini(dev);
- nouveau_fbcon_fini(dev);
- engine->display.destroy(dev);
nouveau_card_takedown(dev);
iounmap(dev_priv->mmio);
@@ -1031,7 +1217,7 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
case NOUVEAU_GETPARAM_BUS_TYPE:
if (drm_pci_device_is_agp(dev))
getparam->value = NV_AGP;
- else if (drm_pci_device_is_pcie(dev))
+ else if (pci_is_pcie(dev->pdev))
getparam->value = NV_PCIE;
else
getparam->value = NV_PCI;
@@ -1052,7 +1238,7 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
getparam->value = 1;
break;
case NOUVEAU_GETPARAM_HAS_PAGEFLIP:
- getparam->value = 1;
+ getparam->value = dev_priv->card_type < NV_D0;
break;
case NOUVEAU_GETPARAM_GRAPH_UNITS:
/* NV40 and NV50 versions are quite different, but register
@@ -1120,6 +1306,23 @@ nouveau_wait_ne(struct drm_device *dev, uint64_t timeout,
return false;
}
+/* Wait until cond(data) == true, up until timeout has hit */
+bool
+nouveau_wait_cb(struct drm_device *dev, u64 timeout,
+ bool (*cond)(void *), void *data)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
+ u64 start = ptimer->read(dev);
+
+ do {
+ if (cond(data) == true)
+ return true;
+ } while (ptimer->read(dev) - start < timeout);
+
+ return false;
+}
+
/* Waits for PGRAPH to go completely idle */
bool nouveau_wait_for_idle(struct drm_device *dev)
{
diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c
index 649b0413b09f..5a46446dd5a8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_temp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_temp.c
@@ -22,6 +22,8 @@
* Authors: Martin Peres
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "nouveau_drv.h"
@@ -43,7 +45,7 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp)
/* Set the default sensor's contants */
sensor->offset_constant = 0;
- sensor->offset_mult = 1;
+ sensor->offset_mult = 0;
sensor->offset_div = 1;
sensor->slope_mult = 1;
sensor->slope_div = 1;
@@ -99,6 +101,13 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp)
sensor->slope_mult = 431;
sensor->slope_div = 10000;
break;
+
+ case 0x67:
+ sensor->offset_mult = -26149;
+ sensor->offset_div = 100;
+ sensor->slope_mult = 484;
+ sensor->slope_div = 10000;
+ break;
}
}
@@ -109,7 +118,7 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp)
/* Read the entries from the table */
for (i = 0; i < entries; i++) {
- u16 value = ROM16(temp[1]);
+ s16 value = ROM16(temp[1]);
switch (temp[0]) {
case 0x01:
@@ -160,8 +169,8 @@ nv40_sensor_setup(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants;
- u32 offset = sensor->offset_mult / sensor->offset_div;
- u32 sensor_calibration;
+ s32 offset = sensor->offset_mult / sensor->offset_div;
+ s32 sensor_calibration;
/* set up the sensors */
sensor_calibration = 120 - offset - sensor->offset_constant;
diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.c b/drivers/gpu/drm/nouveau/nouveau_vm.c
index 519a6b4bba46..ef0832b29ad2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vm.c
@@ -172,9 +172,9 @@ nouveau_vm_unmap_pgt(struct nouveau_vm *vm, int big, u32 fpde, u32 lpde)
vm->map_pgt(vpgd->obj, pde, vpgt->obj);
}
- mutex_unlock(&vm->mm->mutex);
+ mutex_unlock(&vm->mm.mutex);
nouveau_gpuobj_ref(NULL, &pgt);
- mutex_lock(&vm->mm->mutex);
+ mutex_lock(&vm->mm.mutex);
}
}
@@ -191,18 +191,18 @@ nouveau_vm_map_pgt(struct nouveau_vm *vm, u32 pde, u32 type)
pgt_size = (1 << (vm->pgt_bits + 12)) >> type;
pgt_size *= 8;
- mutex_unlock(&vm->mm->mutex);
+ mutex_unlock(&vm->mm.mutex);
ret = nouveau_gpuobj_new(vm->dev, NULL, pgt_size, 0x1000,
NVOBJ_FLAG_ZERO_ALLOC, &pgt);
- mutex_lock(&vm->mm->mutex);
+ mutex_lock(&vm->mm.mutex);
if (unlikely(ret))
return ret;
/* someone beat us to filling the PDE while we didn't have the lock */
if (unlikely(vpgt->refcount[big]++)) {
- mutex_unlock(&vm->mm->mutex);
+ mutex_unlock(&vm->mm.mutex);
nouveau_gpuobj_ref(NULL, &pgt);
- mutex_lock(&vm->mm->mutex);
+ mutex_lock(&vm->mm.mutex);
return 0;
}
@@ -223,10 +223,10 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
u32 fpde, lpde, pde;
int ret;
- mutex_lock(&vm->mm->mutex);
- ret = nouveau_mm_get(vm->mm, page_shift, msize, 0, align, &vma->node);
+ mutex_lock(&vm->mm.mutex);
+ ret = nouveau_mm_get(&vm->mm, page_shift, msize, 0, align, &vma->node);
if (unlikely(ret != 0)) {
- mutex_unlock(&vm->mm->mutex);
+ mutex_unlock(&vm->mm.mutex);
return ret;
}
@@ -245,13 +245,13 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
if (ret) {
if (pde != fpde)
nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1);
- nouveau_mm_put(vm->mm, vma->node);
- mutex_unlock(&vm->mm->mutex);
+ nouveau_mm_put(&vm->mm, vma->node);
+ mutex_unlock(&vm->mm.mutex);
vma->node = NULL;
return ret;
}
}
- mutex_unlock(&vm->mm->mutex);
+ mutex_unlock(&vm->mm.mutex);
vma->vm = vm;
vma->offset = (u64)vma->node->offset << 12;
@@ -270,11 +270,11 @@ nouveau_vm_put(struct nouveau_vma *vma)
fpde = (vma->node->offset >> vm->pgt_bits);
lpde = (vma->node->offset + vma->node->length - 1) >> vm->pgt_bits;
- mutex_lock(&vm->mm->mutex);
+ mutex_lock(&vm->mm.mutex);
nouveau_vm_unmap_pgt(vm, vma->node->type != vm->spg_shift, fpde, lpde);
- nouveau_mm_put(vm->mm, vma->node);
+ nouveau_mm_put(&vm->mm, vma->node);
vma->node = NULL;
- mutex_unlock(&vm->mm->mutex);
+ mutex_unlock(&vm->mm.mutex);
}
int
@@ -306,7 +306,7 @@ nouveau_vm_new(struct drm_device *dev, u64 offset, u64 length, u64 mm_offset,
block = length;
} else
- if (dev_priv->card_type == NV_C0) {
+ if (dev_priv->card_type >= NV_C0) {
vm->map_pgt = nvc0_vm_map_pgt;
vm->map = nvc0_vm_map;
vm->map_sg = nvc0_vm_map_sg;
@@ -360,32 +360,35 @@ nouveau_vm_link(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd)
nouveau_gpuobj_ref(pgd, &vpgd->obj);
- mutex_lock(&vm->mm->mutex);
+ mutex_lock(&vm->mm.mutex);
for (i = vm->fpde; i <= vm->lpde; i++)
vm->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj);
list_add(&vpgd->head, &vm->pgd_list);
- mutex_unlock(&vm->mm->mutex);
+ mutex_unlock(&vm->mm.mutex);
return 0;
}
static void
-nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd)
+nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd)
{
struct nouveau_vm_pgd *vpgd, *tmp;
+ struct nouveau_gpuobj *pgd = NULL;
- if (!pgd)
+ if (!mpgd)
return;
- mutex_lock(&vm->mm->mutex);
+ mutex_lock(&vm->mm.mutex);
list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {
- if (vpgd->obj != pgd)
- continue;
-
- list_del(&vpgd->head);
- nouveau_gpuobj_ref(NULL, &vpgd->obj);
- kfree(vpgd);
+ if (vpgd->obj == mpgd) {
+ pgd = vpgd->obj;
+ list_del(&vpgd->head);
+ kfree(vpgd);
+ break;
+ }
}
- mutex_unlock(&vm->mm->mutex);
+ mutex_unlock(&vm->mm.mutex);
+
+ nouveau_gpuobj_ref(NULL, &pgd);
}
static void
@@ -396,8 +399,8 @@ nouveau_vm_del(struct nouveau_vm *vm)
list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {
nouveau_vm_unlink(vm, vpgd->obj);
}
- WARN_ON(nouveau_mm_fini(&vm->mm) != 0);
+ nouveau_mm_fini(&vm->mm);
kfree(vm->pgt);
kfree(vm);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.h b/drivers/gpu/drm/nouveau/nouveau_vm.h
index c48a9fc2b47b..6ce995f7797e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_vm.h
@@ -41,6 +41,8 @@ struct nouveau_vm_pgd {
};
struct nouveau_vma {
+ struct list_head head;
+ int refcount;
struct nouveau_vm *vm;
struct nouveau_mm_node *node;
u64 offset;
@@ -49,7 +51,7 @@ struct nouveau_vma {
struct nouveau_vm {
struct drm_device *dev;
- struct nouveau_mm *mm;
+ struct nouveau_mm mm;
int refcount;
struct list_head pgd_list;
diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c
index 75e872741d92..86d03e15735d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_volt.c
+++ b/drivers/gpu/drm/nouveau/nouveau_volt.c
@@ -27,7 +27,7 @@
#include "nouveau_drv.h"
#include "nouveau_pm.h"
-static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a };
+static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a, 0x73 };
static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]);
int
@@ -170,6 +170,13 @@ nouveau_volt_init(struct drm_device *dev)
*/
vidshift = 2;
break;
+ case 0x40:
+ headerlen = volt[1];
+ recordlen = volt[2];
+ entries = volt[3]; /* not a clue what the entries are for.. */
+ vidmask = volt[11]; /* guess.. */
+ vidshift = 0;
+ break;
default:
NV_WARN(dev, "voltage table 0x%02x unknown\n", volt[0]);
return;
@@ -197,16 +204,37 @@ nouveau_volt_init(struct drm_device *dev)
}
/* parse vbios entries into common format */
- voltage->level = kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL);
- if (!voltage->level)
- return;
+ voltage->version = volt[0];
+ if (voltage->version < 0x40) {
+ voltage->nr_level = entries;
+ voltage->level =
+ kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL);
+ if (!voltage->level)
+ return;
- entry = volt + headerlen;
- for (i = 0; i < entries; i++, entry += recordlen) {
- voltage->level[i].voltage = entry[0];
- voltage->level[i].vid = entry[1] >> vidshift;
+ entry = volt + headerlen;
+ for (i = 0; i < entries; i++, entry += recordlen) {
+ voltage->level[i].voltage = entry[0] * 10000;
+ voltage->level[i].vid = entry[1] >> vidshift;
+ }
+ } else {
+ u32 volt_uv = ROM32(volt[4]);
+ s16 step_uv = ROM16(volt[8]);
+ u8 vid;
+
+ voltage->nr_level = voltage->vid_mask + 1;
+ voltage->level = kcalloc(voltage->nr_level,
+ sizeof(*voltage->level), GFP_KERNEL);
+ if (!voltage->level)
+ return;
+
+ for (vid = 0; vid <= voltage->vid_mask; vid++) {
+ voltage->level[vid].voltage = volt_uv;
+ voltage->level[vid].vid = vid;
+ volt_uv += step_uv;
+ }
}
- voltage->nr_level = entries;
+
voltage->supported = true;
}
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
index f1a3ae491995..5e45398a9e2d 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -781,11 +781,20 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
- struct drm_framebuffer *drm_fb = nv_crtc->base.fb;
- struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
+ struct drm_framebuffer *drm_fb;
+ struct nouveau_framebuffer *fb;
int arb_burst, arb_lwm;
int ret;
+ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
+
+ /* no fb bound */
+ if (!atomic && !crtc->fb) {
+ NV_DEBUG_KMS(dev, "No FB bound\n");
+ return 0;
+ }
+
+
/* If atomic, we want to switch to the fb we were passed, so
* now we update pointers to do that. (We don't pin; just
* assume we're already pinned and update the base address.)
@@ -794,6 +803,8 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
drm_fb = passed_fb;
fb = nouveau_framebuffer(passed_fb);
} else {
+ drm_fb = crtc->fb;
+ fb = nouveau_framebuffer(crtc->fb);
/* If not atomic, we can go ahead and pin, and unpin the
* old fb we were passed.
*/
@@ -1035,7 +1046,7 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num)
drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs);
drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
- ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
+ ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
0, 0x0000, &nv_crtc->cursor.nvbo);
if (!ret) {
ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c
index 1715e1464b7d..6bd8518d7b2e 100644
--- a/drivers/gpu/drm/nouveau/nv04_display.c
+++ b/drivers/gpu/drm/nouveau/nv04_display.c
@@ -126,27 +126,6 @@ nv04_display_create(struct drm_device *dev)
nouveau_hw_save_vga_fonts(dev, 1);
- drm_mode_config_init(dev);
- drm_mode_create_scaling_mode_property(dev);
- drm_mode_create_dithering_property(dev);
-
- dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
-
- dev->mode_config.min_width = 0;
- dev->mode_config.min_height = 0;
- switch (dev_priv->card_type) {
- case NV_04:
- dev->mode_config.max_width = 2048;
- dev->mode_config.max_height = 2048;
- break;
- default:
- dev->mode_config.max_width = 4096;
- dev->mode_config.max_height = 4096;
- break;
- }
-
- dev->mode_config.fb_base = dev_priv->fb_phys;
-
nv04_crtc_create(dev, 0);
if (nv_two_heads(dev))
nv04_crtc_create(dev, 1);
@@ -235,8 +214,6 @@ nv04_display_destroy(struct drm_device *dev)
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
crtc->funcs->restore(crtc);
- drm_mode_config_cleanup(dev);
-
nouveau_hw_save_vga_fonts(dev, 0);
}
diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c
index 3626ee7db3ba..dbdea8ed3925 100644
--- a/drivers/gpu/drm/nouveau/nv04_graph.c
+++ b/drivers/gpu/drm/nouveau/nv04_graph.c
@@ -450,13 +450,13 @@ nv04_graph_context_del(struct nouveau_channel *chan, int engine)
unsigned long flags;
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
- nv04_graph_fifo_access(dev, false);
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
/* Unload the context if it's the currently active one */
if (nv04_graph_channel(dev) == chan)
nv04_graph_unload_context(dev);
- nv04_graph_fifo_access(dev, true);
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
/* Free the context resources */
@@ -538,24 +538,18 @@ nv04_graph_init(struct drm_device *dev, int engine)
}
static int
-nv04_graph_fini(struct drm_device *dev, int engine)
+nv04_graph_fini(struct drm_device *dev, int engine, bool suspend)
{
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
+ if (!nv_wait(dev, NV04_PGRAPH_STATUS, ~0, 0) && suspend) {
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
+ return -EBUSY;
+ }
nv04_graph_unload_context(dev);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
return 0;
}
-void
-nv04_graph_fifo_access(struct drm_device *dev, bool enabled)
-{
- if (enabled)
- nv_wr32(dev, NV04_PGRAPH_FIFO,
- nv_rd32(dev, NV04_PGRAPH_FIFO) | 1);
- else
- nv_wr32(dev, NV04_PGRAPH_FIFO,
- nv_rd32(dev, NV04_PGRAPH_FIFO) & ~1);
-}
-
static int
nv04_graph_mthd_set_ref(struct nouveau_channel *chan,
u32 class, u32 mthd, u32 data)
diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c
index b8611b955313..c1248e0740a3 100644
--- a/drivers/gpu/drm/nouveau/nv04_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv04_instmem.c
@@ -28,6 +28,31 @@ int nv04_instmem_init(struct drm_device *dev)
/* RAMIN always available */
dev_priv->ramin_available = true;
+ /* Reserve space at end of VRAM for PRAMIN */
+ if (dev_priv->card_type >= NV_40) {
+ u32 vs = hweight8((nv_rd32(dev, 0x001540) & 0x0000ff00) >> 8);
+ u32 rsvd;
+
+ /* estimate grctx size, the magics come from nv40_grctx.c */
+ if (dev_priv->chipset == 0x40) rsvd = 0x6aa0 * vs;
+ else if (dev_priv->chipset < 0x43) rsvd = 0x4f00 * vs;
+ else if (nv44_graph_class(dev)) rsvd = 0x4980 * vs;
+ else rsvd = 0x4a40 * vs;
+ rsvd += 16 * 1024;
+ rsvd *= dev_priv->engine.fifo.channels;
+
+ /* pciegart table */
+ if (pci_is_pcie(dev->pdev))
+ rsvd += 512 * 1024;
+
+ /* object storage */
+ rsvd += 512 * 1024;
+
+ dev_priv->ramin_rsvd_vram = round_up(rsvd, 4096);
+ } else {
+ dev_priv->ramin_rsvd_vram = 512 * 1024;
+ }
+
/* Setup shared RAMHT */
ret = nouveau_gpuobj_new_fake(dev, 0x10000, ~0, 4096,
NVOBJ_FLAG_ZERO_ALLOC, &ramht);
@@ -112,7 +137,8 @@ nv04_instmem_resume(struct drm_device *dev)
}
int
-nv04_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align)
+nv04_instmem_get(struct nouveau_gpuobj *gpuobj, struct nouveau_channel *chan,
+ u32 size, u32 align)
{
struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private;
struct drm_mm_node *ramin = NULL;
diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c
index eb1c70dd82ed..9ae92a87b8cc 100644
--- a/drivers/gpu/drm/nouveau/nv04_pm.c
+++ b/drivers/gpu/drm/nouveau/nv04_pm.c
@@ -68,6 +68,7 @@ void
nv04_pm_clock_set(struct drm_device *dev, void *pre_state)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
struct nv04_pm_state *state = pre_state;
u32 reg = state->pll.reg;
@@ -85,6 +86,9 @@ nv04_pm_clock_set(struct drm_device *dev, void *pre_state)
nv_mask(dev, 0x1002c0, 0, 1 << 8);
}
+ if (reg == NV_PRAMDAC_NVPLL_COEFF)
+ ptimer->init(dev);
+
kfree(state);
}
diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c
index 1d09ddd57399..263301b809dd 100644
--- a/drivers/gpu/drm/nouveau/nv04_timer.c
+++ b/drivers/gpu/drm/nouveau/nv04_timer.c
@@ -6,43 +6,75 @@
int
nv04_timer_init(struct drm_device *dev)
{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 m, n, d;
+
nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000);
nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF);
- /* Just use the pre-existing values when possible for now; these regs
- * are not written in nv (driver writer missed a /4 on the address), and
- * writing 8 and 3 to the correct regs breaks the timings on the LVDS
- * hardware sequencing microcode.
- * A correct solution (involving calculations with the GPU PLL) can
- * be done when kernel modesetting lands
- */
- if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) ||
- !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) {
- nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008);
- nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003);
+ /* aim for 31.25MHz, which gives us nanosecond timestamps */
+ d = 1000000 / 32;
+
+ /* determine base clock for timer source */
+ if (dev_priv->chipset < 0x40) {
+ n = dev_priv->engine.pm.clock_get(dev, PLL_CORE);
+ } else
+ if (dev_priv->chipset == 0x40) {
+ /*XXX: figure this out */
+ n = 0;
+ } else {
+ n = dev_priv->crystal;
+ m = 1;
+ while (n < (d * 2)) {
+ n += (n / m);
+ m++;
+ }
+
+ nv_wr32(dev, 0x009220, m - 1);
+ }
+
+ if (!n) {
+ NV_WARN(dev, "PTIMER: unknown input clock freq\n");
+ if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) ||
+ !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) {
+ nv_wr32(dev, NV04_PTIMER_NUMERATOR, 1);
+ nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 1);
+ }
+ return 0;
+ }
+
+ /* reduce ratio to acceptable values */
+ while (((n % 5) == 0) && ((d % 5) == 0)) {
+ n /= 5;
+ d /= 5;
}
+ while (((n % 2) == 0) && ((d % 2) == 0)) {
+ n /= 2;
+ d /= 2;
+ }
+
+ while (n > 0xffff || d > 0xffff) {
+ n >>= 1;
+ d >>= 1;
+ }
+
+ nv_wr32(dev, NV04_PTIMER_NUMERATOR, n);
+ nv_wr32(dev, NV04_PTIMER_DENOMINATOR, d);
return 0;
}
-uint64_t
+u64
nv04_timer_read(struct drm_device *dev)
{
- uint32_t low;
- /* From kmmio dumps on nv28 this looks like how the blob does this.
- * It reads the high dword twice, before and after.
- * The only explanation seems to be that the 64-bit timer counter
- * advances between high and low dword reads and may corrupt the
- * result. Not confirmed.
- */
- uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
- uint32_t high1;
+ u32 hi, lo;
+
do {
- high1 = high2;
- low = nv_rd32(dev, NV04_PTIMER_TIME_0);
- high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
- } while (high1 != high2);
- return (((uint64_t)high2) << 32) | (uint64_t)low;
+ hi = nv_rd32(dev, NV04_PTIMER_TIME_1);
+ lo = nv_rd32(dev, NV04_PTIMER_TIME_0);
+ } while (hi != nv_rd32(dev, NV04_PTIMER_TIME_1));
+
+ return ((u64)hi << 32 | lo);
}
void
diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c
index 0930c6cb88e0..7255e4a4d3f3 100644
--- a/drivers/gpu/drm/nouveau/nv10_graph.c
+++ b/drivers/gpu/drm/nouveau/nv10_graph.c
@@ -708,8 +708,8 @@ static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan,
0x2c000000 | chan->id << 20 | subchan << 16 | 0x18c);
nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, inst);
nv_mask(dev, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000);
- nv04_graph_fifo_access(dev, true);
- nv04_graph_fifo_access(dev, false);
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
/* Restore the FIFO state */
for (i = 0; i < ARRAY_SIZE(fifo); i++)
@@ -879,13 +879,13 @@ nv10_graph_context_del(struct nouveau_channel *chan, int engine)
unsigned long flags;
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
- nv04_graph_fifo_access(dev, false);
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
/* Unload the context if it's the currently active one */
if (nv10_graph_channel(dev) == chan)
nv10_graph_unload_context(dev);
- nv04_graph_fifo_access(dev, true);
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
/* Free the context resources */
@@ -957,8 +957,13 @@ nv10_graph_init(struct drm_device *dev, int engine)
}
static int
-nv10_graph_fini(struct drm_device *dev, int engine)
+nv10_graph_fini(struct drm_device *dev, int engine, bool suspend)
{
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
+ if (!nv_wait(dev, NV04_PGRAPH_STATUS, ~0, 0) && suspend) {
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
+ return -EBUSY;
+ }
nv10_graph_unload_context(dev);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
return 0;
diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c
index affc7d7dd029..183e37512ef9 100644
--- a/drivers/gpu/drm/nouveau/nv20_graph.c
+++ b/drivers/gpu/drm/nouveau/nv20_graph.c
@@ -454,13 +454,13 @@ nv20_graph_context_del(struct nouveau_channel *chan, int engine)
unsigned long flags;
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
- nv04_graph_fifo_access(dev, false);
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
/* Unload the context if it's the currently active one */
if (nv10_graph_channel(dev) == chan)
nv20_graph_unload_context(dev);
- nv04_graph_fifo_access(dev, true);
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
/* Free the context resources */
@@ -654,8 +654,13 @@ nv30_graph_init(struct drm_device *dev, int engine)
}
int
-nv20_graph_fini(struct drm_device *dev, int engine)
+nv20_graph_fini(struct drm_device *dev, int engine, bool suspend)
{
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
+ if (!nv_wait(dev, NV04_PGRAPH_STATUS, ~0, 0) && suspend) {
+ nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
+ return -EBUSY;
+ }
nv20_graph_unload_context(dev);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
return 0;
@@ -753,6 +758,7 @@ nv20_graph_create(struct drm_device *dev)
break;
default:
NV_ERROR(dev, "PGRAPH: unknown chipset\n");
+ kfree(pgraph);
return 0;
}
} else {
@@ -774,6 +780,7 @@ nv20_graph_create(struct drm_device *dev)
break;
default:
NV_ERROR(dev, "PGRAPH: unknown chipset\n");
+ kfree(pgraph);
return 0;
}
}
diff --git a/drivers/gpu/drm/nouveau/nv40_mpeg.c b/drivers/gpu/drm/nouveau/nv31_mpeg.c
index 6d2af292a2e3..6f06a0713f00 100644
--- a/drivers/gpu/drm/nouveau/nv40_mpeg.c
+++ b/drivers/gpu/drm/nouveau/nv31_mpeg.c
@@ -26,10 +26,32 @@
#include "nouveau_drv.h"
#include "nouveau_ramht.h"
-struct nv40_mpeg_engine {
+struct nv31_mpeg_engine {
struct nouveau_exec_engine base;
+ atomic_t refcount;
};
+
+static int
+nv31_mpeg_context_new(struct nouveau_channel *chan, int engine)
+{
+ struct nv31_mpeg_engine *pmpeg = nv_engine(chan->dev, engine);
+
+ if (!atomic_add_unless(&pmpeg->refcount, 1, 1))
+ return -EBUSY;
+
+ chan->engctx[engine] = (void *)0xdeadcafe;
+ return 0;
+}
+
+static void
+nv31_mpeg_context_del(struct nouveau_channel *chan, int engine)
+{
+ struct nv31_mpeg_engine *pmpeg = nv_engine(chan->dev, engine);
+ atomic_dec(&pmpeg->refcount);
+ chan->engctx[engine] = NULL;
+}
+
static int
nv40_mpeg_context_new(struct nouveau_channel *chan, int engine)
{
@@ -81,7 +103,7 @@ nv40_mpeg_context_del(struct nouveau_channel *chan, int engine)
}
static int
-nv40_mpeg_object_new(struct nouveau_channel *chan, int engine,
+nv31_mpeg_object_new(struct nouveau_channel *chan, int engine,
u32 handle, u16 class)
{
struct drm_device *dev = chan->dev;
@@ -103,10 +125,10 @@ nv40_mpeg_object_new(struct nouveau_channel *chan, int engine,
}
static int
-nv40_mpeg_init(struct drm_device *dev, int engine)
+nv31_mpeg_init(struct drm_device *dev, int engine)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine);
+ struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine);
int i;
/* VPE init */
@@ -121,7 +143,7 @@ nv40_mpeg_init(struct drm_device *dev, int engine)
/* PMPEG init */
nv_wr32(dev, 0x00b32c, 0x00000000);
nv_wr32(dev, 0x00b314, 0x00000100);
- nv_wr32(dev, 0x00b220, 0x00000044);
+ nv_wr32(dev, 0x00b220, nv44_graph_class(dev) ? 0x00000044 : 0x00000031);
nv_wr32(dev, 0x00b300, 0x02001ec1);
nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001);
@@ -137,7 +159,7 @@ nv40_mpeg_init(struct drm_device *dev, int engine)
}
static int
-nv40_mpeg_fini(struct drm_device *dev, int engine)
+nv31_mpeg_fini(struct drm_device *dev, int engine, bool suspend)
{
/*XXX: context save? */
nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000);
@@ -146,7 +168,7 @@ nv40_mpeg_fini(struct drm_device *dev, int engine)
}
static int
-nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data)
+nv31_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data)
{
struct drm_device *dev = chan->dev;
u32 inst = data << 4;
@@ -184,13 +206,17 @@ nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data)
}
static int
-nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst)
+nv31_mpeg_isr_chid(struct drm_device *dev, u32 inst)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *ctx;
unsigned long flags;
int i;
+ /* hardcode drm channel id on nv3x, so swmthd lookup works */
+ if (dev_priv->card_type < NV_40)
+ return 0;
+
spin_lock_irqsave(&dev_priv->channels.lock, flags);
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
if (!dev_priv->channels.ptr[i])
@@ -205,7 +231,7 @@ nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst)
}
static void
-nv40_vpe_set_tile_region(struct drm_device *dev, int i)
+nv31_vpe_set_tile_region(struct drm_device *dev, int i)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
@@ -216,10 +242,10 @@ nv40_vpe_set_tile_region(struct drm_device *dev, int i)
}
static void
-nv40_mpeg_isr(struct drm_device *dev)
+nv31_mpeg_isr(struct drm_device *dev)
{
u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4;
- u32 chid = nv40_mpeg_isr_chid(dev, inst);
+ u32 chid = nv31_mpeg_isr_chid(dev, inst);
u32 stat = nv_rd32(dev, 0x00b100);
u32 type = nv_rd32(dev, 0x00b230);
u32 mthd = nv_rd32(dev, 0x00b234);
@@ -249,10 +275,10 @@ nv40_mpeg_isr(struct drm_device *dev)
}
static void
-nv40_vpe_isr(struct drm_device *dev)
+nv31_vpe_isr(struct drm_device *dev)
{
if (nv_rd32(dev, 0x00b100))
- nv40_mpeg_isr(dev);
+ nv31_mpeg_isr(dev);
if (nv_rd32(dev, 0x00b800)) {
u32 stat = nv_rd32(dev, 0x00b800);
@@ -262,9 +288,9 @@ nv40_vpe_isr(struct drm_device *dev)
}
static void
-nv40_mpeg_destroy(struct drm_device *dev, int engine)
+nv31_mpeg_destroy(struct drm_device *dev, int engine)
{
- struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine);
+ struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine);
nouveau_irq_unregister(dev, 0);
@@ -273,34 +299,41 @@ nv40_mpeg_destroy(struct drm_device *dev, int engine)
}
int
-nv40_mpeg_create(struct drm_device *dev)
+nv31_mpeg_create(struct drm_device *dev)
{
- struct nv40_mpeg_engine *pmpeg;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv31_mpeg_engine *pmpeg;
pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL);
if (!pmpeg)
return -ENOMEM;
-
- pmpeg->base.destroy = nv40_mpeg_destroy;
- pmpeg->base.init = nv40_mpeg_init;
- pmpeg->base.fini = nv40_mpeg_fini;
- pmpeg->base.context_new = nv40_mpeg_context_new;
- pmpeg->base.context_del = nv40_mpeg_context_del;
- pmpeg->base.object_new = nv40_mpeg_object_new;
+ atomic_set(&pmpeg->refcount, 0);
+
+ pmpeg->base.destroy = nv31_mpeg_destroy;
+ pmpeg->base.init = nv31_mpeg_init;
+ pmpeg->base.fini = nv31_mpeg_fini;
+ if (dev_priv->card_type < NV_40) {
+ pmpeg->base.context_new = nv31_mpeg_context_new;
+ pmpeg->base.context_del = nv31_mpeg_context_del;
+ } else {
+ pmpeg->base.context_new = nv40_mpeg_context_new;
+ pmpeg->base.context_del = nv40_mpeg_context_del;
+ }
+ pmpeg->base.object_new = nv31_mpeg_object_new;
/* ISR vector, PMC_ENABLE bit, and TILE regs are shared between
* all VPE engines, for this driver's purposes the PMPEG engine
* will be treated as the "master" and handle the global VPE
* bits too
*/
- pmpeg->base.set_tile_region = nv40_vpe_set_tile_region;
- nouveau_irq_register(dev, 0, nv40_vpe_isr);
+ pmpeg->base.set_tile_region = nv31_vpe_set_tile_region;
+ nouveau_irq_register(dev, 0, nv31_vpe_isr);
NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base);
NVOBJ_CLASS(dev, 0x3174, MPEG);
- NVOBJ_MTHD (dev, 0x3174, 0x0190, nv40_mpeg_mthd_dma);
- NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv40_mpeg_mthd_dma);
- NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv40_mpeg_mthd_dma);
+ NVOBJ_MTHD (dev, 0x3174, 0x0190, nv31_mpeg_mthd_dma);
+ NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv31_mpeg_mthd_dma);
+ NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv31_mpeg_mthd_dma);
#if 0
NVOBJ_ENGINE_ADD(dev, ME, &pme->base);
diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c
index 5beb01b8ace1..ba14a93d8afa 100644
--- a/drivers/gpu/drm/nouveau/nv40_graph.c
+++ b/drivers/gpu/drm/nouveau/nv40_graph.c
@@ -35,89 +35,6 @@ struct nv40_graph_engine {
u32 grctx_size;
};
-static struct nouveau_channel *
-nv40_graph_channel(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj *grctx;
- uint32_t inst;
- int i;
-
- inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
- if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
- return NULL;
- inst = (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) << 4;
-
- for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
- if (!dev_priv->channels.ptr[i])
- continue;
-
- grctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_GR];
- if (grctx && grctx->pinst == inst)
- return dev_priv->channels.ptr[i];
- }
-
- return NULL;
-}
-
-static int
-nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save)
-{
- uint32_t old_cp, tv = 1000, tmp;
- int i;
-
- old_cp = nv_rd32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER);
- nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
-
- tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310);
- tmp |= save ? NV40_PGRAPH_CTXCTL_0310_XFER_SAVE :
- NV40_PGRAPH_CTXCTL_0310_XFER_LOAD;
- nv_wr32(dev, NV40_PGRAPH_CTXCTL_0310, tmp);
-
- tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0304);
- tmp |= NV40_PGRAPH_CTXCTL_0304_XFER_CTX;
- nv_wr32(dev, NV40_PGRAPH_CTXCTL_0304, tmp);
-
- nouveau_wait_for_idle(dev);
-
- for (i = 0; i < tv; i++) {
- if (nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C) == 0)
- break;
- }
-
- nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, old_cp);
-
- if (i == tv) {
- uint32_t ucstat = nv_rd32(dev, NV40_PGRAPH_CTXCTL_UCODE_STAT);
- NV_ERROR(dev, "Failed: Instance=0x%08x Save=%d\n", inst, save);
- NV_ERROR(dev, "IP: 0x%02x, Opcode: 0x%08x\n",
- ucstat >> NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT,
- ucstat & NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK);
- NV_ERROR(dev, "0x40030C = 0x%08x\n",
- nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C));
- return -EBUSY;
- }
-
- return 0;
-}
-
-static int
-nv40_graph_unload_context(struct drm_device *dev)
-{
- uint32_t inst;
- int ret;
-
- inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
- if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
- return 0;
- inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE;
-
- ret = nv40_graph_transfer_context(dev, inst, 1);
-
- nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst);
- return ret;
-}
-
static int
nv40_graph_context_new(struct nouveau_channel *chan, int engine)
{
@@ -163,16 +80,16 @@ nv40_graph_context_del(struct nouveau_channel *chan, int engine)
struct nouveau_gpuobj *grctx = chan->engctx[engine];
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 inst = 0x01000000 | (grctx->pinst >> 4);
unsigned long flags;
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
- nv04_graph_fifo_access(dev, false);
-
- /* Unload the context if it's the currently active one */
- if (nv40_graph_channel(dev) == chan)
- nv40_graph_unload_context(dev);
-
- nv04_graph_fifo_access(dev, true);
+ nv_mask(dev, 0x400720, 0x00000000, 0x00000001);
+ if (nv_rd32(dev, 0x40032c) == inst)
+ nv_mask(dev, 0x40032c, 0x01000000, 0x00000000);
+ if (nv_rd32(dev, 0x400330) == inst)
+ nv_mask(dev, 0x400330, 0x01000000, 0x00000000);
+ nv_mask(dev, 0x400720, 0x00000001, 0x00000001);
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
/* Free the context resources */
@@ -429,9 +346,20 @@ nv40_graph_init(struct drm_device *dev, int engine)
}
static int
-nv40_graph_fini(struct drm_device *dev, int engine)
+nv40_graph_fini(struct drm_device *dev, int engine, bool suspend)
{
- nv40_graph_unload_context(dev);
+ u32 inst = nv_rd32(dev, 0x40032c);
+ if (inst & 0x01000000) {
+ nv_wr32(dev, 0x400720, 0x00000000);
+ nv_wr32(dev, 0x400784, inst);
+ nv_mask(dev, 0x400310, 0x00000020, 0x00000020);
+ nv_mask(dev, 0x400304, 0x00000001, 0x00000001);
+ if (!nv_wait(dev, 0x400300, 0x00000001, 0x00000000)) {
+ u32 insn = nv_rd32(dev, 0x400308);
+ NV_ERROR(dev, "PGRAPH: ctxprog timeout 0x%08x\n", insn);
+ }
+ nv_mask(dev, 0x40032c, 0x01000000, 0x00000000);
+ }
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c
new file mode 100644
index 000000000000..e676b0d53478
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv40_pm.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_bios.h"
+#include "nouveau_pm.h"
+#include "nouveau_hw.h"
+
+#define min2(a,b) ((a) < (b) ? (a) : (b))
+
+static u32
+read_pll_1(struct drm_device *dev, u32 reg)
+{
+ u32 ctrl = nv_rd32(dev, reg + 0x00);
+ int P = (ctrl & 0x00070000) >> 16;
+ int N = (ctrl & 0x0000ff00) >> 8;
+ int M = (ctrl & 0x000000ff) >> 0;
+ u32 ref = 27000, clk = 0;
+
+ if (ctrl & 0x80000000)
+ clk = ref * N / M;
+
+ return clk >> P;
+}
+
+static u32
+read_pll_2(struct drm_device *dev, u32 reg)
+{
+ u32 ctrl = nv_rd32(dev, reg + 0x00);
+ u32 coef = nv_rd32(dev, reg + 0x04);
+ int N2 = (coef & 0xff000000) >> 24;
+ int M2 = (coef & 0x00ff0000) >> 16;
+ int N1 = (coef & 0x0000ff00) >> 8;
+ int M1 = (coef & 0x000000ff) >> 0;
+ int P = (ctrl & 0x00070000) >> 16;
+ u32 ref = 27000, clk = 0;
+
+ if ((ctrl & 0x80000000) && M1) {
+ clk = ref * N1 / M1;
+ if ((ctrl & 0x40000100) == 0x40000000) {
+ if (M2)
+ clk = clk * N2 / M2;
+ else
+ clk = 0;
+ }
+ }
+
+ return clk >> P;
+}
+
+static u32
+read_clk(struct drm_device *dev, u32 src)
+{
+ switch (src) {
+ case 3:
+ return read_pll_2(dev, 0x004000);
+ case 2:
+ return read_pll_1(dev, 0x004008);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int
+nv40_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
+{
+ u32 ctrl = nv_rd32(dev, 0x00c040);
+
+ perflvl->core = read_clk(dev, (ctrl & 0x00000003) >> 0);
+ perflvl->shader = read_clk(dev, (ctrl & 0x00000030) >> 4);
+ perflvl->memory = read_pll_2(dev, 0x4020);
+ return 0;
+}
+
+struct nv40_pm_state {
+ u32 ctrl;
+ u32 npll_ctrl;
+ u32 npll_coef;
+ u32 spll;
+ u32 mpll_ctrl;
+ u32 mpll_coef;
+};
+
+static int
+nv40_calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
+ u32 clk, int *N1, int *M1, int *N2, int *M2, int *log2P)
+{
+ struct nouveau_pll_vals coef;
+ int ret;
+
+ ret = get_pll_limits(dev, reg, pll);
+ if (ret)
+ return ret;
+
+ if (clk < pll->vco1.maxfreq)
+ pll->vco2.maxfreq = 0;
+
+ ret = nouveau_calc_pll_mnp(dev, pll, clk, &coef);
+ if (ret == 0)
+ return -ERANGE;
+
+ *N1 = coef.N1;
+ *M1 = coef.M1;
+ if (N2 && M2) {
+ if (pll->vco2.maxfreq) {
+ *N2 = coef.N2;
+ *M2 = coef.M2;
+ } else {
+ *N2 = 1;
+ *M2 = 1;
+ }
+ }
+ *log2P = coef.log2P;
+ return 0;
+}
+
+void *
+nv40_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
+{
+ struct nv40_pm_state *info;
+ struct pll_lims pll;
+ int N1, N2, M1, M2, log2P;
+ int ret;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ /* core/geometric clock */
+ ret = nv40_calc_pll(dev, 0x004000, &pll, perflvl->core,
+ &N1, &M1, &N2, &M2, &log2P);
+ if (ret < 0)
+ goto out;
+
+ if (N2 == M2) {
+ info->npll_ctrl = 0x80000100 | (log2P << 16);
+ info->npll_coef = (N1 << 8) | M1;
+ } else {
+ info->npll_ctrl = 0xc0000000 | (log2P << 16);
+ info->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
+ }
+
+ /* use the second PLL for shader/rop clock, if it differs from core */
+ if (perflvl->shader && perflvl->shader != perflvl->core) {
+ ret = nv40_calc_pll(dev, 0x004008, &pll, perflvl->shader,
+ &N1, &M1, NULL, NULL, &log2P);
+ if (ret < 0)
+ goto out;
+
+ info->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1;
+ info->ctrl = 0x00000223;
+ } else {
+ info->spll = 0x00000000;
+ info->ctrl = 0x00000333;
+ }
+
+ /* memory clock */
+ if (!perflvl->memory) {
+ info->mpll_ctrl = 0x00000000;
+ goto out;
+ }
+
+ ret = nv40_calc_pll(dev, 0x004020, &pll, perflvl->memory,
+ &N1, &M1, &N2, &M2, &log2P);
+ if (ret < 0)
+ goto out;
+
+ info->mpll_ctrl = 0x80000000 | (log2P << 16);
+ info->mpll_ctrl |= min2(pll.log2p_bias + log2P, pll.max_log2p) << 20;
+ if (N2 == M2) {
+ info->mpll_ctrl |= 0x00000100;
+ info->mpll_coef = (N1 << 8) | M1;
+ } else {
+ info->mpll_ctrl |= 0x40000000;
+ info->mpll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
+ }
+
+out:
+ if (ret < 0) {
+ kfree(info);
+ info = ERR_PTR(ret);
+ }
+ return info;
+}
+
+static bool
+nv40_pm_gr_idle(void *data)
+{
+ struct drm_device *dev = data;
+
+ if ((nv_rd32(dev, 0x400760) & 0x000000f0) >> 4 !=
+ (nv_rd32(dev, 0x400760) & 0x0000000f))
+ return false;
+
+ if (nv_rd32(dev, 0x400700))
+ return false;
+
+ return true;
+}
+
+void
+nv40_pm_clocks_set(struct drm_device *dev, void *pre_state)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv40_pm_state *info = pre_state;
+ unsigned long flags;
+ struct bit_entry M;
+ u32 crtc_mask = 0;
+ u8 sr1[2];
+ int i;
+
+ /* determine which CRTCs are active, fetch VGA_SR1 for each */
+ for (i = 0; i < 2; i++) {
+ u32 vbl = nv_rd32(dev, 0x600808 + (i * 0x2000));
+ u32 cnt = 0;
+ do {
+ if (vbl != nv_rd32(dev, 0x600808 + (i * 0x2000))) {
+ nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01);
+ sr1[i] = nv_rd08(dev, 0x0c03c5 + (i * 0x2000));
+ if (!(sr1[i] & 0x20))
+ crtc_mask |= (1 << i);
+ break;
+ }
+ udelay(1);
+ } while (cnt++ < 32);
+ }
+
+ /* halt and idle engines */
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+ nv_mask(dev, 0x002500, 0x00000001, 0x00000000);
+ if (!nv_wait(dev, 0x002500, 0x00000010, 0x00000000))
+ goto resume;
+ nv_mask(dev, 0x003220, 0x00000001, 0x00000000);
+ if (!nv_wait(dev, 0x003220, 0x00000010, 0x00000000))
+ goto resume;
+ nv_mask(dev, 0x003200, 0x00000001, 0x00000000);
+ nv04_fifo_cache_pull(dev, false);
+
+ if (!nv_wait_cb(dev, nv40_pm_gr_idle, dev))
+ goto resume;
+
+ /* set engine clocks */
+ nv_mask(dev, 0x00c040, 0x00000333, 0x00000000);
+ nv_wr32(dev, 0x004004, info->npll_coef);
+ nv_mask(dev, 0x004000, 0xc0070100, info->npll_ctrl);
+ nv_mask(dev, 0x004008, 0xc007ffff, info->spll);
+ mdelay(5);
+ nv_mask(dev, 0x00c040, 0x00000333, info->ctrl);
+
+ if (!info->mpll_ctrl)
+ goto resume;
+
+ /* wait for vblank start on active crtcs, disable memory access */
+ for (i = 0; i < 2; i++) {
+ if (!(crtc_mask & (1 << i)))
+ continue;
+ nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00000000);
+ nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
+ nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01);
+ nv_wr08(dev, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20);
+ }
+
+ /* prepare ram for reclocking */
+ nv_wr32(dev, 0x1002d4, 0x00000001); /* precharge */
+ nv_wr32(dev, 0x1002d0, 0x00000001); /* refresh */
+ nv_wr32(dev, 0x1002d0, 0x00000001); /* refresh */
+ nv_mask(dev, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */
+ nv_wr32(dev, 0x1002dc, 0x00000001); /* enable self-refresh */
+
+ /* change the PLL of each memory partition */
+ nv_mask(dev, 0x00c040, 0x0000c000, 0x00000000);
+ switch (dev_priv->chipset) {
+ case 0x40:
+ case 0x45:
+ case 0x41:
+ case 0x42:
+ case 0x47:
+ nv_mask(dev, 0x004044, 0xc0771100, info->mpll_ctrl);
+ nv_mask(dev, 0x00402c, 0xc0771100, info->mpll_ctrl);
+ nv_wr32(dev, 0x004048, info->mpll_coef);
+ nv_wr32(dev, 0x004030, info->mpll_coef);
+ case 0x43:
+ case 0x49:
+ case 0x4b:
+ nv_mask(dev, 0x004038, 0xc0771100, info->mpll_ctrl);
+ nv_wr32(dev, 0x00403c, info->mpll_coef);
+ default:
+ nv_mask(dev, 0x004020, 0xc0771100, info->mpll_ctrl);
+ nv_wr32(dev, 0x004024, info->mpll_coef);
+ break;
+ }
+ udelay(100);
+ nv_mask(dev, 0x00c040, 0x0000c000, 0x0000c000);
+
+ /* re-enable normal operation of memory controller */
+ nv_wr32(dev, 0x1002dc, 0x00000000);
+ nv_mask(dev, 0x100210, 0x80000000, 0x80000000);
+ udelay(100);
+
+ /* execute memory reset script from vbios */
+ if (!bit_table(dev, 'M', &M))
+ nouveau_bios_init_exec(dev, ROM16(M.data[0]));
+
+ /* make sure we're in vblank (hopefully the same one as before), and
+ * then re-enable crtc memory access
+ */
+ for (i = 0; i < 2; i++) {
+ if (!(crtc_mask & (1 << i)))
+ continue;
+ nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
+ nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01);
+ nv_wr08(dev, 0x0c03c5 + (i * 0x2000), sr1[i]);
+ }
+
+ /* resume engines */
+resume:
+ nv_wr32(dev, 0x003250, 0x00000001);
+ nv_mask(dev, 0x003220, 0x00000001, 0x00000001);
+ nv_wr32(dev, 0x003200, 0x00000001);
+ nv_wr32(dev, 0x002500, 0x00000001);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
+ kfree(info);
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index ebabacf38da9..882080e0b4f5 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -104,7 +104,7 @@ nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked)
OUT_RING(evo, nv_crtc->lut.depth == 8 ?
NV50_EVO_CRTC_CLUT_MODE_OFF :
NV50_EVO_CRTC_CLUT_MODE_ON);
- OUT_RING(evo, (nv_crtc->lut.nvbo->bo.mem.start << PAGE_SHIFT) >> 8);
+ OUT_RING(evo, nv_crtc->lut.nvbo->bo.offset >> 8);
if (dev_priv->chipset != 0x50) {
BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
OUT_RING(evo, NvEvoVRAM);
@@ -329,8 +329,6 @@ nv50_crtc_destroy(struct drm_crtc *crtc)
drm_crtc_cleanup(&nv_crtc->base);
- nv50_cursor_fini(nv_crtc);
-
nouveau_bo_unmap(nv_crtc->lut.nvbo);
nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
nouveau_bo_unmap(nv_crtc->cursor.nvbo);
@@ -372,7 +370,7 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
nouveau_bo_unmap(cursor);
- nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.mem.start << PAGE_SHIFT);
+ nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset);
nv_crtc->cursor.show(nv_crtc, true);
out:
@@ -519,12 +517,18 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
struct drm_device *dev = nv_crtc->base.dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = nv50_display(dev)->master;
- struct drm_framebuffer *drm_fb = nv_crtc->base.fb;
- struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
+ struct drm_framebuffer *drm_fb;
+ struct nouveau_framebuffer *fb;
int ret;
NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
+ /* no fb bound */
+ if (!atomic && !crtc->fb) {
+ NV_DEBUG_KMS(dev, "No FB bound\n");
+ return 0;
+ }
+
/* If atomic, we want to switch to the fb we were passed, so
* now we update pointers to do that. (We don't pin; just
* assume we're already pinned and update the base address.)
@@ -533,6 +537,8 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
drm_fb = passed_fb;
fb = nouveau_framebuffer(passed_fb);
} else {
+ drm_fb = crtc->fb;
+ fb = nouveau_framebuffer(crtc->fb);
/* If not atomic, we can go ahead and pin, and unpin the
* old fb we were passed.
*/
@@ -546,7 +552,7 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
}
}
- nv_crtc->fb.offset = fb->nvbo->bo.mem.start << PAGE_SHIFT;
+ nv_crtc->fb.offset = fb->nvbo->bo.offset;
nv_crtc->fb.tile_flags = nouveau_bo_tile_layout(fb->nvbo);
nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8;
if (!nv_crtc->fb.blanked && dev_priv->chipset != 0x50) {
@@ -747,7 +753,7 @@ nv50_crtc_create(struct drm_device *dev, int index)
}
nv_crtc->lut.depth = 0;
- ret = nouveau_bo_new(dev, NULL, 4096, 0x100, TTM_PL_FLAG_VRAM,
+ ret = nouveau_bo_new(dev, 4096, 0x100, TTM_PL_FLAG_VRAM,
0, 0x0000, &nv_crtc->lut.nvbo);
if (!ret) {
ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM);
@@ -773,7 +779,7 @@ nv50_crtc_create(struct drm_device *dev, int index)
drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs);
drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
- ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
+ ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
0, 0x0000, &nv_crtc->cursor.nvbo);
if (!ret) {
ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c
index 9752c35bb84b..adfc9b607a50 100644
--- a/drivers/gpu/drm/nouveau/nv50_cursor.c
+++ b/drivers/gpu/drm/nouveau/nv50_cursor.c
@@ -137,21 +137,3 @@ nv50_cursor_init(struct nouveau_crtc *nv_crtc)
nv_crtc->cursor.show = nv50_cursor_show;
return 0;
}
-
-void
-nv50_cursor_fini(struct nouveau_crtc *nv_crtc)
-{
- struct drm_device *dev = nv_crtc->base.dev;
- int idx = nv_crtc->index;
-
- NV_DEBUG_KMS(dev, "\n");
-
- nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0);
- if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx),
- NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
- NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
- NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
- nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx)));
- }
-}
-
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 08da478ba544..d23ca00e7d62 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -247,6 +247,16 @@ static int nv50_display_disable(struct drm_device *dev)
}
}
+ for (i = 0; i < 2; i++) {
+ nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0);
+ if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
+ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
+ NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
+ NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
+ nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)));
+ }
+ }
+
nv50_evo_fini(dev);
for (i = 0; i < 3; i++) {
@@ -286,23 +296,6 @@ int nv50_display_create(struct drm_device *dev)
return -ENOMEM;
dev_priv->engine.display.priv = priv;
- /* init basic kernel modesetting */
- drm_mode_config_init(dev);
-
- /* Initialise some optional connector properties. */
- drm_mode_create_scaling_mode_property(dev);
- drm_mode_create_dithering_property(dev);
-
- dev->mode_config.min_width = 0;
- dev->mode_config.min_height = 0;
-
- dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
-
- dev->mode_config.max_width = 8192;
- dev->mode_config.max_height = 8192;
-
- dev->mode_config.fb_base = dev_priv->fb_phys;
-
/* Create CRTC objects */
for (i = 0; i < 2; i++)
nv50_crtc_create(dev, i);
@@ -364,8 +357,6 @@ nv50_display_destroy(struct drm_device *dev)
NV_DEBUG_KMS(dev, "\n");
- drm_mode_config_cleanup(dev);
-
nv50_display_disable(dev);
nouveau_irq_unregister(dev, 26);
kfree(disp);
@@ -415,8 +406,6 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
/* synchronise with the rendering channel, if necessary */
if (likely(chan)) {
- u64 offset = dispc->sem.bo->vma.offset + dispc->sem.offset;
-
ret = RING_SPACE(chan, 10);
if (ret) {
WIND_RING(evo);
@@ -438,6 +427,8 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
else
OUT_RING (chan, chan->vram_handle);
} else {
+ u64 offset = chan->dispc_vma[nv_crtc->index].offset;
+ offset += dispc->sem.offset;
BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
OUT_RING (chan, upper_32_bits(offset));
OUT_RING (chan, lower_32_bits(offset));
@@ -484,7 +475,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
OUT_RING (evo, 0x00000000);
OUT_RING (evo, 0x00000000);
BEGIN_RING(evo, 0, 0x0800, 5);
- OUT_RING (evo, (nv_fb->nvbo->bo.mem.start << PAGE_SHIFT) >> 8);
+ OUT_RING (evo, nv_fb->nvbo->bo.offset >> 8);
OUT_RING (evo, 0);
OUT_RING (evo, (fb->height << 16) | fb->width);
OUT_RING (evo, nv_fb->r_pitch);
@@ -698,7 +689,7 @@ nv50_display_unk10_handler(struct drm_device *dev)
struct dcb_entry *dcb = &dev_priv->vbios.dcb.entry[i];
if (dcb->type == type && (dcb->or & (1 << or))) {
- nouveau_bios_run_display_table(dev, dcb, 0, -1);
+ nouveau_bios_run_display_table(dev, 0, -1, dcb, -1);
disp->irq.dcb = dcb;
goto ack;
}
@@ -711,37 +702,6 @@ ack:
}
static void
-nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb)
-{
- int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1);
- struct drm_encoder *encoder;
- uint32_t tmp, unk0 = 0, unk1 = 0;
-
- if (dcb->type != OUTPUT_DP)
- return;
-
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-
- if (nv_encoder->dcb == dcb) {
- unk0 = nv_encoder->dp.unk0;
- unk1 = nv_encoder->dp.unk1;
- break;
- }
- }
-
- if (unk0 || unk1) {
- tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
- tmp &= 0xfffffe03;
- nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp | unk0);
-
- tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link));
- tmp &= 0xfef080c0;
- nv_wr32(dev, NV50_SOR_DP_UNK128(or, link), tmp | unk1);
- }
-}
-
-static void
nv50_display_unk20_handler(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -753,7 +713,7 @@ nv50_display_unk20_handler(struct drm_device *dev)
NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30);
dcb = disp->irq.dcb;
if (dcb) {
- nouveau_bios_run_display_table(dev, dcb, 0, -2);
+ nouveau_bios_run_display_table(dev, 0, -2, dcb, -1);
disp->irq.dcb = NULL;
}
@@ -837,9 +797,15 @@ nv50_display_unk20_handler(struct drm_device *dev)
}
script = nv50_display_script_select(dev, dcb, mc, pclk);
- nouveau_bios_run_display_table(dev, dcb, script, pclk);
+ nouveau_bios_run_display_table(dev, script, pclk, dcb, -1);
- nv50_display_unk20_dp_hack(dev, dcb);
+ if (type == OUTPUT_DP) {
+ int link = !(dcb->dpconf.sor.link & 1);
+ if ((mc & 0x000f0000) == 0x00020000)
+ nouveau_dp_tu_update(dev, or, link, pclk, 18);
+ else
+ nouveau_dp_tu_update(dev, or, link, pclk, 24);
+ }
if (dcb->type != OUTPUT_ANALOG) {
tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or));
@@ -904,7 +870,7 @@ nv50_display_unk40_handler(struct drm_device *dev)
if (!dcb)
goto ack;
- nouveau_bios_run_display_table(dev, dcb, script, -pclk);
+ nouveau_bios_run_display_table(dev, script, -pclk, dcb, -1);
nv50_display_unk40_dp_set_tmds(dev, dcb);
ack:
diff --git a/drivers/gpu/drm/nouveau/nv50_evo.c b/drivers/gpu/drm/nouveau/nv50_evo.c
index c8e83c1a4de8..c99d9751880c 100644
--- a/drivers/gpu/drm/nouveau/nv50_evo.c
+++ b/drivers/gpu/drm/nouveau/nv50_evo.c
@@ -38,6 +38,7 @@ nv50_evo_channel_del(struct nouveau_channel **pevo)
return;
*pevo = NULL;
+ nouveau_ramht_ref(NULL, &evo->ramht, evo);
nouveau_gpuobj_channel_takedown(evo);
nouveau_bo_unmap(evo->pushbuf_bo);
nouveau_bo_ref(NULL, &evo->pushbuf_bo);
@@ -116,7 +117,7 @@ nv50_evo_channel_new(struct drm_device *dev, int chid,
evo->user_get = 4;
evo->user_put = 0;
- ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0,
+ ret = nouveau_bo_new(dev, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0,
&evo->pushbuf_bo);
if (ret == 0)
ret = nouveau_bo_pin(evo->pushbuf_bo, TTM_PL_FLAG_VRAM);
@@ -153,7 +154,7 @@ nv50_evo_channel_init(struct nouveau_channel *evo)
{
struct drm_device *dev = evo->dev;
int id = evo->id, ret, i;
- u64 pushbuf = evo->pushbuf_bo->bo.mem.start << PAGE_SHIFT;
+ u64 pushbuf = evo->pushbuf_bo->bo.offset;
u32 tmp;
tmp = nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id));
@@ -331,16 +332,15 @@ nv50_evo_create(struct drm_device *dev)
if (ret)
goto err;
- ret = nouveau_bo_new(dev, NULL, 4096, 0x1000, TTM_PL_FLAG_VRAM,
+ ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
0, 0x0000, &dispc->sem.bo);
if (!ret) {
- offset = dispc->sem.bo->bo.mem.start << PAGE_SHIFT;
-
ret = nouveau_bo_pin(dispc->sem.bo, TTM_PL_FLAG_VRAM);
if (!ret)
ret = nouveau_bo_map(dispc->sem.bo);
if (ret)
nouveau_bo_ref(NULL, &dispc->sem.bo);
+ offset = dispc->sem.bo->bo.offset;
}
if (ret)
diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
index 791ded1c5c6d..dc75a7206524 100644
--- a/drivers/gpu/drm/nouveau/nv50_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
@@ -159,7 +159,7 @@ nv50_fbcon_accel_init(struct fb_info *info)
struct drm_device *dev = nfbdev->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
- struct nouveau_bo *nvbo = nfbdev->nouveau_fb.nvbo;
+ struct nouveau_framebuffer *fb = &nfbdev->nouveau_fb;
int ret, format;
switch (info->var.bits_per_pixel) {
@@ -247,8 +247,8 @@ nv50_fbcon_accel_init(struct fb_info *info)
OUT_RING(chan, info->fix.line_length);
OUT_RING(chan, info->var.xres_virtual);
OUT_RING(chan, info->var.yres_virtual);
- OUT_RING(chan, upper_32_bits(nvbo->vma.offset));
- OUT_RING(chan, lower_32_bits(nvbo->vma.offset));
+ OUT_RING(chan, upper_32_bits(fb->vma.offset));
+ OUT_RING(chan, lower_32_bits(fb->vma.offset));
BEGIN_RING(chan, NvSub2D, 0x0230, 2);
OUT_RING(chan, format);
OUT_RING(chan, 1);
@@ -256,8 +256,8 @@ nv50_fbcon_accel_init(struct fb_info *info)
OUT_RING(chan, info->fix.line_length);
OUT_RING(chan, info->var.xres_virtual);
OUT_RING(chan, info->var.yres_virtual);
- OUT_RING(chan, upper_32_bits(nvbo->vma.offset));
- OUT_RING(chan, lower_32_bits(nvbo->vma.offset));
+ OUT_RING(chan, upper_32_bits(fb->vma.offset));
+ OUT_RING(chan, lower_32_bits(fb->vma.offset));
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c
index d4f4206dad7e..793a5ccca121 100644
--- a/drivers/gpu/drm/nouveau/nv50_gpio.c
+++ b/drivers/gpu/drm/nouveau/nv50_gpio.c
@@ -98,6 +98,37 @@ nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
}
int
+nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag)
+{
+ struct dcb_gpio_entry *gpio;
+ u32 v;
+
+ gpio = nouveau_bios_gpio_entry(dev, tag);
+ if (!gpio)
+ return -ENOENT;
+
+ v = nv_rd32(dev, 0x00d610 + (gpio->line * 4));
+ v &= 0x00004000;
+ return (!!v == (gpio->state[1] & 1));
+}
+
+int
+nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
+{
+ struct dcb_gpio_entry *gpio;
+ u32 v;
+
+ gpio = nouveau_bios_gpio_entry(dev, tag);
+ if (!gpio)
+ return -ENOENT;
+
+ v = gpio->state[state] ^ 2;
+
+ nv_mask(dev, 0x00d610 + (gpio->line * 4), 0x00003000, v << 12);
+ return 0;
+}
+
+int
nv50_gpio_irq_register(struct drm_device *dev, enum dcb_gpio_tag tag,
void (*handler)(void *, int), void *data)
{
diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
index e25cbb46789a..ac601f7c4e1a 100644
--- a/drivers/gpu/drm/nouveau/nv50_graph.c
+++ b/drivers/gpu/drm/nouveau/nv50_graph.c
@@ -31,7 +31,6 @@
#include "nouveau_grctx.h"
#include "nouveau_dma.h"
#include "nouveau_vm.h"
-#include "nouveau_ramht.h"
#include "nv50_evo.h"
struct nv50_graph_engine {
@@ -121,71 +120,62 @@ nv50_graph_unload_context(struct drm_device *dev)
return 0;
}
-static void
-nv50_graph_init_reset(struct drm_device *dev)
-{
- uint32_t pmc_e = NV_PMC_ENABLE_PGRAPH | (1 << 21);
-
- NV_DEBUG(dev, "\n");
-
- nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e);
- nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e);
-}
-
-static void
-nv50_graph_init_intr(struct drm_device *dev)
-{
- NV_DEBUG(dev, "\n");
-
- nv_wr32(dev, NV03_PGRAPH_INTR, 0xffffffff);
- nv_wr32(dev, 0x400138, 0xffffffff);
- nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xffffffff);
-}
-
-static void
-nv50_graph_init_regs__nv(struct drm_device *dev)
+static int
+nv50_graph_init(struct drm_device *dev, int engine)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t units = nv_rd32(dev, 0x1540);
+ struct nv50_graph_engine *pgraph = nv_engine(dev, engine);
+ u32 units = nv_rd32(dev, 0x001540);
int i;
NV_DEBUG(dev, "\n");
+ /* master reset */
+ nv_mask(dev, 0x000200, 0x00201000, 0x00000000);
+ nv_mask(dev, 0x000200, 0x00201000, 0x00201000);
+ nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */
+
+ /* reset/enable traps and interrupts */
nv_wr32(dev, 0x400804, 0xc0000000);
nv_wr32(dev, 0x406800, 0xc0000000);
nv_wr32(dev, 0x400c04, 0xc0000000);
nv_wr32(dev, 0x401800, 0xc0000000);
nv_wr32(dev, 0x405018, 0xc0000000);
nv_wr32(dev, 0x402000, 0xc0000000);
-
for (i = 0; i < 16; i++) {
- if (units & 1 << i) {
- if (dev_priv->chipset < 0xa0) {
- nv_wr32(dev, 0x408900 + (i << 12), 0xc0000000);
- nv_wr32(dev, 0x408e08 + (i << 12), 0xc0000000);
- nv_wr32(dev, 0x408314 + (i << 12), 0xc0000000);
- } else {
- nv_wr32(dev, 0x408600 + (i << 11), 0xc0000000);
- nv_wr32(dev, 0x408708 + (i << 11), 0xc0000000);
- nv_wr32(dev, 0x40831c + (i << 11), 0xc0000000);
- }
+ if (!(units & (1 << i)))
+ continue;
+
+ if (dev_priv->chipset < 0xa0) {
+ nv_wr32(dev, 0x408900 + (i << 12), 0xc0000000);
+ nv_wr32(dev, 0x408e08 + (i << 12), 0xc0000000);
+ nv_wr32(dev, 0x408314 + (i << 12), 0xc0000000);
+ } else {
+ nv_wr32(dev, 0x408600 + (i << 11), 0xc0000000);
+ nv_wr32(dev, 0x408708 + (i << 11), 0xc0000000);
+ nv_wr32(dev, 0x40831c + (i << 11), 0xc0000000);
}
}
nv_wr32(dev, 0x400108, 0xffffffff);
-
- nv_wr32(dev, 0x400824, 0x00004000);
+ nv_wr32(dev, 0x400138, 0xffffffff);
+ nv_wr32(dev, 0x400100, 0xffffffff);
+ nv_wr32(dev, 0x40013c, 0xffffffff);
nv_wr32(dev, 0x400500, 0x00010001);
-}
-
-static void
-nv50_graph_init_zcull(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- int i;
-
- NV_DEBUG(dev, "\n");
+ /* upload context program, initialise ctxctl defaults */
+ nv_wr32(dev, 0x400324, 0x00000000);
+ for (i = 0; i < pgraph->ctxprog_size; i++)
+ nv_wr32(dev, 0x400328, pgraph->ctxprog[i]);
+ nv_wr32(dev, 0x400824, 0x00000000);
+ nv_wr32(dev, 0x400828, 0x00000000);
+ nv_wr32(dev, 0x40082c, 0x00000000);
+ nv_wr32(dev, 0x400830, 0x00000000);
+ nv_wr32(dev, 0x400724, 0x00000000);
+ nv_wr32(dev, 0x40032c, 0x00000000);
+ nv_wr32(dev, 0x400320, 4); /* CTXCTL_CMD = NEWCTXDMA */
+
+ /* some unknown zcull magic */
switch (dev_priv->chipset & 0xf0) {
case 0x50:
case 0x80:
@@ -214,50 +204,18 @@ nv50_graph_init_zcull(struct drm_device *dev)
nv_wr32(dev, 0x402c28 + (i * 8), 0x00000000);
nv_wr32(dev, 0x402c2c + (i * 8), 0x00000000);
}
-}
-
-static int
-nv50_graph_init_ctxctl(struct drm_device *dev)
-{
- struct nv50_graph_engine *pgraph = nv_engine(dev, NVOBJ_ENGINE_GR);
- int i;
-
- NV_DEBUG(dev, "\n");
-
- nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
- for (i = 0; i < pgraph->ctxprog_size; i++)
- nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, pgraph->ctxprog[i]);
-
- nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */
- nv_wr32(dev, 0x400320, 4);
- nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0);
- nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, 0);
- return 0;
-}
-
-static int
-nv50_graph_init(struct drm_device *dev, int engine)
-{
- int ret;
- NV_DEBUG(dev, "\n");
-
- nv50_graph_init_reset(dev);
- nv50_graph_init_regs__nv(dev);
- nv50_graph_init_zcull(dev);
-
- ret = nv50_graph_init_ctxctl(dev);
- if (ret)
- return ret;
-
- nv50_graph_init_intr(dev);
return 0;
}
static int
-nv50_graph_fini(struct drm_device *dev, int engine)
+nv50_graph_fini(struct drm_device *dev, int engine, bool suspend)
{
- NV_DEBUG(dev, "\n");
+ nv_mask(dev, 0x400500, 0x00010001, 0x00000000);
+ if (!nv_wait(dev, 0x400700, ~0, 0) && suspend) {
+ nv_mask(dev, 0x400500, 0x00010001, 0x00010001);
+ return -EBUSY;
+ }
nv50_graph_unload_context(dev);
nv_wr32(dev, 0x40013c, 0x00000000);
return 0;
diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c
index de9abff12b90..4b46d6968566 100644
--- a/drivers/gpu/drm/nouveau/nv50_grctx.c
+++ b/drivers/gpu/drm/nouveau/nv50_grctx.c
@@ -40,6 +40,12 @@
#define CP_FLAG_UNK0B ((0 * 32) + 0xb)
#define CP_FLAG_UNK0B_CLEAR 0
#define CP_FLAG_UNK0B_SET 1
+#define CP_FLAG_XFER_SWITCH ((0 * 32) + 0xe)
+#define CP_FLAG_XFER_SWITCH_DISABLE 0
+#define CP_FLAG_XFER_SWITCH_ENABLE 1
+#define CP_FLAG_STATE ((0 * 32) + 0x1c)
+#define CP_FLAG_STATE_STOPPED 0
+#define CP_FLAG_STATE_RUNNING 1
#define CP_FLAG_UNK1D ((0 * 32) + 0x1d)
#define CP_FLAG_UNK1D_CLEAR 0
#define CP_FLAG_UNK1D_SET 1
@@ -194,6 +200,9 @@ nv50_grctx_init(struct nouveau_grctx *ctx)
"the devs.\n");
return -ENOSYS;
}
+
+ cp_set (ctx, STATE, RUNNING);
+ cp_set (ctx, XFER_SWITCH, ENABLE);
/* decide whether we're loading/unloading the context */
cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save);
cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save);
@@ -260,6 +269,8 @@ nv50_grctx_init(struct nouveau_grctx *ctx)
cp_name(ctx, cp_exit);
cp_set (ctx, USER_SAVE, NOT_PENDING);
cp_set (ctx, USER_LOAD, NOT_PENDING);
+ cp_set (ctx, XFER_SWITCH, DISABLE);
+ cp_set (ctx, STATE, STOPPED);
cp_out (ctx, CP_END);
ctx->ctxvals_pos += 0x400; /* padding... no idea why you need it */
@@ -590,7 +601,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
gr_def(ctx, offset + 0x1c, 0x00880000);
break;
case 0x86:
- gr_def(ctx, offset + 0x1c, 0x008c0000);
+ gr_def(ctx, offset + 0x1c, 0x018c0000);
break;
case 0x92:
case 0x96:
diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
index 4f95a1e5822e..a7c12c94a5a6 100644
--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
@@ -305,9 +305,9 @@ struct nv50_gpuobj_node {
u32 align;
};
-
int
-nv50_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align)
+nv50_instmem_get(struct nouveau_gpuobj *gpuobj, struct nouveau_channel *chan,
+ u32 size, u32 align)
{
struct drm_device *dev = gpuobj->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -336,7 +336,7 @@ nv50_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align)
if (!(gpuobj->flags & NVOBJ_FLAG_VM_USER))
flags |= NV_MEM_ACCESS_SYS;
- ret = nouveau_vm_get(dev_priv->chan_vm, size, 12, flags,
+ ret = nouveau_vm_get(chan->vm, size, 12, flags,
&node->chan_vma);
if (ret) {
vram->put(dev, &node->vram);
@@ -345,7 +345,7 @@ nv50_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align)
}
nouveau_vm_map(&node->chan_vma, node->vram);
- gpuobj->vinst = node->chan_vma.offset;
+ gpuobj->linst = node->chan_vma.offset;
}
gpuobj->size = size;
diff --git a/drivers/gpu/drm/nouveau/nv50_mpeg.c b/drivers/gpu/drm/nouveau/nv50_mpeg.c
index 1dc5913f78c5..b57a2d180ad2 100644
--- a/drivers/gpu/drm/nouveau/nv50_mpeg.c
+++ b/drivers/gpu/drm/nouveau/nv50_mpeg.c
@@ -160,7 +160,7 @@ nv50_mpeg_init(struct drm_device *dev, int engine)
}
static int
-nv50_mpeg_fini(struct drm_device *dev, int engine)
+nv50_mpeg_fini(struct drm_device *dev, int engine, bool suspend)
{
/*XXX: context save for s/r */
nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index 8a2810011bda..3d5a86b98282 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -115,15 +115,15 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
BIT_M.version == 1 && BIT_M.length >= 0x0b) {
script = ROM16(BIT_M.data[0x05]);
if (script)
- nouveau_bios_run_init_table(dev, script, NULL);
+ nouveau_bios_run_init_table(dev, script, NULL, -1);
script = ROM16(BIT_M.data[0x07]);
if (script)
- nouveau_bios_run_init_table(dev, script, NULL);
+ nouveau_bios_run_init_table(dev, script, NULL, -1);
script = ROM16(BIT_M.data[0x09]);
if (script)
- nouveau_bios_run_init_table(dev, script, NULL);
+ nouveau_bios_run_init_table(dev, script, NULL, -1);
- nouveau_bios_run_init_table(dev, perflvl->memscript, NULL);
+ nouveau_bios_run_init_table(dev, perflvl->memscript, NULL, -1);
}
if (state->type == PLL_MEMORY) {
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
index c25c59386420..2633aa8554eb 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -124,7 +124,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
if (mode == DRM_MODE_DPMS_ON) {
u8 status = DP_SET_POWER_D0;
nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
- nouveau_dp_link_train(encoder);
+ nouveau_dp_link_train(encoder, nv_encoder->dp.datarate);
} else {
u8 status = DP_SET_POWER_D3;
nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
@@ -187,14 +187,13 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
+ struct nouveau_connector *nv_connector;
uint32_t mode_ctl = 0;
int ret;
NV_DEBUG_KMS(dev, "or %d type %d -> crtc %d\n",
nv_encoder->or, nv_encoder->dcb->type, crtc->index);
- nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
-
switch (nv_encoder->dcb->type) {
case OUTPUT_TMDS:
if (nv_encoder->dcb->sorconf.link & 1) {
@@ -206,7 +205,15 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
mode_ctl = 0x0200;
break;
case OUTPUT_DP:
- mode_ctl |= (nv_encoder->dp.mc_unknown << 16);
+ nv_connector = nouveau_encoder_connector_get(nv_encoder);
+ if (nv_connector && nv_connector->base.display_info.bpc == 6) {
+ nv_encoder->dp.datarate = crtc->mode->clock * 18 / 8;
+ mode_ctl |= 0x00020000;
+ } else {
+ nv_encoder->dp.datarate = crtc->mode->clock * 24 / 8;
+ mode_ctl |= 0x00050000;
+ }
+
if (nv_encoder->dcb->sorconf.link & 1)
mode_ctl |= 0x00000800;
else
@@ -227,6 +234,8 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC;
+ nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
+
ret = RING_SPACE(evo, 2);
if (ret) {
NV_ERROR(dev, "no space while connecting SOR\n");
@@ -313,29 +322,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry)
encoder->possible_crtcs = entry->heads;
encoder->possible_clones = 0;
- if (nv_encoder->dcb->type == OUTPUT_DP) {
- int or = nv_encoder->or, link = !(entry->dpconf.sor.link & 1);
- uint32_t tmp;
-
- tmp = nv_rd32(dev, 0x61c700 + (or * 0x800));
-
- switch ((tmp & 0x00000f00) >> 8) {
- case 8:
- case 9:
- nv_encoder->dp.mc_unknown = (tmp & 0x000f0000) >> 16;
- tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
- nv_encoder->dp.unk0 = tmp & 0x000001fc;
- tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link));
- nv_encoder->dp.unk1 = tmp & 0x010f7f3f;
- break;
- default:
- break;
- }
-
- if (!nv_encoder->dp.mc_unknown)
- nv_encoder->dp.mc_unknown = 5;
- }
-
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c
index 1a0dd491a0e4..40b84f22d819 100644
--- a/drivers/gpu/drm/nouveau/nv50_vm.c
+++ b/drivers/gpu/drm/nouveau/nv50_vm.c
@@ -156,7 +156,7 @@ nv50_vm_flush(struct nouveau_vm *vm)
pinstmem->flush(vm->dev);
/* BAR */
- if (vm != dev_priv->chan_vm) {
+ if (vm == dev_priv->bar1_vm || vm == dev_priv->bar3_vm) {
nv50_vm_flush_engine(vm->dev, 6);
return;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c
index ffbc3d8cf5be..2e45e57fd869 100644
--- a/drivers/gpu/drm/nouveau/nv50_vram.c
+++ b/drivers/gpu/drm/nouveau/nv50_vram.c
@@ -51,9 +51,7 @@ void
nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
- struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM];
- struct nouveau_mm *mm = man->priv;
+ struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
struct nouveau_mm_node *this;
struct nouveau_mem *mem;
@@ -84,9 +82,7 @@ nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc,
u32 memtype, struct nouveau_mem **pmem)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
- struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM];
- struct nouveau_mm *mm = man->priv;
+ struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
struct nouveau_mm_node *r;
struct nouveau_mem *mem;
int comp = (memtype & 0x300) >> 8;
@@ -164,7 +160,7 @@ nv50_vram_rblock(struct drm_device *dev)
colbits = (r4 & 0x0000f000) >> 12;
rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
- banks = ((r4 & 0x01000000) ? 8 : 4);
+ banks = 1 << (((r4 & 0x03000000) >> 24) + 2);
rowsize = parts * banks * (1 << colbits) * 8;
predicted = rowsize << rowbitsa;
@@ -190,22 +186,35 @@ int
nv50_vram_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
+ const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+ const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+ u32 rblock, length;
dev_priv->vram_size = nv_rd32(dev, 0x10020c);
dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32;
dev_priv->vram_size &= 0xffffffff00ULL;
- switch (dev_priv->chipset) {
- case 0xaa:
- case 0xac:
- case 0xaf:
+ /* IGPs, no funky reordering happens here, they don't have VRAM */
+ if (dev_priv->chipset == 0xaa ||
+ dev_priv->chipset == 0xac ||
+ dev_priv->chipset == 0xaf) {
dev_priv->vram_sys_base = (u64)nv_rd32(dev, 0x100e10) << 12;
- dev_priv->vram_rblock_size = 4096;
- break;
- default:
- dev_priv->vram_rblock_size = nv50_vram_rblock(dev);
- break;
+ rblock = 4096 >> 12;
+ } else {
+ rblock = nv50_vram_rblock(dev) >> 12;
}
- return 0;
+ length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail;
+
+ return nouveau_mm_init(&vram->mm, rsvd_head, length, rblock);
+}
+
+void
+nv50_vram_fini(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
+
+ nouveau_mm_fini(&vram->mm);
}
diff --git a/drivers/gpu/drm/nouveau/nv84_crypt.c b/drivers/gpu/drm/nouveau/nv84_crypt.c
index 75b809a51748..edece9c616eb 100644
--- a/drivers/gpu/drm/nouveau/nv84_crypt.c
+++ b/drivers/gpu/drm/nouveau/nv84_crypt.c
@@ -138,7 +138,7 @@ nv84_crypt_isr(struct drm_device *dev)
}
static int
-nv84_crypt_fini(struct drm_device *dev, int engine)
+nv84_crypt_fini(struct drm_device *dev, int engine, bool suspend)
{
nv_wr32(dev, 0x102140, 0x00000000);
return 0;
diff --git a/drivers/gpu/drm/nouveau/nva3_copy.c b/drivers/gpu/drm/nouveau/nva3_copy.c
index b86820a61220..8f356d58e409 100644
--- a/drivers/gpu/drm/nouveau/nva3_copy.c
+++ b/drivers/gpu/drm/nouveau/nva3_copy.c
@@ -140,7 +140,7 @@ nva3_copy_init(struct drm_device *dev, int engine)
}
static int
-nva3_copy_fini(struct drm_device *dev, int engine)
+nva3_copy_fini(struct drm_device *dev, int engine, bool suspend)
{
nv_mask(dev, 0x104048, 0x00000003, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c
index e4b2b9e934b2..618c144b7a30 100644
--- a/drivers/gpu/drm/nouveau/nva3_pm.c
+++ b/drivers/gpu/drm/nouveau/nva3_pm.c
@@ -27,178 +27,316 @@
#include "nouveau_bios.h"
#include "nouveau_pm.h"
-/* This is actually a lot more complex than it appears here, but hopefully
- * this should be able to deal with what the VBIOS leaves for us..
- *
- * If not, well, I'll jump off that bridge when I come to it.
- */
+static u32 read_clk(struct drm_device *, int, bool);
+static u32 read_pll(struct drm_device *, int, u32);
-struct nva3_pm_state {
- enum pll_types type;
- u32 src0;
- u32 src1;
- u32 ctrl;
- u32 coef;
- u32 old_pnm;
- u32 new_pnm;
- u32 new_div;
-};
+static u32
+read_vco(struct drm_device *dev, int clk)
+{
+ u32 sctl = nv_rd32(dev, 0x4120 + (clk * 4));
+ if ((sctl & 0x00000030) != 0x00000030)
+ return read_pll(dev, 0x41, 0x00e820);
+ return read_pll(dev, 0x42, 0x00e8a0);
+}
-static int
-nva3_pm_pll_offset(u32 id)
+static u32
+read_clk(struct drm_device *dev, int clk, bool ignore_en)
{
- static const u32 pll_map[] = {
- 0x00, PLL_CORE,
- 0x01, PLL_SHADER,
- 0x02, PLL_MEMORY,
- 0x00, 0x00
- };
- const u32 *map = pll_map;
-
- while (map[1]) {
- if (id == map[1])
- return map[0];
- map += 2;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 sctl, sdiv, sclk;
+
+ /* refclk for the 0xe8xx plls is a fixed frequency */
+ if (clk >= 0x40) {
+ if (dev_priv->chipset == 0xaf) {
+ /* no joke.. seriously.. sigh.. */
+ return nv_rd32(dev, 0x00471c) * 1000;
+ }
+
+ return dev_priv->crystal;
}
- return -ENOENT;
+ sctl = nv_rd32(dev, 0x4120 + (clk * 4));
+ if (!ignore_en && !(sctl & 0x00000100))
+ return 0;
+
+ switch (sctl & 0x00003000) {
+ case 0x00000000:
+ return dev_priv->crystal;
+ case 0x00002000:
+ if (sctl & 0x00000040)
+ return 108000;
+ return 100000;
+ case 0x00003000:
+ sclk = read_vco(dev, clk);
+ sdiv = ((sctl & 0x003f0000) >> 16) + 2;
+ return (sclk * 2) / sdiv;
+ default:
+ return 0;
+ }
}
-int
-nva3_pm_clock_get(struct drm_device *dev, u32 id)
+static u32
+read_pll(struct drm_device *dev, int clk, u32 pll)
+{
+ u32 ctrl = nv_rd32(dev, pll + 0);
+ u32 sclk = 0, P = 1, N = 1, M = 1;
+
+ if (!(ctrl & 0x00000008)) {
+ if (ctrl & 0x00000001) {
+ u32 coef = nv_rd32(dev, pll + 4);
+ M = (coef & 0x000000ff) >> 0;
+ N = (coef & 0x0000ff00) >> 8;
+ P = (coef & 0x003f0000) >> 16;
+
+ /* no post-divider on these.. */
+ if ((pll & 0x00ff00) == 0x00e800)
+ P = 1;
+
+ sclk = read_clk(dev, 0x00 + clk, false);
+ }
+ } else {
+ sclk = read_clk(dev, 0x10 + clk, false);
+ }
+
+ return sclk * N / (M * P);
+}
+
+struct creg {
+ u32 clk;
+ u32 pll;
+};
+
+static int
+calc_clk(struct drm_device *dev, int clk, u32 pll, u32 khz, struct creg *reg)
{
- u32 src0, src1, ctrl, coef;
- struct pll_lims pll;
- int ret, off;
- int P, N, M;
+ struct pll_lims limits;
+ u32 oclk, sclk, sdiv;
+ int P, N, M, diff;
+ int ret;
+
+ reg->pll = 0;
+ reg->clk = 0;
+ if (!khz) {
+ NV_DEBUG(dev, "no clock for 0x%04x/0x%02x\n", pll, clk);
+ return 0;
+ }
- ret = get_pll_limits(dev, id, &pll);
+ switch (khz) {
+ case 27000:
+ reg->clk = 0x00000100;
+ return khz;
+ case 100000:
+ reg->clk = 0x00002100;
+ return khz;
+ case 108000:
+ reg->clk = 0x00002140;
+ return khz;
+ default:
+ sclk = read_vco(dev, clk);
+ sdiv = min((sclk * 2) / (khz - 2999), (u32)65);
+ /* if the clock has a PLL attached, and we can get a within
+ * [-2, 3) MHz of a divider, we'll disable the PLL and use
+ * the divider instead.
+ *
+ * divider can go as low as 2, limited here because NVIDIA
+ * and the VBIOS on my NVA8 seem to prefer using the PLL
+ * for 810MHz - is there a good reason?
+ */
+ if (sdiv > 4) {
+ oclk = (sclk * 2) / sdiv;
+ diff = khz - oclk;
+ if (!pll || (diff >= -2000 && diff < 3000)) {
+ reg->clk = (((sdiv - 2) << 16) | 0x00003100);
+ return oclk;
+ }
+ }
+
+ if (!pll) {
+ NV_ERROR(dev, "bad freq %02x: %d %d\n", clk, khz, sclk);
+ return -ERANGE;
+ }
+
+ break;
+ }
+
+ ret = get_pll_limits(dev, pll, &limits);
if (ret)
return ret;
- off = nva3_pm_pll_offset(id);
- if (off < 0)
- return off;
+ limits.refclk = read_clk(dev, clk - 0x10, true);
+ if (!limits.refclk)
+ return -EINVAL;
+
+ ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P);
+ if (ret >= 0) {
+ reg->clk = nv_rd32(dev, 0x4120 + (clk * 4));
+ reg->pll = (P << 16) | (N << 8) | M;
+ }
+ return ret;
+}
+
+static void
+prog_pll(struct drm_device *dev, int clk, u32 pll, struct creg *reg)
+{
+ const u32 src0 = 0x004120 + (clk * 4);
+ const u32 src1 = 0x004160 + (clk * 4);
+ const u32 ctrl = pll + 0;
+ const u32 coef = pll + 4;
+ u32 cntl;
+
+ if (!reg->clk && !reg->pll) {
+ NV_DEBUG(dev, "no clock for %02x\n", clk);
+ return;
+ }
- src0 = nv_rd32(dev, 0x4120 + (off * 4));
- src1 = nv_rd32(dev, 0x4160 + (off * 4));
- ctrl = nv_rd32(dev, pll.reg + 0);
- coef = nv_rd32(dev, pll.reg + 4);
- NV_DEBUG(dev, "PLL %02x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
- id, src0, src1, ctrl, coef);
+ cntl = nv_rd32(dev, ctrl) & 0xfffffff2;
+ if (reg->pll) {
+ nv_mask(dev, src0, 0x00000101, 0x00000101);
+ nv_wr32(dev, coef, reg->pll);
+ nv_wr32(dev, ctrl, cntl | 0x00000015);
+ nv_mask(dev, src1, 0x00000100, 0x00000000);
+ nv_mask(dev, src1, 0x00000001, 0x00000000);
+ } else {
+ nv_mask(dev, src1, 0x003f3141, 0x00000101 | reg->clk);
+ nv_wr32(dev, ctrl, cntl | 0x0000001d);
+ nv_mask(dev, ctrl, 0x00000001, 0x00000000);
+ nv_mask(dev, src0, 0x00000100, 0x00000000);
+ nv_mask(dev, src0, 0x00000001, 0x00000000);
+ }
+}
- if (ctrl & 0x00000008) {
- u32 div = ((src1 & 0x003c0000) >> 18) + 1;
- return (pll.refclk * 2) / div;
+static void
+prog_clk(struct drm_device *dev, int clk, struct creg *reg)
+{
+ if (!reg->clk) {
+ NV_DEBUG(dev, "no clock for %02x\n", clk);
+ return;
}
- P = (coef & 0x003f0000) >> 16;
- N = (coef & 0x0000ff00) >> 8;
- M = (coef & 0x000000ff);
- return pll.refclk * N / M / P;
+ nv_mask(dev, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | reg->clk);
+}
+
+int
+nva3_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
+{
+ perflvl->core = read_pll(dev, 0x00, 0x4200);
+ perflvl->shader = read_pll(dev, 0x01, 0x4220);
+ perflvl->memory = read_pll(dev, 0x02, 0x4000);
+ perflvl->unka0 = read_clk(dev, 0x20, false);
+ perflvl->vdec = read_clk(dev, 0x21, false);
+ perflvl->daemon = read_clk(dev, 0x25, false);
+ perflvl->copy = perflvl->core;
+ return 0;
}
+struct nva3_pm_state {
+ struct creg nclk;
+ struct creg sclk;
+ struct creg mclk;
+ struct creg vdec;
+ struct creg unka0;
+};
+
void *
-nva3_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
- u32 id, int khz)
+nva3_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
- struct nva3_pm_state *pll;
- struct pll_lims limits;
- int N, M, P, diff;
- int ret, off;
+ struct nva3_pm_state *info;
+ int ret;
- ret = get_pll_limits(dev, id, &limits);
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ ret = calc_clk(dev, 0x10, 0x4200, perflvl->core, &info->nclk);
if (ret < 0)
- return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
+ goto out;
- off = nva3_pm_pll_offset(id);
- if (id < 0)
- return ERR_PTR(-EINVAL);
+ ret = calc_clk(dev, 0x11, 0x4220, perflvl->shader, &info->sclk);
+ if (ret < 0)
+ goto out;
+ ret = calc_clk(dev, 0x12, 0x4000, perflvl->memory, &info->mclk);
+ if (ret < 0)
+ goto out;
- pll = kzalloc(sizeof(*pll), GFP_KERNEL);
- if (!pll)
- return ERR_PTR(-ENOMEM);
- pll->type = id;
- pll->src0 = 0x004120 + (off * 4);
- pll->src1 = 0x004160 + (off * 4);
- pll->ctrl = limits.reg + 0;
- pll->coef = limits.reg + 4;
-
- /* If target clock is within [-2, 3) MHz of a divisor, we'll
- * use that instead of calculating MNP values
- */
- pll->new_div = min((limits.refclk * 2) / (khz - 2999), 16);
- if (pll->new_div) {
- diff = khz - ((limits.refclk * 2) / pll->new_div);
- if (diff < -2000 || diff >= 3000)
- pll->new_div = 0;
- }
+ ret = calc_clk(dev, 0x20, 0x0000, perflvl->unka0, &info->unka0);
+ if (ret < 0)
+ goto out;
- if (!pll->new_div) {
- ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P);
- if (ret < 0)
- return ERR_PTR(ret);
+ ret = calc_clk(dev, 0x21, 0x0000, perflvl->vdec, &info->vdec);
+ if (ret < 0)
+ goto out;
- pll->new_pnm = (P << 16) | (N << 8) | M;
- pll->new_div = 2 - 1;
- } else {
- pll->new_pnm = 0;
- pll->new_div--;
+out:
+ if (ret < 0) {
+ kfree(info);
+ info = ERR_PTR(ret);
}
+ return info;
+}
+
+static bool
+nva3_pm_grcp_idle(void *data)
+{
+ struct drm_device *dev = data;
- if ((nv_rd32(dev, pll->src1) & 0x00000101) != 0x00000101)
- pll->old_pnm = nv_rd32(dev, pll->coef);
- return pll;
+ if (!(nv_rd32(dev, 0x400304) & 0x00000001))
+ return true;
+ if (nv_rd32(dev, 0x400308) == 0x0050001c)
+ return true;
+ return false;
}
void
-nva3_pm_clock_set(struct drm_device *dev, void *pre_state)
+nva3_pm_clocks_set(struct drm_device *dev, void *pre_state)
{
- struct nva3_pm_state *pll = pre_state;
- u32 ctrl = 0;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nva3_pm_state *info = pre_state;
+ unsigned long flags;
- /* For the memory clock, NVIDIA will build a "script" describing
- * the reclocking process and ask PDAEMON to execute it.
- */
- if (pll->type == PLL_MEMORY) {
- nv_wr32(dev, 0x100210, 0);
- nv_wr32(dev, 0x1002dc, 1);
- nv_wr32(dev, 0x004018, 0x00001000);
- ctrl = 0x18000100;
+ /* prevent any new grctx switches from starting */
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+ nv_wr32(dev, 0x400324, 0x00000000);
+ nv_wr32(dev, 0x400328, 0x0050001c); /* wait flag 0x1c */
+ /* wait for any pending grctx switches to complete */
+ if (!nv_wait_cb(dev, nva3_pm_grcp_idle, dev)) {
+ NV_ERROR(dev, "pm: ctxprog didn't go idle\n");
+ goto cleanup;
}
-
- if (pll->old_pnm || !pll->new_pnm) {
- nv_mask(dev, pll->src1, 0x003c0101, 0x00000101 |
- (pll->new_div << 18));
- nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl);
- nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000);
+ /* freeze PFIFO */
+ nv_mask(dev, 0x002504, 0x00000001, 0x00000001);
+ if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) {
+ NV_ERROR(dev, "pm: fifo didn't go idle\n");
+ goto cleanup;
}
- if (pll->new_pnm) {
- nv_mask(dev, pll->src0, 0x00000101, 0x00000101);
- nv_wr32(dev, pll->coef, pll->new_pnm);
- nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl);
- nv_mask(dev, pll->ctrl, 0x00000010, 0x00000000);
- nv_mask(dev, pll->ctrl, 0x00020010, 0x00020010);
- nv_wr32(dev, pll->ctrl, 0x00010015 | ctrl);
- nv_mask(dev, pll->src1, 0x00000100, 0x00000000);
- nv_mask(dev, pll->src1, 0x00000001, 0x00000000);
- if (pll->type == PLL_MEMORY)
- nv_wr32(dev, 0x4018, 0x10005000);
- } else {
- nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000);
- nv_mask(dev, pll->src0, 0x00000100, 0x00000000);
- nv_mask(dev, pll->src0, 0x00000001, 0x00000000);
- if (pll->type == PLL_MEMORY)
- nv_wr32(dev, 0x4018, 0x1000d000);
- }
+ prog_pll(dev, 0x00, 0x004200, &info->nclk);
+ prog_pll(dev, 0x01, 0x004220, &info->sclk);
+ prog_clk(dev, 0x20, &info->unka0);
+ prog_clk(dev, 0x21, &info->vdec);
- if (pll->type == PLL_MEMORY) {
+ if (info->mclk.clk || info->mclk.pll) {
+ nv_wr32(dev, 0x100210, 0);
+ nv_wr32(dev, 0x1002dc, 1);
+ nv_wr32(dev, 0x004018, 0x00001000);
+ prog_pll(dev, 0x02, 0x004000, &info->mclk);
+ if (nv_rd32(dev, 0x4000) & 0x00000008)
+ nv_wr32(dev, 0x004018, 0x1000d000);
+ else
+ nv_wr32(dev, 0x004018, 0x10005000);
nv_wr32(dev, 0x1002dc, 0);
nv_wr32(dev, 0x100210, 0x80000000);
}
- kfree(pll);
+cleanup:
+ /* unfreeze PFIFO */
+ nv_mask(dev, 0x002504, 0x00000001, 0x00000000);
+ /* restore ctxprog to normal */
+ nv_wr32(dev, 0x400324, 0x00000000);
+ nv_wr32(dev, 0x400328, 0x0070009c); /* set flag 0x1c */
+ /* unblock it if necessary */
+ if (nv_rd32(dev, 0x400308) == 0x0050001c)
+ nv_mask(dev, 0x400824, 0x10000000, 0x10000000);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+ kfree(info);
}
-
diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.c b/drivers/gpu/drm/nouveau/nvc0_copy.c
index 208fa7ab3f42..dddf006f6d88 100644
--- a/drivers/gpu/drm/nouveau/nvc0_copy.c
+++ b/drivers/gpu/drm/nouveau/nvc0_copy.c
@@ -48,14 +48,14 @@ nvc0_copy_context_new(struct nouveau_channel *chan, int engine)
struct nouveau_gpuobj *ctx = NULL;
int ret;
- ret = nouveau_gpuobj_new(dev, NULL, 256, 256,
+ ret = nouveau_gpuobj_new(dev, chan, 256, 256,
NVOBJ_FLAG_VM | NVOBJ_FLAG_VM_USER |
NVOBJ_FLAG_ZERO_ALLOC, &ctx);
if (ret)
return ret;
- nv_wo32(ramin, pcopy->ctx + 0, lower_32_bits(ctx->vinst));
- nv_wo32(ramin, pcopy->ctx + 4, upper_32_bits(ctx->vinst));
+ nv_wo32(ramin, pcopy->ctx + 0, lower_32_bits(ctx->linst));
+ nv_wo32(ramin, pcopy->ctx + 4, upper_32_bits(ctx->linst));
dev_priv->engine.instmem.flush(dev);
chan->engctx[engine] = ctx;
@@ -127,7 +127,7 @@ nvc0_copy_init(struct drm_device *dev, int engine)
}
static int
-nvc0_copy_fini(struct drm_device *dev, int engine)
+nvc0_copy_fini(struct drm_device *dev, int engine, bool suspend)
{
struct nvc0_copy_engine *pcopy = nv_engine(dev, engine);
diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c
index 26a996025dd2..5bf55038fd92 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fb.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fb.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2010 Red Hat Inc.
+ * Copyright 2011 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -23,16 +23,107 @@
*/
#include "drmP.h"
-
+#include "drm.h"
#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+struct nvc0_fb_priv {
+ struct page *r100c10_page;
+ dma_addr_t r100c10;
+};
+
+static inline void
+nvc0_mfb_subp_isr(struct drm_device *dev, int unit, int subp)
+{
+ u32 subp_base = 0x141000 + (unit * 0x2000) + (subp * 0x400);
+ u32 stat = nv_rd32(dev, subp_base + 0x020);
+
+ if (stat) {
+ NV_INFO(dev, "PMFB%d_SUBP%d: 0x%08x\n", unit, subp, stat);
+ nv_wr32(dev, subp_base + 0x020, stat);
+ }
+}
+
+static void
+nvc0_mfb_isr(struct drm_device *dev)
+{
+ u32 units = nv_rd32(dev, 0x00017c);
+ while (units) {
+ u32 subp, unit = ffs(units) - 1;
+ for (subp = 0; subp < 2; subp++)
+ nvc0_mfb_subp_isr(dev, unit, subp);
+ units &= ~(1 << unit);
+ }
+}
+
+static void
+nvc0_fb_destroy(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
+ struct nvc0_fb_priv *priv = pfb->priv;
+
+ nouveau_irq_unregister(dev, 25);
+
+ if (priv->r100c10_page) {
+ pci_unmap_page(dev->pdev, priv->r100c10, PAGE_SIZE,
+ PCI_DMA_BIDIRECTIONAL);
+ __free_page(priv->r100c10_page);
+ }
+
+ kfree(priv);
+ pfb->priv = NULL;
+}
+
+static int
+nvc0_fb_create(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
+ struct nvc0_fb_priv *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ pfb->priv = priv;
+
+ priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!priv->r100c10_page) {
+ nvc0_fb_destroy(dev);
+ return -ENOMEM;
+ }
+
+ priv->r100c10 = pci_map_page(dev->pdev, priv->r100c10_page, 0,
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ if (pci_dma_mapping_error(dev->pdev, priv->r100c10)) {
+ nvc0_fb_destroy(dev);
+ return -EFAULT;
+ }
+
+ nouveau_irq_register(dev, 25, nvc0_mfb_isr);
+ return 0;
+}
int
nvc0_fb_init(struct drm_device *dev)
{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvc0_fb_priv *priv;
+ int ret;
+
+ if (!dev_priv->engine.fb.priv) {
+ ret = nvc0_fb_create(dev);
+ if (ret)
+ return ret;
+ }
+ priv = dev_priv->engine.fb.priv;
+
+ nv_wr32(dev, 0x100c10, priv->r100c10 >> 8);
return 0;
}
void
nvc0_fb_takedown(struct drm_device *dev)
{
+ nvc0_fb_destroy(dev);
}
diff --git a/drivers/gpu/drm/nouveau/nvc0_fbcon.c b/drivers/gpu/drm/nouveau/nvc0_fbcon.c
index fa5d4c234383..a495e48197ca 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fbcon.c
@@ -159,7 +159,7 @@ nvc0_fbcon_accel_init(struct fb_info *info)
struct drm_device *dev = nfbdev->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
- struct nouveau_bo *nvbo = nfbdev->nouveau_fb.nvbo;
+ struct nouveau_framebuffer *fb = &nfbdev->nouveau_fb;
int ret, format;
ret = nouveau_gpuobj_gr_new(chan, 0x902d, 0x902d);
@@ -203,8 +203,8 @@ nvc0_fbcon_accel_init(struct fb_info *info)
BEGIN_NVC0(chan, 2, NvSub2D, 0x0000, 1);
OUT_RING (chan, 0x0000902d);
BEGIN_NVC0(chan, 2, NvSub2D, 0x0104, 2);
- OUT_RING (chan, upper_32_bits(chan->notifier_bo->bo.offset));
- OUT_RING (chan, lower_32_bits(chan->notifier_bo->bo.offset));
+ OUT_RING (chan, upper_32_bits(chan->notifier_vma.offset));
+ OUT_RING (chan, lower_32_bits(chan->notifier_vma.offset));
BEGIN_NVC0(chan, 2, NvSub2D, 0x0290, 1);
OUT_RING (chan, 0);
BEGIN_NVC0(chan, 2, NvSub2D, 0x0888, 1);
@@ -249,8 +249,8 @@ nvc0_fbcon_accel_init(struct fb_info *info)
OUT_RING (chan, info->fix.line_length);
OUT_RING (chan, info->var.xres_virtual);
OUT_RING (chan, info->var.yres_virtual);
- OUT_RING (chan, upper_32_bits(nvbo->vma.offset));
- OUT_RING (chan, lower_32_bits(nvbo->vma.offset));
+ OUT_RING (chan, upper_32_bits(fb->vma.offset));
+ OUT_RING (chan, lower_32_bits(fb->vma.offset));
BEGIN_NVC0(chan, 2, NvSub2D, 0x0230, 10);
OUT_RING (chan, format);
OUT_RING (chan, 1);
@@ -260,8 +260,8 @@ nvc0_fbcon_accel_init(struct fb_info *info)
OUT_RING (chan, info->fix.line_length);
OUT_RING (chan, info->var.xres_virtual);
OUT_RING (chan, info->var.yres_virtual);
- OUT_RING (chan, upper_32_bits(nvbo->vma.offset));
- OUT_RING (chan, lower_32_bits(nvbo->vma.offset));
+ OUT_RING (chan, upper_32_bits(fb->vma.offset));
+ OUT_RING (chan, lower_32_bits(fb->vma.offset));
FIRE_RING (chan);
return 0;
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
index fb4f5943e01b..dcbe0d5d0241 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -210,10 +210,10 @@ nvc0_fifo_unload_context(struct drm_device *dev)
int i;
for (i = 0; i < 128; i++) {
- if (!(nv_rd32(dev, 0x003004 + (i * 4)) & 1))
+ if (!(nv_rd32(dev, 0x003004 + (i * 8)) & 1))
continue;
- nv_mask(dev, 0x003004 + (i * 4), 0x00000001, 0x00000000);
+ nv_mask(dev, 0x003004 + (i * 8), 0x00000001, 0x00000000);
nv_wr32(dev, 0x002634, i);
if (!nv_wait(dev, 0x002634, 0xffffffff, i)) {
NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n",
@@ -322,7 +322,7 @@ nvc0_fifo_init(struct drm_device *dev)
}
/* PSUBFIFO[n] */
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < priv->spoon_nr; i++) {
nv_mask(dev, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000);
nv_wr32(dev, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */
nv_wr32(dev, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTR_EN */
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c
index ca6db204d644..a74e501afd25 100644
--- a/drivers/gpu/drm/nouveau/nvc0_graph.c
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.c
@@ -23,12 +23,40 @@
*/
#include <linux/firmware.h>
+#include <linux/module.h>
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_mm.h"
+
#include "nvc0_graph.h"
+#include "nvc0_grhub.fuc.h"
+#include "nvc0_grgpc.fuc.h"
+
+static void
+nvc0_graph_ctxctl_debug_unit(struct drm_device *dev, u32 base)
+{
+ NV_INFO(dev, "PGRAPH: %06x - done 0x%08x\n", base,
+ nv_rd32(dev, base + 0x400));
+ NV_INFO(dev, "PGRAPH: %06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base,
+ nv_rd32(dev, base + 0x800), nv_rd32(dev, base + 0x804),
+ nv_rd32(dev, base + 0x808), nv_rd32(dev, base + 0x80c));
+ NV_INFO(dev, "PGRAPH: %06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base,
+ nv_rd32(dev, base + 0x810), nv_rd32(dev, base + 0x814),
+ nv_rd32(dev, base + 0x818), nv_rd32(dev, base + 0x81c));
+}
+
+static void
+nvc0_graph_ctxctl_debug(struct drm_device *dev)
+{
+ u32 gpcnr = nv_rd32(dev, 0x409604) & 0xffff;
+ u32 gpc;
+
+ nvc0_graph_ctxctl_debug_unit(dev, 0x409000);
+ for (gpc = 0; gpc < gpcnr; gpc++)
+ nvc0_graph_ctxctl_debug_unit(dev, 0x502000 + (gpc * 0x8000));
+}
static int
nvc0_graph_load_context(struct nouveau_channel *chan)
@@ -72,24 +100,44 @@ nvc0_graph_construct_context(struct nouveau_channel *chan)
if (!ctx)
return -ENOMEM;
- nvc0_graph_load_context(chan);
-
- nv_wo32(grch->grctx, 0x1c, 1);
- nv_wo32(grch->grctx, 0x20, 0);
- nv_wo32(grch->grctx, 0x28, 0);
- nv_wo32(grch->grctx, 0x2c, 0);
- dev_priv->engine.instmem.flush(dev);
-
- ret = nvc0_grctx_generate(chan);
- if (ret) {
- kfree(ctx);
- return ret;
+ if (!nouveau_ctxfw) {
+ nv_wr32(dev, 0x409840, 0x80000000);
+ nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12);
+ nv_wr32(dev, 0x409504, 0x00000001);
+ if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) {
+ NV_ERROR(dev, "PGRAPH: HUB_SET_CHAN timeout\n");
+ nvc0_graph_ctxctl_debug(dev);
+ ret = -EBUSY;
+ goto err;
+ }
+ } else {
+ nvc0_graph_load_context(chan);
+
+ nv_wo32(grch->grctx, 0x1c, 1);
+ nv_wo32(grch->grctx, 0x20, 0);
+ nv_wo32(grch->grctx, 0x28, 0);
+ nv_wo32(grch->grctx, 0x2c, 0);
+ dev_priv->engine.instmem.flush(dev);
}
- ret = nvc0_graph_unload_context_to(dev, chan->ramin->vinst);
- if (ret) {
- kfree(ctx);
- return ret;
+ ret = nvc0_grctx_generate(chan);
+ if (ret)
+ goto err;
+
+ if (!nouveau_ctxfw) {
+ nv_wr32(dev, 0x409840, 0x80000000);
+ nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12);
+ nv_wr32(dev, 0x409504, 0x00000002);
+ if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) {
+ NV_ERROR(dev, "PGRAPH: HUB_CTX_SAVE timeout\n");
+ nvc0_graph_ctxctl_debug(dev);
+ ret = -EBUSY;
+ goto err;
+ }
+ } else {
+ ret = nvc0_graph_unload_context_to(dev, chan->ramin->vinst);
+ if (ret)
+ goto err;
}
for (i = 0; i < priv->grctx_size; i += 4)
@@ -97,6 +145,10 @@ nvc0_graph_construct_context(struct nouveau_channel *chan)
priv->grctx_vals = ctx;
return 0;
+
+err:
+ kfree(ctx);
+ return ret;
}
static int
@@ -105,64 +157,87 @@ nvc0_graph_create_context_mmio_list(struct nouveau_channel *chan)
struct nvc0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR);
struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR];
struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
int i = 0, gpc, tp, ret;
- u32 magic;
- ret = nouveau_gpuobj_new(dev, NULL, 0x2000, 256, NVOBJ_FLAG_VM,
+ ret = nouveau_gpuobj_new(dev, chan, 0x2000, 256, NVOBJ_FLAG_VM,
&grch->unk408004);
if (ret)
return ret;
- ret = nouveau_gpuobj_new(dev, NULL, 0x8000, 256, NVOBJ_FLAG_VM,
+ ret = nouveau_gpuobj_new(dev, chan, 0x8000, 256, NVOBJ_FLAG_VM,
&grch->unk40800c);
if (ret)
return ret;
- ret = nouveau_gpuobj_new(dev, NULL, 384 * 1024, 4096,
+ ret = nouveau_gpuobj_new(dev, chan, 384 * 1024, 4096,
NVOBJ_FLAG_VM | NVOBJ_FLAG_VM_USER,
&grch->unk418810);
if (ret)
return ret;
- ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0, NVOBJ_FLAG_VM,
+ ret = nouveau_gpuobj_new(dev, chan, 0x1000, 0, NVOBJ_FLAG_VM,
&grch->mmio);
if (ret)
return ret;
nv_wo32(grch->mmio, i++ * 4, 0x00408004);
- nv_wo32(grch->mmio, i++ * 4, grch->unk408004->vinst >> 8);
+ nv_wo32(grch->mmio, i++ * 4, grch->unk408004->linst >> 8);
nv_wo32(grch->mmio, i++ * 4, 0x00408008);
nv_wo32(grch->mmio, i++ * 4, 0x80000018);
nv_wo32(grch->mmio, i++ * 4, 0x0040800c);
- nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->vinst >> 8);
+ nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->linst >> 8);
nv_wo32(grch->mmio, i++ * 4, 0x00408010);
nv_wo32(grch->mmio, i++ * 4, 0x80000000);
nv_wo32(grch->mmio, i++ * 4, 0x00418810);
- nv_wo32(grch->mmio, i++ * 4, 0x80000000 | grch->unk418810->vinst >> 12);
+ nv_wo32(grch->mmio, i++ * 4, 0x80000000 | grch->unk418810->linst >> 12);
nv_wo32(grch->mmio, i++ * 4, 0x00419848);
- nv_wo32(grch->mmio, i++ * 4, 0x10000000 | grch->unk418810->vinst >> 12);
+ nv_wo32(grch->mmio, i++ * 4, 0x10000000 | grch->unk418810->linst >> 12);
nv_wo32(grch->mmio, i++ * 4, 0x00419004);
- nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->vinst >> 8);
+ nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->linst >> 8);
nv_wo32(grch->mmio, i++ * 4, 0x00419008);
nv_wo32(grch->mmio, i++ * 4, 0x00000000);
nv_wo32(grch->mmio, i++ * 4, 0x00418808);
- nv_wo32(grch->mmio, i++ * 4, grch->unk408004->vinst >> 8);
+ nv_wo32(grch->mmio, i++ * 4, grch->unk408004->linst >> 8);
nv_wo32(grch->mmio, i++ * 4, 0x0041880c);
nv_wo32(grch->mmio, i++ * 4, 0x80000018);
- magic = 0x02180000;
- nv_wo32(grch->mmio, i++ * 4, 0x00405830);
- nv_wo32(grch->mmio, i++ * 4, magic);
- for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
- for (tp = 0; tp < priv->tp_nr[gpc]; tp++, magic += 0x02fc) {
- u32 reg = 0x504520 + (gpc * 0x8000) + (tp * 0x0800);
- nv_wo32(grch->mmio, i++ * 4, reg);
- nv_wo32(grch->mmio, i++ * 4, magic);
+ if (dev_priv->chipset != 0xc1) {
+ u32 magic = 0x02180000;
+ nv_wo32(grch->mmio, i++ * 4, 0x00405830);
+ nv_wo32(grch->mmio, i++ * 4, magic);
+ for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+ for (tp = 0; tp < priv->tp_nr[gpc]; tp++) {
+ u32 reg = TP_UNIT(gpc, tp, 0x520);
+ nv_wo32(grch->mmio, i++ * 4, reg);
+ nv_wo32(grch->mmio, i++ * 4, magic);
+ magic += 0x0324;
+ }
+ }
+ } else {
+ u32 magic = 0x02180000;
+ nv_wo32(grch->mmio, i++ * 4, 0x00405830);
+ nv_wo32(grch->mmio, i++ * 4, magic | 0x0000218);
+ nv_wo32(grch->mmio, i++ * 4, 0x004064c4);
+ nv_wo32(grch->mmio, i++ * 4, 0x0086ffff);
+ for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+ for (tp = 0; tp < priv->tp_nr[gpc]; tp++) {
+ u32 reg = TP_UNIT(gpc, tp, 0x520);
+ nv_wo32(grch->mmio, i++ * 4, reg);
+ nv_wo32(grch->mmio, i++ * 4, (1 << 28) | magic);
+ magic += 0x0324;
+ }
+ for (tp = 0; tp < priv->tp_nr[gpc]; tp++) {
+ u32 reg = TP_UNIT(gpc, tp, 0x544);
+ nv_wo32(grch->mmio, i++ * 4, reg);
+ nv_wo32(grch->mmio, i++ * 4, magic);
+ magic += 0x0324;
+ }
}
}
@@ -186,7 +261,7 @@ nvc0_graph_context_new(struct nouveau_channel *chan, int engine)
return -ENOMEM;
chan->engctx[NVOBJ_ENGINE_GR] = grch;
- ret = nouveau_gpuobj_new(dev, NULL, priv->grctx_size, 256,
+ ret = nouveau_gpuobj_new(dev, chan, priv->grctx_size, 256,
NVOBJ_FLAG_VM | NVOBJ_FLAG_ZERO_ALLOC,
&grch->grctx);
if (ret)
@@ -197,8 +272,8 @@ nvc0_graph_context_new(struct nouveau_channel *chan, int engine)
if (ret)
goto error;
- nv_wo32(chan->ramin, 0x0210, lower_32_bits(grctx->vinst) | 4);
- nv_wo32(chan->ramin, 0x0214, upper_32_bits(grctx->vinst));
+ nv_wo32(chan->ramin, 0x0210, lower_32_bits(grctx->linst) | 4);
+ nv_wo32(chan->ramin, 0x0214, upper_32_bits(grctx->linst));
pinstmem->flush(dev);
if (!priv->grctx_vals) {
@@ -210,15 +285,20 @@ nvc0_graph_context_new(struct nouveau_channel *chan, int engine)
for (i = 0; i < priv->grctx_size; i += 4)
nv_wo32(grctx, i, priv->grctx_vals[i / 4]);
- nv_wo32(grctx, 0xf4, 0);
- nv_wo32(grctx, 0xf8, 0);
- nv_wo32(grctx, 0x10, grch->mmio_nr);
- nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->vinst));
- nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->vinst));
- nv_wo32(grctx, 0x1c, 1);
- nv_wo32(grctx, 0x20, 0);
- nv_wo32(grctx, 0x28, 0);
- nv_wo32(grctx, 0x2c, 0);
+ if (!nouveau_ctxfw) {
+ nv_wo32(grctx, 0x00, grch->mmio_nr);
+ nv_wo32(grctx, 0x04, grch->mmio->linst >> 8);
+ } else {
+ nv_wo32(grctx, 0xf4, 0);
+ nv_wo32(grctx, 0xf8, 0);
+ nv_wo32(grctx, 0x10, grch->mmio_nr);
+ nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->linst));
+ nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->linst));
+ nv_wo32(grctx, 0x1c, 1);
+ nv_wo32(grctx, 0x20, 0);
+ nv_wo32(grctx, 0x28, 0);
+ nv_wo32(grctx, 0x2c, 0);
+ }
pinstmem->flush(dev);
return 0;
@@ -248,7 +328,7 @@ nvc0_graph_object_new(struct nouveau_channel *chan, int engine,
}
static int
-nvc0_graph_fini(struct drm_device *dev, int engine)
+nvc0_graph_fini(struct drm_device *dev, int engine, bool suspend)
{
return 0;
}
@@ -296,6 +376,7 @@ static void
nvc0_graph_init_gpc_0(struct drm_device *dev)
{
struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR);
+ const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tp_total);
u32 data[TP_MAX / 8];
u8 tpnr[GPC_MAX];
int i, gpc, tpc;
@@ -307,13 +388,6 @@ nvc0_graph_init_gpc_0(struct drm_device *dev)
* 465: 3/4/4/0 4 7
* 470: 3/3/4/4 5 5
* 480: 3/4/4/4 6 6
- *
- * magicgpc918
- * 450: 00200000 00000000001000000000000000000000
- * 460: 00124925 00000000000100100100100100100101
- * 465: 000ba2e9 00000000000010111010001011101001
- * 470: 00092493 00000000000010010010010010010011
- * 480: 00088889 00000000000010001000100010001001
*/
memset(data, 0x00, sizeof(data));
@@ -336,11 +410,11 @@ nvc0_graph_init_gpc_0(struct drm_device *dev)
nv_wr32(dev, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 |
priv->tp_nr[gpc]);
nv_wr32(dev, GPC_UNIT(gpc, 0x0910), 0x00040000 | priv->tp_total);
- nv_wr32(dev, GPC_UNIT(gpc, 0x0918), priv->magicgpc918);
+ nv_wr32(dev, GPC_UNIT(gpc, 0x0918), magicgpc918);
}
- nv_wr32(dev, GPC_BCAST(0x1bd4), priv->magicgpc918);
- nv_wr32(dev, GPC_BCAST(0x08ac), priv->rop_nr);
+ nv_wr32(dev, GPC_BCAST(0x1bd4), magicgpc918);
+ nv_wr32(dev, GPC_BCAST(0x08ac), nv_rd32(dev, 0x100800));
}
static void
@@ -419,8 +493,51 @@ nvc0_graph_init_fuc(struct drm_device *dev, u32 fuc_base,
static int
nvc0_graph_init_ctxctl(struct drm_device *dev)
{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR);
u32 r000260;
+ int i;
+
+ if (!nouveau_ctxfw) {
+ /* load HUB microcode */
+ r000260 = nv_mask(dev, 0x000260, 0x00000001, 0x00000000);
+ nv_wr32(dev, 0x4091c0, 0x01000000);
+ for (i = 0; i < sizeof(nvc0_grhub_data) / 4; i++)
+ nv_wr32(dev, 0x4091c4, nvc0_grhub_data[i]);
+
+ nv_wr32(dev, 0x409180, 0x01000000);
+ for (i = 0; i < sizeof(nvc0_grhub_code) / 4; i++) {
+ if ((i & 0x3f) == 0)
+ nv_wr32(dev, 0x409188, i >> 6);
+ nv_wr32(dev, 0x409184, nvc0_grhub_code[i]);
+ }
+
+ /* load GPC microcode */
+ nv_wr32(dev, 0x41a1c0, 0x01000000);
+ for (i = 0; i < sizeof(nvc0_grgpc_data) / 4; i++)
+ nv_wr32(dev, 0x41a1c4, nvc0_grgpc_data[i]);
+
+ nv_wr32(dev, 0x41a180, 0x01000000);
+ for (i = 0; i < sizeof(nvc0_grgpc_code) / 4; i++) {
+ if ((i & 0x3f) == 0)
+ nv_wr32(dev, 0x41a188, i >> 6);
+ nv_wr32(dev, 0x41a184, nvc0_grgpc_code[i]);
+ }
+ nv_wr32(dev, 0x000260, r000260);
+
+ /* start HUB ucode running, it'll init the GPCs */
+ nv_wr32(dev, 0x409800, dev_priv->chipset);
+ nv_wr32(dev, 0x40910c, 0x00000000);
+ nv_wr32(dev, 0x409100, 0x00000002);
+ if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) {
+ NV_ERROR(dev, "PGRAPH: HUB_INIT timed out\n");
+ nvc0_graph_ctxctl_debug(dev);
+ return -EBUSY;
+ }
+
+ priv->grctx_size = nv_rd32(dev, 0x409804);
+ return 0;
+ }
/* load fuc microcode */
r000260 = nv_mask(dev, 0x000260, 0x00000001, 0x00000000);
@@ -528,6 +645,22 @@ nvc0_graph_isr_chid(struct drm_device *dev, u64 inst)
}
static void
+nvc0_graph_ctxctl_isr(struct drm_device *dev)
+{
+ u32 ustat = nv_rd32(dev, 0x409c18);
+
+ if (ustat & 0x00000001)
+ NV_INFO(dev, "PGRAPH: CTXCTRL ucode error\n");
+ if (ustat & 0x00080000)
+ NV_INFO(dev, "PGRAPH: CTXCTRL watchdog timeout\n");
+ if (ustat & ~0x00080001)
+ NV_INFO(dev, "PGRAPH: CTXCTRL 0x%08x\n", ustat);
+
+ nvc0_graph_ctxctl_debug(dev);
+ nv_wr32(dev, 0x409c20, ustat);
+}
+
+static void
nvc0_graph_isr(struct drm_device *dev)
{
u64 inst = (u64)(nv_rd32(dev, 0x409b00) & 0x0fffffff) << 12;
@@ -578,11 +711,7 @@ nvc0_graph_isr(struct drm_device *dev)
}
if (stat & 0x00080000) {
- u32 ustat = nv_rd32(dev, 0x409c18);
-
- NV_INFO(dev, "PGRAPH: CTXCTRL ustat 0x%08x\n", ustat);
-
- nv_wr32(dev, 0x409c20, ustat);
+ nvc0_graph_ctxctl_isr(dev);
nv_wr32(dev, 0x400100, 0x00080000);
stat &= ~0x00080000;
}
@@ -595,22 +724,6 @@ nvc0_graph_isr(struct drm_device *dev)
nv_wr32(dev, 0x400500, 0x00010001);
}
-static void
-nvc0_runk140_isr(struct drm_device *dev)
-{
- u32 units = nv_rd32(dev, 0x00017c) & 0x1f;
-
- while (units) {
- u32 unit = ffs(units) - 1;
- u32 reg = 0x140000 + unit * 0x2000;
- u32 st0 = nv_mask(dev, reg + 0x1020, 0, 0);
- u32 st1 = nv_mask(dev, reg + 0x1420, 0, 0);
-
- NV_INFO(dev, "PRUNK140: %d 0x%08x 0x%08x\n", unit, st0, st1);
- units &= ~(1 << unit);
- }
-}
-
static int
nvc0_graph_create_fw(struct drm_device *dev, const char *fwname,
struct nvc0_graph_fuc *fuc)
@@ -651,13 +764,14 @@ nvc0_graph_destroy(struct drm_device *dev, int engine)
{
struct nvc0_graph_priv *priv = nv_engine(dev, engine);
- nvc0_graph_destroy_fw(&priv->fuc409c);
- nvc0_graph_destroy_fw(&priv->fuc409d);
- nvc0_graph_destroy_fw(&priv->fuc41ac);
- nvc0_graph_destroy_fw(&priv->fuc41ad);
+ if (nouveau_ctxfw) {
+ nvc0_graph_destroy_fw(&priv->fuc409c);
+ nvc0_graph_destroy_fw(&priv->fuc409d);
+ nvc0_graph_destroy_fw(&priv->fuc41ac);
+ nvc0_graph_destroy_fw(&priv->fuc41ad);
+ }
nouveau_irq_unregister(dev, 12);
- nouveau_irq_unregister(dev, 25);
nouveau_gpuobj_ref(NULL, &priv->unk4188b8);
nouveau_gpuobj_ref(NULL, &priv->unk4188b4);
@@ -675,13 +789,10 @@ nvc0_graph_create(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvc0_graph_priv *priv;
int ret, gpc, i;
+ u32 fermi;
- switch (dev_priv->chipset) {
- case 0xc0:
- case 0xc3:
- case 0xc4:
- break;
- default:
+ fermi = nvc0_graph_class(dev);
+ if (!fermi) {
NV_ERROR(dev, "PGRAPH: unsupported chipset, please report!\n");
return 0;
}
@@ -699,17 +810,18 @@ nvc0_graph_create(struct drm_device *dev)
NVOBJ_ENGINE_ADD(dev, GR, &priv->base);
nouveau_irq_register(dev, 12, nvc0_graph_isr);
- nouveau_irq_register(dev, 25, nvc0_runk140_isr);
- if (nvc0_graph_create_fw(dev, "fuc409c", &priv->fuc409c) ||
- nvc0_graph_create_fw(dev, "fuc409d", &priv->fuc409d) ||
- nvc0_graph_create_fw(dev, "fuc41ac", &priv->fuc41ac) ||
- nvc0_graph_create_fw(dev, "fuc41ad", &priv->fuc41ad)) {
- ret = 0;
- goto error;
+ if (nouveau_ctxfw) {
+ NV_INFO(dev, "PGRAPH: using external firmware\n");
+ if (nvc0_graph_create_fw(dev, "fuc409c", &priv->fuc409c) ||
+ nvc0_graph_create_fw(dev, "fuc409d", &priv->fuc409d) ||
+ nvc0_graph_create_fw(dev, "fuc41ac", &priv->fuc41ac) ||
+ nvc0_graph_create_fw(dev, "fuc41ad", &priv->fuc41ad)) {
+ ret = 0;
+ goto error;
+ }
}
-
ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b4);
if (ret)
goto error;
@@ -735,25 +847,31 @@ nvc0_graph_create(struct drm_device *dev)
case 0xc0:
if (priv->tp_total == 11) { /* 465, 3/4/4/0, 4 */
priv->magic_not_rop_nr = 0x07;
- /* filled values up to tp_total, the rest 0 */
- priv->magicgpc918 = 0x000ba2e9;
} else
if (priv->tp_total == 14) { /* 470, 3/3/4/4, 5 */
priv->magic_not_rop_nr = 0x05;
- priv->magicgpc918 = 0x00092493;
} else
if (priv->tp_total == 15) { /* 480, 3/4/4/4, 6 */
priv->magic_not_rop_nr = 0x06;
- priv->magicgpc918 = 0x00088889;
}
break;
case 0xc3: /* 450, 4/0/0/0, 2 */
priv->magic_not_rop_nr = 0x03;
- priv->magicgpc918 = 0x00200000;
break;
case 0xc4: /* 460, 3/4/0/0, 4 */
priv->magic_not_rop_nr = 0x01;
- priv->magicgpc918 = 0x00124925;
+ break;
+ case 0xc1: /* 2/0/0/0, 1 */
+ priv->magic_not_rop_nr = 0x01;
+ break;
+ case 0xc8: /* 4/4/3/4, 5 */
+ priv->magic_not_rop_nr = 0x06;
+ break;
+ case 0xce: /* 4/4/0/0, 4 */
+ priv->magic_not_rop_nr = 0x03;
+ break;
+ case 0xcf: /* 4/0/0/0, 3 */
+ priv->magic_not_rop_nr = 0x03;
break;
}
@@ -763,13 +881,16 @@ nvc0_graph_create(struct drm_device *dev)
priv->tp_nr[3], priv->rop_nr);
/* use 0xc3's values... */
priv->magic_not_rop_nr = 0x03;
- priv->magicgpc918 = 0x00200000;
}
NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */
NVOBJ_CLASS(dev, 0x9039, GR); /* M2MF */
NVOBJ_MTHD (dev, 0x9039, 0x0500, nvc0_graph_mthd_page_flip);
NVOBJ_CLASS(dev, 0x9097, GR); /* 3D */
+ if (fermi >= 0x9197)
+ NVOBJ_CLASS(dev, 0x9197, GR); /* 3D (NVC1-) */
+ if (fermi >= 0x9297)
+ NVOBJ_CLASS(dev, 0x9297, GR); /* 3D (NVC8-) */
NVOBJ_CLASS(dev, 0x90c0, GR); /* COMPUTE */
return 0;
@@ -777,20 +898,3 @@ error:
nvc0_graph_destroy(dev, NVOBJ_ENGINE_GR);
return ret;
}
-
-MODULE_FIRMWARE("nouveau/nvc0_fuc409c");
-MODULE_FIRMWARE("nouveau/nvc0_fuc409d");
-MODULE_FIRMWARE("nouveau/nvc0_fuc41ac");
-MODULE_FIRMWARE("nouveau/nvc0_fuc41ad");
-MODULE_FIRMWARE("nouveau/nvc3_fuc409c");
-MODULE_FIRMWARE("nouveau/nvc3_fuc409d");
-MODULE_FIRMWARE("nouveau/nvc3_fuc41ac");
-MODULE_FIRMWARE("nouveau/nvc3_fuc41ad");
-MODULE_FIRMWARE("nouveau/nvc4_fuc409c");
-MODULE_FIRMWARE("nouveau/nvc4_fuc409d");
-MODULE_FIRMWARE("nouveau/nvc4_fuc41ac");
-MODULE_FIRMWARE("nouveau/nvc4_fuc41ad");
-MODULE_FIRMWARE("nouveau/fuc409c");
-MODULE_FIRMWARE("nouveau/fuc409d");
-MODULE_FIRMWARE("nouveau/fuc41ac");
-MODULE_FIRMWARE("nouveau/fuc41ad");
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.fuc b/drivers/gpu/drm/nouveau/nvc0_graph.fuc
new file mode 100644
index 000000000000..2a4b6dc8f9de
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.fuc
@@ -0,0 +1,400 @@
+/* fuc microcode util functions for nvc0 PGRAPH
+ *
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+define(`mmctx_data', `.b32 eval((($2 - 1) << 26) | $1)')
+define(`queue_init', `.skip eval((2 * 4) + ((8 * 4) * 2))')
+
+ifdef(`include_code', `
+// Error codes
+define(`E_BAD_COMMAND', 0x01)
+define(`E_CMD_OVERFLOW', 0x02)
+
+// Util macros to help with debugging ucode hangs etc
+define(`T_WAIT', 0)
+define(`T_MMCTX', 1)
+define(`T_STRWAIT', 2)
+define(`T_STRINIT', 3)
+define(`T_AUTO', 4)
+define(`T_CHAN', 5)
+define(`T_LOAD', 6)
+define(`T_SAVE', 7)
+define(`T_LCHAN', 8)
+define(`T_LCTXH', 9)
+
+define(`trace_set', `
+ mov $r8 0x83c
+ shl b32 $r8 6
+ clear b32 $r9
+ bset $r9 $1
+ iowr I[$r8 + 0x000] $r9 // CC_SCRATCH[7]
+')
+
+define(`trace_clr', `
+ mov $r8 0x85c
+ shl b32 $r8 6
+ clear b32 $r9
+ bset $r9 $1
+ iowr I[$r8 + 0x000] $r9 // CC_SCRATCH[7]
+')
+
+// queue_put - add request to queue
+//
+// In : $r13 queue pointer
+// $r14 command
+// $r15 data
+//
+queue_put:
+ // make sure we have space..
+ ld b32 $r8 D[$r13 + 0x0] // GET
+ ld b32 $r9 D[$r13 + 0x4] // PUT
+ xor $r8 8
+ cmpu b32 $r8 $r9
+ bra ne queue_put_next
+ mov $r15 E_CMD_OVERFLOW
+ call error
+ ret
+
+ // store cmd/data on queue
+ queue_put_next:
+ and $r8 $r9 7
+ shl b32 $r8 3
+ add b32 $r8 $r13
+ add b32 $r8 8
+ st b32 D[$r8 + 0x0] $r14
+ st b32 D[$r8 + 0x4] $r15
+
+ // update PUT
+ add b32 $r9 1
+ and $r9 0xf
+ st b32 D[$r13 + 0x4] $r9
+ ret
+
+// queue_get - fetch request from queue
+//
+// In : $r13 queue pointer
+//
+// Out: $p1 clear on success (data available)
+// $r14 command
+// $r15 data
+//
+queue_get:
+ bset $flags $p1
+ ld b32 $r8 D[$r13 + 0x0] // GET
+ ld b32 $r9 D[$r13 + 0x4] // PUT
+ cmpu b32 $r8 $r9
+ bra e queue_get_done
+ // fetch first cmd/data pair
+ and $r9 $r8 7
+ shl b32 $r9 3
+ add b32 $r9 $r13
+ add b32 $r9 8
+ ld b32 $r14 D[$r9 + 0x0]
+ ld b32 $r15 D[$r9 + 0x4]
+
+ // update GET
+ add b32 $r8 1
+ and $r8 0xf
+ st b32 D[$r13 + 0x0] $r8
+ bclr $flags $p1
+queue_get_done:
+ ret
+
+// nv_rd32 - read 32-bit value from nv register
+//
+// In : $r14 register
+// Out: $r15 value
+//
+nv_rd32:
+ mov $r11 0x728
+ shl b32 $r11 6
+ mov b32 $r12 $r14
+ bset $r12 31 // MMIO_CTRL_PENDING
+ iowr I[$r11 + 0x000] $r12 // MMIO_CTRL
+ nv_rd32_wait:
+ iord $r12 I[$r11 + 0x000]
+ xbit $r12 $r12 31
+ bra ne nv_rd32_wait
+ mov $r10 6 // DONE_MMIO_RD
+ call wait_doneo
+ iord $r15 I[$r11 + 0x100] // MMIO_RDVAL
+ ret
+
+// nv_wr32 - write 32-bit value to nv register
+//
+// In : $r14 register
+// $r15 value
+//
+nv_wr32:
+ mov $r11 0x728
+ shl b32 $r11 6
+ iowr I[$r11 + 0x200] $r15 // MMIO_WRVAL
+ mov b32 $r12 $r14
+ bset $r12 31 // MMIO_CTRL_PENDING
+ bset $r12 30 // MMIO_CTRL_WRITE
+ iowr I[$r11 + 0x000] $r12 // MMIO_CTRL
+ nv_wr32_wait:
+ iord $r12 I[$r11 + 0x000]
+ xbit $r12 $r12 31
+ bra ne nv_wr32_wait
+ ret
+
+// (re)set watchdog timer
+//
+// In : $r15 timeout
+//
+watchdog_reset:
+ mov $r8 0x430
+ shl b32 $r8 6
+ bset $r15 31
+ iowr I[$r8 + 0x000] $r15
+ ret
+
+// clear watchdog timer
+watchdog_clear:
+ mov $r8 0x430
+ shl b32 $r8 6
+ iowr I[$r8 + 0x000] $r0
+ ret
+
+// wait_done{z,o} - wait on FUC_DONE bit to become clear/set
+//
+// In : $r10 bit to wait on
+//
+define(`wait_done', `
+$1:
+ trace_set(T_WAIT);
+ mov $r8 0x818
+ shl b32 $r8 6
+ iowr I[$r8 + 0x000] $r10 // CC_SCRATCH[6] = wait bit
+ wait_done_$1:
+ mov $r8 0x400
+ shl b32 $r8 6
+ iord $r8 I[$r8 + 0x000] // DONE
+ xbit $r8 $r8 $r10
+ bra $2 wait_done_$1
+ trace_clr(T_WAIT)
+ ret
+')
+wait_done(wait_donez, ne)
+wait_done(wait_doneo, e)
+
+// mmctx_size - determine size of a mmio list transfer
+//
+// In : $r14 mmio list head
+// $r15 mmio list tail
+// Out: $r15 transfer size (in bytes)
+//
+mmctx_size:
+ clear b32 $r9
+ nv_mmctx_size_loop:
+ ld b32 $r8 D[$r14]
+ shr b32 $r8 26
+ add b32 $r8 1
+ shl b32 $r8 2
+ add b32 $r9 $r8
+ add b32 $r14 4
+ cmpu b32 $r14 $r15
+ bra ne nv_mmctx_size_loop
+ mov b32 $r15 $r9
+ ret
+
+// mmctx_xfer - execute a list of mmio transfers
+//
+// In : $r10 flags
+// bit 0: direction (0 = save, 1 = load)
+// bit 1: set if first transfer
+// bit 2: set if last transfer
+// $r11 base
+// $r12 mmio list head
+// $r13 mmio list tail
+// $r14 multi_stride
+// $r15 multi_mask
+//
+mmctx_xfer:
+ trace_set(T_MMCTX)
+ mov $r8 0x710
+ shl b32 $r8 6
+ clear b32 $r9
+ or $r11 $r11
+ bra e mmctx_base_disabled
+ iowr I[$r8 + 0x000] $r11 // MMCTX_BASE
+ bset $r9 0 // BASE_EN
+ mmctx_base_disabled:
+ or $r14 $r14
+ bra e mmctx_multi_disabled
+ iowr I[$r8 + 0x200] $r14 // MMCTX_MULTI_STRIDE
+ iowr I[$r8 + 0x300] $r15 // MMCTX_MULTI_MASK
+ bset $r9 1 // MULTI_EN
+ mmctx_multi_disabled:
+ add b32 $r8 0x100
+
+ xbit $r11 $r10 0
+ shl b32 $r11 16 // DIR
+ bset $r11 12 // QLIMIT = 0x10
+ xbit $r14 $r10 1
+ shl b32 $r14 17
+ or $r11 $r14 // START_TRIGGER
+ iowr I[$r8 + 0x000] $r11 // MMCTX_CTRL
+
+ // loop over the mmio list, and send requests to the hw
+ mmctx_exec_loop:
+ // wait for space in mmctx queue
+ mmctx_wait_free:
+ iord $r14 I[$r8 + 0x000] // MMCTX_CTRL
+ and $r14 0x1f
+ bra e mmctx_wait_free
+
+ // queue up an entry
+ ld b32 $r14 D[$r12]
+ or $r14 $r9
+ iowr I[$r8 + 0x300] $r14
+ add b32 $r12 4
+ cmpu b32 $r12 $r13
+ bra ne mmctx_exec_loop
+
+ xbit $r11 $r10 2
+ bra ne mmctx_stop
+ // wait for queue to empty
+ mmctx_fini_wait:
+ iord $r11 I[$r8 + 0x000] // MMCTX_CTRL
+ and $r11 0x1f
+ cmpu b32 $r11 0x10
+ bra ne mmctx_fini_wait
+ mov $r10 2 // DONE_MMCTX
+ call wait_donez
+ bra mmctx_done
+ mmctx_stop:
+ xbit $r11 $r10 0
+ shl b32 $r11 16 // DIR
+ bset $r11 12 // QLIMIT = 0x10
+ bset $r11 18 // STOP_TRIGGER
+ iowr I[$r8 + 0x000] $r11 // MMCTX_CTRL
+ mmctx_stop_wait:
+ // wait for STOP_TRIGGER to clear
+ iord $r11 I[$r8 + 0x000] // MMCTX_CTRL
+ xbit $r11 $r11 18
+ bra ne mmctx_stop_wait
+ mmctx_done:
+ trace_clr(T_MMCTX)
+ ret
+
+// Wait for DONE_STRAND
+//
+strand_wait:
+ push $r10
+ mov $r10 2
+ call wait_donez
+ pop $r10
+ ret
+
+// unknown - call before issuing strand commands
+//
+strand_pre:
+ mov $r8 0x4afc
+ sethi $r8 0x20000
+ mov $r9 0xc
+ iowr I[$r8] $r9
+ call strand_wait
+ ret
+
+// unknown - call after issuing strand commands
+//
+strand_post:
+ mov $r8 0x4afc
+ sethi $r8 0x20000
+ mov $r9 0xd
+ iowr I[$r8] $r9
+ call strand_wait
+ ret
+
+// Selects strand set?!
+//
+// In: $r14 id
+//
+strand_set:
+ mov $r10 0x4ffc
+ sethi $r10 0x20000
+ sub b32 $r11 $r10 0x500
+ mov $r12 0xf
+ iowr I[$r10 + 0x000] $r12 // 0x93c = 0xf
+ mov $r12 0xb
+ iowr I[$r11 + 0x000] $r12 // 0x928 = 0xb
+ call strand_wait
+ iowr I[$r10 + 0x000] $r14 // 0x93c = <id>
+ mov $r12 0xa
+ iowr I[$r11 + 0x000] $r12 // 0x928 = 0xa
+ call strand_wait
+ ret
+
+// Initialise strand context data
+//
+// In : $r15 context base
+// Out: $r15 context size (in bytes)
+//
+// Strandset(?) 3 hardcoded currently
+//
+strand_ctx_init:
+ trace_set(T_STRINIT)
+ call strand_pre
+ mov $r14 3
+ call strand_set
+ mov $r10 0x46fc
+ sethi $r10 0x20000
+ add b32 $r11 $r10 0x400
+ iowr I[$r10 + 0x100] $r0 // STRAND_FIRST_GENE = 0
+ mov $r12 1
+ iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_FIRST_GENE
+ call strand_wait
+ sub b32 $r12 $r0 1
+ iowr I[$r10 + 0x000] $r12 // STRAND_GENE_CNT = 0xffffffff
+ mov $r12 2
+ iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_GENE_CNT
+ call strand_wait
+ call strand_post
+
+ // read the size of each strand, poke the context offset of
+ // each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry
+ // about it later then.
+ mov $r8 0x880
+ shl b32 $r8 6
+ iord $r9 I[$r8 + 0x000] // STRANDS
+ add b32 $r8 0x2200
+ shr b32 $r14 $r15 8
+ ctx_init_strand_loop:
+ iowr I[$r8 + 0x000] $r14 // STRAND_SAVE_SWBASE
+ iowr I[$r8 + 0x100] $r14 // STRAND_LOAD_SWBASE
+ iord $r10 I[$r8 + 0x200] // STRAND_SIZE
+ shr b32 $r10 6
+ add b32 $r10 1
+ add b32 $r14 $r10
+ add b32 $r8 4
+ sub b32 $r9 1
+ bra ne ctx_init_strand_loop
+
+ shl b32 $r14 8
+ sub b32 $r15 $r14 $r15
+ trace_clr(T_STRINIT)
+ ret
+')
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.h b/drivers/gpu/drm/nouveau/nvc0_graph.h
index f5d184e0689d..636fe9812f79 100644
--- a/drivers/gpu/drm/nouveau/nvc0_graph.h
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.h
@@ -57,8 +57,7 @@ struct nvc0_graph_priv {
struct nouveau_gpuobj *unk4188b4;
struct nouveau_gpuobj *unk4188b8;
- u8 magic_not_rop_nr;
- u32 magicgpc918;
+ u8 magic_not_rop_nr;
};
struct nvc0_graph_chan {
@@ -72,4 +71,26 @@ struct nvc0_graph_chan {
int nvc0_grctx_generate(struct nouveau_channel *);
+/* nvc0_graph.c uses this also to determine supported chipsets */
+static inline u32
+nvc0_graph_class(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ switch (dev_priv->chipset) {
+ case 0xc0:
+ case 0xc3:
+ case 0xc4:
+ case 0xce: /* guess, mmio trace shows only 0x9097 state */
+ case 0xcf: /* guess, mmio trace shows only 0x9097 state */
+ return 0x9097;
+ case 0xc1:
+ return 0x9197;
+ case 0xc8:
+ return 0x9297;
+ default:
+ return 0;
+ }
+}
+
#endif
diff --git a/drivers/gpu/drm/nouveau/nvc0_grctx.c b/drivers/gpu/drm/nouveau/nvc0_grctx.c
index 6df066114133..96b0b93d94ca 100644
--- a/drivers/gpu/drm/nouveau/nvc0_grctx.c
+++ b/drivers/gpu/drm/nouveau/nvc0_grctx.c
@@ -45,6 +45,9 @@ nv_mthd(struct drm_device *dev, u32 class, u32 mthd, u32 data)
static void
nvc0_grctx_generate_9097(struct drm_device *dev)
{
+ u32 fermi = nvc0_graph_class(dev);
+ u32 mthd;
+
nv_mthd(dev, 0x9097, 0x0800, 0x00000000);
nv_mthd(dev, 0x9097, 0x0840, 0x00000000);
nv_mthd(dev, 0x9097, 0x0880, 0x00000000);
@@ -824,134 +827,10 @@ nvc0_grctx_generate_9097(struct drm_device *dev)
nv_mthd(dev, 0x9097, 0x1eb8, 0x00000001);
nv_mthd(dev, 0x9097, 0x1ed8, 0x00000001);
nv_mthd(dev, 0x9097, 0x1ef8, 0x00000001);
- nv_mthd(dev, 0x9097, 0x3400, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3404, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3408, 0x00000000);
- nv_mthd(dev, 0x9097, 0x340c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3410, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3414, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3418, 0x00000000);
- nv_mthd(dev, 0x9097, 0x341c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3420, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3424, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3428, 0x00000000);
- nv_mthd(dev, 0x9097, 0x342c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3430, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3434, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3438, 0x00000000);
- nv_mthd(dev, 0x9097, 0x343c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3440, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3444, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3448, 0x00000000);
- nv_mthd(dev, 0x9097, 0x344c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3450, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3454, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3458, 0x00000000);
- nv_mthd(dev, 0x9097, 0x345c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3460, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3464, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3468, 0x00000000);
- nv_mthd(dev, 0x9097, 0x346c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3470, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3474, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3478, 0x00000000);
- nv_mthd(dev, 0x9097, 0x347c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3480, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3484, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3488, 0x00000000);
- nv_mthd(dev, 0x9097, 0x348c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3490, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3494, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3498, 0x00000000);
- nv_mthd(dev, 0x9097, 0x349c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34a0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34a4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34a8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34ac, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34b0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34b4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34b8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34bc, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34c0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34c4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34c8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34cc, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34d0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34d4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34d8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34dc, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34e0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34e4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34e8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34ec, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34f0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34f4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34f8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x34fc, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3500, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3504, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3508, 0x00000000);
- nv_mthd(dev, 0x9097, 0x350c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3510, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3514, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3518, 0x00000000);
- nv_mthd(dev, 0x9097, 0x351c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3520, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3524, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3528, 0x00000000);
- nv_mthd(dev, 0x9097, 0x352c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3530, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3534, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3538, 0x00000000);
- nv_mthd(dev, 0x9097, 0x353c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3540, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3544, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3548, 0x00000000);
- nv_mthd(dev, 0x9097, 0x354c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3550, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3554, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3558, 0x00000000);
- nv_mthd(dev, 0x9097, 0x355c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3560, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3564, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3568, 0x00000000);
- nv_mthd(dev, 0x9097, 0x356c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3570, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3574, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3578, 0x00000000);
- nv_mthd(dev, 0x9097, 0x357c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3580, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3584, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3588, 0x00000000);
- nv_mthd(dev, 0x9097, 0x358c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3590, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3594, 0x00000000);
- nv_mthd(dev, 0x9097, 0x3598, 0x00000000);
- nv_mthd(dev, 0x9097, 0x359c, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35a0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35a4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35a8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35ac, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35b0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35b4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35b8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35bc, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35c0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35c4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35c8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35cc, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35d0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35d4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35d8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35dc, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35e0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35e4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35e8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35ec, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35f0, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35f4, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35f8, 0x00000000);
- nv_mthd(dev, 0x9097, 0x35fc, 0x00000000);
+ if (fermi == 0x9097) {
+ for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4)
+ nv_mthd(dev, 0x9097, mthd, 0x00000000);
+ }
nv_mthd(dev, 0x9097, 0x030c, 0x00000001);
nv_mthd(dev, 0x9097, 0x1944, 0x00000000);
nv_mthd(dev, 0x9097, 0x1514, 0x00000000);
@@ -1321,6 +1200,37 @@ nvc0_grctx_generate_9097(struct drm_device *dev)
}
static void
+nvc0_grctx_generate_9197(struct drm_device *dev)
+{
+ u32 fermi = nvc0_graph_class(dev);
+ u32 mthd;
+
+ if (fermi == 0x9197) {
+ for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4)
+ nv_mthd(dev, 0x9197, mthd, 0x00000000);
+ }
+ nv_mthd(dev, 0x9197, 0x02e4, 0x0000b001);
+}
+
+static void
+nvc0_grctx_generate_9297(struct drm_device *dev)
+{
+ u32 fermi = nvc0_graph_class(dev);
+ u32 mthd;
+
+ if (fermi == 0x9297) {
+ for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4)
+ nv_mthd(dev, 0x9297, mthd, 0x00000000);
+ }
+ nv_mthd(dev, 0x9297, 0x036c, 0x00000000);
+ nv_mthd(dev, 0x9297, 0x0370, 0x00000000);
+ nv_mthd(dev, 0x9297, 0x07a4, 0x00000000);
+ nv_mthd(dev, 0x9297, 0x07a8, 0x00000000);
+ nv_mthd(dev, 0x9297, 0x0374, 0x00000000);
+ nv_mthd(dev, 0x9297, 0x0378, 0x00000020);
+}
+
+static void
nvc0_grctx_generate_902d(struct drm_device *dev)
{
nv_mthd(dev, 0x902d, 0x0200, 0x000000cf);
@@ -1559,8 +1469,15 @@ nvc0_grctx_generate_unk47xx(struct drm_device *dev)
static void
nvc0_grctx_generate_shaders(struct drm_device *dev)
{
- nv_wr32(dev, 0x405800, 0x078000bf);
- nv_wr32(dev, 0x405830, 0x02180000);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->chipset != 0xc1) {
+ nv_wr32(dev, 0x405800, 0x078000bf);
+ nv_wr32(dev, 0x405830, 0x02180000);
+ } else {
+ nv_wr32(dev, 0x405800, 0x0f8000bf);
+ nv_wr32(dev, 0x405830, 0x02180218);
+ }
nv_wr32(dev, 0x405834, 0x00000000);
nv_wr32(dev, 0x405838, 0x00000000);
nv_wr32(dev, 0x405854, 0x00000000);
@@ -1586,10 +1503,16 @@ nvc0_grctx_generate_unk60xx(struct drm_device *dev)
static void
nvc0_grctx_generate_unk64xx(struct drm_device *dev)
{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
nv_wr32(dev, 0x4064a8, 0x00000000);
nv_wr32(dev, 0x4064ac, 0x00003fff);
nv_wr32(dev, 0x4064b4, 0x00000000);
nv_wr32(dev, 0x4064b8, 0x00000000);
+ if (dev_priv->chipset == 0xc1) {
+ nv_wr32(dev, 0x4064c0, 0x80140078);
+ nv_wr32(dev, 0x4064c4, 0x0086ffff);
+ }
}
static void
@@ -1622,21 +1545,14 @@ static void
nvc0_grctx_generate_rop(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int chipset = dev_priv->chipset;
/* ROPC_BROADCAST */
nv_wr32(dev, 0x408800, 0x02802a3c);
nv_wr32(dev, 0x408804, 0x00000040);
- nv_wr32(dev, 0x408808, 0x0003e00d);
- switch (dev_priv->chipset) {
- case 0xc0:
- nv_wr32(dev, 0x408900, 0x0080b801);
- break;
- case 0xc3:
- case 0xc4:
- nv_wr32(dev, 0x408900, 0x3080b801);
- break;
- }
- nv_wr32(dev, 0x408904, 0x02000001);
+ nv_wr32(dev, 0x408808, chipset != 0xc1 ? 0x0003e00d : 0x1003e005);
+ nv_wr32(dev, 0x408900, 0x3080b801);
+ nv_wr32(dev, 0x408904, chipset != 0xc1 ? 0x02000001 : 0x62000001);
nv_wr32(dev, 0x408908, 0x00c80929);
nv_wr32(dev, 0x40890c, 0x00000000);
nv_wr32(dev, 0x408980, 0x0000011d);
@@ -1645,6 +1561,8 @@ nvc0_grctx_generate_rop(struct drm_device *dev)
static void
nvc0_grctx_generate_gpc(struct drm_device *dev)
{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int chipset = dev_priv->chipset;
int i;
/* GPC_BROADCAST */
@@ -1676,7 +1594,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev)
nv_wr32(dev, 0x41880c, 0x00000000);
nv_wr32(dev, 0x418810, 0x00000000);
nv_wr32(dev, 0x418828, 0x00008442);
- nv_wr32(dev, 0x418830, 0x00000001);
+ nv_wr32(dev, 0x418830, chipset != 0xc1 ? 0x00000001 : 0x10000001);
nv_wr32(dev, 0x4188d8, 0x00000008);
nv_wr32(dev, 0x4188e0, 0x01000000);
nv_wr32(dev, 0x4188e8, 0x00000000);
@@ -1684,7 +1602,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev)
nv_wr32(dev, 0x4188f0, 0x00000000);
nv_wr32(dev, 0x4188f4, 0x00000000);
nv_wr32(dev, 0x4188f8, 0x00000000);
- nv_wr32(dev, 0x4188fc, 0x00100000);
+ nv_wr32(dev, 0x4188fc, chipset != 0xc1 ? 0x00100000 : 0x00100018);
nv_wr32(dev, 0x41891c, 0x00ff00ff);
nv_wr32(dev, 0x418924, 0x00000000);
nv_wr32(dev, 0x418928, 0x00ffff00);
@@ -1715,6 +1633,8 @@ nvc0_grctx_generate_gpc(struct drm_device *dev)
nv_wr32(dev, 0x418c24, 0x00000000);
nv_wr32(dev, 0x418c28, 0x00000000);
nv_wr32(dev, 0x418c2c, 0x00000000);
+ if (chipset == 0xc1)
+ nv_wr32(dev, 0x418c6c, 0x00000001);
nv_wr32(dev, 0x418c80, 0x20200004);
nv_wr32(dev, 0x418c8c, 0x00000001);
nv_wr32(dev, 0x419000, 0x00000780);
@@ -1727,10 +1647,13 @@ static void
nvc0_grctx_generate_tp(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int chipset = dev_priv->chipset;
/* GPC_BROADCAST.TP_BROADCAST */
+ nv_wr32(dev, 0x419818, 0x00000000);
+ nv_wr32(dev, 0x41983c, 0x00038bc7);
nv_wr32(dev, 0x419848, 0x00000000);
- nv_wr32(dev, 0x419864, 0x0000012a);
+ nv_wr32(dev, 0x419864, chipset != 0xc1 ? 0x0000012a : 0x00000129);
nv_wr32(dev, 0x419888, 0x00000000);
nv_wr32(dev, 0x419a00, 0x000001f0);
nv_wr32(dev, 0x419a04, 0x00000001);
@@ -1740,8 +1663,8 @@ nvc0_grctx_generate_tp(struct drm_device *dev)
nv_wr32(dev, 0x419a14, 0x00000200);
nv_wr32(dev, 0x419a1c, 0x00000000);
nv_wr32(dev, 0x419a20, 0x00000800);
- if (dev_priv->chipset != 0xc0)
- nv_wr32(dev, 0x00419ac4, 0x0007f440); /* 0xc3 */
+ if (chipset != 0xc0 && chipset != 0xc8)
+ nv_wr32(dev, 0x00419ac4, 0x0007f440);
nv_wr32(dev, 0x419b00, 0x0a418820);
nv_wr32(dev, 0x419b04, 0x062080e6);
nv_wr32(dev, 0x419b08, 0x020398a4);
@@ -1749,17 +1672,22 @@ nvc0_grctx_generate_tp(struct drm_device *dev)
nv_wr32(dev, 0x419b10, 0x0a418820);
nv_wr32(dev, 0x419b14, 0x000000e6);
nv_wr32(dev, 0x419bd0, 0x00900103);
- nv_wr32(dev, 0x419be0, 0x00000001);
+ nv_wr32(dev, 0x419be0, chipset != 0xc1 ? 0x00000001 : 0x00400001);
nv_wr32(dev, 0x419be4, 0x00000000);
nv_wr32(dev, 0x419c00, 0x00000002);
nv_wr32(dev, 0x419c04, 0x00000006);
nv_wr32(dev, 0x419c08, 0x00000002);
nv_wr32(dev, 0x419c20, 0x00000000);
- nv_wr32(dev, 0x419cbc, 0x28137606);
+ if (chipset == 0xce || chipset == 0xcf)
+ nv_wr32(dev, 0x419cb0, 0x00020048);
+ else
+ nv_wr32(dev, 0x419cb0, 0x00060048);
nv_wr32(dev, 0x419ce8, 0x00000000);
nv_wr32(dev, 0x419cf4, 0x00000183);
- nv_wr32(dev, 0x419d20, 0x02180000);
+ nv_wr32(dev, 0x419d20, chipset != 0xc1 ? 0x02180000 : 0x12180000);
nv_wr32(dev, 0x419d24, 0x00001fff);
+ if (chipset == 0xc1)
+ nv_wr32(dev, 0x419d44, 0x02180218);
nv_wr32(dev, 0x419e04, 0x00000000);
nv_wr32(dev, 0x419e08, 0x00000000);
nv_wr32(dev, 0x419e0c, 0x00000000);
@@ -1785,11 +1713,11 @@ nvc0_grctx_generate_tp(struct drm_device *dev)
nv_wr32(dev, 0x419e8c, 0x00000000);
nv_wr32(dev, 0x419e90, 0x00000000);
nv_wr32(dev, 0x419e98, 0x00000000);
- if (dev_priv->chipset != 0xc0)
+ if (chipset != 0xc0 && chipset != 0xc8)
nv_wr32(dev, 0x419ee0, 0x00011110);
nv_wr32(dev, 0x419f50, 0x00000000);
nv_wr32(dev, 0x419f54, 0x00000000);
- if (dev_priv->chipset != 0xc0)
+ if (chipset != 0xc0 && chipset != 0xc8)
nv_wr32(dev, 0x419f58, 0x00000000);
}
@@ -1801,6 +1729,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR];
struct drm_device *dev = chan->dev;
int i, gpc, tp, id;
+ u32 fermi = nvc0_graph_class(dev);
u32 r000260, tmp;
r000260 = nv_rd32(dev, 0x000260);
@@ -1857,10 +1786,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
nv_wr32(dev, 0x40587c, 0x00000000);
if (1) {
- const u8 chipset_tp_max[] = { 16, 0, 0, 4, 8 };
- u8 max = chipset_tp_max[dev_priv->chipset & 0x0f];
- u8 tpnr[GPC_MAX];
- u8 data[32];
+ u8 tpnr[GPC_MAX], data[TP_MAX];
memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr));
memset(data, 0x1f, sizeof(data));
@@ -1874,7 +1800,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
data[tp] = gpc;
}
- for (i = 0; i < max / 4; i++)
+ for (i = 0; i < 4; i++)
nv_wr32(dev, 0x4060a8 + (i * 4), ((u32 *)data)[i]);
}
@@ -1886,6 +1812,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
/* calculate first set of magics */
memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr));
+ gpc = -1;
for (tp = 0; tp < priv->tp_total; tp++) {
do {
gpc = (gpc + 1) % priv->gpc_nr;
@@ -1935,30 +1862,26 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
if (1) {
u32 tp_mask = 0, tp_set = 0;
- u8 tpnr[GPC_MAX];
+ u8 tpnr[GPC_MAX], a, b;
memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr));
for (gpc = 0; gpc < priv->gpc_nr; gpc++)
tp_mask |= ((1 << priv->tp_nr[gpc]) - 1) << (gpc * 8);
- gpc = -1;
- for (i = 0, gpc = -1; i < 32; i++) {
- int ltp = i * (priv->tp_total - 1) / 32;
+ for (i = 0, gpc = -1, b = -1; i < 32; i++) {
+ a = (i * (priv->tp_total - 1)) / 32;
+ if (a != b) {
+ b = a;
+ do {
+ gpc = (gpc + 1) % priv->gpc_nr;
+ } while (!tpnr[gpc]);
+ tp = priv->tp_nr[gpc] - tpnr[gpc]--;
- do {
- gpc = (gpc + 1) % priv->gpc_nr;
- } while (!tpnr[gpc]);
- tp = priv->tp_nr[gpc] - tpnr[gpc]--;
-
- tp_set |= 1 << ((gpc * 8) + tp);
+ tp_set |= 1 << ((gpc * 8) + tp);
+ }
- do {
- nv_wr32(dev, 0x406800 + (i * 0x20), tp_set);
- tp_set ^= tp_mask;
- nv_wr32(dev, 0x406c00 + (i * 0x20), tp_set);
- tp_set ^= tp_mask;
- } while (ltp == (++i * (priv->tp_total - 1) / 32));
- i--;
+ nv_wr32(dev, 0x406800 + (i * 0x20), tp_set);
+ nv_wr32(dev, 0x406c00 + (i * 0x20), tp_set ^ tp_mask);
}
}
@@ -2633,6 +2556,8 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
nv_icmd(dev, 0x0000053f, 0xffff0000);
nv_icmd(dev, 0x00000585, 0x0000003f);
nv_icmd(dev, 0x00000576, 0x00000003);
+ if (dev_priv->chipset == 0xc1)
+ nv_icmd(dev, 0x0000057b, 0x00000059);
nv_icmd(dev, 0x00000586, 0x00000040);
nv_icmd(dev, 0x00000582, 0x00000080);
nv_icmd(dev, 0x00000583, 0x00000080);
@@ -2865,6 +2790,10 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
nv_wr32(dev, 0x404154, 0x00000400);
nvc0_grctx_generate_9097(dev);
+ if (fermi >= 0x9197)
+ nvc0_grctx_generate_9197(dev);
+ if (fermi >= 0x9297)
+ nvc0_grctx_generate_9297(dev);
nvc0_grctx_generate_902d(dev);
nvc0_grctx_generate_9039(dev);
nvc0_grctx_generate_90c0(dev);
diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc
new file mode 100644
index 000000000000..06f5e26d1e0f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc
@@ -0,0 +1,480 @@
+/* fuc microcode for nvc0 PGRAPH/GPC
+ *
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+/* To build:
+ * m4 nvc0_grgpc.fuc | envyas -a -w -m fuc -V nva3 -o nvc0_grgpc.fuc.h
+ */
+
+/* TODO
+ * - bracket certain functions with scratch writes, useful for debugging
+ * - watchdog timer around ctx operations
+ */
+
+.section nvc0_grgpc_data
+include(`nvc0_graph.fuc')
+gpc_id: .b32 0
+gpc_mmio_list_head: .b32 0
+gpc_mmio_list_tail: .b32 0
+
+tpc_count: .b32 0
+tpc_mask: .b32 0
+tpc_mmio_list_head: .b32 0
+tpc_mmio_list_tail: .b32 0
+
+cmd_queue: queue_init
+
+// chipset descriptions
+chipsets:
+.b8 0xc0 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc0_tpc_mmio_tail
+.b8 0xc1 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc1_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc1_tpc_mmio_tail
+.b8 0xc3 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc3_tpc_mmio_tail
+.b8 0xc4 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc3_tpc_mmio_tail
+.b8 0xc8 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc0_tpc_mmio_tail
+.b8 0xce 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc3_tpc_mmio_tail
+.b8 0xcf 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvcf_tpc_mmio_tail
+.b8 0 0 0 0
+
+// GPC mmio lists
+nvc0_gpc_mmio_head:
+mmctx_data(0x000380, 1)
+mmctx_data(0x000400, 6)
+mmctx_data(0x000450, 9)
+mmctx_data(0x000600, 1)
+mmctx_data(0x000684, 1)
+mmctx_data(0x000700, 5)
+mmctx_data(0x000800, 1)
+mmctx_data(0x000808, 3)
+mmctx_data(0x000828, 1)
+mmctx_data(0x000830, 1)
+mmctx_data(0x0008d8, 1)
+mmctx_data(0x0008e0, 1)
+mmctx_data(0x0008e8, 6)
+mmctx_data(0x00091c, 1)
+mmctx_data(0x000924, 3)
+mmctx_data(0x000b00, 1)
+mmctx_data(0x000b08, 6)
+mmctx_data(0x000bb8, 1)
+mmctx_data(0x000c08, 1)
+mmctx_data(0x000c10, 8)
+mmctx_data(0x000c80, 1)
+mmctx_data(0x000c8c, 1)
+mmctx_data(0x001000, 3)
+mmctx_data(0x001014, 1)
+nvc0_gpc_mmio_tail:
+mmctx_data(0x000c6c, 1);
+nvc1_gpc_mmio_tail:
+
+// TPC mmio lists
+nvc0_tpc_mmio_head:
+mmctx_data(0x000018, 1)
+mmctx_data(0x00003c, 1)
+mmctx_data(0x000048, 1)
+mmctx_data(0x000064, 1)
+mmctx_data(0x000088, 1)
+mmctx_data(0x000200, 6)
+mmctx_data(0x00021c, 2)
+mmctx_data(0x000300, 6)
+mmctx_data(0x0003d0, 1)
+mmctx_data(0x0003e0, 2)
+mmctx_data(0x000400, 3)
+mmctx_data(0x000420, 1)
+mmctx_data(0x0004b0, 1)
+mmctx_data(0x0004e8, 1)
+mmctx_data(0x0004f4, 1)
+mmctx_data(0x000520, 2)
+mmctx_data(0x000604, 4)
+mmctx_data(0x000644, 20)
+mmctx_data(0x000698, 1)
+mmctx_data(0x000750, 2)
+nvc0_tpc_mmio_tail:
+mmctx_data(0x000758, 1)
+mmctx_data(0x0002c4, 1)
+mmctx_data(0x0006e0, 1)
+nvcf_tpc_mmio_tail:
+mmctx_data(0x0004bc, 1)
+nvc3_tpc_mmio_tail:
+mmctx_data(0x000544, 1)
+nvc1_tpc_mmio_tail:
+
+
+.section nvc0_grgpc_code
+bra init
+define(`include_code')
+include(`nvc0_graph.fuc')
+
+// reports an exception to the host
+//
+// In: $r15 error code (see nvc0_graph.fuc)
+//
+error:
+ push $r14
+ mov $r14 -0x67ec // 0x9814
+ sethi $r14 0x400000
+ call nv_wr32 // HUB_CTXCTL_CC_SCRATCH[5] = error code
+ add b32 $r14 0x41c
+ mov $r15 1
+ call nv_wr32 // HUB_CTXCTL_INTR_UP_SET
+ pop $r14
+ ret
+
+// GPC fuc initialisation, executed by triggering ucode start, will
+// fall through to main loop after completion.
+//
+// Input:
+// CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh)
+// CC_SCRATCH[1]: context base
+//
+// Output:
+// CC_SCRATCH[0]:
+// 31:31: set to signal completion
+// CC_SCRATCH[1]:
+// 31:0: GPC context size
+//
+init:
+ clear b32 $r0
+ mov $sp $r0
+
+ // enable fifo access
+ mov $r1 0x1200
+ mov $r2 2
+ iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE
+
+ // setup i0 handler, and route all interrupts to it
+ mov $r1 ih
+ mov $iv0 $r1
+ mov $r1 0x400
+ iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH
+
+ // enable fifo interrupt
+ mov $r2 4
+ iowr I[$r1 + 0x000] $r2 // INTR_EN_SET
+
+ // enable interrupts
+ bset $flags ie0
+
+ // figure out which GPC we are, and how many TPCs we have
+ mov $r1 0x608
+ shl b32 $r1 6
+ iord $r2 I[$r1 + 0x000] // UNITS
+ mov $r3 1
+ and $r2 0x1f
+ shl b32 $r3 $r2
+ sub b32 $r3 1
+ st b32 D[$r0 + tpc_count] $r2
+ st b32 D[$r0 + tpc_mask] $r3
+ add b32 $r1 0x400
+ iord $r2 I[$r1 + 0x000] // MYINDEX
+ st b32 D[$r0 + gpc_id] $r2
+
+ // find context data for this chipset
+ mov $r2 0x800
+ shl b32 $r2 6
+ iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0]
+ mov $r1 chipsets - 12
+ init_find_chipset:
+ add b32 $r1 12
+ ld b32 $r3 D[$r1 + 0x00]
+ cmpu b32 $r3 $r2
+ bra e init_context
+ cmpu b32 $r3 0
+ bra ne init_find_chipset
+ // unknown chipset
+ ret
+
+ // initialise context base, and size tracking
+ init_context:
+ mov $r2 0x800
+ shl b32 $r2 6
+ iord $r2 I[$r2 + 0x100] // CC_SCRATCH[1], initial base
+ clear b32 $r3 // track GPC context size here
+
+ // set mmctx base addresses now so we don't have to do it later,
+ // they don't currently ever change
+ mov $r4 0x700
+ shl b32 $r4 6
+ shr b32 $r5 $r2 8
+ iowr I[$r4 + 0x000] $r5 // MMCTX_SAVE_SWBASE
+ iowr I[$r4 + 0x100] $r5 // MMCTX_LOAD_SWBASE
+
+ // calculate GPC mmio context size, store the chipset-specific
+ // mmio list pointers somewhere we can get at them later without
+ // re-parsing the chipset list
+ clear b32 $r14
+ clear b32 $r15
+ ld b16 $r14 D[$r1 + 4]
+ ld b16 $r15 D[$r1 + 6]
+ st b16 D[$r0 + gpc_mmio_list_head] $r14
+ st b16 D[$r0 + gpc_mmio_list_tail] $r15
+ call mmctx_size
+ add b32 $r2 $r15
+ add b32 $r3 $r15
+
+ // calculate per-TPC mmio context size, store the list pointers
+ ld b16 $r14 D[$r1 + 8]
+ ld b16 $r15 D[$r1 + 10]
+ st b16 D[$r0 + tpc_mmio_list_head] $r14
+ st b16 D[$r0 + tpc_mmio_list_tail] $r15
+ call mmctx_size
+ ld b32 $r14 D[$r0 + tpc_count]
+ mulu $r14 $r15
+ add b32 $r2 $r14
+ add b32 $r3 $r14
+
+ // round up base/size to 256 byte boundary (for strand SWBASE)
+ add b32 $r4 0x1300
+ shr b32 $r3 2
+ iowr I[$r4 + 0x000] $r3 // MMCTX_LOAD_COUNT, wtf for?!?
+ shr b32 $r2 8
+ shr b32 $r3 6
+ add b32 $r2 1
+ add b32 $r3 1
+ shl b32 $r2 8
+ shl b32 $r3 8
+
+ // calculate size of strand context data
+ mov b32 $r15 $r2
+ call strand_ctx_init
+ add b32 $r3 $r15
+
+ // save context size, and tell HUB we're done
+ mov $r1 0x800
+ shl b32 $r1 6
+ iowr I[$r1 + 0x100] $r3 // CC_SCRATCH[1] = context size
+ add b32 $r1 0x800
+ clear b32 $r2
+ bset $r2 31
+ iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000
+
+// Main program loop, very simple, sleeps until woken up by the interrupt
+// handler, pulls a command from the queue and executes its handler
+//
+main:
+ bset $flags $p0
+ sleep $p0
+ mov $r13 cmd_queue
+ call queue_get
+ bra $p1 main
+
+ // 0x0000-0x0003 are all context transfers
+ cmpu b32 $r14 0x04
+ bra nc main_not_ctx_xfer
+ // fetch $flags and mask off $p1/$p2
+ mov $r1 $flags
+ mov $r2 0x0006
+ not b32 $r2
+ and $r1 $r2
+ // set $p1/$p2 according to transfer type
+ shl b32 $r14 1
+ or $r1 $r14
+ mov $flags $r1
+ // transfer context data
+ call ctx_xfer
+ bra main
+
+ main_not_ctx_xfer:
+ shl b32 $r15 $r14 16
+ or $r15 E_BAD_COMMAND
+ call error
+ bra main
+
+// interrupt handler
+ih:
+ push $r8
+ mov $r8 $flags
+ push $r8
+ push $r9
+ push $r10
+ push $r11
+ push $r13
+ push $r14
+ push $r15
+
+ // incoming fifo command?
+ iord $r10 I[$r0 + 0x200] // INTR
+ and $r11 $r10 0x00000004
+ bra e ih_no_fifo
+ // queue incoming fifo command for later processing
+ mov $r11 0x1900
+ mov $r13 cmd_queue
+ iord $r14 I[$r11 + 0x100] // FIFO_CMD
+ iord $r15 I[$r11 + 0x000] // FIFO_DATA
+ call queue_put
+ add b32 $r11 0x400
+ mov $r14 1
+ iowr I[$r11 + 0x000] $r14 // FIFO_ACK
+
+ // ack, and wake up main()
+ ih_no_fifo:
+ iowr I[$r0 + 0x100] $r10 // INTR_ACK
+
+ pop $r15
+ pop $r14
+ pop $r13
+ pop $r11
+ pop $r10
+ pop $r9
+ pop $r8
+ mov $flags $r8
+ pop $r8
+ bclr $flags $p0
+ iret
+
+// Set this GPC's bit in HUB_BAR, used to signal completion of various
+// activities to the HUB fuc
+//
+hub_barrier_done:
+ mov $r15 1
+ ld b32 $r14 D[$r0 + gpc_id]
+ shl b32 $r15 $r14
+ mov $r14 -0x6be8 // 0x409418 - HUB_BAR_SET
+ sethi $r14 0x400000
+ call nv_wr32
+ ret
+
+// Disables various things, waits a bit, and re-enables them..
+//
+// Not sure how exactly this helps, perhaps "ENABLE" is not such a
+// good description for the bits we turn off? Anyways, without this,
+// funny things happen.
+//
+ctx_redswitch:
+ mov $r14 0x614
+ shl b32 $r14 6
+ mov $r15 0x020
+ iowr I[$r14] $r15 // GPC_RED_SWITCH = POWER
+ mov $r15 8
+ ctx_redswitch_delay:
+ sub b32 $r15 1
+ bra ne ctx_redswitch_delay
+ mov $r15 0xa20
+ iowr I[$r14] $r15 // GPC_RED_SWITCH = UNK11, ENABLE, POWER
+ ret
+
+// Transfer GPC context data between GPU and storage area
+//
+// In: $r15 context base address
+// $p1 clear on save, set on load
+// $p2 set if opposite direction done/will be done, so:
+// on save it means: "a load will follow this save"
+// on load it means: "a save preceeded this load"
+//
+ctx_xfer:
+ // set context base address
+ mov $r1 0xa04
+ shl b32 $r1 6
+ iowr I[$r1 + 0x000] $r15// MEM_BASE
+ bra not $p1 ctx_xfer_not_load
+ call ctx_redswitch
+ ctx_xfer_not_load:
+
+ // strands
+ mov $r1 0x4afc
+ sethi $r1 0x20000
+ mov $r2 0xc
+ iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c
+ call strand_wait
+ mov $r2 0x47fc
+ sethi $r2 0x20000
+ iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00
+ xbit $r2 $flags $p1
+ add b32 $r2 3
+ iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD)
+
+ // mmio context
+ xbit $r10 $flags $p1 // direction
+ or $r10 2 // first
+ mov $r11 0x0000
+ sethi $r11 0x500000
+ ld b32 $r12 D[$r0 + gpc_id]
+ shl b32 $r12 15
+ add b32 $r11 $r12 // base = NV_PGRAPH_GPCn
+ ld b32 $r12 D[$r0 + gpc_mmio_list_head]
+ ld b32 $r13 D[$r0 + gpc_mmio_list_tail]
+ mov $r14 0 // not multi
+ call mmctx_xfer
+
+ // per-TPC mmio context
+ xbit $r10 $flags $p1 // direction
+ or $r10 4 // last
+ mov $r11 0x4000
+ sethi $r11 0x500000 // base = NV_PGRAPH_GPC0_TPC0
+ ld b32 $r12 D[$r0 + gpc_id]
+ shl b32 $r12 15
+ add b32 $r11 $r12 // base = NV_PGRAPH_GPCn_TPC0
+ ld b32 $r12 D[$r0 + tpc_mmio_list_head]
+ ld b32 $r13 D[$r0 + tpc_mmio_list_tail]
+ ld b32 $r15 D[$r0 + tpc_mask]
+ mov $r14 0x800 // stride = 0x800
+ call mmctx_xfer
+
+ // wait for strands to finish
+ call strand_wait
+
+ // if load, or a save without a load following, do some
+ // unknown stuff that's done after finishing a block of
+ // strand commands
+ bra $p1 ctx_xfer_post
+ bra not $p2 ctx_xfer_done
+ ctx_xfer_post:
+ mov $r1 0x4afc
+ sethi $r1 0x20000
+ mov $r2 0xd
+ iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0d
+ call strand_wait
+
+ // mark completion in HUB's barrier
+ ctx_xfer_done:
+ call hub_barrier_done
+ ret
+
+.align 256
diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h
new file mode 100644
index 000000000000..6f820324480e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h
@@ -0,0 +1,486 @@
+uint32_t nvc0_grgpc_data[] = {
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x000000c0,
+ 0x011c00bc,
+ 0x01700120,
+ 0x000000c1,
+ 0x012000bc,
+ 0x01840120,
+ 0x000000c3,
+ 0x011c00bc,
+ 0x01800120,
+ 0x000000c4,
+ 0x011c00bc,
+ 0x01800120,
+ 0x000000c8,
+ 0x011c00bc,
+ 0x01700120,
+ 0x000000ce,
+ 0x011c00bc,
+ 0x01800120,
+ 0x000000cf,
+ 0x011c00bc,
+ 0x017c0120,
+ 0x00000000,
+ 0x00000380,
+ 0x14000400,
+ 0x20000450,
+ 0x00000600,
+ 0x00000684,
+ 0x10000700,
+ 0x00000800,
+ 0x08000808,
+ 0x00000828,
+ 0x00000830,
+ 0x000008d8,
+ 0x000008e0,
+ 0x140008e8,
+ 0x0000091c,
+ 0x08000924,
+ 0x00000b00,
+ 0x14000b08,
+ 0x00000bb8,
+ 0x00000c08,
+ 0x1c000c10,
+ 0x00000c80,
+ 0x00000c8c,
+ 0x08001000,
+ 0x00001014,
+ 0x00000c6c,
+ 0x00000018,
+ 0x0000003c,
+ 0x00000048,
+ 0x00000064,
+ 0x00000088,
+ 0x14000200,
+ 0x0400021c,
+ 0x14000300,
+ 0x000003d0,
+ 0x040003e0,
+ 0x08000400,
+ 0x00000420,
+ 0x000004b0,
+ 0x000004e8,
+ 0x000004f4,
+ 0x04000520,
+ 0x0c000604,
+ 0x4c000644,
+ 0x00000698,
+ 0x04000750,
+ 0x00000758,
+ 0x000002c4,
+ 0x000006e0,
+ 0x000004bc,
+ 0x00000544,
+};
+
+uint32_t nvc0_grgpc_code[] = {
+ 0x03060ef5,
+ 0x9800d898,
+ 0x86f001d9,
+ 0x0489b808,
+ 0xf00c1bf4,
+ 0x21f502f7,
+ 0x00f802ec,
+ 0xb60798c4,
+ 0x8dbb0384,
+ 0x0880b600,
+ 0x80008e80,
+ 0x90b6018f,
+ 0x0f94f001,
+ 0xf801d980,
+ 0x0131f400,
+ 0x9800d898,
+ 0x89b801d9,
+ 0x210bf404,
+ 0xb60789c4,
+ 0x9dbb0394,
+ 0x0890b600,
+ 0x98009e98,
+ 0x80b6019f,
+ 0x0f84f001,
+ 0xf400d880,
+ 0x00f80132,
+ 0x0728b7f1,
+ 0xb906b4b6,
+ 0xc9f002ec,
+ 0x00bcd01f,
+ 0xc800bccf,
+ 0x1bf41fcc,
+ 0x06a7f0fa,
+ 0x010321f5,
+ 0xf840bfcf,
+ 0x28b7f100,
+ 0x06b4b607,
+ 0xb980bfd0,
+ 0xc9f002ec,
+ 0x1ec9f01f,
+ 0xcf00bcd0,
+ 0xccc800bc,
+ 0xfa1bf41f,
+ 0x87f100f8,
+ 0x84b60430,
+ 0x1ff9f006,
+ 0xf8008fd0,
+ 0x3087f100,
+ 0x0684b604,
+ 0xf80080d0,
+ 0x3c87f100,
+ 0x0684b608,
+ 0x99f094bd,
+ 0x0089d000,
+ 0x081887f1,
+ 0xd00684b6,
+ 0x87f1008a,
+ 0x84b60400,
+ 0x0088cf06,
+ 0xf4888aff,
+ 0x87f1f31b,
+ 0x84b6085c,
+ 0xf094bd06,
+ 0x89d00099,
+ 0xf100f800,
+ 0xb6083c87,
+ 0x94bd0684,
+ 0xd00099f0,
+ 0x87f10089,
+ 0x84b60818,
+ 0x008ad006,
+ 0x040087f1,
+ 0xcf0684b6,
+ 0x8aff0088,
+ 0xf30bf488,
+ 0x085c87f1,
+ 0xbd0684b6,
+ 0x0099f094,
+ 0xf80089d0,
+ 0x9894bd00,
+ 0x85b600e8,
+ 0x0180b61a,
+ 0xbb0284b6,
+ 0xe0b60098,
+ 0x04efb804,
+ 0xb9eb1bf4,
+ 0x00f8029f,
+ 0x083c87f1,
+ 0xbd0684b6,
+ 0x0199f094,
+ 0xf10089d0,
+ 0xb6071087,
+ 0x94bd0684,
+ 0xf405bbfd,
+ 0x8bd0090b,
+ 0x0099f000,
+ 0xf405eefd,
+ 0x8ed00c0b,
+ 0xc08fd080,
+ 0xb70199f0,
+ 0xc8010080,
+ 0xb4b600ab,
+ 0x0cb9f010,
+ 0xb601aec8,
+ 0xbefd11e4,
+ 0x008bd005,
+ 0xf0008ecf,
+ 0x0bf41fe4,
+ 0x00ce98fa,
+ 0xd005e9fd,
+ 0xc0b6c08e,
+ 0x04cdb804,
+ 0xc8e81bf4,
+ 0x1bf402ab,
+ 0x008bcf18,
+ 0xb01fb4f0,
+ 0x1bf410b4,
+ 0x02a7f0f7,
+ 0xf4c921f4,
+ 0xabc81b0e,
+ 0x10b4b600,
+ 0xf00cb9f0,
+ 0x8bd012b9,
+ 0x008bcf00,
+ 0xf412bbc8,
+ 0x87f1fa1b,
+ 0x84b6085c,
+ 0xf094bd06,
+ 0x89d00199,
+ 0xf900f800,
+ 0x02a7f0a0,
+ 0xfcc921f4,
+ 0xf100f8a0,
+ 0xf04afc87,
+ 0x97f00283,
+ 0x0089d00c,
+ 0x020721f5,
+ 0x87f100f8,
+ 0x83f04afc,
+ 0x0d97f002,
+ 0xf50089d0,
+ 0xf8020721,
+ 0xfca7f100,
+ 0x02a3f04f,
+ 0x0500aba2,
+ 0xd00fc7f0,
+ 0xc7f000ac,
+ 0x00bcd00b,
+ 0x020721f5,
+ 0xf000aed0,
+ 0xbcd00ac7,
+ 0x0721f500,
+ 0xf100f802,
+ 0xb6083c87,
+ 0x94bd0684,
+ 0xd00399f0,
+ 0x21f50089,
+ 0xe7f00213,
+ 0x3921f503,
+ 0xfca7f102,
+ 0x02a3f046,
+ 0x0400aba0,
+ 0xf040a0d0,
+ 0xbcd001c7,
+ 0x0721f500,
+ 0x010c9202,
+ 0xf000acd0,
+ 0xbcd002c7,
+ 0x0721f500,
+ 0x2621f502,
+ 0x8087f102,
+ 0x0684b608,
+ 0xb70089cf,
+ 0x95220080,
+ 0x8ed008fe,
+ 0x408ed000,
+ 0xb6808acf,
+ 0xa0b606a5,
+ 0x00eabb01,
+ 0xb60480b6,
+ 0x1bf40192,
+ 0x08e4b6e8,
+ 0xf1f2efbc,
+ 0xb6085c87,
+ 0x94bd0684,
+ 0xd00399f0,
+ 0x00f80089,
+ 0xe7f1e0f9,
+ 0xe3f09814,
+ 0x8d21f440,
+ 0x041ce0b7,
+ 0xf401f7f0,
+ 0xe0fc8d21,
+ 0x04bd00f8,
+ 0xf10004fe,
+ 0xf0120017,
+ 0x12d00227,
+ 0x3e17f100,
+ 0x0010fe04,
+ 0x040017f1,
+ 0xf0c010d0,
+ 0x12d00427,
+ 0x1031f400,
+ 0x060817f1,
+ 0xcf0614b6,
+ 0x37f00012,
+ 0x1f24f001,
+ 0xb60432bb,
+ 0x02800132,
+ 0x04038003,
+ 0x040010b7,
+ 0x800012cf,
+ 0x27f10002,
+ 0x24b60800,
+ 0x0022cf06,
+ 0xb65817f0,
+ 0x13980c10,
+ 0x0432b800,
+ 0xb00b0bf4,
+ 0x1bf40034,
+ 0xf100f8f1,
+ 0xb6080027,
+ 0x22cf0624,
+ 0xf134bd40,
+ 0xb6070047,
+ 0x25950644,
+ 0x0045d008,
+ 0xbd4045d0,
+ 0x58f4bde4,
+ 0x1f58021e,
+ 0x020e4003,
+ 0xf5040f40,
+ 0xbb013d21,
+ 0x3fbb002f,
+ 0x041e5800,
+ 0x40051f58,
+ 0x0f400a0e,
+ 0x3d21f50c,
+ 0x030e9801,
+ 0xbb00effd,
+ 0x3ebb002e,
+ 0x0040b700,
+ 0x0235b613,
+ 0xb60043d0,
+ 0x35b60825,
+ 0x0120b606,
+ 0xb60130b6,
+ 0x34b60824,
+ 0x022fb908,
+ 0x026321f5,
+ 0xf1003fbb,
+ 0xb6080017,
+ 0x13d00614,
+ 0x0010b740,
+ 0xf024bd08,
+ 0x12d01f29,
+ 0x0031f400,
+ 0xf00028f4,
+ 0x21f41cd7,
+ 0xf401f439,
+ 0xf404e4b0,
+ 0x81fe1e18,
+ 0x0627f001,
+ 0x12fd20bd,
+ 0x01e4b604,
+ 0xfe051efd,
+ 0x21f50018,
+ 0x0ef404c3,
+ 0x10ef94d3,
+ 0xf501f5f0,
+ 0xf402ec21,
+ 0x80f9c60e,
+ 0xf90188fe,
+ 0xf990f980,
+ 0xf9b0f9a0,
+ 0xf9e0f9d0,
+ 0x800acff0,
+ 0xf404abc4,
+ 0xb7f11d0b,
+ 0xd7f01900,
+ 0x40becf1c,
+ 0xf400bfcf,
+ 0xb0b70421,
+ 0xe7f00400,
+ 0x00bed001,
+ 0xfc400ad0,
+ 0xfce0fcf0,
+ 0xfcb0fcd0,
+ 0xfc90fca0,
+ 0x0088fe80,
+ 0x32f480fc,
+ 0xf001f800,
+ 0x0e9801f7,
+ 0x04febb00,
+ 0x9418e7f1,
+ 0xf440e3f0,
+ 0x00f88d21,
+ 0x0614e7f1,
+ 0xf006e4b6,
+ 0xefd020f7,
+ 0x08f7f000,
+ 0xf401f2b6,
+ 0xf7f1fd1b,
+ 0xefd00a20,
+ 0xf100f800,
+ 0xb60a0417,
+ 0x1fd00614,
+ 0x0711f400,
+ 0x04a421f5,
+ 0x4afc17f1,
+ 0xf00213f0,
+ 0x12d00c27,
+ 0x0721f500,
+ 0xfc27f102,
+ 0x0223f047,
+ 0xf00020d0,
+ 0x20b6012c,
+ 0x0012d003,
+ 0xf001acf0,
+ 0xb7f002a5,
+ 0x50b3f000,
+ 0xb6000c98,
+ 0xbcbb0fc4,
+ 0x010c9800,
+ 0xf0020d98,
+ 0x21f500e7,
+ 0xacf0015c,
+ 0x04a5f001,
+ 0x4000b7f1,
+ 0x9850b3f0,
+ 0xc4b6000c,
+ 0x00bcbb0f,
+ 0x98050c98,
+ 0x0f98060d,
+ 0x00e7f104,
+ 0x5c21f508,
+ 0x0721f501,
+ 0x0601f402,
+ 0xf11412f4,
+ 0xf04afc17,
+ 0x27f00213,
+ 0x0012d00d,
+ 0x020721f5,
+ 0x048f21f5,
+ 0x000000f8,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc
new file mode 100644
index 000000000000..e4f8c7e89ddd
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc
@@ -0,0 +1,811 @@
+/* fuc microcode for nvc0 PGRAPH/HUB
+ *
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+/* To build:
+ * m4 nvc0_grhub.fuc | envyas -a -w -m fuc -V nva3 -o nvc0_grhub.fuc.h
+ */
+
+.section nvc0_grhub_data
+include(`nvc0_graph.fuc')
+gpc_count: .b32 0
+rop_count: .b32 0
+cmd_queue: queue_init
+hub_mmio_list_head: .b32 0
+hub_mmio_list_tail: .b32 0
+
+ctx_current: .b32 0
+
+chipsets:
+.b8 0xc0 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8 0xc1 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc1_hub_mmio_tail
+.b8 0xc3 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8 0xc4 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8 0xc8 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8 0xce 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8 0xcf 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8 0 0 0 0
+
+nvc0_hub_mmio_head:
+mmctx_data(0x17e91c, 2)
+mmctx_data(0x400204, 2)
+mmctx_data(0x404004, 11)
+mmctx_data(0x404044, 1)
+mmctx_data(0x404094, 14)
+mmctx_data(0x4040d0, 7)
+mmctx_data(0x4040f8, 1)
+mmctx_data(0x404130, 3)
+mmctx_data(0x404150, 3)
+mmctx_data(0x404164, 2)
+mmctx_data(0x404174, 3)
+mmctx_data(0x404200, 8)
+mmctx_data(0x404404, 14)
+mmctx_data(0x404460, 4)
+mmctx_data(0x404480, 1)
+mmctx_data(0x404498, 1)
+mmctx_data(0x404604, 4)
+mmctx_data(0x404618, 32)
+mmctx_data(0x404698, 21)
+mmctx_data(0x4046f0, 2)
+mmctx_data(0x404700, 22)
+mmctx_data(0x405800, 1)
+mmctx_data(0x405830, 3)
+mmctx_data(0x405854, 1)
+mmctx_data(0x405870, 4)
+mmctx_data(0x405a00, 2)
+mmctx_data(0x405a18, 1)
+mmctx_data(0x406020, 1)
+mmctx_data(0x406028, 4)
+mmctx_data(0x4064a8, 2)
+mmctx_data(0x4064b4, 2)
+mmctx_data(0x407804, 1)
+mmctx_data(0x40780c, 6)
+mmctx_data(0x4078bc, 1)
+mmctx_data(0x408000, 7)
+mmctx_data(0x408064, 1)
+mmctx_data(0x408800, 3)
+mmctx_data(0x408900, 4)
+mmctx_data(0x408980, 1)
+nvc0_hub_mmio_tail:
+mmctx_data(0x4064c0, 2)
+nvc1_hub_mmio_tail:
+
+.align 256
+chan_data:
+chan_mmio_count: .b32 0
+chan_mmio_address: .b32 0
+
+.align 256
+xfer_data: .b32 0
+
+.section nvc0_grhub_code
+bra init
+define(`include_code')
+include(`nvc0_graph.fuc')
+
+// reports an exception to the host
+//
+// In: $r15 error code (see nvc0_graph.fuc)
+//
+error:
+ push $r14
+ mov $r14 0x814
+ shl b32 $r14 6
+ iowr I[$r14 + 0x000] $r15 // CC_SCRATCH[5] = error code
+ mov $r14 0xc1c
+ shl b32 $r14 6
+ mov $r15 1
+ iowr I[$r14 + 0x000] $r15 // INTR_UP_SET
+ pop $r14
+ ret
+
+// HUB fuc initialisation, executed by triggering ucode start, will
+// fall through to main loop after completion.
+//
+// Input:
+// CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh)
+//
+// Output:
+// CC_SCRATCH[0]:
+// 31:31: set to signal completion
+// CC_SCRATCH[1]:
+// 31:0: total PGRAPH context size
+//
+init:
+ clear b32 $r0
+ mov $sp $r0
+ mov $xdbase $r0
+
+ // enable fifo access
+ mov $r1 0x1200
+ mov $r2 2
+ iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE
+
+ // setup i0 handler, and route all interrupts to it
+ mov $r1 ih
+ mov $iv0 $r1
+ mov $r1 0x400
+ iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH
+
+ // route HUB_CHANNEL_SWITCH to fuc interrupt 8
+ mov $r3 0x404
+ shl b32 $r3 6
+ mov $r2 0x2003 // { HUB_CHANNEL_SWITCH, ZERO } -> intr 8
+ iowr I[$r3 + 0x000] $r2
+
+ // not sure what these are, route them because NVIDIA does, and
+ // the IRQ handler will signal the host if we ever get one.. we
+ // may find out if/why we need to handle these if so..
+ //
+ mov $r2 0x2004
+ iowr I[$r3 + 0x004] $r2 // { 0x04, ZERO } -> intr 9
+ mov $r2 0x200b
+ iowr I[$r3 + 0x008] $r2 // { 0x0b, ZERO } -> intr 10
+ mov $r2 0x200c
+ iowr I[$r3 + 0x01c] $r2 // { 0x0c, ZERO } -> intr 15
+
+ // enable all INTR_UP interrupts
+ mov $r2 0xc24
+ shl b32 $r2 6
+ not b32 $r3 $r0
+ iowr I[$r2] $r3
+
+ // enable fifo, ctxsw, 9, 10, 15 interrupts
+ mov $r2 -0x78fc // 0x8704
+ sethi $r2 0
+ iowr I[$r1 + 0x000] $r2 // INTR_EN_SET
+
+ // fifo level triggered, rest edge
+ sub b32 $r1 0x100
+ mov $r2 4
+ iowr I[$r1] $r2
+
+ // enable interrupts
+ bset $flags ie0
+
+ // fetch enabled GPC/ROP counts
+ mov $r14 -0x69fc // 0x409604
+ sethi $r14 0x400000
+ call nv_rd32
+ extr $r1 $r15 16:20
+ st b32 D[$r0 + rop_count] $r1
+ and $r15 0x1f
+ st b32 D[$r0 + gpc_count] $r15
+
+ // set BAR_REQMASK to GPC mask
+ mov $r1 1
+ shl b32 $r1 $r15
+ sub b32 $r1 1
+ mov $r2 0x40c
+ shl b32 $r2 6
+ iowr I[$r2 + 0x000] $r1
+ iowr I[$r2 + 0x100] $r1
+
+ // find context data for this chipset
+ mov $r2 0x800
+ shl b32 $r2 6
+ iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0]
+ mov $r15 chipsets - 8
+ init_find_chipset:
+ add b32 $r15 8
+ ld b32 $r3 D[$r15 + 0x00]
+ cmpu b32 $r3 $r2
+ bra e init_context
+ cmpu b32 $r3 0
+ bra ne init_find_chipset
+ // unknown chipset
+ ret
+
+ // context size calculation, reserve first 256 bytes for use by fuc
+ init_context:
+ mov $r1 256
+
+ // calculate size of mmio context data
+ ld b16 $r14 D[$r15 + 4]
+ ld b16 $r15 D[$r15 + 6]
+ sethi $r14 0
+ st b32 D[$r0 + hub_mmio_list_head] $r14
+ st b32 D[$r0 + hub_mmio_list_tail] $r15
+ call mmctx_size
+
+ // set mmctx base addresses now so we don't have to do it later,
+ // they don't (currently) ever change
+ mov $r3 0x700
+ shl b32 $r3 6
+ shr b32 $r4 $r1 8
+ iowr I[$r3 + 0x000] $r4 // MMCTX_SAVE_SWBASE
+ iowr I[$r3 + 0x100] $r4 // MMCTX_LOAD_SWBASE
+ add b32 $r3 0x1300
+ add b32 $r1 $r15
+ shr b32 $r15 2
+ iowr I[$r3 + 0x000] $r15 // MMCTX_LOAD_COUNT, wtf for?!?
+
+ // strands, base offset needs to be aligned to 256 bytes
+ shr b32 $r1 8
+ add b32 $r1 1
+ shl b32 $r1 8
+ mov b32 $r15 $r1
+ call strand_ctx_init
+ add b32 $r1 $r15
+
+ // initialise each GPC in sequence by passing in the offset of its
+ // context data in GPCn_CC_SCRATCH[1], and starting its FUC (which
+ // has previously been uploaded by the host) running.
+ //
+ // the GPC fuc init sequence will set GPCn_CC_SCRATCH[0] bit 31
+ // when it has completed, and return the size of its context data
+ // in GPCn_CC_SCRATCH[1]
+ //
+ ld b32 $r3 D[$r0 + gpc_count]
+ mov $r4 0x2000
+ sethi $r4 0x500000
+ init_gpc:
+ // setup, and start GPC ucode running
+ add b32 $r14 $r4 0x804
+ mov b32 $r15 $r1
+ call nv_wr32 // CC_SCRATCH[1] = ctx offset
+ add b32 $r14 $r4 0x800
+ mov b32 $r15 $r2
+ call nv_wr32 // CC_SCRATCH[0] = chipset
+ add b32 $r14 $r4 0x10c
+ clear b32 $r15
+ call nv_wr32
+ add b32 $r14 $r4 0x104
+ call nv_wr32 // ENTRY
+ add b32 $r14 $r4 0x100
+ mov $r15 2 // CTRL_START_TRIGGER
+ call nv_wr32 // CTRL
+
+ // wait for it to complete, and adjust context size
+ add b32 $r14 $r4 0x800
+ init_gpc_wait:
+ call nv_rd32
+ xbit $r15 $r15 31
+ bra e init_gpc_wait
+ add b32 $r14 $r4 0x804
+ call nv_rd32
+ add b32 $r1 $r15
+
+ // next!
+ add b32 $r4 0x8000
+ sub b32 $r3 1
+ bra ne init_gpc
+
+ // save context size, and tell host we're ready
+ mov $r2 0x800
+ shl b32 $r2 6
+ iowr I[$r2 + 0x100] $r1 // CC_SCRATCH[1] = context size
+ add b32 $r2 0x800
+ clear b32 $r1
+ bset $r1 31
+ iowr I[$r2 + 0x000] $r1 // CC_SCRATCH[0] |= 0x80000000
+
+// Main program loop, very simple, sleeps until woken up by the interrupt
+// handler, pulls a command from the queue and executes its handler
+//
+main:
+ // sleep until we have something to do
+ bset $flags $p0
+ sleep $p0
+ mov $r13 cmd_queue
+ call queue_get
+ bra $p1 main
+
+ // context switch, requested by GPU?
+ cmpu b32 $r14 0x4001
+ bra ne main_not_ctx_switch
+ trace_set(T_AUTO)
+ mov $r1 0xb00
+ shl b32 $r1 6
+ iord $r2 I[$r1 + 0x100] // CHAN_NEXT
+ iord $r1 I[$r1 + 0x000] // CHAN_CUR
+
+ xbit $r3 $r1 31
+ bra e chsw_no_prev
+ xbit $r3 $r2 31
+ bra e chsw_prev_no_next
+ push $r2
+ mov b32 $r2 $r1
+ trace_set(T_SAVE)
+ bclr $flags $p1
+ bset $flags $p2
+ call ctx_xfer
+ trace_clr(T_SAVE);
+ pop $r2
+ trace_set(T_LOAD);
+ bset $flags $p1
+ call ctx_xfer
+ trace_clr(T_LOAD);
+ bra chsw_done
+ chsw_prev_no_next:
+ push $r2
+ mov b32 $r2 $r1
+ bclr $flags $p1
+ bclr $flags $p2
+ call ctx_xfer
+ pop $r2
+ mov $r1 0xb00
+ shl b32 $r1 6
+ iowr I[$r1] $r2
+ bra chsw_done
+ chsw_no_prev:
+ xbit $r3 $r2 31
+ bra e chsw_done
+ bset $flags $p1
+ bclr $flags $p2
+ call ctx_xfer
+
+ // ack the context switch request
+ chsw_done:
+ mov $r1 0xb0c
+ shl b32 $r1 6
+ mov $r2 1
+ iowr I[$r1 + 0x000] $r2 // 0x409b0c
+ trace_clr(T_AUTO)
+ bra main
+
+ // request to set current channel? (*not* a context switch)
+ main_not_ctx_switch:
+ cmpu b32 $r14 0x0001
+ bra ne main_not_ctx_chan
+ mov b32 $r2 $r15
+ call ctx_chan
+ bra main_done
+
+ // request to store current channel context?
+ main_not_ctx_chan:
+ cmpu b32 $r14 0x0002
+ bra ne main_not_ctx_save
+ trace_set(T_SAVE)
+ bclr $flags $p1
+ bclr $flags $p2
+ call ctx_xfer
+ trace_clr(T_SAVE)
+ bra main_done
+
+ main_not_ctx_save:
+ shl b32 $r15 $r14 16
+ or $r15 E_BAD_COMMAND
+ call error
+ bra main
+
+ main_done:
+ mov $r1 0x820
+ shl b32 $r1 6
+ clear b32 $r2
+ bset $r2 31
+ iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000
+ bra main
+
+// interrupt handler
+ih:
+ push $r8
+ mov $r8 $flags
+ push $r8
+ push $r9
+ push $r10
+ push $r11
+ push $r13
+ push $r14
+ push $r15
+
+ // incoming fifo command?
+ iord $r10 I[$r0 + 0x200] // INTR
+ and $r11 $r10 0x00000004
+ bra e ih_no_fifo
+ // queue incoming fifo command for later processing
+ mov $r11 0x1900
+ mov $r13 cmd_queue
+ iord $r14 I[$r11 + 0x100] // FIFO_CMD
+ iord $r15 I[$r11 + 0x000] // FIFO_DATA
+ call queue_put
+ add b32 $r11 0x400
+ mov $r14 1
+ iowr I[$r11 + 0x000] $r14 // FIFO_ACK
+
+ // context switch request?
+ ih_no_fifo:
+ and $r11 $r10 0x00000100
+ bra e ih_no_ctxsw
+ // enqueue a context switch for later processing
+ mov $r13 cmd_queue
+ mov $r14 0x4001
+ call queue_put
+
+ // anything we didn't handle, bring it to the host's attention
+ ih_no_ctxsw:
+ mov $r11 0x104
+ not b32 $r11
+ and $r11 $r10 $r11
+ bra e ih_no_other
+ mov $r10 0xc1c
+ shl b32 $r10 6
+ iowr I[$r10] $r11 // INTR_UP_SET
+
+ // ack, and wake up main()
+ ih_no_other:
+ iowr I[$r0 + 0x100] $r10 // INTR_ACK
+
+ pop $r15
+ pop $r14
+ pop $r13
+ pop $r11
+ pop $r10
+ pop $r9
+ pop $r8
+ mov $flags $r8
+ pop $r8
+ bclr $flags $p0
+ iret
+
+// Not real sure, but, MEM_CMD 7 will hang forever if this isn't done
+ctx_4160s:
+ mov $r14 0x4160
+ sethi $r14 0x400000
+ mov $r15 1
+ call nv_wr32
+ ctx_4160s_wait:
+ call nv_rd32
+ xbit $r15 $r15 4
+ bra e ctx_4160s_wait
+ ret
+
+// Without clearing again at end of xfer, some things cause PGRAPH
+// to hang with STATUS=0x00000007 until it's cleared.. fbcon can
+// still function with it set however...
+ctx_4160c:
+ mov $r14 0x4160
+ sethi $r14 0x400000
+ clear b32 $r15
+ call nv_wr32
+ ret
+
+// Again, not real sure
+//
+// In: $r15 value to set 0x404170 to
+//
+ctx_4170s:
+ mov $r14 0x4170
+ sethi $r14 0x400000
+ or $r15 0x10
+ call nv_wr32
+ ret
+
+// Waits for a ctx_4170s() call to complete
+//
+ctx_4170w:
+ mov $r14 0x4170
+ sethi $r14 0x400000
+ call nv_rd32
+ and $r15 0x10
+ bra ne ctx_4170w
+ ret
+
+// Disables various things, waits a bit, and re-enables them..
+//
+// Not sure how exactly this helps, perhaps "ENABLE" is not such a
+// good description for the bits we turn off? Anyways, without this,
+// funny things happen.
+//
+ctx_redswitch:
+ mov $r14 0x614
+ shl b32 $r14 6
+ mov $r15 0x270
+ iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_GPC, POWER_ALL
+ mov $r15 8
+ ctx_redswitch_delay:
+ sub b32 $r15 1
+ bra ne ctx_redswitch_delay
+ mov $r15 0x770
+ iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL
+ ret
+
+// Not a clue what this is for, except that unless the value is 0x10, the
+// strand context is saved (and presumably restored) incorrectly..
+//
+// In: $r15 value to set to (0x00/0x10 are used)
+//
+ctx_86c:
+ mov $r14 0x86c
+ shl b32 $r14 6
+ iowr I[$r14] $r15 // HUB(0x86c) = val
+ mov $r14 -0x75ec
+ sethi $r14 0x400000
+ call nv_wr32 // ROP(0xa14) = val
+ mov $r14 -0x5794
+ sethi $r14 0x410000
+ call nv_wr32 // GPC(0x86c) = val
+ ret
+
+// ctx_load - load's a channel's ctxctl data, and selects its vm
+//
+// In: $r2 channel address
+//
+ctx_load:
+ trace_set(T_CHAN)
+
+ // switch to channel, somewhat magic in parts..
+ mov $r10 12 // DONE_UNK12
+ call wait_donez
+ mov $r1 0xa24
+ shl b32 $r1 6
+ iowr I[$r1 + 0x000] $r0 // 0x409a24
+ mov $r3 0xb00
+ shl b32 $r3 6
+ iowr I[$r3 + 0x100] $r2 // CHAN_NEXT
+ mov $r1 0xa0c
+ shl b32 $r1 6
+ mov $r4 7
+ iowr I[$r1 + 0x000] $r2 // MEM_CHAN
+ iowr I[$r1 + 0x100] $r4 // MEM_CMD
+ ctx_chan_wait_0:
+ iord $r4 I[$r1 + 0x100]
+ and $r4 0x1f
+ bra ne ctx_chan_wait_0
+ iowr I[$r3 + 0x000] $r2 // CHAN_CUR
+
+ // load channel header, fetch PGRAPH context pointer
+ mov $xtargets $r0
+ bclr $r2 31
+ shl b32 $r2 4
+ add b32 $r2 2
+
+ trace_set(T_LCHAN)
+ mov $r1 0xa04
+ shl b32 $r1 6
+ iowr I[$r1 + 0x000] $r2 // MEM_BASE
+ mov $r1 0xa20
+ shl b32 $r1 6
+ mov $r2 0x0002
+ sethi $r2 0x80000000
+ iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vram
+ mov $r1 0x10 // chan + 0x0210
+ mov $r2 xfer_data
+ sethi $r2 0x00020000 // 16 bytes
+ xdld $r1 $r2
+ xdwait
+ trace_clr(T_LCHAN)
+
+ // update current context
+ ld b32 $r1 D[$r0 + xfer_data + 4]
+ shl b32 $r1 24
+ ld b32 $r2 D[$r0 + xfer_data + 0]
+ shr b32 $r2 8
+ or $r1 $r2
+ st b32 D[$r0 + ctx_current] $r1
+
+ // set transfer base to start of context, and fetch context header
+ trace_set(T_LCTXH)
+ mov $r2 0xa04
+ shl b32 $r2 6
+ iowr I[$r2 + 0x000] $r1 // MEM_BASE
+ mov $r2 1
+ mov $r1 0xa20
+ shl b32 $r1 6
+ iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vm
+ mov $r1 chan_data
+ sethi $r1 0x00060000 // 256 bytes
+ xdld $r0 $r1
+ xdwait
+ trace_clr(T_LCTXH)
+
+ trace_clr(T_CHAN)
+ ret
+
+// ctx_chan - handler for HUB_SET_CHAN command, will set a channel as
+// the active channel for ctxctl, but not actually transfer
+// any context data. intended for use only during initial
+// context construction.
+//
+// In: $r2 channel address
+//
+ctx_chan:
+ call ctx_4160s
+ call ctx_load
+ mov $r10 12 // DONE_UNK12
+ call wait_donez
+ mov $r1 0xa10
+ shl b32 $r1 6
+ mov $r2 5
+ iowr I[$r1 + 0x000] $r2 // MEM_CMD = 5 (???)
+ ctx_chan_wait:
+ iord $r2 I[$r1 + 0x000]
+ or $r2 $r2
+ bra ne ctx_chan_wait
+ call ctx_4160c
+ ret
+
+// Execute per-context state overrides list
+//
+// Only executed on the first load of a channel. Might want to look into
+// removing this and having the host directly modify the channel's context
+// to change this state... The nouveau DRM already builds this list as
+// it's definitely needed for NVIDIA's, so we may as well use it for now
+//
+// Input: $r1 mmio list length
+//
+ctx_mmio_exec:
+ // set transfer base to be the mmio list
+ ld b32 $r3 D[$r0 + chan_mmio_address]
+ mov $r2 0xa04
+ shl b32 $r2 6
+ iowr I[$r2 + 0x000] $r3 // MEM_BASE
+
+ clear b32 $r3
+ ctx_mmio_loop:
+ // fetch next 256 bytes of mmio list if necessary
+ and $r4 $r3 0xff
+ bra ne ctx_mmio_pull
+ mov $r5 xfer_data
+ sethi $r5 0x00060000 // 256 bytes
+ xdld $r3 $r5
+ xdwait
+
+ // execute a single list entry
+ ctx_mmio_pull:
+ ld b32 $r14 D[$r4 + xfer_data + 0x00]
+ ld b32 $r15 D[$r4 + xfer_data + 0x04]
+ call nv_wr32
+
+ // next!
+ add b32 $r3 8
+ sub b32 $r1 1
+ bra ne ctx_mmio_loop
+
+ // set transfer base back to the current context
+ ctx_mmio_done:
+ ld b32 $r3 D[$r0 + ctx_current]
+ iowr I[$r2 + 0x000] $r3 // MEM_BASE
+
+ // disable the mmio list now, we don't need/want to execute it again
+ st b32 D[$r0 + chan_mmio_count] $r0
+ mov $r1 chan_data
+ sethi $r1 0x00060000 // 256 bytes
+ xdst $r0 $r1
+ xdwait
+ ret
+
+// Transfer HUB context data between GPU and storage area
+//
+// In: $r2 channel address
+// $p1 clear on save, set on load
+// $p2 set if opposite direction done/will be done, so:
+// on save it means: "a load will follow this save"
+// on load it means: "a save preceeded this load"
+//
+ctx_xfer:
+ bra not $p1 ctx_xfer_pre
+ bra $p2 ctx_xfer_pre_load
+ ctx_xfer_pre:
+ mov $r15 0x10
+ call ctx_86c
+ call ctx_4160s
+ bra not $p1 ctx_xfer_exec
+
+ ctx_xfer_pre_load:
+ mov $r15 2
+ call ctx_4170s
+ call ctx_4170w
+ call ctx_redswitch
+ clear b32 $r15
+ call ctx_4170s
+ call ctx_load
+
+ // fetch context pointer, and initiate xfer on all GPCs
+ ctx_xfer_exec:
+ ld b32 $r1 D[$r0 + ctx_current]
+ mov $r2 0x414
+ shl b32 $r2 6
+ iowr I[$r2 + 0x000] $r0 // BAR_STATUS = reset
+ mov $r14 -0x5b00
+ sethi $r14 0x410000
+ mov b32 $r15 $r1
+ call nv_wr32 // GPC_BCAST_WRCMD_DATA = ctx pointer
+ add b32 $r14 4
+ xbit $r15 $flags $p1
+ xbit $r2 $flags $p2
+ shl b32 $r2 1
+ or $r15 $r2
+ call nv_wr32 // GPC_BCAST_WRCMD_CMD = GPC_XFER(type)
+
+ // strands
+ mov $r1 0x4afc
+ sethi $r1 0x20000
+ mov $r2 0xc
+ iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c
+ call strand_wait
+ mov $r2 0x47fc
+ sethi $r2 0x20000
+ iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00
+ xbit $r2 $flags $p1
+ add b32 $r2 3
+ iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD)
+
+ // mmio context
+ xbit $r10 $flags $p1 // direction
+ or $r10 6 // first, last
+ mov $r11 0 // base = 0
+ ld b32 $r12 D[$r0 + hub_mmio_list_head]
+ ld b32 $r13 D[$r0 + hub_mmio_list_tail]
+ mov $r14 0 // not multi
+ call mmctx_xfer
+
+ // wait for GPCs to all complete
+ mov $r10 8 // DONE_BAR
+ call wait_doneo
+
+ // wait for strand xfer to complete
+ call strand_wait
+
+ // post-op
+ bra $p1 ctx_xfer_post
+ mov $r10 12 // DONE_UNK12
+ call wait_donez
+ mov $r1 0xa10
+ shl b32 $r1 6
+ mov $r2 5
+ iowr I[$r1] $r2 // MEM_CMD
+ ctx_xfer_post_save_wait:
+ iord $r2 I[$r1]
+ or $r2 $r2
+ bra ne ctx_xfer_post_save_wait
+
+ bra $p2 ctx_xfer_done
+ ctx_xfer_post:
+ mov $r15 2
+ call ctx_4170s
+ clear b32 $r15
+ call ctx_86c
+ call strand_post
+ call ctx_4170w
+ clear b32 $r15
+ call ctx_4170s
+
+ bra not $p1 ctx_xfer_no_post_mmio
+ ld b32 $r1 D[$r0 + chan_mmio_count]
+ or $r1 $r1
+ bra e ctx_xfer_no_post_mmio
+ call ctx_mmio_exec
+
+ ctx_xfer_no_post_mmio:
+ call ctx_4160c
+
+ ctx_xfer_done:
+ ret
+
+.align 256
diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h
new file mode 100644
index 000000000000..241d3263f1e5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h
@@ -0,0 +1,838 @@
+uint32_t nvc0_grhub_data[] = {
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x000000c0,
+ 0x01340098,
+ 0x000000c1,
+ 0x01380098,
+ 0x000000c3,
+ 0x01340098,
+ 0x000000c4,
+ 0x01340098,
+ 0x000000c8,
+ 0x01340098,
+ 0x000000ce,
+ 0x01340098,
+ 0x000000cf,
+ 0x01340098,
+ 0x00000000,
+ 0x0417e91c,
+ 0x04400204,
+ 0x28404004,
+ 0x00404044,
+ 0x34404094,
+ 0x184040d0,
+ 0x004040f8,
+ 0x08404130,
+ 0x08404150,
+ 0x04404164,
+ 0x08404174,
+ 0x1c404200,
+ 0x34404404,
+ 0x0c404460,
+ 0x00404480,
+ 0x00404498,
+ 0x0c404604,
+ 0x7c404618,
+ 0x50404698,
+ 0x044046f0,
+ 0x54404700,
+ 0x00405800,
+ 0x08405830,
+ 0x00405854,
+ 0x0c405870,
+ 0x04405a00,
+ 0x00405a18,
+ 0x00406020,
+ 0x0c406028,
+ 0x044064a8,
+ 0x044064b4,
+ 0x00407804,
+ 0x1440780c,
+ 0x004078bc,
+ 0x18408000,
+ 0x00408064,
+ 0x08408800,
+ 0x0c408900,
+ 0x00408980,
+ 0x044064c0,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
+
+uint32_t nvc0_grhub_code[] = {
+ 0x03090ef5,
+ 0x9800d898,
+ 0x86f001d9,
+ 0x0489b808,
+ 0xf00c1bf4,
+ 0x21f502f7,
+ 0x00f802ec,
+ 0xb60798c4,
+ 0x8dbb0384,
+ 0x0880b600,
+ 0x80008e80,
+ 0x90b6018f,
+ 0x0f94f001,
+ 0xf801d980,
+ 0x0131f400,
+ 0x9800d898,
+ 0x89b801d9,
+ 0x210bf404,
+ 0xb60789c4,
+ 0x9dbb0394,
+ 0x0890b600,
+ 0x98009e98,
+ 0x80b6019f,
+ 0x0f84f001,
+ 0xf400d880,
+ 0x00f80132,
+ 0x0728b7f1,
+ 0xb906b4b6,
+ 0xc9f002ec,
+ 0x00bcd01f,
+ 0xc800bccf,
+ 0x1bf41fcc,
+ 0x06a7f0fa,
+ 0x010321f5,
+ 0xf840bfcf,
+ 0x28b7f100,
+ 0x06b4b607,
+ 0xb980bfd0,
+ 0xc9f002ec,
+ 0x1ec9f01f,
+ 0xcf00bcd0,
+ 0xccc800bc,
+ 0xfa1bf41f,
+ 0x87f100f8,
+ 0x84b60430,
+ 0x1ff9f006,
+ 0xf8008fd0,
+ 0x3087f100,
+ 0x0684b604,
+ 0xf80080d0,
+ 0x3c87f100,
+ 0x0684b608,
+ 0x99f094bd,
+ 0x0089d000,
+ 0x081887f1,
+ 0xd00684b6,
+ 0x87f1008a,
+ 0x84b60400,
+ 0x0088cf06,
+ 0xf4888aff,
+ 0x87f1f31b,
+ 0x84b6085c,
+ 0xf094bd06,
+ 0x89d00099,
+ 0xf100f800,
+ 0xb6083c87,
+ 0x94bd0684,
+ 0xd00099f0,
+ 0x87f10089,
+ 0x84b60818,
+ 0x008ad006,
+ 0x040087f1,
+ 0xcf0684b6,
+ 0x8aff0088,
+ 0xf30bf488,
+ 0x085c87f1,
+ 0xbd0684b6,
+ 0x0099f094,
+ 0xf80089d0,
+ 0x9894bd00,
+ 0x85b600e8,
+ 0x0180b61a,
+ 0xbb0284b6,
+ 0xe0b60098,
+ 0x04efb804,
+ 0xb9eb1bf4,
+ 0x00f8029f,
+ 0x083c87f1,
+ 0xbd0684b6,
+ 0x0199f094,
+ 0xf10089d0,
+ 0xb6071087,
+ 0x94bd0684,
+ 0xf405bbfd,
+ 0x8bd0090b,
+ 0x0099f000,
+ 0xf405eefd,
+ 0x8ed00c0b,
+ 0xc08fd080,
+ 0xb70199f0,
+ 0xc8010080,
+ 0xb4b600ab,
+ 0x0cb9f010,
+ 0xb601aec8,
+ 0xbefd11e4,
+ 0x008bd005,
+ 0xf0008ecf,
+ 0x0bf41fe4,
+ 0x00ce98fa,
+ 0xd005e9fd,
+ 0xc0b6c08e,
+ 0x04cdb804,
+ 0xc8e81bf4,
+ 0x1bf402ab,
+ 0x008bcf18,
+ 0xb01fb4f0,
+ 0x1bf410b4,
+ 0x02a7f0f7,
+ 0xf4c921f4,
+ 0xabc81b0e,
+ 0x10b4b600,
+ 0xf00cb9f0,
+ 0x8bd012b9,
+ 0x008bcf00,
+ 0xf412bbc8,
+ 0x87f1fa1b,
+ 0x84b6085c,
+ 0xf094bd06,
+ 0x89d00199,
+ 0xf900f800,
+ 0x02a7f0a0,
+ 0xfcc921f4,
+ 0xf100f8a0,
+ 0xf04afc87,
+ 0x97f00283,
+ 0x0089d00c,
+ 0x020721f5,
+ 0x87f100f8,
+ 0x83f04afc,
+ 0x0d97f002,
+ 0xf50089d0,
+ 0xf8020721,
+ 0xfca7f100,
+ 0x02a3f04f,
+ 0x0500aba2,
+ 0xd00fc7f0,
+ 0xc7f000ac,
+ 0x00bcd00b,
+ 0x020721f5,
+ 0xf000aed0,
+ 0xbcd00ac7,
+ 0x0721f500,
+ 0xf100f802,
+ 0xb6083c87,
+ 0x94bd0684,
+ 0xd00399f0,
+ 0x21f50089,
+ 0xe7f00213,
+ 0x3921f503,
+ 0xfca7f102,
+ 0x02a3f046,
+ 0x0400aba0,
+ 0xf040a0d0,
+ 0xbcd001c7,
+ 0x0721f500,
+ 0x010c9202,
+ 0xf000acd0,
+ 0xbcd002c7,
+ 0x0721f500,
+ 0x2621f502,
+ 0x8087f102,
+ 0x0684b608,
+ 0xb70089cf,
+ 0x95220080,
+ 0x8ed008fe,
+ 0x408ed000,
+ 0xb6808acf,
+ 0xa0b606a5,
+ 0x00eabb01,
+ 0xb60480b6,
+ 0x1bf40192,
+ 0x08e4b6e8,
+ 0xf1f2efbc,
+ 0xb6085c87,
+ 0x94bd0684,
+ 0xd00399f0,
+ 0x00f80089,
+ 0xe7f1e0f9,
+ 0xe4b60814,
+ 0x00efd006,
+ 0x0c1ce7f1,
+ 0xf006e4b6,
+ 0xefd001f7,
+ 0xf8e0fc00,
+ 0xfe04bd00,
+ 0x07fe0004,
+ 0x0017f100,
+ 0x0227f012,
+ 0xf10012d0,
+ 0xfe05b917,
+ 0x17f10010,
+ 0x10d00400,
+ 0x0437f1c0,
+ 0x0634b604,
+ 0x200327f1,
+ 0xf10032d0,
+ 0xd0200427,
+ 0x27f10132,
+ 0x32d0200b,
+ 0x0c27f102,
+ 0x0732d020,
+ 0x0c2427f1,
+ 0xb90624b6,
+ 0x23d00003,
+ 0x0427f100,
+ 0x0023f087,
+ 0xb70012d0,
+ 0xf0010012,
+ 0x12d00427,
+ 0x1031f400,
+ 0x9604e7f1,
+ 0xf440e3f0,
+ 0xf1c76821,
+ 0x01018090,
+ 0x801ff4f0,
+ 0x17f0000f,
+ 0x041fbb01,
+ 0xf10112b6,
+ 0xb6040c27,
+ 0x21d00624,
+ 0x4021d000,
+ 0x080027f1,
+ 0xcf0624b6,
+ 0xf7f00022,
+ 0x08f0b654,
+ 0xb800f398,
+ 0x0bf40432,
+ 0x0034b00b,
+ 0xf8f11bf4,
+ 0x0017f100,
+ 0x02fe5801,
+ 0xf003ff58,
+ 0x0e8000e3,
+ 0x150f8014,
+ 0x013d21f5,
+ 0x070037f1,
+ 0x950634b6,
+ 0x34d00814,
+ 0x4034d000,
+ 0x130030b7,
+ 0xb6001fbb,
+ 0x3fd002f5,
+ 0x0815b600,
+ 0xb60110b6,
+ 0x1fb90814,
+ 0x6321f502,
+ 0x001fbb02,
+ 0xf1000398,
+ 0xf0200047,
+ 0x4ea05043,
+ 0x1fb90804,
+ 0x8d21f402,
+ 0x08004ea0,
+ 0xf4022fb9,
+ 0x4ea08d21,
+ 0xf4bd010c,
+ 0xa08d21f4,
+ 0xf401044e,
+ 0x4ea08d21,
+ 0xf7f00100,
+ 0x8d21f402,
+ 0x08004ea0,
+ 0xc86821f4,
+ 0x0bf41fff,
+ 0x044ea0fa,
+ 0x6821f408,
+ 0xb7001fbb,
+ 0xb6800040,
+ 0x1bf40132,
+ 0x0027f1b4,
+ 0x0624b608,
+ 0xb74021d0,
+ 0xbd080020,
+ 0x1f19f014,
+ 0xf40021d0,
+ 0x28f40031,
+ 0x08d7f000,
+ 0xf43921f4,
+ 0xe4b1f401,
+ 0x1bf54001,
+ 0x87f100d1,
+ 0x84b6083c,
+ 0xf094bd06,
+ 0x89d00499,
+ 0x0017f100,
+ 0x0614b60b,
+ 0xcf4012cf,
+ 0x13c80011,
+ 0x7e0bf41f,
+ 0xf41f23c8,
+ 0x20f95a0b,
+ 0xf10212b9,
+ 0xb6083c87,
+ 0x94bd0684,
+ 0xd00799f0,
+ 0x32f40089,
+ 0x0231f401,
+ 0x082921f5,
+ 0x085c87f1,
+ 0xbd0684b6,
+ 0x0799f094,
+ 0xfc0089d0,
+ 0x3c87f120,
+ 0x0684b608,
+ 0x99f094bd,
+ 0x0089d006,
+ 0xf50131f4,
+ 0xf1082921,
+ 0xb6085c87,
+ 0x94bd0684,
+ 0xd00699f0,
+ 0x0ef40089,
+ 0xb920f931,
+ 0x32f40212,
+ 0x0232f401,
+ 0x082921f5,
+ 0x17f120fc,
+ 0x14b60b00,
+ 0x0012d006,
+ 0xc8130ef4,
+ 0x0bf41f23,
+ 0x0131f40d,
+ 0xf50232f4,
+ 0xf1082921,
+ 0xb60b0c17,
+ 0x27f00614,
+ 0x0012d001,
+ 0x085c87f1,
+ 0xbd0684b6,
+ 0x0499f094,
+ 0xf50089d0,
+ 0xb0ff200e,
+ 0x1bf401e4,
+ 0x02f2b90d,
+ 0x07b521f5,
+ 0xb0420ef4,
+ 0x1bf402e4,
+ 0x3c87f12e,
+ 0x0684b608,
+ 0x99f094bd,
+ 0x0089d007,
+ 0xf40132f4,
+ 0x21f50232,
+ 0x87f10829,
+ 0x84b6085c,
+ 0xf094bd06,
+ 0x89d00799,
+ 0x110ef400,
+ 0xf010ef94,
+ 0x21f501f5,
+ 0x0ef502ec,
+ 0x17f1fed1,
+ 0x14b60820,
+ 0xf024bd06,
+ 0x12d01f29,
+ 0xbe0ef500,
+ 0xfe80f9fe,
+ 0x80f90188,
+ 0xa0f990f9,
+ 0xd0f9b0f9,
+ 0xf0f9e0f9,
+ 0xc4800acf,
+ 0x0bf404ab,
+ 0x00b7f11d,
+ 0x08d7f019,
+ 0xcf40becf,
+ 0x21f400bf,
+ 0x00b0b704,
+ 0x01e7f004,
+ 0xe400bed0,
+ 0xf40100ab,
+ 0xd7f00d0b,
+ 0x01e7f108,
+ 0x0421f440,
+ 0x0104b7f1,
+ 0xabffb0bd,
+ 0x0d0bf4b4,
+ 0x0c1ca7f1,
+ 0xd006a4b6,
+ 0x0ad000ab,
+ 0xfcf0fc40,
+ 0xfcd0fce0,
+ 0xfca0fcb0,
+ 0xfe80fc90,
+ 0x80fc0088,
+ 0xf80032f4,
+ 0x60e7f101,
+ 0x40e3f041,
+ 0xf401f7f0,
+ 0x21f48d21,
+ 0x04ffc868,
+ 0xf8fa0bf4,
+ 0x60e7f100,
+ 0x40e3f041,
+ 0x21f4f4bd,
+ 0xf100f88d,
+ 0xf04170e7,
+ 0xf5f040e3,
+ 0x8d21f410,
+ 0xe7f100f8,
+ 0xe3f04170,
+ 0x6821f440,
+ 0xf410f4f0,
+ 0x00f8f31b,
+ 0x0614e7f1,
+ 0xf106e4b6,
+ 0xd00270f7,
+ 0xf7f000ef,
+ 0x01f2b608,
+ 0xf1fd1bf4,
+ 0xd00770f7,
+ 0x00f800ef,
+ 0x086ce7f1,
+ 0xd006e4b6,
+ 0xe7f100ef,
+ 0xe3f08a14,
+ 0x8d21f440,
+ 0xa86ce7f1,
+ 0xf441e3f0,
+ 0x00f88d21,
+ 0x083c87f1,
+ 0xbd0684b6,
+ 0x0599f094,
+ 0xf00089d0,
+ 0x21f40ca7,
+ 0x2417f1c9,
+ 0x0614b60a,
+ 0xf10010d0,
+ 0xb60b0037,
+ 0x32d00634,
+ 0x0c17f140,
+ 0x0614b60a,
+ 0xd00747f0,
+ 0x14d00012,
+ 0x4014cf40,
+ 0xf41f44f0,
+ 0x32d0fa1b,
+ 0x000bfe00,
+ 0xb61f2af0,
+ 0x20b60424,
+ 0x3c87f102,
+ 0x0684b608,
+ 0x99f094bd,
+ 0x0089d008,
+ 0x0a0417f1,
+ 0xd00614b6,
+ 0x17f10012,
+ 0x14b60a20,
+ 0x0227f006,
+ 0x800023f1,
+ 0xf00012d0,
+ 0x27f11017,
+ 0x23f00300,
+ 0x0512fa02,
+ 0x87f103f8,
+ 0x84b6085c,
+ 0xf094bd06,
+ 0x89d00899,
+ 0xc1019800,
+ 0x981814b6,
+ 0x25b6c002,
+ 0x0512fd08,
+ 0xf1160180,
+ 0xb6083c87,
+ 0x94bd0684,
+ 0xd00999f0,
+ 0x27f10089,
+ 0x24b60a04,
+ 0x0021d006,
+ 0xf10127f0,
+ 0xb60a2017,
+ 0x12d00614,
+ 0x0017f100,
+ 0x0613f002,
+ 0xf80501fa,
+ 0x5c87f103,
+ 0x0684b608,
+ 0x99f094bd,
+ 0x0089d009,
+ 0x085c87f1,
+ 0xbd0684b6,
+ 0x0599f094,
+ 0xf80089d0,
+ 0x3121f500,
+ 0xb821f506,
+ 0x0ca7f006,
+ 0xf1c921f4,
+ 0xb60a1017,
+ 0x27f00614,
+ 0x0012d005,
+ 0xfd0012cf,
+ 0x1bf40522,
+ 0x4921f5fa,
+ 0x9800f806,
+ 0x27f18103,
+ 0x24b60a04,
+ 0x0023d006,
+ 0x34c434bd,
+ 0x0f1bf4ff,
+ 0x030057f1,
+ 0xfa0653f0,
+ 0x03f80535,
+ 0x98c04e98,
+ 0x21f4c14f,
+ 0x0830b68d,
+ 0xf40112b6,
+ 0x0398df1b,
+ 0x0023d016,
+ 0xf1800080,
+ 0xf0020017,
+ 0x01fa0613,
+ 0xf803f806,
+ 0x0611f400,
+ 0xf01102f4,
+ 0x21f510f7,
+ 0x21f50698,
+ 0x11f40631,
+ 0x02f7f01c,
+ 0x065721f5,
+ 0x066621f5,
+ 0x067821f5,
+ 0x21f5f4bd,
+ 0x21f50657,
+ 0x019806b8,
+ 0x1427f116,
+ 0x0624b604,
+ 0xf10020d0,
+ 0xf0a500e7,
+ 0x1fb941e3,
+ 0x8d21f402,
+ 0xf004e0b6,
+ 0x2cf001fc,
+ 0x0124b602,
+ 0xf405f2fd,
+ 0x17f18d21,
+ 0x13f04afc,
+ 0x0c27f002,
+ 0xf50012d0,
+ 0xf1020721,
+ 0xf047fc27,
+ 0x20d00223,
+ 0x012cf000,
+ 0xd00320b6,
+ 0xacf00012,
+ 0x06a5f001,
+ 0x9800b7f0,
+ 0x0d98140c,
+ 0x00e7f015,
+ 0x015c21f5,
+ 0xf508a7f0,
+ 0xf5010321,
+ 0xf4020721,
+ 0xa7f02201,
+ 0xc921f40c,
+ 0x0a1017f1,
+ 0xf00614b6,
+ 0x12d00527,
+ 0x0012cf00,
+ 0xf40522fd,
+ 0x02f4fa1b,
+ 0x02f7f032,
+ 0x065721f5,
+ 0x21f5f4bd,
+ 0x21f50698,
+ 0x21f50226,
+ 0xf4bd0666,
+ 0x065721f5,
+ 0x981011f4,
+ 0x11fd8001,
+ 0x070bf405,
+ 0x07df21f5,
+ 0x064921f5,
+ 0x000000f8,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/nvc0_instmem.c b/drivers/gpu/drm/nouveau/nvc0_instmem.c
index 82357d2df1f4..b701c439c92e 100644
--- a/drivers/gpu/drm/nouveau/nvc0_instmem.c
+++ b/drivers/gpu/drm/nouveau/nvc0_instmem.c
@@ -32,7 +32,6 @@ struct nvc0_instmem_priv {
struct nouveau_channel *bar1;
struct nouveau_gpuobj *bar3_pgd;
struct nouveau_channel *bar3;
- struct nouveau_gpuobj *chan_pgd;
};
int
@@ -181,17 +180,11 @@ nvc0_instmem_init(struct drm_device *dev)
goto error;
/* channel vm */
- ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0008000000ULL, &vm);
+ ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0008000000ULL,
+ &dev_priv->chan_vm);
if (ret)
goto error;
- ret = nouveau_gpuobj_new(dev, NULL, 0x8000, 4096, 0, &priv->chan_pgd);
- if (ret)
- goto error;
-
- nouveau_vm_ref(vm, &dev_priv->chan_vm, priv->chan_pgd);
- nouveau_vm_ref(NULL, &vm, NULL);
-
nvc0_instmem_resume(dev);
return 0;
error:
@@ -211,8 +204,7 @@ nvc0_instmem_takedown(struct drm_device *dev)
nv_wr32(dev, 0x1704, 0x00000000);
nv_wr32(dev, 0x1714, 0x00000000);
- nouveau_vm_ref(NULL, &dev_priv->chan_vm, priv->chan_pgd);
- nouveau_gpuobj_ref(NULL, &priv->chan_pgd);
+ nouveau_vm_ref(NULL, &dev_priv->chan_vm, NULL);
nvc0_channel_del(&priv->bar1);
nouveau_vm_ref(NULL, &dev_priv->bar1_vm, priv->bar1_pgd);
diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c
new file mode 100644
index 000000000000..929aded35cb5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_pm.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_bios.h"
+#include "nouveau_pm.h"
+
+static u32 read_div(struct drm_device *, int, u32, u32);
+static u32 read_pll(struct drm_device *, u32);
+
+static u32
+read_vco(struct drm_device *dev, u32 dsrc)
+{
+ u32 ssrc = nv_rd32(dev, dsrc);
+ if (!(ssrc & 0x00000100))
+ return read_pll(dev, 0x00e800);
+ return read_pll(dev, 0x00e820);
+}
+
+static u32
+read_pll(struct drm_device *dev, u32 pll)
+{
+ u32 ctrl = nv_rd32(dev, pll + 0);
+ u32 coef = nv_rd32(dev, pll + 4);
+ u32 P = (coef & 0x003f0000) >> 16;
+ u32 N = (coef & 0x0000ff00) >> 8;
+ u32 M = (coef & 0x000000ff) >> 0;
+ u32 sclk, doff;
+
+ if (!(ctrl & 0x00000001))
+ return 0;
+
+ switch (pll & 0xfff000) {
+ case 0x00e000:
+ sclk = 27000;
+ P = 1;
+ break;
+ case 0x137000:
+ doff = (pll - 0x137000) / 0x20;
+ sclk = read_div(dev, doff, 0x137120, 0x137140);
+ break;
+ case 0x132000:
+ switch (pll) {
+ case 0x132000:
+ sclk = read_pll(dev, 0x132020);
+ break;
+ case 0x132020:
+ sclk = read_div(dev, 0, 0x137320, 0x137330);
+ break;
+ default:
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ return sclk * N / M / P;
+}
+
+static u32
+read_div(struct drm_device *dev, int doff, u32 dsrc, u32 dctl)
+{
+ u32 ssrc = nv_rd32(dev, dsrc + (doff * 4));
+ u32 sctl = nv_rd32(dev, dctl + (doff * 4));
+
+ switch (ssrc & 0x00000003) {
+ case 0:
+ if ((ssrc & 0x00030000) != 0x00030000)
+ return 27000;
+ return 108000;
+ case 2:
+ return 100000;
+ case 3:
+ if (sctl & 0x80000000) {
+ u32 sclk = read_vco(dev, dsrc + (doff * 4));
+ u32 sdiv = (sctl & 0x0000003f) + 2;
+ return (sclk * 2) / sdiv;
+ }
+
+ return read_vco(dev, dsrc + (doff * 4));
+ default:
+ return 0;
+ }
+}
+
+static u32
+read_mem(struct drm_device *dev)
+{
+ u32 ssel = nv_rd32(dev, 0x1373f0);
+ if (ssel & 0x00000001)
+ return read_div(dev, 0, 0x137300, 0x137310);
+ return read_pll(dev, 0x132000);
+}
+
+static u32
+read_clk(struct drm_device *dev, int clk)
+{
+ u32 sctl = nv_rd32(dev, 0x137250 + (clk * 4));
+ u32 ssel = nv_rd32(dev, 0x137100);
+ u32 sclk, sdiv;
+
+ if (ssel & (1 << clk)) {
+ if (clk < 7)
+ sclk = read_pll(dev, 0x137000 + (clk * 0x20));
+ else
+ sclk = read_pll(dev, 0x1370e0);
+ sdiv = ((sctl & 0x00003f00) >> 8) + 2;
+ } else {
+ sclk = read_div(dev, clk, 0x137160, 0x1371d0);
+ sdiv = ((sctl & 0x0000003f) >> 0) + 2;
+ }
+
+ if (sctl & 0x80000000)
+ return (sclk * 2) / sdiv;
+ return sclk;
+}
+
+int
+nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
+{
+ perflvl->shader = read_clk(dev, 0x00);
+ perflvl->core = perflvl->shader / 2;
+ perflvl->memory = read_mem(dev);
+ perflvl->rop = read_clk(dev, 0x01);
+ perflvl->hub07 = read_clk(dev, 0x02);
+ perflvl->hub06 = read_clk(dev, 0x07);
+ perflvl->hub01 = read_clk(dev, 0x08);
+ perflvl->copy = read_clk(dev, 0x09);
+ perflvl->daemon = read_clk(dev, 0x0c);
+ perflvl->vdec = read_clk(dev, 0x0e);
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvc0_vm.c b/drivers/gpu/drm/nouveau/nvc0_vm.c
index a179e6c55afb..9e352944a35a 100644
--- a/drivers/gpu/drm/nouveau/nvc0_vm.c
+++ b/drivers/gpu/drm/nouveau/nvc0_vm.c
@@ -105,7 +105,11 @@ nvc0_vm_flush(struct nouveau_vm *vm)
struct drm_device *dev = vm->dev;
struct nouveau_vm_pgd *vpgd;
unsigned long flags;
- u32 engine = (dev_priv->chan_vm == vm) ? 1 : 5;
+ u32 engine;
+
+ engine = 1;
+ if (vm == dev_priv->bar1_vm || vm == dev_priv->bar3_vm)
+ engine |= 4;
pinstmem->flush(vm->dev);
diff --git a/drivers/gpu/drm/nouveau/nvc0_vram.c b/drivers/gpu/drm/nouveau/nvc0_vram.c
index 67c6ec6f34ea..ce984d573a51 100644
--- a/drivers/gpu/drm/nouveau/nvc0_vram.c
+++ b/drivers/gpu/drm/nouveau/nvc0_vram.c
@@ -43,7 +43,7 @@ static const u8 types[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
- 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3,
+ 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3,
3, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 3,
3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 0, 3, 0, 3, 3, 0,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0
@@ -61,9 +61,7 @@ nvc0_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin,
u32 type, struct nouveau_mem **pmem)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
- struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM];
- struct nouveau_mm *mm = man->priv;
+ struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
struct nouveau_mm_node *r;
struct nouveau_mem *mem;
int ret;
@@ -105,9 +103,57 @@ int
nvc0_vram_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
+ const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+ const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+ u32 parts = nv_rd32(dev, 0x121c74);
+ u32 bsize = nv_rd32(dev, 0x10f20c);
+ u32 offset, length;
+ bool uniform = true;
+ int ret, part;
+
+ NV_DEBUG(dev, "0x100800: 0x%08x\n", nv_rd32(dev, 0x100800));
+ NV_DEBUG(dev, "parts 0x%08x bcast_mem_amount 0x%08x\n", parts, bsize);
+
+ /* read amount of vram attached to each memory controller */
+ part = 0;
+ while (parts) {
+ u32 psize = nv_rd32(dev, 0x11020c + (part++ * 0x1000));
+ if (psize == 0)
+ continue;
+ parts--;
+
+ if (psize != bsize) {
+ if (psize < bsize)
+ bsize = psize;
+ uniform = false;
+ }
+
+ NV_DEBUG(dev, "%d: mem_amount 0x%08x\n", part, psize);
+ dev_priv->vram_size += (u64)psize << 20;
+ }
+
+ /* if all controllers have the same amount attached, there's no holes */
+ if (uniform) {
+ offset = rsvd_head;
+ length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail;
+ return nouveau_mm_init(&vram->mm, offset, length, 1);
+ }
+
+ /* otherwise, address lowest common amount from 0GiB */
+ ret = nouveau_mm_init(&vram->mm, rsvd_head, (bsize << 8) * parts, 1);
+ if (ret)
+ return ret;
+
+ /* and the rest starting from (8GiB + common_size) */
+ offset = (0x0200000000ULL >> 12) + (bsize << 8);
+ length = (dev_priv->vram_size >> 12) - (bsize << 8) - rsvd_tail;
+
+ ret = nouveau_mm_init(&vram->mm, offset, length, 0);
+ if (ret) {
+ nouveau_mm_fini(&vram->mm);
+ return ret;
+ }
- dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20;
- dev_priv->vram_size *= nv_rd32(dev, 0x121c74);
- dev_priv->vram_rblock_size = 4096;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
new file mode 100644
index 000000000000..23d63b4b3d77
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -0,0 +1,1473 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <linux/dma-mapping.h>
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_connector.h"
+#include "nouveau_encoder.h"
+#include "nouveau_crtc.h"
+#include "nouveau_dma.h"
+#include "nouveau_fb.h"
+#include "nv50_display.h"
+
+struct nvd0_display {
+ struct nouveau_gpuobj *mem;
+ struct {
+ dma_addr_t handle;
+ u32 *ptr;
+ } evo[1];
+
+ struct tasklet_struct tasklet;
+ u32 modeset;
+};
+
+static struct nvd0_display *
+nvd0_display(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ return dev_priv->engine.display.priv;
+}
+
+static inline int
+evo_icmd(struct drm_device *dev, int id, u32 mthd, u32 data)
+{
+ int ret = 0;
+ nv_mask(dev, 0x610700 + (id * 0x10), 0x00000001, 0x00000001);
+ nv_wr32(dev, 0x610704 + (id * 0x10), data);
+ nv_mask(dev, 0x610704 + (id * 0x10), 0x80000ffc, 0x80000000 | mthd);
+ if (!nv_wait(dev, 0x610704 + (id * 0x10), 0x80000000, 0x00000000))
+ ret = -EBUSY;
+ nv_mask(dev, 0x610700 + (id * 0x10), 0x00000001, 0x00000000);
+ return ret;
+}
+
+static u32 *
+evo_wait(struct drm_device *dev, int id, int nr)
+{
+ struct nvd0_display *disp = nvd0_display(dev);
+ u32 put = nv_rd32(dev, 0x640000 + (id * 0x1000)) / 4;
+
+ if (put + nr >= (PAGE_SIZE / 4)) {
+ disp->evo[id].ptr[put] = 0x20000000;
+
+ nv_wr32(dev, 0x640000 + (id * 0x1000), 0x00000000);
+ if (!nv_wait(dev, 0x640004 + (id * 0x1000), ~0, 0x00000000)) {
+ NV_ERROR(dev, "evo %d dma stalled\n", id);
+ return NULL;
+ }
+
+ put = 0;
+ }
+
+ return disp->evo[id].ptr + put;
+}
+
+static void
+evo_kick(u32 *push, struct drm_device *dev, int id)
+{
+ struct nvd0_display *disp = nvd0_display(dev);
+ nv_wr32(dev, 0x640000 + (id * 0x1000), (push - disp->evo[id].ptr) << 2);
+}
+
+#define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m))
+#define evo_data(p,d) *((p)++) = (d)
+
+static struct drm_crtc *
+nvd0_display_crtc_get(struct drm_encoder *encoder)
+{
+ return nouveau_encoder(encoder)->crtc;
+}
+
+/******************************************************************************
+ * CRTC
+ *****************************************************************************/
+static int
+nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
+{
+ struct drm_device *dev = nv_crtc->base.dev;
+ u32 *push, mode;
+
+ mode = 0x00000000;
+ if (on) {
+ /* 0x11: 6bpc dynamic 2x2
+ * 0x13: 8bpc dynamic 2x2
+ * 0x19: 6bpc static 2x2
+ * 0x1b: 8bpc static 2x2
+ * 0x21: 6bpc temporal
+ * 0x23: 8bpc temporal
+ */
+ mode = 0x00000011;
+ }
+
+ push = evo_wait(dev, 0, 4);
+ if (push) {
+ evo_mthd(push, 0x0490 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, mode);
+ if (update) {
+ evo_mthd(push, 0x0080, 1);
+ evo_data(push, 0x00000000);
+ }
+ evo_kick(push, dev, 0);
+ }
+
+ return 0;
+}
+
+static int
+nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, int type, bool update)
+{
+ struct drm_display_mode *mode = &nv_crtc->base.mode;
+ struct drm_device *dev = nv_crtc->base.dev;
+ struct nouveau_connector *nv_connector;
+ u32 *push, outX, outY;
+
+ outX = mode->hdisplay;
+ outY = mode->vdisplay;
+
+ nv_connector = nouveau_crtc_connector_get(nv_crtc);
+ if (nv_connector && nv_connector->native_mode) {
+ struct drm_display_mode *native = nv_connector->native_mode;
+ u32 xratio = (native->hdisplay << 19) / mode->hdisplay;
+ u32 yratio = (native->vdisplay << 19) / mode->vdisplay;
+
+ switch (type) {
+ case DRM_MODE_SCALE_ASPECT:
+ if (xratio > yratio) {
+ outX = (mode->hdisplay * yratio) >> 19;
+ outY = (mode->vdisplay * yratio) >> 19;
+ } else {
+ outX = (mode->hdisplay * xratio) >> 19;
+ outY = (mode->vdisplay * xratio) >> 19;
+ }
+ break;
+ case DRM_MODE_SCALE_FULLSCREEN:
+ outX = native->hdisplay;
+ outY = native->vdisplay;
+ break;
+ default:
+ break;
+ }
+ }
+
+ push = evo_wait(dev, 0, 16);
+ if (push) {
+ evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3);
+ evo_data(push, (outY << 16) | outX);
+ evo_data(push, (outY << 16) | outX);
+ evo_data(push, (outY << 16) | outX);
+ evo_mthd(push, 0x0494 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, 0x00000000);
+ evo_mthd(push, 0x04b8 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, (mode->vdisplay << 16) | mode->hdisplay);
+ if (update) {
+ evo_mthd(push, 0x0080, 1);
+ evo_data(push, 0x00000000);
+ }
+ evo_kick(push, dev, 0);
+ }
+
+ return 0;
+}
+
+static int
+nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb,
+ int x, int y, bool update)
+{
+ struct nouveau_framebuffer *nvfb = nouveau_framebuffer(fb);
+ u32 *push;
+
+ push = evo_wait(fb->dev, 0, 16);
+ if (push) {
+ evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, nvfb->nvbo->bo.offset >> 8);
+ evo_mthd(push, 0x0468 + (nv_crtc->index * 0x300), 4);
+ evo_data(push, (fb->height << 16) | fb->width);
+ evo_data(push, nvfb->r_pitch);
+ evo_data(push, nvfb->r_format);
+ evo_data(push, nvfb->r_dma);
+ evo_mthd(push, 0x04b0 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, (y << 16) | x);
+ if (update) {
+ evo_mthd(push, 0x0080, 1);
+ evo_data(push, 0x00000000);
+ }
+ evo_kick(push, fb->dev, 0);
+ }
+
+ nv_crtc->fb.tile_flags = nvfb->r_dma;
+ return 0;
+}
+
+static void
+nvd0_crtc_cursor_show(struct nouveau_crtc *nv_crtc, bool show, bool update)
+{
+ struct drm_device *dev = nv_crtc->base.dev;
+ u32 *push = evo_wait(dev, 0, 16);
+ if (push) {
+ if (show) {
+ evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 2);
+ evo_data(push, 0x85000000);
+ evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8);
+ evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1);
+ evo_data(push, NvEvoVRAM);
+ } else {
+ evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, 0x05000000);
+ evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1);
+ evo_data(push, 0x00000000);
+ }
+
+ if (update) {
+ evo_mthd(push, 0x0080, 1);
+ evo_data(push, 0x00000000);
+ }
+
+ evo_kick(push, dev, 0);
+ }
+}
+
+static void
+nvd0_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+}
+
+static void
+nvd0_crtc_prepare(struct drm_crtc *crtc)
+{
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ u32 *push;
+
+ push = evo_wait(crtc->dev, 0, 2);
+ if (push) {
+ evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, 0x00000000);
+ evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, 0x03000000);
+ evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1);
+ evo_data(push, 0x00000000);
+ evo_kick(push, crtc->dev, 0);
+ }
+
+ nvd0_crtc_cursor_show(nv_crtc, false, false);
+}
+
+static void
+nvd0_crtc_commit(struct drm_crtc *crtc)
+{
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ u32 *push;
+
+ push = evo_wait(crtc->dev, 0, 32);
+ if (push) {
+ evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, nv_crtc->fb.tile_flags);
+ evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 4);
+ evo_data(push, 0x83000000);
+ evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8);
+ evo_data(push, 0x00000000);
+ evo_data(push, 0x00000000);
+ evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1);
+ evo_data(push, NvEvoVRAM);
+ evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, 0xffffff00);
+ evo_kick(push, crtc->dev, 0);
+ }
+
+ nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, true);
+}
+
+static bool
+nvd0_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static int
+nvd0_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
+{
+ struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb);
+ int ret;
+
+ ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM);
+ if (ret)
+ return ret;
+
+ if (old_fb) {
+ nvfb = nouveau_framebuffer(old_fb);
+ nouveau_bo_unpin(nvfb->nvbo);
+ }
+
+ return 0;
+}
+
+static int
+nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
+ struct drm_display_mode *mode, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ struct nouveau_connector *nv_connector;
+ u32 htotal = mode->htotal;
+ u32 vtotal = mode->vtotal;
+ u32 hsyncw = mode->hsync_end - mode->hsync_start - 1;
+ u32 vsyncw = mode->vsync_end - mode->vsync_start - 1;
+ u32 hfrntp = mode->hsync_start - mode->hdisplay;
+ u32 vfrntp = mode->vsync_start - mode->vdisplay;
+ u32 hbackp = mode->htotal - mode->hsync_end;
+ u32 vbackp = mode->vtotal - mode->vsync_end;
+ u32 hss2be = hsyncw + hbackp;
+ u32 vss2be = vsyncw + vbackp;
+ u32 hss2de = htotal - hfrntp;
+ u32 vss2de = vtotal - vfrntp;
+ u32 syncs, *push;
+ int ret;
+
+ syncs = 0x00000001;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ syncs |= 0x00000008;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ syncs |= 0x00000010;
+
+ ret = nvd0_crtc_swap_fbs(crtc, old_fb);
+ if (ret)
+ return ret;
+
+ push = evo_wait(crtc->dev, 0, 64);
+ if (push) {
+ evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 5);
+ evo_data(push, 0x00000000);
+ evo_data(push, (vtotal << 16) | htotal);
+ evo_data(push, (vsyncw << 16) | hsyncw);
+ evo_data(push, (vss2be << 16) | hss2be);
+ evo_data(push, (vss2de << 16) | hss2de);
+ evo_mthd(push, 0x042c + (nv_crtc->index * 0x300), 1);
+ evo_data(push, 0x00000000); /* ??? */
+ evo_mthd(push, 0x0450 + (nv_crtc->index * 0x300), 3);
+ evo_data(push, mode->clock * 1000);
+ evo_data(push, 0x00200000); /* ??? */
+ evo_data(push, mode->clock * 1000);
+ evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 1);
+ evo_data(push, syncs);
+ evo_kick(push, crtc->dev, 0);
+ }
+
+ nv_connector = nouveau_crtc_connector_get(nv_crtc);
+ nvd0_crtc_set_dither(nv_crtc, nv_connector->use_dithering, false);
+ nvd0_crtc_set_scale(nv_crtc, nv_connector->scaling_mode, false);
+ nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, false);
+ return 0;
+}
+
+static int
+nvd0_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ int ret;
+
+ if (!crtc->fb) {
+ NV_DEBUG_KMS(crtc->dev, "No FB bound\n");
+ return 0;
+ }
+
+ ret = nvd0_crtc_swap_fbs(crtc, old_fb);
+ if (ret)
+ return ret;
+
+ nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, true);
+ return 0;
+}
+
+static int
+nvd0_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int x, int y,
+ enum mode_set_atomic state)
+{
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ nvd0_crtc_set_image(nv_crtc, fb, x, y, true);
+ return 0;
+}
+
+static void
+nvd0_crtc_lut_load(struct drm_crtc *crtc)
+{
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo);
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ writew(0x6000 + (nv_crtc->lut.r[i] >> 2), lut + (i * 0x20) + 0);
+ writew(0x6000 + (nv_crtc->lut.g[i] >> 2), lut + (i * 0x20) + 2);
+ writew(0x6000 + (nv_crtc->lut.b[i] >> 2), lut + (i * 0x20) + 4);
+ }
+}
+
+static int
+nvd0_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
+ uint32_t handle, uint32_t width, uint32_t height)
+{
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ struct drm_gem_object *gem;
+ struct nouveau_bo *nvbo;
+ bool visible = (handle != 0);
+ int i, ret = 0;
+
+ if (visible) {
+ if (width != 64 || height != 64)
+ return -EINVAL;
+
+ gem = drm_gem_object_lookup(dev, file_priv, handle);
+ if (unlikely(!gem))
+ return -ENOENT;
+ nvbo = nouveau_gem_object(gem);
+
+ ret = nouveau_bo_map(nvbo);
+ if (ret == 0) {
+ for (i = 0; i < 64 * 64; i++) {
+ u32 v = nouveau_bo_rd32(nvbo, i);
+ nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, v);
+ }
+ nouveau_bo_unmap(nvbo);
+ }
+
+ drm_gem_object_unreference_unlocked(gem);
+ }
+
+ if (visible != nv_crtc->cursor.visible) {
+ nvd0_crtc_cursor_show(nv_crtc, visible, true);
+ nv_crtc->cursor.visible = visible;
+ }
+
+ return ret;
+}
+
+static int
+nvd0_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ const u32 data = (y << 16) | x;
+
+ nv_wr32(crtc->dev, 0x64d084 + (nv_crtc->index * 0x1000), data);
+ nv_wr32(crtc->dev, 0x64d080 + (nv_crtc->index * 0x1000), 0x00000000);
+ return 0;
+}
+
+static void
+nvd0_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
+ uint32_t start, uint32_t size)
+{
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ u32 end = max(start + size, (u32)256);
+ u32 i;
+
+ for (i = start; i < end; i++) {
+ nv_crtc->lut.r[i] = r[i];
+ nv_crtc->lut.g[i] = g[i];
+ nv_crtc->lut.b[i] = b[i];
+ }
+
+ nvd0_crtc_lut_load(crtc);
+}
+
+static void
+nvd0_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ nouveau_bo_unmap(nv_crtc->cursor.nvbo);
+ nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
+ nouveau_bo_unmap(nv_crtc->lut.nvbo);
+ nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
+ drm_crtc_cleanup(crtc);
+ kfree(crtc);
+}
+
+static const struct drm_crtc_helper_funcs nvd0_crtc_hfunc = {
+ .dpms = nvd0_crtc_dpms,
+ .prepare = nvd0_crtc_prepare,
+ .commit = nvd0_crtc_commit,
+ .mode_fixup = nvd0_crtc_mode_fixup,
+ .mode_set = nvd0_crtc_mode_set,
+ .mode_set_base = nvd0_crtc_mode_set_base,
+ .mode_set_base_atomic = nvd0_crtc_mode_set_base_atomic,
+ .load_lut = nvd0_crtc_lut_load,
+};
+
+static const struct drm_crtc_funcs nvd0_crtc_func = {
+ .cursor_set = nvd0_crtc_cursor_set,
+ .cursor_move = nvd0_crtc_cursor_move,
+ .gamma_set = nvd0_crtc_gamma_set,
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = nvd0_crtc_destroy,
+};
+
+static void
+nvd0_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y)
+{
+}
+
+static void
+nvd0_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
+{
+}
+
+static int
+nvd0_crtc_create(struct drm_device *dev, int index)
+{
+ struct nouveau_crtc *nv_crtc;
+ struct drm_crtc *crtc;
+ int ret, i;
+
+ nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL);
+ if (!nv_crtc)
+ return -ENOMEM;
+
+ nv_crtc->index = index;
+ nv_crtc->set_dither = nvd0_crtc_set_dither;
+ nv_crtc->set_scale = nvd0_crtc_set_scale;
+ nv_crtc->cursor.set_offset = nvd0_cursor_set_offset;
+ nv_crtc->cursor.set_pos = nvd0_cursor_set_pos;
+ for (i = 0; i < 256; i++) {
+ nv_crtc->lut.r[i] = i << 8;
+ nv_crtc->lut.g[i] = i << 8;
+ nv_crtc->lut.b[i] = i << 8;
+ }
+
+ crtc = &nv_crtc->base;
+ drm_crtc_init(dev, crtc, &nvd0_crtc_func);
+ drm_crtc_helper_add(crtc, &nvd0_crtc_hfunc);
+ drm_mode_crtc_set_gamma_size(crtc, 256);
+
+ ret = nouveau_bo_new(dev, 64 * 64 * 4, 0x100, TTM_PL_FLAG_VRAM,
+ 0, 0x0000, &nv_crtc->cursor.nvbo);
+ if (!ret) {
+ ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
+ if (!ret)
+ ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
+ if (ret)
+ nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
+ }
+
+ if (ret)
+ goto out;
+
+ ret = nouveau_bo_new(dev, 8192, 0x100, TTM_PL_FLAG_VRAM,
+ 0, 0x0000, &nv_crtc->lut.nvbo);
+ if (!ret) {
+ ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM);
+ if (!ret)
+ ret = nouveau_bo_map(nv_crtc->lut.nvbo);
+ if (ret)
+ nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
+ }
+
+ if (ret)
+ goto out;
+
+ nvd0_crtc_lut_load(crtc);
+
+out:
+ if (ret)
+ nvd0_crtc_destroy(crtc);
+ return ret;
+}
+
+/******************************************************************************
+ * DAC
+ *****************************************************************************/
+static void
+nvd0_dac_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ int or = nv_encoder->or;
+ u32 dpms_ctrl;
+
+ dpms_ctrl = 0x80000000;
+ if (mode == DRM_MODE_DPMS_STANDBY || mode == DRM_MODE_DPMS_OFF)
+ dpms_ctrl |= 0x00000001;
+ if (mode == DRM_MODE_DPMS_SUSPEND || mode == DRM_MODE_DPMS_OFF)
+ dpms_ctrl |= 0x00000004;
+
+ nv_wait(dev, 0x61a004 + (or * 0x0800), 0x80000000, 0x00000000);
+ nv_mask(dev, 0x61a004 + (or * 0x0800), 0xc000007f, dpms_ctrl);
+ nv_wait(dev, 0x61a004 + (or * 0x0800), 0x80000000, 0x00000000);
+}
+
+static bool
+nvd0_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nouveau_connector *nv_connector;
+
+ nv_connector = nouveau_encoder_connector_get(nv_encoder);
+ if (nv_connector && nv_connector->native_mode) {
+ if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) {
+ int id = adjusted_mode->base.id;
+ *adjusted_mode = *nv_connector->native_mode;
+ adjusted_mode->base.id = id;
+ }
+ }
+
+ return true;
+}
+
+static void
+nvd0_dac_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void
+nvd0_dac_commit(struct drm_encoder *encoder)
+{
+}
+
+static void
+nvd0_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+ u32 *push;
+
+ nvd0_dac_dpms(encoder, DRM_MODE_DPMS_ON);
+
+ push = evo_wait(encoder->dev, 0, 4);
+ if (push) {
+ evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 2);
+ evo_data(push, 1 << nv_crtc->index);
+ evo_data(push, 0x00ff);
+ evo_kick(push, encoder->dev, 0);
+ }
+
+ nv_encoder->crtc = encoder->crtc;
+}
+
+static void
+nvd0_dac_disconnect(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ u32 *push;
+
+ if (nv_encoder->crtc) {
+ nvd0_crtc_prepare(nv_encoder->crtc);
+
+ push = evo_wait(dev, 0, 4);
+ if (push) {
+ evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 1);
+ evo_data(push, 0x00000000);
+ evo_mthd(push, 0x0080, 1);
+ evo_data(push, 0x00000000);
+ evo_kick(push, dev, 0);
+ }
+
+ nv_encoder->crtc = NULL;
+ }
+}
+
+static enum drm_connector_status
+nvd0_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
+{
+ enum drm_connector_status status = connector_status_disconnected;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ int or = nv_encoder->or;
+ u32 load;
+
+ nv_wr32(dev, 0x61a00c + (or * 0x800), 0x00100000);
+ udelay(9500);
+ nv_wr32(dev, 0x61a00c + (or * 0x800), 0x80000000);
+
+ load = nv_rd32(dev, 0x61a00c + (or * 0x800));
+ if ((load & 0x38000000) == 0x38000000)
+ status = connector_status_connected;
+
+ nv_wr32(dev, 0x61a00c + (or * 0x800), 0x00000000);
+ return status;
+}
+
+static void
+nvd0_dac_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+ kfree(encoder);
+}
+
+static const struct drm_encoder_helper_funcs nvd0_dac_hfunc = {
+ .dpms = nvd0_dac_dpms,
+ .mode_fixup = nvd0_dac_mode_fixup,
+ .prepare = nvd0_dac_prepare,
+ .commit = nvd0_dac_commit,
+ .mode_set = nvd0_dac_mode_set,
+ .disable = nvd0_dac_disconnect,
+ .get_crtc = nvd0_display_crtc_get,
+ .detect = nvd0_dac_detect
+};
+
+static const struct drm_encoder_funcs nvd0_dac_func = {
+ .destroy = nvd0_dac_destroy,
+};
+
+static int
+nvd0_dac_create(struct drm_connector *connector, struct dcb_entry *dcbe)
+{
+ struct drm_device *dev = connector->dev;
+ struct nouveau_encoder *nv_encoder;
+ struct drm_encoder *encoder;
+
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+ if (!nv_encoder)
+ return -ENOMEM;
+ nv_encoder->dcb = dcbe;
+ nv_encoder->or = ffs(dcbe->or) - 1;
+
+ encoder = to_drm_encoder(nv_encoder);
+ encoder->possible_crtcs = dcbe->heads;
+ encoder->possible_clones = 0;
+ drm_encoder_init(dev, encoder, &nvd0_dac_func, DRM_MODE_ENCODER_DAC);
+ drm_encoder_helper_add(encoder, &nvd0_dac_hfunc);
+
+ drm_mode_connector_attach_encoder(connector, encoder);
+ return 0;
+}
+
+/******************************************************************************
+ * SOR
+ *****************************************************************************/
+static void
+nvd0_sor_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct drm_encoder *partner;
+ int or = nv_encoder->or;
+ u32 dpms_ctrl;
+
+ nv_encoder->last_dpms = mode;
+
+ list_for_each_entry(partner, &dev->mode_config.encoder_list, head) {
+ struct nouveau_encoder *nv_partner = nouveau_encoder(partner);
+
+ if (partner->encoder_type != DRM_MODE_ENCODER_TMDS)
+ continue;
+
+ if (nv_partner != nv_encoder &&
+ nv_partner->dcb->or == nv_encoder->or) {
+ if (nv_partner->last_dpms == DRM_MODE_DPMS_ON)
+ return;
+ break;
+ }
+ }
+
+ dpms_ctrl = (mode == DRM_MODE_DPMS_ON);
+ dpms_ctrl |= 0x80000000;
+
+ nv_wait(dev, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000);
+ nv_mask(dev, 0x61c004 + (or * 0x0800), 0x80000001, dpms_ctrl);
+ nv_wait(dev, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000);
+ nv_wait(dev, 0x61c030 + (or * 0x0800), 0x10000000, 0x00000000);
+}
+
+static bool
+nvd0_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nouveau_connector *nv_connector;
+
+ nv_connector = nouveau_encoder_connector_get(nv_encoder);
+ if (nv_connector && nv_connector->native_mode) {
+ if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) {
+ int id = adjusted_mode->base.id;
+ *adjusted_mode = *nv_connector->native_mode;
+ adjusted_mode->base.id = id;
+ }
+ }
+
+ return true;
+}
+
+static void
+nvd0_sor_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void
+nvd0_sor_commit(struct drm_encoder *encoder)
+{
+}
+
+static void
+nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
+ struct drm_display_mode *mode)
+{
+ struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+ struct nouveau_connector *nv_connector;
+ struct nvbios *bios = &dev_priv->vbios;
+ u32 mode_ctrl = (1 << nv_crtc->index);
+ u32 *push, or_config;
+
+ nv_connector = nouveau_encoder_connector_get(nv_encoder);
+ switch (nv_encoder->dcb->type) {
+ case OUTPUT_TMDS:
+ if (nv_encoder->dcb->sorconf.link & 1) {
+ if (mode->clock < 165000)
+ mode_ctrl |= 0x00000100;
+ else
+ mode_ctrl |= 0x00000500;
+ } else {
+ mode_ctrl |= 0x00000200;
+ }
+
+ or_config = (mode_ctrl & 0x00000f00) >> 8;
+ if (mode->clock >= 165000)
+ or_config |= 0x0100;
+ break;
+ case OUTPUT_LVDS:
+ or_config = (mode_ctrl & 0x00000f00) >> 8;
+ if (bios->fp_no_ddc) {
+ if (bios->fp.dual_link)
+ or_config |= 0x0100;
+ if (bios->fp.if_is_24bit)
+ or_config |= 0x0200;
+ } else {
+ if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) {
+ if (((u8 *)nv_connector->edid)[121] == 2)
+ or_config |= 0x0100;
+ } else
+ if (mode->clock >= bios->fp.duallink_transition_clk) {
+ or_config |= 0x0100;
+ }
+
+ if (or_config & 0x0100) {
+ if (bios->fp.strapless_is_24bit & 2)
+ or_config |= 0x0200;
+ } else {
+ if (bios->fp.strapless_is_24bit & 1)
+ or_config |= 0x0200;
+ }
+
+ if (nv_connector->base.display_info.bpc == 8)
+ or_config |= 0x0200;
+
+ }
+ break;
+ default:
+ BUG_ON(1);
+ break;
+ }
+
+ nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON);
+
+ push = evo_wait(encoder->dev, 0, 4);
+ if (push) {
+ evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2);
+ evo_data(push, mode_ctrl);
+ evo_data(push, or_config);
+ evo_kick(push, encoder->dev, 0);
+ }
+
+ nv_encoder->crtc = encoder->crtc;
+}
+
+static void
+nvd0_sor_disconnect(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ u32 *push;
+
+ if (nv_encoder->crtc) {
+ nvd0_crtc_prepare(nv_encoder->crtc);
+
+ push = evo_wait(dev, 0, 4);
+ if (push) {
+ evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1);
+ evo_data(push, 0x00000000);
+ evo_mthd(push, 0x0080, 1);
+ evo_data(push, 0x00000000);
+ evo_kick(push, dev, 0);
+ }
+
+ nv_encoder->crtc = NULL;
+ nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
+ }
+}
+
+static void
+nvd0_sor_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+ kfree(encoder);
+}
+
+static const struct drm_encoder_helper_funcs nvd0_sor_hfunc = {
+ .dpms = nvd0_sor_dpms,
+ .mode_fixup = nvd0_sor_mode_fixup,
+ .prepare = nvd0_sor_prepare,
+ .commit = nvd0_sor_commit,
+ .mode_set = nvd0_sor_mode_set,
+ .disable = nvd0_sor_disconnect,
+ .get_crtc = nvd0_display_crtc_get,
+};
+
+static const struct drm_encoder_funcs nvd0_sor_func = {
+ .destroy = nvd0_sor_destroy,
+};
+
+static int
+nvd0_sor_create(struct drm_connector *connector, struct dcb_entry *dcbe)
+{
+ struct drm_device *dev = connector->dev;
+ struct nouveau_encoder *nv_encoder;
+ struct drm_encoder *encoder;
+
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+ if (!nv_encoder)
+ return -ENOMEM;
+ nv_encoder->dcb = dcbe;
+ nv_encoder->or = ffs(dcbe->or) - 1;
+ nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
+
+ encoder = to_drm_encoder(nv_encoder);
+ encoder->possible_crtcs = dcbe->heads;
+ encoder->possible_clones = 0;
+ drm_encoder_init(dev, encoder, &nvd0_sor_func, DRM_MODE_ENCODER_TMDS);
+ drm_encoder_helper_add(encoder, &nvd0_sor_hfunc);
+
+ drm_mode_connector_attach_encoder(connector, encoder);
+ return 0;
+}
+
+/******************************************************************************
+ * IRQ
+ *****************************************************************************/
+static struct dcb_entry *
+lookup_dcb(struct drm_device *dev, int id, u32 mc)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int type, or, i;
+
+ if (id < 4) {
+ type = OUTPUT_ANALOG;
+ or = id;
+ } else {
+ switch (mc & 0x00000f00) {
+ case 0x00000000: type = OUTPUT_LVDS; break;
+ case 0x00000100: type = OUTPUT_TMDS; break;
+ case 0x00000200: type = OUTPUT_TMDS; break;
+ case 0x00000500: type = OUTPUT_TMDS; break;
+ default:
+ NV_ERROR(dev, "PDISP: unknown SOR mc 0x%08x\n", mc);
+ return NULL;
+ }
+
+ or = id - 4;
+ }
+
+ for (i = 0; i < dev_priv->vbios.dcb.entries; i++) {
+ struct dcb_entry *dcb = &dev_priv->vbios.dcb.entry[i];
+ if (dcb->type == type && (dcb->or & (1 << or)))
+ return dcb;
+ }
+
+ NV_ERROR(dev, "PDISP: DCB for %d/0x%08x not found\n", id, mc);
+ return NULL;
+}
+
+static void
+nvd0_display_unk1_handler(struct drm_device *dev, u32 crtc, u32 mask)
+{
+ struct dcb_entry *dcb;
+ int i;
+
+ for (i = 0; mask && i < 8; i++) {
+ u32 mcc = nv_rd32(dev, 0x640180 + (i * 0x20));
+ if (!(mcc & (1 << crtc)))
+ continue;
+
+ dcb = lookup_dcb(dev, i, mcc);
+ if (!dcb)
+ continue;
+
+ nouveau_bios_run_display_table(dev, 0x0000, -1, dcb, crtc);
+ }
+
+ nv_wr32(dev, 0x6101d4, 0x00000000);
+ nv_wr32(dev, 0x6109d4, 0x00000000);
+ nv_wr32(dev, 0x6101d0, 0x80000000);
+}
+
+static void
+nvd0_display_unk2_handler(struct drm_device *dev, u32 crtc, u32 mask)
+{
+ struct dcb_entry *dcb;
+ u32 or, tmp, pclk;
+ int i;
+
+ for (i = 0; mask && i < 8; i++) {
+ u32 mcc = nv_rd32(dev, 0x640180 + (i * 0x20));
+ if (!(mcc & (1 << crtc)))
+ continue;
+
+ dcb = lookup_dcb(dev, i, mcc);
+ if (!dcb)
+ continue;
+
+ nouveau_bios_run_display_table(dev, 0x0000, -2, dcb, crtc);
+ }
+
+ pclk = nv_rd32(dev, 0x660450 + (crtc * 0x300)) / 1000;
+ if (mask & 0x00010000) {
+ nv50_crtc_set_clock(dev, crtc, pclk);
+ }
+
+ for (i = 0; mask && i < 8; i++) {
+ u32 mcp = nv_rd32(dev, 0x660180 + (i * 0x20));
+ u32 cfg = nv_rd32(dev, 0x660184 + (i * 0x20));
+ if (!(mcp & (1 << crtc)))
+ continue;
+
+ dcb = lookup_dcb(dev, i, mcp);
+ if (!dcb)
+ continue;
+ or = ffs(dcb->or) - 1;
+
+ nouveau_bios_run_display_table(dev, cfg, pclk, dcb, crtc);
+
+ nv_wr32(dev, 0x612200 + (crtc * 0x800), 0x00000000);
+ switch (dcb->type) {
+ case OUTPUT_ANALOG:
+ nv_wr32(dev, 0x612280 + (or * 0x800), 0x00000000);
+ break;
+ case OUTPUT_TMDS:
+ case OUTPUT_LVDS:
+ if (cfg & 0x00000100)
+ tmp = 0x00000101;
+ else
+ tmp = 0x00000000;
+
+ nv_mask(dev, 0x612300 + (or * 0x800), 0x00000707, tmp);
+ break;
+ default:
+ break;
+ }
+
+ break;
+ }
+
+ nv_wr32(dev, 0x6101d4, 0x00000000);
+ nv_wr32(dev, 0x6109d4, 0x00000000);
+ nv_wr32(dev, 0x6101d0, 0x80000000);
+}
+
+static void
+nvd0_display_unk4_handler(struct drm_device *dev, u32 crtc, u32 mask)
+{
+ struct dcb_entry *dcb;
+ int pclk, i;
+
+ pclk = nv_rd32(dev, 0x660450 + (crtc * 0x300)) / 1000;
+
+ for (i = 0; mask && i < 8; i++) {
+ u32 mcp = nv_rd32(dev, 0x660180 + (i * 0x20));
+ u32 cfg = nv_rd32(dev, 0x660184 + (i * 0x20));
+ if (!(mcp & (1 << crtc)))
+ continue;
+
+ dcb = lookup_dcb(dev, i, mcp);
+ if (!dcb)
+ continue;
+
+ nouveau_bios_run_display_table(dev, cfg, -pclk, dcb, crtc);
+ }
+
+ nv_wr32(dev, 0x6101d4, 0x00000000);
+ nv_wr32(dev, 0x6109d4, 0x00000000);
+ nv_wr32(dev, 0x6101d0, 0x80000000);
+}
+
+static void
+nvd0_display_bh(unsigned long data)
+{
+ struct drm_device *dev = (struct drm_device *)data;
+ struct nvd0_display *disp = nvd0_display(dev);
+ u32 mask, crtc;
+ int i;
+
+ if (drm_debug & (DRM_UT_DRIVER | DRM_UT_KMS)) {
+ NV_INFO(dev, "PDISP: modeset req %d\n", disp->modeset);
+ NV_INFO(dev, " STAT: 0x%08x 0x%08x 0x%08x\n",
+ nv_rd32(dev, 0x6101d0),
+ nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4));
+ for (i = 0; i < 8; i++) {
+ NV_INFO(dev, " %s%d: 0x%08x 0x%08x\n",
+ i < 4 ? "DAC" : "SOR", i,
+ nv_rd32(dev, 0x640180 + (i * 0x20)),
+ nv_rd32(dev, 0x660180 + (i * 0x20)));
+ }
+ }
+
+ mask = nv_rd32(dev, 0x6101d4);
+ crtc = 0;
+ if (!mask) {
+ mask = nv_rd32(dev, 0x6109d4);
+ crtc = 1;
+ }
+
+ if (disp->modeset & 0x00000001)
+ nvd0_display_unk1_handler(dev, crtc, mask);
+ if (disp->modeset & 0x00000002)
+ nvd0_display_unk2_handler(dev, crtc, mask);
+ if (disp->modeset & 0x00000004)
+ nvd0_display_unk4_handler(dev, crtc, mask);
+}
+
+static void
+nvd0_display_intr(struct drm_device *dev)
+{
+ struct nvd0_display *disp = nvd0_display(dev);
+ u32 intr = nv_rd32(dev, 0x610088);
+
+ if (intr & 0x00000002) {
+ u32 stat = nv_rd32(dev, 0x61009c);
+ int chid = ffs(stat) - 1;
+ if (chid >= 0) {
+ u32 mthd = nv_rd32(dev, 0x6101f0 + (chid * 12));
+ u32 data = nv_rd32(dev, 0x6101f4 + (chid * 12));
+ u32 unkn = nv_rd32(dev, 0x6101f8 + (chid * 12));
+
+ NV_INFO(dev, "EvoCh: chid %d mthd 0x%04x data 0x%08x "
+ "0x%08x 0x%08x\n",
+ chid, (mthd & 0x0000ffc), data, mthd, unkn);
+ nv_wr32(dev, 0x61009c, (1 << chid));
+ nv_wr32(dev, 0x6101f0 + (chid * 12), 0x90000000);
+ }
+
+ intr &= ~0x00000002;
+ }
+
+ if (intr & 0x00100000) {
+ u32 stat = nv_rd32(dev, 0x6100ac);
+
+ if (stat & 0x00000007) {
+ disp->modeset = stat;
+ tasklet_schedule(&disp->tasklet);
+
+ nv_wr32(dev, 0x6100ac, (stat & 0x00000007));
+ stat &= ~0x00000007;
+ }
+
+ if (stat) {
+ NV_INFO(dev, "PDISP: unknown intr24 0x%08x\n", stat);
+ nv_wr32(dev, 0x6100ac, stat);
+ }
+
+ intr &= ~0x00100000;
+ }
+
+ if (intr & 0x01000000) {
+ u32 stat = nv_rd32(dev, 0x6100bc);
+ nv_wr32(dev, 0x6100bc, stat);
+ intr &= ~0x01000000;
+ }
+
+ if (intr & 0x02000000) {
+ u32 stat = nv_rd32(dev, 0x6108bc);
+ nv_wr32(dev, 0x6108bc, stat);
+ intr &= ~0x02000000;
+ }
+
+ if (intr)
+ NV_INFO(dev, "PDISP: unknown intr 0x%08x\n", intr);
+}
+
+/******************************************************************************
+ * Init
+ *****************************************************************************/
+static void
+nvd0_display_fini(struct drm_device *dev)
+{
+ int i;
+
+ /* fini cursors */
+ for (i = 14; i >= 13; i--) {
+ if (!(nv_rd32(dev, 0x610490 + (i * 0x10)) & 0x00000001))
+ continue;
+
+ nv_mask(dev, 0x610490 + (i * 0x10), 0x00000001, 0x00000000);
+ nv_wait(dev, 0x610490 + (i * 0x10), 0x00010000, 0x00000000);
+ nv_mask(dev, 0x610090, 1 << i, 0x00000000);
+ nv_mask(dev, 0x6100a0, 1 << i, 0x00000000);
+ }
+
+ /* fini master */
+ if (nv_rd32(dev, 0x610490) & 0x00000010) {
+ nv_mask(dev, 0x610490, 0x00000010, 0x00000000);
+ nv_mask(dev, 0x610490, 0x00000003, 0x00000000);
+ nv_wait(dev, 0x610490, 0x80000000, 0x00000000);
+ nv_mask(dev, 0x610090, 0x00000001, 0x00000000);
+ nv_mask(dev, 0x6100a0, 0x00000001, 0x00000000);
+ }
+}
+
+int
+nvd0_display_init(struct drm_device *dev)
+{
+ struct nvd0_display *disp = nvd0_display(dev);
+ u32 *push;
+ int i;
+
+ if (nv_rd32(dev, 0x6100ac) & 0x00000100) {
+ nv_wr32(dev, 0x6100ac, 0x00000100);
+ nv_mask(dev, 0x6194e8, 0x00000001, 0x00000000);
+ if (!nv_wait(dev, 0x6194e8, 0x00000002, 0x00000000)) {
+ NV_ERROR(dev, "PDISP: 0x6194e8 0x%08x\n",
+ nv_rd32(dev, 0x6194e8));
+ return -EBUSY;
+ }
+ }
+
+ /* nfi what these are exactly, i do know that SOR_MODE_CTRL won't
+ * work at all unless you do the SOR part below.
+ */
+ for (i = 0; i < 3; i++) {
+ u32 dac = nv_rd32(dev, 0x61a000 + (i * 0x800));
+ nv_wr32(dev, 0x6101c0 + (i * 0x800), dac);
+ }
+
+ for (i = 0; i < 4; i++) {
+ u32 sor = nv_rd32(dev, 0x61c000 + (i * 0x800));
+ nv_wr32(dev, 0x6301c4 + (i * 0x800), sor);
+ }
+
+ for (i = 0; i < 2; i++) {
+ u32 crtc0 = nv_rd32(dev, 0x616104 + (i * 0x800));
+ u32 crtc1 = nv_rd32(dev, 0x616108 + (i * 0x800));
+ u32 crtc2 = nv_rd32(dev, 0x61610c + (i * 0x800));
+ nv_wr32(dev, 0x6101b4 + (i * 0x800), crtc0);
+ nv_wr32(dev, 0x6101b8 + (i * 0x800), crtc1);
+ nv_wr32(dev, 0x6101bc + (i * 0x800), crtc2);
+ }
+
+ /* point at our hash table / objects, enable interrupts */
+ nv_wr32(dev, 0x610010, (disp->mem->vinst >> 8) | 9);
+ nv_mask(dev, 0x6100b0, 0x00000307, 0x00000307);
+
+ /* init master */
+ nv_wr32(dev, 0x610494, (disp->evo[0].handle >> 8) | 3);
+ nv_wr32(dev, 0x610498, 0x00010000);
+ nv_wr32(dev, 0x61049c, 0x00000001);
+ nv_mask(dev, 0x610490, 0x00000010, 0x00000010);
+ nv_wr32(dev, 0x640000, 0x00000000);
+ nv_wr32(dev, 0x610490, 0x01000013);
+ if (!nv_wait(dev, 0x610490, 0x80000000, 0x00000000)) {
+ NV_ERROR(dev, "PDISP: master 0x%08x\n",
+ nv_rd32(dev, 0x610490));
+ return -EBUSY;
+ }
+ nv_mask(dev, 0x610090, 0x00000001, 0x00000001);
+ nv_mask(dev, 0x6100a0, 0x00000001, 0x00000001);
+
+ /* init cursors */
+ for (i = 13; i <= 14; i++) {
+ nv_wr32(dev, 0x610490 + (i * 0x10), 0x00000001);
+ if (!nv_wait(dev, 0x610490 + (i * 0x10), 0x00010000, 0x00010000)) {
+ NV_ERROR(dev, "PDISP: curs%d 0x%08x\n", i,
+ nv_rd32(dev, 0x610490 + (i * 0x10)));
+ return -EBUSY;
+ }
+
+ nv_mask(dev, 0x610090, 1 << i, 1 << i);
+ nv_mask(dev, 0x6100a0, 1 << i, 1 << i);
+ }
+
+ push = evo_wait(dev, 0, 32);
+ if (!push)
+ return -EBUSY;
+ evo_mthd(push, 0x0088, 1);
+ evo_data(push, NvEvoSync);
+ evo_mthd(push, 0x0084, 1);
+ evo_data(push, 0x00000000);
+ evo_mthd(push, 0x0084, 1);
+ evo_data(push, 0x80000000);
+ evo_mthd(push, 0x008c, 1);
+ evo_data(push, 0x00000000);
+ evo_kick(push, dev, 0);
+
+ return 0;
+}
+
+void
+nvd0_display_destroy(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvd0_display *disp = nvd0_display(dev);
+ struct pci_dev *pdev = dev->pdev;
+
+ nvd0_display_fini(dev);
+
+ pci_free_consistent(pdev, PAGE_SIZE, disp->evo[0].ptr, disp->evo[0].handle);
+ nouveau_gpuobj_ref(NULL, &disp->mem);
+ nouveau_irq_unregister(dev, 26);
+
+ dev_priv->engine.display.priv = NULL;
+ kfree(disp);
+}
+
+int
+nvd0_display_create(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
+ struct dcb_table *dcb = &dev_priv->vbios.dcb;
+ struct drm_connector *connector, *tmp;
+ struct pci_dev *pdev = dev->pdev;
+ struct nvd0_display *disp;
+ struct dcb_entry *dcbe;
+ int ret, i;
+
+ disp = kzalloc(sizeof(*disp), GFP_KERNEL);
+ if (!disp)
+ return -ENOMEM;
+ dev_priv->engine.display.priv = disp;
+
+ /* create crtc objects to represent the hw heads */
+ for (i = 0; i < 2; i++) {
+ ret = nvd0_crtc_create(dev, i);
+ if (ret)
+ goto out;
+ }
+
+ /* create encoder/connector objects based on VBIOS DCB table */
+ for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) {
+ connector = nouveau_connector_create(dev, dcbe->connector);
+ if (IS_ERR(connector))
+ continue;
+
+ if (dcbe->location != DCB_LOC_ON_CHIP) {
+ NV_WARN(dev, "skipping off-chip encoder %d/%d\n",
+ dcbe->type, ffs(dcbe->or) - 1);
+ continue;
+ }
+
+ switch (dcbe->type) {
+ case OUTPUT_TMDS:
+ case OUTPUT_LVDS:
+ nvd0_sor_create(connector, dcbe);
+ break;
+ case OUTPUT_ANALOG:
+ nvd0_dac_create(connector, dcbe);
+ break;
+ default:
+ NV_WARN(dev, "skipping unsupported encoder %d/%d\n",
+ dcbe->type, ffs(dcbe->or) - 1);
+ continue;
+ }
+ }
+
+ /* cull any connectors we created that don't have an encoder */
+ list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) {
+ if (connector->encoder_ids[0])
+ continue;
+
+ NV_WARN(dev, "%s has no encoders, removing\n",
+ drm_get_connector_name(connector));
+ connector->funcs->destroy(connector);
+ }
+
+ /* setup interrupt handling */
+ tasklet_init(&disp->tasklet, nvd0_display_bh, (unsigned long)dev);
+ nouveau_irq_register(dev, 26, nvd0_display_intr);
+
+ /* hash table and dma objects for the memory areas we care about */
+ ret = nouveau_gpuobj_new(dev, NULL, 0x4000, 0x10000,
+ NVOBJ_FLAG_ZERO_ALLOC, &disp->mem);
+ if (ret)
+ goto out;
+
+ nv_wo32(disp->mem, 0x1000, 0x00000049);
+ nv_wo32(disp->mem, 0x1004, (disp->mem->vinst + 0x2000) >> 8);
+ nv_wo32(disp->mem, 0x1008, (disp->mem->vinst + 0x2fff) >> 8);
+ nv_wo32(disp->mem, 0x100c, 0x00000000);
+ nv_wo32(disp->mem, 0x1010, 0x00000000);
+ nv_wo32(disp->mem, 0x1014, 0x00000000);
+ nv_wo32(disp->mem, 0x0000, NvEvoSync);
+ nv_wo32(disp->mem, 0x0004, (0x1000 << 9) | 0x00000001);
+
+ nv_wo32(disp->mem, 0x1020, 0x00000049);
+ nv_wo32(disp->mem, 0x1024, 0x00000000);
+ nv_wo32(disp->mem, 0x1028, (dev_priv->vram_size - 1) >> 8);
+ nv_wo32(disp->mem, 0x102c, 0x00000000);
+ nv_wo32(disp->mem, 0x1030, 0x00000000);
+ nv_wo32(disp->mem, 0x1034, 0x00000000);
+ nv_wo32(disp->mem, 0x0008, NvEvoVRAM);
+ nv_wo32(disp->mem, 0x000c, (0x1020 << 9) | 0x00000001);
+
+ nv_wo32(disp->mem, 0x1040, 0x00000009);
+ nv_wo32(disp->mem, 0x1044, 0x00000000);
+ nv_wo32(disp->mem, 0x1048, (dev_priv->vram_size - 1) >> 8);
+ nv_wo32(disp->mem, 0x104c, 0x00000000);
+ nv_wo32(disp->mem, 0x1050, 0x00000000);
+ nv_wo32(disp->mem, 0x1054, 0x00000000);
+ nv_wo32(disp->mem, 0x0010, NvEvoVRAM_LP);
+ nv_wo32(disp->mem, 0x0014, (0x1040 << 9) | 0x00000001);
+
+ nv_wo32(disp->mem, 0x1060, 0x0fe00009);
+ nv_wo32(disp->mem, 0x1064, 0x00000000);
+ nv_wo32(disp->mem, 0x1068, (dev_priv->vram_size - 1) >> 8);
+ nv_wo32(disp->mem, 0x106c, 0x00000000);
+ nv_wo32(disp->mem, 0x1070, 0x00000000);
+ nv_wo32(disp->mem, 0x1074, 0x00000000);
+ nv_wo32(disp->mem, 0x0018, NvEvoFB32);
+ nv_wo32(disp->mem, 0x001c, (0x1060 << 9) | 0x00000001);
+
+ pinstmem->flush(dev);
+
+ /* push buffers for evo channels */
+ disp->evo[0].ptr =
+ pci_alloc_consistent(pdev, PAGE_SIZE, &disp->evo[0].handle);
+ if (!disp->evo[0].ptr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = nvd0_display_init(dev);
+ if (ret)
+ goto out;
+
+out:
+ if (ret)
+ nvd0_display_destroy(dev);
+ return ret;
+}
diff --git a/drivers/gpu/drm/r128/r128_cce.c b/drivers/gpu/drm/r128/r128_cce.c
index 570e190710bd..bcac90b543ad 100644
--- a/drivers/gpu/drm/r128/r128_cce.c
+++ b/drivers/gpu/drm/r128/r128_cce.c
@@ -32,6 +32,7 @@
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "drmP.h"
#include "drm.h"
diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c
index b9e8efd2b754..4c8796ba6dd8 100644
--- a/drivers/gpu/drm/r128/r128_drv.c
+++ b/drivers/gpu/drm/r128/r128_drv.c
@@ -29,6 +29,8 @@
* Gareth Hughes <gareth@valinux.com>
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "drm.h"
#include "r128_drm.h"
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index 3896ef811102..cf8b4bc3e73d 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -5,6 +5,7 @@
ccflags-y := -Iinclude/drm
hostprogs-y := mkregtable
+clean-files := rn50_reg_safe.h r100_reg_safe.h r200_reg_safe.h rv515_reg_safe.h r300_reg_safe.h r420_reg_safe.h rs600_reg_safe.h r600_reg_safe.h evergreen_reg_safe.h cayman_reg_safe.h
quiet_cmd_mkregtable = MKREGTABLE $@
cmd_mkregtable = $(obj)/mkregtable $< > $@
@@ -69,7 +70,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \
r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \
evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o \
- radeon_trace_points.o ni.o cayman_blit_shaders.o
+ radeon_trace_points.o ni.o cayman_blit_shaders.o atombios_encoders.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c
index ebdb0fdb8348..14cc88aaf3a7 100644
--- a/drivers/gpu/drm/radeon/atom.c
+++ b/drivers/gpu/drm/radeon/atom.c
@@ -277,7 +277,12 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
case ATOM_ARG_FB:
idx = U8(*ptr);
(*ptr)++;
- val = gctx->scratch[((gctx->fb_base + idx) / 4)];
+ if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
+ DRM_ERROR("ATOM: fb read beyond scratch region: %d vs. %d\n",
+ gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
+ val = 0;
+ } else
+ val = gctx->scratch[(gctx->fb_base / 4) + idx];
if (print)
DEBUG("FB[0x%02X]", idx);
break;
@@ -531,7 +536,11 @@ static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
case ATOM_ARG_FB:
idx = U8(*ptr);
(*ptr)++;
- gctx->scratch[((gctx->fb_base + idx) / 4)] = val;
+ if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
+ DRM_ERROR("ATOM: fb write beyond scratch region: %d vs. %d\n",
+ gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
+ } else
+ gctx->scratch[(gctx->fb_base / 4) + idx] = val;
DEBUG("FB[0x%02X]", idx);
break;
case ATOM_ARG_PLL:
@@ -1245,6 +1254,9 @@ struct atom_context *atom_parse(struct card_info *card, void *bios)
char name[512];
int i;
+ if (!ctx)
+ return NULL;
+
ctx->card = card;
ctx->bios = bios;
@@ -1367,11 +1379,13 @@ int atom_allocate_fb_scratch(struct atom_context *ctx)
usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024;
}
+ ctx->scratch_size_bytes = 0;
if (usage_bytes == 0)
usage_bytes = 20 * 1024;
/* allocate some scratch memory */
ctx->scratch = kzalloc(usage_bytes, GFP_KERNEL);
if (!ctx->scratch)
return -ENOMEM;
+ ctx->scratch_size_bytes = usage_bytes;
return 0;
}
diff --git a/drivers/gpu/drm/radeon/atom.h b/drivers/gpu/drm/radeon/atom.h
index a589a55b223e..93cfe2086ba0 100644
--- a/drivers/gpu/drm/radeon/atom.h
+++ b/drivers/gpu/drm/radeon/atom.h
@@ -137,6 +137,7 @@ struct atom_context {
int cs_equal, cs_above;
int io_mode;
uint32_t *scratch;
+ int scratch_size_bytes;
};
extern int atom_debug;
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 9541995e4b21..87631fede1f8 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -466,7 +466,7 @@ static void atombios_crtc_program_ss(struct drm_crtc *crtc,
return;
}
args.v2.ucEnable = enable;
- if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK))
+ if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK) || ASIC_IS_DCE41(rdev))
args.v2.ucEnable = ATOM_DISABLE;
} else if (ASIC_IS_DCE3(rdev)) {
args.v1.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
@@ -558,7 +558,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
bpc = connector->display_info.bpc;
encoder_mode = atombios_get_encoder_mode(encoder);
if ((radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) ||
- radeon_encoder_is_dp_bridge(encoder)) {
+ (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) {
if (connector) {
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *dig_connector =
@@ -638,44 +638,29 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
if (ss_enabled && ss->percentage)
args.v3.sInput.ucDispPllConfig |=
DISPPLL_CONFIG_SS_ENABLE;
- if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT) ||
- radeon_encoder_is_dp_bridge(encoder)) {
+ if (ENCODER_MODE_IS_DP(encoder_mode)) {
+ args.v3.sInput.ucDispPllConfig |=
+ DISPPLL_CONFIG_COHERENT_MODE;
+ /* 16200 or 27000 */
+ args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10);
+ } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
- if (encoder_mode == ATOM_ENCODER_MODE_DP) {
+ if (encoder_mode == ATOM_ENCODER_MODE_HDMI)
+ /* deep color support */
+ args.v3.sInput.usPixelClock =
+ cpu_to_le16((mode->clock * bpc / 8) / 10);
+ if (dig->coherent_mode)
args.v3.sInput.ucDispPllConfig |=
DISPPLL_CONFIG_COHERENT_MODE;
- /* 16200 or 27000 */
- args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10);
- } else {
- if (encoder_mode == ATOM_ENCODER_MODE_HDMI) {
- /* deep color support */
- args.v3.sInput.usPixelClock =
- cpu_to_le16((mode->clock * bpc / 8) / 10);
- }
- if (dig->coherent_mode)
- args.v3.sInput.ucDispPllConfig |=
- DISPPLL_CONFIG_COHERENT_MODE;
- if (mode->clock > 165000)
- args.v3.sInput.ucDispPllConfig |=
- DISPPLL_CONFIG_DUAL_LINK;
- }
- } else if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- if (encoder_mode == ATOM_ENCODER_MODE_DP) {
+ if (mode->clock > 165000)
args.v3.sInput.ucDispPllConfig |=
- DISPPLL_CONFIG_COHERENT_MODE;
- /* 16200 or 27000 */
- args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10);
- } else if (encoder_mode != ATOM_ENCODER_MODE_LVDS) {
- if (mode->clock > 165000)
- args.v3.sInput.ucDispPllConfig |=
- DISPPLL_CONFIG_DUAL_LINK;
- }
+ DISPPLL_CONFIG_DUAL_LINK;
}
- if (radeon_encoder_is_dp_bridge(encoder)) {
- struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder);
- struct radeon_encoder *ext_radeon_encoder = to_radeon_encoder(ext_encoder);
- args.v3.sInput.ucExtTransmitterID = ext_radeon_encoder->encoder_id;
- } else
+ if (radeon_encoder_get_dp_bridge_encoder_id(encoder) !=
+ ENCODER_OBJECT_ID_NONE)
+ args.v3.sInput.ucExtTransmitterID =
+ radeon_encoder_get_dp_bridge_encoder_id(encoder);
+ else
args.v3.sInput.ucExtTransmitterID = 0;
atom_execute_table(rdev->mode_info.atom_context,
@@ -764,7 +749,7 @@ static void atombios_crtc_set_dcpll(struct drm_crtc *crtc,
}
static void atombios_crtc_program_pll(struct drm_crtc *crtc,
- int crtc_id,
+ u32 crtc_id,
int pll_id,
u32 encoder_mode,
u32 encoder_id,
@@ -851,8 +836,7 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
args.v5.ucPpll = pll_id;
break;
case 6:
- args.v6.ulCrtcPclkFreq.ucCRTC = crtc_id;
- args.v6.ulCrtcPclkFreq.ulPixelClock = cpu_to_le32(clock / 10);
+ args.v6.ulDispEngClkFreq = cpu_to_le32(crtc_id << 24 | clock / 10);
args.v6.ucRefDiv = ref_div;
args.v6.usFbDiv = cpu_to_le16(fb_div);
args.v6.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000);
@@ -946,6 +930,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
bpc = connector->display_info.bpc;
switch (encoder_mode) {
+ case ATOM_ENCODER_MODE_DP_MST:
case ATOM_ENCODER_MODE_DP:
/* DP/eDP */
dp_clock = dig_connector->dp_clock / 10;
@@ -1451,7 +1436,7 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
* PPLL/DCPLL programming and only program the DP DTO for the
* crtc virtual pixel clock.
*/
- if (atombios_get_encoder_mode(test_encoder) == ATOM_ENCODER_MODE_DP) {
+ if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
if (ASIC_IS_DCE5(rdev) || rdev->clock.dp_extclk)
return ATOM_PPLL_INVALID;
}
@@ -1537,12 +1522,6 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- struct drm_device *dev = crtc->dev;
- struct radeon_device *rdev = dev->dev_private;
-
- /* adjust pm to upcoming mode change */
- radeon_pm_compute_clocks(rdev);
-
if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode))
return false;
return true;
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 8c0f9e36ff8e..6fb335a4fdda 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -115,6 +115,7 @@ static int radeon_dp_aux_native_write(struct radeon_connector *radeon_connector,
u8 msg[20];
int msg_bytes = send_bytes + 4;
u8 ack;
+ unsigned retry;
if (send_bytes > 16)
return -1;
@@ -125,20 +126,22 @@ static int radeon_dp_aux_native_write(struct radeon_connector *radeon_connector,
msg[3] = (msg_bytes << 4) | (send_bytes - 1);
memcpy(&msg[4], send, send_bytes);
- while (1) {
+ for (retry = 0; retry < 4; retry++) {
ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus,
msg, msg_bytes, NULL, 0, delay, &ack);
- if (ret < 0)
+ if (ret == -EBUSY)
+ continue;
+ else if (ret < 0)
return ret;
if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
- break;
+ return send_bytes;
else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
udelay(400);
else
return -EIO;
}
- return send_bytes;
+ return -EIO;
}
static int radeon_dp_aux_native_read(struct radeon_connector *radeon_connector,
@@ -149,26 +152,31 @@ static int radeon_dp_aux_native_read(struct radeon_connector *radeon_connector,
int msg_bytes = 4;
u8 ack;
int ret;
+ unsigned retry;
msg[0] = address;
msg[1] = address >> 8;
msg[2] = AUX_NATIVE_READ << 4;
msg[3] = (msg_bytes << 4) | (recv_bytes - 1);
- while (1) {
+ for (retry = 0; retry < 4; retry++) {
ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus,
msg, msg_bytes, recv, recv_bytes, delay, &ack);
- if (ret == 0)
- return -EPROTO;
- if (ret < 0)
+ if (ret == -EBUSY)
+ continue;
+ else if (ret < 0)
return ret;
if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
return ret;
else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
udelay(400);
+ else if (ret == 0)
+ return -EPROTO;
else
return -EIO;
}
+
+ return -EIO;
}
static void radeon_write_dpcd_reg(struct radeon_connector *radeon_connector,
@@ -232,7 +240,9 @@ int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
for (retry = 0; retry < 4; retry++) {
ret = radeon_process_aux_ch(auxch,
msg, msg_bytes, reply, reply_bytes, 0, &ack);
- if (ret < 0) {
+ if (ret == -EBUSY)
+ continue;
+ else if (ret < 0) {
DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
return ret;
}
@@ -273,7 +283,7 @@ int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
}
}
- DRM_ERROR("aux i2c too many retries, giving up\n");
+ DRM_DEBUG_KMS("aux i2c too many retries, giving up\n");
return -EREMOTEIO;
}
@@ -472,7 +482,8 @@ static int radeon_dp_get_dp_link_clock(struct drm_connector *connector,
int bpp = convert_bpc_to_bpp(connector->display_info.bpc);
int lane_num, max_pix_clock;
- if (radeon_connector_encoder_is_dp_bridge(connector))
+ if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
+ ENCODER_OBJECT_ID_NUTMEG)
return 270000;
lane_num = radeon_dp_get_dp_lane_number(connector, dpcd, pix_clock);
@@ -543,17 +554,32 @@ static void radeon_dp_set_panel_mode(struct drm_encoder *encoder,
{
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
if (!ASIC_IS_DCE4(rdev))
return;
- if (radeon_connector_encoder_is_dp_bridge(connector))
+ if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
+ ENCODER_OBJECT_ID_NUTMEG)
panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE;
+ else if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
+ ENCODER_OBJECT_ID_TRAVIS)
+ panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
+ else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ u8 tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP);
+ if (tmp & 1)
+ panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
+ }
atombios_dig_encoder_setup(encoder,
ATOM_ENCODER_CMD_SETUP_PANEL_MODE,
panel_mode);
+
+ if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) &&
+ (panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) {
+ radeon_write_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_SET, 1);
+ }
}
void radeon_dp_set_link_config(struct drm_connector *connector,
@@ -613,6 +639,18 @@ static bool radeon_dp_get_link_status(struct radeon_connector *radeon_connector,
return true;
}
+bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
+{
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
+
+ if (!radeon_dp_get_link_status(radeon_connector, link_status))
+ return false;
+ if (dp_channel_eq_ok(link_status, dig->dp_lane_count))
+ return false;
+ return true;
+}
+
struct radeon_dp_link_train_info {
struct radeon_device *rdev;
struct drm_encoder *encoder;
@@ -627,6 +665,7 @@ struct radeon_dp_link_train_info {
u8 train_set[4];
u8 link_status[DP_LINK_STATUS_SIZE];
u8 tries;
+ bool use_dpencoder;
};
static void radeon_dp_update_vs_emph(struct radeon_dp_link_train_info *dp_info)
@@ -646,7 +685,7 @@ static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp)
int rtp = 0;
/* set training pattern on the source */
- if (ASIC_IS_DCE4(dp_info->rdev)) {
+ if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) {
switch (tp) {
case DP_TRAINING_PATTERN_1:
rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1;
@@ -706,7 +745,7 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LINK_BW_SET, tmp);
/* start training on the source */
- if (ASIC_IS_DCE4(dp_info->rdev))
+ if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder)
atombios_dig_encoder_setup(dp_info->encoder,
ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0);
else
@@ -731,7 +770,7 @@ static int radeon_dp_link_train_finish(struct radeon_dp_link_train_info *dp_info
DP_TRAINING_PATTERN_DISABLE);
/* disable the training pattern on the source */
- if (ASIC_IS_DCE4(dp_info->rdev))
+ if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder)
atombios_dig_encoder_setup(dp_info->encoder,
ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0);
else
@@ -869,7 +908,8 @@ void radeon_dp_link_train(struct drm_encoder *encoder,
struct radeon_connector *radeon_connector;
struct radeon_connector_atom_dig *dig_connector;
struct radeon_dp_link_train_info dp_info;
- u8 tmp;
+ int index;
+ u8 tmp, frev, crev;
if (!radeon_encoder->enc_priv)
return;
@@ -884,6 +924,18 @@ void radeon_dp_link_train(struct drm_encoder *encoder,
(dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_eDP))
return;
+ /* DPEncoderService newer than 1.1 can't program properly the
+ * training pattern. When facing such version use the
+ * DIGXEncoderControl (X== 1 | 2)
+ */
+ dp_info.use_dpencoder = true;
+ index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
+ if (atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) {
+ if (crev > 1) {
+ dp_info.use_dpencoder = false;
+ }
+ }
+
dp_info.enc_id = 0;
if (dig->dig_encoder)
dp_info.enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER;
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
new file mode 100644
index 000000000000..39c04c1b8472
--- /dev/null
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -0,0 +1,2369 @@
+/*
+ * Copyright 2007-11 Advanced Micro Devices, Inc.
+ * Copyright 2008 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Dave Airlie
+ * Alex Deucher
+ */
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "radeon_drm.h"
+#include "radeon.h"
+#include "atom.h"
+
+extern int atom_debug;
+
+/* evil but including atombios.h is much worse */
+bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
+ struct drm_display_mode *mode);
+
+
+static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder)
+{
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+ case ENCODER_OBJECT_ID_INTERNAL_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_DDI:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct drm_connector *
+radeon_get_connector_for_encoder_init(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_connector *connector;
+ struct radeon_connector *radeon_connector;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ radeon_connector = to_radeon_connector(connector);
+ if (radeon_encoder->devices & radeon_connector->devices)
+ return connector;
+ }
+ return NULL;
+}
+
+static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+
+ /* set the active encoder to connector routing */
+ radeon_encoder_set_active_device(encoder);
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+ /* hw bug */
+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE)
+ && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2)))
+ adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2;
+
+ /* get the native mode for LVDS */
+ if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT))
+ radeon_panel_mode_fixup(encoder, adjusted_mode);
+
+ /* get the native mode for TV */
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
+ struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv;
+ if (tv_dac) {
+ if (tv_dac->tv_std == TV_STD_NTSC ||
+ tv_dac->tv_std == TV_STD_NTSC_J ||
+ tv_dac->tv_std == TV_STD_PAL_M)
+ radeon_atom_get_tv_timings(rdev, 0, adjusted_mode);
+ else
+ radeon_atom_get_tv_timings(rdev, 1, adjusted_mode);
+ }
+ }
+
+ if (ASIC_IS_DCE3(rdev) &&
+ ((radeon_encoder->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) ||
+ (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE))) {
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+ radeon_dp_set_link_config(connector, mode);
+ }
+
+ return true;
+}
+
+static void
+atombios_dac_setup(struct drm_encoder *encoder, int action)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ DAC_ENCODER_CONTROL_PS_ALLOCATION args;
+ int index = 0;
+ struct radeon_encoder_atom_dac *dac_info = radeon_encoder->enc_priv;
+
+ memset(&args, 0, sizeof(args));
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_DAC1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
+ index = GetIndexIntoMasterTable(COMMAND, DAC1EncoderControl);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DAC2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
+ index = GetIndexIntoMasterTable(COMMAND, DAC2EncoderControl);
+ break;
+ }
+
+ args.ucAction = action;
+
+ if (radeon_encoder->active_device & (ATOM_DEVICE_CRT_SUPPORT))
+ args.ucDacStandard = ATOM_DAC1_PS2;
+ else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
+ args.ucDacStandard = ATOM_DAC1_CV;
+ else {
+ switch (dac_info->tv_std) {
+ case TV_STD_PAL:
+ case TV_STD_PAL_M:
+ case TV_STD_SCART_PAL:
+ case TV_STD_SECAM:
+ case TV_STD_PAL_CN:
+ args.ucDacStandard = ATOM_DAC1_PAL;
+ break;
+ case TV_STD_NTSC:
+ case TV_STD_NTSC_J:
+ case TV_STD_PAL_60:
+ default:
+ args.ucDacStandard = ATOM_DAC1_NTSC;
+ break;
+ }
+ }
+ args.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+}
+
+static void
+atombios_tv_setup(struct drm_encoder *encoder, int action)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ TV_ENCODER_CONTROL_PS_ALLOCATION args;
+ int index = 0;
+ struct radeon_encoder_atom_dac *dac_info = radeon_encoder->enc_priv;
+
+ memset(&args, 0, sizeof(args));
+
+ index = GetIndexIntoMasterTable(COMMAND, TVEncoderControl);
+
+ args.sTVEncoder.ucAction = action;
+
+ if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
+ args.sTVEncoder.ucTvStandard = ATOM_TV_CV;
+ else {
+ switch (dac_info->tv_std) {
+ case TV_STD_NTSC:
+ args.sTVEncoder.ucTvStandard = ATOM_TV_NTSC;
+ break;
+ case TV_STD_PAL:
+ args.sTVEncoder.ucTvStandard = ATOM_TV_PAL;
+ break;
+ case TV_STD_PAL_M:
+ args.sTVEncoder.ucTvStandard = ATOM_TV_PALM;
+ break;
+ case TV_STD_PAL_60:
+ args.sTVEncoder.ucTvStandard = ATOM_TV_PAL60;
+ break;
+ case TV_STD_NTSC_J:
+ args.sTVEncoder.ucTvStandard = ATOM_TV_NTSCJ;
+ break;
+ case TV_STD_SCART_PAL:
+ args.sTVEncoder.ucTvStandard = ATOM_TV_PAL; /* ??? */
+ break;
+ case TV_STD_SECAM:
+ args.sTVEncoder.ucTvStandard = ATOM_TV_SECAM;
+ break;
+ case TV_STD_PAL_CN:
+ args.sTVEncoder.ucTvStandard = ATOM_TV_PALCN;
+ break;
+ default:
+ args.sTVEncoder.ucTvStandard = ATOM_TV_NTSC;
+ break;
+ }
+ }
+
+ args.sTVEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+}
+
+union dvo_encoder_control {
+ ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION ext_tmds;
+ DVO_ENCODER_CONTROL_PS_ALLOCATION dvo;
+ DVO_ENCODER_CONTROL_PS_ALLOCATION_V3 dvo_v3;
+};
+
+void
+atombios_dvo_setup(struct drm_encoder *encoder, int action)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ union dvo_encoder_control args;
+ int index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl);
+ uint8_t frev, crev;
+
+ memset(&args, 0, sizeof(args));
+
+ if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
+ return;
+
+ switch (frev) {
+ case 1:
+ switch (crev) {
+ case 1:
+ /* R4xx, R5xx */
+ args.ext_tmds.sXTmdsEncoder.ucEnable = action;
+
+ if (radeon_encoder->pixel_clock > 165000)
+ args.ext_tmds.sXTmdsEncoder.ucMisc |= PANEL_ENCODER_MISC_DUAL;
+
+ args.ext_tmds.sXTmdsEncoder.ucMisc |= ATOM_PANEL_MISC_888RGB;
+ break;
+ case 2:
+ /* RS600/690/740 */
+ args.dvo.sDVOEncoder.ucAction = action;
+ args.dvo.sDVOEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ /* DFP1, CRT1, TV1 depending on the type of port */
+ args.dvo.sDVOEncoder.ucDeviceType = ATOM_DEVICE_DFP1_INDEX;
+
+ if (radeon_encoder->pixel_clock > 165000)
+ args.dvo.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute |= PANEL_ENCODER_MISC_DUAL;
+ break;
+ case 3:
+ /* R6xx */
+ args.dvo_v3.ucAction = action;
+ args.dvo_v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ args.dvo_v3.ucDVOConfig = 0; /* XXX */
+ break;
+ default:
+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+ break;
+ }
+ break;
+ default:
+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+ break;
+ }
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
+union lvds_encoder_control {
+ LVDS_ENCODER_CONTROL_PS_ALLOCATION v1;
+ LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 v2;
+};
+
+void
+atombios_digital_setup(struct drm_encoder *encoder, int action)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+ union lvds_encoder_control args;
+ int index = 0;
+ int hdmi_detected = 0;
+ uint8_t frev, crev;
+
+ if (!dig)
+ return;
+
+ if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI)
+ hdmi_detected = 1;
+
+ memset(&args, 0, sizeof(args));
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+ index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+ index = GetIndexIntoMasterTable(COMMAND, TMDS1EncoderControl);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
+ index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl);
+ else
+ index = GetIndexIntoMasterTable(COMMAND, TMDS2EncoderControl);
+ break;
+ }
+
+ if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
+ return;
+
+ switch (frev) {
+ case 1:
+ case 2:
+ switch (crev) {
+ case 1:
+ args.v1.ucMisc = 0;
+ args.v1.ucAction = action;
+ if (hdmi_detected)
+ args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
+ args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+ if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL)
+ args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL;
+ if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
+ args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB;
+ } else {
+ if (dig->linkb)
+ args.v1.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB;
+ if (radeon_encoder->pixel_clock > 165000)
+ args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL;
+ /*if (pScrn->rgbBits == 8) */
+ args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB;
+ }
+ break;
+ case 2:
+ case 3:
+ args.v2.ucMisc = 0;
+ args.v2.ucAction = action;
+ if (crev == 3) {
+ if (dig->coherent_mode)
+ args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT;
+ }
+ if (hdmi_detected)
+ args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
+ args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ args.v2.ucTruncate = 0;
+ args.v2.ucSpatial = 0;
+ args.v2.ucTemporal = 0;
+ args.v2.ucFRC = 0;
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+ if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL)
+ args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL;
+ if (dig->lcd_misc & ATOM_PANEL_MISC_SPATIAL) {
+ args.v2.ucSpatial = PANEL_ENCODER_SPATIAL_DITHER_EN;
+ if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
+ args.v2.ucSpatial |= PANEL_ENCODER_SPATIAL_DITHER_DEPTH;
+ }
+ if (dig->lcd_misc & ATOM_PANEL_MISC_TEMPORAL) {
+ args.v2.ucTemporal = PANEL_ENCODER_TEMPORAL_DITHER_EN;
+ if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
+ args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_DITHER_DEPTH;
+ if (((dig->lcd_misc >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT) & 0x3) == 2)
+ args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_LEVEL_4;
+ }
+ } else {
+ if (dig->linkb)
+ args.v2.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB;
+ if (radeon_encoder->pixel_clock > 165000)
+ args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL;
+ }
+ break;
+ default:
+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+ break;
+ }
+ break;
+ default:
+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+ break;
+ }
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
+int
+atombios_get_encoder_mode(struct drm_encoder *encoder)
+{
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct drm_connector *connector;
+ struct radeon_connector *radeon_connector;
+ struct radeon_connector_atom_dig *dig_connector;
+
+ /* dp bridges are always DP */
+ if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)
+ return ATOM_ENCODER_MODE_DP;
+
+ /* DVO is always DVO */
+ if (radeon_encoder->encoder_id == ATOM_ENCODER_MODE_DVO)
+ return ATOM_ENCODER_MODE_DVO;
+
+ connector = radeon_get_connector_for_encoder(encoder);
+ /* if we don't have an active device yet, just use one of
+ * the connectors tied to the encoder.
+ */
+ if (!connector)
+ connector = radeon_get_connector_for_encoder_init(encoder);
+ radeon_connector = to_radeon_connector(connector);
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DVII:
+ case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */
+ if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) {
+ /* fix me */
+ if (ASIC_IS_DCE4(rdev))
+ return ATOM_ENCODER_MODE_DVI;
+ else
+ return ATOM_ENCODER_MODE_HDMI;
+ } else if (radeon_connector->use_digital)
+ return ATOM_ENCODER_MODE_DVI;
+ else
+ return ATOM_ENCODER_MODE_CRT;
+ break;
+ case DRM_MODE_CONNECTOR_DVID:
+ case DRM_MODE_CONNECTOR_HDMIA:
+ default:
+ if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) {
+ /* fix me */
+ if (ASIC_IS_DCE4(rdev))
+ return ATOM_ENCODER_MODE_DVI;
+ else
+ return ATOM_ENCODER_MODE_HDMI;
+ } else
+ return ATOM_ENCODER_MODE_DVI;
+ break;
+ case DRM_MODE_CONNECTOR_LVDS:
+ return ATOM_ENCODER_MODE_LVDS;
+ break;
+ case DRM_MODE_CONNECTOR_DisplayPort:
+ dig_connector = radeon_connector->con_priv;
+ if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
+ (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
+ return ATOM_ENCODER_MODE_DP;
+ else if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) {
+ /* fix me */
+ if (ASIC_IS_DCE4(rdev))
+ return ATOM_ENCODER_MODE_DVI;
+ else
+ return ATOM_ENCODER_MODE_HDMI;
+ } else
+ return ATOM_ENCODER_MODE_DVI;
+ break;
+ case DRM_MODE_CONNECTOR_eDP:
+ return ATOM_ENCODER_MODE_DP;
+ case DRM_MODE_CONNECTOR_DVIA:
+ case DRM_MODE_CONNECTOR_VGA:
+ return ATOM_ENCODER_MODE_CRT;
+ break;
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_9PinDIN:
+ /* fix me */
+ return ATOM_ENCODER_MODE_TV;
+ /*return ATOM_ENCODER_MODE_CV;*/
+ break;
+ }
+}
+
+/*
+ * DIG Encoder/Transmitter Setup
+ *
+ * DCE 3.0/3.1
+ * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA.
+ * Supports up to 3 digital outputs
+ * - 2 DIG encoder blocks.
+ * DIG1 can drive UNIPHY link A or link B
+ * DIG2 can drive UNIPHY link B or LVTMA
+ *
+ * DCE 3.2
+ * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B).
+ * Supports up to 5 digital outputs
+ * - 2 DIG encoder blocks.
+ * DIG1/2 can drive UNIPHY0/1/2 link A or link B
+ *
+ * DCE 4.0/5.0
+ * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B).
+ * Supports up to 6 digital outputs
+ * - 6 DIG encoder blocks.
+ * - DIG to PHY mapping is hardcoded
+ * DIG1 drives UNIPHY0 link A, A+B
+ * DIG2 drives UNIPHY0 link B
+ * DIG3 drives UNIPHY1 link A, A+B
+ * DIG4 drives UNIPHY1 link B
+ * DIG5 drives UNIPHY2 link A, A+B
+ * DIG6 drives UNIPHY2 link B
+ *
+ * DCE 4.1
+ * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B).
+ * Supports up to 6 digital outputs
+ * - 2 DIG encoder blocks.
+ * DIG1/2 can drive UNIPHY0/1/2 link A or link B
+ *
+ * Routing
+ * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links)
+ * Examples:
+ * crtc0 -> dig2 -> LVTMA links A+B -> TMDS/HDMI
+ * crtc1 -> dig1 -> UNIPHY0 link B -> DP
+ * crtc0 -> dig1 -> UNIPHY2 link A -> LVDS
+ * crtc1 -> dig2 -> UNIPHY1 link B+A -> TMDS/HDMI
+ */
+
+union dig_encoder_control {
+ DIG_ENCODER_CONTROL_PS_ALLOCATION v1;
+ DIG_ENCODER_CONTROL_PARAMETERS_V2 v2;
+ DIG_ENCODER_CONTROL_PARAMETERS_V3 v3;
+ DIG_ENCODER_CONTROL_PARAMETERS_V4 v4;
+};
+
+void
+atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+ union dig_encoder_control args;
+ int index = 0;
+ uint8_t frev, crev;
+ int dp_clock = 0;
+ int dp_lane_count = 0;
+ int hpd_id = RADEON_HPD_NONE;
+ int bpc = 8;
+
+ if (connector) {
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct radeon_connector_atom_dig *dig_connector =
+ radeon_connector->con_priv;
+
+ dp_clock = dig_connector->dp_clock;
+ dp_lane_count = dig_connector->dp_lane_count;
+ hpd_id = radeon_connector->hpd.hpd;
+ bpc = connector->display_info.bpc;
+ }
+
+ /* no dig encoder assigned */
+ if (dig->dig_encoder == -1)
+ return;
+
+ memset(&args, 0, sizeof(args));
+
+ if (ASIC_IS_DCE4(rdev))
+ index = GetIndexIntoMasterTable(COMMAND, DIGxEncoderControl);
+ else {
+ if (dig->dig_encoder)
+ index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
+ else
+ index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
+ }
+
+ if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
+ return;
+
+ switch (frev) {
+ case 1:
+ switch (crev) {
+ case 1:
+ args.v1.ucAction = action;
+ args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE)
+ args.v3.ucPanelMode = panel_mode;
+ else
+ args.v1.ucEncoderMode = atombios_get_encoder_mode(encoder);
+
+ if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode))
+ args.v1.ucLaneNum = dp_lane_count;
+ else if (radeon_encoder->pixel_clock > 165000)
+ args.v1.ucLaneNum = 8;
+ else
+ args.v1.ucLaneNum = 4;
+
+ if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode) && (dp_clock == 270000))
+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER2;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER3;
+ break;
+ }
+ if (dig->linkb)
+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
+ else
+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
+ break;
+ case 2:
+ case 3:
+ args.v3.ucAction = action;
+ args.v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE)
+ args.v3.ucPanelMode = panel_mode;
+ else
+ args.v3.ucEncoderMode = atombios_get_encoder_mode(encoder);
+
+ if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode))
+ args.v3.ucLaneNum = dp_lane_count;
+ else if (radeon_encoder->pixel_clock > 165000)
+ args.v3.ucLaneNum = 8;
+ else
+ args.v3.ucLaneNum = 4;
+
+ if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode) && (dp_clock == 270000))
+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ;
+ args.v3.acConfig.ucDigSel = dig->dig_encoder;
+ switch (bpc) {
+ case 0:
+ args.v3.ucBitPerColor = PANEL_BPC_UNDEFINE;
+ break;
+ case 6:
+ args.v3.ucBitPerColor = PANEL_6BIT_PER_COLOR;
+ break;
+ case 8:
+ default:
+ args.v3.ucBitPerColor = PANEL_8BIT_PER_COLOR;
+ break;
+ case 10:
+ args.v3.ucBitPerColor = PANEL_10BIT_PER_COLOR;
+ break;
+ case 12:
+ args.v3.ucBitPerColor = PANEL_12BIT_PER_COLOR;
+ break;
+ case 16:
+ args.v3.ucBitPerColor = PANEL_16BIT_PER_COLOR;
+ break;
+ }
+ break;
+ case 4:
+ args.v4.ucAction = action;
+ args.v4.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE)
+ args.v4.ucPanelMode = panel_mode;
+ else
+ args.v4.ucEncoderMode = atombios_get_encoder_mode(encoder);
+
+ if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode))
+ args.v4.ucLaneNum = dp_lane_count;
+ else if (radeon_encoder->pixel_clock > 165000)
+ args.v4.ucLaneNum = 8;
+ else
+ args.v4.ucLaneNum = 4;
+
+ if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode)) {
+ if (dp_clock == 270000)
+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ;
+ else if (dp_clock == 540000)
+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ;
+ }
+ args.v4.acConfig.ucDigSel = dig->dig_encoder;
+ switch (bpc) {
+ case 0:
+ args.v4.ucBitPerColor = PANEL_BPC_UNDEFINE;
+ break;
+ case 6:
+ args.v4.ucBitPerColor = PANEL_6BIT_PER_COLOR;
+ break;
+ case 8:
+ default:
+ args.v4.ucBitPerColor = PANEL_8BIT_PER_COLOR;
+ break;
+ case 10:
+ args.v4.ucBitPerColor = PANEL_10BIT_PER_COLOR;
+ break;
+ case 12:
+ args.v4.ucBitPerColor = PANEL_12BIT_PER_COLOR;
+ break;
+ case 16:
+ args.v4.ucBitPerColor = PANEL_16BIT_PER_COLOR;
+ break;
+ }
+ if (hpd_id == RADEON_HPD_NONE)
+ args.v4.ucHPD_ID = 0;
+ else
+ args.v4.ucHPD_ID = hpd_id + 1;
+ break;
+ default:
+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+ break;
+ }
+ break;
+ default:
+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+ break;
+ }
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+}
+
+union dig_transmitter_control {
+ DIG_TRANSMITTER_CONTROL_PS_ALLOCATION v1;
+ DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2;
+ DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 v3;
+ DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 v4;
+};
+
+void
+atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+ struct drm_connector *connector;
+ union dig_transmitter_control args;
+ int index = 0;
+ uint8_t frev, crev;
+ bool is_dp = false;
+ int pll_id = 0;
+ int dp_clock = 0;
+ int dp_lane_count = 0;
+ int connector_object_id = 0;
+ int igp_lane_info = 0;
+ int dig_encoder = dig->dig_encoder;
+
+ if (action == ATOM_TRANSMITTER_ACTION_INIT) {
+ connector = radeon_get_connector_for_encoder_init(encoder);
+ /* just needed to avoid bailing in the encoder check. the encoder
+ * isn't used for init
+ */
+ dig_encoder = 0;
+ } else
+ connector = radeon_get_connector_for_encoder(encoder);
+
+ if (connector) {
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct radeon_connector_atom_dig *dig_connector =
+ radeon_connector->con_priv;
+
+ dp_clock = dig_connector->dp_clock;
+ dp_lane_count = dig_connector->dp_lane_count;
+ connector_object_id =
+ (radeon_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
+ igp_lane_info = dig_connector->igp_lane_info;
+ }
+
+ if (encoder->crtc) {
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+ pll_id = radeon_crtc->pll_id;
+ }
+
+ /* no dig encoder assigned */
+ if (dig_encoder == -1)
+ return;
+
+ if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)))
+ is_dp = true;
+
+ memset(&args, 0, sizeof(args));
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ index = GetIndexIntoMasterTable(COMMAND, LVTMATransmitterControl);
+ break;
+ }
+
+ if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
+ return;
+
+ switch (frev) {
+ case 1:
+ switch (crev) {
+ case 1:
+ args.v1.ucAction = action;
+ if (action == ATOM_TRANSMITTER_ACTION_INIT) {
+ args.v1.usInitInfo = cpu_to_le16(connector_object_id);
+ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
+ args.v1.asMode.ucLaneSel = lane_num;
+ args.v1.asMode.ucLaneSet = lane_set;
+ } else {
+ if (is_dp)
+ args.v1.usPixelClock =
+ cpu_to_le16(dp_clock / 10);
+ else if (radeon_encoder->pixel_clock > 165000)
+ args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
+ else
+ args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ }
+
+ args.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL;
+
+ if (dig_encoder)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
+ else
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
+
+ if ((rdev->flags & RADEON_IS_IGP) &&
+ (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY)) {
+ if (is_dp || (radeon_encoder->pixel_clock <= 165000)) {
+ if (igp_lane_info & 0x1)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3;
+ else if (igp_lane_info & 0x2)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_4_7;
+ else if (igp_lane_info & 0x4)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_11;
+ else if (igp_lane_info & 0x8)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15;
+ } else {
+ if (igp_lane_info & 0x3)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7;
+ else if (igp_lane_info & 0xc)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15;
+ }
+ }
+
+ if (dig->linkb)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB;
+ else
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;
+
+ if (is_dp)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
+ else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+ if (dig->coherent_mode)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
+ if (radeon_encoder->pixel_clock > 165000)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK;
+ }
+ break;
+ case 2:
+ args.v2.ucAction = action;
+ if (action == ATOM_TRANSMITTER_ACTION_INIT) {
+ args.v2.usInitInfo = cpu_to_le16(connector_object_id);
+ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
+ args.v2.asMode.ucLaneSel = lane_num;
+ args.v2.asMode.ucLaneSet = lane_set;
+ } else {
+ if (is_dp)
+ args.v2.usPixelClock =
+ cpu_to_le16(dp_clock / 10);
+ else if (radeon_encoder->pixel_clock > 165000)
+ args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
+ else
+ args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ }
+
+ args.v2.acConfig.ucEncoderSel = dig_encoder;
+ if (dig->linkb)
+ args.v2.acConfig.ucLinkSel = 1;
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ args.v2.acConfig.ucTransmitterSel = 0;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ args.v2.acConfig.ucTransmitterSel = 1;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ args.v2.acConfig.ucTransmitterSel = 2;
+ break;
+ }
+
+ if (is_dp) {
+ args.v2.acConfig.fCoherentMode = 1;
+ args.v2.acConfig.fDPConnector = 1;
+ } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+ if (dig->coherent_mode)
+ args.v2.acConfig.fCoherentMode = 1;
+ if (radeon_encoder->pixel_clock > 165000)
+ args.v2.acConfig.fDualLinkConnector = 1;
+ }
+ break;
+ case 3:
+ args.v3.ucAction = action;
+ if (action == ATOM_TRANSMITTER_ACTION_INIT) {
+ args.v3.usInitInfo = cpu_to_le16(connector_object_id);
+ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
+ args.v3.asMode.ucLaneSel = lane_num;
+ args.v3.asMode.ucLaneSet = lane_set;
+ } else {
+ if (is_dp)
+ args.v3.usPixelClock =
+ cpu_to_le16(dp_clock / 10);
+ else if (radeon_encoder->pixel_clock > 165000)
+ args.v3.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
+ else
+ args.v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ }
+
+ if (is_dp)
+ args.v3.ucLaneNum = dp_lane_count;
+ else if (radeon_encoder->pixel_clock > 165000)
+ args.v3.ucLaneNum = 8;
+ else
+ args.v3.ucLaneNum = 4;
+
+ if (dig->linkb)
+ args.v3.acConfig.ucLinkSel = 1;
+ if (dig_encoder & 1)
+ args.v3.acConfig.ucEncoderSel = 1;
+
+ /* Select the PLL for the PHY
+ * DP PHY should be clocked from external src if there is
+ * one.
+ */
+ /* On DCE4, if there is an external clock, it generates the DP ref clock */
+ if (is_dp && rdev->clock.dp_extclk)
+ args.v3.acConfig.ucRefClkSource = 2; /* external src */
+ else
+ args.v3.acConfig.ucRefClkSource = pll_id;
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ args.v3.acConfig.ucTransmitterSel = 0;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ args.v3.acConfig.ucTransmitterSel = 1;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ args.v3.acConfig.ucTransmitterSel = 2;
+ break;
+ }
+
+ if (is_dp)
+ args.v3.acConfig.fCoherentMode = 1; /* DP requires coherent */
+ else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+ if (dig->coherent_mode)
+ args.v3.acConfig.fCoherentMode = 1;
+ if (radeon_encoder->pixel_clock > 165000)
+ args.v3.acConfig.fDualLinkConnector = 1;
+ }
+ break;
+ case 4:
+ args.v4.ucAction = action;
+ if (action == ATOM_TRANSMITTER_ACTION_INIT) {
+ args.v4.usInitInfo = cpu_to_le16(connector_object_id);
+ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
+ args.v4.asMode.ucLaneSel = lane_num;
+ args.v4.asMode.ucLaneSet = lane_set;
+ } else {
+ if (is_dp)
+ args.v4.usPixelClock =
+ cpu_to_le16(dp_clock / 10);
+ else if (radeon_encoder->pixel_clock > 165000)
+ args.v4.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
+ else
+ args.v4.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ }
+
+ if (is_dp)
+ args.v4.ucLaneNum = dp_lane_count;
+ else if (radeon_encoder->pixel_clock > 165000)
+ args.v4.ucLaneNum = 8;
+ else
+ args.v4.ucLaneNum = 4;
+
+ if (dig->linkb)
+ args.v4.acConfig.ucLinkSel = 1;
+ if (dig_encoder & 1)
+ args.v4.acConfig.ucEncoderSel = 1;
+
+ /* Select the PLL for the PHY
+ * DP PHY should be clocked from external src if there is
+ * one.
+ */
+ /* On DCE5 DCPLL usually generates the DP ref clock */
+ if (is_dp) {
+ if (rdev->clock.dp_extclk)
+ args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_EXTCLK;
+ else
+ args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_DCPLL;
+ } else
+ args.v4.acConfig.ucRefClkSource = pll_id;
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ args.v4.acConfig.ucTransmitterSel = 0;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ args.v4.acConfig.ucTransmitterSel = 1;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ args.v4.acConfig.ucTransmitterSel = 2;
+ break;
+ }
+
+ if (is_dp)
+ args.v4.acConfig.fCoherentMode = 1; /* DP requires coherent */
+ else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+ if (dig->coherent_mode)
+ args.v4.acConfig.fCoherentMode = 1;
+ if (radeon_encoder->pixel_clock > 165000)
+ args.v4.acConfig.fDualLinkConnector = 1;
+ }
+ break;
+ default:
+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+ break;
+ }
+ break;
+ default:
+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+ break;
+ }
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
+bool
+atombios_set_edp_panel_power(struct drm_connector *connector, int action)
+{
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct drm_device *dev = radeon_connector->base.dev;
+ struct radeon_device *rdev = dev->dev_private;
+ union dig_transmitter_control args;
+ int index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl);
+ uint8_t frev, crev;
+
+ if (connector->connector_type != DRM_MODE_CONNECTOR_eDP)
+ goto done;
+
+ if (!ASIC_IS_DCE4(rdev))
+ goto done;
+
+ if ((action != ATOM_TRANSMITTER_ACTION_POWER_ON) &&
+ (action != ATOM_TRANSMITTER_ACTION_POWER_OFF))
+ goto done;
+
+ if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
+ goto done;
+
+ memset(&args, 0, sizeof(args));
+
+ args.v1.ucAction = action;
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+ /* wait for the panel to power up */
+ if (action == ATOM_TRANSMITTER_ACTION_POWER_ON) {
+ int i;
+
+ for (i = 0; i < 300; i++) {
+ if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
+ return true;
+ mdelay(1);
+ }
+ return false;
+ }
+done:
+ return true;
+}
+
+union external_encoder_control {
+ EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION v1;
+ EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION_V3 v3;
+};
+
+static void
+atombios_external_encoder_setup(struct drm_encoder *encoder,
+ struct drm_encoder *ext_encoder,
+ int action)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_encoder *ext_radeon_encoder = to_radeon_encoder(ext_encoder);
+ union external_encoder_control args;
+ struct drm_connector *connector;
+ int index = GetIndexIntoMasterTable(COMMAND, ExternalEncoderControl);
+ u8 frev, crev;
+ int dp_clock = 0;
+ int dp_lane_count = 0;
+ int connector_object_id = 0;
+ u32 ext_enum = (ext_radeon_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT;
+ int bpc = 8;
+
+ if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT)
+ connector = radeon_get_connector_for_encoder_init(encoder);
+ else
+ connector = radeon_get_connector_for_encoder(encoder);
+
+ if (connector) {
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct radeon_connector_atom_dig *dig_connector =
+ radeon_connector->con_priv;
+
+ dp_clock = dig_connector->dp_clock;
+ dp_lane_count = dig_connector->dp_lane_count;
+ connector_object_id =
+ (radeon_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
+ bpc = connector->display_info.bpc;
+ }
+
+ memset(&args, 0, sizeof(args));
+
+ if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
+ return;
+
+ switch (frev) {
+ case 1:
+ /* no params on frev 1 */
+ break;
+ case 2:
+ switch (crev) {
+ case 1:
+ case 2:
+ args.v1.sDigEncoder.ucAction = action;
+ args.v1.sDigEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ args.v1.sDigEncoder.ucEncoderMode = atombios_get_encoder_mode(encoder);
+
+ if (ENCODER_MODE_IS_DP(args.v1.sDigEncoder.ucEncoderMode)) {
+ if (dp_clock == 270000)
+ args.v1.sDigEncoder.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
+ args.v1.sDigEncoder.ucLaneNum = dp_lane_count;
+ } else if (radeon_encoder->pixel_clock > 165000)
+ args.v1.sDigEncoder.ucLaneNum = 8;
+ else
+ args.v1.sDigEncoder.ucLaneNum = 4;
+ break;
+ case 3:
+ args.v3.sExtEncoder.ucAction = action;
+ if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT)
+ args.v3.sExtEncoder.usConnectorId = cpu_to_le16(connector_object_id);
+ else
+ args.v3.sExtEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ args.v3.sExtEncoder.ucEncoderMode = atombios_get_encoder_mode(encoder);
+
+ if (ENCODER_MODE_IS_DP(args.v3.sExtEncoder.ucEncoderMode)) {
+ if (dp_clock == 270000)
+ args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ;
+ else if (dp_clock == 540000)
+ args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_5_40GHZ;
+ args.v3.sExtEncoder.ucLaneNum = dp_lane_count;
+ } else if (radeon_encoder->pixel_clock > 165000)
+ args.v3.sExtEncoder.ucLaneNum = 8;
+ else
+ args.v3.sExtEncoder.ucLaneNum = 4;
+ switch (ext_enum) {
+ case GRAPH_OBJECT_ENUM_ID1:
+ args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER1;
+ break;
+ case GRAPH_OBJECT_ENUM_ID2:
+ args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER2;
+ break;
+ case GRAPH_OBJECT_ENUM_ID3:
+ args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER3;
+ break;
+ }
+ switch (bpc) {
+ case 0:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_BPC_UNDEFINE;
+ break;
+ case 6:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_6BIT_PER_COLOR;
+ break;
+ case 8:
+ default:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_8BIT_PER_COLOR;
+ break;
+ case 10:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_10BIT_PER_COLOR;
+ break;
+ case 12:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_12BIT_PER_COLOR;
+ break;
+ case 16:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_16BIT_PER_COLOR;
+ break;
+ }
+ break;
+ default:
+ DRM_ERROR("Unknown table version: %d, %d\n", frev, crev);
+ return;
+ }
+ break;
+ default:
+ DRM_ERROR("Unknown table version: %d, %d\n", frev, crev);
+ return;
+ }
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
+static void
+atombios_yuv_setup(struct drm_encoder *encoder, bool enable)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+ ENABLE_YUV_PS_ALLOCATION args;
+ int index = GetIndexIntoMasterTable(COMMAND, EnableYUV);
+ uint32_t temp, reg;
+
+ memset(&args, 0, sizeof(args));
+
+ if (rdev->family >= CHIP_R600)
+ reg = R600_BIOS_3_SCRATCH;
+ else
+ reg = RADEON_BIOS_3_SCRATCH;
+
+ /* XXX: fix up scratch reg handling */
+ temp = RREG32(reg);
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
+ WREG32(reg, (ATOM_S3_TV1_ACTIVE |
+ (radeon_crtc->crtc_id << 18)));
+ else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
+ WREG32(reg, (ATOM_S3_CV_ACTIVE | (radeon_crtc->crtc_id << 24)));
+ else
+ WREG32(reg, 0);
+
+ if (enable)
+ args.ucEnable = ATOM_ENABLE;
+ args.ucCRTC = radeon_crtc->crtc_id;
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+ WREG32(reg, temp);
+}
+
+static void
+radeon_atom_encoder_dpms_avivo(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args;
+ int index = 0;
+
+ memset(&args, 0, sizeof(args));
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+ index = GetIndexIntoMasterTable(COMMAND, TMDSAOutputControl);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_DDI:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+ index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
+ index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl);
+ else
+ index = GetIndexIntoMasterTable(COMMAND, LVTMAOutputControl);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DAC1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
+ index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl);
+ else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
+ index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl);
+ else
+ index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DAC2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
+ index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl);
+ else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
+ index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl);
+ else
+ index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl);
+ break;
+ default:
+ return;
+ }
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ args.ucAction = ATOM_ENABLE;
+ /* workaround for DVOOutputControl on some RS690 systems */
+ if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DDI) {
+ u32 reg = RREG32(RADEON_BIOS_3_SCRATCH);
+ WREG32(RADEON_BIOS_3_SCRATCH, reg & ~ATOM_S3_DFP2I_ACTIVE);
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ WREG32(RADEON_BIOS_3_SCRATCH, reg);
+ } else
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+ args.ucAction = ATOM_LCD_BLON;
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ }
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ args.ucAction = ATOM_DISABLE;
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+ args.ucAction = ATOM_LCD_BLOFF;
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ }
+ break;
+ }
+}
+
+static void
+radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+ struct radeon_connector *radeon_connector = NULL;
+ struct radeon_connector_atom_dig *radeon_dig_connector = NULL;
+
+ if (connector) {
+ radeon_connector = to_radeon_connector(connector);
+ radeon_dig_connector = radeon_connector->con_priv;
+ }
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ /* some early dce3.2 boards have a bug in their transmitter control table */
+ if ((rdev->family == CHIP_RV710) || (rdev->family == CHIP_RV730))
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
+ else
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
+ if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ atombios_set_edp_panel_power(connector,
+ ATOM_TRANSMITTER_ACTION_POWER_ON);
+ radeon_dig_connector->edp_on = true;
+ }
+ if (ASIC_IS_DCE4(rdev))
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
+ radeon_dp_link_train(encoder, connector);
+ if (ASIC_IS_DCE4(rdev))
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0);
+ }
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0);
+ if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
+ if (ASIC_IS_DCE4(rdev))
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ atombios_set_edp_panel_power(connector,
+ ATOM_TRANSMITTER_ACTION_POWER_OFF);
+ radeon_dig_connector->edp_on = false;
+ }
+ }
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLOFF, 0, 0);
+ break;
+ }
+}
+
+static void
+radeon_atom_encoder_dpms_ext(struct drm_encoder *encoder,
+ struct drm_encoder *ext_encoder,
+ int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ default:
+ if (ASIC_IS_DCE41(rdev)) {
+ atombios_external_encoder_setup(encoder, ext_encoder,
+ EXTERNAL_ENCODER_ACTION_V3_ENABLE_OUTPUT);
+ atombios_external_encoder_setup(encoder, ext_encoder,
+ EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING_OFF);
+ } else
+ atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ if (ASIC_IS_DCE41(rdev)) {
+ atombios_external_encoder_setup(encoder, ext_encoder,
+ EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING);
+ atombios_external_encoder_setup(encoder, ext_encoder,
+ EXTERNAL_ENCODER_ACTION_V3_DISABLE_OUTPUT);
+ } else
+ atombios_external_encoder_setup(encoder, ext_encoder, ATOM_DISABLE);
+ break;
+ }
+}
+
+static void
+radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder);
+
+ DRM_DEBUG_KMS("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n",
+ radeon_encoder->encoder_id, mode, radeon_encoder->devices,
+ radeon_encoder->active_device);
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+ case ENCODER_OBJECT_ID_INTERNAL_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_DDI:
+ case ENCODER_OBJECT_ID_INTERNAL_DAC2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
+ radeon_atom_encoder_dpms_avivo(encoder, mode);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ radeon_atom_encoder_dpms_dig(encoder, mode);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ if (ASIC_IS_DCE5(rdev)) {
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ atombios_dvo_setup(encoder, ATOM_ENABLE);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ atombios_dvo_setup(encoder, ATOM_DISABLE);
+ break;
+ }
+ } else if (ASIC_IS_DCE3(rdev))
+ radeon_atom_encoder_dpms_dig(encoder, mode);
+ else
+ radeon_atom_encoder_dpms_avivo(encoder, mode);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DAC1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
+ if (ASIC_IS_DCE5(rdev)) {
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ atombios_dac_setup(encoder, ATOM_ENABLE);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ atombios_dac_setup(encoder, ATOM_DISABLE);
+ break;
+ }
+ } else
+ radeon_atom_encoder_dpms_avivo(encoder, mode);
+ break;
+ default:
+ return;
+ }
+
+ if (ext_encoder)
+ radeon_atom_encoder_dpms_ext(encoder, ext_encoder, mode);
+
+ radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+}
+
+union crtc_source_param {
+ SELECT_CRTC_SOURCE_PS_ALLOCATION v1;
+ SELECT_CRTC_SOURCE_PARAMETERS_V2 v2;
+};
+
+static void
+atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+ union crtc_source_param args;
+ int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source);
+ uint8_t frev, crev;
+ struct radeon_encoder_atom_dig *dig;
+
+ memset(&args, 0, sizeof(args));
+
+ if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
+ return;
+
+ switch (frev) {
+ case 1:
+ switch (crev) {
+ case 1:
+ default:
+ if (ASIC_IS_AVIVO(rdev))
+ args.v1.ucCRTC = radeon_crtc->crtc_id;
+ else {
+ if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) {
+ args.v1.ucCRTC = radeon_crtc->crtc_id;
+ } else {
+ args.v1.ucCRTC = radeon_crtc->crtc_id << 2;
+ }
+ }
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+ args.v1.ucDevice = ATOM_DEVICE_DFP1_INDEX;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+ if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT)
+ args.v1.ucDevice = ATOM_DEVICE_LCD1_INDEX;
+ else
+ args.v1.ucDevice = ATOM_DEVICE_DFP3_INDEX;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_DDI:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ args.v1.ucDevice = ATOM_DEVICE_DFP2_INDEX;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DAC1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
+ args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX;
+ else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
+ args.v1.ucDevice = ATOM_DEVICE_CV_INDEX;
+ else
+ args.v1.ucDevice = ATOM_DEVICE_CRT1_INDEX;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DAC2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
+ args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX;
+ else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
+ args.v1.ucDevice = ATOM_DEVICE_CV_INDEX;
+ else
+ args.v1.ucDevice = ATOM_DEVICE_CRT2_INDEX;
+ break;
+ }
+ break;
+ case 2:
+ args.v2.ucCRTC = radeon_crtc->crtc_id;
+ if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE) {
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+ args.v2.ucEncodeMode = ATOM_ENCODER_MODE_LVDS;
+ else if (connector->connector_type == DRM_MODE_CONNECTOR_VGA)
+ args.v2.ucEncodeMode = ATOM_ENCODER_MODE_CRT;
+ else
+ args.v2.ucEncodeMode = atombios_get_encoder_mode(encoder);
+ } else
+ args.v2.ucEncodeMode = atombios_get_encoder_mode(encoder);
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ dig = radeon_encoder->enc_priv;
+ switch (dig->dig_encoder) {
+ case 0:
+ args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
+ break;
+ case 1:
+ args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
+ break;
+ case 2:
+ args.v2.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID;
+ break;
+ case 3:
+ args.v2.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID;
+ break;
+ case 4:
+ args.v2.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID;
+ break;
+ case 5:
+ args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID;
+ break;
+ }
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
+ args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
+ else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
+ args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
+ else
+ args.v2.ucEncoderID = ASIC_INT_DAC1_ENCODER_ID;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
+ args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
+ else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
+ args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
+ else
+ args.v2.ucEncoderID = ASIC_INT_DAC2_ENCODER_ID;
+ break;
+ }
+ break;
+ }
+ break;
+ default:
+ DRM_ERROR("Unknown table version: %d, %d\n", frev, crev);
+ return;
+ }
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+ /* update scratch regs with new routing */
+ radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
+}
+
+static void
+atombios_apply_encoder_quirks(struct drm_encoder *encoder,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+
+ /* Funky macbooks */
+ if ((dev->pdev->device == 0x71C5) &&
+ (dev->pdev->subsystem_vendor == 0x106b) &&
+ (dev->pdev->subsystem_device == 0x0080)) {
+ if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) {
+ uint32_t lvtma_bit_depth_control = RREG32(AVIVO_LVTMA_BIT_DEPTH_CONTROL);
+
+ lvtma_bit_depth_control &= ~AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_EN;
+ lvtma_bit_depth_control &= ~AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN;
+
+ WREG32(AVIVO_LVTMA_BIT_DEPTH_CONTROL, lvtma_bit_depth_control);
+ }
+ }
+
+ /* set scaler clears this on some chips */
+ if (ASIC_IS_AVIVO(rdev) &&
+ (!(radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)))) {
+ if (ASIC_IS_DCE4(rdev)) {
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset,
+ EVERGREEN_INTERLEAVE_EN);
+ else
+ WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset, 0);
+ } else {
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset,
+ AVIVO_D1MODE_INTERLEAVE_EN);
+ else
+ WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, 0);
+ }
+ }
+}
+
+static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_encoder *test_encoder;
+ struct radeon_encoder_atom_dig *dig;
+ uint32_t dig_enc_in_use = 0;
+
+ /* DCE4/5 */
+ if (ASIC_IS_DCE4(rdev)) {
+ dig = radeon_encoder->enc_priv;
+ if (ASIC_IS_DCE41(rdev)) {
+ /* ontario follows DCE4 */
+ if (rdev->family == CHIP_PALM) {
+ if (dig->linkb)
+ return 1;
+ else
+ return 0;
+ } else
+ /* llano follows DCE3.2 */
+ return radeon_crtc->crtc_id;
+ } else {
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ if (dig->linkb)
+ return 1;
+ else
+ return 0;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ if (dig->linkb)
+ return 3;
+ else
+ return 2;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ if (dig->linkb)
+ return 5;
+ else
+ return 4;
+ break;
+ }
+ }
+ }
+
+ /* on DCE32 and encoder can driver any block so just crtc id */
+ if (ASIC_IS_DCE32(rdev)) {
+ return radeon_crtc->crtc_id;
+ }
+
+ /* on DCE3 - LVTMA can only be driven by DIGB */
+ list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
+ struct radeon_encoder *radeon_test_encoder;
+
+ if (encoder == test_encoder)
+ continue;
+
+ if (!radeon_encoder_is_digital(test_encoder))
+ continue;
+
+ radeon_test_encoder = to_radeon_encoder(test_encoder);
+ dig = radeon_test_encoder->enc_priv;
+
+ if (dig->dig_encoder >= 0)
+ dig_enc_in_use |= (1 << dig->dig_encoder);
+ }
+
+ if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA) {
+ if (dig_enc_in_use & 0x2)
+ DRM_ERROR("LVDS required digital encoder 2 but it was in use - stealing\n");
+ return 1;
+ }
+ if (!(dig_enc_in_use & 1))
+ return 0;
+ return 1;
+}
+
+/* This only needs to be called once at startup */
+void
+radeon_atom_encoder_init(struct radeon_device *rdev)
+{
+ struct drm_device *dev = rdev->ddev;
+ struct drm_encoder *encoder;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder);
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
+ break;
+ default:
+ break;
+ }
+
+ if (ext_encoder && ASIC_IS_DCE41(rdev))
+ atombios_external_encoder_setup(encoder, ext_encoder,
+ EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT);
+ }
+}
+
+static void
+radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder);
+
+ radeon_encoder->pixel_clock = adjusted_mode->clock;
+
+ if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE4(rdev)) {
+ if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT))
+ atombios_yuv_setup(encoder, true);
+ else
+ atombios_yuv_setup(encoder, false);
+ }
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+ atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_ENABLE);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ if (ASIC_IS_DCE4(rdev)) {
+ /* disable the transmitter */
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+ /* setup and enable the encoder */
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0);
+
+ /* enable the transmitter */
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
+ } else {
+ /* disable the encoder and transmitter */
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+ atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0);
+
+ /* setup and enable the encoder and transmitter */
+ atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0);
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
+ }
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DDI:
+ case ENCODER_OBJECT_ID_INTERNAL_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ atombios_dvo_setup(encoder, ATOM_ENABLE);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DAC1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
+ case ENCODER_OBJECT_ID_INTERNAL_DAC2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
+ atombios_dac_setup(encoder, ATOM_ENABLE);
+ if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) {
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
+ atombios_tv_setup(encoder, ATOM_ENABLE);
+ else
+ atombios_tv_setup(encoder, ATOM_DISABLE);
+ }
+ break;
+ }
+
+ if (ext_encoder) {
+ if (ASIC_IS_DCE41(rdev))
+ atombios_external_encoder_setup(encoder, ext_encoder,
+ EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP);
+ else
+ atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE);
+ }
+
+ atombios_apply_encoder_quirks(encoder, adjusted_mode);
+
+ if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) {
+ r600_hdmi_enable(encoder);
+ r600_hdmi_setmode(encoder, adjusted_mode);
+ }
+}
+
+static bool
+atombios_dac_load_detect(struct drm_encoder *encoder, struct drm_connector *connector)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+ if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT |
+ ATOM_DEVICE_CV_SUPPORT |
+ ATOM_DEVICE_CRT_SUPPORT)) {
+ DAC_LOAD_DETECTION_PS_ALLOCATION args;
+ int index = GetIndexIntoMasterTable(COMMAND, DAC_LoadDetection);
+ uint8_t frev, crev;
+
+ memset(&args, 0, sizeof(args));
+
+ if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
+ return false;
+
+ args.sDacload.ucMisc = 0;
+
+ if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) ||
+ (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1))
+ args.sDacload.ucDacType = ATOM_DAC_A;
+ else
+ args.sDacload.ucDacType = ATOM_DAC_B;
+
+ if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT)
+ args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT1_SUPPORT);
+ else if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT)
+ args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT2_SUPPORT);
+ else if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) {
+ args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CV_SUPPORT);
+ if (crev >= 3)
+ args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb;
+ } else if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) {
+ args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_TV1_SUPPORT);
+ if (crev >= 3)
+ args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb;
+ }
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+ return true;
+ } else
+ return false;
+}
+
+static enum drm_connector_status
+radeon_atom_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ uint32_t bios_0_scratch;
+
+ if (!atombios_dac_load_detect(encoder, connector)) {
+ DRM_DEBUG_KMS("detect returned false \n");
+ return connector_status_unknown;
+ }
+
+ if (rdev->family >= CHIP_R600)
+ bios_0_scratch = RREG32(R600_BIOS_0_SCRATCH);
+ else
+ bios_0_scratch = RREG32(RADEON_BIOS_0_SCRATCH);
+
+ DRM_DEBUG_KMS("Bios 0 scratch %x %08x\n", bios_0_scratch, radeon_encoder->devices);
+ if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) {
+ if (bios_0_scratch & ATOM_S0_CRT1_MASK)
+ return connector_status_connected;
+ }
+ if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) {
+ if (bios_0_scratch & ATOM_S0_CRT2_MASK)
+ return connector_status_connected;
+ }
+ if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) {
+ if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A))
+ return connector_status_connected;
+ }
+ if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) {
+ if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A))
+ return connector_status_connected; /* CTV */
+ else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A))
+ return connector_status_connected; /* STV */
+ }
+ return connector_status_disconnected;
+}
+
+static enum drm_connector_status
+radeon_atom_dig_detect(struct drm_encoder *encoder, struct drm_connector *connector)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder);
+ u32 bios_0_scratch;
+
+ if (!ASIC_IS_DCE4(rdev))
+ return connector_status_unknown;
+
+ if (!ext_encoder)
+ return connector_status_unknown;
+
+ if ((radeon_connector->devices & ATOM_DEVICE_CRT_SUPPORT) == 0)
+ return connector_status_unknown;
+
+ /* load detect on the dp bridge */
+ atombios_external_encoder_setup(encoder, ext_encoder,
+ EXTERNAL_ENCODER_ACTION_V3_DACLOAD_DETECTION);
+
+ bios_0_scratch = RREG32(R600_BIOS_0_SCRATCH);
+
+ DRM_DEBUG_KMS("Bios 0 scratch %x %08x\n", bios_0_scratch, radeon_encoder->devices);
+ if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) {
+ if (bios_0_scratch & ATOM_S0_CRT1_MASK)
+ return connector_status_connected;
+ }
+ if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) {
+ if (bios_0_scratch & ATOM_S0_CRT2_MASK)
+ return connector_status_connected;
+ }
+ if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) {
+ if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A))
+ return connector_status_connected;
+ }
+ if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) {
+ if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A))
+ return connector_status_connected; /* CTV */
+ else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A))
+ return connector_status_connected; /* STV */
+ }
+ return connector_status_disconnected;
+}
+
+void
+radeon_atom_ext_encoder_setup_ddc(struct drm_encoder *encoder)
+{
+ struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder);
+
+ if (ext_encoder)
+ /* ddc_setup on the dp bridge */
+ atombios_external_encoder_setup(encoder, ext_encoder,
+ EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP);
+
+}
+
+static void radeon_atom_encoder_prepare(struct drm_encoder *encoder)
+{
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+
+ if ((radeon_encoder->active_device &
+ (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) ||
+ (radeon_encoder_get_dp_bridge_encoder_id(encoder) !=
+ ENCODER_OBJECT_ID_NONE)) {
+ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+ if (dig)
+ dig->dig_encoder = radeon_atom_pick_dig_encoder(encoder);
+ }
+
+ radeon_atom_output_lock(encoder, true);
+ radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+ if (connector) {
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+ /* select the clock/data port if it uses a router */
+ if (radeon_connector->router.cd_valid)
+ radeon_router_select_cd_port(radeon_connector);
+
+ /* turn eDP panel on for mode set */
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+ atombios_set_edp_panel_power(connector,
+ ATOM_TRANSMITTER_ACTION_POWER_ON);
+ }
+
+ /* this is needed for the pll/ss setup to work correctly in some cases */
+ atombios_set_encoder_crtc_source(encoder);
+}
+
+static void radeon_atom_encoder_commit(struct drm_encoder *encoder)
+{
+ radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+ radeon_atom_output_lock(encoder, false);
+}
+
+static void radeon_atom_encoder_disable(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_encoder_atom_dig *dig;
+
+ /* check for pre-DCE3 cards with shared encoders;
+ * can't really use the links individually, so don't disable
+ * the encoder if it's in use by another connector
+ */
+ if (!ASIC_IS_DCE3(rdev)) {
+ struct drm_encoder *other_encoder;
+ struct radeon_encoder *other_radeon_encoder;
+
+ list_for_each_entry(other_encoder, &dev->mode_config.encoder_list, head) {
+ other_radeon_encoder = to_radeon_encoder(other_encoder);
+ if ((radeon_encoder->encoder_id == other_radeon_encoder->encoder_id) &&
+ drm_helper_encoder_in_use(other_encoder))
+ goto disable_done;
+ }
+ }
+
+ radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+ atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_DISABLE);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ if (ASIC_IS_DCE4(rdev))
+ /* disable the transmitter */
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+ else {
+ /* disable the encoder and transmitter */
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+ atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0);
+ }
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DDI:
+ case ENCODER_OBJECT_ID_INTERNAL_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ atombios_dvo_setup(encoder, ATOM_DISABLE);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DAC1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
+ case ENCODER_OBJECT_ID_INTERNAL_DAC2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
+ atombios_dac_setup(encoder, ATOM_DISABLE);
+ if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
+ atombios_tv_setup(encoder, ATOM_DISABLE);
+ break;
+ }
+
+disable_done:
+ if (radeon_encoder_is_digital(encoder)) {
+ if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI)
+ r600_hdmi_disable(encoder);
+ dig = radeon_encoder->enc_priv;
+ dig->dig_encoder = -1;
+ }
+ radeon_encoder->active_device = 0;
+}
+
+/* these are handled by the primary encoders */
+static void radeon_atom_ext_prepare(struct drm_encoder *encoder)
+{
+
+}
+
+static void radeon_atom_ext_commit(struct drm_encoder *encoder)
+{
+
+}
+
+static void
+radeon_atom_ext_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+
+}
+
+static void radeon_atom_ext_disable(struct drm_encoder *encoder)
+{
+
+}
+
+static void
+radeon_atom_ext_dpms(struct drm_encoder *encoder, int mode)
+{
+
+}
+
+static bool radeon_atom_ext_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static const struct drm_encoder_helper_funcs radeon_atom_ext_helper_funcs = {
+ .dpms = radeon_atom_ext_dpms,
+ .mode_fixup = radeon_atom_ext_mode_fixup,
+ .prepare = radeon_atom_ext_prepare,
+ .mode_set = radeon_atom_ext_mode_set,
+ .commit = radeon_atom_ext_commit,
+ .disable = radeon_atom_ext_disable,
+ /* no detect for TMDS/LVDS yet */
+};
+
+static const struct drm_encoder_helper_funcs radeon_atom_dig_helper_funcs = {
+ .dpms = radeon_atom_encoder_dpms,
+ .mode_fixup = radeon_atom_mode_fixup,
+ .prepare = radeon_atom_encoder_prepare,
+ .mode_set = radeon_atom_encoder_mode_set,
+ .commit = radeon_atom_encoder_commit,
+ .disable = radeon_atom_encoder_disable,
+ .detect = radeon_atom_dig_detect,
+};
+
+static const struct drm_encoder_helper_funcs radeon_atom_dac_helper_funcs = {
+ .dpms = radeon_atom_encoder_dpms,
+ .mode_fixup = radeon_atom_mode_fixup,
+ .prepare = radeon_atom_encoder_prepare,
+ .mode_set = radeon_atom_encoder_mode_set,
+ .commit = radeon_atom_encoder_commit,
+ .detect = radeon_atom_dac_detect,
+};
+
+void radeon_enc_destroy(struct drm_encoder *encoder)
+{
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ kfree(radeon_encoder->enc_priv);
+ drm_encoder_cleanup(encoder);
+ kfree(radeon_encoder);
+}
+
+static const struct drm_encoder_funcs radeon_atom_enc_funcs = {
+ .destroy = radeon_enc_destroy,
+};
+
+struct radeon_encoder_atom_dac *
+radeon_atombios_set_dac_info(struct radeon_encoder *radeon_encoder)
+{
+ struct drm_device *dev = radeon_encoder->base.dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder_atom_dac *dac = kzalloc(sizeof(struct radeon_encoder_atom_dac), GFP_KERNEL);
+
+ if (!dac)
+ return NULL;
+
+ dac->tv_std = radeon_atombios_get_tv_info(rdev);
+ return dac;
+}
+
+struct radeon_encoder_atom_dig *
+radeon_atombios_set_dig_info(struct radeon_encoder *radeon_encoder)
+{
+ int encoder_enum = (radeon_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT;
+ struct radeon_encoder_atom_dig *dig = kzalloc(sizeof(struct radeon_encoder_atom_dig), GFP_KERNEL);
+
+ if (!dig)
+ return NULL;
+
+ /* coherent mode by default */
+ dig->coherent_mode = true;
+ dig->dig_encoder = -1;
+
+ if (encoder_enum == 2)
+ dig->linkb = true;
+ else
+ dig->linkb = false;
+
+ return dig;
+}
+
+void
+radeon_add_atom_encoder(struct drm_device *dev,
+ uint32_t encoder_enum,
+ uint32_t supported_device,
+ u16 caps)
+{
+ struct radeon_device *rdev = dev->dev_private;
+ struct drm_encoder *encoder;
+ struct radeon_encoder *radeon_encoder;
+
+ /* see if we already added it */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ radeon_encoder = to_radeon_encoder(encoder);
+ if (radeon_encoder->encoder_enum == encoder_enum) {
+ radeon_encoder->devices |= supported_device;
+ return;
+ }
+
+ }
+
+ /* add a new one */
+ radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL);
+ if (!radeon_encoder)
+ return;
+
+ encoder = &radeon_encoder->base;
+ switch (rdev->num_crtc) {
+ case 1:
+ encoder->possible_crtcs = 0x1;
+ break;
+ case 2:
+ default:
+ encoder->possible_crtcs = 0x3;
+ break;
+ case 4:
+ encoder->possible_crtcs = 0xf;
+ break;
+ case 6:
+ encoder->possible_crtcs = 0x3f;
+ break;
+ }
+
+ radeon_encoder->enc_priv = NULL;
+
+ radeon_encoder->encoder_enum = encoder_enum;
+ radeon_encoder->encoder_id = (encoder_enum & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
+ radeon_encoder->devices = supported_device;
+ radeon_encoder->rmx_type = RMX_OFF;
+ radeon_encoder->underscan_type = UNDERSCAN_OFF;
+ radeon_encoder->is_ext_encoder = false;
+ radeon_encoder->caps = caps;
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+ radeon_encoder->rmx_type = RMX_FULL;
+ drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS);
+ radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder);
+ } else {
+ drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
+ radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
+ }
+ drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DAC1:
+ drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC);
+ radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder);
+ drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DAC2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
+ drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TVDAC);
+ radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder);
+ drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_DDI:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+ radeon_encoder->rmx_type = RMX_FULL;
+ drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS);
+ radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder);
+ } else if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) {
+ drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC);
+ radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
+ } else {
+ drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
+ radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
+ }
+ drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
+ break;
+ case ENCODER_OBJECT_ID_SI170B:
+ case ENCODER_OBJECT_ID_CH7303:
+ case ENCODER_OBJECT_ID_EXTERNAL_SDVOA:
+ case ENCODER_OBJECT_ID_EXTERNAL_SDVOB:
+ case ENCODER_OBJECT_ID_TITFP513:
+ case ENCODER_OBJECT_ID_VT1623:
+ case ENCODER_OBJECT_ID_HDMI_SI1930:
+ case ENCODER_OBJECT_ID_TRAVIS:
+ case ENCODER_OBJECT_ID_NUTMEG:
+ /* these are handled by the primary encoders */
+ radeon_encoder->is_ext_encoder = true;
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
+ drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS);
+ else if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT))
+ drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC);
+ else
+ drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
+ drm_encoder_helper_add(encoder, &radeon_atom_ext_helper_funcs);
+ break;
+ }
+}
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 660f96401a05..1d603a3335db 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -39,7 +39,32 @@
static void evergreen_gpu_init(struct radeon_device *rdev);
void evergreen_fini(struct radeon_device *rdev);
-static void evergreen_pcie_gen2_enable(struct radeon_device *rdev);
+void evergreen_pcie_gen2_enable(struct radeon_device *rdev);
+
+void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev)
+{
+ u16 ctl, v;
+ int cap, err;
+
+ cap = pci_pcie_cap(rdev->pdev);
+ if (!cap)
+ return;
+
+ err = pci_read_config_word(rdev->pdev, cap + PCI_EXP_DEVCTL, &ctl);
+ if (err)
+ return;
+
+ v = (ctl & PCI_EXP_DEVCTL_READRQ) >> 12;
+
+ /* if bios or OS sets MAX_READ_REQUEST_SIZE to an invalid value, fix it
+ * to avoid hangs or perfomance issues
+ */
+ if ((v == 0) || (v == 6) || (v == 7)) {
+ ctl &= ~PCI_EXP_DEVCTL_READRQ;
+ ctl |= (2 << 12);
+ pci_write_config_word(rdev->pdev, cap + PCI_EXP_DEVCTL, ctl);
+ }
+}
void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc)
{
@@ -132,6 +157,57 @@ int sumo_get_temp(struct radeon_device *rdev)
return actual_temp * 1000;
}
+void sumo_pm_init_profile(struct radeon_device *rdev)
+{
+ int idx;
+
+ /* default */
+ rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+ rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+ rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0;
+
+ /* low,mid sh/mh */
+ if (rdev->flags & RADEON_IS_MOBILITY)
+ idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0);
+ else
+ idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
+
+ rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
+
+ rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
+
+ rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 0;
+
+ rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 0;
+
+ /* high sh/mh */
+ idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
+ rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx =
+ rdev->pm.power_state[idx].num_clock_modes - 1;
+
+ rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx =
+ rdev->pm.power_state[idx].num_clock_modes - 1;
+}
+
void evergreen_pm_misc(struct radeon_device *rdev)
{
int req_ps_idx = rdev->pm.requested_power_state_index;
@@ -328,6 +404,7 @@ void evergreen_hpd_init(struct radeon_device *rdev)
default:
break;
}
+ radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
}
if (rdev->irq.installed)
evergreen_irq_set(rdev);
@@ -743,7 +820,7 @@ static void evergreen_program_watermarks(struct radeon_device *rdev,
!evergreen_average_bandwidth_vs_available_bandwidth(&wm) ||
!evergreen_check_latency_hiding(&wm) ||
(rdev->disp_priority == 2)) {
- DRM_INFO("force priority to high\n");
+ DRM_DEBUG_KMS("force priority to high\n");
priority_a_cnt |= PRIORITY_ALWAYS_ON;
priority_b_cnt |= PRIORITY_ALWAYS_ON;
}
@@ -868,7 +945,7 @@ int evergreen_pcie_gart_enable(struct radeon_device *rdev)
u32 tmp;
int r;
- if (rdev->gart.table.vram.robj == NULL) {
+ if (rdev->gart.robj == NULL) {
dev_err(rdev->dev, "No VRAM object for PCIE GART.\n");
return -EINVAL;
}
@@ -910,6 +987,9 @@ int evergreen_pcie_gart_enable(struct radeon_device *rdev)
WREG32(VM_CONTEXT1_CNTL, 0);
evergreen_pcie_gart_tlb_flush(rdev);
+ DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
+ (unsigned)(rdev->mc.gtt_size >> 20),
+ (unsigned long long)rdev->gart.table_addr);
rdev->gart.ready = true;
return 0;
}
@@ -917,7 +997,6 @@ int evergreen_pcie_gart_enable(struct radeon_device *rdev)
void evergreen_pcie_gart_disable(struct radeon_device *rdev)
{
u32 tmp;
- int r;
/* Disable all tables */
WREG32(VM_CONTEXT0_CNTL, 0);
@@ -937,14 +1016,7 @@ void evergreen_pcie_gart_disable(struct radeon_device *rdev)
WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp);
WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp);
WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp);
- if (rdev->gart.table.vram.robj) {
- r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
- if (likely(r == 0)) {
- radeon_bo_kunmap(rdev->gart.table.vram.robj);
- radeon_bo_unpin(rdev->gart.table.vram.robj);
- radeon_bo_unreserve(rdev->gart.table.vram.robj);
- }
- }
+ radeon_gart_table_vram_unpin(rdev);
}
void evergreen_pcie_gart_fini(struct radeon_device *rdev)
@@ -1198,7 +1270,7 @@ void evergreen_mc_program(struct radeon_device *rdev)
WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR,
rdev->mc.vram_end >> 12);
}
- WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0);
+ WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, rdev->vram_scratch.gpu_addr >> 12);
if (rdev->flags & RADEON_IS_IGP) {
tmp = RREG32(MC_FUS_VM_FB_OFFSET) & 0x000FFFFF;
tmp |= ((rdev->mc.vram_end >> 20) & 0xF) << 24;
@@ -1357,6 +1429,7 @@ int evergreen_cp_resume(struct radeon_device *rdev)
SOFT_RESET_PA |
SOFT_RESET_SH |
SOFT_RESET_VGT |
+ SOFT_RESET_SPI |
SOFT_RESET_SX));
RREG32(GRBM_SOFT_RESET);
mdelay(15);
@@ -1378,13 +1451,11 @@ int evergreen_cp_resume(struct radeon_device *rdev)
/* Initialize the ring buffer's read and write pointers */
WREG32(CP_RB_CNTL, tmp | RB_RPTR_WR_ENA);
WREG32(CP_RB_RPTR_WR, 0);
- WREG32(CP_RB_WPTR, 0);
+ rdev->cp.wptr = 0;
+ WREG32(CP_RB_WPTR, rdev->cp.wptr);
/* set the wb address wether it's enabled or not */
WREG32(CP_RB_RPTR_ADDR,
-#ifdef __BIG_ENDIAN
- RB_RPTR_SWAP(2) |
-#endif
((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC));
WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF);
WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF);
@@ -1403,7 +1474,6 @@ int evergreen_cp_resume(struct radeon_device *rdev)
WREG32(CP_DEBUG, (1 << 27) | (1 << 28));
rdev->cp.rptr = RREG32(CP_RB_RPTR);
- rdev->cp.wptr = RREG32(CP_RB_WPTR);
evergreen_cp_start(rdev);
rdev->cp.ready = true;
@@ -1567,48 +1637,6 @@ static u32 evergreen_get_tile_pipe_to_backend_map(struct radeon_device *rdev,
return backend_map;
}
-static void evergreen_program_channel_remap(struct radeon_device *rdev)
-{
- u32 tcp_chan_steer_lo, tcp_chan_steer_hi, mc_shared_chremap, tmp;
-
- tmp = RREG32(MC_SHARED_CHMAP);
- switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) {
- case 0:
- case 1:
- case 2:
- case 3:
- default:
- /* default mapping */
- mc_shared_chremap = 0x00fac688;
- break;
- }
-
- switch (rdev->family) {
- case CHIP_HEMLOCK:
- case CHIP_CYPRESS:
- case CHIP_BARTS:
- tcp_chan_steer_lo = 0x54763210;
- tcp_chan_steer_hi = 0x0000ba98;
- break;
- case CHIP_JUNIPER:
- case CHIP_REDWOOD:
- case CHIP_CEDAR:
- case CHIP_PALM:
- case CHIP_SUMO:
- case CHIP_SUMO2:
- case CHIP_TURKS:
- case CHIP_CAICOS:
- default:
- tcp_chan_steer_lo = 0x76543210;
- tcp_chan_steer_hi = 0x0000ba98;
- break;
- }
-
- WREG32(TCP_CHAN_STEER_LO, tcp_chan_steer_lo);
- WREG32(TCP_CHAN_STEER_HI, tcp_chan_steer_hi);
- WREG32(MC_SHARED_CHREMAP, mc_shared_chremap);
-}
-
static void evergreen_gpu_init(struct radeon_device *rdev)
{
u32 cc_rb_backend_disable = 0;
@@ -1865,6 +1893,8 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
+ evergreen_fix_pci_max_read_req_size(rdev);
+
cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & ~2;
cc_gc_shader_pipe_config |=
@@ -2000,7 +2030,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
gb_backend_map = 0x66442200;
break;
case CHIP_JUNIPER:
- gb_backend_map = 0x00006420;
+ gb_backend_map = 0x00002200;
break;
default:
gb_backend_map =
@@ -2047,13 +2077,12 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
rdev->config.evergreen.tile_config |=
((gb_addr_config & 0x30000000) >> 28) << 12;
+ rdev->config.evergreen.backend_map = gb_backend_map;
WREG32(GB_BACKEND_MAP, gb_backend_map);
WREG32(GB_ADDR_CONFIG, gb_addr_config);
WREG32(DMIF_ADDR_CONFIG, gb_addr_config);
WREG32(HDP_ADDR_CONFIG, gb_addr_config);
- evergreen_program_channel_remap(rdev);
-
num_shader_engines = ((RREG32(GB_ADDR_CONFIG) & NUM_SHADER_ENGINES(3)) >> 12) + 1;
grbm_gfx_index = INSTANCE_BROADCAST_WRITES;
@@ -2604,7 +2633,7 @@ int evergreen_irq_set(struct radeon_device *rdev)
return 0;
}
-static inline void evergreen_irq_ack(struct radeon_device *rdev)
+static void evergreen_irq_ack(struct radeon_device *rdev)
{
u32 tmp;
@@ -2715,7 +2744,7 @@ void evergreen_irq_suspend(struct radeon_device *rdev)
r600_rlc_stop(rdev);
}
-static inline u32 evergreen_get_ih_wptr(struct radeon_device *rdev)
+static u32 evergreen_get_ih_wptr(struct radeon_device *rdev)
{
u32 wptr, tmp;
@@ -2761,6 +2790,9 @@ int evergreen_irq_process(struct radeon_device *rdev)
return IRQ_NONE;
}
restart_ih:
+ /* Order reading of wptr vs. reading of IH ring data */
+ rmb();
+
/* display interrupts */
evergreen_irq_ack(rdev);
@@ -3018,8 +3050,7 @@ static int evergreen_startup(struct radeon_device *rdev)
int r;
/* enable pcie gen2 link */
- if (!ASIC_IS_DCE5(rdev))
- evergreen_pcie_gen2_enable(rdev);
+ evergreen_pcie_gen2_enable(rdev);
if (ASIC_IS_DCE5(rdev)) {
if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw || !rdev->mc_fw) {
@@ -3044,6 +3075,10 @@ static int evergreen_startup(struct radeon_device *rdev)
}
}
+ r = r600_vram_scratch_init(rdev);
+ if (r)
+ return r;
+
evergreen_mc_program(rdev);
if (rdev->flags & RADEON_IS_AGP) {
evergreen_agp_enable(rdev);
@@ -3056,7 +3091,7 @@ static int evergreen_startup(struct radeon_device *rdev)
r = evergreen_blit_init(rdev);
if (r) {
- evergreen_blit_fini(rdev);
+ r600_blit_fini(rdev);
rdev->asic->copy = NULL;
dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
@@ -3122,43 +3157,14 @@ int evergreen_resume(struct radeon_device *rdev)
int evergreen_suspend(struct radeon_device *rdev)
{
- int r;
-
/* FIXME: we should wait for ring to be empty */
r700_cp_stop(rdev);
rdev->cp.ready = false;
evergreen_irq_suspend(rdev);
radeon_wb_disable(rdev);
evergreen_pcie_gart_disable(rdev);
+ r600_blit_suspend(rdev);
- /* unpin shaders bo */
- r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
- if (likely(r == 0)) {
- radeon_bo_unpin(rdev->r600_blit.shader_obj);
- radeon_bo_unreserve(rdev->r600_blit.shader_obj);
- }
-
- return 0;
-}
-
-int evergreen_copy_blit(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_pages, struct radeon_fence *fence)
-{
- int r;
-
- mutex_lock(&rdev->r600_blit.mutex);
- rdev->r600_blit.vb_ib = NULL;
- r = evergreen_blit_prepare_copy(rdev, num_pages * RADEON_GPU_PAGE_SIZE);
- if (r) {
- if (rdev->r600_blit.vb_ib)
- radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
- mutex_unlock(&rdev->r600_blit.mutex);
- return r;
- }
- evergreen_kms_blit_copy(rdev, src_offset, dst_offset, num_pages * RADEON_GPU_PAGE_SIZE);
- evergreen_blit_done_copy(rdev, fence);
- mutex_unlock(&rdev->r600_blit.mutex);
return 0;
}
@@ -3270,13 +3276,14 @@ int evergreen_init(struct radeon_device *rdev)
void evergreen_fini(struct radeon_device *rdev)
{
- evergreen_blit_fini(rdev);
+ r600_blit_fini(rdev);
r700_cp_fini(rdev);
r600_irq_fini(rdev);
radeon_wb_fini(rdev);
radeon_ib_pool_fini(rdev);
radeon_irq_kms_fini(rdev);
evergreen_pcie_gart_fini(rdev);
+ r600_vram_scratch_fini(rdev);
radeon_gem_fini(rdev);
radeon_fence_driver_fini(rdev);
radeon_agp_fini(rdev);
@@ -3286,7 +3293,7 @@ void evergreen_fini(struct radeon_device *rdev)
rdev->bios = NULL;
}
-static void evergreen_pcie_gen2_enable(struct radeon_device *rdev)
+void evergreen_pcie_gen2_enable(struct radeon_device *rdev)
{
u32 link_width_cntl, speed_cntl;
diff --git a/drivers/gpu/drm/radeon/evergreen_blit_kms.c b/drivers/gpu/drm/radeon/evergreen_blit_kms.c
index 57f3bc17b87e..914e5af84163 100644
--- a/drivers/gpu/drm/radeon/evergreen_blit_kms.c
+++ b/drivers/gpu/drm/radeon/evergreen_blit_kms.c
@@ -56,7 +56,9 @@ set_render_target(struct radeon_device *rdev, int format,
if (h < 8)
h = 8;
- cb_color_info = ((format << 2) | (1 << 24) | (1 << 8));
+ cb_color_info = CB_FORMAT(format) |
+ CB_SOURCE_FORMAT(CB_SF_EXPORT_NORM) |
+ CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
pitch = (w / 8) - 1;
slice = ((w * h) / 64) - 1;
@@ -67,7 +69,7 @@ set_render_target(struct radeon_device *rdev, int format,
radeon_ring_write(rdev, slice);
radeon_ring_write(rdev, 0);
radeon_ring_write(rdev, cb_color_info);
- radeon_ring_write(rdev, (1 << 4));
+ radeon_ring_write(rdev, 0);
radeon_ring_write(rdev, (w - 1) | ((h - 1) << 16));
radeon_ring_write(rdev, 0);
radeon_ring_write(rdev, 0);
@@ -92,6 +94,15 @@ cp_set_surface_sync(struct radeon_device *rdev,
else
cp_coher_size = ((size + 255) >> 8);
+ if (rdev->family >= CHIP_CAYMAN) {
+ /* CP_COHER_CNTL2 has to be set manually when submitting a surface_sync
+ * to the RB directly. For IBs, the CP programs this as part of the
+ * surface_sync packet.
+ */
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+ radeon_ring_write(rdev, (0x85e8 - PACKET3_SET_CONFIG_REG_START) >> 2);
+ radeon_ring_write(rdev, 0); /* CP_COHER_CNTL2 */
+ }
radeon_ring_write(rdev, PACKET3(PACKET3_SURFACE_SYNC, 3));
radeon_ring_write(rdev, sync_type);
radeon_ring_write(rdev, cp_coher_size);
@@ -133,12 +144,16 @@ set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr)
u32 sq_vtx_constant_word2, sq_vtx_constant_word3;
/* high addr, stride */
- sq_vtx_constant_word2 = ((upper_32_bits(gpu_addr) & 0xff) | (16 << 8));
+ sq_vtx_constant_word2 = SQ_VTXC_BASE_ADDR_HI(upper_32_bits(gpu_addr) & 0xff) |
+ SQ_VTXC_STRIDE(16);
#ifdef __BIG_ENDIAN
- sq_vtx_constant_word2 |= (2 << 30);
+ sq_vtx_constant_word2 |= SQ_VTXC_ENDIAN_SWAP(SQ_ENDIAN_8IN32);
#endif
/* xyzw swizzles */
- sq_vtx_constant_word3 = (0 << 3) | (1 << 6) | (2 << 9) | (3 << 12);
+ sq_vtx_constant_word3 = SQ_VTCX_SEL_X(SQ_SEL_X) |
+ SQ_VTCX_SEL_Y(SQ_SEL_Y) |
+ SQ_VTCX_SEL_Z(SQ_SEL_Z) |
+ SQ_VTCX_SEL_W(SQ_SEL_W);
radeon_ring_write(rdev, PACKET3(PACKET3_SET_RESOURCE, 8));
radeon_ring_write(rdev, 0x580);
@@ -149,7 +164,7 @@ set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr)
radeon_ring_write(rdev, 0);
radeon_ring_write(rdev, 0);
radeon_ring_write(rdev, 0);
- radeon_ring_write(rdev, SQ_TEX_VTX_VALID_BUFFER << 30);
+ radeon_ring_write(rdev, S__SQ_CONSTANT_TYPE(SQ_TEX_VTX_VALID_BUFFER));
if ((rdev->family == CHIP_CEDAR) ||
(rdev->family == CHIP_PALM) ||
@@ -168,7 +183,7 @@ set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr)
static void
set_tex_resource(struct radeon_device *rdev,
int format, int w, int h, int pitch,
- u64 gpu_addr)
+ u64 gpu_addr, u32 size)
{
u32 sq_tex_resource_word0, sq_tex_resource_word1;
u32 sq_tex_resource_word4, sq_tex_resource_word7;
@@ -176,14 +191,22 @@ set_tex_resource(struct radeon_device *rdev,
if (h < 1)
h = 1;
- sq_tex_resource_word0 = (1 << 0); /* 2D */
+ sq_tex_resource_word0 = TEX_DIM(SQ_TEX_DIM_2D);
sq_tex_resource_word0 |= ((((pitch >> 3) - 1) << 6) |
((w - 1) << 18));
- sq_tex_resource_word1 = ((h - 1) << 0) | (1 << 28);
+ sq_tex_resource_word1 = ((h - 1) << 0) |
+ TEX_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
/* xyzw swizzles */
- sq_tex_resource_word4 = (0 << 16) | (1 << 19) | (2 << 22) | (3 << 25);
+ sq_tex_resource_word4 = TEX_DST_SEL_X(SQ_SEL_X) |
+ TEX_DST_SEL_Y(SQ_SEL_Y) |
+ TEX_DST_SEL_Z(SQ_SEL_Z) |
+ TEX_DST_SEL_W(SQ_SEL_W);
+
+ sq_tex_resource_word7 = format |
+ S__SQ_CONSTANT_TYPE(SQ_TEX_VTX_VALID_TEXTURE);
- sq_tex_resource_word7 = format | (SQ_TEX_VTX_VALID_TEXTURE << 30);
+ cp_set_surface_sync(rdev,
+ PACKET3_TC_ACTION_ENA, size, gpu_addr);
radeon_ring_write(rdev, PACKET3(PACKET3_SET_RESOURCE, 8));
radeon_ring_write(rdev, 0);
@@ -252,7 +275,7 @@ draw_auto(struct radeon_device *rdev)
}
-/* emits 36 */
+/* emits 39 */
static void
set_default_state(struct radeon_device *rdev)
{
@@ -531,6 +554,11 @@ set_default_state(struct radeon_device *rdev)
radeon_ring_write(rdev, (SQ_DYN_GPR_CNTL_PS_FLUSH_REQ - PACKET3_SET_CONFIG_REG_START) >> 2);
radeon_ring_write(rdev, 0);
+ /* setup LDS */
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+ radeon_ring_write(rdev, (SQ_LDS_RESOURCE_MGMT - PACKET3_SET_CONFIG_REG_START) >> 2);
+ radeon_ring_write(rdev, 0x10001000);
+
/* SQ config */
radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 11));
radeon_ring_write(rdev, (SQ_CONFIG - PACKET3_SET_CONFIG_REG_START) >> 2);
@@ -579,31 +607,6 @@ set_default_state(struct radeon_device *rdev)
}
-static inline uint32_t i2f(uint32_t input)
-{
- u32 result, i, exponent, fraction;
-
- if ((input & 0x3fff) == 0)
- result = 0; /* 0 is a special case */
- else {
- exponent = 140; /* exponent biased by 127; */
- fraction = (input & 0x3fff) << 10; /* cheat and only
- handle numbers below 2^^15 */
- for (i = 0; i < 14; i++) {
- if (fraction & 0x800000)
- break;
- else {
- fraction = fraction << 1; /* keep
- shifting left until top bit = 1 */
- exponent = exponent - 1;
- }
- }
- result = exponent << 23 | (fraction & 0x7fffff); /* mask
- off top bit; assumed 1 */
- }
- return result;
-}
-
int evergreen_blit_init(struct radeon_device *rdev)
{
u32 obj_size;
@@ -612,6 +615,26 @@ int evergreen_blit_init(struct radeon_device *rdev)
u32 packet2s[16];
int num_packet2s = 0;
+ rdev->r600_blit.primitives.set_render_target = set_render_target;
+ rdev->r600_blit.primitives.cp_set_surface_sync = cp_set_surface_sync;
+ rdev->r600_blit.primitives.set_shaders = set_shaders;
+ rdev->r600_blit.primitives.set_vtx_resource = set_vtx_resource;
+ rdev->r600_blit.primitives.set_tex_resource = set_tex_resource;
+ rdev->r600_blit.primitives.set_scissors = set_scissors;
+ rdev->r600_blit.primitives.draw_auto = draw_auto;
+ rdev->r600_blit.primitives.set_default_state = set_default_state;
+
+ rdev->r600_blit.ring_size_common = 55; /* shaders + def state */
+ rdev->r600_blit.ring_size_common += 16; /* fence emit for VB IB */
+ rdev->r600_blit.ring_size_common += 5; /* done copy */
+ rdev->r600_blit.ring_size_common += 16; /* fence emit for done copy */
+
+ rdev->r600_blit.ring_size_per_loop = 74;
+ if (rdev->family >= CHIP_CAYMAN)
+ rdev->r600_blit.ring_size_per_loop += 9; /* additional DWs for surface sync */
+
+ rdev->r600_blit.max_dim = 16384;
+
/* pin copy shader into vram if already initialized */
if (rdev->r600_blit.shader_obj)
goto done;
@@ -707,277 +730,3 @@ done:
radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size);
return 0;
}
-
-void evergreen_blit_fini(struct radeon_device *rdev)
-{
- int r;
-
- radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size);
- if (rdev->r600_blit.shader_obj == NULL)
- return;
- /* If we can't reserve the bo, unref should be enough to destroy
- * it when it becomes idle.
- */
- r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
- if (!r) {
- radeon_bo_unpin(rdev->r600_blit.shader_obj);
- radeon_bo_unreserve(rdev->r600_blit.shader_obj);
- }
- radeon_bo_unref(&rdev->r600_blit.shader_obj);
-}
-
-static int evergreen_vb_ib_get(struct radeon_device *rdev)
-{
- int r;
- r = radeon_ib_get(rdev, &rdev->r600_blit.vb_ib);
- if (r) {
- DRM_ERROR("failed to get IB for vertex buffer\n");
- return r;
- }
-
- rdev->r600_blit.vb_total = 64*1024;
- rdev->r600_blit.vb_used = 0;
- return 0;
-}
-
-static void evergreen_vb_ib_put(struct radeon_device *rdev)
-{
- radeon_fence_emit(rdev, rdev->r600_blit.vb_ib->fence);
- radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
-}
-
-int evergreen_blit_prepare_copy(struct radeon_device *rdev, int size_bytes)
-{
- int r;
- int ring_size, line_size;
- int max_size;
- /* loops of emits + fence emit possible */
- int dwords_per_loop = 74, num_loops;
-
- r = evergreen_vb_ib_get(rdev);
- if (r)
- return r;
-
- /* 8 bpp vs 32 bpp for xfer unit */
- if (size_bytes & 3)
- line_size = 8192;
- else
- line_size = 8192 * 4;
-
- max_size = 8192 * line_size;
-
- /* major loops cover the max size transfer */
- num_loops = ((size_bytes + max_size) / max_size);
- /* minor loops cover the extra non aligned bits */
- num_loops += ((size_bytes % line_size) ? 1 : 0);
- /* calculate number of loops correctly */
- ring_size = num_loops * dwords_per_loop;
- /* set default + shaders */
- ring_size += 52; /* shaders + def state */
- ring_size += 10; /* fence emit for VB IB */
- ring_size += 5; /* done copy */
- ring_size += 10; /* fence emit for done copy */
- r = radeon_ring_lock(rdev, ring_size);
- if (r)
- return r;
-
- set_default_state(rdev); /* 36 */
- set_shaders(rdev); /* 16 */
- return 0;
-}
-
-void evergreen_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence)
-{
- int r;
-
- if (rdev->r600_blit.vb_ib)
- evergreen_vb_ib_put(rdev);
-
- if (fence)
- r = radeon_fence_emit(rdev, fence);
-
- radeon_ring_unlock_commit(rdev);
-}
-
-void evergreen_kms_blit_copy(struct radeon_device *rdev,
- u64 src_gpu_addr, u64 dst_gpu_addr,
- int size_bytes)
-{
- int max_bytes;
- u64 vb_gpu_addr;
- u32 *vb;
-
- DRM_DEBUG("emitting copy %16llx %16llx %d %d\n", src_gpu_addr, dst_gpu_addr,
- size_bytes, rdev->r600_blit.vb_used);
- vb = (u32 *)(rdev->r600_blit.vb_ib->ptr + rdev->r600_blit.vb_used);
- if ((size_bytes & 3) || (src_gpu_addr & 3) || (dst_gpu_addr & 3)) {
- max_bytes = 8192;
-
- while (size_bytes) {
- int cur_size = size_bytes;
- int src_x = src_gpu_addr & 255;
- int dst_x = dst_gpu_addr & 255;
- int h = 1;
- src_gpu_addr = src_gpu_addr & ~255ULL;
- dst_gpu_addr = dst_gpu_addr & ~255ULL;
-
- if (!src_x && !dst_x) {
- h = (cur_size / max_bytes);
- if (h > 8192)
- h = 8192;
- if (h == 0)
- h = 1;
- else
- cur_size = max_bytes;
- } else {
- if (cur_size > max_bytes)
- cur_size = max_bytes;
- if (cur_size > (max_bytes - dst_x))
- cur_size = (max_bytes - dst_x);
- if (cur_size > (max_bytes - src_x))
- cur_size = (max_bytes - src_x);
- }
-
- if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) {
- WARN_ON(1);
- }
-
- vb[0] = i2f(dst_x);
- vb[1] = 0;
- vb[2] = i2f(src_x);
- vb[3] = 0;
-
- vb[4] = i2f(dst_x);
- vb[5] = i2f(h);
- vb[6] = i2f(src_x);
- vb[7] = i2f(h);
-
- vb[8] = i2f(dst_x + cur_size);
- vb[9] = i2f(h);
- vb[10] = i2f(src_x + cur_size);
- vb[11] = i2f(h);
-
- /* src 10 */
- set_tex_resource(rdev, FMT_8,
- src_x + cur_size, h, src_x + cur_size,
- src_gpu_addr);
-
- /* 5 */
- cp_set_surface_sync(rdev,
- PACKET3_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr);
-
-
- /* dst 17 */
- set_render_target(rdev, COLOR_8,
- dst_x + cur_size, h,
- dst_gpu_addr);
-
- /* scissors 12 */
- set_scissors(rdev, dst_x, 0, dst_x + cur_size, h);
-
- /* 15 */
- vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used;
- set_vtx_resource(rdev, vb_gpu_addr);
-
- /* draw 10 */
- draw_auto(rdev);
-
- /* 5 */
- cp_set_surface_sync(rdev,
- PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA,
- cur_size * h, dst_gpu_addr);
-
- vb += 12;
- rdev->r600_blit.vb_used += 12 * 4;
-
- src_gpu_addr += cur_size * h;
- dst_gpu_addr += cur_size * h;
- size_bytes -= cur_size * h;
- }
- } else {
- max_bytes = 8192 * 4;
-
- while (size_bytes) {
- int cur_size = size_bytes;
- int src_x = (src_gpu_addr & 255);
- int dst_x = (dst_gpu_addr & 255);
- int h = 1;
- src_gpu_addr = src_gpu_addr & ~255ULL;
- dst_gpu_addr = dst_gpu_addr & ~255ULL;
-
- if (!src_x && !dst_x) {
- h = (cur_size / max_bytes);
- if (h > 8192)
- h = 8192;
- if (h == 0)
- h = 1;
- else
- cur_size = max_bytes;
- } else {
- if (cur_size > max_bytes)
- cur_size = max_bytes;
- if (cur_size > (max_bytes - dst_x))
- cur_size = (max_bytes - dst_x);
- if (cur_size > (max_bytes - src_x))
- cur_size = (max_bytes - src_x);
- }
-
- if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) {
- WARN_ON(1);
- }
-
- vb[0] = i2f(dst_x / 4);
- vb[1] = 0;
- vb[2] = i2f(src_x / 4);
- vb[3] = 0;
-
- vb[4] = i2f(dst_x / 4);
- vb[5] = i2f(h);
- vb[6] = i2f(src_x / 4);
- vb[7] = i2f(h);
-
- vb[8] = i2f((dst_x + cur_size) / 4);
- vb[9] = i2f(h);
- vb[10] = i2f((src_x + cur_size) / 4);
- vb[11] = i2f(h);
-
- /* src 10 */
- set_tex_resource(rdev, FMT_8_8_8_8,
- (src_x + cur_size) / 4,
- h, (src_x + cur_size) / 4,
- src_gpu_addr);
- /* 5 */
- cp_set_surface_sync(rdev,
- PACKET3_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr);
-
- /* dst 17 */
- set_render_target(rdev, COLOR_8_8_8_8,
- (dst_x + cur_size) / 4, h,
- dst_gpu_addr);
-
- /* scissors 12 */
- set_scissors(rdev, (dst_x / 4), 0, (dst_x + cur_size / 4), h);
-
- /* Vertex buffer setup 15 */
- vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used;
- set_vtx_resource(rdev, vb_gpu_addr);
-
- /* draw 10 */
- draw_auto(rdev);
-
- /* 5 */
- cp_set_surface_sync(rdev,
- PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA,
- cur_size * h, dst_gpu_addr);
-
- /* 74 ring dwords per loop */
- vb += 12;
- rdev->r600_blit.vb_used += 12 * 4;
-
- src_gpu_addr += cur_size * h;
- dst_gpu_addr += cur_size * h;
- size_bytes -= cur_size * h;
- }
- }
-}
-
diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c
index 23d36417158d..38e1bda73d33 100644
--- a/drivers/gpu/drm/radeon/evergreen_cs.c
+++ b/drivers/gpu/drm/radeon/evergreen_cs.c
@@ -122,12 +122,6 @@ static void evergreen_cs_track_init(struct evergreen_cs_track *track)
track->db_s_write_bo = NULL;
}
-static inline int evergreen_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
-{
- /* XXX fill in */
- return 0;
-}
-
static int evergreen_cs_track_check(struct radeon_cs_parser *p)
{
struct evergreen_cs_track *track = p->track;
@@ -236,28 +230,6 @@ static int evergreen_cs_packet_next_reloc(struct radeon_cs_parser *p,
}
/**
- * evergreen_cs_packet_next_is_pkt3_nop() - test if next packet is packet3 nop for reloc
- * @parser: parser structure holding parsing context.
- *
- * Check next packet is relocation packet3, do bo validation and compute
- * GPU offset using the provided start.
- **/
-static inline int evergreen_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p)
-{
- struct radeon_cs_packet p3reloc;
- int r;
-
- r = evergreen_cs_packet_parse(p, &p3reloc, p->idx);
- if (r) {
- return 0;
- }
- if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) {
- return 0;
- }
- return 1;
-}
-
-/**
* evergreen_cs_packet_next_vline() - parse userspace VLINE packet
* @parser: parser structure holding parsing context.
*
@@ -414,7 +386,7 @@ static int evergreen_cs_parse_packet0(struct radeon_cs_parser *p,
* if register is safe. If register is not flag as safe this function
* will test it against a list of register needind special handling.
*/
-static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
+static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
{
struct evergreen_cs_track *track = (struct evergreen_cs_track *)p->track;
struct radeon_cs_reloc *reloc;
@@ -428,7 +400,7 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3
last_reg = ARRAY_SIZE(evergreen_reg_safe_bm);
i = (reg >> 7);
- if (i > last_reg) {
+ if (i >= last_reg) {
dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
return -EINVAL;
}
@@ -508,21 +480,23 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3
}
break;
case DB_Z_INFO:
- r = evergreen_cs_packet_next_reloc(p, &reloc);
- if (r) {
- dev_warn(p->dev, "bad SET_CONTEXT_REG "
- "0x%04X\n", reg);
- return -EINVAL;
- }
track->db_z_info = radeon_get_ib_value(p, idx);
- ib[idx] &= ~Z_ARRAY_MODE(0xf);
- track->db_z_info &= ~Z_ARRAY_MODE(0xf);
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
- ib[idx] |= Z_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
- track->db_z_info |= Z_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
- } else {
- ib[idx] |= Z_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
- track->db_z_info |= Z_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ if (!p->keep_tiling_flags) {
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONTEXT_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ ib[idx] &= ~Z_ARRAY_MODE(0xf);
+ track->db_z_info &= ~Z_ARRAY_MODE(0xf);
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ ib[idx] |= Z_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
+ track->db_z_info |= Z_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
+ } else {
+ ib[idx] |= Z_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ track->db_z_info |= Z_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ }
}
break;
case DB_STENCIL_INFO:
@@ -635,40 +609,44 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3
case CB_COLOR5_INFO:
case CB_COLOR6_INFO:
case CB_COLOR7_INFO:
- r = evergreen_cs_packet_next_reloc(p, &reloc);
- if (r) {
- dev_warn(p->dev, "bad SET_CONTEXT_REG "
- "0x%04X\n", reg);
- return -EINVAL;
- }
tmp = (reg - CB_COLOR0_INFO) / 0x3c;
track->cb_color_info[tmp] = radeon_get_ib_value(p, idx);
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
- ib[idx] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
- track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
- } else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) {
- ib[idx] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
- track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ if (!p->keep_tiling_flags) {
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONTEXT_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ ib[idx] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
+ track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
+ } else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) {
+ ib[idx] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ }
}
break;
case CB_COLOR8_INFO:
case CB_COLOR9_INFO:
case CB_COLOR10_INFO:
case CB_COLOR11_INFO:
- r = evergreen_cs_packet_next_reloc(p, &reloc);
- if (r) {
- dev_warn(p->dev, "bad SET_CONTEXT_REG "
- "0x%04X\n", reg);
- return -EINVAL;
- }
tmp = ((reg - CB_COLOR8_INFO) / 0x1c) + 8;
track->cb_color_info[tmp] = radeon_get_ib_value(p, idx);
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
- ib[idx] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
- track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
- } else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) {
- ib[idx] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
- track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ if (!p->keep_tiling_flags) {
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONTEXT_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ ib[idx] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
+ track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
+ } else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) {
+ ib[idx] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ }
}
break;
case CB_COLOR0_PITCH:
@@ -856,7 +834,6 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3
case SQ_PGM_START_PS:
case SQ_PGM_START_HS:
case SQ_PGM_START_LS:
- case GDS_ADDR_BASE:
case SQ_CONST_MEM_BASE:
case SQ_ALU_CONST_CACHE_GS_0:
case SQ_ALU_CONST_CACHE_GS_1:
@@ -946,6 +923,34 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3
}
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
break;
+ case SX_MEMORY_EXPORT_BASE:
+ if (p->rdev->family >= CHIP_CAYMAN) {
+ dev_warn(p->dev, "bad SET_CONFIG_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONFIG_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ break;
+ case CAYMAN_SX_SCATTER_EXPORT_BASE:
+ if (p->rdev->family < CHIP_CAYMAN) {
+ dev_warn(p->dev, "bad SET_CONTEXT_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONTEXT_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ break;
default:
dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
return -EINVAL;
@@ -963,7 +968,7 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3
* This function will check that the resource has valid field and that
* the texture and mipmap bo object are big enough to cover this resource.
*/
-static inline int evergreen_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
+static int evergreen_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
struct radeon_bo *texture,
struct radeon_bo *mipmap)
{
@@ -1153,6 +1158,34 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return r;
}
break;
+ case PACKET3_DISPATCH_DIRECT:
+ if (pkt->count != 3) {
+ DRM_ERROR("bad DISPATCH_DIRECT\n");
+ return -EINVAL;
+ }
+ r = evergreen_cs_track_check(p);
+ if (r) {
+ dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx);
+ return r;
+ }
+ break;
+ case PACKET3_DISPATCH_INDIRECT:
+ if (pkt->count != 1) {
+ DRM_ERROR("bad DISPATCH_INDIRECT\n");
+ return -EINVAL;
+ }
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("bad DISPATCH_INDIRECT\n");
+ return -EINVAL;
+ }
+ ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
+ r = evergreen_cs_track_check(p);
+ if (r) {
+ dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
+ return r;
+ }
+ break;
case PACKET3_WAIT_REG_MEM:
if (pkt->count != 5) {
DRM_ERROR("bad WAIT_REG_MEM\n");
@@ -1284,10 +1317,12 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
ib[idx+1+(i*8)+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
- ib[idx+1+(i*8)+1] |= TEX_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
- else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
- ib[idx+1+(i*8)+1] |= TEX_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ if (!p->keep_tiling_flags) {
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ ib[idx+1+(i*8)+1] |= TEX_ARRAY_MODE(ARRAY_2D_TILED_THIN1);
+ else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ ib[idx+1+(i*8)+1] |= TEX_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
+ }
texture = reloc->robj;
/* tex mip base */
r = evergreen_cs_packet_next_reloc(p, &reloc);
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index b7b2714f0b32..b937c49054d9 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -351,6 +351,7 @@
#define COLOR_BUFFER_SIZE(x) ((x) << 0)
#define POSITION_BUFFER_SIZE(x) ((x) << 8)
#define SMX_BUFFER_SIZE(x) ((x) << 16)
+#define SX_MEMORY_EXPORT_BASE 0x9010
#define SX_MISC 0x28350
#define CB_PERF_CTR0_SEL_0 0x9A20
@@ -940,11 +941,15 @@
#define CB_COLOR0_SLICE 0x28c68
#define CB_COLOR0_VIEW 0x28c6c
#define CB_COLOR0_INFO 0x28c70
+# define CB_FORMAT(x) ((x) << 2)
# define CB_ARRAY_MODE(x) ((x) << 8)
# define ARRAY_LINEAR_GENERAL 0
# define ARRAY_LINEAR_ALIGNED 1
# define ARRAY_1D_TILED_THIN1 2
# define ARRAY_2D_TILED_THIN1 4
+# define CB_SOURCE_FORMAT(x) ((x) << 24)
+# define CB_SF_EXPORT_FULL 0
+# define CB_SF_EXPORT_NORM 1
#define CB_COLOR0_ATTRIB 0x28c74
#define CB_COLOR0_DIM 0x28c78
/* only CB0-7 blocks have these regs */
@@ -1106,15 +1111,53 @@
#define CB_COLOR7_CLEAR_WORD3 0x28e3c
#define SQ_TEX_RESOURCE_WORD0_0 0x30000
+# define TEX_DIM(x) ((x) << 0)
+# define SQ_TEX_DIM_1D 0
+# define SQ_TEX_DIM_2D 1
+# define SQ_TEX_DIM_3D 2
+# define SQ_TEX_DIM_CUBEMAP 3
+# define SQ_TEX_DIM_1D_ARRAY 4
+# define SQ_TEX_DIM_2D_ARRAY 5
+# define SQ_TEX_DIM_2D_MSAA 6
+# define SQ_TEX_DIM_2D_ARRAY_MSAA 7
#define SQ_TEX_RESOURCE_WORD1_0 0x30004
# define TEX_ARRAY_MODE(x) ((x) << 28)
#define SQ_TEX_RESOURCE_WORD2_0 0x30008
#define SQ_TEX_RESOURCE_WORD3_0 0x3000C
#define SQ_TEX_RESOURCE_WORD4_0 0x30010
+# define TEX_DST_SEL_X(x) ((x) << 16)
+# define TEX_DST_SEL_Y(x) ((x) << 19)
+# define TEX_DST_SEL_Z(x) ((x) << 22)
+# define TEX_DST_SEL_W(x) ((x) << 25)
+# define SQ_SEL_X 0
+# define SQ_SEL_Y 1
+# define SQ_SEL_Z 2
+# define SQ_SEL_W 3
+# define SQ_SEL_0 4
+# define SQ_SEL_1 5
#define SQ_TEX_RESOURCE_WORD5_0 0x30014
#define SQ_TEX_RESOURCE_WORD6_0 0x30018
#define SQ_TEX_RESOURCE_WORD7_0 0x3001c
+#define SQ_VTX_CONSTANT_WORD0_0 0x30000
+#define SQ_VTX_CONSTANT_WORD1_0 0x30004
+#define SQ_VTX_CONSTANT_WORD2_0 0x30008
+# define SQ_VTXC_BASE_ADDR_HI(x) ((x) << 0)
+# define SQ_VTXC_STRIDE(x) ((x) << 8)
+# define SQ_VTXC_ENDIAN_SWAP(x) ((x) << 30)
+# define SQ_ENDIAN_NONE 0
+# define SQ_ENDIAN_8IN16 1
+# define SQ_ENDIAN_8IN32 2
+#define SQ_VTX_CONSTANT_WORD3_0 0x3000C
+# define SQ_VTCX_SEL_X(x) ((x) << 3)
+# define SQ_VTCX_SEL_Y(x) ((x) << 6)
+# define SQ_VTCX_SEL_Z(x) ((x) << 9)
+# define SQ_VTCX_SEL_W(x) ((x) << 12)
+#define SQ_VTX_CONSTANT_WORD4_0 0x30010
+#define SQ_VTX_CONSTANT_WORD5_0 0x30014
+#define SQ_VTX_CONSTANT_WORD6_0 0x30018
+#define SQ_VTX_CONSTANT_WORD7_0 0x3001c
+
/* cayman 3D regs */
#define CAYMAN_VGT_OFFCHIP_LDS_BASE 0x89B0
#define CAYMAN_DB_EQAA 0x28804
@@ -1122,6 +1165,7 @@
#define CAYMAN_PA_SC_AA_CONFIG 0x28BE0
#define CAYMAN_MSAA_NUM_SAMPLES_SHIFT 0
#define CAYMAN_MSAA_NUM_SAMPLES_MASK 0x7
+#define CAYMAN_SX_SCATTER_EXPORT_BASE 0x28358
/* cayman packet3 addition */
#define CAYMAN_PACKET3_DEALLOC_STATE 0x14
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 559dbd412906..0e5799857465 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -24,6 +24,7 @@
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "drmP.h"
#include "radeon.h"
#include "radeon_asic.h"
@@ -39,6 +40,8 @@ extern int evergreen_mc_wait_for_idle(struct radeon_device *rdev);
extern void evergreen_mc_program(struct radeon_device *rdev);
extern void evergreen_irq_suspend(struct radeon_device *rdev);
extern int evergreen_mc_init(struct radeon_device *rdev);
+extern void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev);
+extern void evergreen_pcie_gen2_enable(struct radeon_device *rdev);
#define EVERGREEN_PFP_UCODE_SIZE 1120
#define EVERGREEN_PM4_UCODE_SIZE 1376
@@ -259,8 +262,11 @@ int ni_mc_load_microcode(struct radeon_device *rdev)
WREG32(MC_SEQ_SUP_CNTL, 0x00000001);
/* wait for training to complete */
- while (!(RREG32(MC_IO_PAD_CNTL_D0) & MEM_FALL_OUT_CMD))
- udelay(10);
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (RREG32(MC_IO_PAD_CNTL_D0) & MEM_FALL_OUT_CMD)
+ break;
+ udelay(1);
+ }
if (running)
WREG32(MC_SHARED_BLACKOUT_CNTL, blackout);
@@ -568,36 +574,6 @@ static u32 cayman_get_tile_pipe_to_backend_map(struct radeon_device *rdev,
return backend_map;
}
-static void cayman_program_channel_remap(struct radeon_device *rdev)
-{
- u32 tcp_chan_steer_lo, tcp_chan_steer_hi, mc_shared_chremap, tmp;
-
- tmp = RREG32(MC_SHARED_CHMAP);
- switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) {
- case 0:
- case 1:
- case 2:
- case 3:
- default:
- /* default mapping */
- mc_shared_chremap = 0x00fac688;
- break;
- }
-
- switch (rdev->family) {
- case CHIP_CAYMAN:
- default:
- //tcp_chan_steer_lo = 0x54763210
- tcp_chan_steer_lo = 0x76543210;
- tcp_chan_steer_hi = 0x0000ba98;
- break;
- }
-
- WREG32(TCP_CHAN_STEER_LO, tcp_chan_steer_lo);
- WREG32(TCP_CHAN_STEER_HI, tcp_chan_steer_hi);
- WREG32(MC_SHARED_CHREMAP, mc_shared_chremap);
-}
-
static u32 cayman_get_disable_mask_per_asic(struct radeon_device *rdev,
u32 disable_mask_per_se,
u32 max_disable_mask_per_se,
@@ -669,6 +645,8 @@ static void cayman_gpu_init(struct radeon_device *rdev)
WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
+ evergreen_fix_pci_max_read_req_size(rdev);
+
mc_shared_chmap = RREG32(MC_SHARED_CHMAP);
mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG);
@@ -833,13 +811,12 @@ static void cayman_gpu_init(struct radeon_device *rdev)
rdev->config.cayman.tile_config |=
((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12;
+ rdev->config.cayman.backend_map = gb_backend_map;
WREG32(GB_BACKEND_MAP, gb_backend_map);
WREG32(GB_ADDR_CONFIG, gb_addr_config);
WREG32(DMIF_ADDR_CONFIG, gb_addr_config);
WREG32(HDP_ADDR_CONFIG, gb_addr_config);
- cayman_program_channel_remap(rdev);
-
/* primary versions */
WREG32(CC_RB_BACKEND_DISABLE, cc_rb_backend_disable);
WREG32(CC_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable);
@@ -959,7 +936,7 @@ int cayman_pcie_gart_enable(struct radeon_device *rdev)
{
int r;
- if (rdev->gart.table.vram.robj == NULL) {
+ if (rdev->gart.robj == NULL) {
dev_err(rdev->dev, "No VRAM object for PCIE GART.\n");
return -EINVAL;
}
@@ -995,14 +972,15 @@ int cayman_pcie_gart_enable(struct radeon_device *rdev)
WREG32(VM_CONTEXT1_CNTL, 0);
cayman_pcie_gart_tlb_flush(rdev);
+ DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
+ (unsigned)(rdev->mc.gtt_size >> 20),
+ (unsigned long long)rdev->gart.table_addr);
rdev->gart.ready = true;
return 0;
}
void cayman_pcie_gart_disable(struct radeon_device *rdev)
{
- int r;
-
/* Disable all tables */
WREG32(VM_CONTEXT0_CNTL, 0);
WREG32(VM_CONTEXT1_CNTL, 0);
@@ -1018,14 +996,7 @@ void cayman_pcie_gart_disable(struct radeon_device *rdev)
WREG32(VM_L2_CNTL2, 0);
WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY |
L2_CACHE_BIGK_FRAGMENT_SIZE(6));
- if (rdev->gart.table.vram.robj) {
- r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
- if (likely(r == 0)) {
- radeon_bo_kunmap(rdev->gart.table.vram.robj);
- radeon_bo_unpin(rdev->gart.table.vram.robj);
- radeon_bo_unreserve(rdev->gart.table.vram.robj);
- }
- }
+ radeon_gart_table_vram_unpin(rdev);
}
void cayman_pcie_gart_fini(struct radeon_device *rdev)
@@ -1158,6 +1129,7 @@ int cayman_cp_resume(struct radeon_device *rdev)
SOFT_RESET_PA |
SOFT_RESET_SH |
SOFT_RESET_VGT |
+ SOFT_RESET_SPI |
SOFT_RESET_SX));
RREG32(GRBM_SOFT_RESET);
mdelay(15);
@@ -1182,7 +1154,8 @@ int cayman_cp_resume(struct radeon_device *rdev)
/* Initialize the ring buffer's read and write pointers */
WREG32(CP_RB0_CNTL, tmp | RB_RPTR_WR_ENA);
- WREG32(CP_RB0_WPTR, 0);
+ rdev->cp.wptr = 0;
+ WREG32(CP_RB0_WPTR, rdev->cp.wptr);
/* set the wb address wether it's enabled or not */
WREG32(CP_RB0_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC);
@@ -1202,7 +1175,6 @@ int cayman_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB0_BASE, rdev->cp.gpu_addr >> 8);
rdev->cp.rptr = RREG32(CP_RB0_RPTR);
- rdev->cp.wptr = RREG32(CP_RB0_WPTR);
/* ring1 - compute only */
/* Set ring buffer size */
@@ -1215,7 +1187,8 @@ int cayman_cp_resume(struct radeon_device *rdev)
/* Initialize the ring buffer's read and write pointers */
WREG32(CP_RB1_CNTL, tmp | RB_RPTR_WR_ENA);
- WREG32(CP_RB1_WPTR, 0);
+ rdev->cp1.wptr = 0;
+ WREG32(CP_RB1_WPTR, rdev->cp1.wptr);
/* set the wb address wether it's enabled or not */
WREG32(CP_RB1_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP1_RPTR_OFFSET) & 0xFFFFFFFC);
@@ -1227,7 +1200,6 @@ int cayman_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB1_BASE, rdev->cp1.gpu_addr >> 8);
rdev->cp1.rptr = RREG32(CP_RB1_RPTR);
- rdev->cp1.wptr = RREG32(CP_RB1_WPTR);
/* ring2 - compute only */
/* Set ring buffer size */
@@ -1240,7 +1212,8 @@ int cayman_cp_resume(struct radeon_device *rdev)
/* Initialize the ring buffer's read and write pointers */
WREG32(CP_RB2_CNTL, tmp | RB_RPTR_WR_ENA);
- WREG32(CP_RB2_WPTR, 0);
+ rdev->cp2.wptr = 0;
+ WREG32(CP_RB2_WPTR, rdev->cp2.wptr);
/* set the wb address wether it's enabled or not */
WREG32(CP_RB2_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP2_RPTR_OFFSET) & 0xFFFFFFFC);
@@ -1252,7 +1225,6 @@ int cayman_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB2_BASE, rdev->cp2.gpu_addr >> 8);
rdev->cp2.rptr = RREG32(CP_RB2_RPTR);
- rdev->cp2.wptr = RREG32(CP_RB2_WPTR);
/* start the rings */
cayman_cp_start(rdev);
@@ -1368,6 +1340,9 @@ static int cayman_startup(struct radeon_device *rdev)
{
int r;
+ /* enable pcie gen2 link */
+ evergreen_pcie_gen2_enable(rdev);
+
if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw || !rdev->mc_fw) {
r = ni_init_microcode(rdev);
if (r) {
@@ -1381,6 +1356,10 @@ static int cayman_startup(struct radeon_device *rdev)
return r;
}
+ r = r600_vram_scratch_init(rdev);
+ if (r)
+ return r;
+
evergreen_mc_program(rdev);
r = cayman_pcie_gart_enable(rdev);
if (r)
@@ -1389,7 +1368,7 @@ static int cayman_startup(struct radeon_device *rdev)
r = evergreen_blit_init(rdev);
if (r) {
- evergreen_blit_fini(rdev);
+ r600_blit_fini(rdev);
rdev->asic->copy = NULL;
dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
@@ -1450,21 +1429,13 @@ int cayman_resume(struct radeon_device *rdev)
int cayman_suspend(struct radeon_device *rdev)
{
- int r;
-
/* FIXME: we should wait for ring to be empty */
cayman_cp_enable(rdev, false);
rdev->cp.ready = false;
evergreen_irq_suspend(rdev);
radeon_wb_disable(rdev);
cayman_pcie_gart_disable(rdev);
-
- /* unpin shaders bo */
- r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
- if (likely(r == 0)) {
- radeon_bo_unpin(rdev->r600_blit.shader_obj);
- radeon_bo_unreserve(rdev->r600_blit.shader_obj);
- }
+ r600_blit_suspend(rdev);
return 0;
}
@@ -1577,13 +1548,14 @@ int cayman_init(struct radeon_device *rdev)
void cayman_fini(struct radeon_device *rdev)
{
- evergreen_blit_fini(rdev);
+ r600_blit_fini(rdev);
cayman_cp_fini(rdev);
r600_irq_fini(rdev);
radeon_wb_fini(rdev);
radeon_ib_pool_fini(rdev);
radeon_irq_kms_fini(rdev);
cayman_pcie_gart_fini(rdev);
+ r600_vram_scratch_fini(rdev);
radeon_gem_fini(rdev);
radeon_fence_driver_fini(rdev);
radeon_bo_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index f2204cb1ccdf..ad158ea49901 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -41,6 +41,7 @@
#include <linux/firmware.h>
#include <linux/platform_device.h>
+#include <linux/module.h>
#include "r100_reg_safe.h"
#include "rn50_reg_safe.h"
@@ -68,6 +69,108 @@ MODULE_FIRMWARE(FIRMWARE_R520);
* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
*/
+int r100_reloc_pitch_offset(struct radeon_cs_parser *p,
+ struct radeon_cs_packet *pkt,
+ unsigned idx,
+ unsigned reg)
+{
+ int r;
+ u32 tile_flags = 0;
+ u32 tmp;
+ struct radeon_cs_reloc *reloc;
+ u32 value;
+
+ r = r100_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("No reloc for ib[%d]=0x%04X\n",
+ idx, reg);
+ r100_cs_dump_packet(p, pkt);
+ return r;
+ }
+ value = radeon_get_ib_value(p, idx);
+ tmp = value & 0x003fffff;
+ tmp += (((u32)reloc->lobj.gpu_offset) >> 10);
+
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ tile_flags |= RADEON_DST_TILE_MACRO;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) {
+ if (reg == RADEON_SRC_PITCH_OFFSET) {
+ DRM_ERROR("Cannot src blit from microtiled surface\n");
+ r100_cs_dump_packet(p, pkt);
+ return -EINVAL;
+ }
+ tile_flags |= RADEON_DST_TILE_MICRO;
+ }
+
+ tmp |= tile_flags;
+ p->ib->ptr[idx] = (value & 0x3fc00000) | tmp;
+ return 0;
+}
+
+int r100_packet3_load_vbpntr(struct radeon_cs_parser *p,
+ struct radeon_cs_packet *pkt,
+ int idx)
+{
+ unsigned c, i;
+ struct radeon_cs_reloc *reloc;
+ struct r100_cs_track *track;
+ int r = 0;
+ volatile uint32_t *ib;
+ u32 idx_value;
+
+ ib = p->ib->ptr;
+ track = (struct r100_cs_track *)p->track;
+ c = radeon_get_ib_value(p, idx++) & 0x1F;
+ if (c > 16) {
+ DRM_ERROR("Only 16 vertex buffers are allowed %d\n",
+ pkt->opcode);
+ r100_cs_dump_packet(p, pkt);
+ return -EINVAL;
+ }
+ track->num_arrays = c;
+ for (i = 0; i < (c - 1); i+=2, idx+=3) {
+ r = r100_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("No reloc for packet3 %d\n",
+ pkt->opcode);
+ r100_cs_dump_packet(p, pkt);
+ return r;
+ }
+ idx_value = radeon_get_ib_value(p, idx);
+ ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset);
+
+ track->arrays[i + 0].esize = idx_value >> 8;
+ track->arrays[i + 0].robj = reloc->robj;
+ track->arrays[i + 0].esize &= 0x7F;
+ r = r100_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("No reloc for packet3 %d\n",
+ pkt->opcode);
+ r100_cs_dump_packet(p, pkt);
+ return r;
+ }
+ ib[idx+2] = radeon_get_ib_value(p, idx + 2) + ((u32)reloc->lobj.gpu_offset);
+ track->arrays[i + 1].robj = reloc->robj;
+ track->arrays[i + 1].esize = idx_value >> 24;
+ track->arrays[i + 1].esize &= 0x7F;
+ }
+ if (c & 1) {
+ r = r100_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("No reloc for packet3 %d\n",
+ pkt->opcode);
+ r100_cs_dump_packet(p, pkt);
+ return r;
+ }
+ idx_value = radeon_get_ib_value(p, idx);
+ ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset);
+ track->arrays[i + 0].robj = reloc->robj;
+ track->arrays[i + 0].esize = idx_value >> 8;
+ track->arrays[i + 0].esize &= 0x7F;
+ }
+ return r;
+}
+
void r100_pre_page_flip(struct radeon_device *rdev, int crtc)
{
/* enable the pflip int */
@@ -434,6 +537,7 @@ void r100_hpd_init(struct radeon_device *rdev)
default:
break;
}
+ radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
}
if (rdev->irq.installed)
r100_irq_set(rdev);
@@ -474,7 +578,7 @@ int r100_pci_gart_init(struct radeon_device *rdev)
{
int r;
- if (rdev->gart.table.ram.ptr) {
+ if (rdev->gart.ptr) {
WARN(1, "R100 PCI GART already initialized\n");
return 0;
}
@@ -513,6 +617,9 @@ int r100_pci_gart_enable(struct radeon_device *rdev)
tmp = RREG32(RADEON_AIC_CNTL) | RADEON_PCIGART_TRANSLATE_EN;
WREG32(RADEON_AIC_CNTL, tmp);
r100_pci_gart_tlb_flush(rdev);
+ DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
+ (unsigned)(rdev->mc.gtt_size >> 20),
+ (unsigned long long)rdev->gart.table_addr);
rdev->gart.ready = true;
return 0;
}
@@ -530,10 +637,12 @@ void r100_pci_gart_disable(struct radeon_device *rdev)
int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
{
+ u32 *gtt = rdev->gart.ptr;
+
if (i < 0 || i > rdev->gart.num_gpu_pages) {
return -EINVAL;
}
- rdev->gart.table.ram.ptr[i] = cpu_to_le32(lower_32_bits(addr));
+ gtt[i] = cpu_to_le32(lower_32_bits(addr));
return 0;
}
@@ -588,7 +697,7 @@ void r100_irq_disable(struct radeon_device *rdev)
WREG32(R_000044_GEN_INT_STATUS, tmp);
}
-static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
+static uint32_t r100_irq_ack(struct radeon_device *rdev)
{
uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS);
uint32_t irq_mask = RADEON_SW_INT_TEST |
@@ -721,11 +830,11 @@ void r100_fence_ring_emit(struct radeon_device *rdev,
int r100_copy_blit(struct radeon_device *rdev,
uint64_t src_offset,
uint64_t dst_offset,
- unsigned num_pages,
+ unsigned num_gpu_pages,
struct radeon_fence *fence)
{
uint32_t cur_pages;
- uint32_t stride_bytes = PAGE_SIZE;
+ uint32_t stride_bytes = RADEON_GPU_PAGE_SIZE;
uint32_t pitch;
uint32_t stride_pixels;
unsigned ndw;
@@ -737,7 +846,7 @@ int r100_copy_blit(struct radeon_device *rdev,
/* radeon pitch is /64 */
pitch = stride_bytes / 64;
stride_pixels = stride_bytes / 4;
- num_loops = DIV_ROUND_UP(num_pages, 8191);
+ num_loops = DIV_ROUND_UP(num_gpu_pages, 8191);
/* Ask for enough room for blit + flush + fence */
ndw = 64 + (10 * num_loops);
@@ -746,12 +855,12 @@ int r100_copy_blit(struct radeon_device *rdev,
DRM_ERROR("radeon: moving bo (%d) asking for %u dw.\n", r, ndw);
return -EINVAL;
}
- while (num_pages > 0) {
- cur_pages = num_pages;
+ while (num_gpu_pages > 0) {
+ cur_pages = num_gpu_pages;
if (cur_pages > 8191) {
cur_pages = 8191;
}
- num_pages -= cur_pages;
+ num_gpu_pages -= cur_pages;
/* pages are in Y direction - height
page width in X direction - width */
@@ -773,8 +882,8 @@ int r100_copy_blit(struct radeon_device *rdev,
radeon_ring_write(rdev, (0x1fff) | (0x1fff << 16));
radeon_ring_write(rdev, 0);
radeon_ring_write(rdev, (0x1fff) | (0x1fff << 16));
- radeon_ring_write(rdev, num_pages);
- radeon_ring_write(rdev, num_pages);
+ radeon_ring_write(rdev, num_gpu_pages);
+ radeon_ring_write(rdev, num_gpu_pages);
radeon_ring_write(rdev, cur_pages | (stride_pixels << 16));
}
radeon_ring_write(rdev, PACKET0(RADEON_DSTCACHE_CTLSTAT, 0));
@@ -990,7 +1099,8 @@ int r100_cp_init(struct radeon_device *rdev, unsigned ring_size)
/* Force read & write ptr to 0 */
WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_RPTR_WR_ENA | RADEON_RB_NO_UPDATE);
WREG32(RADEON_CP_RB_RPTR_WR, 0);
- WREG32(RADEON_CP_RB_WPTR, 0);
+ rdev->cp.wptr = 0;
+ WREG32(RADEON_CP_RB_WPTR, rdev->cp.wptr);
/* set the wb address whether it's enabled or not */
WREG32(R_00070C_CP_RB_RPTR_ADDR,
@@ -1007,9 +1117,6 @@ int r100_cp_init(struct radeon_device *rdev, unsigned ring_size)
WREG32(RADEON_CP_RB_CNTL, tmp);
udelay(10);
rdev->cp.rptr = RREG32(RADEON_CP_RB_RPTR);
- rdev->cp.wptr = RREG32(RADEON_CP_RB_WPTR);
- /* protect against crazy HW on resume */
- rdev->cp.wptr &= rdev->cp.ptr_mask;
/* Set cp mode to bus mastering & enable cp*/
WREG32(RADEON_CP_CSQ_MODE,
REG_SET(RADEON_INDIRECT2_START, indirect2_start) |
@@ -3149,7 +3256,7 @@ void r100_bandwidth_update(struct radeon_device *rdev)
}
}
-static inline void r100_cs_track_texture_print(struct r100_cs_track_texture *t)
+static void r100_cs_track_texture_print(struct r100_cs_track_texture *t)
{
DRM_ERROR("pitch %d\n", t->pitch);
DRM_ERROR("use_pitch %d\n", t->use_pitch);
@@ -3967,3 +4074,43 @@ int r100_init(struct radeon_device *rdev)
}
return 0;
}
+
+uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg)
+{
+ if (reg < rdev->rmmio_size)
+ return readl(((void __iomem *)rdev->rmmio) + reg);
+ else {
+ writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
+ return readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
+ }
+}
+
+void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
+{
+ if (reg < rdev->rmmio_size)
+ writel(v, ((void __iomem *)rdev->rmmio) + reg);
+ else {
+ writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
+ writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
+ }
+}
+
+u32 r100_io_rreg(struct radeon_device *rdev, u32 reg)
+{
+ if (reg < rdev->rio_mem_size)
+ return ioread32(rdev->rio_mem + reg);
+ else {
+ iowrite32(reg, rdev->rio_mem + RADEON_MM_INDEX);
+ return ioread32(rdev->rio_mem + RADEON_MM_DATA);
+ }
+}
+
+void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v)
+{
+ if (reg < rdev->rio_mem_size)
+ iowrite32(v, rdev->rio_mem + reg);
+ else {
+ iowrite32(reg, rdev->rio_mem + RADEON_MM_INDEX);
+ iowrite32(v, rdev->rio_mem + RADEON_MM_DATA);
+ }
+}
diff --git a/drivers/gpu/drm/radeon/r100_track.h b/drivers/gpu/drm/radeon/r100_track.h
index 686f9dc5d4bd..6a603b378adb 100644
--- a/drivers/gpu/drm/radeon/r100_track.h
+++ b/drivers/gpu/drm/radeon/r100_track.h
@@ -92,106 +92,10 @@ int r200_packet0_check(struct radeon_cs_parser *p,
struct radeon_cs_packet *pkt,
unsigned idx, unsigned reg);
-
-
-static inline int r100_reloc_pitch_offset(struct radeon_cs_parser *p,
- struct radeon_cs_packet *pkt,
- unsigned idx,
- unsigned reg)
-{
- int r;
- u32 tile_flags = 0;
- u32 tmp;
- struct radeon_cs_reloc *reloc;
- u32 value;
-
- r = r100_cs_packet_next_reloc(p, &reloc);
- if (r) {
- DRM_ERROR("No reloc for ib[%d]=0x%04X\n",
- idx, reg);
- r100_cs_dump_packet(p, pkt);
- return r;
- }
- value = radeon_get_ib_value(p, idx);
- tmp = value & 0x003fffff;
- tmp += (((u32)reloc->lobj.gpu_offset) >> 10);
-
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
- tile_flags |= RADEON_DST_TILE_MACRO;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) {
- if (reg == RADEON_SRC_PITCH_OFFSET) {
- DRM_ERROR("Cannot src blit from microtiled surface\n");
- r100_cs_dump_packet(p, pkt);
- return -EINVAL;
- }
- tile_flags |= RADEON_DST_TILE_MICRO;
- }
-
- tmp |= tile_flags;
- p->ib->ptr[idx] = (value & 0x3fc00000) | tmp;
- return 0;
-}
-
-static inline int r100_packet3_load_vbpntr(struct radeon_cs_parser *p,
- struct radeon_cs_packet *pkt,
- int idx)
-{
- unsigned c, i;
- struct radeon_cs_reloc *reloc;
- struct r100_cs_track *track;
- int r = 0;
- volatile uint32_t *ib;
- u32 idx_value;
-
- ib = p->ib->ptr;
- track = (struct r100_cs_track *)p->track;
- c = radeon_get_ib_value(p, idx++) & 0x1F;
- if (c > 16) {
- DRM_ERROR("Only 16 vertex buffers are allowed %d\n",
- pkt->opcode);
- r100_cs_dump_packet(p, pkt);
- return -EINVAL;
- }
- track->num_arrays = c;
- for (i = 0; i < (c - 1); i+=2, idx+=3) {
- r = r100_cs_packet_next_reloc(p, &reloc);
- if (r) {
- DRM_ERROR("No reloc for packet3 %d\n",
- pkt->opcode);
- r100_cs_dump_packet(p, pkt);
- return r;
- }
- idx_value = radeon_get_ib_value(p, idx);
- ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset);
-
- track->arrays[i + 0].esize = idx_value >> 8;
- track->arrays[i + 0].robj = reloc->robj;
- track->arrays[i + 0].esize &= 0x7F;
- r = r100_cs_packet_next_reloc(p, &reloc);
- if (r) {
- DRM_ERROR("No reloc for packet3 %d\n",
- pkt->opcode);
- r100_cs_dump_packet(p, pkt);
- return r;
- }
- ib[idx+2] = radeon_get_ib_value(p, idx + 2) + ((u32)reloc->lobj.gpu_offset);
- track->arrays[i + 1].robj = reloc->robj;
- track->arrays[i + 1].esize = idx_value >> 24;
- track->arrays[i + 1].esize &= 0x7F;
- }
- if (c & 1) {
- r = r100_cs_packet_next_reloc(p, &reloc);
- if (r) {
- DRM_ERROR("No reloc for packet3 %d\n",
- pkt->opcode);
- r100_cs_dump_packet(p, pkt);
- return r;
- }
- idx_value = radeon_get_ib_value(p, idx);
- ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset);
- track->arrays[i + 0].robj = reloc->robj;
- track->arrays[i + 0].esize = idx_value >> 8;
- track->arrays[i + 0].esize &= 0x7F;
- }
- return r;
-}
+int r100_reloc_pitch_offset(struct radeon_cs_parser *p,
+ struct radeon_cs_packet *pkt,
+ unsigned idx,
+ unsigned reg);
+int r100_packet3_load_vbpntr(struct radeon_cs_parser *p,
+ struct radeon_cs_packet *pkt,
+ int idx);
diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c
index f24058300413..a1f3ba063c2d 100644
--- a/drivers/gpu/drm/radeon/r200.c
+++ b/drivers/gpu/drm/radeon/r200.c
@@ -84,7 +84,7 @@ static int r200_get_vtx_size_0(uint32_t vtx_fmt_0)
int r200_copy_dma(struct radeon_device *rdev,
uint64_t src_offset,
uint64_t dst_offset,
- unsigned num_pages,
+ unsigned num_gpu_pages,
struct radeon_fence *fence)
{
uint32_t size;
@@ -93,7 +93,7 @@ int r200_copy_dma(struct radeon_device *rdev,
int r = 0;
/* radeon pitch is /64 */
- size = num_pages << PAGE_SHIFT;
+ size = num_gpu_pages << RADEON_GPU_PAGE_SHIFT;
num_loops = DIV_ROUND_UP(size, 0x1FFFFF);
r = radeon_ring_lock(rdev, num_loops * 4 + 64);
if (r) {
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
index 55a7f190027e..c93bc64707e1 100644
--- a/drivers/gpu/drm/radeon/r300.c
+++ b/drivers/gpu/drm/radeon/r300.c
@@ -74,7 +74,7 @@ void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev)
int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
{
- void __iomem *ptr = (void *)rdev->gart.table.vram.ptr;
+ void __iomem *ptr = rdev->gart.ptr;
if (i < 0 || i > rdev->gart.num_gpu_pages) {
return -EINVAL;
@@ -93,7 +93,7 @@ int rv370_pcie_gart_init(struct radeon_device *rdev)
{
int r;
- if (rdev->gart.table.vram.robj) {
+ if (rdev->gart.robj) {
WARN(1, "RV370 PCIE GART already initialized\n");
return 0;
}
@@ -116,7 +116,7 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev)
uint32_t tmp;
int r;
- if (rdev->gart.table.vram.robj == NULL) {
+ if (rdev->gart.robj == NULL) {
dev_err(rdev->dev, "No VRAM object for PCIE GART.\n");
return -EINVAL;
}
@@ -144,8 +144,9 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev)
tmp |= RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD;
WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp);
rv370_pcie_gart_tlb_flush(rdev);
- DRM_INFO("PCIE GART of %uM enabled (table at 0x%08X).\n",
- (unsigned)(rdev->mc.gtt_size >> 20), table_addr);
+ DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
+ (unsigned)(rdev->mc.gtt_size >> 20),
+ (unsigned long long)table_addr);
rdev->gart.ready = true;
return 0;
}
@@ -153,7 +154,6 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev)
void rv370_pcie_gart_disable(struct radeon_device *rdev)
{
u32 tmp;
- int r;
WREG32_PCIE(RADEON_PCIE_TX_GART_START_LO, 0);
WREG32_PCIE(RADEON_PCIE_TX_GART_END_LO, 0);
@@ -162,14 +162,7 @@ void rv370_pcie_gart_disable(struct radeon_device *rdev)
tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL);
tmp |= RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD;
WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp & ~RADEON_PCIE_TX_GART_EN);
- if (rdev->gart.table.vram.robj) {
- r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
- if (likely(r == 0)) {
- radeon_bo_kunmap(rdev->gart.table.vram.robj);
- radeon_bo_unpin(rdev->gart.table.vram.robj);
- radeon_bo_unreserve(rdev->gart.table.vram.robj);
- }
- }
+ radeon_gart_table_vram_unpin(rdev);
}
void rv370_pcie_gart_fini(struct radeon_device *rdev)
@@ -708,16 +701,21 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
return r;
}
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
- tile_flags |= R300_TXO_MACRO_TILE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
- tile_flags |= R300_TXO_MICRO_TILE;
- else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
- tile_flags |= R300_TXO_MICRO_TILE_SQUARE;
-
- tmp = idx_value + ((u32)reloc->lobj.gpu_offset);
- tmp |= tile_flags;
- ib[idx] = tmp;
+ if (p->keep_tiling_flags) {
+ ib[idx] = (idx_value & 31) | /* keep the 1st 5 bits */
+ ((idx_value & ~31) + (u32)reloc->lobj.gpu_offset);
+ } else {
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ tile_flags |= R300_TXO_MACRO_TILE;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ tile_flags |= R300_TXO_MICRO_TILE;
+ else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
+ tile_flags |= R300_TXO_MICRO_TILE_SQUARE;
+
+ tmp = idx_value + ((u32)reloc->lobj.gpu_offset);
+ tmp |= tile_flags;
+ ib[idx] = tmp;
+ }
track->textures[i].robj = reloc->robj;
track->tex_dirty = true;
break;
@@ -767,24 +765,26 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
/* RB3D_COLORPITCH1 */
/* RB3D_COLORPITCH2 */
/* RB3D_COLORPITCH3 */
- r = r100_cs_packet_next_reloc(p, &reloc);
- if (r) {
- DRM_ERROR("No reloc for ib[%d]=0x%04X\n",
- idx, reg);
- r100_cs_dump_packet(p, pkt);
- return r;
- }
+ if (!p->keep_tiling_flags) {
+ r = r100_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("No reloc for ib[%d]=0x%04X\n",
+ idx, reg);
+ r100_cs_dump_packet(p, pkt);
+ return r;
+ }
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
- tile_flags |= R300_COLOR_TILE_ENABLE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
- tile_flags |= R300_COLOR_MICROTILE_ENABLE;
- else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
- tile_flags |= R300_COLOR_MICROTILE_SQUARE_ENABLE;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ tile_flags |= R300_COLOR_TILE_ENABLE;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ tile_flags |= R300_COLOR_MICROTILE_ENABLE;
+ else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
+ tile_flags |= R300_COLOR_MICROTILE_SQUARE_ENABLE;
- tmp = idx_value & ~(0x7 << 16);
- tmp |= tile_flags;
- ib[idx] = tmp;
+ tmp = idx_value & ~(0x7 << 16);
+ tmp |= tile_flags;
+ ib[idx] = tmp;
+ }
i = (reg - 0x4E38) >> 2;
track->cb[i].pitch = idx_value & 0x3FFE;
switch (((idx_value >> 21) & 0xF)) {
@@ -850,25 +850,26 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
break;
case 0x4F24:
/* ZB_DEPTHPITCH */
- r = r100_cs_packet_next_reloc(p, &reloc);
- if (r) {
- DRM_ERROR("No reloc for ib[%d]=0x%04X\n",
- idx, reg);
- r100_cs_dump_packet(p, pkt);
- return r;
- }
-
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
- tile_flags |= R300_DEPTHMACROTILE_ENABLE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
- tile_flags |= R300_DEPTHMICROTILE_TILED;
- else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
- tile_flags |= R300_DEPTHMICROTILE_TILED_SQUARE;
+ if (!p->keep_tiling_flags) {
+ r = r100_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("No reloc for ib[%d]=0x%04X\n",
+ idx, reg);
+ r100_cs_dump_packet(p, pkt);
+ return r;
+ }
- tmp = idx_value & ~(0x7 << 16);
- tmp |= tile_flags;
- ib[idx] = tmp;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ tile_flags |= R300_DEPTHMACROTILE_ENABLE;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ tile_flags |= R300_DEPTHMICROTILE_TILED;
+ else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
+ tile_flags |= R300_DEPTHMICROTILE_TILED_SQUARE;
+ tmp = idx_value & ~(0x7 << 16);
+ tmp |= tile_flags;
+ ib[idx] = tmp;
+ }
track->zb.pitch = idx_value & 0x3FFC;
track->zb_dirty = true;
break;
diff --git a/drivers/gpu/drm/radeon/r300_cmdbuf.c b/drivers/gpu/drm/radeon/r300_cmdbuf.c
index c5c2742e4140..1fe98b421c9b 100644
--- a/drivers/gpu/drm/radeon/r300_cmdbuf.c
+++ b/drivers/gpu/drm/radeon/r300_cmdbuf.c
@@ -791,7 +791,7 @@ static __inline__ int r300_emit_packet3(drm_radeon_private_t *dev_priv,
/**
* Emit the sequence to pacify R300.
*/
-static __inline__ void r300_pacify(drm_radeon_private_t *dev_priv)
+static void r300_pacify(drm_radeon_private_t *dev_priv)
{
uint32_t cache_z, cache_3d, cache_2d;
RING_LOCALS;
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index bc54b26cb32f..9cdda0b3b081 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -29,6 +29,7 @@
#include <linux/seq_file.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
+#include <linux/module.h>
#include "drmP.h"
#include "radeon_drm.h"
#include "radeon.h"
@@ -287,24 +288,6 @@ void r600_pm_get_dynpm_state(struct radeon_device *rdev)
pcie_lanes);
}
-static int r600_pm_get_type_index(struct radeon_device *rdev,
- enum radeon_pm_state_type ps_type,
- int instance)
-{
- int i;
- int found_instance = -1;
-
- for (i = 0; i < rdev->pm.num_power_states; i++) {
- if (rdev->pm.power_state[i].type == ps_type) {
- found_instance++;
- if (found_instance == instance)
- return i;
- }
- }
- /* return default if no match */
- return rdev->pm.default_power_state_index;
-}
-
void rs780_pm_init_profile(struct radeon_device *rdev)
{
if (rdev->pm.num_power_states == 2) {
@@ -420,6 +403,8 @@ void rs780_pm_init_profile(struct radeon_device *rdev)
void r600_pm_init_profile(struct radeon_device *rdev)
{
+ int idx;
+
if (rdev->family == CHIP_R600) {
/* XXX */
/* default */
@@ -501,81 +486,43 @@ void r600_pm_init_profile(struct radeon_device *rdev)
rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2;
/* low sh */
- if (rdev->flags & RADEON_IS_MOBILITY) {
- rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0);
- rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0);
- rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
- rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
- } else {
- rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
- rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
- rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
- rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
- }
+ if (rdev->flags & RADEON_IS_MOBILITY)
+ idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0);
+ else
+ idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
+ rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
/* mid sh */
- if (rdev->flags & RADEON_IS_MOBILITY) {
- rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0);
- rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0);
- rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0;
- rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 1;
- } else {
- rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
- rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
- rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0;
- rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 1;
- }
+ rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 1;
/* high sh */
- rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
- rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
+ idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
+ rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 2;
/* low mh */
- if (rdev->flags & RADEON_IS_MOBILITY) {
- rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1);
- rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1);
- rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
- rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
- } else {
- rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1);
- rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1);
- rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
- rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
- }
+ if (rdev->flags & RADEON_IS_MOBILITY)
+ idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1);
+ else
+ idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1);
+ rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
/* mid mh */
- if (rdev->flags & RADEON_IS_MOBILITY) {
- rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1);
- rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1);
- rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0;
- rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 1;
- } else {
- rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1);
- rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1);
- rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0;
- rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 1;
- }
+ rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0;
+ rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 1;
/* high mh */
- rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1);
- rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx =
- r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1);
+ idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1);
+ rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = idx;
+ rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 2;
}
@@ -762,13 +709,14 @@ void r600_hpd_init(struct radeon_device *rdev)
struct drm_device *dev = rdev->ddev;
struct drm_connector *connector;
- if (ASIC_IS_DCE3(rdev)) {
- u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) | DC_HPDx_RX_INT_TIMER(0xfa);
- if (ASIC_IS_DCE32(rdev))
- tmp |= DC_HPDx_EN;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+ if (ASIC_IS_DCE3(rdev)) {
+ u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) | DC_HPDx_RX_INT_TIMER(0xfa);
+ if (ASIC_IS_DCE32(rdev))
+ tmp |= DC_HPDx_EN;
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
switch (radeon_connector->hpd.hpd) {
case RADEON_HPD_1:
WREG32(DC_HPD1_CONTROL, tmp);
@@ -798,10 +746,7 @@ void r600_hpd_init(struct radeon_device *rdev)
default:
break;
}
- }
- } else {
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ } else {
switch (radeon_connector->hpd.hpd) {
case RADEON_HPD_1:
WREG32(DC_HOT_PLUG_DETECT1_CONTROL, DC_HOT_PLUG_DETECTx_EN);
@@ -819,6 +764,7 @@ void r600_hpd_init(struct radeon_device *rdev)
break;
}
}
+ radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
}
if (rdev->irq.installed)
r600_irq_set(rdev);
@@ -896,7 +842,7 @@ void r600_pcie_gart_tlb_flush(struct radeon_device *rdev)
/* flush hdp cache so updates hit vram */
if ((rdev->family >= CHIP_RV770) && (rdev->family <= CHIP_RV740) &&
!(rdev->flags & RADEON_IS_AGP)) {
- void __iomem *ptr = (void *)rdev->gart.table.vram.ptr;
+ void __iomem *ptr = (void *)rdev->gart.ptr;
u32 tmp;
/* r7xx hw bug. write to HDP_DEBUG1 followed by fb read
@@ -931,7 +877,7 @@ int r600_pcie_gart_init(struct radeon_device *rdev)
{
int r;
- if (rdev->gart.table.vram.robj) {
+ if (rdev->gart.robj) {
WARN(1, "R600 PCIE GART already initialized\n");
return 0;
}
@@ -948,7 +894,7 @@ int r600_pcie_gart_enable(struct radeon_device *rdev)
u32 tmp;
int r, i;
- if (rdev->gart.table.vram.robj == NULL) {
+ if (rdev->gart.robj == NULL) {
dev_err(rdev->dev, "No VRAM object for PCIE GART.\n");
return -EINVAL;
}
@@ -993,6 +939,9 @@ int r600_pcie_gart_enable(struct radeon_device *rdev)
WREG32(VM_CONTEXT0_CNTL + (i * 4), 0);
r600_pcie_gart_tlb_flush(rdev);
+ DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
+ (unsigned)(rdev->mc.gtt_size >> 20),
+ (unsigned long long)rdev->gart.table_addr);
rdev->gart.ready = true;
return 0;
}
@@ -1000,7 +949,7 @@ int r600_pcie_gart_enable(struct radeon_device *rdev)
void r600_pcie_gart_disable(struct radeon_device *rdev)
{
u32 tmp;
- int i, r;
+ int i;
/* Disable all tables */
for (i = 0; i < 7; i++)
@@ -1027,14 +976,7 @@ void r600_pcie_gart_disable(struct radeon_device *rdev)
WREG32(MC_VM_L1_TLB_MCB_WR_SYS_CNTL, tmp);
WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp);
WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp);
- if (rdev->gart.table.vram.robj) {
- r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
- if (likely(r == 0)) {
- radeon_bo_kunmap(rdev->gart.table.vram.robj);
- radeon_bo_unpin(rdev->gart.table.vram.robj);
- radeon_bo_unreserve(rdev->gart.table.vram.robj);
- }
- }
+ radeon_gart_table_vram_unpin(rdev);
}
void r600_pcie_gart_fini(struct radeon_device *rdev)
@@ -1134,7 +1076,7 @@ static void r600_mc_program(struct radeon_device *rdev)
WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.vram_start >> 12);
WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.vram_end >> 12);
}
- WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0);
+ WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, rdev->vram_scratch.gpu_addr >> 12);
tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16;
tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF);
WREG32(MC_VM_FB_LOCATION, tmp);
@@ -1273,6 +1215,53 @@ int r600_mc_init(struct radeon_device *rdev)
return 0;
}
+int r600_vram_scratch_init(struct radeon_device *rdev)
+{
+ int r;
+
+ if (rdev->vram_scratch.robj == NULL) {
+ r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE,
+ PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM,
+ &rdev->vram_scratch.robj);
+ if (r) {
+ return r;
+ }
+ }
+
+ r = radeon_bo_reserve(rdev->vram_scratch.robj, false);
+ if (unlikely(r != 0))
+ return r;
+ r = radeon_bo_pin(rdev->vram_scratch.robj,
+ RADEON_GEM_DOMAIN_VRAM, &rdev->vram_scratch.gpu_addr);
+ if (r) {
+ radeon_bo_unreserve(rdev->vram_scratch.robj);
+ return r;
+ }
+ r = radeon_bo_kmap(rdev->vram_scratch.robj,
+ (void **)&rdev->vram_scratch.ptr);
+ if (r)
+ radeon_bo_unpin(rdev->vram_scratch.robj);
+ radeon_bo_unreserve(rdev->vram_scratch.robj);
+
+ return r;
+}
+
+void r600_vram_scratch_fini(struct radeon_device *rdev)
+{
+ int r;
+
+ if (rdev->vram_scratch.robj == NULL) {
+ return;
+ }
+ r = radeon_bo_reserve(rdev->vram_scratch.robj, false);
+ if (likely(r == 0)) {
+ radeon_bo_kunmap(rdev->vram_scratch.robj);
+ radeon_bo_unpin(rdev->vram_scratch.robj);
+ radeon_bo_unreserve(rdev->vram_scratch.robj);
+ }
+ radeon_bo_unref(&rdev->vram_scratch.robj);
+}
+
/* We doesn't check that the GPU really needs a reset we simply do the
* reset, it's up to the caller to determine if the GPU needs one. We
* might add an helper function to check that.
@@ -1662,6 +1651,7 @@ void r600_gpu_init(struct radeon_device *rdev)
R6XX_MAX_BACKENDS_MASK) >> 16)),
(cc_rb_backend_disable >> 16));
rdev->config.r600.tile_config = tiling_config;
+ rdev->config.r600.backend_map = backend_map;
tiling_config |= BACKEND_MAP(backend_map);
WREG32(GB_TILING_CONFIG, tiling_config);
WREG32(DCP_TILING_CONFIG, tiling_config & 0xffff);
@@ -2208,13 +2198,11 @@ int r600_cp_resume(struct radeon_device *rdev)
/* Initialize the ring buffer's read and write pointers */
WREG32(CP_RB_CNTL, tmp | RB_RPTR_WR_ENA);
WREG32(CP_RB_RPTR_WR, 0);
- WREG32(CP_RB_WPTR, 0);
+ rdev->cp.wptr = 0;
+ WREG32(CP_RB_WPTR, rdev->cp.wptr);
/* set the wb address whether it's enabled or not */
WREG32(CP_RB_RPTR_ADDR,
-#ifdef __BIG_ENDIAN
- RB_RPTR_SWAP(2) |
-#endif
((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC));
WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF);
WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF);
@@ -2233,7 +2221,6 @@ int r600_cp_resume(struct radeon_device *rdev)
WREG32(CP_DEBUG, (1 << 27) | (1 << 28));
rdev->cp.rptr = RREG32(CP_RB_RPTR);
- rdev->cp.wptr = RREG32(CP_RB_WPTR);
r600_cp_start(rdev);
rdev->cp.ready = true;
@@ -2330,6 +2317,14 @@ void r600_fence_ring_emit(struct radeon_device *rdev,
if (rdev->wb.use_event) {
u64 addr = rdev->wb.gpu_addr + R600_WB_EVENT_OFFSET +
(u64)(rdev->fence_drv.scratch_reg - rdev->scratch.reg_base);
+ /* flush read cache over gart */
+ radeon_ring_write(rdev, PACKET3(PACKET3_SURFACE_SYNC, 3));
+ radeon_ring_write(rdev, PACKET3_TC_ACTION_ENA |
+ PACKET3_VC_ACTION_ENA |
+ PACKET3_SH_ACTION_ENA);
+ radeon_ring_write(rdev, 0xFFFFFFFF);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 10); /* poll interval */
/* EVENT_WRITE_EOP - flush caches, send int */
radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
radeon_ring_write(rdev, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5));
@@ -2338,6 +2333,14 @@ void r600_fence_ring_emit(struct radeon_device *rdev,
radeon_ring_write(rdev, fence->seq);
radeon_ring_write(rdev, 0);
} else {
+ /* flush read cache over gart */
+ radeon_ring_write(rdev, PACKET3(PACKET3_SURFACE_SYNC, 3));
+ radeon_ring_write(rdev, PACKET3_TC_ACTION_ENA |
+ PACKET3_VC_ACTION_ENA |
+ PACKET3_SH_ACTION_ENA);
+ radeon_ring_write(rdev, 0xFFFFFFFF);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 10); /* poll interval */
radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
radeon_ring_write(rdev, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT) | EVENT_INDEX(0));
/* wait for 3D idle clean */
@@ -2355,26 +2358,42 @@ void r600_fence_ring_emit(struct radeon_device *rdev,
}
int r600_copy_blit(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_pages, struct radeon_fence *fence)
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct radeon_fence *fence)
{
int r;
mutex_lock(&rdev->r600_blit.mutex);
rdev->r600_blit.vb_ib = NULL;
- r = r600_blit_prepare_copy(rdev, num_pages * RADEON_GPU_PAGE_SIZE);
+ r = r600_blit_prepare_copy(rdev, num_gpu_pages);
if (r) {
if (rdev->r600_blit.vb_ib)
radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
mutex_unlock(&rdev->r600_blit.mutex);
return r;
}
- r600_kms_blit_copy(rdev, src_offset, dst_offset, num_pages * RADEON_GPU_PAGE_SIZE);
+ r600_kms_blit_copy(rdev, src_offset, dst_offset, num_gpu_pages);
r600_blit_done_copy(rdev, fence);
mutex_unlock(&rdev->r600_blit.mutex);
return 0;
}
+void r600_blit_suspend(struct radeon_device *rdev)
+{
+ int r;
+
+ /* unpin shaders bo */
+ if (rdev->r600_blit.shader_obj) {
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+ if (!r) {
+ radeon_bo_unpin(rdev->r600_blit.shader_obj);
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+ }
+ }
+}
+
int r600_set_surface_reg(struct radeon_device *rdev, int reg,
uint32_t tiling_flags, uint32_t pitch,
uint32_t offset, uint32_t obj_size)
@@ -2403,6 +2422,10 @@ int r600_startup(struct radeon_device *rdev)
}
}
+ r = r600_vram_scratch_init(rdev);
+ if (r)
+ return r;
+
r600_mc_program(rdev);
if (rdev->flags & RADEON_IS_AGP) {
r600_agp_enable(rdev);
@@ -2494,8 +2517,6 @@ int r600_resume(struct radeon_device *rdev)
int r600_suspend(struct radeon_device *rdev)
{
- int r;
-
r600_audio_fini(rdev);
/* FIXME: we should wait for ring to be empty */
r600_cp_stop(rdev);
@@ -2503,14 +2524,8 @@ int r600_suspend(struct radeon_device *rdev)
r600_irq_suspend(rdev);
radeon_wb_disable(rdev);
r600_pcie_gart_disable(rdev);
- /* unpin shaders bo */
- if (rdev->r600_blit.shader_obj) {
- r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
- if (!r) {
- radeon_bo_unpin(rdev->r600_blit.shader_obj);
- radeon_bo_unreserve(rdev->r600_blit.shader_obj);
- }
- }
+ r600_blit_suspend(rdev);
+
return 0;
}
@@ -2631,6 +2646,7 @@ void r600_fini(struct radeon_device *rdev)
radeon_ib_pool_fini(rdev);
radeon_irq_kms_fini(rdev);
r600_pcie_gart_fini(rdev);
+ r600_vram_scratch_fini(rdev);
radeon_agp_fini(rdev);
radeon_gem_fini(rdev);
radeon_fence_driver_fini(rdev);
@@ -2994,10 +3010,6 @@ int r600_irq_init(struct radeon_device *rdev)
/* RPTR_REARM only works if msi's are enabled */
if (rdev->msi_enabled)
ih_cntl |= RPTR_REARM;
-
-#ifdef __BIG_ENDIAN
- ih_cntl |= IH_MC_SWAP(IH_MC_SWAP_32BIT);
-#endif
WREG32(IH_CNTL, ih_cntl);
/* force the active interrupt state to all disabled */
@@ -3141,7 +3153,7 @@ int r600_irq_set(struct radeon_device *rdev)
return 0;
}
-static inline void r600_irq_ack(struct radeon_device *rdev)
+static void r600_irq_ack(struct radeon_device *rdev)
{
u32 tmp;
@@ -3242,7 +3254,7 @@ void r600_irq_disable(struct radeon_device *rdev)
r600_disable_interrupt_state(rdev);
}
-static inline u32 r600_get_ih_wptr(struct radeon_device *rdev)
+static u32 r600_get_ih_wptr(struct radeon_device *rdev)
{
u32 wptr, tmp;
@@ -3308,6 +3320,10 @@ int r600_irq_process(struct radeon_device *rdev)
if (!rdev->ih.enabled || rdev->shutdown)
return IRQ_NONE;
+ /* No MSIs, need a dummy read to flush PCI DMAs */
+ if (!rdev->msi_enabled)
+ RREG32(IH_RB_WPTR);
+
wptr = r600_get_ih_wptr(rdev);
rptr = rdev->ih.rptr;
DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
@@ -3320,6 +3336,9 @@ int r600_irq_process(struct radeon_device *rdev)
}
restart_ih:
+ /* Order reading of wptr vs. reading of IH ring data */
+ rmb();
+
/* display interrupts */
r600_irq_ack(rdev);
diff --git a/drivers/gpu/drm/radeon/r600_blit.c b/drivers/gpu/drm/radeon/r600_blit.c
index 7f1043448d25..3c031a48205d 100644
--- a/drivers/gpu/drm/radeon/r600_blit.c
+++ b/drivers/gpu/drm/radeon/r600_blit.c
@@ -41,7 +41,7 @@
#define COLOR_5_6_5 0x8
#define COLOR_8_8_8_8 0x1a
-static inline void
+static void
set_render_target(drm_radeon_private_t *dev_priv, int format, int w, int h, u64 gpu_addr)
{
u32 cb_color_info;
@@ -99,7 +99,7 @@ set_render_target(drm_radeon_private_t *dev_priv, int format, int w, int h, u64
ADVANCE_RING();
}
-static inline void
+static void
cp_set_surface_sync(drm_radeon_private_t *dev_priv,
u32 sync_type, u32 size, u64 mc_addr)
{
@@ -121,7 +121,7 @@ cp_set_surface_sync(drm_radeon_private_t *dev_priv,
ADVANCE_RING();
}
-static inline void
+static void
set_shaders(struct drm_device *dev)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
@@ -184,7 +184,7 @@ set_shaders(struct drm_device *dev)
R600_SH_ACTION_ENA, 512, gpu_addr);
}
-static inline void
+static void
set_vtx_resource(drm_radeon_private_t *dev_priv, u64 gpu_addr)
{
uint32_t sq_vtx_constant_word2;
@@ -220,7 +220,7 @@ set_vtx_resource(drm_radeon_private_t *dev_priv, u64 gpu_addr)
R600_VC_ACTION_ENA, 48, gpu_addr);
}
-static inline void
+static void
set_tex_resource(drm_radeon_private_t *dev_priv,
int format, int w, int h, int pitch, u64 gpu_addr)
{
@@ -258,7 +258,7 @@ set_tex_resource(drm_radeon_private_t *dev_priv,
}
-static inline void
+static void
set_scissors(drm_radeon_private_t *dev_priv, int x1, int y1, int x2, int y2)
{
RING_LOCALS;
@@ -282,7 +282,7 @@ set_scissors(drm_radeon_private_t *dev_priv, int x1, int y1, int x2, int y2)
ADVANCE_RING();
}
-static inline void
+static void
draw_auto(drm_radeon_private_t *dev_priv)
{
RING_LOCALS;
@@ -311,7 +311,7 @@ draw_auto(drm_radeon_private_t *dev_priv)
COMMIT_RING();
}
-static inline void
+static void
set_default_state(drm_radeon_private_t *dev_priv)
{
int i;
@@ -489,7 +489,7 @@ set_default_state(drm_radeon_private_t *dev_priv)
ADVANCE_RING();
}
-static inline uint32_t i2f(uint32_t input)
+static uint32_t i2f(uint32_t input)
{
u32 result, i, exponent, fraction;
@@ -515,7 +515,7 @@ static inline uint32_t i2f(uint32_t input)
}
-static inline int r600_nomm_get_vb(struct drm_device *dev)
+static int r600_nomm_get_vb(struct drm_device *dev)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
dev_priv->blit_vb = radeon_freelist_get(dev);
@@ -526,7 +526,7 @@ static inline int r600_nomm_get_vb(struct drm_device *dev)
return 0;
}
-static inline void r600_nomm_put_vb(struct drm_device *dev)
+static void r600_nomm_put_vb(struct drm_device *dev)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
@@ -534,7 +534,7 @@ static inline void r600_nomm_put_vb(struct drm_device *dev)
radeon_cp_discard_buffer(dev, dev_priv->blit_vb->file_priv->master, dev_priv->blit_vb);
}
-static inline void *r600_nomm_get_vb_ptr(struct drm_device *dev)
+static void *r600_nomm_get_vb_ptr(struct drm_device *dev)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
return (((char *)dev->agp_buffer_map->handle +
diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c
index 9aa74c3f8cb6..e09d2818f949 100644
--- a/drivers/gpu/drm/radeon/r600_blit_kms.c
+++ b/drivers/gpu/drm/radeon/r600_blit_kms.c
@@ -42,6 +42,9 @@
#define COLOR_5_6_5 0x8
#define COLOR_8_8_8_8 0x1a
+#define RECT_UNIT_H 32
+#define RECT_UNIT_W (RADEON_GPU_PAGE_SIZE / 4 / RECT_UNIT_H)
+
/* emits 21 on rv770+, 23 on r600 */
static void
set_render_target(struct radeon_device *rdev, int format,
@@ -54,7 +57,9 @@ set_render_target(struct radeon_device *rdev, int format,
if (h < 8)
h = 8;
- cb_color_info = ((format << 2) | (1 << 27) | (1 << 8));
+ cb_color_info = CB_FORMAT(format) |
+ CB_SOURCE_FORMAT(CB_SF_EXPORT_NORM) |
+ CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1);
pitch = (w / 8) - 1;
slice = ((w * h) / 64) - 1;
@@ -164,9 +169,10 @@ set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr)
{
u32 sq_vtx_constant_word2;
- sq_vtx_constant_word2 = ((upper_32_bits(gpu_addr) & 0xff) | (16 << 8));
+ sq_vtx_constant_word2 = SQ_VTXC_BASE_ADDR_HI(upper_32_bits(gpu_addr) & 0xff) |
+ SQ_VTXC_STRIDE(16);
#ifdef __BIG_ENDIAN
- sq_vtx_constant_word2 |= (2 << 30);
+ sq_vtx_constant_word2 |= SQ_VTXC_ENDIAN_SWAP(SQ_ENDIAN_8IN32);
#endif
radeon_ring_write(rdev, PACKET3(PACKET3_SET_RESOURCE, 7));
@@ -195,25 +201,29 @@ set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr)
static void
set_tex_resource(struct radeon_device *rdev,
int format, int w, int h, int pitch,
- u64 gpu_addr)
+ u64 gpu_addr, u32 size)
{
uint32_t sq_tex_resource_word0, sq_tex_resource_word1, sq_tex_resource_word4;
if (h < 1)
h = 1;
- sq_tex_resource_word0 = (1 << 0) | (1 << 3);
- sq_tex_resource_word0 |= ((((pitch >> 3) - 1) << 8) |
- ((w - 1) << 19));
+ sq_tex_resource_word0 = S_038000_DIM(V_038000_SQ_TEX_DIM_2D) |
+ S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1);
+ sq_tex_resource_word0 |= S_038000_PITCH((pitch >> 3) - 1) |
+ S_038000_TEX_WIDTH(w - 1);
+
+ sq_tex_resource_word1 = S_038004_DATA_FORMAT(format);
+ sq_tex_resource_word1 |= S_038004_TEX_HEIGHT(h - 1);
- sq_tex_resource_word1 = (format << 26);
- sq_tex_resource_word1 |= ((h - 1) << 0);
+ sq_tex_resource_word4 = S_038010_REQUEST_SIZE(1) |
+ S_038010_DST_SEL_X(SQ_SEL_X) |
+ S_038010_DST_SEL_Y(SQ_SEL_Y) |
+ S_038010_DST_SEL_Z(SQ_SEL_Z) |
+ S_038010_DST_SEL_W(SQ_SEL_W);
- sq_tex_resource_word4 = ((1 << 14) |
- (0 << 16) |
- (1 << 19) |
- (2 << 22) |
- (3 << 25));
+ cp_set_surface_sync(rdev,
+ PACKET3_TC_ACTION_ENA, size, gpu_addr);
radeon_ring_write(rdev, PACKET3(PACKET3_SET_RESOURCE, 7));
radeon_ring_write(rdev, 0);
@@ -450,7 +460,7 @@ set_default_state(struct radeon_device *rdev)
radeon_ring_write(rdev, sq_stack_resource_mgmt_2);
}
-static inline uint32_t i2f(uint32_t input)
+static uint32_t i2f(uint32_t input)
{
u32 result, i, exponent, fraction;
@@ -483,6 +493,27 @@ int r600_blit_init(struct radeon_device *rdev)
u32 packet2s[16];
int num_packet2s = 0;
+ rdev->r600_blit.primitives.set_render_target = set_render_target;
+ rdev->r600_blit.primitives.cp_set_surface_sync = cp_set_surface_sync;
+ rdev->r600_blit.primitives.set_shaders = set_shaders;
+ rdev->r600_blit.primitives.set_vtx_resource = set_vtx_resource;
+ rdev->r600_blit.primitives.set_tex_resource = set_tex_resource;
+ rdev->r600_blit.primitives.set_scissors = set_scissors;
+ rdev->r600_blit.primitives.draw_auto = draw_auto;
+ rdev->r600_blit.primitives.set_default_state = set_default_state;
+
+ rdev->r600_blit.ring_size_common = 40; /* shaders + def state */
+ rdev->r600_blit.ring_size_common += 16; /* fence emit for VB IB */
+ rdev->r600_blit.ring_size_common += 5; /* done copy */
+ rdev->r600_blit.ring_size_common += 16; /* fence emit for done copy */
+
+ rdev->r600_blit.ring_size_per_loop = 76;
+ /* set_render_target emits 2 extra dwords on rv6xx */
+ if (rdev->family > CHIP_R600 && rdev->family < CHIP_RV770)
+ rdev->r600_blit.ring_size_per_loop += 2;
+
+ rdev->r600_blit.max_dim = 8192;
+
/* pin copy shader into vram if already initialized */
if (rdev->r600_blit.shader_obj)
goto done;
@@ -600,47 +631,80 @@ static void r600_vb_ib_put(struct radeon_device *rdev)
radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
}
-int r600_blit_prepare_copy(struct radeon_device *rdev, int size_bytes)
+static unsigned r600_blit_create_rect(unsigned num_gpu_pages,
+ int *width, int *height, int max_dim)
+{
+ unsigned max_pages;
+ unsigned pages = num_gpu_pages;
+ int w, h;
+
+ if (num_gpu_pages == 0) {
+ /* not supposed to be called with no pages, but just in case */
+ h = 0;
+ w = 0;
+ pages = 0;
+ WARN_ON(1);
+ } else {
+ int rect_order = 2;
+ h = RECT_UNIT_H;
+ while (num_gpu_pages / rect_order) {
+ h *= 2;
+ rect_order *= 4;
+ if (h >= max_dim) {
+ h = max_dim;
+ break;
+ }
+ }
+ max_pages = (max_dim * h) / (RECT_UNIT_W * RECT_UNIT_H);
+ if (pages > max_pages)
+ pages = max_pages;
+ w = (pages * RECT_UNIT_W * RECT_UNIT_H) / h;
+ w = (w / RECT_UNIT_W) * RECT_UNIT_W;
+ pages = (w * h) / (RECT_UNIT_W * RECT_UNIT_H);
+ BUG_ON(pages == 0);
+ }
+
+
+ DRM_DEBUG("blit_rectangle: h=%d, w=%d, pages=%d\n", h, w, pages);
+
+ /* return width and height only of the caller wants it */
+ if (height)
+ *height = h;
+ if (width)
+ *width = w;
+
+ return pages;
+}
+
+
+int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages)
{
int r;
- int ring_size, line_size;
- int max_size;
- /* loops of emits 64 + fence emit possible */
- int dwords_per_loop = 76, num_loops;
+ int ring_size;
+ int num_loops = 0;
+ int dwords_per_loop = rdev->r600_blit.ring_size_per_loop;
r = r600_vb_ib_get(rdev);
if (r)
return r;
- /* set_render_target emits 2 extra dwords on rv6xx */
- if (rdev->family > CHIP_R600 && rdev->family < CHIP_RV770)
- dwords_per_loop += 2;
-
- /* 8 bpp vs 32 bpp for xfer unit */
- if (size_bytes & 3)
- line_size = 8192;
- else
- line_size = 8192*4;
-
- max_size = 8192 * line_size;
+ /* num loops */
+ while (num_gpu_pages) {
+ num_gpu_pages -=
+ r600_blit_create_rect(num_gpu_pages, NULL, NULL,
+ rdev->r600_blit.max_dim);
+ num_loops++;
+ }
- /* major loops cover the max size transfer */
- num_loops = ((size_bytes + max_size) / max_size);
- /* minor loops cover the extra non aligned bits */
- num_loops += ((size_bytes % line_size) ? 1 : 0);
/* calculate number of loops correctly */
ring_size = num_loops * dwords_per_loop;
- /* set default + shaders */
- ring_size += 40; /* shaders + def state */
- ring_size += 10; /* fence emit for VB IB */
- ring_size += 5; /* done copy */
- ring_size += 10; /* fence emit for done copy */
+ ring_size += rdev->r600_blit.ring_size_common;
r = radeon_ring_lock(rdev, ring_size);
if (r)
return r;
- set_default_state(rdev); /* 14 */
- set_shaders(rdev); /* 26 */
+ rdev->r600_blit.primitives.set_default_state(rdev);
+ rdev->r600_blit.primitives.set_shaders(rdev);
return 0;
}
@@ -659,182 +723,61 @@ void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence)
void r600_kms_blit_copy(struct radeon_device *rdev,
u64 src_gpu_addr, u64 dst_gpu_addr,
- int size_bytes)
+ unsigned num_gpu_pages)
{
- int max_bytes;
u64 vb_gpu_addr;
u32 *vb;
- DRM_DEBUG("emitting copy %16llx %16llx %d %d\n", src_gpu_addr, dst_gpu_addr,
- size_bytes, rdev->r600_blit.vb_used);
+ DRM_DEBUG("emitting copy %16llx %16llx %d %d\n",
+ src_gpu_addr, dst_gpu_addr,
+ num_gpu_pages, rdev->r600_blit.vb_used);
vb = (u32 *)(rdev->r600_blit.vb_ib->ptr + rdev->r600_blit.vb_used);
- if ((size_bytes & 3) || (src_gpu_addr & 3) || (dst_gpu_addr & 3)) {
- max_bytes = 8192;
-
- while (size_bytes) {
- int cur_size = size_bytes;
- int src_x = src_gpu_addr & 255;
- int dst_x = dst_gpu_addr & 255;
- int h = 1;
- src_gpu_addr = src_gpu_addr & ~255ULL;
- dst_gpu_addr = dst_gpu_addr & ~255ULL;
-
- if (!src_x && !dst_x) {
- h = (cur_size / max_bytes);
- if (h > 8192)
- h = 8192;
- if (h == 0)
- h = 1;
- else
- cur_size = max_bytes;
- } else {
- if (cur_size > max_bytes)
- cur_size = max_bytes;
- if (cur_size > (max_bytes - dst_x))
- cur_size = (max_bytes - dst_x);
- if (cur_size > (max_bytes - src_x))
- cur_size = (max_bytes - src_x);
- }
-
- if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) {
- WARN_ON(1);
- }
-
- vb[0] = i2f(dst_x);
- vb[1] = 0;
- vb[2] = i2f(src_x);
- vb[3] = 0;
-
- vb[4] = i2f(dst_x);
- vb[5] = i2f(h);
- vb[6] = i2f(src_x);
- vb[7] = i2f(h);
-
- vb[8] = i2f(dst_x + cur_size);
- vb[9] = i2f(h);
- vb[10] = i2f(src_x + cur_size);
- vb[11] = i2f(h);
-
- /* src 9 */
- set_tex_resource(rdev, FMT_8,
- src_x + cur_size, h, src_x + cur_size,
- src_gpu_addr);
-
- /* 5 */
- cp_set_surface_sync(rdev,
- PACKET3_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr);
- /* dst 23 */
- set_render_target(rdev, COLOR_8,
- dst_x + cur_size, h,
- dst_gpu_addr);
+ while (num_gpu_pages) {
+ int w, h;
+ unsigned size_in_bytes;
+ unsigned pages_per_loop =
+ r600_blit_create_rect(num_gpu_pages, &w, &h,
+ rdev->r600_blit.max_dim);
- /* scissors 12 */
- set_scissors(rdev, dst_x, 0, dst_x + cur_size, h);
+ size_in_bytes = pages_per_loop * RADEON_GPU_PAGE_SIZE;
+ DRM_DEBUG("rectangle w=%d h=%d\n", w, h);
- /* 14 */
- vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used;
- set_vtx_resource(rdev, vb_gpu_addr);
-
- /* draw 10 */
- draw_auto(rdev);
-
- /* 5 */
- cp_set_surface_sync(rdev,
- PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA,
- cur_size * h, dst_gpu_addr);
-
- vb += 12;
- rdev->r600_blit.vb_used += 12 * 4;
-
- src_gpu_addr += cur_size * h;
- dst_gpu_addr += cur_size * h;
- size_bytes -= cur_size * h;
+ if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) {
+ WARN_ON(1);
}
- } else {
- max_bytes = 8192 * 4;
-
- while (size_bytes) {
- int cur_size = size_bytes;
- int src_x = (src_gpu_addr & 255);
- int dst_x = (dst_gpu_addr & 255);
- int h = 1;
- src_gpu_addr = src_gpu_addr & ~255ULL;
- dst_gpu_addr = dst_gpu_addr & ~255ULL;
-
- if (!src_x && !dst_x) {
- h = (cur_size / max_bytes);
- if (h > 8192)
- h = 8192;
- if (h == 0)
- h = 1;
- else
- cur_size = max_bytes;
- } else {
- if (cur_size > max_bytes)
- cur_size = max_bytes;
- if (cur_size > (max_bytes - dst_x))
- cur_size = (max_bytes - dst_x);
- if (cur_size > (max_bytes - src_x))
- cur_size = (max_bytes - src_x);
- }
-
- if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) {
- WARN_ON(1);
- }
- vb[0] = i2f(dst_x / 4);
- vb[1] = 0;
- vb[2] = i2f(src_x / 4);
- vb[3] = 0;
-
- vb[4] = i2f(dst_x / 4);
- vb[5] = i2f(h);
- vb[6] = i2f(src_x / 4);
- vb[7] = i2f(h);
-
- vb[8] = i2f((dst_x + cur_size) / 4);
- vb[9] = i2f(h);
- vb[10] = i2f((src_x + cur_size) / 4);
- vb[11] = i2f(h);
-
- /* src 9 */
- set_tex_resource(rdev, FMT_8_8_8_8,
- (src_x + cur_size) / 4,
- h, (src_x + cur_size) / 4,
- src_gpu_addr);
- /* 5 */
- cp_set_surface_sync(rdev,
- PACKET3_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr);
-
- /* dst 23 */
- set_render_target(rdev, COLOR_8_8_8_8,
- (dst_x + cur_size) / 4, h,
- dst_gpu_addr);
-
- /* scissors 12 */
- set_scissors(rdev, (dst_x / 4), 0, (dst_x + cur_size / 4), h);
-
- /* Vertex buffer setup 14 */
- vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used;
- set_vtx_resource(rdev, vb_gpu_addr);
-
- /* draw 10 */
- draw_auto(rdev);
-
- /* 5 */
- cp_set_surface_sync(rdev,
- PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA,
- cur_size * h, dst_gpu_addr);
-
- /* 78 ring dwords per loop */
- vb += 12;
- rdev->r600_blit.vb_used += 12 * 4;
-
- src_gpu_addr += cur_size * h;
- dst_gpu_addr += cur_size * h;
- size_bytes -= cur_size * h;
- }
+ vb[0] = 0;
+ vb[1] = 0;
+ vb[2] = 0;
+ vb[3] = 0;
+
+ vb[4] = 0;
+ vb[5] = i2f(h);
+ vb[6] = 0;
+ vb[7] = i2f(h);
+
+ vb[8] = i2f(w);
+ vb[9] = i2f(h);
+ vb[10] = i2f(w);
+ vb[11] = i2f(h);
+
+ rdev->r600_blit.primitives.set_tex_resource(rdev, FMT_8_8_8_8,
+ w, h, w, src_gpu_addr, size_in_bytes);
+ rdev->r600_blit.primitives.set_render_target(rdev, COLOR_8_8_8_8,
+ w, h, dst_gpu_addr);
+ rdev->r600_blit.primitives.set_scissors(rdev, 0, 0, w, h);
+ vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used;
+ rdev->r600_blit.primitives.set_vtx_resource(rdev, vb_gpu_addr);
+ rdev->r600_blit.primitives.draw_auto(rdev);
+ rdev->r600_blit.primitives.cp_set_surface_sync(rdev,
+ PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA,
+ size_in_bytes, dst_gpu_addr);
+
+ vb += 12;
+ rdev->r600_blit.vb_used += 4*12;
+ src_gpu_addr += size_in_bytes;
+ dst_gpu_addr += size_in_bytes;
+ num_gpu_pages -= pages_per_loop;
}
}
-
diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c
index c3ab959bdc7c..c9db4931913f 100644
--- a/drivers/gpu/drm/radeon/r600_cp.c
+++ b/drivers/gpu/drm/radeon/r600_cp.c
@@ -26,6 +26,8 @@
* Alex Deucher <alexander.deucher@amd.com>
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "drm.h"
#include "radeon_drm.h"
@@ -1802,8 +1804,8 @@ static void r600_cp_init_ring_buffer(struct drm_device *dev,
/* Set ring buffer size */
#ifdef __BIG_ENDIAN
RADEON_WRITE(R600_CP_RB_CNTL,
- RADEON_BUF_SWAP_32BIT |
- RADEON_RB_NO_UPDATE |
+ R600_BUF_SWAP_32BIT |
+ R600_RB_NO_UPDATE |
(dev_priv->ring.rptr_update_l2qw << 8) |
dev_priv->ring.size_l2qw);
#else
@@ -1820,15 +1822,15 @@ static void r600_cp_init_ring_buffer(struct drm_device *dev,
#ifdef __BIG_ENDIAN
RADEON_WRITE(R600_CP_RB_CNTL,
- RADEON_BUF_SWAP_32BIT |
- RADEON_RB_NO_UPDATE |
- RADEON_RB_RPTR_WR_ENA |
+ R600_BUF_SWAP_32BIT |
+ R600_RB_NO_UPDATE |
+ R600_RB_RPTR_WR_ENA |
(dev_priv->ring.rptr_update_l2qw << 8) |
dev_priv->ring.size_l2qw);
#else
RADEON_WRITE(R600_CP_RB_CNTL,
- RADEON_RB_NO_UPDATE |
- RADEON_RB_RPTR_WR_ENA |
+ R600_RB_NO_UPDATE |
+ R600_RB_RPTR_WR_ENA |
(dev_priv->ring.rptr_update_l2qw << 8) |
dev_priv->ring.size_l2qw);
#endif
@@ -1851,13 +1853,8 @@ static void r600_cp_init_ring_buffer(struct drm_device *dev,
- ((unsigned long) dev->sg->virtual)
+ dev_priv->gart_vm_start;
}
- RADEON_WRITE(R600_CP_RB_RPTR_ADDR,
-#ifdef __BIG_ENDIAN
- (2 << 0) |
-#endif
- (rptr_addr & 0xfffffffc));
- RADEON_WRITE(R600_CP_RB_RPTR_ADDR_HI,
- upper_32_bits(rptr_addr));
+ RADEON_WRITE(R600_CP_RB_RPTR_ADDR, (rptr_addr & 0xfffffffc));
+ RADEON_WRITE(R600_CP_RB_RPTR_ADDR_HI, upper_32_bits(rptr_addr));
#ifdef __BIG_ENDIAN
RADEON_WRITE(R600_CP_RB_CNTL,
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index 909bda8dd550..cb1acffd2430 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -162,7 +162,7 @@ static const struct gpu_formats color_formats_table[] = {
[V_038004_FMT_32_AS_32_32_32_32] = { 1, 1, 4, 0, CHIP_CEDAR},
};
-static inline bool fmt_is_valid_color(u32 format)
+static bool fmt_is_valid_color(u32 format)
{
if (format >= ARRAY_SIZE(color_formats_table))
return false;
@@ -173,7 +173,7 @@ static inline bool fmt_is_valid_color(u32 format)
return false;
}
-static inline bool fmt_is_valid_texture(u32 format, enum radeon_family family)
+static bool fmt_is_valid_texture(u32 format, enum radeon_family family)
{
if (format >= ARRAY_SIZE(color_formats_table))
return false;
@@ -187,7 +187,7 @@ static inline bool fmt_is_valid_texture(u32 format, enum radeon_family family)
return false;
}
-static inline int fmt_get_blocksize(u32 format)
+static int fmt_get_blocksize(u32 format)
{
if (format >= ARRAY_SIZE(color_formats_table))
return 0;
@@ -195,7 +195,7 @@ static inline int fmt_get_blocksize(u32 format)
return color_formats_table[format].blocksize;
}
-static inline int fmt_get_nblocksx(u32 format, u32 w)
+static int fmt_get_nblocksx(u32 format, u32 w)
{
unsigned bw;
@@ -209,7 +209,7 @@ static inline int fmt_get_nblocksx(u32 format, u32 w)
return (w + bw - 1) / bw;
}
-static inline int fmt_get_nblocksy(u32 format, u32 h)
+static int fmt_get_nblocksy(u32 format, u32 h)
{
unsigned bh;
@@ -223,25 +223,6 @@ static inline int fmt_get_nblocksy(u32 format, u32 h)
return (h + bh - 1) / bh;
}
-static inline int r600_bpe_from_format(u32 *bpe, u32 format)
-{
- unsigned res;
-
- if (format >= ARRAY_SIZE(color_formats_table))
- goto fail;
-
- res = color_formats_table[format].blocksize;
- if (res == 0)
- goto fail;
-
- *bpe = res;
- return 0;
-
-fail:
- *bpe = 16;
- return -EINVAL;
-}
-
struct array_mode_checker {
int array_mode;
u32 group_size;
@@ -252,7 +233,7 @@ struct array_mode_checker {
};
/* returns alignment in pixels for pitch/height/depth and bytes for base */
-static inline int r600_get_array_mode_alignment(struct array_mode_checker *values,
+static int r600_get_array_mode_alignment(struct array_mode_checker *values,
u32 *pitch_align,
u32 *height_align,
u32 *depth_align,
@@ -331,7 +312,7 @@ static void r600_cs_track_init(struct r600_cs_track *track)
track->db_depth_control = 0xFFFFFFFF;
}
-static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
+static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
{
struct r600_cs_track *track = p->track;
u32 slice_tile_max, size, tmp;
@@ -737,7 +718,7 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p,
* Check next packet is relocation packet3, do bo validation and compute
* GPU offset using the provided start.
**/
-static inline int r600_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p)
+static int r600_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p)
{
struct radeon_cs_packet p3reloc;
int r;
@@ -911,16 +892,15 @@ static int r600_cs_parse_packet0(struct radeon_cs_parser *p,
* if register is safe. If register is not flag as safe this function
* will test it against a list of register needind special handling.
*/
-static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
+static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
{
struct r600_cs_track *track = (struct r600_cs_track *)p->track;
struct radeon_cs_reloc *reloc;
- u32 last_reg = ARRAY_SIZE(r600_reg_safe_bm);
u32 m, i, tmp, *ib;
int r;
i = (reg >> 7);
- if (i > last_reg) {
+ if (i >= ARRAY_SIZE(r600_reg_safe_bm)) {
dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
return -EINVAL;
}
@@ -961,7 +941,8 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
track->db_depth_control = radeon_get_ib_value(p, idx);
break;
case R_028010_DB_DEPTH_INFO:
- if (r600_cs_packet_next_is_pkt3_nop(p)) {
+ if (!p->keep_tiling_flags &&
+ r600_cs_packet_next_is_pkt3_nop(p)) {
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
dev_warn(p->dev, "bad SET_CONTEXT_REG "
@@ -1012,7 +993,8 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
case R_0280B4_CB_COLOR5_INFO:
case R_0280B8_CB_COLOR6_INFO:
case R_0280BC_CB_COLOR7_INFO:
- if (r600_cs_packet_next_is_pkt3_nop(p)) {
+ if (!p->keep_tiling_flags &&
+ r600_cs_packet_next_is_pkt3_nop(p)) {
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
@@ -1200,6 +1182,15 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
}
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
break;
+ case SX_MEMORY_EXPORT_BASE:
+ r = r600_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONFIG_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ break;
default:
dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
return -EINVAL;
@@ -1207,7 +1198,7 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
return 0;
}
-static inline unsigned mip_minify(unsigned size, unsigned level)
+static unsigned mip_minify(unsigned size, unsigned level)
{
unsigned val;
@@ -1277,7 +1268,7 @@ static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned llevel,
* This function will check that the resource has valid field and that
* the texture and mipmap bo object are big enough to cover this resource.
*/
-static inline int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
+static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
struct radeon_bo *texture,
struct radeon_bo *mipmap,
u64 base_offset,
@@ -1302,10 +1293,12 @@ static inline int r600_check_texture_resource(struct radeon_cs_parser *p, u32 i
mip_offset <<= 8;
word0 = radeon_get_ib_value(p, idx + 0);
- if (tiling_flags & RADEON_TILING_MACRO)
- word0 |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1);
- else if (tiling_flags & RADEON_TILING_MICRO)
- word0 |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1);
+ if (!p->keep_tiling_flags) {
+ if (tiling_flags & RADEON_TILING_MACRO)
+ word0 |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1);
+ else if (tiling_flags & RADEON_TILING_MICRO)
+ word0 |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1);
+ }
word1 = radeon_get_ib_value(p, idx + 1);
w0 = G_038000_TEX_WIDTH(word0) + 1;
h0 = G_038004_TEX_HEIGHT(word1) + 1;
@@ -1632,10 +1625,12 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
base_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
- ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1);
- else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
- ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1);
+ if (!p->keep_tiling_flags) {
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1);
+ else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1);
+ }
texture = reloc->robj;
/* tex mip base */
r = r600_cs_packet_next_reloc(p, &reloc);
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 0245ae6c204e..bfe1b5d92afe 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -79,6 +79,11 @@
#define CB_COLOR0_SIZE 0x28060
#define CB_COLOR0_VIEW 0x28080
#define CB_COLOR0_INFO 0x280a0
+# define CB_FORMAT(x) ((x) << 2)
+# define CB_ARRAY_MODE(x) ((x) << 8)
+# define CB_SOURCE_FORMAT(x) ((x) << 27)
+# define CB_SF_EXPORT_FULL 0
+# define CB_SF_EXPORT_NORM 1
#define CB_COLOR0_TILE 0x280c0
#define CB_COLOR0_FRAG 0x280e0
#define CB_COLOR0_MASK 0x28100
@@ -417,6 +422,17 @@
#define SQ_PGM_START_VS 0x28858
#define SQ_PGM_RESOURCES_VS 0x28868
#define SQ_PGM_CF_OFFSET_VS 0x288d0
+
+#define SQ_VTX_CONSTANT_WORD0_0 0x30000
+#define SQ_VTX_CONSTANT_WORD1_0 0x30004
+#define SQ_VTX_CONSTANT_WORD2_0 0x30008
+# define SQ_VTXC_BASE_ADDR_HI(x) ((x) << 0)
+# define SQ_VTXC_STRIDE(x) ((x) << 8)
+# define SQ_VTXC_ENDIAN_SWAP(x) ((x) << 30)
+# define SQ_ENDIAN_NONE 0
+# define SQ_ENDIAN_8IN16 1
+# define SQ_ENDIAN_8IN32 2
+#define SQ_VTX_CONSTANT_WORD3_0 0x3000c
#define SQ_VTX_CONSTANT_WORD6_0 0x38018
#define S__SQ_VTX_CONSTANT_TYPE(x) (((x) & 3) << 30)
#define G__SQ_VTX_CONSTANT_TYPE(x) (((x) >> 30) & 3)
@@ -1352,6 +1368,12 @@
#define S_038010_DST_SEL_W(x) (((x) & 0x7) << 25)
#define G_038010_DST_SEL_W(x) (((x) >> 25) & 0x7)
#define C_038010_DST_SEL_W 0xF1FFFFFF
+# define SQ_SEL_X 0
+# define SQ_SEL_Y 1
+# define SQ_SEL_Z 2
+# define SQ_SEL_W 3
+# define SQ_SEL_0 4
+# define SQ_SEL_1 5
#define S_038010_BASE_LEVEL(x) (((x) & 0xF) << 28)
#define G_038010_BASE_LEVEL(x) (((x) >> 28) & 0xF)
#define C_038010_BASE_LEVEL 0x0FFFFFFF
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index ef0e0e016914..8227e76b5c70 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -60,7 +60,7 @@
* are considered as fatal)
*/
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/kref.h>
@@ -93,6 +93,7 @@ extern int radeon_audio;
extern int radeon_disp_priority;
extern int radeon_hw_i2c;
extern int radeon_pcie_gen2;
+extern int radeon_msi;
/*
* Copy from radeon_drv.h so we don't have to include both and have conflicting
@@ -102,7 +103,7 @@ extern int radeon_pcie_gen2;
#define RADEON_FENCE_JIFFIES_TIMEOUT (HZ / 2)
/* RADEON_IB_POOL_SIZE must be a power of 2 */
#define RADEON_IB_POOL_SIZE 16
-#define RADEON_DEBUGFS_MAX_NUM_FILES 32
+#define RADEON_DEBUGFS_MAX_COMPONENTS 32
#define RADEONFB_CONN_LIMIT 4
#define RADEON_BIOS_NUM_SCRATCH 8
@@ -306,29 +307,17 @@ int radeon_mode_dumb_destroy(struct drm_file *file_priv,
*/
struct radeon_mc;
-struct radeon_gart_table_ram {
- volatile uint32_t *ptr;
-};
-
-struct radeon_gart_table_vram {
- struct radeon_bo *robj;
- volatile uint32_t *ptr;
-};
-
-union radeon_gart_table {
- struct radeon_gart_table_ram ram;
- struct radeon_gart_table_vram vram;
-};
-
#define RADEON_GPU_PAGE_SIZE 4096
#define RADEON_GPU_PAGE_MASK (RADEON_GPU_PAGE_SIZE - 1)
+#define RADEON_GPU_PAGE_SHIFT 12
struct radeon_gart {
dma_addr_t table_addr;
+ struct radeon_bo *robj;
+ void *ptr;
unsigned num_gpu_pages;
unsigned num_cpu_pages;
unsigned table_size;
- union radeon_gart_table table;
struct page **pages;
dma_addr_t *pages_addr;
bool *ttm_alloced;
@@ -339,6 +328,8 @@ int radeon_gart_table_ram_alloc(struct radeon_device *rdev);
void radeon_gart_table_ram_free(struct radeon_device *rdev);
int radeon_gart_table_vram_alloc(struct radeon_device *rdev);
void radeon_gart_table_vram_free(struct radeon_device *rdev);
+int radeon_gart_table_vram_pin(struct radeon_device *rdev);
+void radeon_gart_table_vram_unpin(struct radeon_device *rdev);
int radeon_gart_init(struct radeon_device *rdev);
void radeon_gart_fini(struct radeon_device *rdev);
void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset,
@@ -346,6 +337,7 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset,
int radeon_gart_bind(struct radeon_device *rdev, unsigned offset,
int pages, struct page **pagelist,
dma_addr_t *dma_addr);
+void radeon_gart_restore(struct radeon_device *rdev);
/*
@@ -436,25 +428,26 @@ union radeon_irq_stat_regs {
struct evergreen_irq_stat_regs evergreen;
};
+#define RADEON_MAX_HPD_PINS 6
+#define RADEON_MAX_CRTCS 6
+#define RADEON_MAX_HDMI_BLOCKS 2
+
struct radeon_irq {
bool installed;
bool sw_int;
- /* FIXME: use a define max crtc rather than hardcode it */
- bool crtc_vblank_int[6];
- bool pflip[6];
+ bool crtc_vblank_int[RADEON_MAX_CRTCS];
+ bool pflip[RADEON_MAX_CRTCS];
wait_queue_head_t vblank_queue;
- /* FIXME: use defines for max hpd/dacs */
- bool hpd[6];
+ bool hpd[RADEON_MAX_HPD_PINS];
bool gui_idle;
bool gui_idle_acked;
wait_queue_head_t idle_queue;
- /* FIXME: use defines for max HDMI blocks */
- bool hdmi[2];
+ bool hdmi[RADEON_MAX_HDMI_BLOCKS];
spinlock_t sw_lock;
int sw_refcount;
union radeon_irq_stat_regs stat_regs;
- spinlock_t pflip_lock[6];
- int pflip_refcount[6];
+ spinlock_t pflip_lock[RADEON_MAX_CRTCS];
+ int pflip_refcount[RADEON_MAX_CRTCS];
};
int radeon_irq_kms_init(struct radeon_device *rdev);
@@ -522,9 +515,30 @@ struct r600_ih {
bool enabled;
};
+struct r600_blit_cp_primitives {
+ void (*set_render_target)(struct radeon_device *rdev, int format,
+ int w, int h, u64 gpu_addr);
+ void (*cp_set_surface_sync)(struct radeon_device *rdev,
+ u32 sync_type, u32 size,
+ u64 mc_addr);
+ void (*set_shaders)(struct radeon_device *rdev);
+ void (*set_vtx_resource)(struct radeon_device *rdev, u64 gpu_addr);
+ void (*set_tex_resource)(struct radeon_device *rdev,
+ int format, int w, int h, int pitch,
+ u64 gpu_addr, u32 size);
+ void (*set_scissors)(struct radeon_device *rdev, int x1, int y1,
+ int x2, int y2);
+ void (*draw_auto)(struct radeon_device *rdev);
+ void (*set_default_state)(struct radeon_device *rdev);
+};
+
struct r600_blit {
struct mutex mutex;
struct radeon_bo *shader_obj;
+ struct r600_blit_cp_primitives primitives;
+ int max_dim;
+ int ring_size_common;
+ int ring_size_per_loop;
u64 shader_gpu_addr;
u32 vs_offset, ps_offset;
u32 state_offset;
@@ -533,6 +547,8 @@ struct r600_blit {
struct radeon_ib *vb_ib;
};
+void r600_blit_suspend(struct radeon_device *rdev);
+
int radeon_ib_get(struct radeon_device *rdev, struct radeon_ib **ib);
void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib);
int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib);
@@ -595,37 +611,13 @@ struct radeon_cs_parser {
struct radeon_ib *ib;
void *track;
unsigned family;
- int parser_error;
+ int parser_error;
+ bool keep_tiling_flags;
};
extern int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx);
extern int radeon_cs_finish_pages(struct radeon_cs_parser *p);
-
-
-static inline u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx)
-{
- struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
- u32 pg_idx, pg_offset;
- u32 idx_value = 0;
- int new_page;
-
- pg_idx = (idx * 4) / PAGE_SIZE;
- pg_offset = (idx * 4) % PAGE_SIZE;
-
- if (ibc->kpage_idx[0] == pg_idx)
- return ibc->kpage[0][pg_offset/4];
- if (ibc->kpage_idx[1] == pg_idx)
- return ibc->kpage[1][pg_offset/4];
-
- new_page = radeon_cs_update_pages(p, pg_idx);
- if (new_page < 0) {
- p->parser_error = new_page;
- return 0;
- }
-
- idx_value = ibc->kpage[new_page][pg_offset/4];
- return idx_value;
-}
+extern u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx);
struct radeon_cs_packet {
unsigned idx;
@@ -793,8 +785,7 @@ struct radeon_pm_clock_info {
struct radeon_power_state {
enum radeon_pm_state_type type;
- /* XXX: use a define for num clock modes */
- struct radeon_pm_clock_info clock_info[8];
+ struct radeon_pm_clock_info *clock_info;
/* number of valid clock modes in this power state */
int num_clock_modes;
struct radeon_pm_clock_info *default_clock_mode;
@@ -864,11 +855,14 @@ struct radeon_pm {
struct device *int_hwmon_dev;
};
+int radeon_pm_get_type_index(struct radeon_device *rdev,
+ enum radeon_pm_state_type ps_type,
+ int instance);
/*
* Benchmarking
*/
-void radeon_benchmark(struct radeon_device *rdev);
+void radeon_benchmark(struct radeon_device *rdev, int test_number);
/*
@@ -914,17 +908,17 @@ struct radeon_asic {
int (*copy_blit)(struct radeon_device *rdev,
uint64_t src_offset,
uint64_t dst_offset,
- unsigned num_pages,
+ unsigned num_gpu_pages,
struct radeon_fence *fence);
int (*copy_dma)(struct radeon_device *rdev,
uint64_t src_offset,
uint64_t dst_offset,
- unsigned num_pages,
+ unsigned num_gpu_pages,
struct radeon_fence *fence);
int (*copy)(struct radeon_device *rdev,
uint64_t src_offset,
uint64_t dst_offset,
- unsigned num_pages,
+ unsigned num_gpu_pages,
struct radeon_fence *fence);
uint32_t (*get_engine_clock)(struct radeon_device *rdev);
void (*set_engine_clock)(struct radeon_device *rdev, uint32_t eng_clock);
@@ -1003,6 +997,7 @@ struct r600_asic {
unsigned tiling_npipes;
unsigned tiling_group_size;
unsigned tile_config;
+ unsigned backend_map;
struct r100_gpu_lockup lockup;
};
@@ -1028,6 +1023,7 @@ struct rv770_asic {
unsigned tiling_npipes;
unsigned tiling_group_size;
unsigned tile_config;
+ unsigned backend_map;
struct r100_gpu_lockup lockup;
};
@@ -1054,6 +1050,7 @@ struct evergreen_asic {
unsigned tiling_npipes;
unsigned tiling_group_size;
unsigned tile_config;
+ unsigned backend_map;
struct r100_gpu_lockup lockup;
};
@@ -1141,12 +1138,55 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
-/* VRAM scratch page for HDP bug */
-struct r700_vram_scratch {
+/* VRAM scratch page for HDP bug, default vram page */
+struct r600_vram_scratch {
struct radeon_bo *robj;
volatile uint32_t *ptr;
+ u64 gpu_addr;
+};
+
+
+/*
+ * Mutex which allows recursive locking from the same process.
+ */
+struct radeon_mutex {
+ struct mutex mutex;
+ struct task_struct *owner;
+ int level;
};
+static inline void radeon_mutex_init(struct radeon_mutex *mutex)
+{
+ mutex_init(&mutex->mutex);
+ mutex->owner = NULL;
+ mutex->level = 0;
+}
+
+static inline void radeon_mutex_lock(struct radeon_mutex *mutex)
+{
+ if (mutex_trylock(&mutex->mutex)) {
+ /* The mutex was unlocked before, so it's ours now */
+ mutex->owner = current;
+ } else if (mutex->owner != current) {
+ /* Another process locked the mutex, take it */
+ mutex_lock(&mutex->mutex);
+ mutex->owner = current;
+ }
+ /* Otherwise the mutex was already locked by this process */
+
+ mutex->level++;
+}
+
+static inline void radeon_mutex_unlock(struct radeon_mutex *mutex)
+{
+ if (--mutex->level > 0)
+ return;
+
+ mutex->owner = NULL;
+ mutex_unlock(&mutex->mutex);
+}
+
+
/*
* Core structure, functions and helpers.
*/
@@ -1174,7 +1214,7 @@ struct radeon_device {
/* Register mmio */
resource_size_t rmmio_base;
resource_size_t rmmio_size;
- void *rmmio;
+ void __iomem *rmmio;
radeon_rreg_t mc_rreg;
radeon_wreg_t mc_wreg;
radeon_rreg_t pll_rreg;
@@ -1202,7 +1242,7 @@ struct radeon_device {
struct radeon_gem gem;
struct radeon_pm pm;
uint32_t bios_scratch[RADEON_BIOS_NUM_SCRATCH];
- struct mutex cs_mutex;
+ struct radeon_mutex cs_mutex;
struct radeon_wb wb;
struct radeon_dummy_page dummy_page;
bool gpu_lockup;
@@ -1216,7 +1256,7 @@ struct radeon_device {
const struct firmware *rlc_fw; /* r6/700 RLC firmware */
const struct firmware *mc_fw; /* NI MC firmware */
struct r600_blit r600_blit;
- struct r700_vram_scratch vram_scratch;
+ struct r600_vram_scratch vram_scratch;
int msi_enabled; /* msi enabled */
struct r600_ih ih; /* r6/700 interrupt ring */
struct work_struct hotplug_work;
@@ -1248,45 +1288,10 @@ int radeon_device_init(struct radeon_device *rdev,
void radeon_device_fini(struct radeon_device *rdev);
int radeon_gpu_wait_for_idle(struct radeon_device *rdev);
-static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg)
-{
- if (reg < rdev->rmmio_size)
- return readl(((void __iomem *)rdev->rmmio) + reg);
- else {
- writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
- return readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
- }
-}
-
-static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
-{
- if (reg < rdev->rmmio_size)
- writel(v, ((void __iomem *)rdev->rmmio) + reg);
- else {
- writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
- writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
- }
-}
-
-static inline u32 r100_io_rreg(struct radeon_device *rdev, u32 reg)
-{
- if (reg < rdev->rio_mem_size)
- return ioread32(rdev->rio_mem + reg);
- else {
- iowrite32(reg, rdev->rio_mem + RADEON_MM_INDEX);
- return ioread32(rdev->rio_mem + RADEON_MM_DATA);
- }
-}
-
-static inline void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v)
-{
- if (reg < rdev->rio_mem_size)
- iowrite32(v, rdev->rio_mem + reg);
- else {
- iowrite32(reg, rdev->rio_mem + RADEON_MM_INDEX);
- iowrite32(v, rdev->rio_mem + RADEON_MM_DATA);
- }
-}
+uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg);
+void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
+u32 r100_io_rreg(struct radeon_device *rdev, u32 reg);
+void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v);
/*
* Cast helper
@@ -1296,10 +1301,10 @@ static inline void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v)
/*
* Registers read & write functions.
*/
-#define RREG8(reg) readb(((void __iomem *)rdev->rmmio) + (reg))
-#define WREG8(reg, v) writeb(v, ((void __iomem *)rdev->rmmio) + (reg))
-#define RREG16(reg) readw(((void __iomem *)rdev->rmmio) + (reg))
-#define WREG16(reg, v) writew(v, ((void __iomem *)rdev->rmmio) + (reg))
+#define RREG8(reg) readb((rdev->rmmio) + (reg))
+#define WREG8(reg, v) writeb(v, (rdev->rmmio) + (reg))
+#define RREG16(reg) readw((rdev->rmmio) + (reg))
+#define WREG16(reg, v) writew(v, (rdev->rmmio) + (reg))
#define RREG32(reg) r100_mm_rreg(rdev, (reg))
#define DREG32(reg) printk(KERN_INFO "REGISTER: " #reg " : 0x%08X\n", r100_mm_rreg(rdev, (reg)))
#define WREG32(reg, v) r100_mm_wreg(rdev, (reg), (v))
@@ -1409,19 +1414,19 @@ void radeon_atombios_fini(struct radeon_device *rdev);
/*
* RING helpers.
*/
+
+#if DRM_DEBUG_CODE == 0
static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
{
-#if DRM_DEBUG_CODE
- if (rdev->cp.count_dw <= 0) {
- DRM_ERROR("radeon: writting more dword to ring than expected !\n");
- }
-#endif
rdev->cp.ring[rdev->cp.wptr++] = v;
rdev->cp.wptr &= rdev->cp.ptr_mask;
rdev->cp.count_dw--;
rdev->cp.ring_free_dw--;
}
-
+#else
+/* With debugging this is just too big to inline */
+void radeon_ring_write(struct radeon_device *rdev, uint32_t v);
+#endif
/*
* ASICs macro.
@@ -1475,8 +1480,6 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
/* AGP */
extern int radeon_gpu_reset(struct radeon_device *rdev);
extern void radeon_agp_disable(struct radeon_device *rdev);
-extern int radeon_gart_table_vram_pin(struct radeon_device *rdev);
-extern void radeon_gart_restore(struct radeon_device *rdev);
extern int radeon_modeset_init(struct radeon_device *rdev);
extern void radeon_modeset_fini(struct radeon_device *rdev);
extern bool radeon_card_posted(struct radeon_device *rdev);
@@ -1500,6 +1503,12 @@ extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state);
extern void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size);
/*
+ * R600 vram scratch functions
+ */
+int r600_vram_scratch_init(struct radeon_device *rdev);
+void r600_vram_scratch_fini(struct radeon_device *rdev);
+
+/*
* r600 functions used by radeon_encoder.c
*/
extern void r600_hdmi_enable(struct drm_encoder *encoder);
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index b2449629537d..a2e1eae114ef 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -625,7 +625,7 @@ static struct radeon_asic r600_asic = {
.fence_ring_emit = &r600_fence_ring_emit,
.cs_parse = &r600_cs_parse,
.copy_blit = &r600_copy_blit,
- .copy_dma = &r600_copy_blit,
+ .copy_dma = NULL,
.copy = &r600_copy_blit,
.get_engine_clock = &radeon_atom_get_engine_clock,
.set_engine_clock = &radeon_atom_set_engine_clock,
@@ -672,7 +672,7 @@ static struct radeon_asic rs780_asic = {
.fence_ring_emit = &r600_fence_ring_emit,
.cs_parse = &r600_cs_parse,
.copy_blit = &r600_copy_blit,
- .copy_dma = &r600_copy_blit,
+ .copy_dma = NULL,
.copy = &r600_copy_blit,
.get_engine_clock = &radeon_atom_get_engine_clock,
.set_engine_clock = &radeon_atom_set_engine_clock,
@@ -719,7 +719,7 @@ static struct radeon_asic rv770_asic = {
.fence_ring_emit = &r600_fence_ring_emit,
.cs_parse = &r600_cs_parse,
.copy_blit = &r600_copy_blit,
- .copy_dma = &r600_copy_blit,
+ .copy_dma = NULL,
.copy = &r600_copy_blit,
.get_engine_clock = &radeon_atom_get_engine_clock,
.set_engine_clock = &radeon_atom_set_engine_clock,
@@ -765,9 +765,9 @@ static struct radeon_asic evergreen_asic = {
.get_vblank_counter = &evergreen_get_vblank_counter,
.fence_ring_emit = &r600_fence_ring_emit,
.cs_parse = &evergreen_cs_parse,
- .copy_blit = &evergreen_copy_blit,
- .copy_dma = &evergreen_copy_blit,
- .copy = &evergreen_copy_blit,
+ .copy_blit = &r600_copy_blit,
+ .copy_dma = NULL,
+ .copy = &r600_copy_blit,
.get_engine_clock = &radeon_atom_get_engine_clock,
.set_engine_clock = &radeon_atom_set_engine_clock,
.get_memory_clock = &radeon_atom_get_memory_clock,
@@ -812,9 +812,9 @@ static struct radeon_asic sumo_asic = {
.get_vblank_counter = &evergreen_get_vblank_counter,
.fence_ring_emit = &r600_fence_ring_emit,
.cs_parse = &evergreen_cs_parse,
- .copy_blit = &evergreen_copy_blit,
- .copy_dma = &evergreen_copy_blit,
- .copy = &evergreen_copy_blit,
+ .copy_blit = &r600_copy_blit,
+ .copy_dma = NULL,
+ .copy = &r600_copy_blit,
.get_engine_clock = &radeon_atom_get_engine_clock,
.set_engine_clock = &radeon_atom_set_engine_clock,
.get_memory_clock = NULL,
@@ -834,7 +834,7 @@ static struct radeon_asic sumo_asic = {
.pm_misc = &evergreen_pm_misc,
.pm_prepare = &evergreen_pm_prepare,
.pm_finish = &evergreen_pm_finish,
- .pm_init_profile = &rs780_pm_init_profile,
+ .pm_init_profile = &sumo_pm_init_profile,
.pm_get_dynpm_state = &r600_pm_get_dynpm_state,
.pre_page_flip = &evergreen_pre_page_flip,
.page_flip = &evergreen_page_flip,
@@ -859,9 +859,9 @@ static struct radeon_asic btc_asic = {
.get_vblank_counter = &evergreen_get_vblank_counter,
.fence_ring_emit = &r600_fence_ring_emit,
.cs_parse = &evergreen_cs_parse,
- .copy_blit = &evergreen_copy_blit,
- .copy_dma = &evergreen_copy_blit,
- .copy = &evergreen_copy_blit,
+ .copy_blit = &r600_copy_blit,
+ .copy_dma = NULL,
+ .copy = &r600_copy_blit,
.get_engine_clock = &radeon_atom_get_engine_clock,
.set_engine_clock = &radeon_atom_set_engine_clock,
.get_memory_clock = &radeon_atom_get_memory_clock,
@@ -906,9 +906,9 @@ static struct radeon_asic cayman_asic = {
.get_vblank_counter = &evergreen_get_vblank_counter,
.fence_ring_emit = &r600_fence_ring_emit,
.cs_parse = &evergreen_cs_parse,
- .copy_blit = &evergreen_copy_blit,
- .copy_dma = &evergreen_copy_blit,
- .copy = &evergreen_copy_blit,
+ .copy_blit = &r600_copy_blit,
+ .copy_dma = NULL,
+ .copy = &r600_copy_blit,
.get_engine_clock = &radeon_atom_get_engine_clock,
.set_engine_clock = &radeon_atom_set_engine_clock,
.get_memory_clock = &radeon_atom_get_memory_clock,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index 3d7a0d7c6a9a..59914842a729 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -75,7 +75,7 @@ uint32_t r100_pll_rreg(struct radeon_device *rdev, uint32_t reg);
int r100_copy_blit(struct radeon_device *rdev,
uint64_t src_offset,
uint64_t dst_offset,
- unsigned num_pages,
+ unsigned num_gpu_pages,
struct radeon_fence *fence);
int r100_set_surface_reg(struct radeon_device *rdev, int reg,
uint32_t tiling_flags, uint32_t pitch,
@@ -143,7 +143,7 @@ extern void r100_post_page_flip(struct radeon_device *rdev, int crtc);
extern int r200_copy_dma(struct radeon_device *rdev,
uint64_t src_offset,
uint64_t dst_offset,
- unsigned num_pages,
+ unsigned num_gpu_pages,
struct radeon_fence *fence);
void r200_set_safe_registers(struct radeon_device *rdev);
@@ -311,7 +311,7 @@ void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
int r600_ring_test(struct radeon_device *rdev);
int r600_copy_blit(struct radeon_device *rdev,
uint64_t src_offset, uint64_t dst_offset,
- unsigned num_pages, struct radeon_fence *fence);
+ unsigned num_gpu_pages, struct radeon_fence *fence);
void r600_hpd_init(struct radeon_device *rdev);
void r600_hpd_fini(struct radeon_device *rdev);
bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
@@ -364,11 +364,11 @@ void r600_hdmi_init(struct drm_encoder *encoder);
int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder);
void r600_hdmi_update_audio_settings(struct drm_encoder *encoder);
/* r600 blit */
-int r600_blit_prepare_copy(struct radeon_device *rdev, int size_bytes);
+int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages);
void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence);
void r600_kms_blit_copy(struct radeon_device *rdev,
u64 src_gpu_addr, u64 dst_gpu_addr,
- int size_bytes);
+ unsigned num_gpu_pages);
/*
* rv770,rv730,rv710,rv740
@@ -401,9 +401,6 @@ bool evergreen_gpu_is_lockup(struct radeon_device *rdev);
int evergreen_asic_reset(struct radeon_device *rdev);
void evergreen_bandwidth_update(struct radeon_device *rdev);
void evergreen_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
-int evergreen_copy_blit(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_pages, struct radeon_fence *fence);
void evergreen_hpd_init(struct radeon_device *rdev);
void evergreen_hpd_fini(struct radeon_device *rdev);
bool evergreen_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
@@ -416,18 +413,12 @@ extern int evergreen_cs_parse(struct radeon_cs_parser *p);
extern void evergreen_pm_misc(struct radeon_device *rdev);
extern void evergreen_pm_prepare(struct radeon_device *rdev);
extern void evergreen_pm_finish(struct radeon_device *rdev);
+extern void sumo_pm_init_profile(struct radeon_device *rdev);
extern void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc);
extern u32 evergreen_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
extern void evergreen_post_page_flip(struct radeon_device *rdev, int crtc);
void evergreen_disable_interrupt_state(struct radeon_device *rdev);
int evergreen_blit_init(struct radeon_device *rdev);
-void evergreen_blit_fini(struct radeon_device *rdev);
-/* evergreen blit */
-int evergreen_blit_prepare_copy(struct radeon_device *rdev, int size_bytes);
-void evergreen_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence);
-void evergreen_kms_blit_copy(struct radeon_device *rdev,
- u64 src_gpu_addr, u64 dst_gpu_addr,
- int size_bytes);
/*
* cayman
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index bf2b61584cdb..d24baf30efcb 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -62,7 +62,88 @@ union atom_supported_devices {
struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1;
};
-static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_device *rdev,
+static void radeon_lookup_i2c_gpio_quirks(struct radeon_device *rdev,
+ ATOM_GPIO_I2C_ASSIGMENT *gpio,
+ u8 index)
+{
+ /* r4xx mask is technically not used by the hw, so patch in the legacy mask bits */
+ if ((rdev->family == CHIP_R420) ||
+ (rdev->family == CHIP_R423) ||
+ (rdev->family == CHIP_RV410)) {
+ if ((le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x0018) ||
+ (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x0019) ||
+ (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x001a)) {
+ gpio->ucClkMaskShift = 0x19;
+ gpio->ucDataMaskShift = 0x18;
+ }
+ }
+
+ /* some evergreen boards have bad data for this entry */
+ if (ASIC_IS_DCE4(rdev)) {
+ if ((index == 7) &&
+ (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1936) &&
+ (gpio->sucI2cId.ucAccess == 0)) {
+ gpio->sucI2cId.ucAccess = 0x97;
+ gpio->ucDataMaskShift = 8;
+ gpio->ucDataEnShift = 8;
+ gpio->ucDataY_Shift = 8;
+ gpio->ucDataA_Shift = 8;
+ }
+ }
+
+ /* some DCE3 boards have bad data for this entry */
+ if (ASIC_IS_DCE3(rdev)) {
+ if ((index == 4) &&
+ (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1fda) &&
+ (gpio->sucI2cId.ucAccess == 0x94))
+ gpio->sucI2cId.ucAccess = 0x14;
+ }
+}
+
+static struct radeon_i2c_bus_rec radeon_get_bus_rec_for_i2c_gpio(ATOM_GPIO_I2C_ASSIGMENT *gpio)
+{
+ struct radeon_i2c_bus_rec i2c;
+
+ memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec));
+
+ i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4;
+ i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4;
+ i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4;
+ i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4;
+ i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4;
+ i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4;
+ i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4;
+ i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4;
+ i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift);
+ i2c.mask_data_mask = (1 << gpio->ucDataMaskShift);
+ i2c.en_clk_mask = (1 << gpio->ucClkEnShift);
+ i2c.en_data_mask = (1 << gpio->ucDataEnShift);
+ i2c.y_clk_mask = (1 << gpio->ucClkY_Shift);
+ i2c.y_data_mask = (1 << gpio->ucDataY_Shift);
+ i2c.a_clk_mask = (1 << gpio->ucClkA_Shift);
+ i2c.a_data_mask = (1 << gpio->ucDataA_Shift);
+
+ if (gpio->sucI2cId.sbfAccess.bfHW_Capable)
+ i2c.hw_capable = true;
+ else
+ i2c.hw_capable = false;
+
+ if (gpio->sucI2cId.ucAccess == 0xa0)
+ i2c.mm_i2c = true;
+ else
+ i2c.mm_i2c = false;
+
+ i2c.i2c_id = gpio->sucI2cId.ucAccess;
+
+ if (i2c.mask_clk_reg)
+ i2c.valid = true;
+ else
+ i2c.valid = false;
+
+ return i2c;
+}
+
+static struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_device *rdev,
uint8_t id)
{
struct atom_context *ctx = rdev->mode_info.atom_context;
@@ -85,59 +166,10 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_dev
for (i = 0; i < num_indices; i++) {
gpio = &i2c_info->asGPIO_Info[i];
- /* some evergreen boards have bad data for this entry */
- if (ASIC_IS_DCE4(rdev)) {
- if ((i == 7) &&
- (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1936) &&
- (gpio->sucI2cId.ucAccess == 0)) {
- gpio->sucI2cId.ucAccess = 0x97;
- gpio->ucDataMaskShift = 8;
- gpio->ucDataEnShift = 8;
- gpio->ucDataY_Shift = 8;
- gpio->ucDataA_Shift = 8;
- }
- }
-
- /* some DCE3 boards have bad data for this entry */
- if (ASIC_IS_DCE3(rdev)) {
- if ((i == 4) &&
- (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1fda) &&
- (gpio->sucI2cId.ucAccess == 0x94))
- gpio->sucI2cId.ucAccess = 0x14;
- }
+ radeon_lookup_i2c_gpio_quirks(rdev, gpio, i);
if (gpio->sucI2cId.ucAccess == id) {
- i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4;
- i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4;
- i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4;
- i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4;
- i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4;
- i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4;
- i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4;
- i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4;
- i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift);
- i2c.mask_data_mask = (1 << gpio->ucDataMaskShift);
- i2c.en_clk_mask = (1 << gpio->ucClkEnShift);
- i2c.en_data_mask = (1 << gpio->ucDataEnShift);
- i2c.y_clk_mask = (1 << gpio->ucClkY_Shift);
- i2c.y_data_mask = (1 << gpio->ucDataY_Shift);
- i2c.a_clk_mask = (1 << gpio->ucClkA_Shift);
- i2c.a_data_mask = (1 << gpio->ucDataA_Shift);
-
- if (gpio->sucI2cId.sbfAccess.bfHW_Capable)
- i2c.hw_capable = true;
- else
- i2c.hw_capable = false;
-
- if (gpio->sucI2cId.ucAccess == 0xa0)
- i2c.mm_i2c = true;
- else
- i2c.mm_i2c = false;
-
- i2c.i2c_id = gpio->sucI2cId.ucAccess;
-
- if (i2c.mask_clk_reg)
- i2c.valid = true;
+ i2c = radeon_get_bus_rec_for_i2c_gpio(gpio);
break;
}
}
@@ -157,8 +189,6 @@ void radeon_atombios_i2c_init(struct radeon_device *rdev)
int i, num_indices;
char stmp[32];
- memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec));
-
if (atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) {
i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset);
@@ -167,60 +197,12 @@ void radeon_atombios_i2c_init(struct radeon_device *rdev)
for (i = 0; i < num_indices; i++) {
gpio = &i2c_info->asGPIO_Info[i];
- i2c.valid = false;
-
- /* some evergreen boards have bad data for this entry */
- if (ASIC_IS_DCE4(rdev)) {
- if ((i == 7) &&
- (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1936) &&
- (gpio->sucI2cId.ucAccess == 0)) {
- gpio->sucI2cId.ucAccess = 0x97;
- gpio->ucDataMaskShift = 8;
- gpio->ucDataEnShift = 8;
- gpio->ucDataY_Shift = 8;
- gpio->ucDataA_Shift = 8;
- }
- }
- /* some DCE3 boards have bad data for this entry */
- if (ASIC_IS_DCE3(rdev)) {
- if ((i == 4) &&
- (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1fda) &&
- (gpio->sucI2cId.ucAccess == 0x94))
- gpio->sucI2cId.ucAccess = 0x14;
- }
+ radeon_lookup_i2c_gpio_quirks(rdev, gpio, i);
- i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4;
- i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4;
- i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4;
- i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4;
- i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4;
- i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4;
- i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4;
- i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4;
- i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift);
- i2c.mask_data_mask = (1 << gpio->ucDataMaskShift);
- i2c.en_clk_mask = (1 << gpio->ucClkEnShift);
- i2c.en_data_mask = (1 << gpio->ucDataEnShift);
- i2c.y_clk_mask = (1 << gpio->ucClkY_Shift);
- i2c.y_data_mask = (1 << gpio->ucDataY_Shift);
- i2c.a_clk_mask = (1 << gpio->ucClkA_Shift);
- i2c.a_data_mask = (1 << gpio->ucDataA_Shift);
-
- if (gpio->sucI2cId.sbfAccess.bfHW_Capable)
- i2c.hw_capable = true;
- else
- i2c.hw_capable = false;
-
- if (gpio->sucI2cId.ucAccess == 0xa0)
- i2c.mm_i2c = true;
- else
- i2c.mm_i2c = false;
+ i2c = radeon_get_bus_rec_for_i2c_gpio(gpio);
- i2c.i2c_id = gpio->sucI2cId.ucAccess;
-
- if (i2c.mask_clk_reg) {
- i2c.valid = true;
+ if (i2c.valid) {
sprintf(stmp, "0x%x", i2c.i2c_id);
rdev->i2c_bus[i] = radeon_i2c_create(rdev->ddev, &i2c, stmp);
}
@@ -228,7 +210,7 @@ void radeon_atombios_i2c_init(struct radeon_device *rdev)
}
}
-static inline struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev,
+static struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev,
u8 id)
{
struct atom_context *ctx = rdev->mode_info.atom_context;
@@ -1996,10 +1978,14 @@ static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev)
return state_index;
/* last mode is usually default, array is low to high */
for (i = 0; i < num_modes; i++) {
+ rdev->pm.power_state[state_index].clock_info =
+ kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL);
+ if (!rdev->pm.power_state[state_index].clock_info)
+ return state_index;
+ rdev->pm.power_state[state_index].num_clock_modes = 1;
rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
switch (frev) {
case 1:
- rdev->pm.power_state[state_index].num_clock_modes = 1;
rdev->pm.power_state[state_index].clock_info[0].mclk =
le16_to_cpu(power_info->info.asPowerPlayInfo[i].usMemoryClock);
rdev->pm.power_state[state_index].clock_info[0].sclk =
@@ -2035,7 +2021,6 @@ static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev)
state_index++;
break;
case 2:
- rdev->pm.power_state[state_index].num_clock_modes = 1;
rdev->pm.power_state[state_index].clock_info[0].mclk =
le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMemoryClock);
rdev->pm.power_state[state_index].clock_info[0].sclk =
@@ -2072,7 +2057,6 @@ static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev)
state_index++;
break;
case 3:
- rdev->pm.power_state[state_index].num_clock_modes = 1;
rdev->pm.power_state[state_index].clock_info[0].mclk =
le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMemoryClock);
rdev->pm.power_state[state_index].clock_info[0].sclk =
@@ -2257,7 +2241,7 @@ static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rde
rdev->pm.default_power_state_index = state_index;
rdev->pm.power_state[state_index].default_clock_mode =
&rdev->pm.power_state[state_index].clock_info[mode_index - 1];
- if (ASIC_IS_DCE5(rdev)) {
+ if (ASIC_IS_DCE5(rdev) && !(rdev->flags & RADEON_IS_IGP)) {
/* NI chips post without MC ucode, so default clocks are strobe mode only */
rdev->pm.default_sclk = rdev->pm.power_state[state_index].clock_info[0].sclk;
rdev->pm.default_mclk = rdev->pm.power_state[state_index].clock_info[0].mclk;
@@ -2377,17 +2361,31 @@ static int radeon_atombios_parse_power_table_4_5(struct radeon_device *rdev)
le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
(power_state->v1.ucNonClockStateIndex *
power_info->pplib.ucNonClockSize));
- for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) {
- clock_info = (union pplib_clock_info *)
- (mode_info->atom_context->bios + data_offset +
- le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
- (power_state->v1.ucClockStateIndices[j] *
- power_info->pplib.ucClockInfoSize));
- valid = radeon_atombios_parse_pplib_clock_info(rdev,
- state_index, mode_index,
- clock_info);
- if (valid)
- mode_index++;
+ rdev->pm.power_state[i].clock_info = kzalloc(sizeof(struct radeon_pm_clock_info) *
+ ((power_info->pplib.ucStateEntrySize - 1) ?
+ (power_info->pplib.ucStateEntrySize - 1) : 1),
+ GFP_KERNEL);
+ if (!rdev->pm.power_state[i].clock_info)
+ return state_index;
+ if (power_info->pplib.ucStateEntrySize - 1) {
+ for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) {
+ clock_info = (union pplib_clock_info *)
+ (mode_info->atom_context->bios + data_offset +
+ le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
+ (power_state->v1.ucClockStateIndices[j] *
+ power_info->pplib.ucClockInfoSize));
+ valid = radeon_atombios_parse_pplib_clock_info(rdev,
+ state_index, mode_index,
+ clock_info);
+ if (valid)
+ mode_index++;
+ }
+ } else {
+ rdev->pm.power_state[state_index].clock_info[0].mclk =
+ rdev->clock.default_mclk;
+ rdev->pm.power_state[state_index].clock_info[0].sclk =
+ rdev->clock.default_sclk;
+ mode_index++;
}
rdev->pm.power_state[state_index].num_clock_modes = mode_index;
if (mode_index) {
@@ -2456,18 +2454,32 @@ static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev)
non_clock_array_index = i; /* power_state->v2.nonClockInfoIndex */
non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
&non_clock_info_array->nonClockInfo[non_clock_array_index];
- for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
- clock_array_index = power_state->v2.clockInfoIndex[j];
- /* XXX this might be an inagua bug... */
- if (clock_array_index >= clock_info_array->ucNumEntries)
- continue;
- clock_info = (union pplib_clock_info *)
- &clock_info_array->clockInfo[clock_array_index];
- valid = radeon_atombios_parse_pplib_clock_info(rdev,
- state_index, mode_index,
- clock_info);
- if (valid)
- mode_index++;
+ rdev->pm.power_state[i].clock_info = kzalloc(sizeof(struct radeon_pm_clock_info) *
+ (power_state->v2.ucNumDPMLevels ?
+ power_state->v2.ucNumDPMLevels : 1),
+ GFP_KERNEL);
+ if (!rdev->pm.power_state[i].clock_info)
+ return state_index;
+ if (power_state->v2.ucNumDPMLevels) {
+ for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
+ clock_array_index = power_state->v2.clockInfoIndex[j];
+ /* XXX this might be an inagua bug... */
+ if (clock_array_index >= clock_info_array->ucNumEntries)
+ continue;
+ clock_info = (union pplib_clock_info *)
+ &clock_info_array->clockInfo[clock_array_index];
+ valid = radeon_atombios_parse_pplib_clock_info(rdev,
+ state_index, mode_index,
+ clock_info);
+ if (valid)
+ mode_index++;
+ }
+ } else {
+ rdev->pm.power_state[state_index].clock_info[0].mclk =
+ rdev->clock.default_mclk;
+ rdev->pm.power_state[state_index].clock_info[0].sclk =
+ rdev->clock.default_sclk;
+ mode_index++;
}
rdev->pm.power_state[state_index].num_clock_modes = mode_index;
if (mode_index) {
@@ -2524,19 +2536,23 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev)
} else {
rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state), GFP_KERNEL);
if (rdev->pm.power_state) {
- /* add the default mode */
- rdev->pm.power_state[state_index].type =
- POWER_STATE_TYPE_DEFAULT;
- rdev->pm.power_state[state_index].num_clock_modes = 1;
- rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk;
- rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk;
- rdev->pm.power_state[state_index].default_clock_mode =
- &rdev->pm.power_state[state_index].clock_info[0];
- rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
- rdev->pm.power_state[state_index].pcie_lanes = 16;
- rdev->pm.default_power_state_index = state_index;
- rdev->pm.power_state[state_index].flags = 0;
- state_index++;
+ rdev->pm.power_state[0].clock_info =
+ kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL);
+ if (rdev->pm.power_state[0].clock_info) {
+ /* add the default mode */
+ rdev->pm.power_state[state_index].type =
+ POWER_STATE_TYPE_DEFAULT;
+ rdev->pm.power_state[state_index].num_clock_modes = 1;
+ rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk;
+ rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk;
+ rdev->pm.power_state[state_index].default_clock_mode =
+ &rdev->pm.power_state[state_index].clock_info[0];
+ rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
+ rdev->pm.power_state[state_index].pcie_lanes = 16;
+ rdev->pm.default_power_state_index = state_index;
+ rdev->pm.power_state[state_index].flags = 0;
+ state_index++;
+ }
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c
index 10191d9372d8..17e1a9b2d8fb 100644
--- a/drivers/gpu/drm/radeon/radeon_benchmark.c
+++ b/drivers/gpu/drm/radeon/radeon_benchmark.c
@@ -26,21 +26,81 @@
#include "radeon_reg.h"
#include "radeon.h"
-void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
- unsigned sdomain, unsigned ddomain)
+#define RADEON_BENCHMARK_COPY_BLIT 1
+#define RADEON_BENCHMARK_COPY_DMA 0
+
+#define RADEON_BENCHMARK_ITERATIONS 1024
+#define RADEON_BENCHMARK_COMMON_MODES_N 17
+
+static int radeon_benchmark_do_move(struct radeon_device *rdev, unsigned size,
+ uint64_t saddr, uint64_t daddr,
+ int flag, int n)
+{
+ unsigned long start_jiffies;
+ unsigned long end_jiffies;
+ struct radeon_fence *fence = NULL;
+ int i, r;
+
+ start_jiffies = jiffies;
+ for (i = 0; i < n; i++) {
+ r = radeon_fence_create(rdev, &fence);
+ if (r)
+ return r;
+
+ switch (flag) {
+ case RADEON_BENCHMARK_COPY_DMA:
+ r = radeon_copy_dma(rdev, saddr, daddr,
+ size / RADEON_GPU_PAGE_SIZE,
+ fence);
+ break;
+ case RADEON_BENCHMARK_COPY_BLIT:
+ r = radeon_copy_blit(rdev, saddr, daddr,
+ size / RADEON_GPU_PAGE_SIZE,
+ fence);
+ break;
+ default:
+ DRM_ERROR("Unknown copy method\n");
+ r = -EINVAL;
+ }
+ if (r)
+ goto exit_do_move;
+ r = radeon_fence_wait(fence, false);
+ if (r)
+ goto exit_do_move;
+ radeon_fence_unref(&fence);
+ }
+ end_jiffies = jiffies;
+ r = jiffies_to_msecs(end_jiffies - start_jiffies);
+
+exit_do_move:
+ if (fence)
+ radeon_fence_unref(&fence);
+ return r;
+}
+
+
+static void radeon_benchmark_log_results(int n, unsigned size,
+ unsigned int time,
+ unsigned sdomain, unsigned ddomain,
+ char *kind)
+{
+ unsigned int throughput = (n * (size >> 10)) / time;
+ DRM_INFO("radeon: %s %u bo moves of %u kB from"
+ " %d to %d in %u ms, throughput: %u Mb/s or %u MB/s\n",
+ kind, n, size >> 10, sdomain, ddomain, time,
+ throughput * 8, throughput);
+}
+
+static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size,
+ unsigned sdomain, unsigned ddomain)
{
struct radeon_bo *dobj = NULL;
struct radeon_bo *sobj = NULL;
- struct radeon_fence *fence = NULL;
uint64_t saddr, daddr;
- unsigned long start_jiffies;
- unsigned long end_jiffies;
- unsigned long time;
- unsigned i, n, size;
- int r;
+ int r, n;
+ int time;
- size = bsize;
- n = 1024;
+ n = RADEON_BENCHMARK_ITERATIONS;
r = radeon_bo_create(rdev, size, PAGE_SIZE, true, sdomain, &sobj);
if (r) {
goto out_cleanup;
@@ -67,65 +127,26 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
}
/* r100 doesn't have dma engine so skip the test */
- if (rdev->asic->copy_dma) {
-
- start_jiffies = jiffies;
- for (i = 0; i < n; i++) {
- r = radeon_fence_create(rdev, &fence);
- if (r) {
- goto out_cleanup;
- }
-
- r = radeon_copy_dma(rdev, saddr, daddr,
- size / RADEON_GPU_PAGE_SIZE, fence);
-
- if (r) {
- goto out_cleanup;
- }
- r = radeon_fence_wait(fence, false);
- if (r) {
- goto out_cleanup;
- }
- radeon_fence_unref(&fence);
- }
- end_jiffies = jiffies;
- time = end_jiffies - start_jiffies;
- time = jiffies_to_msecs(time);
- if (time > 0) {
- i = ((n * size) >> 10) / time;
- printk(KERN_INFO "radeon: dma %u bo moves of %ukb from"
- " %d to %d in %lums (%ukb/ms %ukb/s %uM/s)\n",
- n, size >> 10,
- sdomain, ddomain, time,
- i, i * 1000, (i * 1000) / 1024);
- }
- }
-
- start_jiffies = jiffies;
- for (i = 0; i < n; i++) {
- r = radeon_fence_create(rdev, &fence);
- if (r) {
- goto out_cleanup;
- }
- r = radeon_copy_blit(rdev, saddr, daddr, size / RADEON_GPU_PAGE_SIZE, fence);
- if (r) {
- goto out_cleanup;
- }
- r = radeon_fence_wait(fence, false);
- if (r) {
+ /* also, VRAM-to-VRAM test doesn't make much sense for DMA */
+ /* skip it as well if domains are the same */
+ if ((rdev->asic->copy_dma) && (sdomain != ddomain)) {
+ time = radeon_benchmark_do_move(rdev, size, saddr, daddr,
+ RADEON_BENCHMARK_COPY_DMA, n);
+ if (time < 0)
goto out_cleanup;
- }
- radeon_fence_unref(&fence);
- }
- end_jiffies = jiffies;
- time = end_jiffies - start_jiffies;
- time = jiffies_to_msecs(time);
- if (time > 0) {
- i = ((n * size) >> 10) / time;
- printk(KERN_INFO "radeon: blit %u bo moves of %ukb from %d to %d"
- " in %lums (%ukb/ms %ukb/s %uM/s)\n", n, size >> 10,
- sdomain, ddomain, time, i, i * 1000, (i * 1000) / 1024);
+ if (time > 0)
+ radeon_benchmark_log_results(n, size, time,
+ sdomain, ddomain, "dma");
}
+
+ time = radeon_benchmark_do_move(rdev, size, saddr, daddr,
+ RADEON_BENCHMARK_COPY_BLIT, n);
+ if (time < 0)
+ goto out_cleanup;
+ if (time > 0)
+ radeon_benchmark_log_results(n, size, time,
+ sdomain, ddomain, "blit");
+
out_cleanup:
if (sobj) {
r = radeon_bo_reserve(sobj, false);
@@ -143,18 +164,92 @@ out_cleanup:
}
radeon_bo_unref(&dobj);
}
- if (fence) {
- radeon_fence_unref(&fence);
- }
+
if (r) {
- printk(KERN_WARNING "Error while benchmarking BO move.\n");
+ DRM_ERROR("Error while benchmarking BO move.\n");
}
}
-void radeon_benchmark(struct radeon_device *rdev)
+void radeon_benchmark(struct radeon_device *rdev, int test_number)
{
- radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_GTT,
- RADEON_GEM_DOMAIN_VRAM);
- radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_VRAM,
- RADEON_GEM_DOMAIN_GTT);
+ int i;
+ int common_modes[RADEON_BENCHMARK_COMMON_MODES_N] = {
+ 640 * 480 * 4,
+ 720 * 480 * 4,
+ 800 * 600 * 4,
+ 848 * 480 * 4,
+ 1024 * 768 * 4,
+ 1152 * 768 * 4,
+ 1280 * 720 * 4,
+ 1280 * 800 * 4,
+ 1280 * 854 * 4,
+ 1280 * 960 * 4,
+ 1280 * 1024 * 4,
+ 1440 * 900 * 4,
+ 1400 * 1050 * 4,
+ 1680 * 1050 * 4,
+ 1600 * 1200 * 4,
+ 1920 * 1080 * 4,
+ 1920 * 1200 * 4
+ };
+
+ switch (test_number) {
+ case 1:
+ /* simple test, VRAM to GTT and GTT to VRAM */
+ radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_GTT,
+ RADEON_GEM_DOMAIN_VRAM);
+ radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_VRAM,
+ RADEON_GEM_DOMAIN_GTT);
+ break;
+ case 2:
+ /* simple test, VRAM to VRAM */
+ radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_VRAM,
+ RADEON_GEM_DOMAIN_VRAM);
+ break;
+ case 3:
+ /* GTT to VRAM, buffer size sweep, powers of 2 */
+ for (i = 1; i <= 65536; i <<= 1)
+ radeon_benchmark_move(rdev, i*1024,
+ RADEON_GEM_DOMAIN_GTT,
+ RADEON_GEM_DOMAIN_VRAM);
+ break;
+ case 4:
+ /* VRAM to GTT, buffer size sweep, powers of 2 */
+ for (i = 1; i <= 65536; i <<= 1)
+ radeon_benchmark_move(rdev, i*1024,
+ RADEON_GEM_DOMAIN_VRAM,
+ RADEON_GEM_DOMAIN_GTT);
+ break;
+ case 5:
+ /* VRAM to VRAM, buffer size sweep, powers of 2 */
+ for (i = 1; i <= 65536; i <<= 1)
+ radeon_benchmark_move(rdev, i*1024,
+ RADEON_GEM_DOMAIN_VRAM,
+ RADEON_GEM_DOMAIN_VRAM);
+ break;
+ case 6:
+ /* GTT to VRAM, buffer size sweep, common modes */
+ for (i = 1; i < RADEON_BENCHMARK_COMMON_MODES_N; i++)
+ radeon_benchmark_move(rdev, common_modes[i],
+ RADEON_GEM_DOMAIN_GTT,
+ RADEON_GEM_DOMAIN_VRAM);
+ break;
+ case 7:
+ /* VRAM to GTT, buffer size sweep, common modes */
+ for (i = 1; i < RADEON_BENCHMARK_COMMON_MODES_N; i++)
+ radeon_benchmark_move(rdev, common_modes[i],
+ RADEON_GEM_DOMAIN_VRAM,
+ RADEON_GEM_DOMAIN_GTT);
+ break;
+ case 8:
+ /* VRAM to VRAM, buffer size sweep, common modes */
+ for (i = 1; i < RADEON_BENCHMARK_COMMON_MODES_N; i++)
+ radeon_benchmark_move(rdev, common_modes[i],
+ RADEON_GEM_DOMAIN_VRAM,
+ RADEON_GEM_DOMAIN_VRAM);
+ break;
+
+ default:
+ DRM_ERROR("Unknown benchmark\n");
+ }
}
diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c
index 3fc5fa1aefd0..229a20f10e2b 100644
--- a/drivers/gpu/drm/radeon/radeon_bios.c
+++ b/drivers/gpu/drm/radeon/radeon_bios.c
@@ -331,7 +331,7 @@ static bool avivo_read_disabled_bios(struct radeon_device *rdev)
seprom_cntl1 = RREG32(RADEON_SEPROM_CNTL1);
viph_control = RREG32(RADEON_VIPH_CONTROL);
- bus_cntl = RREG32(RADEON_BUS_CNTL);
+ bus_cntl = RREG32(RV370_BUS_CNTL);
d1vga_control = RREG32(AVIVO_D1VGA_CONTROL);
d2vga_control = RREG32(AVIVO_D2VGA_CONTROL);
vga_render_control = RREG32(AVIVO_VGA_RENDER_CONTROL);
@@ -350,7 +350,7 @@ static bool avivo_read_disabled_bios(struct radeon_device *rdev)
WREG32(RADEON_VIPH_CONTROL, (viph_control & ~RADEON_VIPH_EN));
/* enable the rom */
- WREG32(RADEON_BUS_CNTL, (bus_cntl & ~RADEON_BUS_BIOS_DIS_ROM));
+ WREG32(RV370_BUS_CNTL, (bus_cntl & ~RV370_BUS_BIOS_DIS_ROM));
/* Disable VGA mode */
WREG32(AVIVO_D1VGA_CONTROL,
@@ -367,7 +367,7 @@ static bool avivo_read_disabled_bios(struct radeon_device *rdev)
/* restore regs */
WREG32(RADEON_SEPROM_CNTL1, seprom_cntl1);
WREG32(RADEON_VIPH_CONTROL, viph_control);
- WREG32(RADEON_BUS_CNTL, bus_cntl);
+ WREG32(RV370_BUS_CNTL, bus_cntl);
WREG32(AVIVO_D1VGA_CONTROL, d1vga_control);
WREG32(AVIVO_D2VGA_CONTROL, d2vga_control);
WREG32(AVIVO_VGA_RENDER_CONTROL, vga_render_control);
@@ -390,7 +390,10 @@ static bool legacy_read_disabled_bios(struct radeon_device *rdev)
seprom_cntl1 = RREG32(RADEON_SEPROM_CNTL1);
viph_control = RREG32(RADEON_VIPH_CONTROL);
- bus_cntl = RREG32(RADEON_BUS_CNTL);
+ if (rdev->flags & RADEON_IS_PCIE)
+ bus_cntl = RREG32(RV370_BUS_CNTL);
+ else
+ bus_cntl = RREG32(RADEON_BUS_CNTL);
crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL);
crtc2_gen_cntl = 0;
crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL);
@@ -412,7 +415,10 @@ static bool legacy_read_disabled_bios(struct radeon_device *rdev)
WREG32(RADEON_VIPH_CONTROL, (viph_control & ~RADEON_VIPH_EN));
/* enable the rom */
- WREG32(RADEON_BUS_CNTL, (bus_cntl & ~RADEON_BUS_BIOS_DIS_ROM));
+ if (rdev->flags & RADEON_IS_PCIE)
+ WREG32(RV370_BUS_CNTL, (bus_cntl & ~RV370_BUS_BIOS_DIS_ROM));
+ else
+ WREG32(RADEON_BUS_CNTL, (bus_cntl & ~RADEON_BUS_BIOS_DIS_ROM));
/* Turn off mem requests and CRTC for both controllers */
WREG32(RADEON_CRTC_GEN_CNTL,
@@ -439,7 +445,10 @@ static bool legacy_read_disabled_bios(struct radeon_device *rdev)
/* restore regs */
WREG32(RADEON_SEPROM_CNTL1, seprom_cntl1);
WREG32(RADEON_VIPH_CONTROL, viph_control);
- WREG32(RADEON_BUS_CNTL, bus_cntl);
+ if (rdev->flags & RADEON_IS_PCIE)
+ WREG32(RV370_BUS_CNTL, bus_cntl);
+ else
+ WREG32(RADEON_BUS_CNTL, bus_cntl);
WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl);
if (!(rdev->flags & RADEON_SINGLE_CRTC)) {
WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl);
diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c
index 2d48e7a1474b..b6e18c8db9f5 100644
--- a/drivers/gpu/drm/radeon/radeon_clocks.c
+++ b/drivers/gpu/drm/radeon/radeon_clocks.c
@@ -96,7 +96,7 @@ uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
* Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
* tree. Hopefully, ATI OF driver is kind enough to fill these
*/
-static bool __devinit radeon_read_clocks_OF(struct drm_device *dev)
+static bool radeon_read_clocks_OF(struct drm_device *dev)
{
struct radeon_device *rdev = dev->dev_private;
struct device_node *dp = rdev->pdev->dev.of_node;
@@ -166,7 +166,7 @@ static bool __devinit radeon_read_clocks_OF(struct drm_device *dev)
return true;
}
#else
-static bool __devinit radeon_read_clocks_OF(struct drm_device *dev)
+static bool radeon_read_clocks_OF(struct drm_device *dev)
{
return false;
}
@@ -219,6 +219,9 @@ void radeon_get_clock_info(struct drm_device *dev)
} else {
DRM_INFO("Using generic clock info\n");
+ /* may need to be per card */
+ rdev->clock.max_pixel_clock = 35000;
+
if (rdev->flags & RADEON_IS_IGP) {
p1pll->reference_freq = 1432;
p2pll->reference_freq = 1432;
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index e4594676a07c..81fc100be7e1 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -620,8 +620,8 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
i2c.y_data_mask = 0x80;
} else {
/* default masks for ddc pads */
- i2c.mask_clk_mask = RADEON_GPIO_EN_1;
- i2c.mask_data_mask = RADEON_GPIO_EN_0;
+ i2c.mask_clk_mask = RADEON_GPIO_MASK_1;
+ i2c.mask_data_mask = RADEON_GPIO_MASK_0;
i2c.a_clk_mask = RADEON_GPIO_A_1;
i2c.a_data_mask = RADEON_GPIO_A_0;
i2c.en_clk_mask = RADEON_GPIO_EN_1;
@@ -779,7 +779,8 @@ void radeon_combios_i2c_init(struct radeon_device *rdev)
}
}
}
- } else if (rdev->family >= CHIP_R200) {
+ } else if ((rdev->family == CHIP_R200) ||
+ (rdev->family >= CHIP_R300)) {
/* 0x68 */
i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
@@ -2556,25 +2557,28 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev)
u16 offset, misc, misc2 = 0;
u8 rev, blocks, tmp;
int state_index = 0;
+ struct radeon_i2c_bus_rec i2c_bus;
rdev->pm.default_power_state_index = -1;
/* allocate 2 power states */
rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) * 2, GFP_KERNEL);
- if (!rdev->pm.power_state) {
- rdev->pm.default_power_state_index = state_index;
- rdev->pm.num_power_states = 0;
-
- rdev->pm.current_power_state_index = rdev->pm.default_power_state_index;
- rdev->pm.current_clock_mode_index = 0;
- return;
- }
+ if (rdev->pm.power_state) {
+ /* allocate 1 clock mode per state */
+ rdev->pm.power_state[0].clock_info =
+ kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL);
+ rdev->pm.power_state[1].clock_info =
+ kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL);
+ if (!rdev->pm.power_state[0].clock_info ||
+ !rdev->pm.power_state[1].clock_info)
+ goto pm_failed;
+ } else
+ goto pm_failed;
/* check for a thermal chip */
offset = combios_get_table_offset(dev, COMBIOS_OVERDRIVE_INFO_TABLE);
if (offset) {
u8 thermal_controller = 0, gpio = 0, i2c_addr = 0, clk_bit = 0, data_bit = 0;
- struct radeon_i2c_bus_rec i2c_bus;
rev = RBIOS8(offset);
@@ -2616,6 +2620,25 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev)
i2c_new_device(&rdev->pm.i2c_bus->adapter, &info);
}
}
+ } else {
+ /* boards with a thermal chip, but no overdrive table */
+
+ /* Asus 9600xt has an f75375 on the monid bus */
+ if ((dev->pdev->device == 0x4152) &&
+ (dev->pdev->subsystem_vendor == 0x1043) &&
+ (dev->pdev->subsystem_device == 0xc002)) {
+ i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+ rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
+ if (rdev->pm.i2c_bus) {
+ struct i2c_board_info info = { };
+ const char *name = "f75375";
+ info.addr = 0x28;
+ strlcpy(info.type, name, sizeof(info.type));
+ i2c_new_device(&rdev->pm.i2c_bus->adapter, &info);
+ DRM_INFO("Possible %s thermal controller at 0x%02x\n",
+ name, info.addr);
+ }
+ }
}
if (rdev->flags & RADEON_IS_MOBILITY) {
@@ -2715,6 +2738,14 @@ default_mode:
rdev->pm.current_power_state_index = rdev->pm.default_power_state_index;
rdev->pm.current_clock_mode_index = 0;
+ return;
+
+pm_failed:
+ rdev->pm.default_power_state_index = state_index;
+ rdev->pm.num_power_states = 0;
+
+ rdev->pm.current_power_state_index = rdev->pm.default_power_state_index;
+ rdev->pm.current_clock_mode_index = 0;
}
void radeon_external_tmds_setup(struct drm_encoder *encoder)
@@ -3278,6 +3309,14 @@ void radeon_combios_asic_init(struct drm_device *dev)
rdev->pdev->subsystem_device == 0x30a4)
return;
+ /* quirk for rs4xx Compaq Presario V5245EU laptop to make it resume
+ * - it hangs on resume inside the dynclk 1 table.
+ */
+ if (rdev->family == CHIP_RS480 &&
+ rdev->pdev->subsystem_vendor == 0x103c &&
+ rdev->pdev->subsystem_device == 0x30ae)
+ return;
+
/* DYN CLK 1 */
table = combios_get_table_offset(dev, COMBIOS_DYN_CLK_1_TABLE);
if (table)
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index cbfca3a24fdf..e7cb3ab09243 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -44,28 +44,34 @@ extern void
radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder,
struct drm_connector *drm_connector);
-bool radeon_connector_encoder_is_dp_bridge(struct drm_connector *connector);
-
void radeon_connector_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ /* bail if the connector does not have hpd pin, e.g.,
+ * VGA, TV, etc.
+ */
+ if (radeon_connector->hpd.hpd == RADEON_HPD_NONE)
+ return;
+
radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
- /* powering up/down the eDP panel generates hpd events which
- * can interfere with modesetting.
- */
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+ /* if the connector is already off, don't turn it back on */
+ if (connector->dpms != DRM_MODE_DPMS_ON)
return;
- /* pre-r600 did not always have the hpd pins mapped accurately to connectors */
- if (rdev->family >= CHIP_R600) {
- if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
- else
+ /* just deal with DP (not eDP) here. */
+ if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+ int saved_dpms = connector->dpms;
+
+ /* Only turn off the display it it's physically disconnected */
+ if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ else if (radeon_dp_needs_link_train(radeon_connector))
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
+ connector->dpms = saved_dpms;
}
}
@@ -429,11 +435,19 @@ static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder,
{
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
+ struct drm_display_mode *t, *mode;
+
+ /* If the EDID preferred mode doesn't match the native mode, use it */
+ list_for_each_entry_safe(mode, t, &connector->probed_modes, head) {
+ if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+ if (mode->hdisplay != native_mode->hdisplay ||
+ mode->vdisplay != native_mode->vdisplay)
+ memcpy(native_mode, mode, sizeof(*mode));
+ }
+ }
/* Try to get native mode details from EDID if necessary */
if (!native_mode->clock) {
- struct drm_display_mode *t, *mode;
-
list_for_each_entry_safe(mode, t, &connector->probed_modes, head) {
if (mode->hdisplay == native_mode->hdisplay &&
mode->vdisplay == native_mode->vdisplay) {
@@ -444,6 +458,7 @@ static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder,
}
}
}
+
if (!native_mode->clock) {
DRM_DEBUG_KMS("No LVDS native mode details, disabling RMX\n");
radeon_encoder->rmx_type = RMX_OFF;
@@ -657,6 +672,7 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
if (radeon_connector->ddc_bus)
dret = radeon_ddc_probe(radeon_connector);
if (dret) {
+ radeon_connector->detected_by_load = false;
if (radeon_connector->edid) {
kfree(radeon_connector->edid);
radeon_connector->edid = NULL;
@@ -683,12 +699,21 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
} else {
/* if we aren't forcing don't do destructive polling */
- if (!force)
- return connector->status;
+ if (!force) {
+ /* only return the previous status if we last
+ * detected a monitor via load.
+ */
+ if (radeon_connector->detected_by_load)
+ return connector->status;
+ else
+ return ret;
+ }
if (radeon_connector->dac_load_detect && encoder) {
encoder_funcs = encoder->helper_private;
ret = encoder_funcs->detect(encoder, connector);
+ if (ret != connector_status_disconnected)
+ radeon_connector->detected_by_load = true;
}
}
@@ -829,6 +854,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
if (radeon_connector->ddc_bus)
dret = radeon_ddc_probe(radeon_connector);
if (dret) {
+ radeon_connector->detected_by_load = false;
if (radeon_connector->edid) {
kfree(radeon_connector->edid);
radeon_connector->edid = NULL;
@@ -891,8 +917,18 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
if ((ret == connector_status_connected) && (radeon_connector->use_digital == true))
goto out;
+ /* DVI-D and HDMI-A are digital only */
+ if ((connector->connector_type == DRM_MODE_CONNECTOR_DVID) ||
+ (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA))
+ goto out;
+
+ /* if we aren't forcing don't do destructive polling */
if (!force) {
- ret = connector->status;
+ /* only return the previous status if we last
+ * detected a monitor via load.
+ */
+ if (radeon_connector->detected_by_load)
+ ret = connector->status;
goto out;
}
@@ -917,6 +953,8 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
if (ret == connector_status_connected) {
radeon_connector->use_digital = false;
}
+ if (ret != connector_status_disconnected)
+ radeon_connector->detected_by_load = true;
}
break;
}
@@ -1113,7 +1151,8 @@ static int radeon_dp_get_modes(struct drm_connector *connector)
}
} else {
/* need to setup ddc on the bridge */
- if (radeon_connector_encoder_is_dp_bridge(connector)) {
+ if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) !=
+ ENCODER_OBJECT_ID_NONE) {
if (encoder)
radeon_atom_ext_encoder_setup_ddc(encoder);
}
@@ -1123,13 +1162,12 @@ static int radeon_dp_get_modes(struct drm_connector *connector)
return ret;
}
-bool radeon_connector_encoder_is_dp_bridge(struct drm_connector *connector)
+u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector)
{
struct drm_mode_object *obj;
struct drm_encoder *encoder;
struct radeon_encoder *radeon_encoder;
int i;
- bool found = false;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
if (connector->encoder_ids[i] == 0)
@@ -1145,14 +1183,13 @@ bool radeon_connector_encoder_is_dp_bridge(struct drm_connector *connector)
switch (radeon_encoder->encoder_id) {
case ENCODER_OBJECT_ID_TRAVIS:
case ENCODER_OBJECT_ID_NUTMEG:
- found = true;
- break;
+ return radeon_encoder->encoder_id;
default:
break;
}
}
- return found;
+ return ENCODER_OBJECT_ID_NONE;
}
bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector)
@@ -1229,12 +1266,24 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
if (!radeon_dig_connector->edp_on)
atombios_set_edp_panel_power(connector,
ATOM_TRANSMITTER_ACTION_POWER_OFF);
- } else {
- /* need to setup ddc on the bridge */
- if (radeon_connector_encoder_is_dp_bridge(connector)) {
- if (encoder)
- radeon_atom_ext_encoder_setup_ddc(encoder);
+ } else if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) !=
+ ENCODER_OBJECT_ID_NONE) {
+ /* DP bridges are always DP */
+ radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT;
+ /* get the DPCD from the bridge */
+ radeon_dp_getdpcd(radeon_connector);
+
+ if (encoder) {
+ /* setup ddc on the bridge */
+ radeon_atom_ext_encoder_setup_ddc(encoder);
+ if (radeon_ddc_probe(radeon_connector)) /* try DDC */
+ ret = connector_status_connected;
+ else if (radeon_connector->dac_load_detect) { /* try load detection */
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ ret = encoder_funcs->detect(encoder, connector);
+ }
}
+ } else {
radeon_dig_connector->dp_sink_type = radeon_dp_getsinktype(radeon_connector);
if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) {
ret = connector_status_connected;
@@ -1249,16 +1298,6 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
ret = connector_status_connected;
}
}
-
- if ((ret == connector_status_disconnected) &&
- radeon_connector->dac_load_detect) {
- struct drm_encoder *encoder = radeon_best_single_encoder(connector);
- struct drm_encoder_helper_funcs *encoder_funcs;
- if (encoder) {
- encoder_funcs = encoder->helper_private;
- ret = encoder_funcs->detect(encoder, connector);
- }
- }
}
radeon_connector_update_scratch_regs(connector, ret);
@@ -1400,6 +1439,7 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_connector->shared_ddc = shared_ddc;
radeon_connector->connector_object_id = connector_object_id;
radeon_connector->hpd = *hpd;
+
radeon_connector->router = *router;
if (router->ddc_valid || router->cd_valid) {
radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info);
@@ -1746,6 +1786,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
radeon_connector->devices = supported_device;
radeon_connector->connector_object_id = connector_object_id;
radeon_connector->hpd = *hpd;
+
switch (connector_type) {
case DRM_MODE_CONNECTOR_VGA:
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
index 75867792a4e2..72ae8266b8e9 100644
--- a/drivers/gpu/drm/radeon/radeon_cp.c
+++ b/drivers/gpu/drm/radeon/radeon_cp.c
@@ -29,6 +29,8 @@
* Gareth Hughes <gareth@valinux.com>
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "drm.h"
#include "drm_sarea.h"
@@ -2115,7 +2117,7 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags)
if (drm_pci_device_is_agp(dev))
dev_priv->flags |= RADEON_IS_AGP;
- else if (drm_pci_device_is_pcie(dev))
+ else if (pci_is_pcie(dev->pdev))
dev_priv->flags |= RADEON_IS_PCIE;
else
dev_priv->flags |= RADEON_IS_PCI;
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index fae00c0d75aa..29afd71e0840 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -93,7 +93,7 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
{
struct drm_radeon_cs *cs = data;
uint64_t *chunk_array_ptr;
- unsigned size, i;
+ unsigned size, i, flags = 0;
if (!cs->num_chunks) {
return 0;
@@ -140,6 +140,10 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
if (p->chunks[i].length_dw == 0)
return -EINVAL;
}
+ if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS &&
+ !p->chunks[i].length_dw) {
+ return -EINVAL;
+ }
p->chunks[i].length_dw = user_chunk.length_dw;
p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data;
@@ -155,6 +159,9 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
p->chunks[i].user_ptr, size)) {
return -EFAULT;
}
+ if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) {
+ flags = p->chunks[i].kdata[0];
+ }
} else {
p->chunks[i].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL);
p->chunks[i].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL);
@@ -174,6 +181,8 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
p->chunks[p->chunk_ib_idx].length_dw);
return -EINVAL;
}
+
+ p->keep_tiling_flags = (flags & RADEON_CS_KEEP_TILING_FLAGS) != 0;
return 0;
}
@@ -222,7 +231,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
struct radeon_cs_chunk *ib_chunk;
int r;
- mutex_lock(&rdev->cs_mutex);
+ radeon_mutex_lock(&rdev->cs_mutex);
/* initialize parser */
memset(&parser, 0, sizeof(struct radeon_cs_parser));
parser.filp = filp;
@@ -233,14 +242,14 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
if (r) {
DRM_ERROR("Failed to initialize parser !\n");
radeon_cs_parser_fini(&parser, r);
- mutex_unlock(&rdev->cs_mutex);
+ radeon_mutex_unlock(&rdev->cs_mutex);
return r;
}
r = radeon_ib_get(rdev, &parser.ib);
if (r) {
DRM_ERROR("Failed to get ib !\n");
radeon_cs_parser_fini(&parser, r);
- mutex_unlock(&rdev->cs_mutex);
+ radeon_mutex_unlock(&rdev->cs_mutex);
return r;
}
r = radeon_cs_parser_relocs(&parser);
@@ -248,7 +257,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
if (r != -ERESTARTSYS)
DRM_ERROR("Failed to parse relocation %d!\n", r);
radeon_cs_parser_fini(&parser, r);
- mutex_unlock(&rdev->cs_mutex);
+ radeon_mutex_unlock(&rdev->cs_mutex);
return r;
}
/* Copy the packet into the IB, the parser will read from the
@@ -260,14 +269,14 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
if (r || parser.parser_error) {
DRM_ERROR("Invalid command stream !\n");
radeon_cs_parser_fini(&parser, r);
- mutex_unlock(&rdev->cs_mutex);
+ radeon_mutex_unlock(&rdev->cs_mutex);
return r;
}
r = radeon_cs_finish_pages(&parser);
if (r) {
DRM_ERROR("Invalid command stream !\n");
radeon_cs_parser_fini(&parser, r);
- mutex_unlock(&rdev->cs_mutex);
+ radeon_mutex_unlock(&rdev->cs_mutex);
return r;
}
r = radeon_ib_schedule(rdev, parser.ib);
@@ -275,7 +284,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
DRM_ERROR("Failed to schedule IB !\n");
}
radeon_cs_parser_fini(&parser, r);
- mutex_unlock(&rdev->cs_mutex);
+ radeon_mutex_unlock(&rdev->cs_mutex);
return r;
}
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index 3189a7efb2e9..fde25c0d65a0 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -208,23 +208,25 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc,
int xorigin = 0, yorigin = 0;
int w = radeon_crtc->cursor_width;
- if (x < 0)
- xorigin = -x + 1;
- if (y < 0)
- yorigin = -y + 1;
- if (xorigin >= CURSOR_WIDTH)
- xorigin = CURSOR_WIDTH - 1;
- if (yorigin >= CURSOR_HEIGHT)
- yorigin = CURSOR_HEIGHT - 1;
-
if (ASIC_IS_AVIVO(rdev)) {
- int i = 0;
- struct drm_crtc *crtc_p;
-
/* avivo cursor are offset into the total surface */
x += crtc->x;
y += crtc->y;
- DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
+ }
+ DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
+
+ if (x < 0) {
+ xorigin = min(-x, CURSOR_WIDTH - 1);
+ x = 0;
+ }
+ if (y < 0) {
+ yorigin = min(-y, CURSOR_HEIGHT - 1);
+ y = 0;
+ }
+
+ if (ASIC_IS_AVIVO(rdev)) {
+ int i = 0;
+ struct drm_crtc *crtc_p;
/* avivo cursor image can't end on 128 pixel boundary or
* go past the end of the frame if both crtcs are enabled
@@ -253,16 +255,12 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc,
radeon_lock_cursor(crtc, true);
if (ASIC_IS_DCE4(rdev)) {
- WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset,
- ((xorigin ? 0 : x) << 16) |
- (yorigin ? 0 : y));
+ WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y);
WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin);
WREG32(EVERGREEN_CUR_SIZE + radeon_crtc->crtc_offset,
((w - 1) << 16) | (radeon_crtc->cursor_height - 1));
} else if (ASIC_IS_AVIVO(rdev)) {
- WREG32(AVIVO_D1CUR_POSITION + radeon_crtc->crtc_offset,
- ((xorigin ? 0 : x) << 16) |
- (yorigin ? 0 : y));
+ WREG32(AVIVO_D1CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y);
WREG32(AVIVO_D1CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin);
WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset,
((w - 1) << 16) | (radeon_crtc->cursor_height - 1));
@@ -276,8 +274,8 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc,
| yorigin));
WREG32(RADEON_CUR_HORZ_VERT_POSN + radeon_crtc->crtc_offset,
(RADEON_CUR_LOCK
- | ((xorigin ? 0 : x) << 16)
- | (yorigin ? 0 : y)));
+ | (x << 16)
+ | y));
/* offset is from DISP(2)_BASE_ADDRESS */
WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, (radeon_crtc->legacy_cursor_offset +
(yorigin * 256)));
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 7cfaa7e2f3b5..c4d00a171411 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -32,6 +32,7 @@
#include <drm/radeon_drm.h>
#include <linux/vgaarb.h>
#include <linux/vga_switcheroo.h>
+#include <linux/efi.h>
#include "radeon_reg.h"
#include "radeon.h"
#include "atom.h"
@@ -300,6 +301,8 @@ void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64
mc->mc_vram_size = mc->aper_size;
}
mc->vram_end = mc->vram_start + mc->mc_vram_size - 1;
+ if (radeon_vram_limit && radeon_vram_limit < mc->real_vram_size)
+ mc->real_vram_size = radeon_vram_limit;
dev_info(rdev->dev, "VRAM: %lluM 0x%016llX - 0x%016llX (%lluM used)\n",
mc->mc_vram_size >> 20, mc->vram_start,
mc->vram_end, mc->real_vram_size >> 20);
@@ -348,6 +351,9 @@ bool radeon_card_posted(struct radeon_device *rdev)
{
uint32_t reg;
+ if (efi_enabled && rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE)
+ return false;
+
/* first check CRTCs */
if (ASIC_IS_DCE41(rdev)) {
reg = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) |
@@ -704,12 +710,13 @@ int radeon_device_init(struct radeon_device *rdev,
rdev->gpu_lockup = false;
rdev->accel_working = false;
- DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X).\n",
- radeon_family_name[rdev->family], pdev->vendor, pdev->device);
+ DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X).\n",
+ radeon_family_name[rdev->family], pdev->vendor, pdev->device,
+ pdev->subsystem_vendor, pdev->subsystem_device);
/* mutex initialization are all done here so we
* can recall function without having locking issues */
- mutex_init(&rdev->cs_mutex);
+ radeon_mutex_init(&rdev->cs_mutex);
mutex_init(&rdev->ib_pool.mutex);
mutex_init(&rdev->cp.mutex);
mutex_init(&rdev->dc_hw_i2c_mutex);
@@ -743,14 +750,15 @@ int radeon_device_init(struct radeon_device *rdev,
/* set DMA mask + need_dma32 flags.
* PCIE - can handle 40-bits.
- * IGP - can handle 40-bits (in theory)
+ * IGP - can handle 40-bits
* AGP - generally dma32 is safest
- * PCI - only dma32
+ * PCI - dma32 for legacy pci gart, 40 bits on newer asics
*/
rdev->need_dma32 = false;
if (rdev->flags & RADEON_IS_AGP)
rdev->need_dma32 = true;
- if (rdev->flags & RADEON_IS_PCI)
+ if ((rdev->flags & RADEON_IS_PCI) &&
+ (rdev->family < CHIP_RS400))
rdev->need_dma32 = true;
dma_bits = rdev->need_dma32 ? 32 : 40;
@@ -810,7 +818,7 @@ int radeon_device_init(struct radeon_device *rdev,
radeon_test_moves(rdev);
}
if (radeon_benchmarking) {
- radeon_benchmark(rdev);
+ radeon_benchmark(rdev, radeon_benchmarking);
}
return 0;
}
@@ -947,6 +955,9 @@ int radeon_gpu_reset(struct radeon_device *rdev)
int r;
int resched;
+ /* Prevent CS ioctl from interfering */
+ radeon_mutex_lock(&rdev->cs_mutex);
+
radeon_save_bios_scratch_regs(rdev);
/* block TTM */
resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev);
@@ -959,10 +970,15 @@ int radeon_gpu_reset(struct radeon_device *rdev)
radeon_restore_bios_scratch_regs(rdev);
drm_helper_resume_force_mode(rdev->ddev);
ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched);
- return 0;
}
- /* bad news, how to tell it to userspace ? */
- dev_info(rdev->dev, "GPU reset failed\n");
+
+ radeon_mutex_unlock(&rdev->cs_mutex);
+
+ if (r) {
+ /* bad news, how to tell it to userspace ? */
+ dev_info(rdev->dev, "GPU reset failed\n");
+ }
+
return r;
}
@@ -974,7 +990,7 @@ struct radeon_debugfs {
struct drm_info_list *files;
unsigned num_files;
};
-static struct radeon_debugfs _radeon_debugfs[RADEON_DEBUGFS_MAX_NUM_FILES];
+static struct radeon_debugfs _radeon_debugfs[RADEON_DEBUGFS_MAX_COMPONENTS];
static unsigned _radeon_debugfs_count = 0;
int radeon_debugfs_add_files(struct radeon_device *rdev,
@@ -989,14 +1005,17 @@ int radeon_debugfs_add_files(struct radeon_device *rdev,
return 0;
}
}
- if ((_radeon_debugfs_count + nfiles) > RADEON_DEBUGFS_MAX_NUM_FILES) {
- DRM_ERROR("Reached maximum number of debugfs files.\n");
- DRM_ERROR("Report so we increase RADEON_DEBUGFS_MAX_NUM_FILES.\n");
+
+ i = _radeon_debugfs_count + 1;
+ if (i > RADEON_DEBUGFS_MAX_COMPONENTS) {
+ DRM_ERROR("Reached maximum number of debugfs components.\n");
+ DRM_ERROR("Report so we increase "
+ "RADEON_DEBUGFS_MAX_COMPONENTS.\n");
return -EINVAL;
}
_radeon_debugfs[_radeon_debugfs_count].files = files;
_radeon_debugfs[_radeon_debugfs_count].num_files = nfiles;
- _radeon_debugfs_count++;
+ _radeon_debugfs_count = i;
#if defined(CONFIG_DEBUG_FS)
drm_debugfs_create_files(files, nfiles,
rdev->ddev->control->debugfs_root,
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 292f73f0ddbd..a22d6e6a49a2 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -33,8 +33,6 @@
#include "drm_crtc_helper.h"
#include "drm_edid.h"
-static int radeon_ddc_dump(struct drm_connector *connector);
-
static void avivo_crtc_load_lut(struct drm_crtc *crtc)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
@@ -282,7 +280,7 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
spin_lock_irqsave(&rdev->ddev->event_lock, flags);
work = radeon_crtc->unpin_work;
if (work == NULL ||
- !radeon_fence_signaled(work->fence)) {
+ (work->fence && !radeon_fence_signaled(work->fence))) {
spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
return;
}
@@ -348,7 +346,6 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
struct radeon_framebuffer *new_radeon_fb;
struct drm_gem_object *obj;
struct radeon_bo *rbo;
- struct radeon_fence *fence;
struct radeon_unpin_work *work;
unsigned long flags;
u32 tiling_flags, pitch_pixels;
@@ -359,16 +356,9 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
if (work == NULL)
return -ENOMEM;
- r = radeon_fence_create(rdev, &fence);
- if (unlikely(r != 0)) {
- kfree(work);
- DRM_ERROR("flip queue: failed to create fence.\n");
- return -ENOMEM;
- }
work->event = event;
work->rdev = rdev;
work->crtc_id = radeon_crtc->crtc_id;
- work->fence = radeon_fence_ref(fence);
old_radeon_fb = to_radeon_framebuffer(crtc->fb);
new_radeon_fb = to_radeon_framebuffer(fb);
/* schedule unpin of the old buffer */
@@ -377,6 +367,10 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
drm_gem_object_reference(obj);
rbo = gem_to_radeon_bo(obj);
work->old_rbo = rbo;
+ obj = new_radeon_fb->obj;
+ rbo = gem_to_radeon_bo(obj);
+ if (rbo->tbo.sync_obj)
+ work->fence = radeon_fence_ref(rbo->tbo.sync_obj);
INIT_WORK(&work->work, radeon_unpin_work_func);
/* We borrow the event spin lock for protecting unpin_work */
@@ -391,9 +385,6 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
spin_unlock_irqrestore(&dev->event_lock, flags);
/* pin the new buffer */
- obj = new_radeon_fb->obj;
- rbo = gem_to_radeon_bo(obj);
-
DRM_DEBUG_DRIVER("flip-ioctl() cur_fbo = %p, cur_bbo = %p\n",
work->old_rbo, rbo);
@@ -461,37 +452,18 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
goto pflip_cleanup1;
}
- /* 32 ought to cover us */
- r = radeon_ring_lock(rdev, 32);
- if (r) {
- DRM_ERROR("failed to lock the ring before flip\n");
- goto pflip_cleanup2;
- }
-
- /* emit the fence */
- radeon_fence_emit(rdev, fence);
/* set the proper interrupt */
radeon_pre_page_flip(rdev, radeon_crtc->crtc_id);
- /* fire the ring */
- radeon_ring_unlock_commit(rdev);
return 0;
-pflip_cleanup2:
- drm_vblank_put(dev, radeon_crtc->crtc_id);
-
pflip_cleanup1:
- r = radeon_bo_reserve(rbo, false);
- if (unlikely(r != 0)) {
+ if (unlikely(radeon_bo_reserve(rbo, false) != 0)) {
DRM_ERROR("failed to reserve new rbo in error path\n");
goto pflip_cleanup;
}
- r = radeon_bo_unpin(rbo);
- if (unlikely(r != 0)) {
- radeon_bo_unreserve(rbo);
- r = -EINVAL;
+ if (unlikely(radeon_bo_unpin(rbo) != 0)) {
DRM_ERROR("failed to unpin new rbo in error path\n");
- goto pflip_cleanup;
}
radeon_bo_unreserve(rbo);
@@ -499,9 +471,9 @@ pflip_cleanup:
spin_lock_irqsave(&dev->event_lock, flags);
radeon_crtc->unpin_work = NULL;
unlock_free:
- drm_gem_object_unreference_unlocked(old_radeon_fb->obj);
spin_unlock_irqrestore(&dev->event_lock, flags);
- radeon_fence_unref(&fence);
+ drm_gem_object_unreference_unlocked(old_radeon_fb->obj);
+ radeon_fence_unref(&work->fence);
kfree(work);
return r;
@@ -695,7 +667,6 @@ static void radeon_print_display_setup(struct drm_device *dev)
static bool radeon_setup_enc_conn(struct drm_device *dev)
{
struct radeon_device *rdev = dev->dev_private;
- struct drm_connector *drm_connector;
bool ret = false;
if (rdev->bios) {
@@ -715,8 +686,6 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
if (ret) {
radeon_setup_encoder_clones(dev);
radeon_print_display_setup(dev);
- list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head)
- radeon_ddc_dump(drm_connector);
}
return ret;
@@ -733,16 +702,22 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
radeon_router_select_ddc_port(radeon_connector);
if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
- (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
+ (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP) ||
+ (radeon_connector_encoder_get_dp_bridge_encoder_id(&radeon_connector->base) !=
+ ENCODER_OBJECT_ID_NONE)) {
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
+
if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT ||
dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && dig->dp_i2c_bus)
- radeon_connector->edid = drm_get_edid(&radeon_connector->base, &dig->dp_i2c_bus->adapter);
- }
- if (!radeon_connector->ddc_bus)
- return -1;
- if (!radeon_connector->edid) {
- radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base,
+ &dig->dp_i2c_bus->adapter);
+ else if (radeon_connector->ddc_bus && !radeon_connector->edid)
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base,
+ &radeon_connector->ddc_bus->adapter);
+ } else {
+ if (radeon_connector->ddc_bus && !radeon_connector->edid)
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base,
+ &radeon_connector->ddc_bus->adapter);
}
if (!radeon_connector->edid) {
@@ -764,25 +739,6 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
return 0;
}
-static int radeon_ddc_dump(struct drm_connector *connector)
-{
- struct edid *edid;
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- int ret = 0;
-
- /* on hw with routers, select right port */
- if (radeon_connector->router.ddc_valid)
- radeon_router_select_ddc_port(radeon_connector);
-
- if (!radeon_connector->ddc_bus)
- return -1;
- edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
- if (edid) {
- kfree(edid);
- }
- return ret;
-}
-
/* avivo */
static void avivo_get_fb_div(struct radeon_pll *pll,
u32 target_clock,
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 73dfbe8e5f9e..71499fc3daf5 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -36,6 +36,7 @@
#include "drm_pciids.h"
#include <linux/console.h>
+#include <linux/module.h>
/*
@@ -51,9 +52,11 @@
* 2.8.0 - pageflip support, r500 US_FORMAT regs. r500 ARGB2101010 colorbuf, r300->r500 CMASK, clock crystal query
* 2.9.0 - r600 tiling (s3tc,rgtc) working, SET_PREDICATION packet 3 on r600 + eg, backend query
* 2.10.0 - fusion 2D tiling
+ * 2.11.0 - backend map, initial compute support for the CS checker
+ * 2.12.0 - RADEON_CS_KEEP_TILING_FLAGS
*/
#define KMS_DRIVER_MAJOR 2
-#define KMS_DRIVER_MINOR 10
+#define KMS_DRIVER_MINOR 12
#define KMS_DRIVER_PATCHLEVEL 0
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
int radeon_driver_unload_kms(struct drm_device *dev);
@@ -117,6 +120,7 @@ int radeon_audio = 0;
int radeon_disp_priority = 0;
int radeon_hw_i2c = 0;
int radeon_pcie_gen2 = 0;
+int radeon_msi = -1;
MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
module_param_named(no_wb, radeon_no_wb, int, 0444);
@@ -163,6 +167,9 @@ module_param_named(hw_i2c, radeon_hw_i2c, int, 0444);
MODULE_PARM_DESC(pcie_gen2, "PCIE Gen2 mode (1 = enable)");
module_param_named(pcie_gen2, radeon_pcie_gen2, int, 0444);
+MODULE_PARM_DESC(msi, "MSI support (1 = enable, 0 = disable, -1 = auto)");
+module_param_named(msi, radeon_msi, int, 0444);
+
static int radeon_suspend(struct drm_device *dev, pm_message_t state)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
index b293487e5aa3..06e413e6a920 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -29,12 +29,6 @@
#include "radeon.h"
#include "atom.h"
-extern int atom_debug;
-
-/* evil but including atombios.h is much worse */
-bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
- struct drm_display_mode *mode);
-
static uint32_t radeon_encoder_clones(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
@@ -156,27 +150,6 @@ radeon_get_encoder_enum(struct drm_device *dev, uint32_t supported_device, uint8
return ret;
}
-static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder)
-{
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_LVDS:
- case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
- case ENCODER_OBJECT_ID_INTERNAL_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_DDI:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- return true;
- default:
- return false;
- }
-}
-
void
radeon_link_encoder_connector(struct drm_device *dev)
{
@@ -229,23 +202,7 @@ radeon_get_connector_for_encoder(struct drm_encoder *encoder)
return NULL;
}
-static struct drm_connector *
-radeon_get_connector_for_encoder_init(struct drm_encoder *encoder)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_connector *connector;
- struct radeon_connector *radeon_connector;
-
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- radeon_connector = to_radeon_connector(connector);
- if (radeon_encoder->devices & radeon_connector->devices)
- return connector;
- }
- return NULL;
-}
-
-struct drm_encoder *radeon_atom_get_external_encoder(struct drm_encoder *encoder)
+struct drm_encoder *radeon_get_external_encoder(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
@@ -266,9 +223,9 @@ struct drm_encoder *radeon_atom_get_external_encoder(struct drm_encoder *encoder
return NULL;
}
-bool radeon_encoder_is_dp_bridge(struct drm_encoder *encoder)
+u16 radeon_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder)
{
- struct drm_encoder *other_encoder = radeon_atom_get_external_encoder(encoder);
+ struct drm_encoder *other_encoder = radeon_get_external_encoder(encoder);
if (other_encoder) {
struct radeon_encoder *radeon_encoder = to_radeon_encoder(other_encoder);
@@ -332,2077 +289,3 @@ void radeon_panel_mode_fixup(struct drm_encoder *encoder,
}
-static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
-
- /* set the active encoder to connector routing */
- radeon_encoder_set_active_device(encoder);
- drm_mode_set_crtcinfo(adjusted_mode, 0);
-
- /* hw bug */
- if ((mode->flags & DRM_MODE_FLAG_INTERLACE)
- && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2)))
- adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2;
-
- /* get the native mode for LVDS */
- if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT))
- radeon_panel_mode_fixup(encoder, adjusted_mode);
-
- /* get the native mode for TV */
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
- struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv;
- if (tv_dac) {
- if (tv_dac->tv_std == TV_STD_NTSC ||
- tv_dac->tv_std == TV_STD_NTSC_J ||
- tv_dac->tv_std == TV_STD_PAL_M)
- radeon_atom_get_tv_timings(rdev, 0, adjusted_mode);
- else
- radeon_atom_get_tv_timings(rdev, 1, adjusted_mode);
- }
- }
-
- if (ASIC_IS_DCE3(rdev) &&
- ((radeon_encoder->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) ||
- radeon_encoder_is_dp_bridge(encoder))) {
- struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
- radeon_dp_set_link_config(connector, mode);
- }
-
- return true;
-}
-
-static void
-atombios_dac_setup(struct drm_encoder *encoder, int action)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- DAC_ENCODER_CONTROL_PS_ALLOCATION args;
- int index = 0;
- struct radeon_encoder_atom_dac *dac_info = radeon_encoder->enc_priv;
-
- memset(&args, 0, sizeof(args));
-
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_DAC1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
- index = GetIndexIntoMasterTable(COMMAND, DAC1EncoderControl);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DAC2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
- index = GetIndexIntoMasterTable(COMMAND, DAC2EncoderControl);
- break;
- }
-
- args.ucAction = action;
-
- if (radeon_encoder->active_device & (ATOM_DEVICE_CRT_SUPPORT))
- args.ucDacStandard = ATOM_DAC1_PS2;
- else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
- args.ucDacStandard = ATOM_DAC1_CV;
- else {
- switch (dac_info->tv_std) {
- case TV_STD_PAL:
- case TV_STD_PAL_M:
- case TV_STD_SCART_PAL:
- case TV_STD_SECAM:
- case TV_STD_PAL_CN:
- args.ucDacStandard = ATOM_DAC1_PAL;
- break;
- case TV_STD_NTSC:
- case TV_STD_NTSC_J:
- case TV_STD_PAL_60:
- default:
- args.ucDacStandard = ATOM_DAC1_NTSC;
- break;
- }
- }
- args.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
-
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-
-}
-
-static void
-atombios_tv_setup(struct drm_encoder *encoder, int action)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- TV_ENCODER_CONTROL_PS_ALLOCATION args;
- int index = 0;
- struct radeon_encoder_atom_dac *dac_info = radeon_encoder->enc_priv;
-
- memset(&args, 0, sizeof(args));
-
- index = GetIndexIntoMasterTable(COMMAND, TVEncoderControl);
-
- args.sTVEncoder.ucAction = action;
-
- if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
- args.sTVEncoder.ucTvStandard = ATOM_TV_CV;
- else {
- switch (dac_info->tv_std) {
- case TV_STD_NTSC:
- args.sTVEncoder.ucTvStandard = ATOM_TV_NTSC;
- break;
- case TV_STD_PAL:
- args.sTVEncoder.ucTvStandard = ATOM_TV_PAL;
- break;
- case TV_STD_PAL_M:
- args.sTVEncoder.ucTvStandard = ATOM_TV_PALM;
- break;
- case TV_STD_PAL_60:
- args.sTVEncoder.ucTvStandard = ATOM_TV_PAL60;
- break;
- case TV_STD_NTSC_J:
- args.sTVEncoder.ucTvStandard = ATOM_TV_NTSCJ;
- break;
- case TV_STD_SCART_PAL:
- args.sTVEncoder.ucTvStandard = ATOM_TV_PAL; /* ??? */
- break;
- case TV_STD_SECAM:
- args.sTVEncoder.ucTvStandard = ATOM_TV_SECAM;
- break;
- case TV_STD_PAL_CN:
- args.sTVEncoder.ucTvStandard = ATOM_TV_PALCN;
- break;
- default:
- args.sTVEncoder.ucTvStandard = ATOM_TV_NTSC;
- break;
- }
- }
-
- args.sTVEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
-
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-
-}
-
-union dvo_encoder_control {
- ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION ext_tmds;
- DVO_ENCODER_CONTROL_PS_ALLOCATION dvo;
- DVO_ENCODER_CONTROL_PS_ALLOCATION_V3 dvo_v3;
-};
-
-void
-atombios_dvo_setup(struct drm_encoder *encoder, int action)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- union dvo_encoder_control args;
- int index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl);
-
- memset(&args, 0, sizeof(args));
-
- if (ASIC_IS_DCE3(rdev)) {
- /* DCE3+ */
- args.dvo_v3.ucAction = action;
- args.dvo_v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
- args.dvo_v3.ucDVOConfig = 0; /* XXX */
- } else if (ASIC_IS_DCE2(rdev)) {
- /* DCE2 (pre-DCE3 R6xx, RS600/690/740 */
- args.dvo.sDVOEncoder.ucAction = action;
- args.dvo.sDVOEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
- /* DFP1, CRT1, TV1 depending on the type of port */
- args.dvo.sDVOEncoder.ucDeviceType = ATOM_DEVICE_DFP1_INDEX;
-
- if (radeon_encoder->pixel_clock > 165000)
- args.dvo.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute |= PANEL_ENCODER_MISC_DUAL;
- } else {
- /* R4xx, R5xx */
- args.ext_tmds.sXTmdsEncoder.ucEnable = action;
-
- if (radeon_encoder->pixel_clock > 165000)
- args.ext_tmds.sXTmdsEncoder.ucMisc |= PANEL_ENCODER_MISC_DUAL;
-
- /*if (pScrn->rgbBits == 8)*/
- args.ext_tmds.sXTmdsEncoder.ucMisc |= ATOM_PANEL_MISC_888RGB;
- }
-
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-}
-
-union lvds_encoder_control {
- LVDS_ENCODER_CONTROL_PS_ALLOCATION v1;
- LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 v2;
-};
-
-void
-atombios_digital_setup(struct drm_encoder *encoder, int action)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
- union lvds_encoder_control args;
- int index = 0;
- int hdmi_detected = 0;
- uint8_t frev, crev;
-
- if (!dig)
- return;
-
- if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI)
- hdmi_detected = 1;
-
- memset(&args, 0, sizeof(args));
-
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_LVDS:
- index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
- index = GetIndexIntoMasterTable(COMMAND, TMDS1EncoderControl);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
- index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl);
- else
- index = GetIndexIntoMasterTable(COMMAND, TMDS2EncoderControl);
- break;
- }
-
- if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
- return;
-
- switch (frev) {
- case 1:
- case 2:
- switch (crev) {
- case 1:
- args.v1.ucMisc = 0;
- args.v1.ucAction = action;
- if (hdmi_detected)
- args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
- args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL)
- args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL;
- if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
- args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB;
- } else {
- if (dig->linkb)
- args.v1.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB;
- if (radeon_encoder->pixel_clock > 165000)
- args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL;
- /*if (pScrn->rgbBits == 8) */
- args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB;
- }
- break;
- case 2:
- case 3:
- args.v2.ucMisc = 0;
- args.v2.ucAction = action;
- if (crev == 3) {
- if (dig->coherent_mode)
- args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT;
- }
- if (hdmi_detected)
- args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
- args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
- args.v2.ucTruncate = 0;
- args.v2.ucSpatial = 0;
- args.v2.ucTemporal = 0;
- args.v2.ucFRC = 0;
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL)
- args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL;
- if (dig->lcd_misc & ATOM_PANEL_MISC_SPATIAL) {
- args.v2.ucSpatial = PANEL_ENCODER_SPATIAL_DITHER_EN;
- if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
- args.v2.ucSpatial |= PANEL_ENCODER_SPATIAL_DITHER_DEPTH;
- }
- if (dig->lcd_misc & ATOM_PANEL_MISC_TEMPORAL) {
- args.v2.ucTemporal = PANEL_ENCODER_TEMPORAL_DITHER_EN;
- if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
- args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_DITHER_DEPTH;
- if (((dig->lcd_misc >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT) & 0x3) == 2)
- args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_LEVEL_4;
- }
- } else {
- if (dig->linkb)
- args.v2.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB;
- if (radeon_encoder->pixel_clock > 165000)
- args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL;
- }
- break;
- default:
- DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
- break;
- }
- break;
- default:
- DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
- break;
- }
-
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-}
-
-int
-atombios_get_encoder_mode(struct drm_encoder *encoder)
-{
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct drm_connector *connector;
- struct radeon_connector *radeon_connector;
- struct radeon_connector_atom_dig *dig_connector;
-
- /* dp bridges are always DP */
- if (radeon_encoder_is_dp_bridge(encoder))
- return ATOM_ENCODER_MODE_DP;
-
- /* DVO is always DVO */
- if (radeon_encoder->encoder_id == ATOM_ENCODER_MODE_DVO)
- return ATOM_ENCODER_MODE_DVO;
-
- connector = radeon_get_connector_for_encoder(encoder);
- /* if we don't have an active device yet, just use one of
- * the connectors tied to the encoder.
- */
- if (!connector)
- connector = radeon_get_connector_for_encoder_init(encoder);
- radeon_connector = to_radeon_connector(connector);
-
- switch (connector->connector_type) {
- case DRM_MODE_CONNECTOR_DVII:
- case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */
- if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) {
- /* fix me */
- if (ASIC_IS_DCE4(rdev))
- return ATOM_ENCODER_MODE_DVI;
- else
- return ATOM_ENCODER_MODE_HDMI;
- } else if (radeon_connector->use_digital)
- return ATOM_ENCODER_MODE_DVI;
- else
- return ATOM_ENCODER_MODE_CRT;
- break;
- case DRM_MODE_CONNECTOR_DVID:
- case DRM_MODE_CONNECTOR_HDMIA:
- default:
- if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) {
- /* fix me */
- if (ASIC_IS_DCE4(rdev))
- return ATOM_ENCODER_MODE_DVI;
- else
- return ATOM_ENCODER_MODE_HDMI;
- } else
- return ATOM_ENCODER_MODE_DVI;
- break;
- case DRM_MODE_CONNECTOR_LVDS:
- return ATOM_ENCODER_MODE_LVDS;
- break;
- case DRM_MODE_CONNECTOR_DisplayPort:
- dig_connector = radeon_connector->con_priv;
- if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
- (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
- return ATOM_ENCODER_MODE_DP;
- else if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) {
- /* fix me */
- if (ASIC_IS_DCE4(rdev))
- return ATOM_ENCODER_MODE_DVI;
- else
- return ATOM_ENCODER_MODE_HDMI;
- } else
- return ATOM_ENCODER_MODE_DVI;
- break;
- case DRM_MODE_CONNECTOR_eDP:
- return ATOM_ENCODER_MODE_DP;
- case DRM_MODE_CONNECTOR_DVIA:
- case DRM_MODE_CONNECTOR_VGA:
- return ATOM_ENCODER_MODE_CRT;
- break;
- case DRM_MODE_CONNECTOR_Composite:
- case DRM_MODE_CONNECTOR_SVIDEO:
- case DRM_MODE_CONNECTOR_9PinDIN:
- /* fix me */
- return ATOM_ENCODER_MODE_TV;
- /*return ATOM_ENCODER_MODE_CV;*/
- break;
- }
-}
-
-/*
- * DIG Encoder/Transmitter Setup
- *
- * DCE 3.0/3.1
- * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA.
- * Supports up to 3 digital outputs
- * - 2 DIG encoder blocks.
- * DIG1 can drive UNIPHY link A or link B
- * DIG2 can drive UNIPHY link B or LVTMA
- *
- * DCE 3.2
- * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B).
- * Supports up to 5 digital outputs
- * - 2 DIG encoder blocks.
- * DIG1/2 can drive UNIPHY0/1/2 link A or link B
- *
- * DCE 4.0/5.0
- * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B).
- * Supports up to 6 digital outputs
- * - 6 DIG encoder blocks.
- * - DIG to PHY mapping is hardcoded
- * DIG1 drives UNIPHY0 link A, A+B
- * DIG2 drives UNIPHY0 link B
- * DIG3 drives UNIPHY1 link A, A+B
- * DIG4 drives UNIPHY1 link B
- * DIG5 drives UNIPHY2 link A, A+B
- * DIG6 drives UNIPHY2 link B
- *
- * DCE 4.1
- * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B).
- * Supports up to 6 digital outputs
- * - 2 DIG encoder blocks.
- * DIG1/2 can drive UNIPHY0/1/2 link A or link B
- *
- * Routing
- * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links)
- * Examples:
- * crtc0 -> dig2 -> LVTMA links A+B -> TMDS/HDMI
- * crtc1 -> dig1 -> UNIPHY0 link B -> DP
- * crtc0 -> dig1 -> UNIPHY2 link A -> LVDS
- * crtc1 -> dig2 -> UNIPHY1 link B+A -> TMDS/HDMI
- */
-
-union dig_encoder_control {
- DIG_ENCODER_CONTROL_PS_ALLOCATION v1;
- DIG_ENCODER_CONTROL_PARAMETERS_V2 v2;
- DIG_ENCODER_CONTROL_PARAMETERS_V3 v3;
- DIG_ENCODER_CONTROL_PARAMETERS_V4 v4;
-};
-
-void
-atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
- struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
- union dig_encoder_control args;
- int index = 0;
- uint8_t frev, crev;
- int dp_clock = 0;
- int dp_lane_count = 0;
- int hpd_id = RADEON_HPD_NONE;
- int bpc = 8;
-
- if (connector) {
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- struct radeon_connector_atom_dig *dig_connector =
- radeon_connector->con_priv;
-
- dp_clock = dig_connector->dp_clock;
- dp_lane_count = dig_connector->dp_lane_count;
- hpd_id = radeon_connector->hpd.hpd;
- bpc = connector->display_info.bpc;
- }
-
- /* no dig encoder assigned */
- if (dig->dig_encoder == -1)
- return;
-
- memset(&args, 0, sizeof(args));
-
- if (ASIC_IS_DCE4(rdev))
- index = GetIndexIntoMasterTable(COMMAND, DIGxEncoderControl);
- else {
- if (dig->dig_encoder)
- index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
- else
- index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
- }
-
- if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
- return;
-
- args.v1.ucAction = action;
- args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
- if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE)
- args.v3.ucPanelMode = panel_mode;
- else
- args.v1.ucEncoderMode = atombios_get_encoder_mode(encoder);
-
- if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) ||
- (args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP_MST))
- args.v1.ucLaneNum = dp_lane_count;
- else if (radeon_encoder->pixel_clock > 165000)
- args.v1.ucLaneNum = 8;
- else
- args.v1.ucLaneNum = 4;
-
- if (ASIC_IS_DCE5(rdev)) {
- if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) ||
- (args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP_MST)) {
- if (dp_clock == 270000)
- args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ;
- else if (dp_clock == 540000)
- args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ;
- }
- args.v4.acConfig.ucDigSel = dig->dig_encoder;
- switch (bpc) {
- case 0:
- args.v4.ucBitPerColor = PANEL_BPC_UNDEFINE;
- break;
- case 6:
- args.v4.ucBitPerColor = PANEL_6BIT_PER_COLOR;
- break;
- case 8:
- default:
- args.v4.ucBitPerColor = PANEL_8BIT_PER_COLOR;
- break;
- case 10:
- args.v4.ucBitPerColor = PANEL_10BIT_PER_COLOR;
- break;
- case 12:
- args.v4.ucBitPerColor = PANEL_12BIT_PER_COLOR;
- break;
- case 16:
- args.v4.ucBitPerColor = PANEL_16BIT_PER_COLOR;
- break;
- }
- if (hpd_id == RADEON_HPD_NONE)
- args.v4.ucHPD_ID = 0;
- else
- args.v4.ucHPD_ID = hpd_id + 1;
- } else if (ASIC_IS_DCE4(rdev)) {
- if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) && (dp_clock == 270000))
- args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ;
- args.v3.acConfig.ucDigSel = dig->dig_encoder;
- switch (bpc) {
- case 0:
- args.v3.ucBitPerColor = PANEL_BPC_UNDEFINE;
- break;
- case 6:
- args.v3.ucBitPerColor = PANEL_6BIT_PER_COLOR;
- break;
- case 8:
- default:
- args.v3.ucBitPerColor = PANEL_8BIT_PER_COLOR;
- break;
- case 10:
- args.v3.ucBitPerColor = PANEL_10BIT_PER_COLOR;
- break;
- case 12:
- args.v3.ucBitPerColor = PANEL_12BIT_PER_COLOR;
- break;
- case 16:
- args.v3.ucBitPerColor = PANEL_16BIT_PER_COLOR;
- break;
- }
- } else {
- if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) && (dp_clock == 270000))
- args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER2;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER3;
- break;
- }
- if (dig->linkb)
- args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
- else
- args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
- }
-
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-
-}
-
-union dig_transmitter_control {
- DIG_TRANSMITTER_CONTROL_PS_ALLOCATION v1;
- DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2;
- DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 v3;
- DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 v4;
-};
-
-void
-atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
- struct drm_connector *connector;
- union dig_transmitter_control args;
- int index = 0;
- uint8_t frev, crev;
- bool is_dp = false;
- int pll_id = 0;
- int dp_clock = 0;
- int dp_lane_count = 0;
- int connector_object_id = 0;
- int igp_lane_info = 0;
- int dig_encoder = dig->dig_encoder;
-
- if (action == ATOM_TRANSMITTER_ACTION_INIT) {
- connector = radeon_get_connector_for_encoder_init(encoder);
- /* just needed to avoid bailing in the encoder check. the encoder
- * isn't used for init
- */
- dig_encoder = 0;
- } else
- connector = radeon_get_connector_for_encoder(encoder);
-
- if (connector) {
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- struct radeon_connector_atom_dig *dig_connector =
- radeon_connector->con_priv;
-
- dp_clock = dig_connector->dp_clock;
- dp_lane_count = dig_connector->dp_lane_count;
- connector_object_id =
- (radeon_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
- igp_lane_info = dig_connector->igp_lane_info;
- }
-
- /* no dig encoder assigned */
- if (dig_encoder == -1)
- return;
-
- if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_DP)
- is_dp = true;
-
- memset(&args, 0, sizeof(args));
-
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
- index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- index = GetIndexIntoMasterTable(COMMAND, LVTMATransmitterControl);
- break;
- }
-
- if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
- return;
-
- args.v1.ucAction = action;
- if (action == ATOM_TRANSMITTER_ACTION_INIT) {
- args.v1.usInitInfo = cpu_to_le16(connector_object_id);
- } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
- args.v1.asMode.ucLaneSel = lane_num;
- args.v1.asMode.ucLaneSet = lane_set;
- } else {
- if (is_dp)
- args.v1.usPixelClock =
- cpu_to_le16(dp_clock / 10);
- else if (radeon_encoder->pixel_clock > 165000)
- args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
- else
- args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
- }
- if (ASIC_IS_DCE4(rdev)) {
- if (is_dp)
- args.v3.ucLaneNum = dp_lane_count;
- else if (radeon_encoder->pixel_clock > 165000)
- args.v3.ucLaneNum = 8;
- else
- args.v3.ucLaneNum = 4;
-
- if (dig->linkb)
- args.v3.acConfig.ucLinkSel = 1;
- if (dig_encoder & 1)
- args.v3.acConfig.ucEncoderSel = 1;
-
- /* Select the PLL for the PHY
- * DP PHY should be clocked from external src if there is
- * one.
- */
- if (encoder->crtc) {
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
- pll_id = radeon_crtc->pll_id;
- }
-
- if (ASIC_IS_DCE5(rdev)) {
- /* On DCE5 DCPLL usually generates the DP ref clock */
- if (is_dp) {
- if (rdev->clock.dp_extclk)
- args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_EXTCLK;
- else
- args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_DCPLL;
- } else
- args.v4.acConfig.ucRefClkSource = pll_id;
- } else {
- /* On DCE4, if there is an external clock, it generates the DP ref clock */
- if (is_dp && rdev->clock.dp_extclk)
- args.v3.acConfig.ucRefClkSource = 2; /* external src */
- else
- args.v3.acConfig.ucRefClkSource = pll_id;
- }
-
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- args.v3.acConfig.ucTransmitterSel = 0;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- args.v3.acConfig.ucTransmitterSel = 1;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- args.v3.acConfig.ucTransmitterSel = 2;
- break;
- }
-
- if (is_dp)
- args.v3.acConfig.fCoherentMode = 1; /* DP requires coherent */
- else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
- if (dig->coherent_mode)
- args.v3.acConfig.fCoherentMode = 1;
- if (radeon_encoder->pixel_clock > 165000)
- args.v3.acConfig.fDualLinkConnector = 1;
- }
- } else if (ASIC_IS_DCE32(rdev)) {
- args.v2.acConfig.ucEncoderSel = dig_encoder;
- if (dig->linkb)
- args.v2.acConfig.ucLinkSel = 1;
-
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- args.v2.acConfig.ucTransmitterSel = 0;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- args.v2.acConfig.ucTransmitterSel = 1;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- args.v2.acConfig.ucTransmitterSel = 2;
- break;
- }
-
- if (is_dp) {
- args.v2.acConfig.fCoherentMode = 1;
- args.v2.acConfig.fDPConnector = 1;
- } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
- if (dig->coherent_mode)
- args.v2.acConfig.fCoherentMode = 1;
- if (radeon_encoder->pixel_clock > 165000)
- args.v2.acConfig.fDualLinkConnector = 1;
- }
- } else {
- args.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL;
-
- if (dig_encoder)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
- else
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
-
- if ((rdev->flags & RADEON_IS_IGP) &&
- (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY)) {
- if (is_dp || (radeon_encoder->pixel_clock <= 165000)) {
- if (igp_lane_info & 0x1)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3;
- else if (igp_lane_info & 0x2)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_4_7;
- else if (igp_lane_info & 0x4)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_11;
- else if (igp_lane_info & 0x8)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15;
- } else {
- if (igp_lane_info & 0x3)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7;
- else if (igp_lane_info & 0xc)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15;
- }
- }
-
- if (dig->linkb)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB;
- else
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;
-
- if (is_dp)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
- else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
- if (dig->coherent_mode)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
- if (radeon_encoder->pixel_clock > 165000)
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK;
- }
- }
-
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-}
-
-bool
-atombios_set_edp_panel_power(struct drm_connector *connector, int action)
-{
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- struct drm_device *dev = radeon_connector->base.dev;
- struct radeon_device *rdev = dev->dev_private;
- union dig_transmitter_control args;
- int index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl);
- uint8_t frev, crev;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_eDP)
- goto done;
-
- if (!ASIC_IS_DCE4(rdev))
- goto done;
-
- if ((action != ATOM_TRANSMITTER_ACTION_POWER_ON) &&
- (action != ATOM_TRANSMITTER_ACTION_POWER_OFF))
- goto done;
-
- if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
- goto done;
-
- memset(&args, 0, sizeof(args));
-
- args.v1.ucAction = action;
-
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-
- /* wait for the panel to power up */
- if (action == ATOM_TRANSMITTER_ACTION_POWER_ON) {
- int i;
-
- for (i = 0; i < 300; i++) {
- if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
- return true;
- mdelay(1);
- }
- return false;
- }
-done:
- return true;
-}
-
-union external_encoder_control {
- EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION v1;
- EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION_V3 v3;
-};
-
-static void
-atombios_external_encoder_setup(struct drm_encoder *encoder,
- struct drm_encoder *ext_encoder,
- int action)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_encoder *ext_radeon_encoder = to_radeon_encoder(ext_encoder);
- union external_encoder_control args;
- struct drm_connector *connector;
- int index = GetIndexIntoMasterTable(COMMAND, ExternalEncoderControl);
- u8 frev, crev;
- int dp_clock = 0;
- int dp_lane_count = 0;
- int connector_object_id = 0;
- u32 ext_enum = (ext_radeon_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT;
- int bpc = 8;
-
- if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT)
- connector = radeon_get_connector_for_encoder_init(encoder);
- else
- connector = radeon_get_connector_for_encoder(encoder);
-
- if (connector) {
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- struct radeon_connector_atom_dig *dig_connector =
- radeon_connector->con_priv;
-
- dp_clock = dig_connector->dp_clock;
- dp_lane_count = dig_connector->dp_lane_count;
- connector_object_id =
- (radeon_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
- bpc = connector->display_info.bpc;
- }
-
- memset(&args, 0, sizeof(args));
-
- if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
- return;
-
- switch (frev) {
- case 1:
- /* no params on frev 1 */
- break;
- case 2:
- switch (crev) {
- case 1:
- case 2:
- args.v1.sDigEncoder.ucAction = action;
- args.v1.sDigEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
- args.v1.sDigEncoder.ucEncoderMode = atombios_get_encoder_mode(encoder);
-
- if (args.v1.sDigEncoder.ucEncoderMode == ATOM_ENCODER_MODE_DP) {
- if (dp_clock == 270000)
- args.v1.sDigEncoder.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
- args.v1.sDigEncoder.ucLaneNum = dp_lane_count;
- } else if (radeon_encoder->pixel_clock > 165000)
- args.v1.sDigEncoder.ucLaneNum = 8;
- else
- args.v1.sDigEncoder.ucLaneNum = 4;
- break;
- case 3:
- args.v3.sExtEncoder.ucAction = action;
- if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT)
- args.v3.sExtEncoder.usConnectorId = cpu_to_le16(connector_object_id);
- else
- args.v3.sExtEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
- args.v3.sExtEncoder.ucEncoderMode = atombios_get_encoder_mode(encoder);
-
- if (args.v3.sExtEncoder.ucEncoderMode == ATOM_ENCODER_MODE_DP) {
- if (dp_clock == 270000)
- args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ;
- else if (dp_clock == 540000)
- args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_5_40GHZ;
- args.v3.sExtEncoder.ucLaneNum = dp_lane_count;
- } else if (radeon_encoder->pixel_clock > 165000)
- args.v3.sExtEncoder.ucLaneNum = 8;
- else
- args.v3.sExtEncoder.ucLaneNum = 4;
- switch (ext_enum) {
- case GRAPH_OBJECT_ENUM_ID1:
- args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER1;
- break;
- case GRAPH_OBJECT_ENUM_ID2:
- args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER2;
- break;
- case GRAPH_OBJECT_ENUM_ID3:
- args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER3;
- break;
- }
- switch (bpc) {
- case 0:
- args.v3.sExtEncoder.ucBitPerColor = PANEL_BPC_UNDEFINE;
- break;
- case 6:
- args.v3.sExtEncoder.ucBitPerColor = PANEL_6BIT_PER_COLOR;
- break;
- case 8:
- default:
- args.v3.sExtEncoder.ucBitPerColor = PANEL_8BIT_PER_COLOR;
- break;
- case 10:
- args.v3.sExtEncoder.ucBitPerColor = PANEL_10BIT_PER_COLOR;
- break;
- case 12:
- args.v3.sExtEncoder.ucBitPerColor = PANEL_12BIT_PER_COLOR;
- break;
- case 16:
- args.v3.sExtEncoder.ucBitPerColor = PANEL_16BIT_PER_COLOR;
- break;
- }
- break;
- default:
- DRM_ERROR("Unknown table version: %d, %d\n", frev, crev);
- return;
- }
- break;
- default:
- DRM_ERROR("Unknown table version: %d, %d\n", frev, crev);
- return;
- }
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-}
-
-static void
-atombios_yuv_setup(struct drm_encoder *encoder, bool enable)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
- ENABLE_YUV_PS_ALLOCATION args;
- int index = GetIndexIntoMasterTable(COMMAND, EnableYUV);
- uint32_t temp, reg;
-
- memset(&args, 0, sizeof(args));
-
- if (rdev->family >= CHIP_R600)
- reg = R600_BIOS_3_SCRATCH;
- else
- reg = RADEON_BIOS_3_SCRATCH;
-
- /* XXX: fix up scratch reg handling */
- temp = RREG32(reg);
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
- WREG32(reg, (ATOM_S3_TV1_ACTIVE |
- (radeon_crtc->crtc_id << 18)));
- else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
- WREG32(reg, (ATOM_S3_CV_ACTIVE | (radeon_crtc->crtc_id << 24)));
- else
- WREG32(reg, 0);
-
- if (enable)
- args.ucEnable = ATOM_ENABLE;
- args.ucCRTC = radeon_crtc->crtc_id;
-
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-
- WREG32(reg, temp);
-}
-
-static void
-radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder);
- DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args;
- int index = 0;
- bool is_dig = false;
- bool is_dce5_dac = false;
- bool is_dce5_dvo = false;
-
- memset(&args, 0, sizeof(args));
-
- DRM_DEBUG_KMS("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n",
- radeon_encoder->encoder_id, mode, radeon_encoder->devices,
- radeon_encoder->active_device);
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
- index = GetIndexIntoMasterTable(COMMAND, TMDSAOutputControl);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- is_dig = true;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_DDI:
- index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
- if (ASIC_IS_DCE5(rdev))
- is_dce5_dvo = true;
- else if (ASIC_IS_DCE3(rdev))
- is_dig = true;
- else
- index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_LVDS:
- index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
- index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl);
- else
- index = GetIndexIntoMasterTable(COMMAND, LVTMAOutputControl);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DAC1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
- if (ASIC_IS_DCE5(rdev))
- is_dce5_dac = true;
- else {
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
- index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl);
- else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
- index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl);
- else
- index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl);
- }
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DAC2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
- index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl);
- else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
- index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl);
- else
- index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl);
- break;
- }
-
- if (is_dig) {
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- /* some early dce3.2 boards have a bug in their transmitter control table */
- if ((rdev->family == CHIP_RV710) || (rdev->family == CHIP_RV730))
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
- else
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
- if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_DP) {
- struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
-
- if (connector &&
- (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- struct radeon_connector_atom_dig *radeon_dig_connector =
- radeon_connector->con_priv;
- atombios_set_edp_panel_power(connector,
- ATOM_TRANSMITTER_ACTION_POWER_ON);
- radeon_dig_connector->edp_on = true;
- }
- if (ASIC_IS_DCE4(rdev))
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
- radeon_dp_link_train(encoder, connector);
- if (ASIC_IS_DCE4(rdev))
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0);
- }
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0);
- if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_DP) {
- struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
-
- if (ASIC_IS_DCE4(rdev))
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
- if (connector &&
- (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- struct radeon_connector_atom_dig *radeon_dig_connector =
- radeon_connector->con_priv;
- atombios_set_edp_panel_power(connector,
- ATOM_TRANSMITTER_ACTION_POWER_OFF);
- radeon_dig_connector->edp_on = false;
- }
- }
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLOFF, 0, 0);
- break;
- }
- } else if (is_dce5_dac) {
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- atombios_dac_setup(encoder, ATOM_ENABLE);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- atombios_dac_setup(encoder, ATOM_DISABLE);
- break;
- }
- } else if (is_dce5_dvo) {
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- atombios_dvo_setup(encoder, ATOM_ENABLE);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- atombios_dvo_setup(encoder, ATOM_DISABLE);
- break;
- }
- } else {
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- args.ucAction = ATOM_ENABLE;
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- args.ucAction = ATOM_LCD_BLON;
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
- }
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- args.ucAction = ATOM_DISABLE;
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- args.ucAction = ATOM_LCD_BLOFF;
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
- }
- break;
- }
- }
-
- if (ext_encoder) {
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- default:
- if (ASIC_IS_DCE41(rdev)) {
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_ENABLE_OUTPUT);
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING_OFF);
- } else
- atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- if (ASIC_IS_DCE41(rdev)) {
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING);
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_DISABLE_OUTPUT);
- } else
- atombios_external_encoder_setup(encoder, ext_encoder, ATOM_DISABLE);
- break;
- }
- }
-
- radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
-
-}
-
-union crtc_source_param {
- SELECT_CRTC_SOURCE_PS_ALLOCATION v1;
- SELECT_CRTC_SOURCE_PARAMETERS_V2 v2;
-};
-
-static void
-atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
- union crtc_source_param args;
- int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source);
- uint8_t frev, crev;
- struct radeon_encoder_atom_dig *dig;
-
- memset(&args, 0, sizeof(args));
-
- if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
- return;
-
- switch (frev) {
- case 1:
- switch (crev) {
- case 1:
- default:
- if (ASIC_IS_AVIVO(rdev))
- args.v1.ucCRTC = radeon_crtc->crtc_id;
- else {
- if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) {
- args.v1.ucCRTC = radeon_crtc->crtc_id;
- } else {
- args.v1.ucCRTC = radeon_crtc->crtc_id << 2;
- }
- }
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
- args.v1.ucDevice = ATOM_DEVICE_DFP1_INDEX;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_LVDS:
- case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
- if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT)
- args.v1.ucDevice = ATOM_DEVICE_LCD1_INDEX;
- else
- args.v1.ucDevice = ATOM_DEVICE_DFP3_INDEX;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_DDI:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
- args.v1.ucDevice = ATOM_DEVICE_DFP2_INDEX;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DAC1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
- args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX;
- else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
- args.v1.ucDevice = ATOM_DEVICE_CV_INDEX;
- else
- args.v1.ucDevice = ATOM_DEVICE_CRT1_INDEX;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DAC2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
- args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX;
- else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
- args.v1.ucDevice = ATOM_DEVICE_CV_INDEX;
- else
- args.v1.ucDevice = ATOM_DEVICE_CRT2_INDEX;
- break;
- }
- break;
- case 2:
- args.v2.ucCRTC = radeon_crtc->crtc_id;
- args.v2.ucEncodeMode = atombios_get_encoder_mode(encoder);
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- dig = radeon_encoder->enc_priv;
- switch (dig->dig_encoder) {
- case 0:
- args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
- break;
- case 1:
- args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
- break;
- case 2:
- args.v2.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID;
- break;
- case 3:
- args.v2.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID;
- break;
- case 4:
- args.v2.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID;
- break;
- case 5:
- args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID;
- break;
- }
- break;
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
- args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
- args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
- else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
- args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
- else
- args.v2.ucEncoderID = ASIC_INT_DAC1_ENCODER_ID;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
- args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
- else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
- args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
- else
- args.v2.ucEncoderID = ASIC_INT_DAC2_ENCODER_ID;
- break;
- }
- break;
- }
- break;
- default:
- DRM_ERROR("Unknown table version: %d, %d\n", frev, crev);
- return;
- }
-
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-
- /* update scratch regs with new routing */
- radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
-}
-
-static void
-atombios_apply_encoder_quirks(struct drm_encoder *encoder,
- struct drm_display_mode *mode)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
-
- /* Funky macbooks */
- if ((dev->pdev->device == 0x71C5) &&
- (dev->pdev->subsystem_vendor == 0x106b) &&
- (dev->pdev->subsystem_device == 0x0080)) {
- if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) {
- uint32_t lvtma_bit_depth_control = RREG32(AVIVO_LVTMA_BIT_DEPTH_CONTROL);
-
- lvtma_bit_depth_control &= ~AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_EN;
- lvtma_bit_depth_control &= ~AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN;
-
- WREG32(AVIVO_LVTMA_BIT_DEPTH_CONTROL, lvtma_bit_depth_control);
- }
- }
-
- /* set scaler clears this on some chips */
- if (ASIC_IS_AVIVO(rdev) &&
- (!(radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)))) {
- if (ASIC_IS_DCE4(rdev)) {
- if (mode->flags & DRM_MODE_FLAG_INTERLACE)
- WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset,
- EVERGREEN_INTERLEAVE_EN);
- else
- WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset, 0);
- } else {
- if (mode->flags & DRM_MODE_FLAG_INTERLACE)
- WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset,
- AVIVO_D1MODE_INTERLEAVE_EN);
- else
- WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, 0);
- }
- }
-}
-
-static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_encoder *test_encoder;
- struct radeon_encoder_atom_dig *dig;
- uint32_t dig_enc_in_use = 0;
-
- /* DCE4/5 */
- if (ASIC_IS_DCE4(rdev)) {
- dig = radeon_encoder->enc_priv;
- if (ASIC_IS_DCE41(rdev))
- return radeon_crtc->crtc_id;
- else {
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- if (dig->linkb)
- return 1;
- else
- return 0;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- if (dig->linkb)
- return 3;
- else
- return 2;
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- if (dig->linkb)
- return 5;
- else
- return 4;
- break;
- }
- }
- }
-
- /* on DCE32 and encoder can driver any block so just crtc id */
- if (ASIC_IS_DCE32(rdev)) {
- return radeon_crtc->crtc_id;
- }
-
- /* on DCE3 - LVTMA can only be driven by DIGB */
- list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
- struct radeon_encoder *radeon_test_encoder;
-
- if (encoder == test_encoder)
- continue;
-
- if (!radeon_encoder_is_digital(test_encoder))
- continue;
-
- radeon_test_encoder = to_radeon_encoder(test_encoder);
- dig = radeon_test_encoder->enc_priv;
-
- if (dig->dig_encoder >= 0)
- dig_enc_in_use |= (1 << dig->dig_encoder);
- }
-
- if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA) {
- if (dig_enc_in_use & 0x2)
- DRM_ERROR("LVDS required digital encoder 2 but it was in use - stealing\n");
- return 1;
- }
- if (!(dig_enc_in_use & 1))
- return 0;
- return 1;
-}
-
-/* This only needs to be called once at startup */
-void
-radeon_atom_encoder_init(struct radeon_device *rdev)
-{
- struct drm_device *dev = rdev->ddev;
- struct drm_encoder *encoder;
-
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder);
-
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
- break;
- default:
- break;
- }
-
- if (ext_encoder && ASIC_IS_DCE41(rdev))
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT);
- }
-}
-
-static void
-radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder);
-
- radeon_encoder->pixel_clock = adjusted_mode->clock;
-
- if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE4(rdev)) {
- if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT))
- atombios_yuv_setup(encoder, true);
- else
- atombios_yuv_setup(encoder, false);
- }
-
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_LVDS:
- case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
- atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_ENABLE);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- if (ASIC_IS_DCE4(rdev)) {
- /* disable the transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- /* setup and enable the encoder */
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0);
-
- /* enable the transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
- } else {
- /* disable the encoder and transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0);
-
- /* setup and enable the encoder and transmitter */
- atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0);
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
- }
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DDI:
- case ENCODER_OBJECT_ID_INTERNAL_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
- atombios_dvo_setup(encoder, ATOM_ENABLE);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DAC1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
- case ENCODER_OBJECT_ID_INTERNAL_DAC2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
- atombios_dac_setup(encoder, ATOM_ENABLE);
- if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) {
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
- atombios_tv_setup(encoder, ATOM_ENABLE);
- else
- atombios_tv_setup(encoder, ATOM_DISABLE);
- }
- break;
- }
-
- if (ext_encoder) {
- if (ASIC_IS_DCE41(rdev))
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP);
- else
- atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE);
- }
-
- atombios_apply_encoder_quirks(encoder, adjusted_mode);
-
- if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) {
- r600_hdmi_enable(encoder);
- r600_hdmi_setmode(encoder, adjusted_mode);
- }
-}
-
-static bool
-atombios_dac_load_detect(struct drm_encoder *encoder, struct drm_connector *connector)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
-
- if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT |
- ATOM_DEVICE_CV_SUPPORT |
- ATOM_DEVICE_CRT_SUPPORT)) {
- DAC_LOAD_DETECTION_PS_ALLOCATION args;
- int index = GetIndexIntoMasterTable(COMMAND, DAC_LoadDetection);
- uint8_t frev, crev;
-
- memset(&args, 0, sizeof(args));
-
- if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
- return false;
-
- args.sDacload.ucMisc = 0;
-
- if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) ||
- (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1))
- args.sDacload.ucDacType = ATOM_DAC_A;
- else
- args.sDacload.ucDacType = ATOM_DAC_B;
-
- if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT)
- args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT1_SUPPORT);
- else if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT)
- args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT2_SUPPORT);
- else if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) {
- args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CV_SUPPORT);
- if (crev >= 3)
- args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb;
- } else if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) {
- args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_TV1_SUPPORT);
- if (crev >= 3)
- args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb;
- }
-
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-
- return true;
- } else
- return false;
-}
-
-static enum drm_connector_status
-radeon_atom_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- uint32_t bios_0_scratch;
-
- if (!atombios_dac_load_detect(encoder, connector)) {
- DRM_DEBUG_KMS("detect returned false \n");
- return connector_status_unknown;
- }
-
- if (rdev->family >= CHIP_R600)
- bios_0_scratch = RREG32(R600_BIOS_0_SCRATCH);
- else
- bios_0_scratch = RREG32(RADEON_BIOS_0_SCRATCH);
-
- DRM_DEBUG_KMS("Bios 0 scratch %x %08x\n", bios_0_scratch, radeon_encoder->devices);
- if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) {
- if (bios_0_scratch & ATOM_S0_CRT1_MASK)
- return connector_status_connected;
- }
- if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) {
- if (bios_0_scratch & ATOM_S0_CRT2_MASK)
- return connector_status_connected;
- }
- if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) {
- if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A))
- return connector_status_connected;
- }
- if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) {
- if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A))
- return connector_status_connected; /* CTV */
- else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A))
- return connector_status_connected; /* STV */
- }
- return connector_status_disconnected;
-}
-
-static enum drm_connector_status
-radeon_atom_dig_detect(struct drm_encoder *encoder, struct drm_connector *connector)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder);
- u32 bios_0_scratch;
-
- if (!ASIC_IS_DCE4(rdev))
- return connector_status_unknown;
-
- if (!ext_encoder)
- return connector_status_unknown;
-
- if ((radeon_connector->devices & ATOM_DEVICE_CRT_SUPPORT) == 0)
- return connector_status_unknown;
-
- /* load detect on the dp bridge */
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_DACLOAD_DETECTION);
-
- bios_0_scratch = RREG32(R600_BIOS_0_SCRATCH);
-
- DRM_DEBUG_KMS("Bios 0 scratch %x %08x\n", bios_0_scratch, radeon_encoder->devices);
- if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) {
- if (bios_0_scratch & ATOM_S0_CRT1_MASK)
- return connector_status_connected;
- }
- if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) {
- if (bios_0_scratch & ATOM_S0_CRT2_MASK)
- return connector_status_connected;
- }
- if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) {
- if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A))
- return connector_status_connected;
- }
- if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) {
- if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A))
- return connector_status_connected; /* CTV */
- else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A))
- return connector_status_connected; /* STV */
- }
- return connector_status_disconnected;
-}
-
-void
-radeon_atom_ext_encoder_setup_ddc(struct drm_encoder *encoder)
-{
- struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder);
-
- if (ext_encoder)
- /* ddc_setup on the dp bridge */
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP);
-
-}
-
-static void radeon_atom_encoder_prepare(struct drm_encoder *encoder)
-{
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
-
- if ((radeon_encoder->active_device &
- (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) ||
- radeon_encoder_is_dp_bridge(encoder)) {
- struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
- if (dig)
- dig->dig_encoder = radeon_atom_pick_dig_encoder(encoder);
- }
-
- radeon_atom_output_lock(encoder, true);
- radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
-
- if (connector) {
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
-
- /* select the clock/data port if it uses a router */
- if (radeon_connector->router.cd_valid)
- radeon_router_select_cd_port(radeon_connector);
-
- /* turn eDP panel on for mode set */
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
- atombios_set_edp_panel_power(connector,
- ATOM_TRANSMITTER_ACTION_POWER_ON);
- }
-
- /* this is needed for the pll/ss setup to work correctly in some cases */
- atombios_set_encoder_crtc_source(encoder);
-}
-
-static void radeon_atom_encoder_commit(struct drm_encoder *encoder)
-{
- radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
- radeon_atom_output_lock(encoder, false);
-}
-
-static void radeon_atom_encoder_disable(struct drm_encoder *encoder)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_encoder_atom_dig *dig;
-
- /* check for pre-DCE3 cards with shared encoders;
- * can't really use the links individually, so don't disable
- * the encoder if it's in use by another connector
- */
- if (!ASIC_IS_DCE3(rdev)) {
- struct drm_encoder *other_encoder;
- struct radeon_encoder *other_radeon_encoder;
-
- list_for_each_entry(other_encoder, &dev->mode_config.encoder_list, head) {
- other_radeon_encoder = to_radeon_encoder(other_encoder);
- if ((radeon_encoder->encoder_id == other_radeon_encoder->encoder_id) &&
- drm_helper_encoder_in_use(other_encoder))
- goto disable_done;
- }
- }
-
- radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
-
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_LVDS:
- case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
- atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_DISABLE);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- if (ASIC_IS_DCE4(rdev))
- /* disable the transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- else {
- /* disable the encoder and transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0);
- }
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DDI:
- case ENCODER_OBJECT_ID_INTERNAL_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
- atombios_dvo_setup(encoder, ATOM_DISABLE);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DAC1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
- case ENCODER_OBJECT_ID_INTERNAL_DAC2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
- atombios_dac_setup(encoder, ATOM_DISABLE);
- if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
- atombios_tv_setup(encoder, ATOM_DISABLE);
- break;
- }
-
-disable_done:
- if (radeon_encoder_is_digital(encoder)) {
- if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI)
- r600_hdmi_disable(encoder);
- dig = radeon_encoder->enc_priv;
- dig->dig_encoder = -1;
- }
- radeon_encoder->active_device = 0;
-}
-
-/* these are handled by the primary encoders */
-static void radeon_atom_ext_prepare(struct drm_encoder *encoder)
-{
-
-}
-
-static void radeon_atom_ext_commit(struct drm_encoder *encoder)
-{
-
-}
-
-static void
-radeon_atom_ext_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
-
-}
-
-static void radeon_atom_ext_disable(struct drm_encoder *encoder)
-{
-
-}
-
-static void
-radeon_atom_ext_dpms(struct drm_encoder *encoder, int mode)
-{
-
-}
-
-static bool radeon_atom_ext_mode_fixup(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- return true;
-}
-
-static const struct drm_encoder_helper_funcs radeon_atom_ext_helper_funcs = {
- .dpms = radeon_atom_ext_dpms,
- .mode_fixup = radeon_atom_ext_mode_fixup,
- .prepare = radeon_atom_ext_prepare,
- .mode_set = radeon_atom_ext_mode_set,
- .commit = radeon_atom_ext_commit,
- .disable = radeon_atom_ext_disable,
- /* no detect for TMDS/LVDS yet */
-};
-
-static const struct drm_encoder_helper_funcs radeon_atom_dig_helper_funcs = {
- .dpms = radeon_atom_encoder_dpms,
- .mode_fixup = radeon_atom_mode_fixup,
- .prepare = radeon_atom_encoder_prepare,
- .mode_set = radeon_atom_encoder_mode_set,
- .commit = radeon_atom_encoder_commit,
- .disable = radeon_atom_encoder_disable,
- .detect = radeon_atom_dig_detect,
-};
-
-static const struct drm_encoder_helper_funcs radeon_atom_dac_helper_funcs = {
- .dpms = radeon_atom_encoder_dpms,
- .mode_fixup = radeon_atom_mode_fixup,
- .prepare = radeon_atom_encoder_prepare,
- .mode_set = radeon_atom_encoder_mode_set,
- .commit = radeon_atom_encoder_commit,
- .detect = radeon_atom_dac_detect,
-};
-
-void radeon_enc_destroy(struct drm_encoder *encoder)
-{
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- kfree(radeon_encoder->enc_priv);
- drm_encoder_cleanup(encoder);
- kfree(radeon_encoder);
-}
-
-static const struct drm_encoder_funcs radeon_atom_enc_funcs = {
- .destroy = radeon_enc_destroy,
-};
-
-struct radeon_encoder_atom_dac *
-radeon_atombios_set_dac_info(struct radeon_encoder *radeon_encoder)
-{
- struct drm_device *dev = radeon_encoder->base.dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder_atom_dac *dac = kzalloc(sizeof(struct radeon_encoder_atom_dac), GFP_KERNEL);
-
- if (!dac)
- return NULL;
-
- dac->tv_std = radeon_atombios_get_tv_info(rdev);
- return dac;
-}
-
-struct radeon_encoder_atom_dig *
-radeon_atombios_set_dig_info(struct radeon_encoder *radeon_encoder)
-{
- int encoder_enum = (radeon_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT;
- struct radeon_encoder_atom_dig *dig = kzalloc(sizeof(struct radeon_encoder_atom_dig), GFP_KERNEL);
-
- if (!dig)
- return NULL;
-
- /* coherent mode by default */
- dig->coherent_mode = true;
- dig->dig_encoder = -1;
-
- if (encoder_enum == 2)
- dig->linkb = true;
- else
- dig->linkb = false;
-
- return dig;
-}
-
-void
-radeon_add_atom_encoder(struct drm_device *dev,
- uint32_t encoder_enum,
- uint32_t supported_device,
- u16 caps)
-{
- struct radeon_device *rdev = dev->dev_private;
- struct drm_encoder *encoder;
- struct radeon_encoder *radeon_encoder;
-
- /* see if we already added it */
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- radeon_encoder = to_radeon_encoder(encoder);
- if (radeon_encoder->encoder_enum == encoder_enum) {
- radeon_encoder->devices |= supported_device;
- return;
- }
-
- }
-
- /* add a new one */
- radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL);
- if (!radeon_encoder)
- return;
-
- encoder = &radeon_encoder->base;
- switch (rdev->num_crtc) {
- case 1:
- encoder->possible_crtcs = 0x1;
- break;
- case 2:
- default:
- encoder->possible_crtcs = 0x3;
- break;
- case 6:
- encoder->possible_crtcs = 0x3f;
- break;
- }
-
- radeon_encoder->enc_priv = NULL;
-
- radeon_encoder->encoder_enum = encoder_enum;
- radeon_encoder->encoder_id = (encoder_enum & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
- radeon_encoder->devices = supported_device;
- radeon_encoder->rmx_type = RMX_OFF;
- radeon_encoder->underscan_type = UNDERSCAN_OFF;
- radeon_encoder->is_ext_encoder = false;
- radeon_encoder->caps = caps;
-
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_LVDS:
- case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- radeon_encoder->rmx_type = RMX_FULL;
- drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS);
- radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder);
- } else {
- drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
- radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
- }
- drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DAC1:
- drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC);
- radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder);
- drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DAC2:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
- drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TVDAC);
- radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder);
- drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs);
- break;
- case ENCODER_OBJECT_ID_INTERNAL_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_DDI:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- radeon_encoder->rmx_type = RMX_FULL;
- drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS);
- radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder);
- } else if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) {
- drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC);
- radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
- } else {
- drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
- radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
- }
- drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
- break;
- case ENCODER_OBJECT_ID_SI170B:
- case ENCODER_OBJECT_ID_CH7303:
- case ENCODER_OBJECT_ID_EXTERNAL_SDVOA:
- case ENCODER_OBJECT_ID_EXTERNAL_SDVOB:
- case ENCODER_OBJECT_ID_TITFP513:
- case ENCODER_OBJECT_ID_VT1623:
- case ENCODER_OBJECT_ID_HDMI_SI1930:
- case ENCODER_OBJECT_ID_TRAVIS:
- case ENCODER_OBJECT_ID_NUTMEG:
- /* these are handled by the primary encoders */
- radeon_encoder->is_ext_encoder = true;
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
- drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS);
- else if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT))
- drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC);
- else
- drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
- drm_encoder_helper_add(encoder, &radeon_atom_ext_helper_funcs);
- break;
- }
-}
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index 021d2b6b556f..76ec0e9ed8ae 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -29,7 +29,7 @@
* Dave Airlie
*/
#include <linux/seq_file.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/kref.h>
@@ -48,7 +48,7 @@ static void radeon_fence_write(struct radeon_device *rdev, u32 seq)
scratch_index = R600_WB_EVENT_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base;
else
scratch_index = RADEON_WB_SCRATCH_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base;
- rdev->wb.wb[scratch_index/4] = cpu_to_le32(seq);;
+ rdev->wb.wb[scratch_index/4] = cpu_to_le32(seq);
} else
WREG32(rdev->fence_drv.scratch_reg, seq);
}
@@ -263,7 +263,7 @@ retry:
*/
if (seq == rdev->fence_drv.last_seq && radeon_gpu_is_lockup(rdev)) {
/* good news we believe it's a lockup */
- WARN(1, "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n",
+ printk(KERN_WARNING "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n",
fence->seq, seq);
/* FIXME: what should we do ? marking everyone
* as signaled for now
diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
index a533f52fd163..ba7ab79e12c1 100644
--- a/drivers/gpu/drm/radeon/radeon_gart.c
+++ b/drivers/gpu/drm/radeon/radeon_gart.c
@@ -49,27 +49,27 @@ int radeon_gart_table_ram_alloc(struct radeon_device *rdev)
rdev->gart.table_size >> PAGE_SHIFT);
}
#endif
- rdev->gart.table.ram.ptr = ptr;
- memset((void *)rdev->gart.table.ram.ptr, 0, rdev->gart.table_size);
+ rdev->gart.ptr = ptr;
+ memset((void *)rdev->gart.ptr, 0, rdev->gart.table_size);
return 0;
}
void radeon_gart_table_ram_free(struct radeon_device *rdev)
{
- if (rdev->gart.table.ram.ptr == NULL) {
+ if (rdev->gart.ptr == NULL) {
return;
}
#ifdef CONFIG_X86
if (rdev->family == CHIP_RS400 || rdev->family == CHIP_RS480 ||
rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) {
- set_memory_wb((unsigned long)rdev->gart.table.ram.ptr,
+ set_memory_wb((unsigned long)rdev->gart.ptr,
rdev->gart.table_size >> PAGE_SHIFT);
}
#endif
pci_free_consistent(rdev->pdev, rdev->gart.table_size,
- (void *)rdev->gart.table.ram.ptr,
+ (void *)rdev->gart.ptr,
rdev->gart.table_addr);
- rdev->gart.table.ram.ptr = NULL;
+ rdev->gart.ptr = NULL;
rdev->gart.table_addr = 0;
}
@@ -77,10 +77,10 @@ int radeon_gart_table_vram_alloc(struct radeon_device *rdev)
{
int r;
- if (rdev->gart.table.vram.robj == NULL) {
+ if (rdev->gart.robj == NULL) {
r = radeon_bo_create(rdev, rdev->gart.table_size,
PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM,
- &rdev->gart.table.vram.robj);
+ &rdev->gart.robj);
if (r) {
return r;
}
@@ -93,38 +93,46 @@ int radeon_gart_table_vram_pin(struct radeon_device *rdev)
uint64_t gpu_addr;
int r;
- r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
+ r = radeon_bo_reserve(rdev->gart.robj, false);
if (unlikely(r != 0))
return r;
- r = radeon_bo_pin(rdev->gart.table.vram.robj,
+ r = radeon_bo_pin(rdev->gart.robj,
RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
if (r) {
- radeon_bo_unreserve(rdev->gart.table.vram.robj);
+ radeon_bo_unreserve(rdev->gart.robj);
return r;
}
- r = radeon_bo_kmap(rdev->gart.table.vram.robj,
- (void **)&rdev->gart.table.vram.ptr);
+ r = radeon_bo_kmap(rdev->gart.robj, &rdev->gart.ptr);
if (r)
- radeon_bo_unpin(rdev->gart.table.vram.robj);
- radeon_bo_unreserve(rdev->gart.table.vram.robj);
+ radeon_bo_unpin(rdev->gart.robj);
+ radeon_bo_unreserve(rdev->gart.robj);
rdev->gart.table_addr = gpu_addr;
return r;
}
-void radeon_gart_table_vram_free(struct radeon_device *rdev)
+void radeon_gart_table_vram_unpin(struct radeon_device *rdev)
{
int r;
- if (rdev->gart.table.vram.robj == NULL) {
+ if (rdev->gart.robj == NULL) {
return;
}
- r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
+ r = radeon_bo_reserve(rdev->gart.robj, false);
if (likely(r == 0)) {
- radeon_bo_kunmap(rdev->gart.table.vram.robj);
- radeon_bo_unpin(rdev->gart.table.vram.robj);
- radeon_bo_unreserve(rdev->gart.table.vram.robj);
+ radeon_bo_kunmap(rdev->gart.robj);
+ radeon_bo_unpin(rdev->gart.robj);
+ radeon_bo_unreserve(rdev->gart.robj);
+ rdev->gart.ptr = NULL;
}
- radeon_bo_unref(&rdev->gart.table.vram.robj);
+}
+
+void radeon_gart_table_vram_free(struct radeon_device *rdev)
+{
+ if (rdev->gart.robj == NULL) {
+ return;
+ }
+ radeon_gart_table_vram_unpin(rdev);
+ radeon_bo_unref(&rdev->gart.robj);
}
@@ -142,7 +150,7 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset,
u64 page_base;
if (!rdev->gart.ready) {
- WARN(1, "trying to unbind memory to unitialized GART !\n");
+ WARN(1, "trying to unbind memory from uninitialized GART !\n");
return;
}
t = offset / RADEON_GPU_PAGE_SIZE;
@@ -151,12 +159,14 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset,
if (rdev->gart.pages[p]) {
if (!rdev->gart.ttm_alloced[p])
pci_unmap_page(rdev->pdev, rdev->gart.pages_addr[p],
- PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
rdev->gart.pages[p] = NULL;
rdev->gart.pages_addr[p] = rdev->dummy_page.addr;
page_base = rdev->gart.pages_addr[p];
for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) {
- radeon_gart_set_page(rdev, t, page_base);
+ if (rdev->gart.ptr) {
+ radeon_gart_set_page(rdev, t, page_base);
+ }
page_base += RADEON_GPU_PAGE_SIZE;
}
}
@@ -174,7 +184,7 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset,
int i, j;
if (!rdev->gart.ready) {
- WARN(1, "trying to bind memory to unitialized GART !\n");
+ WARN(1, "trying to bind memory to uninitialized GART !\n");
return -EINVAL;
}
t = offset / RADEON_GPU_PAGE_SIZE;
@@ -199,10 +209,12 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset,
}
}
rdev->gart.pages[p] = pagelist[i];
- page_base = rdev->gart.pages_addr[p];
- for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) {
- radeon_gart_set_page(rdev, t, page_base);
- page_base += RADEON_GPU_PAGE_SIZE;
+ if (rdev->gart.ptr) {
+ page_base = rdev->gart.pages_addr[p];
+ for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) {
+ radeon_gart_set_page(rdev, t, page_base);
+ page_base += RADEON_GPU_PAGE_SIZE;
+ }
}
}
mb();
@@ -215,6 +227,9 @@ void radeon_gart_restore(struct radeon_device *rdev)
int i, j, t;
u64 page_base;
+ if (!rdev->gart.ptr) {
+ return;
+ }
for (i = 0, t = 0; i < rdev->gart.num_cpu_pages; i++) {
page_base = rdev->gart.pages_addr[i];
for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) {
diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
index 781196db792f..7bb1b079f480 100644
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
@@ -23,6 +23,8 @@
* Authors: Dave Airlie
* Alex Deucher
*/
+#include <linux/export.h>
+
#include "drmP.h"
#include "radeon_drm.h"
#include "radeon.h"
@@ -34,20 +36,20 @@
*/
bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
{
- u8 out_buf[] = { 0x0, 0x0};
- u8 buf[2];
+ u8 out = 0x0;
+ u8 buf[8];
int ret;
struct i2c_msg msgs[] = {
{
.addr = 0x50,
.flags = 0,
.len = 1,
- .buf = out_buf,
+ .buf = &out,
},
{
.addr = 0x50,
.flags = I2C_M_RD,
- .len = 1,
+ .len = 8,
.buf = buf,
}
};
@@ -57,16 +59,27 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
radeon_router_select_ddc_port(radeon_connector);
ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2);
- if (ret == 2)
- return true;
-
- return false;
+ if (ret != 2)
+ /* Couldn't find an accessible DDC on this connector */
+ return false;
+ /* Probe also for valid EDID header
+ * EDID header starts with:
+ * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00.
+ * Only the first 6 bytes must be valid as
+ * drm_edid_block_valid() can fix the last 2 bytes */
+ if (drm_edid_header_is_valid(buf) < 6) {
+ /* Couldn't find an accessible EDID on this
+ * connector */
+ return false;
+ }
+ return true;
}
/* bit banging i2c */
-static void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state)
+static int pre_xfer(struct i2c_adapter *i2c_adap)
{
+ struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
struct radeon_device *rdev = i2c->dev->dev_private;
struct radeon_i2c_bus_rec *rec = &i2c->rec;
uint32_t temp;
@@ -121,19 +134,30 @@ static void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state)
WREG32(rec->en_data_reg, temp);
/* mask the gpio pins for software use */
- temp = RREG32(rec->mask_clk_reg);
- if (lock_state)
- temp |= rec->mask_clk_mask;
- else
- temp &= ~rec->mask_clk_mask;
+ temp = RREG32(rec->mask_clk_reg) | rec->mask_clk_mask;
WREG32(rec->mask_clk_reg, temp);
temp = RREG32(rec->mask_clk_reg);
+ temp = RREG32(rec->mask_data_reg) | rec->mask_data_mask;
+ WREG32(rec->mask_data_reg, temp);
temp = RREG32(rec->mask_data_reg);
- if (lock_state)
- temp |= rec->mask_data_mask;
- else
- temp &= ~rec->mask_data_mask;
+
+ return 0;
+}
+
+static void post_xfer(struct i2c_adapter *i2c_adap)
+{
+ struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
+ struct radeon_device *rdev = i2c->dev->dev_private;
+ struct radeon_i2c_bus_rec *rec = &i2c->rec;
+ uint32_t temp;
+
+ /* unmask the gpio pins for software use */
+ temp = RREG32(rec->mask_clk_reg) & ~rec->mask_clk_mask;
+ WREG32(rec->mask_clk_reg, temp);
+ temp = RREG32(rec->mask_clk_reg);
+
+ temp = RREG32(rec->mask_data_reg) & ~rec->mask_data_mask;
WREG32(rec->mask_data_reg, temp);
temp = RREG32(rec->mask_data_reg);
}
@@ -193,22 +217,6 @@ static void set_data(void *i2c_priv, int data)
WREG32(rec->en_data_reg, val);
}
-static int pre_xfer(struct i2c_adapter *i2c_adap)
-{
- struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
-
- radeon_i2c_do_lock(i2c, 1);
-
- return 0;
-}
-
-static void post_xfer(struct i2c_adapter *i2c_adap)
-{
- struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
-
- radeon_i2c_do_lock(i2c, 0);
-}
-
/* hw i2c */
static u32 radeon_get_i2c_prescale(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c
index 465746bd51b7..00da38424dfc 100644
--- a/drivers/gpu/drm/radeon/radeon_irq.c
+++ b/drivers/gpu/drm/radeon/radeon_irq.c
@@ -129,7 +129,7 @@ void radeon_disable_vblank(struct drm_device *dev, int crtc)
}
}
-static inline u32 radeon_acknowledge_irqs(drm_radeon_private_t *dev_priv, u32 *r500_disp_int)
+static u32 radeon_acknowledge_irqs(drm_radeon_private_t *dev_priv, u32 *r500_disp_int)
{
u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS);
u32 irq_mask = RADEON_SW_INT_TEST;
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 9ec830c77af0..8f86aeb26693 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -67,10 +67,10 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
/* Disable *all* interrupts */
rdev->irq.sw_int = false;
rdev->irq.gui_idle = false;
- for (i = 0; i < rdev->num_crtc; i++)
- rdev->irq.crtc_vblank_int[i] = false;
- for (i = 0; i < 6; i++) {
+ for (i = 0; i < RADEON_MAX_HPD_PINS; i++)
rdev->irq.hpd[i] = false;
+ for (i = 0; i < RADEON_MAX_CRTCS; i++) {
+ rdev->irq.crtc_vblank_int[i] = false;
rdev->irq.pflip[i] = false;
}
radeon_irq_set(rdev);
@@ -99,15 +99,55 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev)
/* Disable *all* interrupts */
rdev->irq.sw_int = false;
rdev->irq.gui_idle = false;
- for (i = 0; i < rdev->num_crtc; i++)
- rdev->irq.crtc_vblank_int[i] = false;
- for (i = 0; i < 6; i++) {
+ for (i = 0; i < RADEON_MAX_HPD_PINS; i++)
rdev->irq.hpd[i] = false;
+ for (i = 0; i < RADEON_MAX_CRTCS; i++) {
+ rdev->irq.crtc_vblank_int[i] = false;
rdev->irq.pflip[i] = false;
}
radeon_irq_set(rdev);
}
+static bool radeon_msi_ok(struct radeon_device *rdev)
+{
+ /* RV370/RV380 was first asic with MSI support */
+ if (rdev->family < CHIP_RV380)
+ return false;
+
+ /* MSIs don't work on AGP */
+ if (rdev->flags & RADEON_IS_AGP)
+ return false;
+
+ /* force MSI on */
+ if (radeon_msi == 1)
+ return true;
+ else if (radeon_msi == 0)
+ return false;
+
+ /* Quirks */
+ /* HP RS690 only seems to work with MSIs. */
+ if ((rdev->pdev->device == 0x791f) &&
+ (rdev->pdev->subsystem_vendor == 0x103c) &&
+ (rdev->pdev->subsystem_device == 0x30c2))
+ return true;
+
+ /* Dell RS690 only seems to work with MSIs. */
+ if ((rdev->pdev->device == 0x791f) &&
+ (rdev->pdev->subsystem_vendor == 0x1028) &&
+ (rdev->pdev->subsystem_device == 0x01fd))
+ return true;
+
+ if (rdev->flags & RADEON_IS_IGP) {
+ /* APUs work fine with MSIs */
+ if (rdev->family >= CHIP_PALM)
+ return true;
+ /* lots of IGPs have problems with MSIs */
+ return false;
+ }
+
+ return true;
+}
+
int radeon_irq_kms_init(struct radeon_device *rdev)
{
int i;
@@ -124,12 +164,8 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
}
/* enable msi */
rdev->msi_enabled = 0;
- /* MSIs don't seem to work reliably on all IGP
- * chips. Disable MSI on them for now.
- */
- if ((rdev->family >= CHIP_RV380) &&
- ((!(rdev->flags & RADEON_IS_IGP)) || (rdev->family >= CHIP_PALM)) &&
- (!(rdev->flags & RADEON_IS_AGP))) {
+
+ if (radeon_msi_ok(rdev)) {
int ret = pci_enable_msi(rdev->pdev);
if (!ret) {
rdev->msi_enabled = 1;
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index bd58af658581..be2c1224e68a 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -60,7 +60,7 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
/* update BUS flag */
if (drm_pci_device_is_agp(dev)) {
flags |= RADEON_IS_AGP;
- } else if (drm_pci_device_is_pcie(dev)) {
+ } else if (pci_is_pcie(dev->pdev)) {
flags |= RADEON_IS_PCIE;
} else {
flags |= RADEON_IS_PCI;
@@ -237,6 +237,19 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
case RADEON_INFO_FUSION_GART_WORKING:
value = 1;
break;
+ case RADEON_INFO_BACKEND_MAP:
+ if (rdev->family >= CHIP_CAYMAN)
+ value = rdev->config.cayman.backend_map;
+ else if (rdev->family >= CHIP_CEDAR)
+ value = rdev->config.evergreen.backend_map;
+ else if (rdev->family >= CHIP_RV770)
+ value = rdev->config.rv770.backend_map;
+ else if (rdev->family >= CHIP_R600)
+ value = rdev->config.r600.backend_map;
+ else {
+ return -EINVAL;
+ }
+ break;
default:
DRM_DEBUG_KMS("Invalid request %d\n", info->request);
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index 41a5d48e657b..daadf2111040 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -991,12 +991,6 @@ static bool radeon_crtc_mode_fixup(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- struct drm_device *dev = crtc->dev;
- struct radeon_device *rdev = dev->dev_private;
-
- /* adjust pm to upcoming mode change */
- radeon_pm_compute_clocks(rdev);
-
if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode))
return false;
return true;
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_tv.c b/drivers/gpu/drm/radeon/radeon_legacy_tv.c
index c7b6cb428d09..b37ec0f1413a 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_tv.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_tv.c
@@ -864,7 +864,7 @@ void radeon_legacy_tv_adjust_crtc_reg(struct drm_encoder *encoder,
*v_sync_strt_wid = tmp;
}
-static inline int get_post_div(int value)
+static int get_post_div(int value)
{
int post_div;
switch (value) {
diff --git a/drivers/gpu/drm/radeon/radeon_mem.c b/drivers/gpu/drm/radeon/radeon_mem.c
index ed95155c4b1d..988548efea92 100644
--- a/drivers/gpu/drm/radeon/radeon_mem.c
+++ b/drivers/gpu/drm/radeon/radeon_mem.c
@@ -139,7 +139,7 @@ static int init_heap(struct mem_block **heap, int start, int size)
if (!blocks)
return -ENOMEM;
- *heap = kmalloc(sizeof(**heap), GFP_KERNEL);
+ *heap = kzalloc(sizeof(**heap), GFP_KERNEL);
if (!*heap) {
kfree(blocks);
return -ENOMEM;
@@ -150,7 +150,6 @@ static int init_heap(struct mem_block **heap, int start, int size)
blocks->file_priv = NULL;
blocks->next = blocks->prev = *heap;
- memset(*heap, 0, sizeof(**heap));
(*heap)->file_priv = (struct drm_file *) - 1;
(*heap)->next = (*heap)->prev = blocks;
return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 6df4e3cec0c2..2c2e75ef8a37 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -444,6 +444,7 @@ struct radeon_connector {
struct edid *edid;
void *con_priv;
bool dac_load_detect;
+ bool detected_by_load; /* if the connection status was determined by load */
uint16_t connector_object_id;
struct radeon_hpd hpd;
struct radeon_router router;
@@ -455,6 +456,8 @@ struct radeon_framebuffer {
struct drm_gem_object *obj;
};
+#define ENCODER_MODE_IS_DP(em) (((em) == ATOM_ENCODER_MODE_DP) || \
+ ((em) == ATOM_ENCODER_MODE_DP_MST))
extern enum radeon_tv_std
radeon_combios_get_tv_info(struct radeon_device *rdev);
@@ -464,8 +467,8 @@ radeon_atombios_get_tv_info(struct radeon_device *rdev);
extern struct drm_connector *
radeon_get_connector_for_encoder(struct drm_encoder *encoder);
-extern bool radeon_encoder_is_dp_bridge(struct drm_encoder *encoder);
-extern bool radeon_connector_encoder_is_dp_bridge(struct drm_connector *connector);
+extern u16 radeon_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder);
+extern u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector);
extern bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector);
extern bool radeon_connector_is_dp12_capable(struct drm_connector *connector);
@@ -476,6 +479,7 @@ extern void radeon_dp_set_link_config(struct drm_connector *connector,
struct drm_display_mode *mode);
extern void radeon_dp_link_train(struct drm_encoder *encoder,
struct drm_connector *connector);
+extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector);
extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector);
extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector);
extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode);
@@ -484,7 +488,7 @@ extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
int action, uint8_t lane_num,
uint8_t lane_set);
extern void radeon_atom_ext_encoder_setup_ddc(struct drm_encoder *encoder);
-extern struct drm_encoder *radeon_atom_get_external_encoder(struct drm_encoder *encoder);
+extern struct drm_encoder *radeon_get_external_encoder(struct drm_encoder *encoder);
extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
u8 write_byte, u8 *read_byte);
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 976c3b1b1b6e..1c851521f458 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -515,3 +515,44 @@ int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
}
return 0;
}
+
+int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, bool no_wait)
+{
+ int r;
+
+ r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
+ if (unlikely(r != 0))
+ return r;
+ spin_lock(&bo->tbo.bdev->fence_lock);
+ if (mem_type)
+ *mem_type = bo->tbo.mem.mem_type;
+ if (bo->tbo.sync_obj)
+ r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
+ spin_unlock(&bo->tbo.bdev->fence_lock);
+ ttm_bo_unreserve(&bo->tbo);
+ return r;
+}
+
+
+/**
+ * radeon_bo_reserve - reserve bo
+ * @bo: bo structure
+ * @no_wait: don't sleep while trying to reserve (return -EBUSY)
+ *
+ * Returns:
+ * -EBUSY: buffer is busy and @no_wait is true
+ * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
+ * a signal. Release all buffer reservations and return to user-space.
+ */
+int radeon_bo_reserve(struct radeon_bo *bo, bool no_wait)
+{
+ int r;
+
+ r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
+ if (unlikely(r != 0)) {
+ if (r != -ERESTARTSYS)
+ dev_err(bo->rdev->dev, "%p reserve failed\n", bo);
+ return r;
+ }
+ return 0;
+}
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index ede6c13628f2..b07f0f9b8627 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -52,28 +52,7 @@ static inline unsigned radeon_mem_type_to_domain(u32 mem_type)
return 0;
}
-/**
- * radeon_bo_reserve - reserve bo
- * @bo: bo structure
- * @no_wait: don't sleep while trying to reserve (return -EBUSY)
- *
- * Returns:
- * -EBUSY: buffer is busy and @no_wait is true
- * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
- * a signal. Release all buffer reservations and return to user-space.
- */
-static inline int radeon_bo_reserve(struct radeon_bo *bo, bool no_wait)
-{
- int r;
-
- r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
- if (unlikely(r != 0)) {
- if (r != -ERESTARTSYS)
- dev_err(bo->rdev->dev, "%p reserve failed\n", bo);
- return r;
- }
- return 0;
-}
+int radeon_bo_reserve(struct radeon_bo *bo, bool no_wait);
static inline void radeon_bo_unreserve(struct radeon_bo *bo)
{
@@ -118,23 +97,8 @@ static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
return bo->tbo.addr_space_offset;
}
-static inline int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
- bool no_wait)
-{
- int r;
-
- r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
- if (unlikely(r != 0))
- return r;
- spin_lock(&bo->tbo.bdev->fence_lock);
- if (mem_type)
- *mem_type = bo->tbo.mem.mem_type;
- if (bo->tbo.sync_obj)
- r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
- spin_unlock(&bo->tbo.bdev->fence_lock);
- ttm_bo_unreserve(&bo->tbo);
- return r;
-}
+extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
+ bool no_wait);
extern int radeon_bo_create(struct radeon_device *rdev,
unsigned long size, int byte_align,
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index aaa19dc418a0..78a665bd9519 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -53,6 +53,24 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev);
#define ACPI_AC_CLASS "ac_adapter"
+int radeon_pm_get_type_index(struct radeon_device *rdev,
+ enum radeon_pm_state_type ps_type,
+ int instance)
+{
+ int i;
+ int found_instance = -1;
+
+ for (i = 0; i < rdev->pm.num_power_states; i++) {
+ if (rdev->pm.power_state[i].type == ps_type) {
+ found_instance++;
+ if (found_instance == instance)
+ return i;
+ }
+ }
+ /* return default if no match */
+ return rdev->pm.default_power_state_index;
+}
+
#ifdef CONFIG_ACPI
static int radeon_acpi_event(struct notifier_block *nb,
unsigned long val,
@@ -594,6 +612,9 @@ int radeon_pm_init(struct radeon_device *rdev)
if (rdev->pm.default_vddc)
radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
SET_VOLTAGE_TYPE_ASIC_VDDC);
+ if (rdev->pm.default_vddci)
+ radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
+ SET_VOLTAGE_TYPE_ASIC_VDDCI);
if (rdev->pm.default_sclk)
radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
if (rdev->pm.default_mclk)
diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h
index ec93a75369e6..b4ce86455707 100644
--- a/drivers/gpu/drm/radeon/radeon_reg.h
+++ b/drivers/gpu/drm/radeon/radeon_reg.h
@@ -300,6 +300,8 @@
# define RADEON_BUS_READ_BURST (1 << 30)
#define RADEON_BUS_CNTL1 0x0034
# define RADEON_BUS_WAIT_ON_LOCK_EN (1 << 4)
+#define RV370_BUS_CNTL 0x004c
+# define RV370_BUS_BIOS_DIS_ROM (1 << 2)
/* rv370/rv380, rv410, r423/r430/r480, r5xx */
#define RADEON_MSI_REARM_EN 0x0160
# define RV370_MSI_REARM_EN (1 << 0)
@@ -3293,7 +3295,7 @@
# define RADEON_RB_BUFSZ_MASK (0x3f << 0)
# define RADEON_RB_BLKSZ_SHIFT 8
# define RADEON_RB_BLKSZ_MASK (0x3f << 8)
-# define RADEON_BUF_SWAP_32BIT (1 << 17)
+# define RADEON_BUF_SWAP_32BIT (2 << 16)
# define RADEON_MAX_FETCH_SHIFT 18
# define RADEON_MAX_FETCH_MASK (0x3 << 18)
# define RADEON_RB_NO_UPDATE (1 << 27)
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index 08c0233db1b8..49d58202202c 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -35,6 +35,44 @@
int radeon_debugfs_ib_init(struct radeon_device *rdev);
+u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx)
+{
+ struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
+ u32 pg_idx, pg_offset;
+ u32 idx_value = 0;
+ int new_page;
+
+ pg_idx = (idx * 4) / PAGE_SIZE;
+ pg_offset = (idx * 4) % PAGE_SIZE;
+
+ if (ibc->kpage_idx[0] == pg_idx)
+ return ibc->kpage[0][pg_offset/4];
+ if (ibc->kpage_idx[1] == pg_idx)
+ return ibc->kpage[1][pg_offset/4];
+
+ new_page = radeon_cs_update_pages(p, pg_idx);
+ if (new_page < 0) {
+ p->parser_error = new_page;
+ return 0;
+ }
+
+ idx_value = ibc->kpage[new_page][pg_offset/4];
+ return idx_value;
+}
+
+void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
+{
+#if DRM_DEBUG_CODE
+ if (rdev->cp.count_dw <= 0) {
+ DRM_ERROR("radeon: writting more dword to ring than expected !\n");
+ }
+#endif
+ rdev->cp.ring[rdev->cp.wptr++] = v;
+ rdev->cp.wptr &= rdev->cp.ptr_mask;
+ rdev->cp.count_dw--;
+ rdev->cp.ring_free_dw--;
+}
+
void radeon_ib_bogus_cleanup(struct radeon_device *rdev)
{
struct radeon_ib *ib, *n;
diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c
index 92e7ea73b7c5..e8422ae7fe74 100644
--- a/drivers/gpu/drm/radeon/radeon_state.c
+++ b/drivers/gpu/drm/radeon/radeon_state.c
@@ -272,12 +272,12 @@ static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t *
return 0;
}
-static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t *
- dev_priv,
- struct drm_file *file_priv,
- drm_radeon_kcmd_buffer_t *
- cmdbuf,
- unsigned int *cmdsz)
+static int radeon_check_and_fixup_packet3(drm_radeon_private_t *
+ dev_priv,
+ struct drm_file *file_priv,
+ drm_radeon_kcmd_buffer_t *
+ cmdbuf,
+ unsigned int *cmdsz)
{
u32 *cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0);
u32 offset, narrays;
@@ -446,8 +446,8 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t *
* CP hardware state programming functions
*/
-static __inline__ void radeon_emit_clip_rect(drm_radeon_private_t * dev_priv,
- struct drm_clip_rect * box)
+static void radeon_emit_clip_rect(drm_radeon_private_t * dev_priv,
+ struct drm_clip_rect * box)
{
RING_LOCALS;
diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c
index dee4a0c1b4b2..602fa3541c45 100644
--- a/drivers/gpu/drm/radeon/radeon_test.c
+++ b/drivers/gpu/drm/radeon/radeon_test.c
@@ -40,10 +40,14 @@ void radeon_test_moves(struct radeon_device *rdev)
size = 1024 * 1024;
/* Number of tests =
- * (Total GTT - IB pool - writeback page - ring buffer) / test size
+ * (Total GTT - IB pool - writeback page - ring buffers) / test size
*/
- n = ((u32)(rdev->mc.gtt_size - RADEON_IB_POOL_SIZE*64*1024 - RADEON_GPU_PAGE_SIZE -
- rdev->cp.ring_size)) / size;
+ n = rdev->mc.gtt_size - RADEON_IB_POOL_SIZE*64*1024 - rdev->cp.ring_size;
+ if (rdev->wb.wb_obj)
+ n -= RADEON_GPU_PAGE_SIZE;
+ if (rdev->ih.ring_obj)
+ n -= rdev->ih.ring_size;
+ n /= size;
gtt_obj = kzalloc(n * sizeof(*gtt_obj), GFP_KERNEL);
if (!gtt_obj) {
@@ -132,9 +136,15 @@ void radeon_test_moves(struct radeon_device *rdev)
gtt_start++, vram_start++) {
if (*vram_start != gtt_start) {
DRM_ERROR("Incorrect GTT->VRAM copy %d: Got 0x%p, "
- "expected 0x%p (GTT map 0x%p-0x%p)\n",
- i, *vram_start, gtt_start, gtt_map,
- gtt_end);
+ "expected 0x%p (GTT/VRAM offset "
+ "0x%16llx/0x%16llx)\n",
+ i, *vram_start, gtt_start,
+ (unsigned long long)
+ (gtt_addr - rdev->mc.gtt_start +
+ (void*)gtt_start - gtt_map),
+ (unsigned long long)
+ (vram_addr - rdev->mc.vram_start +
+ (void*)gtt_start - gtt_map));
radeon_bo_kunmap(vram_obj);
goto out_cleanup;
}
@@ -175,9 +185,15 @@ void radeon_test_moves(struct radeon_device *rdev)
gtt_start++, vram_start++) {
if (*gtt_start != vram_start) {
DRM_ERROR("Incorrect VRAM->GTT copy %d: Got 0x%p, "
- "expected 0x%p (VRAM map 0x%p-0x%p)\n",
- i, *gtt_start, vram_start, vram_map,
- vram_end);
+ "expected 0x%p (VRAM/GTT offset "
+ "0x%16llx/0x%16llx)\n",
+ i, *gtt_start, vram_start,
+ (unsigned long long)
+ (vram_addr - rdev->mc.vram_start +
+ (void*)vram_start - vram_map),
+ (unsigned long long)
+ (gtt_addr - rdev->mc.gtt_start +
+ (void*)vram_start - vram_map));
radeon_bo_kunmap(gtt_obj[i]);
goto out_cleanup;
}
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index 60125ddba1e9..0b5468bfaf54 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -277,7 +277,12 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
DRM_ERROR("Trying to move memory with CP turned off.\n");
return -EINVAL;
}
- r = radeon_copy(rdev, old_start, new_start, new_mem->num_pages, fence);
+
+ BUILD_BUG_ON((PAGE_SIZE % RADEON_GPU_PAGE_SIZE) != 0);
+
+ r = radeon_copy(rdev, old_start, new_start,
+ new_mem->num_pages * (PAGE_SIZE / RADEON_GPU_PAGE_SIZE), /* GPU pages */
+ fence);
/* FIXME: handle copy error */
r = ttm_bo_move_accel_cleanup(bo, (void *)fence, NULL,
evict, no_wait_reserve, no_wait_gpu, new_mem);
@@ -450,6 +455,29 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_
return -EINVAL;
mem->bus.base = rdev->mc.aper_base;
mem->bus.is_iomem = true;
+#ifdef __alpha__
+ /*
+ * Alpha: use bus.addr to hold the ioremap() return,
+ * so we can modify bus.base below.
+ */
+ if (mem->placement & TTM_PL_FLAG_WC)
+ mem->bus.addr =
+ ioremap_wc(mem->bus.base + mem->bus.offset,
+ mem->bus.size);
+ else
+ mem->bus.addr =
+ ioremap_nocache(mem->bus.base + mem->bus.offset,
+ mem->bus.size);
+
+ /*
+ * Alpha: Use just the bus offset plus
+ * the hose/domain memory base for bus.base.
+ * It then can be used to build PTEs for VRAM
+ * access, as done in ttm_bo_vm_fault().
+ */
+ mem->bus.base = (mem->bus.base & 0x0ffffffffUL) +
+ rdev->ddev->hose->dense_mem_base;
+#endif
break;
default:
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/reg_srcs/cayman b/drivers/gpu/drm/radeon/reg_srcs/cayman
index 0aa8e85a9457..2316977eb924 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/cayman
+++ b/drivers/gpu/drm/radeon/reg_srcs/cayman
@@ -208,6 +208,7 @@ cayman 0x9400
0x0002834C PA_SC_VPORT_ZMAX_15
0x00028350 SX_MISC
0x00028354 SX_SURFACE_SYNC
+0x0002835C SX_SCATTER_EXPORT_SIZE
0x00028380 SQ_VTX_SEMANTIC_0
0x00028384 SQ_VTX_SEMANTIC_1
0x00028388 SQ_VTX_SEMANTIC_2
@@ -432,6 +433,7 @@ cayman 0x9400
0x00028700 SPI_STACK_MGMT
0x00028704 SPI_WAVE_MGMT_1
0x00028708 SPI_WAVE_MGMT_2
+0x00028720 GDS_ADDR_BASE
0x00028724 GDS_ADDR_SIZE
0x00028780 CB_BLEND0_CONTROL
0x00028784 CB_BLEND1_CONTROL
diff --git a/drivers/gpu/drm/radeon/reg_srcs/evergreen b/drivers/gpu/drm/radeon/reg_srcs/evergreen
index 0e28cae7ea43..161737a28c23 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/evergreen
+++ b/drivers/gpu/drm/radeon/reg_srcs/evergreen
@@ -44,6 +44,7 @@ evergreen 0x9400
0x00008E28 SQ_STATIC_THREAD_MGMT_3
0x00008E2C SQ_LDS_RESOURCE_MGMT
0x00008E48 SQ_EX_ALLOC_TABLE_SLOTS
+0x00009014 SX_MEMORY_EXPORT_SIZE
0x00009100 SPI_CONFIG_CNTL
0x0000913C SPI_CONFIG_CNTL_1
0x00009508 TA_CNTL_AUX
@@ -442,7 +443,9 @@ evergreen 0x9400
0x000286EC SPI_COMPUTE_NUM_THREAD_X
0x000286F0 SPI_COMPUTE_NUM_THREAD_Y
0x000286F4 SPI_COMPUTE_NUM_THREAD_Z
+0x00028720 GDS_ADDR_BASE
0x00028724 GDS_ADDR_SIZE
+0x00028728 GDS_ORDERED_WAVE_PER_SE
0x00028780 CB_BLEND0_CONTROL
0x00028784 CB_BLEND1_CONTROL
0x00028788 CB_BLEND2_CONTROL
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r600 b/drivers/gpu/drm/radeon/reg_srcs/r600
index ea49752ee99c..0380c5c15f80 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/r600
+++ b/drivers/gpu/drm/radeon/reg_srcs/r600
@@ -429,6 +429,7 @@ r600 0x9400
0x00028438 SX_ALPHA_REF
0x00028410 SX_ALPHA_TEST_CONTROL
0x00028350 SX_MISC
+0x00009014 SX_MEMORY_EXPORT_SIZE
0x00009604 TC_INVALIDATE
0x00009400 TD_FILTER4
0x00009404 TD_FILTER4_1
diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c
index aa6a66eeb4ec..06b90c87f8f3 100644
--- a/drivers/gpu/drm/radeon/rs400.c
+++ b/drivers/gpu/drm/radeon/rs400.c
@@ -77,7 +77,7 @@ int rs400_gart_init(struct radeon_device *rdev)
{
int r;
- if (rdev->gart.table.ram.ptr) {
+ if (rdev->gart.ptr) {
WARN(1, "RS400 GART already initialized\n");
return 0;
}
@@ -182,6 +182,9 @@ int rs400_gart_enable(struct radeon_device *rdev)
/* Enable gart */
WREG32_MC(RS480_AGP_ADDRESS_SPACE_SIZE, (RS480_GART_EN | size_reg));
rs400_gart_tlb_flush(rdev);
+ DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
+ (unsigned)(rdev->mc.gtt_size >> 20),
+ (unsigned long long)rdev->gart.table_addr);
rdev->gart.ready = true;
return 0;
}
@@ -209,6 +212,7 @@ void rs400_gart_fini(struct radeon_device *rdev)
int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
{
uint32_t entry;
+ u32 *gtt = rdev->gart.ptr;
if (i < 0 || i > rdev->gart.num_gpu_pages) {
return -EINVAL;
@@ -218,7 +222,7 @@ int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
((upper_32_bits(addr) & 0xff) << 4) |
RS400_PTE_WRITEABLE | RS400_PTE_READABLE;
entry = cpu_to_le32(entry);
- rdev->gart.table.ram.ptr[i] = entry;
+ gtt[i] = entry;
return 0;
}
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 6e3b11e5abbe..481b99e89f65 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -287,6 +287,7 @@ void rs600_hpd_init(struct radeon_device *rdev)
default:
break;
}
+ radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
}
if (rdev->irq.installed)
rs600_irq_set(rdev);
@@ -413,7 +414,7 @@ int rs600_gart_init(struct radeon_device *rdev)
{
int r;
- if (rdev->gart.table.vram.robj) {
+ if (rdev->gart.robj) {
WARN(1, "RS600 GART already initialized\n");
return 0;
}
@@ -426,12 +427,12 @@ int rs600_gart_init(struct radeon_device *rdev)
return radeon_gart_table_vram_alloc(rdev);
}
-int rs600_gart_enable(struct radeon_device *rdev)
+static int rs600_gart_enable(struct radeon_device *rdev)
{
u32 tmp;
int r, i;
- if (rdev->gart.table.vram.robj == NULL) {
+ if (rdev->gart.robj == NULL) {
dev_err(rdev->dev, "No VRAM object for PCIE GART.\n");
return -EINVAL;
}
@@ -440,8 +441,8 @@ int rs600_gart_enable(struct radeon_device *rdev)
return r;
radeon_gart_restore(rdev);
/* Enable bus master */
- tmp = RREG32(R_00004C_BUS_CNTL) & C_00004C_BUS_MASTER_DIS;
- WREG32(R_00004C_BUS_CNTL, tmp);
+ tmp = RREG32(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS;
+ WREG32(RADEON_BUS_CNTL, tmp);
/* FIXME: setup default page */
WREG32_MC(R_000100_MC_PT0_CNTL,
(S_000100_EFFECTIVE_L2_CACHE_SIZE(6) |
@@ -484,6 +485,9 @@ int rs600_gart_enable(struct radeon_device *rdev)
tmp = RREG32_MC(R_000009_MC_CNTL1);
WREG32_MC(R_000009_MC_CNTL1, (tmp | S_000009_ENABLE_PAGE_TABLES(1)));
rs600_gart_tlb_flush(rdev);
+ DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
+ (unsigned)(rdev->mc.gtt_size >> 20),
+ (unsigned long long)rdev->gart.table_addr);
rdev->gart.ready = true;
return 0;
}
@@ -491,20 +495,12 @@ int rs600_gart_enable(struct radeon_device *rdev)
void rs600_gart_disable(struct radeon_device *rdev)
{
u32 tmp;
- int r;
/* FIXME: disable out of gart access */
WREG32_MC(R_000100_MC_PT0_CNTL, 0);
tmp = RREG32_MC(R_000009_MC_CNTL1);
WREG32_MC(R_000009_MC_CNTL1, tmp & C_000009_ENABLE_PAGE_TABLES);
- if (rdev->gart.table.vram.robj) {
- r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
- if (r == 0) {
- radeon_bo_kunmap(rdev->gart.table.vram.robj);
- radeon_bo_unpin(rdev->gart.table.vram.robj);
- radeon_bo_unreserve(rdev->gart.table.vram.robj);
- }
- }
+ radeon_gart_table_vram_unpin(rdev);
}
void rs600_gart_fini(struct radeon_device *rdev)
@@ -522,7 +518,7 @@ void rs600_gart_fini(struct radeon_device *rdev)
int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
{
- void __iomem *ptr = (void *)rdev->gart.table.vram.ptr;
+ void __iomem *ptr = (void *)rdev->gart.ptr;
if (i < 0 || i > rdev->gart.num_gpu_pages) {
return -EINVAL;
@@ -530,7 +526,7 @@ int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
addr = addr & 0xFFFFFFFFFFFFF000ULL;
addr |= R600_PTE_VALID | R600_PTE_SYSTEM | R600_PTE_SNOOPED;
addr |= R600_PTE_READABLE | R600_PTE_WRITEABLE;
- writeq(addr, ((void __iomem *)ptr) + (i * 8));
+ writeq(addr, ptr + (i * 8));
return 0;
}
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index 4de51891aa6d..a983f410ab89 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -124,7 +124,7 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev)
u32 tmp;
int r, i;
- if (rdev->gart.table.vram.robj == NULL) {
+ if (rdev->gart.robj == NULL) {
dev_err(rdev->dev, "No VRAM object for PCIE GART.\n");
return -EINVAL;
}
@@ -161,6 +161,9 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev)
WREG32(VM_CONTEXT0_CNTL + (i * 4), 0);
r600_pcie_gart_tlb_flush(rdev);
+ DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
+ (unsigned)(rdev->mc.gtt_size >> 20),
+ (unsigned long long)rdev->gart.table_addr);
rdev->gart.ready = true;
return 0;
}
@@ -168,7 +171,7 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev)
void rv770_pcie_gart_disable(struct radeon_device *rdev)
{
u32 tmp;
- int i, r;
+ int i;
/* Disable all tables */
for (i = 0; i < 7; i++)
@@ -188,14 +191,7 @@ void rv770_pcie_gart_disable(struct radeon_device *rdev)
WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp);
WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp);
WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp);
- if (rdev->gart.table.vram.robj) {
- r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
- if (likely(r == 0)) {
- radeon_bo_kunmap(rdev->gart.table.vram.robj);
- radeon_bo_unpin(rdev->gart.table.vram.robj);
- radeon_bo_unreserve(rdev->gart.table.vram.robj);
- }
- }
+ radeon_gart_table_vram_unpin(rdev);
}
void rv770_pcie_gart_fini(struct radeon_device *rdev)
@@ -279,7 +275,7 @@ static void rv770_mc_program(struct radeon_device *rdev)
WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR,
rdev->mc.vram_end >> 12);
}
- WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0);
+ WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, rdev->vram_scratch.gpu_addr >> 12);
tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16;
tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF);
WREG32(MC_VM_FB_LOCATION, tmp);
@@ -536,55 +532,6 @@ static u32 r700_get_tile_pipe_to_backend_map(struct radeon_device *rdev,
return backend_map;
}
-static void rv770_program_channel_remap(struct radeon_device *rdev)
-{
- u32 tcp_chan_steer, mc_shared_chremap, tmp;
- bool force_no_swizzle;
-
- switch (rdev->family) {
- case CHIP_RV770:
- case CHIP_RV730:
- force_no_swizzle = false;
- break;
- case CHIP_RV710:
- case CHIP_RV740:
- default:
- force_no_swizzle = true;
- break;
- }
-
- tmp = RREG32(MC_SHARED_CHMAP);
- switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) {
- case 0:
- case 1:
- default:
- /* default mapping */
- mc_shared_chremap = 0x00fac688;
- break;
- case 2:
- case 3:
- if (force_no_swizzle)
- mc_shared_chremap = 0x00fac688;
- else
- mc_shared_chremap = 0x00bbc298;
- break;
- }
-
- if (rdev->family == CHIP_RV740)
- tcp_chan_steer = 0x00ef2a60;
- else
- tcp_chan_steer = 0x00fac688;
-
- /* RV770 CE has special chremap setup */
- if (rdev->pdev->device == 0x944e) {
- tcp_chan_steer = 0x00b08b08;
- mc_shared_chremap = 0x00b08b08;
- }
-
- WREG32(TCP_CHAN_STEER, tcp_chan_steer);
- WREG32(MC_SHARED_CHREMAP, mc_shared_chremap);
-}
-
static void rv770_gpu_init(struct radeon_device *rdev)
{
int i, j, num_qd_pipes;
@@ -778,14 +725,13 @@ static void rv770_gpu_init(struct radeon_device *rdev)
(cc_rb_backend_disable >> 16));
rdev->config.rv770.tile_config = gb_tiling_config;
+ rdev->config.rv770.backend_map = backend_map;
gb_tiling_config |= BACKEND_MAP(backend_map);
WREG32(GB_TILING_CONFIG, gb_tiling_config);
WREG32(DCP_TILING_CONFIG, (gb_tiling_config & 0xffff));
WREG32(HDP_TILING_CONFIG, (gb_tiling_config & 0xffff));
- rv770_program_channel_remap(rdev);
-
WREG32(CC_RB_BACKEND_DISABLE, cc_rb_backend_disable);
WREG32(CC_GC_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config);
WREG32(GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config);
@@ -1006,54 +952,6 @@ static void rv770_gpu_init(struct radeon_device *rdev)
}
-static int rv770_vram_scratch_init(struct radeon_device *rdev)
-{
- int r;
- u64 gpu_addr;
-
- if (rdev->vram_scratch.robj == NULL) {
- r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE,
- PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM,
- &rdev->vram_scratch.robj);
- if (r) {
- return r;
- }
- }
-
- r = radeon_bo_reserve(rdev->vram_scratch.robj, false);
- if (unlikely(r != 0))
- return r;
- r = radeon_bo_pin(rdev->vram_scratch.robj,
- RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
- if (r) {
- radeon_bo_unreserve(rdev->vram_scratch.robj);
- return r;
- }
- r = radeon_bo_kmap(rdev->vram_scratch.robj,
- (void **)&rdev->vram_scratch.ptr);
- if (r)
- radeon_bo_unpin(rdev->vram_scratch.robj);
- radeon_bo_unreserve(rdev->vram_scratch.robj);
-
- return r;
-}
-
-static void rv770_vram_scratch_fini(struct radeon_device *rdev)
-{
- int r;
-
- if (rdev->vram_scratch.robj == NULL) {
- return;
- }
- r = radeon_bo_reserve(rdev->vram_scratch.robj, false);
- if (likely(r == 0)) {
- radeon_bo_kunmap(rdev->vram_scratch.robj);
- radeon_bo_unpin(rdev->vram_scratch.robj);
- radeon_bo_unreserve(rdev->vram_scratch.robj);
- }
- radeon_bo_unref(&rdev->vram_scratch.robj);
-}
-
void r700_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc)
{
u64 size_bf, size_af;
@@ -1153,6 +1051,10 @@ static int rv770_startup(struct radeon_device *rdev)
}
}
+ r = r600_vram_scratch_init(rdev);
+ if (r)
+ return r;
+
rv770_mc_program(rdev);
if (rdev->flags & RADEON_IS_AGP) {
rv770_agp_enable(rdev);
@@ -1161,9 +1063,7 @@ static int rv770_startup(struct radeon_device *rdev)
if (r)
return r;
}
- r = rv770_vram_scratch_init(rdev);
- if (r)
- return r;
+
rv770_gpu_init(rdev);
r = r600_blit_init(rdev);
if (r) {
@@ -1234,8 +1134,6 @@ int rv770_resume(struct radeon_device *rdev)
int rv770_suspend(struct radeon_device *rdev)
{
- int r;
-
r600_audio_fini(rdev);
/* FIXME: we should wait for ring to be empty */
r700_cp_stop(rdev);
@@ -1243,14 +1141,8 @@ int rv770_suspend(struct radeon_device *rdev)
r600_irq_suspend(rdev);
radeon_wb_disable(rdev);
rv770_pcie_gart_disable(rdev);
- /* unpin shaders bo */
- if (rdev->r600_blit.shader_obj) {
- r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
- if (likely(r == 0)) {
- radeon_bo_unpin(rdev->r600_blit.shader_obj);
- radeon_bo_unreserve(rdev->r600_blit.shader_obj);
- }
- }
+ r600_blit_suspend(rdev);
+
return 0;
}
@@ -1371,7 +1263,7 @@ void rv770_fini(struct radeon_device *rdev)
radeon_ib_pool_fini(rdev);
radeon_irq_kms_fini(rdev);
rv770_pcie_gart_fini(rdev);
- rv770_vram_scratch_fini(rdev);
+ r600_vram_scratch_fini(rdev);
radeon_gem_fini(rdev);
radeon_fence_driver_fini(rdev);
radeon_agp_fini(rdev);
diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c
index 6464490b240b..5468d1cd3296 100644
--- a/drivers/gpu/drm/savage/savage_drv.c
+++ b/drivers/gpu/drm/savage/savage_drv.c
@@ -23,6 +23,8 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "savage_drm.h"
#include "savage_drv.h"
diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c
index 46d5be6e97e5..a9c5716bea4e 100644
--- a/drivers/gpu/drm/sis/sis_drv.c
+++ b/drivers/gpu/drm/sis/sis_drv.c
@@ -25,6 +25,8 @@
*
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "sis_drm.h"
#include "sis_drv.h"
diff --git a/drivers/gpu/drm/sis/sis_drv.h b/drivers/gpu/drm/sis/sis_drv.h
index ef940bad63f7..194303c177ad 100644
--- a/drivers/gpu/drm/sis/sis_drv.h
+++ b/drivers/gpu/drm/sis/sis_drv.h
@@ -48,8 +48,8 @@ enum sis_family {
#define SIS_BASE (dev_priv->mmio)
-#define SIS_READ(reg) DRM_READ32(SIS_BASE, reg);
-#define SIS_WRITE(reg, val) DRM_WRITE32(SIS_BASE, reg, val);
+#define SIS_READ(reg) DRM_READ32(SIS_BASE, reg)
+#define SIS_WRITE(reg, val) DRM_WRITE32(SIS_BASE, reg, val)
typedef struct drm_sis_private {
drm_local_map_t *mmio;
diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c
index 8bf98810a8d6..cda29911e332 100644
--- a/drivers/gpu/drm/tdfx/tdfx_drv.c
+++ b/drivers/gpu/drm/tdfx/tdfx_drv.c
@@ -30,6 +30,8 @@
* Gareth Hughes <gareth@valinux.com>
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "tdfx_drv.h"
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 2e618b5ac465..0bb0f5f713e6 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -37,7 +37,7 @@
#include <linux/mm.h>
#include <linux/file.h>
#include <linux/module.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#define TTM_ASSERT_LOCKED(param)
#define TTM_DEBUG(fmt, arg...)
@@ -353,8 +353,10 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc)
ret = ttm_tt_set_user(bo->ttm, current,
bo->buffer_start, bo->num_pages);
- if (unlikely(ret != 0))
+ if (unlikely(ret != 0)) {
ttm_tt_destroy(bo->ttm);
+ bo->ttm = NULL;
+ }
break;
default:
printk(KERN_ERR TTM_PFX "Illegal buffer object type\n");
@@ -390,10 +392,13 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
* Create and bind a ttm if required.
*/
- if (!(new_man->flags & TTM_MEMTYPE_FLAG_FIXED) && (bo->ttm == NULL)) {
- ret = ttm_bo_add_ttm(bo, false);
- if (ret)
- goto out_err;
+ if (!(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) {
+ if (bo->ttm == NULL) {
+ bool zero = !(old_man->flags & TTM_MEMTYPE_FLAG_FIXED);
+ ret = ttm_bo_add_ttm(bo, zero);
+ if (ret)
+ goto out_err;
+ }
ret = ttm_tt_set_placement_caching(bo->ttm, mem->placement);
if (ret)
@@ -569,10 +574,16 @@ retry:
return ret;
spin_lock(&glob->lru_lock);
+
+ if (unlikely(list_empty(&bo->ddestroy))) {
+ spin_unlock(&glob->lru_lock);
+ return 0;
+ }
+
ret = ttm_bo_reserve_locked(bo, interruptible,
no_wait_reserve, false, 0);
- if (unlikely(ret != 0) || list_empty(&bo->ddestroy)) {
+ if (unlikely(ret != 0)) {
spin_unlock(&glob->lru_lock);
return ret;
}
@@ -1288,6 +1299,7 @@ int ttm_bo_create(struct ttm_bo_device *bdev,
return ret;
}
+EXPORT_SYMBOL(ttm_bo_create);
static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev,
unsigned mem_type, bool allow_errors)
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 77dbf408c0d0..082fcaea583f 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -321,7 +321,7 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
struct ttm_mem_type_manager *man = &bdev->man[new_mem->mem_type];
struct ttm_tt *ttm = bo->ttm;
struct ttm_mem_reg *old_mem = &bo->mem;
- struct ttm_mem_reg old_copy;
+ struct ttm_mem_reg old_copy = *old_mem;
void *old_iomap;
void *new_iomap;
int ret;
@@ -635,13 +635,13 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
if (ret)
return ret;
- ttm_bo_free_old_node(bo);
if ((man->flags & TTM_MEMTYPE_FLAG_FIXED) &&
(bo->ttm != NULL)) {
ttm_tt_unbind(bo->ttm);
ttm_tt_destroy(bo->ttm);
bo->ttm = NULL;
}
+ ttm_bo_free_old_node(bo);
} else {
/**
* This should help pipeline ordinary buffer moves.
diff --git a/drivers/gpu/drm/ttm/ttm_lock.c b/drivers/gpu/drm/ttm/ttm_lock.c
index de41e55a944a..075daf44bce4 100644
--- a/drivers/gpu/drm/ttm/ttm_lock.c
+++ b/drivers/gpu/drm/ttm/ttm_lock.c
@@ -30,7 +30,7 @@
#include "ttm/ttm_lock.h"
#include "ttm/ttm_module.h"
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/sched.h>
diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
index ebddd443d91a..93577f2e2954 100644
--- a/drivers/gpu/drm/ttm/ttm_object.c
+++ b/drivers/gpu/drm/ttm/ttm_object.c
@@ -55,7 +55,7 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
struct ttm_object_file {
struct ttm_object_device *tdev;
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index d948575717bf..727e93daac3b 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -40,7 +40,7 @@
#include <linux/slab.h>
#include <linux/dma-mapping.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_page_alloc.h"
@@ -355,7 +355,7 @@ restart:
if (nr_free)
goto restart;
- /* Not allowed to fall tough or break because
+ /* Not allowed to fall through or break because
* following context is inside spinlock while we are
* outside here.
*/
@@ -556,7 +556,7 @@ out:
}
/**
- * Fill the given pool if there isn't enough pages and requested number of
+ * Fill the given pool if there aren't enough pages and the requested number of
* pages is small.
*/
static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool,
@@ -576,8 +576,8 @@ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool,
pool->fill_lock = true;
- /* If allocation request is small and there is not enough
- * pages in pool we fill the pool first */
+ /* If allocation request is small and there are not enough
+ * pages in a pool we fill the pool up first. */
if (count < _manager->options.small
&& count > pool->npages) {
struct list_head new_pages;
@@ -614,9 +614,9 @@ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool,
}
/**
- * Cut count nubmer of pages from the pool and put them to return list
+ * Cut 'count' number of pages from the pool and put them on the return list.
*
- * @return count of pages still to allocate to fill the request.
+ * @return count of pages still required to fulfill the request.
*/
static unsigned ttm_page_pool_get_pages(struct ttm_page_pool *pool,
struct list_head *pages, int ttm_flags,
@@ -637,7 +637,7 @@ static unsigned ttm_page_pool_get_pages(struct ttm_page_pool *pool,
goto out;
}
/* find the last pages to include for requested number of pages. Split
- * pool to begin and halves to reduce search space. */
+ * pool to begin and halve it to reduce search space. */
if (count <= pool->npages/2) {
i = 0;
list_for_each(p, &pool->list) {
@@ -651,7 +651,7 @@ static unsigned ttm_page_pool_get_pages(struct ttm_page_pool *pool,
break;
}
}
- /* Cut count number of pages from pool */
+ /* Cut 'count' number of pages from the pool */
list_cut_position(pages, &pool->list, p);
pool->npages -= count;
count = 0;
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
index 58c271ebc0f7..f9cc548d6d98 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -35,6 +35,7 @@
#include <linux/file.h>
#include <linux/swap.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "drm_cache.h"
#include "drm_mem_util.h"
#include "ttm/ttm_module.h"
diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c
index 920a55214bcf..a83e86d3956c 100644
--- a/drivers/gpu/drm/via/via_drv.c
+++ b/drivers/gpu/drm/via/via_drv.c
@@ -22,6 +22,8 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include <linux/module.h>
+
#include "drmP.h"
#include "via_drm.h"
#include "via_drv.h"
diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig
index 30ad13344f7b..794ff67c5701 100644
--- a/drivers/gpu/drm/vmwgfx/Kconfig
+++ b/drivers/gpu/drm/vmwgfx/Kconfig
@@ -7,7 +7,8 @@ config DRM_VMWGFX
select FB_CFB_IMAGEBLIT
select DRM_TTM
help
- KMS enabled DRM driver for SVGA2 virtual hardware.
-
- If unsure say n. The compiled module will be
- called vmwgfx.ko
+ Choose this option if you would like to run 3D acceleration
+ in a VMware virtual machine.
+ This is a KMS enabled DRM driver for the VMware SVGA2
+ virtual hardware.
+ The compiled module will be called "vmwgfx.ko".
diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile
index c9281a1b1d3b..586869c8c11f 100644
--- a/drivers/gpu/drm/vmwgfx/Makefile
+++ b/drivers/gpu/drm/vmwgfx/Makefile
@@ -4,6 +4,7 @@ ccflags-y := -Iinclude/drm
vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
vmwgfx_fb.o vmwgfx_ioctl.o vmwgfx_resource.o vmwgfx_buffer.o \
vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \
- vmwgfx_overlay.o vmwgfx_fence.o vmwgfx_gmrid_manager.o
+ vmwgfx_overlay.o vmwgfx_marker.o vmwgfx_gmrid_manager.o \
+ vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o
obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
diff --git a/drivers/gpu/drm/vmwgfx/svga3d_reg.h b/drivers/gpu/drm/vmwgfx/svga3d_reg.h
index 77cb45331000..d0e085ee8249 100644
--- a/drivers/gpu/drm/vmwgfx/svga3d_reg.h
+++ b/drivers/gpu/drm/vmwgfx/svga3d_reg.h
@@ -57,7 +57,8 @@ typedef enum {
SVGA3D_HWVERSION_WS6_B1 = SVGA3D_MAKE_HWVERSION(1, 1),
SVGA3D_HWVERSION_FUSION_11 = SVGA3D_MAKE_HWVERSION(1, 4),
SVGA3D_HWVERSION_WS65_B1 = SVGA3D_MAKE_HWVERSION(2, 0),
- SVGA3D_HWVERSION_CURRENT = SVGA3D_HWVERSION_WS65_B1,
+ SVGA3D_HWVERSION_WS8_B1 = SVGA3D_MAKE_HWVERSION(2, 1),
+ SVGA3D_HWVERSION_CURRENT = SVGA3D_HWVERSION_WS8_B1,
} SVGA3dHardwareVersion;
/*
@@ -67,7 +68,8 @@ typedef enum {
typedef uint32 SVGA3dBool; /* 32-bit Bool definition */
#define SVGA3D_NUM_CLIPPLANES 6
#define SVGA3D_MAX_SIMULTANEOUS_RENDER_TARGETS 8
-
+#define SVGA3D_MAX_CONTEXT_IDS 256
+#define SVGA3D_MAX_SURFACE_IDS (32 * 1024)
/*
* Surface formats.
@@ -79,76 +81,91 @@ typedef uint32 SVGA3dBool; /* 32-bit Bool definition */
*/
typedef enum SVGA3dSurfaceFormat {
- SVGA3D_FORMAT_INVALID = 0,
+ SVGA3D_FORMAT_INVALID = 0,
- SVGA3D_X8R8G8B8 = 1,
- SVGA3D_A8R8G8B8 = 2,
+ SVGA3D_X8R8G8B8 = 1,
+ SVGA3D_A8R8G8B8 = 2,
- SVGA3D_R5G6B5 = 3,
- SVGA3D_X1R5G5B5 = 4,
- SVGA3D_A1R5G5B5 = 5,
- SVGA3D_A4R4G4B4 = 6,
+ SVGA3D_R5G6B5 = 3,
+ SVGA3D_X1R5G5B5 = 4,
+ SVGA3D_A1R5G5B5 = 5,
+ SVGA3D_A4R4G4B4 = 6,
- SVGA3D_Z_D32 = 7,
- SVGA3D_Z_D16 = 8,
- SVGA3D_Z_D24S8 = 9,
- SVGA3D_Z_D15S1 = 10,
+ SVGA3D_Z_D32 = 7,
+ SVGA3D_Z_D16 = 8,
+ SVGA3D_Z_D24S8 = 9,
+ SVGA3D_Z_D15S1 = 10,
- SVGA3D_LUMINANCE8 = 11,
- SVGA3D_LUMINANCE4_ALPHA4 = 12,
- SVGA3D_LUMINANCE16 = 13,
- SVGA3D_LUMINANCE8_ALPHA8 = 14,
+ SVGA3D_LUMINANCE8 = 11,
+ SVGA3D_LUMINANCE4_ALPHA4 = 12,
+ SVGA3D_LUMINANCE16 = 13,
+ SVGA3D_LUMINANCE8_ALPHA8 = 14,
- SVGA3D_DXT1 = 15,
- SVGA3D_DXT2 = 16,
- SVGA3D_DXT3 = 17,
- SVGA3D_DXT4 = 18,
- SVGA3D_DXT5 = 19,
+ SVGA3D_DXT1 = 15,
+ SVGA3D_DXT2 = 16,
+ SVGA3D_DXT3 = 17,
+ SVGA3D_DXT4 = 18,
+ SVGA3D_DXT5 = 19,
- SVGA3D_BUMPU8V8 = 20,
- SVGA3D_BUMPL6V5U5 = 21,
- SVGA3D_BUMPX8L8V8U8 = 22,
- SVGA3D_BUMPL8V8U8 = 23,
+ SVGA3D_BUMPU8V8 = 20,
+ SVGA3D_BUMPL6V5U5 = 21,
+ SVGA3D_BUMPX8L8V8U8 = 22,
+ SVGA3D_BUMPL8V8U8 = 23,
- SVGA3D_ARGB_S10E5 = 24, /* 16-bit floating-point ARGB */
- SVGA3D_ARGB_S23E8 = 25, /* 32-bit floating-point ARGB */
+ SVGA3D_ARGB_S10E5 = 24, /* 16-bit floating-point ARGB */
+ SVGA3D_ARGB_S23E8 = 25, /* 32-bit floating-point ARGB */
- SVGA3D_A2R10G10B10 = 26,
+ SVGA3D_A2R10G10B10 = 26,
/* signed formats */
- SVGA3D_V8U8 = 27,
- SVGA3D_Q8W8V8U8 = 28,
- SVGA3D_CxV8U8 = 29,
+ SVGA3D_V8U8 = 27,
+ SVGA3D_Q8W8V8U8 = 28,
+ SVGA3D_CxV8U8 = 29,
/* mixed formats */
- SVGA3D_X8L8V8U8 = 30,
- SVGA3D_A2W10V10U10 = 31,
+ SVGA3D_X8L8V8U8 = 30,
+ SVGA3D_A2W10V10U10 = 31,
- SVGA3D_ALPHA8 = 32,
+ SVGA3D_ALPHA8 = 32,
/* Single- and dual-component floating point formats */
- SVGA3D_R_S10E5 = 33,
- SVGA3D_R_S23E8 = 34,
- SVGA3D_RG_S10E5 = 35,
- SVGA3D_RG_S23E8 = 36,
+ SVGA3D_R_S10E5 = 33,
+ SVGA3D_R_S23E8 = 34,
+ SVGA3D_RG_S10E5 = 35,
+ SVGA3D_RG_S23E8 = 36,
/*
* Any surface can be used as a buffer object, but SVGA3D_BUFFER is
* the most efficient format to use when creating new surfaces
* expressly for index or vertex data.
*/
- SVGA3D_BUFFER = 37,
- SVGA3D_Z_D24X8 = 38,
+ SVGA3D_BUFFER = 37,
+
+ SVGA3D_Z_D24X8 = 38,
- SVGA3D_V16U16 = 39,
+ SVGA3D_V16U16 = 39,
- SVGA3D_G16R16 = 40,
- SVGA3D_A16B16G16R16 = 41,
+ SVGA3D_G16R16 = 40,
+ SVGA3D_A16B16G16R16 = 41,
/* Packed Video formats */
- SVGA3D_UYVY = 42,
- SVGA3D_YUY2 = 43,
+ SVGA3D_UYVY = 42,
+ SVGA3D_YUY2 = 43,
+
+ /* Planar video formats */
+ SVGA3D_NV12 = 44,
+
+ /* Video format with alpha */
+ SVGA3D_AYUV = 45,
+
+ SVGA3D_BC4_UNORM = 108,
+ SVGA3D_BC5_UNORM = 111,
+
+ /* Advanced D3D9 depth formats. */
+ SVGA3D_Z_DF16 = 118,
+ SVGA3D_Z_DF24 = 119,
+ SVGA3D_Z_D24S8_INT = 120,
SVGA3D_FORMAT_MAX
} SVGA3dSurfaceFormat;
@@ -414,10 +431,20 @@ typedef enum {
SVGA3D_RS_SRCBLENDALPHA = 94, /* SVGA3dBlendOp */
SVGA3D_RS_DSTBLENDALPHA = 95, /* SVGA3dBlendOp */
SVGA3D_RS_BLENDEQUATIONALPHA = 96, /* SVGA3dBlendEquation */
+ SVGA3D_RS_TRANSPARENCYANTIALIAS = 97, /* SVGA3dTransparencyAntialiasType */
+ SVGA3D_RS_LINEAA = 98, /* SVGA3dBool */
+ SVGA3D_RS_LINEWIDTH = 99, /* float */
SVGA3D_RS_MAX
} SVGA3dRenderStateName;
typedef enum {
+ SVGA3D_TRANSPARENCYANTIALIAS_NORMAL = 0,
+ SVGA3D_TRANSPARENCYANTIALIAS_ALPHATOCOVERAGE = 1,
+ SVGA3D_TRANSPARENCYANTIALIAS_SUPERSAMPLE = 2,
+ SVGA3D_TRANSPARENCYANTIALIAS_MAX
+} SVGA3dTransparencyAntialiasType;
+
+typedef enum {
SVGA3D_VERTEXMATERIAL_NONE = 0, /* Use the value in the current material */
SVGA3D_VERTEXMATERIAL_DIFFUSE = 1, /* Use the value in the diffuse component */
SVGA3D_VERTEXMATERIAL_SPECULAR = 2, /* Use the value in the specular component */
@@ -728,10 +755,10 @@ typedef enum {
SVGA3D_TEX_FILTER_NEAREST = 1,
SVGA3D_TEX_FILTER_LINEAR = 2,
SVGA3D_TEX_FILTER_ANISOTROPIC = 3,
- SVGA3D_TEX_FILTER_FLATCUBIC = 4, // Deprecated, not implemented
- SVGA3D_TEX_FILTER_GAUSSIANCUBIC = 5, // Deprecated, not implemented
- SVGA3D_TEX_FILTER_PYRAMIDALQUAD = 6, // Not currently implemented
- SVGA3D_TEX_FILTER_GAUSSIANQUAD = 7, // Not currently implemented
+ SVGA3D_TEX_FILTER_FLATCUBIC = 4, /* Deprecated, not implemented */
+ SVGA3D_TEX_FILTER_GAUSSIANCUBIC = 5, /* Deprecated, not implemented */
+ SVGA3D_TEX_FILTER_PYRAMIDALQUAD = 6, /* Not currently implemented */
+ SVGA3D_TEX_FILTER_GAUSSIANQUAD = 7, /* Not currently implemented */
SVGA3D_TEX_FILTER_MAX
} SVGA3dTextureFilter;
@@ -799,19 +826,19 @@ typedef enum {
typedef enum {
SVGA3D_DECLUSAGE_POSITION = 0,
- SVGA3D_DECLUSAGE_BLENDWEIGHT, // 1
- SVGA3D_DECLUSAGE_BLENDINDICES, // 2
- SVGA3D_DECLUSAGE_NORMAL, // 3
- SVGA3D_DECLUSAGE_PSIZE, // 4
- SVGA3D_DECLUSAGE_TEXCOORD, // 5
- SVGA3D_DECLUSAGE_TANGENT, // 6
- SVGA3D_DECLUSAGE_BINORMAL, // 7
- SVGA3D_DECLUSAGE_TESSFACTOR, // 8
- SVGA3D_DECLUSAGE_POSITIONT, // 9
- SVGA3D_DECLUSAGE_COLOR, // 10
- SVGA3D_DECLUSAGE_FOG, // 11
- SVGA3D_DECLUSAGE_DEPTH, // 12
- SVGA3D_DECLUSAGE_SAMPLE, // 13
+ SVGA3D_DECLUSAGE_BLENDWEIGHT, /* 1 */
+ SVGA3D_DECLUSAGE_BLENDINDICES, /* 2 */
+ SVGA3D_DECLUSAGE_NORMAL, /* 3 */
+ SVGA3D_DECLUSAGE_PSIZE, /* 4 */
+ SVGA3D_DECLUSAGE_TEXCOORD, /* 5 */
+ SVGA3D_DECLUSAGE_TANGENT, /* 6 */
+ SVGA3D_DECLUSAGE_BINORMAL, /* 7 */
+ SVGA3D_DECLUSAGE_TESSFACTOR, /* 8 */
+ SVGA3D_DECLUSAGE_POSITIONT, /* 9 */
+ SVGA3D_DECLUSAGE_COLOR, /* 10 */
+ SVGA3D_DECLUSAGE_FOG, /* 11 */
+ SVGA3D_DECLUSAGE_DEPTH, /* 12 */
+ SVGA3D_DECLUSAGE_SAMPLE, /* 13 */
SVGA3D_DECLUSAGE_MAX
} SVGA3dDeclUsage;
@@ -819,10 +846,10 @@ typedef enum {
SVGA3D_DECLMETHOD_DEFAULT = 0,
SVGA3D_DECLMETHOD_PARTIALU,
SVGA3D_DECLMETHOD_PARTIALV,
- SVGA3D_DECLMETHOD_CROSSUV, // Normal
+ SVGA3D_DECLMETHOD_CROSSUV, /* Normal */
SVGA3D_DECLMETHOD_UV,
- SVGA3D_DECLMETHOD_LOOKUP, // Lookup a displacement map
- SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, // Lookup a pre-sampled displacement map
+ SVGA3D_DECLMETHOD_LOOKUP, /* Lookup a displacement map */
+ SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, /* Lookup a pre-sampled displacement map */
} SVGA3dDeclMethod;
typedef enum {
@@ -930,7 +957,6 @@ typedef enum {
} SVGA3dCubeFace;
typedef enum {
- SVGA3D_SHADERTYPE_COMPILED_DX8 = 0,
SVGA3D_SHADERTYPE_VS = 1,
SVGA3D_SHADERTYPE_PS = 2,
SVGA3D_SHADERTYPE_MAX
@@ -968,12 +994,18 @@ typedef enum {
} SVGA3dTransferType;
/*
- * The maximum number vertex arrays we're guaranteed to support in
+ * The maximum number of vertex arrays we're guaranteed to support in
* SVGA_3D_CMD_DRAWPRIMITIVES.
*/
#define SVGA3D_MAX_VERTEX_ARRAYS 32
/*
+ * The maximum number of primitive ranges we're guaranteed to support
+ * in SVGA_3D_CMD_DRAWPRIMITIVES.
+ */
+#define SVGA3D_MAX_DRAW_PRIMITIVE_RANGES 32
+
+/*
* Identifiers for commands in the command FIFO.
*
* IDs between 1000 and 1039 (inclusive) were used by obsolete versions of
@@ -990,7 +1022,7 @@ typedef enum {
#define SVGA_3D_CMD_LEGACY_BASE 1000
#define SVGA_3D_CMD_BASE 1040
-#define SVGA_3D_CMD_SURFACE_DEFINE SVGA_3D_CMD_BASE + 0
+#define SVGA_3D_CMD_SURFACE_DEFINE SVGA_3D_CMD_BASE + 0 /* Deprecated */
#define SVGA_3D_CMD_SURFACE_DESTROY SVGA_3D_CMD_BASE + 1
#define SVGA_3D_CMD_SURFACE_COPY SVGA_3D_CMD_BASE + 2
#define SVGA_3D_CMD_SURFACE_STRETCHBLT SVGA_3D_CMD_BASE + 3
@@ -1008,7 +1040,7 @@ typedef enum {
#define SVGA_3D_CMD_SETVIEWPORT SVGA_3D_CMD_BASE + 15
#define SVGA_3D_CMD_SETCLIPPLANE SVGA_3D_CMD_BASE + 16
#define SVGA_3D_CMD_CLEAR SVGA_3D_CMD_BASE + 17
-#define SVGA_3D_CMD_PRESENT SVGA_3D_CMD_BASE + 18 // Deprecated
+#define SVGA_3D_CMD_PRESENT SVGA_3D_CMD_BASE + 18 /* Deprecated */
#define SVGA_3D_CMD_SHADER_DEFINE SVGA_3D_CMD_BASE + 19
#define SVGA_3D_CMD_SHADER_DESTROY SVGA_3D_CMD_BASE + 20
#define SVGA_3D_CMD_SET_SHADER SVGA_3D_CMD_BASE + 21
@@ -1018,9 +1050,13 @@ typedef enum {
#define SVGA_3D_CMD_BEGIN_QUERY SVGA_3D_CMD_BASE + 25
#define SVGA_3D_CMD_END_QUERY SVGA_3D_CMD_BASE + 26
#define SVGA_3D_CMD_WAIT_FOR_QUERY SVGA_3D_CMD_BASE + 27
-#define SVGA_3D_CMD_PRESENT_READBACK SVGA_3D_CMD_BASE + 28 // Deprecated
+#define SVGA_3D_CMD_PRESENT_READBACK SVGA_3D_CMD_BASE + 28 /* Deprecated */
#define SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN SVGA_3D_CMD_BASE + 29
-#define SVGA_3D_CMD_MAX SVGA_3D_CMD_BASE + 30
+#define SVGA_3D_CMD_SURFACE_DEFINE_V2 SVGA_3D_CMD_BASE + 30
+#define SVGA_3D_CMD_GENERATE_MIPMAPS SVGA_3D_CMD_BASE + 31
+#define SVGA_3D_CMD_ACTIVATE_SURFACE SVGA_3D_CMD_BASE + 40
+#define SVGA_3D_CMD_DEACTIVATE_SURFACE SVGA_3D_CMD_BASE + 41
+#define SVGA_3D_CMD_MAX SVGA_3D_CMD_BASE + 42
#define SVGA_3D_CMD_FUTURE_MAX 2000
@@ -1031,9 +1067,9 @@ typedef enum {
typedef struct {
union {
struct {
- uint16 function; // SVGA3dFogFunction
- uint8 type; // SVGA3dFogType
- uint8 base; // SVGA3dFogBase
+ uint16 function; /* SVGA3dFogFunction */
+ uint8 type; /* SVGA3dFogType */
+ uint8 base; /* SVGA3dFogBase */
};
uint32 uintValue;
};
@@ -1109,6 +1145,8 @@ typedef enum {
SVGA3D_SURFACE_HINT_RENDERTARGET = (1 << 6),
SVGA3D_SURFACE_HINT_DEPTHSTENCIL = (1 << 7),
SVGA3D_SURFACE_HINT_WRITEONLY = (1 << 8),
+ SVGA3D_SURFACE_MASKABLE_ANTIALIAS = (1 << 9),
+ SVGA3D_SURFACE_AUTOGENMIPMAPS = (1 << 10),
} SVGA3dSurfaceFlags;
typedef
@@ -1121,6 +1159,12 @@ struct {
uint32 sid;
SVGA3dSurfaceFlags surfaceFlags;
SVGA3dSurfaceFormat format;
+ /*
+ * If surfaceFlags has SVGA3D_SURFACE_CUBEMAP bit set, all SVGA3dSurfaceFace
+ * structures must have the same value of numMipLevels field.
+ * Otherwise, all but the first SVGA3dSurfaceFace structures must have the
+ * numMipLevels set to 0.
+ */
SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES];
/*
* Followed by an SVGA3dSize structure for each mip level in each face.
@@ -1135,6 +1179,31 @@ struct {
typedef
struct {
+ uint32 sid;
+ SVGA3dSurfaceFlags surfaceFlags;
+ SVGA3dSurfaceFormat format;
+ /*
+ * If surfaceFlags has SVGA3D_SURFACE_CUBEMAP bit set, all SVGA3dSurfaceFace
+ * structures must have the same value of numMipLevels field.
+ * Otherwise, all but the first SVGA3dSurfaceFace structures must have the
+ * numMipLevels set to 0.
+ */
+ SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES];
+ uint32 multisampleCount;
+ SVGA3dTextureFilter autogenFilter;
+ /*
+ * Followed by an SVGA3dSize structure for each mip level in each face.
+ *
+ * A note on surface sizes: Sizes are always specified in pixels,
+ * even if the true surface size is not a multiple of the minimum
+ * block size of the surface's format. For example, a 3x3x1 DXT1
+ * compressed texture would actually be stored as a 4x4x1 image in
+ * memory.
+ */
+} SVGA3dCmdDefineSurface_v2; /* SVGA_3D_CMD_SURFACE_DEFINE_V2 */
+
+typedef
+struct {
uint32 sid;
} SVGA3dCmdDestroySurface; /* SVGA_3D_CMD_SURFACE_DESTROY */
@@ -1474,10 +1543,12 @@ struct {
* SVGA3dCmdDrawPrimitives structure. In order,
* they are:
*
- * 1. SVGA3dVertexDecl, quantity 'numVertexDecls'
- * 2. SVGA3dPrimitiveRange, quantity 'numRanges'
+ * 1. SVGA3dVertexDecl, quantity 'numVertexDecls', but no more than
+ * SVGA3D_MAX_VERTEX_ARRAYS;
+ * 2. SVGA3dPrimitiveRange, quantity 'numRanges', but no more than
+ * SVGA3D_MAX_DRAW_PRIMITIVE_RANGES;
* 3. Optionally, SVGA3dVertexDivisor, quantity 'numVertexDecls' (contains
- * the frequency divisor for this the corresponding vertex decl)
+ * the frequency divisor for the corresponding vertex decl).
*/
} SVGA3dCmdDrawPrimitives; /* SVGA_3D_CMD_DRAWPRIMITIVES */
@@ -1671,6 +1742,12 @@ struct {
/* Clipping: zero or more SVGASignedRects follow */
} SVGA3dCmdBlitSurfaceToScreen; /* SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN */
+typedef
+struct {
+ uint32 sid;
+ SVGA3dTextureFilter filter;
+} SVGA3dCmdGenerateMipmaps; /* SVGA_3D_CMD_GENERATE_MIPMAPS */
+
/*
* Capability query index.
@@ -1774,6 +1851,32 @@ typedef enum {
SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16 = 67,
SVGA3D_DEVCAP_SURFACEFMT_UYVY = 68,
SVGA3D_DEVCAP_SURFACEFMT_YUY2 = 69,
+ SVGA3D_DEVCAP_MULTISAMPLE_NONMASKABLESAMPLES = 70,
+ SVGA3D_DEVCAP_MULTISAMPLE_MASKABLESAMPLES = 71,
+ SVGA3D_DEVCAP_ALPHATOCOVERAGE = 72,
+ SVGA3D_DEVCAP_SUPERSAMPLE = 73,
+ SVGA3D_DEVCAP_AUTOGENMIPMAPS = 74,
+ SVGA3D_DEVCAP_SURFACEFMT_NV12 = 75,
+ SVGA3D_DEVCAP_SURFACEFMT_AYUV = 76,
+
+ /*
+ * This is the maximum number of SVGA context IDs that the guest
+ * can define using SVGA_3D_CMD_CONTEXT_DEFINE.
+ */
+ SVGA3D_DEVCAP_MAX_CONTEXT_IDS = 77,
+
+ /*
+ * This is the maximum number of SVGA surface IDs that the guest
+ * can define using SVGA_3D_CMD_SURFACE_DEFINE*.
+ */
+ SVGA3D_DEVCAP_MAX_SURFACE_IDS = 78,
+
+ SVGA3D_DEVCAP_SURFACEFMT_Z_DF16 = 79,
+ SVGA3D_DEVCAP_SURFACEFMT_Z_DF24 = 80,
+ SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8_INT = 81,
+
+ SVGA3D_DEVCAP_SURFACEFMT_BC4_UNORM = 82,
+ SVGA3D_DEVCAP_SURFACEFMT_BC5_UNORM = 83,
/*
* Don't add new caps into the previous section; the values in this
diff --git a/drivers/gpu/drm/vmwgfx/svga_escape.h b/drivers/gpu/drm/vmwgfx/svga_escape.h
index 7b85e9b8c854..8e8d9682e018 100644
--- a/drivers/gpu/drm/vmwgfx/svga_escape.h
+++ b/drivers/gpu/drm/vmwgfx/svga_escape.h
@@ -75,7 +75,7 @@
*/
#define SVGA_ESCAPE_VMWARE_HINT 0x00030000
-#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN 0x00030001 // Deprecated
+#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN 0x00030001 /* Deprecated */
typedef
struct {
diff --git a/drivers/gpu/drm/vmwgfx/svga_overlay.h b/drivers/gpu/drm/vmwgfx/svga_overlay.h
index f753d73c14b4..f38416fcb046 100644
--- a/drivers/gpu/drm/vmwgfx/svga_overlay.h
+++ b/drivers/gpu/drm/vmwgfx/svga_overlay.h
@@ -38,9 +38,9 @@
* Video formats we support
*/
-#define VMWARE_FOURCC_YV12 0x32315659 // 'Y' 'V' '1' '2'
-#define VMWARE_FOURCC_YUY2 0x32595559 // 'Y' 'U' 'Y' '2'
-#define VMWARE_FOURCC_UYVY 0x59565955 // 'U' 'Y' 'V' 'Y'
+#define VMWARE_FOURCC_YV12 0x32315659 /* 'Y' 'V' '1' '2' */
+#define VMWARE_FOURCC_YUY2 0x32595559 /* 'Y' 'U' 'Y' '2' */
+#define VMWARE_FOURCC_UYVY 0x59565955 /* 'U' 'Y' 'V' 'Y' */
typedef enum {
SVGA_OVERLAY_FORMAT_INVALID = 0,
@@ -68,7 +68,7 @@ struct SVGAEscapeVideoSetRegs {
uint32 streamId;
} header;
- // May include zero or more items.
+ /* May include zero or more items. */
struct {
uint32 registerId;
uint32 value;
@@ -134,12 +134,12 @@ struct {
*/
static inline bool
-VMwareVideoGetAttributes(const SVGAOverlayFormat format, // IN
- uint32 *width, // IN / OUT
- uint32 *height, // IN / OUT
- uint32 *size, // OUT
- uint32 *pitches, // OUT (optional)
- uint32 *offsets) // OUT (optional)
+VMwareVideoGetAttributes(const SVGAOverlayFormat format, /* IN */
+ uint32 *width, /* IN / OUT */
+ uint32 *height, /* IN / OUT */
+ uint32 *size, /* OUT */
+ uint32 *pitches, /* OUT (optional) */
+ uint32 *offsets) /* OUT (optional) */
{
int tmp;
@@ -198,4 +198,4 @@ VMwareVideoGetAttributes(const SVGAOverlayFormat format, // IN
return true;
}
-#endif // _SVGA_OVERLAY_H_
+#endif /* _SVGA_OVERLAY_H_ */
diff --git a/drivers/gpu/drm/vmwgfx/svga_reg.h b/drivers/gpu/drm/vmwgfx/svga_reg.h
index 1b96c2ec07dd..01f63cb49678 100644
--- a/drivers/gpu/drm/vmwgfx/svga_reg.h
+++ b/drivers/gpu/drm/vmwgfx/svga_reg.h
@@ -39,6 +39,15 @@
#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405
/*
+ * SVGA_REG_ENABLE bit definitions.
+ */
+#define SVGA_REG_ENABLE_DISABLE 0
+#define SVGA_REG_ENABLE_ENABLE 1
+#define SVGA_REG_ENABLE_HIDE 2
+#define SVGA_REG_ENABLE_ENABLE_HIDE (SVGA_REG_ENABLE_ENABLE |\
+ SVGA_REG_ENABLE_HIDE)
+
+/*
* Legal values for the SVGA_REG_CURSOR_ON register in old-fashioned
* cursor bypass mode. This is still supported, but no new guest
* drivers should use it.
@@ -158,7 +167,9 @@ enum {
SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH = 44,
SVGA_REG_TRACES = 45, /* Enable trace-based updates even when FIFO is on */
- SVGA_REG_TOP = 46, /* Must be 1 more than the last register */
+ SVGA_REG_GMRS_MAX_PAGES = 46, /* Maximum number of 4KB pages for all GMRs */
+ SVGA_REG_MEMORY_SIZE = 47, /* Total dedicated device memory excluding FIFO */
+ SVGA_REG_TOP = 48, /* Must be 1 more than the last register */
SVGA_PALETTE_BASE = 1024, /* Base of SVGA color map */
/* Next 768 (== 256*3) registers exist for colormap */
@@ -265,7 +276,7 @@ enum {
* possible.
*/
#define SVGA_GMR_NULL ((uint32) -1)
-#define SVGA_GMR_FRAMEBUFFER ((uint32) -2) // Guest Framebuffer (GFB)
+#define SVGA_GMR_FRAMEBUFFER ((uint32) -2) /* Guest Framebuffer (GFB) */
typedef
struct SVGAGuestMemDescriptor {
@@ -306,13 +317,35 @@ struct SVGAGMRImageFormat {
struct {
uint32 bitsPerPixel : 8;
uint32 colorDepth : 8;
- uint32 reserved : 16; // Must be zero
+ uint32 reserved : 16; /* Must be zero */
};
uint32 value;
};
} SVGAGMRImageFormat;
+typedef
+struct SVGAGuestImage {
+ SVGAGuestPtr ptr;
+
+ /*
+ * A note on interpretation of pitch: This value of pitch is the
+ * number of bytes between vertically adjacent image
+ * blocks. Normally this is the number of bytes between the first
+ * pixel of two adjacent scanlines. With compressed textures,
+ * however, this may represent the number of bytes between
+ * compression blocks rather than between rows of pixels.
+ *
+ * XXX: Compressed textures currently must be tightly packed in guest memory.
+ *
+ * If the image is 1-dimensional, pitch is ignored.
+ *
+ * If 'pitch' is zero, the SVGA3D device calculates a pitch value
+ * assuming each row of blocks is tightly packed.
+ */
+ uint32 pitch;
+} SVGAGuestImage;
+
/*
* SVGAColorBGRX --
*
@@ -328,7 +361,7 @@ struct SVGAColorBGRX {
uint32 b : 8;
uint32 g : 8;
uint32 r : 8;
- uint32 x : 8; // Unused
+ uint32 x : 8; /* Unused */
};
uint32 value;
@@ -370,23 +403,34 @@ struct SVGASignedPoint {
* Note the holes in the bitfield. Missing bits have been deprecated,
* and must not be reused. Those capabilities will never be reported
* by new versions of the SVGA device.
+ *
+ * SVGA_CAP_GMR2 --
+ * Provides asynchronous commands to define and remap guest memory
+ * regions. Adds device registers SVGA_REG_GMRS_MAX_PAGES and
+ * SVGA_REG_MEMORY_SIZE.
+ *
+ * SVGA_CAP_SCREEN_OBJECT_2 --
+ * Allow screen object support, and require backing stores from the
+ * guest for each screen object.
*/
#define SVGA_CAP_NONE 0x00000000
#define SVGA_CAP_RECT_COPY 0x00000002
#define SVGA_CAP_CURSOR 0x00000020
-#define SVGA_CAP_CURSOR_BYPASS 0x00000040 // Legacy (Use Cursor Bypass 3 instead)
-#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080 // Legacy (Use Cursor Bypass 3 instead)
+#define SVGA_CAP_CURSOR_BYPASS 0x00000040 /* Legacy (Use Cursor Bypass 3 instead) */
+#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080 /* Legacy (Use Cursor Bypass 3 instead) */
#define SVGA_CAP_8BIT_EMULATION 0x00000100
#define SVGA_CAP_ALPHA_CURSOR 0x00000200
#define SVGA_CAP_3D 0x00004000
#define SVGA_CAP_EXTENDED_FIFO 0x00008000
-#define SVGA_CAP_MULTIMON 0x00010000 // Legacy multi-monitor support
+#define SVGA_CAP_MULTIMON 0x00010000 /* Legacy multi-monitor support */
#define SVGA_CAP_PITCHLOCK 0x00020000
#define SVGA_CAP_IRQMASK 0x00040000
-#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000 // Legacy multi-monitor support
+#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000 /* Legacy multi-monitor support */
#define SVGA_CAP_GMR 0x00100000
#define SVGA_CAP_TRACES 0x00200000
+#define SVGA_CAP_GMR2 0x00400000
+#define SVGA_CAP_SCREEN_OBJECT_2 0x00800000
/*
@@ -431,7 +475,7 @@ enum {
SVGA_FIFO_CAPABILITIES = 4,
SVGA_FIFO_FLAGS,
- // Valid with SVGA_FIFO_CAP_FENCE:
+ /* Valid with SVGA_FIFO_CAP_FENCE: */
SVGA_FIFO_FENCE,
/*
@@ -444,33 +488,47 @@ enum {
* extended FIFO.
*/
- // Valid if exists (i.e. if extended FIFO enabled):
+ /* Valid if exists (i.e. if extended FIFO enabled): */
SVGA_FIFO_3D_HWVERSION, /* See SVGA3dHardwareVersion in svga3d_reg.h */
- // Valid with SVGA_FIFO_CAP_PITCHLOCK:
+ /* Valid with SVGA_FIFO_CAP_PITCHLOCK: */
SVGA_FIFO_PITCHLOCK,
- // Valid with SVGA_FIFO_CAP_CURSOR_BYPASS_3:
+ /* Valid with SVGA_FIFO_CAP_CURSOR_BYPASS_3: */
SVGA_FIFO_CURSOR_ON, /* Cursor bypass 3 show/hide register */
SVGA_FIFO_CURSOR_X, /* Cursor bypass 3 x register */
SVGA_FIFO_CURSOR_Y, /* Cursor bypass 3 y register */
SVGA_FIFO_CURSOR_COUNT, /* Incremented when any of the other 3 change */
SVGA_FIFO_CURSOR_LAST_UPDATED,/* Last time the host updated the cursor */
- // Valid with SVGA_FIFO_CAP_RESERVE:
+ /* Valid with SVGA_FIFO_CAP_RESERVE: */
SVGA_FIFO_RESERVED, /* Bytes past NEXT_CMD with real contents */
/*
- * Valid with SVGA_FIFO_CAP_SCREEN_OBJECT:
+ * Valid with SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2:
*
* By default this is SVGA_ID_INVALID, to indicate that the cursor
* coordinates are specified relative to the virtual root. If this
* is set to a specific screen ID, cursor position is reinterpreted
- * as a signed offset relative to that screen's origin. This is the
- * only way to place the cursor on a non-rooted screen.
+ * as a signed offset relative to that screen's origin.
*/
SVGA_FIFO_CURSOR_SCREEN_ID,
/*
+ * Valid with SVGA_FIFO_CAP_DEAD
+ *
+ * An arbitrary value written by the host, drivers should not use it.
+ */
+ SVGA_FIFO_DEAD,
+
+ /*
+ * Valid with SVGA_FIFO_CAP_3D_HWVERSION_REVISED:
+ *
+ * Contains 3D HWVERSION (see SVGA3dHardwareVersion in svga3d_reg.h)
+ * on platforms that can enforce graphics resource limits.
+ */
+ SVGA_FIFO_3D_HWVERSION_REVISED,
+
+ /*
* XXX: The gap here, up until SVGA_FIFO_3D_CAPS, can be used for new
* registers, but this must be done carefully and with judicious use of
* capability bits, since comparisons based on SVGA_FIFO_MIN aren't
@@ -508,7 +566,7 @@ enum {
* sets SVGA_FIFO_MIN high enough to leave room for them.
*/
- // Valid if register exists:
+ /* Valid if register exists: */
SVGA_FIFO_GUEST_3D_HWVERSION, /* Guest driver's 3D version */
SVGA_FIFO_FENCE_GOAL, /* Matching target for SVGA_IRQFLAG_FENCE_GOAL */
SVGA_FIFO_BUSY, /* See "FIFO Synchronization Registers" */
@@ -709,6 +767,37 @@ enum {
*
* - When a screen is resized, either using Screen Object commands or
* legacy multimon registers, its contents are preserved.
+ *
+ * SVGA_FIFO_CAP_GMR2 --
+ *
+ * Provides new commands to define and remap guest memory regions (GMR).
+ *
+ * New 2D commands:
+ * DEFINE_GMR2, REMAP_GMR2.
+ *
+ * SVGA_FIFO_CAP_3D_HWVERSION_REVISED --
+ *
+ * Indicates new register SVGA_FIFO_3D_HWVERSION_REVISED exists.
+ * This register may replace SVGA_FIFO_3D_HWVERSION on platforms
+ * that enforce graphics resource limits. This allows the platform
+ * to clear SVGA_FIFO_3D_HWVERSION and disable 3D in legacy guest
+ * drivers that do not limit their resources.
+ *
+ * Note this is an alias to SVGA_FIFO_CAP_GMR2 because these indicators
+ * are codependent (and thus we use a single capability bit).
+ *
+ * SVGA_FIFO_CAP_SCREEN_OBJECT_2 --
+ *
+ * Modifies the DEFINE_SCREEN command to include a guest provided
+ * backing store in GMR memory and the bytesPerLine for the backing
+ * store. This capability requires the use of a backing store when
+ * creating screen objects. However if SVGA_FIFO_CAP_SCREEN_OBJECT
+ * is present then backing stores are optional.
+ *
+ * SVGA_FIFO_CAP_DEAD --
+ *
+ * Drivers should not use this cap bit. This cap bit can not be
+ * reused since some hosts already expose it.
*/
#define SVGA_FIFO_CAP_NONE 0
@@ -720,6 +809,10 @@ enum {
#define SVGA_FIFO_CAP_ESCAPE (1<<5)
#define SVGA_FIFO_CAP_RESERVE (1<<6)
#define SVGA_FIFO_CAP_SCREEN_OBJECT (1<<7)
+#define SVGA_FIFO_CAP_GMR2 (1<<8)
+#define SVGA_FIFO_CAP_3D_HWVERSION_REVISED SVGA_FIFO_CAP_GMR2
+#define SVGA_FIFO_CAP_SCREEN_OBJECT_2 (1<<9)
+#define SVGA_FIFO_CAP_DEAD (1<<10)
/*
@@ -730,7 +823,7 @@ enum {
#define SVGA_FIFO_FLAG_NONE 0
#define SVGA_FIFO_FLAG_ACCELFRONT (1<<0)
-#define SVGA_FIFO_FLAG_RESERVED (1<<31) // Internal use only
+#define SVGA_FIFO_FLAG_RESERVED (1<<31) /* Internal use only */
/*
* FIFO reservation sentinel value
@@ -763,22 +856,22 @@ enum {
SVGA_VIDEO_DATA_OFFSET,
SVGA_VIDEO_FORMAT,
SVGA_VIDEO_COLORKEY,
- SVGA_VIDEO_SIZE, // Deprecated
+ SVGA_VIDEO_SIZE, /* Deprecated */
SVGA_VIDEO_WIDTH,
SVGA_VIDEO_HEIGHT,
SVGA_VIDEO_SRC_X,
SVGA_VIDEO_SRC_Y,
SVGA_VIDEO_SRC_WIDTH,
SVGA_VIDEO_SRC_HEIGHT,
- SVGA_VIDEO_DST_X, // Signed int32
- SVGA_VIDEO_DST_Y, // Signed int32
+ SVGA_VIDEO_DST_X, /* Signed int32 */
+ SVGA_VIDEO_DST_Y, /* Signed int32 */
SVGA_VIDEO_DST_WIDTH,
SVGA_VIDEO_DST_HEIGHT,
SVGA_VIDEO_PITCH_1,
SVGA_VIDEO_PITCH_2,
SVGA_VIDEO_PITCH_3,
- SVGA_VIDEO_DATA_GMRID, // Optional, defaults to SVGA_GMR_FRAMEBUFFER
- SVGA_VIDEO_DST_SCREEN_ID, // Optional, defaults to virtual coords (SVGA_ID_INVALID)
+ SVGA_VIDEO_DATA_GMRID, /* Optional, defaults to SVGA_GMR_FRAMEBUFFER */
+ SVGA_VIDEO_DST_SCREEN_ID, /* Optional, defaults to virtual coords (SVGA_ID_INVALID) */
SVGA_VIDEO_NUM_REGS
};
@@ -829,15 +922,51 @@ typedef struct SVGAOverlayUnit {
* compatibility. New flags can be added, and the struct may grow,
* but existing fields must retain their meaning.
*
+ * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2 are required fields of
+ * a SVGAGuestPtr that is used to back the screen contents. This
+ * memory must come from the GFB. The guest is not allowed to
+ * access the memory and doing so will have undefined results. The
+ * backing store is required to be page aligned and the size is
+ * padded to the next page boundry. The number of pages is:
+ * (bytesPerLine * size.width * 4 + PAGE_SIZE - 1) / PAGE_SIZE
+ *
+ * The pitch in the backingStore is required to be at least large
+ * enough to hold a 32bbp scanline. It is recommended that the
+ * driver pad bytesPerLine for a potential performance win.
+ *
+ * The cloneCount field is treated as a hint from the guest that
+ * the user wants this display to be cloned, countCount times. A
+ * value of zero means no cloning should happen.
+ */
+
+#define SVGA_SCREEN_MUST_BE_SET (1 << 0) /* Must be set or results undefined */
+#define SVGA_SCREEN_HAS_ROOT SVGA_SCREEN_MUST_BE_SET /* Deprecated */
+#define SVGA_SCREEN_IS_PRIMARY (1 << 1) /* Guest considers this screen to be 'primary' */
+#define SVGA_SCREEN_FULLSCREEN_HINT (1 << 2) /* Guest is running a fullscreen app here */
+
+/*
+ * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2. When the screen is
+ * deactivated the base layer is defined to lose all contents and
+ * become black. When a screen is deactivated the backing store is
+ * optional. When set backingPtr and bytesPerLine will be ignored.
*/
+#define SVGA_SCREEN_DEACTIVATE (1 << 3)
-#define SVGA_SCREEN_HAS_ROOT (1 << 0) // Screen is present in the virtual coord space
-#define SVGA_SCREEN_IS_PRIMARY (1 << 1) // Guest considers this screen to be 'primary'
-#define SVGA_SCREEN_FULLSCREEN_HINT (1 << 2) // Guest is running a fullscreen app here
+/*
+ * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2. When this flag is set
+ * the screen contents will be outputted as all black to the user
+ * though the base layer contents is preserved. The screen base layer
+ * can still be read and written to like normal though the no visible
+ * effect will be seen by the user. When the flag is changed the
+ * screen will be blanked or redrawn to the current contents as needed
+ * without any extra commands from the driver. This flag only has an
+ * effect when the screen is not deactivated.
+ */
+#define SVGA_SCREEN_BLANKING (1 << 4)
typedef
struct SVGAScreenObject {
- uint32 structSize; // sizeof(SVGAScreenObject)
+ uint32 structSize; /* sizeof(SVGAScreenObject) */
uint32 id;
uint32 flags;
struct {
@@ -847,7 +976,14 @@ struct SVGAScreenObject {
struct {
int32 x;
int32 y;
- } root; // Only used if SVGA_SCREEN_HAS_ROOT is set.
+ } root;
+
+ /*
+ * Added and required by SVGA_FIFO_CAP_SCREEN_OBJECT_2, optional
+ * with SVGA_FIFO_CAP_SCREEN_OBJECT.
+ */
+ SVGAGuestImage backingStore;
+ uint32 cloneCount;
} SVGAScreenObject;
@@ -885,6 +1021,8 @@ typedef enum {
SVGA_CMD_BLIT_SCREEN_TO_GMRFB = 38,
SVGA_CMD_ANNOTATION_FILL = 39,
SVGA_CMD_ANNOTATION_COPY = 40,
+ SVGA_CMD_DEFINE_GMR2 = 41,
+ SVGA_CMD_REMAP_GMR2 = 42,
SVGA_CMD_MAX
} SVGAFifoCmdId;
@@ -920,7 +1058,7 @@ typedef enum {
*/
typedef
-struct {
+struct SVGAFifoCmdUpdate {
uint32 x;
uint32 y;
uint32 width;
@@ -939,7 +1077,7 @@ struct {
*/
typedef
-struct {
+struct SVGAFifoCmdRectCopy {
uint32 srcX;
uint32 srcY;
uint32 destX;
@@ -963,14 +1101,14 @@ struct {
*/
typedef
-struct {
- uint32 id; // Reserved, must be zero.
+struct SVGAFifoCmdDefineCursor {
+ uint32 id; /* Reserved, must be zero. */
uint32 hotspotX;
uint32 hotspotY;
uint32 width;
uint32 height;
- uint32 andMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL
- uint32 xorMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL
+ uint32 andMaskDepth; /* Value must be 1 or equal to BITS_PER_PIXEL */
+ uint32 xorMaskDepth; /* Value must be 1 or equal to BITS_PER_PIXEL */
/*
* Followed by scanline data for AND mask, then XOR mask.
* Each scanline is padded to a 32-bit boundary.
@@ -992,8 +1130,8 @@ struct {
*/
typedef
-struct {
- uint32 id; // Reserved, must be zero.
+struct SVGAFifoCmdDefineAlphaCursor {
+ uint32 id; /* Reserved, must be zero. */
uint32 hotspotX;
uint32 hotspotY;
uint32 width;
@@ -1015,7 +1153,7 @@ struct {
*/
typedef
-struct {
+struct SVGAFifoCmdUpdateVerbose {
uint32 x;
uint32 y;
uint32 width;
@@ -1040,13 +1178,13 @@ struct {
#define SVGA_ROP_COPY 0x03
typedef
-struct {
- uint32 color; // In the same format as the GFB
+struct SVGAFifoCmdFrontRopFill {
+ uint32 color; /* In the same format as the GFB */
uint32 x;
uint32 y;
uint32 width;
uint32 height;
- uint32 rop; // Must be SVGA_ROP_COPY
+ uint32 rop; /* Must be SVGA_ROP_COPY */
} SVGAFifoCmdFrontRopFill;
@@ -1083,7 +1221,7 @@ struct {
*/
typedef
-struct {
+struct SVGAFifoCmdEscape {
uint32 nsid;
uint32 size;
/* followed by 'size' bytes of data */
@@ -1113,12 +1251,12 @@ struct {
* registers (SVGA_REG_NUM_GUEST_DISPLAYS, SVGA_REG_DISPLAY_*).
*
* Availability:
- * SVGA_FIFO_CAP_SCREEN_OBJECT
+ * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2
*/
typedef
struct {
- SVGAScreenObject screen; // Variable-length according to version
+ SVGAScreenObject screen; /* Variable-length according to version */
} SVGAFifoCmdDefineScreen;
@@ -1129,7 +1267,7 @@ struct {
* re-use.
*
* Availability:
- * SVGA_FIFO_CAP_SCREEN_OBJECT
+ * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2
*/
typedef
@@ -1182,7 +1320,7 @@ struct {
* GMRFB.
*
* Availability:
- * SVGA_FIFO_CAP_SCREEN_OBJECT
+ * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2
*/
typedef
@@ -1219,7 +1357,7 @@ struct {
* SVGA_CMD_ANNOTATION_* commands for details.
*
* Availability:
- * SVGA_FIFO_CAP_SCREEN_OBJECT
+ * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2
*/
typedef
@@ -1267,7 +1405,7 @@ struct {
* the time any subsequent FENCE commands are reached.
*
* Availability:
- * SVGA_FIFO_CAP_SCREEN_OBJECT
+ * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2
*/
typedef
@@ -1302,7 +1440,7 @@ struct {
* user's display is being remoted over a network connection.
*
* Availability:
- * SVGA_FIFO_CAP_SCREEN_OBJECT
+ * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2
*/
typedef
@@ -1334,7 +1472,7 @@ struct {
* undefined.
*
* Availability:
- * SVGA_FIFO_CAP_SCREEN_OBJECT
+ * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2
*/
typedef
@@ -1343,4 +1481,72 @@ struct {
uint32 srcScreenId;
} SVGAFifoCmdAnnotationCopy;
+
+/*
+ * SVGA_CMD_DEFINE_GMR2 --
+ *
+ * Define guest memory region v2. See the description of GMRs above.
+ *
+ * Availability:
+ * SVGA_CAP_GMR2
+ */
+
+typedef
+struct {
+ uint32 gmrId;
+ uint32 numPages;
+} SVGAFifoCmdDefineGMR2;
+
+
+/*
+ * SVGA_CMD_REMAP_GMR2 --
+ *
+ * Remap guest memory region v2. See the description of GMRs above.
+ *
+ * This command allows guest to modify a portion of an existing GMR by
+ * invalidating it or reassigning it to different guest physical pages.
+ * The pages are identified by physical page number (PPN). The pages
+ * are assumed to be pinned and valid for DMA operations.
+ *
+ * Description of command flags:
+ *
+ * SVGA_REMAP_GMR2_VIA_GMR: If enabled, references a PPN list in a GMR.
+ * The PPN list must not overlap with the remap region (this can be
+ * handled trivially by referencing a separate GMR). If flag is
+ * disabled, PPN list is appended to SVGARemapGMR command.
+ *
+ * SVGA_REMAP_GMR2_PPN64: If set, PPN list is in PPN64 format, otherwise
+ * it is in PPN32 format.
+ *
+ * SVGA_REMAP_GMR2_SINGLE_PPN: If set, PPN list contains a single entry.
+ * A single PPN can be used to invalidate a portion of a GMR or
+ * map it to to a single guest scratch page.
+ *
+ * Availability:
+ * SVGA_CAP_GMR2
+ */
+
+typedef enum {
+ SVGA_REMAP_GMR2_PPN32 = 0,
+ SVGA_REMAP_GMR2_VIA_GMR = (1 << 0),
+ SVGA_REMAP_GMR2_PPN64 = (1 << 1),
+ SVGA_REMAP_GMR2_SINGLE_PPN = (1 << 2),
+} SVGARemapGMR2Flags;
+
+typedef
+struct {
+ uint32 gmrId;
+ SVGARemapGMR2Flags flags;
+ uint32 offsetPages; /* offset in pages to begin remap */
+ uint32 numPages; /* number of pages to remap */
+ /*
+ * Followed by additional data depending on SVGARemapGMR2Flags.
+ *
+ * If flag SVGA_REMAP_GMR2_VIA_GMR is set, single SVGAGuestPtr follows.
+ * Otherwise an array of page descriptors in PPN32 or PPN64 format
+ * (according to flag SVGA_REMAP_GMR2_PPN64) follows. If flag
+ * SVGA_REMAP_GMR2_SINGLE_PPN is set, array contains a single entry.
+ */
+} SVGAFifoCmdRemapGMR2;
+
#endif
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
index 87e43e0733bf..5a72ed908232 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
@@ -42,6 +42,10 @@ static uint32_t sys_placement_flags = TTM_PL_FLAG_SYSTEM |
static uint32_t gmr_placement_flags = VMW_PL_FLAG_GMR |
TTM_PL_FLAG_CACHED;
+static uint32_t gmr_ne_placement_flags = VMW_PL_FLAG_GMR |
+ TTM_PL_FLAG_CACHED |
+ TTM_PL_FLAG_NO_EVICT;
+
struct ttm_placement vmw_vram_placement = {
.fpfn = 0,
.lpfn = 0,
@@ -56,6 +60,11 @@ static uint32_t vram_gmr_placement_flags[] = {
VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
};
+static uint32_t gmr_vram_placement_flags[] = {
+ VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED,
+ TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
+};
+
struct ttm_placement vmw_vram_gmr_placement = {
.fpfn = 0,
.lpfn = 0,
@@ -65,6 +74,20 @@ struct ttm_placement vmw_vram_gmr_placement = {
.busy_placement = &gmr_placement_flags
};
+static uint32_t vram_gmr_ne_placement_flags[] = {
+ TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT,
+ VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
+};
+
+struct ttm_placement vmw_vram_gmr_ne_placement = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .num_placement = 2,
+ .placement = vram_gmr_ne_placement_flags,
+ .num_busy_placement = 1,
+ .busy_placement = &gmr_ne_placement_flags
+};
+
struct ttm_placement vmw_vram_sys_placement = {
.fpfn = 0,
.lpfn = 0,
@@ -92,6 +115,30 @@ struct ttm_placement vmw_sys_placement = {
.busy_placement = &sys_placement_flags
};
+static uint32_t evictable_placement_flags[] = {
+ TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED,
+ TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED,
+ VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+};
+
+struct ttm_placement vmw_evictable_placement = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .num_placement = 3,
+ .placement = evictable_placement_flags,
+ .num_busy_placement = 1,
+ .busy_placement = &sys_placement_flags
+};
+
+struct ttm_placement vmw_srf_placement = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .num_placement = 1,
+ .num_busy_placement = 2,
+ .placement = &gmr_placement_flags,
+ .busy_placement = gmr_vram_placement_flags
+};
+
struct vmw_ttm_backend {
struct ttm_backend backend;
struct page **pages;
@@ -274,39 +321,39 @@ static int vmw_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)
static void *vmw_sync_obj_ref(void *sync_obj)
{
- return sync_obj;
+
+ return (void *)
+ vmw_fence_obj_reference((struct vmw_fence_obj *) sync_obj);
}
static void vmw_sync_obj_unref(void **sync_obj)
{
- *sync_obj = NULL;
+ vmw_fence_obj_unreference((struct vmw_fence_obj **) sync_obj);
}
static int vmw_sync_obj_flush(void *sync_obj, void *sync_arg)
{
- struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
-
- mutex_lock(&dev_priv->hw_mutex);
- vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
- mutex_unlock(&dev_priv->hw_mutex);
+ vmw_fence_obj_flush((struct vmw_fence_obj *) sync_obj);
return 0;
}
static bool vmw_sync_obj_signaled(void *sync_obj, void *sync_arg)
{
- struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
- uint32_t sequence = (unsigned long) sync_obj;
+ unsigned long flags = (unsigned long) sync_arg;
+ return vmw_fence_obj_signaled((struct vmw_fence_obj *) sync_obj,
+ (uint32_t) flags);
- return vmw_fence_signaled(dev_priv, sequence);
}
static int vmw_sync_obj_wait(void *sync_obj, void *sync_arg,
bool lazy, bool interruptible)
{
- struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
- uint32_t sequence = (unsigned long) sync_obj;
+ unsigned long flags = (unsigned long) sync_arg;
- return vmw_wait_fence(dev_priv, false, sequence, false, 3*HZ);
+ return vmw_fence_obj_wait((struct vmw_fence_obj *) sync_obj,
+ (uint32_t) flags,
+ lazy, interruptible,
+ VMW_FENCE_WAIT_TIMEOUT);
}
struct ttm_bo_driver vmw_bo_driver = {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
new file mode 100644
index 000000000000..3fa884db08ab
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
@@ -0,0 +1,322 @@
+/**************************************************************************
+ *
+ * Copyright © 2011 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "ttm/ttm_placement.h"
+
+#include "drmP.h"
+#include "vmwgfx_drv.h"
+
+
+/**
+ * vmw_dmabuf_to_placement - Validate a buffer to placement.
+ *
+ * @dev_priv: Driver private.
+ * @buf: DMA buffer to move.
+ * @pin: Pin buffer if true.
+ * @interruptible: Use interruptible wait.
+ *
+ * May only be called by the current master since it assumes that the
+ * master lock is the current master's lock.
+ * This function takes the master's lock in write mode.
+ * Flushes and unpins the query bo to avoid failures.
+ *
+ * Returns
+ * -ERESTARTSYS if interrupted by a signal.
+ */
+int vmw_dmabuf_to_placement(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *buf,
+ struct ttm_placement *placement,
+ bool interruptible)
+{
+ struct vmw_master *vmaster = dev_priv->active_master;
+ struct ttm_buffer_object *bo = &buf->base;
+ int ret;
+
+ ret = ttm_write_lock(&vmaster->lock, interruptible);
+ if (unlikely(ret != 0))
+ return ret;
+
+ vmw_execbuf_release_pinned_bo(dev_priv, false, 0);
+
+ ret = ttm_bo_reserve(bo, interruptible, false, false, 0);
+ if (unlikely(ret != 0))
+ goto err;
+
+ ret = ttm_bo_validate(bo, placement, interruptible, false, false);
+
+ ttm_bo_unreserve(bo);
+
+err:
+ ttm_write_unlock(&vmaster->lock);
+ return ret;
+}
+
+/**
+ * vmw_dmabuf_to_vram_or_gmr - Move a buffer to vram or gmr.
+ *
+ * May only be called by the current master since it assumes that the
+ * master lock is the current master's lock.
+ * This function takes the master's lock in write mode.
+ * Flushes and unpins the query bo if @pin == true to avoid failures.
+ *
+ * @dev_priv: Driver private.
+ * @buf: DMA buffer to move.
+ * @pin: Pin buffer if true.
+ * @interruptible: Use interruptible wait.
+ *
+ * Returns
+ * -ERESTARTSYS if interrupted by a signal.
+ */
+int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *buf,
+ bool pin, bool interruptible)
+{
+ struct vmw_master *vmaster = dev_priv->active_master;
+ struct ttm_buffer_object *bo = &buf->base;
+ struct ttm_placement *placement;
+ int ret;
+
+ ret = ttm_write_lock(&vmaster->lock, interruptible);
+ if (unlikely(ret != 0))
+ return ret;
+
+ if (pin)
+ vmw_execbuf_release_pinned_bo(dev_priv, false, 0);
+
+ ret = ttm_bo_reserve(bo, interruptible, false, false, 0);
+ if (unlikely(ret != 0))
+ goto err;
+
+ /**
+ * Put BO in VRAM if there is space, otherwise as a GMR.
+ * If there is no space in VRAM and GMR ids are all used up,
+ * start evicting GMRs to make room. If the DMA buffer can't be
+ * used as a GMR, this will return -ENOMEM.
+ */
+
+ if (pin)
+ placement = &vmw_vram_gmr_ne_placement;
+ else
+ placement = &vmw_vram_gmr_placement;
+
+ ret = ttm_bo_validate(bo, placement, interruptible, false, false);
+ if (likely(ret == 0) || ret == -ERESTARTSYS)
+ goto err_unreserve;
+
+
+ /**
+ * If that failed, try VRAM again, this time evicting
+ * previous contents.
+ */
+
+ if (pin)
+ placement = &vmw_vram_ne_placement;
+ else
+ placement = &vmw_vram_placement;
+
+ ret = ttm_bo_validate(bo, placement, interruptible, false, false);
+
+err_unreserve:
+ ttm_bo_unreserve(bo);
+err:
+ ttm_write_unlock(&vmaster->lock);
+ return ret;
+}
+
+/**
+ * vmw_dmabuf_to_vram - Move a buffer to vram.
+ *
+ * May only be called by the current master since it assumes that the
+ * master lock is the current master's lock.
+ * This function takes the master's lock in write mode.
+ *
+ * @dev_priv: Driver private.
+ * @buf: DMA buffer to move.
+ * @pin: Pin buffer in vram if true.
+ * @interruptible: Use interruptible wait.
+ *
+ * Returns
+ * -ERESTARTSYS if interrupted by a signal.
+ */
+int vmw_dmabuf_to_vram(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *buf,
+ bool pin, bool interruptible)
+{
+ struct ttm_placement *placement;
+
+ if (pin)
+ placement = &vmw_vram_ne_placement;
+ else
+ placement = &vmw_vram_placement;
+
+ return vmw_dmabuf_to_placement(dev_priv, buf,
+ placement,
+ interruptible);
+}
+
+/**
+ * vmw_dmabuf_to_start_of_vram - Move a buffer to start of vram.
+ *
+ * May only be called by the current master since it assumes that the
+ * master lock is the current master's lock.
+ * This function takes the master's lock in write mode.
+ * Flushes and unpins the query bo if @pin == true to avoid failures.
+ *
+ * @dev_priv: Driver private.
+ * @buf: DMA buffer to move.
+ * @pin: Pin buffer in vram if true.
+ * @interruptible: Use interruptible wait.
+ *
+ * Returns
+ * -ERESTARTSYS if interrupted by a signal.
+ */
+int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *buf,
+ bool pin, bool interruptible)
+{
+ struct vmw_master *vmaster = dev_priv->active_master;
+ struct ttm_buffer_object *bo = &buf->base;
+ struct ttm_placement placement;
+ int ret = 0;
+
+ if (pin)
+ placement = vmw_vram_ne_placement;
+ else
+ placement = vmw_vram_placement;
+ placement.lpfn = bo->num_pages;
+
+ ret = ttm_write_lock(&vmaster->lock, interruptible);
+ if (unlikely(ret != 0))
+ return ret;
+
+ if (pin)
+ vmw_execbuf_release_pinned_bo(dev_priv, false, 0);
+
+ ret = ttm_bo_reserve(bo, interruptible, false, false, 0);
+ if (unlikely(ret != 0))
+ goto err_unlock;
+
+ /* Is this buffer already in vram but not at the start of it? */
+ if (bo->mem.mem_type == TTM_PL_VRAM &&
+ bo->mem.start < bo->num_pages &&
+ bo->mem.start > 0)
+ (void) ttm_bo_validate(bo, &vmw_sys_placement, false,
+ false, false);
+
+ ret = ttm_bo_validate(bo, &placement, interruptible, false, false);
+
+ /* For some reason we didn't up at the start of vram */
+ WARN_ON(ret == 0 && bo->offset != 0);
+
+ ttm_bo_unreserve(bo);
+err_unlock:
+ ttm_write_unlock(&vmaster->lock);
+
+ return ret;
+}
+
+
+/**
+ * vmw_dmabuf_upin - Unpin the buffer given buffer, does not move the buffer.
+ *
+ * May only be called by the current master since it assumes that the
+ * master lock is the current master's lock.
+ * This function takes the master's lock in write mode.
+ *
+ * @dev_priv: Driver private.
+ * @buf: DMA buffer to unpin.
+ * @interruptible: Use interruptible wait.
+ *
+ * Returns
+ * -ERESTARTSYS if interrupted by a signal.
+ */
+int vmw_dmabuf_unpin(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *buf,
+ bool interruptible)
+{
+ /*
+ * We could in theory early out if the buffer is
+ * unpinned but we need to lock and reserve the buffer
+ * anyways so we don't gain much by that.
+ */
+ return vmw_dmabuf_to_placement(dev_priv, buf,
+ &vmw_evictable_placement,
+ interruptible);
+}
+
+
+/**
+ * vmw_bo_get_guest_ptr - Get the guest ptr representing the current placement
+ * of a buffer.
+ *
+ * @bo: Pointer to a struct ttm_buffer_object. Must be pinned or reserved.
+ * @ptr: SVGAGuestPtr returning the result.
+ */
+void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *bo,
+ SVGAGuestPtr *ptr)
+{
+ if (bo->mem.mem_type == TTM_PL_VRAM) {
+ ptr->gmrId = SVGA_GMR_FRAMEBUFFER;
+ ptr->offset = bo->offset;
+ } else {
+ ptr->gmrId = bo->mem.start;
+ ptr->offset = 0;
+ }
+}
+
+
+/**
+ * vmw_bo_pin - Pin or unpin a buffer object without moving it.
+ *
+ * @bo: The buffer object. Must be reserved, and present either in VRAM
+ * or GMR memory.
+ * @pin: Whether to pin or unpin.
+ *
+ */
+void vmw_bo_pin(struct ttm_buffer_object *bo, bool pin)
+{
+ uint32_t pl_flags;
+ struct ttm_placement placement;
+ uint32_t old_mem_type = bo->mem.mem_type;
+ int ret;
+
+ BUG_ON(!atomic_read(&bo->reserved));
+ BUG_ON(old_mem_type != TTM_PL_VRAM &&
+ old_mem_type != VMW_PL_FLAG_GMR);
+
+ pl_flags = TTM_PL_FLAG_VRAM | VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED;
+ if (pin)
+ pl_flags |= TTM_PL_FLAG_NO_EVICT;
+
+ memset(&placement, 0, sizeof(placement));
+ placement.num_placement = 1;
+ placement.placement = &pl_flags;
+
+ ret = ttm_bo_validate(bo, &placement, false, true, true);
+
+ BUG_ON(ret != 0 || bo->mem.mem_type != old_mem_type);
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 96949b93d920..dff8fc767152 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -24,6 +24,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
+#include <linux/module.h>
#include "drmP.h"
#include "vmwgfx_drv.h"
@@ -82,17 +83,31 @@
#define DRM_IOCTL_VMW_EXECBUF \
DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_EXECBUF, \
struct drm_vmw_execbuf_arg)
-#define DRM_IOCTL_VMW_FIFO_DEBUG \
- DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FIFO_DEBUG, \
- struct drm_vmw_fifo_debug_arg)
+#define DRM_IOCTL_VMW_GET_3D_CAP \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_GET_3D_CAP, \
+ struct drm_vmw_get_3d_cap_arg)
#define DRM_IOCTL_VMW_FENCE_WAIT \
DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FENCE_WAIT, \
struct drm_vmw_fence_wait_arg)
+#define DRM_IOCTL_VMW_FENCE_SIGNALED \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FENCE_SIGNALED, \
+ struct drm_vmw_fence_signaled_arg)
+#define DRM_IOCTL_VMW_FENCE_UNREF \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_FENCE_UNREF, \
+ struct drm_vmw_fence_arg)
+#define DRM_IOCTL_VMW_FENCE_EVENT \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_FENCE_EVENT, \
+ struct drm_vmw_fence_event_arg)
+#define DRM_IOCTL_VMW_PRESENT \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_PRESENT, \
+ struct drm_vmw_present_arg)
+#define DRM_IOCTL_VMW_PRESENT_READBACK \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_PRESENT_READBACK, \
+ struct drm_vmw_present_readback_arg)
#define DRM_IOCTL_VMW_UPDATE_LAYOUT \
- DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_UPDATE_LAYOUT, \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UPDATE_LAYOUT, \
struct drm_vmw_update_layout_arg)
-
/**
* The core DRM version of this macro doesn't account for
* DRM_COMMAND_BASE.
@@ -135,12 +150,28 @@ static struct drm_ioctl_desc vmw_ioctls[] = {
DRM_AUTH | DRM_UNLOCKED),
VMW_IOCTL_DEF(VMW_EXECBUF, vmw_execbuf_ioctl,
DRM_AUTH | DRM_UNLOCKED),
- VMW_IOCTL_DEF(VMW_FIFO_DEBUG, vmw_fifo_debug_ioctl,
- DRM_AUTH | DRM_ROOT_ONLY | DRM_MASTER | DRM_UNLOCKED),
- VMW_IOCTL_DEF(VMW_FENCE_WAIT, vmw_fence_wait_ioctl,
+ VMW_IOCTL_DEF(VMW_FENCE_WAIT, vmw_fence_obj_wait_ioctl,
+ DRM_AUTH | DRM_UNLOCKED),
+ VMW_IOCTL_DEF(VMW_FENCE_SIGNALED,
+ vmw_fence_obj_signaled_ioctl,
+ DRM_AUTH | DRM_UNLOCKED),
+ VMW_IOCTL_DEF(VMW_FENCE_UNREF, vmw_fence_obj_unref_ioctl,
+ DRM_AUTH | DRM_UNLOCKED),
+ VMW_IOCTL_DEF(VMW_FENCE_EVENT,
+ vmw_fence_event_ioctl,
+ DRM_AUTH | DRM_UNLOCKED),
+ VMW_IOCTL_DEF(VMW_GET_3D_CAP, vmw_get_cap_3d_ioctl,
DRM_AUTH | DRM_UNLOCKED),
- VMW_IOCTL_DEF(VMW_UPDATE_LAYOUT, vmw_kms_update_layout_ioctl,
- DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED)
+
+ /* these allow direct access to the framebuffers mark as master only */
+ VMW_IOCTL_DEF(VMW_PRESENT, vmw_present_ioctl,
+ DRM_MASTER | DRM_AUTH | DRM_UNLOCKED),
+ VMW_IOCTL_DEF(VMW_PRESENT_READBACK,
+ vmw_present_readback_ioctl,
+ DRM_MASTER | DRM_AUTH | DRM_UNLOCKED),
+ VMW_IOCTL_DEF(VMW_UPDATE_LAYOUT,
+ vmw_kms_update_layout_ioctl,
+ DRM_MASTER | DRM_UNLOCKED),
};
static struct pci_device_id vmw_pci_id_list[] = {
@@ -189,8 +220,78 @@ static void vmw_print_capabilities(uint32_t capabilities)
DRM_INFO(" GMR.\n");
if (capabilities & SVGA_CAP_TRACES)
DRM_INFO(" Traces.\n");
+ if (capabilities & SVGA_CAP_GMR2)
+ DRM_INFO(" GMR2.\n");
+ if (capabilities & SVGA_CAP_SCREEN_OBJECT_2)
+ DRM_INFO(" Screen Object 2.\n");
+}
+
+
+/**
+ * vmw_execbuf_prepare_dummy_query - Initialize a query result structure at
+ * the start of a buffer object.
+ *
+ * @dev_priv: The device private structure.
+ *
+ * This function will idle the buffer using an uninterruptible wait, then
+ * map the first page and initialize a pending occlusion query result structure,
+ * Finally it will unmap the buffer.
+ *
+ * TODO: Since we're only mapping a single page, we should optimize the map
+ * to use kmap_atomic / iomap_atomic.
+ */
+static void vmw_dummy_query_bo_prepare(struct vmw_private *dev_priv)
+{
+ struct ttm_bo_kmap_obj map;
+ volatile SVGA3dQueryResult *result;
+ bool dummy;
+ int ret;
+ struct ttm_bo_device *bdev = &dev_priv->bdev;
+ struct ttm_buffer_object *bo = dev_priv->dummy_query_bo;
+
+ ttm_bo_reserve(bo, false, false, false, 0);
+ spin_lock(&bdev->fence_lock);
+ ret = ttm_bo_wait(bo, false, false, false);
+ spin_unlock(&bdev->fence_lock);
+ if (unlikely(ret != 0))
+ (void) vmw_fallback_wait(dev_priv, false, true, 0, false,
+ 10*HZ);
+
+ ret = ttm_bo_kmap(bo, 0, 1, &map);
+ if (likely(ret == 0)) {
+ result = ttm_kmap_obj_virtual(&map, &dummy);
+ result->totalSize = sizeof(*result);
+ result->state = SVGA3D_QUERYSTATE_PENDING;
+ result->result32 = 0xff;
+ ttm_bo_kunmap(&map);
+ } else
+ DRM_ERROR("Dummy query buffer map failed.\n");
+ ttm_bo_unreserve(bo);
+}
+
+
+/**
+ * vmw_dummy_query_bo_create - create a bo to hold a dummy query result
+ *
+ * @dev_priv: A device private structure.
+ *
+ * This function creates a small buffer object that holds the query
+ * result for dummy queries emitted as query barriers.
+ * No interruptible waits are done within this function.
+ *
+ * Returns an error if bo creation fails.
+ */
+static int vmw_dummy_query_bo_create(struct vmw_private *dev_priv)
+{
+ return ttm_bo_create(&dev_priv->bdev,
+ PAGE_SIZE,
+ ttm_bo_type_device,
+ &vmw_vram_sys_placement,
+ 0, 0, false, NULL,
+ &dev_priv->dummy_query_bo);
}
+
static int vmw_request_device(struct vmw_private *dev_priv)
{
int ret;
@@ -200,16 +301,42 @@ static int vmw_request_device(struct vmw_private *dev_priv)
DRM_ERROR("Unable to initialize FIFO.\n");
return ret;
}
+ vmw_fence_fifo_up(dev_priv->fman);
+ ret = vmw_dummy_query_bo_create(dev_priv);
+ if (unlikely(ret != 0))
+ goto out_no_query_bo;
+ vmw_dummy_query_bo_prepare(dev_priv);
return 0;
+
+out_no_query_bo:
+ vmw_fence_fifo_down(dev_priv->fman);
+ vmw_fifo_release(dev_priv, &dev_priv->fifo);
+ return ret;
}
static void vmw_release_device(struct vmw_private *dev_priv)
{
+ /*
+ * Previous destructions should've released
+ * the pinned bo.
+ */
+
+ BUG_ON(dev_priv->pinned_bo != NULL);
+
+ ttm_bo_unref(&dev_priv->dummy_query_bo);
+ vmw_fence_fifo_down(dev_priv->fman);
vmw_fifo_release(dev_priv, &dev_priv->fifo);
}
-int vmw_3d_resource_inc(struct vmw_private *dev_priv)
+/**
+ * Increase the 3d resource refcount.
+ * If the count was prevously zero, initialize the fifo, switching to svga
+ * mode. Note that the master holds a ref as well, and may request an
+ * explicit switch to svga mode if fb is not running, using @unhide_svga.
+ */
+int vmw_3d_resource_inc(struct vmw_private *dev_priv,
+ bool unhide_svga)
{
int ret = 0;
@@ -218,19 +345,42 @@ int vmw_3d_resource_inc(struct vmw_private *dev_priv)
ret = vmw_request_device(dev_priv);
if (unlikely(ret != 0))
--dev_priv->num_3d_resources;
+ } else if (unhide_svga) {
+ mutex_lock(&dev_priv->hw_mutex);
+ vmw_write(dev_priv, SVGA_REG_ENABLE,
+ vmw_read(dev_priv, SVGA_REG_ENABLE) &
+ ~SVGA_REG_ENABLE_HIDE);
+ mutex_unlock(&dev_priv->hw_mutex);
}
+
mutex_unlock(&dev_priv->release_mutex);
return ret;
}
-
-void vmw_3d_resource_dec(struct vmw_private *dev_priv)
+/**
+ * Decrease the 3d resource refcount.
+ * If the count reaches zero, disable the fifo, switching to vga mode.
+ * Note that the master holds a refcount as well, and may request an
+ * explicit switch to vga mode when it releases its refcount to account
+ * for the situation of an X server vt switch to VGA with 3d resources
+ * active.
+ */
+void vmw_3d_resource_dec(struct vmw_private *dev_priv,
+ bool hide_svga)
{
int32_t n3d;
mutex_lock(&dev_priv->release_mutex);
if (unlikely(--dev_priv->num_3d_resources == 0))
vmw_release_device(dev_priv);
+ else if (hide_svga) {
+ mutex_lock(&dev_priv->hw_mutex);
+ vmw_write(dev_priv, SVGA_REG_ENABLE,
+ vmw_read(dev_priv, SVGA_REG_ENABLE) |
+ SVGA_REG_ENABLE_HIDE);
+ mutex_unlock(&dev_priv->hw_mutex);
+ }
+
n3d = (int32_t) dev_priv->num_3d_resources;
mutex_unlock(&dev_priv->release_mutex);
@@ -252,7 +402,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
dev_priv->dev = dev;
dev_priv->vmw_chipset = chipset;
- dev_priv->last_read_sequence = (uint32_t) -100;
+ dev_priv->last_read_seqno = (uint32_t) -100;
mutex_init(&dev_priv->hw_mutex);
mutex_init(&dev_priv->cmdbuf_mutex);
mutex_init(&dev_priv->release_mutex);
@@ -263,8 +413,10 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
mutex_init(&dev_priv->init_mutex);
init_waitqueue_head(&dev_priv->fence_queue);
init_waitqueue_head(&dev_priv->fifo_queue);
- atomic_set(&dev_priv->fence_queue_waiters, 0);
+ dev_priv->fence_queue_waiters = 0;
atomic_set(&dev_priv->fifo_queue_waiters, 0);
+ INIT_LIST_HEAD(&dev_priv->surface_lru);
+ dev_priv->used_memory_size = 0;
dev_priv->io_start = pci_resource_start(dev->pdev, 0);
dev_priv->vram_start = pci_resource_start(dev->pdev, 1);
@@ -285,6 +437,10 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES);
+ dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE);
+ dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE);
+ dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH);
+ dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT);
if (dev_priv->capabilities & SVGA_CAP_GMR) {
dev_priv->max_gmr_descriptors =
vmw_read(dev_priv,
@@ -292,11 +448,19 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
dev_priv->max_gmr_ids =
vmw_read(dev_priv, SVGA_REG_GMR_MAX_IDS);
}
-
- dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE);
- dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE);
- dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH);
- dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT);
+ if (dev_priv->capabilities & SVGA_CAP_GMR2) {
+ dev_priv->max_gmr_pages =
+ vmw_read(dev_priv, SVGA_REG_GMRS_MAX_PAGES);
+ dev_priv->memory_size =
+ vmw_read(dev_priv, SVGA_REG_MEMORY_SIZE);
+ dev_priv->memory_size -= dev_priv->vram_size;
+ } else {
+ /*
+ * An arbitrary limit of 512MiB on surface
+ * memory. But all HWV8 hardware supports GMR2.
+ */
+ dev_priv->memory_size = 512*1024*1024;
+ }
mutex_unlock(&dev_priv->hw_mutex);
@@ -308,6 +472,12 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
DRM_INFO("Max GMR descriptors is %u\n",
(unsigned)dev_priv->max_gmr_descriptors);
}
+ if (dev_priv->capabilities & SVGA_CAP_GMR2) {
+ DRM_INFO("Max number of GMR pages is %u\n",
+ (unsigned)dev_priv->max_gmr_pages);
+ DRM_INFO("Max dedicated hypervisor surface memory is %u kiB\n",
+ (unsigned)dev_priv->memory_size / 1024);
+ }
DRM_INFO("VRAM at 0x%08x size is %u kiB\n",
dev_priv->vram_start, dev_priv->vram_size / 1024);
DRM_INFO("MMIO at 0x%08x size is %u kiB\n",
@@ -394,22 +564,34 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
goto out_no_device;
}
}
+
+ dev_priv->fman = vmw_fence_manager_init(dev_priv);
+ if (unlikely(dev_priv->fman == NULL))
+ goto out_no_fman;
+
+ /* Need to start the fifo to check if we can do screen objects */
+ ret = vmw_3d_resource_inc(dev_priv, true);
+ if (unlikely(ret != 0))
+ goto out_no_fifo;
+ vmw_kms_save_vga(dev_priv);
+
+ /* Start kms and overlay systems, needs fifo. */
ret = vmw_kms_init(dev_priv);
if (unlikely(ret != 0))
goto out_no_kms;
vmw_overlay_init(dev_priv);
+
+ /* 3D Depends on Screen Objects being used. */
+ DRM_INFO("Detected %sdevice 3D availability.\n",
+ vmw_fifo_have_3d(dev_priv) ?
+ "" : "no ");
+
+ /* We might be done with the fifo now */
if (dev_priv->enable_fb) {
- ret = vmw_3d_resource_inc(dev_priv);
- if (unlikely(ret != 0))
- goto out_no_fifo;
- vmw_kms_save_vga(dev_priv);
vmw_fb_init(dev_priv);
- DRM_INFO("%s", vmw_fifo_have_3d(dev_priv) ?
- "Detected device 3D availability.\n" :
- "Detected no device 3D availability.\n");
} else {
- DRM_INFO("Delayed 3D detection since we're not "
- "running the device in SVGA mode yet.\n");
+ vmw_kms_restore_vga(dev_priv);
+ vmw_3d_resource_dec(dev_priv, true);
}
if (dev_priv->capabilities & SVGA_CAP_IRQMASK) {
@@ -426,15 +608,19 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
return 0;
out_no_irq:
- if (dev_priv->enable_fb) {
+ if (dev_priv->enable_fb)
vmw_fb_close(dev_priv);
- vmw_kms_restore_vga(dev_priv);
- vmw_3d_resource_dec(dev_priv);
- }
-out_no_fifo:
vmw_overlay_close(dev_priv);
vmw_kms_close(dev_priv);
out_no_kms:
+ /* We still have a 3D resource reference held */
+ if (dev_priv->enable_fb) {
+ vmw_kms_restore_vga(dev_priv);
+ vmw_3d_resource_dec(dev_priv, false);
+ }
+out_no_fifo:
+ vmw_fence_manager_takedown(dev_priv->fman);
+out_no_fman:
if (dev_priv->stealth)
pci_release_region(dev->pdev, 2);
else
@@ -467,15 +653,18 @@ static int vmw_driver_unload(struct drm_device *dev)
unregister_pm_notifier(&dev_priv->pm_nb);
+ if (dev_priv->ctx.cmd_bounce)
+ vfree(dev_priv->ctx.cmd_bounce);
if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
drm_irq_uninstall(dev_priv->dev);
if (dev_priv->enable_fb) {
vmw_fb_close(dev_priv);
vmw_kms_restore_vga(dev_priv);
- vmw_3d_resource_dec(dev_priv);
+ vmw_3d_resource_dec(dev_priv, false);
}
vmw_kms_close(dev_priv);
vmw_overlay_close(dev_priv);
+ vmw_fence_manager_takedown(dev_priv->fman);
if (dev_priv->stealth)
pci_release_region(dev->pdev, 2);
else
@@ -646,7 +835,7 @@ static int vmw_master_set(struct drm_device *dev,
int ret = 0;
if (!dev_priv->enable_fb) {
- ret = vmw_3d_resource_inc(dev_priv);
+ ret = vmw_3d_resource_inc(dev_priv, true);
if (unlikely(ret != 0))
return ret;
vmw_kms_save_vga(dev_priv);
@@ -688,7 +877,7 @@ out_no_active_lock:
vmw_write(dev_priv, SVGA_REG_TRACES, 1);
mutex_unlock(&dev_priv->hw_mutex);
vmw_kms_restore_vga(dev_priv);
- vmw_3d_resource_dec(dev_priv);
+ vmw_3d_resource_dec(dev_priv, true);
}
return ret;
}
@@ -709,7 +898,7 @@ static void vmw_master_drop(struct drm_device *dev,
vmw_fp->locked_master = drm_master_get(file_priv->master);
ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile);
- vmw_kms_idle_workqueues(vmaster);
+ vmw_execbuf_release_pinned_bo(dev_priv, false, 0);
if (unlikely((ret != 0))) {
DRM_ERROR("Unable to lock TTM at VT switch.\n");
@@ -726,7 +915,7 @@ static void vmw_master_drop(struct drm_device *dev,
vmw_write(dev_priv, SVGA_REG_TRACES, 1);
mutex_unlock(&dev_priv->hw_mutex);
vmw_kms_restore_vga(dev_priv);
- vmw_3d_resource_dec(dev_priv);
+ vmw_3d_resource_dec(dev_priv, true);
}
dev_priv->active_master = &dev_priv->fbdev_master;
@@ -761,6 +950,7 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
* This empties VRAM and unbinds all GMR bindings.
* Buffer contents is moved to swappable memory.
*/
+ vmw_execbuf_release_pinned_bo(dev_priv, false, 0);
ttm_bo_swapout_all(&dev_priv->bdev);
break;
@@ -835,7 +1025,7 @@ static int vmw_pm_prepare(struct device *kdev)
*/
dev_priv->suspended = true;
if (dev_priv->enable_fb)
- vmw_3d_resource_dec(dev_priv);
+ vmw_3d_resource_dec(dev_priv, true);
if (dev_priv->num_3d_resources != 0) {
@@ -843,7 +1033,7 @@ static int vmw_pm_prepare(struct device *kdev)
"while 3D resources are active.\n");
if (dev_priv->enable_fb)
- vmw_3d_resource_inc(dev_priv);
+ vmw_3d_resource_inc(dev_priv, true);
dev_priv->suspended = false;
return -EBUSY;
}
@@ -862,7 +1052,7 @@ static void vmw_pm_complete(struct device *kdev)
* start fifo.
*/
if (dev_priv->enable_fb)
- vmw_3d_resource_inc(dev_priv);
+ vmw_3d_resource_inc(dev_priv, false);
dev_priv->suspended = false;
}
@@ -886,6 +1076,8 @@ static struct drm_driver driver = {
.irq_uninstall = vmw_irq_uninstall,
.irq_handler = vmw_irq_handler,
.get_vblank_counter = vmw_get_vblank_counter,
+ .enable_vblank = vmw_enable_vblank,
+ .disable_vblank = vmw_disable_vblank,
.reclaim_buffers_locked = NULL,
.ioctls = vmw_ioctls,
.num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls),
@@ -902,7 +1094,8 @@ static struct drm_driver driver = {
.release = drm_release,
.unlocked_ioctl = vmw_unlocked_ioctl,
.mmap = vmw_mmap,
- .poll = drm_poll,
+ .poll = vmw_fops_poll,
+ .read = vmw_fops_read,
.fasync = drm_fasync,
#if defined(CONFIG_COMPAT)
.compat_ioctl = drm_compat_ioctl,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index 10fc01f69c40..8cca91a93bde 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -38,20 +38,27 @@
#include "ttm/ttm_lock.h"
#include "ttm/ttm_execbuf_util.h"
#include "ttm/ttm_module.h"
+#include "vmwgfx_fence.h"
-#define VMWGFX_DRIVER_DATE "20100927"
-#define VMWGFX_DRIVER_MAJOR 1
-#define VMWGFX_DRIVER_MINOR 4
+#define VMWGFX_DRIVER_DATE "20111025"
+#define VMWGFX_DRIVER_MAJOR 2
+#define VMWGFX_DRIVER_MINOR 3
#define VMWGFX_DRIVER_PATCHLEVEL 0
#define VMWGFX_FILE_PAGE_OFFSET 0x00100000
#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
#define VMWGFX_MAX_RELOCATIONS 2048
-#define VMWGFX_MAX_GMRS 2048
+#define VMWGFX_MAX_VALIDATIONS 2048
#define VMWGFX_MAX_DISPLAYS 16
+#define VMWGFX_CMD_BOUNCE_INIT_SIZE 32768
#define VMW_PL_GMR TTM_PL_PRIV0
#define VMW_PL_FLAG_GMR TTM_PL_FLAG_PRIV0
+#define VMW_RES_CONTEXT ttm_driver_type0
+#define VMW_RES_SURFACE ttm_driver_type1
+#define VMW_RES_STREAM ttm_driver_type2
+#define VMW_RES_FENCE ttm_driver_type3
+
struct vmw_fpriv {
struct drm_master *locked_master;
struct ttm_object_file *tfile;
@@ -72,9 +79,11 @@ struct vmw_resource {
int id;
enum ttm_object_type res_type;
bool avail;
+ void (*remove_from_lists) (struct vmw_resource *res);
void (*hw_destroy) (struct vmw_resource *res);
void (*res_free) (struct vmw_resource *res);
-
+ struct list_head validate_head;
+ struct list_head query_head; /* Protected by the cmdbuf mutex */
/* TODO is a generic snooper needed? */
#if 0
void (*snoop)(struct vmw_resource *res,
@@ -90,8 +99,12 @@ struct vmw_cursor_snooper {
uint32_t *image;
};
+struct vmw_framebuffer;
+struct vmw_surface_offset;
+
struct vmw_surface {
struct vmw_resource res;
+ struct list_head lru_head; /* Protected by the resource lock */
uint32_t flags;
uint32_t format;
uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES];
@@ -102,9 +115,12 @@ struct vmw_surface {
/* TODO so far just a extra pointer */
struct vmw_cursor_snooper snooper;
+ struct ttm_buffer_object *backup;
+ struct vmw_surface_offset *offsets;
+ uint32_t backup_size;
};
-struct vmw_fence_queue {
+struct vmw_marker_queue {
struct list_head head;
struct timespec lag;
struct timespec lag_time;
@@ -115,16 +131,12 @@ struct vmw_fifo_state {
unsigned long reserved_size;
__le32 *dynamic_buffer;
__le32 *static_buffer;
- __le32 *last_buffer;
- uint32_t last_data_size;
- uint32_t last_buffer_size;
- bool last_buffer_add;
unsigned long static_buffer_size;
bool using_bounce_buffer;
uint32_t capabilities;
struct mutex fifo_mutex;
struct rw_semaphore rwsem;
- struct vmw_fence_queue fence_queue;
+ struct vmw_marker_queue marker_queue;
};
struct vmw_relocation {
@@ -136,6 +148,8 @@ struct vmw_sw_context{
struct ida bo_list;
uint32_t last_cid;
bool cid_valid;
+ bool kernel; /**< is the called made from the kernel */
+ struct vmw_resource *cur_ctx;
uint32_t last_sid;
uint32_t sid_translation;
bool sid_valid;
@@ -143,8 +157,16 @@ struct vmw_sw_context{
struct list_head validate_nodes;
struct vmw_relocation relocs[VMWGFX_MAX_RELOCATIONS];
uint32_t cur_reloc;
- struct ttm_validate_buffer val_bufs[VMWGFX_MAX_GMRS];
+ struct ttm_validate_buffer val_bufs[VMWGFX_MAX_VALIDATIONS];
uint32_t cur_val_buf;
+ uint32_t *cmd_bounce;
+ uint32_t cmd_bounce_size;
+ struct list_head resource_list;
+ uint32_t fence_flags;
+ struct list_head query_list;
+ struct ttm_buffer_object *cur_query_bo;
+ uint32_t cur_query_cid;
+ bool query_cid_valid;
};
struct vmw_legacy_display;
@@ -185,6 +207,8 @@ struct vmw_private {
uint32_t capabilities;
uint32_t max_gmr_descriptors;
uint32_t max_gmr_ids;
+ uint32_t max_gmr_pages;
+ uint32_t memory_size;
bool has_gmr;
struct mutex hw_mutex;
@@ -195,12 +219,7 @@ struct vmw_private {
struct vmw_vga_topology_state vga_save[VMWGFX_MAX_DISPLAYS];
uint32_t vga_width;
uint32_t vga_height;
- uint32_t vga_depth;
uint32_t vga_bpp;
- uint32_t vga_pseudo;
- uint32_t vga_red_mask;
- uint32_t vga_green_mask;
- uint32_t vga_blue_mask;
uint32_t vga_bpl;
uint32_t vga_pitchlock;
@@ -212,6 +231,7 @@ struct vmw_private {
void *fb_info;
struct vmw_legacy_display *ldu_priv;
+ struct vmw_screen_object_display *sou_priv;
struct vmw_overlay *overlay_priv;
/*
@@ -240,13 +260,16 @@ struct vmw_private {
* Fencing and IRQs.
*/
- atomic_t fence_seq;
+ atomic_t marker_seq;
wait_queue_head_t fence_queue;
wait_queue_head_t fifo_queue;
- atomic_t fence_queue_waiters;
+ int fence_queue_waiters; /* Protected by hw_mutex */
+ int goal_queue_waiters; /* Protected by hw_mutex */
atomic_t fifo_queue_waiters;
- uint32_t last_read_sequence;
+ uint32_t last_read_seqno;
spinlock_t irq_lock;
+ struct vmw_fence_manager *fman;
+ uint32_t irq_mask;
/*
* Device state
@@ -285,6 +308,26 @@ struct vmw_private {
struct mutex release_mutex;
uint32_t num_3d_resources;
+
+ /*
+ * Query processing. These members
+ * are protected by the cmdbuf mutex.
+ */
+
+ struct ttm_buffer_object *dummy_query_bo;
+ struct ttm_buffer_object *pinned_bo;
+ uint32_t query_cid;
+ bool dummy_query_bo_pinned;
+
+ /*
+ * Surface swapping. The "surface_lru" list is protected by the
+ * resource lock in order to be able to destroy a surface and take
+ * it off the lru atomically. "used_memory_size" is currently
+ * protected by the cmdbuf mutex for simplicity.
+ */
+
+ struct list_head surface_lru;
+ uint32_t used_memory_size;
};
static inline struct vmw_private *vmw_priv(struct drm_device *dev)
@@ -319,8 +362,8 @@ static inline uint32_t vmw_read(struct vmw_private *dev_priv,
return val;
}
-int vmw_3d_resource_inc(struct vmw_private *dev_priv);
-void vmw_3d_resource_dec(struct vmw_private *dev_priv);
+int vmw_3d_resource_inc(struct vmw_private *dev_priv, bool unhide_svga);
+void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga);
/**
* GMR utilities - vmwgfx_gmr.c
@@ -345,7 +388,8 @@ extern int vmw_context_define_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_context_check(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
- int id);
+ int id,
+ struct vmw_resource **p_res);
extern void vmw_surface_res_free(struct vmw_resource *res);
extern int vmw_surface_init(struct vmw_private *dev_priv,
struct vmw_surface *srf,
@@ -363,6 +407,8 @@ extern int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
extern int vmw_surface_check(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
uint32_t handle, int *id);
+extern int vmw_surface_validate(struct vmw_private *dev_priv,
+ struct vmw_surface *srf);
extern void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo);
extern int vmw_dmabuf_init(struct vmw_private *dev_priv,
struct vmw_dma_buffer *vmw_bo,
@@ -378,10 +424,6 @@ extern uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo,
extern void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo);
extern int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
uint32_t id, struct vmw_dma_buffer **out);
-extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
- struct vmw_dma_buffer *bo);
-extern int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv,
- struct vmw_dma_buffer *bo);
extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
@@ -390,7 +432,30 @@ extern int vmw_user_stream_lookup(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
uint32_t *inout_id,
struct vmw_resource **out);
+extern void vmw_resource_unreserve(struct list_head *list);
+/**
+ * DMA buffer helper routines - vmwgfx_dmabuf.c
+ */
+extern int vmw_dmabuf_to_placement(struct vmw_private *vmw_priv,
+ struct vmw_dma_buffer *bo,
+ struct ttm_placement *placement,
+ bool interruptible);
+extern int vmw_dmabuf_to_vram(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *buf,
+ bool pin, bool interruptible);
+extern int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *buf,
+ bool pin, bool interruptible);
+extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
+ struct vmw_dma_buffer *bo,
+ bool pin, bool interruptible);
+extern int vmw_dmabuf_unpin(struct vmw_private *vmw_priv,
+ struct vmw_dma_buffer *bo,
+ bool interruptible);
+extern void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *buf,
+ SVGAGuestPtr *ptr);
+extern void vmw_bo_pin(struct ttm_buffer_object *bo, bool pin);
/**
* Misc Ioctl functionality - vmwgfx_ioctl.c
@@ -398,8 +463,16 @@ extern int vmw_user_stream_lookup(struct vmw_private *dev_priv,
extern int vmw_getparam_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
-extern int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data,
+extern int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+extern int vmw_present_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+extern int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+extern unsigned int vmw_fops_poll(struct file *filp,
+ struct poll_table_struct *wait);
+extern ssize_t vmw_fops_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *offset);
/**
* Fifo utilities - vmwgfx_fifo.c
@@ -412,11 +485,12 @@ extern void vmw_fifo_release(struct vmw_private *dev_priv,
extern void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes);
extern void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes);
extern int vmw_fifo_send_fence(struct vmw_private *dev_priv,
- uint32_t *sequence);
+ uint32_t *seqno);
extern void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason);
-extern int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma);
extern bool vmw_fifo_have_3d(struct vmw_private *dev_priv);
extern bool vmw_fifo_have_pitchlock(struct vmw_private *dev_priv);
+extern int vmw_fifo_emit_dummy_query(struct vmw_private *dev_priv,
+ uint32_t cid);
/**
* TTM glue - vmwgfx_ttm_glue.c
@@ -434,7 +508,10 @@ extern struct ttm_placement vmw_vram_placement;
extern struct ttm_placement vmw_vram_ne_placement;
extern struct ttm_placement vmw_vram_sys_placement;
extern struct ttm_placement vmw_vram_gmr_placement;
+extern struct ttm_placement vmw_vram_gmr_ne_placement;
extern struct ttm_placement vmw_sys_placement;
+extern struct ttm_placement vmw_evictable_placement;
+extern struct ttm_placement vmw_srf_placement;
extern struct ttm_bo_driver vmw_bo_driver;
extern int vmw_dma_quiescent(struct drm_device *dev);
@@ -444,45 +521,70 @@ extern int vmw_dma_quiescent(struct drm_device *dev);
extern int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+extern int vmw_execbuf_process(struct drm_file *file_priv,
+ struct vmw_private *dev_priv,
+ void __user *user_commands,
+ void *kernel_commands,
+ uint32_t command_size,
+ uint64_t throttle_us,
+ struct drm_vmw_fence_rep __user
+ *user_fence_rep);
+
+extern void
+vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv,
+ bool only_on_cid_match, uint32_t cid);
+
+extern int vmw_execbuf_fence_commands(struct drm_file *file_priv,
+ struct vmw_private *dev_priv,
+ struct vmw_fence_obj **p_fence,
+ uint32_t *p_handle);
+extern void vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
+ struct vmw_fpriv *vmw_fp,
+ int ret,
+ struct drm_vmw_fence_rep __user
+ *user_fence_rep,
+ struct vmw_fence_obj *fence,
+ uint32_t fence_handle);
/**
* IRQs and wating - vmwgfx_irq.c
*/
extern irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS);
-extern int vmw_wait_fence(struct vmw_private *dev_priv, bool lazy,
- uint32_t sequence, bool interruptible,
- unsigned long timeout);
+extern int vmw_wait_seqno(struct vmw_private *dev_priv, bool lazy,
+ uint32_t seqno, bool interruptible,
+ unsigned long timeout);
extern void vmw_irq_preinstall(struct drm_device *dev);
extern int vmw_irq_postinstall(struct drm_device *dev);
extern void vmw_irq_uninstall(struct drm_device *dev);
-extern bool vmw_fence_signaled(struct vmw_private *dev_priv,
- uint32_t sequence);
-extern int vmw_fence_wait_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
+extern bool vmw_seqno_passed(struct vmw_private *dev_priv,
+ uint32_t seqno);
extern int vmw_fallback_wait(struct vmw_private *dev_priv,
bool lazy,
bool fifo_idle,
- uint32_t sequence,
+ uint32_t seqno,
bool interruptible,
unsigned long timeout);
-extern void vmw_update_sequence(struct vmw_private *dev_priv,
+extern void vmw_update_seqno(struct vmw_private *dev_priv,
struct vmw_fifo_state *fifo_state);
-
+extern void vmw_seqno_waiter_add(struct vmw_private *dev_priv);
+extern void vmw_seqno_waiter_remove(struct vmw_private *dev_priv);
+extern void vmw_goal_waiter_add(struct vmw_private *dev_priv);
+extern void vmw_goal_waiter_remove(struct vmw_private *dev_priv);
/**
- * Rudimentary fence objects currently used only for throttling -
- * vmwgfx_fence.c
+ * Rudimentary fence-like objects currently used only for throttling -
+ * vmwgfx_marker.c
*/
-extern void vmw_fence_queue_init(struct vmw_fence_queue *queue);
-extern void vmw_fence_queue_takedown(struct vmw_fence_queue *queue);
-extern int vmw_fence_push(struct vmw_fence_queue *queue,
- uint32_t sequence);
-extern int vmw_fence_pull(struct vmw_fence_queue *queue,
- uint32_t signaled_sequence);
+extern void vmw_marker_queue_init(struct vmw_marker_queue *queue);
+extern void vmw_marker_queue_takedown(struct vmw_marker_queue *queue);
+extern int vmw_marker_push(struct vmw_marker_queue *queue,
+ uint32_t seqno);
+extern int vmw_marker_pull(struct vmw_marker_queue *queue,
+ uint32_t signaled_seqno);
extern int vmw_wait_lag(struct vmw_private *dev_priv,
- struct vmw_fence_queue *queue, uint32_t us);
+ struct vmw_marker_queue *queue, uint32_t us);
/**
* Kernel framebuffer - vmwgfx_fb.c
@@ -508,16 +610,31 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,
struct ttm_object_file *tfile,
struct ttm_buffer_object *bo,
SVGA3dCmdHeader *header);
-void vmw_kms_write_svga(struct vmw_private *vmw_priv,
- unsigned width, unsigned height, unsigned pitch,
- unsigned bbp, unsigned depth);
-int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
+int vmw_kms_write_svga(struct vmw_private *vmw_priv,
+ unsigned width, unsigned height, unsigned pitch,
+ unsigned bpp, unsigned depth);
void vmw_kms_idle_workqueues(struct vmw_master *vmaster);
bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
uint32_t pitch,
uint32_t height);
u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc);
+int vmw_enable_vblank(struct drm_device *dev, int crtc);
+void vmw_disable_vblank(struct drm_device *dev, int crtc);
+int vmw_kms_present(struct vmw_private *dev_priv,
+ struct drm_file *file_priv,
+ struct vmw_framebuffer *vfb,
+ struct vmw_surface *surface,
+ uint32_t sid, int32_t destX, int32_t destY,
+ struct drm_vmw_rect *clips,
+ uint32_t num_clips);
+int vmw_kms_readback(struct vmw_private *dev_priv,
+ struct drm_file *file_priv,
+ struct vmw_framebuffer *vfb,
+ struct drm_vmw_fence_rep __user *user_fence_rep,
+ struct drm_vmw_rect *clips,
+ uint32_t num_clips);
+int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
/**
* Overlay control - vmwgfx_overlay.c
@@ -576,4 +693,8 @@ static inline struct vmw_dma_buffer *vmw_dmabuf_reference(struct vmw_dma_buffer
return NULL;
}
+static inline struct ttm_mem_global *vmw_mem_glob(struct vmw_private *dev_priv)
+{
+ return (struct ttm_mem_global *) dev_priv->mem_global_ref.object;
+}
#endif
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index 41b95ed6dbcd..40932fbdac0f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -44,10 +44,71 @@ static int vmw_cmd_ok(struct vmw_private *dev_priv,
return 0;
}
+static void vmw_resource_to_validate_list(struct vmw_sw_context *sw_context,
+ struct vmw_resource **p_res)
+{
+ struct vmw_resource *res = *p_res;
+
+ if (list_empty(&res->validate_head)) {
+ list_add_tail(&res->validate_head, &sw_context->resource_list);
+ *p_res = NULL;
+ } else
+ vmw_resource_unreference(p_res);
+}
+
+/**
+ * vmw_bo_to_validate_list - add a bo to a validate list
+ *
+ * @sw_context: The software context used for this command submission batch.
+ * @bo: The buffer object to add.
+ * @fence_flags: Fence flags to be or'ed with any other fence flags for
+ * this buffer on this submission batch.
+ * @p_val_node: If non-NULL Will be updated with the validate node number
+ * on return.
+ *
+ * Returns -EINVAL if the limit of number of buffer objects per command
+ * submission is reached.
+ */
+static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context,
+ struct ttm_buffer_object *bo,
+ uint32_t fence_flags,
+ uint32_t *p_val_node)
+{
+ uint32_t val_node;
+ struct ttm_validate_buffer *val_buf;
+
+ val_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf);
+
+ if (unlikely(val_node >= VMWGFX_MAX_VALIDATIONS)) {
+ DRM_ERROR("Max number of DMA buffers per submission"
+ " exceeded.\n");
+ return -EINVAL;
+ }
+
+ val_buf = &sw_context->val_bufs[val_node];
+ if (unlikely(val_node == sw_context->cur_val_buf)) {
+ val_buf->new_sync_obj_arg = NULL;
+ val_buf->bo = ttm_bo_reference(bo);
+ list_add_tail(&val_buf->head, &sw_context->validate_nodes);
+ ++sw_context->cur_val_buf;
+ }
+
+ val_buf->new_sync_obj_arg = (void *)
+ ((unsigned long) val_buf->new_sync_obj_arg | fence_flags);
+ sw_context->fence_flags |= fence_flags;
+
+ if (p_val_node)
+ *p_val_node = val_node;
+
+ return 0;
+}
+
static int vmw_cmd_cid_check(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
+ struct vmw_resource *ctx;
+
struct vmw_cid_cmd {
SVGA3dCmdHeader header;
__le32 cid;
@@ -58,7 +119,8 @@ static int vmw_cmd_cid_check(struct vmw_private *dev_priv,
if (likely(sw_context->cid_valid && cmd->cid == sw_context->last_cid))
return 0;
- ret = vmw_context_check(dev_priv, sw_context->tfile, cmd->cid);
+ ret = vmw_context_check(dev_priv, sw_context->tfile, cmd->cid,
+ &ctx);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not find or use context %u\n",
(unsigned) cmd->cid);
@@ -67,6 +129,8 @@ static int vmw_cmd_cid_check(struct vmw_private *dev_priv,
sw_context->last_cid = cmd->cid;
sw_context->cid_valid = true;
+ sw_context->cur_ctx = ctx;
+ vmw_resource_to_validate_list(sw_context, &ctx);
return 0;
}
@@ -75,29 +139,45 @@ static int vmw_cmd_sid_check(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
uint32_t *sid)
{
+ struct vmw_surface *srf;
+ int ret;
+ struct vmw_resource *res;
+
if (*sid == SVGA3D_INVALID_ID)
return 0;
- if (unlikely((!sw_context->sid_valid ||
- *sid != sw_context->last_sid))) {
- int real_id;
- int ret = vmw_surface_check(dev_priv, sw_context->tfile,
- *sid, &real_id);
+ if (likely((sw_context->sid_valid &&
+ *sid == sw_context->last_sid))) {
+ *sid = sw_context->sid_translation;
+ return 0;
+ }
- if (unlikely(ret != 0)) {
- DRM_ERROR("Could ot find or use surface 0x%08x "
- "address 0x%08lx\n",
- (unsigned int) *sid,
- (unsigned long) sid);
- return ret;
- }
+ ret = vmw_user_surface_lookup_handle(dev_priv,
+ sw_context->tfile,
+ *sid, &srf);
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Could ot find or use surface 0x%08x "
+ "address 0x%08lx\n",
+ (unsigned int) *sid,
+ (unsigned long) sid);
+ return ret;
+ }
- sw_context->last_sid = *sid;
- sw_context->sid_valid = true;
- *sid = real_id;
- sw_context->sid_translation = real_id;
- } else
- *sid = sw_context->sid_translation;
+ ret = vmw_surface_validate(dev_priv, srf);
+ if (unlikely(ret != 0)) {
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Could not validate surface.\n");
+ vmw_surface_unreference(&srf);
+ return ret;
+ }
+
+ sw_context->last_sid = *sid;
+ sw_context->sid_valid = true;
+ sw_context->sid_translation = srf->res.id;
+ *sid = sw_context->sid_translation;
+
+ res = &srf->res;
+ vmw_resource_to_validate_list(sw_context, &res);
return 0;
}
@@ -166,6 +246,12 @@ static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv,
} *cmd;
cmd = container_of(header, struct vmw_sid_cmd, header);
+
+ if (unlikely(!sw_context->kernel)) {
+ DRM_ERROR("Kernel only SVGA3d command: %u.\n", cmd->header.id);
+ return -EPERM;
+ }
+
return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.srcImage.sid);
}
@@ -178,10 +264,179 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv,
SVGA3dCmdPresent body;
} *cmd;
+
cmd = container_of(header, struct vmw_sid_cmd, header);
+
+ if (unlikely(!sw_context->kernel)) {
+ DRM_ERROR("Kernel only SVGA3d command: %u.\n", cmd->header.id);
+ return -EPERM;
+ }
+
return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid);
}
+/**
+ * vmw_query_bo_switch_prepare - Prepare to switch pinned buffer for queries.
+ *
+ * @dev_priv: The device private structure.
+ * @cid: The hardware context for the next query.
+ * @new_query_bo: The new buffer holding query results.
+ * @sw_context: The software context used for this command submission.
+ *
+ * This function checks whether @new_query_bo is suitable for holding
+ * query results, and if another buffer currently is pinned for query
+ * results. If so, the function prepares the state of @sw_context for
+ * switching pinned buffers after successful submission of the current
+ * command batch. It also checks whether we're using a new query context.
+ * In that case, it makes sure we emit a query barrier for the old
+ * context before the current query buffer is fenced.
+ */
+static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv,
+ uint32_t cid,
+ struct ttm_buffer_object *new_query_bo,
+ struct vmw_sw_context *sw_context)
+{
+ int ret;
+ bool add_cid = false;
+ uint32_t cid_to_add;
+
+ if (unlikely(new_query_bo != sw_context->cur_query_bo)) {
+
+ if (unlikely(new_query_bo->num_pages > 4)) {
+ DRM_ERROR("Query buffer too large.\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(sw_context->cur_query_bo != NULL)) {
+ BUG_ON(!sw_context->query_cid_valid);
+ add_cid = true;
+ cid_to_add = sw_context->cur_query_cid;
+ ret = vmw_bo_to_validate_list(sw_context,
+ sw_context->cur_query_bo,
+ DRM_VMW_FENCE_FLAG_EXEC,
+ NULL);
+ if (unlikely(ret != 0))
+ return ret;
+ }
+ sw_context->cur_query_bo = new_query_bo;
+
+ ret = vmw_bo_to_validate_list(sw_context,
+ dev_priv->dummy_query_bo,
+ DRM_VMW_FENCE_FLAG_EXEC,
+ NULL);
+ if (unlikely(ret != 0))
+ return ret;
+
+ }
+
+ if (unlikely(cid != sw_context->cur_query_cid &&
+ sw_context->query_cid_valid)) {
+ add_cid = true;
+ cid_to_add = sw_context->cur_query_cid;
+ }
+
+ sw_context->cur_query_cid = cid;
+ sw_context->query_cid_valid = true;
+
+ if (add_cid) {
+ struct vmw_resource *ctx = sw_context->cur_ctx;
+
+ if (list_empty(&ctx->query_head))
+ list_add_tail(&ctx->query_head,
+ &sw_context->query_list);
+ ret = vmw_bo_to_validate_list(sw_context,
+ dev_priv->dummy_query_bo,
+ DRM_VMW_FENCE_FLAG_EXEC,
+ NULL);
+ if (unlikely(ret != 0))
+ return ret;
+ }
+ return 0;
+}
+
+
+/**
+ * vmw_query_bo_switch_commit - Finalize switching pinned query buffer
+ *
+ * @dev_priv: The device private structure.
+ * @sw_context: The software context used for this command submission batch.
+ *
+ * This function will check if we're switching query buffers, and will then,
+ * if no other query waits are issued this command submission batch,
+ * issue a dummy occlusion query wait used as a query barrier. When the fence
+ * object following that query wait has signaled, we are sure that all
+ * preseding queries have finished, and the old query buffer can be unpinned.
+ * However, since both the new query buffer and the old one are fenced with
+ * that fence, we can do an asynchronus unpin now, and be sure that the
+ * old query buffer won't be moved until the fence has signaled.
+ *
+ * As mentioned above, both the new - and old query buffers need to be fenced
+ * using a sequence emitted *after* calling this function.
+ */
+static void vmw_query_bo_switch_commit(struct vmw_private *dev_priv,
+ struct vmw_sw_context *sw_context)
+{
+
+ struct vmw_resource *ctx, *next_ctx;
+ int ret;
+
+ /*
+ * The validate list should still hold references to all
+ * contexts here.
+ */
+
+ list_for_each_entry_safe(ctx, next_ctx, &sw_context->query_list,
+ query_head) {
+ list_del_init(&ctx->query_head);
+
+ BUG_ON(list_empty(&ctx->validate_head));
+
+ ret = vmw_fifo_emit_dummy_query(dev_priv, ctx->id);
+
+ if (unlikely(ret != 0))
+ DRM_ERROR("Out of fifo space for dummy query.\n");
+ }
+
+ if (dev_priv->pinned_bo != sw_context->cur_query_bo) {
+ if (dev_priv->pinned_bo) {
+ vmw_bo_pin(dev_priv->pinned_bo, false);
+ ttm_bo_unref(&dev_priv->pinned_bo);
+ }
+
+ vmw_bo_pin(sw_context->cur_query_bo, true);
+
+ /*
+ * We pin also the dummy_query_bo buffer so that we
+ * don't need to validate it when emitting
+ * dummy queries in context destroy paths.
+ */
+
+ vmw_bo_pin(dev_priv->dummy_query_bo, true);
+ dev_priv->dummy_query_bo_pinned = true;
+
+ dev_priv->query_cid = sw_context->cur_query_cid;
+ dev_priv->pinned_bo =
+ ttm_bo_reference(sw_context->cur_query_bo);
+ }
+}
+
+/**
+ * vmw_query_switch_backoff - clear query barrier list
+ * @sw_context: The sw context used for this submission batch.
+ *
+ * This function is used as part of an error path, where a previously
+ * set up list of query barriers needs to be cleared.
+ *
+ */
+static void vmw_query_switch_backoff(struct vmw_sw_context *sw_context)
+{
+ struct list_head *list, *next;
+
+ list_for_each_safe(list, next, &sw_context->query_list) {
+ list_del_init(list);
+ }
+}
+
static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGAGuestPtr *ptr,
@@ -191,8 +446,6 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,
struct ttm_buffer_object *bo;
uint32_t handle = ptr->gmrId;
struct vmw_relocation *reloc;
- uint32_t cur_validate_node;
- struct ttm_validate_buffer *val_buf;
int ret;
ret = vmw_user_dmabuf_lookup(sw_context->tfile, handle, &vmw_bo);
@@ -212,22 +465,11 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,
reloc = &sw_context->relocs[sw_context->cur_reloc++];
reloc->location = ptr;
- cur_validate_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf);
- if (unlikely(cur_validate_node >= VMWGFX_MAX_GMRS)) {
- DRM_ERROR("Max number of DMA buffers per submission"
- " exceeded.\n");
- ret = -EINVAL;
+ ret = vmw_bo_to_validate_list(sw_context, bo, DRM_VMW_FENCE_FLAG_EXEC,
+ &reloc->index);
+ if (unlikely(ret != 0))
goto out_no_reloc;
- }
- reloc->index = cur_validate_node;
- if (unlikely(cur_validate_node == sw_context->cur_val_buf)) {
- val_buf = &sw_context->val_bufs[cur_validate_node];
- val_buf->bo = ttm_bo_reference(bo);
- val_buf->new_sync_obj_arg = (void *) dev_priv;
- list_add_tail(&val_buf->head, &sw_context->validate_nodes);
- ++sw_context->cur_val_buf;
- }
*vmw_bo_p = vmw_bo;
return 0;
@@ -259,8 +501,11 @@ static int vmw_cmd_end_query(struct vmw_private *dev_priv,
if (unlikely(ret != 0))
return ret;
+ ret = vmw_query_bo_switch_prepare(dev_priv, cmd->q.cid,
+ &vmw_bo->base, sw_context);
+
vmw_dmabuf_unreference(&vmw_bo);
- return 0;
+ return ret;
}
static int vmw_cmd_wait_query(struct vmw_private *dev_priv,
@@ -273,6 +518,7 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv,
SVGA3dCmdWaitForQuery q;
} *cmd;
int ret;
+ struct vmw_resource *ctx;
cmd = container_of(header, struct vmw_query_cmd, header);
ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
@@ -286,10 +532,19 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv,
return ret;
vmw_dmabuf_unreference(&vmw_bo);
+
+ /*
+ * This wait will act as a barrier for previous waits for this
+ * context.
+ */
+
+ ctx = sw_context->cur_ctx;
+ if (!list_empty(&ctx->query_head))
+ list_del_init(&ctx->query_head);
+
return 0;
}
-
static int vmw_cmd_dma(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
@@ -302,6 +557,7 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
SVGA3dCmdSurfaceDMA dma;
} *cmd;
int ret;
+ struct vmw_resource *res;
cmd = container_of(header, struct vmw_dma_cmd, header);
ret = vmw_translate_guest_ptr(dev_priv, sw_context,
@@ -318,18 +574,28 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
goto out_no_reloc;
}
- /**
+ ret = vmw_surface_validate(dev_priv, srf);
+ if (unlikely(ret != 0)) {
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Culd not validate surface.\n");
+ goto out_no_validate;
+ }
+
+ /*
* Patch command stream with device SID.
*/
-
cmd->dma.host.sid = srf->res.id;
vmw_kms_cursor_snoop(srf, sw_context->tfile, bo, header);
- /**
- * FIXME: May deadlock here when called from the
- * command parsing code.
- */
- vmw_surface_unreference(&srf);
+ vmw_dmabuf_unreference(&vmw_bo);
+
+ res = &srf->res;
+ vmw_resource_to_validate_list(sw_context, &res);
+
+ return 0;
+
+out_no_validate:
+ vmw_surface_unreference(&srf);
out_no_reloc:
vmw_dmabuf_unreference(&vmw_bo);
return ret;
@@ -419,6 +685,71 @@ static int vmw_cmd_tex_state(struct vmw_private *dev_priv,
return 0;
}
+static int vmw_cmd_check_define_gmrfb(struct vmw_private *dev_priv,
+ struct vmw_sw_context *sw_context,
+ void *buf)
+{
+ struct vmw_dma_buffer *vmw_bo;
+ int ret;
+
+ struct {
+ uint32_t header;
+ SVGAFifoCmdDefineGMRFB body;
+ } *cmd = buf;
+
+ ret = vmw_translate_guest_ptr(dev_priv, sw_context,
+ &cmd->body.ptr,
+ &vmw_bo);
+ if (unlikely(ret != 0))
+ return ret;
+
+ vmw_dmabuf_unreference(&vmw_bo);
+
+ return ret;
+}
+
+static int vmw_cmd_check_not_3d(struct vmw_private *dev_priv,
+ struct vmw_sw_context *sw_context,
+ void *buf, uint32_t *size)
+{
+ uint32_t size_remaining = *size;
+ uint32_t cmd_id;
+
+ cmd_id = le32_to_cpu(((uint32_t *)buf)[0]);
+ switch (cmd_id) {
+ case SVGA_CMD_UPDATE:
+ *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdUpdate);
+ break;
+ case SVGA_CMD_DEFINE_GMRFB:
+ *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdDefineGMRFB);
+ break;
+ case SVGA_CMD_BLIT_GMRFB_TO_SCREEN:
+ *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdBlitGMRFBToScreen);
+ break;
+ case SVGA_CMD_BLIT_SCREEN_TO_GMRFB:
+ *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdBlitGMRFBToScreen);
+ break;
+ default:
+ DRM_ERROR("Unsupported SVGA command: %u.\n", cmd_id);
+ return -EINVAL;
+ }
+
+ if (*size > size_remaining) {
+ DRM_ERROR("Invalid SVGA command (size mismatch):"
+ " %u.\n", cmd_id);
+ return -EINVAL;
+ }
+
+ if (unlikely(!sw_context->kernel)) {
+ DRM_ERROR("Kernel only SVGA command: %u.\n", cmd_id);
+ return -EPERM;
+ }
+
+ if (cmd_id == SVGA_CMD_DEFINE_GMRFB)
+ return vmw_cmd_check_define_gmrfb(dev_priv, sw_context, buf);
+
+ return 0;
+}
typedef int (*vmw_cmd_func) (struct vmw_private *,
struct vmw_sw_context *,
@@ -471,11 +802,11 @@ static int vmw_cmd_check(struct vmw_private *dev_priv,
SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf;
int ret;
- cmd_id = ((uint32_t *)buf)[0];
- if (cmd_id == SVGA_CMD_UPDATE) {
- *size = 5 << 2;
- return 0;
- }
+ cmd_id = le32_to_cpu(((uint32_t *)buf)[0]);
+ /* Handle any none 3D commands */
+ if (unlikely(cmd_id < SVGA_CMD_MAX))
+ return vmw_cmd_check_not_3d(dev_priv, sw_context, buf, size);
+
cmd_id = le32_to_cpu(header->id);
*size = le32_to_cpu(header->size) + sizeof(SVGA3dCmdHeader);
@@ -500,7 +831,8 @@ out_err:
static int vmw_cmd_check_all(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
- void *buf, uint32_t size)
+ void *buf,
+ uint32_t size)
{
int32_t cur_size = size;
int ret;
@@ -550,7 +882,11 @@ static void vmw_apply_relocations(struct vmw_sw_context *sw_context)
static void vmw_clear_validations(struct vmw_sw_context *sw_context)
{
struct ttm_validate_buffer *entry, *next;
+ struct vmw_resource *res, *res_next;
+ /*
+ * Drop references to DMA buffers held during command submission.
+ */
list_for_each_entry_safe(entry, next, &sw_context->validate_nodes,
head) {
list_del(&entry->head);
@@ -559,6 +895,16 @@ static void vmw_clear_validations(struct vmw_sw_context *sw_context)
sw_context->cur_val_buf--;
}
BUG_ON(sw_context->cur_val_buf != 0);
+
+ /*
+ * Drop references to resources held during command submission.
+ */
+ vmw_resource_unreserve(&sw_context->resource_list);
+ list_for_each_entry_safe(res, res_next, &sw_context->resource_list,
+ validate_head) {
+ list_del_init(&res->validate_head);
+ vmw_resource_unreference(&res);
+ }
}
static int vmw_validate_single_buffer(struct vmw_private *dev_priv,
@@ -566,6 +912,16 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv,
{
int ret;
+
+ /*
+ * Don't validate pinned buffers.
+ */
+
+ if (bo == dev_priv->pinned_bo ||
+ (bo == dev_priv->dummy_query_bo &&
+ dev_priv->dummy_query_bo_pinned))
+ return 0;
+
/**
* Put BO in VRAM if there is space, otherwise as a GMR.
* If there is no space in VRAM and GMR ids are all used up,
@@ -602,57 +958,208 @@ static int vmw_validate_buffers(struct vmw_private *dev_priv,
return 0;
}
-int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+static int vmw_resize_cmd_bounce(struct vmw_sw_context *sw_context,
+ uint32_t size)
+{
+ if (likely(sw_context->cmd_bounce_size >= size))
+ return 0;
+
+ if (sw_context->cmd_bounce_size == 0)
+ sw_context->cmd_bounce_size = VMWGFX_CMD_BOUNCE_INIT_SIZE;
+
+ while (sw_context->cmd_bounce_size < size) {
+ sw_context->cmd_bounce_size =
+ PAGE_ALIGN(sw_context->cmd_bounce_size +
+ (sw_context->cmd_bounce_size >> 1));
+ }
+
+ if (sw_context->cmd_bounce != NULL)
+ vfree(sw_context->cmd_bounce);
+
+ sw_context->cmd_bounce = vmalloc(sw_context->cmd_bounce_size);
+
+ if (sw_context->cmd_bounce == NULL) {
+ DRM_ERROR("Failed to allocate command bounce buffer.\n");
+ sw_context->cmd_bounce_size = 0;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * vmw_execbuf_fence_commands - create and submit a command stream fence
+ *
+ * Creates a fence object and submits a command stream marker.
+ * If this fails for some reason, We sync the fifo and return NULL.
+ * It is then safe to fence buffers with a NULL pointer.
+ *
+ * If @p_handle is not NULL @file_priv must also not be NULL. Creates
+ * a userspace handle if @p_handle is not NULL, otherwise not.
+ */
+
+int vmw_execbuf_fence_commands(struct drm_file *file_priv,
+ struct vmw_private *dev_priv,
+ struct vmw_fence_obj **p_fence,
+ uint32_t *p_handle)
{
- struct vmw_private *dev_priv = vmw_priv(dev);
- struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data;
- struct drm_vmw_fence_rep fence_rep;
- struct drm_vmw_fence_rep __user *user_fence_rep;
- int ret;
- void *user_cmd;
- void *cmd;
uint32_t sequence;
- struct vmw_sw_context *sw_context = &dev_priv->ctx;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
+ int ret;
+ bool synced = false;
- ret = ttm_read_lock(&vmaster->lock, true);
- if (unlikely(ret != 0))
- return ret;
+ /* p_handle implies file_priv. */
+ BUG_ON(p_handle != NULL && file_priv == NULL);
- ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex);
+ ret = vmw_fifo_send_fence(dev_priv, &sequence);
if (unlikely(ret != 0)) {
- ret = -ERESTARTSYS;
- goto out_no_cmd_mutex;
+ DRM_ERROR("Fence submission error. Syncing.\n");
+ synced = true;
}
- cmd = vmw_fifo_reserve(dev_priv, arg->command_size);
- if (unlikely(cmd == NULL)) {
- DRM_ERROR("Failed reserving fifo space for commands.\n");
- ret = -ENOMEM;
- goto out_unlock;
+ if (p_handle != NULL)
+ ret = vmw_user_fence_create(file_priv, dev_priv->fman,
+ sequence,
+ DRM_VMW_FENCE_FLAG_EXEC,
+ p_fence, p_handle);
+ else
+ ret = vmw_fence_create(dev_priv->fman, sequence,
+ DRM_VMW_FENCE_FLAG_EXEC,
+ p_fence);
+
+ if (unlikely(ret != 0 && !synced)) {
+ (void) vmw_fallback_wait(dev_priv, false, false,
+ sequence, false,
+ VMW_FENCE_WAIT_TIMEOUT);
+ *p_fence = NULL;
}
- user_cmd = (void __user *)(unsigned long)arg->commands;
- ret = copy_from_user(cmd, user_cmd, arg->command_size);
+ return 0;
+}
- if (unlikely(ret != 0)) {
- ret = -EFAULT;
- DRM_ERROR("Failed copying commands.\n");
- goto out_commit;
+/**
+ * vmw_execbuf_copy_fence_user - copy fence object information to
+ * user-space.
+ *
+ * @dev_priv: Pointer to a vmw_private struct.
+ * @vmw_fp: Pointer to the struct vmw_fpriv representing the calling file.
+ * @ret: Return value from fence object creation.
+ * @user_fence_rep: User space address of a struct drm_vmw_fence_rep to
+ * which the information should be copied.
+ * @fence: Pointer to the fenc object.
+ * @fence_handle: User-space fence handle.
+ *
+ * This function copies fence information to user-space. If copying fails,
+ * The user-space struct drm_vmw_fence_rep::error member is hopefully
+ * left untouched, and if it's preloaded with an -EFAULT by user-space,
+ * the error will hopefully be detected.
+ * Also if copying fails, user-space will be unable to signal the fence
+ * object so we wait for it immediately, and then unreference the
+ * user-space reference.
+ */
+void
+vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
+ struct vmw_fpriv *vmw_fp,
+ int ret,
+ struct drm_vmw_fence_rep __user *user_fence_rep,
+ struct vmw_fence_obj *fence,
+ uint32_t fence_handle)
+{
+ struct drm_vmw_fence_rep fence_rep;
+
+ if (user_fence_rep == NULL)
+ return;
+
+ memset(&fence_rep, 0, sizeof(fence_rep));
+
+ fence_rep.error = ret;
+ if (ret == 0) {
+ BUG_ON(fence == NULL);
+
+ fence_rep.handle = fence_handle;
+ fence_rep.seqno = fence->seqno;
+ vmw_update_seqno(dev_priv, &dev_priv->fifo);
+ fence_rep.passed_seqno = dev_priv->last_read_seqno;
}
+ /*
+ * copy_to_user errors will be detected by user space not
+ * seeing fence_rep::error filled in. Typically
+ * user-space would have pre-set that member to -EFAULT.
+ */
+ ret = copy_to_user(user_fence_rep, &fence_rep,
+ sizeof(fence_rep));
+
+ /*
+ * User-space lost the fence object. We need to sync
+ * and unreference the handle.
+ */
+ if (unlikely(ret != 0) && (fence_rep.error == 0)) {
+ ttm_ref_object_base_unref(vmw_fp->tfile,
+ fence_handle, TTM_REF_USAGE);
+ DRM_ERROR("Fence copy error. Syncing.\n");
+ (void) vmw_fence_obj_wait(fence, fence->signal_mask,
+ false, false,
+ VMW_FENCE_WAIT_TIMEOUT);
+ }
+}
+
+int vmw_execbuf_process(struct drm_file *file_priv,
+ struct vmw_private *dev_priv,
+ void __user *user_commands,
+ void *kernel_commands,
+ uint32_t command_size,
+ uint64_t throttle_us,
+ struct drm_vmw_fence_rep __user *user_fence_rep)
+{
+ struct vmw_sw_context *sw_context = &dev_priv->ctx;
+ struct vmw_fence_obj *fence;
+ uint32_t handle;
+ void *cmd;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex);
+ if (unlikely(ret != 0))
+ return -ERESTARTSYS;
+
+ if (kernel_commands == NULL) {
+ sw_context->kernel = false;
+
+ ret = vmw_resize_cmd_bounce(sw_context, command_size);
+ if (unlikely(ret != 0))
+ goto out_unlock;
+
+
+ ret = copy_from_user(sw_context->cmd_bounce,
+ user_commands, command_size);
+
+ if (unlikely(ret != 0)) {
+ ret = -EFAULT;
+ DRM_ERROR("Failed copying commands.\n");
+ goto out_unlock;
+ }
+ kernel_commands = sw_context->cmd_bounce;
+ } else
+ sw_context->kernel = true;
+
sw_context->tfile = vmw_fpriv(file_priv)->tfile;
sw_context->cid_valid = false;
sw_context->sid_valid = false;
sw_context->cur_reloc = 0;
sw_context->cur_val_buf = 0;
+ sw_context->fence_flags = 0;
+ INIT_LIST_HEAD(&sw_context->query_list);
+ INIT_LIST_HEAD(&sw_context->resource_list);
+ sw_context->cur_query_bo = dev_priv->pinned_bo;
+ sw_context->cur_query_cid = dev_priv->query_cid;
+ sw_context->query_cid_valid = (dev_priv->pinned_bo != NULL);
INIT_LIST_HEAD(&sw_context->validate_nodes);
- ret = vmw_cmd_check_all(dev_priv, sw_context, cmd, arg->command_size);
+ ret = vmw_cmd_check_all(dev_priv, sw_context, kernel_commands,
+ command_size);
if (unlikely(ret != 0))
goto out_err;
+
ret = ttm_eu_reserve_buffers(&sw_context->validate_nodes);
if (unlikely(ret != 0))
goto out_err;
@@ -663,57 +1170,206 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
vmw_apply_relocations(sw_context);
- if (arg->throttle_us) {
- ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.fence_queue,
- arg->throttle_us);
+ if (throttle_us) {
+ ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.marker_queue,
+ throttle_us);
if (unlikely(ret != 0))
- goto out_err;
+ goto out_throttle;
}
- vmw_fifo_commit(dev_priv, arg->command_size);
-
- ret = vmw_fifo_send_fence(dev_priv, &sequence);
+ cmd = vmw_fifo_reserve(dev_priv, command_size);
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("Failed reserving fifo space for commands.\n");
+ ret = -ENOMEM;
+ goto out_throttle;
+ }
- ttm_eu_fence_buffer_objects(&sw_context->validate_nodes,
- (void *)(unsigned long) sequence);
- vmw_clear_validations(sw_context);
- mutex_unlock(&dev_priv->cmdbuf_mutex);
+ memcpy(cmd, kernel_commands, command_size);
+ vmw_fifo_commit(dev_priv, command_size);
+ vmw_query_bo_switch_commit(dev_priv, sw_context);
+ ret = vmw_execbuf_fence_commands(file_priv, dev_priv,
+ &fence,
+ (user_fence_rep) ? &handle : NULL);
/*
* This error is harmless, because if fence submission fails,
- * vmw_fifo_send_fence will sync.
+ * vmw_fifo_send_fence will sync. The error will be propagated to
+ * user-space in @fence_rep
*/
if (ret != 0)
DRM_ERROR("Fence submission error. Syncing.\n");
- fence_rep.error = ret;
- fence_rep.fence_seq = (uint64_t) sequence;
- fence_rep.pad64 = 0;
-
- user_fence_rep = (struct drm_vmw_fence_rep __user *)
- (unsigned long)arg->fence_rep;
+ ttm_eu_fence_buffer_objects(&sw_context->validate_nodes,
+ (void *) fence);
- /*
- * copy_to_user errors will be detected by user space not
- * seeing fence_rep::error filled in.
- */
+ vmw_clear_validations(sw_context);
+ vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret,
+ user_fence_rep, fence, handle);
- ret = copy_to_user(user_fence_rep, &fence_rep, sizeof(fence_rep));
+ if (likely(fence != NULL))
+ vmw_fence_obj_unreference(&fence);
- vmw_kms_cursor_post_execbuf(dev_priv);
- ttm_read_unlock(&vmaster->lock);
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
return 0;
+
out_err:
vmw_free_relocations(sw_context);
+out_throttle:
+ vmw_query_switch_backoff(sw_context);
ttm_eu_backoff_reservation(&sw_context->validate_nodes);
vmw_clear_validations(sw_context);
-out_commit:
- vmw_fifo_commit(dev_priv, 0);
out_unlock:
mutex_unlock(&dev_priv->cmdbuf_mutex);
-out_no_cmd_mutex:
+ return ret;
+}
+
+/**
+ * vmw_execbuf_unpin_panic - Idle the fifo and unpin the query buffer.
+ *
+ * @dev_priv: The device private structure.
+ *
+ * This function is called to idle the fifo and unpin the query buffer
+ * if the normal way to do this hits an error, which should typically be
+ * extremely rare.
+ */
+static void vmw_execbuf_unpin_panic(struct vmw_private *dev_priv)
+{
+ DRM_ERROR("Can't unpin query buffer. Trying to recover.\n");
+
+ (void) vmw_fallback_wait(dev_priv, false, true, 0, false, 10*HZ);
+ vmw_bo_pin(dev_priv->pinned_bo, false);
+ vmw_bo_pin(dev_priv->dummy_query_bo, false);
+ dev_priv->dummy_query_bo_pinned = false;
+}
+
+
+/**
+ * vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned
+ * query bo.
+ *
+ * @dev_priv: The device private structure.
+ * @only_on_cid_match: Only flush and unpin if the current active query cid
+ * matches @cid.
+ * @cid: Optional context id to match.
+ *
+ * This function should be used to unpin the pinned query bo, or
+ * as a query barrier when we need to make sure that all queries have
+ * finished before the next fifo command. (For example on hardware
+ * context destructions where the hardware may otherwise leak unfinished
+ * queries).
+ *
+ * This function does not return any failure codes, but make attempts
+ * to do safe unpinning in case of errors.
+ *
+ * The function will synchronize on the previous query barrier, and will
+ * thus not finish until that barrier has executed.
+ */
+void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv,
+ bool only_on_cid_match, uint32_t cid)
+{
+ int ret = 0;
+ struct list_head validate_list;
+ struct ttm_validate_buffer pinned_val, query_val;
+ struct vmw_fence_obj *fence;
+
+ mutex_lock(&dev_priv->cmdbuf_mutex);
+
+ if (dev_priv->pinned_bo == NULL)
+ goto out_unlock;
+
+ if (only_on_cid_match && cid != dev_priv->query_cid)
+ goto out_unlock;
+
+ INIT_LIST_HEAD(&validate_list);
+
+ pinned_val.new_sync_obj_arg = (void *)(unsigned long)
+ DRM_VMW_FENCE_FLAG_EXEC;
+ pinned_val.bo = ttm_bo_reference(dev_priv->pinned_bo);
+ list_add_tail(&pinned_val.head, &validate_list);
+
+ query_val.new_sync_obj_arg = pinned_val.new_sync_obj_arg;
+ query_val.bo = ttm_bo_reference(dev_priv->dummy_query_bo);
+ list_add_tail(&query_val.head, &validate_list);
+
+ do {
+ ret = ttm_eu_reserve_buffers(&validate_list);
+ } while (ret == -ERESTARTSYS);
+
+ if (unlikely(ret != 0)) {
+ vmw_execbuf_unpin_panic(dev_priv);
+ goto out_no_reserve;
+ }
+
+ ret = vmw_fifo_emit_dummy_query(dev_priv, dev_priv->query_cid);
+ if (unlikely(ret != 0)) {
+ vmw_execbuf_unpin_panic(dev_priv);
+ goto out_no_emit;
+ }
+
+ vmw_bo_pin(dev_priv->pinned_bo, false);
+ vmw_bo_pin(dev_priv->dummy_query_bo, false);
+ dev_priv->dummy_query_bo_pinned = false;
+
+ (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL);
+ ttm_eu_fence_buffer_objects(&validate_list, (void *) fence);
+
+ ttm_bo_unref(&query_val.bo);
+ ttm_bo_unref(&pinned_val.bo);
+ ttm_bo_unref(&dev_priv->pinned_bo);
+
+out_unlock:
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
+ return;
+
+out_no_emit:
+ ttm_eu_backoff_reservation(&validate_list);
+out_no_reserve:
+ ttm_bo_unref(&query_val.bo);
+ ttm_bo_unref(&pinned_val.bo);
+ ttm_bo_unref(&dev_priv->pinned_bo);
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
+}
+
+
+int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data;
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
+ int ret;
+
+ /*
+ * This will allow us to extend the ioctl argument while
+ * maintaining backwards compatibility:
+ * We take different code paths depending on the value of
+ * arg->version.
+ */
+
+ if (unlikely(arg->version != DRM_VMW_EXECBUF_VERSION)) {
+ DRM_ERROR("Incorrect execbuf version.\n");
+ DRM_ERROR("You're running outdated experimental "
+ "vmwgfx user-space drivers.");
+ return -EINVAL;
+ }
+
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (unlikely(ret != 0))
+ return ret;
+
+ ret = vmw_execbuf_process(file_priv, dev_priv,
+ (void __user *)(unsigned long)arg->commands,
+ NULL, arg->command_size, arg->throttle_us,
+ (void __user *)(unsigned long)arg->fence_rep);
+
+ if (unlikely(ret != 0))
+ goto out_unlock;
+
+ vmw_kms_cursor_post_execbuf(dev_priv);
+
+out_unlock:
ttm_read_unlock(&vmaster->lock);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index bfab60c938ac..34e51a1695b8 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -26,6 +26,8 @@
*
**************************************************************************/
+#include <linux/export.h>
+
#include "drmP.h"
#include "vmwgfx_drv.h"
@@ -158,10 +160,14 @@ static int vmw_fb_set_par(struct fb_info *info)
{
struct vmw_fb_par *par = info->par;
struct vmw_private *vmw_priv = par->vmw_priv;
+ int ret;
+
+ ret = vmw_kms_write_svga(vmw_priv, info->var.xres, info->var.yres,
+ info->fix.line_length,
+ par->bpp, par->depth);
+ if (ret)
+ return ret;
- vmw_kms_write_svga(vmw_priv, info->var.xres, info->var.yres,
- info->fix.line_length,
- par->bpp, par->depth);
if (vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY) {
/* TODO check if pitch and offset changes */
vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
@@ -405,14 +411,14 @@ int vmw_fb_init(struct vmw_private *vmw_priv)
struct fb_info *info;
unsigned initial_width, initial_height;
unsigned fb_width, fb_height;
- unsigned fb_bbp, fb_depth, fb_offset, fb_pitch, fb_size;
+ unsigned fb_bpp, fb_depth, fb_offset, fb_pitch, fb_size;
int ret;
/* XXX These shouldn't be hardcoded. */
initial_width = 800;
initial_height = 600;
- fb_bbp = 32;
+ fb_bpp = 32;
fb_depth = 24;
/* XXX As shouldn't these be as well. */
@@ -422,7 +428,7 @@ int vmw_fb_init(struct vmw_private *vmw_priv)
initial_width = min(fb_width, initial_width);
initial_height = min(fb_height, initial_height);
- fb_pitch = fb_width * fb_bbp / 8;
+ fb_pitch = fb_width * fb_bpp / 8;
fb_size = fb_pitch * fb_height;
fb_offset = vmw_read(vmw_priv, SVGA_REG_FB_OFFSET);
@@ -437,7 +443,7 @@ int vmw_fb_init(struct vmw_private *vmw_priv)
par = info->par;
par->vmw_priv = vmw_priv;
par->depth = fb_depth;
- par->bpp = fb_bbp;
+ par->bpp = fb_bpp;
par->vmalloc = NULL;
par->max_width = fb_width;
par->max_height = fb_height;
@@ -588,58 +594,6 @@ int vmw_fb_close(struct vmw_private *vmw_priv)
return 0;
}
-int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv,
- struct vmw_dma_buffer *vmw_bo)
-{
- struct ttm_buffer_object *bo = &vmw_bo->base;
- int ret = 0;
-
- ret = ttm_bo_reserve(bo, false, false, false, 0);
- if (unlikely(ret != 0))
- return ret;
-
- ret = ttm_bo_validate(bo, &vmw_sys_placement, false, false, false);
- ttm_bo_unreserve(bo);
-
- return ret;
-}
-
-int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
- struct vmw_dma_buffer *vmw_bo)
-{
- struct ttm_buffer_object *bo = &vmw_bo->base;
- struct ttm_placement ne_placement = vmw_vram_ne_placement;
- int ret = 0;
-
- ne_placement.lpfn = bo->num_pages;
-
- /* interuptable? */
- ret = ttm_write_lock(&vmw_priv->active_master->lock, false);
- if (unlikely(ret != 0))
- return ret;
-
- ret = ttm_bo_reserve(bo, false, false, false, 0);
- if (unlikely(ret != 0))
- goto err_unlock;
-
- if (bo->mem.mem_type == TTM_PL_VRAM &&
- bo->mem.start < bo->num_pages &&
- bo->mem.start > 0)
- (void) ttm_bo_validate(bo, &vmw_sys_placement, false,
- false, false);
-
- ret = ttm_bo_validate(bo, &ne_placement, false, false, false);
-
- /* Could probably bug on */
- WARN_ON(bo->offset != 0);
-
- ttm_bo_unreserve(bo);
-err_unlock:
- ttm_write_unlock(&vmw_priv->active_master->lock);
-
- return ret;
-}
-
int vmw_fb_off(struct vmw_private *vmw_priv)
{
struct fb_info *info;
@@ -661,7 +615,7 @@ int vmw_fb_off(struct vmw_private *vmw_priv)
par->bo_ptr = NULL;
ttm_bo_kunmap(&par->map);
- vmw_dmabuf_from_vram(vmw_priv, par->vmw_bo);
+ vmw_dmabuf_unpin(vmw_priv, par->vmw_bo, false);
return 0;
}
@@ -687,7 +641,7 @@ int vmw_fb_on(struct vmw_private *vmw_priv)
/* Make sure that all overlays are stoped when we take over */
vmw_overlay_stop_all(vmw_priv);
- ret = vmw_dmabuf_to_start_of_vram(vmw_priv, par->vmw_bo);
+ ret = vmw_dmabuf_to_start_of_vram(vmw_priv, par->vmw_bo, true, false);
if (unlikely(ret != 0)) {
DRM_ERROR("could not move buffer to start of VRAM\n");
goto err_no_buffer;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index 61eacc1b5ca3..15fb26088d68 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -1,6 +1,6 @@
/**************************************************************************
*
- * Copyright (C) 2010 VMware, Inc., Palo Alto, CA., USA
+ * Copyright © 2011 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -25,149 +25,1100 @@
*
**************************************************************************/
-
+#include "drmP.h"
#include "vmwgfx_drv.h"
-struct vmw_fence {
- struct list_head head;
- uint32_t sequence;
- struct timespec submitted;
+#define VMW_FENCE_WRAP (1 << 31)
+
+struct vmw_fence_manager {
+ int num_fence_objects;
+ struct vmw_private *dev_priv;
+ spinlock_t lock;
+ struct list_head fence_list;
+ struct work_struct work;
+ u32 user_fence_size;
+ u32 fence_size;
+ u32 event_fence_action_size;
+ bool fifo_down;
+ struct list_head cleanup_list;
+ uint32_t pending_actions[VMW_ACTION_MAX];
+ struct mutex goal_irq_mutex;
+ bool goal_irq_on; /* Protected by @goal_irq_mutex */
+ bool seqno_valid; /* Protected by @lock, and may not be set to true
+ without the @goal_irq_mutex held. */
};
-void vmw_fence_queue_init(struct vmw_fence_queue *queue)
+struct vmw_user_fence {
+ struct ttm_base_object base;
+ struct vmw_fence_obj fence;
+};
+
+/**
+ * struct vmw_event_fence_action - fence action that delivers a drm event.
+ *
+ * @e: A struct drm_pending_event that controls the event delivery.
+ * @action: A struct vmw_fence_action to hook up to a fence.
+ * @fence: A referenced pointer to the fence to keep it alive while @action
+ * hangs on it.
+ * @dev: Pointer to a struct drm_device so we can access the event stuff.
+ * @kref: Both @e and @action has destructors, so we need to refcount.
+ * @size: Size accounted for this object.
+ * @tv_sec: If non-null, the variable pointed to will be assigned
+ * current time tv_sec val when the fence signals.
+ * @tv_usec: Must be set if @tv_sec is set, and the variable pointed to will
+ * be assigned the current time tv_usec val when the fence signals.
+ */
+struct vmw_event_fence_action {
+ struct drm_pending_event e;
+ struct vmw_fence_action action;
+ struct vmw_fence_obj *fence;
+ struct drm_device *dev;
+ struct kref kref;
+ uint32_t size;
+ uint32_t *tv_sec;
+ uint32_t *tv_usec;
+};
+
+/**
+ * Note on fencing subsystem usage of irqs:
+ * Typically the vmw_fences_update function is called
+ *
+ * a) When a new fence seqno has been submitted by the fifo code.
+ * b) On-demand when we have waiters. Sleeping waiters will switch on the
+ * ANY_FENCE irq and call vmw_fences_update function each time an ANY_FENCE
+ * irq is received. When the last fence waiter is gone, that IRQ is masked
+ * away.
+ *
+ * In situations where there are no waiters and we don't submit any new fences,
+ * fence objects may not be signaled. This is perfectly OK, since there are
+ * no consumers of the signaled data, but that is NOT ok when there are fence
+ * actions attached to a fence. The fencing subsystem then makes use of the
+ * FENCE_GOAL irq and sets the fence goal seqno to that of the next fence
+ * which has an action attached, and each time vmw_fences_update is called,
+ * the subsystem makes sure the fence goal seqno is updated.
+ *
+ * The fence goal seqno irq is on as long as there are unsignaled fence
+ * objects with actions attached to them.
+ */
+
+static void vmw_fence_obj_destroy_locked(struct kref *kref)
+{
+ struct vmw_fence_obj *fence =
+ container_of(kref, struct vmw_fence_obj, kref);
+
+ struct vmw_fence_manager *fman = fence->fman;
+ unsigned int num_fences;
+
+ list_del_init(&fence->head);
+ num_fences = --fman->num_fence_objects;
+ spin_unlock_irq(&fman->lock);
+ if (fence->destroy)
+ fence->destroy(fence);
+ else
+ kfree(fence);
+
+ spin_lock_irq(&fman->lock);
+}
+
+
+/**
+ * Execute signal actions on fences recently signaled.
+ * This is done from a workqueue so we don't have to execute
+ * signal actions from atomic context.
+ */
+
+static void vmw_fence_work_func(struct work_struct *work)
{
- INIT_LIST_HEAD(&queue->head);
- queue->lag = ns_to_timespec(0);
- getrawmonotonic(&queue->lag_time);
- spin_lock_init(&queue->lock);
+ struct vmw_fence_manager *fman =
+ container_of(work, struct vmw_fence_manager, work);
+ struct list_head list;
+ struct vmw_fence_action *action, *next_action;
+ bool seqno_valid;
+
+ do {
+ INIT_LIST_HEAD(&list);
+ mutex_lock(&fman->goal_irq_mutex);
+
+ spin_lock_irq(&fman->lock);
+ list_splice_init(&fman->cleanup_list, &list);
+ seqno_valid = fman->seqno_valid;
+ spin_unlock_irq(&fman->lock);
+
+ if (!seqno_valid && fman->goal_irq_on) {
+ fman->goal_irq_on = false;
+ vmw_goal_waiter_remove(fman->dev_priv);
+ }
+ mutex_unlock(&fman->goal_irq_mutex);
+
+ if (list_empty(&list))
+ return;
+
+ /*
+ * At this point, only we should be able to manipulate the
+ * list heads of the actions we have on the private list.
+ * hence fman::lock not held.
+ */
+
+ list_for_each_entry_safe(action, next_action, &list, head) {
+ list_del_init(&action->head);
+ if (action->cleanup)
+ action->cleanup(action);
+ }
+ } while (1);
}
-void vmw_fence_queue_takedown(struct vmw_fence_queue *queue)
+struct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv)
{
- struct vmw_fence *fence, *next;
+ struct vmw_fence_manager *fman = kzalloc(sizeof(*fman), GFP_KERNEL);
- spin_lock(&queue->lock);
- list_for_each_entry_safe(fence, next, &queue->head, head) {
- kfree(fence);
- }
- spin_unlock(&queue->lock);
+ if (unlikely(fman == NULL))
+ return NULL;
+
+ fman->dev_priv = dev_priv;
+ spin_lock_init(&fman->lock);
+ INIT_LIST_HEAD(&fman->fence_list);
+ INIT_LIST_HEAD(&fman->cleanup_list);
+ INIT_WORK(&fman->work, &vmw_fence_work_func);
+ fman->fifo_down = true;
+ fman->user_fence_size = ttm_round_pot(sizeof(struct vmw_user_fence));
+ fman->fence_size = ttm_round_pot(sizeof(struct vmw_fence_obj));
+ fman->event_fence_action_size =
+ ttm_round_pot(sizeof(struct vmw_event_fence_action));
+ mutex_init(&fman->goal_irq_mutex);
+
+ return fman;
}
-int vmw_fence_push(struct vmw_fence_queue *queue,
- uint32_t sequence)
+void vmw_fence_manager_takedown(struct vmw_fence_manager *fman)
{
- struct vmw_fence *fence = kmalloc(sizeof(*fence), GFP_KERNEL);
+ unsigned long irq_flags;
+ bool lists_empty;
- if (unlikely(!fence))
- return -ENOMEM;
+ (void) cancel_work_sync(&fman->work);
- fence->sequence = sequence;
- getrawmonotonic(&fence->submitted);
- spin_lock(&queue->lock);
- list_add_tail(&fence->head, &queue->head);
- spin_unlock(&queue->lock);
+ spin_lock_irqsave(&fman->lock, irq_flags);
+ lists_empty = list_empty(&fman->fence_list) &&
+ list_empty(&fman->cleanup_list);
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
- return 0;
+ BUG_ON(!lists_empty);
+ kfree(fman);
}
-int vmw_fence_pull(struct vmw_fence_queue *queue,
- uint32_t signaled_sequence)
+static int vmw_fence_obj_init(struct vmw_fence_manager *fman,
+ struct vmw_fence_obj *fence,
+ u32 seqno,
+ uint32_t mask,
+ void (*destroy) (struct vmw_fence_obj *fence))
{
- struct vmw_fence *fence, *next;
- struct timespec now;
- bool updated = false;
+ unsigned long irq_flags;
+ unsigned int num_fences;
+ int ret = 0;
- spin_lock(&queue->lock);
- getrawmonotonic(&now);
+ fence->seqno = seqno;
+ INIT_LIST_HEAD(&fence->seq_passed_actions);
+ fence->fman = fman;
+ fence->signaled = 0;
+ fence->signal_mask = mask;
+ kref_init(&fence->kref);
+ fence->destroy = destroy;
+ init_waitqueue_head(&fence->queue);
- if (list_empty(&queue->head)) {
- queue->lag = ns_to_timespec(0);
- queue->lag_time = now;
- updated = true;
+ spin_lock_irqsave(&fman->lock, irq_flags);
+ if (unlikely(fman->fifo_down)) {
+ ret = -EBUSY;
goto out_unlock;
}
+ list_add_tail(&fence->head, &fman->fence_list);
+ num_fences = ++fman->num_fence_objects;
- list_for_each_entry_safe(fence, next, &queue->head, head) {
- if (signaled_sequence - fence->sequence > (1 << 30))
- continue;
+out_unlock:
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
+ return ret;
- queue->lag = timespec_sub(now, fence->submitted);
- queue->lag_time = now;
- updated = true;
- list_del(&fence->head);
- kfree(fence);
+}
+
+struct vmw_fence_obj *vmw_fence_obj_reference(struct vmw_fence_obj *fence)
+{
+ if (unlikely(fence == NULL))
+ return NULL;
+
+ kref_get(&fence->kref);
+ return fence;
+}
+
+/**
+ * vmw_fence_obj_unreference
+ *
+ * Note that this function may not be entered with disabled irqs since
+ * it may re-enable them in the destroy function.
+ *
+ */
+void vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p)
+{
+ struct vmw_fence_obj *fence = *fence_p;
+ struct vmw_fence_manager *fman;
+
+ if (unlikely(fence == NULL))
+ return;
+
+ fman = fence->fman;
+ *fence_p = NULL;
+ spin_lock_irq(&fman->lock);
+ BUG_ON(atomic_read(&fence->kref.refcount) == 0);
+ kref_put(&fence->kref, vmw_fence_obj_destroy_locked);
+ spin_unlock_irq(&fman->lock);
+}
+
+void vmw_fences_perform_actions(struct vmw_fence_manager *fman,
+ struct list_head *list)
+{
+ struct vmw_fence_action *action, *next_action;
+
+ list_for_each_entry_safe(action, next_action, list, head) {
+ list_del_init(&action->head);
+ fman->pending_actions[action->type]--;
+ if (action->seq_passed != NULL)
+ action->seq_passed(action);
+
+ /*
+ * Add the cleanup action to the cleanup list so that
+ * it will be performed by a worker task.
+ */
+
+ list_add_tail(&action->head, &fman->cleanup_list);
}
+}
+
+/**
+ * vmw_fence_goal_new_locked - Figure out a new device fence goal
+ * seqno if needed.
+ *
+ * @fman: Pointer to a fence manager.
+ * @passed_seqno: The seqno the device currently signals as passed.
+ *
+ * This function should be called with the fence manager lock held.
+ * It is typically called when we have a new passed_seqno, and
+ * we might need to update the fence goal. It checks to see whether
+ * the current fence goal has already passed, and, in that case,
+ * scans through all unsignaled fences to get the next fence object with an
+ * action attached, and sets the seqno of that fence as a new fence goal.
+ *
+ * returns true if the device goal seqno was updated. False otherwise.
+ */
+static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman,
+ u32 passed_seqno)
+{
+ u32 goal_seqno;
+ __le32 __iomem *fifo_mem;
+ struct vmw_fence_obj *fence;
+
+ if (likely(!fman->seqno_valid))
+ return false;
+
+ fifo_mem = fman->dev_priv->mmio_virt;
+ goal_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE_GOAL);
+ if (likely(passed_seqno - goal_seqno >= VMW_FENCE_WRAP))
+ return false;
+
+ fman->seqno_valid = false;
+ list_for_each_entry(fence, &fman->fence_list, head) {
+ if (!list_empty(&fence->seq_passed_actions)) {
+ fman->seqno_valid = true;
+ iowrite32(fence->seqno,
+ fifo_mem + SVGA_FIFO_FENCE_GOAL);
+ break;
+ }
+ }
+
+ return true;
+}
-out_unlock:
- spin_unlock(&queue->lock);
- return (updated) ? 0 : -EBUSY;
+/**
+ * vmw_fence_goal_check_locked - Replace the device fence goal seqno if
+ * needed.
+ *
+ * @fence: Pointer to a struct vmw_fence_obj the seqno of which should be
+ * considered as a device fence goal.
+ *
+ * This function should be called with the fence manager lock held.
+ * It is typically called when an action has been attached to a fence to
+ * check whether the seqno of that fence should be used for a fence
+ * goal interrupt. This is typically needed if the current fence goal is
+ * invalid, or has a higher seqno than that of the current fence object.
+ *
+ * returns true if the device goal seqno was updated. False otherwise.
+ */
+static bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence)
+{
+ u32 goal_seqno;
+ __le32 __iomem *fifo_mem;
+
+ if (fence->signaled & DRM_VMW_FENCE_FLAG_EXEC)
+ return false;
+
+ fifo_mem = fence->fman->dev_priv->mmio_virt;
+ goal_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE_GOAL);
+ if (likely(fence->fman->seqno_valid &&
+ goal_seqno - fence->seqno < VMW_FENCE_WRAP))
+ return false;
+
+ iowrite32(fence->seqno, fifo_mem + SVGA_FIFO_FENCE_GOAL);
+ fence->fman->seqno_valid = true;
+
+ return true;
}
-static struct timespec vmw_timespec_add(struct timespec t1,
- struct timespec t2)
+void vmw_fences_update(struct vmw_fence_manager *fman)
{
- t1.tv_sec += t2.tv_sec;
- t1.tv_nsec += t2.tv_nsec;
- if (t1.tv_nsec >= 1000000000L) {
- t1.tv_sec += 1;
- t1.tv_nsec -= 1000000000L;
+ unsigned long flags;
+ struct vmw_fence_obj *fence, *next_fence;
+ struct list_head action_list;
+ bool needs_rerun;
+ uint32_t seqno, new_seqno;
+ __le32 __iomem *fifo_mem = fman->dev_priv->mmio_virt;
+
+ seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE);
+rerun:
+ spin_lock_irqsave(&fman->lock, flags);
+ list_for_each_entry_safe(fence, next_fence, &fman->fence_list, head) {
+ if (seqno - fence->seqno < VMW_FENCE_WRAP) {
+ list_del_init(&fence->head);
+ fence->signaled |= DRM_VMW_FENCE_FLAG_EXEC;
+ INIT_LIST_HEAD(&action_list);
+ list_splice_init(&fence->seq_passed_actions,
+ &action_list);
+ vmw_fences_perform_actions(fman, &action_list);
+ wake_up_all(&fence->queue);
+ } else
+ break;
}
- return t1;
+ needs_rerun = vmw_fence_goal_new_locked(fman, seqno);
+
+ if (!list_empty(&fman->cleanup_list))
+ (void) schedule_work(&fman->work);
+ spin_unlock_irqrestore(&fman->lock, flags);
+
+ /*
+ * Rerun if the fence goal seqno was updated, and the
+ * hardware might have raced with that update, so that
+ * we missed a fence_goal irq.
+ */
+
+ if (unlikely(needs_rerun)) {
+ new_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE);
+ if (new_seqno != seqno) {
+ seqno = new_seqno;
+ goto rerun;
+ }
+ }
+}
+
+bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence,
+ uint32_t flags)
+{
+ struct vmw_fence_manager *fman = fence->fman;
+ unsigned long irq_flags;
+ uint32_t signaled;
+
+ spin_lock_irqsave(&fman->lock, irq_flags);
+ signaled = fence->signaled;
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
+
+ flags &= fence->signal_mask;
+ if ((signaled & flags) == flags)
+ return 1;
+
+ if ((signaled & DRM_VMW_FENCE_FLAG_EXEC) == 0)
+ vmw_fences_update(fman);
+
+ spin_lock_irqsave(&fman->lock, irq_flags);
+ signaled = fence->signaled;
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
+
+ return ((signaled & flags) == flags);
}
-static struct timespec vmw_fifo_lag(struct vmw_fence_queue *queue)
+int vmw_fence_obj_wait(struct vmw_fence_obj *fence,
+ uint32_t flags, bool lazy,
+ bool interruptible, unsigned long timeout)
{
- struct timespec now;
+ struct vmw_private *dev_priv = fence->fman->dev_priv;
+ long ret;
+
+ if (likely(vmw_fence_obj_signaled(fence, flags)))
+ return 0;
+
+ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
+ vmw_seqno_waiter_add(dev_priv);
+
+ if (interruptible)
+ ret = wait_event_interruptible_timeout
+ (fence->queue,
+ vmw_fence_obj_signaled(fence, flags),
+ timeout);
+ else
+ ret = wait_event_timeout
+ (fence->queue,
+ vmw_fence_obj_signaled(fence, flags),
+ timeout);
+
+ vmw_seqno_waiter_remove(dev_priv);
+
+ if (unlikely(ret == 0))
+ ret = -EBUSY;
+ else if (likely(ret > 0))
+ ret = 0;
+
+ return ret;
+}
+
+void vmw_fence_obj_flush(struct vmw_fence_obj *fence)
+{
+ struct vmw_private *dev_priv = fence->fman->dev_priv;
+
+ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
+}
+
+static void vmw_fence_destroy(struct vmw_fence_obj *fence)
+{
+ struct vmw_fence_manager *fman = fence->fman;
+
+ kfree(fence);
+ /*
+ * Free kernel space accounting.
+ */
+ ttm_mem_global_free(vmw_mem_glob(fman->dev_priv),
+ fman->fence_size);
+}
+
+int vmw_fence_create(struct vmw_fence_manager *fman,
+ uint32_t seqno,
+ uint32_t mask,
+ struct vmw_fence_obj **p_fence)
+{
+ struct ttm_mem_global *mem_glob = vmw_mem_glob(fman->dev_priv);
+ struct vmw_fence_obj *fence;
+ int ret;
+
+ ret = ttm_mem_global_alloc(mem_glob, fman->fence_size,
+ false, false);
+ if (unlikely(ret != 0))
+ return ret;
+
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+ if (unlikely(fence == NULL)) {
+ ret = -ENOMEM;
+ goto out_no_object;
+ }
+
+ ret = vmw_fence_obj_init(fman, fence, seqno, mask,
+ vmw_fence_destroy);
+ if (unlikely(ret != 0))
+ goto out_err_init;
+
+ *p_fence = fence;
+ return 0;
+
+out_err_init:
+ kfree(fence);
+out_no_object:
+ ttm_mem_global_free(mem_glob, fman->fence_size);
+ return ret;
+}
+
+
+static void vmw_user_fence_destroy(struct vmw_fence_obj *fence)
+{
+ struct vmw_user_fence *ufence =
+ container_of(fence, struct vmw_user_fence, fence);
+ struct vmw_fence_manager *fman = fence->fman;
+
+ kfree(ufence);
+ /*
+ * Free kernel space accounting.
+ */
+ ttm_mem_global_free(vmw_mem_glob(fman->dev_priv),
+ fman->user_fence_size);
+}
+
+static void vmw_user_fence_base_release(struct ttm_base_object **p_base)
+{
+ struct ttm_base_object *base = *p_base;
+ struct vmw_user_fence *ufence =
+ container_of(base, struct vmw_user_fence, base);
+ struct vmw_fence_obj *fence = &ufence->fence;
+
+ *p_base = NULL;
+ vmw_fence_obj_unreference(&fence);
+}
+
+int vmw_user_fence_create(struct drm_file *file_priv,
+ struct vmw_fence_manager *fman,
+ uint32_t seqno,
+ uint32_t mask,
+ struct vmw_fence_obj **p_fence,
+ uint32_t *p_handle)
+{
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_user_fence *ufence;
+ struct vmw_fence_obj *tmp;
+ struct ttm_mem_global *mem_glob = vmw_mem_glob(fman->dev_priv);
+ int ret;
+
+ /*
+ * Kernel memory space accounting, since this object may
+ * be created by a user-space request.
+ */
+
+ ret = ttm_mem_global_alloc(mem_glob, fman->user_fence_size,
+ false, false);
+ if (unlikely(ret != 0))
+ return ret;
+
+ ufence = kzalloc(sizeof(*ufence), GFP_KERNEL);
+ if (unlikely(ufence == NULL)) {
+ ret = -ENOMEM;
+ goto out_no_object;
+ }
+
+ ret = vmw_fence_obj_init(fman, &ufence->fence, seqno,
+ mask, vmw_user_fence_destroy);
+ if (unlikely(ret != 0)) {
+ kfree(ufence);
+ goto out_no_object;
+ }
+
+ /*
+ * The base object holds a reference which is freed in
+ * vmw_user_fence_base_release.
+ */
+ tmp = vmw_fence_obj_reference(&ufence->fence);
+ ret = ttm_base_object_init(tfile, &ufence->base, false,
+ VMW_RES_FENCE,
+ &vmw_user_fence_base_release, NULL);
- spin_lock(&queue->lock);
- getrawmonotonic(&now);
- queue->lag = vmw_timespec_add(queue->lag,
- timespec_sub(now, queue->lag_time));
- queue->lag_time = now;
- spin_unlock(&queue->lock);
- return queue->lag;
+
+ if (unlikely(ret != 0)) {
+ /*
+ * Free the base object's reference
+ */
+ vmw_fence_obj_unreference(&tmp);
+ goto out_err;
+ }
+
+ *p_fence = &ufence->fence;
+ *p_handle = ufence->base.hash.key;
+
+ return 0;
+out_err:
+ tmp = &ufence->fence;
+ vmw_fence_obj_unreference(&tmp);
+out_no_object:
+ ttm_mem_global_free(mem_glob, fman->user_fence_size);
+ return ret;
}
-static bool vmw_lag_lt(struct vmw_fence_queue *queue,
- uint32_t us)
+/**
+ * vmw_fence_fifo_down - signal all unsignaled fence objects.
+ */
+
+void vmw_fence_fifo_down(struct vmw_fence_manager *fman)
+{
+ unsigned long irq_flags;
+ struct list_head action_list;
+ int ret;
+
+ /*
+ * The list may be altered while we traverse it, so always
+ * restart when we've released the fman->lock.
+ */
+
+ spin_lock_irqsave(&fman->lock, irq_flags);
+ fman->fifo_down = true;
+ while (!list_empty(&fman->fence_list)) {
+ struct vmw_fence_obj *fence =
+ list_entry(fman->fence_list.prev, struct vmw_fence_obj,
+ head);
+ kref_get(&fence->kref);
+ spin_unlock_irq(&fman->lock);
+
+ ret = vmw_fence_obj_wait(fence, fence->signal_mask,
+ false, false,
+ VMW_FENCE_WAIT_TIMEOUT);
+
+ if (unlikely(ret != 0)) {
+ list_del_init(&fence->head);
+ fence->signaled |= DRM_VMW_FENCE_FLAG_EXEC;
+ INIT_LIST_HEAD(&action_list);
+ list_splice_init(&fence->seq_passed_actions,
+ &action_list);
+ vmw_fences_perform_actions(fman, &action_list);
+ wake_up_all(&fence->queue);
+ }
+
+ spin_lock_irq(&fman->lock);
+
+ BUG_ON(!list_empty(&fence->head));
+ kref_put(&fence->kref, vmw_fence_obj_destroy_locked);
+ }
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
+}
+
+void vmw_fence_fifo_up(struct vmw_fence_manager *fman)
{
- struct timespec lag, cond;
+ unsigned long irq_flags;
- cond = ns_to_timespec((s64) us * 1000);
- lag = vmw_fifo_lag(queue);
- return (timespec_compare(&lag, &cond) < 1);
+ spin_lock_irqsave(&fman->lock, irq_flags);
+ fman->fifo_down = false;
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
}
-int vmw_wait_lag(struct vmw_private *dev_priv,
- struct vmw_fence_queue *queue, uint32_t us)
+
+int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
- struct vmw_fence *fence;
- uint32_t sequence;
+ struct drm_vmw_fence_wait_arg *arg =
+ (struct drm_vmw_fence_wait_arg *)data;
+ unsigned long timeout;
+ struct ttm_base_object *base;
+ struct vmw_fence_obj *fence;
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
int ret;
+ uint64_t wait_timeout = ((uint64_t)arg->timeout_us * HZ);
+
+ /*
+ * 64-bit division not present on 32-bit systems, so do an
+ * approximation. (Divide by 1000000).
+ */
+
+ wait_timeout = (wait_timeout >> 20) + (wait_timeout >> 24) -
+ (wait_timeout >> 26);
+
+ if (!arg->cookie_valid) {
+ arg->cookie_valid = 1;
+ arg->kernel_cookie = jiffies + wait_timeout;
+ }
+
+ base = ttm_base_object_lookup(tfile, arg->handle);
+ if (unlikely(base == NULL)) {
+ printk(KERN_ERR "Wait invalid fence object handle "
+ "0x%08lx.\n",
+ (unsigned long)arg->handle);
+ return -EINVAL;
+ }
+
+ fence = &(container_of(base, struct vmw_user_fence, base)->fence);
+
+ timeout = jiffies;
+ if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie)) {
+ ret = ((vmw_fence_obj_signaled(fence, arg->flags)) ?
+ 0 : -EBUSY);
+ goto out;
+ }
+
+ timeout = (unsigned long)arg->kernel_cookie - timeout;
+
+ ret = vmw_fence_obj_wait(fence, arg->flags, arg->lazy, true, timeout);
+
+out:
+ ttm_base_object_unref(&base);
+
+ /*
+ * Optionally unref the fence object.
+ */
+
+ if (ret == 0 && (arg->wait_options & DRM_VMW_WAIT_OPTION_UNREF))
+ return ttm_ref_object_base_unref(tfile, arg->handle,
+ TTM_REF_USAGE);
+ return ret;
+}
+
+int vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_vmw_fence_signaled_arg *arg =
+ (struct drm_vmw_fence_signaled_arg *) data;
+ struct ttm_base_object *base;
+ struct vmw_fence_obj *fence;
+ struct vmw_fence_manager *fman;
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_private *dev_priv = vmw_priv(dev);
+
+ base = ttm_base_object_lookup(tfile, arg->handle);
+ if (unlikely(base == NULL)) {
+ printk(KERN_ERR "Fence signaled invalid fence object handle "
+ "0x%08lx.\n",
+ (unsigned long)arg->handle);
+ return -EINVAL;
+ }
+
+ fence = &(container_of(base, struct vmw_user_fence, base)->fence);
+ fman = fence->fman;
+
+ arg->signaled = vmw_fence_obj_signaled(fence, arg->flags);
+ spin_lock_irq(&fman->lock);
+
+ arg->signaled_flags = fence->signaled;
+ arg->passed_seqno = dev_priv->last_read_seqno;
+ spin_unlock_irq(&fman->lock);
+
+ ttm_base_object_unref(&base);
+
+ return 0;
+}
+
+
+int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_vmw_fence_arg *arg =
+ (struct drm_vmw_fence_arg *) data;
+
+ return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
+ arg->handle,
+ TTM_REF_USAGE);
+}
+
+/**
+ * vmw_event_fence_action_destroy
+ *
+ * @kref: The struct kref embedded in a struct vmw_event_fence_action.
+ *
+ * The vmw_event_fence_action destructor that may be called either after
+ * the fence action cleanup, or when the event is delivered.
+ * It frees both the vmw_event_fence_action struct and the actual
+ * event structure copied to user-space.
+ */
+static void vmw_event_fence_action_destroy(struct kref *kref)
+{
+ struct vmw_event_fence_action *eaction =
+ container_of(kref, struct vmw_event_fence_action, kref);
+ struct ttm_mem_global *mem_glob =
+ vmw_mem_glob(vmw_priv(eaction->dev));
+ uint32_t size = eaction->size;
+
+ kfree(eaction->e.event);
+ kfree(eaction);
+ ttm_mem_global_free(mem_glob, size);
+}
+
+
+/**
+ * vmw_event_fence_action_delivered
+ *
+ * @e: The struct drm_pending_event embedded in a struct
+ * vmw_event_fence_action.
+ *
+ * The struct drm_pending_event destructor that is called by drm
+ * once the event is delivered. Since we don't know whether this function
+ * will be called before or after the fence action destructor, we
+ * free a refcount and destroy if it becomes zero.
+ */
+static void vmw_event_fence_action_delivered(struct drm_pending_event *e)
+{
+ struct vmw_event_fence_action *eaction =
+ container_of(e, struct vmw_event_fence_action, e);
+
+ kref_put(&eaction->kref, vmw_event_fence_action_destroy);
+}
+
+
+/**
+ * vmw_event_fence_action_seq_passed
+ *
+ * @action: The struct vmw_fence_action embedded in a struct
+ * vmw_event_fence_action.
+ *
+ * This function is called when the seqno of the fence where @action is
+ * attached has passed. It queues the event on the submitter's event list.
+ * This function is always called from atomic context, and may be called
+ * from irq context. It ups a refcount reflecting that we now have two
+ * destructors.
+ */
+static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action)
+{
+ struct vmw_event_fence_action *eaction =
+ container_of(action, struct vmw_event_fence_action, action);
+ struct drm_device *dev = eaction->dev;
+ struct drm_file *file_priv = eaction->e.file_priv;
+ unsigned long irq_flags;
+
+ kref_get(&eaction->kref);
+ spin_lock_irqsave(&dev->event_lock, irq_flags);
+
+ if (likely(eaction->tv_sec != NULL)) {
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ *eaction->tv_sec = tv.tv_sec;
+ *eaction->tv_usec = tv.tv_usec;
+ }
+
+ list_add_tail(&eaction->e.link, &file_priv->event_list);
+ wake_up_all(&file_priv->event_wait);
+ spin_unlock_irqrestore(&dev->event_lock, irq_flags);
+}
+
+/**
+ * vmw_event_fence_action_cleanup
+ *
+ * @action: The struct vmw_fence_action embedded in a struct
+ * vmw_event_fence_action.
+ *
+ * This function is the struct vmw_fence_action destructor. It's typically
+ * called from a workqueue.
+ */
+static void vmw_event_fence_action_cleanup(struct vmw_fence_action *action)
+{
+ struct vmw_event_fence_action *eaction =
+ container_of(action, struct vmw_event_fence_action, action);
+
+ vmw_fence_obj_unreference(&eaction->fence);
+ kref_put(&eaction->kref, vmw_event_fence_action_destroy);
+}
+
+
+/**
+ * vmw_fence_obj_add_action - Add an action to a fence object.
+ *
+ * @fence - The fence object.
+ * @action - The action to add.
+ *
+ * Note that the action callbacks may be executed before this function
+ * returns.
+ */
+void vmw_fence_obj_add_action(struct vmw_fence_obj *fence,
+ struct vmw_fence_action *action)
+{
+ struct vmw_fence_manager *fman = fence->fman;
+ unsigned long irq_flags;
+ bool run_update = false;
+
+ mutex_lock(&fman->goal_irq_mutex);
+ spin_lock_irqsave(&fman->lock, irq_flags);
+
+ fman->pending_actions[action->type]++;
+ if (fence->signaled & DRM_VMW_FENCE_FLAG_EXEC) {
+ struct list_head action_list;
- while (!vmw_lag_lt(queue, us)) {
- spin_lock(&queue->lock);
- if (list_empty(&queue->head))
- sequence = atomic_read(&dev_priv->fence_seq);
- else {
- fence = list_first_entry(&queue->head,
- struct vmw_fence, head);
- sequence = fence->sequence;
+ INIT_LIST_HEAD(&action_list);
+ list_add_tail(&action->head, &action_list);
+ vmw_fences_perform_actions(fman, &action_list);
+ } else {
+ list_add_tail(&action->head, &fence->seq_passed_actions);
+
+ /*
+ * This function may set fman::seqno_valid, so it must
+ * be run with the goal_irq_mutex held.
+ */
+ run_update = vmw_fence_goal_check_locked(fence);
+ }
+
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
+
+ if (run_update) {
+ if (!fman->goal_irq_on) {
+ fman->goal_irq_on = true;
+ vmw_goal_waiter_add(fman->dev_priv);
}
- spin_unlock(&queue->lock);
+ vmw_fences_update(fman);
+ }
+ mutex_unlock(&fman->goal_irq_mutex);
- ret = vmw_wait_fence(dev_priv, false, sequence, true,
- 3*HZ);
+}
- if (unlikely(ret != 0))
- return ret;
+/**
+ * vmw_event_fence_action_create - Post an event for sending when a fence
+ * object seqno has passed.
+ *
+ * @file_priv: The file connection on which the event should be posted.
+ * @fence: The fence object on which to post the event.
+ * @event: Event to be posted. This event should've been alloced
+ * using k[mz]alloc, and should've been completely initialized.
+ * @interruptible: Interruptible waits if possible.
+ *
+ * As a side effect, the object pointed to by @event may have been
+ * freed when this function returns. If this function returns with
+ * an error code, the caller needs to free that object.
+ */
+
+int vmw_event_fence_action_create(struct drm_file *file_priv,
+ struct vmw_fence_obj *fence,
+ struct drm_event *event,
+ uint32_t *tv_sec,
+ uint32_t *tv_usec,
+ bool interruptible)
+{
+ struct vmw_event_fence_action *eaction;
+ struct ttm_mem_global *mem_glob =
+ vmw_mem_glob(fence->fman->dev_priv);
+ struct vmw_fence_manager *fman = fence->fman;
+ uint32_t size = fman->event_fence_action_size +
+ ttm_round_pot(event->length);
+ int ret;
+
+ /*
+ * Account for internal structure size as well as the
+ * event size itself.
+ */
+
+ ret = ttm_mem_global_alloc(mem_glob, size, false, interruptible);
+ if (unlikely(ret != 0))
+ return ret;
- (void) vmw_fence_pull(queue, sequence);
+ eaction = kzalloc(sizeof(*eaction), GFP_KERNEL);
+ if (unlikely(eaction == NULL)) {
+ ttm_mem_global_free(mem_glob, size);
+ return -ENOMEM;
}
+
+ eaction->e.event = event;
+ eaction->e.file_priv = file_priv;
+ eaction->e.destroy = vmw_event_fence_action_delivered;
+
+ eaction->action.seq_passed = vmw_event_fence_action_seq_passed;
+ eaction->action.cleanup = vmw_event_fence_action_cleanup;
+ eaction->action.type = VMW_ACTION_EVENT;
+
+ eaction->fence = vmw_fence_obj_reference(fence);
+ eaction->dev = fman->dev_priv->dev;
+ eaction->size = size;
+ eaction->tv_sec = tv_sec;
+ eaction->tv_usec = tv_usec;
+
+ kref_init(&eaction->kref);
+ vmw_fence_obj_add_action(fence, &eaction->action);
+
return 0;
}
+int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ struct drm_vmw_fence_event_arg *arg =
+ (struct drm_vmw_fence_event_arg *) data;
+ struct vmw_fence_obj *fence = NULL;
+ struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
+ struct drm_vmw_fence_rep __user *user_fence_rep =
+ (struct drm_vmw_fence_rep __user *)(unsigned long)
+ arg->fence_rep;
+ uint32_t handle;
+ unsigned long irq_flags;
+ struct drm_vmw_event_fence *event;
+ int ret;
+
+ /*
+ * Look up an existing fence object,
+ * and if user-space wants a new reference,
+ * add one.
+ */
+ if (arg->handle) {
+ struct ttm_base_object *base =
+ ttm_base_object_lookup(vmw_fp->tfile, arg->handle);
+ if (unlikely(base == NULL)) {
+ DRM_ERROR("Fence event invalid fence object handle "
+ "0x%08lx.\n",
+ (unsigned long)arg->handle);
+ return -EINVAL;
+ }
+ fence = &(container_of(base, struct vmw_user_fence,
+ base)->fence);
+ (void) vmw_fence_obj_reference(fence);
+
+ if (user_fence_rep != NULL) {
+ bool existed;
+
+ ret = ttm_ref_object_add(vmw_fp->tfile, base,
+ TTM_REF_USAGE, &existed);
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Failed to reference a fence "
+ "object.\n");
+ goto out_no_ref_obj;
+ }
+ handle = base->hash.key;
+ }
+ ttm_base_object_unref(&base);
+ }
+
+ /*
+ * Create a new fence object.
+ */
+ if (!fence) {
+ ret = vmw_execbuf_fence_commands(file_priv, dev_priv,
+ &fence,
+ (user_fence_rep) ?
+ &handle : NULL);
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Fence event failed to create fence.\n");
+ return ret;
+ }
+ }
+
+ BUG_ON(fence == NULL);
+
+ spin_lock_irqsave(&dev->event_lock, irq_flags);
+
+ ret = (file_priv->event_space < sizeof(*event)) ? -EBUSY : 0;
+ if (likely(ret == 0))
+ file_priv->event_space -= sizeof(*event);
+
+ spin_unlock_irqrestore(&dev->event_lock, irq_flags);
+
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Failed to allocate event space for this file.\n");
+ goto out_no_event_space;
+ }
+
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
+ if (unlikely(event == NULL)) {
+ DRM_ERROR("Failed to allocate an event.\n");
+ goto out_no_event;
+ }
+
+ event->base.type = DRM_VMW_EVENT_FENCE_SIGNALED;
+ event->base.length = sizeof(*event);
+ event->user_data = arg->user_data;
+
+ if (arg->flags & DRM_VMW_FE_FLAG_REQ_TIME)
+ ret = vmw_event_fence_action_create(file_priv, fence,
+ &event->base,
+ &event->tv_sec,
+ &event->tv_usec,
+ true);
+ else
+ ret = vmw_event_fence_action_create(file_priv, fence,
+ &event->base,
+ NULL,
+ NULL,
+ true);
+
+ if (unlikely(ret != 0)) {
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Failed to attach event to fence.\n");
+ goto out_no_attach;
+ }
+
+ vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence,
+ handle);
+ vmw_fence_obj_unreference(&fence);
+ return 0;
+out_no_attach:
+ kfree(event);
+out_no_event:
+ spin_lock_irqsave(&dev->event_lock, irq_flags);
+ file_priv->event_space += sizeof(*event);
+ spin_unlock_irqrestore(&dev->event_lock, irq_flags);
+out_no_event_space:
+ if (user_fence_rep != NULL)
+ ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
+ handle, TTM_REF_USAGE);
+out_no_ref_obj:
+ vmw_fence_obj_unreference(&fence);
+ return ret;
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
new file mode 100644
index 000000000000..0854a2096b55
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
@@ -0,0 +1,113 @@
+/**************************************************************************
+ *
+ * Copyright © 2011 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifndef _VMWGFX_FENCE_H_
+
+#define VMW_FENCE_WAIT_TIMEOUT (5*HZ)
+
+struct vmw_private;
+
+struct vmw_fence_manager;
+
+/**
+ *
+ *
+ */
+enum vmw_action_type {
+ VMW_ACTION_EVENT = 0,
+ VMW_ACTION_MAX
+};
+
+struct vmw_fence_action {
+ struct list_head head;
+ enum vmw_action_type type;
+ void (*seq_passed) (struct vmw_fence_action *action);
+ void (*cleanup) (struct vmw_fence_action *action);
+};
+
+struct vmw_fence_obj {
+ struct kref kref;
+ u32 seqno;
+
+ struct vmw_fence_manager *fman;
+ struct list_head head;
+ uint32_t signaled;
+ uint32_t signal_mask;
+ struct list_head seq_passed_actions;
+ void (*destroy)(struct vmw_fence_obj *fence);
+ wait_queue_head_t queue;
+};
+
+extern struct vmw_fence_manager *
+vmw_fence_manager_init(struct vmw_private *dev_priv);
+
+extern void vmw_fence_manager_takedown(struct vmw_fence_manager *fman);
+
+extern void vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p);
+
+extern struct vmw_fence_obj *
+vmw_fence_obj_reference(struct vmw_fence_obj *fence);
+
+extern void vmw_fences_update(struct vmw_fence_manager *fman);
+
+extern bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence,
+ uint32_t flags);
+
+extern int vmw_fence_obj_wait(struct vmw_fence_obj *fence, uint32_t flags,
+ bool lazy,
+ bool interruptible, unsigned long timeout);
+
+extern void vmw_fence_obj_flush(struct vmw_fence_obj *fence);
+
+extern int vmw_fence_create(struct vmw_fence_manager *fman,
+ uint32_t seqno,
+ uint32_t mask,
+ struct vmw_fence_obj **p_fence);
+
+extern int vmw_user_fence_create(struct drm_file *file_priv,
+ struct vmw_fence_manager *fman,
+ uint32_t sequence,
+ uint32_t mask,
+ struct vmw_fence_obj **p_fence,
+ uint32_t *p_handle);
+
+extern void vmw_fence_fifo_up(struct vmw_fence_manager *fman);
+
+extern void vmw_fence_fifo_down(struct vmw_fence_manager *fman);
+
+extern int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+extern int vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+extern int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+extern int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+#endif /* _VMWGFX_FENCE_H_ */
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
index 635c0ffee7fe..03bbc2a6f9a7 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
@@ -45,7 +45,11 @@ bool vmw_fifo_have_3d(struct vmw_private *dev_priv)
if (hwversion == 0)
return false;
- if (hwversion < SVGA3D_HWVERSION_WS65_B1)
+ if (hwversion < SVGA3D_HWVERSION_WS8_B1)
+ return false;
+
+ /* Non-Screen Object path does not support surfaces */
+ if (!dev_priv->sou_priv)
return false;
return true;
@@ -72,22 +76,12 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
uint32_t max;
uint32_t min;
uint32_t dummy;
- int ret;
fifo->static_buffer_size = VMWGFX_FIFO_STATIC_SIZE;
fifo->static_buffer = vmalloc(fifo->static_buffer_size);
if (unlikely(fifo->static_buffer == NULL))
return -ENOMEM;
- fifo->last_buffer_size = VMWGFX_FIFO_STATIC_SIZE;
- fifo->last_data_size = 0;
- fifo->last_buffer_add = false;
- fifo->last_buffer = vmalloc(fifo->last_buffer_size);
- if (unlikely(fifo->last_buffer == NULL)) {
- ret = -ENOMEM;
- goto out_err;
- }
-
fifo->dynamic_buffer = NULL;
fifo->reserved_size = 0;
fifo->using_bounce_buffer = false;
@@ -137,14 +131,10 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
(unsigned int) min,
(unsigned int) fifo->capabilities);
- atomic_set(&dev_priv->fence_seq, dev_priv->last_read_sequence);
- iowrite32(dev_priv->last_read_sequence, fifo_mem + SVGA_FIFO_FENCE);
- vmw_fence_queue_init(&fifo->fence_queue);
+ atomic_set(&dev_priv->marker_seq, dev_priv->last_read_seqno);
+ iowrite32(dev_priv->last_read_seqno, fifo_mem + SVGA_FIFO_FENCE);
+ vmw_marker_queue_init(&fifo->marker_queue);
return vmw_fifo_send_fence(dev_priv, &dummy);
-out_err:
- vfree(fifo->static_buffer);
- fifo->static_buffer = NULL;
- return ret;
}
void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason)
@@ -170,7 +160,7 @@ void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
while (vmw_read(dev_priv, SVGA_REG_BUSY) != 0)
vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
- dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE);
+ dev_priv->last_read_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE);
vmw_write(dev_priv, SVGA_REG_CONFIG_DONE,
dev_priv->config_done_state);
@@ -180,12 +170,7 @@ void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
dev_priv->traces_state);
mutex_unlock(&dev_priv->hw_mutex);
- vmw_fence_queue_takedown(&fifo->fence_queue);
-
- if (likely(fifo->last_buffer != NULL)) {
- vfree(fifo->last_buffer);
- fifo->last_buffer = NULL;
- }
+ vmw_marker_queue_takedown(&fifo->marker_queue);
if (likely(fifo->static_buffer != NULL)) {
vfree(fifo->static_buffer);
@@ -262,9 +247,8 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv,
spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
outl(SVGA_IRQFLAG_FIFO_PROGRESS,
dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
- vmw_write(dev_priv, SVGA_REG_IRQMASK,
- vmw_read(dev_priv, SVGA_REG_IRQMASK) |
- SVGA_IRQFLAG_FIFO_PROGRESS);
+ dev_priv->irq_mask |= SVGA_IRQFLAG_FIFO_PROGRESS;
+ vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask);
spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
}
mutex_unlock(&dev_priv->hw_mutex);
@@ -286,9 +270,8 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv,
mutex_lock(&dev_priv->hw_mutex);
if (atomic_dec_and_test(&dev_priv->fifo_queue_waiters)) {
spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
- vmw_write(dev_priv, SVGA_REG_IRQMASK,
- vmw_read(dev_priv, SVGA_REG_IRQMASK) &
- ~SVGA_IRQFLAG_FIFO_PROGRESS);
+ dev_priv->irq_mask &= ~SVGA_IRQFLAG_FIFO_PROGRESS;
+ vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask);
spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
}
mutex_unlock(&dev_priv->hw_mutex);
@@ -296,6 +279,16 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv,
return ret;
}
+/**
+ * Reserve @bytes number of bytes in the fifo.
+ *
+ * This function will return NULL (error) on two conditions:
+ * If it timeouts waiting for fifo space, or if @bytes is larger than the
+ * available fifo space.
+ *
+ * Returns:
+ * Pointer to the fifo, or null on error (possible hardware hang).
+ */
void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes)
{
struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
@@ -466,7 +459,7 @@ void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes)
mutex_unlock(&fifo_state->fifo_mutex);
}
-int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence)
+int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *seqno)
{
struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
struct svga_fifo_cmd_fence *cmd_fence;
@@ -476,16 +469,16 @@ int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence)
fm = vmw_fifo_reserve(dev_priv, bytes);
if (unlikely(fm == NULL)) {
- *sequence = atomic_read(&dev_priv->fence_seq);
+ *seqno = atomic_read(&dev_priv->marker_seq);
ret = -ENOMEM;
- (void)vmw_fallback_wait(dev_priv, false, true, *sequence,
+ (void)vmw_fallback_wait(dev_priv, false, true, *seqno,
false, 3*HZ);
goto out_err;
}
do {
- *sequence = atomic_add_return(1, &dev_priv->fence_seq);
- } while (*sequence == 0);
+ *seqno = atomic_add_return(1, &dev_priv->marker_seq);
+ } while (*seqno == 0);
if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE)) {
@@ -502,61 +495,68 @@ int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence)
cmd_fence = (struct svga_fifo_cmd_fence *)
((unsigned long)fm + sizeof(__le32));
- iowrite32(*sequence, &cmd_fence->fence);
- fifo_state->last_buffer_add = true;
+ iowrite32(*seqno, &cmd_fence->fence);
vmw_fifo_commit(dev_priv, bytes);
- fifo_state->last_buffer_add = false;
- (void) vmw_fence_push(&fifo_state->fence_queue, *sequence);
- vmw_update_sequence(dev_priv, fifo_state);
+ (void) vmw_marker_push(&fifo_state->marker_queue, *seqno);
+ vmw_update_seqno(dev_priv, fifo_state);
out_err:
return ret;
}
/**
- * Map the first page of the FIFO read-only to user-space.
+ * vmw_fifo_emit_dummy_query - emits a dummy query to the fifo.
+ *
+ * @dev_priv: The device private structure.
+ * @cid: The hardware context id used for the query.
+ *
+ * This function is used to emit a dummy occlusion query with
+ * no primitives rendered between query begin and query end.
+ * It's used to provide a query barrier, in order to know that when
+ * this query is finished, all preceding queries are also finished.
+ *
+ * A Query results structure should have been initialized at the start
+ * of the dev_priv->dummy_query_bo buffer object. And that buffer object
+ * must also be either reserved or pinned when this function is called.
+ *
+ * Returns -ENOMEM on failure to reserve fifo space.
*/
-
-static int vmw_fifo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+int vmw_fifo_emit_dummy_query(struct vmw_private *dev_priv,
+ uint32_t cid)
{
- int ret;
- unsigned long address = (unsigned long)vmf->virtual_address;
+ /*
+ * A query wait without a preceding query end will
+ * actually finish all queries for this cid
+ * without writing to the query result structure.
+ */
- if (address != vma->vm_start)
- return VM_FAULT_SIGBUS;
+ struct ttm_buffer_object *bo = dev_priv->dummy_query_bo;
+ struct {
+ SVGA3dCmdHeader header;
+ SVGA3dCmdWaitForQuery body;
+ } *cmd;
- ret = vm_insert_pfn(vma, address, vma->vm_pgoff);
- if (likely(ret == -EBUSY || ret == 0))
- return VM_FAULT_NOPAGE;
- else if (ret == -ENOMEM)
- return VM_FAULT_OOM;
+ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
- return VM_FAULT_SIGBUS;
-}
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("Out of fifo space for dummy query.\n");
+ return -ENOMEM;
+ }
-static struct vm_operations_struct vmw_fifo_vm_ops = {
- .fault = vmw_fifo_vm_fault,
- .open = NULL,
- .close = NULL
-};
+ cmd->header.id = SVGA_3D_CMD_WAIT_FOR_QUERY;
+ cmd->header.size = sizeof(cmd->body);
+ cmd->body.cid = cid;
+ cmd->body.type = SVGA3D_QUERYTYPE_OCCLUSION;
+
+ if (bo->mem.mem_type == TTM_PL_VRAM) {
+ cmd->body.guestResult.gmrId = SVGA_GMR_FRAMEBUFFER;
+ cmd->body.guestResult.offset = bo->offset;
+ } else {
+ cmd->body.guestResult.gmrId = bo->mem.start;
+ cmd->body.guestResult.offset = 0;
+ }
+
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
-int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- struct drm_file *file_priv;
- struct vmw_private *dev_priv;
-
- file_priv = filp->private_data;
- dev_priv = vmw_priv(file_priv->minor->dev);
-
- if (vma->vm_pgoff != (dev_priv->mmio_start >> PAGE_SHIFT) ||
- (vma->vm_end - vma->vm_start) != PAGE_SIZE)
- return -EINVAL;
-
- vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE);
- vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_SHARED;
- vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
- vma->vm_page_prot = ttm_io_prot(TTM_PL_FLAG_UNCACHED,
- vma->vm_page_prot);
- vma->vm_ops = &vmw_fifo_vm_ops;
return 0;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
index de0c5948521d..f4e7763a7694 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
@@ -1,6 +1,6 @@
/**************************************************************************
*
- * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
+ * Copyright © 2009-2011 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -29,6 +29,77 @@
#include "drmP.h"
#include "ttm/ttm_bo_driver.h"
+#define VMW_PPN_SIZE sizeof(unsigned long)
+
+static int vmw_gmr2_bind(struct vmw_private *dev_priv,
+ struct page *pages[],
+ unsigned long num_pages,
+ int gmr_id)
+{
+ SVGAFifoCmdDefineGMR2 define_cmd;
+ SVGAFifoCmdRemapGMR2 remap_cmd;
+ uint32_t define_size = sizeof(define_cmd) + 4;
+ uint32_t remap_size = VMW_PPN_SIZE * num_pages + sizeof(remap_cmd) + 4;
+ uint32_t *cmd;
+ uint32_t *cmd_orig;
+ uint32_t i;
+
+ cmd_orig = cmd = vmw_fifo_reserve(dev_priv, define_size + remap_size);
+ if (unlikely(cmd == NULL))
+ return -ENOMEM;
+
+ define_cmd.gmrId = gmr_id;
+ define_cmd.numPages = num_pages;
+
+ remap_cmd.gmrId = gmr_id;
+ remap_cmd.flags = (VMW_PPN_SIZE > sizeof(*cmd)) ?
+ SVGA_REMAP_GMR2_PPN64 : SVGA_REMAP_GMR2_PPN32;
+ remap_cmd.offsetPages = 0;
+ remap_cmd.numPages = num_pages;
+
+ *cmd++ = SVGA_CMD_DEFINE_GMR2;
+ memcpy(cmd, &define_cmd, sizeof(define_cmd));
+ cmd += sizeof(define_cmd) / sizeof(uint32);
+
+ *cmd++ = SVGA_CMD_REMAP_GMR2;
+ memcpy(cmd, &remap_cmd, sizeof(remap_cmd));
+ cmd += sizeof(remap_cmd) / sizeof(uint32);
+
+ for (i = 0; i < num_pages; ++i) {
+ if (VMW_PPN_SIZE > 4)
+ *cmd = page_to_pfn(*pages++);
+ else
+ *((uint64_t *)cmd) = page_to_pfn(*pages++);
+
+ cmd += VMW_PPN_SIZE / sizeof(*cmd);
+ }
+
+ vmw_fifo_commit(dev_priv, define_size + remap_size);
+
+ return 0;
+}
+
+static void vmw_gmr2_unbind(struct vmw_private *dev_priv,
+ int gmr_id)
+{
+ SVGAFifoCmdDefineGMR2 define_cmd;
+ uint32_t define_size = sizeof(define_cmd) + 4;
+ uint32_t *cmd;
+
+ cmd = vmw_fifo_reserve(dev_priv, define_size);
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("GMR2 unbind failed.\n");
+ return;
+ }
+ define_cmd.gmrId = gmr_id;
+ define_cmd.numPages = 0;
+
+ *cmd++ = SVGA_CMD_DEFINE_GMR2;
+ memcpy(cmd, &define_cmd, sizeof(define_cmd));
+
+ vmw_fifo_commit(dev_priv, define_size);
+}
+
/**
* FIXME: Adjust to the ttm lowmem / highmem storage to minimize
* the number of used descriptors.
@@ -170,6 +241,9 @@ int vmw_gmr_bind(struct vmw_private *dev_priv,
struct list_head desc_pages;
int ret;
+ if (likely(dev_priv->capabilities & SVGA_CAP_GMR2))
+ return vmw_gmr2_bind(dev_priv, pages, num_pages, gmr_id);
+
if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR)))
return -EINVAL;
@@ -192,6 +266,11 @@ int vmw_gmr_bind(struct vmw_private *dev_priv,
void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id)
{
+ if (likely(dev_priv->capabilities & SVGA_CAP_GMR2)) {
+ vmw_gmr2_unbind(dev_priv, gmr_id);
+ return;
+ }
+
mutex_lock(&dev_priv->hw_mutex);
vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id);
wmb();
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
index ac6e0d1bd629..5f717152cff5 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
@@ -40,6 +40,8 @@ struct vmwgfx_gmrid_man {
spinlock_t lock;
struct ida gmr_ida;
uint32_t max_gmr_ids;
+ uint32_t max_gmr_pages;
+ uint32_t used_gmr_pages;
};
static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man,
@@ -49,33 +51,50 @@ static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man,
{
struct vmwgfx_gmrid_man *gman =
(struct vmwgfx_gmrid_man *)man->priv;
- int ret;
+ int ret = 0;
int id;
mem->mm_node = NULL;
- do {
- if (unlikely(ida_pre_get(&gman->gmr_ida, GFP_KERNEL) == 0))
- return -ENOMEM;
+ spin_lock(&gman->lock);
+
+ if (gman->max_gmr_pages > 0) {
+ gman->used_gmr_pages += bo->num_pages;
+ if (unlikely(gman->used_gmr_pages > gman->max_gmr_pages))
+ goto out_err_locked;
+ }
+ do {
+ spin_unlock(&gman->lock);
+ if (unlikely(ida_pre_get(&gman->gmr_ida, GFP_KERNEL) == 0)) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
spin_lock(&gman->lock);
- ret = ida_get_new(&gman->gmr_ida, &id);
+ ret = ida_get_new(&gman->gmr_ida, &id);
if (unlikely(ret == 0 && id >= gman->max_gmr_ids)) {
ida_remove(&gman->gmr_ida, id);
- spin_unlock(&gman->lock);
- return 0;
+ ret = 0;
+ goto out_err_locked;
}
-
- spin_unlock(&gman->lock);
-
} while (ret == -EAGAIN);
if (likely(ret == 0)) {
mem->mm_node = gman;
mem->start = id;
- }
+ mem->num_pages = bo->num_pages;
+ } else
+ goto out_err_locked;
+
+ spin_unlock(&gman->lock);
+ return 0;
+out_err:
+ spin_lock(&gman->lock);
+out_err_locked:
+ gman->used_gmr_pages -= bo->num_pages;
+ spin_unlock(&gman->lock);
return ret;
}
@@ -88,6 +107,7 @@ static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man,
if (mem->mm_node) {
spin_lock(&gman->lock);
ida_remove(&gman->gmr_ida, mem->start);
+ gman->used_gmr_pages -= mem->num_pages;
spin_unlock(&gman->lock);
mem->mm_node = NULL;
}
@@ -96,6 +116,8 @@ static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man,
static int vmw_gmrid_man_init(struct ttm_mem_type_manager *man,
unsigned long p_size)
{
+ struct vmw_private *dev_priv =
+ container_of(man->bdev, struct vmw_private, bdev);
struct vmwgfx_gmrid_man *gman =
kzalloc(sizeof(*gman), GFP_KERNEL);
@@ -103,6 +125,8 @@ static int vmw_gmrid_man_init(struct ttm_mem_type_manager *man,
return -ENOMEM;
spin_lock_init(&gman->lock);
+ gman->max_gmr_pages = dev_priv->max_gmr_pages;
+ gman->used_gmr_pages = 0;
ida_init(&gman->gmr_ida);
gman->max_gmr_ids = p_size;
man->priv = (void *) gman;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
index 570d57775a58..3f6343502d1f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
@@ -27,6 +27,7 @@
#include "vmwgfx_drv.h"
#include "vmwgfx_drm.h"
+#include "vmwgfx_kms.h"
int vmw_getparam_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
@@ -45,9 +46,6 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,
case DRM_VMW_PARAM_3D:
param->value = vmw_fifo_have_3d(dev_priv) ? 1 : 0;
break;
- case DRM_VMW_PARAM_FIFO_OFFSET:
- param->value = dev_priv->mmio_start;
- break;
case DRM_VMW_PARAM_HW_CAPS:
param->value = dev_priv->capabilities;
break;
@@ -57,6 +55,13 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,
case DRM_VMW_PARAM_MAX_FB_SIZE:
param->value = dev_priv->vram_size;
break;
+ case DRM_VMW_PARAM_FIFO_HW_VERSION:
+ {
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
+
+ param->value = ioread32(fifo_mem + SVGA_FIFO_3D_HWVERSION);
+ break;
+ }
default:
DRM_ERROR("Illegal vmwgfx get param request: %d\n",
param->param);
@@ -66,25 +71,259 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,
return 0;
}
-int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data,
+
+int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
+ struct drm_vmw_get_3d_cap_arg *arg =
+ (struct drm_vmw_get_3d_cap_arg *) data;
struct vmw_private *dev_priv = vmw_priv(dev);
- struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
- struct drm_vmw_fifo_debug_arg *arg =
- (struct drm_vmw_fifo_debug_arg *)data;
- __le32 __user *buffer = (__le32 __user *)
- (unsigned long)arg->debug_buffer;
+ uint32_t size;
+ __le32 __iomem *fifo_mem;
+ void __user *buffer = (void __user *)((unsigned long)(arg->buffer));
+ void *bounce;
+ int ret;
- if (unlikely(fifo_state->last_buffer == NULL))
+ if (unlikely(arg->pad64 != 0)) {
+ DRM_ERROR("Illegal GET_3D_CAP argument.\n");
return -EINVAL;
+ }
+
+ size = (SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1) << 2;
+
+ if (arg->max_size < size)
+ size = arg->max_size;
+
+ bounce = vmalloc(size);
+ if (unlikely(bounce == NULL)) {
+ DRM_ERROR("Failed to allocate bounce buffer for 3D caps.\n");
+ return -ENOMEM;
+ }
+
+ fifo_mem = dev_priv->mmio_virt;
+ memcpy_fromio(bounce, &fifo_mem[SVGA_FIFO_3D_CAPS], size);
+
+ ret = copy_to_user(buffer, bounce, size);
+ vfree(bounce);
+
+ if (unlikely(ret != 0))
+ DRM_ERROR("Failed to report 3D caps info.\n");
+
+ return ret;
+}
+
+int vmw_present_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ struct drm_vmw_present_arg *arg =
+ (struct drm_vmw_present_arg *)data;
+ struct vmw_surface *surface;
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
+ struct drm_vmw_rect __user *clips_ptr;
+ struct drm_vmw_rect *clips = NULL;
+ struct drm_mode_object *obj;
+ struct vmw_framebuffer *vfb;
+ uint32_t num_clips;
+ int ret;
+
+ num_clips = arg->num_clips;
+ clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr;
+
+ if (unlikely(num_clips == 0))
+ return 0;
+
+ if (clips_ptr == NULL) {
+ DRM_ERROR("Variable clips_ptr must be specified.\n");
+ ret = -EINVAL;
+ goto out_clips;
+ }
+
+ clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
+ if (clips == NULL) {
+ DRM_ERROR("Failed to allocate clip rect list.\n");
+ ret = -ENOMEM;
+ goto out_clips;
+ }
+
+ ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips));
+ if (ret) {
+ DRM_ERROR("Failed to copy clip rects from userspace.\n");
+ ret = -EFAULT;
+ goto out_no_copy;
+ }
+
+ ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+ if (unlikely(ret != 0)) {
+ ret = -ERESTARTSYS;
+ goto out_no_mode_mutex;
+ }
+
+ obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ DRM_ERROR("Invalid framebuffer id.\n");
+ ret = -EINVAL;
+ goto out_no_fb;
+ }
+
+ vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj));
+ if (!vfb->dmabuf) {
+ DRM_ERROR("Framebuffer not dmabuf backed.\n");
+ ret = -EINVAL;
+ goto out_no_fb;
+ }
+
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (unlikely(ret != 0))
+ goto out_no_ttm_lock;
+
+ ret = vmw_user_surface_lookup_handle(dev_priv, tfile, arg->sid,
+ &surface);
+ if (ret)
+ goto out_no_surface;
+
+ ret = vmw_kms_present(dev_priv, file_priv,
+ vfb, surface, arg->sid,
+ arg->dest_x, arg->dest_y,
+ clips, num_clips);
- if (arg->debug_buffer_size < fifo_state->last_data_size) {
- arg->used_size = arg->debug_buffer_size;
- arg->did_not_fit = 1;
- } else {
- arg->used_size = fifo_state->last_data_size;
- arg->did_not_fit = 0;
+ /* vmw_user_surface_lookup takes one ref so does new_fb */
+ vmw_surface_unreference(&surface);
+
+out_no_surface:
+ ttm_read_unlock(&vmaster->lock);
+out_no_ttm_lock:
+out_no_fb:
+ mutex_unlock(&dev->mode_config.mutex);
+out_no_mode_mutex:
+out_no_copy:
+ kfree(clips);
+out_clips:
+ return ret;
+}
+
+int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ struct drm_vmw_present_readback_arg *arg =
+ (struct drm_vmw_present_readback_arg *)data;
+ struct drm_vmw_fence_rep __user *user_fence_rep =
+ (struct drm_vmw_fence_rep __user *)
+ (unsigned long)arg->fence_rep;
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
+ struct drm_vmw_rect __user *clips_ptr;
+ struct drm_vmw_rect *clips = NULL;
+ struct drm_mode_object *obj;
+ struct vmw_framebuffer *vfb;
+ uint32_t num_clips;
+ int ret;
+
+ num_clips = arg->num_clips;
+ clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr;
+
+ if (unlikely(num_clips == 0))
+ return 0;
+
+ if (clips_ptr == NULL) {
+ DRM_ERROR("Argument clips_ptr must be specified.\n");
+ ret = -EINVAL;
+ goto out_clips;
+ }
+
+ clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
+ if (clips == NULL) {
+ DRM_ERROR("Failed to allocate clip rect list.\n");
+ ret = -ENOMEM;
+ goto out_clips;
+ }
+
+ ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips));
+ if (ret) {
+ DRM_ERROR("Failed to copy clip rects from userspace.\n");
+ ret = -EFAULT;
+ goto out_no_copy;
+ }
+
+ ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+ if (unlikely(ret != 0)) {
+ ret = -ERESTARTSYS;
+ goto out_no_mode_mutex;
+ }
+
+ obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ DRM_ERROR("Invalid framebuffer id.\n");
+ ret = -EINVAL;
+ goto out_no_fb;
+ }
+
+ vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj));
+ if (!vfb->dmabuf) {
+ DRM_ERROR("Framebuffer not dmabuf backed.\n");
+ ret = -EINVAL;
+ goto out_no_fb;
}
- return copy_to_user(buffer, fifo_state->last_buffer, arg->used_size);
+
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (unlikely(ret != 0))
+ goto out_no_ttm_lock;
+
+ ret = vmw_kms_readback(dev_priv, file_priv,
+ vfb, user_fence_rep,
+ clips, num_clips);
+
+ ttm_read_unlock(&vmaster->lock);
+out_no_ttm_lock:
+out_no_fb:
+ mutex_unlock(&dev->mode_config.mutex);
+out_no_mode_mutex:
+out_no_copy:
+ kfree(clips);
+out_clips:
+ return ret;
+}
+
+
+/**
+ * vmw_fops_poll - wrapper around the drm_poll function
+ *
+ * @filp: See the linux fops poll documentation.
+ * @wait: See the linux fops poll documentation.
+ *
+ * Wrapper around the drm_poll function that makes sure the device is
+ * processing the fifo if drm_poll decides to wait.
+ */
+unsigned int vmw_fops_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ struct drm_file *file_priv = filp->private_data;
+ struct vmw_private *dev_priv =
+ vmw_priv(file_priv->minor->dev);
+
+ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
+ return drm_poll(filp, wait);
+}
+
+
+/**
+ * vmw_fops_read - wrapper around the drm_read function
+ *
+ * @filp: See the linux fops read documentation.
+ * @buffer: See the linux fops read documentation.
+ * @count: See the linux fops read documentation.
+ * offset: See the linux fops read documentation.
+ *
+ * Wrapper around the drm_read function that makes sure the device is
+ * processing the fifo if drm_read decides to wait.
+ */
+ssize_t vmw_fops_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *offset)
+{
+ struct drm_file *file_priv = filp->private_data;
+ struct vmw_private *dev_priv =
+ vmw_priv(file_priv->minor->dev);
+
+ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
+ return drm_read(filp, buffer, count, offset);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c
index e92298a6a383..cabc95f7517e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c
@@ -34,26 +34,33 @@ irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *)arg;
struct vmw_private *dev_priv = vmw_priv(dev);
- uint32_t status;
+ uint32_t status, masked_status;
spin_lock(&dev_priv->irq_lock);
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
+ masked_status = status & dev_priv->irq_mask;
spin_unlock(&dev_priv->irq_lock);
- if (status & SVGA_IRQFLAG_ANY_FENCE)
+ if (likely(status))
+ outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
+
+ if (!masked_status)
+ return IRQ_NONE;
+
+ if (masked_status & (SVGA_IRQFLAG_ANY_FENCE |
+ SVGA_IRQFLAG_FENCE_GOAL)) {
+ vmw_fences_update(dev_priv->fman);
wake_up_all(&dev_priv->fence_queue);
- if (status & SVGA_IRQFLAG_FIFO_PROGRESS)
+ }
+
+ if (masked_status & SVGA_IRQFLAG_FIFO_PROGRESS)
wake_up_all(&dev_priv->fifo_queue);
- if (likely(status)) {
- outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
- return IRQ_HANDLED;
- }
- return IRQ_NONE;
+ return IRQ_HANDLED;
}
-static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence)
+static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t seqno)
{
uint32_t busy;
@@ -64,43 +71,43 @@ static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence)
return (busy == 0);
}
-void vmw_update_sequence(struct vmw_private *dev_priv,
+void vmw_update_seqno(struct vmw_private *dev_priv,
struct vmw_fifo_state *fifo_state)
{
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
+ uint32_t seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE);
- uint32_t sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE);
-
- if (dev_priv->last_read_sequence != sequence) {
- dev_priv->last_read_sequence = sequence;
- vmw_fence_pull(&fifo_state->fence_queue, sequence);
+ if (dev_priv->last_read_seqno != seqno) {
+ dev_priv->last_read_seqno = seqno;
+ vmw_marker_pull(&fifo_state->marker_queue, seqno);
+ vmw_fences_update(dev_priv->fman);
}
}
-bool vmw_fence_signaled(struct vmw_private *dev_priv,
- uint32_t sequence)
+bool vmw_seqno_passed(struct vmw_private *dev_priv,
+ uint32_t seqno)
{
struct vmw_fifo_state *fifo_state;
bool ret;
- if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
+ if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP))
return true;
fifo_state = &dev_priv->fifo;
- vmw_update_sequence(dev_priv, fifo_state);
- if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
+ vmw_update_seqno(dev_priv, fifo_state);
+ if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP))
return true;
if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE) &&
- vmw_fifo_idle(dev_priv, sequence))
+ vmw_fifo_idle(dev_priv, seqno))
return true;
/**
- * Then check if the sequence is higher than what we've actually
+ * Then check if the seqno is higher than what we've actually
* emitted. Then the fence is stale and signaled.
*/
- ret = ((atomic_read(&dev_priv->fence_seq) - sequence)
+ ret = ((atomic_read(&dev_priv->marker_seq) - seqno)
> VMW_FENCE_WRAP);
return ret;
@@ -109,7 +116,7 @@ bool vmw_fence_signaled(struct vmw_private *dev_priv,
int vmw_fallback_wait(struct vmw_private *dev_priv,
bool lazy,
bool fifo_idle,
- uint32_t sequence,
+ uint32_t seqno,
bool interruptible,
unsigned long timeout)
{
@@ -123,7 +130,7 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,
DEFINE_WAIT(__wait);
wait_condition = (fifo_idle) ? &vmw_fifo_idle :
- &vmw_fence_signaled;
+ &vmw_seqno_passed;
/**
* Block command submission while waiting for idle.
@@ -131,14 +138,14 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,
if (fifo_idle)
down_read(&fifo_state->rwsem);
- signal_seq = atomic_read(&dev_priv->fence_seq);
+ signal_seq = atomic_read(&dev_priv->marker_seq);
ret = 0;
for (;;) {
prepare_to_wait(&dev_priv->fence_queue, &__wait,
(interruptible) ?
TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
- if (wait_condition(dev_priv, sequence))
+ if (wait_condition(dev_priv, seqno))
break;
if (time_after_eq(jiffies, end_jiffies)) {
DRM_ERROR("SVGA device lockup.\n");
@@ -175,68 +182,110 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,
return ret;
}
-int vmw_wait_fence(struct vmw_private *dev_priv,
- bool lazy, uint32_t sequence,
- bool interruptible, unsigned long timeout)
+void vmw_seqno_waiter_add(struct vmw_private *dev_priv)
+{
+ mutex_lock(&dev_priv->hw_mutex);
+ if (dev_priv->fence_queue_waiters++ == 0) {
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
+ outl(SVGA_IRQFLAG_ANY_FENCE,
+ dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
+ dev_priv->irq_mask |= SVGA_IRQFLAG_ANY_FENCE;
+ vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
+ }
+ mutex_unlock(&dev_priv->hw_mutex);
+}
+
+void vmw_seqno_waiter_remove(struct vmw_private *dev_priv)
+{
+ mutex_lock(&dev_priv->hw_mutex);
+ if (--dev_priv->fence_queue_waiters == 0) {
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
+ dev_priv->irq_mask &= ~SVGA_IRQFLAG_ANY_FENCE;
+ vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
+ }
+ mutex_unlock(&dev_priv->hw_mutex);
+}
+
+
+void vmw_goal_waiter_add(struct vmw_private *dev_priv)
+{
+ mutex_lock(&dev_priv->hw_mutex);
+ if (dev_priv->goal_queue_waiters++ == 0) {
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
+ outl(SVGA_IRQFLAG_FENCE_GOAL,
+ dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
+ dev_priv->irq_mask |= SVGA_IRQFLAG_FENCE_GOAL;
+ vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
+ }
+ mutex_unlock(&dev_priv->hw_mutex);
+}
+
+void vmw_goal_waiter_remove(struct vmw_private *dev_priv)
+{
+ mutex_lock(&dev_priv->hw_mutex);
+ if (--dev_priv->goal_queue_waiters == 0) {
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
+ dev_priv->irq_mask &= ~SVGA_IRQFLAG_FENCE_GOAL;
+ vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
+ }
+ mutex_unlock(&dev_priv->hw_mutex);
+}
+
+int vmw_wait_seqno(struct vmw_private *dev_priv,
+ bool lazy, uint32_t seqno,
+ bool interruptible, unsigned long timeout)
{
long ret;
- unsigned long irq_flags;
struct vmw_fifo_state *fifo = &dev_priv->fifo;
- if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
+ if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP))
return 0;
- if (likely(vmw_fence_signaled(dev_priv, sequence)))
+ if (likely(vmw_seqno_passed(dev_priv, seqno)))
return 0;
vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
if (!(fifo->capabilities & SVGA_FIFO_CAP_FENCE))
- return vmw_fallback_wait(dev_priv, lazy, true, sequence,
+ return vmw_fallback_wait(dev_priv, lazy, true, seqno,
interruptible, timeout);
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
- return vmw_fallback_wait(dev_priv, lazy, false, sequence,
+ return vmw_fallback_wait(dev_priv, lazy, false, seqno,
interruptible, timeout);
- mutex_lock(&dev_priv->hw_mutex);
- if (atomic_add_return(1, &dev_priv->fence_queue_waiters) > 0) {
- spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
- outl(SVGA_IRQFLAG_ANY_FENCE,
- dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
- vmw_write(dev_priv, SVGA_REG_IRQMASK,
- vmw_read(dev_priv, SVGA_REG_IRQMASK) |
- SVGA_IRQFLAG_ANY_FENCE);
- spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
- }
- mutex_unlock(&dev_priv->hw_mutex);
+ vmw_seqno_waiter_add(dev_priv);
if (interruptible)
ret = wait_event_interruptible_timeout
(dev_priv->fence_queue,
- vmw_fence_signaled(dev_priv, sequence),
+ vmw_seqno_passed(dev_priv, seqno),
timeout);
else
ret = wait_event_timeout
(dev_priv->fence_queue,
- vmw_fence_signaled(dev_priv, sequence),
+ vmw_seqno_passed(dev_priv, seqno),
timeout);
+ vmw_seqno_waiter_remove(dev_priv);
+
if (unlikely(ret == 0))
ret = -EBUSY;
else if (likely(ret > 0))
ret = 0;
- mutex_lock(&dev_priv->hw_mutex);
- if (atomic_dec_and_test(&dev_priv->fence_queue_waiters)) {
- spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
- vmw_write(dev_priv, SVGA_REG_IRQMASK,
- vmw_read(dev_priv, SVGA_REG_IRQMASK) &
- ~SVGA_IRQFLAG_ANY_FENCE);
- spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
- }
- mutex_unlock(&dev_priv->hw_mutex);
-
return ret;
}
@@ -273,25 +322,3 @@ void vmw_irq_uninstall(struct drm_device *dev)
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
}
-
-#define VMW_FENCE_WAIT_TIMEOUT 3*HZ;
-
-int vmw_fence_wait_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- struct drm_vmw_fence_wait_arg *arg =
- (struct drm_vmw_fence_wait_arg *)data;
- unsigned long timeout;
-
- if (!arg->cookie_valid) {
- arg->cookie_valid = 1;
- arg->kernel_cookie = jiffies + VMW_FENCE_WAIT_TIMEOUT;
- }
-
- timeout = jiffies;
- if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie))
- return -EBUSY;
-
- timeout = (unsigned long)arg->kernel_cookie - timeout;
- return vmw_wait_fence(vmw_priv(dev), true, arg->sequence, true, timeout);
-}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index dfe32e62bd90..880e285d7578 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -27,12 +27,10 @@
#include "vmwgfx_kms.h"
+
/* Might need a hrtimer here? */
#define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1)
-static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb);
-static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb);
-
void vmw_display_unit_cleanup(struct vmw_display_unit *du)
{
if (du->cursor_surface)
@@ -107,12 +105,17 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
struct vmw_dma_buffer *dmabuf = NULL;
int ret;
+ /* A lot of the code assumes this */
+ if (handle && (width != 64 || height != 64))
+ return -EINVAL;
+
if (handle) {
ret = vmw_user_surface_lookup_handle(dev_priv, tfile,
handle, &surface);
if (!ret) {
if (!surface->snooper.image) {
DRM_ERROR("surface not suitable for cursor\n");
+ vmw_surface_unreference(&surface);
return -EINVAL;
}
} else {
@@ -178,7 +181,9 @@ err_unreserve:
return 0;
}
- vmw_cursor_update_position(dev_priv, true, du->cursor_x, du->cursor_y);
+ vmw_cursor_update_position(dev_priv, true,
+ du->cursor_x + du->hotspot_x,
+ du->cursor_y + du->hotspot_y);
return 0;
}
@@ -193,7 +198,8 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
du->cursor_y = y + crtc->y;
vmw_cursor_update_position(dev_priv, shown,
- du->cursor_x, du->cursor_y);
+ du->cursor_x + du->hotspot_x,
+ du->cursor_y + du->hotspot_y);
return 0;
}
@@ -214,7 +220,7 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,
SVGA3dCmdHeader header;
SVGA3dCmdSurfaceDMA dma;
} *cmd;
- int ret;
+ int i, ret;
cmd = container_of(header, struct vmw_dma_cmd, header);
@@ -236,16 +242,19 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,
box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) /
sizeof(SVGA3dCopyBox);
- if (cmd->dma.guest.pitch != (64 * 4) ||
- cmd->dma.guest.ptr.offset % PAGE_SIZE ||
+ if (cmd->dma.guest.ptr.offset % PAGE_SIZE ||
box->x != 0 || box->y != 0 || box->z != 0 ||
box->srcx != 0 || box->srcy != 0 || box->srcz != 0 ||
- box->w != 64 || box->h != 64 || box->d != 1 ||
- box_count != 1) {
+ box->d != 1 || box_count != 1) {
/* TODO handle none page aligned offsets */
- /* TODO handle partial uploads and pitch != 256 */
- /* TODO handle more then one copy (size != 64) */
- DRM_ERROR("lazy programmer, can't handle weird stuff\n");
+ /* TODO handle more dst & src != 0 */
+ /* TODO handle more then one copy */
+ DRM_ERROR("Cant snoop dma request for cursor!\n");
+ DRM_ERROR("(%u, %u, %u) (%u, %u, %u) (%ux%ux%u) %u %u\n",
+ box->srcx, box->srcy, box->srcz,
+ box->x, box->y, box->z,
+ box->w, box->h, box->d, box_count,
+ cmd->dma.guest.ptr.offset);
return;
}
@@ -264,7 +273,16 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,
virtual = ttm_kmap_obj_virtual(&map, &dummy);
- memcpy(srf->snooper.image, virtual, 64*64*4);
+ if (box->w == 64 && cmd->dma.guest.pitch == 64*4) {
+ memcpy(srf->snooper.image, virtual, 64*64*4);
+ } else {
+ /* Image is unsigned pointer. */
+ for (i = 0; i < box->h; i++)
+ memcpy(srf->snooper.image + i * 64,
+ virtual + i * cmd->dma.guest.pitch,
+ box->w * 4);
+ }
+
srf->snooper.age++;
/* we can't call this function from this function since execbuf has
@@ -329,41 +347,10 @@ struct vmw_framebuffer_surface {
struct vmw_framebuffer base;
struct vmw_surface *surface;
struct vmw_dma_buffer *buffer;
- struct delayed_work d_work;
- struct mutex work_lock;
- bool present_fs;
struct list_head head;
struct drm_master *master;
};
-/**
- * vmw_kms_idle_workqueues - Flush workqueues on this master
- *
- * @vmaster - Pointer identifying the master, for the surfaces of which
- * we idle the dirty work queues.
- *
- * This function should be called with the ttm lock held in exclusive mode
- * to idle all dirty work queues before the fifo is taken down.
- *
- * The work task may actually requeue itself, but after the flush returns we're
- * sure that there's nothing to present, since the ttm lock is held in
- * exclusive mode, so the fifo will never get used.
- */
-
-void vmw_kms_idle_workqueues(struct vmw_master *vmaster)
-{
- struct vmw_framebuffer_surface *entry;
-
- mutex_lock(&vmaster->fb_surf_mutex);
- list_for_each_entry(entry, &vmaster->fb_surf, head) {
- if (cancel_delayed_work_sync(&entry->d_work))
- (void) entry->d_work.work.func(&entry->d_work.work);
-
- (void) cancel_delayed_work_sync(&entry->d_work);
- }
- mutex_unlock(&vmaster->fb_surf_mutex);
-}
-
void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)
{
struct vmw_framebuffer_surface *vfbs =
@@ -375,65 +362,129 @@ void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)
list_del(&vfbs->head);
mutex_unlock(&vmaster->fb_surf_mutex);
- cancel_delayed_work_sync(&vfbs->d_work);
drm_master_put(&vfbs->master);
drm_framebuffer_cleanup(framebuffer);
vmw_surface_unreference(&vfbs->surface);
+ ttm_base_object_unref(&vfbs->base.user_obj);
kfree(vfbs);
}
-static void vmw_framebuffer_present_fs_callback(struct work_struct *work)
+static int do_surface_dirty_sou(struct vmw_private *dev_priv,
+ struct drm_file *file_priv,
+ struct vmw_framebuffer *framebuffer,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips, int inc)
{
- struct delayed_work *d_work =
- container_of(work, struct delayed_work, work);
- struct vmw_framebuffer_surface *vfbs =
- container_of(d_work, struct vmw_framebuffer_surface, d_work);
- struct vmw_surface *surf = vfbs->surface;
- struct drm_framebuffer *framebuffer = &vfbs->base.base;
- struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
+ struct drm_clip_rect *clips_ptr;
+ struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
+ struct drm_crtc *crtc;
+ size_t fifo_size;
+ int i, num_units;
+ int ret = 0; /* silence warning */
+ int left, right, top, bottom;
struct {
SVGA3dCmdHeader header;
- SVGA3dCmdPresent body;
- SVGA3dCopyRect cr;
+ SVGA3dCmdBlitSurfaceToScreen body;
} *cmd;
+ SVGASignedRect *blits;
- /**
- * Strictly we should take the ttm_lock in read mode before accessing
- * the fifo, to make sure the fifo is present and up. However,
- * instead we flush all workqueues under the ttm lock in exclusive mode
- * before taking down the fifo.
- */
- mutex_lock(&vfbs->work_lock);
- if (!vfbs->present_fs)
- goto out_unlock;
- cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
- if (unlikely(cmd == NULL))
- goto out_resched;
-
- cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT);
- cmd->header.size = cpu_to_le32(sizeof(cmd->body) + sizeof(cmd->cr));
- cmd->body.sid = cpu_to_le32(surf->res.id);
- cmd->cr.x = cpu_to_le32(0);
- cmd->cr.y = cpu_to_le32(0);
- cmd->cr.srcx = cmd->cr.x;
- cmd->cr.srcy = cmd->cr.y;
- cmd->cr.w = cpu_to_le32(framebuffer->width);
- cmd->cr.h = cpu_to_le32(framebuffer->height);
- vfbs->present_fs = false;
- vmw_fifo_commit(dev_priv, sizeof(*cmd));
-out_resched:
- /**
- * Will not re-add if already pending.
- */
- schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE);
-out_unlock:
- mutex_unlock(&vfbs->work_lock);
-}
+ num_units = 0;
+ list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list,
+ head) {
+ if (crtc->fb != &framebuffer->base)
+ continue;
+ units[num_units++] = vmw_crtc_to_du(crtc);
+ }
+
+ BUG_ON(!clips || !num_clips);
+
+ fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips;
+ cmd = kzalloc(fifo_size, GFP_KERNEL);
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("Temporary fifo memory alloc failed.\n");
+ return -ENOMEM;
+ }
+
+ left = clips->x1;
+ right = clips->x2;
+ top = clips->y1;
+ bottom = clips->y2;
+
+ /* skip the first clip rect */
+ for (i = 1, clips_ptr = clips + inc;
+ i < num_clips; i++, clips_ptr += inc) {
+ left = min_t(int, left, (int)clips_ptr->x1);
+ right = max_t(int, right, (int)clips_ptr->x2);
+ top = min_t(int, top, (int)clips_ptr->y1);
+ bottom = max_t(int, bottom, (int)clips_ptr->y2);
+ }
+
+ /* only need to do this once */
+ memset(cmd, 0, fifo_size);
+ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN);
+ cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
+
+ cmd->body.srcRect.left = left;
+ cmd->body.srcRect.right = right;
+ cmd->body.srcRect.top = top;
+ cmd->body.srcRect.bottom = bottom;
+
+ clips_ptr = clips;
+ blits = (SVGASignedRect *)&cmd[1];
+ for (i = 0; i < num_clips; i++, clips_ptr += inc) {
+ blits[i].left = clips_ptr->x1 - left;
+ blits[i].right = clips_ptr->x2 - left;
+ blits[i].top = clips_ptr->y1 - top;
+ blits[i].bottom = clips_ptr->y2 - top;
+ }
+
+ /* do per unit writing, reuse fifo for each */
+ for (i = 0; i < num_units; i++) {
+ struct vmw_display_unit *unit = units[i];
+ int clip_x1 = left - unit->crtc.x;
+ int clip_y1 = top - unit->crtc.y;
+ int clip_x2 = right - unit->crtc.x;
+ int clip_y2 = bottom - unit->crtc.y;
+
+ /* skip any crtcs that misses the clip region */
+ if (clip_x1 >= unit->crtc.mode.hdisplay ||
+ clip_y1 >= unit->crtc.mode.vdisplay ||
+ clip_x2 <= 0 || clip_y2 <= 0)
+ continue;
+
+ /* need to reset sid as it is changed by execbuf */
+ cmd->body.srcImage.sid = cpu_to_le32(framebuffer->user_handle);
+
+ cmd->body.destScreenId = unit->unit;
+
+ /*
+ * The blit command is a lot more resilient then the
+ * readback command when it comes to clip rects. So its
+ * okay to go out of bounds.
+ */
+
+ cmd->body.destRect.left = clip_x1;
+ cmd->body.destRect.right = clip_x2;
+ cmd->body.destRect.top = clip_y1;
+ cmd->body.destRect.bottom = clip_y2;
+ ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
+ fifo_size, 0, NULL);
+
+ if (unlikely(ret != 0))
+ break;
+ }
+
+ kfree(cmd);
+
+ return ret;
+}
+
int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
struct drm_file *file_priv,
unsigned flags, unsigned color,
@@ -444,44 +495,20 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
struct vmw_master *vmaster = vmw_master(file_priv->master);
struct vmw_framebuffer_surface *vfbs =
vmw_framebuffer_to_vfbs(framebuffer);
- struct vmw_surface *surf = vfbs->surface;
struct drm_clip_rect norect;
- SVGA3dCopyRect *cr;
- int i, inc = 1;
- int ret;
-
- struct {
- SVGA3dCmdHeader header;
- SVGA3dCmdPresent body;
- SVGA3dCopyRect cr;
- } *cmd;
+ int ret, inc = 1;
if (unlikely(vfbs->master != file_priv->master))
return -EINVAL;
+ /* Require ScreenObject support for 3D */
+ if (!dev_priv->sou_priv)
+ return -EINVAL;
+
ret = ttm_read_lock(&vmaster->lock, true);
if (unlikely(ret != 0))
return ret;
- if (!num_clips ||
- !(dev_priv->fifo.capabilities &
- SVGA_FIFO_CAP_SCREEN_OBJECT)) {
- int ret;
-
- mutex_lock(&vfbs->work_lock);
- vfbs->present_fs = true;
- ret = schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE);
- mutex_unlock(&vfbs->work_lock);
- if (ret) {
- /**
- * No work pending, Force immediate present.
- */
- vmw_framebuffer_present_fs_callback(&vfbs->d_work.work);
- }
- ttm_read_unlock(&vmaster->lock);
- return 0;
- }
-
if (!num_clips) {
num_clips = 1;
clips = &norect;
@@ -493,29 +520,10 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
inc = 2; /* skip source rects */
}
- cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr));
- if (unlikely(cmd == NULL)) {
- DRM_ERROR("Fifo reserve failed.\n");
- ttm_read_unlock(&vmaster->lock);
- return -ENOMEM;
- }
+ ret = do_surface_dirty_sou(dev_priv, file_priv, &vfbs->base,
+ flags, color,
+ clips, num_clips, inc);
- memset(cmd, 0, sizeof(*cmd));
-
- cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT);
- cmd->header.size = cpu_to_le32(sizeof(cmd->body) + num_clips * sizeof(cmd->cr));
- cmd->body.sid = cpu_to_le32(surf->res.id);
-
- for (i = 0, cr = &cmd->cr; i < num_clips; i++, cr++, clips += inc) {
- cr->x = cpu_to_le16(clips->x1);
- cr->y = cpu_to_le16(clips->y1);
- cr->srcx = cr->x;
- cr->srcy = cr->y;
- cr->w = cpu_to_le16(clips->x2 - clips->x1);
- cr->h = cpu_to_le16(clips->y2 - clips->y1);
- }
-
- vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr));
ttm_read_unlock(&vmaster->lock);
return 0;
}
@@ -540,6 +548,10 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret;
+ /* 3D is only supported on HWv8 hosts which supports screen objects */
+ if (!dev_priv->sou_priv)
+ return -ENOSYS;
+
/*
* Sanity checks.
*/
@@ -567,6 +579,9 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
case 15:
format = SVGA3D_A1R5G5B5;
break;
+ case 8:
+ format = SVGA3D_LUMINANCE8;
+ break;
default:
DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth);
return -EINVAL;
@@ -599,14 +614,11 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
vfbs->base.base.depth = mode_cmd->depth;
vfbs->base.base.width = mode_cmd->width;
vfbs->base.base.height = mode_cmd->height;
- vfbs->base.pin = &vmw_surface_dmabuf_pin;
- vfbs->base.unpin = &vmw_surface_dmabuf_unpin;
vfbs->surface = surface;
+ vfbs->base.user_handle = mode_cmd->handle;
vfbs->master = drm_master_get(file_priv->master);
- mutex_init(&vfbs->work_lock);
mutex_lock(&vmaster->fb_surf_mutex);
- INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback);
list_add_tail(&vfbs->head, &vmaster->fb_surf);
mutex_unlock(&vmaster->fb_surf_mutex);
@@ -641,48 +653,33 @@ void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer)
drm_framebuffer_cleanup(framebuffer);
vmw_dmabuf_unreference(&vfbd->buffer);
+ ttm_base_object_unref(&vfbd->base.user_obj);
kfree(vfbd);
}
-int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
- struct drm_file *file_priv,
- unsigned flags, unsigned color,
- struct drm_clip_rect *clips,
- unsigned num_clips)
+static int do_dmabuf_dirty_ldu(struct vmw_private *dev_priv,
+ struct vmw_framebuffer *framebuffer,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips, int increment)
{
- struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
- struct vmw_master *vmaster = vmw_master(file_priv->master);
- struct drm_clip_rect norect;
- int ret;
+ size_t fifo_size;
+ int i;
+
struct {
uint32_t header;
SVGAFifoCmdUpdate body;
} *cmd;
- int i, increment = 1;
-
- ret = ttm_read_lock(&vmaster->lock, true);
- if (unlikely(ret != 0))
- return ret;
-
- if (!num_clips) {
- num_clips = 1;
- clips = &norect;
- norect.x1 = norect.y1 = 0;
- norect.x2 = framebuffer->width;
- norect.y2 = framebuffer->height;
- } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
- num_clips /= 2;
- increment = 2;
- }
- cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips);
+ fifo_size = sizeof(*cmd) * num_clips;
+ cmd = vmw_fifo_reserve(dev_priv, fifo_size);
if (unlikely(cmd == NULL)) {
DRM_ERROR("Fifo reserve failed.\n");
- ttm_read_unlock(&vmaster->lock);
return -ENOMEM;
}
+ memset(cmd, 0, fifo_size);
for (i = 0; i < num_clips; i++, clips += increment) {
cmd[i].header = cpu_to_le32(SVGA_CMD_UPDATE);
cmd[i].body.x = cpu_to_le32(clips->x1);
@@ -691,57 +688,186 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
cmd[i].body.height = cpu_to_le32(clips->y2 - clips->y1);
}
- vmw_fifo_commit(dev_priv, sizeof(*cmd) * num_clips);
- ttm_read_unlock(&vmaster->lock);
-
+ vmw_fifo_commit(dev_priv, fifo_size);
return 0;
}
-static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {
- .destroy = vmw_framebuffer_dmabuf_destroy,
- .dirty = vmw_framebuffer_dmabuf_dirty,
- .create_handle = vmw_framebuffer_create_handle,
-};
-
-static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb)
+static int do_dmabuf_define_gmrfb(struct drm_file *file_priv,
+ struct vmw_private *dev_priv,
+ struct vmw_framebuffer *framebuffer)
{
- struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
- struct vmw_framebuffer_surface *vfbs =
- vmw_framebuffer_to_vfbs(&vfb->base);
- unsigned long size = vfbs->base.base.pitch * vfbs->base.base.height;
+ int depth = framebuffer->base.depth;
+ size_t fifo_size;
int ret;
- vfbs->buffer = kzalloc(sizeof(*vfbs->buffer), GFP_KERNEL);
- if (unlikely(vfbs->buffer == NULL))
+ struct {
+ uint32_t header;
+ SVGAFifoCmdDefineGMRFB body;
+ } *cmd;
+
+ /* Emulate RGBA support, contrary to svga_reg.h this is not
+ * supported by hosts. This is only a problem if we are reading
+ * this value later and expecting what we uploaded back.
+ */
+ if (depth == 32)
+ depth = 24;
+
+ fifo_size = sizeof(*cmd);
+ cmd = kmalloc(fifo_size, GFP_KERNEL);
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("Failed to allocate temporary cmd buffer.\n");
return -ENOMEM;
+ }
- vmw_overlay_pause_all(dev_priv);
- ret = vmw_dmabuf_init(dev_priv, vfbs->buffer, size,
- &vmw_vram_ne_placement,
- false, &vmw_dmabuf_bo_free);
- vmw_overlay_resume_all(dev_priv);
+ memset(cmd, 0, fifo_size);
+ cmd->header = SVGA_CMD_DEFINE_GMRFB;
+ cmd->body.format.bitsPerPixel = framebuffer->base.bits_per_pixel;
+ cmd->body.format.colorDepth = depth;
+ cmd->body.format.reserved = 0;
+ cmd->body.bytesPerLine = framebuffer->base.pitch;
+ cmd->body.ptr.gmrId = framebuffer->user_handle;
+ cmd->body.ptr.offset = 0;
+
+ ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
+ fifo_size, 0, NULL);
+
+ kfree(cmd);
+
+ return ret;
+}
+
+static int do_dmabuf_dirty_sou(struct drm_file *file_priv,
+ struct vmw_private *dev_priv,
+ struct vmw_framebuffer *framebuffer,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips, int increment)
+{
+ struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
+ struct drm_clip_rect *clips_ptr;
+ int i, k, num_units, ret;
+ struct drm_crtc *crtc;
+ size_t fifo_size;
+
+ struct {
+ uint32_t header;
+ SVGAFifoCmdBlitGMRFBToScreen body;
+ } *blits;
+
+ ret = do_dmabuf_define_gmrfb(file_priv, dev_priv, framebuffer);
if (unlikely(ret != 0))
- vfbs->buffer = NULL;
+ return ret; /* define_gmrfb prints warnings */
+
+ fifo_size = sizeof(*blits) * num_clips;
+ blits = kmalloc(fifo_size, GFP_KERNEL);
+ if (unlikely(blits == NULL)) {
+ DRM_ERROR("Failed to allocate temporary cmd buffer.\n");
+ return -ENOMEM;
+ }
+
+ num_units = 0;
+ list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
+ if (crtc->fb != &framebuffer->base)
+ continue;
+ units[num_units++] = vmw_crtc_to_du(crtc);
+ }
+
+ for (k = 0; k < num_units; k++) {
+ struct vmw_display_unit *unit = units[k];
+ int hit_num = 0;
+
+ clips_ptr = clips;
+ for (i = 0; i < num_clips; i++, clips_ptr += increment) {
+ int clip_x1 = clips_ptr->x1 - unit->crtc.x;
+ int clip_y1 = clips_ptr->y1 - unit->crtc.y;
+ int clip_x2 = clips_ptr->x2 - unit->crtc.x;
+ int clip_y2 = clips_ptr->y2 - unit->crtc.y;
+
+ /* skip any crtcs that misses the clip region */
+ if (clip_x1 >= unit->crtc.mode.hdisplay ||
+ clip_y1 >= unit->crtc.mode.vdisplay ||
+ clip_x2 <= 0 || clip_y2 <= 0)
+ continue;
+
+ blits[hit_num].header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN;
+ blits[hit_num].body.destScreenId = unit->unit;
+ blits[hit_num].body.srcOrigin.x = clips_ptr->x1;
+ blits[hit_num].body.srcOrigin.y = clips_ptr->y1;
+ blits[hit_num].body.destRect.left = clip_x1;
+ blits[hit_num].body.destRect.top = clip_y1;
+ blits[hit_num].body.destRect.right = clip_x2;
+ blits[hit_num].body.destRect.bottom = clip_y2;
+ hit_num++;
+ }
+
+ /* no clips hit the crtc */
+ if (hit_num == 0)
+ continue;
+
+ fifo_size = sizeof(*blits) * hit_num;
+ ret = vmw_execbuf_process(file_priv, dev_priv, NULL, blits,
+ fifo_size, 0, NULL);
+
+ if (unlikely(ret != 0))
+ break;
+ }
+
+ kfree(blits);
return ret;
}
-static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb)
+int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
+ struct drm_file *file_priv,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips)
{
- struct ttm_buffer_object *bo;
- struct vmw_framebuffer_surface *vfbs =
- vmw_framebuffer_to_vfbs(&vfb->base);
+ struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
+ struct vmw_framebuffer_dmabuf *vfbd =
+ vmw_framebuffer_to_vfbd(framebuffer);
+ struct drm_clip_rect norect;
+ int ret, increment = 1;
- if (unlikely(vfbs->buffer == NULL))
- return 0;
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (unlikely(ret != 0))
+ return ret;
- bo = &vfbs->buffer->base;
- ttm_bo_unref(&bo);
- vfbs->buffer = NULL;
+ if (!num_clips) {
+ num_clips = 1;
+ clips = &norect;
+ norect.x1 = norect.y1 = 0;
+ norect.x2 = framebuffer->width;
+ norect.y2 = framebuffer->height;
+ } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
+ num_clips /= 2;
+ increment = 2;
+ }
- return 0;
+ if (dev_priv->ldu_priv) {
+ ret = do_dmabuf_dirty_ldu(dev_priv, &vfbd->base,
+ flags, color,
+ clips, num_clips, increment);
+ } else {
+ ret = do_dmabuf_dirty_sou(file_priv, dev_priv, &vfbd->base,
+ flags, color,
+ clips, num_clips, increment);
+ }
+
+ ttm_read_unlock(&vmaster->lock);
+ return ret;
}
+static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {
+ .destroy = vmw_framebuffer_dmabuf_destroy,
+ .dirty = vmw_framebuffer_dmabuf_dirty,
+ .create_handle = vmw_framebuffer_create_handle,
+};
+
+/**
+ * Pin the dmabuffer to the start of vram.
+ */
static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb)
{
struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
@@ -749,10 +875,12 @@ static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb)
vmw_framebuffer_to_vfbd(&vfb->base);
int ret;
+ /* This code should not be used with screen objects */
+ BUG_ON(dev_priv->sou_priv);
vmw_overlay_pause_all(dev_priv);
- ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer);
+ ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer, true, false);
vmw_overlay_resume_all(dev_priv);
@@ -772,7 +900,7 @@ static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb)
return 0;
}
- return vmw_dmabuf_from_vram(dev_priv, vfbd->buffer);
+ return vmw_dmabuf_unpin(dev_priv, vfbd->buffer, false);
}
static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
@@ -794,6 +922,33 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
return -EINVAL;
}
+ /* Limited framebuffer color depth support for screen objects */
+ if (dev_priv->sou_priv) {
+ switch (mode_cmd->depth) {
+ case 32:
+ case 24:
+ /* Only support 32 bpp for 32 and 24 depth fbs */
+ if (mode_cmd->bpp == 32)
+ break;
+
+ DRM_ERROR("Invalid color depth/bbp: %d %d\n",
+ mode_cmd->depth, mode_cmd->bpp);
+ return -EINVAL;
+ case 16:
+ case 15:
+ /* Only support 16 bpp for 16 and 15 depth fbs */
+ if (mode_cmd->bpp == 16)
+ break;
+
+ DRM_ERROR("Invalid color depth/bbp: %d %d\n",
+ mode_cmd->depth, mode_cmd->bpp);
+ return -EINVAL;
+ default:
+ DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth);
+ return -EINVAL;
+ }
+ }
+
vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL);
if (!vfbd) {
ret = -ENOMEM;
@@ -815,9 +970,13 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
vfbd->base.base.depth = mode_cmd->depth;
vfbd->base.base.width = mode_cmd->width;
vfbd->base.base.height = mode_cmd->height;
- vfbd->base.pin = vmw_framebuffer_dmabuf_pin;
- vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin;
+ if (!dev_priv->sou_priv) {
+ vfbd->base.pin = vmw_framebuffer_dmabuf_pin;
+ vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin;
+ }
+ vfbd->base.dmabuf = true;
vfbd->buffer = dmabuf;
+ vfbd->base.user_handle = mode_cmd->handle;
*out = &vfbd->base;
return 0;
@@ -843,6 +1002,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
struct vmw_framebuffer *vfb = NULL;
struct vmw_surface *surface = NULL;
struct vmw_dma_buffer *bo = NULL;
+ struct ttm_base_object *user_obj;
u64 required_size;
int ret;
@@ -855,7 +1015,22 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
required_size = mode_cmd->pitch * mode_cmd->height;
if (unlikely(required_size > (u64) dev_priv->vram_size)) {
DRM_ERROR("VRAM size is too small for requested mode.\n");
- return NULL;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /*
+ * Take a reference on the user object of the resource
+ * backing the kms fb. This ensures that user-space handle
+ * lookups on that resource will always work as long as
+ * it's registered with a kms framebuffer. This is important,
+ * since vmw_execbuf_process identifies resources in the
+ * command stream using user-space handles.
+ */
+
+ user_obj = ttm_base_object_lookup(tfile, mode_cmd->handle);
+ if (unlikely(user_obj == NULL)) {
+ DRM_ERROR("Could not locate requested kms frame buffer.\n");
+ return ERR_PTR(-ENOENT);
}
/**
@@ -878,8 +1053,10 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
if (ret) {
DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
+ ttm_base_object_unref(&user_obj);
return ERR_PTR(ret);
- }
+ } else
+ vfb->user_obj = user_obj;
return &vfb->base;
try_dmabuf:
@@ -899,8 +1076,10 @@ try_dmabuf:
if (ret) {
DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
+ ttm_base_object_unref(&user_obj);
return ERR_PTR(ret);
- }
+ } else
+ vfb->user_obj = user_obj;
return &vfb->base;
@@ -908,6 +1087,7 @@ err_not_scanout:
DRM_ERROR("surface not marked as scanout\n");
/* vmw_user_surface_lookup takes one ref */
vmw_surface_unreference(&surface);
+ ttm_base_object_unref(&user_obj);
return ERR_PTR(-EINVAL);
}
@@ -916,6 +1096,210 @@ static struct drm_mode_config_funcs vmw_kms_funcs = {
.fb_create = vmw_kms_fb_create,
};
+int vmw_kms_present(struct vmw_private *dev_priv,
+ struct drm_file *file_priv,
+ struct vmw_framebuffer *vfb,
+ struct vmw_surface *surface,
+ uint32_t sid,
+ int32_t destX, int32_t destY,
+ struct drm_vmw_rect *clips,
+ uint32_t num_clips)
+{
+ struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
+ struct drm_crtc *crtc;
+ size_t fifo_size;
+ int i, k, num_units;
+ int ret = 0; /* silence warning */
+
+ struct {
+ SVGA3dCmdHeader header;
+ SVGA3dCmdBlitSurfaceToScreen body;
+ } *cmd;
+ SVGASignedRect *blits;
+
+ num_units = 0;
+ list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
+ if (crtc->fb != &vfb->base)
+ continue;
+ units[num_units++] = vmw_crtc_to_du(crtc);
+ }
+
+ BUG_ON(surface == NULL);
+ BUG_ON(!clips || !num_clips);
+
+ fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips;
+ cmd = kmalloc(fifo_size, GFP_KERNEL);
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("Failed to allocate temporary fifo memory.\n");
+ return -ENOMEM;
+ }
+
+ /* only need to do this once */
+ memset(cmd, 0, fifo_size);
+ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN);
+ cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
+
+ cmd->body.srcRect.left = 0;
+ cmd->body.srcRect.right = surface->sizes[0].width;
+ cmd->body.srcRect.top = 0;
+ cmd->body.srcRect.bottom = surface->sizes[0].height;
+
+ blits = (SVGASignedRect *)&cmd[1];
+ for (i = 0; i < num_clips; i++) {
+ blits[i].left = clips[i].x;
+ blits[i].right = clips[i].x + clips[i].w;
+ blits[i].top = clips[i].y;
+ blits[i].bottom = clips[i].y + clips[i].h;
+ }
+
+ for (k = 0; k < num_units; k++) {
+ struct vmw_display_unit *unit = units[k];
+ int clip_x1 = destX - unit->crtc.x;
+ int clip_y1 = destY - unit->crtc.y;
+ int clip_x2 = clip_x1 + surface->sizes[0].width;
+ int clip_y2 = clip_y1 + surface->sizes[0].height;
+
+ /* skip any crtcs that misses the clip region */
+ if (clip_x1 >= unit->crtc.mode.hdisplay ||
+ clip_y1 >= unit->crtc.mode.vdisplay ||
+ clip_x2 <= 0 || clip_y2 <= 0)
+ continue;
+
+ /* need to reset sid as it is changed by execbuf */
+ cmd->body.srcImage.sid = sid;
+
+ cmd->body.destScreenId = unit->unit;
+
+ /*
+ * The blit command is a lot more resilient then the
+ * readback command when it comes to clip rects. So its
+ * okay to go out of bounds.
+ */
+
+ cmd->body.destRect.left = clip_x1;
+ cmd->body.destRect.right = clip_x2;
+ cmd->body.destRect.top = clip_y1;
+ cmd->body.destRect.bottom = clip_y2;
+
+ ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
+ fifo_size, 0, NULL);
+
+ if (unlikely(ret != 0))
+ break;
+ }
+
+ kfree(cmd);
+
+ return ret;
+}
+
+int vmw_kms_readback(struct vmw_private *dev_priv,
+ struct drm_file *file_priv,
+ struct vmw_framebuffer *vfb,
+ struct drm_vmw_fence_rep __user *user_fence_rep,
+ struct drm_vmw_rect *clips,
+ uint32_t num_clips)
+{
+ struct vmw_framebuffer_dmabuf *vfbd =
+ vmw_framebuffer_to_vfbd(&vfb->base);
+ struct vmw_dma_buffer *dmabuf = vfbd->buffer;
+ struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
+ struct drm_crtc *crtc;
+ size_t fifo_size;
+ int i, k, ret, num_units, blits_pos;
+
+ struct {
+ uint32_t header;
+ SVGAFifoCmdDefineGMRFB body;
+ } *cmd;
+ struct {
+ uint32_t header;
+ SVGAFifoCmdBlitScreenToGMRFB body;
+ } *blits;
+
+ num_units = 0;
+ list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
+ if (crtc->fb != &vfb->base)
+ continue;
+ units[num_units++] = vmw_crtc_to_du(crtc);
+ }
+
+ BUG_ON(dmabuf == NULL);
+ BUG_ON(!clips || !num_clips);
+
+ /* take a safe guess at fifo size */
+ fifo_size = sizeof(*cmd) + sizeof(*blits) * num_clips * num_units;
+ cmd = kmalloc(fifo_size, GFP_KERNEL);
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("Failed to allocate temporary fifo memory.\n");
+ return -ENOMEM;
+ }
+
+ memset(cmd, 0, fifo_size);
+ cmd->header = SVGA_CMD_DEFINE_GMRFB;
+ cmd->body.format.bitsPerPixel = vfb->base.bits_per_pixel;
+ cmd->body.format.colorDepth = vfb->base.depth;
+ cmd->body.format.reserved = 0;
+ cmd->body.bytesPerLine = vfb->base.pitch;
+ cmd->body.ptr.gmrId = vfb->user_handle;
+ cmd->body.ptr.offset = 0;
+
+ blits = (void *)&cmd[1];
+ blits_pos = 0;
+ for (i = 0; i < num_units; i++) {
+ struct drm_vmw_rect *c = clips;
+ for (k = 0; k < num_clips; k++, c++) {
+ /* transform clip coords to crtc origin based coords */
+ int clip_x1 = c->x - units[i]->crtc.x;
+ int clip_x2 = c->x - units[i]->crtc.x + c->w;
+ int clip_y1 = c->y - units[i]->crtc.y;
+ int clip_y2 = c->y - units[i]->crtc.y + c->h;
+ int dest_x = c->x;
+ int dest_y = c->y;
+
+ /* compensate for clipping, we negate
+ * a negative number and add that.
+ */
+ if (clip_x1 < 0)
+ dest_x += -clip_x1;
+ if (clip_y1 < 0)
+ dest_y += -clip_y1;
+
+ /* clip */
+ clip_x1 = max(clip_x1, 0);
+ clip_y1 = max(clip_y1, 0);
+ clip_x2 = min(clip_x2, units[i]->crtc.mode.hdisplay);
+ clip_y2 = min(clip_y2, units[i]->crtc.mode.vdisplay);
+
+ /* and cull any rects that misses the crtc */
+ if (clip_x1 >= units[i]->crtc.mode.hdisplay ||
+ clip_y1 >= units[i]->crtc.mode.vdisplay ||
+ clip_x2 <= 0 || clip_y2 <= 0)
+ continue;
+
+ blits[blits_pos].header = SVGA_CMD_BLIT_SCREEN_TO_GMRFB;
+ blits[blits_pos].body.srcScreenId = units[i]->unit;
+ blits[blits_pos].body.destOrigin.x = dest_x;
+ blits[blits_pos].body.destOrigin.y = dest_y;
+
+ blits[blits_pos].body.srcRect.left = clip_x1;
+ blits[blits_pos].body.srcRect.top = clip_y1;
+ blits[blits_pos].body.srcRect.right = clip_x2;
+ blits[blits_pos].body.srcRect.bottom = clip_y2;
+ blits_pos++;
+ }
+ }
+ /* reset size here and use calculated exact size from loops */
+ fifo_size = sizeof(*cmd) + sizeof(*blits) * blits_pos;
+
+ ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, fifo_size,
+ 0, user_fence_rep);
+
+ kfree(cmd);
+
+ return ret;
+}
+
int vmw_kms_init(struct vmw_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
@@ -929,7 +1313,9 @@ int vmw_kms_init(struct vmw_private *dev_priv)
dev->mode_config.max_width = 8192;
dev->mode_config.max_height = 8192;
- ret = vmw_kms_init_legacy_display_system(dev_priv);
+ ret = vmw_kms_init_screen_object_display(dev_priv);
+ if (ret) /* Fallback */
+ (void)vmw_kms_init_legacy_display_system(dev_priv);
return 0;
}
@@ -942,7 +1328,10 @@ int vmw_kms_close(struct vmw_private *dev_priv)
* drm_encoder_cleanup which takes the lock we deadlock.
*/
drm_mode_config_cleanup(dev_priv->dev);
- vmw_kms_close_legacy_display_system(dev_priv);
+ if (dev_priv->sou_priv)
+ vmw_kms_close_screen_object_display(dev_priv);
+ else
+ vmw_kms_close_legacy_display_system(dev_priv);
return 0;
}
@@ -987,9 +1376,9 @@ out:
return ret;
}
-void vmw_kms_write_svga(struct vmw_private *vmw_priv,
+int vmw_kms_write_svga(struct vmw_private *vmw_priv,
unsigned width, unsigned height, unsigned pitch,
- unsigned bbp, unsigned depth)
+ unsigned bpp, unsigned depth)
{
if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch);
@@ -997,11 +1386,15 @@ void vmw_kms_write_svga(struct vmw_private *vmw_priv,
iowrite32(pitch, vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK);
vmw_write(vmw_priv, SVGA_REG_WIDTH, width);
vmw_write(vmw_priv, SVGA_REG_HEIGHT, height);
- vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bbp);
- vmw_write(vmw_priv, SVGA_REG_DEPTH, depth);
- vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000);
- vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00);
- vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff);
+ vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bpp);
+
+ if (vmw_read(vmw_priv, SVGA_REG_DEPTH) != depth) {
+ DRM_ERROR("Invalid depth %u for %u bpp, host expects %u\n",
+ depth, bpp, vmw_read(vmw_priv, SVGA_REG_DEPTH));
+ return -EINVAL;
+ }
+
+ return 0;
}
int vmw_kms_save_vga(struct vmw_private *vmw_priv)
@@ -1011,12 +1404,7 @@ int vmw_kms_save_vga(struct vmw_private *vmw_priv)
vmw_priv->vga_width = vmw_read(vmw_priv, SVGA_REG_WIDTH);
vmw_priv->vga_height = vmw_read(vmw_priv, SVGA_REG_HEIGHT);
- vmw_priv->vga_depth = vmw_read(vmw_priv, SVGA_REG_DEPTH);
vmw_priv->vga_bpp = vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL);
- vmw_priv->vga_pseudo = vmw_read(vmw_priv, SVGA_REG_PSEUDOCOLOR);
- vmw_priv->vga_red_mask = vmw_read(vmw_priv, SVGA_REG_RED_MASK);
- vmw_priv->vga_blue_mask = vmw_read(vmw_priv, SVGA_REG_BLUE_MASK);
- vmw_priv->vga_green_mask = vmw_read(vmw_priv, SVGA_REG_GREEN_MASK);
if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
vmw_priv->vga_pitchlock =
vmw_read(vmw_priv, SVGA_REG_PITCHLOCK);
@@ -1065,12 +1453,7 @@ int vmw_kms_restore_vga(struct vmw_private *vmw_priv)
vmw_write(vmw_priv, SVGA_REG_WIDTH, vmw_priv->vga_width);
vmw_write(vmw_priv, SVGA_REG_HEIGHT, vmw_priv->vga_height);
- vmw_write(vmw_priv, SVGA_REG_DEPTH, vmw_priv->vga_depth);
vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, vmw_priv->vga_bpp);
- vmw_write(vmw_priv, SVGA_REG_PSEUDOCOLOR, vmw_priv->vga_pseudo);
- vmw_write(vmw_priv, SVGA_REG_RED_MASK, vmw_priv->vga_red_mask);
- vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, vmw_priv->vga_green_mask);
- vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, vmw_priv->vga_blue_mask);
if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
vmw_write(vmw_priv, SVGA_REG_PITCHLOCK,
vmw_priv->vga_pitchlock);
@@ -1095,6 +1478,312 @@ int vmw_kms_restore_vga(struct vmw_private *vmw_priv)
return 0;
}
+bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
+ uint32_t pitch,
+ uint32_t height)
+{
+ return ((u64) pitch * (u64) height) < (u64) dev_priv->vram_size;
+}
+
+
+/**
+ * Function called by DRM code called with vbl_lock held.
+ */
+u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc)
+{
+ return 0;
+}
+
+/**
+ * Function called by DRM code called with vbl_lock held.
+ */
+int vmw_enable_vblank(struct drm_device *dev, int crtc)
+{
+ return -ENOSYS;
+}
+
+/**
+ * Function called by DRM code called with vbl_lock held.
+ */
+void vmw_disable_vblank(struct drm_device *dev, int crtc)
+{
+}
+
+
+/*
+ * Small shared kms functions.
+ */
+
+int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num,
+ struct drm_vmw_rect *rects)
+{
+ struct drm_device *dev = dev_priv->dev;
+ struct vmw_display_unit *du;
+ struct drm_connector *con;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+#if 0
+ {
+ unsigned int i;
+
+ DRM_INFO("%s: new layout ", __func__);
+ for (i = 0; i < num; i++)
+ DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y,
+ rects[i].w, rects[i].h);
+ DRM_INFO("\n");
+ }
+#endif
+
+ list_for_each_entry(con, &dev->mode_config.connector_list, head) {
+ du = vmw_connector_to_du(con);
+ if (num > du->unit) {
+ du->pref_width = rects[du->unit].w;
+ du->pref_height = rects[du->unit].h;
+ du->pref_active = true;
+ du->gui_x = rects[du->unit].x;
+ du->gui_y = rects[du->unit].y;
+ } else {
+ du->pref_width = 800;
+ du->pref_height = 600;
+ du->pref_active = false;
+ }
+ con->status = vmw_du_connector_detect(con, true);
+ }
+
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return 0;
+}
+
+void vmw_du_crtc_save(struct drm_crtc *crtc)
+{
+}
+
+void vmw_du_crtc_restore(struct drm_crtc *crtc)
+{
+}
+
+void vmw_du_crtc_gamma_set(struct drm_crtc *crtc,
+ u16 *r, u16 *g, u16 *b,
+ uint32_t start, uint32_t size)
+{
+ struct vmw_private *dev_priv = vmw_priv(crtc->dev);
+ int i;
+
+ for (i = 0; i < size; i++) {
+ DRM_DEBUG("%d r/g/b = 0x%04x / 0x%04x / 0x%04x\n", i,
+ r[i], g[i], b[i]);
+ vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 0, r[i] >> 8);
+ vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 1, g[i] >> 8);
+ vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 2, b[i] >> 8);
+ }
+}
+
+void vmw_du_connector_dpms(struct drm_connector *connector, int mode)
+{
+}
+
+void vmw_du_connector_save(struct drm_connector *connector)
+{
+}
+
+void vmw_du_connector_restore(struct drm_connector *connector)
+{
+}
+
+enum drm_connector_status
+vmw_du_connector_detect(struct drm_connector *connector, bool force)
+{
+ uint32_t num_displays;
+ struct drm_device *dev = connector->dev;
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ struct vmw_display_unit *du = vmw_connector_to_du(connector);
+
+ mutex_lock(&dev_priv->hw_mutex);
+ num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS);
+ mutex_unlock(&dev_priv->hw_mutex);
+
+ return ((vmw_connector_to_du(connector)->unit < num_displays &&
+ du->pref_active) ?
+ connector_status_connected : connector_status_disconnected);
+}
+
+static struct drm_display_mode vmw_kms_connector_builtin[] = {
+ /* 640x480@60Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
+ 752, 800, 0, 480, 489, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 800x600@60Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1024x768@60Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+ 1184, 1344, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 1152x864@75Hz */
+ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
+ 1344, 1600, 0, 864, 865, 868, 900, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x768@60Hz */
+ { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
+ 1472, 1664, 0, 768, 771, 778, 798, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x800@60Hz */
+ { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
+ 1480, 1680, 0, 800, 803, 809, 831, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 1280x960@60Hz */
+ { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
+ 1488, 1800, 0, 960, 961, 964, 1000, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x1024@60Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1360x768@60Hz */
+ { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
+ 1536, 1792, 0, 768, 771, 777, 795, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x1050@60Hz */
+ { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
+ 1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x900@60Hz */
+ { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
+ 1672, 1904, 0, 900, 903, 909, 934, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@60Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1680x1050@60Hz */
+ { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
+ 1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1792x1344@60Hz */
+ { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
+ 2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1853x1392@60Hz */
+ { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
+ 2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1200@60Hz */
+ { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
+ 2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1440@60Hz */
+ { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
+ 2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 2560x1600@60Hz */
+ { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
+ 3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* Terminate */
+ { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) },
+};
+
+/**
+ * vmw_guess_mode_timing - Provide fake timings for a
+ * 60Hz vrefresh mode.
+ *
+ * @mode - Pointer to a struct drm_display_mode with hdisplay and vdisplay
+ * members filled in.
+ */
+static void vmw_guess_mode_timing(struct drm_display_mode *mode)
+{
+ mode->hsync_start = mode->hdisplay + 50;
+ mode->hsync_end = mode->hsync_start + 50;
+ mode->htotal = mode->hsync_end + 50;
+
+ mode->vsync_start = mode->vdisplay + 50;
+ mode->vsync_end = mode->vsync_start + 50;
+ mode->vtotal = mode->vsync_end + 50;
+
+ mode->clock = (u32)mode->htotal * (u32)mode->vtotal / 100 * 6;
+ mode->vrefresh = drm_mode_vrefresh(mode);
+}
+
+
+int vmw_du_connector_fill_modes(struct drm_connector *connector,
+ uint32_t max_width, uint32_t max_height)
+{
+ struct vmw_display_unit *du = vmw_connector_to_du(connector);
+ struct drm_device *dev = connector->dev;
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ struct drm_display_mode *mode = NULL;
+ struct drm_display_mode *bmode;
+ struct drm_display_mode prefmode = { DRM_MODE("preferred",
+ DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
+ };
+ int i;
+
+ /* Add preferred mode */
+ {
+ mode = drm_mode_duplicate(dev, &prefmode);
+ if (!mode)
+ return 0;
+ mode->hdisplay = du->pref_width;
+ mode->vdisplay = du->pref_height;
+ vmw_guess_mode_timing(mode);
+
+ if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2,
+ mode->vdisplay)) {
+ drm_mode_probed_add(connector, mode);
+ } else {
+ drm_mode_destroy(dev, mode);
+ mode = NULL;
+ }
+
+ if (du->pref_mode) {
+ list_del_init(&du->pref_mode->head);
+ drm_mode_destroy(dev, du->pref_mode);
+ }
+
+ /* mode might be null here, this is intended */
+ du->pref_mode = mode;
+ }
+
+ for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) {
+ bmode = &vmw_kms_connector_builtin[i];
+ if (bmode->hdisplay > max_width ||
+ bmode->vdisplay > max_height)
+ continue;
+
+ if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2,
+ bmode->vdisplay))
+ continue;
+
+ mode = drm_mode_duplicate(dev, bmode);
+ if (!mode)
+ return 0;
+ mode->vrefresh = drm_mode_vrefresh(mode);
+
+ drm_mode_probed_add(connector, mode);
+ }
+
+ /* Move the prefered mode first, help apps pick the right mode. */
+ if (du->pref_mode)
+ list_move(&du->pref_mode->head, &connector->probed_modes);
+
+ drm_mode_connector_list_update(connector);
+
+ return 1;
+}
+
+int vmw_du_connector_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t val)
+{
+ return 0;
+}
+
+
int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -1106,6 +1795,8 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
struct drm_vmw_rect *rects;
unsigned rects_size;
int ret;
+ int i;
+ struct drm_mode_config *mode_config = &dev->mode_config;
ret = ttm_read_lock(&vmaster->lock, true);
if (unlikely(ret != 0))
@@ -1113,7 +1804,7 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
if (!arg->num_outputs) {
struct drm_vmw_rect def_rect = {0, 0, 800, 600};
- vmw_kms_ldu_update_layout(dev_priv, 1, &def_rect);
+ vmw_du_update_layout(dev_priv, 1, &def_rect);
goto out_unlock;
}
@@ -1132,7 +1823,18 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
goto out_free;
}
- vmw_kms_ldu_update_layout(dev_priv, arg->num_outputs, rects);
+ for (i = 0; i < arg->num_outputs; ++i) {
+ if (rects->x < 0 ||
+ rects->y < 0 ||
+ rects->x + rects->w > mode_config->max_width ||
+ rects->y + rects->h > mode_config->max_height) {
+ DRM_ERROR("Invalid GUI layout.\n");
+ ret = -EINVAL;
+ goto out_free;
+ }
+ }
+
+ vmw_du_update_layout(dev_priv, arg->num_outputs, rects);
out_free:
kfree(rects);
@@ -1140,15 +1842,3 @@ out_unlock:
ttm_read_unlock(&vmaster->lock);
return ret;
}
-
-bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
- uint32_t pitch,
- uint32_t height)
-{
- return ((u64) pitch * (u64) height) < (u64) dev_priv->vram_size;
-}
-
-u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc)
-{
- return 0;
-}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
index 8a398a0339b6..af8e6e5bd964 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -31,6 +31,8 @@
#include "drmP.h"
#include "vmwgfx_drv.h"
+#define VMWGFX_NUM_DISPLAY_UNITS 8
+
#define vmw_framebuffer_to_vfb(x) \
container_of(x, struct vmw_framebuffer, base)
@@ -45,6 +47,9 @@ struct vmw_framebuffer {
struct drm_framebuffer base;
int (*pin)(struct vmw_framebuffer *fb);
int (*unpin)(struct vmw_framebuffer *fb);
+ bool dmabuf;
+ struct ttm_base_object *user_obj;
+ uint32_t user_handle;
};
@@ -83,22 +88,65 @@ struct vmw_display_unit {
int hotspot_y;
unsigned unit;
+
+ /*
+ * Prefered mode tracking.
+ */
+ unsigned pref_width;
+ unsigned pref_height;
+ bool pref_active;
+ struct drm_display_mode *pref_mode;
+
+ /*
+ * Gui positioning
+ */
+ int gui_x;
+ int gui_y;
+ bool is_implicit;
};
+#define vmw_crtc_to_du(x) \
+ container_of(x, struct vmw_display_unit, crtc)
+#define vmw_connector_to_du(x) \
+ container_of(x, struct vmw_display_unit, connector)
+
+
/*
* Shared display unit functions - vmwgfx_kms.c
*/
void vmw_display_unit_cleanup(struct vmw_display_unit *du);
+void vmw_du_crtc_save(struct drm_crtc *crtc);
+void vmw_du_crtc_restore(struct drm_crtc *crtc);
+void vmw_du_crtc_gamma_set(struct drm_crtc *crtc,
+ u16 *r, u16 *g, u16 *b,
+ uint32_t start, uint32_t size);
int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
uint32_t handle, uint32_t width, uint32_t height);
int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y);
+void vmw_du_connector_dpms(struct drm_connector *connector, int mode);
+void vmw_du_connector_save(struct drm_connector *connector);
+void vmw_du_connector_restore(struct drm_connector *connector);
+enum drm_connector_status
+vmw_du_connector_detect(struct drm_connector *connector, bool force);
+int vmw_du_connector_fill_modes(struct drm_connector *connector,
+ uint32_t max_width, uint32_t max_height);
+int vmw_du_connector_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t val);
+
/*
* Legacy display unit functions - vmwgfx_ldu.c
*/
int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv);
int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv);
-int vmw_kms_ldu_update_layout(struct vmw_private *dev_priv, unsigned num,
+
+/*
+ * Screen Objects display functions - vmwgfx_scrn.c
+ */
+int vmw_kms_init_screen_object_display(struct vmw_private *dev_priv);
+int vmw_kms_close_screen_object_display(struct vmw_private *dev_priv);
+int vmw_kms_sou_update_layout(struct vmw_private *dev_priv, unsigned num,
struct drm_vmw_rect *rects);
#endif
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index b3a2cd5118d7..90c5e3928491 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -27,7 +27,6 @@
#include "vmwgfx_kms.h"
-#define VMWGFX_LDU_NUM_DU 8
#define vmw_crtc_to_ldu(x) \
container_of(x, struct vmw_legacy_display_unit, base.crtc)
@@ -51,11 +50,6 @@ struct vmw_legacy_display {
struct vmw_legacy_display_unit {
struct vmw_display_unit base;
- unsigned pref_width;
- unsigned pref_height;
- bool pref_active;
- struct drm_display_mode *pref_mode;
-
struct list_head active;
};
@@ -71,20 +65,6 @@ static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu)
* Legacy Display Unit CRTC functions
*/
-static void vmw_ldu_crtc_save(struct drm_crtc *crtc)
-{
-}
-
-static void vmw_ldu_crtc_restore(struct drm_crtc *crtc)
-{
-}
-
-static void vmw_ldu_crtc_gamma_set(struct drm_crtc *crtc,
- u16 *r, u16 *g, u16 *b,
- uint32_t start, uint32_t size)
-{
-}
-
static void vmw_ldu_crtc_destroy(struct drm_crtc *crtc)
{
vmw_ldu_destroy(vmw_crtc_to_ldu(crtc));
@@ -114,10 +94,8 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv)
return 0;
fb = entry->base.crtc.fb;
- vmw_kms_write_svga(dev_priv, w, h, fb->pitch,
- fb->bits_per_pixel, fb->depth);
-
- return 0;
+ return vmw_kms_write_svga(dev_priv, w, h, fb->pitch,
+ fb->bits_per_pixel, fb->depth);
}
if (!list_empty(&lds->active)) {
@@ -265,9 +243,7 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
vmw_ldu_del_active(dev_priv, ldu);
- vmw_ldu_commit_list(dev_priv);
-
- return 0;
+ return vmw_ldu_commit_list(dev_priv);
}
@@ -292,21 +268,20 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
vmw_ldu_add_active(dev_priv, ldu, vfb);
- vmw_ldu_commit_list(dev_priv);
-
- return 0;
+ return vmw_ldu_commit_list(dev_priv);
}
static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
- .save = vmw_ldu_crtc_save,
- .restore = vmw_ldu_crtc_restore,
+ .save = vmw_du_crtc_save,
+ .restore = vmw_du_crtc_restore,
.cursor_set = vmw_du_crtc_cursor_set,
.cursor_move = vmw_du_crtc_cursor_move,
- .gamma_set = vmw_ldu_crtc_gamma_set,
+ .gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_ldu_crtc_destroy,
.set_config = vmw_ldu_crtc_set_config,
};
+
/*
* Legacy Display Unit encoder functions
*/
@@ -324,183 +299,18 @@ static struct drm_encoder_funcs vmw_legacy_encoder_funcs = {
* Legacy Display Unit connector functions
*/
-static void vmw_ldu_connector_dpms(struct drm_connector *connector, int mode)
-{
-}
-
-static void vmw_ldu_connector_save(struct drm_connector *connector)
-{
-}
-
-static void vmw_ldu_connector_restore(struct drm_connector *connector)
-{
-}
-
-static enum drm_connector_status
- vmw_ldu_connector_detect(struct drm_connector *connector,
- bool force)
-{
- if (vmw_connector_to_ldu(connector)->pref_active)
- return connector_status_connected;
- return connector_status_disconnected;
-}
-
-static const struct drm_display_mode vmw_ldu_connector_builtin[] = {
- /* 640x480@60Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
- 752, 800, 0, 480, 489, 492, 525, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 800x600@60Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
- 968, 1056, 0, 600, 601, 605, 628, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1024x768@60Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
- 1184, 1344, 0, 768, 771, 777, 806, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 1152x864@75Hz */
- { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
- 1344, 1600, 0, 864, 865, 868, 900, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x768@60Hz */
- { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
- 1472, 1664, 0, 768, 771, 778, 798, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x800@60Hz */
- { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
- 1480, 1680, 0, 800, 803, 809, 831, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 1280x960@60Hz */
- { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
- 1488, 1800, 0, 960, 961, 964, 1000, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x1024@60Hz */
- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
- 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1360x768@60Hz */
- { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
- 1536, 1792, 0, 768, 771, 777, 795, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x1050@60Hz */
- { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
- 1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x900@60Hz */
- { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
- 1672, 1904, 0, 900, 903, 909, 934, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@60Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1680x1050@60Hz */
- { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
- 1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1792x1344@60Hz */
- { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
- 2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1853x1392@60Hz */
- { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
- 2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1200@60Hz */
- { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
- 2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1440@60Hz */
- { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
- 2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 2560x1600@60Hz */
- { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
- 3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* Terminate */
- { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) },
-};
-
-static int vmw_ldu_connector_fill_modes(struct drm_connector *connector,
- uint32_t max_width, uint32_t max_height)
-{
- struct vmw_legacy_display_unit *ldu = vmw_connector_to_ldu(connector);
- struct drm_device *dev = connector->dev;
- struct vmw_private *dev_priv = vmw_priv(dev);
- struct drm_display_mode *mode = NULL;
- struct drm_display_mode prefmode = { DRM_MODE("preferred",
- DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
- };
- int i;
-
- /* Add preferred mode */
- {
- mode = drm_mode_duplicate(dev, &prefmode);
- if (!mode)
- return 0;
- mode->hdisplay = ldu->pref_width;
- mode->vdisplay = ldu->pref_height;
- mode->vrefresh = drm_mode_vrefresh(mode);
- if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2,
- mode->vdisplay)) {
- drm_mode_probed_add(connector, mode);
-
- if (ldu->pref_mode) {
- list_del_init(&ldu->pref_mode->head);
- drm_mode_destroy(dev, ldu->pref_mode);
- }
-
- ldu->pref_mode = mode;
- }
- }
-
- for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) {
- const struct drm_display_mode *bmode;
-
- bmode = &vmw_ldu_connector_builtin[i];
- if (bmode->hdisplay > max_width ||
- bmode->vdisplay > max_height)
- continue;
-
- if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2,
- bmode->vdisplay))
- continue;
-
- mode = drm_mode_duplicate(dev, bmode);
- if (!mode)
- return 0;
- mode->vrefresh = drm_mode_vrefresh(mode);
-
- drm_mode_probed_add(connector, mode);
- }
-
- drm_mode_connector_list_update(connector);
-
- return 1;
-}
-
-static int vmw_ldu_connector_set_property(struct drm_connector *connector,
- struct drm_property *property,
- uint64_t val)
-{
- return 0;
-}
-
static void vmw_ldu_connector_destroy(struct drm_connector *connector)
{
vmw_ldu_destroy(vmw_connector_to_ldu(connector));
}
static struct drm_connector_funcs vmw_legacy_connector_funcs = {
- .dpms = vmw_ldu_connector_dpms,
- .save = vmw_ldu_connector_save,
- .restore = vmw_ldu_connector_restore,
- .detect = vmw_ldu_connector_detect,
- .fill_modes = vmw_ldu_connector_fill_modes,
- .set_property = vmw_ldu_connector_set_property,
+ .dpms = vmw_du_connector_dpms,
+ .save = vmw_du_connector_save,
+ .restore = vmw_du_connector_restore,
+ .detect = vmw_du_connector_detect,
+ .fill_modes = vmw_du_connector_fill_modes,
+ .set_property = vmw_du_connector_set_property,
.destroy = vmw_ldu_connector_destroy,
};
@@ -523,23 +333,26 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
INIT_LIST_HEAD(&ldu->active);
- ldu->pref_active = (unit == 0);
- ldu->pref_width = 800;
- ldu->pref_height = 600;
- ldu->pref_mode = NULL;
+ ldu->base.pref_active = (unit == 0);
+ ldu->base.pref_width = 800;
+ ldu->base.pref_height = 600;
+ ldu->base.pref_mode = NULL;
+ ldu->base.is_implicit = true;
drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
- DRM_MODE_CONNECTOR_LVDS);
- connector->status = vmw_ldu_connector_detect(connector, true);
+ DRM_MODE_CONNECTOR_VIRTUAL);
+ connector->status = vmw_du_connector_detect(connector, true);
drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs,
- DRM_MODE_ENCODER_LVDS);
+ DRM_MODE_ENCODER_VIRTUAL);
drm_mode_connector_attach_encoder(connector, encoder);
encoder->possible_crtcs = (1 << unit);
encoder->possible_clones = 0;
drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs);
+ drm_mode_crtc_set_gamma_size(crtc, 256);
+
drm_connector_attach_property(connector,
dev->mode_config.dirty_info_property,
1);
@@ -550,8 +363,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
- int i;
- int ret;
+ int i, ret;
if (dev_priv->ldu_priv) {
DRM_INFO("ldu system already on\n");
@@ -559,7 +371,6 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)
}
dev_priv->ldu_priv = kmalloc(sizeof(*dev_priv->ldu_priv), GFP_KERNEL);
-
if (!dev_priv->ldu_priv)
return -ENOMEM;
@@ -568,18 +379,31 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)
dev_priv->ldu_priv->last_num_active = 0;
dev_priv->ldu_priv->fb = NULL;
- drm_mode_create_dirty_info_property(dev_priv->dev);
+ /* for old hardware without multimon only enable one display */
+ if (dev_priv->capabilities & SVGA_CAP_MULTIMON)
+ ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS);
+ else
+ ret = drm_vblank_init(dev, 1);
+ if (ret != 0)
+ goto err_free;
+
+ ret = drm_mode_create_dirty_info_property(dev);
+ if (ret != 0)
+ goto err_vblank_cleanup;
- if (dev_priv->capabilities & SVGA_CAP_MULTIMON) {
- for (i = 0; i < VMWGFX_LDU_NUM_DU; ++i)
+ if (dev_priv->capabilities & SVGA_CAP_MULTIMON)
+ for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i)
vmw_ldu_init(dev_priv, i);
- ret = drm_vblank_init(dev, VMWGFX_LDU_NUM_DU);
- } else {
- /* for old hardware without multimon only enable one display */
+ else
vmw_ldu_init(dev_priv, 0);
- ret = drm_vblank_init(dev, 1);
- }
+ return 0;
+
+err_vblank_cleanup:
+ drm_vblank_cleanup(dev);
+err_free:
+ kfree(dev_priv->ldu_priv);
+ dev_priv->ldu_priv = NULL;
return ret;
}
@@ -587,52 +411,14 @@ int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
- drm_vblank_cleanup(dev);
if (!dev_priv->ldu_priv)
return -ENOSYS;
+ drm_vblank_cleanup(dev);
+
BUG_ON(!list_empty(&dev_priv->ldu_priv->active));
kfree(dev_priv->ldu_priv);
return 0;
}
-
-int vmw_kms_ldu_update_layout(struct vmw_private *dev_priv, unsigned num,
- struct drm_vmw_rect *rects)
-{
- struct drm_device *dev = dev_priv->dev;
- struct vmw_legacy_display_unit *ldu;
- struct drm_connector *con;
- int i;
-
- mutex_lock(&dev->mode_config.mutex);
-
-#if 0
- DRM_INFO("%s: new layout ", __func__);
- for (i = 0; i < (int)num; i++)
- DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y,
- rects[i].w, rects[i].h);
- DRM_INFO("\n");
-#else
- (void)i;
-#endif
-
- list_for_each_entry(con, &dev->mode_config.connector_list, head) {
- ldu = vmw_connector_to_ldu(con);
- if (num > ldu->base.unit) {
- ldu->pref_width = rects[ldu->base.unit].w;
- ldu->pref_height = rects[ldu->base.unit].h;
- ldu->pref_active = true;
- } else {
- ldu->pref_width = 800;
- ldu->pref_height = 600;
- ldu->pref_active = false;
- }
- con->status = vmw_ldu_connector_detect(con, true);
- }
-
- mutex_unlock(&dev->mode_config.mutex);
-
- return 0;
-}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_marker.c b/drivers/gpu/drm/vmwgfx/vmwgfx_marker.c
new file mode 100644
index 000000000000..8a8725c2716c
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_marker.c
@@ -0,0 +1,171 @@
+/**************************************************************************
+ *
+ * Copyright (C) 2010 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "vmwgfx_drv.h"
+
+struct vmw_marker {
+ struct list_head head;
+ uint32_t seqno;
+ struct timespec submitted;
+};
+
+void vmw_marker_queue_init(struct vmw_marker_queue *queue)
+{
+ INIT_LIST_HEAD(&queue->head);
+ queue->lag = ns_to_timespec(0);
+ getrawmonotonic(&queue->lag_time);
+ spin_lock_init(&queue->lock);
+}
+
+void vmw_marker_queue_takedown(struct vmw_marker_queue *queue)
+{
+ struct vmw_marker *marker, *next;
+
+ spin_lock(&queue->lock);
+ list_for_each_entry_safe(marker, next, &queue->head, head) {
+ kfree(marker);
+ }
+ spin_unlock(&queue->lock);
+}
+
+int vmw_marker_push(struct vmw_marker_queue *queue,
+ uint32_t seqno)
+{
+ struct vmw_marker *marker = kmalloc(sizeof(*marker), GFP_KERNEL);
+
+ if (unlikely(!marker))
+ return -ENOMEM;
+
+ marker->seqno = seqno;
+ getrawmonotonic(&marker->submitted);
+ spin_lock(&queue->lock);
+ list_add_tail(&marker->head, &queue->head);
+ spin_unlock(&queue->lock);
+
+ return 0;
+}
+
+int vmw_marker_pull(struct vmw_marker_queue *queue,
+ uint32_t signaled_seqno)
+{
+ struct vmw_marker *marker, *next;
+ struct timespec now;
+ bool updated = false;
+
+ spin_lock(&queue->lock);
+ getrawmonotonic(&now);
+
+ if (list_empty(&queue->head)) {
+ queue->lag = ns_to_timespec(0);
+ queue->lag_time = now;
+ updated = true;
+ goto out_unlock;
+ }
+
+ list_for_each_entry_safe(marker, next, &queue->head, head) {
+ if (signaled_seqno - marker->seqno > (1 << 30))
+ continue;
+
+ queue->lag = timespec_sub(now, marker->submitted);
+ queue->lag_time = now;
+ updated = true;
+ list_del(&marker->head);
+ kfree(marker);
+ }
+
+out_unlock:
+ spin_unlock(&queue->lock);
+
+ return (updated) ? 0 : -EBUSY;
+}
+
+static struct timespec vmw_timespec_add(struct timespec t1,
+ struct timespec t2)
+{
+ t1.tv_sec += t2.tv_sec;
+ t1.tv_nsec += t2.tv_nsec;
+ if (t1.tv_nsec >= 1000000000L) {
+ t1.tv_sec += 1;
+ t1.tv_nsec -= 1000000000L;
+ }
+
+ return t1;
+}
+
+static struct timespec vmw_fifo_lag(struct vmw_marker_queue *queue)
+{
+ struct timespec now;
+
+ spin_lock(&queue->lock);
+ getrawmonotonic(&now);
+ queue->lag = vmw_timespec_add(queue->lag,
+ timespec_sub(now, queue->lag_time));
+ queue->lag_time = now;
+ spin_unlock(&queue->lock);
+ return queue->lag;
+}
+
+
+static bool vmw_lag_lt(struct vmw_marker_queue *queue,
+ uint32_t us)
+{
+ struct timespec lag, cond;
+
+ cond = ns_to_timespec((s64) us * 1000);
+ lag = vmw_fifo_lag(queue);
+ return (timespec_compare(&lag, &cond) < 1);
+}
+
+int vmw_wait_lag(struct vmw_private *dev_priv,
+ struct vmw_marker_queue *queue, uint32_t us)
+{
+ struct vmw_marker *marker;
+ uint32_t seqno;
+ int ret;
+
+ while (!vmw_lag_lt(queue, us)) {
+ spin_lock(&queue->lock);
+ if (list_empty(&queue->head))
+ seqno = atomic_read(&dev_priv->marker_seq);
+ else {
+ marker = list_first_entry(&queue->head,
+ struct vmw_marker, head);
+ seqno = marker->seqno;
+ }
+ spin_unlock(&queue->lock);
+
+ ret = vmw_wait_seqno(dev_priv, false, seqno, true,
+ 3*HZ);
+
+ if (unlikely(ret != 0))
+ return ret;
+
+ (void) vmw_marker_pull(queue, seqno);
+ }
+ return 0;
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c
index f1a52f9e7298..14399eec9c3c 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c
@@ -87,48 +87,6 @@ static inline void fill_flush(struct vmw_escape_video_flush *cmd,
}
/**
- * Pin or unpin a buffer in vram.
- *
- * @dev_priv: Driver private.
- * @buf: DMA buffer to pin or unpin.
- * @pin: Pin buffer in vram if true.
- * @interruptible: Use interruptible wait.
- *
- * Takes the current masters ttm lock in read.
- *
- * Returns
- * -ERESTARTSYS if interrupted by a signal.
- */
-static int vmw_dmabuf_pin_in_vram(struct vmw_private *dev_priv,
- struct vmw_dma_buffer *buf,
- bool pin, bool interruptible)
-{
- struct ttm_buffer_object *bo = &buf->base;
- struct ttm_placement *overlay_placement = &vmw_vram_placement;
- int ret;
-
- ret = ttm_read_lock(&dev_priv->active_master->lock, interruptible);
- if (unlikely(ret != 0))
- return ret;
-
- ret = ttm_bo_reserve(bo, interruptible, false, false, 0);
- if (unlikely(ret != 0))
- goto err;
-
- if (pin)
- overlay_placement = &vmw_vram_ne_placement;
-
- ret = ttm_bo_validate(bo, overlay_placement, interruptible, false, false);
-
- ttm_bo_unreserve(bo);
-
-err:
- ttm_read_unlock(&dev_priv->active_master->lock);
-
- return ret;
-}
-
-/**
* Send put command to hw.
*
* Returns
@@ -139,68 +97,80 @@ static int vmw_overlay_send_put(struct vmw_private *dev_priv,
struct drm_vmw_control_stream_arg *arg,
bool interruptible)
{
+ struct vmw_escape_video_flush *flush;
+ size_t fifo_size;
+ bool have_so = dev_priv->sou_priv ? true : false;
+ int i, num_items;
+ SVGAGuestPtr ptr;
+
struct {
struct vmw_escape_header escape;
struct {
- struct {
- uint32_t cmdType;
- uint32_t streamId;
- } header;
- struct {
- uint32_t registerId;
- uint32_t value;
- } items[SVGA_VIDEO_PITCH_3 + 1];
- } body;
- struct vmw_escape_video_flush flush;
+ uint32_t cmdType;
+ uint32_t streamId;
+ } header;
} *cmds;
- uint32_t offset;
- int i, ret;
+ struct {
+ uint32_t registerId;
+ uint32_t value;
+ } *items;
- for (;;) {
- cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds));
- if (cmds)
- break;
+ /* defines are a index needs + 1 */
+ if (have_so)
+ num_items = SVGA_VIDEO_DST_SCREEN_ID + 1;
+ else
+ num_items = SVGA_VIDEO_PITCH_3 + 1;
- ret = vmw_fallback_wait(dev_priv, false, true, 0,
- interruptible, 3*HZ);
- if (interruptible && ret == -ERESTARTSYS)
- return ret;
- else
- BUG_ON(ret != 0);
+ fifo_size = sizeof(*cmds) + sizeof(*flush) + sizeof(*items) * num_items;
+
+ cmds = vmw_fifo_reserve(dev_priv, fifo_size);
+ /* hardware has hung, can't do anything here */
+ if (!cmds)
+ return -ENOMEM;
+
+ items = (typeof(items))&cmds[1];
+ flush = (struct vmw_escape_video_flush *)&items[num_items];
+
+ /* the size is header + number of items */
+ fill_escape(&cmds->escape, sizeof(*items) * (num_items + 1));
+
+ cmds->header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
+ cmds->header.streamId = arg->stream_id;
+
+ /* the IDs are neatly numbered */
+ for (i = 0; i < num_items; i++)
+ items[i].registerId = i;
+
+ vmw_bo_get_guest_ptr(&buf->base, &ptr);
+ ptr.offset += arg->offset;
+
+ items[SVGA_VIDEO_ENABLED].value = true;
+ items[SVGA_VIDEO_FLAGS].value = arg->flags;
+ items[SVGA_VIDEO_DATA_OFFSET].value = ptr.offset;
+ items[SVGA_VIDEO_FORMAT].value = arg->format;
+ items[SVGA_VIDEO_COLORKEY].value = arg->color_key;
+ items[SVGA_VIDEO_SIZE].value = arg->size;
+ items[SVGA_VIDEO_WIDTH].value = arg->width;
+ items[SVGA_VIDEO_HEIGHT].value = arg->height;
+ items[SVGA_VIDEO_SRC_X].value = arg->src.x;
+ items[SVGA_VIDEO_SRC_Y].value = arg->src.y;
+ items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w;
+ items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h;
+ items[SVGA_VIDEO_DST_X].value = arg->dst.x;
+ items[SVGA_VIDEO_DST_Y].value = arg->dst.y;
+ items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w;
+ items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h;
+ items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0];
+ items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1];
+ items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2];
+ if (have_so) {
+ items[SVGA_VIDEO_DATA_GMRID].value = ptr.gmrId;
+ items[SVGA_VIDEO_DST_SCREEN_ID].value = SVGA_ID_INVALID;
}
- fill_escape(&cmds->escape, sizeof(cmds->body));
- cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
- cmds->body.header.streamId = arg->stream_id;
-
- for (i = 0; i <= SVGA_VIDEO_PITCH_3; i++)
- cmds->body.items[i].registerId = i;
-
- offset = buf->base.offset + arg->offset;
-
- cmds->body.items[SVGA_VIDEO_ENABLED].value = true;
- cmds->body.items[SVGA_VIDEO_FLAGS].value = arg->flags;
- cmds->body.items[SVGA_VIDEO_DATA_OFFSET].value = offset;
- cmds->body.items[SVGA_VIDEO_FORMAT].value = arg->format;
- cmds->body.items[SVGA_VIDEO_COLORKEY].value = arg->color_key;
- cmds->body.items[SVGA_VIDEO_SIZE].value = arg->size;
- cmds->body.items[SVGA_VIDEO_WIDTH].value = arg->width;
- cmds->body.items[SVGA_VIDEO_HEIGHT].value = arg->height;
- cmds->body.items[SVGA_VIDEO_SRC_X].value = arg->src.x;
- cmds->body.items[SVGA_VIDEO_SRC_Y].value = arg->src.y;
- cmds->body.items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w;
- cmds->body.items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h;
- cmds->body.items[SVGA_VIDEO_DST_X].value = arg->dst.x;
- cmds->body.items[SVGA_VIDEO_DST_Y].value = arg->dst.y;
- cmds->body.items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w;
- cmds->body.items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h;
- cmds->body.items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0];
- cmds->body.items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1];
- cmds->body.items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2];
-
- fill_flush(&cmds->flush, arg->stream_id);
+ fill_flush(flush, arg->stream_id);
- vmw_fifo_commit(dev_priv, sizeof(*cmds));
+ vmw_fifo_commit(dev_priv, fifo_size);
return 0;
}
@@ -248,6 +218,25 @@ static int vmw_overlay_send_stop(struct vmw_private *dev_priv,
}
/**
+ * Move a buffer to vram or gmr if @pin is set, else unpin the buffer.
+ *
+ * With the introduction of screen objects buffers could now be
+ * used with GMRs instead of being locked to vram.
+ */
+static int vmw_overlay_move_buffer(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *buf,
+ bool pin, bool inter)
+{
+ if (!pin)
+ return vmw_dmabuf_unpin(dev_priv, buf, inter);
+
+ if (!dev_priv->sou_priv)
+ return vmw_dmabuf_to_vram(dev_priv, buf, true, inter);
+
+ return vmw_dmabuf_to_vram_or_gmr(dev_priv, buf, true, inter);
+}
+
+/**
* Stop or pause a stream.
*
* If the stream is paused the no evict flag is removed from the buffer
@@ -279,8 +268,8 @@ static int vmw_overlay_stop(struct vmw_private *dev_priv,
return ret;
/* We just remove the NO_EVICT flag so no -ENOMEM */
- ret = vmw_dmabuf_pin_in_vram(dev_priv, stream->buf, false,
- interruptible);
+ ret = vmw_overlay_move_buffer(dev_priv, stream->buf, false,
+ interruptible);
if (interruptible && ret == -ERESTARTSYS)
return ret;
else
@@ -342,7 +331,7 @@ static int vmw_overlay_update_stream(struct vmw_private *dev_priv,
/* We don't start the old stream if we are interrupted.
* Might return -ENOMEM if it can't fit the buffer in vram.
*/
- ret = vmw_dmabuf_pin_in_vram(dev_priv, buf, true, interruptible);
+ ret = vmw_overlay_move_buffer(dev_priv, buf, true, interruptible);
if (ret)
return ret;
@@ -351,7 +340,8 @@ static int vmw_overlay_update_stream(struct vmw_private *dev_priv,
/* This one needs to happen no matter what. We only remove
* the NO_EVICT flag so this is safe from -ENOMEM.
*/
- BUG_ON(vmw_dmabuf_pin_in_vram(dev_priv, buf, false, false) != 0);
+ BUG_ON(vmw_overlay_move_buffer(dev_priv, buf, false, false)
+ != 0);
return ret;
}
@@ -585,11 +575,10 @@ int vmw_overlay_init(struct vmw_private *dev_priv)
return -ENOSYS;
}
- overlay = kmalloc(sizeof(*overlay), GFP_KERNEL);
+ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
if (!overlay)
return -ENOMEM;
- memset(overlay, 0, sizeof(*overlay));
mutex_init(&overlay->mutex);
for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
overlay->stream[i].buf = NULL;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 5408b1b7996f..86c5e4cceb31 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -31,10 +31,6 @@
#include "ttm/ttm_placement.h"
#include "drmP.h"
-#define VMW_RES_CONTEXT ttm_driver_type0
-#define VMW_RES_SURFACE ttm_driver_type1
-#define VMW_RES_STREAM ttm_driver_type2
-
struct vmw_user_context {
struct ttm_base_object base;
struct vmw_resource res;
@@ -43,6 +39,7 @@ struct vmw_user_context {
struct vmw_user_surface {
struct ttm_base_object base;
struct vmw_surface srf;
+ uint32_t size;
};
struct vmw_user_dma_buffer {
@@ -65,6 +62,17 @@ struct vmw_user_stream {
struct vmw_stream stream;
};
+struct vmw_surface_offset {
+ uint32_t face;
+ uint32_t mip;
+ uint32_t bo_offset;
+};
+
+
+static uint64_t vmw_user_context_size;
+static uint64_t vmw_user_surface_size;
+static uint64_t vmw_user_stream_size;
+
static inline struct vmw_dma_buffer *
vmw_dma_buffer(struct ttm_buffer_object *bo)
{
@@ -84,13 +92,36 @@ struct vmw_resource *vmw_resource_reference(struct vmw_resource *res)
return res;
}
+
+/**
+ * vmw_resource_release_id - release a resource id to the id manager.
+ *
+ * @res: Pointer to the resource.
+ *
+ * Release the resource id to the resource id manager and set it to -1
+ */
+static void vmw_resource_release_id(struct vmw_resource *res)
+{
+ struct vmw_private *dev_priv = res->dev_priv;
+
+ write_lock(&dev_priv->resource_lock);
+ if (res->id != -1)
+ idr_remove(res->idr, res->id);
+ res->id = -1;
+ write_unlock(&dev_priv->resource_lock);
+}
+
static void vmw_resource_release(struct kref *kref)
{
struct vmw_resource *res =
container_of(kref, struct vmw_resource, kref);
struct vmw_private *dev_priv = res->dev_priv;
+ int id = res->id;
+ struct idr *idr = res->idr;
- idr_remove(res->idr, res->id);
+ res->avail = false;
+ if (res->remove_from_lists != NULL)
+ res->remove_from_lists(res);
write_unlock(&dev_priv->resource_lock);
if (likely(res->hw_destroy != NULL))
@@ -102,6 +133,9 @@ static void vmw_resource_release(struct kref *kref)
kfree(res);
write_lock(&dev_priv->resource_lock);
+
+ if (id != -1)
+ idr_remove(idr, id);
}
void vmw_resource_unreference(struct vmw_resource **p_res)
@@ -115,28 +149,29 @@ void vmw_resource_unreference(struct vmw_resource **p_res)
write_unlock(&dev_priv->resource_lock);
}
-static int vmw_resource_init(struct vmw_private *dev_priv,
- struct vmw_resource *res,
- struct idr *idr,
- enum ttm_object_type obj_type,
- void (*res_free) (struct vmw_resource *res))
+
+/**
+ * vmw_resource_alloc_id - release a resource id to the id manager.
+ *
+ * @dev_priv: Pointer to the device private structure.
+ * @res: Pointer to the resource.
+ *
+ * Allocate the lowest free resource from the resource manager, and set
+ * @res->id to that id. Returns 0 on success and -ENOMEM on failure.
+ */
+static int vmw_resource_alloc_id(struct vmw_private *dev_priv,
+ struct vmw_resource *res)
{
int ret;
- kref_init(&res->kref);
- res->hw_destroy = NULL;
- res->res_free = res_free;
- res->res_type = obj_type;
- res->idr = idr;
- res->avail = false;
- res->dev_priv = dev_priv;
+ BUG_ON(res->id != -1);
do {
- if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
+ if (unlikely(idr_pre_get(res->idr, GFP_KERNEL) == 0))
return -ENOMEM;
write_lock(&dev_priv->resource_lock);
- ret = idr_get_new_above(idr, res, 1, &res->id);
+ ret = idr_get_new_above(res->idr, res, 1, &res->id);
write_unlock(&dev_priv->resource_lock);
} while (ret == -EAGAIN);
@@ -144,6 +179,33 @@ static int vmw_resource_init(struct vmw_private *dev_priv,
return ret;
}
+
+static int vmw_resource_init(struct vmw_private *dev_priv,
+ struct vmw_resource *res,
+ struct idr *idr,
+ enum ttm_object_type obj_type,
+ bool delay_id,
+ void (*res_free) (struct vmw_resource *res),
+ void (*remove_from_lists)
+ (struct vmw_resource *res))
+{
+ kref_init(&res->kref);
+ res->hw_destroy = NULL;
+ res->res_free = res_free;
+ res->remove_from_lists = remove_from_lists;
+ res->res_type = obj_type;
+ res->idr = idr;
+ res->avail = false;
+ res->dev_priv = dev_priv;
+ INIT_LIST_HEAD(&res->query_head);
+ INIT_LIST_HEAD(&res->validate_head);
+ res->id = -1;
+ if (delay_id)
+ return 0;
+ else
+ return vmw_resource_alloc_id(dev_priv, res);
+}
+
/**
* vmw_resource_activate
*
@@ -198,8 +260,12 @@ static void vmw_hw_context_destroy(struct vmw_resource *res)
struct {
SVGA3dCmdHeader header;
SVGA3dCmdDestroyContext body;
- } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
+ } *cmd;
+
+ vmw_execbuf_release_pinned_bo(dev_priv, true, res->id);
+
+ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
if (unlikely(cmd == NULL)) {
DRM_ERROR("Failed reserving FIFO space for surface "
"destruction.\n");
@@ -211,7 +277,7 @@ static void vmw_hw_context_destroy(struct vmw_resource *res)
cmd->body.cid = cpu_to_le32(res->id);
vmw_fifo_commit(dev_priv, sizeof(*cmd));
- vmw_3d_resource_dec(dev_priv);
+ vmw_3d_resource_dec(dev_priv, false);
}
static int vmw_context_init(struct vmw_private *dev_priv,
@@ -226,14 +292,17 @@ static int vmw_context_init(struct vmw_private *dev_priv,
} *cmd;
ret = vmw_resource_init(dev_priv, res, &dev_priv->context_idr,
- VMW_RES_CONTEXT, res_free);
+ VMW_RES_CONTEXT, false, res_free, NULL);
if (unlikely(ret != 0)) {
- if (res_free == NULL)
- kfree(res);
- else
- res_free(res);
- return ret;
+ DRM_ERROR("Failed to allocate a resource id.\n");
+ goto out_early;
+ }
+
+ if (unlikely(res->id >= SVGA3D_MAX_CONTEXT_IDS)) {
+ DRM_ERROR("Out of hw context ids.\n");
+ vmw_resource_unreference(&res);
+ return -ENOMEM;
}
cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
@@ -248,9 +317,16 @@ static int vmw_context_init(struct vmw_private *dev_priv,
cmd->body.cid = cpu_to_le32(res->id);
vmw_fifo_commit(dev_priv, sizeof(*cmd));
- (void) vmw_3d_resource_inc(dev_priv);
+ (void) vmw_3d_resource_inc(dev_priv, false);
vmw_resource_activate(res, vmw_hw_context_destroy);
return 0;
+
+out_early:
+ if (res_free == NULL)
+ kfree(res);
+ else
+ res_free(res);
+ return ret;
}
struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv)
@@ -273,8 +349,11 @@ static void vmw_user_context_free(struct vmw_resource *res)
{
struct vmw_user_context *ctx =
container_of(res, struct vmw_user_context, res);
+ struct vmw_private *dev_priv = res->dev_priv;
kfree(ctx);
+ ttm_mem_global_free(vmw_mem_glob(dev_priv),
+ vmw_user_context_size);
}
/**
@@ -328,23 +407,56 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
- struct vmw_user_context *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+ struct vmw_user_context *ctx;
struct vmw_resource *res;
struct vmw_resource *tmp;
struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret;
- if (unlikely(ctx == NULL))
- return -ENOMEM;
+
+ /*
+ * Approximate idr memory usage with 128 bytes. It will be limited
+ * by maximum number_of contexts anyway.
+ */
+
+ if (unlikely(vmw_user_context_size == 0))
+ vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128;
+
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (unlikely(ret != 0))
+ return ret;
+
+ ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv),
+ vmw_user_context_size,
+ false, true);
+ if (unlikely(ret != 0)) {
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Out of graphics memory for context"
+ " creation.\n");
+ goto out_unlock;
+ }
+
+ ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+ if (unlikely(ctx == NULL)) {
+ ttm_mem_global_free(vmw_mem_glob(dev_priv),
+ vmw_user_context_size);
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
res = &ctx->res;
ctx->base.shareable = false;
ctx->base.tfile = NULL;
+ /*
+ * From here on, the destructor takes over resource freeing.
+ */
+
ret = vmw_context_init(dev_priv, res, vmw_user_context_free);
if (unlikely(ret != 0))
- return ret;
+ goto out_unlock;
tmp = vmw_resource_reference(&ctx->res);
ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT,
@@ -358,13 +470,16 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data,
arg->cid = res->id;
out_err:
vmw_resource_unreference(&res);
+out_unlock:
+ ttm_read_unlock(&vmaster->lock);
return ret;
}
int vmw_context_check(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
- int id)
+ int id,
+ struct vmw_resource **p_res)
{
struct vmw_resource *res;
int ret = 0;
@@ -376,6 +491,8 @@ int vmw_context_check(struct vmw_private *dev_priv,
container_of(res, struct vmw_user_context, res);
if (ctx->base.tfile != tfile && !ctx->base.shareable)
ret = -EPERM;
+ if (p_res)
+ *p_res = vmw_resource_reference(res);
} else
ret = -EINVAL;
read_unlock(&dev_priv->resource_lock);
@@ -383,102 +500,638 @@ int vmw_context_check(struct vmw_private *dev_priv,
return ret;
}
+struct vmw_bpp {
+ uint8_t bpp;
+ uint8_t s_bpp;
+};
+
+/*
+ * Size table for the supported SVGA3D surface formats. It consists of
+ * two values. The bpp value and the s_bpp value which is short for
+ * "stride bits per pixel" The values are given in such a way that the
+ * minimum stride for the image is calculated using
+ *
+ * min_stride = w*s_bpp
+ *
+ * and the total memory requirement for the image is
+ *
+ * h*min_stride*bpp/s_bpp
+ *
+ */
+static const struct vmw_bpp vmw_sf_bpp[] = {
+ [SVGA3D_FORMAT_INVALID] = {0, 0},
+ [SVGA3D_X8R8G8B8] = {32, 32},
+ [SVGA3D_A8R8G8B8] = {32, 32},
+ [SVGA3D_R5G6B5] = {16, 16},
+ [SVGA3D_X1R5G5B5] = {16, 16},
+ [SVGA3D_A1R5G5B5] = {16, 16},
+ [SVGA3D_A4R4G4B4] = {16, 16},
+ [SVGA3D_Z_D32] = {32, 32},
+ [SVGA3D_Z_D16] = {16, 16},
+ [SVGA3D_Z_D24S8] = {32, 32},
+ [SVGA3D_Z_D15S1] = {16, 16},
+ [SVGA3D_LUMINANCE8] = {8, 8},
+ [SVGA3D_LUMINANCE4_ALPHA4] = {8, 8},
+ [SVGA3D_LUMINANCE16] = {16, 16},
+ [SVGA3D_LUMINANCE8_ALPHA8] = {16, 16},
+ [SVGA3D_DXT1] = {4, 16},
+ [SVGA3D_DXT2] = {8, 32},
+ [SVGA3D_DXT3] = {8, 32},
+ [SVGA3D_DXT4] = {8, 32},
+ [SVGA3D_DXT5] = {8, 32},
+ [SVGA3D_BUMPU8V8] = {16, 16},
+ [SVGA3D_BUMPL6V5U5] = {16, 16},
+ [SVGA3D_BUMPX8L8V8U8] = {32, 32},
+ [SVGA3D_ARGB_S10E5] = {16, 16},
+ [SVGA3D_ARGB_S23E8] = {32, 32},
+ [SVGA3D_A2R10G10B10] = {32, 32},
+ [SVGA3D_V8U8] = {16, 16},
+ [SVGA3D_Q8W8V8U8] = {32, 32},
+ [SVGA3D_CxV8U8] = {16, 16},
+ [SVGA3D_X8L8V8U8] = {32, 32},
+ [SVGA3D_A2W10V10U10] = {32, 32},
+ [SVGA3D_ALPHA8] = {8, 8},
+ [SVGA3D_R_S10E5] = {16, 16},
+ [SVGA3D_R_S23E8] = {32, 32},
+ [SVGA3D_RG_S10E5] = {16, 16},
+ [SVGA3D_RG_S23E8] = {32, 32},
+ [SVGA3D_BUFFER] = {8, 8},
+ [SVGA3D_Z_D24X8] = {32, 32},
+ [SVGA3D_V16U16] = {32, 32},
+ [SVGA3D_G16R16] = {32, 32},
+ [SVGA3D_A16B16G16R16] = {64, 64},
+ [SVGA3D_UYVY] = {12, 12},
+ [SVGA3D_YUY2] = {12, 12},
+ [SVGA3D_NV12] = {12, 8},
+ [SVGA3D_AYUV] = {32, 32},
+ [SVGA3D_BC4_UNORM] = {4, 16},
+ [SVGA3D_BC5_UNORM] = {8, 32},
+ [SVGA3D_Z_DF16] = {16, 16},
+ [SVGA3D_Z_DF24] = {24, 24},
+ [SVGA3D_Z_D24S8_INT] = {32, 32}
+};
+
/**
* Surface management.
*/
+struct vmw_surface_dma {
+ SVGA3dCmdHeader header;
+ SVGA3dCmdSurfaceDMA body;
+ SVGA3dCopyBox cb;
+ SVGA3dCmdSurfaceDMASuffix suffix;
+};
+
+struct vmw_surface_define {
+ SVGA3dCmdHeader header;
+ SVGA3dCmdDefineSurface body;
+};
+
+struct vmw_surface_destroy {
+ SVGA3dCmdHeader header;
+ SVGA3dCmdDestroySurface body;
+};
+
+
+/**
+ * vmw_surface_dma_size - Compute fifo size for a dma command.
+ *
+ * @srf: Pointer to a struct vmw_surface
+ *
+ * Computes the required size for a surface dma command for backup or
+ * restoration of the surface represented by @srf.
+ */
+static inline uint32_t vmw_surface_dma_size(const struct vmw_surface *srf)
+{
+ return srf->num_sizes * sizeof(struct vmw_surface_dma);
+}
+
+
+/**
+ * vmw_surface_define_size - Compute fifo size for a surface define command.
+ *
+ * @srf: Pointer to a struct vmw_surface
+ *
+ * Computes the required size for a surface define command for the definition
+ * of the surface represented by @srf.
+ */
+static inline uint32_t vmw_surface_define_size(const struct vmw_surface *srf)
+{
+ return sizeof(struct vmw_surface_define) + srf->num_sizes *
+ sizeof(SVGA3dSize);
+}
+
+
+/**
+ * vmw_surface_destroy_size - Compute fifo size for a surface destroy command.
+ *
+ * Computes the required size for a surface destroy command for the destruction
+ * of a hw surface.
+ */
+static inline uint32_t vmw_surface_destroy_size(void)
+{
+ return sizeof(struct vmw_surface_destroy);
+}
+
+/**
+ * vmw_surface_destroy_encode - Encode a surface_destroy command.
+ *
+ * @id: The surface id
+ * @cmd_space: Pointer to memory area in which the commands should be encoded.
+ */
+static void vmw_surface_destroy_encode(uint32_t id,
+ void *cmd_space)
+{
+ struct vmw_surface_destroy *cmd = (struct vmw_surface_destroy *)
+ cmd_space;
+
+ cmd->header.id = SVGA_3D_CMD_SURFACE_DESTROY;
+ cmd->header.size = sizeof(cmd->body);
+ cmd->body.sid = id;
+}
+
+/**
+ * vmw_surface_define_encode - Encode a surface_define command.
+ *
+ * @srf: Pointer to a struct vmw_surface object.
+ * @cmd_space: Pointer to memory area in which the commands should be encoded.
+ */
+static void vmw_surface_define_encode(const struct vmw_surface *srf,
+ void *cmd_space)
+{
+ struct vmw_surface_define *cmd = (struct vmw_surface_define *)
+ cmd_space;
+ struct drm_vmw_size *src_size;
+ SVGA3dSize *cmd_size;
+ uint32_t cmd_len;
+ int i;
+
+ cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize);
+
+ cmd->header.id = SVGA_3D_CMD_SURFACE_DEFINE;
+ cmd->header.size = cmd_len;
+ cmd->body.sid = srf->res.id;
+ cmd->body.surfaceFlags = srf->flags;
+ cmd->body.format = cpu_to_le32(srf->format);
+ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
+ cmd->body.face[i].numMipLevels = srf->mip_levels[i];
+
+ cmd += 1;
+ cmd_size = (SVGA3dSize *) cmd;
+ src_size = srf->sizes;
+
+ for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) {
+ cmd_size->width = src_size->width;
+ cmd_size->height = src_size->height;
+ cmd_size->depth = src_size->depth;
+ }
+}
+
+
+/**
+ * vmw_surface_dma_encode - Encode a surface_dma command.
+ *
+ * @srf: Pointer to a struct vmw_surface object.
+ * @cmd_space: Pointer to memory area in which the commands should be encoded.
+ * @ptr: Pointer to an SVGAGuestPtr indicating where the surface contents
+ * should be placed or read from.
+ * @to_surface: Boolean whether to DMA to the surface or from the surface.
+ */
+static void vmw_surface_dma_encode(struct vmw_surface *srf,
+ void *cmd_space,
+ const SVGAGuestPtr *ptr,
+ bool to_surface)
+{
+ uint32_t i;
+ uint32_t bpp = vmw_sf_bpp[srf->format].bpp;
+ uint32_t stride_bpp = vmw_sf_bpp[srf->format].s_bpp;
+ struct vmw_surface_dma *cmd = (struct vmw_surface_dma *)cmd_space;
+
+ for (i = 0; i < srf->num_sizes; ++i) {
+ SVGA3dCmdHeader *header = &cmd->header;
+ SVGA3dCmdSurfaceDMA *body = &cmd->body;
+ SVGA3dCopyBox *cb = &cmd->cb;
+ SVGA3dCmdSurfaceDMASuffix *suffix = &cmd->suffix;
+ const struct vmw_surface_offset *cur_offset = &srf->offsets[i];
+ const struct drm_vmw_size *cur_size = &srf->sizes[i];
+
+ header->id = SVGA_3D_CMD_SURFACE_DMA;
+ header->size = sizeof(*body) + sizeof(*cb) + sizeof(*suffix);
+
+ body->guest.ptr = *ptr;
+ body->guest.ptr.offset += cur_offset->bo_offset;
+ body->guest.pitch = (cur_size->width * stride_bpp + 7) >> 3;
+ body->host.sid = srf->res.id;
+ body->host.face = cur_offset->face;
+ body->host.mipmap = cur_offset->mip;
+ body->transfer = ((to_surface) ? SVGA3D_WRITE_HOST_VRAM :
+ SVGA3D_READ_HOST_VRAM);
+ cb->x = 0;
+ cb->y = 0;
+ cb->z = 0;
+ cb->srcx = 0;
+ cb->srcy = 0;
+ cb->srcz = 0;
+ cb->w = cur_size->width;
+ cb->h = cur_size->height;
+ cb->d = cur_size->depth;
+
+ suffix->suffixSize = sizeof(*suffix);
+ suffix->maximumOffset = body->guest.pitch*cur_size->height*
+ cur_size->depth*bpp / stride_bpp;
+ suffix->flags.discard = 0;
+ suffix->flags.unsynchronized = 0;
+ suffix->flags.reserved = 0;
+ ++cmd;
+ }
+};
+
+
static void vmw_hw_surface_destroy(struct vmw_resource *res)
{
struct vmw_private *dev_priv = res->dev_priv;
- struct {
- SVGA3dCmdHeader header;
- SVGA3dCmdDestroySurface body;
- } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
+ struct vmw_surface *srf;
+ void *cmd;
- if (unlikely(cmd == NULL)) {
- DRM_ERROR("Failed reserving FIFO space for surface "
- "destruction.\n");
- return;
- }
+ if (res->id != -1) {
- cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DESTROY);
- cmd->header.size = cpu_to_le32(sizeof(cmd->body));
- cmd->body.sid = cpu_to_le32(res->id);
+ cmd = vmw_fifo_reserve(dev_priv, vmw_surface_destroy_size());
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("Failed reserving FIFO space for surface "
+ "destruction.\n");
+ return;
+ }
- vmw_fifo_commit(dev_priv, sizeof(*cmd));
- vmw_3d_resource_dec(dev_priv);
+ vmw_surface_destroy_encode(res->id, cmd);
+ vmw_fifo_commit(dev_priv, vmw_surface_destroy_size());
+
+ /*
+ * used_memory_size_atomic, or separate lock
+ * to avoid taking dev_priv::cmdbuf_mutex in
+ * the destroy path.
+ */
+
+ mutex_lock(&dev_priv->cmdbuf_mutex);
+ srf = container_of(res, struct vmw_surface, res);
+ dev_priv->used_memory_size -= srf->backup_size;
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
+
+ }
+ vmw_3d_resource_dec(dev_priv, false);
}
void vmw_surface_res_free(struct vmw_resource *res)
{
struct vmw_surface *srf = container_of(res, struct vmw_surface, res);
+ if (srf->backup)
+ ttm_bo_unref(&srf->backup);
+ kfree(srf->offsets);
kfree(srf->sizes);
kfree(srf->snooper.image);
kfree(srf);
}
-int vmw_surface_init(struct vmw_private *dev_priv,
- struct vmw_surface *srf,
- void (*res_free) (struct vmw_resource *res))
+
+/**
+ * vmw_surface_do_validate - make a surface available to the device.
+ *
+ * @dev_priv: Pointer to a device private struct.
+ * @srf: Pointer to a struct vmw_surface.
+ *
+ * If the surface doesn't have a hw id, allocate one, and optionally
+ * DMA the backed up surface contents to the device.
+ *
+ * Returns -EBUSY if there wasn't sufficient device resources to
+ * complete the validation. Retry after freeing up resources.
+ *
+ * May return other errors if the kernel is out of guest resources.
+ */
+int vmw_surface_do_validate(struct vmw_private *dev_priv,
+ struct vmw_surface *srf)
{
- int ret;
- struct {
- SVGA3dCmdHeader header;
- SVGA3dCmdDefineSurface body;
- } *cmd;
- SVGA3dSize *cmd_size;
struct vmw_resource *res = &srf->res;
- struct drm_vmw_size *src_size;
- size_t submit_size;
- uint32_t cmd_len;
- int i;
+ struct list_head val_list;
+ struct ttm_validate_buffer val_buf;
+ uint32_t submit_size;
+ uint8_t *cmd;
+ int ret;
- BUG_ON(res_free == NULL);
- ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr,
- VMW_RES_SURFACE, res_free);
+ if (likely(res->id != -1))
+ return 0;
+
+ if (unlikely(dev_priv->used_memory_size + srf->backup_size >=
+ dev_priv->memory_size))
+ return -EBUSY;
+
+ /*
+ * Reserve- and validate the backup DMA bo.
+ */
+
+ if (srf->backup) {
+ INIT_LIST_HEAD(&val_list);
+ val_buf.bo = ttm_bo_reference(srf->backup);
+ val_buf.new_sync_obj_arg = (void *)((unsigned long)
+ DRM_VMW_FENCE_FLAG_EXEC);
+ list_add_tail(&val_buf.head, &val_list);
+ ret = ttm_eu_reserve_buffers(&val_list);
+ if (unlikely(ret != 0))
+ goto out_no_reserve;
+
+ ret = ttm_bo_validate(srf->backup, &vmw_srf_placement,
+ true, false, false);
+ if (unlikely(ret != 0))
+ goto out_no_validate;
+ }
+
+ /*
+ * Alloc id for the resource.
+ */
+ ret = vmw_resource_alloc_id(dev_priv, res);
if (unlikely(ret != 0)) {
- res_free(res);
- return ret;
+ DRM_ERROR("Failed to allocate a surface id.\n");
+ goto out_no_id;
+ }
+ if (unlikely(res->id >= SVGA3D_MAX_SURFACE_IDS)) {
+ ret = -EBUSY;
+ goto out_no_fifo;
}
- submit_size = sizeof(*cmd) + srf->num_sizes * sizeof(SVGA3dSize);
- cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize);
+
+ /*
+ * Encode surface define- and dma commands.
+ */
+
+ submit_size = vmw_surface_define_size(srf);
+ if (srf->backup)
+ submit_size += vmw_surface_dma_size(srf);
cmd = vmw_fifo_reserve(dev_priv, submit_size);
if (unlikely(cmd == NULL)) {
- DRM_ERROR("Fifo reserve failed for create surface.\n");
- vmw_resource_unreference(&res);
- return -ENOMEM;
+ DRM_ERROR("Failed reserving FIFO space for surface "
+ "validation.\n");
+ ret = -ENOMEM;
+ goto out_no_fifo;
}
- cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DEFINE);
- cmd->header.size = cpu_to_le32(cmd_len);
- cmd->body.sid = cpu_to_le32(res->id);
- cmd->body.surfaceFlags = cpu_to_le32(srf->flags);
- cmd->body.format = cpu_to_le32(srf->format);
- for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
- cmd->body.face[i].numMipLevels =
- cpu_to_le32(srf->mip_levels[i]);
+ vmw_surface_define_encode(srf, cmd);
+ if (srf->backup) {
+ SVGAGuestPtr ptr;
+
+ cmd += vmw_surface_define_size(srf);
+ vmw_bo_get_guest_ptr(srf->backup, &ptr);
+ vmw_surface_dma_encode(srf, cmd, &ptr, true);
}
- cmd += 1;
- cmd_size = (SVGA3dSize *) cmd;
- src_size = srf->sizes;
+ vmw_fifo_commit(dev_priv, submit_size);
- for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) {
- cmd_size->width = cpu_to_le32(src_size->width);
- cmd_size->height = cpu_to_le32(src_size->height);
- cmd_size->depth = cpu_to_le32(src_size->depth);
+ /*
+ * Create a fence object and fence the backup buffer.
+ */
+
+ if (srf->backup) {
+ struct vmw_fence_obj *fence;
+
+ (void) vmw_execbuf_fence_commands(NULL, dev_priv,
+ &fence, NULL);
+ ttm_eu_fence_buffer_objects(&val_list, fence);
+ if (likely(fence != NULL))
+ vmw_fence_obj_unreference(&fence);
+ ttm_bo_unref(&val_buf.bo);
+ ttm_bo_unref(&srf->backup);
+ }
+
+ /*
+ * Surface memory usage accounting.
+ */
+
+ dev_priv->used_memory_size += srf->backup_size;
+
+ return 0;
+
+out_no_fifo:
+ vmw_resource_release_id(res);
+out_no_id:
+out_no_validate:
+ if (srf->backup)
+ ttm_eu_backoff_reservation(&val_list);
+out_no_reserve:
+ if (srf->backup)
+ ttm_bo_unref(&val_buf.bo);
+ return ret;
+}
+
+/**
+ * vmw_surface_evict - Evict a hw surface.
+ *
+ * @dev_priv: Pointer to a device private struct.
+ * @srf: Pointer to a struct vmw_surface
+ *
+ * DMA the contents of a hw surface to a backup guest buffer object,
+ * and destroy the hw surface, releasing its id.
+ */
+int vmw_surface_evict(struct vmw_private *dev_priv,
+ struct vmw_surface *srf)
+{
+ struct vmw_resource *res = &srf->res;
+ struct list_head val_list;
+ struct ttm_validate_buffer val_buf;
+ uint32_t submit_size;
+ uint8_t *cmd;
+ int ret;
+ struct vmw_fence_obj *fence;
+ SVGAGuestPtr ptr;
+
+ BUG_ON(res->id == -1);
+
+ /*
+ * Create a surface backup buffer object.
+ */
+
+ if (!srf->backup) {
+ ret = ttm_bo_create(&dev_priv->bdev, srf->backup_size,
+ ttm_bo_type_device,
+ &vmw_srf_placement, 0, 0, true,
+ NULL, &srf->backup);
+ if (unlikely(ret != 0))
+ return ret;
+ }
+
+ /*
+ * Reserve- and validate the backup DMA bo.
+ */
+
+ INIT_LIST_HEAD(&val_list);
+ val_buf.bo = ttm_bo_reference(srf->backup);
+ val_buf.new_sync_obj_arg = (void *)(unsigned long)
+ DRM_VMW_FENCE_FLAG_EXEC;
+ list_add_tail(&val_buf.head, &val_list);
+ ret = ttm_eu_reserve_buffers(&val_list);
+ if (unlikely(ret != 0))
+ goto out_no_reserve;
+
+ ret = ttm_bo_validate(srf->backup, &vmw_srf_placement,
+ true, false, false);
+ if (unlikely(ret != 0))
+ goto out_no_validate;
+
+
+ /*
+ * Encode the dma- and surface destroy commands.
+ */
+
+ submit_size = vmw_surface_dma_size(srf) + vmw_surface_destroy_size();
+ cmd = vmw_fifo_reserve(dev_priv, submit_size);
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("Failed reserving FIFO space for surface "
+ "eviction.\n");
+ ret = -ENOMEM;
+ goto out_no_fifo;
}
+ vmw_bo_get_guest_ptr(srf->backup, &ptr);
+ vmw_surface_dma_encode(srf, cmd, &ptr, false);
+ cmd += vmw_surface_dma_size(srf);
+ vmw_surface_destroy_encode(res->id, cmd);
vmw_fifo_commit(dev_priv, submit_size);
- (void) vmw_3d_resource_inc(dev_priv);
- vmw_resource_activate(res, vmw_hw_surface_destroy);
+
+ /*
+ * Surface memory usage accounting.
+ */
+
+ dev_priv->used_memory_size -= srf->backup_size;
+
+ /*
+ * Create a fence object and fence the DMA buffer.
+ */
+
+ (void) vmw_execbuf_fence_commands(NULL, dev_priv,
+ &fence, NULL);
+ ttm_eu_fence_buffer_objects(&val_list, fence);
+ if (likely(fence != NULL))
+ vmw_fence_obj_unreference(&fence);
+ ttm_bo_unref(&val_buf.bo);
+
+ /*
+ * Release the surface ID.
+ */
+
+ vmw_resource_release_id(res);
+
return 0;
+
+out_no_fifo:
+out_no_validate:
+ if (srf->backup)
+ ttm_eu_backoff_reservation(&val_list);
+out_no_reserve:
+ ttm_bo_unref(&val_buf.bo);
+ ttm_bo_unref(&srf->backup);
+ return ret;
+}
+
+
+/**
+ * vmw_surface_validate - make a surface available to the device, evicting
+ * other surfaces if needed.
+ *
+ * @dev_priv: Pointer to a device private struct.
+ * @srf: Pointer to a struct vmw_surface.
+ *
+ * Try to validate a surface and if it fails due to limited device resources,
+ * repeatedly try to evict other surfaces until the request can be
+ * acommodated.
+ *
+ * May return errors if out of resources.
+ */
+int vmw_surface_validate(struct vmw_private *dev_priv,
+ struct vmw_surface *srf)
+{
+ int ret;
+ struct vmw_surface *evict_srf;
+
+ do {
+ write_lock(&dev_priv->resource_lock);
+ list_del_init(&srf->lru_head);
+ write_unlock(&dev_priv->resource_lock);
+
+ ret = vmw_surface_do_validate(dev_priv, srf);
+ if (likely(ret != -EBUSY))
+ break;
+
+ write_lock(&dev_priv->resource_lock);
+ if (list_empty(&dev_priv->surface_lru)) {
+ DRM_ERROR("Out of device memory for surfaces.\n");
+ ret = -EBUSY;
+ write_unlock(&dev_priv->resource_lock);
+ break;
+ }
+
+ evict_srf = vmw_surface_reference
+ (list_first_entry(&dev_priv->surface_lru,
+ struct vmw_surface,
+ lru_head));
+ list_del_init(&evict_srf->lru_head);
+
+ write_unlock(&dev_priv->resource_lock);
+ (void) vmw_surface_evict(dev_priv, evict_srf);
+
+ vmw_surface_unreference(&evict_srf);
+
+ } while (1);
+
+ if (unlikely(ret != 0 && srf->res.id != -1)) {
+ write_lock(&dev_priv->resource_lock);
+ list_add_tail(&srf->lru_head, &dev_priv->surface_lru);
+ write_unlock(&dev_priv->resource_lock);
+ }
+
+ return ret;
+}
+
+
+/**
+ * vmw_surface_remove_from_lists - Remove surface resources from lookup lists
+ *
+ * @res: Pointer to a struct vmw_resource embedded in a struct vmw_surface
+ *
+ * As part of the resource destruction, remove the surface from any
+ * lookup lists.
+ */
+static void vmw_surface_remove_from_lists(struct vmw_resource *res)
+{
+ struct vmw_surface *srf = container_of(res, struct vmw_surface, res);
+
+ list_del_init(&srf->lru_head);
+}
+
+int vmw_surface_init(struct vmw_private *dev_priv,
+ struct vmw_surface *srf,
+ void (*res_free) (struct vmw_resource *res))
+{
+ int ret;
+ struct vmw_resource *res = &srf->res;
+
+ BUG_ON(res_free == NULL);
+ INIT_LIST_HEAD(&srf->lru_head);
+ ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr,
+ VMW_RES_SURFACE, true, res_free,
+ vmw_surface_remove_from_lists);
+
+ if (unlikely(ret != 0))
+ res_free(res);
+
+ /*
+ * The surface won't be visible to hardware until a
+ * surface validate.
+ */
+
+ (void) vmw_3d_resource_inc(dev_priv, false);
+ vmw_resource_activate(res, vmw_hw_surface_destroy);
+ return ret;
}
static void vmw_user_surface_free(struct vmw_resource *res)
@@ -486,12 +1139,58 @@ static void vmw_user_surface_free(struct vmw_resource *res)
struct vmw_surface *srf = container_of(res, struct vmw_surface, res);
struct vmw_user_surface *user_srf =
container_of(srf, struct vmw_user_surface, srf);
+ struct vmw_private *dev_priv = srf->res.dev_priv;
+ uint32_t size = user_srf->size;
+ if (srf->backup)
+ ttm_bo_unref(&srf->backup);
+ kfree(srf->offsets);
kfree(srf->sizes);
kfree(srf->snooper.image);
kfree(user_srf);
+ ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
+}
+
+/**
+ * vmw_resource_unreserve - unreserve resources previously reserved for
+ * command submission.
+ *
+ * @list_head: list of resources to unreserve.
+ *
+ * Currently only surfaces are considered, and unreserving a surface
+ * means putting it back on the device's surface lru list,
+ * so that it can be evicted if necessary.
+ * This function traverses the resource list and
+ * checks whether resources are surfaces, and in that case puts them back
+ * on the device's surface LRU list.
+ */
+void vmw_resource_unreserve(struct list_head *list)
+{
+ struct vmw_resource *res;
+ struct vmw_surface *srf;
+ rwlock_t *lock = NULL;
+
+ list_for_each_entry(res, list, validate_head) {
+
+ if (res->res_free != &vmw_surface_res_free &&
+ res->res_free != &vmw_user_surface_free)
+ continue;
+
+ if (unlikely(lock == NULL)) {
+ lock = &res->dev_priv->resource_lock;
+ write_lock(lock);
+ }
+
+ srf = container_of(res, struct vmw_surface, res);
+ list_del_init(&srf->lru_head);
+ list_add_tail(&srf->lru_head, &res->dev_priv->surface_lru);
+ }
+
+ if (lock != NULL)
+ write_unlock(lock);
}
+
int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
uint32_t handle, struct vmw_surface **out)
@@ -556,8 +1255,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
- struct vmw_user_surface *user_srf =
- kmalloc(sizeof(*user_srf), GFP_KERNEL);
+ struct vmw_user_surface *user_srf;
struct vmw_surface *srf;
struct vmw_resource *res;
struct vmw_resource *tmp;
@@ -568,10 +1266,51 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
struct drm_vmw_size __user *user_sizes;
int ret;
- int i;
+ int i, j;
+ uint32_t cur_bo_offset;
+ struct drm_vmw_size *cur_size;
+ struct vmw_surface_offset *cur_offset;
+ uint32_t stride_bpp;
+ uint32_t bpp;
+ uint32_t num_sizes;
+ uint32_t size;
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
- if (unlikely(user_srf == NULL))
- return -ENOMEM;
+ if (unlikely(vmw_user_surface_size == 0))
+ vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) +
+ 128;
+
+ num_sizes = 0;
+ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
+ num_sizes += req->mip_levels[i];
+
+ if (num_sizes > DRM_VMW_MAX_SURFACE_FACES *
+ DRM_VMW_MAX_MIP_LEVELS)
+ return -EINVAL;
+
+ size = vmw_user_surface_size + 128 +
+ ttm_round_pot(num_sizes * sizeof(struct drm_vmw_size)) +
+ ttm_round_pot(num_sizes * sizeof(struct vmw_surface_offset));
+
+
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (unlikely(ret != 0))
+ return ret;
+
+ ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv),
+ size, false, true);
+ if (unlikely(ret != 0)) {
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Out of graphics memory for surface"
+ " creation.\n");
+ goto out_unlock;
+ }
+
+ user_srf = kmalloc(sizeof(*user_srf), GFP_KERNEL);
+ if (unlikely(user_srf == NULL)) {
+ ret = -ENOMEM;
+ goto out_no_user_srf;
+ }
srf = &user_srf->srf;
res = &srf->res;
@@ -579,21 +1318,22 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
srf->flags = req->flags;
srf->format = req->format;
srf->scanout = req->scanout;
- memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels));
- srf->num_sizes = 0;
- for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
- srf->num_sizes += srf->mip_levels[i];
+ srf->backup = NULL;
- if (srf->num_sizes > DRM_VMW_MAX_SURFACE_FACES *
- DRM_VMW_MAX_MIP_LEVELS) {
- ret = -EINVAL;
- goto out_err0;
- }
+ memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels));
+ srf->num_sizes = num_sizes;
+ user_srf->size = size;
srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL);
if (unlikely(srf->sizes == NULL)) {
ret = -ENOMEM;
- goto out_err0;
+ goto out_no_sizes;
+ }
+ srf->offsets = kmalloc(srf->num_sizes * sizeof(*srf->offsets),
+ GFP_KERNEL);
+ if (unlikely(srf->sizes == NULL)) {
+ ret = -ENOMEM;
+ goto out_no_offsets;
}
user_sizes = (struct drm_vmw_size __user *)(unsigned long)
@@ -603,23 +1343,44 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
srf->num_sizes * sizeof(*srf->sizes));
if (unlikely(ret != 0)) {
ret = -EFAULT;
- goto out_err1;
+ goto out_no_copy;
}
+ cur_bo_offset = 0;
+ cur_offset = srf->offsets;
+ cur_size = srf->sizes;
+
+ bpp = vmw_sf_bpp[srf->format].bpp;
+ stride_bpp = vmw_sf_bpp[srf->format].s_bpp;
+
+ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
+ for (j = 0; j < srf->mip_levels[i]; ++j) {
+ uint32_t stride =
+ (cur_size->width * stride_bpp + 7) >> 3;
+
+ cur_offset->face = i;
+ cur_offset->mip = j;
+ cur_offset->bo_offset = cur_bo_offset;
+ cur_bo_offset += stride * cur_size->height *
+ cur_size->depth * bpp / stride_bpp;
+ ++cur_offset;
+ ++cur_size;
+ }
+ }
+ srf->backup_size = cur_bo_offset;
+
if (srf->scanout &&
srf->num_sizes == 1 &&
srf->sizes[0].width == 64 &&
srf->sizes[0].height == 64 &&
srf->format == SVGA3D_A8R8G8B8) {
- srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL);
- /* clear the image */
- if (srf->snooper.image) {
- memset(srf->snooper.image, 0x00, 64 * 64 * 4);
- } else {
+ /* allocate image area and clear it */
+ srf->snooper.image = kzalloc(64 * 64 * 4, GFP_KERNEL);
+ if (!srf->snooper.image) {
DRM_ERROR("Failed to allocate cursor_image\n");
ret = -ENOMEM;
- goto out_err1;
+ goto out_no_copy;
}
} else {
srf->snooper.image = NULL;
@@ -636,7 +1397,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free);
if (unlikely(ret != 0))
- return ret;
+ goto out_unlock;
tmp = vmw_resource_reference(&srf->res);
ret = ttm_base_object_init(tfile, &user_srf->base,
@@ -646,7 +1407,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
if (unlikely(ret != 0)) {
vmw_resource_unreference(&tmp);
vmw_resource_unreference(&res);
- return ret;
+ goto out_unlock;
}
rep->sid = user_srf->base.hash.key;
@@ -654,11 +1415,19 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
DRM_ERROR("Created bad Surface ID.\n");
vmw_resource_unreference(&res);
+
+ ttm_read_unlock(&vmaster->lock);
return 0;
-out_err1:
+out_no_copy:
+ kfree(srf->offsets);
+out_no_offsets:
kfree(srf->sizes);
-out_err0:
+out_no_sizes:
kfree(user_srf);
+out_no_user_srf:
+ ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
+out_unlock:
+ ttm_read_unlock(&vmaster->lock);
return ret;
}
@@ -972,7 +1741,7 @@ static int vmw_stream_init(struct vmw_private *dev_priv,
int ret;
ret = vmw_resource_init(dev_priv, res, &dev_priv->stream_idr,
- VMW_RES_STREAM, res_free);
+ VMW_RES_STREAM, false, res_free, NULL);
if (unlikely(ret != 0)) {
if (res_free == NULL)
@@ -1002,8 +1771,11 @@ static void vmw_user_stream_free(struct vmw_resource *res)
{
struct vmw_user_stream *stream =
container_of(res, struct vmw_user_stream, stream.res);
+ struct vmw_private *dev_priv = res->dev_priv;
kfree(stream);
+ ttm_mem_global_free(vmw_mem_glob(dev_priv),
+ vmw_user_stream_size);
}
/**
@@ -1057,23 +1829,56 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
- struct vmw_user_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL);
+ struct vmw_user_stream *stream;
struct vmw_resource *res;
struct vmw_resource *tmp;
struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret;
- if (unlikely(stream == NULL))
- return -ENOMEM;
+ /*
+ * Approximate idr memory usage with 128 bytes. It will be limited
+ * by maximum number_of streams anyway?
+ */
+
+ if (unlikely(vmw_user_stream_size == 0))
+ vmw_user_stream_size = ttm_round_pot(sizeof(*stream)) + 128;
+
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (unlikely(ret != 0))
+ return ret;
+
+ ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv),
+ vmw_user_stream_size,
+ false, true);
+ if (unlikely(ret != 0)) {
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Out of graphics memory for stream"
+ " creation.\n");
+ goto out_unlock;
+ }
+
+
+ stream = kmalloc(sizeof(*stream), GFP_KERNEL);
+ if (unlikely(stream == NULL)) {
+ ttm_mem_global_free(vmw_mem_glob(dev_priv),
+ vmw_user_stream_size);
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
res = &stream->stream.res;
stream->base.shareable = false;
stream->base.tfile = NULL;
+ /*
+ * From here on, the destructor takes over resource freeing.
+ */
+
ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free);
if (unlikely(ret != 0))
- return ret;
+ goto out_unlock;
tmp = vmw_resource_reference(res);
ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM,
@@ -1087,6 +1892,8 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
arg->stream_id = res->id;
out_err:
vmw_resource_unreference(&res);
+out_unlock:
+ ttm_read_unlock(&vmaster->lock);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
new file mode 100644
index 000000000000..4defdcf1c72e
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -0,0 +1,537 @@
+/**************************************************************************
+ *
+ * Copyright © 2011 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "vmwgfx_kms.h"
+
+
+#define vmw_crtc_to_sou(x) \
+ container_of(x, struct vmw_screen_object_unit, base.crtc)
+#define vmw_encoder_to_sou(x) \
+ container_of(x, struct vmw_screen_object_unit, base.encoder)
+#define vmw_connector_to_sou(x) \
+ container_of(x, struct vmw_screen_object_unit, base.connector)
+
+struct vmw_screen_object_display {
+ unsigned num_implicit;
+
+ struct vmw_framebuffer *implicit_fb;
+};
+
+/**
+ * Display unit using screen objects.
+ */
+struct vmw_screen_object_unit {
+ struct vmw_display_unit base;
+
+ unsigned long buffer_size; /**< Size of allocated buffer */
+ struct vmw_dma_buffer *buffer; /**< Backing store buffer */
+
+ bool defined;
+ bool active_implicit;
+};
+
+static void vmw_sou_destroy(struct vmw_screen_object_unit *sou)
+{
+ vmw_display_unit_cleanup(&sou->base);
+ kfree(sou);
+}
+
+
+/*
+ * Screen Object Display Unit CRTC functions
+ */
+
+static void vmw_sou_crtc_destroy(struct drm_crtc *crtc)
+{
+ vmw_sou_destroy(vmw_crtc_to_sou(crtc));
+}
+
+static void vmw_sou_del_active(struct vmw_private *vmw_priv,
+ struct vmw_screen_object_unit *sou)
+{
+ struct vmw_screen_object_display *ld = vmw_priv->sou_priv;
+
+ if (sou->active_implicit) {
+ if (--(ld->num_implicit) == 0)
+ ld->implicit_fb = NULL;
+ sou->active_implicit = false;
+ }
+}
+
+static void vmw_sou_add_active(struct vmw_private *vmw_priv,
+ struct vmw_screen_object_unit *sou,
+ struct vmw_framebuffer *vfb)
+{
+ struct vmw_screen_object_display *ld = vmw_priv->sou_priv;
+
+ BUG_ON(!ld->num_implicit && ld->implicit_fb);
+
+ if (!sou->active_implicit && sou->base.is_implicit) {
+ ld->implicit_fb = vfb;
+ sou->active_implicit = true;
+ ld->num_implicit++;
+ }
+}
+
+/**
+ * Send the fifo command to create a screen.
+ */
+static int vmw_sou_fifo_create(struct vmw_private *dev_priv,
+ struct vmw_screen_object_unit *sou,
+ uint32_t x, uint32_t y,
+ struct drm_display_mode *mode)
+{
+ size_t fifo_size;
+
+ struct {
+ struct {
+ uint32_t cmdType;
+ } header;
+ SVGAScreenObject obj;
+ } *cmd;
+
+ BUG_ON(!sou->buffer);
+
+ fifo_size = sizeof(*cmd);
+ cmd = vmw_fifo_reserve(dev_priv, fifo_size);
+ /* The hardware has hung, nothing we can do about it here. */
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("Fifo reserve failed.\n");
+ return -ENOMEM;
+ }
+
+ memset(cmd, 0, fifo_size);
+ cmd->header.cmdType = SVGA_CMD_DEFINE_SCREEN;
+ cmd->obj.structSize = sizeof(SVGAScreenObject);
+ cmd->obj.id = sou->base.unit;
+ cmd->obj.flags = SVGA_SCREEN_HAS_ROOT |
+ (sou->base.unit == 0 ? SVGA_SCREEN_IS_PRIMARY : 0);
+ cmd->obj.size.width = mode->hdisplay;
+ cmd->obj.size.height = mode->vdisplay;
+ if (sou->base.is_implicit) {
+ cmd->obj.root.x = x;
+ cmd->obj.root.y = y;
+ } else {
+ cmd->obj.root.x = sou->base.gui_x;
+ cmd->obj.root.y = sou->base.gui_y;
+ }
+
+ /* Ok to assume that buffer is pinned in vram */
+ vmw_bo_get_guest_ptr(&sou->buffer->base, &cmd->obj.backingStore.ptr);
+ cmd->obj.backingStore.pitch = mode->hdisplay * 4;
+
+ vmw_fifo_commit(dev_priv, fifo_size);
+
+ sou->defined = true;
+
+ return 0;
+}
+
+/**
+ * Send the fifo command to destroy a screen.
+ */
+static int vmw_sou_fifo_destroy(struct vmw_private *dev_priv,
+ struct vmw_screen_object_unit *sou)
+{
+ size_t fifo_size;
+ int ret;
+
+ struct {
+ struct {
+ uint32_t cmdType;
+ } header;
+ SVGAFifoCmdDestroyScreen body;
+ } *cmd;
+
+ /* no need to do anything */
+ if (unlikely(!sou->defined))
+ return 0;
+
+ fifo_size = sizeof(*cmd);
+ cmd = vmw_fifo_reserve(dev_priv, fifo_size);
+ /* the hardware has hung, nothing we can do about it here */
+ if (unlikely(cmd == NULL)) {
+ DRM_ERROR("Fifo reserve failed.\n");
+ return -ENOMEM;
+ }
+
+ memset(cmd, 0, fifo_size);
+ cmd->header.cmdType = SVGA_CMD_DESTROY_SCREEN;
+ cmd->body.screenId = sou->base.unit;
+
+ vmw_fifo_commit(dev_priv, fifo_size);
+
+ /* Force sync */
+ ret = vmw_fallback_wait(dev_priv, false, true, 0, false, 3*HZ);
+ if (unlikely(ret != 0))
+ DRM_ERROR("Failed to sync with HW");
+ else
+ sou->defined = false;
+
+ return ret;
+}
+
+/**
+ * Free the backing store.
+ */
+static void vmw_sou_backing_free(struct vmw_private *dev_priv,
+ struct vmw_screen_object_unit *sou)
+{
+ struct ttm_buffer_object *bo;
+
+ if (unlikely(sou->buffer == NULL))
+ return;
+
+ bo = &sou->buffer->base;
+ ttm_bo_unref(&bo);
+ sou->buffer = NULL;
+ sou->buffer_size = 0;
+}
+
+/**
+ * Allocate the backing store for the buffer.
+ */
+static int vmw_sou_backing_alloc(struct vmw_private *dev_priv,
+ struct vmw_screen_object_unit *sou,
+ unsigned long size)
+{
+ int ret;
+
+ if (sou->buffer_size == size)
+ return 0;
+
+ if (sou->buffer)
+ vmw_sou_backing_free(dev_priv, sou);
+
+ sou->buffer = kzalloc(sizeof(*sou->buffer), GFP_KERNEL);
+ if (unlikely(sou->buffer == NULL))
+ return -ENOMEM;
+
+ /* After we have alloced the backing store might not be able to
+ * resume the overlays, this is preferred to failing to alloc.
+ */
+ vmw_overlay_pause_all(dev_priv);
+ ret = vmw_dmabuf_init(dev_priv, sou->buffer, size,
+ &vmw_vram_ne_placement,
+ false, &vmw_dmabuf_bo_free);
+ vmw_overlay_resume_all(dev_priv);
+
+ if (unlikely(ret != 0))
+ sou->buffer = NULL; /* vmw_dmabuf_init frees on error */
+ else
+ sou->buffer_size = size;
+
+ return ret;
+}
+
+static int vmw_sou_crtc_set_config(struct drm_mode_set *set)
+{
+ struct vmw_private *dev_priv;
+ struct vmw_screen_object_unit *sou;
+ struct drm_connector *connector;
+ struct drm_display_mode *mode;
+ struct drm_encoder *encoder;
+ struct vmw_framebuffer *vfb;
+ struct drm_framebuffer *fb;
+ struct drm_crtc *crtc;
+ int ret = 0;
+
+ if (!set)
+ return -EINVAL;
+
+ if (!set->crtc)
+ return -EINVAL;
+
+ /* get the sou */
+ crtc = set->crtc;
+ sou = vmw_crtc_to_sou(crtc);
+ vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL;
+ dev_priv = vmw_priv(crtc->dev);
+
+ if (set->num_connectors > 1) {
+ DRM_ERROR("to many connectors\n");
+ return -EINVAL;
+ }
+
+ if (set->num_connectors == 1 &&
+ set->connectors[0] != &sou->base.connector) {
+ DRM_ERROR("connector doesn't match %p %p\n",
+ set->connectors[0], &sou->base.connector);
+ return -EINVAL;
+ }
+
+ /* sou only supports one fb active at the time */
+ if (sou->base.is_implicit &&
+ dev_priv->sou_priv->implicit_fb && vfb &&
+ !(dev_priv->sou_priv->num_implicit == 1 &&
+ sou->active_implicit) &&
+ dev_priv->sou_priv->implicit_fb != vfb) {
+ DRM_ERROR("Multiple framebuffers not supported\n");
+ return -EINVAL;
+ }
+
+ /* since they always map one to one these are safe */
+ connector = &sou->base.connector;
+ encoder = &sou->base.encoder;
+
+ /* should we turn the crtc off */
+ if (set->num_connectors == 0 || !set->mode || !set->fb) {
+ ret = vmw_sou_fifo_destroy(dev_priv, sou);
+ /* the hardware has hung don't do anything more */
+ if (unlikely(ret != 0))
+ return ret;
+
+ connector->encoder = NULL;
+ encoder->crtc = NULL;
+ crtc->fb = NULL;
+ crtc->x = 0;
+ crtc->y = 0;
+
+ vmw_sou_del_active(dev_priv, sou);
+
+ vmw_sou_backing_free(dev_priv, sou);
+
+ return 0;
+ }
+
+
+ /* we now know we want to set a mode */
+ mode = set->mode;
+ fb = set->fb;
+
+ if (set->x + mode->hdisplay > fb->width ||
+ set->y + mode->vdisplay > fb->height) {
+ DRM_ERROR("set outside of framebuffer\n");
+ return -EINVAL;
+ }
+
+ vmw_fb_off(dev_priv);
+
+ if (mode->hdisplay != crtc->mode.hdisplay ||
+ mode->vdisplay != crtc->mode.vdisplay) {
+ /* no need to check if depth is different, because backing
+ * store depth is forced to 4 by the device.
+ */
+
+ ret = vmw_sou_fifo_destroy(dev_priv, sou);
+ /* the hardware has hung don't do anything more */
+ if (unlikely(ret != 0))
+ return ret;
+
+ vmw_sou_backing_free(dev_priv, sou);
+ }
+
+ if (!sou->buffer) {
+ /* forced to depth 4 by the device */
+ size_t size = mode->hdisplay * mode->vdisplay * 4;
+ ret = vmw_sou_backing_alloc(dev_priv, sou, size);
+ if (unlikely(ret != 0))
+ return ret;
+ }
+
+ ret = vmw_sou_fifo_create(dev_priv, sou, set->x, set->y, mode);
+ if (unlikely(ret != 0)) {
+ /*
+ * We are in a bit of a situation here, the hardware has
+ * hung and we may or may not have a buffer hanging of
+ * the screen object, best thing to do is not do anything
+ * if we where defined, if not just turn the crtc of.
+ * Not what userspace wants but it needs to htfu.
+ */
+ if (sou->defined)
+ return ret;
+
+ connector->encoder = NULL;
+ encoder->crtc = NULL;
+ crtc->fb = NULL;
+ crtc->x = 0;
+ crtc->y = 0;
+
+ return ret;
+ }
+
+ vmw_sou_add_active(dev_priv, sou, vfb);
+
+ connector->encoder = encoder;
+ encoder->crtc = crtc;
+ crtc->mode = *mode;
+ crtc->fb = fb;
+ crtc->x = set->x;
+ crtc->y = set->y;
+
+ return 0;
+}
+
+static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
+ .save = vmw_du_crtc_save,
+ .restore = vmw_du_crtc_restore,
+ .cursor_set = vmw_du_crtc_cursor_set,
+ .cursor_move = vmw_du_crtc_cursor_move,
+ .gamma_set = vmw_du_crtc_gamma_set,
+ .destroy = vmw_sou_crtc_destroy,
+ .set_config = vmw_sou_crtc_set_config,
+};
+
+/*
+ * Screen Object Display Unit encoder functions
+ */
+
+static void vmw_sou_encoder_destroy(struct drm_encoder *encoder)
+{
+ vmw_sou_destroy(vmw_encoder_to_sou(encoder));
+}
+
+static struct drm_encoder_funcs vmw_screen_object_encoder_funcs = {
+ .destroy = vmw_sou_encoder_destroy,
+};
+
+/*
+ * Screen Object Display Unit connector functions
+ */
+
+static void vmw_sou_connector_destroy(struct drm_connector *connector)
+{
+ vmw_sou_destroy(vmw_connector_to_sou(connector));
+}
+
+static struct drm_connector_funcs vmw_legacy_connector_funcs = {
+ .dpms = vmw_du_connector_dpms,
+ .save = vmw_du_connector_save,
+ .restore = vmw_du_connector_restore,
+ .detect = vmw_du_connector_detect,
+ .fill_modes = vmw_du_connector_fill_modes,
+ .set_property = vmw_du_connector_set_property,
+ .destroy = vmw_sou_connector_destroy,
+};
+
+static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
+{
+ struct vmw_screen_object_unit *sou;
+ struct drm_device *dev = dev_priv->dev;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+
+ sou = kzalloc(sizeof(*sou), GFP_KERNEL);
+ if (!sou)
+ return -ENOMEM;
+
+ sou->base.unit = unit;
+ crtc = &sou->base.crtc;
+ encoder = &sou->base.encoder;
+ connector = &sou->base.connector;
+
+ sou->active_implicit = false;
+
+ sou->base.pref_active = (unit == 0);
+ sou->base.pref_width = 800;
+ sou->base.pref_height = 600;
+ sou->base.pref_mode = NULL;
+ sou->base.is_implicit = true;
+
+ drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
+ DRM_MODE_CONNECTOR_VIRTUAL);
+ connector->status = vmw_du_connector_detect(connector, true);
+
+ drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs,
+ DRM_MODE_ENCODER_VIRTUAL);
+ drm_mode_connector_attach_encoder(connector, encoder);
+ encoder->possible_crtcs = (1 << unit);
+ encoder->possible_clones = 0;
+
+ drm_crtc_init(dev, crtc, &vmw_screen_object_crtc_funcs);
+
+ drm_mode_crtc_set_gamma_size(crtc, 256);
+
+ drm_connector_attach_property(connector,
+ dev->mode_config.dirty_info_property,
+ 1);
+
+ return 0;
+}
+
+int vmw_kms_init_screen_object_display(struct vmw_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+ int i, ret;
+
+ if (dev_priv->sou_priv) {
+ DRM_INFO("sou system already on\n");
+ return -EINVAL;
+ }
+
+ if (!(dev_priv->fifo.capabilities & SVGA_FIFO_CAP_SCREEN_OBJECT_2)) {
+ DRM_INFO("Not using screen objects,"
+ " missing cap SCREEN_OBJECT_2\n");
+ return -ENOSYS;
+ }
+
+ ret = -ENOMEM;
+ dev_priv->sou_priv = kmalloc(sizeof(*dev_priv->sou_priv), GFP_KERNEL);
+ if (unlikely(!dev_priv->sou_priv))
+ goto err_no_mem;
+
+ dev_priv->sou_priv->num_implicit = 0;
+ dev_priv->sou_priv->implicit_fb = NULL;
+
+ ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS);
+ if (unlikely(ret != 0))
+ goto err_free;
+
+ ret = drm_mode_create_dirty_info_property(dev);
+ if (unlikely(ret != 0))
+ goto err_vblank_cleanup;
+
+ for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i)
+ vmw_sou_init(dev_priv, i);
+
+ DRM_INFO("Screen objects system initialized\n");
+
+ return 0;
+
+err_vblank_cleanup:
+ drm_vblank_cleanup(dev);
+err_free:
+ kfree(dev_priv->sou_priv);
+ dev_priv->sou_priv = NULL;
+err_no_mem:
+ return ret;
+}
+
+int vmw_kms_close_screen_object_display(struct vmw_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+
+ if (!dev_priv->sou_priv)
+ return -ENOSYS;
+
+ drm_vblank_cleanup(dev);
+
+ kfree(dev_priv->sou_priv);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
index 1e8eedd901e0..d3c11f5184f3 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
@@ -34,9 +34,8 @@ int vmw_mmap(struct file *filp, struct vm_area_struct *vma)
struct vmw_private *dev_priv;
if (unlikely(vma->vm_pgoff < VMWGFX_FILE_PAGE_OFFSET)) {
- if (vmw_fifo_mmap(filp, vma) == 0)
- return 0;
- return drm_mmap(filp, vma);
+ DRM_ERROR("Illegal attempt to mmap old fifo space.\n");
+ return -EINVAL;
}
file_priv = filp->private_data;
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c
index 8a1021f2e319..111d956d8e7d 100644
--- a/drivers/gpu/vga/vgaarb.c
+++ b/drivers/gpu/vga/vgaarb.c
@@ -465,31 +465,29 @@ static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev)
while (new_bus) {
new_bridge = new_bus->self;
- if (new_bridge) {
- /* go through list of devices already registered */
- list_for_each_entry(same_bridge_vgadev, &vga_list, list) {
- bus = same_bridge_vgadev->pdev->bus;
- bridge = bus->self;
-
- /* see if the share a bridge with this device */
- if (new_bridge == bridge) {
- /* if their direct parent bridge is the same
- as any bridge of this device then it can't be used
- for that device */
- same_bridge_vgadev->bridge_has_one_vga = false;
- }
+ /* go through list of devices already registered */
+ list_for_each_entry(same_bridge_vgadev, &vga_list, list) {
+ bus = same_bridge_vgadev->pdev->bus;
+ bridge = bus->self;
+
+ /* see if the share a bridge with this device */
+ if (new_bridge == bridge) {
+ /* if their direct parent bridge is the same
+ as any bridge of this device then it can't be used
+ for that device */
+ same_bridge_vgadev->bridge_has_one_vga = false;
+ }
- /* now iterate the previous devices bridge hierarchy */
- /* if the new devices parent bridge is in the other devices
- hierarchy then we can't use it to control this device */
- while (bus) {
- bridge = bus->self;
- if (bridge) {
- if (bridge == vgadev->pdev->bus->self)
- vgadev->bridge_has_one_vga = false;
- }
- bus = bus->parent;
+ /* now iterate the previous devices bridge hierarchy */
+ /* if the new devices parent bridge is in the other devices
+ hierarchy then we can't use it to control this device */
+ while (bus) {
+ bridge = bus->self;
+ if (bridge) {
+ if (bridge == vgadev->pdev->bus->self)
+ vgadev->bridge_has_one_vga = false;
}
+ bus = bus->parent;
}
}
new_bus = new_bus->parent;
@@ -993,14 +991,20 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
uc = &priv->cards[i];
}
- if (!uc)
- return -EINVAL;
+ if (!uc) {
+ ret_val = -EINVAL;
+ goto done;
+ }
- if (io_state & VGA_RSRC_LEGACY_IO && uc->io_cnt == 0)
- return -EINVAL;
+ if (io_state & VGA_RSRC_LEGACY_IO && uc->io_cnt == 0) {
+ ret_val = -EINVAL;
+ goto done;
+ }
- if (io_state & VGA_RSRC_LEGACY_MEM && uc->mem_cnt == 0)
- return -EINVAL;
+ if (io_state & VGA_RSRC_LEGACY_MEM && uc->mem_cnt == 0) {
+ ret_val = -EINVAL;
+ goto done;
+ }
vga_put(pdev, io_state);
@@ -1171,10 +1175,9 @@ static int vga_arb_open(struct inode *inode, struct file *file)
pr_debug("%s\n", __func__);
- priv = kmalloc(sizeof(struct vga_arb_private), GFP_KERNEL);
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
- memset(priv, 0, sizeof(*priv));
spin_lock_init(&priv->lock);
file->private_data = priv;
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 36ca465c00ce..22a4a051f221 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -69,7 +69,7 @@ config HID_ACRUX
Say Y here if you want to enable support for ACRUX game controllers.
config HID_ACRUX_FF
- tristate "ACRUX force feedback support"
+ bool "ACRUX force feedback support"
depends on HID_ACRUX
select INPUT_FF_MEMLESS
---help---
@@ -172,6 +172,20 @@ config HID_EZKEY
---help---
Support for Ezkey BTC 8193 keyboard.
+config HID_HOLTEK
+ tristate "Holtek On Line Grip based game controller support"
+ depends on USB_HID
+ ---help---
+ Say Y here if you have a Holtek On Line Grip based game controller.
+
+config HOLTEK_FF
+ bool "Holtek On Line Grip force feedback support"
+ depends on HID_HOLTEK
+ select INPUT_FF_MEMLESS
+ ---help---
+ Say Y here if you have a Holtek On Line Grip based game controller
+ and want to have force feedback support for it.
+
config HID_KEYTOUCH
tristate "Keytouch HID devices"
depends on USB_HID
@@ -231,6 +245,15 @@ config HID_LOGITECH
---help---
Support for Logitech devices that are not fully compliant with HID standard.
+config HID_LOGITECH_DJ
+ tristate "Logitech Unifying receivers full support"
+ depends on HID_LOGITECH
+ default m
+ ---help---
+ Say Y if you want support for Logitech Unifying receivers and devices.
+ Unifying receivers are capable of pairing up to 6 Logitech compliant
+ devices to the same receiver.
+
config LOGITECH_FF
bool "Logitech force feedback support"
depends on HID_LOGITECH
@@ -264,13 +287,21 @@ config LOGIG940_FF
Say Y here if you want to enable force feedback support for Logitech
Flight System G940 devices.
-config LOGIWII_FF
- bool "Logitech Speed Force Wireless force feedback support"
+config LOGIWHEELS_FF
+ bool "Logitech wheels configuration and force feedback support"
depends on HID_LOGITECH
select INPUT_FF_MEMLESS
+ default LOGITECH_FF
help
- Say Y here if you want to enable force feedback support for Logitech
- Speed Force Wireless (Wii) devices.
+ Say Y here if you want to enable force feedback and range setting
+ support for following Logitech wheels:
+ - Logitech Driving Force
+ - Logitech Driving Force Pro
+ - Logitech Driving Force GT
+ - Logitech G25
+ - Logitech G27
+ - Logitech MOMO/MOMO 2
+ - Logitech Formula Force EX
config HID_MAGICMOUSE
tristate "Apple MagicMouse multi-touch support"
@@ -314,6 +345,7 @@ config HID_MULTITOUCH
- Hanvon dual touch panels
- Ilitek dual touch panels
- IrTouch Infrared USB panels
+ - LG Display panels (Dell ST2220Tc)
- Lumio CrystalTouch panels
- MosArt dual-touch panels
- PenMount dual touch panels
@@ -322,6 +354,7 @@ config HID_MULTITOUCH
- Stantum multitouch panels
- Touch International Panels
- Unitec Panels
+ - XAT optical touch panels
If unsure, say N.
@@ -426,6 +459,13 @@ config HID_PICOLCD_LEDS
---help---
Provide access to PicoLCD's GPO pins via leds class.
+config HID_PRIMAX
+ tristate "Primax non-fully HID-compliant devices"
+ depends on USB_HID
+ ---help---
+ Support for Primax devices that are not fully compliant with the
+ HID standard.
+
config HID_QUANTA
tristate "Quanta Optical Touch panels"
depends on USB_HID
@@ -435,6 +475,7 @@ config HID_QUANTA
config HID_ROCCAT
tristate "Roccat special event support"
depends on USB_HID
+ select HID_ROCCAT_COMMON
---help---
Support for Roccat special events.
Say Y here if you have a Roccat mouse or keyboard and want OSD or
@@ -442,44 +483,40 @@ config HID_ROCCAT
config HID_ROCCAT_COMMON
tristate
+ depends on HID_ROCCAT
config HID_ROCCAT_ARVO
tristate "Roccat Arvo keyboard support"
depends on USB_HID
- select HID_ROCCAT
- select HID_ROCCAT_COMMON
+ depends on HID_ROCCAT
---help---
Support for Roccat Arvo keyboard.
config HID_ROCCAT_KONE
tristate "Roccat Kone Mouse support"
depends on USB_HID
- select HID_ROCCAT
- select HID_ROCCAT_COMMON
+ depends on HID_ROCCAT
---help---
Support for Roccat Kone mouse.
config HID_ROCCAT_KONEPLUS
tristate "Roccat Kone[+] mouse support"
depends on USB_HID
- select HID_ROCCAT
- select HID_ROCCAT_COMMON
+ depends on HID_ROCCAT
---help---
Support for Roccat Kone[+] mouse.
config HID_ROCCAT_KOVAPLUS
tristate "Roccat Kova[+] mouse support"
depends on USB_HID
- select HID_ROCCAT
- select HID_ROCCAT_COMMON
+ depends on HID_ROCCAT
---help---
Support for Roccat Kova[+] mouse.
config HID_ROCCAT_PYRA
tristate "Roccat Pyra mouse support"
depends on USB_HID
- select HID_ROCCAT
- select HID_ROCCAT_COMMON
+ depends on HID_ROCCAT
---help---
Support for Roccat Pyra mouse.
@@ -495,6 +532,12 @@ config HID_SONY
---help---
Support for Sony PS3 controller.
+config HID_SPEEDLINK
+ tristate "Speedlink VAD Cezanne mouse support"
+ depends on USB_HID
+ ---help---
+ Support for Speedlink Vicious and Divine Cezanne mouse.
+
config HID_SUNPLUS
tristate "Sunplus wireless desktop"
depends on USB_HID
@@ -521,7 +564,11 @@ config HID_SMARTJOYPLUS
tristate "SmartJoy PLUS PS2/USB adapter support"
depends on USB_HID
---help---
- Support for SmartJoy PLUS PS2/USB adapter.
+ Support for SmartJoy PLUS PS2/USB adapter, Super Dual Box,
+ Super Joy Box 3 Pro, Super Dual Box Pro, and Super Joy Box 5 Pro.
+
+ Note that DDR (Dance Dance Revolution) mode is not supported, nor
+ is pressure sensitive buttons on the pro models.
config SMARTJOYPLUS_FF
bool "SmartJoy PLUS PS2/USB adapter force feedback support"
@@ -568,6 +615,14 @@ config HID_WACOM_POWER_SUPPLY
Say Y here if you want to enable power supply status monitoring for
Wacom Bluetooth devices.
+config HID_WIIMOTE
+ tristate "Nintendo Wii Remote support"
+ depends on BT_HIDP
+ depends on LEDS_CLASS
+ select POWER_SUPPLY
+ ---help---
+ Support for the Nintendo Wii Remote bluetooth device.
+
config HID_ZEROPLUS
tristate "Zeroplus based game controller support"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index f8cc4ea7335a..1e0d2a638b28 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -21,7 +21,7 @@ endif
ifdef CONFIG_LOGIG940_FF
hid-logitech-y += hid-lg3ff.o
endif
-ifdef CONFIG_LOGIWII_FF
+ifdef CONFIG_LOGIWHEELS_FF
hid-logitech-y += hid-lg4ff.o
endif
@@ -37,11 +37,13 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
+obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
obj-$(CONFIG_HID_KYE) += hid-kye.o
obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
+obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
@@ -53,6 +55,7 @@ obj-$(CONFIG_HID_QUANTA) += hid-quanta.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
+obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o
obj-$(CONFIG_HID_ROCCAT_COMMON) += hid-roccat-common.o
obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o
@@ -63,6 +66,7 @@ obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
obj-$(CONFIG_HID_SONY) += hid-sony.o
+obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o
obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
@@ -73,6 +77,7 @@ obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
obj-$(CONFIG_HID_WACOM) += hid-wacom.o
obj-$(CONFIG_HID_WALTOP) += hid-waltop.o
+obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o
obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE) += usbhid/
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index b85744fe8464..299d23871122 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -444,11 +444,20 @@ static const struct hid_device_id apple_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_RDESC_JIS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ANSI),
+ .driver_data = APPLE_HAS_FN },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ISO),
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_JIS),
+ .driver_data = APPLE_HAS_FN },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_ISO_KEYBOARD },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO),
+ .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+ APPLE_ISO_KEYBOARD },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
@@ -487,6 +496,24 @@ static const struct hid_device_id apple_devices[] = {
.driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI),
+ .driver_data = APPLE_HAS_FN },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO),
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS),
+ .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI),
+ .driver_data = APPLE_HAS_FN },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO),
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS),
+ .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI),
+ .driver_data = APPLE_HAS_FN },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO),
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS),
+ .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO),
diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c
index b4554288de00..5be858dd9a15 100644
--- a/drivers/hid/hid-axff.c
+++ b/drivers/hid/hid-axff.c
@@ -6,7 +6,7 @@
* Xbox 360 controller.
*
* 1a34:0802 "ACRUX USB GAMEPAD 8116"
- * - tested with a EXEQ EQ-PCU-02090 game controller.
+ * - tested with an EXEQ EQ-PCU-02090 game controller.
*
* Copyright (c) 2010 Sergei Kolzun <x0r@dv-life.ru>
*/
@@ -31,6 +31,7 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/hid.h>
+#include <linux/module.h>
#include "hid-ids.h"
@@ -45,7 +46,10 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect
{
struct hid_device *hid = input_get_drvdata(dev);
struct axff_device *axff = data;
+ struct hid_report *report = axff->report;
+ int field_count = 0;
int left, right;
+ int i, j;
left = effect->u.rumble.strong_magnitude;
right = effect->u.rumble.weak_magnitude;
@@ -55,10 +59,14 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect
left = left * 0xff / 0xffff;
right = right * 0xff / 0xffff;
- axff->report->field[0]->value[0] = left;
- axff->report->field[1]->value[0] = right;
- axff->report->field[2]->value[0] = left;
- axff->report->field[3]->value[0] = right;
+ for (i = 0; i < report->maxfield; i++) {
+ for (j = 0; j < report->field[i]->report_count; j++) {
+ report->field[i]->value[j] =
+ field_count % 2 ? right : left;
+ field_count++;
+ }
+ }
+
dbg_hid("running with 0x%02x 0x%02x", left, right);
usbhid_submit_report(hid, axff->report, USB_DIR_OUT);
@@ -72,6 +80,8 @@ static int axff_init(struct hid_device *hid)
struct hid_input *hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
+ int field_count = 0;
+ int i, j;
int error;
if (list_empty(report_list)) {
@@ -80,9 +90,16 @@ static int axff_init(struct hid_device *hid)
}
report = list_first_entry(report_list, struct hid_report, list);
+ for (i = 0; i < report->maxfield; i++) {
+ for (j = 0; j < report->field[i]->report_count; j++) {
+ report->field[i]->value[j] = 0x00;
+ field_count++;
+ }
+ }
- if (report->maxfield < 4) {
- hid_err(hid, "no fields in the report: %d\n", report->maxfield);
+ if (field_count < 4) {
+ hid_err(hid, "not enough fields in the report: %d\n",
+ field_count);
return -ENODEV;
}
@@ -97,13 +114,9 @@ static int axff_init(struct hid_device *hid)
goto err_free_mem;
axff->report = report;
- axff->report->field[0]->value[0] = 0x00;
- axff->report->field[1]->value[0] = 0x00;
- axff->report->field[2]->value[0] = 0x00;
- axff->report->field[3]->value[0] = 0x00;
usbhid_submit_report(hid, axff->report, USB_DIR_OUT);
- hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun<x0r@dv-life.ru>\n");
+ hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun <x0r@dv-life.ru>\n");
return 0;
@@ -154,6 +167,7 @@ static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id)
error = hid_hw_open(hdev);
if (error) {
dev_err(&hdev->dev, "hw open failed\n");
+ hid_hw_stop(hdev);
return error;
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 6f3289a57888..848a56c0279c 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -29,6 +29,7 @@
#include <linux/wait.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
+#include <linux/semaphore.h>
#include <linux/hid.h>
#include <linux/hiddev.h>
@@ -1085,16 +1086,25 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
struct hid_report *report;
char *buf;
unsigned int i;
- int ret;
+ int ret = 0;
- if (!hid || !hid->driver)
+ if (!hid)
return -ENODEV;
+
+ if (down_trylock(&hid->driver_lock))
+ return -EBUSY;
+
+ if (!hid->driver) {
+ ret = -ENODEV;
+ goto unlock;
+ }
report_enum = hid->report_enum + type;
hdrv = hid->driver;
if (!size) {
dbg_hid("empty report\n");
- return -1;
+ ret = -1;
+ goto unlock;
}
buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC);
@@ -1118,18 +1128,24 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
nomem:
report = hid_get_report(report_enum, data);
- if (!report)
- return -1;
+ if (!report) {
+ ret = -1;
+ goto unlock;
+ }
if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
ret = hdrv->raw_event(hid, report, data, size);
- if (ret != 0)
- return ret < 0 ? ret : 0;
+ if (ret != 0) {
+ ret = ret < 0 ? ret : 0;
+ goto unlock;
+ }
}
hid_report_raw_event(hid, type, data, size, interrupt);
- return 0;
+unlock:
+ up(&hid->driver_lock);
+ return ret;
}
EXPORT_SYMBOL_GPL(hid_input_report);
@@ -1212,6 +1228,12 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev,
connect_mask & HID_CONNECT_HIDINPUT_FORCE))
hdev->claimed |= HID_CLAIMED_INPUT;
+ if (hdev->quirks & HID_QUIRK_MULTITOUCH) {
+ /* this device should be handled by hid-multitouch, skip it */
+ hdev->quirks &= ~HID_QUIRK_MULTITOUCH;
+ return -ENODEV;
+ }
+
if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect &&
!hdev->hiddev_connect(hdev,
connect_mask & HID_CONNECT_HIDDEV_FORCE))
@@ -1340,9 +1362,22 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT) },
@@ -1388,6 +1423,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6650) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, USB_DEVICE_ID_ILITEK_MULTITOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, USB_DEVICE_ID_IRTOUCH_INFRARED_USB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
@@ -1395,6 +1432,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LG, USB_DEVICE_ID_LG_MULTITOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
@@ -1416,8 +1454,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
@@ -1426,10 +1467,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, USB_DEVICE_ID_CRYSTALTOUCH_DUAL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
@@ -1455,6 +1498,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_PCI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
@@ -1491,19 +1535,27 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0709) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
{ }
};
@@ -1610,10 +1662,15 @@ static int hid_device_probe(struct device *dev)
const struct hid_device_id *id;
int ret = 0;
+ if (down_interruptible(&hdev->driver_lock))
+ return -EINTR;
+
if (!hdev->driver) {
id = hid_match_device(hdev, hdrv);
- if (id == NULL)
- return -ENODEV;
+ if (id == NULL) {
+ ret = -ENODEV;
+ goto unlock;
+ }
hdev->driver = hdrv;
if (hdrv->probe) {
@@ -1626,14 +1683,20 @@ static int hid_device_probe(struct device *dev)
if (ret)
hdev->driver = NULL;
}
+unlock:
+ up(&hdev->driver_lock);
return ret;
}
static int hid_device_remove(struct device *dev)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct hid_driver *hdrv = hdev->driver;
+ struct hid_driver *hdrv;
+ if (down_interruptible(&hdev->driver_lock))
+ return -EINTR;
+
+ hdrv = hdev->driver;
if (hdrv) {
if (hdrv->remove)
hdrv->remove(hdev);
@@ -1642,6 +1705,7 @@ static int hid_device_remove(struct device *dev)
hdev->driver = NULL;
}
+ up(&hdev->driver_lock);
return 0;
}
@@ -1771,7 +1835,6 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) },
{ HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) },
- { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_JESS_YUREX) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) },
@@ -1883,6 +1946,15 @@ static const struct hid_device_id hid_mouse_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ }
@@ -1912,6 +1984,11 @@ static bool hid_ignore(struct hid_device *hdev)
hdev->product <= USB_DEVICE_ID_HANWANG_TABLET_LAST)
return true;
break;
+ case USB_VENDOR_ID_JESS:
+ if (hdev->product == USB_DEVICE_ID_JESS_YUREX &&
+ hdev->type == HID_TYPE_USBNONE)
+ return true;
+ break;
}
if (hdev->type == HID_TYPE_USBMOUSE &&
@@ -1985,6 +2062,7 @@ struct hid_device *hid_allocate_device(void)
init_waitqueue_head(&hdev->debug_wait);
INIT_LIST_HEAD(&hdev->debug_list);
+ sema_init(&hdev->driver_lock, 1);
return hdev;
err:
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index bae48745bb42..ee80d733801d 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -31,6 +31,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/sched.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
@@ -450,6 +451,11 @@ void hid_dump_field(struct hid_field *field, int n, struct seq_file *f) {
seq_printf(f, "Logical(");
hid_resolv_usage(field->logical, f); seq_printf(f, ")\n");
}
+ if (field->application) {
+ tab(n, f);
+ seq_printf(f, "Application(");
+ hid_resolv_usage(field->application, f); seq_printf(f, ")\n");
+ }
tab(n, f); seq_printf(f, "Usage(%d)\n", field->maxusage);
for (j = 0; j < field->maxusage; j++) {
tab(n+2, f); hid_resolv_usage(field->usage[j].hid, f); seq_printf(f, "\n");
diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c
index 61eece47204d..e832f44ae383 100644
--- a/drivers/hid/hid-dr.c
+++ b/drivers/hid/hid-dr.c
@@ -31,6 +31,7 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/hid.h>
+#include <linux/module.h>
#include "hid-ids.h"
diff --git a/drivers/hid/hid-emsff.c b/drivers/hid/hid-emsff.c
index 81877c67caea..9bdde867a02f 100644
--- a/drivers/hid/hid-emsff.c
+++ b/drivers/hid/hid-emsff.c
@@ -24,6 +24,7 @@
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/usb.h>
+#include <linux/module.h>
#include "hid-ids.h"
#include "usbhid/usbhid.h"
@@ -126,7 +127,12 @@ static int ems_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err;
}
- emsff_init(hdev);
+ ret = emsff_init(hdev);
+ if (ret) {
+ dev_err(&hdev->dev, "force feedback init failed\n");
+ hid_hw_stop(hdev);
+ goto err;
+ }
return 0;
err:
diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c
index 279ba530003c..f1e1bcf67427 100644
--- a/drivers/hid/hid-gaff.c
+++ b/drivers/hid/hid-gaff.c
@@ -31,6 +31,7 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/hid.h>
+#include <linux/module.h>
#include "hid-ids.h"
#ifdef CONFIG_GREENASIA_FF
diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c
new file mode 100644
index 000000000000..4e7542151e22
--- /dev/null
+++ b/drivers/hid/hid-holtekff.c
@@ -0,0 +1,241 @@
+/*
+ * Force feedback support for Holtek On Line Grip based gamepads
+ *
+ * These include at least a Brazilian "Clone Joypad Super Power Fire"
+ * which uses vendor ID 0x1241 and identifies as "HOLTEK On Line Grip".
+ *
+ * Copyright (c) 2011 Anssi Hannula <anssi.hannula@iki.fi>
+ */
+
+/*
+ * 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/hid.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+#ifdef CONFIG_HOLTEK_FF
+#include "usbhid/usbhid.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
+MODULE_DESCRIPTION("Force feedback support for Holtek On Line Grip based devices");
+
+/*
+ * These commands and parameters are currently known:
+ *
+ * byte 0: command id:
+ * 01 set effect parameters
+ * 02 play specified effect
+ * 03 stop specified effect
+ * 04 stop all effects
+ * 06 stop all effects
+ * (the difference between 04 and 06 isn't known; win driver
+ * sends 06,04 on application init, and 06 otherwise)
+ *
+ * Commands 01 and 02 need to be sent as pairs, i.e. you need to send 01
+ * before each 02.
+ *
+ * The rest of the bytes are parameters. Command 01 takes all of them, and
+ * commands 02,03 take only the effect id.
+ *
+ * byte 1:
+ * bits 0-3: effect id:
+ * 1: very strong rumble
+ * 2: periodic rumble, short intervals
+ * 3: very strong rumble
+ * 4: periodic rumble, long intervals
+ * 5: weak periodic rumble, long intervals
+ * 6: weak periodic rumble, short intervals
+ * 7: periodic rumble, short intervals
+ * 8: strong periodic rumble, short intervals
+ * 9: very strong rumble
+ * a: causes an error
+ * b: very strong periodic rumble, very short intervals
+ * c-f: nothing
+ * bit 6: right (weak) motor enabled
+ * bit 7: left (strong) motor enabled
+ *
+ * bytes 2-3: time in milliseconds, big-endian
+ * bytes 5-6: unknown (win driver seems to use at least 10e0 with effect 1
+ * and 0014 with effect 6)
+ * byte 7:
+ * bits 0-3: effect magnitude
+ */
+
+#define HOLTEKFF_MSG_LENGTH 7
+
+static const u8 start_effect_1[] = { 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const u8 stop_all4[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const u8 stop_all6[] = { 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+struct holtekff_device {
+ struct hid_field *field;
+};
+
+static void holtekff_send(struct holtekff_device *holtekff,
+ struct hid_device *hid,
+ const u8 data[HOLTEKFF_MSG_LENGTH])
+{
+ int i;
+
+ for (i = 0; i < HOLTEKFF_MSG_LENGTH; i++) {
+ holtekff->field->value[i] = data[i];
+ }
+
+ dbg_hid("sending %02x %02x %02x %02x %02x %02x %02x\n", data[0],
+ data[1], data[2], data[3], data[4], data[5], data[6]);
+
+ usbhid_submit_report(hid, holtekff->field->report, USB_DIR_OUT);
+}
+
+static int holtekff_play(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+ struct holtekff_device *holtekff = data;
+ int left, right;
+ /* effect type 1, length 65535 msec */
+ u8 buf[HOLTEKFF_MSG_LENGTH] =
+ { 0x01, 0x01, 0xff, 0xff, 0x10, 0xe0, 0x00 };
+
+ left = effect->u.rumble.strong_magnitude;
+ right = effect->u.rumble.weak_magnitude;
+ dbg_hid("called with 0x%04x 0x%04x\n", left, right);
+
+ if (!left && !right) {
+ holtekff_send(holtekff, hid, stop_all6);
+ return 0;
+ }
+
+ if (left)
+ buf[1] |= 0x80;
+ if (right)
+ buf[1] |= 0x40;
+
+ /* The device takes a single magnitude, so we just sum them up. */
+ buf[6] = min(0xf, (left >> 12) + (right >> 12));
+
+ holtekff_send(holtekff, hid, buf);
+ holtekff_send(holtekff, hid, start_effect_1);
+
+ return 0;
+}
+
+static int holtekff_init(struct hid_device *hid)
+{
+ struct holtekff_device *holtekff;
+ struct hid_report *report;
+ struct hid_input *hidinput = list_entry(hid->inputs.next,
+ struct hid_input, list);
+ struct list_head *report_list =
+ &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct input_dev *dev = hidinput->input;
+ int error;
+
+ if (list_empty(report_list)) {
+ hid_err(hid, "no output report found\n");
+ return -ENODEV;
+ }
+
+ report = list_entry(report_list->next, struct hid_report, list);
+
+ if (report->maxfield < 1 || report->field[0]->report_count != 7) {
+ hid_err(hid, "unexpected output report layout\n");
+ return -ENODEV;
+ }
+
+ holtekff = kzalloc(sizeof(*holtekff), GFP_KERNEL);
+ if (!holtekff)
+ return -ENOMEM;
+
+ set_bit(FF_RUMBLE, dev->ffbit);
+
+ holtekff->field = report->field[0];
+
+ /* initialize the same way as win driver does */
+ holtekff_send(holtekff, hid, stop_all4);
+ holtekff_send(holtekff, hid, stop_all6);
+
+ error = input_ff_create_memless(dev, holtekff, holtekff_play);
+ if (error) {
+ kfree(holtekff);
+ return error;
+ }
+
+ hid_info(hid, "Force feedback for Holtek On Line Grip based devices by Anssi Hannula <anssi.hannula@iki.fi>\n");
+
+ return 0;
+}
+#else
+static inline int holtekff_init(struct hid_device *hid)
+{
+ return 0;
+}
+#endif
+
+static int holtek_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "parse failed\n");
+ goto err;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ goto err;
+ }
+
+ holtekff_init(hdev);
+
+ return 0;
+err:
+ return ret;
+}
+
+static const struct hid_device_id holtek_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, holtek_devices);
+
+static struct hid_driver holtek_driver = {
+ .name = "holtek",
+ .id_table = holtek_devices,
+ .probe = holtek_probe,
+};
+
+static int __init holtek_init(void)
+{
+ return hid_register_driver(&holtek_driver);
+}
+
+static void __exit holtek_exit(void)
+{
+ hid_unregister_driver(&holtek_driver);
+}
+
+module_init(holtek_init);
+module_exit(holtek_exit);
+
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index a756ee6c7df5..06ce996b8b65 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -109,9 +109,22 @@
#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245
#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246
#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247
+#define USB_DEVICE_ID_APPLE_ALU_REVB_ANSI 0x024f
+#define USB_DEVICE_ID_APPLE_ALU_REVB_ISO 0x0250
+#define USB_DEVICE_ID_APPLE_ALU_REVB_JIS 0x0251
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI 0x024c
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO 0x024d
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS 0x024e
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
@@ -274,6 +287,7 @@
#define USB_DEVICE_ID_PENPOWER 0x00f4
#define USB_VENDOR_ID_GREENASIA 0x0e8f
+#define USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD 0x3013
#define USB_VENDOR_ID_GRETAGMACBETH 0x0971
#define USB_DEVICE_ID_GRETAGMACBETH_HUEY 0x2005
@@ -347,9 +361,15 @@
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
+#define USB_VENDOR_ID_IDEACOM 0x1cb6
+#define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650
+
#define USB_VENDOR_ID_ILITEK 0x222a
#define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001
+#define USB_VENDOR_ID_HOLTEK 0x1241
+#define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015
+
#define USB_VENDOR_ID_IMATION 0x0718
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
@@ -416,6 +436,9 @@
#define USB_DEVICE_ID_LD_HYBRID 0x2090
#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0
+#define USB_VENDOR_ID_LG 0x1fd2
+#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
+
#define USB_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
@@ -433,6 +456,7 @@
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298
#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
+#define USB_DEVICE_ID_LOGITECH_DFGT_WHEEL 0xc29a
#define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b
#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
@@ -440,6 +464,8 @@
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513
+#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER 0xc52b
+#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532
#define USB_DEVICE_ID_SPACETRAVELLER 0xc623
#define USB_DEVICE_ID_SPACENAVIGATOR 0xc626
#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704
@@ -472,6 +498,8 @@
#define USB_DEVICE_ID_MS_LK6K 0x00f9
#define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701
#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713
+#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730
+#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c
#define USB_VENDOR_ID_MOJO 0x8282
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
@@ -495,6 +523,9 @@
#define USB_VENDOR_ID_NEXTWINDOW 0x1926
#define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN 0x0003
+#define USB_VENDOR_ID_NINTENDO 0x057e
+#define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306
+
#define USB_VENDOR_ID_NTRIG 0x1b96
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003
@@ -568,6 +599,9 @@
#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001
#define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600
+#define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f
+#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002
+
#define USB_VENDOR_ID_SKYCABLE 0x1223
#define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07
@@ -630,6 +664,7 @@
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U 0x0003
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U 0x0004
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005
+#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064
#define USB_VENDOR_ID_UNITEC 0x227d
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
@@ -662,6 +697,15 @@
#define USB_VENDOR_ID_WISEGROUP_LTD 0x6666
#define USB_VENDOR_ID_WISEGROUP_LTD2 0x6677
#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
+#define USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO 0x8801
+#define USB_DEVICE_ID_SUPER_DUAL_BOX_PRO 0x8802
+#define USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO 0x8804
+
+#define USB_VENDOR_ID_X_TENSIONS 0x1ae7
+#define USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE 0x9001
+
+#define USB_VENDOR_ID_XAT 0x2505
+#define USB_DEVICE_ID_XAT_CSR 0x0220
#define USB_VENDOR_ID_YEALINK 0x6993
#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
@@ -671,4 +715,7 @@
#define USB_VENDOR_ID_ZYDACRON 0x13EC
#define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL 0x0006
+#define USB_VENDOR_ID_PRIMAX 0x0461
+#define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05
+
#endif
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 6559e2e3364e..f333139d1a48 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -474,6 +474,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
map_key_clear(BTN_STYLUS2);
break;
+ case 0x51: /* ContactID */
+ device->quirks |= HID_QUIRK_MULTITOUCH;
+ goto unknown;
+
default: goto unknown;
}
break;
@@ -978,6 +982,13 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
}
+ if (hid->quirks & HID_QUIRK_MULTITOUCH) {
+ /* generic hid does not know how to handle multitouch devices */
+ if (hidinput)
+ goto out_cleanup;
+ goto out_unwind;
+ }
+
if (hidinput && input_register_device(hidinput->input))
goto out_cleanup;
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 21f205f09250..e7a7bd1eb34a 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -41,6 +41,66 @@
#define LG_FF3 0x1000
#define LG_FF4 0x2000
+/* Size of the original descriptor of the Driving Force Pro wheel */
+#define DFP_RDESC_ORIG_SIZE 97
+
+/* Fixed report descriptor for Logitech Driving Force Pro wheel controller
+ *
+ * The original descriptor hides the separate throttle and brake axes in
+ * a custom vendor usage page, providing only a combined value as
+ * GenericDesktop.Y.
+ * This descriptor removes the combined Y axis and instead reports
+ * separate throttle (Y) and brake (RZ).
+ */
+static __u8 dfp_rdesc_fixed[] = {
+0x05, 0x01, /* Usage Page (Desktop), */
+0x09, 0x04, /* Usage (Joystik), */
+0xA1, 0x01, /* Collection (Application), */
+0xA1, 0x02, /* Collection (Logical), */
+0x95, 0x01, /* Report Count (1), */
+0x75, 0x0E, /* Report Size (14), */
+0x14, /* Logical Minimum (0), */
+0x26, 0xFF, 0x3F, /* Logical Maximum (16383), */
+0x34, /* Physical Minimum (0), */
+0x46, 0xFF, 0x3F, /* Physical Maximum (16383), */
+0x09, 0x30, /* Usage (X), */
+0x81, 0x02, /* Input (Variable), */
+0x95, 0x0E, /* Report Count (14), */
+0x75, 0x01, /* Report Size (1), */
+0x25, 0x01, /* Logical Maximum (1), */
+0x45, 0x01, /* Physical Maximum (1), */
+0x05, 0x09, /* Usage Page (Button), */
+0x19, 0x01, /* Usage Minimum (01h), */
+0x29, 0x0E, /* Usage Maximum (0Eh), */
+0x81, 0x02, /* Input (Variable), */
+0x05, 0x01, /* Usage Page (Desktop), */
+0x95, 0x01, /* Report Count (1), */
+0x75, 0x04, /* Report Size (4), */
+0x25, 0x07, /* Logical Maximum (7), */
+0x46, 0x3B, 0x01, /* Physical Maximum (315), */
+0x65, 0x14, /* Unit (Degrees), */
+0x09, 0x39, /* Usage (Hat Switch), */
+0x81, 0x42, /* Input (Variable, Nullstate), */
+0x65, 0x00, /* Unit, */
+0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+0x46, 0xFF, 0x00, /* Physical Maximum (255), */
+0x75, 0x08, /* Report Size (8), */
+0x81, 0x01, /* Input (Constant), */
+0x09, 0x31, /* Usage (Y), */
+0x81, 0x02, /* Input (Variable), */
+0x09, 0x35, /* Usage (Rz), */
+0x81, 0x02, /* Input (Variable), */
+0x81, 0x01, /* Input (Constant), */
+0xC0, /* End Collection, */
+0xA1, 0x02, /* Collection (Logical), */
+0x09, 0x02, /* Usage (02h), */
+0x95, 0x07, /* Report Count (7), */
+0x91, 0x02, /* Output (Variable), */
+0xC0, /* End Collection, */
+0xC0 /* End Collection */
+};
+
+
/*
* Certain Logitech keyboards send in report #3 keys which are far
* above the logical maximum described in descriptor. This extends
@@ -74,6 +134,18 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
rdesc[47] = 0x95;
rdesc[48] = 0x0B;
}
+
+ switch (hdev->product) {
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+ if (*rsize == DFP_RDESC_ORIG_SIZE) {
+ hid_info(hdev,
+ "fixing up Logitech Driving Force Pro report descriptor\n");
+ rdesc = dfp_rdesc_fixed;
+ *rsize = sizeof(dfp_rdesc_fixed);
+ }
+ break;
+ }
+
return rdesc;
}
@@ -291,7 +363,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_free;
}
- if (quirks & (LG_FF | LG_FF2 | LG_FF3))
+ if (quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
connect_mask &= ~HID_CONNECT_FF;
ret = hid_hw_start(hdev, connect_mask);
@@ -300,7 +372,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_free;
}
- if (quirks & LG_FF4) {
+ /* Setup wireless link with Logitech Wii wheel */
+ if(hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
@@ -333,6 +406,15 @@ err_free:
return ret;
}
+static void lg_remove(struct hid_device *hdev)
+{
+ unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+ if(quirks & LG_FF4)
+ lg4ff_deinit(hdev);
+
+ hid_hw_stop(hdev);
+}
+
static const struct hid_device_id lg_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
.driver_data = LG_RDESC | LG_WIRELESS },
@@ -359,7 +441,7 @@ static const struct hid_device_id lg_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
.driver_data = LG_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
- .driver_data = LG_NOGET | LG_FF },
+ .driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
.driver_data = LG_FF2 },
@@ -372,15 +454,17 @@ static const struct hid_device_id lg_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
- .driver_data = LG_FF },
+ .driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
- .driver_data = LG_FF },
+ .driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
- .driver_data = LG_FF },
+ .driver_data = LG_FF4 },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
+ .driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
- .driver_data = LG_FF },
+ .driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
- .driver_data = LG_FF },
+ .driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
.driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
@@ -406,6 +490,7 @@ static struct hid_driver lg_driver = {
.input_mapped = lg_input_mapped,
.event = lg_event,
.probe = lg_probe,
+ .remove = lg_remove,
};
static int __init lg_init(void)
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
index b0100ba2ae0b..4b097286dc78 100644
--- a/drivers/hid/hid-lg.h
+++ b/drivers/hid/hid-lg.h
@@ -19,10 +19,12 @@ int lg3ff_init(struct hid_device *hdev);
static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
#endif
-#ifdef CONFIG_LOGIWII_FF
+#ifdef CONFIG_LOGIWHEELS_FF
int lg4ff_init(struct hid_device *hdev);
+int lg4ff_deinit(struct hid_device *hdev);
#else
static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
+static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
#endif
#endif
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index fa550c8e1d1b..103f30d93f76 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -29,19 +29,108 @@
#include "usbhid/usbhid.h"
#include "hid-lg.h"
+#include "hid-ids.h"
-struct lg4ff_device {
- struct hid_report *report;
+#define DFGT_REV_MAJ 0x13
+#define DFGT_REV_MIN 0x22
+#define DFP_REV_MAJ 0x11
+#define DFP_REV_MIN 0x06
+#define FFEX_REV_MAJ 0x21
+#define FFEX_REV_MIN 0x00
+#define G25_REV_MAJ 0x12
+#define G25_REV_MIN 0x22
+#define G27_REV_MAJ 0x12
+#define G27_REV_MIN 0x38
+
+#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
+
+static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
+static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
+static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+
+static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
+
+static bool list_inited;
+
+struct lg4ff_device_entry {
+ char *device_id; /* Use name in respective kobject structure's address as the ID */
+ __u16 range;
+ __u16 min_range;
+ __u16 max_range;
+ __u8 leds;
+ struct list_head list;
+ void (*set_range)(struct hid_device *hid, u16 range);
};
-static const signed short ff4_wheel_ac[] = {
+static struct lg4ff_device_entry device_list;
+
+static const signed short lg4ff_wheel_effects[] = {
FF_CONSTANT,
FF_AUTOCENTER,
-1
};
-static int hid_lg4ff_play(struct input_dev *dev, void *data,
- struct ff_effect *effect)
+struct lg4ff_wheel {
+ const __u32 product_id;
+ const signed short *ff_effects;
+ const __u16 min_range;
+ const __u16 max_range;
+ void (*set_range)(struct hid_device *hid, u16 range);
+};
+
+static const struct lg4ff_wheel lg4ff_devices[] = {
+ {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
+ {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
+ {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_dfp},
+ {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
+ {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
+ {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
+ {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL},
+ {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}
+};
+
+struct lg4ff_native_cmd {
+ const __u8 cmd_num; /* Number of commands to send */
+ const __u8 cmd[];
+};
+
+struct lg4ff_usb_revision {
+ const __u16 rev_maj;
+ const __u16 rev_min;
+ const struct lg4ff_native_cmd *command;
+};
+
+static const struct lg4ff_native_cmd native_dfp = {
+ 1,
+ {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+
+static const struct lg4ff_native_cmd native_dfgt = {
+ 2,
+ {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */
+ 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* 2nd command */
+};
+
+static const struct lg4ff_native_cmd native_g25 = {
+ 1,
+ {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+
+static const struct lg4ff_native_cmd native_g27 = {
+ 2,
+ {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */
+ 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* 2nd command */
+};
+
+static const struct lg4ff_usb_revision lg4ff_revs[] = {
+ {DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt}, /* Driving Force GT */
+ {DFP_REV_MAJ, DFP_REV_MIN, &native_dfp}, /* Driving Force Pro */
+ {G25_REV_MAJ, G25_REV_MIN, &native_g25}, /* G25 */
+ {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */
+};
+
+static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
@@ -55,13 +144,12 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data,
x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
CLAMP(x);
report->field[0]->value[0] = 0x11; /* Slot 1 */
- report->field[0]->value[1] = 0x10;
+ report->field[0]->value[1] = 0x08;
report->field[0]->value[2] = x;
- report->field[0]->value[3] = 0x00;
+ report->field[0]->value[3] = 0x80;
report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x08;
+ report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
- dbg_hid("Autocenter, x=0x%02X\n", x);
usbhid_submit_report(hid, report, USB_DIR_OUT);
break;
@@ -69,24 +157,184 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data,
return 0;
}
-static void hid_lg4ff_set_autocenter(struct input_dev *dev, u16 magnitude)
+/* Sends default autocentering command compatible with
+ * all wheels except Formula Force EX */
+static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
{
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
- *value++ = 0xfe;
- *value++ = 0x0d;
- *value++ = 0x07;
- *value++ = 0x07;
- *value++ = (magnitude >> 8) & 0xff;
- *value++ = 0x00;
- *value = 0x00;
+ report->field[0]->value[0] = 0xfe;
+ report->field[0]->value[1] = 0x0d;
+ report->field[0]->value[2] = magnitude >> 13;
+ report->field[0]->value[3] = magnitude >> 13;
+ report->field[0]->value[4] = magnitude >> 8;
+ report->field[0]->value[5] = 0x00;
+ report->field[0]->value[6] = 0x00;
+
+ usbhid_submit_report(hid, report, USB_DIR_OUT);
+}
+
+/* Sends autocentering command compatible with Formula Force EX */
+static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ magnitude = magnitude * 90 / 65535;
+
+
+ report->field[0]->value[0] = 0xfe;
+ report->field[0]->value[1] = 0x03;
+ report->field[0]->value[2] = magnitude >> 14;
+ report->field[0]->value[3] = magnitude >> 14;
+ report->field[0]->value[4] = magnitude;
+ report->field[0]->value[5] = 0x00;
+ report->field[0]->value[6] = 0x00;
+
+ usbhid_submit_report(hid, report, USB_DIR_OUT);
+}
+
+/* Sends command to set range compatible with G25/G27/Driving Force GT */
+static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
+{
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
+
+ report->field[0]->value[0] = 0xf8;
+ report->field[0]->value[1] = 0x81;
+ report->field[0]->value[2] = range & 0x00ff;
+ report->field[0]->value[3] = (range & 0xff00) >> 8;
+ report->field[0]->value[4] = 0x00;
+ report->field[0]->value[5] = 0x00;
+ report->field[0]->value[6] = 0x00;
+
+ usbhid_submit_report(hid, report, USB_DIR_OUT);
+}
+
+/* Sends commands to set range compatible with Driving Force Pro wheel */
+static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
+{
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ int start_left, start_right, full_range;
+ dbg_hid("Driving Force Pro: setting range to %u\n", range);
+
+ /* Prepare "coarse" limit command */
+ report->field[0]->value[0] = 0xf8;
+ report->field[0]->value[1] = 0x00; /* Set later */
+ report->field[0]->value[2] = 0x00;
+ report->field[0]->value[3] = 0x00;
+ report->field[0]->value[4] = 0x00;
+ report->field[0]->value[5] = 0x00;
+ report->field[0]->value[6] = 0x00;
+
+ if (range > 200) {
+ report->field[0]->value[1] = 0x03;
+ full_range = 900;
+ } else {
+ report->field[0]->value[1] = 0x02;
+ full_range = 200;
+ }
+ usbhid_submit_report(hid, report, USB_DIR_OUT);
+
+ /* Prepare "fine" limit command */
+ report->field[0]->value[0] = 0x81;
+ report->field[0]->value[1] = 0x0b;
+ report->field[0]->value[2] = 0x00;
+ report->field[0]->value[3] = 0x00;
+ report->field[0]->value[4] = 0x00;
+ report->field[0]->value[5] = 0x00;
+ report->field[0]->value[6] = 0x00;
+
+ if (range == 200 || range == 900) { /* Do not apply any fine limit */
+ usbhid_submit_report(hid, report, USB_DIR_OUT);
+ return;
+ }
+
+ /* Construct fine limit command */
+ start_left = (((full_range - range + 1) * 2047) / full_range);
+ start_right = 0xfff - start_left;
+
+ report->field[0]->value[2] = start_left >> 4;
+ report->field[0]->value[3] = start_right >> 4;
+ report->field[0]->value[4] = 0xff;
+ report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
+ report->field[0]->value[6] = 0xff;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
+static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd)
+{
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ __u8 i, j;
+
+ j = 0;
+ while (j < 7*cmd->cmd_num) {
+ for (i = 0; i < 7; i++)
+ report->field[0]->value[i] = cmd->cmd[j++];
+
+ usbhid_submit_report(hid, report, USB_DIR_OUT);
+ }
+}
+
+/* Read current range and display it in terminal */
+static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct lg4ff_device_entry *uninitialized_var(entry);
+ struct list_head *h;
+ struct hid_device *hid = to_hid_device(dev);
+ size_t count;
+
+ list_for_each(h, &device_list.list) {
+ entry = list_entry(h, struct lg4ff_device_entry, list);
+ if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0)
+ break;
+ }
+ if (h == &device_list.list) {
+ dbg_hid("Device not found!");
+ return 0;
+ }
+
+ count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->range);
+ return count;
+}
+
+/* Set range to user specified value, call appropriate function
+ * according to the type of the wheel */
+static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lg4ff_device_entry *uninitialized_var(entry);
+ struct list_head *h;
+ struct hid_device *hid = to_hid_device(dev);
+ __u16 range = simple_strtoul(buf, NULL, 10);
+
+ list_for_each(h, &device_list.list) {
+ entry = list_entry(h, struct lg4ff_device_entry, list);
+ if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0)
+ break;
+ }
+ if (h == &device_list.list) {
+ dbg_hid("Device not found!");
+ return count;
+ }
+
+ if (range == 0)
+ range = entry->max_range;
+
+ /* Check if the wheel supports range setting
+ * and that the range is within limits for the wheel */
+ if (entry->set_range != NULL && range >= entry->min_range && range <= entry->max_range) {
+ entry->set_range(hid, range);
+ entry->range = range;
+ }
+
+ return count;
+}
int lg4ff_init(struct hid_device *hid)
{
@@ -95,9 +343,10 @@ int lg4ff_init(struct hid_device *hid)
struct input_dev *dev = hidinput->input;
struct hid_report *report;
struct hid_field *field;
- const signed short *ff_bits = ff4_wheel_ac;
- int error;
- int i;
+ struct lg4ff_device_entry *entry;
+ struct usb_device_descriptor *udesc;
+ int error, i, j;
+ __u16 bcdDevice, rev_maj, rev_min;
/* Find the report to use */
if (list_empty(report_list)) {
@@ -118,18 +367,122 @@ int lg4ff_init(struct hid_device *hid)
return -1;
}
- for (i = 0; ff_bits[i] >= 0; i++)
- set_bit(ff_bits[i], dev->ffbit);
+ /* Check what wheel has been connected */
+ for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
+ if (hid->product == lg4ff_devices[i].product_id) {
+ dbg_hid("Found compatible device, product ID %04X\n", lg4ff_devices[i].product_id);
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(lg4ff_devices)) {
+ hid_err(hid, "Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to"
+ "LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>\n");
+ return -1;
+ }
+
+ /* Attempt to switch wheel to native mode when applicable */
+ udesc = &(hid_to_usb_dev(hid)->descriptor);
+ if (!udesc) {
+ hid_err(hid, "NULL USB device descriptor\n");
+ return -1;
+ }
+ bcdDevice = le16_to_cpu(udesc->bcdDevice);
+ rev_maj = bcdDevice >> 8;
+ rev_min = bcdDevice & 0xff;
+
+ if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL) {
+ dbg_hid("Generic wheel detected, can it do native?\n");
+ dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min);
+
+ for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) {
+ if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) {
+ hid_lg4ff_switch_native(hid, lg4ff_revs[j].command);
+ hid_info(hid, "Switched to native mode\n");
+ }
+ }
+ }
+
+ /* Set supported force feedback capabilities */
+ for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
+ set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
if (error)
return error;
- if (test_bit(FF_AUTOCENTER, dev->ffbit))
- dev->ff->set_autocenter = hid_lg4ff_set_autocenter;
+ /* Check if autocentering is available and
+ * set the centering force to zero by default */
+ if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
+ if(rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
+ dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
+ else
+ dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
+
+ dev->ff->set_autocenter(dev, 0);
+ }
+
+ /* Initialize device_list if this is the first device to handle by lg4ff */
+ if (!list_inited) {
+ INIT_LIST_HEAD(&device_list.list);
+ list_inited = 1;
+ }
+
+ /* Add the device to device_list */
+ entry = (struct lg4ff_device_entry *)kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
+ if (!entry) {
+ hid_err(hid, "Cannot add device, insufficient memory.\n");
+ return -ENOMEM;
+ }
+ entry->device_id = kstrdup((&hid->dev)->kobj.name, GFP_KERNEL);
+ if (!entry->device_id) {
+ hid_err(hid, "Cannot set device_id, insufficient memory.\n");
+ kfree(entry);
+ return -ENOMEM;
+ }
+ entry->min_range = lg4ff_devices[i].min_range;
+ entry->max_range = lg4ff_devices[i].max_range;
+ entry->set_range = lg4ff_devices[i].set_range;
+ list_add(&entry->list, &device_list.list);
+
+ /* Create sysfs interface */
+ error = device_create_file(&hid->dev, &dev_attr_range);
+ if (error)
+ return error;
+ dbg_hid("sysfs interface created\n");
+
+ /* Set the maximum range to start with */
+ entry->range = entry->max_range;
+ if (entry->set_range != NULL)
+ entry->set_range(hid, entry->range);
hid_info(hid, "Force feedback for Logitech Speed Force Wireless by Simon Wood <simon@mungewell.org>\n");
return 0;
}
+int lg4ff_deinit(struct hid_device *hid)
+{
+ bool found = 0;
+ struct lg4ff_device_entry *entry;
+ struct list_head *h, *g;
+ list_for_each_safe(h, g, &device_list.list) {
+ entry = list_entry(h, struct lg4ff_device_entry, list);
+ if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) {
+ list_del(h);
+ kfree(entry->device_id);
+ kfree(entry);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ dbg_hid("Device entry not found!\n");
+ return -1;
+ }
+
+ device_remove_file(&hid->dev, &dev_attr_range);
+ dbg_hid("Device successfully unregistered\n");
+ return 0;
+}
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c
index 088f85049290..27bc54f92f44 100644
--- a/drivers/hid/hid-lgff.c
+++ b/drivers/hid/hid-lgff.c
@@ -58,12 +58,6 @@ static const signed short ff_joystick_ac[] = {
-1
};
-static const signed short ff_wheel[] = {
- FF_CONSTANT,
- FF_AUTOCENTER,
- -1
-};
-
static const struct dev_type devices[] = {
{ 0x046d, 0xc211, ff_rumble },
{ 0x046d, 0xc219, ff_rumble },
@@ -71,14 +65,7 @@ static const struct dev_type devices[] = {
{ 0x046d, 0xc286, ff_joystick_ac },
{ 0x046d, 0xc287, ff_joystick_ac },
{ 0x046d, 0xc293, ff_joystick },
- { 0x046d, 0xc294, ff_wheel },
- { 0x046d, 0xc298, ff_wheel },
- { 0x046d, 0xc299, ff_wheel },
- { 0x046d, 0xc29b, ff_wheel },
{ 0x046d, 0xc295, ff_joystick },
- { 0x046d, 0xc298, ff_wheel },
- { 0x046d, 0xc299, ff_wheel },
- { 0x046d, 0xca03, ff_wheel },
};
static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
new file mode 100644
index 000000000000..38b12e45780c
--- /dev/null
+++ b/drivers/hid/hid-logitech-dj.c
@@ -0,0 +1,922 @@
+/*
+ * HID driver for Logitech Unifying receivers
+ *
+ * Copyright (c) 2011 Logitech
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include "usbhid/usbhid.h"
+#include "hid-ids.h"
+#include "hid-logitech-dj.h"
+
+/* Keyboard descriptor (1) */
+static const char kbd_descriptor[] = {
+ 0x05, 0x01, /* USAGE_PAGE (generic Desktop) */
+ 0x09, 0x06, /* USAGE (Keyboard) */
+ 0xA1, 0x01, /* COLLECTION (Application) */
+ 0x85, 0x01, /* REPORT_ID (1) */
+ 0x95, 0x08, /* REPORT_COUNT (8) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
+ 0x05, 0x07, /* USAGE_PAGE (Keyboard) */
+ 0x19, 0xE0, /* USAGE_MINIMUM (Left Control) */
+ 0x29, 0xE7, /* USAGE_MAXIMUM (Right GUI) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x95, 0x05, /* REPORT COUNT (5) */
+ 0x05, 0x08, /* USAGE PAGE (LED page) */
+ 0x19, 0x01, /* USAGE MINIMUM (1) */
+ 0x29, 0x05, /* USAGE MAXIMUM (5) */
+ 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */
+ 0x95, 0x01, /* REPORT COUNT (1) */
+ 0x75, 0x03, /* REPORT SIZE (3) */
+ 0x91, 0x01, /* OUTPUT (Constant) */
+ 0x95, 0x06, /* REPORT_COUNT (6) */
+ 0x75, 0x08, /* REPORT_SIZE (8) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x26, 0xFF, 0x00, /* LOGICAL_MAXIMUM (255) */
+ 0x05, 0x07, /* USAGE_PAGE (Keyboard) */
+ 0x19, 0x00, /* USAGE_MINIMUM (no event) */
+ 0x2A, 0xFF, 0x00, /* USAGE_MAXIMUM (reserved) */
+ 0x81, 0x00, /* INPUT (Data,Ary,Abs) */
+ 0xC0
+};
+
+/* Mouse descriptor (2) */
+static const char mse_descriptor[] = {
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x02, /* USAGE (Mouse) */
+ 0xA1, 0x01, /* COLLECTION (Application) */
+ 0x85, 0x02, /* REPORT_ID = 2 */
+ 0x09, 0x01, /* USAGE (pointer) */
+ 0xA1, 0x00, /* COLLECTION (physical) */
+ 0x05, 0x09, /* USAGE_PAGE (buttons) */
+ 0x19, 0x01, /* USAGE_MIN (1) */
+ 0x29, 0x10, /* USAGE_MAX (16) */
+ 0x15, 0x00, /* LOGICAL_MIN (0) */
+ 0x25, 0x01, /* LOGICAL_MAX (1) */
+ 0x95, 0x10, /* REPORT_COUNT (16) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (data var abs) */
+ 0x05, 0x01, /* USAGE_PAGE (generic desktop) */
+ 0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */
+ 0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */
+ 0x75, 0x0C, /* REPORT_SIZE (12) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x09, 0x30, /* USAGE (X) */
+ 0x09, 0x31, /* USAGE (Y) */
+ 0x81, 0x06, /* INPUT */
+ 0x15, 0x81, /* LOGICAL_MIN (-127) */
+ 0x25, 0x7F, /* LOGICAL_MAX (127) */
+ 0x75, 0x08, /* REPORT_SIZE (8) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x09, 0x38, /* USAGE (wheel) */
+ 0x81, 0x06, /* INPUT */
+ 0x05, 0x0C, /* USAGE_PAGE(consumer) */
+ 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x81, 0x06, /* INPUT */
+ 0xC0, /* END_COLLECTION */
+ 0xC0, /* END_COLLECTION */
+};
+
+/* Consumer Control descriptor (3) */
+static const char consumer_descriptor[] = {
+ 0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */
+ 0x09, 0x01, /* USAGE (Consumer Control) */
+ 0xA1, 0x01, /* COLLECTION (Application) */
+ 0x85, 0x03, /* REPORT_ID = 3 */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x15, 0x01, /* LOGICAL_MIN (1) */
+ 0x26, 0x8C, 0x02, /* LOGICAL_MAX (652) */
+ 0x19, 0x01, /* USAGE_MIN (1) */
+ 0x2A, 0x8C, 0x02, /* USAGE_MAX (652) */
+ 0x81, 0x00, /* INPUT (Data Ary Abs) */
+ 0xC0, /* END_COLLECTION */
+}; /* */
+
+/* System control descriptor (4) */
+static const char syscontrol_descriptor[] = {
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x80, /* USAGE (System Control) */
+ 0xA1, 0x01, /* COLLECTION (Application) */
+ 0x85, 0x04, /* REPORT_ID = 4 */
+ 0x75, 0x02, /* REPORT_SIZE (2) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x15, 0x01, /* LOGICAL_MIN (1) */
+ 0x25, 0x03, /* LOGICAL_MAX (3) */
+ 0x09, 0x82, /* USAGE (System Sleep) */
+ 0x09, 0x81, /* USAGE (System Power Down) */
+ 0x09, 0x83, /* USAGE (System Wake Up) */
+ 0x81, 0x60, /* INPUT (Data Ary Abs NPrf Null) */
+ 0x75, 0x06, /* REPORT_SIZE (6) */
+ 0x81, 0x03, /* INPUT (Cnst Var Abs) */
+ 0xC0, /* END_COLLECTION */
+};
+
+/* Media descriptor (8) */
+static const char media_descriptor[] = {
+ 0x06, 0xbc, 0xff, /* Usage Page 0xffbc */
+ 0x09, 0x88, /* Usage 0x0088 */
+ 0xa1, 0x01, /* BeginCollection */
+ 0x85, 0x08, /* Report ID 8 */
+ 0x19, 0x01, /* Usage Min 0x0001 */
+ 0x29, 0xff, /* Usage Max 0x00ff */
+ 0x15, 0x01, /* Logical Min 1 */
+ 0x26, 0xff, 0x00, /* Logical Max 255 */
+ 0x75, 0x08, /* Report Size 8 */
+ 0x95, 0x01, /* Report Count 1 */
+ 0x81, 0x00, /* Input */
+ 0xc0, /* EndCollection */
+}; /* */
+
+/* Maximum size of all defined hid reports in bytes (including report id) */
+#define MAX_REPORT_SIZE 8
+
+/* Number of possible hid report types that can be created by this driver.
+ *
+ * Right now, RF report types have the same report types (or report id's)
+ * than the hid report created from those RF reports. In the future
+ * this doesnt have to be true.
+ *
+ * For instance, RF report type 0x01 which has a size of 8 bytes, corresponds
+ * to hid report id 0x01, this is standard keyboard. Same thing applies to mice
+ * reports and consumer control, etc. If a new RF report is created, it doesn't
+ * has to have the same report id as its corresponding hid report, so an
+ * translation may have to take place for future report types.
+ */
+#define NUMBER_OF_HID_REPORTS 32
+static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
+ [1] = 8, /* Standard keyboard */
+ [2] = 8, /* Standard mouse */
+ [3] = 5, /* Consumer control */
+ [4] = 2, /* System control */
+ [8] = 2, /* Media Center */
+};
+
+
+#define LOGITECH_DJ_INTERFACE_NUMBER 0x02
+
+static struct hid_ll_driver logi_dj_ll_driver;
+
+static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
+ size_t count,
+ unsigned char report_type);
+
+static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
+ struct dj_report *dj_report)
+{
+ /* Called in delayed work context */
+ struct dj_device *dj_dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&djrcv_dev->lock, flags);
+ dj_dev = djrcv_dev->paired_dj_devices[dj_report->device_index];
+ djrcv_dev->paired_dj_devices[dj_report->device_index] = NULL;
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+
+ if (dj_dev != NULL) {
+ hid_destroy_device(dj_dev->hdev);
+ kfree(dj_dev);
+ } else {
+ dev_err(&djrcv_dev->hdev->dev, "%s: can't destroy a NULL device\n",
+ __func__);
+ }
+}
+
+static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
+ struct dj_report *dj_report)
+{
+ /* Called in delayed work context */
+ struct hid_device *djrcv_hdev = djrcv_dev->hdev;
+ struct usb_interface *intf = to_usb_interface(djrcv_hdev->dev.parent);
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct hid_device *dj_hiddev;
+ struct dj_device *dj_dev;
+
+ /* Device index goes from 1 to 6, we need 3 bytes to store the
+ * semicolon, the index, and a null terminator
+ */
+ unsigned char tmpstr[3];
+
+ if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
+ SPFUNCTION_DEVICE_LIST_EMPTY) {
+ dbg_hid("%s: device list is empty\n", __func__);
+ return;
+ }
+
+ if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
+ (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
+ dev_err(&djrcv_hdev->dev, "%s: invalid device index:%d\n",
+ __func__, dj_report->device_index);
+ return;
+ }
+
+ dj_hiddev = hid_allocate_device();
+ if (IS_ERR(dj_hiddev)) {
+ dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
+ __func__);
+ return;
+ }
+
+ dj_hiddev->ll_driver = &logi_dj_ll_driver;
+ dj_hiddev->hid_output_raw_report = logi_dj_output_hidraw_report;
+
+ dj_hiddev->dev.parent = &djrcv_hdev->dev;
+ dj_hiddev->bus = BUS_USB;
+ dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor);
+ dj_hiddev->product = le16_to_cpu(usbdev->descriptor.idProduct);
+ snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
+ "Logitech Unifying Device. Wireless PID:%02x%02x",
+ dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB],
+ dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]);
+
+ usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys));
+ snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index);
+ strlcat(dj_hiddev->phys, tmpstr, sizeof(dj_hiddev->phys));
+
+ dj_dev = kzalloc(sizeof(struct dj_device), GFP_KERNEL);
+
+ if (!dj_dev) {
+ dev_err(&djrcv_hdev->dev, "%s: failed allocating dj_device\n",
+ __func__);
+ goto dj_device_allocate_fail;
+ }
+
+ dj_dev->reports_supported = le32_to_cpu(
+ dj_report->report_params[DEVICE_PAIRED_RF_REPORT_TYPE]);
+ dj_dev->hdev = dj_hiddev;
+ dj_dev->dj_receiver_dev = djrcv_dev;
+ dj_dev->device_index = dj_report->device_index;
+ dj_hiddev->driver_data = dj_dev;
+
+ djrcv_dev->paired_dj_devices[dj_report->device_index] = dj_dev;
+
+ if (hid_add_device(dj_hiddev)) {
+ dev_err(&djrcv_hdev->dev, "%s: failed adding dj_device\n",
+ __func__);
+ goto hid_add_device_fail;
+ }
+
+ return;
+
+hid_add_device_fail:
+ djrcv_dev->paired_dj_devices[dj_report->device_index] = NULL;
+ kfree(dj_dev);
+dj_device_allocate_fail:
+ hid_destroy_device(dj_hiddev);
+}
+
+static void delayedwork_callback(struct work_struct *work)
+{
+ struct dj_receiver_dev *djrcv_dev =
+ container_of(work, struct dj_receiver_dev, work);
+
+ struct dj_report dj_report;
+ unsigned long flags;
+ int count;
+
+ dbg_hid("%s\n", __func__);
+
+ spin_lock_irqsave(&djrcv_dev->lock, flags);
+
+ count = kfifo_out(&djrcv_dev->notif_fifo, &dj_report,
+ sizeof(struct dj_report));
+
+ if (count != sizeof(struct dj_report)) {
+ dev_err(&djrcv_dev->hdev->dev, "%s: workitem triggered without "
+ "notifications available\n", __func__);
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+ return;
+ }
+
+ if (!kfifo_is_empty(&djrcv_dev->notif_fifo)) {
+ if (schedule_work(&djrcv_dev->work) == 0) {
+ dbg_hid("%s: did not schedule the work item, was "
+ "already queued\n", __func__);
+ }
+ }
+
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+
+ switch (dj_report.report_type) {
+ case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
+ logi_dj_recv_add_djhid_device(djrcv_dev, &dj_report);
+ break;
+ case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
+ logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
+ break;
+ default:
+ dbg_hid("%s: unexpected report type\n", __func__);
+ }
+}
+
+static void logi_dj_recv_queue_notification(struct dj_receiver_dev *djrcv_dev,
+ struct dj_report *dj_report)
+{
+ /* We are called from atomic context (tasklet && djrcv->lock held) */
+
+ kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+ if (schedule_work(&djrcv_dev->work) == 0) {
+ dbg_hid("%s: did not schedule the work item, was already "
+ "queued\n", __func__);
+ }
+}
+
+static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
+ struct dj_report *dj_report)
+{
+ /* We are called from atomic context (tasklet && djrcv->lock held) */
+ unsigned int i;
+ u8 reportbuffer[MAX_REPORT_SIZE];
+ struct dj_device *djdev;
+
+ djdev = djrcv_dev->paired_dj_devices[dj_report->device_index];
+
+ if (!djdev) {
+ dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
+ " is NULL, index %d\n", dj_report->device_index);
+ return;
+ }
+
+ memset(reportbuffer, 0, sizeof(reportbuffer));
+
+ for (i = 0; i < NUMBER_OF_HID_REPORTS; i++) {
+ if (djdev->reports_supported & (1 << i)) {
+ reportbuffer[0] = i;
+ if (hid_input_report(djdev->hdev,
+ HID_INPUT_REPORT,
+ reportbuffer,
+ hid_reportid_size_map[i], 1)) {
+ dbg_hid("hid_input_report error sending null "
+ "report\n");
+ }
+ }
+ }
+}
+
+static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
+ struct dj_report *dj_report)
+{
+ /* We are called from atomic context (tasklet && djrcv->lock held) */
+ struct dj_device *dj_device;
+
+ dj_device = djrcv_dev->paired_dj_devices[dj_report->device_index];
+
+ if (dj_device == NULL) {
+ dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
+ " is NULL, index %d\n", dj_report->device_index);
+ return;
+ }
+
+ if ((dj_report->report_type > ARRAY_SIZE(hid_reportid_size_map) - 1) ||
+ (hid_reportid_size_map[dj_report->report_type] == 0)) {
+ dbg_hid("invalid report type:%x\n", dj_report->report_type);
+ return;
+ }
+
+ if (hid_input_report(dj_device->hdev,
+ HID_INPUT_REPORT, &dj_report->report_type,
+ hid_reportid_size_map[dj_report->report_type], 1)) {
+ dbg_hid("hid_input_report error\n");
+ }
+}
+
+
+static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
+ struct dj_report *dj_report)
+{
+ struct hid_device *hdev = djrcv_dev->hdev;
+ int sent_bytes;
+
+ if (!hdev->hid_output_raw_report) {
+ dev_err(&hdev->dev, "%s:"
+ "hid_output_raw_report is null\n", __func__);
+ return -ENODEV;
+ }
+
+ sent_bytes = hdev->hid_output_raw_report(hdev, (u8 *) dj_report,
+ sizeof(struct dj_report),
+ HID_OUTPUT_REPORT);
+
+ return (sent_bytes < 0) ? sent_bytes : 0;
+}
+
+static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
+{
+ struct dj_report dj_report;
+
+ memset(&dj_report, 0, sizeof(dj_report));
+ dj_report.report_id = REPORT_ID_DJ_SHORT;
+ dj_report.device_index = 0xFF;
+ dj_report.report_type = REPORT_TYPE_CMD_GET_PAIRED_DEVICES;
+ return logi_dj_recv_send_report(djrcv_dev, &dj_report);
+}
+
+static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
+ unsigned timeout)
+{
+ struct dj_report dj_report;
+
+ memset(&dj_report, 0, sizeof(dj_report));
+ dj_report.report_id = REPORT_ID_DJ_SHORT;
+ dj_report.device_index = 0xFF;
+ dj_report.report_type = REPORT_TYPE_CMD_SWITCH;
+ dj_report.report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x1F;
+ dj_report.report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout;
+ return logi_dj_recv_send_report(djrcv_dev, &dj_report);
+}
+
+
+static int logi_dj_ll_open(struct hid_device *hid)
+{
+ dbg_hid("%s:%s\n", __func__, hid->phys);
+ return 0;
+
+}
+
+static void logi_dj_ll_close(struct hid_device *hid)
+{
+ dbg_hid("%s:%s\n", __func__, hid->phys);
+}
+
+static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
+ size_t count,
+ unsigned char report_type)
+{
+ /* Called by hid raw to send data */
+ dbg_hid("%s\n", __func__);
+
+ return 0;
+}
+
+static int logi_dj_ll_parse(struct hid_device *hid)
+{
+ struct dj_device *djdev = hid->driver_data;
+ int retval;
+
+ dbg_hid("%s\n", __func__);
+
+ djdev->hdev->version = 0x0111;
+ djdev->hdev->country = 0x00;
+
+ if (djdev->reports_supported & STD_KEYBOARD) {
+ dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n",
+ __func__, djdev->reports_supported);
+ retval = hid_parse_report(hid,
+ (u8 *) kbd_descriptor,
+ sizeof(kbd_descriptor));
+ if (retval) {
+ dbg_hid("%s: sending a kbd descriptor, hid_parse failed"
+ " error: %d\n", __func__, retval);
+ return retval;
+ }
+ }
+
+ if (djdev->reports_supported & STD_MOUSE) {
+ dbg_hid("%s: sending a mouse descriptor, reports_supported: "
+ "%x\n", __func__, djdev->reports_supported);
+ retval = hid_parse_report(hid,
+ (u8 *) mse_descriptor,
+ sizeof(mse_descriptor));
+ if (retval) {
+ dbg_hid("%s: sending a mouse descriptor, hid_parse "
+ "failed error: %d\n", __func__, retval);
+ return retval;
+ }
+ }
+
+ if (djdev->reports_supported & MULTIMEDIA) {
+ dbg_hid("%s: sending a multimedia report descriptor: %x\n",
+ __func__, djdev->reports_supported);
+ retval = hid_parse_report(hid,
+ (u8 *) consumer_descriptor,
+ sizeof(consumer_descriptor));
+ if (retval) {
+ dbg_hid("%s: sending a consumer_descriptor, hid_parse "
+ "failed error: %d\n", __func__, retval);
+ return retval;
+ }
+ }
+
+ if (djdev->reports_supported & POWER_KEYS) {
+ dbg_hid("%s: sending a power keys report descriptor: %x\n",
+ __func__, djdev->reports_supported);
+ retval = hid_parse_report(hid,
+ (u8 *) syscontrol_descriptor,
+ sizeof(syscontrol_descriptor));
+ if (retval) {
+ dbg_hid("%s: sending a syscontrol_descriptor, "
+ "hid_parse failed error: %d\n",
+ __func__, retval);
+ return retval;
+ }
+ }
+
+ if (djdev->reports_supported & MEDIA_CENTER) {
+ dbg_hid("%s: sending a media center report descriptor: %x\n",
+ __func__, djdev->reports_supported);
+ retval = hid_parse_report(hid,
+ (u8 *) media_descriptor,
+ sizeof(media_descriptor));
+ if (retval) {
+ dbg_hid("%s: sending a media_descriptor, hid_parse "
+ "failed error: %d\n", __func__, retval);
+ return retval;
+ }
+ }
+
+ if (djdev->reports_supported & KBD_LEDS) {
+ dbg_hid("%s: need to send kbd leds report descriptor: %x\n",
+ __func__, djdev->reports_supported);
+ }
+
+ return 0;
+}
+
+static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+ /* Sent by the input layer to handle leds and Force Feedback */
+ struct hid_device *dj_hiddev = input_get_drvdata(dev);
+ struct dj_device *dj_dev = dj_hiddev->driver_data;
+
+ struct dj_receiver_dev *djrcv_dev =
+ dev_get_drvdata(dj_hiddev->dev.parent);
+ struct hid_device *dj_rcv_hiddev = djrcv_dev->hdev;
+ struct hid_report_enum *output_report_enum;
+
+ struct hid_field *field;
+ struct hid_report *report;
+ unsigned char data[8];
+ int offset;
+
+ dbg_hid("%s: %s, type:%d | code:%d | value:%d\n",
+ __func__, dev->phys, type, code, value);
+
+ if (type != EV_LED)
+ return -1;
+
+ offset = hidinput_find_field(dj_hiddev, type, code, &field);
+
+ if (offset == -1) {
+ dev_warn(&dev->dev, "event field not found\n");
+ return -1;
+ }
+ hid_set_field(field, offset, value);
+ hid_output_report(field->report, &data[0]);
+
+ output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT];
+ report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
+ hid_set_field(report->field[0], 0, dj_dev->device_index);
+ hid_set_field(report->field[0], 1, REPORT_TYPE_LEDS);
+ hid_set_field(report->field[0], 2, data[1]);
+
+ usbhid_submit_report(dj_rcv_hiddev, report, USB_DIR_OUT);
+
+ return 0;
+
+}
+
+static int logi_dj_ll_start(struct hid_device *hid)
+{
+ dbg_hid("%s\n", __func__);
+ return 0;
+}
+
+static void logi_dj_ll_stop(struct hid_device *hid)
+{
+ dbg_hid("%s\n", __func__);
+}
+
+
+static struct hid_ll_driver logi_dj_ll_driver = {
+ .parse = logi_dj_ll_parse,
+ .start = logi_dj_ll_start,
+ .stop = logi_dj_ll_stop,
+ .open = logi_dj_ll_open,
+ .close = logi_dj_ll_close,
+ .hidinput_input_event = logi_dj_ll_input_event,
+};
+
+
+static int logi_dj_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *data,
+ int size)
+{
+ struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
+ struct dj_report *dj_report = (struct dj_report *) data;
+ unsigned long flags;
+ bool report_processed = false;
+
+ dbg_hid("%s, size:%d\n", __func__, size);
+
+ /* Here we receive all data coming from iface 2, there are 4 cases:
+ *
+ * 1) Data should continue its normal processing i.e. data does not
+ * come from the DJ collection, in which case we do nothing and
+ * return 0, so hid-core can continue normal processing (will forward
+ * to associated hidraw device)
+ *
+ * 2) Data is from DJ collection, and is intended for this driver i. e.
+ * data contains arrival, departure, etc notifications, in which case
+ * we queue them for delayed processing by the work queue. We return 1
+ * to hid-core as no further processing is required from it.
+ *
+ * 3) Data is from DJ collection, and informs a connection change,
+ * if the change means rf link loss, then we must send a null report
+ * to the upper layer to discard potentially pressed keys that may be
+ * repeated forever by the input layer. Return 1 to hid-core as no
+ * further processing is required.
+ *
+ * 4) Data is from DJ collection and is an actual input event from
+ * a paired DJ device in which case we forward it to the correct hid
+ * device (via hid_input_report() ) and return 1 so hid-core does not do
+ * anything else with it.
+ */
+
+ spin_lock_irqsave(&djrcv_dev->lock, flags);
+ if (dj_report->report_id == REPORT_ID_DJ_SHORT) {
+ switch (dj_report->report_type) {
+ case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
+ case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
+ logi_dj_recv_queue_notification(djrcv_dev, dj_report);
+ break;
+ case REPORT_TYPE_NOTIF_CONNECTION_STATUS:
+ if (dj_report->report_params[CONNECTION_STATUS_PARAM_STATUS] ==
+ STATUS_LINKLOSS) {
+ logi_dj_recv_forward_null_report(djrcv_dev, dj_report);
+ }
+ break;
+ default:
+ logi_dj_recv_forward_report(djrcv_dev, dj_report);
+ }
+ report_processed = true;
+ }
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+
+ return report_processed;
+}
+
+static int logi_dj_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct dj_receiver_dev *djrcv_dev;
+ int retval;
+
+ if (is_dj_device((struct dj_device *)hdev->driver_data))
+ return -ENODEV;
+
+ dbg_hid("%s called for ifnum %d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ /* Ignore interfaces 0 and 1, they will not carry any data, dont create
+ * any hid_device for them */
+ if (intf->cur_altsetting->desc.bInterfaceNumber !=
+ LOGITECH_DJ_INTERFACE_NUMBER) {
+ dbg_hid("%s: ignoring ifnum %d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+ return -ENODEV;
+ }
+
+ /* Treat interface 2 */
+
+ djrcv_dev = kzalloc(sizeof(struct dj_receiver_dev), GFP_KERNEL);
+ if (!djrcv_dev) {
+ dev_err(&hdev->dev,
+ "%s:failed allocating dj_receiver_dev\n", __func__);
+ return -ENOMEM;
+ }
+ djrcv_dev->hdev = hdev;
+ INIT_WORK(&djrcv_dev->work, delayedwork_callback);
+ spin_lock_init(&djrcv_dev->lock);
+ if (kfifo_alloc(&djrcv_dev->notif_fifo,
+ DJ_MAX_NUMBER_NOTIFICATIONS * sizeof(struct dj_report),
+ GFP_KERNEL)) {
+ dev_err(&hdev->dev,
+ "%s:failed allocating notif_fifo\n", __func__);
+ kfree(djrcv_dev);
+ return -ENOMEM;
+ }
+ hid_set_drvdata(hdev, djrcv_dev);
+
+ /* Call to usbhid to fetch the HID descriptors of interface 2 and
+ * subsequently call to the hid/hid-core to parse the fetched
+ * descriptors, this will in turn create the hidraw and hiddev nodes
+ * for interface 2 of the receiver */
+ retval = hid_parse(hdev);
+ if (retval) {
+ dev_err(&hdev->dev,
+ "%s:parse of interface 2 failed\n", __func__);
+ goto hid_parse_fail;
+ }
+
+ /* Starts the usb device and connects to upper interfaces hiddev and
+ * hidraw */
+ retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (retval) {
+ dev_err(&hdev->dev,
+ "%s:hid_hw_start returned error\n", __func__);
+ goto hid_hw_start_fail;
+ }
+
+ retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
+ if (retval < 0) {
+ dev_err(&hdev->dev,
+ "%s:logi_dj_recv_switch_to_dj_mode returned error:%d\n",
+ __func__, retval);
+ goto switch_to_dj_mode_fail;
+ }
+
+ /* This is enabling the polling urb on the IN endpoint */
+ retval = hdev->ll_driver->open(hdev);
+ if (retval < 0) {
+ dev_err(&hdev->dev, "%s:hdev->ll_driver->open returned "
+ "error:%d\n", __func__, retval);
+ goto llopen_failed;
+ }
+
+ retval = logi_dj_recv_query_paired_devices(djrcv_dev);
+ if (retval < 0) {
+ dev_err(&hdev->dev, "%s:logi_dj_recv_query_paired_devices "
+ "error:%d\n", __func__, retval);
+ goto logi_dj_recv_query_paired_devices_failed;
+ }
+
+ return retval;
+
+logi_dj_recv_query_paired_devices_failed:
+ hdev->ll_driver->close(hdev);
+
+llopen_failed:
+switch_to_dj_mode_fail:
+ hid_hw_stop(hdev);
+
+hid_hw_start_fail:
+hid_parse_fail:
+ kfifo_free(&djrcv_dev->notif_fifo);
+ kfree(djrcv_dev);
+ hid_set_drvdata(hdev, NULL);
+ return retval;
+
+}
+
+#ifdef CONFIG_PM
+static int logi_dj_reset_resume(struct hid_device *hdev)
+{
+ int retval;
+ struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
+
+ retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
+ if (retval < 0) {
+ dev_err(&hdev->dev,
+ "%s:logi_dj_recv_switch_to_dj_mode returned error:%d\n",
+ __func__, retval);
+ }
+
+ return 0;
+}
+#endif
+
+static void logi_dj_remove(struct hid_device *hdev)
+{
+ struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
+ struct dj_device *dj_dev;
+ int i;
+
+ dbg_hid("%s\n", __func__);
+
+ cancel_work_sync(&djrcv_dev->work);
+
+ hdev->ll_driver->close(hdev);
+ hid_hw_stop(hdev);
+
+ /* I suppose that at this point the only context that can access
+ * the djrecv_data is this thread as the work item is guaranteed to
+ * have finished and no more raw_event callbacks should arrive after
+ * the remove callback was triggered so no locks are put around the
+ * code below */
+ for (i = 0; i < (DJ_MAX_PAIRED_DEVICES + DJ_DEVICE_INDEX_MIN); i++) {
+ dj_dev = djrcv_dev->paired_dj_devices[i];
+ if (dj_dev != NULL) {
+ hid_destroy_device(dj_dev->hdev);
+ kfree(dj_dev);
+ djrcv_dev->paired_dj_devices[i] = NULL;
+ }
+ }
+
+ kfifo_free(&djrcv_dev->notif_fifo);
+ kfree(djrcv_dev);
+ hid_set_drvdata(hdev, NULL);
+}
+
+static int logi_djdevice_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret;
+ struct dj_device *dj_dev = hdev->driver_data;
+
+ if (!is_dj_device(dj_dev))
+ return -ENODEV;
+
+ ret = hid_parse(hdev);
+ if (!ret)
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
+ return ret;
+}
+
+static const struct hid_device_id logi_dj_receivers[] = {
+ {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
+ {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(hid, logi_dj_receivers);
+
+static struct hid_driver logi_djreceiver_driver = {
+ .name = "logitech-djreceiver",
+ .id_table = logi_dj_receivers,
+ .probe = logi_dj_probe,
+ .remove = logi_dj_remove,
+ .raw_event = logi_dj_raw_event,
+#ifdef CONFIG_PM
+ .reset_resume = logi_dj_reset_resume,
+#endif
+};
+
+
+static const struct hid_device_id logi_dj_devices[] = {
+ {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
+ {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)},
+ {}
+};
+
+static struct hid_driver logi_djdevice_driver = {
+ .name = "logitech-djdevice",
+ .id_table = logi_dj_devices,
+ .probe = logi_djdevice_probe,
+};
+
+
+static int __init logi_dj_init(void)
+{
+ int retval;
+
+ dbg_hid("Logitech-DJ:%s\n", __func__);
+
+ retval = hid_register_driver(&logi_djreceiver_driver);
+ if (retval)
+ return retval;
+
+ retval = hid_register_driver(&logi_djdevice_driver);
+ if (retval)
+ hid_unregister_driver(&logi_djreceiver_driver);
+
+ return retval;
+
+}
+
+static void __exit logi_dj_exit(void)
+{
+ dbg_hid("Logitech-DJ:%s\n", __func__);
+
+ hid_unregister_driver(&logi_djdevice_driver);
+ hid_unregister_driver(&logi_djreceiver_driver);
+
+}
+
+module_init(logi_dj_init);
+module_exit(logi_dj_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Logitech");
+MODULE_AUTHOR("Nestor Lopez Casado");
+MODULE_AUTHOR("nlopezcasad@logitech.com");
diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h
new file mode 100644
index 000000000000..fd28a5e0ca3b
--- /dev/null
+++ b/drivers/hid/hid-logitech-dj.h
@@ -0,0 +1,123 @@
+#ifndef __HID_LOGITECH_DJ_H
+#define __HID_LOGITECH_DJ_H
+
+/*
+ * HID driver for Logitech Unifying receivers
+ *
+ * Copyright (c) 2011 Logitech
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kfifo.h>
+
+#define DJ_MAX_PAIRED_DEVICES 6
+#define DJ_MAX_NUMBER_NOTIFICATIONS 8
+#define DJ_DEVICE_INDEX_MIN 1
+#define DJ_DEVICE_INDEX_MAX 6
+
+#define DJREPORT_SHORT_LENGTH 15
+#define DJREPORT_LONG_LENGTH 32
+
+#define REPORT_ID_DJ_SHORT 0x20
+#define REPORT_ID_DJ_LONG 0x21
+
+#define REPORT_TYPE_RFREPORT_FIRST 0x01
+#define REPORT_TYPE_RFREPORT_LAST 0x1F
+
+/* Command Switch to DJ mode */
+#define REPORT_TYPE_CMD_SWITCH 0x80
+#define CMD_SWITCH_PARAM_DEVBITFIELD 0x00
+#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS 0x01
+#define TIMEOUT_NO_KEEPALIVE 0x00
+
+/* Command to Get the list of Paired devices */
+#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81
+
+/* Device Paired Notification */
+#define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41
+#define SPFUNCTION_MORE_NOTIF_EXPECTED 0x01
+#define SPFUNCTION_DEVICE_LIST_EMPTY 0x02
+#define DEVICE_PAIRED_PARAM_SPFUNCTION 0x00
+#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB 0x01
+#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB 0x02
+#define DEVICE_PAIRED_RF_REPORT_TYPE 0x03
+
+/* Device Un-Paired Notification */
+#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40
+
+
+/* Connection Status Notification */
+#define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42
+#define CONNECTION_STATUS_PARAM_STATUS 0x00
+#define STATUS_LINKLOSS 0x01
+
+/* Error Notification */
+#define REPORT_TYPE_NOTIF_ERROR 0x7F
+#define NOTIF_ERROR_PARAM_ETYPE 0x00
+#define ETYPE_KEEPALIVE_TIMEOUT 0x01
+
+/* supported DJ HID && RF report types */
+#define REPORT_TYPE_KEYBOARD 0x01
+#define REPORT_TYPE_MOUSE 0x02
+#define REPORT_TYPE_CONSUMER_CONTROL 0x03
+#define REPORT_TYPE_SYSTEM_CONTROL 0x04
+#define REPORT_TYPE_MEDIA_CENTER 0x08
+#define REPORT_TYPE_LEDS 0x0E
+
+/* RF Report types bitfield */
+#define STD_KEYBOARD 0x00000002
+#define STD_MOUSE 0x00000004
+#define MULTIMEDIA 0x00000008
+#define POWER_KEYS 0x00000010
+#define MEDIA_CENTER 0x00000100
+#define KBD_LEDS 0x00004000
+
+struct dj_report {
+ u8 report_id;
+ u8 device_index;
+ u8 report_type;
+ u8 report_params[DJREPORT_SHORT_LENGTH - 3];
+};
+
+struct dj_receiver_dev {
+ struct hid_device *hdev;
+ struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
+ DJ_DEVICE_INDEX_MIN];
+ struct work_struct work;
+ struct kfifo notif_fifo;
+ spinlock_t lock;
+};
+
+struct dj_device {
+ struct hid_device *hdev;
+ struct dj_receiver_dev *dj_receiver_dev;
+ u32 reports_supported;
+ u8 device_index;
+};
+
+/**
+ * is_dj_device - know if the given dj_device is not the receiver.
+ * @dj_dev: the dj device to test
+ *
+ * This macro tests if a struct dj_device pointer is a device created
+ * by the bus enumarator.
+ */
+#define is_dj_device(dj_dev) \
+ (&(dj_dev)->dj_receiver_dev->hdev->dev == (dj_dev)->hdev->dev.parent)
+
+#endif
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 0ec91c18a421..2ab71758e2e2 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -81,6 +81,28 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
#define NO_TOUCHES -1
#define SINGLE_TOUCH_UP -2
+/* Touch surface information. Dimension is in hundredths of a mm, min and max
+ * are in units. */
+#define MOUSE_DIMENSION_X (float)9056
+#define MOUSE_MIN_X -1100
+#define MOUSE_MAX_X 1258
+#define MOUSE_RES_X ((MOUSE_MAX_X - MOUSE_MIN_X) / (MOUSE_DIMENSION_X / 100))
+#define MOUSE_DIMENSION_Y (float)5152
+#define MOUSE_MIN_Y -1589
+#define MOUSE_MAX_Y 2047
+#define MOUSE_RES_Y ((MOUSE_MAX_Y - MOUSE_MIN_Y) / (MOUSE_DIMENSION_Y / 100))
+
+#define TRACKPAD_DIMENSION_X (float)13000
+#define TRACKPAD_MIN_X -2909
+#define TRACKPAD_MAX_X 3167
+#define TRACKPAD_RES_X \
+ ((TRACKPAD_MAX_X - TRACKPAD_MIN_X) / (TRACKPAD_DIMENSION_X / 100))
+#define TRACKPAD_DIMENSION_Y (float)11000
+#define TRACKPAD_MIN_Y -2456
+#define TRACKPAD_MAX_Y 2565
+#define TRACKPAD_RES_Y \
+ ((TRACKPAD_MAX_Y - TRACKPAD_MIN_Y) / (TRACKPAD_DIMENSION_Y / 100))
+
/**
* struct magicmouse_sc - Tracks Magic Mouse-specific data.
* @input: Input device through which we report events.
@@ -383,6 +405,13 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
__set_bit(REL_HWHEEL, input->relbit);
}
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+ /* input->keybit is initialized with incorrect button info
+ * for Magic Trackpad. There really is only one physical
+ * button (BTN_LEFT == BTN_MOUSE). Make sure we don't
+ * advertise buttons that don't exist...
+ */
+ __clear_bit(BTN_RIGHT, input->keybit);
+ __clear_bit(BTN_MIDDLE, input->keybit);
__set_bit(BTN_MOUSE, input->keybit);
__set_bit(BTN_TOOL_FINGER, input->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
@@ -406,17 +435,31 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
* inverse of the reported Y.
*/
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
- input_set_abs_params(input, ABS_MT_POSITION_X, -1100,
- 1358, 4, 0);
- input_set_abs_params(input, ABS_MT_POSITION_Y, -1589,
- 2047, 4, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X,
+ MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y,
+ MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0);
+
+ input_abs_set_res(input, ABS_MT_POSITION_X,
+ MOUSE_RES_X);
+ input_abs_set_res(input, ABS_MT_POSITION_Y,
+ MOUSE_RES_Y);
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
- input_set_abs_params(input, ABS_X, -2909, 3167, 4, 0);
- input_set_abs_params(input, ABS_Y, -2456, 2565, 4, 0);
- input_set_abs_params(input, ABS_MT_POSITION_X, -2909,
- 3167, 4, 0);
- input_set_abs_params(input, ABS_MT_POSITION_Y, -2456,
- 2565, 4, 0);
+ input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
+ TRACKPAD_MAX_X, 4, 0);
+ input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
+ TRACKPAD_MAX_Y, 4, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X,
+ TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y,
+ TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0);
+
+ input_abs_set_res(input, ABS_X, TRACKPAD_RES_X);
+ input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y);
+ input_abs_set_res(input, ABS_MT_POSITION_X,
+ TRACKPAD_RES_X);
+ input_abs_set_res(input, ABS_MT_POSITION_Y,
+ TRACKPAD_RES_Y);
}
input_set_events_per_packet(input, 60);
@@ -501,9 +544,17 @@ static int magicmouse_probe(struct hid_device *hdev,
}
report->size = 6;
+ /*
+ * Some devices repond with 'invalid report id' when feature
+ * report switching it into multitouch mode is sent to it.
+ *
+ * This results in -EIO from the _raw low-level transport callback,
+ * but there seems to be no other way of switching the mode.
+ * Thus the super-ugly hacky success check below.
+ */
ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature),
HID_FEATURE_REPORT);
- if (ret != sizeof(feature)) {
+ if (ret != -EIO && ret != sizeof(feature)) {
hid_err(hdev, "unable to request touch data (%d)\n", ret);
goto err_stop_hw;
}
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 0f6fc54dc196..e5c699b6c6f3 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -23,11 +23,12 @@
#include "hid-ids.h"
-#define MS_HIDINPUT 0x01
-#define MS_ERGONOMY 0x02
-#define MS_PRESENTER 0x04
-#define MS_RDESC 0x08
-#define MS_NOGET 0x10
+#define MS_HIDINPUT 0x01
+#define MS_ERGONOMY 0x02
+#define MS_PRESENTER 0x04
+#define MS_RDESC 0x08
+#define MS_NOGET 0x10
+#define MS_DUPLICATE_USAGES 0x20
/*
* Microsoft Wireless Desktop Receiver (Model 1028) has
@@ -109,6 +110,18 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 0;
}
+static int ms_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+ if (quirks & MS_DUPLICATE_USAGES)
+ clear_bit(usage->code, *bit);
+
+ return 0;
+}
+
static int ms_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
@@ -179,8 +192,12 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_ERGONOMY | MS_RDESC },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB),
.driver_data = MS_PRESENTER },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K),
+ .driver_data = MS_ERGONOMY },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0),
.driver_data = MS_NOGET },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
+ .driver_data = MS_DUPLICATE_USAGES },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
.driver_data = MS_PRESENTER },
@@ -193,6 +210,7 @@ static struct hid_driver ms_driver = {
.id_table = ms_devices,
.report_fixup = ms_report_fixup,
.input_mapping = ms_input_mapping,
+ .input_mapped = ms_input_mapped,
.event = ms_event,
.probe = ms_probe,
};
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 62cac4dc3b62..f1c909f1b239 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -47,10 +47,11 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1)
#define MT_QUIRK_CYPRESS (1 << 2)
#define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3)
-#define MT_QUIRK_VALID_IS_INRANGE (1 << 4)
-#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 5)
-#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 6)
-#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 7)
+#define MT_QUIRK_ALWAYS_VALID (1 << 4)
+#define MT_QUIRK_VALID_IS_INRANGE (1 << 5)
+#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6)
+#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 7)
+#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8)
struct mt_slot {
__s32 x, y, p, w, h;
@@ -86,11 +87,12 @@ struct mt_class {
/* classes of device behavior */
#define MT_CLS_DEFAULT 0x0001
-#define MT_CLS_CONFIDENCE 0x0002
-#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0003
-#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0004
-#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0005
-#define MT_CLS_DUAL_NSMU_CONTACTID 0x0006
+#define MT_CLS_SERIAL 0x0002
+#define MT_CLS_CONFIDENCE 0x0003
+#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0004
+#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0005
+#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0006
+#define MT_CLS_DUAL_NSMU_CONTACTID 0x0007
/* vendor specific classes */
#define MT_CLS_3M 0x0101
@@ -134,6 +136,8 @@ static int find_slot_from_contactid(struct mt_device *td)
struct mt_class mt_classes[] = {
{ .name = MT_CLS_DEFAULT,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP },
+ { .name = MT_CLS_SERIAL,
+ .quirks = MT_QUIRK_ALWAYS_VALID},
{ .name = MT_CLS_CONFIDENCE,
.quirks = MT_QUIRK_VALID_IS_CONFIDENCE },
{ .name = MT_CLS_CONFIDENCE_MINUS_ONE,
@@ -213,6 +217,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct mt_class *cls = td->mtclass;
__s32 quirks = cls->quirks;
+ /* Only map fields from TouchScreen or TouchPad collections.
+ * We need to ignore fields that belong to other collections
+ * such as Mouse that might have the same GenericDesktop usages. */
+ if (field->application == HID_DG_TOUCHSCREEN)
+ set_bit(INPUT_PROP_DIRECT, hi->input->propbit);
+ else if (field->application == HID_DG_TOUCHPAD)
+ set_bit(INPUT_PROP_POINTER, hi->input->propbit);
+ else
+ return 0;
+
switch (usage->hid & HID_USAGE_PAGE) {
case HID_UP_GENDESK:
@@ -435,7 +449,9 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
if (hid->claimed & HID_CLAIMED_INPUT && td->slots) {
switch (usage->hid) {
case HID_DG_INRANGE:
- if (quirks & MT_QUIRK_VALID_IS_INRANGE)
+ if (quirks & MT_QUIRK_ALWAYS_VALID)
+ td->curvalid = true;
+ else if (quirks & MT_QUIRK_VALID_IS_INRANGE)
td->curvalid = value;
break;
case HID_DG_TIPSWITCH:
@@ -662,6 +678,11 @@ static const struct hid_device_id mt_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH,
USB_DEVICE_ID_GOODTOUCH_000f) },
+ /* Ideacom panel */
+ { .driver_data = MT_CLS_SERIAL,
+ HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM,
+ USB_DEVICE_ID_IDEACOM_IDC6650) },
+
/* Ilitek dual touch panel */
{ .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_ILITEK,
@@ -672,6 +693,11 @@ static const struct hid_device_id mt_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS,
USB_DEVICE_ID_IRTOUCH_INFRARED_USB) },
+ /* LG Display panels */
+ { .driver_data = MT_CLS_DEFAULT,
+ HID_USB_DEVICE(USB_VENDOR_ID_LG,
+ USB_DEVICE_ID_LG_MULTITOUCH) },
+
/* Lumio panels */
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
HID_USB_DEVICE(USB_VENDOR_ID_LUMIO,
@@ -727,6 +753,10 @@ static const struct hid_device_id mt_devices[] = {
{ .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_UNITEC,
USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) },
+ /* XAT */
+ { .driver_data = MT_CLS_DEFAULT,
+ HID_USB_DEVICE(USB_VENDOR_ID_XAT,
+ USB_DEVICE_ID_XAT_CSR) },
{ }
};
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c
index 9d8710f8bc79..01e7d2cd7c26 100644
--- a/drivers/hid/hid-picolcd.c
+++ b/drivers/hid/hid-picolcd.c
@@ -36,6 +36,7 @@
#include <linux/completion.h>
#include <linux/uaccess.h>
+#include <linux/module.h>
#define PICOLCD_NAME "PicoLCD (graphic)"
@@ -2409,7 +2410,7 @@ static int picolcd_raw_event(struct hid_device *hdev,
#ifdef CONFIG_PM
static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
{
- if (message.event & PM_EVENT_AUTO)
+ if (PMSG_IS_AUTO(message))
return 0;
picolcd_suspend_backlight(hid_get_drvdata(hdev));
diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c
index 06e5300d43d2..070f93a5c11b 100644
--- a/drivers/hid/hid-pl.c
+++ b/drivers/hid/hid-pl.c
@@ -40,6 +40,7 @@
#include <linux/input.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/usb.h>
#include <linux/hid.h>
diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c
new file mode 100644
index 000000000000..4d3c60d88318
--- /dev/null
+++ b/drivers/hid/hid-primax.c
@@ -0,0 +1,117 @@
+/*
+ * HID driver for primax and similar keyboards with in-band modifiers
+ *
+ * Copyright 2011 Google Inc. All Rights Reserved
+ *
+ * Author:
+ * Terry Lambert <tlambert@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static int px_raw_event(struct hid_device *hid, struct hid_report *report,
+ u8 *data, int size)
+{
+ int idx = size;
+
+ switch (report->id) {
+ case 0: /* keyboard input */
+ /*
+ * Convert in-band modifier key values into out of band
+ * modifier bits and pull the key strokes from the report.
+ * Thus a report data set which looked like:
+ *
+ * [00][00][E0][30][00][00][00][00]
+ * (no modifier bits + "Left Shift" key + "1" key)
+ *
+ * Would be converted to:
+ *
+ * [01][00][00][30][00][00][00][00]
+ * (Left Shift modifier bit + "1" key)
+ *
+ * As long as it's in the size range, the upper level
+ * drivers don't particularly care if there are in-band
+ * 0-valued keys, so they don't stop parsing.
+ */
+ while (--idx > 1) {
+ if (data[idx] < 0xE0 || data[idx] > 0xE7)
+ continue;
+ data[0] |= (1 << (data[idx] - 0xE0));
+ data[idx] = 0;
+ }
+ hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, 0);
+ return 1;
+
+ default: /* unknown report */
+ /* Unknown report type; pass upstream */
+ hid_info(hid, "unknown report type %d\n", report->id);
+ break;
+ }
+
+ return 0;
+}
+
+static int px_probe(struct hid_device *hid, const struct hid_device_id *id)
+{
+ int ret;
+
+ ret = hid_parse(hid);
+ if (ret) {
+ hid_err(hid, "parse failed\n");
+ goto fail;
+ }
+
+ ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
+ if (ret)
+ hid_err(hid, "hw start failed\n");
+
+fail:
+ return ret;
+}
+
+static void px_remove(struct hid_device *hid)
+{
+ hid_hw_stop(hid);
+}
+
+static const struct hid_device_id px_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, px_devices);
+
+static struct hid_driver px_driver = {
+ .name = "primax",
+ .id_table = px_devices,
+ .raw_event = px_raw_event,
+ .probe = px_probe,
+ .remove = px_remove,
+};
+
+static int __init px_init(void)
+{
+ return hid_register_driver(&px_driver);
+}
+
+static void __exit px_exit(void)
+{
+ hid_unregister_driver(&px_driver);
+}
+
+module_init(px_init);
+module_exit(px_exit);
+MODULE_AUTHOR("Terry Lambert <tlambert@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c
index ab19f2905d27..f779009104eb 100644
--- a/drivers/hid/hid-prodikeys.c
+++ b/drivers/hid/hid-prodikeys.c
@@ -44,8 +44,6 @@ struct pk_device {
struct pcmidi_snd *pm; /* pcmidi device context */
};
-struct pcmidi_snd;
-
struct pcmidi_sustain {
unsigned long in_use;
struct pcmidi_snd *pm;
@@ -242,7 +240,7 @@ drop_note:
return;
}
-void pcmidi_sustained_note_release(unsigned long data)
+static void pcmidi_sustained_note_release(unsigned long data)
{
struct pcmidi_sustain *pms = (struct pcmidi_sustain *)data;
@@ -250,7 +248,7 @@ void pcmidi_sustained_note_release(unsigned long data)
pms->in_use = 0;
}
-void init_sustain_timers(struct pcmidi_snd *pm)
+static void init_sustain_timers(struct pcmidi_snd *pm)
{
struct pcmidi_sustain *pms;
unsigned i;
@@ -264,7 +262,7 @@ void init_sustain_timers(struct pcmidi_snd *pm)
}
}
-void stop_sustain_timers(struct pcmidi_snd *pm)
+static void stop_sustain_timers(struct pcmidi_snd *pm)
{
struct pcmidi_sustain *pms;
unsigned i;
@@ -499,7 +497,7 @@ static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)
return 1;
}
-int pcmidi_handle_report(
+static int pcmidi_handle_report(
struct pcmidi_snd *pm, unsigned report_id, u8 *data, int size)
{
int ret = 0;
@@ -518,7 +516,8 @@ int pcmidi_handle_report(
return ret;
}
-void pcmidi_setup_extra_keys(struct pcmidi_snd *pm, struct input_dev *input)
+static void pcmidi_setup_extra_keys(
+ struct pcmidi_snd *pm, struct input_dev *input)
{
/* reassigned functionality for N/A keys
MY PICTURES => KEY_WORDPROCESSOR
@@ -602,7 +601,7 @@ static struct snd_rawmidi_ops pcmidi_in_ops = {
.trigger = pcmidi_in_trigger
};
-int pcmidi_snd_initialise(struct pcmidi_snd *pm)
+static int pcmidi_snd_initialise(struct pcmidi_snd *pm)
{
static int dev;
struct snd_card *card;
@@ -720,7 +719,7 @@ fail:
return err;
}
-int pcmidi_snd_terminate(struct pcmidi_snd *pm)
+static int pcmidi_snd_terminate(struct pcmidi_snd *pm)
{
if (pm->card) {
stop_sustain_timers(pm);
@@ -817,7 +816,7 @@ static int pk_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (pm == NULL) {
hid_err(hdev, "can't alloc descriptor\n");
ret = -ENOMEM;
- goto err_free;
+ goto err_free_pk;
}
pm->pk = pk;
@@ -850,10 +849,10 @@ static int pk_probe(struct hid_device *hdev, const struct hid_device_id *id)
err_stop:
hid_hw_stop(hdev);
err_free:
- if (pm != NULL)
- kfree(pm);
-
+ kfree(pm);
+err_free_pk:
kfree(pk);
+
return ret;
}
diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c
index 2307471d96dc..093bfad00b02 100644
--- a/drivers/hid/hid-roccat-arvo.c
+++ b/drivers/hid/hid-roccat-arvo.c
@@ -39,7 +39,7 @@ static ssize_t arvo_sysfs_show_mode_key(struct device *dev,
int retval;
mutex_lock(&arvo->arvo_lock);
- retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_MODE_KEY,
+ retval = roccat_common_receive(usb_dev, ARVO_COMMAND_MODE_KEY,
&temp_buf, sizeof(struct arvo_mode_key));
mutex_unlock(&arvo->arvo_lock);
if (retval)
@@ -67,7 +67,7 @@ static ssize_t arvo_sysfs_set_mode_key(struct device *dev,
temp_buf.state = state;
mutex_lock(&arvo->arvo_lock);
- retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_MODE_KEY,
+ retval = roccat_common_send(usb_dev, ARVO_COMMAND_MODE_KEY,
&temp_buf, sizeof(struct arvo_mode_key));
mutex_unlock(&arvo->arvo_lock);
if (retval)
@@ -87,7 +87,7 @@ static ssize_t arvo_sysfs_show_key_mask(struct device *dev,
int retval;
mutex_lock(&arvo->arvo_lock);
- retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_KEY_MASK,
+ retval = roccat_common_receive(usb_dev, ARVO_COMMAND_KEY_MASK,
&temp_buf, sizeof(struct arvo_key_mask));
mutex_unlock(&arvo->arvo_lock);
if (retval)
@@ -115,7 +115,7 @@ static ssize_t arvo_sysfs_set_key_mask(struct device *dev,
temp_buf.key_mask = key_mask;
mutex_lock(&arvo->arvo_lock);
- retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_KEY_MASK,
+ retval = roccat_common_send(usb_dev, ARVO_COMMAND_KEY_MASK,
&temp_buf, sizeof(struct arvo_key_mask));
mutex_unlock(&arvo->arvo_lock);
if (retval)
@@ -130,7 +130,7 @@ static int arvo_get_actual_profile(struct usb_device *usb_dev)
struct arvo_actual_profile temp_buf;
int retval;
- retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE,
+ retval = roccat_common_receive(usb_dev, ARVO_COMMAND_ACTUAL_PROFILE,
&temp_buf, sizeof(struct arvo_actual_profile));
if (retval)
@@ -163,11 +163,14 @@ static ssize_t arvo_sysfs_set_actual_profile(struct device *dev,
if (retval)
return retval;
+ if (profile < 1 || profile > 5)
+ return -EINVAL;
+
temp_buf.command = ARVO_COMMAND_ACTUAL_PROFILE;
temp_buf.actual_profile = profile;
mutex_lock(&arvo->arvo_lock);
- retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE,
+ retval = roccat_common_send(usb_dev, ARVO_COMMAND_ACTUAL_PROFILE,
&temp_buf, sizeof(struct arvo_actual_profile));
if (!retval) {
arvo->actual_profile = profile;
@@ -225,7 +228,7 @@ static ssize_t arvo_sysfs_write_button(struct file *fp,
loff_t off, size_t count)
{
return arvo_sysfs_write(fp, kobj, buf, off, count,
- sizeof(struct arvo_button), ARVO_USB_COMMAND_BUTTON);
+ sizeof(struct arvo_button), ARVO_COMMAND_BUTTON);
}
static ssize_t arvo_sysfs_read_info(struct file *fp,
@@ -233,7 +236,7 @@ static ssize_t arvo_sysfs_read_info(struct file *fp,
loff_t off, size_t count)
{
return arvo_sysfs_read(fp, kobj, buf, off, count,
- sizeof(struct arvo_info), ARVO_USB_COMMAND_INFO);
+ sizeof(struct arvo_info), ARVO_COMMAND_INFO);
}
@@ -399,7 +402,7 @@ static int arvo_raw_event(struct hid_device *hdev,
if (size != 3)
return 0;
- if (arvo->roccat_claimed)
+ if (arvo && arvo->roccat_claimed)
arvo_report_to_chrdev(arvo, data);
return 0;
diff --git a/drivers/hid/hid-roccat-arvo.h b/drivers/hid/hid-roccat-arvo.h
index d284a781c99e..ce8415e4f009 100644
--- a/drivers/hid/hid-roccat-arvo.h
+++ b/drivers/hid/hid-roccat-arvo.h
@@ -46,19 +46,6 @@ enum arvo_commands {
ARVO_COMMAND_ACTUAL_PROFILE = 0x7,
};
-enum arvo_usb_commands {
- ARVO_USB_COMMAND_MODE_KEY = 0x303,
- /*
- * read/write
- * Read uses both index bytes as profile/key indexes
- * Write has index 0, profile/key is determined by payload
- */
- ARVO_USB_COMMAND_BUTTON = 0x304,
- ARVO_USB_COMMAND_INFO = 0x305,
- ARVO_USB_COMMAND_KEY_MASK = 0x306,
- ARVO_USB_COMMAND_ACTUAL_PROFILE = 0x307,
-};
-
struct arvo_special_report {
uint8_t unknown1; /* always 0x01 */
uint8_t event;
diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c
index 13b1eb0c8c65..b07e7f96a358 100644
--- a/drivers/hid/hid-roccat-common.c
+++ b/drivers/hid/hid-roccat-common.c
@@ -11,10 +11,17 @@
* any later version.
*/
+#include <linux/hid.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "hid-roccat-common.h"
-int roccat_common_receive(struct usb_device *usb_dev, uint usb_command,
+static inline uint16_t roccat_common_feature_report(uint8_t report_id)
+{
+ return 0x300 | report_id;
+}
+
+int roccat_common_receive(struct usb_device *usb_dev, uint report_id,
void *data, uint size)
{
char *buf;
@@ -25,9 +32,10 @@ int roccat_common_receive(struct usb_device *usb_dev, uint usb_command,
return -ENOMEM;
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
- USB_REQ_CLEAR_FEATURE,
+ HID_REQ_GET_REPORT,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
- usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
+ roccat_common_feature_report(report_id),
+ 0, buf, size, USB_CTRL_SET_TIMEOUT);
memcpy(data, buf, size);
kfree(buf);
@@ -35,7 +43,7 @@ int roccat_common_receive(struct usb_device *usb_dev, uint usb_command,
}
EXPORT_SYMBOL_GPL(roccat_common_receive);
-int roccat_common_send(struct usb_device *usb_dev, uint usb_command,
+int roccat_common_send(struct usb_device *usb_dev, uint report_id,
void const *data, uint size)
{
char *buf;
@@ -48,9 +56,10 @@ int roccat_common_send(struct usb_device *usb_dev, uint usb_command,
memcpy(buf, data, size);
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
- USB_REQ_SET_CONFIGURATION,
+ HID_REQ_SET_REPORT,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
- usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
+ roccat_common_feature_report(report_id),
+ 0, buf, size, USB_CTRL_SET_TIMEOUT);
kfree(buf);
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
diff --git a/drivers/hid/hid-roccat-common.h b/drivers/hid/hid-roccat-common.h
index fe45fae05bb9..9a5bc61f9699 100644
--- a/drivers/hid/hid-roccat-common.h
+++ b/drivers/hid/hid-roccat-common.h
@@ -15,9 +15,9 @@
#include <linux/usb.h>
#include <linux/types.h>
-int roccat_common_receive(struct usb_device *usb_dev, uint usb_command,
+int roccat_common_receive(struct usb_device *usb_dev, uint report_id,
void *data, uint size);
-int roccat_common_send(struct usb_device *usb_dev, uint usb_command,
+int roccat_common_send(struct usb_device *usb_dev, uint report_id,
void const *data, uint size);
#endif
diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c
index a57838d15267..e2072afb34bb 100644
--- a/drivers/hid/hid-roccat-kone.c
+++ b/drivers/hid/hid-roccat-kone.c
@@ -37,6 +37,62 @@
static uint profile_numbers[5] = {0, 1, 2, 3, 4};
+static void kone_profile_activated(struct kone_device *kone, uint new_profile)
+{
+ kone->actual_profile = new_profile;
+ kone->actual_dpi = kone->profiles[new_profile - 1].startup_dpi;
+}
+
+static void kone_profile_report(struct kone_device *kone, uint new_profile)
+{
+ struct kone_roccat_report roccat_report;
+ roccat_report.event = kone_mouse_event_switch_profile;
+ roccat_report.value = new_profile;
+ roccat_report.key = 0;
+ roccat_report_event(kone->chrdev_minor, (uint8_t *)&roccat_report);
+}
+
+static int kone_receive(struct usb_device *usb_dev, uint usb_command,
+ void *data, uint size)
+{
+ char *buf;
+ int len;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+ HID_REQ_GET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+ usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
+
+ memcpy(data, buf, size);
+ kfree(buf);
+ return ((len < 0) ? len : ((len != size) ? -EIO : 0));
+}
+
+static int kone_send(struct usb_device *usb_dev, uint usb_command,
+ void const *data, uint size)
+{
+ char *buf;
+ int len;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ memcpy(buf, data, size);
+
+ len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+ HID_REQ_SET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
+
+ kfree(buf);
+ return ((len < 0) ? len : ((len != size) ? -EIO : 0));
+}
+
/* kone_class is used for creating sysfs attributes via roccat char device */
static struct class *kone_class;
@@ -68,7 +124,7 @@ static int kone_check_write(struct usb_device *usb_dev)
*/
msleep(80);
- retval = roccat_common_receive(usb_dev,
+ retval = kone_receive(usb_dev,
kone_command_confirm_write, &data, 1);
if (retval)
return retval;
@@ -96,7 +152,7 @@ static int kone_check_write(struct usb_device *usb_dev)
static int kone_get_settings(struct usb_device *usb_dev,
struct kone_settings *buf)
{
- return roccat_common_receive(usb_dev, kone_command_settings, buf,
+ return kone_receive(usb_dev, kone_command_settings, buf,
sizeof(struct kone_settings));
}
@@ -109,7 +165,7 @@ static int kone_set_settings(struct usb_device *usb_dev,
struct kone_settings const *settings)
{
int retval;
- retval = roccat_common_send(usb_dev, kone_command_settings,
+ retval = kone_send(usb_dev, kone_command_settings,
settings, sizeof(struct kone_settings));
if (retval)
return retval;
@@ -182,7 +238,7 @@ static int kone_get_weight(struct usb_device *usb_dev, int *result)
int retval;
uint8_t data;
- retval = roccat_common_receive(usb_dev, kone_command_weight, &data, 1);
+ retval = kone_receive(usb_dev, kone_command_weight, &data, 1);
if (retval)
return retval;
@@ -201,7 +257,7 @@ static int kone_get_firmware_version(struct usb_device *usb_dev, int *result)
int retval;
uint16_t data;
- retval = roccat_common_receive(usb_dev, kone_command_firmware_version,
+ retval = kone_receive(usb_dev, kone_command_firmware_version,
&data, 2);
if (retval)
return retval;
@@ -242,7 +298,7 @@ static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj,
container_of(kobj, struct device, kobj)->parent->parent;
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
- int retval = 0, difference;
+ int retval = 0, difference, old_profile;
/* I need to get my data in one piece */
if (off != 0 || count != sizeof(struct kone_settings))
@@ -253,21 +309,20 @@ static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj,
if (difference) {
retval = kone_set_settings(usb_dev,
(struct kone_settings const *)buf);
- if (!retval)
- memcpy(&kone->settings, buf,
- sizeof(struct kone_settings));
- }
- mutex_unlock(&kone->kone_lock);
+ if (retval) {
+ mutex_unlock(&kone->kone_lock);
+ return retval;
+ }
- if (retval)
- return retval;
+ old_profile = kone->settings.startup_profile;
+ memcpy(&kone->settings, buf, sizeof(struct kone_settings));
- /*
- * If we get here, treat settings as okay and update actual values
- * according to startup_profile
- */
- kone->actual_profile = kone->settings.startup_profile;
- kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi;
+ kone_profile_activated(kone, kone->settings.startup_profile);
+
+ if (kone->settings.startup_profile != old_profile)
+ kone_profile_report(kone, kone->settings.startup_profile);
+ }
+ mutex_unlock(&kone->kone_lock);
return sizeof(struct kone_settings);
}
@@ -384,7 +439,7 @@ static int kone_tcu_command(struct usb_device *usb_dev, int number)
{
unsigned char value;
value = number;
- return roccat_common_send(usb_dev, kone_command_calibrate, &value, 1);
+ return kone_send(usb_dev, kone_command_calibrate, &value, 1);
}
/*
@@ -460,6 +515,8 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev,
goto exit_no_settings;
goto exit_unlock;
}
+ /* calibration resets profile */
+ kone_profile_activated(kone, kone->settings.startup_profile);
}
retval = size;
@@ -503,16 +560,16 @@ static ssize_t kone_sysfs_set_startup_profile(struct device *dev,
kone_set_settings_checksum(&kone->settings);
retval = kone_set_settings(usb_dev, &kone->settings);
-
- mutex_unlock(&kone->kone_lock);
-
- if (retval)
+ if (retval) {
+ mutex_unlock(&kone->kone_lock);
return retval;
+ }
/* changing the startup profile immediately activates this profile */
- kone->actual_profile = new_startup_profile;
- kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi;
+ kone_profile_activated(kone, new_startup_profile);
+ kone_profile_report(kone, new_startup_profile);
+ mutex_unlock(&kone->kone_lock);
return size;
}
@@ -624,8 +681,7 @@ static int kone_init_kone_device_struct(struct usb_device *usb_dev,
if (retval)
return retval;
- kone->actual_profile = kone->settings.startup_profile;
- kone->actual_dpi = kone->profiles[kone->actual_profile].startup_dpi;
+ kone_profile_activated(kone, kone->settings.startup_profile);
return 0;
}
@@ -735,10 +791,10 @@ static void kone_keep_values_up_to_date(struct kone_device *kone,
{
switch (event->event) {
case kone_mouse_event_switch_profile:
+ kone->actual_dpi = kone->profiles[event->value - 1].
+ startup_dpi;
case kone_mouse_event_osd_profile:
kone->actual_profile = event->value;
- kone->actual_dpi = kone->profiles[kone->actual_profile - 1].
- startup_dpi;
break;
case kone_mouse_event_switch_dpi:
case kone_mouse_event_osd_dpi:
@@ -791,6 +847,9 @@ static int kone_raw_event(struct hid_device *hdev, struct hid_report *report,
if (size != sizeof(struct kone_mouse_event))
return 0;
+ if (kone == NULL)
+ return 0;
+
/*
* Firmware 1.38 introduced new behaviour for tilt and special buttons.
* Pressed button is reported in each movement event.
diff --git a/drivers/hid/hid-roccat-kone.h b/drivers/hid/hid-roccat-kone.h
index 4109a028e138..64abb5b8a59a 100644
--- a/drivers/hid/hid-roccat-kone.h
+++ b/drivers/hid/hid-roccat-kone.h
@@ -166,7 +166,7 @@ enum kone_mouse_events {
/* osd events are thought to be display on screen */
kone_mouse_event_osd_dpi = 0xa0,
kone_mouse_event_osd_profile = 0xb0,
- /* TODO clarify meaning and occurrence of kone_mouse_event_calibration */
+ /* TODO clarify meaning and occurence of kone_mouse_event_calibration */
kone_mouse_event_calibration = 0xc0,
kone_mouse_event_call_overlong_macro = 0xe0,
/* switch events notify if user changed values with mousebutton click */
diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c
index 5b640a7a15a7..59e47770fa10 100644
--- a/drivers/hid/hid-roccat-koneplus.c
+++ b/drivers/hid/hid-roccat-koneplus.c
@@ -50,7 +50,7 @@ static int koneplus_send_control(struct usb_device *usb_dev, uint value,
control.value = value;
control.request = request;
- return roccat_common_send(usb_dev, KONEPLUS_USB_COMMAND_CONTROL,
+ return roccat_common_send(usb_dev, KONEPLUS_COMMAND_CONTROL,
&control, sizeof(struct koneplus_control));
}
@@ -60,7 +60,7 @@ static int koneplus_receive_control_status(struct usb_device *usb_dev)
struct koneplus_control control;
do {
- retval = roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_CONTROL,
+ retval = roccat_common_receive(usb_dev, KONEPLUS_COMMAND_CONTROL,
&control, sizeof(struct koneplus_control));
/* check if we get a completely wrong answer */
@@ -120,7 +120,7 @@ static int koneplus_select_profile(struct usb_device *usb_dev, uint number,
static int koneplus_get_info(struct usb_device *usb_dev,
struct koneplus_info *buf)
{
- return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_INFO,
+ return roccat_common_receive(usb_dev, KONEPLUS_COMMAND_INFO,
buf, sizeof(struct koneplus_info));
}
@@ -134,14 +134,14 @@ static int koneplus_get_profile_settings(struct usb_device *usb_dev,
if (retval)
return retval;
- return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS,
+ return roccat_common_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS,
buf, sizeof(struct koneplus_profile_settings));
}
static int koneplus_set_profile_settings(struct usb_device *usb_dev,
struct koneplus_profile_settings const *settings)
{
- return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS,
+ return koneplus_send(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS,
settings, sizeof(struct koneplus_profile_settings));
}
@@ -155,14 +155,14 @@ static int koneplus_get_profile_buttons(struct usb_device *usb_dev,
if (retval)
return retval;
- return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS,
+ return roccat_common_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS,
buf, sizeof(struct koneplus_profile_buttons));
}
static int koneplus_set_profile_buttons(struct usb_device *usb_dev,
struct koneplus_profile_buttons const *buttons)
{
- return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS,
+ return koneplus_send(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS,
buttons, sizeof(struct koneplus_profile_buttons));
}
@@ -172,7 +172,7 @@ static int koneplus_get_actual_profile(struct usb_device *usb_dev)
struct koneplus_actual_profile buf;
int retval;
- retval = roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_ACTUAL_PROFILE,
+ retval = roccat_common_receive(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE,
&buf, sizeof(struct koneplus_actual_profile));
return retval ? retval : buf.actual_profile;
@@ -187,7 +187,7 @@ static int koneplus_set_actual_profile(struct usb_device *usb_dev,
buf.size = sizeof(struct koneplus_actual_profile);
buf.actual_profile = new_profile;
- return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_ACTUAL_PROFILE,
+ return koneplus_send(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE,
&buf, sizeof(struct koneplus_actual_profile));
}
@@ -240,12 +240,20 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj,
return real_size;
}
+static ssize_t koneplus_sysfs_write_talk(struct file *fp,
+ struct kobject *kobj, struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+ return koneplus_sysfs_write(fp, kobj, buf, off, count,
+ sizeof(struct koneplus_talk), KONEPLUS_COMMAND_TALK);
+}
+
static ssize_t koneplus_sysfs_write_macro(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
return koneplus_sysfs_write(fp, kobj, buf, off, count,
- sizeof(struct koneplus_macro), KONEPLUS_USB_COMMAND_MACRO);
+ sizeof(struct koneplus_macro), KONEPLUS_COMMAND_MACRO);
}
static ssize_t koneplus_sysfs_read_sensor(struct file *fp,
@@ -253,7 +261,7 @@ static ssize_t koneplus_sysfs_read_sensor(struct file *fp,
loff_t off, size_t count)
{
return koneplus_sysfs_read(fp, kobj, buf, off, count,
- sizeof(struct koneplus_sensor), KONEPLUS_USB_COMMAND_SENSOR);
+ sizeof(struct koneplus_sensor), KONEPLUS_COMMAND_SENSOR);
}
static ssize_t koneplus_sysfs_write_sensor(struct file *fp,
@@ -261,7 +269,7 @@ static ssize_t koneplus_sysfs_write_sensor(struct file *fp,
loff_t off, size_t count)
{
return koneplus_sysfs_write(fp, kobj, buf, off, count,
- sizeof(struct koneplus_sensor), KONEPLUS_USB_COMMAND_SENSOR);
+ sizeof(struct koneplus_sensor), KONEPLUS_COMMAND_SENSOR);
}
static ssize_t koneplus_sysfs_write_tcu(struct file *fp,
@@ -269,7 +277,7 @@ static ssize_t koneplus_sysfs_write_tcu(struct file *fp,
loff_t off, size_t count)
{
return koneplus_sysfs_write(fp, kobj, buf, off, count,
- sizeof(struct koneplus_tcu), KONEPLUS_USB_COMMAND_TCU);
+ sizeof(struct koneplus_tcu), KONEPLUS_COMMAND_TCU);
}
static ssize_t koneplus_sysfs_read_tcu_image(struct file *fp,
@@ -277,7 +285,7 @@ static ssize_t koneplus_sysfs_read_tcu_image(struct file *fp,
loff_t off, size_t count)
{
return koneplus_sysfs_read(fp, kobj, buf, off, count,
- sizeof(struct koneplus_tcu_image), KONEPLUS_USB_COMMAND_TCU);
+ sizeof(struct koneplus_tcu_image), KONEPLUS_COMMAND_TCU);
}
static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp,
@@ -423,6 +431,9 @@ static ssize_t koneplus_sysfs_set_actual_profile(struct device *dev,
if (retval)
return retval;
+ if (profile > 4)
+ return -EINVAL;
+
mutex_lock(&koneplus->koneplus_lock);
retval = koneplus_set_actual_profile(usb_dev, profile);
@@ -431,7 +442,7 @@ static ssize_t koneplus_sysfs_set_actual_profile(struct device *dev,
return retval;
}
- koneplus->actual_profile = profile;
+ koneplus_profile_activated(koneplus, profile);
roccat_report.type = KONEPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE;
roccat_report.data1 = profile + 1;
@@ -557,6 +568,11 @@ static struct bin_attribute koneplus_bin_attributes[] = {
.size = sizeof(struct koneplus_macro),
.write = koneplus_sysfs_write_macro
},
+ {
+ .attr = { .name = "talk", .mode = 0220 },
+ .size = sizeof(struct koneplus_talk),
+ .write = koneplus_sysfs_write_talk
+ },
__ATTR_NULL
};
@@ -738,6 +754,9 @@ static int koneplus_raw_event(struct hid_device *hdev,
!= USB_INTERFACE_PROTOCOL_MOUSE)
return 0;
+ if (koneplus == NULL)
+ return 0;
+
koneplus_keep_values_up_to_date(koneplus, data);
if (koneplus->roccat_claimed)
diff --git a/drivers/hid/hid-roccat-koneplus.h b/drivers/hid/hid-roccat-koneplus.h
index c57a376ab8ae..c03332a4fa9a 100644
--- a/drivers/hid/hid-roccat-koneplus.h
+++ b/drivers/hid/hid-roccat-koneplus.h
@@ -14,6 +14,12 @@
#include <linux/types.h>
+struct koneplus_talk {
+ uint8_t command; /* KONEPLUS_COMMAND_TALK */
+ uint8_t size; /* always 0x10 */
+ uint8_t data[14];
+} __packed;
+
/*
* case 1: writes request 80 and reads value 1
*
@@ -137,26 +143,14 @@ enum koneplus_commands {
KONEPLUS_COMMAND_PROFILE_BUTTONS = 0x7,
KONEPLUS_COMMAND_MACRO = 0x8,
KONEPLUS_COMMAND_INFO = 0x9,
+ KONEPLUS_COMMAND_TCU = 0xc,
KONEPLUS_COMMAND_E = 0xe,
KONEPLUS_COMMAND_SENSOR = 0xf,
+ KONEPLUS_COMMAND_TALK = 0x10,
KONEPLUS_COMMAND_FIRMWARE_WRITE = 0x1b,
KONEPLUS_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c,
};
-enum koneplus_usb_commands {
- KONEPLUS_USB_COMMAND_CONTROL = 0x304,
- KONEPLUS_USB_COMMAND_ACTUAL_PROFILE = 0x305,
- KONEPLUS_USB_COMMAND_PROFILE_SETTINGS = 0x306,
- KONEPLUS_USB_COMMAND_PROFILE_BUTTONS = 0x307,
- KONEPLUS_USB_COMMAND_MACRO = 0x308,
- KONEPLUS_USB_COMMAND_INFO = 0x309,
- KONEPLUS_USB_COMMAND_TCU = 0x30c,
- KONEPLUS_USB_COMMAND_E = 0x30e,
- KONEPLUS_USB_COMMAND_SENSOR = 0x30f,
- KONEPLUS_USB_COMMAND_FIRMWARE_WRITE = 0x31b,
- KONEPLUS_USB_COMMAND_FIRMWARE_WRITE_CONTROL = 0x31c,
-};
-
enum koneplus_mouse_report_numbers {
KONEPLUS_MOUSE_REPORT_NUMBER_HID = 1,
KONEPLUS_MOUSE_REPORT_NUMBER_AUDIO = 2,
@@ -193,6 +187,7 @@ enum koneplus_mouse_report_button_types {
* data2 = action
*/
KONEPLUS_MOUSE_REPORT_BUTTON_TYPE_MULTIMEDIA = 0xf0,
+ KONEPLUS_MOUSE_REPORT_TALK = 0xff,
};
enum koneplus_mouse_report_button_action {
diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c
index 984be2f8967e..112d934132c8 100644
--- a/drivers/hid/hid-roccat-kovaplus.c
+++ b/drivers/hid/hid-roccat-kovaplus.c
@@ -58,7 +58,7 @@ static int kovaplus_send_control(struct usb_device *usb_dev, uint value,
control.value = value;
control.request = request;
- retval = roccat_common_send(usb_dev, KOVAPLUS_USB_COMMAND_CONTROL,
+ retval = roccat_common_send(usb_dev, KOVAPLUS_COMMAND_CONTROL,
&control, sizeof(struct kovaplus_control));
return retval;
@@ -70,7 +70,7 @@ static int kovaplus_receive_control_status(struct usb_device *usb_dev)
struct kovaplus_control control;
do {
- retval = roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_CONTROL,
+ retval = roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_CONTROL,
&control, sizeof(struct kovaplus_control));
/* check if we get a completely wrong answer */
@@ -90,7 +90,7 @@ static int kovaplus_receive_control_status(struct usb_device *usb_dev)
if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD)
return -EINVAL;
- hid_err(usb_dev, "kovaplus_receive_control_status: "
+ hid_err(usb_dev, "roccat_common_receive_control_status: "
"unknown response value 0x%x\n", control.value);
return -EINVAL;
} while (1);
@@ -119,7 +119,7 @@ static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
static int kovaplus_get_info(struct usb_device *usb_dev,
struct kovaplus_info *buf)
{
- return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_INFO,
+ return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_INFO,
buf, sizeof(struct kovaplus_info));
}
@@ -133,14 +133,14 @@ static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
if (retval)
return retval;
- return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS,
+ return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
buf, sizeof(struct kovaplus_profile_settings));
}
static int kovaplus_set_profile_settings(struct usb_device *usb_dev,
struct kovaplus_profile_settings const *settings)
{
- return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS,
+ return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
settings, sizeof(struct kovaplus_profile_settings));
}
@@ -154,14 +154,14 @@ static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
if (retval)
return retval;
- return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS,
+ return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
buf, sizeof(struct kovaplus_profile_buttons));
}
static int kovaplus_set_profile_buttons(struct usb_device *usb_dev,
struct kovaplus_profile_buttons const *buttons)
{
- return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS,
+ return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
buttons, sizeof(struct kovaplus_profile_buttons));
}
@@ -171,7 +171,7 @@ static int kovaplus_get_actual_profile(struct usb_device *usb_dev)
struct kovaplus_actual_profile buf;
int retval;
- retval = roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE,
+ retval = roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
&buf, sizeof(struct kovaplus_actual_profile));
return retval ? retval : buf.actual_profile;
@@ -186,7 +186,7 @@ static int kovaplus_set_actual_profile(struct usb_device *usb_dev,
buf.size = sizeof(struct kovaplus_actual_profile);
buf.actual_profile = new_profile;
- return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE,
+ return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
&buf, sizeof(struct kovaplus_actual_profile));
}
@@ -323,6 +323,7 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
struct usb_device *usb_dev;
unsigned long profile;
int retval;
+ struct kovaplus_roccat_report roccat_report;
dev = dev->parent->parent;
kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
@@ -337,10 +338,22 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
mutex_lock(&kovaplus->kovaplus_lock);
retval = kovaplus_set_actual_profile(usb_dev, profile);
- kovaplus->actual_profile = profile;
- mutex_unlock(&kovaplus->kovaplus_lock);
- if (retval)
+ if (retval) {
+ mutex_unlock(&kovaplus->kovaplus_lock);
return retval;
+ }
+
+ kovaplus_profile_activated(kovaplus, profile);
+
+ roccat_report.type = KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1;
+ roccat_report.profile = profile + 1;
+ roccat_report.button = 0;
+ roccat_report.data1 = profile + 1;
+ roccat_report.data2 = 0;
+ roccat_report_event(kovaplus->chrdev_minor,
+ (uint8_t const *)&roccat_report);
+
+ mutex_unlock(&kovaplus->kovaplus_lock);
return size;
}
@@ -662,6 +675,9 @@ static int kovaplus_raw_event(struct hid_device *hdev,
!= USB_INTERFACE_PROTOCOL_MOUSE)
return 0;
+ if (kovaplus == NULL)
+ return 0;
+
kovaplus_keep_values_up_to_date(kovaplus, data);
if (kovaplus->roccat_claimed)
diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h
index ce40607d21c7..fb2aed44a8e0 100644
--- a/drivers/hid/hid-roccat-kovaplus.h
+++ b/drivers/hid/hid-roccat-kovaplus.h
@@ -83,15 +83,6 @@ enum kovaplus_commands {
KOVAPLUS_COMMAND_A = 0xa,
};
-enum kovaplus_usb_commands {
- KOVAPLUS_USB_COMMAND_CONTROL = 0x304,
- KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE = 0x305,
- KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS = 0x306,
- KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS = 0x307,
- KOVAPLUS_USB_COMMAND_INFO = 0x309,
- KOVAPLUS_USB_COMMAND_A = 0x30a,
-};
-
enum kovaplus_mouse_report_numbers {
KOVAPLUS_MOUSE_REPORT_NUMBER_MOUSE = 1,
KOVAPLUS_MOUSE_REPORT_NUMBER_AUDIO = 2,
diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c
index 38280c055a19..df05c1b1064f 100644
--- a/drivers/hid/hid-roccat-pyra.c
+++ b/drivers/hid/hid-roccat-pyra.c
@@ -53,7 +53,7 @@ static int pyra_send_control(struct usb_device *usb_dev, int value,
control.value = value;
control.request = request;
- return roccat_common_send(usb_dev, PYRA_USB_COMMAND_CONTROL,
+ return roccat_common_send(usb_dev, PYRA_COMMAND_CONTROL,
&control, sizeof(struct pyra_control));
}
@@ -64,7 +64,7 @@ static int pyra_receive_control_status(struct usb_device *usb_dev)
do {
msleep(10);
- retval = roccat_common_receive(usb_dev, PYRA_USB_COMMAND_CONTROL,
+ retval = roccat_common_receive(usb_dev, PYRA_COMMAND_CONTROL,
&control, sizeof(struct pyra_control));
/* requested too early, try again */
@@ -89,7 +89,7 @@ static int pyra_get_profile_settings(struct usb_device *usb_dev,
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
if (retval)
return retval;
- return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_PROFILE_SETTINGS,
+ return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
buf, sizeof(struct pyra_profile_settings));
}
@@ -101,20 +101,20 @@ static int pyra_get_profile_buttons(struct usb_device *usb_dev,
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
if (retval)
return retval;
- return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_PROFILE_BUTTONS,
+ return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS,
buf, sizeof(struct pyra_profile_buttons));
}
static int pyra_get_settings(struct usb_device *usb_dev,
struct pyra_settings *buf)
{
- return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_SETTINGS,
+ return roccat_common_receive(usb_dev, PYRA_COMMAND_SETTINGS,
buf, sizeof(struct pyra_settings));
}
static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
{
- return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_INFO,
+ return roccat_common_receive(usb_dev, PYRA_COMMAND_INFO,
buf, sizeof(struct pyra_info));
}
@@ -131,26 +131,22 @@ static int pyra_send(struct usb_device *usb_dev, uint command,
static int pyra_set_profile_settings(struct usb_device *usb_dev,
struct pyra_profile_settings const *settings)
{
- return pyra_send(usb_dev, PYRA_USB_COMMAND_PROFILE_SETTINGS, settings,
+ return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, settings,
sizeof(struct pyra_profile_settings));
}
static int pyra_set_profile_buttons(struct usb_device *usb_dev,
struct pyra_profile_buttons const *buttons)
{
- return pyra_send(usb_dev, PYRA_USB_COMMAND_PROFILE_BUTTONS, buttons,
+ return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, buttons,
sizeof(struct pyra_profile_buttons));
}
static int pyra_set_settings(struct usb_device *usb_dev,
struct pyra_settings const *settings)
{
- int retval;
- retval = roccat_common_send(usb_dev, PYRA_USB_COMMAND_SETTINGS, settings,
+ return pyra_send(usb_dev, PYRA_COMMAND_SETTINGS, settings,
sizeof(struct pyra_settings));
- if (retval)
- return retval;
- return pyra_receive_control_status(usb_dev);
}
static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
@@ -302,6 +298,7 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp,
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval = 0;
int difference;
+ struct pyra_roccat_report roccat_report;
if (off != 0 || count != sizeof(struct pyra_settings))
return -EINVAL;
@@ -311,17 +308,23 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp,
if (difference) {
retval = pyra_set_settings(usb_dev,
(struct pyra_settings const *)buf);
- if (!retval)
- memcpy(&pyra->settings, buf,
- sizeof(struct pyra_settings));
- }
- mutex_unlock(&pyra->pyra_lock);
+ if (retval) {
+ mutex_unlock(&pyra->pyra_lock);
+ return retval;
+ }
- if (retval)
- return retval;
+ memcpy(&pyra->settings, buf,
+ sizeof(struct pyra_settings));
- profile_activated(pyra, pyra->settings.startup_profile);
+ profile_activated(pyra, pyra->settings.startup_profile);
+ roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2;
+ roccat_report.value = pyra->settings.startup_profile + 1;
+ roccat_report.key = 0;
+ roccat_report_event(pyra->chrdev_minor,
+ (uint8_t const *)&roccat_report);
+ }
+ mutex_unlock(&pyra->pyra_lock);
return sizeof(struct pyra_settings);
}
@@ -641,6 +644,9 @@ static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
!= USB_INTERFACE_PROTOCOL_MOUSE)
return 0;
+ if (pyra == NULL)
+ return 0;
+
pyra_keep_values_up_to_date(pyra, data);
if (pyra->roccat_claimed)
diff --git a/drivers/hid/hid-roccat-pyra.h b/drivers/hid/hid-roccat-pyra.h
index 14cbbe1621e0..0442d7fa2dcf 100644
--- a/drivers/hid/hid-roccat-pyra.h
+++ b/drivers/hid/hid-roccat-pyra.h
@@ -83,15 +83,6 @@ enum pyra_commands {
PYRA_COMMAND_B = 0xb
};
-enum pyra_usb_commands {
- PYRA_USB_COMMAND_CONTROL = 0x304,
- PYRA_USB_COMMAND_SETTINGS = 0x305,
- PYRA_USB_COMMAND_PROFILE_SETTINGS = 0x306,
- PYRA_USB_COMMAND_PROFILE_BUTTONS = 0x307,
- PYRA_USB_COMMAND_INFO = 0x309,
- PYRA_USB_COMMAND_B = 0x30b /* writes 3 bytes */
-};
-
enum pyra_mouse_report_numbers {
PYRA_MOUSE_REPORT_NUMBER_HID = 1,
PYRA_MOUSE_REPORT_NUMBER_AUDIO = 2,
diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c
index 5666e7587b18..b685b04dbf9d 100644
--- a/drivers/hid/hid-roccat.c
+++ b/drivers/hid/hid-roccat.c
@@ -27,6 +27,7 @@
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/hid-roccat.h>
+#include <linux/module.h>
#define ROCCAT_FIRST_MINOR 0
#define ROCCAT_MAX_DEVICES 8
@@ -162,27 +163,27 @@ static int roccat_open(struct inode *inode, struct file *file)
device = devices[minor];
- mutex_lock(&device->readers_lock);
-
if (!device) {
pr_emerg("roccat device with minor %d doesn't exist\n", minor);
error = -ENODEV;
- goto exit_err;
+ goto exit_err_devices;
}
+ mutex_lock(&device->readers_lock);
+
if (!device->open++) {
/* power on device on adding first reader */
error = hid_hw_power(device->hid, PM_HINT_FULLON);
if (error < 0) {
--device->open;
- goto exit_err;
+ goto exit_err_readers;
}
error = hid_hw_open(device->hid);
if (error < 0) {
hid_hw_power(device->hid, PM_HINT_NORMAL);
--device->open;
- goto exit_err;
+ goto exit_err_readers;
}
}
@@ -193,13 +194,13 @@ static int roccat_open(struct inode *inode, struct file *file)
list_add_tail(&reader->node, &device->readers);
file->private_data = reader;
-exit_unlock:
+exit_err_readers:
mutex_unlock(&device->readers_lock);
+exit_err_devices:
mutex_unlock(&devices_lock);
+ if (error)
+ kfree(reader);
return error;
-exit_err:
- kfree(reader);
- goto exit_unlock;
}
static int roccat_release(struct inode *inode, struct file *file)
diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c
index 16f7cafc9695..4b1448613ea6 100644
--- a/drivers/hid/hid-sjoy.c
+++ b/drivers/hid/hid-sjoy.c
@@ -30,6 +30,7 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/hid.h>
+#include <linux/module.h>
#include "hid-ids.h"
#ifdef CONFIG_SMARTJOYPLUS_FF
@@ -65,8 +66,7 @@ static int sjoyff_init(struct hid_device *hid)
{
struct sjoyff_device *sjoyff;
struct hid_report *report;
- struct hid_input *hidinput = list_entry(hid->inputs.next,
- struct hid_input, list);
+ struct hid_input *hidinput;
struct list_head *report_list =
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct list_head *report_ptr = report_list;
@@ -78,43 +78,45 @@ static int sjoyff_init(struct hid_device *hid)
return -ENODEV;
}
- report_ptr = report_ptr->next;
+ list_for_each_entry(hidinput, &hid->inputs, list) {
+ report_ptr = report_ptr->next;
- if (report_ptr == report_list) {
- hid_err(hid, "required output report is missing\n");
- return -ENODEV;
- }
+ if (report_ptr == report_list) {
+ hid_err(hid, "required output report is missing\n");
+ return -ENODEV;
+ }
- report = list_entry(report_ptr, struct hid_report, list);
- if (report->maxfield < 1) {
- hid_err(hid, "no fields in the report\n");
- return -ENODEV;
- }
+ report = list_entry(report_ptr, struct hid_report, list);
+ if (report->maxfield < 1) {
+ hid_err(hid, "no fields in the report\n");
+ return -ENODEV;
+ }
- if (report->field[0]->report_count < 3) {
- hid_err(hid, "not enough values in the field\n");
- return -ENODEV;
- }
+ if (report->field[0]->report_count < 3) {
+ hid_err(hid, "not enough values in the field\n");
+ return -ENODEV;
+ }
- sjoyff = kzalloc(sizeof(struct sjoyff_device), GFP_KERNEL);
- if (!sjoyff)
- return -ENOMEM;
+ sjoyff = kzalloc(sizeof(struct sjoyff_device), GFP_KERNEL);
+ if (!sjoyff)
+ return -ENOMEM;
- dev = hidinput->input;
+ dev = hidinput->input;
- set_bit(FF_RUMBLE, dev->ffbit);
+ set_bit(FF_RUMBLE, dev->ffbit);
- error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play);
- if (error) {
- kfree(sjoyff);
- return error;
- }
+ error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play);
+ if (error) {
+ kfree(sjoyff);
+ return error;
+ }
- sjoyff->report = report;
- sjoyff->report->field[0]->value[0] = 0x01;
- sjoyff->report->field[0]->value[1] = 0x00;
- sjoyff->report->field[0]->value[2] = 0x00;
- usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT);
+ sjoyff->report = report;
+ sjoyff->report->field[0]->value[0] = 0x01;
+ sjoyff->report->field[0]->value[1] = 0x00;
+ sjoyff->report->field[0]->value[2] = 0x00;
+ usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT);
+ }
hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter\n");
@@ -131,6 +133,8 @@ static int sjoy_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
+ hdev->quirks |= id->driver_data;
+
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
@@ -151,7 +155,17 @@ err:
}
static const struct hid_device_id sjoy_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO),
+ .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET |
+ HID_QUIRK_SKIP_OUTPUT_REPORTS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO),
+ .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET |
+ HID_QUIRK_SKIP_OUTPUT_REPORTS },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD),
+ .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET |
+ HID_QUIRK_SKIP_OUTPUT_REPORTS },
{ }
};
MODULE_DEVICE_TABLE(hid, sjoy_devices);
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 936c911fdca6..5cd25bd907f8 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -28,6 +28,12 @@
#define SIXAXIS_CONTROLLER_USB (1 << 1)
#define SIXAXIS_CONTROLLER_BT (1 << 2)
+static const u8 sixaxis_rdesc_fixup[] = {
+ 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C,
+ 0x81, 0x01, 0x75, 0x10, 0x95, 0x04, 0x26, 0xFF,
+ 0x03, 0x46, 0xFF, 0x03, 0x09, 0x01, 0x81, 0x02
+};
+
struct sony_sc {
unsigned long quirks;
};
@@ -43,9 +49,37 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
hid_info(hdev, "Fixing up Sony Vaio VGX report descriptor\n");
rdesc[55] = 0x06;
}
+
+ /* The HID descriptor exposed over BT has a trailing zero byte */
+ if ((((sc->quirks & SIXAXIS_CONTROLLER_USB) && *rsize == 148) ||
+ ((sc->quirks & SIXAXIS_CONTROLLER_BT) && *rsize == 149)) &&
+ rdesc[83] == 0x75) {
+ hid_info(hdev, "Fixing up Sony Sixaxis report descriptor\n");
+ memcpy((void *)&rdesc[83], (void *)&sixaxis_rdesc_fixup,
+ sizeof(sixaxis_rdesc_fixup));
+ }
return rdesc;
}
+static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
+ __u8 *rd, int size)
+{
+ struct sony_sc *sc = hid_get_drvdata(hdev);
+
+ /* Sixaxis HID report has acclerometers/gyro with MSByte first, this
+ * has to be BYTE_SWAPPED before passing up to joystick interface
+ */
+ if ((sc->quirks & (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)) &&
+ rd[0] == 0x01 && size == 49) {
+ swap(rd[41], rd[42]);
+ swap(rd[43], rd[44]);
+ swap(rd[45], rd[46]);
+ swap(rd[47], rd[48]);
+ }
+
+ return 0;
+}
+
/*
* The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP
* like it should according to usbhid/hid-core.c::usbhid_output_raw_report()
@@ -194,6 +228,7 @@ static struct hid_driver sony_driver = {
.probe = sony_probe,
.remove = sony_remove,
.report_fixup = sony_report_fixup,
+ .raw_event = sony_raw_event
};
static int __init sony_init(void)
diff --git a/drivers/hid/hid-speedlink.c b/drivers/hid/hid-speedlink.c
new file mode 100644
index 000000000000..602013741718
--- /dev/null
+++ b/drivers/hid/hid-speedlink.c
@@ -0,0 +1,89 @@
+/*
+ * HID driver for Speedlink Vicious and Divine Cezanne (USB mouse).
+ * Fixes "jumpy" cursor and removes nonexistent keyboard LEDS from
+ * the HID descriptor.
+ *
+ * Copyright (c) 2011 Stefan Kriwanek <mail@stefankriwanek.de>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+
+static const struct hid_device_id speedlink_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE)},
+ { }
+};
+
+static int speedlink_input_mapping(struct hid_device *hdev,
+ struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ /*
+ * The Cezanne mouse has a second "keyboard" USB endpoint for it is
+ * able to map keyboard events to the button presses.
+ * It sends a standard keyboard report descriptor, though, whose
+ * LEDs we ignore.
+ */
+ switch (usage->hid & HID_USAGE_PAGE) {
+ case HID_UP_LED:
+ return -1;
+ }
+ return 0;
+}
+
+static int speedlink_event(struct hid_device *hdev, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ /* No other conditions due to usage_table. */
+ /* Fix "jumpy" cursor (invalid events sent by device). */
+ if (value == 256)
+ return 1;
+ /* Drop useless distance 0 events (on button clicks etc.) as well */
+ if (value == 0)
+ return 1;
+
+ return 0;
+}
+
+MODULE_DEVICE_TABLE(hid, speedlink_devices);
+
+static const struct hid_usage_id speedlink_grabbed_usages[] = {
+ { HID_GD_X, EV_REL, 0 },
+ { HID_GD_Y, EV_REL, 1 },
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
+static struct hid_driver speedlink_driver = {
+ .name = "speedlink",
+ .id_table = speedlink_devices,
+ .usage_table = speedlink_grabbed_usages,
+ .input_mapping = speedlink_input_mapping,
+ .event = speedlink_event,
+};
+
+static int __init speedlink_init(void)
+{
+ return hid_register_driver(&speedlink_driver);
+}
+
+static void __exit speedlink_exit(void)
+{
+ hid_unregister_driver(&speedlink_driver);
+}
+
+module_init(speedlink_init);
+module_exit(speedlink_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c
index 575862b0688e..83a933b9c2e9 100644
--- a/drivers/hid/hid-tmff.c
+++ b/drivers/hid/hid-tmff.c
@@ -31,6 +31,7 @@
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/usb.h>
+#include <linux/module.h>
#include "hid-ids.h"
diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c
index 05fdc85a76e5..e15732f1a22d 100644
--- a/drivers/hid/hid-uclogic.c
+++ b/drivers/hid/hid-uclogic.c
@@ -343,6 +343,193 @@ static __u8 wp8060u_rdesc_fixed[] = {
};
/*
+ * Original WP1062 report descriptor.
+ *
+ * Only report ID 9 is actually used.
+ *
+ * Usage Page (Digitizer), ; Digitizer (0Dh)
+ * Usage (Pen), ; Pen (02h, application collection)
+ * Collection (Application),
+ * Report ID (7),
+ * Usage (Stylus), ; Stylus (20h, logical collection)
+ * Collection (Physical),
+ * Usage (Tip Switch), ; Tip switch (42h, momentary control)
+ * Usage (Barrel Switch), ; Barrel switch (44h, momentary control)
+ * Usage (Eraser), ; Eraser (45h, momentary control)
+ * Logical Minimum (0),
+ * Logical Maximum (1),
+ * Report Size (1),
+ * Report Count (3),
+ * Input (Variable),
+ * Report Count (3),
+ * Input (Constant, Variable),
+ * Usage (In Range), ; In range (32h, momentary control)
+ * Report Count (1),
+ * Input (Variable),
+ * Report Count (1),
+ * Input (Constant, Variable),
+ * Usage Page (Desktop), ; Generic desktop controls (01h)
+ * Usage (X), ; X (30h, dynamic value)
+ * Report Size (16),
+ * Report Count (1),
+ * Push,
+ * Unit Exponent (13),
+ * Unit (Inch),
+ * Physical Minimum (0),
+ * Physical Maximum (10000),
+ * Logical Maximum (20000),
+ * Input (Variable),
+ * Usage (Y), ; Y (31h, dynamic value)
+ * Physical Maximum (6583),
+ * Logical Maximum (13166),
+ * Input (Variable),
+ * Pop,
+ * Usage Page (Digitizer), ; Digitizer (0Dh)
+ * Usage (Tip Pressure), ; Tip pressure (30h, dynamic value)
+ * Logical Maximum (1023),
+ * Input (Variable),
+ * Report Size (16),
+ * End Collection,
+ * End Collection,
+ * Usage Page (Desktop), ; Generic desktop controls (01h)
+ * Usage (Mouse), ; Mouse (02h, application collection)
+ * Collection (Application),
+ * Report ID (8),
+ * Usage (Pointer), ; Pointer (01h, physical collection)
+ * Collection (Physical),
+ * Usage Page (Button), ; Button (09h)
+ * Usage Minimum (01h),
+ * Usage Maximum (03h),
+ * Logical Minimum (0),
+ * Logical Maximum (1),
+ * Report Count (3),
+ * Report Size (1),
+ * Input (Variable),
+ * Report Count (5),
+ * Input (Constant),
+ * Usage Page (Desktop), ; Generic desktop controls (01h)
+ * Usage (X), ; X (30h, dynamic value)
+ * Usage (Y), ; Y (31h, dynamic value)
+ * Usage (Wheel), ; Wheel (38h, dynamic value)
+ * Usage (00h),
+ * Logical Minimum (-127),
+ * Logical Maximum (127),
+ * Report Size (8),
+ * Report Count (4),
+ * Input (Variable, Relative),
+ * End Collection,
+ * End Collection,
+ * Usage Page (Desktop), ; Generic desktop controls (01h)
+ * Usage (Mouse), ; Mouse (02h, application collection)
+ * Collection (Application),
+ * Report ID (9),
+ * Usage (Pointer), ; Pointer (01h, physical collection)
+ * Collection (Physical),
+ * Usage Page (Button), ; Button (09h)
+ * Usage Minimum (01h),
+ * Usage Maximum (03h),
+ * Logical Minimum (0),
+ * Logical Maximum (1),
+ * Report Count (3),
+ * Report Size (1),
+ * Input (Variable),
+ * Report Count (4),
+ * Input (Constant),
+ * Usage Page (Digitizer), ; Digitizer (0Dh)
+ * Usage (In Range), ; In range (32h, momentary control)
+ * Report Count (1),
+ * Input (Variable),
+ * Usage Page (Desktop), ; Generic desktop controls (01h)
+ * Usage (X), ; X (30h, dynamic value)
+ * Report Size (16),
+ * Report Count (1),
+ * Push,
+ * Unit Exponent (13),
+ * Unit (Inch),
+ * Physical Minimum (0),
+ * Physical Maximum (10000),
+ * Logical Maximum (20000),
+ * Input (Variable),
+ * Usage (Y), ; Y (31h, dynamic value)
+ * Physical Maximum (6583),
+ * Logical Maximum (13166),
+ * Input (Variable),
+ * Pop,
+ * Usage Page (Digitizer), ; Digitizer (0Dh)
+ * Usage (Tip Pressure), ; Tip pressure (30h, dynamic value)
+ * Logical Maximum (1023),
+ * Report Count (1),
+ * Report Size (16),
+ * Input (Variable),
+ * End Collection,
+ * End Collection,
+ * Usage Page (Desktop), ; Generic desktop controls (01h)
+ * Usage (00h),
+ * Collection (Application),
+ * Report ID (4),
+ * Logical Minimum (0),
+ * Logical Maximum (255),
+ * Usage (00h),
+ * Report Size (8),
+ * Report Count (3),
+ * Feature (Variable),
+ * End Collection
+ */
+
+/* Size of the original descriptor of WP1062 tablet */
+#define WP1062_RDESC_ORIG_SIZE 254
+
+/*
+ * Fixed WP1062 report descriptor.
+ *
+ * Removed unused reports, corrected second barrel button usage code, physical
+ * units.
+ */
+static __u8 wp1062_rdesc_fixed[] = {
+ 0x05, 0x0D, /* Usage Page (Digitizer), */
+ 0x09, 0x02, /* Usage (Pen), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x09, /* Report ID (9), */
+ 0x09, 0x20, /* Usage (Stylus), */
+ 0xA0, /* Collection (Physical), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x09, 0x42, /* Usage (Tip Switch), */
+ 0x09, 0x44, /* Usage (Barrel Switch), */
+ 0x09, 0x46, /* Usage (Tablet Pick), */
+ 0x14, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x04, /* Report Count (4), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x09, 0x32, /* Usage (In Range), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x14, /* Logical Minimum (0), */
+ 0xA4, /* Push, */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x55, 0xFD, /* Unit Exponent (-3), */
+ 0x65, 0x13, /* Unit (Inch), */
+ 0x34, /* Physical Minimum (0), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x46, 0x10, 0x27, /* Physical Maximum (10000), */
+ 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x31, /* Usage (Y), */
+ 0x46, 0xB7, 0x19, /* Physical Maximum (6583), */
+ 0x26, 0x6E, 0x33, /* Logical Maximum (13166), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xB4, /* Pop, */
+ 0x09, 0x30, /* Usage (Tip Pressure), */
+ 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xC0, /* End Collection, */
+ 0xC0 /* End Collection */
+};
+
+/*
* Original PF1209 report descriptor.
*
* The descriptor is similar to WPXXXXU descriptors, with an addition of a
@@ -584,6 +771,12 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
*rsize = sizeof(wp8060u_rdesc_fixed);
}
break;
+ case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062:
+ if (*rsize == WP1062_RDESC_ORIG_SIZE) {
+ rdesc = wp1062_rdesc_fixed;
+ *rsize = sizeof(wp1062_rdesc_fixed);
+ }
+ break;
}
return rdesc;
@@ -598,6 +791,8 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
+ USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index 06888323828c..17bb88f782b6 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -304,11 +304,51 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
return 1;
}
+static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage, unsigned long **bit,
+ int *max)
+{
+ struct input_dev *input = hi->input;
+
+ __set_bit(INPUT_PROP_POINTER, input->propbit);
+
+ /* Basics */
+ input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
+
+ __set_bit(REL_WHEEL, input->relbit);
+
+ __set_bit(BTN_TOOL_PEN, input->keybit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ __set_bit(BTN_STYLUS, input->keybit);
+ __set_bit(BTN_STYLUS2, input->keybit);
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_RIGHT, input->keybit);
+ __set_bit(BTN_MIDDLE, input->keybit);
+
+ /* Pad */
+ input->evbit[0] |= BIT(EV_MSC);
+
+ __set_bit(MSC_SERIAL, input->mscbit);
+
+ __set_bit(BTN_0, input->keybit);
+ __set_bit(BTN_1, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
+
+ /* Distance, rubber and mouse */
+ __set_bit(BTN_TOOL_RUBBER, input->keybit);
+ __set_bit(BTN_TOOL_MOUSE, input->keybit);
+
+ input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
+ input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
+ input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
+
+ return 0;
+}
+
static int wacom_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
- struct hid_input *hidinput;
- struct input_dev *input;
struct wacom_data *wdata;
int ret;
@@ -353,11 +393,7 @@ static int wacom_probe(struct hid_device *hdev,
if (ret) {
hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n",
ret);
- /*
- * battery attribute is not critical for the tablet, but if it
- * failed then there is no need to create ac attribute
- */
- goto move_on;
+ goto err_battery;
}
wdata->ac.properties = wacom_ac_props;
@@ -371,51 +407,18 @@ static int wacom_probe(struct hid_device *hdev,
if (ret) {
hid_warn(hdev,
"can't create ac battery attribute, err: %d\n", ret);
- /*
- * ac attribute is not critical for the tablet, but if it
- * failed then we don't want to battery attribute to exist
- */
- power_supply_unregister(&wdata->battery);
+ goto err_ac;
}
-
-move_on:
#endif
- hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
- input = hidinput->input;
-
- /* Basics */
- input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
-
- __set_bit(REL_WHEEL, input->relbit);
-
- __set_bit(BTN_TOOL_PEN, input->keybit);
- __set_bit(BTN_TOUCH, input->keybit);
- __set_bit(BTN_STYLUS, input->keybit);
- __set_bit(BTN_STYLUS2, input->keybit);
- __set_bit(BTN_LEFT, input->keybit);
- __set_bit(BTN_RIGHT, input->keybit);
- __set_bit(BTN_MIDDLE, input->keybit);
-
- /* Pad */
- input->evbit[0] |= BIT(EV_MSC);
-
- __set_bit(MSC_SERIAL, input->mscbit);
-
- __set_bit(BTN_0, input->keybit);
- __set_bit(BTN_1, input->keybit);
- __set_bit(BTN_TOOL_FINGER, input->keybit);
-
- /* Distance, rubber and mouse */
- __set_bit(BTN_TOOL_RUBBER, input->keybit);
- __set_bit(BTN_TOOL_MOUSE, input->keybit);
-
- input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
- input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
- input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
- input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
-
return 0;
+#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
+err_ac:
+ power_supply_unregister(&wdata->battery);
+err_battery:
+ device_remove_file(&hdev->dev, &dev_attr_speed);
+ hid_hw_stop(hdev);
+#endif
err_free:
kfree(wdata);
return ret;
@@ -426,6 +429,7 @@ static void wacom_remove(struct hid_device *hdev)
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
struct wacom_data *wdata = hid_get_drvdata(hdev);
#endif
+ device_remove_file(&hdev->dev, &dev_attr_speed);
hid_hw_stop(hdev);
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
@@ -448,6 +452,7 @@ static struct hid_driver wacom_driver = {
.probe = wacom_probe,
.remove = wacom_remove,
.raw_event = wacom_raw_event,
+ .input_mapped = wacom_input_mapped,
};
static int __init wacom_init(void)
diff --git a/drivers/hid/hid-wiimote.c b/drivers/hid/hid-wiimote.c
new file mode 100644
index 000000000000..76739c07fa3c
--- /dev/null
+++ b/drivers/hid/hid-wiimote.c
@@ -0,0 +1,1346 @@
+/*
+ * HID driver for Nintendo Wiimote devices
+ * Copyright (c) 2011 David Herrmann
+ */
+
+/*
+ * 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/completion.h>
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include <linux/spinlock.h>
+#include "hid-ids.h"
+
+#define WIIMOTE_VERSION "0.2"
+#define WIIMOTE_NAME "Nintendo Wii Remote"
+#define WIIMOTE_BUFSIZE 32
+
+struct wiimote_buf {
+ __u8 data[HID_MAX_BUFFER_SIZE];
+ size_t size;
+};
+
+struct wiimote_state {
+ spinlock_t lock;
+ __u8 flags;
+ __u8 accel_split[2];
+
+ /* synchronous cmd requests */
+ struct mutex sync;
+ struct completion ready;
+ int cmd;
+ __u32 opt;
+
+ /* results of synchronous requests */
+ __u8 cmd_battery;
+ __u8 cmd_err;
+};
+
+struct wiimote_data {
+ struct hid_device *hdev;
+ struct input_dev *input;
+ struct led_classdev *leds[4];
+ struct input_dev *accel;
+ struct input_dev *ir;
+ struct power_supply battery;
+
+ spinlock_t qlock;
+ __u8 head;
+ __u8 tail;
+ struct wiimote_buf outq[WIIMOTE_BUFSIZE];
+ struct work_struct worker;
+
+ struct wiimote_state state;
+};
+
+#define WIIPROTO_FLAG_LED1 0x01
+#define WIIPROTO_FLAG_LED2 0x02
+#define WIIPROTO_FLAG_LED3 0x04
+#define WIIPROTO_FLAG_LED4 0x08
+#define WIIPROTO_FLAG_RUMBLE 0x10
+#define WIIPROTO_FLAG_ACCEL 0x20
+#define WIIPROTO_FLAG_IR_BASIC 0x40
+#define WIIPROTO_FLAG_IR_EXT 0x80
+#define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */
+#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
+ WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
+#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \
+ WIIPROTO_FLAG_IR_FULL)
+
+/* return flag for led \num */
+#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1))
+
+enum wiiproto_reqs {
+ WIIPROTO_REQ_NULL = 0x0,
+ WIIPROTO_REQ_RUMBLE = 0x10,
+ WIIPROTO_REQ_LED = 0x11,
+ WIIPROTO_REQ_DRM = 0x12,
+ WIIPROTO_REQ_IR1 = 0x13,
+ WIIPROTO_REQ_SREQ = 0x15,
+ WIIPROTO_REQ_WMEM = 0x16,
+ WIIPROTO_REQ_RMEM = 0x17,
+ WIIPROTO_REQ_IR2 = 0x1a,
+ WIIPROTO_REQ_STATUS = 0x20,
+ WIIPROTO_REQ_DATA = 0x21,
+ WIIPROTO_REQ_RETURN = 0x22,
+ WIIPROTO_REQ_DRM_K = 0x30,
+ WIIPROTO_REQ_DRM_KA = 0x31,
+ WIIPROTO_REQ_DRM_KE = 0x32,
+ WIIPROTO_REQ_DRM_KAI = 0x33,
+ WIIPROTO_REQ_DRM_KEE = 0x34,
+ WIIPROTO_REQ_DRM_KAE = 0x35,
+ WIIPROTO_REQ_DRM_KIE = 0x36,
+ WIIPROTO_REQ_DRM_KAIE = 0x37,
+ WIIPROTO_REQ_DRM_E = 0x3d,
+ WIIPROTO_REQ_DRM_SKAI1 = 0x3e,
+ WIIPROTO_REQ_DRM_SKAI2 = 0x3f,
+};
+
+enum wiiproto_keys {
+ WIIPROTO_KEY_LEFT,
+ WIIPROTO_KEY_RIGHT,
+ WIIPROTO_KEY_UP,
+ WIIPROTO_KEY_DOWN,
+ WIIPROTO_KEY_PLUS,
+ WIIPROTO_KEY_MINUS,
+ WIIPROTO_KEY_ONE,
+ WIIPROTO_KEY_TWO,
+ WIIPROTO_KEY_A,
+ WIIPROTO_KEY_B,
+ WIIPROTO_KEY_HOME,
+ WIIPROTO_KEY_COUNT
+};
+
+static __u16 wiiproto_keymap[] = {
+ KEY_LEFT, /* WIIPROTO_KEY_LEFT */
+ KEY_RIGHT, /* WIIPROTO_KEY_RIGHT */
+ KEY_UP, /* WIIPROTO_KEY_UP */
+ KEY_DOWN, /* WIIPROTO_KEY_DOWN */
+ KEY_NEXT, /* WIIPROTO_KEY_PLUS */
+ KEY_PREVIOUS, /* WIIPROTO_KEY_MINUS */
+ BTN_1, /* WIIPROTO_KEY_ONE */
+ BTN_2, /* WIIPROTO_KEY_TWO */
+ BTN_A, /* WIIPROTO_KEY_A */
+ BTN_B, /* WIIPROTO_KEY_B */
+ BTN_MODE, /* WIIPROTO_KEY_HOME */
+};
+
+static enum power_supply_property wiimote_battery_props[] = {
+ POWER_SUPPLY_PROP_CAPACITY
+};
+
+/* requires the state.lock spinlock to be held */
+static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd,
+ __u32 opt)
+{
+ return wdata->state.cmd == cmd && wdata->state.opt == opt;
+}
+
+/* requires the state.lock spinlock to be held */
+static inline void wiimote_cmd_complete(struct wiimote_data *wdata)
+{
+ wdata->state.cmd = WIIPROTO_REQ_NULL;
+ complete(&wdata->state.ready);
+}
+
+static inline int wiimote_cmd_acquire(struct wiimote_data *wdata)
+{
+ return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0;
+}
+
+/* requires the state.lock spinlock to be held */
+static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd,
+ __u32 opt)
+{
+ INIT_COMPLETION(wdata->state.ready);
+ wdata->state.cmd = cmd;
+ wdata->state.opt = opt;
+}
+
+static inline void wiimote_cmd_release(struct wiimote_data *wdata)
+{
+ mutex_unlock(&wdata->state.sync);
+}
+
+static inline int wiimote_cmd_wait(struct wiimote_data *wdata)
+{
+ int ret;
+
+ ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ);
+ if (ret < 0)
+ return -ERESTARTSYS;
+ else if (ret == 0)
+ return -EIO;
+ else
+ return 0;
+}
+
+static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
+ size_t count)
+{
+ __u8 *buf;
+ ssize_t ret;
+
+ if (!hdev->hid_output_raw_report)
+ return -ENODEV;
+
+ buf = kmemdup(buffer, count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT);
+
+ kfree(buf);
+ return ret;
+}
+
+static void wiimote_worker(struct work_struct *work)
+{
+ struct wiimote_data *wdata = container_of(work, struct wiimote_data,
+ worker);
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdata->qlock, flags);
+
+ while (wdata->head != wdata->tail) {
+ spin_unlock_irqrestore(&wdata->qlock, flags);
+ wiimote_hid_send(wdata->hdev, wdata->outq[wdata->tail].data,
+ wdata->outq[wdata->tail].size);
+ spin_lock_irqsave(&wdata->qlock, flags);
+
+ wdata->tail = (wdata->tail + 1) % WIIMOTE_BUFSIZE;
+ }
+
+ spin_unlock_irqrestore(&wdata->qlock, flags);
+}
+
+static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
+ size_t count)
+{
+ unsigned long flags;
+ __u8 newhead;
+
+ if (count > HID_MAX_BUFFER_SIZE) {
+ hid_warn(wdata->hdev, "Sending too large output report\n");
+ return;
+ }
+
+ /*
+ * Copy new request into our output queue and check whether the
+ * queue is full. If it is full, discard this request.
+ * If it is empty we need to start a new worker that will
+ * send out the buffer to the hid device.
+ * If the queue is not empty, then there must be a worker
+ * that is currently sending out our buffer and this worker
+ * will reschedule itself until the queue is empty.
+ */
+
+ spin_lock_irqsave(&wdata->qlock, flags);
+
+ memcpy(wdata->outq[wdata->head].data, buffer, count);
+ wdata->outq[wdata->head].size = count;
+ newhead = (wdata->head + 1) % WIIMOTE_BUFSIZE;
+
+ if (wdata->head == wdata->tail) {
+ wdata->head = newhead;
+ schedule_work(&wdata->worker);
+ } else if (newhead != wdata->tail) {
+ wdata->head = newhead;
+ } else {
+ hid_warn(wdata->hdev, "Output queue is full");
+ }
+
+ spin_unlock_irqrestore(&wdata->qlock, flags);
+}
+
+/*
+ * This sets the rumble bit on the given output report if rumble is
+ * currently enabled.
+ * \cmd1 must point to the second byte in the output report => &cmd[1]
+ * This must be called on nearly every output report before passing it
+ * into the output queue!
+ */
+static inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1)
+{
+ if (wdata->state.flags & WIIPROTO_FLAG_RUMBLE)
+ *cmd1 |= 0x01;
+}
+
+static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble)
+{
+ __u8 cmd[2];
+
+ rumble = !!rumble;
+ if (rumble == !!(wdata->state.flags & WIIPROTO_FLAG_RUMBLE))
+ return;
+
+ if (rumble)
+ wdata->state.flags |= WIIPROTO_FLAG_RUMBLE;
+ else
+ wdata->state.flags &= ~WIIPROTO_FLAG_RUMBLE;
+
+ cmd[0] = WIIPROTO_REQ_RUMBLE;
+ cmd[1] = 0;
+
+ wiiproto_keep_rumble(wdata, &cmd[1]);
+ wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
+{
+ __u8 cmd[2];
+
+ leds &= WIIPROTO_FLAGS_LEDS;
+ if ((wdata->state.flags & WIIPROTO_FLAGS_LEDS) == leds)
+ return;
+ wdata->state.flags = (wdata->state.flags & ~WIIPROTO_FLAGS_LEDS) | leds;
+
+ cmd[0] = WIIPROTO_REQ_LED;
+ cmd[1] = 0;
+
+ if (leds & WIIPROTO_FLAG_LED1)
+ cmd[1] |= 0x10;
+ if (leds & WIIPROTO_FLAG_LED2)
+ cmd[1] |= 0x20;
+ if (leds & WIIPROTO_FLAG_LED3)
+ cmd[1] |= 0x40;
+ if (leds & WIIPROTO_FLAG_LED4)
+ cmd[1] |= 0x80;
+
+ wiiproto_keep_rumble(wdata, &cmd[1]);
+ wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+/*
+ * Check what peripherals of the wiimote are currently
+ * active and select a proper DRM that supports all of
+ * the requested data inputs.
+ */
+static __u8 select_drm(struct wiimote_data *wdata)
+{
+ __u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR;
+
+ if (ir == WIIPROTO_FLAG_IR_BASIC) {
+ if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
+ return WIIPROTO_REQ_DRM_KAIE;
+ else
+ return WIIPROTO_REQ_DRM_KIE;
+ } else if (ir == WIIPROTO_FLAG_IR_EXT) {
+ return WIIPROTO_REQ_DRM_KAI;
+ } else if (ir == WIIPROTO_FLAG_IR_FULL) {
+ return WIIPROTO_REQ_DRM_SKAI1;
+ } else {
+ if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
+ return WIIPROTO_REQ_DRM_KA;
+ else
+ return WIIPROTO_REQ_DRM_K;
+ }
+}
+
+static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
+{
+ __u8 cmd[3];
+
+ if (drm == WIIPROTO_REQ_NULL)
+ drm = select_drm(wdata);
+
+ cmd[0] = WIIPROTO_REQ_DRM;
+ cmd[1] = 0;
+ cmd[2] = drm;
+
+ wiiproto_keep_rumble(wdata, &cmd[1]);
+ wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+static void wiiproto_req_status(struct wiimote_data *wdata)
+{
+ __u8 cmd[2];
+
+ cmd[0] = WIIPROTO_REQ_SREQ;
+ cmd[1] = 0;
+
+ wiiproto_keep_rumble(wdata, &cmd[1]);
+ wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel)
+{
+ accel = !!accel;
+ if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
+ return;
+
+ if (accel)
+ wdata->state.flags |= WIIPROTO_FLAG_ACCEL;
+ else
+ wdata->state.flags &= ~WIIPROTO_FLAG_ACCEL;
+
+ wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+}
+
+static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags)
+{
+ __u8 cmd[2];
+
+ cmd[0] = WIIPROTO_REQ_IR1;
+ cmd[1] = flags;
+
+ wiiproto_keep_rumble(wdata, &cmd[1]);
+ wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+static void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags)
+{
+ __u8 cmd[2];
+
+ cmd[0] = WIIPROTO_REQ_IR2;
+ cmd[1] = flags;
+
+ wiiproto_keep_rumble(wdata, &cmd[1]);
+ wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+#define wiiproto_req_wreg(wdata, os, buf, sz) \
+ wiiproto_req_wmem((wdata), false, (os), (buf), (sz))
+
+#define wiiproto_req_weeprom(wdata, os, buf, sz) \
+ wiiproto_req_wmem((wdata), true, (os), (buf), (sz))
+
+static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom,
+ __u32 offset, const __u8 *buf, __u8 size)
+{
+ __u8 cmd[22];
+
+ if (size > 16 || size == 0) {
+ hid_warn(wdata->hdev, "Invalid length %d wmem request\n", size);
+ return;
+ }
+
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = WIIPROTO_REQ_WMEM;
+ cmd[2] = (offset >> 16) & 0xff;
+ cmd[3] = (offset >> 8) & 0xff;
+ cmd[4] = offset & 0xff;
+ cmd[5] = size;
+ memcpy(&cmd[6], buf, size);
+
+ if (!eeprom)
+ cmd[1] |= 0x04;
+
+ wiiproto_keep_rumble(wdata, &cmd[1]);
+ wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+/* requries the cmd-mutex to be held */
+static int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
+ const __u8 *wmem, __u8 size)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wiimote_cmd_set(wdata, WIIPROTO_REQ_WMEM, 0);
+ wiiproto_req_wreg(wdata, offset, wmem, size);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ ret = wiimote_cmd_wait(wdata);
+ if (!ret && wdata->state.cmd_err)
+ ret = -EIO;
+
+ return ret;
+}
+
+static int wiimote_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct wiimote_data *wdata = container_of(psy,
+ struct wiimote_data, battery);
+ int ret = 0, state;
+ unsigned long flags;
+
+ ret = wiimote_cmd_acquire(wdata);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0);
+ wiiproto_req_status(wdata);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ ret = wiimote_cmd_wait(wdata);
+ state = wdata->state.cmd_battery;
+ wiimote_cmd_release(wdata);
+
+ if (ret)
+ return ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = state * 100 / 255;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode)
+{
+ int ret;
+ unsigned long flags;
+ __u8 format = 0;
+ static const __u8 data_enable[] = { 0x01 };
+ static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01,
+ 0x00, 0xaa, 0x00, 0x64 };
+ static const __u8 data_sens2[] = { 0x63, 0x03 };
+ static const __u8 data_fin[] = { 0x08 };
+
+ spin_lock_irqsave(&wdata->state.lock, flags);
+
+ if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) {
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+ return 0;
+ }
+
+ if (mode == 0) {
+ wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
+ wiiproto_req_ir1(wdata, 0);
+ wiiproto_req_ir2(wdata, 0);
+ wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+ return 0;
+ }
+
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ ret = wiimote_cmd_acquire(wdata);
+ if (ret)
+ return ret;
+
+ /* send PIXEL CLOCK ENABLE cmd first */
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0);
+ wiiproto_req_ir1(wdata, 0x06);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ ret = wiimote_cmd_wait(wdata);
+ if (ret)
+ goto unlock;
+ if (wdata->state.cmd_err) {
+ ret = -EIO;
+ goto unlock;
+ }
+
+ /* enable IR LOGIC */
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0);
+ wiiproto_req_ir2(wdata, 0x06);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ ret = wiimote_cmd_wait(wdata);
+ if (ret)
+ goto unlock;
+ if (wdata->state.cmd_err) {
+ ret = -EIO;
+ goto unlock;
+ }
+
+ /* enable IR cam but do not make it send data, yet */
+ ret = wiimote_cmd_write(wdata, 0xb00030, data_enable,
+ sizeof(data_enable));
+ if (ret)
+ goto unlock;
+
+ /* write first sensitivity block */
+ ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1,
+ sizeof(data_sens1));
+ if (ret)
+ goto unlock;
+
+ /* write second sensitivity block */
+ ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2,
+ sizeof(data_sens2));
+ if (ret)
+ goto unlock;
+
+ /* put IR cam into desired state */
+ switch (mode) {
+ case WIIPROTO_FLAG_IR_FULL:
+ format = 5;
+ break;
+ case WIIPROTO_FLAG_IR_EXT:
+ format = 3;
+ break;
+ case WIIPROTO_FLAG_IR_BASIC:
+ format = 1;
+ break;
+ }
+ ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format));
+ if (ret)
+ goto unlock;
+
+ /* make IR cam send data */
+ ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin));
+ if (ret)
+ goto unlock;
+
+ /* request new DRM mode compatible to IR mode */
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
+ wdata->state.flags |= mode & WIIPROTO_FLAGS_IR;
+ wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+unlock:
+ wiimote_cmd_release(wdata);
+ return ret;
+}
+
+static enum led_brightness wiimote_leds_get(struct led_classdev *led_dev)
+{
+ struct wiimote_data *wdata;
+ struct device *dev = led_dev->dev->parent;
+ int i;
+ unsigned long flags;
+ bool value = false;
+
+ wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
+
+ for (i = 0; i < 4; ++i) {
+ if (wdata->leds[i] == led_dev) {
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ value = wdata->state.flags & WIIPROTO_FLAG_LED(i + 1);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+ break;
+ }
+ }
+
+ return value ? LED_FULL : LED_OFF;
+}
+
+static void wiimote_leds_set(struct led_classdev *led_dev,
+ enum led_brightness value)
+{
+ struct wiimote_data *wdata;
+ struct device *dev = led_dev->dev->parent;
+ int i;
+ unsigned long flags;
+ __u8 state, flag;
+
+ wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
+
+ for (i = 0; i < 4; ++i) {
+ if (wdata->leds[i] == led_dev) {
+ flag = WIIPROTO_FLAG_LED(i + 1);
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ state = wdata->state.flags;
+ if (value == LED_OFF)
+ wiiproto_req_leds(wdata, state & ~flag);
+ else
+ wiiproto_req_leds(wdata, state | flag);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+ break;
+ }
+ }
+}
+
+static int wiimote_ff_play(struct input_dev *dev, void *data,
+ struct ff_effect *eff)
+{
+ struct wiimote_data *wdata = input_get_drvdata(dev);
+ __u8 value;
+ unsigned long flags;
+
+ /*
+ * The wiimote supports only a single rumble motor so if any magnitude
+ * is set to non-zero then we start the rumble motor. If both are set to
+ * zero, we stop the rumble motor.
+ */
+
+ if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
+ value = 1;
+ else
+ value = 0;
+
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wiiproto_req_rumble(wdata, value);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ return 0;
+}
+
+static int wiimote_input_open(struct input_dev *dev)
+{
+ struct wiimote_data *wdata = input_get_drvdata(dev);
+
+ return hid_hw_open(wdata->hdev);
+}
+
+static void wiimote_input_close(struct input_dev *dev)
+{
+ struct wiimote_data *wdata = input_get_drvdata(dev);
+
+ hid_hw_close(wdata->hdev);
+}
+
+static int wiimote_accel_open(struct input_dev *dev)
+{
+ struct wiimote_data *wdata = input_get_drvdata(dev);
+ int ret;
+ unsigned long flags;
+
+ ret = hid_hw_open(wdata->hdev);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wiiproto_req_accel(wdata, true);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ return 0;
+}
+
+static void wiimote_accel_close(struct input_dev *dev)
+{
+ struct wiimote_data *wdata = input_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wiiproto_req_accel(wdata, false);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ hid_hw_close(wdata->hdev);
+}
+
+static int wiimote_ir_open(struct input_dev *dev)
+{
+ struct wiimote_data *wdata = input_get_drvdata(dev);
+ int ret;
+
+ ret = hid_hw_open(wdata->hdev);
+ if (ret)
+ return ret;
+
+ ret = wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC);
+ if (ret) {
+ hid_hw_close(wdata->hdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void wiimote_ir_close(struct input_dev *dev)
+{
+ struct wiimote_data *wdata = input_get_drvdata(dev);
+
+ wiimote_init_ir(wdata, 0);
+ hid_hw_close(wdata->hdev);
+}
+
+static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
+{
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT],
+ !!(payload[0] & 0x01));
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_RIGHT],
+ !!(payload[0] & 0x02));
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_DOWN],
+ !!(payload[0] & 0x04));
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_UP],
+ !!(payload[0] & 0x08));
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_PLUS],
+ !!(payload[0] & 0x10));
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_TWO],
+ !!(payload[1] & 0x01));
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_ONE],
+ !!(payload[1] & 0x02));
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_B],
+ !!(payload[1] & 0x04));
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_A],
+ !!(payload[1] & 0x08));
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_MINUS],
+ !!(payload[1] & 0x10));
+ input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_HOME],
+ !!(payload[1] & 0x80));
+ input_sync(wdata->input);
+}
+
+static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
+{
+ __u16 x, y, z;
+
+ if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
+ return;
+
+ /*
+ * payload is: BB BB XX YY ZZ
+ * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ
+ * contain the upper 8 bits of each value. The lower 2 bits are
+ * contained in the buttons data BB BB.
+ * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the
+ * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y
+ * accel value and bit 6 is the second bit of the Z value.
+ * The first bit of Y and Z values is not available and always set to 0.
+ * 0x200 is returned on no movement.
+ */
+
+ x = payload[2] << 2;
+ y = payload[3] << 2;
+ z = payload[4] << 2;
+
+ x |= (payload[0] >> 5) & 0x3;
+ y |= (payload[1] >> 4) & 0x2;
+ z |= (payload[1] >> 5) & 0x2;
+
+ input_report_abs(wdata->accel, ABS_RX, x - 0x200);
+ input_report_abs(wdata->accel, ABS_RY, y - 0x200);
+ input_report_abs(wdata->accel, ABS_RZ, z - 0x200);
+ input_sync(wdata->accel);
+}
+
+#define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
+ ABS_HAT0X, ABS_HAT0Y)
+#define ir_to_input1(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
+ ABS_HAT1X, ABS_HAT1Y)
+#define ir_to_input2(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
+ ABS_HAT2X, ABS_HAT2Y)
+#define ir_to_input3(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
+ ABS_HAT3X, ABS_HAT3Y)
+
+static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir,
+ bool packed, __u8 xid, __u8 yid)
+{
+ __u16 x, y;
+
+ if (!(wdata->state.flags & WIIPROTO_FLAGS_IR))
+ return;
+
+ /*
+ * Basic IR data is encoded into 3 bytes. The first two bytes are the
+ * upper 8 bit of the X/Y data, the 3rd byte contains the lower 2 bits
+ * of both.
+ * If data is packed, then the 3rd byte is put first and slightly
+ * reordered. This allows to interleave packed and non-packed data to
+ * have two IR sets in 5 bytes instead of 6.
+ * The resulting 10bit X/Y values are passed to the ABS_HATXY input dev.
+ */
+
+ if (packed) {
+ x = ir[1] << 2;
+ y = ir[2] << 2;
+
+ x |= ir[0] & 0x3;
+ y |= (ir[0] >> 2) & 0x3;
+ } else {
+ x = ir[0] << 2;
+ y = ir[1] << 2;
+
+ x |= (ir[2] >> 4) & 0x3;
+ y |= (ir[2] >> 6) & 0x3;
+ }
+
+ input_report_abs(wdata->ir, xid, x);
+ input_report_abs(wdata->ir, yid, y);
+}
+
+static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
+{
+ handler_keys(wdata, payload);
+
+ /* on status reports the drm is reset so we need to resend the drm */
+ wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+
+ if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) {
+ wdata->state.cmd_battery = payload[5];
+ wiimote_cmd_complete(wdata);
+ }
+}
+
+static void handler_data(struct wiimote_data *wdata, const __u8 *payload)
+{
+ handler_keys(wdata, payload);
+}
+
+static void handler_return(struct wiimote_data *wdata, const __u8 *payload)
+{
+ __u8 err = payload[3];
+ __u8 cmd = payload[2];
+
+ handler_keys(wdata, payload);
+
+ if (wiimote_cmd_pending(wdata, cmd, 0)) {
+ wdata->state.cmd_err = err;
+ wiimote_cmd_complete(wdata);
+ } else if (err) {
+ hid_warn(wdata->hdev, "Remote error %hhu on req %hhu\n", err,
+ cmd);
+ }
+}
+
+static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload)
+{
+ handler_keys(wdata, payload);
+ handler_accel(wdata, payload);
+}
+
+static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload)
+{
+ handler_keys(wdata, payload);
+}
+
+static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
+{
+ handler_keys(wdata, payload);
+ handler_accel(wdata, payload);
+ ir_to_input0(wdata, &payload[5], false);
+ ir_to_input1(wdata, &payload[8], false);
+ ir_to_input2(wdata, &payload[11], false);
+ ir_to_input3(wdata, &payload[14], false);
+ input_sync(wdata->ir);
+}
+
+static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload)
+{
+ handler_keys(wdata, payload);
+}
+
+static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
+{
+ handler_keys(wdata, payload);
+ ir_to_input0(wdata, &payload[2], false);
+ ir_to_input1(wdata, &payload[4], true);
+ ir_to_input2(wdata, &payload[7], false);
+ ir_to_input3(wdata, &payload[9], true);
+ input_sync(wdata->ir);
+}
+
+static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload)
+{
+ handler_keys(wdata, payload);
+ handler_accel(wdata, payload);
+}
+
+static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
+{
+ handler_keys(wdata, payload);
+ handler_accel(wdata, payload);
+ ir_to_input0(wdata, &payload[5], false);
+ ir_to_input1(wdata, &payload[7], true);
+ ir_to_input2(wdata, &payload[10], false);
+ ir_to_input3(wdata, &payload[12], true);
+ input_sync(wdata->ir);
+}
+
+static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload)
+{
+}
+
+static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
+{
+ handler_keys(wdata, payload);
+
+ wdata->state.accel_split[0] = payload[2];
+ wdata->state.accel_split[1] = (payload[0] >> 1) & (0x10 | 0x20);
+ wdata->state.accel_split[1] |= (payload[1] << 1) & (0x40 | 0x80);
+
+ ir_to_input0(wdata, &payload[3], false);
+ ir_to_input1(wdata, &payload[12], false);
+ input_sync(wdata->ir);
+}
+
+static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload)
+{
+ __u8 buf[5];
+
+ handler_keys(wdata, payload);
+
+ wdata->state.accel_split[1] |= (payload[0] >> 5) & (0x01 | 0x02);
+ wdata->state.accel_split[1] |= (payload[1] >> 3) & (0x04 | 0x08);
+
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = wdata->state.accel_split[0];
+ buf[3] = payload[2];
+ buf[4] = wdata->state.accel_split[1];
+ handler_accel(wdata, buf);
+
+ ir_to_input2(wdata, &payload[3], false);
+ ir_to_input3(wdata, &payload[12], false);
+ input_sync(wdata->ir);
+}
+
+struct wiiproto_handler {
+ __u8 id;
+ size_t size;
+ void (*func)(struct wiimote_data *wdata, const __u8 *payload);
+};
+
+static struct wiiproto_handler handlers[] = {
+ { .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status },
+ { .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data },
+ { .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return },
+ { .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys },
+ { .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA },
+ { .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE },
+ { .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI },
+ { .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE },
+ { .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE },
+ { .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE },
+ { .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE },
+ { .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E },
+ { .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 },
+ { .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 },
+ { .id = 0 }
+};
+
+static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *raw_data, int size)
+{
+ struct wiimote_data *wdata = hid_get_drvdata(hdev);
+ struct wiiproto_handler *h;
+ int i;
+ unsigned long flags;
+ bool handled = false;
+
+ if (size < 1)
+ return -EINVAL;
+
+ spin_lock_irqsave(&wdata->state.lock, flags);
+
+ for (i = 0; handlers[i].id; ++i) {
+ h = &handlers[i];
+ if (h->id == raw_data[0] && h->size < size) {
+ h->func(wdata, &raw_data[1]);
+ handled = true;
+ }
+ }
+
+ if (!handled)
+ hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0],
+ size);
+
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ return 0;
+}
+
+static void wiimote_leds_destroy(struct wiimote_data *wdata)
+{
+ int i;
+ struct led_classdev *led;
+
+ for (i = 0; i < 4; ++i) {
+ if (wdata->leds[i]) {
+ led = wdata->leds[i];
+ wdata->leds[i] = NULL;
+ led_classdev_unregister(led);
+ kfree(led);
+ }
+ }
+}
+
+static int wiimote_leds_create(struct wiimote_data *wdata)
+{
+ int i, ret;
+ struct device *dev = &wdata->hdev->dev;
+ size_t namesz = strlen(dev_name(dev)) + 9;
+ struct led_classdev *led;
+ char *name;
+
+ for (i = 0; i < 4; ++i) {
+ led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL);
+ if (!led) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ name = (void*)&led[1];
+ snprintf(name, namesz, "%s:blue:p%d", dev_name(dev), i);
+ led->name = name;
+ led->brightness = 0;
+ led->max_brightness = 1;
+ led->brightness_get = wiimote_leds_get;
+ led->brightness_set = wiimote_leds_set;
+
+ ret = led_classdev_register(dev, led);
+ if (ret) {
+ kfree(led);
+ goto err;
+ }
+ wdata->leds[i] = led;
+ }
+
+ return 0;
+
+err:
+ wiimote_leds_destroy(wdata);
+ return ret;
+}
+
+static struct wiimote_data *wiimote_create(struct hid_device *hdev)
+{
+ struct wiimote_data *wdata;
+ int i;
+
+ wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
+ if (!wdata)
+ return NULL;
+
+ wdata->input = input_allocate_device();
+ if (!wdata->input)
+ goto err;
+
+ wdata->hdev = hdev;
+ hid_set_drvdata(hdev, wdata);
+
+ input_set_drvdata(wdata->input, wdata);
+ wdata->input->open = wiimote_input_open;
+ wdata->input->close = wiimote_input_close;
+ wdata->input->dev.parent = &wdata->hdev->dev;
+ wdata->input->id.bustype = wdata->hdev->bus;
+ wdata->input->id.vendor = wdata->hdev->vendor;
+ wdata->input->id.product = wdata->hdev->product;
+ wdata->input->id.version = wdata->hdev->version;
+ wdata->input->name = WIIMOTE_NAME;
+
+ set_bit(EV_KEY, wdata->input->evbit);
+ for (i = 0; i < WIIPROTO_KEY_COUNT; ++i)
+ set_bit(wiiproto_keymap[i], wdata->input->keybit);
+
+ set_bit(FF_RUMBLE, wdata->input->ffbit);
+ if (input_ff_create_memless(wdata->input, NULL, wiimote_ff_play))
+ goto err_input;
+
+ wdata->accel = input_allocate_device();
+ if (!wdata->accel)
+ goto err_input;
+
+ input_set_drvdata(wdata->accel, wdata);
+ wdata->accel->open = wiimote_accel_open;
+ wdata->accel->close = wiimote_accel_close;
+ wdata->accel->dev.parent = &wdata->hdev->dev;
+ wdata->accel->id.bustype = wdata->hdev->bus;
+ wdata->accel->id.vendor = wdata->hdev->vendor;
+ wdata->accel->id.product = wdata->hdev->product;
+ wdata->accel->id.version = wdata->hdev->version;
+ wdata->accel->name = WIIMOTE_NAME " Accelerometer";
+
+ set_bit(EV_ABS, wdata->accel->evbit);
+ set_bit(ABS_RX, wdata->accel->absbit);
+ set_bit(ABS_RY, wdata->accel->absbit);
+ set_bit(ABS_RZ, wdata->accel->absbit);
+ input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4);
+ input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4);
+ input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4);
+
+ wdata->ir = input_allocate_device();
+ if (!wdata->ir)
+ goto err_ir;
+
+ input_set_drvdata(wdata->ir, wdata);
+ wdata->ir->open = wiimote_ir_open;
+ wdata->ir->close = wiimote_ir_close;
+ wdata->ir->dev.parent = &wdata->hdev->dev;
+ wdata->ir->id.bustype = wdata->hdev->bus;
+ wdata->ir->id.vendor = wdata->hdev->vendor;
+ wdata->ir->id.product = wdata->hdev->product;
+ wdata->ir->id.version = wdata->hdev->version;
+ wdata->ir->name = WIIMOTE_NAME " IR";
+
+ set_bit(EV_ABS, wdata->ir->evbit);
+ set_bit(ABS_HAT0X, wdata->ir->absbit);
+ set_bit(ABS_HAT0Y, wdata->ir->absbit);
+ set_bit(ABS_HAT1X, wdata->ir->absbit);
+ set_bit(ABS_HAT1Y, wdata->ir->absbit);
+ set_bit(ABS_HAT2X, wdata->ir->absbit);
+ set_bit(ABS_HAT2Y, wdata->ir->absbit);
+ set_bit(ABS_HAT3X, wdata->ir->absbit);
+ set_bit(ABS_HAT3Y, wdata->ir->absbit);
+ input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4);
+ input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4);
+ input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4);
+ input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4);
+ input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4);
+ input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4);
+ input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4);
+ input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4);
+
+ spin_lock_init(&wdata->qlock);
+ INIT_WORK(&wdata->worker, wiimote_worker);
+
+ spin_lock_init(&wdata->state.lock);
+ init_completion(&wdata->state.ready);
+ mutex_init(&wdata->state.sync);
+
+ return wdata;
+
+err_ir:
+ input_free_device(wdata->accel);
+err_input:
+ input_free_device(wdata->input);
+err:
+ kfree(wdata);
+ return NULL;
+}
+
+static void wiimote_destroy(struct wiimote_data *wdata)
+{
+ wiimote_leds_destroy(wdata);
+
+ power_supply_unregister(&wdata->battery);
+ input_unregister_device(wdata->accel);
+ input_unregister_device(wdata->ir);
+ input_unregister_device(wdata->input);
+ cancel_work_sync(&wdata->worker);
+ hid_hw_stop(wdata->hdev);
+
+ kfree(wdata);
+}
+
+static int wiimote_hid_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct wiimote_data *wdata;
+ int ret;
+
+ wdata = wiimote_create(hdev);
+ if (!wdata) {
+ hid_err(hdev, "Can't alloc device\n");
+ return -ENOMEM;
+ }
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "HID parse failed\n");
+ goto err;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "HW start failed\n");
+ goto err;
+ }
+
+ ret = input_register_device(wdata->accel);
+ if (ret) {
+ hid_err(hdev, "Cannot register input device\n");
+ goto err_stop;
+ }
+
+ ret = input_register_device(wdata->ir);
+ if (ret) {
+ hid_err(hdev, "Cannot register input device\n");
+ goto err_ir;
+ }
+
+ ret = input_register_device(wdata->input);
+ if (ret) {
+ hid_err(hdev, "Cannot register input device\n");
+ goto err_input;
+ }
+
+ wdata->battery.properties = wiimote_battery_props;
+ wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props);
+ wdata->battery.get_property = wiimote_battery_get_property;
+ wdata->battery.name = "wiimote_battery";
+ wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+ wdata->battery.use_for_apm = 0;
+
+ ret = power_supply_register(&wdata->hdev->dev, &wdata->battery);
+ if (ret) {
+ hid_err(hdev, "Cannot register battery device\n");
+ goto err_battery;
+ }
+
+ ret = wiimote_leds_create(wdata);
+ if (ret)
+ goto err_free;
+
+ hid_info(hdev, "New device registered\n");
+
+ /* by default set led1 after device initialization */
+ spin_lock_irq(&wdata->state.lock);
+ wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1);
+ spin_unlock_irq(&wdata->state.lock);
+
+ return 0;
+
+err_free:
+ wiimote_destroy(wdata);
+ return ret;
+
+err_battery:
+ input_unregister_device(wdata->input);
+ wdata->input = NULL;
+err_input:
+ input_unregister_device(wdata->ir);
+ wdata->ir = NULL;
+err_ir:
+ input_unregister_device(wdata->accel);
+ wdata->accel = NULL;
+err_stop:
+ hid_hw_stop(hdev);
+err:
+ input_free_device(wdata->ir);
+ input_free_device(wdata->accel);
+ input_free_device(wdata->input);
+ kfree(wdata);
+ return ret;
+}
+
+static void wiimote_hid_remove(struct hid_device *hdev)
+{
+ struct wiimote_data *wdata = hid_get_drvdata(hdev);
+
+ hid_info(hdev, "Device removed\n");
+ wiimote_destroy(wdata);
+}
+
+static const struct hid_device_id wiimote_hid_devices[] = {
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
+ USB_DEVICE_ID_NINTENDO_WIIMOTE) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, wiimote_hid_devices);
+
+static struct hid_driver wiimote_hid_driver = {
+ .name = "wiimote",
+ .id_table = wiimote_hid_devices,
+ .probe = wiimote_hid_probe,
+ .remove = wiimote_hid_remove,
+ .raw_event = wiimote_hid_event,
+};
+
+static int __init wiimote_init(void)
+{
+ int ret;
+
+ ret = hid_register_driver(&wiimote_hid_driver);
+ if (ret)
+ pr_err("Can't register wiimote hid driver\n");
+
+ return ret;
+}
+
+static void __exit wiimote_exit(void)
+{
+ hid_unregister_driver(&wiimote_hid_driver);
+}
+
+module_init(wiimote_init);
+module_exit(wiimote_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
+MODULE_DESCRIPTION(WIIMOTE_NAME " Device Driver");
+MODULE_VERSION(WIIMOTE_VERSION);
diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c
index f31fab012f2f..f6ba81df71bd 100644
--- a/drivers/hid/hid-zpff.c
+++ b/drivers/hid/hid-zpff.c
@@ -25,6 +25,7 @@
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/usb.h>
+#include <linux/module.h>
#include "hid-ids.h"
diff --git a/drivers/hid/hid-zydacron.c b/drivers/hid/hid-zydacron.c
index e90371508fd2..1ad85f2257b4 100644
--- a/drivers/hid/hid-zydacron.c
+++ b/drivers/hid/hid-zydacron.c
@@ -201,9 +201,7 @@ static void zc_remove(struct hid_device *hdev)
struct zc_device *zc = hid_get_drvdata(hdev);
hid_hw_stop(hdev);
-
- if (NULL != zc)
- kfree(zc);
+ kfree(zc);
}
static const struct hid_device_id zc_devices[] = {
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index c79578b5a788..cf7d6d58e79f 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -259,7 +259,6 @@ static int hidraw_open(struct inode *inode, struct file *file)
mutex_lock(&minors_lock);
if (!hidraw_table[minor]) {
- kfree(list);
err = -ENODEV;
goto out_unlock;
}
@@ -272,8 +271,10 @@ static int hidraw_open(struct inode *inode, struct file *file)
dev = hidraw_table[minor];
if (!dev->open++) {
err = hid_hw_power(dev->hid, PM_HINT_FULLON);
- if (err < 0)
+ if (err < 0) {
+ dev->open--;
goto out_unlock;
+ }
err = hid_hw_open(dev->hid);
if (err < 0) {
@@ -285,6 +286,8 @@ static int hidraw_open(struct inode *inode, struct file *file)
out_unlock:
mutex_unlock(&minors_lock);
out:
+ if (err < 0)
+ kfree(list);
return err;
}
@@ -510,13 +513,12 @@ void hidraw_disconnect(struct hid_device *hid)
{
struct hidraw *hidraw = hid->hidraw;
+ mutex_lock(&minors_lock);
hidraw->exist = 0;
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
- mutex_lock(&minors_lock);
hidraw_table[hidraw->minor] = NULL;
- mutex_unlock(&minors_lock);
if (hidraw->open) {
hid_hw_close(hid);
@@ -524,6 +526,7 @@ void hidraw_disconnect(struct hid_device *hid)
} else {
kfree(hidraw);
}
+ mutex_unlock(&minors_lock);
}
EXPORT_SYMBOL_GPL(hidraw_disconnect);
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 38c261a40c74..b403fcef0b86 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -1191,6 +1191,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
if (intf->cur_altsetting->desc.bInterfaceProtocol ==
USB_INTERFACE_PROTOCOL_MOUSE)
hid->type = HID_TYPE_USBMOUSE;
+ else if (intf->cur_altsetting->desc.bInterfaceProtocol == 0)
+ hid->type = HID_TYPE_USBNONE;
if (dev->manufacturer)
strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
@@ -1268,7 +1270,7 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
static void hid_cease_io(struct usbhid_device *usbhid)
{
- del_timer(&usbhid->io_retry);
+ del_timer_sync(&usbhid->io_retry);
usb_kill_urb(usbhid->urbin);
usb_kill_urb(usbhid->urbctrl);
usb_kill_urb(usbhid->urbout);
@@ -1330,7 +1332,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
struct usbhid_device *usbhid = hid->driver_data;
int status;
- if (message.event & PM_EVENT_AUTO) {
+ if (PMSG_IS_AUTO(message)) {
spin_lock_irq(&usbhid->lock); /* Sync with error handler */
if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
&& !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
@@ -1365,7 +1367,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
return -EIO;
}
- if (!ignoreled && (message.event & PM_EVENT_AUTO)) {
+ if (!ignoreled && PMSG_IS_AUTO(message)) {
spin_lock_irq(&usbhid->lock);
if (test_bit(HID_LED_ON, &usbhid->iofl)) {
spin_unlock_irq(&usbhid->lock);
@@ -1378,8 +1380,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
hid_cancel_delayed_stuff(usbhid);
hid_cease_io(usbhid);
- if ((message.event & PM_EVENT_AUTO) &&
- test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
+ if (PMSG_IS_AUTO(message) && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
/* lost race against keypresses */
status = hid_start_in(hid);
if (status < 0)
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 621959d5cc42..5028d60a22a1 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -16,6 +16,7 @@
*/
#include <linux/hid.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include "../hid-ids.h"
@@ -47,6 +48,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
{ USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT },
@@ -79,16 +81,15 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH, HID_QUIRK_MULTI_INPUT },
- { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
- { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_PI_ENGINEERING, USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL, HID_QUIRK_HIDINPUT_FORCE },
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS },
{ 0, 0 }
};
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 7c1188b53c3e..4ef02b269a71 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -641,6 +641,8 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct usb_device *dev = hid_to_usb_dev(hid);
struct usbhid_device *usbhid = hid->driver_data;
+ memset(&dinfo, 0, sizeof(dinfo));
+
dinfo.bustype = BUS_USB;
dinfo.busnum = dev->bus->busnum;
dinfo.devnum = dev->devnum;
diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
new file mode 100644
index 000000000000..9fa09ac000ad
--- /dev/null
+++ b/drivers/hv/Kconfig
@@ -0,0 +1,14 @@
+config HYPERV
+ tristate "Microsoft Hyper-V client drivers"
+ depends on X86 && ACPI && PCI
+ help
+ Select this option to run Linux as a Hyper-V client operating
+ system.
+
+config HYPERV_UTILS
+ tristate "Microsoft Hyper-V Utilities driver"
+ depends on HYPERV && CONNECTOR && NLS
+ help
+ Select this option to enable the Hyper-V Utilities.
+
+
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
new file mode 100644
index 000000000000..a23938b991c9
--- /dev/null
+++ b/drivers/hv/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_HYPERV) += hv_vmbus.o
+obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
+
+hv_vmbus-y := vmbus_drv.o \
+ hv.o connection.o channel.o \
+ channel_mgmt.o ring_buffer.o
+hv_utils-y := hv_util.o hv_kvp.o
diff --git a/drivers/staging/hv/channel.c b/drivers/hv/channel.c
index f655e59a9a8f..406537420fff 100644
--- a/drivers/staging/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -26,8 +26,8 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/hyperv.h>
-#include "hyperv.h"
#include "hyperv_vmbus.h"
#define NUM_PAGES_SPANNED(addr, len) \
@@ -39,7 +39,6 @@ static int create_gpadl_header(
u32 size, /* page-size multiple */
struct vmbus_channel_msginfo **msginfo,
u32 *messagecount);
-static void dump_vmbus_channel(struct vmbus_channel *channel);
static void vmbus_setevent(struct vmbus_channel *channel);
/*
@@ -77,15 +76,14 @@ void vmbus_get_debug_info(struct vmbus_channel *channel,
struct hv_monitor_page *monitorpage;
u8 monitor_group = (u8)channel->offermsg.monitorid / 32;
u8 monitor_offset = (u8)channel->offermsg.monitorid % 32;
- /* u32 monitorBit = 1 << monitorOffset; */
debuginfo->relid = channel->offermsg.child_relid;
debuginfo->state = channel->state;
memcpy(&debuginfo->interfacetype,
- &channel->offermsg.offer.if_type, sizeof(struct hv_guid));
+ &channel->offermsg.offer.if_type, sizeof(uuid_le));
memcpy(&debuginfo->interface_instance,
&channel->offermsg.offer.if_instance,
- sizeof(struct hv_guid));
+ sizeof(uuid_le));
monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages;
@@ -120,8 +118,8 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
u32 recv_ringbuffer_size, void *userdata, u32 userdatalen,
void (*onchannelcallback)(void *context), void *context)
{
- struct vmbus_channel_open_channel *openMsg;
- struct vmbus_channel_msginfo *openInfo = NULL;
+ struct vmbus_channel_open_channel *open_msg;
+ struct vmbus_channel_msginfo *open_info = NULL;
void *in, *out;
unsigned long flags;
int ret, t, err = 0;
@@ -174,24 +172,24 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
}
/* Create and init the channel open message */
- openInfo = kmalloc(sizeof(*openInfo) +
+ open_info = kmalloc(sizeof(*open_info) +
sizeof(struct vmbus_channel_open_channel),
GFP_KERNEL);
- if (!openInfo) {
+ if (!open_info) {
err = -ENOMEM;
goto errorout;
}
- init_completion(&openInfo->waitevent);
+ init_completion(&open_info->waitevent);
- openMsg = (struct vmbus_channel_open_channel *)openInfo->msg;
- openMsg->header.msgtype = CHANNELMSG_OPENCHANNEL;
- openMsg->openid = newchannel->offermsg.child_relid; /* FIXME */
- openMsg->child_relid = newchannel->offermsg.child_relid;
- openMsg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle;
- openMsg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >>
+ open_msg = (struct vmbus_channel_open_channel *)open_info->msg;
+ open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL;
+ open_msg->openid = newchannel->offermsg.child_relid;
+ open_msg->child_relid = newchannel->offermsg.child_relid;
+ open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle;
+ open_msg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >>
PAGE_SHIFT;
- openMsg->server_contextarea_gpadlhandle = 0; /* TODO */
+ open_msg->server_contextarea_gpadlhandle = 0;
if (userdatalen > MAX_USER_DEFINED_BYTES) {
err = -EINVAL;
@@ -199,35 +197,35 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
}
if (userdatalen)
- memcpy(openMsg->userdata, userdata, userdatalen);
+ memcpy(open_msg->userdata, userdata, userdatalen);
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
- list_add_tail(&openInfo->msglistentry,
+ list_add_tail(&open_info->msglistentry,
&vmbus_connection.chn_msg_list);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
- ret = vmbus_post_msg(openMsg,
+ ret = vmbus_post_msg(open_msg,
sizeof(struct vmbus_channel_open_channel));
if (ret != 0)
- goto Cleanup;
+ goto cleanup;
- t = wait_for_completion_timeout(&openInfo->waitevent, HZ);
+ t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ);
if (t == 0) {
err = -ETIMEDOUT;
goto errorout;
}
- if (openInfo->response.open_result.status)
- err = openInfo->response.open_result.status;
+ if (open_info->response.open_result.status)
+ err = open_info->response.open_result.status;
-Cleanup:
+cleanup:
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
- list_del(&openInfo->msglistentry);
+ list_del(&open_info->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
- kfree(openInfo);
+ kfree(open_info);
return err;
errorout:
@@ -235,57 +233,12 @@ errorout:
hv_ringbuffer_cleanup(&newchannel->inbound);
free_pages((unsigned long)out,
get_order(send_ringbuffer_size + recv_ringbuffer_size));
- kfree(openInfo);
+ kfree(open_info);
return err;
}
EXPORT_SYMBOL_GPL(vmbus_open);
/*
- * dump_gpadl_body - Dump the gpadl body message to the console for
- * debugging purposes.
- */
-static void dump_gpadl_body(struct vmbus_channel_gpadl_body *gpadl, u32 len)
-{
- int i;
- int pfncount;
-
- pfncount = (len - sizeof(struct vmbus_channel_gpadl_body)) /
- sizeof(u64);
-
- DPRINT_DBG(VMBUS, "gpadl body - len %d pfn count %d", len, pfncount);
-
- for (i = 0; i < pfncount; i++)
- DPRINT_DBG(VMBUS, "gpadl body - %d) pfn %llu",
- i, gpadl->pfn[i]);
-}
-
-/*
- * dump_gpadl_header - Dump the gpadl header message to the console for
- * debugging purposes.
- */
-static void dump_gpadl_header(struct vmbus_channel_gpadl_header *gpadl)
-{
- int i, j;
- int pagecount;
-
- DPRINT_DBG(VMBUS,
- "gpadl header - relid %d, range count %d, range buflen %d",
- gpadl->child_relid, gpadl->rangecount, gpadl->range_buflen);
- for (i = 0; i < gpadl->rangecount; i++) {
- pagecount = gpadl->range[i].byte_count >> PAGE_SHIFT;
- pagecount = (pagecount > 26) ? 26 : pagecount;
-
- DPRINT_DBG(VMBUS, "gpadl range %d - len %d offset %d "
- "page count %d", i, gpadl->range[i].byte_count,
- gpadl->range[i].byte_offset, pagecount);
-
- for (j = 0; j < pagecount; j++)
- DPRINT_DBG(VMBUS, "%d) pfn %llu", j,
- gpadl->range[i].pfn_array[j]);
- }
-}
-
-/*
* create_gpadl_header - Creates a gpadl for the specified buffer
*/
static int create_gpadl_header(void *kbuffer, u32 size,
@@ -356,20 +309,35 @@ static int create_gpadl_header(void *kbuffer, u32 size,
sizeof(struct vmbus_channel_gpadl_body) +
pfncurr * sizeof(u64);
msgbody = kzalloc(msgsize, GFP_KERNEL);
- /* FIXME: we probably need to more if this fails */
- if (!msgbody)
+
+ if (!msgbody) {
+ struct vmbus_channel_msginfo *pos = NULL;
+ struct vmbus_channel_msginfo *tmp = NULL;
+ /*
+ * Free up all the allocated messages.
+ */
+ list_for_each_entry_safe(pos, tmp,
+ &msgheader->submsglist,
+ msglistentry) {
+
+ list_del(&pos->msglistentry);
+ kfree(pos);
+ }
+
goto nomem;
+ }
+
msgbody->msgsize = msgsize;
(*messagecount)++;
gpadl_body =
(struct vmbus_channel_gpadl_body *)msgbody->msg;
/*
- * FIXME:
* Gpadl is u32 and we are using a pointer which could
* be 64-bit
+ * This is governed by the guest/host protocol and
+ * so the hypervisor gurantees that this is ok.
*/
- /* gpadl_body->Gpadl = kbuffer; */
for (i = 0; i < pfncurr; i++)
gpadl_body->pfn[i] = pfn + pfnsum + i;
@@ -423,7 +391,6 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
{
struct vmbus_channel_gpadl_header *gpadlmsg;
struct vmbus_channel_gpadl_body *gpadl_body;
- /* struct vmbus_channel_gpadl_created *gpadlCreated; */
struct vmbus_channel_msginfo *msginfo = NULL;
struct vmbus_channel_msginfo *submsginfo;
u32 msgcount;
@@ -447,7 +414,6 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
gpadlmsg->child_relid = channel->offermsg.child_relid;
gpadlmsg->gpadl = next_gpadl_handle;
- dump_gpadl_header(gpadlmsg);
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_add_tail(&msginfo->msglistentry,
@@ -458,12 +424,11 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize -
sizeof(*msginfo));
if (ret != 0)
- goto Cleanup;
+ goto cleanup;
if (msgcount > 1) {
list_for_each(curr, &msginfo->submsglist) {
- /* FIXME: should this use list_entry() instead ? */
submsginfo = (struct vmbus_channel_msginfo *)curr;
gpadl_body =
(struct vmbus_channel_gpadl_body *)submsginfo->msg;
@@ -472,24 +437,22 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
CHANNELMSG_GPADL_BODY;
gpadl_body->gpadl = next_gpadl_handle;
- dump_gpadl_body(gpadl_body, submsginfo->msgsize -
- sizeof(*submsginfo));
ret = vmbus_post_msg(gpadl_body,
submsginfo->msgsize -
sizeof(*submsginfo));
if (ret != 0)
- goto Cleanup;
+ goto cleanup;
}
}
- t = wait_for_completion_timeout(&msginfo->waitevent, HZ);
+ t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
BUG_ON(t == 0);
/* At this point, we received the gpadl created msg */
*gpadl_handle = gpadlmsg->gpadl;
-Cleanup:
+cleanup:
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&msginfo->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
@@ -509,8 +472,6 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
unsigned long flags;
int ret, t;
- /* ASSERT(gpadl_handle != 0); */
-
info = kmalloc(sizeof(*info) +
sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL);
if (!info)
@@ -532,7 +493,7 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
sizeof(struct vmbus_channel_gpadl_teardown));
BUG_ON(ret != 0);
- t = wait_for_completion_timeout(&info->waitevent, HZ);
+ t = wait_for_completion_timeout(&info->waitevent, 5*HZ);
BUG_ON(t == 0);
/* Received a torndown response */
@@ -551,24 +512,18 @@ EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
void vmbus_close(struct vmbus_channel *channel)
{
struct vmbus_channel_close_channel *msg;
- struct vmbus_channel_msginfo *info;
- unsigned long flags;
int ret;
+ unsigned long flags;
/* Stop callback and cancel the timer asap */
+ spin_lock_irqsave(&channel->inbound_lock, flags);
channel->onchannel_callback = NULL;
- del_timer_sync(&channel->poll_timer);
+ spin_unlock_irqrestore(&channel->inbound_lock, flags);
/* Send a closing message */
- info = kmalloc(sizeof(*info) +
- sizeof(struct vmbus_channel_close_channel), GFP_KERNEL);
- /* FIXME: can't do anything other than return here because the
- * function is void */
- if (!info)
- return;
+ msg = &channel->close_msg.msg;
- msg = (struct vmbus_channel_close_channel *)info->msg;
msg->header.msgtype = CHANNELMSG_CLOSECHANNEL;
msg->child_relid = channel->offermsg.child_relid;
@@ -580,8 +535,6 @@ void vmbus_close(struct vmbus_channel *channel)
vmbus_teardown_gpadl(channel,
channel->ringbuffer_gpadlhandle);
- /* TODO: Send a msg to release the childRelId */
-
/* Cleanup the ring buffers for this channel */
hv_ringbuffer_cleanup(&channel->outbound);
hv_ringbuffer_cleanup(&channel->inbound);
@@ -589,21 +542,7 @@ void vmbus_close(struct vmbus_channel *channel)
free_pages((unsigned long)channel->ringbuffer_pages,
get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
- kfree(info);
- /*
- * If we are closing the channel during an error path in
- * opening the channel, don't free the channel since the
- * caller will free the channel
- */
-
- if (channel->state == CHANNEL_OPEN_STATE) {
- spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
- list_del(&channel->listentry);
- spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
-
- free_channel(channel);
- }
}
EXPORT_SYMBOL_GPL(vmbus_close);
@@ -632,7 +571,6 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer,
u64 aligned_data = 0;
int ret;
- dump_vmbus_channel(channel);
/* Setup the descriptor */
desc.type = type; /* VmbusPacketTypeDataInBand; */
@@ -650,7 +588,6 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer,
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3);
- /* TODO: We should determine if this is optional */
if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound))
vmbus_setevent(channel);
@@ -679,7 +616,6 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
if (pagecount > MAX_PAGE_BUFFER_COUNT)
return -EINVAL;
- dump_vmbus_channel(channel);
/*
* Adjust the size down since vmbus_channel_packet_page_buffer is the
@@ -713,7 +649,6 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3);
- /* TODO: We should determine if this is optional */
if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound))
vmbus_setevent(channel);
@@ -739,7 +674,6 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
multi_pagebuffer->len);
- dump_vmbus_channel(channel);
if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT))
return -EINVAL;
@@ -777,7 +711,6 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3);
- /* TODO: We should determine if this is optional */
if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound))
vmbus_setevent(channel);
@@ -805,19 +738,15 @@ int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
u32 packetlen;
u32 userlen;
int ret;
- unsigned long flags;
*buffer_actual_len = 0;
*requestid = 0;
- spin_lock_irqsave(&channel->inbound_lock, flags);
ret = hv_ringbuffer_peek(&channel->inbound, &desc,
sizeof(struct vmpacket_descriptor));
- if (ret != 0) {
- spin_unlock_irqrestore(&channel->inbound_lock, flags);
+ if (ret != 0)
return 0;
- }
packetlen = desc.len8 << 3;
userlen = packetlen - (desc.offset8 << 3);
@@ -825,11 +754,10 @@ int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
*buffer_actual_len = userlen;
if (userlen > bufferlen) {
- spin_unlock_irqrestore(&channel->inbound_lock, flags);
pr_err("Buffer too small - got %d needs %d\n",
bufferlen, userlen);
- return -1;
+ return -ETOOSMALL;
}
*requestid = desc.trans_id;
@@ -838,7 +766,6 @@ int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen,
(desc.offset8 << 3));
- spin_unlock_irqrestore(&channel->inbound_lock, flags);
return 0;
}
@@ -855,19 +782,15 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer,
u32 packetlen;
u32 userlen;
int ret;
- unsigned long flags;
*buffer_actual_len = 0;
*requestid = 0;
- spin_lock_irqsave(&channel->inbound_lock, flags);
ret = hv_ringbuffer_peek(&channel->inbound, &desc,
sizeof(struct vmpacket_descriptor));
- if (ret != 0) {
- spin_unlock_irqrestore(&channel->inbound_lock, flags);
+ if (ret != 0)
return 0;
- }
packetlen = desc.len8 << 3;
@@ -876,12 +799,10 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer,
*buffer_actual_len = packetlen;
if (packetlen > bufferlen) {
- spin_unlock_irqrestore(&channel->inbound_lock, flags);
-
pr_err("Buffer too small - needed %d bytes but "
"got space for only %d bytes\n",
packetlen, bufferlen);
- return -2;
+ return -ENOBUFS;
}
*requestid = desc.trans_id;
@@ -889,40 +810,6 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer,
/* Copy over the entire packet to the user buffer */
ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0);
- spin_unlock_irqrestore(&channel->inbound_lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw);
-
-/*
- * vmbus_onchannel_event - Channel event callback
- */
-void vmbus_onchannel_event(struct vmbus_channel *channel)
-{
- dump_vmbus_channel(channel);
-
- channel->onchannel_callback(channel->channel_callback_context);
-
- mod_timer(&channel->poll_timer, jiffies + usecs_to_jiffies(100));
-}
-
-/*
- * vmbus_ontimer - Timer event callback
- */
-void vmbus_ontimer(unsigned long data)
-{
- struct vmbus_channel *channel = (struct vmbus_channel *)data;
-
- if (channel->onchannel_callback)
- channel->onchannel_callback(channel->channel_callback_context);
-}
-
-/*
- * dump_vmbus_channel- Dump vmbus channel info to the console
- */
-static void dump_vmbus_channel(struct vmbus_channel *channel)
-{
- DPRINT_DBG(VMBUS, "Channel (%d)", channel->offermsg.child_relid);
- hv_dump_ring_info(&channel->outbound, "Outbound ");
- hv_dump_ring_info(&channel->inbound, "Inbound ");
-}
diff --git a/drivers/staging/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 957d61ee4ceb..12b85ff957fd 100644
--- a/drivers/staging/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -28,8 +28,8 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/completion.h>
+#include <linux/hyperv.h>
-#include "hyperv.h"
#include "hyperv_vmbus.h"
struct vmbus_channel_message_table_entry {
@@ -40,12 +40,12 @@ struct vmbus_channel_message_table_entry {
#define MAX_MSG_TYPES 4
#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 8
-static const struct hv_guid
+static const uuid_le
supported_device_classes[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = {
/* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */
/* Storage - SCSI */
{
- .data = {
+ .b = {
0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f
}
@@ -54,7 +54,7 @@ static const struct hv_guid
/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
/* Network */
{
- .data = {
+ .b = {
0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E
}
@@ -63,7 +63,7 @@ static const struct hv_guid
/* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */
/* Input */
{
- .data = {
+ .b = {
0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A
}
@@ -72,7 +72,7 @@ static const struct hv_guid
/* {32412632-86cb-44a2-9b5c-50d1417354f5} */
/* IDE */
{
- .data = {
+ .b = {
0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,
0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5
}
@@ -80,7 +80,7 @@ static const struct hv_guid
/* 0E0B6031-5213-4934-818B-38D90CED39DB */
/* Shutdown */
{
- .data = {
+ .b = {
0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49,
0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB
}
@@ -88,7 +88,7 @@ static const struct hv_guid
/* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */
/* TimeSync */
{
- .data = {
+ .b = {
0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49,
0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf
}
@@ -96,7 +96,7 @@ static const struct hv_guid
/* {57164f39-9115-4e78-ab55-382f3bd5422d} */
/* Heartbeat */
{
- .data = {
+ .b = {
0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e,
0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d
}
@@ -104,7 +104,7 @@ static const struct hv_guid
/* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */
/* KVP */
{
- .data = {
+ .b = {
0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d,
0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6
}
@@ -114,7 +114,7 @@ static const struct hv_guid
/**
- * prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
+ * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
* @icmsghdrp: Pointer to msg header structure
* @icmsg_negotiate: Pointer to negotiate message structure
* @buf: Raw buffer channel data
@@ -128,9 +128,8 @@ static const struct hv_guid
*
* Mainly used by Hyper-V drivers.
*/
-void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
- struct icmsg_negotiate *negop,
- u8 *buf)
+void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
+ struct icmsg_negotiate *negop, u8 *buf)
{
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
icmsghdrp->icmsgsize = 0x10;
@@ -156,119 +155,7 @@ void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
negop->icmsg_vercnt = 1;
}
}
-EXPORT_SYMBOL(prep_negotiate_resp);
-
-/**
- * chn_cb_negotiate() - Default handler for non IDE/SCSI/NETWORK
- * Hyper-V requests
- * @context: Pointer to argument structure.
- *
- * Set up the default handler for non device driver specific requests
- * from Hyper-V. This stub responds to the default negotiate messages
- * that come in for every non IDE/SCSI/Network request.
- * This behavior is normally overwritten in the hv_utils driver. That
- * driver handles requests like graceful shutdown, heartbeats etc.
- *
- * Mainly used by Hyper-V drivers.
- */
-void chn_cb_negotiate(void *context)
-{
- struct vmbus_channel *channel = context;
- u8 *buf;
- u32 buflen, recvlen;
- u64 requestid;
-
- struct icmsg_hdr *icmsghdrp;
- struct icmsg_negotiate *negop = NULL;
-
- if (channel->util_index >= 0) {
- /*
- * This is a properly initialized util channel.
- * Route this callback appropriately and setup state
- * so that we don't need to reroute again.
- */
- if (hv_cb_utils[channel->util_index].callback != NULL) {
- /*
- * The util driver has established a handler for
- * this service; do the magic.
- */
- channel->onchannel_callback =
- hv_cb_utils[channel->util_index].callback;
- (hv_cb_utils[channel->util_index].callback)(channel);
- return;
- }
- }
-
- buflen = PAGE_SIZE;
- buf = kmalloc(buflen, GFP_ATOMIC);
-
- vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
-
- if (recvlen > 0) {
- icmsghdrp = (struct icmsg_hdr *)&buf[
- sizeof(struct vmbuspipe_hdr)];
-
- prep_negotiate_resp(icmsghdrp, negop, buf);
-
- icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
- | ICMSGHDRFLAG_RESPONSE;
-
- vmbus_sendpacket(channel, buf,
- recvlen, requestid,
- VM_PKT_DATA_INBAND, 0);
- }
-
- kfree(buf);
-}
-EXPORT_SYMBOL(chn_cb_negotiate);
-
-/*
- * Function table used for message responses for non IDE/SCSI/Network type
- * messages. (Such as KVP/Shutdown etc)
- */
-struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = {
- /* 0E0B6031-5213-4934-818B-38D90CED39DB */
- /* Shutdown */
- {
- .msg_type = HV_SHUTDOWN_MSG,
- .data = {
- 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49,
- 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB
- },
- .log_msg = "Shutdown channel functionality initialized"
- },
-
- /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */
- /* TimeSync */
- {
- .msg_type = HV_TIMESYNC_MSG,
- .data = {
- 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49,
- 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf
- },
- .log_msg = "Timesync channel functionality initialized"
- },
- /* {57164f39-9115-4e78-ab55-382f3bd5422d} */
- /* Heartbeat */
- {
- .msg_type = HV_HEARTBEAT_MSG,
- .data = {
- 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e,
- 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d
- },
- .log_msg = "Heartbeat channel functionality initialized"
- },
- /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */
- /* KVP */
- {
- .data = {
- 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d,
- 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6
- },
- .log_msg = "KVP channel functionality initialized"
- },
-};
-EXPORT_SYMBOL(hv_cb_utils);
+EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
/*
* alloc_channel - Allocate and initialize a vmbus channel object
@@ -283,10 +170,6 @@ static struct vmbus_channel *alloc_channel(void)
spin_lock_init(&channel->inbound_lock);
- init_timer(&channel->poll_timer);
- channel->poll_timer.data = (unsigned long)channel;
- channel->poll_timer.function = vmbus_ontimer;
-
channel->controlwq = create_workqueue("hv_vmbus_ctl");
if (!channel->controlwq) {
kfree(channel);
@@ -313,9 +196,8 @@ static void release_channel(struct work_struct *work)
/*
* free_channel - Release the resources used by the vmbus channel object
*/
-void free_channel(struct vmbus_channel *channel)
+static void free_channel(struct vmbus_channel *channel)
{
- del_timer_sync(&channel->poll_timer);
/*
* We have to release the channel's workqueue/thread in the vmbus's
@@ -338,7 +220,7 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
struct vmbus_channel,
work);
- vmbus_child_device_unregister(channel->device_obj);
+ vmbus_device_unregister(channel->device_obj);
}
/*
@@ -353,7 +235,6 @@ static void vmbus_process_offer(struct work_struct *work)
struct vmbus_channel *channel;
bool fnew = true;
int ret;
- int cnt;
unsigned long flags;
/* The next possible work is rescind handling */
@@ -363,12 +244,10 @@ static void vmbus_process_offer(struct work_struct *work)
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
- if (!memcmp(&channel->offermsg.offer.if_type,
- &newchannel->offermsg.offer.if_type,
- sizeof(struct hv_guid)) &&
- !memcmp(&channel->offermsg.offer.if_instance,
- &newchannel->offermsg.offer.if_instance,
- sizeof(struct hv_guid))) {
+ if (!uuid_le_cmp(channel->offermsg.offer.if_type,
+ newchannel->offermsg.offer.if_type) &&
+ !uuid_le_cmp(channel->offermsg.offer.if_instance,
+ newchannel->offermsg.offer.if_instance)) {
fnew = false;
break;
}
@@ -390,7 +269,7 @@ static void vmbus_process_offer(struct work_struct *work)
* We need to set the DeviceObject field before calling
* vmbus_child_dev_add()
*/
- newchannel->device_obj = vmbus_child_device_create(
+ newchannel->device_obj = vmbus_device_create(
&newchannel->offermsg.offer.if_type,
&newchannel->offermsg.offer.if_instance,
newchannel);
@@ -400,7 +279,7 @@ static void vmbus_process_offer(struct work_struct *work)
* binding which eventually invokes the device driver's AddDevice()
* method.
*/
- ret = vmbus_child_device_register(newchannel->device_obj);
+ ret = vmbus_device_register(newchannel->device_obj);
if (ret != 0) {
pr_err("unable to add child device object (relid %d)\n",
newchannel->offermsg.child_relid);
@@ -417,48 +296,26 @@ static void vmbus_process_offer(struct work_struct *work)
* can cleanup properly
*/
newchannel->state = CHANNEL_OPEN_STATE;
- newchannel->util_index = -1; /* Invalid index */
-
- /* Open IC channels */
- for (cnt = 0; cnt < MAX_MSG_TYPES; cnt++) {
- if (memcmp(&newchannel->offermsg.offer.if_type,
- &hv_cb_utils[cnt].data,
- sizeof(struct hv_guid)) == 0 &&
- vmbus_open(newchannel, 2 * PAGE_SIZE,
- 2 * PAGE_SIZE, NULL, 0,
- chn_cb_negotiate,
- newchannel) == 0) {
- hv_cb_utils[cnt].channel = newchannel;
- newchannel->util_index = cnt;
-
- pr_info("%s\n", hv_cb_utils[cnt].log_msg);
-
- }
- }
}
}
/*
* vmbus_onoffer - Handler for channel offers from vmbus in parent partition.
*
- * We ignore all offers except network and storage offers. For each network and
- * storage offers, we create a channel object and queue a work item to the
- * channel object to process the offer synchronously
*/
static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
{
struct vmbus_channel_offer_channel *offer;
struct vmbus_channel *newchannel;
- struct hv_guid *guidtype;
- struct hv_guid *guidinstance;
+ uuid_le *guidtype;
+ uuid_le *guidinstance;
int i;
int fsupported = 0;
offer = (struct vmbus_channel_offer_channel *)hdr;
for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) {
- if (memcmp(&offer->offer.if_type,
- &supported_device_classes[i],
- sizeof(struct hv_guid)) == 0) {
+ if (!uuid_le_cmp(offer->offer.if_type,
+ supported_device_classes[i])) {
fsupported = 1;
break;
}
@@ -482,7 +339,6 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
newchannel->monitor_grp = (u8)offer->monitorid / 32;
newchannel->monitor_bit = (u8)offer->monitorid % 32;
- /* TODO: Make sure the offer comes from our parent partition */
INIT_WORK(&newchannel->work, vmbus_process_offer);
queue_work(newchannel->controlwq, &newchannel->work);
}
@@ -773,7 +629,7 @@ int vmbus_request_offers(void)
goto cleanup;
}
- t = wait_for_completion_timeout(&msginfo->waitevent, HZ);
+ t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
if (t == 0) {
ret = -ETIMEDOUT;
goto cleanup;
diff --git a/drivers/staging/hv/connection.c b/drivers/hv/connection.c
index 37bbf770ef11..650c9f0b6642 100644
--- a/drivers/staging/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -25,11 +25,12 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/wait.h>
+#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
-
-#include "hyperv.h"
+#include <linux/hyperv.h>
+#include <asm/hyperv.h>
#include "hyperv_vmbus.h"
@@ -49,15 +50,11 @@ int vmbus_connect(void)
struct vmbus_channel_initiate_contact *msg;
unsigned long flags;
- /* Make sure we are not connecting or connected */
- if (vmbus_connection.conn_state != DISCONNECTED)
- return -1;
-
/* Initialize the vmbus connection */
vmbus_connection.conn_state = CONNECTING;
vmbus_connection.work_queue = create_workqueue("hv_vmbus_con");
if (!vmbus_connection.work_queue) {
- ret = -1;
+ ret = -ENOMEM;
goto cleanup;
}
@@ -74,7 +71,7 @@ int vmbus_connect(void)
vmbus_connection.int_page =
(void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0);
if (vmbus_connection.int_page == NULL) {
- ret = -1;
+ ret = -ENOMEM;
goto cleanup;
}
@@ -90,7 +87,7 @@ int vmbus_connect(void)
vmbus_connection.monitor_pages =
(void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1);
if (vmbus_connection.monitor_pages == NULL) {
- ret = -1;
+ ret = -ENOMEM;
goto cleanup;
}
@@ -135,7 +132,7 @@ int vmbus_connect(void)
}
/* Wait for the connection response */
- t = wait_for_completion_timeout(&msginfo->waitevent, HZ);
+ t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
if (t == 0) {
spin_lock_irqsave(&vmbus_connection.channelmsg_lock,
flags);
@@ -157,7 +154,7 @@ int vmbus_connect(void)
pr_err("Unable to connect, "
"Version %d not supported by Hyper-V\n",
VMBUS_REVISION_NUMBER);
- ret = -1;
+ ret = -ECONNREFUSED;
goto cleanup;
}
@@ -185,44 +182,6 @@ cleanup:
return ret;
}
-/*
- * vmbus_disconnect -
- * Sends a disconnect request on the partition service connection
- */
-int vmbus_disconnect(void)
-{
- int ret = 0;
- struct vmbus_channel_message_header *msg;
-
- /* Make sure we are connected */
- if (vmbus_connection.conn_state != CONNECTED)
- return -1;
-
- msg = kzalloc(sizeof(struct vmbus_channel_message_header), GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- msg->msgtype = CHANNELMSG_UNLOAD;
-
- ret = vmbus_post_msg(msg,
- sizeof(struct vmbus_channel_message_header));
- if (ret != 0)
- goto cleanup;
-
- free_pages((unsigned long)vmbus_connection.int_page, 0);
- free_pages((unsigned long)vmbus_connection.monitor_pages, 1);
-
- /* TODO: iterate thru the msg list and free up */
- destroy_workqueue(vmbus_connection.work_queue);
-
- vmbus_connection.conn_state = DISCONNECTED;
-
- pr_info("hv_vmbus disconnected\n");
-
-cleanup:
- kfree(msg);
- return ret;
-}
/*
* relid2channel - Get the channel object given its
@@ -252,8 +211,7 @@ struct vmbus_channel *relid2channel(u32 relid)
static void process_chn_event(u32 relid)
{
struct vmbus_channel *channel;
-
- /* ASSERT(relId > 0); */
+ unsigned long flags;
/*
* Find the channel based on this relid and invokes the
@@ -261,11 +219,27 @@ static void process_chn_event(u32 relid)
*/
channel = relid2channel(relid);
- if (channel) {
- vmbus_onchannel_event(channel);
- } else {
+ if (!channel) {
pr_err("channel not found for relid - %u\n", relid);
+ return;
}
+
+ /*
+ * A channel once created is persistent even when there
+ * is no driver handling the device. An unloading driver
+ * sets the onchannel_callback to NULL under the
+ * protection of the channel inbound_lock. Thus, checking
+ * and invoking the driver specific callback takes care of
+ * orderly unloading of the driver.
+ */
+
+ spin_lock_irqsave(&channel->inbound_lock, flags);
+ if (channel->onchannel_callback != NULL)
+ channel->onchannel_callback(channel->channel_callback_context);
+ else
+ pr_err("no channel callback for relid - %u\n", relid);
+
+ spin_unlock_irqrestore(&channel->inbound_lock, flags);
}
/*
@@ -286,16 +260,17 @@ void vmbus_on_event(unsigned long data)
if (!recv_int_page[dword])
continue;
for (bit = 0; bit < 32; bit++) {
- if (sync_test_and_clear_bit(bit, (unsigned long *)&recv_int_page[dword])) {
+ if (sync_test_and_clear_bit(bit,
+ (unsigned long *)&recv_int_page[dword])) {
relid = (dword << 5) + bit;
- if (relid == 0) {
+ if (relid == 0)
/*
* Special case - vmbus
* channel protocol msg
*/
continue;
- }
+
process_chn_event(relid);
}
}
@@ -308,10 +283,25 @@ void vmbus_on_event(unsigned long data)
int vmbus_post_msg(void *buffer, size_t buflen)
{
union hv_connection_id conn_id;
+ int ret = 0;
+ int retries = 0;
conn_id.asu32 = 0;
conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID;
- return hv_post_message(conn_id, 1, buffer, buflen);
+
+ /*
+ * hv_post_message() can have transient failures because of
+ * insufficient resources. Retry the operation a couple of
+ * times before giving up.
+ */
+ while (retries < 3) {
+ ret = hv_post_message(conn_id, 1, buffer, buflen);
+ if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
+ return ret;
+ retries++;
+ msleep(100);
+ }
+ return ret;
}
/*
diff --git a/drivers/staging/hv/hv.c b/drivers/hv/hv.c
index a2cc0911de58..0fb100ed91a3 100644
--- a/drivers/staging/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -25,8 +25,8 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
-
-#include "hyperv.h"
+#include <linux/hyperv.h>
+#include <asm/hyperv.h>
#include "hyperv_vmbus.h"
/* The one and only */
@@ -111,7 +111,7 @@ static u64 do_hypercall(u64 control, void *input, void *output)
u64 hv_status = 0;
u64 input_address = (input) ? virt_to_phys(input) : 0;
u64 output_address = (output) ? virt_to_phys(output) : 0;
- volatile void *hypercall_page = hv_context.hypercall_page;
+ void *hypercall_page = hv_context.hypercall_page;
__asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8");
__asm__ __volatile__("call *%3" : "=a" (hv_status) :
@@ -132,7 +132,7 @@ static u64 do_hypercall(u64 control, void *input, void *output)
u64 output_address = (output) ? virt_to_phys(output) : 0;
u32 output_address_hi = output_address >> 32;
u32 output_address_lo = output_address & 0xFFFFFFFF;
- volatile void *hypercall_page = hv_context.hypercall_page;
+ void *hypercall_page = hv_context.hypercall_page;
__asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi),
"=a"(hv_status_lo) : "d" (control_hi),
@@ -151,7 +151,6 @@ static u64 do_hypercall(u64 control, void *input, void *output)
*/
int hv_init(void)
{
- int ret = 0;
int max_leaf;
union hv_x64_msr_hypercall_contents hypercall_msr;
void *virtaddr = NULL;
@@ -164,11 +163,7 @@ int hv_init(void)
goto cleanup;
max_leaf = query_hypervisor_info();
- /* HvQueryHypervisorFeatures(maxLeaf); */
- /*
- * We only support running on top of Hyper-V
- */
rdmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid);
if (hv_context.guestid != 0)
@@ -181,10 +176,6 @@ int hv_init(void)
/* See if the hypercall page is already set */
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
- /*
- * Allocate the hypercall page memory
- * virtaddr = osd_page_alloc(1);
- */
virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC);
if (!virtaddr)
@@ -222,7 +213,7 @@ int hv_init(void)
hv_context.signal_event_param->flag_number = 0;
hv_context.signal_event_param->rsvdz = 0;
- return ret;
+ return 0;
cleanup:
if (virtaddr) {
@@ -233,8 +224,8 @@ cleanup:
vfree(virtaddr);
}
- ret = -1;
- return ret;
+
+ return -ENOTSUPP;
}
/*
@@ -277,11 +268,11 @@ u16 hv_post_message(union hv_connection_id connection_id,
unsigned long addr;
if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
- return -1;
+ return -EMSGSIZE;
addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC);
if (!addr)
- return -1;
+ return -ENOMEM;
aligned_msg = (struct hv_input_post_message *)
(ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN));
@@ -378,7 +369,7 @@ void hv_synic_init(void *irqarg)
shared_sint.as_uint64 = 0;
shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
shared_sint.masked = false;
- shared_sint.auto_eoi = true;
+ shared_sint.auto_eoi = false;
wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
diff --git a/drivers/staging/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index 13b0ecf7d5d6..89f52440fcf4 100644
--- a/drivers/staging/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -26,8 +26,8 @@
#include <linux/nls.h>
#include <linux/connector.h>
#include <linux/workqueue.h>
+#include <linux/hyperv.h>
-#include "hyperv.h"
#include "hv_kvp.h"
@@ -44,21 +44,24 @@
static struct {
bool active; /* transaction status - active or not */
int recv_len; /* number of bytes received. */
+ int index; /* current index */
struct vmbus_channel *recv_channel; /* chn we got the request */
u64 recv_req_id; /* request ID. */
} kvp_transaction;
-static int kvp_send_key(int index);
+static void kvp_send_key(struct work_struct *dummy);
+
+#define TIMEOUT_FIRED 1
static void kvp_respond_to_host(char *key, char *value, int error);
static void kvp_work_func(struct work_struct *dummy);
static void kvp_register(void);
static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func);
+static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL };
static const char kvp_name[] = "kvp_kernel_module";
-static int timeout_fired;
static u8 *recv_buffer;
/*
* Register the kernel component with the user-level daemon.
@@ -90,8 +93,7 @@ kvp_work_func(struct work_struct *dummy)
* If the timer fires, the user-mode component has not responded;
* process the pending transaction.
*/
- kvp_respond_to_host("Unknown key", "Guest timed out", timeout_fired);
- timeout_fired = 1;
+ kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED);
}
/*
@@ -121,10 +123,11 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
}
}
-static int
-kvp_send_key(int index)
+static void
+kvp_send_key(struct work_struct *dummy)
{
struct cn_msg *msg;
+ int index = kvp_transaction.index;
msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC);
@@ -136,9 +139,8 @@ kvp_send_key(int index)
msg->len = sizeof(struct hv_ku_msg);
cn_netlink_send(msg, 0, GFP_ATOMIC);
kfree(msg);
- return 0;
}
- return 1;
+ return;
}
/*
@@ -177,6 +179,15 @@ kvp_respond_to_host(char *key, char *value, int error)
channel = kvp_transaction.recv_channel;
req_id = kvp_transaction.recv_req_id;
+ kvp_transaction.active = false;
+
+ if (channel->onchannel_callback == NULL)
+ /*
+ * We have raced with util driver being unloaded;
+ * silently return.
+ */
+ return;
+
icmsghdrp = (struct icmsg_hdr *)
&recv_buffer[sizeof(struct vmbuspipe_hdr)];
kvp_msg = (struct hv_kvp_msg *)
@@ -217,7 +228,6 @@ response_done:
vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
VM_PKT_DATA_INBAND, 0);
- kvp_transaction.active = false;
}
/*
@@ -243,10 +253,6 @@ void hv_kvp_onchannelcallback(void *context)
struct icmsg_negotiate *negop = NULL;
- if (kvp_transaction.active)
- return;
-
-
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid);
if (recvlen > 0) {
@@ -254,7 +260,7 @@ void hv_kvp_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
- prep_negotiate_resp(icmsghdrp, negop, recv_buffer);
+ vmbus_prep_negotiate_resp(icmsghdrp, negop, recv_buffer);
} else {
kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
@@ -282,6 +288,7 @@ void hv_kvp_onchannelcallback(void *context)
kvp_transaction.recv_channel = channel;
kvp_transaction.recv_req_id = requestid;
kvp_transaction.active = true;
+ kvp_transaction.index = kvp_data->index;
/*
* Get the information from the
@@ -292,8 +299,8 @@ void hv_kvp_onchannelcallback(void *context)
* Set a timeout to deal with
* user-mode not responding.
*/
- kvp_send_key(kvp_data->index);
- schedule_delayed_work(&kvp_work, 100);
+ schedule_work(&kvp_sendkey_work);
+ schedule_delayed_work(&kvp_work, 5*HZ);
return;
@@ -312,16 +319,14 @@ callback_done:
}
int
-hv_kvp_init(void)
+hv_kvp_init(struct hv_util_service *srv)
{
int err;
err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback);
if (err)
return err;
- recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!recv_buffer)
- return -ENOMEM;
+ recv_buffer = srv->recv_buffer;
return 0;
}
@@ -330,5 +335,5 @@ void hv_kvp_deinit(void)
{
cn_del_callback(&kvp_id);
cancel_delayed_work_sync(&kvp_work);
- kfree(recv_buffer);
+ cancel_work_sync(&kvp_sendkey_work);
}
diff --git a/drivers/staging/hv/hv_kvp.h b/drivers/hv/hv_kvp.h
index 8c402f357d37..9b765d7df838 100644
--- a/drivers/staging/hv/hv_kvp.h
+++ b/drivers/hv/hv_kvp.h
@@ -175,7 +175,7 @@ struct hv_kvp_msg {
struct hv_kvp_msg_enumerate kvp_data;
};
-int hv_kvp_init(void);
+int hv_kvp_init(struct hv_util_service *);
void hv_kvp_deinit(void);
void hv_kvp_onchannelcallback(void *);
diff --git a/drivers/staging/hv/hv_util.c b/drivers/hv/hv_util.c
index c164b54b4cd7..55d58f21e6d4 100644
--- a/drivers/staging/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -26,15 +26,31 @@
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/reboot.h>
-#include <linux/dmi.h>
-#include <linux/pci.h>
+#include <linux/hyperv.h>
-#include "hyperv.h"
#include "hv_kvp.h"
-static u8 *shut_txf_buf;
-static u8 *time_txf_buf;
-static u8 *hbeat_txf_buf;
+
+static void shutdown_onchannelcallback(void *context);
+static struct hv_util_service util_shutdown = {
+ .util_cb = shutdown_onchannelcallback,
+};
+
+static void timesync_onchannelcallback(void *context);
+static struct hv_util_service util_timesynch = {
+ .util_cb = timesync_onchannelcallback,
+};
+
+static void heartbeat_onchannelcallback(void *context);
+static struct hv_util_service util_heartbeat = {
+ .util_cb = heartbeat_onchannelcallback,
+};
+
+static struct hv_util_service util_kvp = {
+ .util_cb = hv_kvp_onchannelcallback,
+ .util_init = hv_kvp_init,
+ .util_deinit = hv_kvp_deinit,
+};
static void shutdown_onchannelcallback(void *context)
{
@@ -42,6 +58,7 @@ static void shutdown_onchannelcallback(void *context)
u32 recvlen;
u64 requestid;
u8 execute_shutdown = false;
+ u8 *shut_txf_buf = util_shutdown.recv_buffer;
struct shutdown_msg_data *shutdown_msg;
@@ -56,7 +73,7 @@ static void shutdown_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
- prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf);
+ vmbus_prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf);
} else {
shutdown_msg =
(struct shutdown_msg_data *)&shut_txf_buf[
@@ -91,7 +108,7 @@ static void shutdown_onchannelcallback(void *context)
}
if (execute_shutdown == true)
- orderly_poweroff(false);
+ orderly_poweroff(true);
}
/*
@@ -109,6 +126,24 @@ static inline void do_adj_guesttime(u64 hosttime)
}
/*
+ * Set the host time in a process context.
+ */
+
+struct adj_time_work {
+ struct work_struct work;
+ u64 host_time;
+};
+
+static void hv_set_host_time(struct work_struct *work)
+{
+ struct adj_time_work *wrk;
+
+ wrk = container_of(work, struct adj_time_work, work);
+ do_adj_guesttime(wrk->host_time);
+ kfree(wrk);
+}
+
+/*
* Synchronize time with host after reboot, restore, etc.
*
* ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
@@ -121,17 +156,26 @@ static inline void do_adj_guesttime(u64 hosttime)
*/
static inline void adj_guesttime(u64 hosttime, u8 flags)
{
+ struct adj_time_work *wrk;
static s32 scnt = 50;
+ wrk = kmalloc(sizeof(struct adj_time_work), GFP_ATOMIC);
+ if (wrk == NULL)
+ return;
+
+ wrk->host_time = hosttime;
if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
- do_adj_guesttime(hosttime);
+ INIT_WORK(&wrk->work, hv_set_host_time);
+ schedule_work(&wrk->work);
return;
}
if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
scnt--;
- do_adj_guesttime(hosttime);
- }
+ INIT_WORK(&wrk->work, hv_set_host_time);
+ schedule_work(&wrk->work);
+ } else
+ kfree(wrk);
}
/*
@@ -144,6 +188,7 @@ static void timesync_onchannelcallback(void *context)
u64 requestid;
struct icmsg_hdr *icmsghdrp;
struct ictimesync_data *timedatap;
+ u8 *time_txf_buf = util_timesynch.recv_buffer;
vmbus_recvpacket(channel, time_txf_buf,
PAGE_SIZE, &recvlen, &requestid);
@@ -153,7 +198,7 @@ static void timesync_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
- prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf);
+ vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf);
} else {
timedatap = (struct ictimesync_data *)&time_txf_buf[
sizeof(struct vmbuspipe_hdr) +
@@ -182,6 +227,7 @@ static void heartbeat_onchannelcallback(void *context)
u64 requestid;
struct icmsg_hdr *icmsghdrp;
struct heartbeat_msg_data *heartbeat_msg;
+ u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
vmbus_recvpacket(channel, hbeat_txf_buf,
PAGE_SIZE, &recvlen, &requestid);
@@ -191,7 +237,7 @@ static void heartbeat_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
- prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf);
+ vmbus_prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf);
} else {
heartbeat_msg =
(struct heartbeat_msg_data *)&hbeat_txf_buf[
@@ -210,92 +256,94 @@ static void heartbeat_onchannelcallback(void *context)
}
}
-static const struct pci_device_id __initconst
-hv_utils_pci_table[] __maybe_unused = {
- { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
-
-
-static const struct dmi_system_id __initconst
-hv_utils_dmi_table[] __maybe_unused = {
- {
- .ident = "Hyper-V",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
- DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
- },
- },
- { },
-};
-MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
-
-
-static int __init init_hyperv_utils(void)
+static int util_probe(struct hv_device *dev,
+ const struct hv_vmbus_device_id *dev_id)
{
- pr_info("Registering HyperV Utility Driver\n");
-
- if (hv_kvp_init())
- return -ENODEV;
-
-
- if (!dmi_check_system(hv_utils_dmi_table))
- return -ENODEV;
-
- shut_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- time_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- hbeat_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ struct hv_util_service *srv =
+ (struct hv_util_service *)dev_id->driver_data;
+ int ret;
- if (!shut_txf_buf || !time_txf_buf || !hbeat_txf_buf) {
- pr_info("Unable to allocate memory for receive buffer\n");
- kfree(shut_txf_buf);
- kfree(time_txf_buf);
- kfree(hbeat_txf_buf);
+ srv->recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!srv->recv_buffer)
return -ENOMEM;
+ if (srv->util_init) {
+ ret = srv->util_init(srv);
+ if (ret) {
+ ret = -ENODEV;
+ goto error1;
+ }
}
- hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback;
+ ret = vmbus_open(dev->channel, 2 * PAGE_SIZE, 2 * PAGE_SIZE, NULL, 0,
+ srv->util_cb, dev->channel);
+ if (ret)
+ goto error;
+
+ hv_set_drvdata(dev, srv);
+ return 0;
- hv_cb_utils[HV_TIMESYNC_MSG].callback = &timesync_onchannelcallback;
+error:
+ if (srv->util_deinit)
+ srv->util_deinit();
+error1:
+ kfree(srv->recv_buffer);
+ return ret;
+}
- hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback;
+static int util_remove(struct hv_device *dev)
+{
+ struct hv_util_service *srv = hv_get_drvdata(dev);
- hv_cb_utils[HV_KVP_MSG].callback = &hv_kvp_onchannelcallback;
+ vmbus_close(dev->channel);
+ if (srv->util_deinit)
+ srv->util_deinit();
+ kfree(srv->recv_buffer);
return 0;
}
-static void exit_hyperv_utils(void)
-{
- pr_info("De-Registered HyperV Utility Driver\n");
+static const struct hv_vmbus_device_id id_table[] = {
+ /* Shutdown guid */
+ { VMBUS_DEVICE(0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49,
+ 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB)
+ .driver_data = (unsigned long)&util_shutdown },
+ /* Time synch guid */
+ { VMBUS_DEVICE(0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49,
+ 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf)
+ .driver_data = (unsigned long)&util_timesynch },
+ /* Heartbeat guid */
+ { VMBUS_DEVICE(0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e,
+ 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d)
+ .driver_data = (unsigned long)&util_heartbeat },
+ /* KVP guid */
+ { VMBUS_DEVICE(0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d,
+ 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6)
+ .driver_data = (unsigned long)&util_kvp },
+ { },
+};
- if (hv_cb_utils[HV_SHUTDOWN_MSG].channel != NULL)
- hv_cb_utils[HV_SHUTDOWN_MSG].channel->onchannel_callback =
- &chn_cb_negotiate;
- hv_cb_utils[HV_SHUTDOWN_MSG].callback = NULL;
+MODULE_DEVICE_TABLE(vmbus, id_table);
- if (hv_cb_utils[HV_TIMESYNC_MSG].channel != NULL)
- hv_cb_utils[HV_TIMESYNC_MSG].channel->onchannel_callback =
- &chn_cb_negotiate;
- hv_cb_utils[HV_TIMESYNC_MSG].callback = NULL;
+/* The one and only one */
+static struct hv_driver util_drv = {
+ .name = "hv_util",
+ .id_table = id_table,
+ .probe = util_probe,
+ .remove = util_remove,
+};
- if (hv_cb_utils[HV_HEARTBEAT_MSG].channel != NULL)
- hv_cb_utils[HV_HEARTBEAT_MSG].channel->onchannel_callback =
- &chn_cb_negotiate;
- hv_cb_utils[HV_HEARTBEAT_MSG].callback = NULL;
+static int __init init_hyperv_utils(void)
+{
+ pr_info("Registering HyperV Utility Driver\n");
- if (hv_cb_utils[HV_KVP_MSG].channel != NULL)
- hv_cb_utils[HV_KVP_MSG].channel->onchannel_callback =
- &chn_cb_negotiate;
- hv_cb_utils[HV_KVP_MSG].callback = NULL;
+ return vmbus_driver_register(&util_drv);
+}
- hv_kvp_deinit();
+static void exit_hyperv_utils(void)
+{
+ pr_info("De-Registered HyperV Utility Driver\n");
- kfree(shut_txf_buf);
- kfree(time_txf_buf);
- kfree(hbeat_txf_buf);
+ vmbus_driver_unregister(&util_drv);
}
module_init(init_hyperv_utils);
diff --git a/drivers/staging/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index bf30a425b643..0aee1122734c 100644
--- a/drivers/staging/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -28,8 +28,7 @@
#include <linux/list.h>
#include <asm/sync_bitops.h>
#include <linux/atomic.h>
-
-#include "hyperv.h"
+#include <linux/hyperv.h>
/*
* The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent
@@ -451,8 +450,8 @@ enum {
/* #define VMBUS_PORT_ID 11 */
/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */
-static const struct hv_guid VMBUS_SERVICE_ID = {
- .data = {
+static const uuid_le VMBUS_SERVICE_ID = {
+ .b = {
0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c,
0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4
},
@@ -530,8 +529,6 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info,
u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info);
-void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix);
-
void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info);
@@ -601,12 +598,12 @@ extern struct vmbus_connection vmbus_connection;
/* General vmbus interface */
-struct hv_device *vmbus_child_device_create(struct hv_guid *type,
- struct hv_guid *instance,
+struct hv_device *vmbus_device_create(uuid_le *type,
+ uuid_le *instance,
struct vmbus_channel *channel);
-int vmbus_child_device_register(struct hv_device *child_device_obj);
-void vmbus_child_device_unregister(struct hv_device *device_obj);
+int vmbus_device_register(struct hv_device *child_device_obj);
+void vmbus_device_unregister(struct hv_device *device_obj);
/* static void */
/* VmbusChildDeviceDestroy( */
@@ -619,8 +616,6 @@ struct vmbus_channel *relid2channel(u32 relid);
int vmbus_connect(void);
-int vmbus_disconnect(void);
-
int vmbus_post_msg(void *buffer, size_t buflen);
int vmbus_set_event(u32 child_relid);
diff --git a/drivers/staging/hv/ring_buffer.c b/drivers/hv/ring_buffer.c
index 3da333018b5a..8af25a097d75 100644
--- a/drivers/staging/hv/ring_buffer.c
+++ b/drivers/hv/ring_buffer.c
@@ -25,8 +25,8 @@
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/hyperv.h>
-#include "hyperv.h"
#include "hyperv_vmbus.h"
@@ -34,7 +34,8 @@
/* Amount of space to write to */
-#define BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w))
+#define BYTES_AVAIL_TO_WRITE(r, w, z) \
+ ((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w))
/*
@@ -50,6 +51,8 @@ hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi,
{
u32 read_loc, write_loc;
+ smp_read_barrier_depends();
+
/* Capture the read/write indices before they changed */
read_loc = rbi->ring_buffer->read_index;
write_loc = rbi->ring_buffer->write_index;
@@ -169,37 +172,6 @@ hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info)
return (u64)ring_info->ring_buffer->write_index << 32;
}
-
-/*
- *
- * hv_dump_ring_info()
- *
- * Dump out to console the ring buffer info
- *
- */
-void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix)
-{
- u32 bytes_avail_towrite;
- u32 bytes_avail_toread;
-
- hv_get_ringbuffer_availbytes(ring_info,
- &bytes_avail_toread,
- &bytes_avail_towrite);
-
- DPRINT(VMBUS,
- DEBUG_RING_LVL,
- "%s <<ringinfo %p buffer %p avail write %u "
- "avail read %u read idx %u write idx %u>>",
- prefix,
- ring_info,
- ring_info->ring_buffer->buffer,
- bytes_avail_towrite,
- bytes_avail_toread,
- ring_info->ring_buffer->read_index,
- ring_info->ring_buffer->write_index);
-}
-
-
/*
*
* hv_copyfrom_ringbuffer()
@@ -388,7 +360,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
/* is empty since the read index == write index */
if (bytes_avail_towrite <= totalbytes_towrite) {
spin_unlock_irqrestore(&outring_info->ring_lock, flags);
- return -1;
+ return -EAGAIN;
}
/* Write to the ring buffer */
@@ -411,7 +383,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
sizeof(u64));
/* Make sure we flush all writes before updating the writeIndex */
- mb();
+ smp_wmb();
/* Now, update the write location */
hv_set_next_write_location(outring_info, next_write_location);
@@ -448,7 +420,7 @@ int hv_ringbuffer_peek(struct hv_ring_buffer_info *Inring_info,
spin_unlock_irqrestore(&Inring_info->ring_lock, flags);
- return -1;
+ return -EAGAIN;
}
/* Convert to byte offset */
@@ -494,7 +466,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer,
if (bytes_avail_toread < buflen) {
spin_unlock_irqrestore(&inring_info->ring_lock, flags);
- return -1;
+ return -EAGAIN;
}
next_read_location =
@@ -513,7 +485,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer,
/* Make sure all reads are done before we update the read index since */
/* the writer may start writing to the read area once the read index */
/*is updated */
- mb();
+ smp_mb();
/* Update the read index */
hv_set_next_read_location(inring_info, next_read_location);
diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index ec1d38cd481c..0c048dd8013f 100644
--- a/drivers/staging/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -28,31 +28,41 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/sysctl.h>
-#include <linux/pci.h>
-#include <linux/dmi.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
#include <linux/completion.h>
-
-#include "hyperv.h"
+#include <linux/hyperv.h>
+#include <asm/hyperv.h>
#include "hyperv_vmbus.h"
-static struct pci_dev *hv_pci_dev;
+static struct acpi_device *hv_acpi_dev;
static struct tasklet_struct msg_dpc;
static struct tasklet_struct event_dpc;
-
-unsigned int vmbus_loglevel = (ALL_MODULES << 16 | INFO_LVL);
-EXPORT_SYMBOL(vmbus_loglevel);
- /* (ALL_MODULES << 16 | DEBUG_LVL_ENTEREXIT); */
- /* (((VMBUS | VMBUS_DRV)<<16) | DEBUG_LVL_ENTEREXIT); */
-
-static int pci_probe_error;
static struct completion probe_event;
static int irq;
+struct hv_device_info {
+ u32 chn_id;
+ u32 chn_state;
+ uuid_le chn_type;
+ uuid_le chn_instance;
+
+ u32 monitor_id;
+ u32 server_monitor_pending;
+ u32 server_monitor_latency;
+ u32 server_monitor_conn_id;
+ u32 client_monitor_pending;
+ u32 client_monitor_latency;
+ u32 client_monitor_conn_id;
+
+ struct hv_dev_port_info inbound;
+ struct hv_dev_port_info outbound;
+};
+
+
static void get_channel_info(struct hv_device *device,
struct hv_device_info *info)
{
@@ -66,9 +76,9 @@ static void get_channel_info(struct hv_device *device,
info->chn_id = debug_info.relid;
info->chn_state = debug_info.state;
memcpy(&info->chn_type, &debug_info.interfacetype,
- sizeof(struct hv_guid));
+ sizeof(uuid_le));
memcpy(&info->chn_instance, &debug_info.interface_instance,
- sizeof(struct hv_guid));
+ sizeof(uuid_le));
info->monitor_id = debug_info.monitorid;
@@ -98,6 +108,14 @@ static void get_channel_info(struct hv_device *device,
debug_info.outbound.bytes_avail_towrite;
}
+#define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2)
+static void print_alias_name(struct hv_device *hv_dev, char *alias_name)
+{
+ int i;
+ for (i = 0; i < VMBUS_ALIAS_LEN; i += 2)
+ sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]);
+}
+
/*
* vmbus_show_device_attr - Show the device attribute in sysfs.
*
@@ -108,98 +126,106 @@ static ssize_t vmbus_show_device_attr(struct device *dev,
struct device_attribute *dev_attr,
char *buf)
{
- struct hv_device *device_ctx = device_to_hv_device(dev);
- struct hv_device_info device_info;
+ struct hv_device *hv_dev = device_to_hv_device(dev);
+ struct hv_device_info *device_info;
+ char alias_name[VMBUS_ALIAS_LEN + 1];
+ int ret = 0;
- memset(&device_info, 0, sizeof(struct hv_device_info));
+ device_info = kzalloc(sizeof(struct hv_device_info), GFP_KERNEL);
+ if (!device_info)
+ return ret;
- get_channel_info(device_ctx, &device_info);
+ get_channel_info(hv_dev, device_info);
if (!strcmp(dev_attr->attr.name, "class_id")) {
- return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x%02x%02x%02x%02x%02x%02x}\n",
- device_info.chn_type.data[3],
- device_info.chn_type.data[2],
- device_info.chn_type.data[1],
- device_info.chn_type.data[0],
- device_info.chn_type.data[5],
- device_info.chn_type.data[4],
- device_info.chn_type.data[7],
- device_info.chn_type.data[6],
- device_info.chn_type.data[8],
- device_info.chn_type.data[9],
- device_info.chn_type.data[10],
- device_info.chn_type.data[11],
- device_info.chn_type.data[12],
- device_info.chn_type.data[13],
- device_info.chn_type.data[14],
- device_info.chn_type.data[15]);
+ device_info->chn_type.b[3],
+ device_info->chn_type.b[2],
+ device_info->chn_type.b[1],
+ device_info->chn_type.b[0],
+ device_info->chn_type.b[5],
+ device_info->chn_type.b[4],
+ device_info->chn_type.b[7],
+ device_info->chn_type.b[6],
+ device_info->chn_type.b[8],
+ device_info->chn_type.b[9],
+ device_info->chn_type.b[10],
+ device_info->chn_type.b[11],
+ device_info->chn_type.b[12],
+ device_info->chn_type.b[13],
+ device_info->chn_type.b[14],
+ device_info->chn_type.b[15]);
} else if (!strcmp(dev_attr->attr.name, "device_id")) {
- return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x%02x%02x%02x%02x%02x%02x}\n",
- device_info.chn_instance.data[3],
- device_info.chn_instance.data[2],
- device_info.chn_instance.data[1],
- device_info.chn_instance.data[0],
- device_info.chn_instance.data[5],
- device_info.chn_instance.data[4],
- device_info.chn_instance.data[7],
- device_info.chn_instance.data[6],
- device_info.chn_instance.data[8],
- device_info.chn_instance.data[9],
- device_info.chn_instance.data[10],
- device_info.chn_instance.data[11],
- device_info.chn_instance.data[12],
- device_info.chn_instance.data[13],
- device_info.chn_instance.data[14],
- device_info.chn_instance.data[15]);
+ device_info->chn_instance.b[3],
+ device_info->chn_instance.b[2],
+ device_info->chn_instance.b[1],
+ device_info->chn_instance.b[0],
+ device_info->chn_instance.b[5],
+ device_info->chn_instance.b[4],
+ device_info->chn_instance.b[7],
+ device_info->chn_instance.b[6],
+ device_info->chn_instance.b[8],
+ device_info->chn_instance.b[9],
+ device_info->chn_instance.b[10],
+ device_info->chn_instance.b[11],
+ device_info->chn_instance.b[12],
+ device_info->chn_instance.b[13],
+ device_info->chn_instance.b[14],
+ device_info->chn_instance.b[15]);
+ } else if (!strcmp(dev_attr->attr.name, "modalias")) {
+ print_alias_name(hv_dev, alias_name);
+ ret = sprintf(buf, "vmbus:%s\n", alias_name);
} else if (!strcmp(dev_attr->attr.name, "state")) {
- return sprintf(buf, "%d\n", device_info.chn_state);
+ ret = sprintf(buf, "%d\n", device_info->chn_state);
} else if (!strcmp(dev_attr->attr.name, "id")) {
- return sprintf(buf, "%d\n", device_info.chn_id);
+ ret = sprintf(buf, "%d\n", device_info->chn_id);
} else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) {
- return sprintf(buf, "%d\n", device_info.outbound.int_mask);
+ ret = sprintf(buf, "%d\n", device_info->outbound.int_mask);
} else if (!strcmp(dev_attr->attr.name, "out_read_index")) {
- return sprintf(buf, "%d\n", device_info.outbound.read_idx);
+ ret = sprintf(buf, "%d\n", device_info->outbound.read_idx);
} else if (!strcmp(dev_attr->attr.name, "out_write_index")) {
- return sprintf(buf, "%d\n", device_info.outbound.write_idx);
+ ret = sprintf(buf, "%d\n", device_info->outbound.write_idx);
} else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) {
- return sprintf(buf, "%d\n",
- device_info.outbound.bytes_avail_toread);
+ ret = sprintf(buf, "%d\n",
+ device_info->outbound.bytes_avail_toread);
} else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) {
- return sprintf(buf, "%d\n",
- device_info.outbound.bytes_avail_towrite);
+ ret = sprintf(buf, "%d\n",
+ device_info->outbound.bytes_avail_towrite);
} else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) {
- return sprintf(buf, "%d\n", device_info.inbound.int_mask);
+ ret = sprintf(buf, "%d\n", device_info->inbound.int_mask);
} else if (!strcmp(dev_attr->attr.name, "in_read_index")) {
- return sprintf(buf, "%d\n", device_info.inbound.read_idx);
+ ret = sprintf(buf, "%d\n", device_info->inbound.read_idx);
} else if (!strcmp(dev_attr->attr.name, "in_write_index")) {
- return sprintf(buf, "%d\n", device_info.inbound.write_idx);
+ ret = sprintf(buf, "%d\n", device_info->inbound.write_idx);
} else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) {
- return sprintf(buf, "%d\n",
- device_info.inbound.bytes_avail_toread);
+ ret = sprintf(buf, "%d\n",
+ device_info->inbound.bytes_avail_toread);
} else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) {
- return sprintf(buf, "%d\n",
- device_info.inbound.bytes_avail_towrite);
+ ret = sprintf(buf, "%d\n",
+ device_info->inbound.bytes_avail_towrite);
} else if (!strcmp(dev_attr->attr.name, "monitor_id")) {
- return sprintf(buf, "%d\n", device_info.monitor_id);
+ ret = sprintf(buf, "%d\n", device_info->monitor_id);
} else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) {
- return sprintf(buf, "%d\n", device_info.server_monitor_pending);
+ ret = sprintf(buf, "%d\n", device_info->server_monitor_pending);
} else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) {
- return sprintf(buf, "%d\n", device_info.server_monitor_latency);
+ ret = sprintf(buf, "%d\n", device_info->server_monitor_latency);
} else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) {
- return sprintf(buf, "%d\n",
- device_info.server_monitor_conn_id);
+ ret = sprintf(buf, "%d\n",
+ device_info->server_monitor_conn_id);
} else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) {
- return sprintf(buf, "%d\n", device_info.client_monitor_pending);
+ ret = sprintf(buf, "%d\n", device_info->client_monitor_pending);
} else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) {
- return sprintf(buf, "%d\n", device_info.client_monitor_latency);
+ ret = sprintf(buf, "%d\n", device_info->client_monitor_latency);
} else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) {
- return sprintf(buf, "%d\n",
- device_info.client_monitor_conn_id);
- } else {
- return 0;
+ ret = sprintf(buf, "%d\n",
+ device_info->client_monitor_conn_id);
}
+
+ kfree(device_info);
+ return ret;
}
/* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */
@@ -209,6 +235,7 @@ static struct device_attribute vmbus_device_attrs[] = {
__ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(modalias, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
@@ -239,76 +266,60 @@ static struct device_attribute vmbus_device_attrs[] = {
* This routine is invoked when a device is added or removed on the vmbus to
* generate a uevent to udev in the userspace. The udev will then look at its
* rule and the uevent generated here to load the appropriate driver
+ *
+ * The alias string will be of the form vmbus:guid where guid is the string
+ * representation of the device guid (each byte of the guid will be
+ * represented with two hex characters.
*/
static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env)
{
struct hv_device *dev = device_to_hv_device(device);
int ret;
+ char alias_name[VMBUS_ALIAS_LEN + 1];
+
+ print_alias_name(dev, alias_name);
+ ret = add_uevent_var(env, "MODALIAS=vmbus:%s", alias_name);
+ return ret;
+}
- ret = add_uevent_var(env, "VMBUS_DEVICE_CLASS_GUID={"
- "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
- "%02x%02x%02x%02x%02x%02x%02x%02x}",
- dev->dev_type.data[3],
- dev->dev_type.data[2],
- dev->dev_type.data[1],
- dev->dev_type.data[0],
- dev->dev_type.data[5],
- dev->dev_type.data[4],
- dev->dev_type.data[7],
- dev->dev_type.data[6],
- dev->dev_type.data[8],
- dev->dev_type.data[9],
- dev->dev_type.data[10],
- dev->dev_type.data[11],
- dev->dev_type.data[12],
- dev->dev_type.data[13],
- dev->dev_type.data[14],
- dev->dev_type.data[15]);
+static uuid_le null_guid;
- if (ret)
- return ret;
+static inline bool is_null_guid(const __u8 *guid)
+{
+ if (memcmp(guid, &null_guid, sizeof(uuid_le)))
+ return false;
+ return true;
+}
- ret = add_uevent_var(env, "VMBUS_DEVICE_DEVICE_GUID={"
- "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
- "%02x%02x%02x%02x%02x%02x%02x%02x}",
- dev->dev_instance.data[3],
- dev->dev_instance.data[2],
- dev->dev_instance.data[1],
- dev->dev_instance.data[0],
- dev->dev_instance.data[5],
- dev->dev_instance.data[4],
- dev->dev_instance.data[7],
- dev->dev_instance.data[6],
- dev->dev_instance.data[8],
- dev->dev_instance.data[9],
- dev->dev_instance.data[10],
- dev->dev_instance.data[11],
- dev->dev_instance.data[12],
- dev->dev_instance.data[13],
- dev->dev_instance.data[14],
- dev->dev_instance.data[15]);
- if (ret)
- return ret;
+/*
+ * Return a matching hv_vmbus_device_id pointer.
+ * If there is no match, return NULL.
+ */
+static const struct hv_vmbus_device_id *hv_vmbus_get_id(
+ const struct hv_vmbus_device_id *id,
+ __u8 *guid)
+{
+ for (; !is_null_guid(id->guid); id++)
+ if (!memcmp(&id->guid, guid, sizeof(uuid_le)))
+ return id;
- return 0;
+ return NULL;
}
+
/*
* vmbus_match - Attempt to match the specified device to the specified driver
*/
static int vmbus_match(struct device *device, struct device_driver *driver)
{
- int match = 0;
struct hv_driver *drv = drv_to_hv_drv(driver);
- struct hv_device *device_ctx = device_to_hv_device(device);
+ struct hv_device *hv_dev = device_to_hv_device(device);
- /* We found our driver ? */
- if (memcmp(&device_ctx->dev_type, &drv->dev_type,
- sizeof(struct hv_guid)) == 0)
- match = 1;
+ if (hv_vmbus_get_id(drv->id_table, hv_dev->dev_type.b))
+ return 1;
- return match;
+ return 0;
}
/*
@@ -320,9 +331,11 @@ static int vmbus_probe(struct device *child_device)
struct hv_driver *drv =
drv_to_hv_drv(child_device->driver);
struct hv_device *dev = device_to_hv_device(child_device);
+ const struct hv_vmbus_device_id *dev_id;
+ dev_id = hv_vmbus_get_id(drv->id_table, dev->dev_type.b);
if (drv->probe) {
- ret = drv->probe(dev);
+ ret = drv->probe(dev, dev_id);
if (ret != 0)
pr_err("probe failed for device %s (%d)\n",
dev_name(child_device), ret);
@@ -330,7 +343,7 @@ static int vmbus_probe(struct device *child_device)
} else {
pr_err("probe not set for driver %s\n",
dev_name(child_device));
- ret = -1;
+ ret = -ENODEV;
}
return ret;
}
@@ -340,22 +353,14 @@ static int vmbus_probe(struct device *child_device)
*/
static int vmbus_remove(struct device *child_device)
{
- int ret;
- struct hv_driver *drv;
-
+ struct hv_driver *drv = drv_to_hv_drv(child_device->driver);
struct hv_device *dev = device_to_hv_device(child_device);
- if (child_device->driver) {
- drv = drv_to_hv_drv(child_device->driver);
-
- if (drv->remove) {
- ret = drv->remove(dev);
- } else {
- pr_err("remove not set for driver %s\n",
- dev_name(child_device));
- ret = -1;
- }
- }
+ if (drv->remove)
+ drv->remove(dev);
+ else
+ pr_err("remove not set for driver %s\n",
+ dev_name(child_device));
return 0;
}
@@ -388,9 +393,9 @@ static void vmbus_shutdown(struct device *child_device)
*/
static void vmbus_device_release(struct device *device)
{
- struct hv_device *device_ctx = device_to_hv_device(device);
+ struct hv_device *hv_dev = device_to_hv_device(device);
- kfree(device_ctx);
+ kfree(hv_dev);
}
@@ -423,9 +428,6 @@ static void vmbus_onmessage_work(struct work_struct *work)
kfree(ctx);
}
-/*
- * vmbus_on_msg_dpc - DPC routine to handle messages from the hypervisior
- */
static void vmbus_on_msg_dpc(unsigned long data)
{
int cpu = smp_processor_id();
@@ -456,7 +458,7 @@ static void vmbus_on_msg_dpc(unsigned long data)
* will not deliver any more messages since there is
* no empty slot
*/
- mb();
+ smp_mb();
if (msg->header.message_flags.msg_pending) {
/*
@@ -469,54 +471,42 @@ static void vmbus_on_msg_dpc(unsigned long data)
}
}
-/*
- * vmbus_on_isr - ISR routine
- */
-static int vmbus_on_isr(void)
+static irqreturn_t vmbus_isr(int irq, void *dev_id)
{
- int ret = 0;
int cpu = smp_processor_id();
void *page_addr;
struct hv_message *msg;
union hv_synic_event_flags *event;
+ bool handled = false;
- page_addr = hv_context.synic_message_page[cpu];
- msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
-
- /* Check if there are actual msgs to be process */
- if (msg->header.message_type != HVMSG_NONE)
- ret |= 0x1;
+ /*
+ * Check for events before checking for messages. This is the order
+ * in which events and messages are checked in Windows guests on
+ * Hyper-V, and the Windows team suggested we do the same.
+ */
- /* TODO: Check if there are events to be process */
page_addr = hv_context.synic_event_page[cpu];
event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT;
/* Since we are a child, we only need to check bit 0 */
- if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0]))
- ret |= 0x2;
-
- return ret;
-}
-
-
-static irqreturn_t vmbus_isr(int irq, void *dev_id)
-{
- int ret;
-
- ret = vmbus_on_isr();
+ if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) {
+ handled = true;
+ tasklet_schedule(&event_dpc);
+ }
- /* Schedules a dpc if necessary */
- if (ret > 0) {
- if (test_bit(0, (unsigned long *)&ret))
- tasklet_schedule(&msg_dpc);
+ page_addr = hv_context.synic_message_page[cpu];
+ msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
- if (test_bit(1, (unsigned long *)&ret))
- tasklet_schedule(&event_dpc);
+ /* Check if there are actual msgs to be processed */
+ if (msg->header.message_type != HVMSG_NONE) {
+ handled = true;
+ tasklet_schedule(&msg_dpc);
+ }
+ if (handled)
return IRQ_HANDLED;
- } else {
+ else
return IRQ_NONE;
- }
}
/*
@@ -528,7 +518,7 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id)
* - get the irq resource
* - retrieve the channel offers
*/
-static int vmbus_bus_init(struct pci_dev *pdev)
+static int vmbus_bus_init(int irq)
{
int ret;
unsigned int vector;
@@ -537,36 +527,26 @@ static int vmbus_bus_init(struct pci_dev *pdev)
ret = hv_init();
if (ret != 0) {
pr_err("Unable to initialize the hypervisor - 0x%x\n", ret);
- goto cleanup;
+ return ret;
}
- /* Initialize the bus context */
tasklet_init(&msg_dpc, vmbus_on_msg_dpc, 0);
tasklet_init(&event_dpc, vmbus_on_event, 0);
- /* Now, register the bus with LDM */
ret = bus_register(&hv_bus);
- if (ret) {
- ret = -1;
- goto cleanup;
- }
+ if (ret)
+ goto err_cleanup;
- /* Get the interrupt resource */
- ret = request_irq(pdev->irq, vmbus_isr,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM,
- driver_name, pdev);
+ ret = request_irq(irq, vmbus_isr, IRQF_SAMPLE_RANDOM,
+ driver_name, hv_acpi_dev);
if (ret != 0) {
pr_err("Unable to request IRQ %d\n",
- pdev->irq);
-
- bus_unregister(&hv_bus);
-
- ret = -1;
- goto cleanup;
+ irq);
+ goto err_unregister;
}
- vector = IRQ0_VECTOR + pdev->irq;
+ vector = IRQ0_VECTOR + irq;
/*
* Notify the hypervisor of our irq and
@@ -574,78 +554,81 @@ static int vmbus_bus_init(struct pci_dev *pdev)
*/
on_each_cpu(hv_synic_init, (void *)&vector, 1);
ret = vmbus_connect();
- if (ret) {
- free_irq(pdev->irq, pdev);
- bus_unregister(&hv_bus);
- goto cleanup;
- }
-
+ if (ret)
+ goto err_irq;
vmbus_request_offers();
-cleanup:
+ return 0;
+
+err_irq:
+ free_irq(irq, hv_acpi_dev);
+
+err_unregister:
+ bus_unregister(&hv_bus);
+
+err_cleanup:
+ hv_cleanup();
+
return ret;
}
/**
- * vmbus_child_driver_register() - Register a vmbus's child driver
- * @drv: Pointer to driver structure you want to register
- *
+ * __vmbus_child_driver_register - Register a vmbus's driver
+ * @drv: Pointer to driver structure you want to register
+ * @owner: owner module of the drv
+ * @mod_name: module name string
*
* Registers the given driver with Linux through the 'driver_register()' call
- * And sets up the hyper-v vmbus handling for this driver.
+ * and sets up the hyper-v vmbus handling for this driver.
* It will return the state of the 'driver_register()' call.
*
- * Mainly used by Hyper-V drivers.
*/
-int vmbus_child_driver_register(struct device_driver *drv)
+int __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, const char *mod_name)
{
int ret;
- pr_info("child driver registering - name %s\n", drv->name);
+ pr_info("registering driver %s\n", hv_driver->name);
- /* The child driver on this vmbus */
- drv->bus = &hv_bus;
+ hv_driver->driver.name = hv_driver->name;
+ hv_driver->driver.owner = owner;
+ hv_driver->driver.mod_name = mod_name;
+ hv_driver->driver.bus = &hv_bus;
- ret = driver_register(drv);
+ ret = driver_register(&hv_driver->driver);
vmbus_request_offers();
return ret;
}
-EXPORT_SYMBOL(vmbus_child_driver_register);
+EXPORT_SYMBOL_GPL(__vmbus_driver_register);
/**
- * vmbus_child_driver_unregister() - Unregister a vmbus's child driver
- * @drv: Pointer to driver structure you want to un-register
- *
+ * vmbus_driver_unregister() - Unregister a vmbus's driver
+ * @drv: Pointer to driver structure you want to un-register
*
- * Un-register the given driver with Linux through the 'driver_unregister()'
- * call. And ungegisters the driver from the Hyper-V vmbus handler.
- *
- * Mainly used by Hyper-V drivers.
+ * Un-register the given driver that was previous registered with a call to
+ * vmbus_driver_register()
*/
-void vmbus_child_driver_unregister(struct device_driver *drv)
+void vmbus_driver_unregister(struct hv_driver *hv_driver)
{
- pr_info("child driver unregistering - name %s\n", drv->name);
+ pr_info("unregistering driver %s\n", hv_driver->name);
- driver_unregister(drv);
+ driver_unregister(&hv_driver->driver);
- drv->bus = NULL;
}
-EXPORT_SYMBOL(vmbus_child_driver_unregister);
+EXPORT_SYMBOL_GPL(vmbus_driver_unregister);
/*
- * vmbus_child_device_create - Creates and registers a new child device
+ * vmbus_device_create - Creates and registers a new child device
* on the vmbus.
*/
-struct hv_device *vmbus_child_device_create(struct hv_guid *type,
- struct hv_guid *instance,
+struct hv_device *vmbus_device_create(uuid_le *type,
+ uuid_le *instance,
struct vmbus_channel *channel)
{
struct hv_device *child_device_obj;
- /* Allocate the new child device */
child_device_obj = kzalloc(sizeof(struct hv_device), GFP_KERNEL);
if (!child_device_obj) {
pr_err("Unable to allocate device object for child device\n");
@@ -653,30 +636,28 @@ struct hv_device *vmbus_child_device_create(struct hv_guid *type,
}
child_device_obj->channel = channel;
- memcpy(&child_device_obj->dev_type, type, sizeof(struct hv_guid));
+ memcpy(&child_device_obj->dev_type, type, sizeof(uuid_le));
memcpy(&child_device_obj->dev_instance, instance,
- sizeof(struct hv_guid));
+ sizeof(uuid_le));
return child_device_obj;
}
/*
- * vmbus_child_device_register - Register the child device
+ * vmbus_device_register - Register the child device
*/
-int vmbus_child_device_register(struct hv_device *child_device_obj)
+int vmbus_device_register(struct hv_device *child_device_obj)
{
int ret = 0;
static atomic_t device_num = ATOMIC_INIT(0);
- /* Set the device name. Otherwise, device_register() will fail. */
dev_set_name(&child_device_obj->device, "vmbus_0_%d",
atomic_inc_return(&device_num));
- /* The new device belongs to this bus */
- child_device_obj->device.bus = &hv_bus; /* device->dev.bus; */
- child_device_obj->device.parent = &hv_pci_dev->dev;
+ child_device_obj->device.bus = &hv_bus;
+ child_device_obj->device.parent = &hv_acpi_dev->dev;
child_device_obj->device.release = vmbus_device_release;
/*
@@ -695,10 +676,10 @@ int vmbus_child_device_register(struct hv_device *child_device_obj)
}
/*
- * vmbus_child_device_unregister - Remove the specified child device
+ * vmbus_device_unregister - Remove the specified child device
* from the vmbus.
*/
-void vmbus_child_device_unregister(struct hv_device *device_obj)
+void vmbus_device_unregister(struct hv_device *device_obj)
{
/*
* Kick off the process of unregistering the device.
@@ -733,9 +714,10 @@ static int vmbus_acpi_add(struct acpi_device *device)
{
acpi_status result;
- result =
- acpi_walk_resources(device->handle, METHOD_NAME__CRS,
- vmbus_walk_resources, &irq);
+ hv_acpi_dev = device;
+
+ result = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
+ vmbus_walk_resources, &irq);
if (ACPI_FAILURE(result)) {
complete(&probe_event);
@@ -747,6 +729,7 @@ static int vmbus_acpi_add(struct acpi_device *device)
static const struct acpi_device_id vmbus_acpi_device_ids[] = {
{"VMBUS", 0},
+ {"VMBus", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids);
@@ -759,74 +742,9 @@ static struct acpi_driver vmbus_acpi_driver = {
},
};
-static int vmbus_acpi_init(void)
-{
- int result;
-
-
- result = acpi_bus_register_driver(&vmbus_acpi_driver);
- if (result < 0)
- return result;
-
- return 0;
-}
-
-static void vmbus_acpi_exit(void)
+static int __init hv_acpi_init(void)
{
- acpi_bus_unregister_driver(&vmbus_acpi_driver);
-
- return;
-}
-
-
-static int __devinit hv_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- hv_pci_dev = pdev;
-
- pci_probe_error = pci_enable_device(pdev);
- if (pci_probe_error)
- goto probe_cleanup;
-
- /*
- * If the PCI sub-sytem did not assign us an
- * irq, use the bios provided one.
- */
-
- if (pdev->irq == 0)
- pdev->irq = irq;
-
- pci_probe_error = vmbus_bus_init(pdev);
-
- if (pci_probe_error)
- pci_disable_device(pdev);
-
-probe_cleanup:
- complete(&probe_event);
- return pci_probe_error;
-}
-
-/*
- * We use a PCI table to determine if we should autoload this driver This is
- * needed by distro tools to determine if the hyperv drivers should be
- * installed and/or configured. We don't do anything else with the table, but
- * it needs to be present.
- */
-static const struct pci_device_id microsoft_hv_pci_table[] = {
- { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, microsoft_hv_pci_table);
-
-static struct pci_driver hv_bus_driver = {
- .name = "hv_bus",
- .probe = hv_pci_probe,
- .id_table = microsoft_hv_pci_table,
-};
-
-static int __init hv_pci_init(void)
-{
- int ret;
+ int ret, t;
init_completion(&probe_event);
@@ -834,37 +752,35 @@ static int __init hv_pci_init(void)
* Get irq resources first.
*/
- ret = vmbus_acpi_init();
+ ret = acpi_bus_register_driver(&vmbus_acpi_driver);
+
if (ret)
return ret;
- wait_for_completion(&probe_event);
+ t = wait_for_completion_timeout(&probe_event, 5*HZ);
+ if (t == 0) {
+ ret = -ETIMEDOUT;
+ goto cleanup;
+ }
if (irq <= 0) {
- vmbus_acpi_exit();
- return -ENODEV;
+ ret = -ENODEV;
+ goto cleanup;
}
- vmbus_acpi_exit();
- init_completion(&probe_event);
- ret = pci_register_driver(&hv_bus_driver);
+ ret = vmbus_bus_init(irq);
if (ret)
- return ret;
- /*
- * All the vmbus initialization occurs within the
- * hv_pci_probe() function. Wait for hv_pci_probe()
- * to complete.
- */
- wait_for_completion(&probe_event);
+ goto cleanup;
+
+ return 0;
- if (pci_probe_error)
- pci_unregister_driver(&hv_bus_driver);
- return pci_probe_error;
+cleanup:
+ acpi_bus_unregister_driver(&vmbus_acpi_driver);
+ return ret;
}
MODULE_LICENSE("GPL");
MODULE_VERSION(HV_DRV_VERSION);
-module_param(vmbus_loglevel, int, S_IRUGO|S_IWUSR);
-module_init(hv_pci_init);
+module_init(hv_acpi_init);
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5f888f7e7dcb..91be41f60809 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -68,6 +68,16 @@ config SENSORS_ABITUGURU3
This driver can also be built as a module. If so, the module
will be called abituguru3.
+config SENSORS_AD7314
+ tristate "Analog Devices AD7314 and compatibles"
+ depends on SPI && EXPERIMENTAL
+ help
+ If you say yes here you get support for the Analog Devices
+ AD7314, ADT7301 and ADT7302 temperature sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called ad7314.
+
config SENSORS_AD7414
tristate "Analog Devices AD7414"
depends on I2C && EXPERIMENTAL
@@ -303,6 +313,16 @@ config SENSORS_DS1621
This driver can also be built as a module. If so, the module
will be called ds1621.
+config SENSORS_EXYNOS4_TMU
+ tristate "Temperature sensor on Samsung EXYNOS4"
+ depends on ARCH_EXYNOS4
+ help
+ If you say yes here you get support for TMU (Thermal Managment
+ Unit) on SAMSUNG EXYNOS4 series of SoC.
+
+ This driver can also be built as a module. If so, the module
+ will be called exynos4-tmu.
+
config SENSORS_I5K_AMB
tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
depends on PCI && EXPERIMENTAL
@@ -315,6 +335,7 @@ config SENSORS_I5K_AMB
config SENSORS_F71805F
tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG"
+ depends on !PPC
help
If you say yes here you get support for hardware monitoring
features of the Fintek F71805F/FG, F71806F/FG and F71872F/FG
@@ -325,6 +346,7 @@ config SENSORS_F71805F
config SENSORS_F71882FG
tristate "Fintek F71882FG and compatibles"
+ depends on !PPC
help
If you say yes here you get support for hardware monitoring
features of many Fintek Super-I/O (LPC) chips. The currently
@@ -448,6 +470,7 @@ config SENSORS_IBMPEX
config SENSORS_IT87
tristate "ITE IT87xx and compatibles"
+ depends on !PPC
select HWMON_VID
help
If you say yes here you get support for ITE IT8705F, IT8712F,
@@ -531,6 +554,7 @@ config SENSORS_LM75
If you say yes here you get support for one common type of
temperature sensor chip, with models including:
+ - Analog Devices ADT75
- Dallas Semiconductor DS75 and DS1775
- Maxim MAX6625 and MAX6626
- Microchip MCP980x
@@ -623,7 +647,7 @@ config SENSORS_LM90
LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008,
- and Winbond/Nuvoton W83L771W/G/AWG/ASG sensor chips.
+ Winbond/Nuvoton W83L771W/G/AWG/ASG and Philips SA56004 sensor chips.
This driver can also be built as a module. If so, the module
will be called lm90.
@@ -694,14 +718,24 @@ config SENSORS_LTC4261
be called ltc4261.
config SENSORS_LM95241
- tristate "National Semiconductor LM95241 sensor chip"
+ tristate "National Semiconductor LM95241 and compatibles"
depends on I2C
help
- If you say yes here you get support for LM95241 sensor chip.
+ If you say yes here you get support for LM95231 and LM95241 sensor
+ chips.
This driver can also be built as a module. If so, the module
will be called lm95241.
+config SENSORS_LM95245
+ tristate "National Semiconductor LM95245 sensor chip"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for LM95245 sensor chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm95245.
+
config SENSORS_MAX1111
tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip"
depends on SPI_MASTER
@@ -736,6 +770,16 @@ config SENSORS_MAX1619
This driver can also be built as a module. If so, the module
will be called max1619.
+config SENSORS_MAX1668
+ tristate "Maxim MAX1668 and compatibles"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for MAX1668, MAX1989 and
+ MAX1805 chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called max1668.
+
config SENSORS_MAX6639
tristate "Maxim MAX6639 sensor chip"
depends on I2C && EXPERIMENTAL
@@ -767,8 +811,23 @@ config SENSORS_MAX6650
This driver can also be built as a module. If so, the module
will be called max6650.
+config SENSORS_NTC_THERMISTOR
+ tristate "NTC thermistor support"
+ depends on EXPERIMENTAL
+ help
+ This driver supports NTC thermistors sensor reading and its
+ interpretation. The driver can also monitor the temperature and
+ send notifications about the temperature.
+
+ Currently, this driver supports
+ NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333.
+
+ This driver can also be built as a module. If so, the module
+ will be called ntc-thermistor.
+
config SENSORS_PC87360
tristate "National Semiconductor PC87360 family"
+ depends on !PPC
select HWMON_VID
help
If you say yes here you get access to the hardware monitoring
@@ -782,6 +841,7 @@ config SENSORS_PC87360
config SENSORS_PC87427
tristate "National Semiconductor PC87427"
+ depends on !PPC
help
If you say yes here you get access to the hardware monitoring
functions of the National Semiconductor PC87427 Super-I/O chip.
@@ -807,92 +867,7 @@ config SENSORS_PCF8591
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
-config PMBUS
- tristate "PMBus support"
- depends on I2C && EXPERIMENTAL
- default n
- help
- Say yes here if you want to enable PMBus support.
-
- This driver can also be built as a module. If so, the module will
- be called pmbus_core.
-
-if PMBUS
-
-config SENSORS_PMBUS
- tristate "Generic PMBus devices"
- default n
- help
- If you say yes here you get hardware monitoring support for generic
- PMBus devices, including but not limited to BMR450, BMR451, BMR453,
- BMR454, and LTC2978.
-
- This driver can also be built as a module. If so, the module will
- be called pmbus.
-
-config SENSORS_ADM1275
- tristate "Analog Devices ADM1275"
- default n
- help
- If you say yes here you get hardware monitoring support for Analog
- Devices ADM1275 Hot-Swap Controller and Digital Power Monitor.
-
- This driver can also be built as a module. If so, the module will
- be called adm1275.
-
-config SENSORS_MAX16064
- tristate "Maxim MAX16064"
- default n
- help
- If you say yes here you get hardware monitoring support for Maxim
- MAX16064.
-
- This driver can also be built as a module. If so, the module will
- be called max16064.
-
-config SENSORS_MAX34440
- tristate "Maxim MAX34440/MAX34441"
- default n
- help
- If you say yes here you get hardware monitoring support for Maxim
- MAX34440 and MAX34441.
-
- This driver can also be built as a module. If so, the module will
- be called max34440.
-
-config SENSORS_MAX8688
- tristate "Maxim MAX8688"
- default n
- help
- If you say yes here you get hardware monitoring support for Maxim
- MAX8688.
-
- This driver can also be built as a module. If so, the module will
- be called max8688.
-
-config SENSORS_UCD9000
- tristate "TI UCD90120, UCD90124, UCD9090, UCD90910"
- default n
- help
- If you say yes here you get hardware monitoring support for TI
- UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health
- Controllers.
-
- This driver can also be built as a module. If so, the module will
- be called ucd9000.
-
-config SENSORS_UCD9200
- tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248"
- default n
- help
- If you say yes here you get hardware monitoring support for TI
- UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248
- Digital PWM System Controllers.
-
- This driver can also be built as a module. If so, the module will
- be called ucd9200.
-
-endif # PMBUS
+source drivers/hwmon/pmbus/Kconfig
config SENSORS_SHT15
tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
@@ -958,7 +933,7 @@ config SENSORS_SMM665
config SENSORS_DME1737
tristate "SMSC DME1737, SCH311x and compatibles"
- depends on I2C && EXPERIMENTAL
+ depends on I2C && EXPERIMENTAL && !PPC
select HWMON_VID
help
If you say yes here you get support for the hardware monitoring
@@ -1000,6 +975,7 @@ config SENSORS_EMC6W201
config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles"
+ depends on !PPC
help
If you say yes here you get support for the integrated fan
monitoring and control capabilities of the SMSC LPC47B27x,
@@ -1033,7 +1009,7 @@ config SENSORS_SMSC47M192
config SENSORS_SMSC47B397
tristate "SMSC LPC47B397-NC"
- depends on EXPERIMENTAL
+ depends on EXPERIMENTAL && !PPC
help
If you say yes here you get support for the SMSC LPC47B397-NC
sensor chip.
@@ -1041,8 +1017,14 @@ config SENSORS_SMSC47B397
This driver can also be built as a module. If so, the module
will be called smsc47b397.
+config SENSORS_SCH56XX_COMMON
+ tristate
+ default n
+
config SENSORS_SCH5627
tristate "SMSC SCH5627"
+ depends on !PPC
+ select SENSORS_SCH56XX_COMMON
help
If you say yes here you get support for the hardware monitoring
features of the SMSC SCH5627 Super-I/O chip.
@@ -1050,6 +1032,22 @@ config SENSORS_SCH5627
This driver can also be built as a module. If so, the module
will be called sch5627.
+config SENSORS_SCH5636
+ tristate "SMSC SCH5636"
+ depends on !PPC
+ select SENSORS_SCH56XX_COMMON
+ help
+ SMSC SCH5636 Super I/O chips include an embedded microcontroller for
+ hardware monitoring solutions, allowing motherboard manufacturers to
+ create their own custom hwmon solution based upon the SCH5636.
+
+ Currently this driver only supports the Fujitsu Theseus SCH5636 based
+ hwmon solution. Say yes here if you want support for the Fujitsu
+ Theseus' hardware monitoring features.
+
+ This driver can also be built as a module. If so, the module
+ will be called sch5636.
+
config SENSORS_ADS1015
tristate "Texas Instruments ADS1015"
depends on I2C
@@ -1142,6 +1140,7 @@ config SENSORS_TWL4030_MADC
config SENSORS_VIA_CPUTEMP
tristate "VIA CPU temperature sensor"
depends on X86
+ select HWMON_VID
help
If you say yes here you get support for the temperature
sensor inside your CPU. Supported are all known variants of
@@ -1159,6 +1158,7 @@ config SENSORS_VIA686A
config SENSORS_VT1211
tristate "VIA VT1211"
+ depends on !PPC
select HWMON_VID
help
If you say yes here then you get support for hardware monitoring
@@ -1271,6 +1271,7 @@ config SENSORS_W83L786NG
config SENSORS_W83627HF
tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF"
+ depends on !PPC
select HWMON_VID
help
If you say yes here you get support for the Winbond W836X7 series
@@ -1281,7 +1282,8 @@ config SENSORS_W83627HF
will be called w83627hf.
config SENSORS_W83627EHF
- tristate "Winbond W83627EHF/EHG/DHG, W83667HG, NCT6775F, NCT6776F"
+ tristate "Winbond W83627EHF/EHG/DHG/UHG, W83667HG, NCT6775F, NCT6776F"
+ depends on !PPC
select HWMON_VID
help
If you say yes here you get support for the hardware
@@ -1290,7 +1292,8 @@ config SENSORS_W83627EHF
This driver also supports the W83627EHG, which is the lead-free
version of the W83627EHF, and the W83627DHG, which is a similar
chip suited for specific Intel processors that use PECI such as
- the Core 2 Duo.
+ the Core 2 Duo. And also the W83627UHG, which is a stripped down
+ version of the W83627DHG (as far as hardware monitoring goes.)
This driver also supports Nuvoton W83667HG, W83667HG-B, NCT6775F
(also known as W83667HG-I), and NCT6776F.
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 28061cfa0cdb..8251ce8cd035 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
+obj-$(CONFIG_SENSORS_AD7314) += ad7314.o
obj-$(CONFIG_SENSORS_AD7414) += ad7414.o
obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o
@@ -47,6 +48,7 @@ obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o
+obj-$(CONFIG_SENSORS_EXYNOS4_TMU) += exynos4_tmu.o
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
@@ -80,6 +82,7 @@ obj-$(CONFIG_SENSORS_LM90) += lm90.o
obj-$(CONFIG_SENSORS_LM92) += lm92.o
obj-$(CONFIG_SENSORS_LM93) += lm93.o
obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
+obj-$(CONFIG_SENSORS_LM95245) += lm95245.o
obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o
obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
@@ -87,15 +90,19 @@ obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
+obj-$(CONFIG_SENSORS_MAX1668) += max1668.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
+obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o
+obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o
+obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o
obj-$(CONFIG_SENSORS_SHT15) += sht15.o
obj-$(CONFIG_SENSORS_SHT21) += sht21.o
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
@@ -119,15 +126,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
-# PMBus drivers
-obj-$(CONFIG_PMBUS) += pmbus_core.o
-obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
-obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
-obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
-obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
-obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
-obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
-obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
+obj-$(CONFIG_PMBUS) += pmbus/
ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c
new file mode 100644
index 000000000000..318e38e85376
--- /dev/null
+++ b/drivers/hwmon/ad7314.c
@@ -0,0 +1,186 @@
+/*
+ * AD7314 digital temperature sensor driver for AD7314, ADT7301 and ADT7302
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * Conversion to hwmon from IIO done by Jonathan Cameron <jic23@cam.ac.uk>
+ */
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+/*
+ * AD7314 power mode
+ */
+#define AD7314_PD 0x2000
+
+/*
+ * AD7314 temperature masks
+ */
+#define AD7314_TEMP_SIGN 0x200
+#define AD7314_TEMP_MASK 0x7FE0
+#define AD7314_TEMP_OFFSET 5
+
+/*
+ * ADT7301 and ADT7302 temperature masks
+ */
+#define ADT7301_TEMP_SIGN 0x2000
+#define ADT7301_TEMP_MASK 0x3FFF
+
+enum ad7314_variant {
+ adt7301,
+ adt7302,
+ ad7314,
+};
+
+struct ad7314_data {
+ struct spi_device *spi_dev;
+ struct device *hwmon_dev;
+ u16 rx ____cacheline_aligned;
+};
+
+static int ad7314_spi_read(struct ad7314_data *chip, s16 *data)
+{
+ int ret;
+
+ ret = spi_read(chip->spi_dev, (u8 *)&chip->rx, sizeof(chip->rx));
+ if (ret < 0) {
+ dev_err(&chip->spi_dev->dev, "SPI read error\n");
+ return ret;
+ }
+
+ *data = be16_to_cpu(chip->rx);
+
+ return ret;
+}
+
+static ssize_t ad7314_show_temperature(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ad7314_data *chip = dev_get_drvdata(dev);
+ s16 data;
+ int ret;
+
+ ret = ad7314_spi_read(chip, &data);
+ if (ret < 0)
+ return ret;
+ switch (spi_get_device_id(chip->spi_dev)->driver_data) {
+ case ad7314:
+ data = (data & AD7314_TEMP_MASK) >> AD7314_TEMP_OFFSET;
+ data = (data << 6) >> 6;
+
+ return sprintf(buf, "%d\n", 250 * data);
+ case adt7301:
+ case adt7302:
+ /*
+ * Documented as a 13 bit twos complement register
+ * with a sign bit - which is a 14 bit 2's complement
+ * register. 1lsb - 31.25 milli degrees centigrade
+ */
+ data &= ADT7301_TEMP_MASK;
+ data = (data << 2) >> 2;
+
+ return sprintf(buf, "%d\n",
+ DIV_ROUND_CLOSEST(data * 3125, 100));
+ default:
+ return -EINVAL;
+ }
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+ ad7314_show_temperature, NULL, 0);
+
+static struct attribute *ad7314_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7314_group = {
+ .attrs = ad7314_attributes,
+};
+
+static int __devinit ad7314_probe(struct spi_device *spi_dev)
+{
+ int ret;
+ struct ad7314_data *chip;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ dev_set_drvdata(&spi_dev->dev, chip);
+
+ ret = sysfs_create_group(&spi_dev->dev.kobj, &ad7314_group);
+ if (ret < 0)
+ goto error_free_chip;
+ chip->hwmon_dev = hwmon_device_register(&spi_dev->dev);
+ if (IS_ERR(chip->hwmon_dev)) {
+ ret = PTR_ERR(chip->hwmon_dev);
+ goto error_remove_group;
+ }
+
+ return 0;
+error_remove_group:
+ sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group);
+error_free_chip:
+ kfree(chip);
+error_ret:
+ return ret;
+}
+
+static int __devexit ad7314_remove(struct spi_device *spi_dev)
+{
+ struct ad7314_data *chip = dev_get_drvdata(&spi_dev->dev);
+
+ hwmon_device_unregister(chip->hwmon_dev);
+ sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group);
+ kfree(chip);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7314_id[] = {
+ { "adt7301", adt7301 },
+ { "adt7302", adt7302 },
+ { "ad7314", ad7314 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7314_id);
+
+static struct spi_driver ad7314_driver = {
+ .driver = {
+ .name = "ad7314",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7314_probe,
+ .remove = __devexit_p(ad7314_remove),
+ .id_table = ad7314_id,
+};
+
+static __init int ad7314_init(void)
+{
+ return spi_register_driver(&ad7314_driver);
+}
+module_init(ad7314_init);
+
+static __exit void ad7314_exit(void)
+{
+ spi_unregister_driver(&ad7314_driver);
+}
+module_exit(ad7314_exit);
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital"
+ " temperature sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c
index d46c0c758ddf..df29a7fff9e7 100644
--- a/drivers/hwmon/ad7414.c
+++ b/drivers/hwmon/ad7414.c
@@ -58,10 +58,9 @@ static inline int ad7414_temp_from_reg(s16 reg)
static inline int ad7414_read(struct i2c_client *client, u8 reg)
{
- if (reg == AD7414_REG_TEMP) {
- int value = i2c_smbus_read_word_data(client, reg);
- return (value < 0) ? value : swab16(value);
- } else
+ if (reg == AD7414_REG_TEMP)
+ return i2c_smbus_read_word_swapped(client, reg);
+ else
return i2c_smbus_read_byte_data(client, reg);
}
diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c
index ffc781fec185..8cb718ce8237 100644
--- a/drivers/hwmon/ad7418.c
+++ b/drivers/hwmon/ad7418.c
@@ -76,20 +76,6 @@ static struct i2c_driver ad7418_driver = {
.id_table = ad7418_id,
};
-/* All registers are word-sized, except for the configuration registers.
- * AD7418 uses a high-byte first convention. Do NOT use those functions to
- * access the configuration registers CONF and CONF2, as they are byte-sized.
- */
-static inline int ad7418_read(struct i2c_client *client, u8 reg)
-{
- return swab16(i2c_smbus_read_word_data(client, reg));
-}
-
-static inline int ad7418_write(struct i2c_client *client, u8 reg, u16 value)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(value));
-}
-
static void ad7418_init_client(struct i2c_client *client)
{
struct ad7418_data *data = i2c_get_clientdata(client);
@@ -128,7 +114,9 @@ static struct ad7418_data *ad7418_update_device(struct device *dev)
udelay(30);
for (i = 0; i < 3; i++) {
- data->temp[i] = ad7418_read(client, AD7418_REG_TEMP[i]);
+ data->temp[i] =
+ i2c_smbus_read_word_swapped(client,
+ AD7418_REG_TEMP[i]);
}
for (i = 0, ch = 4; i < data->adc_max; i++, ch--) {
@@ -138,11 +126,12 @@ static struct ad7418_data *ad7418_update_device(struct device *dev)
udelay(15);
data->in[data->adc_max - 1 - i] =
- ad7418_read(client, AD7418_REG_ADC);
+ i2c_smbus_read_word_swapped(client,
+ AD7418_REG_ADC);
}
/* restore old configuration value */
- ad7418_write(client, AD7418_REG_CONF, cfg);
+ i2c_smbus_write_word_swapped(client, AD7418_REG_CONF, cfg);
data->last_updated = jiffies;
data->valid = 1;
@@ -182,7 +171,9 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
mutex_lock(&data->lock);
data->temp[attr->index] = LM75_TEMP_TO_REG(temp);
- ad7418_write(client, AD7418_REG_TEMP[attr->index], data->temp[attr->index]);
+ i2c_smbus_write_word_swapped(client,
+ AD7418_REG_TEMP[attr->index],
+ data->temp[attr->index]);
mutex_unlock(&data->lock);
return count;
}
diff --git a/drivers/hwmon/adm1275.c b/drivers/hwmon/adm1275.c
deleted file mode 100644
index b9b7caf4a1d2..000000000000
--- a/drivers/hwmon/adm1275.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Hardware monitoring driver for Analog Devices ADM1275 Hot-Swap Controller
- * and Digital Power Monitor
- *
- * Copyright (c) 2011 Ericsson AB.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include "pmbus.h"
-
-#define ADM1275_PMON_CONFIG 0xd4
-
-#define ADM1275_VIN_VOUT_SELECT (1 << 6)
-#define ADM1275_VRANGE (1 << 5)
-
-static int adm1275_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- int config;
- int ret;
- struct pmbus_driver_info *info;
-
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_READ_BYTE_DATA))
- return -ENODEV;
-
- info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
- if (config < 0) {
- ret = config;
- goto err_mem;
- }
-
- info->pages = 1;
- info->direct[PSC_VOLTAGE_IN] = true;
- info->direct[PSC_VOLTAGE_OUT] = true;
- info->direct[PSC_CURRENT_OUT] = true;
- info->m[PSC_CURRENT_OUT] = 800;
- info->b[PSC_CURRENT_OUT] = 20475;
- info->R[PSC_CURRENT_OUT] = -1;
- info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
-
- if (config & ADM1275_VRANGE) {
- info->m[PSC_VOLTAGE_IN] = 19045;
- info->b[PSC_VOLTAGE_IN] = 0;
- info->R[PSC_VOLTAGE_IN] = -2;
- info->m[PSC_VOLTAGE_OUT] = 19045;
- info->b[PSC_VOLTAGE_OUT] = 0;
- info->R[PSC_VOLTAGE_OUT] = -2;
- } else {
- info->m[PSC_VOLTAGE_IN] = 6666;
- info->b[PSC_VOLTAGE_IN] = 0;
- info->R[PSC_VOLTAGE_IN] = -1;
- info->m[PSC_VOLTAGE_OUT] = 6666;
- info->b[PSC_VOLTAGE_OUT] = 0;
- info->R[PSC_VOLTAGE_OUT] = -1;
- }
-
- if (config & ADM1275_VIN_VOUT_SELECT)
- info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
- else
- info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
-
- ret = pmbus_do_probe(client, id, info);
- if (ret)
- goto err_mem;
- return 0;
-
-err_mem:
- kfree(info);
- return ret;
-}
-
-static int adm1275_remove(struct i2c_client *client)
-{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- int ret;
-
- ret = pmbus_do_remove(client);
- kfree(info);
- return ret;
-}
-
-static const struct i2c_device_id adm1275_id[] = {
- {"adm1275", 0},
- { }
-};
-MODULE_DEVICE_TABLE(i2c, adm1275_id);
-
-static struct i2c_driver adm1275_driver = {
- .driver = {
- .name = "adm1275",
- },
- .probe = adm1275_probe,
- .remove = adm1275_remove,
- .id_table = adm1275_id,
-};
-
-static int __init adm1275_init(void)
-{
- return i2c_add_driver(&adm1275_driver);
-}
-
-static void __exit adm1275_exit(void)
-{
- i2c_del_driver(&adm1275_driver);
-}
-
-MODULE_AUTHOR("Guenter Roeck");
-MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275");
-MODULE_LICENSE("GPL");
-module_init(adm1275_init);
-module_exit(adm1275_exit);
diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c
index e9beeda4cbe5..eedca3cf9968 100644
--- a/drivers/hwmon/ads1015.c
+++ b/drivers/hwmon/ads1015.c
@@ -59,19 +59,6 @@ struct ads1015_data {
struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
};
-static s32 ads1015_read_reg(struct i2c_client *client, unsigned int reg)
-{
- s32 data = i2c_smbus_read_word_data(client, reg);
-
- return (data < 0) ? data : swab16(data);
-}
-
-static s32 ads1015_write_reg(struct i2c_client *client, unsigned int reg,
- u16 val)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(val));
-}
-
static int ads1015_read_value(struct i2c_client *client, unsigned int channel,
int *value)
{
@@ -87,7 +74,7 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel,
mutex_lock(&data->update_lock);
/* get channel parameters */
- res = ads1015_read_reg(client, ADS1015_CONFIG);
+ res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG);
if (res < 0)
goto err_unlock;
config = res;
@@ -101,13 +88,13 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel,
config |= (pga & 0x0007) << 9;
config |= (data_rate & 0x0007) << 5;
- res = ads1015_write_reg(client, ADS1015_CONFIG, config);
+ res = i2c_smbus_write_word_swapped(client, ADS1015_CONFIG, config);
if (res < 0)
goto err_unlock;
/* wait until conversion finished */
msleep(conversion_time_ms);
- res = ads1015_read_reg(client, ADS1015_CONFIG);
+ res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG);
if (res < 0)
goto err_unlock;
config = res;
@@ -117,7 +104,7 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel,
goto err_unlock;
}
- res = ads1015_read_reg(client, ADS1015_CONVERSION);
+ res = i2c_smbus_read_word_swapped(client, ADS1015_CONVERSION);
if (res < 0)
goto err_unlock;
conversion = res;
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index c42c5a69a664..cfcc3b6fb6bf 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -74,13 +74,6 @@ static int ads7828_detect(struct i2c_client *client,
static int ads7828_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-/* The ADS7828 returns the 12-bit sample in two bytes,
- these are read as a word then byte-swapped */
-static u16 ads7828_read_value(struct i2c_client *client, u8 reg)
-{
- return swab16(i2c_smbus_read_word_data(client, reg));
-}
-
static inline u8 channel_cmd_byte(int ch)
{
/* cmd byte C2,C1,C0 - see datasheet */
@@ -104,7 +97,8 @@ static struct ads7828_data *ads7828_update_device(struct device *dev)
for (ch = 0; ch < ADS7828_NCH; ch++) {
u8 cmd = channel_cmd_byte(ch);
- data->adc_input[ch] = ads7828_read_value(client, cmd);
+ data->adc_input[ch] =
+ i2c_smbus_read_word_swapped(client, cmd);
}
data->last_updated = jiffies;
data->valid = 1;
@@ -203,7 +197,7 @@ static int ads7828_detect(struct i2c_client *client,
for (ch = 0; ch < ADS7828_NCH; ch++) {
u16 in_data;
u8 cmd = channel_cmd_byte(ch);
- in_data = ads7828_read_value(client, cmd);
+ in_data = i2c_smbus_read_word_swapped(client, cmd);
if (in_data & 0xF000) {
pr_debug("%s : Doesn't look like an ads7828 device\n",
__func__);
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index c02a052d3085..d7bd1f3f2a31 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -829,17 +829,17 @@ static int asb100_read_value(struct i2c_client *client, u16 reg)
/* convert from ISA to LM75 I2C addresses */
switch (reg & 0xff) {
case 0x50: /* TEMP */
- res = swab16(i2c_smbus_read_word_data(cl, 0));
+ res = i2c_smbus_read_word_swapped(cl, 0);
break;
case 0x52: /* CONFIG */
res = i2c_smbus_read_byte_data(cl, 1);
break;
case 0x53: /* HYST */
- res = swab16(i2c_smbus_read_word_data(cl, 2));
+ res = i2c_smbus_read_word_swapped(cl, 2);
break;
case 0x55: /* MAX */
default:
- res = swab16(i2c_smbus_read_word_data(cl, 3));
+ res = i2c_smbus_read_word_swapped(cl, 3);
break;
}
}
@@ -877,10 +877,10 @@ static void asb100_write_value(struct i2c_client *client, u16 reg, u16 value)
i2c_smbus_write_byte_data(cl, 1, value & 0xff);
break;
case 0x53: /* HYST */
- i2c_smbus_write_word_data(cl, 2, swab16(value));
+ i2c_smbus_write_word_swapped(cl, 2, value);
break;
case 0x55: /* MAX */
- i2c_smbus_write_word_data(cl, 3, swab16(value));
+ i2c_smbus_write_word_swapped(cl, 3, value);
break;
}
}
diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c
index dcb78a7a8047..00e98517f94c 100644
--- a/drivers/hwmon/asus_atk0110.c
+++ b/drivers/hwmon/asus_atk0110.c
@@ -674,6 +674,7 @@ static int atk_debugfs_gitm_get(void *p, u64 *val)
else
err = -EIO;
+ ACPI_FREE(ret);
return err;
}
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 0070d5476dd0..104b3767516c 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -36,28 +36,37 @@
#include <linux/cpu.h>
#include <linux/pci.h>
#include <linux/smp.h>
+#include <linux/moduleparam.h>
#include <asm/msr.h>
#include <asm/processor.h>
#define DRVNAME "coretemp"
+/*
+ * force_tjmax only matters when TjMax can't be read from the CPU itself.
+ * When set, it replaces the driver's suboptimal heuristic.
+ */
+static int force_tjmax;
+module_param_named(tjmax, force_tjmax, int, 0444);
+MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
+
#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */
#define NUM_REAL_CORES 16 /* Number of Real cores per cpu */
#define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */
-#define MAX_ATTRS 5 /* Maximum no of per-core attrs */
+#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */
+#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1)
#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
#ifdef CONFIG_SMP
#define TO_PHYS_ID(cpu) cpu_data(cpu).phys_proc_id
#define TO_CORE_ID(cpu) cpu_data(cpu).cpu_core_id
-#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO)
#define for_each_sibling(i, cpu) for_each_cpu(i, cpu_sibling_mask(cpu))
#else
#define TO_PHYS_ID(cpu) (cpu)
#define TO_CORE_ID(cpu) (cpu)
-#define TO_ATTR_NO(cpu) (cpu)
#define for_each_sibling(i, cpu) for (i = 0; false; )
#endif
+#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO)
/*
* Per-Core Temperature Data
@@ -67,6 +76,7 @@
* This value is passed as "id" field to rdmsr/wrmsr functions.
* @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,
* from where the temperature values should be read.
+ * @attr_size: Total number of pre-core attrs displayed in the sysfs.
* @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.
* Otherwise, temp_data holds coretemp data.
* @valid: If this is 1, the current temperature is valid.
@@ -79,10 +89,11 @@ struct temp_data {
unsigned int cpu;
u32 cpu_core_id;
u32 status_reg;
+ int attr_size;
bool is_pkg_data;
bool valid;
- struct sensor_device_attribute sd_attrs[MAX_ATTRS];
- char attr_name[MAX_ATTRS][CORETEMP_NAME_LENGTH];
+ struct sensor_device_attribute sd_attrs[TOTAL_ATTRS];
+ char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];
struct mutex update_lock;
};
@@ -276,7 +287,6 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
{
- /* The 100C is default for both mobile and non mobile CPUs */
int err;
u32 eax, edx;
u32 val;
@@ -287,7 +297,8 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
*/
err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
if (err) {
- dev_warn(dev, "Unable to read TjMax from CPU.\n");
+ if (c->x86_model > 0xe && c->x86_model != 0x1c)
+ dev_warn(dev, "Unable to read TjMax from CPU %u\n", id);
} else {
val = (eax >> 16) & 0xff;
/*
@@ -295,11 +306,17 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
* will be used
*/
if (val) {
- dev_info(dev, "TjMax is %d C.\n", val);
+ dev_dbg(dev, "TjMax is %d degrees C\n", val);
return val * 1000;
}
}
+ if (force_tjmax) {
+ dev_notice(dev, "TjMax forced to %d degrees C by user\n",
+ force_tjmax);
+ return force_tjmax * 1000;
+ }
+
/*
* An assumption is made for early CPUs and unreadable MSR.
* NOTE: the calculated value may not be correct.
@@ -307,30 +324,6 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
return adjust_tjmax(c, id, dev);
}
-static void __devinit get_ucode_rev_on_cpu(void *edx)
-{
- u32 eax;
-
- wrmsr(MSR_IA32_UCODE_REV, 0, 0);
- sync_core();
- rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx);
-}
-
-static int get_pkg_tjmax(unsigned int cpu, struct device *dev)
-{
- int err;
- u32 eax, edx, val;
-
- err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
- if (!err) {
- val = (eax >> 16) & 0xff;
- if (val)
- return val * 1000;
- }
- dev_warn(dev, "Unable to read Pkg-TjMax from CPU:%u\n", cpu);
- return 100000; /* Default TjMax: 100 degree celsius */
-}
-
static int create_name_attr(struct platform_data *pdata, struct device *dev)
{
sysfs_attr_init(&pdata->name_attr.attr);
@@ -344,23 +337,22 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev,
int attr_no)
{
int err, i;
- static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev,
+ static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev,
struct device_attribute *devattr, char *buf) = {
- show_label, show_crit_alarm, show_ttarget,
- show_temp, show_tjmax };
- static const char *names[MAX_ATTRS] = {
+ show_label, show_crit_alarm, show_temp, show_tjmax,
+ show_ttarget };
+ static const char *const names[TOTAL_ATTRS] = {
"temp%d_label", "temp%d_crit_alarm",
- "temp%d_max", "temp%d_input",
- "temp%d_crit" };
+ "temp%d_input", "temp%d_crit",
+ "temp%d_max" };
- for (i = 0; i < MAX_ATTRS; i++) {
+ for (i = 0; i < tdata->attr_size; i++) {
snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],
attr_no);
sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr);
tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i];
tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO;
tdata->sd_attrs[i].dev_attr.show = rd_ptr[i];
- tdata->sd_attrs[i].dev_attr.store = NULL;
tdata->sd_attrs[i].index = attr_no;
err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);
if (err)
@@ -374,65 +366,20 @@ exit_free:
return err;
}
-static void update_ttarget(__u8 cpu_model, struct temp_data *tdata,
- struct device *dev)
-{
- int err;
- u32 eax, edx;
-
- /*
- * Initialize ttarget value. Eventually this will be
- * initialized with the value from MSR_IA32_THERM_INTERRUPT
- * register. If IA32_TEMPERATURE_TARGET is supported, this
- * value will be over written below.
- * To Do: Patch to initialize ttarget from MSR_IA32_THERM_INTERRUPT
- */
- tdata->ttarget = tdata->tjmax - 20000;
-
- /*
- * Read the still undocumented IA32_TEMPERATURE_TARGET. It exists
- * on older CPUs but not in this register,
- * Atoms don't have it either.
- */
- if (cpu_model > 0xe && cpu_model != 0x1c) {
- err = rdmsr_safe_on_cpu(tdata->cpu,
- MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
- if (err) {
- dev_warn(dev,
- "Unable to read IA32_TEMPERATURE_TARGET MSR\n");
- } else {
- tdata->ttarget = tdata->tjmax -
- ((eax >> 8) & 0xff) * 1000;
- }
- }
-}
-static int __devinit chk_ucode_version(struct platform_device *pdev)
+static int __cpuinit chk_ucode_version(unsigned int cpu)
{
- struct cpuinfo_x86 *c = &cpu_data(pdev->id);
- int err;
- u32 edx;
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
/*
* Check if we have problem with errata AE18 of Core processors:
* Readings might stop update when processor visited too deep sleep,
* fixed for stepping D0 (6EC).
*/
- if (c->x86_model == 0xe && c->x86_mask < 0xc) {
- /* check for microcode update */
- err = smp_call_function_single(pdev->id, get_ucode_rev_on_cpu,
- &edx, 1);
- if (err) {
- dev_err(&pdev->dev,
- "Cannot determine microcode revision of "
- "CPU#%u (%d)!\n", pdev->id, err);
- return -ENODEV;
- } else if (edx < 0x39) {
- dev_err(&pdev->dev,
- "Errata AE18 not fixed, update BIOS or "
- "microcode of the CPU!\n");
- return -ENODEV;
- }
+ if (c->x86_model == 0xe && c->x86_mask < 0xc && c->microcode < 0x39) {
+ pr_err("Errata AE18 not fixed, update BIOS or "
+ "microcode of the CPU!\n");
+ return -ENODEV;
}
return 0;
}
@@ -467,15 +414,16 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
tdata->is_pkg_data = pkg_flag;
tdata->cpu = cpu;
tdata->cpu_core_id = TO_CORE_ID(cpu);
+ tdata->attr_size = MAX_CORE_ATTRS;
mutex_init(&tdata->update_lock);
return tdata;
}
-static int create_core_data(struct platform_data *pdata,
- struct platform_device *pdev,
+static int create_core_data(struct platform_device *pdev,
unsigned int cpu, int pkg_flag)
{
struct temp_data *tdata;
+ struct platform_data *pdata = platform_get_drvdata(pdev);
struct cpuinfo_x86 *c = &cpu_data(cpu);
u32 eax, edx;
int err, attr_no;
@@ -511,12 +459,23 @@ static int create_core_data(struct platform_data *pdata,
goto exit_free;
/* We can access status register. Get Critical Temperature */
- if (pkg_flag)
- tdata->tjmax = get_pkg_tjmax(pdev->id, &pdev->dev);
- else
- tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
+ tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
+
+ /*
+ * Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET.
+ * The target temperature is available on older CPUs but not in this
+ * register. Atoms don't have the register at all.
+ */
+ if (c->x86_model > 0xe && c->x86_model != 0x1c) {
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET,
+ &eax, &edx);
+ if (!err) {
+ tdata->ttarget
+ = tdata->tjmax - ((eax >> 8) & 0xff) * 1000;
+ tdata->attr_size++;
+ }
+ }
- update_ttarget(c->x86_model, tdata, &pdev->dev);
pdata->core_data[attr_no] = tdata;
/* Create sysfs interfaces */
@@ -526,22 +485,20 @@ static int create_core_data(struct platform_data *pdata,
return 0;
exit_free:
+ pdata->core_data[attr_no] = NULL;
kfree(tdata);
return err;
}
static void coretemp_add_core(unsigned int cpu, int pkg_flag)
{
- struct platform_data *pdata;
struct platform_device *pdev = coretemp_get_pdev(cpu);
int err;
if (!pdev)
return;
- pdata = platform_get_drvdata(pdev);
-
- err = create_core_data(pdata, pdev, cpu, pkg_flag);
+ err = create_core_data(pdev, cpu, pkg_flag);
if (err)
dev_err(&pdev->dev, "Adding Core %u failed\n", cpu);
}
@@ -553,7 +510,7 @@ static void coretemp_remove_core(struct platform_data *pdata,
struct temp_data *tdata = pdata->core_data[indx];
/* Remove the sysfs attributes */
- for (i = 0; i < MAX_ATTRS; i++)
+ for (i = 0; i < tdata->attr_size; i++)
device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
kfree(pdata->core_data[indx]);
@@ -565,11 +522,6 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
struct platform_data *pdata;
int err;
- /* Check the microcode version of the CPU */
- err = chk_ucode_version(pdev);
- if (err)
- return err;
-
/* Initialize the per-package data structures */
pdata = kzalloc(sizeof(struct platform_data), GFP_KERNEL);
if (!pdata)
@@ -579,7 +531,7 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
if (err)
goto exit_free;
- pdata->phys_proc_id = TO_PHYS_ID(pdev->id);
+ pdata->phys_proc_id = pdev->id;
platform_set_drvdata(pdev, pdata);
pdata->hwmon_dev = hwmon_device_register(&pdev->dev);
@@ -631,7 +583,7 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)
mutex_lock(&pdev_list_mutex);
- pdev = platform_device_alloc(DRVNAME, cpu);
+ pdev = platform_device_alloc(DRVNAME, TO_PHYS_ID(cpu));
if (!pdev) {
err = -ENOMEM;
pr_err("Device allocation failed\n");
@@ -651,7 +603,7 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)
}
pdev_entry->pdev = pdev;
- pdev_entry->phys_proc_id = TO_PHYS_ID(cpu);
+ pdev_entry->phys_proc_id = pdev->id;
list_add_tail(&pdev_entry->list, &pdev_list);
mutex_unlock(&pdev_list_mutex);
@@ -712,6 +664,10 @@ static void __cpuinit get_core_online(unsigned int cpu)
return;
if (!pdev) {
+ /* Check the microcode version of the CPU */
+ if (chk_ucode_version(cpu))
+ return;
+
/*
* Alright, we have DTS support.
* We are bringing the _first_ core in this pkg
diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c
index e11363467a8d..ef1ac996752e 100644
--- a/drivers/hwmon/ds1621.c
+++ b/drivers/hwmon/ds1621.c
@@ -80,24 +80,6 @@ struct ds1621_data {
u8 conf; /* Register encoding, combined */
};
-/* Temperature registers are word-sized.
- DS1621 uses a high-byte first convention, which is exactly opposite to
- the SMBus standard. */
-static int ds1621_read_temp(struct i2c_client *client, u8 reg)
-{
- int ret;
-
- ret = i2c_smbus_read_word_data(client, reg);
- if (ret < 0)
- return ret;
- return swab16(ret);
-}
-
-static int ds1621_write_temp(struct i2c_client *client, u8 reg, u16 value)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(value));
-}
-
static void ds1621_init_client(struct i2c_client *client)
{
u8 conf, new_conf;
@@ -136,7 +118,7 @@ static struct ds1621_data *ds1621_update_client(struct device *dev)
data->conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
for (i = 0; i < ARRAY_SIZE(data->temp); i++)
- data->temp[i] = ds1621_read_temp(client,
+ data->temp[i] = i2c_smbus_read_word_swapped(client,
DS1621_REG_TEMP[i]);
/* reset alarms if necessary */
@@ -177,8 +159,8 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
mutex_lock(&data->update_lock);
data->temp[attr->index] = val;
- ds1621_write_temp(client, DS1621_REG_TEMP[attr->index],
- data->temp[attr->index]);
+ i2c_smbus_write_word_swapped(client, DS1621_REG_TEMP[attr->index],
+ data->temp[attr->index]);
mutex_unlock(&data->update_lock);
return count;
}
diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c
index 257957c69d92..225ae4f36583 100644
--- a/drivers/hwmon/ds620.c
+++ b/drivers/hwmon/ds620.c
@@ -72,36 +72,16 @@ struct ds620_data {
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
- u16 temp[3]; /* Register values, word */
+ s16 temp[3]; /* Register values, word */
};
-/*
- * Temperature registers are word-sized.
- * DS620 uses a high-byte first convention, which is exactly opposite to
- * the SMBus standard.
- */
-static int ds620_read_temp(struct i2c_client *client, u8 reg)
-{
- int ret;
-
- ret = i2c_smbus_read_word_data(client, reg);
- if (ret < 0)
- return ret;
- return swab16(ret);
-}
-
-static int ds620_write_temp(struct i2c_client *client, u8 reg, u16 value)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(value));
-}
-
static void ds620_init_client(struct i2c_client *client)
{
struct ds620_platform_data *ds620_info = client->dev.platform_data;
u16 conf, new_conf;
new_conf = conf =
- swab16(i2c_smbus_read_word_data(client, DS620_REG_CONF));
+ i2c_smbus_read_word_swapped(client, DS620_REG_CONF);
/* switch to continuous conversion mode */
new_conf &= ~DS620_REG_CONFIG_1SHOT;
@@ -118,8 +98,7 @@ static void ds620_init_client(struct i2c_client *client)
new_conf |= DS620_REG_CONFIG_R1 | DS620_REG_CONFIG_R0;
if (conf != new_conf)
- i2c_smbus_write_word_data(client, DS620_REG_CONF,
- swab16(new_conf));
+ i2c_smbus_write_word_swapped(client, DS620_REG_CONF, new_conf);
/* start conversion */
i2c_smbus_write_byte(client, DS620_COM_START);
@@ -141,8 +120,8 @@ static struct ds620_data *ds620_update_client(struct device *dev)
dev_dbg(&client->dev, "Starting ds620 update\n");
for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
- res = ds620_read_temp(client,
- DS620_REG_TEMP[i]);
+ res = i2c_smbus_read_word_swapped(client,
+ DS620_REG_TEMP[i]);
if (res < 0) {
ret = ERR_PTR(res);
goto abort;
@@ -191,8 +170,8 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
mutex_lock(&data->update_lock);
data->temp[attr->index] = val;
- ds620_write_temp(client, DS620_REG_TEMP[attr->index],
- data->temp[attr->index]);
+ i2c_smbus_write_word_swapped(client, DS620_REG_TEMP[attr->index],
+ data->temp[attr->index]);
mutex_unlock(&data->update_lock);
return count;
}
@@ -210,16 +189,15 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
return PTR_ERR(data);
/* reset alarms if necessary */
- res = i2c_smbus_read_word_data(client, DS620_REG_CONF);
+ res = i2c_smbus_read_word_swapped(client, DS620_REG_CONF);
if (res < 0)
return res;
- conf = swab16(res);
- new_conf = conf;
+ new_conf = conf = res;
new_conf &= ~attr->index;
if (conf != new_conf) {
- res = i2c_smbus_write_word_data(client, DS620_REG_CONF,
- swab16(new_conf));
+ res = i2c_smbus_write_word_swapped(client, DS620_REG_CONF,
+ new_conf);
if (res < 0)
return res;
}
diff --git a/drivers/hwmon/exynos4_tmu.c b/drivers/hwmon/exynos4_tmu.c
new file mode 100644
index 000000000000..faa0884f61f6
--- /dev/null
+++ b/drivers/hwmon/exynos4_tmu.c
@@ -0,0 +1,524 @@
+/*
+ * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/platform_data/exynos4_tmu.h>
+
+#define EXYNOS4_TMU_REG_TRIMINFO 0x0
+#define EXYNOS4_TMU_REG_CONTROL 0x20
+#define EXYNOS4_TMU_REG_STATUS 0x28
+#define EXYNOS4_TMU_REG_CURRENT_TEMP 0x40
+#define EXYNOS4_TMU_REG_THRESHOLD_TEMP 0x44
+#define EXYNOS4_TMU_REG_TRIG_LEVEL0 0x50
+#define EXYNOS4_TMU_REG_TRIG_LEVEL1 0x54
+#define EXYNOS4_TMU_REG_TRIG_LEVEL2 0x58
+#define EXYNOS4_TMU_REG_TRIG_LEVEL3 0x5C
+#define EXYNOS4_TMU_REG_PAST_TEMP0 0x60
+#define EXYNOS4_TMU_REG_PAST_TEMP1 0x64
+#define EXYNOS4_TMU_REG_PAST_TEMP2 0x68
+#define EXYNOS4_TMU_REG_PAST_TEMP3 0x6C
+#define EXYNOS4_TMU_REG_INTEN 0x70
+#define EXYNOS4_TMU_REG_INTSTAT 0x74
+#define EXYNOS4_TMU_REG_INTCLEAR 0x78
+
+#define EXYNOS4_TMU_GAIN_SHIFT 8
+#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT 24
+
+#define EXYNOS4_TMU_TRIM_TEMP_MASK 0xff
+#define EXYNOS4_TMU_CORE_ON 3
+#define EXYNOS4_TMU_CORE_OFF 2
+#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET 50
+#define EXYNOS4_TMU_TRIG_LEVEL0_MASK 0x1
+#define EXYNOS4_TMU_TRIG_LEVEL1_MASK 0x10
+#define EXYNOS4_TMU_TRIG_LEVEL2_MASK 0x100
+#define EXYNOS4_TMU_TRIG_LEVEL3_MASK 0x1000
+#define EXYNOS4_TMU_INTCLEAR_VAL 0x1111
+
+struct exynos4_tmu_data {
+ struct exynos4_tmu_platform_data *pdata;
+ struct device *hwmon_dev;
+ struct resource *mem;
+ void __iomem *base;
+ int irq;
+ struct work_struct irq_work;
+ struct mutex lock;
+ struct clk *clk;
+ u8 temp_error1, temp_error2;
+};
+
+/*
+ * TMU treats temperature as a mapped temperature code.
+ * The temperature is converted differently depending on the calibration type.
+ */
+static int temp_to_code(struct exynos4_tmu_data *data, u8 temp)
+{
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ int temp_code;
+
+ /* temp should range between 25 and 125 */
+ if (temp < 25 || temp > 125) {
+ temp_code = -EINVAL;
+ goto out;
+ }
+
+ switch (pdata->cal_type) {
+ case TYPE_TWO_POINT_TRIMMING:
+ temp_code = (temp - 25) *
+ (data->temp_error2 - data->temp_error1) /
+ (85 - 25) + data->temp_error1;
+ break;
+ case TYPE_ONE_POINT_TRIMMING:
+ temp_code = temp + data->temp_error1 - 25;
+ break;
+ default:
+ temp_code = temp + EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
+ break;
+ }
+out:
+ return temp_code;
+}
+
+/*
+ * Calculate a temperature value from a temperature code.
+ * The unit of the temperature is degree Celsius.
+ */
+static int code_to_temp(struct exynos4_tmu_data *data, u8 temp_code)
+{
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ int temp;
+
+ /* temp_code should range between 75 and 175 */
+ if (temp_code < 75 || temp_code > 175) {
+ temp = -ENODATA;
+ goto out;
+ }
+
+ switch (pdata->cal_type) {
+ case TYPE_TWO_POINT_TRIMMING:
+ temp = (temp_code - data->temp_error1) * (85 - 25) /
+ (data->temp_error2 - data->temp_error1) + 25;
+ break;
+ case TYPE_ONE_POINT_TRIMMING:
+ temp = temp_code - data->temp_error1 + 25;
+ break;
+ default:
+ temp = temp_code - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
+ break;
+ }
+out:
+ return temp;
+}
+
+static int exynos4_tmu_initialize(struct platform_device *pdev)
+{
+ struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ unsigned int status, trim_info;
+ int ret = 0, threshold_code;
+
+ mutex_lock(&data->lock);
+ clk_enable(data->clk);
+
+ status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
+ if (!status) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* Save trimming info in order to perform calibration */
+ trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
+ data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
+ data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
+
+ /* Write temperature code for threshold */
+ threshold_code = temp_to_code(data, pdata->threshold);
+ if (threshold_code < 0) {
+ ret = threshold_code;
+ goto out;
+ }
+ writeb(threshold_code,
+ data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
+
+ writeb(pdata->trigger_levels[0],
+ data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
+ writeb(pdata->trigger_levels[1],
+ data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
+ writeb(pdata->trigger_levels[2],
+ data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
+ writeb(pdata->trigger_levels[3],
+ data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
+
+ writel(EXYNOS4_TMU_INTCLEAR_VAL,
+ data->base + EXYNOS4_TMU_REG_INTCLEAR);
+out:
+ clk_disable(data->clk);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static void exynos4_tmu_control(struct platform_device *pdev, bool on)
+{
+ struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ unsigned int con, interrupt_en;
+
+ mutex_lock(&data->lock);
+ clk_enable(data->clk);
+
+ con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
+ pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
+ if (on) {
+ con |= EXYNOS4_TMU_CORE_ON;
+ interrupt_en = pdata->trigger_level3_en << 12 |
+ pdata->trigger_level2_en << 8 |
+ pdata->trigger_level1_en << 4 |
+ pdata->trigger_level0_en;
+ } else {
+ con |= EXYNOS4_TMU_CORE_OFF;
+ interrupt_en = 0; /* Disable all interrupts */
+ }
+ writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
+ writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
+
+ clk_disable(data->clk);
+ mutex_unlock(&data->lock);
+}
+
+static int exynos4_tmu_read(struct exynos4_tmu_data *data)
+{
+ u8 temp_code;
+ int temp;
+
+ mutex_lock(&data->lock);
+ clk_enable(data->clk);
+
+ temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
+ temp = code_to_temp(data, temp_code);
+
+ clk_disable(data->clk);
+ mutex_unlock(&data->lock);
+
+ return temp;
+}
+
+static void exynos4_tmu_work(struct work_struct *work)
+{
+ struct exynos4_tmu_data *data = container_of(work,
+ struct exynos4_tmu_data, irq_work);
+
+ mutex_lock(&data->lock);
+ clk_enable(data->clk);
+
+ writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
+
+ kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
+
+ enable_irq(data->irq);
+
+ clk_disable(data->clk);
+ mutex_unlock(&data->lock);
+}
+
+static irqreturn_t exynos4_tmu_irq(int irq, void *id)
+{
+ struct exynos4_tmu_data *data = id;
+
+ disable_irq_nosync(irq);
+ schedule_work(&data->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t exynos4_tmu_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "exynos4-tmu\n");
+}
+
+static ssize_t exynos4_tmu_show_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = exynos4_tmu_read(data);
+ if (ret < 0)
+ return ret;
+
+ /* convert from degree Celsius to millidegree Celsius */
+ return sprintf(buf, "%d\n", ret * 1000);
+}
+
+static ssize_t exynos4_tmu_show_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ int temp;
+ unsigned int trigger_level;
+
+ temp = exynos4_tmu_read(data);
+ if (temp < 0)
+ return temp;
+
+ trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
+
+ return sprintf(buf, "%d\n", !!(temp > trigger_level));
+}
+
+static ssize_t exynos4_tmu_show_level(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ unsigned int temp = pdata->threshold +
+ pdata->trigger_levels[attr->index];
+
+ return sprintf(buf, "%u\n", temp * 1000);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
+ exynos4_tmu_show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
+ exynos4_tmu_show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
+ exynos4_tmu_show_alarm, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
+ exynos4_tmu_show_level, NULL, 3);
+
+static struct attribute *exynos4_tmu_attributes[] = {
+ &dev_attr_name.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_emergency.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group exynos4_tmu_attr_group = {
+ .attrs = exynos4_tmu_attributes,
+};
+
+static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
+{
+ struct exynos4_tmu_data *data;
+ struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform init data supplied.\n");
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(struct exynos4_tmu_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev, "Failed to allocate driver structure\n");
+ return -ENOMEM;
+ }
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (data->irq < 0) {
+ ret = data->irq;
+ dev_err(&pdev->dev, "Failed to get platform irq\n");
+ goto err_free;
+ }
+
+ INIT_WORK(&data->irq_work, exynos4_tmu_work);
+
+ data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!data->mem) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get platform resource\n");
+ goto err_free;
+ }
+
+ data->mem = request_mem_region(data->mem->start,
+ resource_size(data->mem), pdev->name);
+ if (!data->mem) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "Failed to request memory region\n");
+ goto err_free;
+ }
+
+ data->base = ioremap(data->mem->start, resource_size(data->mem));
+ if (!data->base) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "Failed to ioremap memory\n");
+ goto err_mem_region;
+ }
+
+ ret = request_irq(data->irq, exynos4_tmu_irq,
+ IRQF_TRIGGER_RISING,
+ "exynos4-tmu", data);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
+ goto err_io_remap;
+ }
+
+ data->clk = clk_get(NULL, "tmu_apbif");
+ if (IS_ERR(data->clk)) {
+ ret = PTR_ERR(data->clk);
+ dev_err(&pdev->dev, "Failed to get clock\n");
+ goto err_irq;
+ }
+
+ data->pdata = pdata;
+ platform_set_drvdata(pdev, data);
+ mutex_init(&data->lock);
+
+ ret = exynos4_tmu_initialize(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to initialize TMU\n");
+ goto err_clk;
+ }
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create sysfs group\n");
+ goto err_clk;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ ret = PTR_ERR(data->hwmon_dev);
+ dev_err(&pdev->dev, "Failed to register hwmon device\n");
+ goto err_create_group;
+ }
+
+ exynos4_tmu_control(pdev, true);
+
+ return 0;
+
+err_create_group:
+ sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+err_clk:
+ platform_set_drvdata(pdev, NULL);
+ clk_put(data->clk);
+err_irq:
+ free_irq(data->irq, data);
+err_io_remap:
+ iounmap(data->base);
+err_mem_region:
+ release_mem_region(data->mem->start, resource_size(data->mem));
+err_free:
+ kfree(data);
+
+ return ret;
+}
+
+static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
+{
+ struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+
+ exynos4_tmu_control(pdev, false);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+
+ clk_put(data->clk);
+
+ free_irq(data->irq, data);
+
+ iounmap(data->base);
+ release_mem_region(data->mem->start, resource_size(data->mem));
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ exynos4_tmu_control(pdev, false);
+
+ return 0;
+}
+
+static int exynos4_tmu_resume(struct platform_device *pdev)
+{
+ exynos4_tmu_initialize(pdev);
+ exynos4_tmu_control(pdev, true);
+
+ return 0;
+}
+#else
+#define exynos4_tmu_suspend NULL
+#define exynos4_tmu_resume NULL
+#endif
+
+static struct platform_driver exynos4_tmu_driver = {
+ .driver = {
+ .name = "exynos4-tmu",
+ .owner = THIS_MODULE,
+ },
+ .probe = exynos4_tmu_probe,
+ .remove = __devexit_p(exynos4_tmu_remove),
+ .suspend = exynos4_tmu_suspend,
+ .resume = exynos4_tmu_resume,
+};
+
+static int __init exynos4_tmu_driver_init(void)
+{
+ return platform_driver_register(&exynos4_tmu_driver);
+}
+module_init(exynos4_tmu_driver_init);
+
+static void __exit exynos4_tmu_driver_exit(void)
+{
+ platform_driver_unregister(&exynos4_tmu_driver);
+}
+module_exit(exynos4_tmu_driver_exit);
+
+MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos4-tmu");
diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c
index 2d96ed2bf8ed..59dd881c71d8 100644
--- a/drivers/hwmon/f71882fg.c
+++ b/drivers/hwmon/f71882fg.c
@@ -605,7 +605,7 @@ static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = {
/* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the
standard models */
-static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
+static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[3][7] = { {
SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 0),
@@ -627,7 +627,7 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
0, 0),
SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 0),
-
+}, {
SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 1),
@@ -649,7 +649,7 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
0, 1),
SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
-
+}, {
SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 2),
@@ -671,12 +671,12 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
0, 2),
SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
-};
+} };
/* PWM attr for the f71808e/f71869, almost identical to the f71862fg, but the
pwm setting when the temperature is above the pwmX_auto_point1_temp can be
programmed instead of being hardcoded to 0xff */
-static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = {
+static struct sensor_device_attribute_2 f71869_auto_pwm_attr[3][8] = { {
SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 0),
@@ -701,7 +701,7 @@ static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = {
0, 0),
SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 0),
-
+}, {
SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 1),
@@ -726,7 +726,7 @@ static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = {
0, 1),
SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
-
+}, {
SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 2),
@@ -751,7 +751,7 @@ static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = {
0, 2),
SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
-};
+} };
/* PWM attr for the standard models */
static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { {
@@ -928,7 +928,7 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = {
/* PWM attr for the f8000, zones mapped to temp instead of to pwm!
Also the register block at offset A0 maps to TEMP1 (so our temp2, as the
F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */
-static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = {
+static struct sensor_device_attribute_2 f8000_auto_pwm_attr[3][14] = { {
SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 0),
@@ -969,7 +969,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 2, 2),
SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
-
+}, {
SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 1),
@@ -1010,7 +1010,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 2, 0),
SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 0),
-
+}, {
SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 2),
@@ -1051,7 +1051,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 2, 1),
SENSOR_ATTR_2(temp3_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
-};
+} };
/* Super I/O functions */
static inline int superio_inb(int base, int reg)
@@ -2154,6 +2154,104 @@ static void f71882fg_remove_sysfs_files(struct platform_device *pdev,
device_remove_file(&pdev->dev, &attr[i].dev_attr);
}
+static int __devinit f71882fg_create_fan_sysfs_files(
+ struct platform_device *pdev, int idx)
+{
+ struct f71882fg_data *data = platform_get_drvdata(pdev);
+ int err;
+
+ /* Sanity check the pwm setting */
+ err = 0;
+ switch (data->type) {
+ case f71858fg:
+ if (((data->pwm_enable >> (idx * 2)) & 3) == 3)
+ err = 1;
+ break;
+ case f71862fg:
+ if (((data->pwm_enable >> (idx * 2)) & 1) != 1)
+ err = 1;
+ break;
+ case f8000:
+ if (idx == 2)
+ err = data->pwm_enable & 0x20;
+ break;
+ default:
+ break;
+ }
+ if (err) {
+ dev_err(&pdev->dev,
+ "Invalid (reserved) pwm settings: 0x%02x, "
+ "skipping fan %d\n",
+ (data->pwm_enable >> (idx * 2)) & 3, idx + 1);
+ return 0; /* This is a non fatal condition */
+ }
+
+ err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[idx][0],
+ ARRAY_SIZE(fxxxx_fan_attr[0]));
+ if (err)
+ return err;
+
+ if (f71882fg_fan_has_beep[data->type]) {
+ err = f71882fg_create_sysfs_files(pdev,
+ &fxxxx_fan_beep_attr[idx],
+ 1);
+ if (err)
+ return err;
+ }
+
+ dev_info(&pdev->dev, "Fan: %d is in %s mode\n", idx + 1,
+ (data->pwm_enable & (1 << (2 * idx))) ? "duty-cycle" : "RPM");
+
+ /* Check for unsupported auto pwm settings */
+ switch (data->type) {
+ case f71808e:
+ case f71808a:
+ case f71869:
+ case f71869a:
+ case f71889fg:
+ case f71889ed:
+ case f71889a:
+ data->pwm_auto_point_mapping[idx] =
+ f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(idx));
+ if ((data->pwm_auto_point_mapping[idx] & 0x80) ||
+ (data->pwm_auto_point_mapping[idx] & 3) == 0) {
+ dev_warn(&pdev->dev,
+ "Auto pwm controlled by raw digital "
+ "data, disabling pwm auto_point "
+ "sysfs attributes for fan %d\n", idx + 1);
+ return 0; /* This is a non fatal condition */
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (data->type) {
+ case f71862fg:
+ err = f71882fg_create_sysfs_files(pdev,
+ &f71862fg_auto_pwm_attr[idx][0],
+ ARRAY_SIZE(f71862fg_auto_pwm_attr[0]));
+ break;
+ case f71808e:
+ case f71869:
+ err = f71882fg_create_sysfs_files(pdev,
+ &f71869_auto_pwm_attr[idx][0],
+ ARRAY_SIZE(f71869_auto_pwm_attr[0]));
+ break;
+ case f8000:
+ err = f71882fg_create_sysfs_files(pdev,
+ &f8000_auto_pwm_attr[idx][0],
+ ARRAY_SIZE(f8000_auto_pwm_attr[0]));
+ break;
+ default:
+ err = f71882fg_create_sysfs_files(pdev,
+ &fxxxx_auto_pwm_attr[idx][0],
+ ARRAY_SIZE(fxxxx_auto_pwm_attr[0]));
+ }
+
+ return err;
+}
+
static int __devinit f71882fg_probe(struct platform_device *pdev)
{
struct f71882fg_data *data;
@@ -2272,117 +2370,29 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
data->pwm_enable =
f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
- /* Sanity check the pwm settings */
- switch (data->type) {
- case f71858fg:
- err = 0;
- for (i = 0; i < nr_fans; i++)
- if (((data->pwm_enable >> (i * 2)) & 3) == 3)
- err = 1;
- break;
- case f71862fg:
- err = (data->pwm_enable & 0x15) != 0x15;
- break;
- case f8000:
- err = data->pwm_enable & 0x20;
- break;
- default:
- err = 0;
- break;
- }
- if (err) {
- dev_err(&pdev->dev,
- "Invalid (reserved) pwm settings: 0x%02x\n",
- (unsigned int)data->pwm_enable);
- err = -ENODEV;
- goto exit_unregister_sysfs;
- }
-
- err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[0][0],
- ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans);
- if (err)
- goto exit_unregister_sysfs;
-
- if (f71882fg_fan_has_beep[data->type]) {
- err = f71882fg_create_sysfs_files(pdev,
- fxxxx_fan_beep_attr, nr_fans);
+ for (i = 0; i < nr_fans; i++) {
+ err = f71882fg_create_fan_sysfs_files(pdev, i);
if (err)
goto exit_unregister_sysfs;
}
- switch (data->type) {
- case f71808e:
- case f71808a:
- case f71869:
- case f71869a:
- case f71889fg:
- case f71889ed:
- case f71889a:
- for (i = 0; i < nr_fans; i++) {
- data->pwm_auto_point_mapping[i] =
- f71882fg_read8(data,
- F71882FG_REG_POINT_MAPPING(i));
- if ((data->pwm_auto_point_mapping[i] & 0x80) ||
- (data->pwm_auto_point_mapping[i] & 3) == 0)
- break;
- }
- if (i != nr_fans) {
- dev_warn(&pdev->dev,
- "Auto pwm controlled by raw digital "
- "data, disabling pwm auto_point "
- "sysfs attributes\n");
- goto no_pwm_auto_point;
- }
- break;
- default:
- break;
- }
-
+ /* Some types have 1 extra fan with limited functionality */
switch (data->type) {
case f71808a:
err = f71882fg_create_sysfs_files(pdev,
- &fxxxx_auto_pwm_attr[0][0],
- ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
- if (err)
- goto exit_unregister_sysfs;
- err = f71882fg_create_sysfs_files(pdev,
f71808a_fan3_attr,
ARRAY_SIZE(f71808a_fan3_attr));
break;
- case f71862fg:
- err = f71882fg_create_sysfs_files(pdev,
- f71862fg_auto_pwm_attr,
- ARRAY_SIZE(f71862fg_auto_pwm_attr));
- break;
- case f71808e:
- case f71869:
- err = f71882fg_create_sysfs_files(pdev,
- f71869_auto_pwm_attr,
- ARRAY_SIZE(f71869_auto_pwm_attr));
- break;
case f8000:
err = f71882fg_create_sysfs_files(pdev,
f8000_fan_attr,
ARRAY_SIZE(f8000_fan_attr));
- if (err)
- goto exit_unregister_sysfs;
- err = f71882fg_create_sysfs_files(pdev,
- f8000_auto_pwm_attr,
- ARRAY_SIZE(f8000_auto_pwm_attr));
break;
default:
- err = f71882fg_create_sysfs_files(pdev,
- &fxxxx_auto_pwm_attr[0][0],
- ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
+ break;
}
if (err)
goto exit_unregister_sysfs;
-
-no_pwm_auto_point:
- for (i = 0; i < nr_fans; i++)
- dev_info(&pdev->dev, "Fan: %d is in %s mode\n", i + 1,
- (data->pwm_enable & (1 << 2 * i)) ?
- "duty-cycle" : "RPM");
}
data->hwmon_dev = hwmon_device_register(&pdev->dev);
@@ -2476,22 +2486,23 @@ static int f71882fg_remove(struct platform_device *pdev)
break;
case f71862fg:
f71882fg_remove_sysfs_files(pdev,
- f71862fg_auto_pwm_attr,
- ARRAY_SIZE(f71862fg_auto_pwm_attr));
+ &f71862fg_auto_pwm_attr[0][0],
+ ARRAY_SIZE(f71862fg_auto_pwm_attr[0]) *
+ nr_fans);
break;
case f71808e:
case f71869:
f71882fg_remove_sysfs_files(pdev,
- f71869_auto_pwm_attr,
- ARRAY_SIZE(f71869_auto_pwm_attr));
+ &f71869_auto_pwm_attr[0][0],
+ ARRAY_SIZE(f71869_auto_pwm_attr[0]) * nr_fans);
break;
case f8000:
f71882fg_remove_sysfs_files(pdev,
f8000_fan_attr,
ARRAY_SIZE(f8000_fan_attr));
f71882fg_remove_sysfs_files(pdev,
- f8000_auto_pwm_attr,
- ARRAY_SIZE(f8000_auto_pwm_attr));
+ &f8000_auto_pwm_attr[0][0],
+ ARRAY_SIZE(f8000_auto_pwm_attr[0]) * nr_fans);
break;
default:
f71882fg_remove_sysfs_files(pdev,
diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c
index e7ae5743e181..a13e2da97e30 100644
--- a/drivers/hwmon/gl518sm.c
+++ b/drivers/hwmon/gl518sm.c
@@ -591,7 +591,7 @@ static int gl518_remove(struct i2c_client *client)
static int gl518_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
- return swab16(i2c_smbus_read_word_data(client, reg));
+ return i2c_smbus_read_word_swapped(client, reg);
else
return i2c_smbus_read_byte_data(client, reg);
}
@@ -599,7 +599,7 @@ static int gl518_read_value(struct i2c_client *client, u8 reg)
static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
- return i2c_smbus_write_word_data(client, reg, swab16(value));
+ return i2c_smbus_write_word_swapped(client, reg, value);
else
return i2c_smbus_write_byte_data(client, reg, value);
}
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
index ec588026f0a9..cd6085bbfba7 100644
--- a/drivers/hwmon/gl520sm.c
+++ b/drivers/hwmon/gl520sm.c
@@ -273,7 +273,7 @@ static SENSOR_DEVICE_ATTR(in4_max, S_IRUGO | S_IWUSR,
#define DIV_FROM_REG(val) (1 << (val))
#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000/((val) << (div))))
-#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255));
+#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255))
static ssize_t get_fan_input(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -821,7 +821,7 @@ static int gl520_remove(struct i2c_client *client)
static int gl520_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
- return swab16(i2c_smbus_read_word_data(client, reg));
+ return i2c_smbus_read_word_swapped(client, reg);
else
return i2c_smbus_read_byte_data(client, reg);
}
@@ -829,7 +829,7 @@ static int gl520_read_value(struct i2c_client *client, u8 reg)
static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
- return i2c_smbus_write_word_data(client, reg, swab16(value));
+ return i2c_smbus_write_word_swapped(client, reg, value);
else
return i2c_smbus_write_byte_data(client, reg, value);
}
diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c
index c8195a077da3..932da8a5aaf4 100644
--- a/drivers/hwmon/hwmon-vid.c
+++ b/drivers/hwmon/hwmon-vid.c
@@ -140,7 +140,11 @@ int vid_from_reg(int val, u8 vrm)
return(val & 0x10 ? 975 - (val & 0xF) * 25 :
1750 - val * 50);
case 13:
+ case 131:
val &= 0x3f;
+ /* Exception for Eden ULV 500 MHz */
+ if (vrm == 131 && val == 0x3f)
+ val++;
return(1708 - val * 16);
case 14: /* Intel Core */
/* compute in uV, round to mV */
@@ -205,11 +209,45 @@ static struct vrm_model vrm_models[] = {
{X86_VENDOR_CENTAUR, 0x6, 0x9, 0x7, 85}, /* Nehemiah */
{X86_VENDOR_CENTAUR, 0x6, 0x9, ANY, 17}, /* C3-M, Eden-N */
{X86_VENDOR_CENTAUR, 0x6, 0xA, 0x7, 0}, /* No information */
- {X86_VENDOR_CENTAUR, 0x6, 0xA, ANY, 13}, /* C7, Esther */
+ {X86_VENDOR_CENTAUR, 0x6, 0xA, ANY, 13}, /* C7-M, C7, Eden (Esther) */
+ {X86_VENDOR_CENTAUR, 0x6, 0xD, ANY, 134}, /* C7-D, C7-M, C7, Eden (Esther) */
{X86_VENDOR_UNKNOWN, ANY, ANY, ANY, 0} /* stop here */
};
+/*
+ * Special case for VIA model D: there are two different possible
+ * VID tables, so we have to figure out first, which one must be
+ * used. This resolves temporary drm value 134 to 14 (Intel Core
+ * 7-bit VID), 13 (Pentium M 6-bit VID) or 131 (Pentium M 6-bit VID
+ * + quirk for Eden ULV 500 MHz).
+ * Note: something similar might be needed for model A, I'm not sure.
+ */
+static u8 get_via_model_d_vrm(void)
+{
+ unsigned int vid, brand, dummy;
+ static const char *brands[4] = {
+ "C7-M", "C7", "Eden", "C7-D"
+ };
+
+ rdmsr(0x198, dummy, vid);
+ vid &= 0xff;
+
+ rdmsr(0x1154, brand, dummy);
+ brand = ((brand >> 4) ^ (brand >> 2)) & 0x03;
+
+ if (vid > 0x3f) {
+ pr_info("Using %d-bit VID table for VIA %s CPU\n",
+ 7, brands[brand]);
+ return 14;
+ } else {
+ pr_info("Using %d-bit VID table for VIA %s CPU\n",
+ 6, brands[brand]);
+ /* Enable quirk for Eden */
+ return brand == 2 ? 131 : 13;
+ }
+}
+
static u8 find_vrm(u8 eff_family, u8 eff_model, u8 eff_stepping, u8 vendor)
{
int i = 0;
@@ -247,6 +285,8 @@ u8 vid_which_vrm(void)
eff_model += ((eax & 0x000F0000)>>16)<<4;
}
vrm_ret = find_vrm(eff_family, eff_model, eff_stepping, c->x86_vendor);
+ if (vrm_ret == 134)
+ vrm_ret = get_via_model_d_vrm();
if (vrm_ret == 0)
pr_info("Unknown VRM version of your x86 CPU\n");
return vrm_ret;
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index a61e7815a2a9..6460487e41b5 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -27,8 +27,7 @@
static struct class *hwmon_class;
-static DEFINE_IDR(hwmon_idr);
-static DEFINE_SPINLOCK(idr_lock);
+static DEFINE_IDA(hwmon_ida);
/**
* hwmon_device_register - register w/ hwmon
@@ -42,30 +41,17 @@ static DEFINE_SPINLOCK(idr_lock);
struct device *hwmon_device_register(struct device *dev)
{
struct device *hwdev;
- int id, err;
-
-again:
- if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0))
- return ERR_PTR(-ENOMEM);
-
- spin_lock(&idr_lock);
- err = idr_get_new(&hwmon_idr, NULL, &id);
- spin_unlock(&idr_lock);
+ int id;
- if (unlikely(err == -EAGAIN))
- goto again;
- else if (unlikely(err))
- return ERR_PTR(err);
+ id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
+ if (id < 0)
+ return ERR_PTR(id);
- id = id & MAX_ID_MASK;
hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL,
HWMON_ID_FORMAT, id);
- if (IS_ERR(hwdev)) {
- spin_lock(&idr_lock);
- idr_remove(&hwmon_idr, id);
- spin_unlock(&idr_lock);
- }
+ if (IS_ERR(hwdev))
+ ida_simple_remove(&hwmon_ida, id);
return hwdev;
}
@@ -81,9 +67,7 @@ void hwmon_device_unregister(struct device *dev)
if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) {
device_unregister(dev);
- spin_lock(&idr_lock);
- idr_remove(&hwmon_idr, id);
- spin_unlock(&idr_lock);
+ ida_simple_remove(&hwmon_ida, id);
} else
dev_dbg(dev->parent,
"hwmon_device_unregister() failed: bad class ID!\n");
diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c
index c4c40be0edbf..d22f241b6a67 100644
--- a/drivers/hwmon/i5k_amb.c
+++ b/drivers/hwmon/i5k_amb.c
@@ -114,7 +114,6 @@ struct i5k_amb_data {
void __iomem *amb_mmio;
struct i5k_device_attribute *attrs;
unsigned int num_attrs;
- unsigned long chipset_id;
};
static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
@@ -444,8 +443,6 @@ static int __devinit i5k_find_amb_registers(struct i5k_amb_data *data,
goto out;
}
- data->chipset_id = devid;
-
res = 0;
out:
pci_dev_put(pcidev);
@@ -478,23 +475,13 @@ out:
return res;
}
-static unsigned long i5k_channel_pci_id(struct i5k_amb_data *data,
- unsigned long channel)
-{
- switch (data->chipset_id) {
- case PCI_DEVICE_ID_INTEL_5000_ERR:
- return PCI_DEVICE_ID_INTEL_5000_FBD0 + channel;
- case PCI_DEVICE_ID_INTEL_5400_ERR:
- return PCI_DEVICE_ID_INTEL_5400_FBD0 + channel;
- default:
- BUG();
- }
-}
-
-static unsigned long chipset_ids[] = {
- PCI_DEVICE_ID_INTEL_5000_ERR,
- PCI_DEVICE_ID_INTEL_5400_ERR,
- 0
+static struct {
+ unsigned long err;
+ unsigned long fbd0;
+} chipset_ids[] __devinitdata = {
+ { PCI_DEVICE_ID_INTEL_5000_ERR, PCI_DEVICE_ID_INTEL_5000_FBD0 },
+ { PCI_DEVICE_ID_INTEL_5400_ERR, PCI_DEVICE_ID_INTEL_5400_FBD0 },
+ { 0, 0 }
};
#ifdef MODULE
@@ -510,8 +497,7 @@ static int __devinit i5k_amb_probe(struct platform_device *pdev)
{
struct i5k_amb_data *data;
struct resource *reso;
- int i;
- int res = -ENODEV;
+ int i, res;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
@@ -520,22 +506,22 @@ static int __devinit i5k_amb_probe(struct platform_device *pdev)
/* Figure out where the AMB registers live */
i = 0;
do {
- res = i5k_find_amb_registers(data, chipset_ids[i]);
+ res = i5k_find_amb_registers(data, chipset_ids[i].err);
+ if (res == 0)
+ break;
i++;
- } while (res && chipset_ids[i]);
+ } while (chipset_ids[i].err);
if (res)
goto err;
/* Copy the DIMM presence map for the first two channels */
- res = i5k_channel_probe(&data->amb_present[0],
- i5k_channel_pci_id(data, 0));
+ res = i5k_channel_probe(&data->amb_present[0], chipset_ids[i].fbd0);
if (res)
goto err;
/* Copy the DIMM presence map for the optional second two channels */
- i5k_channel_probe(&data->amb_present[2],
- i5k_channel_pci_id(data, 1));
+ i5k_channel_probe(&data->amb_present[2], chipset_ids[i].fbd0 + 1);
/* Set up resource regions */
reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME);
diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
index 1a409c5bc9bc..6a967d7dbdee 100644
--- a/drivers/hwmon/ibmaem.c
+++ b/drivers/hwmon/ibmaem.c
@@ -88,8 +88,7 @@
#define AEM_MIN_POWER_INTERVAL 200
#define UJ_PER_MJ 1000L
-static DEFINE_IDR(aem_idr);
-static DEFINE_SPINLOCK(aem_idr_lock);
+static DEFINE_IDA(aem_ida);
static struct platform_driver aem_driver = {
.driver = {
@@ -148,8 +147,9 @@ struct aem_data {
int id;
struct aem_ipmi_data ipmi;
- /* Function to update sensors */
+ /* Function and buffer to update sensors */
void (*update)(struct aem_data *data);
+ struct aem_read_sensor_resp *rs_resp;
/*
* AEM 1.x sensors:
@@ -246,8 +246,6 @@ static void aem_bmc_gone(int iface);
static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
static void aem_remove_sensors(struct aem_data *data);
-static int aem_init_aem1(struct aem_ipmi_data *probe);
-static int aem_init_aem2(struct aem_ipmi_data *probe);
static int aem1_find_sensors(struct aem_data *data);
static int aem2_find_sensors(struct aem_data *data);
static void update_aem1_sensors(struct aem_data *data);
@@ -356,47 +354,16 @@ static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
complete(&data->read_complete);
}
-/* ID functions */
-
-/* Obtain an id */
-static int aem_idr_get(int *id)
-{
- int i, err;
-
-again:
- if (unlikely(!idr_pre_get(&aem_idr, GFP_KERNEL)))
- return -ENOMEM;
-
- spin_lock(&aem_idr_lock);
- err = idr_get_new(&aem_idr, NULL, &i);
- spin_unlock(&aem_idr_lock);
-
- if (unlikely(err == -EAGAIN))
- goto again;
- else if (unlikely(err))
- return err;
-
- *id = i & MAX_ID_MASK;
- return 0;
-}
-
-/* Release an object ID */
-static void aem_idr_put(int id)
-{
- spin_lock(&aem_idr_lock);
- idr_remove(&aem_idr, id);
- spin_unlock(&aem_idr_lock);
-}
-
/* Sensor support functions */
-/* Read a sensor value */
+/* Read a sensor value; must be called with data->lock held */
static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg,
void *buf, size_t size)
{
int rs_size, res;
struct aem_read_sensor_req rs_req;
- struct aem_read_sensor_resp *rs_resp;
+ /* Use preallocated rx buffer */
+ struct aem_read_sensor_resp *rs_resp = data->rs_resp;
struct aem_ipmi_data *ipmi = &data->ipmi;
/* AEM registers are 1, 2, 4 or 8 bytes */
@@ -422,23 +389,21 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg,
ipmi->tx_message.data_len = sizeof(rs_req);
rs_size = sizeof(*rs_resp) + size;
- rs_resp = kzalloc(rs_size, GFP_KERNEL);
- if (!rs_resp)
- return -ENOMEM;
-
ipmi->rx_msg_data = rs_resp;
ipmi->rx_msg_len = rs_size;
aem_send_message(ipmi);
res = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT);
- if (!res)
- return -ETIMEDOUT;
+ if (!res) {
+ res = -ETIMEDOUT;
+ goto out;
+ }
if (ipmi->rx_result || ipmi->rx_msg_len != rs_size ||
memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) {
- kfree(rs_resp);
- return -ENOENT;
+ res = -ENOENT;
+ goto out;
}
switch (size) {
@@ -463,8 +428,10 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg,
break;
}
}
+ res = 0;
- return 0;
+out:
+ return res;
}
/* Update AEM energy registers */
@@ -521,11 +488,12 @@ static void aem_delete(struct aem_data *data)
{
list_del(&data->list);
aem_remove_sensors(data);
+ kfree(data->rs_resp);
hwmon_device_unregister(data->hwmon_dev);
ipmi_destroy_user(data->ipmi.user);
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
- aem_idr_put(data->id);
+ ida_simple_remove(&aem_ida, data->id);
kfree(data);
}
@@ -582,7 +550,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
/* Create sub-device for this fw instance */
- if (aem_idr_get(&data->id))
+ data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
+ if (data->id < 0)
goto id_err;
data->pdev = platform_device_alloc(DRVNAME, data->id);
@@ -597,24 +566,31 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
platform_set_drvdata(data->pdev, data);
/* Set up IPMI interface */
- if (aem_init_ipmi_data(&data->ipmi, probe->interface,
- probe->bmc_device))
+ res = aem_init_ipmi_data(&data->ipmi, probe->interface,
+ probe->bmc_device);
+ if (res)
goto ipmi_err;
/* Register with hwmon */
data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
-
if (IS_ERR(data->hwmon_dev)) {
dev_err(&data->pdev->dev, "Unable to register hwmon "
"device for IPMI interface %d\n",
probe->interface);
+ res = PTR_ERR(data->hwmon_dev);
goto hwmon_reg_err;
}
data->update = update_aem1_sensors;
+ data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL);
+ if (!data->rs_resp) {
+ res = -ENOMEM;
+ goto alloc_resp_err;
+ }
/* Find sensors */
- if (aem1_find_sensors(data))
+ res = aem1_find_sensors(data);
+ if (res)
goto sensor_err;
/* Add to our list of AEM devices */
@@ -626,6 +602,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
return 0;
sensor_err:
+ kfree(data->rs_resp);
+alloc_resp_err:
hwmon_device_unregister(data->hwmon_dev);
hwmon_reg_err:
ipmi_destroy_user(data->ipmi.user);
@@ -633,7 +611,7 @@ ipmi_err:
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
dev_err:
- aem_idr_put(data->id);
+ ida_simple_remove(&aem_ida, data->id);
id_err:
kfree(data);
@@ -641,7 +619,7 @@ id_err:
}
/* Find and initialize all AEM1 instances */
-static int aem_init_aem1(struct aem_ipmi_data *probe)
+static void aem_init_aem1(struct aem_ipmi_data *probe)
{
int num, i, err;
@@ -652,11 +630,8 @@ static int aem_init_aem1(struct aem_ipmi_data *probe)
dev_err(probe->bmc_device,
"Error %d initializing AEM1 0x%X\n",
err, i);
- return err;
}
}
-
- return 0;
}
/* Probe functions for AEM2 devices */
@@ -715,7 +690,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
/* Create sub-device for this fw instance */
- if (aem_idr_get(&data->id))
+ data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
+ if (data->id < 0)
goto id_err;
data->pdev = platform_device_alloc(DRVNAME, data->id);
@@ -730,24 +706,31 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
platform_set_drvdata(data->pdev, data);
/* Set up IPMI interface */
- if (aem_init_ipmi_data(&data->ipmi, probe->interface,
- probe->bmc_device))
+ res = aem_init_ipmi_data(&data->ipmi, probe->interface,
+ probe->bmc_device);
+ if (res)
goto ipmi_err;
/* Register with hwmon */
data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
-
if (IS_ERR(data->hwmon_dev)) {
dev_err(&data->pdev->dev, "Unable to register hwmon "
"device for IPMI interface %d\n",
probe->interface);
+ res = PTR_ERR(data->hwmon_dev);
goto hwmon_reg_err;
}
data->update = update_aem2_sensors;
+ data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL);
+ if (!data->rs_resp) {
+ res = -ENOMEM;
+ goto alloc_resp_err;
+ }
/* Find sensors */
- if (aem2_find_sensors(data))
+ res = aem2_find_sensors(data);
+ if (res)
goto sensor_err;
/* Add to our list of AEM devices */
@@ -759,6 +742,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
return 0;
sensor_err:
+ kfree(data->rs_resp);
+alloc_resp_err:
hwmon_device_unregister(data->hwmon_dev);
hwmon_reg_err:
ipmi_destroy_user(data->ipmi.user);
@@ -766,7 +751,7 @@ ipmi_err:
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
dev_err:
- aem_idr_put(data->id);
+ ida_simple_remove(&aem_ida, data->id);
id_err:
kfree(data);
@@ -774,7 +759,7 @@ id_err:
}
/* Find and initialize all AEM2 instances */
-static int aem_init_aem2(struct aem_ipmi_data *probe)
+static void aem_init_aem2(struct aem_ipmi_data *probe)
{
struct aem_find_instance_resp fi_resp;
int err;
@@ -793,12 +778,9 @@ static int aem_init_aem2(struct aem_ipmi_data *probe)
dev_err(probe->bmc_device,
"Error %d initializing AEM2 0x%X\n",
err, fi_resp.module_handle);
- return err;
}
i++;
}
-
- return 0;
}
/* Probe a BMC for AEM firmware instances */
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index bb6405b92007..d912649fac50 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -1172,6 +1172,32 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
struct it87_data *data = it87_update_device(dev);
return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
}
+
+static ssize_t clear_intrusion(struct device *dev, struct device_attribute
+ *attr, const char *buf, size_t count)
+{
+ struct it87_data *data = dev_get_drvdata(dev);
+ long val;
+ int config;
+
+ if (strict_strtol(buf, 10, &val) < 0 || val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ config = it87_read_value(data, IT87_REG_CONFIG);
+ if (config < 0) {
+ count = config;
+ } else {
+ config |= 1 << 5;
+ it87_write_value(data, IT87_REG_CONFIG, config);
+ /* Invalidate cache to force re-read */
+ data->valid = 0;
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 8);
static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 9);
static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 10);
@@ -1188,6 +1214,8 @@ static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 6);
static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16);
static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17);
static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18);
+static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR,
+ show_alarm, clear_intrusion, 4);
static ssize_t show_beep(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -1350,6 +1378,7 @@ static struct attribute *it87_attributes[] = {
&sensor_dev_attr_temp3_alarm.dev_attr.attr,
&dev_attr_alarms.attr,
+ &sensor_dev_attr_intrusion0_alarm.dev_attr.attr,
&dev_attr_name.attr,
NULL
};
@@ -1538,7 +1567,7 @@ static struct attribute *it87_attributes_label[] = {
};
static const struct attribute_group it87_group_label = {
- .attrs = it87_attributes_vid,
+ .attrs = it87_attributes_label,
};
/* SuperIO detection - will change isa_address if a chip is found */
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
index 02cebb74e206..2d3d72805ff4 100644
--- a/drivers/hwmon/jc42.c
+++ b/drivers/hwmon/jc42.c
@@ -154,8 +154,6 @@ static int jc42_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info);
static int jc42_remove(struct i2c_client *client);
-static int jc42_read_value(struct i2c_client *client, u8 reg);
-static int jc42_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct jc42_data *jc42_update_device(struct device *dev);
@@ -187,7 +185,7 @@ static int jc42_suspend(struct device *dev)
struct jc42_data *data = i2c_get_clientdata(client);
data->config |= JC42_CFG_SHUTDOWN;
- jc42_write_value(client, JC42_REG_CONFIG, data->config);
+ i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, data->config);
return 0;
}
@@ -197,7 +195,7 @@ static int jc42_resume(struct device *dev)
struct jc42_data *data = i2c_get_clientdata(client);
data->config &= ~JC42_CFG_SHUTDOWN;
- jc42_write_value(client, JC42_REG_CONFIG, data->config);
+ i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, data->config);
return 0;
}
@@ -315,7 +313,7 @@ static ssize_t set_##value(struct device *dev, \
return -EINVAL; \
mutex_lock(&data->update_lock); \
data->value = jc42_temp_to_reg(val, data->extended); \
- err = jc42_write_value(client, reg, data->value); \
+ err = i2c_smbus_write_word_swapped(client, reg, data->value); \
if (err < 0) \
ret = err; \
mutex_unlock(&data->update_lock); \
@@ -357,7 +355,8 @@ static ssize_t set_temp_crit_hyst(struct device *dev,
data->config = (data->config
& ~(JC42_CFG_HYST_MASK << JC42_CFG_HYST_SHIFT))
| (hyst << JC42_CFG_HYST_SHIFT);
- err = jc42_write_value(client, JC42_REG_CONFIG, data->config);
+ err = i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG,
+ data->config);
if (err < 0)
ret = err;
mutex_unlock(&data->update_lock);
@@ -452,10 +451,10 @@ static int jc42_detect(struct i2c_client *new_client,
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
- cap = jc42_read_value(new_client, JC42_REG_CAP);
- config = jc42_read_value(new_client, JC42_REG_CONFIG);
- manid = jc42_read_value(new_client, JC42_REG_MANID);
- devid = jc42_read_value(new_client, JC42_REG_DEVICEID);
+ cap = i2c_smbus_read_word_swapped(new_client, JC42_REG_CAP);
+ config = i2c_smbus_read_word_swapped(new_client, JC42_REG_CONFIG);
+ manid = i2c_smbus_read_word_swapped(new_client, JC42_REG_MANID);
+ devid = i2c_smbus_read_word_swapped(new_client, JC42_REG_DEVICEID);
if (cap < 0 || config < 0 || manid < 0 || devid < 0)
return -ENODEV;
@@ -489,14 +488,14 @@ static int jc42_probe(struct i2c_client *new_client,
i2c_set_clientdata(new_client, data);
mutex_init(&data->update_lock);
- cap = jc42_read_value(new_client, JC42_REG_CAP);
+ cap = i2c_smbus_read_word_swapped(new_client, JC42_REG_CAP);
if (cap < 0) {
err = -EINVAL;
goto exit_free;
}
data->extended = !!(cap & JC42_CAP_RANGE);
- config = jc42_read_value(new_client, JC42_REG_CONFIG);
+ config = i2c_smbus_read_word_swapped(new_client, JC42_REG_CONFIG);
if (config < 0) {
err = -EINVAL;
goto exit_free;
@@ -504,7 +503,8 @@ static int jc42_probe(struct i2c_client *new_client,
data->orig_config = config;
if (config & JC42_CFG_SHUTDOWN) {
config &= ~JC42_CFG_SHUTDOWN;
- jc42_write_value(new_client, JC42_REG_CONFIG, config);
+ i2c_smbus_write_word_swapped(new_client, JC42_REG_CONFIG,
+ config);
}
data->config = config;
@@ -535,25 +535,12 @@ static int jc42_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &jc42_group);
if (data->config != data->orig_config)
- jc42_write_value(client, JC42_REG_CONFIG, data->orig_config);
+ i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG,
+ data->orig_config);
kfree(data);
return 0;
}
-/* All registers are word-sized. */
-static int jc42_read_value(struct i2c_client *client, u8 reg)
-{
- int ret = i2c_smbus_read_word_data(client, reg);
- if (ret < 0)
- return ret;
- return swab16(ret);
-}
-
-static int jc42_write_value(struct i2c_client *client, u8 reg, u16 value)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(value));
-}
-
static struct jc42_data *jc42_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -564,28 +551,29 @@ static struct jc42_data *jc42_update_device(struct device *dev)
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
- val = jc42_read_value(client, JC42_REG_TEMP);
+ val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP);
if (val < 0) {
ret = ERR_PTR(val);
goto abort;
}
data->temp_input = val;
- val = jc42_read_value(client, JC42_REG_TEMP_CRITICAL);
+ val = i2c_smbus_read_word_swapped(client,
+ JC42_REG_TEMP_CRITICAL);
if (val < 0) {
ret = ERR_PTR(val);
goto abort;
}
data->temp_crit = val;
- val = jc42_read_value(client, JC42_REG_TEMP_LOWER);
+ val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP_LOWER);
if (val < 0) {
ret = ERR_PTR(val);
goto abort;
}
data->temp_min = val;
- val = jc42_read_value(client, JC42_REG_TEMP_UPPER);
+ val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP_UPPER);
if (val < 0) {
ret = ERR_PTR(val);
goto abort;
diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c
index 29b9030d42c3..9e64d96620d3 100644
--- a/drivers/hwmon/lm73.c
+++ b/drivers/hwmon/lm73.c
@@ -34,7 +34,7 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c,
#define LM73_REG_CTRL 0x04
#define LM73_REG_ID 0x07
-#define LM73_ID 0x9001 /* or 0x190 after a swab16() */
+#define LM73_ID 0x9001 /* 0x0190, byte-swapped */
#define DRVNAME "lm73"
#define LM73_TEMP_MIN (-40)
#define LM73_TEMP_MAX 150
@@ -57,7 +57,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
/* Write value */
value = (short) SENSORS_LIMIT(temp/250, (LM73_TEMP_MIN*4),
(LM73_TEMP_MAX*4)) << 5;
- i2c_smbus_write_word_data(client, attr->index, swab16(value));
+ i2c_smbus_write_word_swapped(client, attr->index, value);
return count;
}
@@ -68,8 +68,8 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *da,
struct i2c_client *client = to_i2c_client(dev);
/* use integer division instead of equivalent right shift to
guarantee arithmetic shift and preserve the sign */
- int temp = ((s16) (swab16(i2c_smbus_read_word_data(client,
- attr->index)))*250) / 32;
+ int temp = ((s16) (i2c_smbus_read_word_swapped(client,
+ attr->index))*250) / 32;
return sprintf(buf, "%d\n", temp);
}
@@ -150,17 +150,31 @@ static int lm73_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
- u16 id;
- u8 ctrl;
+ int id, ctrl, conf;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
+ /*
+ * Do as much detection as possible with byte reads first, as word
+ * reads can confuse other devices.
+ */
+ ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL);
+ if (ctrl < 0 || (ctrl & 0x10))
+ return -ENODEV;
+
+ conf = i2c_smbus_read_byte_data(new_client, LM73_REG_CONF);
+ if (conf < 0 || (conf & 0x0c))
+ return -ENODEV;
+
+ id = i2c_smbus_read_byte_data(new_client, LM73_REG_ID);
+ if (id < 0 || id != (LM73_ID & 0xff))
+ return -ENODEV;
+
/* Check device ID */
id = i2c_smbus_read_word_data(new_client, LM73_REG_ID);
- ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL);
- if ((id != LM73_ID) || (ctrl & 0x10))
+ if (id < 0 || id != LM73_ID)
return -ENODEV;
strlcpy(info->type, "lm73", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index ef902d5d06ab..1888dd0fc05f 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -35,6 +35,7 @@
*/
enum lm75_type { /* keep sorted in alphabetical order */
+ adt75,
ds1775,
ds75,
lm75,
@@ -213,6 +214,7 @@ static int lm75_remove(struct i2c_client *client)
}
static const struct i2c_device_id lm75_ids[] = {
+ { "adt75", adt75, },
{ "ds1775", ds1775, },
{ "ds75", ds75, },
{ "lm75", lm75, },
@@ -247,19 +249,30 @@ static int lm75_detect(struct i2c_client *new_client,
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
- /* Now, we do the remaining detection. There is no identification-
- dedicated register so we have to rely on several tricks:
- unused bits, registers cycling over 8-address boundaries,
- addresses 0x04-0x07 returning the last read value.
- The cycling+unused addresses combination is not tested,
- since it would significantly slow the detection down and would
- hardly add any value.
-
- The National Semiconductor LM75A is different than earlier
- LM75s. It has an ID byte of 0xaX (where X is the chip
- revision, with 1 being the only revision in existence) in
- register 7, and unused registers return 0xff rather than the
- last read value. */
+ /*
+ * Now, we do the remaining detection. There is no identification-
+ * dedicated register so we have to rely on several tricks:
+ * unused bits, registers cycling over 8-address boundaries,
+ * addresses 0x04-0x07 returning the last read value.
+ * The cycling+unused addresses combination is not tested,
+ * since it would significantly slow the detection down and would
+ * hardly add any value.
+ *
+ * The National Semiconductor LM75A is different than earlier
+ * LM75s. It has an ID byte of 0xaX (where X is the chip
+ * revision, with 1 being the only revision in existence) in
+ * register 7, and unused registers return 0xff rather than the
+ * last read value.
+ *
+ * Note that this function only detects the original National
+ * Semiconductor LM75 and the LM75A. Clones from other vendors
+ * aren't detected, on purpose, because they are typically never
+ * found on PC hardware. They are found on embedded designs where
+ * they can be instantiated explicitly so detection is not needed.
+ * The absence of identification registers on all these clones
+ * would make their exhaustive detection very difficult and weak,
+ * and odds are that the driver would bind to unsupported devices.
+ */
/* Unused bits */
conf = i2c_smbus_read_byte_data(new_client, 1);
@@ -371,13 +384,10 @@ static struct i2c_driver lm75_driver = {
*/
static int lm75_read_value(struct i2c_client *client, u8 reg)
{
- int value;
-
if (reg == LM75_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
-
- value = i2c_smbus_read_word_data(client, reg);
- return (value < 0) ? value : swab16(value);
+ else
+ return i2c_smbus_read_word_swapped(client, reg);
}
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
@@ -385,7 +395,7 @@ static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
if (reg == LM75_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
- return i2c_smbus_write_word_data(client, reg, swab16(value));
+ return i2c_smbus_write_word_swapped(client, reg, value);
}
static struct lm75_data *lm75_update_device(struct device *dev)
diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c
index b28a297be50c..8dfc6782d596 100644
--- a/drivers/hwmon/lm77.c
+++ b/drivers/hwmon/lm77.c
@@ -365,7 +365,7 @@ static u16 lm77_read_value(struct i2c_client *client, u8 reg)
if (reg == LM77_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
else
- return swab16(i2c_smbus_read_word_data(client, reg));
+ return i2c_smbus_read_word_swapped(client, reg);
}
static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
@@ -373,7 +373,7 @@ static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
if (reg == LM77_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
- return i2c_smbus_write_word_data(client, reg, swab16(value));
+ return i2c_smbus_write_word_swapped(client, reg, value);
}
static void lm77_init_client(struct i2c_client *client)
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index 4cb24eafe318..6df0b4681710 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -2,7 +2,7 @@
lm78.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
- Copyright (c) 2007 Jean Delvare <khali@linux-fr.org>
+ Copyright (c) 2007, 2011 Jean Delvare <khali@linux-fr.org>
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
@@ -26,23 +26,21 @@
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
-#include <linux/platform_device.h>
-#include <linux/ioport.h>
#include <linux/hwmon.h>
#include <linux/hwmon-vid.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
-#include <linux/io.h>
-/* ISA device, if found */
-static struct platform_device *pdev;
+#ifdef CONFIG_ISA
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#endif
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
0x2e, 0x2f, I2C_CLIENT_END };
-static unsigned short isa_address = 0x290;
-
enum chips { lm78, lm79 };
/* Many LM78 constants specified below */
@@ -143,50 +141,12 @@ struct lm78_data {
};
-static int lm78_i2c_detect(struct i2c_client *client,
- struct i2c_board_info *info);
-static int lm78_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id);
-static int lm78_i2c_remove(struct i2c_client *client);
-
-static int __devinit lm78_isa_probe(struct platform_device *pdev);
-static int __devexit lm78_isa_remove(struct platform_device *pdev);
-
static int lm78_read_value(struct lm78_data *data, u8 reg);
static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value);
static struct lm78_data *lm78_update_device(struct device *dev);
static void lm78_init_device(struct lm78_data *data);
-static const struct i2c_device_id lm78_i2c_id[] = {
- { "lm78", lm78 },
- { "lm79", lm79 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, lm78_i2c_id);
-
-static struct i2c_driver lm78_driver = {
- .class = I2C_CLASS_HWMON,
- .driver = {
- .name = "lm78",
- },
- .probe = lm78_i2c_probe,
- .remove = lm78_i2c_remove,
- .id_table = lm78_i2c_id,
- .detect = lm78_i2c_detect,
- .address_list = normal_i2c,
-};
-
-static struct platform_driver lm78_isa_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "lm78",
- },
- .probe = lm78_isa_probe,
- .remove = __devexit_p(lm78_isa_remove),
-};
-
-
/* 7 Voltages */
static ssize_t show_in(struct device *dev, struct device_attribute *da,
char *buf)
@@ -514,6 +474,16 @@ static const struct attribute_group lm78_group = {
.attrs = lm78_attributes,
};
+/*
+ * ISA related code
+ */
+#ifdef CONFIG_ISA
+
+/* ISA device, if found */
+static struct platform_device *pdev;
+
+static unsigned short isa_address = 0x290;
+
/* I2C devices get this name attribute automatically, but for ISA devices
we must create it by ourselves. */
static ssize_t show_name(struct device *dev, struct device_attribute
@@ -525,6 +495,11 @@ static ssize_t show_name(struct device *dev, struct device_attribute
}
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static struct lm78_data *lm78_data_if_isa(void)
+{
+ return pdev ? platform_get_drvdata(pdev) : NULL;
+}
+
/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
{
@@ -558,12 +533,24 @@ static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
return 1;
}
+#else /* !CONFIG_ISA */
+
+static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
+{
+ return 0;
+}
+
+static struct lm78_data *lm78_data_if_isa(void)
+{
+ return NULL;
+}
+#endif /* CONFIG_ISA */
static int lm78_i2c_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
int i;
- struct lm78_data *isa = pdev ? platform_get_drvdata(pdev) : NULL;
+ struct lm78_data *isa = lm78_data_if_isa();
const char *client_name;
struct i2c_adapter *adapter = client->adapter;
int address = client->addr;
@@ -663,76 +650,24 @@ static int lm78_i2c_remove(struct i2c_client *client)
return 0;
}
-static int __devinit lm78_isa_probe(struct platform_device *pdev)
-{
- int err;
- struct lm78_data *data;
- struct resource *res;
-
- /* Reserve the ISA region */
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) {
- err = -EBUSY;
- goto exit;
- }
-
- if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
- err = -ENOMEM;
- goto exit_release_region;
- }
- mutex_init(&data->lock);
- data->isa_addr = res->start;
- platform_set_drvdata(pdev, data);
-
- if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
- data->type = lm79;
- data->name = "lm79";
- } else {
- data->type = lm78;
- data->name = "lm78";
- }
-
- /* Initialize the LM78 chip */
- lm78_init_device(data);
-
- /* Register sysfs hooks */
- if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group))
- || (err = device_create_file(&pdev->dev, &dev_attr_name)))
- goto exit_remove_files;
-
- data->hwmon_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto exit_remove_files;
- }
-
- return 0;
-
- exit_remove_files:
- sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
- device_remove_file(&pdev->dev, &dev_attr_name);
- kfree(data);
- exit_release_region:
- release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
- exit:
- return err;
-}
-
-static int __devexit lm78_isa_remove(struct platform_device *pdev)
-{
- struct lm78_data *data = platform_get_drvdata(pdev);
- struct resource *res;
-
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
- device_remove_file(&pdev->dev, &dev_attr_name);
- kfree(data);
-
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
+static const struct i2c_device_id lm78_i2c_id[] = {
+ { "lm78", lm78 },
+ { "lm79", lm79 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm78_i2c_id);
- return 0;
-}
+static struct i2c_driver lm78_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm78",
+ },
+ .probe = lm78_i2c_probe,
+ .remove = lm78_i2c_remove,
+ .id_table = lm78_i2c_id,
+ .detect = lm78_i2c_detect,
+ .address_list = normal_i2c,
+};
/* The SMBus locks itself, but ISA access must be locked explicitly!
We don't want to lock the whole ISA bus, so we lock each client
@@ -743,6 +678,7 @@ static int lm78_read_value(struct lm78_data *data, u8 reg)
{
struct i2c_client *client = data->client;
+#ifdef CONFIG_ISA
if (!client) { /* ISA device */
int res;
mutex_lock(&data->lock);
@@ -751,6 +687,7 @@ static int lm78_read_value(struct lm78_data *data, u8 reg)
mutex_unlock(&data->lock);
return res;
} else
+#endif
return i2c_smbus_read_byte_data(client, reg);
}
@@ -765,6 +702,7 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
{
struct i2c_client *client = data->client;
+#ifdef CONFIG_ISA
if (!client) { /* ISA device */
mutex_lock(&data->lock);
outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
@@ -772,6 +710,7 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
mutex_unlock(&data->lock);
return 0;
} else
+#endif
return i2c_smbus_write_byte_data(client, reg, value);
}
@@ -849,6 +788,88 @@ static struct lm78_data *lm78_update_device(struct device *dev)
return data;
}
+#ifdef CONFIG_ISA
+static int __devinit lm78_isa_probe(struct platform_device *pdev)
+{
+ int err;
+ struct lm78_data *data;
+ struct resource *res;
+
+ /* Reserve the ISA region */
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) {
+ err = -EBUSY;
+ goto exit;
+ }
+
+ data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit_release_region;
+ }
+ mutex_init(&data->lock);
+ data->isa_addr = res->start;
+ platform_set_drvdata(pdev, data);
+
+ if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
+ data->type = lm79;
+ data->name = "lm79";
+ } else {
+ data->type = lm78;
+ data->name = "lm78";
+ }
+
+ /* Initialize the LM78 chip */
+ lm78_init_device(data);
+
+ /* Register sysfs hooks */
+ if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group))
+ || (err = device_create_file(&pdev->dev, &dev_attr_name)))
+ goto exit_remove_files;
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+ exit_remove_files:
+ sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+ kfree(data);
+ exit_release_region:
+ release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
+ exit:
+ return err;
+}
+
+static int __devexit lm78_isa_remove(struct platform_device *pdev)
+{
+ struct lm78_data *data = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+ kfree(data);
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
+
+ return 0;
+}
+
+static struct platform_driver lm78_isa_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "lm78",
+ },
+ .probe = lm78_isa_probe,
+ .remove = __devexit_p(lm78_isa_remove),
+};
+
/* return 1 if a supported chip is found, 0 otherwise */
static int __init lm78_isa_found(unsigned short address)
{
@@ -969,12 +990,10 @@ static int __init lm78_isa_device_add(unsigned short address)
return err;
}
-static int __init sm_lm78_init(void)
+static int __init lm78_isa_register(void)
{
int res;
- /* We register the ISA device first, so that we can skip the
- * registration of an I2C interface to the same device. */
if (lm78_isa_found(isa_address)) {
res = platform_driver_register(&lm78_isa_driver);
if (res)
@@ -986,32 +1005,62 @@ static int __init sm_lm78_init(void)
goto exit_unreg_isa_driver;
}
- res = i2c_add_driver(&lm78_driver);
- if (res)
- goto exit_unreg_isa_device;
-
return 0;
- exit_unreg_isa_device:
- platform_device_unregister(pdev);
exit_unreg_isa_driver:
platform_driver_unregister(&lm78_isa_driver);
exit:
return res;
}
-static void __exit sm_lm78_exit(void)
+static void lm78_isa_unregister(void)
{
if (pdev) {
platform_device_unregister(pdev);
platform_driver_unregister(&lm78_isa_driver);
}
- i2c_del_driver(&lm78_driver);
}
+#else /* !CONFIG_ISA */
+static int __init lm78_isa_register(void)
+{
+ return 0;
+}
+
+static void lm78_isa_unregister(void)
+{
+}
+#endif /* CONFIG_ISA */
+static int __init sm_lm78_init(void)
+{
+ int res;
+
+ /* We register the ISA device first, so that we can skip the
+ * registration of an I2C interface to the same device. */
+ res = lm78_isa_register();
+ if (res)
+ goto exit;
+
+ res = i2c_add_driver(&lm78_driver);
+ if (res)
+ goto exit_unreg_isa_device;
+
+ return 0;
+
+ exit_unreg_isa_device:
+ lm78_isa_unregister();
+ exit:
+ return res;
+}
+
+static void __exit sm_lm78_exit(void)
+{
+ lm78_isa_unregister();
+ i2c_del_driver(&lm78_driver);
+}
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_AUTHOR("Frodo Looijaard, Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("LM78/LM79 driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 2f94f9504804..615bc4f4e530 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -54,6 +54,9 @@
* and extended mode. They are mostly compatible with LM90 except for a data
* format difference for the temperature value registers.
*
+ * This driver also supports the SA56004 from Philips. This device is
+ * pin-compatible with the LM86, the ED/EDP parts are also address-compatible.
+ *
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
@@ -96,13 +99,15 @@
* MAX6659 can have address 0x4c, 0x4d or 0x4e.
* MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
* 0x4c, 0x4d or 0x4e.
+ * SA56004 can have address 0x48 through 0x4F.
*/
static const unsigned short normal_i2c[] = {
- 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+ 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
+ 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
- max6646, w83l771, max6696 };
+ max6646, w83l771, max6696, sa56004 };
/*
* The LM90 registers
@@ -152,6 +157,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
#define MAX6659_REG_R_LOCAL_EMERG 0x17
#define MAX6659_REG_W_LOCAL_EMERG 0x17
+/* SA56004 registers */
+
+#define SA56004_REG_R_LOCAL_TEMPL 0x22
+
#define LM90_DEF_CONVRATE_RVAL 6 /* Def conversion rate register value */
#define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */
@@ -161,7 +170,6 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
#define LM90_FLAG_ADT7461_EXT (1 << 0) /* ADT7461 extended mode */
/* Device features */
#define LM90_HAVE_OFFSET (1 << 1) /* temperature offset register */
-#define LM90_HAVE_LOCAL_EXT (1 << 2) /* extended local temperature */
#define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */
#define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */
#define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */
@@ -192,6 +200,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "max6696", max6696 },
{ "nct1008", adt7461 },
{ "w83l771", w83l771 },
+ { "sa56004", sa56004 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm90_id);
@@ -204,6 +213,7 @@ struct lm90_params {
u16 alert_alarms; /* Which alarm bits trigger ALERT# */
/* Upper 8 bits for max6695/96 */
u8 max_convrate; /* Maximum conversion rate register value */
+ u8 reg_local_ext; /* Extended local temp register (optional) */
};
static const struct lm90_params lm90_params[] = {
@@ -235,19 +245,20 @@ static const struct lm90_params lm90_params[] = {
.max_convrate = 9,
},
[max6646] = {
- .flags = LM90_HAVE_LOCAL_EXT,
.alert_alarms = 0x7c,
.max_convrate = 6,
+ .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
},
[max6657] = {
- .flags = LM90_HAVE_LOCAL_EXT,
.alert_alarms = 0x7c,
.max_convrate = 8,
+ .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
},
[max6659] = {
- .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY,
+ .flags = LM90_HAVE_EMERGENCY,
.alert_alarms = 0x7c,
.max_convrate = 8,
+ .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
},
[max6680] = {
.flags = LM90_HAVE_OFFSET,
@@ -255,16 +266,23 @@ static const struct lm90_params lm90_params[] = {
.max_convrate = 7,
},
[max6696] = {
- .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY
+ .flags = LM90_HAVE_EMERGENCY
| LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3,
.alert_alarms = 0x187c,
.max_convrate = 6,
+ .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
},
[w83l771] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
.alert_alarms = 0x7c,
.max_convrate = 8,
},
+ [sa56004] = {
+ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
+ .alert_alarms = 0x7b,
+ .max_convrate = 9,
+ .reg_local_ext = SA56004_REG_R_LOCAL_TEMPL,
+ },
};
/*
@@ -286,6 +304,7 @@ struct lm90_data {
u16 alert_alarms; /* Which alarm bits trigger ALERT# */
/* Upper 8 bits for max6695/96 */
u8 max_convrate; /* Maximum conversion rate */
+ u8 reg_local_ext; /* local extension register offset */
/* registers values */
s8 temp8[8]; /* 0: local low limit
@@ -452,9 +471,9 @@ static struct lm90_data *lm90_update_device(struct device *dev)
lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]);
lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst);
- if (data->flags & LM90_HAVE_LOCAL_EXT) {
+ if (data->reg_local_ext) {
lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
- MAX6657_REG_R_LOCAL_TEMPL,
+ data->reg_local_ext,
&data->temp11[4]);
} else {
if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP,
@@ -1086,40 +1105,37 @@ static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec);
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm90_detect(struct i2c_client *new_client,
+static int lm90_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
- struct i2c_adapter *adapter = new_client->adapter;
- int address = new_client->addr;
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
const char *name = NULL;
- int man_id, chip_id, reg_config1, reg_convrate;
+ int man_id, chip_id, config1, config2, convrate;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
/* detection and identification */
- if ((man_id = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_MAN_ID)) < 0
- || (chip_id = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_CHIP_ID)) < 0
- || (reg_config1 = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_CONFIG1)) < 0
- || (reg_convrate = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_CONVRATE)) < 0)
+ man_id = i2c_smbus_read_byte_data(client, LM90_REG_R_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, LM90_REG_R_CHIP_ID);
+ config1 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1);
+ convrate = i2c_smbus_read_byte_data(client, LM90_REG_R_CONVRATE);
+ if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0)
return -ENODEV;
- if ((address == 0x4C || address == 0x4D)
- && man_id == 0x01) { /* National Semiconductor */
- int reg_config2;
-
- reg_config2 = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_CONFIG2);
- if (reg_config2 < 0)
+ if (man_id == 0x01 || man_id == 0x5C || man_id == 0x41) {
+ config2 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG2);
+ if (config2 < 0)
return -ENODEV;
+ } else
+ config2 = 0; /* Make compiler happy */
- if ((reg_config1 & 0x2A) == 0x00
- && (reg_config2 & 0xF8) == 0x00
- && reg_convrate <= 0x09) {
+ if ((address == 0x4C || address == 0x4D)
+ && man_id == 0x01) { /* National Semiconductor */
+ if ((config1 & 0x2A) == 0x00
+ && (config2 & 0xF8) == 0x00
+ && convrate <= 0x09) {
if (address == 0x4C
&& (chip_id & 0xF0) == 0x20) { /* LM90 */
name = "lm90";
@@ -1143,8 +1159,8 @@ static int lm90_detect(struct i2c_client *new_client,
if ((address == 0x4C || address == 0x4D)
&& man_id == 0x41) { /* Analog Devices */
if ((chip_id & 0xF0) == 0x40 /* ADM1032 */
- && (reg_config1 & 0x3F) == 0x00
- && reg_convrate <= 0x0A) {
+ && (config1 & 0x3F) == 0x00
+ && convrate <= 0x0A) {
name = "adm1032";
/* The ADM1032 supports PEC, but only if combined
transactions are not used. */
@@ -1153,18 +1169,18 @@ static int lm90_detect(struct i2c_client *new_client,
info->flags |= I2C_CLIENT_PEC;
} else
if (chip_id == 0x51 /* ADT7461 */
- && (reg_config1 & 0x1B) == 0x00
- && reg_convrate <= 0x0A) {
+ && (config1 & 0x1B) == 0x00
+ && convrate <= 0x0A) {
name = "adt7461";
} else
if (chip_id == 0x57 /* ADT7461A, NCT1008 */
- && (reg_config1 & 0x1B) == 0x00
- && reg_convrate <= 0x0A) {
+ && (config1 & 0x1B) == 0x00
+ && convrate <= 0x0A) {
name = "adt7461a";
}
} else
if (man_id == 0x4D) { /* Maxim */
- int reg_emerg, reg_emerg2, reg_status2;
+ int emerg, emerg2, status2;
/*
* We read MAX6659_REG_R_REMOTE_EMERG twice, and re-read
@@ -1172,13 +1188,15 @@ static int lm90_detect(struct i2c_client *new_client,
* exists, both readings will reflect the same value. Otherwise,
* the readings will be different.
*/
- if ((reg_emerg = i2c_smbus_read_byte_data(new_client,
- MAX6659_REG_R_REMOTE_EMERG)) < 0
- || i2c_smbus_read_byte_data(new_client, LM90_REG_R_MAN_ID) < 0
- || (reg_emerg2 = i2c_smbus_read_byte_data(new_client,
- MAX6659_REG_R_REMOTE_EMERG)) < 0
- || (reg_status2 = i2c_smbus_read_byte_data(new_client,
- MAX6696_REG_R_STATUS2)) < 0)
+ emerg = i2c_smbus_read_byte_data(client,
+ MAX6659_REG_R_REMOTE_EMERG);
+ man_id = i2c_smbus_read_byte_data(client,
+ LM90_REG_R_MAN_ID);
+ emerg2 = i2c_smbus_read_byte_data(client,
+ MAX6659_REG_R_REMOTE_EMERG);
+ status2 = i2c_smbus_read_byte_data(client,
+ MAX6696_REG_R_STATUS2);
+ if (emerg < 0 || man_id < 0 || emerg2 < 0 || status2 < 0)
return -ENODEV;
/*
@@ -1196,8 +1214,8 @@ static int lm90_detect(struct i2c_client *new_client,
*/
if (chip_id == man_id
&& (address == 0x4C || address == 0x4D || address == 0x4E)
- && (reg_config1 & 0x1F) == (man_id & 0x0F)
- && reg_convrate <= 0x09) {
+ && (config1 & 0x1F) == (man_id & 0x0F)
+ && convrate <= 0x09) {
if (address == 0x4C)
name = "max6657";
else
@@ -1215,10 +1233,10 @@ static int lm90_detect(struct i2c_client *new_client,
* one of those registers exists.
*/
if (chip_id == 0x01
- && (reg_config1 & 0x10) == 0x00
- && (reg_status2 & 0x01) == 0x00
- && reg_emerg == reg_emerg2
- && reg_convrate <= 0x07) {
+ && (config1 & 0x10) == 0x00
+ && (status2 & 0x01) == 0x00
+ && emerg == emerg2
+ && convrate <= 0x07) {
name = "max6696";
} else
/*
@@ -1228,8 +1246,8 @@ static int lm90_detect(struct i2c_client *new_client,
* second to last bit of config1 (software reset).
*/
if (chip_id == 0x01
- && (reg_config1 & 0x03) == 0x00
- && reg_convrate <= 0x07) {
+ && (config1 & 0x03) == 0x00
+ && convrate <= 0x07) {
name = "max6680";
} else
/*
@@ -1238,31 +1256,33 @@ static int lm90_detect(struct i2c_client *new_client,
* register are unused and should return zero when read.
*/
if (chip_id == 0x59
- && (reg_config1 & 0x3f) == 0x00
- && reg_convrate <= 0x07) {
+ && (config1 & 0x3f) == 0x00
+ && convrate <= 0x07) {
name = "max6646";
}
} else
if (address == 0x4C
&& man_id == 0x5C) { /* Winbond/Nuvoton */
- int reg_config2;
-
- reg_config2 = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_CONFIG2);
- if (reg_config2 < 0)
- return -ENODEV;
-
- if ((reg_config1 & 0x2A) == 0x00
- && (reg_config2 & 0xF8) == 0x00) {
+ if ((config1 & 0x2A) == 0x00
+ && (config2 & 0xF8) == 0x00) {
if (chip_id == 0x01 /* W83L771W/G */
- && reg_convrate <= 0x09) {
+ && convrate <= 0x09) {
name = "w83l771";
} else
if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */
- && reg_convrate <= 0x08) {
+ && convrate <= 0x08) {
name = "w83l771";
}
}
+ } else
+ if (address >= 0x48 && address <= 0x4F
+ && man_id == 0xA1) { /* NXP Semiconductor/Philips */
+ if (chip_id == 0x00
+ && (config1 & 0x2A) == 0x00
+ && (config2 & 0xFE) == 0x00
+ && convrate <= 0x09) {
+ name = "sa56004";
+ }
}
if (!name) { /* identification failed */
@@ -1279,19 +1299,18 @@ static int lm90_detect(struct i2c_client *new_client,
static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data)
{
+ struct device *dev = &client->dev;
+
if (data->flags & LM90_HAVE_TEMP3)
- sysfs_remove_group(&client->dev.kobj, &lm90_temp3_group);
+ sysfs_remove_group(&dev->kobj, &lm90_temp3_group);
if (data->flags & LM90_HAVE_EMERGENCY_ALARM)
- sysfs_remove_group(&client->dev.kobj,
- &lm90_emergency_alarm_group);
+ sysfs_remove_group(&dev->kobj, &lm90_emergency_alarm_group);
if (data->flags & LM90_HAVE_EMERGENCY)
- sysfs_remove_group(&client->dev.kobj,
- &lm90_emergency_group);
+ sysfs_remove_group(&dev->kobj, &lm90_emergency_group);
if (data->flags & LM90_HAVE_OFFSET)
- device_remove_file(&client->dev,
- &sensor_dev_attr_temp2_offset.dev_attr);
- device_remove_file(&client->dev, &dev_attr_pec);
- sysfs_remove_group(&client->dev.kobj, &lm90_group);
+ device_remove_file(dev, &sensor_dev_attr_temp2_offset.dev_attr);
+ device_remove_file(dev, &dev_attr_pec);
+ sysfs_remove_group(&dev->kobj, &lm90_group);
}
static void lm90_init_client(struct i2c_client *client)
@@ -1340,10 +1359,11 @@ static void lm90_init_client(struct i2c_client *client)
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config);
}
-static int lm90_probe(struct i2c_client *new_client,
+static int lm90_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct i2c_adapter *adapter = to_i2c_adapter(new_client->dev.parent);
+ struct device *dev = &client->dev;
+ struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
struct lm90_data *data;
int err;
@@ -1352,14 +1372,14 @@ static int lm90_probe(struct i2c_client *new_client,
err = -ENOMEM;
goto exit;
}
- i2c_set_clientdata(new_client, data);
+ i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* Set the device type */
data->kind = id->driver_data;
if (data->kind == adm1032) {
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
- new_client->flags &= ~I2C_CLIENT_PEC;
+ client->flags &= ~I2C_CLIENT_PEC;
}
/* Different devices have different alarm bits triggering the
@@ -1368,48 +1388,47 @@ static int lm90_probe(struct i2c_client *new_client,
/* Set chip capabilities */
data->flags = lm90_params[data->kind].flags;
+ data->reg_local_ext = lm90_params[data->kind].reg_local_ext;
/* Set maximum conversion rate */
data->max_convrate = lm90_params[data->kind].max_convrate;
/* Initialize the LM90 chip */
- lm90_init_client(new_client);
+ lm90_init_client(client);
/* Register sysfs hooks */
- err = sysfs_create_group(&new_client->dev.kobj, &lm90_group);
+ err = sysfs_create_group(&dev->kobj, &lm90_group);
if (err)
goto exit_free;
- if (new_client->flags & I2C_CLIENT_PEC) {
- err = device_create_file(&new_client->dev, &dev_attr_pec);
+ if (client->flags & I2C_CLIENT_PEC) {
+ err = device_create_file(dev, &dev_attr_pec);
if (err)
goto exit_remove_files;
}
if (data->flags & LM90_HAVE_OFFSET) {
- err = device_create_file(&new_client->dev,
+ err = device_create_file(dev,
&sensor_dev_attr_temp2_offset.dev_attr);
if (err)
goto exit_remove_files;
}
if (data->flags & LM90_HAVE_EMERGENCY) {
- err = sysfs_create_group(&new_client->dev.kobj,
- &lm90_emergency_group);
+ err = sysfs_create_group(&dev->kobj, &lm90_emergency_group);
if (err)
goto exit_remove_files;
}
if (data->flags & LM90_HAVE_EMERGENCY_ALARM) {
- err = sysfs_create_group(&new_client->dev.kobj,
+ err = sysfs_create_group(&dev->kobj,
&lm90_emergency_alarm_group);
if (err)
goto exit_remove_files;
}
if (data->flags & LM90_HAVE_TEMP3) {
- err = sysfs_create_group(&new_client->dev.kobj,
- &lm90_temp3_group);
+ err = sysfs_create_group(&dev->kobj, &lm90_temp3_group);
if (err)
goto exit_remove_files;
}
- data->hwmon_dev = hwmon_device_register(&new_client->dev);
+ data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove_files;
@@ -1418,7 +1437,7 @@ static int lm90_probe(struct i2c_client *new_client,
return 0;
exit_remove_files:
- lm90_remove_files(new_client, data);
+ lm90_remove_files(client, data);
exit_free:
kfree(data);
exit:
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index 7c31e6205f85..8fcbd4d422c5 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -117,16 +117,16 @@ static struct lm92_data *lm92_update_device(struct device *dev)
if (time_after(jiffies, data->last_updated + HZ)
|| !data->valid) {
dev_dbg(&client->dev, "Updating lm92 data\n");
- data->temp1_input = swab16(i2c_smbus_read_word_data(client,
- LM92_REG_TEMP));
- data->temp1_hyst = swab16(i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_HYST));
- data->temp1_crit = swab16(i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_CRIT));
- data->temp1_min = swab16(i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_LOW));
- data->temp1_max = swab16(i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_HIGH));
+ data->temp1_input = i2c_smbus_read_word_swapped(client,
+ LM92_REG_TEMP);
+ data->temp1_hyst = i2c_smbus_read_word_swapped(client,
+ LM92_REG_TEMP_HYST);
+ data->temp1_crit = i2c_smbus_read_word_swapped(client,
+ LM92_REG_TEMP_CRIT);
+ data->temp1_min = i2c_smbus_read_word_swapped(client,
+ LM92_REG_TEMP_LOW);
+ data->temp1_max = i2c_smbus_read_word_swapped(client,
+ LM92_REG_TEMP_HIGH);
data->last_updated = jiffies;
data->valid = 1;
@@ -158,7 +158,7 @@ static ssize_t set_##value(struct device *dev, struct device_attribute *attr, co
\
mutex_lock(&data->update_lock); \
data->value = TEMP_TO_REG(val); \
- i2c_smbus_write_word_data(client, reg, swab16(data->value)); \
+ i2c_smbus_write_word_swapped(client, reg, data->value); \
mutex_unlock(&data->update_lock); \
return count; \
}
@@ -194,8 +194,8 @@ static ssize_t set_temp1_crit_hyst(struct device *dev, struct device_attribute *
mutex_lock(&data->update_lock);
data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - val;
- i2c_smbus_write_word_data(client, LM92_REG_TEMP_HYST,
- swab16(TEMP_TO_REG(data->temp1_hyst)));
+ i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST,
+ TEMP_TO_REG(data->temp1_hyst));
mutex_unlock(&data->update_lock);
return count;
}
diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c
index d3b464b74ced..513901d592a9 100644
--- a/drivers/hwmon/lm95241.c
+++ b/drivers/hwmon/lm95241.c
@@ -74,8 +74,9 @@ static const unsigned short normal_i2c[] = {
#define TT_OFF 0
#define TT_ON 1
#define TT_MASK 7
-#define MANUFACTURER_ID 0x01
-#define DEFAULT_REVISION 0xA4
+#define NATSEMI_MAN_ID 0x01
+#define LM95231_CHIP_ID 0xA1
+#define LM95241_CHIP_ID 0xA4
static const u8 lm95241_reg_address[] = {
LM95241_REG_R_LOCAL_TEMPH,
@@ -338,20 +339,25 @@ static int lm95241_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
- int address = new_client->addr;
const char *name;
+ int mfg_id, chip_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID)
- == MANUFACTURER_ID)
- && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID)
- == DEFAULT_REVISION)) {
- name = DEVNAME;
- } else {
- dev_dbg(&adapter->dev, "LM95241 detection failed at 0x%02x\n",
- address);
+ mfg_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID);
+ if (mfg_id != NATSEMI_MAN_ID)
+ return -ENODEV;
+
+ chip_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID);
+ switch (chip_id) {
+ case LM95231_CHIP_ID:
+ name = "lm95231";
+ break;
+ case LM95241_CHIP_ID:
+ name = "lm95241";
+ break;
+ default:
return -ENODEV;
}
@@ -431,7 +437,8 @@ static int lm95241_remove(struct i2c_client *client)
/* Driver data (common to all clients) */
static const struct i2c_device_id lm95241_id[] = {
- { DEVNAME, 0 },
+ { "lm95231", 0 },
+ { "lm95241", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm95241_id);
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
new file mode 100644
index 000000000000..dce9e68241e6
--- /dev/null
+++ b/drivers/hwmon/lm95245.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com>
+ *
+ * The LM95245 is a sensor chip made by National Semiconductors.
+ * It reports up to two temperatures (its own plus an external one).
+ * Complete datasheet can be obtained from National's website at:
+ * http://www.national.com/ds.cgi/LM/LM95245.pdf
+ *
+ * This driver is based on lm95241.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+#define DEVNAME "lm95245"
+
+static const unsigned short normal_i2c[] = {
+ 0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END };
+
+/* LM95245 registers */
+/* general registers */
+#define LM95245_REG_RW_CONFIG1 0x03
+#define LM95245_REG_RW_CONVERS_RATE 0x04
+#define LM95245_REG_W_ONE_SHOT 0x0F
+
+/* diode configuration */
+#define LM95245_REG_RW_CONFIG2 0xBF
+#define LM95245_REG_RW_REMOTE_OFFH 0x11
+#define LM95245_REG_RW_REMOTE_OFFL 0x12
+
+/* status registers */
+#define LM95245_REG_R_STATUS1 0x02
+#define LM95245_REG_R_STATUS2 0x33
+
+/* limit registers */
+#define LM95245_REG_RW_REMOTE_OS_LIMIT 0x07
+#define LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT 0x20
+#define LM95245_REG_RW_REMOTE_TCRIT_LIMIT 0x19
+#define LM95245_REG_RW_COMMON_HYSTERESIS 0x21
+
+/* temperature signed */
+#define LM95245_REG_R_LOCAL_TEMPH_S 0x00
+#define LM95245_REG_R_LOCAL_TEMPL_S 0x30
+#define LM95245_REG_R_REMOTE_TEMPH_S 0x01
+#define LM95245_REG_R_REMOTE_TEMPL_S 0x10
+/* temperature unsigned */
+#define LM95245_REG_R_REMOTE_TEMPH_U 0x31
+#define LM95245_REG_R_REMOTE_TEMPL_U 0x32
+
+/* id registers */
+#define LM95245_REG_R_MAN_ID 0xFE
+#define LM95245_REG_R_CHIP_ID 0xFF
+
+/* LM95245 specific bitfields */
+#define CFG_STOP 0x40
+#define CFG_REMOTE_TCRIT_MASK 0x10
+#define CFG_REMOTE_OS_MASK 0x08
+#define CFG_LOCAL_TCRIT_MASK 0x04
+#define CFG_LOCAL_OS_MASK 0x02
+
+#define CFG2_OS_A0 0x40
+#define CFG2_DIODE_FAULT_OS 0x20
+#define CFG2_DIODE_FAULT_TCRIT 0x10
+#define CFG2_REMOTE_TT 0x08
+#define CFG2_REMOTE_FILTER_DIS 0x00
+#define CFG2_REMOTE_FILTER_EN 0x06
+
+/* conversation rate in ms */
+#define RATE_CR0063 0x00
+#define RATE_CR0364 0x01
+#define RATE_CR1000 0x02
+#define RATE_CR2500 0x03
+
+#define STATUS1_DIODE_FAULT 0x04
+#define STATUS1_RTCRIT 0x02
+#define STATUS1_LOC 0x01
+
+#define MANUFACTURER_ID 0x01
+#define DEFAULT_REVISION 0xB3
+
+static const u8 lm95245_reg_address[] = {
+ LM95245_REG_R_LOCAL_TEMPH_S,
+ LM95245_REG_R_LOCAL_TEMPL_S,
+ LM95245_REG_R_REMOTE_TEMPH_S,
+ LM95245_REG_R_REMOTE_TEMPL_S,
+ LM95245_REG_R_REMOTE_TEMPH_U,
+ LM95245_REG_R_REMOTE_TEMPL_U,
+ LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT,
+ LM95245_REG_RW_REMOTE_TCRIT_LIMIT,
+ LM95245_REG_RW_COMMON_HYSTERESIS,
+ LM95245_REG_R_STATUS1,
+};
+
+/* Client data (each client gets its own) */
+struct lm95245_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ unsigned long last_updated; /* in jiffies */
+ unsigned long interval; /* in msecs */
+ bool valid; /* zero until following fields are valid */
+ /* registers values */
+ u8 regs[ARRAY_SIZE(lm95245_reg_address)];
+ u8 config1, config2;
+};
+
+/* Conversions */
+static int temp_from_reg_unsigned(u8 val_h, u8 val_l)
+{
+ return val_h * 1000 + val_l * 1000 / 256;
+}
+
+static int temp_from_reg_signed(u8 val_h, u8 val_l)
+{
+ if (val_h & 0x80)
+ return (val_h - 0x100) * 1000;
+ return temp_from_reg_unsigned(val_h, val_l);
+}
+
+static struct lm95245_data *lm95245_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95245_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated
+ + msecs_to_jiffies(data->interval)) || !data->valid) {
+ int i;
+
+ dev_dbg(&client->dev, "Updating lm95245 data.\n");
+ for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++)
+ data->regs[i]
+ = i2c_smbus_read_byte_data(client,
+ lm95245_reg_address[i]);
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static unsigned long lm95245_read_conversion_rate(struct i2c_client *client)
+{
+ int rate;
+ unsigned long interval;
+
+ rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE);
+
+ switch (rate) {
+ case RATE_CR0063:
+ interval = 63;
+ break;
+ case RATE_CR0364:
+ interval = 364;
+ break;
+ case RATE_CR1000:
+ interval = 1000;
+ break;
+ case RATE_CR2500:
+ default:
+ interval = 2500;
+ break;
+ }
+
+ return interval;
+}
+
+static unsigned long lm95245_set_conversion_rate(struct i2c_client *client,
+ unsigned long interval)
+{
+ int rate;
+
+ if (interval <= 63) {
+ interval = 63;
+ rate = RATE_CR0063;
+ } else if (interval <= 364) {
+ interval = 364;
+ rate = RATE_CR0364;
+ } else if (interval <= 1000) {
+ interval = 1000;
+ rate = RATE_CR1000;
+ } else {
+ interval = 2500;
+ rate = RATE_CR2500;
+ }
+
+ i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate);
+
+ return interval;
+}
+
+/* Sysfs stuff */
+static ssize_t show_input(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm95245_data *data = lm95245_update_device(dev);
+ int temp;
+ int index = to_sensor_dev_attr(attr)->index;
+
+ /*
+ * Index 0 (Local temp) is always signed
+ * Index 2 (Remote temp) has both signed and unsigned data
+ * use signed calculation for remote if signed bit is set
+ */
+ if (index == 0 || data->regs[index] & 0x80)
+ temp = temp_from_reg_signed(data->regs[index],
+ data->regs[index + 1]);
+ else
+ temp = temp_from_reg_unsigned(data->regs[index + 2],
+ data->regs[index + 3]);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp);
+}
+
+static ssize_t show_limit(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm95245_data *data = lm95245_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n",
+ data->regs[index] * 1000);
+}
+
+static ssize_t set_limit(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95245_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ val /= 1000;
+
+ val = SENSORS_LIMIT(val, 0, (index == 6 ? 127 : 255));
+
+ mutex_lock(&data->update_lock);
+
+ data->valid = 0;
+
+ i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95245_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ val /= 1000;
+
+ val = SENSORS_LIMIT(val, 0, 31);
+
+ mutex_lock(&data->update_lock);
+
+ data->valid = 0;
+
+ /* shared crit hysteresis */
+ i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS,
+ val);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_type(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95245_data *data = i2c_get_clientdata(client);
+
+ return snprintf(buf, PAGE_SIZE - 1,
+ data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n");
+}
+
+static ssize_t set_type(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95245_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+ if (val != 1 && val != 2)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ if (val == 1)
+ data->config2 |= CFG2_REMOTE_TT;
+ else
+ data->config2 &= ~CFG2_REMOTE_TT;
+
+ data->valid = 0;
+
+ i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2,
+ data->config2);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm95245_data *data = lm95245_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n",
+ !!(data->regs[9] & index));
+}
+
+static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm95245_data *data = lm95245_update_device(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval);
+}
+
+static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95245_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ data->interval = lm95245_set_conversion_rate(client, val);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit,
+ set_limit, 6);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_limit,
+ set_crit_hyst, 8);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
+ STATUS1_LOC);
+
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit,
+ set_limit, 7);
+static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_limit,
+ set_crit_hyst, 8);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
+ STATUS1_RTCRIT);
+static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type,
+ set_type, 0);
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL,
+ STATUS1_DIODE_FAULT);
+
+static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
+ set_interval);
+
+static struct attribute *lm95245_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &dev_attr_update_interval.attr,
+ NULL
+};
+
+static const struct attribute_group lm95245_group = {
+ .attrs = lm95245_attributes,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm95245_detect(struct i2c_client *new_client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID)
+ != MANUFACTURER_ID
+ || i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID)
+ != DEFAULT_REVISION)
+ return -ENODEV;
+
+ strlcpy(info->type, DEVNAME, I2C_NAME_SIZE);
+ return 0;
+}
+
+static void lm95245_init_client(struct i2c_client *client)
+{
+ struct lm95245_data *data = i2c_get_clientdata(client);
+
+ data->valid = 0;
+ data->interval = lm95245_read_conversion_rate(client);
+
+ data->config1 = i2c_smbus_read_byte_data(client,
+ LM95245_REG_RW_CONFIG1);
+ data->config2 = i2c_smbus_read_byte_data(client,
+ LM95245_REG_RW_CONFIG2);
+
+ if (data->config1 & CFG_STOP) {
+ /* Clear the standby bit */
+ data->config1 &= ~CFG_STOP;
+ i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1,
+ data->config1);
+ }
+}
+
+static int lm95245_probe(struct i2c_client *new_client,
+ const struct i2c_device_id *id)
+{
+ struct lm95245_data *data;
+ int err;
+
+ data = kzalloc(sizeof(struct lm95245_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(new_client, data);
+ mutex_init(&data->update_lock);
+
+ /* Initialize the LM95245 chip */
+ lm95245_init_client(new_client);
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&new_client->dev.kobj, &lm95245_group);
+ if (err)
+ goto exit_free;
+
+ data->hwmon_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &lm95245_group);
+exit_free:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int lm95245_remove(struct i2c_client *client)
+{
+ struct lm95245_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm95245_group);
+
+ kfree(data);
+ return 0;
+}
+
+/* Driver data (common to all clients) */
+static const struct i2c_device_id lm95245_id[] = {
+ { DEVNAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm95245_id);
+
+static struct i2c_driver lm95245_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DEVNAME,
+ },
+ .probe = lm95245_probe,
+ .remove = lm95245_remove,
+ .id_table = lm95245_id,
+ .detect = lm95245_detect,
+ .address_list = normal_i2c,
+};
+
+static int __init sensors_lm95245_init(void)
+{
+ return i2c_add_driver(&lm95245_driver);
+}
+
+static void __exit sensors_lm95245_exit(void)
+{
+ i2c_del_driver(&lm95245_driver);
+}
+
+MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
+MODULE_DESCRIPTION("LM95245 sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm95245_init);
+module_exit(sensors_lm95245_exit);
diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c
index 12a54aa29776..c97b78ef9116 100644
--- a/drivers/hwmon/max1111.c
+++ b/drivers/hwmon/max1111.c
@@ -38,8 +38,10 @@ struct max1111_data {
struct device *hwmon_dev;
struct spi_message msg;
struct spi_transfer xfer[2];
- uint8_t *tx_buf;
- uint8_t *rx_buf;
+ uint8_t tx_buf[MAX1111_TX_BUF_SIZE];
+ uint8_t rx_buf[MAX1111_RX_BUF_SIZE];
+ struct mutex drvdata_lock;
+ /* protect msg, xfer and buffers from multiple access */
};
static int max1111_read(struct device *dev, int channel)
@@ -48,6 +50,9 @@ static int max1111_read(struct device *dev, int channel)
uint8_t v1, v2;
int err;
+ /* writing to drvdata struct is not thread safe, wait on mutex */
+ mutex_lock(&data->drvdata_lock);
+
data->tx_buf[0] = (channel << MAX1111_CTRL_SEL_SH) |
MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 |
MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR;
@@ -55,12 +60,15 @@ static int max1111_read(struct device *dev, int channel)
err = spi_sync(data->spi, &data->msg);
if (err < 0) {
dev_err(dev, "spi_sync failed with %d\n", err);
+ mutex_unlock(&data->drvdata_lock);
return err;
}
v1 = data->rx_buf[0];
v2 = data->rx_buf[1];
+ mutex_unlock(&data->drvdata_lock);
+
if ((v1 & 0xc0) || (v2 & 0x3f))
return -EINVAL;
@@ -123,33 +131,23 @@ static const struct attribute_group max1111_attr_group = {
.attrs = max1111_attributes,
};
-static int setup_transfer(struct max1111_data *data)
+static int __devinit setup_transfer(struct max1111_data *data)
{
struct spi_message *m;
struct spi_transfer *x;
- data->tx_buf = kmalloc(MAX1111_TX_BUF_SIZE, GFP_KERNEL);
- if (!data->tx_buf)
- return -ENOMEM;
-
- data->rx_buf = kmalloc(MAX1111_RX_BUF_SIZE, GFP_KERNEL);
- if (!data->rx_buf) {
- kfree(data->tx_buf);
- return -ENOMEM;
- }
-
m = &data->msg;
x = &data->xfer[0];
spi_message_init(m);
x->tx_buf = &data->tx_buf[0];
- x->len = 1;
+ x->len = MAX1111_TX_BUF_SIZE;
spi_message_add_tail(x, m);
x++;
x->rx_buf = &data->rx_buf[0];
- x->len = 2;
+ x->len = MAX1111_RX_BUF_SIZE;
spi_message_add_tail(x, m);
return 0;
@@ -176,13 +174,15 @@ static int __devinit max1111_probe(struct spi_device *spi)
if (err)
goto err_free_data;
+ mutex_init(&data->drvdata_lock);
+
data->spi = spi;
spi_set_drvdata(spi, data);
err = sysfs_create_group(&spi->dev.kobj, &max1111_attr_group);
if (err) {
dev_err(&spi->dev, "failed to create attribute group\n");
- goto err_free_all;
+ goto err_free_data;
}
data->hwmon_dev = hwmon_device_register(&spi->dev);
@@ -199,9 +199,6 @@ static int __devinit max1111_probe(struct spi_device *spi)
err_remove:
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
-err_free_all:
- kfree(data->rx_buf);
- kfree(data->tx_buf);
err_free_data:
kfree(data);
return err;
@@ -213,8 +210,7 @@ static int __devexit max1111_remove(struct spi_device *spi)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
- kfree(data->rx_buf);
- kfree(data->tx_buf);
+ mutex_destroy(&data->drvdata_lock);
kfree(data);
return 0;
}
diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c
index d94a24fdf4ba..385886a4f224 100644
--- a/drivers/hwmon/max16065.c
+++ b/drivers/hwmon/max16065.c
@@ -124,7 +124,7 @@ static inline int MV_TO_LIMIT(int mv, int range)
static inline int ADC_TO_CURR(int adc, int gain)
{
- return adc * 1400000 / gain * 255;
+ return adc * 1400000 / (gain * 255);
}
/*
@@ -137,10 +137,10 @@ static int max16065_read_adc(struct i2c_client *client, int reg)
{
int rv;
- rv = i2c_smbus_read_word_data(client, reg);
+ rv = i2c_smbus_read_word_swapped(client, reg);
if (unlikely(rv < 0))
return rv;
- return ((rv & 0xff) << 2) | ((rv >> 14) & 0x03);
+ return rv >> 6;
}
static struct max16065_data *max16065_update_device(struct device *dev)
diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c
new file mode 100644
index 000000000000..20d1b2ddffb6
--- /dev/null
+++ b/drivers/hwmon/max1668.c
@@ -0,0 +1,502 @@
+/*
+ Copyright (c) 2011 David George <david.george@ska.ac.za>
+
+ based on adm1021.c
+ some credit to Christoph Scheurer, but largely a rewrite
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static unsigned short max1668_addr_list[] = {
+ 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+
+/* max1668 registers */
+
+#define MAX1668_REG_TEMP(nr) (nr)
+#define MAX1668_REG_STAT1 0x05
+#define MAX1668_REG_STAT2 0x06
+#define MAX1668_REG_MAN_ID 0xfe
+#define MAX1668_REG_DEV_ID 0xff
+
+/* limits */
+
+/* write high limits */
+#define MAX1668_REG_LIMH_WR(nr) (0x13 + 2 * (nr))
+/* write low limits */
+#define MAX1668_REG_LIML_WR(nr) (0x14 + 2 * (nr))
+/* read high limits */
+#define MAX1668_REG_LIMH_RD(nr) (0x08 + 2 * (nr))
+/* read low limits */
+#define MAX1668_REG_LIML_RD(nr) (0x09 + 2 * (nr))
+
+/* manufacturer and device ID Constants */
+#define MAN_ID_MAXIM 0x4d
+#define DEV_ID_MAX1668 0x3
+#define DEV_ID_MAX1805 0x5
+#define DEV_ID_MAX1989 0xb
+
+/* read only mode module parameter */
+static int read_only;
+module_param(read_only, bool, 0);
+MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
+
+enum chips { max1668, max1805, max1989 };
+
+struct max1668_data {
+ struct device *hwmon_dev;
+ enum chips type;
+
+ struct mutex update_lock;
+ char valid; /* !=0 if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* 1x local and 4x remote */
+ s8 temp_max[5];
+ s8 temp_min[5];
+ s8 temp[5];
+ u16 alarms;
+};
+
+static struct max1668_data *max1668_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max1668_data *data = i2c_get_clientdata(client);
+ struct max1668_data *ret = data;
+ s32 val;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (data->valid && !time_after(jiffies,
+ data->last_updated + HZ + HZ / 2))
+ goto abort;
+
+ for (i = 0; i < 5; i++) {
+ val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp[i] = (s8) val;
+
+ val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp_max[i] = (s8) val;
+
+ val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp_min[i] = (s8) val;
+ }
+
+ val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->alarms = val << 8;
+
+ val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->alarms |= val;
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+abort:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static ssize_t show_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max1668_data *data = max1668_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->temp[index] * 1000);
+}
+
+static ssize_t show_temp_max(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max1668_data *data = max1668_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->temp_max[index] * 1000);
+}
+
+static ssize_t show_temp_min(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max1668_data *data = max1668_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->temp_min[index] * 1000);
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct max1668_data *data = max1668_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1);
+}
+
+static ssize_t show_fault(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max1668_data *data = max1668_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%u\n",
+ (data->alarms & (1 << 12)) && data->temp[index] == 127);
+}
+
+static ssize_t set_temp_max(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max1668_data *data = i2c_get_clientdata(client);
+ long temp;
+ int ret;
+
+ ret = kstrtol(buf, 10, &temp);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[index] = SENSORS_LIMIT(temp/1000, -128, 127);
+ if (i2c_smbus_write_byte_data(client,
+ MAX1668_REG_LIMH_WR(index),
+ data->temp_max[index]))
+ count = -EIO;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t set_temp_min(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max1668_data *data = i2c_get_clientdata(client);
+ long temp;
+ int ret;
+
+ ret = kstrtol(buf, 10, &temp);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ data->temp_min[index] = SENSORS_LIMIT(temp/1000, -128, 127);
+ if (i2c_smbus_write_byte_data(client,
+ MAX1668_REG_LIML_WR(index),
+ data->temp_max[index]))
+ count = -EIO;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max,
+ set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min,
+ set_temp_min, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max,
+ set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min,
+ set_temp_min, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max,
+ set_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min,
+ set_temp_min, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max,
+ set_temp_max, 3);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min,
+ set_temp_min, 3);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max,
+ set_temp_max, 4);
+static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min,
+ set_temp_min, 4);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4);
+
+/* Attributes common to MAX1668, MAX1989 and MAX1805 */
+static struct attribute *max1668_attribute_common[] = {
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ NULL
+};
+
+/* Attributes not present on MAX1805 */
+static struct attribute *max1668_attribute_unique[] = {
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_min.dev_attr.attr,
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ &sensor_dev_attr_temp5_fault.dev_attr.attr,
+ NULL
+};
+
+static mode_t max1668_attribute_mode(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ int ret = S_IRUGO;
+ if (read_only)
+ return ret;
+ if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp2_max.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp3_max.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp4_max.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp5_max.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp1_min.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp2_min.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp3_min.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp4_min.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp5_min.dev_attr.attr)
+ ret |= S_IWUSR;
+ return ret;
+}
+
+static const struct attribute_group max1668_group_common = {
+ .attrs = max1668_attribute_common,
+ .is_visible = max1668_attribute_mode
+};
+
+static const struct attribute_group max1668_group_unique = {
+ .attrs = max1668_attribute_unique,
+ .is_visible = max1668_attribute_mode
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max1668_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ const char *type_name;
+ int man_id, dev_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Check for unsupported part */
+ man_id = i2c_smbus_read_byte_data(client, MAX1668_REG_MAN_ID);
+ if (man_id != MAN_ID_MAXIM)
+ return -ENODEV;
+
+ dev_id = i2c_smbus_read_byte_data(client, MAX1668_REG_DEV_ID);
+ if (dev_id < 0)
+ return -ENODEV;
+
+ type_name = NULL;
+ if (dev_id == DEV_ID_MAX1668)
+ type_name = "max1668";
+ else if (dev_id == DEV_ID_MAX1805)
+ type_name = "max1805";
+ else if (dev_id == DEV_ID_MAX1989)
+ type_name = "max1989";
+
+ if (!type_name)
+ return -ENODEV;
+
+ strlcpy(info->type, type_name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int max1668_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct max1668_data *data;
+ int err;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = kzalloc(sizeof(struct max1668_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->type = id->driver_data;
+ mutex_init(&data->update_lock);
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &max1668_group_common);
+ if (err)
+ goto error_free;
+
+ if (data->type == max1668 || data->type == max1989) {
+ err = sysfs_create_group(&client->dev.kobj,
+ &max1668_group_unique);
+ if (err)
+ goto error_sysrem0;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto error_sysrem1;
+ }
+
+ return 0;
+
+error_sysrem1:
+ if (data->type == max1668 || data->type == max1989)
+ sysfs_remove_group(&client->dev.kobj, &max1668_group_unique);
+error_sysrem0:
+ sysfs_remove_group(&client->dev.kobj, &max1668_group_common);
+error_free:
+ kfree(data);
+ return err;
+}
+
+static int max1668_remove(struct i2c_client *client)
+{
+ struct max1668_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ if (data->type == max1668 || data->type == max1989)
+ sysfs_remove_group(&client->dev.kobj, &max1668_group_unique);
+
+ sysfs_remove_group(&client->dev.kobj, &max1668_group_common);
+
+ kfree(data);
+ return 0;
+}
+
+static const struct i2c_device_id max1668_id[] = {
+ { "max1668", max1668 },
+ { "max1805", max1805 },
+ { "max1989", max1989 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max1668_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver max1668_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "max1668",
+ },
+ .probe = max1668_probe,
+ .remove = max1668_remove,
+ .id_table = max1668_id,
+ .detect = max1668_detect,
+ .address_list = max1668_addr_list,
+};
+
+static int __init sensors_max1668_init(void)
+{
+ return i2c_add_driver(&max1668_driver);
+}
+
+static void __exit sensors_max1668_exit(void)
+{
+ i2c_del_driver(&max1668_driver);
+}
+
+MODULE_AUTHOR("David George <david.george@ska.ac.za>");
+MODULE_DESCRIPTION("MAX1668 remote temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_max1668_init)
+module_exit(sensors_max1668_exit)
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
index d5226c9e1201..ef65ab56b094 100644
--- a/drivers/hwmon/mc13783-adc.c
+++ b/drivers/hwmon/mc13783-adc.c
@@ -31,7 +31,7 @@
#define MC13783_ADC_NAME "mc13783-adc"
struct mc13783_adc_priv {
- struct mc13783 *mc13783;
+ struct mc13xxx *mc13xxx;
struct device *hwmon_dev;
};
@@ -51,8 +51,8 @@ static int mc13783_adc_read(struct device *dev,
unsigned int sample[4];
int ret;
- ret = mc13783_adc_do_conversion(priv->mc13783,
- MC13783_ADC_MODE_MULT_CHAN,
+ ret = mc13xxx_adc_do_conversion(priv->mc13xxx,
+ MC13XXX_ADC_MODE_MULT_CHAN,
channel, sample);
if (ret)
return ret;
@@ -147,9 +147,9 @@ static const struct attribute_group mc13783_group_ts = {
static int mc13783_adc_use_touchscreen(struct platform_device *pdev)
{
struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
- unsigned flags = mc13783_get_flags(priv->mc13783);
+ unsigned flags = mc13xxx_get_flags(priv->mc13xxx);
- return flags & MC13783_USE_TOUCHSCREEN;
+ return flags & MC13XXX_USE_TOUCHSCREEN;
}
static int __init mc13783_adc_probe(struct platform_device *pdev)
@@ -161,7 +161,7 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
+ priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
platform_set_drvdata(pdev, priv);
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
new file mode 100644
index 000000000000..eab11615dced
--- /dev/null
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -0,0 +1,452 @@
+/*
+ * ntc_thermistor.c - NTC Thermistors
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/math64.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+
+#include <linux/platform_data/ntc_thermistor.h>
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+struct ntc_compensation {
+ int temp_C;
+ unsigned int ohm;
+};
+
+/*
+ * A compensation table should be sorted by the values of .ohm
+ * in descending order.
+ * The following compensation tables are from the specification of Murata NTC
+ * Thermistors Datasheet
+ */
+const struct ntc_compensation ncpXXwb473[] = {
+ { .temp_C = -40, .ohm = 1747920 },
+ { .temp_C = -35, .ohm = 1245428 },
+ { .temp_C = -30, .ohm = 898485 },
+ { .temp_C = -25, .ohm = 655802 },
+ { .temp_C = -20, .ohm = 483954 },
+ { .temp_C = -15, .ohm = 360850 },
+ { .temp_C = -10, .ohm = 271697 },
+ { .temp_C = -5, .ohm = 206463 },
+ { .temp_C = 0, .ohm = 158214 },
+ { .temp_C = 5, .ohm = 122259 },
+ { .temp_C = 10, .ohm = 95227 },
+ { .temp_C = 15, .ohm = 74730 },
+ { .temp_C = 20, .ohm = 59065 },
+ { .temp_C = 25, .ohm = 47000 },
+ { .temp_C = 30, .ohm = 37643 },
+ { .temp_C = 35, .ohm = 30334 },
+ { .temp_C = 40, .ohm = 24591 },
+ { .temp_C = 45, .ohm = 20048 },
+ { .temp_C = 50, .ohm = 16433 },
+ { .temp_C = 55, .ohm = 13539 },
+ { .temp_C = 60, .ohm = 11209 },
+ { .temp_C = 65, .ohm = 9328 },
+ { .temp_C = 70, .ohm = 7798 },
+ { .temp_C = 75, .ohm = 6544 },
+ { .temp_C = 80, .ohm = 5518 },
+ { .temp_C = 85, .ohm = 4674 },
+ { .temp_C = 90, .ohm = 3972 },
+ { .temp_C = 95, .ohm = 3388 },
+ { .temp_C = 100, .ohm = 2902 },
+ { .temp_C = 105, .ohm = 2494 },
+ { .temp_C = 110, .ohm = 2150 },
+ { .temp_C = 115, .ohm = 1860 },
+ { .temp_C = 120, .ohm = 1615 },
+ { .temp_C = 125, .ohm = 1406 },
+};
+const struct ntc_compensation ncpXXwl333[] = {
+ { .temp_C = -40, .ohm = 1610154 },
+ { .temp_C = -35, .ohm = 1130850 },
+ { .temp_C = -30, .ohm = 802609 },
+ { .temp_C = -25, .ohm = 575385 },
+ { .temp_C = -20, .ohm = 416464 },
+ { .temp_C = -15, .ohm = 304219 },
+ { .temp_C = -10, .ohm = 224193 },
+ { .temp_C = -5, .ohm = 166623 },
+ { .temp_C = 0, .ohm = 124850 },
+ { .temp_C = 5, .ohm = 94287 },
+ { .temp_C = 10, .ohm = 71747 },
+ { .temp_C = 15, .ohm = 54996 },
+ { .temp_C = 20, .ohm = 42455 },
+ { .temp_C = 25, .ohm = 33000 },
+ { .temp_C = 30, .ohm = 25822 },
+ { .temp_C = 35, .ohm = 20335 },
+ { .temp_C = 40, .ohm = 16115 },
+ { .temp_C = 45, .ohm = 12849 },
+ { .temp_C = 50, .ohm = 10306 },
+ { .temp_C = 55, .ohm = 8314 },
+ { .temp_C = 60, .ohm = 6746 },
+ { .temp_C = 65, .ohm = 5503 },
+ { .temp_C = 70, .ohm = 4513 },
+ { .temp_C = 75, .ohm = 3721 },
+ { .temp_C = 80, .ohm = 3084 },
+ { .temp_C = 85, .ohm = 2569 },
+ { .temp_C = 90, .ohm = 2151 },
+ { .temp_C = 95, .ohm = 1809 },
+ { .temp_C = 100, .ohm = 1529 },
+ { .temp_C = 105, .ohm = 1299 },
+ { .temp_C = 110, .ohm = 1108 },
+ { .temp_C = 115, .ohm = 949 },
+ { .temp_C = 120, .ohm = 817 },
+ { .temp_C = 125, .ohm = 707 },
+};
+
+struct ntc_data {
+ struct device *hwmon_dev;
+ struct ntc_thermistor_platform_data *pdata;
+ const struct ntc_compensation *comp;
+ struct device *dev;
+ int n_comp;
+ char name[PLATFORM_NAME_SIZE];
+};
+
+static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
+{
+ if (divisor == 0 && dividend == 0)
+ return 0;
+ if (divisor == 0)
+ return UINT_MAX;
+ return div64_u64(dividend, divisor);
+}
+
+static unsigned int get_ohm_of_thermistor(struct ntc_data *data,
+ unsigned int uV)
+{
+ struct ntc_thermistor_platform_data *pdata = data->pdata;
+ u64 mV = uV / 1000;
+ u64 pmV = pdata->pullup_uV / 1000;
+ u64 N, puO, pdO;
+ puO = pdata->pullup_ohm;
+ pdO = pdata->pulldown_ohm;
+
+ if (mV == 0) {
+ if (pdata->connect == NTC_CONNECTED_POSITIVE)
+ return UINT_MAX;
+ return 0;
+ }
+ if (mV >= pmV)
+ return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
+ 0 : UINT_MAX;
+
+ if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0)
+ N = div64_u64_safe(pdO * (pmV - mV), mV);
+ else if (pdata->connect == NTC_CONNECTED_GROUND && pdO == 0)
+ N = div64_u64_safe(puO * mV, pmV - mV);
+ else if (pdata->connect == NTC_CONNECTED_POSITIVE)
+ N = div64_u64_safe(pdO * puO * (pmV - mV),
+ puO * mV - pdO * (pmV - mV));
+ else
+ N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV);
+
+ return (unsigned int) N;
+}
+
+static int lookup_comp(struct ntc_data *data,
+ unsigned int ohm, int *i_low, int *i_high)
+{
+ int start, end, mid = -1;
+
+ /* Do a binary search on compensation table */
+ start = 0;
+ end = data->n_comp;
+
+ while (end > start) {
+ mid = start + (end - start) / 2;
+ if (data->comp[mid].ohm < ohm)
+ end = mid;
+ else if (data->comp[mid].ohm > ohm)
+ start = mid + 1;
+ else
+ break;
+ }
+
+ if (mid == 0) {
+ if (data->comp[mid].ohm > ohm) {
+ *i_high = mid;
+ *i_low = mid + 1;
+ return 0;
+ } else {
+ *i_low = mid;
+ *i_high = -1;
+ return -EINVAL;
+ }
+ }
+ if (mid == (data->n_comp - 1)) {
+ if (data->comp[mid].ohm <= ohm) {
+ *i_low = mid;
+ *i_high = mid - 1;
+ return 0;
+ } else {
+ *i_low = -1;
+ *i_high = mid;
+ return -EINVAL;
+ }
+ }
+
+ if (data->comp[mid].ohm <= ohm) {
+ *i_low = mid;
+ *i_high = mid - 1;
+ } else {
+ *i_low = mid + 1;
+ *i_high = mid;
+ }
+
+ return 0;
+}
+
+static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp)
+{
+ int low, high;
+ int ret;
+
+ ret = lookup_comp(data, ohm, &low, &high);
+ if (ret) {
+ /* Unable to use linear approximation */
+ if (low != -1)
+ *temp = data->comp[low].temp_C * 1000;
+ else if (high != -1)
+ *temp = data->comp[high].temp_C * 1000;
+ else
+ return ret;
+ } else {
+ *temp = data->comp[low].temp_C * 1000 +
+ ((data->comp[high].temp_C - data->comp[low].temp_C) *
+ 1000 * ((int)ohm - (int)data->comp[low].ohm)) /
+ ((int)data->comp[high].ohm - (int)data->comp[low].ohm);
+ }
+
+ return 0;
+}
+
+static int ntc_thermistor_read(struct ntc_data *data, int *temp)
+{
+ int ret;
+ int read_ohm, read_uV;
+ unsigned int ohm = 0;
+
+ if (data->pdata->read_ohm) {
+ read_ohm = data->pdata->read_ohm();
+ if (read_ohm < 0)
+ return read_ohm;
+ ohm = (unsigned int)read_ohm;
+ }
+
+ if (data->pdata->read_uV) {
+ read_uV = data->pdata->read_uV();
+ if (read_uV < 0)
+ return read_uV;
+ ohm = get_ohm_of_thermistor(data, (unsigned int)read_uV);
+ }
+
+ ret = get_temp_mC(data, ohm, temp);
+ if (ret) {
+ dev_dbg(data->dev, "Sensor reading function not available.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static ssize_t ntc_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ntc_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+static ssize_t ntc_show_type(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "4\n");
+}
+
+static ssize_t ntc_show_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ntc_data *data = dev_get_drvdata(dev);
+ int temp, ret;
+
+ ret = ntc_thermistor_read(data, &temp);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%d\n", temp);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0);
+static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL);
+
+static struct attribute *ntc_attributes[] = {
+ &dev_attr_name.attr,
+ &sensor_dev_attr_temp1_type.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ntc_attr_group = {
+ .attrs = ntc_attributes,
+};
+
+static int __devinit ntc_thermistor_probe(struct platform_device *pdev)
+{
+ struct ntc_data *data;
+ struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data;
+ int ret = 0;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform init data supplied.\n");
+ return -ENODEV;
+ }
+
+ /* Either one of the two is required. */
+ if (!pdata->read_uV && !pdata->read_ohm) {
+ dev_err(&pdev->dev, "Both read_uV and read_ohm missing."
+ "Need either one of the two.\n");
+ return -EINVAL;
+ }
+
+ if (pdata->read_uV && pdata->read_ohm) {
+ dev_warn(&pdev->dev, "Only one of read_uV and read_ohm "
+ "is needed; ignoring read_uV.\n");
+ pdata->read_uV = NULL;
+ }
+
+ if (pdata->read_uV && (pdata->pullup_uV == 0 ||
+ (pdata->pullup_ohm == 0 && pdata->connect ==
+ NTC_CONNECTED_GROUND) ||
+ (pdata->pulldown_ohm == 0 && pdata->connect ==
+ NTC_CONNECTED_POSITIVE) ||
+ (pdata->connect != NTC_CONNECTED_POSITIVE &&
+ pdata->connect != NTC_CONNECTED_GROUND))) {
+ dev_err(&pdev->dev, "Required data to use read_uV not "
+ "supplied.\n");
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = &pdev->dev;
+ data->pdata = pdata;
+ strncpy(data->name, pdev->id_entry->name, PLATFORM_NAME_SIZE);
+
+ switch (pdev->id_entry->driver_data) {
+ case TYPE_NCPXXWB473:
+ data->comp = ncpXXwb473;
+ data->n_comp = ARRAY_SIZE(ncpXXwb473);
+ break;
+ case TYPE_NCPXXWL333:
+ data->comp = ncpXXwl333;
+ data->n_comp = ARRAY_SIZE(ncpXXwl333);
+ break;
+ default:
+ dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
+ pdev->id_entry->driver_data,
+ pdev->id_entry->name);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group);
+ if (ret) {
+ dev_err(data->dev, "unable to create sysfs files\n");
+ goto err;
+ }
+
+ data->hwmon_dev = hwmon_device_register(data->dev);
+ if (IS_ERR_OR_NULL(data->hwmon_dev)) {
+ dev_err(data->dev, "unable to register as hwmon device.\n");
+ ret = -EINVAL;
+ goto err_after_sysfs;
+ }
+
+ dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n",
+ pdev->name, pdev->id, pdev->id_entry->name,
+ pdev->id_entry->driver_data);
+ return 0;
+err_after_sysfs:
+ sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
+err:
+ kfree(data);
+ return ret;
+}
+
+static int __devexit ntc_thermistor_remove(struct platform_device *pdev)
+{
+ struct ntc_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(data);
+
+ return 0;
+}
+
+static const struct platform_device_id ntc_thermistor_id[] = {
+ { "ncp15wb473", TYPE_NCPXXWB473 },
+ { "ncp18wb473", TYPE_NCPXXWB473 },
+ { "ncp21wb473", TYPE_NCPXXWB473 },
+ { "ncp03wb473", TYPE_NCPXXWB473 },
+ { "ncp15wl333", TYPE_NCPXXWL333 },
+ { },
+};
+
+static struct platform_driver ntc_thermistor_driver = {
+ .driver = {
+ .name = "ntc-thermistor",
+ .owner = THIS_MODULE,
+ },
+ .probe = ntc_thermistor_probe,
+ .remove = __devexit_p(ntc_thermistor_remove),
+ .id_table = ntc_thermistor_id,
+};
+
+static int __init ntc_thermistor_init(void)
+{
+ return platform_driver_register(&ntc_thermistor_driver);
+}
+
+module_init(ntc_thermistor_init);
+
+static void __exit ntc_thermistor_cleanup(void)
+{
+ platform_driver_unregister(&ntc_thermistor_driver);
+}
+
+module_exit(ntc_thermistor_cleanup);
+
+MODULE_DESCRIPTION("NTC Thermistor Driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ntc-thermistor");
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
new file mode 100644
index 000000000000..4b26f51920ba
--- /dev/null
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -0,0 +1,122 @@
+#
+# PMBus chip drivers configuration
+#
+
+menuconfig PMBUS
+ tristate "PMBus support"
+ depends on I2C && EXPERIMENTAL
+ default n
+ help
+ Say yes here if you want to enable PMBus support.
+
+ This driver can also be built as a module. If so, the module will
+ be called pmbus_core.
+
+if PMBUS
+
+config SENSORS_PMBUS
+ tristate "Generic PMBus devices"
+ default y
+ help
+ If you say yes here you get hardware monitoring support for generic
+ PMBus devices, including but not limited to ADP4000, BMR450, BMR451,
+ BMR453, BMR454, NCP4200, and NCP4208.
+
+ This driver can also be built as a module. If so, the module will
+ be called pmbus.
+
+config SENSORS_ADM1275
+ tristate "Analog Devices ADM1275 and compatibles"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADM1275 and ADM1276 Hot-Swap Controller and Digital Power
+ Monitor.
+
+ This driver can also be built as a module. If so, the module will
+ be called adm1275.
+
+config SENSORS_LM25066
+ tristate "National Semiconductor LM25066 and compatibles"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for National
+ Semiconductor LM25066, LM5064, and LM5066.
+
+ This driver can also be built as a module. If so, the module will
+ be called lm25066.
+
+config SENSORS_LTC2978
+ tristate "Linear Technologies LTC2978 and LTC3880"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for Linear
+ Technology LTC2978 and LTC3880.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2978.
+
+config SENSORS_MAX16064
+ tristate "Maxim MAX16064"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for Maxim
+ MAX16064.
+
+ This driver can also be built as a module. If so, the module will
+ be called max16064.
+
+config SENSORS_MAX34440
+ tristate "Maxim MAX34440/MAX34441"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for Maxim
+ MAX34440 and MAX34441.
+
+ This driver can also be built as a module. If so, the module will
+ be called max34440.
+
+config SENSORS_MAX8688
+ tristate "Maxim MAX8688"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for Maxim
+ MAX8688.
+
+ This driver can also be built as a module. If so, the module will
+ be called max8688.
+
+config SENSORS_UCD9000
+ tristate "TI UCD90120, UCD90124, UCD9090, UCD90910"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for TI
+ UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health
+ Controllers.
+
+ This driver can also be built as a module. If so, the module will
+ be called ucd9000.
+
+config SENSORS_UCD9200
+ tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for TI
+ UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248
+ Digital PWM System Controllers.
+
+ This driver can also be built as a module. If so, the module will
+ be called ucd9200.
+
+config SENSORS_ZL6100
+ tristate "Intersil ZL6100 and compatibles"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for Intersil
+ ZL2004, ZL2006, ZL2008, ZL2105, ZL2106, ZL6100, and ZL6105 Digital
+ DC/DC Controllers.
+
+ This driver can also be built as a module. If so, the module will
+ be called zl6100.
+
+endif # PMBUS
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
new file mode 100644
index 000000000000..789376c85dbb
--- /dev/null
+++ b/drivers/hwmon/pmbus/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for PMBus chip drivers.
+#
+
+obj-$(CONFIG_PMBUS) += pmbus_core.o
+obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
+obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
+obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
+obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
+obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
+obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
+obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
+obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
+obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
+obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
new file mode 100644
index 000000000000..980a4d9d5028
--- /dev/null
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -0,0 +1,314 @@
+/*
+ * Hardware monitoring driver for Analog Devices ADM1275 Hot-Swap Controller
+ * and Digital Power Monitor
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+enum chips { adm1275, adm1276 };
+
+#define ADM1275_PEAK_IOUT 0xd0
+#define ADM1275_PEAK_VIN 0xd1
+#define ADM1275_PEAK_VOUT 0xd2
+#define ADM1275_PMON_CONFIG 0xd4
+
+#define ADM1275_VIN_VOUT_SELECT (1 << 6)
+#define ADM1275_VRANGE (1 << 5)
+
+#define ADM1275_IOUT_WARN2_LIMIT 0xd7
+#define ADM1275_DEVICE_CONFIG 0xd8
+
+#define ADM1275_IOUT_WARN2_SELECT (1 << 4)
+
+#define ADM1276_PEAK_PIN 0xda
+
+#define ADM1275_MFR_STATUS_IOUT_WARN2 (1 << 0)
+
+struct adm1275_data {
+ int id;
+ bool have_oc_fault;
+ struct pmbus_driver_info info;
+};
+
+#define to_adm1275_data(x) container_of(x, struct adm1275_data, info)
+
+static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct adm1275_data *data = to_adm1275_data(info);
+ int ret = 0;
+
+ if (page)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_IOUT_UC_FAULT_LIMIT:
+ if (data->have_oc_fault) {
+ ret = -ENXIO;
+ break;
+ }
+ ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT);
+ break;
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ if (!data->have_oc_fault) {
+ ret = -ENXIO;
+ break;
+ }
+ ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT);
+ break;
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT);
+ break;
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT);
+ break;
+ case PMBUS_VIRT_READ_VIN_MAX:
+ ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN);
+ break;
+ case PMBUS_VIRT_READ_PIN_MAX:
+ if (data->id != adm1276) {
+ ret = -ENXIO;
+ break;
+ }
+ ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ break;
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ if (data->id != adm1276)
+ ret = -ENXIO;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ int ret;
+
+ if (page)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_IOUT_UC_FAULT_LIMIT:
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ ret = pmbus_write_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT,
+ word);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0);
+ break;
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0);
+ break;
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct adm1275_data *data = to_adm1275_data(info);
+ int mfr_status, ret;
+
+ if (page > 0)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_STATUS_IOUT:
+ ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT);
+ if (ret < 0)
+ break;
+ mfr_status = pmbus_read_byte_data(client, page,
+ PMBUS_STATUS_MFR_SPECIFIC);
+ if (mfr_status < 0) {
+ ret = mfr_status;
+ break;
+ }
+ if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) {
+ ret |= data->have_oc_fault ?
+ PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT;
+ }
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int adm1275_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int config, device_config;
+ int ret;
+ struct pmbus_driver_info *info;
+ struct adm1275_data *data;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA))
+ return -ENODEV;
+
+ data = kzalloc(sizeof(struct adm1275_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
+ if (config < 0) {
+ ret = config;
+ goto err_mem;
+ }
+
+ device_config = i2c_smbus_read_byte_data(client, ADM1275_DEVICE_CONFIG);
+ if (device_config < 0) {
+ ret = device_config;
+ goto err_mem;
+ }
+
+ data->id = id->driver_data;
+ info = &data->info;
+
+ info->pages = 1;
+ info->format[PSC_VOLTAGE_IN] = direct;
+ info->format[PSC_VOLTAGE_OUT] = direct;
+ info->format[PSC_CURRENT_OUT] = direct;
+ info->m[PSC_CURRENT_OUT] = 807;
+ info->b[PSC_CURRENT_OUT] = 20475;
+ info->R[PSC_CURRENT_OUT] = -1;
+ info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
+
+ info->read_word_data = adm1275_read_word_data;
+ info->read_byte_data = adm1275_read_byte_data;
+ info->write_word_data = adm1275_write_word_data;
+
+ if (config & ADM1275_VRANGE) {
+ info->m[PSC_VOLTAGE_IN] = 19199;
+ info->b[PSC_VOLTAGE_IN] = 0;
+ info->R[PSC_VOLTAGE_IN] = -2;
+ info->m[PSC_VOLTAGE_OUT] = 19199;
+ info->b[PSC_VOLTAGE_OUT] = 0;
+ info->R[PSC_VOLTAGE_OUT] = -2;
+ } else {
+ info->m[PSC_VOLTAGE_IN] = 6720;
+ info->b[PSC_VOLTAGE_IN] = 0;
+ info->R[PSC_VOLTAGE_IN] = -1;
+ info->m[PSC_VOLTAGE_OUT] = 6720;
+ info->b[PSC_VOLTAGE_OUT] = 0;
+ info->R[PSC_VOLTAGE_OUT] = -1;
+ }
+
+ if (device_config & ADM1275_IOUT_WARN2_SELECT)
+ data->have_oc_fault = true;
+
+ switch (id->driver_data) {
+ case adm1275:
+ if (config & ADM1275_VIN_VOUT_SELECT)
+ info->func[0] |=
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ else
+ info->func[0] |=
+ PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
+ break;
+ case adm1276:
+ info->format[PSC_POWER] = direct;
+ info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN
+ | PMBUS_HAVE_STATUS_INPUT;
+ if (config & ADM1275_VIN_VOUT_SELECT)
+ info->func[0] |=
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ if (config & ADM1275_VRANGE) {
+ info->m[PSC_POWER] = 6043;
+ info->b[PSC_POWER] = 0;
+ info->R[PSC_POWER] = -2;
+ } else {
+ info->m[PSC_POWER] = 2115;
+ info->b[PSC_POWER] = 0;
+ info->R[PSC_POWER] = -1;
+ }
+ break;
+ }
+
+ ret = pmbus_do_probe(client, id, info);
+ if (ret)
+ goto err_mem;
+ return 0;
+
+err_mem:
+ kfree(data);
+ return ret;
+}
+
+static int adm1275_remove(struct i2c_client *client)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct adm1275_data *data = to_adm1275_data(info);
+
+ pmbus_do_remove(client);
+ kfree(data);
+ return 0;
+}
+
+static const struct i2c_device_id adm1275_id[] = {
+ { "adm1275", adm1275 },
+ { "adm1276", adm1276 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1275_id);
+
+static struct i2c_driver adm1275_driver = {
+ .driver = {
+ .name = "adm1275",
+ },
+ .probe = adm1275_probe,
+ .remove = adm1275_remove,
+ .id_table = adm1275_id,
+};
+
+static int __init adm1275_init(void)
+{
+ return i2c_add_driver(&adm1275_driver);
+}
+
+static void __exit adm1275_exit(void)
+{
+ i2c_del_driver(&adm1275_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles");
+MODULE_LICENSE("GPL");
+module_init(adm1275_init);
+module_exit(adm1275_exit);
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
new file mode 100644
index 000000000000..84a37f0c8db6
--- /dev/null
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -0,0 +1,351 @@
+/*
+ * Hardware monitoring driver for LM25066 / LM5064 / LM5066
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+enum chips { lm25066, lm5064, lm5066 };
+
+#define LM25066_READ_VAUX 0xd0
+#define LM25066_MFR_READ_IIN 0xd1
+#define LM25066_MFR_READ_PIN 0xd2
+#define LM25066_MFR_IIN_OC_WARN_LIMIT 0xd3
+#define LM25066_MFR_PIN_OP_WARN_LIMIT 0xd4
+#define LM25066_READ_PIN_PEAK 0xd5
+#define LM25066_CLEAR_PIN_PEAK 0xd6
+#define LM25066_DEVICE_SETUP 0xd9
+#define LM25066_READ_AVG_VIN 0xdc
+#define LM25066_READ_AVG_VOUT 0xdd
+#define LM25066_READ_AVG_IIN 0xde
+#define LM25066_READ_AVG_PIN 0xdf
+
+#define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */
+
+struct lm25066_data {
+ int id;
+ struct pmbus_driver_info info;
+};
+
+#define to_lm25066_data(x) container_of(x, struct lm25066_data, info)
+
+static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct lm25066_data *data = to_lm25066_data(info);
+ int ret;
+
+ if (page > 1)
+ return -ENXIO;
+
+ /* Map READ_VAUX into READ_VOUT register on page 1 */
+ if (page == 1) {
+ switch (reg) {
+ case PMBUS_READ_VOUT:
+ ret = pmbus_read_word_data(client, 0,
+ LM25066_READ_VAUX);
+ if (ret < 0)
+ break;
+ /* Adjust returned value to match VOUT coefficients */
+ switch (data->id) {
+ case lm25066:
+ /* VOUT: 4.54 mV VAUX: 283.2 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
+ break;
+ case lm5064:
+ /* VOUT: 4.53 mV VAUX: 700 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 70, 453);
+ break;
+ case lm5066:
+ /* VOUT: 2.18 mV VAUX: 725 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
+ break;
+ }
+ break;
+ default:
+ /* No other valid registers on page 1 */
+ ret = -ENXIO;
+ break;
+ }
+ goto done;
+ }
+
+ switch (reg) {
+ case PMBUS_READ_IIN:
+ ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN);
+ break;
+ case PMBUS_READ_PIN:
+ ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN);
+ break;
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ ret = pmbus_read_word_data(client, 0,
+ LM25066_MFR_IIN_OC_WARN_LIMIT);
+ break;
+ case PMBUS_PIN_OP_WARN_LIMIT:
+ ret = pmbus_read_word_data(client, 0,
+ LM25066_MFR_PIN_OP_WARN_LIMIT);
+ break;
+ case PMBUS_VIRT_READ_VIN_AVG:
+ ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN);
+ break;
+ case PMBUS_VIRT_READ_VOUT_AVG:
+ ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT);
+ break;
+ case PMBUS_VIRT_READ_IIN_AVG:
+ ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN);
+ break;
+ case PMBUS_VIRT_READ_PIN_AVG:
+ ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN);
+ break;
+ case PMBUS_VIRT_READ_PIN_MAX:
+ ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK);
+ break;
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+done:
+ return ret;
+}
+
+static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ int ret;
+
+ if (page > 1)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ ret = pmbus_write_word_data(client, 0,
+ LM25066_MFR_IIN_OC_WARN_LIMIT,
+ word);
+ break;
+ case PMBUS_PIN_OP_WARN_LIMIT:
+ ret = pmbus_write_word_data(client, 0,
+ LM25066_MFR_PIN_OP_WARN_LIMIT,
+ word);
+ break;
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int lm25066_write_byte(struct i2c_client *client, int page, u8 value)
+{
+ if (page > 1)
+ return -ENXIO;
+
+ if (page <= 0)
+ return pmbus_write_byte(client, page, value);
+
+ return 0;
+}
+
+static int lm25066_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int config;
+ int ret;
+ struct lm25066_data *data;
+ struct pmbus_driver_info *info;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA))
+ return -ENODEV;
+
+ data = kzalloc(sizeof(struct lm25066_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP);
+ if (config < 0) {
+ ret = config;
+ goto err_mem;
+ }
+
+ data->id = id->driver_data;
+ info = &data->info;
+
+ info->pages = 2;
+ info->format[PSC_VOLTAGE_IN] = direct;
+ info->format[PSC_VOLTAGE_OUT] = direct;
+ info->format[PSC_CURRENT_IN] = direct;
+ info->format[PSC_TEMPERATURE] = direct;
+ info->format[PSC_POWER] = direct;
+
+ info->m[PSC_TEMPERATURE] = 16;
+ info->b[PSC_TEMPERATURE] = 0;
+ info->R[PSC_TEMPERATURE] = 0;
+
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT
+ | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN
+ | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ info->func[1] = PMBUS_HAVE_VOUT;
+
+ info->read_word_data = lm25066_read_word_data;
+ info->write_word_data = lm25066_write_word_data;
+ info->write_byte = lm25066_write_byte;
+
+ switch (id->driver_data) {
+ case lm25066:
+ info->m[PSC_VOLTAGE_IN] = 22070;
+ info->b[PSC_VOLTAGE_IN] = 0;
+ info->R[PSC_VOLTAGE_IN] = -2;
+ info->m[PSC_VOLTAGE_OUT] = 22070;
+ info->b[PSC_VOLTAGE_OUT] = 0;
+ info->R[PSC_VOLTAGE_OUT] = -2;
+
+ if (config & LM25066_DEV_SETUP_CL) {
+ info->m[PSC_CURRENT_IN] = 6852;
+ info->b[PSC_CURRENT_IN] = 0;
+ info->R[PSC_CURRENT_IN] = -2;
+ info->m[PSC_POWER] = 369;
+ info->b[PSC_POWER] = 0;
+ info->R[PSC_POWER] = -2;
+ } else {
+ info->m[PSC_CURRENT_IN] = 13661;
+ info->b[PSC_CURRENT_IN] = 0;
+ info->R[PSC_CURRENT_IN] = -2;
+ info->m[PSC_POWER] = 736;
+ info->b[PSC_POWER] = 0;
+ info->R[PSC_POWER] = -2;
+ }
+ break;
+ case lm5064:
+ info->m[PSC_VOLTAGE_IN] = 22075;
+ info->b[PSC_VOLTAGE_IN] = 0;
+ info->R[PSC_VOLTAGE_IN] = -2;
+ info->m[PSC_VOLTAGE_OUT] = 22075;
+ info->b[PSC_VOLTAGE_OUT] = 0;
+ info->R[PSC_VOLTAGE_OUT] = -2;
+
+ if (config & LM25066_DEV_SETUP_CL) {
+ info->m[PSC_CURRENT_IN] = 6713;
+ info->b[PSC_CURRENT_IN] = 0;
+ info->R[PSC_CURRENT_IN] = -2;
+ info->m[PSC_POWER] = 3619;
+ info->b[PSC_POWER] = 0;
+ info->R[PSC_POWER] = -3;
+ } else {
+ info->m[PSC_CURRENT_IN] = 13426;
+ info->b[PSC_CURRENT_IN] = 0;
+ info->R[PSC_CURRENT_IN] = -2;
+ info->m[PSC_POWER] = 7238;
+ info->b[PSC_POWER] = 0;
+ info->R[PSC_POWER] = -3;
+ }
+ break;
+ case lm5066:
+ info->m[PSC_VOLTAGE_IN] = 4587;
+ info->b[PSC_VOLTAGE_IN] = 0;
+ info->R[PSC_VOLTAGE_IN] = -2;
+ info->m[PSC_VOLTAGE_OUT] = 4587;
+ info->b[PSC_VOLTAGE_OUT] = 0;
+ info->R[PSC_VOLTAGE_OUT] = -2;
+
+ if (config & LM25066_DEV_SETUP_CL) {
+ info->m[PSC_CURRENT_IN] = 10753;
+ info->b[PSC_CURRENT_IN] = 0;
+ info->R[PSC_CURRENT_IN] = -2;
+ info->m[PSC_POWER] = 1204;
+ info->b[PSC_POWER] = 0;
+ info->R[PSC_POWER] = -3;
+ } else {
+ info->m[PSC_CURRENT_IN] = 5405;
+ info->b[PSC_CURRENT_IN] = 0;
+ info->R[PSC_CURRENT_IN] = -2;
+ info->m[PSC_POWER] = 605;
+ info->b[PSC_POWER] = 0;
+ info->R[PSC_POWER] = -3;
+ }
+ break;
+ default:
+ ret = -ENODEV;
+ goto err_mem;
+ }
+
+ ret = pmbus_do_probe(client, id, info);
+ if (ret)
+ goto err_mem;
+ return 0;
+
+err_mem:
+ kfree(data);
+ return ret;
+}
+
+static int lm25066_remove(struct i2c_client *client)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct lm25066_data *data = to_lm25066_data(info);
+
+ pmbus_do_remove(client);
+ kfree(data);
+ return 0;
+}
+
+static const struct i2c_device_id lm25066_id[] = {
+ {"lm25066", lm25066},
+ {"lm5064", lm5064},
+ {"lm5066", lm5066},
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, lm25066_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver lm25066_driver = {
+ .driver = {
+ .name = "lm25066",
+ },
+ .probe = lm25066_probe,
+ .remove = lm25066_remove,
+ .id_table = lm25066_id,
+};
+
+static int __init lm25066_init(void)
+{
+ return i2c_add_driver(&lm25066_driver);
+}
+
+static void __exit lm25066_exit(void)
+{
+ i2c_del_driver(&lm25066_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066");
+MODULE_LICENSE("GPL");
+module_init(lm25066_init);
+module_exit(lm25066_exit);
diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
new file mode 100644
index 000000000000..820fff48910b
--- /dev/null
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -0,0 +1,408 @@
+/*
+ * Hardware monitoring driver for LTC2978 and LTC3880
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+enum chips { ltc2978, ltc3880 };
+
+/* LTC2978 and LTC3880 */
+#define LTC2978_MFR_VOUT_PEAK 0xdd
+#define LTC2978_MFR_VIN_PEAK 0xde
+#define LTC2978_MFR_TEMPERATURE_PEAK 0xdf
+#define LTC2978_MFR_SPECIAL_ID 0xe7
+
+/* LTC2978 only */
+#define LTC2978_MFR_VOUT_MIN 0xfb
+#define LTC2978_MFR_VIN_MIN 0xfc
+#define LTC2978_MFR_TEMPERATURE_MIN 0xfd
+
+/* LTC3880 only */
+#define LTC3880_MFR_IOUT_PEAK 0xd7
+#define LTC3880_MFR_CLEAR_PEAKS 0xe3
+#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4
+
+#define LTC2978_ID_REV1 0x0121
+#define LTC2978_ID_REV2 0x0122
+#define LTC3880_ID 0x4000
+#define LTC3880_ID_MASK 0xff00
+
+/*
+ * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which
+ * happens pretty much each time chip data is updated. Raw peak data therefore
+ * does not provide much value. To be able to provide useful peak data, keep an
+ * internal cache of measured peak data, which is only cleared if an explicit
+ * "clear peak" command is executed for the sensor in question.
+ */
+struct ltc2978_data {
+ enum chips id;
+ int vin_min, vin_max;
+ int temp_min, temp_max;
+ int vout_min[8], vout_max[8];
+ int iout_max[2];
+ int temp2_max[2];
+ struct pmbus_driver_info info;
+};
+
+#define to_ltc2978_data(x) container_of(x, struct ltc2978_data, info)
+
+static inline int lin11_to_val(int data)
+{
+ s16 e = ((s16)data) >> 11;
+ s32 m = (((s16)(data << 5)) >> 5);
+
+ /*
+ * mantissa is 10 bit + sign, exponent adds up to 15 bit.
+ * Add 6 bit to exponent for maximum accuracy (10 + 15 + 6 = 31).
+ */
+ e += 6;
+ return (e < 0 ? m >> -e : m << e);
+}
+
+static int ltc2978_read_word_data_common(struct i2c_client *client, int page,
+ int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ltc2978_data *data = to_ltc2978_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VIN_MAX:
+ ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_PEAK);
+ if (ret >= 0) {
+ if (lin11_to_val(ret) > lin11_to_val(data->vin_max))
+ data->vin_max = ret;
+ ret = data->vin_max;
+ }
+ break;
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK);
+ if (ret >= 0) {
+ /*
+ * VOUT is 16 bit unsigned with fixed exponent,
+ * so we can compare it directly
+ */
+ if (ret > data->vout_max[page])
+ data->vout_max[page] = ret;
+ ret = data->vout_max[page];
+ }
+ break;
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ ret = pmbus_read_word_data(client, page,
+ LTC2978_MFR_TEMPERATURE_PEAK);
+ if (ret >= 0) {
+ if (lin11_to_val(ret) > lin11_to_val(data->temp_max))
+ data->temp_max = ret;
+ ret = data->temp_max;
+ }
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ltc2978_data *data = to_ltc2978_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VIN_MIN:
+ ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_MIN);
+ if (ret >= 0) {
+ if (lin11_to_val(ret) < lin11_to_val(data->vin_min))
+ data->vin_min = ret;
+ ret = data->vin_min;
+ }
+ break;
+ case PMBUS_VIRT_READ_VOUT_MIN:
+ ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_MIN);
+ if (ret >= 0) {
+ /*
+ * VOUT_MIN is known to not be supported on some lots
+ * of LTC2978 revision 1, and will return the maximum
+ * possible voltage if read. If VOUT_MAX is valid and
+ * lower than the reading of VOUT_MIN, use it instead.
+ */
+ if (data->vout_max[page] && ret > data->vout_max[page])
+ ret = data->vout_max[page];
+ if (ret < data->vout_min[page])
+ data->vout_min[page] = ret;
+ ret = data->vout_min[page];
+ }
+ break;
+ case PMBUS_VIRT_READ_TEMP_MIN:
+ ret = pmbus_read_word_data(client, page,
+ LTC2978_MFR_TEMPERATURE_MIN);
+ if (ret >= 0) {
+ if (lin11_to_val(ret)
+ < lin11_to_val(data->temp_min))
+ data->temp_min = ret;
+ ret = data->temp_min;
+ }
+ break;
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_READ_TEMP2_MAX:
+ case PMBUS_VIRT_RESET_TEMP2_HISTORY:
+ ret = -ENXIO;
+ break;
+ default:
+ ret = ltc2978_read_word_data_common(client, page, reg);
+ break;
+ }
+ return ret;
+}
+
+static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ltc2978_data *data = to_ltc2978_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ ret = pmbus_read_word_data(client, page, LTC3880_MFR_IOUT_PEAK);
+ if (ret >= 0) {
+ if (lin11_to_val(ret)
+ > lin11_to_val(data->iout_max[page]))
+ data->iout_max[page] = ret;
+ ret = data->iout_max[page];
+ }
+ break;
+ case PMBUS_VIRT_READ_TEMP2_MAX:
+ ret = pmbus_read_word_data(client, page,
+ LTC3880_MFR_TEMPERATURE2_PEAK);
+ if (ret >= 0) {
+ if (lin11_to_val(ret)
+ > lin11_to_val(data->temp2_max[page]))
+ data->temp2_max[page] = ret;
+ ret = data->temp2_max[page];
+ }
+ break;
+ case PMBUS_VIRT_READ_VIN_MIN:
+ case PMBUS_VIRT_READ_VOUT_MIN:
+ case PMBUS_VIRT_READ_TEMP_MIN:
+ ret = -ENXIO;
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP2_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = ltc2978_read_word_data_common(client, page, reg);
+ break;
+ }
+ return ret;
+}
+
+static int ltc2978_clear_peaks(struct i2c_client *client, int page,
+ enum chips id)
+{
+ int ret;
+
+ if (id == ltc2978)
+ ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
+ else
+ ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS);
+
+ return ret;
+}
+
+static int ltc2978_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ltc2978_data *data = to_ltc2978_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ data->iout_max[page] = 0x7fff;
+ ret = ltc2978_clear_peaks(client, page, data->id);
+ break;
+ case PMBUS_VIRT_RESET_TEMP2_HISTORY:
+ data->temp2_max[page] = 0x7fff;
+ ret = ltc2978_clear_peaks(client, page, data->id);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ data->vout_min[page] = 0xffff;
+ data->vout_max[page] = 0;
+ ret = ltc2978_clear_peaks(client, page, data->id);
+ break;
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ data->vin_min = 0x7bff;
+ data->vin_max = 0;
+ ret = ltc2978_clear_peaks(client, page, data->id);
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ data->temp_min = 0x7bff;
+ data->temp_max = 0x7fff;
+ ret = ltc2978_clear_peaks(client, page, data->id);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static const struct i2c_device_id ltc2978_id[] = {
+ {"ltc2978", ltc2978},
+ {"ltc3880", ltc3880},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ltc2978_id);
+
+static int ltc2978_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int chip_id, ret, i;
+ struct ltc2978_data *data;
+ struct pmbus_driver_info *info;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENODEV;
+
+ data = kzalloc(sizeof(struct ltc2978_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID);
+ if (chip_id < 0) {
+ ret = chip_id;
+ goto err_mem;
+ }
+
+ if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) {
+ data->id = ltc2978;
+ } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) {
+ data->id = ltc3880;
+ } else {
+ dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id);
+ ret = -ENODEV;
+ goto err_mem;
+ }
+ if (data->id != id->driver_data)
+ dev_warn(&client->dev,
+ "Device mismatch: Configured %s, detected %s\n",
+ id->name,
+ ltc2978_id[data->id].name);
+
+ info = &data->info;
+ info->write_word_data = ltc2978_write_word_data;
+
+ data->vout_min[0] = 0xffff;
+ data->vin_min = 0x7bff;
+ data->temp_min = 0x7bff;
+ data->temp_max = 0x7fff;
+
+ switch (id->driver_data) {
+ case ltc2978:
+ info->read_word_data = ltc2978_read_word_data;
+ info->pages = 8;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ for (i = 1; i < 8; i++) {
+ info->func[i] = PMBUS_HAVE_VOUT
+ | PMBUS_HAVE_STATUS_VOUT;
+ data->vout_min[i] = 0xffff;
+ }
+ break;
+ case ltc3880:
+ info->read_word_data = ltc3880_read_word_data;
+ info->pages = 2;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
+ | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
+ info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_POUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ data->vout_min[1] = 0xffff;
+ break;
+ default:
+ ret = -ENODEV;
+ goto err_mem;
+ }
+
+ ret = pmbus_do_probe(client, id, info);
+ if (ret)
+ goto err_mem;
+ return 0;
+
+err_mem:
+ kfree(data);
+ return ret;
+}
+
+static int ltc2978_remove(struct i2c_client *client)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct ltc2978_data *data = to_ltc2978_data(info);
+
+ pmbus_do_remove(client);
+ kfree(data);
+ return 0;
+}
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ltc2978_driver = {
+ .driver = {
+ .name = "ltc2978",
+ },
+ .probe = ltc2978_probe,
+ .remove = ltc2978_remove,
+ .id_table = ltc2978_id,
+};
+
+static int __init ltc2978_init(void)
+{
+ return i2c_add_driver(&ltc2978_driver);
+}
+
+static void __exit ltc2978_exit(void)
+{
+ i2c_del_driver(&ltc2978_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus driver for LTC2978 and LTC3880");
+MODULE_LICENSE("GPL");
+module_init(ltc2978_init);
+module_exit(ltc2978_exit);
diff --git a/drivers/hwmon/max16064.c b/drivers/hwmon/pmbus/max16064.c
index 1d6d717060d3..1d77cf4d2d44 100644
--- a/drivers/hwmon/max16064.c
+++ b/drivers/hwmon/pmbus/max16064.c
@@ -25,11 +25,60 @@
#include <linux/i2c.h>
#include "pmbus.h"
+#define MAX16064_MFR_VOUT_PEAK 0xd4
+#define MAX16064_MFR_TEMPERATURE_PEAK 0xd6
+
+static int max16064_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ ret = pmbus_read_word_data(client, page,
+ MAX16064_MFR_VOUT_PEAK);
+ break;
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ ret = pmbus_read_word_data(client, page,
+ MAX16064_MFR_TEMPERATURE_PEAK);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int max16064_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ MAX16064_MFR_VOUT_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ MAX16064_MFR_TEMPERATURE_PEAK,
+ 0xffff);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
static struct pmbus_driver_info max16064_info = {
.pages = 4,
- .direct[PSC_VOLTAGE_IN] = true,
- .direct[PSC_VOLTAGE_OUT] = true,
- .direct[PSC_TEMPERATURE] = true,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
.m[PSC_VOLTAGE_IN] = 19995,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = -1,
@@ -44,6 +93,8 @@ static struct pmbus_driver_info max16064_info = {
.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .read_word_data = max16064_read_word_data,
+ .write_word_data = max16064_write_word_data,
};
static int max16064_probe(struct i2c_client *client,
@@ -54,7 +105,8 @@ static int max16064_probe(struct i2c_client *client,
static int max16064_remove(struct i2c_client *client)
{
- return pmbus_do_remove(client);
+ pmbus_do_remove(client);
+ return 0;
}
static const struct i2c_device_id max16064_id[] = {
diff --git a/drivers/hwmon/max34440.c b/drivers/hwmon/pmbus/max34440.c
index db11e1a175b2..beaf5a8d9c45 100644
--- a/drivers/hwmon/max34440.c
+++ b/drivers/hwmon/pmbus/max34440.c
@@ -27,19 +27,80 @@
enum chips { max34440, max34441 };
+#define MAX34440_MFR_VOUT_PEAK 0xd4
+#define MAX34440_MFR_IOUT_PEAK 0xd5
+#define MAX34440_MFR_TEMPERATURE_PEAK 0xd6
+
#define MAX34440_STATUS_OC_WARN (1 << 0)
#define MAX34440_STATUS_OC_FAULT (1 << 1)
#define MAX34440_STATUS_OT_FAULT (1 << 5)
#define MAX34440_STATUS_OT_WARN (1 << 6)
-static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
+static int max34440_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ ret = pmbus_read_word_data(client, page,
+ MAX34440_MFR_VOUT_PEAK);
+ break;
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ ret = pmbus_read_word_data(client, page,
+ MAX34440_MFR_IOUT_PEAK);
+ break;
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ ret = pmbus_read_word_data(client, page,
+ MAX34440_MFR_TEMPERATURE_PEAK);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int max34440_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
{
int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ MAX34440_MFR_VOUT_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ MAX34440_MFR_IOUT_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ MAX34440_MFR_TEMPERATURE_PEAK,
+ 0xffff);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret = 0;
int mfg_status;
- ret = pmbus_set_page(client, page);
- if (ret < 0)
- return ret;
+ if (page >= 0) {
+ ret = pmbus_set_page(client, page);
+ if (ret < 0)
+ return ret;
+ }
switch (reg) {
case PMBUS_STATUS_IOUT:
@@ -72,10 +133,10 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
static struct pmbus_driver_info max34440_info[] = {
[max34440] = {
.pages = 14,
- .direct[PSC_VOLTAGE_IN] = true,
- .direct[PSC_VOLTAGE_OUT] = true,
- .direct[PSC_TEMPERATURE] = true,
- .direct[PSC_CURRENT_OUT] = true,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
.m[PSC_VOLTAGE_IN] = 1,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 3, /* R = 0 in datasheet reflects mV */
@@ -109,14 +170,16 @@ static struct pmbus_driver_info max34440_info[] = {
.func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.read_byte_data = max34440_read_byte_data,
+ .read_word_data = max34440_read_word_data,
+ .write_word_data = max34440_write_word_data,
},
[max34441] = {
.pages = 12,
- .direct[PSC_VOLTAGE_IN] = true,
- .direct[PSC_VOLTAGE_OUT] = true,
- .direct[PSC_TEMPERATURE] = true,
- .direct[PSC_CURRENT_OUT] = true,
- .direct[PSC_FAN] = true,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_FAN] = direct,
.m[PSC_VOLTAGE_IN] = 1,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 3,
@@ -150,6 +213,8 @@ static struct pmbus_driver_info max34440_info[] = {
.func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.read_byte_data = max34440_read_byte_data,
+ .read_word_data = max34440_read_word_data,
+ .write_word_data = max34440_write_word_data,
},
};
@@ -161,7 +226,8 @@ static int max34440_probe(struct i2c_client *client,
static int max34440_remove(struct i2c_client *client)
{
- return pmbus_do_remove(client);
+ pmbus_do_remove(client);
+ return 0;
}
static const struct i2c_device_id max34440_id[] = {
diff --git a/drivers/hwmon/max8688.c b/drivers/hwmon/pmbus/max8688.c
index 7fb93f4e9f21..e2b74bb399ba 100644
--- a/drivers/hwmon/max8688.c
+++ b/drivers/hwmon/pmbus/max8688.c
@@ -25,6 +25,9 @@
#include <linux/i2c.h>
#include "pmbus.h"
+#define MAX8688_MFR_VOUT_PEAK 0xd4
+#define MAX8688_MFR_IOUT_PEAK 0xd5
+#define MAX8688_MFR_TEMPERATURE_PEAK 0xd6
#define MAX8688_MFG_STATUS 0xd8
#define MAX8688_STATUS_OC_FAULT (1 << 4)
@@ -37,13 +40,69 @@
#define MAX8688_STATUS_OT_FAULT (1 << 13)
#define MAX8688_STATUS_OT_WARNING (1 << 14)
+static int max8688_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ if (page)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ ret = pmbus_read_word_data(client, 0, MAX8688_MFR_VOUT_PEAK);
+ break;
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ ret = pmbus_read_word_data(client, 0, MAX8688_MFR_IOUT_PEAK);
+ break;
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ ret = pmbus_read_word_data(client, 0,
+ MAX8688_MFR_TEMPERATURE_PEAK);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int max8688_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK,
+ 0);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK,
+ 0);
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = pmbus_write_word_data(client, 0,
+ MAX8688_MFR_TEMPERATURE_PEAK,
+ 0xffff);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)
{
int ret = 0;
int mfg_status;
- if (page)
- return -EINVAL;
+ if (page > 0)
+ return -ENXIO;
switch (reg) {
case PMBUS_STATUS_VOUT:
@@ -91,10 +150,10 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)
static struct pmbus_driver_info max8688_info = {
.pages = 1,
- .direct[PSC_VOLTAGE_IN] = true,
- .direct[PSC_VOLTAGE_OUT] = true,
- .direct[PSC_TEMPERATURE] = true,
- .direct[PSC_CURRENT_OUT] = true,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
.m[PSC_VOLTAGE_IN] = 19995,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = -1,
@@ -111,6 +170,8 @@ static struct pmbus_driver_info max8688_info = {
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_STATUS_TEMP,
.read_byte_data = max8688_read_byte_data,
+ .read_word_data = max8688_read_word_data,
+ .write_word_data = max8688_write_word_data,
};
static int max8688_probe(struct i2c_client *client,
@@ -121,7 +182,8 @@ static int max8688_probe(struct i2c_client *client,
static int max8688_remove(struct i2c_client *client)
{
- return pmbus_do_remove(client);
+ pmbus_do_remove(client);
+ return 0;
}
static const struct i2c_device_id max8688_id[] = {
diff --git a/drivers/hwmon/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
index 9b1f0c37ef77..995e873197e3 100644
--- a/drivers/hwmon/pmbus.c
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -96,6 +96,8 @@ static void pmbus_find_sensor_groups(struct i2c_client *client,
static int pmbus_identify(struct i2c_client *client,
struct pmbus_driver_info *info)
{
+ int ret = 0;
+
if (!info->pages) {
/*
* Check if the PAGE command is supported. If it is,
@@ -117,6 +119,27 @@ static int pmbus_identify(struct i2c_client *client,
}
}
+ if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) {
+ int vout_mode;
+
+ vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
+ if (vout_mode >= 0 && vout_mode != 0xff) {
+ switch (vout_mode >> 5) {
+ case 0:
+ break;
+ case 1:
+ info->format[PSC_VOLTAGE_OUT] = vid;
+ break;
+ case 2:
+ info->format[PSC_VOLTAGE_OUT] = direct;
+ break;
+ default:
+ ret = -ENODEV;
+ goto abort;
+ }
+ }
+ }
+
/*
* We should check if the COEFFICIENTS register is supported.
* If it is, and the chip is configured for direct mode, we can read
@@ -125,13 +148,18 @@ static int pmbus_identify(struct i2c_client *client,
*
* To do this, we will need access to a chip which actually supports the
* COEFFICIENTS command, since the command is too complex to implement
- * without testing it.
+ * without testing it. Until then, abort if a chip configured for direct
+ * mode was detected.
*/
+ if (info->format[PSC_VOLTAGE_OUT] == direct) {
+ ret = -ENODEV;
+ goto abort;
+ }
/* Try to find sensor groups */
pmbus_find_sensor_groups(client, info);
-
- return 0;
+abort:
+ return ret;
}
static int pmbus_probe(struct i2c_client *client,
@@ -159,25 +187,30 @@ out:
static int pmbus_remove(struct i2c_client *client)
{
- int ret;
const struct pmbus_driver_info *info;
info = pmbus_get_driver_info(client);
- ret = pmbus_do_remove(client);
+ pmbus_do_remove(client);
kfree(info);
- return ret;
+ return 0;
}
/*
* Use driver_data to set the number of pages supported by the chip.
*/
static const struct i2c_device_id pmbus_id[] = {
+ {"adp4000", 1},
{"bmr450", 1},
{"bmr451", 1},
{"bmr453", 1},
{"bmr454", 1},
- {"ltc2978", 8},
+ {"ncp4200", 1},
+ {"ncp4208", 1},
+ {"pdt003", 1},
+ {"pdt006", 1},
+ {"pdt012", 1},
{"pmbus", 0},
+ {"udt020", 1},
{}
};
diff --git a/drivers/hwmon/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 50647ab7235a..5d31d1c2c0f5 100644
--- a/drivers/hwmon/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -126,6 +126,53 @@
#define PMBUS_MFR_SERIAL 0x9E
/*
+ * Virtual registers.
+ * Useful to support attributes which are not supported by standard PMBus
+ * registers but exist as manufacturer specific registers on individual chips.
+ * Must be mapped to real registers in device specific code.
+ *
+ * Semantics:
+ * Virtual registers are all word size.
+ * READ registers are read-only; writes are either ignored or return an error.
+ * RESET registers are read/write. Reading reset registers returns zero
+ * (used for detection), writing any value causes the associated history to be
+ * reset.
+ * Virtual registers have to be handled in device specific driver code. Chip
+ * driver code returns non-negative register values if a virtual register is
+ * supported, or a negative error code if not. The chip driver may return
+ * -ENODATA or any other error code in this case, though an error code other
+ * than -ENODATA is handled more efficiently and thus preferred. Either case,
+ * the calling PMBus core code will abort if the chip driver returns an error
+ * code when reading or writing virtual registers.
+ */
+#define PMBUS_VIRT_BASE 0x100
+#define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 0)
+#define PMBUS_VIRT_READ_TEMP_MAX (PMBUS_VIRT_BASE + 1)
+#define PMBUS_VIRT_RESET_TEMP_HISTORY (PMBUS_VIRT_BASE + 2)
+#define PMBUS_VIRT_READ_VIN_AVG (PMBUS_VIRT_BASE + 3)
+#define PMBUS_VIRT_READ_VIN_MIN (PMBUS_VIRT_BASE + 4)
+#define PMBUS_VIRT_READ_VIN_MAX (PMBUS_VIRT_BASE + 5)
+#define PMBUS_VIRT_RESET_VIN_HISTORY (PMBUS_VIRT_BASE + 6)
+#define PMBUS_VIRT_READ_IIN_AVG (PMBUS_VIRT_BASE + 7)
+#define PMBUS_VIRT_READ_IIN_MIN (PMBUS_VIRT_BASE + 8)
+#define PMBUS_VIRT_READ_IIN_MAX (PMBUS_VIRT_BASE + 9)
+#define PMBUS_VIRT_RESET_IIN_HISTORY (PMBUS_VIRT_BASE + 10)
+#define PMBUS_VIRT_READ_PIN_AVG (PMBUS_VIRT_BASE + 11)
+#define PMBUS_VIRT_READ_PIN_MAX (PMBUS_VIRT_BASE + 12)
+#define PMBUS_VIRT_RESET_PIN_HISTORY (PMBUS_VIRT_BASE + 13)
+#define PMBUS_VIRT_READ_VOUT_AVG (PMBUS_VIRT_BASE + 14)
+#define PMBUS_VIRT_READ_VOUT_MIN (PMBUS_VIRT_BASE + 15)
+#define PMBUS_VIRT_READ_VOUT_MAX (PMBUS_VIRT_BASE + 16)
+#define PMBUS_VIRT_RESET_VOUT_HISTORY (PMBUS_VIRT_BASE + 17)
+#define PMBUS_VIRT_READ_IOUT_AVG (PMBUS_VIRT_BASE + 18)
+#define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 19)
+#define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 20)
+#define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 21)
+#define PMBUS_VIRT_READ_TEMP2_MIN (PMBUS_VIRT_BASE + 22)
+#define PMBUS_VIRT_READ_TEMP2_MAX (PMBUS_VIRT_BASE + 23)
+#define PMBUS_VIRT_RESET_TEMP2_HISTORY (PMBUS_VIRT_BASE + 24)
+
+/*
* CAPABILITY
*/
#define PB_CAPABILITY_SMBALERT (1<<4)
@@ -266,11 +313,11 @@ enum pmbus_sensor_classes {
#define PMBUS_HAVE_STATUS_FAN12 (1 << 16)
#define PMBUS_HAVE_STATUS_FAN34 (1 << 17)
+enum pmbus_data_format { linear = 0, direct, vid };
+
struct pmbus_driver_info {
int pages; /* Total number of pages */
- bool direct[PSC_NUM_CLASSES];
- /* true if device uses direct data format
- for the given sensor class */
+ enum pmbus_data_format format[PSC_NUM_CLASSES];
/*
* Support one set of coefficients for each sensor type
* Used for chips providing data in direct mode.
@@ -284,8 +331,18 @@ struct pmbus_driver_info {
* The following functions map manufacturing specific register values
* to PMBus standard register values. Specify only if mapping is
* necessary.
+ * Functions return the register value (read) or zero (write) if
+ * successful. A return value of -ENODATA indicates that there is no
+ * manufacturer specific register, but that a standard PMBus register
+ * may exist. Any other negative return value indicates that the
+ * register does not exist, and that no attempt should be made to read
+ * the standard register.
*/
int (*read_byte_data)(struct i2c_client *client, int page, int reg);
+ int (*read_word_data)(struct i2c_client *client, int page, int reg);
+ int (*write_word_data)(struct i2c_client *client, int page, int reg,
+ u16 word);
+ int (*write_byte)(struct i2c_client *client, int page, u8 value);
/*
* The identify function determines supported PMBus functionality.
* This function is only necessary if a chip driver supports multiple
@@ -299,12 +356,15 @@ struct pmbus_driver_info {
int pmbus_set_page(struct i2c_client *client, u8 page);
int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
+int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word);
+int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
+int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
void pmbus_clear_faults(struct i2c_client *client);
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
struct pmbus_driver_info *info);
-int pmbus_do_remove(struct i2c_client *client);
+void pmbus_do_remove(struct i2c_client *client);
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client
*client);
diff --git a/drivers/hwmon/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 744672c1f26d..00460d8d8423 100644
--- a/drivers/hwmon/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -33,14 +33,18 @@
/*
* Constants needed to determine number of sensors, booleans, and labels.
*/
-#define PMBUS_MAX_INPUT_SENSORS 11 /* 6*volt, 3*curr, 2*power */
-#define PMBUS_VOUT_SENSORS_PER_PAGE 5 /* input, min, max, lcrit,
- crit */
-#define PMBUS_IOUT_SENSORS_PER_PAGE 4 /* input, min, max, crit */
+#define PMBUS_MAX_INPUT_SENSORS 22 /* 10*volt, 7*curr, 5*power */
+#define PMBUS_VOUT_SENSORS_PER_PAGE 9 /* input, min, max, lcrit,
+ crit, lowest, highest, avg,
+ reset */
+#define PMBUS_IOUT_SENSORS_PER_PAGE 8 /* input, min, max, crit,
+ lowest, highest, avg,
+ reset */
#define PMBUS_POUT_SENSORS_PER_PAGE 4 /* input, cap, max, crit */
#define PMBUS_MAX_SENSORS_PER_FAN 1 /* input */
-#define PMBUS_MAX_SENSORS_PER_TEMP 5 /* input, min, max, lcrit,
- crit */
+#define PMBUS_MAX_SENSORS_PER_TEMP 8 /* input, min, max, lcrit,
+ crit, lowest, highest,
+ reset */
#define PMBUS_MAX_INPUT_BOOLEANS 7 /* v: min_alarm, max_alarm,
lcrit_alarm, crit_alarm;
@@ -74,11 +78,13 @@
#define PB_STATUS_INPUT_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES)
#define PB_STATUS_TEMP_BASE (PB_STATUS_INPUT_BASE + 1)
+#define PMBUS_NAME_SIZE 24
+
struct pmbus_sensor {
- char name[I2C_NAME_SIZE]; /* sysfs sensor name */
+ char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */
struct sensor_device_attribute attribute;
u8 page; /* page number */
- u8 reg; /* register */
+ u16 reg; /* register */
enum pmbus_sensor_classes class; /* sensor class */
bool update; /* runtime sensor update needed */
int data; /* Sensor data.
@@ -86,14 +92,14 @@ struct pmbus_sensor {
};
struct pmbus_boolean {
- char name[I2C_NAME_SIZE]; /* sysfs boolean name */
+ char name[PMBUS_NAME_SIZE]; /* sysfs boolean name */
struct sensor_device_attribute attribute;
};
struct pmbus_label {
- char name[I2C_NAME_SIZE]; /* sysfs label name */
+ char name[PMBUS_NAME_SIZE]; /* sysfs label name */
struct sensor_device_attribute attribute;
- char label[I2C_NAME_SIZE]; /* label */
+ char label[PMBUS_NAME_SIZE]; /* label */
};
struct pmbus_data {
@@ -154,7 +160,7 @@ int pmbus_set_page(struct i2c_client *client, u8 page)
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
if (newpage != page)
- rv = -EINVAL;
+ rv = -EIO;
else
data->currpage = page;
}
@@ -162,19 +168,39 @@ int pmbus_set_page(struct i2c_client *client, u8 page)
}
EXPORT_SYMBOL_GPL(pmbus_set_page);
-static int pmbus_write_byte(struct i2c_client *client, u8 page, u8 value)
+int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
{
int rv;
- rv = pmbus_set_page(client, page);
- if (rv < 0)
- return rv;
+ if (page >= 0) {
+ rv = pmbus_set_page(client, page);
+ if (rv < 0)
+ return rv;
+ }
return i2c_smbus_write_byte(client, value);
}
+EXPORT_SYMBOL_GPL(pmbus_write_byte);
-static int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg,
- u16 word)
+/*
+ * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if
+ * a device specific mapping funcion exists and calls it if necessary.
+ */
+static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->write_byte) {
+ status = info->write_byte(client, page, value);
+ if (status != -ENODATA)
+ return status;
+ }
+ return pmbus_write_byte(client, page, value);
+}
+
+int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word)
{
int rv;
@@ -184,6 +210,28 @@ static int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg,
return i2c_smbus_write_word_data(client, reg, word);
}
+EXPORT_SYMBOL_GPL(pmbus_write_word_data);
+
+/*
+ * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->write_word_data) {
+ status = info->write_word_data(client, page, reg, word);
+ if (status != -ENODATA)
+ return status;
+ }
+ if (reg >= PMBUS_VIRT_BASE)
+ return -ENXIO;
+ return pmbus_write_word_data(client, page, reg, word);
+}
int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)
{
@@ -197,20 +245,61 @@ int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)
}
EXPORT_SYMBOL_GPL(pmbus_read_word_data);
-static int pmbus_read_byte_data(struct i2c_client *client, u8 page, u8 reg)
+/*
+ * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->read_word_data) {
+ status = info->read_word_data(client, page, reg);
+ if (status != -ENODATA)
+ return status;
+ }
+ if (reg >= PMBUS_VIRT_BASE)
+ return -ENXIO;
+ return pmbus_read_word_data(client, page, reg);
+}
+
+int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
{
int rv;
- rv = pmbus_set_page(client, page);
- if (rv < 0)
- return rv;
+ if (page >= 0) {
+ rv = pmbus_set_page(client, page);
+ if (rv < 0)
+ return rv;
+ }
return i2c_smbus_read_byte_data(client, reg);
}
+EXPORT_SYMBOL_GPL(pmbus_read_byte_data);
+
+/*
+ * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->read_byte_data) {
+ status = info->read_byte_data(client, page, reg);
+ if (status != -ENODATA)
+ return status;
+ }
+ return pmbus_read_byte_data(client, page, reg);
+}
static void pmbus_clear_fault_page(struct i2c_client *client, int page)
{
- pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
+ _pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
}
void pmbus_clear_faults(struct i2c_client *client)
@@ -223,15 +312,15 @@ void pmbus_clear_faults(struct i2c_client *client)
}
EXPORT_SYMBOL_GPL(pmbus_clear_faults);
-static int pmbus_check_status_cml(struct i2c_client *client, int page)
+static int pmbus_check_status_cml(struct i2c_client *client)
{
int status, status2;
- status = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE);
+ status = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_BYTE);
if (status < 0 || (status & PB_STATUS_CML)) {
- status2 = pmbus_read_byte_data(client, page, PMBUS_STATUS_CML);
+ status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND))
- return -EINVAL;
+ return -EIO;
}
return 0;
}
@@ -241,10 +330,10 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg)
int rv;
struct pmbus_data *data = i2c_get_clientdata(client);
- rv = pmbus_read_byte_data(client, page, reg);
+ rv = _pmbus_read_byte_data(client, page, reg);
if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
- rv = pmbus_check_status_cml(client, page);
- pmbus_clear_fault_page(client, page);
+ rv = pmbus_check_status_cml(client);
+ pmbus_clear_fault_page(client, -1);
return rv >= 0;
}
EXPORT_SYMBOL_GPL(pmbus_check_byte_register);
@@ -254,10 +343,10 @@ bool pmbus_check_word_register(struct i2c_client *client, int page, int reg)
int rv;
struct pmbus_data *data = i2c_get_clientdata(client);
- rv = pmbus_read_word_data(client, page, reg);
+ rv = _pmbus_read_word_data(client, page, reg);
if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
- rv = pmbus_check_status_cml(client, page);
- pmbus_clear_fault_page(client, page);
+ rv = pmbus_check_status_cml(client);
+ pmbus_clear_fault_page(client, -1);
return rv >= 0;
}
EXPORT_SYMBOL_GPL(pmbus_check_word_register);
@@ -270,24 +359,6 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
}
EXPORT_SYMBOL_GPL(pmbus_get_driver_info);
-/*
- * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
- * a device specific mapping funcion exists and calls it if necessary.
- */
-static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
-{
- struct pmbus_data *data = i2c_get_clientdata(client);
- const struct pmbus_driver_info *info = data->info;
- int status;
-
- if (info->read_byte_data) {
- status = info->read_byte_data(client, page, reg);
- if (status != -ENODATA)
- return status;
- }
- return pmbus_read_byte_data(client, page, reg);
-}
-
static struct pmbus_data *pmbus_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -300,8 +371,8 @@ static struct pmbus_data *pmbus_update_device(struct device *dev)
for (i = 0; i < info->pages; i++)
data->status[PB_STATUS_BASE + i]
- = pmbus_read_byte_data(client, i,
- PMBUS_STATUS_BYTE);
+ = _pmbus_read_byte_data(client, i,
+ PMBUS_STATUS_BYTE);
for (i = 0; i < info->pages; i++) {
if (!(info->func[i] & PMBUS_HAVE_STATUS_VOUT))
continue;
@@ -347,8 +418,9 @@ static struct pmbus_data *pmbus_update_device(struct device *dev)
if (!data->valid || sensor->update)
sensor->data
- = pmbus_read_word_data(client, sensor->page,
- sensor->reg);
+ = _pmbus_read_word_data(client,
+ sensor->page,
+ sensor->reg);
}
pmbus_clear_faults(client);
data->last_updated = jiffies;
@@ -362,8 +434,8 @@ static struct pmbus_data *pmbus_update_device(struct device *dev)
* Convert linear sensor values to milli- or micro-units
* depending on sensor type.
*/
-static int pmbus_reg2data_linear(struct pmbus_data *data,
- struct pmbus_sensor *sensor)
+static long pmbus_reg2data_linear(struct pmbus_data *data,
+ struct pmbus_sensor *sensor)
{
s16 exponent;
s32 mantissa;
@@ -373,13 +445,8 @@ static int pmbus_reg2data_linear(struct pmbus_data *data,
exponent = data->exponent;
mantissa = (u16) sensor->data;
} else { /* LINEAR11 */
- exponent = (sensor->data >> 11) & 0x001f;
- mantissa = sensor->data & 0x07ff;
-
- if (exponent > 0x0f)
- exponent |= 0xffe0; /* sign extend exponent */
- if (mantissa > 0x03ff)
- mantissa |= 0xfffff800; /* sign extend mantissa */
+ exponent = ((s16)sensor->data) >> 11;
+ mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5;
}
val = mantissa;
@@ -397,15 +464,15 @@ static int pmbus_reg2data_linear(struct pmbus_data *data,
else
val >>= -exponent;
- return (int)val;
+ return val;
}
/*
* Convert direct sensor values to milli- or micro-units
* depending on sensor type.
*/
-static int pmbus_reg2data_direct(struct pmbus_data *data,
- struct pmbus_sensor *sensor)
+static long pmbus_reg2data_direct(struct pmbus_data *data,
+ struct pmbus_sensor *sensor)
{
long val = (s16) sensor->data;
long m, b, R;
@@ -440,18 +507,40 @@ static int pmbus_reg2data_direct(struct pmbus_data *data,
R++;
}
- return (int)((val - b) / m);
+ return (val - b) / m;
}
-static int pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
+/*
+ * Convert VID sensor values to milli- or micro-units
+ * depending on sensor type.
+ * We currently only support VR11.
+ */
+static long pmbus_reg2data_vid(struct pmbus_data *data,
+ struct pmbus_sensor *sensor)
{
- int val;
+ long val = sensor->data;
- if (data->info->direct[sensor->class])
+ if (val < 0x02 || val > 0xb2)
+ return 0;
+ return DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100);
+}
+
+static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
+{
+ long val;
+
+ switch (data->info->format[sensor->class]) {
+ case direct:
val = pmbus_reg2data_direct(data, sensor);
- else
+ break;
+ case vid:
+ val = pmbus_reg2data_vid(data, sensor);
+ break;
+ case linear:
+ default:
val = pmbus_reg2data_linear(data, sensor);
-
+ break;
+ }
return val;
}
@@ -561,16 +650,31 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data,
return val;
}
+static u16 pmbus_data2reg_vid(struct pmbus_data *data,
+ enum pmbus_sensor_classes class, long val)
+{
+ val = SENSORS_LIMIT(val, 500, 1600);
+
+ return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625);
+}
+
static u16 pmbus_data2reg(struct pmbus_data *data,
enum pmbus_sensor_classes class, long val)
{
u16 regval;
- if (data->info->direct[class])
+ switch (data->info->format[class]) {
+ case direct:
regval = pmbus_data2reg_direct(data, class, val);
- else
+ break;
+ case vid:
+ regval = pmbus_data2reg_vid(data, class, val);
+ break;
+ case linear:
+ default:
regval = pmbus_data2reg_linear(data, class, val);
-
+ break;
+ }
return regval;
}
@@ -619,7 +723,7 @@ static int pmbus_get_boolean(struct pmbus_data *data, int index, int *val)
if (!s1 && !s2)
*val = !!regval;
else {
- int v1, v2;
+ long v1, v2;
struct pmbus_sensor *sensor1, *sensor2;
sensor1 = &data->sensors[s1];
@@ -661,7 +765,7 @@ static ssize_t pmbus_show_sensor(struct device *dev,
if (sensor->data < 0)
return sensor->data;
- return snprintf(buf, PAGE_SIZE, "%d\n", pmbus_reg2data(data, sensor));
+ return snprintf(buf, PAGE_SIZE, "%ld\n", pmbus_reg2data(data, sensor));
}
static ssize_t pmbus_set_sensor(struct device *dev,
@@ -682,7 +786,7 @@ static ssize_t pmbus_set_sensor(struct device *dev,
mutex_lock(&data->update_lock);
regval = pmbus_data2reg(data, sensor->class, val);
- ret = pmbus_write_word_data(client, sensor->page, sensor->reg, regval);
+ ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval);
if (ret < 0)
rv = ret;
else
@@ -867,7 +971,10 @@ static void pmbus_find_max_attr(struct i2c_client *client,
* and its associated alarm attribute.
*/
struct pmbus_limit_attr {
- u8 reg; /* Limit register */
+ u16 reg; /* Limit register */
+ bool update; /* True if register needs updates */
+ bool low; /* True if low limit; for limits with compare
+ functions only */
const char *attr; /* Attribute name */
const char *alarm; /* Alarm attribute name */
u32 sbit; /* Alarm attribute status bit */
@@ -912,13 +1019,15 @@ static bool pmbus_add_limit_attrs(struct i2c_client *client,
if (pmbus_check_word_register(client, page, l->reg)) {
cindex = data->num_sensors;
pmbus_add_sensor(data, name, l->attr, index, page,
- l->reg, attr->class, attr->update,
+ l->reg, attr->class,
+ attr->update || l->update,
false);
- if (info->func[page] & attr->sfunc) {
+ if (l->sbit && (info->func[page] & attr->sfunc)) {
if (attr->compare) {
pmbus_add_boolean_cmp(data, name,
l->alarm, index,
- cbase, cindex,
+ l->low ? cindex : cbase,
+ l->low ? cbase : cindex,
attr->sbase + page, l->sbit);
} else {
pmbus_add_boolean_reg(data, name,
@@ -953,9 +1062,11 @@ static void pmbus_add_sensor_attrs_one(struct i2c_client *client,
index, page, cbase, attr);
/*
* Add generic alarm attribute only if there are no individual
- * alarm attributes, and if there is a global alarm bit.
+ * alarm attributes, if there is a global alarm bit, and if
+ * the generic status register for this page is accessible.
*/
- if (!have_alarm && attr->gbit)
+ if (!have_alarm && attr->gbit &&
+ pmbus_check_byte_register(client, page, PMBUS_STATUS_BYTE))
pmbus_add_boolean_reg(data, name, "alarm", index,
PB_STATUS_BASE + page,
attr->gbit);
@@ -1008,6 +1119,21 @@ static const struct pmbus_limit_attr vin_limit_attrs[] = {
.attr = "crit",
.alarm = "crit_alarm",
.sbit = PB_VOLTAGE_OV_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_VIN_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_VIN_MIN,
+ .update = true,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_VIN_MAX,
+ .update = true,
+ .attr = "highest",
+ }, {
+ .reg = PMBUS_VIRT_RESET_VIN_HISTORY,
+ .attr = "reset_history",
},
};
@@ -1032,6 +1158,21 @@ static const struct pmbus_limit_attr vout_limit_attrs[] = {
.attr = "crit",
.alarm = "crit_alarm",
.sbit = PB_VOLTAGE_OV_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_VOUT_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_VOUT_MIN,
+ .update = true,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_VOUT_MAX,
+ .update = true,
+ .attr = "highest",
+ }, {
+ .reg = PMBUS_VIRT_RESET_VOUT_HISTORY,
+ .attr = "reset_history",
}
};
@@ -1078,6 +1219,21 @@ static const struct pmbus_limit_attr iin_limit_attrs[] = {
.attr = "crit",
.alarm = "crit_alarm",
.sbit = PB_IIN_OC_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_IIN_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_IIN_MIN,
+ .update = true,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_IIN_MAX,
+ .update = true,
+ .attr = "highest",
+ }, {
+ .reg = PMBUS_VIRT_RESET_IIN_HISTORY,
+ .attr = "reset_history",
}
};
@@ -1097,6 +1253,21 @@ static const struct pmbus_limit_attr iout_limit_attrs[] = {
.attr = "crit",
.alarm = "crit_alarm",
.sbit = PB_IOUT_OC_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_IOUT_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_IOUT_MIN,
+ .update = true,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_IOUT_MAX,
+ .update = true,
+ .attr = "highest",
+ }, {
+ .reg = PMBUS_VIRT_RESET_IOUT_HISTORY,
+ .attr = "reset_history",
}
};
@@ -1132,6 +1303,17 @@ static const struct pmbus_limit_attr pin_limit_attrs[] = {
.attr = "max",
.alarm = "alarm",
.sbit = PB_PIN_OP_WARNING,
+ }, {
+ .reg = PMBUS_VIRT_READ_PIN_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_PIN_MAX,
+ .update = true,
+ .attr = "input_highest",
+ }, {
+ .reg = PMBUS_VIRT_RESET_PIN_HISTORY,
+ .attr = "reset_history",
}
};
@@ -1182,11 +1364,83 @@ static const struct pmbus_sensor_attr power_attributes[] = {
static const struct pmbus_limit_attr temp_limit_attrs[] = {
{
.reg = PMBUS_UT_WARN_LIMIT,
+ .low = true,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_TEMP_UT_WARNING,
+ }, {
+ .reg = PMBUS_UT_FAULT_LIMIT,
+ .low = true,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_TEMP_UT_FAULT,
+ }, {
+ .reg = PMBUS_OT_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_TEMP_OT_WARNING,
+ }, {
+ .reg = PMBUS_OT_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_TEMP_OT_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_TEMP_MIN,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_TEMP_MAX,
+ .attr = "highest",
+ }, {
+ .reg = PMBUS_VIRT_RESET_TEMP_HISTORY,
+ .attr = "reset_history",
+ }
+};
+
+static const struct pmbus_limit_attr temp_limit_attrs2[] = {
+ {
+ .reg = PMBUS_UT_WARN_LIMIT,
+ .low = true,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_TEMP_UT_WARNING,
+ }, {
+ .reg = PMBUS_UT_FAULT_LIMIT,
+ .low = true,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_TEMP_UT_FAULT,
+ }, {
+ .reg = PMBUS_OT_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_TEMP_OT_WARNING,
+ }, {
+ .reg = PMBUS_OT_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_TEMP_OT_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_TEMP2_MIN,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_TEMP2_MAX,
+ .attr = "highest",
+ }, {
+ .reg = PMBUS_VIRT_RESET_TEMP2_HISTORY,
+ .attr = "reset_history",
+ }
+};
+
+static const struct pmbus_limit_attr temp_limit_attrs3[] = {
+ {
+ .reg = PMBUS_UT_WARN_LIMIT,
+ .low = true,
.attr = "min",
.alarm = "min_alarm",
.sbit = PB_TEMP_UT_WARNING,
}, {
.reg = PMBUS_UT_FAULT_LIMIT,
+ .low = true,
.attr = "lcrit",
.alarm = "lcrit_alarm",
.sbit = PB_TEMP_UT_FAULT,
@@ -1226,8 +1480,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = {
.sfunc = PMBUS_HAVE_STATUS_TEMP,
.sbase = PB_STATUS_TEMP_BASE,
.gbit = PB_STATUS_TEMPERATURE,
- .limit = temp_limit_attrs,
- .nlimit = ARRAY_SIZE(temp_limit_attrs),
+ .limit = temp_limit_attrs2,
+ .nlimit = ARRAY_SIZE(temp_limit_attrs2),
}, {
.reg = PMBUS_READ_TEMPERATURE_3,
.class = PSC_TEMPERATURE,
@@ -1238,8 +1492,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = {
.sfunc = PMBUS_HAVE_STATUS_TEMP,
.sbase = PB_STATUS_TEMP_BASE,
.gbit = PB_STATUS_TEMPERATURE,
- .limit = temp_limit_attrs,
- .nlimit = ARRAY_SIZE(temp_limit_attrs),
+ .limit = temp_limit_attrs3,
+ .nlimit = ARRAY_SIZE(temp_limit_attrs3),
}
};
@@ -1369,10 +1623,10 @@ static void pmbus_find_attributes(struct i2c_client *client,
static int pmbus_identify_common(struct i2c_client *client,
struct pmbus_data *data)
{
- int vout_mode = -1, exponent;
+ int vout_mode = -1;
if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE))
- vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
+ vout_mode = _pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
if (vout_mode >= 0 && vout_mode != 0xff) {
/*
* Not all chips support the VOUT_MODE command,
@@ -1380,17 +1634,17 @@ static int pmbus_identify_common(struct i2c_client *client,
*/
switch (vout_mode >> 5) {
case 0: /* linear mode */
- if (data->info->direct[PSC_VOLTAGE_OUT])
+ if (data->info->format[PSC_VOLTAGE_OUT] != linear)
return -ENODEV;
- exponent = vout_mode & 0x1f;
- /* and sign-extend it */
- if (exponent & 0x10)
- exponent |= ~0x1f;
- data->exponent = exponent;
+ data->exponent = ((s8)(vout_mode << 3)) >> 3;
+ break;
+ case 1: /* VID mode */
+ if (data->info->format[PSC_VOLTAGE_OUT] != vid)
+ return -ENODEV;
break;
case 2: /* direct mode */
- if (!data->info->direct[PSC_VOLTAGE_OUT])
+ if (data->info->format[PSC_VOLTAGE_OUT] != direct)
return -ENODEV;
break;
default:
@@ -1454,19 +1708,7 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
if (info->pages <= 0 || info->pages > PMBUS_PAGES) {
dev_err(&client->dev, "Bad number of PMBus pages: %d\n",
info->pages);
- ret = -EINVAL;
- goto out_data;
- }
- /*
- * Bail out if more than one page was configured, but we can not
- * select the highest page. This is an indication that the wrong
- * chip type was selected. Better bail out now than keep
- * returning errors later on.
- */
- if (info->pages > 1 && pmbus_set_page(client, info->pages - 1) < 0) {
- dev_err(&client->dev, "Failed to select page %d\n",
- info->pages - 1);
- ret = -EINVAL;
+ ret = -ENODEV;
goto out_data;
}
@@ -1548,7 +1790,7 @@ out_data:
}
EXPORT_SYMBOL_GPL(pmbus_do_probe);
-int pmbus_do_remove(struct i2c_client *client)
+void pmbus_do_remove(struct i2c_client *client)
{
struct pmbus_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
@@ -1558,7 +1800,6 @@ int pmbus_do_remove(struct i2c_client *client)
kfree(data->booleans);
kfree(data->sensors);
kfree(data);
- return 0;
}
EXPORT_SYMBOL_GPL(pmbus_do_remove);
diff --git a/drivers/hwmon/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index ace1c7319734..4ff6cf289f85 100644
--- a/drivers/hwmon/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -74,8 +74,8 @@ static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg)
switch (reg) {
case PMBUS_FAN_CONFIG_12:
- if (page)
- return -EINVAL;
+ if (page > 0)
+ return -ENXIO;
ret = ucd9000_get_fan_config(client, 0);
if (ret < 0)
@@ -88,8 +88,8 @@ static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg)
ret = fan_config;
break;
case PMBUS_FAN_CONFIG_34:
- if (page)
- return -EINVAL;
+ if (page > 0)
+ return -ENXIO;
ret = ucd9000_get_fan_config(client, 2);
if (ret < 0)
@@ -141,13 +141,11 @@ static int ucd9000_probe(struct i2c_client *client,
block_buffer[ret] = '\0';
dev_info(&client->dev, "Device ID %s\n", block_buffer);
- mid = NULL;
- for (i = 0; i < ARRAY_SIZE(ucd9000_id); i++) {
- mid = &ucd9000_id[i];
+ for (mid = ucd9000_id; mid->name[0]; mid++) {
if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
break;
}
- if (!mid || !strlen(mid->name)) {
+ if (!mid->name[0]) {
dev_err(&client->dev, "Unsupported device\n");
return -ENODEV;
}
@@ -241,13 +239,12 @@ out:
static int ucd9000_remove(struct i2c_client *client)
{
- int ret;
struct ucd9000_data *data;
data = to_ucd9000_data(pmbus_get_driver_info(client));
- ret = pmbus_do_remove(client);
+ pmbus_do_remove(client);
kfree(data);
- return ret;
+ return 0;
}
diff --git a/drivers/hwmon/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c
index ffcc1cf3609d..6e1c1a80ab85 100644
--- a/drivers/hwmon/ucd9200.c
+++ b/drivers/hwmon/pmbus/ucd9200.c
@@ -68,13 +68,11 @@ static int ucd9200_probe(struct i2c_client *client,
block_buffer[ret] = '\0';
dev_info(&client->dev, "Device ID %s\n", block_buffer);
- mid = NULL;
- for (i = 0; i < ARRAY_SIZE(ucd9200_id); i++) {
- mid = &ucd9200_id[i];
+ for (mid = ucd9200_id; mid->name[0]; mid++) {
if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
break;
}
- if (!mid || !strlen(mid->name)) {
+ if (!mid->name[0]) {
dev_err(&client->dev, "Unsupported device\n");
return -ENODEV;
}
@@ -173,13 +171,12 @@ out:
static int ucd9200_remove(struct i2c_client *client)
{
- int ret;
const struct pmbus_driver_info *info;
info = pmbus_get_driver_info(client);
- ret = pmbus_do_remove(client);
+ pmbus_do_remove(client);
kfree(info);
- return ret;
+ return 0;
}
diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c
new file mode 100644
index 000000000000..2bc980006f83
--- /dev/null
+++ b/drivers/hwmon/pmbus/zl6100.c
@@ -0,0 +1,256 @@
+/*
+ * Hardware monitoring driver for ZL6100 and compatibles
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/ktime.h>
+#include <linux/delay.h>
+#include "pmbus.h"
+
+enum chips { zl2004, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105 };
+
+struct zl6100_data {
+ int id;
+ ktime_t access; /* chip access time */
+ struct pmbus_driver_info info;
+};
+
+#define to_zl6100_data(x) container_of(x, struct zl6100_data, info)
+
+#define ZL6100_DEVICE_ID 0xe4
+
+#define ZL6100_WAIT_TIME 1000 /* uS */
+
+static ushort delay = ZL6100_WAIT_TIME;
+module_param(delay, ushort, 0644);
+MODULE_PARM_DESC(delay, "Delay between chip accesses in uS");
+
+/* Some chips need a delay between accesses */
+static inline void zl6100_wait(const struct zl6100_data *data)
+{
+ if (delay) {
+ s64 delta = ktime_us_delta(ktime_get(), data->access);
+ if (delta < delay)
+ udelay(delay - delta);
+ }
+}
+
+static int zl6100_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct zl6100_data *data = to_zl6100_data(info);
+ int ret;
+
+ if (page || reg >= PMBUS_VIRT_BASE)
+ return -ENXIO;
+
+ zl6100_wait(data);
+ ret = pmbus_read_word_data(client, page, reg);
+ data->access = ktime_get();
+
+ return ret;
+}
+
+static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct zl6100_data *data = to_zl6100_data(info);
+ int ret;
+
+ if (page > 0)
+ return -ENXIO;
+
+ zl6100_wait(data);
+ ret = pmbus_read_byte_data(client, page, reg);
+ data->access = ktime_get();
+
+ return ret;
+}
+
+static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct zl6100_data *data = to_zl6100_data(info);
+ int ret;
+
+ if (page || reg >= PMBUS_VIRT_BASE)
+ return -ENXIO;
+
+ zl6100_wait(data);
+ ret = pmbus_write_word_data(client, page, reg, word);
+ data->access = ktime_get();
+
+ return ret;
+}
+
+static int zl6100_write_byte(struct i2c_client *client, int page, u8 value)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct zl6100_data *data = to_zl6100_data(info);
+ int ret;
+
+ if (page > 0)
+ return -ENXIO;
+
+ zl6100_wait(data);
+ ret = pmbus_write_byte(client, page, value);
+ data->access = ktime_get();
+
+ return ret;
+}
+
+static const struct i2c_device_id zl6100_id[] = {
+ {"zl2004", zl2004},
+ {"zl2006", zl2006},
+ {"zl2008", zl2008},
+ {"zl2105", zl2105},
+ {"zl2106", zl2106},
+ {"zl6100", zl6100},
+ {"zl6105", zl6105},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, zl6100_id);
+
+static int zl6100_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct zl6100_data *data;
+ struct pmbus_driver_info *info;
+ u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
+ const struct i2c_device_id *mid;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA
+ | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID,
+ device_id);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read device ID\n");
+ return ret;
+ }
+ device_id[ret] = '\0';
+ dev_info(&client->dev, "Device ID %s\n", device_id);
+
+ mid = NULL;
+ for (mid = zl6100_id; mid->name[0]; mid++) {
+ if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
+ break;
+ }
+ if (!mid->name[0]) {
+ dev_err(&client->dev, "Unsupported device\n");
+ return -ENODEV;
+ }
+ if (id->driver_data != mid->driver_data)
+ dev_notice(&client->dev,
+ "Device mismatch: Configured %s, detected %s\n",
+ id->name, mid->name);
+
+ data = kzalloc(sizeof(struct zl6100_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->id = mid->driver_data;
+
+ /*
+ * ZL2008, ZL2105, and ZL6100 are known to require a wait time
+ * between I2C accesses. ZL2004 and ZL6105 are known to be safe.
+ *
+ * Only clear the wait time for chips known to be safe. The wait time
+ * can be cleared later for additional chips if tests show that it
+ * is not needed (in other words, better be safe than sorry).
+ */
+ if (data->id == zl2004 || data->id == zl6105)
+ delay = 0;
+
+ /*
+ * Since there was a direct I2C device access above, wait before
+ * accessing the chip again.
+ * Set the timestamp, wait, then set it again. This should provide
+ * enough buffer time to be safe.
+ */
+ data->access = ktime_get();
+ zl6100_wait(data);
+ data->access = ktime_get();
+
+ info = &data->info;
+
+ info->pages = 1;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
+
+ info->read_word_data = zl6100_read_word_data;
+ info->read_byte_data = zl6100_read_byte_data;
+ info->write_word_data = zl6100_write_word_data;
+ info->write_byte = zl6100_write_byte;
+
+ ret = pmbus_do_probe(client, mid, info);
+ if (ret)
+ goto err_mem;
+ return 0;
+
+err_mem:
+ kfree(data);
+ return ret;
+}
+
+static int zl6100_remove(struct i2c_client *client)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct zl6100_data *data = to_zl6100_data(info);
+
+ pmbus_do_remove(client);
+ kfree(data);
+ return 0;
+}
+
+static struct i2c_driver zl6100_driver = {
+ .driver = {
+ .name = "zl6100",
+ },
+ .probe = zl6100_probe,
+ .remove = zl6100_remove,
+ .id_table = zl6100_id,
+};
+
+static int __init zl6100_init(void)
+{
+ return i2c_add_driver(&zl6100_driver);
+}
+
+static void __exit zl6100_exit(void)
+{
+ i2c_del_driver(&zl6100_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles");
+MODULE_LICENSE("GPL");
+module_init(zl6100_init);
+module_exit(zl6100_exit);
diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c
index 3494a4cce414..e3b5c6039c25 100644
--- a/drivers/hwmon/sch5627.c
+++ b/drivers/hwmon/sch5627.c
@@ -28,33 +28,15 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
-#include <linux/io.h>
-#include <linux/acpi.h>
-#include <linux/delay.h>
+#include "sch56xx-common.h"
#define DRVNAME "sch5627"
#define DEVNAME DRVNAME /* We only support one model */
-#define SIO_SCH5627_EM_LD 0x0C /* Embedded Microcontroller LD */
-#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */
-#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
-
-#define SIO_REG_LDSEL 0x07 /* Logical device select */
-#define SIO_REG_DEVID 0x20 /* Device ID */
-#define SIO_REG_ENABLE 0x30 /* Logical device enable */
-#define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */
-
-#define SIO_SCH5627_ID 0xC6 /* Chipset ID */
-
-#define REGION_LENGTH 9
-
#define SCH5627_HWMON_ID 0xa5
#define SCH5627_COMPANY_ID 0x5c
#define SCH5627_PRIMARY_ID 0xa0
-#define SCH5627_CMD_READ 0x02
-#define SCH5627_CMD_WRITE 0x03
-
#define SCH5627_REG_BUILD_CODE 0x39
#define SCH5627_REG_BUILD_ID 0x3a
#define SCH5627_REG_HWMON_ID 0x3c
@@ -111,182 +93,6 @@ struct sch5627_data {
u16 in[SCH5627_NO_IN];
};
-static struct platform_device *sch5627_pdev;
-
-/* Super I/O functions */
-static inline int superio_inb(int base, int reg)
-{
- outb(reg, base);
- return inb(base + 1);
-}
-
-static inline int superio_enter(int base)
-{
- /* Don't step on other drivers' I/O space by accident */
- if (!request_muxed_region(base, 2, DRVNAME)) {
- pr_err("I/O address 0x%04x already in use\n", base);
- return -EBUSY;
- }
-
- outb(SIO_UNLOCK_KEY, base);
-
- return 0;
-}
-
-static inline void superio_select(int base, int ld)
-{
- outb(SIO_REG_LDSEL, base);
- outb(ld, base + 1);
-}
-
-static inline void superio_exit(int base)
-{
- outb(SIO_LOCK_KEY, base);
- release_region(base, 2);
-}
-
-static int sch5627_send_cmd(struct sch5627_data *data, u8 cmd, u16 reg, u8 v)
-{
- u8 val;
- int i;
- /*
- * According to SMSC for the commands we use the maximum time for
- * the EM to respond is 15 ms, but testing shows in practice it
- * responds within 15-32 reads, so we first busy poll, and if
- * that fails sleep a bit and try again until we are way past
- * the 15 ms maximum response time.
- */
- const int max_busy_polls = 64;
- const int max_lazy_polls = 32;
-
- /* (Optional) Write-Clear the EC to Host Mailbox Register */
- val = inb(data->addr + 1);
- outb(val, data->addr + 1);
-
- /* Set Mailbox Address Pointer to first location in Region 1 */
- outb(0x00, data->addr + 2);
- outb(0x80, data->addr + 3);
-
- /* Write Request Packet Header */
- outb(cmd, data->addr + 4); /* VREG Access Type read:0x02 write:0x03 */
- outb(0x01, data->addr + 5); /* # of Entries: 1 Byte (8-bit) */
- outb(0x04, data->addr + 2); /* Mailbox AP to first data entry loc. */
-
- /* Write Value field */
- if (cmd == SCH5627_CMD_WRITE)
- outb(v, data->addr + 4);
-
- /* Write Address field */
- outb(reg & 0xff, data->addr + 6);
- outb(reg >> 8, data->addr + 7);
-
- /* Execute the Random Access Command */
- outb(0x01, data->addr); /* Write 01h to the Host-to-EC register */
-
- /* EM Interface Polling "Algorithm" */
- for (i = 0; i < max_busy_polls + max_lazy_polls; i++) {
- if (i >= max_busy_polls)
- msleep(1);
- /* Read Interrupt source Register */
- val = inb(data->addr + 8);
- /* Write Clear the interrupt source bits */
- if (val)
- outb(val, data->addr + 8);
- /* Command Completed ? */
- if (val & 0x01)
- break;
- }
- if (i == max_busy_polls + max_lazy_polls) {
- pr_err("Max retries exceeded reading virtual "
- "register 0x%04hx (%d)\n", reg, 1);
- return -EIO;
- }
-
- /*
- * According to SMSC we may need to retry this, but sofar I've always
- * seen this succeed in 1 try.
- */
- for (i = 0; i < max_busy_polls; i++) {
- /* Read EC-to-Host Register */
- val = inb(data->addr + 1);
- /* Command Completed ? */
- if (val == 0x01)
- break;
-
- if (i == 0)
- pr_warn("EC reports: 0x%02x reading virtual register "
- "0x%04hx\n", (unsigned int)val, reg);
- }
- if (i == max_busy_polls) {
- pr_err("Max retries exceeded reading virtual "
- "register 0x%04hx (%d)\n", reg, 2);
- return -EIO;
- }
-
- /*
- * According to the SMSC app note we should now do:
- *
- * Set Mailbox Address Pointer to first location in Region 1 *
- * outb(0x00, data->addr + 2);
- * outb(0x80, data->addr + 3);
- *
- * But if we do that things don't work, so let's not.
- */
-
- /* Read Value field */
- if (cmd == SCH5627_CMD_READ)
- return inb(data->addr + 4);
-
- return 0;
-}
-
-static int sch5627_read_virtual_reg(struct sch5627_data *data, u16 reg)
-{
- return sch5627_send_cmd(data, SCH5627_CMD_READ, reg, 0);
-}
-
-static int sch5627_write_virtual_reg(struct sch5627_data *data,
- u16 reg, u8 val)
-{
- return sch5627_send_cmd(data, SCH5627_CMD_WRITE, reg, val);
-}
-
-static int sch5627_read_virtual_reg16(struct sch5627_data *data, u16 reg)
-{
- int lsb, msb;
-
- /* Read LSB first, this will cause the matching MSB to be latched */
- lsb = sch5627_read_virtual_reg(data, reg);
- if (lsb < 0)
- return lsb;
-
- msb = sch5627_read_virtual_reg(data, reg + 1);
- if (msb < 0)
- return msb;
-
- return lsb | (msb << 8);
-}
-
-static int sch5627_read_virtual_reg12(struct sch5627_data *data, u16 msb_reg,
- u16 lsn_reg, int high_nibble)
-{
- int msb, lsn;
-
- /* Read MSB first, this will cause the matching LSN to be latched */
- msb = sch5627_read_virtual_reg(data, msb_reg);
- if (msb < 0)
- return msb;
-
- lsn = sch5627_read_virtual_reg(data, lsn_reg);
- if (lsn < 0)
- return lsn;
-
- if (high_nibble)
- return (msb << 4) | (lsn >> 4);
- else
- return (msb << 4) | (lsn & 0x0f);
-}
-
static struct sch5627_data *sch5627_update_device(struct device *dev)
{
struct sch5627_data *data = dev_get_drvdata(dev);
@@ -297,7 +103,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev)
/* Trigger a Vbat voltage measurement every 5 minutes */
if (time_after(jiffies, data->last_battery + 300 * HZ)) {
- sch5627_write_virtual_reg(data, SCH5627_REG_CTRL,
+ sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL,
data->control | 0x10);
data->last_battery = jiffies;
}
@@ -305,7 +111,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev)
/* Cache the values for 1 second */
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
for (i = 0; i < SCH5627_NO_TEMPS; i++) {
- val = sch5627_read_virtual_reg12(data,
+ val = sch56xx_read_virtual_reg12(data->addr,
SCH5627_REG_TEMP_MSB[i],
SCH5627_REG_TEMP_LSN[i],
SCH5627_REG_TEMP_HIGH_NIBBLE[i]);
@@ -317,7 +123,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev)
}
for (i = 0; i < SCH5627_NO_FANS; i++) {
- val = sch5627_read_virtual_reg16(data,
+ val = sch56xx_read_virtual_reg16(data->addr,
SCH5627_REG_FAN[i]);
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
@@ -327,7 +133,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev)
}
for (i = 0; i < SCH5627_NO_IN; i++) {
- val = sch5627_read_virtual_reg12(data,
+ val = sch56xx_read_virtual_reg12(data->addr,
SCH5627_REG_IN_MSB[i],
SCH5627_REG_IN_LSN[i],
SCH5627_REG_IN_HIGH_NIBBLE[i]);
@@ -355,18 +161,21 @@ static int __devinit sch5627_read_limits(struct sch5627_data *data)
* Note what SMSC calls ABS, is what lm_sensors calls max
* (aka high), and HIGH is what lm_sensors calls crit.
*/
- val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_ABS[i]);
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5627_REG_TEMP_ABS[i]);
if (val < 0)
return val;
data->temp_max[i] = val;
- val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_HIGH[i]);
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5627_REG_TEMP_HIGH[i]);
if (val < 0)
return val;
data->temp_crit[i] = val;
}
for (i = 0; i < SCH5627_NO_FANS; i++) {
- val = sch5627_read_virtual_reg16(data, SCH5627_REG_FAN_MIN[i]);
+ val = sch56xx_read_virtual_reg16(data->addr,
+ SCH5627_REG_FAN_MIN[i]);
if (val < 0)
return val;
data->fan_min[i] = val;
@@ -667,7 +476,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)
mutex_init(&data->update_lock);
platform_set_drvdata(pdev, data);
- val = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_ID);
+ val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID);
if (val < 0) {
err = val;
goto error;
@@ -679,7 +488,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)
goto error;
}
- val = sch5627_read_virtual_reg(data, SCH5627_REG_COMPANY_ID);
+ val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID);
if (val < 0) {
err = val;
goto error;
@@ -691,7 +500,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)
goto error;
}
- val = sch5627_read_virtual_reg(data, SCH5627_REG_PRIMARY_ID);
+ val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID);
if (val < 0) {
err = val;
goto error;
@@ -703,25 +512,28 @@ static int __devinit sch5627_probe(struct platform_device *pdev)
goto error;
}
- build_code = sch5627_read_virtual_reg(data, SCH5627_REG_BUILD_CODE);
+ build_code = sch56xx_read_virtual_reg(data->addr,
+ SCH5627_REG_BUILD_CODE);
if (build_code < 0) {
err = build_code;
goto error;
}
- build_id = sch5627_read_virtual_reg16(data, SCH5627_REG_BUILD_ID);
+ build_id = sch56xx_read_virtual_reg16(data->addr,
+ SCH5627_REG_BUILD_ID);
if (build_id < 0) {
err = build_id;
goto error;
}
- hwmon_rev = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_REV);
+ hwmon_rev = sch56xx_read_virtual_reg(data->addr,
+ SCH5627_REG_HWMON_REV);
if (hwmon_rev < 0) {
err = hwmon_rev;
goto error;
}
- val = sch5627_read_virtual_reg(data, SCH5627_REG_CTRL);
+ val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL);
if (val < 0) {
err = val;
goto error;
@@ -734,7 +546,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)
}
/* Trigger a Vbat voltage measurement, so that we get a valid reading
the first time we read Vbat */
- sch5627_write_virtual_reg(data, SCH5627_REG_CTRL,
+ sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL,
data->control | 0x10);
data->last_battery = jiffies;
@@ -746,6 +558,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)
if (err)
goto error;
+ pr_info("found %s chip at %#hx\n", DEVNAME, data->addr);
pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n",
build_code, build_id, hwmon_rev);
@@ -768,85 +581,6 @@ error:
return err;
}
-static int __init sch5627_find(int sioaddr, unsigned short *address)
-{
- u8 devid;
- int err = superio_enter(sioaddr);
- if (err)
- return err;
-
- devid = superio_inb(sioaddr, SIO_REG_DEVID);
- if (devid != SIO_SCH5627_ID) {
- pr_debug("Unsupported device id: 0x%02x\n",
- (unsigned int)devid);
- err = -ENODEV;
- goto exit;
- }
-
- superio_select(sioaddr, SIO_SCH5627_EM_LD);
-
- if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
- pr_warn("Device not activated\n");
- err = -ENODEV;
- goto exit;
- }
-
- /*
- * Warning the order of the low / high byte is the other way around
- * as on most other superio devices!!
- */
- *address = superio_inb(sioaddr, SIO_REG_ADDR) |
- superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8;
- if (*address == 0) {
- pr_warn("Base address not set\n");
- err = -ENODEV;
- goto exit;
- }
-
- pr_info("Found %s chip at %#hx\n", DEVNAME, *address);
-exit:
- superio_exit(sioaddr);
- return err;
-}
-
-static int __init sch5627_device_add(unsigned short address)
-{
- struct resource res = {
- .start = address,
- .end = address + REGION_LENGTH - 1,
- .flags = IORESOURCE_IO,
- };
- int err;
-
- sch5627_pdev = platform_device_alloc(DRVNAME, address);
- if (!sch5627_pdev)
- return -ENOMEM;
-
- res.name = sch5627_pdev->name;
- err = acpi_check_resource_conflict(&res);
- if (err)
- goto exit_device_put;
-
- err = platform_device_add_resources(sch5627_pdev, &res, 1);
- if (err) {
- pr_err("Device resource addition failed\n");
- goto exit_device_put;
- }
-
- err = platform_device_add(sch5627_pdev);
- if (err) {
- pr_err("Device addition failed\n");
- goto exit_device_put;
- }
-
- return 0;
-
-exit_device_put:
- platform_device_put(sch5627_pdev);
-
- return err;
-}
-
static struct platform_driver sch5627_driver = {
.driver = {
.owner = THIS_MODULE,
@@ -858,31 +592,11 @@ static struct platform_driver sch5627_driver = {
static int __init sch5627_init(void)
{
- int err = -ENODEV;
- unsigned short address;
-
- if (sch5627_find(0x4e, &address) && sch5627_find(0x2e, &address))
- goto exit;
-
- err = platform_driver_register(&sch5627_driver);
- if (err)
- goto exit;
-
- err = sch5627_device_add(address);
- if (err)
- goto exit_driver;
-
- return 0;
-
-exit_driver:
- platform_driver_unregister(&sch5627_driver);
-exit:
- return err;
+ return platform_driver_register(&sch5627_driver);
}
static void __exit sch5627_exit(void)
{
- platform_device_unregister(sch5627_pdev);
platform_driver_unregister(&sch5627_driver);
}
diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c
new file mode 100644
index 000000000000..244407aa79fc
--- /dev/null
+++ b/drivers/hwmon/sch5636.c
@@ -0,0 +1,539 @@
+/***************************************************************************
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * 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. *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include "sch56xx-common.h"
+
+#define DRVNAME "sch5636"
+#define DEVNAME "theseus" /* We only support one model for now */
+
+#define SCH5636_REG_FUJITSU_ID 0x780
+#define SCH5636_REG_FUJITSU_REV 0x783
+
+#define SCH5636_NO_INS 5
+#define SCH5636_NO_TEMPS 16
+#define SCH5636_NO_FANS 8
+
+static const u16 SCH5636_REG_IN_VAL[SCH5636_NO_INS] = {
+ 0x22, 0x23, 0x24, 0x25, 0x189 };
+static const u16 SCH5636_REG_IN_FACTORS[SCH5636_NO_INS] = {
+ 4400, 1500, 4000, 4400, 16000 };
+static const char * const SCH5636_IN_LABELS[SCH5636_NO_INS] = {
+ "3.3V", "VREF", "VBAT", "3.3AUX", "12V" };
+
+static const u16 SCH5636_REG_TEMP_VAL[SCH5636_NO_TEMPS] = {
+ 0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181,
+ 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C };
+#define SCH5636_REG_TEMP_CTRL(i) (0x790 + (i))
+#define SCH5636_TEMP_WORKING 0x01
+#define SCH5636_TEMP_ALARM 0x02
+#define SCH5636_TEMP_DEACTIVATED 0x80
+
+static const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = {
+ 0x2C, 0x2E, 0x30, 0x32, 0x62, 0x64, 0x66, 0x68 };
+#define SCH5636_REG_FAN_CTRL(i) (0x880 + (i))
+/* FAULT in datasheet, but acts as an alarm */
+#define SCH5636_FAN_ALARM 0x04
+#define SCH5636_FAN_NOT_PRESENT 0x08
+#define SCH5636_FAN_DEACTIVATED 0x80
+
+
+struct sch5636_data {
+ unsigned short addr;
+ struct device *hwmon_dev;
+
+ struct mutex update_lock;
+ char valid; /* !=0 if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+ u8 in[SCH5636_NO_INS];
+ u8 temp_val[SCH5636_NO_TEMPS];
+ u8 temp_ctrl[SCH5636_NO_TEMPS];
+ u16 fan_val[SCH5636_NO_FANS];
+ u8 fan_ctrl[SCH5636_NO_FANS];
+};
+
+static struct sch5636_data *sch5636_update_device(struct device *dev)
+{
+ struct sch5636_data *data = dev_get_drvdata(dev);
+ struct sch5636_data *ret = data;
+ int i, val;
+
+ mutex_lock(&data->update_lock);
+
+ /* Cache the values for 1 second */
+ if (data->valid && !time_after(jiffies, data->last_updated + HZ))
+ goto abort;
+
+ for (i = 0; i < SCH5636_NO_INS; i++) {
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_IN_VAL[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->in[i] = val;
+ }
+
+ for (i = 0; i < SCH5636_NO_TEMPS; i++) {
+ if (data->temp_ctrl[i] & SCH5636_TEMP_DEACTIVATED)
+ continue;
+
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_TEMP_VAL[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp_val[i] = val;
+
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_TEMP_CTRL(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp_ctrl[i] = val;
+ /* Alarms need to be explicitly write-cleared */
+ if (val & SCH5636_TEMP_ALARM) {
+ sch56xx_write_virtual_reg(data->addr,
+ SCH5636_REG_TEMP_CTRL(i), val);
+ }
+ }
+
+ for (i = 0; i < SCH5636_NO_FANS; i++) {
+ if (data->fan_ctrl[i] & SCH5636_FAN_DEACTIVATED)
+ continue;
+
+ val = sch56xx_read_virtual_reg16(data->addr,
+ SCH5636_REG_FAN_VAL[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->fan_val[i] = val;
+
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_FAN_CTRL(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->fan_ctrl[i] = val;
+ /* Alarms need to be explicitly write-cleared */
+ if (val & SCH5636_FAN_ALARM) {
+ sch56xx_write_virtual_reg(data->addr,
+ SCH5636_REG_FAN_CTRL(i), val);
+ }
+ }
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int reg_to_rpm(u16 reg)
+{
+ if (reg == 0)
+ return -EIO;
+ if (reg == 0xffff)
+ return 0;
+
+ return 5400540 / reg;
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME);
+}
+
+static ssize_t show_in_value(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = DIV_ROUND_CLOSEST(
+ data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index],
+ 255);
+ return snprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t show_in_label(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ SCH5636_IN_LABELS[attr->index]);
+}
+
+static ssize_t show_temp_value(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = (data->temp_val[attr->index] - 64) * 1000;
+ return snprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t show_temp_fault(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1;
+ return snprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t show_temp_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t show_fan_value(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = reg_to_rpm(data->fan_val[attr->index]);
+ if (val < 0)
+ return val;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t show_fan_fault(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static struct sensor_device_attribute sch5636_attr[] = {
+ SENSOR_ATTR(name, 0444, show_name, NULL, 0),
+ SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0),
+ SENSOR_ATTR(in0_label, 0444, show_in_label, NULL, 0),
+ SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1),
+ SENSOR_ATTR(in1_label, 0444, show_in_label, NULL, 1),
+ SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2),
+ SENSOR_ATTR(in2_label, 0444, show_in_label, NULL, 2),
+ SENSOR_ATTR(in3_input, 0444, show_in_value, NULL, 3),
+ SENSOR_ATTR(in3_label, 0444, show_in_label, NULL, 3),
+ SENSOR_ATTR(in4_input, 0444, show_in_value, NULL, 4),
+ SENSOR_ATTR(in4_label, 0444, show_in_label, NULL, 4),
+};
+
+static struct sensor_device_attribute sch5636_temp_attr[] = {
+ SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0),
+ SENSOR_ATTR(temp1_fault, 0444, show_temp_fault, NULL, 0),
+ SENSOR_ATTR(temp1_alarm, 0444, show_temp_alarm, NULL, 0),
+ SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1),
+ SENSOR_ATTR(temp2_fault, 0444, show_temp_fault, NULL, 1),
+ SENSOR_ATTR(temp2_alarm, 0444, show_temp_alarm, NULL, 1),
+ SENSOR_ATTR(temp3_input, 0444, show_temp_value, NULL, 2),
+ SENSOR_ATTR(temp3_fault, 0444, show_temp_fault, NULL, 2),
+ SENSOR_ATTR(temp3_alarm, 0444, show_temp_alarm, NULL, 2),
+ SENSOR_ATTR(temp4_input, 0444, show_temp_value, NULL, 3),
+ SENSOR_ATTR(temp4_fault, 0444, show_temp_fault, NULL, 3),
+ SENSOR_ATTR(temp4_alarm, 0444, show_temp_alarm, NULL, 3),
+ SENSOR_ATTR(temp5_input, 0444, show_temp_value, NULL, 4),
+ SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4),
+ SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4),
+ SENSOR_ATTR(temp6_input, 0444, show_temp_value, NULL, 5),
+ SENSOR_ATTR(temp6_fault, 0444, show_temp_fault, NULL, 5),
+ SENSOR_ATTR(temp6_alarm, 0444, show_temp_alarm, NULL, 5),
+ SENSOR_ATTR(temp7_input, 0444, show_temp_value, NULL, 6),
+ SENSOR_ATTR(temp7_fault, 0444, show_temp_fault, NULL, 6),
+ SENSOR_ATTR(temp7_alarm, 0444, show_temp_alarm, NULL, 6),
+ SENSOR_ATTR(temp8_input, 0444, show_temp_value, NULL, 7),
+ SENSOR_ATTR(temp8_fault, 0444, show_temp_fault, NULL, 7),
+ SENSOR_ATTR(temp8_alarm, 0444, show_temp_alarm, NULL, 7),
+ SENSOR_ATTR(temp9_input, 0444, show_temp_value, NULL, 8),
+ SENSOR_ATTR(temp9_fault, 0444, show_temp_fault, NULL, 8),
+ SENSOR_ATTR(temp9_alarm, 0444, show_temp_alarm, NULL, 8),
+ SENSOR_ATTR(temp10_input, 0444, show_temp_value, NULL, 9),
+ SENSOR_ATTR(temp10_fault, 0444, show_temp_fault, NULL, 9),
+ SENSOR_ATTR(temp10_alarm, 0444, show_temp_alarm, NULL, 9),
+ SENSOR_ATTR(temp11_input, 0444, show_temp_value, NULL, 10),
+ SENSOR_ATTR(temp11_fault, 0444, show_temp_fault, NULL, 10),
+ SENSOR_ATTR(temp11_alarm, 0444, show_temp_alarm, NULL, 10),
+ SENSOR_ATTR(temp12_input, 0444, show_temp_value, NULL, 11),
+ SENSOR_ATTR(temp12_fault, 0444, show_temp_fault, NULL, 11),
+ SENSOR_ATTR(temp12_alarm, 0444, show_temp_alarm, NULL, 11),
+ SENSOR_ATTR(temp13_input, 0444, show_temp_value, NULL, 12),
+ SENSOR_ATTR(temp13_fault, 0444, show_temp_fault, NULL, 12),
+ SENSOR_ATTR(temp13_alarm, 0444, show_temp_alarm, NULL, 12),
+ SENSOR_ATTR(temp14_input, 0444, show_temp_value, NULL, 13),
+ SENSOR_ATTR(temp14_fault, 0444, show_temp_fault, NULL, 13),
+ SENSOR_ATTR(temp14_alarm, 0444, show_temp_alarm, NULL, 13),
+ SENSOR_ATTR(temp15_input, 0444, show_temp_value, NULL, 14),
+ SENSOR_ATTR(temp15_fault, 0444, show_temp_fault, NULL, 14),
+ SENSOR_ATTR(temp15_alarm, 0444, show_temp_alarm, NULL, 14),
+ SENSOR_ATTR(temp16_input, 0444, show_temp_value, NULL, 15),
+ SENSOR_ATTR(temp16_fault, 0444, show_temp_fault, NULL, 15),
+ SENSOR_ATTR(temp16_alarm, 0444, show_temp_alarm, NULL, 15),
+};
+
+static struct sensor_device_attribute sch5636_fan_attr[] = {
+ SENSOR_ATTR(fan1_input, 0444, show_fan_value, NULL, 0),
+ SENSOR_ATTR(fan1_fault, 0444, show_fan_fault, NULL, 0),
+ SENSOR_ATTR(fan1_alarm, 0444, show_fan_alarm, NULL, 0),
+ SENSOR_ATTR(fan2_input, 0444, show_fan_value, NULL, 1),
+ SENSOR_ATTR(fan2_fault, 0444, show_fan_fault, NULL, 1),
+ SENSOR_ATTR(fan2_alarm, 0444, show_fan_alarm, NULL, 1),
+ SENSOR_ATTR(fan3_input, 0444, show_fan_value, NULL, 2),
+ SENSOR_ATTR(fan3_fault, 0444, show_fan_fault, NULL, 2),
+ SENSOR_ATTR(fan3_alarm, 0444, show_fan_alarm, NULL, 2),
+ SENSOR_ATTR(fan4_input, 0444, show_fan_value, NULL, 3),
+ SENSOR_ATTR(fan4_fault, 0444, show_fan_fault, NULL, 3),
+ SENSOR_ATTR(fan4_alarm, 0444, show_fan_alarm, NULL, 3),
+ SENSOR_ATTR(fan5_input, 0444, show_fan_value, NULL, 4),
+ SENSOR_ATTR(fan5_fault, 0444, show_fan_fault, NULL, 4),
+ SENSOR_ATTR(fan5_alarm, 0444, show_fan_alarm, NULL, 4),
+ SENSOR_ATTR(fan6_input, 0444, show_fan_value, NULL, 5),
+ SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5),
+ SENSOR_ATTR(fan6_alarm, 0444, show_fan_alarm, NULL, 5),
+ SENSOR_ATTR(fan7_input, 0444, show_fan_value, NULL, 6),
+ SENSOR_ATTR(fan7_fault, 0444, show_fan_fault, NULL, 6),
+ SENSOR_ATTR(fan7_alarm, 0444, show_fan_alarm, NULL, 6),
+ SENSOR_ATTR(fan8_input, 0444, show_fan_value, NULL, 7),
+ SENSOR_ATTR(fan8_fault, 0444, show_fan_fault, NULL, 7),
+ SENSOR_ATTR(fan8_alarm, 0444, show_fan_alarm, NULL, 7),
+};
+
+static int sch5636_remove(struct platform_device *pdev)
+{
+ struct sch5636_data *data = platform_get_drvdata(pdev);
+ int i;
+
+ if (data->hwmon_dev)
+ hwmon_device_unregister(data->hwmon_dev);
+
+ for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++)
+ device_remove_file(&pdev->dev, &sch5636_attr[i].dev_attr);
+
+ for (i = 0; i < SCH5636_NO_TEMPS * 3; i++)
+ device_remove_file(&pdev->dev,
+ &sch5636_temp_attr[i].dev_attr);
+
+ for (i = 0; i < SCH5636_NO_FANS * 3; i++)
+ device_remove_file(&pdev->dev,
+ &sch5636_fan_attr[i].dev_attr);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(data);
+
+ return 0;
+}
+
+static int __devinit sch5636_probe(struct platform_device *pdev)
+{
+ struct sch5636_data *data;
+ int i, err, val, revision[2];
+ char id[4];
+
+ data = kzalloc(sizeof(struct sch5636_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
+ mutex_init(&data->update_lock);
+ platform_set_drvdata(pdev, data);
+
+ for (i = 0; i < 3; i++) {
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_FUJITSU_ID + i);
+ if (val < 0) {
+ pr_err("Could not read Fujitsu id byte at %#x\n",
+ SCH5636_REG_FUJITSU_ID + i);
+ err = val;
+ goto error;
+ }
+ id[i] = val;
+ }
+ id[i] = '\0';
+
+ if (strcmp(id, "THS")) {
+ pr_err("Unknown Fujitsu id: %02x%02x%02x\n",
+ id[0], id[1], id[2]);
+ err = -ENODEV;
+ goto error;
+ }
+
+ for (i = 0; i < 2; i++) {
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_FUJITSU_REV + i);
+ if (val < 0) {
+ err = val;
+ goto error;
+ }
+ revision[i] = val;
+ }
+ pr_info("Found %s chip at %#hx, revison: %d.%02d\n", DEVNAME,
+ data->addr, revision[0], revision[1]);
+
+ /* Read all temp + fan ctrl registers to determine which are active */
+ for (i = 0; i < SCH5636_NO_TEMPS; i++) {
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_TEMP_CTRL(i));
+ if (unlikely(val < 0)) {
+ err = val;
+ goto error;
+ }
+ data->temp_ctrl[i] = val;
+ }
+
+ for (i = 0; i < SCH5636_NO_FANS; i++) {
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_FAN_CTRL(i));
+ if (unlikely(val < 0)) {
+ err = val;
+ goto error;
+ }
+ data->fan_ctrl[i] = val;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) {
+ err = device_create_file(&pdev->dev,
+ &sch5636_attr[i].dev_attr);
+ if (err)
+ goto error;
+ }
+
+ for (i = 0; i < (SCH5636_NO_TEMPS * 3); i++) {
+ if (data->temp_ctrl[i/3] & SCH5636_TEMP_DEACTIVATED)
+ continue;
+
+ err = device_create_file(&pdev->dev,
+ &sch5636_temp_attr[i].dev_attr);
+ if (err)
+ goto error;
+ }
+
+ for (i = 0; i < (SCH5636_NO_FANS * 3); i++) {
+ if (data->fan_ctrl[i/3] & SCH5636_FAN_DEACTIVATED)
+ continue;
+
+ err = device_create_file(&pdev->dev,
+ &sch5636_fan_attr[i].dev_attr);
+ if (err)
+ goto error;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ data->hwmon_dev = NULL;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ sch5636_remove(pdev);
+ return err;
+}
+
+static struct platform_driver sch5636_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRVNAME,
+ },
+ .probe = sch5636_probe,
+ .remove = sch5636_remove,
+};
+
+static int __init sch5636_init(void)
+{
+ return platform_driver_register(&sch5636_driver);
+}
+
+static void __exit sch5636_exit(void)
+{
+ platform_driver_unregister(&sch5636_driver);
+}
+
+MODULE_DESCRIPTION("SMSC SCH5636 Hardware Monitoring Driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
+
+module_init(sch5636_init);
+module_exit(sch5636_exit);
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c
new file mode 100644
index 000000000000..fac32ee0b10e
--- /dev/null
+++ b/drivers/hwmon/sch56xx-common.c
@@ -0,0 +1,340 @@
+/***************************************************************************
+ * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * 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. *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include "sch56xx-common.h"
+
+#define SIO_SCH56XX_LD_EM 0x0C /* Embedded uController Logical Dev */
+#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */
+#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */
+
+#define SIO_SCH5627_ID 0xC6 /* Chipset ID */
+#define SIO_SCH5636_ID 0xC7 /* Chipset ID */
+
+#define REGION_LENGTH 9
+
+#define SCH56XX_CMD_READ 0x02
+#define SCH56XX_CMD_WRITE 0x03
+
+static struct platform_device *sch56xx_pdev;
+
+/* Super I/O functions */
+static inline int superio_inb(int base, int reg)
+{
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static inline int superio_enter(int base)
+{
+ /* Don't step on other drivers' I/O space by accident */
+ if (!request_muxed_region(base, 2, "sch56xx")) {
+ pr_err("I/O address 0x%04x already in use\n", base);
+ return -EBUSY;
+ }
+
+ outb(SIO_UNLOCK_KEY, base);
+
+ return 0;
+}
+
+static inline void superio_select(int base, int ld)
+{
+ outb(SIO_REG_LDSEL, base);
+ outb(ld, base + 1);
+}
+
+static inline void superio_exit(int base)
+{
+ outb(SIO_LOCK_KEY, base);
+ release_region(base, 2);
+}
+
+static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v)
+{
+ u8 val;
+ int i;
+ /*
+ * According to SMSC for the commands we use the maximum time for
+ * the EM to respond is 15 ms, but testing shows in practice it
+ * responds within 15-32 reads, so we first busy poll, and if
+ * that fails sleep a bit and try again until we are way past
+ * the 15 ms maximum response time.
+ */
+ const int max_busy_polls = 64;
+ const int max_lazy_polls = 32;
+
+ /* (Optional) Write-Clear the EC to Host Mailbox Register */
+ val = inb(addr + 1);
+ outb(val, addr + 1);
+
+ /* Set Mailbox Address Pointer to first location in Region 1 */
+ outb(0x00, addr + 2);
+ outb(0x80, addr + 3);
+
+ /* Write Request Packet Header */
+ outb(cmd, addr + 4); /* VREG Access Type read:0x02 write:0x03 */
+ outb(0x01, addr + 5); /* # of Entries: 1 Byte (8-bit) */
+ outb(0x04, addr + 2); /* Mailbox AP to first data entry loc. */
+
+ /* Write Value field */
+ if (cmd == SCH56XX_CMD_WRITE)
+ outb(v, addr + 4);
+
+ /* Write Address field */
+ outb(reg & 0xff, addr + 6);
+ outb(reg >> 8, addr + 7);
+
+ /* Execute the Random Access Command */
+ outb(0x01, addr); /* Write 01h to the Host-to-EC register */
+
+ /* EM Interface Polling "Algorithm" */
+ for (i = 0; i < max_busy_polls + max_lazy_polls; i++) {
+ if (i >= max_busy_polls)
+ msleep(1);
+ /* Read Interrupt source Register */
+ val = inb(addr + 8);
+ /* Write Clear the interrupt source bits */
+ if (val)
+ outb(val, addr + 8);
+ /* Command Completed ? */
+ if (val & 0x01)
+ break;
+ }
+ if (i == max_busy_polls + max_lazy_polls) {
+ pr_err("Max retries exceeded reading virtual "
+ "register 0x%04hx (%d)\n", reg, 1);
+ return -EIO;
+ }
+
+ /*
+ * According to SMSC we may need to retry this, but sofar I've always
+ * seen this succeed in 1 try.
+ */
+ for (i = 0; i < max_busy_polls; i++) {
+ /* Read EC-to-Host Register */
+ val = inb(addr + 1);
+ /* Command Completed ? */
+ if (val == 0x01)
+ break;
+
+ if (i == 0)
+ pr_warn("EC reports: 0x%02x reading virtual register "
+ "0x%04hx\n", (unsigned int)val, reg);
+ }
+ if (i == max_busy_polls) {
+ pr_err("Max retries exceeded reading virtual "
+ "register 0x%04hx (%d)\n", reg, 2);
+ return -EIO;
+ }
+
+ /*
+ * According to the SMSC app note we should now do:
+ *
+ * Set Mailbox Address Pointer to first location in Region 1 *
+ * outb(0x00, addr + 2);
+ * outb(0x80, addr + 3);
+ *
+ * But if we do that things don't work, so let's not.
+ */
+
+ /* Read Value field */
+ if (cmd == SCH56XX_CMD_READ)
+ return inb(addr + 4);
+
+ return 0;
+}
+
+int sch56xx_read_virtual_reg(u16 addr, u16 reg)
+{
+ return sch56xx_send_cmd(addr, SCH56XX_CMD_READ, reg, 0);
+}
+EXPORT_SYMBOL(sch56xx_read_virtual_reg);
+
+int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val)
+{
+ return sch56xx_send_cmd(addr, SCH56XX_CMD_WRITE, reg, val);
+}
+EXPORT_SYMBOL(sch56xx_write_virtual_reg);
+
+int sch56xx_read_virtual_reg16(u16 addr, u16 reg)
+{
+ int lsb, msb;
+
+ /* Read LSB first, this will cause the matching MSB to be latched */
+ lsb = sch56xx_read_virtual_reg(addr, reg);
+ if (lsb < 0)
+ return lsb;
+
+ msb = sch56xx_read_virtual_reg(addr, reg + 1);
+ if (msb < 0)
+ return msb;
+
+ return lsb | (msb << 8);
+}
+EXPORT_SYMBOL(sch56xx_read_virtual_reg16);
+
+int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg,
+ int high_nibble)
+{
+ int msb, lsn;
+
+ /* Read MSB first, this will cause the matching LSN to be latched */
+ msb = sch56xx_read_virtual_reg(addr, msb_reg);
+ if (msb < 0)
+ return msb;
+
+ lsn = sch56xx_read_virtual_reg(addr, lsn_reg);
+ if (lsn < 0)
+ return lsn;
+
+ if (high_nibble)
+ return (msb << 4) | (lsn >> 4);
+ else
+ return (msb << 4) | (lsn & 0x0f);
+}
+EXPORT_SYMBOL(sch56xx_read_virtual_reg12);
+
+static int __init sch56xx_find(int sioaddr, unsigned short *address,
+ const char **name)
+{
+ u8 devid;
+ int err;
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ devid = superio_inb(sioaddr, SIO_REG_DEVID);
+ switch (devid) {
+ case SIO_SCH5627_ID:
+ *name = "sch5627";
+ break;
+ case SIO_SCH5636_ID:
+ *name = "sch5636";
+ break;
+ default:
+ pr_debug("Unsupported device id: 0x%02x\n",
+ (unsigned int)devid);
+ err = -ENODEV;
+ goto exit;
+ }
+
+ superio_select(sioaddr, SIO_SCH56XX_LD_EM);
+
+ if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
+ pr_warn("Device not activated\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ /*
+ * Warning the order of the low / high byte is the other way around
+ * as on most other superio devices!!
+ */
+ *address = superio_inb(sioaddr, SIO_REG_ADDR) |
+ superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8;
+ if (*address == 0) {
+ pr_warn("Base address not set\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+exit:
+ superio_exit(sioaddr);
+ return err;
+}
+
+static int __init sch56xx_device_add(unsigned short address, const char *name)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + REGION_LENGTH - 1,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ sch56xx_pdev = platform_device_alloc(name, address);
+ if (!sch56xx_pdev)
+ return -ENOMEM;
+
+ res.name = sch56xx_pdev->name;
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ goto exit_device_put;
+
+ err = platform_device_add_resources(sch56xx_pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed\n");
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(sch56xx_pdev);
+ if (err) {
+ pr_err("Device addition failed\n");
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(sch56xx_pdev);
+
+ return err;
+}
+
+static int __init sch56xx_init(void)
+{
+ int err;
+ unsigned short address;
+ const char *name;
+
+ err = sch56xx_find(0x4e, &address, &name);
+ if (err)
+ err = sch56xx_find(0x2e, &address, &name);
+ if (err)
+ return err;
+
+ return sch56xx_device_add(address, name);
+}
+
+static void __exit sch56xx_exit(void)
+{
+ platform_device_unregister(sch56xx_pdev);
+}
+
+MODULE_DESCRIPTION("SMSC SCH56xx Hardware Monitoring Common Code");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
+
+module_init(sch56xx_init);
+module_exit(sch56xx_exit);
diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h
new file mode 100644
index 000000000000..d5eaf3b9ebf5
--- /dev/null
+++ b/drivers/hwmon/sch56xx-common.h
@@ -0,0 +1,24 @@
+/***************************************************************************
+ * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * 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. *
+ ***************************************************************************/
+
+int sch56xx_read_virtual_reg(u16 addr, u16 reg);
+int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val);
+int sch56xx_read_virtual_reg16(u16 addr, u16 reg);
+int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg,
+ int high_nibble);
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index cf4330b352ef..fe4104c6b764 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -32,7 +32,7 @@
#include <linux/sht15.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
/* Commands */
#define SHT15_MEASURE_TEMP 0x03
@@ -671,7 +671,7 @@ static ssize_t sht15_show_status(struct device *dev,
* @buf: sysfs buffer to read the new heater state from.
* @count: length of the data.
*
- * Will be called on read access to heater_enable sysfs attribute.
+ * Will be called on write access to heater_enable sysfs attribute.
* Returns number of bytes actually decoded, negative errno on error.
*/
static ssize_t sht15_store_heater(struct device *dev,
diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c
index 1c8c9812f244..15398780cc00 100644
--- a/drivers/hwmon/sht21.c
+++ b/drivers/hwmon/sht21.c
@@ -83,25 +83,6 @@ static inline int sht21_rh_ticks_to_per_cent_mille(int ticks)
}
/**
- * sht21_read_word_data() - read word from register
- * @client: I2C client device
- * @reg: I2C command byte
- *
- * Returns value, negative errno on error.
- */
-static inline int sht21_read_word_data(struct i2c_client *client, u8 reg)
-{
- int ret = i2c_smbus_read_word_data(client, reg);
- if (ret < 0)
- return ret;
- /*
- * SMBus specifies low byte first, but the SHT21 returns MSB
- * first, so we have to swab16 the values
- */
- return swab16(ret);
-}
-
-/**
* sht21_update_measurements() - get updated measurements from device
* @client: I2C client device
*
@@ -119,12 +100,13 @@ static int sht21_update_measurements(struct i2c_client *client)
* maximum two measurements per second at 12bit accuracy shall be made.
*/
if (time_after(jiffies, sht21->last_update + HZ / 2) || !sht21->valid) {
- ret = sht21_read_word_data(client, SHT21_TRIG_T_MEASUREMENT_HM);
+ ret = i2c_smbus_read_word_swapped(client,
+ SHT21_TRIG_T_MEASUREMENT_HM);
if (ret < 0)
goto out;
sht21->temperature = sht21_temp_ticks_to_millicelsius(ret);
- ret = sht21_read_word_data(client,
- SHT21_TRIG_RH_MEASUREMENT_HM);
+ ret = i2c_smbus_read_word_swapped(client,
+ SHT21_TRIG_RH_MEASUREMENT_HM);
if (ret < 0)
goto out;
sht21->humidity = sht21_rh_ticks_to_per_cent_mille(ret);
diff --git a/drivers/hwmon/smm665.c b/drivers/hwmon/smm665.c
index 425df5bccd45..411638181fd8 100644
--- a/drivers/hwmon/smm665.c
+++ b/drivers/hwmon/smm665.c
@@ -214,33 +214,26 @@ static int smm665_read_adc(struct smm665_data *data, int adc)
*
* Neither i2c_smbus_read_byte() nor
* i2c_smbus_read_block_data() worked here,
- * so use i2c_smbus_read_word_data() instead.
+ * so use i2c_smbus_read_word_swapped() instead.
* We could also try to use i2c_master_recv(),
* but that is not always supported.
*/
- rv = i2c_smbus_read_word_data(client, 0);
+ rv = i2c_smbus_read_word_swapped(client, 0);
if (rv < 0) {
dev_dbg(&client->dev, "Failed to read ADC value: error %d", rv);
return -1;
}
/*
* Validate/verify readback adc channel (in bit 11..14).
- * High byte is in lower 8 bit of rv, so only shift by 3.
*/
- radc = (rv >> 3) & 0x0f;
+ radc = (rv >> 11) & 0x0f;
if (radc != adc) {
dev_dbg(&client->dev, "Unexpected RADC: Expected %d got %d",
adc, radc);
return -EIO;
}
- /*
- * Chip replies with H/L, while SMBus expects L/H.
- * Thus, byte order is reversed, and we have to swap
- * the result.
- */
- rv = swab16(rv) & SMM665_ADC_MASK;
- return rv;
+ return rv & SMM665_ADC_MASK;
}
static struct smm665_data *smm665_update_device(struct device *dev)
diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c
index 9fb7516e6f45..65c88ff5645a 100644
--- a/drivers/hwmon/smsc47b397.c
+++ b/drivers/hwmon/smsc47b397.c
@@ -113,7 +113,7 @@ struct smsc47b397_data {
u8 temp[4];
};
-static int smsc47b397_read_value(struct smsc47b397_data* data, u8 reg)
+static int smsc47b397_read_value(struct smsc47b397_data *data, u8 reg)
{
int res;
@@ -265,7 +265,8 @@ static int __devinit smsc47b397_probe(struct platform_device *pdev)
return -EBUSY;
}
- if (!(data = kzalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) {
+ data = kzalloc(sizeof(struct smsc47b397_data), GFP_KERNEL);
+ if (!data) {
err = -ENOMEM;
goto error_release;
}
@@ -276,7 +277,8 @@ static int __devinit smsc47b397_probe(struct platform_device *pdev)
mutex_init(&data->update_lock);
platform_set_drvdata(pdev, data);
- if ((err = sysfs_create_group(&dev->kobj, &smsc47b397_group)))
+ err = sysfs_create_group(&dev->kobj, &smsc47b397_group);
+ if (err)
goto error_free;
data->hwmon_dev = hwmon_device_register(dev);
@@ -345,7 +347,7 @@ static int __init smsc47b397_find(unsigned short *addr)
superio_enter();
id = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
- switch(id) {
+ switch (id) {
case 0x81:
name = "SCH5307-NS";
break;
@@ -379,7 +381,8 @@ static int __init smsc47b397_init(void)
unsigned short address;
int ret;
- if ((ret = smsc47b397_find(&address)))
+ ret = smsc47b397_find(&address);
+ if (ret)
return ret;
ret = platform_driver_register(&smsc47b397_driver);
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 5bd194968801..643aa8c94535 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -55,19 +55,6 @@ struct tmp102 {
int temp[3];
};
-/* SMBus specifies low byte first, but the TMP102 returns high byte first,
- * so we have to swab16 the values */
-static inline int tmp102_read_reg(struct i2c_client *client, u8 reg)
-{
- int result = i2c_smbus_read_word_data(client, reg);
- return result < 0 ? result : swab16(result);
-}
-
-static inline int tmp102_write_reg(struct i2c_client *client, u8 reg, u16 val)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(val));
-}
-
/* convert left adjusted 13-bit TMP102 register value to milliCelsius */
static inline int tmp102_reg_to_mC(s16 val)
{
@@ -94,7 +81,8 @@ static struct tmp102 *tmp102_update_device(struct i2c_client *client)
if (time_after(jiffies, tmp102->last_update + HZ / 3)) {
int i;
for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) {
- int status = tmp102_read_reg(client, tmp102_reg[i]);
+ int status = i2c_smbus_read_word_swapped(client,
+ tmp102_reg[i]);
if (status > -1)
tmp102->temp[i] = tmp102_reg_to_mC(status);
}
@@ -130,8 +118,8 @@ static ssize_t tmp102_set_temp(struct device *dev,
mutex_lock(&tmp102->lock);
tmp102->temp[sda->index] = val;
- status = tmp102_write_reg(client, tmp102_reg[sda->index],
- tmp102_mC_to_reg(val));
+ status = i2c_smbus_write_word_swapped(client, tmp102_reg[sda->index],
+ tmp102_mC_to_reg(val));
mutex_unlock(&tmp102->lock);
return status ? : count;
}
@@ -178,18 +166,19 @@ static int __devinit tmp102_probe(struct i2c_client *client,
}
i2c_set_clientdata(client, tmp102);
- status = tmp102_read_reg(client, TMP102_CONF_REG);
+ status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (status < 0) {
dev_err(&client->dev, "error reading config register\n");
goto fail_free;
}
tmp102->config_orig = status;
- status = tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONFIG);
+ status = i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
+ TMP102_CONFIG);
if (status < 0) {
dev_err(&client->dev, "error writing config register\n");
goto fail_restore_config;
}
- status = tmp102_read_reg(client, TMP102_CONF_REG);
+ status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (status < 0) {
dev_err(&client->dev, "error reading config register\n");
goto fail_restore_config;
@@ -222,7 +211,8 @@ static int __devinit tmp102_probe(struct i2c_client *client,
fail_remove_sysfs:
sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
fail_restore_config:
- tmp102_write_reg(client, TMP102_CONF_REG, tmp102->config_orig);
+ i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
+ tmp102->config_orig);
fail_free:
kfree(tmp102);
@@ -240,10 +230,10 @@ static int __devexit tmp102_remove(struct i2c_client *client)
if (tmp102->config_orig & TMP102_CONF_SD) {
int config;
- config = tmp102_read_reg(client, TMP102_CONF_REG);
+ config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (config >= 0)
- tmp102_write_reg(client, TMP102_CONF_REG,
- config | TMP102_CONF_SD);
+ i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
+ config | TMP102_CONF_SD);
}
kfree(tmp102);
@@ -257,12 +247,12 @@ static int tmp102_suspend(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
int config;
- config = tmp102_read_reg(client, TMP102_CONF_REG);
+ config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (config < 0)
return config;
config |= TMP102_CONF_SD;
- return tmp102_write_reg(client, TMP102_CONF_REG, config);
+ return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config);
}
static int tmp102_resume(struct device *dev)
@@ -270,12 +260,12 @@ static int tmp102_resume(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
int config;
- config = tmp102_read_reg(client, TMP102_CONF_REG);
+ config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (config < 0)
return config;
config &= ~TMP102_CONF_SD;
- return tmp102_write_reg(client, TMP102_CONF_REG, config);
+ return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config);
}
static const struct dev_pm_ops tmp102_dev_pm_ops = {
diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c
index 27a62711e0a6..3cd07bf42dca 100644
--- a/drivers/hwmon/ultra45_env.c
+++ b/drivers/hwmon/ultra45_env.c
@@ -6,6 +6,7 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/io.h>
#include <linux/hwmon.h>
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index 0d18de424c66..8eac67d769fa 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -27,6 +27,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/hwmon.h>
+#include <linux/hwmon-vid.h>
#include <linux/sysfs.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
@@ -48,8 +49,10 @@ enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME };
struct via_cputemp_data {
struct device *hwmon_dev;
const char *name;
+ u8 vrm;
u32 id;
- u32 msr;
+ u32 msr_temp;
+ u32 msr_vid;
};
/*
@@ -77,13 +80,27 @@ static ssize_t show_temp(struct device *dev,
u32 eax, edx;
int err;
- err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx);
+ err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
if (err)
return -EAGAIN;
return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000);
}
+static ssize_t show_cpu_vid(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct via_cputemp_data *data = dev_get_drvdata(dev);
+ u32 eax, edx;
+ int err;
+
+ err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx);
+ if (err)
+ return -EAGAIN;
+
+ return sprintf(buf, "%d\n", vid_from_reg(~edx & 0x7f, data->vrm));
+}
+
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
SHOW_TEMP);
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
@@ -100,6 +117,9 @@ static const struct attribute_group via_cputemp_group = {
.attrs = via_cputemp_attributes,
};
+/* Optional attributes */
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_cpu_vid, NULL);
+
static int __devinit via_cputemp_probe(struct platform_device *pdev)
{
struct via_cputemp_data *data;
@@ -122,11 +142,12 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)
/* C7 A */
case 0xD:
/* C7 D */
- data->msr = 0x1169;
+ data->msr_temp = 0x1169;
+ data->msr_vid = 0x198;
break;
case 0xF:
/* Nano */
- data->msr = 0x1423;
+ data->msr_temp = 0x1423;
break;
default:
err = -ENODEV;
@@ -134,7 +155,7 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)
}
/* test if we can access the TEMPERATURE MSR */
- err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx);
+ err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
if (err) {
dev_err(&pdev->dev,
"Unable to access TEMPERATURE MSR, giving up\n");
@@ -147,6 +168,15 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)
if (err)
goto exit_free;
+ if (data->msr_vid)
+ data->vrm = vid_which_vrm();
+
+ if (data->vrm) {
+ err = device_create_file(&pdev->dev, &dev_attr_cpu0_vid);
+ if (err)
+ goto exit_remove;
+ }
+
data->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
@@ -158,6 +188,8 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)
return 0;
exit_remove:
+ if (data->vrm)
+ device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
exit_free:
platform_set_drvdata(pdev, NULL);
@@ -171,6 +203,8 @@ static int __devexit via_cputemp_remove(struct platform_device *pdev)
struct via_cputemp_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
+ if (data->vrm)
+ device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
platform_set_drvdata(pdev, NULL);
kfree(data);
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index f2b377c56a3a..93f5fc7d6059 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -1,7 +1,7 @@
/*
w83627ehf - Driver for the hardware monitoring functionality of
the Winbond W83627EHF Super-I/O chip
- Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2005-2011 Jean Delvare <khali@linux-fr.org>
Copyright (C) 2006 Yuan Mu (Winbond),
Rudolf Marek <r.marek@assembler.cz>
David Hubbard <david.c.hubbard@gmail.com>
@@ -39,6 +39,7 @@
0x8860 0xa1
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3
+ w83627uhg 8 2 2 2 0xa230 0xc1 0x5ca3
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
w83667hg-b 9 5 3 4 0xb350 0xc1 0x5ca3
nct6775f 9 4 3 9 0xb470 0xc1 0x5ca3
@@ -61,14 +62,17 @@
#include <linux/io.h>
#include "lm75.h"
-enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b, nct6775,
- nct6776 };
+enum kinds {
+ w83627ehf, w83627dhg, w83627dhg_p, w83627uhg,
+ w83667hg, w83667hg_b, nct6775, nct6776,
+};
/* used to set data->name = w83627ehf_device_names[data->sio_kind] */
static const char * const w83627ehf_device_names[] = {
"w83627ehf",
"w83627dhg",
"w83627dhg",
+ "w83627uhg",
"w83667hg",
"w83667hg",
"nct6775",
@@ -104,6 +108,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
#define SIO_W83627EHG_ID 0x8860
#define SIO_W83627DHG_ID 0xa020
#define SIO_W83627DHG_P_ID 0xb070
+#define SIO_W83627UHG_ID 0xa230
#define SIO_W83667HG_ID 0xa510
#define SIO_W83667HG_B_ID 0xb350
#define SIO_NCT6775_ID 0xb470
@@ -197,6 +202,9 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0, 0x152, 0x252, 0 };
#define W83627EHF_REG_ALARM2 0x45A
#define W83627EHF_REG_ALARM3 0x45B
+#define W83627EHF_REG_CASEOPEN_DET 0x42 /* SMI STATUS #2 */
+#define W83627EHF_REG_CASEOPEN_CLR 0x46 /* SMI MASK #3 */
+
/* SmartFan registers */
#define W83627EHF_REG_FAN_STEPUP_TIME 0x0f
#define W83627EHF_REG_FAN_STEPDOWN_TIME 0x0e
@@ -316,7 +324,7 @@ static const char *const nct6776_temp_label[] = {
#define NUM_REG_TEMP ARRAY_SIZE(NCT6775_REG_TEMP)
-static inline int is_word_sized(u16 reg)
+static int is_word_sized(u16 reg)
{
return ((((reg & 0xff00) == 0x100
|| (reg & 0xff00) == 0x200)
@@ -385,34 +393,23 @@ div_from_reg(u8 reg)
return 1 << reg;
}
-static inline int
-temp_from_reg(u16 reg, s16 regval)
-{
- if (is_word_sized(reg))
- return LM75_TEMP_FROM_REG(regval);
- return regval * 1000;
-}
-
-static inline u16
-temp_to_reg(u16 reg, long temp)
-{
- if (is_word_sized(reg))
- return LM75_TEMP_TO_REG(temp);
- return DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, -127000, 128000), 1000);
-}
-
-/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */
-
-static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 };
+/* Some of the voltage inputs have internal scaling, the tables below
+ * contain 8 (the ADC LSB in mV) * scaling factor * 100 */
+static const u16 scale_in_common[10] = {
+ 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800
+};
+static const u16 scale_in_w83627uhg[9] = {
+ 800, 800, 3328, 3424, 800, 800, 0, 3328, 3400
+};
-static inline long in_from_reg(u8 reg, u8 nr)
+static inline long in_from_reg(u8 reg, u8 nr, const u16 *scale_in)
{
- return reg * scale_in[nr];
+ return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100);
}
-static inline u8 in_to_reg(u32 val, u8 nr)
+static inline u8 in_to_reg(u32 val, u8 nr, const u16 *scale_in)
{
- return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0,
+ return SENSORS_LIMIT(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0,
255);
}
@@ -443,6 +440,7 @@ struct w83627ehf_data {
const u16 *REG_FAN_STOP_TIME;
const u16 *REG_FAN_MAX_OUTPUT;
const u16 *REG_FAN_STEP_OUTPUT;
+ const u16 *scale_in;
unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
@@ -468,6 +466,7 @@ struct w83627ehf_data {
s16 temp_max[9];
s16 temp_max_hyst[9];
u32 alarms;
+ u8 caseopen;
u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
u8 pwm_enable[4]; /* 1->manual
@@ -493,7 +492,8 @@ struct w83627ehf_data {
u8 vrm;
u16 have_temp;
- u8 in6_skip;
+ u8 in6_skip:1;
+ u8 temp3_val_only:1;
};
struct w83627ehf_sio_data {
@@ -556,6 +556,26 @@ static int w83627ehf_write_value(struct w83627ehf_data *data, u16 reg,
return 0;
}
+/* We left-align 8-bit temperature values to make the code simpler */
+static u16 w83627ehf_read_temp(struct w83627ehf_data *data, u16 reg)
+{
+ u16 res;
+
+ res = w83627ehf_read_value(data, reg);
+ if (!is_word_sized(reg))
+ res <<= 8;
+
+ return res;
+}
+
+static int w83627ehf_write_temp(struct w83627ehf_data *data, u16 reg,
+ u16 value)
+{
+ if (!is_word_sized(reg))
+ value >>= 8;
+ return w83627ehf_write_value(data, reg, value);
+}
+
/* This function assumes that the caller holds data->update_lock */
static void nct6775_write_fan_div(struct w83627ehf_data *data, int nr)
{
@@ -770,6 +790,9 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
/* Measured voltages and limits */
for (i = 0; i < data->in_num; i++) {
+ if ((i == 6) && data->in6_skip)
+ continue;
+
data->in[i] = w83627ehf_read_value(data,
W83627EHF_REG_IN(i));
data->in_min[i] = w83627ehf_read_value(data,
@@ -854,15 +877,15 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
for (i = 0; i < NUM_REG_TEMP; i++) {
if (!(data->have_temp & (1 << i)))
continue;
- data->temp[i] = w83627ehf_read_value(data,
+ data->temp[i] = w83627ehf_read_temp(data,
data->reg_temp[i]);
if (data->reg_temp_over[i])
data->temp_max[i]
- = w83627ehf_read_value(data,
+ = w83627ehf_read_temp(data,
data->reg_temp_over[i]);
if (data->reg_temp_hyst[i])
data->temp_max_hyst[i]
- = w83627ehf_read_value(data,
+ = w83627ehf_read_temp(data,
data->reg_temp_hyst[i]);
}
@@ -873,6 +896,9 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
(w83627ehf_read_value(data,
W83627EHF_REG_ALARM3) << 16);
+ data->caseopen = w83627ehf_read_value(data,
+ W83627EHF_REG_CASEOPEN_DET);
+
data->last_updated = jiffies;
data->valid = 1;
}
@@ -893,7 +919,8 @@ show_##reg(struct device *dev, struct device_attribute *attr, \
struct sensor_device_attribute *sensor_attr = \
to_sensor_dev_attr(attr); \
int nr = sensor_attr->index; \
- return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr)); \
+ return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr, \
+ data->scale_in)); \
}
show_in_reg(in)
show_in_reg(in_min)
@@ -914,7 +941,7 @@ store_in_##reg(struct device *dev, struct device_attribute *attr, \
if (err < 0) \
return err; \
mutex_lock(&data->update_lock); \
- data->in_##reg[nr] = in_to_reg(val, nr); \
+ data->in_##reg[nr] = in_to_reg(val, nr, data->scale_in); \
w83627ehf_write_value(data, W83627EHF_REG_IN_##REG(nr), \
data->in_##reg[nr]); \
mutex_unlock(&data->update_lock); \
@@ -1155,8 +1182,7 @@ show_##reg(struct device *dev, struct device_attribute *attr, \
struct sensor_device_attribute *sensor_attr = \
to_sensor_dev_attr(attr); \
int nr = sensor_attr->index; \
- return sprintf(buf, "%d\n", \
- temp_from_reg(data->addr[nr], data->reg[nr])); \
+ return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->reg[nr])); \
}
show_temp_reg(reg_temp, temp);
show_temp_reg(reg_temp_over, temp_max);
@@ -1177,9 +1203,8 @@ store_##reg(struct device *dev, struct device_attribute *attr, \
if (err < 0) \
return err; \
mutex_lock(&data->update_lock); \
- data->reg[nr] = temp_to_reg(data->addr[nr], val); \
- w83627ehf_write_value(data, data->addr[nr], \
- data->reg[nr]); \
+ data->reg[nr] = LM75_TEMP_TO_REG(val); \
+ w83627ehf_write_temp(data, data->addr[nr], data->reg[nr]); \
mutex_unlock(&data->update_lock); \
return count; \
}
@@ -1605,25 +1630,28 @@ static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
store_fan_step_output, 3),
};
+static struct sensor_device_attribute sda_sf3_arrays_fan3[] = {
+ SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+ store_fan_stop_time, 2),
+ SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+ store_fan_start_output, 2),
+ SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
+ store_fan_stop_output, 2),
+};
+
static struct sensor_device_attribute sda_sf3_arrays[] = {
SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
store_fan_stop_time, 0),
SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
store_fan_stop_time, 1),
- SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
- store_fan_stop_time, 2),
SENSOR_ATTR(pwm1_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
store_fan_start_output, 0),
SENSOR_ATTR(pwm2_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
store_fan_start_output, 1),
- SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
- store_fan_start_output, 2),
SENSOR_ATTR(pwm1_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
store_fan_stop_output, 0),
SENSOR_ATTR(pwm2_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
store_fan_stop_output, 1),
- SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
- store_fan_stop_output, 2),
};
@@ -1654,6 +1682,48 @@ show_vid(struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+
+/* Case open detection */
+
+static ssize_t
+show_caseopen(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83627ehf_data *data = w83627ehf_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ !!(data->caseopen & to_sensor_dev_attr_2(attr)->index));
+}
+
+static ssize_t
+clear_caseopen(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w83627ehf_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ u16 reg, mask;
+
+ if (strict_strtoul(buf, 10, &val) || val != 0)
+ return -EINVAL;
+
+ mask = to_sensor_dev_attr_2(attr)->nr;
+
+ mutex_lock(&data->update_lock);
+ reg = w83627ehf_read_value(data, W83627EHF_REG_CASEOPEN_CLR);
+ w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg | mask);
+ w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg & ~mask);
+ data->valid = 0; /* Force cache refresh */
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static struct sensor_device_attribute_2 sda_caseopen[] = {
+ SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_caseopen,
+ clear_caseopen, 0x80, 0x10),
+ SENSOR_ATTR_2(intrusion1_alarm, S_IWUSR | S_IRUGO, show_caseopen,
+ clear_caseopen, 0x40, 0x40),
+};
+
/*
* Driver and device management
*/
@@ -1674,6 +1744,8 @@ static void w83627ehf_device_remove_files(struct device *dev)
data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff)
device_remove_file(dev, &attr->dev_attr);
}
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++)
+ device_remove_file(dev, &sda_sf3_arrays_fan3[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
for (i = 0; i < data->in_num; i++) {
@@ -1702,6 +1774,8 @@ static void w83627ehf_device_remove_files(struct device *dev)
continue;
device_remove_file(dev, &sda_temp_input[i].dev_attr);
device_remove_file(dev, &sda_temp_label[i].dev_attr);
+ if (i == 2 && data->temp3_val_only)
+ continue;
device_remove_file(dev, &sda_temp_max[i].dev_attr);
device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr);
if (i > 2)
@@ -1710,12 +1784,16 @@ static void w83627ehf_device_remove_files(struct device *dev)
device_remove_file(dev, &sda_temp_type[i].dev_attr);
}
+ device_remove_file(dev, &sda_caseopen[0].dev_attr);
+ device_remove_file(dev, &sda_caseopen[1].dev_attr);
+
device_remove_file(dev, &dev_attr_name);
device_remove_file(dev, &dev_attr_cpu0_vid);
}
/* Get the monitoring functions started */
-static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data)
+static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data,
+ enum kinds kind)
{
int i;
u8 tmp, diode;
@@ -1746,10 +1824,29 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data)
w83627ehf_write_value(data, W83627EHF_REG_VBAT, tmp | 0x01);
/* Get thermal sensor types */
- diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE);
+ switch (kind) {
+ case w83627ehf:
+ diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE);
+ break;
+ case w83627uhg:
+ diode = 0x00;
+ break;
+ default:
+ diode = 0x70;
+ }
for (i = 0; i < 3; i++) {
- if ((tmp & (0x02 << i)))
- data->temp_type[i] = (diode & (0x10 << i)) ? 1 : 2;
+ const char *label = NULL;
+
+ if (data->temp_label)
+ label = data->temp_label[data->temp_src[i]];
+
+ /* Digital source overrides analog type */
+ if (label && strncmp(label, "PECI", 4) == 0)
+ data->temp_type[i] = 6;
+ else if (label && strncmp(label, "AMD", 3) == 0)
+ data->temp_type[i] = 5;
+ else if ((tmp & (0x02 << i)))
+ data->temp_type[i] = (diode & (0x10 << i)) ? 1 : 3;
else
data->temp_type[i] = 4; /* thermistor */
}
@@ -1781,13 +1878,98 @@ static void w82627ehf_swap_tempreg(struct w83627ehf_data *data,
data->reg_temp_config[r2] = tmp;
}
+static void __devinit
+w83627ehf_set_temp_reg_ehf(struct w83627ehf_data *data, int n_temp)
+{
+ int i;
+
+ for (i = 0; i < n_temp; i++) {
+ data->reg_temp[i] = W83627EHF_REG_TEMP[i];
+ data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i];
+ data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i];
+ data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i];
+ }
+}
+
+static void __devinit
+w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data,
+ struct w83627ehf_data *data)
+{
+ int fan3pin, fan4pin, fan4min, fan5pin, regval;
+
+ /* The W83627UHG is simple, only two fan inputs, no config */
+ if (sio_data->kind == w83627uhg) {
+ data->has_fan = 0x03; /* fan1 and fan2 */
+ data->has_fan_min = 0x03;
+ return;
+ }
+
+ superio_enter(sio_data->sioreg);
+
+ /* fan4 and fan5 share some pins with the GPIO and serial flash */
+ if (sio_data->kind == nct6775) {
+ /* On NCT6775, fan4 shares pins with the fdc interface */
+ fan3pin = 1;
+ fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80);
+ fan4min = 0;
+ fan5pin = 0;
+ } else if (sio_data->kind == nct6776) {
+ fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40);
+ fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01);
+ fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02);
+ fan4min = fan4pin;
+ } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
+ fan3pin = 1;
+ fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40;
+ fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20;
+ fan4min = fan4pin;
+ } else {
+ fan3pin = 1;
+ fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06);
+ fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02);
+ fan4min = fan4pin;
+ }
+
+ superio_exit(sio_data->sioreg);
+
+ data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */
+ data->has_fan |= (fan3pin << 2);
+ data->has_fan_min |= (fan3pin << 2);
+
+ if (sio_data->kind == nct6775 || sio_data->kind == nct6776) {
+ /*
+ * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1
+ * register
+ */
+ data->has_fan |= (fan4pin << 3) | (fan5pin << 4);
+ data->has_fan_min |= (fan4min << 3) | (fan5pin << 4);
+ } else {
+ /*
+ * It looks like fan4 and fan5 pins can be alternatively used
+ * as fan on/off switches, but fan5 control is write only :/
+ * We assume that if the serial interface is disabled, designers
+ * connected fan5 as input unless they are emitting log 1, which
+ * is not the default.
+ */
+ regval = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1);
+ if ((regval & (1 << 2)) && fan4pin) {
+ data->has_fan |= (1 << 3);
+ data->has_fan_min |= (1 << 3);
+ }
+ if (!(regval & (1 << 1)) && fan5pin) {
+ data->has_fan |= (1 << 4);
+ data->has_fan_min |= (1 << 4);
+ }
+ }
+}
+
static int __devinit w83627ehf_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct w83627ehf_sio_data *sio_data = dev->platform_data;
struct w83627ehf_data *data;
struct resource *res;
- u8 fan3pin, fan4pin, fan4min, fan5pin, en_vrm10;
+ u8 en_vrm10;
int i, err = 0;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
@@ -1813,23 +1995,24 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
/* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */
data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9;
- /* 667HG, NCT6775F, and NCT6776F have 3 pwms */
- data->pwm_num = (sio_data->kind == w83667hg
- || sio_data->kind == w83667hg_b
- || sio_data->kind == nct6775
- || sio_data->kind == nct6776) ? 3 : 4;
+ /* 667HG, NCT6775F, and NCT6776F have 3 pwms, and 627UHG has only 2 */
+ switch (sio_data->kind) {
+ default:
+ data->pwm_num = 4;
+ break;
+ case w83667hg:
+ case w83667hg_b:
+ case nct6775:
+ case nct6776:
+ data->pwm_num = 3;
+ break;
+ case w83627uhg:
+ data->pwm_num = 2;
+ break;
+ }
+ /* Default to 3 temperature inputs, code below will adjust as needed */
data->have_temp = 0x07;
- /* Check temp3 configuration bit for 667HG */
- if (sio_data->kind == w83667hg) {
- u8 reg;
-
- reg = w83627ehf_read_value(data, W83627EHF_REG_TEMP_CONFIG[2]);
- if (reg & 0x01)
- data->have_temp &= ~(1 << 2);
- else
- data->in6_skip = 1; /* either temp3 or in6 */
- }
/* Deal with temperature register setup first. */
if (sio_data->kind == nct6775 || sio_data->kind == nct6776) {
@@ -1906,16 +2089,12 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
} else if (sio_data->kind == w83667hg_b) {
u8 reg;
+ w83627ehf_set_temp_reg_ehf(data, 4);
+
/*
* Temperature sources are selected with bank 0, registers 0x49
* and 0x4a.
*/
- for (i = 0; i < ARRAY_SIZE(W83627EHF_REG_TEMP); i++) {
- data->reg_temp[i] = W83627EHF_REG_TEMP[i];
- data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i];
- data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i];
- data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i];
- }
reg = w83627ehf_read_value(data, 0x4a);
data->temp_src[0] = reg >> 5;
reg = w83627ehf_read_value(data, 0x49);
@@ -1949,13 +2128,60 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
data->in6_skip = 1;
data->temp_label = w83667hg_b_temp_label;
+ } else if (sio_data->kind == w83627uhg) {
+ u8 reg;
+
+ w83627ehf_set_temp_reg_ehf(data, 3);
+
+ /*
+ * Temperature sources for temp1 and temp2 are selected with
+ * bank 0, registers 0x49 and 0x4a.
+ */
+ data->temp_src[0] = 0; /* SYSTIN */
+ reg = w83627ehf_read_value(data, 0x49) & 0x07;
+ /* Adjust to have the same mapping as other source registers */
+ if (reg == 0)
+ data->temp_src[1]++;
+ else if (reg >= 2 && reg <= 5)
+ data->temp_src[1] += 2;
+ else /* should never happen */
+ data->have_temp &= ~(1 << 1);
+ reg = w83627ehf_read_value(data, 0x4a);
+ data->temp_src[2] = reg >> 5;
+
+ /*
+ * Skip temp3 if source is invalid or the same as temp1
+ * or temp2.
+ */
+ if (data->temp_src[2] == 2 || data->temp_src[2] == 3 ||
+ data->temp_src[2] == data->temp_src[0] ||
+ ((data->have_temp & (1 << 1)) &&
+ data->temp_src[2] == data->temp_src[1]))
+ data->have_temp &= ~(1 << 2);
+ else
+ data->temp3_val_only = 1; /* No limit regs */
+
+ data->in6_skip = 1; /* No VIN3 */
+
+ data->temp_label = w83667hg_b_temp_label;
} else {
+ w83627ehf_set_temp_reg_ehf(data, 3);
+
/* Temperature sources are fixed */
- for (i = 0; i < 3; i++) {
- data->reg_temp[i] = W83627EHF_REG_TEMP[i];
- data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i];
- data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i];
- data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i];
+
+ if (sio_data->kind == w83667hg) {
+ u8 reg;
+
+ /*
+ * Chip supports either AUXTIN or VIN3. Try to find
+ * out which one.
+ */
+ reg = w83627ehf_read_value(data,
+ W83627EHF_REG_TEMP_CONFIG[2]);
+ if (reg & 0x01)
+ data->have_temp &= ~(1 << 2);
+ else
+ data->in6_skip = 1;
}
}
@@ -2015,8 +2241,14 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
W83627EHF_REG_FAN_STEP_OUTPUT_COMMON;
}
+ /* Setup input voltage scaling factors */
+ if (sio_data->kind == w83627uhg)
+ data->scale_in = scale_in_w83627uhg;
+ else
+ data->scale_in = scale_in_common;
+
/* Initialize the chip */
- w83627ehf_init_device(data);
+ w83627ehf_init_device(data, sio_data->kind);
data->vrm = vid_which_vrm();
superio_enter(sio_data->sioreg);
@@ -2031,7 +2263,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
err = device_create_file(dev, &dev_attr_cpu0_vid);
if (err)
goto exit_release;
- } else {
+ } else if (sio_data->kind != w83627uhg) {
superio_select(sio_data->sioreg, W83627EHF_LD_HWM);
if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) {
/* Set VID input sensibility if needed. In theory the
@@ -2072,30 +2304,6 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
}
}
- /* fan4 and fan5 share some pins with the GPIO and serial flash */
- if (sio_data->kind == nct6775) {
- /* On NCT6775, fan4 shares pins with the fdc interface */
- fan3pin = 1;
- fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80);
- fan4min = 0;
- fan5pin = 0;
- } else if (sio_data->kind == nct6776) {
- fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40);
- fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01);
- fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02);
- fan4min = fan4pin;
- } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
- fan3pin = 1;
- fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40;
- fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20;
- fan4min = fan4pin;
- } else {
- fan3pin = 1;
- fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06);
- fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02);
- fan4min = fan4pin;
- }
-
if (fan_debounce &&
(sio_data->kind == nct6775 || sio_data->kind == nct6776)) {
u8 tmp;
@@ -2113,34 +2321,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
superio_exit(sio_data->sioreg);
- /* It looks like fan4 and fan5 pins can be alternatively used
- as fan on/off switches, but fan5 control is write only :/
- We assume that if the serial interface is disabled, designers
- connected fan5 as input unless they are emitting log 1, which
- is not the default. */
-
- data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */
-
- data->has_fan |= (fan3pin << 2);
- data->has_fan_min |= (fan3pin << 2);
-
- /*
- * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1 register
- */
- if (sio_data->kind == nct6775 || sio_data->kind == nct6776) {
- data->has_fan |= (fan4pin << 3) | (fan5pin << 4);
- data->has_fan_min |= (fan4min << 3) | (fan5pin << 4);
- } else {
- i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1);
- if ((i & (1 << 2)) && fan4pin) {
- data->has_fan |= (1 << 3);
- data->has_fan_min |= (1 << 3);
- }
- if (!(i & (1 << 1)) && fan5pin) {
- data->has_fan |= (1 << 4);
- data->has_fan_min |= (1 << 4);
- }
- }
+ w83627ehf_check_fan_inputs(sio_data, data);
/* Read fan clock dividers immediately */
w83627ehf_update_fan_div_common(dev, data);
@@ -2172,7 +2353,14 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
goto exit_remove;
}
}
- /* if fan4 is enabled create the sf3 files for it */
+ /* if fan3 and fan4 are enabled create the sf3 files for them */
+ if ((data->has_fan & (1 << 2)) && data->pwm_num >= 3)
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) {
+ err = device_create_file(dev,
+ &sda_sf3_arrays_fan3[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4)
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
err = device_create_file(dev,
@@ -2240,6 +2428,8 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
if (err)
goto exit_remove;
}
+ if (i == 2 && data->temp3_val_only)
+ continue;
if (data->reg_temp_over[i]) {
err = device_create_file(dev,
&sda_temp_max[i].dev_attr);
@@ -2261,6 +2451,16 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
goto exit_remove;
}
+ err = device_create_file(dev, &sda_caseopen[0].dev_attr);
+ if (err)
+ goto exit_remove;
+
+ if (sio_data->kind == nct6776) {
+ err = device_create_file(dev, &sda_caseopen[1].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+
err = device_create_file(dev, &dev_attr_name);
if (err)
goto exit_remove;
@@ -2313,6 +2513,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
static const char __initdata sio_name_W83627EHG[] = "W83627EHG";
static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P";
+ static const char __initdata sio_name_W83627UHG[] = "W83627UHG";
static const char __initdata sio_name_W83667HG[] = "W83667HG";
static const char __initdata sio_name_W83667HG_B[] = "W83667HG-B";
static const char __initdata sio_name_NCT6775[] = "NCT6775F";
@@ -2345,6 +2546,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
sio_data->kind = w83627dhg_p;
sio_name = sio_name_W83627DHG_P;
break;
+ case SIO_W83627UHG_ID:
+ sio_data->kind = w83627uhg;
+ sio_name = sio_name_W83627UHG;
+ break;
case SIO_W83667HG_ID:
sio_data->kind = w83667hg;
sio_name = sio_name_W83667HG;
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index eed43a008be1..65b685e2c7b7 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -1245,17 +1245,17 @@ w83781d_read_value_i2c(struct w83781d_data *data, u16 reg)
/* convert from ISA to LM75 I2C addresses */
switch (reg & 0xff) {
case 0x50: /* TEMP */
- res = swab16(i2c_smbus_read_word_data(cl, 0));
+ res = i2c_smbus_read_word_swapped(cl, 0);
break;
case 0x52: /* CONFIG */
res = i2c_smbus_read_byte_data(cl, 1);
break;
case 0x53: /* HYST */
- res = swab16(i2c_smbus_read_word_data(cl, 2));
+ res = i2c_smbus_read_word_swapped(cl, 2);
break;
case 0x55: /* OVER */
default:
- res = swab16(i2c_smbus_read_word_data(cl, 3));
+ res = i2c_smbus_read_word_swapped(cl, 3);
break;
}
}
@@ -1289,10 +1289,10 @@ w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value)
i2c_smbus_write_byte_data(cl, 1, value & 0xff);
break;
case 0x53: /* HYST */
- i2c_smbus_write_word_data(cl, 2, swab16(value));
+ i2c_smbus_write_word_swapped(cl, 2, value);
break;
case 0x55: /* OVER */
- i2c_smbus_write_word_data(cl, 3, swab16(value));
+ i2c_smbus_write_word_swapped(cl, 3, value);
break;
}
}
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index 17cf1ab95521..8c2844e5691c 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -329,8 +329,8 @@ static int w83791d_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int w83791d_remove(struct i2c_client *client);
-static int w83791d_read(struct i2c_client *client, u8 register);
-static int w83791d_write(struct i2c_client *client, u8 register, u8 value);
+static int w83791d_read(struct i2c_client *client, u8 reg);
+static int w83791d_write(struct i2c_client *client, u8 reg, u8 value);
static struct w83791d_data *w83791d_update_device(struct device *dev);
#ifdef DEBUG
diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
index 1f29bab6b3e5..c7c3128393d1 100644
--- a/drivers/hwspinlock/Kconfig
+++ b/drivers/hwspinlock/Kconfig
@@ -2,22 +2,31 @@
# Generic HWSPINLOCK framework
#
+# HWSPINLOCK always gets selected by whoever wants it.
config HWSPINLOCK
- tristate "Generic Hardware Spinlock framework"
- depends on ARCH_OMAP4
- help
- Say y here to support the generic hardware spinlock framework.
- You only need to enable this if you have hardware spinlock module
- on your system (usually only relevant if your system has remote slave
- coprocessors).
+ tristate
- If unsure, say N.
+menu "Hardware Spinlock drivers"
config HWSPINLOCK_OMAP
tristate "OMAP Hardware Spinlock device"
- depends on HWSPINLOCK && ARCH_OMAP4
+ depends on ARCH_OMAP4
+ select HWSPINLOCK
help
Say y here to support the OMAP Hardware Spinlock device (firstly
introduced in OMAP4).
If unsure, say N.
+
+config HSEM_U8500
+ tristate "STE Hardware Semaphore functionality"
+ depends on ARCH_U8500
+ select HWSPINLOCK
+ help
+ Say y here to support the STE Hardware Semaphore functionality, which
+ provides a synchronisation mechanism for the various processor on the
+ SoC.
+
+ If unsure, say N.
+
+endmenu
diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile
index 5729a3f7ed3d..93eb64b66486 100644
--- a/drivers/hwspinlock/Makefile
+++ b/drivers/hwspinlock/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o
obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o
+obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o
diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c
index 43a62714b4fb..61c9cf15fa52 100644
--- a/drivers/hwspinlock/hwspinlock_core.c
+++ b/drivers/hwspinlock/hwspinlock_core.c
@@ -26,6 +26,7 @@
#include <linux/radix-tree.h>
#include <linux/hwspinlock.h>
#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
#include "hwspinlock_internal.h"
@@ -52,10 +53,12 @@
static RADIX_TREE(hwspinlock_tree, GFP_KERNEL);
/*
- * Synchronization of access to the tree is achieved using this spinlock,
+ * Synchronization of access to the tree is achieved using this mutex,
* as the radix-tree API requires that users provide all synchronisation.
+ * A mutex is needed because we're using non-atomic radix tree allocations.
*/
-static DEFINE_SPINLOCK(hwspinlock_tree_lock);
+static DEFINE_MUTEX(hwspinlock_tree_lock);
+
/**
* __hwspin_trylock() - attempt to lock a specific hwspinlock
@@ -114,7 +117,7 @@ int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
return -EBUSY;
/* try to take the hwspinlock device */
- ret = hwlock->ops->trylock(hwlock);
+ ret = hwlock->bank->ops->trylock(hwlock);
/* if hwlock is already taken, undo spin_trylock_* and exit */
if (!ret) {
@@ -196,8 +199,8 @@ int __hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to,
* Allow platform-specific relax handlers to prevent
* hogging the interconnect (no sleeping, though)
*/
- if (hwlock->ops->relax)
- hwlock->ops->relax(hwlock);
+ if (hwlock->bank->ops->relax)
+ hwlock->bank->ops->relax(hwlock);
}
return ret;
@@ -242,7 +245,7 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
*/
mb();
- hwlock->ops->unlock(hwlock);
+ hwlock->bank->ops->unlock(hwlock);
/* Undo the spin_trylock{_irq, _irqsave} called while locking */
if (mode == HWLOCK_IRQSTATE)
@@ -254,68 +257,37 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
}
EXPORT_SYMBOL_GPL(__hwspin_unlock);
-/**
- * hwspin_lock_register() - register a new hw spinlock
- * @hwlock: hwspinlock to register.
- *
- * This function should be called from the underlying platform-specific
- * implementation, to register a new hwspinlock instance.
- *
- * Can be called from an atomic context (will not sleep) but not from
- * within interrupt context.
- *
- * Returns 0 on success, or an appropriate error code on failure
- */
-int hwspin_lock_register(struct hwspinlock *hwlock)
+static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)
{
struct hwspinlock *tmp;
int ret;
- if (!hwlock || !hwlock->ops ||
- !hwlock->ops->trylock || !hwlock->ops->unlock) {
- pr_err("invalid parameters\n");
- return -EINVAL;
- }
-
- spin_lock_init(&hwlock->lock);
-
- spin_lock(&hwspinlock_tree_lock);
+ mutex_lock(&hwspinlock_tree_lock);
- ret = radix_tree_insert(&hwspinlock_tree, hwlock->id, hwlock);
- if (ret)
+ ret = radix_tree_insert(&hwspinlock_tree, id, hwlock);
+ if (ret) {
+ if (ret == -EEXIST)
+ pr_err("hwspinlock id %d already exists!\n", id);
goto out;
+ }
/* mark this hwspinlock as available */
- tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id,
- HWSPINLOCK_UNUSED);
+ tmp = radix_tree_tag_set(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);
/* self-sanity check which should never fail */
WARN_ON(tmp != hwlock);
out:
- spin_unlock(&hwspinlock_tree_lock);
- return ret;
+ mutex_unlock(&hwspinlock_tree_lock);
+ return 0;
}
-EXPORT_SYMBOL_GPL(hwspin_lock_register);
-/**
- * hwspin_lock_unregister() - unregister an hw spinlock
- * @id: index of the specific hwspinlock to unregister
- *
- * This function should be called from the underlying platform-specific
- * implementation, to unregister an existing (and unused) hwspinlock.
- *
- * Can be called from an atomic context (will not sleep) but not from
- * within interrupt context.
- *
- * Returns the address of hwspinlock @id on success, or NULL on failure
- */
-struct hwspinlock *hwspin_lock_unregister(unsigned int id)
+static struct hwspinlock *hwspin_lock_unregister_single(unsigned int id)
{
struct hwspinlock *hwlock = NULL;
int ret;
- spin_lock(&hwspinlock_tree_lock);
+ mutex_lock(&hwspinlock_tree_lock);
/* make sure the hwspinlock is not in use (tag is set) */
ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);
@@ -331,9 +303,91 @@ struct hwspinlock *hwspin_lock_unregister(unsigned int id)
}
out:
- spin_unlock(&hwspinlock_tree_lock);
+ mutex_unlock(&hwspinlock_tree_lock);
return hwlock;
}
+
+/**
+ * hwspin_lock_register() - register a new hw spinlock device
+ * @bank: the hwspinlock device, which usually provides numerous hw locks
+ * @dev: the backing device
+ * @ops: hwspinlock handlers for this device
+ * @base_id: id of the first hardware spinlock in this bank
+ * @num_locks: number of hwspinlocks provided by this device
+ *
+ * This function should be called from the underlying platform-specific
+ * implementation, to register a new hwspinlock device instance.
+ *
+ * Should be called from a process context (might sleep)
+ *
+ * Returns 0 on success, or an appropriate error code on failure
+ */
+int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev,
+ const struct hwspinlock_ops *ops, int base_id, int num_locks)
+{
+ struct hwspinlock *hwlock;
+ int ret = 0, i;
+
+ if (!bank || !ops || !dev || !num_locks || !ops->trylock ||
+ !ops->unlock) {
+ pr_err("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ bank->dev = dev;
+ bank->ops = ops;
+ bank->base_id = base_id;
+ bank->num_locks = num_locks;
+
+ for (i = 0; i < num_locks; i++) {
+ hwlock = &bank->lock[i];
+
+ spin_lock_init(&hwlock->lock);
+ hwlock->bank = bank;
+
+ ret = hwspin_lock_register_single(hwlock, i);
+ if (ret)
+ goto reg_failed;
+ }
+
+ return 0;
+
+reg_failed:
+ while (--i >= 0)
+ hwspin_lock_unregister_single(i);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(hwspin_lock_register);
+
+/**
+ * hwspin_lock_unregister() - unregister an hw spinlock device
+ * @bank: the hwspinlock device, which usually provides numerous hw locks
+ *
+ * This function should be called from the underlying platform-specific
+ * implementation, to unregister an existing (and unused) hwspinlock.
+ *
+ * Should be called from a process context (might sleep)
+ *
+ * Returns 0 on success, or an appropriate error code on failure
+ */
+int hwspin_lock_unregister(struct hwspinlock_device *bank)
+{
+ struct hwspinlock *hwlock, *tmp;
+ int i;
+
+ for (i = 0; i < bank->num_locks; i++) {
+ hwlock = &bank->lock[i];
+
+ tmp = hwspin_lock_unregister_single(bank->base_id + i);
+ if (!tmp)
+ return -EBUSY;
+
+ /* self-sanity check that should never fail */
+ WARN_ON(tmp != hwlock);
+ }
+
+ return 0;
+}
EXPORT_SYMBOL_GPL(hwspin_lock_unregister);
/**
@@ -348,24 +402,25 @@ EXPORT_SYMBOL_GPL(hwspin_lock_unregister);
*/
static int __hwspin_lock_request(struct hwspinlock *hwlock)
{
+ struct device *dev = hwlock->bank->dev;
struct hwspinlock *tmp;
int ret;
/* prevent underlying implementation from being removed */
- if (!try_module_get(hwlock->owner)) {
- dev_err(hwlock->dev, "%s: can't get owner\n", __func__);
+ if (!try_module_get(dev->driver->owner)) {
+ dev_err(dev, "%s: can't get owner\n", __func__);
return -EINVAL;
}
/* notify PM core that power is now needed */
- ret = pm_runtime_get_sync(hwlock->dev);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- dev_err(hwlock->dev, "%s: can't power on device\n", __func__);
+ dev_err(dev, "%s: can't power on device\n", __func__);
return ret;
}
/* mark hwspinlock as used, should not fail */
- tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock->id,
+ tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock_to_id(hwlock),
HWSPINLOCK_UNUSED);
/* self-sanity check that should never fail */
@@ -387,7 +442,7 @@ int hwspin_lock_get_id(struct hwspinlock *hwlock)
return -EINVAL;
}
- return hwlock->id;
+ return hwlock_to_id(hwlock);
}
EXPORT_SYMBOL_GPL(hwspin_lock_get_id);
@@ -400,9 +455,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_get_id);
* to the remote core before it can be used for synchronization (to get the
* id of a given hwlock, use hwspin_lock_get_id()).
*
- * Can be called from an atomic context (will not sleep) but not from
- * within interrupt context (simply because there is no use case for
- * that yet).
+ * Should be called from a process context (might sleep)
*
* Returns the address of the assigned hwspinlock, or NULL on error
*/
@@ -411,7 +464,7 @@ struct hwspinlock *hwspin_lock_request(void)
struct hwspinlock *hwlock;
int ret;
- spin_lock(&hwspinlock_tree_lock);
+ mutex_lock(&hwspinlock_tree_lock);
/* look for an unused lock */
ret = radix_tree_gang_lookup_tag(&hwspinlock_tree, (void **)&hwlock,
@@ -431,7 +484,7 @@ struct hwspinlock *hwspin_lock_request(void)
hwlock = NULL;
out:
- spin_unlock(&hwspinlock_tree_lock);
+ mutex_unlock(&hwspinlock_tree_lock);
return hwlock;
}
EXPORT_SYMBOL_GPL(hwspin_lock_request);
@@ -445,9 +498,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request);
* Usually early board code will be calling this function in order to
* reserve specific hwspinlock ids for predefined purposes.
*
- * Can be called from an atomic context (will not sleep) but not from
- * within interrupt context (simply because there is no use case for
- * that yet).
+ * Should be called from a process context (might sleep)
*
* Returns the address of the assigned hwspinlock, or NULL on error
*/
@@ -456,7 +507,7 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id)
struct hwspinlock *hwlock;
int ret;
- spin_lock(&hwspinlock_tree_lock);
+ mutex_lock(&hwspinlock_tree_lock);
/* make sure this hwspinlock exists */
hwlock = radix_tree_lookup(&hwspinlock_tree, id);
@@ -466,7 +517,7 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id)
}
/* sanity check (this shouldn't happen) */
- WARN_ON(hwlock->id != id);
+ WARN_ON(hwlock_to_id(hwlock) != id);
/* make sure this hwspinlock is unused */
ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);
@@ -482,7 +533,7 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id)
hwlock = NULL;
out:
- spin_unlock(&hwspinlock_tree_lock);
+ mutex_unlock(&hwspinlock_tree_lock);
return hwlock;
}
EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);
@@ -495,14 +546,13 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);
* Should only be called with an @hwlock that was retrieved from
* an earlier call to omap_hwspin_lock_request{_specific}.
*
- * Can be called from an atomic context (will not sleep) but not from
- * within interrupt context (simply because there is no use case for
- * that yet).
+ * Should be called from a process context (might sleep)
*
* Returns 0 on success, or an appropriate error code on failure
*/
int hwspin_lock_free(struct hwspinlock *hwlock)
{
+ struct device *dev = hwlock->bank->dev;
struct hwspinlock *tmp;
int ret;
@@ -511,34 +561,34 @@ int hwspin_lock_free(struct hwspinlock *hwlock)
return -EINVAL;
}
- spin_lock(&hwspinlock_tree_lock);
+ mutex_lock(&hwspinlock_tree_lock);
/* make sure the hwspinlock is used */
- ret = radix_tree_tag_get(&hwspinlock_tree, hwlock->id,
+ ret = radix_tree_tag_get(&hwspinlock_tree, hwlock_to_id(hwlock),
HWSPINLOCK_UNUSED);
if (ret == 1) {
- dev_err(hwlock->dev, "%s: hwlock is already free\n", __func__);
+ dev_err(dev, "%s: hwlock is already free\n", __func__);
dump_stack();
ret = -EINVAL;
goto out;
}
/* notify the underlying device that power is not needed */
- ret = pm_runtime_put(hwlock->dev);
+ ret = pm_runtime_put(dev);
if (ret < 0)
goto out;
/* mark this hwspinlock as available */
- tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id,
+ tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock_to_id(hwlock),
HWSPINLOCK_UNUSED);
/* sanity check (this shouldn't happen) */
WARN_ON(tmp != hwlock);
- module_put(hwlock->owner);
+ module_put(dev->driver->owner);
out:
- spin_unlock(&hwspinlock_tree_lock);
+ mutex_unlock(&hwspinlock_tree_lock);
return ret;
}
EXPORT_SYMBOL_GPL(hwspin_lock_free);
diff --git a/drivers/hwspinlock/hwspinlock_internal.h b/drivers/hwspinlock/hwspinlock_internal.h
index 69935e6b93e5..d26f78b8f214 100644
--- a/drivers/hwspinlock/hwspinlock_internal.h
+++ b/drivers/hwspinlock/hwspinlock_internal.h
@@ -21,6 +21,8 @@
#include <linux/spinlock.h>
#include <linux/device.h>
+struct hwspinlock_device;
+
/**
* struct hwspinlock_ops - platform-specific hwspinlock handlers
*
@@ -39,23 +41,37 @@ struct hwspinlock_ops {
/**
* struct hwspinlock - this struct represents a single hwspinlock instance
- *
- * @dev: underlying device, will be used to invoke runtime PM api
- * @ops: platform-specific hwspinlock handlers
- * @id: a global, unique, system-wide, index of the lock.
+ * @bank: the hwspinlock_device structure which owns this lock
* @lock: initialized and used by hwspinlock core
- * @owner: underlying implementation module, used to maintain module ref count
- *
- * Note: currently simplicity was opted for, but later we can squeeze some
- * memory bytes by grouping the dev, ops and owner members in a single
- * per-platform struct, and have all hwspinlocks point at it.
+ * @priv: private data, owned by the underlying platform-specific hwspinlock drv
*/
struct hwspinlock {
+ struct hwspinlock_device *bank;
+ spinlock_t lock;
+ void *priv;
+};
+
+/**
+ * struct hwspinlock_device - a device which usually spans numerous hwspinlocks
+ * @dev: underlying device, will be used to invoke runtime PM api
+ * @ops: platform-specific hwspinlock handlers
+ * @base_id: id index of the first lock in this device
+ * @num_locks: number of locks in this device
+ * @lock: dynamically allocated array of 'struct hwspinlock'
+ */
+struct hwspinlock_device {
struct device *dev;
const struct hwspinlock_ops *ops;
- int id;
- spinlock_t lock;
- struct module *owner;
+ int base_id;
+ int num_locks;
+ struct hwspinlock lock[0];
};
+static inline int hwlock_to_id(struct hwspinlock *hwlock)
+{
+ int local_id = hwlock - &hwlock->bank->lock[0];
+
+ return hwlock->bank->base_id + local_id;
+}
+
#endif /* __HWSPINLOCK_HWSPINLOCK_H */
diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c
index a8f02734c026..887d34effb3a 100644
--- a/drivers/hwspinlock/omap_hwspinlock.c
+++ b/drivers/hwspinlock/omap_hwspinlock.c
@@ -41,33 +41,20 @@
#define SPINLOCK_NOTTAKEN (0) /* free */
#define SPINLOCK_TAKEN (1) /* locked */
-#define to_omap_hwspinlock(lock) \
- container_of(lock, struct omap_hwspinlock, lock)
-
-struct omap_hwspinlock {
- struct hwspinlock lock;
- void __iomem *addr;
-};
-
-struct omap_hwspinlock_state {
- int num_locks; /* Total number of locks in system */
- void __iomem *io_base; /* Mapped base address */
-};
-
static int omap_hwspinlock_trylock(struct hwspinlock *lock)
{
- struct omap_hwspinlock *omap_lock = to_omap_hwspinlock(lock);
+ void __iomem *lock_addr = lock->priv;
/* attempt to acquire the lock by reading its value */
- return (SPINLOCK_NOTTAKEN == readl(omap_lock->addr));
+ return (SPINLOCK_NOTTAKEN == readl(lock_addr));
}
static void omap_hwspinlock_unlock(struct hwspinlock *lock)
{
- struct omap_hwspinlock *omap_lock = to_omap_hwspinlock(lock);
+ void __iomem *lock_addr = lock->priv;
/* release the lock by writing 0 to it */
- writel(SPINLOCK_NOTTAKEN, omap_lock->addr);
+ writel(SPINLOCK_NOTTAKEN, lock_addr);
}
/*
@@ -93,26 +80,23 @@ static const struct hwspinlock_ops omap_hwspinlock_ops = {
static int __devinit omap_hwspinlock_probe(struct platform_device *pdev)
{
- struct omap_hwspinlock *omap_lock;
- struct omap_hwspinlock_state *state;
- struct hwspinlock *lock;
+ struct hwspinlock_pdata *pdata = pdev->dev.platform_data;
+ struct hwspinlock_device *bank;
+ struct hwspinlock *hwlock;
struct resource *res;
void __iomem *io_base;
- int i, ret;
+ int num_locks, i, ret;
+
+ if (!pdata)
+ return -ENODEV;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
io_base = ioremap(res->start, resource_size(res));
- if (!io_base) {
- ret = -ENOMEM;
- goto free_state;
- }
+ if (!io_base)
+ return -ENOMEM;
/* Determine number of locks */
i = readl(io_base + SYSSTATUS_OFFSET);
@@ -124,10 +108,18 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev)
goto iounmap_base;
}
- state->num_locks = i * 32;
- state->io_base = io_base;
+ num_locks = i * 32; /* actual number of locks in this device */
+
+ bank = kzalloc(sizeof(*bank) + num_locks * sizeof(*hwlock), GFP_KERNEL);
+ if (!bank) {
+ ret = -ENOMEM;
+ goto iounmap_base;
+ }
+
+ platform_set_drvdata(pdev, bank);
- platform_set_drvdata(pdev, state);
+ for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++)
+ hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i;
/*
* runtime PM will make sure the clock of this module is
@@ -135,79 +127,46 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev)
*/
pm_runtime_enable(&pdev->dev);
- for (i = 0; i < state->num_locks; i++) {
- omap_lock = kzalloc(sizeof(*omap_lock), GFP_KERNEL);
- if (!omap_lock) {
- ret = -ENOMEM;
- goto free_locks;
- }
-
- omap_lock->lock.dev = &pdev->dev;
- omap_lock->lock.owner = THIS_MODULE;
- omap_lock->lock.id = i;
- omap_lock->lock.ops = &omap_hwspinlock_ops;
- omap_lock->addr = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i;
-
- ret = hwspin_lock_register(&omap_lock->lock);
- if (ret) {
- kfree(omap_lock);
- goto free_locks;
- }
- }
+ ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops,
+ pdata->base_id, num_locks);
+ if (ret)
+ goto reg_fail;
return 0;
-free_locks:
- while (--i >= 0) {
- lock = hwspin_lock_unregister(i);
- /* this should't happen, but let's give our best effort */
- if (!lock) {
- dev_err(&pdev->dev, "%s: cleanups failed\n", __func__);
- continue;
- }
- omap_lock = to_omap_hwspinlock(lock);
- kfree(omap_lock);
- }
+reg_fail:
pm_runtime_disable(&pdev->dev);
+ kfree(bank);
iounmap_base:
iounmap(io_base);
-free_state:
- kfree(state);
return ret;
}
-static int omap_hwspinlock_remove(struct platform_device *pdev)
+static int __devexit omap_hwspinlock_remove(struct platform_device *pdev)
{
- struct omap_hwspinlock_state *state = platform_get_drvdata(pdev);
- struct hwspinlock *lock;
- struct omap_hwspinlock *omap_lock;
- int i;
-
- for (i = 0; i < state->num_locks; i++) {
- lock = hwspin_lock_unregister(i);
- /* this shouldn't happen at this point. if it does, at least
- * don't continue with the remove */
- if (!lock) {
- dev_err(&pdev->dev, "%s: failed on %d\n", __func__, i);
- return -EBUSY;
- }
-
- omap_lock = to_omap_hwspinlock(lock);
- kfree(omap_lock);
+ struct hwspinlock_device *bank = platform_get_drvdata(pdev);
+ void __iomem *io_base = bank->lock[0].priv - LOCK_BASE_OFFSET;
+ int ret;
+
+ ret = hwspin_lock_unregister(bank);
+ if (ret) {
+ dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
+ return ret;
}
pm_runtime_disable(&pdev->dev);
- iounmap(state->io_base);
- kfree(state);
+ iounmap(io_base);
+ kfree(bank);
return 0;
}
static struct platform_driver omap_hwspinlock_driver = {
.probe = omap_hwspinlock_probe,
- .remove = omap_hwspinlock_remove,
+ .remove = __devexit_p(omap_hwspinlock_remove),
.driver = {
.name = "omap_hwspinlock",
+ .owner = THIS_MODULE,
},
};
diff --git a/drivers/hwspinlock/u8500_hsem.c b/drivers/hwspinlock/u8500_hsem.c
new file mode 100644
index 000000000000..86980fe04117
--- /dev/null
+++ b/drivers/hwspinlock/u8500_hsem.c
@@ -0,0 +1,197 @@
+/*
+ * u8500 HWSEM driver
+ *
+ * Copyright (C) 2010-2011 ST-Ericsson
+ *
+ * Implements u8500 semaphore handling for protocol 1, no interrupts.
+ *
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ * Heavily borrowed from the work of :
+ * Simon Que <sque@ti.com>
+ * Hari Kanigeri <h-kanigeri2@ti.com>
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/hwspinlock.h>
+#include <linux/platform_device.h>
+
+#include "hwspinlock_internal.h"
+
+/*
+ * Implementation of STE's HSem protocol 1 without interrutps.
+ * The only masterID we allow is '0x01' to force people to use
+ * HSems for synchronisation between processors rather than processes
+ * on the ARM core.
+ */
+
+#define U8500_MAX_SEMAPHORE 32 /* a total of 32 semaphore */
+#define RESET_SEMAPHORE (0) /* free */
+
+/*
+ * CPU ID for master running u8500 kernel.
+ * Hswpinlocks should only be used to synchonise operations
+ * between the Cortex A9 core and the other CPUs. Hence
+ * forcing the masterID to a preset value.
+ */
+#define HSEM_MASTER_ID 0x01
+
+#define HSEM_REGISTER_OFFSET 0x08
+
+#define HSEM_CTRL_REG 0x00
+#define HSEM_ICRALL 0x90
+#define HSEM_PROTOCOL_1 0x01
+
+static int u8500_hsem_trylock(struct hwspinlock *lock)
+{
+ void __iomem *lock_addr = lock->priv;
+
+ writel(HSEM_MASTER_ID, lock_addr);
+
+ /* get only first 4 bit and compare to masterID.
+ * if equal, we have the semaphore, otherwise
+ * someone else has it.
+ */
+ return (HSEM_MASTER_ID == (0x0F & readl(lock_addr)));
+}
+
+static void u8500_hsem_unlock(struct hwspinlock *lock)
+{
+ void __iomem *lock_addr = lock->priv;
+
+ /* release the lock by writing 0 to it */
+ writel(RESET_SEMAPHORE, lock_addr);
+}
+
+/*
+ * u8500: what value is recommended here ?
+ */
+static void u8500_hsem_relax(struct hwspinlock *lock)
+{
+ ndelay(50);
+}
+
+static const struct hwspinlock_ops u8500_hwspinlock_ops = {
+ .trylock = u8500_hsem_trylock,
+ .unlock = u8500_hsem_unlock,
+ .relax = u8500_hsem_relax,
+};
+
+static int __devinit u8500_hsem_probe(struct platform_device *pdev)
+{
+ struct hwspinlock_pdata *pdata = pdev->dev.platform_data;
+ struct hwspinlock_device *bank;
+ struct hwspinlock *hwlock;
+ struct resource *res;
+ void __iomem *io_base;
+ int i, ret, num_locks = U8500_MAX_SEMAPHORE;
+ ulong val;
+
+ if (!pdata)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ io_base = ioremap(res->start, resource_size(res));
+ if (!io_base)
+ return -ENOMEM;
+
+ /* make sure protocol 1 is selected */
+ val = readl(io_base + HSEM_CTRL_REG);
+ writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG);
+
+ /* clear all interrupts */
+ writel(0xFFFF, io_base + HSEM_ICRALL);
+
+ bank = kzalloc(sizeof(*bank) + num_locks * sizeof(*hwlock), GFP_KERNEL);
+ if (!bank) {
+ ret = -ENOMEM;
+ goto iounmap_base;
+ }
+
+ platform_set_drvdata(pdev, bank);
+
+ for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++)
+ hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i;
+
+ /* no pm needed for HSem but required to comply with hwspilock core */
+ pm_runtime_enable(&pdev->dev);
+
+ ret = hwspin_lock_register(bank, &pdev->dev, &u8500_hwspinlock_ops,
+ pdata->base_id, num_locks);
+ if (ret)
+ goto reg_fail;
+
+ return 0;
+
+reg_fail:
+ pm_runtime_disable(&pdev->dev);
+ kfree(bank);
+iounmap_base:
+ iounmap(io_base);
+ return ret;
+}
+
+static int __devexit u8500_hsem_remove(struct platform_device *pdev)
+{
+ struct hwspinlock_device *bank = platform_get_drvdata(pdev);
+ void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET;
+ int ret;
+
+ /* clear all interrupts */
+ writel(0xFFFF, io_base + HSEM_ICRALL);
+
+ ret = hwspin_lock_unregister(bank);
+ if (ret) {
+ dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ pm_runtime_disable(&pdev->dev);
+ iounmap(io_base);
+ kfree(bank);
+
+ return 0;
+}
+
+static struct platform_driver u8500_hsem_driver = {
+ .probe = u8500_hsem_probe,
+ .remove = __devexit_p(u8500_hsem_remove),
+ .driver = {
+ .name = "u8500_hsem",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init u8500_hsem_init(void)
+{
+ return platform_driver_register(&u8500_hsem_driver);
+}
+/* board init code might need to reserve hwspinlocks for predefined purposes */
+postcore_initcall(u8500_hsem_init);
+
+static void __exit u8500_hsem_exit(void)
+{
+ platform_driver_unregister(&u8500_hsem_driver);
+}
+module_exit(u8500_hsem_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Hardware Spinlock driver for u8500");
+MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 30f06e956bfb..5f13c62e64b4 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -7,7 +7,7 @@ menuconfig I2C
depends on HAS_IOMEM
select RT_MUTEXES
---help---
- I2C (pronounce: I-square-C) is a slow serial bus protocol used in
+ I2C (pronounce: I-squared-C) is a slow serial bus protocol used in
many micro controller applications and developed by Philips. SMBus,
or System Management Bus is a subset of the I2C protocol. More
information is contained in the directory <file:Documentation/i2c/>,
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index d6d58684712b..525c7345fa0b 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -47,8 +47,8 @@
/* ----- global variables --------------------------------------------- */
static int bit_test; /* see if the line-setting functions work */
-module_param(bit_test, bool, 0);
-MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck");
+module_param(bit_test, int, S_IRUGO);
+MODULE_PARM_DESC(bit_test, "lines testing - 0 off; 1 report; 2 fail if stuck");
#ifdef DEBUG
static int i2c_debug = 1;
@@ -250,7 +250,9 @@ static int test_bus(struct i2c_adapter *i2c_adap)
sda = getsda(adap);
scl = (adap->getscl == NULL) ? 1 : getscl(adap);
if (!scl || !sda) {
- printk(KERN_WARNING "%s: bus seems to be busy\n", name);
+ printk(KERN_WARNING
+ "%s: bus seems to be busy (scl=%d, sda=%d)\n",
+ name, scl, sda);
goto bailout;
}
@@ -441,7 +443,7 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
acknak(i2c_adap, 0);
dev_err(&i2c_adap->dev, "readbytes: invalid "
"block length (%d)\n", inval);
- return -EREMOTEIO;
+ return -EPROTO;
}
/* The original count value accounts for the extra
bytes, that is, either 1 for a regular transaction,
@@ -470,7 +472,7 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
* reads, writes as well as 10bit-addresses.
* returns:
* 0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set
- * -x an error occurred (like: -EREMOTEIO if the device did not answer, or
+ * -x an error occurred (like: -ENXIO if the device did not answer, or
* -ETIMEDOUT, for example if the lines are stuck...)
*/
static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
@@ -486,21 +488,21 @@ static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
if (flags & I2C_M_TEN) {
/* a ten bit address */
- addr = 0xf0 | ((msg->addr >> 7) & 0x03);
+ addr = 0xf0 | ((msg->addr >> 7) & 0x06);
bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr);
/* try extended address code...*/
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok) {
dev_err(&i2c_adap->dev,
"died at extended address code\n");
- return -EREMOTEIO;
+ return -ENXIO;
}
/* the remaining 8 bit address */
- ret = i2c_outb(i2c_adap, msg->addr & 0x7f);
+ ret = i2c_outb(i2c_adap, msg->addr & 0xff);
if ((ret != 1) && !nak_ok) {
/* the chip did not ack / xmission error occurred */
dev_err(&i2c_adap->dev, "died at 2nd address code\n");
- return -EREMOTEIO;
+ return -ENXIO;
}
if (flags & I2C_M_RD) {
bit_dbg(3, &i2c_adap->dev, "emitting repeated "
@@ -512,7 +514,7 @@ static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
if ((ret != 1) && !nak_ok) {
dev_err(&i2c_adap->dev,
"died at repeated address code\n");
- return -EREMOTEIO;
+ return -EIO;
}
}
} else { /* normal 7bit address */
@@ -570,7 +572,7 @@ static int bit_xfer(struct i2c_adapter *i2c_adap,
ret, ret == 1 ? "" : "s");
if (ret < pmsg->len) {
if (ret >= 0)
- ret = -EREMOTEIO;
+ ret = -EIO;
goto bailout;
}
} else {
@@ -581,7 +583,7 @@ static int bit_xfer(struct i2c_adapter *i2c_adap,
ret, ret == 1 ? "" : "s");
if (ret < pmsg->len) {
if (ret >= 0)
- ret = -EREMOTEIO;
+ ret = -EIO;
goto bailout;
}
}
@@ -624,7 +626,7 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap,
if (bit_test) {
ret = test_bus(adap);
- if (ret < 0)
+ if (bit_test >= 2 && ret < 0)
return -ENODEV;
}
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index 4ca9cf9cde73..beb9ffe2564b 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -196,7 +196,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
} else {
dev_dbg(&i2c_adap->dev, "bus is not idle. status is "
"%#04x\n", state);
- return -EAGAIN;
+ return -EBUSY;
}
}
@@ -224,7 +224,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
}
curmsg = 0;
- ret = -EREMOTEIO;
+ ret = -EIO;
while (curmsg < num) {
state = pca_status(adap);
@@ -259,6 +259,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */
DEB2("NOT ACK received after SLA+W\n");
pca_stop(adap);
+ ret = -ENXIO;
goto out;
case 0x40: /* SLA+R has been transmitted; ACK has been received */
@@ -283,6 +284,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */
DEB2("NOT ACK received after SLA+R\n");
pca_stop(adap);
+ ret = -ENXIO;
goto out;
case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 646068e5100b..a3afac4be734 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -110,7 +110,6 @@ config I2C_I801
config I2C_ISCH
tristate "Intel SCH SMBus 1.0"
depends on PCI
- select MFD_CORE
select LPC_SCH
help
Say Y here if you want to use SMBus controller on the Intel SCH
@@ -301,7 +300,7 @@ config I2C_AT91
config I2C_AU1550
tristate "Au1550/Au1200 SMBus interface"
- depends on SOC_AU1550 || SOC_AU1200
+ depends on MIPS_ALCHEMY
help
If you say yes to this option, support will be included for the
Au1550 and Au1200 SMBus interface.
@@ -350,15 +349,25 @@ config I2C_DAVINCI
devices such as DaVinci NIC.
For details please see http://www.ti.com/davinci
-config I2C_DESIGNWARE
- tristate "Synopsys DesignWare"
+config I2C_DESIGNWARE_PLATFORM
+ tristate "Synopsys DesignWare Platfrom"
depends on HAVE_CLK
help
If you say yes to this option, support will be included for the
Synopsys DesignWare I2C adapter. Only master mode is supported.
This driver can also be built as a module. If so, the module
- will be called i2c-designware.
+ will be called i2c-designware-platform.
+
+config I2C_DESIGNWARE_PCI
+ tristate "Synopsys DesignWare PCI"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the
+ Synopsys DesignWare I2C adapter. Only master mode is supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-designware-pci.
config I2C_GPIO
tristate "GPIO-based bitbanging I2C"
@@ -789,7 +798,7 @@ config I2C_ACORN
config I2C_ELEKTOR
tristate "Elektor ISA card"
- depends on ISA && BROKEN_ON_SMP
+ depends on ISA && HAS_IOPORT && BROKEN_ON_SMP
select I2C_ALGOPCF
help
This supports the PCF8584 ISA bus I2C adapter. Say Y if you own
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index e6cf294d3729..fba6da60aa0e 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -33,7 +33,10 @@ obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
-obj-$(CONFIG_I2C_DESIGNWARE) += i2c-designware.o
+obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
+i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-core.o
+obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
+i2c-designware-pci-objs := i2c-designware-pcidrv.o i2c-designware-core.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index dd364171f9c5..b6807db7b36f 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -1,23 +1,23 @@
/*
- Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>,
- Philip Edelbrock <phil@netroedge.com>,
- Mark D. Studebaker <mdsxyz123@yahoo.com>,
- Dan Eaton <dan.eaton@rocketlogix.com> and
- Stephen Rousset<stephen.rousset@rocketlogix.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>,
+ * Philip Edelbrock <phil@netroedge.com>,
+ * Mark D. Studebaker <mdsxyz123@yahoo.com>,
+ * Dan Eaton <dan.eaton@rocketlogix.com> and
+ * Stephen Rousset <stephen.rousset@rocketlogix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
@@ -254,8 +254,8 @@ static int ali1535_transaction(struct i2c_adapter *adap)
if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) {
/* do a clear-on-write */
outb_p(0xFF, SMBHSTSTS);
- if ((temp = inb_p(SMBHSTSTS)) &
- (ALI1535_STS_ERR | ALI1535_STS_BUSY)) {
+ temp = inb_p(SMBHSTSTS);
+ if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) {
/* This is probably going to be correctable only by a
* power reset as one of the bits now appears to be
* stuck */
@@ -267,9 +267,8 @@ static int ali1535_transaction(struct i2c_adapter *adap)
}
} else {
/* check and clear done bit */
- if (temp & ALI1535_STS_DONE) {
+ if (temp & ALI1535_STS_DONE)
outb_p(temp, SMBHSTSTS);
- }
}
/* start the transaction by writing anything to the start register */
@@ -278,7 +277,7 @@ static int ali1535_transaction(struct i2c_adapter *adap)
/* We will always wait for a fraction of a second! */
timeout = 0;
do {
- msleep(1);
+ usleep_range(1000, 2000);
temp = inb_p(SMBHSTSTS);
} while (((temp & ALI1535_STS_BUSY) && !(temp & ALI1535_STS_IDLE))
&& (timeout++ < MAX_TIMEOUT));
@@ -325,12 +324,12 @@ static int ali1535_transaction(struct i2c_adapter *adap)
/* take consequent actions for error conditions */
if (!(temp & ALI1535_STS_DONE)) {
/* issue "kill" to reset host controller */
- outb_p(ALI1535_KILL,SMBHSTTYP);
- outb_p(0xFF,SMBHSTSTS);
+ outb_p(ALI1535_KILL, SMBHSTTYP);
+ outb_p(0xFF, SMBHSTSTS);
} else if (temp & ALI1535_STS_ERR) {
/* issue "timeout" to reset all devices on bus */
- outb_p(ALI1535_T_OUT,SMBHSTTYP);
- outb_p(0xFF,SMBHSTSTS);
+ outb_p(ALI1535_T_OUT, SMBHSTTYP);
+ outb_p(0xFF, SMBHSTSTS);
}
return result;
@@ -351,7 +350,7 @@ static s32 ali1535_access(struct i2c_adapter *adap, u16 addr,
for (timeout = 0;
(timeout < MAX_TIMEOUT) && !(temp & ALI1535_STS_IDLE);
timeout++) {
- msleep(1);
+ usleep_range(1000, 2000);
temp = inb_p(SMBHSTSTS);
}
if (timeout >= MAX_TIMEOUT)
@@ -480,12 +479,12 @@ static struct i2c_adapter ali1535_adapter = {
.algo = &smbus_algorithm,
};
-static const struct pci_device_id ali1535_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(ali1535_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
{ },
};
-MODULE_DEVICE_TABLE (pci, ali1535_ids);
+MODULE_DEVICE_TABLE(pci, ali1535_ids);
static int __devinit ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c
index 532828bc50e6..f314d7f433d3 100644
--- a/drivers/i2c/busses/i2c-au1550.c
+++ b/drivers/i2c/busses/i2c-au1550.c
@@ -36,32 +36,44 @@
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <asm/mach-au1x00/au1xxx.h>
+#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_psc.h>
+#define PSC_SEL 0x00
+#define PSC_CTRL 0x04
+#define PSC_SMBCFG 0x08
+#define PSC_SMBMSK 0x0C
+#define PSC_SMBPCR 0x10
+#define PSC_SMBSTAT 0x14
+#define PSC_SMBEVNT 0x18
+#define PSC_SMBTXRX 0x1C
+#define PSC_SMBTMR 0x20
+
struct i2c_au1550_data {
- u32 psc_base;
+ void __iomem *psc_base;
int xfer_timeout;
- int ack_timeout;
struct i2c_adapter adap;
struct resource *ioarea;
};
-static int
-wait_xfer_done(struct i2c_au1550_data *adap)
+static inline void WR(struct i2c_au1550_data *a, int r, unsigned long v)
{
- u32 stat;
- int i;
- volatile psc_smb_t *sp;
+ __raw_writel(v, a->psc_base + r);
+ wmb();
+}
- sp = (volatile psc_smb_t *)(adap->psc_base);
+static inline unsigned long RD(struct i2c_au1550_data *a, int r)
+{
+ return __raw_readl(a->psc_base + r);
+}
- /* Wait for Tx Buffer Empty
- */
+static int wait_xfer_done(struct i2c_au1550_data *adap)
+{
+ int i;
+
+ /* Wait for Tx Buffer Empty */
for (i = 0; i < adap->xfer_timeout; i++) {
- stat = sp->psc_smbstat;
- au_sync();
- if ((stat & PSC_SMBSTAT_TE) != 0)
+ if (RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_TE)
return 0;
udelay(1);
@@ -70,41 +82,27 @@ wait_xfer_done(struct i2c_au1550_data *adap)
return -ETIMEDOUT;
}
-static int
-wait_ack(struct i2c_au1550_data *adap)
+static int wait_ack(struct i2c_au1550_data *adap)
{
- u32 stat;
- volatile psc_smb_t *sp;
+ unsigned long stat;
if (wait_xfer_done(adap))
return -ETIMEDOUT;
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
- stat = sp->psc_smbevnt;
- au_sync();
-
+ stat = RD(adap, PSC_SMBEVNT);
if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0)
return -ETIMEDOUT;
return 0;
}
-static int
-wait_master_done(struct i2c_au1550_data *adap)
+static int wait_master_done(struct i2c_au1550_data *adap)
{
- u32 stat;
- int i;
- volatile psc_smb_t *sp;
+ int i;
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
- /* Wait for Master Done.
- */
- for (i = 0; i < adap->xfer_timeout; i++) {
- stat = sp->psc_smbevnt;
- au_sync();
- if ((stat & PSC_SMBEVNT_MD) != 0)
+ /* Wait for Master Done. */
+ for (i = 0; i < 2 * adap->xfer_timeout; i++) {
+ if ((RD(adap, PSC_SMBEVNT) & PSC_SMBEVNT_MD) != 0)
return 0;
udelay(1);
}
@@ -115,29 +113,20 @@ wait_master_done(struct i2c_au1550_data *adap)
static int
do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q)
{
- volatile psc_smb_t *sp;
- u32 stat;
+ unsigned long stat;
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
- /* Reset the FIFOs, clear events.
- */
- stat = sp->psc_smbstat;
- sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
- au_sync();
+ /* Reset the FIFOs, clear events. */
+ stat = RD(adap, PSC_SMBSTAT);
+ WR(adap, PSC_SMBEVNT, PSC_SMBEVNT_ALLCLR);
if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE)) {
- sp->psc_smbpcr = PSC_SMBPCR_DC;
- au_sync();
- do {
- stat = sp->psc_smbpcr;
- au_sync();
- } while ((stat & PSC_SMBPCR_DC) != 0);
+ WR(adap, PSC_SMBPCR, PSC_SMBPCR_DC);
+ while ((RD(adap, PSC_SMBPCR) & PSC_SMBPCR_DC) != 0)
+ cpu_relax();
udelay(50);
}
- /* Write out the i2c chip address and specify operation
- */
+ /* Write out the i2c chip address and specify operation */
addr <<= 1;
if (rd)
addr |= 1;
@@ -146,56 +135,42 @@ do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q)
if (q)
addr |= PSC_SMBTXRX_STP;
- /* Put byte into fifo, start up master.
- */
- sp->psc_smbtxrx = addr;
- au_sync();
- sp->psc_smbpcr = PSC_SMBPCR_MS;
- au_sync();
+ /* Put byte into fifo, start up master. */
+ WR(adap, PSC_SMBTXRX, addr);
+ WR(adap, PSC_SMBPCR, PSC_SMBPCR_MS);
if (wait_ack(adap))
return -EIO;
return (q) ? wait_master_done(adap) : 0;
}
-static u32
-wait_for_rx_byte(struct i2c_au1550_data *adap, u32 *ret_data)
+static int wait_for_rx_byte(struct i2c_au1550_data *adap, unsigned char *out)
{
- int j;
- u32 data, stat;
- volatile psc_smb_t *sp;
+ int j;
if (wait_xfer_done(adap))
return -EIO;
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
j = adap->xfer_timeout * 100;
do {
j--;
if (j <= 0)
return -EIO;
- stat = sp->psc_smbstat;
- au_sync();
- if ((stat & PSC_SMBSTAT_RE) == 0)
+ if ((RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_RE) == 0)
j = 0;
else
udelay(1);
} while (j > 0);
- data = sp->psc_smbtxrx;
- au_sync();
- *ret_data = data;
+
+ *out = RD(adap, PSC_SMBTXRX);
return 0;
}
-static int
-i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
+static int i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
unsigned int len)
{
- int i;
- u32 data;
- volatile psc_smb_t *sp;
+ int i;
if (len == 0)
return 0;
@@ -204,62 +179,46 @@ i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
* zero bytes for timing, waiting for bytes to appear in the
* receive fifo, then reading the bytes.
*/
-
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
i = 0;
- while (i < (len-1)) {
- sp->psc_smbtxrx = 0;
- au_sync();
- if (wait_for_rx_byte(adap, &data))
+ while (i < (len - 1)) {
+ WR(adap, PSC_SMBTXRX, 0);
+ if (wait_for_rx_byte(adap, &buf[i]))
return -EIO;
- buf[i] = data;
i++;
}
- /* The last byte has to indicate transfer done.
- */
- sp->psc_smbtxrx = PSC_SMBTXRX_STP;
- au_sync();
+ /* The last byte has to indicate transfer done. */
+ WR(adap, PSC_SMBTXRX, PSC_SMBTXRX_STP);
if (wait_master_done(adap))
return -EIO;
- data = sp->psc_smbtxrx;
- au_sync();
- buf[i] = data;
+ buf[i] = (unsigned char)(RD(adap, PSC_SMBTXRX) & 0xff);
return 0;
}
-static int
-i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
+static int i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
unsigned int len)
{
- int i;
- u32 data;
- volatile psc_smb_t *sp;
+ int i;
+ unsigned long data;
if (len == 0)
return 0;
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
i = 0;
while (i < (len-1)) {
data = buf[i];
- sp->psc_smbtxrx = data;
- au_sync();
+ WR(adap, PSC_SMBTXRX, data);
if (wait_ack(adap))
return -EIO;
i++;
}
- /* The last byte has to indicate transfer done.
- */
+ /* The last byte has to indicate transfer done. */
data = buf[i];
data |= PSC_SMBTXRX_STP;
- sp->psc_smbtxrx = data;
- au_sync();
+ WR(adap, PSC_SMBTXRX, data);
if (wait_master_done(adap))
return -EIO;
return 0;
@@ -269,12 +228,10 @@ static int
au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
{
struct i2c_au1550_data *adap = i2c_adap->algo_data;
- volatile psc_smb_t *sp = (volatile psc_smb_t *)adap->psc_base;
struct i2c_msg *p;
int i, err = 0;
- sp->psc_ctrl = PSC_CTRL_ENABLE;
- au_sync();
+ WR(adap, PSC_CTRL, PSC_CTRL_ENABLE);
for (i = 0; !err && i < num; i++) {
p = &msgs[i];
@@ -293,14 +250,12 @@ au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
if (err == 0)
err = num;
- sp->psc_ctrl = PSC_CTRL_SUSPEND;
- au_sync();
+ WR(adap, PSC_CTRL, PSC_CTRL_SUSPEND);
return err;
}
-static u32
-au1550_func(struct i2c_adapter *adap)
+static u32 au1550_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
@@ -312,57 +267,45 @@ static const struct i2c_algorithm au1550_algo = {
static void i2c_au1550_setup(struct i2c_au1550_data *priv)
{
- volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base;
- u32 stat;
-
- sp->psc_ctrl = PSC_CTRL_DISABLE;
- au_sync();
- sp->psc_sel = PSC_SEL_PS_SMBUSMODE;
- sp->psc_smbcfg = 0;
- au_sync();
- sp->psc_ctrl = PSC_CTRL_ENABLE;
- au_sync();
- do {
- stat = sp->psc_smbstat;
- au_sync();
- } while ((stat & PSC_SMBSTAT_SR) == 0);
+ unsigned long cfg;
- sp->psc_smbcfg = (PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 |
- PSC_SMBCFG_DD_DISABLE);
+ WR(priv, PSC_CTRL, PSC_CTRL_DISABLE);
+ WR(priv, PSC_SEL, PSC_SEL_PS_SMBUSMODE);
+ WR(priv, PSC_SMBCFG, 0);
+ WR(priv, PSC_CTRL, PSC_CTRL_ENABLE);
+ while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0)
+ cpu_relax();
+
+ cfg = PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 | PSC_SMBCFG_DD_DISABLE;
+ WR(priv, PSC_SMBCFG, cfg);
/* Divide by 8 to get a 6.25 MHz clock. The later protocol
* timings are based on this clock.
*/
- sp->psc_smbcfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8);
- sp->psc_smbmsk = PSC_SMBMSK_ALLMASK;
- au_sync();
+ cfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8);
+ WR(priv, PSC_SMBCFG, cfg);
+ WR(priv, PSC_SMBMSK, PSC_SMBMSK_ALLMASK);
/* Set the protocol timer values. See Table 71 in the
* Au1550 Data Book for standard timing values.
*/
- sp->psc_smbtmr = PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
+ WR(priv, PSC_SMBTMR, PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
PSC_SMBTMR_SET_PU(15) | PSC_SMBTMR_SET_SH(15) | \
PSC_SMBTMR_SET_SU(15) | PSC_SMBTMR_SET_CL(15) | \
- PSC_SMBTMR_SET_CH(15);
- au_sync();
+ PSC_SMBTMR_SET_CH(15));
- sp->psc_smbcfg |= PSC_SMBCFG_DE_ENABLE;
- do {
- stat = sp->psc_smbstat;
- au_sync();
- } while ((stat & PSC_SMBSTAT_SR) == 0);
+ cfg |= PSC_SMBCFG_DE_ENABLE;
+ WR(priv, PSC_SMBCFG, cfg);
+ while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0)
+ cpu_relax();
- sp->psc_ctrl = PSC_CTRL_SUSPEND;
- au_sync();
+ WR(priv, PSC_CTRL, PSC_CTRL_SUSPEND);
}
static void i2c_au1550_disable(struct i2c_au1550_data *priv)
{
- volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base;
-
- sp->psc_smbcfg = 0;
- sp->psc_ctrl = PSC_CTRL_DISABLE;
- au_sync();
+ WR(priv, PSC_SMBCFG, 0);
+ WR(priv, PSC_CTRL, PSC_CTRL_DISABLE);
}
/*
@@ -396,9 +339,12 @@ i2c_au1550_probe(struct platform_device *pdev)
goto out_mem;
}
- priv->psc_base = CKSEG1ADDR(r->start);
+ priv->psc_base = ioremap(r->start, resource_size(r));
+ if (!priv->psc_base) {
+ ret = -EIO;
+ goto out_map;
+ }
priv->xfer_timeout = 200;
- priv->ack_timeout = 200;
priv->adap.nr = pdev->id;
priv->adap.algo = &au1550_algo;
@@ -406,8 +352,7 @@ i2c_au1550_probe(struct platform_device *pdev)
priv->adap.dev.parent = &pdev->dev;
strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name));
- /* Now, set up the PSC for SMBus PIO mode.
- */
+ /* Now, set up the PSC for SMBus PIO mode. */
i2c_au1550_setup(priv);
ret = i2c_add_numbered_adapter(&priv->adap);
@@ -417,7 +362,8 @@ i2c_au1550_probe(struct platform_device *pdev)
}
i2c_au1550_disable(priv);
-
+ iounmap(priv->psc_base);
+out_map:
release_resource(priv->ioarea);
kfree(priv->ioarea);
out_mem:
@@ -426,14 +372,14 @@ out:
return ret;
}
-static int __devexit
-i2c_au1550_remove(struct platform_device *pdev)
+static int __devexit i2c_au1550_remove(struct platform_device *pdev)
{
struct i2c_au1550_data *priv = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
i2c_del_adapter(&priv->adap);
i2c_au1550_disable(priv);
+ iounmap(priv->psc_base);
release_resource(priv->ioarea);
kfree(priv->ioarea);
kfree(priv);
@@ -441,49 +387,51 @@ i2c_au1550_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int
-i2c_au1550_suspend(struct platform_device *pdev, pm_message_t state)
+static int i2c_au1550_suspend(struct device *dev)
{
- struct i2c_au1550_data *priv = platform_get_drvdata(pdev);
+ struct i2c_au1550_data *priv = dev_get_drvdata(dev);
i2c_au1550_disable(priv);
return 0;
}
-static int
-i2c_au1550_resume(struct platform_device *pdev)
+static int i2c_au1550_resume(struct device *dev)
{
- struct i2c_au1550_data *priv = platform_get_drvdata(pdev);
+ struct i2c_au1550_data *priv = dev_get_drvdata(dev);
i2c_au1550_setup(priv);
return 0;
}
+
+static const struct dev_pm_ops i2c_au1550_pmops = {
+ .suspend = i2c_au1550_suspend,
+ .resume = i2c_au1550_resume,
+};
+
+#define AU1XPSC_SMBUS_PMOPS (&i2c_au1550_pmops)
+
#else
-#define i2c_au1550_suspend NULL
-#define i2c_au1550_resume NULL
+#define AU1XPSC_SMBUS_PMOPS NULL
#endif
static struct platform_driver au1xpsc_smbus_driver = {
.driver = {
.name = "au1xpsc_smbus",
.owner = THIS_MODULE,
+ .pm = AU1XPSC_SMBUS_PMOPS,
},
.probe = i2c_au1550_probe,
.remove = __devexit_p(i2c_au1550_remove),
- .suspend = i2c_au1550_suspend,
- .resume = i2c_au1550_resume,
};
-static int __init
-i2c_au1550_init(void)
+static int __init i2c_au1550_init(void)
{
return platform_driver_register(&au1xpsc_smbus_driver);
}
-static void __exit
-i2c_au1550_exit(void)
+static void __exit i2c_au1550_exit(void)
{
platform_driver_unregister(&au1xpsc_smbus_driver);
}
diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c
index cbc98aea5b09..cdb59e5b23f7 100644
--- a/drivers/i2c/busses/i2c-bfin-twi.c
+++ b/drivers/i2c/busses/i2c-bfin-twi.c
@@ -631,7 +631,7 @@ static int i2c_bfin_twi_resume(struct platform_device *pdev)
struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
int rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
- IRQF_DISABLED, pdev->name, iface);
+ 0, pdev->name, iface);
if (rc) {
dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
return -ENODEV;
@@ -702,7 +702,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
}
rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
- IRQF_DISABLED, pdev->name, iface);
+ 0, pdev->name, iface);
if (rc) {
dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
rc = -ENODEV;
diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c
index 3a20961bef1e..b1d9cd28d8da 100644
--- a/drivers/i2c/busses/i2c-cpm.c
+++ b/drivers/i2c/busses/i2c-cpm.c
@@ -662,11 +662,8 @@ static int __devinit cpm_i2c_probe(struct platform_device *ofdev)
/* register new adapter to i2c module... */
data = of_get_property(ofdev->dev.of_node, "linux,i2c-index", &len);
- if (data && len == 4) {
- cpm->adap.nr = *data;
- result = i2c_add_numbered_adapter(&cpm->adap);
- } else
- result = i2c_add_adapter(&cpm->adap);
+ cpm->adap.nr = (data && len == 4) ? be32_to_cpup(data) : -1;
+ result = i2c_add_numbered_adapter(&cpm->adap);
if (result < 0) {
dev_err(&ofdev->dev, "Unable to register with I2C\n");
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware-core.c
index b7a51c43b185..df8799241009 100644
--- a/drivers/i2c/busses/i2c-designware.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -25,18 +25,15 @@
* ----------------------------------------------------------------------------
*
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/errno.h>
-#include <linux/sched.h>
#include <linux/err.h>
+#include <linux/i2c.h>
#include <linux/interrupt.h>
-#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include "i2c-designware-core.h"
/*
* Registers offset
@@ -68,15 +65,10 @@
#define DW_IC_STATUS 0x70
#define DW_IC_TXFLR 0x74
#define DW_IC_RXFLR 0x78
-#define DW_IC_COMP_PARAM_1 0xf4
#define DW_IC_TX_ABRT_SOURCE 0x80
-
-#define DW_IC_CON_MASTER 0x1
-#define DW_IC_CON_SPEED_STD 0x2
-#define DW_IC_CON_SPEED_FAST 0x4
-#define DW_IC_CON_10BITADDR_MASTER 0x10
-#define DW_IC_CON_RESTART_EN 0x20
-#define DW_IC_CON_SLAVE_DISABLE 0x40
+#define DW_IC_COMP_PARAM_1 0xf4
+#define DW_IC_COMP_TYPE 0xfc
+#define DW_IC_COMP_TYPE_VALUE 0x44570140
#define DW_IC_INTR_RX_UNDER 0x001
#define DW_IC_INTR_RX_OVER 0x002
@@ -170,55 +162,23 @@ static char *abort_sources[] = {
"lost arbitration",
};
-/**
- * struct dw_i2c_dev - private i2c-designware data
- * @dev: driver model device node
- * @base: IO registers pointer
- * @cmd_complete: tx completion indicator
- * @lock: protect this struct and IO registers
- * @clk: input reference clock
- * @cmd_err: run time hadware error code
- * @msgs: points to an array of messages currently being transferred
- * @msgs_num: the number of elements in msgs
- * @msg_write_idx: the element index of the current tx message in the msgs
- * array
- * @tx_buf_len: the length of the current tx buffer
- * @tx_buf: the current tx buffer
- * @msg_read_idx: the element index of the current rx message in the msgs
- * array
- * @rx_buf_len: the length of the current rx buffer
- * @rx_buf: the current rx buffer
- * @msg_err: error status of the current transfer
- * @status: i2c master status, one of STATUS_*
- * @abort_source: copy of the TX_ABRT_SOURCE register
- * @irq: interrupt number for the i2c master
- * @adapter: i2c subsystem adapter node
- * @tx_fifo_depth: depth of the hardware tx fifo
- * @rx_fifo_depth: depth of the hardware rx fifo
- */
-struct dw_i2c_dev {
- struct device *dev;
- void __iomem *base;
- struct completion cmd_complete;
- struct mutex lock;
- struct clk *clk;
- int cmd_err;
- struct i2c_msg *msgs;
- int msgs_num;
- int msg_write_idx;
- u32 tx_buf_len;
- u8 *tx_buf;
- int msg_read_idx;
- u32 rx_buf_len;
- u8 *rx_buf;
- int msg_err;
- unsigned int status;
- u32 abort_source;
- int irq;
- struct i2c_adapter adapter;
- unsigned int tx_fifo_depth;
- unsigned int rx_fifo_depth;
-};
+u32 dw_readl(struct dw_i2c_dev *dev, int offset)
+{
+ u32 value = readl(dev->base + offset);
+
+ if (dev->swab)
+ return swab32(value);
+ else
+ return value;
+}
+
+void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
+{
+ if (dev->swab)
+ b = swab32(b);
+
+ writel(b, dev->base + offset);
+}
static u32
i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
@@ -283,13 +243,29 @@ static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
* This function is called during I2C init function, and in case of timeout at
* run time.
*/
-static void i2c_dw_init(struct dw_i2c_dev *dev)
+int i2c_dw_init(struct dw_i2c_dev *dev)
{
- u32 input_clock_khz = clk_get_rate(dev->clk) / 1000;
- u32 ic_con, hcnt, lcnt;
+ u32 input_clock_khz;
+ u32 hcnt, lcnt;
+ u32 reg;
+
+ input_clock_khz = dev->get_clk_rate_khz(dev);
+
+ /* Configure register endianess access */
+ reg = dw_readl(dev, DW_IC_COMP_TYPE);
+ if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
+ dev->swab = 1;
+ reg = DW_IC_COMP_TYPE_VALUE;
+ }
+
+ if (reg != DW_IC_COMP_TYPE_VALUE) {
+ dev_err(dev->dev, "Unknown Synopsys component type: "
+ "0x%08x\n", reg);
+ return -ENODEV;
+ }
/* Disable the adapter */
- writel(0, dev->base + DW_IC_ENABLE);
+ dw_writel(dev, 0, DW_IC_ENABLE);
/* set standard and fast speed deviders for high/low periods */
@@ -303,8 +279,8 @@ static void i2c_dw_init(struct dw_i2c_dev *dev)
47, /* tLOW = 4.7 us */
3, /* tf = 0.3 us */
0); /* No offset */
- writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT);
- writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT);
+ dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT);
+ dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT);
dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
/* Fast-mode */
@@ -317,18 +293,17 @@ static void i2c_dw_init(struct dw_i2c_dev *dev)
13, /* tLOW = 1.3 us */
3, /* tf = 0.3 us */
0); /* No offset */
- writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT);
- writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT);
+ dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT);
+ dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT);
dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
/* Configure Tx/Rx FIFO threshold levels */
- writel(dev->tx_fifo_depth - 1, dev->base + DW_IC_TX_TL);
- writel(0, dev->base + DW_IC_RX_TL);
+ dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL);
+ dw_writel(dev, 0, DW_IC_RX_TL);
/* configure the i2c master */
- ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
- DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
- writel(ic_con, dev->base + DW_IC_CON);
+ dw_writel(dev, dev->master_cfg , DW_IC_CON);
+ return 0;
}
/*
@@ -338,7 +313,7 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
{
int timeout = TIMEOUT;
- while (readl(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
+ while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
if (timeout <= 0) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
return -ETIMEDOUT;
@@ -356,24 +331,24 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
u32 ic_con;
/* Disable the adapter */
- writel(0, dev->base + DW_IC_ENABLE);
+ dw_writel(dev, 0, DW_IC_ENABLE);
/* set the slave (target) address */
- writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
+ dw_writel(dev, msgs[dev->msg_write_idx].addr, DW_IC_TAR);
/* if the slave address is ten bit address, enable 10BITADDR */
- ic_con = readl(dev->base + DW_IC_CON);
+ ic_con = dw_readl(dev, DW_IC_CON);
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
ic_con |= DW_IC_CON_10BITADDR_MASTER;
else
ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
- writel(ic_con, dev->base + DW_IC_CON);
+ dw_writel(dev, ic_con, DW_IC_CON);
/* Enable the adapter */
- writel(1, dev->base + DW_IC_ENABLE);
+ dw_writel(dev, 1, DW_IC_ENABLE);
/* Enable interrupts */
- writel(DW_IC_INTR_DEFAULT_MASK, dev->base + DW_IC_INTR_MASK);
+ dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK);
}
/*
@@ -382,7 +357,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
* messages into the tx buffer. Even if the size of i2c_msg data is
* longer than the size of the tx buffer, it handles everything.
*/
-static void
+void
i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
@@ -390,7 +365,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
int tx_limit, rx_limit;
u32 addr = msgs[dev->msg_write_idx].addr;
u32 buf_len = dev->tx_buf_len;
- u8 *buf = dev->tx_buf;;
+ u8 *buf = dev->tx_buf;
intr_mask = DW_IC_INTR_DEFAULT_MASK;
@@ -420,15 +395,15 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
buf_len = msgs[dev->msg_write_idx].len;
}
- tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR);
- rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR);
+ tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR);
+ rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR);
while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
- writel(0x100, dev->base + DW_IC_DATA_CMD);
+ dw_writel(dev, 0x100, DW_IC_DATA_CMD);
rx_limit--;
} else
- writel(*buf++, dev->base + DW_IC_DATA_CMD);
+ dw_writel(dev, *buf++, DW_IC_DATA_CMD);
tx_limit--; buf_len--;
}
@@ -453,7 +428,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
if (dev->msg_err)
intr_mask = 0;
- writel(intr_mask, dev->base + DW_IC_INTR_MASK);
+ dw_writel(dev, intr_mask, DW_IC_INTR_MASK);
}
static void
@@ -477,10 +452,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
buf = dev->rx_buf;
}
- rx_valid = readl(dev->base + DW_IC_RXFLR);
+ rx_valid = dw_readl(dev, DW_IC_RXFLR);
for (; len > 0 && rx_valid > 0; len--, rx_valid--)
- *buf++ = readl(dev->base + DW_IC_DATA_CMD);
+ *buf++ = dw_readl(dev, DW_IC_DATA_CMD);
if (len > 0) {
dev->status |= STATUS_READ_IN_PROGRESS;
@@ -518,7 +493,7 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
/*
* Prepare controller for a transaction and call i2c_dw_xfer_msg
*/
-static int
+int
i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
@@ -527,6 +502,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
mutex_lock(&dev->lock);
+ pm_runtime_get_sync(dev->dev);
INIT_COMPLETION(dev->cmd_complete);
dev->msgs = msgs;
@@ -563,7 +539,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
/* no error */
if (likely(!dev->cmd_err)) {
/* Disable the adapter */
- writel(0, dev->base + DW_IC_ENABLE);
+ dw_writel(dev, 0, DW_IC_ENABLE);
ret = num;
goto done;
}
@@ -576,19 +552,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
ret = -EIO;
done:
+ pm_runtime_put(dev->dev);
mutex_unlock(&dev->lock);
return ret;
}
-static u32 i2c_dw_func(struct i2c_adapter *adap)
+u32 i2c_dw_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C |
- I2C_FUNC_10BIT_ADDR |
- I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_I2C_BLOCK;
+ struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+ return dev->functionality;
}
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
@@ -601,47 +574,47 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
* in the IC_RAW_INTR_STAT register.
*
* That is,
- * stat = readl(IC_INTR_STAT);
+ * stat = dw_readl(IC_INTR_STAT);
* equals to,
- * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
+ * stat = dw_readl(IC_RAW_INTR_STAT) & dw_readl(IC_INTR_MASK);
*
* The raw version might be useful for debugging purposes.
*/
- stat = readl(dev->base + DW_IC_INTR_STAT);
+ stat = dw_readl(dev, DW_IC_INTR_STAT);
/*
* Do not use the IC_CLR_INTR register to clear interrupts, or
* you'll miss some interrupts, triggered during the period from
- * readl(IC_INTR_STAT) to readl(IC_CLR_INTR).
+ * dw_readl(IC_INTR_STAT) to dw_readl(IC_CLR_INTR).
*
* Instead, use the separately-prepared IC_CLR_* registers.
*/
if (stat & DW_IC_INTR_RX_UNDER)
- readl(dev->base + DW_IC_CLR_RX_UNDER);
+ dw_readl(dev, DW_IC_CLR_RX_UNDER);
if (stat & DW_IC_INTR_RX_OVER)
- readl(dev->base + DW_IC_CLR_RX_OVER);
+ dw_readl(dev, DW_IC_CLR_RX_OVER);
if (stat & DW_IC_INTR_TX_OVER)
- readl(dev->base + DW_IC_CLR_TX_OVER);
+ dw_readl(dev, DW_IC_CLR_TX_OVER);
if (stat & DW_IC_INTR_RD_REQ)
- readl(dev->base + DW_IC_CLR_RD_REQ);
+ dw_readl(dev, DW_IC_CLR_RD_REQ);
if (stat & DW_IC_INTR_TX_ABRT) {
/*
* The IC_TX_ABRT_SOURCE register is cleared whenever
* the IC_CLR_TX_ABRT is read. Preserve it beforehand.
*/
- dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE);
- readl(dev->base + DW_IC_CLR_TX_ABRT);
+ dev->abort_source = dw_readl(dev, DW_IC_TX_ABRT_SOURCE);
+ dw_readl(dev, DW_IC_CLR_TX_ABRT);
}
if (stat & DW_IC_INTR_RX_DONE)
- readl(dev->base + DW_IC_CLR_RX_DONE);
+ dw_readl(dev, DW_IC_CLR_RX_DONE);
if (stat & DW_IC_INTR_ACTIVITY)
- readl(dev->base + DW_IC_CLR_ACTIVITY);
+ dw_readl(dev, DW_IC_CLR_ACTIVITY);
if (stat & DW_IC_INTR_STOP_DET)
- readl(dev->base + DW_IC_CLR_STOP_DET);
+ dw_readl(dev, DW_IC_CLR_STOP_DET);
if (stat & DW_IC_INTR_START_DET)
- readl(dev->base + DW_IC_CLR_START_DET);
+ dw_readl(dev, DW_IC_CLR_START_DET);
if (stat & DW_IC_INTR_GEN_CALL)
- readl(dev->base + DW_IC_CLR_GEN_CALL);
+ dw_readl(dev, DW_IC_CLR_GEN_CALL);
return stat;
}
@@ -650,13 +623,19 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
* Interrupt service routine. This gets called whenever an I2C interrupt
* occurs.
*/
-static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
+irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
{
struct dw_i2c_dev *dev = dev_id;
- u32 stat;
+ u32 stat, enabled;
+
+ enabled = dw_readl(dev, DW_IC_ENABLE);
+ stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
+ dev_dbg(dev->dev, "%s: %s enabled= 0x%x stat=0x%x\n", __func__,
+ dev->adapter.name, enabled, stat);
+ if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
+ return IRQ_NONE;
stat = i2c_dw_read_clear_intrbits(dev);
- dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
if (stat & DW_IC_INTR_TX_ABRT) {
dev->cmd_err |= DW_IC_ERR_TX_ABRT;
@@ -666,7 +645,7 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
* Anytime TX_ABRT is set, the contents of the tx/rx
* buffers are flushed. Make sure to skip them.
*/
- writel(0, dev->base + DW_IC_INTR_MASK);
+ dw_writel(dev, 0, DW_IC_INTR_MASK);
goto tx_aborted;
}
@@ -689,159 +668,38 @@ tx_aborted:
return IRQ_HANDLED;
}
-static struct i2c_algorithm i2c_dw_algo = {
- .master_xfer = i2c_dw_xfer,
- .functionality = i2c_dw_func,
-};
-
-static int __devinit dw_i2c_probe(struct platform_device *pdev)
+void i2c_dw_enable(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev;
- struct i2c_adapter *adap;
- struct resource *mem, *ioarea;
- int irq, r;
-
- /* NOTE: driver uses the static register mapping */
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "no mem resource?\n");
- return -EINVAL;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
- return irq; /* -ENXIO */
- }
-
- ioarea = request_mem_region(mem->start, resource_size(mem),
- pdev->name);
- if (!ioarea) {
- dev_err(&pdev->dev, "I2C region already claimed\n");
- return -EBUSY;
- }
-
- dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
- if (!dev) {
- r = -ENOMEM;
- goto err_release_region;
- }
-
- init_completion(&dev->cmd_complete);
- mutex_init(&dev->lock);
- dev->dev = get_device(&pdev->dev);
- dev->irq = irq;
- platform_set_drvdata(pdev, dev);
-
- dev->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- r = -ENODEV;
- goto err_free_mem;
- }
- clk_enable(dev->clk);
-
- dev->base = ioremap(mem->start, resource_size(mem));
- if (dev->base == NULL) {
- dev_err(&pdev->dev, "failure mapping io resources\n");
- r = -EBUSY;
- goto err_unuse_clocks;
- }
- {
- u32 param1 = readl(dev->base + DW_IC_COMP_PARAM_1);
-
- dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;
- dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1;
- }
- i2c_dw_init(dev);
-
- writel(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
- r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev);
- if (r) {
- dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
- goto err_iounmap;
- }
-
- adap = &dev->adapter;
- i2c_set_adapdata(adap, dev);
- adap->owner = THIS_MODULE;
- adap->class = I2C_CLASS_HWMON;
- strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
- sizeof(adap->name));
- adap->algo = &i2c_dw_algo;
- adap->dev.parent = &pdev->dev;
-
- adap->nr = pdev->id;
- r = i2c_add_numbered_adapter(adap);
- if (r) {
- dev_err(&pdev->dev, "failure adding adapter\n");
- goto err_free_irq;
- }
-
- return 0;
-
-err_free_irq:
- free_irq(dev->irq, dev);
-err_iounmap:
- iounmap(dev->base);
-err_unuse_clocks:
- clk_disable(dev->clk);
- clk_put(dev->clk);
- dev->clk = NULL;
-err_free_mem:
- platform_set_drvdata(pdev, NULL);
- put_device(&pdev->dev);
- kfree(dev);
-err_release_region:
- release_mem_region(mem->start, resource_size(mem));
-
- return r;
+ /* Enable the adapter */
+ dw_writel(dev, 1, DW_IC_ENABLE);
}
-static int __devexit dw_i2c_remove(struct platform_device *pdev)
+u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
- struct resource *mem;
-
- platform_set_drvdata(pdev, NULL);
- i2c_del_adapter(&dev->adapter);
- put_device(&pdev->dev);
-
- clk_disable(dev->clk);
- clk_put(dev->clk);
- dev->clk = NULL;
-
- writel(0, dev->base + DW_IC_ENABLE);
- free_irq(dev->irq, dev);
- kfree(dev);
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(mem->start, resource_size(mem));
- return 0;
+ return dw_readl(dev, DW_IC_ENABLE);
}
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:i2c_designware");
+void i2c_dw_disable(struct dw_i2c_dev *dev)
+{
+ /* Disable controller */
+ dw_writel(dev, 0, DW_IC_ENABLE);
-static struct platform_driver dw_i2c_driver = {
- .remove = __devexit_p(dw_i2c_remove),
- .driver = {
- .name = "i2c_designware",
- .owner = THIS_MODULE,
- },
-};
+ /* Disable all interupts */
+ dw_writel(dev, 0, DW_IC_INTR_MASK);
+ dw_readl(dev, DW_IC_CLR_INTR);
+}
-static int __init dw_i2c_init_driver(void)
+void i2c_dw_clear_int(struct dw_i2c_dev *dev)
{
- return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
+ dw_readl(dev, DW_IC_CLR_INTR);
}
-module_init(dw_i2c_init_driver);
-static void __exit dw_i2c_exit_driver(void)
+void i2c_dw_disable_int(struct dw_i2c_dev *dev)
{
- platform_driver_unregister(&dw_i2c_driver);
+ dw_writel(dev, 0, DW_IC_INTR_MASK);
}
-module_exit(dw_i2c_exit_driver);
-MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
-MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
-MODULE_LICENSE("GPL");
+u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev)
+{
+ return dw_readl(dev, DW_IC_COMP_PARAM_1);
+}
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
new file mode 100644
index 000000000000..02d1a2ddd853
--- /dev/null
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -0,0 +1,105 @@
+/*
+ * Synopsys DesignWare I2C adapter driver (master only).
+ *
+ * Based on the TI DAVINCI I2C adapter driver.
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2007 MontaVista Software Inc.
+ * Copyright (C) 2009 Provigent Ltd.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+
+#define DW_IC_CON_MASTER 0x1
+#define DW_IC_CON_SPEED_STD 0x2
+#define DW_IC_CON_SPEED_FAST 0x4
+#define DW_IC_CON_10BITADDR_MASTER 0x10
+#define DW_IC_CON_RESTART_EN 0x20
+#define DW_IC_CON_SLAVE_DISABLE 0x40
+
+
+/**
+ * struct dw_i2c_dev - private i2c-designware data
+ * @dev: driver model device node
+ * @base: IO registers pointer
+ * @cmd_complete: tx completion indicator
+ * @lock: protect this struct and IO registers
+ * @clk: input reference clock
+ * @cmd_err: run time hadware error code
+ * @msgs: points to an array of messages currently being transfered
+ * @msgs_num: the number of elements in msgs
+ * @msg_write_idx: the element index of the current tx message in the msgs
+ * array
+ * @tx_buf_len: the length of the current tx buffer
+ * @tx_buf: the current tx buffer
+ * @msg_read_idx: the element index of the current rx message in the msgs
+ * array
+ * @rx_buf_len: the length of the current rx buffer
+ * @rx_buf: the current rx buffer
+ * @msg_err: error status of the current transfer
+ * @status: i2c master status, one of STATUS_*
+ * @abort_source: copy of the TX_ABRT_SOURCE register
+ * @irq: interrupt number for the i2c master
+ * @adapter: i2c subsystem adapter node
+ * @tx_fifo_depth: depth of the hardware tx fifo
+ * @rx_fifo_depth: depth of the hardware rx fifo
+ */
+struct dw_i2c_dev {
+ struct device *dev;
+ void __iomem *base;
+ struct completion cmd_complete;
+ struct mutex lock;
+ struct clk *clk;
+ u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
+ struct dw_pci_controller *controller;
+ int cmd_err;
+ struct i2c_msg *msgs;
+ int msgs_num;
+ int msg_write_idx;
+ u32 tx_buf_len;
+ u8 *tx_buf;
+ int msg_read_idx;
+ u32 rx_buf_len;
+ u8 *rx_buf;
+ int msg_err;
+ unsigned int status;
+ u32 abort_source;
+ int irq;
+ int swab;
+ struct i2c_adapter adapter;
+ u32 functionality;
+ u32 master_cfg;
+ unsigned int tx_fifo_depth;
+ unsigned int rx_fifo_depth;
+};
+
+extern u32 dw_readl(struct dw_i2c_dev *dev, int offset);
+extern void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
+extern int i2c_dw_init(struct dw_i2c_dev *dev);
+extern int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num);
+extern u32 i2c_dw_func(struct i2c_adapter *adap);
+extern irqreturn_t i2c_dw_isr(int this_irq, void *dev_id);
+extern void i2c_dw_enable(struct dw_i2c_dev *dev);
+extern u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev);
+extern void i2c_dw_disable(struct dw_i2c_dev *dev);
+extern void i2c_dw_clear_int(struct dw_i2c_dev *dev);
+extern void i2c_dw_disable_int(struct dw_i2c_dev *dev);
+extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
new file mode 100644
index 000000000000..9e89e7313d62
--- /dev/null
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -0,0 +1,392 @@
+/*
+ * Synopsys DesignWare I2C adapter driver (master only).
+ *
+ * Based on the TI DAVINCI I2C adapter driver.
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2007 MontaVista Software Inc.
+ * Copyright (C) 2009 Provigent Ltd.
+ * Copyright (C) 2011 Intel corporation.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include "i2c-designware-core.h"
+
+#define DRIVER_NAME "i2c-designware-pci"
+
+enum dw_pci_ctl_id_t {
+ moorestown_0,
+ moorestown_1,
+ moorestown_2,
+
+ medfield_0,
+ medfield_1,
+ medfield_2,
+ medfield_3,
+ medfield_4,
+ medfield_5,
+};
+
+struct dw_pci_controller {
+ u32 bus_num;
+ u32 bus_cfg;
+ u32 tx_fifo_depth;
+ u32 rx_fifo_depth;
+ u32 clk_khz;
+};
+
+#define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \
+ DW_IC_CON_SLAVE_DISABLE | \
+ DW_IC_CON_RESTART_EN)
+
+static struct dw_pci_controller dw_pci_controllers[] = {
+ [moorestown_0] = {
+ .bus_num = 0,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [moorestown_1] = {
+ .bus_num = 1,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [moorestown_2] = {
+ .bus_num = 2,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_0] = {
+ .bus_num = 0,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_1] = {
+ .bus_num = 1,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_2] = {
+ .bus_num = 2,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_3] = {
+ .bus_num = 3,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_STD,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_4] = {
+ .bus_num = 4,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_5] = {
+ .bus_num = 5,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+};
+static struct i2c_algorithm i2c_dw_algo = {
+ .master_xfer = i2c_dw_xfer,
+ .functionality = i2c_dw_func,
+};
+
+static int i2c_dw_pci_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct dw_i2c_dev *i2c = pci_get_drvdata(pdev);
+ int err;
+
+
+ i2c_dw_disable(i2c);
+
+ err = pci_save_state(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_save_state failed\n");
+ return err;
+ }
+
+ err = pci_set_power_state(pdev, PCI_D3hot);
+ if (err) {
+ dev_err(&pdev->dev, "pci_set_power_state failed\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int i2c_dw_pci_resume(struct device *dev)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct dw_i2c_dev *i2c = pci_get_drvdata(pdev);
+ int err;
+ u32 enabled;
+
+ enabled = i2c_dw_is_enabled(i2c);
+ if (enabled)
+ return 0;
+
+ err = pci_set_power_state(pdev, PCI_D0);
+ if (err) {
+ dev_err(&pdev->dev, "pci_set_power_state() failed\n");
+ return err;
+ }
+
+ pci_restore_state(pdev);
+
+ i2c_dw_init(i2c);
+ i2c_dw_enable(i2c);
+ return 0;
+}
+
+static int i2c_dw_pci_runtime_idle(struct device *dev)
+{
+ int err = pm_schedule_suspend(dev, 500);
+ dev_dbg(dev, "runtime_idle called\n");
+
+ if (err != 0)
+ return 0;
+ return -EBUSY;
+}
+
+static const struct dev_pm_ops i2c_dw_pm_ops = {
+ .resume = i2c_dw_pci_resume,
+ .suspend = i2c_dw_pci_suspend,
+ SET_RUNTIME_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume,
+ i2c_dw_pci_runtime_idle)
+};
+
+static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
+{
+ return dev->controller->clk_khz;
+}
+
+static int __devinit i2c_dw_pci_probe(struct pci_dev *pdev,
+const struct pci_device_id *id)
+{
+ struct dw_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ unsigned long start, len;
+ void __iomem *base;
+ int r;
+ struct dw_pci_controller *controller;
+
+ if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) {
+ printk(KERN_ERR "dw_i2c_pci_probe: invalid driver data %ld\n",
+ id->driver_data);
+ return -EINVAL;
+ }
+
+ controller = &dw_pci_controllers[id->driver_data];
+
+ r = pci_enable_device(pdev);
+ if (r) {
+ dev_err(&pdev->dev, "Failed to enable I2C PCI device (%d)\n",
+ r);
+ goto exit;
+ }
+
+ /* Determine the address of the I2C area */
+ start = pci_resource_start(pdev, 0);
+ len = pci_resource_len(pdev, 0);
+ if (!start || len == 0) {
+ dev_err(&pdev->dev, "base address not set\n");
+ r = -ENODEV;
+ goto exit;
+ }
+
+ r = pci_request_region(pdev, 0, DRIVER_NAME);
+ if (r) {
+ dev_err(&pdev->dev, "failed to request I2C region "
+ "0x%lx-0x%lx\n", start,
+ (unsigned long)pci_resource_end(pdev, 0));
+ goto exit;
+ }
+
+ base = ioremap_nocache(start, len);
+ if (!base) {
+ dev_err(&pdev->dev, "I/O memory remapping failed\n");
+ r = -ENOMEM;
+ goto err_release_region;
+ }
+
+
+ dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ r = -ENOMEM;
+ goto err_release_region;
+ }
+
+ init_completion(&dev->cmd_complete);
+ mutex_init(&dev->lock);
+ dev->clk = NULL;
+ dev->controller = controller;
+ dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
+ dev->base = base;
+ dev->dev = get_device(&pdev->dev);
+ dev->functionality =
+ I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
+ dev->master_cfg = controller->bus_cfg;
+
+ pci_set_drvdata(pdev, dev);
+
+ dev->tx_fifo_depth = controller->tx_fifo_depth;
+ dev->rx_fifo_depth = controller->rx_fifo_depth;
+ r = i2c_dw_init(dev);
+ if (r)
+ goto err_iounmap;
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = 0;
+ adap->algo = &i2c_dw_algo;
+ adap->dev.parent = &pdev->dev;
+ adap->nr = controller->bus_num;
+ snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci-%d",
+ adap->nr);
+
+ r = request_irq(pdev->irq, i2c_dw_isr, IRQF_SHARED, adap->name, dev);
+ if (r) {
+ dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
+ goto err_iounmap;
+ }
+
+ i2c_dw_disable_int(dev);
+ i2c_dw_clear_int(dev);
+ r = i2c_add_numbered_adapter(adap);
+ if (r) {
+ dev_err(&pdev->dev, "failure adding adapter\n");
+ goto err_free_irq;
+ }
+
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
+
+ return 0;
+
+err_free_irq:
+ free_irq(pdev->irq, dev);
+err_iounmap:
+ iounmap(dev->base);
+ pci_set_drvdata(pdev, NULL);
+ put_device(&pdev->dev);
+ kfree(dev);
+err_release_region:
+ pci_release_region(pdev, 0);
+exit:
+ return r;
+}
+
+static void __devexit i2c_dw_pci_remove(struct pci_dev *pdev)
+{
+ struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
+
+ i2c_dw_disable(dev);
+ pm_runtime_forbid(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+
+ pci_set_drvdata(pdev, NULL);
+ i2c_del_adapter(&dev->adapter);
+ put_device(&pdev->dev);
+
+ free_irq(dev->irq, dev);
+ kfree(dev);
+ pci_release_region(pdev, 0);
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("i2c_designware-pci");
+
+DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = {
+ /* Moorestown */
+ { PCI_VDEVICE(INTEL, 0x0802), moorestown_0 },
+ { PCI_VDEVICE(INTEL, 0x0803), moorestown_1 },
+ { PCI_VDEVICE(INTEL, 0x0804), moorestown_2 },
+ /* Medfield */
+ { PCI_VDEVICE(INTEL, 0x0817), medfield_3,},
+ { PCI_VDEVICE(INTEL, 0x0818), medfield_4 },
+ { PCI_VDEVICE(INTEL, 0x0819), medfield_5 },
+ { PCI_VDEVICE(INTEL, 0x082C), medfield_0 },
+ { PCI_VDEVICE(INTEL, 0x082D), medfield_1 },
+ { PCI_VDEVICE(INTEL, 0x082E), medfield_2 },
+ { 0,}
+};
+MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
+
+static struct pci_driver dw_i2c_driver = {
+ .name = DRIVER_NAME,
+ .id_table = i2_designware_pci_ids,
+ .probe = i2c_dw_pci_probe,
+ .remove = __devexit_p(i2c_dw_pci_remove),
+ .driver = {
+ .pm = &i2c_dw_pm_ops,
+ },
+};
+
+static int __init dw_i2c_init_driver(void)
+{
+ return pci_register_driver(&dw_i2c_driver);
+}
+module_init(dw_i2c_init_driver);
+
+static void __exit dw_i2c_exit_driver(void)
+{
+ pci_unregister_driver(&dw_i2c_driver);
+}
+module_exit(dw_i2c_exit_driver);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
new file mode 100644
index 000000000000..2d3657ab1258
--- /dev/null
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -0,0 +1,215 @@
+/*
+ * Synopsys DesignWare I2C adapter driver (master only).
+ *
+ * Based on the TI DAVINCI I2C adapter driver.
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2007 MontaVista Software Inc.
+ * Copyright (C) 2009 Provigent Ltd.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ----------------------------------------------------------------------------
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "i2c-designware-core.h"
+
+static struct i2c_algorithm i2c_dw_algo = {
+ .master_xfer = i2c_dw_xfer,
+ .functionality = i2c_dw_func,
+};
+static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
+{
+ return clk_get_rate(dev->clk)/1000;
+}
+
+static int __devinit dw_i2c_probe(struct platform_device *pdev)
+{
+ struct dw_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ struct resource *mem, *ioarea;
+ int irq, r;
+
+ /* NOTE: driver uses the static register mapping */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return irq; /* -ENXIO */
+ }
+
+ ioarea = request_mem_region(mem->start, resource_size(mem),
+ pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "I2C region already claimed\n");
+ return -EBUSY;
+ }
+
+ dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ r = -ENOMEM;
+ goto err_release_region;
+ }
+
+ init_completion(&dev->cmd_complete);
+ mutex_init(&dev->lock);
+ dev->dev = get_device(&pdev->dev);
+ dev->irq = irq;
+ platform_set_drvdata(pdev, dev);
+
+ dev->clk = clk_get(&pdev->dev, NULL);
+ dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
+
+ if (IS_ERR(dev->clk)) {
+ r = -ENODEV;
+ goto err_free_mem;
+ }
+ clk_enable(dev->clk);
+
+ dev->functionality =
+ I2C_FUNC_I2C |
+ I2C_FUNC_10BIT_ADDR |
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
+ dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
+ DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
+
+ dev->base = ioremap(mem->start, resource_size(mem));
+ if (dev->base == NULL) {
+ dev_err(&pdev->dev, "failure mapping io resources\n");
+ r = -EBUSY;
+ goto err_unuse_clocks;
+ }
+ {
+ u32 param1 = i2c_dw_read_comp_param(dev);
+
+ dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;
+ dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1;
+ }
+ r = i2c_dw_init(dev);
+ if (r)
+ goto err_iounmap;
+
+ i2c_dw_disable_int(dev);
+ r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev);
+ if (r) {
+ dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
+ goto err_iounmap;
+ }
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
+ sizeof(adap->name));
+ adap->algo = &i2c_dw_algo;
+ adap->dev.parent = &pdev->dev;
+
+ adap->nr = pdev->id;
+ r = i2c_add_numbered_adapter(adap);
+ if (r) {
+ dev_err(&pdev->dev, "failure adding adapter\n");
+ goto err_free_irq;
+ }
+
+ return 0;
+
+err_free_irq:
+ free_irq(dev->irq, dev);
+err_iounmap:
+ iounmap(dev->base);
+err_unuse_clocks:
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ dev->clk = NULL;
+err_free_mem:
+ platform_set_drvdata(pdev, NULL);
+ put_device(&pdev->dev);
+ kfree(dev);
+err_release_region:
+ release_mem_region(mem->start, resource_size(mem));
+
+ return r;
+}
+
+static int __devexit dw_i2c_remove(struct platform_device *pdev)
+{
+ struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ i2c_del_adapter(&dev->adapter);
+ put_device(&pdev->dev);
+
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ dev->clk = NULL;
+
+ i2c_dw_disable(dev);
+ free_irq(dev->irq, dev);
+ kfree(dev);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, resource_size(mem));
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:i2c_designware");
+
+static struct platform_driver dw_i2c_driver = {
+ .remove = __devexit_p(dw_i2c_remove),
+ .driver = {
+ .name = "i2c_designware",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init dw_i2c_init_driver(void)
+{
+ return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
+}
+module_init(dw_i2c_init_driver);
+
+static void __exit dw_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&dw_i2c_driver);
+}
+module_exit(dw_i2c_exit_driver);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index 8abfa4a03ce1..8cebef49aeac 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -64,6 +64,7 @@
#define TEN_BIT_ADDR_DEFAULT 0xF000
#define TEN_BIT_ADDR_MASK 0xF0
#define PCH_START 0x0020
+#define PCH_RESTART 0x0004
#define PCH_ESR_START 0x0001
#define PCH_BUFF_START 0x1
#define PCH_REPSTART 0x0004
@@ -273,23 +274,24 @@ static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
s32 timeout)
{
void __iomem *p = adap->pch_base_address;
+ ktime_t ns_val;
+
+ if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
+ return 0;
/* MAX timeout value is timeout*1000*1000nsec */
- ktime_t ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000);
+ ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000);
do {
- if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
- break;
msleep(20);
+ if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
+ return 0;
} while (ktime_lt(ktime_get(), ns_val));
pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR));
+ pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME);
+ pch_i2c_init(adap);
- if (timeout == 0) {
- pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME);
- return -ETIME;
- }
-
- return 0;
+ return -ETIME;
}
/**
@@ -311,21 +313,19 @@ static void pch_i2c_start(struct i2c_algo_pch_data *adap)
*/
static s32 pch_i2c_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
{
- s32 ret;
+ long ret;
ret = wait_event_timeout(pch_event,
(adap->pch_event_flag != 0), msecs_to_jiffies(50));
- if (ret < 0) {
- pch_err(adap, "timeout: %x\n", adap->pch_event_flag);
- return ret;
- }
if (ret == 0) {
pch_err(adap, "timeout: %x\n", adap->pch_event_flag);
+ adap->pch_event_flag = 0;
return -ETIMEDOUT;
}
if (adap->pch_event_flag & I2C_ERROR_MASK) {
pch_err(adap, "error bits set: %x\n", adap->pch_event_flag);
+ adap->pch_event_flag = 0;
return -EIO;
}
@@ -394,6 +394,7 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
u32 addr_2_msb;
u32 addr_8_lsb;
s32 wrcount;
+ s32 rtn;
void __iomem *p = adap->pch_base_address;
length = msgs->len;
@@ -412,15 +413,29 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
}
if (msgs->flags & I2C_M_TEN) {
- addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7);
+ addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7) & 0x06;
iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
if (first)
pch_i2c_start(adap);
- if (pch_i2c_wait_for_xfer_complete(adap) == 0 &&
- pch_i2c_getack(adap) == 0) {
+
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
+ return -EIO;
+ }
addr_8_lsb = (addr & I2C_ADDR_MSK);
iowrite32(addr_8_lsb, p + PCH_I2CDR);
- } else {
+ } else if (rtn == -EIO) { /* Arbitration Lost */
+ pch_err(adap, "Lost Arbitration\n");
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMAL_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMIF_BIT);
+ pch_i2c_init(adap);
+ return -EAGAIN;
+ } else { /* wait-event timeout */
pch_i2c_stop(adap);
return -ETIME;
}
@@ -431,30 +446,51 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
pch_i2c_start(adap);
}
- if ((pch_i2c_wait_for_xfer_complete(adap) == 0) &&
- (pch_i2c_getack(adap) == 0)) {
- for (wrcount = 0; wrcount < length; ++wrcount) {
- /* write buffer value to I2C data register */
- iowrite32(buf[wrcount], p + PCH_I2CDR);
- pch_dbg(adap, "writing %x to Data register\n",
- buf[wrcount]);
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
+ return -EIO;
+ }
+ } else if (rtn == -EIO) { /* Arbitration Lost */
+ pch_err(adap, "Lost Arbitration\n");
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT);
+ pch_i2c_init(adap);
+ return -EAGAIN;
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
+ }
- if (pch_i2c_wait_for_xfer_complete(adap) != 0)
- return -ETIME;
+ for (wrcount = 0; wrcount < length; ++wrcount) {
+ /* write buffer value to I2C data register */
+ iowrite32(buf[wrcount], p + PCH_I2CDR);
+ pch_dbg(adap, "writing %x to Data register\n", buf[wrcount]);
- if (pch_i2c_getack(adap))
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
return -EIO;
+ }
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMCF_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMIF_BIT);
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
}
+ }
- /* check if this is the last message */
- if (last)
- pch_i2c_stop(adap);
- else
- pch_i2c_repstart(adap);
- } else {
+ /* check if this is the last message */
+ if (last)
pch_i2c_stop(adap);
- return -EIO;
- }
+ else
+ pch_i2c_repstart(adap);
pch_dbg(adap, "return=%d\n", wrcount);
@@ -484,6 +520,19 @@ static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap)
}
/**
+ * pch_i2c_restart() - Generate I2C restart condition in normal mode.
+ * @adap: Pointer to struct i2c_algo_pch_data.
+ *
+ * Generate I2C restart condition in normal mode by setting I2CCTL.I2CRSTA.
+ */
+static void pch_i2c_restart(struct i2c_algo_pch_data *adap)
+{
+ void __iomem *p = adap->pch_base_address;
+ pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL));
+ pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_RESTART);
+}
+
+/**
* pch_i2c_readbytes() - read data from I2C bus in normal mode.
* @i2c_adap: Pointer to the struct i2c_adapter.
* @msgs: Pointer to i2c_msg structure.
@@ -500,7 +549,9 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
u32 length;
u32 addr;
u32 addr_2_msb;
+ u32 addr_8_lsb;
void __iomem *p = adap->pch_base_address;
+ s32 rtn;
length = msgs->len;
buf = msgs->buf;
@@ -515,9 +566,55 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
}
if (msgs->flags & I2C_M_TEN) {
- addr_2_msb = (((addr & I2C_MSB_2B_MSK) >> 7) | (I2C_RD));
+ addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7);
iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+ if (first)
+ pch_i2c_start(adap);
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
+ return -EIO;
+ }
+ addr_8_lsb = (addr & I2C_ADDR_MSK);
+ iowrite32(addr_8_lsb, p + PCH_I2CDR);
+ } else if (rtn == -EIO) { /* Arbitration Lost */
+ pch_err(adap, "Lost Arbitration\n");
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMAL_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMIF_BIT);
+ pch_i2c_init(adap);
+ return -EAGAIN;
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
+ }
+ pch_i2c_restart(adap);
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
+ return -EIO;
+ }
+ addr_2_msb |= I2C_RD;
+ iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK,
+ p + PCH_I2CDR);
+ } else if (rtn == -EIO) { /* Arbitration Lost */
+ pch_err(adap, "Lost Arbitration\n");
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMAL_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMIF_BIT);
+ pch_i2c_init(adap);
+ return -EAGAIN;
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
+ }
} else {
/* 7 address bits + R/W bit */
addr = (((addr) << 1) | (I2C_RD));
@@ -528,56 +625,81 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
if (first)
pch_i2c_start(adap);
- if ((pch_i2c_wait_for_xfer_complete(adap) == 0) &&
- (pch_i2c_getack(adap) == 0)) {
- pch_dbg(adap, "return %d\n", 0);
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
+ return -EIO;
+ }
+ } else if (rtn == -EIO) { /* Arbitration Lost */
+ pch_err(adap, "Lost Arbitration\n");
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT);
+ pch_i2c_init(adap);
+ return -EAGAIN;
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
+ }
- if (length == 0) {
- pch_i2c_stop(adap);
- ioread32(p + PCH_I2CDR); /* Dummy read needs */
+ if (length == 0) {
+ pch_i2c_stop(adap);
+ ioread32(p + PCH_I2CDR); /* Dummy read needs */
- count = length;
- } else {
- int read_index;
- int loop;
- pch_i2c_sendack(adap);
+ count = length;
+ } else {
+ int read_index;
+ int loop;
+ pch_i2c_sendack(adap);
- /* Dummy read */
- for (loop = 1, read_index = 0; loop < length; loop++) {
- buf[read_index] = ioread32(p + PCH_I2CDR);
+ /* Dummy read */
+ for (loop = 1, read_index = 0; loop < length; loop++) {
+ buf[read_index] = ioread32(p + PCH_I2CDR);
- if (loop != 1)
- read_index++;
+ if (loop != 1)
+ read_index++;
- if (pch_i2c_wait_for_xfer_complete(adap) != 0) {
- pch_i2c_stop(adap);
- return -ETIME;
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave"
+ "address setting\n");
+ return -EIO;
}
- } /* end for */
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
+ }
- pch_i2c_sendnack(adap);
+ } /* end for */
- buf[read_index] = ioread32(p + PCH_I2CDR);
+ pch_i2c_sendnack(adap);
- if (length != 1)
- read_index++;
+ buf[read_index] = ioread32(p + PCH_I2CDR); /* Read final - 1 */
- if (pch_i2c_wait_for_xfer_complete(adap) == 0) {
- if (last)
- pch_i2c_stop(adap);
- else
- pch_i2c_repstart(adap);
+ if (length != 1)
+ read_index++;
- buf[read_index++] = ioread32(p + PCH_I2CDR);
- count = read_index;
- } else {
- count = -ETIME;
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave"
+ "address setting\n");
+ return -EIO;
}
-
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
}
- } else {
- count = -ETIME;
- pch_i2c_stop(adap);
+
+ if (last)
+ pch_i2c_stop(adap);
+ else
+ pch_i2c_repstart(adap);
+
+ buf[read_index++] = ioread32(p + PCH_I2CDR); /* Read Final */
+ count = read_index;
}
return count;
@@ -673,32 +795,33 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap,
/* transfer not completed */
adap->pch_i2c_xfer_in_progress = true;
- pmsg = &msgs[0];
- pmsg->flags |= adap->pch_buff_mode_en;
- status = pmsg->flags;
- pch_dbg(adap,
- "After invoking I2C_MODE_SEL :flag= 0x%x\n", status);
- /* calculate sub address length and message length */
- /* these are applicable only for buffer mode */
- subaddrlen = pmsg->buf[0];
- /* calculate actual message length excluding
- * the sub address fields */
- msglen = (pmsg->len) - (subaddrlen + 1);
- if (status & (I2C_M_RD)) {
- pch_dbg(adap, "invoking pch_i2c_readbytes\n");
- ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num),
- (i == 0));
- } else {
- pch_dbg(adap, "invoking pch_i2c_writebytes\n");
- ret = pch_i2c_writebytes(i2c_adap, pmsg, (i + 1 == num),
- (i == 0));
+ for (i = 0; i < num && ret >= 0; i++) {
+ pmsg = &msgs[i];
+ pmsg->flags |= adap->pch_buff_mode_en;
+ status = pmsg->flags;
+ pch_dbg(adap,
+ "After invoking I2C_MODE_SEL :flag= 0x%x\n", status);
+ /* calculate sub address length and message length */
+ /* these are applicable only for buffer mode */
+ subaddrlen = pmsg->buf[0];
+ /* calculate actual message length excluding
+ * the sub address fields */
+ msglen = (pmsg->len) - (subaddrlen + 1);
+
+ if ((status & (I2C_M_RD)) != false) {
+ ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num),
+ (i == 0));
+ } else {
+ ret = pch_i2c_writebytes(i2c_adap, pmsg, (i + 1 == num),
+ (i == 0));
+ }
}
adap->pch_i2c_xfer_in_progress = false; /* transfer completed */
mutex_unlock(&pch_mutex);
- return ret;
+ return (ret < 0) ? ret : num;
}
/**
diff --git a/drivers/i2c/busses/i2c-highlander.c b/drivers/i2c/busses/i2c-highlander.c
index 3df1bc80f37a..63bb1cc2a042 100644
--- a/drivers/i2c/busses/i2c-highlander.c
+++ b/drivers/i2c/busses/i2c-highlander.c
@@ -227,7 +227,7 @@ static int highlander_i2c_read(struct highlander_i2c_dev *dev)
/*
* The R0P7780LC0011RL FPGA needs a significant delay between
- * data read cycles, otherwise the transciever gets confused and
+ * data read cycles, otherwise the transceiver gets confused and
* garbage is returned when the read is subsequently aborted.
*
* It is not sufficient to wait for BBSY.
@@ -387,7 +387,7 @@ static int __devinit highlander_i2c_probe(struct platform_device *pdev)
dev->irq = 0;
if (dev->irq) {
- ret = request_irq(dev->irq, highlander_i2c_irq, IRQF_DISABLED,
+ ret = request_irq(dev->irq, highlander_i2c_irq, 0,
pdev->name, dev);
if (unlikely(ret))
goto err_unmap;
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 4c2a62b75b5c..58832e578fff 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -48,6 +48,9 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_i2c.h>
#include <mach/irqs.h>
#include <mach/hardware.h>
@@ -125,6 +128,11 @@ struct imx_i2c_struct {
unsigned int ifdr; /* IMX_I2C_IFDR */
};
+static const struct of_device_id i2c_imx_dt_ids[] = {
+ { .compatible = "fsl,imx1-i2c", },
+ { /* sentinel */ }
+};
+
/** Functions for IMX I2C adapter driver ***************************************
*******************************************************************************/
@@ -466,10 +474,10 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
{
struct imx_i2c_struct *i2c_imx;
struct resource *res;
- struct imxi2c_platform_data *pdata;
+ struct imxi2c_platform_data *pdata = pdev->dev.platform_data;
void __iomem *base;
resource_size_t res_size;
- int irq;
+ int irq, bitrate;
int ret;
dev_dbg(&pdev->dev, "<%s>\n", __func__);
@@ -485,19 +493,11 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
return -ENOENT;
}
- pdata = pdev->dev.platform_data;
-
- if (pdata && pdata->init) {
- ret = pdata->init(&pdev->dev);
- if (ret)
- return ret;
- }
-
res_size = resource_size(res);
if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {
- ret = -EBUSY;
- goto fail0;
+ dev_err(&pdev->dev, "request_mem_region failed\n");
+ return -EBUSY;
}
base = ioremap(res->start, res_size);
@@ -520,6 +520,7 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
i2c_imx->adapter.algo = &i2c_imx_algo;
i2c_imx->adapter.dev.parent = &pdev->dev;
i2c_imx->adapter.nr = pdev->id;
+ i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
i2c_imx->irq = irq;
i2c_imx->base = base;
i2c_imx->res = res;
@@ -546,10 +547,12 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
/* Set up clock divider */
- if (pdata && pdata->bitrate)
- i2c_imx_set_clk(i2c_imx, pdata->bitrate);
- else
- i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);
+ bitrate = IMX_I2C_BIT_RATE;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &bitrate);
+ if (ret < 0 && pdata && pdata->bitrate)
+ bitrate = pdata->bitrate;
+ i2c_imx_set_clk(i2c_imx, bitrate);
/* Set up chip registers to defaults */
writeb(0, i2c_imx->base + IMX_I2C_I2CR);
@@ -562,6 +565,8 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
goto fail5;
}
+ of_i2c_register_devices(&i2c_imx->adapter);
+
/* Set up platform driver data */
platform_set_drvdata(pdev, i2c_imx);
@@ -586,16 +591,12 @@ fail2:
iounmap(base);
fail1:
release_mem_region(res->start, resource_size(res));
-fail0:
- if (pdata && pdata->exit)
- pdata->exit(&pdev->dev);
return ret; /* Return error number */
}
static int __exit i2c_imx_remove(struct platform_device *pdev)
{
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
- struct imxi2c_platform_data *pdata = pdev->dev.platform_data;
/* remove adapter */
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
@@ -611,10 +612,6 @@ static int __exit i2c_imx_remove(struct platform_device *pdev)
writeb(0, i2c_imx->base + IMX_I2C_I2CR);
writeb(0, i2c_imx->base + IMX_I2C_I2SR);
- /* Shut down hardware */
- if (pdata && pdata->exit)
- pdata->exit(&pdev->dev);
-
clk_put(i2c_imx->clk);
iounmap(i2c_imx->base);
@@ -628,6 +625,7 @@ static struct platform_driver i2c_imx_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = i2c_imx_dt_ids,
}
};
diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c
index 5d8aed5ec21b..c01e9519f6c1 100644
--- a/drivers/i2c/busses/i2c-ixp2000.c
+++ b/drivers/i2c/busses/i2c-ixp2000.c
@@ -35,7 +35,7 @@
#include <linux/slab.h>
#include <mach/hardware.h> /* Pick up IXP2000-specific bits */
-#include <mach/gpio.h>
+#include <mach/gpio-ixp2000.h>
static inline int ixp2000_scl_pin(void *data)
{
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 0c731ca69f15..5267ab93d550 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -63,11 +63,11 @@
/* Master controller (MCR) register */
#define I2C_MCR_OP (0x1 << 0) /* Operation */
#define I2C_MCR_A7 (0x7f << 1) /* 7-bit address */
-#define I2C_MCR_EA10 (0x7 << 8) /* 10-bit Extended address */
+#define I2C_MCR_EA10 (0x7 << 8) /* 10-bit Extended address */
#define I2C_MCR_SB (0x1 << 11) /* Extended address */
#define I2C_MCR_AM (0x3 << 12) /* Address type */
-#define I2C_MCR_STOP (0x1 << 14) /* Stop condition */
-#define I2C_MCR_LENGTH (0x7ff << 15) /* Transaction length */
+#define I2C_MCR_STOP (0x1 << 14) /* Stop condition */
+#define I2C_MCR_LENGTH (0x7ff << 15) /* Transaction length */
/* Status register (SR) */
#define I2C_SR_OP (0x3 << 0) /* Operation */
@@ -77,7 +77,7 @@
#define I2C_SR_LENGTH (0x7ff << 9) /* Transfer length */
/* Interrupt mask set/clear (IMSCR) bits */
-#define I2C_IT_TXFE (0x1 << 0)
+#define I2C_IT_TXFE (0x1 << 0)
#define I2C_IT_TXFNE (0x1 << 1)
#define I2C_IT_TXFF (0x1 << 2)
#define I2C_IT_TXFOVR (0x1 << 3)
@@ -135,30 +135,31 @@ struct i2c_nmk_client {
};
/**
- * struct nmk_i2c_dev - private data structure of the controller
- * @pdev: parent platform device
- * @adap: corresponding I2C adapter
- * @irq: interrupt line for the controller
- * @virtbase: virtual io memory area
- * @clk: hardware i2c block clock
- * @cfg: machine provided controller configuration
- * @cli: holder of client specific data
- * @stop: stop condition
- * @xfer_complete: acknowledge completion for a I2C message
- * @result: controller propogated result
- * @busy: Busy doing transfer
+ * struct nmk_i2c_dev - private data structure of the controller.
+ * @pdev: parent platform device.
+ * @adap: corresponding I2C adapter.
+ * @irq: interrupt line for the controller.
+ * @virtbase: virtual io memory area.
+ * @clk: hardware i2c block clock.
+ * @cfg: machine provided controller configuration.
+ * @cli: holder of client specific data.
+ * @stop: stop condition.
+ * @xfer_complete: acknowledge completion for a I2C message.
+ * @result: controller propogated result.
+ * @regulator: pointer to i2c regulator.
+ * @busy: Busy doing transfer.
*/
struct nmk_i2c_dev {
struct platform_device *pdev;
- struct i2c_adapter adap;
- int irq;
+ struct i2c_adapter adap;
+ int irq;
void __iomem *virtbase;
struct clk *clk;
struct nmk_i2c_controller cfg;
struct i2c_nmk_client cli;
- int stop;
+ int stop;
struct completion xfer_complete;
- int result;
+ int result;
struct regulator *regulator;
bool busy;
};
@@ -216,8 +217,9 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
}
}
- dev_err(&dev->pdev->dev, "flushing operation timed out "
- "giving up after %d attempts", LOOP_ATTEMPTS);
+ dev_err(&dev->pdev->dev,
+ "flushing operation timed out giving up after %d attempts",
+ LOOP_ATTEMPTS);
return -ETIMEDOUT;
}
@@ -269,7 +271,7 @@ exit:
}
/* enable peripheral, master mode operation */
-#define DEFAULT_I2C_REG_CR ((1 << 1) | I2C_CR_PE)
+#define DEFAULT_I2C_REG_CR ((1 << 1) | I2C_CR_PE)
/**
* load_i2c_mcr_reg() - load the MCR register
@@ -362,8 +364,8 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
* and high speed (up to 3.4 Mb/s)
*/
if (dev->cfg.sm > I2C_FREQ_MODE_FAST) {
- dev_err(&dev->pdev->dev, "do not support this mode "
- "defaulting to std. mode\n");
+ dev_err(&dev->pdev->dev,
+ "do not support this mode defaulting to std. mode\n");
brcr2 = i2c_clk/(100000 * 2) & 0xffff;
writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR);
writel(I2C_FREQ_MODE_STANDARD << 4,
@@ -417,12 +419,12 @@ static int read_i2c(struct nmk_i2c_dev *dev)
writel(readl(dev->virtbase + I2C_IMSCR) | irq_mask,
dev->virtbase + I2C_IMSCR);
- timeout = wait_for_completion_interruptible_timeout(
+ timeout = wait_for_completion_timeout(
&dev->xfer_complete, dev->adap.timeout);
if (timeout < 0) {
dev_err(&dev->pdev->dev,
- "wait_for_completion_interruptible_timeout"
+ "wait_for_completion_timeout "
"returned %d waiting for event\n", timeout);
status = timeout;
}
@@ -504,12 +506,12 @@ static int write_i2c(struct nmk_i2c_dev *dev)
writel(readl(dev->virtbase + I2C_IMSCR) | irq_mask,
dev->virtbase + I2C_IMSCR);
- timeout = wait_for_completion_interruptible_timeout(
+ timeout = wait_for_completion_timeout(
&dev->xfer_complete, dev->adap.timeout);
if (timeout < 0) {
dev_err(&dev->pdev->dev,
- "wait_for_completion_interruptible_timeout"
+ "wait_for_completion_timeout "
"returned %d waiting for event\n", timeout);
status = timeout;
}
@@ -555,8 +557,8 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
if (((i2c_sr >> 2) & 0x3) == 0x3) {
/* get the abort cause */
cause = (i2c_sr >> 4) & 0x7;
- dev_err(&dev->pdev->dev, "%s\n", cause
- >= ARRAY_SIZE(abort_causes) ?
+ dev_err(&dev->pdev->dev, "%s\n",
+ cause >= ARRAY_SIZE(abort_causes) ?
"unknown reason" :
abort_causes[cause]);
}
@@ -581,13 +583,13 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
*
* NOTE:
* READ TRANSFER : We impose a restriction of the first message to be the
- * index message for any read transaction.
- * - a no index is coded as '0',
- * - 2byte big endian index is coded as '3'
- * !!! msg[0].buf holds the actual index.
- * This is compatible with generic messages of smbus emulator
- * that send a one byte index.
- * eg. a I2C transation to read 2 bytes from index 0
+ * index message for any read transaction.
+ * - a no index is coded as '0',
+ * - 2byte big endian index is coded as '3'
+ * !!! msg[0].buf holds the actual index.
+ * This is compatible with generic messages of smbus emulator
+ * that send a one byte index.
+ * eg. a I2C transation to read 2 bytes from index 0
* idx = 0;
* msg[0].addr = client->addr;
* msg[0].flags = 0x0;
@@ -643,8 +645,8 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
for (i = 0; i < num_msgs; i++) {
if (unlikely(msgs[i].flags & I2C_M_TEN)) {
- dev_err(&dev->pdev->dev, "10 bit addressing"
- "not supported\n");
+ dev_err(&dev->pdev->dev,
+ "10 bit addressing not supported\n");
status = -EINVAL;
goto out;
@@ -788,8 +790,9 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
if (dev->cli.count) {
dev->result = -EIO;
- dev_err(&dev->pdev->dev, "%lu bytes still remain to be"
- "xfered\n", dev->cli.count);
+ dev_err(&dev->pdev->dev,
+ "%lu bytes still remain to be xfered\n",
+ dev->cli.count);
(void) init_hw(dev);
}
complete(&dev->xfer_complete);
@@ -922,7 +925,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
}
if (request_mem_region(res->start, resource_size(res),
- DRIVER_NAME "I/O region") == NULL) {
+ DRIVER_NAME "I/O region") == NULL) {
ret = -EBUSY;
goto err_no_region;
}
@@ -934,7 +937,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
}
dev->irq = platform_get_irq(pdev, 0);
- ret = request_irq(dev->irq, i2c_irq_handler, IRQF_DISABLED,
+ ret = request_irq(dev->irq, i2c_irq_handler, 0,
DRIVER_NAME, dev);
if (ret) {
dev_err(&pdev->dev, "cannot claim the irq %d\n", dev->irq);
@@ -979,8 +982,9 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(adap, dev);
- dev_info(&pdev->dev, "initialize %s on virtual "
- "base %p\n", adap->name, dev->virtbase);
+ dev_info(&pdev->dev,
+ "initialize %s on virtual base %p\n",
+ adap->name, dev->virtbase);
ret = i2c_add_numbered_adapter(adap);
if (ret) {
diff --git a/drivers/i2c/busses/i2c-nuc900.c b/drivers/i2c/busses/i2c-nuc900.c
index 72434263787b..835e47b39bc2 100644
--- a/drivers/i2c/busses/i2c-nuc900.c
+++ b/drivers/i2c/busses/i2c-nuc900.c
@@ -610,7 +610,7 @@ static int __devinit nuc900_i2c_probe(struct platform_device *pdev)
goto err_iomap;
}
- ret = request_irq(i2c->irq, nuc900_i2c_irq, IRQF_DISABLED | IRQF_SHARED,
+ ret = request_irq(i2c->irq, nuc900_i2c_irq, IRQF_SHARED,
dev_name(&pdev->dev), i2c);
if (ret != 0) {
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 58a58c7eaa17..a43d0023446a 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -42,12 +42,12 @@
#include <linux/pm_runtime.h>
/* I2C controller revisions */
-#define OMAP_I2C_REV_2 0x20
+#define OMAP_I2C_OMAP1_REV_2 0x20
/* I2C controller revisions present on specific hardware */
#define OMAP_I2C_REV_ON_2430 0x36
#define OMAP_I2C_REV_ON_3430 0x3C
-#define OMAP_I2C_REV_ON_4430 0x40
+#define OMAP_I2C_REV_ON_3530_4430 0x40
/* timeout waiting for the controller to respond */
#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
@@ -72,11 +72,12 @@ enum {
OMAP_I2C_SCLH_REG,
OMAP_I2C_SYSTEST_REG,
OMAP_I2C_BUFSTAT_REG,
- OMAP_I2C_REVNB_LO,
- OMAP_I2C_REVNB_HI,
- OMAP_I2C_IRQSTATUS_RAW,
- OMAP_I2C_IRQENABLE_SET,
- OMAP_I2C_IRQENABLE_CLR,
+ /* only on OMAP4430 */
+ OMAP_I2C_IP_V2_REVNB_LO,
+ OMAP_I2C_IP_V2_REVNB_HI,
+ OMAP_I2C_IP_V2_IRQSTATUS_RAW,
+ OMAP_I2C_IP_V2_IRQENABLE_SET,
+ OMAP_I2C_IP_V2_IRQENABLE_CLR,
};
/* I2C Interrupt Enable Register (OMAP_I2C_IE): */
@@ -193,7 +194,6 @@ struct omap_i2c_dev {
*/
u8 rev;
unsigned b_hw:1; /* bad h/w fixes */
- unsigned idle:1;
u16 iestate; /* Saved interrupt register */
u16 pscstate;
u16 scllstate;
@@ -204,7 +204,7 @@ struct omap_i2c_dev {
u16 errata;
};
-const static u8 reg_map[] = {
+static const u8 reg_map_ip_v1[] = {
[OMAP_I2C_REV_REG] = 0x00,
[OMAP_I2C_IE_REG] = 0x01,
[OMAP_I2C_STAT_REG] = 0x02,
@@ -225,7 +225,7 @@ const static u8 reg_map[] = {
[OMAP_I2C_BUFSTAT_REG] = 0x10,
};
-const static u8 omap4_reg_map[] = {
+static const u8 reg_map_ip_v2[] = {
[OMAP_I2C_REV_REG] = 0x04,
[OMAP_I2C_IE_REG] = 0x2c,
[OMAP_I2C_STAT_REG] = 0x28,
@@ -244,11 +244,11 @@ const static u8 omap4_reg_map[] = {
[OMAP_I2C_SCLH_REG] = 0xb8,
[OMAP_I2C_SYSTEST_REG] = 0xbC,
[OMAP_I2C_BUFSTAT_REG] = 0xc0,
- [OMAP_I2C_REVNB_LO] = 0x00,
- [OMAP_I2C_REVNB_HI] = 0x04,
- [OMAP_I2C_IRQSTATUS_RAW] = 0x24,
- [OMAP_I2C_IRQENABLE_SET] = 0x2c,
- [OMAP_I2C_IRQENABLE_CLR] = 0x30,
+ [OMAP_I2C_IP_V2_REVNB_LO] = 0x00,
+ [OMAP_I2C_IP_V2_REVNB_HI] = 0x04,
+ [OMAP_I2C_IP_V2_IRQSTATUS_RAW] = 0x24,
+ [OMAP_I2C_IP_V2_IRQENABLE_SET] = 0x2c,
+ [OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30,
};
static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
@@ -266,17 +266,11 @@ static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
static void omap_i2c_unidle(struct omap_i2c_dev *dev)
{
- struct platform_device *pdev;
struct omap_i2c_bus_platform_data *pdata;
- WARN_ON(!dev->idle);
+ pdata = dev->dev->platform_data;
- pdev = to_platform_device(dev->dev);
- pdata = pdev->dev.platform_data;
-
- pm_runtime_get_sync(&pdev->dev);
-
- if (cpu_is_omap34xx()) {
+ if (pdata->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) {
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate);
omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate);
@@ -286,7 +280,6 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev)
omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
}
- dev->idle = 0;
/*
* Don't write to this register if the IE state is 0 as it can
@@ -298,32 +291,25 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev)
static void omap_i2c_idle(struct omap_i2c_dev *dev)
{
- struct platform_device *pdev;
struct omap_i2c_bus_platform_data *pdata;
u16 iv;
- WARN_ON(dev->idle);
-
- pdev = to_platform_device(dev->dev);
- pdata = pdev->dev.platform_data;
+ pdata = dev->dev->platform_data;
dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
- if (dev->rev >= OMAP_I2C_REV_ON_4430)
- omap_i2c_write_reg(dev, OMAP_I2C_IRQENABLE_CLR, 1);
+ if (pdata->rev == OMAP_I2C_IP_VERSION_2)
+ omap_i2c_write_reg(dev, OMAP_I2C_IP_V2_IRQENABLE_CLR, 1);
else
omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0);
- if (dev->rev < OMAP_I2C_REV_2) {
+ if (dev->rev < OMAP_I2C_OMAP1_REV_2) {
iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */
} else {
omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, dev->iestate);
- /* Flush posted write before the dev->idle store occurs */
+ /* Flush posted write */
omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
}
- dev->idle = 1;
-
- pm_runtime_put_sync(&pdev->dev);
}
static int omap_i2c_init(struct omap_i2c_dev *dev)
@@ -334,8 +320,11 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
unsigned long timeout;
unsigned long internal_clk = 0;
struct clk *fclk;
+ struct omap_i2c_bus_platform_data *pdata;
+
+ pdata = dev->dev->platform_data;
- if (dev->rev >= OMAP_I2C_REV_2) {
+ if (dev->rev >= OMAP_I2C_OMAP1_REV_2) {
/* Disable I2C controller before soft reset */
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG,
omap_i2c_read_reg(dev, OMAP_I2C_CON_REG) &
@@ -378,12 +367,13 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
* REVISIT: Some wkup sources might not be needed.
*/
dev->westate = OMAP_I2C_WE_ALL;
- omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
+ omap_i2c_write_reg(dev, OMAP_I2C_WE_REG,
+ dev->westate);
}
}
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
- if (cpu_class_is_omap1()) {
+ if (pdata->flags & OMAP_I2C_FLAG_ALWAYS_ARMXOR_CLK) {
/*
* The I2C functional clock is the armxor_ck, so there's
* no need to get "armxor_ck" separately. Now, if OMAP2420
@@ -407,7 +397,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
psc = fclk_rate / 12000000;
}
- if (!(cpu_class_is_omap1() || cpu_is_omap2420())) {
+ if (!(pdata->flags & OMAP_I2C_FLAG_SIMPLE_CLOCK)) {
/*
* HSI2C controller internal clk rate should be 19.2 Mhz for
@@ -415,7 +405,8 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
* to get longer filter period for better noise suppression.
* The filter is iclk (fclk for HS) period.
*/
- if (dev->speed > 400 || cpu_is_omap2430())
+ if (dev->speed > 400 ||
+ pdata->flags & OMAP_I2C_FLAG_FORCE_19200_INT_CLK)
internal_clk = 19200;
else if (dev->speed > 100)
internal_clk = 9600;
@@ -484,7 +475,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
dev->errata = 0;
- if (cpu_is_omap2430() || cpu_is_omap34xx())
+ if (pdata->flags & OMAP_I2C_FLAG_APPLY_ERRATA_I207)
dev->errata |= I2C_OMAP_ERRATA_I207;
/* Enable interrupts */
@@ -493,7 +484,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
OMAP_I2C_IE_AL) | ((dev->fifo_size) ?
(OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0);
omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
- if (cpu_is_omap34xx()) {
+ if (pdata->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) {
dev->pscstate = psc;
dev->scllstate = scll;
dev->sclhstate = sclh;
@@ -642,7 +633,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
int i;
int r;
- omap_i2c_unidle(dev);
+ pm_runtime_get_sync(dev->dev);
r = omap_i2c_wait_for_bb(dev);
if (r < 0)
@@ -665,7 +656,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
omap_i2c_wait_for_bb(dev);
out:
- omap_i2c_idle(dev);
+ pm_runtime_put(dev->dev);
return r;
}
@@ -720,12 +711,12 @@ static inline void i2c_omap_errata_i207(struct omap_i2c_dev *dev, u16 stat)
#ifdef CONFIG_ARCH_OMAP15XX
static irqreturn_t
-omap_i2c_rev1_isr(int this_irq, void *dev_id)
+omap_i2c_omap1_isr(int this_irq, void *dev_id)
{
struct omap_i2c_dev *dev = dev_id;
u16 iv, w;
- if (dev->idle)
+ if (pm_runtime_suspended(dev->dev))
return IRQ_NONE;
iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG);
@@ -774,7 +765,7 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id)
return IRQ_HANDLED;
}
#else
-#define omap_i2c_rev1_isr NULL
+#define omap_i2c_omap1_isr NULL
#endif
/*
@@ -813,8 +804,11 @@ omap_i2c_isr(int this_irq, void *dev_id)
u16 bits;
u16 stat, w;
int err, count = 0;
+ struct omap_i2c_bus_platform_data *pdata;
+
+ pdata = dev->dev->platform_data;
- if (dev->idle)
+ if (pm_runtime_suspended(dev->dev))
return IRQ_NONE;
bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
@@ -881,8 +875,8 @@ complete:
* Data reg in 2430, omap3 and
* omap4 is 8 bit wide
*/
- if (cpu_class_is_omap1() ||
- cpu_is_omap2420()) {
+ if (pdata->flags &
+ OMAP_I2C_FLAG_16BIT_DATA_REG) {
if (dev->buf_len) {
*dev->buf++ = w >> 8;
dev->buf_len--;
@@ -924,8 +918,8 @@ complete:
* Data reg in 2430, omap3 and
* omap4 is 8 bit wide
*/
- if (cpu_class_is_omap1() ||
- cpu_is_omap2420()) {
+ if (pdata->flags &
+ OMAP_I2C_FLAG_16BIT_DATA_REG) {
if (dev->buf_len) {
w |= *dev->buf++ << 8;
dev->buf_len--;
@@ -1016,7 +1010,6 @@ omap_i2c_probe(struct platform_device *pdev)
}
dev->speed = speed;
- dev->idle = 1;
dev->dev = &pdev->dev;
dev->irq = irq->start;
dev->base = ioremap(mem->start, resource_size(mem));
@@ -1027,27 +1020,22 @@ omap_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev);
- if (cpu_is_omap7xx())
- dev->reg_shift = 1;
- else if (cpu_is_omap44xx())
- dev->reg_shift = 0;
- else
- dev->reg_shift = 2;
+ dev->reg_shift = (pdata->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & 3;
- if (cpu_is_omap44xx())
- dev->regs = (u8 *) omap4_reg_map;
+ if (pdata->rev == OMAP_I2C_IP_VERSION_2)
+ dev->regs = (u8 *)reg_map_ip_v2;
else
- dev->regs = (u8 *) reg_map;
+ dev->regs = (u8 *)reg_map_ip_v1;
- pm_runtime_enable(&pdev->dev);
- omap_i2c_unidle(dev);
+ pm_runtime_enable(dev->dev);
+ pm_runtime_get_sync(dev->dev);
dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;
if (dev->rev <= OMAP_I2C_REV_ON_3430)
dev->errata |= I2C_OMAP3_1P153;
- if (!(cpu_class_is_omap1() || cpu_is_omap2420())) {
+ if (!(pdata->flags & OMAP_I2C_FLAG_NO_FIFO)) {
u16 s;
/* Set up the fifo size - Get total size */
@@ -1059,7 +1047,7 @@ omap_i2c_probe(struct platform_device *pdev)
* size. This is to ensure that we can handle the status on int
* call back latencies.
*/
- if (dev->rev >= OMAP_I2C_REV_ON_4430) {
+ if (dev->rev >= OMAP_I2C_REV_ON_3530_4430) {
dev->fifo_size = 0;
dev->b_hw = 0; /* Disable hardware fixes */
} else {
@@ -1075,7 +1063,8 @@ omap_i2c_probe(struct platform_device *pdev)
/* reset ASAP, clearing any IRQs */
omap_i2c_init(dev);
- isr = (dev->rev < OMAP_I2C_REV_2) ? omap_i2c_rev1_isr : omap_i2c_isr;
+ isr = (dev->rev < OMAP_I2C_OMAP1_REV_2) ? omap_i2c_omap1_isr :
+ omap_i2c_isr;
r = request_irq(dev->irq, isr, 0, pdev->name, dev);
if (r) {
@@ -1083,10 +1072,10 @@ omap_i2c_probe(struct platform_device *pdev)
goto err_unuse_clocks;
}
- dev_info(dev->dev, "bus %d rev%d.%d at %d kHz\n",
- pdev->id, dev->rev >> 4, dev->rev & 0xf, dev->speed);
+ dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", pdev->id,
+ pdata->rev, dev->rev >> 4, dev->rev & 0xf, dev->speed);
- omap_i2c_idle(dev);
+ pm_runtime_put(dev->dev);
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
@@ -1110,7 +1099,7 @@ err_free_irq:
free_irq(dev->irq, dev);
err_unuse_clocks:
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
- omap_i2c_idle(dev);
+ pm_runtime_put(dev->dev);
iounmap(dev->base);
err_free_mem:
platform_set_drvdata(pdev, NULL);
@@ -1139,28 +1128,30 @@ omap_i2c_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_SUSPEND
-static int omap_i2c_suspend(struct device *dev)
+#ifdef CONFIG_PM_RUNTIME
+static int omap_i2c_runtime_suspend(struct device *dev)
{
- if (!pm_runtime_suspended(dev))
- if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend)
- dev->bus->pm->runtime_suspend(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_i2c_dev *_dev = platform_get_drvdata(pdev);
+
+ omap_i2c_idle(_dev);
return 0;
}
-static int omap_i2c_resume(struct device *dev)
+static int omap_i2c_runtime_resume(struct device *dev)
{
- if (!pm_runtime_suspended(dev))
- if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume)
- dev->bus->pm->runtime_resume(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_i2c_dev *_dev = platform_get_drvdata(pdev);
+
+ omap_i2c_unidle(_dev);
return 0;
}
static struct dev_pm_ops omap_i2c_pm_ops = {
- .suspend = omap_i2c_suspend,
- .resume = omap_i2c_resume,
+ .runtime_suspend = omap_i2c_runtime_suspend,
+ .runtime_resume = omap_i2c_runtime_resume,
};
#define OMAP_I2C_PM_OPS (&omap_i2c_pm_ops)
#else
diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c
index dfa7ae9c1b8e..127051b06921 100644
--- a/drivers/i2c/busses/i2c-pmcmsp.c
+++ b/drivers/i2c/busses/i2c-pmcmsp.c
@@ -306,7 +306,7 @@ static int __devinit pmcmsptwi_probe(struct platform_device *pldev)
pmcmsptwi_data.irq = platform_get_irq(pldev, 0);
if (pmcmsptwi_data.irq) {
rc = request_irq(pmcmsptwi_data.irq, &pmcmsptwi_interrupt,
- IRQF_SHARED | IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
+ IRQF_SHARED | IRQF_SAMPLE_RANDOM,
pldev->name, &pmcmsptwi_data);
if (rc == 0) {
/*
diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c
index 6659d269b841..632e088760a3 100644
--- a/drivers/i2c/busses/i2c-pxa-pci.c
+++ b/drivers/i2c/busses/i2c-pxa-pci.c
@@ -3,6 +3,7 @@
* It does not support slave mode, the register slightly moved. This PCI
* device provides three bars, every contains a single I2C controller.
*/
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/i2c/pxa-i2c.h>
@@ -109,12 +110,15 @@ static int __devinit ce4100_i2c_probe(struct pci_dev *dev,
return -EINVAL;
}
sds = kzalloc(sizeof(*sds), GFP_KERNEL);
- if (!sds)
+ if (!sds) {
+ ret = -ENOMEM;
goto err_mem;
+ }
for (i = 0; i < ARRAY_SIZE(sds->pdev); i++) {
sds->pdev[i] = add_i2c_device(dev, i);
if (IS_ERR(sds->pdev[i])) {
+ ret = PTR_ERR(sds->pdev[i]);
while (--i >= 0)
platform_device_unregister(sds->pdev[i]);
goto err_dev_add;
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index f59224a5c761..d60364650990 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -1079,7 +1079,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
* The reason to do so is to avoid sysfs names that only make
* sense when there are multiple adapters.
*/
- i2c->adap.nr = dev->id != -1 ? dev->id : 0;
+ i2c->adap.nr = dev->id;
snprintf(i2c->adap.name, sizeof(i2c->adap.name), "pxa_i2c-i2c.%u",
i2c->adap.nr);
@@ -1142,10 +1142,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
i2c->adap.dev.of_node = dev->dev.of_node;
#endif
- if (i2c_type == REGS_CE4100)
- ret = i2c_add_adapter(&i2c->adap);
- else
- ret = i2c_add_numbered_adapter(&i2c->adap);
+ ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
printk(KERN_INFO "I2C: Failed to add bus\n");
goto eadapt;
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index f84a63c6dd97..2754cef86a06 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -35,6 +35,8 @@
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/of_i2c.h>
+#include <linux/of_gpio.h>
#include <asm/irq.h>
@@ -78,6 +80,8 @@ struct s3c24xx_i2c {
struct resource *ioarea;
struct i2c_adapter adap;
+ struct s3c2410_platform_i2c *pdata;
+ int gpios[2];
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
@@ -95,6 +99,12 @@ static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c)
struct platform_device *pdev = to_platform_device(i2c->dev);
enum s3c24xx_i2c_type type;
+#ifdef CONFIG_OF
+ if (i2c->dev->of_node)
+ return of_device_is_compatible(i2c->dev->of_node,
+ "samsung,s3c2440-i2c");
+#endif
+
type = platform_get_device_id(pdev)->driver_data;
return type == TYPE_S3C2440;
}
@@ -625,7 +635,7 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
{
- struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data;
+ struct s3c2410_platform_i2c *pdata = i2c->pdata;
unsigned long clkin = clk_get_rate(i2c->clk);
unsigned int divs, div1;
unsigned long target_frequency;
@@ -741,6 +751,49 @@ static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c)
}
#endif
+#ifdef CONFIG_OF
+static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
+{
+ int idx, gpio, ret;
+
+ for (idx = 0; idx < 2; idx++) {
+ gpio = of_get_gpio(i2c->dev->of_node, idx);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(i2c->dev, "invalid gpio[%d]: %d\n", idx, gpio);
+ goto free_gpio;
+ }
+
+ ret = gpio_request(gpio, "i2c-bus");
+ if (ret) {
+ dev_err(i2c->dev, "gpio [%d] request failed\n", gpio);
+ goto free_gpio;
+ }
+ }
+ return 0;
+
+free_gpio:
+ while (--idx >= 0)
+ gpio_free(i2c->gpios[idx]);
+ return -EINVAL;
+}
+
+static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
+{
+ unsigned int idx;
+ for (idx = 0; idx < 2; idx++)
+ gpio_free(i2c->gpios[idx]);
+}
+#else
+static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
+{
+ return -EINVAL;
+}
+
+static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
+{
+}
+#endif
+
/* s3c24xx_i2c_init
*
* initialise the controller, set the IO lines and frequency
@@ -754,12 +807,15 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
/* get the plafrom data */
- pdata = i2c->dev->platform_data;
+ pdata = i2c->pdata;
/* inititalise the gpio */
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i2c->dev));
+ else
+ if (s3c24xx_i2c_parse_dt_gpio(i2c))
+ return -EINVAL;
/* write slave address */
@@ -785,6 +841,34 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
return 0;
}
+#ifdef CONFIG_OF
+/* s3c24xx_i2c_parse_dt
+ *
+ * Parse the device tree node and retreive the platform data.
+*/
+
+static void
+s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
+{
+ struct s3c2410_platform_i2c *pdata = i2c->pdata;
+
+ if (!np)
+ return;
+
+ pdata->bus_num = -1; /* i2c bus number is dynamically assigned */
+ of_property_read_u32(np, "samsung,i2c-sda-delay", &pdata->sda_delay);
+ of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr);
+ of_property_read_u32(np, "samsung,i2c-max-bus-freq",
+ (u32 *)&pdata->frequency);
+}
+#else
+static void
+s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
+{
+ return;
+}
+#endif
+
/* s3c24xx_i2c_probe
*
* called by the bus driver when a suitable device is found
@@ -793,14 +877,16 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata;
+ struct s3c2410_platform_i2c *pdata = NULL;
struct resource *res;
int ret;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
+ if (!pdev->dev.of_node) {
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
}
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
@@ -809,6 +895,17 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!i2c->pdata) {
+ ret = -ENOMEM;
+ goto err_noclk;
+ }
+
+ if (pdata)
+ memcpy(i2c->pdata, pdata, sizeof(*pdata));
+ else
+ s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
+
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
@@ -883,7 +980,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
goto err_iomap;
}
- ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
+ ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
dev_name(&pdev->dev), i2c);
if (ret != 0) {
@@ -903,7 +1000,8 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
* being bus 0.
*/
- i2c->adap.nr = pdata->bus_num;
+ i2c->adap.nr = i2c->pdata->bus_num;
+ i2c->adap.dev.of_node = pdev->dev.of_node;
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
@@ -911,6 +1009,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
goto err_cpufreq;
}
+ of_i2c_register_devices(&i2c->adap);
platform_set_drvdata(pdev, i2c);
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
@@ -959,6 +1058,7 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
iounmap(i2c->regs);
release_resource(i2c->ioarea);
+ s3c24xx_i2c_dt_gpio_free(i2c);
kfree(i2c->ioarea);
kfree(i2c);
@@ -1012,6 +1112,17 @@ static struct platform_device_id s3c24xx_driver_ids[] = {
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
+#ifdef CONFIG_OF
+static const struct of_device_id s3c24xx_i2c_match[] = {
+ { .compatible = "samsung,s3c2410-i2c" },
+ { .compatible = "samsung,s3c2440-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
+#else
+#define s3c24xx_i2c_match NULL
+#endif
+
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
@@ -1020,6 +1131,7 @@ static struct platform_driver s3c24xx_i2c_driver = {
.owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
+ .of_match_table = s3c24xx_i2c_match,
},
};
diff --git a/drivers/i2c/busses/i2c-s6000.c b/drivers/i2c/busses/i2c-s6000.c
index cb5d01e279c6..c64ba736f480 100644
--- a/drivers/i2c/busses/i2c-s6000.c
+++ b/drivers/i2c/busses/i2c-s6000.c
@@ -341,10 +341,7 @@ static int __devinit s6i2c_probe(struct platform_device *dev)
i2c_wr16(iface, S6_I2C_TXTL, 0);
platform_set_drvdata(dev, iface);
- if (bus_num < 0)
- rc = i2c_add_adapter(p_adap);
- else
- rc = i2c_add_numbered_adapter(p_adap);
+ rc = i2c_add_numbered_adapter(p_adap);
if (rc)
goto err_irq_free;
return 0;
diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c
index 3cad8fecc3d3..a67132b2e092 100644
--- a/drivers/i2c/busses/i2c-sh7760.c
+++ b/drivers/i2c/busses/i2c-sh7760.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/module.h>
#include <asm/clock.h>
#include <asm/i2c-sh7760.h>
@@ -502,7 +503,7 @@ static int __devinit sh7760_i2c_probe(struct platform_device *pdev)
}
OUT32(id, I2CCCR, ret);
- if (request_irq(id->irq, sh7760_i2c_irq, IRQF_DISABLED,
+ if (request_irq(id->irq, sh7760_i2c_irq, 0,
SH7760_I2C_DEVNAME, id)) {
dev_err(&pdev->dev, "cannot get irq %d\n", id->irq);
ret = -EBUSY;
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index f633a53b6dbe..675c9692d148 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -543,7 +543,7 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook)
while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) {
for (n = res->start; hook && n <= res->end; n++) {
- if (request_irq(n, sh_mobile_i2c_isr, IRQF_DISABLED,
+ if (request_irq(n, sh_mobile_i2c_isr, 0,
dev_name(&dev->dev), dev)) {
for (n--; n >= res->start; n--)
free_irq(n, dev);
diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c
index 99879617e686..4d44af181f37 100644
--- a/drivers/i2c/busses/i2c-stu300.c
+++ b/drivers/i2c/busses/i2c-stu300.c
@@ -916,7 +916,7 @@ stu300_probe(struct platform_device *pdev)
}
dev->irq = platform_get_irq(pdev, 0);
- if (request_irq(dev->irq, stu300_irh, IRQF_DISABLED,
+ if (request_irq(dev->irq, stu300_irh, 0,
NAME, dev)) {
ret = -EIO;
goto err_no_irq;
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index fb3b4f8f8152..46b6500c5478 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -26,6 +26,8 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/i2c-tegra.h>
+#include <linux/of_i2c.h>
+#include <linux/module.h>
#include <asm/unaligned.h>
@@ -269,14 +271,30 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
/* Rounds down to not include partial word at the end of buf */
words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
- if (words_to_transfer > tx_fifo_avail)
- words_to_transfer = tx_fifo_avail;
- i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
-
- buf += words_to_transfer * BYTES_PER_FIFO_WORD;
- buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD;
- tx_fifo_avail -= words_to_transfer;
+ /* It's very common to have < 4 bytes, so optimize that case. */
+ if (words_to_transfer) {
+ if (words_to_transfer > tx_fifo_avail)
+ words_to_transfer = tx_fifo_avail;
+
+ /*
+ * Update state before writing to FIFO. If this casues us
+ * to finish writing all bytes (AKA buf_remaining goes to 0) we
+ * have a potential for an interrupt (PACKET_XFER_COMPLETE is
+ * not maskable). We need to make sure that the isr sees
+ * buf_remaining as 0 and doesn't call us back re-entrantly.
+ */
+ buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD;
+ tx_fifo_avail -= words_to_transfer;
+ i2c_dev->msg_buf_remaining = buf_remaining;
+ i2c_dev->msg_buf = buf +
+ words_to_transfer * BYTES_PER_FIFO_WORD;
+ barrier();
+
+ i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
+
+ buf += words_to_transfer * BYTES_PER_FIFO_WORD;
+ }
/*
* If there is a partial word at the end of buf, handle it manually to
@@ -286,14 +304,15 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
if (tx_fifo_avail > 0 && buf_remaining > 0) {
BUG_ON(buf_remaining > 3);
memcpy(&val, buf, buf_remaining);
+
+ /* Again update before writing to FIFO to make sure isr sees. */
+ i2c_dev->msg_buf_remaining = 0;
+ i2c_dev->msg_buf = NULL;
+ barrier();
+
i2c_writel(i2c_dev, val, I2C_TX_FIFO);
- buf_remaining = 0;
- tx_fifo_avail--;
}
- BUG_ON(tx_fifo_avail > 0 && buf_remaining > 0);
- i2c_dev->msg_buf_remaining = buf_remaining;
- i2c_dev->msg_buf = buf;
return 0;
}
@@ -410,9 +429,10 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
}
- if ((status & I2C_INT_PACKET_XFER_COMPLETE) &&
- !i2c_dev->msg_buf_remaining)
+ if (status & I2C_INT_PACKET_XFER_COMPLETE) {
+ BUG_ON(i2c_dev->msg_buf_remaining);
complete(&i2c_dev->msg_complete);
+ }
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
if (i2c_dev->is_dvc)
@@ -530,7 +550,7 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
static u32 tegra_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm tegra_i2c_algo = {
@@ -546,7 +566,8 @@ static int tegra_i2c_probe(struct platform_device *pdev)
struct resource *iomem;
struct clk *clk;
struct clk *i2c_clk;
- void *base;
+ const unsigned int *prop;
+ void __iomem *base;
int irq;
int ret = 0;
@@ -603,7 +624,17 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->irq = irq;
i2c_dev->cont_id = pdev->id;
i2c_dev->dev = &pdev->dev;
- i2c_dev->bus_clk_rate = pdata ? pdata->bus_clk_rate : 100000;
+
+ i2c_dev->bus_clk_rate = 100000; /* default clock rate */
+ if (pdata) {
+ i2c_dev->bus_clk_rate = pdata->bus_clk_rate;
+
+ } else if (i2c_dev->dev->of_node) { /* if there is a device tree node ... */
+ prop = of_get_property(i2c_dev->dev->of_node,
+ "clock-frequency", NULL);
+ if (prop)
+ i2c_dev->bus_clk_rate = be32_to_cpup(prop);
+ }
if (pdev->id == 3)
i2c_dev->is_dvc = 1;
@@ -633,6 +664,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->adapter.dev.parent = &pdev->dev;
i2c_dev->adapter.nr = pdev->id;
+ i2c_dev->adapter.dev.of_node = pdev->dev.of_node;
ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
if (ret) {
@@ -640,6 +672,8 @@ static int tegra_i2c_probe(struct platform_device *pdev)
goto err_free_irq;
}
+ of_i2c_register_devices(&i2c_dev->adapter);
+
return 0;
err_free_irq:
free_irq(i2c_dev->irq, i2c_dev);
@@ -704,6 +738,17 @@ static int tegra_i2c_resume(struct platform_device *pdev)
}
#endif
+#if defined(CONFIG_OF)
+/* Match table for of_platform binding */
+static const struct of_device_id tegra_i2c_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra20-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);
+#else
+#define tegra_i2c_of_match NULL
+#endif
+
static struct platform_driver tegra_i2c_driver = {
.probe = tegra_i2c_probe,
.remove = tegra_i2c_remove,
@@ -714,6 +759,7 @@ static struct platform_driver tegra_i2c_driver = {
.driver = {
.name = "tegra-i2c",
.owner = THIS_MODULE,
+ .of_match_table = tegra_i2c_of_match,
},
};
diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c
index 986e5f62debe..91e349c884c5 100644
--- a/drivers/i2c/busses/scx200_acb.c
+++ b/drivers/i2c/busses/scx200_acb.c
@@ -550,7 +550,7 @@ static int __devexit scx200_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_driver scx200_pci_drv = {
+static struct platform_driver scx200_pci_driver = {
.driver = {
.name = "cs5535-smb",
.owner = THIS_MODULE,
@@ -593,14 +593,14 @@ static int __init scx200_acb_init(void)
return 0;
/* No ISA devices; register the platform driver for PCI-based devices */
- return platform_driver_register(&scx200_pci_drv);
+ return platform_driver_register(&scx200_pci_driver);
}
static void __exit scx200_acb_cleanup(void)
{
struct scx200_acb_iface *iface;
- platform_driver_unregister(&scx200_pci_drv);
+ platform_driver_unregister(&scx200_pci_driver);
mutex_lock(&scx200_acb_list_mutex);
while ((iface = scx200_acb_list) != NULL) {
diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c
index 3ca2e012e789..10274ffb66d7 100644
--- a/drivers/i2c/i2c-boardinfo.c
+++ b/drivers/i2c/i2c-boardinfo.c
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <linux/rwsem.h>
#include "i2c-core.h"
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 9a58994ff7ea..1e5606185b4f 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -539,8 +539,10 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
+ /* For 10-bit clients, add an arbitrary offset to avoid collisions */
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
- client->addr);
+ client->addr | ((client->flags & I2C_CLIENT_TEN)
+ ? 0xa000 : 0));
status = device_register(&client->dev);
if (status)
goto out_err;
@@ -925,6 +927,9 @@ EXPORT_SYMBOL(i2c_add_adapter);
* or otherwise built in to the system's mainboard, and where i2c_board_info
* is used to properly configure I2C devices.
*
+ * If the requested bus number is set to -1, then this function will behave
+ * identically to i2c_add_adapter, and will dynamically assign a bus number.
+ *
* If no devices have pre-been declared for this bus, then be sure to
* register the adapter before any dynamically allocated ones. Otherwise
* the required bus ID may not be available.
@@ -940,6 +945,8 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adap)
int id;
int status;
+ if (adap->nr == -1) /* -1 means dynamically assign bus id */
+ return i2c_add_adapter(adap);
if (adap->nr & ~MAX_ID_MASK)
return -EINVAL;
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index c90ce50b619f..57a45ce84b2d 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -579,7 +579,7 @@ static int i2cdev_detach_adapter(struct device *dev, void *dummy)
return 0;
}
-int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
+static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
void *data)
{
struct device *dev = data;
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 9827c5e686cb..5a26584934ca 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -327,7 +327,7 @@ config BLK_DEV_OPTI621
select BLK_DEV_IDEPCI
help
This is a driver for the OPTi 82C621 EIDE controller.
- Please read the comments at the top of <file:drivers/ide/pci/opti621.c>.
+ Please read the comments at the top of <file:drivers/ide/opti621.c>.
config BLK_DEV_RZ1000
tristate "RZ1000 chipset bugfix/support"
@@ -365,7 +365,7 @@ config BLK_DEV_ALI15X3
normal dual channel support.
Please read the comments at the top of
- <file:drivers/ide/pci/alim15x3.c>.
+ <file:drivers/ide/alim15x3.c>.
If unsure, say N.
@@ -528,7 +528,7 @@ config BLK_DEV_NS87415
This driver adds detection and support for the NS87415 chip
(used mainly on SPARC64 and PA-RISC machines).
- Please read the comments at the top of <file:drivers/ide/pci/ns87415.c>.
+ Please read the comments at the top of <file:drivers/ide/ns87415.c>.
config BLK_DEV_PDC202XX_OLD
tristate "PROMISE PDC202{46|62|65|67} support"
@@ -547,7 +547,7 @@ config BLK_DEV_PDC202XX_OLD
for more than one card.
Please read the comments at the top of
- <file:drivers/ide/pci/pdc202xx_old.c>.
+ <file:drivers/ide/pdc202xx_old.c>.
If unsure, say N.
@@ -593,7 +593,7 @@ config BLK_DEV_SIS5513
ATA100: SiS635, SiS645, SiS650, SiS730, SiS735, SiS740,
SiS745, SiS750
- Please read the comments at the top of <file:drivers/ide/pci/sis5513.c>.
+ Please read the comments at the top of <file:drivers/ide/sis5513.c>.
config BLK_DEV_SL82C105
tristate "Winbond SL82c105 support"
@@ -616,7 +616,7 @@ config BLK_DEV_SLC90E66
look-a-like to the PIIX4 it should be a nice addition.
Please read the comments at the top of
- <file:drivers/ide/pci/slc90e66.c>.
+ <file:drivers/ide/slc90e66.c>.
config BLK_DEV_TRM290
tristate "Tekram TRM290 chipset support"
@@ -625,7 +625,7 @@ config BLK_DEV_TRM290
This driver adds support for bus master DMA transfers
using the Tekram TRM290 PCI IDE chip. Volunteers are
needed for further tweaking and development.
- Please read the comments at the top of <file:drivers/ide/pci/trm290.c>.
+ Please read the comments at the top of <file:drivers/ide/trm290.c>.
config BLK_DEV_VIA82CXXX
tristate "VIA82CXXX chipset support"
@@ -677,19 +677,19 @@ config BLK_DEV_IDE_PMAC_ATA100FIRST
config BLK_DEV_IDE_AU1XXX
bool "IDE for AMD Alchemy Au1200"
- depends on SOC_AU1200
+ depends on MIPS_ALCHEMY
select IDE_XFER_MODE
choice
prompt "IDE Mode for AMD Alchemy Au1200"
- default CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA
- depends on SOC_AU1200 && BLK_DEV_IDE_AU1XXX
+ default BLK_DEV_IDE_AU1XXX_PIO_DBDMA
+ depends on BLK_DEV_IDE_AU1XXX
config BLK_DEV_IDE_AU1XXX_PIO_DBDMA
bool "PIO+DbDMA IDE for AMD Alchemy Au1200"
config BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
bool "MDMA2+DbDMA IDE for AMD Alchemy Au1200"
- depends on SOC_AU1200 && BLK_DEV_IDE_AU1XXX
+ depends on BLK_DEV_IDE_AU1XXX
endchoice
config BLK_DEV_IDE_TX4938
@@ -836,7 +836,7 @@ config BLK_DEV_ALI14XX
of the ALI M1439/1443/1445/1487/1489 chipsets, and permits faster
I/O speeds to be set as well.
See the files <file:Documentation/ide/ide.txt> and
- <file:drivers/ide/legacy/ali14xx.c> for more info.
+ <file:drivers/ide/ali14xx.c> for more info.
config BLK_DEV_DTC2278
tristate "DTC-2278 support"
@@ -847,7 +847,7 @@ config BLK_DEV_DTC2278
boot parameter. It enables support for the secondary IDE interface
of the DTC-2278 card, and permits faster I/O speeds to be set as
well. See the <file:Documentation/ide/ide.txt> and
- <file:drivers/ide/legacy/dtc2278.c> files for more info.
+ <file:drivers/ide/dtc2278.c> files for more info.
config BLK_DEV_HT6560B
tristate "Holtek HT6560B support"
@@ -858,7 +858,7 @@ config BLK_DEV_HT6560B
boot parameter. It enables support for the secondary IDE interface
of the Holtek card, and permits faster I/O speeds to be set as well.
See the <file:Documentation/ide/ide.txt> and
- <file:drivers/ide/legacy/ht6560b.c> files for more info.
+ <file:drivers/ide/ht6560b.c> files for more info.
config BLK_DEV_QD65XX
tristate "QDI QD65xx support"
@@ -867,7 +867,7 @@ config BLK_DEV_QD65XX
help
This driver is enabled at runtime using the "qd65xx.probe" kernel
boot parameter. It permits faster I/O speeds to be set. See the
- <file:Documentation/ide/ide.txt> and <file:drivers/ide/legacy/qd65xx.c>
+ <file:Documentation/ide/ide.txt> and <file:drivers/ide/qd65xx.c>
for more info.
config BLK_DEV_UMC8672
@@ -879,7 +879,7 @@ config BLK_DEV_UMC8672
boot parameter. It enables support for the secondary IDE interface
of the UMC-8672, and permits faster I/O speeds to be set as well.
See the files <file:Documentation/ide/ide.txt> and
- <file:drivers/ide/legacy/umc8672.c> for more info.
+ <file:drivers/ide/umc8672.c> for more info.
endif
diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c
index 000a78e5246c..6dede8f366c5 100644
--- a/drivers/ide/at91_ide.c
+++ b/drivers/ide/at91_ide.c
@@ -28,7 +28,7 @@
#include <linux/platform_device.h>
#include <mach/board.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#include <mach/at91sam9_smc.h>
#define DRV_NAME "at91_ide"
diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c
index b26c23416fa7..259786ca8b75 100644
--- a/drivers/ide/au1xxx-ide.c
+++ b/drivers/ide/au1xxx-ide.c
@@ -36,13 +36,17 @@
#include <linux/ide.h>
#include <linux/scatterlist.h>
-#include <asm/mach-au1x00/au1xxx.h>
+#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h>
#include <asm/mach-au1x00/au1xxx_ide.h>
#define DRV_NAME "au1200-ide"
#define DRV_AUTHOR "Enrico Walther <enrico.walther@amd.com> / Pete Popov <ppopov@embeddedalley.com>"
+#ifndef IDE_REG_SHIFT
+#define IDE_REG_SHIFT 5
+#endif
+
/* enable the burstmode in the dbdma */
#define IDE_AU1XXX_BURSTMODE 1
@@ -317,10 +321,11 @@ static void auide_ddma_rx_callback(int irq, void *param)
}
#endif /* end CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA */
-static void auide_init_dbdma_dev(dbdev_tab_t *dev, u32 dev_id, u32 tsize, u32 devwidth, u32 flags)
+static void auide_init_dbdma_dev(dbdev_tab_t *dev, u32 dev_id, u32 tsize,
+ u32 devwidth, u32 flags, u32 regbase)
{
dev->dev_id = dev_id;
- dev->dev_physaddr = (u32)IDE_PHYS_ADDR;
+ dev->dev_physaddr = CPHYSADDR(regbase);
dev->dev_intlevel = 0;
dev->dev_intpolarity = 0;
dev->dev_tsize = tsize;
@@ -344,7 +349,7 @@ static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
dbdev_tab_t source_dev_tab, target_dev_tab;
u32 dev_id, tsize, devwidth, flags;
- dev_id = IDE_DDMA_REQ;
+ dev_id = hwif->ddma_id;
tsize = 8; /* 1 */
devwidth = 32; /* 16 */
@@ -356,20 +361,17 @@ static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
#endif
/* setup dev_tab for tx channel */
- auide_init_dbdma_dev( &source_dev_tab,
- dev_id,
- tsize, devwidth, DEV_FLAGS_OUT | flags);
+ auide_init_dbdma_dev(&source_dev_tab, dev_id, tsize, devwidth,
+ DEV_FLAGS_OUT | flags, auide->regbase);
auide->tx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
- auide_init_dbdma_dev( &source_dev_tab,
- dev_id,
- tsize, devwidth, DEV_FLAGS_IN | flags);
+ auide_init_dbdma_dev(&source_dev_tab, dev_id, tsize, devwidth,
+ DEV_FLAGS_IN | flags, auide->regbase);
auide->rx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
/* We also need to add a target device for the DMA */
- auide_init_dbdma_dev( &target_dev_tab,
- (u32)DSCR_CMD0_ALWAYS,
- tsize, devwidth, DEV_FLAGS_ANYUSE);
+ auide_init_dbdma_dev(&target_dev_tab, (u32)DSCR_CMD0_ALWAYS, tsize,
+ devwidth, DEV_FLAGS_ANYUSE, auide->regbase);
auide->target_dev_id = au1xxx_ddma_add_device(&target_dev_tab);
/* Get a channel for TX */
@@ -411,14 +413,12 @@ static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
#endif
/* setup dev_tab for tx channel */
- auide_init_dbdma_dev( &source_dev_tab,
- (u32)DSCR_CMD0_ALWAYS,
- 8, 32, DEV_FLAGS_OUT | flags);
+ auide_init_dbdma_dev(&source_dev_tab, (u32)DSCR_CMD0_ALWAYS, 8, 32,
+ DEV_FLAGS_OUT | flags, auide->regbase);
auide->tx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
- auide_init_dbdma_dev( &source_dev_tab,
- (u32)DSCR_CMD0_ALWAYS,
- 8, 32, DEV_FLAGS_IN | flags);
+ auide_init_dbdma_dev(&source_dev_tab, (u32)DSCR_CMD0_ALWAYS, 8, 32,
+ DEV_FLAGS_IN | flags, auide->regbase);
auide->rx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
/* Get a channel for TX */
@@ -540,6 +540,14 @@ static int au_ide_probe(struct platform_device *dev)
goto out;
}
+ res = platform_get_resource(dev, IORESOURCE_DMA, 0);
+ if (!res) {
+ pr_debug("%s: no DDMA ID resource\n", DRV_NAME);
+ ret = -ENODEV;
+ goto out;
+ }
+ ahwif->ddma_id = res->start;
+
memset(&hw, 0, sizeof(hw));
auide_setup_ports(&hw, ahwif);
hw.irq = ahwif->irq;
diff --git a/drivers/ide/buddha.c b/drivers/ide/buddha.c
index ab4f169d0837..b1d38590ac01 100644
--- a/drivers/ide/buddha.c
+++ b/drivers/ide/buddha.c
@@ -23,6 +23,7 @@
#include <linux/zorro.h>
#include <linux/ide.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
diff --git a/drivers/ide/cmd640.c b/drivers/ide/cmd640.c
index cb10201a15ed..a81bd7575792 100644
--- a/drivers/ide/cmd640.c
+++ b/drivers/ide/cmd640.c
@@ -105,6 +105,7 @@
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <asm/io.h>
diff --git a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c
index 3be60da52123..847553fd8b96 100644
--- a/drivers/ide/cy82c693.c
+++ b/drivers/ide/cy82c693.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1998-2000 Andreas S. Krebs (akrebs@altavista.net), Maintainer
* Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>, Integrator
- * Copyright (C) 2007-2010 Bartlomiej Zolnierkiewicz
+ * Copyright (C) 2007-2011 Bartlomiej Zolnierkiewicz
*
* CYPRESS CY82C693 chipset IDE controller
*
@@ -90,7 +90,7 @@ static void cy82c693_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
u8 time_16, time_8;
/* select primary or secondary channel */
- if (hwif->index > 0) { /* drive is on the secondary channel */
+ if (drive->dn > 1) { /* drive is on the secondary channel */
dev = pci_get_slot(dev->bus, dev->devfn+1);
if (!dev) {
printk(KERN_ERR "%s: tune_drive: "
@@ -141,6 +141,8 @@ static void cy82c693_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, time_16);
pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, time_8);
}
+ if (drive->dn > 1)
+ pci_dev_put(dev);
}
static void __devinit init_iops_cy82c693(ide_hwif_t *hwif)
diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c
index 4a697a238e28..8716066a2f2b 100644
--- a/drivers/ide/icside.c
+++ b/drivers/ide/icside.c
@@ -521,8 +521,8 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec)
if (ec->dma != NO_DMA && !request_dma(ec->dma, DRV_NAME)) {
d.init_dma = icside_dma_init;
d.port_ops = &icside_v6_port_ops;
+ } else
d.dma_ops = NULL;
- }
ret = ide_host_register(host, &d, hws);
if (ret)
diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c
index 2af8cb460a3b..f22edc66b030 100644
--- a/drivers/ide/ide-acpi.c
+++ b/drivers/ide/ide-acpi.c
@@ -17,6 +17,7 @@
#include <linux/ide.h>
#include <linux/pci.h>
#include <linux/dmi.h>
+#include <linux/module.h>
#include <acpi/acpi_bus.h>
diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c
index 6f218e014e99..fac3d9da2e07 100644
--- a/drivers/ide/ide-atapi.c
+++ b/drivers/ide/ide-atapi.c
@@ -5,6 +5,7 @@
#include <linux/kernel.h>
#include <linux/cdrom.h>
#include <linux/delay.h>
+#include <linux/export.h>
#include <linux/ide.h>
#include <linux/scatterlist.h>
#include <linux/gfp.h>
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 04b09564bfa9..8126824daccb 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -43,7 +43,6 @@
/* For SCSI -> ATAPI command conversion */
#include <scsi/scsi.h>
-#include <linux/irq.h>
#include <linux/io.h>
#include <asm/byteorder.h>
#include <linux/uaccess.h>
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index 274798068a54..16f69be820c7 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -435,7 +435,12 @@ static int idedisk_prep_fn(struct request_queue *q, struct request *rq)
if (!(rq->cmd_flags & REQ_FLUSH))
return BLKPREP_OK;
- cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+ if (rq->special) {
+ cmd = rq->special;
+ memset(cmd, 0, sizeof(*cmd));
+ } else {
+ cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+ }
/* FIXME: map struct ide_taskfile on rq->cmd[] */
BUG_ON(cmd == NULL);
diff --git a/drivers/ide/ide-disk_proc.c b/drivers/ide/ide-disk_proc.c
index f9bbd904eae7..8b570a17bcd9 100644
--- a/drivers/ide/ide-disk_proc.c
+++ b/drivers/ide/ide-disk_proc.c
@@ -1,6 +1,7 @@
#include <linux/kernel.h>
#include <linux/ide.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <linux/seq_file.h>
#include "ide-disk.h"
diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c
index e4cdf78cc3e9..289d16c87b8f 100644
--- a/drivers/ide/ide-dma-sff.c
+++ b/drivers/ide/ide-dma-sff.c
@@ -1,5 +1,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/ide.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
index d4136908f916..17a65ac56491 100644
--- a/drivers/ide/ide-dma.c
+++ b/drivers/ide/ide-dma.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/ide.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
diff --git a/drivers/ide/ide-eh.c b/drivers/ide/ide-eh.c
index c0aa93fb7a60..32970664c275 100644
--- a/drivers/ide/ide-eh.c
+++ b/drivers/ide/ide-eh.c
@@ -1,5 +1,6 @@
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/ide.h>
#include <linux/delay.h>
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index 61fdf544fbd6..3d42043fec51 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -35,7 +35,6 @@
#include <scsi/scsi_ioctl.h>
#include <asm/byteorder.h>
-#include <linux/irq.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <asm/unaligned.h>
diff --git a/drivers/ide/ide-floppy_proc.c b/drivers/ide/ide-floppy_proc.c
index d711d9b883de..1600720f3e86 100644
--- a/drivers/ide/ide-floppy_proc.c
+++ b/drivers/ide/ide-floppy_proc.c
@@ -1,4 +1,5 @@
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/ide.h>
#include <linux/seq_file.h>
diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c
index 46721c454518..19763977568c 100644
--- a/drivers/ide/ide-io-std.c
+++ b/drivers/ide/ide-io-std.c
@@ -1,5 +1,6 @@
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/ide.h>
#if defined(CONFIG_ARM) || defined(CONFIG_M68K) || defined(CONFIG_MIPS) || \
diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c
index 9965ecd5078c..4d19eb9772a1 100644
--- a/drivers/ide/ide-ioctls.c
+++ b/drivers/ide/ide-ioctls.c
@@ -2,6 +2,7 @@
* IDE ioctls handling.
*/
+#include <linux/export.h>
#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/slab.h>
diff --git a/drivers/ide/ide-legacy.c b/drivers/ide/ide-legacy.c
index b9654a7bb7be..30fe3630734f 100644
--- a/drivers/ide/ide-legacy.c
+++ b/drivers/ide/ide-legacy.c
@@ -1,4 +1,5 @@
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/ide.h>
static void ide_legacy_init_one(struct ide_hw **hws, struct ide_hw *hw,
diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c
index e386a32dc9ba..d9c9829c8b20 100644
--- a/drivers/ide/ide-lib.c
+++ b/drivers/ide/ide-lib.c
@@ -1,6 +1,7 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/ide.h>
#include <linux/bitops.h>
diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c
index 017b1df3b805..e5f3db831373 100644
--- a/drivers/ide/ide-pnp.c
+++ b/drivers/ide/ide-pnp.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/pnp.h>
#include <linux/ide.h>
+#include <linux/module.h>
#define DRV_NAME "ide-pnp"
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index 7ecb1ade8874..ce8237d36159 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -41,7 +41,6 @@
#include <scsi/scsi.h>
#include <asm/byteorder.h>
-#include <linux/irq.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <asm/unaligned.h>
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index 600c89a3d137..5bc2839ebcfd 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -11,6 +11,7 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
diff --git a/drivers/ide/ide-xfer-mode.c b/drivers/ide/ide-xfer-mode.c
index 5fc8d5c17de9..eb421883c16d 100644
--- a/drivers/ide/ide-xfer-mode.c
+++ b/drivers/ide/ide-xfer-mode.c
@@ -1,6 +1,7 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/ide.h>
#include <linux/bitops.h>
diff --git a/drivers/ide/ide_platform.c b/drivers/ide/ide_platform.c
index 542603b394e4..962693b10a1c 100644
--- a/drivers/ide/ide_platform.c
+++ b/drivers/ide/ide_platform.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/ata_platform.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
static void __devinit plat_ide_setup_ports(struct ide_hw *hw,
@@ -95,7 +96,10 @@ static int __devinit plat_ide_probe(struct platform_device *pdev)
plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start);
hw.dev = &pdev->dev;
- d.irq_flags = res_irq->flags;
+ d.irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
+ if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE)
+ d.irq_flags |= IRQF_SHARED;
+
if (mmio)
d.host_flags |= IDE_HFLAG_MMIO;
diff --git a/drivers/ide/macide.c b/drivers/ide/macide.c
index 505ec43e5606..adc5fe9daafc 100644
--- a/drivers/ide/macide.c
+++ b/drivers/ide/macide.c
@@ -17,6 +17,7 @@
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/ide.h>
+#include <linux/module.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
diff --git a/drivers/ide/palm_bk3710.c b/drivers/ide/palm_bk3710.c
index 9e8f4e1b0cc9..712c7904d03e 100644
--- a/drivers/ide/palm_bk3710.c
+++ b/drivers/ide/palm_bk3710.c
@@ -342,7 +342,7 @@ static int __init palm_bk3710_probe(struct platform_device *pdev)
return -ENODEV;
}
- mem_size = mem->end - mem->start + 1;
+ mem_size = resource_size(mem);
if (request_mem_region(mem->start, mem_size, "palm_bk3710") == NULL) {
printk(KERN_ERR "failed to request memory region\n");
return -EBUSY;
diff --git a/drivers/ide/piix.c b/drivers/ide/piix.c
index b59d04c72051..1892e81fb00f 100644
--- a/drivers/ide/piix.c
+++ b/drivers/ide/piix.c
@@ -331,7 +331,7 @@ static const struct ide_port_ops ich_port_ops = {
.udma_mask = udma, \
}
-#define DECLARE_ICH_DEV(udma) \
+#define DECLARE_ICH_DEV(mwdma, udma) \
{ \
.name = DRV_NAME, \
.init_chipset = init_chipset_ich, \
@@ -340,7 +340,7 @@ static const struct ide_port_ops ich_port_ops = {
.port_ops = &ich_port_ops, \
.pio_mask = ATA_PIO4, \
.swdma_mask = ATA_SWDMA2_ONLY, \
- .mwdma_mask = ATA_MWDMA12_ONLY, \
+ .mwdma_mask = mwdma, \
.udma_mask = udma, \
}
@@ -362,13 +362,15 @@ static const struct ide_port_info piix_pci_info[] __devinitdata = {
/* 2: PIIX4 */
DECLARE_PIIX_DEV(ATA_UDMA2),
/* 3: ICH0 */
- DECLARE_ICH_DEV(ATA_UDMA2),
+ DECLARE_ICH_DEV(ATA_MWDMA12_ONLY, ATA_UDMA2),
/* 4: ICH */
- DECLARE_ICH_DEV(ATA_UDMA4),
+ DECLARE_ICH_DEV(ATA_MWDMA12_ONLY, ATA_UDMA4),
/* 5: PIIX4 */
DECLARE_PIIX_DEV(ATA_UDMA4),
- /* 6: ICH[2-7]/ICH[2-3]M/C-ICH/ICH5-SATA/ESB2/ICH8M */
- DECLARE_ICH_DEV(ATA_UDMA5),
+ /* 6: ICH[2-6]/ICH[2-3]M/C-ICH/ICH5-SATA/ESB2/ICH8M */
+ DECLARE_ICH_DEV(ATA_MWDMA12_ONLY, ATA_UDMA5),
+ /* 7: ICH7/7-R, no MWDMA1 */
+ DECLARE_ICH_DEV(ATA_MWDMA2_ONLY, ATA_UDMA5),
};
/**
@@ -438,9 +440,9 @@ static const struct pci_device_id piix_pci_tbl[] = {
#endif
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB_2), 6 },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH6_19), 6 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH7_21), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH7_21), 7 },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_1), 6 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB2_18), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB2_18), 7 },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH8_6), 6 },
{ 0, },
};
diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c
index 1db7c4368dbf..e944c7f705f7 100644
--- a/drivers/ide/pmac.c
+++ b/drivers/ide/pmac.c
@@ -28,6 +28,7 @@
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/notifier.h>
+#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/pci.h>
#include <linux/adb.h>
diff --git a/drivers/ide/q40ide.c b/drivers/ide/q40ide.c
index 90786083b439..ecd0a69245f6 100644
--- a/drivers/ide/q40ide.c
+++ b/drivers/ide/q40ide.c
@@ -15,6 +15,7 @@
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/ide.h>
+#include <linux/module.h>
#include <asm/ide.h>
diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c
index ab3db61d2ba0..34a5e5223d50 100644
--- a/drivers/ide/setup-pci.c
+++ b/drivers/ide/setup-pci.c
@@ -8,6 +8,7 @@
#include <linux/types.h>
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/drivers/ide/tc86c001.c b/drivers/ide/tc86c001.c
index e444d24934b3..4799d5c384e7 100644
--- a/drivers/ide/tc86c001.c
+++ b/drivers/ide/tc86c001.c
@@ -10,6 +10,7 @@
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/ide.h>
+#include <linux/module.h>
#define DRV_NAME "tc86c001"
diff --git a/drivers/ide/triflex.c b/drivers/ide/triflex.c
index e53a1b78378b..281c91426345 100644
--- a/drivers/ide/triflex.c
+++ b/drivers/ide/triflex.c
@@ -113,12 +113,26 @@ static const struct pci_device_id triflex_pci_tbl[] = {
};
MODULE_DEVICE_TABLE(pci, triflex_pci_tbl);
+#ifdef CONFIG_PM
+static int triflex_ide_pci_suspend(struct pci_dev *dev, pm_message_t state)
+{
+ /*
+ * We must not disable or powerdown the device.
+ * APM bios refuses to suspend if IDE is not accessible.
+ */
+ pci_save_state(dev);
+ return 0;
+}
+#else
+#define triflex_ide_pci_suspend NULL
+#endif
+
static struct pci_driver triflex_pci_driver = {
.name = "TRIFLEX_IDE",
.id_table = triflex_pci_tbl,
.probe = triflex_init_one,
.remove = ide_pci_remove,
- .suspend = ide_pci_suspend,
+ .suspend = triflex_ide_pci_suspend,
.resume = ide_pci_resume,
};
diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c
index bed3e39aac96..71c231954972 100644
--- a/drivers/ide/tx4939ide.c
+++ b/drivers/ide/tx4939ide.c
@@ -551,10 +551,10 @@ static int __init tx4939ide_probe(struct platform_device *pdev)
return -ENODEV;
if (!devm_request_mem_region(&pdev->dev, res->start,
- res->end - res->start + 1, "tx4938ide"))
+ resource_size(res), "tx4938ide"))
return -EBUSY;
mapbase = (unsigned long)devm_ioremap(&pdev->dev, res->start,
- res->end - res->start + 1);
+ resource_size(res));
if (!mapbase)
return -EBUSY;
memset(&hw, 0, sizeof(hw));
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index a46dddf61078..5d2f8e13cf0e 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -61,6 +61,7 @@
#include <linux/sched.h>
#include <linux/notifier.h>
#include <linux/cpu.h>
+#include <linux/module.h>
#include <asm/mwait.h>
#include <asm/msr.h>
@@ -81,7 +82,8 @@ static unsigned int mwait_substates;
static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */
static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;
-static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state);
+static int intel_idle(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index);
static struct cpuidle_state *cpuidle_state_table;
@@ -109,7 +111,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C1 */
.name = "C1-NHM",
.desc = "MWAIT 0x00",
- .driver_data = (void *) 0x00,
.flags = CPUIDLE_FLAG_TIME_VALID,
.exit_latency = 3,
.target_residency = 6,
@@ -117,7 +118,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C2 */
.name = "C3-NHM",
.desc = "MWAIT 0x10",
- .driver_data = (void *) 0x10,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 20,
.target_residency = 80,
@@ -125,7 +125,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C3 */
.name = "C6-NHM",
.desc = "MWAIT 0x20",
- .driver_data = (void *) 0x20,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 200,
.target_residency = 800,
@@ -137,7 +136,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C1 */
.name = "C1-SNB",
.desc = "MWAIT 0x00",
- .driver_data = (void *) 0x00,
.flags = CPUIDLE_FLAG_TIME_VALID,
.exit_latency = 1,
.target_residency = 1,
@@ -145,7 +143,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C2 */
.name = "C3-SNB",
.desc = "MWAIT 0x10",
- .driver_data = (void *) 0x10,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 80,
.target_residency = 211,
@@ -153,7 +150,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C3 */
.name = "C6-SNB",
.desc = "MWAIT 0x20",
- .driver_data = (void *) 0x20,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 104,
.target_residency = 345,
@@ -161,7 +157,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C4 */
.name = "C7-SNB",
.desc = "MWAIT 0x30",
- .driver_data = (void *) 0x30,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 109,
.target_residency = 345,
@@ -173,7 +168,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C1 */
.name = "C1-ATM",
.desc = "MWAIT 0x00",
- .driver_data = (void *) 0x00,
.flags = CPUIDLE_FLAG_TIME_VALID,
.exit_latency = 1,
.target_residency = 4,
@@ -181,7 +175,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C2 */
.name = "C2-ATM",
.desc = "MWAIT 0x10",
- .driver_data = (void *) 0x10,
.flags = CPUIDLE_FLAG_TIME_VALID,
.exit_latency = 20,
.target_residency = 80,
@@ -190,7 +183,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C4 */
.name = "C4-ATM",
.desc = "MWAIT 0x30",
- .driver_data = (void *) 0x30,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 100,
.target_residency = 400,
@@ -199,23 +191,55 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C6 */
.name = "C6-ATM",
.desc = "MWAIT 0x52",
- .driver_data = (void *) 0x52,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 140,
.target_residency = 560,
.enter = &intel_idle },
};
+static int get_driver_data(int cstate)
+{
+ int driver_data;
+ switch (cstate) {
+
+ case 1: /* MWAIT C1 */
+ driver_data = 0x00;
+ break;
+ case 2: /* MWAIT C2 */
+ driver_data = 0x10;
+ break;
+ case 3: /* MWAIT C3 */
+ driver_data = 0x20;
+ break;
+ case 4: /* MWAIT C4 */
+ driver_data = 0x30;
+ break;
+ case 5: /* MWAIT C5 */
+ driver_data = 0x40;
+ break;
+ case 6: /* MWAIT C6 */
+ driver_data = 0x52;
+ break;
+ default:
+ driver_data = 0x00;
+ }
+ return driver_data;
+}
+
/**
* intel_idle
* @dev: cpuidle_device
- * @state: cpuidle state
+ * @drv: cpuidle driver
+ * @index: index of cpuidle state
*
*/
-static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
+static int intel_idle(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
{
unsigned long ecx = 1; /* break on interrupt flag */
- unsigned long eax = (unsigned long)cpuidle_get_statedata(state);
+ struct cpuidle_state *state = &drv->states[index];
+ struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+ unsigned long eax = (unsigned long)cpuidle_get_statedata(state_usage);
unsigned int cstate;
ktime_t kt_before, kt_after;
s64 usec_delta;
@@ -256,7 +280,10 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
if (!(lapic_timer_reliable_states & (1 << (cstate))))
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
- return usec_delta;
+ /* Update cpuidle counters */
+ dev->last_residency = (int)usec_delta;
+
+ return index;
}
static void __setup_broadcast_timer(void *arg)
@@ -397,6 +424,60 @@ static void intel_idle_cpuidle_devices_uninit(void)
return;
}
/*
+ * intel_idle_cpuidle_driver_init()
+ * allocate, initialize cpuidle_states
+ */
+static int intel_idle_cpuidle_driver_init(void)
+{
+ int cstate;
+ struct cpuidle_driver *drv = &intel_idle_driver;
+
+ drv->state_count = 1;
+
+ for (cstate = 1; cstate < MWAIT_MAX_NUM_CSTATES; ++cstate) {
+ int num_substates;
+
+ if (cstate > max_cstate) {
+ printk(PREFIX "max_cstate %d reached\n",
+ max_cstate);
+ break;
+ }
+
+ /* does the state exist in CPUID.MWAIT? */
+ num_substates = (mwait_substates >> ((cstate) * 4))
+ & MWAIT_SUBSTATE_MASK;
+ if (num_substates == 0)
+ continue;
+ /* is the state not enabled? */
+ if (cpuidle_state_table[cstate].enter == NULL) {
+ /* does the driver not know about the state? */
+ if (*cpuidle_state_table[cstate].name == '\0')
+ pr_debug(PREFIX "unaware of model 0x%x"
+ " MWAIT %d please"
+ " contact lenb@kernel.org",
+ boot_cpu_data.x86_model, cstate);
+ continue;
+ }
+
+ if ((cstate > 2) &&
+ !boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
+ mark_tsc_unstable("TSC halts in idle"
+ " states deeper than C2");
+
+ drv->states[drv->state_count] = /* structure copy */
+ cpuidle_state_table[cstate];
+
+ drv->state_count += 1;
+ }
+
+ if (auto_demotion_disable_flags)
+ smp_call_function(auto_demotion_disable, NULL, 1);
+
+ return 0;
+}
+
+
+/*
* intel_idle_cpuidle_devices_init()
* allocate, initialize, register cpuidle_devices
*/
@@ -430,22 +511,11 @@ static int intel_idle_cpuidle_devices_init(void)
continue;
/* is the state not enabled? */
if (cpuidle_state_table[cstate].enter == NULL) {
- /* does the driver not know about the state? */
- if (*cpuidle_state_table[cstate].name == '\0')
- pr_debug(PREFIX "unaware of model 0x%x"
- " MWAIT %d please"
- " contact lenb@kernel.org",
- boot_cpu_data.x86_model, cstate);
continue;
}
- if ((cstate > 2) &&
- !boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
- mark_tsc_unstable("TSC halts in idle"
- " states deeper than C2");
-
- dev->states[dev->state_count] = /* structure copy */
- cpuidle_state_table[cstate];
+ dev->states_usage[dev->state_count].driver_data =
+ (void *)get_driver_data(cstate);
dev->state_count += 1;
}
@@ -458,8 +528,6 @@ static int intel_idle_cpuidle_devices_init(void)
return -EIO;
}
}
- if (auto_demotion_disable_flags)
- smp_call_function(auto_demotion_disable, NULL, 1);
return 0;
}
@@ -477,6 +545,7 @@ static int __init intel_idle_init(void)
if (retval)
return retval;
+ intel_idle_cpuidle_driver_init();
retval = cpuidle_register_driver(&intel_idle_driver);
if (retval) {
printk(KERN_DEBUG PREFIX "intel_idle yielding to %s",
diff --git a/drivers/ieee802154/Makefile b/drivers/ieee802154/Makefile
index 68999137dedf..800a3894af0d 100644
--- a/drivers/ieee802154/Makefile
+++ b/drivers/ieee802154/Makefile
@@ -1,3 +1 @@
obj-$(CONFIG_IEEE802154_FAKEHARD) += fakehard.o
-
-ccflags-y := -DDEBUG -DCONFIG_FFD
diff --git a/drivers/ieee802154/fakehard.c b/drivers/ieee802154/fakehard.c
index a5a49a1baae7..eb0e2ccc79ae 100644
--- a/drivers/ieee802154/fakehard.c
+++ b/drivers/ieee802154/fakehard.c
@@ -370,8 +370,6 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev)
return -ENOMEM;
}
- phy->dev.platform_data = dev;
-
memcpy(dev->dev_addr, "\xba\xbe\xca\xfe\xde\xad\xbe\xef",
dev->addr_len);
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 8e21d457b899..691276bafd78 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -37,6 +37,7 @@
#include <linux/inetdevice.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/module.h>
#include <net/arp.h>
#include <net/neighbour.h>
#include <net/route.h>
@@ -215,7 +216,7 @@ static int addr4_resolve(struct sockaddr_in *src_in,
neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev);
if (!neigh || !(neigh->nud_state & NUD_VALID)) {
- neigh_event_send(rt->dst.neighbour, NULL);
+ neigh_event_send(dst_get_neighbour(&rt->dst), NULL);
ret = -ENODATA;
if (neigh)
goto release;
@@ -273,9 +274,10 @@ static int addr6_resolve(struct sockaddr_in6 *src_in,
goto put;
}
- neigh = dst->neighbour;
+ neigh = dst_get_neighbour(dst);
if (!neigh || !(neigh->nud_state & NUD_VALID)) {
- neigh_event_send(dst->neighbour, NULL);
+ if (neigh)
+ neigh_event_send(neigh, NULL);
ret = -ENODATA;
goto put;
}
diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c
index f9ba7d74dfc0..9353992f9eea 100644
--- a/drivers/infiniband/core/cache.c
+++ b/drivers/infiniband/core/cache.c
@@ -302,7 +302,8 @@ static void ib_cache_event(struct ib_event_handler *handler,
event->event == IB_EVENT_LID_CHANGE ||
event->event == IB_EVENT_PKEY_CHANGE ||
event->event == IB_EVENT_SM_CHANGE ||
- event->event == IB_EVENT_CLIENT_REREGISTER) {
+ event->event == IB_EVENT_CLIENT_REREGISTER ||
+ event->event == IB_EVENT_GID_CHANGE) {
work = kmalloc(sizeof *work, GFP_ATOMIC);
if (work) {
INIT_WORK(&work->work, ib_cache_task);
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index fc0f2bd9ca82..8b72f39202fb 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -36,6 +36,7 @@
#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
+#include <linux/module.h>
#include <linux/err.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
@@ -889,6 +890,8 @@ retest:
break;
case IB_CM_ESTABLISHED:
spin_unlock_irq(&cm_id_priv->lock);
+ if (cm_id_priv->qp_type == IB_QPT_XRC_TGT)
+ break;
ib_send_cm_dreq(cm_id, NULL, 0);
goto retest;
case IB_CM_DREQ_SENT:
@@ -1008,7 +1011,6 @@ static void cm_format_req(struct cm_req_msg *req_msg,
req_msg->service_id = param->service_id;
req_msg->local_ca_guid = cm_id_priv->id.device->node_guid;
cm_req_set_local_qpn(req_msg, cpu_to_be32(param->qp_num));
- cm_req_set_resp_res(req_msg, param->responder_resources);
cm_req_set_init_depth(req_msg, param->initiator_depth);
cm_req_set_remote_resp_timeout(req_msg,
param->remote_cm_response_timeout);
@@ -1017,12 +1019,16 @@ static void cm_format_req(struct cm_req_msg *req_msg,
cm_req_set_starting_psn(req_msg, cpu_to_be32(param->starting_psn));
cm_req_set_local_resp_timeout(req_msg,
param->local_cm_response_timeout);
- cm_req_set_retry_count(req_msg, param->retry_count);
req_msg->pkey = param->primary_path->pkey;
cm_req_set_path_mtu(req_msg, param->primary_path->mtu);
- cm_req_set_rnr_retry_count(req_msg, param->rnr_retry_count);
cm_req_set_max_cm_retries(req_msg, param->max_cm_retries);
- cm_req_set_srq(req_msg, param->srq);
+
+ if (param->qp_type != IB_QPT_XRC_INI) {
+ cm_req_set_resp_res(req_msg, param->responder_resources);
+ cm_req_set_retry_count(req_msg, param->retry_count);
+ cm_req_set_rnr_retry_count(req_msg, param->rnr_retry_count);
+ cm_req_set_srq(req_msg, param->srq);
+ }
if (pri_path->hop_limit <= 1) {
req_msg->primary_local_lid = pri_path->slid;
@@ -1080,7 +1086,8 @@ static int cm_validate_req_param(struct ib_cm_req_param *param)
if (!param->primary_path)
return -EINVAL;
- if (param->qp_type != IB_QPT_RC && param->qp_type != IB_QPT_UC)
+ if (param->qp_type != IB_QPT_RC && param->qp_type != IB_QPT_UC &&
+ param->qp_type != IB_QPT_XRC_INI)
return -EINVAL;
if (param->private_data &&
@@ -1601,18 +1608,24 @@ static void cm_format_rep(struct cm_rep_msg *rep_msg,
cm_format_mad_hdr(&rep_msg->hdr, CM_REP_ATTR_ID, cm_id_priv->tid);
rep_msg->local_comm_id = cm_id_priv->id.local_id;
rep_msg->remote_comm_id = cm_id_priv->id.remote_id;
- cm_rep_set_local_qpn(rep_msg, cpu_to_be32(param->qp_num));
cm_rep_set_starting_psn(rep_msg, cpu_to_be32(param->starting_psn));
rep_msg->resp_resources = param->responder_resources;
- rep_msg->initiator_depth = param->initiator_depth;
cm_rep_set_target_ack_delay(rep_msg,
cm_id_priv->av.port->cm_dev->ack_delay);
cm_rep_set_failover(rep_msg, param->failover_accepted);
- cm_rep_set_flow_ctrl(rep_msg, param->flow_control);
cm_rep_set_rnr_retry_count(rep_msg, param->rnr_retry_count);
- cm_rep_set_srq(rep_msg, param->srq);
rep_msg->local_ca_guid = cm_id_priv->id.device->node_guid;
+ if (cm_id_priv->qp_type != IB_QPT_XRC_TGT) {
+ rep_msg->initiator_depth = param->initiator_depth;
+ cm_rep_set_flow_ctrl(rep_msg, param->flow_control);
+ cm_rep_set_srq(rep_msg, param->srq);
+ cm_rep_set_local_qpn(rep_msg, cpu_to_be32(param->qp_num));
+ } else {
+ cm_rep_set_srq(rep_msg, 1);
+ cm_rep_set_local_eecn(rep_msg, cpu_to_be32(param->qp_num));
+ }
+
if (param->private_data && param->private_data_len)
memcpy(rep_msg->private_data, param->private_data,
param->private_data_len);
@@ -1660,7 +1673,7 @@ int ib_send_cm_rep(struct ib_cm_id *cm_id,
cm_id_priv->initiator_depth = param->initiator_depth;
cm_id_priv->responder_resources = param->responder_resources;
cm_id_priv->rq_psn = cm_rep_get_starting_psn(rep_msg);
- cm_id_priv->local_qpn = cm_rep_get_local_qpn(rep_msg);
+ cm_id_priv->local_qpn = cpu_to_be32(param->qp_num & 0xFFFFFF);
out: spin_unlock_irqrestore(&cm_id_priv->lock, flags);
return ret;
@@ -1731,7 +1744,7 @@ error: spin_unlock_irqrestore(&cm_id_priv->lock, flags);
}
EXPORT_SYMBOL(ib_send_cm_rtu);
-static void cm_format_rep_event(struct cm_work *work)
+static void cm_format_rep_event(struct cm_work *work, enum ib_qp_type qp_type)
{
struct cm_rep_msg *rep_msg;
struct ib_cm_rep_event_param *param;
@@ -1740,7 +1753,7 @@ static void cm_format_rep_event(struct cm_work *work)
param = &work->cm_event.param.rep_rcvd;
param->remote_ca_guid = rep_msg->local_ca_guid;
param->remote_qkey = be32_to_cpu(rep_msg->local_qkey);
- param->remote_qpn = be32_to_cpu(cm_rep_get_local_qpn(rep_msg));
+ param->remote_qpn = be32_to_cpu(cm_rep_get_qpn(rep_msg, qp_type));
param->starting_psn = be32_to_cpu(cm_rep_get_starting_psn(rep_msg));
param->responder_resources = rep_msg->initiator_depth;
param->initiator_depth = rep_msg->resp_resources;
@@ -1808,7 +1821,7 @@ static int cm_rep_handler(struct cm_work *work)
return -EINVAL;
}
- cm_format_rep_event(work);
+ cm_format_rep_event(work, cm_id_priv->qp_type);
spin_lock_irq(&cm_id_priv->lock);
switch (cm_id_priv->id.state) {
@@ -1823,7 +1836,7 @@ static int cm_rep_handler(struct cm_work *work)
cm_id_priv->timewait_info->work.remote_id = rep_msg->local_comm_id;
cm_id_priv->timewait_info->remote_ca_guid = rep_msg->local_ca_guid;
- cm_id_priv->timewait_info->remote_qpn = cm_rep_get_local_qpn(rep_msg);
+ cm_id_priv->timewait_info->remote_qpn = cm_rep_get_qpn(rep_msg, cm_id_priv->qp_type);
spin_lock(&cm.lock);
/* Check for duplicate REP. */
@@ -1850,7 +1863,7 @@ static int cm_rep_handler(struct cm_work *work)
cm_id_priv->id.state = IB_CM_REP_RCVD;
cm_id_priv->id.remote_id = rep_msg->local_comm_id;
- cm_id_priv->remote_qpn = cm_rep_get_local_qpn(rep_msg);
+ cm_id_priv->remote_qpn = cm_rep_get_qpn(rep_msg, cm_id_priv->qp_type);
cm_id_priv->initiator_depth = rep_msg->resp_resources;
cm_id_priv->responder_resources = rep_msg->initiator_depth;
cm_id_priv->sq_psn = cm_rep_get_starting_psn(rep_msg);
@@ -3492,7 +3505,8 @@ static int cm_init_qp_rtr_attr(struct cm_id_private *cm_id_priv,
qp_attr->path_mtu = cm_id_priv->path_mtu;
qp_attr->dest_qp_num = be32_to_cpu(cm_id_priv->remote_qpn);
qp_attr->rq_psn = be32_to_cpu(cm_id_priv->rq_psn);
- if (cm_id_priv->qp_type == IB_QPT_RC) {
+ if (cm_id_priv->qp_type == IB_QPT_RC ||
+ cm_id_priv->qp_type == IB_QPT_XRC_TGT) {
*qp_attr_mask |= IB_QP_MAX_DEST_RD_ATOMIC |
IB_QP_MIN_RNR_TIMER;
qp_attr->max_dest_rd_atomic =
@@ -3537,15 +3551,21 @@ static int cm_init_qp_rts_attr(struct cm_id_private *cm_id_priv,
if (cm_id_priv->id.lap_state == IB_CM_LAP_UNINIT) {
*qp_attr_mask = IB_QP_STATE | IB_QP_SQ_PSN;
qp_attr->sq_psn = be32_to_cpu(cm_id_priv->sq_psn);
- if (cm_id_priv->qp_type == IB_QPT_RC) {
- *qp_attr_mask |= IB_QP_TIMEOUT | IB_QP_RETRY_CNT |
- IB_QP_RNR_RETRY |
+ switch (cm_id_priv->qp_type) {
+ case IB_QPT_RC:
+ case IB_QPT_XRC_INI:
+ *qp_attr_mask |= IB_QP_RETRY_CNT | IB_QP_RNR_RETRY |
IB_QP_MAX_QP_RD_ATOMIC;
- qp_attr->timeout = cm_id_priv->av.timeout;
qp_attr->retry_cnt = cm_id_priv->retry_count;
qp_attr->rnr_retry = cm_id_priv->rnr_retry_count;
- qp_attr->max_rd_atomic =
- cm_id_priv->initiator_depth;
+ qp_attr->max_rd_atomic = cm_id_priv->initiator_depth;
+ /* fall through */
+ case IB_QPT_XRC_TGT:
+ *qp_attr_mask |= IB_QP_TIMEOUT;
+ qp_attr->timeout = cm_id_priv->av.timeout;
+ break;
+ default:
+ break;
}
if (cm_id_priv->alt_av.ah_attr.dlid) {
*qp_attr_mask |= IB_QP_PATH_MIG_STATE;
diff --git a/drivers/infiniband/core/cm_msgs.h b/drivers/infiniband/core/cm_msgs.h
index 7e63c08f697c..505db2a59e7f 100644
--- a/drivers/infiniband/core/cm_msgs.h
+++ b/drivers/infiniband/core/cm_msgs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004 Intel Corporation. All rights reserved.
+ * Copyright (c) 2004, 2011 Intel Corporation. All rights reserved.
* Copyright (c) 2004 Topspin Corporation. All rights reserved.
* Copyright (c) 2004 Voltaire Corporation. All rights reserved.
*
@@ -86,7 +86,7 @@ struct cm_req_msg {
__be16 pkey;
/* path MTU:4, RDC exists:1, RNR retry count:3. */
u8 offset50;
- /* max CM Retries:4, SRQ:1, rsvd:3 */
+ /* max CM Retries:4, SRQ:1, extended transport type:3 */
u8 offset51;
__be16 primary_local_lid;
@@ -175,6 +175,11 @@ static inline enum ib_qp_type cm_req_get_qp_type(struct cm_req_msg *req_msg)
switch(transport_type) {
case 0: return IB_QPT_RC;
case 1: return IB_QPT_UC;
+ case 3:
+ switch (req_msg->offset51 & 0x7) {
+ case 1: return IB_QPT_XRC_TGT;
+ default: return 0;
+ }
default: return 0;
}
}
@@ -188,6 +193,12 @@ static inline void cm_req_set_qp_type(struct cm_req_msg *req_msg,
req_msg->offset40) &
0xFFFFFFF9) | 0x2);
break;
+ case IB_QPT_XRC_INI:
+ req_msg->offset40 = cpu_to_be32((be32_to_cpu(
+ req_msg->offset40) &
+ 0xFFFFFFF9) | 0x6);
+ req_msg->offset51 = (req_msg->offset51 & 0xF8) | 1;
+ break;
default:
req_msg->offset40 = cpu_to_be32(be32_to_cpu(
req_msg->offset40) &
@@ -527,6 +538,23 @@ static inline void cm_rep_set_local_qpn(struct cm_rep_msg *rep_msg, __be32 qpn)
(be32_to_cpu(rep_msg->offset12) & 0x000000FF));
}
+static inline __be32 cm_rep_get_local_eecn(struct cm_rep_msg *rep_msg)
+{
+ return cpu_to_be32(be32_to_cpu(rep_msg->offset16) >> 8);
+}
+
+static inline void cm_rep_set_local_eecn(struct cm_rep_msg *rep_msg, __be32 eecn)
+{
+ rep_msg->offset16 = cpu_to_be32((be32_to_cpu(eecn) << 8) |
+ (be32_to_cpu(rep_msg->offset16) & 0x000000FF));
+}
+
+static inline __be32 cm_rep_get_qpn(struct cm_rep_msg *rep_msg, enum ib_qp_type qp_type)
+{
+ return (qp_type == IB_QPT_XRC_INI) ?
+ cm_rep_get_local_eecn(rep_msg) : cm_rep_get_local_qpn(rep_msg);
+}
+
static inline __be32 cm_rep_get_starting_psn(struct cm_rep_msg *rep_msg)
{
return cpu_to_be32(be32_to_cpu(rep_msg->offset20) >> 8);
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index b6a33b3c516d..75ff821c0af0 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -41,6 +41,7 @@
#include <linux/idr.h>
#include <linux/inetdevice.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <net/tcp.h>
#include <net/ipv6.h>
@@ -81,6 +82,7 @@ static DEFINE_IDR(sdp_ps);
static DEFINE_IDR(tcp_ps);
static DEFINE_IDR(udp_ps);
static DEFINE_IDR(ipoib_ps);
+static DEFINE_IDR(ib_ps);
struct cma_device {
struct list_head list;
@@ -359,6 +361,10 @@ static int cma_acquire_dev(struct rdma_id_private *id_priv)
enum rdma_link_layer dev_ll = dev_addr->dev_type == ARPHRD_INFINIBAND ?
IB_LINK_LAYER_INFINIBAND : IB_LINK_LAYER_ETHERNET;
+ if (dev_ll != IB_LINK_LAYER_INFINIBAND &&
+ id_priv->id.ps == RDMA_PS_IPOIB)
+ return -EINVAL;
+
mutex_lock(&lock);
iboe_addr_get_sgid(dev_addr, &iboe_gid);
memcpy(&gid, dev_addr->src_dev_addr +
@@ -406,11 +412,6 @@ static int cma_disable_callback(struct rdma_id_private *id_priv,
return 0;
}
-static int cma_has_cm_dev(struct rdma_id_private *id_priv)
-{
- return (id_priv->id.device && id_priv->cm_id.ib);
-}
-
struct rdma_cm_id *rdma_create_id(rdma_cm_event_handler event_handler,
void *context, enum rdma_port_space ps,
enum ib_qp_type qp_type)
@@ -920,11 +921,11 @@ void rdma_destroy_id(struct rdma_cm_id *id)
if (id_priv->cma_dev) {
switch (rdma_node_get_transport(id_priv->id.device->node_type)) {
case RDMA_TRANSPORT_IB:
- if (id_priv->cm_id.ib && !IS_ERR(id_priv->cm_id.ib))
+ if (id_priv->cm_id.ib)
ib_destroy_cm_id(id_priv->cm_id.ib);
break;
case RDMA_TRANSPORT_IWARP:
- if (id_priv->cm_id.iw && !IS_ERR(id_priv->cm_id.iw))
+ if (id_priv->cm_id.iw)
iw_destroy_cm_id(id_priv->cm_id.iw);
break;
default:
@@ -1085,12 +1086,12 @@ static struct rdma_id_private *cma_new_conn_id(struct rdma_cm_id *listen_id,
if (cma_get_net_info(ib_event->private_data, listen_id->ps,
&ip_ver, &port, &src, &dst))
- goto err;
+ return NULL;
id = rdma_create_id(listen_id->event_handler, listen_id->context,
listen_id->ps, ib_event->param.req_rcvd.qp_type);
if (IS_ERR(id))
- goto err;
+ return NULL;
cma_save_net_info(&id->route.addr, &listen_id->route.addr,
ip_ver, port, src, dst);
@@ -1100,7 +1101,7 @@ static struct rdma_id_private *cma_new_conn_id(struct rdma_cm_id *listen_id,
rt->path_rec = kmalloc(sizeof *rt->path_rec * rt->num_paths,
GFP_KERNEL);
if (!rt->path_rec)
- goto destroy_id;
+ goto err;
rt->path_rec[0] = *ib_event->param.req_rcvd.primary_path;
if (rt->num_paths == 2)
@@ -1114,7 +1115,7 @@ static struct rdma_id_private *cma_new_conn_id(struct rdma_cm_id *listen_id,
ret = rdma_translate_ip((struct sockaddr *) &rt->addr.src_addr,
&rt->addr.dev_addr);
if (ret)
- goto destroy_id;
+ goto err;
}
rdma_addr_set_dgid(&rt->addr.dev_addr, &rt->path_rec[0].dgid);
@@ -1122,9 +1123,8 @@ static struct rdma_id_private *cma_new_conn_id(struct rdma_cm_id *listen_id,
id_priv->state = RDMA_CM_CONNECT;
return id_priv;
-destroy_id:
- rdma_destroy_id(id);
err:
+ rdma_destroy_id(id);
return NULL;
}
@@ -1181,6 +1181,15 @@ static void cma_set_req_event_data(struct rdma_cm_event *event,
event->param.conn.qp_num = req_data->remote_qpn;
}
+static int cma_check_req_qp_type(struct rdma_cm_id *id, struct ib_cm_event *ib_event)
+{
+ return (((ib_event->event == IB_CM_REQ_RECEIVED) ||
+ (ib_event->param.req_rcvd.qp_type == id->qp_type)) ||
+ ((ib_event->event == IB_CM_SIDR_REQ_RECEIVED) &&
+ (id->qp_type == IB_QPT_UD)) ||
+ (!id->qp_type));
+}
+
static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
{
struct rdma_id_private *listen_id, *conn_id;
@@ -1188,13 +1197,16 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
int offset, ret;
listen_id = cm_id->context;
+ if (!cma_check_req_qp_type(&listen_id->id, ib_event))
+ return -EINVAL;
+
if (cma_disable_callback(listen_id, RDMA_CM_LISTEN))
return -ECONNABORTED;
memset(&event, 0, sizeof event);
offset = cma_user_data_offset(listen_id->id.ps);
event.event = RDMA_CM_EVENT_CONNECT_REQUEST;
- if (listen_id->id.qp_type == IB_QPT_UD) {
+ if (ib_event->event == IB_CM_SIDR_REQ_RECEIVED) {
conn_id = cma_new_udp_id(&listen_id->id, ib_event);
event.param.ud.private_data = ib_event->private_data + offset;
event.param.ud.private_data_len =
@@ -1330,6 +1342,8 @@ static int cma_iw_handler(struct iw_cm_id *iw_id, struct iw_cm_event *iw_event)
switch (iw_event->status) {
case 0:
event.event = RDMA_CM_EVENT_ESTABLISHED;
+ event.param.conn.initiator_depth = iw_event->ird;
+ event.param.conn.responder_resources = iw_event->ord;
break;
case -ECONNRESET:
case -ECONNREFUSED:
@@ -1345,6 +1359,8 @@ static int cma_iw_handler(struct iw_cm_id *iw_id, struct iw_cm_event *iw_event)
break;
case IW_CM_EVENT_ESTABLISHED:
event.event = RDMA_CM_EVENT_ESTABLISHED;
+ event.param.conn.initiator_depth = iw_event->ird;
+ event.param.conn.responder_resources = iw_event->ord;
break;
default:
BUG_ON(1);
@@ -1435,8 +1451,8 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
event.event = RDMA_CM_EVENT_CONNECT_REQUEST;
event.param.conn.private_data = iw_event->private_data;
event.param.conn.private_data_len = iw_event->private_data_len;
- event.param.conn.initiator_depth = attr.max_qp_init_rd_atom;
- event.param.conn.responder_resources = attr.max_qp_rd_atom;
+ event.param.conn.initiator_depth = iw_event->ird;
+ event.param.conn.responder_resources = iw_event->ord;
/*
* Protect against the user destroying conn_id from another thread
@@ -1468,13 +1484,15 @@ static int cma_ib_listen(struct rdma_id_private *id_priv)
{
struct ib_cm_compare_data compare_data;
struct sockaddr *addr;
+ struct ib_cm_id *id;
__be64 svc_id;
int ret;
- id_priv->cm_id.ib = ib_create_cm_id(id_priv->id.device, cma_req_handler,
- id_priv);
- if (IS_ERR(id_priv->cm_id.ib))
- return PTR_ERR(id_priv->cm_id.ib);
+ id = ib_create_cm_id(id_priv->id.device, cma_req_handler, id_priv);
+ if (IS_ERR(id))
+ return PTR_ERR(id);
+
+ id_priv->cm_id.ib = id;
addr = (struct sockaddr *) &id_priv->id.route.addr.src_addr;
svc_id = cma_get_service_id(id_priv->id.ps, addr);
@@ -1497,12 +1515,15 @@ static int cma_iw_listen(struct rdma_id_private *id_priv, int backlog)
{
int ret;
struct sockaddr_in *sin;
+ struct iw_cm_id *id;
- id_priv->cm_id.iw = iw_create_cm_id(id_priv->id.device,
- iw_conn_req_handler,
- id_priv);
- if (IS_ERR(id_priv->cm_id.iw))
- return PTR_ERR(id_priv->cm_id.iw);
+ id = iw_create_cm_id(id_priv->id.device,
+ iw_conn_req_handler,
+ id_priv);
+ if (IS_ERR(id))
+ return PTR_ERR(id);
+
+ id_priv->cm_id.iw = id;
sin = (struct sockaddr_in *) &id_priv->id.route.addr.src_addr;
id_priv->cm_id.iw->local_addr = *sin;
@@ -2231,6 +2252,9 @@ static int cma_get_port(struct rdma_id_private *id_priv)
case RDMA_PS_IPOIB:
ps = &ipoib_ps;
break;
+ case RDMA_PS_IB:
+ ps = &ib_ps;
+ break;
default:
return -EPROTONOSUPPORT;
}
@@ -2484,6 +2508,7 @@ static int cma_resolve_ib_udp(struct rdma_id_private *id_priv,
{
struct ib_cm_sidr_req_param req;
struct rdma_route *route;
+ struct ib_cm_id *id;
int ret;
req.private_data_len = sizeof(struct cma_hdr) +
@@ -2501,12 +2526,13 @@ static int cma_resolve_ib_udp(struct rdma_id_private *id_priv,
if (ret)
goto out;
- id_priv->cm_id.ib = ib_create_cm_id(id_priv->id.device,
- cma_sidr_rep_handler, id_priv);
- if (IS_ERR(id_priv->cm_id.ib)) {
- ret = PTR_ERR(id_priv->cm_id.ib);
+ id = ib_create_cm_id(id_priv->id.device, cma_sidr_rep_handler,
+ id_priv);
+ if (IS_ERR(id)) {
+ ret = PTR_ERR(id);
goto out;
}
+ id_priv->cm_id.ib = id;
req.path = route->path_rec;
req.service_id = cma_get_service_id(id_priv->id.ps,
@@ -2530,6 +2556,7 @@ static int cma_connect_ib(struct rdma_id_private *id_priv,
struct ib_cm_req_param req;
struct rdma_route *route;
void *private_data;
+ struct ib_cm_id *id;
int offset, ret;
memset(&req, 0, sizeof req);
@@ -2543,12 +2570,12 @@ static int cma_connect_ib(struct rdma_id_private *id_priv,
memcpy(private_data + offset, conn_param->private_data,
conn_param->private_data_len);
- id_priv->cm_id.ib = ib_create_cm_id(id_priv->id.device, cma_ib_handler,
- id_priv);
- if (IS_ERR(id_priv->cm_id.ib)) {
- ret = PTR_ERR(id_priv->cm_id.ib);
+ id = ib_create_cm_id(id_priv->id.device, cma_ib_handler, id_priv);
+ if (IS_ERR(id)) {
+ ret = PTR_ERR(id);
goto out;
}
+ id_priv->cm_id.ib = id;
route = &id_priv->id.route;
ret = cma_format_hdr(private_data, id_priv->id.ps, route);
@@ -2563,7 +2590,7 @@ static int cma_connect_ib(struct rdma_id_private *id_priv,
req.service_id = cma_get_service_id(id_priv->id.ps,
(struct sockaddr *) &route->addr.dst_addr);
req.qp_num = id_priv->qp_num;
- req.qp_type = IB_QPT_RC;
+ req.qp_type = id_priv->id.qp_type;
req.starting_psn = id_priv->seq_num;
req.responder_resources = conn_param->responder_resources;
req.initiator_depth = conn_param->initiator_depth;
@@ -2577,8 +2604,8 @@ static int cma_connect_ib(struct rdma_id_private *id_priv,
ret = ib_send_cm_req(id_priv->cm_id.ib, &req);
out:
- if (ret && !IS_ERR(id_priv->cm_id.ib)) {
- ib_destroy_cm_id(id_priv->cm_id.ib);
+ if (ret && !IS_ERR(id)) {
+ ib_destroy_cm_id(id);
id_priv->cm_id.ib = NULL;
}
@@ -2595,10 +2622,8 @@ static int cma_connect_iw(struct rdma_id_private *id_priv,
struct iw_cm_conn_param iw_param;
cm_id = iw_create_cm_id(id_priv->id.device, cma_iw_handler, id_priv);
- if (IS_ERR(cm_id)) {
- ret = PTR_ERR(cm_id);
- goto out;
- }
+ if (IS_ERR(cm_id))
+ return PTR_ERR(cm_id);
id_priv->cm_id.iw = cm_id;
@@ -2612,17 +2637,19 @@ static int cma_connect_iw(struct rdma_id_private *id_priv,
if (ret)
goto out;
- iw_param.ord = conn_param->initiator_depth;
- iw_param.ird = conn_param->responder_resources;
- iw_param.private_data = conn_param->private_data;
- iw_param.private_data_len = conn_param->private_data_len;
- if (id_priv->id.qp)
+ if (conn_param) {
+ iw_param.ord = conn_param->initiator_depth;
+ iw_param.ird = conn_param->responder_resources;
+ iw_param.private_data = conn_param->private_data;
+ iw_param.private_data_len = conn_param->private_data_len;
+ iw_param.qpn = id_priv->id.qp ? id_priv->qp_num : conn_param->qp_num;
+ } else {
+ memset(&iw_param, 0, sizeof iw_param);
iw_param.qpn = id_priv->qp_num;
- else
- iw_param.qpn = conn_param->qp_num;
+ }
ret = iw_cm_connect(cm_id, &iw_param);
out:
- if (ret && !IS_ERR(cm_id)) {
+ if (ret) {
iw_destroy_cm_id(cm_id);
id_priv->cm_id.iw = NULL;
}
@@ -2761,14 +2788,20 @@ int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
- if (id->qp_type == IB_QPT_UD)
- ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
- conn_param->private_data,
- conn_param->private_data_len);
- else if (conn_param)
- ret = cma_accept_ib(id_priv, conn_param);
- else
- ret = cma_rep_recv(id_priv);
+ if (id->qp_type == IB_QPT_UD) {
+ if (conn_param)
+ ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
+ conn_param->private_data,
+ conn_param->private_data_len);
+ else
+ ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
+ NULL, 0);
+ } else {
+ if (conn_param)
+ ret = cma_accept_ib(id_priv, conn_param);
+ else
+ ret = cma_rep_recv(id_priv);
+ }
break;
case RDMA_TRANSPORT_IWARP:
ret = cma_accept_iw(id_priv, conn_param);
@@ -2795,7 +2828,7 @@ int rdma_notify(struct rdma_cm_id *id, enum ib_event_type event)
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- if (!cma_has_cm_dev(id_priv))
+ if (!id_priv->cm_id.ib)
return -EINVAL;
switch (id->device->node_type) {
@@ -2817,7 +2850,7 @@ int rdma_reject(struct rdma_cm_id *id, const void *private_data,
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- if (!cma_has_cm_dev(id_priv))
+ if (!id_priv->cm_id.ib)
return -EINVAL;
switch (rdma_node_get_transport(id->device->node_type)) {
@@ -2848,7 +2881,7 @@ int rdma_disconnect(struct rdma_cm_id *id)
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- if (!cma_has_cm_dev(id_priv))
+ if (!id_priv->cm_id.ib)
return -EINVAL;
switch (rdma_node_get_transport(id->device->node_type)) {
@@ -3456,6 +3489,7 @@ static void __exit cma_cleanup(void)
idr_destroy(&tcp_ps);
idr_destroy(&udp_ps);
idr_destroy(&ipoib_ps);
+ idr_destroy(&ib_ps);
}
module_init(cma_init);
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 4007f721d25d..e711de400a01 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -627,6 +627,9 @@ int ib_modify_device(struct ib_device *device,
int device_modify_mask,
struct ib_device_modify *device_modify)
{
+ if (!device->modify_device)
+ return -ENOSYS;
+
return device->modify_device(device, device_modify_mask,
device_modify);
}
@@ -647,6 +650,9 @@ int ib_modify_port(struct ib_device *device,
u8 port_num, int port_modify_mask,
struct ib_port_modify *port_modify)
{
+ if (!device->modify_port)
+ return -ENOSYS;
+
if (port_num < start_port(device) || port_num > end_port(device))
return -EINVAL;
diff --git a/drivers/infiniband/core/fmr_pool.c b/drivers/infiniband/core/fmr_pool.c
index 4507043d24c8..176c8f90f2bb 100644
--- a/drivers/infiniband/core/fmr_pool.c
+++ b/drivers/infiniband/core/fmr_pool.c
@@ -33,6 +33,7 @@
#include <linux/errno.h>
#include <linux/spinlock.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/jhash.h>
#include <linux/kthread.h>
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c
index a9c042345c6f..1a696f76b616 100644
--- a/drivers/infiniband/core/iwcm.c
+++ b/drivers/infiniband/core/iwcm.c
@@ -45,6 +45,7 @@
#include <linux/workqueue.h>
#include <linux/completion.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <rdma/iw_cm.h>
#include <rdma/ib_addr.h>
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index b4d8672a3e4e..2fe428bba54c 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -35,6 +35,7 @@
*/
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <rdma/ib_cache.h>
#include "mad_priv.h"
@@ -1596,6 +1597,9 @@ find_mad_agent(struct ib_mad_port_private *port_priv,
mad->mad_hdr.class_version].class;
if (!class)
goto out;
+ if (convert_mgmt_class(mad->mad_hdr.mgmt_class) >=
+ IB_MGMT_MAX_METHODS)
+ goto out;
method = class->method_table[convert_mgmt_class(
mad->mad_hdr.mgmt_class)];
if (method)
diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c
index 68b4162fd9d2..d2360a8ef0b2 100644
--- a/drivers/infiniband/core/multicast.c
+++ b/drivers/infiniband/core/multicast.c
@@ -34,6 +34,7 @@
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/random.h>
diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c
index 4a5abaf0a25c..d1c8196d15d7 100644
--- a/drivers/infiniband/core/netlink.c
+++ b/drivers/infiniband/core/netlink.c
@@ -32,6 +32,7 @@
#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
+#include <linux/export.h>
#include <net/netlink.h>
#include <net/net_namespace.h>
#include <net/sock.h>
@@ -148,7 +149,7 @@ static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return -EINVAL;
return netlink_dump_start(nls, skb, nlh,
client->cb_table[op].dump,
- NULL);
+ NULL, 0);
}
}
diff --git a/drivers/infiniband/core/packer.c b/drivers/infiniband/core/packer.c
index 019bd4b0863e..1b65986c0be3 100644
--- a/drivers/infiniband/core/packer.c
+++ b/drivers/infiniband/core/packer.c
@@ -31,6 +31,7 @@
* SOFTWARE.
*/
+#include <linux/export.h>
#include <linux/string.h>
#include <rdma/ib_pack.h>
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 9ab5df72df7b..c61bca30fd2d 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -35,6 +35,7 @@
#include "core_priv.h"
#include <linux/slab.h>
+#include <linux/stat.h>
#include <linux/string.h>
#include <rdma/ib_mad.h>
@@ -185,17 +186,35 @@ static ssize_t rate_show(struct ib_port *p, struct port_attribute *unused,
if (ret)
return ret;
+ rate = (25 * attr.active_speed) / 10;
+
switch (attr.active_speed) {
- case 2: speed = " DDR"; break;
- case 4: speed = " QDR"; break;
+ case 2:
+ speed = " DDR";
+ break;
+ case 4:
+ speed = " QDR";
+ break;
+ case 8:
+ speed = " FDR10";
+ rate = 10;
+ break;
+ case 16:
+ speed = " FDR";
+ rate = 14;
+ break;
+ case 32:
+ speed = " EDR";
+ rate = 25;
+ break;
}
- rate = 25 * ib_width_enum_to_int(attr.active_width) * attr.active_speed;
+ rate *= ib_width_enum_to_int(attr.active_width);
if (rate < 0)
return -EINVAL;
return sprintf(buf, "%d%s Gb/sec (%dX%s)\n",
- rate / 10, rate % 10 ? ".5" : "",
+ rate, (attr.active_speed == 1) ? ".5" : "",
ib_width_enum_to_int(attr.active_width), speed);
}
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index 08f948df8fa9..b8a0b4a7811b 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -1122,7 +1122,7 @@ static ssize_t ib_ucm_write(struct file *filp, const char __user *buf,
if (copy_from_user(&hdr, buf, sizeof(hdr)))
return -EFAULT;
- if (hdr.cmd < 0 || hdr.cmd >= ARRAY_SIZE(ucm_cmd_table))
+ if (hdr.cmd >= ARRAY_SIZE(ucm_cmd_table))
return -EINVAL;
if (hdr.in + sizeof(hdr) > len)
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 71be5eebd683..b37b0c02a7b9 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -41,6 +41,7 @@
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
+#include <linux/module.h>
#include <rdma/rdma_user_cm.h>
#include <rdma/ib_marshall.h>
@@ -276,7 +277,7 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id,
ucma_set_event_context(ctx, event, uevent);
uevent->resp.event = event->event;
uevent->resp.status = event->status;
- if (cm_id->ps == RDMA_PS_UDP || cm_id->ps == RDMA_PS_IPOIB)
+ if (cm_id->qp_type == IB_QPT_UD)
ucma_copy_ud_event(&uevent->resp.param.ud, &event->param.ud);
else
ucma_copy_conn_event(&uevent->resp.param.conn,
@@ -377,6 +378,9 @@ static int ucma_get_qp_type(struct rdma_ucm_create_id *cmd, enum ib_qp_type *qp_
case RDMA_PS_IPOIB:
*qp_type = IB_QPT_UD;
return 0;
+ case RDMA_PS_IB:
+ *qp_type = cmd->qp_type;
+ return 0;
default:
return -EINVAL;
}
@@ -1270,7 +1274,7 @@ static ssize_t ucma_write(struct file *filp, const char __user *buf,
if (copy_from_user(&hdr, buf, sizeof(hdr)))
return -EFAULT;
- if (hdr.cmd < 0 || hdr.cmd >= ARRAY_SIZE(ucma_cmd_table))
+ if (hdr.cmd >= ARRAY_SIZE(ucma_cmd_table))
return -EINVAL;
if (hdr.in + sizeof(hdr) > len)
diff --git a/drivers/infiniband/core/ud_header.c b/drivers/infiniband/core/ud_header.c
index 9b737ff133e2..72feee620ebf 100644
--- a/drivers/infiniband/core/ud_header.c
+++ b/drivers/infiniband/core/ud_header.c
@@ -33,6 +33,7 @@
#include <linux/errno.h>
#include <linux/string.h>
+#include <linux/export.h>
#include <linux/if_ether.h>
#include <rdma/ib_pack.h>
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index b645e558876f..71f0c0f7df94 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -35,6 +35,7 @@
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/sched.h>
+#include <linux/export.h>
#include <linux/hugetlb.h>
#include <linux/dma-attrs.h>
#include <linux/slab.h>
@@ -136,7 +137,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
down_write(&current->mm->mmap_sem);
- locked = npages + current->mm->locked_vm;
+ locked = npages + current->mm->pinned_vm;
lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) {
@@ -206,7 +207,7 @@ out:
__ib_umem_release(context->device, umem, 0);
kfree(umem);
} else
- current->mm->locked_vm = locked;
+ current->mm->pinned_vm = locked;
up_write(&current->mm->mmap_sem);
if (vma_list)
@@ -222,7 +223,7 @@ static void ib_umem_account(struct work_struct *work)
struct ib_umem *umem = container_of(work, struct ib_umem, work);
down_write(&umem->mm->mmap_sem);
- umem->mm->locked_vm -= umem->diff;
+ umem->mm->pinned_vm -= umem->diff;
up_write(&umem->mm->mmap_sem);
mmput(umem->mm);
kfree(umem);
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index 8d261b6ea5fe..07db22997e97 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -458,8 +458,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
goto err;
}
- if (packet->mad.hdr.id < 0 ||
- packet->mad.hdr.id >= IB_UMAD_MAX_AGENTS) {
+ if (packet->mad.hdr.id >= IB_UMAD_MAX_AGENTS) {
ret = -EINVAL;
goto err;
}
@@ -703,7 +702,7 @@ static int ib_umad_unreg_agent(struct ib_umad_file *file, u32 __user *arg)
mutex_lock(&file->port->file_mutex);
mutex_lock(&file->mutex);
- if (id < 0 || id >= IB_UMAD_MAX_AGENTS || !__get_agent(file, id)) {
+ if (id >= IB_UMAD_MAX_AGENTS || !__get_agent(file, id)) {
ret = -EINVAL;
goto out;
}
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index a078e5624d22..5bcb2afd3dcb 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -76,6 +76,8 @@ struct ib_uverbs_device {
struct ib_device *ib_dev;
int devnum;
struct cdev cdev;
+ struct rb_root xrcd_tree;
+ struct mutex xrcd_tree_mutex;
};
struct ib_uverbs_event_file {
@@ -120,6 +122,16 @@ struct ib_uevent_object {
u32 events_reported;
};
+struct ib_uxrcd_object {
+ struct ib_uobject uobject;
+ atomic_t refcnt;
+};
+
+struct ib_usrq_object {
+ struct ib_uevent_object uevent;
+ struct ib_uxrcd_object *uxrcd;
+};
+
struct ib_uqp_object {
struct ib_uevent_object uevent;
struct list_head mcast_list;
@@ -142,6 +154,7 @@ extern struct idr ib_uverbs_ah_idr;
extern struct idr ib_uverbs_cq_idr;
extern struct idr ib_uverbs_qp_idr;
extern struct idr ib_uverbs_srq_idr;
+extern struct idr ib_uverbs_xrcd_idr;
void idr_remove_uobj(struct idr *idp, struct ib_uobject *uobj);
@@ -161,6 +174,7 @@ void ib_uverbs_qp_event_handler(struct ib_event *event, void *context_ptr);
void ib_uverbs_srq_event_handler(struct ib_event *event, void *context_ptr);
void ib_uverbs_event_handler(struct ib_event_handler *handler,
struct ib_event *event);
+void ib_uverbs_dealloc_xrcd(struct ib_uverbs_device *dev, struct ib_xrcd *xrcd);
#define IB_UVERBS_DECLARE_CMD(name) \
ssize_t ib_uverbs_##name(struct ib_uverbs_file *file, \
@@ -181,6 +195,7 @@ IB_UVERBS_DECLARE_CMD(poll_cq);
IB_UVERBS_DECLARE_CMD(req_notify_cq);
IB_UVERBS_DECLARE_CMD(destroy_cq);
IB_UVERBS_DECLARE_CMD(create_qp);
+IB_UVERBS_DECLARE_CMD(open_qp);
IB_UVERBS_DECLARE_CMD(query_qp);
IB_UVERBS_DECLARE_CMD(modify_qp);
IB_UVERBS_DECLARE_CMD(destroy_qp);
@@ -195,5 +210,8 @@ IB_UVERBS_DECLARE_CMD(create_srq);
IB_UVERBS_DECLARE_CMD(modify_srq);
IB_UVERBS_DECLARE_CMD(query_srq);
IB_UVERBS_DECLARE_CMD(destroy_srq);
+IB_UVERBS_DECLARE_CMD(create_xsrq);
+IB_UVERBS_DECLARE_CMD(open_xrcd);
+IB_UVERBS_DECLARE_CMD(close_xrcd);
#endif /* UVERBS_H */
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index c42699285f8e..254f1649c734 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -47,6 +47,7 @@ static struct lock_class_key cq_lock_key;
static struct lock_class_key qp_lock_key;
static struct lock_class_key ah_lock_key;
static struct lock_class_key srq_lock_key;
+static struct lock_class_key xrcd_lock_key;
#define INIT_UDATA(udata, ibuf, obuf, ilen, olen) \
do { \
@@ -255,6 +256,18 @@ static void put_srq_read(struct ib_srq *srq)
put_uobj_read(srq->uobject);
}
+static struct ib_xrcd *idr_read_xrcd(int xrcd_handle, struct ib_ucontext *context,
+ struct ib_uobject **uobj)
+{
+ *uobj = idr_read_uobj(&ib_uverbs_xrcd_idr, xrcd_handle, context, 0);
+ return *uobj ? (*uobj)->object : NULL;
+}
+
+static void put_xrcd_read(struct ib_uobject *uobj)
+{
+ put_uobj_read(uobj);
+}
+
ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
const char __user *buf,
int in_len, int out_len)
@@ -298,6 +311,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
INIT_LIST_HEAD(&ucontext->qp_list);
INIT_LIST_HEAD(&ucontext->srq_list);
INIT_LIST_HEAD(&ucontext->ah_list);
+ INIT_LIST_HEAD(&ucontext->xrcd_list);
ucontext->closing = 0;
resp.num_comp_vectors = file->device->num_comp_vectors;
@@ -579,6 +593,310 @@ ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file,
return in_len;
}
+struct xrcd_table_entry {
+ struct rb_node node;
+ struct ib_xrcd *xrcd;
+ struct inode *inode;
+};
+
+static int xrcd_table_insert(struct ib_uverbs_device *dev,
+ struct inode *inode,
+ struct ib_xrcd *xrcd)
+{
+ struct xrcd_table_entry *entry, *scan;
+ struct rb_node **p = &dev->xrcd_tree.rb_node;
+ struct rb_node *parent = NULL;
+
+ entry = kmalloc(sizeof *entry, GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->xrcd = xrcd;
+ entry->inode = inode;
+
+ while (*p) {
+ parent = *p;
+ scan = rb_entry(parent, struct xrcd_table_entry, node);
+
+ if (inode < scan->inode) {
+ p = &(*p)->rb_left;
+ } else if (inode > scan->inode) {
+ p = &(*p)->rb_right;
+ } else {
+ kfree(entry);
+ return -EEXIST;
+ }
+ }
+
+ rb_link_node(&entry->node, parent, p);
+ rb_insert_color(&entry->node, &dev->xrcd_tree);
+ igrab(inode);
+ return 0;
+}
+
+static struct xrcd_table_entry *xrcd_table_search(struct ib_uverbs_device *dev,
+ struct inode *inode)
+{
+ struct xrcd_table_entry *entry;
+ struct rb_node *p = dev->xrcd_tree.rb_node;
+
+ while (p) {
+ entry = rb_entry(p, struct xrcd_table_entry, node);
+
+ if (inode < entry->inode)
+ p = p->rb_left;
+ else if (inode > entry->inode)
+ p = p->rb_right;
+ else
+ return entry;
+ }
+
+ return NULL;
+}
+
+static struct ib_xrcd *find_xrcd(struct ib_uverbs_device *dev, struct inode *inode)
+{
+ struct xrcd_table_entry *entry;
+
+ entry = xrcd_table_search(dev, inode);
+ if (!entry)
+ return NULL;
+
+ return entry->xrcd;
+}
+
+static void xrcd_table_delete(struct ib_uverbs_device *dev,
+ struct inode *inode)
+{
+ struct xrcd_table_entry *entry;
+
+ entry = xrcd_table_search(dev, inode);
+ if (entry) {
+ iput(inode);
+ rb_erase(&entry->node, &dev->xrcd_tree);
+ kfree(entry);
+ }
+}
+
+ssize_t ib_uverbs_open_xrcd(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len,
+ int out_len)
+{
+ struct ib_uverbs_open_xrcd cmd;
+ struct ib_uverbs_open_xrcd_resp resp;
+ struct ib_udata udata;
+ struct ib_uxrcd_object *obj;
+ struct ib_xrcd *xrcd = NULL;
+ struct file *f = NULL;
+ struct inode *inode = NULL;
+ int ret = 0;
+ int new_xrcd = 0;
+
+ if (out_len < sizeof resp)
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, buf, sizeof cmd))
+ return -EFAULT;
+
+ INIT_UDATA(&udata, buf + sizeof cmd,
+ (unsigned long) cmd.response + sizeof resp,
+ in_len - sizeof cmd, out_len - sizeof resp);
+
+ mutex_lock(&file->device->xrcd_tree_mutex);
+
+ if (cmd.fd != -1) {
+ /* search for file descriptor */
+ f = fget(cmd.fd);
+ if (!f) {
+ ret = -EBADF;
+ goto err_tree_mutex_unlock;
+ }
+
+ inode = f->f_dentry->d_inode;
+ if (!inode) {
+ ret = -EBADF;
+ goto err_tree_mutex_unlock;
+ }
+
+ xrcd = find_xrcd(file->device, inode);
+ if (!xrcd && !(cmd.oflags & O_CREAT)) {
+ /* no file descriptor. Need CREATE flag */
+ ret = -EAGAIN;
+ goto err_tree_mutex_unlock;
+ }
+
+ if (xrcd && cmd.oflags & O_EXCL) {
+ ret = -EINVAL;
+ goto err_tree_mutex_unlock;
+ }
+ }
+
+ obj = kmalloc(sizeof *obj, GFP_KERNEL);
+ if (!obj) {
+ ret = -ENOMEM;
+ goto err_tree_mutex_unlock;
+ }
+
+ init_uobj(&obj->uobject, 0, file->ucontext, &xrcd_lock_key);
+
+ down_write(&obj->uobject.mutex);
+
+ if (!xrcd) {
+ xrcd = file->device->ib_dev->alloc_xrcd(file->device->ib_dev,
+ file->ucontext, &udata);
+ if (IS_ERR(xrcd)) {
+ ret = PTR_ERR(xrcd);
+ goto err;
+ }
+
+ xrcd->inode = inode;
+ xrcd->device = file->device->ib_dev;
+ atomic_set(&xrcd->usecnt, 0);
+ mutex_init(&xrcd->tgt_qp_mutex);
+ INIT_LIST_HEAD(&xrcd->tgt_qp_list);
+ new_xrcd = 1;
+ }
+
+ atomic_set(&obj->refcnt, 0);
+ obj->uobject.object = xrcd;
+ ret = idr_add_uobj(&ib_uverbs_xrcd_idr, &obj->uobject);
+ if (ret)
+ goto err_idr;
+
+ memset(&resp, 0, sizeof resp);
+ resp.xrcd_handle = obj->uobject.id;
+
+ if (inode) {
+ if (new_xrcd) {
+ /* create new inode/xrcd table entry */
+ ret = xrcd_table_insert(file->device, inode, xrcd);
+ if (ret)
+ goto err_insert_xrcd;
+ }
+ atomic_inc(&xrcd->usecnt);
+ }
+
+ if (copy_to_user((void __user *) (unsigned long) cmd.response,
+ &resp, sizeof resp)) {
+ ret = -EFAULT;
+ goto err_copy;
+ }
+
+ if (f)
+ fput(f);
+
+ mutex_lock(&file->mutex);
+ list_add_tail(&obj->uobject.list, &file->ucontext->xrcd_list);
+ mutex_unlock(&file->mutex);
+
+ obj->uobject.live = 1;
+ up_write(&obj->uobject.mutex);
+
+ mutex_unlock(&file->device->xrcd_tree_mutex);
+ return in_len;
+
+err_copy:
+ if (inode) {
+ if (new_xrcd)
+ xrcd_table_delete(file->device, inode);
+ atomic_dec(&xrcd->usecnt);
+ }
+
+err_insert_xrcd:
+ idr_remove_uobj(&ib_uverbs_xrcd_idr, &obj->uobject);
+
+err_idr:
+ ib_dealloc_xrcd(xrcd);
+
+err:
+ put_uobj_write(&obj->uobject);
+
+err_tree_mutex_unlock:
+ if (f)
+ fput(f);
+
+ mutex_unlock(&file->device->xrcd_tree_mutex);
+
+ return ret;
+}
+
+ssize_t ib_uverbs_close_xrcd(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len,
+ int out_len)
+{
+ struct ib_uverbs_close_xrcd cmd;
+ struct ib_uobject *uobj;
+ struct ib_xrcd *xrcd = NULL;
+ struct inode *inode = NULL;
+ struct ib_uxrcd_object *obj;
+ int live;
+ int ret = 0;
+
+ if (copy_from_user(&cmd, buf, sizeof cmd))
+ return -EFAULT;
+
+ mutex_lock(&file->device->xrcd_tree_mutex);
+ uobj = idr_write_uobj(&ib_uverbs_xrcd_idr, cmd.xrcd_handle, file->ucontext);
+ if (!uobj) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ xrcd = uobj->object;
+ inode = xrcd->inode;
+ obj = container_of(uobj, struct ib_uxrcd_object, uobject);
+ if (atomic_read(&obj->refcnt)) {
+ put_uobj_write(uobj);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (!inode || atomic_dec_and_test(&xrcd->usecnt)) {
+ ret = ib_dealloc_xrcd(uobj->object);
+ if (!ret)
+ uobj->live = 0;
+ }
+
+ live = uobj->live;
+ if (inode && ret)
+ atomic_inc(&xrcd->usecnt);
+
+ put_uobj_write(uobj);
+
+ if (ret)
+ goto out;
+
+ if (inode && !live)
+ xrcd_table_delete(file->device, inode);
+
+ idr_remove_uobj(&ib_uverbs_xrcd_idr, uobj);
+ mutex_lock(&file->mutex);
+ list_del(&uobj->list);
+ mutex_unlock(&file->mutex);
+
+ put_uobj(uobj);
+ ret = in_len;
+
+out:
+ mutex_unlock(&file->device->xrcd_tree_mutex);
+ return ret;
+}
+
+void ib_uverbs_dealloc_xrcd(struct ib_uverbs_device *dev,
+ struct ib_xrcd *xrcd)
+{
+ struct inode *inode;
+
+ inode = xrcd->inode;
+ if (inode && !atomic_dec_and_test(&xrcd->usecnt))
+ return;
+
+ ib_dealloc_xrcd(xrcd);
+
+ if (inode)
+ xrcd_table_delete(dev, inode);
+}
+
ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,
const char __user *buf, int in_len,
int out_len)
@@ -1052,9 +1370,12 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
struct ib_uverbs_create_qp_resp resp;
struct ib_udata udata;
struct ib_uqp_object *obj;
- struct ib_pd *pd;
- struct ib_cq *scq, *rcq;
- struct ib_srq *srq;
+ struct ib_device *device;
+ struct ib_pd *pd = NULL;
+ struct ib_xrcd *xrcd = NULL;
+ struct ib_uobject *uninitialized_var(xrcd_uobj);
+ struct ib_cq *scq = NULL, *rcq = NULL;
+ struct ib_srq *srq = NULL;
struct ib_qp *qp;
struct ib_qp_init_attr attr;
int ret;
@@ -1076,15 +1397,39 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
init_uobj(&obj->uevent.uobject, cmd.user_handle, file->ucontext, &qp_lock_key);
down_write(&obj->uevent.uobject.mutex);
- srq = cmd.is_srq ? idr_read_srq(cmd.srq_handle, file->ucontext) : NULL;
- pd = idr_read_pd(cmd.pd_handle, file->ucontext);
- scq = idr_read_cq(cmd.send_cq_handle, file->ucontext, 0);
- rcq = cmd.recv_cq_handle == cmd.send_cq_handle ?
- scq : idr_read_cq(cmd.recv_cq_handle, file->ucontext, 1);
+ if (cmd.qp_type == IB_QPT_XRC_TGT) {
+ xrcd = idr_read_xrcd(cmd.pd_handle, file->ucontext, &xrcd_uobj);
+ if (!xrcd) {
+ ret = -EINVAL;
+ goto err_put;
+ }
+ device = xrcd->device;
+ } else {
+ pd = idr_read_pd(cmd.pd_handle, file->ucontext);
+ scq = idr_read_cq(cmd.send_cq_handle, file->ucontext, 0);
+ if (!pd || !scq) {
+ ret = -EINVAL;
+ goto err_put;
+ }
- if (!pd || !scq || !rcq || (cmd.is_srq && !srq)) {
- ret = -EINVAL;
- goto err_put;
+ if (cmd.qp_type == IB_QPT_XRC_INI) {
+ cmd.max_recv_wr = cmd.max_recv_sge = 0;
+ } else {
+ if (cmd.is_srq) {
+ srq = idr_read_srq(cmd.srq_handle, file->ucontext);
+ if (!srq || srq->srq_type != IB_SRQT_BASIC) {
+ ret = -EINVAL;
+ goto err_put;
+ }
+ }
+ rcq = (cmd.recv_cq_handle == cmd.send_cq_handle) ?
+ scq : idr_read_cq(cmd.recv_cq_handle, file->ucontext, 1);
+ if (!rcq) {
+ ret = -EINVAL;
+ goto err_put;
+ }
+ }
+ device = pd->device;
}
attr.event_handler = ib_uverbs_qp_event_handler;
@@ -1092,6 +1437,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
attr.send_cq = scq;
attr.recv_cq = rcq;
attr.srq = srq;
+ attr.xrcd = xrcd;
attr.sq_sig_type = cmd.sq_sig_all ? IB_SIGNAL_ALL_WR : IB_SIGNAL_REQ_WR;
attr.qp_type = cmd.qp_type;
attr.create_flags = 0;
@@ -1106,26 +1452,34 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
INIT_LIST_HEAD(&obj->uevent.event_list);
INIT_LIST_HEAD(&obj->mcast_list);
- qp = pd->device->create_qp(pd, &attr, &udata);
+ if (cmd.qp_type == IB_QPT_XRC_TGT)
+ qp = ib_create_qp(pd, &attr);
+ else
+ qp = device->create_qp(pd, &attr, &udata);
+
if (IS_ERR(qp)) {
ret = PTR_ERR(qp);
goto err_put;
}
- qp->device = pd->device;
- qp->pd = pd;
- qp->send_cq = attr.send_cq;
- qp->recv_cq = attr.recv_cq;
- qp->srq = attr.srq;
- qp->uobject = &obj->uevent.uobject;
- qp->event_handler = attr.event_handler;
- qp->qp_context = attr.qp_context;
- qp->qp_type = attr.qp_type;
- atomic_inc(&pd->usecnt);
- atomic_inc(&attr.send_cq->usecnt);
- atomic_inc(&attr.recv_cq->usecnt);
- if (attr.srq)
- atomic_inc(&attr.srq->usecnt);
+ if (cmd.qp_type != IB_QPT_XRC_TGT) {
+ qp->real_qp = qp;
+ qp->device = device;
+ qp->pd = pd;
+ qp->send_cq = attr.send_cq;
+ qp->recv_cq = attr.recv_cq;
+ qp->srq = attr.srq;
+ qp->event_handler = attr.event_handler;
+ qp->qp_context = attr.qp_context;
+ qp->qp_type = attr.qp_type;
+ atomic_inc(&pd->usecnt);
+ atomic_inc(&attr.send_cq->usecnt);
+ if (attr.recv_cq)
+ atomic_inc(&attr.recv_cq->usecnt);
+ if (attr.srq)
+ atomic_inc(&attr.srq->usecnt);
+ }
+ qp->uobject = &obj->uevent.uobject;
obj->uevent.uobject.object = qp;
ret = idr_add_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);
@@ -1147,9 +1501,13 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
goto err_copy;
}
- put_pd_read(pd);
- put_cq_read(scq);
- if (rcq != scq)
+ if (xrcd)
+ put_xrcd_read(xrcd_uobj);
+ if (pd)
+ put_pd_read(pd);
+ if (scq)
+ put_cq_read(scq);
+ if (rcq && rcq != scq)
put_cq_read(rcq);
if (srq)
put_srq_read(srq);
@@ -1171,6 +1529,8 @@ err_destroy:
ib_destroy_qp(qp);
err_put:
+ if (xrcd)
+ put_xrcd_read(xrcd_uobj);
if (pd)
put_pd_read(pd);
if (scq)
@@ -1184,6 +1544,98 @@ err_put:
return ret;
}
+ssize_t ib_uverbs_open_qp(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len, int out_len)
+{
+ struct ib_uverbs_open_qp cmd;
+ struct ib_uverbs_create_qp_resp resp;
+ struct ib_udata udata;
+ struct ib_uqp_object *obj;
+ struct ib_xrcd *xrcd;
+ struct ib_uobject *uninitialized_var(xrcd_uobj);
+ struct ib_qp *qp;
+ struct ib_qp_open_attr attr;
+ int ret;
+
+ if (out_len < sizeof resp)
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, buf, sizeof cmd))
+ return -EFAULT;
+
+ INIT_UDATA(&udata, buf + sizeof cmd,
+ (unsigned long) cmd.response + sizeof resp,
+ in_len - sizeof cmd, out_len - sizeof resp);
+
+ obj = kmalloc(sizeof *obj, GFP_KERNEL);
+ if (!obj)
+ return -ENOMEM;
+
+ init_uobj(&obj->uevent.uobject, cmd.user_handle, file->ucontext, &qp_lock_key);
+ down_write(&obj->uevent.uobject.mutex);
+
+ xrcd = idr_read_xrcd(cmd.pd_handle, file->ucontext, &xrcd_uobj);
+ if (!xrcd) {
+ ret = -EINVAL;
+ goto err_put;
+ }
+
+ attr.event_handler = ib_uverbs_qp_event_handler;
+ attr.qp_context = file;
+ attr.qp_num = cmd.qpn;
+ attr.qp_type = cmd.qp_type;
+
+ obj->uevent.events_reported = 0;
+ INIT_LIST_HEAD(&obj->uevent.event_list);
+ INIT_LIST_HEAD(&obj->mcast_list);
+
+ qp = ib_open_qp(xrcd, &attr);
+ if (IS_ERR(qp)) {
+ ret = PTR_ERR(qp);
+ goto err_put;
+ }
+
+ qp->uobject = &obj->uevent.uobject;
+
+ obj->uevent.uobject.object = qp;
+ ret = idr_add_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);
+ if (ret)
+ goto err_destroy;
+
+ memset(&resp, 0, sizeof resp);
+ resp.qpn = qp->qp_num;
+ resp.qp_handle = obj->uevent.uobject.id;
+
+ if (copy_to_user((void __user *) (unsigned long) cmd.response,
+ &resp, sizeof resp)) {
+ ret = -EFAULT;
+ goto err_remove;
+ }
+
+ put_xrcd_read(xrcd_uobj);
+
+ mutex_lock(&file->mutex);
+ list_add_tail(&obj->uevent.uobject.list, &file->ucontext->qp_list);
+ mutex_unlock(&file->mutex);
+
+ obj->uevent.uobject.live = 1;
+
+ up_write(&obj->uevent.uobject.mutex);
+
+ return in_len;
+
+err_remove:
+ idr_remove_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);
+
+err_destroy:
+ ib_destroy_qp(qp);
+
+err_put:
+ put_xrcd_read(xrcd_uobj);
+ put_uobj_write(&obj->uevent.uobject);
+ return ret;
+}
+
ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file,
const char __user *buf, int in_len,
int out_len)
@@ -1284,6 +1736,20 @@ out:
return ret ? ret : in_len;
}
+/* Remove ignored fields set in the attribute mask */
+static int modify_qp_mask(enum ib_qp_type qp_type, int mask)
+{
+ switch (qp_type) {
+ case IB_QPT_XRC_INI:
+ return mask & ~(IB_QP_MAX_DEST_RD_ATOMIC | IB_QP_MIN_RNR_TIMER);
+ case IB_QPT_XRC_TGT:
+ return mask & ~(IB_QP_MAX_QP_RD_ATOMIC | IB_QP_RETRY_CNT |
+ IB_QP_RNR_RETRY);
+ default:
+ return mask;
+ }
+}
+
ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,
const char __user *buf, int in_len,
int out_len)
@@ -1356,7 +1822,12 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,
attr->alt_ah_attr.ah_flags = cmd.alt_dest.is_global ? IB_AH_GRH : 0;
attr->alt_ah_attr.port_num = cmd.alt_dest.port_num;
- ret = qp->device->modify_qp(qp, attr, cmd.attr_mask, &udata);
+ if (qp->real_qp == qp) {
+ ret = qp->device->modify_qp(qp, attr,
+ modify_qp_mask(qp->qp_type, cmd.attr_mask), &udata);
+ } else {
+ ret = ib_modify_qp(qp, attr, modify_qp_mask(qp->qp_type, cmd.attr_mask));
+ }
put_qp_read(qp);
@@ -1553,7 +2024,7 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
}
resp.bad_wr = 0;
- ret = qp->device->post_send(qp, wr, &bad_wr);
+ ret = qp->device->post_send(qp->real_qp, wr, &bad_wr);
if (ret)
for (next = wr; next; next = next->next) {
++resp.bad_wr;
@@ -1691,7 +2162,7 @@ ssize_t ib_uverbs_post_recv(struct ib_uverbs_file *file,
goto out;
resp.bad_wr = 0;
- ret = qp->device->post_recv(qp, wr, &bad_wr);
+ ret = qp->device->post_recv(qp->real_qp, wr, &bad_wr);
put_qp_read(qp);
@@ -1975,107 +2446,199 @@ out_put:
return ret ? ret : in_len;
}
-ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file,
- const char __user *buf, int in_len,
- int out_len)
+int __uverbs_create_xsrq(struct ib_uverbs_file *file,
+ struct ib_uverbs_create_xsrq *cmd,
+ struct ib_udata *udata)
{
- struct ib_uverbs_create_srq cmd;
struct ib_uverbs_create_srq_resp resp;
- struct ib_udata udata;
- struct ib_uevent_object *obj;
+ struct ib_usrq_object *obj;
struct ib_pd *pd;
struct ib_srq *srq;
+ struct ib_uobject *uninitialized_var(xrcd_uobj);
struct ib_srq_init_attr attr;
int ret;
- if (out_len < sizeof resp)
- return -ENOSPC;
-
- if (copy_from_user(&cmd, buf, sizeof cmd))
- return -EFAULT;
-
- INIT_UDATA(&udata, buf + sizeof cmd,
- (unsigned long) cmd.response + sizeof resp,
- in_len - sizeof cmd, out_len - sizeof resp);
-
obj = kmalloc(sizeof *obj, GFP_KERNEL);
if (!obj)
return -ENOMEM;
- init_uobj(&obj->uobject, cmd.user_handle, file->ucontext, &srq_lock_key);
- down_write(&obj->uobject.mutex);
+ init_uobj(&obj->uevent.uobject, cmd->user_handle, file->ucontext, &srq_lock_key);
+ down_write(&obj->uevent.uobject.mutex);
- pd = idr_read_pd(cmd.pd_handle, file->ucontext);
+ pd = idr_read_pd(cmd->pd_handle, file->ucontext);
if (!pd) {
ret = -EINVAL;
goto err;
}
+ if (cmd->srq_type == IB_SRQT_XRC) {
+ attr.ext.xrc.cq = idr_read_cq(cmd->cq_handle, file->ucontext, 0);
+ if (!attr.ext.xrc.cq) {
+ ret = -EINVAL;
+ goto err_put_pd;
+ }
+
+ attr.ext.xrc.xrcd = idr_read_xrcd(cmd->xrcd_handle, file->ucontext, &xrcd_uobj);
+ if (!attr.ext.xrc.xrcd) {
+ ret = -EINVAL;
+ goto err_put_cq;
+ }
+
+ obj->uxrcd = container_of(xrcd_uobj, struct ib_uxrcd_object, uobject);
+ atomic_inc(&obj->uxrcd->refcnt);
+ }
+
attr.event_handler = ib_uverbs_srq_event_handler;
attr.srq_context = file;
- attr.attr.max_wr = cmd.max_wr;
- attr.attr.max_sge = cmd.max_sge;
- attr.attr.srq_limit = cmd.srq_limit;
+ attr.srq_type = cmd->srq_type;
+ attr.attr.max_wr = cmd->max_wr;
+ attr.attr.max_sge = cmd->max_sge;
+ attr.attr.srq_limit = cmd->srq_limit;
- obj->events_reported = 0;
- INIT_LIST_HEAD(&obj->event_list);
+ obj->uevent.events_reported = 0;
+ INIT_LIST_HEAD(&obj->uevent.event_list);
- srq = pd->device->create_srq(pd, &attr, &udata);
+ srq = pd->device->create_srq(pd, &attr, udata);
if (IS_ERR(srq)) {
ret = PTR_ERR(srq);
goto err_put;
}
- srq->device = pd->device;
- srq->pd = pd;
- srq->uobject = &obj->uobject;
+ srq->device = pd->device;
+ srq->pd = pd;
+ srq->srq_type = cmd->srq_type;
+ srq->uobject = &obj->uevent.uobject;
srq->event_handler = attr.event_handler;
srq->srq_context = attr.srq_context;
+
+ if (cmd->srq_type == IB_SRQT_XRC) {
+ srq->ext.xrc.cq = attr.ext.xrc.cq;
+ srq->ext.xrc.xrcd = attr.ext.xrc.xrcd;
+ atomic_inc(&attr.ext.xrc.cq->usecnt);
+ atomic_inc(&attr.ext.xrc.xrcd->usecnt);
+ }
+
atomic_inc(&pd->usecnt);
atomic_set(&srq->usecnt, 0);
- obj->uobject.object = srq;
- ret = idr_add_uobj(&ib_uverbs_srq_idr, &obj->uobject);
+ obj->uevent.uobject.object = srq;
+ ret = idr_add_uobj(&ib_uverbs_srq_idr, &obj->uevent.uobject);
if (ret)
goto err_destroy;
memset(&resp, 0, sizeof resp);
- resp.srq_handle = obj->uobject.id;
+ resp.srq_handle = obj->uevent.uobject.id;
resp.max_wr = attr.attr.max_wr;
resp.max_sge = attr.attr.max_sge;
+ if (cmd->srq_type == IB_SRQT_XRC)
+ resp.srqn = srq->ext.xrc.srq_num;
- if (copy_to_user((void __user *) (unsigned long) cmd.response,
+ if (copy_to_user((void __user *) (unsigned long) cmd->response,
&resp, sizeof resp)) {
ret = -EFAULT;
goto err_copy;
}
+ if (cmd->srq_type == IB_SRQT_XRC) {
+ put_uobj_read(xrcd_uobj);
+ put_cq_read(attr.ext.xrc.cq);
+ }
put_pd_read(pd);
mutex_lock(&file->mutex);
- list_add_tail(&obj->uobject.list, &file->ucontext->srq_list);
+ list_add_tail(&obj->uevent.uobject.list, &file->ucontext->srq_list);
mutex_unlock(&file->mutex);
- obj->uobject.live = 1;
+ obj->uevent.uobject.live = 1;
- up_write(&obj->uobject.mutex);
+ up_write(&obj->uevent.uobject.mutex);
- return in_len;
+ return 0;
err_copy:
- idr_remove_uobj(&ib_uverbs_srq_idr, &obj->uobject);
+ idr_remove_uobj(&ib_uverbs_srq_idr, &obj->uevent.uobject);
err_destroy:
ib_destroy_srq(srq);
err_put:
+ if (cmd->srq_type == IB_SRQT_XRC) {
+ atomic_dec(&obj->uxrcd->refcnt);
+ put_uobj_read(xrcd_uobj);
+ }
+
+err_put_cq:
+ if (cmd->srq_type == IB_SRQT_XRC)
+ put_cq_read(attr.ext.xrc.cq);
+
+err_put_pd:
put_pd_read(pd);
err:
- put_uobj_write(&obj->uobject);
+ put_uobj_write(&obj->uevent.uobject);
return ret;
}
+ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len,
+ int out_len)
+{
+ struct ib_uverbs_create_srq cmd;
+ struct ib_uverbs_create_xsrq xcmd;
+ struct ib_uverbs_create_srq_resp resp;
+ struct ib_udata udata;
+ int ret;
+
+ if (out_len < sizeof resp)
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, buf, sizeof cmd))
+ return -EFAULT;
+
+ xcmd.response = cmd.response;
+ xcmd.user_handle = cmd.user_handle;
+ xcmd.srq_type = IB_SRQT_BASIC;
+ xcmd.pd_handle = cmd.pd_handle;
+ xcmd.max_wr = cmd.max_wr;
+ xcmd.max_sge = cmd.max_sge;
+ xcmd.srq_limit = cmd.srq_limit;
+
+ INIT_UDATA(&udata, buf + sizeof cmd,
+ (unsigned long) cmd.response + sizeof resp,
+ in_len - sizeof cmd, out_len - sizeof resp);
+
+ ret = __uverbs_create_xsrq(file, &xcmd, &udata);
+ if (ret)
+ return ret;
+
+ return in_len;
+}
+
+ssize_t ib_uverbs_create_xsrq(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len, int out_len)
+{
+ struct ib_uverbs_create_xsrq cmd;
+ struct ib_uverbs_create_srq_resp resp;
+ struct ib_udata udata;
+ int ret;
+
+ if (out_len < sizeof resp)
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, buf, sizeof cmd))
+ return -EFAULT;
+
+ INIT_UDATA(&udata, buf + sizeof cmd,
+ (unsigned long) cmd.response + sizeof resp,
+ in_len - sizeof cmd, out_len - sizeof resp);
+
+ ret = __uverbs_create_xsrq(file, &cmd, &udata);
+ if (ret)
+ return ret;
+
+ return in_len;
+}
+
ssize_t ib_uverbs_modify_srq(struct ib_uverbs_file *file,
const char __user *buf, int in_len,
int out_len)
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 56898b6578a4..879636746373 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -72,6 +72,7 @@ DEFINE_IDR(ib_uverbs_ah_idr);
DEFINE_IDR(ib_uverbs_cq_idr);
DEFINE_IDR(ib_uverbs_qp_idr);
DEFINE_IDR(ib_uverbs_srq_idr);
+DEFINE_IDR(ib_uverbs_xrcd_idr);
static DEFINE_SPINLOCK(map_lock);
static DECLARE_BITMAP(dev_map, IB_UVERBS_MAX_DEVICES);
@@ -107,6 +108,10 @@ static ssize_t (*uverbs_cmd_table[])(struct ib_uverbs_file *file,
[IB_USER_VERBS_CMD_MODIFY_SRQ] = ib_uverbs_modify_srq,
[IB_USER_VERBS_CMD_QUERY_SRQ] = ib_uverbs_query_srq,
[IB_USER_VERBS_CMD_DESTROY_SRQ] = ib_uverbs_destroy_srq,
+ [IB_USER_VERBS_CMD_OPEN_XRCD] = ib_uverbs_open_xrcd,
+ [IB_USER_VERBS_CMD_CLOSE_XRCD] = ib_uverbs_close_xrcd,
+ [IB_USER_VERBS_CMD_CREATE_XSRQ] = ib_uverbs_create_xsrq,
+ [IB_USER_VERBS_CMD_OPEN_QP] = ib_uverbs_open_qp
};
static void ib_uverbs_add_one(struct ib_device *device);
@@ -202,8 +207,12 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
container_of(uobj, struct ib_uqp_object, uevent.uobject);
idr_remove_uobj(&ib_uverbs_qp_idr, uobj);
- ib_uverbs_detach_umcast(qp, uqp);
- ib_destroy_qp(qp);
+ if (qp != qp->real_qp) {
+ ib_close_qp(qp);
+ } else {
+ ib_uverbs_detach_umcast(qp, uqp);
+ ib_destroy_qp(qp);
+ }
ib_uverbs_release_uevent(file, &uqp->uevent);
kfree(uqp);
}
@@ -241,6 +250,18 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
kfree(uobj);
}
+ mutex_lock(&file->device->xrcd_tree_mutex);
+ list_for_each_entry_safe(uobj, tmp, &context->xrcd_list, list) {
+ struct ib_xrcd *xrcd = uobj->object;
+ struct ib_uxrcd_object *uxrcd =
+ container_of(uobj, struct ib_uxrcd_object, uobject);
+
+ idr_remove_uobj(&ib_uverbs_xrcd_idr, uobj);
+ ib_uverbs_dealloc_xrcd(file->device, xrcd);
+ kfree(uxrcd);
+ }
+ mutex_unlock(&file->device->xrcd_tree_mutex);
+
list_for_each_entry_safe(uobj, tmp, &context->pd_list, list) {
struct ib_pd *pd = uobj->object;
@@ -557,8 +578,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
if (hdr.in_words * 4 != count)
return -EINVAL;
- if (hdr.command < 0 ||
- hdr.command >= ARRAY_SIZE(uverbs_cmd_table) ||
+ if (hdr.command >= ARRAY_SIZE(uverbs_cmd_table) ||
!uverbs_cmd_table[hdr.command])
return -EINVAL;
@@ -741,6 +761,8 @@ static void ib_uverbs_add_one(struct ib_device *device)
kref_init(&uverbs_dev->ref);
init_completion(&uverbs_dev->comp);
+ uverbs_dev->xrcd_tree = RB_ROOT;
+ mutex_init(&uverbs_dev->xrcd_tree_mutex);
spin_lock(&map_lock);
devnum = find_first_zero_bit(dev_map, IB_UVERBS_MAX_DEVICES);
diff --git a/drivers/infiniband/core/uverbs_marshall.c b/drivers/infiniband/core/uverbs_marshall.c
index 1b1146f87124..e7bee46868d1 100644
--- a/drivers/infiniband/core/uverbs_marshall.c
+++ b/drivers/infiniband/core/uverbs_marshall.c
@@ -30,6 +30,7 @@
* SOFTWARE.
*/
+#include <linux/export.h>
#include <rdma/ib_marshall.h>
void ib_copy_ah_attr_to_user(struct ib_uverbs_ah_attr *dst,
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index af7a8b08b2e9..602b1bd723a9 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -38,7 +38,9 @@
#include <linux/errno.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/string.h>
+#include <linux/slab.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_cache.h>
@@ -77,6 +79,31 @@ enum ib_rate mult_to_ib_rate(int mult)
}
EXPORT_SYMBOL(mult_to_ib_rate);
+int ib_rate_to_mbps(enum ib_rate rate)
+{
+ switch (rate) {
+ case IB_RATE_2_5_GBPS: return 2500;
+ case IB_RATE_5_GBPS: return 5000;
+ case IB_RATE_10_GBPS: return 10000;
+ case IB_RATE_20_GBPS: return 20000;
+ case IB_RATE_30_GBPS: return 30000;
+ case IB_RATE_40_GBPS: return 40000;
+ case IB_RATE_60_GBPS: return 60000;
+ case IB_RATE_80_GBPS: return 80000;
+ case IB_RATE_120_GBPS: return 120000;
+ case IB_RATE_14_GBPS: return 14062;
+ case IB_RATE_56_GBPS: return 56250;
+ case IB_RATE_112_GBPS: return 112500;
+ case IB_RATE_168_GBPS: return 168750;
+ case IB_RATE_25_GBPS: return 25781;
+ case IB_RATE_100_GBPS: return 103125;
+ case IB_RATE_200_GBPS: return 206250;
+ case IB_RATE_300_GBPS: return 309375;
+ default: return -1;
+ }
+}
+EXPORT_SYMBOL(ib_rate_to_mbps);
+
enum rdma_transport_type
rdma_node_get_transport(enum rdma_node_type node_type)
{
@@ -250,6 +277,13 @@ struct ib_srq *ib_create_srq(struct ib_pd *pd,
srq->uobject = NULL;
srq->event_handler = srq_init_attr->event_handler;
srq->srq_context = srq_init_attr->srq_context;
+ srq->srq_type = srq_init_attr->srq_type;
+ if (srq->srq_type == IB_SRQT_XRC) {
+ srq->ext.xrc.xrcd = srq_init_attr->ext.xrc.xrcd;
+ srq->ext.xrc.cq = srq_init_attr->ext.xrc.cq;
+ atomic_inc(&srq->ext.xrc.xrcd->usecnt);
+ atomic_inc(&srq->ext.xrc.cq->usecnt);
+ }
atomic_inc(&pd->usecnt);
atomic_set(&srq->usecnt, 0);
}
@@ -279,16 +313,29 @@ EXPORT_SYMBOL(ib_query_srq);
int ib_destroy_srq(struct ib_srq *srq)
{
struct ib_pd *pd;
+ enum ib_srq_type srq_type;
+ struct ib_xrcd *uninitialized_var(xrcd);
+ struct ib_cq *uninitialized_var(cq);
int ret;
if (atomic_read(&srq->usecnt))
return -EBUSY;
pd = srq->pd;
+ srq_type = srq->srq_type;
+ if (srq_type == IB_SRQT_XRC) {
+ xrcd = srq->ext.xrc.xrcd;
+ cq = srq->ext.xrc.cq;
+ }
ret = srq->device->destroy_srq(srq);
- if (!ret)
+ if (!ret) {
atomic_dec(&pd->usecnt);
+ if (srq_type == IB_SRQT_XRC) {
+ atomic_dec(&xrcd->usecnt);
+ atomic_dec(&cq->usecnt);
+ }
+ }
return ret;
}
@@ -296,28 +343,123 @@ EXPORT_SYMBOL(ib_destroy_srq);
/* Queue pairs */
+static void __ib_shared_qp_event_handler(struct ib_event *event, void *context)
+{
+ struct ib_qp *qp = context;
+
+ list_for_each_entry(event->element.qp, &qp->open_list, open_list)
+ event->element.qp->event_handler(event, event->element.qp->qp_context);
+}
+
+static void __ib_insert_xrcd_qp(struct ib_xrcd *xrcd, struct ib_qp *qp)
+{
+ mutex_lock(&xrcd->tgt_qp_mutex);
+ list_add(&qp->xrcd_list, &xrcd->tgt_qp_list);
+ mutex_unlock(&xrcd->tgt_qp_mutex);
+}
+
+static struct ib_qp *__ib_open_qp(struct ib_qp *real_qp,
+ void (*event_handler)(struct ib_event *, void *),
+ void *qp_context)
+{
+ struct ib_qp *qp;
+ unsigned long flags;
+
+ qp = kzalloc(sizeof *qp, GFP_KERNEL);
+ if (!qp)
+ return ERR_PTR(-ENOMEM);
+
+ qp->real_qp = real_qp;
+ atomic_inc(&real_qp->usecnt);
+ qp->device = real_qp->device;
+ qp->event_handler = event_handler;
+ qp->qp_context = qp_context;
+ qp->qp_num = real_qp->qp_num;
+ qp->qp_type = real_qp->qp_type;
+
+ spin_lock_irqsave(&real_qp->device->event_handler_lock, flags);
+ list_add(&qp->open_list, &real_qp->open_list);
+ spin_unlock_irqrestore(&real_qp->device->event_handler_lock, flags);
+
+ return qp;
+}
+
+struct ib_qp *ib_open_qp(struct ib_xrcd *xrcd,
+ struct ib_qp_open_attr *qp_open_attr)
+{
+ struct ib_qp *qp, *real_qp;
+
+ if (qp_open_attr->qp_type != IB_QPT_XRC_TGT)
+ return ERR_PTR(-EINVAL);
+
+ qp = ERR_PTR(-EINVAL);
+ mutex_lock(&xrcd->tgt_qp_mutex);
+ list_for_each_entry(real_qp, &xrcd->tgt_qp_list, xrcd_list) {
+ if (real_qp->qp_num == qp_open_attr->qp_num) {
+ qp = __ib_open_qp(real_qp, qp_open_attr->event_handler,
+ qp_open_attr->qp_context);
+ break;
+ }
+ }
+ mutex_unlock(&xrcd->tgt_qp_mutex);
+ return qp;
+}
+EXPORT_SYMBOL(ib_open_qp);
+
struct ib_qp *ib_create_qp(struct ib_pd *pd,
struct ib_qp_init_attr *qp_init_attr)
{
- struct ib_qp *qp;
+ struct ib_qp *qp, *real_qp;
+ struct ib_device *device;
- qp = pd->device->create_qp(pd, qp_init_attr, NULL);
+ device = pd ? pd->device : qp_init_attr->xrcd->device;
+ qp = device->create_qp(pd, qp_init_attr, NULL);
if (!IS_ERR(qp)) {
- qp->device = pd->device;
- qp->pd = pd;
- qp->send_cq = qp_init_attr->send_cq;
- qp->recv_cq = qp_init_attr->recv_cq;
- qp->srq = qp_init_attr->srq;
- qp->uobject = NULL;
- qp->event_handler = qp_init_attr->event_handler;
- qp->qp_context = qp_init_attr->qp_context;
- qp->qp_type = qp_init_attr->qp_type;
- atomic_inc(&pd->usecnt);
- atomic_inc(&qp_init_attr->send_cq->usecnt);
- atomic_inc(&qp_init_attr->recv_cq->usecnt);
- if (qp_init_attr->srq)
- atomic_inc(&qp_init_attr->srq->usecnt);
+ qp->device = device;
+ qp->real_qp = qp;
+ qp->uobject = NULL;
+ qp->qp_type = qp_init_attr->qp_type;
+
+ if (qp_init_attr->qp_type == IB_QPT_XRC_TGT) {
+ qp->event_handler = __ib_shared_qp_event_handler;
+ qp->qp_context = qp;
+ qp->pd = NULL;
+ qp->send_cq = qp->recv_cq = NULL;
+ qp->srq = NULL;
+ qp->xrcd = qp_init_attr->xrcd;
+ atomic_inc(&qp_init_attr->xrcd->usecnt);
+ INIT_LIST_HEAD(&qp->open_list);
+ atomic_set(&qp->usecnt, 0);
+
+ real_qp = qp;
+ qp = __ib_open_qp(real_qp, qp_init_attr->event_handler,
+ qp_init_attr->qp_context);
+ if (!IS_ERR(qp))
+ __ib_insert_xrcd_qp(qp_init_attr->xrcd, real_qp);
+ else
+ real_qp->device->destroy_qp(real_qp);
+ } else {
+ qp->event_handler = qp_init_attr->event_handler;
+ qp->qp_context = qp_init_attr->qp_context;
+ if (qp_init_attr->qp_type == IB_QPT_XRC_INI) {
+ qp->recv_cq = NULL;
+ qp->srq = NULL;
+ } else {
+ qp->recv_cq = qp_init_attr->recv_cq;
+ atomic_inc(&qp_init_attr->recv_cq->usecnt);
+ qp->srq = qp_init_attr->srq;
+ if (qp->srq)
+ atomic_inc(&qp_init_attr->srq->usecnt);
+ }
+
+ qp->pd = pd;
+ qp->send_cq = qp_init_attr->send_cq;
+ qp->xrcd = NULL;
+
+ atomic_inc(&pd->usecnt);
+ atomic_inc(&qp_init_attr->send_cq->usecnt);
+ }
}
return qp;
@@ -326,8 +468,8 @@ EXPORT_SYMBOL(ib_create_qp);
static const struct {
int valid;
- enum ib_qp_attr_mask req_param[IB_QPT_RAW_ETHERTYPE + 1];
- enum ib_qp_attr_mask opt_param[IB_QPT_RAW_ETHERTYPE + 1];
+ enum ib_qp_attr_mask req_param[IB_QPT_MAX];
+ enum ib_qp_attr_mask opt_param[IB_QPT_MAX];
} qp_state_table[IB_QPS_ERR + 1][IB_QPS_ERR + 1] = {
[IB_QPS_RESET] = {
[IB_QPS_RESET] = { .valid = 1 },
@@ -343,6 +485,12 @@ static const struct {
[IB_QPT_RC] = (IB_QP_PKEY_INDEX |
IB_QP_PORT |
IB_QP_ACCESS_FLAGS),
+ [IB_QPT_XRC_INI] = (IB_QP_PKEY_INDEX |
+ IB_QP_PORT |
+ IB_QP_ACCESS_FLAGS),
+ [IB_QPT_XRC_TGT] = (IB_QP_PKEY_INDEX |
+ IB_QP_PORT |
+ IB_QP_ACCESS_FLAGS),
[IB_QPT_SMI] = (IB_QP_PKEY_INDEX |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_PKEY_INDEX |
@@ -365,6 +513,12 @@ static const struct {
[IB_QPT_RC] = (IB_QP_PKEY_INDEX |
IB_QP_PORT |
IB_QP_ACCESS_FLAGS),
+ [IB_QPT_XRC_INI] = (IB_QP_PKEY_INDEX |
+ IB_QP_PORT |
+ IB_QP_ACCESS_FLAGS),
+ [IB_QPT_XRC_TGT] = (IB_QP_PKEY_INDEX |
+ IB_QP_PORT |
+ IB_QP_ACCESS_FLAGS),
[IB_QPT_SMI] = (IB_QP_PKEY_INDEX |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_PKEY_INDEX |
@@ -384,6 +538,16 @@ static const struct {
IB_QP_RQ_PSN |
IB_QP_MAX_DEST_RD_ATOMIC |
IB_QP_MIN_RNR_TIMER),
+ [IB_QPT_XRC_INI] = (IB_QP_AV |
+ IB_QP_PATH_MTU |
+ IB_QP_DEST_QPN |
+ IB_QP_RQ_PSN),
+ [IB_QPT_XRC_TGT] = (IB_QP_AV |
+ IB_QP_PATH_MTU |
+ IB_QP_DEST_QPN |
+ IB_QP_RQ_PSN |
+ IB_QP_MAX_DEST_RD_ATOMIC |
+ IB_QP_MIN_RNR_TIMER),
},
.opt_param = {
[IB_QPT_UD] = (IB_QP_PKEY_INDEX |
@@ -394,6 +558,12 @@ static const struct {
[IB_QPT_RC] = (IB_QP_ALT_PATH |
IB_QP_ACCESS_FLAGS |
IB_QP_PKEY_INDEX),
+ [IB_QPT_XRC_INI] = (IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PKEY_INDEX),
+ [IB_QPT_XRC_TGT] = (IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PKEY_INDEX),
[IB_QPT_SMI] = (IB_QP_PKEY_INDEX |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_PKEY_INDEX |
@@ -414,6 +584,13 @@ static const struct {
IB_QP_RNR_RETRY |
IB_QP_SQ_PSN |
IB_QP_MAX_QP_RD_ATOMIC),
+ [IB_QPT_XRC_INI] = (IB_QP_TIMEOUT |
+ IB_QP_RETRY_CNT |
+ IB_QP_RNR_RETRY |
+ IB_QP_SQ_PSN |
+ IB_QP_MAX_QP_RD_ATOMIC),
+ [IB_QPT_XRC_TGT] = (IB_QP_TIMEOUT |
+ IB_QP_SQ_PSN),
[IB_QPT_SMI] = IB_QP_SQ_PSN,
[IB_QPT_GSI] = IB_QP_SQ_PSN,
},
@@ -429,6 +606,15 @@ static const struct {
IB_QP_ACCESS_FLAGS |
IB_QP_MIN_RNR_TIMER |
IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_INI] = (IB_QP_CUR_STATE |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_TGT] = (IB_QP_CUR_STATE |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_MIN_RNR_TIMER |
+ IB_QP_PATH_MIG_STATE),
[IB_QPT_SMI] = (IB_QP_CUR_STATE |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_CUR_STATE |
@@ -453,6 +639,15 @@ static const struct {
IB_QP_ALT_PATH |
IB_QP_PATH_MIG_STATE |
IB_QP_MIN_RNR_TIMER),
+ [IB_QPT_XRC_INI] = (IB_QP_CUR_STATE |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_ALT_PATH |
+ IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_TGT] = (IB_QP_CUR_STATE |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_ALT_PATH |
+ IB_QP_PATH_MIG_STATE |
+ IB_QP_MIN_RNR_TIMER),
[IB_QPT_SMI] = (IB_QP_CUR_STATE |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_CUR_STATE |
@@ -465,6 +660,8 @@ static const struct {
[IB_QPT_UD] = IB_QP_EN_SQD_ASYNC_NOTIFY,
[IB_QPT_UC] = IB_QP_EN_SQD_ASYNC_NOTIFY,
[IB_QPT_RC] = IB_QP_EN_SQD_ASYNC_NOTIFY,
+ [IB_QPT_XRC_INI] = IB_QP_EN_SQD_ASYNC_NOTIFY,
+ [IB_QPT_XRC_TGT] = IB_QP_EN_SQD_ASYNC_NOTIFY, /* ??? */
[IB_QPT_SMI] = IB_QP_EN_SQD_ASYNC_NOTIFY,
[IB_QPT_GSI] = IB_QP_EN_SQD_ASYNC_NOTIFY
}
@@ -487,6 +684,15 @@ static const struct {
IB_QP_ACCESS_FLAGS |
IB_QP_MIN_RNR_TIMER |
IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_INI] = (IB_QP_CUR_STATE |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_TGT] = (IB_QP_CUR_STATE |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_MIN_RNR_TIMER |
+ IB_QP_PATH_MIG_STATE),
[IB_QPT_SMI] = (IB_QP_CUR_STATE |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_CUR_STATE |
@@ -515,6 +721,25 @@ static const struct {
IB_QP_PKEY_INDEX |
IB_QP_MIN_RNR_TIMER |
IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_INI] = (IB_QP_PORT |
+ IB_QP_AV |
+ IB_QP_TIMEOUT |
+ IB_QP_RETRY_CNT |
+ IB_QP_RNR_RETRY |
+ IB_QP_MAX_QP_RD_ATOMIC |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PKEY_INDEX |
+ IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_TGT] = (IB_QP_PORT |
+ IB_QP_AV |
+ IB_QP_TIMEOUT |
+ IB_QP_MAX_DEST_RD_ATOMIC |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PKEY_INDEX |
+ IB_QP_MIN_RNR_TIMER |
+ IB_QP_PATH_MIG_STATE),
[IB_QPT_SMI] = (IB_QP_PKEY_INDEX |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_PKEY_INDEX |
@@ -579,7 +804,7 @@ int ib_modify_qp(struct ib_qp *qp,
struct ib_qp_attr *qp_attr,
int qp_attr_mask)
{
- return qp->device->modify_qp(qp, qp_attr, qp_attr_mask, NULL);
+ return qp->device->modify_qp(qp->real_qp, qp_attr, qp_attr_mask, NULL);
}
EXPORT_SYMBOL(ib_modify_qp);
@@ -589,11 +814,59 @@ int ib_query_qp(struct ib_qp *qp,
struct ib_qp_init_attr *qp_init_attr)
{
return qp->device->query_qp ?
- qp->device->query_qp(qp, qp_attr, qp_attr_mask, qp_init_attr) :
+ qp->device->query_qp(qp->real_qp, qp_attr, qp_attr_mask, qp_init_attr) :
-ENOSYS;
}
EXPORT_SYMBOL(ib_query_qp);
+int ib_close_qp(struct ib_qp *qp)
+{
+ struct ib_qp *real_qp;
+ unsigned long flags;
+
+ real_qp = qp->real_qp;
+ if (real_qp == qp)
+ return -EINVAL;
+
+ spin_lock_irqsave(&real_qp->device->event_handler_lock, flags);
+ list_del(&qp->open_list);
+ spin_unlock_irqrestore(&real_qp->device->event_handler_lock, flags);
+
+ atomic_dec(&real_qp->usecnt);
+ kfree(qp);
+
+ return 0;
+}
+EXPORT_SYMBOL(ib_close_qp);
+
+static int __ib_destroy_shared_qp(struct ib_qp *qp)
+{
+ struct ib_xrcd *xrcd;
+ struct ib_qp *real_qp;
+ int ret;
+
+ real_qp = qp->real_qp;
+ xrcd = real_qp->xrcd;
+
+ mutex_lock(&xrcd->tgt_qp_mutex);
+ ib_close_qp(qp);
+ if (atomic_read(&real_qp->usecnt) == 0)
+ list_del(&real_qp->xrcd_list);
+ else
+ real_qp = NULL;
+ mutex_unlock(&xrcd->tgt_qp_mutex);
+
+ if (real_qp) {
+ ret = ib_destroy_qp(real_qp);
+ if (!ret)
+ atomic_dec(&xrcd->usecnt);
+ else
+ __ib_insert_xrcd_qp(xrcd, real_qp);
+ }
+
+ return 0;
+}
+
int ib_destroy_qp(struct ib_qp *qp)
{
struct ib_pd *pd;
@@ -601,16 +874,25 @@ int ib_destroy_qp(struct ib_qp *qp)
struct ib_srq *srq;
int ret;
- pd = qp->pd;
- scq = qp->send_cq;
- rcq = qp->recv_cq;
- srq = qp->srq;
+ if (atomic_read(&qp->usecnt))
+ return -EBUSY;
+
+ if (qp->real_qp != qp)
+ return __ib_destroy_shared_qp(qp);
+
+ pd = qp->pd;
+ scq = qp->send_cq;
+ rcq = qp->recv_cq;
+ srq = qp->srq;
ret = qp->device->destroy_qp(qp);
if (!ret) {
- atomic_dec(&pd->usecnt);
- atomic_dec(&scq->usecnt);
- atomic_dec(&rcq->usecnt);
+ if (pd)
+ atomic_dec(&pd->usecnt);
+ if (scq)
+ atomic_dec(&scq->usecnt);
+ if (rcq)
+ atomic_dec(&rcq->usecnt);
if (srq)
atomic_dec(&srq->usecnt);
}
@@ -920,3 +1202,42 @@ int ib_detach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid)
return qp->device->detach_mcast(qp, gid, lid);
}
EXPORT_SYMBOL(ib_detach_mcast);
+
+struct ib_xrcd *ib_alloc_xrcd(struct ib_device *device)
+{
+ struct ib_xrcd *xrcd;
+
+ if (!device->alloc_xrcd)
+ return ERR_PTR(-ENOSYS);
+
+ xrcd = device->alloc_xrcd(device, NULL, NULL);
+ if (!IS_ERR(xrcd)) {
+ xrcd->device = device;
+ xrcd->inode = NULL;
+ atomic_set(&xrcd->usecnt, 0);
+ mutex_init(&xrcd->tgt_qp_mutex);
+ INIT_LIST_HEAD(&xrcd->tgt_qp_list);
+ }
+
+ return xrcd;
+}
+EXPORT_SYMBOL(ib_alloc_xrcd);
+
+int ib_dealloc_xrcd(struct ib_xrcd *xrcd)
+{
+ struct ib_qp *qp;
+ int ret;
+
+ if (atomic_read(&xrcd->usecnt))
+ return -EBUSY;
+
+ while (!list_empty(&xrcd->tgt_qp_list)) {
+ qp = list_entry(xrcd->tgt_qp_list.next, struct ib_qp, xrcd_list);
+ ret = ib_destroy_qp(qp);
+ if (ret)
+ return ret;
+ }
+
+ return xrcd->device->dealloc_xrcd(xrcd);
+}
+EXPORT_SYMBOL(ib_dealloc_xrcd);
diff --git a/drivers/infiniband/hw/amso1100/c2.c b/drivers/infiniband/hw/amso1100/c2.c
index 0cfc455630d0..5ce7b9e8bff6 100644
--- a/drivers/infiniband/hw/amso1100/c2.c
+++ b/drivers/infiniband/hw/amso1100/c2.c
@@ -36,6 +36,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/inetdevice.h>
+#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
@@ -799,13 +800,10 @@ static int c2_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
/* Loop thru additional data fragments and queue them */
if (skb_shinfo(skb)->nr_frags) {
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- maplen = frag->size;
- mapaddr =
- pci_map_page(c2dev->pcidev, frag->page,
- frag->page_offset, maplen,
- PCI_DMA_TODEVICE);
-
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ maplen = skb_frag_size(frag);
+ mapaddr = skb_frag_dma_map(&c2dev->pcidev->dev, frag,
+ 0, maplen, DMA_TO_DEVICE);
elem = elem->next;
elem->skb = NULL;
elem->mapaddr = mapaddr;
diff --git a/drivers/infiniband/hw/amso1100/c2_ae.c b/drivers/infiniband/hw/amso1100/c2_ae.c
index 24f9e3a90e8e..32d34e88d5cf 100644
--- a/drivers/infiniband/hw/amso1100/c2_ae.c
+++ b/drivers/infiniband/hw/amso1100/c2_ae.c
@@ -288,6 +288,11 @@ void c2_ae_event(struct c2_dev *c2dev, u32 mq_index)
cm_event.private_data_len =
be32_to_cpu(req->private_data_length);
cm_event.private_data = req->private_data;
+ /*
+ * Until ird/ord negotiation via MPAv2 support is added, send
+ * max supported values
+ */
+ cm_event.ird = cm_event.ord = 128;
if (cm_id->event_handler)
cm_id->event_handler(cm_id, &cm_event);
diff --git a/drivers/infiniband/hw/amso1100/c2_intr.c b/drivers/infiniband/hw/amso1100/c2_intr.c
index 0ebe4e806b86..8951db4ae29d 100644
--- a/drivers/infiniband/hw/amso1100/c2_intr.c
+++ b/drivers/infiniband/hw/amso1100/c2_intr.c
@@ -183,6 +183,11 @@ static void handle_vq(struct c2_dev *c2dev, u32 mq_index)
case IW_CM_EVENT_ESTABLISHED:
c2_set_qp_state(req->qp,
C2_QP_STATE_RTS);
+ /*
+ * Until ird/ord negotiation via MPAv2 support is added, send
+ * max supported values
+ */
+ cm_event.ird = cm_event.ord = 128;
case IW_CM_EVENT_CLOSE:
/*
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c
index aeebc4d37e33..12f923d64e42 100644
--- a/drivers/infiniband/hw/amso1100/c2_provider.c
+++ b/drivers/infiniband/hw/amso1100/c2_provider.c
@@ -99,14 +99,6 @@ static int c2_query_port(struct ib_device *ibdev,
return 0;
}
-static int c2_modify_port(struct ib_device *ibdev,
- u8 port, int port_modify_mask,
- struct ib_port_modify *props)
-{
- pr_debug("%s:%u\n", __func__, __LINE__);
- return 0;
-}
-
static int c2_query_pkey(struct ib_device *ibdev,
u8 port, u16 index, u16 * pkey)
{
@@ -761,10 +753,7 @@ static struct net_device *c2_pseudo_netdev_init(struct c2_dev *c2dev)
memcpy_fromio(netdev->dev_addr, c2dev->kva + C2_REGS_RDMA_ENADDR, 6);
/* Print out the MAC address */
- pr_debug("%s: MAC %02X:%02X:%02X:%02X:%02X:%02X\n",
- netdev->name,
- netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
- netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
+ pr_debug("%s: MAC %pM\n", netdev->name, netdev->dev_addr);
#if 0
/* Disable network packets */
@@ -817,7 +806,6 @@ int c2_register_device(struct c2_dev *dev)
dev->ibdev.dma_device = &dev->pcidev->dev;
dev->ibdev.query_device = c2_query_device;
dev->ibdev.query_port = c2_query_port;
- dev->ibdev.modify_port = c2_modify_port;
dev->ibdev.query_pkey = c2_query_pkey;
dev->ibdev.query_gid = c2_query_gid;
dev->ibdev.alloc_ucontext = c2_alloc_ucontext;
diff --git a/drivers/infiniband/hw/cxgb3/Makefile b/drivers/infiniband/hw/cxgb3/Makefile
index 621619c794e5..2761364185af 100644
--- a/drivers/infiniband/hw/cxgb3/Makefile
+++ b/drivers/infiniband/hw/cxgb3/Makefile
@@ -1,4 +1,4 @@
-ccflags-y := -Idrivers/net/cxgb3
+ccflags-y := -Idrivers/net/ethernet/chelsio/cxgb3
obj-$(CONFIG_INFINIBAND_CXGB3) += iw_cxgb3.o
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index 0a5008fbebac..de6d0774e609 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -287,7 +287,7 @@ void __free_ep(struct kref *kref)
if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) {
cxgb3_remove_tid(ep->com.tdev, (void *)ep, ep->hwtid);
dst_release(ep->dst);
- l2t_release(L2DATA(ep->com.tdev), ep->l2t);
+ l2t_release(ep->com.tdev, ep->l2t);
}
kfree(ep);
}
@@ -753,6 +753,11 @@ static void connect_request_upcall(struct iwch_ep *ep)
event.private_data_len = ep->plen;
event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
event.provider_data = ep;
+ /*
+ * Until ird/ord negotiation via MPAv2 support is added, send max
+ * supported values
+ */
+ event.ird = event.ord = 8;
if (state_read(&ep->parent_ep->com) != DEAD) {
get_ep(&ep->com);
ep->parent_ep->com.cm_id->event_handler(
@@ -770,6 +775,11 @@ static void established_upcall(struct iwch_ep *ep)
PDBG("%s ep %p\n", __func__, ep);
memset(&event, 0, sizeof(event));
event.event = IW_CM_EVENT_ESTABLISHED;
+ /*
+ * Until ird/ord negotiation via MPAv2 support is added, send max
+ * supported values
+ */
+ event.ird = event.ord = 8;
if (ep->com.cm_id) {
PDBG("%s ep %p tid %d\n", __func__, ep, ep->hwtid);
ep->com.cm_id->event_handler(ep->com.cm_id, &event);
@@ -1178,7 +1188,7 @@ static int act_open_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
release_tid(ep->com.tdev, GET_TID(rpl), NULL);
cxgb3_free_atid(ep->com.tdev, ep->atid);
dst_release(ep->dst);
- l2t_release(L2DATA(ep->com.tdev), ep->l2t);
+ l2t_release(ep->com.tdev, ep->l2t);
put_ep(&ep->com);
return CPL_RET_BUF_DONE;
}
@@ -1328,6 +1338,7 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
struct iwch_ep *child_ep, *parent_ep = ctx;
struct cpl_pass_accept_req *req = cplhdr(skb);
unsigned int hwtid = GET_TID(req);
+ struct neighbour *neigh;
struct dst_entry *dst;
struct l2t_entry *l2t;
struct rtable *rt;
@@ -1364,7 +1375,8 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
goto reject;
}
dst = &rt->dst;
- l2t = t3_l2t_get(tdev, dst->neighbour, dst->neighbour->dev);
+ neigh = dst_get_neighbour(dst);
+ l2t = t3_l2t_get(tdev, neigh, neigh->dev);
if (!l2t) {
printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
__func__);
@@ -1375,7 +1387,7 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
if (!child_ep) {
printk(KERN_ERR MOD "%s - failed to allocate ep entry!\n",
__func__);
- l2t_release(L2DATA(tdev), l2t);
+ l2t_release(tdev, l2t);
dst_release(dst);
goto reject;
}
@@ -1874,10 +1886,11 @@ static int is_loopback_dst(struct iw_cm_id *cm_id)
int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
{
- int err = 0;
struct iwch_dev *h = to_iwch_dev(cm_id->device);
+ struct neighbour *neigh;
struct iwch_ep *ep;
struct rtable *rt;
+ int err = 0;
if (is_loopback_dst(cm_id)) {
err = -ENOSYS;
@@ -1933,9 +1946,10 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
}
ep->dst = &rt->dst;
+ neigh = dst_get_neighbour(ep->dst);
+
/* get a l2t entry */
- ep->l2t = t3_l2t_get(ep->com.tdev, ep->dst->neighbour,
- ep->dst->neighbour->dev);
+ ep->l2t = t3_l2t_get(ep->com.tdev, neigh, neigh->dev);
if (!ep->l2t) {
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
err = -ENOMEM;
@@ -1952,7 +1966,7 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
if (!err)
goto out;
- l2t_release(L2DATA(h->rdev.t3cdev_p), ep->l2t);
+ l2t_release(h->rdev.t3cdev_p, ep->l2t);
fail4:
dst_release(ep->dst);
fail3:
@@ -2123,7 +2137,7 @@ int iwch_ep_redirect(void *ctx, struct dst_entry *old, struct dst_entry *new,
PDBG("%s ep %p redirect to dst %p l2t %p\n", __func__, ep, new,
l2t);
dst_hold(new);
- l2t_release(L2DATA(ep->com.tdev), ep->l2t);
+ l2t_release(ep->com.tdev, ep->l2t);
ep->l2t = l2t;
dst_release(old);
ep->dst = new;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_ev.c b/drivers/infiniband/hw/cxgb3/iwch_ev.c
index 71e0d845da3d..abcc9e76962b 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_ev.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_ev.c
@@ -46,6 +46,7 @@ static void post_qp_event(struct iwch_dev *rnicp, struct iwch_cq *chp,
struct ib_event event;
struct iwch_qp_attributes attrs;
struct iwch_qp *qhp;
+ unsigned long flag;
spin_lock(&rnicp->lock);
qhp = get_qhp(rnicp, CQE_QPID(rsp_msg->cqe));
@@ -94,7 +95,9 @@ static void post_qp_event(struct iwch_dev *rnicp, struct iwch_cq *chp,
if (qhp->ibqp.event_handler)
(*qhp->ibqp.event_handler)(&event, qhp->ibqp.qp_context);
+ spin_lock_irqsave(&chp->comp_handler_lock, flag);
(*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
+ spin_unlock_irqrestore(&chp->comp_handler_lock, flag);
if (atomic_dec_and_test(&qhp->refcnt))
wake_up(&qhp->wait);
@@ -107,6 +110,7 @@ void iwch_ev_dispatch(struct cxio_rdev *rdev_p, struct sk_buff *skb)
struct iwch_cq *chp;
struct iwch_qp *qhp;
u32 cqid = RSPQ_CQID(rsp_msg);
+ unsigned long flag;
rnicp = (struct iwch_dev *) rdev_p->ulp;
spin_lock(&rnicp->lock);
@@ -170,7 +174,9 @@ void iwch_ev_dispatch(struct cxio_rdev *rdev_p, struct sk_buff *skb)
*/
if (qhp->ep && SQ_TYPE(rsp_msg->cqe))
dst_confirm(qhp->ep->dst);
+ spin_lock_irqsave(&chp->comp_handler_lock, flag);
(*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
+ spin_unlock_irqrestore(&chp->comp_handler_lock, flag);
break;
case TPT_ERR_STAG:
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index 2e2741307af4..37c224fc3ad9 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -61,13 +61,6 @@
#include "iwch_user.h"
#include "common.h"
-static int iwch_modify_port(struct ib_device *ibdev,
- u8 port, int port_modify_mask,
- struct ib_port_modify *props)
-{
- return -ENOSYS;
-}
-
static struct ib_ah *iwch_ah_create(struct ib_pd *pd,
struct ib_ah_attr *ah_attr)
{
@@ -197,6 +190,7 @@ static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int ve
chp->rhp = rhp;
chp->ibcq.cqe = 1 << chp->cq.size_log2;
spin_lock_init(&chp->lock);
+ spin_lock_init(&chp->comp_handler_lock);
atomic_set(&chp->refcnt, 1);
init_waitqueue_head(&chp->wait);
if (insert_handle(rhp, &rhp->cqidr, chp, chp->cq.cqid)) {
@@ -1392,7 +1386,6 @@ int iwch_register_device(struct iwch_dev *dev)
dev->ibdev.dma_device = &(dev->rdev.rnic_info.pdev->dev);
dev->ibdev.query_device = iwch_query_device;
dev->ibdev.query_port = iwch_query_port;
- dev->ibdev.modify_port = iwch_modify_port;
dev->ibdev.query_pkey = iwch_query_pkey;
dev->ibdev.query_gid = iwch_query_gid;
dev->ibdev.alloc_ucontext = iwch_alloc_ucontext;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.h b/drivers/infiniband/hw/cxgb3/iwch_provider.h
index 9a342c9b220d..87c14b0c5ac0 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.h
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.h
@@ -103,6 +103,7 @@ struct iwch_cq {
struct iwch_dev *rhp;
struct t3_cq cq;
spinlock_t lock;
+ spinlock_t comp_handler_lock;
atomic_t refcnt;
wait_queue_head_t wait;
u32 __user *user_rptr_addr;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c
index ecd313f359a4..bea5839d89ee 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_qp.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c
@@ -822,8 +822,11 @@ static void __flush_qp(struct iwch_qp *qhp, struct iwch_cq *rchp,
flushed = cxio_flush_rq(&qhp->wq, &rchp->cq, count);
spin_unlock(&qhp->lock);
spin_unlock_irqrestore(&rchp->lock, *flag);
- if (flushed)
+ if (flushed) {
+ spin_lock_irqsave(&rchp->comp_handler_lock, *flag);
(*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+ spin_unlock_irqrestore(&rchp->comp_handler_lock, *flag);
+ }
/* locking hierarchy: cq lock first, then qp lock. */
spin_lock_irqsave(&schp->lock, *flag);
@@ -833,8 +836,11 @@ static void __flush_qp(struct iwch_qp *qhp, struct iwch_cq *rchp,
flushed = cxio_flush_sq(&qhp->wq, &schp->cq, count);
spin_unlock(&qhp->lock);
spin_unlock_irqrestore(&schp->lock, *flag);
- if (flushed)
+ if (flushed) {
+ spin_lock_irqsave(&schp->comp_handler_lock, *flag);
(*schp->ibcq.comp_handler)(&schp->ibcq, schp->ibcq.cq_context);
+ spin_unlock_irqrestore(&schp->comp_handler_lock, *flag);
+ }
/* deref */
if (atomic_dec_and_test(&qhp->refcnt))
@@ -853,11 +859,15 @@ static void flush_qp(struct iwch_qp *qhp, unsigned long *flag)
if (qhp->ibqp.uobject) {
cxio_set_wq_in_error(&qhp->wq);
cxio_set_cq_in_error(&rchp->cq);
+ spin_lock_irqsave(&rchp->comp_handler_lock, *flag);
(*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+ spin_unlock_irqrestore(&rchp->comp_handler_lock, *flag);
if (schp != rchp) {
cxio_set_cq_in_error(&schp->cq);
+ spin_lock_irqsave(&schp->comp_handler_lock, *flag);
(*schp->ibcq.comp_handler)(&schp->ibcq,
schp->ibcq.cq_context);
+ spin_unlock_irqrestore(&schp->comp_handler_lock, *flag);
}
return;
}
diff --git a/drivers/infiniband/hw/cxgb4/Makefile b/drivers/infiniband/hw/cxgb4/Makefile
index cd20b1342aec..46b878ca2c3b 100644
--- a/drivers/infiniband/hw/cxgb4/Makefile
+++ b/drivers/infiniband/hw/cxgb4/Makefile
@@ -1,4 +1,4 @@
-ccflags-y := -Idrivers/net/cxgb4
+ccflags-y := -Idrivers/net/ethernet/chelsio/cxgb4
obj-$(CONFIG_INFINIBAND_CXGB4) += iw_cxgb4.o
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 31fb44085c9b..b36cdac9c558 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -103,7 +103,8 @@ MODULE_PARM_DESC(ep_timeout_secs, "CM Endpoint operation timeout "
static int mpa_rev = 1;
module_param(mpa_rev, int, 0644);
MODULE_PARM_DESC(mpa_rev, "MPA Revision, 0 supports amso1100, "
- "1 is spec compliant. (default=1)");
+ "1 is RFC0544 spec compliant, 2 is IETF MPA Peer Connect Draft"
+ " compliant (default=1)");
static int markers_enabled;
module_param(markers_enabled, int, 0644);
@@ -497,17 +498,21 @@ static int send_connect(struct c4iw_ep *ep)
return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t);
}
-static void send_mpa_req(struct c4iw_ep *ep, struct sk_buff *skb)
+static void send_mpa_req(struct c4iw_ep *ep, struct sk_buff *skb,
+ u8 mpa_rev_to_use)
{
int mpalen, wrlen;
struct fw_ofld_tx_data_wr *req;
struct mpa_message *mpa;
+ struct mpa_v2_conn_params mpa_v2_params;
PDBG("%s ep %p tid %u pd_len %d\n", __func__, ep, ep->hwtid, ep->plen);
BUG_ON(skb_cloned(skb));
mpalen = sizeof(*mpa) + ep->plen;
+ if (mpa_rev_to_use == 2)
+ mpalen += sizeof(struct mpa_v2_conn_params);
wrlen = roundup(mpalen + sizeof *req, 16);
skb = get_skb(skb, wrlen, GFP_KERNEL);
if (!skb) {
@@ -533,12 +538,39 @@ static void send_mpa_req(struct c4iw_ep *ep, struct sk_buff *skb)
mpa = (struct mpa_message *)(req + 1);
memcpy(mpa->key, MPA_KEY_REQ, sizeof(mpa->key));
mpa->flags = (crc_enabled ? MPA_CRC : 0) |
- (markers_enabled ? MPA_MARKERS : 0);
+ (markers_enabled ? MPA_MARKERS : 0) |
+ (mpa_rev_to_use == 2 ? MPA_ENHANCED_RDMA_CONN : 0);
mpa->private_data_size = htons(ep->plen);
- mpa->revision = mpa_rev;
+ mpa->revision = mpa_rev_to_use;
+ if (mpa_rev_to_use == 1)
+ ep->tried_with_mpa_v1 = 1;
+
+ if (mpa_rev_to_use == 2) {
+ mpa->private_data_size +=
+ htons(sizeof(struct mpa_v2_conn_params));
+ mpa_v2_params.ird = htons((u16)ep->ird);
+ mpa_v2_params.ord = htons((u16)ep->ord);
+
+ if (peer2peer) {
+ mpa_v2_params.ird |= htons(MPA_V2_PEER2PEER_MODEL);
+ if (p2p_type == FW_RI_INIT_P2PTYPE_RDMA_WRITE)
+ mpa_v2_params.ord |=
+ htons(MPA_V2_RDMA_WRITE_RTR);
+ else if (p2p_type == FW_RI_INIT_P2PTYPE_READ_REQ)
+ mpa_v2_params.ord |=
+ htons(MPA_V2_RDMA_READ_RTR);
+ }
+ memcpy(mpa->private_data, &mpa_v2_params,
+ sizeof(struct mpa_v2_conn_params));
- if (ep->plen)
- memcpy(mpa->private_data, ep->mpa_pkt + sizeof(*mpa), ep->plen);
+ if (ep->plen)
+ memcpy(mpa->private_data +
+ sizeof(struct mpa_v2_conn_params),
+ ep->mpa_pkt + sizeof(*mpa), ep->plen);
+ } else
+ if (ep->plen)
+ memcpy(mpa->private_data,
+ ep->mpa_pkt + sizeof(*mpa), ep->plen);
/*
* Reference the mpa skb. This ensures the data area
@@ -562,10 +594,13 @@ static int send_mpa_reject(struct c4iw_ep *ep, const void *pdata, u8 plen)
struct fw_ofld_tx_data_wr *req;
struct mpa_message *mpa;
struct sk_buff *skb;
+ struct mpa_v2_conn_params mpa_v2_params;
PDBG("%s ep %p tid %u pd_len %d\n", __func__, ep, ep->hwtid, ep->plen);
mpalen = sizeof(*mpa) + plen;
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn)
+ mpalen += sizeof(struct mpa_v2_conn_params);
wrlen = roundup(mpalen + sizeof *req, 16);
skb = get_skb(NULL, wrlen, GFP_KERNEL);
@@ -595,8 +630,29 @@ static int send_mpa_reject(struct c4iw_ep *ep, const void *pdata, u8 plen)
mpa->flags = MPA_REJECT;
mpa->revision = mpa_rev;
mpa->private_data_size = htons(plen);
- if (plen)
- memcpy(mpa->private_data, pdata, plen);
+
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn) {
+ mpa->flags |= MPA_ENHANCED_RDMA_CONN;
+ mpa->private_data_size +=
+ htons(sizeof(struct mpa_v2_conn_params));
+ mpa_v2_params.ird = htons(((u16)ep->ird) |
+ (peer2peer ? MPA_V2_PEER2PEER_MODEL :
+ 0));
+ mpa_v2_params.ord = htons(((u16)ep->ord) | (peer2peer ?
+ (p2p_type ==
+ FW_RI_INIT_P2PTYPE_RDMA_WRITE ?
+ MPA_V2_RDMA_WRITE_RTR : p2p_type ==
+ FW_RI_INIT_P2PTYPE_READ_REQ ?
+ MPA_V2_RDMA_READ_RTR : 0) : 0));
+ memcpy(mpa->private_data, &mpa_v2_params,
+ sizeof(struct mpa_v2_conn_params));
+
+ if (ep->plen)
+ memcpy(mpa->private_data +
+ sizeof(struct mpa_v2_conn_params), pdata, plen);
+ } else
+ if (plen)
+ memcpy(mpa->private_data, pdata, plen);
/*
* Reference the mpa skb again. This ensures the data area
@@ -617,10 +673,13 @@ static int send_mpa_reply(struct c4iw_ep *ep, const void *pdata, u8 plen)
struct fw_ofld_tx_data_wr *req;
struct mpa_message *mpa;
struct sk_buff *skb;
+ struct mpa_v2_conn_params mpa_v2_params;
PDBG("%s ep %p tid %u pd_len %d\n", __func__, ep, ep->hwtid, ep->plen);
mpalen = sizeof(*mpa) + plen;
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn)
+ mpalen += sizeof(struct mpa_v2_conn_params);
wrlen = roundup(mpalen + sizeof *req, 16);
skb = get_skb(NULL, wrlen, GFP_KERNEL);
@@ -649,10 +708,36 @@ static int send_mpa_reply(struct c4iw_ep *ep, const void *pdata, u8 plen)
memcpy(mpa->key, MPA_KEY_REP, sizeof(mpa->key));
mpa->flags = (ep->mpa_attr.crc_enabled ? MPA_CRC : 0) |
(markers_enabled ? MPA_MARKERS : 0);
- mpa->revision = mpa_rev;
+ mpa->revision = ep->mpa_attr.version;
mpa->private_data_size = htons(plen);
- if (plen)
- memcpy(mpa->private_data, pdata, plen);
+
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn) {
+ mpa->flags |= MPA_ENHANCED_RDMA_CONN;
+ mpa->private_data_size +=
+ htons(sizeof(struct mpa_v2_conn_params));
+ mpa_v2_params.ird = htons((u16)ep->ird);
+ mpa_v2_params.ord = htons((u16)ep->ord);
+ if (peer2peer && (ep->mpa_attr.p2p_type !=
+ FW_RI_INIT_P2PTYPE_DISABLED)) {
+ mpa_v2_params.ird |= htons(MPA_V2_PEER2PEER_MODEL);
+
+ if (p2p_type == FW_RI_INIT_P2PTYPE_RDMA_WRITE)
+ mpa_v2_params.ord |=
+ htons(MPA_V2_RDMA_WRITE_RTR);
+ else if (p2p_type == FW_RI_INIT_P2PTYPE_READ_REQ)
+ mpa_v2_params.ord |=
+ htons(MPA_V2_RDMA_READ_RTR);
+ }
+
+ memcpy(mpa->private_data, &mpa_v2_params,
+ sizeof(struct mpa_v2_conn_params));
+
+ if (ep->plen)
+ memcpy(mpa->private_data +
+ sizeof(struct mpa_v2_conn_params), pdata, plen);
+ } else
+ if (plen)
+ memcpy(mpa->private_data, pdata, plen);
/*
* Reference the mpa skb. This ensures the data area
@@ -695,7 +780,10 @@ static int act_establish(struct c4iw_dev *dev, struct sk_buff *skb)
/* start MPA negotiation */
send_flowc(ep, NULL);
- send_mpa_req(ep, skb);
+ if (ep->retry_with_mpa_v1)
+ send_mpa_req(ep, skb, 1);
+ else
+ send_mpa_req(ep, skb, mpa_rev);
return 0;
}
@@ -769,8 +857,19 @@ static void connect_reply_upcall(struct c4iw_ep *ep, int status)
event.remote_addr = ep->com.remote_addr;
if ((status == 0) || (status == -ECONNREFUSED)) {
- event.private_data_len = ep->plen;
- event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
+ if (!ep->tried_with_mpa_v1) {
+ /* this means MPA_v2 is used */
+ event.private_data_len = ep->plen -
+ sizeof(struct mpa_v2_conn_params);
+ event.private_data = ep->mpa_pkt +
+ sizeof(struct mpa_message) +
+ sizeof(struct mpa_v2_conn_params);
+ } else {
+ /* this means MPA_v1 is used */
+ event.private_data_len = ep->plen;
+ event.private_data = ep->mpa_pkt +
+ sizeof(struct mpa_message);
+ }
}
PDBG("%s ep %p tid %u status %d\n", __func__, ep,
@@ -793,9 +892,22 @@ static void connect_request_upcall(struct c4iw_ep *ep)
event.event = IW_CM_EVENT_CONNECT_REQUEST;
event.local_addr = ep->com.local_addr;
event.remote_addr = ep->com.remote_addr;
- event.private_data_len = ep->plen;
- event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
event.provider_data = ep;
+ if (!ep->tried_with_mpa_v1) {
+ /* this means MPA_v2 is used */
+ event.ord = ep->ord;
+ event.ird = ep->ird;
+ event.private_data_len = ep->plen -
+ sizeof(struct mpa_v2_conn_params);
+ event.private_data = ep->mpa_pkt + sizeof(struct mpa_message) +
+ sizeof(struct mpa_v2_conn_params);
+ } else {
+ /* this means MPA_v1 is used. Send max supported */
+ event.ord = c4iw_max_read_depth;
+ event.ird = c4iw_max_read_depth;
+ event.private_data_len = ep->plen;
+ event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
+ }
if (state_read(&ep->parent_ep->com) != DEAD) {
c4iw_get_ep(&ep->com);
ep->parent_ep->com.cm_id->event_handler(
@@ -813,6 +925,8 @@ static void established_upcall(struct c4iw_ep *ep)
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
memset(&event, 0, sizeof(event));
event.event = IW_CM_EVENT_ESTABLISHED;
+ event.ird = ep->ird;
+ event.ord = ep->ord;
if (ep->com.cm_id) {
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
ep->com.cm_id->event_handler(ep->com.cm_id, &event);
@@ -848,7 +962,10 @@ static int update_rx_credits(struct c4iw_ep *ep, u32 credits)
static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
{
struct mpa_message *mpa;
+ struct mpa_v2_conn_params *mpa_v2_params;
u16 plen;
+ u16 resp_ird, resp_ord;
+ u8 rtr_mismatch = 0, insuff_ird = 0;
struct c4iw_qp_attributes attrs;
enum c4iw_qp_attr_mask mask;
int err;
@@ -888,7 +1005,9 @@ static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
mpa = (struct mpa_message *) ep->mpa_pkt;
/* Validate MPA header. */
- if (mpa->revision != mpa_rev) {
+ if (mpa->revision > mpa_rev) {
+ printk(KERN_ERR MOD "%s MPA version mismatch. Local = %d,"
+ " Received = %d\n", __func__, mpa_rev, mpa->revision);
err = -EPROTO;
goto err;
}
@@ -938,13 +1057,66 @@ static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
ep->mpa_attr.crc_enabled = (mpa->flags & MPA_CRC) | crc_enabled ? 1 : 0;
ep->mpa_attr.recv_marker_enabled = markers_enabled;
ep->mpa_attr.xmit_marker_enabled = mpa->flags & MPA_MARKERS ? 1 : 0;
- ep->mpa_attr.version = mpa_rev;
- ep->mpa_attr.p2p_type = peer2peer ? p2p_type :
- FW_RI_INIT_P2PTYPE_DISABLED;
+ ep->mpa_attr.version = mpa->revision;
+ ep->mpa_attr.p2p_type = FW_RI_INIT_P2PTYPE_DISABLED;
+
+ if (mpa->revision == 2) {
+ ep->mpa_attr.enhanced_rdma_conn =
+ mpa->flags & MPA_ENHANCED_RDMA_CONN ? 1 : 0;
+ if (ep->mpa_attr.enhanced_rdma_conn) {
+ mpa_v2_params = (struct mpa_v2_conn_params *)
+ (ep->mpa_pkt + sizeof(*mpa));
+ resp_ird = ntohs(mpa_v2_params->ird) &
+ MPA_V2_IRD_ORD_MASK;
+ resp_ord = ntohs(mpa_v2_params->ord) &
+ MPA_V2_IRD_ORD_MASK;
+
+ /*
+ * This is a double-check. Ideally, below checks are
+ * not required since ird/ord stuff has been taken
+ * care of in c4iw_accept_cr
+ */
+ if ((ep->ird < resp_ord) || (ep->ord > resp_ird)) {
+ err = -ENOMEM;
+ ep->ird = resp_ord;
+ ep->ord = resp_ird;
+ insuff_ird = 1;
+ }
+
+ if (ntohs(mpa_v2_params->ird) &
+ MPA_V2_PEER2PEER_MODEL) {
+ if (ntohs(mpa_v2_params->ord) &
+ MPA_V2_RDMA_WRITE_RTR)
+ ep->mpa_attr.p2p_type =
+ FW_RI_INIT_P2PTYPE_RDMA_WRITE;
+ else if (ntohs(mpa_v2_params->ord) &
+ MPA_V2_RDMA_READ_RTR)
+ ep->mpa_attr.p2p_type =
+ FW_RI_INIT_P2PTYPE_READ_REQ;
+ }
+ }
+ } else if (mpa->revision == 1)
+ if (peer2peer)
+ ep->mpa_attr.p2p_type = p2p_type;
+
PDBG("%s - crc_enabled=%d, recv_marker_enabled=%d, "
- "xmit_marker_enabled=%d, version=%d\n", __func__,
- ep->mpa_attr.crc_enabled, ep->mpa_attr.recv_marker_enabled,
- ep->mpa_attr.xmit_marker_enabled, ep->mpa_attr.version);
+ "xmit_marker_enabled=%d, version=%d p2p_type=%d local-p2p_type = "
+ "%d\n", __func__, ep->mpa_attr.crc_enabled,
+ ep->mpa_attr.recv_marker_enabled,
+ ep->mpa_attr.xmit_marker_enabled, ep->mpa_attr.version,
+ ep->mpa_attr.p2p_type, p2p_type);
+
+ /*
+ * If responder's RTR does not match with that of initiator, assign
+ * FW_RI_INIT_P2PTYPE_DISABLED in mpa attributes so that RTR is not
+ * generated when moving QP to RTS state.
+ * A TERM message will be sent after QP has moved to RTS state
+ */
+ if ((ep->mpa_attr.version == 2) &&
+ (ep->mpa_attr.p2p_type != p2p_type)) {
+ ep->mpa_attr.p2p_type = FW_RI_INIT_P2PTYPE_DISABLED;
+ rtr_mismatch = 1;
+ }
attrs.mpa_attr = ep->mpa_attr;
attrs.max_ird = ep->ird;
@@ -961,6 +1133,39 @@ static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
ep->com.qp, mask, &attrs, 1);
if (err)
goto err;
+
+ /*
+ * If responder's RTR requirement did not match with what initiator
+ * supports, generate TERM message
+ */
+ if (rtr_mismatch) {
+ printk(KERN_ERR "%s: RTR mismatch, sending TERM\n", __func__);
+ attrs.layer_etype = LAYER_MPA | DDP_LLP;
+ attrs.ecode = MPA_NOMATCH_RTR;
+ attrs.next_state = C4IW_QP_STATE_TERMINATE;
+ err = c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
+ C4IW_QP_ATTR_NEXT_STATE, &attrs, 0);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Generate TERM if initiator IRD is not sufficient for responder
+ * provided ORD. Currently, we do the same behaviour even when
+ * responder provided IRD is also not sufficient as regards to
+ * initiator ORD.
+ */
+ if (insuff_ird) {
+ printk(KERN_ERR "%s: Insufficient IRD, sending TERM\n",
+ __func__);
+ attrs.layer_etype = LAYER_MPA | DDP_LLP;
+ attrs.ecode = MPA_INSUFF_IRD;
+ attrs.next_state = C4IW_QP_STATE_TERMINATE;
+ err = c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
+ C4IW_QP_ATTR_NEXT_STATE, &attrs, 0);
+ err = -ENOMEM;
+ goto out;
+ }
goto out;
err:
state_set(&ep->com, ABORTING);
@@ -973,6 +1178,7 @@ out:
static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
{
struct mpa_message *mpa;
+ struct mpa_v2_conn_params *mpa_v2_params;
u16 plen;
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
@@ -1013,7 +1219,9 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
/*
* Validate MPA Header.
*/
- if (mpa->revision != mpa_rev) {
+ if (mpa->revision > mpa_rev) {
+ printk(KERN_ERR MOD "%s MPA version mismatch. Local = %d,"
+ " Received = %d\n", __func__, mpa_rev, mpa->revision);
abort_connection(ep, skb, GFP_KERNEL);
return;
}
@@ -1056,9 +1264,37 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
ep->mpa_attr.crc_enabled = (mpa->flags & MPA_CRC) | crc_enabled ? 1 : 0;
ep->mpa_attr.recv_marker_enabled = markers_enabled;
ep->mpa_attr.xmit_marker_enabled = mpa->flags & MPA_MARKERS ? 1 : 0;
- ep->mpa_attr.version = mpa_rev;
- ep->mpa_attr.p2p_type = peer2peer ? p2p_type :
- FW_RI_INIT_P2PTYPE_DISABLED;
+ ep->mpa_attr.version = mpa->revision;
+ if (mpa->revision == 1)
+ ep->tried_with_mpa_v1 = 1;
+ ep->mpa_attr.p2p_type = FW_RI_INIT_P2PTYPE_DISABLED;
+
+ if (mpa->revision == 2) {
+ ep->mpa_attr.enhanced_rdma_conn =
+ mpa->flags & MPA_ENHANCED_RDMA_CONN ? 1 : 0;
+ if (ep->mpa_attr.enhanced_rdma_conn) {
+ mpa_v2_params = (struct mpa_v2_conn_params *)
+ (ep->mpa_pkt + sizeof(*mpa));
+ ep->ird = ntohs(mpa_v2_params->ird) &
+ MPA_V2_IRD_ORD_MASK;
+ ep->ord = ntohs(mpa_v2_params->ord) &
+ MPA_V2_IRD_ORD_MASK;
+ if (ntohs(mpa_v2_params->ird) & MPA_V2_PEER2PEER_MODEL)
+ if (peer2peer) {
+ if (ntohs(mpa_v2_params->ord) &
+ MPA_V2_RDMA_WRITE_RTR)
+ ep->mpa_attr.p2p_type =
+ FW_RI_INIT_P2PTYPE_RDMA_WRITE;
+ else if (ntohs(mpa_v2_params->ord) &
+ MPA_V2_RDMA_READ_RTR)
+ ep->mpa_attr.p2p_type =
+ FW_RI_INIT_P2PTYPE_READ_REQ;
+ }
+ }
+ } else if (mpa->revision == 1)
+ if (peer2peer)
+ ep->mpa_attr.p2p_type = p2p_type;
+
PDBG("%s - crc_enabled=%d, recv_marker_enabled=%d, "
"xmit_marker_enabled=%d, version=%d p2p_type=%d\n", __func__,
ep->mpa_attr.crc_enabled, ep->mpa_attr.recv_marker_enabled,
@@ -1325,6 +1561,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
unsigned int stid = GET_POPEN_TID(ntohl(req->tos_stid));
struct tid_info *t = dev->rdev.lldi.tids;
unsigned int hwtid = GET_TID(req);
+ struct neighbour *neigh;
struct dst_entry *dst;
struct l2t_entry *l2t;
struct rtable *rt;
@@ -1357,11 +1594,11 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
goto reject;
}
dst = &rt->dst;
- if (dst->neighbour->dev->flags & IFF_LOOPBACK) {
+ neigh = dst_get_neighbour(dst);
+ if (neigh->dev->flags & IFF_LOOPBACK) {
pdev = ip_dev_find(&init_net, peer_ip);
BUG_ON(!pdev);
- l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, dst->neighbour,
- pdev, 0);
+ l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, neigh, pdev, 0);
mtu = pdev->mtu;
tx_chan = cxgb4_port_chan(pdev);
smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1;
@@ -1372,17 +1609,16 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
rss_qid = dev->rdev.lldi.rxq_ids[cxgb4_port_idx(pdev) * step];
dev_put(pdev);
} else {
- l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, dst->neighbour,
- dst->neighbour->dev, 0);
+ l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, neigh, neigh->dev, 0);
mtu = dst_mtu(dst);
- tx_chan = cxgb4_port_chan(dst->neighbour->dev);
- smac_idx = (cxgb4_port_viid(dst->neighbour->dev) & 0x7F) << 1;
+ tx_chan = cxgb4_port_chan(neigh->dev);
+ smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1;
step = dev->rdev.lldi.ntxq / dev->rdev.lldi.nchan;
- txq_idx = cxgb4_port_idx(dst->neighbour->dev) * step;
- ctrlq_idx = cxgb4_port_idx(dst->neighbour->dev);
+ txq_idx = cxgb4_port_idx(neigh->dev) * step;
+ ctrlq_idx = cxgb4_port_idx(neigh->dev);
step = dev->rdev.lldi.nrxq / dev->rdev.lldi.nchan;
rss_qid = dev->rdev.lldi.rxq_ids[
- cxgb4_port_idx(dst->neighbour->dev) * step];
+ cxgb4_port_idx(neigh->dev) * step];
}
if (!l2t) {
printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
@@ -1550,6 +1786,112 @@ static int is_neg_adv_abort(unsigned int status)
status == CPL_ERR_PERSIST_NEG_ADVICE;
}
+static int c4iw_reconnect(struct c4iw_ep *ep)
+{
+ int err = 0;
+ struct rtable *rt;
+ struct net_device *pdev;
+ struct neighbour *neigh;
+ int step;
+
+ PDBG("%s qp %p cm_id %p\n", __func__, ep->com.qp, ep->com.cm_id);
+ init_timer(&ep->timer);
+
+ /*
+ * Allocate an active TID to initiate a TCP connection.
+ */
+ ep->atid = cxgb4_alloc_atid(ep->com.dev->rdev.lldi.tids, ep);
+ if (ep->atid == -1) {
+ printk(KERN_ERR MOD "%s - cannot alloc atid.\n", __func__);
+ err = -ENOMEM;
+ goto fail2;
+ }
+
+ /* find a route */
+ rt = find_route(ep->com.dev,
+ ep->com.cm_id->local_addr.sin_addr.s_addr,
+ ep->com.cm_id->remote_addr.sin_addr.s_addr,
+ ep->com.cm_id->local_addr.sin_port,
+ ep->com.cm_id->remote_addr.sin_port, 0);
+ if (!rt) {
+ printk(KERN_ERR MOD "%s - cannot find route.\n", __func__);
+ err = -EHOSTUNREACH;
+ goto fail3;
+ }
+ ep->dst = &rt->dst;
+
+ neigh = dst_get_neighbour(ep->dst);
+
+ /* get a l2t entry */
+ if (neigh->dev->flags & IFF_LOOPBACK) {
+ PDBG("%s LOOPBACK\n", __func__);
+ pdev = ip_dev_find(&init_net,
+ ep->com.cm_id->remote_addr.sin_addr.s_addr);
+ ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
+ neigh, pdev, 0);
+ ep->mtu = pdev->mtu;
+ ep->tx_chan = cxgb4_port_chan(pdev);
+ ep->smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1;
+ step = ep->com.dev->rdev.lldi.ntxq /
+ ep->com.dev->rdev.lldi.nchan;
+ ep->txq_idx = cxgb4_port_idx(pdev) * step;
+ step = ep->com.dev->rdev.lldi.nrxq /
+ ep->com.dev->rdev.lldi.nchan;
+ ep->ctrlq_idx = cxgb4_port_idx(pdev);
+ ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
+ cxgb4_port_idx(pdev) * step];
+ dev_put(pdev);
+ } else {
+ ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
+ neigh, neigh->dev, 0);
+ ep->mtu = dst_mtu(ep->dst);
+ ep->tx_chan = cxgb4_port_chan(neigh->dev);
+ ep->smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1;
+ step = ep->com.dev->rdev.lldi.ntxq /
+ ep->com.dev->rdev.lldi.nchan;
+ ep->txq_idx = cxgb4_port_idx(neigh->dev) * step;
+ ep->ctrlq_idx = cxgb4_port_idx(neigh->dev);
+ step = ep->com.dev->rdev.lldi.nrxq /
+ ep->com.dev->rdev.lldi.nchan;
+ ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
+ cxgb4_port_idx(neigh->dev) * step];
+ }
+ if (!ep->l2t) {
+ printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
+ err = -ENOMEM;
+ goto fail4;
+ }
+
+ PDBG("%s txq_idx %u tx_chan %u smac_idx %u rss_qid %u l2t_idx %u\n",
+ __func__, ep->txq_idx, ep->tx_chan, ep->smac_idx, ep->rss_qid,
+ ep->l2t->idx);
+
+ state_set(&ep->com, CONNECTING);
+ ep->tos = 0;
+
+ /* send connect request to rnic */
+ err = send_connect(ep);
+ if (!err)
+ goto out;
+
+ cxgb4_l2t_release(ep->l2t);
+fail4:
+ dst_release(ep->dst);
+fail3:
+ cxgb4_free_atid(ep->com.dev->rdev.lldi.tids, ep->atid);
+fail2:
+ /*
+ * remember to send notification to upper layer.
+ * We are in here so the upper layer is not aware that this is
+ * re-connect attempt and so, upper layer is still waiting for
+ * response of 1st connect request.
+ */
+ connect_reply_upcall(ep, -ECONNRESET);
+ c4iw_put_ep(&ep->com);
+out:
+ return err;
+}
+
static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
{
struct cpl_abort_req_rss *req = cplhdr(skb);
@@ -1573,8 +1915,11 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
/*
* Wake up any threads in rdma_init() or rdma_fini().
+ * However, this is not needed if com state is just
+ * MPA_REQ_SENT
*/
- c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
+ if (ep->com.state != MPA_REQ_SENT)
+ c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
mutex_lock(&ep->com.mutex);
switch (ep->com.state) {
@@ -1585,7 +1930,21 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
break;
case MPA_REQ_SENT:
stop_ep_timer(ep);
- connect_reply_upcall(ep, -ECONNRESET);
+ if (mpa_rev == 2 && ep->tried_with_mpa_v1)
+ connect_reply_upcall(ep, -ECONNRESET);
+ else {
+ /*
+ * we just don't send notification upwards because we
+ * want to retry with mpa_v1 without upper layers even
+ * knowing it.
+ *
+ * do some housekeeping so as to re-initiate the
+ * connection
+ */
+ PDBG("%s: mpa_rev=%d. Retrying with mpav1\n", __func__,
+ mpa_rev);
+ ep->retry_with_mpa_v1 = 1;
+ }
break;
case MPA_REP_SENT:
break;
@@ -1621,7 +1980,9 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
dst_confirm(ep->dst);
if (ep->com.state != ABORTING) {
__state_set(&ep->com, DEAD);
- release = 1;
+ /* we don't release if we want to retry with mpa_v1 */
+ if (!ep->retry_with_mpa_v1)
+ release = 1;
}
mutex_unlock(&ep->com.mutex);
@@ -1641,6 +2002,15 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
out:
if (release)
release_ep_resources(ep);
+
+ /* retry with mpa-v1 */
+ if (ep && ep->retry_with_mpa_v1) {
+ cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid);
+ dst_release(ep->dst);
+ cxgb4_l2t_release(ep->l2t);
+ c4iw_reconnect(ep);
+ }
+
return 0;
}
@@ -1792,18 +2162,40 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
goto err;
}
- cm_id->add_ref(cm_id);
- ep->com.cm_id = cm_id;
- ep->com.qp = qp;
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn) {
+ if (conn_param->ord > ep->ird) {
+ ep->ird = conn_param->ird;
+ ep->ord = conn_param->ord;
+ send_mpa_reject(ep, conn_param->private_data,
+ conn_param->private_data_len);
+ abort_connection(ep, NULL, GFP_KERNEL);
+ err = -ENOMEM;
+ goto err;
+ }
+ if (conn_param->ird > ep->ord) {
+ if (!ep->ord)
+ conn_param->ird = 1;
+ else {
+ abort_connection(ep, NULL, GFP_KERNEL);
+ err = -ENOMEM;
+ goto err;
+ }
+ }
+ }
ep->ird = conn_param->ird;
ep->ord = conn_param->ord;
- if (peer2peer && ep->ird == 0)
- ep->ird = 1;
+ if (ep->mpa_attr.version != 2)
+ if (peer2peer && ep->ird == 0)
+ ep->ird = 1;
PDBG("%s %d ird %d ord %d\n", __func__, __LINE__, ep->ird, ep->ord);
+ cm_id->add_ref(cm_id);
+ ep->com.cm_id = cm_id;
+ ep->com.qp = qp;
+
/* bind QP to EP and move to RTS */
attrs.mpa_attr = ep->mpa_attr;
attrs.max_ird = ep->ird;
@@ -1847,6 +2239,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
struct c4iw_ep *ep;
struct rtable *rt;
struct net_device *pdev;
+ struct neighbour *neigh;
int step;
if ((conn_param->ord > c4iw_max_read_depth) ||
@@ -1908,14 +2301,15 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
}
ep->dst = &rt->dst;
+ neigh = dst_get_neighbour(ep->dst);
+
/* get a l2t entry */
- if (ep->dst->neighbour->dev->flags & IFF_LOOPBACK) {
+ if (neigh->dev->flags & IFF_LOOPBACK) {
PDBG("%s LOOPBACK\n", __func__);
pdev = ip_dev_find(&init_net,
cm_id->remote_addr.sin_addr.s_addr);
ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
- ep->dst->neighbour,
- pdev, 0);
+ neigh, pdev, 0);
ep->mtu = pdev->mtu;
ep->tx_chan = cxgb4_port_chan(pdev);
ep->smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1;
@@ -1930,20 +2324,20 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
dev_put(pdev);
} else {
ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
- ep->dst->neighbour,
- ep->dst->neighbour->dev, 0);
+ neigh, neigh->dev, 0);
ep->mtu = dst_mtu(ep->dst);
- ep->tx_chan = cxgb4_port_chan(ep->dst->neighbour->dev);
- ep->smac_idx = (cxgb4_port_viid(ep->dst->neighbour->dev) &
- 0x7F) << 1;
+ ep->tx_chan = cxgb4_port_chan(neigh->dev);
+ ep->smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1;
step = ep->com.dev->rdev.lldi.ntxq /
ep->com.dev->rdev.lldi.nchan;
- ep->txq_idx = cxgb4_port_idx(ep->dst->neighbour->dev) * step;
- ep->ctrlq_idx = cxgb4_port_idx(ep->dst->neighbour->dev);
+ ep->txq_idx = cxgb4_port_idx(neigh->dev) * step;
+ ep->ctrlq_idx = cxgb4_port_idx(neigh->dev);
step = ep->com.dev->rdev.lldi.nrxq /
ep->com.dev->rdev.lldi.nchan;
ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
- cxgb4_port_idx(ep->dst->neighbour->dev) * step];
+ cxgb4_port_idx(neigh->dev) * step];
+ ep->retry_with_mpa_v1 = 0;
+ ep->tried_with_mpa_v1 = 0;
}
if (!ep->l2t) {
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
@@ -2323,8 +2717,11 @@ static int peer_abort_intr(struct c4iw_dev *dev, struct sk_buff *skb)
/*
* Wake up any threads in rdma_init() or rdma_fini().
+ * However, this is not needed if com state is just
+ * MPA_REQ_SENT
*/
- c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
+ if (ep->com.state != MPA_REQ_SENT)
+ c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
sched(dev, skb);
return 0;
}
diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c
index 1720dc790d13..f35a935267e7 100644
--- a/drivers/infiniband/hw/cxgb4/cq.c
+++ b/drivers/infiniband/hw/cxgb4/cq.c
@@ -185,7 +185,7 @@ static void insert_recv_cqe(struct t4_wq *wq, struct t4_cq *cq)
V_CQE_OPCODE(FW_RI_SEND) |
V_CQE_TYPE(0) |
V_CQE_SWCQE(1) |
- V_CQE_QPID(wq->rq.qid));
+ V_CQE_QPID(wq->sq.qid));
cqe.bits_type_ts = cpu_to_be64(V_CQE_GENBIT((u64)cq->gen));
cq->sw_queue[cq->sw_pidx] = cqe;
t4_swcq_produce(cq);
@@ -818,6 +818,7 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
chp->cq.size--; /* status page */
chp->ibcq.cqe = entries - 2;
spin_lock_init(&chp->lock);
+ spin_lock_init(&chp->comp_handler_lock);
atomic_set(&chp->refcnt, 1);
init_waitqueue_head(&chp->wait);
ret = insert_handle(rhp, &rhp->cqidr, chp, chp->cq.cqid);
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index 40a13cc633a3..6d0df6ec161b 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -376,10 +376,8 @@ struct uld_ctx {
struct c4iw_dev *dev;
};
-static void c4iw_remove(struct uld_ctx *ctx)
+static void c4iw_dealloc(struct uld_ctx *ctx)
{
- PDBG("%s c4iw_dev %p\n", __func__, ctx->dev);
- c4iw_unregister_device(ctx->dev);
c4iw_rdev_close(&ctx->dev->rdev);
idr_destroy(&ctx->dev->cqidr);
idr_destroy(&ctx->dev->qpidr);
@@ -389,11 +387,30 @@ static void c4iw_remove(struct uld_ctx *ctx)
ctx->dev = NULL;
}
+static void c4iw_remove(struct uld_ctx *ctx)
+{
+ PDBG("%s c4iw_dev %p\n", __func__, ctx->dev);
+ c4iw_unregister_device(ctx->dev);
+ c4iw_dealloc(ctx);
+}
+
+static int rdma_supported(const struct cxgb4_lld_info *infop)
+{
+ return infop->vr->stag.size > 0 && infop->vr->pbl.size > 0 &&
+ infop->vr->rq.size > 0 && infop->vr->qp.size > 0 &&
+ infop->vr->cq.size > 0 && infop->vr->ocq.size > 0;
+}
+
static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
{
struct c4iw_dev *devp;
int ret;
+ if (!rdma_supported(infop)) {
+ printk(KERN_INFO MOD "%s: RDMA not supported on this device.\n",
+ pci_name(infop->pdev));
+ return ERR_PTR(-ENOSYS);
+ }
devp = (struct c4iw_dev *)ib_alloc_device(sizeof(*devp));
if (!devp) {
printk(KERN_ERR MOD "Cannot allocate ib device\n");
@@ -414,7 +431,6 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
ret = c4iw_rdev_open(&devp->rdev);
if (ret) {
- mutex_unlock(&dev_mutex);
printk(KERN_ERR MOD "Unable to open CXIO rdev err %d\n", ret);
ib_dealloc_device(&devp->ibdev);
return ERR_PTR(ret);
@@ -519,15 +535,24 @@ static int c4iw_uld_state_change(void *handle, enum cxgb4_state new_state)
case CXGB4_STATE_UP:
printk(KERN_INFO MOD "%s: Up\n", pci_name(ctx->lldi.pdev));
if (!ctx->dev) {
- int ret = 0;
+ int ret;
ctx->dev = c4iw_alloc(&ctx->lldi);
- if (!IS_ERR(ctx->dev))
- ret = c4iw_register_device(ctx->dev);
- if (IS_ERR(ctx->dev) || ret)
+ if (IS_ERR(ctx->dev)) {
+ printk(KERN_ERR MOD
+ "%s: initialization failed: %ld\n",
+ pci_name(ctx->lldi.pdev),
+ PTR_ERR(ctx->dev));
+ ctx->dev = NULL;
+ break;
+ }
+ ret = c4iw_register_device(ctx->dev);
+ if (ret) {
printk(KERN_ERR MOD
"%s: RDMA registration failed: %d\n",
pci_name(ctx->lldi.pdev), ret);
+ c4iw_dealloc(ctx);
+ }
}
break;
case CXGB4_STATE_DOWN:
diff --git a/drivers/infiniband/hw/cxgb4/ev.c b/drivers/infiniband/hw/cxgb4/ev.c
index c13041a0aeba..397cb36cf103 100644
--- a/drivers/infiniband/hw/cxgb4/ev.c
+++ b/drivers/infiniband/hw/cxgb4/ev.c
@@ -42,6 +42,7 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp,
{
struct ib_event event;
struct c4iw_qp_attributes attrs;
+ unsigned long flag;
if ((qhp->attr.state == C4IW_QP_STATE_ERROR) ||
(qhp->attr.state == C4IW_QP_STATE_TERMINATE)) {
@@ -72,7 +73,9 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp,
if (qhp->ibqp.event_handler)
(*qhp->ibqp.event_handler)(&event, qhp->ibqp.qp_context);
+ spin_lock_irqsave(&chp->comp_handler_lock, flag);
(*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
+ spin_unlock_irqrestore(&chp->comp_handler_lock, flag);
}
void c4iw_ev_dispatch(struct c4iw_dev *dev, struct t4_cqe *err_cqe)
@@ -183,11 +186,14 @@ out:
int c4iw_ev_handler(struct c4iw_dev *dev, u32 qid)
{
struct c4iw_cq *chp;
+ unsigned long flag;
chp = get_chp(dev, qid);
- if (chp)
+ if (chp) {
+ spin_lock_irqsave(&chp->comp_handler_lock, flag);
(*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
- else
+ spin_unlock_irqrestore(&chp->comp_handler_lock, flag);
+ } else
PDBG("%s unknown cqid 0x%x\n", __func__, qid);
return 0;
}
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index 4f045375c8e2..1357c5bf209b 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -309,6 +309,7 @@ struct c4iw_cq {
struct c4iw_dev *rhp;
struct t4_cq cq;
spinlock_t lock;
+ spinlock_t comp_handler_lock;
atomic_t refcnt;
wait_queue_head_t wait;
};
@@ -323,6 +324,7 @@ struct c4iw_mpa_attributes {
u8 recv_marker_enabled;
u8 xmit_marker_enabled;
u8 crc_enabled;
+ u8 enhanced_rdma_conn;
u8 version;
u8 p2p_type;
};
@@ -349,6 +351,8 @@ struct c4iw_qp_attributes {
u8 is_terminate_local;
struct c4iw_mpa_attributes mpa_attr;
struct c4iw_ep *llp_stream_handle;
+ u8 layer_etype;
+ u8 ecode;
};
struct c4iw_qp {
@@ -501,11 +505,18 @@ enum c4iw_mmid_state {
#define MPA_KEY_REP "MPA ID Rep Frame"
#define MPA_MAX_PRIVATE_DATA 256
+#define MPA_ENHANCED_RDMA_CONN 0x10
#define MPA_REJECT 0x20
#define MPA_CRC 0x40
#define MPA_MARKERS 0x80
#define MPA_FLAGS_MASK 0xE0
+#define MPA_V2_PEER2PEER_MODEL 0x8000
+#define MPA_V2_ZERO_LEN_FPDU_RTR 0x4000
+#define MPA_V2_RDMA_WRITE_RTR 0x8000
+#define MPA_V2_RDMA_READ_RTR 0x4000
+#define MPA_V2_IRD_ORD_MASK 0x3FFF
+
#define c4iw_put_ep(ep) { \
PDBG("put_ep (via %s:%u) ep %p refcnt %d\n", __func__, __LINE__, \
ep, atomic_read(&((ep)->kref.refcount))); \
@@ -528,6 +539,11 @@ struct mpa_message {
u8 private_data[0];
};
+struct mpa_v2_conn_params {
+ __be16 ird;
+ __be16 ord;
+};
+
struct terminate_message {
u8 layer_etype;
u8 ecode;
@@ -580,7 +596,10 @@ enum c4iw_ddp_ecodes {
enum c4iw_mpa_ecodes {
MPA_CRC_ERR = 0x02,
- MPA_MARKER_ERR = 0x03
+ MPA_MARKER_ERR = 0x03,
+ MPA_LOCAL_CATA = 0x05,
+ MPA_INSUFF_IRD = 0x06,
+ MPA_NOMATCH_RTR = 0x07,
};
enum c4iw_ep_state {
@@ -651,6 +670,8 @@ struct c4iw_ep {
u16 txq_idx;
u16 ctrlq_idx;
u8 tos;
+ u8 retry_with_mpa_v1;
+ u8 tried_with_mpa_v1;
};
static inline struct c4iw_ep *to_ep(struct iw_cm_id *cm_id)
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index 0347eed4a167..40c835309e49 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -31,7 +31,7 @@
*/
#include <rdma/ib_umem.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "iw_cxgb4.h"
diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c
index 5b9e4220ca08..247fe706e7fa 100644
--- a/drivers/infiniband/hw/cxgb4/provider.c
+++ b/drivers/infiniband/hw/cxgb4/provider.c
@@ -58,13 +58,6 @@ static int fastreg_support = 1;
module_param(fastreg_support, int, 0644);
MODULE_PARM_DESC(fastreg_support, "Advertise fastreg support (default=1)");
-static int c4iw_modify_port(struct ib_device *ibdev,
- u8 port, int port_modify_mask,
- struct ib_port_modify *props)
-{
- return -ENOSYS;
-}
-
static struct ib_ah *c4iw_ah_create(struct ib_pd *pd,
struct ib_ah_attr *ah_attr)
{
@@ -456,7 +449,6 @@ int c4iw_register_device(struct c4iw_dev *dev)
dev->ibdev.dma_device = &(dev->rdev.lldi.pdev->dev);
dev->ibdev.query_device = c4iw_query_device;
dev->ibdev.query_port = c4iw_query_port;
- dev->ibdev.modify_port = c4iw_modify_port;
dev->ibdev.query_pkey = c4iw_query_pkey;
dev->ibdev.query_gid = c4iw_query_gid;
dev->ibdev.alloc_ucontext = c4iw_alloc_ucontext;
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index a41578e48c7b..5f940aeaab1e 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -29,6 +29,9 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
+#include <linux/module.h>
+
#include "iw_cxgb4.h"
static int ocqp_support = 1;
@@ -917,7 +920,11 @@ static void post_terminate(struct c4iw_qp *qhp, struct t4_cqe *err_cqe,
wqe->u.terminate.type = FW_RI_TYPE_TERMINATE;
wqe->u.terminate.immdlen = cpu_to_be32(sizeof *term);
term = (struct terminate_message *)wqe->u.terminate.termmsg;
- build_term_codes(err_cqe, &term->layer_etype, &term->ecode);
+ if (qhp->attr.layer_etype == (LAYER_MPA|DDP_LLP)) {
+ term->layer_etype = qhp->attr.layer_etype;
+ term->ecode = qhp->attr.ecode;
+ } else
+ build_term_codes(err_cqe, &term->layer_etype, &term->ecode);
c4iw_ofld_send(&qhp->rhp->rdev, skb);
}
@@ -941,8 +948,11 @@ static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp,
flushed = c4iw_flush_rq(&qhp->wq, &rchp->cq, count);
spin_unlock(&qhp->lock);
spin_unlock_irqrestore(&rchp->lock, flag);
- if (flushed)
+ if (flushed) {
+ spin_lock_irqsave(&rchp->comp_handler_lock, flag);
(*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+ spin_unlock_irqrestore(&rchp->comp_handler_lock, flag);
+ }
/* locking hierarchy: cq lock first, then qp lock. */
spin_lock_irqsave(&schp->lock, flag);
@@ -952,13 +962,17 @@ static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp,
flushed = c4iw_flush_sq(&qhp->wq, &schp->cq, count);
spin_unlock(&qhp->lock);
spin_unlock_irqrestore(&schp->lock, flag);
- if (flushed)
+ if (flushed) {
+ spin_lock_irqsave(&schp->comp_handler_lock, flag);
(*schp->ibcq.comp_handler)(&schp->ibcq, schp->ibcq.cq_context);
+ spin_unlock_irqrestore(&schp->comp_handler_lock, flag);
+ }
}
static void flush_qp(struct c4iw_qp *qhp)
{
struct c4iw_cq *rchp, *schp;
+ unsigned long flag;
rchp = get_chp(qhp->rhp, qhp->attr.rcq);
schp = get_chp(qhp->rhp, qhp->attr.scq);
@@ -966,8 +980,16 @@ static void flush_qp(struct c4iw_qp *qhp)
if (qhp->ibqp.uobject) {
t4_set_wq_in_error(&qhp->wq);
t4_set_cq_in_error(&rchp->cq);
- if (schp != rchp)
+ spin_lock_irqsave(&rchp->comp_handler_lock, flag);
+ (*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+ spin_unlock_irqrestore(&rchp->comp_handler_lock, flag);
+ if (schp != rchp) {
t4_set_cq_in_error(&schp->cq);
+ spin_lock_irqsave(&schp->comp_handler_lock, flag);
+ (*schp->ibcq.comp_handler)(&schp->ibcq,
+ schp->ibcq.cq_context);
+ spin_unlock_irqrestore(&schp->comp_handler_lock, flag);
+ }
return;
}
__flush_qp(qhp, rchp, schp);
@@ -1012,6 +1034,7 @@ out:
static void build_rtr_msg(u8 p2p_type, struct fw_ri_init *init)
{
+ PDBG("%s p2p_type = %d\n", __func__, p2p_type);
memset(&init->u, 0, sizeof init->u);
switch (p2p_type) {
case FW_RI_INIT_P2PTYPE_RDMA_WRITE:
@@ -1206,12 +1229,16 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
disconnect = 1;
c4iw_get_ep(&qhp->ep->com);
}
+ if (qhp->ibqp.uobject)
+ t4_set_wq_in_error(&qhp->wq);
ret = rdma_fini(rhp, qhp, ep);
if (ret)
goto err;
break;
case C4IW_QP_STATE_TERMINATE:
set_state(qhp, C4IW_QP_STATE_TERMINATE);
+ qhp->attr.layer_etype = attrs->layer_etype;
+ qhp->attr.ecode = attrs->ecode;
if (qhp->ibqp.uobject)
t4_set_wq_in_error(&qhp->wq);
ep = qhp->ep;
@@ -1222,6 +1249,8 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
break;
case C4IW_QP_STATE_ERROR:
set_state(qhp, C4IW_QP_STATE_ERROR);
+ if (qhp->ibqp.uobject)
+ t4_set_wq_in_error(&qhp->wq);
if (!internal) {
abort = 1;
disconnect = 1;
@@ -1334,7 +1363,10 @@ int c4iw_destroy_qp(struct ib_qp *ib_qp)
rhp = qhp->rhp;
attrs.next_state = C4IW_QP_STATE_ERROR;
- c4iw_modify_qp(rhp, qhp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 0);
+ if (qhp->attr.state == C4IW_QP_STATE_TERMINATE)
+ c4iw_modify_qp(rhp, qhp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
+ else
+ c4iw_modify_qp(rhp, qhp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 0);
wait_event(qhp->wait, !qhp->ep);
remove_handle(rhp, &rhp->qpidr, qhp->wq.sq.qid);
diff --git a/drivers/infiniband/hw/cxgb4/resource.c b/drivers/infiniband/hw/cxgb4/resource.c
index 4fb50d58b493..407ff3924150 100644
--- a/drivers/infiniband/hw/cxgb4/resource.c
+++ b/drivers/infiniband/hw/cxgb4/resource.c
@@ -37,6 +37,7 @@
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/genalloc.h>
+#include <linux/ratelimit.h>
#include "iw_cxgb4.h"
#define RANDOM_SIZE 16
@@ -311,8 +312,8 @@ u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size)
{
unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size);
PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size);
- if (!addr && printk_ratelimit())
- printk(KERN_WARNING MOD "%s: Out of PBL memory\n",
+ if (!addr)
+ printk_ratelimited(KERN_WARNING MOD "%s: Out of PBL memory\n",
pci_name(rdev->lldi.pdev));
return (u32)addr;
}
@@ -373,8 +374,8 @@ u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size)
{
unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6);
PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size << 6);
- if (!addr && printk_ratelimit())
- printk(KERN_WARNING MOD "%s: Out of RQT memory\n",
+ if (!addr)
+ printk_ratelimited(KERN_WARNING MOD "%s: Out of RQT memory\n",
pci_name(rdev->lldi.pdev));
return (u32)addr;
}
diff --git a/drivers/infiniband/hw/ehca/ehca_eq.c b/drivers/infiniband/hw/ehca/ehca_eq.c
index d9b1bb40f480..818d721fc448 100644
--- a/drivers/infiniband/hw/ehca/ehca_eq.c
+++ b/drivers/infiniband/hw/ehca/ehca_eq.c
@@ -125,7 +125,7 @@ int ehca_create_eq(struct ehca_shca *shca,
tasklet_init(&eq->interrupt_task, ehca_tasklet_eq, (long)shca);
ret = ibmebus_request_irq(eq->ist, ehca_interrupt_eq,
- IRQF_DISABLED, "ehca_eq",
+ 0, "ehca_eq",
(void *)shca);
if (ret < 0)
ehca_err(ib_dev, "Can't map interrupt handler.");
@@ -133,7 +133,7 @@ int ehca_create_eq(struct ehca_shca *shca,
tasklet_init(&eq->interrupt_task, ehca_tasklet_neq, (long)shca);
ret = ibmebus_request_irq(eq->ist, ehca_interrupt_neq,
- IRQF_DISABLED, "ehca_neq",
+ 0, "ehca_neq",
(void *)shca);
if (ret < 0)
ehca_err(ib_dev, "Can't map interrupt handler.");
diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c
index 32fb34201aba..964f85520798 100644
--- a/drivers/infiniband/hw/ehca/ehca_qp.c
+++ b/drivers/infiniband/hw/ehca/ehca_qp.c
@@ -977,6 +977,9 @@ struct ib_srq *ehca_create_srq(struct ib_pd *pd,
struct hcp_modify_qp_control_block *mqpcb;
u64 hret, update_mask;
+ if (srq_init_attr->srq_type != IB_SRQT_BASIC)
+ return ERR_PTR(-ENOSYS);
+
/* For common attributes, internal_create_qp() takes its info
* out of qp_init_attr, so copy all common attrs there.
*/
diff --git a/drivers/infiniband/hw/ehca/ehca_tools.h b/drivers/infiniband/hw/ehca/ehca_tools.h
index f09914cccf53..54c0d23bad92 100644
--- a/drivers/infiniband/hw/ehca/ehca_tools.h
+++ b/drivers/infiniband/hw/ehca/ehca_tools.h
@@ -58,7 +58,7 @@
#include <linux/cpu.h>
#include <linux/device.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/abs_addr.h>
#include <asm/ibmebus.h>
#include <asm/io.h>
diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c
index daef61d5e5bb..714293b78518 100644
--- a/drivers/infiniband/hw/ipath/ipath_diag.c
+++ b/drivers/infiniband/hw/ipath/ipath_diag.c
@@ -45,6 +45,7 @@
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
+#include <linux/export.h>
#include <asm/uaccess.h>
#include "ipath_kernel.h"
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index be24ac726114..bfca37b2432f 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -41,6 +41,7 @@
#include <linux/vmalloc.h>
#include <linux/bitmap.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "ipath_kernel.h"
#include "ipath_verbs.h"
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index ee79a2d97b14..736d9edbdbe7 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -35,11 +35,13 @@
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/swap.h>
+#include <linux/export.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/highmem.h>
#include <linux/io.h>
#include <linux/jiffies.h>
+#include <linux/cpu.h>
#include <asm/pgtable.h>
#include "ipath_kernel.h"
@@ -1684,17 +1686,19 @@ static int find_best_unit(struct file *fp,
* information. There may be some issues with dual core numbering
* as well. This needs more work prior to release.
*/
- if (!cpumask_empty(&current->cpus_allowed) &&
- !cpumask_full(&current->cpus_allowed)) {
+ if (!cpumask_empty(tsk_cpus_allowed(current)) &&
+ !cpumask_full(tsk_cpus_allowed(current))) {
int ncpus = num_online_cpus(), curcpu = -1, nset = 0;
- for (i = 0; i < ncpus; i++)
- if (cpumask_test_cpu(i, &current->cpus_allowed)) {
+ get_online_cpus();
+ for_each_online_cpu(i)
+ if (cpumask_test_cpu(i, tsk_cpus_allowed(current))) {
ipath_cdbg(PROC, "%s[%u] affinity set for "
"cpu %d/%d\n", current->comm,
current->pid, i, ncpus);
curcpu = i;
nset++;
}
+ put_online_cpus();
if (curcpu != -1 && nset != ncpus) {
if (npresent) {
prefunit = curcpu / (ncpus / npresent);
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
index 7c1eebe8c7c9..49b09c697c7c 100644
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -33,7 +33,9 @@
#include <linux/pci.h>
#include <linux/netdevice.h>
+#include <linux/moduleparam.h>
#include <linux/slab.h>
+#include <linux/stat.h>
#include <linux/vmalloc.h>
#include "ipath_kernel.h"
diff --git a/drivers/infiniband/hw/ipath/ipath_mad.c b/drivers/infiniband/hw/ipath/ipath_mad.c
index ceb98ee78666..43f2d0424d4f 100644
--- a/drivers/infiniband/hw/ipath/ipath_mad.c
+++ b/drivers/infiniband/hw/ipath/ipath_mad.c
@@ -32,6 +32,7 @@
*/
#include <rdma/ib_smi.h>
+#include <rdma/ib_pma.h>
#include "ipath_kernel.h"
#include "ipath_verbs.h"
@@ -789,151 +790,18 @@ static int recv_subn_set_pkeytable(struct ib_smp *smp,
return recv_subn_get_pkeytable(smp, ibdev);
}
-#define IB_PMA_CLASS_PORT_INFO cpu_to_be16(0x0001)
-#define IB_PMA_PORT_SAMPLES_CONTROL cpu_to_be16(0x0010)
-#define IB_PMA_PORT_SAMPLES_RESULT cpu_to_be16(0x0011)
-#define IB_PMA_PORT_COUNTERS cpu_to_be16(0x0012)
-#define IB_PMA_PORT_COUNTERS_EXT cpu_to_be16(0x001D)
-#define IB_PMA_PORT_SAMPLES_RESULT_EXT cpu_to_be16(0x001E)
-
-struct ib_perf {
- u8 base_version;
- u8 mgmt_class;
- u8 class_version;
- u8 method;
- __be16 status;
- __be16 unused;
- __be64 tid;
- __be16 attr_id;
- __be16 resv;
- __be32 attr_mod;
- u8 reserved[40];
- u8 data[192];
-} __attribute__ ((packed));
-
-struct ib_pma_classportinfo {
- u8 base_version;
- u8 class_version;
- __be16 cap_mask;
- u8 reserved[3];
- u8 resp_time_value; /* only lower 5 bits */
- union ib_gid redirect_gid;
- __be32 redirect_tc_sl_fl; /* 8, 4, 20 bits respectively */
- __be16 redirect_lid;
- __be16 redirect_pkey;
- __be32 redirect_qp; /* only lower 24 bits */
- __be32 redirect_qkey;
- union ib_gid trap_gid;
- __be32 trap_tc_sl_fl; /* 8, 4, 20 bits respectively */
- __be16 trap_lid;
- __be16 trap_pkey;
- __be32 trap_hl_qp; /* 8, 24 bits respectively */
- __be32 trap_qkey;
-} __attribute__ ((packed));
-
-struct ib_pma_portsamplescontrol {
- u8 opcode;
- u8 port_select;
- u8 tick;
- u8 counter_width; /* only lower 3 bits */
- __be32 counter_mask0_9; /* 2, 10 * 3, bits */
- __be16 counter_mask10_14; /* 1, 5 * 3, bits */
- u8 sample_mechanisms;
- u8 sample_status; /* only lower 2 bits */
- __be64 option_mask;
- __be64 vendor_mask;
- __be32 sample_start;
- __be32 sample_interval;
- __be16 tag;
- __be16 counter_select[15];
-} __attribute__ ((packed));
-
-struct ib_pma_portsamplesresult {
- __be16 tag;
- __be16 sample_status; /* only lower 2 bits */
- __be32 counter[15];
-} __attribute__ ((packed));
-
-struct ib_pma_portsamplesresult_ext {
- __be16 tag;
- __be16 sample_status; /* only lower 2 bits */
- __be32 extended_width; /* only upper 2 bits */
- __be64 counter[15];
-} __attribute__ ((packed));
-
-struct ib_pma_portcounters {
- u8 reserved;
- u8 port_select;
- __be16 counter_select;
- __be16 symbol_error_counter;
- u8 link_error_recovery_counter;
- u8 link_downed_counter;
- __be16 port_rcv_errors;
- __be16 port_rcv_remphys_errors;
- __be16 port_rcv_switch_relay_errors;
- __be16 port_xmit_discards;
- u8 port_xmit_constraint_errors;
- u8 port_rcv_constraint_errors;
- u8 reserved1;
- u8 lli_ebor_errors; /* 4, 4, bits */
- __be16 reserved2;
- __be16 vl15_dropped;
- __be32 port_xmit_data;
- __be32 port_rcv_data;
- __be32 port_xmit_packets;
- __be32 port_rcv_packets;
-} __attribute__ ((packed));
-
-#define IB_PMA_SEL_SYMBOL_ERROR cpu_to_be16(0x0001)
-#define IB_PMA_SEL_LINK_ERROR_RECOVERY cpu_to_be16(0x0002)
-#define IB_PMA_SEL_LINK_DOWNED cpu_to_be16(0x0004)
-#define IB_PMA_SEL_PORT_RCV_ERRORS cpu_to_be16(0x0008)
-#define IB_PMA_SEL_PORT_RCV_REMPHYS_ERRORS cpu_to_be16(0x0010)
-#define IB_PMA_SEL_PORT_XMIT_DISCARDS cpu_to_be16(0x0040)
-#define IB_PMA_SEL_LOCAL_LINK_INTEGRITY_ERRORS cpu_to_be16(0x0200)
-#define IB_PMA_SEL_EXCESSIVE_BUFFER_OVERRUNS cpu_to_be16(0x0400)
-#define IB_PMA_SEL_PORT_VL15_DROPPED cpu_to_be16(0x0800)
-#define IB_PMA_SEL_PORT_XMIT_DATA cpu_to_be16(0x1000)
-#define IB_PMA_SEL_PORT_RCV_DATA cpu_to_be16(0x2000)
-#define IB_PMA_SEL_PORT_XMIT_PACKETS cpu_to_be16(0x4000)
-#define IB_PMA_SEL_PORT_RCV_PACKETS cpu_to_be16(0x8000)
-
-struct ib_pma_portcounters_ext {
- u8 reserved;
- u8 port_select;
- __be16 counter_select;
- __be32 reserved1;
- __be64 port_xmit_data;
- __be64 port_rcv_data;
- __be64 port_xmit_packets;
- __be64 port_rcv_packets;
- __be64 port_unicast_xmit_packets;
- __be64 port_unicast_rcv_packets;
- __be64 port_multicast_xmit_packets;
- __be64 port_multicast_rcv_packets;
-} __attribute__ ((packed));
-
-#define IB_PMA_SELX_PORT_XMIT_DATA cpu_to_be16(0x0001)
-#define IB_PMA_SELX_PORT_RCV_DATA cpu_to_be16(0x0002)
-#define IB_PMA_SELX_PORT_XMIT_PACKETS cpu_to_be16(0x0004)
-#define IB_PMA_SELX_PORT_RCV_PACKETS cpu_to_be16(0x0008)
-#define IB_PMA_SELX_PORT_UNI_XMIT_PACKETS cpu_to_be16(0x0010)
-#define IB_PMA_SELX_PORT_UNI_RCV_PACKETS cpu_to_be16(0x0020)
-#define IB_PMA_SELX_PORT_MULTI_XMIT_PACKETS cpu_to_be16(0x0040)
-#define IB_PMA_SELX_PORT_MULTI_RCV_PACKETS cpu_to_be16(0x0080)
-
-static int recv_pma_get_classportinfo(struct ib_perf *pmp)
+static int recv_pma_get_classportinfo(struct ib_pma_mad *pmp)
{
- struct ib_pma_classportinfo *p =
- (struct ib_pma_classportinfo *)pmp->data;
+ struct ib_class_port_info *p =
+ (struct ib_class_port_info *)pmp->data;
memset(pmp->data, 0, sizeof(pmp->data));
- if (pmp->attr_mod != 0)
- pmp->status |= IB_SMP_INVALID_FIELD;
+ if (pmp->mad_hdr.attr_mod != 0)
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
/* Indicate AllPortSelect is valid (only one port anyway) */
- p->cap_mask = cpu_to_be16(1 << 8);
+ p->capability_mask = cpu_to_be16(1 << 8);
p->base_version = 1;
p->class_version = 1;
/*
@@ -957,7 +825,7 @@ static int recv_pma_get_classportinfo(struct ib_perf *pmp)
COUNTER_MASK(1, 3) | \
COUNTER_MASK(1, 4))
-static int recv_pma_get_portsamplescontrol(struct ib_perf *pmp,
+static int recv_pma_get_portsamplescontrol(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portsamplescontrol *p =
@@ -970,9 +838,9 @@ static int recv_pma_get_portsamplescontrol(struct ib_perf *pmp,
memset(pmp->data, 0, sizeof(pmp->data));
p->port_select = port_select;
- if (pmp->attr_mod != 0 ||
+ if (pmp->mad_hdr.attr_mod != 0 ||
(port_select != port && port_select != 0xFF))
- pmp->status |= IB_SMP_INVALID_FIELD;
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
/*
* Ticks are 10x the link transfer period which for 2.5Gbs is 4
* nsec. 0 == 4 nsec., 1 == 8 nsec., ..., 255 == 1020 nsec. Sample
@@ -1006,7 +874,7 @@ static int recv_pma_get_portsamplescontrol(struct ib_perf *pmp,
return reply((struct ib_smp *) pmp);
}
-static int recv_pma_set_portsamplescontrol(struct ib_perf *pmp,
+static int recv_pma_set_portsamplescontrol(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portsamplescontrol *p =
@@ -1017,9 +885,9 @@ static int recv_pma_set_portsamplescontrol(struct ib_perf *pmp,
u8 status;
int ret;
- if (pmp->attr_mod != 0 ||
+ if (pmp->mad_hdr.attr_mod != 0 ||
(p->port_select != port && p->port_select != 0xFF)) {
- pmp->status |= IB_SMP_INVALID_FIELD;
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
ret = reply((struct ib_smp *) pmp);
goto bail;
}
@@ -1093,7 +961,7 @@ static u64 get_counter(struct ipath_ibdev *dev,
return ret;
}
-static int recv_pma_get_portsamplesresult(struct ib_perf *pmp,
+static int recv_pma_get_portsamplesresult(struct ib_pma_mad *pmp,
struct ib_device *ibdev)
{
struct ib_pma_portsamplesresult *p =
@@ -1118,7 +986,7 @@ static int recv_pma_get_portsamplesresult(struct ib_perf *pmp,
return reply((struct ib_smp *) pmp);
}
-static int recv_pma_get_portsamplesresult_ext(struct ib_perf *pmp,
+static int recv_pma_get_portsamplesresult_ext(struct ib_pma_mad *pmp,
struct ib_device *ibdev)
{
struct ib_pma_portsamplesresult_ext *p =
@@ -1145,7 +1013,7 @@ static int recv_pma_get_portsamplesresult_ext(struct ib_perf *pmp,
return reply((struct ib_smp *) pmp);
}
-static int recv_pma_get_portcounters(struct ib_perf *pmp,
+static int recv_pma_get_portcounters(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portcounters *p = (struct ib_pma_portcounters *)
@@ -1179,9 +1047,9 @@ static int recv_pma_get_portcounters(struct ib_perf *pmp,
memset(pmp->data, 0, sizeof(pmp->data));
p->port_select = port_select;
- if (pmp->attr_mod != 0 ||
+ if (pmp->mad_hdr.attr_mod != 0 ||
(port_select != port && port_select != 0xFF))
- pmp->status |= IB_SMP_INVALID_FIELD;
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
if (cntrs.symbol_error_counter > 0xFFFFUL)
p->symbol_error_counter = cpu_to_be16(0xFFFF);
@@ -1216,7 +1084,7 @@ static int recv_pma_get_portcounters(struct ib_perf *pmp,
cntrs.local_link_integrity_errors = 0xFUL;
if (cntrs.excessive_buffer_overrun_errors > 0xFUL)
cntrs.excessive_buffer_overrun_errors = 0xFUL;
- p->lli_ebor_errors = (cntrs.local_link_integrity_errors << 4) |
+ p->link_overrun_errors = (cntrs.local_link_integrity_errors << 4) |
cntrs.excessive_buffer_overrun_errors;
if (cntrs.vl15_dropped > 0xFFFFUL)
p->vl15_dropped = cpu_to_be16(0xFFFF);
@@ -1244,7 +1112,7 @@ static int recv_pma_get_portcounters(struct ib_perf *pmp,
return reply((struct ib_smp *) pmp);
}
-static int recv_pma_get_portcounters_ext(struct ib_perf *pmp,
+static int recv_pma_get_portcounters_ext(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portcounters_ext *p =
@@ -1265,9 +1133,9 @@ static int recv_pma_get_portcounters_ext(struct ib_perf *pmp,
memset(pmp->data, 0, sizeof(pmp->data));
p->port_select = port_select;
- if (pmp->attr_mod != 0 ||
+ if (pmp->mad_hdr.attr_mod != 0 ||
(port_select != port && port_select != 0xFF))
- pmp->status |= IB_SMP_INVALID_FIELD;
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
p->port_xmit_data = cpu_to_be64(swords);
p->port_rcv_data = cpu_to_be64(rwords);
@@ -1281,7 +1149,7 @@ static int recv_pma_get_portcounters_ext(struct ib_perf *pmp,
return reply((struct ib_smp *) pmp);
}
-static int recv_pma_set_portcounters(struct ib_perf *pmp,
+static int recv_pma_set_portcounters(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portcounters *p = (struct ib_pma_portcounters *)
@@ -1344,7 +1212,7 @@ static int recv_pma_set_portcounters(struct ib_perf *pmp,
return recv_pma_get_portcounters(pmp, ibdev, port);
}
-static int recv_pma_set_portcounters_ext(struct ib_perf *pmp,
+static int recv_pma_set_portcounters_ext(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portcounters *p = (struct ib_pma_portcounters *)
@@ -1518,19 +1386,19 @@ static int process_perf(struct ib_device *ibdev, u8 port_num,
struct ib_mad *in_mad,
struct ib_mad *out_mad)
{
- struct ib_perf *pmp = (struct ib_perf *)out_mad;
+ struct ib_pma_mad *pmp = (struct ib_pma_mad *)out_mad;
int ret;
*out_mad = *in_mad;
- if (pmp->class_version != 1) {
- pmp->status |= IB_SMP_UNSUP_VERSION;
+ if (pmp->mad_hdr.class_version != 1) {
+ pmp->mad_hdr.status |= IB_SMP_UNSUP_VERSION;
ret = reply((struct ib_smp *) pmp);
goto bail;
}
- switch (pmp->method) {
+ switch (pmp->mad_hdr.method) {
case IB_MGMT_METHOD_GET:
- switch (pmp->attr_id) {
+ switch (pmp->mad_hdr.attr_id) {
case IB_PMA_CLASS_PORT_INFO:
ret = recv_pma_get_classportinfo(pmp);
goto bail;
@@ -1554,13 +1422,13 @@ static int process_perf(struct ib_device *ibdev, u8 port_num,
port_num);
goto bail;
default:
- pmp->status |= IB_SMP_UNSUP_METH_ATTR;
+ pmp->mad_hdr.status |= IB_SMP_UNSUP_METH_ATTR;
ret = reply((struct ib_smp *) pmp);
goto bail;
}
case IB_MGMT_METHOD_SET:
- switch (pmp->attr_id) {
+ switch (pmp->mad_hdr.attr_id) {
case IB_PMA_PORT_SAMPLES_CONTROL:
ret = recv_pma_set_portsamplescontrol(pmp, ibdev,
port_num);
@@ -1574,7 +1442,7 @@ static int process_perf(struct ib_device *ibdev, u8 port_num,
port_num);
goto bail;
default:
- pmp->status |= IB_SMP_UNSUP_METH_ATTR;
+ pmp->mad_hdr.status |= IB_SMP_UNSUP_METH_ATTR;
ret = reply((struct ib_smp *) pmp);
goto bail;
}
@@ -1588,7 +1456,7 @@ static int process_perf(struct ib_device *ibdev, u8 port_num,
ret = IB_MAD_RESULT_SUCCESS;
goto bail;
default:
- pmp->status |= IB_SMP_UNSUP_METHOD;
+ pmp->mad_hdr.status |= IB_SMP_UNSUP_METHOD;
ret = reply((struct ib_smp *) pmp);
}
diff --git a/drivers/infiniband/hw/ipath/ipath_srq.c b/drivers/infiniband/hw/ipath/ipath_srq.c
index 386e2c717c53..26271984b717 100644
--- a/drivers/infiniband/hw/ipath/ipath_srq.c
+++ b/drivers/infiniband/hw/ipath/ipath_srq.c
@@ -107,6 +107,11 @@ struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
u32 sz;
struct ib_srq *ret;
+ if (srq_init_attr->srq_type != IB_SRQT_BASIC) {
+ ret = ERR_PTR(-ENOSYS);
+ goto done;
+ }
+
if (srq_init_attr->attr.max_wr == 0) {
ret = ERR_PTR(-EINVAL);
goto done;
diff --git a/drivers/infiniband/hw/ipath/ipath_sysfs.c b/drivers/infiniband/hw/ipath/ipath_sysfs.c
index 8991677e9a08..75558f33f1cb 100644
--- a/drivers/infiniband/hw/ipath/ipath_sysfs.c
+++ b/drivers/infiniband/hw/ipath/ipath_sysfs.c
@@ -32,6 +32,7 @@
*/
#include <linux/ctype.h>
+#include <linux/stat.h>
#include "ipath_kernel.h"
#include "ipath_verbs.h"
diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c
index cfed5399f074..dc66c4506916 100644
--- a/drivers/infiniband/hw/ipath/ipath_user_pages.c
+++ b/drivers/infiniband/hw/ipath/ipath_user_pages.c
@@ -79,7 +79,7 @@ static int __ipath_get_user_pages(unsigned long start_page, size_t num_pages,
goto bail_release;
}
- current->mm->locked_vm += num_pages;
+ current->mm->pinned_vm += num_pages;
ret = 0;
goto bail;
@@ -178,7 +178,7 @@ void ipath_release_user_pages(struct page **p, size_t num_pages)
__ipath_release_user_pages(p, num_pages, 1);
- current->mm->locked_vm -= num_pages;
+ current->mm->pinned_vm -= num_pages;
up_write(&current->mm->mmap_sem);
}
@@ -195,7 +195,7 @@ static void user_pages_account(struct work_struct *_work)
container_of(_work, struct ipath_user_pages_work, work);
down_write(&work->mm->mmap_sem);
- work->mm->locked_vm -= work->num_pages;
+ work->mm->pinned_vm -= work->num_pages;
up_write(&work->mm->mmap_sem);
mmput(work->mm);
kfree(work);
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
index dd7f26d04d46..439c35d4a669 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -35,6 +35,7 @@
#include <rdma/ib_user_verbs.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/utsname.h>
#include <linux/rculist.h>
diff --git a/drivers/infiniband/hw/mlx4/Kconfig b/drivers/infiniband/hw/mlx4/Kconfig
index bd995b2b50d8..24ab11a9ad1e 100644
--- a/drivers/infiniband/hw/mlx4/Kconfig
+++ b/drivers/infiniband/hw/mlx4/Kconfig
@@ -1,6 +1,7 @@
config MLX4_INFINIBAND
tristate "Mellanox ConnectX HCA support"
- depends on NETDEVICES && NETDEV_10000 && PCI
+ depends on NETDEVICES && ETHERNET && PCI
+ select NET_VENDOR_MELLANOX
select MLX4_CORE
---help---
This driver provides low-level InfiniBand support for
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index 57ffa50f509e..f36da994a85a 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -35,6 +35,7 @@
#include <linux/mlx4/cmd.h>
#include <linux/gfp.h>
+#include <rdma/ib_pma.h>
#include "mlx4_ib.h"
@@ -232,7 +233,7 @@ static void forward_trap(struct mlx4_ib_dev *dev, u8 port_num, struct ib_mad *ma
}
}
-int mlx4_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
+static int ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
struct ib_wc *in_wc, struct ib_grh *in_grh,
struct ib_mad *in_mad, struct ib_mad *out_mad)
{
@@ -302,6 +303,71 @@ int mlx4_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;
}
+static void edit_counter(struct mlx4_counter *cnt,
+ struct ib_pma_portcounters *pma_cnt)
+{
+ pma_cnt->port_xmit_data = cpu_to_be32((be64_to_cpu(cnt->tx_bytes)>>2));
+ pma_cnt->port_rcv_data = cpu_to_be32((be64_to_cpu(cnt->rx_bytes)>>2));
+ pma_cnt->port_xmit_packets = cpu_to_be32(be64_to_cpu(cnt->tx_frames));
+ pma_cnt->port_rcv_packets = cpu_to_be32(be64_to_cpu(cnt->rx_frames));
+}
+
+static int iboe_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
+ struct ib_wc *in_wc, struct ib_grh *in_grh,
+ struct ib_mad *in_mad, struct ib_mad *out_mad)
+{
+ struct mlx4_cmd_mailbox *mailbox;
+ struct mlx4_ib_dev *dev = to_mdev(ibdev);
+ int err;
+ u32 inmod = dev->counters[port_num - 1] & 0xffff;
+ u8 mode;
+
+ if (in_mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_PERF_MGMT)
+ return -EINVAL;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev->dev);
+ if (IS_ERR(mailbox))
+ return IB_MAD_RESULT_FAILURE;
+
+ err = mlx4_cmd_box(dev->dev, 0, mailbox->dma, inmod, 0,
+ MLX4_CMD_QUERY_IF_STAT, MLX4_CMD_TIME_CLASS_C);
+ if (err)
+ err = IB_MAD_RESULT_FAILURE;
+ else {
+ memset(out_mad->data, 0, sizeof out_mad->data);
+ mode = ((struct mlx4_counter *)mailbox->buf)->counter_mode;
+ switch (mode & 0xf) {
+ case 0:
+ edit_counter(mailbox->buf,
+ (void *)(out_mad->data + 40));
+ err = IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;
+ break;
+ default:
+ err = IB_MAD_RESULT_FAILURE;
+ }
+ }
+
+ mlx4_free_cmd_mailbox(dev->dev, mailbox);
+
+ return err;
+}
+
+int mlx4_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
+ struct ib_wc *in_wc, struct ib_grh *in_grh,
+ struct ib_mad *in_mad, struct ib_mad *out_mad)
+{
+ switch (rdma_port_get_link_layer(ibdev, port_num)) {
+ case IB_LINK_LAYER_INFINIBAND:
+ return ib_process_mad(ibdev, mad_flags, port_num, in_wc,
+ in_grh, in_mad, out_mad);
+ case IB_LINK_LAYER_ETHERNET:
+ return iboe_process_mad(ibdev, mad_flags, port_num, in_wc,
+ in_grh, in_mad, out_mad);
+ default:
+ return -EINVAL;
+ }
+}
+
static void send_handler(struct ib_mad_agent *agent,
struct ib_mad_send_wc *mad_send_wc)
{
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index fbe1973f77b0..77f3dbc0aaa1 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -128,6 +128,8 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
(dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_REMOTE_INV) &&
(dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_FAST_REG_WR))
props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS;
+ if (dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC)
+ props->device_cap_flags |= IB_DEVICE_XRC;
props->vendor_id = be32_to_cpup((__be32 *) (out_mad->data + 36)) &
0xffffff;
@@ -181,8 +183,12 @@ mlx4_ib_port_link_layer(struct ib_device *device, u8 port_num)
static int ib_link_query_port(struct ib_device *ibdev, u8 port,
struct ib_port_attr *props,
+ struct ib_smp *in_mad,
struct ib_smp *out_mad)
{
+ int ext_active_speed;
+ int err;
+
props->lid = be16_to_cpup((__be16 *) (out_mad->data + 16));
props->lmc = out_mad->data[34] & 0x7;
props->sm_lid = be16_to_cpup((__be16 *) (out_mad->data + 18));
@@ -203,6 +209,39 @@ static int ib_link_query_port(struct ib_device *ibdev, u8 port,
props->max_vl_num = out_mad->data[37] >> 4;
props->init_type_reply = out_mad->data[41] >> 4;
+ /* Check if extended speeds (EDR/FDR/...) are supported */
+ if (props->port_cap_flags & IB_PORT_EXTENDED_SPEEDS_SUP) {
+ ext_active_speed = out_mad->data[62] >> 4;
+
+ switch (ext_active_speed) {
+ case 1:
+ props->active_speed = 16; /* FDR */
+ break;
+ case 2:
+ props->active_speed = 32; /* EDR */
+ break;
+ }
+ }
+
+ /* If reported active speed is QDR, check if is FDR-10 */
+ if (props->active_speed == 4) {
+ if (to_mdev(ibdev)->dev->caps.ext_port_cap[port] &
+ MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO) {
+ init_query_mad(in_mad);
+ in_mad->attr_id = MLX4_ATTR_EXTENDED_PORT_INFO;
+ in_mad->attr_mod = cpu_to_be32(port);
+
+ err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port,
+ NULL, NULL, in_mad, out_mad);
+ if (err)
+ return err;
+
+ /* Checking LinkSpeedActive for FDR-10 */
+ if (out_mad->data[15] & 0x1)
+ props->active_speed = 8;
+ }
+ }
+
return 0;
}
@@ -227,7 +266,7 @@ static int eth_link_query_port(struct ib_device *ibdev, u8 port,
props->pkey_tbl_len = 1;
props->bad_pkey_cntr = be16_to_cpup((__be16 *) (out_mad->data + 46));
props->qkey_viol_cntr = be16_to_cpup((__be16 *) (out_mad->data + 48));
- props->max_mtu = IB_MTU_2048;
+ props->max_mtu = IB_MTU_4096;
props->subnet_timeout = 0;
props->max_vl_num = out_mad->data[37] >> 4;
props->init_type_reply = 0;
@@ -274,7 +313,7 @@ static int mlx4_ib_query_port(struct ib_device *ibdev, u8 port,
goto out;
err = mlx4_ib_port_link_layer(ibdev, port) == IB_LINK_LAYER_INFINIBAND ?
- ib_link_query_port(ibdev, port, props, out_mad) :
+ ib_link_query_port(ibdev, port, props, in_mad, out_mad) :
eth_link_query_port(ibdev, port, props, out_mad);
out:
@@ -566,6 +605,57 @@ static int mlx4_ib_dealloc_pd(struct ib_pd *pd)
return 0;
}
+static struct ib_xrcd *mlx4_ib_alloc_xrcd(struct ib_device *ibdev,
+ struct ib_ucontext *context,
+ struct ib_udata *udata)
+{
+ struct mlx4_ib_xrcd *xrcd;
+ int err;
+
+ if (!(to_mdev(ibdev)->dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC))
+ return ERR_PTR(-ENOSYS);
+
+ xrcd = kmalloc(sizeof *xrcd, GFP_KERNEL);
+ if (!xrcd)
+ return ERR_PTR(-ENOMEM);
+
+ err = mlx4_xrcd_alloc(to_mdev(ibdev)->dev, &xrcd->xrcdn);
+ if (err)
+ goto err1;
+
+ xrcd->pd = ib_alloc_pd(ibdev);
+ if (IS_ERR(xrcd->pd)) {
+ err = PTR_ERR(xrcd->pd);
+ goto err2;
+ }
+
+ xrcd->cq = ib_create_cq(ibdev, NULL, NULL, xrcd, 1, 0);
+ if (IS_ERR(xrcd->cq)) {
+ err = PTR_ERR(xrcd->cq);
+ goto err3;
+ }
+
+ return &xrcd->ibxrcd;
+
+err3:
+ ib_dealloc_pd(xrcd->pd);
+err2:
+ mlx4_xrcd_free(to_mdev(ibdev)->dev, xrcd->xrcdn);
+err1:
+ kfree(xrcd);
+ return ERR_PTR(err);
+}
+
+static int mlx4_ib_dealloc_xrcd(struct ib_xrcd *xrcd)
+{
+ ib_destroy_cq(to_mxrcd(xrcd)->cq);
+ ib_dealloc_pd(to_mxrcd(xrcd)->pd);
+ mlx4_xrcd_free(to_mdev(xrcd->device)->dev, to_mxrcd(xrcd)->xrcdn);
+ kfree(xrcd);
+
+ return 0;
+}
+
static int add_gid_entry(struct ib_qp *ibqp, union ib_gid *gid)
{
struct mlx4_ib_qp *mqp = to_mqp(ibqp);
@@ -816,7 +906,7 @@ static void update_gids_task(struct work_struct *work)
memcpy(gw->dev->iboe.gid_table[gw->port - 1], gw->gids, sizeof gw->gids);
event.device = &gw->dev->ib_dev;
event.element.port_num = gw->port;
- event.event = IB_EVENT_LID_CHANGE;
+ event.event = IB_EVENT_GID_CHANGE;
ib_dispatch_event(&event);
}
@@ -1044,7 +1134,9 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
(1ull << IB_USER_VERBS_CMD_CREATE_SRQ) |
(1ull << IB_USER_VERBS_CMD_MODIFY_SRQ) |
(1ull << IB_USER_VERBS_CMD_QUERY_SRQ) |
- (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ);
+ (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_XSRQ) |
+ (1ull << IB_USER_VERBS_CMD_OPEN_QP);
ibdev->ib_dev.query_device = mlx4_ib_query_device;
ibdev->ib_dev.query_port = mlx4_ib_query_port;
@@ -1093,16 +1185,34 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
ibdev->ib_dev.unmap_fmr = mlx4_ib_unmap_fmr;
ibdev->ib_dev.dealloc_fmr = mlx4_ib_fmr_dealloc;
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC) {
+ ibdev->ib_dev.alloc_xrcd = mlx4_ib_alloc_xrcd;
+ ibdev->ib_dev.dealloc_xrcd = mlx4_ib_dealloc_xrcd;
+ ibdev->ib_dev.uverbs_cmd_mask |=
+ (1ull << IB_USER_VERBS_CMD_OPEN_XRCD) |
+ (1ull << IB_USER_VERBS_CMD_CLOSE_XRCD);
+ }
+
spin_lock_init(&iboe->lock);
if (init_node_data(ibdev))
goto err_map;
+ for (i = 0; i < ibdev->num_ports; ++i) {
+ if (mlx4_ib_port_link_layer(&ibdev->ib_dev, i + 1) ==
+ IB_LINK_LAYER_ETHERNET) {
+ err = mlx4_counter_alloc(ibdev->dev, &ibdev->counters[i]);
+ if (err)
+ ibdev->counters[i] = -1;
+ } else
+ ibdev->counters[i] = -1;
+ }
+
spin_lock_init(&ibdev->sm_lock);
mutex_init(&ibdev->cap_mask_mutex);
if (ib_register_device(&ibdev->ib_dev, NULL))
- goto err_map;
+ goto err_counter;
if (mlx4_ib_mad_init(ibdev))
goto err_reg;
@@ -1132,6 +1242,10 @@ err_notif:
err_reg:
ib_unregister_device(&ibdev->ib_dev);
+err_counter:
+ for (; i; --i)
+ mlx4_counter_free(ibdev->dev, ibdev->counters[i - 1]);
+
err_map:
iounmap(ibdev->uar_map);
@@ -1160,7 +1274,8 @@ static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr)
ibdev->iboe.nb.notifier_call = NULL;
}
iounmap(ibdev->uar_map);
-
+ for (p = 0; p < ibdev->num_ports; ++p)
+ mlx4_counter_free(ibdev->dev, ibdev->counters[p]);
mlx4_foreach_port(p, dev, MLX4_PORT_TYPE_IB)
mlx4_CLOSE_PORT(dev, p);
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index 2a322f21049f..ed80345c99ae 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -56,6 +56,13 @@ struct mlx4_ib_pd {
u32 pdn;
};
+struct mlx4_ib_xrcd {
+ struct ib_xrcd ibxrcd;
+ u32 xrcdn;
+ struct ib_pd *pd;
+ struct ib_cq *cq;
+};
+
struct mlx4_ib_cq_buf {
struct mlx4_buf buf;
struct mlx4_mtt mtt;
@@ -138,6 +145,7 @@ struct mlx4_ib_qp {
struct mlx4_mtt mtt;
int buf_size;
struct mutex mutex;
+ u16 xrcdn;
u32 flags;
u8 port;
u8 alt_port;
@@ -193,6 +201,7 @@ struct mlx4_ib_dev {
struct mutex cap_mask_mutex;
bool ib_active;
struct mlx4_ib_iboe iboe;
+ int counters[MLX4_MAX_PORTS];
};
static inline struct mlx4_ib_dev *to_mdev(struct ib_device *ibdev)
@@ -210,6 +219,11 @@ static inline struct mlx4_ib_pd *to_mpd(struct ib_pd *ibpd)
return container_of(ibpd, struct mlx4_ib_pd, ibpd);
}
+static inline struct mlx4_ib_xrcd *to_mxrcd(struct ib_xrcd *ibxrcd)
+{
+ return container_of(ibxrcd, struct mlx4_ib_xrcd, ibxrcd);
+}
+
static inline struct mlx4_ib_cq *to_mcq(struct ib_cq *ibcq)
{
return container_of(ibcq, struct mlx4_ib_cq, ibcq);
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index 2001f20a4361..a16f0c8e6f3f 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -302,15 +302,14 @@ static int send_wqe_overhead(enum ib_qp_type type, u32 flags)
}
static int set_rq_size(struct mlx4_ib_dev *dev, struct ib_qp_cap *cap,
- int is_user, int has_srq, struct mlx4_ib_qp *qp)
+ int is_user, int has_rq, struct mlx4_ib_qp *qp)
{
/* Sanity check RQ size before proceeding */
if (cap->max_recv_wr > dev->dev->caps.max_wqes ||
cap->max_recv_sge > dev->dev->caps.max_rq_sg)
return -EINVAL;
- if (has_srq) {
- /* QPs attached to an SRQ should have no RQ */
+ if (!has_rq) {
if (cap->max_recv_wr)
return -EINVAL;
@@ -463,6 +462,14 @@ static int set_user_sq_size(struct mlx4_ib_dev *dev,
return 0;
}
+static int qp_has_rq(struct ib_qp_init_attr *attr)
+{
+ if (attr->qp_type == IB_QPT_XRC_INI || attr->qp_type == IB_QPT_XRC_TGT)
+ return 0;
+
+ return !attr->srq;
+}
+
static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
struct ib_qp_init_attr *init_attr,
struct ib_udata *udata, int sqpn, struct mlx4_ib_qp *qp)
@@ -479,7 +486,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR)
qp->sq_signal_bits = cpu_to_be32(MLX4_WQE_CTRL_CQ_UPDATE);
- err = set_rq_size(dev, &init_attr->cap, !!pd->uobject, !!init_attr->srq, qp);
+ err = set_rq_size(dev, &init_attr->cap, !!pd->uobject, qp_has_rq(init_attr), qp);
if (err)
goto err;
@@ -513,7 +520,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
if (err)
goto err_mtt;
- if (!init_attr->srq) {
+ if (qp_has_rq(init_attr)) {
err = mlx4_ib_db_map_user(to_mucontext(pd->uobject->context),
ucmd.db_addr, &qp->db);
if (err)
@@ -532,7 +539,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
if (err)
goto err;
- if (!init_attr->srq) {
+ if (qp_has_rq(init_attr)) {
err = mlx4_db_alloc(dev->dev, &qp->db, 0);
if (err)
goto err;
@@ -575,6 +582,9 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
if (err)
goto err_qpn;
+ if (init_attr->qp_type == IB_QPT_XRC_TGT)
+ qp->mqp.qpn |= (1 << 23);
+
/*
* Hardware wants QPN written in big-endian order (after
* shifting) for send doorbell. Precompute this value to save
@@ -592,9 +602,8 @@ err_qpn:
err_wrid:
if (pd->uobject) {
- if (!init_attr->srq)
- mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context),
- &qp->db);
+ if (qp_has_rq(init_attr))
+ mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &qp->db);
} else {
kfree(qp->sq.wrid);
kfree(qp->rq.wrid);
@@ -610,7 +619,7 @@ err_buf:
mlx4_buf_free(dev->dev, qp->buf_size, &qp->buf);
err_db:
- if (!pd->uobject && !init_attr->srq)
+ if (!pd->uobject && qp_has_rq(init_attr))
mlx4_db_free(dev->dev, &qp->db);
err:
@@ -671,6 +680,33 @@ static void del_gid_entries(struct mlx4_ib_qp *qp)
}
}
+static struct mlx4_ib_pd *get_pd(struct mlx4_ib_qp *qp)
+{
+ if (qp->ibqp.qp_type == IB_QPT_XRC_TGT)
+ return to_mpd(to_mxrcd(qp->ibqp.xrcd)->pd);
+ else
+ return to_mpd(qp->ibqp.pd);
+}
+
+static void get_cqs(struct mlx4_ib_qp *qp,
+ struct mlx4_ib_cq **send_cq, struct mlx4_ib_cq **recv_cq)
+{
+ switch (qp->ibqp.qp_type) {
+ case IB_QPT_XRC_TGT:
+ *send_cq = to_mcq(to_mxrcd(qp->ibqp.xrcd)->cq);
+ *recv_cq = *send_cq;
+ break;
+ case IB_QPT_XRC_INI:
+ *send_cq = to_mcq(qp->ibqp.send_cq);
+ *recv_cq = *send_cq;
+ break;
+ default:
+ *send_cq = to_mcq(qp->ibqp.send_cq);
+ *recv_cq = to_mcq(qp->ibqp.recv_cq);
+ break;
+ }
+}
+
static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
int is_user)
{
@@ -682,8 +718,7 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
printk(KERN_WARNING "mlx4_ib: modify QP %06x to RESET failed.\n",
qp->mqp.qpn);
- send_cq = to_mcq(qp->ibqp.send_cq);
- recv_cq = to_mcq(qp->ibqp.recv_cq);
+ get_cqs(qp, &send_cq, &recv_cq);
mlx4_ib_lock_cqs(send_cq, recv_cq);
@@ -706,7 +741,7 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
mlx4_mtt_cleanup(dev->dev, &qp->mtt);
if (is_user) {
- if (!qp->ibqp.srq)
+ if (qp->rq.wqe_cnt)
mlx4_ib_db_unmap_user(to_mucontext(qp->ibqp.uobject->context),
&qp->db);
ib_umem_release(qp->umem);
@@ -714,7 +749,7 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
kfree(qp->sq.wrid);
kfree(qp->rq.wrid);
mlx4_buf_free(dev->dev, qp->buf_size, &qp->buf);
- if (!qp->ibqp.srq)
+ if (qp->rq.wqe_cnt)
mlx4_db_free(dev->dev, &qp->db);
}
@@ -725,10 +760,10 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
struct ib_qp_init_attr *init_attr,
struct ib_udata *udata)
{
- struct mlx4_ib_dev *dev = to_mdev(pd->device);
struct mlx4_ib_sqp *sqp;
struct mlx4_ib_qp *qp;
int err;
+ u16 xrcdn = 0;
/*
* We only support LSO and multicast loopback blocking, and
@@ -739,10 +774,20 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
return ERR_PTR(-EINVAL);
if (init_attr->create_flags &&
- (pd->uobject || init_attr->qp_type != IB_QPT_UD))
+ (udata || init_attr->qp_type != IB_QPT_UD))
return ERR_PTR(-EINVAL);
switch (init_attr->qp_type) {
+ case IB_QPT_XRC_TGT:
+ pd = to_mxrcd(init_attr->xrcd)->pd;
+ xrcdn = to_mxrcd(init_attr->xrcd)->xrcdn;
+ init_attr->send_cq = to_mxrcd(init_attr->xrcd)->cq;
+ /* fall through */
+ case IB_QPT_XRC_INI:
+ if (!(to_mdev(pd->device)->dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC))
+ return ERR_PTR(-ENOSYS);
+ init_attr->recv_cq = init_attr->send_cq;
+ /* fall through */
case IB_QPT_RC:
case IB_QPT_UC:
case IB_QPT_UD:
@@ -751,13 +796,14 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
if (!qp)
return ERR_PTR(-ENOMEM);
- err = create_qp_common(dev, pd, init_attr, udata, 0, qp);
+ err = create_qp_common(to_mdev(pd->device), pd, init_attr, udata, 0, qp);
if (err) {
kfree(qp);
return ERR_PTR(err);
}
qp->ibqp.qp_num = qp->mqp.qpn;
+ qp->xrcdn = xrcdn;
break;
}
@@ -765,7 +811,7 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
case IB_QPT_GSI:
{
/* Userspace is not allowed to create special QPs: */
- if (pd->uobject)
+ if (udata)
return ERR_PTR(-EINVAL);
sqp = kzalloc(sizeof *sqp, GFP_KERNEL);
@@ -774,8 +820,8 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
qp = &sqp->qp;
- err = create_qp_common(dev, pd, init_attr, udata,
- dev->dev->caps.sqp_start +
+ err = create_qp_common(to_mdev(pd->device), pd, init_attr, udata,
+ to_mdev(pd->device)->dev->caps.sqp_start +
(init_attr->qp_type == IB_QPT_SMI ? 0 : 2) +
init_attr->port_num - 1,
qp);
@@ -801,11 +847,13 @@ int mlx4_ib_destroy_qp(struct ib_qp *qp)
{
struct mlx4_ib_dev *dev = to_mdev(qp->device);
struct mlx4_ib_qp *mqp = to_mqp(qp);
+ struct mlx4_ib_pd *pd;
if (is_qp0(dev, mqp))
mlx4_CLOSE_PORT(dev->dev, mqp->port);
- destroy_qp_common(dev, mqp, !!qp->pd->uobject);
+ pd = get_pd(mqp);
+ destroy_qp_common(dev, mqp, !!pd->ibpd.uobject);
if (is_sqp(dev, mqp))
kfree(to_msqp(mqp));
@@ -821,6 +869,8 @@ static int to_mlx4_st(enum ib_qp_type type)
case IB_QPT_RC: return MLX4_QP_ST_RC;
case IB_QPT_UC: return MLX4_QP_ST_UC;
case IB_QPT_UD: return MLX4_QP_ST_UD;
+ case IB_QPT_XRC_INI:
+ case IB_QPT_XRC_TGT: return MLX4_QP_ST_XRC;
case IB_QPT_SMI:
case IB_QPT_GSI: return MLX4_QP_ST_MLX;
default: return -1;
@@ -893,7 +943,6 @@ static int mlx4_set_path(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah,
--path->static_rate;
} else
path->static_rate = 0;
- path->counter_index = 0xff;
if (ah->ah_flags & IB_AH_GRH) {
if (ah->grh.sgid_index >= dev->dev->caps.gid_table_len[port]) {
@@ -960,6 +1009,8 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
{
struct mlx4_ib_dev *dev = to_mdev(ibqp->device);
struct mlx4_ib_qp *qp = to_mqp(ibqp);
+ struct mlx4_ib_pd *pd;
+ struct mlx4_ib_cq *send_cq, *recv_cq;
struct mlx4_qp_context *context;
enum mlx4_qp_optpar optpar = 0;
int sqd_event;
@@ -1015,8 +1066,10 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
context->sq_size_stride = ilog2(qp->sq.wqe_cnt) << 3;
context->sq_size_stride |= qp->sq.wqe_shift - 4;
- if (cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT)
+ if (cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT) {
context->sq_size_stride |= !!qp->sq_no_prefetch << 7;
+ context->xrcd = cpu_to_be32((u32) qp->xrcdn);
+ }
if (qp->ibqp.uobject)
context->usr_page = cpu_to_be32(to_mucontext(ibqp->uobject->context)->uar.index);
@@ -1034,6 +1087,15 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
}
}
+ if (cur_state == IB_QPS_INIT && new_state == IB_QPS_RTR) {
+ if (dev->counters[qp->port - 1] != -1) {
+ context->pri_path.counter_index =
+ dev->counters[qp->port - 1];
+ optpar |= MLX4_QP_OPTPAR_COUNTER_INDEX;
+ } else
+ context->pri_path.counter_index = 0xff;
+ }
+
if (attr_mask & IB_QP_PKEY_INDEX) {
context->pri_path.pkey_index = attr->pkey_index;
optpar |= MLX4_QP_OPTPAR_PKEY_INDEX;
@@ -1071,8 +1133,12 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
optpar |= MLX4_QP_OPTPAR_ALT_ADDR_PATH;
}
- context->pd = cpu_to_be32(to_mpd(ibqp->pd)->pdn);
- context->params1 = cpu_to_be32(MLX4_IB_ACK_REQ_FREQ << 28);
+ pd = get_pd(qp);
+ get_cqs(qp, &send_cq, &recv_cq);
+ context->pd = cpu_to_be32(pd->pdn);
+ context->cqn_send = cpu_to_be32(send_cq->mcq.cqn);
+ context->cqn_recv = cpu_to_be32(recv_cq->mcq.cqn);
+ context->params1 = cpu_to_be32(MLX4_IB_ACK_REQ_FREQ << 28);
/* Set "fast registration enabled" for all kernel QPs */
if (!qp->ibqp.uobject)
@@ -1098,8 +1164,6 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
if (attr_mask & IB_QP_SQ_PSN)
context->next_send_psn = cpu_to_be32(attr->sq_psn);
- context->cqn_send = cpu_to_be32(to_mcq(ibqp->send_cq)->mcq.cqn);
-
if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) {
if (attr->max_dest_rd_atomic)
context->params2 |=
@@ -1122,8 +1186,6 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
if (attr_mask & IB_QP_RQ_PSN)
context->rnr_nextrecvpsn |= cpu_to_be32(attr->rq_psn);
- context->cqn_recv = cpu_to_be32(to_mcq(ibqp->recv_cq)->mcq.cqn);
-
if (attr_mask & IB_QP_QKEY) {
context->qkey = cpu_to_be32(attr->qkey);
optpar |= MLX4_QP_OPTPAR_Q_KEY;
@@ -1132,7 +1194,7 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
if (ibqp->srq)
context->srqn = cpu_to_be32(1 << 24 | to_msrq(ibqp->srq)->msrq.srqn);
- if (!ibqp->srq && cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT)
+ if (qp->rq.wqe_cnt && cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT)
context->db_rec_addr = cpu_to_be64(qp->db.dma);
if (cur_state == IB_QPS_INIT &&
@@ -1217,17 +1279,17 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
* entries and reinitialize the QP.
*/
if (new_state == IB_QPS_RESET && !ibqp->uobject) {
- mlx4_ib_cq_clean(to_mcq(ibqp->recv_cq), qp->mqp.qpn,
+ mlx4_ib_cq_clean(recv_cq, qp->mqp.qpn,
ibqp->srq ? to_msrq(ibqp->srq): NULL);
- if (ibqp->send_cq != ibqp->recv_cq)
- mlx4_ib_cq_clean(to_mcq(ibqp->send_cq), qp->mqp.qpn, NULL);
+ if (send_cq != recv_cq)
+ mlx4_ib_cq_clean(send_cq, qp->mqp.qpn, NULL);
qp->rq.head = 0;
qp->rq.tail = 0;
qp->sq.head = 0;
qp->sq.tail = 0;
qp->sq_next_wqe = 0;
- if (!ibqp->srq)
+ if (qp->rq.wqe_cnt)
*qp->db.db = 0;
}
@@ -1539,14 +1601,13 @@ static void set_masked_atomic_seg(struct mlx4_wqe_masked_atomic_seg *aseg,
}
static void set_datagram_seg(struct mlx4_wqe_datagram_seg *dseg,
- struct ib_send_wr *wr, __be16 *vlan)
+ struct ib_send_wr *wr)
{
memcpy(dseg->av, &to_mah(wr->wr.ud.ah)->av, sizeof (struct mlx4_av));
dseg->dqpn = cpu_to_be32(wr->wr.ud.remote_qpn);
dseg->qkey = cpu_to_be32(wr->wr.ud.remote_qkey);
dseg->vlan = to_mah(wr->wr.ud.ah)->av.eth.vlan;
memcpy(dseg->mac, to_mah(wr->wr.ud.ah)->av.eth.mac, 6);
- *vlan = dseg->vlan;
}
static void set_mlx_icrc_seg(void *dseg)
@@ -1649,7 +1710,6 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
__be32 uninitialized_var(lso_hdr_sz);
__be32 blh;
int i;
- __be16 vlan = cpu_to_be16(0xffff);
spin_lock_irqsave(&qp->sq.lock, flags);
@@ -1753,7 +1813,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
break;
case IB_QPT_UD:
- set_datagram_seg(wqe, wr, &vlan);
+ set_datagram_seg(wqe, wr);
wqe += sizeof (struct mlx4_wqe_datagram_seg);
size += sizeof (struct mlx4_wqe_datagram_seg) / 16;
@@ -1816,11 +1876,6 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
ctrl->fence_size = (wr->send_flags & IB_SEND_FENCE ?
MLX4_WQE_CTRL_FENCE : 0) | size;
- if (be16_to_cpu(vlan) < 0x1000) {
- ctrl->ins_vlan = 1 << 6;
- ctrl->vlan_tag = vlan;
- }
-
/*
* Make sure descriptor is fully written before
* setting ownership bit (because HW can start
diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c
index 818b7ecace5e..39542f3703b8 100644
--- a/drivers/infiniband/hw/mlx4/srq.c
+++ b/drivers/infiniband/hw/mlx4/srq.c
@@ -76,6 +76,8 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd,
struct mlx4_ib_srq *srq;
struct mlx4_wqe_srq_next_seg *next;
struct mlx4_wqe_data_seg *scatter;
+ u32 cqn;
+ u16 xrcdn;
int desc_size;
int buf_size;
int err;
@@ -174,12 +176,18 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd,
}
}
- err = mlx4_srq_alloc(dev->dev, to_mpd(pd)->pdn, &srq->mtt,
+ cqn = (init_attr->srq_type == IB_SRQT_XRC) ?
+ to_mcq(init_attr->ext.xrc.cq)->mcq.cqn : 0;
+ xrcdn = (init_attr->srq_type == IB_SRQT_XRC) ?
+ to_mxrcd(init_attr->ext.xrc.xrcd)->xrcdn :
+ (u16) dev->dev->caps.reserved_xrcds;
+ err = mlx4_srq_alloc(dev->dev, to_mpd(pd)->pdn, cqn, xrcdn, &srq->mtt,
srq->db.dma, &srq->msrq);
if (err)
goto err_wrid;
srq->msrq.event = mlx4_ib_srq_event;
+ srq->ibsrq.ext.xrc.srq_num = srq->msrq.srqn;
if (pd->uobject)
if (ib_copy_to_udata(udata, &srq->msrq.srqn, sizeof (__u32))) {
diff --git a/drivers/infiniband/hw/mthca/mthca_catas.c b/drivers/infiniband/hw/mthca/mthca_catas.c
index e4a08c2819e4..712d2a30fbe5 100644
--- a/drivers/infiniband/hw/mthca/mthca_catas.c
+++ b/drivers/infiniband/hw/mthca/mthca_catas.c
@@ -31,6 +31,7 @@
*/
#include <linux/jiffies.h>
+#include <linux/module.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.c b/drivers/infiniband/hw/mthca/mthca_cmd.c
index 7bfa2a164955..9d3e5c1ac60e 100644
--- a/drivers/infiniband/hw/mthca/mthca_cmd.c
+++ b/drivers/infiniband/hw/mthca/mthca_cmd.c
@@ -36,6 +36,7 @@
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/sched.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <rdma/ib_mad.h>
@@ -301,6 +302,38 @@ static int mthca_cmd_post(struct mthca_dev *dev,
return err;
}
+
+static int mthca_status_to_errno(u8 status)
+{
+ static const int trans_table[] = {
+ [MTHCA_CMD_STAT_INTERNAL_ERR] = -EIO,
+ [MTHCA_CMD_STAT_BAD_OP] = -EPERM,
+ [MTHCA_CMD_STAT_BAD_PARAM] = -EINVAL,
+ [MTHCA_CMD_STAT_BAD_SYS_STATE] = -ENXIO,
+ [MTHCA_CMD_STAT_BAD_RESOURCE] = -EBADF,
+ [MTHCA_CMD_STAT_RESOURCE_BUSY] = -EBUSY,
+ [MTHCA_CMD_STAT_DDR_MEM_ERR] = -ENOMEM,
+ [MTHCA_CMD_STAT_EXCEED_LIM] = -ENOMEM,
+ [MTHCA_CMD_STAT_BAD_RES_STATE] = -EBADF,
+ [MTHCA_CMD_STAT_BAD_INDEX] = -EBADF,
+ [MTHCA_CMD_STAT_BAD_NVMEM] = -EFAULT,
+ [MTHCA_CMD_STAT_BAD_QPEE_STATE] = -EINVAL,
+ [MTHCA_CMD_STAT_BAD_SEG_PARAM] = -EFAULT,
+ [MTHCA_CMD_STAT_REG_BOUND] = -EBUSY,
+ [MTHCA_CMD_STAT_LAM_NOT_PRE] = -EAGAIN,
+ [MTHCA_CMD_STAT_BAD_PKT] = -EBADMSG,
+ [MTHCA_CMD_STAT_BAD_SIZE] = -ENOMEM,
+ };
+
+ if (status >= ARRAY_SIZE(trans_table) ||
+ (status != MTHCA_CMD_STAT_OK
+ && trans_table[status] == 0))
+ return -EINVAL;
+
+ return trans_table[status];
+}
+
+
static int mthca_cmd_poll(struct mthca_dev *dev,
u64 in_param,
u64 *out_param,
@@ -308,11 +341,11 @@ static int mthca_cmd_poll(struct mthca_dev *dev,
u32 in_modifier,
u8 op_modifier,
u16 op,
- unsigned long timeout,
- u8 *status)
+ unsigned long timeout)
{
int err = 0;
unsigned long end;
+ u8 status;
down(&dev->cmd.poll_sem);
@@ -341,7 +374,12 @@ static int mthca_cmd_poll(struct mthca_dev *dev,
(u64) be32_to_cpu((__force __be32)
__raw_readl(dev->hcr + HCR_OUT_PARAM_OFFSET + 4));
- *status = be32_to_cpu((__force __be32) __raw_readl(dev->hcr + HCR_STATUS_OFFSET)) >> 24;
+ status = be32_to_cpu((__force __be32) __raw_readl(dev->hcr + HCR_STATUS_OFFSET)) >> 24;
+ if (status) {
+ mthca_dbg(dev, "Command %02x completed with status %02x\n",
+ op, status);
+ err = mthca_status_to_errno(status);
+ }
out:
up(&dev->cmd.poll_sem);
@@ -374,8 +412,7 @@ static int mthca_cmd_wait(struct mthca_dev *dev,
u32 in_modifier,
u8 op_modifier,
u16 op,
- unsigned long timeout,
- u8 *status)
+ unsigned long timeout)
{
int err = 0;
struct mthca_cmd_context *context;
@@ -407,10 +444,11 @@ static int mthca_cmd_wait(struct mthca_dev *dev,
if (err)
goto out;
- *status = context->status;
- if (*status)
+ if (context->status) {
mthca_dbg(dev, "Command %02x completed with status %02x\n",
- op, *status);
+ op, context->status);
+ err = mthca_status_to_errno(context->status);
+ }
if (out_is_imm)
*out_param = context->out_param;
@@ -432,17 +470,16 @@ static int mthca_cmd_box(struct mthca_dev *dev,
u32 in_modifier,
u8 op_modifier,
u16 op,
- unsigned long timeout,
- u8 *status)
+ unsigned long timeout)
{
if (dev->cmd.flags & MTHCA_CMD_USE_EVENTS)
return mthca_cmd_wait(dev, in_param, &out_param, 0,
in_modifier, op_modifier, op,
- timeout, status);
+ timeout);
else
return mthca_cmd_poll(dev, in_param, &out_param, 0,
in_modifier, op_modifier, op,
- timeout, status);
+ timeout);
}
/* Invoke a command with no output parameter */
@@ -451,11 +488,10 @@ static int mthca_cmd(struct mthca_dev *dev,
u32 in_modifier,
u8 op_modifier,
u16 op,
- unsigned long timeout,
- u8 *status)
+ unsigned long timeout)
{
return mthca_cmd_box(dev, in_param, 0, in_modifier,
- op_modifier, op, timeout, status);
+ op_modifier, op, timeout);
}
/*
@@ -469,17 +505,16 @@ static int mthca_cmd_imm(struct mthca_dev *dev,
u32 in_modifier,
u8 op_modifier,
u16 op,
- unsigned long timeout,
- u8 *status)
+ unsigned long timeout)
{
if (dev->cmd.flags & MTHCA_CMD_USE_EVENTS)
return mthca_cmd_wait(dev, in_param, out_param, 1,
in_modifier, op_modifier, op,
- timeout, status);
+ timeout);
else
return mthca_cmd_poll(dev, in_param, out_param, 1,
in_modifier, op_modifier, op,
- timeout, status);
+ timeout);
}
int mthca_cmd_init(struct mthca_dev *dev)
@@ -596,14 +631,14 @@ void mthca_free_mailbox(struct mthca_dev *dev, struct mthca_mailbox *mailbox)
kfree(mailbox);
}
-int mthca_SYS_EN(struct mthca_dev *dev, u8 *status)
+int mthca_SYS_EN(struct mthca_dev *dev)
{
u64 out;
int ret;
- ret = mthca_cmd_imm(dev, 0, &out, 0, 0, CMD_SYS_EN, CMD_TIME_CLASS_D, status);
+ ret = mthca_cmd_imm(dev, 0, &out, 0, 0, CMD_SYS_EN, CMD_TIME_CLASS_D);
- if (*status == MTHCA_CMD_STAT_DDR_MEM_ERR)
+ if (ret == -ENOMEM)
mthca_warn(dev, "SYS_EN DDR error: syn=%x, sock=%d, "
"sladdr=%d, SPD source=%s\n",
(int) (out >> 6) & 0xf, (int) (out >> 4) & 3,
@@ -612,13 +647,13 @@ int mthca_SYS_EN(struct mthca_dev *dev, u8 *status)
return ret;
}
-int mthca_SYS_DIS(struct mthca_dev *dev, u8 *status)
+int mthca_SYS_DIS(struct mthca_dev *dev)
{
- return mthca_cmd(dev, 0, 0, 0, CMD_SYS_DIS, CMD_TIME_CLASS_C, status);
+ return mthca_cmd(dev, 0, 0, 0, CMD_SYS_DIS, CMD_TIME_CLASS_C);
}
static int mthca_map_cmd(struct mthca_dev *dev, u16 op, struct mthca_icm *icm,
- u64 virt, u8 *status)
+ u64 virt)
{
struct mthca_mailbox *mailbox;
struct mthca_icm_iter iter;
@@ -666,8 +701,8 @@ static int mthca_map_cmd(struct mthca_dev *dev, u16 op, struct mthca_icm *icm,
if (++nent == MTHCA_MAILBOX_SIZE / 16) {
err = mthca_cmd(dev, mailbox->dma, nent, 0, op,
- CMD_TIME_CLASS_B, status);
- if (err || *status)
+ CMD_TIME_CLASS_B);
+ if (err)
goto out;
nent = 0;
}
@@ -676,7 +711,7 @@ static int mthca_map_cmd(struct mthca_dev *dev, u16 op, struct mthca_icm *icm,
if (nent)
err = mthca_cmd(dev, mailbox->dma, nent, 0, op,
- CMD_TIME_CLASS_B, status);
+ CMD_TIME_CLASS_B);
switch (op) {
case CMD_MAP_FA:
@@ -696,19 +731,19 @@ out:
return err;
}
-int mthca_MAP_FA(struct mthca_dev *dev, struct mthca_icm *icm, u8 *status)
+int mthca_MAP_FA(struct mthca_dev *dev, struct mthca_icm *icm)
{
- return mthca_map_cmd(dev, CMD_MAP_FA, icm, -1, status);
+ return mthca_map_cmd(dev, CMD_MAP_FA, icm, -1);
}
-int mthca_UNMAP_FA(struct mthca_dev *dev, u8 *status)
+int mthca_UNMAP_FA(struct mthca_dev *dev)
{
- return mthca_cmd(dev, 0, 0, 0, CMD_UNMAP_FA, CMD_TIME_CLASS_B, status);
+ return mthca_cmd(dev, 0, 0, 0, CMD_UNMAP_FA, CMD_TIME_CLASS_B);
}
-int mthca_RUN_FW(struct mthca_dev *dev, u8 *status)
+int mthca_RUN_FW(struct mthca_dev *dev)
{
- return mthca_cmd(dev, 0, 0, 0, CMD_RUN_FW, CMD_TIME_CLASS_A, status);
+ return mthca_cmd(dev, 0, 0, 0, CMD_RUN_FW, CMD_TIME_CLASS_A);
}
static void mthca_setup_cmd_doorbells(struct mthca_dev *dev, u64 base)
@@ -737,7 +772,7 @@ static void mthca_setup_cmd_doorbells(struct mthca_dev *dev, u64 base)
mthca_dbg(dev, "Mapped doorbell page for posting FW commands\n");
}
-int mthca_QUERY_FW(struct mthca_dev *dev, u8 *status)
+int mthca_QUERY_FW(struct mthca_dev *dev)
{
struct mthca_mailbox *mailbox;
u32 *outbox;
@@ -771,7 +806,7 @@ int mthca_QUERY_FW(struct mthca_dev *dev, u8 *status)
outbox = mailbox->buf;
err = mthca_cmd_box(dev, 0, mailbox->dma, 0, 0, CMD_QUERY_FW,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
if (err)
goto out;
@@ -843,7 +878,7 @@ out:
return err;
}
-int mthca_ENABLE_LAM(struct mthca_dev *dev, u8 *status)
+int mthca_ENABLE_LAM(struct mthca_dev *dev)
{
struct mthca_mailbox *mailbox;
u8 info;
@@ -864,14 +899,11 @@ int mthca_ENABLE_LAM(struct mthca_dev *dev, u8 *status)
outbox = mailbox->buf;
err = mthca_cmd_box(dev, 0, mailbox->dma, 0, 0, CMD_ENABLE_LAM,
- CMD_TIME_CLASS_C, status);
+ CMD_TIME_CLASS_C);
if (err)
goto out;
- if (*status == MTHCA_CMD_STAT_LAM_NOT_PRE)
- goto out;
-
MTHCA_GET(dev->ddr_start, outbox, ENABLE_LAM_START_OFFSET);
MTHCA_GET(dev->ddr_end, outbox, ENABLE_LAM_END_OFFSET);
MTHCA_GET(info, outbox, ENABLE_LAM_INFO_OFFSET);
@@ -896,12 +928,12 @@ out:
return err;
}
-int mthca_DISABLE_LAM(struct mthca_dev *dev, u8 *status)
+int mthca_DISABLE_LAM(struct mthca_dev *dev)
{
- return mthca_cmd(dev, 0, 0, 0, CMD_SYS_DIS, CMD_TIME_CLASS_C, status);
+ return mthca_cmd(dev, 0, 0, 0, CMD_SYS_DIS, CMD_TIME_CLASS_C);
}
-int mthca_QUERY_DDR(struct mthca_dev *dev, u8 *status)
+int mthca_QUERY_DDR(struct mthca_dev *dev)
{
struct mthca_mailbox *mailbox;
u8 info;
@@ -922,7 +954,7 @@ int mthca_QUERY_DDR(struct mthca_dev *dev, u8 *status)
outbox = mailbox->buf;
err = mthca_cmd_box(dev, 0, mailbox->dma, 0, 0, CMD_QUERY_DDR,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
if (err)
goto out;
@@ -952,7 +984,7 @@ out:
}
int mthca_QUERY_DEV_LIM(struct mthca_dev *dev,
- struct mthca_dev_lim *dev_lim, u8 *status)
+ struct mthca_dev_lim *dev_lim)
{
struct mthca_mailbox *mailbox;
u32 *outbox;
@@ -1028,7 +1060,7 @@ int mthca_QUERY_DEV_LIM(struct mthca_dev *dev,
outbox = mailbox->buf;
err = mthca_cmd_box(dev, 0, mailbox->dma, 0, 0, CMD_QUERY_DEV_LIM,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
if (err)
goto out;
@@ -1232,7 +1264,7 @@ static void get_board_id(void *vsd, char *board_id)
}
int mthca_QUERY_ADAPTER(struct mthca_dev *dev,
- struct mthca_adapter *adapter, u8 *status)
+ struct mthca_adapter *adapter)
{
struct mthca_mailbox *mailbox;
u32 *outbox;
@@ -1251,7 +1283,7 @@ int mthca_QUERY_ADAPTER(struct mthca_dev *dev,
outbox = mailbox->buf;
err = mthca_cmd_box(dev, 0, mailbox->dma, 0, 0, CMD_QUERY_ADAPTER,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
if (err)
goto out;
@@ -1275,8 +1307,7 @@ out:
}
int mthca_INIT_HCA(struct mthca_dev *dev,
- struct mthca_init_hca_param *param,
- u8 *status)
+ struct mthca_init_hca_param *param)
{
struct mthca_mailbox *mailbox;
__be32 *inbox;
@@ -1393,7 +1424,8 @@ int mthca_INIT_HCA(struct mthca_dev *dev,
MTHCA_PUT(inbox, param->uarc_base, INIT_HCA_UAR_CTX_BASE_OFFSET);
}
- err = mthca_cmd(dev, mailbox->dma, 0, 0, CMD_INIT_HCA, CMD_TIME_CLASS_D, status);
+ err = mthca_cmd(dev, mailbox->dma, 0, 0,
+ CMD_INIT_HCA, CMD_TIME_CLASS_D);
mthca_free_mailbox(dev, mailbox);
return err;
@@ -1401,7 +1433,7 @@ int mthca_INIT_HCA(struct mthca_dev *dev,
int mthca_INIT_IB(struct mthca_dev *dev,
struct mthca_init_ib_param *param,
- int port, u8 *status)
+ int port)
{
struct mthca_mailbox *mailbox;
u32 *inbox;
@@ -1445,24 +1477,24 @@ int mthca_INIT_IB(struct mthca_dev *dev,
MTHCA_PUT(inbox, param->si_guid, INIT_IB_SI_GUID_OFFSET);
err = mthca_cmd(dev, mailbox->dma, port, 0, CMD_INIT_IB,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
mthca_free_mailbox(dev, mailbox);
return err;
}
-int mthca_CLOSE_IB(struct mthca_dev *dev, int port, u8 *status)
+int mthca_CLOSE_IB(struct mthca_dev *dev, int port)
{
- return mthca_cmd(dev, 0, port, 0, CMD_CLOSE_IB, CMD_TIME_CLASS_A, status);
+ return mthca_cmd(dev, 0, port, 0, CMD_CLOSE_IB, CMD_TIME_CLASS_A);
}
-int mthca_CLOSE_HCA(struct mthca_dev *dev, int panic, u8 *status)
+int mthca_CLOSE_HCA(struct mthca_dev *dev, int panic)
{
- return mthca_cmd(dev, 0, 0, panic, CMD_CLOSE_HCA, CMD_TIME_CLASS_C, status);
+ return mthca_cmd(dev, 0, 0, panic, CMD_CLOSE_HCA, CMD_TIME_CLASS_C);
}
int mthca_SET_IB(struct mthca_dev *dev, struct mthca_set_ib_param *param,
- int port, u8 *status)
+ int port)
{
struct mthca_mailbox *mailbox;
u32 *inbox;
@@ -1491,18 +1523,18 @@ int mthca_SET_IB(struct mthca_dev *dev, struct mthca_set_ib_param *param,
MTHCA_PUT(inbox, param->si_guid, SET_IB_SI_GUID_OFFSET);
err = mthca_cmd(dev, mailbox->dma, port, 0, CMD_SET_IB,
- CMD_TIME_CLASS_B, status);
+ CMD_TIME_CLASS_B);
mthca_free_mailbox(dev, mailbox);
return err;
}
-int mthca_MAP_ICM(struct mthca_dev *dev, struct mthca_icm *icm, u64 virt, u8 *status)
+int mthca_MAP_ICM(struct mthca_dev *dev, struct mthca_icm *icm, u64 virt)
{
- return mthca_map_cmd(dev, CMD_MAP_ICM, icm, virt, status);
+ return mthca_map_cmd(dev, CMD_MAP_ICM, icm, virt);
}
-int mthca_MAP_ICM_page(struct mthca_dev *dev, u64 dma_addr, u64 virt, u8 *status)
+int mthca_MAP_ICM_page(struct mthca_dev *dev, u64 dma_addr, u64 virt)
{
struct mthca_mailbox *mailbox;
__be64 *inbox;
@@ -1517,7 +1549,7 @@ int mthca_MAP_ICM_page(struct mthca_dev *dev, u64 dma_addr, u64 virt, u8 *status
inbox[1] = cpu_to_be64(dma_addr);
err = mthca_cmd(dev, mailbox->dma, 1, 0, CMD_MAP_ICM,
- CMD_TIME_CLASS_B, status);
+ CMD_TIME_CLASS_B);
mthca_free_mailbox(dev, mailbox);
@@ -1528,31 +1560,31 @@ int mthca_MAP_ICM_page(struct mthca_dev *dev, u64 dma_addr, u64 virt, u8 *status
return err;
}
-int mthca_UNMAP_ICM(struct mthca_dev *dev, u64 virt, u32 page_count, u8 *status)
+int mthca_UNMAP_ICM(struct mthca_dev *dev, u64 virt, u32 page_count)
{
mthca_dbg(dev, "Unmapping %d pages at %llx from ICM.\n",
page_count, (unsigned long long) virt);
- return mthca_cmd(dev, virt, page_count, 0, CMD_UNMAP_ICM, CMD_TIME_CLASS_B, status);
+ return mthca_cmd(dev, virt, page_count, 0,
+ CMD_UNMAP_ICM, CMD_TIME_CLASS_B);
}
-int mthca_MAP_ICM_AUX(struct mthca_dev *dev, struct mthca_icm *icm, u8 *status)
+int mthca_MAP_ICM_AUX(struct mthca_dev *dev, struct mthca_icm *icm)
{
- return mthca_map_cmd(dev, CMD_MAP_ICM_AUX, icm, -1, status);
+ return mthca_map_cmd(dev, CMD_MAP_ICM_AUX, icm, -1);
}
-int mthca_UNMAP_ICM_AUX(struct mthca_dev *dev, u8 *status)
+int mthca_UNMAP_ICM_AUX(struct mthca_dev *dev)
{
- return mthca_cmd(dev, 0, 0, 0, CMD_UNMAP_ICM_AUX, CMD_TIME_CLASS_B, status);
+ return mthca_cmd(dev, 0, 0, 0, CMD_UNMAP_ICM_AUX, CMD_TIME_CLASS_B);
}
-int mthca_SET_ICM_SIZE(struct mthca_dev *dev, u64 icm_size, u64 *aux_pages,
- u8 *status)
+int mthca_SET_ICM_SIZE(struct mthca_dev *dev, u64 icm_size, u64 *aux_pages)
{
- int ret = mthca_cmd_imm(dev, icm_size, aux_pages, 0, 0, CMD_SET_ICM_SIZE,
- CMD_TIME_CLASS_A, status);
+ int ret = mthca_cmd_imm(dev, icm_size, aux_pages, 0,
+ 0, CMD_SET_ICM_SIZE, CMD_TIME_CLASS_A);
- if (ret || status)
+ if (ret)
return ret;
/*
@@ -1566,74 +1598,73 @@ int mthca_SET_ICM_SIZE(struct mthca_dev *dev, u64 icm_size, u64 *aux_pages,
}
int mthca_SW2HW_MPT(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int mpt_index, u8 *status)
+ int mpt_index)
{
return mthca_cmd(dev, mailbox->dma, mpt_index, 0, CMD_SW2HW_MPT,
- CMD_TIME_CLASS_B, status);
+ CMD_TIME_CLASS_B);
}
int mthca_HW2SW_MPT(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int mpt_index, u8 *status)
+ int mpt_index)
{
return mthca_cmd_box(dev, 0, mailbox ? mailbox->dma : 0, mpt_index,
!mailbox, CMD_HW2SW_MPT,
- CMD_TIME_CLASS_B, status);
+ CMD_TIME_CLASS_B);
}
int mthca_WRITE_MTT(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int num_mtt, u8 *status)
+ int num_mtt)
{
return mthca_cmd(dev, mailbox->dma, num_mtt, 0, CMD_WRITE_MTT,
- CMD_TIME_CLASS_B, status);
+ CMD_TIME_CLASS_B);
}
-int mthca_SYNC_TPT(struct mthca_dev *dev, u8 *status)
+int mthca_SYNC_TPT(struct mthca_dev *dev)
{
- return mthca_cmd(dev, 0, 0, 0, CMD_SYNC_TPT, CMD_TIME_CLASS_B, status);
+ return mthca_cmd(dev, 0, 0, 0, CMD_SYNC_TPT, CMD_TIME_CLASS_B);
}
int mthca_MAP_EQ(struct mthca_dev *dev, u64 event_mask, int unmap,
- int eq_num, u8 *status)
+ int eq_num)
{
mthca_dbg(dev, "%s mask %016llx for eqn %d\n",
unmap ? "Clearing" : "Setting",
(unsigned long long) event_mask, eq_num);
return mthca_cmd(dev, event_mask, (unmap << 31) | eq_num,
- 0, CMD_MAP_EQ, CMD_TIME_CLASS_B, status);
+ 0, CMD_MAP_EQ, CMD_TIME_CLASS_B);
}
int mthca_SW2HW_EQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int eq_num, u8 *status)
+ int eq_num)
{
return mthca_cmd(dev, mailbox->dma, eq_num, 0, CMD_SW2HW_EQ,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
}
int mthca_HW2SW_EQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int eq_num, u8 *status)
+ int eq_num)
{
return mthca_cmd_box(dev, 0, mailbox->dma, eq_num, 0,
CMD_HW2SW_EQ,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
}
int mthca_SW2HW_CQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int cq_num, u8 *status)
+ int cq_num)
{
return mthca_cmd(dev, mailbox->dma, cq_num, 0, CMD_SW2HW_CQ,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
}
int mthca_HW2SW_CQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int cq_num, u8 *status)
+ int cq_num)
{
return mthca_cmd_box(dev, 0, mailbox->dma, cq_num, 0,
CMD_HW2SW_CQ,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
}
-int mthca_RESIZE_CQ(struct mthca_dev *dev, int cq_num, u32 lkey, u8 log_size,
- u8 *status)
+int mthca_RESIZE_CQ(struct mthca_dev *dev, int cq_num, u32 lkey, u8 log_size)
{
struct mthca_mailbox *mailbox;
__be32 *inbox;
@@ -1657,44 +1688,43 @@ int mthca_RESIZE_CQ(struct mthca_dev *dev, int cq_num, u32 lkey, u8 log_size,
MTHCA_PUT(inbox, lkey, RESIZE_CQ_LKEY_OFFSET);
err = mthca_cmd(dev, mailbox->dma, cq_num, 1, CMD_RESIZE_CQ,
- CMD_TIME_CLASS_B, status);
+ CMD_TIME_CLASS_B);
mthca_free_mailbox(dev, mailbox);
return err;
}
int mthca_SW2HW_SRQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int srq_num, u8 *status)
+ int srq_num)
{
return mthca_cmd(dev, mailbox->dma, srq_num, 0, CMD_SW2HW_SRQ,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
}
int mthca_HW2SW_SRQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int srq_num, u8 *status)
+ int srq_num)
{
return mthca_cmd_box(dev, 0, mailbox->dma, srq_num, 0,
CMD_HW2SW_SRQ,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
}
int mthca_QUERY_SRQ(struct mthca_dev *dev, u32 num,
- struct mthca_mailbox *mailbox, u8 *status)
+ struct mthca_mailbox *mailbox)
{
return mthca_cmd_box(dev, 0, mailbox->dma, num, 0,
- CMD_QUERY_SRQ, CMD_TIME_CLASS_A, status);
+ CMD_QUERY_SRQ, CMD_TIME_CLASS_A);
}
-int mthca_ARM_SRQ(struct mthca_dev *dev, int srq_num, int limit, u8 *status)
+int mthca_ARM_SRQ(struct mthca_dev *dev, int srq_num, int limit)
{
return mthca_cmd(dev, limit, srq_num, 0, CMD_ARM_SRQ,
- CMD_TIME_CLASS_B, status);
+ CMD_TIME_CLASS_B);
}
int mthca_MODIFY_QP(struct mthca_dev *dev, enum ib_qp_state cur,
enum ib_qp_state next, u32 num, int is_ee,
- struct mthca_mailbox *mailbox, u32 optmask,
- u8 *status)
+ struct mthca_mailbox *mailbox, u32 optmask)
{
static const u16 op[IB_QPS_ERR + 1][IB_QPS_ERR + 1] = {
[IB_QPS_RESET] = {
@@ -1755,7 +1785,7 @@ int mthca_MODIFY_QP(struct mthca_dev *dev, enum ib_qp_state cur,
err = mthca_cmd_box(dev, 0, mailbox ? mailbox->dma : 0,
(!!is_ee << 24) | num, op_mod,
- op[cur][next], CMD_TIME_CLASS_C, status);
+ op[cur][next], CMD_TIME_CLASS_C);
if (0 && mailbox) {
int i;
@@ -1789,21 +1819,20 @@ int mthca_MODIFY_QP(struct mthca_dev *dev, enum ib_qp_state cur,
}
err = mthca_cmd(dev, mailbox->dma, optmask | (!!is_ee << 24) | num,
- op_mod, op[cur][next], CMD_TIME_CLASS_C, status);
+ op_mod, op[cur][next], CMD_TIME_CLASS_C);
}
return err;
}
int mthca_QUERY_QP(struct mthca_dev *dev, u32 num, int is_ee,
- struct mthca_mailbox *mailbox, u8 *status)
+ struct mthca_mailbox *mailbox)
{
return mthca_cmd_box(dev, 0, mailbox->dma, (!!is_ee << 24) | num, 0,
- CMD_QUERY_QPEE, CMD_TIME_CLASS_A, status);
+ CMD_QUERY_QPEE, CMD_TIME_CLASS_A);
}
-int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn,
- u8 *status)
+int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn)
{
u8 op_mod;
@@ -1825,12 +1854,12 @@ int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn,
}
return mthca_cmd(dev, 0, qpn, op_mod, CMD_CONF_SPECIAL_QP,
- CMD_TIME_CLASS_B, status);
+ CMD_TIME_CLASS_B);
}
int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey,
int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
- void *in_mad, void *response_mad, u8 *status)
+ void *in_mad, void *response_mad)
{
struct mthca_mailbox *inmailbox, *outmailbox;
void *inbox;
@@ -1897,9 +1926,9 @@ int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey,
err = mthca_cmd_box(dev, inmailbox->dma, outmailbox->dma,
in_modifier, op_modifier,
- CMD_MAD_IFC, CMD_TIME_CLASS_C, status);
+ CMD_MAD_IFC, CMD_TIME_CLASS_C);
- if (!err && !*status)
+ if (!err)
memcpy(response_mad, outmailbox->buf, 256);
mthca_free_mailbox(dev, inmailbox);
@@ -1908,33 +1937,33 @@ int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey,
}
int mthca_READ_MGM(struct mthca_dev *dev, int index,
- struct mthca_mailbox *mailbox, u8 *status)
+ struct mthca_mailbox *mailbox)
{
return mthca_cmd_box(dev, 0, mailbox->dma, index, 0,
- CMD_READ_MGM, CMD_TIME_CLASS_A, status);
+ CMD_READ_MGM, CMD_TIME_CLASS_A);
}
int mthca_WRITE_MGM(struct mthca_dev *dev, int index,
- struct mthca_mailbox *mailbox, u8 *status)
+ struct mthca_mailbox *mailbox)
{
return mthca_cmd(dev, mailbox->dma, index, 0, CMD_WRITE_MGM,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
}
int mthca_MGID_HASH(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- u16 *hash, u8 *status)
+ u16 *hash)
{
u64 imm;
int err;
err = mthca_cmd_imm(dev, mailbox->dma, &imm, 0, 0, CMD_MGID_HASH,
- CMD_TIME_CLASS_A, status);
+ CMD_TIME_CLASS_A);
*hash = imm;
return err;
}
-int mthca_NOP(struct mthca_dev *dev, u8 *status)
+int mthca_NOP(struct mthca_dev *dev)
{
- return mthca_cmd(dev, 0, 0x1f, 0, CMD_NOP, msecs_to_jiffies(100), status);
+ return mthca_cmd(dev, 0, 0x1f, 0, CMD_NOP, msecs_to_jiffies(100));
}
diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.h b/drivers/infiniband/hw/mthca/mthca_cmd.h
index 6efd3265f248..f952244c54de 100644
--- a/drivers/infiniband/hw/mthca/mthca_cmd.h
+++ b/drivers/infiniband/hw/mthca/mthca_cmd.h
@@ -252,79 +252,74 @@ struct mthca_mailbox *mthca_alloc_mailbox(struct mthca_dev *dev,
gfp_t gfp_mask);
void mthca_free_mailbox(struct mthca_dev *dev, struct mthca_mailbox *mailbox);
-int mthca_SYS_EN(struct mthca_dev *dev, u8 *status);
-int mthca_SYS_DIS(struct mthca_dev *dev, u8 *status);
-int mthca_MAP_FA(struct mthca_dev *dev, struct mthca_icm *icm, u8 *status);
-int mthca_UNMAP_FA(struct mthca_dev *dev, u8 *status);
-int mthca_RUN_FW(struct mthca_dev *dev, u8 *status);
-int mthca_QUERY_FW(struct mthca_dev *dev, u8 *status);
-int mthca_ENABLE_LAM(struct mthca_dev *dev, u8 *status);
-int mthca_DISABLE_LAM(struct mthca_dev *dev, u8 *status);
-int mthca_QUERY_DDR(struct mthca_dev *dev, u8 *status);
+int mthca_SYS_EN(struct mthca_dev *dev);
+int mthca_SYS_DIS(struct mthca_dev *dev);
+int mthca_MAP_FA(struct mthca_dev *dev, struct mthca_icm *icm);
+int mthca_UNMAP_FA(struct mthca_dev *dev);
+int mthca_RUN_FW(struct mthca_dev *dev);
+int mthca_QUERY_FW(struct mthca_dev *dev);
+int mthca_ENABLE_LAM(struct mthca_dev *dev);
+int mthca_DISABLE_LAM(struct mthca_dev *dev);
+int mthca_QUERY_DDR(struct mthca_dev *dev);
int mthca_QUERY_DEV_LIM(struct mthca_dev *dev,
- struct mthca_dev_lim *dev_lim, u8 *status);
+ struct mthca_dev_lim *dev_lim);
int mthca_QUERY_ADAPTER(struct mthca_dev *dev,
- struct mthca_adapter *adapter, u8 *status);
+ struct mthca_adapter *adapter);
int mthca_INIT_HCA(struct mthca_dev *dev,
- struct mthca_init_hca_param *param,
- u8 *status);
+ struct mthca_init_hca_param *param);
int mthca_INIT_IB(struct mthca_dev *dev,
struct mthca_init_ib_param *param,
- int port, u8 *status);
-int mthca_CLOSE_IB(struct mthca_dev *dev, int port, u8 *status);
-int mthca_CLOSE_HCA(struct mthca_dev *dev, int panic, u8 *status);
+ int port);
+int mthca_CLOSE_IB(struct mthca_dev *dev, int port);
+int mthca_CLOSE_HCA(struct mthca_dev *dev, int panic);
int mthca_SET_IB(struct mthca_dev *dev, struct mthca_set_ib_param *param,
- int port, u8 *status);
-int mthca_MAP_ICM(struct mthca_dev *dev, struct mthca_icm *icm, u64 virt, u8 *status);
-int mthca_MAP_ICM_page(struct mthca_dev *dev, u64 dma_addr, u64 virt, u8 *status);
-int mthca_UNMAP_ICM(struct mthca_dev *dev, u64 virt, u32 page_count, u8 *status);
-int mthca_MAP_ICM_AUX(struct mthca_dev *dev, struct mthca_icm *icm, u8 *status);
-int mthca_UNMAP_ICM_AUX(struct mthca_dev *dev, u8 *status);
-int mthca_SET_ICM_SIZE(struct mthca_dev *dev, u64 icm_size, u64 *aux_pages,
- u8 *status);
+ int port);
+int mthca_MAP_ICM(struct mthca_dev *dev, struct mthca_icm *icm, u64 virt);
+int mthca_MAP_ICM_page(struct mthca_dev *dev, u64 dma_addr, u64 virt);
+int mthca_UNMAP_ICM(struct mthca_dev *dev, u64 virt, u32 page_count);
+int mthca_MAP_ICM_AUX(struct mthca_dev *dev, struct mthca_icm *icm);
+int mthca_UNMAP_ICM_AUX(struct mthca_dev *dev);
+int mthca_SET_ICM_SIZE(struct mthca_dev *dev, u64 icm_size, u64 *aux_pages);
int mthca_SW2HW_MPT(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int mpt_index, u8 *status);
+ int mpt_index);
int mthca_HW2SW_MPT(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int mpt_index, u8 *status);
+ int mpt_index);
int mthca_WRITE_MTT(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int num_mtt, u8 *status);
-int mthca_SYNC_TPT(struct mthca_dev *dev, u8 *status);
+ int num_mtt);
+int mthca_SYNC_TPT(struct mthca_dev *dev);
int mthca_MAP_EQ(struct mthca_dev *dev, u64 event_mask, int unmap,
- int eq_num, u8 *status);
+ int eq_num);
int mthca_SW2HW_EQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int eq_num, u8 *status);
+ int eq_num);
int mthca_HW2SW_EQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int eq_num, u8 *status);
+ int eq_num);
int mthca_SW2HW_CQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int cq_num, u8 *status);
+ int cq_num);
int mthca_HW2SW_CQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int cq_num, u8 *status);
-int mthca_RESIZE_CQ(struct mthca_dev *dev, int cq_num, u32 lkey, u8 log_size,
- u8 *status);
+ int cq_num);
+int mthca_RESIZE_CQ(struct mthca_dev *dev, int cq_num, u32 lkey, u8 log_size);
int mthca_SW2HW_SRQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int srq_num, u8 *status);
+ int srq_num);
int mthca_HW2SW_SRQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- int srq_num, u8 *status);
+ int srq_num);
int mthca_QUERY_SRQ(struct mthca_dev *dev, u32 num,
- struct mthca_mailbox *mailbox, u8 *status);
-int mthca_ARM_SRQ(struct mthca_dev *dev, int srq_num, int limit, u8 *status);
+ struct mthca_mailbox *mailbox);
+int mthca_ARM_SRQ(struct mthca_dev *dev, int srq_num, int limit);
int mthca_MODIFY_QP(struct mthca_dev *dev, enum ib_qp_state cur,
enum ib_qp_state next, u32 num, int is_ee,
- struct mthca_mailbox *mailbox, u32 optmask,
- u8 *status);
+ struct mthca_mailbox *mailbox, u32 optmask);
int mthca_QUERY_QP(struct mthca_dev *dev, u32 num, int is_ee,
- struct mthca_mailbox *mailbox, u8 *status);
-int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn,
- u8 *status);
+ struct mthca_mailbox *mailbox);
+int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn);
int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey,
int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
- void *in_mad, void *response_mad, u8 *status);
+ void *in_mad, void *response_mad);
int mthca_READ_MGM(struct mthca_dev *dev, int index,
- struct mthca_mailbox *mailbox, u8 *status);
+ struct mthca_mailbox *mailbox);
int mthca_WRITE_MGM(struct mthca_dev *dev, int index,
- struct mthca_mailbox *mailbox, u8 *status);
+ struct mthca_mailbox *mailbox);
int mthca_MGID_HASH(struct mthca_dev *dev, struct mthca_mailbox *mailbox,
- u16 *hash, u8 *status);
-int mthca_NOP(struct mthca_dev *dev, u8 *status);
+ u16 *hash);
+int mthca_NOP(struct mthca_dev *dev);
#endif /* MTHCA_CMD_H */
diff --git a/drivers/infiniband/hw/mthca/mthca_cq.c b/drivers/infiniband/hw/mthca/mthca_cq.c
index 18ee3fa4b88c..53157b86a1ba 100644
--- a/drivers/infiniband/hw/mthca/mthca_cq.c
+++ b/drivers/infiniband/hw/mthca/mthca_cq.c
@@ -779,7 +779,6 @@ int mthca_init_cq(struct mthca_dev *dev, int nent,
struct mthca_mailbox *mailbox;
struct mthca_cq_context *cq_context;
int err = -ENOMEM;
- u8 status;
cq->ibcq.cqe = nent - 1;
cq->is_kernel = !ctx;
@@ -847,19 +846,12 @@ int mthca_init_cq(struct mthca_dev *dev, int nent,
cq_context->state_db = cpu_to_be32(cq->arm_db_index);
}
- err = mthca_SW2HW_CQ(dev, mailbox, cq->cqn, &status);
+ err = mthca_SW2HW_CQ(dev, mailbox, cq->cqn);
if (err) {
mthca_warn(dev, "SW2HW_CQ failed (%d)\n", err);
goto err_out_free_mr;
}
- if (status) {
- mthca_warn(dev, "SW2HW_CQ returned status 0x%02x\n",
- status);
- err = -EINVAL;
- goto err_out_free_mr;
- }
-
spin_lock_irq(&dev->cq_table.lock);
if (mthca_array_set(&dev->cq_table.cq,
cq->cqn & (dev->limits.num_cqs - 1),
@@ -915,7 +907,6 @@ void mthca_free_cq(struct mthca_dev *dev,
{
struct mthca_mailbox *mailbox;
int err;
- u8 status;
mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
if (IS_ERR(mailbox)) {
@@ -923,11 +914,9 @@ void mthca_free_cq(struct mthca_dev *dev,
return;
}
- err = mthca_HW2SW_CQ(dev, mailbox, cq->cqn, &status);
+ err = mthca_HW2SW_CQ(dev, mailbox, cq->cqn);
if (err)
mthca_warn(dev, "HW2SW_CQ failed (%d)\n", err);
- else if (status)
- mthca_warn(dev, "HW2SW_CQ returned status 0x%02x\n", status);
if (0) {
__be32 *ctx = mailbox->buf;
diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c
index 76785c653c13..7c9d35f39d75 100644
--- a/drivers/infiniband/hw/mthca/mthca_eq.c
+++ b/drivers/infiniband/hw/mthca/mthca_eq.c
@@ -474,7 +474,6 @@ static int mthca_create_eq(struct mthca_dev *dev,
struct mthca_eq_context *eq_context;
int err = -ENOMEM;
int i;
- u8 status;
eq->dev = dev;
eq->nent = roundup_pow_of_two(max(nent, 2));
@@ -543,15 +542,9 @@ static int mthca_create_eq(struct mthca_dev *dev,
eq_context->intr = intr;
eq_context->lkey = cpu_to_be32(eq->mr.ibmr.lkey);
- err = mthca_SW2HW_EQ(dev, mailbox, eq->eqn, &status);
+ err = mthca_SW2HW_EQ(dev, mailbox, eq->eqn);
if (err) {
- mthca_warn(dev, "SW2HW_EQ failed (%d)\n", err);
- goto err_out_free_mr;
- }
- if (status) {
- mthca_warn(dev, "SW2HW_EQ returned status 0x%02x\n",
- status);
- err = -EINVAL;
+ mthca_warn(dev, "SW2HW_EQ returned %d\n", err);
goto err_out_free_mr;
}
@@ -597,7 +590,6 @@ static void mthca_free_eq(struct mthca_dev *dev,
{
struct mthca_mailbox *mailbox;
int err;
- u8 status;
int npages = (eq->nent * MTHCA_EQ_ENTRY_SIZE + PAGE_SIZE - 1) /
PAGE_SIZE;
int i;
@@ -606,11 +598,9 @@ static void mthca_free_eq(struct mthca_dev *dev,
if (IS_ERR(mailbox))
return;
- err = mthca_HW2SW_EQ(dev, mailbox, eq->eqn, &status);
+ err = mthca_HW2SW_EQ(dev, mailbox, eq->eqn);
if (err)
- mthca_warn(dev, "HW2SW_EQ failed (%d)\n", err);
- if (status)
- mthca_warn(dev, "HW2SW_EQ returned status 0x%02x\n", status);
+ mthca_warn(dev, "HW2SW_EQ returned %d\n", err);
dev->eq_table.arm_mask &= ~eq->eqn_mask;
@@ -738,7 +728,6 @@ static void mthca_unmap_eq_regs(struct mthca_dev *dev)
int mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt)
{
int ret;
- u8 status;
/*
* We assume that mapping one page is enough for the whole EQ
@@ -757,9 +746,7 @@ int mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt)
return -ENOMEM;
}
- ret = mthca_MAP_ICM_page(dev, dev->eq_table.icm_dma, icm_virt, &status);
- if (!ret && status)
- ret = -EINVAL;
+ ret = mthca_MAP_ICM_page(dev, dev->eq_table.icm_dma, icm_virt);
if (ret) {
pci_unmap_page(dev->pdev, dev->eq_table.icm_dma, PAGE_SIZE,
PCI_DMA_BIDIRECTIONAL);
@@ -771,9 +758,7 @@ int mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt)
void mthca_unmap_eq_icm(struct mthca_dev *dev)
{
- u8 status;
-
- mthca_UNMAP_ICM(dev, dev->eq_table.icm_virt, 1, &status);
+ mthca_UNMAP_ICM(dev, dev->eq_table.icm_virt, 1);
pci_unmap_page(dev->pdev, dev->eq_table.icm_dma, PAGE_SIZE,
PCI_DMA_BIDIRECTIONAL);
__free_page(dev->eq_table.icm_page);
@@ -782,7 +767,6 @@ void mthca_unmap_eq_icm(struct mthca_dev *dev)
int mthca_init_eq_table(struct mthca_dev *dev)
{
int err;
- u8 status;
u8 intr;
int i;
@@ -864,22 +848,16 @@ int mthca_init_eq_table(struct mthca_dev *dev)
}
err = mthca_MAP_EQ(dev, async_mask(dev),
- 0, dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn, &status);
+ 0, dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn);
if (err)
mthca_warn(dev, "MAP_EQ for async EQ %d failed (%d)\n",
dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn, err);
- if (status)
- mthca_warn(dev, "MAP_EQ for async EQ %d returned status 0x%02x\n",
- dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn, status);
err = mthca_MAP_EQ(dev, MTHCA_CMD_EVENT_MASK,
- 0, dev->eq_table.eq[MTHCA_EQ_CMD].eqn, &status);
+ 0, dev->eq_table.eq[MTHCA_EQ_CMD].eqn);
if (err)
mthca_warn(dev, "MAP_EQ for cmd EQ %d failed (%d)\n",
dev->eq_table.eq[MTHCA_EQ_CMD].eqn, err);
- if (status)
- mthca_warn(dev, "MAP_EQ for cmd EQ %d returned status 0x%02x\n",
- dev->eq_table.eq[MTHCA_EQ_CMD].eqn, status);
for (i = 0; i < MTHCA_NUM_EQ; ++i)
if (mthca_is_memfree(dev))
@@ -909,15 +887,14 @@ err_out_free:
void mthca_cleanup_eq_table(struct mthca_dev *dev)
{
- u8 status;
int i;
mthca_free_irqs(dev);
mthca_MAP_EQ(dev, async_mask(dev),
- 1, dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn, &status);
+ 1, dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn);
mthca_MAP_EQ(dev, MTHCA_CMD_EVENT_MASK,
- 1, dev->eq_table.eq[MTHCA_EQ_CMD].eqn, &status);
+ 1, dev->eq_table.eq[MTHCA_EQ_CMD].eqn);
for (i = 0; i < MTHCA_NUM_EQ; ++i)
mthca_free_eq(dev, &dev->eq_table.eq[i]);
diff --git a/drivers/infiniband/hw/mthca/mthca_mad.c b/drivers/infiniband/hw/mthca/mthca_mad.c
index 03a59534f59e..b6f7f457fc55 100644
--- a/drivers/infiniband/hw/mthca/mthca_mad.c
+++ b/drivers/infiniband/hw/mthca/mthca_mad.c
@@ -201,7 +201,6 @@ int mthca_process_mad(struct ib_device *ibdev,
struct ib_mad *out_mad)
{
int err;
- u8 status;
u16 slid = in_wc ? in_wc->slid : be16_to_cpu(IB_LID_PERMISSIVE);
u16 prev_lid = 0;
struct ib_port_attr pattr;
@@ -252,17 +251,11 @@ int mthca_process_mad(struct ib_device *ibdev,
err = mthca_MAD_IFC(to_mdev(ibdev),
mad_flags & IB_MAD_IGNORE_MKEY,
mad_flags & IB_MAD_IGNORE_BKEY,
- port_num, in_wc, in_grh, in_mad, out_mad,
- &status);
- if (err) {
- mthca_err(to_mdev(ibdev), "MAD_IFC failed\n");
- return IB_MAD_RESULT_FAILURE;
- }
- if (status == MTHCA_CMD_STAT_BAD_PKT)
+ port_num, in_wc, in_grh, in_mad, out_mad);
+ if (err == -EBADMSG)
return IB_MAD_RESULT_SUCCESS;
- if (status) {
- mthca_err(to_mdev(ibdev), "MAD_IFC returned status %02x\n",
- status);
+ else if (err) {
+ mthca_err(to_mdev(ibdev), "MAD_IFC returned %d\n", err);
return IB_MAD_RESULT_FAILURE;
}
diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c
index f24b79b805f2..aa12a533ae9e 100644
--- a/drivers/infiniband/hw/mthca/mthca_main.c
+++ b/drivers/infiniband/hw/mthca/mthca_main.c
@@ -149,7 +149,7 @@ static int mthca_tune_pci(struct mthca_dev *mdev)
} else if (!(mdev->mthca_flags & MTHCA_FLAG_PCIE))
mthca_info(mdev, "No PCI-X capability, not setting RBC.\n");
- if (pci_find_capability(mdev->pdev, PCI_CAP_ID_EXP)) {
+ if (pci_is_pcie(mdev->pdev)) {
if (pcie_set_readrq(mdev->pdev, 4096)) {
mthca_err(mdev, "Couldn't write PCI Express read request, "
"aborting.\n");
@@ -165,19 +165,14 @@ static int mthca_tune_pci(struct mthca_dev *mdev)
static int mthca_dev_lim(struct mthca_dev *mdev, struct mthca_dev_lim *dev_lim)
{
int err;
- u8 status;
mdev->limits.mtt_seg_size = (1 << log_mtts_per_seg) * 8;
- err = mthca_QUERY_DEV_LIM(mdev, dev_lim, &status);
+ err = mthca_QUERY_DEV_LIM(mdev, dev_lim);
if (err) {
- mthca_err(mdev, "QUERY_DEV_LIM command failed, aborting.\n");
+ mthca_err(mdev, "QUERY_DEV_LIM command returned %d"
+ ", aborting.\n", err);
return err;
}
- if (status) {
- mthca_err(mdev, "QUERY_DEV_LIM returned status 0x%02x, "
- "aborting.\n", status);
- return -EINVAL;
- }
if (dev_lim->min_page_sz > PAGE_SIZE) {
mthca_err(mdev, "HCA minimum page size of %d bigger than "
"kernel PAGE_SIZE of %ld, aborting.\n",
@@ -293,49 +288,32 @@ static int mthca_dev_lim(struct mthca_dev *mdev, struct mthca_dev_lim *dev_lim)
static int mthca_init_tavor(struct mthca_dev *mdev)
{
s64 size;
- u8 status;
int err;
struct mthca_dev_lim dev_lim;
struct mthca_profile profile;
struct mthca_init_hca_param init_hca;
- err = mthca_SYS_EN(mdev, &status);
+ err = mthca_SYS_EN(mdev);
if (err) {
- mthca_err(mdev, "SYS_EN command failed, aborting.\n");
+ mthca_err(mdev, "SYS_EN command returned %d, aborting.\n", err);
return err;
}
- if (status) {
- mthca_err(mdev, "SYS_EN returned status 0x%02x, "
- "aborting.\n", status);
- return -EINVAL;
- }
- err = mthca_QUERY_FW(mdev, &status);
+ err = mthca_QUERY_FW(mdev);
if (err) {
- mthca_err(mdev, "QUERY_FW command failed, aborting.\n");
- goto err_disable;
- }
- if (status) {
- mthca_err(mdev, "QUERY_FW returned status 0x%02x, "
- "aborting.\n", status);
- err = -EINVAL;
+ mthca_err(mdev, "QUERY_FW command returned %d,"
+ " aborting.\n", err);
goto err_disable;
}
- err = mthca_QUERY_DDR(mdev, &status);
+ err = mthca_QUERY_DDR(mdev);
if (err) {
- mthca_err(mdev, "QUERY_DDR command failed, aborting.\n");
- goto err_disable;
- }
- if (status) {
- mthca_err(mdev, "QUERY_DDR returned status 0x%02x, "
- "aborting.\n", status);
- err = -EINVAL;
+ mthca_err(mdev, "QUERY_DDR command returned %d, aborting.\n", err);
goto err_disable;
}
err = mthca_dev_lim(mdev, &dev_lim);
if (err) {
- mthca_err(mdev, "QUERY_DEV_LIM command failed, aborting.\n");
+ mthca_err(mdev, "QUERY_DEV_LIM command returned %d, aborting.\n", err);
goto err_disable;
}
@@ -351,29 +329,22 @@ static int mthca_init_tavor(struct mthca_dev *mdev)
goto err_disable;
}
- err = mthca_INIT_HCA(mdev, &init_hca, &status);
+ err = mthca_INIT_HCA(mdev, &init_hca);
if (err) {
- mthca_err(mdev, "INIT_HCA command failed, aborting.\n");
- goto err_disable;
- }
- if (status) {
- mthca_err(mdev, "INIT_HCA returned status 0x%02x, "
- "aborting.\n", status);
- err = -EINVAL;
+ mthca_err(mdev, "INIT_HCA command returned %d, aborting.\n", err);
goto err_disable;
}
return 0;
err_disable:
- mthca_SYS_DIS(mdev, &status);
+ mthca_SYS_DIS(mdev);
return err;
}
static int mthca_load_fw(struct mthca_dev *mdev)
{
- u8 status;
int err;
/* FIXME: use HCA-attached memory for FW if present */
@@ -386,31 +357,21 @@ static int mthca_load_fw(struct mthca_dev *mdev)
return -ENOMEM;
}
- err = mthca_MAP_FA(mdev, mdev->fw.arbel.fw_icm, &status);
+ err = mthca_MAP_FA(mdev, mdev->fw.arbel.fw_icm);
if (err) {
- mthca_err(mdev, "MAP_FA command failed, aborting.\n");
- goto err_free;
- }
- if (status) {
- mthca_err(mdev, "MAP_FA returned status 0x%02x, aborting.\n", status);
- err = -EINVAL;
+ mthca_err(mdev, "MAP_FA command returned %d, aborting.\n", err);
goto err_free;
}
- err = mthca_RUN_FW(mdev, &status);
+ err = mthca_RUN_FW(mdev);
if (err) {
- mthca_err(mdev, "RUN_FW command failed, aborting.\n");
- goto err_unmap_fa;
- }
- if (status) {
- mthca_err(mdev, "RUN_FW returned status 0x%02x, aborting.\n", status);
- err = -EINVAL;
+ mthca_err(mdev, "RUN_FW command returned %d, aborting.\n", err);
goto err_unmap_fa;
}
return 0;
err_unmap_fa:
- mthca_UNMAP_FA(mdev, &status);
+ mthca_UNMAP_FA(mdev);
err_free:
mthca_free_icm(mdev, mdev->fw.arbel.fw_icm, 0);
@@ -423,19 +384,13 @@ static int mthca_init_icm(struct mthca_dev *mdev,
u64 icm_size)
{
u64 aux_pages;
- u8 status;
int err;
- err = mthca_SET_ICM_SIZE(mdev, icm_size, &aux_pages, &status);
+ err = mthca_SET_ICM_SIZE(mdev, icm_size, &aux_pages);
if (err) {
- mthca_err(mdev, "SET_ICM_SIZE command failed, aborting.\n");
+ mthca_err(mdev, "SET_ICM_SIZE command returned %d, aborting.\n", err);
return err;
}
- if (status) {
- mthca_err(mdev, "SET_ICM_SIZE returned status 0x%02x, "
- "aborting.\n", status);
- return -EINVAL;
- }
mthca_dbg(mdev, "%lld KB of HCA context requires %lld KB aux memory.\n",
(unsigned long long) icm_size >> 10,
@@ -448,14 +403,9 @@ static int mthca_init_icm(struct mthca_dev *mdev,
return -ENOMEM;
}
- err = mthca_MAP_ICM_AUX(mdev, mdev->fw.arbel.aux_icm, &status);
+ err = mthca_MAP_ICM_AUX(mdev, mdev->fw.arbel.aux_icm);
if (err) {
- mthca_err(mdev, "MAP_ICM_AUX command failed, aborting.\n");
- goto err_free_aux;
- }
- if (status) {
- mthca_err(mdev, "MAP_ICM_AUX returned status 0x%02x, aborting.\n", status);
- err = -EINVAL;
+ mthca_err(mdev, "MAP_ICM_AUX returned %d, aborting.\n", err);
goto err_free_aux;
}
@@ -596,7 +546,7 @@ err_unmap_eq:
mthca_unmap_eq_icm(mdev);
err_unmap_aux:
- mthca_UNMAP_ICM_AUX(mdev, &status);
+ mthca_UNMAP_ICM_AUX(mdev);
err_free_aux:
mthca_free_icm(mdev, mdev->fw.arbel.aux_icm, 0);
@@ -606,7 +556,6 @@ err_free_aux:
static void mthca_free_icms(struct mthca_dev *mdev)
{
- u8 status;
mthca_free_icm_table(mdev, mdev->mcg_table.table);
if (mdev->mthca_flags & MTHCA_FLAG_SRQ)
@@ -619,7 +568,7 @@ static void mthca_free_icms(struct mthca_dev *mdev)
mthca_free_icm_table(mdev, mdev->mr_table.mtt_table);
mthca_unmap_eq_icm(mdev);
- mthca_UNMAP_ICM_AUX(mdev, &status);
+ mthca_UNMAP_ICM_AUX(mdev);
mthca_free_icm(mdev, mdev->fw.arbel.aux_icm, 0);
}
@@ -629,43 +578,32 @@ static int mthca_init_arbel(struct mthca_dev *mdev)
struct mthca_profile profile;
struct mthca_init_hca_param init_hca;
s64 icm_size;
- u8 status;
int err;
- err = mthca_QUERY_FW(mdev, &status);
+ err = mthca_QUERY_FW(mdev);
if (err) {
- mthca_err(mdev, "QUERY_FW command failed, aborting.\n");
+ mthca_err(mdev, "QUERY_FW command failed %d, aborting.\n", err);
return err;
}
- if (status) {
- mthca_err(mdev, "QUERY_FW returned status 0x%02x, "
- "aborting.\n", status);
- return -EINVAL;
- }
- err = mthca_ENABLE_LAM(mdev, &status);
- if (err) {
- mthca_err(mdev, "ENABLE_LAM command failed, aborting.\n");
- return err;
- }
- if (status == MTHCA_CMD_STAT_LAM_NOT_PRE) {
+ err = mthca_ENABLE_LAM(mdev);
+ if (err == -EAGAIN) {
mthca_dbg(mdev, "No HCA-attached memory (running in MemFree mode)\n");
mdev->mthca_flags |= MTHCA_FLAG_NO_LAM;
- } else if (status) {
- mthca_err(mdev, "ENABLE_LAM returned status 0x%02x, "
- "aborting.\n", status);
- return -EINVAL;
+ } else if (err) {
+ mthca_err(mdev, "ENABLE_LAM returned %d, aborting.\n", err);
+ return err;
}
err = mthca_load_fw(mdev);
if (err) {
- mthca_err(mdev, "Failed to start FW, aborting.\n");
+ mthca_err(mdev, "Loading FW returned %d, aborting.\n", err);
goto err_disable;
}
err = mthca_dev_lim(mdev, &dev_lim);
if (err) {
- mthca_err(mdev, "QUERY_DEV_LIM command failed, aborting.\n");
+ mthca_err(mdev, "QUERY_DEV_LIM returned %d, aborting.\n", err);
goto err_stop_fw;
}
@@ -685,15 +623,9 @@ static int mthca_init_arbel(struct mthca_dev *mdev)
if (err)
goto err_stop_fw;
- err = mthca_INIT_HCA(mdev, &init_hca, &status);
+ err = mthca_INIT_HCA(mdev, &init_hca);
if (err) {
- mthca_err(mdev, "INIT_HCA command failed, aborting.\n");
- goto err_free_icm;
- }
- if (status) {
- mthca_err(mdev, "INIT_HCA returned status 0x%02x, "
- "aborting.\n", status);
- err = -EINVAL;
+ mthca_err(mdev, "INIT_HCA command returned %d, aborting.\n", err);
goto err_free_icm;
}
@@ -703,37 +635,34 @@ err_free_icm:
mthca_free_icms(mdev);
err_stop_fw:
- mthca_UNMAP_FA(mdev, &status);
+ mthca_UNMAP_FA(mdev);
mthca_free_icm(mdev, mdev->fw.arbel.fw_icm, 0);
err_disable:
if (!(mdev->mthca_flags & MTHCA_FLAG_NO_LAM))
- mthca_DISABLE_LAM(mdev, &status);
+ mthca_DISABLE_LAM(mdev);
return err;
}
static void mthca_close_hca(struct mthca_dev *mdev)
{
- u8 status;
-
- mthca_CLOSE_HCA(mdev, 0, &status);
+ mthca_CLOSE_HCA(mdev, 0);
if (mthca_is_memfree(mdev)) {
mthca_free_icms(mdev);
- mthca_UNMAP_FA(mdev, &status);
+ mthca_UNMAP_FA(mdev);
mthca_free_icm(mdev, mdev->fw.arbel.fw_icm, 0);
if (!(mdev->mthca_flags & MTHCA_FLAG_NO_LAM))
- mthca_DISABLE_LAM(mdev, &status);
+ mthca_DISABLE_LAM(mdev);
} else
- mthca_SYS_DIS(mdev, &status);
+ mthca_SYS_DIS(mdev);
}
static int mthca_init_hca(struct mthca_dev *mdev)
{
- u8 status;
int err;
struct mthca_adapter adapter;
@@ -745,15 +674,9 @@ static int mthca_init_hca(struct mthca_dev *mdev)
if (err)
return err;
- err = mthca_QUERY_ADAPTER(mdev, &adapter, &status);
+ err = mthca_QUERY_ADAPTER(mdev, &adapter);
if (err) {
- mthca_err(mdev, "QUERY_ADAPTER command failed, aborting.\n");
- goto err_close;
- }
- if (status) {
- mthca_err(mdev, "QUERY_ADAPTER returned status 0x%02x, "
- "aborting.\n", status);
- err = -EINVAL;
+ mthca_err(mdev, "QUERY_ADAPTER command returned %d, aborting.\n", err);
goto err_close;
}
@@ -772,7 +695,6 @@ err_close:
static int mthca_setup_hca(struct mthca_dev *dev)
{
int err;
- u8 status;
MTHCA_INIT_DOORBELL_LOCK(&dev->doorbell_lock);
@@ -833,8 +755,8 @@ static int mthca_setup_hca(struct mthca_dev *dev)
goto err_eq_table_free;
}
- err = mthca_NOP(dev, &status);
- if (err || status) {
+ err = mthca_NOP(dev);
+ if (err) {
if (dev->mthca_flags & MTHCA_FLAG_MSI_X) {
mthca_warn(dev, "NOP command failed to generate interrupt "
"(IRQ %d).\n",
@@ -1166,7 +1088,6 @@ err_disable_pdev:
static void __mthca_remove_one(struct pci_dev *pdev)
{
struct mthca_dev *mdev = pci_get_drvdata(pdev);
- u8 status;
int p;
if (mdev) {
@@ -1174,7 +1095,7 @@ static void __mthca_remove_one(struct pci_dev *pdev)
mthca_unregister_device(mdev);
for (p = 1; p <= mdev->limits.num_ports; ++p)
- mthca_CLOSE_IB(mdev, p, &status);
+ mthca_CLOSE_IB(mdev, p);
mthca_cleanup_mcg_table(mdev);
mthca_cleanup_av_table(mdev);
diff --git a/drivers/infiniband/hw/mthca/mthca_mcg.c b/drivers/infiniband/hw/mthca/mthca_mcg.c
index 515790a606e6..6304ae8f4a6c 100644
--- a/drivers/infiniband/hw/mthca/mthca_mcg.c
+++ b/drivers/infiniband/hw/mthca/mthca_mcg.c
@@ -68,7 +68,6 @@ static int find_mgm(struct mthca_dev *dev,
struct mthca_mgm *mgm = mgm_mailbox->buf;
u8 *mgid;
int err;
- u8 status;
mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
if (IS_ERR(mailbox))
@@ -77,12 +76,9 @@ static int find_mgm(struct mthca_dev *dev,
memcpy(mgid, gid, 16);
- err = mthca_MGID_HASH(dev, mailbox, hash, &status);
- if (err)
- goto out;
- if (status) {
- mthca_err(dev, "MGID_HASH returned status %02x\n", status);
- err = -EINVAL;
+ err = mthca_MGID_HASH(dev, mailbox, hash);
+ if (err) {
+ mthca_err(dev, "MGID_HASH failed (%d)\n", err);
goto out;
}
@@ -93,12 +89,9 @@ static int find_mgm(struct mthca_dev *dev,
*prev = -1;
do {
- err = mthca_READ_MGM(dev, *index, mgm_mailbox, &status);
- if (err)
- goto out;
- if (status) {
- mthca_err(dev, "READ_MGM returned status %02x\n", status);
- err = -EINVAL;
+ err = mthca_READ_MGM(dev, *index, mgm_mailbox);
+ if (err) {
+ mthca_err(dev, "READ_MGM failed (%d)\n", err);
goto out;
}
@@ -134,7 +127,6 @@ int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
int link = 0;
int i;
int err;
- u8 status;
mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
if (IS_ERR(mailbox))
@@ -160,12 +152,9 @@ int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
goto out;
}
- err = mthca_READ_MGM(dev, index, mailbox, &status);
- if (err)
- goto out;
- if (status) {
- mthca_err(dev, "READ_MGM returned status %02x\n", status);
- err = -EINVAL;
+ err = mthca_READ_MGM(dev, index, mailbox);
+ if (err) {
+ mthca_err(dev, "READ_MGM failed (%d)\n", err);
goto out;
}
memset(mgm, 0, sizeof *mgm);
@@ -189,11 +178,9 @@ int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
goto out;
}
- err = mthca_WRITE_MGM(dev, index, mailbox, &status);
- if (err)
- goto out;
- if (status) {
- mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
+ err = mthca_WRITE_MGM(dev, index, mailbox);
+ if (err) {
+ mthca_err(dev, "WRITE_MGM failed %d\n", err);
err = -EINVAL;
goto out;
}
@@ -201,24 +188,17 @@ int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
if (!link)
goto out;
- err = mthca_READ_MGM(dev, prev, mailbox, &status);
- if (err)
- goto out;
- if (status) {
- mthca_err(dev, "READ_MGM returned status %02x\n", status);
- err = -EINVAL;
+ err = mthca_READ_MGM(dev, prev, mailbox);
+ if (err) {
+ mthca_err(dev, "READ_MGM failed %d\n", err);
goto out;
}
mgm->next_gid_index = cpu_to_be32(index << 6);
- err = mthca_WRITE_MGM(dev, prev, mailbox, &status);
+ err = mthca_WRITE_MGM(dev, prev, mailbox);
if (err)
- goto out;
- if (status) {
- mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
- err = -EINVAL;
- }
+ mthca_err(dev, "WRITE_MGM returned %d\n", err);
out:
if (err && link && index != -1) {
@@ -240,7 +220,6 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
int prev, index;
int i, loc;
int err;
- u8 status;
mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
if (IS_ERR(mailbox))
@@ -275,12 +254,9 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
mgm->qp[loc] = mgm->qp[i - 1];
mgm->qp[i - 1] = 0;
- err = mthca_WRITE_MGM(dev, index, mailbox, &status);
- if (err)
- goto out;
- if (status) {
- mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
- err = -EINVAL;
+ err = mthca_WRITE_MGM(dev, index, mailbox);
+ if (err) {
+ mthca_err(dev, "WRITE_MGM returned %d\n", err);
goto out;
}
@@ -292,24 +268,17 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
int amgm_index_to_free = be32_to_cpu(mgm->next_gid_index) >> 6;
if (amgm_index_to_free) {
err = mthca_READ_MGM(dev, amgm_index_to_free,
- mailbox, &status);
- if (err)
- goto out;
- if (status) {
- mthca_err(dev, "READ_MGM returned status %02x\n",
- status);
- err = -EINVAL;
+ mailbox);
+ if (err) {
+ mthca_err(dev, "READ_MGM returned %d\n", err);
goto out;
}
} else
memset(mgm->gid, 0, 16);
- err = mthca_WRITE_MGM(dev, index, mailbox, &status);
- if (err)
- goto out;
- if (status) {
- mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
- err = -EINVAL;
+ err = mthca_WRITE_MGM(dev, index, mailbox);
+ if (err) {
+ mthca_err(dev, "WRITE_MGM returned %d\n", err);
goto out;
}
if (amgm_index_to_free) {
@@ -319,23 +288,17 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
} else {
/* Remove entry from AMGM */
int curr_next_index = be32_to_cpu(mgm->next_gid_index) >> 6;
- err = mthca_READ_MGM(dev, prev, mailbox, &status);
- if (err)
- goto out;
- if (status) {
- mthca_err(dev, "READ_MGM returned status %02x\n", status);
- err = -EINVAL;
+ err = mthca_READ_MGM(dev, prev, mailbox);
+ if (err) {
+ mthca_err(dev, "READ_MGM returned %d\n", err);
goto out;
}
mgm->next_gid_index = cpu_to_be32(curr_next_index << 6);
- err = mthca_WRITE_MGM(dev, prev, mailbox, &status);
- if (err)
- goto out;
- if (status) {
- mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
- err = -EINVAL;
+ err = mthca_WRITE_MGM(dev, prev, mailbox);
+ if (err) {
+ mthca_err(dev, "WRITE_MGM returned %d\n", err);
goto out;
}
BUG_ON(index < dev->limits.num_mgms);
diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.c b/drivers/infiniband/hw/mthca/mthca_memfree.c
index 8c2a83732b5d..7d2e42dd6926 100644
--- a/drivers/infiniband/hw/mthca/mthca_memfree.c
+++ b/drivers/infiniband/hw/mthca/mthca_memfree.c
@@ -223,7 +223,6 @@ int mthca_table_get(struct mthca_dev *dev, struct mthca_icm_table *table, int ob
{
int i = (obj & (table->num_obj - 1)) * table->obj_size / MTHCA_TABLE_CHUNK_SIZE;
int ret = 0;
- u8 status;
mutex_lock(&table->mutex);
@@ -240,8 +239,8 @@ int mthca_table_get(struct mthca_dev *dev, struct mthca_icm_table *table, int ob
goto out;
}
- if (mthca_MAP_ICM(dev, table->icm[i], table->virt + i * MTHCA_TABLE_CHUNK_SIZE,
- &status) || status) {
+ if (mthca_MAP_ICM(dev, table->icm[i],
+ table->virt + i * MTHCA_TABLE_CHUNK_SIZE)) {
mthca_free_icm(dev, table->icm[i], table->coherent);
table->icm[i] = NULL;
ret = -ENOMEM;
@@ -258,7 +257,6 @@ out:
void mthca_table_put(struct mthca_dev *dev, struct mthca_icm_table *table, int obj)
{
int i;
- u8 status;
if (!mthca_is_memfree(dev))
return;
@@ -269,8 +267,7 @@ void mthca_table_put(struct mthca_dev *dev, struct mthca_icm_table *table, int o
if (--table->icm[i]->refcount == 0) {
mthca_UNMAP_ICM(dev, table->virt + i * MTHCA_TABLE_CHUNK_SIZE,
- MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE,
- &status);
+ MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE);
mthca_free_icm(dev, table->icm[i], table->coherent);
table->icm[i] = NULL;
}
@@ -366,7 +363,6 @@ struct mthca_icm_table *mthca_alloc_icm_table(struct mthca_dev *dev,
int num_icm;
unsigned chunk_size;
int i;
- u8 status;
obj_per_chunk = MTHCA_TABLE_CHUNK_SIZE / obj_size;
num_icm = DIV_ROUND_UP(nobj, obj_per_chunk);
@@ -396,8 +392,8 @@ struct mthca_icm_table *mthca_alloc_icm_table(struct mthca_dev *dev,
__GFP_NOWARN, use_coherent);
if (!table->icm[i])
goto err;
- if (mthca_MAP_ICM(dev, table->icm[i], virt + i * MTHCA_TABLE_CHUNK_SIZE,
- &status) || status) {
+ if (mthca_MAP_ICM(dev, table->icm[i],
+ virt + i * MTHCA_TABLE_CHUNK_SIZE)) {
mthca_free_icm(dev, table->icm[i], table->coherent);
table->icm[i] = NULL;
goto err;
@@ -416,8 +412,7 @@ err:
for (i = 0; i < num_icm; ++i)
if (table->icm[i]) {
mthca_UNMAP_ICM(dev, virt + i * MTHCA_TABLE_CHUNK_SIZE,
- MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE,
- &status);
+ MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE);
mthca_free_icm(dev, table->icm[i], table->coherent);
}
@@ -429,13 +424,12 @@ err:
void mthca_free_icm_table(struct mthca_dev *dev, struct mthca_icm_table *table)
{
int i;
- u8 status;
for (i = 0; i < table->num_icm; ++i)
if (table->icm[i]) {
- mthca_UNMAP_ICM(dev, table->virt + i * MTHCA_TABLE_CHUNK_SIZE,
- MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE,
- &status);
+ mthca_UNMAP_ICM(dev,
+ table->virt + i * MTHCA_TABLE_CHUNK_SIZE,
+ MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE);
mthca_free_icm(dev, table->icm[i], table->coherent);
}
@@ -454,7 +448,6 @@ int mthca_map_user_db(struct mthca_dev *dev, struct mthca_uar *uar,
{
struct page *pages[1];
int ret = 0;
- u8 status;
int i;
if (!mthca_is_memfree(dev))
@@ -494,9 +487,7 @@ int mthca_map_user_db(struct mthca_dev *dev, struct mthca_uar *uar,
}
ret = mthca_MAP_ICM_page(dev, sg_dma_address(&db_tab->page[i].mem),
- mthca_uarc_virt(dev, uar, i), &status);
- if (!ret && status)
- ret = -EINVAL;
+ mthca_uarc_virt(dev, uar, i));
if (ret) {
pci_unmap_sg(dev->pdev, &db_tab->page[i].mem, 1, PCI_DMA_TODEVICE);
put_page(sg_page(&db_tab->page[i].mem));
@@ -557,14 +548,13 @@ void mthca_cleanup_user_db_tab(struct mthca_dev *dev, struct mthca_uar *uar,
struct mthca_user_db_table *db_tab)
{
int i;
- u8 status;
if (!mthca_is_memfree(dev))
return;
for (i = 0; i < dev->uar_table.uarc_size / MTHCA_ICM_PAGE_SIZE; ++i) {
if (db_tab->page[i].uvirt) {
- mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, uar, i), 1, &status);
+ mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, uar, i), 1);
pci_unmap_sg(dev->pdev, &db_tab->page[i].mem, 1, PCI_DMA_TODEVICE);
put_page(sg_page(&db_tab->page[i].mem));
}
@@ -581,7 +571,6 @@ int mthca_alloc_db(struct mthca_dev *dev, enum mthca_db_type type,
int i, j;
struct mthca_db_page *page;
int ret = 0;
- u8 status;
mutex_lock(&dev->db_tab->mutex);
@@ -644,9 +633,7 @@ alloc:
memset(page->db_rec, 0, MTHCA_ICM_PAGE_SIZE);
ret = mthca_MAP_ICM_page(dev, page->mapping,
- mthca_uarc_virt(dev, &dev->driver_uar, i), &status);
- if (!ret && status)
- ret = -EINVAL;
+ mthca_uarc_virt(dev, &dev->driver_uar, i));
if (ret) {
dma_free_coherent(&dev->pdev->dev, MTHCA_ICM_PAGE_SIZE,
page->db_rec, page->mapping);
@@ -678,7 +665,6 @@ void mthca_free_db(struct mthca_dev *dev, int type, int db_index)
{
int i, j;
struct mthca_db_page *page;
- u8 status;
i = db_index / MTHCA_DB_REC_PER_PAGE;
j = db_index % MTHCA_DB_REC_PER_PAGE;
@@ -694,7 +680,7 @@ void mthca_free_db(struct mthca_dev *dev, int type, int db_index)
if (bitmap_empty(page->used, MTHCA_DB_REC_PER_PAGE) &&
i >= dev->db_tab->max_group1 - 1) {
- mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, &dev->driver_uar, i), 1, &status);
+ mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, &dev->driver_uar, i), 1);
dma_free_coherent(&dev->pdev->dev, MTHCA_ICM_PAGE_SIZE,
page->db_rec, page->mapping);
@@ -745,7 +731,6 @@ int mthca_init_db_tab(struct mthca_dev *dev)
void mthca_cleanup_db_tab(struct mthca_dev *dev)
{
int i;
- u8 status;
if (!mthca_is_memfree(dev))
return;
@@ -763,7 +748,7 @@ void mthca_cleanup_db_tab(struct mthca_dev *dev)
if (!bitmap_empty(dev->db_tab->page[i].used, MTHCA_DB_REC_PER_PAGE))
mthca_warn(dev, "Kernel UARC page %d not empty\n", i);
- mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, &dev->driver_uar, i), 1, &status);
+ mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, &dev->driver_uar, i), 1);
dma_free_coherent(&dev->pdev->dev, MTHCA_ICM_PAGE_SIZE,
dev->db_tab->page[i].db_rec,
diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c
index 44045c8846db..ed9a989e501b 100644
--- a/drivers/infiniband/hw/mthca/mthca_mr.c
+++ b/drivers/infiniband/hw/mthca/mthca_mr.c
@@ -146,7 +146,7 @@ static int mthca_buddy_init(struct mthca_buddy *buddy, int max_order)
buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *),
GFP_KERNEL);
- buddy->num_free = kzalloc((buddy->max_order + 1) * sizeof (int *),
+ buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free,
GFP_KERNEL);
if (!buddy->bits || !buddy->num_free)
goto err_out;
@@ -257,7 +257,6 @@ static int __mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt,
struct mthca_mailbox *mailbox;
__be64 *mtt_entry;
int err = 0;
- u8 status;
int i;
mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
@@ -281,17 +280,11 @@ static int __mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt,
if (i & 1)
mtt_entry[i + 2] = 0;
- err = mthca_WRITE_MTT(dev, mailbox, (i + 1) & ~1, &status);
+ err = mthca_WRITE_MTT(dev, mailbox, (i + 1) & ~1);
if (err) {
mthca_warn(dev, "WRITE_MTT failed (%d)\n", err);
goto out;
}
- if (status) {
- mthca_warn(dev, "WRITE_MTT returned status 0x%02x\n",
- status);
- err = -EINVAL;
- goto out;
- }
list_len -= i;
start_index += i;
@@ -441,7 +434,6 @@ int mthca_mr_alloc(struct mthca_dev *dev, u32 pd, int buffer_size_shift,
u32 key;
int i;
int err;
- u8 status;
WARN_ON(buffer_size_shift >= 32);
@@ -497,16 +489,10 @@ int mthca_mr_alloc(struct mthca_dev *dev, u32 pd, int buffer_size_shift,
}
err = mthca_SW2HW_MPT(dev, mailbox,
- key & (dev->limits.num_mpts - 1),
- &status);
+ key & (dev->limits.num_mpts - 1));
if (err) {
mthca_warn(dev, "SW2HW_MPT failed (%d)\n", err);
goto err_out_mailbox;
- } else if (status) {
- mthca_warn(dev, "SW2HW_MPT returned status 0x%02x\n",
- status);
- err = -EINVAL;
- goto err_out_mailbox;
}
mthca_free_mailbox(dev, mailbox);
@@ -567,17 +553,12 @@ static void mthca_free_region(struct mthca_dev *dev, u32 lkey)
void mthca_free_mr(struct mthca_dev *dev, struct mthca_mr *mr)
{
int err;
- u8 status;
err = mthca_HW2SW_MPT(dev, NULL,
key_to_hw_index(dev, mr->ibmr.lkey) &
- (dev->limits.num_mpts - 1),
- &status);
+ (dev->limits.num_mpts - 1));
if (err)
mthca_warn(dev, "HW2SW_MPT failed (%d)\n", err);
- else if (status)
- mthca_warn(dev, "HW2SW_MPT returned status 0x%02x\n",
- status);
mthca_free_region(dev, mr->ibmr.lkey);
mthca_free_mtt(dev, mr->mtt);
@@ -590,7 +571,6 @@ int mthca_fmr_alloc(struct mthca_dev *dev, u32 pd,
struct mthca_mailbox *mailbox;
u64 mtt_seg;
u32 key, idx;
- u8 status;
int list_len = mr->attr.max_pages;
int err = -ENOMEM;
int i;
@@ -672,18 +652,11 @@ int mthca_fmr_alloc(struct mthca_dev *dev, u32 pd,
}
err = mthca_SW2HW_MPT(dev, mailbox,
- key & (dev->limits.num_mpts - 1),
- &status);
+ key & (dev->limits.num_mpts - 1));
if (err) {
mthca_warn(dev, "SW2HW_MPT failed (%d)\n", err);
goto err_out_mailbox_free;
}
- if (status) {
- mthca_warn(dev, "SW2HW_MPT returned status 0x%02x\n",
- status);
- err = -EINVAL;
- goto err_out_mailbox_free;
- }
mthca_free_mailbox(dev, mailbox);
return 0;
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c
index 1e0b4b6074ad..5b71d43bd89c 100644
--- a/drivers/infiniband/hw/mthca/mthca_provider.c
+++ b/drivers/infiniband/hw/mthca/mthca_provider.c
@@ -40,7 +40,9 @@
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/stat.h>
#include <linux/mm.h>
+#include <linux/export.h>
#include "mthca_dev.h"
#include "mthca_cmd.h"
@@ -63,8 +65,6 @@ static int mthca_query_device(struct ib_device *ibdev,
int err = -ENOMEM;
struct mthca_dev *mdev = to_mdev(ibdev);
- u8 status;
-
in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL);
out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
if (!in_mad || !out_mad)
@@ -78,14 +78,9 @@ static int mthca_query_device(struct ib_device *ibdev,
in_mad->attr_id = IB_SMP_ATTR_NODE_INFO;
err = mthca_MAD_IFC(mdev, 1, 1,
- 1, NULL, NULL, in_mad, out_mad,
- &status);
+ 1, NULL, NULL, in_mad, out_mad);
if (err)
goto out;
- if (status) {
- err = -EINVAL;
- goto out;
- }
props->device_cap_flags = mdev->device_cap_flags;
props->vendor_id = be32_to_cpup((__be32 *) (out_mad->data + 36)) &
@@ -141,7 +136,6 @@ static int mthca_query_port(struct ib_device *ibdev,
struct ib_smp *in_mad = NULL;
struct ib_smp *out_mad = NULL;
int err = -ENOMEM;
- u8 status;
in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL);
out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
@@ -155,14 +149,9 @@ static int mthca_query_port(struct ib_device *ibdev,
in_mad->attr_mod = cpu_to_be32(port);
err = mthca_MAD_IFC(to_mdev(ibdev), 1, 1,
- port, NULL, NULL, in_mad, out_mad,
- &status);
+ port, NULL, NULL, in_mad, out_mad);
if (err)
goto out;
- if (status) {
- err = -EINVAL;
- goto out;
- }
props->lid = be16_to_cpup((__be16 *) (out_mad->data + 16));
props->lmc = out_mad->data[34] & 0x7;
@@ -214,7 +203,6 @@ static int mthca_modify_port(struct ib_device *ibdev,
struct mthca_set_ib_param set_ib;
struct ib_port_attr attr;
int err;
- u8 status;
if (mutex_lock_interruptible(&to_mdev(ibdev)->cap_mask_mutex))
return -ERESTARTSYS;
@@ -229,14 +217,9 @@ static int mthca_modify_port(struct ib_device *ibdev,
set_ib.cap_mask = (attr.port_cap_flags | props->set_port_cap_mask) &
~props->clr_port_cap_mask;
- err = mthca_SET_IB(to_mdev(ibdev), &set_ib, port, &status);
+ err = mthca_SET_IB(to_mdev(ibdev), &set_ib, port);
if (err)
goto out;
- if (status) {
- err = -EINVAL;
- goto out;
- }
-
out:
mutex_unlock(&to_mdev(ibdev)->cap_mask_mutex);
return err;
@@ -248,7 +231,6 @@ static int mthca_query_pkey(struct ib_device *ibdev,
struct ib_smp *in_mad = NULL;
struct ib_smp *out_mad = NULL;
int err = -ENOMEM;
- u8 status;
in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL);
out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
@@ -260,14 +242,9 @@ static int mthca_query_pkey(struct ib_device *ibdev,
in_mad->attr_mod = cpu_to_be32(index / 32);
err = mthca_MAD_IFC(to_mdev(ibdev), 1, 1,
- port, NULL, NULL, in_mad, out_mad,
- &status);
+ port, NULL, NULL, in_mad, out_mad);
if (err)
goto out;
- if (status) {
- err = -EINVAL;
- goto out;
- }
*pkey = be16_to_cpu(((__be16 *) out_mad->data)[index % 32]);
@@ -283,7 +260,6 @@ static int mthca_query_gid(struct ib_device *ibdev, u8 port,
struct ib_smp *in_mad = NULL;
struct ib_smp *out_mad = NULL;
int err = -ENOMEM;
- u8 status;
in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL);
out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
@@ -295,14 +271,9 @@ static int mthca_query_gid(struct ib_device *ibdev, u8 port,
in_mad->attr_mod = cpu_to_be32(port);
err = mthca_MAD_IFC(to_mdev(ibdev), 1, 1,
- port, NULL, NULL, in_mad, out_mad,
- &status);
+ port, NULL, NULL, in_mad, out_mad);
if (err)
goto out;
- if (status) {
- err = -EINVAL;
- goto out;
- }
memcpy(gid->raw, out_mad->data + 8, 8);
@@ -311,14 +282,9 @@ static int mthca_query_gid(struct ib_device *ibdev, u8 port,
in_mad->attr_mod = cpu_to_be32(index / 8);
err = mthca_MAD_IFC(to_mdev(ibdev), 1, 1,
- port, NULL, NULL, in_mad, out_mad,
- &status);
+ port, NULL, NULL, in_mad, out_mad);
if (err)
goto out;
- if (status) {
- err = -EINVAL;
- goto out;
- }
memcpy(gid->raw + 8, out_mad->data + (index % 8) * 8, 8);
@@ -474,6 +440,9 @@ static struct ib_srq *mthca_create_srq(struct ib_pd *pd,
struct mthca_srq *srq;
int err;
+ if (init_attr->srq_type != IB_SRQT_BASIC)
+ return ERR_PTR(-ENOSYS);
+
srq = kmalloc(sizeof *srq, GFP_KERNEL);
if (!srq)
return ERR_PTR(-ENOMEM);
@@ -800,7 +769,6 @@ static int mthca_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *uda
struct mthca_cq *cq = to_mcq(ibcq);
struct mthca_resize_cq ucmd;
u32 lkey;
- u8 status;
int ret;
if (entries < 1 || entries > dev->limits.max_cqes)
@@ -827,9 +795,7 @@ static int mthca_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *uda
lkey = ucmd.lkey;
}
- ret = mthca_RESIZE_CQ(dev, cq->cqn, lkey, ilog2(entries), &status);
- if (status)
- ret = -EINVAL;
+ ret = mthca_RESIZE_CQ(dev, cq->cqn, lkey, ilog2(entries));
if (ret) {
if (cq->resize_buf) {
@@ -1161,7 +1127,6 @@ static int mthca_unmap_fmr(struct list_head *fmr_list)
{
struct ib_fmr *fmr;
int err;
- u8 status;
struct mthca_dev *mdev = NULL;
list_for_each_entry(fmr, fmr_list, list) {
@@ -1182,12 +1147,8 @@ static int mthca_unmap_fmr(struct list_head *fmr_list)
list_for_each_entry(fmr, fmr_list, list)
mthca_tavor_fmr_unmap(mdev, to_mfmr(fmr));
- err = mthca_SYNC_TPT(mdev, &status);
- if (err)
- return err;
- if (status)
- return -EINVAL;
- return 0;
+ err = mthca_SYNC_TPT(mdev);
+ return err;
}
static ssize_t show_rev(struct device *device, struct device_attribute *attr,
@@ -1253,7 +1214,6 @@ static int mthca_init_node_data(struct mthca_dev *dev)
struct ib_smp *in_mad = NULL;
struct ib_smp *out_mad = NULL;
int err = -ENOMEM;
- u8 status;
in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL);
out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
@@ -1264,28 +1224,18 @@ static int mthca_init_node_data(struct mthca_dev *dev)
in_mad->attr_id = IB_SMP_ATTR_NODE_DESC;
err = mthca_MAD_IFC(dev, 1, 1,
- 1, NULL, NULL, in_mad, out_mad,
- &status);
+ 1, NULL, NULL, in_mad, out_mad);
if (err)
goto out;
- if (status) {
- err = -EINVAL;
- goto out;
- }
memcpy(dev->ib_dev.node_desc, out_mad->data, 64);
in_mad->attr_id = IB_SMP_ATTR_NODE_INFO;
err = mthca_MAD_IFC(dev, 1, 1,
- 1, NULL, NULL, in_mad, out_mad,
- &status);
+ 1, NULL, NULL, in_mad, out_mad);
if (err)
goto out;
- if (status) {
- err = -EINVAL;
- goto out;
- }
if (mthca_is_memfree(dev))
dev->rev_id = be32_to_cpup((__be32 *) (out_mad->data + 32));
diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c
index a34c9d38e822..9601049e14d0 100644
--- a/drivers/infiniband/hw/mthca/mthca_qp.c
+++ b/drivers/infiniband/hw/mthca/mthca_qp.c
@@ -308,7 +308,6 @@ static void store_attrs(struct mthca_sqp *sqp, const struct ib_qp_attr *attr,
static void init_port(struct mthca_dev *dev, int port)
{
int err;
- u8 status;
struct mthca_init_ib_param param;
memset(&param, 0, sizeof param);
@@ -319,11 +318,9 @@ static void init_port(struct mthca_dev *dev, int port)
param.gid_cap = dev->limits.gid_table_len;
param.pkey_cap = dev->limits.pkey_table_len;
- err = mthca_INIT_IB(dev, &param, port, &status);
+ err = mthca_INIT_IB(dev, &param, port);
if (err)
mthca_warn(dev, "INIT_IB failed, return code %d.\n", err);
- if (status)
- mthca_warn(dev, "INIT_IB returned status %02x.\n", status);
}
static __be32 get_hw_access_flags(struct mthca_qp *qp, const struct ib_qp_attr *attr,
@@ -433,7 +430,6 @@ int mthca_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr_m
struct mthca_qp_param *qp_param;
struct mthca_qp_context *context;
int mthca_state;
- u8 status;
mutex_lock(&qp->mutex);
@@ -448,12 +444,9 @@ int mthca_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr_m
goto out;
}
- err = mthca_QUERY_QP(dev, qp->qpn, 0, mailbox, &status);
- if (err)
- goto out_mailbox;
- if (status) {
- mthca_warn(dev, "QUERY_QP returned status %02x\n", status);
- err = -EINVAL;
+ err = mthca_QUERY_QP(dev, qp->qpn, 0, mailbox);
+ if (err) {
+ mthca_warn(dev, "QUERY_QP failed (%d)\n", err);
goto out_mailbox;
}
@@ -555,7 +548,6 @@ static int __mthca_modify_qp(struct ib_qp *ibqp,
struct mthca_qp_param *qp_param;
struct mthca_qp_context *qp_context;
u32 sqd_event = 0;
- u8 status;
int err = -EINVAL;
mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
@@ -781,13 +773,10 @@ static int __mthca_modify_qp(struct ib_qp *ibqp,
sqd_event = 1 << 31;
err = mthca_MODIFY_QP(dev, cur_state, new_state, qp->qpn, 0,
- mailbox, sqd_event, &status);
- if (err)
- goto out_mailbox;
- if (status) {
- mthca_warn(dev, "modify QP %d->%d returned status %02x.\n",
- cur_state, new_state, status);
- err = -EINVAL;
+ mailbox, sqd_event);
+ if (err) {
+ mthca_warn(dev, "modify QP %d->%d returned %d.\n",
+ cur_state, new_state, err);
goto out_mailbox;
}
@@ -817,7 +806,7 @@ static int __mthca_modify_qp(struct ib_qp *ibqp,
cur_state != IB_QPS_ERR &&
(new_state == IB_QPS_RESET ||
new_state == IB_QPS_ERR))
- mthca_CLOSE_IB(dev, qp->port, &status);
+ mthca_CLOSE_IB(dev, qp->port);
}
/*
@@ -1429,7 +1418,6 @@ static inline int get_qp_refcount(struct mthca_dev *dev, struct mthca_qp *qp)
void mthca_free_qp(struct mthca_dev *dev,
struct mthca_qp *qp)
{
- u8 status;
struct mthca_cq *send_cq;
struct mthca_cq *recv_cq;
@@ -1454,7 +1442,7 @@ void mthca_free_qp(struct mthca_dev *dev,
if (qp->state != IB_QPS_RESET)
mthca_MODIFY_QP(dev, qp->state, IB_QPS_RESET, qp->qpn, 0,
- NULL, 0, &status);
+ NULL, 0);
/*
* If this is a userspace QP, the buffers, MR, CQs and so on
@@ -2263,7 +2251,6 @@ void mthca_free_err_wqe(struct mthca_dev *dev, struct mthca_qp *qp, int is_send,
int mthca_init_qp_table(struct mthca_dev *dev)
{
int err;
- u8 status;
int i;
spin_lock_init(&dev->qp_table.lock);
@@ -2290,15 +2277,10 @@ int mthca_init_qp_table(struct mthca_dev *dev)
for (i = 0; i < 2; ++i) {
err = mthca_CONF_SPECIAL_QP(dev, i ? IB_QPT_GSI : IB_QPT_SMI,
- dev->qp_table.sqp_start + i * 2,
- &status);
- if (err)
- goto err_out;
- if (status) {
+ dev->qp_table.sqp_start + i * 2);
+ if (err) {
mthca_warn(dev, "CONF_SPECIAL_QP returned "
- "status %02x, aborting.\n",
- status);
- err = -EINVAL;
+ "%d, aborting.\n", err);
goto err_out;
}
}
@@ -2306,7 +2288,7 @@ int mthca_init_qp_table(struct mthca_dev *dev)
err_out:
for (i = 0; i < 2; ++i)
- mthca_CONF_SPECIAL_QP(dev, i, 0, &status);
+ mthca_CONF_SPECIAL_QP(dev, i, 0);
mthca_array_cleanup(&dev->qp_table.qp, dev->limits.num_qps);
mthca_alloc_cleanup(&dev->qp_table.alloc);
@@ -2317,10 +2299,9 @@ int mthca_init_qp_table(struct mthca_dev *dev)
void mthca_cleanup_qp_table(struct mthca_dev *dev)
{
int i;
- u8 status;
for (i = 0; i < 2; ++i)
- mthca_CONF_SPECIAL_QP(dev, i, 0, &status);
+ mthca_CONF_SPECIAL_QP(dev, i, 0);
mthca_array_cleanup(&dev->qp_table.qp, dev->limits.num_qps);
mthca_alloc_cleanup(&dev->qp_table.alloc);
diff --git a/drivers/infiniband/hw/mthca/mthca_reset.c b/drivers/infiniband/hw/mthca/mthca_reset.c
index 2a13a163d337..4fa3534ec233 100644
--- a/drivers/infiniband/hw/mthca/mthca_reset.c
+++ b/drivers/infiniband/hw/mthca/mthca_reset.c
@@ -113,7 +113,7 @@ int mthca_reset(struct mthca_dev *mdev)
}
hca_pcix_cap = pci_find_capability(mdev->pdev, PCI_CAP_ID_PCIX);
- hca_pcie_cap = pci_find_capability(mdev->pdev, PCI_CAP_ID_EXP);
+ hca_pcie_cap = pci_pcie_cap(mdev->pdev);
if (bridge) {
bridge_header = kmalloc(256, GFP_KERNEL);
diff --git a/drivers/infiniband/hw/mthca/mthca_srq.c b/drivers/infiniband/hw/mthca/mthca_srq.c
index 4fabe62aab8a..d22f970480c0 100644
--- a/drivers/infiniband/hw/mthca/mthca_srq.c
+++ b/drivers/infiniband/hw/mthca/mthca_srq.c
@@ -200,7 +200,6 @@ int mthca_alloc_srq(struct mthca_dev *dev, struct mthca_pd *pd,
struct ib_srq_attr *attr, struct mthca_srq *srq)
{
struct mthca_mailbox *mailbox;
- u8 status;
int ds;
int err;
@@ -266,18 +265,12 @@ int mthca_alloc_srq(struct mthca_dev *dev, struct mthca_pd *pd,
else
mthca_tavor_init_srq_context(dev, pd, srq, mailbox->buf);
- err = mthca_SW2HW_SRQ(dev, mailbox, srq->srqn, &status);
+ err = mthca_SW2HW_SRQ(dev, mailbox, srq->srqn);
if (err) {
mthca_warn(dev, "SW2HW_SRQ failed (%d)\n", err);
goto err_out_free_buf;
}
- if (status) {
- mthca_warn(dev, "SW2HW_SRQ returned status 0x%02x\n",
- status);
- err = -EINVAL;
- goto err_out_free_buf;
- }
spin_lock_irq(&dev->srq_table.lock);
if (mthca_array_set(&dev->srq_table.srq,
@@ -299,11 +292,9 @@ int mthca_alloc_srq(struct mthca_dev *dev, struct mthca_pd *pd,
return 0;
err_out_free_srq:
- err = mthca_HW2SW_SRQ(dev, mailbox, srq->srqn, &status);
+ err = mthca_HW2SW_SRQ(dev, mailbox, srq->srqn);
if (err)
mthca_warn(dev, "HW2SW_SRQ failed (%d)\n", err);
- else if (status)
- mthca_warn(dev, "HW2SW_SRQ returned status 0x%02x\n", status);
err_out_free_buf:
if (!pd->ibpd.uobject)
@@ -340,7 +331,6 @@ void mthca_free_srq(struct mthca_dev *dev, struct mthca_srq *srq)
{
struct mthca_mailbox *mailbox;
int err;
- u8 status;
mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
if (IS_ERR(mailbox)) {
@@ -348,11 +338,9 @@ void mthca_free_srq(struct mthca_dev *dev, struct mthca_srq *srq)
return;
}
- err = mthca_HW2SW_SRQ(dev, mailbox, srq->srqn, &status);
+ err = mthca_HW2SW_SRQ(dev, mailbox, srq->srqn);
if (err)
mthca_warn(dev, "HW2SW_SRQ failed (%d)\n", err);
- else if (status)
- mthca_warn(dev, "HW2SW_SRQ returned status 0x%02x\n", status);
spin_lock_irq(&dev->srq_table.lock);
mthca_array_clear(&dev->srq_table.srq,
@@ -378,8 +366,7 @@ int mthca_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
{
struct mthca_dev *dev = to_mdev(ibsrq->device);
struct mthca_srq *srq = to_msrq(ibsrq);
- int ret;
- u8 status;
+ int ret = 0;
/* We don't support resizing SRQs (yet?) */
if (attr_mask & IB_SRQ_MAX_WR)
@@ -391,16 +378,11 @@ int mthca_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
return -EINVAL;
mutex_lock(&srq->mutex);
- ret = mthca_ARM_SRQ(dev, srq->srqn, attr->srq_limit, &status);
+ ret = mthca_ARM_SRQ(dev, srq->srqn, attr->srq_limit);
mutex_unlock(&srq->mutex);
-
- if (ret)
- return ret;
- if (status)
- return -EINVAL;
}
- return 0;
+ return ret;
}
int mthca_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr)
@@ -410,14 +392,13 @@ int mthca_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr)
struct mthca_mailbox *mailbox;
struct mthca_arbel_srq_context *arbel_ctx;
struct mthca_tavor_srq_context *tavor_ctx;
- u8 status;
int err;
mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
if (IS_ERR(mailbox))
return PTR_ERR(mailbox);
- err = mthca_QUERY_SRQ(dev, srq->srqn, mailbox, &status);
+ err = mthca_QUERY_SRQ(dev, srq->srqn, mailbox);
if (err)
goto out;
diff --git a/drivers/infiniband/hw/nes/Makefile b/drivers/infiniband/hw/nes/Makefile
index 35148513c47e..97820c23ecef 100644
--- a/drivers/infiniband/hw/nes/Makefile
+++ b/drivers/infiniband/hw/nes/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_INFINIBAND_NES) += iw_nes.o
-iw_nes-objs := nes.o nes_hw.o nes_nic.o nes_utils.o nes_verbs.o nes_cm.o
+iw_nes-objs := nes.o nes_hw.o nes_nic.o nes_utils.o nes_verbs.o nes_cm.o nes_mgt.o
diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c
index 2d668c69f6d9..5965b3df8f2f 100644
--- a/drivers/infiniband/hw/nes/nes.c
+++ b/drivers/infiniband/hw/nes/nes.c
@@ -84,7 +84,7 @@ module_param(send_first, int, 0644);
MODULE_PARM_DESC(send_first, "Send RDMA Message First on Active Connection");
-unsigned int nes_drv_opt = 0;
+unsigned int nes_drv_opt = NES_DRV_OPT_DISABLE_INT_MOD | NES_DRV_OPT_ENABLE_PAU;
module_param(nes_drv_opt, int, 0644);
MODULE_PARM_DESC(nes_drv_opt, "Driver option parameters");
@@ -130,9 +130,6 @@ static struct notifier_block nes_net_notifier = {
.notifier_call = nes_net_event
};
-
-
-
/**
* nes_inetaddr_event
*/
@@ -321,6 +318,9 @@ void nes_rem_ref(struct ib_qp *ibqp)
}
if (atomic_dec_and_test(&nesqp->refcount)) {
+ if (nesqp->pau_mode)
+ nes_destroy_pau_qp(nesdev, nesqp);
+
/* Destroy the QP */
cqp_request = nes_get_cqp_request(nesdev);
if (cqp_request == NULL) {
diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h
index 6fe79876009e..568b4f11380a 100644
--- a/drivers/infiniband/hw/nes/nes.h
+++ b/drivers/infiniband/hw/nes/nes.h
@@ -102,6 +102,7 @@
#define NES_DRV_OPT_NO_INLINE_DATA 0x00000080
#define NES_DRV_OPT_DISABLE_INT_MOD 0x00000100
#define NES_DRV_OPT_DISABLE_VIRT_WQ 0x00000200
+#define NES_DRV_OPT_ENABLE_PAU 0x00000400
#define NES_AEQ_EVENT_TIMEOUT 2500
#define NES_DISCONNECT_EVENT_TIMEOUT 2000
@@ -128,6 +129,7 @@
#define NES_DBG_IW_RX 0x00020000
#define NES_DBG_IW_TX 0x00040000
#define NES_DBG_SHUTDOWN 0x00080000
+#define NES_DBG_PAU 0x00100000
#define NES_DBG_RSVD1 0x10000000
#define NES_DBG_RSVD2 0x20000000
#define NES_DBG_RSVD3 0x40000000
@@ -162,6 +164,7 @@ do { \
#include "nes_context.h"
#include "nes_user.h"
#include "nes_cm.h"
+#include "nes_mgt.h"
extern int max_mtu;
#define max_frame_len (max_mtu+ETH_HLEN)
@@ -202,6 +205,8 @@ extern atomic_t cm_nodes_created;
extern atomic_t cm_nodes_destroyed;
extern atomic_t cm_accel_dropped_pkts;
extern atomic_t cm_resets_recvd;
+extern atomic_t pau_qps_created;
+extern atomic_t pau_qps_destroyed;
extern u32 int_mod_timer_init;
extern u32 int_mod_cq_depth_256;
@@ -273,6 +278,14 @@ struct nes_device {
u8 link_recheck;
};
+/* Receive skb private area - must fit in skb->cb area */
+struct nes_rskb_cb {
+ u64 busaddr;
+ u32 maplen;
+ u32 seqnum;
+ u8 *data_start;
+ struct nes_qp *nesqp;
+};
static inline __le32 get_crc_value(struct nes_v4_quad *nes_quad)
{
@@ -305,8 +318,8 @@ set_wqe_32bit_value(__le32 *wqe_words, u32 index, u32 value)
static inline void
nes_fill_init_cqp_wqe(struct nes_hw_cqp_wqe *cqp_wqe, struct nes_device *nesdev)
{
- set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_COMP_CTX_LOW_IDX,
- (u64)((unsigned long) &nesdev->cqp));
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_LOW_IDX] = 0;
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_HIGH_IDX] = 0;
cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] = 0;
cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] = 0;
cqp_wqe->wqe_words[NES_CQP_STAG_WQE_PBL_BLK_COUNT_IDX] = 0;
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index e74cdf9ef471..dfce9ea98a39 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -34,7 +34,7 @@
#define TCPOPT_TIMESTAMP 8
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/tcp.h>
@@ -77,26 +77,19 @@ atomic_t cm_nodes_destroyed;
atomic_t cm_accel_dropped_pkts;
atomic_t cm_resets_recvd;
-static inline int mini_cm_accelerated(struct nes_cm_core *,
- struct nes_cm_node *);
-static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *,
- struct nes_vnic *, struct nes_cm_info *);
+static inline int mini_cm_accelerated(struct nes_cm_core *, struct nes_cm_node *);
+static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *, struct nes_vnic *, struct nes_cm_info *);
static int mini_cm_del_listen(struct nes_cm_core *, struct nes_cm_listener *);
-static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *,
- struct nes_vnic *, u16, void *, struct nes_cm_info *);
+static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *, struct nes_vnic *, u16, void *, struct nes_cm_info *);
static int mini_cm_close(struct nes_cm_core *, struct nes_cm_node *);
-static int mini_cm_accept(struct nes_cm_core *, struct ietf_mpa_frame *,
- struct nes_cm_node *);
-static int mini_cm_reject(struct nes_cm_core *, struct ietf_mpa_frame *,
- struct nes_cm_node *);
-static int mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *,
- struct sk_buff *);
+static int mini_cm_accept(struct nes_cm_core *, struct nes_cm_node *);
+static int mini_cm_reject(struct nes_cm_core *, struct nes_cm_node *);
+static int mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *, struct sk_buff *);
static int mini_cm_dealloc_core(struct nes_cm_core *);
static int mini_cm_get(struct nes_cm_core *);
static int mini_cm_set(struct nes_cm_core *, u32, u32);
-static void form_cm_frame(struct sk_buff *, struct nes_cm_node *,
- void *, u32, void *, u32, u8);
+static void form_cm_frame(struct sk_buff *, struct nes_cm_node *, void *, u32, void *, u32, u8);
static int add_ref_cm_node(struct nes_cm_node *);
static int rem_ref_cm_node(struct nes_cm_core *, struct nes_cm_node *);
@@ -111,16 +104,14 @@ static int send_syn(struct nes_cm_node *, u32, struct sk_buff *);
static int send_reset(struct nes_cm_node *, struct sk_buff *);
static int send_ack(struct nes_cm_node *cm_node, struct sk_buff *skb);
static int send_fin(struct nes_cm_node *cm_node, struct sk_buff *skb);
-static void process_packet(struct nes_cm_node *, struct sk_buff *,
- struct nes_cm_core *);
+static void process_packet(struct nes_cm_node *, struct sk_buff *, struct nes_cm_core *);
static void active_open_err(struct nes_cm_node *, struct sk_buff *, int);
static void passive_open_err(struct nes_cm_node *, struct sk_buff *, int);
static void cleanup_retrans_entry(struct nes_cm_node *);
static void handle_rcv_mpa(struct nes_cm_node *, struct sk_buff *);
static void free_retrans_entry(struct nes_cm_node *cm_node);
-static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
- struct sk_buff *skb, int optionsize, int passive);
+static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph, struct sk_buff *skb, int optionsize, int passive);
/* CM event handler functions */
static void cm_event_connected(struct nes_cm_event *);
@@ -130,6 +121,12 @@ static void cm_event_mpa_req(struct nes_cm_event *);
static void cm_event_mpa_reject(struct nes_cm_event *);
static void handle_recv_entry(struct nes_cm_node *cm_node, u32 rem_node);
+/* MPA build functions */
+static int cm_build_mpa_frame(struct nes_cm_node *, u8 **, u16 *, u8 *, u8);
+static void build_mpa_v2(struct nes_cm_node *, void *, u8);
+static void build_mpa_v1(struct nes_cm_node *, void *, u8);
+static void build_rdma0_msg(struct nes_cm_node *, struct nes_qp **);
+
static void print_core(struct nes_cm_core *core);
/* External CM API Interface */
@@ -159,12 +156,21 @@ atomic_t cm_connecteds;
atomic_t cm_connect_reqs;
atomic_t cm_rejects;
+int nes_add_ref_cm_node(struct nes_cm_node *cm_node)
+{
+ return add_ref_cm_node(cm_node);
+}
+
+int nes_rem_ref_cm_node(struct nes_cm_node *cm_node)
+{
+ return rem_ref_cm_node(cm_node->cm_core, cm_node);
+}
/**
* create_event
*/
-static struct nes_cm_event *create_event(struct nes_cm_node *cm_node,
- enum nes_cm_event_type type)
+static struct nes_cm_event *create_event(struct nes_cm_node * cm_node,
+ enum nes_cm_event_type type)
{
struct nes_cm_event *event;
@@ -186,10 +192,10 @@ static struct nes_cm_event *create_event(struct nes_cm_node *cm_node,
event->cm_info.cm_id = cm_node->cm_id;
nes_debug(NES_DBG_CM, "cm_node=%p Created event=%p, type=%u, "
- "dst_addr=%08x[%x], src_addr=%08x[%x]\n",
- cm_node, event, type, event->cm_info.loc_addr,
- event->cm_info.loc_port, event->cm_info.rem_addr,
- event->cm_info.rem_port);
+ "dst_addr=%08x[%x], src_addr=%08x[%x]\n",
+ cm_node, event, type, event->cm_info.loc_addr,
+ event->cm_info.loc_port, event->cm_info.rem_addr,
+ event->cm_info.rem_port);
nes_cm_post_event(event);
return event;
@@ -201,14 +207,19 @@ static struct nes_cm_event *create_event(struct nes_cm_node *cm_node,
*/
static int send_mpa_request(struct nes_cm_node *cm_node, struct sk_buff *skb)
{
+ u8 start_addr = 0;
+ u8 *start_ptr = &start_addr;
+ u8 **start_buff = &start_ptr;
+ u16 buff_len = 0;
+
if (!skb) {
nes_debug(NES_DBG_CM, "skb set to NULL\n");
return -1;
}
/* send an MPA Request frame */
- form_cm_frame(skb, cm_node, NULL, 0, &cm_node->mpa_frame,
- cm_node->mpa_frame_size, SET_ACK);
+ cm_build_mpa_frame(cm_node, start_buff, &buff_len, NULL, MPA_KEY_REQUEST);
+ form_cm_frame(skb, cm_node, NULL, 0, *start_buff, buff_len, SET_ACK);
return schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
}
@@ -217,7 +228,11 @@ static int send_mpa_request(struct nes_cm_node *cm_node, struct sk_buff *skb)
static int send_mpa_reject(struct nes_cm_node *cm_node)
{
- struct sk_buff *skb = NULL;
+ struct sk_buff *skb = NULL;
+ u8 start_addr = 0;
+ u8 *start_ptr = &start_addr;
+ u8 **start_buff = &start_ptr;
+ u16 buff_len = 0;
skb = dev_alloc_skb(MAX_CM_BUFFER);
if (!skb) {
@@ -226,8 +241,8 @@ static int send_mpa_reject(struct nes_cm_node *cm_node)
}
/* send an MPA reject frame */
- form_cm_frame(skb, cm_node, NULL, 0, &cm_node->mpa_frame,
- cm_node->mpa_frame_size, SET_ACK | SET_FIN);
+ cm_build_mpa_frame(cm_node, start_buff, &buff_len, NULL, MPA_KEY_REPLY);
+ form_cm_frame(skb, cm_node, NULL, 0, *start_buff, buff_len, SET_ACK | SET_FIN);
cm_node->state = NES_CM_STATE_FIN_WAIT1;
return schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
@@ -239,24 +254,31 @@ static int send_mpa_reject(struct nes_cm_node *cm_node)
* IETF MPA frame
*/
static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
- u32 len)
+ u32 len)
{
- struct ietf_mpa_frame *mpa_frame;
+ struct ietf_mpa_v1 *mpa_frame;
+ struct ietf_mpa_v2 *mpa_v2_frame;
+ struct ietf_rtr_msg *rtr_msg;
+ int mpa_hdr_len;
+ int priv_data_len;
*type = NES_MPA_REQUEST_ACCEPT;
/* assume req frame is in tcp data payload */
- if (len < sizeof(struct ietf_mpa_frame)) {
+ if (len < sizeof(struct ietf_mpa_v1)) {
nes_debug(NES_DBG_CM, "The received ietf buffer was too small (%x)\n", len);
return -EINVAL;
}
- mpa_frame = (struct ietf_mpa_frame *)buffer;
- cm_node->mpa_frame_size = ntohs(mpa_frame->priv_data_len);
+ /* points to the beginning of the frame, which could be MPA V1 or V2 */
+ mpa_frame = (struct ietf_mpa_v1 *)buffer;
+ mpa_hdr_len = sizeof(struct ietf_mpa_v1);
+ priv_data_len = ntohs(mpa_frame->priv_data_len);
+
/* make sure mpa private data len is less than 512 bytes */
- if (cm_node->mpa_frame_size > IETF_MAX_PRIV_DATA_LEN) {
+ if (priv_data_len > IETF_MAX_PRIV_DATA_LEN) {
nes_debug(NES_DBG_CM, "The received Length of Private"
- " Data field exceeds 512 octets\n");
+ " Data field exceeds 512 octets\n");
return -EINVAL;
}
/*
@@ -264,11 +286,22 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
* received MPA version and MPA key information
*
*/
- if (mpa_frame->rev != mpa_version) {
+ if (mpa_frame->rev != IETF_MPA_V1 && mpa_frame->rev != IETF_MPA_V2) {
+ nes_debug(NES_DBG_CM, "The received mpa version"
+ " is not supported\n");
+ return -EINVAL;
+ }
+ /*
+ * backwards compatibility only
+ */
+ if (mpa_frame->rev > cm_node->mpa_frame_rev) {
nes_debug(NES_DBG_CM, "The received mpa version"
- " can not be interoperated\n");
+ " can not be interoperated\n");
return -EINVAL;
+ } else {
+ cm_node->mpa_frame_rev = mpa_frame->rev;
}
+
if (cm_node->state != NES_CM_STATE_MPAREQ_SENT) {
if (memcmp(mpa_frame->key, IEFT_MPA_KEY_REQ, IETF_MPA_KEY_SIZE)) {
nes_debug(NES_DBG_CM, "Unexpected MPA Key received \n");
@@ -281,25 +314,75 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
}
}
- if (cm_node->mpa_frame_size + sizeof(struct ietf_mpa_frame) != len) {
+
+ if (priv_data_len + mpa_hdr_len != len) {
nes_debug(NES_DBG_CM, "The received ietf buffer was not right"
- " complete (%x + %x != %x)\n",
- cm_node->mpa_frame_size,
- (u32)sizeof(struct ietf_mpa_frame), len);
+ " complete (%x + %x != %x)\n",
+ priv_data_len, mpa_hdr_len, len);
return -EINVAL;
}
/* make sure it does not exceed the max size */
if (len > MAX_CM_BUFFER) {
nes_debug(NES_DBG_CM, "The received ietf buffer was too large"
- " (%x + %x != %x)\n",
- cm_node->mpa_frame_size,
- (u32)sizeof(struct ietf_mpa_frame), len);
+ " (%x + %x != %x)\n",
+ priv_data_len, mpa_hdr_len, len);
return -EINVAL;
}
+ cm_node->mpa_frame_size = priv_data_len;
+
+ switch (mpa_frame->rev) {
+ case IETF_MPA_V2: {
+ u16 ird_size;
+ u16 ord_size;
+ mpa_v2_frame = (struct ietf_mpa_v2 *)buffer;
+ mpa_hdr_len += IETF_RTR_MSG_SIZE;
+ cm_node->mpa_frame_size -= IETF_RTR_MSG_SIZE;
+ rtr_msg = &mpa_v2_frame->rtr_msg;
+
+ /* parse rtr message */
+ rtr_msg->ctrl_ird = ntohs(rtr_msg->ctrl_ird);
+ rtr_msg->ctrl_ord = ntohs(rtr_msg->ctrl_ord);
+ ird_size = rtr_msg->ctrl_ird & IETF_NO_IRD_ORD;
+ ord_size = rtr_msg->ctrl_ord & IETF_NO_IRD_ORD;
+
+ if (!(rtr_msg->ctrl_ird & IETF_PEER_TO_PEER)) {
+ /* send reset */
+ return -EINVAL;
+ }
+
+ if (cm_node->state != NES_CM_STATE_MPAREQ_SENT) {
+ /* responder */
+ if (cm_node->ord_size > ird_size)
+ cm_node->ord_size = ird_size;
+ } else {
+ /* initiator */
+ if (cm_node->ord_size > ird_size)
+ cm_node->ord_size = ird_size;
+
+ if (cm_node->ird_size < ord_size) {
+ /* no resources available */
+ /* send terminate message */
+ return -EINVAL;
+ }
+ }
+
+ if (rtr_msg->ctrl_ord & IETF_RDMA0_READ) {
+ cm_node->send_rdma0_op = SEND_RDMA_READ_ZERO;
+ } else if (rtr_msg->ctrl_ord & IETF_RDMA0_WRITE) {
+ cm_node->send_rdma0_op = SEND_RDMA_WRITE_ZERO;
+ } else { /* Not supported RDMA0 operation */
+ return -EINVAL;
+ }
+ break;
+ }
+ case IETF_MPA_V1:
+ default:
+ break;
+ }
+
/* copy entire MPA frame to our cm_node's frame */
- memcpy(cm_node->mpa_frame_buf, buffer + sizeof(struct ietf_mpa_frame),
- cm_node->mpa_frame_size);
+ memcpy(cm_node->mpa_frame_buf, buffer + mpa_hdr_len, cm_node->mpa_frame_size);
if (mpa_frame->flags & IETF_MPA_FLAGS_REJECT)
*type = NES_MPA_REQUEST_REJECT;
@@ -312,8 +395,8 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
* node info to build.
*/
static void form_cm_frame(struct sk_buff *skb,
- struct nes_cm_node *cm_node, void *options, u32 optionsize,
- void *data, u32 datasize, u8 flags)
+ struct nes_cm_node *cm_node, void *options, u32 optionsize,
+ void *data, u32 datasize, u8 flags)
{
struct tcphdr *tcph;
struct iphdr *iph;
@@ -322,14 +405,14 @@ static void form_cm_frame(struct sk_buff *skb,
u16 packetsize = sizeof(*iph);
packetsize += sizeof(*tcph);
- packetsize += optionsize + datasize;
+ packetsize += optionsize + datasize;
+ skb_trim(skb, 0);
memset(skb->data, 0x00, ETH_HLEN + sizeof(*iph) + sizeof(*tcph));
- skb->len = 0;
buf = skb_put(skb, packetsize + ETH_HLEN);
- ethh = (struct ethhdr *) buf;
+ ethh = (struct ethhdr *)buf;
buf += ETH_HLEN;
iph = (struct iphdr *)buf;
@@ -337,7 +420,7 @@ static void form_cm_frame(struct sk_buff *skb,
tcph = (struct tcphdr *)buf;
skb_reset_mac_header(skb);
skb_set_network_header(skb, ETH_HLEN);
- skb_set_transport_header(skb, ETH_HLEN+sizeof(*iph));
+ skb_set_transport_header(skb, ETH_HLEN + sizeof(*iph));
buf += sizeof(*tcph);
skb->ip_summed = CHECKSUM_PARTIAL;
@@ -350,14 +433,14 @@ static void form_cm_frame(struct sk_buff *skb,
ethh->h_proto = htons(0x0800);
iph->version = IPVERSION;
- iph->ihl = 5; /* 5 * 4Byte words, IP headr len */
+ iph->ihl = 5; /* 5 * 4Byte words, IP headr len */
iph->tos = 0;
iph->tot_len = htons(packetsize);
iph->id = htons(++cm_node->tcp_cntxt.loc_id);
iph->frag_off = htons(0x4000);
iph->ttl = 0x40;
- iph->protocol = 0x06; /* IPPROTO_TCP */
+ iph->protocol = 0x06; /* IPPROTO_TCP */
iph->saddr = htonl(cm_node->loc_addr);
iph->daddr = htonl(cm_node->rem_addr);
@@ -370,14 +453,16 @@ static void form_cm_frame(struct sk_buff *skb,
cm_node->tcp_cntxt.loc_ack_num = cm_node->tcp_cntxt.rcv_nxt;
tcph->ack_seq = htonl(cm_node->tcp_cntxt.loc_ack_num);
tcph->ack = 1;
- } else
+ } else {
tcph->ack_seq = 0;
+ }
if (flags & SET_SYN) {
cm_node->tcp_cntxt.loc_seq_num++;
tcph->syn = 1;
- } else
+ } else {
cm_node->tcp_cntxt.loc_seq_num += datasize;
+ }
if (flags & SET_FIN) {
cm_node->tcp_cntxt.loc_seq_num++;
@@ -398,10 +483,8 @@ static void form_cm_frame(struct sk_buff *skb,
skb_shinfo(skb)->nr_frags = 0;
cm_packets_created++;
-
}
-
/**
* print_core - dump a cm core
*/
@@ -413,7 +496,7 @@ static void print_core(struct nes_cm_core *core)
return;
nes_debug(NES_DBG_CM, "---------------------------------------------\n");
- nes_debug(NES_DBG_CM, "State : %u \n", core->state);
+ nes_debug(NES_DBG_CM, "State : %u \n", core->state);
nes_debug(NES_DBG_CM, "Listen Nodes : %u \n", atomic_read(&core->listen_node_cnt));
nes_debug(NES_DBG_CM, "Active Nodes : %u \n", atomic_read(&core->node_cnt));
@@ -423,6 +506,147 @@ static void print_core(struct nes_cm_core *core)
nes_debug(NES_DBG_CM, "-------------- end core ---------------\n");
}
+/**
+ * cm_build_mpa_frame - build a MPA V1 frame or MPA V2 frame
+ */
+static int cm_build_mpa_frame(struct nes_cm_node *cm_node, u8 **start_buff,
+ u16 *buff_len, u8 *pci_mem, u8 mpa_key)
+{
+ int ret = 0;
+
+ *start_buff = (pci_mem) ? pci_mem : &cm_node->mpa_frame_buf[0];
+
+ switch (cm_node->mpa_frame_rev) {
+ case IETF_MPA_V1:
+ *start_buff = (u8 *)*start_buff + sizeof(struct ietf_rtr_msg);
+ *buff_len = sizeof(struct ietf_mpa_v1) + cm_node->mpa_frame_size;
+ build_mpa_v1(cm_node, *start_buff, mpa_key);
+ break;
+ case IETF_MPA_V2:
+ *buff_len = sizeof(struct ietf_mpa_v2) + cm_node->mpa_frame_size;
+ build_mpa_v2(cm_node, *start_buff, mpa_key);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+/**
+ * build_mpa_v2 - build a MPA V2 frame
+ */
+static void build_mpa_v2(struct nes_cm_node *cm_node,
+ void *start_addr, u8 mpa_key)
+{
+ struct ietf_mpa_v2 *mpa_frame = (struct ietf_mpa_v2 *)start_addr;
+ struct ietf_rtr_msg *rtr_msg = &mpa_frame->rtr_msg;
+
+ /* initialize the upper 5 bytes of the frame */
+ build_mpa_v1(cm_node, start_addr, mpa_key);
+ mpa_frame->flags |= IETF_MPA_V2_FLAG; /* set a bit to indicate MPA V2 */
+ mpa_frame->priv_data_len += htons(IETF_RTR_MSG_SIZE);
+
+ /* initialize RTR msg */
+ rtr_msg->ctrl_ird = (cm_node->ird_size > IETF_NO_IRD_ORD) ?
+ IETF_NO_IRD_ORD : cm_node->ird_size;
+ rtr_msg->ctrl_ord = (cm_node->ord_size > IETF_NO_IRD_ORD) ?
+ IETF_NO_IRD_ORD : cm_node->ord_size;
+
+ rtr_msg->ctrl_ird |= IETF_PEER_TO_PEER;
+ rtr_msg->ctrl_ird |= IETF_FLPDU_ZERO_LEN;
+
+ switch (mpa_key) {
+ case MPA_KEY_REQUEST:
+ rtr_msg->ctrl_ord |= IETF_RDMA0_WRITE;
+ rtr_msg->ctrl_ord |= IETF_RDMA0_READ;
+ break;
+ case MPA_KEY_REPLY:
+ switch (cm_node->send_rdma0_op) {
+ case SEND_RDMA_WRITE_ZERO:
+ rtr_msg->ctrl_ord |= IETF_RDMA0_WRITE;
+ break;
+ case SEND_RDMA_READ_ZERO:
+ rtr_msg->ctrl_ord |= IETF_RDMA0_READ;
+ break;
+ }
+ }
+ rtr_msg->ctrl_ird = htons(rtr_msg->ctrl_ird);
+ rtr_msg->ctrl_ord = htons(rtr_msg->ctrl_ord);
+}
+
+/**
+ * build_mpa_v1 - build a MPA V1 frame
+ */
+static void build_mpa_v1(struct nes_cm_node *cm_node, void *start_addr, u8 mpa_key)
+{
+ struct ietf_mpa_v1 *mpa_frame = (struct ietf_mpa_v1 *)start_addr;
+
+ switch (mpa_key) {
+ case MPA_KEY_REQUEST:
+ memcpy(mpa_frame->key, IEFT_MPA_KEY_REQ, IETF_MPA_KEY_SIZE);
+ break;
+ case MPA_KEY_REPLY:
+ memcpy(mpa_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
+ break;
+ }
+ mpa_frame->flags = IETF_MPA_FLAGS_CRC;
+ mpa_frame->rev = cm_node->mpa_frame_rev;
+ mpa_frame->priv_data_len = htons(cm_node->mpa_frame_size);
+}
+
+static void build_rdma0_msg(struct nes_cm_node *cm_node, struct nes_qp **nesqp_addr)
+{
+ u64 u64temp;
+ struct nes_qp *nesqp = *nesqp_addr;
+ struct nes_hw_qp_wqe *wqe = &nesqp->hwqp.sq_vbase[0];
+
+ u64temp = (unsigned long)nesqp;
+ u64temp |= NES_SW_CONTEXT_ALIGN >> 1;
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, u64temp);
+
+ wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = 0;
+
+ switch (cm_node->send_rdma0_op) {
+ case SEND_RDMA_WRITE_ZERO:
+ nes_debug(NES_DBG_CM, "Sending first write.\n");
+ wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
+ cpu_to_le32(NES_IWARP_SQ_OP_RDMAW);
+ wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
+ break;
+
+ case SEND_RDMA_READ_ZERO:
+ default:
+ if (cm_node->send_rdma0_op != SEND_RDMA_READ_ZERO) {
+ printk(KERN_ERR "%s[%u]: Unsupported RDMA0 len operation=%u\n",
+ __func__, __LINE__, cm_node->send_rdma0_op);
+ WARN_ON(1);
+ }
+ nes_debug(NES_DBG_CM, "Sending first rdma operation.\n");
+ wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
+ cpu_to_le32(NES_IWARP_SQ_OP_RDMAR);
+ wqe->wqe_words[NES_IWARP_SQ_WQE_RDMA_TO_LOW_IDX] = 1;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_RDMA_TO_HIGH_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_RDMA_LENGTH_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_RDMA_STAG_IDX] = 1;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 1;
+ break;
+ }
+
+ if (nesqp->sq_kmapped) {
+ nesqp->sq_kmapped = 0;
+ kunmap(nesqp->page);
+ }
+
+ /*use the reserved spot on the WQ for the extra first WQE*/
+ nesqp->nesqp_context->ird_ord_sizes &= cpu_to_le32(~(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
+ NES_QPCONTEXT_ORDIRD_WRPDU |
+ NES_QPCONTEXT_ORDIRD_ALSMM));
+ nesqp->skip_lsmm = 1;
+ nesqp->hwqp.sq_tail = 0;
+}
/**
* schedule_nes_timer
@@ -430,10 +654,10 @@ static void print_core(struct nes_cm_core *core)
* rem_ref_cm_node(cm_core, cm_node);add_ref_cm_node(cm_node);
*/
int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
- enum nes_timer_type type, int send_retrans,
- int close_when_complete)
+ enum nes_timer_type type, int send_retrans,
+ int close_when_complete)
{
- unsigned long flags;
+ unsigned long flags;
struct nes_cm_core *cm_core = cm_node->cm_core;
struct nes_timer_entry *new_send;
int ret = 0;
@@ -454,7 +678,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
new_send->close_when_complete = close_when_complete;
if (type == NES_TIMER_TYPE_CLOSE) {
- new_send->timetosend += (HZ/10);
+ new_send->timetosend += (HZ / 10);
if (cm_node->recv_entry) {
kfree(new_send);
WARN_ON(1);
@@ -475,7 +699,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
ret = nes_nic_cm_xmit(new_send->skb, cm_node->netdev);
if (ret != NETDEV_TX_OK) {
nes_debug(NES_DBG_CM, "Error sending packet %p "
- "(jiffies = %lu)\n", new_send, jiffies);
+ "(jiffies = %lu)\n", new_send, jiffies);
new_send->timetosend = jiffies;
ret = NETDEV_TX_OK;
} else {
@@ -504,6 +728,7 @@ static void nes_retrans_expired(struct nes_cm_node *cm_node)
struct iw_cm_id *cm_id = cm_node->cm_id;
enum nes_cm_node_state state = cm_node->state;
cm_node->state = NES_CM_STATE_CLOSED;
+
switch (state) {
case NES_CM_STATE_SYN_RCVD:
case NES_CM_STATE_CLOSING:
@@ -536,10 +761,10 @@ static void handle_recv_entry(struct nes_cm_node *cm_node, u32 rem_node)
spin_lock_irqsave(&nesqp->lock, qplockflags);
if (nesqp->cm_id) {
nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, "
- "refcount = %d: HIT A "
- "NES_TIMER_TYPE_CLOSE with something "
- "to do!!!\n", nesqp->hwqp.qp_id, cm_id,
- atomic_read(&nesqp->refcount));
+ "refcount = %d: HIT A "
+ "NES_TIMER_TYPE_CLOSE with something "
+ "to do!!!\n", nesqp->hwqp.qp_id, cm_id,
+ atomic_read(&nesqp->refcount));
nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
nesqp->ibqp_state = IB_QPS_ERR;
@@ -548,10 +773,10 @@ static void handle_recv_entry(struct nes_cm_node *cm_node, u32 rem_node)
} else {
spin_unlock_irqrestore(&nesqp->lock, qplockflags);
nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, "
- "refcount = %d: HIT A "
- "NES_TIMER_TYPE_CLOSE with nothing "
- "to do!!!\n", nesqp->hwqp.qp_id, cm_id,
- atomic_read(&nesqp->refcount));
+ "refcount = %d: HIT A "
+ "NES_TIMER_TYPE_CLOSE with nothing "
+ "to do!!!\n", nesqp->hwqp.qp_id, cm_id,
+ atomic_read(&nesqp->refcount));
}
} else if (rem_node) {
/* TIME_WAIT state */
@@ -580,11 +805,12 @@ static void nes_cm_timer_tick(unsigned long pass)
int ret = NETDEV_TX_OK;
struct list_head timer_list;
+
INIT_LIST_HEAD(&timer_list);
spin_lock_irqsave(&cm_core->ht_lock, flags);
list_for_each_safe(list_node, list_core_temp,
- &cm_core->connected_nodes) {
+ &cm_core->connected_nodes) {
cm_node = container_of(list_node, struct nes_cm_node, list);
if ((cm_node->recv_entry) || (cm_node->send_entry)) {
add_ref_cm_node(cm_node);
@@ -595,18 +821,19 @@ static void nes_cm_timer_tick(unsigned long pass)
list_for_each_safe(list_node, list_core_temp, &timer_list) {
cm_node = container_of(list_node, struct nes_cm_node,
- timer_entry);
+ timer_entry);
recv_entry = cm_node->recv_entry;
if (recv_entry) {
if (time_after(recv_entry->timetosend, jiffies)) {
if (nexttimeout > recv_entry->timetosend ||
- !settimer) {
+ !settimer) {
nexttimeout = recv_entry->timetosend;
settimer = 1;
}
- } else
+ } else {
handle_recv_entry(cm_node, 1);
+ }
}
spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
@@ -617,8 +844,8 @@ static void nes_cm_timer_tick(unsigned long pass)
if (time_after(send_entry->timetosend, jiffies)) {
if (cm_node->state != NES_CM_STATE_TSA) {
if ((nexttimeout >
- send_entry->timetosend) ||
- !settimer) {
+ send_entry->timetosend) ||
+ !settimer) {
nexttimeout =
send_entry->timetosend;
settimer = 1;
@@ -630,13 +857,13 @@ static void nes_cm_timer_tick(unsigned long pass)
}
if ((cm_node->state == NES_CM_STATE_TSA) ||
- (cm_node->state == NES_CM_STATE_CLOSED)) {
+ (cm_node->state == NES_CM_STATE_CLOSED)) {
free_retrans_entry(cm_node);
break;
}
if (!send_entry->retranscount ||
- !send_entry->retrycount) {
+ !send_entry->retrycount) {
cm_packets_dropped++;
free_retrans_entry(cm_node);
@@ -645,28 +872,28 @@ static void nes_cm_timer_tick(unsigned long pass)
nes_retrans_expired(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
spin_lock_irqsave(&cm_node->retrans_list_lock,
- flags);
+ flags);
break;
}
atomic_inc(&send_entry->skb->users);
cm_packets_retrans++;
nes_debug(NES_DBG_CM, "Retransmitting send_entry %p "
- "for node %p, jiffies = %lu, time to send = "
- "%lu, retranscount = %u, send_entry->seq_num = "
- "0x%08X, cm_node->tcp_cntxt.rem_ack_num = "
- "0x%08X\n", send_entry, cm_node, jiffies,
- send_entry->timetosend,
- send_entry->retranscount,
- send_entry->seq_num,
- cm_node->tcp_cntxt.rem_ack_num);
+ "for node %p, jiffies = %lu, time to send = "
+ "%lu, retranscount = %u, send_entry->seq_num = "
+ "0x%08X, cm_node->tcp_cntxt.rem_ack_num = "
+ "0x%08X\n", send_entry, cm_node, jiffies,
+ send_entry->timetosend,
+ send_entry->retranscount,
+ send_entry->seq_num,
+ cm_node->tcp_cntxt.rem_ack_num);
spin_unlock_irqrestore(&cm_node->retrans_list_lock,
- flags);
+ flags);
ret = nes_nic_cm_xmit(send_entry->skb, cm_node->netdev);
spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
if (ret != NETDEV_TX_OK) {
nes_debug(NES_DBG_CM, "rexmit failed for "
- "node=%p\n", cm_node);
+ "node=%p\n", cm_node);
cm_packets_bounced++;
send_entry->retrycount--;
nexttimeout = jiffies + NES_SHORT_TIME;
@@ -676,18 +903,18 @@ static void nes_cm_timer_tick(unsigned long pass)
cm_packets_sent++;
}
nes_debug(NES_DBG_CM, "Packet Sent: retrans count = "
- "%u, retry count = %u.\n",
- send_entry->retranscount,
- send_entry->retrycount);
+ "%u, retry count = %u.\n",
+ send_entry->retranscount,
+ send_entry->retrycount);
if (send_entry->send_retrans) {
send_entry->retranscount--;
timetosend = (NES_RETRY_TIMEOUT <<
- (NES_DEFAULT_RETRANS - send_entry->retranscount));
+ (NES_DEFAULT_RETRANS - send_entry->retranscount));
send_entry->timetosend = jiffies +
- min(timetosend, NES_MAX_TIMEOUT);
+ min(timetosend, NES_MAX_TIMEOUT);
if (nexttimeout > send_entry->timetosend ||
- !settimer) {
+ !settimer) {
nexttimeout = send_entry->timetosend;
settimer = 1;
}
@@ -696,11 +923,11 @@ static void nes_cm_timer_tick(unsigned long pass)
close_when_complete =
send_entry->close_when_complete;
nes_debug(NES_DBG_CM, "cm_node=%p state=%d\n",
- cm_node, cm_node->state);
+ cm_node, cm_node->state);
free_retrans_entry(cm_node);
if (close_when_complete)
rem_ref_cm_node(cm_node->cm_core,
- cm_node);
+ cm_node);
}
} while (0);
@@ -710,7 +937,7 @@ static void nes_cm_timer_tick(unsigned long pass)
if (settimer) {
if (!timer_pending(&cm_core->tcp_timer)) {
- cm_core->tcp_timer.expires = nexttimeout;
+ cm_core->tcp_timer.expires = nexttimeout;
add_timer(&cm_core->tcp_timer);
}
}
@@ -721,13 +948,13 @@ static void nes_cm_timer_tick(unsigned long pass)
* send_syn
*/
static int send_syn(struct nes_cm_node *cm_node, u32 sendack,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
int ret;
int flags = SET_SYN;
char optionsbuffer[sizeof(struct option_mss) +
- sizeof(struct option_windowscale) + sizeof(struct option_base) +
- TCP_OPTIONS_PADDING];
+ sizeof(struct option_windowscale) + sizeof(struct option_base) +
+ TCP_OPTIONS_PADDING];
int optionssize = 0;
/* Sending MSS option */
@@ -854,7 +1081,7 @@ static int send_fin(struct nes_cm_node *cm_node, struct sk_buff *skb)
* find_node - find a cm node that matches the reference cm node
*/
static struct nes_cm_node *find_node(struct nes_cm_core *cm_core,
- u16 rem_port, nes_addr_t rem_addr, u16 loc_port, nes_addr_t loc_addr)
+ u16 rem_port, nes_addr_t rem_addr, u16 loc_port, nes_addr_t loc_addr)
{
unsigned long flags;
struct list_head *hte;
@@ -868,12 +1095,12 @@ static struct nes_cm_node *find_node(struct nes_cm_core *cm_core,
list_for_each_entry(cm_node, hte, list) {
/* compare quad, return node handle if a match */
nes_debug(NES_DBG_CM, "finding node %x:%x =? %x:%x ^ %x:%x =? %x:%x\n",
- cm_node->loc_addr, cm_node->loc_port,
- loc_addr, loc_port,
- cm_node->rem_addr, cm_node->rem_port,
- rem_addr, rem_port);
+ cm_node->loc_addr, cm_node->loc_port,
+ loc_addr, loc_port,
+ cm_node->rem_addr, cm_node->rem_port,
+ rem_addr, rem_port);
if ((cm_node->loc_addr == loc_addr) && (cm_node->loc_port == loc_port) &&
- (cm_node->rem_addr == rem_addr) && (cm_node->rem_port == rem_port)) {
+ (cm_node->rem_addr == rem_addr) && (cm_node->rem_port == rem_port)) {
add_ref_cm_node(cm_node);
spin_unlock_irqrestore(&cm_core->ht_lock, flags);
return cm_node;
@@ -890,7 +1117,7 @@ static struct nes_cm_node *find_node(struct nes_cm_core *cm_core,
* find_listener - find a cm node listening on this addr-port pair
*/
static struct nes_cm_listener *find_listener(struct nes_cm_core *cm_core,
- nes_addr_t dst_addr, u16 dst_port, enum nes_cm_listener_state listener_state)
+ nes_addr_t dst_addr, u16 dst_port, enum nes_cm_listener_state listener_state)
{
unsigned long flags;
struct nes_cm_listener *listen_node;
@@ -900,9 +1127,9 @@ static struct nes_cm_listener *find_listener(struct nes_cm_core *cm_core,
list_for_each_entry(listen_node, &cm_core->listen_list.list, list) {
/* compare node pair, return node handle if a match */
if (((listen_node->loc_addr == dst_addr) ||
- listen_node->loc_addr == 0x00000000) &&
- (listen_node->loc_port == dst_port) &&
- (listener_state & listen_node->listener_state)) {
+ listen_node->loc_addr == 0x00000000) &&
+ (listen_node->loc_port == dst_port) &&
+ (listener_state & listen_node->listener_state)) {
atomic_inc(&listen_node->ref_count);
spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
return listen_node;
@@ -927,7 +1154,7 @@ static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node
return -EINVAL;
nes_debug(NES_DBG_CM, "Adding Node %p to Active Connection HT\n",
- cm_node);
+ cm_node);
spin_lock_irqsave(&cm_core->ht_lock, flags);
@@ -946,7 +1173,7 @@ static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node
* mini_cm_dec_refcnt_listen
*/
static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
- struct nes_cm_listener *listener, int free_hanging_nodes)
+ struct nes_cm_listener *listener, int free_hanging_nodes)
{
int ret = -EINVAL;
int err = 0;
@@ -957,8 +1184,8 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
struct list_head reset_list;
nes_debug(NES_DBG_CM, "attempting listener= %p free_nodes= %d, "
- "refcnt=%d\n", listener, free_hanging_nodes,
- atomic_read(&listener->ref_count));
+ "refcnt=%d\n", listener, free_hanging_nodes,
+ atomic_read(&listener->ref_count));
/* free non-accelerated child nodes for this listener */
INIT_LIST_HEAD(&reset_list);
if (free_hanging_nodes) {
@@ -966,7 +1193,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
list_for_each_safe(list_pos, list_temp,
&g_cm_core->connected_nodes) {
cm_node = container_of(list_pos, struct nes_cm_node,
- list);
+ list);
if ((cm_node->listener == listener) &&
(!cm_node->accelerated)) {
add_ref_cm_node(cm_node);
@@ -978,7 +1205,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
list_for_each_safe(list_pos, list_temp, &reset_list) {
cm_node = container_of(list_pos, struct nes_cm_node,
- reset_entry);
+ reset_entry);
{
struct nes_cm_node *loopback = cm_node->loopbackpartner;
enum nes_cm_node_state old_state;
@@ -990,7 +1217,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
err = send_reset(cm_node, NULL);
if (err) {
cm_node->state =
- NES_CM_STATE_CLOSED;
+ NES_CM_STATE_CLOSED;
WARN_ON(1);
} else {
old_state = cm_node->state;
@@ -1035,10 +1262,9 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
- if (listener->nesvnic) {
+ if (listener->nesvnic)
nes_manage_apbvt(listener->nesvnic, listener->loc_port,
- PCI_FUNC(listener->nesvnic->nesdev->pcidev->devfn), NES_MANAGE_APBVT_DEL);
- }
+ PCI_FUNC(listener->nesvnic->nesdev->pcidev->devfn), NES_MANAGE_APBVT_DEL);
nes_debug(NES_DBG_CM, "destroying listener (%p)\n", listener);
@@ -1052,8 +1278,8 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
if (listener) {
if (atomic_read(&listener->pend_accepts_cnt) > 0)
nes_debug(NES_DBG_CM, "destroying listener (%p)"
- " with non-zero pending accepts=%u\n",
- listener, atomic_read(&listener->pend_accepts_cnt));
+ " with non-zero pending accepts=%u\n",
+ listener, atomic_read(&listener->pend_accepts_cnt));
}
return ret;
@@ -1064,7 +1290,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
* mini_cm_del_listen
*/
static int mini_cm_del_listen(struct nes_cm_core *cm_core,
- struct nes_cm_listener *listener)
+ struct nes_cm_listener *listener)
{
listener->listener_state = NES_CM_LISTENER_PASSIVE_STATE;
listener->cm_id = NULL; /* going to be destroyed pretty soon */
@@ -1076,9 +1302,10 @@ static int mini_cm_del_listen(struct nes_cm_core *cm_core,
* mini_cm_accelerated
*/
static inline int mini_cm_accelerated(struct nes_cm_core *cm_core,
- struct nes_cm_node *cm_node)
+ struct nes_cm_node *cm_node)
{
u32 was_timer_set;
+
cm_node->accelerated = 1;
if (cm_node->accept_pend) {
@@ -1112,7 +1339,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
rt = ip_route_output(&init_net, htonl(dst_ip), 0, 0, 0);
if (IS_ERR(rt)) {
printk(KERN_ERR "%s: ip_route_output_key failed for 0x%08X\n",
- __func__, dst_ip);
+ __func__, dst_ip);
return rc;
}
@@ -1130,7 +1357,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
if (arpindex >= 0) {
if (!memcmp(nesadapter->arp_table[arpindex].mac_addr,
- neigh->ha, ETH_ALEN)){
+ neigh->ha, ETH_ALEN)) {
/* Mac address same as in nes_arp_table */
neigh_release(neigh);
ip_rt_put(rt);
@@ -1138,8 +1365,8 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
}
nes_manage_arp_cache(nesvnic->netdev,
- nesadapter->arp_table[arpindex].mac_addr,
- dst_ip, NES_ARP_DELETE);
+ nesadapter->arp_table[arpindex].mac_addr,
+ dst_ip, NES_ARP_DELETE);
}
nes_manage_arp_cache(nesvnic->netdev, neigh->ha,
@@ -1151,7 +1378,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
}
if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID)))
- neigh_event_send(rt->dst.neighbour, NULL);
+ neigh_event_send(dst_get_neighbour(&rt->dst), NULL);
ip_rt_put(rt);
return rc;
@@ -1161,8 +1388,8 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
* make_cm_node - create a new instance of a cm node
*/
static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
- struct nes_vnic *nesvnic, struct nes_cm_info *cm_info,
- struct nes_cm_listener *listener)
+ struct nes_vnic *nesvnic, struct nes_cm_info *cm_info,
+ struct nes_cm_listener *listener)
{
struct nes_cm_node *cm_node;
struct timespec ts;
@@ -1181,7 +1408,12 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
cm_node->rem_addr = cm_info->rem_addr;
cm_node->loc_port = cm_info->loc_port;
cm_node->rem_port = cm_info->rem_port;
- cm_node->send_write0 = send_first;
+
+ cm_node->mpa_frame_rev = mpa_version;
+ cm_node->send_rdma0_op = SEND_RDMA_READ_ZERO;
+ cm_node->ird_size = IETF_NO_IRD_ORD;
+ cm_node->ord_size = IETF_NO_IRD_ORD;
+
nes_debug(NES_DBG_CM, "Make node addresses : loc = %pI4:%x, rem = %pI4:%x\n",
&cm_node->loc_addr, cm_node->loc_port,
&cm_node->rem_addr, cm_node->rem_port);
@@ -1191,7 +1423,7 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
memcpy(cm_node->loc_mac, nesvnic->netdev->dev_addr, ETH_ALEN);
nes_debug(NES_DBG_CM, "listener=%p, cm_id=%p\n", cm_node->listener,
- cm_node->cm_id);
+ cm_node->cm_id);
spin_lock_init(&cm_node->retrans_list_lock);
@@ -1202,11 +1434,11 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
cm_node->tcp_cntxt.loc_id = NES_CM_DEF_LOCAL_ID;
cm_node->tcp_cntxt.rcv_wscale = NES_CM_DEFAULT_RCV_WND_SCALE;
cm_node->tcp_cntxt.rcv_wnd = NES_CM_DEFAULT_RCV_WND_SCALED >>
- NES_CM_DEFAULT_RCV_WND_SCALE;
+ NES_CM_DEFAULT_RCV_WND_SCALE;
ts = current_kernel_time();
cm_node->tcp_cntxt.loc_seq_num = htonl(ts.tv_nsec);
cm_node->tcp_cntxt.mss = nesvnic->max_frame_size - sizeof(struct iphdr) -
- sizeof(struct tcphdr) - ETH_HLEN - VLAN_HLEN;
+ sizeof(struct tcphdr) - ETH_HLEN - VLAN_HLEN;
cm_node->tcp_cntxt.rcv_nxt = 0;
/* get a unique session ID , add thread_id to an upcounter to handle race */
atomic_inc(&cm_core->node_cnt);
@@ -1222,12 +1454,11 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
cm_node->loopbackpartner = NULL;
/* get the mac addr for the remote node */
- if (ipv4_is_loopback(htonl(cm_node->rem_addr)))
+ if (ipv4_is_loopback(htonl(cm_node->rem_addr))) {
arpindex = nes_arp_table(nesdev, ntohl(nesvnic->local_ipaddr), NULL, NES_ARP_RESOLVE);
- else {
+ } else {
oldarpindex = nes_arp_table(nesdev, cm_node->rem_addr, NULL, NES_ARP_RESOLVE);
arpindex = nes_addr_resolve_neigh(nesvnic, cm_info->rem_addr, oldarpindex);
-
}
if (arpindex < 0) {
kfree(cm_node);
@@ -1260,7 +1491,7 @@ static int add_ref_cm_node(struct nes_cm_node *cm_node)
* rem_ref_cm_node - destroy an instance of a cm node
*/
static int rem_ref_cm_node(struct nes_cm_core *cm_core,
- struct nes_cm_node *cm_node)
+ struct nes_cm_node *cm_node)
{
unsigned long flags;
struct nes_qp *nesqp;
@@ -1291,9 +1522,9 @@ static int rem_ref_cm_node(struct nes_cm_core *cm_core,
} else {
if (cm_node->apbvt_set && cm_node->nesvnic) {
nes_manage_apbvt(cm_node->nesvnic, cm_node->loc_port,
- PCI_FUNC(
- cm_node->nesvnic->nesdev->pcidev->devfn),
- NES_MANAGE_APBVT_DEL);
+ PCI_FUNC(
+ cm_node->nesvnic->nesdev->pcidev->devfn),
+ NES_MANAGE_APBVT_DEL);
}
}
@@ -1314,7 +1545,7 @@ static int rem_ref_cm_node(struct nes_cm_core *cm_core,
* process_options
*/
static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc,
- u32 optionsize, u32 syn_packet)
+ u32 optionsize, u32 syn_packet)
{
u32 tmp;
u32 offset = 0;
@@ -1332,15 +1563,15 @@ static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc,
continue;
case OPTION_NUMBER_MSS:
nes_debug(NES_DBG_CM, "%s: MSS Length: %d Offset: %d "
- "Size: %d\n", __func__,
- all_options->as_mss.length, offset, optionsize);
+ "Size: %d\n", __func__,
+ all_options->as_mss.length, offset, optionsize);
got_mss_option = 1;
if (all_options->as_mss.length != 4) {
return 1;
} else {
tmp = ntohs(all_options->as_mss.mss);
if (tmp > 0 && tmp <
- cm_node->tcp_cntxt.mss)
+ cm_node->tcp_cntxt.mss)
cm_node->tcp_cntxt.mss = tmp;
}
break;
@@ -1348,12 +1579,9 @@ static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc,
cm_node->tcp_cntxt.snd_wscale =
all_options->as_windowscale.shiftcount;
break;
- case OPTION_NUMBER_WRITE0:
- cm_node->send_write0 = 1;
- break;
default:
nes_debug(NES_DBG_CM, "TCP Option not understood: %x\n",
- all_options->as_base.optionnum);
+ all_options->as_base.optionnum);
break;
}
offset += all_options->as_base.length;
@@ -1372,8 +1600,8 @@ static void drop_packet(struct sk_buff *skb)
static void handle_fin_pkt(struct nes_cm_node *cm_node)
{
nes_debug(NES_DBG_CM, "Received FIN, cm_node = %p, state = %u. "
- "refcnt=%d\n", cm_node, cm_node->state,
- atomic_read(&cm_node->ref_count));
+ "refcnt=%d\n", cm_node, cm_node->state,
+ atomic_read(&cm_node->ref_count));
switch (cm_node->state) {
case NES_CM_STATE_SYN_RCVD:
case NES_CM_STATE_SYN_SENT:
@@ -1439,7 +1667,20 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
"listener=%p state=%d\n", __func__, __LINE__, cm_node,
cm_node->listener, cm_node->state);
- active_open_err(cm_node, skb, reset);
+ switch (cm_node->mpa_frame_rev) {
+ case IETF_MPA_V2:
+ cm_node->mpa_frame_rev = IETF_MPA_V1;
+ /* send a syn and goto syn sent state */
+ cm_node->state = NES_CM_STATE_SYN_SENT;
+ if (send_syn(cm_node, 0, NULL)) {
+ active_open_err(cm_node, skb, reset);
+ }
+ break;
+ case IETF_MPA_V1:
+ default:
+ active_open_err(cm_node, skb, reset);
+ break;
+ }
break;
case NES_CM_STATE_MPAREQ_RCVD:
atomic_inc(&cm_node->passive_state);
@@ -1475,21 +1716,21 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb)
{
-
- int ret = 0;
+ int ret = 0;
int datasize = skb->len;
u8 *dataloc = skb->data;
enum nes_cm_event_type type = NES_CM_EVENT_UNKNOWN;
- u32 res_type;
+ u32 res_type;
+
ret = parse_mpa(cm_node, dataloc, &res_type, datasize);
if (ret) {
nes_debug(NES_DBG_CM, "didn't like MPA Request\n");
if (cm_node->state == NES_CM_STATE_MPAREQ_SENT) {
nes_debug(NES_DBG_CM, "%s[%u] create abort for "
- "cm_node=%p listener=%p state=%d\n", __func__,
- __LINE__, cm_node, cm_node->listener,
- cm_node->state);
+ "cm_node=%p listener=%p state=%d\n", __func__,
+ __LINE__, cm_node, cm_node->listener,
+ cm_node->state);
active_open_err(cm_node, skb, 1);
} else {
passive_open_err(cm_node, skb, 1);
@@ -1499,16 +1740,15 @@ static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb)
switch (cm_node->state) {
case NES_CM_STATE_ESTABLISHED:
- if (res_type == NES_MPA_REQUEST_REJECT) {
+ if (res_type == NES_MPA_REQUEST_REJECT)
/*BIG problem as we are receiving the MPA.. So should
- * not be REJECT.. This is Passive Open.. We can
- * only receive it Reject for Active Open...*/
+ * not be REJECT.. This is Passive Open.. We can
+ * only receive it Reject for Active Open...*/
WARN_ON(1);
- }
cm_node->state = NES_CM_STATE_MPAREQ_RCVD;
type = NES_CM_EVENT_MPA_REQ;
atomic_set(&cm_node->passive_state,
- NES_PASSIVE_STATE_INDICATED);
+ NES_PASSIVE_STATE_INDICATED);
break;
case NES_CM_STATE_MPAREQ_SENT:
cleanup_retrans_entry(cm_node);
@@ -1535,8 +1775,8 @@ static void indicate_pkt_err(struct nes_cm_node *cm_node, struct sk_buff *skb)
case NES_CM_STATE_SYN_SENT:
case NES_CM_STATE_MPAREQ_SENT:
nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
- "listener=%p state=%d\n", __func__, __LINE__, cm_node,
- cm_node->listener, cm_node->state);
+ "listener=%p state=%d\n", __func__, __LINE__, cm_node,
+ cm_node->listener, cm_node->state);
active_open_err(cm_node, skb, 1);
break;
case NES_CM_STATE_ESTABLISHED:
@@ -1550,11 +1790,11 @@ static void indicate_pkt_err(struct nes_cm_node *cm_node, struct sk_buff *skb)
}
static int check_syn(struct nes_cm_node *cm_node, struct tcphdr *tcph,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
int err;
- err = ((ntohl(tcph->ack_seq) == cm_node->tcp_cntxt.loc_seq_num))? 0 : 1;
+ err = ((ntohl(tcph->ack_seq) == cm_node->tcp_cntxt.loc_seq_num)) ? 0 : 1;
if (err)
active_open_err(cm_node, skb, 1);
@@ -1562,7 +1802,7 @@ static int check_syn(struct nes_cm_node *cm_node, struct tcphdr *tcph,
}
static int check_seq(struct nes_cm_node *cm_node, struct tcphdr *tcph,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
int err = 0;
u32 seq;
@@ -1570,21 +1810,22 @@ static int check_seq(struct nes_cm_node *cm_node, struct tcphdr *tcph,
u32 loc_seq_num = cm_node->tcp_cntxt.loc_seq_num;
u32 rcv_nxt = cm_node->tcp_cntxt.rcv_nxt;
u32 rcv_wnd;
+
seq = ntohl(tcph->seq);
ack_seq = ntohl(tcph->ack_seq);
rcv_wnd = cm_node->tcp_cntxt.rcv_wnd;
if (ack_seq != loc_seq_num)
err = 1;
- else if (!between(seq, rcv_nxt, (rcv_nxt+rcv_wnd)))
+ else if (!between(seq, rcv_nxt, (rcv_nxt + rcv_wnd)))
err = 1;
if (err) {
nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
- "listener=%p state=%d\n", __func__, __LINE__, cm_node,
- cm_node->listener, cm_node->state);
+ "listener=%p state=%d\n", __func__, __LINE__, cm_node,
+ cm_node->listener, cm_node->state);
indicate_pkt_err(cm_node, skb);
nes_debug(NES_DBG_CM, "seq ERROR cm_node =%p seq=0x%08X "
- "rcv_nxt=0x%08X rcv_wnd=0x%x\n", cm_node, seq, rcv_nxt,
- rcv_wnd);
+ "rcv_nxt=0x%08X rcv_wnd=0x%x\n", cm_node, seq, rcv_nxt,
+ rcv_wnd);
}
return err;
}
@@ -1594,9 +1835,8 @@ static int check_seq(struct nes_cm_node *cm_node, struct tcphdr *tcph,
* is created with a listener or it may comein as rexmitted packet which in
* that case will be just dropped.
*/
-
static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
- struct tcphdr *tcph)
+ struct tcphdr *tcph)
{
int ret;
u32 inc_sequence;
@@ -1615,15 +1855,15 @@ static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
case NES_CM_STATE_LISTENING:
/* Passive OPEN */
if (atomic_read(&cm_node->listener->pend_accepts_cnt) >
- cm_node->listener->backlog) {
+ cm_node->listener->backlog) {
nes_debug(NES_DBG_CM, "drop syn due to backlog "
- "pressure \n");
+ "pressure \n");
cm_backlog_drops++;
passive_open_err(cm_node, skb, 0);
break;
}
ret = handle_tcp_options(cm_node, tcph, skb, optionsize,
- 1);
+ 1);
if (ret) {
passive_open_err(cm_node, skb, 0);
/* drop pkt */
@@ -1657,9 +1897,8 @@ static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
}
static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
- struct tcphdr *tcph)
+ struct tcphdr *tcph)
{
-
int ret;
u32 inc_sequence;
int optionsize;
@@ -1678,7 +1917,7 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
ret = handle_tcp_options(cm_node, tcph, skb, optionsize, 0);
if (ret) {
nes_debug(NES_DBG_CM, "cm_node=%p tcp_options failed\n",
- cm_node);
+ cm_node);
break;
}
cleanup_retrans_entry(cm_node);
@@ -1717,12 +1956,13 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
}
static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
- struct tcphdr *tcph)
+ struct tcphdr *tcph)
{
int datasize = 0;
u32 inc_sequence;
int ret = 0;
int optionsize;
+
optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
if (check_seq(cm_node, tcph, skb))
@@ -1743,8 +1983,9 @@ static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
handle_rcv_mpa(cm_node, skb);
- } else /* rcvd ACK only */
+ } else { /* rcvd ACK only */
dev_kfree_skb_any(skb);
+ }
break;
case NES_CM_STATE_ESTABLISHED:
/* Passive OPEN */
@@ -1752,16 +1993,18 @@ static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
handle_rcv_mpa(cm_node, skb);
- } else
+ } else {
drop_packet(skb);
+ }
break;
case NES_CM_STATE_MPAREQ_SENT:
cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
handle_rcv_mpa(cm_node, skb);
- } else /* Could be just an ack pkt.. */
+ } else { /* Could be just an ack pkt.. */
dev_kfree_skb_any(skb);
+ }
break;
case NES_CM_STATE_LISTENING:
cleanup_retrans_entry(cm_node);
@@ -1802,14 +2045,15 @@ static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
- struct sk_buff *skb, int optionsize, int passive)
+ struct sk_buff *skb, int optionsize, int passive)
{
u8 *optionsloc = (u8 *)&tcph[1];
+
if (optionsize) {
if (process_options(cm_node, optionsloc, optionsize,
- (u32)tcph->syn)) {
+ (u32)tcph->syn)) {
nes_debug(NES_DBG_CM, "%s: Node %p, Sending RESET\n",
- __func__, cm_node);
+ __func__, cm_node);
if (passive)
passive_open_err(cm_node, skb, 1);
else
@@ -1819,7 +2063,7 @@ static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
}
cm_node->tcp_cntxt.snd_wnd = ntohs(tcph->window) <<
- cm_node->tcp_cntxt.snd_wscale;
+ cm_node->tcp_cntxt.snd_wscale;
if (cm_node->tcp_cntxt.snd_wnd > cm_node->tcp_cntxt.max_snd_wnd)
cm_node->tcp_cntxt.max_snd_wnd = cm_node->tcp_cntxt.snd_wnd;
@@ -1830,18 +2074,18 @@ static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
* active_open_err() will send reset() if flag set..
* It will also send ABORT event.
*/
-
static void active_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb,
- int reset)
+ int reset)
{
cleanup_retrans_entry(cm_node);
if (reset) {
nes_debug(NES_DBG_CM, "ERROR active err called for cm_node=%p, "
- "state=%d\n", cm_node, cm_node->state);
+ "state=%d\n", cm_node, cm_node->state);
add_ref_cm_node(cm_node);
send_reset(cm_node, skb);
- } else
+ } else {
dev_kfree_skb_any(skb);
+ }
cm_node->state = NES_CM_STATE_CLOSED;
create_event(cm_node, NES_CM_EVENT_ABORTED);
@@ -1851,15 +2095,14 @@ static void active_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb,
* passive_open_err() will either do a reset() or will free up the skb and
* remove the cm_node.
*/
-
static void passive_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb,
- int reset)
+ int reset)
{
cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
if (reset) {
nes_debug(NES_DBG_CM, "passive_open_err sending RST for "
- "cm_node=%p state =%d\n", cm_node, cm_node->state);
+ "cm_node=%p state =%d\n", cm_node, cm_node->state);
send_reset(cm_node, skb);
} else {
dev_kfree_skb_any(skb);
@@ -1874,6 +2117,7 @@ static void passive_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb,
static void free_retrans_entry(struct nes_cm_node *cm_node)
{
struct nes_timer_entry *send_entry;
+
send_entry = cm_node->send_entry;
if (send_entry) {
cm_node->send_entry = NULL;
@@ -1897,26 +2141,28 @@ static void cleanup_retrans_entry(struct nes_cm_node *cm_node)
* Returns skb if to be freed, else it will return NULL if already used..
*/
static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
- struct nes_cm_core *cm_core)
+ struct nes_cm_core *cm_core)
{
- enum nes_tcpip_pkt_type pkt_type = NES_PKT_TYPE_UNKNOWN;
+ enum nes_tcpip_pkt_type pkt_type = NES_PKT_TYPE_UNKNOWN;
struct tcphdr *tcph = tcp_hdr(skb);
- u32 fin_set = 0;
+ u32 fin_set = 0;
int ret = 0;
+
skb_pull(skb, ip_hdr(skb)->ihl << 2);
nes_debug(NES_DBG_CM, "process_packet: cm_node=%p state =%d syn=%d "
- "ack=%d rst=%d fin=%d\n", cm_node, cm_node->state, tcph->syn,
- tcph->ack, tcph->rst, tcph->fin);
+ "ack=%d rst=%d fin=%d\n", cm_node, cm_node->state, tcph->syn,
+ tcph->ack, tcph->rst, tcph->fin);
- if (tcph->rst)
+ if (tcph->rst) {
pkt_type = NES_PKT_TYPE_RST;
- else if (tcph->syn) {
+ } else if (tcph->syn) {
pkt_type = NES_PKT_TYPE_SYN;
if (tcph->ack)
pkt_type = NES_PKT_TYPE_SYNACK;
- } else if (tcph->ack)
+ } else if (tcph->ack) {
pkt_type = NES_PKT_TYPE_ACK;
+ }
if (tcph->fin)
fin_set = 1;
@@ -1947,17 +2193,17 @@ static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
* mini_cm_listen - create a listen node with params
*/
static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core,
- struct nes_vnic *nesvnic, struct nes_cm_info *cm_info)
+ struct nes_vnic *nesvnic, struct nes_cm_info *cm_info)
{
struct nes_cm_listener *listener;
unsigned long flags;
nes_debug(NES_DBG_CM, "Search for 0x%08x : 0x%04x\n",
- cm_info->loc_addr, cm_info->loc_port);
+ cm_info->loc_addr, cm_info->loc_port);
/* cannot have multiple matching listeners */
listener = find_listener(cm_core, htonl(cm_info->loc_addr),
- htons(cm_info->loc_port), NES_CM_LISTENER_EITHER_STATE);
+ htons(cm_info->loc_port), NES_CM_LISTENER_EITHER_STATE);
if (listener && listener->listener_state == NES_CM_LISTENER_ACTIVE_STATE) {
/* find automatically incs ref count ??? */
atomic_dec(&listener->ref_count);
@@ -2003,9 +2249,9 @@ static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core,
}
nes_debug(NES_DBG_CM, "Api - listen(): addr=0x%08X, port=0x%04x,"
- " listener = %p, backlog = %d, cm_id = %p.\n",
- cm_info->loc_addr, cm_info->loc_port,
- listener, listener->backlog, listener->cm_id);
+ " listener = %p, backlog = %d, cm_id = %p.\n",
+ cm_info->loc_addr, cm_info->loc_port,
+ listener, listener->backlog, listener->cm_id);
return listener;
}
@@ -2015,26 +2261,20 @@ static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core,
* mini_cm_connect - make a connection node with params
*/
static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
- struct nes_vnic *nesvnic, u16 private_data_len,
- void *private_data, struct nes_cm_info *cm_info)
+ struct nes_vnic *nesvnic, u16 private_data_len,
+ void *private_data, struct nes_cm_info *cm_info)
{
int ret = 0;
struct nes_cm_node *cm_node;
struct nes_cm_listener *loopbackremotelistener;
struct nes_cm_node *loopbackremotenode;
struct nes_cm_info loopback_cm_info;
- u16 mpa_frame_size = sizeof(struct ietf_mpa_frame) + private_data_len;
- struct ietf_mpa_frame *mpa_frame = NULL;
+ u8 *start_buff;
/* create a CM connection node */
cm_node = make_cm_node(cm_core, nesvnic, cm_info, NULL);
if (!cm_node)
return NULL;
- mpa_frame = &cm_node->mpa_frame;
- memcpy(mpa_frame->key, IEFT_MPA_KEY_REQ, IETF_MPA_KEY_SIZE);
- mpa_frame->flags = IETF_MPA_FLAGS_CRC;
- mpa_frame->rev = IETF_MPA_VERSION;
- mpa_frame->priv_data_len = htons(private_data_len);
/* set our node side to client (active) side */
cm_node->tcp_cntxt.client = 1;
@@ -2042,8 +2282,8 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
if (cm_info->loc_addr == cm_info->rem_addr) {
loopbackremotelistener = find_listener(cm_core,
- ntohl(nesvnic->local_ipaddr), cm_node->rem_port,
- NES_CM_LISTENER_ACTIVE_STATE);
+ ntohl(nesvnic->local_ipaddr), cm_node->rem_port,
+ NES_CM_LISTENER_ACTIVE_STATE);
if (loopbackremotelistener == NULL) {
create_event(cm_node, NES_CM_EVENT_ABORTED);
} else {
@@ -2052,7 +2292,7 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
loopback_cm_info.rem_port = cm_info->loc_port;
loopback_cm_info.cm_id = loopbackremotelistener->cm_id;
loopbackremotenode = make_cm_node(cm_core, nesvnic,
- &loopback_cm_info, loopbackremotelistener);
+ &loopback_cm_info, loopbackremotelistener);
if (!loopbackremotenode) {
rem_ref_cm_node(cm_node->cm_core, cm_node);
return NULL;
@@ -2063,7 +2303,7 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
NES_CM_DEFAULT_RCV_WND_SCALE;
cm_node->loopbackpartner = loopbackremotenode;
memcpy(loopbackremotenode->mpa_frame_buf, private_data,
- private_data_len);
+ private_data_len);
loopbackremotenode->mpa_frame_size = private_data_len;
/* we are done handling this state. */
@@ -2091,12 +2331,10 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
return cm_node;
}
- /* set our node side to client (active) side */
- cm_node->tcp_cntxt.client = 1;
- /* init our MPA frame ptr */
- memcpy(mpa_frame->priv_data, private_data, private_data_len);
+ start_buff = &cm_node->mpa_frame_buf[0] + sizeof(struct ietf_mpa_v2);
+ cm_node->mpa_frame_size = private_data_len;
- cm_node->mpa_frame_size = mpa_frame_size;
+ memcpy(start_buff, private_data, private_data_len);
/* send a syn and goto syn sent state */
cm_node->state = NES_CM_STATE_SYN_SENT;
@@ -2105,18 +2343,19 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
if (ret) {
/* error in sending the syn free up the cm_node struct */
nes_debug(NES_DBG_CM, "Api - connect() FAILED: dest "
- "addr=0x%08X, port=0x%04x, cm_node=%p, cm_id = %p.\n",
- cm_node->rem_addr, cm_node->rem_port, cm_node,
- cm_node->cm_id);
+ "addr=0x%08X, port=0x%04x, cm_node=%p, cm_id = %p.\n",
+ cm_node->rem_addr, cm_node->rem_port, cm_node,
+ cm_node->cm_id);
rem_ref_cm_node(cm_node->cm_core, cm_node);
cm_node = NULL;
}
- if (cm_node)
+ if (cm_node) {
nes_debug(NES_DBG_CM, "Api - connect(): dest addr=0x%08X,"
- "port=0x%04x, cm_node=%p, cm_id = %p.\n",
- cm_node->rem_addr, cm_node->rem_port, cm_node,
- cm_node->cm_id);
+ "port=0x%04x, cm_node=%p, cm_id = %p.\n",
+ cm_node->rem_addr, cm_node->rem_port, cm_node,
+ cm_node->cm_id);
+ }
return cm_node;
}
@@ -2126,8 +2365,7 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
* mini_cm_accept - accept a connection
* This function is never called
*/
-static int mini_cm_accept(struct nes_cm_core *cm_core,
- struct ietf_mpa_frame *mpa_frame, struct nes_cm_node *cm_node)
+static int mini_cm_accept(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node)
{
return 0;
}
@@ -2136,8 +2374,7 @@ static int mini_cm_accept(struct nes_cm_core *cm_core,
/**
* mini_cm_reject - reject and teardown a connection
*/
-static int mini_cm_reject(struct nes_cm_core *cm_core,
- struct ietf_mpa_frame *mpa_frame, struct nes_cm_node *cm_node)
+static int mini_cm_reject(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node)
{
int ret = 0;
int err = 0;
@@ -2147,7 +2384,7 @@ static int mini_cm_reject(struct nes_cm_core *cm_core,
struct nes_cm_node *loopback = cm_node->loopbackpartner;
nes_debug(NES_DBG_CM, "%s cm_node=%p type=%d state=%d\n",
- __func__, cm_node, cm_node->tcp_cntxt.client, cm_node->state);
+ __func__, cm_node, cm_node->tcp_cntxt.client, cm_node->state);
if (cm_node->tcp_cntxt.client)
return ret;
@@ -2168,8 +2405,9 @@ static int mini_cm_reject(struct nes_cm_core *cm_core,
err = send_reset(cm_node, NULL);
if (err)
WARN_ON(1);
- } else
+ } else {
cm_id->add_ref(cm_id);
+ }
}
}
} else {
@@ -2244,7 +2482,7 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod
case NES_CM_STATE_TSA:
if (cm_node->send_entry)
printk(KERN_ERR "ERROR Close got called from STATE_TSA "
- "send_entry=%p\n", cm_node->send_entry);
+ "send_entry=%p\n", cm_node->send_entry);
ret = rem_ref_cm_node(cm_core, cm_node);
break;
}
@@ -2257,7 +2495,7 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod
* node state machine
*/
static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
- struct nes_vnic *nesvnic, struct sk_buff *skb)
+ struct nes_vnic *nesvnic, struct sk_buff *skb)
{
struct nes_cm_node *cm_node = NULL;
struct nes_cm_listener *listener = NULL;
@@ -2269,9 +2507,8 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
if (!skb)
return 0;
- if (skb->len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
+ if (skb->len < sizeof(struct iphdr) + sizeof(struct tcphdr))
return 0;
- }
iph = (struct iphdr *)skb->data;
tcph = (struct tcphdr *)(skb->data + sizeof(struct iphdr));
@@ -2289,8 +2526,8 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
do {
cm_node = find_node(cm_core,
- nfo.rem_port, nfo.rem_addr,
- nfo.loc_port, nfo.loc_addr);
+ nfo.rem_port, nfo.rem_addr,
+ nfo.loc_port, nfo.loc_addr);
if (!cm_node) {
/* Only type of packet accepted are for */
@@ -2300,8 +2537,8 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
break;
}
listener = find_listener(cm_core, nfo.loc_addr,
- nfo.loc_port,
- NES_CM_LISTENER_ACTIVE_STATE);
+ nfo.loc_port,
+ NES_CM_LISTENER_ACTIVE_STATE);
if (!listener) {
nfo.cm_id = NULL;
nfo.conn_type = 0;
@@ -2312,10 +2549,10 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
nfo.cm_id = listener->cm_id;
nfo.conn_type = listener->conn_type;
cm_node = make_cm_node(cm_core, nesvnic, &nfo,
- listener);
+ listener);
if (!cm_node) {
nes_debug(NES_DBG_CM, "Unable to allocate "
- "node\n");
+ "node\n");
cm_packets_dropped++;
atomic_dec(&listener->ref_count);
dev_kfree_skb_any(skb);
@@ -2331,9 +2568,13 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
}
add_ref_cm_node(cm_node);
} else if (cm_node->state == NES_CM_STATE_TSA) {
- rem_ref_cm_node(cm_core, cm_node);
- atomic_inc(&cm_accel_dropped_pkts);
- dev_kfree_skb_any(skb);
+ if (cm_node->nesqp->pau_mode)
+ nes_queue_mgt_skbs(skb, nesvnic, cm_node->nesqp);
+ else {
+ rem_ref_cm_node(cm_core, cm_node);
+ atomic_inc(&cm_accel_dropped_pkts);
+ dev_kfree_skb_any(skb);
+ }
break;
}
skb_reset_network_header(skb);
@@ -2363,7 +2604,7 @@ static struct nes_cm_core *nes_cm_alloc_core(void)
init_timer(&cm_core->tcp_timer);
cm_core->tcp_timer.function = nes_cm_timer_tick;
- cm_core->mtu = NES_CM_DEFAULT_MTU;
+ cm_core->mtu = NES_CM_DEFAULT_MTU;
cm_core->state = NES_CM_STATE_INITED;
cm_core->free_tx_pkt_max = NES_CM_DEFAULT_FREE_PKTS;
@@ -2401,9 +2642,8 @@ static int mini_cm_dealloc_core(struct nes_cm_core *cm_core)
barrier();
- if (timer_pending(&cm_core->tcp_timer)) {
+ if (timer_pending(&cm_core->tcp_timer))
del_timer(&cm_core->tcp_timer);
- }
destroy_workqueue(cm_core->event_wq);
destroy_workqueue(cm_core->disconn_wq);
@@ -2458,8 +2698,8 @@ static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_nod
return -EINVAL;
nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_IPV4 |
- NES_QPCONTEXT_MISC_NO_NAGLE | NES_QPCONTEXT_MISC_DO_NOT_FRAG |
- NES_QPCONTEXT_MISC_DROS);
+ NES_QPCONTEXT_MISC_NO_NAGLE | NES_QPCONTEXT_MISC_DO_NOT_FRAG |
+ NES_QPCONTEXT_MISC_DROS);
if (cm_node->tcp_cntxt.snd_wscale || cm_node->tcp_cntxt.rcv_wscale)
nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_WSCALE);
@@ -2469,15 +2709,15 @@ static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_nod
nesqp->nesqp_context->mss |= cpu_to_le32(((u32)cm_node->tcp_cntxt.mss) << 16);
nesqp->nesqp_context->tcp_state_flow_label |= cpu_to_le32(
- (u32)NES_QPCONTEXT_TCPSTATE_EST << NES_QPCONTEXT_TCPFLOW_TCP_STATE_SHIFT);
+ (u32)NES_QPCONTEXT_TCPSTATE_EST << NES_QPCONTEXT_TCPFLOW_TCP_STATE_SHIFT);
nesqp->nesqp_context->pd_index_wscale |= cpu_to_le32(
- (cm_node->tcp_cntxt.snd_wscale << NES_QPCONTEXT_PDWSCALE_SND_WSCALE_SHIFT) &
- NES_QPCONTEXT_PDWSCALE_SND_WSCALE_MASK);
+ (cm_node->tcp_cntxt.snd_wscale << NES_QPCONTEXT_PDWSCALE_SND_WSCALE_SHIFT) &
+ NES_QPCONTEXT_PDWSCALE_SND_WSCALE_MASK);
nesqp->nesqp_context->pd_index_wscale |= cpu_to_le32(
- (cm_node->tcp_cntxt.rcv_wscale << NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_SHIFT) &
- NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_MASK);
+ (cm_node->tcp_cntxt.rcv_wscale << NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_SHIFT) &
+ NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_MASK);
nesqp->nesqp_context->keepalive = cpu_to_le32(0x80);
nesqp->nesqp_context->ts_recent = 0;
@@ -2486,24 +2726,24 @@ static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_nod
nesqp->nesqp_context->snd_wnd = cpu_to_le32(cm_node->tcp_cntxt.snd_wnd);
nesqp->nesqp_context->rcv_nxt = cpu_to_le32(cm_node->tcp_cntxt.rcv_nxt);
nesqp->nesqp_context->rcv_wnd = cpu_to_le32(cm_node->tcp_cntxt.rcv_wnd <<
- cm_node->tcp_cntxt.rcv_wscale);
+ cm_node->tcp_cntxt.rcv_wscale);
nesqp->nesqp_context->snd_max = cpu_to_le32(cm_node->tcp_cntxt.loc_seq_num);
nesqp->nesqp_context->snd_una = cpu_to_le32(cm_node->tcp_cntxt.loc_seq_num);
nesqp->nesqp_context->srtt = 0;
nesqp->nesqp_context->rttvar = cpu_to_le32(0x6);
nesqp->nesqp_context->ssthresh = cpu_to_le32(0x3FFFC000);
- nesqp->nesqp_context->cwnd = cpu_to_le32(2*cm_node->tcp_cntxt.mss);
+ nesqp->nesqp_context->cwnd = cpu_to_le32(2 * cm_node->tcp_cntxt.mss);
nesqp->nesqp_context->snd_wl1 = cpu_to_le32(cm_node->tcp_cntxt.rcv_nxt);
nesqp->nesqp_context->snd_wl2 = cpu_to_le32(cm_node->tcp_cntxt.loc_seq_num);
nesqp->nesqp_context->max_snd_wnd = cpu_to_le32(cm_node->tcp_cntxt.max_snd_wnd);
nes_debug(NES_DBG_CM, "QP%u: rcv_nxt = 0x%08X, snd_nxt = 0x%08X,"
- " Setting MSS to %u, PDWscale = 0x%08X, rcv_wnd = %u, context misc = 0x%08X.\n",
- nesqp->hwqp.qp_id, le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
- le32_to_cpu(nesqp->nesqp_context->snd_nxt),
- cm_node->tcp_cntxt.mss, le32_to_cpu(nesqp->nesqp_context->pd_index_wscale),
- le32_to_cpu(nesqp->nesqp_context->rcv_wnd),
- le32_to_cpu(nesqp->nesqp_context->misc));
+ " Setting MSS to %u, PDWscale = 0x%08X, rcv_wnd = %u, context misc = 0x%08X.\n",
+ nesqp->hwqp.qp_id, le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
+ le32_to_cpu(nesqp->nesqp_context->snd_nxt),
+ cm_node->tcp_cntxt.mss, le32_to_cpu(nesqp->nesqp_context->pd_index_wscale),
+ le32_to_cpu(nesqp->nesqp_context->rcv_wnd),
+ le32_to_cpu(nesqp->nesqp_context->misc));
nes_debug(NES_DBG_CM, " snd_wnd = 0x%08X.\n", le32_to_cpu(nesqp->nesqp_context->snd_wnd));
nes_debug(NES_DBG_CM, " snd_cwnd = 0x%08X.\n", le32_to_cpu(nesqp->nesqp_context->cwnd));
nes_debug(NES_DBG_CM, " max_swnd = 0x%08X.\n", le32_to_cpu(nesqp->nesqp_context->max_snd_wnd));
@@ -2524,7 +2764,7 @@ int nes_cm_disconn(struct nes_qp *nesqp)
work = kzalloc(sizeof *work, GFP_ATOMIC);
if (!work)
- return -ENOMEM; /* Timer will clean up */
+ return -ENOMEM; /* Timer will clean up */
nes_add_ref(&nesqp->ibqp);
work->nesqp = nesqp;
@@ -2544,7 +2784,7 @@ static void nes_disconnect_worker(struct work_struct *work)
kfree(dwork);
nes_debug(NES_DBG_CM, "processing AEQE id 0x%04X for QP%u.\n",
- nesqp->last_aeq, nesqp->hwqp.qp_id);
+ nesqp->last_aeq, nesqp->hwqp.qp_id);
nes_cm_disconn_true(nesqp);
nes_rem_ref(&nesqp->ibqp);
}
@@ -2580,7 +2820,7 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
/* make sure we havent already closed this connection */
if (!cm_id) {
nes_debug(NES_DBG_CM, "QP%u disconnect_worker cmid is NULL\n",
- nesqp->hwqp.qp_id);
+ nesqp->hwqp.qp_id);
spin_unlock_irqrestore(&nesqp->lock, flags);
return -1;
}
@@ -2589,7 +2829,7 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
nes_debug(NES_DBG_CM, "Disconnecting QP%u\n", nesqp->hwqp.qp_id);
original_hw_tcp_state = nesqp->hw_tcp_state;
- original_ibqp_state = nesqp->ibqp_state;
+ original_ibqp_state = nesqp->ibqp_state;
last_ae = nesqp->last_aeq;
if (nesqp->term_flags) {
@@ -2647,16 +2887,16 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
cm_event.private_data_len = 0;
nes_debug(NES_DBG_CM, "Generating a CM Disconnect Event"
- " for QP%u, SQ Head = %u, SQ Tail = %u. "
- "cm_id = %p, refcount = %u.\n",
- nesqp->hwqp.qp_id, nesqp->hwqp.sq_head,
- nesqp->hwqp.sq_tail, cm_id,
- atomic_read(&nesqp->refcount));
+ " for QP%u, SQ Head = %u, SQ Tail = %u. "
+ "cm_id = %p, refcount = %u.\n",
+ nesqp->hwqp.qp_id, nesqp->hwqp.sq_head,
+ nesqp->hwqp.sq_tail, cm_id,
+ atomic_read(&nesqp->refcount));
ret = cm_id->event_handler(cm_id, &cm_event);
if (ret)
nes_debug(NES_DBG_CM, "OFA CM event_handler "
- "returned, ret=%d\n", ret);
+ "returned, ret=%d\n", ret);
}
if (issue_close) {
@@ -2674,9 +2914,8 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
cm_event.private_data_len = 0;
ret = cm_id->event_handler(cm_id, &cm_event);
- if (ret) {
+ if (ret)
nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
- }
cm_id->rem_ref(cm_id);
}
@@ -2716,8 +2955,8 @@ static int nes_disconnect(struct nes_qp *nesqp, int abrupt)
if (nesqp->lsmm_mr)
nesibdev->ibdev.dereg_mr(nesqp->lsmm_mr);
pci_free_consistent(nesdev->pcidev,
- nesqp->private_data_len+sizeof(struct ietf_mpa_frame),
- nesqp->ietf_frame, nesqp->ietf_frame_pbase);
+ nesqp->private_data_len + nesqp->ietf_frame_size,
+ nesqp->ietf_frame, nesqp->ietf_frame_pbase);
}
}
@@ -2756,6 +2995,12 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
struct ib_phys_buf ibphysbuf;
struct nes_pd *nespd;
u64 tagged_offset;
+ u8 mpa_frame_offset = 0;
+ struct ietf_mpa_v2 *mpa_v2_frame;
+ u8 start_addr = 0;
+ u8 *start_ptr = &start_addr;
+ u8 **start_buff = &start_ptr;
+ u16 buff_len = 0;
ibqp = nes_get_qp(cm_id->device, conn_param->qpn);
if (!ibqp)
@@ -2796,53 +3041,49 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nes_debug(NES_DBG_CM, "netdev refcnt = %u.\n",
netdev_refcnt_read(nesvnic->netdev));
+ nesqp->ietf_frame_size = sizeof(struct ietf_mpa_v2);
/* allocate the ietf frame and space for private data */
nesqp->ietf_frame = pci_alloc_consistent(nesdev->pcidev,
- sizeof(struct ietf_mpa_frame) + conn_param->private_data_len,
- &nesqp->ietf_frame_pbase);
+ nesqp->ietf_frame_size + conn_param->private_data_len,
+ &nesqp->ietf_frame_pbase);
if (!nesqp->ietf_frame) {
- nes_debug(NES_DBG_CM, "Unable to allocate memory for private "
- "data\n");
+ nes_debug(NES_DBG_CM, "Unable to allocate memory for private data\n");
return -ENOMEM;
}
+ mpa_v2_frame = (struct ietf_mpa_v2 *)nesqp->ietf_frame;
+ if (cm_node->mpa_frame_rev == IETF_MPA_V1)
+ mpa_frame_offset = 4;
- /* setup the MPA frame */
- nesqp->private_data_len = conn_param->private_data_len;
- memcpy(nesqp->ietf_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
-
- memcpy(nesqp->ietf_frame->priv_data, conn_param->private_data,
- conn_param->private_data_len);
+ memcpy(mpa_v2_frame->priv_data, conn_param->private_data,
+ conn_param->private_data_len);
- nesqp->ietf_frame->priv_data_len =
- cpu_to_be16(conn_param->private_data_len);
- nesqp->ietf_frame->rev = mpa_version;
- nesqp->ietf_frame->flags = IETF_MPA_FLAGS_CRC;
+ cm_build_mpa_frame(cm_node, start_buff, &buff_len, nesqp->ietf_frame, MPA_KEY_REPLY);
+ nesqp->private_data_len = conn_param->private_data_len;
/* setup our first outgoing iWarp send WQE (the IETF frame response) */
wqe = &nesqp->hwqp.sq_vbase[0];
if (cm_id->remote_addr.sin_addr.s_addr !=
- cm_id->local_addr.sin_addr.s_addr) {
+ cm_id->local_addr.sin_addr.s_addr) {
u64temp = (unsigned long)nesqp;
nesibdev = nesvnic->nesibdev;
nespd = nesqp->nespd;
- ibphysbuf.addr = nesqp->ietf_frame_pbase;
- ibphysbuf.size = conn_param->private_data_len +
- sizeof(struct ietf_mpa_frame);
- tagged_offset = (u64)(unsigned long)nesqp->ietf_frame;
+ ibphysbuf.addr = nesqp->ietf_frame_pbase + mpa_frame_offset;
+ ibphysbuf.size = buff_len;
+ tagged_offset = (u64)(unsigned long)*start_buff;
ibmr = nesibdev->ibdev.reg_phys_mr((struct ib_pd *)nespd,
- &ibphysbuf, 1,
- IB_ACCESS_LOCAL_WRITE,
- &tagged_offset);
+ &ibphysbuf, 1,
+ IB_ACCESS_LOCAL_WRITE,
+ &tagged_offset);
if (!ibmr) {
nes_debug(NES_DBG_CM, "Unable to register memory region"
- "for lSMM for cm_node = %p \n",
- cm_node);
+ "for lSMM for cm_node = %p \n",
+ cm_node);
pci_free_consistent(nesdev->pcidev,
- nesqp->private_data_len+sizeof(struct ietf_mpa_frame),
- nesqp->ietf_frame, nesqp->ietf_frame_pbase);
+ nesqp->private_data_len + nesqp->ietf_frame_size,
+ nesqp->ietf_frame, nesqp->ietf_frame_pbase);
return -ENOMEM;
}
@@ -2850,22 +3091,20 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
ibmr->device = nespd->ibpd.device;
nesqp->lsmm_mr = ibmr;
- u64temp |= NES_SW_CONTEXT_ALIGN>>1;
+ u64temp |= NES_SW_CONTEXT_ALIGN >> 1;
set_wqe_64bit_value(wqe->wqe_words,
- NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX,
- u64temp);
+ NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX,
+ u64temp);
wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
cpu_to_le32(NES_IWARP_SQ_WQE_STREAMING |
- NES_IWARP_SQ_WQE_WRPDU);
+ NES_IWARP_SQ_WQE_WRPDU);
wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] =
- cpu_to_le32(conn_param->private_data_len +
- sizeof(struct ietf_mpa_frame));
+ cpu_to_le32(buff_len);
set_wqe_64bit_value(wqe->wqe_words,
- NES_IWARP_SQ_WQE_FRAG0_LOW_IDX,
- (u64)(unsigned long)nesqp->ietf_frame);
+ NES_IWARP_SQ_WQE_FRAG0_LOW_IDX,
+ (u64)(unsigned long)(*start_buff));
wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] =
- cpu_to_le32(conn_param->private_data_len +
- sizeof(struct ietf_mpa_frame));
+ cpu_to_le32(buff_len);
wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = ibmr->lkey;
if (nesqp->sq_kmapped) {
nesqp->sq_kmapped = 0;
@@ -2874,7 +3113,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nesqp->nesqp_context->ird_ord_sizes |=
cpu_to_le32(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
- NES_QPCONTEXT_ORDIRD_WRPDU);
+ NES_QPCONTEXT_ORDIRD_WRPDU);
} else {
nesqp->nesqp_context->ird_ord_sizes |=
cpu_to_le32(NES_QPCONTEXT_ORDIRD_WRPDU);
@@ -2888,11 +3127,11 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
/* nesqp->cm_node = (void *)cm_id->provider_data; */
cm_id->provider_data = nesqp;
- nesqp->active_conn = 0;
+ nesqp->active_conn = 0;
if (cm_node->state == NES_CM_STATE_TSA)
nes_debug(NES_DBG_CM, "Already state = TSA for cm_node=%p\n",
- cm_node);
+ cm_node);
nes_cm_init_tsa_conn(nesqp, cm_node);
@@ -2909,13 +3148,13 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr));
nesqp->nesqp_context->misc2 |= cpu_to_le32(
- (u32)PCI_FUNC(nesdev->pcidev->devfn) <<
- NES_QPCONTEXT_MISC2_SRC_IP_SHIFT);
+ (u32)PCI_FUNC(nesdev->pcidev->devfn) <<
+ NES_QPCONTEXT_MISC2_SRC_IP_SHIFT);
nesqp->nesqp_context->arp_index_vlan |=
cpu_to_le32(nes_arp_table(nesdev,
- le32_to_cpu(nesqp->nesqp_context->ip0), NULL,
- NES_ARP_RESOLVE) << 16);
+ le32_to_cpu(nesqp->nesqp_context->ip0), NULL,
+ NES_ARP_RESOLVE) << 16);
nesqp->nesqp_context->ts_val_delta = cpu_to_le32(
jiffies - nes_read_indexed(nesdev, NES_IDX_TCP_NOW));
@@ -2941,7 +3180,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
crc_value = get_crc_value(&nes_quad);
nesqp->hte_index = cpu_to_be32(crc_value ^ 0xffffffff);
nes_debug(NES_DBG_CM, "HTE Index = 0x%08X, CRC = 0x%08X\n",
- nesqp->hte_index, nesqp->hte_index & adapter->hte_index_mask);
+ nesqp->hte_index, nesqp->hte_index & adapter->hte_index_mask);
nesqp->hte_index &= adapter->hte_index_mask;
nesqp->nesqp_context->hte_index = cpu_to_le32(nesqp->hte_index);
@@ -2949,17 +3188,15 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
cm_node->cm_core->api->accelerated(cm_node->cm_core, cm_node);
nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = "
- "0x%08X:0x%04X, rcv_nxt=0x%08X, snd_nxt=0x%08X, mpa + "
- "private data length=%zu.\n", nesqp->hwqp.qp_id,
- ntohl(cm_id->remote_addr.sin_addr.s_addr),
- ntohs(cm_id->remote_addr.sin_port),
- ntohl(cm_id->local_addr.sin_addr.s_addr),
- ntohs(cm_id->local_addr.sin_port),
- le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
- le32_to_cpu(nesqp->nesqp_context->snd_nxt),
- conn_param->private_data_len +
- sizeof(struct ietf_mpa_frame));
-
+ "0x%08X:0x%04X, rcv_nxt=0x%08X, snd_nxt=0x%08X, mpa + "
+ "private data length=%u.\n", nesqp->hwqp.qp_id,
+ ntohl(cm_id->remote_addr.sin_addr.s_addr),
+ ntohs(cm_id->remote_addr.sin_port),
+ ntohl(cm_id->local_addr.sin_addr.s_addr),
+ ntohs(cm_id->local_addr.sin_port),
+ le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
+ le32_to_cpu(nesqp->nesqp_context->snd_nxt),
+ buff_len);
/* notify OF layer that accept event was successful */
cm_id->add_ref(cm_id);
@@ -2980,12 +3217,12 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nesqp->private_data_len;
/* copy entire MPA frame to our cm_node's frame */
memcpy(cm_node->loopbackpartner->mpa_frame_buf,
- nesqp->ietf_frame->priv_data, nesqp->private_data_len);
+ conn_param->private_data, conn_param->private_data_len);
create_event(cm_node->loopbackpartner, NES_CM_EVENT_CONNECTED);
}
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, "
- "ret=%d\n", __func__, __LINE__, ret);
+ "ret=%d\n", __func__, __LINE__, ret);
return 0;
}
@@ -2998,34 +3235,28 @@ int nes_reject(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
{
struct nes_cm_node *cm_node;
struct nes_cm_node *loopback;
-
struct nes_cm_core *cm_core;
+ u8 *start_buff;
atomic_inc(&cm_rejects);
- cm_node = (struct nes_cm_node *) cm_id->provider_data;
+ cm_node = (struct nes_cm_node *)cm_id->provider_data;
loopback = cm_node->loopbackpartner;
cm_core = cm_node->cm_core;
cm_node->cm_id = cm_id;
- cm_node->mpa_frame_size = sizeof(struct ietf_mpa_frame) + pdata_len;
- if (cm_node->mpa_frame_size > MAX_CM_BUFFER)
+ if (pdata_len + sizeof(struct ietf_mpa_v2) > MAX_CM_BUFFER)
return -EINVAL;
- memcpy(&cm_node->mpa_frame.key[0], IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
if (loopback) {
memcpy(&loopback->mpa_frame.priv_data, pdata, pdata_len);
loopback->mpa_frame.priv_data_len = pdata_len;
- loopback->mpa_frame_size = sizeof(struct ietf_mpa_frame) +
- pdata_len;
+ loopback->mpa_frame_size = pdata_len;
} else {
- memcpy(&cm_node->mpa_frame.priv_data, pdata, pdata_len);
- cm_node->mpa_frame.priv_data_len = cpu_to_be16(pdata_len);
+ start_buff = &cm_node->mpa_frame_buf[0] + sizeof(struct ietf_mpa_v2);
+ cm_node->mpa_frame_size = pdata_len;
+ memcpy(start_buff, pdata, pdata_len);
}
-
- cm_node->mpa_frame.rev = mpa_version;
- cm_node->mpa_frame.flags = IETF_MPA_FLAGS_CRC | IETF_MPA_FLAGS_REJECT;
-
- return cm_core->api->reject(cm_core, &cm_node->mpa_frame, cm_node);
+ return cm_core->api->reject(cm_core, cm_node);
}
@@ -3052,7 +3283,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nesvnic = to_nesvnic(nesqp->ibqp.device);
if (!nesvnic)
return -EINVAL;
- nesdev = nesvnic->nesdev;
+ nesdev = nesvnic->nesdev;
if (!nesdev)
return -EINVAL;
@@ -3060,12 +3291,12 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
return -EINVAL;
nes_debug(NES_DBG_CM, "QP%u, current IP = 0x%08X, Destination IP = "
- "0x%08X:0x%04X, local = 0x%08X:0x%04X.\n", nesqp->hwqp.qp_id,
- ntohl(nesvnic->local_ipaddr),
- ntohl(cm_id->remote_addr.sin_addr.s_addr),
- ntohs(cm_id->remote_addr.sin_port),
- ntohl(cm_id->local_addr.sin_addr.s_addr),
- ntohs(cm_id->local_addr.sin_port));
+ "0x%08X:0x%04X, local = 0x%08X:0x%04X.\n", nesqp->hwqp.qp_id,
+ ntohl(nesvnic->local_ipaddr),
+ ntohl(cm_id->remote_addr.sin_addr.s_addr),
+ ntohs(cm_id->remote_addr.sin_port),
+ ntohl(cm_id->local_addr.sin_addr.s_addr),
+ ntohs(cm_id->local_addr.sin_port));
atomic_inc(&cm_connects);
nesqp->active_conn = 1;
@@ -3079,12 +3310,12 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((u32)conn_param->ord);
nes_debug(NES_DBG_CM, "requested ord = 0x%08X.\n", (u32)conn_param->ord);
nes_debug(NES_DBG_CM, "mpa private data len =%u\n",
- conn_param->private_data_len);
+ conn_param->private_data_len);
if (cm_id->local_addr.sin_addr.s_addr !=
- cm_id->remote_addr.sin_addr.s_addr) {
+ cm_id->remote_addr.sin_addr.s_addr) {
nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
- PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
+ PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
apbvt_set = 1;
}
@@ -3100,13 +3331,13 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
/* create a connect CM node connection */
cm_node = g_cm_core->api->connect(g_cm_core, nesvnic,
- conn_param->private_data_len, (void *)conn_param->private_data,
- &cm_info);
+ conn_param->private_data_len, (void *)conn_param->private_data,
+ &cm_info);
if (!cm_node) {
if (apbvt_set)
nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
- PCI_FUNC(nesdev->pcidev->devfn),
- NES_MANAGE_APBVT_DEL);
+ PCI_FUNC(nesdev->pcidev->devfn),
+ NES_MANAGE_APBVT_DEL);
cm_id->rem_ref(cm_id);
return -ENOMEM;
@@ -3156,7 +3387,7 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog)
cm_node = g_cm_core->api->listen(g_cm_core, nesvnic, &cm_info);
if (!cm_node) {
printk(KERN_ERR "%s[%u] Error returned from listen API call\n",
- __func__, __LINE__);
+ __func__, __LINE__);
return -ENOMEM;
}
@@ -3164,12 +3395,12 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog)
if (!cm_node->reused_node) {
err = nes_manage_apbvt(nesvnic,
- ntohs(cm_id->local_addr.sin_port),
- PCI_FUNC(nesvnic->nesdev->pcidev->devfn),
- NES_MANAGE_APBVT_ADD);
+ ntohs(cm_id->local_addr.sin_port),
+ PCI_FUNC(nesvnic->nesdev->pcidev->devfn),
+ NES_MANAGE_APBVT_ADD);
if (err) {
printk(KERN_ERR "nes_manage_apbvt call returned %d.\n",
- err);
+ err);
g_cm_core->api->stop_listener(g_cm_core, (void *)cm_node);
return err;
}
@@ -3206,13 +3437,13 @@ int nes_destroy_listen(struct iw_cm_id *cm_id)
int nes_cm_recv(struct sk_buff *skb, struct net_device *netdevice)
{
int rc = 0;
+
cm_packets_received++;
- if ((g_cm_core) && (g_cm_core->api)) {
+ if ((g_cm_core) && (g_cm_core->api))
rc = g_cm_core->api->recv_pkt(g_cm_core, netdev_priv(netdevice), skb);
- } else {
+ else
nes_debug(NES_DBG_CM, "Unable to process packet for CM,"
- " cm is not setup properly.\n");
- }
+ " cm is not setup properly.\n");
return rc;
}
@@ -3227,11 +3458,10 @@ int nes_cm_start(void)
nes_debug(NES_DBG_CM, "\n");
/* create the primary CM core, pass this handle to subsequent core inits */
g_cm_core = nes_cm_alloc_core();
- if (g_cm_core) {
+ if (g_cm_core)
return 0;
- } else {
+ else
return -ENOMEM;
- }
}
@@ -3252,7 +3482,6 @@ int nes_cm_stop(void)
*/
static void cm_event_connected(struct nes_cm_event *event)
{
- u64 u64temp;
struct nes_qp *nesqp;
struct nes_vnic *nesvnic;
struct nes_device *nesdev;
@@ -3261,7 +3490,6 @@ static void cm_event_connected(struct nes_cm_event *event)
struct ib_qp_attr attr;
struct iw_cm_id *cm_id;
struct iw_cm_event cm_event;
- struct nes_hw_qp_wqe *wqe;
struct nes_v4_quad nes_quad;
u32 crc_value;
int ret;
@@ -3275,17 +3503,16 @@ static void cm_event_connected(struct nes_cm_event *event)
nesdev = nesvnic->nesdev;
nesadapter = nesdev->nesadapter;
- if (nesqp->destroyed) {
+ if (nesqp->destroyed)
return;
- }
atomic_inc(&cm_connecteds);
nes_debug(NES_DBG_CM, "QP%u attempting to connect to 0x%08X:0x%04X on"
- " local port 0x%04X. jiffies = %lu.\n",
- nesqp->hwqp.qp_id,
- ntohl(cm_id->remote_addr.sin_addr.s_addr),
- ntohs(cm_id->remote_addr.sin_port),
- ntohs(cm_id->local_addr.sin_port),
- jiffies);
+ " local port 0x%04X. jiffies = %lu.\n",
+ nesqp->hwqp.qp_id,
+ ntohl(cm_id->remote_addr.sin_addr.s_addr),
+ ntohs(cm_id->remote_addr.sin_port),
+ ntohs(cm_id->local_addr.sin_port),
+ jiffies);
nes_cm_init_tsa_conn(nesqp, cm_node);
@@ -3316,40 +3543,12 @@ static void cm_event_connected(struct nes_cm_event *event)
NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT);
/* Adjust tail for not having a LSMM */
- nesqp->hwqp.sq_tail = 1;
+ /*nesqp->hwqp.sq_tail = 1;*/
-#if defined(NES_SEND_FIRST_WRITE)
- if (cm_node->send_write0) {
- nes_debug(NES_DBG_CM, "Sending first write.\n");
- wqe = &nesqp->hwqp.sq_vbase[0];
- u64temp = (unsigned long)nesqp;
- u64temp |= NES_SW_CONTEXT_ALIGN>>1;
- set_wqe_64bit_value(wqe->wqe_words,
- NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, u64temp);
- wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
- cpu_to_le32(NES_IWARP_SQ_OP_RDMAW);
- wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = 0;
- wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = 0;
- wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = 0;
- wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = 0;
- wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
+ build_rdma0_msg(cm_node, &nesqp);
- if (nesqp->sq_kmapped) {
- nesqp->sq_kmapped = 0;
- kunmap(nesqp->page);
- }
-
- /* use the reserved spot on the WQ for the extra first WQE */
- nesqp->nesqp_context->ird_ord_sizes &=
- cpu_to_le32(~(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
- NES_QPCONTEXT_ORDIRD_WRPDU |
- NES_QPCONTEXT_ORDIRD_ALSMM));
- nesqp->skip_lsmm = 1;
- nesqp->hwqp.sq_tail = 0;
- nes_write32(nesdev->regs + NES_WQE_ALLOC,
- (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id);
- }
-#endif
+ nes_write32(nesdev->regs + NES_WQE_ALLOC,
+ (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id);
memset(&nes_quad, 0, sizeof(nes_quad));
@@ -3366,13 +3565,13 @@ static void cm_event_connected(struct nes_cm_event *event)
crc_value = get_crc_value(&nes_quad);
nesqp->hte_index = cpu_to_be32(crc_value ^ 0xffffffff);
nes_debug(NES_DBG_CM, "HTE Index = 0x%08X, After CRC = 0x%08X\n",
- nesqp->hte_index, nesqp->hte_index & nesadapter->hte_index_mask);
+ nesqp->hte_index, nesqp->hte_index & nesadapter->hte_index_mask);
nesqp->hte_index &= nesadapter->hte_index_mask;
nesqp->nesqp_context->hte_index = cpu_to_le32(nesqp->hte_index);
nesqp->ietf_frame = &cm_node->mpa_frame;
- nesqp->private_data_len = (u8) cm_node->mpa_frame_size;
+ nesqp->private_data_len = (u8)cm_node->mpa_frame_size;
cm_node->cm_core->api->accelerated(cm_node->cm_core, cm_node);
/* notify OF layer we successfully created the requested connection */
@@ -3384,7 +3583,9 @@ static void cm_event_connected(struct nes_cm_event *event)
cm_event.remote_addr = cm_id->remote_addr;
cm_event.private_data = (void *)event->cm_node->mpa_frame_buf;
- cm_event.private_data_len = (u8) event->cm_node->mpa_frame_size;
+ cm_event.private_data_len = (u8)event->cm_node->mpa_frame_size;
+ cm_event.ird = cm_node->ird_size;
+ cm_event.ord = cm_node->ord_size;
cm_event.local_addr.sin_addr.s_addr = event->cm_info.rem_addr;
ret = cm_id->event_handler(cm_id, &cm_event);
@@ -3392,12 +3593,12 @@ static void cm_event_connected(struct nes_cm_event *event)
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, "
- "ret=%d\n", __func__, __LINE__, ret);
+ "ret=%d\n", __func__, __LINE__, ret);
attr.qp_state = IB_QPS_RTS;
nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
nes_debug(NES_DBG_CM, "Exiting connect thread for QP%u. jiffies = "
- "%lu\n", nesqp->hwqp.qp_id, jiffies);
+ "%lu\n", nesqp->hwqp.qp_id, jiffies);
return;
}
@@ -3418,16 +3619,14 @@ static void cm_event_connect_error(struct nes_cm_event *event)
return;
cm_id = event->cm_node->cm_id;
- if (!cm_id) {
+ if (!cm_id)
return;
- }
nes_debug(NES_DBG_CM, "cm_node=%p, cm_id=%p\n", event->cm_node, cm_id);
nesqp = cm_id->provider_data;
- if (!nesqp) {
+ if (!nesqp)
return;
- }
/* notify OF layer about this connection error event */
/* cm_id->rem_ref(cm_id); */
@@ -3442,14 +3641,14 @@ static void cm_event_connect_error(struct nes_cm_event *event)
cm_event.private_data_len = 0;
nes_debug(NES_DBG_CM, "call CM_EVENT REJECTED, local_addr=%08x, "
- "remove_addr=%08x\n", cm_event.local_addr.sin_addr.s_addr,
- cm_event.remote_addr.sin_addr.s_addr);
+ "remove_addr=%08x\n", cm_event.local_addr.sin_addr.s_addr,
+ cm_event.remote_addr.sin_addr.s_addr);
ret = cm_id->event_handler(cm_id, &cm_event);
nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, "
- "ret=%d\n", __func__, __LINE__, ret);
+ "ret=%d\n", __func__, __LINE__, ret);
cm_id->rem_ref(cm_id);
rem_ref_cm_node(event->cm_node->cm_core, event->cm_node);
@@ -3519,7 +3718,7 @@ static void cm_event_reset(struct nes_cm_event *event)
*/
static void cm_event_mpa_req(struct nes_cm_event *event)
{
- struct iw_cm_id *cm_id;
+ struct iw_cm_id *cm_id;
struct iw_cm_event cm_event;
int ret;
struct nes_cm_node *cm_node;
@@ -3531,7 +3730,7 @@ static void cm_event_mpa_req(struct nes_cm_event *event)
atomic_inc(&cm_connect_reqs);
nes_debug(NES_DBG_CM, "cm_node = %p - cm_id = %p, jiffies = %lu\n",
- cm_node, cm_id, jiffies);
+ cm_node, cm_id, jiffies);
cm_event.event = IW_CM_EVENT_CONNECT_REQUEST;
cm_event.status = 0;
@@ -3545,19 +3744,21 @@ static void cm_event_mpa_req(struct nes_cm_event *event)
cm_event.remote_addr.sin_port = htons(event->cm_info.rem_port);
cm_event.remote_addr.sin_addr.s_addr = htonl(event->cm_info.rem_addr);
cm_event.private_data = cm_node->mpa_frame_buf;
- cm_event.private_data_len = (u8) cm_node->mpa_frame_size;
+ cm_event.private_data_len = (u8)cm_node->mpa_frame_size;
+ cm_event.ird = cm_node->ird_size;
+ cm_event.ord = cm_node->ord_size;
ret = cm_id->event_handler(cm_id, &cm_event);
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, ret=%d\n",
- __func__, __LINE__, ret);
+ __func__, __LINE__, ret);
return;
}
static void cm_event_mpa_reject(struct nes_cm_event *event)
{
- struct iw_cm_id *cm_id;
+ struct iw_cm_id *cm_id;
struct iw_cm_event cm_event;
struct nes_cm_node *cm_node;
int ret;
@@ -3569,7 +3770,7 @@ static void cm_event_mpa_reject(struct nes_cm_event *event)
atomic_inc(&cm_connect_reqs);
nes_debug(NES_DBG_CM, "cm_node = %p - cm_id = %p, jiffies = %lu\n",
- cm_node, cm_id, jiffies);
+ cm_node, cm_id, jiffies);
cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
cm_event.status = -ECONNREFUSED;
@@ -3584,17 +3785,17 @@ static void cm_event_mpa_reject(struct nes_cm_event *event)
cm_event.remote_addr.sin_addr.s_addr = htonl(event->cm_info.rem_addr);
cm_event.private_data = cm_node->mpa_frame_buf;
- cm_event.private_data_len = (u8) cm_node->mpa_frame_size;
+ cm_event.private_data_len = (u8)cm_node->mpa_frame_size;
nes_debug(NES_DBG_CM, "call CM_EVENT_MPA_REJECTED, local_addr=%08x, "
- "remove_addr=%08x\n",
- cm_event.local_addr.sin_addr.s_addr,
- cm_event.remote_addr.sin_addr.s_addr);
+ "remove_addr=%08x\n",
+ cm_event.local_addr.sin_addr.s_addr,
+ cm_event.remote_addr.sin_addr.s_addr);
ret = cm_id->event_handler(cm_id, &cm_event);
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, ret=%d\n",
- __func__, __LINE__, ret);
+ __func__, __LINE__, ret);
return;
}
@@ -3613,7 +3814,7 @@ static int nes_cm_post_event(struct nes_cm_event *event)
event->cm_info.cm_id->add_ref(event->cm_info.cm_id);
INIT_WORK(&event->event_work, nes_cm_event_handler);
nes_debug(NES_DBG_CM, "cm_node=%p queue_work, event=%p\n",
- event->cm_node, event);
+ event->cm_node, event);
queue_work(event->cm_node->cm_core->event_wq, &event->event_work);
@@ -3630,7 +3831,7 @@ static int nes_cm_post_event(struct nes_cm_event *event)
static void nes_cm_event_handler(struct work_struct *work)
{
struct nes_cm_event *event = container_of(work, struct nes_cm_event,
- event_work);
+ event_work);
struct nes_cm_core *cm_core;
if ((!event) || (!event->cm_node) || (!event->cm_node->cm_core))
@@ -3638,29 +3839,29 @@ static void nes_cm_event_handler(struct work_struct *work)
cm_core = event->cm_node->cm_core;
nes_debug(NES_DBG_CM, "event=%p, event->type=%u, events posted=%u\n",
- event, event->type, atomic_read(&cm_core->events_posted));
+ event, event->type, atomic_read(&cm_core->events_posted));
switch (event->type) {
case NES_CM_EVENT_MPA_REQ:
cm_event_mpa_req(event);
nes_debug(NES_DBG_CM, "cm_node=%p CM Event: MPA REQUEST\n",
- event->cm_node);
+ event->cm_node);
break;
case NES_CM_EVENT_RESET:
nes_debug(NES_DBG_CM, "cm_node = %p CM Event: RESET\n",
- event->cm_node);
+ event->cm_node);
cm_event_reset(event);
break;
case NES_CM_EVENT_CONNECTED:
if ((!event->cm_node->cm_id) ||
- (event->cm_node->state != NES_CM_STATE_TSA))
+ (event->cm_node->state != NES_CM_STATE_TSA))
break;
cm_event_connected(event);
nes_debug(NES_DBG_CM, "CM Event: CONNECTED\n");
break;
case NES_CM_EVENT_MPA_REJECT:
if ((!event->cm_node->cm_id) ||
- (event->cm_node->state == NES_CM_STATE_TSA))
+ (event->cm_node->state == NES_CM_STATE_TSA))
break;
cm_event_mpa_reject(event);
nes_debug(NES_DBG_CM, "CM Event: REJECT\n");
@@ -3668,7 +3869,7 @@ static void nes_cm_event_handler(struct work_struct *work)
case NES_CM_EVENT_ABORTED:
if ((!event->cm_node->cm_id) ||
- (event->cm_node->state == NES_CM_STATE_TSA))
+ (event->cm_node->state == NES_CM_STATE_TSA))
break;
cm_event_connect_error(event);
nes_debug(NES_DBG_CM, "CM Event: ABORTED\n");
diff --git a/drivers/infiniband/hw/nes/nes_cm.h b/drivers/infiniband/hw/nes/nes_cm.h
index d9825fda70a1..bdfa1fbb35fc 100644
--- a/drivers/infiniband/hw/nes/nes_cm.h
+++ b/drivers/infiniband/hw/nes/nes_cm.h
@@ -48,7 +48,16 @@
#define IETF_MPA_KEY_SIZE 16
#define IETF_MPA_VERSION 1
#define IETF_MAX_PRIV_DATA_LEN 512
-#define IETF_MPA_FRAME_SIZE 20
+#define IETF_MPA_FRAME_SIZE 20
+#define IETF_RTR_MSG_SIZE 4
+#define IETF_MPA_V2_FLAG 0x10
+
+/* IETF RTR MSG Fields */
+#define IETF_PEER_TO_PEER 0x8000
+#define IETF_FLPDU_ZERO_LEN 0x4000
+#define IETF_RDMA0_WRITE 0x8000
+#define IETF_RDMA0_READ 0x4000
+#define IETF_NO_IRD_ORD 0x3FFF
enum ietf_mpa_flags {
IETF_MPA_FLAGS_MARKERS = 0x80, /* receive Markers */
@@ -56,7 +65,7 @@ enum ietf_mpa_flags {
IETF_MPA_FLAGS_REJECT = 0x20, /* Reject */
};
-struct ietf_mpa_frame {
+struct ietf_mpa_v1 {
u8 key[IETF_MPA_KEY_SIZE];
u8 flags;
u8 rev;
@@ -66,6 +75,20 @@ struct ietf_mpa_frame {
#define ietf_mpa_req_resp_frame ietf_mpa_frame
+struct ietf_rtr_msg {
+ __be16 ctrl_ird;
+ __be16 ctrl_ord;
+};
+
+struct ietf_mpa_v2 {
+ u8 key[IETF_MPA_KEY_SIZE];
+ u8 flags;
+ u8 rev;
+ __be16 priv_data_len;
+ struct ietf_rtr_msg rtr_msg;
+ u8 priv_data[0];
+};
+
struct nes_v4_quad {
u32 rsvd0;
__le32 DstIpAdrIndex; /* Only most significant 5 bits are valid */
@@ -171,8 +194,7 @@ struct nes_timer_entry {
#define NES_CM_DEF_SEQ2 0x18ed5740
#define NES_CM_DEF_LOCAL_ID2 0xb807
-#define MAX_CM_BUFFER (IETF_MPA_FRAME_SIZE + IETF_MAX_PRIV_DATA_LEN)
-
+#define MAX_CM_BUFFER (IETF_MPA_FRAME_SIZE + IETF_RTR_MSG_SIZE + IETF_MAX_PRIV_DATA_LEN)
typedef u32 nes_addr_t;
@@ -204,6 +226,21 @@ enum nes_cm_node_state {
NES_CM_STATE_CLOSED
};
+enum mpa_frame_version {
+ IETF_MPA_V1 = 1,
+ IETF_MPA_V2 = 2
+};
+
+enum mpa_frame_key {
+ MPA_KEY_REQUEST,
+ MPA_KEY_REPLY
+};
+
+enum send_rdma0 {
+ SEND_RDMA_READ_ZERO = 1,
+ SEND_RDMA_WRITE_ZERO = 2
+};
+
enum nes_tcpip_pkt_type {
NES_PKT_TYPE_UNKNOWN,
NES_PKT_TYPE_SYN,
@@ -245,9 +282,9 @@ struct nes_cm_tcp_context {
enum nes_cm_listener_state {
- NES_CM_LISTENER_PASSIVE_STATE=1,
- NES_CM_LISTENER_ACTIVE_STATE=2,
- NES_CM_LISTENER_EITHER_STATE=3
+ NES_CM_LISTENER_PASSIVE_STATE = 1,
+ NES_CM_LISTENER_ACTIVE_STATE = 2,
+ NES_CM_LISTENER_EITHER_STATE = 3
};
struct nes_cm_listener {
@@ -283,16 +320,20 @@ struct nes_cm_node {
struct nes_cm_node *loopbackpartner;
- struct nes_timer_entry *send_entry;
-
+ struct nes_timer_entry *send_entry;
+ struct nes_timer_entry *recv_entry;
spinlock_t retrans_list_lock;
- struct nes_timer_entry *recv_entry;
+ enum send_rdma0 send_rdma0_op;
- int send_write0;
union {
- struct ietf_mpa_frame mpa_frame;
- u8 mpa_frame_buf[MAX_CM_BUFFER];
+ struct ietf_mpa_v1 mpa_frame;
+ struct ietf_mpa_v2 mpa_v2_frame;
+ u8 mpa_frame_buf[MAX_CM_BUFFER];
};
+ enum mpa_frame_version mpa_frame_rev;
+ u16 ird_size;
+ u16 ord_size;
+
u16 mpa_frame_size;
struct iw_cm_id *cm_id;
struct list_head list;
@@ -399,10 +440,8 @@ struct nes_cm_ops {
struct nes_vnic *, u16, void *,
struct nes_cm_info *);
int (*close)(struct nes_cm_core *, struct nes_cm_node *);
- int (*accept)(struct nes_cm_core *, struct ietf_mpa_frame *,
- struct nes_cm_node *);
- int (*reject)(struct nes_cm_core *, struct ietf_mpa_frame *,
- struct nes_cm_node *);
+ int (*accept)(struct nes_cm_core *, struct nes_cm_node *);
+ int (*reject)(struct nes_cm_core *, struct nes_cm_node *);
int (*recv_pkt)(struct nes_cm_core *, struct nes_vnic *,
struct sk_buff *);
int (*destroy_cm_core)(struct nes_cm_core *);
@@ -422,5 +461,7 @@ int nes_destroy_listen(struct iw_cm_id *);
int nes_cm_recv(struct sk_buff *, struct net_device *);
int nes_cm_start(void);
int nes_cm_stop(void);
+int nes_add_ref_cm_node(struct nes_cm_node *cm_node);
+int nes_rem_ref_cm_node(struct nes_cm_node *cm_node);
#endif /* NES_CM_H */
diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c
index 96fa9a4cafdf..7c0ff19ce382 100644
--- a/drivers/infiniband/hw/nes/nes_hw.c
+++ b/drivers/infiniband/hw/nes/nes_hw.c
@@ -110,6 +110,14 @@ static unsigned char *nes_tcp_state_str[] = {
};
#endif
+static inline void print_ip(struct nes_cm_node *cm_node)
+{
+ unsigned char *rem_addr;
+ if (cm_node) {
+ rem_addr = (unsigned char *)&cm_node->rem_addr;
+ printk(KERN_ERR PFX "Remote IP addr: %pI4\n", rem_addr);
+ }
+}
/**
* nes_nic_init_timer_defaults
@@ -1555,6 +1563,7 @@ static void nes_replenish_nic_rq(struct nes_vnic *nesvnic)
struct nes_hw_nic_rq_wqe *nic_rqe;
struct nes_hw_nic *nesnic;
struct nes_device *nesdev;
+ struct nes_rskb_cb *cb;
u32 rx_wqes_posted = 0;
nesnic = &nesvnic->nic;
@@ -1580,6 +1589,9 @@ static void nes_replenish_nic_rq(struct nes_vnic *nesvnic)
bus_address = pci_map_single(nesdev->pcidev,
skb->data, nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->busaddr = bus_address;
+ cb->maplen = nesvnic->max_frame_size;
nic_rqe = &nesnic->rq_vbase[nesvnic->nic.rq_head];
nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] =
@@ -1669,6 +1681,7 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)
u32 cqp_head;
u32 counter;
u32 wqe_count;
+ struct nes_rskb_cb *cb;
u8 jumbomode=0;
/* Allocate fragment, SQ, RQ, and CQ; Reuse CEQ based on the PCI function */
@@ -1845,6 +1858,9 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)
pmem = pci_map_single(nesdev->pcidev, skb->data,
nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->busaddr = pmem;
+ cb->maplen = nesvnic->max_frame_size;
nic_rqe = &nesvnic->nic.rq_vbase[counter];
nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] = cpu_to_le32(nesvnic->max_frame_size);
@@ -1873,6 +1889,13 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)
jumbomode = 1;
nes_nic_init_timer_defaults(nesdev, jumbomode);
}
+ if ((nesdev->nesadapter->allow_unaligned_fpdus) &&
+ (nes_init_mgt_qp(nesdev, netdev, nesvnic))) {
+ nes_debug(NES_DBG_INIT, "%s: Out of memory for pau nic\n", netdev->name);
+ nes_destroy_nic_qp(nesvnic);
+ return -ENOMEM;
+ }
+
nesvnic->lro_mgr.max_aggr = nes_lro_max_aggr;
nesvnic->lro_mgr.max_desc = NES_MAX_LRO_DESCRIPTORS;
nesvnic->lro_mgr.lro_arr = nesvnic->lro_desc;
@@ -1895,28 +1918,29 @@ void nes_destroy_nic_qp(struct nes_vnic *nesvnic)
struct nes_device *nesdev = nesvnic->nesdev;
struct nes_hw_cqp_wqe *cqp_wqe;
struct nes_hw_nic_sq_wqe *nic_sqe;
- struct nes_hw_nic_rq_wqe *nic_rqe;
__le16 *wqe_fragment_length;
u16 wqe_fragment_index;
- u64 wqe_frag;
u32 cqp_head;
u32 wqm_cfg0;
unsigned long flags;
+ struct sk_buff *rx_skb;
+ struct nes_rskb_cb *cb;
int ret;
+ if (nesdev->nesadapter->allow_unaligned_fpdus)
+ nes_destroy_mgt(nesvnic);
+
/* clear wqe stall before destroying NIC QP */
wqm_cfg0 = nes_read_indexed(nesdev, NES_IDX_WQM_CONFIG0);
nes_write_indexed(nesdev, NES_IDX_WQM_CONFIG0, wqm_cfg0 & 0xFFFF7FFF);
/* Free remaining NIC receive buffers */
while (nesvnic->nic.rq_head != nesvnic->nic.rq_tail) {
- nic_rqe = &nesvnic->nic.rq_vbase[nesvnic->nic.rq_tail];
- wqe_frag = (u64)le32_to_cpu(
- nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX]);
- wqe_frag |= ((u64)le32_to_cpu(
- nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX]))<<32;
- pci_unmap_single(nesdev->pcidev, (dma_addr_t)wqe_frag,
- nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ rx_skb = nesvnic->nic.rx_skb[nesvnic->nic.rq_tail];
+ cb = (struct nes_rskb_cb *)&rx_skb->cb[0];
+ pci_unmap_single(nesdev->pcidev, cb->busaddr, cb->maplen,
+ PCI_DMA_FROMDEVICE);
+
dev_kfree_skb(nesvnic->nic.rx_skb[nesvnic->nic.rq_tail++]);
nesvnic->nic.rq_tail &= (nesvnic->nic.rq_size - 1);
}
@@ -2775,6 +2799,7 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
struct nes_hw_nic_sq_wqe *nic_sqe;
struct sk_buff *skb;
struct sk_buff *rx_skb;
+ struct nes_rskb_cb *cb;
__le16 *wqe_fragment_length;
u32 head;
u32 cq_size;
@@ -2859,6 +2884,8 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
bus_address += ((u64)le32_to_cpu(nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX])) << 32;
pci_unmap_single(nesdev->pcidev, bus_address,
nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ cb = (struct nes_rskb_cb *)&rx_skb->cb[0];
+ cb->busaddr = 0;
/* rx_skb->tail = rx_skb->data + rx_pkt_size; */
/* rx_skb->len = rx_pkt_size; */
rx_skb->len = 0; /* TODO: see if this is necessary */
@@ -2917,24 +2944,19 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
goto skip_rx_indicate0;
- if ((cqe_misc & NES_NIC_CQE_TAG_VALID) &&
- (nesvnic->vlan_grp != NULL)) {
+ if (cqe_misc & NES_NIC_CQE_TAG_VALID) {
vlan_tag = (u16)(le32_to_cpu(
cq->cq_vbase[head].cqe_words[NES_NIC_CQE_TAG_PKT_TYPE_IDX])
>> 16);
nes_debug(NES_DBG_CQ, "%s: Reporting stripped VLAN packet. Tag = 0x%04X\n",
nesvnic->netdev->name, vlan_tag);
- if (nes_use_lro)
- lro_vlan_hwaccel_receive_skb(&nesvnic->lro_mgr, rx_skb,
- nesvnic->vlan_grp, vlan_tag, NULL);
- else
- nes_vlan_rx(rx_skb, nesvnic->vlan_grp, vlan_tag);
- } else {
- if (nes_use_lro)
- lro_receive_skb(&nesvnic->lro_mgr, rx_skb, NULL);
- else
- nes_netif_rx(rx_skb);
+
+ __vlan_hwaccel_put_tag(rx_skb, vlan_tag);
}
+ if (nes_use_lro)
+ lro_receive_skb(&nesvnic->lro_mgr, rx_skb, NULL);
+ else
+ netif_receive_skb(rx_skb);
skip_rx_indicate0:
;
@@ -2988,6 +3010,7 @@ skip_rx_indicate0:
}
+
/**
* nes_cqp_ce_handler
*/
@@ -3002,6 +3025,8 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
u32 cq_size;
u32 cqe_count=0;
u32 error_code;
+ u32 opcode;
+ u32 ctx_index;
/* u32 counter; */
head = cq->cq_head;
@@ -3012,12 +3037,9 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
/* nes_debug(NES_DBG_CQP, "head=%u cqe_words=%08X\n", head,
le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX])); */
- if (le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX]) & NES_CQE_VALID) {
- u64temp = (((u64)(le32_to_cpu(cq->cq_vbase[head].
- cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX]))) << 32) |
- ((u64)(le32_to_cpu(cq->cq_vbase[head].
- cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX])));
- cqp = *((struct nes_hw_cqp **)&u64temp);
+ opcode = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX]);
+ if (opcode & NES_CQE_VALID) {
+ cqp = &nesdev->cqp;
error_code = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_ERROR_CODE_IDX]);
if (error_code) {
@@ -3026,15 +3048,14 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX])&0x3f,
(u16)(error_code >> 16),
(u16)error_code);
- nes_debug(NES_DBG_CQP, "cqp: qp_id=%u, sq_head=%u, sq_tail=%u\n",
- cqp->qp_id, cqp->sq_head, cqp->sq_tail);
}
- u64temp = (((u64)(le32_to_cpu(nesdev->cqp.sq_vbase[cqp->sq_tail].
- wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX]))) << 32) |
- ((u64)(le32_to_cpu(nesdev->cqp.sq_vbase[cqp->sq_tail].
- wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX])));
- cqp_request = *((struct nes_cqp_request **)&u64temp);
+ u64temp = (((u64)(le32_to_cpu(cq->cq_vbase[head].
+ cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX]))) << 32) |
+ ((u64)(le32_to_cpu(cq->cq_vbase[head].
+ cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX])));
+
+ cqp_request = (struct nes_cqp_request *)(unsigned long)u64temp;
if (cqp_request) {
if (cqp_request->waiting) {
/* nes_debug(NES_DBG_CQP, "%s: Waking up requestor\n"); */
@@ -3080,9 +3101,15 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
cqp_wqe = &nesdev->cqp.sq_vbase[head];
memcpy(cqp_wqe, &cqp_request->cqp_wqe, sizeof(*cqp_wqe));
barrier();
- cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] =
+
+ opcode = cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX];
+ if ((opcode & NES_CQP_OPCODE_MASK) == NES_CQP_DOWNLOAD_SEGMENT)
+ ctx_index = NES_CQP_WQE_DL_COMP_CTX_LOW_IDX;
+ else
+ ctx_index = NES_CQP_WQE_COMP_CTX_LOW_IDX;
+ cqp_wqe->wqe_words[ctx_index] =
cpu_to_le32((u32)((unsigned long)cqp_request));
- cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] =
+ cqp_wqe->wqe_words[ctx_index + 1] =
cpu_to_le32((u32)(upper_32_bits((unsigned long)cqp_request)));
nes_debug(NES_DBG_CQP, "CQP request %p (opcode 0x%02X) put on CQPs SQ wqe%u.\n",
cqp_request, le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX])&0x3f, head);
@@ -3098,7 +3125,6 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
nes_read32(nesdev->regs+NES_CQE_ALLOC);
}
-
static u8 *locate_mpa(u8 *pkt, u32 aeq_info)
{
if (aeq_info & NES_AEQE_Q2_DATA_ETHERNET) {
@@ -3558,9 +3584,9 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
aeqe_cq_id = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]);
if (aeq_info & NES_AEQE_QP) {
- if ((!nes_is_resource_allocated(nesadapter, nesadapter->allocated_qps,
- aeqe_cq_id)) ||
- (atomic_read(&nesqp->close_timer_started)))
+ if (!nes_is_resource_allocated(nesadapter,
+ nesadapter->allocated_qps,
+ aeqe_cq_id))
return;
}
@@ -3571,8 +3597,7 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
if (atomic_inc_return(&nesqp->close_timer_started) == 1) {
if ((tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) &&
- (nesqp->ibqp_state == IB_QPS_RTS) &&
- ((nesadapter->eeprom_version >> 16) != NES_A0)) {
+ (nesqp->ibqp_state == IB_QPS_RTS)) {
spin_lock_irqsave(&nesqp->lock, flags);
nesqp->hw_iwarp_state = iwarp_state;
nesqp->hw_tcp_state = tcp_state;
@@ -3599,9 +3624,10 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
return;
}
spin_lock_irqsave(&nesqp->lock, flags);
- nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_CLOSING;
+ nesqp->hw_iwarp_state = iwarp_state;
+ nesqp->hw_tcp_state = tcp_state;
+ nesqp->last_aeq = async_event_id;
spin_unlock_irqrestore(&nesqp->lock, flags);
- nes_hw_modify_qp(nesdev, nesqp, NES_CQP_QP_IWARP_STATE_CLOSING, 0, 0);
nes_cm_disconn(nesqp);
break;
@@ -3699,7 +3725,9 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
case NES_AEQE_AEID_ROE_INVALID_RDMA_WRITE_OR_READ_RESP:
printk(KERN_ERR PFX "QP[%u] async_event_id=0x%04X IB_EVENT_QP_FATAL\n",
nesqp->hwqp.qp_id, async_event_id);
- nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL);
+ print_ip(nesqp->cm_node);
+ if (!atomic_read(&nesqp->close_timer_started))
+ nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL);
break;
case NES_AEQE_AEID_CQ_OPERATION_ERROR:
diff --git a/drivers/infiniband/hw/nes/nes_hw.h b/drivers/infiniband/hw/nes/nes_hw.h
index 91594116f947..0b590e152c6a 100644
--- a/drivers/infiniband/hw/nes/nes_hw.h
+++ b/drivers/infiniband/hw/nes/nes_hw.h
@@ -47,6 +47,11 @@
#define NES_MULTICAST_PF_MAX 8
#define NES_A0 3
+#define NES_ENABLE_PAU 0x07000001
+#define NES_DISABLE_PAU 0x07000000
+#define NES_PAU_COUNTER 10
+#define NES_CQP_OPCODE_MASK 0x3f
+
enum pci_regs {
NES_INT_STAT = 0x0000,
NES_INT_MASK = 0x0004,
@@ -73,8 +78,10 @@ enum indexed_regs {
NES_IDX_QP_CONTROL = 0x0040,
NES_IDX_FLM_CONTROL = 0x0080,
NES_IDX_INT_CPU_STATUS = 0x00a0,
+ NES_IDX_GPR_TRIGGER = 0x00bc,
NES_IDX_GPIO_CONTROL = 0x00f0,
NES_IDX_GPIO_DATA = 0x00f4,
+ NES_IDX_GPR2 = 0x010c,
NES_IDX_TCP_CONFIG0 = 0x01e4,
NES_IDX_TCP_TIMER_CONFIG = 0x01ec,
NES_IDX_TCP_NOW = 0x01f0,
@@ -202,6 +209,7 @@ enum nes_cqp_opcodes {
NES_CQP_REGISTER_SHARED_STAG = 0x0c,
NES_CQP_DEALLOCATE_STAG = 0x0d,
NES_CQP_MANAGE_ARP_CACHE = 0x0f,
+ NES_CQP_DOWNLOAD_SEGMENT = 0x10,
NES_CQP_SUSPEND_QPS = 0x11,
NES_CQP_UPLOAD_CONTEXT = 0x13,
NES_CQP_CREATE_CEQ = 0x16,
@@ -210,7 +218,8 @@ enum nes_cqp_opcodes {
NES_CQP_DESTROY_AEQ = 0x1b,
NES_CQP_LMI_ACCESS = 0x20,
NES_CQP_FLUSH_WQES = 0x22,
- NES_CQP_MANAGE_APBVT = 0x23
+ NES_CQP_MANAGE_APBVT = 0x23,
+ NES_CQP_MANAGE_QUAD_HASH = 0x25
};
enum nes_cqp_wqe_word_idx {
@@ -222,6 +231,14 @@ enum nes_cqp_wqe_word_idx {
NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX = 5,
};
+enum nes_cqp_wqe_word_download_idx { /* format differs from other cqp ops */
+ NES_CQP_WQE_DL_OPCODE_IDX = 0,
+ NES_CQP_WQE_DL_COMP_CTX_LOW_IDX = 1,
+ NES_CQP_WQE_DL_COMP_CTX_HIGH_IDX = 2,
+ NES_CQP_WQE_DL_LENGTH_0_TOTAL_IDX = 3
+ /* For index values 4-15 use NES_NIC_SQ_WQE_ values */
+};
+
enum nes_cqp_cq_wqeword_idx {
NES_CQP_CQ_WQE_PBL_LOW_IDX = 6,
NES_CQP_CQ_WQE_PBL_HIGH_IDX = 7,
@@ -242,6 +259,7 @@ enum nes_cqp_stag_wqeword_idx {
NES_CQP_STAG_WQE_PBL_LEN_IDX = 14
};
+#define NES_CQP_OP_LOGICAL_PORT_SHIFT 26
#define NES_CQP_OP_IWARP_STATE_SHIFT 28
#define NES_CQP_OP_TERMLEN_SHIFT 28
@@ -599,6 +617,7 @@ enum nes_nic_sq_wqe_bits {
enum nes_nic_cqe_word_idx {
NES_NIC_CQE_ACCQP_ID_IDX = 0,
+ NES_NIC_CQE_HASH_RCVNXT = 1,
NES_NIC_CQE_TAG_PKT_TYPE_IDX = 2,
NES_NIC_CQE_MISC_IDX = 3,
};
@@ -1005,6 +1024,11 @@ struct nes_arp_entry {
#define NES_NIC_CQ_DOWNWARD_TREND 16
#define NES_PFT_SIZE 48
+#define NES_MGT_WQ_COUNT 32
+#define NES_MGT_CTX_SIZE ((NES_NIC_CTX_RQ_SIZE_32) | (NES_NIC_CTX_SQ_SIZE_32))
+#define NES_MGT_QP_OFFSET 36
+#define NES_MGT_QP_COUNT 4
+
struct nes_hw_tune_timer {
/* u16 cq_count; */
u16 threshold_low;
@@ -1118,6 +1142,7 @@ struct nes_adapter {
u32 et_rate_sample_interval;
u32 timer_int_limit;
u32 wqm_quanta;
+ u8 allow_unaligned_fpdus;
/* Adapter base MAC address */
u32 mac_addr_low;
@@ -1211,7 +1236,6 @@ struct nes_vnic {
/* void *mem; */
struct nes_device *nesdev;
struct net_device *netdev;
- struct vlan_group *vlan_grp;
atomic_t rx_skbs_needed;
atomic_t rx_skb_timer_running;
int budget;
@@ -1252,6 +1276,14 @@ struct nes_vnic {
enum ib_event_type delayed_event;
enum ib_event_type last_dispatched_event;
spinlock_t port_ibevent_lock;
+ u32 mgt_mem_size;
+ void *mgt_vbase;
+ dma_addr_t mgt_pbase;
+ struct nes_vnic_mgt *mgtvnic[NES_MGT_QP_COUNT];
+ struct task_struct *mgt_thread;
+ wait_queue_head_t mgt_wait_queue;
+ struct sk_buff_head mgt_skb_list;
+
};
struct nes_ib_device {
@@ -1357,7 +1389,4 @@ struct nes_terminate_hdr {
#define NES_LINK_RECHECK_DELAY msecs_to_jiffies(50)
#define NES_LINK_RECHECK_MAX 60
-#define nes_vlan_rx vlan_hwaccel_receive_skb
-#define nes_netif_rx netif_receive_skb
-
#endif /* __NES_HW_H */
diff --git a/drivers/infiniband/hw/nes/nes_mgt.c b/drivers/infiniband/hw/nes/nes_mgt.c
new file mode 100644
index 000000000000..b3b2a240c6e9
--- /dev/null
+++ b/drivers/infiniband/hw/nes/nes_mgt.c
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (c) 2006 - 2009 Intel-NE, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/kthread.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
+#include "nes.h"
+#include "nes_mgt.h"
+
+atomic_t pau_qps_created;
+atomic_t pau_qps_destroyed;
+
+static void nes_replenish_mgt_rq(struct nes_vnic_mgt *mgtvnic)
+{
+ unsigned long flags;
+ dma_addr_t bus_address;
+ struct sk_buff *skb;
+ struct nes_hw_nic_rq_wqe *nic_rqe;
+ struct nes_hw_mgt *nesmgt;
+ struct nes_device *nesdev;
+ struct nes_rskb_cb *cb;
+ u32 rx_wqes_posted = 0;
+
+ nesmgt = &mgtvnic->mgt;
+ nesdev = mgtvnic->nesvnic->nesdev;
+ spin_lock_irqsave(&nesmgt->rq_lock, flags);
+ if (nesmgt->replenishing_rq != 0) {
+ if (((nesmgt->rq_size - 1) == atomic_read(&mgtvnic->rx_skbs_needed)) &&
+ (atomic_read(&mgtvnic->rx_skb_timer_running) == 0)) {
+ atomic_set(&mgtvnic->rx_skb_timer_running, 1);
+ spin_unlock_irqrestore(&nesmgt->rq_lock, flags);
+ mgtvnic->rq_wqes_timer.expires = jiffies + (HZ / 2); /* 1/2 second */
+ add_timer(&mgtvnic->rq_wqes_timer);
+ } else {
+ spin_unlock_irqrestore(&nesmgt->rq_lock, flags);
+ }
+ return;
+ }
+ nesmgt->replenishing_rq = 1;
+ spin_unlock_irqrestore(&nesmgt->rq_lock, flags);
+ do {
+ skb = dev_alloc_skb(mgtvnic->nesvnic->max_frame_size);
+ if (skb) {
+ skb->dev = mgtvnic->nesvnic->netdev;
+
+ bus_address = pci_map_single(nesdev->pcidev,
+ skb->data, mgtvnic->nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->busaddr = bus_address;
+ cb->maplen = mgtvnic->nesvnic->max_frame_size;
+
+ nic_rqe = &nesmgt->rq_vbase[mgtvnic->mgt.rq_head];
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] =
+ cpu_to_le32(mgtvnic->nesvnic->max_frame_size);
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_3_2_IDX] = 0;
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX] =
+ cpu_to_le32((u32)bus_address);
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX] =
+ cpu_to_le32((u32)((u64)bus_address >> 32));
+ nesmgt->rx_skb[nesmgt->rq_head] = skb;
+ nesmgt->rq_head++;
+ nesmgt->rq_head &= nesmgt->rq_size - 1;
+ atomic_dec(&mgtvnic->rx_skbs_needed);
+ barrier();
+ if (++rx_wqes_posted == 255) {
+ nes_write32(nesdev->regs + NES_WQE_ALLOC, (rx_wqes_posted << 24) | nesmgt->qp_id);
+ rx_wqes_posted = 0;
+ }
+ } else {
+ spin_lock_irqsave(&nesmgt->rq_lock, flags);
+ if (((nesmgt->rq_size - 1) == atomic_read(&mgtvnic->rx_skbs_needed)) &&
+ (atomic_read(&mgtvnic->rx_skb_timer_running) == 0)) {
+ atomic_set(&mgtvnic->rx_skb_timer_running, 1);
+ spin_unlock_irqrestore(&nesmgt->rq_lock, flags);
+ mgtvnic->rq_wqes_timer.expires = jiffies + (HZ / 2); /* 1/2 second */
+ add_timer(&mgtvnic->rq_wqes_timer);
+ } else {
+ spin_unlock_irqrestore(&nesmgt->rq_lock, flags);
+ }
+ break;
+ }
+ } while (atomic_read(&mgtvnic->rx_skbs_needed));
+ barrier();
+ if (rx_wqes_posted)
+ nes_write32(nesdev->regs + NES_WQE_ALLOC, (rx_wqes_posted << 24) | nesmgt->qp_id);
+ nesmgt->replenishing_rq = 0;
+}
+
+/**
+ * nes_mgt_rq_wqes_timeout
+ */
+static void nes_mgt_rq_wqes_timeout(unsigned long parm)
+{
+ struct nes_vnic_mgt *mgtvnic = (struct nes_vnic_mgt *)parm;
+
+ atomic_set(&mgtvnic->rx_skb_timer_running, 0);
+ if (atomic_read(&mgtvnic->rx_skbs_needed))
+ nes_replenish_mgt_rq(mgtvnic);
+}
+
+/**
+ * nes_mgt_free_skb - unmap and free skb
+ */
+static void nes_mgt_free_skb(struct nes_device *nesdev, struct sk_buff *skb, u32 dir)
+{
+ struct nes_rskb_cb *cb;
+
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ pci_unmap_single(nesdev->pcidev, cb->busaddr, cb->maplen, dir);
+ cb->busaddr = 0;
+ dev_kfree_skb_any(skb);
+}
+
+/**
+ * nes_download_callback - handle download completions
+ */
+static void nes_download_callback(struct nes_device *nesdev, struct nes_cqp_request *cqp_request)
+{
+ struct pau_fpdu_info *fpdu_info = cqp_request->cqp_callback_pointer;
+ struct nes_qp *nesqp = fpdu_info->nesqp;
+ struct sk_buff *skb;
+ int i;
+
+ for (i = 0; i < fpdu_info->frag_cnt; i++) {
+ skb = fpdu_info->frags[i].skb;
+ if (fpdu_info->frags[i].cmplt) {
+ nes_mgt_free_skb(nesdev, skb, PCI_DMA_TODEVICE);
+ nes_rem_ref_cm_node(nesqp->cm_node);
+ }
+ }
+
+ if (fpdu_info->hdr_vbase)
+ pci_free_consistent(nesdev->pcidev, fpdu_info->hdr_len,
+ fpdu_info->hdr_vbase, fpdu_info->hdr_pbase);
+ kfree(fpdu_info);
+}
+
+/**
+ * nes_get_seq - Get the seq, ack_seq and window from the packet
+ */
+static u32 nes_get_seq(struct sk_buff *skb, u32 *ack, u16 *wnd, u32 *fin_rcvd, u32 *rst_rcvd)
+{
+ struct nes_rskb_cb *cb = (struct nes_rskb_cb *)&skb->cb[0];
+ struct iphdr *iph = (struct iphdr *)(cb->data_start + ETH_HLEN);
+ struct tcphdr *tcph = (struct tcphdr *)(((char *)iph) + (4 * iph->ihl));
+
+ *ack = be32_to_cpu(tcph->ack_seq);
+ *wnd = be16_to_cpu(tcph->window);
+ *fin_rcvd = tcph->fin;
+ *rst_rcvd = tcph->rst;
+ return be32_to_cpu(tcph->seq);
+}
+
+/**
+ * nes_get_next_skb - Get the next skb based on where current skb is in the queue
+ */
+static struct sk_buff *nes_get_next_skb(struct nes_device *nesdev, struct nes_qp *nesqp,
+ struct sk_buff *skb, u32 nextseq, u32 *ack,
+ u16 *wnd, u32 *fin_rcvd, u32 *rst_rcvd)
+{
+ u32 seq;
+ bool processacks;
+ struct sk_buff *old_skb;
+
+ if (skb) {
+ /* Continue processing fpdu */
+ if (skb->next == (struct sk_buff *)&nesqp->pau_list)
+ goto out;
+ skb = skb->next;
+ processacks = false;
+ } else {
+ /* Starting a new one */
+ if (skb_queue_empty(&nesqp->pau_list))
+ goto out;
+ skb = skb_peek(&nesqp->pau_list);
+ processacks = true;
+ }
+
+ while (1) {
+ seq = nes_get_seq(skb, ack, wnd, fin_rcvd, rst_rcvd);
+ if (seq == nextseq) {
+ if (skb->len || processacks)
+ break;
+ } else if (after(seq, nextseq)) {
+ goto out;
+ }
+
+ if (skb->next == (struct sk_buff *)&nesqp->pau_list)
+ goto out;
+
+ old_skb = skb;
+ skb = skb->next;
+ skb_unlink(old_skb, &nesqp->pau_list);
+ nes_mgt_free_skb(nesdev, old_skb, PCI_DMA_TODEVICE);
+ nes_rem_ref_cm_node(nesqp->cm_node);
+ }
+ return skb;
+
+out:
+ return NULL;
+}
+
+/**
+ * get_fpdu_info - Find the next complete fpdu and return its fragments.
+ */
+static int get_fpdu_info(struct nes_device *nesdev, struct nes_qp *nesqp,
+ struct pau_fpdu_info **pau_fpdu_info)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct tcphdr *tcph;
+ struct nes_rskb_cb *cb;
+ struct pau_fpdu_info *fpdu_info = NULL;
+ struct pau_fpdu_frag frags[MAX_FPDU_FRAGS];
+ unsigned long flags;
+ u32 fpdu_len = 0;
+ u32 tmp_len;
+ int frag_cnt = 0;
+ u32 tot_len;
+ u32 frag_tot;
+ u32 ack;
+ u32 fin_rcvd;
+ u32 rst_rcvd;
+ u16 wnd;
+ int i;
+ int rc = 0;
+
+ *pau_fpdu_info = NULL;
+
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
+ skb = nes_get_next_skb(nesdev, nesqp, NULL, nesqp->pau_rcv_nxt, &ack, &wnd, &fin_rcvd, &rst_rcvd);
+ if (!skb) {
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ goto out;
+ }
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ if (skb->len) {
+ fpdu_len = be16_to_cpu(*(__be16 *) skb->data) + MPA_FRAMING;
+ fpdu_len = (fpdu_len + 3) & 0xfffffffc;
+ tmp_len = fpdu_len;
+
+ /* See if we have all of the fpdu */
+ frag_tot = 0;
+ memset(&frags, 0, sizeof frags);
+ for (i = 0; i < MAX_FPDU_FRAGS; i++) {
+ frags[i].physaddr = cb->busaddr;
+ frags[i].physaddr += skb->data - cb->data_start;
+ frags[i].frag_len = min(tmp_len, skb->len);
+ frags[i].skb = skb;
+ frags[i].cmplt = (skb->len == frags[i].frag_len);
+ frag_tot += frags[i].frag_len;
+ frag_cnt++;
+
+ tmp_len -= frags[i].frag_len;
+ if (tmp_len == 0)
+ break;
+
+ skb = nes_get_next_skb(nesdev, nesqp, skb,
+ nesqp->pau_rcv_nxt + frag_tot, &ack, &wnd, &fin_rcvd, &rst_rcvd);
+ if (!skb) {
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ goto out;
+ } else if (rst_rcvd) {
+ /* rst received in the middle of fpdu */
+ for (; i >= 0; i--) {
+ skb_unlink(frags[i].skb, &nesqp->pau_list);
+ nes_mgt_free_skb(nesdev, frags[i].skb, PCI_DMA_TODEVICE);
+ }
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ frags[0].physaddr = cb->busaddr;
+ frags[0].physaddr += skb->data - cb->data_start;
+ frags[0].frag_len = skb->len;
+ frags[0].skb = skb;
+ frags[0].cmplt = true;
+ frag_cnt = 1;
+ break;
+ }
+
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ }
+ } else {
+ /* no data */
+ frags[0].physaddr = cb->busaddr;
+ frags[0].frag_len = 0;
+ frags[0].skb = skb;
+ frags[0].cmplt = true;
+ frag_cnt = 1;
+ }
+
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+
+ /* Found one */
+ fpdu_info = kzalloc(sizeof(*fpdu_info), GFP_ATOMIC);
+ if (fpdu_info == NULL) {
+ nes_debug(NES_DBG_PAU, "Failed to alloc a fpdu_info.\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ fpdu_info->cqp_request = nes_get_cqp_request(nesdev);
+ if (fpdu_info->cqp_request == NULL) {
+ nes_debug(NES_DBG_PAU, "Failed to get a cqp_request.\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ cb = (struct nes_rskb_cb *)&frags[0].skb->cb[0];
+ iph = (struct iphdr *)(cb->data_start + ETH_HLEN);
+ tcph = (struct tcphdr *)(((char *)iph) + (4 * iph->ihl));
+ fpdu_info->hdr_len = (((unsigned char *)tcph) + 4 * (tcph->doff)) - cb->data_start;
+ fpdu_info->data_len = fpdu_len;
+ tot_len = fpdu_info->hdr_len + fpdu_len - ETH_HLEN;
+
+ if (frags[0].cmplt) {
+ fpdu_info->hdr_pbase = cb->busaddr;
+ fpdu_info->hdr_vbase = NULL;
+ } else {
+ fpdu_info->hdr_vbase = pci_alloc_consistent(nesdev->pcidev,
+ fpdu_info->hdr_len, &fpdu_info->hdr_pbase);
+ if (!fpdu_info->hdr_vbase) {
+ nes_debug(NES_DBG_PAU, "Unable to allocate memory for pau first frag\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Copy hdrs, adjusting len and seqnum */
+ memcpy(fpdu_info->hdr_vbase, cb->data_start, fpdu_info->hdr_len);
+ iph = (struct iphdr *)(fpdu_info->hdr_vbase + ETH_HLEN);
+ tcph = (struct tcphdr *)(((char *)iph) + (4 * iph->ihl));
+ }
+
+ iph->tot_len = cpu_to_be16(tot_len);
+ iph->saddr = cpu_to_be32(0x7f000001);
+
+ tcph->seq = cpu_to_be32(nesqp->pau_rcv_nxt);
+ tcph->ack_seq = cpu_to_be32(ack);
+ tcph->window = cpu_to_be16(wnd);
+
+ nesqp->pau_rcv_nxt += fpdu_len + fin_rcvd;
+
+ memcpy(fpdu_info->frags, frags, sizeof(fpdu_info->frags));
+ fpdu_info->frag_cnt = frag_cnt;
+ fpdu_info->nesqp = nesqp;
+ *pau_fpdu_info = fpdu_info;
+
+ /* Update skb's for next pass */
+ for (i = 0; i < frag_cnt; i++) {
+ cb = (struct nes_rskb_cb *)&frags[i].skb->cb[0];
+ skb_pull(frags[i].skb, frags[i].frag_len);
+
+ if (frags[i].skb->len == 0) {
+ /* Pull skb off the list - it will be freed in the callback */
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
+ skb_unlink(frags[i].skb, &nesqp->pau_list);
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ } else {
+ /* Last skb still has data so update the seq */
+ iph = (struct iphdr *)(cb->data_start + ETH_HLEN);
+ tcph = (struct tcphdr *)(((char *)iph) + (4 * iph->ihl));
+ tcph->seq = cpu_to_be32(nesqp->pau_rcv_nxt);
+ }
+ }
+
+out:
+ if (rc) {
+ if (fpdu_info) {
+ if (fpdu_info->cqp_request)
+ nes_put_cqp_request(nesdev, fpdu_info->cqp_request);
+ kfree(fpdu_info);
+ }
+ }
+ return rc;
+}
+
+/**
+ * forward_fpdu - send complete fpdus, one at a time
+ */
+static int forward_fpdus(struct nes_vnic *nesvnic, struct nes_qp *nesqp)
+{
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct pau_fpdu_info *fpdu_info;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_cqp_request *cqp_request;
+ u64 u64tmp;
+ u32 u32tmp;
+ int rc;
+
+ while (1) {
+ rc = get_fpdu_info(nesdev, nesqp, &fpdu_info);
+ if (fpdu_info == NULL)
+ return rc;
+
+ cqp_request = fpdu_info->cqp_request;
+ cqp_wqe = &cqp_request->cqp_wqe;
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_DL_OPCODE_IDX,
+ NES_CQP_DOWNLOAD_SEGMENT |
+ (((u32)nesvnic->logical_port) << NES_CQP_OP_LOGICAL_PORT_SHIFT));
+
+ u32tmp = fpdu_info->hdr_len << 16;
+ u32tmp |= fpdu_info->hdr_len + (u32)fpdu_info->data_len;
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_DL_LENGTH_0_TOTAL_IDX,
+ u32tmp);
+
+ u32tmp = (fpdu_info->frags[1].frag_len << 16) | fpdu_info->frags[0].frag_len;
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_LENGTH_2_1_IDX,
+ u32tmp);
+
+ u32tmp = (fpdu_info->frags[3].frag_len << 16) | fpdu_info->frags[2].frag_len;
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_LENGTH_4_3_IDX,
+ u32tmp);
+
+ u64tmp = (u64)fpdu_info->hdr_pbase;
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG0_LOW_IDX,
+ lower_32_bits(u64tmp));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG0_HIGH_IDX,
+ upper_32_bits(u64tmp >> 32));
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG1_LOW_IDX,
+ lower_32_bits(fpdu_info->frags[0].physaddr));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG1_HIGH_IDX,
+ upper_32_bits(fpdu_info->frags[0].physaddr));
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG2_LOW_IDX,
+ lower_32_bits(fpdu_info->frags[1].physaddr));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG2_HIGH_IDX,
+ upper_32_bits(fpdu_info->frags[1].physaddr));
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG3_LOW_IDX,
+ lower_32_bits(fpdu_info->frags[2].physaddr));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG3_HIGH_IDX,
+ upper_32_bits(fpdu_info->frags[2].physaddr));
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG4_LOW_IDX,
+ lower_32_bits(fpdu_info->frags[3].physaddr));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG4_HIGH_IDX,
+ upper_32_bits(fpdu_info->frags[3].physaddr));
+
+ cqp_request->cqp_callback_pointer = fpdu_info;
+ cqp_request->callback = 1;
+ cqp_request->cqp_callback = nes_download_callback;
+
+ atomic_set(&cqp_request->refcount, 1);
+ nes_post_cqp_request(nesdev, cqp_request);
+ }
+
+ return 0;
+}
+
+static void process_fpdus(struct nes_vnic *nesvnic, struct nes_qp *nesqp)
+{
+ int again = 1;
+ unsigned long flags;
+
+ do {
+ /* Ignore rc - if it failed, tcp retries will cause it to try again */
+ forward_fpdus(nesvnic, nesqp);
+
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
+ if (nesqp->pau_pending) {
+ nesqp->pau_pending = 0;
+ } else {
+ nesqp->pau_busy = 0;
+ again = 0;
+ }
+
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ } while (again);
+}
+
+/**
+ * queue_fpdus - Handle fpdu's that hw passed up to sw
+ */
+static void queue_fpdus(struct sk_buff *skb, struct nes_vnic *nesvnic, struct nes_qp *nesqp)
+{
+ struct sk_buff *tmpskb;
+ struct nes_rskb_cb *cb;
+ struct iphdr *iph;
+ struct tcphdr *tcph;
+ unsigned char *tcph_end;
+ u32 rcv_nxt;
+ u32 rcv_wnd;
+ u32 seqnum;
+ u32 len;
+ bool process_it = false;
+ unsigned long flags;
+
+ /* Move data ptr to after tcp header */
+ iph = (struct iphdr *)skb->data;
+ tcph = (struct tcphdr *)(((char *)iph) + (4 * iph->ihl));
+ seqnum = be32_to_cpu(tcph->seq);
+ tcph_end = (((char *)tcph) + (4 * tcph->doff));
+
+ len = be16_to_cpu(iph->tot_len);
+ if (skb->len > len)
+ skb_trim(skb, len);
+ skb_pull(skb, tcph_end - skb->data);
+
+ /* Initialize tracking values */
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->seqnum = seqnum;
+
+ /* Make sure data is in the receive window */
+ rcv_nxt = nesqp->pau_rcv_nxt;
+ rcv_wnd = le32_to_cpu(nesqp->nesqp_context->rcv_wnd);
+ if (!between(seqnum, rcv_nxt, (rcv_nxt + rcv_wnd))) {
+ nes_mgt_free_skb(nesvnic->nesdev, skb, PCI_DMA_TODEVICE);
+ nes_rem_ref_cm_node(nesqp->cm_node);
+ return;
+ }
+
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
+
+ if (nesqp->pau_busy)
+ nesqp->pau_pending = 1;
+ else
+ nesqp->pau_busy = 1;
+
+ /* Queue skb by sequence number */
+ if (skb_queue_len(&nesqp->pau_list) == 0) {
+ skb_queue_head(&nesqp->pau_list, skb);
+ } else {
+ tmpskb = nesqp->pau_list.next;
+ while (tmpskb != (struct sk_buff *)&nesqp->pau_list) {
+ cb = (struct nes_rskb_cb *)&tmpskb->cb[0];
+ if (before(seqnum, cb->seqnum))
+ break;
+ tmpskb = tmpskb->next;
+ }
+ skb_insert(tmpskb, skb, &nesqp->pau_list);
+ }
+ if (nesqp->pau_state == PAU_READY)
+ process_it = true;
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+
+ if (process_it)
+ process_fpdus(nesvnic, nesqp);
+
+ return;
+}
+
+/**
+ * mgt_thread - Handle mgt skbs in a safe context
+ */
+static int mgt_thread(void *context)
+{
+ struct nes_vnic *nesvnic = context;
+ struct sk_buff *skb;
+ struct nes_rskb_cb *cb;
+
+ while (!kthread_should_stop()) {
+ wait_event_interruptible(nesvnic->mgt_wait_queue,
+ skb_queue_len(&nesvnic->mgt_skb_list) || kthread_should_stop());
+ while ((skb_queue_len(&nesvnic->mgt_skb_list)) && !kthread_should_stop()) {
+ skb = skb_dequeue(&nesvnic->mgt_skb_list);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->data_start = skb->data - ETH_HLEN;
+ cb->busaddr = pci_map_single(nesvnic->nesdev->pcidev, cb->data_start,
+ nesvnic->max_frame_size, PCI_DMA_TODEVICE);
+ queue_fpdus(skb, nesvnic, cb->nesqp);
+ }
+ }
+
+ /* Closing down so delete any entries on the queue */
+ while (skb_queue_len(&nesvnic->mgt_skb_list)) {
+ skb = skb_dequeue(&nesvnic->mgt_skb_list);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ nes_rem_ref_cm_node(cb->nesqp->cm_node);
+ dev_kfree_skb_any(skb);
+ }
+ return 0;
+}
+
+/**
+ * nes_queue_skbs - Queue skb so it can be handled in a thread context
+ */
+void nes_queue_mgt_skbs(struct sk_buff *skb, struct nes_vnic *nesvnic, struct nes_qp *nesqp)
+{
+ struct nes_rskb_cb *cb;
+
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->nesqp = nesqp;
+ skb_queue_tail(&nesvnic->mgt_skb_list, skb);
+ wake_up_interruptible(&nesvnic->mgt_wait_queue);
+}
+
+void nes_destroy_pau_qp(struct nes_device *nesdev, struct nes_qp *nesqp)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+ atomic_inc(&pau_qps_destroyed);
+
+ /* Free packets that have not yet been forwarded */
+ /* Lock is acquired by skb_dequeue when removing the skb */
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
+ while (skb_queue_len(&nesqp->pau_list)) {
+ skb = skb_dequeue(&nesqp->pau_list);
+ nes_mgt_free_skb(nesdev, skb, PCI_DMA_TODEVICE);
+ nes_rem_ref_cm_node(nesqp->cm_node);
+ }
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+}
+
+static void nes_chg_qh_handler(struct nes_device *nesdev, struct nes_cqp_request *cqp_request)
+{
+ struct pau_qh_chg *qh_chg = cqp_request->cqp_callback_pointer;
+ struct nes_cqp_request *new_request;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_adapter *nesadapter;
+ struct nes_qp *nesqp;
+ struct nes_v4_quad nes_quad;
+ u32 crc_value;
+ u64 u64temp;
+
+ nesadapter = nesdev->nesadapter;
+ nesqp = qh_chg->nesqp;
+
+ /* Should we handle the bad completion */
+ if (cqp_request->major_code) {
+ printk(KERN_ERR PFX "Invalid cqp_request major_code=0x%x\n",
+ cqp_request->major_code);
+ WARN_ON(1);
+ }
+
+ switch (nesqp->pau_state) {
+ case PAU_DEL_QH:
+ /* Old hash code deleted, now set the new one */
+ nesqp->pau_state = PAU_ADD_LB_QH;
+ new_request = nes_get_cqp_request(nesdev);
+ if (new_request == NULL) {
+ nes_debug(NES_DBG_PAU, "Failed to get a new_request.\n");
+ WARN_ON(1);
+ return;
+ }
+
+ memset(&nes_quad, 0, sizeof(nes_quad));
+ nes_quad.DstIpAdrIndex =
+ cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24);
+ nes_quad.SrcIpadr = cpu_to_be32(0x7f000001);
+ nes_quad.TcpPorts[0] = swab16(nesqp->nesqp_context->tcpPorts[1]);
+ nes_quad.TcpPorts[1] = swab16(nesqp->nesqp_context->tcpPorts[0]);
+
+ /* Produce hash key */
+ crc_value = get_crc_value(&nes_quad);
+ nesqp->hte_index = cpu_to_be32(crc_value ^ 0xffffffff);
+ nes_debug(NES_DBG_PAU, "new HTE Index = 0x%08X, CRC = 0x%08X\n",
+ nesqp->hte_index, nesqp->hte_index & nesadapter->hte_index_mask);
+
+ nesqp->hte_index &= nesadapter->hte_index_mask;
+ nesqp->nesqp_context->hte_index = cpu_to_le32(nesqp->hte_index);
+ nesqp->nesqp_context->ip0 = cpu_to_le32(0x7f000001);
+ nesqp->nesqp_context->rcv_nxt = cpu_to_le32(nesqp->pau_rcv_nxt);
+
+ cqp_wqe = &new_request->cqp_wqe;
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words,
+ NES_CQP_WQE_OPCODE_IDX, NES_CQP_MANAGE_QUAD_HASH |
+ NES_CQP_QP_TYPE_IWARP | NES_CQP_QP_CONTEXT_VALID | NES_CQP_QP_IWARP_STATE_RTS);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX, nesqp->hwqp.qp_id);
+ u64temp = (u64)nesqp->nesqp_context_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, u64temp);
+
+ nes_debug(NES_DBG_PAU, "Waiting for CQP completion for adding the quad hash.\n");
+
+ new_request->cqp_callback_pointer = qh_chg;
+ new_request->callback = 1;
+ new_request->cqp_callback = nes_chg_qh_handler;
+ atomic_set(&new_request->refcount, 1);
+ nes_post_cqp_request(nesdev, new_request);
+ break;
+
+ case PAU_ADD_LB_QH:
+ /* Start processing the queued fpdu's */
+ nesqp->pau_state = PAU_READY;
+ process_fpdus(qh_chg->nesvnic, qh_chg->nesqp);
+ kfree(qh_chg);
+ break;
+ }
+}
+
+/**
+ * nes_change_quad_hash
+ */
+static int nes_change_quad_hash(struct nes_device *nesdev,
+ struct nes_vnic *nesvnic, struct nes_qp *nesqp)
+{
+ struct nes_cqp_request *cqp_request = NULL;
+ struct pau_qh_chg *qh_chg = NULL;
+ u64 u64temp;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ int ret = 0;
+
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_PAU, "Failed to get a cqp_request.\n");
+ ret = -ENOMEM;
+ goto chg_qh_err;
+ }
+
+ qh_chg = kmalloc(sizeof *qh_chg, GFP_ATOMIC);
+ if (qh_chg == NULL) {
+ nes_debug(NES_DBG_PAU, "Failed to get a cqp_request.\n");
+ ret = -ENOMEM;
+ goto chg_qh_err;
+ }
+ qh_chg->nesdev = nesdev;
+ qh_chg->nesvnic = nesvnic;
+ qh_chg->nesqp = nesqp;
+ nesqp->pau_state = PAU_DEL_QH;
+
+ cqp_wqe = &cqp_request->cqp_wqe;
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words,
+ NES_CQP_WQE_OPCODE_IDX, NES_CQP_MANAGE_QUAD_HASH | NES_CQP_QP_DEL_HTE |
+ NES_CQP_QP_TYPE_IWARP | NES_CQP_QP_CONTEXT_VALID | NES_CQP_QP_IWARP_STATE_RTS);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX, nesqp->hwqp.qp_id);
+ u64temp = (u64)nesqp->nesqp_context_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, u64temp);
+
+ nes_debug(NES_DBG_PAU, "Waiting for CQP completion for deleting the quad hash.\n");
+
+ cqp_request->cqp_callback_pointer = qh_chg;
+ cqp_request->callback = 1;
+ cqp_request->cqp_callback = nes_chg_qh_handler;
+ atomic_set(&cqp_request->refcount, 1);
+ nes_post_cqp_request(nesdev, cqp_request);
+
+ return ret;
+
+chg_qh_err:
+ kfree(qh_chg);
+ if (cqp_request)
+ nes_put_cqp_request(nesdev, cqp_request);
+ return ret;
+}
+
+/**
+ * nes_mgt_ce_handler
+ * This management code deals with any packed and unaligned (pau) fpdu's
+ * that the hardware cannot handle.
+ */
+static void nes_mgt_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
+{
+ struct nes_vnic_mgt *mgtvnic = container_of(cq, struct nes_vnic_mgt, mgt_cq);
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u32 head;
+ u32 cq_size;
+ u32 cqe_count = 0;
+ u32 cqe_misc;
+ u32 qp_id = 0;
+ u32 skbs_needed;
+ unsigned long context;
+ struct nes_qp *nesqp;
+ struct sk_buff *rx_skb;
+ struct nes_rskb_cb *cb;
+
+ head = cq->cq_head;
+ cq_size = cq->cq_size;
+
+ while (1) {
+ cqe_misc = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_NIC_CQE_MISC_IDX]);
+ if (!(cqe_misc & NES_NIC_CQE_VALID))
+ break;
+
+ nesqp = NULL;
+ if (cqe_misc & NES_NIC_CQE_ACCQP_VALID) {
+ qp_id = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_NIC_CQE_ACCQP_ID_IDX]);
+ qp_id &= 0x001fffff;
+ if (qp_id < nesadapter->max_qp) {
+ context = (unsigned long)nesadapter->qp_table[qp_id - NES_FIRST_QPN];
+ nesqp = (struct nes_qp *)context;
+ }
+ }
+
+ if (nesqp) {
+ if (nesqp->pau_mode == false) {
+ nesqp->pau_mode = true; /* First time for this qp */
+ nesqp->pau_rcv_nxt = le32_to_cpu(
+ cq->cq_vbase[head].cqe_words[NES_NIC_CQE_HASH_RCVNXT]);
+ skb_queue_head_init(&nesqp->pau_list);
+ spin_lock_init(&nesqp->pau_lock);
+ atomic_inc(&pau_qps_created);
+ nes_change_quad_hash(nesdev, mgtvnic->nesvnic, nesqp);
+ }
+
+ rx_skb = mgtvnic->mgt.rx_skb[mgtvnic->mgt.rq_tail];
+ rx_skb->len = 0;
+ skb_put(rx_skb, cqe_misc & 0x0000ffff);
+ rx_skb->protocol = eth_type_trans(rx_skb, mgtvnic->nesvnic->netdev);
+ cb = (struct nes_rskb_cb *)&rx_skb->cb[0];
+ pci_unmap_single(nesdev->pcidev, cb->busaddr, cb->maplen, PCI_DMA_FROMDEVICE);
+ cb->busaddr = 0;
+ mgtvnic->mgt.rq_tail++;
+ mgtvnic->mgt.rq_tail &= mgtvnic->mgt.rq_size - 1;
+
+ nes_add_ref_cm_node(nesqp->cm_node);
+ nes_queue_mgt_skbs(rx_skb, mgtvnic->nesvnic, nesqp);
+ } else {
+ printk(KERN_ERR PFX "Invalid QP %d for packed/unaligned handling\n", qp_id);
+ }
+
+ cq->cq_vbase[head].cqe_words[NES_NIC_CQE_MISC_IDX] = 0;
+ cqe_count++;
+ if (++head >= cq_size)
+ head = 0;
+
+ if (cqe_count == 255) {
+ /* Replenish mgt CQ */
+ nes_write32(nesdev->regs + NES_CQE_ALLOC, cq->cq_number | (cqe_count << 16));
+ nesdev->currcq_count += cqe_count;
+ cqe_count = 0;
+ }
+
+ skbs_needed = atomic_inc_return(&mgtvnic->rx_skbs_needed);
+ if (skbs_needed > (mgtvnic->mgt.rq_size >> 1))
+ nes_replenish_mgt_rq(mgtvnic);
+ }
+
+ cq->cq_head = head;
+ nes_write32(nesdev->regs + NES_CQE_ALLOC, NES_CQE_ALLOC_NOTIFY_NEXT |
+ cq->cq_number | (cqe_count << 16));
+ nes_read32(nesdev->regs + NES_CQE_ALLOC);
+ nesdev->currcq_count += cqe_count;
+}
+
+/**
+ * nes_init_mgt_qp
+ */
+int nes_init_mgt_qp(struct nes_device *nesdev, struct net_device *netdev, struct nes_vnic *nesvnic)
+{
+ struct nes_vnic_mgt *mgtvnic;
+ u32 counter;
+ void *vmem;
+ dma_addr_t pmem;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ u32 cqp_head;
+ unsigned long flags;
+ struct nes_hw_nic_qp_context *mgt_context;
+ u64 u64temp;
+ struct nes_hw_nic_rq_wqe *mgt_rqe;
+ struct sk_buff *skb;
+ u32 wqe_count;
+ struct nes_rskb_cb *cb;
+ u32 mgt_mem_size;
+ void *mgt_vbase;
+ dma_addr_t mgt_pbase;
+ int i;
+ int ret;
+
+ /* Allocate space the all mgt QPs once */
+ mgtvnic = kzalloc(NES_MGT_QP_COUNT * sizeof(struct nes_vnic_mgt), GFP_KERNEL);
+ if (mgtvnic == NULL) {
+ nes_debug(NES_DBG_INIT, "Unable to allocate memory for mgt structure\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate fragment, RQ, and CQ; Reuse CEQ based on the PCI function */
+ /* We are not sending from this NIC so sq is not allocated */
+ mgt_mem_size = 256 +
+ (NES_MGT_WQ_COUNT * sizeof(struct nes_hw_nic_rq_wqe)) +
+ (NES_MGT_WQ_COUNT * sizeof(struct nes_hw_nic_cqe)) +
+ sizeof(struct nes_hw_nic_qp_context);
+ mgt_mem_size = (mgt_mem_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+ mgt_vbase = pci_alloc_consistent(nesdev->pcidev, NES_MGT_QP_COUNT * mgt_mem_size, &mgt_pbase);
+ if (!mgt_vbase) {
+ kfree(mgtvnic);
+ nes_debug(NES_DBG_INIT, "Unable to allocate memory for mgt host descriptor rings\n");
+ return -ENOMEM;
+ }
+
+ nesvnic->mgt_mem_size = NES_MGT_QP_COUNT * mgt_mem_size;
+ nesvnic->mgt_vbase = mgt_vbase;
+ nesvnic->mgt_pbase = mgt_pbase;
+
+ skb_queue_head_init(&nesvnic->mgt_skb_list);
+ init_waitqueue_head(&nesvnic->mgt_wait_queue);
+ nesvnic->mgt_thread = kthread_run(mgt_thread, nesvnic, "nes_mgt_thread");
+
+ for (i = 0; i < NES_MGT_QP_COUNT; i++) {
+ mgtvnic->nesvnic = nesvnic;
+ mgtvnic->mgt.qp_id = nesdev->mac_index + NES_MGT_QP_OFFSET + i;
+ memset(mgt_vbase, 0, mgt_mem_size);
+ nes_debug(NES_DBG_INIT, "Allocated mgt QP structures at %p (phys = %016lX), size = %u.\n",
+ mgt_vbase, (unsigned long)mgt_pbase, mgt_mem_size);
+
+ vmem = (void *)(((unsigned long)mgt_vbase + (256 - 1)) &
+ ~(unsigned long)(256 - 1));
+ pmem = (dma_addr_t)(((unsigned long long)mgt_pbase + (256 - 1)) &
+ ~(unsigned long long)(256 - 1));
+
+ spin_lock_init(&mgtvnic->mgt.rq_lock);
+
+ /* setup the RQ */
+ mgtvnic->mgt.rq_vbase = vmem;
+ mgtvnic->mgt.rq_pbase = pmem;
+ mgtvnic->mgt.rq_head = 0;
+ mgtvnic->mgt.rq_tail = 0;
+ mgtvnic->mgt.rq_size = NES_MGT_WQ_COUNT;
+
+ /* setup the CQ */
+ vmem += (NES_MGT_WQ_COUNT * sizeof(struct nes_hw_nic_rq_wqe));
+ pmem += (NES_MGT_WQ_COUNT * sizeof(struct nes_hw_nic_rq_wqe));
+
+ mgtvnic->mgt_cq.cq_number = mgtvnic->mgt.qp_id;
+ mgtvnic->mgt_cq.cq_vbase = vmem;
+ mgtvnic->mgt_cq.cq_pbase = pmem;
+ mgtvnic->mgt_cq.cq_head = 0;
+ mgtvnic->mgt_cq.cq_size = NES_MGT_WQ_COUNT;
+
+ mgtvnic->mgt_cq.ce_handler = nes_mgt_ce_handler;
+
+ /* Send CreateCQ request to CQP */
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ cqp_head = nesdev->cqp.sq_head;
+
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(
+ NES_CQP_CREATE_CQ | NES_CQP_CQ_CEQ_VALID |
+ ((u32)mgtvnic->mgt_cq.cq_size << 16));
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(
+ mgtvnic->mgt_cq.cq_number | ((u32)nesdev->ceq_index << 16));
+ u64temp = (u64)mgtvnic->mgt_cq.cq_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_CQ_WQE_PBL_LOW_IDX, u64temp);
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX] = 0;
+ u64temp = (unsigned long)&mgtvnic->mgt_cq;
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_LOW_IDX] = cpu_to_le32((u32)(u64temp >> 1));
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX] =
+ cpu_to_le32(((u32)((u64temp) >> 33)) & 0x7FFFFFFF);
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_DOORBELL_INDEX_HIGH_IDX] = 0;
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ /* Send CreateQP request to CQP */
+ mgt_context = (void *)(&mgtvnic->mgt_cq.cq_vbase[mgtvnic->mgt_cq.cq_size]);
+ mgt_context->context_words[NES_NIC_CTX_MISC_IDX] =
+ cpu_to_le32((u32)NES_MGT_CTX_SIZE |
+ ((u32)PCI_FUNC(nesdev->pcidev->devfn) << 12));
+ nes_debug(NES_DBG_INIT, "RX_WINDOW_BUFFER_PAGE_TABLE_SIZE = 0x%08X, RX_WINDOW_BUFFER_SIZE = 0x%08X\n",
+ nes_read_indexed(nesdev, NES_IDX_RX_WINDOW_BUFFER_PAGE_TABLE_SIZE),
+ nes_read_indexed(nesdev, NES_IDX_RX_WINDOW_BUFFER_SIZE));
+ if (nes_read_indexed(nesdev, NES_IDX_RX_WINDOW_BUFFER_SIZE) != 0)
+ mgt_context->context_words[NES_NIC_CTX_MISC_IDX] |= cpu_to_le32(NES_NIC_BACK_STORE);
+
+ u64temp = (u64)mgtvnic->mgt.rq_pbase;
+ mgt_context->context_words[NES_NIC_CTX_SQ_LOW_IDX] = cpu_to_le32((u32)u64temp);
+ mgt_context->context_words[NES_NIC_CTX_SQ_HIGH_IDX] = cpu_to_le32((u32)(u64temp >> 32));
+ u64temp = (u64)mgtvnic->mgt.rq_pbase;
+ mgt_context->context_words[NES_NIC_CTX_RQ_LOW_IDX] = cpu_to_le32((u32)u64temp);
+ mgt_context->context_words[NES_NIC_CTX_RQ_HIGH_IDX] = cpu_to_le32((u32)(u64temp >> 32));
+
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(NES_CQP_CREATE_QP |
+ NES_CQP_QP_TYPE_NIC);
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(mgtvnic->mgt.qp_id);
+ u64temp = (u64)mgtvnic->mgt_cq.cq_pbase +
+ (mgtvnic->mgt_cq.cq_size * sizeof(struct nes_hw_nic_cqe));
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, u64temp);
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+ nesdev->cqp.sq_head = cqp_head;
+
+ barrier();
+
+ /* Ring doorbell (2 WQEs) */
+ nes_write32(nesdev->regs + NES_WQE_ALLOC, 0x02800000 | nesdev->cqp.qp_id);
+
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ nes_debug(NES_DBG_INIT, "Waiting for create MGT QP%u to complete.\n",
+ mgtvnic->mgt.qp_id);
+
+ ret = wait_event_timeout(nesdev->cqp.waitq, (nesdev->cqp.sq_tail == cqp_head),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_INIT, "Create MGT QP%u completed, wait_event_timeout ret = %u.\n",
+ mgtvnic->mgt.qp_id, ret);
+ if (!ret) {
+ nes_debug(NES_DBG_INIT, "MGT QP%u create timeout expired\n", mgtvnic->mgt.qp_id);
+ if (i == 0) {
+ pci_free_consistent(nesdev->pcidev, nesvnic->mgt_mem_size, nesvnic->mgt_vbase,
+ nesvnic->mgt_pbase);
+ kfree(mgtvnic);
+ } else {
+ nes_destroy_mgt(nesvnic);
+ }
+ return -EIO;
+ }
+
+ /* Populate the RQ */
+ for (counter = 0; counter < (NES_MGT_WQ_COUNT - 1); counter++) {
+ skb = dev_alloc_skb(nesvnic->max_frame_size);
+ if (!skb) {
+ nes_debug(NES_DBG_INIT, "%s: out of memory for receive skb\n", netdev->name);
+ return -ENOMEM;
+ }
+
+ skb->dev = netdev;
+
+ pmem = pci_map_single(nesdev->pcidev, skb->data,
+ nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->busaddr = pmem;
+ cb->maplen = nesvnic->max_frame_size;
+
+ mgt_rqe = &mgtvnic->mgt.rq_vbase[counter];
+ mgt_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] = cpu_to_le32((u32)nesvnic->max_frame_size);
+ mgt_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_3_2_IDX] = 0;
+ mgt_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX] = cpu_to_le32((u32)pmem);
+ mgt_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX] = cpu_to_le32((u32)((u64)pmem >> 32));
+ mgtvnic->mgt.rx_skb[counter] = skb;
+ }
+
+ init_timer(&mgtvnic->rq_wqes_timer);
+ mgtvnic->rq_wqes_timer.function = nes_mgt_rq_wqes_timeout;
+ mgtvnic->rq_wqes_timer.data = (unsigned long)mgtvnic;
+
+ wqe_count = NES_MGT_WQ_COUNT - 1;
+ mgtvnic->mgt.rq_head = wqe_count;
+ barrier();
+ do {
+ counter = min(wqe_count, ((u32)255));
+ wqe_count -= counter;
+ nes_write32(nesdev->regs + NES_WQE_ALLOC, (counter << 24) | mgtvnic->mgt.qp_id);
+ } while (wqe_count);
+
+ nes_write32(nesdev->regs + NES_CQE_ALLOC, NES_CQE_ALLOC_NOTIFY_NEXT |
+ mgtvnic->mgt_cq.cq_number);
+ nes_read32(nesdev->regs + NES_CQE_ALLOC);
+
+ mgt_vbase += mgt_mem_size;
+ mgt_pbase += mgt_mem_size;
+ nesvnic->mgtvnic[i] = mgtvnic++;
+ }
+ return 0;
+}
+
+
+void nes_destroy_mgt(struct nes_vnic *nesvnic)
+{
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_vnic_mgt *mgtvnic;
+ struct nes_vnic_mgt *first_mgtvnic;
+ unsigned long flags;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ u32 cqp_head;
+ struct sk_buff *rx_skb;
+ int i;
+ int ret;
+
+ kthread_stop(nesvnic->mgt_thread);
+
+ /* Free remaining NIC receive buffers */
+ first_mgtvnic = nesvnic->mgtvnic[0];
+ for (i = 0; i < NES_MGT_QP_COUNT; i++) {
+ mgtvnic = nesvnic->mgtvnic[i];
+ if (mgtvnic == NULL)
+ continue;
+
+ while (mgtvnic->mgt.rq_head != mgtvnic->mgt.rq_tail) {
+ rx_skb = mgtvnic->mgt.rx_skb[mgtvnic->mgt.rq_tail];
+ nes_mgt_free_skb(nesdev, rx_skb, PCI_DMA_FROMDEVICE);
+ mgtvnic->mgt.rq_tail++;
+ mgtvnic->mgt.rq_tail &= (mgtvnic->mgt.rq_size - 1);
+ }
+
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+
+ /* Destroy NIC QP */
+ cqp_head = nesdev->cqp.sq_head;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ (NES_CQP_DESTROY_QP | NES_CQP_QP_TYPE_NIC));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
+ mgtvnic->mgt.qp_id);
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+
+ /* Destroy NIC CQ */
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ (NES_CQP_DESTROY_CQ | ((u32)mgtvnic->mgt_cq.cq_size << 16)));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
+ (mgtvnic->mgt_cq.cq_number | ((u32)nesdev->ceq_index << 16)));
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+
+ nesdev->cqp.sq_head = cqp_head;
+ barrier();
+
+ /* Ring doorbell (2 WQEs) */
+ nes_write32(nesdev->regs + NES_WQE_ALLOC, 0x02800000 | nesdev->cqp.qp_id);
+
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ nes_debug(NES_DBG_SHUTDOWN, "Waiting for CQP, cqp_head=%u, cqp.sq_head=%u,"
+ " cqp.sq_tail=%u, cqp.sq_size=%u\n",
+ cqp_head, nesdev->cqp.sq_head,
+ nesdev->cqp.sq_tail, nesdev->cqp.sq_size);
+
+ ret = wait_event_timeout(nesdev->cqp.waitq, (nesdev->cqp.sq_tail == cqp_head),
+ NES_EVENT_TIMEOUT);
+
+ nes_debug(NES_DBG_SHUTDOWN, "Destroy MGT QP returned, wait_event_timeout ret = %u, cqp_head=%u,"
+ " cqp.sq_head=%u, cqp.sq_tail=%u\n",
+ ret, cqp_head, nesdev->cqp.sq_head, nesdev->cqp.sq_tail);
+ if (!ret)
+ nes_debug(NES_DBG_SHUTDOWN, "MGT QP%u destroy timeout expired\n",
+ mgtvnic->mgt.qp_id);
+
+ nesvnic->mgtvnic[i] = NULL;
+ }
+
+ if (nesvnic->mgt_vbase) {
+ pci_free_consistent(nesdev->pcidev, nesvnic->mgt_mem_size, nesvnic->mgt_vbase,
+ nesvnic->mgt_pbase);
+ nesvnic->mgt_vbase = NULL;
+ nesvnic->mgt_pbase = 0;
+ }
+
+ kfree(first_mgtvnic);
+}
diff --git a/drivers/infiniband/hw/nes/nes_mgt.h b/drivers/infiniband/hw/nes/nes_mgt.h
new file mode 100644
index 000000000000..8c8af254555a
--- /dev/null
+++ b/drivers/infiniband/hw/nes/nes_mgt.h
@@ -0,0 +1,97 @@
+/*
+* Copyright (c) 2010 Intel-NE, Inc. All rights reserved.
+*
+* This software is available to you under a choice of one of two
+* licenses. You may choose to be licensed under the terms of the GNU
+* General Public License (GPL) Version 2, available from the file
+* COPYING in the main directory of this source tree, or the
+* OpenIB.org BSD license below:
+*
+* Redistribution and use in source and binary forms, with or
+* without modification, are permitted provided that the following
+* conditions are met:
+*
+* - Redistributions of source code must retain the above
+* copyright notice, this list of conditions and the following
+* disclaimer.
+*
+* - Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials
+* provided with the distribution.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+
+#ifndef __NES_MGT_H
+#define __NES_MGT_H
+
+#define MPA_FRAMING 6 /* length is 2 bytes, crc is 4 bytes */
+
+int nes_init_mgt_qp(struct nes_device *nesdev, struct net_device *netdev, struct nes_vnic *nesvnic);
+void nes_queue_mgt_skbs(struct sk_buff *skb, struct nes_vnic *nesvnic, struct nes_qp *nesqp);
+void nes_destroy_mgt(struct nes_vnic *nesvnic);
+void nes_destroy_pau_qp(struct nes_device *nesdev, struct nes_qp *nesqp);
+
+struct nes_hw_mgt {
+ struct nes_hw_nic_rq_wqe *rq_vbase; /* virtual address of rq */
+ dma_addr_t rq_pbase; /* PCI memory for host rings */
+ struct sk_buff *rx_skb[NES_NIC_WQ_SIZE];
+ u16 qp_id;
+ u16 sq_head;
+ u16 rq_head;
+ u16 rq_tail;
+ u16 rq_size;
+ u8 replenishing_rq;
+ u8 reserved;
+ spinlock_t rq_lock;
+};
+
+struct nes_vnic_mgt {
+ struct nes_vnic *nesvnic;
+ struct nes_hw_mgt mgt;
+ struct nes_hw_nic_cq mgt_cq;
+ atomic_t rx_skbs_needed;
+ struct timer_list rq_wqes_timer;
+ atomic_t rx_skb_timer_running;
+};
+
+#define MAX_FPDU_FRAGS 4
+struct pau_fpdu_frag {
+ struct sk_buff *skb;
+ u64 physaddr;
+ u32 frag_len;
+ bool cmplt;
+};
+
+struct pau_fpdu_info {
+ struct nes_qp *nesqp;
+ struct nes_cqp_request *cqp_request;
+ void *hdr_vbase;
+ dma_addr_t hdr_pbase;
+ int hdr_len;
+ u16 data_len;
+ u16 frag_cnt;
+ struct pau_fpdu_frag frags[MAX_FPDU_FRAGS];
+};
+
+enum pau_qh_state {
+ PAU_DEL_QH,
+ PAU_ADD_LB_QH,
+ PAU_READY
+};
+
+struct pau_qh_chg {
+ struct nes_device *nesdev;
+ struct nes_vnic *nesvnic;
+ struct nes_qp *nesqp;
+};
+
+#endif /* __NES_MGT_H */
diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c
index d3a1c41cfd27..c00d2f3f8966 100644
--- a/drivers/infiniband/hw/nes/nes_nic.c
+++ b/drivers/infiniband/hw/nes/nes_nic.c
@@ -441,13 +441,13 @@ static int nes_nic_send(struct sk_buff *skb, struct net_device *netdev)
nesnic->tx_skb[nesnic->sq_head] = skb;
for (skb_fragment_index = 0; skb_fragment_index < skb_shinfo(skb)->nr_frags;
skb_fragment_index++) {
- bus_address = pci_map_page( nesdev->pcidev,
- skb_shinfo(skb)->frags[skb_fragment_index].page,
- skb_shinfo(skb)->frags[skb_fragment_index].page_offset,
- skb_shinfo(skb)->frags[skb_fragment_index].size,
- PCI_DMA_TODEVICE);
+ skb_frag_t *frag =
+ &skb_shinfo(skb)->frags[skb_fragment_index];
+ bus_address = skb_frag_dma_map(&nesdev->pcidev->dev,
+ frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
wqe_fragment_length[wqe_fragment_index] =
- cpu_to_le16(skb_shinfo(skb)->frags[skb_fragment_index].size);
+ cpu_to_le16(skb_frag_size(&skb_shinfo(skb)->frags[skb_fragment_index]));
set_wqe_64bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_FRAG0_LOW_IDX+(2*wqe_fragment_index),
bus_address);
wqe_fragment_index++;
@@ -561,11 +561,12 @@ tso_sq_no_longer_full:
/* Map all the buffers */
for (tso_frag_count=0; tso_frag_count < skb_shinfo(skb)->nr_frags;
tso_frag_count++) {
- tso_bus_address[tso_frag_count] = pci_map_page( nesdev->pcidev,
- skb_shinfo(skb)->frags[tso_frag_count].page,
- skb_shinfo(skb)->frags[tso_frag_count].page_offset,
- skb_shinfo(skb)->frags[tso_frag_count].size,
- PCI_DMA_TODEVICE);
+ skb_frag_t *frag =
+ &skb_shinfo(skb)->frags[tso_frag_count];
+ tso_bus_address[tso_frag_count] =
+ skb_frag_dma_map(&nesdev->pcidev->dev,
+ frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
}
tso_frag_index = 0;
@@ -636,11 +637,11 @@ tso_sq_no_longer_full:
}
while (wqe_fragment_index < 5) {
wqe_fragment_length[wqe_fragment_index] =
- cpu_to_le16(skb_shinfo(skb)->frags[tso_frag_index].size);
+ cpu_to_le16(skb_frag_size(&skb_shinfo(skb)->frags[tso_frag_index]));
set_wqe_64bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_FRAG0_LOW_IDX+(2*wqe_fragment_index),
(u64)tso_bus_address[tso_frag_index]);
wqe_fragment_index++;
- tso_wqe_length += skb_shinfo(skb)->frags[tso_frag_index++].size;
+ tso_wqe_length += skb_frag_size(&skb_shinfo(skb)->frags[tso_frag_index++]);
if (wqe_fragment_index < 5)
wqe_fragment_length[wqe_fragment_index] = 0;
if (tso_frag_index == tso_frag_count)
@@ -1090,6 +1091,8 @@ static const char nes_ethtool_stringset[][ETH_GSTRING_LEN] = {
"LRO aggregated",
"LRO flushed",
"LRO no_desc",
+ "PAU CreateQPs",
+ "PAU DestroyQPs",
};
#define NES_ETHTOOL_STAT_COUNT ARRAY_SIZE(nes_ethtool_stringset)
@@ -1305,6 +1308,8 @@ static void nes_netdev_get_ethtool_stats(struct net_device *netdev,
target_stat_values[++index] = nesvnic->lro_mgr.stats.aggregated;
target_stat_values[++index] = nesvnic->lro_mgr.stats.flushed;
target_stat_values[++index] = nesvnic->lro_mgr.stats.no_desc;
+ target_stat_values[++index] = atomic_read(&pau_qps_created);
+ target_stat_values[++index] = atomic_read(&pau_qps_destroyed);
}
/**
@@ -1584,23 +1589,19 @@ static const struct ethtool_ops nes_ethtool_ops = {
.set_pauseparam = nes_netdev_set_pauseparam,
};
-
-static void nes_netdev_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
+static void nes_vlan_mode(struct net_device *netdev, struct nes_device *nesdev, u32 features)
{
- struct nes_vnic *nesvnic = netdev_priv(netdev);
- struct nes_device *nesdev = nesvnic->nesdev;
struct nes_adapter *nesadapter = nesdev->nesadapter;
u32 u32temp;
unsigned long flags;
spin_lock_irqsave(&nesadapter->phy_lock, flags);
- nesvnic->vlan_grp = grp;
nes_debug(NES_DBG_NETDEV, "%s: %s\n", __func__, netdev->name);
/* Enable/Disable VLAN Stripping */
u32temp = nes_read_indexed(nesdev, NES_IDX_PCIX_DIAG);
- if (grp)
+ if (features & NETIF_F_HW_VLAN_RX)
u32temp &= 0xfdffffff;
else
u32temp |= 0x02000000;
@@ -1609,17 +1610,44 @@ static void nes_netdev_vlan_rx_register(struct net_device *netdev, struct vlan_g
spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
}
+static u32 nes_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int nes_set_features(struct net_device *netdev, u32 features)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ u32 changed = netdev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ nes_vlan_mode(netdev, nesdev, features);
+
+ return 0;
+}
+
static const struct net_device_ops nes_netdev_ops = {
- .ndo_open = nes_netdev_open,
+ .ndo_open = nes_netdev_open,
.ndo_stop = nes_netdev_stop,
- .ndo_start_xmit = nes_netdev_start_xmit,
+ .ndo_start_xmit = nes_netdev_start_xmit,
.ndo_get_stats = nes_netdev_get_stats,
- .ndo_tx_timeout = nes_netdev_tx_timeout,
+ .ndo_tx_timeout = nes_netdev_tx_timeout,
.ndo_set_mac_address = nes_netdev_set_mac_address,
- .ndo_set_multicast_list = nes_netdev_set_multicast_list,
+ .ndo_set_rx_mode = nes_netdev_set_multicast_list,
.ndo_change_mtu = nes_netdev_change_mtu,
.ndo_validate_addr = eth_validate_addr,
- .ndo_vlan_rx_register = nes_netdev_vlan_rx_register,
+ .ndo_fix_features = nes_fix_features,
+ .ndo_set_features = nes_set_features,
};
/**
@@ -1656,7 +1684,7 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
netdev->ethtool_ops = &nes_ethtool_ops;
netif_napi_add(netdev, &nesvnic->napi, nes_netdev_poll, 128);
nes_debug(NES_DBG_INIT, "Enabling VLAN Insert/Delete.\n");
- netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+ netdev->features |= NETIF_F_HW_VLAN_TX;
/* Fill in the port structure */
nesvnic->netdev = netdev;
@@ -1683,7 +1711,8 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
netdev->dev_addr[5] = (u8)u64temp;
memcpy(netdev->perm_addr, netdev->dev_addr, 6);
- netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM;
+ netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_HW_VLAN_RX;
if ((nesvnic->logical_port < 2) || (nesdev->nesadapter->hw_rev != NE020_REV))
netdev->hw_features |= NETIF_F_TSO;
netdev->features |= netdev->hw_features;
@@ -1815,6 +1844,8 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
nes_init_phy(nesdev);
}
+ nes_vlan_mode(netdev, nesdev, netdev->features);
+
return netdev;
}
diff --git a/drivers/infiniband/hw/nes/nes_utils.c b/drivers/infiniband/hw/nes/nes_utils.c
index f9c417c6b3b3..cd10968bfa22 100644
--- a/drivers/infiniband/hw/nes/nes_utils.c
+++ b/drivers/infiniband/hw/nes/nes_utils.c
@@ -51,13 +51,34 @@
#include "nes.h"
-
-
static u16 nes_read16_eeprom(void __iomem *addr, u16 offset);
u32 mh_detected;
u32 mh_pauses_sent;
+u32 nes_set_pau(struct nes_device *nesdev)
+{
+ u32 ret = 0;
+ u32 counter;
+
+ nes_write_indexed(nesdev, NES_IDX_GPR2, NES_ENABLE_PAU);
+ nes_write_indexed(nesdev, NES_IDX_GPR_TRIGGER, 1);
+
+ for (counter = 0; counter < NES_PAU_COUNTER; counter++) {
+ udelay(30);
+ if (!nes_read_indexed(nesdev, NES_IDX_GPR2)) {
+ printk(KERN_INFO PFX "PAU is supported.\n");
+ break;
+ }
+ nes_write_indexed(nesdev, NES_IDX_GPR_TRIGGER, 1);
+ }
+ if (counter == NES_PAU_COUNTER) {
+ printk(KERN_INFO PFX "PAU is not supported.\n");
+ return -EPERM;
+ }
+ return ret;
+}
+
/**
* nes_read_eeprom_values -
*/
@@ -187,6 +208,11 @@ int nes_read_eeprom_values(struct nes_device *nesdev, struct nes_adapter *nesada
if (((major_ver == 3) && (minor_ver >= 16)) || (major_ver > 3))
nesadapter->send_term_ok = 1;
+ if (nes_drv_opt & NES_DRV_OPT_ENABLE_PAU) {
+ if (!nes_set_pau(nesdev))
+ nesadapter->allow_unaligned_fpdus = 1;
+ }
+
nesadapter->firmware_version = (((u32)(u8)(eeprom_data>>8)) << 16) +
(u32)((u8)eeprom_data);
@@ -594,6 +620,7 @@ void nes_put_cqp_request(struct nes_device *nesdev,
nes_free_cqp_request(nesdev, cqp_request);
}
+
/**
* nes_post_cqp_request
*/
@@ -604,6 +631,8 @@ void nes_post_cqp_request(struct nes_device *nesdev,
unsigned long flags;
u32 cqp_head;
u64 u64temp;
+ u32 opcode;
+ int ctx_index = NES_CQP_WQE_COMP_CTX_LOW_IDX;
spin_lock_irqsave(&nesdev->cqp.lock, flags);
@@ -614,17 +643,20 @@ void nes_post_cqp_request(struct nes_device *nesdev,
nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
memcpy(cqp_wqe, &cqp_request->cqp_wqe, sizeof(*cqp_wqe));
+ opcode = le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX]);
+ if ((opcode & NES_CQP_OPCODE_MASK) == NES_CQP_DOWNLOAD_SEGMENT)
+ ctx_index = NES_CQP_WQE_DL_COMP_CTX_LOW_IDX;
barrier();
u64temp = (unsigned long)cqp_request;
- set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_COMP_SCRATCH_LOW_IDX,
- u64temp);
+ set_wqe_64bit_value(cqp_wqe->wqe_words, ctx_index, u64temp);
nes_debug(NES_DBG_CQP, "CQP request (opcode 0x%02X), line 1 = 0x%08X put on CQPs SQ,"
- " request = %p, cqp_head = %u, cqp_tail = %u, cqp_size = %u,"
- " waiting = %d, refcount = %d.\n",
- le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX])&0x3f,
- le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX]), cqp_request,
- nesdev->cqp.sq_head, nesdev->cqp.sq_tail, nesdev->cqp.sq_size,
- cqp_request->waiting, atomic_read(&cqp_request->refcount));
+ " request = %p, cqp_head = %u, cqp_tail = %u, cqp_size = %u,"
+ " waiting = %d, refcount = %d.\n",
+ opcode & NES_CQP_OPCODE_MASK,
+ le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX]), cqp_request,
+ nesdev->cqp.sq_head, nesdev->cqp.sq_tail, nesdev->cqp.sq_size,
+ cqp_request->waiting, atomic_read(&cqp_request->refcount));
+
barrier();
/* Ring doorbell (1 WQEs) */
@@ -645,7 +677,6 @@ void nes_post_cqp_request(struct nes_device *nesdev,
return;
}
-
/**
* nes_arp_table
*/
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index 95ca93ceedac..5095bc41c6cc 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -605,16 +605,6 @@ static int nes_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr
/**
- * nes_modify_port
- */
-static int nes_modify_port(struct ib_device *ibdev, u8 port,
- int port_modify_mask, struct ib_port_modify *props)
-{
- return 0;
-}
-
-
-/**
* nes_query_pkey
*/
static int nes_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey)
@@ -1468,7 +1458,7 @@ static int nes_destroy_qp(struct ib_qp *ibqp)
struct ib_qp_attr attr;
struct iw_cm_id *cm_id;
struct iw_cm_event cm_event;
- int ret;
+ int ret = 0;
atomic_inc(&sw_qps_destroyed);
nesqp->destroyed = 1;
@@ -1521,7 +1511,6 @@ static int nes_destroy_qp(struct ib_qp *ibqp)
if ((nesqp->nesrcq) && (nesqp->nesrcq != nesqp->nesscq))
nes_clean_cq(nesqp, nesqp->nesrcq);
}
-
nes_rem_ref(&nesqp->ibqp);
return 0;
}
@@ -2348,8 +2337,10 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
skip_pages = ((u32)region->offset) >> 12;
- if (ib_copy_from_udata(&req, udata, sizeof(req)))
+ if (ib_copy_from_udata(&req, udata, sizeof(req))) {
+ ib_umem_release(region);
return ERR_PTR(-EFAULT);
+ }
nes_debug(NES_DBG_MR, "Memory Registration type = %08X.\n", req.reg_type);
switch (req.reg_type) {
@@ -2641,6 +2632,7 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
return &nesmr->ibmr;
}
+ ib_umem_release(region);
return ERR_PTR(-ENOSYS);
}
@@ -3882,7 +3874,6 @@ struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev)
nesibdev->ibdev.dev.parent = &nesdev->pcidev->dev;
nesibdev->ibdev.query_device = nes_query_device;
nesibdev->ibdev.query_port = nes_query_port;
- nesibdev->ibdev.modify_port = nes_modify_port;
nesibdev->ibdev.query_pkey = nes_query_pkey;
nesibdev->ibdev.query_gid = nes_query_gid;
nesibdev->ibdev.alloc_ucontext = nes_alloc_ucontext;
diff --git a/drivers/infiniband/hw/nes/nes_verbs.h b/drivers/infiniband/hw/nes/nes_verbs.h
index 2df9993e0cac..fe6b6e92fa90 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.h
+++ b/drivers/infiniband/hw/nes/nes_verbs.h
@@ -139,7 +139,8 @@ struct nes_qp {
struct nes_cq *nesrcq;
struct nes_pd *nespd;
void *cm_node; /* handle of the node this QP is associated with */
- struct ietf_mpa_frame *ietf_frame;
+ void *ietf_frame;
+ u8 ietf_frame_size;
dma_addr_t ietf_frame_pbase;
struct ib_mr *lsmm_mr;
struct nes_hw_qp hwqp;
@@ -154,6 +155,7 @@ struct nes_qp {
u32 mmap_sq_db_index;
u32 mmap_rq_db_index;
spinlock_t lock;
+ spinlock_t pau_lock;
struct nes_qp_context *nesqp_context;
dma_addr_t nesqp_context_pbase;
void *pbl_vbase;
@@ -161,6 +163,8 @@ struct nes_qp {
struct page *page;
struct timer_list terminate_timer;
enum ib_event_type terminate_eventtype;
+ struct sk_buff_head pau_list;
+ u32 pau_rcv_nxt;
u16 active_conn:1;
u16 skip_lsmm:1;
u16 user_mode:1;
@@ -168,7 +172,8 @@ struct nes_qp {
u16 flush_issued:1;
u16 destroyed:1;
u16 sig_all:1;
- u16 rsvd:9;
+ u16 pau_mode:1;
+ u16 rsvd:8;
u16 private_data_len;
u16 term_sq_flush_code;
u16 term_rq_flush_code;
@@ -176,5 +181,8 @@ struct nes_qp {
u8 hw_tcp_state;
u8 term_flags;
u8 sq_kmapped;
+ u8 pau_busy;
+ u8 pau_pending;
+ u8 pau_state;
};
#endif /* NES_VERBS_H */
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index 769a1d9da4b7..b881bdc401f5 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -171,7 +171,9 @@ struct qib_ctxtdata {
/* how many alloc_pages() chunks in rcvegrbuf_pages */
u32 rcvegrbuf_chunks;
/* how many egrbufs per chunk */
- u32 rcvegrbufs_perchunk;
+ u16 rcvegrbufs_perchunk;
+ /* ilog2 of above */
+ u16 rcvegrbufs_perchunk_shift;
/* order for rcvegrbuf_pages */
size_t rcvegrbuf_size;
/* rcvhdrq size (for freeing) */
@@ -221,6 +223,9 @@ struct qib_ctxtdata {
/* ctxt rcvhdrq head offset */
u32 head;
u32 pkt_count;
+ /* lookaside fields */
+ struct qib_qp *lookaside_qp;
+ u32 lookaside_qpn;
/* QPs waiting for context processing */
struct list_head qp_wait_list;
};
@@ -807,6 +812,10 @@ struct qib_devdata {
* supports, less gives more pio bufs/ctxt, etc.
*/
u32 cfgctxts;
+ /*
+ * number of ctxts available for PSM open
+ */
+ u32 freectxts;
/*
* hint that we should update pioavailshadow before
@@ -936,7 +945,9 @@ struct qib_devdata {
/* chip address space used by 4k pio buffers */
u32 align4k;
/* size of each rcvegrbuffer */
- u32 rcvegrbufsize;
+ u16 rcvegrbufsize;
+ /* log2 of above */
+ u16 rcvegrbufsize_shift;
/* localbus width (1, 2,4,8,16,32) from config space */
u32 lbus_width;
/* localbus speed in MHz */
@@ -1012,6 +1023,8 @@ struct qib_devdata {
u8 psxmitwait_supported;
/* cycle length of PS* counters in HW (in picoseconds) */
u16 psxmitwait_check_rate;
+ /* high volume overflow errors defered to tasklet */
+ struct tasklet_struct error_tasklet;
};
/* hol_state values */
@@ -1433,6 +1446,7 @@ extern struct mutex qib_mutex;
struct qib_hwerror_msgs {
u64 mask;
const char *msg;
+ size_t sz;
};
#define QLOGIC_IB_HWE_MSG(a, b) { .mask = a, .msg = b }
diff --git a/drivers/infiniband/hw/qib/qib_diag.c b/drivers/infiniband/hw/qib/qib_diag.c
index 204c4dd9dce0..9892456a4348 100644
--- a/drivers/infiniband/hw/qib/qib_diag.c
+++ b/drivers/infiniband/hw/qib/qib_diag.c
@@ -46,6 +46,7 @@
#include <linux/pci.h>
#include <linux/poll.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
diff --git a/drivers/infiniband/hw/qib/qib_driver.c b/drivers/infiniband/hw/qib/qib_driver.c
index 23e584f4c36c..c90a55f4120f 100644
--- a/drivers/infiniband/hw/qib/qib_driver.c
+++ b/drivers/infiniband/hw/qib/qib_driver.c
@@ -37,6 +37,7 @@
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
+#include <linux/module.h>
#include "qib.h"
@@ -279,10 +280,10 @@ bail:
*/
static inline void *qib_get_egrbuf(const struct qib_ctxtdata *rcd, u32 etail)
{
- const u32 chunk = etail / rcd->rcvegrbufs_perchunk;
- const u32 idx = etail % rcd->rcvegrbufs_perchunk;
+ const u32 chunk = etail >> rcd->rcvegrbufs_perchunk_shift;
+ const u32 idx = etail & ((u32)rcd->rcvegrbufs_perchunk - 1);
- return rcd->rcvegrbuf[chunk] + idx * rcd->dd->rcvegrbufsize;
+ return rcd->rcvegrbuf[chunk] + (idx << rcd->dd->rcvegrbufsize_shift);
}
/*
@@ -310,7 +311,6 @@ static u32 qib_rcv_hdrerr(struct qib_ctxtdata *rcd, struct qib_pportdata *ppd,
u32 opcode;
u32 psn;
int diff;
- unsigned long flags;
/* Sanity check packet */
if (tlen < 24)
@@ -365,7 +365,6 @@ static u32 qib_rcv_hdrerr(struct qib_ctxtdata *rcd, struct qib_pportdata *ppd,
switch (qp->ibqp.qp_type) {
case IB_QPT_RC:
- spin_lock_irqsave(&qp->s_lock, flags);
ruc_res =
qib_ruc_check_hdr(
ibp, hdr,
@@ -373,11 +372,8 @@ static u32 qib_rcv_hdrerr(struct qib_ctxtdata *rcd, struct qib_pportdata *ppd,
qp,
be32_to_cpu(ohdr->bth[0]));
if (ruc_res) {
- spin_unlock_irqrestore(&qp->s_lock,
- flags);
goto unlock;
}
- spin_unlock_irqrestore(&qp->s_lock, flags);
/* Only deal with RDMA Writes for now */
if (opcode <
@@ -547,6 +543,15 @@ move_along:
updegr = 0;
}
}
+ /*
+ * Notify qib_destroy_qp() if it is waiting
+ * for lookaside_qp to finish.
+ */
+ if (rcd->lookaside_qp) {
+ if (atomic_dec_and_test(&rcd->lookaside_qp->refcount))
+ wake_up(&rcd->lookaside_qp->wait);
+ rcd->lookaside_qp = NULL;
+ }
rcd->head = l;
rcd->pkt_count += i;
diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c
index 406fca50d036..574600ef5b42 100644
--- a/drivers/infiniband/hw/qib/qib_file_ops.c
+++ b/drivers/infiniband/hw/qib/qib_file_ops.c
@@ -43,6 +43,7 @@
#include <linux/jiffies.h>
#include <asm/pgtable.h>
#include <linux/delay.h>
+#include <linux/export.h>
#include "qib.h"
#include "qib_common.h"
@@ -1284,6 +1285,7 @@ static int setup_ctxt(struct qib_pportdata *ppd, int ctxt,
strlcpy(rcd->comm, current->comm, sizeof(rcd->comm));
ctxt_fp(fp) = rcd;
qib_stats.sps_ctxts++;
+ dd->freectxts++;
ret = 0;
goto bail;
@@ -1527,6 +1529,7 @@ done_chk_sdma:
struct qib_filedata *fd = fp->private_data;
const struct qib_ctxtdata *rcd = fd->rcd;
const struct qib_devdata *dd = rcd->dd;
+ unsigned int weight;
if (dd->flags & QIB_HAS_SEND_DMA) {
fd->pq = qib_user_sdma_queue_create(&dd->pcidev->dev,
@@ -1545,8 +1548,8 @@ done_chk_sdma:
* it just means that sooner or later we don't recommend
* a cpu, and let the scheduler do it's best.
*/
- if (!ret && cpus_weight(current->cpus_allowed) >=
- qib_cpulist_count) {
+ weight = cpumask_weight(tsk_cpus_allowed(current));
+ if (!ret && weight >= qib_cpulist_count) {
int cpu;
cpu = find_first_zero_bit(qib_cpulist,
qib_cpulist_count);
@@ -1554,13 +1557,13 @@ done_chk_sdma:
__set_bit(cpu, qib_cpulist);
fd->rec_cpu_num = cpu;
}
- } else if (cpus_weight(current->cpus_allowed) == 1 &&
- test_bit(first_cpu(current->cpus_allowed),
+ } else if (weight == 1 &&
+ test_bit(cpumask_first(tsk_cpus_allowed(current)),
qib_cpulist))
qib_devinfo(dd->pcidev, "%s PID %u affinity "
"set to cpu %d; already allocated\n",
current->comm, current->pid,
- first_cpu(current->cpus_allowed));
+ cpumask_first(tsk_cpus_allowed(current)));
}
mutex_unlock(&qib_mutex);
@@ -1791,6 +1794,7 @@ static int qib_close(struct inode *in, struct file *fp)
if (dd->pageshadow)
unlock_expected_tids(rcd);
qib_stats.sps_ctxts--;
+ dd->freectxts--;
}
mutex_unlock(&qib_mutex);
@@ -1904,8 +1908,9 @@ int qib_set_uevent_bits(struct qib_pportdata *ppd, const int evtbit)
struct qib_ctxtdata *rcd;
unsigned ctxt;
int ret = 0;
+ unsigned long flags;
- spin_lock(&ppd->dd->uctxt_lock);
+ spin_lock_irqsave(&ppd->dd->uctxt_lock, flags);
for (ctxt = ppd->dd->first_user_ctxt; ctxt < ppd->dd->cfgctxts;
ctxt++) {
rcd = ppd->dd->rcd[ctxt];
@@ -1924,7 +1929,7 @@ int qib_set_uevent_bits(struct qib_pportdata *ppd, const int evtbit)
ret = 1;
break;
}
- spin_unlock(&ppd->dd->uctxt_lock);
+ spin_unlock_irqrestore(&ppd->dd->uctxt_lock, flags);
return ret;
}
diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c
index d8ca0a0b970d..781a802a321f 100644
--- a/drivers/infiniband/hw/qib/qib_iba6120.c
+++ b/drivers/infiniband/hw/qib/qib_iba6120.c
@@ -3273,6 +3273,8 @@ static int init_6120_variables(struct qib_devdata *dd)
/* we always allocate at least 2048 bytes for eager buffers */
ret = ib_mtu_enum_to_int(qib_ibmtu);
dd->rcvegrbufsize = ret != -1 ? max(ret, 2048) : QIB_DEFAULT_MTU;
+ BUG_ON(!is_power_of_2(dd->rcvegrbufsize));
+ dd->rcvegrbufsize_shift = ilog2(dd->rcvegrbufsize);
qib_6120_tidtemplate(dd);
diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c
index c765a2eb04cf..439d3c503cd5 100644
--- a/drivers/infiniband/hw/qib/qib_iba7220.c
+++ b/drivers/infiniband/hw/qib/qib_iba7220.c
@@ -39,6 +39,7 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/io.h>
#include <rdma/ib_verbs.h>
@@ -2434,6 +2435,7 @@ static int qib_7220_set_ib_cfg(struct qib_pportdata *ppd, int which, u32 val)
int lsb, ret = 0, setforce = 0;
u16 lcmd, licmd;
unsigned long flags;
+ u32 tmp = 0;
switch (which) {
case QIB_IB_CFG_LIDLMC:
@@ -2467,9 +2469,6 @@ static int qib_7220_set_ib_cfg(struct qib_pportdata *ppd, int which, u32 val)
maskr = IBA7220_IBC_WIDTH_MASK;
lsb = IBA7220_IBC_WIDTH_SHIFT;
setforce = 1;
- spin_lock_irqsave(&ppd->lflags_lock, flags);
- ppd->lflags |= QIBL_IB_FORCE_NOTIFY;
- spin_unlock_irqrestore(&ppd->lflags_lock, flags);
break;
case QIB_IB_CFG_SPD_ENB: /* set allowed Link speeds */
@@ -2643,6 +2642,28 @@ static int qib_7220_set_ib_cfg(struct qib_pportdata *ppd, int which, u32 val)
goto bail;
}
qib_set_ib_7220_lstate(ppd, lcmd, licmd);
+
+ maskr = IBA7220_IBC_WIDTH_MASK;
+ lsb = IBA7220_IBC_WIDTH_SHIFT;
+ tmp = (ppd->cpspec->ibcddrctrl >> lsb) & maskr;
+ /* If the width active on the chip does not match the
+ * width in the shadow register, write the new active
+ * width to the chip.
+ * We don't have to worry about speed as the speed is taken
+ * care of by set_7220_ibspeed_fast called by ib_updown.
+ */
+ if (ppd->link_width_enabled-1 != tmp) {
+ ppd->cpspec->ibcddrctrl &= ~(maskr << lsb);
+ ppd->cpspec->ibcddrctrl |=
+ (((u64)(ppd->link_width_enabled-1) & maskr) <<
+ lsb);
+ qib_write_kreg(dd, kr_ibcddrctrl,
+ ppd->cpspec->ibcddrctrl);
+ qib_write_kreg(dd, kr_scratch, 0);
+ spin_lock_irqsave(&ppd->lflags_lock, flags);
+ ppd->lflags |= QIBL_IB_FORCE_NOTIFY;
+ spin_unlock_irqrestore(&ppd->lflags_lock, flags);
+ }
goto bail;
case QIB_IB_CFG_HRTBT: /* set Heartbeat off/enable/auto */
@@ -4065,6 +4086,8 @@ static int qib_init_7220_variables(struct qib_devdata *dd)
/* we always allocate at least 2048 bytes for eager buffers */
ret = ib_mtu_enum_to_int(qib_ibmtu);
dd->rcvegrbufsize = ret != -1 ? max(ret, 2048) : QIB_DEFAULT_MTU;
+ BUG_ON(!is_power_of_2(dd->rcvegrbufsize));
+ dd->rcvegrbufsize_shift = ilog2(dd->rcvegrbufsize);
qib_7220_tidtemplate(dd);
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index 8ec5237031a0..5bd2162b95dc 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -40,6 +40,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/jiffies.h>
+#include <linux/module.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_smi.h>
@@ -114,6 +115,10 @@ static ushort qib_singleport;
module_param_named(singleport, qib_singleport, ushort, S_IRUGO);
MODULE_PARM_DESC(singleport, "Use only IB port 1; more per-port buffer space");
+static ushort qib_krcvq01_no_msi;
+module_param_named(krcvq01_no_msi, qib_krcvq01_no_msi, ushort, S_IRUGO);
+MODULE_PARM_DESC(krcvq01_no_msi, "No MSI for kctx < 2");
+
/*
* Receive header queue sizes
*/
@@ -397,7 +402,6 @@ MODULE_PARM_DESC(txselect, \
#define crp_txdroppedpkt CREG_IDX(TxDroppedPktCnt)
#define crp_txhdrerr CREG_IDX(TxHeadersErrCnt)
#define crp_txlenerr CREG_IDX(TxLenErrCnt)
-#define crp_txlenerr CREG_IDX(TxLenErrCnt)
#define crp_txminmaxlenerr CREG_IDX(TxMaxMinLenErrCnt)
#define crp_txsdmadesc CREG_IDX(TxSDmaDescCnt)
#define crp_txunderrun CREG_IDX(TxUnderrunCnt)
@@ -1107,9 +1111,9 @@ static inline u32 read_7322_creg32_port(const struct qib_pportdata *ppd,
#define AUTONEG_TRIES 3 /* sequential retries to negotiate DDR */
#define HWE_AUTO(fldname) { .mask = SYM_MASK(HwErrMask, fldname##Mask), \
- .msg = #fldname }
+ .msg = #fldname , .sz = sizeof(#fldname) }
#define HWE_AUTO_P(fldname, port) { .mask = SYM_MASK(HwErrMask, \
- fldname##Mask##_##port), .msg = #fldname }
+ fldname##Mask##_##port), .msg = #fldname , .sz = sizeof(#fldname) }
static const struct qib_hwerror_msgs qib_7322_hwerror_msgs[] = {
HWE_AUTO_P(IBSerdesPClkNotDetect, 1),
HWE_AUTO_P(IBSerdesPClkNotDetect, 0),
@@ -1127,14 +1131,16 @@ static const struct qib_hwerror_msgs qib_7322_hwerror_msgs[] = {
HWE_AUTO_P(IBCBusFromSPCParityErr, 0),
HWE_AUTO(statusValidNoEop),
HWE_AUTO(LATriggered),
- { .mask = 0 }
+ { .mask = 0, .sz = 0 }
};
#define E_AUTO(fldname) { .mask = SYM_MASK(ErrMask, fldname##Mask), \
- .msg = #fldname }
+ .msg = #fldname, .sz = sizeof(#fldname) }
#define E_P_AUTO(fldname) { .mask = SYM_MASK(ErrMask_0, fldname##Mask), \
- .msg = #fldname }
+ .msg = #fldname, .sz = sizeof(#fldname) }
static const struct qib_hwerror_msgs qib_7322error_msgs[] = {
+ E_AUTO(RcvEgrFullErr),
+ E_AUTO(RcvHdrFullErr),
E_AUTO(ResetNegated),
E_AUTO(HardwareErr),
E_AUTO(InvalidAddrErr),
@@ -1147,9 +1153,7 @@ static const struct qib_hwerror_msgs qib_7322error_msgs[] = {
E_AUTO(SendSpecialTriggerErr),
E_AUTO(SDmaWrongPortErr),
E_AUTO(SDmaBufMaskDuplicateErr),
- E_AUTO(RcvHdrFullErr),
- E_AUTO(RcvEgrFullErr),
- { .mask = 0 }
+ { .mask = 0, .sz = 0 }
};
static const struct qib_hwerror_msgs qib_7322p_error_msgs[] = {
@@ -1159,7 +1163,8 @@ static const struct qib_hwerror_msgs qib_7322p_error_msgs[] = {
/*
* SDmaHaltErr is not really an error, make it clearer;
*/
- {.mask = SYM_MASK(ErrMask_0, SDmaHaltErrMask), .msg = "SDmaHalted"},
+ {.mask = SYM_MASK(ErrMask_0, SDmaHaltErrMask), .msg = "SDmaHalted",
+ .sz = 11},
E_P_AUTO(SDmaDescAddrMisalignErr),
E_P_AUTO(SDmaUnexpDataErr),
E_P_AUTO(SDmaMissingDwErr),
@@ -1195,7 +1200,7 @@ static const struct qib_hwerror_msgs qib_7322p_error_msgs[] = {
E_P_AUTO(RcvICRCErr),
E_P_AUTO(RcvVCRCErr),
E_P_AUTO(RcvFormatErr),
- { .mask = 0 }
+ { .mask = 0, .sz = 0 }
};
/*
@@ -1203,17 +1208,17 @@ static const struct qib_hwerror_msgs qib_7322p_error_msgs[] = {
* context
*/
#define INTR_AUTO(fldname) { .mask = SYM_MASK(IntMask, fldname##Mask), \
- .msg = #fldname }
+ .msg = #fldname, .sz = sizeof(#fldname) }
/* Below generates "auto-message" for interrupts specific to a port */
#define INTR_AUTO_P(fldname) { .mask = MASK_ACROSS(\
SYM_LSB(IntMask, fldname##Mask##_0), \
SYM_LSB(IntMask, fldname##Mask##_1)), \
- .msg = #fldname "_P" }
+ .msg = #fldname "_P", .sz = sizeof(#fldname "_P") }
/* For some reason, the SerDesTrimDone bits are reversed */
#define INTR_AUTO_PI(fldname) { .mask = MASK_ACROSS(\
SYM_LSB(IntMask, fldname##Mask##_1), \
SYM_LSB(IntMask, fldname##Mask##_0)), \
- .msg = #fldname "_P" }
+ .msg = #fldname "_P", .sz = sizeof(#fldname "_P") }
/*
* Below generates "auto-message" for interrupts specific to a context,
* with ctxt-number appended
@@ -1221,7 +1226,7 @@ static const struct qib_hwerror_msgs qib_7322p_error_msgs[] = {
#define INTR_AUTO_C(fldname) { .mask = MASK_ACROSS(\
SYM_LSB(IntMask, fldname##0IntMask), \
SYM_LSB(IntMask, fldname##17IntMask)), \
- .msg = #fldname "_C"}
+ .msg = #fldname "_C", .sz = sizeof(#fldname "_C") }
static const struct qib_hwerror_msgs qib_7322_intr_msgs[] = {
INTR_AUTO_P(SDmaInt),
@@ -1235,11 +1240,12 @@ static const struct qib_hwerror_msgs qib_7322_intr_msgs[] = {
INTR_AUTO_P(SendDoneInt),
INTR_AUTO(SendBufAvailInt),
INTR_AUTO_C(RcvAvail),
- { .mask = 0 }
+ { .mask = 0, .sz = 0 }
};
#define TXSYMPTOM_AUTO_P(fldname) \
- { .mask = SYM_MASK(SendHdrErrSymptom_0, fldname), .msg = #fldname }
+ { .mask = SYM_MASK(SendHdrErrSymptom_0, fldname), \
+ .msg = #fldname, .sz = sizeof(#fldname) }
static const struct qib_hwerror_msgs hdrchk_msgs[] = {
TXSYMPTOM_AUTO_P(NonKeyPacket),
TXSYMPTOM_AUTO_P(GRHFail),
@@ -1248,7 +1254,7 @@ static const struct qib_hwerror_msgs hdrchk_msgs[] = {
TXSYMPTOM_AUTO_P(SLIDFail),
TXSYMPTOM_AUTO_P(RawIPV6),
TXSYMPTOM_AUTO_P(PacketTooSmall),
- { .mask = 0 }
+ { .mask = 0, .sz = 0 }
};
#define IBA7322_HDRHEAD_PKTINT_SHIFT 32 /* interrupt cnt in upper 32 bits */
@@ -1293,7 +1299,7 @@ static void err_decode(char *msg, size_t len, u64 errs,
u64 these, lmask;
int took, multi, n = 0;
- while (msp && msp->mask) {
+ while (errs && msp && msp->mask) {
multi = (msp->mask & (msp->mask - 1));
while (errs & msp->mask) {
these = (errs & msp->mask);
@@ -1304,9 +1310,14 @@ static void err_decode(char *msg, size_t len, u64 errs,
*msg++ = ',';
len--;
}
- took = scnprintf(msg, len, "%s", msp->msg);
+ BUG_ON(!msp->sz);
+ /* msp->sz counts the nul */
+ took = min_t(size_t, msp->sz - (size_t)1, len);
+ memcpy(msg, msp->msg, took);
len -= took;
msg += took;
+ if (len)
+ *msg = '\0';
}
errs &= ~lmask;
if (len && multi) {
@@ -1644,6 +1655,14 @@ done:
return;
}
+static void qib_error_tasklet(unsigned long data)
+{
+ struct qib_devdata *dd = (struct qib_devdata *)data;
+
+ handle_7322_errors(dd);
+ qib_write_kreg(dd, kr_errmask, dd->cspec->errormask);
+}
+
static void reenable_chase(unsigned long opaque)
{
struct qib_pportdata *ppd = (struct qib_pportdata *)opaque;
@@ -2292,12 +2311,15 @@ static int qib_7322_bringup_serdes(struct qib_pportdata *ppd)
val = ppd->cpspec->ibcctrl_a | (QLOGIC_IB_IBCC_LINKINITCMD_DISABLE <<
QLOGIC_IB_IBCC_LINKINITCMD_SHIFT);
+ ppd->cpspec->ibcctrl_a = val;
/*
* Reset the PCS interface to the serdes (and also ibc, which is still
* in reset from above). Writes new value of ibcctrl_a as last step.
*/
qib_7322_mini_pcs_reset(ppd);
qib_write_kreg(dd, kr_scratch, 0ULL);
+ /* clear the linkinit cmds */
+ ppd->cpspec->ibcctrl_a &= ~SYM_MASK(IBCCtrlA_0, LinkInitCmd);
if (!ppd->cpspec->ibcctrl_b) {
unsigned lse = ppd->link_speed_enabled;
@@ -2369,11 +2391,6 @@ static int qib_7322_bringup_serdes(struct qib_pportdata *ppd)
qib_write_kreg_port(ppd, krp_rcvctrl, ppd->p_rcvctrl);
spin_unlock_irqrestore(&dd->cspec->rcvmod_lock, flags);
- /* Hold the link state machine for mezz boards */
- if (IS_QMH(dd) || IS_QME(dd))
- qib_set_ib_7322_lstate(ppd, 0,
- QLOGIC_IB_IBCC_LINKINITCMD_DISABLE);
-
/* Also enable IBSTATUSCHG interrupt. */
val = qib_read_kreg_port(ppd, krp_errmask);
qib_write_kreg_port(ppd, krp_errmask,
@@ -2725,8 +2742,10 @@ static noinline void unlikely_7322_intr(struct qib_devdata *dd, u64 istat)
unknown_7322_ibits(dd, istat);
if (istat & QIB_I_GPIO)
unknown_7322_gpio_intr(dd);
- if (istat & QIB_I_C_ERROR)
- handle_7322_errors(dd);
+ if (istat & QIB_I_C_ERROR) {
+ qib_write_kreg(dd, kr_errmask, 0ULL);
+ tasklet_schedule(&dd->error_tasklet);
+ }
if (istat & INT_MASK_P(Err, 0) && dd->rcd[0])
handle_7322_p_errors(dd->rcd[0]->ppd);
if (istat & INT_MASK_P(Err, 1) && dd->rcd[1])
@@ -2833,9 +2852,8 @@ static irqreturn_t qib_7322intr(int irq, void *data)
for (i = 0; i < dd->first_user_ctxt; i++) {
if (ctxtrbits & rmask) {
ctxtrbits &= ~rmask;
- if (dd->rcd[i]) {
+ if (dd->rcd[i])
qib_kreceive(dd->rcd[i], NULL, &npkts);
- }
}
rmask <<= 1;
}
@@ -3125,6 +3143,8 @@ try_intx:
arg = dd->rcd[ctxt];
if (!arg)
continue;
+ if (qib_krcvq01_no_msi && ctxt < 2)
+ continue;
lsb = QIB_I_RCVAVAIL_LSB + ctxt;
handler = qib_7322pintr;
name = QIB_DRV_NAME " (kctx)";
@@ -3159,6 +3179,8 @@ try_intx:
for (i = 0; i < ARRAY_SIZE(redirect); i++)
qib_write_kreg(dd, kr_intredirect + i, redirect[i]);
dd->cspec->main_int_mask = mask;
+ tasklet_init(&dd->error_tasklet, qib_error_tasklet,
+ (unsigned long)dd);
bail:;
}
@@ -5206,6 +5228,8 @@ static int qib_7322_ib_updown(struct qib_pportdata *ppd, int ibup, u64 ibcs)
QIBL_IB_AUTONEG_INPROG)))
set_7322_ibspeed_fast(ppd, ppd->link_speed_enabled);
if (!(ppd->lflags & QIBL_IB_AUTONEG_INPROG)) {
+ struct qib_qsfp_data *qd =
+ &ppd->cpspec->qsfp_data;
/* unlock the Tx settings, speed may change */
qib_write_kreg_port(ppd, krp_tx_deemph_override,
SYM_MASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
@@ -5213,6 +5237,12 @@ static int qib_7322_ib_updown(struct qib_pportdata *ppd, int ibup, u64 ibcs)
qib_cancel_sends(ppd);
/* on link down, ensure sane pcs state */
qib_7322_mini_pcs_reset(ppd);
+ /* schedule the qsfp refresh which should turn the link
+ off */
+ if (ppd->dd->flags & QIB_HAS_QSFP) {
+ qd->t_insert = get_jiffies_64();
+ schedule_work(&qd->work);
+ }
spin_lock_irqsave(&ppd->sdma_lock, flags);
if (__qib_sdma_running(ppd))
__qib_sdma_process_event(ppd,
@@ -5563,43 +5593,79 @@ static void qsfp_7322_event(struct work_struct *work)
struct qib_qsfp_data *qd;
struct qib_pportdata *ppd;
u64 pwrup;
+ unsigned long flags;
int ret;
u32 le2;
qd = container_of(work, struct qib_qsfp_data, work);
ppd = qd->ppd;
- pwrup = qd->t_insert + msecs_to_jiffies(QSFP_PWR_LAG_MSEC);
+ pwrup = qd->t_insert +
+ msecs_to_jiffies(QSFP_PWR_LAG_MSEC - QSFP_MODPRS_LAG_MSEC);
- /*
- * Some QSFP's not only do not respond until the full power-up
- * time, but may behave badly if we try. So hold off responding
- * to insertion.
- */
- while (1) {
- u64 now = get_jiffies_64();
- if (time_after64(now, pwrup))
- break;
- msleep(20);
- }
- ret = qib_refresh_qsfp_cache(ppd, &qd->cache);
- /*
- * Need to change LE2 back to defaults if we couldn't
- * read the cable type (to handle cable swaps), so do this
- * even on failure to read cable information. We don't
- * get here for QME, so IS_QME check not needed here.
- */
- if (!ret && !ppd->dd->cspec->r1) {
- if (QSFP_IS_ACTIVE_FAR(qd->cache.tech))
- le2 = LE2_QME;
- else if (qd->cache.atten[1] >= qib_long_atten &&
- QSFP_IS_CU(qd->cache.tech))
- le2 = LE2_5m;
- else
+ /* Delay for 20 msecs to allow ModPrs resistor to setup */
+ mdelay(QSFP_MODPRS_LAG_MSEC);
+
+ if (!qib_qsfp_mod_present(ppd)) {
+ ppd->cpspec->qsfp_data.modpresent = 0;
+ /* Set the physical link to disabled */
+ qib_set_ib_7322_lstate(ppd, 0,
+ QLOGIC_IB_IBCC_LINKINITCMD_DISABLE);
+ spin_lock_irqsave(&ppd->lflags_lock, flags);
+ ppd->lflags &= ~QIBL_LINKV;
+ spin_unlock_irqrestore(&ppd->lflags_lock, flags);
+ } else {
+ /*
+ * Some QSFP's not only do not respond until the full power-up
+ * time, but may behave badly if we try. So hold off responding
+ * to insertion.
+ */
+ while (1) {
+ u64 now = get_jiffies_64();
+ if (time_after64(now, pwrup))
+ break;
+ msleep(20);
+ }
+
+ ret = qib_refresh_qsfp_cache(ppd, &qd->cache);
+
+ /*
+ * Need to change LE2 back to defaults if we couldn't
+ * read the cable type (to handle cable swaps), so do this
+ * even on failure to read cable information. We don't
+ * get here for QME, so IS_QME check not needed here.
+ */
+ if (!ret && !ppd->dd->cspec->r1) {
+ if (QSFP_IS_ACTIVE_FAR(qd->cache.tech))
+ le2 = LE2_QME;
+ else if (qd->cache.atten[1] >= qib_long_atten &&
+ QSFP_IS_CU(qd->cache.tech))
+ le2 = LE2_5m;
+ else
+ le2 = LE2_DEFAULT;
+ } else
le2 = LE2_DEFAULT;
- } else
- le2 = LE2_DEFAULT;
- ibsd_wr_allchans(ppd, 13, (le2 << 7), BMASK(9, 7));
- init_txdds_table(ppd, 0);
+ ibsd_wr_allchans(ppd, 13, (le2 << 7), BMASK(9, 7));
+ /*
+ * We always change parameteters, since we can choose
+ * values for cables without eeproms, and the cable may have
+ * changed from a cable with full or partial eeprom content
+ * to one with partial or no content.
+ */
+ init_txdds_table(ppd, 0);
+ /* The physical link is being re-enabled only when the
+ * previous state was DISABLED and the VALID bit is not
+ * set. This should only happen when the cable has been
+ * physically pulled. */
+ if (!ppd->cpspec->qsfp_data.modpresent &&
+ (ppd->lflags & (QIBL_LINKV | QIBL_IB_LINK_DISABLED))) {
+ ppd->cpspec->qsfp_data.modpresent = 1;
+ qib_set_ib_7322_lstate(ppd, 0,
+ QLOGIC_IB_IBCC_LINKINITCMD_SLEEP);
+ spin_lock_irqsave(&ppd->lflags_lock, flags);
+ ppd->lflags |= QIBL_LINKV;
+ spin_unlock_irqrestore(&ppd->lflags_lock, flags);
+ }
+ }
}
/*
@@ -5703,7 +5769,8 @@ static void set_no_qsfp_atten(struct qib_devdata *dd, int change)
/* now change the IBC and serdes, overriding generic */
init_txdds_table(ppd, 1);
/* Re-enable the physical state machine on mezz boards
- * now that the correct settings have been set. */
+ * now that the correct settings have been set.
+ * QSFP boards are handles by the QSFP event handler */
if (IS_QMH(dd) || IS_QME(dd))
qib_set_ib_7322_lstate(ppd, 0,
QLOGIC_IB_IBCC_LINKINITCMD_SLEEP);
@@ -6181,6 +6248,8 @@ static int qib_init_7322_variables(struct qib_devdata *dd)
/* we always allocate at least 2048 bytes for eager buffers */
dd->rcvegrbufsize = max(mtu, 2048);
+ BUG_ON(!is_power_of_2(dd->rcvegrbufsize));
+ dd->rcvegrbufsize_shift = ilog2(dd->rcvegrbufsize);
qib_7322_tidtemplate(dd);
@@ -6788,6 +6857,10 @@ struct qib_devdata *qib_init_iba7322_funcs(struct pci_dev *pdev,
(i >= ARRAY_SIZE(irq_table) &&
dd->rcd[i - ARRAY_SIZE(irq_table)]))
actual_cnt++;
+ /* reduce by ctxt's < 2 */
+ if (qib_krcvq01_no_msi)
+ actual_cnt -= dd->num_pports;
+
tabsize = actual_cnt;
dd->cspec->msix_entries = kmalloc(tabsize *
sizeof(struct msix_entry), GFP_KERNEL);
@@ -7119,7 +7192,8 @@ static void find_best_ent(struct qib_pportdata *ppd,
}
}
- /* Lookup serdes setting by cable type and attenuation */
+ /* Active cables don't have attenuation so we only set SERDES
+ * settings to account for the attenuation of the board traces. */
if (!override && QSFP_IS_ACTIVE(qd->tech)) {
*sdr_dds = txdds_sdr + ppd->dd->board_atten;
*ddr_dds = txdds_ddr + ppd->dd->board_atten;
@@ -7436,12 +7510,6 @@ static int serdes_7322_init_new(struct qib_pportdata *ppd)
u32 le_val, rxcaldone;
int chan, chan_done = (1 << SERDES_CHANS) - 1;
- /*
- * Initialize the Tx DDS tables. Also done every QSFP event,
- * for adapters with QSFP
- */
- init_txdds_table(ppd, 0);
-
/* Clear cmode-override, may be set from older driver */
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 10, 0 << 14, 1 << 14);
@@ -7627,6 +7695,12 @@ static int serdes_7322_init_new(struct qib_pportdata *ppd)
/* VGA output common mode */
ibsd_wr_allchans(ppd, 12, (3 << 2), BMASK(3, 2));
+ /*
+ * Initialize the Tx DDS tables. Also done every QSFP event,
+ * for adapters with QSFP
+ */
+ init_txdds_table(ppd, 0);
+
return 0;
}
diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c
index a01f3fce8eb3..58b0f8ad4a29 100644
--- a/drivers/infiniband/hw/qib/qib_init.c
+++ b/drivers/infiniband/hw/qib/qib_init.c
@@ -37,6 +37,7 @@
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/idr.h>
+#include <linux/module.h>
#include "qib.h"
#include "qib_common.h"
@@ -183,6 +184,9 @@ struct qib_ctxtdata *qib_create_ctxtdata(struct qib_pportdata *ppd, u32 ctxt)
rcd->rcvegrbuf_chunks = (rcd->rcvegrcnt +
rcd->rcvegrbufs_perchunk - 1) /
rcd->rcvegrbufs_perchunk;
+ BUG_ON(!is_power_of_2(rcd->rcvegrbufs_perchunk));
+ rcd->rcvegrbufs_perchunk_shift =
+ ilog2(rcd->rcvegrbufs_perchunk);
}
return rcd;
}
@@ -398,6 +402,7 @@ static void enable_chip(struct qib_devdata *dd)
if (rcd)
dd->f_rcvctrl(rcd->ppd, rcvmask, i);
}
+ dd->freectxts = dd->cfgctxts - dd->first_user_ctxt;
}
static void verify_interrupt(unsigned long opaque)
@@ -581,10 +586,6 @@ int qib_init(struct qib_devdata *dd, int reinit)
continue;
}
- /* let link come up, and enable IBC */
- spin_lock_irqsave(&ppd->lflags_lock, flags);
- ppd->lflags &= ~QIBL_IB_LINK_DISABLED;
- spin_unlock_irqrestore(&ppd->lflags_lock, flags);
portok++;
}
diff --git a/drivers/infiniband/hw/qib/qib_mad.c b/drivers/infiniband/hw/qib/qib_mad.c
index 8fd3df5bf04d..3b3745f261f0 100644
--- a/drivers/infiniband/hw/qib/qib_mad.c
+++ b/drivers/infiniband/hw/qib/qib_mad.c
@@ -1125,22 +1125,22 @@ static int subn_trap_repress(struct ib_smp *smp, struct ib_device *ibdev,
return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
}
-static int pma_get_classportinfo(struct ib_perf *pmp,
+static int pma_get_classportinfo(struct ib_pma_mad *pmp,
struct ib_device *ibdev)
{
- struct ib_pma_classportinfo *p =
- (struct ib_pma_classportinfo *)pmp->data;
+ struct ib_class_port_info *p =
+ (struct ib_class_port_info *)pmp->data;
struct qib_devdata *dd = dd_from_ibdev(ibdev);
memset(pmp->data, 0, sizeof(pmp->data));
- if (pmp->attr_mod != 0)
- pmp->status |= IB_SMP_INVALID_FIELD;
+ if (pmp->mad_hdr.attr_mod != 0)
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
/* Note that AllPortSelect is not valid */
p->base_version = 1;
p->class_version = 1;
- p->cap_mask = IB_PMA_CLASS_CAP_EXT_WIDTH;
+ p->capability_mask = IB_PMA_CLASS_CAP_EXT_WIDTH;
/*
* Set the most significant bit of CM2 to indicate support for
* congestion statistics
@@ -1154,7 +1154,7 @@ static int pma_get_classportinfo(struct ib_perf *pmp,
return reply((struct ib_smp *) pmp);
}
-static int pma_get_portsamplescontrol(struct ib_perf *pmp,
+static int pma_get_portsamplescontrol(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portsamplescontrol *p =
@@ -1169,8 +1169,8 @@ static int pma_get_portsamplescontrol(struct ib_perf *pmp,
memset(pmp->data, 0, sizeof(pmp->data));
p->port_select = port_select;
- if (pmp->attr_mod != 0 || port_select != port) {
- pmp->status |= IB_SMP_INVALID_FIELD;
+ if (pmp->mad_hdr.attr_mod != 0 || port_select != port) {
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
goto bail;
}
spin_lock_irqsave(&ibp->lock, flags);
@@ -1192,7 +1192,7 @@ bail:
return reply((struct ib_smp *) pmp);
}
-static int pma_set_portsamplescontrol(struct ib_perf *pmp,
+static int pma_set_portsamplescontrol(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portsamplescontrol *p =
@@ -1205,8 +1205,8 @@ static int pma_set_portsamplescontrol(struct ib_perf *pmp,
u8 status, xmit_flags;
int ret;
- if (pmp->attr_mod != 0 || p->port_select != port) {
- pmp->status |= IB_SMP_INVALID_FIELD;
+ if (pmp->mad_hdr.attr_mod != 0 || p->port_select != port) {
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
ret = reply((struct ib_smp *) pmp);
goto bail;
}
@@ -1321,7 +1321,7 @@ static u64 get_cache_hw_sample_counters(struct qib_pportdata *ppd,
return ret;
}
-static int pma_get_portsamplesresult(struct ib_perf *pmp,
+static int pma_get_portsamplesresult(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portsamplesresult *p =
@@ -1360,7 +1360,7 @@ static int pma_get_portsamplesresult(struct ib_perf *pmp,
return reply((struct ib_smp *) pmp);
}
-static int pma_get_portsamplesresult_ext(struct ib_perf *pmp,
+static int pma_get_portsamplesresult_ext(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portsamplesresult_ext *p =
@@ -1402,7 +1402,7 @@ static int pma_get_portsamplesresult_ext(struct ib_perf *pmp,
return reply((struct ib_smp *) pmp);
}
-static int pma_get_portcounters(struct ib_perf *pmp,
+static int pma_get_portcounters(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portcounters *p = (struct ib_pma_portcounters *)
@@ -1436,8 +1436,8 @@ static int pma_get_portcounters(struct ib_perf *pmp,
memset(pmp->data, 0, sizeof(pmp->data));
p->port_select = port_select;
- if (pmp->attr_mod != 0 || port_select != port)
- pmp->status |= IB_SMP_INVALID_FIELD;
+ if (pmp->mad_hdr.attr_mod != 0 || port_select != port)
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
if (cntrs.symbol_error_counter > 0xFFFFUL)
p->symbol_error_counter = cpu_to_be16(0xFFFF);
@@ -1472,7 +1472,7 @@ static int pma_get_portcounters(struct ib_perf *pmp,
cntrs.local_link_integrity_errors = 0xFUL;
if (cntrs.excessive_buffer_overrun_errors > 0xFUL)
cntrs.excessive_buffer_overrun_errors = 0xFUL;
- p->lli_ebor_errors = (cntrs.local_link_integrity_errors << 4) |
+ p->link_overrun_errors = (cntrs.local_link_integrity_errors << 4) |
cntrs.excessive_buffer_overrun_errors;
if (cntrs.vl15_dropped > 0xFFFFUL)
p->vl15_dropped = cpu_to_be16(0xFFFF);
@@ -1500,7 +1500,7 @@ static int pma_get_portcounters(struct ib_perf *pmp,
return reply((struct ib_smp *) pmp);
}
-static int pma_get_portcounters_cong(struct ib_perf *pmp,
+static int pma_get_portcounters_cong(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
/* Congestion PMA packets start at offset 24 not 64 */
@@ -1510,7 +1510,7 @@ static int pma_get_portcounters_cong(struct ib_perf *pmp,
struct qib_ibport *ibp = to_iport(ibdev, port);
struct qib_pportdata *ppd = ppd_from_ibp(ibp);
struct qib_devdata *dd = dd_from_ppd(ppd);
- u32 port_select = be32_to_cpu(pmp->attr_mod) & 0xFF;
+ u32 port_select = be32_to_cpu(pmp->mad_hdr.attr_mod) & 0xFF;
u64 xmit_wait_counter;
unsigned long flags;
@@ -1519,9 +1519,9 @@ static int pma_get_portcounters_cong(struct ib_perf *pmp,
* SET method ends up calling this anyway.
*/
if (!dd->psxmitwait_supported)
- pmp->status |= IB_SMP_UNSUP_METH_ATTR;
+ pmp->mad_hdr.status |= IB_SMP_UNSUP_METH_ATTR;
if (port_select != port)
- pmp->status |= IB_SMP_INVALID_FIELD;
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
qib_get_counters(ppd, &cntrs);
spin_lock_irqsave(&ppd->ibport_data.lock, flags);
@@ -1603,7 +1603,7 @@ static int pma_get_portcounters_cong(struct ib_perf *pmp,
cntrs.local_link_integrity_errors = 0xFUL;
if (cntrs.excessive_buffer_overrun_errors > 0xFUL)
cntrs.excessive_buffer_overrun_errors = 0xFUL;
- p->lli_ebor_errors = (cntrs.local_link_integrity_errors << 4) |
+ p->link_overrun_errors = (cntrs.local_link_integrity_errors << 4) |
cntrs.excessive_buffer_overrun_errors;
if (cntrs.vl15_dropped > 0xFFFFUL)
p->vl15_dropped = cpu_to_be16(0xFFFF);
@@ -1613,7 +1613,7 @@ static int pma_get_portcounters_cong(struct ib_perf *pmp,
return reply((struct ib_smp *)pmp);
}
-static int pma_get_portcounters_ext(struct ib_perf *pmp,
+static int pma_get_portcounters_ext(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portcounters_ext *p =
@@ -1626,8 +1626,8 @@ static int pma_get_portcounters_ext(struct ib_perf *pmp,
memset(pmp->data, 0, sizeof(pmp->data));
p->port_select = port_select;
- if (pmp->attr_mod != 0 || port_select != port) {
- pmp->status |= IB_SMP_INVALID_FIELD;
+ if (pmp->mad_hdr.attr_mod != 0 || port_select != port) {
+ pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
goto bail;
}
@@ -1652,7 +1652,7 @@ bail:
return reply((struct ib_smp *) pmp);
}
-static int pma_set_portcounters(struct ib_perf *pmp,
+static int pma_set_portcounters(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portcounters *p = (struct ib_pma_portcounters *)
@@ -1715,14 +1715,14 @@ static int pma_set_portcounters(struct ib_perf *pmp,
return pma_get_portcounters(pmp, ibdev, port);
}
-static int pma_set_portcounters_cong(struct ib_perf *pmp,
+static int pma_set_portcounters_cong(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct qib_ibport *ibp = to_iport(ibdev, port);
struct qib_pportdata *ppd = ppd_from_ibp(ibp);
struct qib_devdata *dd = dd_from_ppd(ppd);
struct qib_verbs_counters cntrs;
- u32 counter_select = (be32_to_cpu(pmp->attr_mod) >> 24) & 0xFF;
+ u32 counter_select = (be32_to_cpu(pmp->mad_hdr.attr_mod) >> 24) & 0xFF;
int ret = 0;
unsigned long flags;
@@ -1766,7 +1766,7 @@ static int pma_set_portcounters_cong(struct ib_perf *pmp,
return ret;
}
-static int pma_set_portcounters_ext(struct ib_perf *pmp,
+static int pma_set_portcounters_ext(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
struct ib_pma_portcounters *p = (struct ib_pma_portcounters *)
@@ -1959,19 +1959,19 @@ static int process_perf(struct ib_device *ibdev, u8 port,
struct ib_mad *in_mad,
struct ib_mad *out_mad)
{
- struct ib_perf *pmp = (struct ib_perf *)out_mad;
+ struct ib_pma_mad *pmp = (struct ib_pma_mad *)out_mad;
int ret;
*out_mad = *in_mad;
- if (pmp->class_version != 1) {
- pmp->status |= IB_SMP_UNSUP_VERSION;
+ if (pmp->mad_hdr.class_version != 1) {
+ pmp->mad_hdr.status |= IB_SMP_UNSUP_VERSION;
ret = reply((struct ib_smp *) pmp);
goto bail;
}
- switch (pmp->method) {
+ switch (pmp->mad_hdr.method) {
case IB_MGMT_METHOD_GET:
- switch (pmp->attr_id) {
+ switch (pmp->mad_hdr.attr_id) {
case IB_PMA_CLASS_PORT_INFO:
ret = pma_get_classportinfo(pmp, ibdev);
goto bail;
@@ -1994,13 +1994,13 @@ static int process_perf(struct ib_device *ibdev, u8 port,
ret = pma_get_portcounters_cong(pmp, ibdev, port);
goto bail;
default:
- pmp->status |= IB_SMP_UNSUP_METH_ATTR;
+ pmp->mad_hdr.status |= IB_SMP_UNSUP_METH_ATTR;
ret = reply((struct ib_smp *) pmp);
goto bail;
}
case IB_MGMT_METHOD_SET:
- switch (pmp->attr_id) {
+ switch (pmp->mad_hdr.attr_id) {
case IB_PMA_PORT_SAMPLES_CONTROL:
ret = pma_set_portsamplescontrol(pmp, ibdev, port);
goto bail;
@@ -2014,7 +2014,7 @@ static int process_perf(struct ib_device *ibdev, u8 port,
ret = pma_set_portcounters_cong(pmp, ibdev, port);
goto bail;
default:
- pmp->status |= IB_SMP_UNSUP_METH_ATTR;
+ pmp->mad_hdr.status |= IB_SMP_UNSUP_METH_ATTR;
ret = reply((struct ib_smp *) pmp);
goto bail;
}
@@ -2030,7 +2030,7 @@ static int process_perf(struct ib_device *ibdev, u8 port,
goto bail;
default:
- pmp->status |= IB_SMP_UNSUP_METHOD;
+ pmp->mad_hdr.status |= IB_SMP_UNSUP_METHOD;
ret = reply((struct ib_smp *) pmp);
}
diff --git a/drivers/infiniband/hw/qib/qib_mad.h b/drivers/infiniband/hw/qib/qib_mad.h
index 7840ab593bcf..ecc416cdbaaa 100644
--- a/drivers/infiniband/hw/qib/qib_mad.h
+++ b/drivers/infiniband/hw/qib/qib_mad.h
@@ -32,6 +32,8 @@
* SOFTWARE.
*/
+#include <rdma/ib_pma.h>
+
#define IB_SMP_UNSUP_VERSION cpu_to_be16(0x0004)
#define IB_SMP_UNSUP_METHOD cpu_to_be16(0x0008)
#define IB_SMP_UNSUP_METH_ATTR cpu_to_be16(0x000C)
@@ -180,109 +182,8 @@ struct ib_vl_weight_elem {
#define IB_VLARB_HIGHPRI_0_31 3
#define IB_VLARB_HIGHPRI_32_63 4
-/*
- * PMA class portinfo capability mask bits
- */
-#define IB_PMA_CLASS_CAP_ALLPORTSELECT cpu_to_be16(1 << 8)
-#define IB_PMA_CLASS_CAP_EXT_WIDTH cpu_to_be16(1 << 9)
-#define IB_PMA_CLASS_CAP_XMIT_WAIT cpu_to_be16(1 << 12)
-
-#define IB_PMA_CLASS_PORT_INFO cpu_to_be16(0x0001)
-#define IB_PMA_PORT_SAMPLES_CONTROL cpu_to_be16(0x0010)
-#define IB_PMA_PORT_SAMPLES_RESULT cpu_to_be16(0x0011)
-#define IB_PMA_PORT_COUNTERS cpu_to_be16(0x0012)
-#define IB_PMA_PORT_COUNTERS_EXT cpu_to_be16(0x001D)
-#define IB_PMA_PORT_SAMPLES_RESULT_EXT cpu_to_be16(0x001E)
#define IB_PMA_PORT_COUNTERS_CONG cpu_to_be16(0xFF00)
-struct ib_perf {
- u8 base_version;
- u8 mgmt_class;
- u8 class_version;
- u8 method;
- __be16 status;
- __be16 unused;
- __be64 tid;
- __be16 attr_id;
- __be16 resv;
- __be32 attr_mod;
- u8 reserved[40];
- u8 data[192];
-} __attribute__ ((packed));
-
-struct ib_pma_classportinfo {
- u8 base_version;
- u8 class_version;
- __be16 cap_mask;
- u8 reserved[3];
- u8 resp_time_value; /* only lower 5 bits */
- union ib_gid redirect_gid;
- __be32 redirect_tc_sl_fl; /* 8, 4, 20 bits respectively */
- __be16 redirect_lid;
- __be16 redirect_pkey;
- __be32 redirect_qp; /* only lower 24 bits */
- __be32 redirect_qkey;
- union ib_gid trap_gid;
- __be32 trap_tc_sl_fl; /* 8, 4, 20 bits respectively */
- __be16 trap_lid;
- __be16 trap_pkey;
- __be32 trap_hl_qp; /* 8, 24 bits respectively */
- __be32 trap_qkey;
-} __attribute__ ((packed));
-
-struct ib_pma_portsamplescontrol {
- u8 opcode;
- u8 port_select;
- u8 tick;
- u8 counter_width; /* only lower 3 bits */
- __be32 counter_mask0_9; /* 2, 10 * 3, bits */
- __be16 counter_mask10_14; /* 1, 5 * 3, bits */
- u8 sample_mechanisms;
- u8 sample_status; /* only lower 2 bits */
- __be64 option_mask;
- __be64 vendor_mask;
- __be32 sample_start;
- __be32 sample_interval;
- __be16 tag;
- __be16 counter_select[15];
-} __attribute__ ((packed));
-
-struct ib_pma_portsamplesresult {
- __be16 tag;
- __be16 sample_status; /* only lower 2 bits */
- __be32 counter[15];
-} __attribute__ ((packed));
-
-struct ib_pma_portsamplesresult_ext {
- __be16 tag;
- __be16 sample_status; /* only lower 2 bits */
- __be32 extended_width; /* only upper 2 bits */
- __be64 counter[15];
-} __attribute__ ((packed));
-
-struct ib_pma_portcounters {
- u8 reserved;
- u8 port_select;
- __be16 counter_select;
- __be16 symbol_error_counter;
- u8 link_error_recovery_counter;
- u8 link_downed_counter;
- __be16 port_rcv_errors;
- __be16 port_rcv_remphys_errors;
- __be16 port_rcv_switch_relay_errors;
- __be16 port_xmit_discards;
- u8 port_xmit_constraint_errors;
- u8 port_rcv_constraint_errors;
- u8 reserved1;
- u8 lli_ebor_errors; /* 4, 4, bits */
- __be16 reserved2;
- __be16 vl15_dropped;
- __be32 port_xmit_data;
- __be32 port_rcv_data;
- __be32 port_xmit_packets;
- __be32 port_rcv_packets;
-} __attribute__ ((packed));
-
struct ib_pma_portcounters_cong {
u8 reserved;
u8 reserved1;
@@ -297,7 +198,7 @@ struct ib_pma_portcounters_cong {
u8 port_xmit_constraint_errors;
u8 port_rcv_constraint_errors;
u8 reserved2;
- u8 lli_ebor_errors; /* 4, 4, bits */
+ u8 link_overrun_errors; /* LocalLink: 7:4, BufferOverrun: 3:0 */
__be16 reserved3;
__be16 vl15_dropped;
__be64 port_xmit_data;
@@ -316,49 +217,11 @@ struct ib_pma_portcounters_cong {
/* number of 4nsec cycles equaling 2secs */
#define QIB_CONG_TIMER_PSINTERVAL 0x1DCD64EC
-#define IB_PMA_SEL_SYMBOL_ERROR cpu_to_be16(0x0001)
-#define IB_PMA_SEL_LINK_ERROR_RECOVERY cpu_to_be16(0x0002)
-#define IB_PMA_SEL_LINK_DOWNED cpu_to_be16(0x0004)
-#define IB_PMA_SEL_PORT_RCV_ERRORS cpu_to_be16(0x0008)
-#define IB_PMA_SEL_PORT_RCV_REMPHYS_ERRORS cpu_to_be16(0x0010)
-#define IB_PMA_SEL_PORT_XMIT_DISCARDS cpu_to_be16(0x0040)
-#define IB_PMA_SEL_LOCAL_LINK_INTEGRITY_ERRORS cpu_to_be16(0x0200)
-#define IB_PMA_SEL_EXCESSIVE_BUFFER_OVERRUNS cpu_to_be16(0x0400)
-#define IB_PMA_SEL_PORT_VL15_DROPPED cpu_to_be16(0x0800)
-#define IB_PMA_SEL_PORT_XMIT_DATA cpu_to_be16(0x1000)
-#define IB_PMA_SEL_PORT_RCV_DATA cpu_to_be16(0x2000)
-#define IB_PMA_SEL_PORT_XMIT_PACKETS cpu_to_be16(0x4000)
-#define IB_PMA_SEL_PORT_RCV_PACKETS cpu_to_be16(0x8000)
-
#define IB_PMA_SEL_CONG_ALL 0x01
#define IB_PMA_SEL_CONG_PORT_DATA 0x02
#define IB_PMA_SEL_CONG_XMIT 0x04
#define IB_PMA_SEL_CONG_ROUTING 0x08
-struct ib_pma_portcounters_ext {
- u8 reserved;
- u8 port_select;
- __be16 counter_select;
- __be32 reserved1;
- __be64 port_xmit_data;
- __be64 port_rcv_data;
- __be64 port_xmit_packets;
- __be64 port_rcv_packets;
- __be64 port_unicast_xmit_packets;
- __be64 port_unicast_rcv_packets;
- __be64 port_multicast_xmit_packets;
- __be64 port_multicast_rcv_packets;
-} __attribute__ ((packed));
-
-#define IB_PMA_SELX_PORT_XMIT_DATA cpu_to_be16(0x0001)
-#define IB_PMA_SELX_PORT_RCV_DATA cpu_to_be16(0x0002)
-#define IB_PMA_SELX_PORT_XMIT_PACKETS cpu_to_be16(0x0004)
-#define IB_PMA_SELX_PORT_RCV_PACKETS cpu_to_be16(0x0008)
-#define IB_PMA_SELX_PORT_UNI_XMIT_PACKETS cpu_to_be16(0x0010)
-#define IB_PMA_SELX_PORT_UNI_RCV_PACKETS cpu_to_be16(0x0020)
-#define IB_PMA_SELX_PORT_MULTI_XMIT_PACKETS cpu_to_be16(0x0040)
-#define IB_PMA_SELX_PORT_MULTI_RCV_PACKETS cpu_to_be16(0x0080)
-
/*
* The PortSamplesControl.CounterMasks field is an array of 3 bit fields
* which specify the N'th counter's capabilities. See ch. 16.1.3.2.
diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c
index 891cc2ff5f00..97a8bdf68e60 100644
--- a/drivers/infiniband/hw/qib/qib_pcie.c
+++ b/drivers/infiniband/hw/qib/qib_pcie.c
@@ -35,6 +35,7 @@
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include <linux/aer.h>
+#include <linux/module.h>
#include "qib.h"
@@ -255,7 +256,7 @@ int qib_pcie_params(struct qib_devdata *dd, u32 minw, u32 *nent,
u16 linkstat, speed;
int pos = 0, pose, ret = 1;
- pose = pci_find_capability(dd->pcidev, PCI_CAP_ID_EXP);
+ pose = pci_pcie_cap(dd->pcidev);
if (!pose) {
qib_dev_err(dd, "Can't find PCI Express capability!\n");
/* set up something... */
@@ -509,7 +510,7 @@ static int qib_tune_pcie_coalesce(struct qib_devdata *dd)
qib_devinfo(dd->pcidev, "Parent not root\n");
return 1;
}
- ppos = pci_find_capability(parent, PCI_CAP_ID_EXP);
+ ppos = pci_pcie_cap(parent);
if (!ppos)
return 1;
if (parent->vendor != 0x8086)
@@ -578,14 +579,14 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd)
qib_devinfo(dd->pcidev, "Parent not root\n");
goto bail;
}
- ppos = pci_find_capability(parent, PCI_CAP_ID_EXP);
+ ppos = pci_pcie_cap(parent);
if (ppos) {
pci_read_config_word(parent, ppos + PCI_EXP_DEVCAP, &pcaps);
pci_read_config_word(parent, ppos + PCI_EXP_DEVCTL, &pctl);
} else
goto bail;
/* Find out supported and configured values for endpoint (us) */
- epos = pci_find_capability(dd->pcidev, PCI_CAP_ID_EXP);
+ epos = pci_pcie_cap(dd->pcidev);
if (epos) {
pci_read_config_word(dd->pcidev, epos + PCI_EXP_DEVCAP, &ecaps);
pci_read_config_word(dd->pcidev, epos + PCI_EXP_DEVCTL, &ectl);
diff --git a/drivers/infiniband/hw/qib/qib_qp.c b/drivers/infiniband/hw/qib/qib_qp.c
index e16751f8639e..7e7e16fbee99 100644
--- a/drivers/infiniband/hw/qib/qib_qp.c
+++ b/drivers/infiniband/hw/qib/qib_qp.c
@@ -34,6 +34,7 @@
#include <linux/err.h>
#include <linux/vmalloc.h>
+#include <linux/jhash.h>
#include "qib.h"
@@ -204,6 +205,13 @@ static void free_qpn(struct qib_qpn_table *qpt, u32 qpn)
clear_bit(qpn & BITS_PER_PAGE_MASK, map->page);
}
+static inline unsigned qpn_hash(struct qib_ibdev *dev, u32 qpn)
+{
+ return jhash_1word(qpn, dev->qp_rnd) &
+ (dev->qp_table_size - 1);
+}
+
+
/*
* Put the QP into the hash table.
* The hash table holds a reference to the QP.
@@ -211,22 +219,23 @@ static void free_qpn(struct qib_qpn_table *qpt, u32 qpn)
static void insert_qp(struct qib_ibdev *dev, struct qib_qp *qp)
{
struct qib_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
- unsigned n = qp->ibqp.qp_num % dev->qp_table_size;
unsigned long flags;
+ unsigned n = qpn_hash(dev, qp->ibqp.qp_num);
spin_lock_irqsave(&dev->qpt_lock, flags);
+ atomic_inc(&qp->refcount);
if (qp->ibqp.qp_num == 0)
- ibp->qp0 = qp;
+ rcu_assign_pointer(ibp->qp0, qp);
else if (qp->ibqp.qp_num == 1)
- ibp->qp1 = qp;
+ rcu_assign_pointer(ibp->qp1, qp);
else {
qp->next = dev->qp_table[n];
- dev->qp_table[n] = qp;
+ rcu_assign_pointer(dev->qp_table[n], qp);
}
- atomic_inc(&qp->refcount);
spin_unlock_irqrestore(&dev->qpt_lock, flags);
+ synchronize_rcu();
}
/*
@@ -236,29 +245,32 @@ static void insert_qp(struct qib_ibdev *dev, struct qib_qp *qp)
static void remove_qp(struct qib_ibdev *dev, struct qib_qp *qp)
{
struct qib_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
- struct qib_qp *q, **qpp;
+ unsigned n = qpn_hash(dev, qp->ibqp.qp_num);
unsigned long flags;
- qpp = &dev->qp_table[qp->ibqp.qp_num % dev->qp_table_size];
-
spin_lock_irqsave(&dev->qpt_lock, flags);
if (ibp->qp0 == qp) {
- ibp->qp0 = NULL;
atomic_dec(&qp->refcount);
+ rcu_assign_pointer(ibp->qp0, NULL);
} else if (ibp->qp1 == qp) {
- ibp->qp1 = NULL;
atomic_dec(&qp->refcount);
- } else
+ rcu_assign_pointer(ibp->qp1, NULL);
+ } else {
+ struct qib_qp *q, **qpp;
+
+ qpp = &dev->qp_table[n];
for (; (q = *qpp) != NULL; qpp = &q->next)
if (q == qp) {
- *qpp = qp->next;
- qp->next = NULL;
atomic_dec(&qp->refcount);
+ rcu_assign_pointer(*qpp, qp->next);
+ qp->next = NULL;
break;
}
+ }
spin_unlock_irqrestore(&dev->qpt_lock, flags);
+ synchronize_rcu();
}
/**
@@ -280,21 +292,24 @@ unsigned qib_free_all_qps(struct qib_devdata *dd)
if (!qib_mcast_tree_empty(ibp))
qp_inuse++;
- if (ibp->qp0)
+ rcu_read_lock();
+ if (rcu_dereference(ibp->qp0))
qp_inuse++;
- if (ibp->qp1)
+ if (rcu_dereference(ibp->qp1))
qp_inuse++;
+ rcu_read_unlock();
}
spin_lock_irqsave(&dev->qpt_lock, flags);
for (n = 0; n < dev->qp_table_size; n++) {
qp = dev->qp_table[n];
- dev->qp_table[n] = NULL;
+ rcu_assign_pointer(dev->qp_table[n], NULL);
for (; qp; qp = qp->next)
qp_inuse++;
}
spin_unlock_irqrestore(&dev->qpt_lock, flags);
+ synchronize_rcu();
return qp_inuse;
}
@@ -309,25 +324,28 @@ unsigned qib_free_all_qps(struct qib_devdata *dd)
*/
struct qib_qp *qib_lookup_qpn(struct qib_ibport *ibp, u32 qpn)
{
- struct qib_ibdev *dev = &ppd_from_ibp(ibp)->dd->verbs_dev;
- unsigned long flags;
- struct qib_qp *qp;
+ struct qib_qp *qp = NULL;
- spin_lock_irqsave(&dev->qpt_lock, flags);
+ if (unlikely(qpn <= 1)) {
+ rcu_read_lock();
+ if (qpn == 0)
+ qp = rcu_dereference(ibp->qp0);
+ else
+ qp = rcu_dereference(ibp->qp1);
+ } else {
+ struct qib_ibdev *dev = &ppd_from_ibp(ibp)->dd->verbs_dev;
+ unsigned n = qpn_hash(dev, qpn);
- if (qpn == 0)
- qp = ibp->qp0;
- else if (qpn == 1)
- qp = ibp->qp1;
- else
- for (qp = dev->qp_table[qpn % dev->qp_table_size]; qp;
- qp = qp->next)
+ rcu_read_lock();
+ for (qp = dev->qp_table[n]; rcu_dereference(qp); qp = qp->next)
if (qp->ibqp.qp_num == qpn)
break;
+ }
if (qp)
- atomic_inc(&qp->refcount);
+ if (unlikely(!atomic_inc_not_zero(&qp->refcount)))
+ qp = NULL;
- spin_unlock_irqrestore(&dev->qpt_lock, flags);
+ rcu_read_unlock();
return qp;
}
@@ -765,8 +783,10 @@ int qib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
}
}
- if (attr_mask & IB_QP_PATH_MTU)
+ if (attr_mask & IB_QP_PATH_MTU) {
qp->path_mtu = pmtu;
+ qp->pmtu = ib_mtu_enum_to_int(pmtu);
+ }
if (attr_mask & IB_QP_RETRY_CNT) {
qp->s_retry_cnt = attr->retry_cnt;
@@ -781,8 +801,12 @@ int qib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
if (attr_mask & IB_QP_MIN_RNR_TIMER)
qp->r_min_rnr_timer = attr->min_rnr_timer;
- if (attr_mask & IB_QP_TIMEOUT)
+ if (attr_mask & IB_QP_TIMEOUT) {
qp->timeout = attr->timeout;
+ qp->timeout_jiffies =
+ usecs_to_jiffies((4096UL * (1UL << qp->timeout)) /
+ 1000UL);
+ }
if (attr_mask & IB_QP_QKEY)
qp->qkey = attr->qkey;
@@ -1013,6 +1037,10 @@ struct ib_qp *qib_create_qp(struct ib_pd *ibpd,
ret = ERR_PTR(-ENOMEM);
goto bail_swq;
}
+ RCU_INIT_POINTER(qp->next, NULL);
+ qp->timeout_jiffies =
+ usecs_to_jiffies((4096UL * (1UL << qp->timeout)) /
+ 1000UL);
if (init_attr->srq)
sz = 0;
else {
diff --git a/drivers/infiniband/hw/qib/qib_qsfp.c b/drivers/infiniband/hw/qib/qib_qsfp.c
index 3374a52232c1..e06c4ed383f1 100644
--- a/drivers/infiniband/hw/qib/qib_qsfp.c
+++ b/drivers/infiniband/hw/qib/qib_qsfp.c
@@ -273,18 +273,12 @@ int qib_refresh_qsfp_cache(struct qib_pportdata *ppd, struct qib_qsfp_cache *cp)
int ret;
int idx;
u16 cks;
- u32 mask;
u8 peek[4];
/* ensure sane contents on invalid reads, for cable swaps */
memset(cp, 0, sizeof(*cp));
- mask = QSFP_GPIO_MOD_PRS_N;
- if (ppd->hw_pidx)
- mask <<= QSFP_GPIO_PORT2_SHIFT;
-
- ret = ppd->dd->f_gpio_mod(ppd->dd, 0, 0, 0);
- if (ret & mask) {
+ if (!qib_qsfp_mod_present(ppd)) {
ret = -ENODEV;
goto bail;
}
@@ -444,6 +438,19 @@ const char * const qib_qsfp_devtech[16] = {
static const char *pwr_codes = "1.5W2.0W2.5W3.5W";
+int qib_qsfp_mod_present(struct qib_pportdata *ppd)
+{
+ u32 mask;
+ int ret;
+
+ mask = QSFP_GPIO_MOD_PRS_N <<
+ (ppd->hw_pidx * QSFP_GPIO_PORT2_SHIFT);
+ ret = ppd->dd->f_gpio_mod(ppd->dd, 0, 0, 0);
+
+ return !((ret & mask) >>
+ ((ppd->hw_pidx * QSFP_GPIO_PORT2_SHIFT) + 3));
+}
+
/*
* Initialize structures that control access to QSFP. Called once per port
* on cards that support QSFP.
@@ -452,7 +459,6 @@ void qib_qsfp_init(struct qib_qsfp_data *qd,
void (*fevent)(struct work_struct *))
{
u32 mask, highs;
- int pins;
struct qib_devdata *dd = qd->ppd->dd;
@@ -480,8 +486,7 @@ void qib_qsfp_init(struct qib_qsfp_data *qd,
mask <<= QSFP_GPIO_PORT2_SHIFT;
/* Do not try to wait here. Better to let event handle it */
- pins = dd->f_gpio_mod(dd, 0, 0, 0);
- if (pins & mask)
+ if (!qib_qsfp_mod_present(qd->ppd))
goto bail;
/* We see a module, but it may be unwise to look yet. Just schedule */
qd->t_insert = get_jiffies_64();
diff --git a/drivers/infiniband/hw/qib/qib_qsfp.h b/drivers/infiniband/hw/qib/qib_qsfp.h
index c109bbdc90ac..46002a9417c0 100644
--- a/drivers/infiniband/hw/qib/qib_qsfp.h
+++ b/drivers/infiniband/hw/qib/qib_qsfp.h
@@ -34,6 +34,7 @@
#define QSFP_DEV 0xA0
#define QSFP_PWR_LAG_MSEC 2000
+#define QSFP_MODPRS_LAG_MSEC 20
/*
* Below are masks for various QSFP signals, for Port 1.
@@ -177,10 +178,12 @@ struct qib_qsfp_data {
struct work_struct work;
struct qib_qsfp_cache cache;
u64 t_insert;
+ u8 modpresent;
};
extern int qib_refresh_qsfp_cache(struct qib_pportdata *ppd,
struct qib_qsfp_cache *cp);
+extern int qib_qsfp_mod_present(struct qib_pportdata *ppd);
extern void qib_qsfp_init(struct qib_qsfp_data *qd,
void (*fevent)(struct work_struct *));
extern void qib_qsfp_deinit(struct qib_qsfp_data *qd);
diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c
index eca0c41f1226..894afac26f3b 100644
--- a/drivers/infiniband/hw/qib/qib_rc.c
+++ b/drivers/infiniband/hw/qib/qib_rc.c
@@ -59,8 +59,7 @@ static void start_timer(struct qib_qp *qp)
qp->s_flags |= QIB_S_TIMER;
qp->s_timer.function = rc_timeout;
/* 4.096 usec. * (1 << qp->timeout) */
- qp->s_timer.expires = jiffies +
- usecs_to_jiffies((4096UL * (1UL << qp->timeout)) / 1000UL);
+ qp->s_timer.expires = jiffies + qp->timeout_jiffies;
add_timer(&qp->s_timer);
}
@@ -239,7 +238,7 @@ int qib_make_rc_req(struct qib_qp *qp)
u32 len;
u32 bth0;
u32 bth2;
- u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+ u32 pmtu = qp->pmtu;
char newreq;
unsigned long flags;
int ret = 0;
@@ -272,13 +271,9 @@ int qib_make_rc_req(struct qib_qp *qp)
goto bail;
}
wqe = get_swqe_ptr(qp, qp->s_last);
- while (qp->s_last != qp->s_acked) {
- qib_send_complete(qp, wqe, IB_WC_SUCCESS);
- if (++qp->s_last >= qp->s_size)
- qp->s_last = 0;
- wqe = get_swqe_ptr(qp, qp->s_last);
- }
- qib_send_complete(qp, wqe, IB_WC_WR_FLUSH_ERR);
+ qib_send_complete(qp, wqe, qp->s_last != qp->s_acked ?
+ IB_WC_SUCCESS : IB_WC_WR_FLUSH_ERR);
+ /* will get called again */
goto done;
}
@@ -1519,9 +1514,7 @@ read_middle:
* 4.096 usec. * (1 << qp->timeout)
*/
qp->s_flags |= QIB_S_TIMER;
- mod_timer(&qp->s_timer, jiffies +
- usecs_to_jiffies((4096UL * (1UL << qp->timeout)) /
- 1000UL));
+ mod_timer(&qp->s_timer, jiffies + qp->timeout_jiffies);
if (qp->s_flags & QIB_S_WAIT_ACK) {
qp->s_flags &= ~QIB_S_WAIT_ACK;
qib_schedule_send(qp);
@@ -1732,7 +1725,7 @@ static int qib_rc_rcv_error(struct qib_other_headers *ohdr,
* same request.
*/
offset = ((psn - e->psn) & QIB_PSN_MASK) *
- ib_mtu_enum_to_int(qp->path_mtu);
+ qp->pmtu;
len = be32_to_cpu(reth->length);
if (unlikely(offset + len != e->rdma_sge.sge_length))
goto unlock_done;
@@ -1876,7 +1869,7 @@ void qib_rc_rcv(struct qib_ctxtdata *rcd, struct qib_ib_header *hdr,
u32 psn;
u32 pad;
struct ib_wc wc;
- u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+ u32 pmtu = qp->pmtu;
int diff;
struct ib_reth *reth;
unsigned long flags;
@@ -1892,10 +1885,8 @@ void qib_rc_rcv(struct qib_ctxtdata *rcd, struct qib_ib_header *hdr,
}
opcode = be32_to_cpu(ohdr->bth[0]);
- spin_lock_irqsave(&qp->s_lock, flags);
if (qib_ruc_check_hdr(ibp, hdr, has_grh, qp, opcode))
- goto sunlock;
- spin_unlock_irqrestore(&qp->s_lock, flags);
+ return;
psn = be32_to_cpu(ohdr->bth[2]);
opcode >>= 24;
@@ -1955,8 +1946,6 @@ void qib_rc_rcv(struct qib_ctxtdata *rcd, struct qib_ib_header *hdr,
break;
}
- memset(&wc, 0, sizeof wc);
-
if (qp->state == IB_QPS_RTR && !(qp->r_flags & QIB_R_COMM_EST)) {
qp->r_flags |= QIB_R_COMM_EST;
if (qp->ibqp.event_handler) {
@@ -2009,16 +1998,19 @@ send_middle:
goto rnr_nak;
qp->r_rcv_len = 0;
if (opcode == OP(SEND_ONLY))
- goto send_last;
- /* FALLTHROUGH */
+ goto no_immediate_data;
+ /* FALLTHROUGH for SEND_ONLY_WITH_IMMEDIATE */
case OP(SEND_LAST_WITH_IMMEDIATE):
send_last_imm:
wc.ex.imm_data = ohdr->u.imm_data;
hdrsize += 4;
wc.wc_flags = IB_WC_WITH_IMM;
- /* FALLTHROUGH */
+ goto send_last;
case OP(SEND_LAST):
case OP(RDMA_WRITE_LAST):
+no_immediate_data:
+ wc.wc_flags = 0;
+ wc.ex.imm_data = 0;
send_last:
/* Get the number of bytes the message was padded by. */
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
@@ -2051,6 +2043,12 @@ send_last:
wc.src_qp = qp->remote_qpn;
wc.slid = qp->remote_ah_attr.dlid;
wc.sl = qp->remote_ah_attr.sl;
+ /* zero fields that are N/A */
+ wc.vendor_err = 0;
+ wc.pkey_index = 0;
+ wc.dlid_path_bits = 0;
+ wc.port_num = 0;
+ wc.csum_ok = 0;
/* Signal completion event if the solicited bit is set. */
qib_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
(ohdr->bth[0] &
@@ -2089,7 +2087,7 @@ send_last:
if (opcode == OP(RDMA_WRITE_FIRST))
goto send_middle;
else if (opcode == OP(RDMA_WRITE_ONLY))
- goto send_last;
+ goto no_immediate_data;
ret = qib_get_rwqe(qp, 1);
if (ret < 0)
goto nack_op_err;
diff --git a/drivers/infiniband/hw/qib/qib_ruc.c b/drivers/infiniband/hw/qib/qib_ruc.c
index eb78d9367f06..b4b37e47321a 100644
--- a/drivers/infiniband/hw/qib/qib_ruc.c
+++ b/drivers/infiniband/hw/qib/qib_ruc.c
@@ -260,12 +260,15 @@ static int gid_ok(union ib_gid *gid, __be64 gid_prefix, __be64 id)
/*
*
- * This should be called with the QP s_lock held.
+ * This should be called with the QP r_lock held.
+ *
+ * The s_lock will be acquired around the qib_migrate_qp() call.
*/
int qib_ruc_check_hdr(struct qib_ibport *ibp, struct qib_ib_header *hdr,
int has_grh, struct qib_qp *qp, u32 bth0)
{
__be64 guid;
+ unsigned long flags;
if (qp->s_mig_state == IB_MIG_ARMED && (bth0 & IB_BTH_MIG_REQ)) {
if (!has_grh) {
@@ -295,7 +298,9 @@ int qib_ruc_check_hdr(struct qib_ibport *ibp, struct qib_ib_header *hdr,
if (be16_to_cpu(hdr->lrh[3]) != qp->alt_ah_attr.dlid ||
ppd_from_ibp(ibp)->port != qp->alt_ah_attr.port_num)
goto err;
+ spin_lock_irqsave(&qp->s_lock, flags);
qib_migrate_qp(qp);
+ spin_unlock_irqrestore(&qp->s_lock, flags);
} else {
if (!has_grh) {
if (qp->remote_ah_attr.ah_flags & IB_AH_GRH)
diff --git a/drivers/infiniband/hw/qib/qib_sd7220.c b/drivers/infiniband/hw/qib/qib_sd7220.c
index e9f9f8bc3204..de1a4b2f33c0 100644
--- a/drivers/infiniband/hw/qib/qib_sd7220.c
+++ b/drivers/infiniband/hw/qib/qib_sd7220.c
@@ -38,6 +38,7 @@
#include <linux/pci.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include "qib.h"
diff --git a/drivers/infiniband/hw/qib/qib_sdma.c b/drivers/infiniband/hw/qib/qib_sdma.c
index cad44491320b..12a9604310d7 100644
--- a/drivers/infiniband/hw/qib/qib_sdma.c
+++ b/drivers/infiniband/hw/qib/qib_sdma.c
@@ -32,6 +32,7 @@
#include <linux/spinlock.h>
#include <linux/netdevice.h>
+#include <linux/moduleparam.h>
#include "qib.h"
#include "qib_common.h"
diff --git a/drivers/infiniband/hw/qib/qib_srq.c b/drivers/infiniband/hw/qib/qib_srq.c
index c3ec8efc2ed8..d6235931a1ba 100644
--- a/drivers/infiniband/hw/qib/qib_srq.c
+++ b/drivers/infiniband/hw/qib/qib_srq.c
@@ -107,6 +107,11 @@ struct ib_srq *qib_create_srq(struct ib_pd *ibpd,
u32 sz;
struct ib_srq *ret;
+ if (srq_init_attr->srq_type != IB_SRQT_BASIC) {
+ ret = ERR_PTR(-ENOSYS);
+ goto done;
+ }
+
if (srq_init_attr->attr.max_sge == 0 ||
srq_init_attr->attr.max_sge > ib_qib_max_srq_sges ||
srq_init_attr->attr.max_wr == 0 ||
diff --git a/drivers/infiniband/hw/qib/qib_sysfs.c b/drivers/infiniband/hw/qib/qib_sysfs.c
index d50a33fe8bbc..78fbd56879d4 100644
--- a/drivers/infiniband/hw/qib/qib_sysfs.c
+++ b/drivers/infiniband/hw/qib/qib_sysfs.c
@@ -507,6 +507,17 @@ static ssize_t show_nctxts(struct device *device,
dd->first_user_ctxt);
}
+static ssize_t show_nfreectxts(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct qib_ibdev *dev =
+ container_of(device, struct qib_ibdev, ibdev.dev);
+ struct qib_devdata *dd = dd_from_dev(dev);
+
+ /* Return the number of free user ports (contexts) available. */
+ return scnprintf(buf, PAGE_SIZE, "%u\n", dd->freectxts);
+}
+
static ssize_t show_serial(struct device *device,
struct device_attribute *attr, char *buf)
{
@@ -604,6 +615,7 @@ static DEVICE_ATTR(hca_type, S_IRUGO, show_hca, NULL);
static DEVICE_ATTR(board_id, S_IRUGO, show_hca, NULL);
static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
static DEVICE_ATTR(nctxts, S_IRUGO, show_nctxts, NULL);
+static DEVICE_ATTR(nfreectxts, S_IRUGO, show_nfreectxts, NULL);
static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL);
static DEVICE_ATTR(boardversion, S_IRUGO, show_boardversion, NULL);
static DEVICE_ATTR(logged_errors, S_IRUGO, show_logged_errs, NULL);
@@ -617,6 +629,7 @@ static struct device_attribute *qib_attributes[] = {
&dev_attr_board_id,
&dev_attr_version,
&dev_attr_nctxts,
+ &dev_attr_nfreectxts,
&dev_attr_serial,
&dev_attr_boardversion,
&dev_attr_logged_errors,
diff --git a/drivers/infiniband/hw/qib/qib_tx.c b/drivers/infiniband/hw/qib/qib_tx.c
index 7f36454c225e..1bf626c40172 100644
--- a/drivers/infiniband/hw/qib/qib_tx.c
+++ b/drivers/infiniband/hw/qib/qib_tx.c
@@ -36,6 +36,7 @@
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
+#include <linux/moduleparam.h>
#include "qib.h"
diff --git a/drivers/infiniband/hw/qib/qib_uc.c b/drivers/infiniband/hw/qib/qib_uc.c
index 32ccf3c824ca..847e7afdfd94 100644
--- a/drivers/infiniband/hw/qib/qib_uc.c
+++ b/drivers/infiniband/hw/qib/qib_uc.c
@@ -51,7 +51,7 @@ int qib_make_uc_req(struct qib_qp *qp)
u32 hwords;
u32 bth0;
u32 len;
- u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+ u32 pmtu = qp->pmtu;
int ret = 0;
spin_lock_irqsave(&qp->s_lock, flags);
@@ -243,13 +243,12 @@ void qib_uc_rcv(struct qib_ibport *ibp, struct qib_ib_header *hdr,
int has_grh, void *data, u32 tlen, struct qib_qp *qp)
{
struct qib_other_headers *ohdr;
- unsigned long flags;
u32 opcode;
u32 hdrsize;
u32 psn;
u32 pad;
struct ib_wc wc;
- u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+ u32 pmtu = qp->pmtu;
struct ib_reth *reth;
int ret;
@@ -263,14 +262,11 @@ void qib_uc_rcv(struct qib_ibport *ibp, struct qib_ib_header *hdr,
}
opcode = be32_to_cpu(ohdr->bth[0]);
- spin_lock_irqsave(&qp->s_lock, flags);
if (qib_ruc_check_hdr(ibp, hdr, has_grh, qp, opcode))
- goto sunlock;
- spin_unlock_irqrestore(&qp->s_lock, flags);
+ return;
psn = be32_to_cpu(ohdr->bth[2]);
opcode >>= 24;
- memset(&wc, 0, sizeof wc);
/* Compare the PSN verses the expected PSN. */
if (unlikely(qib_cmp24(psn, qp->r_psn) != 0)) {
@@ -370,7 +366,7 @@ send_first:
}
qp->r_rcv_len = 0;
if (opcode == OP(SEND_ONLY))
- goto send_last;
+ goto no_immediate_data;
else if (opcode == OP(SEND_ONLY_WITH_IMMEDIATE))
goto send_last_imm;
/* FALLTHROUGH */
@@ -389,8 +385,11 @@ send_last_imm:
wc.ex.imm_data = ohdr->u.imm_data;
hdrsize += 4;
wc.wc_flags = IB_WC_WITH_IMM;
- /* FALLTHROUGH */
+ goto send_last;
case OP(SEND_LAST):
+no_immediate_data:
+ wc.ex.imm_data = 0;
+ wc.wc_flags = 0;
send_last:
/* Get the number of bytes the message was padded by. */
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
@@ -418,6 +417,12 @@ last_imm:
wc.src_qp = qp->remote_qpn;
wc.slid = qp->remote_ah_attr.dlid;
wc.sl = qp->remote_ah_attr.sl;
+ /* zero fields that are N/A */
+ wc.vendor_err = 0;
+ wc.pkey_index = 0;
+ wc.dlid_path_bits = 0;
+ wc.port_num = 0;
+ wc.csum_ok = 0;
/* Signal completion event if the solicited bit is set. */
qib_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
(ohdr->bth[0] &
@@ -546,6 +551,4 @@ op_err:
qib_rc_error(qp, IB_WC_LOC_QP_OP_ERR);
return;
-sunlock:
- spin_unlock_irqrestore(&qp->s_lock, flags);
}
diff --git a/drivers/infiniband/hw/qib/qib_user_pages.c b/drivers/infiniband/hw/qib/qib_user_pages.c
index 7689e49c13c9..2bc1d2b96298 100644
--- a/drivers/infiniband/hw/qib/qib_user_pages.c
+++ b/drivers/infiniband/hw/qib/qib_user_pages.c
@@ -74,7 +74,7 @@ static int __qib_get_user_pages(unsigned long start_page, size_t num_pages,
goto bail_release;
}
- current->mm->locked_vm += num_pages;
+ current->mm->pinned_vm += num_pages;
ret = 0;
goto bail;
@@ -151,7 +151,7 @@ void qib_release_user_pages(struct page **p, size_t num_pages)
__qib_release_user_pages(p, num_pages, 1);
if (current->mm) {
- current->mm->locked_vm -= num_pages;
+ current->mm->pinned_vm -= num_pages;
up_write(&current->mm->mmap_sem);
}
}
diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c
index 9fab40488850..a894762da462 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.c
+++ b/drivers/infiniband/hw/qib/qib_verbs.c
@@ -35,14 +35,16 @@
#include <rdma/ib_mad.h>
#include <rdma/ib_user_verbs.h>
#include <linux/io.h>
+#include <linux/module.h>
#include <linux/utsname.h>
#include <linux/rculist.h>
#include <linux/mm.h>
+#include <linux/random.h>
#include "qib.h"
#include "qib_common.h"
-static unsigned int ib_qib_qp_table_size = 251;
+static unsigned int ib_qib_qp_table_size = 256;
module_param_named(qp_table_size, ib_qib_qp_table_size, uint, S_IRUGO);
MODULE_PARM_DESC(qp_table_size, "QP table size");
@@ -659,17 +661,25 @@ void qib_ib_rcv(struct qib_ctxtdata *rcd, void *rhdr, void *data, u32 tlen)
if (atomic_dec_return(&mcast->refcount) <= 1)
wake_up(&mcast->wait);
} else {
- qp = qib_lookup_qpn(ibp, qp_num);
- if (!qp)
- goto drop;
+ if (rcd->lookaside_qp) {
+ if (rcd->lookaside_qpn != qp_num) {
+ if (atomic_dec_and_test(
+ &rcd->lookaside_qp->refcount))
+ wake_up(
+ &rcd->lookaside_qp->wait);
+ rcd->lookaside_qp = NULL;
+ }
+ }
+ if (!rcd->lookaside_qp) {
+ qp = qib_lookup_qpn(ibp, qp_num);
+ if (!qp)
+ goto drop;
+ rcd->lookaside_qp = qp;
+ rcd->lookaside_qpn = qp_num;
+ } else
+ qp = rcd->lookaside_qp;
ibp->n_unicast_rcv++;
qib_qp_rcv(rcd, hdr, lnh == QIB_LRH_GRH, data, tlen, qp);
- /*
- * Notify qib_destroy_qp() if it is waiting
- * for us to finish.
- */
- if (atomic_dec_and_test(&qp->refcount))
- wake_up(&qp->wait);
}
return;
@@ -1974,6 +1984,8 @@ static void init_ibport(struct qib_pportdata *ppd)
ibp->z_excessive_buffer_overrun_errors =
cntrs.excessive_buffer_overrun_errors;
ibp->z_vl15_dropped = cntrs.vl15_dropped;
+ RCU_INIT_POINTER(ibp->qp0, NULL);
+ RCU_INIT_POINTER(ibp->qp1, NULL);
}
/**
@@ -1990,12 +2002,15 @@ int qib_register_ib_device(struct qib_devdata *dd)
int ret;
dev->qp_table_size = ib_qib_qp_table_size;
- dev->qp_table = kzalloc(dev->qp_table_size * sizeof *dev->qp_table,
+ get_random_bytes(&dev->qp_rnd, sizeof(dev->qp_rnd));
+ dev->qp_table = kmalloc(dev->qp_table_size * sizeof *dev->qp_table,
GFP_KERNEL);
if (!dev->qp_table) {
ret = -ENOMEM;
goto err_qpt;
}
+ for (i = 0; i < dev->qp_table_size; i++)
+ RCU_INIT_POINTER(dev->qp_table[i], NULL);
for (i = 0; i < dd->num_pports; i++)
init_ibport(ppd + i);
diff --git a/drivers/infiniband/hw/qib/qib_verbs.h b/drivers/infiniband/hw/qib/qib_verbs.h
index 95e5b47223b3..0c19ef0c4123 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.h
+++ b/drivers/infiniband/hw/qib/qib_verbs.h
@@ -485,6 +485,7 @@ struct qib_qp {
u8 alt_timeout; /* Alternate path timeout for this QP */
u8 port_num;
enum ib_mtu path_mtu;
+ u32 pmtu; /* decoded from path_mtu */
u32 remote_qpn;
u32 qkey; /* QKEY for this QP (for UD or RD) */
u32 s_size; /* send work queue size */
@@ -495,6 +496,7 @@ struct qib_qp {
u32 s_last; /* last completed entry */
u32 s_ssn; /* SSN of tail entry */
u32 s_lsn; /* limit sequence number (credit) */
+ unsigned long timeout_jiffies; /* computed from timeout */
struct qib_swqe *s_wq; /* send work queue */
struct qib_swqe *s_wqe;
struct qib_rq r_rq; /* receive work queue */
@@ -723,7 +725,8 @@ struct qib_ibdev {
dma_addr_t pio_hdrs_phys;
/* list of QPs waiting for RNR timer */
spinlock_t pending_lock; /* protect wait lists, PMA counters, etc. */
- unsigned qp_table_size; /* size of the hash table */
+ u32 qp_table_size; /* size of the hash table */
+ u32 qp_rnd; /* random bytes for hash */
spinlock_t qpt_lock;
u32 n_piowait;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 7b6985a2e652..b3cc1e062b17 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -45,7 +45,7 @@
#include <net/neighbour.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_pack.h>
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 39913a065f99..014504d8e43c 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -37,6 +37,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/moduleparam.h>
#include "ipoib.h"
@@ -84,7 +85,7 @@ static void ipoib_cm_dma_unmap_rx(struct ipoib_dev_priv *priv, int frags,
ib_dma_unmap_single(priv->ca, mapping[0], IPOIB_CM_HEAD_SIZE, DMA_FROM_DEVICE);
for (i = 0; i < frags; ++i)
- ib_dma_unmap_single(priv->ca, mapping[i + 1], PAGE_SIZE, DMA_FROM_DEVICE);
+ ib_dma_unmap_page(priv->ca, mapping[i + 1], PAGE_SIZE, DMA_FROM_DEVICE);
}
static int ipoib_cm_post_receive_srq(struct net_device *dev, int id)
@@ -169,7 +170,7 @@ static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev,
goto partial_error;
skb_fill_page_desc(skb, i, page, 0, PAGE_SIZE);
- mapping[i + 1] = ib_dma_map_page(priv->ca, skb_shinfo(skb)->frags[i].page,
+ mapping[i + 1] = ib_dma_map_page(priv->ca, page,
0, PAGE_SIZE, DMA_FROM_DEVICE);
if (unlikely(ib_dma_mapping_error(priv->ca, mapping[i + 1])))
goto partial_error;
@@ -183,7 +184,7 @@ partial_error:
ib_dma_unmap_single(priv->ca, mapping[0], IPOIB_CM_HEAD_SIZE, DMA_FROM_DEVICE);
for (; i > 0; --i)
- ib_dma_unmap_single(priv->ca, mapping[i], PAGE_SIZE, DMA_FROM_DEVICE);
+ ib_dma_unmap_page(priv->ca, mapping[i], PAGE_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
return NULL;
@@ -537,12 +538,13 @@ static void skb_put_frags(struct sk_buff *skb, unsigned int hdr_space,
if (length == 0) {
/* don't need this page */
- skb_fill_page_desc(toskb, i, frag->page, 0, PAGE_SIZE);
+ skb_fill_page_desc(toskb, i, skb_frag_page(frag),
+ 0, PAGE_SIZE);
--skb_shinfo(skb)->nr_frags;
} else {
size = min(length, (unsigned) PAGE_SIZE);
- frag->size = size;
+ skb_frag_size_set(frag, size);
skb->data_len += size;
skb->truesize += size;
skb->len += size;
@@ -1496,6 +1498,7 @@ static void ipoib_cm_create_srq(struct net_device *dev, int max_sge)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ib_srq_init_attr srq_init_attr = {
+ .srq_type = IB_SRQT_BASIC,
.attr = {
.max_wr = ipoib_recvq_size,
.max_sge = max_sge
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
index 86eae229dc49..50061854616e 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_fs.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
@@ -37,6 +37,7 @@
struct file_operations;
#include <linux/debugfs.h>
+#include <linux/export.h>
#include "ipoib.h"
@@ -212,16 +213,15 @@ static int ipoib_path_seq_show(struct seq_file *file, void *iter_ptr)
gid_buf, path.pathrec.dlid ? "yes" : "no");
if (path.pathrec.dlid) {
- rate = ib_rate_to_mult(path.pathrec.rate) * 25;
+ rate = ib_rate_to_mbps(path.pathrec.rate);
seq_printf(file,
" DLID: 0x%04x\n"
" SL: %12d\n"
- " rate: %*d%s Gb/sec\n",
+ " rate: %8d.%d Gb/sec\n",
be16_to_cpu(path.pathrec.dlid),
path.pathrec.sl,
- 10 - ((rate % 10) ? 2 : 0),
- rate / 10, rate % 10 ? ".5" : "");
+ rate / 1000, rate % 1000);
}
seq_putc(file, '\n');
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index 81ae61d68a22..0ef9af94997d 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -34,6 +34,7 @@
*/
#include <linux/delay.h>
+#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
@@ -117,7 +118,7 @@ static void ipoib_ud_skb_put_frags(struct ipoib_dev_priv *priv,
size = length - IPOIB_UD_HEAD_SIZE;
- frag->size = size;
+ skb_frag_size_set(frag, size);
skb->data_len += size;
skb->truesize += size;
} else
@@ -182,7 +183,7 @@ static struct sk_buff *ipoib_alloc_rx_skb(struct net_device *dev, int id)
goto partial_error;
skb_fill_page_desc(skb, 0, page, 0, PAGE_SIZE);
mapping[1] =
- ib_dma_map_page(priv->ca, skb_shinfo(skb)->frags[0].page,
+ ib_dma_map_page(priv->ca, page,
0, PAGE_SIZE, DMA_FROM_DEVICE);
if (unlikely(ib_dma_mapping_error(priv->ca, mapping[1])))
goto partial_error;
@@ -322,9 +323,10 @@ static int ipoib_dma_map_tx(struct ib_device *ca,
off = 0;
for (i = 0; i < skb_shinfo(skb)->nr_frags; ++i) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- mapping[i + off] = ib_dma_map_page(ca, frag->page,
- frag->page_offset, frag->size,
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ mapping[i + off] = ib_dma_map_page(ca,
+ skb_frag_page(frag),
+ frag->page_offset, skb_frag_size(frag),
DMA_TO_DEVICE);
if (unlikely(ib_dma_mapping_error(ca, mapping[i + off])))
goto partial_error;
@@ -333,8 +335,9 @@ static int ipoib_dma_map_tx(struct ib_device *ca,
partial_error:
for (; i > 0; --i) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
- ib_dma_unmap_page(ca, mapping[i - !off], frag->size, DMA_TO_DEVICE);
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
+
+ ib_dma_unmap_page(ca, mapping[i - !off], skb_frag_size(frag), DMA_TO_DEVICE);
}
if (off)
@@ -358,8 +361,9 @@ static void ipoib_dma_unmap_tx(struct ib_device *ca,
off = 0;
for (i = 0; i < skb_shinfo(skb)->nr_frags; ++i) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- ib_dma_unmap_page(ca, mapping[i + off], frag->size,
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ ib_dma_unmap_page(ca, mapping[i + off], skb_frag_size(frag),
DMA_TO_DEVICE);
}
}
@@ -509,7 +513,7 @@ static inline int post_send(struct ipoib_dev_priv *priv,
for (i = 0; i < nr_frags; ++i) {
priv->tx_sge[i + off].addr = mapping[i + off];
- priv->tx_sge[i + off].length = frags[i].size;
+ priv->tx_sge[i + off].length = skb_frag_size(&frags[i]);
}
priv->tx_wr.num_sge = nr_frags + off;
priv->tx_wr.wr_id = wr_id;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 86addca9ddf6..7567b6000230 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -560,9 +560,11 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_path *path;
struct ipoib_neigh *neigh;
+ struct neighbour *n;
unsigned long flags;
- neigh = ipoib_neigh_alloc(skb_dst(skb)->neighbour, skb->dev);
+ n = dst_get_neighbour(skb_dst(skb));
+ neigh = ipoib_neigh_alloc(n, skb->dev);
if (!neigh) {
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
@@ -571,9 +573,9 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
spin_lock_irqsave(&priv->lock, flags);
- path = __path_find(dev, skb_dst(skb)->neighbour->ha + 4);
+ path = __path_find(dev, n->ha + 4);
if (!path) {
- path = path_rec_create(dev, skb_dst(skb)->neighbour->ha + 4);
+ path = path_rec_create(dev, n->ha + 4);
if (!path)
goto err_path;
@@ -607,7 +609,7 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
}
} else {
spin_unlock_irqrestore(&priv->lock, flags);
- ipoib_send(dev, skb, path->ah, IPOIB_QPN(skb_dst(skb)->neighbour->ha));
+ ipoib_send(dev, skb, path->ah, IPOIB_QPN(n->ha));
return;
}
} else {
@@ -637,17 +639,20 @@ err_drop:
static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(skb->dev);
+ struct dst_entry *dst = skb_dst(skb);
+ struct neighbour *n;
/* Look up path record for unicasts */
- if (skb_dst(skb)->neighbour->ha[4] != 0xff) {
+ n = dst_get_neighbour(dst);
+ if (n->ha[4] != 0xff) {
neigh_add_path(skb, dev);
return;
}
/* Add in the P_Key for multicasts */
- skb_dst(skb)->neighbour->ha[8] = (priv->pkey >> 8) & 0xff;
- skb_dst(skb)->neighbour->ha[9] = priv->pkey & 0xff;
- ipoib_mcast_send(dev, skb_dst(skb)->neighbour->ha + 4, skb);
+ n->ha[8] = (priv->pkey >> 8) & 0xff;
+ n->ha[9] = priv->pkey & 0xff;
+ ipoib_mcast_send(dev, n->ha + 4, skb);
}
static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
@@ -712,18 +717,22 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_neigh *neigh;
+ struct neighbour *n = NULL;
unsigned long flags;
- if (likely(skb_dst(skb) && skb_dst(skb)->neighbour)) {
- if (unlikely(!*to_ipoib_neigh(skb_dst(skb)->neighbour))) {
+ if (likely(skb_dst(skb)))
+ n = dst_get_neighbour(skb_dst(skb));
+
+ if (likely(n)) {
+ if (unlikely(!*to_ipoib_neigh(n))) {
ipoib_path_lookup(skb, dev);
return NETDEV_TX_OK;
}
- neigh = *to_ipoib_neigh(skb_dst(skb)->neighbour);
+ neigh = *to_ipoib_neigh(n);
if (unlikely((memcmp(&neigh->dgid.raw,
- skb_dst(skb)->neighbour->ha + 4,
+ n->ha + 4,
sizeof(union ib_gid))) ||
(neigh->dev != dev))) {
spin_lock_irqsave(&priv->lock, flags);
@@ -749,7 +758,7 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
} else if (neigh->ah) {
- ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(skb_dst(skb)->neighbour->ha));
+ ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha));
return NETDEV_TX_OK;
}
@@ -812,6 +821,8 @@ static int ipoib_hard_header(struct sk_buff *skb,
const void *daddr, const void *saddr, unsigned len)
{
struct ipoib_header *header;
+ struct dst_entry *dst;
+ struct neighbour *n;
header = (struct ipoib_header *) skb_push(skb, sizeof *header);
@@ -823,7 +834,11 @@ static int ipoib_hard_header(struct sk_buff *skb,
* destination address onto the front of the skb so we can
* figure out where to send the packet later.
*/
- if ((!skb_dst(skb) || !skb_dst(skb)->neighbour) && daddr) {
+ dst = skb_dst(skb);
+ n = NULL;
+ if (dst)
+ n = dst_get_neighbour(dst);
+ if ((!dst || !n) && daddr) {
struct ipoib_pseudoheader *phdr =
(struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr);
memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN);
@@ -983,7 +998,7 @@ static const struct net_device_ops ipoib_netdev_ops = {
.ndo_fix_features = ipoib_fix_features,
.ndo_start_xmit = ipoib_start_xmit,
.ndo_tx_timeout = ipoib_timeout,
- .ndo_set_multicast_list = ipoib_set_mcast_list,
+ .ndo_set_rx_mode = ipoib_set_mcast_list,
.ndo_neigh_setup = ipoib_neigh_setup_dev,
};
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 3871ac663554..1b7a97686356 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -34,6 +34,7 @@
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
+#include <linux/moduleparam.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/igmp.h>
@@ -258,11 +259,15 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,
netif_tx_lock_bh(dev);
while (!skb_queue_empty(&mcast->pkt_queue)) {
struct sk_buff *skb = skb_dequeue(&mcast->pkt_queue);
+ struct dst_entry *dst = skb_dst(skb);
+ struct neighbour *n = NULL;
+
netif_tx_unlock_bh(dev);
skb->dev = dev;
-
- if (!skb_dst(skb) || !skb_dst(skb)->neighbour) {
+ if (dst)
+ n = dst_get_neighbour(dst);
+ if (!dst || !n) {
/* put pseudoheader back on for next time */
skb_push(skb, sizeof (struct ipoib_pseudoheader));
}
@@ -715,11 +720,13 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
out:
if (mcast && mcast->ah) {
- if (skb_dst(skb) &&
- skb_dst(skb)->neighbour &&
- !*to_ipoib_neigh(skb_dst(skb)->neighbour)) {
- struct ipoib_neigh *neigh = ipoib_neigh_alloc(skb_dst(skb)->neighbour,
- skb->dev);
+ struct dst_entry *dst = skb_dst(skb);
+ struct neighbour *n = NULL;
+ if (dst)
+ n = dst_get_neighbour(dst);
+ if (n && !*to_ipoib_neigh(n)) {
+ struct ipoib_neigh *neigh = ipoib_neigh_alloc(n,
+ skb->dev);
if (neigh) {
kref_get(&mcast->ah->ref);
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 8db008de5392..7e7373a700e6 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -57,6 +57,7 @@
#include <linux/scatterlist.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <net/sock.h>
@@ -101,13 +102,17 @@ iscsi_iser_recv(struct iscsi_conn *conn,
/* verify PDU length */
datalen = ntoh24(hdr->dlength);
- if (datalen != rx_data_len) {
- printk(KERN_ERR "iscsi_iser: datalen %d (hdr) != %d (IB) \n",
- datalen, rx_data_len);
+ if (datalen > rx_data_len || (datalen + 4) < rx_data_len) {
+ iser_err("wrong datalen %d (hdr), %d (IB)\n",
+ datalen, rx_data_len);
rc = ISCSI_ERR_DATALEN;
goto error;
}
+ if (datalen != rx_data_len)
+ iser_dbg("aligned datalen (%d) hdr, %d (IB)\n",
+ datalen, rx_data_len);
+
/* read AHS */
ahslen = hdr->hlength * 4;
@@ -147,7 +152,6 @@ int iser_initialize_task_headers(struct iscsi_task *task,
tx_desc->tx_sg[0].length = ISER_HEADERS_LEN;
tx_desc->tx_sg[0].lkey = device->mr->lkey;
- iser_task->headers_initialized = 1;
iser_task->iser_conn = iser_conn;
return 0;
}
@@ -162,8 +166,7 @@ iscsi_iser_task_init(struct iscsi_task *task)
{
struct iscsi_iser_task *iser_task = task->dd_data;
- if (!iser_task->headers_initialized)
- if (iser_initialize_task_headers(task, &iser_task->desc))
+ if (iser_initialize_task_headers(task, &iser_task->desc))
return -ENOMEM;
/* mgmt task */
@@ -274,6 +277,13 @@ iscsi_iser_task_xmit(struct iscsi_task *task)
static void iscsi_iser_cleanup_task(struct iscsi_task *task)
{
struct iscsi_iser_task *iser_task = task->dd_data;
+ struct iser_tx_desc *tx_desc = &iser_task->desc;
+
+ struct iscsi_iser_conn *iser_conn = task->conn->dd_data;
+ struct iser_device *device = iser_conn->ib_conn->device;
+
+ ib_dma_unmap_single(device->ib_device,
+ tx_desc->dma_addr, ISER_HEADERS_LEN, DMA_TO_DEVICE);
/* mgmt tasks do not need special cleanup */
if (!task->sc)
@@ -628,6 +638,59 @@ iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
iser_conn_terminate(ib_conn);
}
+static mode_t iser_attr_is_visible(int param_type, int param)
+{
+ switch (param_type) {
+ case ISCSI_HOST_PARAM:
+ switch (param) {
+ case ISCSI_HOST_PARAM_NETDEV_NAME:
+ case ISCSI_HOST_PARAM_HWADDRESS:
+ case ISCSI_HOST_PARAM_INITIATOR_NAME:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ case ISCSI_PARAM:
+ switch (param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ case ISCSI_PARAM_HDRDGST_EN:
+ case ISCSI_PARAM_DATADGST_EN:
+ case ISCSI_PARAM_CONN_ADDRESS:
+ case ISCSI_PARAM_CONN_PORT:
+ case ISCSI_PARAM_EXP_STATSN:
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ case ISCSI_PARAM_PING_TMO:
+ case ISCSI_PARAM_RECV_TMO:
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ case ISCSI_PARAM_MAX_R2T:
+ case ISCSI_PARAM_IMM_DATA_EN:
+ case ISCSI_PARAM_FIRST_BURST:
+ case ISCSI_PARAM_MAX_BURST:
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ case ISCSI_PARAM_TARGET_NAME:
+ case ISCSI_PARAM_TPGT:
+ case ISCSI_PARAM_USERNAME:
+ case ISCSI_PARAM_PASSWORD:
+ case ISCSI_PARAM_USERNAME_IN:
+ case ISCSI_PARAM_PASSWORD_IN:
+ case ISCSI_PARAM_FAST_ABORT:
+ case ISCSI_PARAM_ABORT_TMO:
+ case ISCSI_PARAM_LU_RESET_TMO:
+ case ISCSI_PARAM_TGT_RESET_TMO:
+ case ISCSI_PARAM_IFACE_NAME:
+ case ISCSI_PARAM_INITIATOR_NAME:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
static struct scsi_host_template iscsi_iser_sht = {
.module = THIS_MODULE,
.name = "iSCSI Initiator over iSER, v." DRV_VER,
@@ -649,32 +712,6 @@ static struct iscsi_transport iscsi_iser_transport = {
.owner = THIS_MODULE,
.name = "iser",
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T,
- .param_mask = ISCSI_MAX_RECV_DLENGTH |
- ISCSI_MAX_XMIT_DLENGTH |
- ISCSI_HDRDGST_EN |
- ISCSI_DATADGST_EN |
- ISCSI_INITIAL_R2T_EN |
- ISCSI_MAX_R2T |
- ISCSI_IMM_DATA_EN |
- ISCSI_FIRST_BURST |
- ISCSI_MAX_BURST |
- ISCSI_PDU_INORDER_EN |
- ISCSI_DATASEQ_INORDER_EN |
- ISCSI_CONN_PORT |
- ISCSI_CONN_ADDRESS |
- ISCSI_EXP_STATSN |
- ISCSI_PERSISTENT_PORT |
- ISCSI_PERSISTENT_ADDRESS |
- ISCSI_TARGET_NAME | ISCSI_TPGT |
- ISCSI_USERNAME | ISCSI_PASSWORD |
- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
- ISCSI_PING_TMO | ISCSI_RECV_TMO |
- ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
- .host_param_mask = ISCSI_HOST_HWADDRESS |
- ISCSI_HOST_NETDEV_NAME |
- ISCSI_HOST_INITIATOR_NAME,
/* session management */
.create_session = iscsi_iser_session_create,
.destroy_session = iscsi_iser_session_destroy,
@@ -682,6 +719,7 @@ static struct iscsi_transport iscsi_iser_transport = {
.create_conn = iscsi_iser_conn_create,
.bind_conn = iscsi_iser_conn_bind,
.destroy_conn = iscsi_iser_conn_destroy,
+ .attr_is_visible = iser_attr_is_visible,
.set_param = iscsi_iser_set_param,
.get_conn_param = iscsi_conn_get_param,
.get_ep_param = iscsi_iser_get_ep_param,
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 2f02ab0ccc1e..db7ea3704da7 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -45,6 +45,7 @@
#include <scsi/libiscsi.h>
#include <scsi/scsi_transport_iscsi.h>
+#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/list.h>
@@ -88,7 +89,7 @@
} while (0)
#define SHIFT_4K 12
-#define SIZE_4K (1UL << SHIFT_4K)
+#define SIZE_4K (1ULL << SHIFT_4K)
#define MASK_4K (~(SIZE_4K-1))
/* support up to 512KB in one RDMA */
@@ -256,7 +257,8 @@ struct iser_conn {
struct list_head conn_list; /* entry in ig conn list */
char *login_buf;
- u64 login_dma;
+ char *login_req_buf, *login_resp_buf;
+ u64 login_req_dma, login_resp_dma;
unsigned int rx_desc_head;
struct iser_rx_desc *rx_descs;
struct ib_recv_wr rx_wr[ISER_MIN_POSTED_RX];
@@ -276,7 +278,6 @@ struct iscsi_iser_task {
struct iser_regd_buf rdma_regd[ISER_DIRS_NUM];/* regd rdma buf */
struct iser_data_buf data[ISER_DIRS_NUM]; /* orig. data des*/
struct iser_data_buf data_copy[ISER_DIRS_NUM];/* contig. copy */
- int headers_initialized;
};
struct iser_page_vec {
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index 95a08a8ca8aa..a607542fc796 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -221,8 +221,14 @@ void iser_free_rx_descriptors(struct iser_conn *ib_conn)
struct iser_device *device = ib_conn->device;
if (ib_conn->login_buf) {
- ib_dma_unmap_single(device->ib_device, ib_conn->login_dma,
- ISER_RX_LOGIN_SIZE, DMA_FROM_DEVICE);
+ if (ib_conn->login_req_dma)
+ ib_dma_unmap_single(device->ib_device,
+ ib_conn->login_req_dma,
+ ISCSI_DEF_MAX_RECV_SEG_LEN, DMA_TO_DEVICE);
+ if (ib_conn->login_resp_dma)
+ ib_dma_unmap_single(device->ib_device,
+ ib_conn->login_resp_dma,
+ ISER_RX_LOGIN_SIZE, DMA_FROM_DEVICE);
kfree(ib_conn->login_buf);
}
@@ -271,7 +277,7 @@ int iser_send_command(struct iscsi_conn *conn,
unsigned long edtl;
int err;
struct iser_data_buf *data_buf;
- struct iscsi_cmd *hdr = (struct iscsi_cmd *)task->hdr;
+ struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)task->hdr;
struct scsi_cmnd *sc = task->sc;
struct iser_tx_desc *tx_desc = &iser_task->desc;
@@ -394,6 +400,7 @@ int iser_send_control(struct iscsi_conn *conn,
unsigned long data_seg_len;
int err = 0;
struct iser_device *device;
+ struct iser_conn *ib_conn = iser_conn->ib_conn;
/* build the tx desc regd header and add it to the tx desc dto */
mdesc->type = ISCSI_TX_CONTROL;
@@ -409,10 +416,20 @@ int iser_send_control(struct iscsi_conn *conn,
iser_err("data present on non login task!!!\n");
goto send_control_error;
}
- memcpy(iser_conn->ib_conn->login_buf, task->data,
+
+ ib_dma_sync_single_for_cpu(device->ib_device,
+ ib_conn->login_req_dma, task->data_count,
+ DMA_TO_DEVICE);
+
+ memcpy(iser_conn->ib_conn->login_req_buf, task->data,
task->data_count);
- tx_dsg->addr = iser_conn->ib_conn->login_dma;
- tx_dsg->length = data_seg_len;
+
+ ib_dma_sync_single_for_device(device->ib_device,
+ ib_conn->login_req_dma, task->data_count,
+ DMA_TO_DEVICE);
+
+ tx_dsg->addr = iser_conn->ib_conn->login_req_dma;
+ tx_dsg->length = task->data_count;
tx_dsg->lkey = device->mr->lkey;
mdesc->num_sge = 2;
}
@@ -445,8 +462,8 @@ void iser_rcv_completion(struct iser_rx_desc *rx_desc,
int rx_buflen, outstanding, count, err;
/* differentiate between login to all other PDUs */
- if ((char *)rx_desc == ib_conn->login_buf) {
- rx_dma = ib_conn->login_dma;
+ if ((char *)rx_desc == ib_conn->login_resp_buf) {
+ rx_dma = ib_conn->login_resp_dma;
rx_buflen = ISER_RX_LOGIN_SIZE;
} else {
rx_dma = rx_desc->dma_addr;
@@ -473,7 +490,7 @@ void iser_rcv_completion(struct iser_rx_desc *rx_desc,
* for the posted rx bufs refcount to become zero handles everything */
conn->ib_conn->post_recv_buf_count--;
- if (rx_dma == ib_conn->login_dma)
+ if (rx_dma == ib_conn->login_resp_dma)
return;
outstanding = ib_conn->post_recv_buf_count;
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index ede1475bee09..e28877c4ce15 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -155,20 +155,39 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn)
{
struct iser_device *device;
struct ib_qp_init_attr init_attr;
- int ret = -ENOMEM;
+ int req_err, resp_err, ret = -ENOMEM;
struct ib_fmr_pool_param params;
BUG_ON(ib_conn->device == NULL);
device = ib_conn->device;
- ib_conn->login_buf = kmalloc(ISER_RX_LOGIN_SIZE, GFP_KERNEL);
+ ib_conn->login_buf = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN +
+ ISER_RX_LOGIN_SIZE, GFP_KERNEL);
if (!ib_conn->login_buf)
goto out_err;
- ib_conn->login_dma = ib_dma_map_single(ib_conn->device->ib_device,
- (void *)ib_conn->login_buf, ISER_RX_LOGIN_SIZE,
- DMA_FROM_DEVICE);
+ ib_conn->login_req_buf = ib_conn->login_buf;
+ ib_conn->login_resp_buf = ib_conn->login_buf + ISCSI_DEF_MAX_RECV_SEG_LEN;
+
+ ib_conn->login_req_dma = ib_dma_map_single(ib_conn->device->ib_device,
+ (void *)ib_conn->login_req_buf,
+ ISCSI_DEF_MAX_RECV_SEG_LEN, DMA_TO_DEVICE);
+
+ ib_conn->login_resp_dma = ib_dma_map_single(ib_conn->device->ib_device,
+ (void *)ib_conn->login_resp_buf,
+ ISER_RX_LOGIN_SIZE, DMA_FROM_DEVICE);
+
+ req_err = ib_dma_mapping_error(device->ib_device, ib_conn->login_req_dma);
+ resp_err = ib_dma_mapping_error(device->ib_device, ib_conn->login_resp_dma);
+
+ if (req_err || resp_err) {
+ if (req_err)
+ ib_conn->login_req_dma = 0;
+ if (resp_err)
+ ib_conn->login_resp_dma = 0;
+ goto out_err;
+ }
ib_conn->page_vec = kmalloc(sizeof(struct iser_page_vec) +
(sizeof(u64) * (ISCSI_ISER_SG_TABLESIZE +1)),
@@ -658,11 +677,11 @@ int iser_post_recvl(struct iser_conn *ib_conn)
struct ib_sge sge;
int ib_ret;
- sge.addr = ib_conn->login_dma;
+ sge.addr = ib_conn->login_resp_dma;
sge.length = ISER_RX_LOGIN_SIZE;
sge.lkey = ib_conn->device->mr->lkey;
- rx_wr.wr_id = (unsigned long)ib_conn->login_buf;
+ rx_wr.wr_id = (unsigned long)ib_conn->login_resp_buf;
rx_wr.sg_list = &sge;
rx_wr.num_sge = 1;
rx_wr.next = NULL;
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index ee165fdcb596..0bfa545675b8 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -39,7 +39,7 @@
#include <linux/random.h>
#include <linux/jiffies.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -2127,6 +2127,8 @@ static ssize_t srp_create_target(struct device *dev,
return -ENOMEM;
target_host->transportt = ib_srp_transport_template;
+ target_host->max_channel = 0;
+ target_host->max_id = 1;
target_host->max_lun = SRP_MAX_LUN;
target_host->max_cmd_len = sizeof ((struct srp_cmd *) (void *) 0L)->cdb;
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 23e82e46656d..001b147c7f95 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -3,7 +3,7 @@
#
menu "Input device support"
- depends on !S390
+ depends on !S390 && !UML
config INPUT
tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index 3367f760d75a..480eb9d9876a 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -309,9 +309,10 @@ EXPORT_SYMBOL_GPL(input_ff_event);
* Once ff device is created you need to setup its upload, erase,
* playback and other handlers before registering input device
*/
-int input_ff_create(struct input_dev *dev, int max_effects)
+int input_ff_create(struct input_dev *dev, unsigned int max_effects)
{
struct ff_device *ff;
+ size_t ff_dev_size;
int i;
if (!max_effects) {
@@ -319,8 +320,12 @@ int input_ff_create(struct input_dev *dev, int max_effects)
return -EINVAL;
}
- ff = kzalloc(sizeof(struct ff_device) +
- max_effects * sizeof(struct file *), GFP_KERNEL);
+ ff_dev_size = sizeof(struct ff_device) +
+ max_effects * sizeof(struct file *);
+ if (ff_dev_size < max_effects) /* overflow */
+ return -EINVAL;
+
+ ff = kzalloc(ff_dev_size, GFP_KERNEL);
if (!ff)
return -ENOMEM;
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index 5b8f59d6c3e8..c351aa421f8f 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -47,7 +47,7 @@ static void gameport_disconnect_port(struct gameport *gameport);
#if defined(__i386__)
-#include <asm/i8253.h>
+#include <linux/i8253.h>
#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182/HZ:0))
#define GET_TIME(x) do { x = get_time_pit(); } while (0)
diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c
index 1accb89ae66f..e46a86776a6b 100644
--- a/drivers/input/input-compat.c
+++ b/drivers/input/input-compat.c
@@ -8,6 +8,7 @@
* the Free Software Foundation.
*/
+#include <linux/export.h>
#include <asm/uaccess.h>
#include "input-compat.h"
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index c48c81f0308d..f658086fbbe0 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -9,6 +9,7 @@
*/
#include <linux/input/mt.h>
+#include <linux/export.h>
#include <linux/slab.h>
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
@@ -117,6 +118,7 @@ void input_mt_report_finger_count(struct input_dev *dev, int count)
input_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, count == 2);
input_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, count == 3);
input_event(dev, EV_KEY, BTN_TOOL_QUADTAP, count == 4);
+ input_event(dev, EV_KEY, BTN_TOOL_QUINTTAP, count == 5);
}
EXPORT_SYMBOL(input_mt_report_finger_count);
diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c
index b1aabde87523..7dfe1009fae0 100644
--- a/drivers/input/input-polldev.c
+++ b/drivers/input/input-polldev.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
+#include <linux/module.h>
#include <linux/input-polldev.h>
MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
@@ -49,8 +50,10 @@ static int input_open_polled_device(struct input_dev *input)
dev->open(dev);
/* Only start polling if polling is enabled */
- if (dev->poll_interval > 0)
- queue_delayed_work(system_freezable_wq, &dev->work, 0);
+ if (dev->poll_interval > 0) {
+ dev->poll(dev);
+ input_polldev_queue_work(dev);
+ }
return 0;
}
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
index 4afe0a3b4884..358cd7ee905b 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -136,10 +136,10 @@ struct analog_port {
#ifdef __i386__
-#include <asm/i8253.h>
+#include <linux/i8253.h>
#define GET_TIME(x) do { if (cpu_has_tsc) rdtscl(x); else x = get_time_pit(); } while (0)
-#define DELTA(x,y) (cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? CLOCK_TICK_RATE / HZ : 0)))
+#define DELTA(x,y) (cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? PIT_TICK_RATE / HZ : 0)))
#define TIME_NAME (cpu_has_tsc?"TSC":"PIT")
static unsigned int get_time_pit(void)
{
diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c
index f6732b57ca07..6d6e7418dc21 100644
--- a/drivers/input/joystick/as5011.c
+++ b/drivers/input/joystick/as5011.c
@@ -30,6 +30,7 @@
#include <linux/delay.h>
#include <linux/input/as5011.h>
#include <linux/slab.h>
+#include <linux/module.h>
#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick"
#define MODULE_DEVICE_ALIAS "as5011"
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 56abf3d0e911..d72887585a14 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -154,10 +154,13 @@ static const struct xpad_device {
{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
{ 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX },
{ 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+ { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 },
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
{ 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
@@ -236,9 +239,10 @@ static struct usb_device_id xpad_table [] = {
XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */
XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */
+ XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */
XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */
- XPAD_XBOX360_VENDOR(0x1bad), /* Rock Band Drums */
+ XPAD_XBOX360_VENDOR(0x1bad), /* Harminix Rock Band Guitar and Drums */
XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */
{ }
};
@@ -545,7 +549,7 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
struct usb_endpoint_descriptor *ep_irq_out;
int error;
- if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX)
+ if (xpad->xtype == XTYPE_UNKNOWN)
return 0;
xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN,
@@ -579,13 +583,13 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
static void xpad_stop_output(struct usb_xpad *xpad)
{
- if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX)
+ if (xpad->xtype != XTYPE_UNKNOWN)
usb_kill_urb(xpad->irq_out);
}
static void xpad_deinit_output(struct usb_xpad *xpad)
{
- if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX) {
+ if (xpad->xtype != XTYPE_UNKNOWN) {
usb_free_urb(xpad->irq_out);
usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
xpad->odata, xpad->odata_dma);
@@ -632,6 +636,23 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+ case XTYPE_XBOX360W:
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x01;
+ xpad->odata[2] = 0x0F;
+ xpad->odata[3] = 0xC0;
+ xpad->odata[4] = 0x00;
+ xpad->odata[5] = strong / 256;
+ xpad->odata[6] = weak / 256;
+ xpad->odata[7] = 0x00;
+ xpad->odata[8] = 0x00;
+ xpad->odata[9] = 0x00;
+ xpad->odata[10] = 0x00;
+ xpad->odata[11] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 12;
+
+ return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+
default:
dbg("%s - rumble command sent to unsupported xpad type: %d",
__func__, xpad->xtype);
@@ -644,7 +665,7 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
static int xpad_init_ff(struct usb_xpad *xpad)
{
- if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX)
+ if (xpad->xtype == XTYPE_UNKNOWN)
return 0;
input_set_capability(xpad->dev, EV_FF, FF_RUMBLE);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index b4dee9d5a055..615c21f2a553 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -33,10 +33,10 @@ config KEYBOARD_ADP5588
module will be called adp5588-keys.
config KEYBOARD_ADP5589
- tristate "ADP5589 I2C QWERTY Keypad and IO Expander"
+ tristate "ADP5585/ADP5589 I2C QWERTY Keypad and IO Expander"
depends on I2C
help
- Say Y here if you want to use a ADP5589 attached to your
+ Say Y here if you want to use a ADP5585/ADP5589 attached to your
system I2C bus.
To compile this driver as a module, choose M here: the
diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
index af45d275f686..4a7f534cf64b 100644
--- a/drivers/input/keyboard/adp5588-keys.c
+++ b/drivers/input/keyboard/adp5588-keys.c
@@ -9,7 +9,6 @@
*/
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -551,7 +550,7 @@ static int __devinit adp5588_probe(struct i2c_client *client,
}
error = request_irq(client->irq, adp5588_irq,
- IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+ IRQF_TRIGGER_FALLING,
client->dev.driver->name, kpad);
if (error) {
dev_err(&client->dev, "irq %d busy?\n", client->irq);
@@ -669,4 +668,3 @@ module_exit(adp5588_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("ADP5588/87 Keypad driver");
-MODULE_ALIAS("platform:adp5588-keys");
diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
index 631598663aab..02b5d53031bf 100644
--- a/drivers/input/keyboard/adp5589-keys.c
+++ b/drivers/input/keyboard/adp5589-keys.c
@@ -1,5 +1,5 @@
/*
- * Description: keypad driver for ADP5589
+ * Description: keypad driver for ADP5589, ADP5585
* I2C QWERTY Keypad and IO Expander
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
@@ -8,7 +8,6 @@
*/
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -23,35 +22,165 @@
#include <linux/input/adp5589.h>
+/* ADP5589/ADP5585 Common Registers */
+#define ADP5589_5_ID 0x00
+#define ADP5589_5_INT_STATUS 0x01
+#define ADP5589_5_STATUS 0x02
+#define ADP5589_5_FIFO_1 0x03
+#define ADP5589_5_FIFO_2 0x04
+#define ADP5589_5_FIFO_3 0x05
+#define ADP5589_5_FIFO_4 0x06
+#define ADP5589_5_FIFO_5 0x07
+#define ADP5589_5_FIFO_6 0x08
+#define ADP5589_5_FIFO_7 0x09
+#define ADP5589_5_FIFO_8 0x0A
+#define ADP5589_5_FIFO_9 0x0B
+#define ADP5589_5_FIFO_10 0x0C
+#define ADP5589_5_FIFO_11 0x0D
+#define ADP5589_5_FIFO_12 0x0E
+#define ADP5589_5_FIFO_13 0x0F
+#define ADP5589_5_FIFO_14 0x10
+#define ADP5589_5_FIFO_15 0x11
+#define ADP5589_5_FIFO_16 0x12
+#define ADP5589_5_GPI_INT_STAT_A 0x13
+#define ADP5589_5_GPI_INT_STAT_B 0x14
+
+/* ADP5589 Registers */
+#define ADP5589_GPI_INT_STAT_C 0x15
+#define ADP5589_GPI_STATUS_A 0x16
+#define ADP5589_GPI_STATUS_B 0x17
+#define ADP5589_GPI_STATUS_C 0x18
+#define ADP5589_RPULL_CONFIG_A 0x19
+#define ADP5589_RPULL_CONFIG_B 0x1A
+#define ADP5589_RPULL_CONFIG_C 0x1B
+#define ADP5589_RPULL_CONFIG_D 0x1C
+#define ADP5589_RPULL_CONFIG_E 0x1D
+#define ADP5589_GPI_INT_LEVEL_A 0x1E
+#define ADP5589_GPI_INT_LEVEL_B 0x1F
+#define ADP5589_GPI_INT_LEVEL_C 0x20
+#define ADP5589_GPI_EVENT_EN_A 0x21
+#define ADP5589_GPI_EVENT_EN_B 0x22
+#define ADP5589_GPI_EVENT_EN_C 0x23
+#define ADP5589_GPI_INTERRUPT_EN_A 0x24
+#define ADP5589_GPI_INTERRUPT_EN_B 0x25
+#define ADP5589_GPI_INTERRUPT_EN_C 0x26
+#define ADP5589_DEBOUNCE_DIS_A 0x27
+#define ADP5589_DEBOUNCE_DIS_B 0x28
+#define ADP5589_DEBOUNCE_DIS_C 0x29
+#define ADP5589_GPO_DATA_OUT_A 0x2A
+#define ADP5589_GPO_DATA_OUT_B 0x2B
+#define ADP5589_GPO_DATA_OUT_C 0x2C
+#define ADP5589_GPO_OUT_MODE_A 0x2D
+#define ADP5589_GPO_OUT_MODE_B 0x2E
+#define ADP5589_GPO_OUT_MODE_C 0x2F
+#define ADP5589_GPIO_DIRECTION_A 0x30
+#define ADP5589_GPIO_DIRECTION_B 0x31
+#define ADP5589_GPIO_DIRECTION_C 0x32
+#define ADP5589_UNLOCK1 0x33
+#define ADP5589_UNLOCK2 0x34
+#define ADP5589_EXT_LOCK_EVENT 0x35
+#define ADP5589_UNLOCK_TIMERS 0x36
+#define ADP5589_LOCK_CFG 0x37
+#define ADP5589_RESET1_EVENT_A 0x38
+#define ADP5589_RESET1_EVENT_B 0x39
+#define ADP5589_RESET1_EVENT_C 0x3A
+#define ADP5589_RESET2_EVENT_A 0x3B
+#define ADP5589_RESET2_EVENT_B 0x3C
+#define ADP5589_RESET_CFG 0x3D
+#define ADP5589_PWM_OFFT_LOW 0x3E
+#define ADP5589_PWM_OFFT_HIGH 0x3F
+#define ADP5589_PWM_ONT_LOW 0x40
+#define ADP5589_PWM_ONT_HIGH 0x41
+#define ADP5589_PWM_CFG 0x42
+#define ADP5589_CLOCK_DIV_CFG 0x43
+#define ADP5589_LOGIC_1_CFG 0x44
+#define ADP5589_LOGIC_2_CFG 0x45
+#define ADP5589_LOGIC_FF_CFG 0x46
+#define ADP5589_LOGIC_INT_EVENT_EN 0x47
+#define ADP5589_POLL_PTIME_CFG 0x48
+#define ADP5589_PIN_CONFIG_A 0x49
+#define ADP5589_PIN_CONFIG_B 0x4A
+#define ADP5589_PIN_CONFIG_C 0x4B
+#define ADP5589_PIN_CONFIG_D 0x4C
+#define ADP5589_GENERAL_CFG 0x4D
+#define ADP5589_INT_EN 0x4E
+
+/* ADP5585 Registers */
+#define ADP5585_GPI_STATUS_A 0x15
+#define ADP5585_GPI_STATUS_B 0x16
+#define ADP5585_RPULL_CONFIG_A 0x17
+#define ADP5585_RPULL_CONFIG_B 0x18
+#define ADP5585_RPULL_CONFIG_C 0x19
+#define ADP5585_RPULL_CONFIG_D 0x1A
+#define ADP5585_GPI_INT_LEVEL_A 0x1B
+#define ADP5585_GPI_INT_LEVEL_B 0x1C
+#define ADP5585_GPI_EVENT_EN_A 0x1D
+#define ADP5585_GPI_EVENT_EN_B 0x1E
+#define ADP5585_GPI_INTERRUPT_EN_A 0x1F
+#define ADP5585_GPI_INTERRUPT_EN_B 0x20
+#define ADP5585_DEBOUNCE_DIS_A 0x21
+#define ADP5585_DEBOUNCE_DIS_B 0x22
+#define ADP5585_GPO_DATA_OUT_A 0x23
+#define ADP5585_GPO_DATA_OUT_B 0x24
+#define ADP5585_GPO_OUT_MODE_A 0x25
+#define ADP5585_GPO_OUT_MODE_B 0x26
+#define ADP5585_GPIO_DIRECTION_A 0x27
+#define ADP5585_GPIO_DIRECTION_B 0x28
+#define ADP5585_RESET1_EVENT_A 0x29
+#define ADP5585_RESET1_EVENT_B 0x2A
+#define ADP5585_RESET1_EVENT_C 0x2B
+#define ADP5585_RESET2_EVENT_A 0x2C
+#define ADP5585_RESET2_EVENT_B 0x2D
+#define ADP5585_RESET_CFG 0x2E
+#define ADP5585_PWM_OFFT_LOW 0x2F
+#define ADP5585_PWM_OFFT_HIGH 0x30
+#define ADP5585_PWM_ONT_LOW 0x31
+#define ADP5585_PWM_ONT_HIGH 0x32
+#define ADP5585_PWM_CFG 0x33
+#define ADP5585_LOGIC_CFG 0x34
+#define ADP5585_LOGIC_FF_CFG 0x35
+#define ADP5585_LOGIC_INT_EVENT_EN 0x36
+#define ADP5585_POLL_PTIME_CFG 0x37
+#define ADP5585_PIN_CONFIG_A 0x38
+#define ADP5585_PIN_CONFIG_B 0x39
+#define ADP5585_PIN_CONFIG_D 0x3A
+#define ADP5585_GENERAL_CFG 0x3B
+#define ADP5585_INT_EN 0x3C
+
+/* ID Register */
+#define ADP5589_5_DEVICE_ID_MASK 0xF
+#define ADP5589_5_MAN_ID_MASK 0xF
+#define ADP5589_5_MAN_ID_SHIFT 4
+#define ADP5589_5_MAN_ID 0x02
+
/* GENERAL_CFG Register */
#define OSC_EN (1 << 7)
#define CORE_CLK(x) (((x) & 0x3) << 5)
-#define LCK_TRK_LOGIC (1 << 4)
-#define LCK_TRK_GPI (1 << 3)
+#define LCK_TRK_LOGIC (1 << 4) /* ADP5589 only */
+#define LCK_TRK_GPI (1 << 3) /* ADP5589 only */
#define INT_CFG (1 << 1)
#define RST_CFG (1 << 0)
/* INT_EN Register */
-#define LOGIC2_IEN (1 << 5)
+#define LOGIC2_IEN (1 << 5) /* ADP5589 only */
#define LOGIC1_IEN (1 << 4)
-#define LOCK_IEN (1 << 3)
+#define LOCK_IEN (1 << 3) /* ADP5589 only */
#define OVRFLOW_IEN (1 << 2)
#define GPI_IEN (1 << 1)
#define EVENT_IEN (1 << 0)
/* Interrupt Status Register */
-#define LOGIC2_INT (1 << 5)
+#define LOGIC2_INT (1 << 5) /* ADP5589 only */
#define LOGIC1_INT (1 << 4)
-#define LOCK_INT (1 << 3)
+#define LOCK_INT (1 << 3) /* ADP5589 only */
#define OVRFLOW_INT (1 << 2)
#define GPI_INT (1 << 1)
#define EVENT_INT (1 << 0)
/* STATUS Register */
-
-#define LOGIC2_STAT (1 << 7)
+#define LOGIC2_STAT (1 << 7) /* ADP5589 only */
#define LOGIC1_STAT (1 << 6)
-#define LOCK_STAT (1 << 5)
+#define LOCK_STAT (1 << 5) /* ADP5589 only */
#define KEC 0xF
/* PIN_CONFIG_D Register */
@@ -62,27 +191,54 @@
#define LOCK_EN (1 << 0)
#define PTIME_MASK 0x3
-#define LTIME_MASK 0x3
+#define LTIME_MASK 0x3 /* ADP5589 only */
/* Key Event Register xy */
#define KEY_EV_PRESSED (1 << 7)
#define KEY_EV_MASK (0x7F)
#define KEYP_MAX_EVENT 16
+#define ADP5589_MAXGPIO 19
+#define ADP5585_MAXGPIO 11 /* 10 on the ADP5585-01, 11 on ADP5585-02 */
-#define MAXGPIO 19
-#define ADP_BANK(offs) ((offs) >> 3)
-#define ADP_BIT(offs) (1u << ((offs) & 0x7))
+enum {
+ ADP5589,
+ ADP5585_01,
+ ADP5585_02
+};
+
+struct adp_constants {
+ u8 maxgpio;
+ u8 keymapsize;
+ u8 gpi_pin_row_base;
+ u8 gpi_pin_row_end;
+ u8 gpi_pin_col_base;
+ u8 gpi_pin_base;
+ u8 gpi_pin_end;
+ u8 gpimapsize_max;
+ u8 max_row_num;
+ u8 max_col_num;
+ u8 row_mask;
+ u8 col_mask;
+ u8 col_shift;
+ u8 c4_extend_cfg;
+ u8 (*bank) (u8 offset);
+ u8 (*bit) (u8 offset);
+ u8 (*reg) (u8 reg);
+};
struct adp5589_kpad {
struct i2c_client *client;
struct input_dev *input;
+ const struct adp_constants *var;
unsigned short keycode[ADP5589_KEYMAPSIZE];
const struct adp5589_gpi_map *gpimap;
unsigned short gpimapsize;
unsigned extend_cfg;
+ bool is_adp5585;
+ bool adp5585_support_row5;
#ifdef CONFIG_GPIOLIB
- unsigned char gpiomap[MAXGPIO];
+ unsigned char gpiomap[ADP5589_MAXGPIO];
bool export_gpio;
struct gpio_chip gc;
struct mutex gpio_lock; /* Protect cached dir, dat_out */
@@ -91,6 +247,129 @@ struct adp5589_kpad {
#endif
};
+/*
+ * ADP5589 / ADP5585 derivative / variant handling
+ */
+
+
+/* ADP5589 */
+
+static unsigned char adp5589_bank(unsigned char offset)
+{
+ return offset >> 3;
+}
+
+static unsigned char adp5589_bit(unsigned char offset)
+{
+ return 1u << (offset & 0x7);
+}
+
+static unsigned char adp5589_reg(unsigned char reg)
+{
+ return reg;
+}
+
+static const struct adp_constants const_adp5589 = {
+ .maxgpio = ADP5589_MAXGPIO,
+ .keymapsize = ADP5589_KEYMAPSIZE,
+ .gpi_pin_row_base = ADP5589_GPI_PIN_ROW_BASE,
+ .gpi_pin_row_end = ADP5589_GPI_PIN_ROW_END,
+ .gpi_pin_col_base = ADP5589_GPI_PIN_COL_BASE,
+ .gpi_pin_base = ADP5589_GPI_PIN_BASE,
+ .gpi_pin_end = ADP5589_GPI_PIN_END,
+ .gpimapsize_max = ADP5589_GPIMAPSIZE_MAX,
+ .c4_extend_cfg = 12,
+ .max_row_num = ADP5589_MAX_ROW_NUM,
+ .max_col_num = ADP5589_MAX_COL_NUM,
+ .row_mask = ADP5589_ROW_MASK,
+ .col_mask = ADP5589_COL_MASK,
+ .col_shift = ADP5589_COL_SHIFT,
+ .bank = adp5589_bank,
+ .bit = adp5589_bit,
+ .reg = adp5589_reg,
+};
+
+/* ADP5585 */
+
+static unsigned char adp5585_bank(unsigned char offset)
+{
+ return offset > ADP5585_MAX_ROW_NUM;
+}
+
+static unsigned char adp5585_bit(unsigned char offset)
+{
+ return (offset > ADP5585_MAX_ROW_NUM) ?
+ 1u << (offset - ADP5585_COL_SHIFT) : 1u << offset;
+}
+
+static const unsigned char adp5585_reg_lut[] = {
+ [ADP5589_GPI_STATUS_A] = ADP5585_GPI_STATUS_A,
+ [ADP5589_GPI_STATUS_B] = ADP5585_GPI_STATUS_B,
+ [ADP5589_RPULL_CONFIG_A] = ADP5585_RPULL_CONFIG_A,
+ [ADP5589_RPULL_CONFIG_B] = ADP5585_RPULL_CONFIG_B,
+ [ADP5589_RPULL_CONFIG_C] = ADP5585_RPULL_CONFIG_C,
+ [ADP5589_RPULL_CONFIG_D] = ADP5585_RPULL_CONFIG_D,
+ [ADP5589_GPI_INT_LEVEL_A] = ADP5585_GPI_INT_LEVEL_A,
+ [ADP5589_GPI_INT_LEVEL_B] = ADP5585_GPI_INT_LEVEL_B,
+ [ADP5589_GPI_EVENT_EN_A] = ADP5585_GPI_EVENT_EN_A,
+ [ADP5589_GPI_EVENT_EN_B] = ADP5585_GPI_EVENT_EN_B,
+ [ADP5589_GPI_INTERRUPT_EN_A] = ADP5585_GPI_INTERRUPT_EN_A,
+ [ADP5589_GPI_INTERRUPT_EN_B] = ADP5585_GPI_INTERRUPT_EN_B,
+ [ADP5589_DEBOUNCE_DIS_A] = ADP5585_DEBOUNCE_DIS_A,
+ [ADP5589_DEBOUNCE_DIS_B] = ADP5585_DEBOUNCE_DIS_B,
+ [ADP5589_GPO_DATA_OUT_A] = ADP5585_GPO_DATA_OUT_A,
+ [ADP5589_GPO_DATA_OUT_B] = ADP5585_GPO_DATA_OUT_B,
+ [ADP5589_GPO_OUT_MODE_A] = ADP5585_GPO_OUT_MODE_A,
+ [ADP5589_GPO_OUT_MODE_B] = ADP5585_GPO_OUT_MODE_B,
+ [ADP5589_GPIO_DIRECTION_A] = ADP5585_GPIO_DIRECTION_A,
+ [ADP5589_GPIO_DIRECTION_B] = ADP5585_GPIO_DIRECTION_B,
+ [ADP5589_RESET1_EVENT_A] = ADP5585_RESET1_EVENT_A,
+ [ADP5589_RESET1_EVENT_B] = ADP5585_RESET1_EVENT_B,
+ [ADP5589_RESET1_EVENT_C] = ADP5585_RESET1_EVENT_C,
+ [ADP5589_RESET2_EVENT_A] = ADP5585_RESET2_EVENT_A,
+ [ADP5589_RESET2_EVENT_B] = ADP5585_RESET2_EVENT_B,
+ [ADP5589_RESET_CFG] = ADP5585_RESET_CFG,
+ [ADP5589_PWM_OFFT_LOW] = ADP5585_PWM_OFFT_LOW,
+ [ADP5589_PWM_OFFT_HIGH] = ADP5585_PWM_OFFT_HIGH,
+ [ADP5589_PWM_ONT_LOW] = ADP5585_PWM_ONT_LOW,
+ [ADP5589_PWM_ONT_HIGH] = ADP5585_PWM_ONT_HIGH,
+ [ADP5589_PWM_CFG] = ADP5585_PWM_CFG,
+ [ADP5589_LOGIC_1_CFG] = ADP5585_LOGIC_CFG,
+ [ADP5589_LOGIC_FF_CFG] = ADP5585_LOGIC_FF_CFG,
+ [ADP5589_LOGIC_INT_EVENT_EN] = ADP5585_LOGIC_INT_EVENT_EN,
+ [ADP5589_POLL_PTIME_CFG] = ADP5585_POLL_PTIME_CFG,
+ [ADP5589_PIN_CONFIG_A] = ADP5585_PIN_CONFIG_A,
+ [ADP5589_PIN_CONFIG_B] = ADP5585_PIN_CONFIG_B,
+ [ADP5589_PIN_CONFIG_D] = ADP5585_PIN_CONFIG_D,
+ [ADP5589_GENERAL_CFG] = ADP5585_GENERAL_CFG,
+ [ADP5589_INT_EN] = ADP5585_INT_EN,
+};
+
+static unsigned char adp5585_reg(unsigned char reg)
+{
+ return adp5585_reg_lut[reg];
+}
+
+static const struct adp_constants const_adp5585 = {
+ .maxgpio = ADP5585_MAXGPIO,
+ .keymapsize = ADP5585_KEYMAPSIZE,
+ .gpi_pin_row_base = ADP5585_GPI_PIN_ROW_BASE,
+ .gpi_pin_row_end = ADP5585_GPI_PIN_ROW_END,
+ .gpi_pin_col_base = ADP5585_GPI_PIN_COL_BASE,
+ .gpi_pin_base = ADP5585_GPI_PIN_BASE,
+ .gpi_pin_end = ADP5585_GPI_PIN_END,
+ .gpimapsize_max = ADP5585_GPIMAPSIZE_MAX,
+ .c4_extend_cfg = 10,
+ .max_row_num = ADP5585_MAX_ROW_NUM,
+ .max_col_num = ADP5585_MAX_COL_NUM,
+ .row_mask = ADP5585_ROW_MASK,
+ .col_mask = ADP5585_COL_MASK,
+ .col_shift = ADP5585_COL_SHIFT,
+ .bank = adp5585_bank,
+ .bit = adp5585_bit,
+ .reg = adp5585_reg,
+};
+
static int adp5589_read(struct i2c_client *client, u8 reg)
{
int ret = i2c_smbus_read_byte_data(client, reg);
@@ -110,19 +389,20 @@ static int adp5589_write(struct i2c_client *client, u8 reg, u8 val)
static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off)
{
struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
- unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
- unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
+ unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
+ unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
- return !!(adp5589_read(kpad->client, ADP5589_GPI_STATUS_A + bank) &
- bit);
+ return !!(adp5589_read(kpad->client,
+ kpad->var->reg(ADP5589_GPI_STATUS_A) + bank) &
+ bit);
}
static void adp5589_gpio_set_value(struct gpio_chip *chip,
unsigned off, int val)
{
struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
- unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
- unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
+ unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
+ unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
mutex_lock(&kpad->gpio_lock);
@@ -131,8 +411,8 @@ static void adp5589_gpio_set_value(struct gpio_chip *chip,
else
kpad->dat_out[bank] &= ~bit;
- adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank,
- kpad->dat_out[bank]);
+ adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) +
+ bank, kpad->dat_out[bank]);
mutex_unlock(&kpad->gpio_lock);
}
@@ -140,14 +420,15 @@ static void adp5589_gpio_set_value(struct gpio_chip *chip,
static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off)
{
struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
- unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
- unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
+ unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
+ unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
int ret;
mutex_lock(&kpad->gpio_lock);
kpad->dir[bank] &= ~bit;
- ret = adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank,
+ ret = adp5589_write(kpad->client,
+ kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank,
kpad->dir[bank]);
mutex_unlock(&kpad->gpio_lock);
@@ -159,8 +440,8 @@ static int adp5589_gpio_direction_output(struct gpio_chip *chip,
unsigned off, int val)
{
struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
- unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
- unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
+ unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
+ unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
int ret;
mutex_lock(&kpad->gpio_lock);
@@ -172,9 +453,10 @@ static int adp5589_gpio_direction_output(struct gpio_chip *chip,
else
kpad->dat_out[bank] &= ~bit;
- ret = adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank,
- kpad->dat_out[bank]);
- ret |= adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank,
+ ret = adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A)
+ + bank, kpad->dat_out[bank]);
+ ret |= adp5589_write(kpad->client,
+ kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank,
kpad->dir[bank]);
mutex_unlock(&kpad->gpio_lock);
@@ -185,26 +467,29 @@ static int adp5589_gpio_direction_output(struct gpio_chip *chip,
static int __devinit adp5589_build_gpiomap(struct adp5589_kpad *kpad,
const struct adp5589_kpad_platform_data *pdata)
{
- bool pin_used[MAXGPIO];
+ bool pin_used[ADP5589_MAXGPIO];
int n_unused = 0;
int i;
memset(pin_used, false, sizeof(pin_used));
- for (i = 0; i < MAXGPIO; i++)
+ for (i = 0; i < kpad->var->maxgpio; i++)
if (pdata->keypad_en_mask & (1 << i))
pin_used[i] = true;
for (i = 0; i < kpad->gpimapsize; i++)
- pin_used[kpad->gpimap[i].pin - ADP5589_GPI_PIN_BASE] = true;
+ pin_used[kpad->gpimap[i].pin - kpad->var->gpi_pin_base] = true;
if (kpad->extend_cfg & R4_EXTEND_CFG)
pin_used[4] = true;
if (kpad->extend_cfg & C4_EXTEND_CFG)
- pin_used[12] = true;
+ pin_used[kpad->var->c4_extend_cfg] = true;
+
+ if (!kpad->adp5585_support_row5)
+ pin_used[5] = true;
- for (i = 0; i < MAXGPIO; i++)
+ for (i = 0; i < kpad->var->maxgpio; i++)
if (!pin_used[i])
kpad->gpiomap[n_unused++] = i;
@@ -247,11 +532,11 @@ static int __devinit adp5589_gpio_add(struct adp5589_kpad *kpad)
return error;
}
- for (i = 0; i <= ADP_BANK(MAXGPIO); i++) {
- kpad->dat_out[i] = adp5589_read(kpad->client,
- ADP5589_GPO_DATA_OUT_A + i);
- kpad->dir[i] = adp5589_read(kpad->client,
- ADP5589_GPIO_DIRECTION_A + i);
+ for (i = 0; i <= kpad->var->bank(kpad->var->maxgpio); i++) {
+ kpad->dat_out[i] = adp5589_read(kpad->client, kpad->var->reg(
+ ADP5589_GPO_DATA_OUT_A) + i);
+ kpad->dir[i] = adp5589_read(kpad->client, kpad->var->reg(
+ ADP5589_GPIO_DIRECTION_A) + i);
}
if (gpio_data->setup) {
@@ -318,11 +603,11 @@ static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt)
int i;
for (i = 0; i < ev_cnt; i++) {
- int key = adp5589_read(kpad->client, ADP5589_FIFO_1 + i);
+ int key = adp5589_read(kpad->client, ADP5589_5_FIFO_1 + i);
int key_val = key & KEY_EV_MASK;
- if (key_val >= ADP5589_GPI_PIN_BASE &&
- key_val <= ADP5589_GPI_PIN_END) {
+ if (key_val >= kpad->var->gpi_pin_base &&
+ key_val <= kpad->var->gpi_pin_end) {
adp5589_report_switches(kpad, key, key_val);
} else {
input_report_key(kpad->input,
@@ -338,29 +623,30 @@ static irqreturn_t adp5589_irq(int irq, void *handle)
struct i2c_client *client = kpad->client;
int status, ev_cnt;
- status = adp5589_read(client, ADP5589_INT_STATUS);
+ status = adp5589_read(client, ADP5589_5_INT_STATUS);
if (status & OVRFLOW_INT) /* Unlikely and should never happen */
dev_err(&client->dev, "Event Overflow Error\n");
if (status & EVENT_INT) {
- ev_cnt = adp5589_read(client, ADP5589_STATUS) & KEC;
+ ev_cnt = adp5589_read(client, ADP5589_5_STATUS) & KEC;
if (ev_cnt) {
adp5589_report_events(kpad, ev_cnt);
input_sync(kpad->input);
}
}
- adp5589_write(client, ADP5589_INT_STATUS, status); /* Status is W1C */
+ adp5589_write(client, ADP5589_5_INT_STATUS, status); /* Status is W1C */
return IRQ_HANDLED;
}
-static int __devinit adp5589_get_evcode(struct adp5589_kpad *kpad, unsigned short key)
+static int __devinit adp5589_get_evcode(struct adp5589_kpad *kpad,
+ unsigned short key)
{
int i;
- for (i = 0; i < ADP5589_KEYMAPSIZE; i++)
+ for (i = 0; i < kpad->var->keymapsize; i++)
if (key == kpad->keycode[i])
return (i + 1) | KEY_EV_PRESSED;
@@ -373,19 +659,23 @@ static int __devinit adp5589_setup(struct adp5589_kpad *kpad)
{
struct i2c_client *client = kpad->client;
const struct adp5589_kpad_platform_data *pdata =
- client->dev.platform_data;
- int i, ret;
+ client->dev.platform_data;
+ u8 (*reg) (u8) = kpad->var->reg;
unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;
unsigned char pull_mask = 0;
+ int i, ret;
+
+ ret = adp5589_write(client, reg(ADP5589_PIN_CONFIG_A),
+ pdata->keypad_en_mask & kpad->var->row_mask);
+ ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_B),
+ (pdata->keypad_en_mask >> kpad->var->col_shift) &
+ kpad->var->col_mask);
- ret = adp5589_write(client, ADP5589_PIN_CONFIG_A,
- pdata->keypad_en_mask & 0xFF);
- ret |= adp5589_write(client, ADP5589_PIN_CONFIG_B,
- (pdata->keypad_en_mask >> 8) & 0xFF);
- ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C,
- (pdata->keypad_en_mask >> 16) & 0xFF);
+ if (!kpad->is_adp5585)
+ ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C,
+ (pdata->keypad_en_mask >> 16) & 0xFF);
- if (pdata->en_keylock) {
+ if (!kpad->is_adp5585 && pdata->en_keylock) {
ret |= adp5589_write(client, ADP5589_UNLOCK1,
pdata->unlock_key1);
ret |= adp5589_write(client, ADP5589_UNLOCK2,
@@ -396,96 +686,130 @@ static int __devinit adp5589_setup(struct adp5589_kpad *kpad)
}
for (i = 0; i < KEYP_MAX_EVENT; i++)
- ret |= adp5589_read(client, ADP5589_FIFO_1 + i);
+ ret |= adp5589_read(client, ADP5589_5_FIFO_1 + i);
for (i = 0; i < pdata->gpimapsize; i++) {
unsigned short pin = pdata->gpimap[i].pin;
- if (pin <= ADP5589_GPI_PIN_ROW_END) {
- evt_mode1 |= (1 << (pin - ADP5589_GPI_PIN_ROW_BASE));
+ if (pin <= kpad->var->gpi_pin_row_end) {
+ evt_mode1 |= (1 << (pin - kpad->var->gpi_pin_row_base));
} else {
evt_mode2 |=
- ((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) & 0xFF);
- evt_mode3 |=
- ((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) >> 8);
+ ((1 << (pin - kpad->var->gpi_pin_col_base)) & 0xFF);
+ if (!kpad->is_adp5585)
+ evt_mode3 |= ((1 << (pin -
+ kpad->var->gpi_pin_col_base)) >> 8);
}
}
if (pdata->gpimapsize) {
- ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_A, evt_mode1);
- ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_B, evt_mode2);
- ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_C, evt_mode3);
+ ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_A),
+ evt_mode1);
+ ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_B),
+ evt_mode2);
+ if (!kpad->is_adp5585)
+ ret |= adp5589_write(client,
+ reg(ADP5589_GPI_EVENT_EN_C),
+ evt_mode3);
}
if (pdata->pull_dis_mask & pdata->pullup_en_100k &
- pdata->pullup_en_300k & pdata->pulldown_en_300k)
+ pdata->pullup_en_300k & pdata->pulldown_en_300k)
dev_warn(&client->dev, "Conflicting pull resistor config\n");
- for (i = 0; i < MAXGPIO; i++) {
- unsigned val = 0;
+ for (i = 0; i <= kpad->var->max_row_num; i++) {
+ unsigned val = 0, bit = (1 << i);
+ if (pdata->pullup_en_300k & bit)
+ val = 0;
+ else if (pdata->pulldown_en_300k & bit)
+ val = 1;
+ else if (pdata->pullup_en_100k & bit)
+ val = 2;
+ else if (pdata->pull_dis_mask & bit)
+ val = 3;
+
+ pull_mask |= val << (2 * (i & 0x3));
+
+ if (i == 3 || i == kpad->var->max_row_num) {
+ ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A)
+ + (i >> 2), pull_mask);
+ pull_mask = 0;
+ }
+ }
- if (pdata->pullup_en_300k & (1 << i))
+ for (i = 0; i <= kpad->var->max_col_num; i++) {
+ unsigned val = 0, bit = 1 << (i + kpad->var->col_shift);
+ if (pdata->pullup_en_300k & bit)
val = 0;
- else if (pdata->pulldown_en_300k & (1 << i))
+ else if (pdata->pulldown_en_300k & bit)
val = 1;
- else if (pdata->pullup_en_100k & (1 << i))
+ else if (pdata->pullup_en_100k & bit)
val = 2;
- else if (pdata->pull_dis_mask & (1 << i))
+ else if (pdata->pull_dis_mask & bit)
val = 3;
pull_mask |= val << (2 * (i & 0x3));
- if ((i & 0x3) == 0x3 || i == MAXGPIO - 1) {
+ if (i == 3 || i == kpad->var->max_col_num) {
ret |= adp5589_write(client,
- ADP5589_RPULL_CONFIG_A + (i >> 2),
- pull_mask);
+ reg(ADP5585_RPULL_CONFIG_C) +
+ (i >> 2), pull_mask);
pull_mask = 0;
}
}
if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) {
- ret |= adp5589_write(client, ADP5589_RESET1_EVENT_A,
+ ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_A),
adp5589_get_evcode(kpad,
pdata->reset1_key_1));
- ret |= adp5589_write(client, ADP5589_RESET1_EVENT_B,
+ ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_B),
adp5589_get_evcode(kpad,
pdata->reset1_key_2));
- ret |= adp5589_write(client, ADP5589_RESET1_EVENT_C,
+ ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_C),
adp5589_get_evcode(kpad,
pdata->reset1_key_3));
kpad->extend_cfg |= R4_EXTEND_CFG;
}
if (pdata->reset2_key_1 && pdata->reset2_key_2) {
- ret |= adp5589_write(client, ADP5589_RESET2_EVENT_A,
+ ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_A),
adp5589_get_evcode(kpad,
pdata->reset2_key_1));
- ret |= adp5589_write(client, ADP5589_RESET2_EVENT_B,
+ ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_B),
adp5589_get_evcode(kpad,
pdata->reset2_key_2));
kpad->extend_cfg |= C4_EXTEND_CFG;
}
if (kpad->extend_cfg) {
- ret |= adp5589_write(client, ADP5589_RESET_CFG,
+ ret |= adp5589_write(client, reg(ADP5589_RESET_CFG),
pdata->reset_cfg);
- ret |= adp5589_write(client, ADP5589_PIN_CONFIG_D,
+ ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_D),
kpad->extend_cfg);
}
- for (i = 0; i <= ADP_BANK(MAXGPIO); i++)
- ret |= adp5589_write(client, ADP5589_DEBOUNCE_DIS_A + i,
- pdata->debounce_dis_mask >> (i * 8));
+ ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_A),
+ pdata->debounce_dis_mask & kpad->var->row_mask);
- ret |= adp5589_write(client, ADP5589_POLL_PTIME_CFG,
+ ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_B),
+ (pdata->debounce_dis_mask >> kpad->var->col_shift)
+ & kpad->var->col_mask);
+
+ if (!kpad->is_adp5585)
+ ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_C),
+ (pdata->debounce_dis_mask >> 16) & 0xFF);
+
+ ret |= adp5589_write(client, reg(ADP5589_POLL_PTIME_CFG),
pdata->scan_cycle_time & PTIME_MASK);
- ret |= adp5589_write(client, ADP5589_INT_STATUS, LOGIC2_INT |
- LOGIC1_INT | OVRFLOW_INT | LOCK_INT |
+ ret |= adp5589_write(client, ADP5589_5_INT_STATUS,
+ (kpad->is_adp5585 ? 0 : LOGIC2_INT) |
+ LOGIC1_INT | OVRFLOW_INT |
+ (kpad->is_adp5585 ? 0 : LOCK_INT) |
GPI_INT | EVENT_INT); /* Status is W1C */
- ret |= adp5589_write(client, ADP5589_GENERAL_CFG,
+ ret |= adp5589_write(client, reg(ADP5589_GENERAL_CFG),
INT_CFG | OSC_EN | CORE_CLK(3));
- ret |= adp5589_write(client, ADP5589_INT_EN,
+ ret |= adp5589_write(client, reg(ADP5589_INT_EN),
OVRFLOW_IEN | GPI_IEN | EVENT_IEN);
if (ret < 0) {
@@ -498,30 +822,33 @@ static int __devinit adp5589_setup(struct adp5589_kpad *kpad)
static void __devinit adp5589_report_switch_state(struct adp5589_kpad *kpad)
{
- int gpi_stat1 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_A);
- int gpi_stat2 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_B);
- int gpi_stat3 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_C);
int gpi_stat_tmp, pin_loc;
int i;
+ int gpi_stat1 = adp5589_read(kpad->client,
+ kpad->var->reg(ADP5589_GPI_STATUS_A));
+ int gpi_stat2 = adp5589_read(kpad->client,
+ kpad->var->reg(ADP5589_GPI_STATUS_B));
+ int gpi_stat3 = !kpad->is_adp5585 ?
+ adp5589_read(kpad->client, ADP5589_GPI_STATUS_C) : 0;
for (i = 0; i < kpad->gpimapsize; i++) {
unsigned short pin = kpad->gpimap[i].pin;
- if (pin <= ADP5589_GPI_PIN_ROW_END) {
+ if (pin <= kpad->var->gpi_pin_row_end) {
gpi_stat_tmp = gpi_stat1;
- pin_loc = pin - ADP5589_GPI_PIN_ROW_BASE;
- } else if ((pin - ADP5589_GPI_PIN_COL_BASE) < 8) {
+ pin_loc = pin - kpad->var->gpi_pin_row_base;
+ } else if ((pin - kpad->var->gpi_pin_col_base) < 8) {
gpi_stat_tmp = gpi_stat2;
- pin_loc = pin - ADP5589_GPI_PIN_COL_BASE;
+ pin_loc = pin - kpad->var->gpi_pin_col_base;
} else {
gpi_stat_tmp = gpi_stat3;
- pin_loc = pin - ADP5589_GPI_PIN_COL_BASE - 8;
+ pin_loc = pin - kpad->var->gpi_pin_col_base - 8;
}
if (gpi_stat_tmp < 0) {
dev_err(&kpad->client->dev,
- "Can't read GPIO_DAT_STAT switch"
- " %d default to OFF\n", pin);
+ "Can't read GPIO_DAT_STAT switch %d, default to OFF\n",
+ pin);
gpi_stat_tmp = 0;
}
@@ -537,7 +864,8 @@ static int __devinit adp5589_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adp5589_kpad *kpad;
- const struct adp5589_kpad_platform_data *pdata;
+ const struct adp5589_kpad_platform_data *pdata =
+ client->dev.platform_data;
struct input_dev *input;
unsigned int revid;
int ret, i;
@@ -549,56 +877,79 @@ static int __devinit adp5589_probe(struct i2c_client *client,
return -EIO;
}
- pdata = client->dev.platform_data;
if (!pdata) {
dev_err(&client->dev, "no platform data?\n");
return -EINVAL;
}
- if (!((pdata->keypad_en_mask & 0xFF) &&
- (pdata->keypad_en_mask >> 8)) || !pdata->keymap) {
+ kpad = kzalloc(sizeof(*kpad), GFP_KERNEL);
+ if (!kpad)
+ return -ENOMEM;
+
+ switch (id->driver_data) {
+ case ADP5585_02:
+ kpad->adp5585_support_row5 = true;
+ case ADP5585_01:
+ kpad->is_adp5585 = true;
+ kpad->var = &const_adp5585;
+ break;
+ case ADP5589:
+ kpad->var = &const_adp5589;
+ break;
+ }
+
+ if (!((pdata->keypad_en_mask & kpad->var->row_mask) &&
+ (pdata->keypad_en_mask >> kpad->var->col_shift)) ||
+ !pdata->keymap) {
dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
- return -EINVAL;
+ error = -EINVAL;
+ goto err_free_mem;
}
- if (pdata->keymapsize != ADP5589_KEYMAPSIZE) {
+ if (pdata->keymapsize != kpad->var->keymapsize) {
dev_err(&client->dev, "invalid keymapsize\n");
- return -EINVAL;
+ error = -EINVAL;
+ goto err_free_mem;
}
if (!pdata->gpimap && pdata->gpimapsize) {
dev_err(&client->dev, "invalid gpimap from pdata\n");
- return -EINVAL;
+ error = -EINVAL;
+ goto err_free_mem;
}
- if (pdata->gpimapsize > ADP5589_GPIMAPSIZE_MAX) {
+ if (pdata->gpimapsize > kpad->var->gpimapsize_max) {
dev_err(&client->dev, "invalid gpimapsize\n");
- return -EINVAL;
+ error = -EINVAL;
+ goto err_free_mem;
}
for (i = 0; i < pdata->gpimapsize; i++) {
unsigned short pin = pdata->gpimap[i].pin;
- if (pin < ADP5589_GPI_PIN_BASE || pin > ADP5589_GPI_PIN_END) {
+ if (pin < kpad->var->gpi_pin_base ||
+ pin > kpad->var->gpi_pin_end) {
dev_err(&client->dev, "invalid gpi pin data\n");
- return -EINVAL;
+ error = -EINVAL;
+ goto err_free_mem;
}
- if ((1 << (pin - ADP5589_GPI_PIN_ROW_BASE)) &
+ if ((1 << (pin - kpad->var->gpi_pin_row_base)) &
pdata->keypad_en_mask) {
dev_err(&client->dev, "invalid gpi row/col data\n");
- return -EINVAL;
+ error = -EINVAL;
+ goto err_free_mem;
}
}
if (!client->irq) {
dev_err(&client->dev, "no IRQ?\n");
- return -EINVAL;
+ error = -EINVAL;
+ goto err_free_mem;
}
- kpad = kzalloc(sizeof(*kpad), GFP_KERNEL);
input = input_allocate_device();
- if (!kpad || !input) {
+ if (!input) {
error = -ENOMEM;
goto err_free_mem;
}
@@ -606,13 +957,13 @@ static int __devinit adp5589_probe(struct i2c_client *client,
kpad->client = client;
kpad->input = input;
- ret = adp5589_read(client, ADP5589_ID);
+ ret = adp5589_read(client, ADP5589_5_ID);
if (ret < 0) {
error = ret;
- goto err_free_mem;
+ goto err_free_input;
}
- revid = (u8) ret & ADP5589_DEVICE_ID_MASK;
+ revid = (u8) ret & ADP5589_5_DEVICE_ID_MASK;
input->name = client->name;
input->phys = "adp5589-keys/input0";
@@ -653,7 +1004,7 @@ static int __devinit adp5589_probe(struct i2c_client *client,
error = input_register_device(input);
if (error) {
dev_err(&client->dev, "unable to register input device\n");
- goto err_free_mem;
+ goto err_free_input;
}
error = request_threaded_irq(client->irq, NULL, adp5589_irq,
@@ -686,8 +1037,9 @@ err_free_irq:
err_unreg_dev:
input_unregister_device(input);
input = NULL;
-err_free_mem:
+err_free_input:
input_free_device(input);
+err_free_mem:
kfree(kpad);
return error;
@@ -697,7 +1049,7 @@ static int __devexit adp5589_remove(struct i2c_client *client)
{
struct adp5589_kpad *kpad = i2c_get_clientdata(client);
- adp5589_write(client, ADP5589_GENERAL_CFG, 0);
+ adp5589_write(client, kpad->var->reg(ADP5589_GENERAL_CFG), 0);
free_irq(client->irq, kpad);
input_unregister_device(kpad->input);
adp5589_gpio_remove(kpad);
@@ -737,7 +1089,9 @@ static int adp5589_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume);
static const struct i2c_device_id adp5589_id[] = {
- {"adp5589-keys", 0},
+ {"adp5589-keys", ADP5589},
+ {"adp5585-keys", ADP5585_01},
+ {"adp5585-02-keys", ADP5585_02}, /* Adds ROW5 to ADP5585 */
{}
};
@@ -768,4 +1122,4 @@ module_exit(adp5589_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("ADP5589 Keypad driver");
+MODULE_DESCRIPTION("ADP5589/ADP5585 Keypad driver");
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 11478eb2c27d..19cfc0cf558c 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -1578,14 +1578,14 @@ static int __init atkbd_setup_forced_release(const struct dmi_system_id *id)
atkbd_platform_fixup = atkbd_apply_forced_release_keylist;
atkbd_platform_fixup_data = id->driver_data;
- return 0;
+ return 1;
}
static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id)
{
atkbd_platform_scancode_fixup = id->driver_data;
- return 0;
+ return 1;
}
static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c
index cd89d17162a3..9d82b3aeff5e 100644
--- a/drivers/input/keyboard/davinci_keyscan.c
+++ b/drivers/input/keyboard/davinci_keyscan.c
@@ -271,7 +271,7 @@ static int __init davinci_ks_probe(struct platform_device *pdev)
}
error = request_irq(davinci_ks->irq, davinci_ks_interrupt,
- IRQF_DISABLED, pdev->name, davinci_ks);
+ 0, pdev->name, davinci_ks);
if (error < 0) {
dev_err(dev, "unable to register davinci key scan interrupt\n");
goto fail5;
diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c
index c8242dd190d0..4662c5da8018 100644
--- a/drivers/input/keyboard/ep93xx_keypad.c
+++ b/drivers/input/keyboard/ep93xx_keypad.c
@@ -20,6 +20,7 @@
* flag.
*/
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
@@ -322,7 +323,7 @@ static int __devinit ep93xx_keypad_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, keypad);
err = request_irq(keypad->irq, ep93xx_keypad_irq_handler,
- IRQF_DISABLED, pdev->name, keypad);
+ 0, pdev->name, keypad);
if (err)
goto failed_free_dev;
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 6e6145b9a4c1..ed1ed469d085 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -2,6 +2,7 @@
* Driver for keys on GPIO lines capable of generating interrupts.
*
* Copyright 2005 Phil Blundell
+ * Copyright 2010, 2011 David Jander <david@protonic.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,6 +26,8 @@
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
struct gpio_button_data {
struct gpio_keys_button *button;
@@ -415,7 +418,7 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
if (!button->can_disable)
irqflags |= IRQF_SHARED;
- error = request_any_context_irq(irq, gpio_keys_isr, irqflags, desc, bdata);
+ error = request_threaded_irq(irq, NULL, gpio_keys_isr, irqflags, desc, bdata);
if (error < 0) {
dev_err(dev, "Unable to claim irq %d; error %d\n",
irq, error);
@@ -445,15 +448,122 @@ static void gpio_keys_close(struct input_dev *input)
ddata->disable(input->dev.parent);
}
+/*
+ * Handlers for alternative sources of platform_data
+ */
+#ifdef CONFIG_OF
+/*
+ * Translate OpenFirmware node properties into platform_data
+ */
+static int gpio_keys_get_devtree_pdata(struct device *dev,
+ struct gpio_keys_platform_data *pdata)
+{
+ struct device_node *node, *pp;
+ int i;
+ struct gpio_keys_button *buttons;
+ u32 reg;
+
+ node = dev->of_node;
+ if (node == NULL)
+ return -ENODEV;
+
+ memset(pdata, 0, sizeof *pdata);
+
+ pdata->rep = !!of_get_property(node, "autorepeat", NULL);
+
+ /* First count the subnodes */
+ pdata->nbuttons = 0;
+ pp = NULL;
+ while ((pp = of_get_next_child(node, pp)))
+ pdata->nbuttons++;
+
+ if (pdata->nbuttons == 0)
+ return -ENODEV;
+
+ buttons = kzalloc(pdata->nbuttons * (sizeof *buttons), GFP_KERNEL);
+ if (!buttons)
+ return -ENOMEM;
+
+ pp = NULL;
+ i = 0;
+ while ((pp = of_get_next_child(node, pp))) {
+ enum of_gpio_flags flags;
+
+ if (!of_find_property(pp, "gpios", NULL)) {
+ pdata->nbuttons--;
+ dev_warn(dev, "Found button without gpios\n");
+ continue;
+ }
+ buttons[i].gpio = of_get_gpio_flags(pp, 0, &flags);
+ buttons[i].active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+ if (of_property_read_u32(pp, "linux,code", &reg)) {
+ dev_err(dev, "Button without keycode: 0x%x\n", buttons[i].gpio);
+ goto out_fail;
+ }
+ buttons[i].code = reg;
+
+ buttons[i].desc = of_get_property(pp, "label", NULL);
+
+ if (of_property_read_u32(pp, "linux,input-type", &reg) == 0)
+ buttons[i].type = reg;
+ else
+ buttons[i].type = EV_KEY;
+
+ buttons[i].wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);
+
+ if (of_property_read_u32(pp, "debounce-interval", &reg) == 0)
+ buttons[i].debounce_interval = reg;
+ else
+ buttons[i].debounce_interval = 5;
+
+ i++;
+ }
+
+ pdata->buttons = buttons;
+
+ return 0;
+
+out_fail:
+ kfree(buttons);
+ return -ENODEV;
+}
+
+static struct of_device_id gpio_keys_of_match[] = {
+ { .compatible = "gpio-keys", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
+
+#else
+
+static int gpio_keys_get_devtree_pdata(struct device *dev,
+ struct gpio_keys_platform_data *altp)
+{
+ return -ENODEV;
+}
+
+#define gpio_keys_of_match NULL
+
+#endif
+
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata;
struct device *dev = &pdev->dev;
+ struct gpio_keys_platform_data alt_pdata;
struct input_dev *input;
int i, error;
int wakeup = 0;
+ if (!pdata) {
+ error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);
+ if (error)
+ return error;
+ pdata = &alt_pdata;
+ }
+
ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
pdata->nbuttons * sizeof(struct gpio_button_data),
GFP_KERNEL);
@@ -544,13 +654,15 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
fail1:
input_free_device(input);
kfree(ddata);
+ /* If we have no platform_data, we allocated buttons dynamically. */
+ if (!pdev->dev.platform_data)
+ kfree(pdata->buttons);
return error;
}
static int __devexit gpio_keys_remove(struct platform_device *pdev)
{
- struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
struct input_dev *input = ddata->input;
int i;
@@ -559,31 +671,39 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 0);
- for (i = 0; i < pdata->nbuttons; i++) {
- int irq = gpio_to_irq(pdata->buttons[i].gpio);
+ for (i = 0; i < ddata->n_buttons; i++) {
+ int irq = gpio_to_irq(ddata->data[i].button->gpio);
free_irq(irq, &ddata->data[i]);
if (ddata->data[i].timer_debounce)
del_timer_sync(&ddata->data[i].timer);
cancel_work_sync(&ddata->data[i].work);
- gpio_free(pdata->buttons[i].gpio);
+ gpio_free(ddata->data[i].button->gpio);
}
input_unregister_device(input);
+ /*
+ * If we had no platform_data, we allocated buttons dynamically, and
+ * must free them here. ddata->data[0].button is the pointer to the
+ * beginning of the allocated array.
+ */
+ if (!pdev->dev.platform_data)
+ kfree(ddata->data[0].button);
+
+ kfree(ddata);
+
return 0;
}
-
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int gpio_keys_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
int i;
- if (device_may_wakeup(&pdev->dev)) {
- for (i = 0; i < pdata->nbuttons; i++) {
- struct gpio_keys_button *button = &pdata->buttons[i];
+ if (device_may_wakeup(dev)) {
+ for (i = 0; i < ddata->n_buttons; i++) {
+ struct gpio_keys_button *button = ddata->data[i].button;
if (button->wakeup) {
int irq = gpio_to_irq(button->gpio);
enable_irq_wake(irq);
@@ -596,15 +716,13 @@ static int gpio_keys_suspend(struct device *dev)
static int gpio_keys_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
- struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
int i;
- for (i = 0; i < pdata->nbuttons; i++) {
+ for (i = 0; i < ddata->n_buttons; i++) {
- struct gpio_keys_button *button = &pdata->buttons[i];
- if (button->wakeup && device_may_wakeup(&pdev->dev)) {
+ struct gpio_keys_button *button = ddata->data[i].button;
+ if (button->wakeup && device_may_wakeup(dev)) {
int irq = gpio_to_irq(button->gpio);
disable_irq_wake(irq);
}
@@ -615,22 +733,18 @@ static int gpio_keys_resume(struct device *dev)
return 0;
}
-
-static const struct dev_pm_ops gpio_keys_pm_ops = {
- .suspend = gpio_keys_suspend,
- .resume = gpio_keys_resume,
-};
#endif
+static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
+
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.remove = __devexit_p(gpio_keys_remove),
.driver = {
.name = "gpio-keys",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
.pm = &gpio_keys_pm_ops,
-#endif
+ .of_match_table = gpio_keys_of_match,
}
};
@@ -644,10 +758,10 @@ static void __exit gpio_keys_exit(void)
platform_driver_unregister(&gpio_keys_device_driver);
}
-module_init(gpio_keys_init);
+late_initcall(gpio_keys_init);
module_exit(gpio_keys_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
-MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs");
+MODULE_DESCRIPTION("Keyboard driver for GPIOs");
MODULE_ALIAS("platform:gpio-keys");
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c
index d92c15c39e68..ccebd2d09151 100644
--- a/drivers/input/keyboard/imx_keypad.c
+++ b/drivers/input/keyboard/imx_keypad.c
@@ -510,7 +510,7 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev)
/* Ensure that the keypad will stay dormant until opened */
imx_keypad_inhibit(keypad);
- error = request_irq(irq, imx_keypad_irq_handler, IRQF_DISABLED,
+ error = request_irq(irq, imx_keypad_irq_handler, 0,
pdev->name, keypad);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
@@ -567,10 +567,54 @@ static int __devexit imx_keypad_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int imx_kbd_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct imx_keypad *kbd = platform_get_drvdata(pdev);
+ struct input_dev *input_dev = kbd->input_dev;
+
+ /* imx kbd can wake up system even clock is disabled */
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ clk_disable(kbd->clk);
+
+ mutex_unlock(&input_dev->mutex);
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(kbd->irq);
+
+ return 0;
+}
+
+static int imx_kbd_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct imx_keypad *kbd = platform_get_drvdata(pdev);
+ struct input_dev *input_dev = kbd->input_dev;
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(kbd->irq);
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ clk_enable(kbd->clk);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(imx_kbd_pm_ops, imx_kbd_suspend, imx_kbd_resume);
+
static struct platform_driver imx_keypad_driver = {
.driver = {
.name = "imx-keypad",
.owner = THIS_MODULE,
+ .pm = &imx_kbd_pm_ops,
},
.probe = imx_keypad_probe,
.remove = __devexit_p(imx_keypad_remove),
diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c
index 2cd3e1d56ea4..0aa6740e60d0 100644
--- a/drivers/input/keyboard/jornada720_kbd.c
+++ b/drivers/input/keyboard/jornada720_kbd.c
@@ -129,7 +129,7 @@ static int __devinit jornada720_kbd_probe(struct platform_device *pdev)
err = request_irq(IRQ_GPIO0,
jornada720_kbd_interrupt,
- IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_FALLING,
"jornadakbd", pdev);
if (err) {
printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n");
diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c
index 71f744a8e686..82d1dc8badd5 100644
--- a/drivers/input/keyboard/lm8323.c
+++ b/drivers/input/keyboard/lm8323.c
@@ -146,7 +146,6 @@ struct lm8323_chip {
/* device lock */
struct mutex lock;
struct i2c_client *client;
- struct work_struct work;
struct input_dev *idev;
bool kp_enabled;
bool pm_suspend;
@@ -162,7 +161,6 @@ struct lm8323_chip {
#define client_to_lm8323(c) container_of(c, struct lm8323_chip, client)
#define dev_to_lm8323(d) container_of(d, struct lm8323_chip, client->dev)
-#define work_to_lm8323(w) container_of(w, struct lm8323_chip, work)
#define cdev_to_pwm(c) container_of(c, struct lm8323_pwm, cdev)
#define work_to_pwm(w) container_of(w, struct lm8323_pwm, work)
@@ -375,9 +373,9 @@ static void pwm_done(struct lm8323_pwm *pwm)
* Bottom half: handle the interrupt by posting key events, or dealing with
* errors appropriately.
*/
-static void lm8323_work(struct work_struct *work)
+static irqreturn_t lm8323_irq(int irq, void *_lm)
{
- struct lm8323_chip *lm = work_to_lm8323(work);
+ struct lm8323_chip *lm = _lm;
u8 ints;
int i;
@@ -409,16 +407,6 @@ static void lm8323_work(struct work_struct *work)
}
mutex_unlock(&lm->lock);
-}
-
-/*
- * We cannot use I2C in interrupt context, so we just schedule work.
- */
-static irqreturn_t lm8323_irq(int irq, void *data)
-{
- struct lm8323_chip *lm = data;
-
- schedule_work(&lm->work);
return IRQ_HANDLED;
}
@@ -675,7 +663,6 @@ static int __devinit lm8323_probe(struct i2c_client *client,
lm->client = client;
lm->idev = idev;
mutex_init(&lm->lock);
- INIT_WORK(&lm->work, lm8323_work);
lm->size_x = pdata->size_x;
lm->size_y = pdata->size_y;
@@ -746,9 +733,8 @@ static int __devinit lm8323_probe(struct i2c_client *client,
goto fail3;
}
- err = request_irq(client->irq, lm8323_irq,
- IRQF_TRIGGER_FALLING | IRQF_DISABLED,
- "lm8323", lm);
+ err = request_threaded_irq(client->irq, NULL, lm8323_irq,
+ IRQF_TRIGGER_LOW|IRQF_ONESHOT, "lm8323", lm);
if (err) {
dev_err(&client->dev, "could not get IRQ %d\n", client->irq);
goto fail4;
@@ -768,8 +754,11 @@ fail3:
device_remove_file(&client->dev, &dev_attr_disable_kp);
fail2:
while (--pwm >= 0)
- if (lm->pwm[pwm].enabled)
+ if (lm->pwm[pwm].enabled) {
+ device_remove_file(lm->pwm[pwm].cdev.dev,
+ &dev_attr_time);
led_classdev_unregister(&lm->pwm[pwm].cdev);
+ }
fail1:
input_free_device(idev);
kfree(lm);
@@ -783,22 +772,23 @@ static int __devexit lm8323_remove(struct i2c_client *client)
disable_irq_wake(client->irq);
free_irq(client->irq, lm);
- cancel_work_sync(&lm->work);
input_unregister_device(lm->idev);
device_remove_file(&lm->client->dev, &dev_attr_disable_kp);
for (i = 0; i < 3; i++)
- if (lm->pwm[i].enabled)
+ if (lm->pwm[i].enabled) {
+ device_remove_file(lm->pwm[i].cdev.dev, &dev_attr_time);
led_classdev_unregister(&lm->pwm[i].cdev);
+ }
kfree(lm);
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* We don't need to explicitly suspend the chip, as it already switches off
* when there's no activity.
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index b02e4268e18f..e2ae657717ea 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -343,7 +343,6 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev,
for (i = 0; i < pdata->num_row_gpios; i++) {
err = request_irq(gpio_to_irq(pdata->row_gpios[i]),
matrix_keypad_interrupt,
- IRQF_DISABLED |
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
"matrix-keypad", keypad);
diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c
index 0a9e81194888..1c1615d9a7f9 100644
--- a/drivers/input/keyboard/mpr121_touchkey.c
+++ b/drivers/input/keyboard/mpr121_touchkey.c
@@ -43,14 +43,15 @@
* enabled capacitance sensing inputs and its run/suspend mode.
*/
#define ELECTRODE_CONF_ADDR 0x5e
+#define ELECTRODE_CONF_QUICK_CHARGE 0x80
#define AUTO_CONFIG_CTRL_ADDR 0x7b
#define AUTO_CONFIG_USL_ADDR 0x7d
#define AUTO_CONFIG_LSL_ADDR 0x7e
#define AUTO_CONFIG_TL_ADDR 0x7f
/* Threshold of touch/release trigger */
-#define TOUCH_THRESHOLD 0x0f
-#define RELEASE_THRESHOLD 0x0a
+#define TOUCH_THRESHOLD 0x08
+#define RELEASE_THRESHOLD 0x05
/* Masks for touch and release triggers */
#define TOUCH_STATUS_MASK 0xfff
/* MPR121 has 12 keys */
@@ -127,7 +128,7 @@ static int __devinit mpr121_phys_init(const struct mpr121_platform_data *pdata,
struct i2c_client *client)
{
const struct mpr121_init_register *reg;
- unsigned char usl, lsl, tl;
+ unsigned char usl, lsl, tl, eleconf;
int i, t, vdd, ret;
/* Set up touch/release threshold for ele0-ele11 */
@@ -163,8 +164,15 @@ static int __devinit mpr121_phys_init(const struct mpr121_platform_data *pdata,
ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl);
ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl);
ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl);
+
+ /*
+ * Quick charge bit will let the capacitive charge to ready
+ * state quickly, or the buttons may not function after system
+ * boot.
+ */
+ eleconf = mpr121->keycount | ELECTRODE_CONF_QUICK_CHARGE;
ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
- mpr121->keycount);
+ eleconf);
if (ret != 0)
goto err_i2c_write;
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
index 6e0f23091360..fcdec5e2b297 100644
--- a/drivers/input/keyboard/nomadik-ske-keypad.c
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -18,6 +18,7 @@
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/clk.h>
+#include <linux/module.h>
#include <plat/ske.h>
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
index 33d0bdc837c0..323bcdfff248 100644
--- a/drivers/input/keyboard/omap-keypad.c
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -35,7 +35,7 @@
#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/slab.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#include <plat/keypad.h>
#include <plat/menelaus.h>
#include <asm/irq.h>
diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
index 6229c3e8e78b..e7cc51d0fb34 100644
--- a/drivers/input/keyboard/pmic8xxx-keypad.c
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -700,9 +700,9 @@ static int __devinit pmic8xxx_kp_probe(struct platform_device *pdev)
return 0;
err_pmic_reg_read:
- free_irq(kp->key_stuck_irq, NULL);
+ free_irq(kp->key_stuck_irq, kp);
err_req_stuck_irq:
- free_irq(kp->key_sense_irq, NULL);
+ free_irq(kp->key_sense_irq, kp);
err_gpio_config:
err_get_irq:
input_free_device(kp->input);
@@ -717,8 +717,8 @@ static int __devexit pmic8xxx_kp_remove(struct platform_device *pdev)
struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
- free_irq(kp->key_stuck_irq, NULL);
- free_irq(kp->key_sense_irq, NULL);
+ free_irq(kp->key_stuck_irq, kp);
+ free_irq(kp->key_sense_irq, kp);
input_unregister_device(kp->input);
kfree(kp);
diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c
index 4b0ec35259a1..eca6ae63de14 100644
--- a/drivers/input/keyboard/pxa27x_keypad.c
+++ b/drivers/input/keyboard/pxa27x_keypad.c
@@ -535,7 +535,7 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
input_dev->evbit[0] |= BIT_MASK(EV_REL);
}
- error = request_irq(irq, pxa27x_keypad_irq_handler, IRQF_DISABLED,
+ error = request_irq(irq, pxa27x_keypad_irq_handler, 0,
pdev->name, keypad);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
diff --git a/drivers/input/keyboard/pxa930_rotary.c b/drivers/input/keyboard/pxa930_rotary.c
index b7123a44b6ec..35451bf780c7 100644
--- a/drivers/input/keyboard/pxa930_rotary.c
+++ b/drivers/input/keyboard/pxa930_rotary.c
@@ -148,7 +148,7 @@ static int __devinit pxa930_rotary_probe(struct platform_device *pdev)
r->input_dev = input_dev;
input_set_drvdata(input_dev, r);
- err = request_irq(irq, rotary_irq, IRQF_DISABLED,
+ err = request_irq(irq, rotary_irq, 0,
"enhanced rotary", r);
if (err) {
dev_err(&pdev->dev, "failed to request IRQ\n");
diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c
index ca7b89196ab7..b21bf5b876bb 100644
--- a/drivers/input/keyboard/qt1070.c
+++ b/drivers/input/keyboard/qt1070.c
@@ -239,8 +239,6 @@ static int __devexit qt1070_remove(struct i2c_client *client)
input_unregister_device(data->input);
kfree(data);
- i2c_set_clientdata(client, NULL);
-
return 0;
}
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
index 6876700a4469..934aeb583b30 100644
--- a/drivers/input/keyboard/sh_keysc.c
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -291,7 +291,7 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev)
return 0;
}
-#if CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_SLEEP
static int sh_keysc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c
index 99122f59e988..f60c9e82f204 100644
--- a/drivers/input/keyboard/tc3589x-keypad.c
+++ b/drivers/input/keyboard/tc3589x-keypad.c
@@ -90,7 +90,7 @@ struct tc_keypad {
bool keypad_stopped;
};
-static int __devinit tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad)
+static int tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad)
{
int ret;
struct tc3589x *tc3589x = keypad->tc3589x;
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index 2b3b73ec6689..cf3228b0ab90 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -19,6 +19,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/platform_device.h>
@@ -37,7 +38,7 @@
#define KBC_ROW_SCAN_DLY 5
/* KBC uses a 32KHz clock so a cycle = 1/32Khz */
-#define KBC_CYCLE_USEC 32
+#define KBC_CYCLE_MS 32
/* KBC Registers */
@@ -54,6 +55,7 @@
#define KBC_ROW_CFG0_0 0x8
#define KBC_COL_CFG0_0 0x18
+#define KBC_TO_CNT_0 0x24
#define KBC_INIT_DLY_0 0x28
#define KBC_RPT_DLY_0 0x2c
#define KBC_KP_ENT0_0 0x30
@@ -69,6 +71,7 @@ struct tegra_kbc {
spinlock_t lock;
unsigned int repoll_dly;
unsigned long cp_dly_jiffies;
+ unsigned int cp_to_wkup_dly;
bool use_fn_map;
bool use_ghost_filter;
const struct tegra_kbc_platform_data *pdata;
@@ -257,12 +260,10 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
u32 val = 0;
unsigned int i;
unsigned int num_down = 0;
- unsigned long flags;
bool fn_keypress = false;
bool key_in_same_row = false;
bool key_in_same_col = false;
- spin_lock_irqsave(&kbc->lock, flags);
for (i = 0; i < KBC_MAX_KPENT; i++) {
if ((i % 4) == 0)
val = readl(kbc->mmio + KBC_KP_ENT0_0 + i);
@@ -291,7 +292,7 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
* any 2 of the 3 keys share a row, and any 2 of them share a column.
* If so ignore the key presses for this iteration.
*/
- if ((kbc->use_ghost_filter) && (num_down >= 3)) {
+ if (kbc->use_ghost_filter && num_down >= 3) {
for (i = 0; i < num_down; i++) {
unsigned int j;
u8 curr_col = scancodes[i] & 0x07;
@@ -324,8 +325,6 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
}
}
- spin_unlock_irqrestore(&kbc->lock, flags);
-
/* Ignore the key presses for this iteration? */
if (key_in_same_col && key_in_same_row)
return;
@@ -340,6 +339,18 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
kbc->num_pressed_keys = num_down;
}
+static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable)
+{
+ u32 val;
+
+ val = readl(kbc->mmio + KBC_CONTROL_0);
+ if (enable)
+ val |= KBC_CONTROL_FIFO_CNT_INT_EN;
+ else
+ val &= ~KBC_CONTROL_FIFO_CNT_INT_EN;
+ writel(val, kbc->mmio + KBC_CONTROL_0);
+}
+
static void tegra_kbc_keypress_timer(unsigned long data)
{
struct tegra_kbc *kbc = (struct tegra_kbc *)data;
@@ -347,6 +358,8 @@ static void tegra_kbc_keypress_timer(unsigned long data)
u32 val;
unsigned int i;
+ spin_lock_irqsave(&kbc->lock, flags);
+
val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf;
if (val) {
unsigned long dly;
@@ -368,26 +381,19 @@ static void tegra_kbc_keypress_timer(unsigned long data)
kbc->num_pressed_keys = 0;
/* All keys are released so enable the keypress interrupt */
- spin_lock_irqsave(&kbc->lock, flags);
- val = readl(kbc->mmio + KBC_CONTROL_0);
- val |= KBC_CONTROL_FIFO_CNT_INT_EN;
- writel(val, kbc->mmio + KBC_CONTROL_0);
- spin_unlock_irqrestore(&kbc->lock, flags);
+ tegra_kbc_set_fifo_interrupt(kbc, true);
}
+
+ spin_unlock_irqrestore(&kbc->lock, flags);
}
static irqreturn_t tegra_kbc_isr(int irq, void *args)
{
struct tegra_kbc *kbc = args;
- u32 val, ctl;
+ unsigned long flags;
+ u32 val;
- /*
- * Until all keys are released, defer further processing to
- * the polling loop in tegra_kbc_keypress_timer
- */
- ctl = readl(kbc->mmio + KBC_CONTROL_0);
- ctl &= ~KBC_CONTROL_FIFO_CNT_INT_EN;
- writel(ctl, kbc->mmio + KBC_CONTROL_0);
+ spin_lock_irqsave(&kbc->lock, flags);
/*
* Quickly bail out & reenable interrupts if the fifo threshold
@@ -398,15 +404,15 @@ static irqreturn_t tegra_kbc_isr(int irq, void *args)
if (val & KBC_INT_FIFO_CNT_INT_STATUS) {
/*
- * Schedule timer to run when hardware is in continuous
- * polling mode.
+ * Until all keys are released, defer further processing to
+ * the polling loop in tegra_kbc_keypress_timer.
*/
+ tegra_kbc_set_fifo_interrupt(kbc, false);
mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies);
- } else {
- ctl |= KBC_CONTROL_FIFO_CNT_INT_EN;
- writel(ctl, kbc->mmio + KBC_CONTROL_0);
}
+ spin_unlock_irqrestore(&kbc->lock, flags);
+
return IRQ_HANDLED;
}
@@ -454,7 +460,6 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
static int tegra_kbc_start(struct tegra_kbc *kbc)
{
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
- unsigned long flags;
unsigned int debounce_cnt;
u32 val = 0;
@@ -492,7 +497,6 @@ static int tegra_kbc_start(struct tegra_kbc *kbc)
* Atomically clear out any remaining entries in the key FIFO
* and enable keyboard interrupts.
*/
- spin_lock_irqsave(&kbc->lock, flags);
while (1) {
val = readl(kbc->mmio + KBC_INT_0);
val >>= 4;
@@ -503,7 +507,6 @@ static int tegra_kbc_start(struct tegra_kbc *kbc)
val = readl(kbc->mmio + KBC_KP_ENT1_0);
}
writel(0x7, kbc->mmio + KBC_INT_0);
- spin_unlock_irqrestore(&kbc->lock, flags);
enable_irq(kbc->irq);
@@ -647,7 +650,7 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt;
- kbc->repoll_dly = ((kbc->repoll_dly * KBC_CYCLE_USEC) + 999) / 1000;
+ kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
@@ -657,7 +660,7 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
input_set_drvdata(input_dev, kbc);
- input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_dev->keycode = kbc->keycode;
@@ -701,7 +704,7 @@ err_iounmap:
err_free_mem_region:
release_mem_region(res->start, resource_size(res));
err_free_mem:
- input_free_device(kbc->idev);
+ input_free_device(input_dev);
kfree(kbc);
return err;
@@ -733,18 +736,30 @@ static int tegra_kbc_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct tegra_kbc *kbc = platform_get_drvdata(pdev);
+ mutex_lock(&kbc->idev->mutex);
if (device_may_wakeup(&pdev->dev)) {
- tegra_kbc_setup_wakekeys(kbc, true);
- enable_irq_wake(kbc->irq);
+ disable_irq(kbc->irq);
+ del_timer_sync(&kbc->timer);
+ tegra_kbc_set_fifo_interrupt(kbc, false);
+
/* Forcefully clear the interrupt status */
writel(0x7, kbc->mmio + KBC_INT_0);
+ /*
+ * Store the previous resident time of continuous polling mode.
+ * Force the keyboard into interrupt mode.
+ */
+ kbc->cp_to_wkup_dly = readl(kbc->mmio + KBC_TO_CNT_0);
+ writel(0, kbc->mmio + KBC_TO_CNT_0);
+
+ tegra_kbc_setup_wakekeys(kbc, true);
msleep(30);
+
+ enable_irq_wake(kbc->irq);
} else {
- mutex_lock(&kbc->idev->mutex);
if (kbc->idev->users)
tegra_kbc_stop(kbc);
- mutex_unlock(&kbc->idev->mutex);
}
+ mutex_unlock(&kbc->idev->mutex);
return 0;
}
@@ -755,15 +770,22 @@ static int tegra_kbc_resume(struct device *dev)
struct tegra_kbc *kbc = platform_get_drvdata(pdev);
int err = 0;
+ mutex_lock(&kbc->idev->mutex);
if (device_may_wakeup(&pdev->dev)) {
disable_irq_wake(kbc->irq);
tegra_kbc_setup_wakekeys(kbc, false);
+
+ /* Restore the resident time of continuous polling mode. */
+ writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0);
+
+ tegra_kbc_set_fifo_interrupt(kbc, true);
+
+ enable_irq(kbc->irq);
} else {
- mutex_lock(&kbc->idev->mutex);
if (kbc->idev->users)
err = tegra_kbc_start(kbc);
- mutex_unlock(&kbc->idev->mutex);
}
+ mutex_unlock(&kbc->idev->mutex);
return err;
}
diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c
index c8f097a15d89..66e55e5cfdd6 100644
--- a/drivers/input/keyboard/tnetv107x-keypad.c
+++ b/drivers/input/keyboard/tnetv107x-keypad.c
@@ -24,6 +24,7 @@
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/input/matrix_keypad.h>
+#include <linux/module.h>
#define BITS(x) (BIT(x) - 1)
@@ -337,5 +338,5 @@ module_exit(keypad_exit);
MODULE_AUTHOR("Cyril Chemparathy");
MODULE_DESCRIPTION("TNETV107X Keypad Driver");
-MODULE_ALIAS("platform: tnetv107x-keypad");
+MODULE_ALIAS("platform:tnetv107x-keypad");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c
index ee2bf6bcf291..318586dadacf 100644
--- a/drivers/input/keyboard/w90p910_keypad.c
+++ b/drivers/input/keyboard/w90p910_keypad.c
@@ -203,7 +203,7 @@ static int __devinit w90p910_keypad_probe(struct platform_device *pdev)
input_dev->keycode, input_dev->keybit);
error = request_irq(keypad->irq, w90p910_keypad_irq_handler,
- IRQF_DISABLED, pdev->name, keypad);
+ 0, pdev->name, keypad);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
goto failed_put_clk;
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 45dc6aa62ba4..22d875fde53a 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -62,6 +62,17 @@ config INPUT_AD714X_SPI
To compile this driver as a module, choose M here: the
module will be called ad714x-spi.
+config INPUT_BMA150
+ tristate "BMA150/SMB380 acceleration sensor support"
+ depends on I2C
+ select INPUT_POLLDEV
+ help
+ Say Y here if you have Bosch Sensortec's BMA150 or SMB380
+ acceleration sensor hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bma150.
+
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on PCSPKR_PLATFORM
@@ -74,6 +85,29 @@ config INPUT_PCSPKR
To compile this driver as a module, choose M here: the
module will be called pcspkr.
+config INPUT_PM8XXX_VIBRATOR
+ tristate "Qualcomm PM8XXX vibrator support"
+ depends on MFD_PM8XXX
+ select INPUT_FF_MEMLESS
+ help
+ This option enables device driver support for the vibrator
+ on Qualcomm PM8xxx chip. This driver supports ff-memless interface
+ from input framework.
+
+ To compile this driver as module, choose M here: the
+ module will be called pm8xxx-vibrator.
+
+config INPUT_PMIC8XXX_PWRKEY
+ tristate "PMIC8XXX power key support"
+ depends on MFD_PM8XXX
+ help
+ Say Y here if you want support for the PMIC8XXX power key.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pmic8xxx-pwrkey.
+
config INPUT_SPARCSPKR
tristate "SPARC Speaker support"
depends on PCI && SPARC64
@@ -100,6 +134,37 @@ config INPUT_MAX8925_ONKEY
To compile this driver as a module, choose M here: the module
will be called max8925_onkey.
+config INPUT_MC13783_PWRBUTTON
+ tristate "MC13783 ON buttons"
+ depends on MFD_MC13783
+ help
+ Support the ON buttons of MC13783 PMIC as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mc13783-pwrbutton.
+
+config INPUT_MMA8450
+ tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer"
+ depends on I2C
+ select INPUT_POLLDEV
+ help
+ Say Y here if you want to support Freescale's MMA8450 Accelerometer
+ through I2C interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mma8450.
+
+config INPUT_MPU3050
+ tristate "MPU3050 Triaxial gyroscope sensor"
+ depends on I2C
+ help
+ Say Y here if you want to support InvenSense MPU3050
+ connected via an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mpu3050.
+
config INPUT_APANEL
tristate "Fujitsu Lifebook Application Panel buttons"
depends on X86 && I2C && LEDS_CLASS
@@ -162,22 +227,6 @@ config INPUT_ATLAS_BTNS
To compile this driver as a module, choose M here: the module will
be called atlas_btns.
-config INPUT_ATI_REMOTE
- tristate "ATI / X10 USB RF remote control"
- depends on USB_ARCH_HAS_HCD
- select USB
- help
- Say Y here if you want to use an ATI or X10 "Lola" USB remote control.
- These are RF remotes with USB receivers.
- The ATI remote comes with many of ATI's All-In-Wonder video cards.
- The X10 "Lola" remote is available at:
- <http://www.x10.com/products/lola_sg1.htm>
- This driver provides mouse pointer, left and right mouse buttons,
- and maps all the other remote buttons to keypress events.
-
- To compile this driver as a module, choose M here: the module will be
- called ati_remote.
-
config INPUT_ATI_REMOTE2
tristate "ATI / Philips USB RF remote control"
depends on USB_ARCH_HAS_HCD
@@ -209,6 +258,23 @@ config INPUT_KEYSPAN_REMOTE
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
+config INPUT_KXTJ9
+ tristate "Kionix KXTJ9 tri-axis digital accelerometer"
+ depends on I2C
+ help
+ Say Y here to enable support for the Kionix KXTJ9 digital tri-axis
+ accelerometer.
+
+ To compile this driver as a module, choose M here: the module will
+ be called kxtj9.
+
+config INPUT_KXTJ9_POLLED_MODE
+ bool "Enable polling mode support"
+ depends on INPUT_KXTJ9
+ select INPUT_POLLDEV
+ help
+ Say Y here if you need accelerometer to work in polling mode.
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
@@ -267,7 +333,7 @@ config INPUT_TWL4030_PWRBUTTON
config INPUT_TWL4030_VIBRA
tristate "Support for TWL4030 Vibrator"
depends on TWL4030_CORE
- select TWL4030_CODEC
+ select MFD_TWL4030_AUDIO
select INPUT_FF_MEMLESS
help
This option enables support for TWL4030 Vibrator Driver.
@@ -275,6 +341,17 @@ config INPUT_TWL4030_VIBRA
To compile this driver as a module, choose M here. The module will
be called twl4030_vibra.
+config INPUT_TWL6040_VIBRA
+ tristate "Support for TWL6040 Vibrator"
+ depends on TWL4030_CORE
+ select TWL6040_CORE
+ select INPUT_FF_MEMLESS
+ help
+ This option enables support for TWL6040 Vibrator Driver.
+
+ To compile this driver as a module, choose M here. The module will
+ be called twl6040_vibra.
+
config INPUT_UINPUT
tristate "User level driver support"
help
@@ -330,17 +407,6 @@ config INPUT_PWM_BEEPER
To compile this driver as a module, choose M here: the module will be
called pwm-beeper.
-config INPUT_PMIC8XXX_PWRKEY
- tristate "PMIC8XXX power key support"
- depends on MFD_PM8XXX
- help
- Say Y here if you want support for the PMIC8XXX power key.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called pmic8xxx-pwrkey.
-
config INPUT_GPIO_ROTARY_ENCODER
tristate "Rotary encoders connected to GPIO pins"
depends on GPIOLIB && GENERIC_GPIO
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 38efb2cb182b..a244fc6a781c 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -13,10 +13,10 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o
obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o
obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
-obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
+obj-$(CONFIG_INPUT_BMA150) += bma150.o
obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
@@ -25,24 +25,29 @@ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
+obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
+obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
+obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
+obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
+obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o
+obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
-obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
+obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
-
diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c
index e21deb1baa8a..56810fb4eadd 100644
--- a/drivers/input/misc/ad714x-i2c.c
+++ b/drivers/input/misc/ad714x-i2c.c
@@ -1,7 +1,7 @@
/*
* AD714X CapTouch Programmable Controller driver (I2C bus)
*
- * Copyright 2009 Analog Devices Inc.
+ * Copyright 2009-2011 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
@@ -27,54 +27,49 @@ static int ad714x_i2c_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(ad714x_i2c_pm, ad714x_i2c_suspend, ad714x_i2c_resume);
-static int ad714x_i2c_write(struct device *dev, unsigned short reg,
- unsigned short data)
+static int ad714x_i2c_write(struct ad714x_chip *chip,
+ unsigned short reg, unsigned short data)
{
- struct i2c_client *client = to_i2c_client(dev);
- int ret = 0;
- u8 *_reg = (u8 *)&reg;
- u8 *_data = (u8 *)&data;
-
- u8 tx[4] = {
- _reg[1],
- _reg[0],
- _data[1],
- _data[0]
- };
-
- ret = i2c_master_send(client, tx, 4);
- if (ret < 0)
- dev_err(&client->dev, "I2C write error\n");
-
- return ret;
+ struct i2c_client *client = to_i2c_client(chip->dev);
+ int error;
+
+ chip->xfer_buf[0] = cpu_to_be16(reg);
+ chip->xfer_buf[1] = cpu_to_be16(data);
+
+ error = i2c_master_send(client, (u8 *)chip->xfer_buf,
+ 2 * sizeof(*chip->xfer_buf));
+ if (unlikely(error < 0)) {
+ dev_err(&client->dev, "I2C write error: %d\n", error);
+ return error;
+ }
+
+ return 0;
}
-static int ad714x_i2c_read(struct device *dev, unsigned short reg,
- unsigned short *data)
+static int ad714x_i2c_read(struct ad714x_chip *chip,
+ unsigned short reg, unsigned short *data, size_t len)
{
- struct i2c_client *client = to_i2c_client(dev);
- int ret = 0;
- u8 *_reg = (u8 *)&reg;
- u8 *_data = (u8 *)data;
-
- u8 tx[2] = {
- _reg[1],
- _reg[0]
- };
- u8 rx[2];
-
- ret = i2c_master_send(client, tx, 2);
- if (ret >= 0)
- ret = i2c_master_recv(client, rx, 2);
-
- if (unlikely(ret < 0)) {
- dev_err(&client->dev, "I2C read error\n");
- } else {
- _data[0] = rx[1];
- _data[1] = rx[0];
+ struct i2c_client *client = to_i2c_client(chip->dev);
+ int i;
+ int error;
+
+ chip->xfer_buf[0] = cpu_to_be16(reg);
+
+ error = i2c_master_send(client, (u8 *)chip->xfer_buf,
+ sizeof(*chip->xfer_buf));
+ if (error >= 0)
+ error = i2c_master_recv(client, (u8 *)chip->xfer_buf,
+ len * sizeof(*chip->xfer_buf));
+
+ if (unlikely(error < 0)) {
+ dev_err(&client->dev, "I2C read error: %d\n", error);
+ return error;
}
- return ret;
+ for (i = 0; i < len; i++)
+ data[i] = be16_to_cpu(chip->xfer_buf[i]);
+
+ return 0;
}
static int __devinit ad714x_i2c_probe(struct i2c_client *client,
@@ -121,13 +116,13 @@ static struct i2c_driver ad714x_i2c_driver = {
.id_table = ad714x_id,
};
-static __init int ad714x_i2c_init(void)
+static int __init ad714x_i2c_init(void)
{
return i2c_add_driver(&ad714x_i2c_driver);
}
module_init(ad714x_i2c_init);
-static __exit void ad714x_i2c_exit(void)
+static void __exit ad714x_i2c_exit(void)
{
i2c_del_driver(&ad714x_i2c_driver);
}
diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c
index 4120dd549305..875b50811361 100644
--- a/drivers/input/misc/ad714x-spi.c
+++ b/drivers/input/misc/ad714x-spi.c
@@ -1,12 +1,12 @@
/*
* AD714X CapTouch Programmable Controller driver (SPI bus)
*
- * Copyright 2009 Analog Devices Inc.
+ * Copyright 2009-2011 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
-#include <linux/input.h> /* BUS_I2C */
+#include <linux/input.h> /* BUS_SPI */
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/pm.h>
@@ -30,30 +30,68 @@ static int ad714x_spi_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(ad714x_spi_pm, ad714x_spi_suspend, ad714x_spi_resume);
-static int ad714x_spi_read(struct device *dev, unsigned short reg,
- unsigned short *data)
+static int ad714x_spi_read(struct ad714x_chip *chip,
+ unsigned short reg, unsigned short *data, size_t len)
{
- struct spi_device *spi = to_spi_device(dev);
- unsigned short tx = AD714x_SPI_CMD_PREFIX | AD714x_SPI_READ | reg;
+ struct spi_device *spi = to_spi_device(chip->dev);
+ struct spi_message message;
+ struct spi_transfer xfer[2];
+ int i;
+ int error;
+
+ spi_message_init(&message);
+ memset(xfer, 0, sizeof(xfer));
+
+ chip->xfer_buf[0] = cpu_to_be16(AD714x_SPI_CMD_PREFIX |
+ AD714x_SPI_READ | reg);
+ xfer[0].tx_buf = &chip->xfer_buf[0];
+ xfer[0].len = sizeof(chip->xfer_buf[0]);
+ spi_message_add_tail(&xfer[0], &message);
+
+ xfer[1].rx_buf = &chip->xfer_buf[1];
+ xfer[1].len = sizeof(chip->xfer_buf[1]) * len;
+ spi_message_add_tail(&xfer[1], &message);
+
+ error = spi_sync(spi, &message);
+ if (unlikely(error)) {
+ dev_err(chip->dev, "SPI read error: %d\n", error);
+ return error;
+ }
+
+ for (i = 0; i < len; i++)
+ data[i] = be16_to_cpu(chip->xfer_buf[i + 1]);
- return spi_write_then_read(spi, (u8 *)&tx, 2, (u8 *)data, 2);
+ return 0;
}
-static int ad714x_spi_write(struct device *dev, unsigned short reg,
- unsigned short data)
+static int ad714x_spi_write(struct ad714x_chip *chip,
+ unsigned short reg, unsigned short data)
{
- struct spi_device *spi = to_spi_device(dev);
- unsigned short tx[2] = {
- AD714x_SPI_CMD_PREFIX | reg,
- data
- };
+ struct spi_device *spi = to_spi_device(chip->dev);
+ int error;
+
+ chip->xfer_buf[0] = cpu_to_be16(AD714x_SPI_CMD_PREFIX | reg);
+ chip->xfer_buf[1] = cpu_to_be16(data);
+
+ error = spi_write(spi, (u8 *)chip->xfer_buf,
+ 2 * sizeof(*chip->xfer_buf));
+ if (unlikely(error)) {
+ dev_err(chip->dev, "SPI write error: %d\n", error);
+ return error;
+ }
- return spi_write(spi, (u8 *)tx, 4);
+ return 0;
}
static int __devinit ad714x_spi_probe(struct spi_device *spi)
{
struct ad714x_chip *chip;
+ int err;
+
+ spi->bits_per_word = 8;
+ err = spi_setup(spi);
+ if (err < 0)
+ return err;
chip = ad714x_probe(&spi->dev, BUS_SPI, spi->irq,
ad714x_spi_read, ad714x_spi_write);
diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c
index c3a62c42cd28..0ac75bbad4d6 100644
--- a/drivers/input/misc/ad714x.c
+++ b/drivers/input/misc/ad714x.c
@@ -1,7 +1,7 @@
/*
* AD714X CapTouch Programmable Controller driver supporting AD7142/3/7/8/7A
*
- * Copyright 2009 Analog Devices Inc.
+ * Copyright 2009-2011 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/input/ad714x.h>
+#include <linux/module.h>
#include "ad714x.h"
#define AD714X_PWR_CTRL 0x0
@@ -59,7 +60,6 @@
#define STAGE11_AMBIENT 0x27D
#define PER_STAGE_REG_NUM 36
-#define STAGE_NUM 12
#define STAGE_CFGREG_NUM 8
#define SYS_CFGREG_NUM 8
@@ -124,27 +124,6 @@ struct ad714x_driver_data {
* information to integrate all things which will be private data
* of spi/i2c device
*/
-struct ad714x_chip {
- unsigned short h_state;
- unsigned short l_state;
- unsigned short c_state;
- unsigned short adc_reg[STAGE_NUM];
- unsigned short amb_reg[STAGE_NUM];
- unsigned short sensor_val[STAGE_NUM];
-
- struct ad714x_platform_data *hw;
- struct ad714x_driver_data *sw;
-
- int irq;
- struct device *dev;
- ad714x_read_t read;
- ad714x_write_t write;
-
- struct mutex mutex;
-
- unsigned product;
- unsigned version;
-};
static void ad714x_use_com_int(struct ad714x_chip *ad714x,
int start_stage, int end_stage)
@@ -154,13 +133,13 @@ static void ad714x_use_com_int(struct ad714x_chip *ad714x,
mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
- ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data);
+ ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1);
data |= 1 << end_stage;
- ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data);
+ ad714x->write(ad714x, STG_COM_INT_EN_REG, data);
- ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data);
+ ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1);
data &= ~mask;
- ad714x->write(ad714x->dev, STG_HIGH_INT_EN_REG, data);
+ ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data);
}
static void ad714x_use_thr_int(struct ad714x_chip *ad714x,
@@ -171,13 +150,13 @@ static void ad714x_use_thr_int(struct ad714x_chip *ad714x,
mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
- ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data);
+ ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1);
data &= ~(1 << end_stage);
- ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data);
+ ad714x->write(ad714x, STG_COM_INT_EN_REG, data);
- ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data);
+ ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1);
data |= mask;
- ad714x->write(ad714x->dev, STG_HIGH_INT_EN_REG, data);
+ ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data);
}
static int ad714x_cal_highest_stage(struct ad714x_chip *ad714x,
@@ -273,15 +252,16 @@ static void ad714x_slider_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
int i;
+ ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage,
+ &ad714x->adc_reg[hw->start_stage],
+ hw->end_stage - hw->start_stage + 1);
+
for (i = hw->start_stage; i <= hw->end_stage; i++) {
- ad714x->read(ad714x->dev, CDC_RESULT_S0 + i,
- &ad714x->adc_reg[i]);
- ad714x->read(ad714x->dev,
- STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
- &ad714x->amb_reg[i]);
-
- ad714x->sensor_val[i] = abs(ad714x->adc_reg[i] -
- ad714x->amb_reg[i]);
+ ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
+ &ad714x->amb_reg[i], 1);
+
+ ad714x->sensor_val[i] =
+ abs(ad714x->adc_reg[i] - ad714x->amb_reg[i]);
}
}
@@ -444,15 +424,16 @@ static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
int i;
+ ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage,
+ &ad714x->adc_reg[hw->start_stage],
+ hw->end_stage - hw->start_stage + 1);
+
for (i = hw->start_stage; i <= hw->end_stage; i++) {
- ad714x->read(ad714x->dev, CDC_RESULT_S0 + i,
- &ad714x->adc_reg[i]);
- ad714x->read(ad714x->dev,
- STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
- &ad714x->amb_reg[i]);
+ ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
+ &ad714x->amb_reg[i], 1);
if (ad714x->adc_reg[i] > ad714x->amb_reg[i])
- ad714x->sensor_val[i] = ad714x->adc_reg[i] -
- ad714x->amb_reg[i];
+ ad714x->sensor_val[i] =
+ ad714x->adc_reg[i] - ad714x->amb_reg[i];
else
ad714x->sensor_val[i] = 0;
}
@@ -597,15 +578,16 @@ static void touchpad_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
int i;
+ ad714x->read(ad714x, CDC_RESULT_S0 + hw->x_start_stage,
+ &ad714x->adc_reg[hw->x_start_stage],
+ hw->x_end_stage - hw->x_start_stage + 1);
+
for (i = hw->x_start_stage; i <= hw->x_end_stage; i++) {
- ad714x->read(ad714x->dev, CDC_RESULT_S0 + i,
- &ad714x->adc_reg[i]);
- ad714x->read(ad714x->dev,
- STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
- &ad714x->amb_reg[i]);
+ ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
+ &ad714x->amb_reg[i], 1);
if (ad714x->adc_reg[i] > ad714x->amb_reg[i])
- ad714x->sensor_val[i] = ad714x->adc_reg[i] -
- ad714x->amb_reg[i];
+ ad714x->sensor_val[i] =
+ ad714x->adc_reg[i] - ad714x->amb_reg[i];
else
ad714x->sensor_val[i] = 0;
}
@@ -891,7 +873,7 @@ static int ad714x_hw_detect(struct ad714x_chip *ad714x)
{
unsigned short data;
- ad714x->read(ad714x->dev, AD714X_PARTID_REG, &data);
+ ad714x->read(ad714x, AD714X_PARTID_REG, &data, 1);
switch (data & 0xFFF0) {
case AD7142_PARTID:
ad714x->product = 0x7142;
@@ -940,23 +922,20 @@ static void ad714x_hw_init(struct ad714x_chip *ad714x)
for (i = 0; i < STAGE_NUM; i++) {
reg_base = AD714X_STAGECFG_REG + i * STAGE_CFGREG_NUM;
for (j = 0; j < STAGE_CFGREG_NUM; j++)
- ad714x->write(ad714x->dev, reg_base + j,
+ ad714x->write(ad714x, reg_base + j,
ad714x->hw->stage_cfg_reg[i][j]);
}
for (i = 0; i < SYS_CFGREG_NUM; i++)
- ad714x->write(ad714x->dev, AD714X_SYSCFG_REG + i,
+ ad714x->write(ad714x, AD714X_SYSCFG_REG + i,
ad714x->hw->sys_cfg_reg[i]);
for (i = 0; i < SYS_CFGREG_NUM; i++)
- ad714x->read(ad714x->dev, AD714X_SYSCFG_REG + i,
- &data);
+ ad714x->read(ad714x, AD714X_SYSCFG_REG + i, &data, 1);
- ad714x->write(ad714x->dev, AD714X_STG_CAL_EN_REG, 0xFFF);
+ ad714x->write(ad714x, AD714X_STG_CAL_EN_REG, 0xFFF);
/* clear all interrupts */
- ad714x->read(ad714x->dev, STG_LOW_INT_STA_REG, &data);
- ad714x->read(ad714x->dev, STG_HIGH_INT_STA_REG, &data);
- ad714x->read(ad714x->dev, STG_COM_INT_STA_REG, &data);
+ ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
}
static irqreturn_t ad714x_interrupt_thread(int irq, void *data)
@@ -966,9 +945,7 @@ static irqreturn_t ad714x_interrupt_thread(int irq, void *data)
mutex_lock(&ad714x->mutex);
- ad714x->read(ad714x->dev, STG_LOW_INT_STA_REG, &ad714x->l_state);
- ad714x->read(ad714x->dev, STG_HIGH_INT_STA_REG, &ad714x->h_state);
- ad714x->read(ad714x->dev, STG_COM_INT_STA_REG, &ad714x->c_state);
+ ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
for (i = 0; i < ad714x->hw->button_num; i++)
ad714x_button_state_machine(ad714x, i);
@@ -1245,7 +1222,7 @@ int ad714x_disable(struct ad714x_chip *ad714x)
mutex_lock(&ad714x->mutex);
data = ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL] | 0x3;
- ad714x->write(ad714x->dev, AD714X_PWR_CTRL, data);
+ ad714x->write(ad714x, AD714X_PWR_CTRL, data);
mutex_unlock(&ad714x->mutex);
@@ -1255,24 +1232,20 @@ EXPORT_SYMBOL(ad714x_disable);
int ad714x_enable(struct ad714x_chip *ad714x)
{
- unsigned short data;
-
dev_dbg(ad714x->dev, "%s enter\n", __func__);
mutex_lock(&ad714x->mutex);
/* resume to non-shutdown mode */
- ad714x->write(ad714x->dev, AD714X_PWR_CTRL,
+ ad714x->write(ad714x, AD714X_PWR_CTRL,
ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL]);
/* make sure the interrupt output line is not low level after resume,
* otherwise we will get no chance to enter falling-edge irq again
*/
- ad714x->read(ad714x->dev, STG_LOW_INT_STA_REG, &data);
- ad714x->read(ad714x->dev, STG_HIGH_INT_STA_REG, &data);
- ad714x->read(ad714x->dev, STG_COM_INT_STA_REG, &data);
+ ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
mutex_unlock(&ad714x->mutex);
diff --git a/drivers/input/misc/ad714x.h b/drivers/input/misc/ad714x.h
index 45c54fb13f07..3c85455aa66d 100644
--- a/drivers/input/misc/ad714x.h
+++ b/drivers/input/misc/ad714x.h
@@ -1,7 +1,7 @@
/*
* AD714X CapTouch Programmable Controller driver (bus interfaces)
*
- * Copyright 2009 Analog Devices Inc.
+ * Copyright 2009-2011 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
@@ -11,11 +11,40 @@
#include <linux/types.h>
+#define STAGE_NUM 12
+
struct device;
+struct ad714x_platform_data;
+struct ad714x_driver_data;
struct ad714x_chip;
-typedef int (*ad714x_read_t)(struct device *, unsigned short, unsigned short *);
-typedef int (*ad714x_write_t)(struct device *, unsigned short, unsigned short);
+typedef int (*ad714x_read_t)(struct ad714x_chip *, unsigned short, unsigned short *, size_t);
+typedef int (*ad714x_write_t)(struct ad714x_chip *, unsigned short, unsigned short);
+
+struct ad714x_chip {
+ unsigned short l_state;
+ unsigned short h_state;
+ unsigned short c_state;
+ unsigned short adc_reg[STAGE_NUM];
+ unsigned short amb_reg[STAGE_NUM];
+ unsigned short sensor_val[STAGE_NUM];
+
+ struct ad714x_platform_data *hw;
+ struct ad714x_driver_data *sw;
+
+ int irq;
+ struct device *dev;
+ ad714x_read_t read;
+ ad714x_write_t write;
+
+ struct mutex mutex;
+
+ unsigned product;
+ unsigned version;
+
+ __be16 xfer_buf[16] ____cacheline_aligned;
+
+};
int ad714x_disable(struct ad714x_chip *ad714x);
int ad714x_enable(struct ad714x_chip *ad714x);
diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c
index 144ddbdeb9b3..09244804fb97 100644
--- a/drivers/input/misc/adxl34x.c
+++ b/drivers/input/misc/adxl34x.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/input/adxl34x.h>
+#include <linux/module.h>
#include "adxl34x.h"
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
index 1de58e8a1b71..8d345e87075e 100644
--- a/drivers/input/misc/ati_remote2.c
+++ b/drivers/input/misc/ati_remote2.c
@@ -11,6 +11,7 @@
#include <linux/usb/input.h>
#include <linux/slab.h>
+#include <linux/module.h>
#define DRIVER_DESC "ATI/Philips USB RF remote driver"
#define DRIVER_VERSION "0.3"
diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c
index 4f72bdd69410..d00edc9f39d1 100644
--- a/drivers/input/misc/bfin_rotary.c
+++ b/drivers/input/misc/bfin_rotary.c
@@ -6,7 +6,6 @@
*/
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
new file mode 100644
index 000000000000..8f55b54352b6
--- /dev/null
+++ b/drivers/input/misc/bma150.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright (c) 2011 Bosch Sensortec GmbH
+ * Copyright (c) 2011 Unixphere
+ *
+ * This driver adds support for Bosch Sensortec's digital acceleration
+ * sensors BMA150 and SMB380.
+ * The SMB380 is fully compatible with BMA150 and only differs in packaging.
+ *
+ * The datasheet for the BMA150 chip can be found here:
+ * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMA150-DS000-07.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/bma150.h>
+
+#define ABSMAX_ACC_VAL 0x01FF
+#define ABSMIN_ACC_VAL -(ABSMAX_ACC_VAL)
+
+/* Each axis is represented by a 2-byte data word */
+#define BMA150_XYZ_DATA_SIZE 6
+
+/* Input poll interval in milliseconds */
+#define BMA150_POLL_INTERVAL 10
+#define BMA150_POLL_MAX 200
+#define BMA150_POLL_MIN 0
+
+#define BMA150_BW_25HZ 0
+#define BMA150_BW_50HZ 1
+#define BMA150_BW_100HZ 2
+#define BMA150_BW_190HZ 3
+#define BMA150_BW_375HZ 4
+#define BMA150_BW_750HZ 5
+#define BMA150_BW_1500HZ 6
+
+#define BMA150_RANGE_2G 0
+#define BMA150_RANGE_4G 1
+#define BMA150_RANGE_8G 2
+
+#define BMA150_MODE_NORMAL 0
+#define BMA150_MODE_SLEEP 2
+#define BMA150_MODE_WAKE_UP 3
+
+/* Data register addresses */
+#define BMA150_DATA_0_REG 0x00
+#define BMA150_DATA_1_REG 0x01
+#define BMA150_DATA_2_REG 0x02
+
+/* Control register addresses */
+#define BMA150_CTRL_0_REG 0x0A
+#define BMA150_CTRL_1_REG 0x0B
+#define BMA150_CTRL_2_REG 0x14
+#define BMA150_CTRL_3_REG 0x15
+
+/* Configuration/Setting register addresses */
+#define BMA150_CFG_0_REG 0x0C
+#define BMA150_CFG_1_REG 0x0D
+#define BMA150_CFG_2_REG 0x0E
+#define BMA150_CFG_3_REG 0x0F
+#define BMA150_CFG_4_REG 0x10
+#define BMA150_CFG_5_REG 0x11
+
+#define BMA150_CHIP_ID 2
+#define BMA150_CHIP_ID_REG BMA150_DATA_0_REG
+
+#define BMA150_ACC_X_LSB_REG BMA150_DATA_2_REG
+
+#define BMA150_SLEEP_POS 0
+#define BMA150_SLEEP_MSK 0x01
+#define BMA150_SLEEP_REG BMA150_CTRL_0_REG
+
+#define BMA150_BANDWIDTH_POS 0
+#define BMA150_BANDWIDTH_MSK 0x07
+#define BMA150_BANDWIDTH_REG BMA150_CTRL_2_REG
+
+#define BMA150_RANGE_POS 3
+#define BMA150_RANGE_MSK 0x18
+#define BMA150_RANGE_REG BMA150_CTRL_2_REG
+
+#define BMA150_WAKE_UP_POS 0
+#define BMA150_WAKE_UP_MSK 0x01
+#define BMA150_WAKE_UP_REG BMA150_CTRL_3_REG
+
+#define BMA150_SW_RES_POS 1
+#define BMA150_SW_RES_MSK 0x02
+#define BMA150_SW_RES_REG BMA150_CTRL_0_REG
+
+/* Any-motion interrupt register fields */
+#define BMA150_ANY_MOTION_EN_POS 6
+#define BMA150_ANY_MOTION_EN_MSK 0x40
+#define BMA150_ANY_MOTION_EN_REG BMA150_CTRL_1_REG
+
+#define BMA150_ANY_MOTION_DUR_POS 6
+#define BMA150_ANY_MOTION_DUR_MSK 0xC0
+#define BMA150_ANY_MOTION_DUR_REG BMA150_CFG_5_REG
+
+#define BMA150_ANY_MOTION_THRES_REG BMA150_CFG_4_REG
+
+/* Advanced interrupt register fields */
+#define BMA150_ADV_INT_EN_POS 6
+#define BMA150_ADV_INT_EN_MSK 0x40
+#define BMA150_ADV_INT_EN_REG BMA150_CTRL_3_REG
+
+/* High-G interrupt register fields */
+#define BMA150_HIGH_G_EN_POS 1
+#define BMA150_HIGH_G_EN_MSK 0x02
+#define BMA150_HIGH_G_EN_REG BMA150_CTRL_1_REG
+
+#define BMA150_HIGH_G_HYST_POS 3
+#define BMA150_HIGH_G_HYST_MSK 0x38
+#define BMA150_HIGH_G_HYST_REG BMA150_CFG_5_REG
+
+#define BMA150_HIGH_G_DUR_REG BMA150_CFG_3_REG
+#define BMA150_HIGH_G_THRES_REG BMA150_CFG_2_REG
+
+/* Low-G interrupt register fields */
+#define BMA150_LOW_G_EN_POS 0
+#define BMA150_LOW_G_EN_MSK 0x01
+#define BMA150_LOW_G_EN_REG BMA150_CTRL_1_REG
+
+#define BMA150_LOW_G_HYST_POS 0
+#define BMA150_LOW_G_HYST_MSK 0x07
+#define BMA150_LOW_G_HYST_REG BMA150_CFG_5_REG
+
+#define BMA150_LOW_G_DUR_REG BMA150_CFG_1_REG
+#define BMA150_LOW_G_THRES_REG BMA150_CFG_0_REG
+
+struct bma150_data {
+ struct i2c_client *client;
+ struct input_polled_dev *input_polled;
+ struct input_dev *input;
+ u8 mode;
+};
+
+/*
+ * The settings for the given range, bandwidth and interrupt features
+ * are stated and verified by Bosch Sensortec where they are configured
+ * to provide a generic sensitivity performance.
+ */
+static struct bma150_cfg default_cfg __devinitdata = {
+ .any_motion_int = 1,
+ .hg_int = 1,
+ .lg_int = 1,
+ .any_motion_dur = 0,
+ .any_motion_thres = 0,
+ .hg_hyst = 0,
+ .hg_dur = 150,
+ .hg_thres = 160,
+ .lg_hyst = 0,
+ .lg_dur = 150,
+ .lg_thres = 20,
+ .range = BMA150_RANGE_2G,
+ .bandwidth = BMA150_BW_50HZ
+};
+
+static int bma150_write_byte(struct i2c_client *client, u8 reg, u8 val)
+{
+ s32 ret;
+
+ /* As per specification, disable irq in between register writes */
+ if (client->irq)
+ disable_irq_nosync(client->irq);
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+
+ if (client->irq)
+ enable_irq(client->irq);
+
+ return ret;
+}
+
+static int bma150_set_reg_bits(struct i2c_client *client,
+ int val, int shift, u8 mask, u8 reg)
+{
+ int data;
+
+ data = i2c_smbus_read_byte_data(client, reg);
+ if (data < 0)
+ return data;
+
+ data = (data & ~mask) | ((val << shift) & mask);
+ return bma150_write_byte(client, reg, data);
+}
+
+static int bma150_set_mode(struct bma150_data *bma150, u8 mode)
+{
+ int error;
+
+ error = bma150_set_reg_bits(bma150->client, mode, BMA150_WAKE_UP_POS,
+ BMA150_WAKE_UP_MSK, BMA150_WAKE_UP_REG);
+ if (error)
+ return error;
+
+ error = bma150_set_reg_bits(bma150->client, mode, BMA150_SLEEP_POS,
+ BMA150_SLEEP_MSK, BMA150_SLEEP_REG);
+ if (error)
+ return error;
+
+ if (mode == BMA150_MODE_NORMAL)
+ msleep(2);
+
+ bma150->mode = mode;
+ return 0;
+}
+
+static int __devinit bma150_soft_reset(struct bma150_data *bma150)
+{
+ int error;
+
+ error = bma150_set_reg_bits(bma150->client, 1, BMA150_SW_RES_POS,
+ BMA150_SW_RES_MSK, BMA150_SW_RES_REG);
+ if (error)
+ return error;
+
+ msleep(2);
+ return 0;
+}
+
+static int __devinit bma150_set_range(struct bma150_data *bma150, u8 range)
+{
+ return bma150_set_reg_bits(bma150->client, range, BMA150_RANGE_POS,
+ BMA150_RANGE_MSK, BMA150_RANGE_REG);
+}
+
+static int __devinit bma150_set_bandwidth(struct bma150_data *bma150, u8 bw)
+{
+ return bma150_set_reg_bits(bma150->client, bw, BMA150_BANDWIDTH_POS,
+ BMA150_BANDWIDTH_MSK, BMA150_BANDWIDTH_REG);
+}
+
+static int __devinit bma150_set_low_g_interrupt(struct bma150_data *bma150,
+ u8 enable, u8 hyst, u8 dur, u8 thres)
+{
+ int error;
+
+ error = bma150_set_reg_bits(bma150->client, hyst,
+ BMA150_LOW_G_HYST_POS, BMA150_LOW_G_HYST_MSK,
+ BMA150_LOW_G_HYST_REG);
+ if (error)
+ return error;
+
+ error = bma150_write_byte(bma150->client, BMA150_LOW_G_DUR_REG, dur);
+ if (error)
+ return error;
+
+ error = bma150_write_byte(bma150->client, BMA150_LOW_G_THRES_REG, thres);
+ if (error)
+ return error;
+
+ return bma150_set_reg_bits(bma150->client, !!enable,
+ BMA150_LOW_G_EN_POS, BMA150_LOW_G_EN_MSK,
+ BMA150_LOW_G_EN_REG);
+}
+
+static int __devinit bma150_set_high_g_interrupt(struct bma150_data *bma150,
+ u8 enable, u8 hyst, u8 dur, u8 thres)
+{
+ int error;
+
+ error = bma150_set_reg_bits(bma150->client, hyst,
+ BMA150_HIGH_G_HYST_POS, BMA150_HIGH_G_HYST_MSK,
+ BMA150_HIGH_G_HYST_REG);
+ if (error)
+ return error;
+
+ error = bma150_write_byte(bma150->client,
+ BMA150_HIGH_G_DUR_REG, dur);
+ if (error)
+ return error;
+
+ error = bma150_write_byte(bma150->client,
+ BMA150_HIGH_G_THRES_REG, thres);
+ if (error)
+ return error;
+
+ return bma150_set_reg_bits(bma150->client, !!enable,
+ BMA150_HIGH_G_EN_POS, BMA150_HIGH_G_EN_MSK,
+ BMA150_HIGH_G_EN_REG);
+}
+
+
+static int __devinit bma150_set_any_motion_interrupt(struct bma150_data *bma150,
+ u8 enable, u8 dur, u8 thres)
+{
+ int error;
+
+ error = bma150_set_reg_bits(bma150->client, dur,
+ BMA150_ANY_MOTION_DUR_POS,
+ BMA150_ANY_MOTION_DUR_MSK,
+ BMA150_ANY_MOTION_DUR_REG);
+ if (error)
+ return error;
+
+ error = bma150_write_byte(bma150->client,
+ BMA150_ANY_MOTION_THRES_REG, thres);
+ if (error)
+ return error;
+
+ error = bma150_set_reg_bits(bma150->client, !!enable,
+ BMA150_ADV_INT_EN_POS, BMA150_ADV_INT_EN_MSK,
+ BMA150_ADV_INT_EN_REG);
+ if (error)
+ return error;
+
+ return bma150_set_reg_bits(bma150->client, !!enable,
+ BMA150_ANY_MOTION_EN_POS,
+ BMA150_ANY_MOTION_EN_MSK,
+ BMA150_ANY_MOTION_EN_REG);
+}
+
+static void bma150_report_xyz(struct bma150_data *bma150)
+{
+ u8 data[BMA150_XYZ_DATA_SIZE];
+ s16 x, y, z;
+ s32 ret;
+
+ ret = i2c_smbus_read_i2c_block_data(bma150->client,
+ BMA150_ACC_X_LSB_REG, BMA150_XYZ_DATA_SIZE, data);
+ if (ret != BMA150_XYZ_DATA_SIZE)
+ return;
+
+ x = ((0xc0 & data[0]) >> 6) | (data[1] << 2);
+ y = ((0xc0 & data[2]) >> 6) | (data[3] << 2);
+ z = ((0xc0 & data[4]) >> 6) | (data[5] << 2);
+
+ /* sign extension */
+ x = (s16) (x << 6) >> 6;
+ y = (s16) (y << 6) >> 6;
+ z = (s16) (z << 6) >> 6;
+
+ input_report_abs(bma150->input, ABS_X, x);
+ input_report_abs(bma150->input, ABS_Y, y);
+ input_report_abs(bma150->input, ABS_Z, z);
+ input_sync(bma150->input);
+}
+
+static irqreturn_t bma150_irq_thread(int irq, void *dev)
+{
+ bma150_report_xyz(dev);
+
+ return IRQ_HANDLED;
+}
+
+static void bma150_poll(struct input_polled_dev *dev)
+{
+ bma150_report_xyz(dev->private);
+}
+
+static int bma150_open(struct bma150_data *bma150)
+{
+ int error;
+
+ error = pm_runtime_get_sync(&bma150->client->dev);
+ if (error && error != -ENOSYS)
+ return error;
+
+ /*
+ * See if runtime PM woke up the device. If runtime PM
+ * is disabled we need to do it ourselves.
+ */
+ if (bma150->mode != BMA150_MODE_NORMAL) {
+ error = bma150_set_mode(bma150, BMA150_MODE_NORMAL);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static void bma150_close(struct bma150_data *bma150)
+{
+ pm_runtime_put_sync(&bma150->client->dev);
+
+ if (bma150->mode != BMA150_MODE_SLEEP)
+ bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static int bma150_irq_open(struct input_dev *input)
+{
+ struct bma150_data *bma150 = input_get_drvdata(input);
+
+ return bma150_open(bma150);
+}
+
+static void bma150_irq_close(struct input_dev *input)
+{
+ struct bma150_data *bma150 = input_get_drvdata(input);
+
+ bma150_close(bma150);
+}
+
+static void bma150_poll_open(struct input_polled_dev *ipoll_dev)
+{
+ struct bma150_data *bma150 = ipoll_dev->private;
+
+ bma150_open(bma150);
+}
+
+static void bma150_poll_close(struct input_polled_dev *ipoll_dev)
+{
+ struct bma150_data *bma150 = ipoll_dev->private;
+
+ bma150_close(bma150);
+}
+
+static int __devinit bma150_initialize(struct bma150_data *bma150,
+ const struct bma150_cfg *cfg)
+{
+ int error;
+
+ error = bma150_soft_reset(bma150);
+ if (error)
+ return error;
+
+ error = bma150_set_bandwidth(bma150, cfg->bandwidth);
+ if (error)
+ return error;
+
+ error = bma150_set_range(bma150, cfg->range);
+ if (error)
+ return error;
+
+ if (bma150->client->irq) {
+ error = bma150_set_any_motion_interrupt(bma150,
+ cfg->any_motion_int,
+ cfg->any_motion_dur,
+ cfg->any_motion_thres);
+ if (error)
+ return error;
+
+ error = bma150_set_high_g_interrupt(bma150,
+ cfg->hg_int, cfg->hg_hyst,
+ cfg->hg_dur, cfg->hg_thres);
+ if (error)
+ return error;
+
+ error = bma150_set_low_g_interrupt(bma150,
+ cfg->lg_int, cfg->lg_hyst,
+ cfg->lg_dur, cfg->lg_thres);
+ if (error)
+ return error;
+ }
+
+ return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static void __devinit bma150_init_input_device(struct bma150_data *bma150,
+ struct input_dev *idev)
+{
+ idev->name = BMA150_DRIVER;
+ idev->phys = BMA150_DRIVER "/input0";
+ idev->id.bustype = BUS_I2C;
+ idev->dev.parent = &bma150->client->dev;
+
+ idev->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+ input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+ input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+}
+
+static int __devinit bma150_register_input_device(struct bma150_data *bma150)
+{
+ struct input_dev *idev;
+ int error;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ bma150_init_input_device(bma150, idev);
+
+ idev->open = bma150_irq_open;
+ idev->close = bma150_irq_close;
+ input_set_drvdata(idev, bma150);
+
+ error = input_register_device(idev);
+ if (error) {
+ input_free_device(idev);
+ return error;
+ }
+
+ bma150->input = idev;
+ return 0;
+}
+
+static int __devinit bma150_register_polled_device(struct bma150_data *bma150)
+{
+ struct input_polled_dev *ipoll_dev;
+ int error;
+
+ ipoll_dev = input_allocate_polled_device();
+ if (!ipoll_dev)
+ return -ENOMEM;
+
+ ipoll_dev->private = bma150;
+ ipoll_dev->open = bma150_poll_open;
+ ipoll_dev->close = bma150_poll_close;
+ ipoll_dev->poll = bma150_poll;
+ ipoll_dev->poll_interval = BMA150_POLL_INTERVAL;
+ ipoll_dev->poll_interval_min = BMA150_POLL_MIN;
+ ipoll_dev->poll_interval_max = BMA150_POLL_MAX;
+
+ bma150_init_input_device(bma150, ipoll_dev->input);
+
+ error = input_register_polled_device(ipoll_dev);
+ if (error) {
+ input_free_polled_device(ipoll_dev);
+ return error;
+ }
+
+ bma150->input_polled = ipoll_dev;
+ bma150->input = ipoll_dev->input;
+
+ return 0;
+}
+
+static int __devinit bma150_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct bma150_platform_data *pdata = client->dev.platform_data;
+ const struct bma150_cfg *cfg;
+ struct bma150_data *bma150;
+ int chip_id;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "i2c_check_functionality error\n");
+ return -EIO;
+ }
+
+ chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG);
+ if (chip_id != BMA150_CHIP_ID) {
+ dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id);
+ return -EINVAL;
+ }
+
+ bma150 = kzalloc(sizeof(struct bma150_data), GFP_KERNEL);
+ if (!bma150)
+ return -ENOMEM;
+
+ bma150->client = client;
+
+ if (pdata) {
+ if (pdata->irq_gpio_cfg) {
+ error = pdata->irq_gpio_cfg();
+ if (error) {
+ dev_err(&client->dev,
+ "IRQ GPIO conf. error %d, error %d\n",
+ client->irq, error);
+ goto err_free_mem;
+ }
+ }
+ cfg = &pdata->cfg;
+ } else {
+ cfg = &default_cfg;
+ }
+
+ error = bma150_initialize(bma150, cfg);
+ if (error)
+ goto err_free_mem;
+
+ if (client->irq > 0) {
+ error = bma150_register_input_device(bma150);
+ if (error)
+ goto err_free_mem;
+
+ error = request_threaded_irq(client->irq,
+ NULL, bma150_irq_thread,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ BMA150_DRIVER, bma150);
+ if (error) {
+ dev_err(&client->dev,
+ "irq request failed %d, error %d\n",
+ client->irq, error);
+ input_unregister_device(bma150->input);
+ goto err_free_mem;
+ }
+ } else {
+ error = bma150_register_polled_device(bma150);
+ if (error)
+ goto err_free_mem;
+ }
+
+ i2c_set_clientdata(client, bma150);
+
+ pm_runtime_enable(&client->dev);
+
+ return 0;
+
+err_free_mem:
+ kfree(bma150);
+ return error;
+}
+
+static int __devexit bma150_remove(struct i2c_client *client)
+{
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ pm_runtime_disable(&client->dev);
+
+ if (client->irq > 0) {
+ free_irq(client->irq, bma150);
+ input_unregister_device(bma150->input);
+ } else {
+ input_unregister_polled_device(bma150->input_polled);
+ input_free_polled_device(bma150->input_polled);
+ }
+
+ kfree(bma150);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bma150_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static int bma150_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ return bma150_set_mode(bma150, BMA150_MODE_NORMAL);
+}
+#endif
+
+static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL);
+
+static const struct i2c_device_id bma150_id[] = {
+ { "bma150", 0 },
+ { "smb380", 0 },
+ { "bma023", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, bma150_id);
+
+static struct i2c_driver bma150_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = BMA150_DRIVER,
+ .pm = &bma150_pm,
+ },
+ .class = I2C_CLASS_HWMON,
+ .id_table = bma150_id,
+ .probe = bma150_probe,
+ .remove = __devexit_p(bma150_remove),
+};
+
+static int __init BMA150_init(void)
+{
+ return i2c_add_driver(&bma150_driver);
+}
+
+static void __exit BMA150_exit(void)
+{
+ i2c_del_driver(&bma150_driver);
+}
+
+MODULE_AUTHOR("Albert Zhang <xu.zhang@bosch-sensortec.com>");
+MODULE_DESCRIPTION("BMA150 driver");
+MODULE_LICENSE("GPL");
+
+module_init(BMA150_init);
+module_exit(BMA150_exit);
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index b09c7d127219..ab860511f016 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -475,7 +475,7 @@ static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
le16_to_cpu(dev->ctl_req->wIndex),
dev->ctl_data,
USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
- if (error && error != EINTR)
+ if (error < 0 && error != -EINTR)
err("%s: usb_control_msg() failed %d", __func__, error);
}
diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c
index 1633b6342267..80793f1608eb 100644
--- a/drivers/input/misc/cma3000_d0x.c
+++ b/drivers/input/misc/cma3000_d0x.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/input/cma3000.h>
+#include <linux/module.h>
#include "cma3000_d0x.h"
diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c
index 19af682c24fb..7283dd2a1ad3 100644
--- a/drivers/input/misc/dm355evm_keys.c
+++ b/drivers/input/misc/dm355evm_keys.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include <linux/i2c/dm355evm_msp.h>
+#include <linux/module.h>
/*
diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c
index 1f38302a5951..302ab46ce752 100644
--- a/drivers/input/misc/ixp4xx-beeper.c
+++ b/drivers/input/misc/ixp4xx-beeper.c
@@ -111,7 +111,7 @@ static int __devinit ixp4xx_spkr_probe(struct platform_device *dev)
input_dev->event = ixp4xx_spkr_event;
err = request_irq(IRQ_IXP4XX_TIMER2, &ixp4xx_spkr_interrupt,
- IRQF_DISABLED | IRQF_NO_SUSPEND, "ixp4xx-beeper",
+ IRQF_NO_SUSPEND, "ixp4xx-beeper",
(void *) dev->id);
if (err)
goto err_free_device;
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
new file mode 100644
index 000000000000..783597a9a64a
--- /dev/null
+++ b/drivers/input/misc/kxtj9.c
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2011 Kionix, Inc.
+ * Written by Chris Hudson <chudson@kionix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input/kxtj9.h>
+#include <linux/input-polldev.h>
+
+#define NAME "kxtj9"
+#define G_MAX 8000
+/* OUTPUT REGISTERS */
+#define XOUT_L 0x06
+#define WHO_AM_I 0x0F
+/* CONTROL REGISTERS */
+#define INT_REL 0x1A
+#define CTRL_REG1 0x1B
+#define INT_CTRL1 0x1E
+#define DATA_CTRL 0x21
+/* CONTROL REGISTER 1 BITS */
+#define PC1_OFF 0x7F
+#define PC1_ON (1 << 7)
+/* Data ready funtion enable bit: set during probe if using irq mode */
+#define DRDYE (1 << 5)
+/* INTERRUPT CONTROL REGISTER 1 BITS */
+/* Set these during probe if using irq mode */
+#define KXTJ9_IEL (1 << 3)
+#define KXTJ9_IEA (1 << 4)
+#define KXTJ9_IEN (1 << 5)
+/* INPUT_ABS CONSTANTS */
+#define FUZZ 3
+#define FLAT 3
+/* RESUME STATE INDICES */
+#define RES_DATA_CTRL 0
+#define RES_CTRL_REG1 1
+#define RES_INT_CTRL1 2
+#define RESUME_ENTRIES 3
+
+/*
+ * The following table lists the maximum appropriate poll interval for each
+ * available output data rate.
+ */
+static const struct {
+ unsigned int cutoff;
+ u8 mask;
+} kxtj9_odr_table[] = {
+ { 3, ODR800F },
+ { 5, ODR400F },
+ { 10, ODR200F },
+ { 20, ODR100F },
+ { 40, ODR50F },
+ { 80, ODR25F },
+ { 0, ODR12_5F},
+};
+
+struct kxtj9_data {
+ struct i2c_client *client;
+ struct kxtj9_platform_data pdata;
+ struct input_dev *input_dev;
+#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
+ struct input_polled_dev *poll_dev;
+#endif
+ unsigned int last_poll_interval;
+ u8 shift;
+ u8 ctrl_reg1;
+ u8 data_ctrl;
+ u8 int_ctrl;
+};
+
+static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = tj9->client->addr,
+ .flags = tj9->client->flags,
+ .len = 1,
+ .buf = &addr,
+ },
+ {
+ .addr = tj9->client->addr,
+ .flags = tj9->client->flags | I2C_M_RD,
+ .len = len,
+ .buf = data,
+ },
+ };
+
+ return i2c_transfer(tj9->client->adapter, msgs, 2);
+}
+
+static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9)
+{
+ s16 acc_data[3]; /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
+ s16 x, y, z;
+ int err;
+
+ err = kxtj9_i2c_read(tj9, XOUT_L, (u8 *)acc_data, 6);
+ if (err < 0)
+ dev_err(&tj9->client->dev, "accelerometer data read failed\n");
+
+ x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]) >> tj9->shift;
+ y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]) >> tj9->shift;
+ z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]) >> tj9->shift;
+
+ input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x);
+ input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y);
+ input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_z ? -z : z);
+ input_sync(tj9->input_dev);
+}
+
+static irqreturn_t kxtj9_isr(int irq, void *dev)
+{
+ struct kxtj9_data *tj9 = dev;
+ int err;
+
+ /* data ready is the only possible interrupt type */
+ kxtj9_report_acceleration_data(tj9);
+
+ err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+ if (err < 0)
+ dev_err(&tj9->client->dev,
+ "error clearing interrupt status: %d\n", err);
+
+ return IRQ_HANDLED;
+}
+
+static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
+{
+ switch (new_g_range) {
+ case KXTJ9_G_2G:
+ tj9->shift = 4;
+ break;
+ case KXTJ9_G_4G:
+ tj9->shift = 3;
+ break;
+ case KXTJ9_G_8G:
+ tj9->shift = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tj9->ctrl_reg1 &= 0xe7;
+ tj9->ctrl_reg1 |= new_g_range;
+
+ return 0;
+}
+
+static int kxtj9_update_odr(struct kxtj9_data *tj9, unsigned int poll_interval)
+{
+ int err;
+ int i;
+
+ /* Use the lowest ODR that can support the requested poll interval */
+ for (i = 0; i < ARRAY_SIZE(kxtj9_odr_table); i++) {
+ tj9->data_ctrl = kxtj9_odr_table[i].mask;
+ if (poll_interval < kxtj9_odr_table[i].cutoff)
+ break;
+ }
+
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
+ if (err < 0)
+ return err;
+
+ err = i2c_smbus_write_byte_data(tj9->client, DATA_CTRL, tj9->data_ctrl);
+ if (err < 0)
+ return err;
+
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int kxtj9_device_power_on(struct kxtj9_data *tj9)
+{
+ if (tj9->pdata.power_on)
+ return tj9->pdata.power_on();
+
+ return 0;
+}
+
+static void kxtj9_device_power_off(struct kxtj9_data *tj9)
+{
+ int err;
+
+ tj9->ctrl_reg1 &= PC1_OFF;
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+ if (err < 0)
+ dev_err(&tj9->client->dev, "soft power off failed\n");
+
+ if (tj9->pdata.power_off)
+ tj9->pdata.power_off();
+}
+
+static int kxtj9_enable(struct kxtj9_data *tj9)
+{
+ int err;
+
+ err = kxtj9_device_power_on(tj9);
+ if (err < 0)
+ return err;
+
+ /* ensure that PC1 is cleared before updating control registers */
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
+ if (err < 0)
+ return err;
+
+ /* only write INT_CTRL_REG1 if in irq mode */
+ if (tj9->client->irq) {
+ err = i2c_smbus_write_byte_data(tj9->client,
+ INT_CTRL1, tj9->int_ctrl);
+ if (err < 0)
+ return err;
+ }
+
+ err = kxtj9_update_g_range(tj9, tj9->pdata.g_range);
+ if (err < 0)
+ return err;
+
+ /* turn on outputs */
+ tj9->ctrl_reg1 |= PC1_ON;
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+ if (err < 0)
+ return err;
+
+ err = kxtj9_update_odr(tj9, tj9->last_poll_interval);
+ if (err < 0)
+ return err;
+
+ /* clear initial interrupt if in irq mode */
+ if (tj9->client->irq) {
+ err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+ if (err < 0) {
+ dev_err(&tj9->client->dev,
+ "error clearing interrupt: %d\n", err);
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ kxtj9_device_power_off(tj9);
+ return err;
+}
+
+static void kxtj9_disable(struct kxtj9_data *tj9)
+{
+ kxtj9_device_power_off(tj9);
+}
+
+static int kxtj9_input_open(struct input_dev *input)
+{
+ struct kxtj9_data *tj9 = input_get_drvdata(input);
+
+ return kxtj9_enable(tj9);
+}
+
+static void kxtj9_input_close(struct input_dev *dev)
+{
+ struct kxtj9_data *tj9 = input_get_drvdata(dev);
+
+ kxtj9_disable(tj9);
+}
+
+static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
+ struct input_dev *input_dev)
+{
+ __set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
+ input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
+ input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
+
+ input_dev->name = "kxtj9_accel";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &tj9->client->dev;
+}
+
+static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&tj9->client->dev, "input device allocate failed\n");
+ return -ENOMEM;
+ }
+
+ tj9->input_dev = input_dev;
+
+ input_dev->open = kxtj9_input_open;
+ input_dev->close = kxtj9_input_close;
+ input_set_drvdata(input_dev, tj9);
+
+ kxtj9_init_input_device(tj9, input_dev);
+
+ err = input_register_device(tj9->input_dev);
+ if (err) {
+ dev_err(&tj9->client->dev,
+ "unable to register input polled device %s: %d\n",
+ tj9->input_dev->name, err);
+ input_free_device(tj9->input_dev);
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * When IRQ mode is selected, we need to provide an interface to allow the user
+ * to change the output data rate of the part. For consistency, we are using
+ * the set_poll method, which accepts a poll interval in milliseconds, and then
+ * calls update_odr() while passing this value as an argument. In IRQ mode, the
+ * data outputs will not be read AT the requested poll interval, rather, the
+ * lowest ODR that can support the requested interval. The client application
+ * will be responsible for retrieving data from the input node at the desired
+ * interval.
+ */
+
+/* Returns currently selected poll interval (in ms) */
+static ssize_t kxtj9_get_poll(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", tj9->last_poll_interval);
+}
+
+/* Allow users to select a new poll interval (in ms) */
+static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+ struct input_dev *input_dev = tj9->input_dev;
+ unsigned int interval;
+ int error;
+
+ error = kstrtouint(buf, 10, &interval);
+ if (error < 0)
+ return error;
+
+ /* Lock the device to prevent races with open/close (and itself) */
+ mutex_lock(&input_dev->mutex);
+
+ disable_irq(client->irq);
+
+ /*
+ * Set current interval to the greater of the minimum interval or
+ * the requested interval
+ */
+ tj9->last_poll_interval = max(interval, tj9->pdata.min_interval);
+
+ kxtj9_update_odr(tj9, tj9->last_poll_interval);
+
+ enable_irq(client->irq);
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
+
+static struct attribute *kxtj9_attributes[] = {
+ &dev_attr_poll.attr,
+ NULL
+};
+
+static struct attribute_group kxtj9_attribute_group = {
+ .attrs = kxtj9_attributes
+};
+
+
+#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
+static void kxtj9_poll(struct input_polled_dev *dev)
+{
+ struct kxtj9_data *tj9 = dev->private;
+ unsigned int poll_interval = dev->poll_interval;
+
+ kxtj9_report_acceleration_data(tj9);
+
+ if (poll_interval != tj9->last_poll_interval) {
+ kxtj9_update_odr(tj9, poll_interval);
+ tj9->last_poll_interval = poll_interval;
+ }
+}
+
+static void kxtj9_polled_input_open(struct input_polled_dev *dev)
+{
+ struct kxtj9_data *tj9 = dev->private;
+
+ kxtj9_enable(tj9);
+}
+
+static void kxtj9_polled_input_close(struct input_polled_dev *dev)
+{
+ struct kxtj9_data *tj9 = dev->private;
+
+ kxtj9_disable(tj9);
+}
+
+static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+{
+ int err;
+ struct input_polled_dev *poll_dev;
+ poll_dev = input_allocate_polled_device();
+
+ if (!poll_dev) {
+ dev_err(&tj9->client->dev,
+ "Failed to allocate polled device\n");
+ return -ENOMEM;
+ }
+
+ tj9->poll_dev = poll_dev;
+ tj9->input_dev = poll_dev->input;
+
+ poll_dev->private = tj9;
+ poll_dev->poll = kxtj9_poll;
+ poll_dev->open = kxtj9_polled_input_open;
+ poll_dev->close = kxtj9_polled_input_close;
+
+ kxtj9_init_input_device(tj9, poll_dev->input);
+
+ err = input_register_polled_device(poll_dev);
+ if (err) {
+ dev_err(&tj9->client->dev,
+ "Unable to register polled device, err=%d\n", err);
+ input_free_polled_device(poll_dev);
+ return err;
+ }
+
+ return 0;
+}
+
+static void __devexit kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
+{
+ input_unregister_polled_device(tj9->poll_dev);
+ input_free_polled_device(tj9->poll_dev);
+}
+
+#else
+
+static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+{
+ return -ENOSYS;
+}
+
+static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
+{
+}
+
+#endif
+
+static int __devinit kxtj9_verify(struct kxtj9_data *tj9)
+{
+ int retval;
+
+ retval = kxtj9_device_power_on(tj9);
+ if (retval < 0)
+ return retval;
+
+ retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I);
+ if (retval < 0) {
+ dev_err(&tj9->client->dev, "read err int source\n");
+ goto out;
+ }
+
+ retval = retval != 0x06 ? -EIO : 0;
+
+out:
+ kxtj9_device_power_off(tj9);
+ return retval;
+}
+
+static int __devinit kxtj9_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct kxtj9_platform_data *pdata = client->dev.platform_data;
+ struct kxtj9_data *tj9;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "client is not i2c capable\n");
+ return -ENXIO;
+ }
+
+ if (!pdata) {
+ dev_err(&client->dev, "platform data is NULL; exiting\n");
+ return -EINVAL;
+ }
+
+ tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL);
+ if (!tj9) {
+ dev_err(&client->dev,
+ "failed to allocate memory for module data\n");
+ return -ENOMEM;
+ }
+
+ tj9->client = client;
+ tj9->pdata = *pdata;
+
+ if (pdata->init) {
+ err = pdata->init();
+ if (err < 0)
+ goto err_free_mem;
+ }
+
+ err = kxtj9_verify(tj9);
+ if (err < 0) {
+ dev_err(&client->dev, "device not recognized\n");
+ goto err_pdata_exit;
+ }
+
+ i2c_set_clientdata(client, tj9);
+
+ tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
+ tj9->data_ctrl = tj9->pdata.data_odr_init;
+
+ if (client->irq) {
+ /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
+ tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
+ tj9->ctrl_reg1 |= DRDYE;
+
+ err = kxtj9_setup_input_device(tj9);
+ if (err)
+ goto err_pdata_exit;
+
+ err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "kxtj9-irq", tj9);
+ if (err) {
+ dev_err(&client->dev, "request irq failed: %d\n", err);
+ goto err_destroy_input;
+ }
+
+ err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
+ if (err) {
+ dev_err(&client->dev, "sysfs create failed: %d\n", err);
+ goto err_free_irq;
+ }
+
+ } else {
+ err = kxtj9_setup_polled_device(tj9);
+ if (err)
+ goto err_pdata_exit;
+ }
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, tj9);
+err_destroy_input:
+ input_unregister_device(tj9->input_dev);
+err_pdata_exit:
+ if (tj9->pdata.exit)
+ tj9->pdata.exit();
+err_free_mem:
+ kfree(tj9);
+ return err;
+}
+
+static int __devexit kxtj9_remove(struct i2c_client *client)
+{
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+
+ if (client->irq) {
+ sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
+ free_irq(client->irq, tj9);
+ input_unregister_device(tj9->input_dev);
+ } else {
+ kxtj9_teardown_polled_device(tj9);
+ }
+
+ if (tj9->pdata.exit)
+ tj9->pdata.exit();
+
+ kfree(tj9);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int kxtj9_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+ struct input_dev *input_dev = tj9->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ kxtj9_disable(tj9);
+
+ mutex_unlock(&input_dev->mutex);
+ return 0;
+}
+
+static int kxtj9_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+ struct input_dev *input_dev = tj9->input_dev;
+ int retval = 0;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ kxtj9_enable(tj9);
+
+ mutex_unlock(&input_dev->mutex);
+ return retval;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
+
+static const struct i2c_device_id kxtj9_id[] = {
+ { NAME, 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, kxtj9_id);
+
+static struct i2c_driver kxtj9_driver = {
+ .driver = {
+ .name = NAME,
+ .owner = THIS_MODULE,
+ .pm = &kxtj9_pm_ops,
+ },
+ .probe = kxtj9_probe,
+ .remove = __devexit_p(kxtj9_remove),
+ .id_table = kxtj9_id,
+};
+
+static int __init kxtj9_init(void)
+{
+ return i2c_add_driver(&kxtj9_driver);
+}
+module_init(kxtj9_init);
+
+static void __exit kxtj9_exit(void)
+{
+ i2c_del_driver(&kxtj9_driver);
+}
+module_exit(kxtj9_exit);
+
+MODULE_DESCRIPTION("KXTJ9 accelerometer driver");
+MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c
new file mode 100644
index 000000000000..09b052288657
--- /dev/null
+++ b/drivers/input/misc/mc13783-pwrbutton.c
@@ -0,0 +1,282 @@
+/**
+ * Copyright (C) 2011 Philippe Rétornaz
+ *
+ * Based on twl4030-pwrbutton driver by:
+ * Peter De Schrijver <peter.de-schrijver@nokia.com>
+ * Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+struct mc13783_pwrb {
+ struct input_dev *pwr;
+ struct mc13xxx *mc13783;
+#define MC13783_PWRB_B1_POL_INVERT (1 << 0)
+#define MC13783_PWRB_B2_POL_INVERT (1 << 1)
+#define MC13783_PWRB_B3_POL_INVERT (1 << 2)
+ int flags;
+ unsigned short keymap[3];
+};
+
+#define MC13783_REG_INTERRUPT_SENSE_1 5
+#define MC13783_IRQSENSE1_ONOFD1S (1 << 3)
+#define MC13783_IRQSENSE1_ONOFD2S (1 << 4)
+#define MC13783_IRQSENSE1_ONOFD3S (1 << 5)
+
+#define MC13783_REG_POWER_CONTROL_2 15
+#define MC13783_POWER_CONTROL_2_ON1BDBNC 4
+#define MC13783_POWER_CONTROL_2_ON2BDBNC 6
+#define MC13783_POWER_CONTROL_2_ON3BDBNC 8
+#define MC13783_POWER_CONTROL_2_ON1BRSTEN (1 << 1)
+#define MC13783_POWER_CONTROL_2_ON2BRSTEN (1 << 2)
+#define MC13783_POWER_CONTROL_2_ON3BRSTEN (1 << 3)
+
+static irqreturn_t button_irq(int irq, void *_priv)
+{
+ struct mc13783_pwrb *priv = _priv;
+ int val;
+
+ mc13xxx_irq_ack(priv->mc13783, irq);
+ mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val);
+
+ switch (irq) {
+ case MC13783_IRQ_ONOFD1:
+ val = val & MC13783_IRQSENSE1_ONOFD1S ? 1 : 0;
+ if (priv->flags & MC13783_PWRB_B1_POL_INVERT)
+ val ^= 1;
+ input_report_key(priv->pwr, priv->keymap[0], val);
+ break;
+
+ case MC13783_IRQ_ONOFD2:
+ val = val & MC13783_IRQSENSE1_ONOFD2S ? 1 : 0;
+ if (priv->flags & MC13783_PWRB_B2_POL_INVERT)
+ val ^= 1;
+ input_report_key(priv->pwr, priv->keymap[1], val);
+ break;
+
+ case MC13783_IRQ_ONOFD3:
+ val = val & MC13783_IRQSENSE1_ONOFD3S ? 1 : 0;
+ if (priv->flags & MC13783_PWRB_B3_POL_INVERT)
+ val ^= 1;
+ input_report_key(priv->pwr, priv->keymap[2], val);
+ break;
+ }
+
+ input_sync(priv->pwr);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mc13783_pwrbutton_probe(struct platform_device *pdev)
+{
+ const struct mc13xxx_buttons_platform_data *pdata;
+ struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent);
+ struct input_dev *pwr;
+ struct mc13783_pwrb *priv;
+ int err = 0;
+ int reg = 0;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ pwr = input_allocate_device();
+ if (!pwr) {
+ dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ return -ENOMEM;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ err = -ENOMEM;
+ dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ goto free_input_dev;
+ }
+
+ reg |= (pdata->b1on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON1BDBNC;
+ reg |= (pdata->b2on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON2BDBNC;
+ reg |= (pdata->b3on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON3BDBNC;
+
+ priv->pwr = pwr;
+ priv->mc13783 = mc13783;
+
+ mc13xxx_lock(mc13783);
+
+ if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) {
+ priv->keymap[0] = pdata->b1on_key;
+ if (pdata->b1on_key != KEY_RESERVED)
+ __set_bit(pdata->b1on_key, pwr->keybit);
+
+ if (pdata->b1on_flags & MC13783_BUTTON_POL_INVERT)
+ priv->flags |= MC13783_PWRB_B1_POL_INVERT;
+
+ if (pdata->b1on_flags & MC13783_BUTTON_RESET_EN)
+ reg |= MC13783_POWER_CONTROL_2_ON1BRSTEN;
+
+ err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD1,
+ button_irq, "b1on", priv);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't request irq\n");
+ goto free_priv;
+ }
+ }
+
+ if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) {
+ priv->keymap[1] = pdata->b2on_key;
+ if (pdata->b2on_key != KEY_RESERVED)
+ __set_bit(pdata->b2on_key, pwr->keybit);
+
+ if (pdata->b2on_flags & MC13783_BUTTON_POL_INVERT)
+ priv->flags |= MC13783_PWRB_B2_POL_INVERT;
+
+ if (pdata->b2on_flags & MC13783_BUTTON_RESET_EN)
+ reg |= MC13783_POWER_CONTROL_2_ON2BRSTEN;
+
+ err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD2,
+ button_irq, "b2on", priv);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't request irq\n");
+ goto free_irq_b1;
+ }
+ }
+
+ if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) {
+ priv->keymap[2] = pdata->b3on_key;
+ if (pdata->b3on_key != KEY_RESERVED)
+ __set_bit(pdata->b3on_key, pwr->keybit);
+
+ if (pdata->b3on_flags & MC13783_BUTTON_POL_INVERT)
+ priv->flags |= MC13783_PWRB_B3_POL_INVERT;
+
+ if (pdata->b3on_flags & MC13783_BUTTON_RESET_EN)
+ reg |= MC13783_POWER_CONTROL_2_ON3BRSTEN;
+
+ err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD3,
+ button_irq, "b3on", priv);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't request irq: %d\n", err);
+ goto free_irq_b2;
+ }
+ }
+
+ mc13xxx_reg_rmw(mc13783, MC13783_REG_POWER_CONTROL_2, 0x3FE, reg);
+
+ mc13xxx_unlock(mc13783);
+
+ pwr->name = "mc13783_pwrbutton";
+ pwr->phys = "mc13783_pwrbutton/input0";
+ pwr->dev.parent = &pdev->dev;
+
+ pwr->keycode = priv->keymap;
+ pwr->keycodemax = ARRAY_SIZE(priv->keymap);
+ pwr->keycodesize = sizeof(priv->keymap[0]);
+ __set_bit(EV_KEY, pwr->evbit);
+
+ err = input_register_device(pwr);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
+ goto free_irq;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+
+free_irq:
+ mc13xxx_lock(mc13783);
+
+ if (pdata->b3on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD3, priv);
+
+free_irq_b2:
+ if (pdata->b2on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD2, priv);
+
+free_irq_b1:
+ if (pdata->b1on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD1, priv);
+
+free_priv:
+ mc13xxx_unlock(mc13783);
+ kfree(priv);
+
+free_input_dev:
+ input_free_device(pwr);
+
+ return err;
+}
+
+static int __devexit mc13783_pwrbutton_remove(struct platform_device *pdev)
+{
+ struct mc13783_pwrb *priv = platform_get_drvdata(pdev);
+ const struct mc13xxx_buttons_platform_data *pdata;
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ mc13xxx_lock(priv->mc13783);
+
+ if (pdata->b3on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD3, priv);
+ if (pdata->b2on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD2, priv);
+ if (pdata->b1on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD1, priv);
+
+ mc13xxx_unlock(priv->mc13783);
+
+ input_unregister_device(priv->pwr);
+ kfree(priv);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+struct platform_driver mc13783_pwrbutton_driver = {
+ .probe = mc13783_pwrbutton_probe,
+ .remove = __devexit_p(mc13783_pwrbutton_remove),
+ .driver = {
+ .name = "mc13783-pwrbutton",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mc13783_pwrbutton_init(void)
+{
+ return platform_driver_register(&mc13783_pwrbutton_driver);
+}
+module_init(mc13783_pwrbutton_init);
+
+static void __exit mc13783_pwrbutton_exit(void)
+{
+ platform_driver_unregister(&mc13783_pwrbutton_driver);
+}
+module_exit(mc13783_pwrbutton_exit);
+
+MODULE_ALIAS("platform:mc13783-pwrbutton");
+MODULE_DESCRIPTION("MC13783 Power Button");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Philippe Retornaz");
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c
new file mode 100644
index 000000000000..4d60080bb5d5
--- /dev/null
+++ b/drivers/input/misc/mma8450.c
@@ -0,0 +1,264 @@
+/*
+ * Driver for Freescale's 3-Axis Accelerometer MMA8450
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input-polldev.h>
+#include <linux/of_device.h>
+
+#define MMA8450_DRV_NAME "mma8450"
+
+#define MODE_CHANGE_DELAY_MS 100
+#define POLL_INTERVAL 100
+#define POLL_INTERVAL_MAX 500
+
+/* register definitions */
+#define MMA8450_STATUS 0x00
+#define MMA8450_STATUS_ZXYDR 0x08
+
+#define MMA8450_OUT_X8 0x01
+#define MMA8450_OUT_Y8 0x02
+#define MMA8450_OUT_Z8 0x03
+
+#define MMA8450_OUT_X_LSB 0x05
+#define MMA8450_OUT_X_MSB 0x06
+#define MMA8450_OUT_Y_LSB 0x07
+#define MMA8450_OUT_Y_MSB 0x08
+#define MMA8450_OUT_Z_LSB 0x09
+#define MMA8450_OUT_Z_MSB 0x0a
+
+#define MMA8450_XYZ_DATA_CFG 0x16
+
+#define MMA8450_CTRL_REG1 0x38
+#define MMA8450_CTRL_REG2 0x39
+
+/* mma8450 status */
+struct mma8450 {
+ struct i2c_client *client;
+ struct input_polled_dev *idev;
+};
+
+static int mma8450_read(struct mma8450 *m, unsigned off)
+{
+ struct i2c_client *c = m->client;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(c, off);
+ if (ret < 0)
+ dev_err(&c->dev,
+ "failed to read register 0x%02x, error %d\n",
+ off, ret);
+
+ return ret;
+}
+
+static int mma8450_write(struct mma8450 *m, unsigned off, u8 v)
+{
+ struct i2c_client *c = m->client;
+ int error;
+
+ error = i2c_smbus_write_byte_data(c, off, v);
+ if (error < 0) {
+ dev_err(&c->dev,
+ "failed to write to register 0x%02x, error %d\n",
+ off, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int mma8450_read_block(struct mma8450 *m, unsigned off,
+ u8 *buf, size_t size)
+{
+ struct i2c_client *c = m->client;
+ int err;
+
+ err = i2c_smbus_read_i2c_block_data(c, off, size, buf);
+ if (err < 0) {
+ dev_err(&c->dev,
+ "failed to read block data at 0x%02x, error %d\n",
+ MMA8450_OUT_X_LSB, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void mma8450_poll(struct input_polled_dev *dev)
+{
+ struct mma8450 *m = dev->private;
+ int x, y, z;
+ int ret;
+ u8 buf[6];
+
+ ret = mma8450_read(m, MMA8450_STATUS);
+ if (ret < 0)
+ return;
+
+ if (!(ret & MMA8450_STATUS_ZXYDR))
+ return;
+
+ ret = mma8450_read_block(m, MMA8450_OUT_X_LSB, buf, sizeof(buf));
+ if (ret < 0)
+ return;
+
+ x = ((buf[1] << 4) & 0xff0) | (buf[0] & 0xf);
+ y = ((buf[3] << 4) & 0xff0) | (buf[2] & 0xf);
+ z = ((buf[5] << 4) & 0xff0) | (buf[4] & 0xf);
+
+ input_report_abs(dev->input, ABS_X, x);
+ input_report_abs(dev->input, ABS_Y, y);
+ input_report_abs(dev->input, ABS_Z, z);
+ input_sync(dev->input);
+}
+
+/* Initialize the MMA8450 chip */
+static void mma8450_open(struct input_polled_dev *dev)
+{
+ struct mma8450 *m = dev->private;
+ int err;
+
+ /* enable all events from X/Y/Z, no FIFO */
+ err = mma8450_write(m, MMA8450_XYZ_DATA_CFG, 0x07);
+ if (err)
+ return;
+
+ /*
+ * Sleep mode poll rate - 50Hz
+ * System output data rate - 400Hz
+ * Full scale selection - Active, +/- 2G
+ */
+ err = mma8450_write(m, MMA8450_CTRL_REG1, 0x01);
+ if (err < 0)
+ return;
+
+ msleep(MODE_CHANGE_DELAY_MS);
+}
+
+static void mma8450_close(struct input_polled_dev *dev)
+{
+ struct mma8450 *m = dev->private;
+
+ mma8450_write(m, MMA8450_CTRL_REG1, 0x00);
+ mma8450_write(m, MMA8450_CTRL_REG2, 0x01);
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+static int __devinit mma8450_probe(struct i2c_client *c,
+ const struct i2c_device_id *id)
+{
+ struct input_polled_dev *idev;
+ struct mma8450 *m;
+ int err;
+
+ m = kzalloc(sizeof(struct mma8450), GFP_KERNEL);
+ idev = input_allocate_polled_device();
+ if (!m || !idev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ m->client = c;
+ m->idev = idev;
+
+ idev->private = m;
+ idev->input->name = MMA8450_DRV_NAME;
+ idev->input->id.bustype = BUS_I2C;
+ idev->poll = mma8450_poll;
+ idev->poll_interval = POLL_INTERVAL;
+ idev->poll_interval_max = POLL_INTERVAL_MAX;
+ idev->open = mma8450_open;
+ idev->close = mma8450_close;
+
+ __set_bit(EV_ABS, idev->input->evbit);
+ input_set_abs_params(idev->input, ABS_X, -2048, 2047, 32, 32);
+ input_set_abs_params(idev->input, ABS_Y, -2048, 2047, 32, 32);
+ input_set_abs_params(idev->input, ABS_Z, -2048, 2047, 32, 32);
+
+ err = input_register_polled_device(idev);
+ if (err) {
+ dev_err(&c->dev, "failed to register polled input device\n");
+ goto err_free_mem;
+ }
+
+ return 0;
+
+err_free_mem:
+ input_free_polled_device(idev);
+ kfree(m);
+ return err;
+}
+
+static int __devexit mma8450_remove(struct i2c_client *c)
+{
+ struct mma8450 *m = i2c_get_clientdata(c);
+ struct input_polled_dev *idev = m->idev;
+
+ input_unregister_polled_device(idev);
+ input_free_polled_device(idev);
+ kfree(m);
+
+ return 0;
+}
+
+static const struct i2c_device_id mma8450_id[] = {
+ { MMA8450_DRV_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, mma8450_id);
+
+static const struct of_device_id mma8450_dt_ids[] = {
+ { .compatible = "fsl,mma8450", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mma8450_dt_ids);
+
+static struct i2c_driver mma8450_driver = {
+ .driver = {
+ .name = MMA8450_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mma8450_dt_ids,
+ },
+ .probe = mma8450_probe,
+ .remove = __devexit_p(mma8450_remove),
+ .id_table = mma8450_id,
+};
+
+static int __init mma8450_init(void)
+{
+ return i2c_add_driver(&mma8450_driver);
+}
+module_init(mma8450_init);
+
+static void __exit mma8450_exit(void)
+{
+ i2c_del_driver(&mma8450_driver);
+}
+module_exit(mma8450_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
new file mode 100644
index 000000000000..f71dc728da58
--- /dev/null
+++ b/drivers/input/misc/mpu3050.c
@@ -0,0 +1,376 @@
+/*
+ * MPU3050 Tri-axis gyroscope driver
+ *
+ * Copyright (C) 2011 Wistron Co.Ltd
+ * Joseph Lai <joseph_lai@wistron.com>
+ *
+ * Trimmed down by Alan Cox <alan@linux.intel.com> to produce this version
+ *
+ * This is a 'lite' version of the driver, while we consider the right way
+ * to present the other features to user space. In particular it requires the
+ * device has an IRQ, and it only provides an input interface, so is not much
+ * use for device orientation. A fuller version is available from the Meego
+ * tree.
+ *
+ * This program is based on bma023.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+#define MPU3050_CHIP_ID_REG 0x00
+#define MPU3050_CHIP_ID 0x69
+#define MPU3050_XOUT_H 0x1D
+#define MPU3050_PWR_MGM 0x3E
+#define MPU3050_PWR_MGM_POS 6
+#define MPU3050_PWR_MGM_MASK 0x40
+
+#define MPU3050_AUTO_DELAY 1000
+
+#define MPU3050_MIN_VALUE -32768
+#define MPU3050_MAX_VALUE 32767
+
+struct axis_data {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+struct mpu3050_sensor {
+ struct i2c_client *client;
+ struct device *dev;
+ struct input_dev *idev;
+};
+
+/**
+ * mpu3050_xyz_read_reg - read the axes values
+ * @buffer: provide register addr and get register
+ * @length: length of register
+ *
+ * Reads the register values in one transaction or returns a negative
+ * error code on failure.
+ */
+static int mpu3050_xyz_read_reg(struct i2c_client *client,
+ u8 *buffer, int length)
+{
+ /*
+ * Annoying we can't make this const because the i2c layer doesn't
+ * declare input buffers const.
+ */
+ char cmd = MPU3050_XOUT_H;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &cmd,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = buffer,
+ },
+ };
+
+ return i2c_transfer(client->adapter, msg, 2);
+}
+
+/**
+ * mpu3050_read_xyz - get co-ordinates from device
+ * @client: i2c address of sensor
+ * @coords: co-ordinates to update
+ *
+ * Return the converted X Y and Z co-ordinates from the sensor device
+ */
+static void mpu3050_read_xyz(struct i2c_client *client,
+ struct axis_data *coords)
+{
+ u16 buffer[3];
+
+ mpu3050_xyz_read_reg(client, (u8 *)buffer, 6);
+ coords->x = be16_to_cpu(buffer[0]);
+ coords->y = be16_to_cpu(buffer[1]);
+ coords->z = be16_to_cpu(buffer[2]);
+ dev_dbg(&client->dev, "%s: x %d, y %d, z %d\n", __func__,
+ coords->x, coords->y, coords->z);
+}
+
+/**
+ * mpu3050_set_power_mode - set the power mode
+ * @client: i2c client for the sensor
+ * @val: value to switch on/off of power, 1: normal power, 0: low power
+ *
+ * Put device to normal-power mode or low-power mode.
+ */
+static void mpu3050_set_power_mode(struct i2c_client *client, u8 val)
+{
+ u8 value;
+
+ value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
+ value = (value & ~MPU3050_PWR_MGM_MASK) |
+ (((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^
+ MPU3050_PWR_MGM_MASK);
+ i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, value);
+}
+
+/**
+ * mpu3050_input_open - called on input event open
+ * @input: input dev of opened device
+ *
+ * The input layer calls this function when input event is opened. The
+ * function will push the device to resume. Then, the device is ready
+ * to provide data.
+ */
+static int mpu3050_input_open(struct input_dev *input)
+{
+ struct mpu3050_sensor *sensor = input_get_drvdata(input);
+
+ pm_runtime_get(sensor->dev);
+
+ return 0;
+}
+
+/**
+ * mpu3050_input_close - called on input event close
+ * @input: input dev of closed device
+ *
+ * The input layer calls this function when input event is closed. The
+ * function will push the device to suspend.
+ */
+static void mpu3050_input_close(struct input_dev *input)
+{
+ struct mpu3050_sensor *sensor = input_get_drvdata(input);
+
+ pm_runtime_put(sensor->dev);
+}
+
+/**
+ * mpu3050_interrupt_thread - handle an IRQ
+ * @irq: interrupt numner
+ * @data: the sensor
+ *
+ * Called by the kernel single threaded after an interrupt occurs. Read
+ * the sensor data and generate an input event for it.
+ */
+static irqreturn_t mpu3050_interrupt_thread(int irq, void *data)
+{
+ struct mpu3050_sensor *sensor = data;
+ struct axis_data axis;
+
+ mpu3050_read_xyz(sensor->client, &axis);
+
+ input_report_abs(sensor->idev, ABS_X, axis.x);
+ input_report_abs(sensor->idev, ABS_Y, axis.y);
+ input_report_abs(sensor->idev, ABS_Z, axis.z);
+ input_sync(sensor->idev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * mpu3050_probe - device detection callback
+ * @client: i2c client of found device
+ * @id: id match information
+ *
+ * The I2C layer calls us when it believes a sensor is present at this
+ * address. Probe to see if this is correct and to validate the device.
+ *
+ * If present install the relevant sysfs interfaces and input device.
+ */
+static int __devinit mpu3050_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mpu3050_sensor *sensor;
+ struct input_dev *idev;
+ int ret;
+ int error;
+
+ sensor = kzalloc(sizeof(struct mpu3050_sensor), GFP_KERNEL);
+ idev = input_allocate_device();
+ if (!sensor || !idev) {
+ dev_err(&client->dev, "failed to allocate driver data\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ sensor->client = client;
+ sensor->dev = &client->dev;
+ sensor->idev = idev;
+
+ mpu3050_set_power_mode(client, 1);
+ msleep(10);
+
+ ret = i2c_smbus_read_byte_data(client, MPU3050_CHIP_ID_REG);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to detect device\n");
+ error = -ENXIO;
+ goto err_free_mem;
+ }
+
+ if (ret != MPU3050_CHIP_ID) {
+ dev_err(&client->dev, "unsupported chip id\n");
+ error = -ENXIO;
+ goto err_free_mem;
+ }
+
+ idev->name = "MPU3050";
+ idev->id.bustype = BUS_I2C;
+ idev->dev.parent = &client->dev;
+
+ idev->open = mpu3050_input_open;
+ idev->close = mpu3050_input_close;
+
+ __set_bit(EV_ABS, idev->evbit);
+ input_set_abs_params(idev, ABS_X,
+ MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+ input_set_abs_params(idev, ABS_Y,
+ MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+ input_set_abs_params(idev, ABS_Z,
+ MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+
+ input_set_drvdata(idev, sensor);
+
+ pm_runtime_set_active(&client->dev);
+
+ error = request_threaded_irq(client->irq,
+ NULL, mpu3050_interrupt_thread,
+ IRQF_TRIGGER_RISING,
+ "mpu_int", sensor);
+ if (error) {
+ dev_err(&client->dev,
+ "can't get IRQ %d, error %d\n", client->irq, error);
+ goto err_pm_set_suspended;
+ }
+
+ error = input_register_device(idev);
+ if (error) {
+ dev_err(&client->dev, "failed to register input device\n");
+ goto err_free_irq;
+ }
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY);
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, sensor);
+err_pm_set_suspended:
+ pm_runtime_set_suspended(&client->dev);
+err_free_mem:
+ input_free_device(idev);
+ kfree(sensor);
+ return error;
+}
+
+/**
+ * mpu3050_remove - remove a sensor
+ * @client: i2c client of sensor being removed
+ *
+ * Our sensor is going away, clean up the resources.
+ */
+static int __devexit mpu3050_remove(struct i2c_client *client)
+{
+ struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ free_irq(client->irq, sensor);
+ input_unregister_device(sensor->idev);
+ kfree(sensor);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * mpu3050_suspend - called on device suspend
+ * @dev: device being suspended
+ *
+ * Put the device into sleep mode before we suspend the machine.
+ */
+static int mpu3050_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ mpu3050_set_power_mode(client, 0);
+
+ return 0;
+}
+
+/**
+ * mpu3050_resume - called on device resume
+ * @dev: device being resumed
+ *
+ * Put the device into powered mode on resume.
+ */
+static int mpu3050_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ mpu3050_set_power_mode(client, 1);
+ msleep(100); /* wait for gyro chip resume */
+
+ return 0;
+}
+#endif
+
+static UNIVERSAL_DEV_PM_OPS(mpu3050_pm, mpu3050_suspend, mpu3050_resume, NULL);
+
+static const struct i2c_device_id mpu3050_ids[] = {
+ { "mpu3050", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mpu3050_ids);
+
+static struct i2c_driver mpu3050_i2c_driver = {
+ .driver = {
+ .name = "mpu3050",
+ .owner = THIS_MODULE,
+ .pm = &mpu3050_pm,
+ },
+ .probe = mpu3050_probe,
+ .remove = __devexit_p(mpu3050_remove),
+ .id_table = mpu3050_ids,
+};
+
+static int __init mpu3050_init(void)
+{
+ return i2c_add_driver(&mpu3050_i2c_driver);
+}
+module_init(mpu3050_init);
+
+static void __exit mpu3050_exit(void)
+{
+ i2c_del_driver(&mpu3050_i2c_driver);
+}
+module_exit(mpu3050_exit);
+
+MODULE_AUTHOR("Wistron Corp.");
+MODULE_DESCRIPTION("MPU3050 Tri-axis gyroscope driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c
index f080dd31499b..34f4d2e0f50f 100644
--- a/drivers/input/misc/pcspkr.c
+++ b/drivers/input/misc/pcspkr.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/i8253.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/platform_device.h>
@@ -25,14 +26,6 @@ MODULE_DESCRIPTION("PC Speaker beeper driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pcspkr");
-#if defined(CONFIG_MIPS) || defined(CONFIG_X86)
-/* Use the global PIT lock ! */
-#include <asm/i8253.h>
-#else
-#include <asm/8253pit.h>
-static DEFINE_RAW_SPINLOCK(i8253_lock);
-#endif
-
static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
unsigned int count = 0;
diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c
new file mode 100644
index 000000000000..43192930824b
--- /dev/null
+++ b/drivers/input/misc/pm8xxx-vibrator.c
@@ -0,0 +1,296 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/core.h>
+
+#define VIB_DRV 0x4A
+
+#define VIB_DRV_SEL_MASK 0xf8
+#define VIB_DRV_SEL_SHIFT 0x03
+#define VIB_DRV_EN_MANUAL_MASK 0xfc
+
+#define VIB_MAX_LEVEL_mV (3100)
+#define VIB_MIN_LEVEL_mV (1200)
+#define VIB_MAX_LEVELS (VIB_MAX_LEVEL_mV - VIB_MIN_LEVEL_mV)
+
+#define MAX_FF_SPEED 0xff
+
+/**
+ * struct pm8xxx_vib - structure to hold vibrator data
+ * @vib_input_dev: input device supporting force feedback
+ * @work: work structure to set the vibration parameters
+ * @dev: device supporting force feedback
+ * @speed: speed of vibration set from userland
+ * @active: state of vibrator
+ * @level: level of vibration to set in the chip
+ * @reg_vib_drv: VIB_DRV register value
+ */
+struct pm8xxx_vib {
+ struct input_dev *vib_input_dev;
+ struct work_struct work;
+ struct device *dev;
+ int speed;
+ int level;
+ bool active;
+ u8 reg_vib_drv;
+};
+
+/**
+ * pm8xxx_vib_read_u8 - helper to read a byte from pmic chip
+ * @vib: pointer to vibrator structure
+ * @data: placeholder for data to be read
+ * @reg: register address
+ */
+static int pm8xxx_vib_read_u8(struct pm8xxx_vib *vib,
+ u8 *data, u16 reg)
+{
+ int rc;
+
+ rc = pm8xxx_readb(vib->dev->parent, reg, data);
+ if (rc < 0)
+ dev_warn(vib->dev, "Error reading pm8xxx reg 0x%x(0x%x)\n",
+ reg, rc);
+ return rc;
+}
+
+/**
+ * pm8xxx_vib_write_u8 - helper to write a byte to pmic chip
+ * @vib: pointer to vibrator structure
+ * @data: data to write
+ * @reg: register address
+ */
+static int pm8xxx_vib_write_u8(struct pm8xxx_vib *vib,
+ u8 data, u16 reg)
+{
+ int rc;
+
+ rc = pm8xxx_writeb(vib->dev->parent, reg, data);
+ if (rc < 0)
+ dev_warn(vib->dev, "Error writing pm8xxx reg 0x%x(0x%x)\n",
+ reg, rc);
+ return rc;
+}
+
+/**
+ * pm8xxx_vib_set - handler to start/stop vibration
+ * @vib: pointer to vibrator structure
+ * @on: state to set
+ */
+static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on)
+{
+ int rc;
+ u8 val = vib->reg_vib_drv;
+
+ if (on)
+ val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK);
+ else
+ val &= ~VIB_DRV_SEL_MASK;
+
+ rc = pm8xxx_vib_write_u8(vib, val, VIB_DRV);
+ if (rc < 0)
+ return rc;
+
+ vib->reg_vib_drv = val;
+ return 0;
+}
+
+/**
+ * pm8xxx_work_handler - worker to set vibration level
+ * @work: pointer to work_struct
+ */
+static void pm8xxx_work_handler(struct work_struct *work)
+{
+ struct pm8xxx_vib *vib = container_of(work, struct pm8xxx_vib, work);
+ int rc;
+ u8 val;
+
+ rc = pm8xxx_vib_read_u8(vib, &val, VIB_DRV);
+ if (rc < 0)
+ return;
+
+ /*
+ * pmic vibrator supports voltage ranges from 1.2 to 3.1V, so
+ * scale the level to fit into these ranges.
+ */
+ if (vib->speed) {
+ vib->active = true;
+ vib->level = ((VIB_MAX_LEVELS * vib->speed) / MAX_FF_SPEED) +
+ VIB_MIN_LEVEL_mV;
+ vib->level /= 100;
+ } else {
+ vib->active = false;
+ vib->level = VIB_MIN_LEVEL_mV / 100;
+ }
+
+ pm8xxx_vib_set(vib, vib->active);
+}
+
+/**
+ * pm8xxx_vib_close - callback of input close callback
+ * @dev: input device pointer
+ *
+ * Turns off the vibrator.
+ */
+static void pm8xxx_vib_close(struct input_dev *dev)
+{
+ struct pm8xxx_vib *vib = input_get_drvdata(dev);
+
+ cancel_work_sync(&vib->work);
+ if (vib->active)
+ pm8xxx_vib_set(vib, false);
+}
+
+/**
+ * pm8xxx_vib_play_effect - function to handle vib effects.
+ * @dev: input device pointer
+ * @data: data of effect
+ * @effect: effect to play
+ *
+ * Currently this driver supports only rumble effects.
+ */
+static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct pm8xxx_vib *vib = input_get_drvdata(dev);
+
+ vib->speed = effect->u.rumble.strong_magnitude >> 8;
+ if (!vib->speed)
+ vib->speed = effect->u.rumble.weak_magnitude >> 9;
+
+ schedule_work(&vib->work);
+
+ return 0;
+}
+
+static int __devinit pm8xxx_vib_probe(struct platform_device *pdev)
+
+{
+ struct pm8xxx_vib *vib;
+ struct input_dev *input_dev;
+ int error;
+ u8 val;
+
+ vib = kzalloc(sizeof(*vib), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!vib || !input_dev) {
+ dev_err(&pdev->dev, "couldn't allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ INIT_WORK(&vib->work, pm8xxx_work_handler);
+ vib->dev = &pdev->dev;
+ vib->vib_input_dev = input_dev;
+
+ /* operate in manual mode */
+ error = pm8xxx_vib_read_u8(vib, &val, VIB_DRV);
+ if (error < 0)
+ goto err_free_mem;
+ val &= ~VIB_DRV_EN_MANUAL_MASK;
+ error = pm8xxx_vib_write_u8(vib, val, VIB_DRV);
+ if (error < 0)
+ goto err_free_mem;
+
+ vib->reg_vib_drv = val;
+
+ input_dev->name = "pm8xxx_vib_ffmemless";
+ input_dev->id.version = 1;
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->close = pm8xxx_vib_close;
+ input_set_drvdata(input_dev, vib);
+ input_set_capability(vib->vib_input_dev, EV_FF, FF_RUMBLE);
+
+ error = input_ff_create_memless(input_dev, NULL,
+ pm8xxx_vib_play_effect);
+ if (error) {
+ dev_err(&pdev->dev,
+ "couldn't register vibrator as FF device\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "couldn't register input device\n");
+ goto err_destroy_memless;
+ }
+
+ platform_set_drvdata(pdev, vib);
+ return 0;
+
+err_destroy_memless:
+ input_ff_destroy(input_dev);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(vib);
+
+ return error;
+}
+
+static int __devexit pm8xxx_vib_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_vib *vib = platform_get_drvdata(pdev);
+
+ input_unregister_device(vib->vib_input_dev);
+ kfree(vib);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pm8xxx_vib_suspend(struct device *dev)
+{
+ struct pm8xxx_vib *vib = dev_get_drvdata(dev);
+
+ /* Turn off the vibrator */
+ pm8xxx_vib_set(vib, false);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL);
+
+static struct platform_driver pm8xxx_vib_driver = {
+ .probe = pm8xxx_vib_probe,
+ .remove = __devexit_p(pm8xxx_vib_remove),
+ .driver = {
+ .name = "pm8xxx-vib",
+ .owner = THIS_MODULE,
+ .pm = &pm8xxx_vib_pm_ops,
+ },
+};
+
+static int __init pm8xxx_vib_init(void)
+{
+ return platform_driver_register(&pm8xxx_vib_driver);
+}
+module_init(pm8xxx_vib_init);
+
+static void __exit pm8xxx_vib_exit(void)
+{
+ platform_driver_unregister(&pm8xxx_vib_driver);
+}
+module_exit(pm8xxx_vib_exit);
+
+MODULE_ALIAS("platform:pm8xxx_vib");
+MODULE_DESCRIPTION("PMIC8xxx vibrator driver based on ff-memless framework");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Amy Maloche <amaloche@codeaurora.org>");
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
index 2c8b84dd9dac..2be21694fac1 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -7,7 +7,7 @@
* state machine code inspired by code from Tim Ruetz
*
* A generic driver for rotary encoders connected to GPIO lines.
- * See file:Documentation/input/rotary_encoder.txt for more information
+ * See file:Documentation/input/rotary-encoder.txt for more information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
index 014dd4ad0d4f..3c1a432c14dc 100644
--- a/drivers/input/misc/twl4030-vibra.c
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -28,7 +28,7 @@
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/i2c/twl.h>
-#include <linux/mfd/twl4030-codec.h>
+#include <linux/mfd/twl4030-audio.h>
#include <linux/input.h>
#include <linux/slab.h>
@@ -67,7 +67,7 @@ static void vibra_enable(struct vibra_info *info)
{
u8 reg;
- twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
+ twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
/* turn H-Bridge on */
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
@@ -75,7 +75,7 @@ static void vibra_enable(struct vibra_info *info)
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
(reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
- twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
+ twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL);
info->enabled = true;
}
@@ -90,8 +90,8 @@ static void vibra_disable(struct vibra_info *info)
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
(reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
- twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
- twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
+ twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL);
+ twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
info->enabled = false;
}
@@ -196,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
{
- struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data;
+ struct twl4030_vibra_data *pdata = pdev->dev.platform_data;
struct vibra_info *info;
int ret;
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c
new file mode 100644
index 000000000000..ad153a417eed
--- /dev/null
+++ b/drivers/input/misc/twl6040-vibra.c
@@ -0,0 +1,430 @@
+/*
+ * twl6040-vibra.c - TWL6040 Vibrator driver
+ *
+ * Author: Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
+ * Author: Misael Lopez Cruz <misael.lopez@ti.com>
+ *
+ * Copyright: (C) 2011 Texas Instruments, Inc.
+ *
+ * Based on twl4030-vibra.c by Henrik Saari <henrik.saari@nokia.com>
+ * Felipe Balbi <felipe.balbi@nokia.com>
+ * Jari Vanhala <ext-javi.vanhala@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/twl6040.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#define EFFECT_DIR_180_DEG 0x8000
+
+/* Recommended modulation index 85% */
+#define TWL6040_VIBRA_MOD 85
+
+#define TWL6040_NUM_SUPPLIES 2
+
+struct vibra_info {
+ struct device *dev;
+ struct input_dev *input_dev;
+ struct workqueue_struct *workqueue;
+ struct work_struct play_work;
+ struct mutex mutex;
+ int irq;
+
+ bool enabled;
+ int weak_speed;
+ int strong_speed;
+ int direction;
+
+ unsigned int vibldrv_res;
+ unsigned int vibrdrv_res;
+ unsigned int viblmotor_res;
+ unsigned int vibrmotor_res;
+
+ struct regulator_bulk_data supplies[TWL6040_NUM_SUPPLIES];
+
+ struct twl6040 *twl6040;
+};
+
+static irqreturn_t twl6040_vib_irq_handler(int irq, void *data)
+{
+ struct vibra_info *info = data;
+ struct twl6040 *twl6040 = info->twl6040;
+ u8 status;
+
+ status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
+ if (status & TWL6040_VIBLOCDET) {
+ dev_warn(info->dev, "Left Vibrator overcurrent detected\n");
+ twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLL,
+ TWL6040_VIBENA);
+ }
+ if (status & TWL6040_VIBROCDET) {
+ dev_warn(info->dev, "Right Vibrator overcurrent detected\n");
+ twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLR,
+ TWL6040_VIBENA);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void twl6040_vibra_enable(struct vibra_info *info)
+{
+ struct twl6040 *twl6040 = info->twl6040;
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(info->supplies), info->supplies);
+ if (ret) {
+ dev_err(info->dev, "failed to enable regulators %d\n", ret);
+ return;
+ }
+
+ twl6040_power(info->twl6040, 1);
+ if (twl6040_get_revid(twl6040) <= TWL6040_REV_ES1_1) {
+ /*
+ * ERRATA: Disable overcurrent protection for at least
+ * 3ms when enabling vibrator drivers to avoid false
+ * overcurrent detection
+ */
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL,
+ TWL6040_VIBENA | TWL6040_VIBCTRL);
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR,
+ TWL6040_VIBENA | TWL6040_VIBCTRL);
+ usleep_range(3000, 3500);
+ }
+
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL,
+ TWL6040_VIBENA);
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR,
+ TWL6040_VIBENA);
+
+ info->enabled = true;
+}
+
+static void twl6040_vibra_disable(struct vibra_info *info)
+{
+ struct twl6040 *twl6040 = info->twl6040;
+
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, 0x00);
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, 0x00);
+ twl6040_power(info->twl6040, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(info->supplies), info->supplies);
+
+ info->enabled = false;
+}
+
+static u8 twl6040_vibra_code(int vddvib, int vibdrv_res, int motor_res,
+ int speed, int direction)
+{
+ int vpk, max_code;
+ u8 vibdat;
+
+ /* output swing */
+ vpk = (vddvib * motor_res * TWL6040_VIBRA_MOD) /
+ (100 * (vibdrv_res + motor_res));
+
+ /* 50mV per VIBDAT code step */
+ max_code = vpk / 50;
+ if (max_code > TWL6040_VIBDAT_MAX)
+ max_code = TWL6040_VIBDAT_MAX;
+
+ /* scale speed to max allowed code */
+ vibdat = (u8)((speed * max_code) / USHRT_MAX);
+
+ /* 2's complement for direction > 180 degrees */
+ vibdat *= direction;
+
+ return vibdat;
+}
+
+static void twl6040_vibra_set_effect(struct vibra_info *info)
+{
+ struct twl6040 *twl6040 = info->twl6040;
+ u8 vibdatl, vibdatr;
+ int volt;
+
+ /* weak motor */
+ volt = regulator_get_voltage(info->supplies[0].consumer) / 1000;
+ vibdatl = twl6040_vibra_code(volt, info->vibldrv_res,
+ info->viblmotor_res,
+ info->weak_speed, info->direction);
+
+ /* strong motor */
+ volt = regulator_get_voltage(info->supplies[1].consumer) / 1000;
+ vibdatr = twl6040_vibra_code(volt, info->vibrdrv_res,
+ info->vibrmotor_res,
+ info->strong_speed, info->direction);
+
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBDATL, vibdatl);
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBDATR, vibdatr);
+}
+
+static void vibra_play_work(struct work_struct *work)
+{
+ struct vibra_info *info = container_of(work,
+ struct vibra_info, play_work);
+
+ mutex_lock(&info->mutex);
+
+ if (info->weak_speed || info->strong_speed) {
+ if (!info->enabled)
+ twl6040_vibra_enable(info);
+
+ twl6040_vibra_set_effect(info);
+ } else if (info->enabled)
+ twl6040_vibra_disable(info);
+
+ mutex_unlock(&info->mutex);
+}
+
+static int vibra_play(struct input_dev *input, void *data,
+ struct ff_effect *effect)
+{
+ struct vibra_info *info = input_get_drvdata(input);
+ int ret;
+
+ /* Do not allow effect, while the routing is set to use audio */
+ ret = twl6040_get_vibralr_status(info->twl6040);
+ if (ret & TWL6040_VIBSEL) {
+ dev_info(&input->dev, "Vibra is configured for audio\n");
+ return -EBUSY;
+ }
+
+ info->weak_speed = effect->u.rumble.weak_magnitude;
+ info->strong_speed = effect->u.rumble.strong_magnitude;
+ info->direction = effect->direction < EFFECT_DIR_180_DEG ? 1 : -1;
+
+ ret = queue_work(info->workqueue, &info->play_work);
+ if (!ret) {
+ dev_info(&input->dev, "work is already on queue\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void twl6040_vibra_close(struct input_dev *input)
+{
+ struct vibra_info *info = input_get_drvdata(input);
+
+ cancel_work_sync(&info->play_work);
+
+ mutex_lock(&info->mutex);
+
+ if (info->enabled)
+ twl6040_vibra_disable(info);
+
+ mutex_unlock(&info->mutex);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int twl6040_vibra_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vibra_info *info = platform_get_drvdata(pdev);
+
+ mutex_lock(&info->mutex);
+
+ if (info->enabled)
+ twl6040_vibra_disable(info);
+
+ mutex_unlock(&info->mutex);
+
+ return 0;
+}
+
+#endif
+
+static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL);
+
+static int __devinit twl6040_vibra_probe(struct platform_device *pdev)
+{
+ struct twl4030_vibra_data *pdata = pdev->dev.platform_data;
+ struct vibra_info *info;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "platform_data not available\n");
+ return -EINVAL;
+ }
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(&pdev->dev, "couldn't allocate memory\n");
+ return -ENOMEM;
+ }
+
+ info->dev = &pdev->dev;
+ info->twl6040 = dev_get_drvdata(pdev->dev.parent);
+ info->vibldrv_res = pdata->vibldrv_res;
+ info->vibrdrv_res = pdata->vibrdrv_res;
+ info->viblmotor_res = pdata->viblmotor_res;
+ info->vibrmotor_res = pdata->vibrmotor_res;
+ if ((!info->vibldrv_res && !info->viblmotor_res) ||
+ (!info->vibrdrv_res && !info->vibrmotor_res)) {
+ dev_err(info->dev, "invalid vibra driver/motor resistance\n");
+ ret = -EINVAL;
+ goto err_kzalloc;
+ }
+
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(info->dev, "invalid irq\n");
+ ret = -EINVAL;
+ goto err_kzalloc;
+ }
+
+ mutex_init(&info->mutex);
+
+ info->input_dev = input_allocate_device();
+ if (info->input_dev == NULL) {
+ dev_err(info->dev, "couldn't allocate input device\n");
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ input_set_drvdata(info->input_dev, info);
+
+ info->input_dev->name = "twl6040:vibrator";
+ info->input_dev->id.version = 1;
+ info->input_dev->dev.parent = pdev->dev.parent;
+ info->input_dev->close = twl6040_vibra_close;
+ __set_bit(FF_RUMBLE, info->input_dev->ffbit);
+
+ ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+ if (ret < 0) {
+ dev_err(info->dev, "couldn't register vibrator to FF\n");
+ goto err_ialloc;
+ }
+
+ ret = input_register_device(info->input_dev);
+ if (ret < 0) {
+ dev_err(info->dev, "couldn't register input device\n");
+ goto err_iff;
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0,
+ "twl6040_irq_vib", info);
+ if (ret) {
+ dev_err(info->dev, "VIB IRQ request failed: %d\n", ret);
+ goto err_irq;
+ }
+
+ info->supplies[0].supply = "vddvibl";
+ info->supplies[1].supply = "vddvibr";
+ ret = regulator_bulk_get(info->dev, ARRAY_SIZE(info->supplies),
+ info->supplies);
+ if (ret) {
+ dev_err(info->dev, "couldn't get regulators %d\n", ret);
+ goto err_regulator;
+ }
+
+ if (pdata->vddvibl_uV) {
+ ret = regulator_set_voltage(info->supplies[0].consumer,
+ pdata->vddvibl_uV,
+ pdata->vddvibl_uV);
+ if (ret) {
+ dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
+ ret);
+ goto err_voltage;
+ }
+ }
+
+ if (pdata->vddvibr_uV) {
+ ret = regulator_set_voltage(info->supplies[1].consumer,
+ pdata->vddvibr_uV,
+ pdata->vddvibr_uV);
+ if (ret) {
+ dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
+ ret);
+ goto err_voltage;
+ }
+ }
+
+ info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0);
+ if (info->workqueue == NULL) {
+ dev_err(info->dev, "couldn't create workqueue\n");
+ ret = -ENOMEM;
+ goto err_voltage;
+ }
+ INIT_WORK(&info->play_work, vibra_play_work);
+
+ return 0;
+
+err_voltage:
+ regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
+err_regulator:
+ free_irq(info->irq, info);
+err_irq:
+ input_unregister_device(info->input_dev);
+ info->input_dev = NULL;
+err_iff:
+ if (info->input_dev)
+ input_ff_destroy(info->input_dev);
+err_ialloc:
+ input_free_device(info->input_dev);
+err_kzalloc:
+ kfree(info);
+ return ret;
+}
+
+static int __devexit twl6040_vibra_remove(struct platform_device *pdev)
+{
+ struct vibra_info *info = platform_get_drvdata(pdev);
+
+ input_unregister_device(info->input_dev);
+ free_irq(info->irq, info);
+ regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
+ destroy_workqueue(info->workqueue);
+ kfree(info);
+
+ return 0;
+}
+
+static struct platform_driver twl6040_vibra_driver = {
+ .probe = twl6040_vibra_probe,
+ .remove = __devexit_p(twl6040_vibra_remove),
+ .driver = {
+ .name = "twl6040-vibra",
+ .owner = THIS_MODULE,
+ .pm = &twl6040_vibra_pm_ops,
+ },
+};
+
+static int __init twl6040_vibra_init(void)
+{
+ return platform_driver_register(&twl6040_vibra_driver);
+}
+module_init(twl6040_vibra_init);
+
+static void __exit twl6040_vibra_exit(void)
+{
+ platform_driver_unregister(&twl6040_vibra_driver);
+}
+module_exit(twl6040_vibra_exit);
+
+MODULE_ALIAS("platform:twl6040-vibra");
+MODULE_DESCRIPTION("TWL6040 Vibra driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
+MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
index 62bae99424e6..ad2e51c04db8 100644
--- a/drivers/input/misc/xen-kbdfront.c
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -373,7 +373,7 @@ static struct xenbus_driver xenkbd_driver = {
static int __init xenkbd_init(void)
{
- if (!xen_pv_domain())
+ if (!xen_domain())
return -ENODEV;
/* Nothing to do if running in dom0. */
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 99d58764ef03..003587c71f43 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -23,13 +23,6 @@
#include "psmouse.h"
#include "alps.h"
-#undef DEBUG
-#ifdef DEBUG
-#define dbg(format, arg...) printk(KERN_INFO "alps.c: " format "\n", ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-
#define ALPS_OLDPROTO 0x01 /* old style input */
#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */
#define ALPS_PASS 0x04 /* device has a pass-through port */
@@ -297,10 +290,10 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
psmouse->packet[4] |
psmouse->packet[5]) & 0x80) ||
(!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) {
- dbg("refusing packet %x %x %x %x "
- "(suspected interleaved ps/2)\n",
- psmouse->packet[3], psmouse->packet[4],
- psmouse->packet[5], psmouse->packet[6]);
+ psmouse_dbg(psmouse,
+ "refusing packet %x %x %x %x (suspected interleaved ps/2)\n",
+ psmouse->packet[3], psmouse->packet[4],
+ psmouse->packet[5], psmouse->packet[6]);
return PSMOUSE_BAD_DATA;
}
@@ -319,13 +312,13 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
* There is also possibility that we got 6-byte ALPS
* packet followed by 3-byte packet from trackpoint. We
* can not distinguish between these 2 scenarios but
- * becase the latter is unlikely to happen in course of
+ * because the latter is unlikely to happen in course of
* normal operation (user would need to press all
* buttons on the pad and start moving trackpoint
* without touching the pad surface) we assume former.
* Even if we are wrong the wost thing that would happen
* the cursor would jump but we should not get protocol
- * desynchronization.
+ * de-synchronization.
*/
alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3],
@@ -361,10 +354,10 @@ static void alps_flush_packet(unsigned long data)
if ((psmouse->packet[3] |
psmouse->packet[4] |
psmouse->packet[5]) & 0x80) {
- dbg("refusing packet %x %x %x "
- "(suspected interleaved ps/2)\n",
- psmouse->packet[3], psmouse->packet[4],
- psmouse->packet[5]);
+ psmouse_dbg(psmouse,
+ "refusing packet %x %x %x (suspected interleaved ps/2)\n",
+ psmouse->packet[3], psmouse->packet[4],
+ psmouse->packet[5]);
} else {
alps_process_packet(psmouse);
}
@@ -396,16 +389,18 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
}
if (!alps_is_valid_first_byte(model, psmouse->packet[0])) {
- dbg("refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n",
- psmouse->packet[0], model->mask0, model->byte0);
+ psmouse_dbg(psmouse,
+ "refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n",
+ psmouse->packet[0], model->mask0, model->byte0);
return PSMOUSE_BAD_DATA;
}
/* Bytes 2 - 6 should have 0 in the highest bit */
if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 &&
(psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
- dbg("refusing packet[%i] = %x\n",
- psmouse->pktcnt - 1, psmouse->packet[psmouse->pktcnt - 1]);
+ psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
+ psmouse->pktcnt - 1,
+ psmouse->packet[psmouse->pktcnt - 1]);
return PSMOUSE_BAD_DATA;
}
@@ -439,7 +434,8 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return NULL;
- dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+ psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x",
+ param[0], param[1], param[2]);
if (param[0] != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100))
return NULL;
@@ -459,7 +455,8 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return NULL;
- dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+ psmouse_dbg(psmouse, "E7 report: %2.2x %2.2x %2.2x",
+ param[0], param[1], param[2]);
if (version) {
for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++)
@@ -527,7 +524,8 @@ static int alps_get_status(struct psmouse *psmouse, char *param)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
- dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+ psmouse_dbg(psmouse, "Status: %2.2x %2.2x %2.2x",
+ param[0], param[1], param[2]);
return 0;
}
@@ -605,12 +603,12 @@ static int alps_hw_init(struct psmouse *psmouse)
}
if (alps_tap_mode(psmouse, true)) {
- printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n");
+ psmouse_warn(psmouse, "Failed to enable hardware tapping\n");
return -1;
}
if (alps_absolute_mode(psmouse)) {
- printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
+ psmouse_err(psmouse, "Failed to enable absolute mode\n");
return -1;
}
@@ -621,7 +619,7 @@ static int alps_hw_init(struct psmouse *psmouse)
/* ALPS needs stream mode, otherwise it won't report any data */
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) {
- printk(KERN_ERR "alps.c: Failed to enable stream mode\n");
+ psmouse_err(psmouse, "Failed to enable stream mode\n");
return -1;
}
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index 3126983c004a..5ec617e28f7e 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -67,6 +67,18 @@
#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245
#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246
#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247
+/* MacbookAir4,1 (unibody, July 2011) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b
+/* MacbookAir4,2 (unibody, July 2011) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI 0x024c
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO 0x024d
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS 0x024e
+/* Macbook8,2 (unibody) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254
#define BCM5974_DEVICE(prod) { \
.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
@@ -104,6 +116,18 @@ static const struct usb_device_id bcm5974_table[] = {
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS),
+ /* MacbookAir4,1 */
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS),
+ /* MacbookAir4,2 */
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ISO),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_JIS),
+ /* MacbookPro8,2 */
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS),
/* Terminating entry */
{}
};
@@ -294,6 +318,42 @@ static const struct bcm5974_config bcm5974_config_table[] = {
{ DIM_X, DIM_X / SN_COORD, -4415, 5050 },
{ DIM_Y, DIM_Y / SN_COORD, -55, 6680 }
},
+ {
+ USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI,
+ USB_DEVICE_ID_APPLE_WELLSPRING6_ISO,
+ USB_DEVICE_ID_APPLE_WELLSPRING6_JIS,
+ HAS_INTEGRATED_BUTTON,
+ 0x84, sizeof(struct bt_data),
+ 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
+ { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
+ { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
+ { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
+ { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+ },
+ {
+ USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI,
+ USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO,
+ USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS,
+ HAS_INTEGRATED_BUTTON,
+ 0x84, sizeof(struct bt_data),
+ 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
+ { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
+ { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
+ { DIM_X, DIM_X / SN_COORD, -4750, 5280 },
+ { DIM_Y, DIM_Y / SN_COORD, -150, 6730 }
+ },
+ {
+ USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI,
+ USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO,
+ USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS,
+ HAS_INTEGRATED_BUTTON,
+ 0x84, sizeof(struct bt_data),
+ 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
+ { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
+ { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
+ { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
+ { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+ },
{}
};
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 32503565faf9..e2a9867c19d5 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -10,8 +10,6 @@
* Trademarks are the property of their respective owners.
*/
-#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
-
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -25,13 +23,10 @@
#define elantech_debug(fmt, ...) \
do { \
if (etd->debug) \
- printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \
+ psmouse_printk(KERN_DEBUG, psmouse, \
+ fmt, ##__VA_ARGS__); \
} while (0)
-static bool force_elantech;
-module_param_named(force_elantech, force_elantech, bool, 0644);
-MODULE_PARM_DESC(force_elantech, "Force the Elantech PS/2 protocol extension to be used, 1 = enabled, 0 = disabled (default).");
-
/*
* Send a Synaptics style sliced query command
*/
@@ -40,7 +35,7 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
{
if (psmouse_sliced_command(psmouse, c) ||
ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
- pr_err("synaptics_send_cmd query 0x%02x failed.\n", c);
+ psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c);
return -1;
}
@@ -69,7 +64,7 @@ static int elantech_ps2_command(struct psmouse *psmouse,
} while (tries > 0);
if (rc)
- pr_err("ps2 command 0x%02x failed.\n", command);
+ psmouse_err(psmouse, "ps2 command 0x%02x failed.\n", command);
return rc;
}
@@ -84,7 +79,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
unsigned char param[3];
int rc = 0;
- if (reg < 0x10 || reg > 0x26)
+ if (reg < 0x07 || reg > 0x26)
return -1;
if (reg > 0x11 && reg < 0x20)
@@ -108,12 +103,24 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
rc = -1;
}
break;
+
+ case 3 ... 4:
+ if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
+ elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, reg) ||
+ elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
+ rc = -1;
+ }
+ break;
}
if (rc)
- pr_err("failed to read register 0x%02x.\n", reg);
- else
+ psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg);
+ else if (etd->hw_version != 4)
*val = param[0];
+ else
+ *val = param[1];
return rc;
}
@@ -127,7 +134,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
struct elantech_data *etd = psmouse->private;
int rc = 0;
- if (reg < 0x10 || reg > 0x26)
+ if (reg < 0x07 || reg > 0x26)
return -1;
if (reg > 0x11 && reg < 0x20)
@@ -154,11 +161,38 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
rc = -1;
}
break;
+
+ case 3:
+ if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
+ elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, reg) ||
+ elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, val) ||
+ elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
+ rc = -1;
+ }
+ break;
+
+ case 4:
+ if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
+ elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, reg) ||
+ elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
+ elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, val) ||
+ elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
+ rc = -1;
+ }
+ break;
}
if (rc)
- pr_err("failed to write register 0x%02x with value 0x%02x.\n",
- reg, val);
+ psmouse_err(psmouse,
+ "failed to write register 0x%02x with value 0x%02x.\n",
+ reg, val);
return rc;
}
@@ -166,13 +200,13 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
/*
* Dump a complete mouse movement packet to the syslog
*/
-static void elantech_packet_dump(unsigned char *packet, int size)
+static void elantech_packet_dump(struct psmouse *psmouse)
{
int i;
- printk(KERN_DEBUG pr_fmt("PS/2 packet ["));
- for (i = 0; i < size; i++)
- printk("%s0x%02x ", (i) ? ", " : " ", packet[i]);
+ psmouse_printk(KERN_DEBUG, psmouse, "PS/2 packet [");
+ for (i = 0; i < psmouse->pktsize; i++)
+ printk("%s0x%02x ", i ? ", " : " ", psmouse->packet[i]);
printk("]\n");
}
@@ -223,7 +257,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
input_report_abs(dev, ABS_X,
((packet[1] & 0x0c) << 6) | packet[2]);
input_report_abs(dev, ABS_Y,
- ETP_YMAX_V1 - (((packet[1] & 0x03) << 8) | packet[3]));
+ etd->y_max - (((packet[1] & 0x03) << 8) | packet[3]));
}
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
@@ -233,7 +267,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
if (etd->fw_version < 0x020000 &&
- (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
+ (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
/* rocker up */
input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
/* rocker down */
@@ -273,11 +307,11 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
struct elantech_data *etd = psmouse->private;
struct input_dev *dev = psmouse->dev;
unsigned char *packet = psmouse->packet;
- unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0, width = 0, pres = 0;
+ unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+ unsigned int width = 0, pres = 0;
/* byte 0: n1 n0 . . . . R L */
fingers = (packet[0] & 0xc0) >> 6;
- input_report_key(dev, BTN_TOUCH, fingers != 0);
switch (fingers) {
case 3:
@@ -290,18 +324,15 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
/* pass through... */
case 1:
/*
- * byte 1: . . . . . x10 x9 x8
+ * byte 1: . . . . x11 x10 x9 x8
* byte 2: x7 x6 x5 x4 x4 x2 x1 x0
*/
- x1 = ((packet[1] & 0x07) << 8) | packet[2];
+ x1 = ((packet[1] & 0x0f) << 8) | packet[2];
/*
- * byte 4: . . . . . . y9 y8
+ * byte 4: . . . . y11 y10 y9 y8
* byte 5: y7 y6 y5 y4 y3 y2 y1 y0
*/
- y1 = ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]);
-
- input_report_abs(dev, ABS_X, x1);
- input_report_abs(dev, ABS_Y, y1);
+ y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
@@ -314,22 +345,18 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
* byte 0: . . ay8 ax8 . . . .
* byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0
*/
- x1 = ((packet[0] & 0x10) << 4) | packet[1];
+ x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2;
/* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
- y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]);
+ y1 = etd->y_max -
+ ((((packet[0] & 0x20) << 3) | packet[2]) << 2);
/*
* byte 3: . . by8 bx8 . . . .
* byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0
*/
- x2 = ((packet[3] & 0x10) << 4) | packet[4];
+ x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2;
/* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
- y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]);
- /*
- * For compatibility with the X Synaptics driver scale up
- * one coordinate and report as ordinary mouse movent
- */
- input_report_abs(dev, ABS_X, x1 << 2);
- input_report_abs(dev, ABS_Y, y1 << 2);
+ y2 = etd->y_max -
+ ((((packet[3] & 0x20) << 3) | packet[5]) << 2);
/* Unknown so just report sensible values */
pres = 127;
@@ -337,6 +364,11 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
break;
}
+ input_report_key(dev, BTN_TOUCH, fingers != 0);
+ if (fingers != 0) {
+ input_report_abs(dev, ABS_X, x1);
+ input_report_abs(dev, ABS_Y, y1);
+ }
elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
@@ -352,7 +384,208 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_sync(dev);
}
-static int elantech_check_parity_v1(struct psmouse *psmouse)
+/*
+ * Interpret complete data packets and report absolute mode input events for
+ * hardware version 3. (12 byte packets for two fingers)
+ */
+static void elantech_report_absolute_v3(struct psmouse *psmouse,
+ int packet_type)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct elantech_data *etd = psmouse->private;
+ unsigned char *packet = psmouse->packet;
+ unsigned int fingers = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+ unsigned int width = 0, pres = 0;
+
+ /* byte 0: n1 n0 . . . . R L */
+ fingers = (packet[0] & 0xc0) >> 6;
+
+ switch (fingers) {
+ case 3:
+ case 1:
+ /*
+ * byte 1: . . . . x11 x10 x9 x8
+ * byte 2: x7 x6 x5 x4 x4 x2 x1 x0
+ */
+ x1 = ((packet[1] & 0x0f) << 8) | packet[2];
+ /*
+ * byte 4: . . . . y11 y10 y9 y8
+ * byte 5: y7 y6 y5 y4 y3 y2 y1 y0
+ */
+ y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
+ break;
+
+ case 2:
+ if (packet_type == PACKET_V3_HEAD) {
+ /*
+ * byte 1: . . . . ax11 ax10 ax9 ax8
+ * byte 2: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0
+ */
+ etd->mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2];
+ /*
+ * byte 4: . . . . ay11 ay10 ay9 ay8
+ * byte 5: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0
+ */
+ etd->mt[0].y = etd->y_max -
+ (((packet[4] & 0x0f) << 8) | packet[5]);
+ /*
+ * wait for next packet
+ */
+ return;
+ }
+
+ /* packet_type == PACKET_V3_TAIL */
+ x1 = etd->mt[0].x;
+ y1 = etd->mt[0].y;
+ x2 = ((packet[1] & 0x0f) << 8) | packet[2];
+ y2 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
+ break;
+ }
+
+ pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
+ width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
+
+ input_report_key(dev, BTN_TOUCH, fingers != 0);
+ if (fingers != 0) {
+ input_report_abs(dev, ABS_X, x1);
+ input_report_abs(dev, ABS_Y, y1);
+ }
+ elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
+ input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+ input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+ input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+ input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+ input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+ input_report_abs(dev, ABS_PRESSURE, pres);
+ input_report_abs(dev, ABS_TOOL_WIDTH, width);
+
+ input_sync(dev);
+}
+
+static void elantech_input_sync_v4(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ unsigned char *packet = psmouse->packet;
+
+ input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+ input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+ input_mt_report_pointer_emulation(dev, true);
+ input_sync(dev);
+}
+
+static void process_packet_status_v4(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ unsigned char *packet = psmouse->packet;
+ unsigned fingers;
+ int i;
+
+ /* notify finger state change */
+ fingers = packet[1] & 0x1f;
+ for (i = 0; i < ETP_MAX_FINGERS; i++) {
+ if ((fingers & (1 << i)) == 0) {
+ input_mt_slot(dev, i);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, false);
+ }
+ }
+
+ elantech_input_sync_v4(psmouse);
+}
+
+static void process_packet_head_v4(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct elantech_data *etd = psmouse->private;
+ unsigned char *packet = psmouse->packet;
+ int id = ((packet[3] & 0xe0) >> 5) - 1;
+ int pres, traces;
+
+ if (id < 0)
+ return;
+
+ etd->mt[id].x = ((packet[1] & 0x0f) << 8) | packet[2];
+ etd->mt[id].y = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
+ pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
+ traces = (packet[0] & 0xf0) >> 4;
+
+ input_mt_slot(dev, id);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+
+ input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y);
+ input_report_abs(dev, ABS_MT_PRESSURE, pres);
+ input_report_abs(dev, ABS_MT_TOUCH_MAJOR, traces * etd->width);
+ /* report this for backwards compatibility */
+ input_report_abs(dev, ABS_TOOL_WIDTH, traces);
+
+ elantech_input_sync_v4(psmouse);
+}
+
+static void process_packet_motion_v4(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct elantech_data *etd = psmouse->private;
+ unsigned char *packet = psmouse->packet;
+ int weight, delta_x1 = 0, delta_y1 = 0, delta_x2 = 0, delta_y2 = 0;
+ int id, sid;
+
+ id = ((packet[0] & 0xe0) >> 5) - 1;
+ if (id < 0)
+ return;
+
+ sid = ((packet[3] & 0xe0) >> 5) - 1;
+ weight = (packet[0] & 0x10) ? ETP_WEIGHT_VALUE : 1;
+ /*
+ * Motion packets give us the delta of x, y values of specific fingers,
+ * but in two's complement. Let the compiler do the conversion for us.
+ * Also _enlarge_ the numbers to int, in case of overflow.
+ */
+ delta_x1 = (signed char)packet[1];
+ delta_y1 = (signed char)packet[2];
+ delta_x2 = (signed char)packet[4];
+ delta_y2 = (signed char)packet[5];
+
+ etd->mt[id].x += delta_x1 * weight;
+ etd->mt[id].y -= delta_y1 * weight;
+ input_mt_slot(dev, id);
+ input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y);
+
+ if (sid >= 0) {
+ etd->mt[sid].x += delta_x2 * weight;
+ etd->mt[sid].y -= delta_y2 * weight;
+ input_mt_slot(dev, sid);
+ input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[sid].x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[sid].y);
+ }
+
+ elantech_input_sync_v4(psmouse);
+}
+
+static void elantech_report_absolute_v4(struct psmouse *psmouse,
+ int packet_type)
+{
+ switch (packet_type) {
+ case PACKET_V4_STATUS:
+ process_packet_status_v4(psmouse);
+ break;
+
+ case PACKET_V4_HEAD:
+ process_packet_head_v4(psmouse);
+ break;
+
+ case PACKET_V4_MOTION:
+ process_packet_motion_v4(psmouse);
+ break;
+
+ case PACKET_UNKNOWN:
+ default:
+ /* impossible to get here */
+ break;
+ }
+}
+
+static int elantech_packet_check_v1(struct psmouse *psmouse)
{
struct elantech_data *etd = psmouse->private;
unsigned char *packet = psmouse->packet;
@@ -376,31 +609,142 @@ static int elantech_check_parity_v1(struct psmouse *psmouse)
etd->parity[packet[3]] == p3;
}
+static int elantech_debounce_check_v2(struct psmouse *psmouse)
+{
+ /*
+ * When we encounter packet that matches this exactly, it means the
+ * hardware is in debounce status. Just ignore the whole packet.
+ */
+ const u8 debounce_packet[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff };
+ unsigned char *packet = psmouse->packet;
+
+ return !memcmp(packet, debounce_packet, sizeof(debounce_packet));
+}
+
+static int elantech_packet_check_v2(struct psmouse *psmouse)
+{
+ struct elantech_data *etd = psmouse->private;
+ unsigned char *packet = psmouse->packet;
+
+ /*
+ * V2 hardware has two flavors. Older ones that do not report pressure,
+ * and newer ones that reports pressure and width. With newer ones, all
+ * packets (1, 2, 3 finger touch) have the same constant bits. With
+ * older ones, 1/3 finger touch packets and 2 finger touch packets
+ * have different constant bits.
+ * With all three cases, if the constant bits are not exactly what I
+ * expected, I consider them invalid.
+ */
+ if (etd->reports_pressure)
+ return (packet[0] & 0x0c) == 0x04 &&
+ (packet[3] & 0x0f) == 0x02;
+
+ if ((packet[0] & 0xc0) == 0x80)
+ return (packet[0] & 0x0c) == 0x0c &&
+ (packet[3] & 0x0e) == 0x08;
+
+ return (packet[0] & 0x3c) == 0x3c &&
+ (packet[1] & 0xf0) == 0x00 &&
+ (packet[3] & 0x3e) == 0x38 &&
+ (packet[4] & 0xf0) == 0x00;
+}
+
+/*
+ * We check the constant bits to determine what packet type we get,
+ * so packet checking is mandatory for v3 and later hardware.
+ */
+static int elantech_packet_check_v3(struct psmouse *psmouse)
+{
+ const u8 debounce_packet[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff };
+ unsigned char *packet = psmouse->packet;
+
+ /*
+ * check debounce first, it has the same signature in byte 0
+ * and byte 3 as PACKET_V3_HEAD.
+ */
+ if (!memcmp(packet, debounce_packet, sizeof(debounce_packet)))
+ return PACKET_DEBOUNCE;
+
+ if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02)
+ return PACKET_V3_HEAD;
+
+ if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c)
+ return PACKET_V3_TAIL;
+
+ return PACKET_UNKNOWN;
+}
+
+static int elantech_packet_check_v4(struct psmouse *psmouse)
+{
+ unsigned char *packet = psmouse->packet;
+
+ if ((packet[0] & 0x0c) == 0x04 &&
+ (packet[3] & 0x1f) == 0x11)
+ return PACKET_V4_HEAD;
+
+ if ((packet[0] & 0x0c) == 0x04 &&
+ (packet[3] & 0x1f) == 0x12)
+ return PACKET_V4_MOTION;
+
+ if ((packet[0] & 0x0c) == 0x04 &&
+ (packet[3] & 0x1f) == 0x10)
+ return PACKET_V4_STATUS;
+
+ return PACKET_UNKNOWN;
+}
+
/*
* Process byte stream from mouse and handle complete packets
*/
static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
{
struct elantech_data *etd = psmouse->private;
+ int packet_type;
if (psmouse->pktcnt < psmouse->pktsize)
return PSMOUSE_GOOD_DATA;
if (etd->debug > 1)
- elantech_packet_dump(psmouse->packet, psmouse->pktsize);
+ elantech_packet_dump(psmouse);
switch (etd->hw_version) {
case 1:
- if (etd->paritycheck && !elantech_check_parity_v1(psmouse))
+ if (etd->paritycheck && !elantech_packet_check_v1(psmouse))
return PSMOUSE_BAD_DATA;
elantech_report_absolute_v1(psmouse);
break;
case 2:
- /* We don't know how to check parity in protocol v2 */
+ /* ignore debounce */
+ if (elantech_debounce_check_v2(psmouse))
+ return PSMOUSE_FULL_PACKET;
+
+ if (etd->paritycheck && !elantech_packet_check_v2(psmouse))
+ return PSMOUSE_BAD_DATA;
+
elantech_report_absolute_v2(psmouse);
break;
+
+ case 3:
+ packet_type = elantech_packet_check_v3(psmouse);
+ /* ignore debounce */
+ if (packet_type == PACKET_DEBOUNCE)
+ return PSMOUSE_FULL_PACKET;
+
+ if (packet_type == PACKET_UNKNOWN)
+ return PSMOUSE_BAD_DATA;
+
+ elantech_report_absolute_v3(psmouse, packet_type);
+ break;
+
+ case 4:
+ packet_type = elantech_packet_check_v4(psmouse);
+ if (packet_type == PACKET_UNKNOWN)
+ return PSMOUSE_BAD_DATA;
+
+ elantech_report_absolute_v4(psmouse, packet_type);
+ break;
}
return PSMOUSE_FULL_PACKET;
@@ -435,15 +779,29 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
elantech_write_reg(psmouse, 0x11, etd->reg_11) ||
elantech_write_reg(psmouse, 0x21, etd->reg_21)) {
rc = -1;
- break;
}
+ break;
+
+ case 3:
+ etd->reg_10 = 0x0b;
+ if (elantech_write_reg(psmouse, 0x10, etd->reg_10))
+ rc = -1;
+
+ break;
+
+ case 4:
+ etd->reg_07 = 0x01;
+ if (elantech_write_reg(psmouse, 0x07, etd->reg_07))
+ rc = -1;
+
+ goto skip_readback_reg_10; /* v4 has no reg 0x10 to read */
}
if (rc == 0) {
/*
* Read back reg 0x10. For hardware version 1 we must make
* sure the absolute mode bit is set. For hardware version 2
- * the touchpad is probably initalising and not ready until
+ * the touchpad is probably initializing and not ready until
* we read back the value we just wrote.
*/
do {
@@ -456,27 +814,115 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
} while (tries > 0);
if (rc) {
- pr_err("failed to read back register 0x10.\n");
+ psmouse_err(psmouse,
+ "failed to read back register 0x10.\n");
} else if (etd->hw_version == 1 &&
!(val & ETP_R10_ABSOLUTE_MODE)) {
- pr_err("touchpad refuses to switch to absolute mode.\n");
+ psmouse_err(psmouse,
+ "touchpad refuses to switch to absolute mode.\n");
rc = -1;
}
}
+ skip_readback_reg_10:
if (rc)
- pr_err("failed to initialise registers.\n");
+ psmouse_err(psmouse, "failed to initialise registers.\n");
return rc;
}
+static int elantech_set_range(struct psmouse *psmouse,
+ unsigned int *x_min, unsigned int *y_min,
+ unsigned int *x_max, unsigned int *y_max,
+ unsigned int *width)
+{
+ struct elantech_data *etd = psmouse->private;
+ unsigned char param[3];
+ unsigned char traces;
+
+ switch (etd->hw_version) {
+ case 1:
+ *x_min = ETP_XMIN_V1;
+ *y_min = ETP_YMIN_V1;
+ *x_max = ETP_XMAX_V1;
+ *y_max = ETP_YMAX_V1;
+ break;
+
+ case 2:
+ if (etd->fw_version == 0x020800 ||
+ etd->fw_version == 0x020b00 ||
+ etd->fw_version == 0x020030) {
+ *x_min = ETP_XMIN_V2;
+ *y_min = ETP_YMIN_V2;
+ *x_max = ETP_XMAX_V2;
+ *y_max = ETP_YMAX_V2;
+ } else {
+ int i;
+ int fixed_dpi;
+
+ i = (etd->fw_version > 0x020800 &&
+ etd->fw_version < 0x020900) ? 1 : 2;
+
+ if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+ return -1;
+
+ fixed_dpi = param[1] & 0x10;
+
+ if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) {
+ if (synaptics_send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
+ return -1;
+
+ *x_max = (etd->capabilities[1] - i) * param[1] / 2;
+ *y_max = (etd->capabilities[2] - i) * param[2] / 2;
+ } else if (etd->fw_version == 0x040216) {
+ *x_max = 819;
+ *y_max = 405;
+ } else if (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) {
+ *x_max = 900;
+ *y_max = 500;
+ } else {
+ *x_max = (etd->capabilities[1] - i) * 64;
+ *y_max = (etd->capabilities[2] - i) * 64;
+ }
+ }
+ break;
+
+ case 3:
+ if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+ return -1;
+
+ *x_max = (0x0f & param[0]) << 8 | param[1];
+ *y_max = (0xf0 & param[0]) << 4 | param[2];
+ break;
+
+ case 4:
+ if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+ return -1;
+
+ *x_max = (0x0f & param[0]) << 8 | param[1];
+ *y_max = (0xf0 & param[0]) << 4 | param[2];
+ traces = etd->capabilities[1];
+ if ((traces < 2) || (traces > *x_max))
+ return -1;
+
+ *width = *x_max / (traces - 1);
+ break;
+ }
+
+ return 0;
+}
+
/*
* Set the appropriate event bits for the input subsystem
*/
-static void elantech_set_input_params(struct psmouse *psmouse)
+static int elantech_set_input_params(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
struct elantech_data *etd = psmouse->private;
+ unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
+
+ if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
+ return -1;
__set_bit(EV_KEY, dev->evbit);
__set_bit(EV_ABS, dev->evbit);
@@ -494,30 +940,64 @@ static void elantech_set_input_params(struct psmouse *psmouse)
case 1:
/* Rocker button */
if (etd->fw_version < 0x020000 &&
- (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
+ (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
__set_bit(BTN_FORWARD, dev->keybit);
__set_bit(BTN_BACK, dev->keybit);
}
- input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, ETP_XMAX_V1, 0, 0);
- input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, ETP_YMAX_V1, 0, 0);
+ input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
+ input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
break;
case 2:
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
- input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
- input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
+ __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+ /* fall through */
+ case 3:
+ input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
+ input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
if (etd->reports_pressure) {
input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
ETP_PMAX_V2, 0, 0);
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
ETP_WMAX_V2, 0, 0);
}
- __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
input_mt_init_slots(dev, 2);
- input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
- input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+ break;
+
+ case 4:
+ __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+ /* For X to recognize me as touchpad. */
+ input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
+ input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
+ /*
+ * range of pressure and width is the same as v2,
+ * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility.
+ */
+ input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
+ ETP_PMAX_V2, 0, 0);
+ input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
+ ETP_WMAX_V2, 0, 0);
+ /* Multitouch capable pad, up to 5 fingers. */
+ input_mt_init_slots(dev, ETP_MAX_FINGERS);
+ input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+ input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2,
+ ETP_PMAX_V2, 0, 0);
+ /*
+ * The firmware reports how many trace lines the finger spans,
+ * convert to surface unit as Protocol-B requires.
+ */
+ input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0,
+ ETP_WMAX_V2 * width, 0, 0);
break;
}
+
+ etd->y_max = y_max;
+ etd->width = width;
+
+ return 0;
}
struct elantech_attr_data {
@@ -587,6 +1067,7 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
elantech_show_int_attr, \
elantech_set_int_attr)
+ELANTECH_INT_ATTR(reg_07, 0x07);
ELANTECH_INT_ATTR(reg_10, 0x10);
ELANTECH_INT_ATTR(reg_11, 0x11);
ELANTECH_INT_ATTR(reg_20, 0x20);
@@ -600,6 +1081,7 @@ ELANTECH_INT_ATTR(debug, 0);
ELANTECH_INT_ATTR(paritycheck, 0);
static struct attribute *elantech_attrs[] = {
+ &psmouse_attr_reg_07.dattr.attr,
&psmouse_attr_reg_10.dattr.attr,
&psmouse_attr_reg_11.dattr.attr,
&psmouse_attr_reg_20.dattr.attr,
@@ -651,7 +1133,7 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
- pr_debug("sending Elantech magic knock failed.\n");
+ psmouse_dbg(psmouse, "sending Elantech magic knock failed.\n");
return -1;
}
@@ -659,9 +1141,11 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
* Report this in case there are Elantech models that use a different
* set of magic numbers
*/
- if (param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) {
- pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
- param[0], param[1], param[2]);
+ if (param[0] != 0x3c || param[1] != 0x03 ||
+ (param[2] != 0xc8 && param[2] != 0x00)) {
+ psmouse_dbg(psmouse,
+ "unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
+ param[0], param[1], param[2]);
return -1;
}
@@ -671,20 +1155,18 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
* to Elantech magic knock and there might be more.
*/
if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
- pr_debug("failed to query firmware version.\n");
+ psmouse_dbg(psmouse, "failed to query firmware version.\n");
return -1;
}
- pr_debug("Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n",
- param[0], param[1], param[2]);
+ psmouse_dbg(psmouse,
+ "Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n",
+ param[0], param[1], param[2]);
if (!elantech_is_signature_valid(param)) {
- if (!force_elantech) {
- pr_debug("Probably not a real Elantech touchpad. Aborting.\n");
- return -1;
- }
-
- pr_debug("Probably not a real Elantech touchpad. Enabling anyway due to force_elantech.\n");
+ psmouse_dbg(psmouse,
+ "Probably not a real Elantech touchpad. Aborting.\n");
+ return -1;
}
if (set_properties) {
@@ -715,7 +1197,8 @@ static int elantech_reconnect(struct psmouse *psmouse)
return -1;
if (elantech_set_absolute_mode(psmouse)) {
- pr_err("failed to put touchpad back into absolute mode.\n");
+ psmouse_err(psmouse,
+ "failed to put touchpad back into absolute mode.\n");
return -1;
}
@@ -723,6 +1206,58 @@ static int elantech_reconnect(struct psmouse *psmouse)
}
/*
+ * determine hardware version and set some properties according to it.
+ */
+static int elantech_set_properties(struct elantech_data *etd)
+{
+ /* This represents the version of IC body. */
+ int ver = (etd->fw_version & 0x0f0000) >> 16;
+
+ /* Early version of Elan touchpads doesn't obey the rule. */
+ if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600)
+ etd->hw_version = 1;
+ else {
+ switch (ver) {
+ case 2:
+ case 4:
+ etd->hw_version = 2;
+ break;
+ case 5:
+ etd->hw_version = 3;
+ break;
+ case 6:
+ etd->hw_version = 4;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ /*
+ * Turn on packet checking by default.
+ */
+ etd->paritycheck = 1;
+
+ /*
+ * This firmware suffers from misreporting coordinates when
+ * a touch action starts causing the mouse cursor or scrolled page
+ * to jump. Enable a workaround.
+ */
+ etd->jumpy_cursor =
+ (etd->fw_version == 0x020022 || etd->fw_version == 0x020600);
+
+ if (etd->hw_version > 1) {
+ /* For now show extra debug information */
+ etd->debug = 1;
+
+ if (etd->fw_version >= 0x020800)
+ etd->reports_pressure = true;
+ }
+
+ return 0;
+}
+
+/*
* Initialize the touchpad and create sysfs entries
*/
int elantech_init(struct psmouse *psmouse)
@@ -743,70 +1278,53 @@ int elantech_init(struct psmouse *psmouse)
* Do the version query again so we can store the result
*/
if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
- pr_err("failed to query firmware version.\n");
+ psmouse_err(psmouse, "failed to query firmware version.\n");
goto init_fail;
}
-
etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
- /*
- * Assume every version greater than this is new EeePC style
- * hardware with 6 byte packets
- */
- if (etd->fw_version >= 0x020030) {
- etd->hw_version = 2;
- /* For now show extra debug information */
- etd->debug = 1;
- /* Don't know how to do parity checking for version 2 */
- etd->paritycheck = 0;
-
- if (etd->fw_version >= 0x020800)
- etd->reports_pressure = true;
-
- } else {
- etd->hw_version = 1;
- etd->paritycheck = 1;
- }
-
- pr_info("assuming hardware version %d, firmware version %d.%d.%d\n",
- etd->hw_version, param[0], param[1], param[2]);
-
- if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) {
- pr_err("failed to query capabilities.\n");
+ if (elantech_set_properties(etd)) {
+ psmouse_err(psmouse, "unknown hardware version, aborting...\n");
goto init_fail;
}
- pr_info("Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
- param[0], param[1], param[2]);
- etd->capabilities = param[0];
+ psmouse_info(psmouse,
+ "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n",
+ etd->hw_version, param[0], param[1], param[2]);
- /*
- * This firmware suffers from misreporting coordinates when
- * a touch action starts causing the mouse cursor or scrolled page
- * to jump. Enable a workaround.
- */
- if (etd->fw_version == 0x020022 || etd->fw_version == 0x020600) {
- pr_info("firmware version 2.0.34/2.6.0 detected, enabling jumpy cursor workaround\n");
- etd->jumpy_cursor = true;
+ if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
+ etd->capabilities)) {
+ psmouse_err(psmouse, "failed to query capabilities.\n");
+ goto init_fail;
}
+ psmouse_info(psmouse,
+ "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
+ etd->capabilities[0], etd->capabilities[1],
+ etd->capabilities[2]);
if (elantech_set_absolute_mode(psmouse)) {
- pr_err("failed to put touchpad into absolute mode.\n");
+ psmouse_err(psmouse,
+ "failed to put touchpad into absolute mode.\n");
goto init_fail;
}
- elantech_set_input_params(psmouse);
+ if (elantech_set_input_params(psmouse)) {
+ psmouse_err(psmouse, "failed to query touchpad range.\n");
+ goto init_fail;
+ }
error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
&elantech_attr_group);
if (error) {
- pr_err("failed to create sysfs attributes, error: %d.\n", error);
+ psmouse_err(psmouse,
+ "failed to create sysfs attributes, error: %d.\n",
+ error);
goto init_fail;
}
psmouse->protocol_handler = elantech_process_byte;
psmouse->disconnect = elantech_disconnect;
psmouse->reconnect = elantech_reconnect;
- psmouse->pktsize = etd->hw_version == 2 ? 6 : 4;
+ psmouse->pktsize = etd->hw_version > 1 ? 6 : 4;
return 0;
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index fabb2b99615c..9e5f1aabea7e 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -16,14 +16,17 @@
/*
* Command values for Synaptics style queries
*/
+#define ETP_FW_ID_QUERY 0x00
#define ETP_FW_VERSION_QUERY 0x01
#define ETP_CAPABILITIES_QUERY 0x02
+#define ETP_SAMPLE_QUERY 0x03
/*
* Command values for register reading or writing
*/
#define ETP_REGISTER_READ 0x10
#define ETP_REGISTER_WRITE 0x11
+#define ETP_REGISTER_READWRITE 0x00
/*
* Hardware version 2 custom PS/2 command value
@@ -66,16 +69,13 @@
#define ETP_YMAX_V1 (384 - ETP_EDGE_FUZZ_V1)
/*
- * It seems the resolution for hardware version 2 doubled.
- * Hence the X and Y ranges are doubled too.
- * The bezel around the pad also appears to be smaller
+ * The resolution for older v2 hardware doubled.
+ * (newer v2's firmware provides command so we can query)
*/
-#define ETP_EDGE_FUZZ_V2 8
-
-#define ETP_XMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2)
-#define ETP_XMAX_V2 (1152 - ETP_EDGE_FUZZ_V2)
-#define ETP_YMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2)
-#define ETP_YMAX_V2 ( 768 - ETP_EDGE_FUZZ_V2)
+#define ETP_XMIN_V2 0
+#define ETP_XMAX_V2 1152
+#define ETP_YMIN_V2 0
+#define ETP_YMAX_V2 768
#define ETP_PMIN_V2 0
#define ETP_PMAX_V2 255
@@ -83,17 +83,37 @@
#define ETP_WMAX_V2 15
/*
- * For two finger touches the coordinate of each finger gets reported
- * separately but with reduced resolution.
+ * v3 hardware has 2 kinds of packet types,
+ * v4 hardware has 3.
+ */
+#define PACKET_UNKNOWN 0x01
+#define PACKET_DEBOUNCE 0x02
+#define PACKET_V3_HEAD 0x03
+#define PACKET_V3_TAIL 0x04
+#define PACKET_V4_HEAD 0x05
+#define PACKET_V4_MOTION 0x06
+#define PACKET_V4_STATUS 0x07
+
+/*
+ * track up to 5 fingers for v4 hardware
+ */
+#define ETP_MAX_FINGERS 5
+
+/*
+ * weight value for v4 hardware
*/
-#define ETP_2FT_FUZZ 4
+#define ETP_WEIGHT_VALUE 5
-#define ETP_2FT_XMIN ( 0 + ETP_2FT_FUZZ)
-#define ETP_2FT_XMAX (288 - ETP_2FT_FUZZ)
-#define ETP_2FT_YMIN ( 0 + ETP_2FT_FUZZ)
-#define ETP_2FT_YMAX (192 - ETP_2FT_FUZZ)
+/*
+ * The base position for one finger, v4 hardware
+ */
+struct finger_pos {
+ unsigned int x;
+ unsigned int y;
+};
struct elantech_data {
+ unsigned char reg_07;
unsigned char reg_10;
unsigned char reg_11;
unsigned char reg_20;
@@ -104,13 +124,16 @@ struct elantech_data {
unsigned char reg_25;
unsigned char reg_26;
unsigned char debug;
- unsigned char capabilities;
+ unsigned char capabilities[3];
bool paritycheck;
bool jumpy_cursor;
bool reports_pressure;
unsigned char hw_version;
unsigned int fw_version;
unsigned int single_finger_reports;
+ unsigned int y_max;
+ unsigned int width;
+ struct finger_pos mt[ETP_MAX_FINGERS];
unsigned char parity[256];
};
diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c
index 7b6ce178f1b6..58902fbb9896 100644
--- a/drivers/input/mouse/gpio_mouse.c
+++ b/drivers/input/mouse/gpio_mouse.c
@@ -191,7 +191,7 @@ static void __exit gpio_mouse_exit(void)
}
module_exit(gpio_mouse_exit);
-MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("GPIO mouse driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */
diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c
index 95577c15ae56..0470dd46b566 100644
--- a/drivers/input/mouse/hgpk.c
+++ b/drivers/input/mouse/hgpk.c
@@ -32,6 +32,7 @@
#define DEBUG
#include <linux/slab.h>
#include <linux/input.h>
+#include <linux/module.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <linux/delay.h>
@@ -135,10 +136,10 @@ static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y)
/* discard if too big, or half that but > 4 times the prev delta */
if (avx > recalib_delta ||
(avx > recalib_delta / 2 && ((avx / 4) > priv->xlast))) {
- hgpk_err(psmouse, "detected %dpx jump in x\n", x);
+ psmouse_warn(psmouse, "detected %dpx jump in x\n", x);
priv->xbigj = avx;
} else if (approx_half(avx, priv->xbigj)) {
- hgpk_err(psmouse, "detected secondary %dpx jump in x\n", x);
+ psmouse_warn(psmouse, "detected secondary %dpx jump in x\n", x);
priv->xbigj = avx;
priv->xsaw_secondary++;
} else {
@@ -150,10 +151,10 @@ static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y)
if (avy > recalib_delta ||
(avy > recalib_delta / 2 && ((avy / 4) > priv->ylast))) {
- hgpk_err(psmouse, "detected %dpx jump in y\n", y);
+ psmouse_warn(psmouse, "detected %dpx jump in y\n", y);
priv->ybigj = avy;
} else if (approx_half(avy, priv->ybigj)) {
- hgpk_err(psmouse, "detected secondary %dpx jump in y\n", y);
+ psmouse_warn(psmouse, "detected secondary %dpx jump in y\n", y);
priv->ybigj = avy;
priv->ysaw_secondary++;
} else {
@@ -167,7 +168,7 @@ static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y)
priv->ylast = avy;
if (do_recal && jumpy_delay) {
- hgpk_err(psmouse, "scheduling recalibration\n");
+ psmouse_warn(psmouse, "scheduling recalibration\n");
psmouse_queue_work(psmouse, &priv->recalib_wq,
msecs_to_jiffies(jumpy_delay));
}
@@ -259,8 +260,8 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,
* movement, it is probably a case of the user moving the
* cursor very slowly across the screen. */
if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) {
- hgpk_err(psmouse, "packet spew detected (%d,%d)\n",
- priv->x_tally, priv->y_tally);
+ psmouse_warn(psmouse, "packet spew detected (%d,%d)\n",
+ priv->x_tally, priv->y_tally);
priv->spew_flag = RECALIBRATING;
psmouse_queue_work(psmouse, &priv->recalib_wq,
msecs_to_jiffies(spew_delay));
@@ -332,12 +333,12 @@ static bool hgpk_is_byte_valid(struct psmouse *psmouse, unsigned char *packet)
}
if (!valid)
- hgpk_dbg(psmouse,
- "bad data, mode %d (%d) %02x %02x %02x %02x %02x %02x\n",
- priv->mode, pktcnt,
- psmouse->packet[0], psmouse->packet[1],
- psmouse->packet[2], psmouse->packet[3],
- psmouse->packet[4], psmouse->packet[5]);
+ psmouse_dbg(psmouse,
+ "bad data, mode %d (%d) %02x %02x %02x %02x %02x %02x\n",
+ priv->mode, pktcnt,
+ psmouse->packet[0], psmouse->packet[1],
+ psmouse->packet[2], psmouse->packet[3],
+ psmouse->packet[4], psmouse->packet[5]);
return valid;
}
@@ -360,19 +361,20 @@ static void hgpk_process_advanced_packet(struct psmouse *psmouse)
input_report_abs(idev, ABS_PRESSURE, z);
if (tpdebug)
- hgpk_dbg(psmouse, "pd=%d fd=%d z=%d",
- pt_down, finger_down, z);
+ psmouse_dbg(psmouse, "pd=%d fd=%d z=%d",
+ pt_down, finger_down, z);
} else {
/*
* PenTablet mode does not report pressure, so we don't
* report it here
*/
if (tpdebug)
- hgpk_dbg(psmouse, "pd=%d ", down);
+ psmouse_dbg(psmouse, "pd=%d ", down);
}
if (tpdebug)
- hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y);
+ psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n",
+ left, right, x, y);
input_report_key(idev, BTN_TOUCH, down);
input_report_key(idev, BTN_LEFT, left);
@@ -394,7 +396,7 @@ static void hgpk_process_advanced_packet(struct psmouse *psmouse)
if (x == priv->abs_x && y == priv->abs_y) {
if (++priv->dupe_count > SPEW_WATCH_COUNT) {
if (tpdebug)
- hgpk_dbg(psmouse, "hard spew detected\n");
+ psmouse_dbg(psmouse, "hard spew detected\n");
priv->spew_flag = RECALIBRATING;
psmouse_queue_work(psmouse, &priv->recalib_wq,
msecs_to_jiffies(spew_delay));
@@ -411,7 +413,7 @@ static void hgpk_process_advanced_packet(struct psmouse *psmouse)
int y_diff = priv->abs_y - y;
if (hgpk_discard_decay_hack(psmouse, x_diff, y_diff)) {
if (tpdebug)
- hgpk_dbg(psmouse, "discarding\n");
+ psmouse_dbg(psmouse, "discarding\n");
goto done;
}
hgpk_spewing_hack(psmouse, left, right, x_diff, y_diff);
@@ -436,20 +438,21 @@ static void hgpk_process_simple_packet(struct psmouse *psmouse)
int y = ((packet[0] << 3) & 0x100) - packet[2];
if (packet[0] & 0xc0)
- hgpk_dbg(psmouse,
- "overflow -- 0x%02x 0x%02x 0x%02x\n",
- packet[0], packet[1], packet[2]);
+ psmouse_dbg(psmouse,
+ "overflow -- 0x%02x 0x%02x 0x%02x\n",
+ packet[0], packet[1], packet[2]);
if (hgpk_discard_decay_hack(psmouse, x, y)) {
if (tpdebug)
- hgpk_dbg(psmouse, "discarding\n");
+ psmouse_dbg(psmouse, "discarding\n");
return;
}
hgpk_spewing_hack(psmouse, left, right, x, y);
if (tpdebug)
- hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y);
+ psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n",
+ left, right, x, y);
input_report_key(dev, BTN_LEFT, left);
input_report_key(dev, BTN_RIGHT, right);
@@ -481,9 +484,8 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
* ugh, got a packet inside our recalibration
* window, schedule another recalibration.
*/
- hgpk_dbg(psmouse,
- "packet inside calibration window, "
- "queueing another recalibration\n");
+ psmouse_dbg(psmouse,
+ "packet inside calibration window, queueing another recalibration\n");
psmouse_queue_work(psmouse, &priv->recalib_wq,
msecs_to_jiffies(post_interrupt_delay));
}
@@ -627,7 +629,7 @@ static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate)
err = hgpk_select_mode(psmouse);
if (err) {
- hgpk_err(psmouse, "failed to select mode\n");
+ psmouse_err(psmouse, "failed to select mode\n");
return err;
}
@@ -647,11 +649,11 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse)
return 0;
if (!autorecal) {
- hgpk_dbg(psmouse, "recalibrations disabled, ignoring\n");
+ psmouse_dbg(psmouse, "recalibration disabled, ignoring\n");
return 0;
}
- hgpk_dbg(psmouse, "recalibrating touchpad..\n");
+ psmouse_dbg(psmouse, "recalibrating touchpad..\n");
/* we don't want to race with the irq handler, nor with resyncs */
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
@@ -674,7 +676,7 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse)
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
if (tpdebug)
- hgpk_dbg(psmouse, "touchpad reactivated\n");
+ psmouse_dbg(psmouse, "touchpad reactivated\n");
/*
* If we get packets right away after recalibrating, it's likely
@@ -726,16 +728,16 @@ static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable)
err = hgpk_reset_device(psmouse, false);
if (err) {
- hgpk_err(psmouse, "Failed to reset device!\n");
+ psmouse_err(psmouse, "Failed to reset device!\n");
return err;
}
/* should be all set, enable the touchpad */
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
- hgpk_dbg(psmouse, "Touchpad powered up.\n");
+ psmouse_dbg(psmouse, "Touchpad powered up.\n");
} else {
- hgpk_dbg(psmouse, "Powering off touchpad.\n");
+ psmouse_dbg(psmouse, "Powering off touchpad.\n");
if (ps2_command(ps2dev, NULL, 0xec) ||
ps2_command(ps2dev, NULL, 0xec) ||
@@ -922,7 +924,7 @@ static void hgpk_recalib_work(struct work_struct *work)
struct psmouse *psmouse = priv->psmouse;
if (hgpk_force_recalibrate(psmouse))
- hgpk_err(psmouse, "recalibration failed!\n");
+ psmouse_err(psmouse, "recalibration failed!\n");
}
static int hgpk_register(struct psmouse *psmouse)
@@ -946,14 +948,15 @@ static int hgpk_register(struct psmouse *psmouse)
err = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_powered.dattr);
if (err) {
- hgpk_err(psmouse, "Failed creating 'powered' sysfs node\n");
+ psmouse_err(psmouse, "Failed creating 'powered' sysfs node\n");
return err;
}
err = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_hgpk_mode.dattr);
if (err) {
- hgpk_err(psmouse, "Failed creating 'hgpk_mode' sysfs node\n");
+ psmouse_err(psmouse,
+ "Failed creating 'hgpk_mode' sysfs node\n");
goto err_remove_powered;
}
@@ -962,8 +965,8 @@ static int hgpk_register(struct psmouse *psmouse)
err = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_recalibrate.dattr);
if (err) {
- hgpk_err(psmouse,
- "Failed creating 'recalibrate' sysfs node\n");
+ psmouse_err(psmouse,
+ "Failed creating 'recalibrate' sysfs node\n");
goto err_remove_mode;
}
}
@@ -1026,13 +1029,13 @@ static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse)
return -EIO;
}
- hgpk_dbg(psmouse, "ID: %02x %02x %02x\n", param[0], param[1], param[2]);
+ psmouse_dbg(psmouse, "ID: %02x %02x %02x\n", param[0], param[1], param[2]);
/* HGPK signature: 0x67, 0x00, 0x<model> */
if (param[0] != 0x67 || param[1] != 0x00)
return -ENODEV;
- hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]);
+ psmouse_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]);
return param[2];
}
diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h
index 311c0e87fcbf..dd686771cfe0 100644
--- a/drivers/input/mouse/hgpk.h
+++ b/drivers/input/mouse/hgpk.h
@@ -46,17 +46,6 @@ struct hgpk_data {
int xsaw_secondary, ysaw_secondary; /* jumpiness detection */
};
-#define hgpk_dbg(psmouse, format, arg...) \
- dev_dbg(&(psmouse)->ps2dev.serio->dev, format, ## arg)
-#define hgpk_err(psmouse, format, arg...) \
- dev_err(&(psmouse)->ps2dev.serio->dev, format, ## arg)
-#define hgpk_info(psmouse, format, arg...) \
- dev_info(&(psmouse)->ps2dev.serio->dev, format, ## arg)
-#define hgpk_warn(psmouse, format, arg...) \
- dev_warn(&(psmouse)->ps2dev.serio->dev, format, ## arg)
-#define hgpk_notice(psmouse, format, arg...) \
- dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg)
-
#ifdef CONFIG_MOUSE_PS2_OLPC
void hgpk_module_init(void);
int hgpk_detect(struct psmouse *psmouse, bool set_properties);
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
index c31ad11df6bb..2c4db636de6c 100644
--- a/drivers/input/mouse/lifebook.c
+++ b/drivers/input/mouse/lifebook.c
@@ -33,7 +33,7 @@ static const char *desired_serio_phys;
static int lifebook_limit_serio3(const struct dmi_system_id *d)
{
desired_serio_phys = "isa0060/serio3";
- return 0;
+ return 1;
}
static bool lifebook_use_6byte_proto;
@@ -41,7 +41,7 @@ static bool lifebook_use_6byte_proto;
static int lifebook_set_6byte_proto(const struct dmi_system_id *d)
{
lifebook_use_6byte_proto = true;
- return 0;
+ return 1;
}
static const struct dmi_system_id __initconst lifebook_dmi_table[] = {
@@ -169,8 +169,8 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
if (relative_packet) {
if (!dev2)
- printk(KERN_WARNING "lifebook.c: got relative packet "
- "but no relative device set up\n");
+ psmouse_warn(psmouse,
+ "got relative packet but no relative device set up\n");
} else {
if (lifebook_use_6byte_proto) {
input_report_abs(dev1, ABS_X,
@@ -212,7 +212,7 @@ static int lifebook_absolute_mode(struct psmouse *psmouse)
/*
* Enable absolute output -- ps2_command fails always but if
- * you leave this call out the touchsreen will never send
+ * you leave this call out the touchscreen will never send
* absolute coordinates
*/
param = lifebook_use_6byte_proto ? 0x08 : 0x07;
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
index c9983aee9082..faac2c3bef74 100644
--- a/drivers/input/mouse/logips2pp.c
+++ b/drivers/input/mouse/logips2pp.c
@@ -82,11 +82,11 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
packet[0] = packet[2] | 0x08;
break;
-#ifdef DEBUG
default:
- printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n",
- (packet[1] >> 4) | (packet[0] & 0x30));
-#endif
+ psmouse_dbg(psmouse,
+ "Received PS2++ packet #%x, but don't know how to handle.\n",
+ (packet[1] >> 4) | (packet[0] & 0x30));
+ break;
}
} else {
/* Standard PS/2 motion data */
@@ -382,7 +382,7 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties)
}
} else {
- printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model);
+ psmouse_warn(psmouse, "Detected unknown Logitech mouse model %d\n", model);
}
if (set_properties) {
@@ -400,9 +400,9 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties)
error = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_smartscroll.dattr);
if (error) {
- printk(KERN_ERR
- "logips2pp.c: failed to create smartscroll "
- "sysfs attribute, error: %d\n", error);
+ psmouse_err(psmouse,
+ "failed to create smartscroll sysfs attribute, error: %d\n",
+ error);
return -1;
}
}
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 3f74baee102b..9f352fbd7b4f 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -11,6 +11,9 @@
* the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define psmouse_fmt(fmt) fmt
+
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -251,11 +254,14 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
switch (rc) {
case PSMOUSE_BAD_DATA:
if (psmouse->state == PSMOUSE_ACTIVATED) {
- printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n",
- psmouse->name, psmouse->phys, psmouse->pktcnt);
+ psmouse_warn(psmouse,
+ "%s at %s lost sync at byte %d\n",
+ psmouse->name, psmouse->phys,
+ psmouse->pktcnt);
if (++psmouse->out_of_sync_cnt == psmouse->resetafter) {
__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
- printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n");
+ psmouse_notice(psmouse,
+ "issuing reconnect request\n");
serio_reconnect(psmouse->ps2dev.serio);
return -1;
}
@@ -267,8 +273,9 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
psmouse->pktcnt = 0;
if (psmouse->out_of_sync_cnt) {
psmouse->out_of_sync_cnt = 0;
- printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n",
- psmouse->name, psmouse->phys);
+ psmouse_notice(psmouse,
+ "%s at %s - driver resynced.\n",
+ psmouse->name, psmouse->phys);
}
break;
@@ -295,9 +302,10 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
((flags & SERIO_PARITY) && !psmouse->ignore_parity))) {
if (psmouse->state == PSMOUSE_ACTIVATED)
- printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n",
- flags & SERIO_TIMEOUT ? " timeout" : "",
- flags & SERIO_PARITY ? " bad parity" : "");
+ psmouse_warn(psmouse,
+ "bad data from KBC -%s%s\n",
+ flags & SERIO_TIMEOUT ? " timeout" : "",
+ flags & SERIO_PARITY ? " bad parity" : "");
ps2_cmd_aborted(&psmouse->ps2dev);
goto out;
}
@@ -315,8 +323,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
if (psmouse->state == PSMOUSE_ACTIVATED &&
psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
- printk(KERN_INFO "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
- psmouse->name, psmouse->phys, psmouse->pktcnt);
+ psmouse_info(psmouse, "%s at %s lost synchronization, throwing %d bytes away.\n",
+ psmouse->name, psmouse->phys, psmouse->pktcnt);
psmouse->badbyte = psmouse->packet[0];
__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
@@ -943,7 +951,8 @@ static int psmouse_probe(struct psmouse *psmouse)
*/
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
- printk(KERN_WARNING "psmouse.c: Failed to reset mouse on %s\n", ps2dev->serio->phys);
+ psmouse_warn(psmouse, "Failed to reset mouse on %s\n",
+ ps2dev->serio->phys);
return 0;
}
@@ -1005,8 +1014,8 @@ static void psmouse_initialize(struct psmouse *psmouse)
static void psmouse_activate(struct psmouse *psmouse)
{
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE))
- printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n",
- psmouse->ps2dev.serio->phys);
+ psmouse_warn(psmouse, "Failed to enable mouse on %s\n",
+ psmouse->ps2dev.serio->phys);
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
}
@@ -1020,14 +1029,14 @@ static void psmouse_activate(struct psmouse *psmouse)
static void psmouse_deactivate(struct psmouse *psmouse)
{
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE))
- printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n",
- psmouse->ps2dev.serio->phys);
+ psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n",
+ psmouse->ps2dev.serio->phys);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
}
/*
- * psmouse_poll() - default poll hanlder. Everyone except for ALPS uses it.
+ * psmouse_poll() - default poll handler. Everyone except for ALPS uses it.
*/
static int psmouse_poll(struct psmouse *psmouse)
@@ -1115,14 +1124,15 @@ static void psmouse_resync(struct work_struct *work)
}
if (!enabled) {
- printk(KERN_WARNING "psmouse.c: failed to re-enable mouse on %s\n",
- psmouse->ps2dev.serio->phys);
+ psmouse_warn(psmouse, "failed to re-enable mouse on %s\n",
+ psmouse->ps2dev.serio->phys);
failed = true;
}
if (failed) {
psmouse_set_state(psmouse, PSMOUSE_IGNORE);
- printk(KERN_INFO "psmouse.c: resync failed, issuing reconnect request\n");
+ psmouse_info(psmouse,
+ "resync failed, issuing reconnect request\n");
serio_reconnect(serio);
} else
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
@@ -1155,8 +1165,8 @@ static void psmouse_cleanup(struct serio *serio)
* Disable stream mode so cleanup routine can proceed undisturbed.
*/
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE))
- printk(KERN_WARNING "psmouse.c: Failed to disable mouse on %s\n",
- psmouse->ps2dev.serio->phys);
+ psmouse_warn(psmouse, "Failed to disable mouse on %s\n",
+ psmouse->ps2dev.serio->phys);
if (psmouse->cleanup)
psmouse->cleanup(psmouse);
@@ -1400,7 +1410,8 @@ static int psmouse_reconnect(struct serio *serio)
int rc = -1;
if (!drv || !psmouse) {
- printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n");
+ psmouse_dbg(psmouse,
+ "reconnect request, but serio is disconnected, ignoring...\n");
return -1;
}
@@ -1427,8 +1438,9 @@ static int psmouse_reconnect(struct serio *serio)
goto out;
}
- /* ok, the device type (and capabilities) match the old one,
- * we can continue using it, complete intialization
+ /*
+ * OK, the device type (and capabilities) match the old one,
+ * we can continue using it, complete initialization
*/
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
@@ -1586,9 +1598,8 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
while (!list_empty(&serio->children)) {
if (++retry > 3) {
- printk(KERN_WARNING
- "psmouse: failed to destroy children ports, "
- "protocol change aborted.\n");
+ psmouse_warn(psmouse,
+ "failed to destroy children ports, protocol change aborted.\n");
input_free_device(new_dev);
return -EIO;
}
@@ -1715,7 +1726,7 @@ static int __init psmouse_init(void)
kpsmoused_wq = create_singlethread_workqueue("kpsmoused");
if (!kpsmoused_wq) {
- printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n");
+ pr_err("failed to create kpsmoused workqueue\n");
return -ENOMEM;
}
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 593e910bfc7a..9b84b0c4e371 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -150,4 +150,29 @@ static struct psmouse_attribute psmouse_attr_##_name = { \
static ssize_t _set(struct psmouse *, void *, const char *, size_t); \
__PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, NULL, _set, true)
+#ifndef psmouse_fmt
+#define psmouse_fmt(fmt) KBUILD_BASENAME ": " fmt
+#endif
+
+#define psmouse_dbg(psmouse, format, ...) \
+ dev_dbg(&(psmouse)->ps2dev.serio->dev, \
+ psmouse_fmt(format), ##__VA_ARGS__)
+#define psmouse_info(psmouse, format, ...) \
+ dev_info(&(psmouse)->ps2dev.serio->dev, \
+ psmouse_fmt(format), ##__VA_ARGS__)
+#define psmouse_warn(psmouse, format, ...) \
+ dev_warn(&(psmouse)->ps2dev.serio->dev, \
+ psmouse_fmt(format), ##__VA_ARGS__)
+#define psmouse_err(psmouse, format, ...) \
+ dev_err(&(psmouse)->ps2dev.serio->dev, \
+ psmouse_fmt(format), ##__VA_ARGS__)
+#define psmouse_notice(psmouse, format, ...) \
+ dev_notice(&(psmouse)->ps2dev.serio->dev, \
+ psmouse_fmt(format), ##__VA_ARGS__)
+#define psmouse_printk(level, psmouse, format, ...) \
+ dev_printk(level, \
+ &(psmouse)->ps2dev.serio->dev, \
+ psmouse_fmt(format), ##__VA_ARGS__)
+
+
#endif /* _PSMOUSE_H */
diff --git a/drivers/input/mouse/pxa930_trkball.c b/drivers/input/mouse/pxa930_trkball.c
index 943cfec15665..ee3b0ca9d592 100644
--- a/drivers/input/mouse/pxa930_trkball.c
+++ b/drivers/input/mouse/pxa930_trkball.c
@@ -12,7 +12,6 @@
#include <linux/init.h>
#include <linux/input.h>
-#include <linux/version.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -184,7 +183,7 @@ static int __devinit pxa930_trkball_probe(struct platform_device *pdev)
/* held the module in reset, will be enabled in open() */
pxa930_trkball_disable(trkball);
- error = request_irq(irq, pxa930_trkball_interrupt, IRQF_DISABLED,
+ error = request_irq(irq, pxa930_trkball_interrupt, 0,
pdev->name, trkball);
if (error) {
dev_err(&pdev->dev, "failed to request irq: %d\n", error);
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index 1242775fee19..c5b12d2e955a 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -20,7 +20,6 @@
*/
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/input.h>
#include <linux/ctype.h>
#include <linux/libps2.h>
@@ -608,11 +607,12 @@ static void fsp_packet_debug(unsigned char packet[])
ps2_packet_cnt++;
jiffies_msec = jiffies_to_msecs(jiffies);
- printk(KERN_DEBUG "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n",
- jiffies_msec, packet[0], packet[1], packet[2], packet[3]);
+ psmouse_dbg(psmouse,
+ "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n",
+ jiffies_msec, packet[0], packet[1], packet[2], packet[3]);
if (jiffies_msec - ps2_last_second > 1000) {
- printk(KERN_DEBUG "PS/2 packets/sec = %d\n", ps2_packet_cnt);
+ psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n", ps2_packet_cnt);
ps2_packet_cnt = 0;
ps2_last_second = jiffies_msec;
}
@@ -821,9 +821,9 @@ int fsp_init(struct psmouse *psmouse)
return -ENODEV;
}
- printk(KERN_INFO
- "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n",
- ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7);
+ psmouse_info(psmouse,
+ "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n",
+ ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7);
psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL);
if (!priv)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index e06e045bf907..c080b828e5dc 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -44,6 +44,16 @@
#define YMIN_NOMINAL 1408
#define YMAX_NOMINAL 4448
+/*
+ * Synaptics touchpads report the y coordinate from bottom to top, which is
+ * opposite from what userspace expects.
+ * This function is used to invert y before reporting.
+ */
+static int synaptics_invert_y(int y)
+{
+ return YMAX_NOMINAL + YMIN_NOMINAL - y;
+}
+
/*****************************************************************************
* Stuff we need even when we do not want native Synaptics support
@@ -157,8 +167,8 @@ static int synaptics_capability(struct psmouse *psmouse)
if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
- printk(KERN_ERR "Synaptics claims to have extended capabilities,"
- " but I'm not able to read them.\n");
+ psmouse_warn(psmouse,
+ "device claims to have extended capabilities, but I'm not able to read them.\n");
} else {
priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
@@ -173,8 +183,8 @@ static int synaptics_capability(struct psmouse *psmouse)
if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) {
if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) {
- printk(KERN_ERR "Synaptics claims to have extended capability 0x0c,"
- " but I'm not able to read it.\n");
+ psmouse_warn(psmouse,
+ "device claims to have extended capability 0x0c, but I'm not able to read it.\n");
} else {
priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2];
}
@@ -207,27 +217,37 @@ static int synaptics_identify(struct psmouse *psmouse)
static int synaptics_resolution(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
- unsigned char res[3];
- unsigned char max[3];
+ unsigned char resp[3];
if (SYN_ID_MAJOR(priv->identity) < 4)
return 0;
- if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res) == 0) {
- if (res[0] != 0 && (res[1] & 0x80) && res[2] != 0) {
- priv->x_res = res[0]; /* x resolution in units/mm */
- priv->y_res = res[2]; /* y resolution in units/mm */
+ if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp) == 0) {
+ if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) {
+ priv->x_res = resp[0]; /* x resolution in units/mm */
+ priv->y_res = resp[2]; /* y resolution in units/mm */
}
}
if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&
SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) {
- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_DIMENSIONS, max)) {
- printk(KERN_ERR "Synaptics claims to have dimensions query,"
- " but I'm not able to read it.\n");
+ if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) {
+ psmouse_warn(psmouse,
+ "device claims to have max coordinates query, but I'm not able to read it.\n");
+ } else {
+ priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+ priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+ }
+ }
+
+ if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 &&
+ SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c)) {
+ if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) {
+ psmouse_warn(psmouse,
+ "device claims to have min coordinates query, but I'm not able to read it.\n");
} else {
- priv->x_max = (max[0] << 5) | ((max[1] & 0x0f) << 1);
- priv->y_max = (max[2] << 5) | ((max[1] & 0xf0) >> 3);
+ priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+ priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
}
}
@@ -284,7 +304,8 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
static unsigned char param = 0xc8;
struct synaptics_data *priv = psmouse->private;
- if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+ if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
return 0;
if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
@@ -367,7 +388,8 @@ static void synaptics_pt_activate(struct psmouse *psmouse)
priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT;
if (synaptics_mode_cmd(psmouse, priv->mode))
- printk(KERN_INFO "synaptics: failed to switch guest protocol\n");
+ psmouse_warn(psmouse,
+ "failed to switch guest protocol\n");
}
}
@@ -377,7 +399,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!serio) {
- printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
+ psmouse_err(psmouse,
+ "not enough memory for pass-through port\n");
return;
}
@@ -391,7 +414,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)
psmouse->pt_activate = synaptics_pt_activate;
- printk(KERN_INFO "serio: %s port at %s\n", serio->name, psmouse->phys);
+ psmouse_info(psmouse, "serio: %s port at %s\n",
+ serio->name, psmouse->phys);
serio_register_port(serio);
}
@@ -399,6 +423,44 @@ static void synaptics_pt_create(struct psmouse *psmouse)
* Functions to interpret the absolute mode packets
****************************************************************************/
+static void synaptics_mt_state_set(struct synaptics_mt_state *state, int count,
+ int sgm, int agm)
+{
+ state->count = count;
+ state->sgm = sgm;
+ state->agm = agm;
+}
+
+static void synaptics_parse_agm(const unsigned char buf[],
+ struct synaptics_data *priv,
+ struct synaptics_hw_state *hw)
+{
+ struct synaptics_hw_state *agm = &priv->agm;
+ int agm_packet_type;
+
+ agm_packet_type = (buf[5] & 0x30) >> 4;
+ switch (agm_packet_type) {
+ case 1:
+ /* Gesture packet: (x, y, z) half resolution */
+ agm->w = hw->w;
+ agm->x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
+ agm->y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
+ agm->z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+ break;
+
+ case 2:
+ /* AGM-CONTACT packet: (count, sgm, agm) */
+ synaptics_mt_state_set(&agm->mt_state, buf[1], buf[2], buf[4]);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Record that at least one AGM has been received since last SGM */
+ priv->agm_pending = true;
+}
+
static int synaptics_parse_hw_state(const unsigned char buf[],
struct synaptics_data *priv,
struct synaptics_hw_state *hw)
@@ -406,26 +468,10 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
memset(hw, 0, sizeof(struct synaptics_hw_state));
if (SYN_MODEL_NEWABS(priv->model_id)) {
- hw->x = (((buf[3] & 0x10) << 8) |
- ((buf[1] & 0x0f) << 8) |
- buf[4]);
- hw->y = (((buf[3] & 0x20) << 7) |
- ((buf[1] & 0xf0) << 4) |
- buf[5]);
-
- hw->z = buf[2];
hw->w = (((buf[0] & 0x30) >> 2) |
((buf[0] & 0x04) >> 1) |
((buf[3] & 0x04) >> 2));
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
- /* Gesture packet: (x, y, z) at half resolution */
- priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
- priv->mt.y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
- priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
- return 1;
- }
-
hw->left = (buf[0] & 0x01) ? 1 : 0;
hw->right = (buf[0] & 0x02) ? 1 : 0;
@@ -448,6 +494,21 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
+ if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) &&
+ hw->w == 2) {
+ synaptics_parse_agm(buf, priv, hw);
+ return 1;
+ }
+
+ hw->x = (((buf[3] & 0x10) << 8) |
+ ((buf[1] & 0x0f) << 8) |
+ buf[4]);
+ hw->y = (((buf[3] & 0x20) << 7) |
+ ((buf[1] & 0xf0) << 4) |
+ buf[5]);
+ hw->z = buf[2];
+
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
((buf[0] ^ buf[3]) & 0x02)) {
switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
@@ -485,14 +546,14 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
return 0;
}
-static void set_slot(struct input_dev *dev, int slot, bool active, int x, int y)
+static void synaptics_report_semi_mt_slot(struct input_dev *dev, int slot,
+ bool active, int x, int y)
{
input_mt_slot(dev, slot);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
if (active) {
input_report_abs(dev, ABS_MT_POSITION_X, x);
- input_report_abs(dev, ABS_MT_POSITION_Y,
- YMAX_NOMINAL + YMIN_NOMINAL - y);
+ input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(y));
}
}
@@ -502,17 +563,401 @@ static void synaptics_report_semi_mt_data(struct input_dev *dev,
int num_fingers)
{
if (num_fingers >= 2) {
- set_slot(dev, 0, true, min(a->x, b->x), min(a->y, b->y));
- set_slot(dev, 1, true, max(a->x, b->x), max(a->y, b->y));
+ synaptics_report_semi_mt_slot(dev, 0, true, min(a->x, b->x),
+ min(a->y, b->y));
+ synaptics_report_semi_mt_slot(dev, 1, true, max(a->x, b->x),
+ max(a->y, b->y));
} else if (num_fingers == 1) {
- set_slot(dev, 0, true, a->x, a->y);
- set_slot(dev, 1, false, 0, 0);
+ synaptics_report_semi_mt_slot(dev, 0, true, a->x, a->y);
+ synaptics_report_semi_mt_slot(dev, 1, false, 0, 0);
} else {
- set_slot(dev, 0, false, 0, 0);
- set_slot(dev, 1, false, 0, 0);
+ synaptics_report_semi_mt_slot(dev, 0, false, 0, 0);
+ synaptics_report_semi_mt_slot(dev, 1, false, 0, 0);
}
}
+static void synaptics_report_buttons(struct psmouse *psmouse,
+ const struct synaptics_hw_state *hw)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
+ int i;
+
+ input_report_key(dev, BTN_LEFT, hw->left);
+ input_report_key(dev, BTN_RIGHT, hw->right);
+
+ if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+ input_report_key(dev, BTN_MIDDLE, hw->middle);
+
+ if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+ input_report_key(dev, BTN_FORWARD, hw->up);
+ input_report_key(dev, BTN_BACK, hw->down);
+ }
+
+ for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+ input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i));
+}
+
+static void synaptics_report_slot(struct input_dev *dev, int slot,
+ const struct synaptics_hw_state *hw)
+{
+ input_mt_slot(dev, slot);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL));
+ if (!hw)
+ return;
+
+ input_report_abs(dev, ABS_MT_POSITION_X, hw->x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y));
+ input_report_abs(dev, ABS_MT_PRESSURE, hw->z);
+}
+
+static void synaptics_report_mt_data(struct psmouse *psmouse,
+ struct synaptics_mt_state *mt_state,
+ const struct synaptics_hw_state *sgm)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
+ struct synaptics_hw_state *agm = &priv->agm;
+ struct synaptics_mt_state *old = &priv->mt_state;
+
+ switch (mt_state->count) {
+ case 0:
+ synaptics_report_slot(dev, 0, NULL);
+ synaptics_report_slot(dev, 1, NULL);
+ break;
+ case 1:
+ if (mt_state->sgm == -1) {
+ synaptics_report_slot(dev, 0, NULL);
+ synaptics_report_slot(dev, 1, NULL);
+ } else if (mt_state->sgm == 0) {
+ synaptics_report_slot(dev, 0, sgm);
+ synaptics_report_slot(dev, 1, NULL);
+ } else {
+ synaptics_report_slot(dev, 0, NULL);
+ synaptics_report_slot(dev, 1, sgm);
+ }
+ break;
+ default:
+ /*
+ * If the finger slot contained in SGM is valid, and either
+ * hasn't changed, or is new, then report SGM in MTB slot 0.
+ * Otherwise, empty MTB slot 0.
+ */
+ if (mt_state->sgm != -1 &&
+ (mt_state->sgm == old->sgm || old->sgm == -1))
+ synaptics_report_slot(dev, 0, sgm);
+ else
+ synaptics_report_slot(dev, 0, NULL);
+
+ /*
+ * If the finger slot contained in AGM is valid, and either
+ * hasn't changed, or is new, then report AGM in MTB slot 1.
+ * Otherwise, empty MTB slot 1.
+ */
+ if (mt_state->agm != -1 &&
+ (mt_state->agm == old->agm || old->agm == -1))
+ synaptics_report_slot(dev, 1, agm);
+ else
+ synaptics_report_slot(dev, 1, NULL);
+ break;
+ }
+
+ /* Don't use active slot count to generate BTN_TOOL events. */
+ input_mt_report_pointer_emulation(dev, false);
+
+ /* Send the number of fingers reported by touchpad itself. */
+ input_mt_report_finger_count(dev, mt_state->count);
+
+ synaptics_report_buttons(psmouse, sgm);
+
+ input_sync(dev);
+}
+
+/* Handle case where mt_state->count = 0 */
+static void synaptics_image_sensor_0f(struct synaptics_data *priv,
+ struct synaptics_mt_state *mt_state)
+{
+ synaptics_mt_state_set(mt_state, 0, -1, -1);
+ priv->mt_state_lost = false;
+}
+
+/* Handle case where mt_state->count = 1 */
+static void synaptics_image_sensor_1f(struct synaptics_data *priv,
+ struct synaptics_mt_state *mt_state)
+{
+ struct synaptics_hw_state *agm = &priv->agm;
+ struct synaptics_mt_state *old = &priv->mt_state;
+
+ /*
+ * If the last AGM was (0,0,0), and there is only one finger left,
+ * then we absolutely know that SGM contains slot 0, and all other
+ * fingers have been removed.
+ */
+ if (priv->agm_pending && agm->z == 0) {
+ synaptics_mt_state_set(mt_state, 1, 0, -1);
+ priv->mt_state_lost = false;
+ return;
+ }
+
+ switch (old->count) {
+ case 0:
+ synaptics_mt_state_set(mt_state, 1, 0, -1);
+ break;
+ case 1:
+ /*
+ * If mt_state_lost, then the previous transition was 3->1,
+ * and SGM now contains either slot 0 or 1, but we don't know
+ * which. So, we just assume that the SGM now contains slot 1.
+ *
+ * If pending AGM and either:
+ * (a) the previous SGM slot contains slot 0, or
+ * (b) there was no SGM slot
+ * then, the SGM now contains slot 1
+ *
+ * Case (a) happens with very rapid "drum roll" gestures, where
+ * slot 0 finger is lifted and a new slot 1 finger touches
+ * within one reporting interval.
+ *
+ * Case (b) happens if initially two or more fingers tap
+ * briefly, and all but one lift before the end of the first
+ * reporting interval.
+ *
+ * (In both these cases, slot 0 will becomes empty, so SGM
+ * contains slot 1 with the new finger)
+ *
+ * Else, if there was no previous SGM, it now contains slot 0.
+ *
+ * Otherwise, SGM still contains the same slot.
+ */
+ if (priv->mt_state_lost ||
+ (priv->agm_pending && old->sgm <= 0))
+ synaptics_mt_state_set(mt_state, 1, 1, -1);
+ else if (old->sgm == -1)
+ synaptics_mt_state_set(mt_state, 1, 0, -1);
+ break;
+ case 2:
+ /*
+ * If mt_state_lost, we don't know which finger SGM contains.
+ *
+ * So, report 1 finger, but with both slots empty.
+ * We will use slot 1 on subsequent 1->1
+ */
+ if (priv->mt_state_lost) {
+ synaptics_mt_state_set(mt_state, 1, -1, -1);
+ break;
+ }
+ /*
+ * Since the last AGM was NOT (0,0,0), it was the finger in
+ * slot 0 that has been removed.
+ * So, SGM now contains previous AGM's slot, and AGM is now
+ * empty.
+ */
+ synaptics_mt_state_set(mt_state, 1, old->agm, -1);
+ break;
+ case 3:
+ /*
+ * Since last AGM was not (0,0,0), we don't know which finger
+ * is left.
+ *
+ * So, report 1 finger, but with both slots empty.
+ * We will use slot 1 on subsequent 1->1
+ */
+ synaptics_mt_state_set(mt_state, 1, -1, -1);
+ priv->mt_state_lost = true;
+ break;
+ case 4:
+ case 5:
+ /* mt_state was updated by AGM-CONTACT packet */
+ break;
+ }
+}
+
+/* Handle case where mt_state->count = 2 */
+static void synaptics_image_sensor_2f(struct synaptics_data *priv,
+ struct synaptics_mt_state *mt_state)
+{
+ struct synaptics_mt_state *old = &priv->mt_state;
+
+ switch (old->count) {
+ case 0:
+ synaptics_mt_state_set(mt_state, 2, 0, 1);
+ break;
+ case 1:
+ /*
+ * If previous SGM contained slot 1 or higher, SGM now contains
+ * slot 0 (the newly touching finger) and AGM contains SGM's
+ * previous slot.
+ *
+ * Otherwise, SGM still contains slot 0 and AGM now contains
+ * slot 1.
+ */
+ if (old->sgm >= 1)
+ synaptics_mt_state_set(mt_state, 2, 0, old->sgm);
+ else
+ synaptics_mt_state_set(mt_state, 2, 0, 1);
+ break;
+ case 2:
+ /*
+ * If mt_state_lost, SGM now contains either finger 1 or 2, but
+ * we don't know which.
+ * So, we just assume that the SGM contains slot 0 and AGM 1.
+ */
+ if (priv->mt_state_lost)
+ synaptics_mt_state_set(mt_state, 2, 0, 1);
+ /*
+ * Otherwise, use the same mt_state, since it either hasn't
+ * changed, or was updated by a recently received AGM-CONTACT
+ * packet.
+ */
+ break;
+ case 3:
+ /*
+ * 3->2 transitions have two unsolvable problems:
+ * 1) no indication is given which finger was removed
+ * 2) no way to tell if agm packet was for finger 3
+ * before 3->2, or finger 2 after 3->2.
+ *
+ * So, report 2 fingers, but empty all slots.
+ * We will guess slots [0,1] on subsequent 2->2.
+ */
+ synaptics_mt_state_set(mt_state, 2, -1, -1);
+ priv->mt_state_lost = true;
+ break;
+ case 4:
+ case 5:
+ /* mt_state was updated by AGM-CONTACT packet */
+ break;
+ }
+}
+
+/* Handle case where mt_state->count = 3 */
+static void synaptics_image_sensor_3f(struct synaptics_data *priv,
+ struct synaptics_mt_state *mt_state)
+{
+ struct synaptics_mt_state *old = &priv->mt_state;
+
+ switch (old->count) {
+ case 0:
+ synaptics_mt_state_set(mt_state, 3, 0, 2);
+ break;
+ case 1:
+ /*
+ * If previous SGM contained slot 2 or higher, SGM now contains
+ * slot 0 (one of the newly touching fingers) and AGM contains
+ * SGM's previous slot.
+ *
+ * Otherwise, SGM now contains slot 0 and AGM contains slot 2.
+ */
+ if (old->sgm >= 2)
+ synaptics_mt_state_set(mt_state, 3, 0, old->sgm);
+ else
+ synaptics_mt_state_set(mt_state, 3, 0, 2);
+ break;
+ case 2:
+ /*
+ * If the AGM previously contained slot 3 or higher, then the
+ * newly touching finger is in the lowest available slot.
+ *
+ * If SGM was previously 1 or higher, then the new SGM is
+ * now slot 0 (with a new finger), otherwise, the new finger
+ * is now in a hidden slot between 0 and AGM's slot.
+ *
+ * In all such cases, the SGM now contains slot 0, and the AGM
+ * continues to contain the same slot as before.
+ */
+ if (old->agm >= 3) {
+ synaptics_mt_state_set(mt_state, 3, 0, old->agm);
+ break;
+ }
+
+ /*
+ * After some 3->1 and all 3->2 transitions, we lose track
+ * of which slot is reported by SGM and AGM.
+ *
+ * For 2->3 in this state, report 3 fingers, but empty all
+ * slots, and we will guess (0,2) on a subsequent 0->3.
+ *
+ * To userspace, the resulting transition will look like:
+ * 2:[0,1] -> 3:[-1,-1] -> 3:[0,2]
+ */
+ if (priv->mt_state_lost) {
+ synaptics_mt_state_set(mt_state, 3, -1, -1);
+ break;
+ }
+
+ /*
+ * If the (SGM,AGM) really previously contained slots (0, 1),
+ * then we cannot know what slot was just reported by the AGM,
+ * because the 2->3 transition can occur either before or after
+ * the AGM packet. Thus, this most recent AGM could contain
+ * either the same old slot 1 or the new slot 2.
+ * Subsequent AGMs will be reporting slot 2.
+ *
+ * To userspace, the resulting transition will look like:
+ * 2:[0,1] -> 3:[0,-1] -> 3:[0,2]
+ */
+ synaptics_mt_state_set(mt_state, 3, 0, -1);
+ break;
+ case 3:
+ /*
+ * If, for whatever reason, the previous agm was invalid,
+ * Assume SGM now contains slot 0, AGM now contains slot 2.
+ */
+ if (old->agm <= 2)
+ synaptics_mt_state_set(mt_state, 3, 0, 2);
+ /*
+ * mt_state either hasn't changed, or was updated by a recently
+ * received AGM-CONTACT packet.
+ */
+ break;
+
+ case 4:
+ case 5:
+ /* mt_state was updated by AGM-CONTACT packet */
+ break;
+ }
+}
+
+/* Handle case where mt_state->count = 4, or = 5 */
+static void synaptics_image_sensor_45f(struct synaptics_data *priv,
+ struct synaptics_mt_state *mt_state)
+{
+ /* mt_state was updated correctly by AGM-CONTACT packet */
+ priv->mt_state_lost = false;
+}
+
+static void synaptics_image_sensor_process(struct psmouse *psmouse,
+ struct synaptics_hw_state *sgm)
+{
+ struct synaptics_data *priv = psmouse->private;
+ struct synaptics_hw_state *agm = &priv->agm;
+ struct synaptics_mt_state mt_state;
+
+ /* Initialize using current mt_state (as updated by last agm) */
+ mt_state = agm->mt_state;
+
+ /*
+ * Update mt_state using the new finger count and current mt_state.
+ */
+ if (sgm->z == 0)
+ synaptics_image_sensor_0f(priv, &mt_state);
+ else if (sgm->w >= 4)
+ synaptics_image_sensor_1f(priv, &mt_state);
+ else if (sgm->w == 0)
+ synaptics_image_sensor_2f(priv, &mt_state);
+ else if (sgm->w == 1 && mt_state.count <= 3)
+ synaptics_image_sensor_3f(priv, &mt_state);
+ else
+ synaptics_image_sensor_45f(priv, &mt_state);
+
+ /* Send resulting input events to user space */
+ synaptics_report_mt_data(psmouse, &mt_state, sgm);
+
+ /* Store updated mt_state */
+ priv->mt_state = agm->mt_state = mt_state;
+ priv->agm_pending = false;
+}
+
/*
* called for each full received packet from the touchpad
*/
@@ -523,11 +968,15 @@ static void synaptics_process_packet(struct psmouse *psmouse)
struct synaptics_hw_state hw;
int num_fingers;
int finger_width;
- int i;
if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
return;
+ if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ synaptics_image_sensor_process(psmouse, &hw);
+ return;
+ }
+
if (hw.scroll) {
priv->scroll += hw.scroll;
@@ -573,7 +1022,8 @@ static void synaptics_process_packet(struct psmouse *psmouse)
}
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
- synaptics_report_semi_mt_data(dev, &hw, &priv->mt, num_fingers);
+ synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
+ num_fingers);
/* Post events
* BTN_TOUCH has to be first as mousedev relies on it when doing
@@ -584,7 +1034,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
if (num_fingers > 0) {
input_report_abs(dev, ABS_X, hw.x);
- input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
+ input_report_abs(dev, ABS_Y, synaptics_invert_y(hw.y));
}
input_report_abs(dev, ABS_PRESSURE, hw.z);
@@ -592,35 +1042,25 @@ static void synaptics_process_packet(struct psmouse *psmouse)
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
- input_report_key(dev, BTN_LEFT, hw.left);
- input_report_key(dev, BTN_RIGHT, hw.right);
-
if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
}
- if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
- input_report_key(dev, BTN_MIDDLE, hw.middle);
-
- if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
- input_report_key(dev, BTN_FORWARD, hw.up);
- input_report_key(dev, BTN_BACK, hw.down);
- }
-
- for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
- input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
+ synaptics_report_buttons(psmouse, &hw);
input_sync(dev);
}
-static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type)
+static int synaptics_validate_byte(struct psmouse *psmouse,
+ int idx, unsigned char pkt_type)
{
static const unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
static const unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
static const unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
static const unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
static const unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
+ const char *packet = psmouse->packet;
if (idx < 0 || idx > 4)
return 0;
@@ -638,7 +1078,7 @@ static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned cha
return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
default:
- printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type);
+ psmouse_err(psmouse, "unknown packet type %d\n", pkt_type);
return 0;
}
}
@@ -648,8 +1088,8 @@ static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)
int i;
for (i = 0; i < 5; i++)
- if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) {
- printk(KERN_INFO "synaptics: using relaxed packet validation\n");
+ if (!synaptics_validate_byte(psmouse, i, SYN_NEWABS_STRICT)) {
+ psmouse_info(psmouse, "using relaxed packet validation\n");
return SYN_NEWABS_RELAXED;
}
@@ -674,13 +1114,30 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
return PSMOUSE_FULL_PACKET;
}
- return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ?
+ return synaptics_validate_byte(psmouse, psmouse->pktcnt - 1, priv->pkt_type) ?
PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
}
/*****************************************************************************
* Driver initialization/cleanup functions
****************************************************************************/
+static void set_abs_position_params(struct input_dev *dev,
+ struct synaptics_data *priv, int x_code,
+ int y_code)
+{
+ int x_min = priv->x_min ?: XMIN_NOMINAL;
+ int x_max = priv->x_max ?: XMAX_NOMINAL;
+ int y_min = priv->y_min ?: YMIN_NOMINAL;
+ int y_max = priv->y_max ?: YMAX_NOMINAL;
+ int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ?
+ SYN_REDUCED_FILTER_FUZZ : 0;
+
+ input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0);
+ input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0);
+ input_abs_set_res(dev, x_code, priv->x_res);
+ input_abs_set_res(dev, y_code, priv->y_res);
+}
+
static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
{
int i;
@@ -688,19 +1145,25 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
__set_bit(INPUT_PROP_POINTER, dev->propbit);
__set_bit(EV_ABS, dev->evbit);
- input_set_abs_params(dev, ABS_X,
- XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0);
- input_set_abs_params(dev, ABS_Y,
- YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0);
+ set_abs_position_params(dev, priv, ABS_X, ABS_Y);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+ if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ input_mt_init_slots(dev, 2);
+ set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+ ABS_MT_POSITION_Y);
+ /* Image sensors can report per-contact pressure */
+ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+ /* Image sensors can signal 4 and 5 finger clicks */
+ __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+ __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
+ } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+ /* Non-image sensors with AGM use semi-mt */
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
input_mt_init_slots(dev, 2);
- input_set_abs_params(dev, ABS_MT_POSITION_X, XMIN_NOMINAL,
- priv->x_max ?: XMAX_NOMINAL, 0, 0);
- input_set_abs_params(dev, ABS_MT_POSITION_Y, YMIN_NOMINAL,
- priv->y_max ?: YMAX_NOMINAL, 0, 0);
+ set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+ ABS_MT_POSITION_Y);
}
if (SYN_CAP_PALMDETECT(priv->capabilities))
@@ -733,9 +1196,6 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
__clear_bit(REL_X, dev->relbit);
__clear_bit(REL_Y, dev->relbit);
- input_abs_set_res(dev, ABS_X, priv->x_res);
- input_abs_set_res(dev, ABS_Y, priv->y_res);
-
if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
/* Clickpads report only left button */
@@ -767,21 +1227,21 @@ static int synaptics_reconnect(struct psmouse *psmouse)
return -1;
if (retry > 1)
- printk(KERN_DEBUG "Synaptics reconnected after %d tries\n",
- retry);
+ psmouse_dbg(psmouse, "reconnected after %d tries\n", retry);
if (synaptics_query_hardware(psmouse)) {
- printk(KERN_ERR "Unable to query Synaptics hardware.\n");
+ psmouse_err(psmouse, "Unable to query device.\n");
return -1;
}
if (synaptics_set_absolute_mode(psmouse)) {
- printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
+ psmouse_err(psmouse, "Unable to initialize device.\n");
return -1;
}
if (synaptics_set_advanced_gesture_mode(psmouse)) {
- printk(KERN_ERR "Advanced gesture mode reconnect failed.\n");
+ psmouse_err(psmouse,
+ "Advanced gesture mode reconnect failed.\n");
return -1;
}
@@ -789,12 +1249,12 @@ static int synaptics_reconnect(struct psmouse *psmouse)
old_priv.model_id != priv->model_id ||
old_priv.capabilities != priv->capabilities ||
old_priv.ext_cap != priv->ext_cap) {
- printk(KERN_ERR "Synaptics hardware appears to be different: "
- "id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n",
- old_priv.identity, priv->identity,
- old_priv.model_id, priv->model_id,
- old_priv.capabilities, priv->capabilities,
- old_priv.ext_cap, priv->ext_cap);
+ psmouse_err(psmouse,
+ "hardware appears to be different: id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n",
+ old_priv.identity, priv->identity,
+ old_priv.model_id, priv->model_id,
+ old_priv.capabilities, priv->capabilities,
+ old_priv.ext_cap, priv->ext_cap);
return -1;
}
@@ -875,7 +1335,8 @@ int synaptics_init(struct psmouse *psmouse)
* just fine.
*/
if (broken_olpc_ec) {
- printk(KERN_INFO "synaptics: OLPC XO detected, not enabling Synaptics protocol.\n");
+ psmouse_info(psmouse,
+ "OLPC XO detected, not enabling Synaptics protocol.\n");
return -ENODEV;
}
@@ -886,26 +1347,28 @@ int synaptics_init(struct psmouse *psmouse)
psmouse_reset(psmouse);
if (synaptics_query_hardware(psmouse)) {
- printk(KERN_ERR "Unable to query Synaptics hardware.\n");
+ psmouse_err(psmouse, "Unable to query device.\n");
goto init_fail;
}
if (synaptics_set_absolute_mode(psmouse)) {
- printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
+ psmouse_err(psmouse, "Unable to initialize device.\n");
goto init_fail;
}
if (synaptics_set_advanced_gesture_mode(psmouse)) {
- printk(KERN_ERR "Advanced gesture mode init failed.\n");
+ psmouse_err(psmouse, "Advanced gesture mode init failed.\n");
goto init_fail;
}
priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
- printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n",
- SYN_ID_MODEL(priv->identity),
- SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
- priv->model_id, priv->capabilities, priv->ext_cap, priv->ext_cap_0c);
+ psmouse_info(psmouse,
+ "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n",
+ SYN_ID_MODEL(priv->identity),
+ SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
+ priv->model_id,
+ priv->capabilities, priv->ext_cap, priv->ext_cap_0c);
set_input_params(psmouse->dev, priv);
@@ -937,8 +1400,9 @@ int synaptics_init(struct psmouse *psmouse)
* the same rate as a standard PS/2 mouse).
*/
if (psmouse->rate >= 80 && impaired_toshiba_kbc) {
- printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n",
- dmi_get_system_info(DMI_PRODUCT_NAME));
+ psmouse_info(psmouse,
+ "Toshiba %s detected, limiting rate to 40pps.\n",
+ dmi_get_system_info(DMI_PRODUCT_NAME));
psmouse->rate = 40;
}
@@ -971,4 +1435,3 @@ bool synaptics_supported(void)
}
#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
-
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 7453938bf5ef..622aea8dd7e0 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -19,7 +19,8 @@
#define SYN_QUE_RESOLUTION 0x08
#define SYN_QUE_EXT_CAPAB 0x09
#define SYN_QUE_EXT_CAPAB_0C 0x0c
-#define SYN_QUE_EXT_DIMENSIONS 0x0d
+#define SYN_QUE_EXT_MAX_COORDS 0x0d
+#define SYN_QUE_EXT_MIN_COORDS 0x0f
/* synatics modes */
#define SYN_BIT_ABSOLUTE_MODE (1 << 7)
@@ -66,18 +67,24 @@
* 1 0x60 multifinger mode identifies firmware finger counting
* (not reporting!) algorithm.
* Not particularly meaningful
- * 1 0x80 covered pad W clipped to 14, 15 == pad mostly covered
- * 2 0x01 clickpad bit 1 2-button ClickPad
- * 2 0x02 deluxe LED controls touchpad support LED commands
+ * 1 0x80 covered pad W clipped to 14, 15 == pad mostly covered
+ * 2 0x01 clickpad bit 1 2-button ClickPad
+ * 2 0x02 deluxe LED controls touchpad support LED commands
* ala multimedia control bar
* 2 0x04 reduced filtering firmware does less filtering on
* position data, driver should watch
* for noise.
+ * 2 0x08 image sensor image sensor tracks 5 fingers, but only
+ * reports 2.
+ * 2 0x20 report min query 0x0f gives min coord reported
*/
#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */
#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */
#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000)
+#define SYN_CAP_MIN_DIMENSIONS(ex0c) ((ex0c) & 0x002000)
#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000)
+#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400)
+#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
@@ -104,10 +111,22 @@
#define SYN_NEWABS_RELAXED 2
#define SYN_OLDABS 3
+/* amount to fuzz position data when touchpad reports reduced filtering */
+#define SYN_REDUCED_FILTER_FUZZ 8
+
/*
- * A structure to describe the state of the touchpad hardware (buttons and pad)
+ * A structure to describe which internal touchpad finger slots are being
+ * reported in raw packets.
*/
+struct synaptics_mt_state {
+ int count; /* num fingers being tracked */
+ int sgm; /* which slot is reported by sgm pkt */
+ int agm; /* which slot is reported by agm pkt*/
+};
+/*
+ * A structure to describe the state of the touchpad hardware (buttons and pad)
+ */
struct synaptics_hw_state {
int x;
int y;
@@ -120,6 +139,9 @@ struct synaptics_hw_state {
unsigned int down:1;
unsigned char ext_buttons;
signed char scroll;
+
+ /* As reported in last AGM-CONTACT packets */
+ struct synaptics_mt_state mt_state;
};
struct synaptics_data {
@@ -130,7 +152,8 @@ struct synaptics_data {
unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */
unsigned long int identity; /* Identification */
unsigned int x_res, y_res; /* X/Y resolution in units/mm */
- unsigned int x_max, y_max; /* Max dimensions (from FW) */
+ unsigned int x_max, y_max; /* Max coordinates (from FW) */
+ unsigned int x_min, y_min; /* Min coordinates (from FW) */
unsigned char pkt_type; /* packet type - old, new, etc */
unsigned char mode; /* current mode byte */
@@ -138,7 +161,15 @@ struct synaptics_data {
struct serio *pt_port; /* Pass-through serio port */
- struct synaptics_hw_state mt; /* current gesture packet */
+ struct synaptics_mt_state mt_state; /* Current mt finger state */
+ bool mt_state_lost; /* mt_state may be incorrect */
+
+ /*
+ * Last received Advanced Gesture Mode (AGM) packet. An AGM packet
+ * contains position data for a second contact, at half resolution.
+ */
+ struct synaptics_hw_state agm;
+ bool agm_pending; /* new AGM packet received */
};
void synaptics_module_init(void);
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c
index cba3c84d2f21..4b755cb5b38c 100644
--- a/drivers/input/mouse/synaptics_i2c.c
+++ b/drivers/input/mouse/synaptics_i2c.c
@@ -570,7 +570,7 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client,
"Requesting IRQ: %d\n", touch->client->irq);
ret = request_irq(touch->client->irq, synaptics_i2c_irq,
- IRQF_DISABLED|IRQ_TYPE_EDGE_FALLING,
+ IRQ_TYPE_EDGE_FALLING,
DRIVER_NAME, touch);
if (ret) {
dev_warn(&touch->client->dev,
@@ -619,7 +619,7 @@ static int __devexit synaptics_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int synaptics_i2c_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
diff --git a/drivers/input/serio/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c
index 4b2a42f9f0bb..d4d08bd9205b 100644
--- a/drivers/input/serio/ams_delta_serio.c
+++ b/drivers/input/serio/ams_delta_serio.c
@@ -24,6 +24,7 @@
#include <linux/irq.h>
#include <linux/serio.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <asm/mach-types.h>
#include <plat/board-ams-delta.h>
diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c
index 6ee8f0ddad51..95280f9207e1 100644
--- a/drivers/input/serio/at32psif.c
+++ b/drivers/input/serio/at32psif.c
@@ -372,6 +372,6 @@ static void __exit psif_exit(void)
module_init(psif_init);
module_exit(psif_exit);
-MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 42206205e4f5..979c443bf1ef 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -795,7 +795,7 @@ int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback)
/************************* Keepalive timer task *********************/
-void hp_sdc_kicker (unsigned long data)
+static void hp_sdc_kicker(unsigned long data)
{
tasklet_schedule(&hp_sdc.task);
/* Re-insert the periodic task. */
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index bb9f5d31f0d0..b4cfc6c8be89 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -431,6 +431,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
},
},
+ {
+ /* Newer HP Pavilion dv4 models */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"),
+ },
+ },
{ }
};
@@ -560,6 +567,13 @@ static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
},
},
+ {
+ /* Newer HP Pavilion dv4 models */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"),
+ },
+ },
{ }
};
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index 980af94ba9c8..07a8363f3c5c 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -210,7 +210,7 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
/*
* Some devices (Synaptics) peform the reset before
* ACKing the reset command, and so it can take a long
- * time before the ACK arrrives.
+ * time before the ACK arrives.
*/
if (ps2_sendbyte(ps2dev, command & 0xff,
command == PS2_CMD_RESET_BAT ? 1000 : 200))
diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c
index d55874e5d1c2..44fc8b4bcd81 100644
--- a/drivers/input/serio/sa1111ps2.c
+++ b/drivers/input/serio/sa1111ps2.c
@@ -300,8 +300,7 @@ static int __devinit ps2_probe(struct sa1111_dev *dev)
out:
sa1111_disable_device(ps2if->dev);
- release_mem_region(dev->res.start,
- dev->res.end - dev->res.start + 1);
+ release_mem_region(dev->res.start, resource_size(&dev->res));
free:
sa1111_set_drvdata(dev, NULL);
kfree(ps2if);
@@ -317,8 +316,7 @@ static int __devexit ps2_remove(struct sa1111_dev *dev)
struct ps2if *ps2if = sa1111_get_drvdata(dev);
serio_unregister_port(ps2if->io);
- release_mem_region(dev->res.start,
- dev->res.end - dev->res.start + 1);
+ release_mem_region(dev->res.start, resource_size(&dev->res));
sa1111_set_drvdata(dev, NULL);
kfree(ps2if);
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index b7ba4597f7f0..4d4cd142bbbb 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -9,6 +9,7 @@
* the Free Software Foundation.
*/
+#include <linux/kref.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
@@ -33,15 +34,16 @@ struct serio_raw {
unsigned int tail, head;
char name[16];
- unsigned int refcnt;
+ struct kref kref;
struct serio *serio;
struct miscdevice dev;
wait_queue_head_t wait;
- struct list_head list;
+ struct list_head client_list;
struct list_head node;
+ bool dead;
};
-struct serio_raw_list {
+struct serio_raw_client {
struct fasync_struct *fasync;
struct serio_raw *serio_raw;
struct list_head node;
@@ -49,7 +51,6 @@ struct serio_raw_list {
static DEFINE_MUTEX(serio_raw_mutex);
static LIST_HEAD(serio_raw_list);
-static unsigned int serio_raw_no;
/*********************************************************************
* Interface with userspace (file operations) *
@@ -57,9 +58,9 @@ static unsigned int serio_raw_no;
static int serio_raw_fasync(int fd, struct file *file, int on)
{
- struct serio_raw_list *list = file->private_data;
+ struct serio_raw_client *client = file->private_data;
- return fasync_helper(fd, file, on, &list->fasync);
+ return fasync_helper(fd, file, on, &client->fasync);
}
static struct serio_raw *serio_raw_locate(int minor)
@@ -77,8 +78,8 @@ static struct serio_raw *serio_raw_locate(int minor)
static int serio_raw_open(struct inode *inode, struct file *file)
{
struct serio_raw *serio_raw;
- struct serio_raw_list *list;
- int retval = 0;
+ struct serio_raw_client *client;
+ int retval;
retval = mutex_lock_interruptible(&serio_raw_mutex);
if (retval)
@@ -90,60 +91,61 @@ static int serio_raw_open(struct inode *inode, struct file *file)
goto out;
}
- if (!serio_raw->serio) {
+ if (serio_raw->dead) {
retval = -ENODEV;
goto out;
}
- list = kzalloc(sizeof(struct serio_raw_list), GFP_KERNEL);
- if (!list) {
+ client = kzalloc(sizeof(struct serio_raw_client), GFP_KERNEL);
+ if (!client) {
retval = -ENOMEM;
goto out;
}
- list->serio_raw = serio_raw;
- file->private_data = list;
+ client->serio_raw = serio_raw;
+ file->private_data = client;
+
+ kref_get(&serio_raw->kref);
- serio_raw->refcnt++;
- list_add_tail(&list->node, &serio_raw->list);
+ serio_pause_rx(serio_raw->serio);
+ list_add_tail(&client->node, &serio_raw->client_list);
+ serio_continue_rx(serio_raw->serio);
out:
mutex_unlock(&serio_raw_mutex);
return retval;
}
-static int serio_raw_cleanup(struct serio_raw *serio_raw)
+static void serio_raw_free(struct kref *kref)
{
- if (--serio_raw->refcnt == 0) {
- misc_deregister(&serio_raw->dev);
- list_del_init(&serio_raw->node);
- kfree(serio_raw);
+ struct serio_raw *serio_raw =
+ container_of(kref, struct serio_raw, kref);
- return 1;
- }
-
- return 0;
+ put_device(&serio_raw->serio->dev);
+ kfree(serio_raw);
}
static int serio_raw_release(struct inode *inode, struct file *file)
{
- struct serio_raw_list *list = file->private_data;
- struct serio_raw *serio_raw = list->serio_raw;
+ struct serio_raw_client *client = file->private_data;
+ struct serio_raw *serio_raw = client->serio_raw;
- mutex_lock(&serio_raw_mutex);
+ serio_pause_rx(serio_raw->serio);
+ list_del(&client->node);
+ serio_continue_rx(serio_raw->serio);
- serio_raw_cleanup(serio_raw);
+ kfree(client);
+
+ kref_put(&serio_raw->kref, serio_raw_free);
- mutex_unlock(&serio_raw_mutex);
return 0;
}
-static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
+static bool serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
{
- unsigned long flags;
- int empty;
+ bool empty;
- spin_lock_irqsave(&serio_raw->serio->lock, flags);
+ serio_pause_rx(serio_raw->serio);
empty = serio_raw->head == serio_raw->tail;
if (!empty) {
@@ -151,30 +153,31 @@ static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN;
}
- spin_unlock_irqrestore(&serio_raw->serio->lock, flags);
+ serio_continue_rx(serio_raw->serio);
return !empty;
}
-static ssize_t serio_raw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+static ssize_t serio_raw_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
{
- struct serio_raw_list *list = file->private_data;
- struct serio_raw *serio_raw = list->serio_raw;
+ struct serio_raw_client *client = file->private_data;
+ struct serio_raw *serio_raw = client->serio_raw;
char uninitialized_var(c);
ssize_t retval = 0;
- if (!serio_raw->serio)
+ if (serio_raw->dead)
return -ENODEV;
if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
- retval = wait_event_interruptible(list->serio_raw->wait,
- serio_raw->head != serio_raw->tail || !serio_raw->serio);
+ retval = wait_event_interruptible(serio_raw->wait,
+ serio_raw->head != serio_raw->tail || serio_raw->dead);
if (retval)
return retval;
- if (!serio_raw->serio)
+ if (serio_raw->dead)
return -ENODEV;
while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) {
@@ -186,9 +189,11 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer, size_t cou
return retval;
}
-static ssize_t serio_raw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
{
- struct serio_raw_list *list = file->private_data;
+ struct serio_raw_client *client = file->private_data;
+ struct serio_raw *serio_raw = client->serio_raw;
ssize_t written = 0;
int retval;
unsigned char c;
@@ -197,7 +202,7 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer, siz
if (retval)
return retval;
- if (!list->serio_raw->serio) {
+ if (serio_raw->dead) {
retval = -ENODEV;
goto out;
}
@@ -210,7 +215,7 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer, siz
retval = -EFAULT;
goto out;
}
- if (serio_write(list->serio_raw->serio, c)) {
+ if (serio_write(serio_raw->serio, c)) {
retval = -EIO;
goto out;
}
@@ -224,46 +229,49 @@ out:
static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
{
- struct serio_raw_list *list = file->private_data;
+ struct serio_raw_client *client = file->private_data;
+ struct serio_raw *serio_raw = client->serio_raw;
+ unsigned int mask;
- poll_wait(file, &list->serio_raw->wait, wait);
+ poll_wait(file, &serio_raw->wait, wait);
- if (list->serio_raw->head != list->serio_raw->tail)
+ mask = serio_raw->dead ? POLLHUP | POLLERR : POLLOUT | POLLWRNORM;
+ if (serio_raw->head != serio_raw->tail)
return POLLIN | POLLRDNORM;
return 0;
}
static const struct file_operations serio_raw_fops = {
- .owner = THIS_MODULE,
- .open = serio_raw_open,
- .release = serio_raw_release,
- .read = serio_raw_read,
- .write = serio_raw_write,
- .poll = serio_raw_poll,
- .fasync = serio_raw_fasync,
- .llseek = noop_llseek,
+ .owner = THIS_MODULE,
+ .open = serio_raw_open,
+ .release = serio_raw_release,
+ .read = serio_raw_read,
+ .write = serio_raw_write,
+ .poll = serio_raw_poll,
+ .fasync = serio_raw_fasync,
+ .llseek = noop_llseek,
};
/*********************************************************************
- * Interface with serio port *
+ * Interface with serio port *
*********************************************************************/
static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
unsigned int dfl)
{
struct serio_raw *serio_raw = serio_get_drvdata(serio);
- struct serio_raw_list *list;
+ struct serio_raw_client *client;
unsigned int head = serio_raw->head;
- /* we are holding serio->lock here so we are prootected */
+ /* we are holding serio->lock here so we are protected */
serio_raw->queue[head] = data;
head = (head + 1) % SERIO_RAW_QUEUE_LEN;
if (likely(head != serio_raw->tail)) {
serio_raw->head = head;
- list_for_each_entry(list, &serio_raw->list, node)
- kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ list_for_each_entry(client, &serio_raw->client_list, node)
+ kill_fasync(&client->fasync, SIGIO, POLL_IN);
wake_up_interruptible(&serio_raw->wait);
}
@@ -272,29 +280,37 @@ static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
{
+ static atomic_t serio_raw_no = ATOMIC_INIT(0);
struct serio_raw *serio_raw;
int err;
- if (!(serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL))) {
- printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n");
+ serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL);
+ if (!serio_raw) {
+ dev_dbg(&serio->dev, "can't allocate memory for a device\n");
return -ENOMEM;
}
- mutex_lock(&serio_raw_mutex);
+ snprintf(serio_raw->name, sizeof(serio_raw->name),
+ "serio_raw%ld", (long)atomic_inc_return(&serio_raw_no) - 1);
+ kref_init(&serio_raw->kref);
+ INIT_LIST_HEAD(&serio_raw->client_list);
+ init_waitqueue_head(&serio_raw->wait);
- snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++);
- serio_raw->refcnt = 1;
serio_raw->serio = serio;
- INIT_LIST_HEAD(&serio_raw->list);
- init_waitqueue_head(&serio_raw->wait);
+ get_device(&serio->dev);
serio_set_drvdata(serio, serio_raw);
err = serio_open(serio, drv);
if (err)
- goto out_free;
+ goto err_free;
+
+ err = mutex_lock_killable(&serio_raw_mutex);
+ if (err)
+ goto err_close;
list_add_tail(&serio_raw->node, &serio_raw_list);
+ mutex_unlock(&serio_raw_mutex);
serio_raw->dev.minor = PSMOUSE_MINOR;
serio_raw->dev.name = serio_raw->name;
@@ -308,23 +324,23 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
}
if (err) {
- printk(KERN_INFO "serio_raw: failed to register raw access device for %s\n",
+ dev_err(&serio->dev,
+ "failed to register raw access device for %s\n",
serio->phys);
- goto out_close;
+ goto err_unlink;
}
- printk(KERN_INFO "serio_raw: raw access enabled on %s (%s, minor %d)\n",
- serio->phys, serio_raw->name, serio_raw->dev.minor);
- goto out;
+ dev_info(&serio->dev, "raw access enabled on %s (%s, minor %d)\n",
+ serio->phys, serio_raw->name, serio_raw->dev.minor);
+ return 0;
-out_close:
- serio_close(serio);
+err_unlink:
list_del_init(&serio_raw->node);
-out_free:
+err_close:
+ serio_close(serio);
+err_free:
serio_set_drvdata(serio, NULL);
- kfree(serio_raw);
-out:
- mutex_unlock(&serio_raw_mutex);
+ kref_put(&serio_raw->kref, serio_raw_free);
return err;
}
@@ -334,7 +350,8 @@ static int serio_raw_reconnect(struct serio *serio)
struct serio_driver *drv = serio->drv;
if (!drv || !serio_raw) {
- printk(KERN_DEBUG "serio_raw: reconnect request, but serio is disconnected, ignoring...\n");
+ dev_dbg(&serio->dev,
+ "reconnect request, but serio is disconnected, ignoring...\n");
return -1;
}
@@ -345,22 +362,40 @@ static int serio_raw_reconnect(struct serio *serio)
return 0;
}
+/*
+ * Wake up users waiting for IO so they can disconnect from
+ * dead device.
+ */
+static void serio_raw_hangup(struct serio_raw *serio_raw)
+{
+ struct serio_raw_client *client;
+
+ serio_pause_rx(serio_raw->serio);
+ list_for_each_entry(client, &serio_raw->client_list, node)
+ kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+ serio_continue_rx(serio_raw->serio);
+
+ wake_up_interruptible(&serio_raw->wait);
+}
+
+
static void serio_raw_disconnect(struct serio *serio)
{
- struct serio_raw *serio_raw;
+ struct serio_raw *serio_raw = serio_get_drvdata(serio);
+
+ misc_deregister(&serio_raw->dev);
mutex_lock(&serio_raw_mutex);
+ serio_raw->dead = true;
+ list_del_init(&serio_raw->node);
+ mutex_unlock(&serio_raw_mutex);
- serio_raw = serio_get_drvdata(serio);
+ serio_raw_hangup(serio_raw);
serio_close(serio);
- serio_set_drvdata(serio, NULL);
-
- serio_raw->serio = NULL;
- if (!serio_raw_cleanup(serio_raw))
- wake_up_interruptible(&serio_raw->wait);
+ kref_put(&serio_raw->kref, serio_raw_free);
- mutex_unlock(&serio_raw_mutex);
+ serio_set_drvdata(serio, NULL);
}
static struct serio_device_id serio_raw_serio_ids[] = {
@@ -391,7 +426,7 @@ static struct serio_driver serio_raw_drv = {
.connect = serio_raw_connect,
.reconnect = serio_raw_reconnect,
.disconnect = serio_raw_disconnect,
- .manual_bind = 1,
+ .manual_bind = true,
};
static int __init serio_raw_init(void)
diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c
index 80baa53da5b1..d64c5a43aaad 100644
--- a/drivers/input/serio/xilinx_ps2.c
+++ b/drivers/input/serio/xilinx_ps2.c
@@ -23,7 +23,7 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/io.h>
-
+#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c
index fdb6a3976f94..75fb040a3435 100644
--- a/drivers/input/sparse-keymap.c
+++ b/drivers/input/sparse-keymap.c
@@ -15,6 +15,7 @@
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <linux/module.h>
#include <linux/slab.h>
MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c
index 0a619c558bfb..6d89fd1842c3 100644
--- a/drivers/input/tablet/aiptek.c
+++ b/drivers/input/tablet/aiptek.c
@@ -225,7 +225,6 @@
/* toolMode codes
*/
#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN
-#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN
#define AIPTEK_TOOL_BUTTON_PENCIL_MODE BTN_TOOL_PENCIL
#define AIPTEK_TOOL_BUTTON_BRUSH_MODE BTN_TOOL_BRUSH
#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE BTN_TOOL_AIRBRUSH
diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h
index 23317bd09c82..0783864a7dc2 100644
--- a/drivers/input/tablet/wacom.h
+++ b/drivers/input/tablet/wacom.h
@@ -11,7 +11,7 @@
* Copyright (c) 2000 Daniel Egger <egger@suse.de>
* Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
* Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
- * Copyright (c) 2002-2009 Ping Cheng <pingc@wacom.com>
+ * Copyright (c) 2002-2011 Ping Cheng <pingc@wacom.com>
*
* ChangeLog:
* v0.1 (vp) - Initial release
@@ -93,7 +93,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v1.52"
+#define DRIVER_VERSION "v1.53"
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB Wacom tablet driver"
#define DRIVER_LICENSE "GPL"
@@ -114,6 +114,12 @@ struct wacom {
struct mutex lock;
bool open;
char phys[32];
+ struct wacom_led {
+ u8 select[2]; /* status led selector (0..3) */
+ u8 llv; /* status led brightness no button (1..127) */
+ u8 hlv; /* status led brightness button pressed (1..127) */
+ u8 img_lum; /* OLED matrix display brightness */
+ } led;
};
extern const struct usb_device_id wacom_ids[];
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index 449c0a46dbac..1c1b7b43cf92 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -48,26 +48,49 @@ struct hid_descriptor {
/* defines to get/set USB message */
#define USB_REQ_GET_REPORT 0x01
#define USB_REQ_SET_REPORT 0x09
+
#define WAC_HID_FEATURE_REPORT 0x03
+#define WAC_MSG_RETRIES 5
+
+#define WAC_CMD_LED_CONTROL 0x20
+#define WAC_CMD_ICON_START 0x21
+#define WAC_CMD_ICON_XFER 0x23
+#define WAC_CMD_RETRIES 10
-static int usb_get_report(struct usb_interface *intf, unsigned char type,
- unsigned char id, void *buf, int size)
+static int wacom_get_report(struct usb_interface *intf, u8 type, u8 id,
+ void *buf, size_t size, unsigned int retries)
{
- return usb_control_msg(interface_to_usbdev(intf),
- usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
- USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
- buf, size, 100);
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int retval;
+
+ do {
+ retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id,
+ intf->altsetting[0].desc.bInterfaceNumber,
+ buf, size, 100);
+ } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+
+ return retval;
}
-static int usb_set_report(struct usb_interface *intf, unsigned char type,
- unsigned char id, void *buf, int size)
+static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id,
+ void *buf, size_t size, unsigned int retries)
{
- return usb_control_msg(interface_to_usbdev(intf),
- usb_sndctrlpipe(interface_to_usbdev(intf), 0),
- USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
- buf, size, 1000);
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int retval;
+
+ do {
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id,
+ intf->altsetting[0].desc.bInterfaceNumber,
+ buf, size, 1000);
+ } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+
+ return retval;
}
static void wacom_sys_irq(struct urb *urb)
@@ -165,7 +188,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
report,
hid_desc->wDescriptorLength,
5000); /* 5 secs */
- } while (result < 0 && limit++ < 5);
+ } while (result < 0 && limit++ < WAC_MSG_RETRIES);
/* No need to parse the Descriptor. It isn't an error though */
if (result < 0)
@@ -228,13 +251,6 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
get_unaligned_le16(&report[i + 3]);
i += 4;
}
- } else if (usage == WCM_DIGITIZER) {
- /* max pressure isn't reported
- features->pressure_max = (unsigned short)
- (report[i+4] << 8 | report[i + 3]);
- */
- features->pressure_max = 255;
- i += 4;
}
break;
@@ -290,13 +306,6 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
pen = 1;
i++;
break;
-
- case HID_USAGE_UNDEFINED:
- if (usage == WCM_DESKTOP && finger) /* capacity */
- features->pressure_max =
- get_unaligned_le16(&report[i + 3]);
- i += 4;
- break;
}
break;
@@ -319,35 +328,37 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
int limit = 0, report_id = 2;
int error = -ENOMEM;
- rep_data = kmalloc(2, GFP_KERNEL);
+ rep_data = kmalloc(4, GFP_KERNEL);
if (!rep_data)
return error;
- /* ask to report tablet data if it is 2FGT Tablet PC or
+ /* ask to report tablet data if it is MT Tablet PC or
* not a Tablet PC */
if (features->type == TABLETPC2FG) {
do {
rep_data[0] = 3;
rep_data[1] = 4;
+ rep_data[2] = 0;
+ rep_data[3] = 0;
report_id = 3;
- error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
- report_id, rep_data, 2);
+ error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
+ report_id, rep_data, 4, 1);
if (error >= 0)
- error = usb_get_report(intf,
- WAC_HID_FEATURE_REPORT, report_id,
- rep_data, 3);
- } while ((error < 0 || rep_data[1] != 4) && limit++ < 5);
+ error = wacom_get_report(intf,
+ WAC_HID_FEATURE_REPORT,
+ report_id, rep_data, 4, 1);
+ } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES);
} else if (features->type != TABLETPC) {
do {
rep_data[0] = 2;
rep_data[1] = 2;
- error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
- report_id, rep_data, 2);
+ error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
+ report_id, rep_data, 2, 1);
if (error >= 0)
- error = usb_get_report(intf,
- WAC_HID_FEATURE_REPORT, report_id,
- rep_data, 2);
- } while ((error < 0 || rep_data[1] != 2) && limit++ < 5);
+ error = wacom_get_report(intf,
+ WAC_HID_FEATURE_REPORT,
+ report_id, rep_data, 2, 1);
+ } while ((error < 0 || rep_data[1] != 2) && limit++ < WAC_MSG_RETRIES);
}
kfree(rep_data);
@@ -465,6 +476,275 @@ static void wacom_remove_shared_data(struct wacom_wac *wacom)
}
}
+static int wacom_led_control(struct wacom *wacom)
+{
+ unsigned char *buf;
+ int retval, led = 0;
+
+ buf = kzalloc(9, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (wacom->wacom_wac.features.type == WACOM_21UX2)
+ led = (wacom->led.select[1] << 4) | 0x40;
+
+ led |= wacom->led.select[0] | 0x4;
+
+ buf[0] = WAC_CMD_LED_CONTROL;
+ buf[1] = led;
+ buf[2] = wacom->led.llv;
+ buf[3] = wacom->led.hlv;
+ buf[4] = wacom->led.img_lum;
+
+ retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL,
+ buf, 9, WAC_CMD_RETRIES);
+ kfree(buf);
+
+ return retval;
+}
+
+static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img)
+{
+ unsigned char *buf;
+ int i, retval;
+
+ buf = kzalloc(259, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Send 'start' command */
+ buf[0] = WAC_CMD_ICON_START;
+ buf[1] = 1;
+ retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START,
+ buf, 2, WAC_CMD_RETRIES);
+ if (retval < 0)
+ goto out;
+
+ buf[0] = WAC_CMD_ICON_XFER;
+ buf[1] = button_id & 0x07;
+ for (i = 0; i < 4; i++) {
+ buf[2] = i;
+ memcpy(buf + 3, img + i * 256, 256);
+
+ retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_XFER,
+ buf, 259, WAC_CMD_RETRIES);
+ if (retval < 0)
+ break;
+ }
+
+ /* Send 'stop' */
+ buf[0] = WAC_CMD_ICON_START;
+ buf[1] = 0;
+ wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START,
+ buf, 2, WAC_CMD_RETRIES);
+
+out:
+ kfree(buf);
+ return retval;
+}
+
+static ssize_t wacom_led_select_store(struct device *dev, int set_id,
+ const char *buf, size_t count)
+{
+ struct wacom *wacom = dev_get_drvdata(dev);
+ unsigned int id;
+ int err;
+
+ err = kstrtouint(buf, 10, &id);
+ if (err)
+ return err;
+
+ mutex_lock(&wacom->lock);
+
+ wacom->led.select[set_id] = id & 0x3;
+ err = wacom_led_control(wacom);
+
+ mutex_unlock(&wacom->lock);
+
+ return err < 0 ? err : count;
+}
+
+#define DEVICE_LED_SELECT_ATTR(SET_ID) \
+static ssize_t wacom_led##SET_ID##_select_store(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ return wacom_led_select_store(dev, SET_ID, buf, count); \
+} \
+static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct wacom *wacom = dev_get_drvdata(dev); \
+ return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]); \
+} \
+static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR, \
+ wacom_led##SET_ID##_select_show, \
+ wacom_led##SET_ID##_select_store)
+
+DEVICE_LED_SELECT_ATTR(0);
+DEVICE_LED_SELECT_ATTR(1);
+
+static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest,
+ const char *buf, size_t count)
+{
+ unsigned int value;
+ int err;
+
+ err = kstrtouint(buf, 10, &value);
+ if (err)
+ return err;
+
+ mutex_lock(&wacom->lock);
+
+ *dest = value & 0x7f;
+ err = wacom_led_control(wacom);
+
+ mutex_unlock(&wacom->lock);
+
+ return err < 0 ? err : count;
+}
+
+#define DEVICE_LUMINANCE_ATTR(name, field) \
+static ssize_t wacom_##name##_luminance_store(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct wacom *wacom = dev_get_drvdata(dev); \
+ \
+ return wacom_luminance_store(wacom, &wacom->led.field, \
+ buf, count); \
+} \
+static DEVICE_ATTR(name##_luminance, S_IWUSR, \
+ NULL, wacom_##name##_luminance_store)
+
+DEVICE_LUMINANCE_ATTR(status0, llv);
+DEVICE_LUMINANCE_ATTR(status1, hlv);
+DEVICE_LUMINANCE_ATTR(buttons, img_lum);
+
+static ssize_t wacom_button_image_store(struct device *dev, int button_id,
+ const char *buf, size_t count)
+{
+ struct wacom *wacom = dev_get_drvdata(dev);
+ int err;
+
+ if (count != 1024)
+ return -EINVAL;
+
+ mutex_lock(&wacom->lock);
+
+ err = wacom_led_putimage(wacom, button_id, buf);
+
+ mutex_unlock(&wacom->lock);
+
+ return err < 0 ? err : count;
+}
+
+#define DEVICE_BTNIMG_ATTR(BUTTON_ID) \
+static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ return wacom_button_image_store(dev, BUTTON_ID, buf, count); \
+} \
+static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR, \
+ NULL, wacom_btnimg##BUTTON_ID##_store)
+
+DEVICE_BTNIMG_ATTR(0);
+DEVICE_BTNIMG_ATTR(1);
+DEVICE_BTNIMG_ATTR(2);
+DEVICE_BTNIMG_ATTR(3);
+DEVICE_BTNIMG_ATTR(4);
+DEVICE_BTNIMG_ATTR(5);
+DEVICE_BTNIMG_ATTR(6);
+DEVICE_BTNIMG_ATTR(7);
+
+static struct attribute *cintiq_led_attrs[] = {
+ &dev_attr_status_led0_select.attr,
+ &dev_attr_status_led1_select.attr,
+ NULL
+};
+
+static struct attribute_group cintiq_led_attr_group = {
+ .name = "wacom_led",
+ .attrs = cintiq_led_attrs,
+};
+
+static struct attribute *intuos4_led_attrs[] = {
+ &dev_attr_status0_luminance.attr,
+ &dev_attr_status1_luminance.attr,
+ &dev_attr_status_led0_select.attr,
+ &dev_attr_buttons_luminance.attr,
+ &dev_attr_button0_rawimg.attr,
+ &dev_attr_button1_rawimg.attr,
+ &dev_attr_button2_rawimg.attr,
+ &dev_attr_button3_rawimg.attr,
+ &dev_attr_button4_rawimg.attr,
+ &dev_attr_button5_rawimg.attr,
+ &dev_attr_button6_rawimg.attr,
+ &dev_attr_button7_rawimg.attr,
+ NULL
+};
+
+static struct attribute_group intuos4_led_attr_group = {
+ .name = "wacom_led",
+ .attrs = intuos4_led_attrs,
+};
+
+static int wacom_initialize_leds(struct wacom *wacom)
+{
+ int error;
+
+ /* Initialize default values */
+ switch (wacom->wacom_wac.features.type) {
+ case INTUOS4:
+ case INTUOS4L:
+ wacom->led.select[0] = 0;
+ wacom->led.select[1] = 0;
+ wacom->led.llv = 10;
+ wacom->led.hlv = 20;
+ wacom->led.img_lum = 10;
+ error = sysfs_create_group(&wacom->intf->dev.kobj,
+ &intuos4_led_attr_group);
+ break;
+
+ case WACOM_21UX2:
+ wacom->led.select[0] = 0;
+ wacom->led.select[1] = 0;
+ wacom->led.llv = 0;
+ wacom->led.hlv = 0;
+ wacom->led.img_lum = 0;
+
+ error = sysfs_create_group(&wacom->intf->dev.kobj,
+ &cintiq_led_attr_group);
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (error) {
+ dev_err(&wacom->intf->dev,
+ "cannot create sysfs group err: %d\n", error);
+ return error;
+ }
+ wacom_led_control(wacom);
+
+ return 0;
+}
+
+static void wacom_destroy_leds(struct wacom *wacom)
+{
+ switch (wacom->wacom_wac.features.type) {
+ case INTUOS4:
+ case INTUOS4L:
+ sysfs_remove_group(&wacom->intf->dev.kobj,
+ &intuos4_led_attr_group);
+ break;
+
+ case WACOM_21UX2:
+ sysfs_remove_group(&wacom->intf->dev.kobj,
+ &cintiq_led_attr_group);
+ break;
+ }
+}
+
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
@@ -553,16 +833,21 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
wacom->irq->transfer_dma = wacom->data_dma;
wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- error = input_register_device(input_dev);
+ error = wacom_initialize_leds(wacom);
if (error)
goto fail4;
+ error = input_register_device(input_dev);
+ if (error)
+ goto fail5;
+
/* Note that if query fails it is not a hard failure */
wacom_query_tablet_data(intf, features);
usb_set_intfdata(intf, wacom);
return 0;
+ fail5: wacom_destroy_leds(wacom);
fail4: wacom_remove_shared_data(wacom_wac);
fail3: usb_free_urb(wacom->irq);
fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
@@ -579,6 +864,7 @@ static void wacom_disconnect(struct usb_interface *intf)
usb_kill_urb(wacom->irq);
input_unregister_device(wacom->wacom_wac.input);
+ wacom_destroy_leds(wacom);
usb_free_urb(wacom->irq);
usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
wacom->wacom_wac.data, wacom->data_dma);
@@ -601,17 +887,16 @@ static int wacom_resume(struct usb_interface *intf)
{
struct wacom *wacom = usb_get_intfdata(intf);
struct wacom_features *features = &wacom->wacom_wac.features;
- int rv;
+ int rv = 0;
mutex_lock(&wacom->lock);
/* switch to wacom mode first */
wacom_query_tablet_data(intf, features);
+ wacom_led_control(wacom);
- if (wacom->open)
- rv = usb_submit_urb(wacom->irq, GFP_NOIO);
- else
- rv = 0;
+ if (wacom->open && usb_submit_urb(wacom->irq, GFP_NOIO) < 0)
+ rv = -EIO;
mutex_unlock(&wacom->lock);
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 08ba5ad9c9be..da0d8761e778 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -15,6 +15,7 @@
#include "wacom_wac.h"
#include "wacom.h"
#include <linux/input/mt.h>
+#include <linux/hid.h>
/* resolution for penabled devices */
#define WACOM_PL_RES 20
@@ -264,6 +265,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
wacom->id[0] = 0;
input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
input_report_key(input, wacom->tool[0], prox);
+ input_event(input, EV_MSC, MSC_SERIAL, 1);
input_sync(input); /* sync last event */
}
@@ -273,11 +275,10 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
prox = data[7] & 0xf8;
if (prox || wacom->id[1]) {
wacom->id[1] = PAD_DEVICE_ID;
- input_report_key(input, BTN_0, (data[7] & 0x40));
- input_report_key(input, BTN_4, (data[7] & 0x80));
+ input_report_key(input, BTN_BACK, (data[7] & 0x40));
+ input_report_key(input, BTN_FORWARD, (data[7] & 0x80));
rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
input_report_rel(input, REL_WHEEL, rw);
- input_report_key(input, BTN_TOOL_FINGER, 0xf0);
if (!prox)
wacom->id[1] = 0;
input_report_abs(input, ABS_MISC, wacom->id[1]);
@@ -290,18 +291,17 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
prox = (data[7] & 0xf8) || data[8];
if (prox || wacom->id[1]) {
wacom->id[1] = PAD_DEVICE_ID;
- input_report_key(input, BTN_0, (data[7] & 0x08));
- input_report_key(input, BTN_1, (data[7] & 0x20));
- input_report_key(input, BTN_4, (data[7] & 0x10));
- input_report_key(input, BTN_5, (data[7] & 0x40));
+ input_report_key(input, BTN_BACK, (data[7] & 0x08));
+ input_report_key(input, BTN_LEFT, (data[7] & 0x20));
+ input_report_key(input, BTN_FORWARD, (data[7] & 0x10));
+ input_report_key(input, BTN_RIGHT, (data[7] & 0x40));
input_report_abs(input, ABS_WHEEL, (data[8] & 0x7f));
- input_report_key(input, BTN_TOOL_FINGER, 0xf0);
if (!prox)
wacom->id[1] = 0;
input_report_abs(input, ABS_MISC, wacom->id[1]);
input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+ retval = 1;
}
- retval = 1;
break;
}
exit:
@@ -494,10 +494,6 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
/* pad packets. Works as a second tool and is always in prox */
if (data[0] == WACOM_REPORT_INTUOSPAD) {
- /* initiate the pad as a device */
- if (wacom->tool[1] != BTN_TOOL_FINGER)
- wacom->tool[1] = BTN_TOOL_FINGER;
-
if (features->type >= INTUOS4S && features->type <= INTUOS4L) {
input_report_key(input, BTN_0, (data[2] & 0x01));
input_report_key(input, BTN_1, (data[3] & 0x01));
@@ -804,25 +800,26 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
int i;
for (i = 0; i < 2; i++) {
- int p = data[9 * i + 2];
- bool touch = p && !wacom->shared->stylus_in_proximity;
+ int offset = (data[1] & 0x80) ? (8 * i) : (9 * i);
+ bool touch = data[offset + 3] & 0x80;
- input_mt_slot(input, i);
- input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
/*
* Touch events need to be disabled while stylus is
* in proximity because user's hand is resting on touchpad
* and sending unwanted events. User expects tablet buttons
* to continue working though.
*/
+ touch = touch && !wacom->shared->stylus_in_proximity;
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
if (touch) {
- int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff;
- int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff;
+ int x = get_unaligned_be16(&data[offset + 3]) & 0x7ff;
+ int y = get_unaligned_be16(&data[offset + 5]) & 0x7ff;
if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) {
x <<= 5;
y <<= 5;
}
- input_report_abs(input, ABS_MT_PRESSURE, p);
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
}
@@ -877,7 +874,15 @@ static int wacom_bpt_pen(struct wacom_wac *wacom)
x = le16_to_cpup((__le16 *)&data[2]);
y = le16_to_cpup((__le16 *)&data[4]);
p = le16_to_cpup((__le16 *)&data[6]);
- d = data[8];
+ /*
+ * Convert distance from out prox to distance from tablet.
+ * distance will be greater than distance_max once
+ * touching and applying pressure; do not report negative
+ * distance.
+ */
+ if (data[8] <= wacom->features.distance_max)
+ d = wacom->features.distance_max - data[8];
+
pen = data[1] & 0x01;
btn1 = data[1] & 0x02;
btn2 = data[1] & 0x04;
@@ -1033,8 +1038,6 @@ void wacom_setup_device_quirks(struct wacom_features *features)
features->y_max <<= 5;
features->x_fuzz <<= 5;
features->y_fuzz <<= 5;
- features->pressure_max = 256;
- features->pressure_fuzz = 16;
features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
}
}
@@ -1060,10 +1063,11 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
features->x_fuzz, 0);
input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
features->y_fuzz, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
- features->pressure_fuzz, 0);
if (features->device_type == BTN_TOOL_PEN) {
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
+ features->pressure_fuzz, 0);
+
/* penabled devices have fixed resolution for each model */
input_abs_set_res(input_dev, ABS_X, features->x_resolution);
input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
@@ -1080,18 +1084,14 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
switch (wacom_wac->features.type) {
case WACOM_MO:
- __set_bit(BTN_1, input_dev->keybit);
- __set_bit(BTN_5, input_dev->keybit);
-
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
/* fall through */
case WACOM_G4:
input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
- __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
- __set_bit(BTN_0, input_dev->keybit);
- __set_bit(BTN_4, input_dev->keybit);
+ __set_bit(BTN_BACK, input_dev->keybit);
+ __set_bit(BTN_FORWARD, input_dev->keybit);
/* fall through */
case GRAPHIRE:
@@ -1106,6 +1106,8 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
__set_bit(BTN_STYLUS, input_dev->keybit);
__set_bit(BTN_STYLUS2, input_dev->keybit);
+
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
break;
case WACOM_21UX2:
@@ -1127,11 +1129,13 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
case CINTIQ:
for (i = 0; i < 8; i++)
__set_bit(BTN_0 + i, input_dev->keybit);
- __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
wacom_setup_cintiq(wacom_wac);
break;
@@ -1151,13 +1155,13 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_2, input_dev->keybit);
__set_bit(BTN_3, input_dev->keybit);
- __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
-
input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
/* fall through */
case INTUOS:
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
wacom_setup_intuos(wacom_wac);
break;
@@ -1170,10 +1174,11 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
case INTUOS4S:
for (i = 0; i < 7; i++)
__set_bit(BTN_0 + i, input_dev->keybit);
- __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
wacom_setup_intuos(wacom_wac);
+
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
break;
case TABLETPC2FG:
@@ -1192,26 +1197,40 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
case TABLETPC:
__clear_bit(ABS_MISC, input_dev->absbit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
if (features->device_type != BTN_TOOL_PEN)
break; /* no need to process stylus stuff */
/* fall through */
case PL:
- case PTU:
case DTU:
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
+ __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
__set_bit(BTN_STYLUS, input_dev->keybit);
__set_bit(BTN_STYLUS2, input_dev->keybit);
+
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+ break;
+
+ case PTU:
+ __set_bit(BTN_STYLUS2, input_dev->keybit);
/* fall through */
case PENPARTNER:
+ __set_bit(BTN_TOOL_PEN, input_dev->keybit);
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+ __set_bit(BTN_STYLUS, input_dev->keybit);
+
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
break;
case BAMBOO_PT:
__clear_bit(ABS_MISC, input_dev->absbit);
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
if (features->device_type == BTN_TOOL_DOUBLETAP) {
__set_bit(BTN_LEFT, input_dev->keybit);
__set_bit(BTN_FORWARD, input_dev->keybit);
@@ -1228,14 +1247,14 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, features->y_max,
features->y_fuzz, 0);
- input_set_abs_params(input_dev, ABS_MT_PRESSURE,
- 0, features->pressure_max,
- features->pressure_fuzz, 0);
} else if (features->device_type == BTN_TOOL_PEN) {
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
__set_bit(BTN_STYLUS, input_dev->keybit);
__set_bit(BTN_STYLUS2, input_dev->keybit);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+ features->distance_max,
+ 0, 0);
}
break;
}
@@ -1295,6 +1314,12 @@ static const struct wacom_features wacom_features_0x65 =
static const struct wacom_features wacom_features_0x69 =
{ "Wacom Bamboo1", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511,
63, GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES };
+static const struct wacom_features wacom_features_0x6A =
+ { "Wacom Bamboo1 4x6", WACOM_PKGLEN_GRAPHIRE, 14760, 9225, 1023,
+ 63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x6B =
+ { "Wacom Bamboo1 5x8", WACOM_PKGLEN_GRAPHIRE, 21648, 13530, 1023,
+ 63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x20 =
{ "Wacom Intuos 4x5", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023,
31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -1427,6 +1452,9 @@ static const struct wacom_features wacom_features_0x90 =
static const struct wacom_features wacom_features_0x93 =
{ "Wacom ISDv4 93", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255,
0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x97 =
+ { "Wacom ISDv4 97", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 511,
+ 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x9A =
{ "Wacom ISDv4 9A", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255,
0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -1447,34 +1475,37 @@ static const struct wacom_features wacom_features_0x47 =
31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xD0 =
{ "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
- 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xD1 =
{ "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
- 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xD2 =
{ "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
- 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xD3 =
- { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023,
- 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023,
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xD4 =
- { "Wacom Bamboo Pen", WACOM_PKGLEN_BBFUN, 14720, 9200, 255,
- 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ { "Wacom Bamboo Pen", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xD5 =
+ { "Wacom Bamboo Pen 6x8", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023,
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xD6 =
{ "Wacom BambooPT 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
- 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xD7 =
{ "Wacom BambooPT 2FG Small", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
- 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xD8 =
- { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023,
- 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023,
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xDA =
{ "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
- 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static struct wacom_features wacom_features_0xDB =
- { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023,
- 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023,
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x6004 =
{ "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255,
0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -1483,6 +1514,11 @@ static const struct wacom_features wacom_features_0x6004 =
USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \
.driver_info = (kernel_ulong_t)&wacom_features_##prod
+#define USB_DEVICE_DETAILED(prod, class, sub, proto) \
+ USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_WACOM, prod, class, \
+ sub, proto), \
+ .driver_info = (kernel_ulong_t)&wacom_features_##prod
+
#define USB_DEVICE_LENOVO(prod) \
USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \
.driver_info = (kernel_ulong_t)&wacom_features_##prod
@@ -1506,6 +1542,8 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x64) },
{ USB_DEVICE_WACOM(0x65) },
{ USB_DEVICE_WACOM(0x69) },
+ { USB_DEVICE_WACOM(0x6A) },
+ { USB_DEVICE_WACOM(0x6B) },
{ USB_DEVICE_WACOM(0x20) },
{ USB_DEVICE_WACOM(0x21) },
{ USB_DEVICE_WACOM(0x22) },
@@ -1545,12 +1583,19 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0xC5) },
{ USB_DEVICE_WACOM(0xC6) },
{ USB_DEVICE_WACOM(0xC7) },
- { USB_DEVICE_WACOM(0xCE) },
+ /*
+ * DTU-2231 has two interfaces on the same configuration,
+ * only one is used.
+ */
+ { USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID,
+ USB_INTERFACE_SUBCLASS_BOOT,
+ USB_INTERFACE_PROTOCOL_MOUSE) },
{ USB_DEVICE_WACOM(0xD0) },
{ USB_DEVICE_WACOM(0xD1) },
{ USB_DEVICE_WACOM(0xD2) },
{ USB_DEVICE_WACOM(0xD3) },
{ USB_DEVICE_WACOM(0xD4) },
+ { USB_DEVICE_WACOM(0xD5) },
{ USB_DEVICE_WACOM(0xD6) },
{ USB_DEVICE_WACOM(0xD7) },
{ USB_DEVICE_WACOM(0xD8) },
@@ -1560,6 +1605,7 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0xCC) },
{ USB_DEVICE_WACOM(0x90) },
{ USB_DEVICE_WACOM(0x93) },
+ { USB_DEVICE_WACOM(0x97) },
{ USB_DEVICE_WACOM(0x9A) },
{ USB_DEVICE_WACOM(0x9F) },
{ USB_DEVICE_WACOM(0xE2) },
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index cabd9e54863f..3488ffe1fa0a 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -651,6 +651,18 @@ config TOUCHSCREEN_TOUCHIT213
To compile this driver as a module, choose M here: the
module will be called touchit213.
+config TOUCHSCREEN_TSC_SERIO
+ tristate "TSC-10/25/40 serial touchscreen support"
+ select SERIO
+ help
+ Say Y here if you have a TSC-10, 25 or 40 serial touchscreen connected
+ to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsc40.
+
config TOUCHSCREEN_TSC2005
tristate "TSC2005 based touchscreens"
depends on SPI_MASTER && GENERIC_HARDIRQS
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 282d6f76ae26..f957676035a4 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o
obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c
index 714d4e0f9f95..400131df677b 100644
--- a/drivers/input/touchscreen/ad7877.c
+++ b/drivers/input/touchscreen/ad7877.c
@@ -45,6 +45,7 @@
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/ad7877.h>
+#include <linux/module.h>
#include <asm/irq.h>
#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(100)
diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c
index 4e4e58cec6c8..c789b974c795 100644
--- a/drivers/input/touchscreen/ad7879-i2c.c
+++ b/drivers/input/touchscreen/ad7879-i2c.c
@@ -16,7 +16,7 @@
#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int ad7879_i2c_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -36,9 +36,9 @@ static int ad7879_i2c_resume(struct device *dev)
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(ad7879_i2c_pm, ad7879_i2c_suspend, ad7879_i2c_resume);
-#endif
/* All registers are word-sized.
* AD7879 uses a high-byte first convention.
@@ -119,9 +119,7 @@ static struct i2c_driver ad7879_i2c_driver = {
.driver = {
.name = "ad7879",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
.pm = &ad7879_i2c_pm,
-#endif
},
.probe = ad7879_i2c_probe,
.remove = __devexit_p(ad7879_i2c_remove),
diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c
index ddf732f3cafc..b1643c8fa7c9 100644
--- a/drivers/input/touchscreen/ad7879-spi.c
+++ b/drivers/input/touchscreen/ad7879-spi.c
@@ -9,6 +9,7 @@
#include <linux/input.h> /* BUS_SPI */
#include <linux/pm.h>
#include <linux/spi/spi.h>
+#include <linux/module.h>
#include "ad7879.h"
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
index bc3b5187f3a3..3b2e9ed2aeec 100644
--- a/drivers/input/touchscreen/ad7879.c
+++ b/drivers/input/touchscreen/ad7879.c
@@ -33,6 +33,7 @@
#include <linux/gpio.h>
#include <linux/spi/ad7879.h>
+#include <linux/module.h>
#include "ad7879.h"
#define AD7879_REG_ZEROS 0
@@ -249,12 +250,14 @@ static void __ad7879_enable(struct ad7879 *ts)
static void __ad7879_disable(struct ad7879 *ts)
{
+ u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) |
+ AD7879_PM(AD7879_PM_SHUTDOWN);
disable_irq(ts->irq);
if (del_timer_sync(&ts->timer))
ad7879_ts_event_release(ts);
- ad7879_write(ts, AD7879_REG_CTRL2, AD7879_PM(AD7879_PM_SHUTDOWN));
+ ad7879_write(ts, AD7879_REG_CTRL2, reg);
}
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 5196861b86ef..de31ec6fe9e4 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -31,6 +31,7 @@
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
#include <linux/regulator/consumer.h>
+#include <linux/module.h>
#include <asm/irq.h>
/*
@@ -967,17 +968,12 @@ static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads784
ts->get_pendown_state = pdata->get_pendown_state;
} else if (gpio_is_valid(pdata->gpio_pendown)) {
- err = gpio_request(pdata->gpio_pendown, "ads7846_pendown");
+ err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN,
+ "ads7846_pendown");
if (err) {
- dev_err(&spi->dev, "failed to request pendown GPIO%d\n",
- pdata->gpio_pendown);
- return err;
- }
- err = gpio_direction_input(pdata->gpio_pendown);
- if (err) {
- dev_err(&spi->dev, "failed to setup pendown GPIO%d\n",
- pdata->gpio_pendown);
- gpio_free(pdata->gpio_pendown);
+ dev_err(&spi->dev,
+ "failed to request/setup pendown GPIO%d: %d\n",
+ pdata->gpio_pendown, err);
return err;
}
diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c
index fa8e56bd9094..8034cbb20f74 100644
--- a/drivers/input/touchscreen/atmel-wm97xx.c
+++ b/drivers/input/touchscreen/atmel-wm97xx.c
@@ -164,7 +164,7 @@ static irqreturn_t atmel_wm97xx_channel_b_interrupt(int irq, void *dev_id)
data = ac97c_readl(atmel_wm97xx, CBRHR);
value = data & 0x0fff;
- source = data & WM97XX_ADCSRC_MASK;
+ source = data & WM97XX_ADCSEL_MASK;
pen_down = (data & WM97XX_PEN_DOWN) >> 8;
if (source == WM97XX_ADCSEL_X)
@@ -442,6 +442,6 @@ static void __exit atmel_wm97xx_exit(void)
}
module_exit(atmel_wm97xx_exit);
-MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 1e61387c73ca..a596c2775d1a 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -48,41 +48,47 @@
#define MXT_OBJECT_SIZE 6
/* Object types */
-#define MXT_DEBUG_DIAGNOSTIC 37
-#define MXT_GEN_MESSAGE 5
-#define MXT_GEN_COMMAND 6
-#define MXT_GEN_POWER 7
-#define MXT_GEN_ACQUIRE 8
-#define MXT_TOUCH_MULTI 9
-#define MXT_TOUCH_KEYARRAY 15
-#define MXT_TOUCH_PROXIMITY 23
-#define MXT_PROCI_GRIPFACE 20
-#define MXT_PROCG_NOISE 22
-#define MXT_PROCI_ONETOUCH 24
-#define MXT_PROCI_TWOTOUCH 27
-#define MXT_PROCI_GRIP 40
-#define MXT_PROCI_PALM 41
-#define MXT_SPT_COMMSCONFIG 18
-#define MXT_SPT_GPIOPWM 19
-#define MXT_SPT_SELFTEST 25
-#define MXT_SPT_CTECONFIG 28
-#define MXT_SPT_USERDATA 38
-#define MXT_SPT_DIGITIZER 43
-#define MXT_SPT_MESSAGECOUNT 44
-
-/* MXT_GEN_COMMAND field */
+#define MXT_DEBUG_DIAGNOSTIC_T37 37
+#define MXT_GEN_MESSAGE_T5 5
+#define MXT_GEN_COMMAND_T6 6
+#define MXT_GEN_POWER_T7 7
+#define MXT_GEN_ACQUIRE_T8 8
+#define MXT_GEN_DATASOURCE_T53 53
+#define MXT_TOUCH_MULTI_T9 9
+#define MXT_TOUCH_KEYARRAY_T15 15
+#define MXT_TOUCH_PROXIMITY_T23 23
+#define MXT_TOUCH_PROXKEY_T52 52
+#define MXT_PROCI_GRIPFACE_T20 20
+#define MXT_PROCG_NOISE_T22 22
+#define MXT_PROCI_ONETOUCH_T24 24
+#define MXT_PROCI_TWOTOUCH_T27 27
+#define MXT_PROCI_GRIP_T40 40
+#define MXT_PROCI_PALM_T41 41
+#define MXT_PROCI_TOUCHSUPPRESSION_T42 42
+#define MXT_PROCI_STYLUS_T47 47
+#define MXT_PROCG_NOISESUPPRESSION_T48 48
+#define MXT_SPT_COMMSCONFIG_T18 18
+#define MXT_SPT_GPIOPWM_T19 19
+#define MXT_SPT_SELFTEST_T25 25
+#define MXT_SPT_CTECONFIG_T28 28
+#define MXT_SPT_USERDATA_T38 38
+#define MXT_SPT_DIGITIZER_T43 43
+#define MXT_SPT_MESSAGECOUNT_T44 44
+#define MXT_SPT_CTECONFIG_T46 46
+
+/* MXT_GEN_COMMAND_T6 field */
#define MXT_COMMAND_RESET 0
#define MXT_COMMAND_BACKUPNV 1
#define MXT_COMMAND_CALIBRATE 2
#define MXT_COMMAND_REPORTALL 3
#define MXT_COMMAND_DIAGNOSTIC 5
-/* MXT_GEN_POWER field */
+/* MXT_GEN_POWER_T7 field */
#define MXT_POWER_IDLEACQINT 0
#define MXT_POWER_ACTVACQINT 1
#define MXT_POWER_ACTV2IDLETO 2
-/* MXT_GEN_ACQUIRE field */
+/* MXT_GEN_ACQUIRE_T8 field */
#define MXT_ACQUIRE_CHRGTIME 0
#define MXT_ACQUIRE_TCHDRIFT 2
#define MXT_ACQUIRE_DRIFTST 3
@@ -91,7 +97,7 @@
#define MXT_ACQUIRE_ATCHCALST 6
#define MXT_ACQUIRE_ATCHCALSTHR 7
-/* MXT_TOUCH_MULTI field */
+/* MXT_TOUCH_MULTI_T9 field */
#define MXT_TOUCH_CTRL 0
#define MXT_TOUCH_XORIGIN 1
#define MXT_TOUCH_YORIGIN 2
@@ -121,7 +127,7 @@
#define MXT_TOUCH_YEDGEDIST 29
#define MXT_TOUCH_JUMPLIMIT 30
-/* MXT_PROCI_GRIPFACE field */
+/* MXT_PROCI_GRIPFACE_T20 field */
#define MXT_GRIPFACE_CTRL 0
#define MXT_GRIPFACE_XLOGRIP 1
#define MXT_GRIPFACE_XHIGRIP 2
@@ -151,11 +157,11 @@
#define MXT_NOISE_FREQ4 15
#define MXT_NOISE_IDLEGCAFVALID 16
-/* MXT_SPT_COMMSCONFIG */
+/* MXT_SPT_COMMSCONFIG_T18 */
#define MXT_COMMS_CTRL 0
#define MXT_COMMS_CMD 1
-/* MXT_SPT_CTECONFIG field */
+/* MXT_SPT_CTECONFIG_T28 field */
#define MXT_CTE_CTRL 0
#define MXT_CTE_CMD 1
#define MXT_CTE_MODE 2
@@ -166,7 +172,7 @@
#define MXT_VOLTAGE_DEFAULT 2700000
#define MXT_VOLTAGE_STEP 10000
-/* Define for MXT_GEN_COMMAND */
+/* Define for MXT_GEN_COMMAND_T6 */
#define MXT_BOOT_VALUE 0xa5
#define MXT_BACKUP_VALUE 0x55
#define MXT_BACKUP_TIME 25 /* msec */
@@ -238,6 +244,7 @@ struct mxt_finger {
int x;
int y;
int area;
+ int pressure;
};
/* Each client has this additional data */
@@ -256,24 +263,31 @@ struct mxt_data {
static bool mxt_object_readable(unsigned int type)
{
switch (type) {
- case MXT_GEN_MESSAGE:
- case MXT_GEN_COMMAND:
- case MXT_GEN_POWER:
- case MXT_GEN_ACQUIRE:
- case MXT_TOUCH_MULTI:
- case MXT_TOUCH_KEYARRAY:
- case MXT_TOUCH_PROXIMITY:
- case MXT_PROCI_GRIPFACE:
- case MXT_PROCG_NOISE:
- case MXT_PROCI_ONETOUCH:
- case MXT_PROCI_TWOTOUCH:
- case MXT_PROCI_GRIP:
- case MXT_PROCI_PALM:
- case MXT_SPT_COMMSCONFIG:
- case MXT_SPT_GPIOPWM:
- case MXT_SPT_SELFTEST:
- case MXT_SPT_CTECONFIG:
- case MXT_SPT_USERDATA:
+ case MXT_GEN_MESSAGE_T5:
+ case MXT_GEN_COMMAND_T6:
+ case MXT_GEN_POWER_T7:
+ case MXT_GEN_ACQUIRE_T8:
+ case MXT_GEN_DATASOURCE_T53:
+ case MXT_TOUCH_MULTI_T9:
+ case MXT_TOUCH_KEYARRAY_T15:
+ case MXT_TOUCH_PROXIMITY_T23:
+ case MXT_TOUCH_PROXKEY_T52:
+ case MXT_PROCI_GRIPFACE_T20:
+ case MXT_PROCG_NOISE_T22:
+ case MXT_PROCI_ONETOUCH_T24:
+ case MXT_PROCI_TWOTOUCH_T27:
+ case MXT_PROCI_GRIP_T40:
+ case MXT_PROCI_PALM_T41:
+ case MXT_PROCI_TOUCHSUPPRESSION_T42:
+ case MXT_PROCI_STYLUS_T47:
+ case MXT_PROCG_NOISESUPPRESSION_T48:
+ case MXT_SPT_COMMSCONFIG_T18:
+ case MXT_SPT_GPIOPWM_T19:
+ case MXT_SPT_SELFTEST_T25:
+ case MXT_SPT_CTECONFIG_T28:
+ case MXT_SPT_USERDATA_T38:
+ case MXT_SPT_DIGITIZER_T43:
+ case MXT_SPT_CTECONFIG_T46:
return true;
default:
return false;
@@ -283,21 +297,28 @@ static bool mxt_object_readable(unsigned int type)
static bool mxt_object_writable(unsigned int type)
{
switch (type) {
- case MXT_GEN_COMMAND:
- case MXT_GEN_POWER:
- case MXT_GEN_ACQUIRE:
- case MXT_TOUCH_MULTI:
- case MXT_TOUCH_KEYARRAY:
- case MXT_TOUCH_PROXIMITY:
- case MXT_PROCI_GRIPFACE:
- case MXT_PROCG_NOISE:
- case MXT_PROCI_ONETOUCH:
- case MXT_PROCI_TWOTOUCH:
- case MXT_PROCI_GRIP:
- case MXT_PROCI_PALM:
- case MXT_SPT_GPIOPWM:
- case MXT_SPT_SELFTEST:
- case MXT_SPT_CTECONFIG:
+ case MXT_GEN_COMMAND_T6:
+ case MXT_GEN_POWER_T7:
+ case MXT_GEN_ACQUIRE_T8:
+ case MXT_TOUCH_MULTI_T9:
+ case MXT_TOUCH_KEYARRAY_T15:
+ case MXT_TOUCH_PROXIMITY_T23:
+ case MXT_TOUCH_PROXKEY_T52:
+ case MXT_PROCI_GRIPFACE_T20:
+ case MXT_PROCG_NOISE_T22:
+ case MXT_PROCI_ONETOUCH_T24:
+ case MXT_PROCI_TWOTOUCH_T27:
+ case MXT_PROCI_GRIP_T40:
+ case MXT_PROCI_PALM_T41:
+ case MXT_PROCI_TOUCHSUPPRESSION_T42:
+ case MXT_PROCI_STYLUS_T47:
+ case MXT_PROCG_NOISESUPPRESSION_T48:
+ case MXT_SPT_COMMSCONFIG_T18:
+ case MXT_SPT_GPIOPWM_T19:
+ case MXT_SPT_SELFTEST_T25:
+ case MXT_SPT_CTECONFIG_T28:
+ case MXT_SPT_DIGITIZER_T43:
+ case MXT_SPT_CTECONFIG_T46:
return true;
default:
return false;
@@ -455,7 +476,7 @@ static int mxt_read_message(struct mxt_data *data,
struct mxt_object *object;
u16 reg;
- object = mxt_get_object(data, MXT_GEN_MESSAGE);
+ object = mxt_get_object(data, MXT_GEN_MESSAGE_T5);
if (!object)
return -EINVAL;
@@ -516,6 +537,8 @@ static void mxt_input_report(struct mxt_data *data, int single_id)
finger[id].x);
input_report_abs(input_dev, ABS_MT_POSITION_Y,
finger[id].y);
+ input_report_abs(input_dev, ABS_MT_PRESSURE,
+ finger[id].pressure);
} else {
finger[id].status = 0;
}
@@ -526,6 +549,8 @@ static void mxt_input_report(struct mxt_data *data, int single_id)
if (status != MXT_RELEASE) {
input_report_abs(input_dev, ABS_X, finger[single_id].x);
input_report_abs(input_dev, ABS_Y, finger[single_id].y);
+ input_report_abs(input_dev,
+ ABS_PRESSURE, finger[single_id].pressure);
}
input_sync(input_dev);
@@ -540,6 +565,7 @@ static void mxt_input_touchevent(struct mxt_data *data,
int x;
int y;
int area;
+ int pressure;
/* Check the touch is present on the screen */
if (!(status & MXT_DETECT)) {
@@ -564,6 +590,7 @@ static void mxt_input_touchevent(struct mxt_data *data,
y = y >> 2;
area = message->message[4];
+ pressure = message->message[5];
dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
status & MXT_MOVE ? "moved" : "pressed",
@@ -574,6 +601,7 @@ static void mxt_input_touchevent(struct mxt_data *data,
finger[id].x = x;
finger[id].y = y;
finger[id].area = area;
+ finger[id].pressure = pressure;
mxt_input_report(data, id);
}
@@ -597,8 +625,8 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
reportid = message.reportid;
- /* whether reportid is thing of MXT_TOUCH_MULTI */
- object = mxt_get_object(data, MXT_TOUCH_MULTI);
+ /* whether reportid is thing of MXT_TOUCH_MULTI_T9 */
+ object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
if (!object)
goto end;
@@ -635,7 +663,9 @@ static int mxt_check_reg_init(struct mxt_data *data)
if (!mxt_object_writable(object->type))
continue;
- for (j = 0; j < object->size + 1; j++) {
+ for (j = 0;
+ j < (object->size + 1) * (object->instances + 1);
+ j++) {
config_offset = index + j;
if (config_offset > pdata->config_length) {
dev_err(dev, "Not enough config data!\n");
@@ -644,7 +674,7 @@ static int mxt_check_reg_init(struct mxt_data *data)
mxt_write_object(data, object->type, j,
pdata->config[config_offset]);
}
- index += object->size + 1;
+ index += (object->size + 1) * (object->instances + 1);
}
return 0;
@@ -678,31 +708,31 @@ static void mxt_handle_pdata(struct mxt_data *data)
u8 voltage;
/* Set touchscreen lines */
- mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_XSIZE,
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE,
pdata->x_line);
- mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_YSIZE,
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE,
pdata->y_line);
/* Set touchscreen orient */
- mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_ORIENT,
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT,
pdata->orient);
/* Set touchscreen burst length */
- mxt_write_object(data, MXT_TOUCH_MULTI,
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
MXT_TOUCH_BLEN, pdata->blen);
/* Set touchscreen threshold */
- mxt_write_object(data, MXT_TOUCH_MULTI,
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
MXT_TOUCH_TCHTHR, pdata->threshold);
/* Set touchscreen resolution */
- mxt_write_object(data, MXT_TOUCH_MULTI,
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff);
- mxt_write_object(data, MXT_TOUCH_MULTI,
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8);
- mxt_write_object(data, MXT_TOUCH_MULTI,
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff);
- mxt_write_object(data, MXT_TOUCH_MULTI,
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8);
/* Set touchscreen voltage */
@@ -715,7 +745,7 @@ static void mxt_handle_pdata(struct mxt_data *data)
voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) /
MXT_VOLTAGE_STEP;
- mxt_write_object(data, MXT_SPT_CTECONFIG,
+ mxt_write_object(data, MXT_SPT_CTECONFIG_T28,
MXT_CTE_VOLTAGE, voltage);
}
}
@@ -819,13 +849,13 @@ static int mxt_initialize(struct mxt_data *data)
mxt_handle_pdata(data);
/* Backup to memory */
- mxt_write_object(data, MXT_GEN_COMMAND,
+ mxt_write_object(data, MXT_GEN_COMMAND_T6,
MXT_COMMAND_BACKUPNV,
MXT_BACKUP_VALUE);
msleep(MXT_BACKUP_TIME);
/* Soft reset */
- mxt_write_object(data, MXT_GEN_COMMAND,
+ mxt_write_object(data, MXT_GEN_COMMAND_T6,
MXT_COMMAND_RESET, 1);
msleep(MXT_RESET_TIME);
@@ -880,12 +910,17 @@ static ssize_t mxt_object_show(struct device *dev,
for (i = 0; i < data->info.object_num; i++) {
object = data->object_table + i;
- count += sprintf(buf + count,
- "Object Table Element %d(Type %d)\n",
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "Object[%d] (Type %d)\n",
i + 1, object->type);
+ if (count >= PAGE_SIZE)
+ return PAGE_SIZE - 1;
if (!mxt_object_readable(object->type)) {
- count += sprintf(buf + count, "\n");
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\n");
+ if (count >= PAGE_SIZE)
+ return PAGE_SIZE - 1;
continue;
}
@@ -895,11 +930,15 @@ static ssize_t mxt_object_show(struct device *dev,
if (error)
return error;
- count += sprintf(buf + count,
- " Byte %d: 0x%x (%d)\n", j, val, val);
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\t[%2d]: %02x (%d)\n", j, val, val);
+ if (count >= PAGE_SIZE)
+ return PAGE_SIZE - 1;
}
- count += sprintf(buf + count, "\n");
+ count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+ if (count >= PAGE_SIZE)
+ return PAGE_SIZE - 1;
}
return count;
@@ -921,7 +960,7 @@ static int mxt_load_fw(struct device *dev, const char *fn)
}
/* Change to the bootloader mode */
- mxt_write_object(data, MXT_GEN_COMMAND,
+ mxt_write_object(data, MXT_GEN_COMMAND_T6,
MXT_COMMAND_RESET, MXT_BOOT_VALUE);
msleep(MXT_RESET_TIME);
@@ -1027,14 +1066,14 @@ static void mxt_start(struct mxt_data *data)
{
/* Touch enable */
mxt_write_object(data,
- MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0x83);
+ MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
}
static void mxt_stop(struct mxt_data *data)
{
/* Touch disable */
mxt_write_object(data,
- MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0);
+ MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
}
static int mxt_input_open(struct input_dev *dev)
@@ -1094,6 +1133,8 @@ static int __devinit mxt_probe(struct i2c_client *client,
0, data->max_x, 0, 0);
input_set_abs_params(input_dev, ABS_Y,
0, data->max_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ 0, 255, 0, 0);
/* For multi touch */
input_mt_init_slots(input_dev, MXT_MAX_FINGER);
@@ -1103,6 +1144,8 @@ static int __devinit mxt_probe(struct i2c_client *client,
0, data->max_x, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, data->max_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
input_set_drvdata(input_dev, data);
i2c_set_clientdata(client, data);
@@ -1182,7 +1225,7 @@ static int mxt_resume(struct device *dev)
struct input_dev *input_dev = data->input_dev;
/* Soft reset */
- mxt_write_object(data, MXT_GEN_COMMAND,
+ mxt_write_object(data, MXT_GEN_COMMAND_T6,
MXT_COMMAND_RESET, 1);
msleep(MXT_RESET_TIME);
diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c
index 432c69be6ac6..122a87883659 100644
--- a/drivers/input/touchscreen/atmel_tsadcc.c
+++ b/drivers/input/touchscreen/atmel_tsadcc.c
@@ -229,7 +229,7 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev)
goto err_release_mem;
}
- err = request_irq(ts_dev->irq, atmel_tsadcc_interrupt, IRQF_DISABLED,
+ err = request_irq(ts_dev->irq, atmel_tsadcc_interrupt, 0,
pdev->dev.driver->name, ts_dev);
if (err) {
dev_err(&pdev->dev, "failed to allocate irq.\n");
diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c
index 1507ce108d5b..902c7214e887 100644
--- a/drivers/input/touchscreen/bu21013_ts.c
+++ b/drivers/input/touchscreen/bu21013_ts.c
@@ -13,6 +13,7 @@
#include <linux/input/bu21013.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
+#include <linux/module.h>
#define PEN_DOWN_INTR 0
#define MAX_FINGERS 2
diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c
index a93c5c26ab3f..d8815c5d54ad 100644
--- a/drivers/input/touchscreen/cy8ctmg110_ts.c
+++ b/drivers/input/touchscreen/cy8ctmg110_ts.c
@@ -84,9 +84,9 @@ static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg,
memcpy(i2c_data + 1, value, len);
ret = i2c_master_send(client, i2c_data, len + 1);
- if (ret != 1) {
+ if (ret != len + 1) {
dev_err(&client->dev, "i2c write data cmd failed\n");
- return ret ? ret : -EIO;
+ return ret < 0 ? ret : -EIO;
}
return 0;
@@ -193,6 +193,8 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client,
ts->client = client;
ts->input = input_dev;
+ ts->reset_pin = pdata->reset_pin;
+ ts->irq_pin = pdata->irq_pin;
snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev));
@@ -328,7 +330,7 @@ static int __devexit cy8ctmg110_remove(struct i2c_client *client)
return 0;
}
-static struct i2c_device_id cy8ctmg110_idtable[] = {
+static const struct i2c_device_id cy8ctmg110_idtable[] = {
{ CY8CTMG110_DRIVER_NAME, 1 },
{ }
};
diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c
index 211811ae5525..6107e563e681 100644
--- a/drivers/input/touchscreen/h3600_ts_input.c
+++ b/drivers/input/touchscreen/h3600_ts_input.c
@@ -396,14 +396,14 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)
set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE);
if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler,
- IRQF_SHARED | IRQF_DISABLED, "h3600_action", ts->dev)) {
+ IRQF_SHARED, "h3600_action", ts->dev)) {
printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n");
err = -EBUSY;
goto fail1;
}
if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler,
- IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", ts->dev)) {
+ IRQF_SHARED, "h3600_suspend", ts->dev)) {
printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n");
err = -EBUSY;
goto fail2;
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
index dd4e8f020b99..639a6044183d 100644
--- a/drivers/input/touchscreen/hp680_ts_input.c
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -93,7 +93,7 @@ static int __init hp680_ts_init(void)
hp680_ts_dev->phys = "hp680_ts/input0";
if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
- IRQF_DISABLED, MODNAME, 0) < 0) {
+ 0, MODNAME, 0) < 0) {
printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
HP680_TS_IRQ);
err = -EBUSY;
diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c
index 66c96bfc5522..327695268e06 100644
--- a/drivers/input/touchscreen/intel-mid-touch.c
+++ b/drivers/input/touchscreen/intel-mid-touch.c
@@ -448,15 +448,11 @@ static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev)
*/
static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)
{
- int err, i, found;
+ int found = 0;
+ int err, i;
u8 r8;
- found = -1;
-
for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
- if (found >= 0)
- break;
-
err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);
if (err)
return err;
@@ -466,16 +462,15 @@ static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)
break;
}
}
- if (found < 0)
- return 0;
if (tsdev->vendor == PMIC_VENDOR_FS) {
- if (found && found > (MRSTOUCH_MAX_CHANNELS - 18))
+ if (found > MRSTOUCH_MAX_CHANNELS - 18)
return -ENOSPC;
} else {
- if (found && found > (MRSTOUCH_MAX_CHANNELS - 4))
+ if (found > MRSTOUCH_MAX_CHANNELS - 4)
return -ENOSPC;
}
+
return found;
}
diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
index 4b0a061811ff..50076c2d59e2 100644
--- a/drivers/input/touchscreen/jornada720_ts.c
+++ b/drivers/input/touchscreen/jornada720_ts.c
@@ -127,7 +127,7 @@ static int __devinit jornada720_ts_probe(struct platform_device *pdev)
error = request_irq(IRQ_GPIO9,
jornada720_ts_interrupt,
- IRQF_DISABLED | IRQF_TRIGGER_RISING,
+ IRQF_TRIGGER_RISING,
"HP7XX Touchscreen driver", pdev);
if (error) {
printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n");
diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c
index dcf803f5a1f7..0a484ed5295c 100644
--- a/drivers/input/touchscreen/lpc32xx_ts.c
+++ b/drivers/input/touchscreen/lpc32xx_ts.c
@@ -276,7 +276,7 @@ static int __devinit lpc32xx_ts_probe(struct platform_device *pdev)
input_set_drvdata(input, tsc);
error = request_irq(tsc->irq, lpc32xx_ts_interrupt,
- IRQF_DISABLED, pdev->name, tsc);
+ 0, pdev->name, tsc);
if (error) {
dev_err(&pdev->dev, "failed requesting interrupt\n");
goto err_put_clock;
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
index 3242e7076258..e966c29ff1bb 100644
--- a/drivers/input/touchscreen/mainstone-wm97xx.c
+++ b/drivers/input/touchscreen/mainstone-wm97xx.c
@@ -157,9 +157,9 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm)
x, y, p);
/* are samples valid */
- if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X ||
- (y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y ||
- (p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES)
+ if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X ||
+ (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y ||
+ (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)
goto up;
/* coordinate is good */
diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
index 4f2713d92791..4627fe55b401 100644
--- a/drivers/input/touchscreen/max11801_ts.c
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -9,7 +9,8 @@
*
* 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.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*/
/*
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
index c5bc62d85bb6..ede02743eac1 100644
--- a/drivers/input/touchscreen/mc13783_ts.c
+++ b/drivers/input/touchscreen/mc13783_ts.c
@@ -35,7 +35,7 @@ MODULE_PARM_DESC(sample_tolerance,
struct mc13783_ts_priv {
struct input_dev *idev;
- struct mc13783 *mc13783;
+ struct mc13xxx *mc13xxx;
struct delayed_work work;
struct workqueue_struct *workq;
unsigned int sample[4];
@@ -45,7 +45,7 @@ static irqreturn_t mc13783_ts_handler(int irq, void *data)
{
struct mc13783_ts_priv *priv = data;
- mc13783_irq_ack(priv->mc13783, irq);
+ mc13xxx_irq_ack(priv->mc13xxx, irq);
/*
* Kick off reading coordinates. Note that if work happens already
@@ -121,10 +121,10 @@ static void mc13783_ts_work(struct work_struct *work)
{
struct mc13783_ts_priv *priv =
container_of(work, struct mc13783_ts_priv, work.work);
- unsigned int mode = MC13783_ADC_MODE_TS;
+ unsigned int mode = MC13XXX_ADC_MODE_TS;
unsigned int channel = 12;
- if (mc13783_adc_do_conversion(priv->mc13783,
+ if (mc13xxx_adc_do_conversion(priv->mc13xxx,
mode, channel, priv->sample) == 0)
mc13783_ts_report_sample(priv);
}
@@ -134,21 +134,21 @@ static int mc13783_ts_open(struct input_dev *dev)
struct mc13783_ts_priv *priv = input_get_drvdata(dev);
int ret;
- mc13783_lock(priv->mc13783);
+ mc13xxx_lock(priv->mc13xxx);
- mc13783_irq_ack(priv->mc13783, MC13783_IRQ_TS);
+ mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS);
- ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_TS,
+ ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS,
mc13783_ts_handler, MC13783_TS_NAME, priv);
if (ret)
goto out;
- ret = mc13783_reg_rmw(priv->mc13783, MC13783_ADC0,
- MC13783_ADC0_TSMOD_MASK, MC13783_ADC0_TSMOD0);
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0,
+ MC13XXX_ADC0_TSMOD_MASK, MC13XXX_ADC0_TSMOD0);
if (ret)
- mc13783_irq_free(priv->mc13783, MC13783_IRQ_TS, priv);
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);
out:
- mc13783_unlock(priv->mc13783);
+ mc13xxx_unlock(priv->mc13xxx);
return ret;
}
@@ -156,11 +156,11 @@ static void mc13783_ts_close(struct input_dev *dev)
{
struct mc13783_ts_priv *priv = input_get_drvdata(dev);
- mc13783_lock(priv->mc13783);
- mc13783_reg_rmw(priv->mc13783, MC13783_ADC0,
- MC13783_ADC0_TSMOD_MASK, 0);
- mc13783_irq_free(priv->mc13783, MC13783_IRQ_TS, priv);
- mc13783_unlock(priv->mc13783);
+ mc13xxx_lock(priv->mc13xxx);
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0,
+ MC13XXX_ADC0_TSMOD_MASK, 0);
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);
+ mc13xxx_unlock(priv->mc13xxx);
cancel_delayed_work_sync(&priv->work);
}
@@ -177,7 +177,7 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
goto err_free_mem;
INIT_DELAYED_WORK(&priv->work, mc13783_ts_work);
- priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
+ priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
priv->idev = idev;
/*
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
index c7f9cebebbb6..4c012fb2b01e 100644
--- a/drivers/input/touchscreen/penmount.c
+++ b/drivers/input/touchscreen/penmount.c
@@ -2,6 +2,7 @@
* Penmount serial touchscreen driver
*
* Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2011 John Sung <penmount.touch@gmail.com>
*
* Based on ELO driver (drivers/input/touchscreen/elo.c)
* Copyright (c) 2004 Vojtech Pavlik
@@ -18,12 +19,14 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/serio.h>
#include <linux/init.h>
-#define DRIVER_DESC "Penmount serial touchscreen driver"
+#define DRIVER_DESC "PenMount serial touchscreen driver"
MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_AUTHOR("John Sung <penmount.touch@gmail.com>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
@@ -31,7 +34,19 @@ MODULE_LICENSE("GPL");
* Definitions & global arrays.
*/
-#define PM_MAX_LENGTH 5
+#define PM_MAX_LENGTH 6
+#define PM_MAX_MTSLOT 16
+#define PM_3000_MTSLOT 2
+#define PM_6250_MTSLOT 12
+
+/*
+ * Multi-touch slot
+ */
+
+struct mt_slot {
+ unsigned short x, y;
+ bool active; /* is the touch valid? */
+};
/*
* Per-touchscreen data.
@@ -43,25 +58,119 @@ struct pm {
int idx;
unsigned char data[PM_MAX_LENGTH];
char phys[32];
+ unsigned char packetsize;
+ unsigned char maxcontacts;
+ struct mt_slot slots[PM_MAX_MTSLOT];
+ void (*parse_packet)(struct pm *);
};
-static irqreturn_t pm_interrupt(struct serio *serio,
- unsigned char data, unsigned int flags)
+/*
+ * pm_mtevent() sends mt events and also emulates pointer movement
+ */
+
+static void pm_mtevent(struct pm *pm, struct input_dev *input)
+{
+ int i;
+
+ for (i = 0; i < pm->maxcontacts; ++i) {
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER,
+ pm->slots[i].active);
+ if (pm->slots[i].active) {
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, pm->slots[i].x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, pm->slots[i].y);
+ }
+ }
+
+ input_mt_report_pointer_emulation(input, true);
+ input_sync(input);
+}
+
+/*
+ * pm_checkpacket() checks if data packet is valid
+ */
+
+static bool pm_checkpacket(unsigned char *packet)
+{
+ int total = 0;
+ int i;
+
+ for (i = 0; i < 5; i++)
+ total += packet[i];
+
+ return packet[5] == (unsigned char)~(total & 0xff);
+}
+
+static void pm_parse_9000(struct pm *pm)
{
- struct pm *pm = serio_get_drvdata(serio);
struct input_dev *dev = pm->dev;
- pm->data[pm->idx] = data;
+ if ((pm->data[0] & 0x80) && pm->packetsize == ++pm->idx) {
+ input_report_abs(dev, ABS_X, pm->data[1] * 128 + pm->data[2]);
+ input_report_abs(dev, ABS_Y, pm->data[3] * 128 + pm->data[4]);
+ input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40));
+ input_sync(dev);
+ pm->idx = 0;
+ }
+}
- if (pm->data[0] & 0x80) {
- if (PM_MAX_LENGTH == ++pm->idx) {
- input_report_abs(dev, ABS_X, pm->data[2] * 128 + pm->data[1]);
- input_report_abs(dev, ABS_Y, pm->data[4] * 128 + pm->data[3]);
- input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40));
+static void pm_parse_6000(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0xbf) == 0x30 && pm->packetsize == ++pm->idx) {
+ if (pm_checkpacket(pm->data)) {
+ input_report_abs(dev, ABS_X,
+ pm->data[2] * 256 + pm->data[1]);
+ input_report_abs(dev, ABS_Y,
+ pm->data[4] * 256 + pm->data[3]);
+ input_report_key(dev, BTN_TOUCH, pm->data[0] & 0x40);
input_sync(dev);
- pm->idx = 0;
}
+ pm->idx = 0;
+ }
+}
+
+static void pm_parse_3000(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0xce) == 0x40 && pm->packetsize == ++pm->idx) {
+ if (pm_checkpacket(pm->data)) {
+ int slotnum = pm->data[0] & 0x0f;
+ pm->slots[slotnum].active = pm->data[0] & 0x30;
+ pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1];
+ pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3];
+ pm_mtevent(pm, dev);
+ }
+ pm->idx = 0;
+ }
+}
+
+static void pm_parse_6250(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0xb0) == 0x30 && pm->packetsize == ++pm->idx) {
+ if (pm_checkpacket(pm->data)) {
+ int slotnum = pm->data[0] & 0x0f;
+ pm->slots[slotnum].active = pm->data[0] & 0x40;
+ pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1];
+ pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3];
+ pm_mtevent(pm, dev);
+ }
+ pm->idx = 0;
}
+}
+
+static irqreturn_t pm_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct pm *pm = serio_get_drvdata(serio);
+
+ pm->data[pm->idx] = data;
+
+ pm->parse_packet(pm);
return IRQ_HANDLED;
}
@@ -74,17 +183,17 @@ static void pm_disconnect(struct serio *serio)
{
struct pm *pm = serio_get_drvdata(serio);
- input_get_device(pm->dev);
- input_unregister_device(pm->dev);
serio_close(serio);
- serio_set_drvdata(serio, NULL);
- input_put_device(pm->dev);
+
+ input_unregister_device(pm->dev);
kfree(pm);
+
+ serio_set_drvdata(serio, NULL);
}
/*
* pm_connect() is the routine that is called when someone adds a
- * new serio device that supports Gunze protocol and registers it as
+ * new serio device that supports PenMount protocol and registers it as
* an input device.
*/
@@ -92,6 +201,7 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)
{
struct pm *pm;
struct input_dev *input_dev;
+ int max_x, max_y;
int err;
pm = kzalloc(sizeof(struct pm), GFP_KERNEL);
@@ -104,8 +214,9 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)
pm->serio = serio;
pm->dev = input_dev;
snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys);
+ pm->maxcontacts = 1;
- input_dev->name = "Penmount Serial TouchScreen";
+ input_dev->name = "PenMount Serial TouchScreen";
input_dev->phys = pm->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_PENMOUNT;
@@ -113,10 +224,52 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.version = 0x0100;
input_dev->dev.parent = &serio->dev;
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- input_set_abs_params(pm->dev, ABS_X, 0, 0x3ff, 0, 0);
- input_set_abs_params(pm->dev, ABS_Y, 0, 0x3ff, 0, 0);
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ switch (serio->id.id) {
+ default:
+ case 0:
+ pm->packetsize = 5;
+ pm->parse_packet = pm_parse_9000;
+ input_dev->id.product = 0x9000;
+ max_x = max_y = 0x3ff;
+ break;
+
+ case 1:
+ pm->packetsize = 6;
+ pm->parse_packet = pm_parse_6000;
+ input_dev->id.product = 0x6000;
+ max_x = max_y = 0x3ff;
+ break;
+
+ case 2:
+ pm->packetsize = 6;
+ pm->parse_packet = pm_parse_3000;
+ input_dev->id.product = 0x3000;
+ max_x = max_y = 0x7ff;
+ pm->maxcontacts = PM_3000_MTSLOT;
+ break;
+
+ case 3:
+ pm->packetsize = 6;
+ pm->parse_packet = pm_parse_6250;
+ input_dev->id.product = 0x6250;
+ max_x = max_y = 0x3ff;
+ pm->maxcontacts = PM_6250_MTSLOT;
+ break;
+ }
+
+ input_set_abs_params(pm->dev, ABS_X, 0, max_x, 0, 0);
+ input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0);
+
+ if (pm->maxcontacts > 1) {
+ input_mt_init_slots(pm->dev, pm->maxcontacts);
+ input_set_abs_params(pm->dev,
+ ABS_MT_POSITION_X, 0, max_x, 0, 0);
+ input_set_abs_params(pm->dev,
+ ABS_MT_POSITION_Y, 0, max_y, 0, 0);
+ }
serio_set_drvdata(serio, pm);
@@ -155,7 +308,7 @@ MODULE_DEVICE_TABLE(serio, pm_serio_ids);
static struct serio_driver pm_drv = {
.driver = {
- .name = "penmountlpc",
+ .name = "serio-penmount",
},
.description = DRIVER_DESC,
.id_table = pm_serio_ids,
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
index 8feb7f3c8be1..64ce697a3456 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -328,7 +328,7 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev)
ts.shift = info->oversampling_shift;
ts.features = platform_get_device_id(pdev)->driver_data;
- ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED,
+ ret = request_irq(ts.irq_tc, stylus_irq, 0,
"s3c2410_ts_pen", ts.input);
if (ret) {
dev_err(dev, "cannot get TC interrupt\n");
diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c
index 22a3411e93c5..0e8f63e5b36f 100644
--- a/drivers/input/touchscreen/tnetv107x-ts.c
+++ b/drivers/input/touchscreen/tnetv107x-ts.c
@@ -13,6 +13,7 @@
* GNU General Public License for more details.
*/
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/errno.h>
@@ -393,5 +394,5 @@ module_exit(tsc_exit);
MODULE_AUTHOR("Cyril Chemparathy");
MODULE_DESCRIPTION("TNETV107X Touchscreen Driver");
-MODULE_ALIAS("platform: tnetv107x-ts");
+MODULE_ALIAS("platform:tnetv107x-ts");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index fadc11545b1e..1f674cb6c55b 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -66,7 +66,6 @@ struct ts_event {
struct tsc2007 {
struct input_dev *input;
char phys[32];
- struct delayed_work work;
struct i2c_client *client;
@@ -76,9 +75,11 @@ struct tsc2007 {
unsigned long poll_delay;
unsigned long poll_period;
- bool pendown;
int irq;
+ wait_queue_head_t wait;
+ bool stopped;
+
int (*get_pendown_state)(void);
void (*clear_penirq)(void);
};
@@ -141,25 +142,8 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
return rt;
}
-static void tsc2007_send_up_event(struct tsc2007 *tsc)
+static bool tsc2007_is_pen_down(struct tsc2007 *ts)
{
- struct input_dev *input = tsc->input;
-
- dev_dbg(&tsc->client->dev, "UP\n");
-
- input_report_key(input, BTN_TOUCH, 0);
- input_report_abs(input, ABS_PRESSURE, 0);
- input_sync(input);
-}
-
-static void tsc2007_work(struct work_struct *work)
-{
- struct tsc2007 *ts =
- container_of(to_delayed_work(work), struct tsc2007, work);
- bool debounced = false;
- struct ts_event tc;
- u32 rt;
-
/*
* NOTE: We can't rely on the pressure to determine the pen down
* state, even though this controller has a pressure sensor.
@@ -170,79 +154,82 @@ static void tsc2007_work(struct work_struct *work)
* The only safe way to check for the pen up condition is in the
* work function by reading the pen signal state (it's a GPIO
* and IRQ). Unfortunately such callback is not always available,
- * in that case we have rely on the pressure anyway.
+ * in that case we assume that the pen is down and expect caller
+ * to fall back on the pressure reading.
*/
- if (ts->get_pendown_state) {
- if (unlikely(!ts->get_pendown_state())) {
- tsc2007_send_up_event(ts);
- ts->pendown = false;
- goto out;
- }
- dev_dbg(&ts->client->dev, "pen is still down\n");
- }
+ if (!ts->get_pendown_state)
+ return true;
+
+ return ts->get_pendown_state();
+}
+
+static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
+{
+ struct tsc2007 *ts = handle;
+ struct input_dev *input = ts->input;
+ struct ts_event tc;
+ u32 rt;
- tsc2007_read_values(ts, &tc);
+ while (!ts->stopped && tsc2007_is_pen_down(ts)) {
- rt = tsc2007_calculate_pressure(ts, &tc);
- if (rt > ts->max_rt) {
- /*
- * Sample found inconsistent by debouncing or pressure is
- * beyond the maximum. Don't report it to user space,
- * repeat at least once more the measurement.
- */
- dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
- debounced = true;
- goto out;
+ /* pen is down, continue with the measurement */
+ tsc2007_read_values(ts, &tc);
- }
+ rt = tsc2007_calculate_pressure(ts, &tc);
- if (rt) {
- struct input_dev *input = ts->input;
+ if (rt == 0 && !ts->get_pendown_state) {
+ /*
+ * If pressure reported is 0 and we don't have
+ * callback to check pendown state, we have to
+ * assume that pen was lifted up.
+ */
+ break;
+ }
- if (!ts->pendown) {
- dev_dbg(&ts->client->dev, "DOWN\n");
+ if (rt <= ts->max_rt) {
+ dev_dbg(&ts->client->dev,
+ "DOWN point(%4d,%4d), pressure (%4u)\n",
+ tc.x, tc.y, rt);
input_report_key(input, BTN_TOUCH, 1);
- ts->pendown = true;
+ input_report_abs(input, ABS_X, tc.x);
+ input_report_abs(input, ABS_Y, tc.y);
+ input_report_abs(input, ABS_PRESSURE, rt);
+
+ input_sync(input);
+
+ } else {
+ /*
+ * Sample found inconsistent by debouncing or pressure is
+ * beyond the maximum. Don't report it to user space,
+ * repeat at least once more the measurement.
+ */
+ dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
}
- input_report_abs(input, ABS_X, tc.x);
- input_report_abs(input, ABS_Y, tc.y);
- input_report_abs(input, ABS_PRESSURE, rt);
+ wait_event_timeout(ts->wait, ts->stopped,
+ msecs_to_jiffies(ts->poll_period));
+ }
- input_sync(input);
+ dev_dbg(&ts->client->dev, "UP\n");
- dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
- tc.x, tc.y, rt);
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_sync(input);
- } else if (!ts->get_pendown_state && ts->pendown) {
- /*
- * We don't have callback to check pendown state, so we
- * have to assume that since pressure reported is 0 the
- * pen was lifted up.
- */
- tsc2007_send_up_event(ts);
- ts->pendown = false;
- }
+ if (ts->clear_penirq)
+ ts->clear_penirq();
- out:
- if (ts->pendown || debounced)
- schedule_delayed_work(&ts->work,
- msecs_to_jiffies(ts->poll_period));
- else
- enable_irq(ts->irq);
+ return IRQ_HANDLED;
}
-static irqreturn_t tsc2007_irq(int irq, void *handle)
+static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
{
struct tsc2007 *ts = handle;
- if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {
- disable_irq_nosync(ts->irq);
- schedule_delayed_work(&ts->work,
- msecs_to_jiffies(ts->poll_delay));
- }
+ if (!ts->get_pendown_state || likely(ts->get_pendown_state()))
+ return IRQ_WAKE_THREAD;
if (ts->clear_penirq)
ts->clear_penirq();
@@ -250,17 +237,40 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
return IRQ_HANDLED;
}
-static void tsc2007_free_irq(struct tsc2007 *ts)
+static void tsc2007_stop(struct tsc2007 *ts)
{
- free_irq(ts->irq, ts);
- if (cancel_delayed_work_sync(&ts->work)) {
- /*
- * Work was pending, therefore we need to enable
- * IRQ here to balance the disable_irq() done in the
- * interrupt handler.
- */
- enable_irq(ts->irq);
+ ts->stopped = true;
+ mb();
+ wake_up(&ts->wait);
+
+ disable_irq(ts->irq);
+}
+
+static int tsc2007_open(struct input_dev *input_dev)
+{
+ struct tsc2007 *ts = input_get_drvdata(input_dev);
+ int err;
+
+ ts->stopped = false;
+ mb();
+
+ enable_irq(ts->irq);
+
+ /* Prepare for touch readings - power down ADC and enable PENIRQ */
+ err = tsc2007_xfer(ts, PWRDOWN);
+ if (err < 0) {
+ tsc2007_stop(ts);
+ return err;
}
+
+ return 0;
+}
+
+static void tsc2007_close(struct input_dev *input_dev)
+{
+ struct tsc2007 *ts = input_get_drvdata(input_dev);
+
+ tsc2007_stop(ts);
}
static int __devinit tsc2007_probe(struct i2c_client *client,
@@ -290,7 +300,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
ts->client = client;
ts->irq = client->irq;
ts->input = input_dev;
- INIT_DELAYED_WORK(&ts->work, tsc2007_work);
+ init_waitqueue_head(&ts->wait);
ts->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms;
@@ -300,6 +310,12 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
ts->get_pendown_state = pdata->get_pendown_state;
ts->clear_penirq = pdata->clear_penirq;
+ if (pdata->x_plate_ohms == 0) {
+ dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
+ err = -EINVAL;
+ goto err_free_mem;
+ }
+
snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev));
@@ -307,6 +323,11 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
input_dev->phys = ts->phys;
input_dev->id.bustype = BUS_I2C;
+ input_dev->open = tsc2007_open;
+ input_dev->close = tsc2007_close;
+
+ input_set_drvdata(input_dev, ts);
+
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
@@ -318,17 +339,14 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
if (pdata->init_platform_hw)
pdata->init_platform_hw();
- err = request_irq(ts->irq, tsc2007_irq, 0,
- client->dev.driver->name, ts);
+ err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq,
+ IRQF_ONESHOT, client->dev.driver->name, ts);
if (err < 0) {
dev_err(&client->dev, "irq %d busy?\n", ts->irq);
goto err_free_mem;
}
- /* Prepare for touch readings - power down ADC and enable PENIRQ */
- err = tsc2007_xfer(ts, PWRDOWN);
- if (err < 0)
- goto err_free_irq;
+ tsc2007_stop(ts);
err = input_register_device(input_dev);
if (err)
@@ -339,7 +357,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
return 0;
err_free_irq:
- tsc2007_free_irq(ts);
+ free_irq(ts->irq, ts);
if (pdata->exit_platform_hw)
pdata->exit_platform_hw();
err_free_mem:
@@ -353,7 +371,7 @@ static int __devexit tsc2007_remove(struct i2c_client *client)
struct tsc2007 *ts = i2c_get_clientdata(client);
struct tsc2007_platform_data *pdata = client->dev.platform_data;
- tsc2007_free_irq(ts);
+ free_irq(ts->irq, ts);
if (pdata->exit_platform_hw)
pdata->exit_platform_hw();
diff --git a/drivers/input/touchscreen/tsc40.c b/drivers/input/touchscreen/tsc40.c
new file mode 100644
index 000000000000..29d5ed4dd31c
--- /dev/null
+++ b/drivers/input/touchscreen/tsc40.c
@@ -0,0 +1,184 @@
+/*
+ * TSC-40 serial touchscreen driver. It should be compatible with
+ * TSC-10 and 25.
+ *
+ * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ * License: GPLv2 as published by the FSF.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define PACKET_LENGTH 5
+struct tsc_ser {
+ struct input_dev *dev;
+ struct serio *serio;
+ u32 idx;
+ unsigned char data[PACKET_LENGTH];
+ char phys[32];
+};
+
+static void tsc_process_data(struct tsc_ser *ptsc)
+{
+ struct input_dev *dev = ptsc->dev;
+ u8 *data = ptsc->data;
+ u32 x;
+ u32 y;
+
+ x = ((data[1] & 0x03) << 8) | data[2];
+ y = ((data[3] & 0x03) << 8) | data[4];
+
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_report_key(dev, BTN_TOUCH, 1);
+
+ input_sync(dev);
+}
+
+static irqreturn_t tsc_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct tsc_ser *ptsc = serio_get_drvdata(serio);
+ struct input_dev *dev = ptsc->dev;
+
+ ptsc->data[ptsc->idx] = data;
+ switch (ptsc->idx++) {
+ case 0:
+ if (unlikely((data & 0x3e) != 0x10)) {
+ dev_dbg(&serio->dev,
+ "unsynchronized packet start (0x%02x)\n", data);
+ ptsc->idx = 0;
+ } else if (!(data & 0x01)) {
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_sync(dev);
+ ptsc->idx = 0;
+ }
+ break;
+
+ case 1:
+ case 3:
+ if (unlikely(data & 0xfc)) {
+ dev_dbg(&serio->dev,
+ "unsynchronized data 0x%02x at offset %d\n",
+ data, ptsc->idx - 1);
+ ptsc->idx = 0;
+ }
+ break;
+
+ case 4:
+ tsc_process_data(ptsc);
+ ptsc->idx = 0;
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int tsc_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tsc_ser *ptsc;
+ struct input_dev *input_dev;
+ int error;
+
+ ptsc = kzalloc(sizeof(struct tsc_ser), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ptsc || !input_dev) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ ptsc->serio = serio;
+ ptsc->dev = input_dev;
+ snprintf(ptsc->phys, sizeof(ptsc->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "TSC-10/25/40 Serial TouchScreen";
+ input_dev->phys = ptsc->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TSC40;
+ input_dev->id.product = 40;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_abs_params(ptsc->dev, ABS_X, 0, 0x3ff, 0, 0);
+ input_set_abs_params(ptsc->dev, ABS_Y, 0, 0x3ff, 0, 0);
+ input_set_abs_params(ptsc->dev, ABS_PRESSURE, 0, 0, 0, 0);
+
+ serio_set_drvdata(serio, ptsc);
+
+ error = serio_open(serio, drv);
+ if (error)
+ goto fail2;
+
+ error = input_register_device(ptsc->dev);
+ if (error)
+ goto fail3;
+
+ return 0;
+
+fail3:
+ serio_close(serio);
+fail2:
+ serio_set_drvdata(serio, NULL);
+fail1:
+ input_free_device(input_dev);
+ kfree(ptsc);
+ return error;
+}
+
+static void tsc_disconnect(struct serio *serio)
+{
+ struct tsc_ser *ptsc = serio_get_drvdata(serio);
+
+ serio_close(serio);
+
+ input_unregister_device(ptsc->dev);
+ kfree(ptsc);
+
+ serio_set_drvdata(serio, NULL);
+}
+
+static struct serio_device_id tsc_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TSC40,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(serio, tsc_serio_ids);
+
+#define DRIVER_DESC "TSC-10/25/40 serial touchscreen driver"
+
+static struct serio_driver tsc_drv = {
+ .driver = {
+ .name = "tsc40",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tsc_serio_ids,
+ .interrupt = tsc_interrupt,
+ .connect = tsc_connect,
+ .disconnect = tsc_disconnect,
+};
+
+static int __init tsc_ser_init(void)
+{
+ return serio_register_driver(&tsc_drv);
+}
+module_init(tsc_ser_init);
+
+static void __exit tsc_exit(void)
+{
+ serio_unregister_driver(&tsc_drv);
+}
+module_exit(tsc_exit);
+
+MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c
index 7a45d68c3516..217aa51135c5 100644
--- a/drivers/input/touchscreen/w90p910_ts.c
+++ b/drivers/input/touchscreen/w90p910_ts.c
@@ -279,7 +279,7 @@ static int __devinit w90x900ts_probe(struct platform_device *pdev)
w90p910_ts->irq_num = platform_get_irq(pdev, 0);
if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt,
- IRQF_DISABLED, "w90p910ts", w90p910_ts)) {
+ 0, "w90p910ts", w90p910_ts)) {
err = -EBUSY;
goto fail4;
}
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index c14412ef4648..1569a3934ab2 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -367,6 +367,20 @@ static int w8001_command(struct w8001 *w8001, unsigned char command,
return rc;
}
+static int w8001_open(struct input_dev *dev)
+{
+ struct w8001 *w8001 = input_get_drvdata(dev);
+
+ return w8001_command(w8001, W8001_CMD_START, false);
+}
+
+static void w8001_close(struct input_dev *dev)
+{
+ struct w8001 *w8001 = input_get_drvdata(dev);
+
+ w8001_command(w8001, W8001_CMD_STOP, false);
+}
+
static int w8001_setup(struct w8001 *w8001)
{
struct input_dev *dev = w8001->dev;
@@ -383,6 +397,8 @@ static int w8001_setup(struct w8001 *w8001)
dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
+ __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+
/* penabled? */
error = w8001_command(w8001, W8001_CMD_QUERY, true);
if (!error) {
@@ -474,7 +490,7 @@ static int w8001_setup(struct w8001 *w8001)
strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
- return w8001_command(w8001, W8001_CMD_START, false);
+ return 0;
}
/*
@@ -485,12 +501,12 @@ static void w8001_disconnect(struct serio *serio)
{
struct w8001 *w8001 = serio_get_drvdata(serio);
- input_get_device(w8001->dev);
- input_unregister_device(w8001->dev);
serio_close(serio);
- serio_set_drvdata(serio, NULL);
- input_put_device(w8001->dev);
+
+ input_unregister_device(w8001->dev);
kfree(w8001);
+
+ serio_set_drvdata(serio, NULL);
}
/*
@@ -534,6 +550,11 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.version = 0x0100;
input_dev->dev.parent = &serio->dev;
+ input_dev->open = w8001_open;
+ input_dev->close = w8001_close;
+
+ input_set_drvdata(input_dev, w8001);
+
err = input_register_device(w8001->dev);
if (err)
goto fail3;
diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c
index 98e61175d3f5..adc13a523ab5 100644
--- a/drivers/input/touchscreen/wm9705.c
+++ b/drivers/input/touchscreen/wm9705.c
@@ -215,8 +215,9 @@ static inline int is_pden(struct wm97xx *wm)
static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
{
int timeout = 5 * delay;
+ bool wants_pen = adcsel & WM97XX_PEN_DOWN;
- if (!wm->pen_probably_down) {
+ if (wants_pen && !wm->pen_probably_down) {
u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
if (!(data & WM97XX_PEN_DOWN))
return RC_PENUP;
@@ -224,13 +225,10 @@ static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
}
/* set up digitiser */
- if (adcsel & 0x8000)
- adcsel = ((adcsel & 0x7fff) + 3) << 12;
-
if (wm->mach_ops && wm->mach_ops->pre_sample)
wm->mach_ops->pre_sample(adcsel);
- wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
- adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
+ | WM97XX_POLL | WM97XX_DELAY(delay));
/* wait 3 AC97 time slots + delay for conversion */
poll_delay(delay);
@@ -256,13 +254,14 @@ static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
wm->mach_ops->post_sample(adcsel);
/* check we have correct sample */
- if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
- dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
- *sample & WM97XX_ADCSEL_MASK);
+ if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+ dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+ adcsel & WM97XX_ADCSEL_MASK,
+ *sample & WM97XX_ADCSEL_MASK);
return RC_PENUP;
}
- if (!(*sample & WM97XX_PEN_DOWN)) {
+ if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
wm->pen_probably_down = 0;
return RC_PENUP;
}
@@ -277,14 +276,14 @@ static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
{
int rc;
- rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x);
+ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
if (rc != RC_VALID)
return rc;
- rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y);
+ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
if (rc != RC_VALID)
return rc;
if (pil) {
- rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p);
+ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, &data->p);
if (rc != RC_VALID)
return rc;
} else
diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c
index 2bc2fb801009..6e743e3dfda4 100644
--- a/drivers/input/touchscreen/wm9712.c
+++ b/drivers/input/touchscreen/wm9712.c
@@ -255,8 +255,9 @@ static inline int is_pden(struct wm97xx *wm)
static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
{
int timeout = 5 * delay;
+ bool wants_pen = adcsel & WM97XX_PEN_DOWN;
- if (!wm->pen_probably_down) {
+ if (wants_pen && !wm->pen_probably_down) {
u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
if (!(data & WM97XX_PEN_DOWN))
return RC_PENUP;
@@ -264,13 +265,10 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
}
/* set up digitiser */
- if (adcsel & 0x8000)
- adcsel = ((adcsel & 0x7fff) + 3) << 12;
-
if (wm->mach_ops && wm->mach_ops->pre_sample)
wm->mach_ops->pre_sample(adcsel);
- wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
- adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
+ | WM97XX_POLL | WM97XX_DELAY(delay));
/* wait 3 AC97 time slots + delay for conversion */
poll_delay(delay);
@@ -296,13 +294,14 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
wm->mach_ops->post_sample(adcsel);
/* check we have correct sample */
- if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
- dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
- *sample & WM97XX_ADCSEL_MASK);
+ if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+ dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+ adcsel & WM97XX_ADCSEL_MASK,
+ *sample & WM97XX_ADCSEL_MASK);
return RC_PENUP;
}
- if (!(*sample & WM97XX_PEN_DOWN)) {
+ if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
wm->pen_probably_down = 0;
return RC_PENUP;
}
@@ -387,16 +386,18 @@ static int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
if (rc != RC_VALID)
return rc;
} else {
- rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x);
+ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN,
+ &data->x);
if (rc != RC_VALID)
return rc;
- rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y);
+ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN,
+ &data->y);
if (rc != RC_VALID)
return rc;
if (pil && !five_wire) {
- rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES,
+ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,
&data->p);
if (rc != RC_VALID)
return rc;
diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c
index 73ec99568f12..7405353199d7 100644
--- a/drivers/input/touchscreen/wm9713.c
+++ b/drivers/input/touchscreen/wm9713.c
@@ -261,8 +261,9 @@ static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
{
u16 dig1;
int timeout = 5 * delay;
+ bool wants_pen = adcsel & WM97XX_PEN_DOWN;
- if (!wm->pen_probably_down) {
+ if (wants_pen && !wm->pen_probably_down) {
u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
if (!(data & WM97XX_PEN_DOWN))
return RC_PENUP;
@@ -270,15 +271,14 @@ static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
}
/* set up digitiser */
- if (adcsel & 0x8000)
- adcsel = 1 << ((adcsel & 0x7fff) + 3);
-
dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
dig1 &= ~WM9713_ADCSEL_MASK;
+ /* WM97XX_ADCSEL_* channels need to be converted to WM9713 format */
+ dig1 |= 1 << ((adcsel & WM97XX_ADCSEL_MASK) >> 12);
if (wm->mach_ops && wm->mach_ops->pre_sample)
wm->mach_ops->pre_sample(adcsel);
- wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel | WM9713_POLL);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL);
/* wait 3 AC97 time slots + delay for conversion */
poll_delay(delay);
@@ -304,13 +304,14 @@ static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
wm->mach_ops->post_sample(adcsel);
/* check we have correct sample */
- if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) {
- dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
- *sample & WM97XX_ADCSRC_MASK);
+ if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+ dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+ adcsel & WM97XX_ADCSEL_MASK,
+ *sample & WM97XX_ADCSEL_MASK);
return RC_PENUP;
}
- if (!(*sample & WM97XX_PEN_DOWN)) {
+ if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
wm->pen_probably_down = 0;
return RC_PENUP;
}
@@ -400,14 +401,14 @@ static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
if (rc != RC_VALID)
return rc;
} else {
- rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x);
+ rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
if (rc != RC_VALID)
return rc;
- rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y);
+ rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
if (rc != RC_VALID)
return rc;
if (pil) {
- rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES,
+ rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,
&data->p);
if (rc != RC_VALID)
return rc;
diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c
index 5b0f15ec874a..f6328c0cded6 100644
--- a/drivers/input/touchscreen/zylonite-wm97xx.c
+++ b/drivers/input/touchscreen/zylonite-wm97xx.c
@@ -122,9 +122,9 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm)
x, y, p);
/* are samples valid */
- if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X ||
- (y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y ||
- (p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES)
+ if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X ||
+ (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y ||
+ (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)
goto up;
/* coordinate is good */
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
new file mode 100644
index 000000000000..5414253b185a
--- /dev/null
+++ b/drivers/iommu/Kconfig
@@ -0,0 +1,134 @@
+# IOMMU_API always gets selected by whoever wants it.
+config IOMMU_API
+ bool
+
+menuconfig IOMMU_SUPPORT
+ bool "IOMMU Hardware Support"
+ default y
+ ---help---
+ Say Y here if you want to compile device drivers for IO Memory
+ Management Units into the kernel. These devices usually allow to
+ remap DMA requests and/or remap interrupts from other devices on the
+ system.
+
+if IOMMU_SUPPORT
+
+# MSM IOMMU support
+config MSM_IOMMU
+ bool "MSM IOMMU Support"
+ depends on ARCH_MSM8X60 || ARCH_MSM8960
+ select IOMMU_API
+ help
+ Support for the IOMMUs found on certain Qualcomm SOCs.
+ These IOMMUs allow virtualization of the address space used by most
+ cores within the multimedia subsystem.
+
+ If unsure, say N here.
+
+config IOMMU_PGTABLES_L2
+ def_bool y
+ depends on MSM_IOMMU && MMU && SMP && CPU_DCACHE_DISABLE=n
+
+# AMD IOMMU support
+config AMD_IOMMU
+ bool "AMD IOMMU support"
+ select SWIOTLB
+ select PCI_MSI
+ select PCI_IOV
+ select IOMMU_API
+ depends on X86_64 && PCI && ACPI
+ ---help---
+ With this option you can enable support for AMD IOMMU hardware in
+ your system. An IOMMU is a hardware component which provides
+ remapping of DMA memory accesses from devices. With an AMD IOMMU you
+ can isolate the the DMA memory of different devices and protect the
+ system from misbehaving device drivers or hardware.
+
+ You can find out if your system has an AMD IOMMU if you look into
+ your BIOS for an option to enable it or if you have an IVRS ACPI
+ table.
+
+config AMD_IOMMU_STATS
+ bool "Export AMD IOMMU statistics to debugfs"
+ depends on AMD_IOMMU
+ select DEBUG_FS
+ ---help---
+ This option enables code in the AMD IOMMU driver to collect various
+ statistics about whats happening in the driver and exports that
+ information to userspace via debugfs.
+ If unsure, say N.
+
+# Intel IOMMU support
+config DMAR_TABLE
+ bool
+
+config INTEL_IOMMU
+ bool "Support for Intel IOMMU using DMA Remapping Devices"
+ depends on PCI_MSI && ACPI && (X86 || IA64_GENERIC)
+ select IOMMU_API
+ select DMAR_TABLE
+ help
+ DMA remapping (DMAR) devices support enables independent address
+ translations for Direct Memory Access (DMA) from devices.
+ These DMA remapping devices are reported via ACPI tables
+ and include PCI device scope covered by these DMA
+ remapping devices.
+
+config INTEL_IOMMU_DEFAULT_ON
+ def_bool y
+ prompt "Enable Intel DMA Remapping Devices by default"
+ depends on INTEL_IOMMU
+ help
+ Selecting this option will enable a DMAR device at boot time if
+ one is found. If this option is not selected, DMAR support can
+ be enabled by passing intel_iommu=on to the kernel.
+
+config INTEL_IOMMU_BROKEN_GFX_WA
+ bool "Workaround broken graphics drivers (going away soon)"
+ depends on INTEL_IOMMU && BROKEN && X86
+ ---help---
+ Current Graphics drivers tend to use physical address
+ for DMA and avoid using DMA APIs. Setting this config
+ option permits the IOMMU driver to set a unity map for
+ all the OS-visible memory. Hence the driver can continue
+ to use physical addresses for DMA, at least until this
+ option is removed in the 2.6.32 kernel.
+
+config INTEL_IOMMU_FLOPPY_WA
+ def_bool y
+ depends on INTEL_IOMMU && X86
+ ---help---
+ Floppy disk drivers are known to bypass DMA API calls
+ thereby failing to work when IOMMU is enabled. This
+ workaround will setup a 1:1 mapping for the first
+ 16MiB to make floppy (an ISA device) work.
+
+config IRQ_REMAP
+ bool "Support for Interrupt Remapping (EXPERIMENTAL)"
+ depends on X86_64 && X86_IO_APIC && PCI_MSI && ACPI && EXPERIMENTAL
+ select DMAR_TABLE
+ ---help---
+ Supports Interrupt remapping for IO-APIC and MSI devices.
+ To use x2apic mode in the CPU's which support x2APIC enhancements or
+ to support platforms with CPU's having > 8 bit APIC ID, say Y.
+
+# OMAP IOMMU support
+config OMAP_IOMMU
+ bool "OMAP IOMMU Support"
+ depends on ARCH_OMAP
+ select IOMMU_API
+
+config OMAP_IOVMM
+ tristate "OMAP IO Virtual Memory Manager Support"
+ depends on OMAP_IOMMU
+
+config OMAP_IOMMU_DEBUG
+ tristate "Export OMAP IOMMU/IOVMM internals in DebugFS"
+ depends on OMAP_IOVMM && DEBUG_FS
+ help
+ Select this to see extensive information about
+ the internal state of OMAP IOMMU/IOVMM in debugfs.
+
+ Say N unless you know you need this.
+
+endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
new file mode 100644
index 000000000000..2f4448794bc7
--- /dev/null
+++ b/drivers/iommu/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_IOMMU_API) += iommu.o
+obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
+obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
+obj-$(CONFIG_DMAR_TABLE) += dmar.o
+obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o
+obj-$(CONFIG_IRQ_REMAP) += intr_remapping.o
+obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
+obj-$(CONFIG_OMAP_IOVMM) += omap-iovmm.o
+obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
new file mode 100644
index 000000000000..4ee277a8521a
--- /dev/null
+++ b/drivers/iommu/amd_iommu.c
@@ -0,0 +1,2826 @@
+/*
+ * Copyright (C) 2007-2010 Advanced Micro Devices, Inc.
+ * Author: Joerg Roedel <joerg.roedel@amd.com>
+ * Leo Duran <leo.duran@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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/pci-ats.h>
+#include <linux/bitmap.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/iommu-helper.h>
+#include <linux/iommu.h>
+#include <linux/delay.h>
+#include <linux/amd-iommu.h>
+#include <asm/msidef.h>
+#include <asm/proto.h>
+#include <asm/iommu.h>
+#include <asm/gart.h>
+#include <asm/dma.h>
+
+#include "amd_iommu_proto.h"
+#include "amd_iommu_types.h"
+
+#define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28))
+
+#define LOOP_TIMEOUT 100000
+
+static DEFINE_RWLOCK(amd_iommu_devtable_lock);
+
+/* A list of preallocated protection domains */
+static LIST_HEAD(iommu_pd_list);
+static DEFINE_SPINLOCK(iommu_pd_list_lock);
+
+/* List of all available dev_data structures */
+static LIST_HEAD(dev_data_list);
+static DEFINE_SPINLOCK(dev_data_list_lock);
+
+/*
+ * Domain for untranslated devices - only allocated
+ * if iommu=pt passed on kernel cmd line.
+ */
+static struct protection_domain *pt_domain;
+
+static struct iommu_ops amd_iommu_ops;
+
+/*
+ * general struct to manage commands send to an IOMMU
+ */
+struct iommu_cmd {
+ u32 data[4];
+};
+
+static void update_domain(struct protection_domain *domain);
+
+/****************************************************************************
+ *
+ * Helper functions
+ *
+ ****************************************************************************/
+
+static struct iommu_dev_data *alloc_dev_data(u16 devid)
+{
+ struct iommu_dev_data *dev_data;
+ unsigned long flags;
+
+ dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
+ if (!dev_data)
+ return NULL;
+
+ dev_data->devid = devid;
+ atomic_set(&dev_data->bind, 0);
+
+ spin_lock_irqsave(&dev_data_list_lock, flags);
+ list_add_tail(&dev_data->dev_data_list, &dev_data_list);
+ spin_unlock_irqrestore(&dev_data_list_lock, flags);
+
+ return dev_data;
+}
+
+static void free_dev_data(struct iommu_dev_data *dev_data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_data_list_lock, flags);
+ list_del(&dev_data->dev_data_list);
+ spin_unlock_irqrestore(&dev_data_list_lock, flags);
+
+ kfree(dev_data);
+}
+
+static struct iommu_dev_data *search_dev_data(u16 devid)
+{
+ struct iommu_dev_data *dev_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_data_list_lock, flags);
+ list_for_each_entry(dev_data, &dev_data_list, dev_data_list) {
+ if (dev_data->devid == devid)
+ goto out_unlock;
+ }
+
+ dev_data = NULL;
+
+out_unlock:
+ spin_unlock_irqrestore(&dev_data_list_lock, flags);
+
+ return dev_data;
+}
+
+static struct iommu_dev_data *find_dev_data(u16 devid)
+{
+ struct iommu_dev_data *dev_data;
+
+ dev_data = search_dev_data(devid);
+
+ if (dev_data == NULL)
+ dev_data = alloc_dev_data(devid);
+
+ return dev_data;
+}
+
+static inline u16 get_device_id(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return calc_devid(pdev->bus->number, pdev->devfn);
+}
+
+static struct iommu_dev_data *get_dev_data(struct device *dev)
+{
+ return dev->archdata.iommu;
+}
+
+/*
+ * In this function the list of preallocated protection domains is traversed to
+ * find the domain for a specific device
+ */
+static struct dma_ops_domain *find_protection_domain(u16 devid)
+{
+ struct dma_ops_domain *entry, *ret = NULL;
+ unsigned long flags;
+ u16 alias = amd_iommu_alias_table[devid];
+
+ if (list_empty(&iommu_pd_list))
+ return NULL;
+
+ spin_lock_irqsave(&iommu_pd_list_lock, flags);
+
+ list_for_each_entry(entry, &iommu_pd_list, list) {
+ if (entry->target_dev == devid ||
+ entry->target_dev == alias) {
+ ret = entry;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&iommu_pd_list_lock, flags);
+
+ return ret;
+}
+
+/*
+ * This function checks if the driver got a valid device from the caller to
+ * avoid dereferencing invalid pointers.
+ */
+static bool check_device(struct device *dev)
+{
+ u16 devid;
+
+ if (!dev || !dev->dma_mask)
+ return false;
+
+ /* No device or no PCI device */
+ if (dev->bus != &pci_bus_type)
+ return false;
+
+ devid = get_device_id(dev);
+
+ /* Out of our scope? */
+ if (devid > amd_iommu_last_bdf)
+ return false;
+
+ if (amd_iommu_rlookup_table[devid] == NULL)
+ return false;
+
+ return true;
+}
+
+static int iommu_init_device(struct device *dev)
+{
+ struct iommu_dev_data *dev_data;
+ u16 alias;
+
+ if (dev->archdata.iommu)
+ return 0;
+
+ dev_data = find_dev_data(get_device_id(dev));
+ if (!dev_data)
+ return -ENOMEM;
+
+ alias = amd_iommu_alias_table[dev_data->devid];
+ if (alias != dev_data->devid) {
+ struct iommu_dev_data *alias_data;
+
+ alias_data = find_dev_data(alias);
+ if (alias_data == NULL) {
+ pr_err("AMD-Vi: Warning: Unhandled device %s\n",
+ dev_name(dev));
+ free_dev_data(dev_data);
+ return -ENOTSUPP;
+ }
+ dev_data->alias_data = alias_data;
+ }
+
+ dev->archdata.iommu = dev_data;
+
+ return 0;
+}
+
+static void iommu_ignore_device(struct device *dev)
+{
+ u16 devid, alias;
+
+ devid = get_device_id(dev);
+ alias = amd_iommu_alias_table[devid];
+
+ memset(&amd_iommu_dev_table[devid], 0, sizeof(struct dev_table_entry));
+ memset(&amd_iommu_dev_table[alias], 0, sizeof(struct dev_table_entry));
+
+ amd_iommu_rlookup_table[devid] = NULL;
+ amd_iommu_rlookup_table[alias] = NULL;
+}
+
+static void iommu_uninit_device(struct device *dev)
+{
+ /*
+ * Nothing to do here - we keep dev_data around for unplugged devices
+ * and reuse it when the device is re-plugged - not doing so would
+ * introduce a ton of races.
+ */
+}
+
+void __init amd_iommu_uninit_devices(void)
+{
+ struct iommu_dev_data *dev_data, *n;
+ struct pci_dev *pdev = NULL;
+
+ for_each_pci_dev(pdev) {
+
+ if (!check_device(&pdev->dev))
+ continue;
+
+ iommu_uninit_device(&pdev->dev);
+ }
+
+ /* Free all of our dev_data structures */
+ list_for_each_entry_safe(dev_data, n, &dev_data_list, dev_data_list)
+ free_dev_data(dev_data);
+}
+
+int __init amd_iommu_init_devices(void)
+{
+ struct pci_dev *pdev = NULL;
+ int ret = 0;
+
+ for_each_pci_dev(pdev) {
+
+ if (!check_device(&pdev->dev))
+ continue;
+
+ ret = iommu_init_device(&pdev->dev);
+ if (ret == -ENOTSUPP)
+ iommu_ignore_device(&pdev->dev);
+ else if (ret)
+ goto out_free;
+ }
+
+ return 0;
+
+out_free:
+
+ amd_iommu_uninit_devices();
+
+ return ret;
+}
+#ifdef CONFIG_AMD_IOMMU_STATS
+
+/*
+ * Initialization code for statistics collection
+ */
+
+DECLARE_STATS_COUNTER(compl_wait);
+DECLARE_STATS_COUNTER(cnt_map_single);
+DECLARE_STATS_COUNTER(cnt_unmap_single);
+DECLARE_STATS_COUNTER(cnt_map_sg);
+DECLARE_STATS_COUNTER(cnt_unmap_sg);
+DECLARE_STATS_COUNTER(cnt_alloc_coherent);
+DECLARE_STATS_COUNTER(cnt_free_coherent);
+DECLARE_STATS_COUNTER(cross_page);
+DECLARE_STATS_COUNTER(domain_flush_single);
+DECLARE_STATS_COUNTER(domain_flush_all);
+DECLARE_STATS_COUNTER(alloced_io_mem);
+DECLARE_STATS_COUNTER(total_map_requests);
+
+static struct dentry *stats_dir;
+static struct dentry *de_fflush;
+
+static void amd_iommu_stats_add(struct __iommu_counter *cnt)
+{
+ if (stats_dir == NULL)
+ return;
+
+ cnt->dent = debugfs_create_u64(cnt->name, 0444, stats_dir,
+ &cnt->value);
+}
+
+static void amd_iommu_stats_init(void)
+{
+ stats_dir = debugfs_create_dir("amd-iommu", NULL);
+ if (stats_dir == NULL)
+ return;
+
+ de_fflush = debugfs_create_bool("fullflush", 0444, stats_dir,
+ (u32 *)&amd_iommu_unmap_flush);
+
+ amd_iommu_stats_add(&compl_wait);
+ amd_iommu_stats_add(&cnt_map_single);
+ amd_iommu_stats_add(&cnt_unmap_single);
+ amd_iommu_stats_add(&cnt_map_sg);
+ amd_iommu_stats_add(&cnt_unmap_sg);
+ amd_iommu_stats_add(&cnt_alloc_coherent);
+ amd_iommu_stats_add(&cnt_free_coherent);
+ amd_iommu_stats_add(&cross_page);
+ amd_iommu_stats_add(&domain_flush_single);
+ amd_iommu_stats_add(&domain_flush_all);
+ amd_iommu_stats_add(&alloced_io_mem);
+ amd_iommu_stats_add(&total_map_requests);
+}
+
+#endif
+
+/****************************************************************************
+ *
+ * Interrupt handling functions
+ *
+ ****************************************************************************/
+
+static void dump_dte_entry(u16 devid)
+{
+ int i;
+
+ for (i = 0; i < 8; ++i)
+ pr_err("AMD-Vi: DTE[%d]: %08x\n", i,
+ amd_iommu_dev_table[devid].data[i]);
+}
+
+static void dump_command(unsigned long phys_addr)
+{
+ struct iommu_cmd *cmd = phys_to_virt(phys_addr);
+ int i;
+
+ for (i = 0; i < 4; ++i)
+ pr_err("AMD-Vi: CMD[%d]: %08x\n", i, cmd->data[i]);
+}
+
+static void iommu_print_event(struct amd_iommu *iommu, void *__evt)
+{
+ u32 *event = __evt;
+ int type = (event[1] >> EVENT_TYPE_SHIFT) & EVENT_TYPE_MASK;
+ int devid = (event[0] >> EVENT_DEVID_SHIFT) & EVENT_DEVID_MASK;
+ int domid = (event[1] >> EVENT_DOMID_SHIFT) & EVENT_DOMID_MASK;
+ int flags = (event[1] >> EVENT_FLAGS_SHIFT) & EVENT_FLAGS_MASK;
+ u64 address = (u64)(((u64)event[3]) << 32) | event[2];
+
+ printk(KERN_ERR "AMD-Vi: Event logged [");
+
+ switch (type) {
+ case EVENT_TYPE_ILL_DEV:
+ printk("ILLEGAL_DEV_TABLE_ENTRY device=%02x:%02x.%x "
+ "address=0x%016llx flags=0x%04x]\n",
+ PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ address, flags);
+ dump_dte_entry(devid);
+ break;
+ case EVENT_TYPE_IO_FAULT:
+ printk("IO_PAGE_FAULT device=%02x:%02x.%x "
+ "domain=0x%04x address=0x%016llx flags=0x%04x]\n",
+ PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ domid, address, flags);
+ break;
+ case EVENT_TYPE_DEV_TAB_ERR:
+ printk("DEV_TAB_HARDWARE_ERROR device=%02x:%02x.%x "
+ "address=0x%016llx flags=0x%04x]\n",
+ PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ address, flags);
+ break;
+ case EVENT_TYPE_PAGE_TAB_ERR:
+ printk("PAGE_TAB_HARDWARE_ERROR device=%02x:%02x.%x "
+ "domain=0x%04x address=0x%016llx flags=0x%04x]\n",
+ PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ domid, address, flags);
+ break;
+ case EVENT_TYPE_ILL_CMD:
+ printk("ILLEGAL_COMMAND_ERROR address=0x%016llx]\n", address);
+ dump_command(address);
+ break;
+ case EVENT_TYPE_CMD_HARD_ERR:
+ printk("COMMAND_HARDWARE_ERROR address=0x%016llx "
+ "flags=0x%04x]\n", address, flags);
+ break;
+ case EVENT_TYPE_IOTLB_INV_TO:
+ printk("IOTLB_INV_TIMEOUT device=%02x:%02x.%x "
+ "address=0x%016llx]\n",
+ PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ address);
+ break;
+ case EVENT_TYPE_INV_DEV_REQ:
+ printk("INVALID_DEVICE_REQUEST device=%02x:%02x.%x "
+ "address=0x%016llx flags=0x%04x]\n",
+ PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ address, flags);
+ break;
+ default:
+ printk(KERN_ERR "UNKNOWN type=0x%02x]\n", type);
+ }
+}
+
+static void iommu_poll_events(struct amd_iommu *iommu)
+{
+ u32 head, tail;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ head = readl(iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
+ tail = readl(iommu->mmio_base + MMIO_EVT_TAIL_OFFSET);
+
+ while (head != tail) {
+ iommu_print_event(iommu, iommu->evt_buf + head);
+ head = (head + EVENT_ENTRY_SIZE) % iommu->evt_buf_size;
+ }
+
+ writel(head, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+}
+
+irqreturn_t amd_iommu_int_thread(int irq, void *data)
+{
+ struct amd_iommu *iommu;
+
+ for_each_iommu(iommu)
+ iommu_poll_events(iommu);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t amd_iommu_int_handler(int irq, void *data)
+{
+ return IRQ_WAKE_THREAD;
+}
+
+/****************************************************************************
+ *
+ * IOMMU command queuing functions
+ *
+ ****************************************************************************/
+
+static int wait_on_sem(volatile u64 *sem)
+{
+ int i = 0;
+
+ while (*sem == 0 && i < LOOP_TIMEOUT) {
+ udelay(1);
+ i += 1;
+ }
+
+ if (i == LOOP_TIMEOUT) {
+ pr_alert("AMD-Vi: Completion-Wait loop timed out\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void copy_cmd_to_buffer(struct amd_iommu *iommu,
+ struct iommu_cmd *cmd,
+ u32 tail)
+{
+ u8 *target;
+
+ target = iommu->cmd_buf + tail;
+ tail = (tail + sizeof(*cmd)) % iommu->cmd_buf_size;
+
+ /* Copy command to buffer */
+ memcpy(target, cmd, sizeof(*cmd));
+
+ /* Tell the IOMMU about it */
+ writel(tail, iommu->mmio_base + MMIO_CMD_TAIL_OFFSET);
+}
+
+static void build_completion_wait(struct iommu_cmd *cmd, u64 address)
+{
+ WARN_ON(address & 0x7ULL);
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->data[0] = lower_32_bits(__pa(address)) | CMD_COMPL_WAIT_STORE_MASK;
+ cmd->data[1] = upper_32_bits(__pa(address));
+ cmd->data[2] = 1;
+ CMD_SET_TYPE(cmd, CMD_COMPL_WAIT);
+}
+
+static void build_inv_dte(struct iommu_cmd *cmd, u16 devid)
+{
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->data[0] = devid;
+ CMD_SET_TYPE(cmd, CMD_INV_DEV_ENTRY);
+}
+
+static void build_inv_iommu_pages(struct iommu_cmd *cmd, u64 address,
+ size_t size, u16 domid, int pde)
+{
+ u64 pages;
+ int s;
+
+ pages = iommu_num_pages(address, size, PAGE_SIZE);
+ s = 0;
+
+ if (pages > 1) {
+ /*
+ * If we have to flush more than one page, flush all
+ * TLB entries for this domain
+ */
+ address = CMD_INV_IOMMU_ALL_PAGES_ADDRESS;
+ s = 1;
+ }
+
+ address &= PAGE_MASK;
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->data[1] |= domid;
+ cmd->data[2] = lower_32_bits(address);
+ cmd->data[3] = upper_32_bits(address);
+ CMD_SET_TYPE(cmd, CMD_INV_IOMMU_PAGES);
+ if (s) /* size bit - we flush more than one 4kb page */
+ cmd->data[2] |= CMD_INV_IOMMU_PAGES_SIZE_MASK;
+ if (pde) /* PDE bit - we wan't flush everything not only the PTEs */
+ cmd->data[2] |= CMD_INV_IOMMU_PAGES_PDE_MASK;
+}
+
+static void build_inv_iotlb_pages(struct iommu_cmd *cmd, u16 devid, int qdep,
+ u64 address, size_t size)
+{
+ u64 pages;
+ int s;
+
+ pages = iommu_num_pages(address, size, PAGE_SIZE);
+ s = 0;
+
+ if (pages > 1) {
+ /*
+ * If we have to flush more than one page, flush all
+ * TLB entries for this domain
+ */
+ address = CMD_INV_IOMMU_ALL_PAGES_ADDRESS;
+ s = 1;
+ }
+
+ address &= PAGE_MASK;
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->data[0] = devid;
+ cmd->data[0] |= (qdep & 0xff) << 24;
+ cmd->data[1] = devid;
+ cmd->data[2] = lower_32_bits(address);
+ cmd->data[3] = upper_32_bits(address);
+ CMD_SET_TYPE(cmd, CMD_INV_IOTLB_PAGES);
+ if (s)
+ cmd->data[2] |= CMD_INV_IOMMU_PAGES_SIZE_MASK;
+}
+
+static void build_inv_all(struct iommu_cmd *cmd)
+{
+ memset(cmd, 0, sizeof(*cmd));
+ CMD_SET_TYPE(cmd, CMD_INV_ALL);
+}
+
+/*
+ * Writes the command to the IOMMUs command buffer and informs the
+ * hardware about the new command.
+ */
+static int iommu_queue_command_sync(struct amd_iommu *iommu,
+ struct iommu_cmd *cmd,
+ bool sync)
+{
+ u32 left, tail, head, next_tail;
+ unsigned long flags;
+
+ WARN_ON(iommu->cmd_buf_size & CMD_BUFFER_UNINITIALIZED);
+
+again:
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ head = readl(iommu->mmio_base + MMIO_CMD_HEAD_OFFSET);
+ tail = readl(iommu->mmio_base + MMIO_CMD_TAIL_OFFSET);
+ next_tail = (tail + sizeof(*cmd)) % iommu->cmd_buf_size;
+ left = (head - next_tail) % iommu->cmd_buf_size;
+
+ if (left <= 2) {
+ struct iommu_cmd sync_cmd;
+ volatile u64 sem = 0;
+ int ret;
+
+ build_completion_wait(&sync_cmd, (u64)&sem);
+ copy_cmd_to_buffer(iommu, &sync_cmd, tail);
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+
+ if ((ret = wait_on_sem(&sem)) != 0)
+ return ret;
+
+ goto again;
+ }
+
+ copy_cmd_to_buffer(iommu, cmd, tail);
+
+ /* We need to sync now to make sure all commands are processed */
+ iommu->need_sync = sync;
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+
+ return 0;
+}
+
+static int iommu_queue_command(struct amd_iommu *iommu, struct iommu_cmd *cmd)
+{
+ return iommu_queue_command_sync(iommu, cmd, true);
+}
+
+/*
+ * This function queues a completion wait command into the command
+ * buffer of an IOMMU
+ */
+static int iommu_completion_wait(struct amd_iommu *iommu)
+{
+ struct iommu_cmd cmd;
+ volatile u64 sem = 0;
+ int ret;
+
+ if (!iommu->need_sync)
+ return 0;
+
+ build_completion_wait(&cmd, (u64)&sem);
+
+ ret = iommu_queue_command_sync(iommu, &cmd, false);
+ if (ret)
+ return ret;
+
+ return wait_on_sem(&sem);
+}
+
+static int iommu_flush_dte(struct amd_iommu *iommu, u16 devid)
+{
+ struct iommu_cmd cmd;
+
+ build_inv_dte(&cmd, devid);
+
+ return iommu_queue_command(iommu, &cmd);
+}
+
+static void iommu_flush_dte_all(struct amd_iommu *iommu)
+{
+ u32 devid;
+
+ for (devid = 0; devid <= 0xffff; ++devid)
+ iommu_flush_dte(iommu, devid);
+
+ iommu_completion_wait(iommu);
+}
+
+/*
+ * This function uses heavy locking and may disable irqs for some time. But
+ * this is no issue because it is only called during resume.
+ */
+static void iommu_flush_tlb_all(struct amd_iommu *iommu)
+{
+ u32 dom_id;
+
+ for (dom_id = 0; dom_id <= 0xffff; ++dom_id) {
+ struct iommu_cmd cmd;
+ build_inv_iommu_pages(&cmd, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS,
+ dom_id, 1);
+ iommu_queue_command(iommu, &cmd);
+ }
+
+ iommu_completion_wait(iommu);
+}
+
+static void iommu_flush_all(struct amd_iommu *iommu)
+{
+ struct iommu_cmd cmd;
+
+ build_inv_all(&cmd);
+
+ iommu_queue_command(iommu, &cmd);
+ iommu_completion_wait(iommu);
+}
+
+void iommu_flush_all_caches(struct amd_iommu *iommu)
+{
+ if (iommu_feature(iommu, FEATURE_IA)) {
+ iommu_flush_all(iommu);
+ } else {
+ iommu_flush_dte_all(iommu);
+ iommu_flush_tlb_all(iommu);
+ }
+}
+
+/*
+ * Command send function for flushing on-device TLB
+ */
+static int device_flush_iotlb(struct iommu_dev_data *dev_data,
+ u64 address, size_t size)
+{
+ struct amd_iommu *iommu;
+ struct iommu_cmd cmd;
+ int qdep;
+
+ qdep = dev_data->ats.qdep;
+ iommu = amd_iommu_rlookup_table[dev_data->devid];
+
+ build_inv_iotlb_pages(&cmd, dev_data->devid, qdep, address, size);
+
+ return iommu_queue_command(iommu, &cmd);
+}
+
+/*
+ * Command send function for invalidating a device table entry
+ */
+static int device_flush_dte(struct iommu_dev_data *dev_data)
+{
+ struct amd_iommu *iommu;
+ int ret;
+
+ iommu = amd_iommu_rlookup_table[dev_data->devid];
+
+ ret = iommu_flush_dte(iommu, dev_data->devid);
+ if (ret)
+ return ret;
+
+ if (dev_data->ats.enabled)
+ ret = device_flush_iotlb(dev_data, 0, ~0UL);
+
+ return ret;
+}
+
+/*
+ * TLB invalidation function which is called from the mapping functions.
+ * It invalidates a single PTE if the range to flush is within a single
+ * page. Otherwise it flushes the whole TLB of the IOMMU.
+ */
+static void __domain_flush_pages(struct protection_domain *domain,
+ u64 address, size_t size, int pde)
+{
+ struct iommu_dev_data *dev_data;
+ struct iommu_cmd cmd;
+ int ret = 0, i;
+
+ build_inv_iommu_pages(&cmd, address, size, domain->id, pde);
+
+ for (i = 0; i < amd_iommus_present; ++i) {
+ if (!domain->dev_iommu[i])
+ continue;
+
+ /*
+ * Devices of this domain are behind this IOMMU
+ * We need a TLB flush
+ */
+ ret |= iommu_queue_command(amd_iommus[i], &cmd);
+ }
+
+ list_for_each_entry(dev_data, &domain->dev_list, list) {
+
+ if (!dev_data->ats.enabled)
+ continue;
+
+ ret |= device_flush_iotlb(dev_data, address, size);
+ }
+
+ WARN_ON(ret);
+}
+
+static void domain_flush_pages(struct protection_domain *domain,
+ u64 address, size_t size)
+{
+ __domain_flush_pages(domain, address, size, 0);
+}
+
+/* Flush the whole IO/TLB for a given protection domain */
+static void domain_flush_tlb(struct protection_domain *domain)
+{
+ __domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 0);
+}
+
+/* Flush the whole IO/TLB for a given protection domain - including PDE */
+static void domain_flush_tlb_pde(struct protection_domain *domain)
+{
+ __domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 1);
+}
+
+static void domain_flush_complete(struct protection_domain *domain)
+{
+ int i;
+
+ for (i = 0; i < amd_iommus_present; ++i) {
+ if (!domain->dev_iommu[i])
+ continue;
+
+ /*
+ * Devices of this domain are behind this IOMMU
+ * We need to wait for completion of all commands.
+ */
+ iommu_completion_wait(amd_iommus[i]);
+ }
+}
+
+
+/*
+ * This function flushes the DTEs for all devices in domain
+ */
+static void domain_flush_devices(struct protection_domain *domain)
+{
+ struct iommu_dev_data *dev_data;
+
+ list_for_each_entry(dev_data, &domain->dev_list, list)
+ device_flush_dte(dev_data);
+}
+
+/****************************************************************************
+ *
+ * The functions below are used the create the page table mappings for
+ * unity mapped regions.
+ *
+ ****************************************************************************/
+
+/*
+ * This function is used to add another level to an IO page table. Adding
+ * another level increases the size of the address space by 9 bits to a size up
+ * to 64 bits.
+ */
+static bool increase_address_space(struct protection_domain *domain,
+ gfp_t gfp)
+{
+ u64 *pte;
+
+ if (domain->mode == PAGE_MODE_6_LEVEL)
+ /* address space already 64 bit large */
+ return false;
+
+ pte = (void *)get_zeroed_page(gfp);
+ if (!pte)
+ return false;
+
+ *pte = PM_LEVEL_PDE(domain->mode,
+ virt_to_phys(domain->pt_root));
+ domain->pt_root = pte;
+ domain->mode += 1;
+ domain->updated = true;
+
+ return true;
+}
+
+static u64 *alloc_pte(struct protection_domain *domain,
+ unsigned long address,
+ unsigned long page_size,
+ u64 **pte_page,
+ gfp_t gfp)
+{
+ int level, end_lvl;
+ u64 *pte, *page;
+
+ BUG_ON(!is_power_of_2(page_size));
+
+ while (address > PM_LEVEL_SIZE(domain->mode))
+ increase_address_space(domain, gfp);
+
+ level = domain->mode - 1;
+ pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)];
+ address = PAGE_SIZE_ALIGN(address, page_size);
+ end_lvl = PAGE_SIZE_LEVEL(page_size);
+
+ while (level > end_lvl) {
+ if (!IOMMU_PTE_PRESENT(*pte)) {
+ page = (u64 *)get_zeroed_page(gfp);
+ if (!page)
+ return NULL;
+ *pte = PM_LEVEL_PDE(level, virt_to_phys(page));
+ }
+
+ /* No level skipping support yet */
+ if (PM_PTE_LEVEL(*pte) != level)
+ return NULL;
+
+ level -= 1;
+
+ pte = IOMMU_PTE_PAGE(*pte);
+
+ if (pte_page && level == end_lvl)
+ *pte_page = pte;
+
+ pte = &pte[PM_LEVEL_INDEX(level, address)];
+ }
+
+ return pte;
+}
+
+/*
+ * This function checks if there is a PTE for a given dma address. If
+ * there is one, it returns the pointer to it.
+ */
+static u64 *fetch_pte(struct protection_domain *domain, unsigned long address)
+{
+ int level;
+ u64 *pte;
+
+ if (address > PM_LEVEL_SIZE(domain->mode))
+ return NULL;
+
+ level = domain->mode - 1;
+ pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)];
+
+ while (level > 0) {
+
+ /* Not Present */
+ if (!IOMMU_PTE_PRESENT(*pte))
+ return NULL;
+
+ /* Large PTE */
+ if (PM_PTE_LEVEL(*pte) == 0x07) {
+ unsigned long pte_mask, __pte;
+
+ /*
+ * If we have a series of large PTEs, make
+ * sure to return a pointer to the first one.
+ */
+ pte_mask = PTE_PAGE_SIZE(*pte);
+ pte_mask = ~((PAGE_SIZE_PTE_COUNT(pte_mask) << 3) - 1);
+ __pte = ((unsigned long)pte) & pte_mask;
+
+ return (u64 *)__pte;
+ }
+
+ /* No level skipping support yet */
+ if (PM_PTE_LEVEL(*pte) != level)
+ return NULL;
+
+ level -= 1;
+
+ /* Walk to the next level */
+ pte = IOMMU_PTE_PAGE(*pte);
+ pte = &pte[PM_LEVEL_INDEX(level, address)];
+ }
+
+ return pte;
+}
+
+/*
+ * Generic mapping functions. It maps a physical address into a DMA
+ * address space. It allocates the page table pages if necessary.
+ * In the future it can be extended to a generic mapping function
+ * supporting all features of AMD IOMMU page tables like level skipping
+ * and full 64 bit address spaces.
+ */
+static int iommu_map_page(struct protection_domain *dom,
+ unsigned long bus_addr,
+ unsigned long phys_addr,
+ int prot,
+ unsigned long page_size)
+{
+ u64 __pte, *pte;
+ int i, count;
+
+ if (!(prot & IOMMU_PROT_MASK))
+ return -EINVAL;
+
+ bus_addr = PAGE_ALIGN(bus_addr);
+ phys_addr = PAGE_ALIGN(phys_addr);
+ count = PAGE_SIZE_PTE_COUNT(page_size);
+ pte = alloc_pte(dom, bus_addr, page_size, NULL, GFP_KERNEL);
+
+ for (i = 0; i < count; ++i)
+ if (IOMMU_PTE_PRESENT(pte[i]))
+ return -EBUSY;
+
+ if (page_size > PAGE_SIZE) {
+ __pte = PAGE_SIZE_PTE(phys_addr, page_size);
+ __pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_P | IOMMU_PTE_FC;
+ } else
+ __pte = phys_addr | IOMMU_PTE_P | IOMMU_PTE_FC;
+
+ if (prot & IOMMU_PROT_IR)
+ __pte |= IOMMU_PTE_IR;
+ if (prot & IOMMU_PROT_IW)
+ __pte |= IOMMU_PTE_IW;
+
+ for (i = 0; i < count; ++i)
+ pte[i] = __pte;
+
+ update_domain(dom);
+
+ return 0;
+}
+
+static unsigned long iommu_unmap_page(struct protection_domain *dom,
+ unsigned long bus_addr,
+ unsigned long page_size)
+{
+ unsigned long long unmap_size, unmapped;
+ u64 *pte;
+
+ BUG_ON(!is_power_of_2(page_size));
+
+ unmapped = 0;
+
+ while (unmapped < page_size) {
+
+ pte = fetch_pte(dom, bus_addr);
+
+ if (!pte) {
+ /*
+ * No PTE for this address
+ * move forward in 4kb steps
+ */
+ unmap_size = PAGE_SIZE;
+ } else if (PM_PTE_LEVEL(*pte) == 0) {
+ /* 4kb PTE found for this address */
+ unmap_size = PAGE_SIZE;
+ *pte = 0ULL;
+ } else {
+ int count, i;
+
+ /* Large PTE found which maps this address */
+ unmap_size = PTE_PAGE_SIZE(*pte);
+ count = PAGE_SIZE_PTE_COUNT(unmap_size);
+ for (i = 0; i < count; i++)
+ pte[i] = 0ULL;
+ }
+
+ bus_addr = (bus_addr & ~(unmap_size - 1)) + unmap_size;
+ unmapped += unmap_size;
+ }
+
+ BUG_ON(!is_power_of_2(unmapped));
+
+ return unmapped;
+}
+
+/*
+ * This function checks if a specific unity mapping entry is needed for
+ * this specific IOMMU.
+ */
+static int iommu_for_unity_map(struct amd_iommu *iommu,
+ struct unity_map_entry *entry)
+{
+ u16 bdf, i;
+
+ for (i = entry->devid_start; i <= entry->devid_end; ++i) {
+ bdf = amd_iommu_alias_table[i];
+ if (amd_iommu_rlookup_table[bdf] == iommu)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * This function actually applies the mapping to the page table of the
+ * dma_ops domain.
+ */
+static int dma_ops_unity_map(struct dma_ops_domain *dma_dom,
+ struct unity_map_entry *e)
+{
+ u64 addr;
+ int ret;
+
+ for (addr = e->address_start; addr < e->address_end;
+ addr += PAGE_SIZE) {
+ ret = iommu_map_page(&dma_dom->domain, addr, addr, e->prot,
+ PAGE_SIZE);
+ if (ret)
+ return ret;
+ /*
+ * if unity mapping is in aperture range mark the page
+ * as allocated in the aperture
+ */
+ if (addr < dma_dom->aperture_size)
+ __set_bit(addr >> PAGE_SHIFT,
+ dma_dom->aperture[0]->bitmap);
+ }
+
+ return 0;
+}
+
+/*
+ * Init the unity mappings for a specific IOMMU in the system
+ *
+ * Basically iterates over all unity mapping entries and applies them to
+ * the default domain DMA of that IOMMU if necessary.
+ */
+static int iommu_init_unity_mappings(struct amd_iommu *iommu)
+{
+ struct unity_map_entry *entry;
+ int ret;
+
+ list_for_each_entry(entry, &amd_iommu_unity_map, list) {
+ if (!iommu_for_unity_map(iommu, entry))
+ continue;
+ ret = dma_ops_unity_map(iommu->default_dom, entry);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Inits the unity mappings required for a specific device
+ */
+static int init_unity_mappings_for_device(struct dma_ops_domain *dma_dom,
+ u16 devid)
+{
+ struct unity_map_entry *e;
+ int ret;
+
+ list_for_each_entry(e, &amd_iommu_unity_map, list) {
+ if (!(devid >= e->devid_start && devid <= e->devid_end))
+ continue;
+ ret = dma_ops_unity_map(dma_dom, e);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * The next functions belong to the address allocator for the dma_ops
+ * interface functions. They work like the allocators in the other IOMMU
+ * drivers. Its basically a bitmap which marks the allocated pages in
+ * the aperture. Maybe it could be enhanced in the future to a more
+ * efficient allocator.
+ *
+ ****************************************************************************/
+
+/*
+ * The address allocator core functions.
+ *
+ * called with domain->lock held
+ */
+
+/*
+ * Used to reserve address ranges in the aperture (e.g. for exclusion
+ * ranges.
+ */
+static void dma_ops_reserve_addresses(struct dma_ops_domain *dom,
+ unsigned long start_page,
+ unsigned int pages)
+{
+ unsigned int i, last_page = dom->aperture_size >> PAGE_SHIFT;
+
+ if (start_page + pages > last_page)
+ pages = last_page - start_page;
+
+ for (i = start_page; i < start_page + pages; ++i) {
+ int index = i / APERTURE_RANGE_PAGES;
+ int page = i % APERTURE_RANGE_PAGES;
+ __set_bit(page, dom->aperture[index]->bitmap);
+ }
+}
+
+/*
+ * This function is used to add a new aperture range to an existing
+ * aperture in case of dma_ops domain allocation or address allocation
+ * failure.
+ */
+static int alloc_new_range(struct dma_ops_domain *dma_dom,
+ bool populate, gfp_t gfp)
+{
+ int index = dma_dom->aperture_size >> APERTURE_RANGE_SHIFT;
+ struct amd_iommu *iommu;
+ unsigned long i, old_size;
+
+#ifdef CONFIG_IOMMU_STRESS
+ populate = false;
+#endif
+
+ if (index >= APERTURE_MAX_RANGES)
+ return -ENOMEM;
+
+ dma_dom->aperture[index] = kzalloc(sizeof(struct aperture_range), gfp);
+ if (!dma_dom->aperture[index])
+ return -ENOMEM;
+
+ dma_dom->aperture[index]->bitmap = (void *)get_zeroed_page(gfp);
+ if (!dma_dom->aperture[index]->bitmap)
+ goto out_free;
+
+ dma_dom->aperture[index]->offset = dma_dom->aperture_size;
+
+ if (populate) {
+ unsigned long address = dma_dom->aperture_size;
+ int i, num_ptes = APERTURE_RANGE_PAGES / 512;
+ u64 *pte, *pte_page;
+
+ for (i = 0; i < num_ptes; ++i) {
+ pte = alloc_pte(&dma_dom->domain, address, PAGE_SIZE,
+ &pte_page, gfp);
+ if (!pte)
+ goto out_free;
+
+ dma_dom->aperture[index]->pte_pages[i] = pte_page;
+
+ address += APERTURE_RANGE_SIZE / 64;
+ }
+ }
+
+ old_size = dma_dom->aperture_size;
+ dma_dom->aperture_size += APERTURE_RANGE_SIZE;
+
+ /* Reserve address range used for MSI messages */
+ if (old_size < MSI_ADDR_BASE_LO &&
+ dma_dom->aperture_size > MSI_ADDR_BASE_LO) {
+ unsigned long spage;
+ int pages;
+
+ pages = iommu_num_pages(MSI_ADDR_BASE_LO, 0x10000, PAGE_SIZE);
+ spage = MSI_ADDR_BASE_LO >> PAGE_SHIFT;
+
+ dma_ops_reserve_addresses(dma_dom, spage, pages);
+ }
+
+ /* Initialize the exclusion range if necessary */
+ for_each_iommu(iommu) {
+ if (iommu->exclusion_start &&
+ iommu->exclusion_start >= dma_dom->aperture[index]->offset
+ && iommu->exclusion_start < dma_dom->aperture_size) {
+ unsigned long startpage;
+ int pages = iommu_num_pages(iommu->exclusion_start,
+ iommu->exclusion_length,
+ PAGE_SIZE);
+ startpage = iommu->exclusion_start >> PAGE_SHIFT;
+ dma_ops_reserve_addresses(dma_dom, startpage, pages);
+ }
+ }
+
+ /*
+ * Check for areas already mapped as present in the new aperture
+ * range and mark those pages as reserved in the allocator. Such
+ * mappings may already exist as a result of requested unity
+ * mappings for devices.
+ */
+ for (i = dma_dom->aperture[index]->offset;
+ i < dma_dom->aperture_size;
+ i += PAGE_SIZE) {
+ u64 *pte = fetch_pte(&dma_dom->domain, i);
+ if (!pte || !IOMMU_PTE_PRESENT(*pte))
+ continue;
+
+ dma_ops_reserve_addresses(dma_dom, i >> PAGE_SHIFT, 1);
+ }
+
+ update_domain(&dma_dom->domain);
+
+ return 0;
+
+out_free:
+ update_domain(&dma_dom->domain);
+
+ free_page((unsigned long)dma_dom->aperture[index]->bitmap);
+
+ kfree(dma_dom->aperture[index]);
+ dma_dom->aperture[index] = NULL;
+
+ return -ENOMEM;
+}
+
+static unsigned long dma_ops_area_alloc(struct device *dev,
+ struct dma_ops_domain *dom,
+ unsigned int pages,
+ unsigned long align_mask,
+ u64 dma_mask,
+ unsigned long start)
+{
+ unsigned long next_bit = dom->next_address % APERTURE_RANGE_SIZE;
+ int max_index = dom->aperture_size >> APERTURE_RANGE_SHIFT;
+ int i = start >> APERTURE_RANGE_SHIFT;
+ unsigned long boundary_size;
+ unsigned long address = -1;
+ unsigned long limit;
+
+ next_bit >>= PAGE_SHIFT;
+
+ boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
+ PAGE_SIZE) >> PAGE_SHIFT;
+
+ for (;i < max_index; ++i) {
+ unsigned long offset = dom->aperture[i]->offset >> PAGE_SHIFT;
+
+ if (dom->aperture[i]->offset >= dma_mask)
+ break;
+
+ limit = iommu_device_max_index(APERTURE_RANGE_PAGES, offset,
+ dma_mask >> PAGE_SHIFT);
+
+ address = iommu_area_alloc(dom->aperture[i]->bitmap,
+ limit, next_bit, pages, 0,
+ boundary_size, align_mask);
+ if (address != -1) {
+ address = dom->aperture[i]->offset +
+ (address << PAGE_SHIFT);
+ dom->next_address = address + (pages << PAGE_SHIFT);
+ break;
+ }
+
+ next_bit = 0;
+ }
+
+ return address;
+}
+
+static unsigned long dma_ops_alloc_addresses(struct device *dev,
+ struct dma_ops_domain *dom,
+ unsigned int pages,
+ unsigned long align_mask,
+ u64 dma_mask)
+{
+ unsigned long address;
+
+#ifdef CONFIG_IOMMU_STRESS
+ dom->next_address = 0;
+ dom->need_flush = true;
+#endif
+
+ address = dma_ops_area_alloc(dev, dom, pages, align_mask,
+ dma_mask, dom->next_address);
+
+ if (address == -1) {
+ dom->next_address = 0;
+ address = dma_ops_area_alloc(dev, dom, pages, align_mask,
+ dma_mask, 0);
+ dom->need_flush = true;
+ }
+
+ if (unlikely(address == -1))
+ address = DMA_ERROR_CODE;
+
+ WARN_ON((address + (PAGE_SIZE*pages)) > dom->aperture_size);
+
+ return address;
+}
+
+/*
+ * The address free function.
+ *
+ * called with domain->lock held
+ */
+static void dma_ops_free_addresses(struct dma_ops_domain *dom,
+ unsigned long address,
+ unsigned int pages)
+{
+ unsigned i = address >> APERTURE_RANGE_SHIFT;
+ struct aperture_range *range = dom->aperture[i];
+
+ BUG_ON(i >= APERTURE_MAX_RANGES || range == NULL);
+
+#ifdef CONFIG_IOMMU_STRESS
+ if (i < 4)
+ return;
+#endif
+
+ if (address >= dom->next_address)
+ dom->need_flush = true;
+
+ address = (address % APERTURE_RANGE_SIZE) >> PAGE_SHIFT;
+
+ bitmap_clear(range->bitmap, address, pages);
+
+}
+
+/****************************************************************************
+ *
+ * The next functions belong to the domain allocation. A domain is
+ * allocated for every IOMMU as the default domain. If device isolation
+ * is enabled, every device get its own domain. The most important thing
+ * about domains is the page table mapping the DMA address space they
+ * contain.
+ *
+ ****************************************************************************/
+
+/*
+ * This function adds a protection domain to the global protection domain list
+ */
+static void add_domain_to_list(struct protection_domain *domain)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_iommu_pd_lock, flags);
+ list_add(&domain->list, &amd_iommu_pd_list);
+ spin_unlock_irqrestore(&amd_iommu_pd_lock, flags);
+}
+
+/*
+ * This function removes a protection domain to the global
+ * protection domain list
+ */
+static void del_domain_from_list(struct protection_domain *domain)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_iommu_pd_lock, flags);
+ list_del(&domain->list);
+ spin_unlock_irqrestore(&amd_iommu_pd_lock, flags);
+}
+
+static u16 domain_id_alloc(void)
+{
+ unsigned long flags;
+ int id;
+
+ write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ id = find_first_zero_bit(amd_iommu_pd_alloc_bitmap, MAX_DOMAIN_ID);
+ BUG_ON(id == 0);
+ if (id > 0 && id < MAX_DOMAIN_ID)
+ __set_bit(id, amd_iommu_pd_alloc_bitmap);
+ else
+ id = 0;
+ write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+
+ return id;
+}
+
+static void domain_id_free(int id)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ if (id > 0 && id < MAX_DOMAIN_ID)
+ __clear_bit(id, amd_iommu_pd_alloc_bitmap);
+ write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+}
+
+static void free_pagetable(struct protection_domain *domain)
+{
+ int i, j;
+ u64 *p1, *p2, *p3;
+
+ p1 = domain->pt_root;
+
+ if (!p1)
+ return;
+
+ for (i = 0; i < 512; ++i) {
+ if (!IOMMU_PTE_PRESENT(p1[i]))
+ continue;
+
+ p2 = IOMMU_PTE_PAGE(p1[i]);
+ for (j = 0; j < 512; ++j) {
+ if (!IOMMU_PTE_PRESENT(p2[j]))
+ continue;
+ p3 = IOMMU_PTE_PAGE(p2[j]);
+ free_page((unsigned long)p3);
+ }
+
+ free_page((unsigned long)p2);
+ }
+
+ free_page((unsigned long)p1);
+
+ domain->pt_root = NULL;
+}
+
+/*
+ * Free a domain, only used if something went wrong in the
+ * allocation path and we need to free an already allocated page table
+ */
+static void dma_ops_domain_free(struct dma_ops_domain *dom)
+{
+ int i;
+
+ if (!dom)
+ return;
+
+ del_domain_from_list(&dom->domain);
+
+ free_pagetable(&dom->domain);
+
+ for (i = 0; i < APERTURE_MAX_RANGES; ++i) {
+ if (!dom->aperture[i])
+ continue;
+ free_page((unsigned long)dom->aperture[i]->bitmap);
+ kfree(dom->aperture[i]);
+ }
+
+ kfree(dom);
+}
+
+/*
+ * Allocates a new protection domain usable for the dma_ops functions.
+ * It also initializes the page table and the address allocator data
+ * structures required for the dma_ops interface
+ */
+static struct dma_ops_domain *dma_ops_domain_alloc(void)
+{
+ struct dma_ops_domain *dma_dom;
+
+ dma_dom = kzalloc(sizeof(struct dma_ops_domain), GFP_KERNEL);
+ if (!dma_dom)
+ return NULL;
+
+ spin_lock_init(&dma_dom->domain.lock);
+
+ dma_dom->domain.id = domain_id_alloc();
+ if (dma_dom->domain.id == 0)
+ goto free_dma_dom;
+ INIT_LIST_HEAD(&dma_dom->domain.dev_list);
+ dma_dom->domain.mode = PAGE_MODE_2_LEVEL;
+ dma_dom->domain.pt_root = (void *)get_zeroed_page(GFP_KERNEL);
+ dma_dom->domain.flags = PD_DMA_OPS_MASK;
+ dma_dom->domain.priv = dma_dom;
+ if (!dma_dom->domain.pt_root)
+ goto free_dma_dom;
+
+ dma_dom->need_flush = false;
+ dma_dom->target_dev = 0xffff;
+
+ add_domain_to_list(&dma_dom->domain);
+
+ if (alloc_new_range(dma_dom, true, GFP_KERNEL))
+ goto free_dma_dom;
+
+ /*
+ * mark the first page as allocated so we never return 0 as
+ * a valid dma-address. So we can use 0 as error value
+ */
+ dma_dom->aperture[0]->bitmap[0] = 1;
+ dma_dom->next_address = 0;
+
+
+ return dma_dom;
+
+free_dma_dom:
+ dma_ops_domain_free(dma_dom);
+
+ return NULL;
+}
+
+/*
+ * little helper function to check whether a given protection domain is a
+ * dma_ops domain
+ */
+static bool dma_ops_domain(struct protection_domain *domain)
+{
+ return domain->flags & PD_DMA_OPS_MASK;
+}
+
+static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats)
+{
+ u64 pte_root = virt_to_phys(domain->pt_root);
+ u32 flags = 0;
+
+ pte_root |= (domain->mode & DEV_ENTRY_MODE_MASK)
+ << DEV_ENTRY_MODE_SHIFT;
+ pte_root |= IOMMU_PTE_IR | IOMMU_PTE_IW | IOMMU_PTE_P | IOMMU_PTE_TV;
+
+ if (ats)
+ flags |= DTE_FLAG_IOTLB;
+
+ amd_iommu_dev_table[devid].data[3] |= flags;
+ amd_iommu_dev_table[devid].data[2] = domain->id;
+ amd_iommu_dev_table[devid].data[1] = upper_32_bits(pte_root);
+ amd_iommu_dev_table[devid].data[0] = lower_32_bits(pte_root);
+}
+
+static void clear_dte_entry(u16 devid)
+{
+ /* remove entry from the device table seen by the hardware */
+ amd_iommu_dev_table[devid].data[0] = IOMMU_PTE_P | IOMMU_PTE_TV;
+ amd_iommu_dev_table[devid].data[1] = 0;
+ amd_iommu_dev_table[devid].data[2] = 0;
+
+ amd_iommu_apply_erratum_63(devid);
+}
+
+static void do_attach(struct iommu_dev_data *dev_data,
+ struct protection_domain *domain)
+{
+ struct amd_iommu *iommu;
+ bool ats;
+
+ iommu = amd_iommu_rlookup_table[dev_data->devid];
+ ats = dev_data->ats.enabled;
+
+ /* Update data structures */
+ dev_data->domain = domain;
+ list_add(&dev_data->list, &domain->dev_list);
+ set_dte_entry(dev_data->devid, domain, ats);
+
+ /* Do reference counting */
+ domain->dev_iommu[iommu->index] += 1;
+ domain->dev_cnt += 1;
+
+ /* Flush the DTE entry */
+ device_flush_dte(dev_data);
+}
+
+static void do_detach(struct iommu_dev_data *dev_data)
+{
+ struct amd_iommu *iommu;
+
+ iommu = amd_iommu_rlookup_table[dev_data->devid];
+
+ /* decrease reference counters */
+ dev_data->domain->dev_iommu[iommu->index] -= 1;
+ dev_data->domain->dev_cnt -= 1;
+
+ /* Update data structures */
+ dev_data->domain = NULL;
+ list_del(&dev_data->list);
+ clear_dte_entry(dev_data->devid);
+
+ /* Flush the DTE entry */
+ device_flush_dte(dev_data);
+}
+
+/*
+ * If a device is not yet associated with a domain, this function does
+ * assigns it visible for the hardware
+ */
+static int __attach_device(struct iommu_dev_data *dev_data,
+ struct protection_domain *domain)
+{
+ int ret;
+
+ /* lock domain */
+ spin_lock(&domain->lock);
+
+ if (dev_data->alias_data != NULL) {
+ struct iommu_dev_data *alias_data = dev_data->alias_data;
+
+ /* Some sanity checks */
+ ret = -EBUSY;
+ if (alias_data->domain != NULL &&
+ alias_data->domain != domain)
+ goto out_unlock;
+
+ if (dev_data->domain != NULL &&
+ dev_data->domain != domain)
+ goto out_unlock;
+
+ /* Do real assignment */
+ if (alias_data->domain == NULL)
+ do_attach(alias_data, domain);
+
+ atomic_inc(&alias_data->bind);
+ }
+
+ if (dev_data->domain == NULL)
+ do_attach(dev_data, domain);
+
+ atomic_inc(&dev_data->bind);
+
+ ret = 0;
+
+out_unlock:
+
+ /* ready */
+ spin_unlock(&domain->lock);
+
+ return ret;
+}
+
+/*
+ * If a device is not yet associated with a domain, this function does
+ * assigns it visible for the hardware
+ */
+static int attach_device(struct device *dev,
+ struct protection_domain *domain)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct iommu_dev_data *dev_data;
+ unsigned long flags;
+ int ret;
+
+ dev_data = get_dev_data(dev);
+
+ if (amd_iommu_iotlb_sup && pci_enable_ats(pdev, PAGE_SHIFT) == 0) {
+ dev_data->ats.enabled = true;
+ dev_data->ats.qdep = pci_ats_queue_depth(pdev);
+ }
+
+ write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ ret = __attach_device(dev_data, domain);
+ write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+
+ /*
+ * We might boot into a crash-kernel here. The crashed kernel
+ * left the caches in the IOMMU dirty. So we have to flush
+ * here to evict all dirty stuff.
+ */
+ domain_flush_tlb_pde(domain);
+
+ return ret;
+}
+
+/*
+ * Removes a device from a protection domain (unlocked)
+ */
+static void __detach_device(struct iommu_dev_data *dev_data)
+{
+ struct protection_domain *domain;
+ unsigned long flags;
+
+ BUG_ON(!dev_data->domain);
+
+ domain = dev_data->domain;
+
+ spin_lock_irqsave(&domain->lock, flags);
+
+ if (dev_data->alias_data != NULL) {
+ struct iommu_dev_data *alias_data = dev_data->alias_data;
+
+ if (atomic_dec_and_test(&alias_data->bind))
+ do_detach(alias_data);
+ }
+
+ if (atomic_dec_and_test(&dev_data->bind))
+ do_detach(dev_data);
+
+ spin_unlock_irqrestore(&domain->lock, flags);
+
+ /*
+ * If we run in passthrough mode the device must be assigned to the
+ * passthrough domain if it is detached from any other domain.
+ * Make sure we can deassign from the pt_domain itself.
+ */
+ if (iommu_pass_through &&
+ (dev_data->domain == NULL && domain != pt_domain))
+ __attach_device(dev_data, pt_domain);
+}
+
+/*
+ * Removes a device from a protection domain (with devtable_lock held)
+ */
+static void detach_device(struct device *dev)
+{
+ struct iommu_dev_data *dev_data;
+ unsigned long flags;
+
+ dev_data = get_dev_data(dev);
+
+ /* lock device table */
+ write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ __detach_device(dev_data);
+ write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+
+ if (dev_data->ats.enabled) {
+ pci_disable_ats(to_pci_dev(dev));
+ dev_data->ats.enabled = false;
+ }
+}
+
+/*
+ * Find out the protection domain structure for a given PCI device. This
+ * will give us the pointer to the page table root for example.
+ */
+static struct protection_domain *domain_for_device(struct device *dev)
+{
+ struct iommu_dev_data *dev_data;
+ struct protection_domain *dom = NULL;
+ unsigned long flags;
+
+ dev_data = get_dev_data(dev);
+
+ if (dev_data->domain)
+ return dev_data->domain;
+
+ if (dev_data->alias_data != NULL) {
+ struct iommu_dev_data *alias_data = dev_data->alias_data;
+
+ read_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ if (alias_data->domain != NULL) {
+ __attach_device(dev_data, alias_data->domain);
+ dom = alias_data->domain;
+ }
+ read_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+ }
+
+ return dom;
+}
+
+static int device_change_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+ u16 devid;
+ struct protection_domain *domain;
+ struct dma_ops_domain *dma_domain;
+ struct amd_iommu *iommu;
+ unsigned long flags;
+
+ if (!check_device(dev))
+ return 0;
+
+ devid = get_device_id(dev);
+ iommu = amd_iommu_rlookup_table[devid];
+
+ switch (action) {
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+
+ domain = domain_for_device(dev);
+
+ if (!domain)
+ goto out;
+ if (iommu_pass_through)
+ break;
+ detach_device(dev);
+ break;
+ case BUS_NOTIFY_ADD_DEVICE:
+
+ iommu_init_device(dev);
+
+ domain = domain_for_device(dev);
+
+ /* allocate a protection domain if a device is added */
+ dma_domain = find_protection_domain(devid);
+ if (dma_domain)
+ goto out;
+ dma_domain = dma_ops_domain_alloc();
+ if (!dma_domain)
+ goto out;
+ dma_domain->target_dev = devid;
+
+ spin_lock_irqsave(&iommu_pd_list_lock, flags);
+ list_add_tail(&dma_domain->list, &iommu_pd_list);
+ spin_unlock_irqrestore(&iommu_pd_list_lock, flags);
+
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+
+ iommu_uninit_device(dev);
+
+ default:
+ goto out;
+ }
+
+ iommu_completion_wait(iommu);
+
+out:
+ return 0;
+}
+
+static struct notifier_block device_nb = {
+ .notifier_call = device_change_notifier,
+};
+
+void amd_iommu_init_notifier(void)
+{
+ bus_register_notifier(&pci_bus_type, &device_nb);
+}
+
+/*****************************************************************************
+ *
+ * The next functions belong to the dma_ops mapping/unmapping code.
+ *
+ *****************************************************************************/
+
+/*
+ * In the dma_ops path we only have the struct device. This function
+ * finds the corresponding IOMMU, the protection domain and the
+ * requestor id for a given device.
+ * If the device is not yet associated with a domain this is also done
+ * in this function.
+ */
+static struct protection_domain *get_domain(struct device *dev)
+{
+ struct protection_domain *domain;
+ struct dma_ops_domain *dma_dom;
+ u16 devid = get_device_id(dev);
+
+ if (!check_device(dev))
+ return ERR_PTR(-EINVAL);
+
+ domain = domain_for_device(dev);
+ if (domain != NULL && !dma_ops_domain(domain))
+ return ERR_PTR(-EBUSY);
+
+ if (domain != NULL)
+ return domain;
+
+ /* Device not bount yet - bind it */
+ dma_dom = find_protection_domain(devid);
+ if (!dma_dom)
+ dma_dom = amd_iommu_rlookup_table[devid]->default_dom;
+ attach_device(dev, &dma_dom->domain);
+ DUMP_printk("Using protection domain %d for device %s\n",
+ dma_dom->domain.id, dev_name(dev));
+
+ return &dma_dom->domain;
+}
+
+static void update_device_table(struct protection_domain *domain)
+{
+ struct iommu_dev_data *dev_data;
+
+ list_for_each_entry(dev_data, &domain->dev_list, list)
+ set_dte_entry(dev_data->devid, domain, dev_data->ats.enabled);
+}
+
+static void update_domain(struct protection_domain *domain)
+{
+ if (!domain->updated)
+ return;
+
+ update_device_table(domain);
+
+ domain_flush_devices(domain);
+ domain_flush_tlb_pde(domain);
+
+ domain->updated = false;
+}
+
+/*
+ * This function fetches the PTE for a given address in the aperture
+ */
+static u64* dma_ops_get_pte(struct dma_ops_domain *dom,
+ unsigned long address)
+{
+ struct aperture_range *aperture;
+ u64 *pte, *pte_page;
+
+ aperture = dom->aperture[APERTURE_RANGE_INDEX(address)];
+ if (!aperture)
+ return NULL;
+
+ pte = aperture->pte_pages[APERTURE_PAGE_INDEX(address)];
+ if (!pte) {
+ pte = alloc_pte(&dom->domain, address, PAGE_SIZE, &pte_page,
+ GFP_ATOMIC);
+ aperture->pte_pages[APERTURE_PAGE_INDEX(address)] = pte_page;
+ } else
+ pte += PM_LEVEL_INDEX(0, address);
+
+ update_domain(&dom->domain);
+
+ return pte;
+}
+
+/*
+ * This is the generic map function. It maps one 4kb page at paddr to
+ * the given address in the DMA address space for the domain.
+ */
+static dma_addr_t dma_ops_domain_map(struct dma_ops_domain *dom,
+ unsigned long address,
+ phys_addr_t paddr,
+ int direction)
+{
+ u64 *pte, __pte;
+
+ WARN_ON(address > dom->aperture_size);
+
+ paddr &= PAGE_MASK;
+
+ pte = dma_ops_get_pte(dom, address);
+ if (!pte)
+ return DMA_ERROR_CODE;
+
+ __pte = paddr | IOMMU_PTE_P | IOMMU_PTE_FC;
+
+ if (direction == DMA_TO_DEVICE)
+ __pte |= IOMMU_PTE_IR;
+ else if (direction == DMA_FROM_DEVICE)
+ __pte |= IOMMU_PTE_IW;
+ else if (direction == DMA_BIDIRECTIONAL)
+ __pte |= IOMMU_PTE_IR | IOMMU_PTE_IW;
+
+ WARN_ON(*pte);
+
+ *pte = __pte;
+
+ return (dma_addr_t)address;
+}
+
+/*
+ * The generic unmapping function for on page in the DMA address space.
+ */
+static void dma_ops_domain_unmap(struct dma_ops_domain *dom,
+ unsigned long address)
+{
+ struct aperture_range *aperture;
+ u64 *pte;
+
+ if (address >= dom->aperture_size)
+ return;
+
+ aperture = dom->aperture[APERTURE_RANGE_INDEX(address)];
+ if (!aperture)
+ return;
+
+ pte = aperture->pte_pages[APERTURE_PAGE_INDEX(address)];
+ if (!pte)
+ return;
+
+ pte += PM_LEVEL_INDEX(0, address);
+
+ WARN_ON(!*pte);
+
+ *pte = 0ULL;
+}
+
+/*
+ * This function contains common code for mapping of a physically
+ * contiguous memory region into DMA address space. It is used by all
+ * mapping functions provided with this IOMMU driver.
+ * Must be called with the domain lock held.
+ */
+static dma_addr_t __map_single(struct device *dev,
+ struct dma_ops_domain *dma_dom,
+ phys_addr_t paddr,
+ size_t size,
+ int dir,
+ bool align,
+ u64 dma_mask)
+{
+ dma_addr_t offset = paddr & ~PAGE_MASK;
+ dma_addr_t address, start, ret;
+ unsigned int pages;
+ unsigned long align_mask = 0;
+ int i;
+
+ pages = iommu_num_pages(paddr, size, PAGE_SIZE);
+ paddr &= PAGE_MASK;
+
+ INC_STATS_COUNTER(total_map_requests);
+
+ if (pages > 1)
+ INC_STATS_COUNTER(cross_page);
+
+ if (align)
+ align_mask = (1UL << get_order(size)) - 1;
+
+retry:
+ address = dma_ops_alloc_addresses(dev, dma_dom, pages, align_mask,
+ dma_mask);
+ if (unlikely(address == DMA_ERROR_CODE)) {
+ /*
+ * setting next_address here will let the address
+ * allocator only scan the new allocated range in the
+ * first run. This is a small optimization.
+ */
+ dma_dom->next_address = dma_dom->aperture_size;
+
+ if (alloc_new_range(dma_dom, false, GFP_ATOMIC))
+ goto out;
+
+ /*
+ * aperture was successfully enlarged by 128 MB, try
+ * allocation again
+ */
+ goto retry;
+ }
+
+ start = address;
+ for (i = 0; i < pages; ++i) {
+ ret = dma_ops_domain_map(dma_dom, start, paddr, dir);
+ if (ret == DMA_ERROR_CODE)
+ goto out_unmap;
+
+ paddr += PAGE_SIZE;
+ start += PAGE_SIZE;
+ }
+ address += offset;
+
+ ADD_STATS_COUNTER(alloced_io_mem, size);
+
+ if (unlikely(dma_dom->need_flush && !amd_iommu_unmap_flush)) {
+ domain_flush_tlb(&dma_dom->domain);
+ dma_dom->need_flush = false;
+ } else if (unlikely(amd_iommu_np_cache))
+ domain_flush_pages(&dma_dom->domain, address, size);
+
+out:
+ return address;
+
+out_unmap:
+
+ for (--i; i >= 0; --i) {
+ start -= PAGE_SIZE;
+ dma_ops_domain_unmap(dma_dom, start);
+ }
+
+ dma_ops_free_addresses(dma_dom, address, pages);
+
+ return DMA_ERROR_CODE;
+}
+
+/*
+ * Does the reverse of the __map_single function. Must be called with
+ * the domain lock held too
+ */
+static void __unmap_single(struct dma_ops_domain *dma_dom,
+ dma_addr_t dma_addr,
+ size_t size,
+ int dir)
+{
+ dma_addr_t flush_addr;
+ dma_addr_t i, start;
+ unsigned int pages;
+
+ if ((dma_addr == DMA_ERROR_CODE) ||
+ (dma_addr + size > dma_dom->aperture_size))
+ return;
+
+ flush_addr = dma_addr;
+ pages = iommu_num_pages(dma_addr, size, PAGE_SIZE);
+ dma_addr &= PAGE_MASK;
+ start = dma_addr;
+
+ for (i = 0; i < pages; ++i) {
+ dma_ops_domain_unmap(dma_dom, start);
+ start += PAGE_SIZE;
+ }
+
+ SUB_STATS_COUNTER(alloced_io_mem, size);
+
+ dma_ops_free_addresses(dma_dom, dma_addr, pages);
+
+ if (amd_iommu_unmap_flush || dma_dom->need_flush) {
+ domain_flush_pages(&dma_dom->domain, flush_addr, size);
+ dma_dom->need_flush = false;
+ }
+}
+
+/*
+ * The exported map_single function for dma_ops.
+ */
+static dma_addr_t map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ unsigned long flags;
+ struct protection_domain *domain;
+ dma_addr_t addr;
+ u64 dma_mask;
+ phys_addr_t paddr = page_to_phys(page) + offset;
+
+ INC_STATS_COUNTER(cnt_map_single);
+
+ domain = get_domain(dev);
+ if (PTR_ERR(domain) == -EINVAL)
+ return (dma_addr_t)paddr;
+ else if (IS_ERR(domain))
+ return DMA_ERROR_CODE;
+
+ dma_mask = *dev->dma_mask;
+
+ spin_lock_irqsave(&domain->lock, flags);
+
+ addr = __map_single(dev, domain->priv, paddr, size, dir, false,
+ dma_mask);
+ if (addr == DMA_ERROR_CODE)
+ goto out;
+
+ domain_flush_complete(domain);
+
+out:
+ spin_unlock_irqrestore(&domain->lock, flags);
+
+ return addr;
+}
+
+/*
+ * The exported unmap_single function for dma_ops.
+ */
+static void unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size,
+ enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ unsigned long flags;
+ struct protection_domain *domain;
+
+ INC_STATS_COUNTER(cnt_unmap_single);
+
+ domain = get_domain(dev);
+ if (IS_ERR(domain))
+ return;
+
+ spin_lock_irqsave(&domain->lock, flags);
+
+ __unmap_single(domain->priv, dma_addr, size, dir);
+
+ domain_flush_complete(domain);
+
+ spin_unlock_irqrestore(&domain->lock, flags);
+}
+
+/*
+ * This is a special map_sg function which is used if we should map a
+ * device which is not handled by an AMD IOMMU in the system.
+ */
+static int map_sg_no_iommu(struct device *dev, struct scatterlist *sglist,
+ int nelems, int dir)
+{
+ struct scatterlist *s;
+ int i;
+
+ for_each_sg(sglist, s, nelems, i) {
+ s->dma_address = (dma_addr_t)sg_phys(s);
+ s->dma_length = s->length;
+ }
+
+ return nelems;
+}
+
+/*
+ * The exported map_sg function for dma_ops (handles scatter-gather
+ * lists).
+ */
+static int map_sg(struct device *dev, struct scatterlist *sglist,
+ int nelems, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ unsigned long flags;
+ struct protection_domain *domain;
+ int i;
+ struct scatterlist *s;
+ phys_addr_t paddr;
+ int mapped_elems = 0;
+ u64 dma_mask;
+
+ INC_STATS_COUNTER(cnt_map_sg);
+
+ domain = get_domain(dev);
+ if (PTR_ERR(domain) == -EINVAL)
+ return map_sg_no_iommu(dev, sglist, nelems, dir);
+ else if (IS_ERR(domain))
+ return 0;
+
+ dma_mask = *dev->dma_mask;
+
+ spin_lock_irqsave(&domain->lock, flags);
+
+ for_each_sg(sglist, s, nelems, i) {
+ paddr = sg_phys(s);
+
+ s->dma_address = __map_single(dev, domain->priv,
+ paddr, s->length, dir, false,
+ dma_mask);
+
+ if (s->dma_address) {
+ s->dma_length = s->length;
+ mapped_elems++;
+ } else
+ goto unmap;
+ }
+
+ domain_flush_complete(domain);
+
+out:
+ spin_unlock_irqrestore(&domain->lock, flags);
+
+ return mapped_elems;
+unmap:
+ for_each_sg(sglist, s, mapped_elems, i) {
+ if (s->dma_address)
+ __unmap_single(domain->priv, s->dma_address,
+ s->dma_length, dir);
+ s->dma_address = s->dma_length = 0;
+ }
+
+ mapped_elems = 0;
+
+ goto out;
+}
+
+/*
+ * The exported map_sg function for dma_ops (handles scatter-gather
+ * lists).
+ */
+static void unmap_sg(struct device *dev, struct scatterlist *sglist,
+ int nelems, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ unsigned long flags;
+ struct protection_domain *domain;
+ struct scatterlist *s;
+ int i;
+
+ INC_STATS_COUNTER(cnt_unmap_sg);
+
+ domain = get_domain(dev);
+ if (IS_ERR(domain))
+ return;
+
+ spin_lock_irqsave(&domain->lock, flags);
+
+ for_each_sg(sglist, s, nelems, i) {
+ __unmap_single(domain->priv, s->dma_address,
+ s->dma_length, dir);
+ s->dma_address = s->dma_length = 0;
+ }
+
+ domain_flush_complete(domain);
+
+ spin_unlock_irqrestore(&domain->lock, flags);
+}
+
+/*
+ * The exported alloc_coherent function for dma_ops.
+ */
+static void *alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_addr, gfp_t flag)
+{
+ unsigned long flags;
+ void *virt_addr;
+ struct protection_domain *domain;
+ phys_addr_t paddr;
+ u64 dma_mask = dev->coherent_dma_mask;
+
+ INC_STATS_COUNTER(cnt_alloc_coherent);
+
+ domain = get_domain(dev);
+ if (PTR_ERR(domain) == -EINVAL) {
+ virt_addr = (void *)__get_free_pages(flag, get_order(size));
+ *dma_addr = __pa(virt_addr);
+ return virt_addr;
+ } else if (IS_ERR(domain))
+ return NULL;
+
+ dma_mask = dev->coherent_dma_mask;
+ flag &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
+ flag |= __GFP_ZERO;
+
+ virt_addr = (void *)__get_free_pages(flag, get_order(size));
+ if (!virt_addr)
+ return NULL;
+
+ paddr = virt_to_phys(virt_addr);
+
+ if (!dma_mask)
+ dma_mask = *dev->dma_mask;
+
+ spin_lock_irqsave(&domain->lock, flags);
+
+ *dma_addr = __map_single(dev, domain->priv, paddr,
+ size, DMA_BIDIRECTIONAL, true, dma_mask);
+
+ if (*dma_addr == DMA_ERROR_CODE) {
+ spin_unlock_irqrestore(&domain->lock, flags);
+ goto out_free;
+ }
+
+ domain_flush_complete(domain);
+
+ spin_unlock_irqrestore(&domain->lock, flags);
+
+ return virt_addr;
+
+out_free:
+
+ free_pages((unsigned long)virt_addr, get_order(size));
+
+ return NULL;
+}
+
+/*
+ * The exported free_coherent function for dma_ops.
+ */
+static void free_coherent(struct device *dev, size_t size,
+ void *virt_addr, dma_addr_t dma_addr)
+{
+ unsigned long flags;
+ struct protection_domain *domain;
+
+ INC_STATS_COUNTER(cnt_free_coherent);
+
+ domain = get_domain(dev);
+ if (IS_ERR(domain))
+ goto free_mem;
+
+ spin_lock_irqsave(&domain->lock, flags);
+
+ __unmap_single(domain->priv, dma_addr, size, DMA_BIDIRECTIONAL);
+
+ domain_flush_complete(domain);
+
+ spin_unlock_irqrestore(&domain->lock, flags);
+
+free_mem:
+ free_pages((unsigned long)virt_addr, get_order(size));
+}
+
+/*
+ * This function is called by the DMA layer to find out if we can handle a
+ * particular device. It is part of the dma_ops.
+ */
+static int amd_iommu_dma_supported(struct device *dev, u64 mask)
+{
+ return check_device(dev);
+}
+
+/*
+ * The function for pre-allocating protection domains.
+ *
+ * If the driver core informs the DMA layer if a driver grabs a device
+ * we don't need to preallocate the protection domains anymore.
+ * For now we have to.
+ */
+static void prealloc_protection_domains(void)
+{
+ struct pci_dev *dev = NULL;
+ struct dma_ops_domain *dma_dom;
+ u16 devid;
+
+ for_each_pci_dev(dev) {
+
+ /* Do we handle this device? */
+ if (!check_device(&dev->dev))
+ continue;
+
+ /* Is there already any domain for it? */
+ if (domain_for_device(&dev->dev))
+ continue;
+
+ devid = get_device_id(&dev->dev);
+
+ dma_dom = dma_ops_domain_alloc();
+ if (!dma_dom)
+ continue;
+ init_unity_mappings_for_device(dma_dom, devid);
+ dma_dom->target_dev = devid;
+
+ attach_device(&dev->dev, &dma_dom->domain);
+
+ list_add_tail(&dma_dom->list, &iommu_pd_list);
+ }
+}
+
+static struct dma_map_ops amd_iommu_dma_ops = {
+ .alloc_coherent = alloc_coherent,
+ .free_coherent = free_coherent,
+ .map_page = map_page,
+ .unmap_page = unmap_page,
+ .map_sg = map_sg,
+ .unmap_sg = unmap_sg,
+ .dma_supported = amd_iommu_dma_supported,
+};
+
+static unsigned device_dma_ops_init(void)
+{
+ struct pci_dev *pdev = NULL;
+ unsigned unhandled = 0;
+
+ for_each_pci_dev(pdev) {
+ if (!check_device(&pdev->dev)) {
+ unhandled += 1;
+ continue;
+ }
+
+ pdev->dev.archdata.dma_ops = &amd_iommu_dma_ops;
+ }
+
+ return unhandled;
+}
+
+/*
+ * The function which clues the AMD IOMMU driver into dma_ops.
+ */
+
+void __init amd_iommu_init_api(void)
+{
+ bus_set_iommu(&pci_bus_type, &amd_iommu_ops);
+}
+
+int __init amd_iommu_init_dma_ops(void)
+{
+ struct amd_iommu *iommu;
+ int ret, unhandled;
+
+ /*
+ * first allocate a default protection domain for every IOMMU we
+ * found in the system. Devices not assigned to any other
+ * protection domain will be assigned to the default one.
+ */
+ for_each_iommu(iommu) {
+ iommu->default_dom = dma_ops_domain_alloc();
+ if (iommu->default_dom == NULL)
+ return -ENOMEM;
+ iommu->default_dom->domain.flags |= PD_DEFAULT_MASK;
+ ret = iommu_init_unity_mappings(iommu);
+ if (ret)
+ goto free_domains;
+ }
+
+ /*
+ * Pre-allocate the protection domains for each device.
+ */
+ prealloc_protection_domains();
+
+ iommu_detected = 1;
+ swiotlb = 0;
+
+ /* Make the driver finally visible to the drivers */
+ unhandled = device_dma_ops_init();
+ if (unhandled && max_pfn > MAX_DMA32_PFN) {
+ /* There are unhandled devices - initialize swiotlb for them */
+ swiotlb = 1;
+ }
+
+ amd_iommu_stats_init();
+
+ return 0;
+
+free_domains:
+
+ for_each_iommu(iommu) {
+ if (iommu->default_dom)
+ dma_ops_domain_free(iommu->default_dom);
+ }
+
+ return ret;
+}
+
+/*****************************************************************************
+ *
+ * The following functions belong to the exported interface of AMD IOMMU
+ *
+ * This interface allows access to lower level functions of the IOMMU
+ * like protection domain handling and assignement of devices to domains
+ * which is not possible with the dma_ops interface.
+ *
+ *****************************************************************************/
+
+static void cleanup_domain(struct protection_domain *domain)
+{
+ struct iommu_dev_data *dev_data, *next;
+ unsigned long flags;
+
+ write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+
+ list_for_each_entry_safe(dev_data, next, &domain->dev_list, list) {
+ __detach_device(dev_data);
+ atomic_set(&dev_data->bind, 0);
+ }
+
+ write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+}
+
+static void protection_domain_free(struct protection_domain *domain)
+{
+ if (!domain)
+ return;
+
+ del_domain_from_list(domain);
+
+ if (domain->id)
+ domain_id_free(domain->id);
+
+ kfree(domain);
+}
+
+static struct protection_domain *protection_domain_alloc(void)
+{
+ struct protection_domain *domain;
+
+ domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+ if (!domain)
+ return NULL;
+
+ spin_lock_init(&domain->lock);
+ mutex_init(&domain->api_lock);
+ domain->id = domain_id_alloc();
+ if (!domain->id)
+ goto out_err;
+ INIT_LIST_HEAD(&domain->dev_list);
+
+ add_domain_to_list(domain);
+
+ return domain;
+
+out_err:
+ kfree(domain);
+
+ return NULL;
+}
+
+static int amd_iommu_domain_init(struct iommu_domain *dom)
+{
+ struct protection_domain *domain;
+
+ domain = protection_domain_alloc();
+ if (!domain)
+ goto out_free;
+
+ domain->mode = PAGE_MODE_3_LEVEL;
+ domain->pt_root = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!domain->pt_root)
+ goto out_free;
+
+ dom->priv = domain;
+
+ return 0;
+
+out_free:
+ protection_domain_free(domain);
+
+ return -ENOMEM;
+}
+
+static void amd_iommu_domain_destroy(struct iommu_domain *dom)
+{
+ struct protection_domain *domain = dom->priv;
+
+ if (!domain)
+ return;
+
+ if (domain->dev_cnt > 0)
+ cleanup_domain(domain);
+
+ BUG_ON(domain->dev_cnt != 0);
+
+ free_pagetable(domain);
+
+ protection_domain_free(domain);
+
+ dom->priv = NULL;
+}
+
+static void amd_iommu_detach_device(struct iommu_domain *dom,
+ struct device *dev)
+{
+ struct iommu_dev_data *dev_data = dev->archdata.iommu;
+ struct amd_iommu *iommu;
+ u16 devid;
+
+ if (!check_device(dev))
+ return;
+
+ devid = get_device_id(dev);
+
+ if (dev_data->domain != NULL)
+ detach_device(dev);
+
+ iommu = amd_iommu_rlookup_table[devid];
+ if (!iommu)
+ return;
+
+ iommu_completion_wait(iommu);
+}
+
+static int amd_iommu_attach_device(struct iommu_domain *dom,
+ struct device *dev)
+{
+ struct protection_domain *domain = dom->priv;
+ struct iommu_dev_data *dev_data;
+ struct amd_iommu *iommu;
+ int ret;
+
+ if (!check_device(dev))
+ return -EINVAL;
+
+ dev_data = dev->archdata.iommu;
+
+ iommu = amd_iommu_rlookup_table[dev_data->devid];
+ if (!iommu)
+ return -EINVAL;
+
+ if (dev_data->domain)
+ detach_device(dev);
+
+ ret = attach_device(dev, domain);
+
+ iommu_completion_wait(iommu);
+
+ return ret;
+}
+
+static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
+ phys_addr_t paddr, int gfp_order, int iommu_prot)
+{
+ unsigned long page_size = 0x1000UL << gfp_order;
+ struct protection_domain *domain = dom->priv;
+ int prot = 0;
+ int ret;
+
+ if (iommu_prot & IOMMU_READ)
+ prot |= IOMMU_PROT_IR;
+ if (iommu_prot & IOMMU_WRITE)
+ prot |= IOMMU_PROT_IW;
+
+ mutex_lock(&domain->api_lock);
+ ret = iommu_map_page(domain, iova, paddr, prot, page_size);
+ mutex_unlock(&domain->api_lock);
+
+ return ret;
+}
+
+static int amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova,
+ int gfp_order)
+{
+ struct protection_domain *domain = dom->priv;
+ unsigned long page_size, unmap_size;
+
+ page_size = 0x1000UL << gfp_order;
+
+ mutex_lock(&domain->api_lock);
+ unmap_size = iommu_unmap_page(domain, iova, page_size);
+ mutex_unlock(&domain->api_lock);
+
+ domain_flush_tlb_pde(domain);
+
+ return get_order(unmap_size);
+}
+
+static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
+ unsigned long iova)
+{
+ struct protection_domain *domain = dom->priv;
+ unsigned long offset_mask;
+ phys_addr_t paddr;
+ u64 *pte, __pte;
+
+ pte = fetch_pte(domain, iova);
+
+ if (!pte || !IOMMU_PTE_PRESENT(*pte))
+ return 0;
+
+ if (PM_PTE_LEVEL(*pte) == 0)
+ offset_mask = PAGE_SIZE - 1;
+ else
+ offset_mask = PTE_PAGE_SIZE(*pte) - 1;
+
+ __pte = *pte & PM_ADDR_MASK;
+ paddr = (__pte & ~offset_mask) | (iova & offset_mask);
+
+ return paddr;
+}
+
+static int amd_iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ switch (cap) {
+ case IOMMU_CAP_CACHE_COHERENCY:
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct iommu_ops amd_iommu_ops = {
+ .domain_init = amd_iommu_domain_init,
+ .domain_destroy = amd_iommu_domain_destroy,
+ .attach_dev = amd_iommu_attach_device,
+ .detach_dev = amd_iommu_detach_device,
+ .map = amd_iommu_map,
+ .unmap = amd_iommu_unmap,
+ .iova_to_phys = amd_iommu_iova_to_phys,
+ .domain_has_cap = amd_iommu_domain_has_cap,
+};
+
+/*****************************************************************************
+ *
+ * The next functions do a basic initialization of IOMMU for pass through
+ * mode
+ *
+ * In passthrough mode the IOMMU is initialized and enabled but not used for
+ * DMA-API translation.
+ *
+ *****************************************************************************/
+
+int __init amd_iommu_init_passthrough(void)
+{
+ struct amd_iommu *iommu;
+ struct pci_dev *dev = NULL;
+ u16 devid;
+
+ /* allocate passthrough domain */
+ pt_domain = protection_domain_alloc();
+ if (!pt_domain)
+ return -ENOMEM;
+
+ pt_domain->mode |= PAGE_MODE_NONE;
+
+ for_each_pci_dev(dev) {
+ if (!check_device(&dev->dev))
+ continue;
+
+ devid = get_device_id(&dev->dev);
+
+ iommu = amd_iommu_rlookup_table[devid];
+ if (!iommu)
+ continue;
+
+ attach_device(&dev->dev, pt_domain);
+ }
+
+ pr_info("AMD-Vi: Initialized for Passthrough Mode\n");
+
+ return 0;
+}
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
new file mode 100644
index 000000000000..82d2410f4205
--- /dev/null
+++ b/drivers/iommu/amd_iommu_init.c
@@ -0,0 +1,1574 @@
+/*
+ * Copyright (C) 2007-2010 Advanced Micro Devices, Inc.
+ * Author: Joerg Roedel <joerg.roedel@amd.com>
+ * Leo Duran <leo.duran@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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/acpi.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <linux/interrupt.h>
+#include <linux/msi.h>
+#include <linux/amd-iommu.h>
+#include <asm/pci-direct.h>
+#include <asm/iommu.h>
+#include <asm/gart.h>
+#include <asm/x86_init.h>
+#include <asm/iommu_table.h>
+
+#include "amd_iommu_proto.h"
+#include "amd_iommu_types.h"
+
+/*
+ * definitions for the ACPI scanning code
+ */
+#define IVRS_HEADER_LENGTH 48
+
+#define ACPI_IVHD_TYPE 0x10
+#define ACPI_IVMD_TYPE_ALL 0x20
+#define ACPI_IVMD_TYPE 0x21
+#define ACPI_IVMD_TYPE_RANGE 0x22
+
+#define IVHD_DEV_ALL 0x01
+#define IVHD_DEV_SELECT 0x02
+#define IVHD_DEV_SELECT_RANGE_START 0x03
+#define IVHD_DEV_RANGE_END 0x04
+#define IVHD_DEV_ALIAS 0x42
+#define IVHD_DEV_ALIAS_RANGE 0x43
+#define IVHD_DEV_EXT_SELECT 0x46
+#define IVHD_DEV_EXT_SELECT_RANGE 0x47
+
+#define IVHD_FLAG_HT_TUN_EN_MASK 0x01
+#define IVHD_FLAG_PASSPW_EN_MASK 0x02
+#define IVHD_FLAG_RESPASSPW_EN_MASK 0x04
+#define IVHD_FLAG_ISOC_EN_MASK 0x08
+
+#define IVMD_FLAG_EXCL_RANGE 0x08
+#define IVMD_FLAG_UNITY_MAP 0x01
+
+#define ACPI_DEVFLAG_INITPASS 0x01
+#define ACPI_DEVFLAG_EXTINT 0x02
+#define ACPI_DEVFLAG_NMI 0x04
+#define ACPI_DEVFLAG_SYSMGT1 0x10
+#define ACPI_DEVFLAG_SYSMGT2 0x20
+#define ACPI_DEVFLAG_LINT0 0x40
+#define ACPI_DEVFLAG_LINT1 0x80
+#define ACPI_DEVFLAG_ATSDIS 0x10000000
+
+/*
+ * ACPI table definitions
+ *
+ * These data structures are laid over the table to parse the important values
+ * out of it.
+ */
+
+/*
+ * structure describing one IOMMU in the ACPI table. Typically followed by one
+ * or more ivhd_entrys.
+ */
+struct ivhd_header {
+ u8 type;
+ u8 flags;
+ u16 length;
+ u16 devid;
+ u16 cap_ptr;
+ u64 mmio_phys;
+ u16 pci_seg;
+ u16 info;
+ u32 reserved;
+} __attribute__((packed));
+
+/*
+ * A device entry describing which devices a specific IOMMU translates and
+ * which requestor ids they use.
+ */
+struct ivhd_entry {
+ u8 type;
+ u16 devid;
+ u8 flags;
+ u32 ext;
+} __attribute__((packed));
+
+/*
+ * An AMD IOMMU memory definition structure. It defines things like exclusion
+ * ranges for devices and regions that should be unity mapped.
+ */
+struct ivmd_header {
+ u8 type;
+ u8 flags;
+ u16 length;
+ u16 devid;
+ u16 aux;
+ u64 resv;
+ u64 range_start;
+ u64 range_length;
+} __attribute__((packed));
+
+bool amd_iommu_dump;
+
+static int __initdata amd_iommu_detected;
+static bool __initdata amd_iommu_disabled;
+
+u16 amd_iommu_last_bdf; /* largest PCI device id we have
+ to handle */
+LIST_HEAD(amd_iommu_unity_map); /* a list of required unity mappings
+ we find in ACPI */
+bool amd_iommu_unmap_flush; /* if true, flush on every unmap */
+
+LIST_HEAD(amd_iommu_list); /* list of all AMD IOMMUs in the
+ system */
+
+/* Array to assign indices to IOMMUs*/
+struct amd_iommu *amd_iommus[MAX_IOMMUS];
+int amd_iommus_present;
+
+/* IOMMUs have a non-present cache? */
+bool amd_iommu_np_cache __read_mostly;
+bool amd_iommu_iotlb_sup __read_mostly = true;
+
+/*
+ * The ACPI table parsing functions set this variable on an error
+ */
+static int __initdata amd_iommu_init_err;
+
+/*
+ * List of protection domains - used during resume
+ */
+LIST_HEAD(amd_iommu_pd_list);
+spinlock_t amd_iommu_pd_lock;
+
+/*
+ * Pointer to the device table which is shared by all AMD IOMMUs
+ * it is indexed by the PCI device id or the HT unit id and contains
+ * information about the domain the device belongs to as well as the
+ * page table root pointer.
+ */
+struct dev_table_entry *amd_iommu_dev_table;
+
+/*
+ * The alias table is a driver specific data structure which contains the
+ * mappings of the PCI device ids to the actual requestor ids on the IOMMU.
+ * More than one device can share the same requestor id.
+ */
+u16 *amd_iommu_alias_table;
+
+/*
+ * The rlookup table is used to find the IOMMU which is responsible
+ * for a specific device. It is also indexed by the PCI device id.
+ */
+struct amd_iommu **amd_iommu_rlookup_table;
+
+/*
+ * AMD IOMMU allows up to 2^16 differend protection domains. This is a bitmap
+ * to know which ones are already in use.
+ */
+unsigned long *amd_iommu_pd_alloc_bitmap;
+
+static u32 dev_table_size; /* size of the device table */
+static u32 alias_table_size; /* size of the alias table */
+static u32 rlookup_table_size; /* size if the rlookup table */
+
+/*
+ * This function flushes all internal caches of
+ * the IOMMU used by this driver.
+ */
+extern void iommu_flush_all_caches(struct amd_iommu *iommu);
+
+static inline void update_last_devid(u16 devid)
+{
+ if (devid > amd_iommu_last_bdf)
+ amd_iommu_last_bdf = devid;
+}
+
+static inline unsigned long tbl_size(int entry_size)
+{
+ unsigned shift = PAGE_SHIFT +
+ get_order(((int)amd_iommu_last_bdf + 1) * entry_size);
+
+ return 1UL << shift;
+}
+
+/* Access to l1 and l2 indexed register spaces */
+
+static u32 iommu_read_l1(struct amd_iommu *iommu, u16 l1, u8 address)
+{
+ u32 val;
+
+ pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16));
+ pci_read_config_dword(iommu->dev, 0xfc, &val);
+ return val;
+}
+
+static void iommu_write_l1(struct amd_iommu *iommu, u16 l1, u8 address, u32 val)
+{
+ pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16 | 1 << 31));
+ pci_write_config_dword(iommu->dev, 0xfc, val);
+ pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16));
+}
+
+static u32 iommu_read_l2(struct amd_iommu *iommu, u8 address)
+{
+ u32 val;
+
+ pci_write_config_dword(iommu->dev, 0xf0, address);
+ pci_read_config_dword(iommu->dev, 0xf4, &val);
+ return val;
+}
+
+static void iommu_write_l2(struct amd_iommu *iommu, u8 address, u32 val)
+{
+ pci_write_config_dword(iommu->dev, 0xf0, (address | 1 << 8));
+ pci_write_config_dword(iommu->dev, 0xf4, val);
+}
+
+/****************************************************************************
+ *
+ * AMD IOMMU MMIO register space handling functions
+ *
+ * These functions are used to program the IOMMU device registers in
+ * MMIO space required for that driver.
+ *
+ ****************************************************************************/
+
+/*
+ * This function set the exclusion range in the IOMMU. DMA accesses to the
+ * exclusion range are passed through untranslated
+ */
+static void iommu_set_exclusion_range(struct amd_iommu *iommu)
+{
+ u64 start = iommu->exclusion_start & PAGE_MASK;
+ u64 limit = (start + iommu->exclusion_length) & PAGE_MASK;
+ u64 entry;
+
+ if (!iommu->exclusion_start)
+ return;
+
+ entry = start | MMIO_EXCL_ENABLE_MASK;
+ memcpy_toio(iommu->mmio_base + MMIO_EXCL_BASE_OFFSET,
+ &entry, sizeof(entry));
+
+ entry = limit;
+ memcpy_toio(iommu->mmio_base + MMIO_EXCL_LIMIT_OFFSET,
+ &entry, sizeof(entry));
+}
+
+/* Programs the physical address of the device table into the IOMMU hardware */
+static void __init iommu_set_device_table(struct amd_iommu *iommu)
+{
+ u64 entry;
+
+ BUG_ON(iommu->mmio_base == NULL);
+
+ entry = virt_to_phys(amd_iommu_dev_table);
+ entry |= (dev_table_size >> 12) - 1;
+ memcpy_toio(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET,
+ &entry, sizeof(entry));
+}
+
+/* Generic functions to enable/disable certain features of the IOMMU. */
+static void iommu_feature_enable(struct amd_iommu *iommu, u8 bit)
+{
+ u32 ctrl;
+
+ ctrl = readl(iommu->mmio_base + MMIO_CONTROL_OFFSET);
+ ctrl |= (1 << bit);
+ writel(ctrl, iommu->mmio_base + MMIO_CONTROL_OFFSET);
+}
+
+static void iommu_feature_disable(struct amd_iommu *iommu, u8 bit)
+{
+ u32 ctrl;
+
+ ctrl = readl(iommu->mmio_base + MMIO_CONTROL_OFFSET);
+ ctrl &= ~(1 << bit);
+ writel(ctrl, iommu->mmio_base + MMIO_CONTROL_OFFSET);
+}
+
+/* Function to enable the hardware */
+static void iommu_enable(struct amd_iommu *iommu)
+{
+ static const char * const feat_str[] = {
+ "PreF", "PPR", "X2APIC", "NX", "GT", "[5]",
+ "IA", "GA", "HE", "PC", NULL
+ };
+ int i;
+
+ printk(KERN_INFO "AMD-Vi: Enabling IOMMU at %s cap 0x%hx",
+ dev_name(&iommu->dev->dev), iommu->cap_ptr);
+
+ if (iommu->cap & (1 << IOMMU_CAP_EFR)) {
+ printk(KERN_CONT " extended features: ");
+ for (i = 0; feat_str[i]; ++i)
+ if (iommu_feature(iommu, (1ULL << i)))
+ printk(KERN_CONT " %s", feat_str[i]);
+ }
+ printk(KERN_CONT "\n");
+
+ iommu_feature_enable(iommu, CONTROL_IOMMU_EN);
+}
+
+static void iommu_disable(struct amd_iommu *iommu)
+{
+ /* Disable command buffer */
+ iommu_feature_disable(iommu, CONTROL_CMDBUF_EN);
+
+ /* Disable event logging and event interrupts */
+ iommu_feature_disable(iommu, CONTROL_EVT_INT_EN);
+ iommu_feature_disable(iommu, CONTROL_EVT_LOG_EN);
+
+ /* Disable IOMMU hardware itself */
+ iommu_feature_disable(iommu, CONTROL_IOMMU_EN);
+}
+
+/*
+ * mapping and unmapping functions for the IOMMU MMIO space. Each AMD IOMMU in
+ * the system has one.
+ */
+static u8 * __init iommu_map_mmio_space(u64 address)
+{
+ u8 *ret;
+
+ if (!request_mem_region(address, MMIO_REGION_LENGTH, "amd_iommu")) {
+ pr_err("AMD-Vi: Can not reserve memory region %llx for mmio\n",
+ address);
+ pr_err("AMD-Vi: This is a BIOS bug. Please contact your hardware vendor\n");
+ return NULL;
+ }
+
+ ret = ioremap_nocache(address, MMIO_REGION_LENGTH);
+ if (ret != NULL)
+ return ret;
+
+ release_mem_region(address, MMIO_REGION_LENGTH);
+
+ return NULL;
+}
+
+static void __init iommu_unmap_mmio_space(struct amd_iommu *iommu)
+{
+ if (iommu->mmio_base)
+ iounmap(iommu->mmio_base);
+ release_mem_region(iommu->mmio_phys, MMIO_REGION_LENGTH);
+}
+
+/****************************************************************************
+ *
+ * The functions below belong to the first pass of AMD IOMMU ACPI table
+ * parsing. In this pass we try to find out the highest device id this
+ * code has to handle. Upon this information the size of the shared data
+ * structures is determined later.
+ *
+ ****************************************************************************/
+
+/*
+ * This function calculates the length of a given IVHD entry
+ */
+static inline int ivhd_entry_length(u8 *ivhd)
+{
+ return 0x04 << (*ivhd >> 6);
+}
+
+/*
+ * This function reads the last device id the IOMMU has to handle from the PCI
+ * capability header for this IOMMU
+ */
+static int __init find_last_devid_on_pci(int bus, int dev, int fn, int cap_ptr)
+{
+ u32 cap;
+
+ cap = read_pci_config(bus, dev, fn, cap_ptr+MMIO_RANGE_OFFSET);
+ update_last_devid(calc_devid(MMIO_GET_BUS(cap), MMIO_GET_LD(cap)));
+
+ return 0;
+}
+
+/*
+ * After reading the highest device id from the IOMMU PCI capability header
+ * this function looks if there is a higher device id defined in the ACPI table
+ */
+static int __init find_last_devid_from_ivhd(struct ivhd_header *h)
+{
+ u8 *p = (void *)h, *end = (void *)h;
+ struct ivhd_entry *dev;
+
+ p += sizeof(*h);
+ end += h->length;
+
+ find_last_devid_on_pci(PCI_BUS(h->devid),
+ PCI_SLOT(h->devid),
+ PCI_FUNC(h->devid),
+ h->cap_ptr);
+
+ while (p < end) {
+ dev = (struct ivhd_entry *)p;
+ switch (dev->type) {
+ case IVHD_DEV_SELECT:
+ case IVHD_DEV_RANGE_END:
+ case IVHD_DEV_ALIAS:
+ case IVHD_DEV_EXT_SELECT:
+ /* all the above subfield types refer to device ids */
+ update_last_devid(dev->devid);
+ break;
+ default:
+ break;
+ }
+ p += ivhd_entry_length(p);
+ }
+
+ WARN_ON(p != end);
+
+ return 0;
+}
+
+/*
+ * Iterate over all IVHD entries in the ACPI table and find the highest device
+ * id which we need to handle. This is the first of three functions which parse
+ * the ACPI table. So we check the checksum here.
+ */
+static int __init find_last_devid_acpi(struct acpi_table_header *table)
+{
+ int i;
+ u8 checksum = 0, *p = (u8 *)table, *end = (u8 *)table;
+ struct ivhd_header *h;
+
+ /*
+ * Validate checksum here so we don't need to do it when
+ * we actually parse the table
+ */
+ for (i = 0; i < table->length; ++i)
+ checksum += p[i];
+ if (checksum != 0) {
+ /* ACPI table corrupt */
+ amd_iommu_init_err = -ENODEV;
+ return 0;
+ }
+
+ p += IVRS_HEADER_LENGTH;
+
+ end += table->length;
+ while (p < end) {
+ h = (struct ivhd_header *)p;
+ switch (h->type) {
+ case ACPI_IVHD_TYPE:
+ find_last_devid_from_ivhd(h);
+ break;
+ default:
+ break;
+ }
+ p += h->length;
+ }
+ WARN_ON(p != end);
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * The following functions belong the the code path which parses the ACPI table
+ * the second time. In this ACPI parsing iteration we allocate IOMMU specific
+ * data structures, initialize the device/alias/rlookup table and also
+ * basically initialize the hardware.
+ *
+ ****************************************************************************/
+
+/*
+ * Allocates the command buffer. This buffer is per AMD IOMMU. We can
+ * write commands to that buffer later and the IOMMU will execute them
+ * asynchronously
+ */
+static u8 * __init alloc_command_buffer(struct amd_iommu *iommu)
+{
+ u8 *cmd_buf = (u8 *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ get_order(CMD_BUFFER_SIZE));
+
+ if (cmd_buf == NULL)
+ return NULL;
+
+ iommu->cmd_buf_size = CMD_BUFFER_SIZE | CMD_BUFFER_UNINITIALIZED;
+
+ return cmd_buf;
+}
+
+/*
+ * This function resets the command buffer if the IOMMU stopped fetching
+ * commands from it.
+ */
+void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu)
+{
+ iommu_feature_disable(iommu, CONTROL_CMDBUF_EN);
+
+ writel(0x00, iommu->mmio_base + MMIO_CMD_HEAD_OFFSET);
+ writel(0x00, iommu->mmio_base + MMIO_CMD_TAIL_OFFSET);
+
+ iommu_feature_enable(iommu, CONTROL_CMDBUF_EN);
+}
+
+/*
+ * This function writes the command buffer address to the hardware and
+ * enables it.
+ */
+static void iommu_enable_command_buffer(struct amd_iommu *iommu)
+{
+ u64 entry;
+
+ BUG_ON(iommu->cmd_buf == NULL);
+
+ entry = (u64)virt_to_phys(iommu->cmd_buf);
+ entry |= MMIO_CMD_SIZE_512;
+
+ memcpy_toio(iommu->mmio_base + MMIO_CMD_BUF_OFFSET,
+ &entry, sizeof(entry));
+
+ amd_iommu_reset_cmd_buffer(iommu);
+ iommu->cmd_buf_size &= ~(CMD_BUFFER_UNINITIALIZED);
+}
+
+static void __init free_command_buffer(struct amd_iommu *iommu)
+{
+ free_pages((unsigned long)iommu->cmd_buf,
+ get_order(iommu->cmd_buf_size & ~(CMD_BUFFER_UNINITIALIZED)));
+}
+
+/* allocates the memory where the IOMMU will log its events to */
+static u8 * __init alloc_event_buffer(struct amd_iommu *iommu)
+{
+ iommu->evt_buf = (u8 *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ get_order(EVT_BUFFER_SIZE));
+
+ if (iommu->evt_buf == NULL)
+ return NULL;
+
+ iommu->evt_buf_size = EVT_BUFFER_SIZE;
+
+ return iommu->evt_buf;
+}
+
+static void iommu_enable_event_buffer(struct amd_iommu *iommu)
+{
+ u64 entry;
+
+ BUG_ON(iommu->evt_buf == NULL);
+
+ entry = (u64)virt_to_phys(iommu->evt_buf) | EVT_LEN_MASK;
+
+ memcpy_toio(iommu->mmio_base + MMIO_EVT_BUF_OFFSET,
+ &entry, sizeof(entry));
+
+ /* set head and tail to zero manually */
+ writel(0x00, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
+ writel(0x00, iommu->mmio_base + MMIO_EVT_TAIL_OFFSET);
+
+ iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN);
+}
+
+static void __init free_event_buffer(struct amd_iommu *iommu)
+{
+ free_pages((unsigned long)iommu->evt_buf, get_order(EVT_BUFFER_SIZE));
+}
+
+/* sets a specific bit in the device table entry. */
+static void set_dev_entry_bit(u16 devid, u8 bit)
+{
+ int i = (bit >> 5) & 0x07;
+ int _bit = bit & 0x1f;
+
+ amd_iommu_dev_table[devid].data[i] |= (1 << _bit);
+}
+
+static int get_dev_entry_bit(u16 devid, u8 bit)
+{
+ int i = (bit >> 5) & 0x07;
+ int _bit = bit & 0x1f;
+
+ return (amd_iommu_dev_table[devid].data[i] & (1 << _bit)) >> _bit;
+}
+
+
+void amd_iommu_apply_erratum_63(u16 devid)
+{
+ int sysmgt;
+
+ sysmgt = get_dev_entry_bit(devid, DEV_ENTRY_SYSMGT1) |
+ (get_dev_entry_bit(devid, DEV_ENTRY_SYSMGT2) << 1);
+
+ if (sysmgt == 0x01)
+ set_dev_entry_bit(devid, DEV_ENTRY_IW);
+}
+
+/* Writes the specific IOMMU for a device into the rlookup table */
+static void __init set_iommu_for_device(struct amd_iommu *iommu, u16 devid)
+{
+ amd_iommu_rlookup_table[devid] = iommu;
+}
+
+/*
+ * This function takes the device specific flags read from the ACPI
+ * table and sets up the device table entry with that information
+ */
+static void __init set_dev_entry_from_acpi(struct amd_iommu *iommu,
+ u16 devid, u32 flags, u32 ext_flags)
+{
+ if (flags & ACPI_DEVFLAG_INITPASS)
+ set_dev_entry_bit(devid, DEV_ENTRY_INIT_PASS);
+ if (flags & ACPI_DEVFLAG_EXTINT)
+ set_dev_entry_bit(devid, DEV_ENTRY_EINT_PASS);
+ if (flags & ACPI_DEVFLAG_NMI)
+ set_dev_entry_bit(devid, DEV_ENTRY_NMI_PASS);
+ if (flags & ACPI_DEVFLAG_SYSMGT1)
+ set_dev_entry_bit(devid, DEV_ENTRY_SYSMGT1);
+ if (flags & ACPI_DEVFLAG_SYSMGT2)
+ set_dev_entry_bit(devid, DEV_ENTRY_SYSMGT2);
+ if (flags & ACPI_DEVFLAG_LINT0)
+ set_dev_entry_bit(devid, DEV_ENTRY_LINT0_PASS);
+ if (flags & ACPI_DEVFLAG_LINT1)
+ set_dev_entry_bit(devid, DEV_ENTRY_LINT1_PASS);
+
+ amd_iommu_apply_erratum_63(devid);
+
+ set_iommu_for_device(iommu, devid);
+}
+
+/*
+ * Reads the device exclusion range from ACPI and initialize IOMMU with
+ * it
+ */
+static void __init set_device_exclusion_range(u16 devid, struct ivmd_header *m)
+{
+ struct amd_iommu *iommu = amd_iommu_rlookup_table[devid];
+
+ if (!(m->flags & IVMD_FLAG_EXCL_RANGE))
+ return;
+
+ if (iommu) {
+ /*
+ * We only can configure exclusion ranges per IOMMU, not
+ * per device. But we can enable the exclusion range per
+ * device. This is done here
+ */
+ set_dev_entry_bit(m->devid, DEV_ENTRY_EX);
+ iommu->exclusion_start = m->range_start;
+ iommu->exclusion_length = m->range_length;
+ }
+}
+
+/*
+ * This function reads some important data from the IOMMU PCI space and
+ * initializes the driver data structure with it. It reads the hardware
+ * capabilities and the first/last device entries
+ */
+static void __init init_iommu_from_pci(struct amd_iommu *iommu)
+{
+ int cap_ptr = iommu->cap_ptr;
+ u32 range, misc, low, high;
+ int i, j;
+
+ pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET,
+ &iommu->cap);
+ pci_read_config_dword(iommu->dev, cap_ptr + MMIO_RANGE_OFFSET,
+ &range);
+ pci_read_config_dword(iommu->dev, cap_ptr + MMIO_MISC_OFFSET,
+ &misc);
+
+ iommu->first_device = calc_devid(MMIO_GET_BUS(range),
+ MMIO_GET_FD(range));
+ iommu->last_device = calc_devid(MMIO_GET_BUS(range),
+ MMIO_GET_LD(range));
+ iommu->evt_msi_num = MMIO_MSI_NUM(misc);
+
+ if (!(iommu->cap & (1 << IOMMU_CAP_IOTLB)))
+ amd_iommu_iotlb_sup = false;
+
+ /* read extended feature bits */
+ low = readl(iommu->mmio_base + MMIO_EXT_FEATURES);
+ high = readl(iommu->mmio_base + MMIO_EXT_FEATURES + 4);
+
+ iommu->features = ((u64)high << 32) | low;
+
+ if (!is_rd890_iommu(iommu->dev))
+ return;
+
+ /*
+ * Some rd890 systems may not be fully reconfigured by the BIOS, so
+ * it's necessary for us to store this information so it can be
+ * reprogrammed on resume
+ */
+
+ pci_read_config_dword(iommu->dev, iommu->cap_ptr + 4,
+ &iommu->stored_addr_lo);
+ pci_read_config_dword(iommu->dev, iommu->cap_ptr + 8,
+ &iommu->stored_addr_hi);
+
+ /* Low bit locks writes to configuration space */
+ iommu->stored_addr_lo &= ~1;
+
+ for (i = 0; i < 6; i++)
+ for (j = 0; j < 0x12; j++)
+ iommu->stored_l1[i][j] = iommu_read_l1(iommu, i, j);
+
+ for (i = 0; i < 0x83; i++)
+ iommu->stored_l2[i] = iommu_read_l2(iommu, i);
+}
+
+/*
+ * Takes a pointer to an AMD IOMMU entry in the ACPI table and
+ * initializes the hardware and our data structures with it.
+ */
+static void __init init_iommu_from_acpi(struct amd_iommu *iommu,
+ struct ivhd_header *h)
+{
+ u8 *p = (u8 *)h;
+ u8 *end = p, flags = 0;
+ u16 devid = 0, devid_start = 0, devid_to = 0;
+ u32 dev_i, ext_flags = 0;
+ bool alias = false;
+ struct ivhd_entry *e;
+
+ /*
+ * First save the recommended feature enable bits from ACPI
+ */
+ iommu->acpi_flags = h->flags;
+
+ /*
+ * Done. Now parse the device entries
+ */
+ p += sizeof(struct ivhd_header);
+ end += h->length;
+
+
+ while (p < end) {
+ e = (struct ivhd_entry *)p;
+ switch (e->type) {
+ case IVHD_DEV_ALL:
+
+ DUMP_printk(" DEV_ALL\t\t\t first devid: %02x:%02x.%x"
+ " last device %02x:%02x.%x flags: %02x\n",
+ PCI_BUS(iommu->first_device),
+ PCI_SLOT(iommu->first_device),
+ PCI_FUNC(iommu->first_device),
+ PCI_BUS(iommu->last_device),
+ PCI_SLOT(iommu->last_device),
+ PCI_FUNC(iommu->last_device),
+ e->flags);
+
+ for (dev_i = iommu->first_device;
+ dev_i <= iommu->last_device; ++dev_i)
+ set_dev_entry_from_acpi(iommu, dev_i,
+ e->flags, 0);
+ break;
+ case IVHD_DEV_SELECT:
+
+ DUMP_printk(" DEV_SELECT\t\t\t devid: %02x:%02x.%x "
+ "flags: %02x\n",
+ PCI_BUS(e->devid),
+ PCI_SLOT(e->devid),
+ PCI_FUNC(e->devid),
+ e->flags);
+
+ devid = e->devid;
+ set_dev_entry_from_acpi(iommu, devid, e->flags, 0);
+ break;
+ case IVHD_DEV_SELECT_RANGE_START:
+
+ DUMP_printk(" DEV_SELECT_RANGE_START\t "
+ "devid: %02x:%02x.%x flags: %02x\n",
+ PCI_BUS(e->devid),
+ PCI_SLOT(e->devid),
+ PCI_FUNC(e->devid),
+ e->flags);
+
+ devid_start = e->devid;
+ flags = e->flags;
+ ext_flags = 0;
+ alias = false;
+ break;
+ case IVHD_DEV_ALIAS:
+
+ DUMP_printk(" DEV_ALIAS\t\t\t devid: %02x:%02x.%x "
+ "flags: %02x devid_to: %02x:%02x.%x\n",
+ PCI_BUS(e->devid),
+ PCI_SLOT(e->devid),
+ PCI_FUNC(e->devid),
+ e->flags,
+ PCI_BUS(e->ext >> 8),
+ PCI_SLOT(e->ext >> 8),
+ PCI_FUNC(e->ext >> 8));
+
+ devid = e->devid;
+ devid_to = e->ext >> 8;
+ set_dev_entry_from_acpi(iommu, devid , e->flags, 0);
+ set_dev_entry_from_acpi(iommu, devid_to, e->flags, 0);
+ amd_iommu_alias_table[devid] = devid_to;
+ break;
+ case IVHD_DEV_ALIAS_RANGE:
+
+ DUMP_printk(" DEV_ALIAS_RANGE\t\t "
+ "devid: %02x:%02x.%x flags: %02x "
+ "devid_to: %02x:%02x.%x\n",
+ PCI_BUS(e->devid),
+ PCI_SLOT(e->devid),
+ PCI_FUNC(e->devid),
+ e->flags,
+ PCI_BUS(e->ext >> 8),
+ PCI_SLOT(e->ext >> 8),
+ PCI_FUNC(e->ext >> 8));
+
+ devid_start = e->devid;
+ flags = e->flags;
+ devid_to = e->ext >> 8;
+ ext_flags = 0;
+ alias = true;
+ break;
+ case IVHD_DEV_EXT_SELECT:
+
+ DUMP_printk(" DEV_EXT_SELECT\t\t devid: %02x:%02x.%x "
+ "flags: %02x ext: %08x\n",
+ PCI_BUS(e->devid),
+ PCI_SLOT(e->devid),
+ PCI_FUNC(e->devid),
+ e->flags, e->ext);
+
+ devid = e->devid;
+ set_dev_entry_from_acpi(iommu, devid, e->flags,
+ e->ext);
+ break;
+ case IVHD_DEV_EXT_SELECT_RANGE:
+
+ DUMP_printk(" DEV_EXT_SELECT_RANGE\t devid: "
+ "%02x:%02x.%x flags: %02x ext: %08x\n",
+ PCI_BUS(e->devid),
+ PCI_SLOT(e->devid),
+ PCI_FUNC(e->devid),
+ e->flags, e->ext);
+
+ devid_start = e->devid;
+ flags = e->flags;
+ ext_flags = e->ext;
+ alias = false;
+ break;
+ case IVHD_DEV_RANGE_END:
+
+ DUMP_printk(" DEV_RANGE_END\t\t devid: %02x:%02x.%x\n",
+ PCI_BUS(e->devid),
+ PCI_SLOT(e->devid),
+ PCI_FUNC(e->devid));
+
+ devid = e->devid;
+ for (dev_i = devid_start; dev_i <= devid; ++dev_i) {
+ if (alias) {
+ amd_iommu_alias_table[dev_i] = devid_to;
+ set_dev_entry_from_acpi(iommu,
+ devid_to, flags, ext_flags);
+ }
+ set_dev_entry_from_acpi(iommu, dev_i,
+ flags, ext_flags);
+ }
+ break;
+ default:
+ break;
+ }
+
+ p += ivhd_entry_length(p);
+ }
+}
+
+/* Initializes the device->iommu mapping for the driver */
+static int __init init_iommu_devices(struct amd_iommu *iommu)
+{
+ u32 i;
+
+ for (i = iommu->first_device; i <= iommu->last_device; ++i)
+ set_iommu_for_device(iommu, i);
+
+ return 0;
+}
+
+static void __init free_iommu_one(struct amd_iommu *iommu)
+{
+ free_command_buffer(iommu);
+ free_event_buffer(iommu);
+ iommu_unmap_mmio_space(iommu);
+}
+
+static void __init free_iommu_all(void)
+{
+ struct amd_iommu *iommu, *next;
+
+ for_each_iommu_safe(iommu, next) {
+ list_del(&iommu->list);
+ free_iommu_one(iommu);
+ kfree(iommu);
+ }
+}
+
+/*
+ * This function clues the initialization function for one IOMMU
+ * together and also allocates the command buffer and programs the
+ * hardware. It does NOT enable the IOMMU. This is done afterwards.
+ */
+static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
+{
+ spin_lock_init(&iommu->lock);
+
+ /* Add IOMMU to internal data structures */
+ list_add_tail(&iommu->list, &amd_iommu_list);
+ iommu->index = amd_iommus_present++;
+
+ if (unlikely(iommu->index >= MAX_IOMMUS)) {
+ WARN(1, "AMD-Vi: System has more IOMMUs than supported by this driver\n");
+ return -ENOSYS;
+ }
+
+ /* Index is fine - add IOMMU to the array */
+ amd_iommus[iommu->index] = iommu;
+
+ /*
+ * Copy data from ACPI table entry to the iommu struct
+ */
+ iommu->dev = pci_get_bus_and_slot(PCI_BUS(h->devid), h->devid & 0xff);
+ if (!iommu->dev)
+ return 1;
+
+ iommu->cap_ptr = h->cap_ptr;
+ iommu->pci_seg = h->pci_seg;
+ iommu->mmio_phys = h->mmio_phys;
+ iommu->mmio_base = iommu_map_mmio_space(h->mmio_phys);
+ if (!iommu->mmio_base)
+ return -ENOMEM;
+
+ iommu->cmd_buf = alloc_command_buffer(iommu);
+ if (!iommu->cmd_buf)
+ return -ENOMEM;
+
+ iommu->evt_buf = alloc_event_buffer(iommu);
+ if (!iommu->evt_buf)
+ return -ENOMEM;
+
+ iommu->int_enabled = false;
+
+ init_iommu_from_pci(iommu);
+ init_iommu_from_acpi(iommu, h);
+ init_iommu_devices(iommu);
+
+ if (iommu->cap & (1UL << IOMMU_CAP_NPCACHE))
+ amd_iommu_np_cache = true;
+
+ return pci_enable_device(iommu->dev);
+}
+
+/*
+ * Iterates over all IOMMU entries in the ACPI table, allocates the
+ * IOMMU structure and initializes it with init_iommu_one()
+ */
+static int __init init_iommu_all(struct acpi_table_header *table)
+{
+ u8 *p = (u8 *)table, *end = (u8 *)table;
+ struct ivhd_header *h;
+ struct amd_iommu *iommu;
+ int ret;
+
+ end += table->length;
+ p += IVRS_HEADER_LENGTH;
+
+ while (p < end) {
+ h = (struct ivhd_header *)p;
+ switch (*p) {
+ case ACPI_IVHD_TYPE:
+
+ DUMP_printk("device: %02x:%02x.%01x cap: %04x "
+ "seg: %d flags: %01x info %04x\n",
+ PCI_BUS(h->devid), PCI_SLOT(h->devid),
+ PCI_FUNC(h->devid), h->cap_ptr,
+ h->pci_seg, h->flags, h->info);
+ DUMP_printk(" mmio-addr: %016llx\n",
+ h->mmio_phys);
+
+ iommu = kzalloc(sizeof(struct amd_iommu), GFP_KERNEL);
+ if (iommu == NULL) {
+ amd_iommu_init_err = -ENOMEM;
+ return 0;
+ }
+
+ ret = init_iommu_one(iommu, h);
+ if (ret) {
+ amd_iommu_init_err = ret;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ p += h->length;
+
+ }
+ WARN_ON(p != end);
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * The following functions initialize the MSI interrupts for all IOMMUs
+ * in the system. Its a bit challenging because there could be multiple
+ * IOMMUs per PCI BDF but we can call pci_enable_msi(x) only once per
+ * pci_dev.
+ *
+ ****************************************************************************/
+
+static int iommu_setup_msi(struct amd_iommu *iommu)
+{
+ int r;
+
+ if (pci_enable_msi(iommu->dev))
+ return 1;
+
+ r = request_threaded_irq(iommu->dev->irq,
+ amd_iommu_int_handler,
+ amd_iommu_int_thread,
+ 0, "AMD-Vi",
+ iommu->dev);
+
+ if (r) {
+ pci_disable_msi(iommu->dev);
+ return 1;
+ }
+
+ iommu->int_enabled = true;
+ iommu_feature_enable(iommu, CONTROL_EVT_INT_EN);
+
+ return 0;
+}
+
+static int iommu_init_msi(struct amd_iommu *iommu)
+{
+ if (iommu->int_enabled)
+ return 0;
+
+ if (pci_find_capability(iommu->dev, PCI_CAP_ID_MSI))
+ return iommu_setup_msi(iommu);
+
+ return 1;
+}
+
+/****************************************************************************
+ *
+ * The next functions belong to the third pass of parsing the ACPI
+ * table. In this last pass the memory mapping requirements are
+ * gathered (like exclusion and unity mapping reanges).
+ *
+ ****************************************************************************/
+
+static void __init free_unity_maps(void)
+{
+ struct unity_map_entry *entry, *next;
+
+ list_for_each_entry_safe(entry, next, &amd_iommu_unity_map, list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+}
+
+/* called when we find an exclusion range definition in ACPI */
+static int __init init_exclusion_range(struct ivmd_header *m)
+{
+ int i;
+
+ switch (m->type) {
+ case ACPI_IVMD_TYPE:
+ set_device_exclusion_range(m->devid, m);
+ break;
+ case ACPI_IVMD_TYPE_ALL:
+ for (i = 0; i <= amd_iommu_last_bdf; ++i)
+ set_device_exclusion_range(i, m);
+ break;
+ case ACPI_IVMD_TYPE_RANGE:
+ for (i = m->devid; i <= m->aux; ++i)
+ set_device_exclusion_range(i, m);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* called for unity map ACPI definition */
+static int __init init_unity_map_range(struct ivmd_header *m)
+{
+ struct unity_map_entry *e = 0;
+ char *s;
+
+ e = kzalloc(sizeof(*e), GFP_KERNEL);
+ if (e == NULL)
+ return -ENOMEM;
+
+ switch (m->type) {
+ default:
+ kfree(e);
+ return 0;
+ case ACPI_IVMD_TYPE:
+ s = "IVMD_TYPEi\t\t\t";
+ e->devid_start = e->devid_end = m->devid;
+ break;
+ case ACPI_IVMD_TYPE_ALL:
+ s = "IVMD_TYPE_ALL\t\t";
+ e->devid_start = 0;
+ e->devid_end = amd_iommu_last_bdf;
+ break;
+ case ACPI_IVMD_TYPE_RANGE:
+ s = "IVMD_TYPE_RANGE\t\t";
+ e->devid_start = m->devid;
+ e->devid_end = m->aux;
+ break;
+ }
+ e->address_start = PAGE_ALIGN(m->range_start);
+ e->address_end = e->address_start + PAGE_ALIGN(m->range_length);
+ e->prot = m->flags >> 1;
+
+ DUMP_printk("%s devid_start: %02x:%02x.%x devid_end: %02x:%02x.%x"
+ " range_start: %016llx range_end: %016llx flags: %x\n", s,
+ PCI_BUS(e->devid_start), PCI_SLOT(e->devid_start),
+ PCI_FUNC(e->devid_start), PCI_BUS(e->devid_end),
+ PCI_SLOT(e->devid_end), PCI_FUNC(e->devid_end),
+ e->address_start, e->address_end, m->flags);
+
+ list_add_tail(&e->list, &amd_iommu_unity_map);
+
+ return 0;
+}
+
+/* iterates over all memory definitions we find in the ACPI table */
+static int __init init_memory_definitions(struct acpi_table_header *table)
+{
+ u8 *p = (u8 *)table, *end = (u8 *)table;
+ struct ivmd_header *m;
+
+ end += table->length;
+ p += IVRS_HEADER_LENGTH;
+
+ while (p < end) {
+ m = (struct ivmd_header *)p;
+ if (m->flags & IVMD_FLAG_EXCL_RANGE)
+ init_exclusion_range(m);
+ else if (m->flags & IVMD_FLAG_UNITY_MAP)
+ init_unity_map_range(m);
+
+ p += m->length;
+ }
+
+ return 0;
+}
+
+/*
+ * Init the device table to not allow DMA access for devices and
+ * suppress all page faults
+ */
+static void init_device_table(void)
+{
+ u32 devid;
+
+ for (devid = 0; devid <= amd_iommu_last_bdf; ++devid) {
+ set_dev_entry_bit(devid, DEV_ENTRY_VALID);
+ set_dev_entry_bit(devid, DEV_ENTRY_TRANSLATION);
+ }
+}
+
+static void iommu_init_flags(struct amd_iommu *iommu)
+{
+ iommu->acpi_flags & IVHD_FLAG_HT_TUN_EN_MASK ?
+ iommu_feature_enable(iommu, CONTROL_HT_TUN_EN) :
+ iommu_feature_disable(iommu, CONTROL_HT_TUN_EN);
+
+ iommu->acpi_flags & IVHD_FLAG_PASSPW_EN_MASK ?
+ iommu_feature_enable(iommu, CONTROL_PASSPW_EN) :
+ iommu_feature_disable(iommu, CONTROL_PASSPW_EN);
+
+ iommu->acpi_flags & IVHD_FLAG_RESPASSPW_EN_MASK ?
+ iommu_feature_enable(iommu, CONTROL_RESPASSPW_EN) :
+ iommu_feature_disable(iommu, CONTROL_RESPASSPW_EN);
+
+ iommu->acpi_flags & IVHD_FLAG_ISOC_EN_MASK ?
+ iommu_feature_enable(iommu, CONTROL_ISOC_EN) :
+ iommu_feature_disable(iommu, CONTROL_ISOC_EN);
+
+ /*
+ * make IOMMU memory accesses cache coherent
+ */
+ iommu_feature_enable(iommu, CONTROL_COHERENT_EN);
+}
+
+static void iommu_apply_resume_quirks(struct amd_iommu *iommu)
+{
+ int i, j;
+ u32 ioc_feature_control;
+ struct pci_dev *pdev = NULL;
+
+ /* RD890 BIOSes may not have completely reconfigured the iommu */
+ if (!is_rd890_iommu(iommu->dev))
+ return;
+
+ /*
+ * First, we need to ensure that the iommu is enabled. This is
+ * controlled by a register in the northbridge
+ */
+ pdev = pci_get_bus_and_slot(iommu->dev->bus->number, PCI_DEVFN(0, 0));
+
+ if (!pdev)
+ return;
+
+ /* Select Northbridge indirect register 0x75 and enable writing */
+ pci_write_config_dword(pdev, 0x60, 0x75 | (1 << 7));
+ pci_read_config_dword(pdev, 0x64, &ioc_feature_control);
+
+ /* Enable the iommu */
+ if (!(ioc_feature_control & 0x1))
+ pci_write_config_dword(pdev, 0x64, ioc_feature_control | 1);
+
+ pci_dev_put(pdev);
+
+ /* Restore the iommu BAR */
+ pci_write_config_dword(iommu->dev, iommu->cap_ptr + 4,
+ iommu->stored_addr_lo);
+ pci_write_config_dword(iommu->dev, iommu->cap_ptr + 8,
+ iommu->stored_addr_hi);
+
+ /* Restore the l1 indirect regs for each of the 6 l1s */
+ for (i = 0; i < 6; i++)
+ for (j = 0; j < 0x12; j++)
+ iommu_write_l1(iommu, i, j, iommu->stored_l1[i][j]);
+
+ /* Restore the l2 indirect regs */
+ for (i = 0; i < 0x83; i++)
+ iommu_write_l2(iommu, i, iommu->stored_l2[i]);
+
+ /* Lock PCI setup registers */
+ pci_write_config_dword(iommu->dev, iommu->cap_ptr + 4,
+ iommu->stored_addr_lo | 1);
+}
+
+/*
+ * This function finally enables all IOMMUs found in the system after
+ * they have been initialized
+ */
+static void enable_iommus(void)
+{
+ struct amd_iommu *iommu;
+
+ for_each_iommu(iommu) {
+ iommu_disable(iommu);
+ iommu_init_flags(iommu);
+ iommu_set_device_table(iommu);
+ iommu_enable_command_buffer(iommu);
+ iommu_enable_event_buffer(iommu);
+ iommu_set_exclusion_range(iommu);
+ iommu_init_msi(iommu);
+ iommu_enable(iommu);
+ iommu_flush_all_caches(iommu);
+ }
+}
+
+static void disable_iommus(void)
+{
+ struct amd_iommu *iommu;
+
+ for_each_iommu(iommu)
+ iommu_disable(iommu);
+}
+
+/*
+ * Suspend/Resume support
+ * disable suspend until real resume implemented
+ */
+
+static void amd_iommu_resume(void)
+{
+ struct amd_iommu *iommu;
+
+ for_each_iommu(iommu)
+ iommu_apply_resume_quirks(iommu);
+
+ /* re-load the hardware */
+ enable_iommus();
+
+ /*
+ * we have to flush after the IOMMUs are enabled because a
+ * disabled IOMMU will never execute the commands we send
+ */
+ for_each_iommu(iommu)
+ iommu_flush_all_caches(iommu);
+}
+
+static int amd_iommu_suspend(void)
+{
+ /* disable IOMMUs to go out of the way for BIOS */
+ disable_iommus();
+
+ return 0;
+}
+
+static struct syscore_ops amd_iommu_syscore_ops = {
+ .suspend = amd_iommu_suspend,
+ .resume = amd_iommu_resume,
+};
+
+/*
+ * This is the core init function for AMD IOMMU hardware in the system.
+ * This function is called from the generic x86 DMA layer initialization
+ * code.
+ *
+ * This function basically parses the ACPI table for AMD IOMMU (IVRS)
+ * three times:
+ *
+ * 1 pass) Find the highest PCI device id the driver has to handle.
+ * Upon this information the size of the data structures is
+ * determined that needs to be allocated.
+ *
+ * 2 pass) Initialize the data structures just allocated with the
+ * information in the ACPI table about available AMD IOMMUs
+ * in the system. It also maps the PCI devices in the
+ * system to specific IOMMUs
+ *
+ * 3 pass) After the basic data structures are allocated and
+ * initialized we update them with information about memory
+ * remapping requirements parsed out of the ACPI table in
+ * this last pass.
+ *
+ * After that the hardware is initialized and ready to go. In the last
+ * step we do some Linux specific things like registering the driver in
+ * the dma_ops interface and initializing the suspend/resume support
+ * functions. Finally it prints some information about AMD IOMMUs and
+ * the driver state and enables the hardware.
+ */
+static int __init amd_iommu_init(void)
+{
+ int i, ret = 0;
+
+ /*
+ * First parse ACPI tables to find the largest Bus/Dev/Func
+ * we need to handle. Upon this information the shared data
+ * structures for the IOMMUs in the system will be allocated
+ */
+ if (acpi_table_parse("IVRS", find_last_devid_acpi) != 0)
+ return -ENODEV;
+
+ ret = amd_iommu_init_err;
+ if (ret)
+ goto out;
+
+ dev_table_size = tbl_size(DEV_TABLE_ENTRY_SIZE);
+ alias_table_size = tbl_size(ALIAS_TABLE_ENTRY_SIZE);
+ rlookup_table_size = tbl_size(RLOOKUP_TABLE_ENTRY_SIZE);
+
+ ret = -ENOMEM;
+
+ /* Device table - directly used by all IOMMUs */
+ amd_iommu_dev_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ get_order(dev_table_size));
+ if (amd_iommu_dev_table == NULL)
+ goto out;
+
+ /*
+ * Alias table - map PCI Bus/Dev/Func to Bus/Dev/Func the
+ * IOMMU see for that device
+ */
+ amd_iommu_alias_table = (void *)__get_free_pages(GFP_KERNEL,
+ get_order(alias_table_size));
+ if (amd_iommu_alias_table == NULL)
+ goto free;
+
+ /* IOMMU rlookup table - find the IOMMU for a specific device */
+ amd_iommu_rlookup_table = (void *)__get_free_pages(
+ GFP_KERNEL | __GFP_ZERO,
+ get_order(rlookup_table_size));
+ if (amd_iommu_rlookup_table == NULL)
+ goto free;
+
+ amd_iommu_pd_alloc_bitmap = (void *)__get_free_pages(
+ GFP_KERNEL | __GFP_ZERO,
+ get_order(MAX_DOMAIN_ID/8));
+ if (amd_iommu_pd_alloc_bitmap == NULL)
+ goto free;
+
+ /* init the device table */
+ init_device_table();
+
+ /*
+ * let all alias entries point to itself
+ */
+ for (i = 0; i <= amd_iommu_last_bdf; ++i)
+ amd_iommu_alias_table[i] = i;
+
+ /*
+ * never allocate domain 0 because its used as the non-allocated and
+ * error value placeholder
+ */
+ amd_iommu_pd_alloc_bitmap[0] = 1;
+
+ spin_lock_init(&amd_iommu_pd_lock);
+
+ /*
+ * now the data structures are allocated and basically initialized
+ * start the real acpi table scan
+ */
+ ret = -ENODEV;
+ if (acpi_table_parse("IVRS", init_iommu_all) != 0)
+ goto free;
+
+ if (amd_iommu_init_err) {
+ ret = amd_iommu_init_err;
+ goto free;
+ }
+
+ if (acpi_table_parse("IVRS", init_memory_definitions) != 0)
+ goto free;
+
+ if (amd_iommu_init_err) {
+ ret = amd_iommu_init_err;
+ goto free;
+ }
+
+ ret = amd_iommu_init_devices();
+ if (ret)
+ goto free;
+
+ enable_iommus();
+
+ if (iommu_pass_through)
+ ret = amd_iommu_init_passthrough();
+ else
+ ret = amd_iommu_init_dma_ops();
+
+ if (ret)
+ goto free_disable;
+
+ amd_iommu_init_api();
+
+ amd_iommu_init_notifier();
+
+ register_syscore_ops(&amd_iommu_syscore_ops);
+
+ if (iommu_pass_through)
+ goto out;
+
+ if (amd_iommu_unmap_flush)
+ printk(KERN_INFO "AMD-Vi: IO/TLB flush on unmap enabled\n");
+ else
+ printk(KERN_INFO "AMD-Vi: Lazy IO/TLB flushing enabled\n");
+
+ x86_platform.iommu_shutdown = disable_iommus;
+out:
+ return ret;
+
+free_disable:
+ disable_iommus();
+
+free:
+ amd_iommu_uninit_devices();
+
+ free_pages((unsigned long)amd_iommu_pd_alloc_bitmap,
+ get_order(MAX_DOMAIN_ID/8));
+
+ free_pages((unsigned long)amd_iommu_rlookup_table,
+ get_order(rlookup_table_size));
+
+ free_pages((unsigned long)amd_iommu_alias_table,
+ get_order(alias_table_size));
+
+ free_pages((unsigned long)amd_iommu_dev_table,
+ get_order(dev_table_size));
+
+ free_iommu_all();
+
+ free_unity_maps();
+
+#ifdef CONFIG_GART_IOMMU
+ /*
+ * We failed to initialize the AMD IOMMU - try fallback to GART
+ * if possible.
+ */
+ gart_iommu_init();
+
+#endif
+
+ goto out;
+}
+
+/****************************************************************************
+ *
+ * Early detect code. This code runs at IOMMU detection time in the DMA
+ * layer. It just looks if there is an IVRS ACPI table to detect AMD
+ * IOMMUs
+ *
+ ****************************************************************************/
+static int __init early_amd_iommu_detect(struct acpi_table_header *table)
+{
+ return 0;
+}
+
+int __init amd_iommu_detect(void)
+{
+ if (no_iommu || (iommu_detected && !gart_iommu_aperture))
+ return -ENODEV;
+
+ if (amd_iommu_disabled)
+ return -ENODEV;
+
+ if (acpi_table_parse("IVRS", early_amd_iommu_detect) == 0) {
+ iommu_detected = 1;
+ amd_iommu_detected = 1;
+ x86_init.iommu.iommu_init = amd_iommu_init;
+
+ /* Make sure ACS will be enabled */
+ pci_request_acs();
+ return 1;
+ }
+ return -ENODEV;
+}
+
+/****************************************************************************
+ *
+ * Parsing functions for the AMD IOMMU specific kernel command line
+ * options.
+ *
+ ****************************************************************************/
+
+static int __init parse_amd_iommu_dump(char *str)
+{
+ amd_iommu_dump = true;
+
+ return 1;
+}
+
+static int __init parse_amd_iommu_options(char *str)
+{
+ for (; *str; ++str) {
+ if (strncmp(str, "fullflush", 9) == 0)
+ amd_iommu_unmap_flush = true;
+ if (strncmp(str, "off", 3) == 0)
+ amd_iommu_disabled = true;
+ }
+
+ return 1;
+}
+
+__setup("amd_iommu_dump", parse_amd_iommu_dump);
+__setup("amd_iommu=", parse_amd_iommu_options);
+
+IOMMU_INIT_FINISH(amd_iommu_detect,
+ gart_iommu_hole_init,
+ 0,
+ 0);
diff --git a/drivers/iommu/amd_iommu_proto.h b/drivers/iommu/amd_iommu_proto.h
new file mode 100644
index 000000000000..7ffaa64410b0
--- /dev/null
+++ b/drivers/iommu/amd_iommu_proto.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009-2010 Advanced Micro Devices, Inc.
+ * Author: Joerg Roedel <joerg.roedel@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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 _ASM_X86_AMD_IOMMU_PROTO_H
+#define _ASM_X86_AMD_IOMMU_PROTO_H
+
+#include "amd_iommu_types.h"
+
+extern int amd_iommu_init_dma_ops(void);
+extern int amd_iommu_init_passthrough(void);
+extern irqreturn_t amd_iommu_int_thread(int irq, void *data);
+extern irqreturn_t amd_iommu_int_handler(int irq, void *data);
+extern void amd_iommu_apply_erratum_63(u16 devid);
+extern void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu);
+extern int amd_iommu_init_devices(void);
+extern void amd_iommu_uninit_devices(void);
+extern void amd_iommu_init_notifier(void);
+extern void amd_iommu_init_api(void);
+#ifndef CONFIG_AMD_IOMMU_STATS
+
+static inline void amd_iommu_stats_init(void) { }
+
+#endif /* !CONFIG_AMD_IOMMU_STATS */
+
+static inline bool is_rd890_iommu(struct pci_dev *pdev)
+{
+ return (pdev->vendor == PCI_VENDOR_ID_ATI) &&
+ (pdev->device == PCI_DEVICE_ID_RD890_IOMMU);
+}
+
+static inline bool iommu_feature(struct amd_iommu *iommu, u64 f)
+{
+ if (!(iommu->cap & (1 << IOMMU_CAP_EFR)))
+ return false;
+
+ return !!(iommu->features & f);
+}
+
+#endif /* _ASM_X86_AMD_IOMMU_PROTO_H */
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
new file mode 100644
index 000000000000..5b9c5075e81a
--- /dev/null
+++ b/drivers/iommu/amd_iommu_types.h
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2007-2010 Advanced Micro Devices, Inc.
+ * Author: Joerg Roedel <joerg.roedel@amd.com>
+ * Leo Duran <leo.duran@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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 _ASM_X86_AMD_IOMMU_TYPES_H
+#define _ASM_X86_AMD_IOMMU_TYPES_H
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+/*
+ * Maximum number of IOMMUs supported
+ */
+#define MAX_IOMMUS 32
+
+/*
+ * some size calculation constants
+ */
+#define DEV_TABLE_ENTRY_SIZE 32
+#define ALIAS_TABLE_ENTRY_SIZE 2
+#define RLOOKUP_TABLE_ENTRY_SIZE (sizeof(void *))
+
+/* Length of the MMIO region for the AMD IOMMU */
+#define MMIO_REGION_LENGTH 0x4000
+
+/* Capability offsets used by the driver */
+#define MMIO_CAP_HDR_OFFSET 0x00
+#define MMIO_RANGE_OFFSET 0x0c
+#define MMIO_MISC_OFFSET 0x10
+
+/* Masks, shifts and macros to parse the device range capability */
+#define MMIO_RANGE_LD_MASK 0xff000000
+#define MMIO_RANGE_FD_MASK 0x00ff0000
+#define MMIO_RANGE_BUS_MASK 0x0000ff00
+#define MMIO_RANGE_LD_SHIFT 24
+#define MMIO_RANGE_FD_SHIFT 16
+#define MMIO_RANGE_BUS_SHIFT 8
+#define MMIO_GET_LD(x) (((x) & MMIO_RANGE_LD_MASK) >> MMIO_RANGE_LD_SHIFT)
+#define MMIO_GET_FD(x) (((x) & MMIO_RANGE_FD_MASK) >> MMIO_RANGE_FD_SHIFT)
+#define MMIO_GET_BUS(x) (((x) & MMIO_RANGE_BUS_MASK) >> MMIO_RANGE_BUS_SHIFT)
+#define MMIO_MSI_NUM(x) ((x) & 0x1f)
+
+/* Flag masks for the AMD IOMMU exclusion range */
+#define MMIO_EXCL_ENABLE_MASK 0x01ULL
+#define MMIO_EXCL_ALLOW_MASK 0x02ULL
+
+/* Used offsets into the MMIO space */
+#define MMIO_DEV_TABLE_OFFSET 0x0000
+#define MMIO_CMD_BUF_OFFSET 0x0008
+#define MMIO_EVT_BUF_OFFSET 0x0010
+#define MMIO_CONTROL_OFFSET 0x0018
+#define MMIO_EXCL_BASE_OFFSET 0x0020
+#define MMIO_EXCL_LIMIT_OFFSET 0x0028
+#define MMIO_EXT_FEATURES 0x0030
+#define MMIO_CMD_HEAD_OFFSET 0x2000
+#define MMIO_CMD_TAIL_OFFSET 0x2008
+#define MMIO_EVT_HEAD_OFFSET 0x2010
+#define MMIO_EVT_TAIL_OFFSET 0x2018
+#define MMIO_STATUS_OFFSET 0x2020
+
+
+/* Extended Feature Bits */
+#define FEATURE_PREFETCH (1ULL<<0)
+#define FEATURE_PPR (1ULL<<1)
+#define FEATURE_X2APIC (1ULL<<2)
+#define FEATURE_NX (1ULL<<3)
+#define FEATURE_GT (1ULL<<4)
+#define FEATURE_IA (1ULL<<6)
+#define FEATURE_GA (1ULL<<7)
+#define FEATURE_HE (1ULL<<8)
+#define FEATURE_PC (1ULL<<9)
+
+/* MMIO status bits */
+#define MMIO_STATUS_COM_WAIT_INT_MASK 0x04
+
+/* event logging constants */
+#define EVENT_ENTRY_SIZE 0x10
+#define EVENT_TYPE_SHIFT 28
+#define EVENT_TYPE_MASK 0xf
+#define EVENT_TYPE_ILL_DEV 0x1
+#define EVENT_TYPE_IO_FAULT 0x2
+#define EVENT_TYPE_DEV_TAB_ERR 0x3
+#define EVENT_TYPE_PAGE_TAB_ERR 0x4
+#define EVENT_TYPE_ILL_CMD 0x5
+#define EVENT_TYPE_CMD_HARD_ERR 0x6
+#define EVENT_TYPE_IOTLB_INV_TO 0x7
+#define EVENT_TYPE_INV_DEV_REQ 0x8
+#define EVENT_DEVID_MASK 0xffff
+#define EVENT_DEVID_SHIFT 0
+#define EVENT_DOMID_MASK 0xffff
+#define EVENT_DOMID_SHIFT 0
+#define EVENT_FLAGS_MASK 0xfff
+#define EVENT_FLAGS_SHIFT 0x10
+
+/* feature control bits */
+#define CONTROL_IOMMU_EN 0x00ULL
+#define CONTROL_HT_TUN_EN 0x01ULL
+#define CONTROL_EVT_LOG_EN 0x02ULL
+#define CONTROL_EVT_INT_EN 0x03ULL
+#define CONTROL_COMWAIT_EN 0x04ULL
+#define CONTROL_PASSPW_EN 0x08ULL
+#define CONTROL_RESPASSPW_EN 0x09ULL
+#define CONTROL_COHERENT_EN 0x0aULL
+#define CONTROL_ISOC_EN 0x0bULL
+#define CONTROL_CMDBUF_EN 0x0cULL
+#define CONTROL_PPFLOG_EN 0x0dULL
+#define CONTROL_PPFINT_EN 0x0eULL
+
+/* command specific defines */
+#define CMD_COMPL_WAIT 0x01
+#define CMD_INV_DEV_ENTRY 0x02
+#define CMD_INV_IOMMU_PAGES 0x03
+#define CMD_INV_IOTLB_PAGES 0x04
+#define CMD_INV_ALL 0x08
+
+#define CMD_COMPL_WAIT_STORE_MASK 0x01
+#define CMD_COMPL_WAIT_INT_MASK 0x02
+#define CMD_INV_IOMMU_PAGES_SIZE_MASK 0x01
+#define CMD_INV_IOMMU_PAGES_PDE_MASK 0x02
+
+#define CMD_INV_IOMMU_ALL_PAGES_ADDRESS 0x7fffffffffffffffULL
+
+/* macros and definitions for device table entries */
+#define DEV_ENTRY_VALID 0x00
+#define DEV_ENTRY_TRANSLATION 0x01
+#define DEV_ENTRY_IR 0x3d
+#define DEV_ENTRY_IW 0x3e
+#define DEV_ENTRY_NO_PAGE_FAULT 0x62
+#define DEV_ENTRY_EX 0x67
+#define DEV_ENTRY_SYSMGT1 0x68
+#define DEV_ENTRY_SYSMGT2 0x69
+#define DEV_ENTRY_INIT_PASS 0xb8
+#define DEV_ENTRY_EINT_PASS 0xb9
+#define DEV_ENTRY_NMI_PASS 0xba
+#define DEV_ENTRY_LINT0_PASS 0xbe
+#define DEV_ENTRY_LINT1_PASS 0xbf
+#define DEV_ENTRY_MODE_MASK 0x07
+#define DEV_ENTRY_MODE_SHIFT 0x09
+
+/* constants to configure the command buffer */
+#define CMD_BUFFER_SIZE 8192
+#define CMD_BUFFER_UNINITIALIZED 1
+#define CMD_BUFFER_ENTRIES 512
+#define MMIO_CMD_SIZE_SHIFT 56
+#define MMIO_CMD_SIZE_512 (0x9ULL << MMIO_CMD_SIZE_SHIFT)
+
+/* constants for event buffer handling */
+#define EVT_BUFFER_SIZE 8192 /* 512 entries */
+#define EVT_LEN_MASK (0x9ULL << 56)
+
+#define PAGE_MODE_NONE 0x00
+#define PAGE_MODE_1_LEVEL 0x01
+#define PAGE_MODE_2_LEVEL 0x02
+#define PAGE_MODE_3_LEVEL 0x03
+#define PAGE_MODE_4_LEVEL 0x04
+#define PAGE_MODE_5_LEVEL 0x05
+#define PAGE_MODE_6_LEVEL 0x06
+
+#define PM_LEVEL_SHIFT(x) (12 + ((x) * 9))
+#define PM_LEVEL_SIZE(x) (((x) < 6) ? \
+ ((1ULL << PM_LEVEL_SHIFT((x))) - 1): \
+ (0xffffffffffffffffULL))
+#define PM_LEVEL_INDEX(x, a) (((a) >> PM_LEVEL_SHIFT((x))) & 0x1ffULL)
+#define PM_LEVEL_ENC(x) (((x) << 9) & 0xe00ULL)
+#define PM_LEVEL_PDE(x, a) ((a) | PM_LEVEL_ENC((x)) | \
+ IOMMU_PTE_P | IOMMU_PTE_IR | IOMMU_PTE_IW)
+#define PM_PTE_LEVEL(pte) (((pte) >> 9) & 0x7ULL)
+
+#define PM_MAP_4k 0
+#define PM_ADDR_MASK 0x000ffffffffff000ULL
+#define PM_MAP_MASK(lvl) (PM_ADDR_MASK & \
+ (~((1ULL << (12 + ((lvl) * 9))) - 1)))
+#define PM_ALIGNED(lvl, addr) ((PM_MAP_MASK(lvl) & (addr)) == (addr))
+
+/*
+ * Returns the page table level to use for a given page size
+ * Pagesize is expected to be a power-of-two
+ */
+#define PAGE_SIZE_LEVEL(pagesize) \
+ ((__ffs(pagesize) - 12) / 9)
+/*
+ * Returns the number of ptes to use for a given page size
+ * Pagesize is expected to be a power-of-two
+ */
+#define PAGE_SIZE_PTE_COUNT(pagesize) \
+ (1ULL << ((__ffs(pagesize) - 12) % 9))
+
+/*
+ * Aligns a given io-virtual address to a given page size
+ * Pagesize is expected to be a power-of-two
+ */
+#define PAGE_SIZE_ALIGN(address, pagesize) \
+ ((address) & ~((pagesize) - 1))
+/*
+ * Creates an IOMMU PTE for an address an a given pagesize
+ * The PTE has no permission bits set
+ * Pagesize is expected to be a power-of-two larger than 4096
+ */
+#define PAGE_SIZE_PTE(address, pagesize) \
+ (((address) | ((pagesize) - 1)) & \
+ (~(pagesize >> 1)) & PM_ADDR_MASK)
+
+/*
+ * Takes a PTE value with mode=0x07 and returns the page size it maps
+ */
+#define PTE_PAGE_SIZE(pte) \
+ (1ULL << (1 + ffz(((pte) | 0xfffULL))))
+
+#define IOMMU_PTE_P (1ULL << 0)
+#define IOMMU_PTE_TV (1ULL << 1)
+#define IOMMU_PTE_U (1ULL << 59)
+#define IOMMU_PTE_FC (1ULL << 60)
+#define IOMMU_PTE_IR (1ULL << 61)
+#define IOMMU_PTE_IW (1ULL << 62)
+
+#define DTE_FLAG_IOTLB 0x01
+
+#define IOMMU_PAGE_MASK (((1ULL << 52) - 1) & ~0xfffULL)
+#define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_P)
+#define IOMMU_PTE_PAGE(pte) (phys_to_virt((pte) & IOMMU_PAGE_MASK))
+#define IOMMU_PTE_MODE(pte) (((pte) >> 9) & 0x07)
+
+#define IOMMU_PROT_MASK 0x03
+#define IOMMU_PROT_IR 0x01
+#define IOMMU_PROT_IW 0x02
+
+/* IOMMU capabilities */
+#define IOMMU_CAP_IOTLB 24
+#define IOMMU_CAP_NPCACHE 26
+#define IOMMU_CAP_EFR 27
+
+#define MAX_DOMAIN_ID 65536
+
+/* FIXME: move this macro to <linux/pci.h> */
+#define PCI_BUS(x) (((x) >> 8) & 0xff)
+
+/* Protection domain flags */
+#define PD_DMA_OPS_MASK (1UL << 0) /* domain used for dma_ops */
+#define PD_DEFAULT_MASK (1UL << 1) /* domain is a default dma_ops
+ domain for an IOMMU */
+#define PD_PASSTHROUGH_MASK (1UL << 2) /* domain has no page
+ translation */
+
+extern bool amd_iommu_dump;
+#define DUMP_printk(format, arg...) \
+ do { \
+ if (amd_iommu_dump) \
+ printk(KERN_INFO "AMD-Vi: " format, ## arg); \
+ } while(0);
+
+/* global flag if IOMMUs cache non-present entries */
+extern bool amd_iommu_np_cache;
+/* Only true if all IOMMUs support device IOTLBs */
+extern bool amd_iommu_iotlb_sup;
+
+/*
+ * Make iterating over all IOMMUs easier
+ */
+#define for_each_iommu(iommu) \
+ list_for_each_entry((iommu), &amd_iommu_list, list)
+#define for_each_iommu_safe(iommu, next) \
+ list_for_each_entry_safe((iommu), (next), &amd_iommu_list, list)
+
+#define APERTURE_RANGE_SHIFT 27 /* 128 MB */
+#define APERTURE_RANGE_SIZE (1ULL << APERTURE_RANGE_SHIFT)
+#define APERTURE_RANGE_PAGES (APERTURE_RANGE_SIZE >> PAGE_SHIFT)
+#define APERTURE_MAX_RANGES 32 /* allows 4GB of DMA address space */
+#define APERTURE_RANGE_INDEX(a) ((a) >> APERTURE_RANGE_SHIFT)
+#define APERTURE_PAGE_INDEX(a) (((a) >> 21) & 0x3fULL)
+
+/*
+ * This structure contains generic data for IOMMU protection domains
+ * independent of their use.
+ */
+struct protection_domain {
+ struct list_head list; /* for list of all protection domains */
+ struct list_head dev_list; /* List of all devices in this domain */
+ spinlock_t lock; /* mostly used to lock the page table*/
+ struct mutex api_lock; /* protect page tables in the iommu-api path */
+ u16 id; /* the domain id written to the device table */
+ int mode; /* paging mode (0-6 levels) */
+ u64 *pt_root; /* page table root pointer */
+ unsigned long flags; /* flags to find out type of domain */
+ bool updated; /* complete domain flush required */
+ unsigned dev_cnt; /* devices assigned to this domain */
+ unsigned dev_iommu[MAX_IOMMUS]; /* per-IOMMU reference count */
+ void *priv; /* private data */
+
+};
+
+/*
+ * This struct contains device specific data for the IOMMU
+ */
+struct iommu_dev_data {
+ struct list_head list; /* For domain->dev_list */
+ struct list_head dev_data_list; /* For global dev_data_list */
+ struct iommu_dev_data *alias_data;/* The alias dev_data */
+ struct protection_domain *domain; /* Domain the device is bound to */
+ atomic_t bind; /* Domain attach reverent count */
+ u16 devid; /* PCI Device ID */
+ struct {
+ bool enabled;
+ int qdep;
+ } ats; /* ATS state */
+};
+
+/*
+ * For dynamic growth the aperture size is split into ranges of 128MB of
+ * DMA address space each. This struct represents one such range.
+ */
+struct aperture_range {
+
+ /* address allocation bitmap */
+ unsigned long *bitmap;
+
+ /*
+ * Array of PTE pages for the aperture. In this array we save all the
+ * leaf pages of the domain page table used for the aperture. This way
+ * we don't need to walk the page table to find a specific PTE. We can
+ * just calculate its address in constant time.
+ */
+ u64 *pte_pages[64];
+
+ unsigned long offset;
+};
+
+/*
+ * Data container for a dma_ops specific protection domain
+ */
+struct dma_ops_domain {
+ struct list_head list;
+
+ /* generic protection domain information */
+ struct protection_domain domain;
+
+ /* size of the aperture for the mappings */
+ unsigned long aperture_size;
+
+ /* address we start to search for free addresses */
+ unsigned long next_address;
+
+ /* address space relevant data */
+ struct aperture_range *aperture[APERTURE_MAX_RANGES];
+
+ /* This will be set to true when TLB needs to be flushed */
+ bool need_flush;
+
+ /*
+ * if this is a preallocated domain, keep the device for which it was
+ * preallocated in this variable
+ */
+ u16 target_dev;
+};
+
+/*
+ * Structure where we save information about one hardware AMD IOMMU in the
+ * system.
+ */
+struct amd_iommu {
+ struct list_head list;
+
+ /* Index within the IOMMU array */
+ int index;
+
+ /* locks the accesses to the hardware */
+ spinlock_t lock;
+
+ /* Pointer to PCI device of this IOMMU */
+ struct pci_dev *dev;
+
+ /* physical address of MMIO space */
+ u64 mmio_phys;
+ /* virtual address of MMIO space */
+ u8 *mmio_base;
+
+ /* capabilities of that IOMMU read from ACPI */
+ u32 cap;
+
+ /* flags read from acpi table */
+ u8 acpi_flags;
+
+ /* Extended features */
+ u64 features;
+
+ /*
+ * Capability pointer. There could be more than one IOMMU per PCI
+ * device function if there are more than one AMD IOMMU capability
+ * pointers.
+ */
+ u16 cap_ptr;
+
+ /* pci domain of this IOMMU */
+ u16 pci_seg;
+
+ /* first device this IOMMU handles. read from PCI */
+ u16 first_device;
+ /* last device this IOMMU handles. read from PCI */
+ u16 last_device;
+
+ /* start of exclusion range of that IOMMU */
+ u64 exclusion_start;
+ /* length of exclusion range of that IOMMU */
+ u64 exclusion_length;
+
+ /* command buffer virtual address */
+ u8 *cmd_buf;
+ /* size of command buffer */
+ u32 cmd_buf_size;
+
+ /* size of event buffer */
+ u32 evt_buf_size;
+ /* event buffer virtual address */
+ u8 *evt_buf;
+ /* MSI number for event interrupt */
+ u16 evt_msi_num;
+
+ /* true if interrupts for this IOMMU are already enabled */
+ bool int_enabled;
+
+ /* if one, we need to send a completion wait command */
+ bool need_sync;
+
+ /* default dma_ops domain for that IOMMU */
+ struct dma_ops_domain *default_dom;
+
+ /*
+ * We can't rely on the BIOS to restore all values on reinit, so we
+ * need to stash them
+ */
+
+ /* The iommu BAR */
+ u32 stored_addr_lo;
+ u32 stored_addr_hi;
+
+ /*
+ * Each iommu has 6 l1s, each of which is documented as having 0x12
+ * registers
+ */
+ u32 stored_l1[6][0x12];
+
+ /* The l2 indirect registers */
+ u32 stored_l2[0x83];
+};
+
+/*
+ * List with all IOMMUs in the system. This list is not locked because it is
+ * only written and read at driver initialization or suspend time
+ */
+extern struct list_head amd_iommu_list;
+
+/*
+ * Array with pointers to each IOMMU struct
+ * The indices are referenced in the protection domains
+ */
+extern struct amd_iommu *amd_iommus[MAX_IOMMUS];
+
+/* Number of IOMMUs present in the system */
+extern int amd_iommus_present;
+
+/*
+ * Declarations for the global list of all protection domains
+ */
+extern spinlock_t amd_iommu_pd_lock;
+extern struct list_head amd_iommu_pd_list;
+
+/*
+ * Structure defining one entry in the device table
+ */
+struct dev_table_entry {
+ u32 data[8];
+};
+
+/*
+ * One entry for unity mappings parsed out of the ACPI table.
+ */
+struct unity_map_entry {
+ struct list_head list;
+
+ /* starting device id this entry is used for (including) */
+ u16 devid_start;
+ /* end device id this entry is used for (including) */
+ u16 devid_end;
+
+ /* start address to unity map (including) */
+ u64 address_start;
+ /* end address to unity map (including) */
+ u64 address_end;
+
+ /* required protection */
+ int prot;
+};
+
+/*
+ * List of all unity mappings. It is not locked because as runtime it is only
+ * read. It is created at ACPI table parsing time.
+ */
+extern struct list_head amd_iommu_unity_map;
+
+/*
+ * Data structures for device handling
+ */
+
+/*
+ * Device table used by hardware. Read and write accesses by software are
+ * locked with the amd_iommu_pd_table lock.
+ */
+extern struct dev_table_entry *amd_iommu_dev_table;
+
+/*
+ * Alias table to find requestor ids to device ids. Not locked because only
+ * read on runtime.
+ */
+extern u16 *amd_iommu_alias_table;
+
+/*
+ * Reverse lookup table to find the IOMMU which translates a specific device.
+ */
+extern struct amd_iommu **amd_iommu_rlookup_table;
+
+/* size of the dma_ops aperture as power of 2 */
+extern unsigned amd_iommu_aperture_order;
+
+/* largest PCI device id we expect translation requests for */
+extern u16 amd_iommu_last_bdf;
+
+/* allocation bitmap for domain ids */
+extern unsigned long *amd_iommu_pd_alloc_bitmap;
+
+/*
+ * If true, the addresses will be flushed on unmap time, not when
+ * they are reused
+ */
+extern bool amd_iommu_unmap_flush;
+
+/* takes bus and device/function and returns the device id
+ * FIXME: should that be in generic PCI code? */
+static inline u16 calc_devid(u8 bus, u8 devfn)
+{
+ return (((u16)bus) << 8) | devfn;
+}
+
+#ifdef CONFIG_AMD_IOMMU_STATS
+
+struct __iommu_counter {
+ char *name;
+ struct dentry *dent;
+ u64 value;
+};
+
+#define DECLARE_STATS_COUNTER(nm) \
+ static struct __iommu_counter nm = { \
+ .name = #nm, \
+ }
+
+#define INC_STATS_COUNTER(name) name.value += 1
+#define ADD_STATS_COUNTER(name, x) name.value += (x)
+#define SUB_STATS_COUNTER(name, x) name.value -= (x)
+
+#else /* CONFIG_AMD_IOMMU_STATS */
+
+#define DECLARE_STATS_COUNTER(name)
+#define INC_STATS_COUNTER(name)
+#define ADD_STATS_COUNTER(name, x)
+#define SUB_STATS_COUNTER(name, x)
+
+#endif /* CONFIG_AMD_IOMMU_STATS */
+
+#endif /* _ASM_X86_AMD_IOMMU_TYPES_H */
diff --git a/drivers/pci/dmar.c b/drivers/iommu/dmar.c
index 3dc9befa5aec..35c1e17fce1d 100644
--- a/drivers/pci/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -46,7 +46,7 @@
*/
LIST_HEAD(dmar_drhd_units);
-static struct acpi_table_header * __initdata dmar_tbl;
+struct acpi_table_header * __initdata dmar_tbl;
static acpi_size dmar_tbl_size;
static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
@@ -118,8 +118,8 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
return 0;
}
-static int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
- struct pci_dev ***devices, u16 segment)
+int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
+ struct pci_dev ***devices, u16 segment)
{
struct acpi_dmar_device_scope *scope;
void * tmp = start;
@@ -217,133 +217,6 @@ static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru)
return ret;
}
-#ifdef CONFIG_DMAR
-LIST_HEAD(dmar_rmrr_units);
-
-static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr)
-{
- list_add(&rmrr->list, &dmar_rmrr_units);
-}
-
-
-static int __init
-dmar_parse_one_rmrr(struct acpi_dmar_header *header)
-{
- struct acpi_dmar_reserved_memory *rmrr;
- struct dmar_rmrr_unit *rmrru;
-
- rmrru = kzalloc(sizeof(*rmrru), GFP_KERNEL);
- if (!rmrru)
- return -ENOMEM;
-
- rmrru->hdr = header;
- rmrr = (struct acpi_dmar_reserved_memory *)header;
- rmrru->base_address = rmrr->base_address;
- rmrru->end_address = rmrr->end_address;
-
- dmar_register_rmrr_unit(rmrru);
- return 0;
-}
-
-static int __init
-rmrr_parse_dev(struct dmar_rmrr_unit *rmrru)
-{
- struct acpi_dmar_reserved_memory *rmrr;
- int ret;
-
- rmrr = (struct acpi_dmar_reserved_memory *) rmrru->hdr;
- ret = dmar_parse_dev_scope((void *)(rmrr + 1),
- ((void *)rmrr) + rmrr->header.length,
- &rmrru->devices_cnt, &rmrru->devices, rmrr->segment);
-
- if (ret || (rmrru->devices_cnt == 0)) {
- list_del(&rmrru->list);
- kfree(rmrru);
- }
- return ret;
-}
-
-static LIST_HEAD(dmar_atsr_units);
-
-static int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
-{
- struct acpi_dmar_atsr *atsr;
- struct dmar_atsr_unit *atsru;
-
- atsr = container_of(hdr, struct acpi_dmar_atsr, header);
- atsru = kzalloc(sizeof(*atsru), GFP_KERNEL);
- if (!atsru)
- return -ENOMEM;
-
- atsru->hdr = hdr;
- atsru->include_all = atsr->flags & 0x1;
-
- list_add(&atsru->list, &dmar_atsr_units);
-
- return 0;
-}
-
-static int __init atsr_parse_dev(struct dmar_atsr_unit *atsru)
-{
- int rc;
- struct acpi_dmar_atsr *atsr;
-
- if (atsru->include_all)
- return 0;
-
- atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
- rc = dmar_parse_dev_scope((void *)(atsr + 1),
- (void *)atsr + atsr->header.length,
- &atsru->devices_cnt, &atsru->devices,
- atsr->segment);
- if (rc || !atsru->devices_cnt) {
- list_del(&atsru->list);
- kfree(atsru);
- }
-
- return rc;
-}
-
-int dmar_find_matched_atsr_unit(struct pci_dev *dev)
-{
- int i;
- struct pci_bus *bus;
- struct acpi_dmar_atsr *atsr;
- struct dmar_atsr_unit *atsru;
-
- dev = pci_physfn(dev);
-
- list_for_each_entry(atsru, &dmar_atsr_units, list) {
- atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
- if (atsr->segment == pci_domain_nr(dev->bus))
- goto found;
- }
-
- return 0;
-
-found:
- for (bus = dev->bus; bus; bus = bus->parent) {
- struct pci_dev *bridge = bus->self;
-
- if (!bridge || !pci_is_pcie(bridge) ||
- bridge->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
- return 0;
-
- if (bridge->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
- for (i = 0; i < atsru->devices_cnt; i++)
- if (atsru->devices[i] == bridge)
- return 1;
- break;
- }
- }
-
- if (atsru->include_all)
- return 1;
-
- return 0;
-}
-#endif
-
#ifdef CONFIG_ACPI_NUMA
static int __init
dmar_parse_one_rhsa(struct acpi_dmar_header *header)
@@ -484,14 +357,10 @@ parse_dmar_table(void)
ret = dmar_parse_one_drhd(entry_header);
break;
case ACPI_DMAR_TYPE_RESERVED_MEMORY:
-#ifdef CONFIG_DMAR
ret = dmar_parse_one_rmrr(entry_header);
-#endif
break;
case ACPI_DMAR_TYPE_ATSR:
-#ifdef CONFIG_DMAR
ret = dmar_parse_one_atsr(entry_header);
-#endif
break;
case ACPI_DMAR_HARDWARE_AFFINITY:
#ifdef CONFIG_ACPI_NUMA
@@ -557,34 +426,31 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev)
int __init dmar_dev_scope_init(void)
{
+ static int dmar_dev_scope_initialized;
struct dmar_drhd_unit *drhd, *drhd_n;
int ret = -ENODEV;
+ if (dmar_dev_scope_initialized)
+ return dmar_dev_scope_initialized;
+
+ if (list_empty(&dmar_drhd_units))
+ goto fail;
+
list_for_each_entry_safe(drhd, drhd_n, &dmar_drhd_units, list) {
ret = dmar_parse_dev(drhd);
if (ret)
- return ret;
+ goto fail;
}
-#ifdef CONFIG_DMAR
- {
- struct dmar_rmrr_unit *rmrr, *rmrr_n;
- struct dmar_atsr_unit *atsr, *atsr_n;
-
- list_for_each_entry_safe(rmrr, rmrr_n, &dmar_rmrr_units, list) {
- ret = rmrr_parse_dev(rmrr);
- if (ret)
- return ret;
- }
+ ret = dmar_parse_rmrr_atsr_dev();
+ if (ret)
+ goto fail;
- list_for_each_entry_safe(atsr, atsr_n, &dmar_atsr_units, list) {
- ret = atsr_parse_dev(atsr);
- if (ret)
- return ret;
- }
- }
-#endif
+ dmar_dev_scope_initialized = 1;
+ return 0;
+fail:
+ dmar_dev_scope_initialized = ret;
return ret;
}
@@ -611,14 +477,6 @@ int __init dmar_table_init(void)
return -ENODEV;
}
-#ifdef CONFIG_DMAR
- if (list_empty(&dmar_rmrr_units))
- printk(KERN_INFO PREFIX "No RMRR found\n");
-
- if (list_empty(&dmar_atsr_units))
- printk(KERN_INFO PREFIX "No ATSR found\n");
-#endif
-
return 0;
}
@@ -682,9 +540,6 @@ int __init check_zero_address(void)
return 1;
failed:
-#ifdef CONFIG_DMAR
- dmar_disabled = 1;
-#endif
return 0;
}
@@ -696,22 +551,21 @@ int __init detect_intel_iommu(void)
if (ret)
ret = check_zero_address();
{
-#ifdef CONFIG_INTR_REMAP
struct acpi_table_dmar *dmar;
dmar = (struct acpi_table_dmar *) dmar_tbl;
- if (ret && cpu_has_x2apic && dmar->flags & 0x1)
+
+ if (ret && intr_remapping_enabled && cpu_has_x2apic &&
+ dmar->flags & 0x1)
printk(KERN_INFO
- "Queued invalidation will be enabled to support "
- "x2apic and Intr-remapping.\n");
-#endif
-#ifdef CONFIG_DMAR
+ "Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");
+
if (ret && !no_iommu && !iommu_detected && !dmar_disabled) {
iommu_detected = 1;
/* Make sure ACS will be enabled */
pci_request_acs();
}
-#endif
+
#ifdef CONFIG_X86
if (ret)
x86_init.iommu.iommu_init = intel_iommu_init;
@@ -758,7 +612,6 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
goto err_unmap;
}
-#ifdef CONFIG_DMAR
agaw = iommu_calculate_agaw(iommu);
if (agaw < 0) {
printk(KERN_ERR
@@ -773,7 +626,6 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
iommu->seq_id);
goto err_unmap;
}
-#endif
iommu->agaw = agaw;
iommu->msagaw = msagaw;
@@ -800,7 +652,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
(unsigned long long)iommu->cap,
(unsigned long long)iommu->ecap);
- spin_lock_init(&iommu->register_lock);
+ raw_spin_lock_init(&iommu->register_lock);
drhd->iommu = iommu;
return 0;
@@ -817,9 +669,7 @@ void free_iommu(struct intel_iommu *iommu)
if (!iommu)
return;
-#ifdef CONFIG_DMAR
free_dmar_iommu(iommu);
-#endif
if (iommu->reg)
iounmap(iommu->reg);
@@ -921,11 +771,11 @@ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
restart:
rc = 0;
- spin_lock_irqsave(&qi->q_lock, flags);
+ raw_spin_lock_irqsave(&qi->q_lock, flags);
while (qi->free_cnt < 3) {
- spin_unlock_irqrestore(&qi->q_lock, flags);
+ raw_spin_unlock_irqrestore(&qi->q_lock, flags);
cpu_relax();
- spin_lock_irqsave(&qi->q_lock, flags);
+ raw_spin_lock_irqsave(&qi->q_lock, flags);
}
index = qi->free_head;
@@ -965,15 +815,15 @@ restart:
if (rc)
break;
- spin_unlock(&qi->q_lock);
+ raw_spin_unlock(&qi->q_lock);
cpu_relax();
- spin_lock(&qi->q_lock);
+ raw_spin_lock(&qi->q_lock);
}
qi->desc_status[index] = QI_DONE;
reclaim_free_desc(qi);
- spin_unlock_irqrestore(&qi->q_lock, flags);
+ raw_spin_unlock_irqrestore(&qi->q_lock, flags);
if (rc == -EAGAIN)
goto restart;
@@ -1062,7 +912,7 @@ void dmar_disable_qi(struct intel_iommu *iommu)
if (!ecap_qis(iommu->ecap))
return;
- spin_lock_irqsave(&iommu->register_lock, flags);
+ raw_spin_lock_irqsave(&iommu->register_lock, flags);
sts = dmar_readq(iommu->reg + DMAR_GSTS_REG);
if (!(sts & DMA_GSTS_QIES))
@@ -1082,7 +932,7 @@ void dmar_disable_qi(struct intel_iommu *iommu)
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl,
!(sts & DMA_GSTS_QIES), sts);
end:
- spin_unlock_irqrestore(&iommu->register_lock, flags);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
}
/*
@@ -1097,7 +947,7 @@ static void __dmar_enable_qi(struct intel_iommu *iommu)
qi->free_head = qi->free_tail = 0;
qi->free_cnt = QI_LENGTH;
- spin_lock_irqsave(&iommu->register_lock, flags);
+ raw_spin_lock_irqsave(&iommu->register_lock, flags);
/* write zero to the tail reg */
writel(0, iommu->reg + DMAR_IQT_REG);
@@ -1110,7 +960,7 @@ static void __dmar_enable_qi(struct intel_iommu *iommu)
/* Make sure hardware complete it */
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts);
- spin_unlock_irqrestore(&iommu->register_lock, flags);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
}
/*
@@ -1159,7 +1009,7 @@ int dmar_enable_qi(struct intel_iommu *iommu)
qi->free_head = qi->free_tail = 0;
qi->free_cnt = QI_LENGTH;
- spin_lock_init(&qi->q_lock);
+ raw_spin_lock_init(&qi->q_lock);
__dmar_enable_qi(iommu);
@@ -1225,11 +1075,11 @@ void dmar_msi_unmask(struct irq_data *data)
unsigned long flag;
/* unmask it */
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
writel(0, iommu->reg + DMAR_FECTL_REG);
/* Read a reg to force flush the post write */
readl(iommu->reg + DMAR_FECTL_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
}
void dmar_msi_mask(struct irq_data *data)
@@ -1238,11 +1088,11 @@ void dmar_msi_mask(struct irq_data *data)
struct intel_iommu *iommu = irq_data_get_irq_handler_data(data);
/* mask it */
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
writel(DMA_FECTL_IM, iommu->reg + DMAR_FECTL_REG);
/* Read a reg to force flush the post write */
readl(iommu->reg + DMAR_FECTL_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
}
void dmar_msi_write(int irq, struct msi_msg *msg)
@@ -1250,11 +1100,11 @@ void dmar_msi_write(int irq, struct msi_msg *msg)
struct intel_iommu *iommu = irq_get_handler_data(irq);
unsigned long flag;
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
writel(msg->data, iommu->reg + DMAR_FEDATA_REG);
writel(msg->address_lo, iommu->reg + DMAR_FEADDR_REG);
writel(msg->address_hi, iommu->reg + DMAR_FEUADDR_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
}
void dmar_msi_read(int irq, struct msi_msg *msg)
@@ -1262,11 +1112,11 @@ void dmar_msi_read(int irq, struct msi_msg *msg)
struct intel_iommu *iommu = irq_get_handler_data(irq);
unsigned long flag;
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
msg->data = readl(iommu->reg + DMAR_FEDATA_REG);
msg->address_lo = readl(iommu->reg + DMAR_FEADDR_REG);
msg->address_hi = readl(iommu->reg + DMAR_FEUADDR_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
}
static int dmar_fault_do_one(struct intel_iommu *iommu, int type,
@@ -1303,7 +1153,7 @@ irqreturn_t dmar_fault(int irq, void *dev_id)
u32 fault_status;
unsigned long flag;
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
fault_status = readl(iommu->reg + DMAR_FSTS_REG);
if (fault_status)
printk(KERN_ERR "DRHD: handling fault status reg %x\n",
@@ -1342,7 +1192,7 @@ irqreturn_t dmar_fault(int irq, void *dev_id)
writel(DMA_FRCD_F, iommu->reg + reg +
fault_index * PRIMARY_FAULT_REG_LEN + 12);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
dmar_fault_do_one(iommu, type, fault_reason,
source_id, guest_addr);
@@ -1350,14 +1200,14 @@ irqreturn_t dmar_fault(int irq, void *dev_id)
fault_index++;
if (fault_index >= cap_num_fault_regs(iommu->cap))
fault_index = 0;
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
}
clear_rest:
/* clear all the other faults */
fault_status = readl(iommu->reg + DMAR_FSTS_REG);
writel(fault_status, iommu->reg + DMAR_FSTS_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
return IRQ_HANDLED;
}
@@ -1388,7 +1238,7 @@ int dmar_set_interrupt(struct intel_iommu *iommu)
return ret;
}
- ret = request_irq(irq, dmar_fault, 0, iommu->name, iommu);
+ ret = request_irq(irq, dmar_fault, IRQF_NO_THREAD, iommu->name, iommu);
if (ret)
printk(KERN_ERR "IOMMU: can't request irq\n");
return ret;
diff --git a/drivers/pci/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 8ec352077e1a..bcbd693b351a 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -24,6 +24,7 @@
#include <linux/init.h>
#include <linux/bitmap.h>
#include <linux/debugfs.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
@@ -42,7 +43,6 @@
#include <linux/pci-ats.h>
#include <asm/cacheflush.h>
#include <asm/iommu.h>
-#include "pci.h"
#define ROOT_SIZE VTD_PAGE_SIZE
#define CONTEXT_SIZE VTD_PAGE_SIZE
@@ -307,6 +307,11 @@ static inline bool dma_pte_present(struct dma_pte *pte)
return (pte->val & 3) != 0;
}
+static inline bool dma_pte_superpage(struct dma_pte *pte)
+{
+ return (pte->val & (1 << 7));
+}
+
static inline int first_pte_in_page(struct dma_pte *pte)
{
return !((unsigned long)pte & ~VTD_PAGE_MASK);
@@ -394,17 +399,20 @@ static long list_size;
static void domain_remove_dev_info(struct dmar_domain *domain);
-#ifdef CONFIG_DMAR_DEFAULT_ON
+#ifdef CONFIG_INTEL_IOMMU_DEFAULT_ON
int dmar_disabled = 0;
#else
int dmar_disabled = 1;
-#endif /*CONFIG_DMAR_DEFAULT_ON*/
+#endif /*CONFIG_INTEL_IOMMU_DEFAULT_ON*/
static int dmar_map_gfx = 1;
static int dmar_forcedac;
static int intel_iommu_strict;
static int intel_iommu_superpage = 1;
+int intel_iommu_gfx_mapped;
+EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped);
+
#define DUMMY_DEVICE_DOMAIN_INFO ((struct device_domain_info *)(-1))
static DEFINE_SPINLOCK(device_domain_lock);
static LIST_HEAD(device_domain_list);
@@ -578,17 +586,18 @@ static void domain_update_iommu_snooping(struct dmar_domain *domain)
static void domain_update_iommu_superpage(struct dmar_domain *domain)
{
- int i, mask = 0xf;
+ struct dmar_drhd_unit *drhd;
+ struct intel_iommu *iommu = NULL;
+ int mask = 0xf;
if (!intel_iommu_superpage) {
domain->iommu_superpage = 0;
return;
}
- domain->iommu_superpage = 4; /* 1TiB */
-
- for_each_set_bit(i, &domain->iommu_bmp, g_num_of_iommus) {
- mask |= cap_super_page_val(g_iommus[i]->cap);
+ /* set iommu_superpage to the smallest common denominator */
+ for_each_active_iommu(iommu, drhd) {
+ mask &= cap_super_page_val(iommu->cap);
if (!mask) {
break;
}
@@ -731,29 +740,23 @@ out:
}
static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
- unsigned long pfn, int large_level)
+ unsigned long pfn, int target_level)
{
int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
struct dma_pte *parent, *pte = NULL;
int level = agaw_to_level(domain->agaw);
- int offset, target_level;
+ int offset;
BUG_ON(!domain->pgd);
BUG_ON(addr_width < BITS_PER_LONG && pfn >> addr_width);
parent = domain->pgd;
- /* Search pte */
- if (!large_level)
- target_level = 1;
- else
- target_level = large_level;
-
while (level > 0) {
void *tmp_page;
offset = pfn_level_offset(pfn, level);
pte = &parent[offset];
- if (!large_level && (pte->val & DMA_PTE_LARGE_PAGE))
+ if (!target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
break;
if (level == target_level)
break;
@@ -817,13 +820,14 @@ static struct dma_pte *dma_pfn_level_pte(struct dmar_domain *domain,
}
/* clear last level pte, a tlb flush should be followed */
-static void dma_pte_clear_range(struct dmar_domain *domain,
+static int dma_pte_clear_range(struct dmar_domain *domain,
unsigned long start_pfn,
unsigned long last_pfn)
{
int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
unsigned int large_page = 1;
struct dma_pte *first_pte, *pte;
+ int order;
BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width);
BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width);
@@ -847,6 +851,9 @@ static void dma_pte_clear_range(struct dmar_domain *domain,
(void *)pte - (void *)first_pte);
} while (start_pfn && start_pfn <= last_pfn);
+
+ order = (large_page - 1) * 9;
+ return order;
}
/* free page table pages. last level pte should already be cleared */
@@ -933,7 +940,7 @@ static void iommu_set_root_entry(struct intel_iommu *iommu)
addr = iommu->root_entry;
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
dmar_writeq(iommu->reg + DMAR_RTADDR_REG, virt_to_phys(addr));
writel(iommu->gcmd | DMA_GCMD_SRTP, iommu->reg + DMAR_GCMD_REG);
@@ -942,7 +949,7 @@ static void iommu_set_root_entry(struct intel_iommu *iommu)
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
readl, (sts & DMA_GSTS_RTPS), sts);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
}
static void iommu_flush_write_buffer(struct intel_iommu *iommu)
@@ -953,14 +960,14 @@ static void iommu_flush_write_buffer(struct intel_iommu *iommu)
if (!rwbf_quirk && !cap_rwbf(iommu->cap))
return;
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
writel(iommu->gcmd | DMA_GCMD_WBF, iommu->reg + DMAR_GCMD_REG);
/* Make sure hardware complete it */
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
readl, (!(val & DMA_GSTS_WBFS)), val);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
}
/* return value determine if we need a write buffer flush */
@@ -987,14 +994,14 @@ static void __iommu_flush_context(struct intel_iommu *iommu,
}
val |= DMA_CCMD_ICC;
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
dmar_writeq(iommu->reg + DMAR_CCMD_REG, val);
/* Make sure hardware complete it */
IOMMU_WAIT_OP(iommu, DMAR_CCMD_REG,
dmar_readq, (!(val & DMA_CCMD_ICC)), val);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
}
/* return value determine if we need a write buffer flush */
@@ -1033,7 +1040,7 @@ static void __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did,
if (cap_write_drain(iommu->cap))
val |= DMA_TLB_WRITE_DRAIN;
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
/* Note: Only uses first TLB reg currently */
if (val_iva)
dmar_writeq(iommu->reg + tlb_offset, val_iva);
@@ -1043,7 +1050,7 @@ static void __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did,
IOMMU_WAIT_OP(iommu, tlb_offset + 8,
dmar_readq, (!(val & DMA_TLB_IVT)), val);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
/* check IOTLB invalidation granularity */
if (DMA_TLB_IAIG(val) == 0)
@@ -1159,7 +1166,7 @@ static void iommu_disable_protect_mem_regions(struct intel_iommu *iommu)
u32 pmen;
unsigned long flags;
- spin_lock_irqsave(&iommu->register_lock, flags);
+ raw_spin_lock_irqsave(&iommu->register_lock, flags);
pmen = readl(iommu->reg + DMAR_PMEN_REG);
pmen &= ~DMA_PMEN_EPM;
writel(pmen, iommu->reg + DMAR_PMEN_REG);
@@ -1168,7 +1175,7 @@ static void iommu_disable_protect_mem_regions(struct intel_iommu *iommu)
IOMMU_WAIT_OP(iommu, DMAR_PMEN_REG,
readl, !(pmen & DMA_PMEN_PRS), pmen);
- spin_unlock_irqrestore(&iommu->register_lock, flags);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
}
static int iommu_enable_translation(struct intel_iommu *iommu)
@@ -1176,7 +1183,7 @@ static int iommu_enable_translation(struct intel_iommu *iommu)
u32 sts;
unsigned long flags;
- spin_lock_irqsave(&iommu->register_lock, flags);
+ raw_spin_lock_irqsave(&iommu->register_lock, flags);
iommu->gcmd |= DMA_GCMD_TE;
writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
@@ -1184,7 +1191,7 @@ static int iommu_enable_translation(struct intel_iommu *iommu)
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
readl, (sts & DMA_GSTS_TES), sts);
- spin_unlock_irqrestore(&iommu->register_lock, flags);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
return 0;
}
@@ -1193,7 +1200,7 @@ static int iommu_disable_translation(struct intel_iommu *iommu)
u32 sts;
unsigned long flag;
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
iommu->gcmd &= ~DMA_GCMD_TE;
writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
@@ -1201,7 +1208,7 @@ static int iommu_disable_translation(struct intel_iommu *iommu)
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
readl, (!(sts & DMA_GSTS_TES)), sts);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
return 0;
}
@@ -2151,7 +2158,7 @@ static inline int iommu_prepare_rmrr_dev(struct dmar_rmrr_unit *rmrr,
rmrr->end_address);
}
-#ifdef CONFIG_DMAR_FLOPPY_WA
+#ifdef CONFIG_INTEL_IOMMU_FLOPPY_WA
static inline void iommu_prepare_isa(void)
{
struct pci_dev *pdev;
@@ -2174,7 +2181,7 @@ static inline void iommu_prepare_isa(void)
{
return;
}
-#endif /* !CONFIG_DMAR_FLPY_WA */
+#endif /* !CONFIG_INTEL_IOMMU_FLPY_WA */
static int md_domain_init(struct dmar_domain *domain, int guest_width);
@@ -2479,7 +2486,7 @@ static int __init init_dmars(void)
if (iommu_pass_through)
iommu_identity_mapping |= IDENTMAP_ALL;
-#ifdef CONFIG_DMAR_BROKEN_GFX_WA
+#ifdef CONFIG_INTEL_IOMMU_BROKEN_GFX_WA
iommu_identity_mapping |= IDENTMAP_GFX;
#endif
@@ -3221,9 +3228,6 @@ static void __init init_no_remapping_devices(void)
}
}
- if (dmar_map_gfx)
- return;
-
for_each_drhd_unit(drhd) {
int i;
if (drhd->ignored || drhd->include_all)
@@ -3231,18 +3235,23 @@ static void __init init_no_remapping_devices(void)
for (i = 0; i < drhd->devices_cnt; i++)
if (drhd->devices[i] &&
- !IS_GFX_DEVICE(drhd->devices[i]))
+ !IS_GFX_DEVICE(drhd->devices[i]))
break;
if (i < drhd->devices_cnt)
continue;
- /* bypass IOMMU if it is just for gfx devices */
- drhd->ignored = 1;
- for (i = 0; i < drhd->devices_cnt; i++) {
- if (!drhd->devices[i])
- continue;
- drhd->devices[i]->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
+ /* This IOMMU has *only* gfx devices. Either bypass it or
+ set the gfx_mapped flag, as appropriate */
+ if (dmar_map_gfx) {
+ intel_iommu_gfx_mapped = 1;
+ } else {
+ drhd->ignored = 1;
+ for (i = 0; i < drhd->devices_cnt; i++) {
+ if (!drhd->devices[i])
+ continue;
+ drhd->devices[i]->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
+ }
}
}
}
@@ -3315,7 +3324,7 @@ static int iommu_suspend(void)
for_each_active_iommu(iommu, drhd) {
iommu_disable_translation(iommu);
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
iommu->iommu_state[SR_DMAR_FECTL_REG] =
readl(iommu->reg + DMAR_FECTL_REG);
@@ -3326,7 +3335,7 @@ static int iommu_suspend(void)
iommu->iommu_state[SR_DMAR_FEUADDR_REG] =
readl(iommu->reg + DMAR_FEUADDR_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
}
return 0;
@@ -3353,7 +3362,7 @@ static void iommu_resume(void)
for_each_active_iommu(iommu, drhd) {
- spin_lock_irqsave(&iommu->register_lock, flag);
+ raw_spin_lock_irqsave(&iommu->register_lock, flag);
writel(iommu->iommu_state[SR_DMAR_FECTL_REG],
iommu->reg + DMAR_FECTL_REG);
@@ -3364,7 +3373,7 @@ static void iommu_resume(void)
writel(iommu->iommu_state[SR_DMAR_FEUADDR_REG],
iommu->reg + DMAR_FEUADDR_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
}
for_each_active_iommu(iommu, drhd)
@@ -3385,6 +3394,151 @@ static void __init init_iommu_pm_ops(void)
static inline void init_iommu_pm_ops(void) {}
#endif /* CONFIG_PM */
+LIST_HEAD(dmar_rmrr_units);
+
+static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr)
+{
+ list_add(&rmrr->list, &dmar_rmrr_units);
+}
+
+
+int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
+{
+ struct acpi_dmar_reserved_memory *rmrr;
+ struct dmar_rmrr_unit *rmrru;
+
+ rmrru = kzalloc(sizeof(*rmrru), GFP_KERNEL);
+ if (!rmrru)
+ return -ENOMEM;
+
+ rmrru->hdr = header;
+ rmrr = (struct acpi_dmar_reserved_memory *)header;
+ rmrru->base_address = rmrr->base_address;
+ rmrru->end_address = rmrr->end_address;
+
+ dmar_register_rmrr_unit(rmrru);
+ return 0;
+}
+
+static int __init
+rmrr_parse_dev(struct dmar_rmrr_unit *rmrru)
+{
+ struct acpi_dmar_reserved_memory *rmrr;
+ int ret;
+
+ rmrr = (struct acpi_dmar_reserved_memory *) rmrru->hdr;
+ ret = dmar_parse_dev_scope((void *)(rmrr + 1),
+ ((void *)rmrr) + rmrr->header.length,
+ &rmrru->devices_cnt, &rmrru->devices, rmrr->segment);
+
+ if (ret || (rmrru->devices_cnt == 0)) {
+ list_del(&rmrru->list);
+ kfree(rmrru);
+ }
+ return ret;
+}
+
+static LIST_HEAD(dmar_atsr_units);
+
+int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
+{
+ struct acpi_dmar_atsr *atsr;
+ struct dmar_atsr_unit *atsru;
+
+ atsr = container_of(hdr, struct acpi_dmar_atsr, header);
+ atsru = kzalloc(sizeof(*atsru), GFP_KERNEL);
+ if (!atsru)
+ return -ENOMEM;
+
+ atsru->hdr = hdr;
+ atsru->include_all = atsr->flags & 0x1;
+
+ list_add(&atsru->list, &dmar_atsr_units);
+
+ return 0;
+}
+
+static int __init atsr_parse_dev(struct dmar_atsr_unit *atsru)
+{
+ int rc;
+ struct acpi_dmar_atsr *atsr;
+
+ if (atsru->include_all)
+ return 0;
+
+ atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
+ rc = dmar_parse_dev_scope((void *)(atsr + 1),
+ (void *)atsr + atsr->header.length,
+ &atsru->devices_cnt, &atsru->devices,
+ atsr->segment);
+ if (rc || !atsru->devices_cnt) {
+ list_del(&atsru->list);
+ kfree(atsru);
+ }
+
+ return rc;
+}
+
+int dmar_find_matched_atsr_unit(struct pci_dev *dev)
+{
+ int i;
+ struct pci_bus *bus;
+ struct acpi_dmar_atsr *atsr;
+ struct dmar_atsr_unit *atsru;
+
+ dev = pci_physfn(dev);
+
+ list_for_each_entry(atsru, &dmar_atsr_units, list) {
+ atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
+ if (atsr->segment == pci_domain_nr(dev->bus))
+ goto found;
+ }
+
+ return 0;
+
+found:
+ for (bus = dev->bus; bus; bus = bus->parent) {
+ struct pci_dev *bridge = bus->self;
+
+ if (!bridge || !pci_is_pcie(bridge) ||
+ bridge->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
+ return 0;
+
+ if (bridge->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
+ for (i = 0; i < atsru->devices_cnt; i++)
+ if (atsru->devices[i] == bridge)
+ return 1;
+ break;
+ }
+ }
+
+ if (atsru->include_all)
+ return 1;
+
+ return 0;
+}
+
+int dmar_parse_rmrr_atsr_dev(void)
+{
+ struct dmar_rmrr_unit *rmrr, *rmrr_n;
+ struct dmar_atsr_unit *atsr, *atsr_n;
+ int ret = 0;
+
+ list_for_each_entry_safe(rmrr, rmrr_n, &dmar_rmrr_units, list) {
+ ret = rmrr_parse_dev(rmrr);
+ if (ret)
+ return ret;
+ }
+
+ list_for_each_entry_safe(atsr, atsr_n, &dmar_atsr_units, list) {
+ ret = atsr_parse_dev(atsr);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
/*
* Here we only respond to action of unbound device from driver.
*
@@ -3434,16 +3588,12 @@ int __init intel_iommu_init(void)
return -ENODEV;
}
- if (dmar_dev_scope_init()) {
+ if (dmar_dev_scope_init() < 0) {
if (force_on)
panic("tboot: Failed to initialize DMAR device scope\n");
return -ENODEV;
}
- /*
- * Check the need for DMA-remapping initialization now.
- * Above initialization will also be used by Interrupt-remapping.
- */
if (no_iommu || dmar_disabled)
return -ENODEV;
@@ -3453,6 +3603,12 @@ int __init intel_iommu_init(void)
return -ENODEV;
}
+ if (list_empty(&dmar_rmrr_units))
+ printk(KERN_INFO "DMAR: No RMRR found\n");
+
+ if (list_empty(&dmar_atsr_units))
+ printk(KERN_INFO "DMAR: No ATSR found\n");
+
if (dmar_init_reserved_ranges()) {
if (force_on)
panic("tboot: Failed to reserve iommu ranges\n");
@@ -3481,7 +3637,7 @@ int __init intel_iommu_init(void)
init_iommu_pm_ops();
- register_iommu(&intel_iommu_ops);
+ bus_set_iommu(&pci_bus_type, &intel_iommu_ops);
bus_register_notifier(&pci_bus_type, &device_nb);
@@ -3563,6 +3719,8 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain,
found = 1;
}
+ spin_unlock_irqrestore(&device_domain_lock, flags);
+
if (found == 0) {
unsigned long tmp_flags;
spin_lock_irqsave(&domain->iommu_lock, tmp_flags);
@@ -3579,8 +3737,6 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain,
spin_unlock_irqrestore(&iommu->lock, tmp_flags);
}
}
-
- spin_unlock_irqrestore(&device_domain_lock, flags);
}
static void vm_domain_remove_all_dev_info(struct dmar_domain *domain)
@@ -3734,6 +3890,7 @@ static int intel_iommu_domain_init(struct iommu_domain *domain)
vm_domain_exit(dmar_domain);
return -ENOMEM;
}
+ domain_update_iommu_cap(dmar_domain);
domain->priv = dmar_domain;
return 0;
@@ -3859,14 +4016,15 @@ static int intel_iommu_unmap(struct iommu_domain *domain,
{
struct dmar_domain *dmar_domain = domain->priv;
size_t size = PAGE_SIZE << gfp_order;
+ int order;
- dma_pte_clear_range(dmar_domain, iova >> VTD_PAGE_SHIFT,
+ order = dma_pte_clear_range(dmar_domain, iova >> VTD_PAGE_SHIFT,
(iova + size - 1) >> VTD_PAGE_SHIFT);
if (dmar_domain->max_addr == iova + size)
dmar_domain->max_addr = iova;
- return gfp_order;
+ return order;
}
static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
@@ -3945,7 +4103,11 @@ static void __devinit quirk_calpella_no_shadow_gtt(struct pci_dev *dev)
if (!(ggc & GGC_MEMORY_VT_ENABLED)) {
printk(KERN_INFO "DMAR: BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n");
dmar_map_gfx = 0;
- }
+ } else if (dmar_map_gfx) {
+ /* we have to ensure the gfx device is idle before we flush */
+ printk(KERN_INFO "DMAR: Disabling batched IOTLB flush on Ironlake\n");
+ intel_iommu_strict = 1;
+ }
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0040, quirk_calpella_no_shadow_gtt);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0044, quirk_calpella_no_shadow_gtt);
diff --git a/drivers/pci/intr_remapping.c b/drivers/iommu/intr_remapping.c
index 3607faf28a4d..07c9f189f314 100644
--- a/drivers/pci/intr_remapping.c
+++ b/drivers/iommu/intr_remapping.c
@@ -13,7 +13,6 @@
#include "intr_remapping.h"
#include <acpi/acpi.h>
#include <asm/pci-direct.h>
-#include "pci.h"
static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
static struct hpet_scope ir_hpet[MAX_HPET_TBS];
@@ -22,6 +21,7 @@ int intr_remapping_enabled;
static int disable_intremap;
static int disable_sourceid_checking;
+static int no_x2apic_optout;
static __init int setup_nointremap(char *str)
{
@@ -35,18 +35,26 @@ static __init int setup_intremap(char *str)
if (!str)
return -EINVAL;
- if (!strncmp(str, "on", 2))
- disable_intremap = 0;
- else if (!strncmp(str, "off", 3))
- disable_intremap = 1;
- else if (!strncmp(str, "nosid", 5))
- disable_sourceid_checking = 1;
+ while (*str) {
+ if (!strncmp(str, "on", 2))
+ disable_intremap = 0;
+ else if (!strncmp(str, "off", 3))
+ disable_intremap = 1;
+ else if (!strncmp(str, "nosid", 5))
+ disable_sourceid_checking = 1;
+ else if (!strncmp(str, "no_x2apic_optout", 16))
+ no_x2apic_optout = 1;
+
+ str += strcspn(str, ",");
+ while (*str == ',')
+ str++;
+ }
return 0;
}
early_param("intremap", setup_intremap);
-static DEFINE_SPINLOCK(irq_2_ir_lock);
+static DEFINE_RAW_SPINLOCK(irq_2_ir_lock);
static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
{
@@ -63,12 +71,12 @@ int get_irte(int irq, struct irte *entry)
if (!entry || !irq_iommu)
return -1;
- spin_lock_irqsave(&irq_2_ir_lock, flags);
+ raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
index = irq_iommu->irte_index + irq_iommu->sub_handle;
*entry = *(irq_iommu->iommu->ir_table->base + index);
- spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+ raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
return 0;
}
@@ -102,7 +110,7 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
return -1;
}
- spin_lock_irqsave(&irq_2_ir_lock, flags);
+ raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
do {
for (i = index; i < index + count; i++)
if (table->base[i].present)
@@ -114,7 +122,7 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
index = (index + count) % INTR_REMAP_TABLE_ENTRIES;
if (index == start_index) {
- spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+ raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
printk(KERN_ERR "can't allocate an IRTE\n");
return -1;
}
@@ -128,7 +136,7 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
irq_iommu->sub_handle = 0;
irq_iommu->irte_mask = mask;
- spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+ raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
return index;
}
@@ -153,10 +161,10 @@ int map_irq_to_irte_handle(int irq, u16 *sub_handle)
if (!irq_iommu)
return -1;
- spin_lock_irqsave(&irq_2_ir_lock, flags);
+ raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
*sub_handle = irq_iommu->sub_handle;
index = irq_iommu->irte_index;
- spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+ raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
return index;
}
@@ -168,14 +176,14 @@ int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle)
if (!irq_iommu)
return -1;
- spin_lock_irqsave(&irq_2_ir_lock, flags);
+ raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
irq_iommu->iommu = iommu;
irq_iommu->irte_index = index;
irq_iommu->sub_handle = subhandle;
irq_iommu->irte_mask = 0;
- spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+ raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
return 0;
}
@@ -191,7 +199,7 @@ int modify_irte(int irq, struct irte *irte_modified)
if (!irq_iommu)
return -1;
- spin_lock_irqsave(&irq_2_ir_lock, flags);
+ raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
iommu = irq_iommu->iommu;
@@ -203,7 +211,7 @@ int modify_irte(int irq, struct irte *irte_modified)
__iommu_flush_cache(iommu, irte, sizeof(*irte));
rc = qi_flush_iec(iommu, index, 0);
- spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+ raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
return rc;
}
@@ -271,7 +279,7 @@ int free_irte(int irq)
if (!irq_iommu)
return -1;
- spin_lock_irqsave(&irq_2_ir_lock, flags);
+ raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
rc = clear_entries(irq_iommu);
@@ -280,7 +288,7 @@ int free_irte(int irq)
irq_iommu->sub_handle = 0;
irq_iommu->irte_mask = 0;
- spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+ raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
return rc;
}
@@ -410,7 +418,7 @@ static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
addr = virt_to_phys((void *)iommu->ir_table->base);
- spin_lock_irqsave(&iommu->register_lock, flags);
+ raw_spin_lock_irqsave(&iommu->register_lock, flags);
dmar_writeq(iommu->reg + DMAR_IRTA_REG,
(addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE);
@@ -421,7 +429,7 @@ static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
readl, (sts & DMA_GSTS_IRTPS), sts);
- spin_unlock_irqrestore(&iommu->register_lock, flags);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
/*
* global invalidation of interrupt entry cache before enabling
@@ -429,7 +437,7 @@ static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
*/
qi_global_iec(iommu);
- spin_lock_irqsave(&iommu->register_lock, flags);
+ raw_spin_lock_irqsave(&iommu->register_lock, flags);
/* Enable interrupt-remapping */
iommu->gcmd |= DMA_GCMD_IRE;
@@ -438,7 +446,7 @@ static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
readl, (sts & DMA_GSTS_IRES), sts);
- spin_unlock_irqrestore(&iommu->register_lock, flags);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
}
@@ -486,7 +494,7 @@ static void iommu_disable_intr_remapping(struct intel_iommu *iommu)
*/
qi_global_iec(iommu);
- spin_lock_irqsave(&iommu->register_lock, flags);
+ raw_spin_lock_irqsave(&iommu->register_lock, flags);
sts = dmar_readq(iommu->reg + DMAR_GSTS_REG);
if (!(sts & DMA_GSTS_IRES))
@@ -499,7 +507,16 @@ static void iommu_disable_intr_remapping(struct intel_iommu *iommu)
readl, !(sts & DMA_GSTS_IRES), sts);
end:
- spin_unlock_irqrestore(&iommu->register_lock, flags);
+ raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
+}
+
+static int __init dmar_x2apic_optout(void)
+{
+ struct acpi_table_dmar *dmar;
+ dmar = (struct acpi_table_dmar *)dmar_tbl;
+ if (!dmar || no_x2apic_optout)
+ return 0;
+ return dmar->flags & DMAR_X2APIC_OPT_OUT;
}
int __init intr_remapping_supported(void)
@@ -522,16 +539,25 @@ int __init intr_remapping_supported(void)
return 1;
}
-int __init enable_intr_remapping(int eim)
+int __init enable_intr_remapping(void)
{
struct dmar_drhd_unit *drhd;
int setup = 0;
+ int eim = 0;
if (parse_ioapics_under_ir() != 1) {
printk(KERN_INFO "Not enable interrupt remapping\n");
return -1;
}
+ if (x2apic_supported()) {
+ eim = !dmar_x2apic_optout();
+ WARN(!eim, KERN_WARNING
+ "Your BIOS is broken and requested that x2apic be disabled\n"
+ "This will leave your machine vulnerable to irq-injection attacks\n"
+ "Use 'intremap=no_x2apic_optout' to override BIOS request\n");
+ }
+
for_each_drhd_unit(drhd) {
struct intel_iommu *iommu = drhd->iommu;
@@ -607,8 +633,9 @@ int __init enable_intr_remapping(int eim)
goto error;
intr_remapping_enabled = 1;
+ pr_info("Enabled IRQ remapping in %s mode\n", eim ? "x2apic" : "xapic");
- return 0;
+ return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
error:
/*
@@ -746,6 +773,15 @@ int __init parse_ioapics_under_ir(void)
return ir_supported;
}
+int ir_dev_scope_init(void)
+{
+ if (!intr_remapping_enabled)
+ return 0;
+
+ return dmar_dev_scope_init();
+}
+rootfs_initcall(ir_dev_scope_init);
+
void disable_intr_remapping(void)
{
struct dmar_drhd_unit *drhd;
diff --git a/drivers/pci/intr_remapping.h b/drivers/iommu/intr_remapping.h
index 5662fecfee60..5662fecfee60 100644
--- a/drivers/pci/intr_remapping.h
+++ b/drivers/iommu/intr_remapping.h
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
new file mode 100644
index 000000000000..2fb2963df553
--- /dev/null
+++ b/drivers/iommu/iommu.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
+ * Author: Joerg Roedel <joerg.roedel@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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/device.h>
+#include <linux/kernel.h>
+#include <linux/bug.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/iommu.h>
+
+static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops)
+{
+}
+
+/**
+ * bus_set_iommu - set iommu-callbacks for the bus
+ * @bus: bus.
+ * @ops: the callbacks provided by the iommu-driver
+ *
+ * This function is called by an iommu driver to set the iommu methods
+ * used for a particular bus. Drivers for devices on that bus can use
+ * the iommu-api after these ops are registered.
+ * This special function is needed because IOMMUs are usually devices on
+ * the bus itself, so the iommu drivers are not initialized when the bus
+ * is set up. With this function the iommu-driver can set the iommu-ops
+ * afterwards.
+ */
+int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops)
+{
+ if (bus->iommu_ops != NULL)
+ return -EBUSY;
+
+ bus->iommu_ops = ops;
+
+ /* Do IOMMU specific setup for this bus-type */
+ iommu_bus_init(bus, ops);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(bus_set_iommu);
+
+bool iommu_present(struct bus_type *bus)
+{
+ return bus->iommu_ops != NULL;
+}
+EXPORT_SYMBOL_GPL(iommu_present);
+
+/**
+ * iommu_set_fault_handler() - set a fault handler for an iommu domain
+ * @domain: iommu domain
+ * @handler: fault handler
+ *
+ * This function should be used by IOMMU users which want to be notified
+ * whenever an IOMMU fault happens.
+ *
+ * The fault handler itself should return 0 on success, and an appropriate
+ * error code otherwise.
+ */
+void iommu_set_fault_handler(struct iommu_domain *domain,
+ iommu_fault_handler_t handler)
+{
+ BUG_ON(!domain);
+
+ domain->handler = handler;
+}
+EXPORT_SYMBOL_GPL(iommu_set_fault_handler);
+
+struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
+{
+ struct iommu_domain *domain;
+ int ret;
+
+ if (bus == NULL || bus->iommu_ops == NULL)
+ return NULL;
+
+ domain = kmalloc(sizeof(*domain), GFP_KERNEL);
+ if (!domain)
+ return NULL;
+
+ domain->ops = bus->iommu_ops;
+
+ ret = domain->ops->domain_init(domain);
+ if (ret)
+ goto out_free;
+
+ return domain;
+
+out_free:
+ kfree(domain);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(iommu_domain_alloc);
+
+void iommu_domain_free(struct iommu_domain *domain)
+{
+ if (likely(domain->ops->domain_destroy != NULL))
+ domain->ops->domain_destroy(domain);
+
+ kfree(domain);
+}
+EXPORT_SYMBOL_GPL(iommu_domain_free);
+
+int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
+{
+ if (unlikely(domain->ops->attach_dev == NULL))
+ return -ENODEV;
+
+ return domain->ops->attach_dev(domain, dev);
+}
+EXPORT_SYMBOL_GPL(iommu_attach_device);
+
+void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
+{
+ if (unlikely(domain->ops->detach_dev == NULL))
+ return;
+
+ domain->ops->detach_dev(domain, dev);
+}
+EXPORT_SYMBOL_GPL(iommu_detach_device);
+
+phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long iova)
+{
+ if (unlikely(domain->ops->iova_to_phys == NULL))
+ return 0;
+
+ return domain->ops->iova_to_phys(domain, iova);
+}
+EXPORT_SYMBOL_GPL(iommu_iova_to_phys);
+
+int iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ if (unlikely(domain->ops->domain_has_cap == NULL))
+ return 0;
+
+ return domain->ops->domain_has_cap(domain, cap);
+}
+EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
+
+int iommu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t paddr, int gfp_order, int prot)
+{
+ size_t size;
+
+ if (unlikely(domain->ops->map == NULL))
+ return -ENODEV;
+
+ size = PAGE_SIZE << gfp_order;
+
+ BUG_ON(!IS_ALIGNED(iova | paddr, size));
+
+ return domain->ops->map(domain, iova, paddr, gfp_order, prot);
+}
+EXPORT_SYMBOL_GPL(iommu_map);
+
+int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order)
+{
+ size_t size;
+
+ if (unlikely(domain->ops->unmap == NULL))
+ return -ENODEV;
+
+ size = PAGE_SIZE << gfp_order;
+
+ BUG_ON(!IS_ALIGNED(iova, size));
+
+ return domain->ops->unmap(domain, iova, gfp_order);
+}
+EXPORT_SYMBOL_GPL(iommu_unmap);
diff --git a/drivers/pci/iova.c b/drivers/iommu/iova.c
index c5c274ab5c5a..c5c274ab5c5a 100644
--- a/drivers/pci/iova.c
+++ b/drivers/iommu/iova.c
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
new file mode 100644
index 000000000000..5865dd2e28f9
--- /dev/null
+++ b/drivers/iommu/msm_iommu.c
@@ -0,0 +1,738 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/clk.h>
+
+#include <asm/cacheflush.h>
+#include <asm/sizes.h>
+
+#include <mach/iommu_hw-8xxx.h>
+#include <mach/iommu.h>
+
+#define MRC(reg, processor, op1, crn, crm, op2) \
+__asm__ __volatile__ ( \
+" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 "\n" \
+: "=r" (reg))
+
+#define RCP15_PRRR(reg) MRC(reg, p15, 0, c10, c2, 0)
+#define RCP15_NMRR(reg) MRC(reg, p15, 0, c10, c2, 1)
+
+static int msm_iommu_tex_class[4];
+
+DEFINE_SPINLOCK(msm_iommu_lock);
+
+struct msm_priv {
+ unsigned long *pgtable;
+ struct list_head list_attached;
+};
+
+static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+ int ret;
+
+ ret = clk_enable(drvdata->pclk);
+ if (ret)
+ goto fail;
+
+ if (drvdata->clk) {
+ ret = clk_enable(drvdata->clk);
+ if (ret)
+ clk_disable(drvdata->pclk);
+ }
+fail:
+ return ret;
+}
+
+static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+ if (drvdata->clk)
+ clk_disable(drvdata->clk);
+ clk_disable(drvdata->pclk);
+}
+
+static int __flush_iotlb(struct iommu_domain *domain)
+{
+ struct msm_priv *priv = domain->priv;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ int ret = 0;
+#ifndef CONFIG_IOMMU_PGTABLES_L2
+ unsigned long *fl_table = priv->pgtable;
+ int i;
+
+ if (!list_empty(&priv->list_attached)) {
+ dmac_flush_range(fl_table, fl_table + SZ_16K);
+
+ for (i = 0; i < NUM_FL_PTE; i++)
+ if ((fl_table[i] & 0x03) == FL_TYPE_TABLE) {
+ void *sl_table = __va(fl_table[i] &
+ FL_BASE_MASK);
+ dmac_flush_range(sl_table, sl_table + SZ_4K);
+ }
+ }
+#endif
+
+ list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
+ if (!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent)
+ BUG();
+
+ iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
+ BUG_ON(!iommu_drvdata);
+
+ ret = __enable_clocks(iommu_drvdata);
+ if (ret)
+ goto fail;
+
+ SET_CTX_TLBIALL(iommu_drvdata->base, ctx_drvdata->num, 0);
+ __disable_clocks(iommu_drvdata);
+ }
+fail:
+ return ret;
+}
+
+static void __reset_context(void __iomem *base, int ctx)
+{
+ SET_BPRCOSH(base, ctx, 0);
+ SET_BPRCISH(base, ctx, 0);
+ SET_BPRCNSH(base, ctx, 0);
+ SET_BPSHCFG(base, ctx, 0);
+ SET_BPMTCFG(base, ctx, 0);
+ SET_ACTLR(base, ctx, 0);
+ SET_SCTLR(base, ctx, 0);
+ SET_FSRRESTORE(base, ctx, 0);
+ SET_TTBR0(base, ctx, 0);
+ SET_TTBR1(base, ctx, 0);
+ SET_TTBCR(base, ctx, 0);
+ SET_BFBCR(base, ctx, 0);
+ SET_PAR(base, ctx, 0);
+ SET_FAR(base, ctx, 0);
+ SET_CTX_TLBIALL(base, ctx, 0);
+ SET_TLBFLPTER(base, ctx, 0);
+ SET_TLBSLPTER(base, ctx, 0);
+ SET_TLBLKCR(base, ctx, 0);
+ SET_PRRR(base, ctx, 0);
+ SET_NMRR(base, ctx, 0);
+}
+
+static void __program_context(void __iomem *base, int ctx, phys_addr_t pgtable)
+{
+ unsigned int prrr, nmrr;
+ __reset_context(base, ctx);
+
+ /* Set up HTW mode */
+ /* TLB miss configuration: perform HTW on miss */
+ SET_TLBMCFG(base, ctx, 0x3);
+
+ /* V2P configuration: HTW for access */
+ SET_V2PCFG(base, ctx, 0x3);
+
+ SET_TTBCR(base, ctx, 0);
+ SET_TTBR0_PA(base, ctx, (pgtable >> 14));
+
+ /* Invalidate the TLB for this context */
+ SET_CTX_TLBIALL(base, ctx, 0);
+
+ /* Set interrupt number to "secure" interrupt */
+ SET_IRPTNDX(base, ctx, 0);
+
+ /* Enable context fault interrupt */
+ SET_CFEIE(base, ctx, 1);
+
+ /* Stall access on a context fault and let the handler deal with it */
+ SET_CFCFG(base, ctx, 1);
+
+ /* Redirect all cacheable requests to L2 slave port. */
+ SET_RCISH(base, ctx, 1);
+ SET_RCOSH(base, ctx, 1);
+ SET_RCNSH(base, ctx, 1);
+
+ /* Turn on TEX Remap */
+ SET_TRE(base, ctx, 1);
+
+ /* Set TEX remap attributes */
+ RCP15_PRRR(prrr);
+ RCP15_NMRR(nmrr);
+ SET_PRRR(base, ctx, prrr);
+ SET_NMRR(base, ctx, nmrr);
+
+ /* Turn on BFB prefetch */
+ SET_BFBDFE(base, ctx, 1);
+
+#ifdef CONFIG_IOMMU_PGTABLES_L2
+ /* Configure page tables as inner-cacheable and shareable to reduce
+ * the TLB miss penalty.
+ */
+ SET_TTBR0_SH(base, ctx, 1);
+ SET_TTBR1_SH(base, ctx, 1);
+
+ SET_TTBR0_NOS(base, ctx, 1);
+ SET_TTBR1_NOS(base, ctx, 1);
+
+ SET_TTBR0_IRGNH(base, ctx, 0); /* WB, WA */
+ SET_TTBR0_IRGNL(base, ctx, 1);
+
+ SET_TTBR1_IRGNH(base, ctx, 0); /* WB, WA */
+ SET_TTBR1_IRGNL(base, ctx, 1);
+
+ SET_TTBR0_ORGN(base, ctx, 1); /* WB, WA */
+ SET_TTBR1_ORGN(base, ctx, 1); /* WB, WA */
+#endif
+
+ /* Enable the MMU */
+ SET_M(base, ctx, 1);
+}
+
+static int msm_iommu_domain_init(struct iommu_domain *domain)
+{
+ struct msm_priv *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+
+ if (!priv)
+ goto fail_nomem;
+
+ INIT_LIST_HEAD(&priv->list_attached);
+ priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL,
+ get_order(SZ_16K));
+
+ if (!priv->pgtable)
+ goto fail_nomem;
+
+ memset(priv->pgtable, 0, SZ_16K);
+ domain->priv = priv;
+ return 0;
+
+fail_nomem:
+ kfree(priv);
+ return -ENOMEM;
+}
+
+static void msm_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct msm_priv *priv;
+ unsigned long flags;
+ unsigned long *fl_table;
+ int i;
+
+ spin_lock_irqsave(&msm_iommu_lock, flags);
+ priv = domain->priv;
+ domain->priv = NULL;
+
+ if (priv) {
+ fl_table = priv->pgtable;
+
+ for (i = 0; i < NUM_FL_PTE; i++)
+ if ((fl_table[i] & 0x03) == FL_TYPE_TABLE)
+ free_page((unsigned long) __va(((fl_table[i]) &
+ FL_BASE_MASK)));
+
+ free_pages((unsigned long)priv->pgtable, get_order(SZ_16K));
+ priv->pgtable = NULL;
+ }
+
+ kfree(priv);
+ spin_unlock_irqrestore(&msm_iommu_lock, flags);
+}
+
+static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ struct msm_priv *priv;
+ struct msm_iommu_ctx_dev *ctx_dev;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ struct msm_iommu_ctx_drvdata *tmp_drvdata;
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&msm_iommu_lock, flags);
+
+ priv = domain->priv;
+
+ if (!priv || !dev) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ iommu_drvdata = dev_get_drvdata(dev->parent);
+ ctx_drvdata = dev_get_drvdata(dev);
+ ctx_dev = dev->platform_data;
+
+ if (!iommu_drvdata || !ctx_drvdata || !ctx_dev) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!list_empty(&ctx_drvdata->attached_elm)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ list_for_each_entry(tmp_drvdata, &priv->list_attached, attached_elm)
+ if (tmp_drvdata == ctx_drvdata) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ ret = __enable_clocks(iommu_drvdata);
+ if (ret)
+ goto fail;
+
+ __program_context(iommu_drvdata->base, ctx_dev->num,
+ __pa(priv->pgtable));
+
+ __disable_clocks(iommu_drvdata);
+ list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
+ ret = __flush_iotlb(domain);
+
+fail:
+ spin_unlock_irqrestore(&msm_iommu_lock, flags);
+ return ret;
+}
+
+static void msm_iommu_detach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct msm_priv *priv;
+ struct msm_iommu_ctx_dev *ctx_dev;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&msm_iommu_lock, flags);
+ priv = domain->priv;
+
+ if (!priv || !dev)
+ goto fail;
+
+ iommu_drvdata = dev_get_drvdata(dev->parent);
+ ctx_drvdata = dev_get_drvdata(dev);
+ ctx_dev = dev->platform_data;
+
+ if (!iommu_drvdata || !ctx_drvdata || !ctx_dev)
+ goto fail;
+
+ ret = __flush_iotlb(domain);
+ if (ret)
+ goto fail;
+
+ ret = __enable_clocks(iommu_drvdata);
+ if (ret)
+ goto fail;
+
+ __reset_context(iommu_drvdata->base, ctx_dev->num);
+ __disable_clocks(iommu_drvdata);
+ list_del_init(&ctx_drvdata->attached_elm);
+
+fail:
+ spin_unlock_irqrestore(&msm_iommu_lock, flags);
+}
+
+static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
+ phys_addr_t pa, int order, int prot)
+{
+ struct msm_priv *priv;
+ unsigned long flags;
+ unsigned long *fl_table;
+ unsigned long *fl_pte;
+ unsigned long fl_offset;
+ unsigned long *sl_table;
+ unsigned long *sl_pte;
+ unsigned long sl_offset;
+ unsigned int pgprot;
+ size_t len = 0x1000UL << order;
+ int ret = 0, tex, sh;
+
+ spin_lock_irqsave(&msm_iommu_lock, flags);
+
+ sh = (prot & MSM_IOMMU_ATTR_SH) ? 1 : 0;
+ tex = msm_iommu_tex_class[prot & MSM_IOMMU_CP_MASK];
+
+ if (tex < 0 || tex > NUM_TEX_CLASS - 1) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ priv = domain->priv;
+ if (!priv) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ fl_table = priv->pgtable;
+
+ if (len != SZ_16M && len != SZ_1M &&
+ len != SZ_64K && len != SZ_4K) {
+ pr_debug("Bad size: %d\n", len);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!fl_table) {
+ pr_debug("Null page table\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (len == SZ_16M || len == SZ_1M) {
+ pgprot = sh ? FL_SHARED : 0;
+ pgprot |= tex & 0x01 ? FL_BUFFERABLE : 0;
+ pgprot |= tex & 0x02 ? FL_CACHEABLE : 0;
+ pgprot |= tex & 0x04 ? FL_TEX0 : 0;
+ } else {
+ pgprot = sh ? SL_SHARED : 0;
+ pgprot |= tex & 0x01 ? SL_BUFFERABLE : 0;
+ pgprot |= tex & 0x02 ? SL_CACHEABLE : 0;
+ pgprot |= tex & 0x04 ? SL_TEX0 : 0;
+ }
+
+ fl_offset = FL_OFFSET(va); /* Upper 12 bits */
+ fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
+
+ if (len == SZ_16M) {
+ int i = 0;
+ for (i = 0; i < 16; i++)
+ *(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION |
+ FL_AP_READ | FL_AP_WRITE | FL_TYPE_SECT |
+ FL_SHARED | FL_NG | pgprot;
+ }
+
+ if (len == SZ_1M)
+ *fl_pte = (pa & 0xFFF00000) | FL_AP_READ | FL_AP_WRITE | FL_NG |
+ FL_TYPE_SECT | FL_SHARED | pgprot;
+
+ /* Need a 2nd level table */
+ if ((len == SZ_4K || len == SZ_64K) && (*fl_pte) == 0) {
+ unsigned long *sl;
+ sl = (unsigned long *) __get_free_pages(GFP_ATOMIC,
+ get_order(SZ_4K));
+
+ if (!sl) {
+ pr_debug("Could not allocate second level table\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ memset(sl, 0, SZ_4K);
+ *fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | FL_TYPE_TABLE);
+ }
+
+ sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+ sl_offset = SL_OFFSET(va);
+ sl_pte = sl_table + sl_offset;
+
+
+ if (len == SZ_4K)
+ *sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_AP0 | SL_AP1 | SL_NG |
+ SL_SHARED | SL_TYPE_SMALL | pgprot;
+
+ if (len == SZ_64K) {
+ int i;
+
+ for (i = 0; i < 16; i++)
+ *(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_AP0 |
+ SL_NG | SL_AP1 | SL_SHARED | SL_TYPE_LARGE | pgprot;
+ }
+
+ ret = __flush_iotlb(domain);
+fail:
+ spin_unlock_irqrestore(&msm_iommu_lock, flags);
+ return ret;
+}
+
+static int msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
+ int order)
+{
+ struct msm_priv *priv;
+ unsigned long flags;
+ unsigned long *fl_table;
+ unsigned long *fl_pte;
+ unsigned long fl_offset;
+ unsigned long *sl_table;
+ unsigned long *sl_pte;
+ unsigned long sl_offset;
+ size_t len = 0x1000UL << order;
+ int i, ret = 0;
+
+ spin_lock_irqsave(&msm_iommu_lock, flags);
+
+ priv = domain->priv;
+
+ if (!priv) {
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ fl_table = priv->pgtable;
+
+ if (len != SZ_16M && len != SZ_1M &&
+ len != SZ_64K && len != SZ_4K) {
+ pr_debug("Bad length: %d\n", len);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!fl_table) {
+ pr_debug("Null page table\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ fl_offset = FL_OFFSET(va); /* Upper 12 bits */
+ fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
+
+ if (*fl_pte == 0) {
+ pr_debug("First level PTE is 0\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ /* Unmap supersection */
+ if (len == SZ_16M)
+ for (i = 0; i < 16; i++)
+ *(fl_pte+i) = 0;
+
+ if (len == SZ_1M)
+ *fl_pte = 0;
+
+ sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+ sl_offset = SL_OFFSET(va);
+ sl_pte = sl_table + sl_offset;
+
+ if (len == SZ_64K) {
+ for (i = 0; i < 16; i++)
+ *(sl_pte+i) = 0;
+ }
+
+ if (len == SZ_4K)
+ *sl_pte = 0;
+
+ if (len == SZ_4K || len == SZ_64K) {
+ int used = 0;
+
+ for (i = 0; i < NUM_SL_PTE; i++)
+ if (sl_table[i])
+ used = 1;
+ if (!used) {
+ free_page((unsigned long)sl_table);
+ *fl_pte = 0;
+ }
+ }
+
+ ret = __flush_iotlb(domain);
+
+ /*
+ * the IOMMU API requires us to return the order of the unmapped
+ * page (on success).
+ */
+ if (!ret)
+ ret = order;
+fail:
+ spin_unlock_irqrestore(&msm_iommu_lock, flags);
+ return ret;
+}
+
+static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long va)
+{
+ struct msm_priv *priv;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ unsigned int par;
+ unsigned long flags;
+ void __iomem *base;
+ phys_addr_t ret = 0;
+ int ctx;
+
+ spin_lock_irqsave(&msm_iommu_lock, flags);
+
+ priv = domain->priv;
+ if (list_empty(&priv->list_attached))
+ goto fail;
+
+ ctx_drvdata = list_entry(priv->list_attached.next,
+ struct msm_iommu_ctx_drvdata, attached_elm);
+ iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
+
+ base = iommu_drvdata->base;
+ ctx = ctx_drvdata->num;
+
+ ret = __enable_clocks(iommu_drvdata);
+ if (ret)
+ goto fail;
+
+ /* Invalidate context TLB */
+ SET_CTX_TLBIALL(base, ctx, 0);
+ SET_V2PPR(base, ctx, va & V2Pxx_VA);
+
+ par = GET_PAR(base, ctx);
+
+ /* We are dealing with a supersection */
+ if (GET_NOFAULT_SS(base, ctx))
+ ret = (par & 0xFF000000) | (va & 0x00FFFFFF);
+ else /* Upper 20 bits from PAR, lower 12 from VA */
+ ret = (par & 0xFFFFF000) | (va & 0x00000FFF);
+
+ if (GET_FAULT(base, ctx))
+ ret = 0;
+
+ __disable_clocks(iommu_drvdata);
+fail:
+ spin_unlock_irqrestore(&msm_iommu_lock, flags);
+ return ret;
+}
+
+static int msm_iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ return 0;
+}
+
+static void print_ctx_regs(void __iomem *base, int ctx)
+{
+ unsigned int fsr = GET_FSR(base, ctx);
+ pr_err("FAR = %08x PAR = %08x\n",
+ GET_FAR(base, ctx), GET_PAR(base, ctx));
+ pr_err("FSR = %08x [%s%s%s%s%s%s%s%s%s%s]\n", fsr,
+ (fsr & 0x02) ? "TF " : "",
+ (fsr & 0x04) ? "AFF " : "",
+ (fsr & 0x08) ? "APF " : "",
+ (fsr & 0x10) ? "TLBMF " : "",
+ (fsr & 0x20) ? "HTWDEEF " : "",
+ (fsr & 0x40) ? "HTWSEEF " : "",
+ (fsr & 0x80) ? "MHF " : "",
+ (fsr & 0x10000) ? "SL " : "",
+ (fsr & 0x40000000) ? "SS " : "",
+ (fsr & 0x80000000) ? "MULTI " : "");
+
+ pr_err("FSYNR0 = %08x FSYNR1 = %08x\n",
+ GET_FSYNR0(base, ctx), GET_FSYNR1(base, ctx));
+ pr_err("TTBR0 = %08x TTBR1 = %08x\n",
+ GET_TTBR0(base, ctx), GET_TTBR1(base, ctx));
+ pr_err("SCTLR = %08x ACTLR = %08x\n",
+ GET_SCTLR(base, ctx), GET_ACTLR(base, ctx));
+ pr_err("PRRR = %08x NMRR = %08x\n",
+ GET_PRRR(base, ctx), GET_NMRR(base, ctx));
+}
+
+irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id)
+{
+ struct msm_iommu_drvdata *drvdata = dev_id;
+ void __iomem *base;
+ unsigned int fsr;
+ int i, ret;
+
+ spin_lock(&msm_iommu_lock);
+
+ if (!drvdata) {
+ pr_err("Invalid device ID in context interrupt handler\n");
+ goto fail;
+ }
+
+ base = drvdata->base;
+
+ pr_err("Unexpected IOMMU page fault!\n");
+ pr_err("base = %08x\n", (unsigned int) base);
+
+ ret = __enable_clocks(drvdata);
+ if (ret)
+ goto fail;
+
+ for (i = 0; i < drvdata->ncb; i++) {
+ fsr = GET_FSR(base, i);
+ if (fsr) {
+ pr_err("Fault occurred in context %d.\n", i);
+ pr_err("Interesting registers:\n");
+ print_ctx_regs(base, i);
+ SET_FSR(base, i, 0x4000000F);
+ }
+ }
+ __disable_clocks(drvdata);
+fail:
+ spin_unlock(&msm_iommu_lock);
+ return 0;
+}
+
+static struct iommu_ops msm_iommu_ops = {
+ .domain_init = msm_iommu_domain_init,
+ .domain_destroy = msm_iommu_domain_destroy,
+ .attach_dev = msm_iommu_attach_dev,
+ .detach_dev = msm_iommu_detach_dev,
+ .map = msm_iommu_map,
+ .unmap = msm_iommu_unmap,
+ .iova_to_phys = msm_iommu_iova_to_phys,
+ .domain_has_cap = msm_iommu_domain_has_cap
+};
+
+static int __init get_tex_class(int icp, int ocp, int mt, int nos)
+{
+ int i = 0;
+ unsigned int prrr = 0;
+ unsigned int nmrr = 0;
+ int c_icp, c_ocp, c_mt, c_nos;
+
+ RCP15_PRRR(prrr);
+ RCP15_NMRR(nmrr);
+
+ for (i = 0; i < NUM_TEX_CLASS; i++) {
+ c_nos = PRRR_NOS(prrr, i);
+ c_mt = PRRR_MT(prrr, i);
+ c_icp = NMRR_ICP(nmrr, i);
+ c_ocp = NMRR_OCP(nmrr, i);
+
+ if (icp == c_icp && ocp == c_ocp && c_mt == mt && c_nos == nos)
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+static void __init setup_iommu_tex_classes(void)
+{
+ msm_iommu_tex_class[MSM_IOMMU_ATTR_NONCACHED] =
+ get_tex_class(CP_NONCACHED, CP_NONCACHED, MT_NORMAL, 1);
+
+ msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_WA] =
+ get_tex_class(CP_WB_WA, CP_WB_WA, MT_NORMAL, 1);
+
+ msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_NWA] =
+ get_tex_class(CP_WB_NWA, CP_WB_NWA, MT_NORMAL, 1);
+
+ msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WT] =
+ get_tex_class(CP_WT, CP_WT, MT_NORMAL, 1);
+}
+
+static int __init msm_iommu_init(void)
+{
+ setup_iommu_tex_classes();
+ bus_set_iommu(&platform_bus_type, &msm_iommu_ops);
+ return 0;
+}
+
+subsys_initcall(msm_iommu_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>");
diff --git a/drivers/iommu/msm_iommu_dev.c b/drivers/iommu/msm_iommu_dev.c
new file mode 100644
index 000000000000..8e8fb079852d
--- /dev/null
+++ b/drivers/iommu/msm_iommu_dev.c
@@ -0,0 +1,422 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/iommu.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <mach/iommu_hw-8xxx.h>
+#include <mach/iommu.h>
+#include <mach/clk.h>
+
+struct iommu_ctx_iter_data {
+ /* input */
+ const char *name;
+
+ /* output */
+ struct device *dev;
+};
+
+static struct platform_device *msm_iommu_root_dev;
+
+static int each_iommu_ctx(struct device *dev, void *data)
+{
+ struct iommu_ctx_iter_data *res = data;
+ struct msm_iommu_ctx_dev *c = dev->platform_data;
+
+ if (!res || !c || !c->name || !res->name)
+ return -EINVAL;
+
+ if (!strcmp(res->name, c->name)) {
+ res->dev = dev;
+ return 1;
+ }
+ return 0;
+}
+
+static int each_iommu(struct device *dev, void *data)
+{
+ return device_for_each_child(dev, data, each_iommu_ctx);
+}
+
+struct device *msm_iommu_get_ctx(const char *ctx_name)
+{
+ struct iommu_ctx_iter_data r;
+ int found;
+
+ if (!msm_iommu_root_dev) {
+ pr_err("No root IOMMU device.\n");
+ goto fail;
+ }
+
+ r.name = ctx_name;
+ found = device_for_each_child(&msm_iommu_root_dev->dev, &r, each_iommu);
+
+ if (!found) {
+ pr_err("Could not find context <%s>\n", ctx_name);
+ goto fail;
+ }
+
+ return r.dev;
+fail:
+ return NULL;
+}
+EXPORT_SYMBOL(msm_iommu_get_ctx);
+
+static void msm_iommu_reset(void __iomem *base, int ncb)
+{
+ int ctx;
+
+ SET_RPUE(base, 0);
+ SET_RPUEIE(base, 0);
+ SET_ESRRESTORE(base, 0);
+ SET_TBE(base, 0);
+ SET_CR(base, 0);
+ SET_SPDMBE(base, 0);
+ SET_TESTBUSCR(base, 0);
+ SET_TLBRSW(base, 0);
+ SET_GLOBAL_TLBIALL(base, 0);
+ SET_RPU_ACR(base, 0);
+ SET_TLBLKCRWE(base, 1);
+
+ for (ctx = 0; ctx < ncb; ctx++) {
+ SET_BPRCOSH(base, ctx, 0);
+ SET_BPRCISH(base, ctx, 0);
+ SET_BPRCNSH(base, ctx, 0);
+ SET_BPSHCFG(base, ctx, 0);
+ SET_BPMTCFG(base, ctx, 0);
+ SET_ACTLR(base, ctx, 0);
+ SET_SCTLR(base, ctx, 0);
+ SET_FSRRESTORE(base, ctx, 0);
+ SET_TTBR0(base, ctx, 0);
+ SET_TTBR1(base, ctx, 0);
+ SET_TTBCR(base, ctx, 0);
+ SET_BFBCR(base, ctx, 0);
+ SET_PAR(base, ctx, 0);
+ SET_FAR(base, ctx, 0);
+ SET_CTX_TLBIALL(base, ctx, 0);
+ SET_TLBFLPTER(base, ctx, 0);
+ SET_TLBSLPTER(base, ctx, 0);
+ SET_TLBLKCR(base, ctx, 0);
+ SET_PRRR(base, ctx, 0);
+ SET_NMRR(base, ctx, 0);
+ SET_CONTEXTIDR(base, ctx, 0);
+ }
+}
+
+static int msm_iommu_probe(struct platform_device *pdev)
+{
+ struct resource *r, *r2;
+ struct clk *iommu_clk;
+ struct clk *iommu_pclk;
+ struct msm_iommu_drvdata *drvdata;
+ struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data;
+ void __iomem *regs_base;
+ resource_size_t len;
+ int ret, irq, par;
+
+ if (pdev->id == -1) {
+ msm_iommu_root_dev = pdev;
+ return 0;
+ }
+
+ drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
+
+ if (!drvdata) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ if (!iommu_dev) {
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ iommu_pclk = clk_get(NULL, "smmu_pclk");
+ if (IS_ERR(iommu_pclk)) {
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ ret = clk_enable(iommu_pclk);
+ if (ret)
+ goto fail_enable;
+
+ iommu_clk = clk_get(&pdev->dev, "iommu_clk");
+
+ if (!IS_ERR(iommu_clk)) {
+ if (clk_get_rate(iommu_clk) == 0)
+ clk_set_min_rate(iommu_clk, 1);
+
+ ret = clk_enable(iommu_clk);
+ if (ret) {
+ clk_put(iommu_clk);
+ goto fail_pclk;
+ }
+ } else
+ iommu_clk = NULL;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "physbase");
+
+ if (!r) {
+ ret = -ENODEV;
+ goto fail_clk;
+ }
+
+ len = resource_size(r);
+
+ r2 = request_mem_region(r->start, len, r->name);
+ if (!r2) {
+ pr_err("Could not request memory region: start=%p, len=%d\n",
+ (void *) r->start, len);
+ ret = -EBUSY;
+ goto fail_clk;
+ }
+
+ regs_base = ioremap(r2->start, len);
+
+ if (!regs_base) {
+ pr_err("Could not ioremap: start=%p, len=%d\n",
+ (void *) r2->start, len);
+ ret = -EBUSY;
+ goto fail_mem;
+ }
+
+ irq = platform_get_irq_byname(pdev, "secure_irq");
+ if (irq < 0) {
+ ret = -ENODEV;
+ goto fail_io;
+ }
+
+ msm_iommu_reset(regs_base, iommu_dev->ncb);
+
+ SET_M(regs_base, 0, 1);
+ SET_PAR(regs_base, 0, 0);
+ SET_V2PCFG(regs_base, 0, 1);
+ SET_V2PPR(regs_base, 0, 0);
+ par = GET_PAR(regs_base, 0);
+ SET_V2PCFG(regs_base, 0, 0);
+ SET_M(regs_base, 0, 0);
+
+ if (!par) {
+ pr_err("%s: Invalid PAR value detected\n", iommu_dev->name);
+ ret = -ENODEV;
+ goto fail_io;
+ }
+
+ ret = request_irq(irq, msm_iommu_fault_handler, 0,
+ "msm_iommu_secure_irpt_handler", drvdata);
+ if (ret) {
+ pr_err("Request IRQ %d failed with ret=%d\n", irq, ret);
+ goto fail_io;
+ }
+
+
+ drvdata->pclk = iommu_pclk;
+ drvdata->clk = iommu_clk;
+ drvdata->base = regs_base;
+ drvdata->irq = irq;
+ drvdata->ncb = iommu_dev->ncb;
+
+ pr_info("device %s mapped at %p, irq %d with %d ctx banks\n",
+ iommu_dev->name, regs_base, irq, iommu_dev->ncb);
+
+ platform_set_drvdata(pdev, drvdata);
+
+ if (iommu_clk)
+ clk_disable(iommu_clk);
+
+ clk_disable(iommu_pclk);
+
+ return 0;
+fail_io:
+ iounmap(regs_base);
+fail_mem:
+ release_mem_region(r->start, len);
+fail_clk:
+ if (iommu_clk) {
+ clk_disable(iommu_clk);
+ clk_put(iommu_clk);
+ }
+fail_pclk:
+ clk_disable(iommu_pclk);
+fail_enable:
+ clk_put(iommu_pclk);
+fail:
+ kfree(drvdata);
+ return ret;
+}
+
+static int msm_iommu_remove(struct platform_device *pdev)
+{
+ struct msm_iommu_drvdata *drv = NULL;
+
+ drv = platform_get_drvdata(pdev);
+ if (drv) {
+ if (drv->clk)
+ clk_put(drv->clk);
+ clk_put(drv->pclk);
+ memset(drv, 0, sizeof(*drv));
+ kfree(drv);
+ platform_set_drvdata(pdev, NULL);
+ }
+ return 0;
+}
+
+static int msm_iommu_ctx_probe(struct platform_device *pdev)
+{
+ struct msm_iommu_ctx_dev *c = pdev->dev.platform_data;
+ struct msm_iommu_drvdata *drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
+ int i, ret;
+ if (!c || !pdev->dev.parent) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ drvdata = dev_get_drvdata(pdev->dev.parent);
+
+ if (!drvdata) {
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ ctx_drvdata = kzalloc(sizeof(*ctx_drvdata), GFP_KERNEL);
+ if (!ctx_drvdata) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ ctx_drvdata->num = c->num;
+ ctx_drvdata->pdev = pdev;
+
+ INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
+ platform_set_drvdata(pdev, ctx_drvdata);
+
+ ret = clk_enable(drvdata->pclk);
+ if (ret)
+ goto fail;
+
+ if (drvdata->clk) {
+ ret = clk_enable(drvdata->clk);
+ if (ret) {
+ clk_disable(drvdata->pclk);
+ goto fail;
+ }
+ }
+
+ /* Program the M2V tables for this context */
+ for (i = 0; i < MAX_NUM_MIDS; i++) {
+ int mid = c->mids[i];
+ if (mid == -1)
+ break;
+
+ SET_M2VCBR_N(drvdata->base, mid, 0);
+ SET_CBACR_N(drvdata->base, c->num, 0);
+
+ /* Set VMID = 0 */
+ SET_VMID(drvdata->base, mid, 0);
+
+ /* Set the context number for that MID to this context */
+ SET_CBNDX(drvdata->base, mid, c->num);
+
+ /* Set MID associated with this context bank to 0*/
+ SET_CBVMID(drvdata->base, c->num, 0);
+
+ /* Set the ASID for TLB tagging for this context */
+ SET_CONTEXTIDR_ASID(drvdata->base, c->num, c->num);
+
+ /* Set security bit override to be Non-secure */
+ SET_NSCFG(drvdata->base, mid, 3);
+ }
+
+ if (drvdata->clk)
+ clk_disable(drvdata->clk);
+ clk_disable(drvdata->pclk);
+
+ dev_info(&pdev->dev, "context %s using bank %d\n", c->name, c->num);
+ return 0;
+fail:
+ kfree(ctx_drvdata);
+ return ret;
+}
+
+static int msm_iommu_ctx_remove(struct platform_device *pdev)
+{
+ struct msm_iommu_ctx_drvdata *drv = NULL;
+ drv = platform_get_drvdata(pdev);
+ if (drv) {
+ memset(drv, 0, sizeof(struct msm_iommu_ctx_drvdata));
+ kfree(drv);
+ platform_set_drvdata(pdev, NULL);
+ }
+ return 0;
+}
+
+static struct platform_driver msm_iommu_driver = {
+ .driver = {
+ .name = "msm_iommu",
+ },
+ .probe = msm_iommu_probe,
+ .remove = msm_iommu_remove,
+};
+
+static struct platform_driver msm_iommu_ctx_driver = {
+ .driver = {
+ .name = "msm_iommu_ctx",
+ },
+ .probe = msm_iommu_ctx_probe,
+ .remove = msm_iommu_ctx_remove,
+};
+
+static int __init msm_iommu_driver_init(void)
+{
+ int ret;
+ ret = platform_driver_register(&msm_iommu_driver);
+ if (ret != 0) {
+ pr_err("Failed to register IOMMU driver\n");
+ goto error;
+ }
+
+ ret = platform_driver_register(&msm_iommu_ctx_driver);
+ if (ret != 0) {
+ pr_err("Failed to register IOMMU context driver\n");
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+static void __exit msm_iommu_driver_exit(void)
+{
+ platform_driver_unregister(&msm_iommu_ctx_driver);
+ platform_driver_unregister(&msm_iommu_driver);
+}
+
+subsys_initcall(msm_iommu_driver_init);
+module_exit(msm_iommu_driver_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>");
diff --git a/drivers/iommu/omap-iommu-debug.c b/drivers/iommu/omap-iommu-debug.c
new file mode 100644
index 000000000000..288da5c1499d
--- /dev/null
+++ b/drivers/iommu/omap-iommu-debug.c
@@ -0,0 +1,419 @@
+/*
+ * omap iommu: debugfs interface
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+
+#include <plat/iommu.h>
+#include <plat/iovmm.h>
+
+#include <plat/iopgtable.h>
+
+#define MAXCOLUMN 100 /* for short messages */
+
+static DEFINE_MUTEX(iommu_debug_lock);
+
+static struct dentry *iommu_debug_root;
+
+static ssize_t debug_read_ver(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ u32 ver = omap_iommu_arch_version();
+ char buf[MAXCOLUMN], *p = buf;
+
+ p += sprintf(p, "H/W version: %d.%d\n", (ver >> 4) & 0xf , ver & 0xf);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+}
+
+static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ char *p, *buf;
+ ssize_t bytes;
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ mutex_lock(&iommu_debug_lock);
+
+ bytes = omap_iommu_dump_ctx(obj, p, count);
+ bytes = simple_read_from_buffer(userbuf, count, ppos, buf, bytes);
+
+ mutex_unlock(&iommu_debug_lock);
+ kfree(buf);
+
+ return bytes;
+}
+
+static ssize_t debug_read_tlb(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ char *p, *buf;
+ ssize_t bytes, rest;
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ mutex_lock(&iommu_debug_lock);
+
+ p += sprintf(p, "%8s %8s\n", "cam:", "ram:");
+ p += sprintf(p, "-----------------------------------------\n");
+ rest = count - (p - buf);
+ p += omap_dump_tlb_entries(obj, p, rest);
+
+ bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+
+ mutex_unlock(&iommu_debug_lock);
+ kfree(buf);
+
+ return bytes;
+}
+
+static ssize_t debug_write_pagetable(struct file *file,
+ const char __user *userbuf, size_t count, loff_t *ppos)
+{
+ struct iotlb_entry e;
+ struct cr_regs cr;
+ int err;
+ struct omap_iommu *obj = file->private_data;
+ char buf[MAXCOLUMN], *p = buf;
+
+ count = min(count, sizeof(buf));
+
+ mutex_lock(&iommu_debug_lock);
+ if (copy_from_user(p, userbuf, count)) {
+ mutex_unlock(&iommu_debug_lock);
+ return -EFAULT;
+ }
+
+ sscanf(p, "%x %x", &cr.cam, &cr.ram);
+ if (!cr.cam || !cr.ram) {
+ mutex_unlock(&iommu_debug_lock);
+ return -EINVAL;
+ }
+
+ omap_iotlb_cr_to_e(&cr, &e);
+ err = omap_iopgtable_store_entry(obj, &e);
+ if (err)
+ dev_err(obj->dev, "%s: fail to store cr\n", __func__);
+
+ mutex_unlock(&iommu_debug_lock);
+ return count;
+}
+
+#define dump_ioptable_entry_one(lv, da, val) \
+ ({ \
+ int __err = 0; \
+ ssize_t bytes; \
+ const int maxcol = 22; \
+ const char *str = "%d: %08x %08x\n"; \
+ bytes = snprintf(p, maxcol, str, lv, da, val); \
+ p += bytes; \
+ len -= bytes; \
+ if (len < maxcol) \
+ __err = -ENOMEM; \
+ __err; \
+ })
+
+static ssize_t dump_ioptable(struct omap_iommu *obj, char *buf, ssize_t len)
+{
+ int i;
+ u32 *iopgd;
+ char *p = buf;
+
+ spin_lock(&obj->page_table_lock);
+
+ iopgd = iopgd_offset(obj, 0);
+ for (i = 0; i < PTRS_PER_IOPGD; i++, iopgd++) {
+ int j, err;
+ u32 *iopte;
+ u32 da;
+
+ if (!*iopgd)
+ continue;
+
+ if (!(*iopgd & IOPGD_TABLE)) {
+ da = i << IOPGD_SHIFT;
+
+ err = dump_ioptable_entry_one(1, da, *iopgd);
+ if (err)
+ goto out;
+ continue;
+ }
+
+ iopte = iopte_offset(iopgd, 0);
+
+ for (j = 0; j < PTRS_PER_IOPTE; j++, iopte++) {
+ if (!*iopte)
+ continue;
+
+ da = (i << IOPGD_SHIFT) + (j << IOPTE_SHIFT);
+ err = dump_ioptable_entry_one(2, da, *iopgd);
+ if (err)
+ goto out;
+ }
+ }
+out:
+ spin_unlock(&obj->page_table_lock);
+
+ return p - buf;
+}
+
+static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ char *p, *buf;
+ size_t bytes;
+
+ buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ p += sprintf(p, "L: %8s %8s\n", "da:", "pa:");
+ p += sprintf(p, "-----------------------------------------\n");
+
+ mutex_lock(&iommu_debug_lock);
+
+ bytes = PAGE_SIZE - (p - buf);
+ p += dump_ioptable(obj, p, bytes);
+
+ bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+
+ mutex_unlock(&iommu_debug_lock);
+ free_page((unsigned long)buf);
+
+ return bytes;
+}
+
+static ssize_t debug_read_mmap(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ char *p, *buf;
+ struct iovm_struct *tmp;
+ int uninitialized_var(i);
+ ssize_t bytes;
+
+ buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ p += sprintf(p, "%-3s %-8s %-8s %6s %8s\n",
+ "No", "start", "end", "size", "flags");
+ p += sprintf(p, "-------------------------------------------------\n");
+
+ mutex_lock(&iommu_debug_lock);
+
+ list_for_each_entry(tmp, &obj->mmap, list) {
+ size_t len;
+ const char *str = "%3d %08x-%08x %6x %8x\n";
+ const int maxcol = 39;
+
+ len = tmp->da_end - tmp->da_start;
+ p += snprintf(p, maxcol, str,
+ i, tmp->da_start, tmp->da_end, len, tmp->flags);
+
+ if (PAGE_SIZE - (p - buf) < maxcol)
+ break;
+ i++;
+ }
+
+ bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+
+ mutex_unlock(&iommu_debug_lock);
+ free_page((unsigned long)buf);
+
+ return bytes;
+}
+
+static ssize_t debug_read_mem(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ char *p, *buf;
+ struct iovm_struct *area;
+ ssize_t bytes;
+
+ count = min_t(ssize_t, count, PAGE_SIZE);
+
+ buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ mutex_lock(&iommu_debug_lock);
+
+ area = omap_find_iovm_area(obj, (u32)ppos);
+ if (IS_ERR(area)) {
+ bytes = -EINVAL;
+ goto err_out;
+ }
+ memcpy(p, area->va, count);
+ p += count;
+
+ bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+err_out:
+ mutex_unlock(&iommu_debug_lock);
+ free_page((unsigned long)buf);
+
+ return bytes;
+}
+
+static ssize_t debug_write_mem(struct file *file, const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ struct iovm_struct *area;
+ char *p, *buf;
+
+ count = min_t(size_t, count, PAGE_SIZE);
+
+ buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ mutex_lock(&iommu_debug_lock);
+
+ if (copy_from_user(p, userbuf, count)) {
+ count = -EFAULT;
+ goto err_out;
+ }
+
+ area = omap_find_iovm_area(obj, (u32)ppos);
+ if (IS_ERR(area)) {
+ count = -EINVAL;
+ goto err_out;
+ }
+ memcpy(area->va, p, count);
+err_out:
+ mutex_unlock(&iommu_debug_lock);
+ free_page((unsigned long)buf);
+
+ return count;
+}
+
+static int debug_open_generic(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+#define DEBUG_FOPS(name) \
+ static const struct file_operations debug_##name##_fops = { \
+ .open = debug_open_generic, \
+ .read = debug_read_##name, \
+ .write = debug_write_##name, \
+ .llseek = generic_file_llseek, \
+ };
+
+#define DEBUG_FOPS_RO(name) \
+ static const struct file_operations debug_##name##_fops = { \
+ .open = debug_open_generic, \
+ .read = debug_read_##name, \
+ .llseek = generic_file_llseek, \
+ };
+
+DEBUG_FOPS_RO(ver);
+DEBUG_FOPS_RO(regs);
+DEBUG_FOPS_RO(tlb);
+DEBUG_FOPS(pagetable);
+DEBUG_FOPS_RO(mmap);
+DEBUG_FOPS(mem);
+
+#define __DEBUG_ADD_FILE(attr, mode) \
+ { \
+ struct dentry *dent; \
+ dent = debugfs_create_file(#attr, mode, parent, \
+ obj, &debug_##attr##_fops); \
+ if (!dent) \
+ return -ENOMEM; \
+ }
+
+#define DEBUG_ADD_FILE(name) __DEBUG_ADD_FILE(name, 600)
+#define DEBUG_ADD_FILE_RO(name) __DEBUG_ADD_FILE(name, 400)
+
+static int iommu_debug_register(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_iommu *obj = platform_get_drvdata(pdev);
+ struct dentry *d, *parent;
+
+ if (!obj || !obj->dev)
+ return -EINVAL;
+
+ d = debugfs_create_dir(obj->name, iommu_debug_root);
+ if (!d)
+ return -ENOMEM;
+ parent = d;
+
+ d = debugfs_create_u8("nr_tlb_entries", 400, parent,
+ (u8 *)&obj->nr_tlb_entries);
+ if (!d)
+ return -ENOMEM;
+
+ DEBUG_ADD_FILE_RO(ver);
+ DEBUG_ADD_FILE_RO(regs);
+ DEBUG_ADD_FILE_RO(tlb);
+ DEBUG_ADD_FILE(pagetable);
+ DEBUG_ADD_FILE_RO(mmap);
+ DEBUG_ADD_FILE(mem);
+
+ return 0;
+}
+
+static int __init iommu_debug_init(void)
+{
+ struct dentry *d;
+ int err;
+
+ d = debugfs_create_dir("iommu", NULL);
+ if (!d)
+ return -ENOMEM;
+ iommu_debug_root = d;
+
+ err = omap_foreach_iommu_device(d, iommu_debug_register);
+ if (err)
+ goto err_out;
+ return 0;
+
+err_out:
+ debugfs_remove_recursive(iommu_debug_root);
+ return err;
+}
+module_init(iommu_debug_init)
+
+static void __exit iommu_debugfs_exit(void)
+{
+ debugfs_remove_recursive(iommu_debug_root);
+}
+module_exit(iommu_debugfs_exit)
+
+MODULE_DESCRIPTION("omap iommu: debugfs interface");
+MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
new file mode 100644
index 000000000000..8f32b2bf7587
--- /dev/null
+++ b/drivers/iommu/omap-iommu.c
@@ -0,0 +1,1245 @@
+/*
+ * omap iommu: tlb and pagetable primitives
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ *
+ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>,
+ * Paul Mundt and Toshihiro Kobayashi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/iommu.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#include <asm/cacheflush.h>
+
+#include <plat/iommu.h>
+
+#include <plat/iopgtable.h>
+
+#define for_each_iotlb_cr(obj, n, __i, cr) \
+ for (__i = 0; \
+ (__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \
+ __i++)
+
+/**
+ * struct omap_iommu_domain - omap iommu domain
+ * @pgtable: the page table
+ * @iommu_dev: an omap iommu device attached to this domain. only a single
+ * iommu device can be attached for now.
+ * @lock: domain lock, should be taken when attaching/detaching
+ */
+struct omap_iommu_domain {
+ u32 *pgtable;
+ struct omap_iommu *iommu_dev;
+ spinlock_t lock;
+};
+
+/* accommodate the difference between omap1 and omap2/3 */
+static const struct iommu_functions *arch_iommu;
+
+static struct platform_driver omap_iommu_driver;
+static struct kmem_cache *iopte_cachep;
+
+/**
+ * omap_install_iommu_arch - Install archtecure specific iommu functions
+ * @ops: a pointer to architecture specific iommu functions
+ *
+ * There are several kind of iommu algorithm(tlb, pagetable) among
+ * omap series. This interface installs such an iommu algorighm.
+ **/
+int omap_install_iommu_arch(const struct iommu_functions *ops)
+{
+ if (arch_iommu)
+ return -EBUSY;
+
+ arch_iommu = ops;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_install_iommu_arch);
+
+/**
+ * omap_uninstall_iommu_arch - Uninstall archtecure specific iommu functions
+ * @ops: a pointer to architecture specific iommu functions
+ *
+ * This interface uninstalls the iommu algorighm installed previously.
+ **/
+void omap_uninstall_iommu_arch(const struct iommu_functions *ops)
+{
+ if (arch_iommu != ops)
+ pr_err("%s: not your arch\n", __func__);
+
+ arch_iommu = NULL;
+}
+EXPORT_SYMBOL_GPL(omap_uninstall_iommu_arch);
+
+/**
+ * omap_iommu_save_ctx - Save registers for pm off-mode support
+ * @obj: target iommu
+ **/
+void omap_iommu_save_ctx(struct omap_iommu *obj)
+{
+ arch_iommu->save_ctx(obj);
+}
+EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
+
+/**
+ * omap_iommu_restore_ctx - Restore registers for pm off-mode support
+ * @obj: target iommu
+ **/
+void omap_iommu_restore_ctx(struct omap_iommu *obj)
+{
+ arch_iommu->restore_ctx(obj);
+}
+EXPORT_SYMBOL_GPL(omap_iommu_restore_ctx);
+
+/**
+ * omap_iommu_arch_version - Return running iommu arch version
+ **/
+u32 omap_iommu_arch_version(void)
+{
+ return arch_iommu->version;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_arch_version);
+
+static int iommu_enable(struct omap_iommu *obj)
+{
+ int err;
+
+ if (!obj)
+ return -EINVAL;
+
+ if (!arch_iommu)
+ return -ENODEV;
+
+ clk_enable(obj->clk);
+
+ err = arch_iommu->enable(obj);
+
+ clk_disable(obj->clk);
+ return err;
+}
+
+static void iommu_disable(struct omap_iommu *obj)
+{
+ if (!obj)
+ return;
+
+ clk_enable(obj->clk);
+
+ arch_iommu->disable(obj);
+
+ clk_disable(obj->clk);
+}
+
+/*
+ * TLB operations
+ */
+void omap_iotlb_cr_to_e(struct cr_regs *cr, struct iotlb_entry *e)
+{
+ BUG_ON(!cr || !e);
+
+ arch_iommu->cr_to_e(cr, e);
+}
+EXPORT_SYMBOL_GPL(omap_iotlb_cr_to_e);
+
+static inline int iotlb_cr_valid(struct cr_regs *cr)
+{
+ if (!cr)
+ return -EINVAL;
+
+ return arch_iommu->cr_valid(cr);
+}
+
+static inline struct cr_regs *iotlb_alloc_cr(struct omap_iommu *obj,
+ struct iotlb_entry *e)
+{
+ if (!e)
+ return NULL;
+
+ return arch_iommu->alloc_cr(obj, e);
+}
+
+static u32 iotlb_cr_to_virt(struct cr_regs *cr)
+{
+ return arch_iommu->cr_to_virt(cr);
+}
+
+static u32 get_iopte_attr(struct iotlb_entry *e)
+{
+ return arch_iommu->get_pte_attr(e);
+}
+
+static u32 iommu_report_fault(struct omap_iommu *obj, u32 *da)
+{
+ return arch_iommu->fault_isr(obj, da);
+}
+
+static void iotlb_lock_get(struct omap_iommu *obj, struct iotlb_lock *l)
+{
+ u32 val;
+
+ val = iommu_read_reg(obj, MMU_LOCK);
+
+ l->base = MMU_LOCK_BASE(val);
+ l->vict = MMU_LOCK_VICT(val);
+
+}
+
+static void iotlb_lock_set(struct omap_iommu *obj, struct iotlb_lock *l)
+{
+ u32 val;
+
+ val = (l->base << MMU_LOCK_BASE_SHIFT);
+ val |= (l->vict << MMU_LOCK_VICT_SHIFT);
+
+ iommu_write_reg(obj, val, MMU_LOCK);
+}
+
+static void iotlb_read_cr(struct omap_iommu *obj, struct cr_regs *cr)
+{
+ arch_iommu->tlb_read_cr(obj, cr);
+}
+
+static void iotlb_load_cr(struct omap_iommu *obj, struct cr_regs *cr)
+{
+ arch_iommu->tlb_load_cr(obj, cr);
+
+ iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
+ iommu_write_reg(obj, 1, MMU_LD_TLB);
+}
+
+/**
+ * iotlb_dump_cr - Dump an iommu tlb entry into buf
+ * @obj: target iommu
+ * @cr: contents of cam and ram register
+ * @buf: output buffer
+ **/
+static inline ssize_t iotlb_dump_cr(struct omap_iommu *obj, struct cr_regs *cr,
+ char *buf)
+{
+ BUG_ON(!cr || !buf);
+
+ return arch_iommu->dump_cr(obj, cr, buf);
+}
+
+/* only used in iotlb iteration for-loop */
+static struct cr_regs __iotlb_read_cr(struct omap_iommu *obj, int n)
+{
+ struct cr_regs cr;
+ struct iotlb_lock l;
+
+ iotlb_lock_get(obj, &l);
+ l.vict = n;
+ iotlb_lock_set(obj, &l);
+ iotlb_read_cr(obj, &cr);
+
+ return cr;
+}
+
+/**
+ * load_iotlb_entry - Set an iommu tlb entry
+ * @obj: target iommu
+ * @e: an iommu tlb entry info
+ **/
+#ifdef PREFETCH_IOTLB
+static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
+{
+ int err = 0;
+ struct iotlb_lock l;
+ struct cr_regs *cr;
+
+ if (!obj || !obj->nr_tlb_entries || !e)
+ return -EINVAL;
+
+ clk_enable(obj->clk);
+
+ iotlb_lock_get(obj, &l);
+ if (l.base == obj->nr_tlb_entries) {
+ dev_warn(obj->dev, "%s: preserve entries full\n", __func__);
+ err = -EBUSY;
+ goto out;
+ }
+ if (!e->prsvd) {
+ int i;
+ struct cr_regs tmp;
+
+ for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, tmp)
+ if (!iotlb_cr_valid(&tmp))
+ break;
+
+ if (i == obj->nr_tlb_entries) {
+ dev_dbg(obj->dev, "%s: full: no entry\n", __func__);
+ err = -EBUSY;
+ goto out;
+ }
+
+ iotlb_lock_get(obj, &l);
+ } else {
+ l.vict = l.base;
+ iotlb_lock_set(obj, &l);
+ }
+
+ cr = iotlb_alloc_cr(obj, e);
+ if (IS_ERR(cr)) {
+ clk_disable(obj->clk);
+ return PTR_ERR(cr);
+ }
+
+ iotlb_load_cr(obj, cr);
+ kfree(cr);
+
+ if (e->prsvd)
+ l.base++;
+ /* increment victim for next tlb load */
+ if (++l.vict == obj->nr_tlb_entries)
+ l.vict = l.base;
+ iotlb_lock_set(obj, &l);
+out:
+ clk_disable(obj->clk);
+ return err;
+}
+
+#else /* !PREFETCH_IOTLB */
+
+static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
+{
+ return 0;
+}
+
+#endif /* !PREFETCH_IOTLB */
+
+static int prefetch_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
+{
+ return load_iotlb_entry(obj, e);
+}
+
+/**
+ * flush_iotlb_page - Clear an iommu tlb entry
+ * @obj: target iommu
+ * @da: iommu device virtual address
+ *
+ * Clear an iommu tlb entry which includes 'da' address.
+ **/
+static void flush_iotlb_page(struct omap_iommu *obj, u32 da)
+{
+ int i;
+ struct cr_regs cr;
+
+ clk_enable(obj->clk);
+
+ for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) {
+ u32 start;
+ size_t bytes;
+
+ if (!iotlb_cr_valid(&cr))
+ continue;
+
+ start = iotlb_cr_to_virt(&cr);
+ bytes = iopgsz_to_bytes(cr.cam & 3);
+
+ if ((start <= da) && (da < start + bytes)) {
+ dev_dbg(obj->dev, "%s: %08x<=%08x(%x)\n",
+ __func__, start, da, bytes);
+ iotlb_load_cr(obj, &cr);
+ iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
+ }
+ }
+ clk_disable(obj->clk);
+
+ if (i == obj->nr_tlb_entries)
+ dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da);
+}
+
+/**
+ * flush_iotlb_all - Clear all iommu tlb entries
+ * @obj: target iommu
+ **/
+static void flush_iotlb_all(struct omap_iommu *obj)
+{
+ struct iotlb_lock l;
+
+ clk_enable(obj->clk);
+
+ l.base = 0;
+ l.vict = 0;
+ iotlb_lock_set(obj, &l);
+
+ iommu_write_reg(obj, 1, MMU_GFLUSH);
+
+ clk_disable(obj->clk);
+}
+
+#if defined(CONFIG_OMAP_IOMMU_DEBUG) || defined(CONFIG_OMAP_IOMMU_DEBUG_MODULE)
+
+ssize_t omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t bytes)
+{
+ if (!obj || !buf)
+ return -EINVAL;
+
+ clk_enable(obj->clk);
+
+ bytes = arch_iommu->dump_ctx(obj, buf, bytes);
+
+ clk_disable(obj->clk);
+
+ return bytes;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_dump_ctx);
+
+static int
+__dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num)
+{
+ int i;
+ struct iotlb_lock saved;
+ struct cr_regs tmp;
+ struct cr_regs *p = crs;
+
+ clk_enable(obj->clk);
+ iotlb_lock_get(obj, &saved);
+
+ for_each_iotlb_cr(obj, num, i, tmp) {
+ if (!iotlb_cr_valid(&tmp))
+ continue;
+ *p++ = tmp;
+ }
+
+ iotlb_lock_set(obj, &saved);
+ clk_disable(obj->clk);
+
+ return p - crs;
+}
+
+/**
+ * omap_dump_tlb_entries - dump cr arrays to given buffer
+ * @obj: target iommu
+ * @buf: output buffer
+ **/
+size_t omap_dump_tlb_entries(struct omap_iommu *obj, char *buf, ssize_t bytes)
+{
+ int i, num;
+ struct cr_regs *cr;
+ char *p = buf;
+
+ num = bytes / sizeof(*cr);
+ num = min(obj->nr_tlb_entries, num);
+
+ cr = kcalloc(num, sizeof(*cr), GFP_KERNEL);
+ if (!cr)
+ return 0;
+
+ num = __dump_tlb_entries(obj, cr, num);
+ for (i = 0; i < num; i++)
+ p += iotlb_dump_cr(obj, cr + i, p);
+ kfree(cr);
+
+ return p - buf;
+}
+EXPORT_SYMBOL_GPL(omap_dump_tlb_entries);
+
+int omap_foreach_iommu_device(void *data, int (*fn)(struct device *, void *))
+{
+ return driver_for_each_device(&omap_iommu_driver.driver,
+ NULL, data, fn);
+}
+EXPORT_SYMBOL_GPL(omap_foreach_iommu_device);
+
+#endif /* CONFIG_OMAP_IOMMU_DEBUG_MODULE */
+
+/*
+ * H/W pagetable operations
+ */
+static void flush_iopgd_range(u32 *first, u32 *last)
+{
+ /* FIXME: L2 cache should be taken care of if it exists */
+ do {
+ asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pgd"
+ : : "r" (first));
+ first += L1_CACHE_BYTES / sizeof(*first);
+ } while (first <= last);
+}
+
+static void flush_iopte_range(u32 *first, u32 *last)
+{
+ /* FIXME: L2 cache should be taken care of if it exists */
+ do {
+ asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pte"
+ : : "r" (first));
+ first += L1_CACHE_BYTES / sizeof(*first);
+ } while (first <= last);
+}
+
+static void iopte_free(u32 *iopte)
+{
+ /* Note: freed iopte's must be clean ready for re-use */
+ kmem_cache_free(iopte_cachep, iopte);
+}
+
+static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
+{
+ u32 *iopte;
+
+ /* a table has already existed */
+ if (*iopgd)
+ goto pte_ready;
+
+ /*
+ * do the allocation outside the page table lock
+ */
+ spin_unlock(&obj->page_table_lock);
+ iopte = kmem_cache_zalloc(iopte_cachep, GFP_KERNEL);
+ spin_lock(&obj->page_table_lock);
+
+ if (!*iopgd) {
+ if (!iopte)
+ return ERR_PTR(-ENOMEM);
+
+ *iopgd = virt_to_phys(iopte) | IOPGD_TABLE;
+ flush_iopgd_range(iopgd, iopgd);
+
+ dev_vdbg(obj->dev, "%s: a new pte:%p\n", __func__, iopte);
+ } else {
+ /* We raced, free the reduniovant table */
+ iopte_free(iopte);
+ }
+
+pte_ready:
+ iopte = iopte_offset(iopgd, da);
+
+ dev_vdbg(obj->dev,
+ "%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n",
+ __func__, da, iopgd, *iopgd, iopte, *iopte);
+
+ return iopte;
+}
+
+static int iopgd_alloc_section(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
+{
+ u32 *iopgd = iopgd_offset(obj, da);
+
+ if ((da | pa) & ~IOSECTION_MASK) {
+ dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
+ __func__, da, pa, IOSECTION_SIZE);
+ return -EINVAL;
+ }
+
+ *iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION;
+ flush_iopgd_range(iopgd, iopgd);
+ return 0;
+}
+
+static int iopgd_alloc_super(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
+{
+ u32 *iopgd = iopgd_offset(obj, da);
+ int i;
+
+ if ((da | pa) & ~IOSUPER_MASK) {
+ dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
+ __func__, da, pa, IOSUPER_SIZE);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 16; i++)
+ *(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER;
+ flush_iopgd_range(iopgd, iopgd + 15);
+ return 0;
+}
+
+static int iopte_alloc_page(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
+{
+ u32 *iopgd = iopgd_offset(obj, da);
+ u32 *iopte = iopte_alloc(obj, iopgd, da);
+
+ if (IS_ERR(iopte))
+ return PTR_ERR(iopte);
+
+ *iopte = (pa & IOPAGE_MASK) | prot | IOPTE_SMALL;
+ flush_iopte_range(iopte, iopte);
+
+ dev_vdbg(obj->dev, "%s: da:%08x pa:%08x pte:%p *pte:%08x\n",
+ __func__, da, pa, iopte, *iopte);
+
+ return 0;
+}
+
+static int iopte_alloc_large(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
+{
+ u32 *iopgd = iopgd_offset(obj, da);
+ u32 *iopte = iopte_alloc(obj, iopgd, da);
+ int i;
+
+ if ((da | pa) & ~IOLARGE_MASK) {
+ dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
+ __func__, da, pa, IOLARGE_SIZE);
+ return -EINVAL;
+ }
+
+ if (IS_ERR(iopte))
+ return PTR_ERR(iopte);
+
+ for (i = 0; i < 16; i++)
+ *(iopte + i) = (pa & IOLARGE_MASK) | prot | IOPTE_LARGE;
+ flush_iopte_range(iopte, iopte + 15);
+ return 0;
+}
+
+static int
+iopgtable_store_entry_core(struct omap_iommu *obj, struct iotlb_entry *e)
+{
+ int (*fn)(struct omap_iommu *, u32, u32, u32);
+ u32 prot;
+ int err;
+
+ if (!obj || !e)
+ return -EINVAL;
+
+ switch (e->pgsz) {
+ case MMU_CAM_PGSZ_16M:
+ fn = iopgd_alloc_super;
+ break;
+ case MMU_CAM_PGSZ_1M:
+ fn = iopgd_alloc_section;
+ break;
+ case MMU_CAM_PGSZ_64K:
+ fn = iopte_alloc_large;
+ break;
+ case MMU_CAM_PGSZ_4K:
+ fn = iopte_alloc_page;
+ break;
+ default:
+ fn = NULL;
+ BUG();
+ break;
+ }
+
+ prot = get_iopte_attr(e);
+
+ spin_lock(&obj->page_table_lock);
+ err = fn(obj, e->da, e->pa, prot);
+ spin_unlock(&obj->page_table_lock);
+
+ return err;
+}
+
+/**
+ * omap_iopgtable_store_entry - Make an iommu pte entry
+ * @obj: target iommu
+ * @e: an iommu tlb entry info
+ **/
+int omap_iopgtable_store_entry(struct omap_iommu *obj, struct iotlb_entry *e)
+{
+ int err;
+
+ flush_iotlb_page(obj, e->da);
+ err = iopgtable_store_entry_core(obj, e);
+ if (!err)
+ prefetch_iotlb_entry(obj, e);
+ return err;
+}
+EXPORT_SYMBOL_GPL(omap_iopgtable_store_entry);
+
+/**
+ * iopgtable_lookup_entry - Lookup an iommu pte entry
+ * @obj: target iommu
+ * @da: iommu device virtual address
+ * @ppgd: iommu pgd entry pointer to be returned
+ * @ppte: iommu pte entry pointer to be returned
+ **/
+static void
+iopgtable_lookup_entry(struct omap_iommu *obj, u32 da, u32 **ppgd, u32 **ppte)
+{
+ u32 *iopgd, *iopte = NULL;
+
+ iopgd = iopgd_offset(obj, da);
+ if (!*iopgd)
+ goto out;
+
+ if (iopgd_is_table(*iopgd))
+ iopte = iopte_offset(iopgd, da);
+out:
+ *ppgd = iopgd;
+ *ppte = iopte;
+}
+
+static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
+{
+ size_t bytes;
+ u32 *iopgd = iopgd_offset(obj, da);
+ int nent = 1;
+
+ if (!*iopgd)
+ return 0;
+
+ if (iopgd_is_table(*iopgd)) {
+ int i;
+ u32 *iopte = iopte_offset(iopgd, da);
+
+ bytes = IOPTE_SIZE;
+ if (*iopte & IOPTE_LARGE) {
+ nent *= 16;
+ /* rewind to the 1st entry */
+ iopte = iopte_offset(iopgd, (da & IOLARGE_MASK));
+ }
+ bytes *= nent;
+ memset(iopte, 0, nent * sizeof(*iopte));
+ flush_iopte_range(iopte, iopte + (nent - 1) * sizeof(*iopte));
+
+ /*
+ * do table walk to check if this table is necessary or not
+ */
+ iopte = iopte_offset(iopgd, 0);
+ for (i = 0; i < PTRS_PER_IOPTE; i++)
+ if (iopte[i])
+ goto out;
+
+ iopte_free(iopte);
+ nent = 1; /* for the next L1 entry */
+ } else {
+ bytes = IOPGD_SIZE;
+ if ((*iopgd & IOPGD_SUPER) == IOPGD_SUPER) {
+ nent *= 16;
+ /* rewind to the 1st entry */
+ iopgd = iopgd_offset(obj, (da & IOSUPER_MASK));
+ }
+ bytes *= nent;
+ }
+ memset(iopgd, 0, nent * sizeof(*iopgd));
+ flush_iopgd_range(iopgd, iopgd + (nent - 1) * sizeof(*iopgd));
+out:
+ return bytes;
+}
+
+/**
+ * iopgtable_clear_entry - Remove an iommu pte entry
+ * @obj: target iommu
+ * @da: iommu device virtual address
+ **/
+static size_t iopgtable_clear_entry(struct omap_iommu *obj, u32 da)
+{
+ size_t bytes;
+
+ spin_lock(&obj->page_table_lock);
+
+ bytes = iopgtable_clear_entry_core(obj, da);
+ flush_iotlb_page(obj, da);
+
+ spin_unlock(&obj->page_table_lock);
+
+ return bytes;
+}
+
+static void iopgtable_clear_entry_all(struct omap_iommu *obj)
+{
+ int i;
+
+ spin_lock(&obj->page_table_lock);
+
+ for (i = 0; i < PTRS_PER_IOPGD; i++) {
+ u32 da;
+ u32 *iopgd;
+
+ da = i << IOPGD_SHIFT;
+ iopgd = iopgd_offset(obj, da);
+
+ if (!*iopgd)
+ continue;
+
+ if (iopgd_is_table(*iopgd))
+ iopte_free(iopte_offset(iopgd, 0));
+
+ *iopgd = 0;
+ flush_iopgd_range(iopgd, iopgd);
+ }
+
+ flush_iotlb_all(obj);
+
+ spin_unlock(&obj->page_table_lock);
+}
+
+/*
+ * Device IOMMU generic operations
+ */
+static irqreturn_t iommu_fault_handler(int irq, void *data)
+{
+ u32 da, errs;
+ u32 *iopgd, *iopte;
+ struct omap_iommu *obj = data;
+ struct iommu_domain *domain = obj->domain;
+
+ if (!obj->refcount)
+ return IRQ_NONE;
+
+ clk_enable(obj->clk);
+ errs = iommu_report_fault(obj, &da);
+ clk_disable(obj->clk);
+ if (errs == 0)
+ return IRQ_HANDLED;
+
+ /* Fault callback or TLB/PTE Dynamic loading */
+ if (!report_iommu_fault(domain, obj->dev, da, 0))
+ return IRQ_HANDLED;
+
+ iommu_disable(obj);
+
+ iopgd = iopgd_offset(obj, da);
+
+ if (!iopgd_is_table(*iopgd)) {
+ dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p "
+ "*pgd:px%08x\n", obj->name, errs, da, iopgd, *iopgd);
+ return IRQ_NONE;
+ }
+
+ iopte = iopte_offset(iopgd, da);
+
+ dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:0x%08x "
+ "pte:0x%p *pte:0x%08x\n", obj->name, errs, da, iopgd, *iopgd,
+ iopte, *iopte);
+
+ return IRQ_NONE;
+}
+
+static int device_match_by_alias(struct device *dev, void *data)
+{
+ struct omap_iommu *obj = to_iommu(dev);
+ const char *name = data;
+
+ pr_debug("%s: %s %s\n", __func__, obj->name, name);
+
+ return strcmp(obj->name, name) == 0;
+}
+
+/**
+ * omap_find_iommu_device() - find an omap iommu device by name
+ * @name: name of the iommu device
+ *
+ * The generic iommu API requires the caller to provide the device
+ * he wishes to attach to a certain iommu domain.
+ *
+ * Drivers generally should not bother with this as it should just
+ * be taken care of by the DMA-API using dev_archdata.
+ *
+ * This function is provided as an interim solution until the latter
+ * materializes, and omap3isp is fully migrated to the DMA-API.
+ */
+struct device *omap_find_iommu_device(const char *name)
+{
+ return driver_find_device(&omap_iommu_driver.driver, NULL,
+ (void *)name,
+ device_match_by_alias);
+}
+EXPORT_SYMBOL_GPL(omap_find_iommu_device);
+
+/**
+ * omap_iommu_attach() - attach iommu device to an iommu domain
+ * @dev: target omap iommu device
+ * @iopgd: page table
+ **/
+static struct omap_iommu *omap_iommu_attach(struct device *dev, u32 *iopgd)
+{
+ int err = -ENOMEM;
+ struct omap_iommu *obj = to_iommu(dev);
+
+ spin_lock(&obj->iommu_lock);
+
+ /* an iommu device can only be attached once */
+ if (++obj->refcount > 1) {
+ dev_err(dev, "%s: already attached!\n", obj->name);
+ err = -EBUSY;
+ goto err_enable;
+ }
+
+ obj->iopgd = iopgd;
+ err = iommu_enable(obj);
+ if (err)
+ goto err_enable;
+ flush_iotlb_all(obj);
+
+ if (!try_module_get(obj->owner))
+ goto err_module;
+
+ spin_unlock(&obj->iommu_lock);
+
+ dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
+ return obj;
+
+err_module:
+ if (obj->refcount == 1)
+ iommu_disable(obj);
+err_enable:
+ obj->refcount--;
+ spin_unlock(&obj->iommu_lock);
+ return ERR_PTR(err);
+}
+
+/**
+ * omap_iommu_detach - release iommu device
+ * @obj: target iommu
+ **/
+static void omap_iommu_detach(struct omap_iommu *obj)
+{
+ if (!obj || IS_ERR(obj))
+ return;
+
+ spin_lock(&obj->iommu_lock);
+
+ if (--obj->refcount == 0)
+ iommu_disable(obj);
+
+ module_put(obj->owner);
+
+ obj->iopgd = NULL;
+
+ spin_unlock(&obj->iommu_lock);
+
+ dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
+}
+
+/*
+ * OMAP Device MMU(IOMMU) detection
+ */
+static int __devinit omap_iommu_probe(struct platform_device *pdev)
+{
+ int err = -ENODEV;
+ int irq;
+ struct omap_iommu *obj;
+ struct resource *res;
+ struct iommu_platform_data *pdata = pdev->dev.platform_data;
+
+ if (pdev->num_resources != 2)
+ return -EINVAL;
+
+ obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
+ if (!obj)
+ return -ENOMEM;
+
+ obj->clk = clk_get(&pdev->dev, pdata->clk_name);
+ if (IS_ERR(obj->clk))
+ goto err_clk;
+
+ obj->nr_tlb_entries = pdata->nr_tlb_entries;
+ obj->name = pdata->name;
+ obj->dev = &pdev->dev;
+ obj->ctx = (void *)obj + sizeof(*obj);
+ obj->da_start = pdata->da_start;
+ obj->da_end = pdata->da_end;
+
+ spin_lock_init(&obj->iommu_lock);
+ mutex_init(&obj->mmap_lock);
+ spin_lock_init(&obj->page_table_lock);
+ INIT_LIST_HEAD(&obj->mmap);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENODEV;
+ goto err_mem;
+ }
+
+ res = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+ if (!res) {
+ err = -EIO;
+ goto err_mem;
+ }
+
+ obj->regbase = ioremap(res->start, resource_size(res));
+ if (!obj->regbase) {
+ err = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ err = -ENODEV;
+ goto err_irq;
+ }
+ err = request_irq(irq, iommu_fault_handler, IRQF_SHARED,
+ dev_name(&pdev->dev), obj);
+ if (err < 0)
+ goto err_irq;
+ platform_set_drvdata(pdev, obj);
+
+ dev_info(&pdev->dev, "%s registered\n", obj->name);
+ return 0;
+
+err_irq:
+ iounmap(obj->regbase);
+err_ioremap:
+ release_mem_region(res->start, resource_size(res));
+err_mem:
+ clk_put(obj->clk);
+err_clk:
+ kfree(obj);
+ return err;
+}
+
+static int __devexit omap_iommu_remove(struct platform_device *pdev)
+{
+ int irq;
+ struct resource *res;
+ struct omap_iommu *obj = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ iopgtable_clear_entry_all(obj);
+
+ irq = platform_get_irq(pdev, 0);
+ free_irq(irq, obj);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+ iounmap(obj->regbase);
+
+ clk_put(obj->clk);
+ dev_info(&pdev->dev, "%s removed\n", obj->name);
+ kfree(obj);
+ return 0;
+}
+
+static struct platform_driver omap_iommu_driver = {
+ .probe = omap_iommu_probe,
+ .remove = __devexit_p(omap_iommu_remove),
+ .driver = {
+ .name = "omap-iommu",
+ },
+};
+
+static void iopte_cachep_ctor(void *iopte)
+{
+ clean_dcache_area(iopte, IOPTE_TABLE_SIZE);
+}
+
+static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
+ phys_addr_t pa, int order, int prot)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu *oiommu = omap_domain->iommu_dev;
+ struct device *dev = oiommu->dev;
+ size_t bytes = PAGE_SIZE << order;
+ struct iotlb_entry e;
+ int omap_pgsz;
+ u32 ret, flags;
+
+ /* we only support mapping a single iommu page for now */
+ omap_pgsz = bytes_to_iopgsz(bytes);
+ if (omap_pgsz < 0) {
+ dev_err(dev, "invalid size to map: %d\n", bytes);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "mapping da 0x%lx to pa 0x%x size 0x%x\n", da, pa, bytes);
+
+ flags = omap_pgsz | prot;
+
+ iotlb_init_entry(&e, da, pa, flags);
+
+ ret = omap_iopgtable_store_entry(oiommu, &e);
+ if (ret)
+ dev_err(dev, "omap_iopgtable_store_entry failed: %d\n", ret);
+
+ return ret;
+}
+
+static int omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
+ int order)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu *oiommu = omap_domain->iommu_dev;
+ struct device *dev = oiommu->dev;
+ size_t unmap_size;
+
+ dev_dbg(dev, "unmapping da 0x%lx order %d\n", da, order);
+
+ unmap_size = iopgtable_clear_entry(oiommu, da);
+
+ return unmap_size ? get_order(unmap_size) : -EINVAL;
+}
+
+static int
+omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu *oiommu;
+ int ret = 0;
+
+ spin_lock(&omap_domain->lock);
+
+ /* only a single device is supported per domain for now */
+ if (omap_domain->iommu_dev) {
+ dev_err(dev, "iommu domain is already attached\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* get a handle to and enable the omap iommu */
+ oiommu = omap_iommu_attach(dev, omap_domain->pgtable);
+ if (IS_ERR(oiommu)) {
+ ret = PTR_ERR(oiommu);
+ dev_err(dev, "can't get omap iommu: %d\n", ret);
+ goto out;
+ }
+
+ omap_domain->iommu_dev = oiommu;
+ oiommu->domain = domain;
+
+out:
+ spin_unlock(&omap_domain->lock);
+ return ret;
+}
+
+static void omap_iommu_detach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu *oiommu = to_iommu(dev);
+
+ spin_lock(&omap_domain->lock);
+
+ /* only a single device is supported per domain for now */
+ if (omap_domain->iommu_dev != oiommu) {
+ dev_err(dev, "invalid iommu device\n");
+ goto out;
+ }
+
+ iopgtable_clear_entry_all(oiommu);
+
+ omap_iommu_detach(oiommu);
+
+ omap_domain->iommu_dev = NULL;
+
+out:
+ spin_unlock(&omap_domain->lock);
+}
+
+static int omap_iommu_domain_init(struct iommu_domain *domain)
+{
+ struct omap_iommu_domain *omap_domain;
+
+ omap_domain = kzalloc(sizeof(*omap_domain), GFP_KERNEL);
+ if (!omap_domain) {
+ pr_err("kzalloc failed\n");
+ goto out;
+ }
+
+ omap_domain->pgtable = kzalloc(IOPGD_TABLE_SIZE, GFP_KERNEL);
+ if (!omap_domain->pgtable) {
+ pr_err("kzalloc failed\n");
+ goto fail_nomem;
+ }
+
+ /*
+ * should never fail, but please keep this around to ensure
+ * we keep the hardware happy
+ */
+ BUG_ON(!IS_ALIGNED((long)omap_domain->pgtable, IOPGD_TABLE_SIZE));
+
+ clean_dcache_area(omap_domain->pgtable, IOPGD_TABLE_SIZE);
+ spin_lock_init(&omap_domain->lock);
+
+ domain->priv = omap_domain;
+
+ return 0;
+
+fail_nomem:
+ kfree(omap_domain);
+out:
+ return -ENOMEM;
+}
+
+/* assume device was already detached */
+static void omap_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+
+ domain->priv = NULL;
+
+ kfree(omap_domain->pgtable);
+ kfree(omap_domain);
+}
+
+static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long da)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu *oiommu = omap_domain->iommu_dev;
+ struct device *dev = oiommu->dev;
+ u32 *pgd, *pte;
+ phys_addr_t ret = 0;
+
+ iopgtable_lookup_entry(oiommu, da, &pgd, &pte);
+
+ if (pte) {
+ if (iopte_is_small(*pte))
+ ret = omap_iommu_translate(*pte, da, IOPTE_MASK);
+ else if (iopte_is_large(*pte))
+ ret = omap_iommu_translate(*pte, da, IOLARGE_MASK);
+ else
+ dev_err(dev, "bogus pte 0x%x", *pte);
+ } else {
+ if (iopgd_is_section(*pgd))
+ ret = omap_iommu_translate(*pgd, da, IOSECTION_MASK);
+ else if (iopgd_is_super(*pgd))
+ ret = omap_iommu_translate(*pgd, da, IOSUPER_MASK);
+ else
+ dev_err(dev, "bogus pgd 0x%x", *pgd);
+ }
+
+ return ret;
+}
+
+static int omap_iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ return 0;
+}
+
+static struct iommu_ops omap_iommu_ops = {
+ .domain_init = omap_iommu_domain_init,
+ .domain_destroy = omap_iommu_domain_destroy,
+ .attach_dev = omap_iommu_attach_dev,
+ .detach_dev = omap_iommu_detach_dev,
+ .map = omap_iommu_map,
+ .unmap = omap_iommu_unmap,
+ .iova_to_phys = omap_iommu_iova_to_phys,
+ .domain_has_cap = omap_iommu_domain_has_cap,
+};
+
+static int __init omap_iommu_init(void)
+{
+ struct kmem_cache *p;
+ const unsigned long flags = SLAB_HWCACHE_ALIGN;
+ size_t align = 1 << 10; /* L2 pagetable alignement */
+
+ p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, align, flags,
+ iopte_cachep_ctor);
+ if (!p)
+ return -ENOMEM;
+ iopte_cachep = p;
+
+ bus_set_iommu(&platform_bus_type, &omap_iommu_ops);
+
+ return platform_driver_register(&omap_iommu_driver);
+}
+module_init(omap_iommu_init);
+
+static void __exit omap_iommu_exit(void)
+{
+ kmem_cache_destroy(iopte_cachep);
+
+ platform_driver_unregister(&omap_iommu_driver);
+}
+module_exit(omap_iommu_exit);
+
+MODULE_DESCRIPTION("omap iommu: tlb and pagetable primitives");
+MODULE_ALIAS("platform:omap-iommu");
+MODULE_AUTHOR("Hiroshi DOYU, Paul Mundt and Toshihiro Kobayashi");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/omap-iovmm.c b/drivers/iommu/omap-iovmm.c
new file mode 100644
index 000000000000..46be456fcc00
--- /dev/null
+++ b/drivers/iommu/omap-iovmm.c
@@ -0,0 +1,743 @@
+/*
+ * omap iommu: simple virtual address space management
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+#include <linux/scatterlist.h>
+#include <linux/iommu.h>
+
+#include <asm/cacheflush.h>
+#include <asm/mach/map.h>
+
+#include <plat/iommu.h>
+#include <plat/iovmm.h>
+
+#include <plat/iopgtable.h>
+
+static struct kmem_cache *iovm_area_cachep;
+
+/* return the offset of the first scatterlist entry in a sg table */
+static unsigned int sgtable_offset(const struct sg_table *sgt)
+{
+ if (!sgt || !sgt->nents)
+ return 0;
+
+ return sgt->sgl->offset;
+}
+
+/* return total bytes of sg buffers */
+static size_t sgtable_len(const struct sg_table *sgt)
+{
+ unsigned int i, total = 0;
+ struct scatterlist *sg;
+
+ if (!sgt)
+ return 0;
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ size_t bytes;
+
+ bytes = sg->length + sg->offset;
+
+ if (!iopgsz_ok(bytes)) {
+ pr_err("%s: sg[%d] not iommu pagesize(%u %u)\n",
+ __func__, i, bytes, sg->offset);
+ return 0;
+ }
+
+ if (i && sg->offset) {
+ pr_err("%s: sg[%d] offset not allowed in internal "
+ "entries\n", __func__, i);
+ return 0;
+ }
+
+ total += bytes;
+ }
+
+ return total;
+}
+#define sgtable_ok(x) (!!sgtable_len(x))
+
+static unsigned max_alignment(u32 addr)
+{
+ int i;
+ unsigned pagesize[] = { SZ_16M, SZ_1M, SZ_64K, SZ_4K, };
+ for (i = 0; i < ARRAY_SIZE(pagesize) && addr & (pagesize[i] - 1); i++)
+ ;
+ return (i < ARRAY_SIZE(pagesize)) ? pagesize[i] : 0;
+}
+
+/*
+ * calculate the optimal number sg elements from total bytes based on
+ * iommu superpages
+ */
+static unsigned sgtable_nents(size_t bytes, u32 da, u32 pa)
+{
+ unsigned nr_entries = 0, ent_sz;
+
+ if (!IS_ALIGNED(bytes, PAGE_SIZE)) {
+ pr_err("%s: wrong size %08x\n", __func__, bytes);
+ return 0;
+ }
+
+ while (bytes) {
+ ent_sz = max_alignment(da | pa);
+ ent_sz = min_t(unsigned, ent_sz, iopgsz_max(bytes));
+ nr_entries++;
+ da += ent_sz;
+ pa += ent_sz;
+ bytes -= ent_sz;
+ }
+
+ return nr_entries;
+}
+
+/* allocate and initialize sg_table header(a kind of 'superblock') */
+static struct sg_table *sgtable_alloc(const size_t bytes, u32 flags,
+ u32 da, u32 pa)
+{
+ unsigned int nr_entries;
+ int err;
+ struct sg_table *sgt;
+
+ if (!bytes)
+ return ERR_PTR(-EINVAL);
+
+ if (!IS_ALIGNED(bytes, PAGE_SIZE))
+ return ERR_PTR(-EINVAL);
+
+ if (flags & IOVMF_LINEAR) {
+ nr_entries = sgtable_nents(bytes, da, pa);
+ if (!nr_entries)
+ return ERR_PTR(-EINVAL);
+ } else
+ nr_entries = bytes / PAGE_SIZE;
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return ERR_PTR(-ENOMEM);
+
+ err = sg_alloc_table(sgt, nr_entries, GFP_KERNEL);
+ if (err) {
+ kfree(sgt);
+ return ERR_PTR(err);
+ }
+
+ pr_debug("%s: sgt:%p(%d entries)\n", __func__, sgt, nr_entries);
+
+ return sgt;
+}
+
+/* free sg_table header(a kind of superblock) */
+static void sgtable_free(struct sg_table *sgt)
+{
+ if (!sgt)
+ return;
+
+ sg_free_table(sgt);
+ kfree(sgt);
+
+ pr_debug("%s: sgt:%p\n", __func__, sgt);
+}
+
+/* map 'sglist' to a contiguous mpu virtual area and return 'va' */
+static void *vmap_sg(const struct sg_table *sgt)
+{
+ u32 va;
+ size_t total;
+ unsigned int i;
+ struct scatterlist *sg;
+ struct vm_struct *new;
+ const struct mem_type *mtype;
+
+ mtype = get_mem_type(MT_DEVICE);
+ if (!mtype)
+ return ERR_PTR(-EINVAL);
+
+ total = sgtable_len(sgt);
+ if (!total)
+ return ERR_PTR(-EINVAL);
+
+ new = __get_vm_area(total, VM_IOREMAP, VMALLOC_START, VMALLOC_END);
+ if (!new)
+ return ERR_PTR(-ENOMEM);
+ va = (u32)new->addr;
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ size_t bytes;
+ u32 pa;
+ int err;
+
+ pa = sg_phys(sg) - sg->offset;
+ bytes = sg->length + sg->offset;
+
+ BUG_ON(bytes != PAGE_SIZE);
+
+ err = ioremap_page(va, pa, mtype);
+ if (err)
+ goto err_out;
+
+ va += bytes;
+ }
+
+ flush_cache_vmap((unsigned long)new->addr,
+ (unsigned long)(new->addr + total));
+ return new->addr;
+
+err_out:
+ WARN_ON(1); /* FIXME: cleanup some mpu mappings */
+ vunmap(new->addr);
+ return ERR_PTR(-EAGAIN);
+}
+
+static inline void vunmap_sg(const void *va)
+{
+ vunmap(va);
+}
+
+static struct iovm_struct *__find_iovm_area(struct omap_iommu *obj,
+ const u32 da)
+{
+ struct iovm_struct *tmp;
+
+ list_for_each_entry(tmp, &obj->mmap, list) {
+ if ((da >= tmp->da_start) && (da < tmp->da_end)) {
+ size_t len;
+
+ len = tmp->da_end - tmp->da_start;
+
+ dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n",
+ __func__, tmp->da_start, da, tmp->da_end, len,
+ tmp->flags);
+
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * omap_find_iovm_area - find iovma which includes @da
+ * @da: iommu device virtual address
+ *
+ * Find the existing iovma starting at @da
+ */
+struct iovm_struct *omap_find_iovm_area(struct omap_iommu *obj, u32 da)
+{
+ struct iovm_struct *area;
+
+ mutex_lock(&obj->mmap_lock);
+ area = __find_iovm_area(obj, da);
+ mutex_unlock(&obj->mmap_lock);
+
+ return area;
+}
+EXPORT_SYMBOL_GPL(omap_find_iovm_area);
+
+/*
+ * This finds the hole(area) which fits the requested address and len
+ * in iovmas mmap, and returns the new allocated iovma.
+ */
+static struct iovm_struct *alloc_iovm_area(struct omap_iommu *obj, u32 da,
+ size_t bytes, u32 flags)
+{
+ struct iovm_struct *new, *tmp;
+ u32 start, prev_end, alignment;
+
+ if (!obj || !bytes)
+ return ERR_PTR(-EINVAL);
+
+ start = da;
+ alignment = PAGE_SIZE;
+
+ if (~flags & IOVMF_DA_FIXED) {
+ /* Don't map address 0 */
+ start = obj->da_start ? obj->da_start : alignment;
+
+ if (flags & IOVMF_LINEAR)
+ alignment = iopgsz_max(bytes);
+ start = roundup(start, alignment);
+ } else if (start < obj->da_start || start > obj->da_end ||
+ obj->da_end - start < bytes) {
+ return ERR_PTR(-EINVAL);
+ }
+
+ tmp = NULL;
+ if (list_empty(&obj->mmap))
+ goto found;
+
+ prev_end = 0;
+ list_for_each_entry(tmp, &obj->mmap, list) {
+
+ if (prev_end > start)
+ break;
+
+ if (tmp->da_start > start && (tmp->da_start - start) >= bytes)
+ goto found;
+
+ if (tmp->da_end >= start && ~flags & IOVMF_DA_FIXED)
+ start = roundup(tmp->da_end + 1, alignment);
+
+ prev_end = tmp->da_end;
+ }
+
+ if ((start >= prev_end) && (obj->da_end - start >= bytes))
+ goto found;
+
+ dev_dbg(obj->dev, "%s: no space to fit %08x(%x) flags: %08x\n",
+ __func__, da, bytes, flags);
+
+ return ERR_PTR(-EINVAL);
+
+found:
+ new = kmem_cache_zalloc(iovm_area_cachep, GFP_KERNEL);
+ if (!new)
+ return ERR_PTR(-ENOMEM);
+
+ new->iommu = obj;
+ new->da_start = start;
+ new->da_end = start + bytes;
+ new->flags = flags;
+
+ /*
+ * keep ascending order of iovmas
+ */
+ if (tmp)
+ list_add_tail(&new->list, &tmp->list);
+ else
+ list_add(&new->list, &obj->mmap);
+
+ dev_dbg(obj->dev, "%s: found %08x-%08x-%08x(%x) %08x\n",
+ __func__, new->da_start, start, new->da_end, bytes, flags);
+
+ return new;
+}
+
+static void free_iovm_area(struct omap_iommu *obj, struct iovm_struct *area)
+{
+ size_t bytes;
+
+ BUG_ON(!obj || !area);
+
+ bytes = area->da_end - area->da_start;
+
+ dev_dbg(obj->dev, "%s: %08x-%08x(%x) %08x\n",
+ __func__, area->da_start, area->da_end, bytes, area->flags);
+
+ list_del(&area->list);
+ kmem_cache_free(iovm_area_cachep, area);
+}
+
+/**
+ * omap_da_to_va - convert (d) to (v)
+ * @obj: objective iommu
+ * @da: iommu device virtual address
+ * @va: mpu virtual address
+ *
+ * Returns mpu virtual addr which corresponds to a given device virtual addr
+ */
+void *omap_da_to_va(struct omap_iommu *obj, u32 da)
+{
+ void *va = NULL;
+ struct iovm_struct *area;
+
+ mutex_lock(&obj->mmap_lock);
+
+ area = __find_iovm_area(obj, da);
+ if (!area) {
+ dev_dbg(obj->dev, "%s: no da area(%08x)\n", __func__, da);
+ goto out;
+ }
+ va = area->va;
+out:
+ mutex_unlock(&obj->mmap_lock);
+
+ return va;
+}
+EXPORT_SYMBOL_GPL(omap_da_to_va);
+
+static void sgtable_fill_vmalloc(struct sg_table *sgt, void *_va)
+{
+ unsigned int i;
+ struct scatterlist *sg;
+ void *va = _va;
+ void *va_end;
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ struct page *pg;
+ const size_t bytes = PAGE_SIZE;
+
+ /*
+ * iommu 'superpage' isn't supported with 'omap_iommu_vmalloc()'
+ */
+ pg = vmalloc_to_page(va);
+ BUG_ON(!pg);
+ sg_set_page(sg, pg, bytes, 0);
+
+ va += bytes;
+ }
+
+ va_end = _va + PAGE_SIZE * i;
+}
+
+static inline void sgtable_drain_vmalloc(struct sg_table *sgt)
+{
+ /*
+ * Actually this is not necessary at all, just exists for
+ * consistency of the code readability.
+ */
+ BUG_ON(!sgt);
+}
+
+/* create 'da' <-> 'pa' mapping from 'sgt' */
+static int map_iovm_area(struct iommu_domain *domain, struct iovm_struct *new,
+ const struct sg_table *sgt, u32 flags)
+{
+ int err;
+ unsigned int i, j;
+ struct scatterlist *sg;
+ u32 da = new->da_start;
+ int order;
+
+ if (!domain || !sgt)
+ return -EINVAL;
+
+ BUG_ON(!sgtable_ok(sgt));
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ u32 pa;
+ size_t bytes;
+
+ pa = sg_phys(sg) - sg->offset;
+ bytes = sg->length + sg->offset;
+
+ flags &= ~IOVMF_PGSZ_MASK;
+
+ if (bytes_to_iopgsz(bytes) < 0)
+ goto err_out;
+
+ order = get_order(bytes);
+
+ pr_debug("%s: [%d] %08x %08x(%x)\n", __func__,
+ i, da, pa, bytes);
+
+ err = iommu_map(domain, da, pa, order, flags);
+ if (err)
+ goto err_out;
+
+ da += bytes;
+ }
+ return 0;
+
+err_out:
+ da = new->da_start;
+
+ for_each_sg(sgt->sgl, sg, i, j) {
+ size_t bytes;
+
+ bytes = sg->length + sg->offset;
+ order = get_order(bytes);
+
+ /* ignore failures.. we're already handling one */
+ iommu_unmap(domain, da, order);
+
+ da += bytes;
+ }
+ return err;
+}
+
+/* release 'da' <-> 'pa' mapping */
+static void unmap_iovm_area(struct iommu_domain *domain, struct omap_iommu *obj,
+ struct iovm_struct *area)
+{
+ u32 start;
+ size_t total = area->da_end - area->da_start;
+ const struct sg_table *sgt = area->sgt;
+ struct scatterlist *sg;
+ int i, err;
+
+ BUG_ON(!sgtable_ok(sgt));
+ BUG_ON((!total) || !IS_ALIGNED(total, PAGE_SIZE));
+
+ start = area->da_start;
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ size_t bytes;
+ int order;
+
+ bytes = sg->length + sg->offset;
+ order = get_order(bytes);
+
+ err = iommu_unmap(domain, start, order);
+ if (err < 0)
+ break;
+
+ dev_dbg(obj->dev, "%s: unmap %08x(%x) %08x\n",
+ __func__, start, bytes, area->flags);
+
+ BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE));
+
+ total -= bytes;
+ start += bytes;
+ }
+ BUG_ON(total);
+}
+
+/* template function for all unmapping */
+static struct sg_table *unmap_vm_area(struct iommu_domain *domain,
+ struct omap_iommu *obj, const u32 da,
+ void (*fn)(const void *), u32 flags)
+{
+ struct sg_table *sgt = NULL;
+ struct iovm_struct *area;
+
+ if (!IS_ALIGNED(da, PAGE_SIZE)) {
+ dev_err(obj->dev, "%s: alignment err(%08x)\n", __func__, da);
+ return NULL;
+ }
+
+ mutex_lock(&obj->mmap_lock);
+
+ area = __find_iovm_area(obj, da);
+ if (!area) {
+ dev_dbg(obj->dev, "%s: no da area(%08x)\n", __func__, da);
+ goto out;
+ }
+
+ if ((area->flags & flags) != flags) {
+ dev_err(obj->dev, "%s: wrong flags(%08x)\n", __func__,
+ area->flags);
+ goto out;
+ }
+ sgt = (struct sg_table *)area->sgt;
+
+ unmap_iovm_area(domain, obj, area);
+
+ fn(area->va);
+
+ dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n", __func__,
+ area->da_start, da, area->da_end,
+ area->da_end - area->da_start, area->flags);
+
+ free_iovm_area(obj, area);
+out:
+ mutex_unlock(&obj->mmap_lock);
+
+ return sgt;
+}
+
+static u32 map_iommu_region(struct iommu_domain *domain, struct omap_iommu *obj,
+ u32 da, const struct sg_table *sgt, void *va,
+ size_t bytes, u32 flags)
+{
+ int err = -ENOMEM;
+ struct iovm_struct *new;
+
+ mutex_lock(&obj->mmap_lock);
+
+ new = alloc_iovm_area(obj, da, bytes, flags);
+ if (IS_ERR(new)) {
+ err = PTR_ERR(new);
+ goto err_alloc_iovma;
+ }
+ new->va = va;
+ new->sgt = sgt;
+
+ if (map_iovm_area(domain, new, sgt, new->flags))
+ goto err_map;
+
+ mutex_unlock(&obj->mmap_lock);
+
+ dev_dbg(obj->dev, "%s: da:%08x(%x) flags:%08x va:%p\n",
+ __func__, new->da_start, bytes, new->flags, va);
+
+ return new->da_start;
+
+err_map:
+ free_iovm_area(obj, new);
+err_alloc_iovma:
+ mutex_unlock(&obj->mmap_lock);
+ return err;
+}
+
+static inline u32
+__iommu_vmap(struct iommu_domain *domain, struct omap_iommu *obj,
+ u32 da, const struct sg_table *sgt,
+ void *va, size_t bytes, u32 flags)
+{
+ return map_iommu_region(domain, obj, da, sgt, va, bytes, flags);
+}
+
+/**
+ * omap_iommu_vmap - (d)-(p)-(v) address mapper
+ * @obj: objective iommu
+ * @sgt: address of scatter gather table
+ * @flags: iovma and page property
+ *
+ * Creates 1-n-1 mapping with given @sgt and returns @da.
+ * All @sgt element must be io page size aligned.
+ */
+u32 omap_iommu_vmap(struct iommu_domain *domain, struct omap_iommu *obj, u32 da,
+ const struct sg_table *sgt, u32 flags)
+{
+ size_t bytes;
+ void *va = NULL;
+
+ if (!obj || !obj->dev || !sgt)
+ return -EINVAL;
+
+ bytes = sgtable_len(sgt);
+ if (!bytes)
+ return -EINVAL;
+ bytes = PAGE_ALIGN(bytes);
+
+ if (flags & IOVMF_MMIO) {
+ va = vmap_sg(sgt);
+ if (IS_ERR(va))
+ return PTR_ERR(va);
+ }
+
+ flags |= IOVMF_DISCONT;
+ flags |= IOVMF_MMIO;
+
+ da = __iommu_vmap(domain, obj, da, sgt, va, bytes, flags);
+ if (IS_ERR_VALUE(da))
+ vunmap_sg(va);
+
+ return da + sgtable_offset(sgt);
+}
+EXPORT_SYMBOL_GPL(omap_iommu_vmap);
+
+/**
+ * omap_iommu_vunmap - release virtual mapping obtained by 'omap_iommu_vmap()'
+ * @obj: objective iommu
+ * @da: iommu device virtual address
+ *
+ * Free the iommu virtually contiguous memory area starting at
+ * @da, which was returned by 'omap_iommu_vmap()'.
+ */
+struct sg_table *
+omap_iommu_vunmap(struct iommu_domain *domain, struct omap_iommu *obj, u32 da)
+{
+ struct sg_table *sgt;
+ /*
+ * 'sgt' is allocated before 'omap_iommu_vmalloc()' is called.
+ * Just returns 'sgt' to the caller to free
+ */
+ da &= PAGE_MASK;
+ sgt = unmap_vm_area(domain, obj, da, vunmap_sg,
+ IOVMF_DISCONT | IOVMF_MMIO);
+ if (!sgt)
+ dev_dbg(obj->dev, "%s: No sgt\n", __func__);
+ return sgt;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_vunmap);
+
+/**
+ * omap_iommu_vmalloc - (d)-(p)-(v) address allocator and mapper
+ * @obj: objective iommu
+ * @da: contiguous iommu virtual memory
+ * @bytes: allocation size
+ * @flags: iovma and page property
+ *
+ * Allocate @bytes linearly and creates 1-n-1 mapping and returns
+ * @da again, which might be adjusted if 'IOVMF_DA_FIXED' is not set.
+ */
+u32
+omap_iommu_vmalloc(struct iommu_domain *domain, struct omap_iommu *obj, u32 da,
+ size_t bytes, u32 flags)
+{
+ void *va;
+ struct sg_table *sgt;
+
+ if (!obj || !obj->dev || !bytes)
+ return -EINVAL;
+
+ bytes = PAGE_ALIGN(bytes);
+
+ va = vmalloc(bytes);
+ if (!va)
+ return -ENOMEM;
+
+ flags |= IOVMF_DISCONT;
+ flags |= IOVMF_ALLOC;
+
+ sgt = sgtable_alloc(bytes, flags, da, 0);
+ if (IS_ERR(sgt)) {
+ da = PTR_ERR(sgt);
+ goto err_sgt_alloc;
+ }
+ sgtable_fill_vmalloc(sgt, va);
+
+ da = __iommu_vmap(domain, obj, da, sgt, va, bytes, flags);
+ if (IS_ERR_VALUE(da))
+ goto err_iommu_vmap;
+
+ return da;
+
+err_iommu_vmap:
+ sgtable_drain_vmalloc(sgt);
+ sgtable_free(sgt);
+err_sgt_alloc:
+ vfree(va);
+ return da;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_vmalloc);
+
+/**
+ * omap_iommu_vfree - release memory allocated by 'omap_iommu_vmalloc()'
+ * @obj: objective iommu
+ * @da: iommu device virtual address
+ *
+ * Frees the iommu virtually continuous memory area starting at
+ * @da, as obtained from 'omap_iommu_vmalloc()'.
+ */
+void omap_iommu_vfree(struct iommu_domain *domain, struct omap_iommu *obj,
+ const u32 da)
+{
+ struct sg_table *sgt;
+
+ sgt = unmap_vm_area(domain, obj, da, vfree,
+ IOVMF_DISCONT | IOVMF_ALLOC);
+ if (!sgt)
+ dev_dbg(obj->dev, "%s: No sgt\n", __func__);
+ sgtable_free(sgt);
+}
+EXPORT_SYMBOL_GPL(omap_iommu_vfree);
+
+static int __init iovmm_init(void)
+{
+ const unsigned long flags = SLAB_HWCACHE_ALIGN;
+ struct kmem_cache *p;
+
+ p = kmem_cache_create("iovm_area_cache", sizeof(struct iovm_struct), 0,
+ flags, NULL);
+ if (!p)
+ return -ENOMEM;
+ iovm_area_cachep = p;
+
+ return 0;
+}
+module_init(iovmm_init);
+
+static void __exit iovmm_exit(void)
+{
+ kmem_cache_destroy(iovm_area_cachep);
+}
+module_exit(iovmm_exit);
+
+MODULE_DESCRIPTION("omap iommu: simple virtual address space management");
+MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/isdn/Kconfig b/drivers/isdn/Kconfig
index 4fb601670de3..a233ed53913a 100644
--- a/drivers/isdn/Kconfig
+++ b/drivers/isdn/Kconfig
@@ -5,7 +5,7 @@
menuconfig ISDN
bool "ISDN support"
depends on NET
- depends on !S390
+ depends on !S390 && !UML
---help---
ISDN ("Integrated Services Digital Network", called RNIS in France)
is a fully digital telephone service that can be used for voice and
diff --git a/drivers/isdn/capi/kcapi_proc.c b/drivers/isdn/capi/kcapi_proc.c
index ea2dff602e49..8d51cd1bf674 100644
--- a/drivers/isdn/capi/kcapi_proc.c
+++ b/drivers/isdn/capi/kcapi_proc.c
@@ -14,6 +14,7 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
+#include <linux/export.h>
static char *state2str(unsigned short state)
{
diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
index c3b1dc3a13a0..fddae72e3f98 100644
--- a/drivers/isdn/gigaset/asyncdata.c
+++ b/drivers/isdn/gigaset/asyncdata.c
@@ -16,6 +16,7 @@
#include "gigaset.h"
#include <linux/crc-ccitt.h>
#include <linux/bitrev.h>
+#include <linux/export.h>
/* check if byte must be stuffed/escaped
* I'm not sure which data should be encoded.
diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
index 658e75f18d05..6d5ceeece9f2 100644
--- a/drivers/isdn/gigaset/capi.c
+++ b/drivers/isdn/gigaset/capi.c
@@ -17,6 +17,7 @@
#include <linux/isdn/capilli.h>
#include <linux/isdn/capicmd.h>
#include <linux/isdn/capiutil.h>
+#include <linux/export.h>
/* missing from kernelcapi.h */
#define CapiNcpiNotSupportedByProtocol 0x0001
diff --git a/drivers/isdn/gigaset/dummyll.c b/drivers/isdn/gigaset/dummyll.c
index bd0b1eaa7572..19b1c779d50f 100644
--- a/drivers/isdn/gigaset/dummyll.c
+++ b/drivers/isdn/gigaset/dummyll.c
@@ -11,6 +11,7 @@
* =====================================================================
*/
+#include <linux/export.h>
#include "gigaset.h"
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
index ba74646cf0e4..6d12623c1db0 100644
--- a/drivers/isdn/gigaset/ev-layer.c
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -13,6 +13,7 @@
* =====================================================================
*/
+#include <linux/export.h>
#include "gigaset.h"
/* ========================================================== */
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index 6dd360734cfd..212efaf9a4e4 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -34,7 +34,7 @@
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/list.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#define GIG_VERSION {0, 5, 0, 0}
#define GIG_COMPAT {0, 4, 0, 0}
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c
index 9bec8b969964..04231cb2f031 100644
--- a/drivers/isdn/gigaset/i4l.c
+++ b/drivers/isdn/gigaset/i4l.c
@@ -15,6 +15,7 @@
#include "gigaset.h"
#include <linux/isdnif.h>
+#include <linux/export.h>
#define SBUFSIZE 4096 /* sk_buff payload size */
#define TRANSBUFSIZE 768 /* bytes per skb for transparent receive */
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
index e35058bcd7b9..ee0a549a933a 100644
--- a/drivers/isdn/gigaset/interface.c
+++ b/drivers/isdn/gigaset/interface.c
@@ -14,6 +14,7 @@
#include "gigaset.h"
#include <linux/gigaset_dev.h>
#include <linux/tty_flip.h>
+#include <linux/module.h>
/*** our ioctls ***/
diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c
index 472a2af79446..861b6511f3ee 100644
--- a/drivers/isdn/hardware/mISDN/avmfritz.c
+++ b/drivers/isdn/hardware/mISDN/avmfritz.c
@@ -20,6 +20,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index f6f3c87cc7c2..a440d7fff0ad 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -152,6 +152,7 @@
#define HFC_MULTI_VERSION "2.03"
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index b01a7be1300f..3261de18a91e 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -44,6 +44,7 @@
*
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
index bc0529ac88a1..6218775ce87d 100644
--- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c
+++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
@@ -38,6 +38,7 @@
*
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c
index 64ecc6f5ffaf..d2ffb1d9b831 100644
--- a/drivers/isdn/hardware/mISDN/mISDNipac.c
+++ b/drivers/isdn/hardware/mISDN/mISDNipac.c
@@ -20,6 +20,7 @@
*
*/
+#include <linux/irqreturn.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mISDNhw.h>
diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c
index d13fa5b119f5..7034af28d464 100644
--- a/drivers/isdn/hardware/mISDN/mISDNisar.c
+++ b/drivers/isdn/hardware/mISDN/mISDNisar.c
@@ -29,6 +29,7 @@
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include <linux/mISDNhw.h>
+#include <linux/module.h>
#include "isar.h"
#define ISAR_REV "2.1"
diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c
index db25b6b2ae39..5ef9f11ee74b 100644
--- a/drivers/isdn/hardware/mISDN/netjet.c
+++ b/drivers/isdn/hardware/mISDN/netjet.c
@@ -20,6 +20,7 @@
*
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c
index 9e07246bb9e7..4d0d41ea1228 100644
--- a/drivers/isdn/hardware/mISDN/speedfax.c
+++ b/drivers/isdn/hardware/mISDN/speedfax.c
@@ -22,6 +22,7 @@
*
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c
index 9e84870b971c..e10e0284533c 100644
--- a/drivers/isdn/hardware/mISDN/w6692.c
+++ b/drivers/isdn/hardware/mISDN/w6692.c
@@ -21,6 +21,7 @@
*
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c
index 37e685eafd24..c4897e1075d8 100644
--- a/drivers/isdn/hisax/callc.c
+++ b/drivers/isdn/hisax/callc.c
@@ -65,7 +65,7 @@ hisax_findcard(int driverid)
return (struct IsdnCardState *) 0;
}
-static __attribute__((format(printf, 3, 4))) void
+static __printf(3, 4) void
link_debug(struct Channel *chanp, int direction, char *fmt, ...)
{
va_list args;
@@ -1068,7 +1068,7 @@ init_d_st(struct Channel *chanp)
return 0;
}
-static __attribute__((format(printf, 2, 3))) void
+static __printf(2, 3) void
callc_debug(struct FsmInst *fi, char *fmt, ...)
{
va_list args;
diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h
index de1c669c7b13..aff45a11a92d 100644
--- a/drivers/isdn/hisax/hisax.h
+++ b/drivers/isdn/hisax/hisax.h
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/mman.h>
+#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/timer.h>
#include <linux/wait.h>
@@ -1286,9 +1287,9 @@ int jiftime(char *s, long mark);
int HiSax_command(isdn_ctrl * ic);
int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
-__attribute__((format(printf, 3, 4)))
+__printf(3, 4)
void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...);
-__attribute__((format(printf, 3, 0)))
+__printf(3, 0)
void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args);
void HiSax_reportcard(int cardnr, int sel);
int QuickHex(char *txt, u_char * p, int cnt);
diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c
index 8b0a7d86b30f..478ebab54ca4 100644
--- a/drivers/isdn/hisax/hisax_fcpcipnp.c
+++ b/drivers/isdn/hisax/hisax_fcpcipnp.c
@@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/isapnp.h>
#include <linux/kmod.h>
diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h
index 425d86116f2b..66ddcab19bba 100644
--- a/drivers/isdn/hisax/isdnl1.h
+++ b/drivers/isdn/hisax/isdnl1.h
@@ -21,7 +21,7 @@
#define B_XMTBUFREADY 1
#define B_ACKPENDING 2
-__attribute__((format(printf, 2, 3)))
+__printf(2, 3)
void debugl1(struct IsdnCardState *cs, char *fmt, ...);
void DChannel_proc_xmt(struct IsdnCardState *cs);
void DChannel_proc_rcv(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c
index ad291f21b201..1c24e4457b6f 100644
--- a/drivers/isdn/hisax/isdnl3.c
+++ b/drivers/isdn/hisax/isdnl3.c
@@ -66,7 +66,7 @@ static char *strL3Event[] =
"EV_TIMEOUT",
};
-static __attribute__((format(printf, 2, 3))) void
+static __printf(2, 3) void
l3m_debug(struct FsmInst *fi, char *fmt, ...)
{
va_list args;
diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c
index b0d9ab1f21c0..6a8acf65777d 100644
--- a/drivers/isdn/hisax/l3dss1.c
+++ b/drivers/isdn/hisax/l3dss1.c
@@ -353,7 +353,7 @@ l3dss1_parse_facility(struct PStack *st, struct l3_process *pc,
{ l3dss1_dummy_invoke(st, cr, id, ident, p, nlen);
return;
}
-#ifdef HISAX_DE_AOC
+#ifdef CONFIG_DE_AOC
{
#define FOO1(s,a,b) \
@@ -422,9 +422,9 @@ l3dss1_parse_facility(struct PStack *st, struct l3_process *pc,
#undef FOO1
}
-#else /* not HISAX_DE_AOC */
+#else /* not CONFIG_DE_AOC */
l3_debug(st, "invoke break");
-#endif /* not HISAX_DE_AOC */
+#endif /* not CONFIG_DE_AOC */
break;
case 2: /* return result */
/* if no process available handle separately */
diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c
index 44082637a09f..db247b79e561 100644
--- a/drivers/isdn/hisax/st5481_d.c
+++ b/drivers/isdn/hisax/st5481_d.c
@@ -167,7 +167,7 @@ static struct FsmNode L1FnList[] __initdata =
{ST_L1_F8, EV_IND_RSY, l1_ignore},
};
-static __attribute__((format(printf, 2, 3)))
+static __printf(2, 3)
void l1m_debug(struct FsmInst *fi, char *fmt, ...)
{
va_list args;
@@ -270,7 +270,7 @@ static char *strDoutEvent[] =
"EV_DOUT_UNDERRUN",
};
-static __attribute__((format(printf, 2, 3)))
+static __printf(2, 3)
void dout_debug(struct FsmInst *fi, char *fmt, ...)
{
va_list args;
diff --git a/drivers/isdn/i4l/isdn_bsdcomp.c b/drivers/isdn/i4l/isdn_bsdcomp.c
index 02d9918705dd..aa0b6a6f5ef4 100644
--- a/drivers/isdn/i4l/isdn_bsdcomp.c
+++ b/drivers/isdn/i4l/isdn_bsdcomp.c
@@ -155,7 +155,7 @@ struct bsd_db {
#define LAST 255
#define MAXCODE(b) ((1 << (b)) - 1)
-#define BADCODEM1 MAXCODE(MAX_BSD_BITS);
+#define BADCODEM1 MAXCODE(MAX_BSD_BITS)
#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
^ (unsigned long)(prefix))
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
index 6ed82add6ffa..6ddb795e31c5 100644
--- a/drivers/isdn/i4l/isdn_common.c
+++ b/drivers/isdn/i4l/isdn_common.c
@@ -2308,11 +2308,11 @@ static int __init isdn_init(void)
int i;
char tmprev[50];
- if (!(dev = vmalloc(sizeof(isdn_dev)))) {
+ dev = vzalloc(sizeof(isdn_dev));
+ if (!dev) {
printk(KERN_WARNING "isdn: Could not allocate device-struct.\n");
return -EIO;
}
- memset((char *) dev, 0, sizeof(isdn_dev));
init_timer(&dev->timer);
dev->timer.function = isdn_timer_funct;
spin_lock_init(&dev->lock);
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 97988111e45a..1f73d7f7e024 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -1983,13 +1983,14 @@ isdn_net_rebuild_header(struct sk_buff *skb)
return ret;
}
-static int isdn_header_cache(const struct neighbour *neigh, struct hh_cache *hh)
+static int isdn_header_cache(const struct neighbour *neigh, struct hh_cache *hh,
+ __be16 type)
{
const struct net_device *dev = neigh->dev;
isdn_net_local *lp = netdev_priv(dev);
if (lp->p_encap == ISDN_NET_ENCAP_ETHER)
- return eth_header_cache(neigh, hh);
+ return eth_header_cache(neigh, hh, type);
return -1;
}
@@ -2531,6 +2532,9 @@ static void _isdn_setup(struct net_device *dev)
/* Setup the generic properties */
dev->flags = IFF_NOARP|IFF_POINTOPOINT;
+
+ /* isdn prepends a header in the tx path, can't share skbs */
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
dev->header_ops = NULL;
dev->netdev_ops = &isdn_netdev_ops;
diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c
index d8504279e502..2c26b64ebbea 100644
--- a/drivers/isdn/i4l/isdn_tty.c
+++ b/drivers/isdn/i4l/isdn_tty.c
@@ -750,6 +750,7 @@ isdn_tty_modem_hup(modem_info * info, int local)
* supplementary service (CAPI 2.0 part III)
*/
#include <linux/isdn/capicmd.h>
+#include <linux/module.h>
int
isdn_tty_capi_facility(capi_msg *cm) {
@@ -1693,7 +1694,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp)
* line status register.
*/
if (info->flags & ISDN_ASYNC_INITIALIZED) {
- tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
+ tty_wait_until_sent_from_close(tty, 3000); /* 30 seconds timeout */
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
diff --git a/drivers/isdn/mISDN/clock.c b/drivers/isdn/mISDN/clock.c
index 1fa629b3b940..7418f2d811d8 100644
--- a/drivers/isdn/mISDN/clock.c
+++ b/drivers/isdn/mISDN/clock.c
@@ -38,6 +38,7 @@
#include <linux/stddef.h>
#include <linux/spinlock.h>
#include <linux/mISDNif.h>
+#include <linux/export.h>
#include "core.h"
static u_int *debug;
diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c
index 9c7c6451bf3d..b8f18bd09e43 100644
--- a/drivers/isdn/mISDN/dsp_audio.c
+++ b/drivers/isdn/mISDN/dsp_audio.c
@@ -12,6 +12,7 @@
#include <linux/delay.h>
#include <linux/mISDNif.h>
#include <linux/mISDNdsp.h>
+#include <linux/export.h>
#include "core.h"
#include "dsp.h"
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
index 2877291a9ed8..0c41553ce684 100644
--- a/drivers/isdn/mISDN/dsp_core.c
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -1052,12 +1052,11 @@ dspcreate(struct channel_req *crq)
if (crq->protocol != ISDN_P_B_L2DSP
&& crq->protocol != ISDN_P_B_L2DSPHDLC)
return -EPROTONOSUPPORT;
- ndsp = vmalloc(sizeof(struct dsp));
+ ndsp = vzalloc(sizeof(struct dsp));
if (!ndsp) {
printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__);
return -ENOMEM;
}
- memset(ndsp, 0, sizeof(struct dsp));
if (dsp_debug & DEBUG_DSP_CTRL)
printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__);
diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c
index 621f31007095..b6c9a5889346 100644
--- a/drivers/isdn/mISDN/dsp_pipeline.c
+++ b/drivers/isdn/mISDN/dsp_pipeline.c
@@ -30,6 +30,7 @@
#include <linux/string.h>
#include <linux/mISDNif.h>
#include <linux/mISDNdsp.h>
+#include <linux/export.h>
#include "dsp.h"
#include "dsp_hwec.h"
diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c
index bbfd1b863ed3..5a89972624d8 100644
--- a/drivers/isdn/mISDN/l1oip_codec.c
+++ b/drivers/isdn/mISDN/l1oip_codec.c
@@ -330,14 +330,12 @@ l1oip_4bit_alloc(int ulaw)
return 0;
/* alloc conversion tables */
- table_com = vmalloc(65536);
- table_dec = vmalloc(512);
+ table_com = vzalloc(65536);
+ table_dec = vzalloc(512);
if (!table_com || !table_dec) {
l1oip_4bit_free();
return -ENOMEM;
}
- memset(table_com, 0, 65536);
- memset(table_dec, 0, 512);
/* generate compression table */
i1 = 0;
while (i1 < 256) {
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
index 8e325227b4c0..738ea8dd0adf 100644
--- a/drivers/isdn/mISDN/socket.c
+++ b/drivers/isdn/mISDN/socket.c
@@ -17,6 +17,7 @@
#include <linux/mISDNif.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "core.h"
static u_int *debug;
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 713d43b4e563..ff203a421863 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -92,7 +92,7 @@ config LEDS_NET48XX
config LEDS_NET5501
tristate "LED Support for Soekris net5501 series Error LED"
depends on LEDS_TRIGGERS
- depends on X86 && LEDS_GPIO_PLATFORM && GPIO_CS5535
+ depends on X86 && GPIO_CS5535
select LEDS_TRIGGER_DEFAULT_ON
default n
help
@@ -113,14 +113,6 @@ config LEDS_WRAP
help
This option enables support for the PCEngines WRAP programmable LEDs.
-config LEDS_ALIX2
- tristate "LED Support for ALIX.2 and ALIX.3 series"
- depends on LEDS_CLASS
- depends on X86 && !GPIO_CS5535 && !CS5535_GPIO
- help
- This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs.
- You have to set leds-alix2.force=1 for boards with Award BIOS.
-
config LEDS_COBALT_QUBE
tristate "LED Support for the Cobalt Qube series front LED"
depends on LEDS_CLASS
@@ -182,23 +174,6 @@ config LEDS_GPIO
defined as platform devices and/or OpenFirmware platform devices.
The code to use these bindings can be selected below.
-config LEDS_GPIO_PLATFORM
- bool "Platform device bindings for GPIO LEDs"
- depends on LEDS_GPIO
- default y
- help
- Let the leds-gpio driver drive LEDs which have been defined as
- platform devices. If you don't know what this means, say yes.
-
-config LEDS_GPIO_OF
- bool "OpenFirmware platform device bindings for GPIO LEDs"
- depends on LEDS_GPIO && OF_DEVICE
- default y
- help
- Let the leds-gpio driver drive LEDs which have been defined as
- of_platform devices. For instance, LEDs which are listed in a "dts"
- file.
-
config LEDS_LP3944
tristate "LED Support for N.S. LP3944 (Fun Light) I2C chip"
depends on LEDS_CLASS
@@ -382,6 +357,7 @@ config LEDS_NS2
config LEDS_NETXBIG
tristate "LED support for Big Network series LEDs"
depends on MACH_NET2BIG_V2 || MACH_NET5BIG_V2
+ depends on LEDS_CLASS
default y
help
This option enable support for LEDs found on the LaCie 2Big
@@ -399,6 +375,18 @@ config LEDS_ASIC3
cannot be used. This driver supports hardware blinking with an on+off
period from 62ms to 125s. Say Y to enable LEDs on the HP iPAQ hx4700.
+config LEDS_RENESAS_TPU
+ bool "LED support for Renesas TPU"
+ depends on LEDS_CLASS && HAVE_CLK && GENERIC_GPIO
+ help
+ This option enables build of the LED TPU platform driver,
+ suitable to drive any TPU channel on newer Renesas SoCs.
+ The driver controls the GPIO pin connected to the LED via
+ the GPIO framework and expects the LED to be connected to
+ a pin that can be driven in both GPIO mode and using TPU
+ pin function. The latter to support brightness control.
+ Brightness control is supported but hardware blinking is not.
+
config LEDS_TRIGGERS
bool "LED Trigger support"
depends on LEDS_CLASS
@@ -416,7 +404,7 @@ config LEDS_TRIGGER_TIMER
This allows LEDs to be controlled by a programmable timer
via sysfs. Some LED hardware can be programmed to start
blinking the LED without any further software interaction.
- For more details read Documentation/leds-class.txt.
+ For more details read Documentation/leds/leds-class.txt.
If unsure, say Y.
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index bbfd2e367dc0..e4f6bf568880 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -16,7 +16,6 @@ obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
obj-$(CONFIG_LEDS_NET5501) += leds-net5501.o
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
-obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
@@ -43,6 +42,7 @@ obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
+obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/dell-led.c b/drivers/leds/dell-led.c
index 52590296af33..e5c57389efd6 100644
--- a/drivers/leds/dell-led.c
+++ b/drivers/leds/dell-led.c
@@ -14,6 +14,7 @@
#include <linux/acpi.h>
#include <linux/leds.h>
#include <linux/slab.h>
+#include <linux/module.h>
MODULE_AUTHOR("Louis Davis/Jim Dailey");
MODULE_DESCRIPTION("Dell LED Control Driver");
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index dc3d3d83191a..6d5628bb0601 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -267,6 +267,8 @@ void led_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
+ del_timer_sync(&led_cdev->blink_timer);
+
if (led_cdev->blink_set &&
!led_cdev->blink_set(led_cdev, delay_on, delay_off))
return;
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 4bebae733349..6f1ff93d7cec 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -261,9 +261,12 @@ void led_trigger_register_simple(const char *name, struct led_trigger **tp)
if (trigger) {
trigger->name = name;
err = led_trigger_register(trigger);
- if (err < 0)
+ if (err < 0) {
+ kfree(trigger);
+ trigger = NULL;
printk(KERN_WARNING "LED trigger %s failed to register"
" (%d)\n", name, err);
+ }
} else
printk(KERN_WARNING "LED trigger %s failed to register"
" (no memory)\n", name);
diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c
index 0d4c16678ace..0810604dc701 100644
--- a/drivers/leds/leds-88pm860x.c
+++ b/drivers/leds/leds-88pm860x.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/mfd/88pm860x.h>
+#include <linux/module.h>
#define LED_PWM_SHIFT (3)
#define LED_PWM_MASK (0x1F)
diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c
deleted file mode 100644
index f59ffadf5125..000000000000
--- a/drivers/leds/leds-alix2.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * LEDs driver for PCEngines ALIX.2 and ALIX.3
- *
- * Copyright (C) 2008 Constantin Baranov <const@mimas.ru>
- */
-
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/leds.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/string.h>
-#include <linux/pci.h>
-
-static int force = 0;
-module_param(force, bool, 0444);
-MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs");
-
-#define MSR_LBAR_GPIO 0x5140000C
-#define CS5535_GPIO_SIZE 256
-
-static u32 gpio_base;
-
-static struct pci_device_id divil_pci[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
- { } /* NULL entry */
-};
-MODULE_DEVICE_TABLE(pci, divil_pci);
-
-struct alix_led {
- struct led_classdev cdev;
- unsigned short port;
- unsigned int on_value;
- unsigned int off_value;
-};
-
-static void alix_led_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct alix_led *led_dev =
- container_of(led_cdev, struct alix_led, cdev);
-
- if (brightness)
- outl(led_dev->on_value, gpio_base + led_dev->port);
- else
- outl(led_dev->off_value, gpio_base + led_dev->port);
-}
-
-static struct alix_led alix_leds[] = {
- {
- .cdev = {
- .name = "alix:1",
- .brightness_set = alix_led_set,
- },
- .port = 0x00,
- .on_value = 1 << 22,
- .off_value = 1 << 6,
- },
- {
- .cdev = {
- .name = "alix:2",
- .brightness_set = alix_led_set,
- },
- .port = 0x80,
- .on_value = 1 << 25,
- .off_value = 1 << 9,
- },
- {
- .cdev = {
- .name = "alix:3",
- .brightness_set = alix_led_set,
- },
- .port = 0x80,
- .on_value = 1 << 27,
- .off_value = 1 << 11,
- },
-};
-
-static int __init alix_led_probe(struct platform_device *pdev)
-{
- int i;
- int ret;
-
- for (i = 0; i < ARRAY_SIZE(alix_leds); i++) {
- alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
- ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev);
- if (ret < 0)
- goto fail;
- }
- return 0;
-
-fail:
- while (--i >= 0)
- led_classdev_unregister(&alix_leds[i].cdev);
- return ret;
-}
-
-static int alix_led_remove(struct platform_device *pdev)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(alix_leds); i++)
- led_classdev_unregister(&alix_leds[i].cdev);
- return 0;
-}
-
-static struct platform_driver alix_led_driver = {
- .remove = alix_led_remove,
- .driver = {
- .name = KBUILD_MODNAME,
- .owner = THIS_MODULE,
- },
-};
-
-static int __init alix_present(unsigned long bios_phys,
- const char *alix_sig,
- size_t alix_sig_len)
-{
- const size_t bios_len = 0x00010000;
- const char *bios_virt;
- const char *scan_end;
- const char *p;
- char name[64];
-
- if (force) {
- printk(KERN_NOTICE "%s: forced to skip BIOS test, "
- "assume system has ALIX.2 style LEDs\n",
- KBUILD_MODNAME);
- return 1;
- }
-
- bios_virt = phys_to_virt(bios_phys);
- scan_end = bios_virt + bios_len - (alix_sig_len + 2);
- for (p = bios_virt; p < scan_end; p++) {
- const char *tail;
- char *a;
-
- if (memcmp(p, alix_sig, alix_sig_len) != 0)
- continue;
-
- memcpy(name, p, sizeof(name));
-
- /* remove the first \0 character from string */
- a = strchr(name, '\0');
- if (a)
- *a = ' ';
-
- /* cut the string at a newline */
- a = strchr(name, '\r');
- if (a)
- *a = '\0';
-
- tail = p + alix_sig_len;
- if ((tail[0] == '2' || tail[0] == '3')) {
- printk(KERN_INFO
- "%s: system is recognized as \"%s\"\n",
- KBUILD_MODNAME, name);
- return 1;
- }
- }
-
- return 0;
-}
-
-static struct platform_device *pdev;
-
-static int __init alix_pci_led_init(void)
-{
- u32 low, hi;
-
- if (pci_dev_present(divil_pci) == 0) {
- printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n");
- return -ENODEV;
- }
-
- /* Grab the GPIO I/O range */
- rdmsr(MSR_LBAR_GPIO, low, hi);
-
- /* Check the mask and whether GPIO is enabled (sanity check) */
- if (hi != 0x0000f001) {
- printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n");
- return -ENODEV;
- }
-
- /* Mask off the IO base address */
- gpio_base = low & 0x0000ff00;
-
- if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) {
- printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n");
- return -ENODEV;
- }
-
- /* Set GPIO function to output */
- outl(1 << 6, gpio_base + 0x04);
- outl(1 << 9, gpio_base + 0x84);
- outl(1 << 11, gpio_base + 0x84);
-
- return 0;
-}
-
-static int __init alix_led_init(void)
-{
- int ret = -ENODEV;
- const char tinybios_sig[] = "PC Engines ALIX.";
- const char coreboot_sig[] = "PC Engines\0ALIX.";
-
- if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) ||
- alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1))
- ret = alix_pci_led_init();
-
- if (ret < 0)
- return ret;
-
- pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
- if (!IS_ERR(pdev)) {
- ret = platform_driver_probe(&alix_led_driver, alix_led_probe);
- if (ret)
- platform_device_unregister(pdev);
- } else
- ret = PTR_ERR(pdev);
-
- return ret;
-}
-
-static void __exit alix_led_exit(void)
-{
- platform_device_unregister(pdev);
- platform_driver_unregister(&alix_led_driver);
- release_region(gpio_base, CS5535_GPIO_SIZE);
-}
-
-module_init(alix_led_init);
-module_exit(alix_led_exit);
-
-MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>");
-MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c
index b9826032450b..8c00937bf7e7 100644
--- a/drivers/leds/leds-ams-delta.c
+++ b/drivers/leds/leds-ams-delta.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c
index 22f847c890c9..48d9fe61bdfc 100644
--- a/drivers/leds/leds-asic3.c
+++ b/drivers/leds/leds-asic3.c
@@ -14,6 +14,7 @@
#include <linux/mfd/asic3.h>
#include <linux/mfd/core.h>
+#include <linux/module.h>
/*
* The HTC ASIC3 LED GPIOs are inputs, not outputs.
@@ -107,9 +108,10 @@ static int __devinit asic3_led_probe(struct platform_device *pdev)
}
led->cdev->name = led->name;
- led->cdev->default_trigger = led->default_trigger;
+ led->cdev->flags = LED_CORE_SUSPENDRESUME;
led->cdev->brightness_set = brightness_set;
led->cdev->blink_set = blink_set;
+ led->cdev->default_trigger = led->default_trigger;
ret = led_classdev_register(&pdev->dev, led->cdev);
if (ret < 0)
@@ -136,12 +138,44 @@ static int __devexit asic3_led_remove(struct platform_device *pdev)
return mfd_cell_disable(pdev);
}
+static int asic3_led_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+ int ret;
+
+ ret = 0;
+ if (cell->suspend)
+ ret = (*cell->suspend)(pdev);
+
+ return ret;
+}
+
+static int asic3_led_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+ int ret;
+
+ ret = 0;
+ if (cell->resume)
+ ret = (*cell->resume)(pdev);
+
+ return ret;
+}
+
+static const struct dev_pm_ops asic3_led_pm_ops = {
+ .suspend = asic3_led_suspend,
+ .resume = asic3_led_resume,
+};
+
static struct platform_driver asic3_led_driver = {
.probe = asic3_led_probe,
.remove = __devexit_p(asic3_led_remove),
.driver = {
.name = "leds-asic3",
.owner = THIS_MODULE,
+ .pm = &asic3_led_pm_ops,
},
};
diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c
index c941d906bba6..109c875ea233 100644
--- a/drivers/leds/leds-atmel-pwm.c
+++ b/drivers/leds/leds-atmel-pwm.c
@@ -4,6 +4,7 @@
#include <linux/io.h>
#include <linux/atmel_pwm.h>
#include <linux/slab.h>
+#include <linux/module.h>
struct pwmled {
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
index 3ebe3824662d..ea2185531f82 100644
--- a/drivers/leds/leds-bd2802.c
+++ b/drivers/leds/leds-bd2802.c
@@ -662,6 +662,11 @@ failed_unregister_led1_R:
static void bd2802_unregister_led_classdev(struct bd2802_led *led)
{
cancel_work_sync(&led->work);
+ led_classdev_unregister(&led->cdev_led2b);
+ led_classdev_unregister(&led->cdev_led2g);
+ led_classdev_unregister(&led->cdev_led2r);
+ led_classdev_unregister(&led->cdev_led1b);
+ led_classdev_unregister(&led->cdev_led1g);
led_classdev_unregister(&led->cdev_led1r);
}
diff --git a/drivers/leds/leds-cobalt-raq.c b/drivers/leds/leds-cobalt-raq.c
index 438d48384636..aac1c073fe7b 100644
--- a/drivers/leds/leds-cobalt-raq.c
+++ b/drivers/leds/leds-cobalt-raq.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/types.h>
+#include <linux/export.h>
#define LED_WEB 0x04
#define LED_POWER_OFF 0x08
diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c
index d11d05be0dee..49aceffaa5b6 100644
--- a/drivers/leds/leds-fsg.c
+++ b/drivers/leds/leds-fsg.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
+#include <linux/module.h>
#include <mach/hardware.h>
#include <asm/io.h>
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index b0480c8fbcbf..399a86f2013a 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -18,6 +18,7 @@
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/module.h>
#include <asm/gpio.h>
@@ -121,7 +122,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template,
}
led_dat->cdev.brightness_set = gpio_led_set;
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
- state = !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low;
+ state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low;
else
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
@@ -165,7 +166,7 @@ static inline int sizeof_gpio_leds_priv(int num_leds)
}
/* Code to create from OpenFirmware platform devices */
-#ifdef CONFIG_LEDS_GPIO_OF
+#ifdef CONFIG_OF_GPIO
static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node, *child;
@@ -223,13 +224,13 @@ static const struct of_device_id of_gpio_leds_match[] = {
{ .compatible = "gpio-leds", },
{},
};
-#else
+#else /* CONFIG_OF_GPIO */
static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev)
{
return NULL;
}
#define of_gpio_leds_match NULL
-#endif
+#endif /* CONFIG_OF_GPIO */
static int __devinit gpio_led_probe(struct platform_device *pdev)
diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c
index e4ce1fd46338..bcfbd3a60eab 100644
--- a/drivers/leds/leds-hp6xx.c
+++ b/drivers/leds/leds-hp6xx.c
@@ -10,6 +10,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index 4d7ce7631acf..0630e4f4b286 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -18,6 +18,7 @@
#include <linux/led-lm3530.h>
#include <linux/types.h>
#include <linux/regulator/consumer.h>
+#include <linux/module.h>
#define LM3530_LED_DEV "lcd-backlight"
#define LM3530_NAME "lm3530-led"
@@ -68,17 +69,16 @@
#define LM3530_ALS2_IMP_SHIFT (4)
/* Zone Boundary Register defaults */
-#define LM3530_DEF_ZB_0 (0x33)
-#define LM3530_DEF_ZB_1 (0x66)
-#define LM3530_DEF_ZB_2 (0x99)
-#define LM3530_DEF_ZB_3 (0xCC)
+#define LM3530_ALS_ZB_MAX (4)
+#define LM3530_ALS_WINDOW_mV (1000)
+#define LM3530_ALS_OFFSET_mV (4)
/* Zone Target Register defaults */
-#define LM3530_DEF_ZT_0 (0x19)
-#define LM3530_DEF_ZT_1 (0x33)
+#define LM3530_DEF_ZT_0 (0x7F)
+#define LM3530_DEF_ZT_1 (0x66)
#define LM3530_DEF_ZT_2 (0x4C)
-#define LM3530_DEF_ZT_3 (0x66)
-#define LM3530_DEF_ZT_4 (0x7F)
+#define LM3530_DEF_ZT_3 (0x33)
+#define LM3530_DEF_ZT_4 (0x19)
struct lm3530_mode_map {
const char *mode;
@@ -150,6 +150,8 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
u8 als_imp_sel = 0;
u8 brightness;
u8 reg_val[LM3530_REG_MAX];
+ u8 zones[LM3530_ALS_ZB_MAX];
+ u32 als_vmin, als_vmax, als_vstep;
struct lm3530_platform_data *pltfm = drvdata->pdata;
struct i2c_client *client = drvdata->client;
@@ -161,6 +163,26 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
gen_config |= (LM3530_ENABLE_I2C);
if (drvdata->mode == LM3530_BL_MODE_ALS) {
+ if (pltfm->als_vmax == 0) {
+ pltfm->als_vmin = als_vmin = 0;
+ pltfm->als_vmin = als_vmax = LM3530_ALS_WINDOW_mV;
+ }
+
+ als_vmin = pltfm->als_vmin;
+ als_vmax = pltfm->als_vmax;
+
+ if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)
+ pltfm->als_vmax = als_vmax =
+ als_vmin + LM3530_ALS_WINDOW_mV;
+
+ /* n zone boundary makes n+1 zones */
+ als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1);
+
+ for (i = 0; i < LM3530_ALS_ZB_MAX; i++)
+ zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) +
+ als_vstep + (i * als_vstep)) * LED_FULL)
+ / 1000;
+
als_config =
(pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
(LM3530_ENABLE_ALS) |
@@ -169,6 +191,7 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
als_imp_sel =
(pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
(pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
+
}
if (drvdata->mode == LM3530_BL_MODE_PWM)
@@ -190,10 +213,10 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
reg_val[3] = 0x00; /* LM3530_ALS_ZONE_REG */
reg_val[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */
- reg_val[6] = LM3530_DEF_ZB_0; /* LM3530_ALS_ZB0_REG */
- reg_val[7] = LM3530_DEF_ZB_1; /* LM3530_ALS_ZB1_REG */
- reg_val[8] = LM3530_DEF_ZB_2; /* LM3530_ALS_ZB2_REG */
- reg_val[9] = LM3530_DEF_ZB_3; /* LM3530_ALS_ZB3_REG */
+ reg_val[6] = zones[0]; /* LM3530_ALS_ZB0_REG */
+ reg_val[7] = zones[1]; /* LM3530_ALS_ZB1_REG */
+ reg_val[8] = zones[2]; /* LM3530_ALS_ZB2_REG */
+ reg_val[9] = zones[3]; /* LM3530_ALS_ZB3_REG */
reg_val[10] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */
reg_val[11] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */
reg_val[12] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */
@@ -265,6 +288,24 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
}
}
+static ssize_t lm3530_mode_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = container_of(
+ dev->parent, struct i2c_client, dev);
+ struct lm3530_data *drvdata = i2c_get_clientdata(client);
+ int i, len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(mode_map); i++)
+ if (drvdata->mode == mode_map[i].mode_val)
+ len += sprintf(buf + len, "[%s] ", mode_map[i].mode);
+ else
+ len += sprintf(buf + len, "%s ", mode_map[i].mode);
+
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
*attr, const char *buf, size_t size)
@@ -298,8 +339,7 @@ static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
return sizeof(drvdata->mode);
}
-
-static DEVICE_ATTR(mode, 0644, NULL, lm3530_mode_set);
+static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set);
static int __devinit lm3530_probe(struct i2c_client *client,
const struct i2c_device_id *id)
@@ -382,7 +422,6 @@ err_class_register:
err_reg_init:
regulator_put(drvdata->regulator);
err_regulator_get:
- i2c_set_clientdata(client, NULL);
kfree(drvdata);
err_out:
return err;
@@ -410,7 +449,7 @@ MODULE_DEVICE_TABLE(i2c, lm3530_id);
static struct i2c_driver lm3530_i2c_driver = {
.probe = lm3530_probe,
- .remove = lm3530_remove,
+ .remove = __devexit_p(lm3530_remove),
.id_table = lm3530_id,
.driver = {
.name = LM3530_NAME,
diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c
index 1f7c10f6b7f2..80ba048889d6 100644
--- a/drivers/leds/leds-locomo.c
+++ b/drivers/leds/leds-locomo.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/leds.h>
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index cc1dc4817fac..cb641f1b3342 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -97,6 +97,9 @@
/* Status */
#define LP5521_EXT_CLK_USED 0x08
+/* default R channel current register value */
+#define LP5521_REG_R_CURR_DEFAULT 0xAF
+
struct lp5521_engine {
int id;
u8 mode;
@@ -175,14 +178,14 @@ static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode)
mode = LP5521_CMD_DIRECT;
ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state);
+ if (ret < 0)
+ return ret;
/* set mode only for this engine */
engine_state &= ~(engine->engine_mask);
mode &= engine->engine_mask;
engine_state |= mode;
- ret |= lp5521_write(client, LP5521_REG_OP_MODE, engine_state);
-
- return ret;
+ return lp5521_write(client, LP5521_REG_OP_MODE, engine_state);
}
static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern)
@@ -643,6 +646,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
struct lp5521_chip *chip;
struct lp5521_platform_data *pdata;
int ret, i, led;
+ u8 buf;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -681,6 +685,20 @@ static int __devinit lp5521_probe(struct i2c_client *client,
* Exact value is not available. 10 - 20ms
* appears to be enough for reset.
*/
+
+ /*
+ * Make sure that the chip is reset by reading back the r channel
+ * current reg. This is dummy read is required on some platforms -
+ * otherwise further access to the R G B channels in the
+ * LP5521_REG_ENABLE register will not have any effect - strange!
+ */
+ lp5521_read(client, LP5521_REG_R_CURRENT, &buf);
+ if (buf != LP5521_REG_R_CURR_DEFAULT) {
+ dev_err(&client->dev, "error in reseting chip\n");
+ goto fail2;
+ }
+ usleep_range(10000, 20000);
+
ret = lp5521_detect(client);
if (ret) {
@@ -744,7 +762,7 @@ fail1:
return ret;
}
-static int lp5521_remove(struct i2c_client *client)
+static int __devexit lp5521_remove(struct i2c_client *client)
{
struct lp5521_chip *chip = i2c_get_clientdata(client);
int i;
@@ -775,7 +793,7 @@ static struct i2c_driver lp5521_driver = {
.name = "lp5521",
},
.probe = lp5521_probe,
- .remove = lp5521_remove,
+ .remove = __devexit_p(lp5521_remove),
.id_table = lp5521_id,
};
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c
index 2579678f97a6..53f67b8ce55d 100644
--- a/drivers/leds/leds-lt3593.c
+++ b/drivers/leds/leds-lt3593.c
@@ -24,6 +24,7 @@
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/slab.h>
+#include <linux/module.h>
struct lt3593_led_data {
struct led_classdev cdev;
diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c
index f369e56d6547..b3393a9f2139 100644
--- a/drivers/leds/leds-mc13783.c
+++ b/drivers/leds/leds-mc13783.c
@@ -21,13 +21,13 @@
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
-#include <linux/mfd/mc13783.h>
+#include <linux/mfd/mc13xxx.h>
#include <linux/slab.h>
struct mc13783_led {
struct led_classdev cdev;
struct work_struct work;
- struct mc13783 *master;
+ struct mc13xxx *master;
enum led_brightness new_brightness;
int id;
};
@@ -111,11 +111,11 @@ static void mc13783_led_work(struct work_struct *work)
break;
}
- mc13783_lock(led->master);
+ mc13xxx_lock(led->master);
- mc13783_reg_rmw(led->master, reg, mask, value);
+ mc13xxx_reg_rmw(led->master, reg, mask, value);
- mc13783_unlock(led->master);
+ mc13xxx_unlock(led->master);
}
static void mc13783_led_set(struct led_classdev *led_cdev,
@@ -172,23 +172,23 @@ static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current)
break;
}
- mc13783_lock(led->master);
+ mc13xxx_lock(led->master);
- ret = mc13783_reg_rmw(led->master, reg, mask << shift,
+ ret = mc13xxx_reg_rmw(led->master, reg, mask << shift,
value << shift);
- mc13783_unlock(led->master);
+ mc13xxx_unlock(led->master);
return ret;
}
static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
{
- struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent);
+ struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent);
int ret = 0;
int reg = 0;
- mc13783_lock(dev);
+ mc13xxx_lock(dev);
if (pdata->flags & MC13783_LED_TC1HALF)
reg |= MC13783_LED_C1_TC1HALF_BIT;
@@ -196,7 +196,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
if (pdata->flags & MC13783_LED_SLEWLIMTC)
reg |= MC13783_LED_Cx_SLEWLIM_BIT;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg);
if (ret)
goto out;
@@ -206,7 +206,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
if (pdata->flags & MC13783_LED_SLEWLIMBL)
reg |= MC13783_LED_Cx_SLEWLIM_BIT;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg);
if (ret)
goto out;
@@ -216,7 +216,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
if (pdata->flags & MC13783_LED_TRIODE_TC1)
reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg);
if (ret)
goto out;
@@ -226,7 +226,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
if (pdata->flags & MC13783_LED_TRIODE_TC2)
reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg);
if (ret)
goto out;
@@ -236,7 +236,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
if (pdata->flags & MC13783_LED_TRIODE_TC3)
reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, reg);
if (ret)
goto out;
@@ -255,17 +255,17 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
reg |= (pdata->abref & MC13783_LED_C0_ABREF_MASK) <<
MC13783_LED_C0_ABREF;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg);
out:
- mc13783_unlock(dev);
+ mc13xxx_unlock(dev);
return ret;
}
static int __devinit mc13783_led_probe(struct platform_device *pdev)
{
- struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct mc13783_led_platform_data *led_cur;
+ struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct mc13xxx_led_platform_data *led_cur;
struct mc13783_led *led, *led_dat;
int ret, i;
int init_led = 0;
@@ -351,9 +351,9 @@ err_free:
static int __devexit mc13783_led_remove(struct platform_device *pdev)
{
- struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct mc13783_led *led = platform_get_drvdata(pdev);
- struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent);
+ struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent);
int i;
for (i = 0; i < pdata->num_leds; i++) {
@@ -361,16 +361,16 @@ static int __devexit mc13783_led_remove(struct platform_device *pdev)
cancel_work_sync(&led[i].work);
}
- mc13783_lock(dev);
+ mc13xxx_lock(dev);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0);
- mc13783_unlock(dev);
+ mc13xxx_unlock(dev);
kfree(led);
return 0;
diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c
index 93987a12da49..f117f7326c5b 100644
--- a/drivers/leds/leds-net48xx.c
+++ b/drivers/leds/leds-net48xx.c
@@ -18,6 +18,7 @@
#include <asm/io.h>
#include <linux/nsc_gpio.h>
#include <linux/scx200_gpio.h>
+#include <linux/module.h>
#define DRVNAME "net48xx-led"
#define NET48XX_ERROR_LED_GPIO 20
diff --git a/drivers/leds/leds-net5501.c b/drivers/leds/leds-net5501.c
index 7e764b8365e6..0555d4709a7c 100644
--- a/drivers/leds/leds-net5501.c
+++ b/drivers/leds/leds-net5501.c
@@ -16,6 +16,7 @@
#include <linux/leds.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
+#include <linux/module.h>
#include <asm/geode.h>
diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c
index f77d48d0b3e4..37b7d0cfe586 100644
--- a/drivers/leds/leds-ns2.c
+++ b/drivers/leds/leds-ns2.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/leds.h>
+#include <linux/module.h>
#include <mach/leds-ns2.h>
/*
diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c
new file mode 100644
index 000000000000..3ee540eb127e
--- /dev/null
+++ b/drivers/leds/leds-renesas-tpu.c
@@ -0,0 +1,357 @@
+/*
+ * LED control using Renesas TPU
+ *
+ * Copyright (C) 2011 Magnus Damm
+ *
+ * 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
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/printk.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/leds.h>
+#include <linux/platform_data/leds-renesas-tpu.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+
+enum r_tpu_pin { R_TPU_PIN_UNUSED, R_TPU_PIN_GPIO, R_TPU_PIN_GPIO_FN };
+enum r_tpu_timer { R_TPU_TIMER_UNUSED, R_TPU_TIMER_ON };
+
+struct r_tpu_priv {
+ struct led_classdev ldev;
+ void __iomem *mapbase;
+ struct clk *clk;
+ struct platform_device *pdev;
+ enum r_tpu_pin pin_state;
+ enum r_tpu_timer timer_state;
+ unsigned long min_rate;
+ unsigned int refresh_rate;
+ struct work_struct work;
+ enum led_brightness new_brightness;
+};
+
+static DEFINE_SPINLOCK(r_tpu_lock);
+
+#define TSTR -1 /* Timer start register (shared register) */
+#define TCR 0 /* Timer control register (+0x00) */
+#define TMDR 1 /* Timer mode register (+0x04) */
+#define TIOR 2 /* Timer I/O control register (+0x08) */
+#define TIER 3 /* Timer interrupt enable register (+0x0c) */
+#define TSR 4 /* Timer status register (+0x10) */
+#define TCNT 5 /* Timer counter (+0x14) */
+#define TGRA 6 /* Timer general register A (+0x18) */
+#define TGRB 7 /* Timer general register B (+0x1c) */
+#define TGRC 8 /* Timer general register C (+0x20) */
+#define TGRD 9 /* Timer general register D (+0x24) */
+
+static inline unsigned short r_tpu_read(struct r_tpu_priv *p, int reg_nr)
+{
+ struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
+ void __iomem *base = p->mapbase;
+ unsigned long offs = reg_nr << 2;
+
+ if (reg_nr == TSTR)
+ return ioread16(base - cfg->channel_offset);
+
+ return ioread16(base + offs);
+}
+
+static inline void r_tpu_write(struct r_tpu_priv *p, int reg_nr,
+ unsigned short value)
+{
+ struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
+ void __iomem *base = p->mapbase;
+ unsigned long offs = reg_nr << 2;
+
+ if (reg_nr == TSTR) {
+ iowrite16(value, base - cfg->channel_offset);
+ return;
+ }
+
+ iowrite16(value, base + offs);
+}
+
+static void r_tpu_start_stop_ch(struct r_tpu_priv *p, int start)
+{
+ struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
+ unsigned long flags, value;
+
+ /* start stop register shared by multiple timer channels */
+ spin_lock_irqsave(&r_tpu_lock, flags);
+ value = r_tpu_read(p, TSTR);
+
+ if (start)
+ value |= 1 << cfg->timer_bit;
+ else
+ value &= ~(1 << cfg->timer_bit);
+
+ r_tpu_write(p, TSTR, value);
+ spin_unlock_irqrestore(&r_tpu_lock, flags);
+}
+
+static int r_tpu_enable(struct r_tpu_priv *p, enum led_brightness brightness)
+{
+ struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
+ int prescaler[] = { 1, 4, 16, 64 };
+ int k, ret;
+ unsigned long rate, tmp;
+
+ if (p->timer_state == R_TPU_TIMER_ON)
+ return 0;
+
+ /* wake up device and enable clock */
+ pm_runtime_get_sync(&p->pdev->dev);
+ ret = clk_enable(p->clk);
+ if (ret) {
+ dev_err(&p->pdev->dev, "cannot enable clock\n");
+ return ret;
+ }
+
+ /* make sure channel is disabled */
+ r_tpu_start_stop_ch(p, 0);
+
+ /* get clock rate after enabling it */
+ rate = clk_get_rate(p->clk);
+
+ /* pick the lowest acceptable rate */
+ for (k = 0; k < ARRAY_SIZE(prescaler); k++)
+ if ((rate / prescaler[k]) < p->min_rate)
+ break;
+
+ if (!k) {
+ dev_err(&p->pdev->dev, "clock rate mismatch\n");
+ goto err0;
+ }
+ dev_dbg(&p->pdev->dev, "rate = %lu, prescaler %u\n",
+ rate, prescaler[k - 1]);
+
+ /* clear TCNT on TGRB match, count on rising edge, set prescaler */
+ r_tpu_write(p, TCR, 0x0040 | (k - 1));
+
+ /* output 0 until TGRA, output 1 until TGRB */
+ r_tpu_write(p, TIOR, 0x0002);
+
+ rate /= prescaler[k - 1] * p->refresh_rate;
+ r_tpu_write(p, TGRB, rate);
+ dev_dbg(&p->pdev->dev, "TRGB = 0x%04lx\n", rate);
+
+ tmp = (cfg->max_brightness - brightness) * rate;
+ r_tpu_write(p, TGRA, tmp / cfg->max_brightness);
+ dev_dbg(&p->pdev->dev, "TRGA = 0x%04lx\n", tmp / cfg->max_brightness);
+
+ /* PWM mode */
+ r_tpu_write(p, TMDR, 0x0002);
+
+ /* enable channel */
+ r_tpu_start_stop_ch(p, 1);
+
+ p->timer_state = R_TPU_TIMER_ON;
+ return 0;
+ err0:
+ clk_disable(p->clk);
+ pm_runtime_put_sync(&p->pdev->dev);
+ return -ENOTSUPP;
+}
+
+static void r_tpu_disable(struct r_tpu_priv *p)
+{
+ if (p->timer_state == R_TPU_TIMER_UNUSED)
+ return;
+
+ /* disable channel */
+ r_tpu_start_stop_ch(p, 0);
+
+ /* stop clock and mark device as idle */
+ clk_disable(p->clk);
+ pm_runtime_put_sync(&p->pdev->dev);
+
+ p->timer_state = R_TPU_TIMER_UNUSED;
+}
+
+static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state,
+ enum led_brightness brightness)
+{
+ struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
+
+ if (p->pin_state == new_state) {
+ if (p->pin_state == R_TPU_PIN_GPIO)
+ gpio_set_value(cfg->pin_gpio, brightness);
+ return;
+ }
+
+ if (p->pin_state == R_TPU_PIN_GPIO)
+ gpio_free(cfg->pin_gpio);
+
+ if (p->pin_state == R_TPU_PIN_GPIO_FN)
+ gpio_free(cfg->pin_gpio_fn);
+
+ if (new_state == R_TPU_PIN_GPIO) {
+ gpio_request(cfg->pin_gpio, cfg->name);
+ gpio_direction_output(cfg->pin_gpio, !!brightness);
+ }
+ if (new_state == R_TPU_PIN_GPIO_FN)
+ gpio_request(cfg->pin_gpio_fn, cfg->name);
+
+ p->pin_state = new_state;
+}
+
+static void r_tpu_work(struct work_struct *work)
+{
+ struct r_tpu_priv *p = container_of(work, struct r_tpu_priv, work);
+ enum led_brightness brightness = p->new_brightness;
+
+ r_tpu_disable(p);
+
+ /* off and maximum are handled as GPIO pins, in between PWM */
+ if ((brightness == 0) || (brightness == p->ldev.max_brightness))
+ r_tpu_set_pin(p, R_TPU_PIN_GPIO, brightness);
+ else {
+ r_tpu_set_pin(p, R_TPU_PIN_GPIO_FN, 0);
+ r_tpu_enable(p, brightness);
+ }
+}
+
+static void r_tpu_set_brightness(struct led_classdev *ldev,
+ enum led_brightness brightness)
+{
+ struct r_tpu_priv *p = container_of(ldev, struct r_tpu_priv, ldev);
+ p->new_brightness = brightness;
+ schedule_work(&p->work);
+}
+
+static int __devinit r_tpu_probe(struct platform_device *pdev)
+{
+ struct led_renesas_tpu_config *cfg = pdev->dev.platform_data;
+ struct r_tpu_priv *p;
+ struct resource *res;
+ int ret = -ENXIO;
+
+ if (!cfg) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ goto err0;
+ }
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
+ goto err1;
+ }
+
+ /* map memory, let mapbase point to our channel */
+ p->mapbase = ioremap_nocache(res->start, resource_size(res));
+ if (p->mapbase == NULL) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ goto err1;
+ }
+
+ /* get hold of clock */
+ p->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(p->clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+ ret = PTR_ERR(p->clk);
+ goto err2;
+ }
+
+ p->pdev = pdev;
+ p->pin_state = R_TPU_PIN_UNUSED;
+ p->timer_state = R_TPU_TIMER_UNUSED;
+ p->refresh_rate = cfg->refresh_rate ? cfg->refresh_rate : 100;
+ r_tpu_set_pin(p, R_TPU_PIN_GPIO, LED_OFF);
+ platform_set_drvdata(pdev, p);
+
+ INIT_WORK(&p->work, r_tpu_work);
+
+ p->ldev.name = cfg->name;
+ p->ldev.brightness = LED_OFF;
+ p->ldev.max_brightness = cfg->max_brightness;
+ p->ldev.brightness_set = r_tpu_set_brightness;
+ p->ldev.flags |= LED_CORE_SUSPENDRESUME;
+ ret = led_classdev_register(&pdev->dev, &p->ldev);
+ if (ret < 0)
+ goto err3;
+
+ /* max_brightness may be updated by the LED core code */
+ p->min_rate = p->ldev.max_brightness * p->refresh_rate;
+
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+
+ err3:
+ r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
+ clk_put(p->clk);
+ err2:
+ iounmap(p->mapbase);
+ err1:
+ kfree(p);
+ err0:
+ return ret;
+}
+
+static int __devexit r_tpu_remove(struct platform_device *pdev)
+{
+ struct r_tpu_priv *p = platform_get_drvdata(pdev);
+
+ r_tpu_set_brightness(&p->ldev, LED_OFF);
+ led_classdev_unregister(&p->ldev);
+ cancel_work_sync(&p->work);
+ r_tpu_disable(p);
+ r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
+
+ pm_runtime_disable(&pdev->dev);
+ clk_put(p->clk);
+
+ iounmap(p->mapbase);
+ kfree(p);
+ return 0;
+}
+
+static struct platform_driver r_tpu_device_driver = {
+ .probe = r_tpu_probe,
+ .remove = __devexit_p(r_tpu_remove),
+ .driver = {
+ .name = "leds-renesas-tpu",
+ }
+};
+
+static int __init r_tpu_init(void)
+{
+ return platform_driver_register(&r_tpu_device_driver);
+}
+
+static void __exit r_tpu_exit(void)
+{
+ platform_driver_unregister(&r_tpu_device_driver);
+}
+
+module_init(r_tpu_init);
+module_exit(r_tpu_exit);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("Renesas TPU LED Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c
index a77771dc2e95..29f8b0f0e2c6 100644
--- a/drivers/leds/leds-s3c24xx.c
+++ b/drivers/leds/leds-s3c24xx.c
@@ -17,6 +17,7 @@
#include <linux/leds.h>
#include <linux/gpio.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c
index ab6d18f5c39f..1757396b20b3 100644
--- a/drivers/leds/leds-sunfire.c
+++ b/drivers/leds/leds-sunfire.c
@@ -127,17 +127,19 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
struct led_type *types)
{
struct sunfire_drvdata *p;
- int i, err = -EINVAL;
+ int i, err;
if (pdev->num_resources != 1) {
printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
pdev->num_resources);
+ err = -EINVAL;
goto out;
}
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p) {
printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
+ err = -ENOMEM;
goto out;
}
@@ -160,14 +162,14 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
dev_set_drvdata(&pdev->dev, p);
- err = 0;
-out:
- return err;
+ return 0;
out_unregister_led_cdevs:
for (i--; i >= 0; i--)
led_classdev_unregister(&p->leds[i].led_cdev);
- goto out;
+ kfree(p);
+out:
+ return err;
}
static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)
diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c
index ef5c24140a44..b1eb34c3e81f 100644
--- a/drivers/leds/leds-wm831x-status.c
+++ b/drivers/leds/leds-wm831x-status.c
@@ -18,6 +18,7 @@
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/status.h>
+#include <linux/module.h>
struct wm831x_status {
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
index f14edd82cb00..4a1276578352 100644
--- a/drivers/leds/leds-wm8350.c
+++ b/drivers/leds/leds-wm8350.c
@@ -17,6 +17,7 @@
#include <linux/mfd/wm8350/pmic.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/module.h>
/* Microamps */
static const int isink_cur[] = {
diff --git a/drivers/leds/leds-wrap.c b/drivers/leds/leds-wrap.c
index 2982c86ac4cf..6e21e654bb02 100644
--- a/drivers/leds/leds-wrap.c
+++ b/drivers/leds/leds-wrap.c
@@ -17,6 +17,7 @@
#include <linux/err.h>
#include <asm/io.h>
#include <linux/scx200_gpio.h>
+#include <linux/module.h>
#define DRVNAME "wrap-led"
#define WRAP_POWER_LED_GPIO 2
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c
index d87c9d02f786..328c64c0841c 100644
--- a/drivers/leds/ledtrig-timer.c
+++ b/drivers/leds/ledtrig-timer.c
@@ -41,6 +41,7 @@ static ssize_t led_delay_on_store(struct device *dev,
if (count == size) {
led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);
+ led_cdev->blink_delay_on = state;
ret = count;
}
@@ -69,6 +70,7 @@ static ssize_t led_delay_off_store(struct device *dev,
if (count == size) {
led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);
+ led_cdev->blink_delay_off = state;
ret = count;
}
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c
index efa202499e37..b5fdcb78a75b 100644
--- a/drivers/lguest/core.c
+++ b/drivers/lguest/core.c
@@ -117,7 +117,7 @@ static __init int map_switcher(void)
/*
* Now the Switcher is mapped at the right address, we can't fail!
- * Copy in the compiled-in Switcher code (from <arch>_switcher.S).
+ * Copy in the compiled-in Switcher code (from x86/switcher_32.S).
*/
memcpy(switcher_vma->addr, start_switcher_text,
end_switcher_text - start_switcher_text);
@@ -232,6 +232,13 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
}
}
+ /*
+ * All long-lived kernel loops need to check with this horrible
+ * thing called the freezer. If the Host is trying to suspend,
+ * it stops us.
+ */
+ try_to_freeze();
+
/* Check for signals */
if (signal_pending(current))
return -ERESTARTSYS;
@@ -246,13 +253,6 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
try_deliver_interrupt(cpu, irq, more);
/*
- * All long-lived kernel loops need to check with this horrible
- * thing called the freezer. If the Host is trying to suspend,
- * it stops us.
- */
- try_to_freeze();
-
- /*
* Just make absolutely sure the Guest is still alive. One of
* those hypercalls could have been fatal, for example.
*/
@@ -313,7 +313,7 @@ static int __init init(void)
int err;
/* Lguest can't run under Xen, VMI or itself. It does Tricky Stuff. */
- if (paravirt_enabled()) {
+ if (get_kernel_rpl() != 0) {
printk("lguest is afraid of being a guest\n");
return -EPERM;
}
diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c
index daaf86631647..28433a155d67 100644
--- a/drivers/lguest/interrupts_and_traps.c
+++ b/drivers/lguest/interrupts_and_traps.c
@@ -375,11 +375,9 @@ static bool direct_trap(unsigned int num)
/*
* The Host needs to see page faults (for shadow paging and to save the
* fault address), general protection faults (in/out emulation) and
- * device not available (TS handling), invalid opcode fault (kvm hcall),
- * and of course, the hypercall trap.
+ * device not available (TS handling) and of course, the hypercall trap.
*/
- return num != 14 && num != 13 && num != 7 &&
- num != 6 && num != LGUEST_TRAP_ENTRY;
+ return num != 14 && num != 13 && num != 7 && num != LGUEST_TRAP_ENTRY;
}
/*:*/
@@ -429,8 +427,8 @@ void pin_stack_pages(struct lg_cpu *cpu)
/*
* Direct traps also mean that we need to know whenever the Guest wants to use
- * a different kernel stack, so we can change the IDT entries to use that
- * stack. The IDT entries expect a virtual address, so unlike most addresses
+ * a different kernel stack, so we can change the guest TSS to use that
+ * stack. The TSS entries expect a virtual address, so unlike most addresses
* the Guest gives us, the "esp" (stack pointer) value here is virtual, not
* physical.
*
diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h
index 9136411fadd5..295df06e6590 100644
--- a/drivers/lguest/lg.h
+++ b/drivers/lguest/lg.h
@@ -59,6 +59,8 @@ struct lg_cpu {
struct lguest_pages *last_pages;
+ /* Initialization mode: linear map everything. */
+ bool linear_pages;
int cpu_pgd; /* Which pgd this cpu is currently using */
/* If a hypercall was asked for, this points to the arguments. */
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c
index 69c84a1d88ea..0dc30ffde5ad 100644
--- a/drivers/lguest/lguest_device.c
+++ b/drivers/lguest/lguest_device.c
@@ -15,6 +15,7 @@
#include <linux/interrupt.h>
#include <linux/virtio_ring.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/paravirt.h>
@@ -109,6 +110,17 @@ static u32 lg_get_features(struct virtio_device *vdev)
}
/*
+ * To notify on reset or feature finalization, we (ab)use the NOTIFY
+ * hypercall, with the descriptor address of the device.
+ */
+static void status_notify(struct virtio_device *vdev)
+{
+ unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;
+
+ hcall(LHCALL_NOTIFY, (max_pfn << PAGE_SHIFT) + offset, 0, 0, 0);
+}
+
+/*
* The virtio core takes the features the Host offers, and copies the ones
* supported by the driver into the vdev->features array. Once that's all
* sorted out, this routine is called so we can tell the Host which features we
@@ -135,6 +147,9 @@ static void lg_finalize_features(struct virtio_device *vdev)
if (test_bit(i, vdev->features))
out_features[i / 8] |= (1 << (i % 8));
}
+
+ /* Tell Host we've finished with this device's feature negotiation */
+ status_notify(vdev);
}
/* Once they've found a field, getting a copy of it is easy. */
@@ -168,28 +183,21 @@ static u8 lg_get_status(struct virtio_device *vdev)
return to_lgdev(vdev)->desc->status;
}
-/*
- * To notify on status updates, we (ab)use the NOTIFY hypercall, with the
- * descriptor address of the device. A zero status means "reset".
- */
-static void set_status(struct virtio_device *vdev, u8 status)
-{
- unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;
-
- /* We set the status. */
- to_lgdev(vdev)->desc->status = status;
- hcall(LHCALL_NOTIFY, (max_pfn << PAGE_SHIFT) + offset, 0, 0, 0);
-}
-
static void lg_set_status(struct virtio_device *vdev, u8 status)
{
BUG_ON(!status);
- set_status(vdev, status);
+ to_lgdev(vdev)->desc->status = status;
+
+ /* Tell Host immediately if we failed. */
+ if (status & VIRTIO_CONFIG_S_FAILED)
+ status_notify(vdev);
}
static void lg_reset(struct virtio_device *vdev)
{
- set_status(vdev, 0);
+ /* 0 status means "reset" */
+ to_lgdev(vdev)->desc->status = 0;
+ status_notify(vdev);
}
/*
diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c
index 948c547b8e9e..ff4a0bc9904d 100644
--- a/drivers/lguest/lguest_user.c
+++ b/drivers/lguest/lguest_user.c
@@ -1,8 +1,10 @@
-/*P:200 This contains all the /dev/lguest code, whereby the userspace launcher
- * controls and communicates with the Guest. For example, the first write will
- * tell us the Guest's memory layout and entry point. A read will run the
- * Guest until something happens, such as a signal or the Guest doing a NOTIFY
- * out to the Launcher.
+/*P:200 This contains all the /dev/lguest code, whereby the userspace
+ * launcher controls and communicates with the Guest. For example,
+ * the first write will tell us the Guest's memory layout and entry
+ * point. A read will run the Guest until something happens, such as
+ * a signal or the Guest doing a NOTIFY out to the Launcher. There is
+ * also a way for the Launcher to attach eventfds to particular NOTIFY
+ * values instead of returning from the read() call.
:*/
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
@@ -11,6 +13,7 @@
#include <linux/eventfd.h>
#include <linux/file.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "lg.h"
/*L:056
@@ -357,8 +360,8 @@ static int initialize(struct file *file, const unsigned long __user *input)
goto free_eventfds;
/*
- * Initialize the Guest's shadow page tables, using the toplevel
- * address the Launcher gave us. This allocates memory, so can fail.
+ * Initialize the Guest's shadow page tables. This allocates
+ * memory, so can fail.
*/
err = init_guest_pagetable(lg);
if (err)
@@ -516,6 +519,7 @@ static const struct file_operations lguest_fops = {
.read = read,
.llseek = default_llseek,
};
+/*:*/
/*
* This is a textbook example of a "misc" character device. Populate a "struct
diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c
index d21578ee95de..3b62be160a6e 100644
--- a/drivers/lguest/page_tables.c
+++ b/drivers/lguest/page_tables.c
@@ -17,7 +17,6 @@
#include <linux/percpu.h>
#include <asm/tlbflush.h>
#include <asm/uaccess.h>
-#include <asm/bootparam.h>
#include "lg.h"
/*M:008
@@ -156,7 +155,7 @@ static pte_t *spte_addr(struct lg_cpu *cpu, pgd_t spgd, unsigned long vaddr)
}
/*
- * These functions are just like the above two, except they access the Guest
+ * These functions are just like the above, except they access the Guest
* page tables. Hence they return a Guest address.
*/
static unsigned long gpgd_addr(struct lg_cpu *cpu, unsigned long vaddr)
@@ -196,7 +195,7 @@ static unsigned long gpte_addr(struct lg_cpu *cpu,
#endif
/*:*/
-/*M:014
+/*M:007
* get_pfn is slow: we could probably try to grab batches of pages here as
* an optimization (ie. pre-faulting).
:*/
@@ -325,10 +324,15 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
#endif
/* First step: get the top-level Guest page table entry. */
- gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t);
- /* Toplevel not present? We can't map it in. */
- if (!(pgd_flags(gpgd) & _PAGE_PRESENT))
- return false;
+ if (unlikely(cpu->linear_pages)) {
+ /* Faking up a linear mapping. */
+ gpgd = __pgd(CHECK_GPGD_MASK);
+ } else {
+ gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t);
+ /* Toplevel not present? We can't map it in. */
+ if (!(pgd_flags(gpgd) & _PAGE_PRESENT))
+ return false;
+ }
/* Now look at the matching shadow entry. */
spgd = spgd_addr(cpu, cpu->cpu_pgd, vaddr);
@@ -353,10 +357,15 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
}
#ifdef CONFIG_X86_PAE
- gpmd = lgread(cpu, gpmd_addr(gpgd, vaddr), pmd_t);
- /* Middle level not present? We can't map it in. */
- if (!(pmd_flags(gpmd) & _PAGE_PRESENT))
- return false;
+ if (unlikely(cpu->linear_pages)) {
+ /* Faking up a linear mapping. */
+ gpmd = __pmd(_PAGE_TABLE);
+ } else {
+ gpmd = lgread(cpu, gpmd_addr(gpgd, vaddr), pmd_t);
+ /* Middle level not present? We can't map it in. */
+ if (!(pmd_flags(gpmd) & _PAGE_PRESENT))
+ return false;
+ }
/* Now look at the matching shadow entry. */
spmd = spmd_addr(cpu, *spgd, vaddr);
@@ -397,8 +406,13 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
gpte_ptr = gpte_addr(cpu, gpgd, vaddr);
#endif
- /* Read the actual PTE value. */
- gpte = lgread(cpu, gpte_ptr, pte_t);
+ if (unlikely(cpu->linear_pages)) {
+ /* Linear? Make up a PTE which points to same page. */
+ gpte = __pte((vaddr & PAGE_MASK) | _PAGE_RW | _PAGE_PRESENT);
+ } else {
+ /* Read the actual PTE value. */
+ gpte = lgread(cpu, gpte_ptr, pte_t);
+ }
/* If this page isn't in the Guest page tables, we can't page it in. */
if (!(pte_flags(gpte) & _PAGE_PRESENT))
@@ -454,7 +468,8 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
* Finally, we write the Guest PTE entry back: we've set the
* _PAGE_ACCESSED and maybe the _PAGE_DIRTY flags.
*/
- lgwrite(cpu, gpte_ptr, pte_t, gpte);
+ if (likely(!cpu->linear_pages))
+ lgwrite(cpu, gpte_ptr, pte_t, gpte);
/*
* The fault is fixed, the page table is populated, the mapping
@@ -612,6 +627,11 @@ unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr)
#ifdef CONFIG_X86_PAE
pmd_t gpmd;
#endif
+
+ /* Still not set up? Just map 1:1. */
+ if (unlikely(cpu->linear_pages))
+ return vaddr;
+
/* First step: get the top-level Guest page table entry. */
gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t);
/* Toplevel not present? We can't map it in. */
@@ -708,32 +728,6 @@ static unsigned int new_pgdir(struct lg_cpu *cpu,
return next;
}
-/*H:430
- * (iv) Switching page tables
- *
- * Now we've seen all the page table setting and manipulation, let's see
- * what happens when the Guest changes page tables (ie. changes the top-level
- * pgdir). This occurs on almost every context switch.
- */
-void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable)
-{
- int newpgdir, repin = 0;
-
- /* Look to see if we have this one already. */
- newpgdir = find_pgdir(cpu->lg, pgtable);
- /*
- * If not, we allocate or mug an existing one: if it's a fresh one,
- * repin gets set to 1.
- */
- if (newpgdir == ARRAY_SIZE(cpu->lg->pgdirs))
- newpgdir = new_pgdir(cpu, pgtable, &repin);
- /* Change the current pgd index to the new one. */
- cpu->cpu_pgd = newpgdir;
- /* If it was completely blank, we map in the Guest kernel stack */
- if (repin)
- pin_stack_pages(cpu);
-}
-
/*H:470
* Finally, a routine which throws away everything: all PGD entries in all
* the shadow page tables, including the Guest's kernel mappings. This is used
@@ -780,6 +774,44 @@ void guest_pagetable_clear_all(struct lg_cpu *cpu)
/* We need the Guest kernel stack mapped again. */
pin_stack_pages(cpu);
}
+
+/*H:430
+ * (iv) Switching page tables
+ *
+ * Now we've seen all the page table setting and manipulation, let's see
+ * what happens when the Guest changes page tables (ie. changes the top-level
+ * pgdir). This occurs on almost every context switch.
+ */
+void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable)
+{
+ int newpgdir, repin = 0;
+
+ /*
+ * The very first time they call this, we're actually running without
+ * any page tables; we've been making it up. Throw them away now.
+ */
+ if (unlikely(cpu->linear_pages)) {
+ release_all_pagetables(cpu->lg);
+ cpu->linear_pages = false;
+ /* Force allocation of a new pgdir. */
+ newpgdir = ARRAY_SIZE(cpu->lg->pgdirs);
+ } else {
+ /* Look to see if we have this one already. */
+ newpgdir = find_pgdir(cpu->lg, pgtable);
+ }
+
+ /*
+ * If not, we allocate or mug an existing one: if it's a fresh one,
+ * repin gets set to 1.
+ */
+ if (newpgdir == ARRAY_SIZE(cpu->lg->pgdirs))
+ newpgdir = new_pgdir(cpu, pgtable, &repin);
+ /* Change the current pgd index to the new one. */
+ cpu->cpu_pgd = newpgdir;
+ /* If it was completely blank, we map in the Guest kernel stack */
+ if (repin)
+ pin_stack_pages(cpu);
+}
/*:*/
/*M:009
@@ -919,168 +951,26 @@ void guest_set_pmd(struct lguest *lg, unsigned long pmdp, u32 idx)
}
#endif
-/*H:505
- * To get through boot, we construct simple identity page mappings (which
- * set virtual == physical) and linear mappings which will get the Guest far
- * enough into the boot to create its own. The linear mapping means we
- * simplify the Guest boot, but it makes assumptions about their PAGE_OFFSET,
- * as you'll see.
- *
- * We lay them out of the way, just below the initrd (which is why we need to
- * know its size here).
- */
-static unsigned long setup_pagetables(struct lguest *lg,
- unsigned long mem,
- unsigned long initrd_size)
-{
- pgd_t __user *pgdir;
- pte_t __user *linear;
- unsigned long mem_base = (unsigned long)lg->mem_base;
- unsigned int mapped_pages, i, linear_pages;
-#ifdef CONFIG_X86_PAE
- pmd_t __user *pmds;
- unsigned int j;
- pgd_t pgd;
- pmd_t pmd;
-#else
- unsigned int phys_linear;
-#endif
-
- /*
- * We have mapped_pages frames to map, so we need linear_pages page
- * tables to map them.
- */
- mapped_pages = mem / PAGE_SIZE;
- linear_pages = (mapped_pages + PTRS_PER_PTE - 1) / PTRS_PER_PTE;
-
- /* We put the toplevel page directory page at the top of memory. */
- pgdir = (pgd_t *)(mem + mem_base - initrd_size - PAGE_SIZE);
-
- /* Now we use the next linear_pages pages as pte pages */
- linear = (void *)pgdir - linear_pages * PAGE_SIZE;
-
-#ifdef CONFIG_X86_PAE
- /*
- * And the single mid page goes below that. We only use one, but
- * that's enough to map 1G, which definitely gets us through boot.
- */
- pmds = (void *)linear - PAGE_SIZE;
-#endif
- /*
- * Linear mapping is easy: put every page's address into the
- * mapping in order.
- */
- for (i = 0; i < mapped_pages; i++) {
- pte_t pte;
- pte = pfn_pte(i, __pgprot(_PAGE_PRESENT|_PAGE_RW|_PAGE_USER));
- if (copy_to_user(&linear[i], &pte, sizeof(pte)) != 0)
- return -EFAULT;
- }
-
-#ifdef CONFIG_X86_PAE
- /*
- * Make the Guest PMD entries point to the corresponding place in the
- * linear mapping (up to one page worth of PMD).
- */
- for (i = j = 0; i < mapped_pages && j < PTRS_PER_PMD;
- i += PTRS_PER_PTE, j++) {
- pmd = pfn_pmd(((unsigned long)&linear[i] - mem_base)/PAGE_SIZE,
- __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER));
-
- if (copy_to_user(&pmds[j], &pmd, sizeof(pmd)) != 0)
- return -EFAULT;
- }
-
- /* One PGD entry, pointing to that PMD page. */
- pgd = __pgd(((unsigned long)pmds - mem_base) | _PAGE_PRESENT);
- /* Copy it in as the first PGD entry (ie. addresses 0-1G). */
- if (copy_to_user(&pgdir[0], &pgd, sizeof(pgd)) != 0)
- return -EFAULT;
- /*
- * And the other PGD entry to make the linear mapping at PAGE_OFFSET
- */
- if (copy_to_user(&pgdir[KERNEL_PGD_BOUNDARY], &pgd, sizeof(pgd)))
- return -EFAULT;
-#else
- /*
- * The top level points to the linear page table pages above.
- * We setup the identity and linear mappings here.
- */
- phys_linear = (unsigned long)linear - mem_base;
- for (i = 0; i < mapped_pages; i += PTRS_PER_PTE) {
- pgd_t pgd;
- /*
- * Create a PGD entry which points to the right part of the
- * linear PTE pages.
- */
- pgd = __pgd((phys_linear + i * sizeof(pte_t)) |
- (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER));
-
- /*
- * Copy it into the PGD page at 0 and PAGE_OFFSET.
- */
- if (copy_to_user(&pgdir[i / PTRS_PER_PTE], &pgd, sizeof(pgd))
- || copy_to_user(&pgdir[pgd_index(PAGE_OFFSET)
- + i / PTRS_PER_PTE],
- &pgd, sizeof(pgd)))
- return -EFAULT;
- }
-#endif
-
- /*
- * We return the top level (guest-physical) address: we remember where
- * this is to write it into lguest_data when the Guest initializes.
- */
- return (unsigned long)pgdir - mem_base;
-}
-
/*H:500
* (vii) Setting up the page tables initially.
*
- * When a Guest is first created, the Launcher tells us where the toplevel of
- * its first page table is. We set some things up here:
+ * When a Guest is first created, set initialize a shadow page table which
+ * we will populate on future faults. The Guest doesn't have any actual
+ * pagetables yet, so we set linear_pages to tell demand_page() to fake it
+ * for the moment.
*/
int init_guest_pagetable(struct lguest *lg)
{
- u64 mem;
- u32 initrd_size;
- struct boot_params __user *boot = (struct boot_params *)lg->mem_base;
-#ifdef CONFIG_X86_PAE
- pgd_t *pgd;
- pmd_t *pmd_table;
-#endif
- /*
- * Get the Guest memory size and the ramdisk size from the boot header
- * located at lg->mem_base (Guest address 0).
- */
- if (copy_from_user(&mem, &boot->e820_map[0].size, sizeof(mem))
- || get_user(initrd_size, &boot->hdr.ramdisk_size))
- return -EFAULT;
+ struct lg_cpu *cpu = &lg->cpus[0];
+ int allocated = 0;
- /*
- * We start on the first shadow page table, and give it a blank PGD
- * page.
- */
- lg->pgdirs[0].gpgdir = setup_pagetables(lg, mem, initrd_size);
- if (IS_ERR_VALUE(lg->pgdirs[0].gpgdir))
- return lg->pgdirs[0].gpgdir;
- lg->pgdirs[0].pgdir = (pgd_t *)get_zeroed_page(GFP_KERNEL);
- if (!lg->pgdirs[0].pgdir)
+ /* lg (and lg->cpus[]) starts zeroed: this allocates a new pgdir */
+ cpu->cpu_pgd = new_pgdir(cpu, 0, &allocated);
+ if (!allocated)
return -ENOMEM;
-#ifdef CONFIG_X86_PAE
- /* For PAE, we also create the initial mid-level. */
- pgd = lg->pgdirs[0].pgdir;
- pmd_table = (pmd_t *) get_zeroed_page(GFP_KERNEL);
- if (!pmd_table)
- return -ENOMEM;
-
- set_pgd(pgd + SWITCHER_PGD_INDEX,
- __pgd(__pa(pmd_table) | _PAGE_PRESENT));
-#endif
-
- /* This is the current page table. */
- lg->cpus[0].cpu_pgd = 0;
+ /* We start with a linear mapping until the initialize. */
+ cpu->linear_pages = true;
return 0;
}
@@ -1095,10 +985,10 @@ void page_table_guest_data_init(struct lg_cpu *cpu)
* of virtual addresses used by the Switcher.
*/
|| put_user(RESERVE_MEM * 1024 * 1024,
- &cpu->lg->lguest_data->reserve_mem)
- || put_user(cpu->lg->pgdirs[0].gpgdir,
- &cpu->lg->lguest_data->pgdir))
+ &cpu->lg->lguest_data->reserve_mem)) {
kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data);
+ return;
+ }
/*
* In flush_user_mappings() we loop from 0 to
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c
index 9f1659c3d1f3..65af42f2d593 100644
--- a/drivers/lguest/x86/core.c
+++ b/drivers/lguest/x86/core.c
@@ -269,10 +269,10 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
static int emulate_insn(struct lg_cpu *cpu)
{
u8 insn;
- unsigned int insnlen = 0, in = 0, shift = 0;
+ unsigned int insnlen = 0, in = 0, small_operand = 0;
/*
* The eip contains the *virtual* address of the Guest's instruction:
- * guest_pa just subtracts the Guest's page_offset.
+ * walk the Guest's page tables to find the "physical" address.
*/
unsigned long physaddr = guest_pa(cpu, cpu->regs->eip);
@@ -300,11 +300,10 @@ static int emulate_insn(struct lg_cpu *cpu)
}
/*
- * 0x66 is an "operand prefix". It means it's using the upper 16 bits
- * of the eax register.
+ * 0x66 is an "operand prefix". It means a 16, not 32 bit in/out.
*/
if (insn == 0x66) {
- shift = 16;
+ small_operand = 1;
/* The instruction is 1 byte so far, read the next byte. */
insnlen = 1;
insn = lgread(cpu, physaddr + insnlen, u8);
@@ -340,11 +339,14 @@ static int emulate_insn(struct lg_cpu *cpu)
* traditionally means "there's nothing there".
*/
if (in) {
- /* Lower bit tells is whether it's a 16 or 32 bit access */
- if (insn & 0x1)
- cpu->regs->eax = 0xFFFFFFFF;
- else
- cpu->regs->eax |= (0xFFFF << shift);
+ /* Lower bit tells means it's a 32/16 bit access */
+ if (insn & 0x1) {
+ if (small_operand)
+ cpu->regs->eax |= 0xFFFF;
+ else
+ cpu->regs->eax = 0xFFFFFFFF;
+ } else
+ cpu->regs->eax |= 0xFF;
}
/* Finally, we've "done" the instruction, so move past it. */
cpu->regs->eip += insnlen;
@@ -352,69 +354,6 @@ static int emulate_insn(struct lg_cpu *cpu)
return 1;
}
-/*
- * Our hypercalls mechanism used to be based on direct software interrupts.
- * After Anthony's "Refactor hypercall infrastructure" kvm patch, we decided to
- * change over to using kvm hypercalls.
- *
- * KVM_HYPERCALL is actually a "vmcall" instruction, which generates an invalid
- * opcode fault (fault 6) on non-VT cpus, so the easiest solution seemed to be
- * an *emulation approach*: if the fault was really produced by an hypercall
- * (is_hypercall() does exactly this check), we can just call the corresponding
- * hypercall host implementation function.
- *
- * But these invalid opcode faults are notably slower than software interrupts.
- * So we implemented the *patching (or rewriting) approach*: every time we hit
- * the KVM_HYPERCALL opcode in Guest code, we patch it to the old "int 0x1f"
- * opcode, so next time the Guest calls this hypercall it will use the
- * faster trap mechanism.
- *
- * Matias even benchmarked it to convince you: this shows the average cycle
- * cost of a hypercall. For each alternative solution mentioned above we've
- * made 5 runs of the benchmark:
- *
- * 1) direct software interrupt: 2915, 2789, 2764, 2721, 2898
- * 2) emulation technique: 3410, 3681, 3466, 3392, 3780
- * 3) patching (rewrite) technique: 2977, 2975, 2891, 2637, 2884
- *
- * One two-line function is worth a 20% hypercall speed boost!
- */
-static void rewrite_hypercall(struct lg_cpu *cpu)
-{
- /*
- * This are the opcodes we use to patch the Guest. The opcode for "int
- * $0x1f" is "0xcd 0x1f" but vmcall instruction is 3 bytes long, so we
- * complete the sequence with a NOP (0x90).
- */
- u8 insn[3] = {0xcd, 0x1f, 0x90};
-
- __lgwrite(cpu, guest_pa(cpu, cpu->regs->eip), insn, sizeof(insn));
- /*
- * The above write might have caused a copy of that page to be made
- * (if it was read-only). We need to make sure the Guest has
- * up-to-date pagetables. As this doesn't happen often, we can just
- * drop them all.
- */
- guest_pagetable_clear_all(cpu);
-}
-
-static bool is_hypercall(struct lg_cpu *cpu)
-{
- u8 insn[3];
-
- /*
- * This must be the Guest kernel trying to do something.
- * The bottom two bits of the CS segment register are the privilege
- * level.
- */
- if ((cpu->regs->cs & 3) != GUEST_PL)
- return false;
-
- /* Is it a vmcall? */
- __lgread(cpu, insn, guest_pa(cpu, cpu->regs->eip), sizeof(insn));
- return insn[0] == 0x0f && insn[1] == 0x01 && insn[2] == 0xc1;
-}
-
/*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */
void lguest_arch_handle_trap(struct lg_cpu *cpu)
{
@@ -429,20 +368,6 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu)
if (emulate_insn(cpu))
return;
}
- /*
- * If KVM is active, the vmcall instruction triggers a General
- * Protection Fault. Normally it triggers an invalid opcode
- * fault (6):
- */
- case 6:
- /*
- * We need to check if ring == GUEST_PL and faulting
- * instruction == vmcall.
- */
- if (is_hypercall(cpu)) {
- rewrite_hypercall(cpu);
- return;
- }
break;
case 14: /* We've intercepted a Page Fault. */
/*
@@ -486,7 +411,7 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu)
* These values mean a real interrupt occurred, in which case
* the Host handler has already been run. We just do a
* friendly check if another process should now be run, then
- * return to run the Guest again
+ * return to run the Guest again.
*/
cond_resched();
return;
@@ -536,7 +461,7 @@ void __init lguest_arch_host_init(void)
int i;
/*
- * Most of the i386/switcher.S doesn't care that it's been moved; on
+ * Most of the x86/switcher_32.S doesn't care that it's been moved; on
* Intel, jumps are relative, and it doesn't access any references to
* external code or data.
*
@@ -664,7 +589,7 @@ void __init lguest_arch_host_init(void)
clear_cpu_cap(&boot_cpu_data, X86_FEATURE_PGE);
}
put_online_cpus();
-};
+}
/*:*/
void __exit lguest_arch_host_fini(void)
@@ -747,8 +672,6 @@ int lguest_arch_init_hypercalls(struct lg_cpu *cpu)
/*:*/
/*L:030
- * lguest_arch_setup_regs()
- *
* Most of the Guest's registers are left alone: we used get_zeroed_page() to
* allocate the structure, so they will be 0.
*/
diff --git a/drivers/macintosh/nvram.c b/drivers/macintosh/nvram.c
index a271c8218d82..f0e03e7937e3 100644
--- a/drivers/macintosh/nvram.c
+++ b/drivers/macintosh/nvram.c
@@ -21,12 +21,16 @@
static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
{
switch (origin) {
+ case 0:
+ break;
case 1:
offset += file->f_pos;
break;
case 2:
offset += NVRAM_SIZE;
break;
+ default:
+ offset = -1;
}
if (offset < 0)
return -EINVAL;
diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c
index 817f37a875c9..c9570fcf1cce 100644
--- a/drivers/macintosh/via-macii.c
+++ b/drivers/macintosh/via-macii.c
@@ -159,7 +159,7 @@ int macii_init(void)
err = macii_init_via();
if (err) goto out;
- err = request_irq(IRQ_MAC_ADB, macii_interrupt, IRQ_FLG_LOCK, "ADB",
+ err = request_irq(IRQ_MAC_ADB, macii_interrupt, 0, "ADB",
macii_interrupt);
if (err) goto out;
diff --git a/drivers/macintosh/via-maciisi.c b/drivers/macintosh/via-maciisi.c
index 9ab5b0c34f0d..34d02a91b29f 100644
--- a/drivers/macintosh/via-maciisi.c
+++ b/drivers/macintosh/via-maciisi.c
@@ -122,8 +122,8 @@ maciisi_init(void)
return err;
}
- if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, IRQ_FLG_LOCK | IRQ_FLG_FAST,
- "ADB", maciisi_interrupt)) {
+ if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, 0, "ADB",
+ maciisi_interrupt)) {
printk(KERN_ERR "maciisi_init: can't get irq %d\n", IRQ_MAC_ADB);
return -EAGAIN;
}
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 8420129fc5ee..faa4741df6d3 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -208,6 +208,16 @@ config DM_DEBUG
If unsure, say N.
+config DM_BUFIO
+ tristate
+ depends on BLK_DEV_DM && EXPERIMENTAL
+ ---help---
+ This interface allows you to do buffered I/O on a device and acts
+ as a cache, holding recently-read blocks in memory and performing
+ delayed writes.
+
+source "drivers/md/persistent-data/Kconfig"
+
config DM_CRYPT
tristate "Crypt target support"
depends on BLK_DEV_DM
@@ -233,6 +243,32 @@ config DM_SNAPSHOT
---help---
Allow volume managers to take writable snapshots of a device.
+config DM_THIN_PROVISIONING
+ tristate "Thin provisioning target (EXPERIMENTAL)"
+ depends on BLK_DEV_DM && EXPERIMENTAL
+ select DM_PERSISTENT_DATA
+ ---help---
+ Provides thin provisioning and snapshots that share a data store.
+
+config DM_DEBUG_BLOCK_STACK_TRACING
+ boolean "Keep stack trace of thin provisioning block lock holders"
+ depends on STACKTRACE_SUPPORT && DM_THIN_PROVISIONING
+ select STACKTRACE
+ ---help---
+ Enable this for messages that may help debug problems with the
+ block manager locking used by thin provisioning.
+
+ If unsure, say N.
+
+config DM_DEBUG_SPACE_MAPS
+ boolean "Extra validation for thin provisioning space maps"
+ depends on DM_THIN_PROVISIONING
+ ---help---
+ Enable this for messages that may help debug problems with the
+ space maps used by thin provisioning.
+
+ If unsure, say N.
+
config DM_MIRROR
tristate "Mirror target"
depends on BLK_DEV_DM
@@ -241,12 +277,13 @@ config DM_MIRROR
needed for live data migration tools such as 'pvmove'.
config DM_RAID
- tristate "RAID 4/5/6 target (EXPERIMENTAL)"
+ tristate "RAID 1/4/5/6 target (EXPERIMENTAL)"
depends on BLK_DEV_DM && EXPERIMENTAL
+ select MD_RAID1
select MD_RAID456
select BLK_DEV_MD
---help---
- A dm target that supports RAID4, RAID5 and RAID6 mappings
+ A dm target that supports RAID1, RAID4, RAID5 and RAID6 mappings
A RAID-5 set of N drives with a capacity of C MB per drive provides
the capacity of C * (N - 1) MB, and protects against a failure
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 448838b1f92a..046860c7a166 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -10,6 +10,7 @@ dm-snapshot-y += dm-snap.o dm-exception-store.o dm-snap-transient.o \
dm-mirror-y += dm-raid1.o
dm-log-userspace-y \
+= dm-log-userspace-base.o dm-log-userspace-transfer.o
+dm-thin-pool-y += dm-thin.o dm-thin-metadata.o
md-mod-y += md.o bitmap.o
raid456-y += raid5.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_MD_MULTIPATH) += multipath.o
obj-$(CONFIG_MD_FAULTY) += faulty.o
obj-$(CONFIG_BLK_DEV_MD) += md-mod.o
obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
+obj-$(CONFIG_DM_BUFIO) += dm-bufio.o
obj-$(CONFIG_DM_CRYPT) += dm-crypt.o
obj-$(CONFIG_DM_DELAY) += dm-delay.o
obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o
@@ -34,10 +36,12 @@ obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o
obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o
obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o
obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o
+obj-$(CONFIG_DM_PERSISTENT_DATA) += persistent-data/
obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o dm-region-hash.o
obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o
obj-$(CONFIG_DM_ZERO) += dm-zero.o
obj-$(CONFIG_DM_RAID) += dm-raid.o
+obj-$(CONFIG_DM_THIN_PROVISIONING) += dm-thin-pool.o
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 574b09afedd3..7878712721bf 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -29,36 +29,6 @@
#include "md.h"
#include "bitmap.h"
-#include <linux/dm-dirty-log.h>
-/* debug macros */
-
-#define DEBUG 0
-
-#if DEBUG
-/* these are for debugging purposes only! */
-
-/* define one and only one of these */
-#define INJECT_FAULTS_1 0 /* cause bitmap_alloc_page to fail always */
-#define INJECT_FAULTS_2 0 /* cause bitmap file to be kicked when first bit set*/
-#define INJECT_FAULTS_3 0 /* treat bitmap file as kicked at init time */
-#define INJECT_FAULTS_4 0 /* undef */
-#define INJECT_FAULTS_5 0 /* undef */
-#define INJECT_FAULTS_6 0
-
-/* if these are defined, the driver will fail! debug only */
-#define INJECT_FATAL_FAULT_1 0 /* fail kmalloc, causing bitmap_create to fail */
-#define INJECT_FATAL_FAULT_2 0 /* undef */
-#define INJECT_FATAL_FAULT_3 0 /* undef */
-#endif
-
-#ifndef PRINTK
-# if DEBUG > 0
-# define PRINTK(x...) printk(KERN_DEBUG x)
-# else
-# define PRINTK(x...)
-# endif
-#endif
-
static inline char *bmname(struct bitmap *bitmap)
{
return bitmap->mddev ? mdname(bitmap->mddev) : "mdX";
@@ -71,16 +41,12 @@ static unsigned char *bitmap_alloc_page(struct bitmap *bitmap)
{
unsigned char *page;
-#ifdef INJECT_FAULTS_1
- page = NULL;
-#else
page = kzalloc(PAGE_SIZE, GFP_NOIO);
-#endif
if (!page)
printk("%s: bitmap_alloc_page FAILED\n", bmname(bitmap));
else
- PRINTK("%s: bitmap_alloc_page: allocated page at %p\n",
- bmname(bitmap), page);
+ pr_debug("%s: bitmap_alloc_page: allocated page at %p\n",
+ bmname(bitmap), page);
return page;
}
@@ -89,7 +55,7 @@ static unsigned char *bitmap_alloc_page(struct bitmap *bitmap)
*/
static void bitmap_free_page(struct bitmap *bitmap, unsigned char *page)
{
- PRINTK("%s: bitmap_free_page: free page %p\n", bmname(bitmap), page);
+ pr_debug("%s: bitmap_free_page: free page %p\n", bmname(bitmap), page);
kfree(page);
}
@@ -134,8 +100,8 @@ __acquires(bitmap->lock)
spin_lock_irq(&bitmap->lock);
if (mappage == NULL) {
- PRINTK("%s: bitmap map page allocation failed, hijacking\n",
- bmname(bitmap));
+ pr_debug("%s: bitmap map page allocation failed, hijacking\n",
+ bmname(bitmap));
/* failed - set the hijacked flag so that we can use the
* pointer as a counter */
if (!bitmap->bp[page].map)
@@ -188,13 +154,13 @@ static void bitmap_checkfree(struct bitmap *bitmap, unsigned long page)
*/
/* IO operations when bitmap is stored near all superblocks */
-static struct page *read_sb_page(mddev_t *mddev, loff_t offset,
+static struct page *read_sb_page(struct mddev *mddev, loff_t offset,
struct page *page,
unsigned long index, int size)
{
/* choose a good rdev and read the page from there */
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
sector_t target;
int did_alloc = 0;
@@ -227,7 +193,7 @@ static struct page *read_sb_page(mddev_t *mddev, loff_t offset,
}
-static mdk_rdev_t *next_active_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
+static struct md_rdev *next_active_rdev(struct md_rdev *rdev, struct mddev *mddev)
{
/* Iterate the disks of an mddev, using rcu to protect access to the
* linked list, and raising the refcount of devices we return to ensure
@@ -248,7 +214,7 @@ static mdk_rdev_t *next_active_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
pos = &rdev->same_set;
}
list_for_each_continue_rcu(pos, &mddev->disks) {
- rdev = list_entry(pos, mdk_rdev_t, same_set);
+ rdev = list_entry(pos, struct md_rdev, same_set);
if (rdev->raid_disk >= 0 &&
!test_bit(Faulty, &rdev->flags)) {
/* this is a usable devices */
@@ -263,9 +229,9 @@ static mdk_rdev_t *next_active_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait)
{
- mdk_rdev_t *rdev = NULL;
+ struct md_rdev *rdev = NULL;
struct block_device *bdev;
- mddev_t *mddev = bitmap->mddev;
+ struct mddev *mddev = bitmap->mddev;
while ((rdev = next_active_rdev(rdev, mddev)) != NULL) {
int size = PAGE_SIZE;
@@ -410,8 +376,8 @@ static struct page *read_page(struct file *file, unsigned long index,
struct buffer_head *bh;
sector_t block;
- PRINTK("read bitmap file (%dB @ %llu)\n", (int)PAGE_SIZE,
- (unsigned long long)index << PAGE_SHIFT);
+ pr_debug("read bitmap file (%dB @ %llu)\n", (int)PAGE_SIZE,
+ (unsigned long long)index << PAGE_SHIFT);
page = alloc_page(GFP_KERNEL);
if (!page)
@@ -775,10 +741,8 @@ static inline unsigned long file_page_offset(struct bitmap *bitmap, unsigned lon
* 0 or page 1
*/
static inline struct page *filemap_get_page(struct bitmap *bitmap,
- unsigned long chunk)
+ unsigned long chunk)
{
- if (bitmap->filemap == NULL)
- return NULL;
if (file_page_index(bitmap, chunk) >= bitmap->file_pages)
return NULL;
return bitmap->filemap[file_page_index(bitmap, chunk)
@@ -871,35 +835,27 @@ static void bitmap_file_kick(struct bitmap *bitmap)
enum bitmap_page_attr {
BITMAP_PAGE_DIRTY = 0, /* there are set bits that need to be synced */
- BITMAP_PAGE_CLEAN = 1, /* there are bits that might need to be cleared */
+ BITMAP_PAGE_PENDING = 1, /* there are bits that are being cleaned.
+ * i.e. counter is 1 or 2. */
BITMAP_PAGE_NEEDWRITE = 2, /* there are cleared bits that need to be synced */
};
static inline void set_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- if (page)
- __set_bit((page->index<<2) + attr, bitmap->filemap_attr);
- else
- __set_bit(attr, &bitmap->logattrs);
+ __set_bit((page->index<<2) + attr, bitmap->filemap_attr);
}
static inline void clear_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- if (page)
- __clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
- else
- __clear_bit(attr, &bitmap->logattrs);
+ __clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
}
static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- if (page)
- return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
- else
- return test_bit(attr, &bitmap->logattrs);
+ return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
}
/*
@@ -912,30 +868,26 @@ static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *p
static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
{
unsigned long bit;
- struct page *page = NULL;
+ struct page *page;
void *kaddr;
unsigned long chunk = block >> CHUNK_BLOCK_SHIFT(bitmap);
- if (!bitmap->filemap) {
- struct dm_dirty_log *log = bitmap->mddev->bitmap_info.log;
- if (log)
- log->type->mark_region(log, chunk);
- } else {
+ if (!bitmap->filemap)
+ return;
- page = filemap_get_page(bitmap, chunk);
- if (!page)
- return;
- bit = file_page_offset(bitmap, chunk);
+ page = filemap_get_page(bitmap, chunk);
+ if (!page)
+ return;
+ bit = file_page_offset(bitmap, chunk);
- /* set the bit */
- kaddr = kmap_atomic(page, KM_USER0);
- if (bitmap->flags & BITMAP_HOSTENDIAN)
- set_bit(bit, kaddr);
- else
- __test_and_set_bit_le(bit, kaddr);
- kunmap_atomic(kaddr, KM_USER0);
- PRINTK("set file bit %lu page %lu\n", bit, page->index);
- }
+ /* set the bit */
+ kaddr = kmap_atomic(page, KM_USER0);
+ if (bitmap->flags & BITMAP_HOSTENDIAN)
+ set_bit(bit, kaddr);
+ else
+ __set_bit_le(bit, kaddr);
+ kunmap_atomic(kaddr, KM_USER0);
+ pr_debug("set file bit %lu page %lu\n", bit, page->index);
/* record page number so it gets flushed to disk when unplug occurs */
set_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
}
@@ -952,16 +904,6 @@ void bitmap_unplug(struct bitmap *bitmap)
if (!bitmap)
return;
- if (!bitmap->filemap) {
- /* Must be using a dirty_log */
- struct dm_dirty_log *log = bitmap->mddev->bitmap_info.log;
- dirty = test_and_clear_bit(BITMAP_PAGE_DIRTY, &bitmap->logattrs);
- need_write = test_and_clear_bit(BITMAP_PAGE_NEEDWRITE, &bitmap->logattrs);
- if (dirty || need_write)
- if (log->type->flush(log))
- bitmap->flags |= BITMAP_WRITE_ERROR;
- goto out;
- }
/* look at each page to see if there are any set bits that need to be
* flushed out to disk */
@@ -990,7 +932,6 @@ void bitmap_unplug(struct bitmap *bitmap)
else
md_super_wait(bitmap->mddev);
}
-out:
if (bitmap->flags & BITMAP_WRITE_ERROR)
bitmap_file_kick(bitmap);
}
@@ -1024,11 +965,7 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
BUG_ON(!file && !bitmap->mddev->bitmap_info.offset);
-#ifdef INJECT_FAULTS_3
- outofdate = 1;
-#else
outofdate = bitmap->flags & BITMAP_STALE;
-#endif
if (outofdate)
printk(KERN_INFO "%s: bitmap file is out of date, doing full "
"recovery\n", bmname(bitmap));
@@ -1138,7 +1075,6 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
(sector_t)i << CHUNK_BLOCK_SHIFT(bitmap),
needed);
bit_cnt++;
- set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
}
}
@@ -1173,6 +1109,7 @@ void bitmap_write_all(struct bitmap *bitmap)
for (i = 0; i < bitmap->file_pages; i++)
set_page_attr(bitmap, bitmap->filemap[i],
BITMAP_PAGE_NEEDWRITE);
+ bitmap->allclean = 0;
}
static void bitmap_count_page(struct bitmap *bitmap, sector_t offset, int inc)
@@ -1191,7 +1128,7 @@ static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
* out to disk
*/
-void bitmap_daemon_work(mddev_t *mddev)
+void bitmap_daemon_work(struct mddev *mddev)
{
struct bitmap *bitmap;
unsigned long j;
@@ -1199,7 +1136,6 @@ void bitmap_daemon_work(mddev_t *mddev)
struct page *page = NULL, *lastpage = NULL;
sector_t blocks;
void *paddr;
- struct dm_dirty_log *log = mddev->bitmap_info.log;
/* Use a mutex to guard daemon_work against
* bitmap_destroy.
@@ -1224,26 +1160,23 @@ void bitmap_daemon_work(mddev_t *mddev)
spin_lock_irqsave(&bitmap->lock, flags);
for (j = 0; j < bitmap->chunks; j++) {
bitmap_counter_t *bmc;
- if (!bitmap->filemap) {
- if (!log)
- /* error or shutdown */
- break;
- } else
- page = filemap_get_page(bitmap, j);
+ if (!bitmap->filemap)
+ /* error or shutdown */
+ break;
+
+ page = filemap_get_page(bitmap, j);
if (page != lastpage) {
/* skip this page unless it's marked as needing cleaning */
- if (!test_page_attr(bitmap, page, BITMAP_PAGE_CLEAN)) {
+ if (!test_page_attr(bitmap, page, BITMAP_PAGE_PENDING)) {
int need_write = test_page_attr(bitmap, page,
BITMAP_PAGE_NEEDWRITE);
if (need_write)
clear_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
- if (need_write) {
+ if (need_write)
write_page(bitmap, page, 0);
- bitmap->allclean = 0;
- }
spin_lock_irqsave(&bitmap->lock, flags);
j |= (PAGE_BITS - 1);
continue;
@@ -1251,12 +1184,16 @@ void bitmap_daemon_work(mddev_t *mddev)
/* grab the new page, sync and release the old */
if (lastpage != NULL) {
- if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
- clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
+ if (test_page_attr(bitmap, lastpage,
+ BITMAP_PAGE_NEEDWRITE)) {
+ clear_page_attr(bitmap, lastpage,
+ BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
write_page(bitmap, lastpage, 0);
} else {
- set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
+ set_page_attr(bitmap, lastpage,
+ BITMAP_PAGE_NEEDWRITE);
+ bitmap->allclean = 0;
spin_unlock_irqrestore(&bitmap->lock, flags);
}
} else
@@ -1278,19 +1215,17 @@ void bitmap_daemon_work(mddev_t *mddev)
}
spin_lock_irqsave(&bitmap->lock, flags);
if (!bitmap->need_sync)
- clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
+ clear_page_attr(bitmap, page, BITMAP_PAGE_PENDING);
+ else
+ bitmap->allclean = 0;
}
bmc = bitmap_get_counter(bitmap,
(sector_t)j << CHUNK_BLOCK_SHIFT(bitmap),
&blocks, 0);
- if (bmc) {
- if (*bmc)
- bitmap->allclean = 0;
-
- if (*bmc == 2) {
- *bmc = 1; /* maybe clear the bit next time */
- set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
- } else if (*bmc == 1 && !bitmap->need_sync) {
+ if (!bmc)
+ j |= PAGE_COUNTER_MASK;
+ else if (*bmc) {
+ if (*bmc == 1 && !bitmap->need_sync) {
/* we can clear the bit */
*bmc = 0;
bitmap_count_page(bitmap,
@@ -1298,36 +1233,35 @@ void bitmap_daemon_work(mddev_t *mddev)
-1);
/* clear the bit */
- if (page) {
- paddr = kmap_atomic(page, KM_USER0);
- if (bitmap->flags & BITMAP_HOSTENDIAN)
- clear_bit(file_page_offset(bitmap, j),
- paddr);
- else
- __test_and_clear_bit_le(file_page_offset(bitmap, j),
- paddr);
- kunmap_atomic(paddr, KM_USER0);
- } else
- log->type->clear_region(log, j);
+ paddr = kmap_atomic(page, KM_USER0);
+ if (bitmap->flags & BITMAP_HOSTENDIAN)
+ clear_bit(file_page_offset(bitmap, j),
+ paddr);
+ else
+ __clear_bit_le(
+ file_page_offset(bitmap,
+ j),
+ paddr);
+ kunmap_atomic(paddr, KM_USER0);
+ } else if (*bmc <= 2) {
+ *bmc = 1; /* maybe clear the bit next time */
+ set_page_attr(bitmap, page, BITMAP_PAGE_PENDING);
+ bitmap->allclean = 0;
}
- } else
- j |= PAGE_COUNTER_MASK;
+ }
}
spin_unlock_irqrestore(&bitmap->lock, flags);
/* now sync the final page */
- if (lastpage != NULL || log != NULL) {
+ if (lastpage != NULL) {
spin_lock_irqsave(&bitmap->lock, flags);
if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
- if (lastpage)
- write_page(bitmap, lastpage, 0);
- else
- if (log->type->flush(log))
- bitmap->flags |= BITMAP_WRITE_ERROR;
+ write_page(bitmap, lastpage, 0);
} else {
set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
+ bitmap->allclean = 0;
spin_unlock_irqrestore(&bitmap->lock, flags);
}
}
@@ -1393,8 +1327,8 @@ int bitmap_startwrite(struct bitmap *bitmap, sector_t offset, unsigned long sect
if (bw > bitmap->behind_writes_used)
bitmap->behind_writes_used = bw;
- PRINTK(KERN_DEBUG "inc write-behind count %d/%d\n",
- bw, bitmap->max_write_behind);
+ pr_debug("inc write-behind count %d/%lu\n",
+ bw, bitmap->mddev->bitmap_info.max_write_behind);
}
while (sectors) {
@@ -1441,7 +1375,6 @@ int bitmap_startwrite(struct bitmap *bitmap, sector_t offset, unsigned long sect
else
sectors = 0;
}
- bitmap->allclean = 0;
return 0;
}
EXPORT_SYMBOL(bitmap_startwrite);
@@ -1454,8 +1387,9 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto
if (behind) {
if (atomic_dec_and_test(&bitmap->behind_writes))
wake_up(&bitmap->behind_wait);
- PRINTK(KERN_DEBUG "dec write-behind count %d/%d\n",
- atomic_read(&bitmap->behind_writes), bitmap->max_write_behind);
+ pr_debug("dec write-behind count %d/%lu\n",
+ atomic_read(&bitmap->behind_writes),
+ bitmap->mddev->bitmap_info.max_write_behind);
}
if (bitmap->mddev->degraded)
/* Never clear bits or update events_cleared when degraded */
@@ -1487,13 +1421,14 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto
wake_up(&bitmap->overflow_wait);
(*bmc)--;
- if (*bmc <= 2)
+ if (*bmc <= 2) {
set_page_attr(bitmap,
filemap_get_page(
bitmap,
offset >> CHUNK_BLOCK_SHIFT(bitmap)),
- BITMAP_PAGE_CLEAN);
-
+ BITMAP_PAGE_PENDING);
+ bitmap->allclean = 0;
+ }
spin_unlock_irqrestore(&bitmap->lock, flags);
offset += blocks;
if (sectors > blocks)
@@ -1529,7 +1464,6 @@ static int __bitmap_start_sync(struct bitmap *bitmap, sector_t offset, sector_t
}
}
spin_unlock_irq(&bitmap->lock);
- bitmap->allclean = 0;
return rv;
}
@@ -1577,15 +1511,16 @@ void bitmap_end_sync(struct bitmap *bitmap, sector_t offset, sector_t *blocks, i
if (!NEEDED(*bmc) && aborted)
*bmc |= NEEDED_MASK;
else {
- if (*bmc <= 2)
+ if (*bmc <= 2) {
set_page_attr(bitmap,
filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap)),
- BITMAP_PAGE_CLEAN);
+ BITMAP_PAGE_PENDING);
+ bitmap->allclean = 0;
+ }
}
}
unlock:
spin_unlock_irqrestore(&bitmap->lock, flags);
- bitmap->allclean = 0;
}
EXPORT_SYMBOL(bitmap_end_sync);
@@ -1656,10 +1591,10 @@ static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int n
*bmc = 1 | (needed ? NEEDED_MASK : 0);
bitmap_count_page(bitmap, offset, 1);
page = filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap));
- set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
+ set_page_attr(bitmap, page, BITMAP_PAGE_PENDING);
+ bitmap->allclean = 0;
}
spin_unlock_irq(&bitmap->lock);
- bitmap->allclean = 0;
}
/* dirty the memory and file bits for bitmap chunks "s" to "e" */
@@ -1683,7 +1618,7 @@ void bitmap_dirty_bits(struct bitmap *bitmap, unsigned long s, unsigned long e)
/*
* flush out any pending updates
*/
-void bitmap_flush(mddev_t *mddev)
+void bitmap_flush(struct mddev *mddev)
{
struct bitmap *bitmap = mddev->bitmap;
long sleep;
@@ -1731,7 +1666,7 @@ static void bitmap_free(struct bitmap *bitmap)
kfree(bitmap);
}
-void bitmap_destroy(mddev_t *mddev)
+void bitmap_destroy(struct mddev *mddev)
{
struct bitmap *bitmap = mddev->bitmap;
@@ -1754,7 +1689,7 @@ void bitmap_destroy(mddev_t *mddev)
* initialize the bitmap structure
* if this returns an error, bitmap_destroy must be called to do clean up
*/
-int bitmap_create(mddev_t *mddev)
+int bitmap_create(struct mddev *mddev)
{
struct bitmap *bitmap;
sector_t blocks = mddev->resync_max_sectors;
@@ -1767,12 +1702,10 @@ int bitmap_create(mddev_t *mddev)
BUILD_BUG_ON(sizeof(bitmap_super_t) != 256);
if (!file
- && !mddev->bitmap_info.offset
- && !mddev->bitmap_info.log) /* bitmap disabled, nothing to do */
+ && !mddev->bitmap_info.offset) /* bitmap disabled, nothing to do */
return 0;
BUG_ON(file && mddev->bitmap_info.offset);
- BUG_ON(mddev->bitmap_info.offset && mddev->bitmap_info.log);
bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL);
if (!bitmap)
@@ -1838,11 +1771,8 @@ int bitmap_create(mddev_t *mddev)
bitmap->pages = pages;
bitmap->missing_pages = pages;
-#ifdef INJECT_FATAL_FAULT_1
- bitmap->bp = NULL;
-#else
bitmap->bp = kzalloc(pages * sizeof(*bitmap->bp), GFP_KERNEL);
-#endif
+
err = -ENOMEM;
if (!bitmap->bp)
goto error;
@@ -1860,9 +1790,10 @@ int bitmap_create(mddev_t *mddev)
return err;
}
-int bitmap_load(mddev_t *mddev)
+int bitmap_load(struct mddev *mddev)
{
int err = 0;
+ sector_t start = 0;
sector_t sector = 0;
struct bitmap *bitmap = mddev->bitmap;
@@ -1881,24 +1812,14 @@ int bitmap_load(mddev_t *mddev)
}
bitmap_close_sync(bitmap);
- if (mddev->bitmap_info.log) {
- unsigned long i;
- struct dm_dirty_log *log = mddev->bitmap_info.log;
- for (i = 0; i < bitmap->chunks; i++)
- if (!log->type->in_sync(log, i, 1))
- bitmap_set_memory_bits(bitmap,
- (sector_t)i << CHUNK_BLOCK_SHIFT(bitmap),
- 1);
- } else {
- sector_t start = 0;
- if (mddev->degraded == 0
- || bitmap->events_cleared == mddev->events)
- /* no need to keep dirty bits to optimise a
- * re-add of a missing device */
- start = mddev->recovery_cp;
-
- err = bitmap_init_from_disk(bitmap, start);
- }
+ if (mddev->degraded == 0
+ || bitmap->events_cleared == mddev->events)
+ /* no need to keep dirty bits to optimise a
+ * re-add of a missing device */
+ start = mddev->recovery_cp;
+
+ err = bitmap_init_from_disk(bitmap, start);
+
if (err)
goto out;
@@ -1915,7 +1836,7 @@ out:
EXPORT_SYMBOL_GPL(bitmap_load);
static ssize_t
-location_show(mddev_t *mddev, char *page)
+location_show(struct mddev *mddev, char *page)
{
ssize_t len;
if (mddev->bitmap_info.file)
@@ -1929,7 +1850,7 @@ location_show(mddev_t *mddev, char *page)
}
static ssize_t
-location_store(mddev_t *mddev, const char *buf, size_t len)
+location_store(struct mddev *mddev, const char *buf, size_t len)
{
if (mddev->pers) {
@@ -2006,7 +1927,7 @@ static struct md_sysfs_entry bitmap_location =
__ATTR(location, S_IRUGO|S_IWUSR, location_show, location_store);
static ssize_t
-timeout_show(mddev_t *mddev, char *page)
+timeout_show(struct mddev *mddev, char *page)
{
ssize_t len;
unsigned long secs = mddev->bitmap_info.daemon_sleep / HZ;
@@ -2020,7 +1941,7 @@ timeout_show(mddev_t *mddev, char *page)
}
static ssize_t
-timeout_store(mddev_t *mddev, const char *buf, size_t len)
+timeout_store(struct mddev *mddev, const char *buf, size_t len)
{
/* timeout can be set at any time */
unsigned long timeout;
@@ -2056,13 +1977,13 @@ static struct md_sysfs_entry bitmap_timeout =
__ATTR(time_base, S_IRUGO|S_IWUSR, timeout_show, timeout_store);
static ssize_t
-backlog_show(mddev_t *mddev, char *page)
+backlog_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%lu\n", mddev->bitmap_info.max_write_behind);
}
static ssize_t
-backlog_store(mddev_t *mddev, const char *buf, size_t len)
+backlog_store(struct mddev *mddev, const char *buf, size_t len)
{
unsigned long backlog;
int rv = strict_strtoul(buf, 10, &backlog);
@@ -2078,13 +1999,13 @@ static struct md_sysfs_entry bitmap_backlog =
__ATTR(backlog, S_IRUGO|S_IWUSR, backlog_show, backlog_store);
static ssize_t
-chunksize_show(mddev_t *mddev, char *page)
+chunksize_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%lu\n", mddev->bitmap_info.chunksize);
}
static ssize_t
-chunksize_store(mddev_t *mddev, const char *buf, size_t len)
+chunksize_store(struct mddev *mddev, const char *buf, size_t len)
{
/* Can only be changed when no bitmap is active */
int rv;
@@ -2104,13 +2025,13 @@ chunksize_store(mddev_t *mddev, const char *buf, size_t len)
static struct md_sysfs_entry bitmap_chunksize =
__ATTR(chunksize, S_IRUGO|S_IWUSR, chunksize_show, chunksize_store);
-static ssize_t metadata_show(mddev_t *mddev, char *page)
+static ssize_t metadata_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%s\n", (mddev->bitmap_info.external
? "external" : "internal"));
}
-static ssize_t metadata_store(mddev_t *mddev, const char *buf, size_t len)
+static ssize_t metadata_store(struct mddev *mddev, const char *buf, size_t len)
{
if (mddev->bitmap ||
mddev->bitmap_info.file ||
@@ -2128,7 +2049,7 @@ static ssize_t metadata_store(mddev_t *mddev, const char *buf, size_t len)
static struct md_sysfs_entry bitmap_metadata =
__ATTR(metadata, S_IRUGO|S_IWUSR, metadata_show, metadata_store);
-static ssize_t can_clear_show(mddev_t *mddev, char *page)
+static ssize_t can_clear_show(struct mddev *mddev, char *page)
{
int len;
if (mddev->bitmap)
@@ -2139,7 +2060,7 @@ static ssize_t can_clear_show(mddev_t *mddev, char *page)
return len;
}
-static ssize_t can_clear_store(mddev_t *mddev, const char *buf, size_t len)
+static ssize_t can_clear_store(struct mddev *mddev, const char *buf, size_t len)
{
if (mddev->bitmap == NULL)
return -ENOENT;
@@ -2158,7 +2079,7 @@ static struct md_sysfs_entry bitmap_can_clear =
__ATTR(can_clear, S_IRUGO|S_IWUSR, can_clear_show, can_clear_store);
static ssize_t
-behind_writes_used_show(mddev_t *mddev, char *page)
+behind_writes_used_show(struct mddev *mddev, char *page)
{
if (mddev->bitmap == NULL)
return sprintf(page, "0\n");
@@ -2167,7 +2088,7 @@ behind_writes_used_show(mddev_t *mddev, char *page)
}
static ssize_t
-behind_writes_used_reset(mddev_t *mddev, const char *buf, size_t len)
+behind_writes_used_reset(struct mddev *mddev, const char *buf, size_t len)
{
if (mddev->bitmap)
mddev->bitmap->behind_writes_used = 0;
diff --git a/drivers/md/bitmap.h b/drivers/md/bitmap.h
index b2a127e891ac..a15436dd9b3e 100644
--- a/drivers/md/bitmap.h
+++ b/drivers/md/bitmap.h
@@ -193,7 +193,7 @@ struct bitmap {
unsigned long pages; /* total number of pages in the bitmap */
unsigned long missing_pages; /* number of pages not yet allocated */
- mddev_t *mddev; /* the md device that the bitmap is for */
+ struct mddev *mddev; /* the md device that the bitmap is for */
/* bitmap chunksize -- how much data does each bit represent? */
unsigned long chunkshift; /* chunksize = 2^chunkshift (for bitops) */
@@ -212,10 +212,6 @@ struct bitmap {
unsigned long file_pages; /* number of pages in the file */
int last_page_size; /* bytes in the last page */
- unsigned long logattrs; /* used when filemap_attr doesn't exist
- * because we are working with a dirty_log
- */
-
unsigned long flags;
int allclean;
@@ -237,16 +233,15 @@ struct bitmap {
wait_queue_head_t behind_wait;
struct sysfs_dirent *sysfs_can_clear;
-
};
/* the bitmap API */
/* these are used only by md/bitmap */
-int bitmap_create(mddev_t *mddev);
-int bitmap_load(mddev_t *mddev);
-void bitmap_flush(mddev_t *mddev);
-void bitmap_destroy(mddev_t *mddev);
+int bitmap_create(struct mddev *mddev);
+int bitmap_load(struct mddev *mddev);
+void bitmap_flush(struct mddev *mddev);
+void bitmap_destroy(struct mddev *mddev);
void bitmap_print_sb(struct bitmap *bitmap);
void bitmap_update_sb(struct bitmap *bitmap);
@@ -267,7 +262,7 @@ void bitmap_close_sync(struct bitmap *bitmap);
void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector);
void bitmap_unplug(struct bitmap *bitmap);
-void bitmap_daemon_work(mddev_t *mddev);
+void bitmap_daemon_work(struct mddev *mddev);
#endif
#endif
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
new file mode 100644
index 000000000000..0a6806f80ab5
--- /dev/null
+++ b/drivers/md/dm-bufio.c
@@ -0,0 +1,1700 @@
+/*
+ * Copyright (C) 2009-2011 Red Hat, Inc.
+ *
+ * Author: Mikulas Patocka <mpatocka@redhat.com>
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-bufio.h"
+
+#include <linux/device-mapper.h>
+#include <linux/dm-io.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/version.h>
+#include <linux/shrinker.h>
+#include <linux/module.h>
+
+#define DM_MSG_PREFIX "bufio"
+
+/*
+ * Memory management policy:
+ * Limit the number of buffers to DM_BUFIO_MEMORY_PERCENT of main memory
+ * or DM_BUFIO_VMALLOC_PERCENT of vmalloc memory (whichever is lower).
+ * Always allocate at least DM_BUFIO_MIN_BUFFERS buffers.
+ * Start background writeback when there are DM_BUFIO_WRITEBACK_PERCENT
+ * dirty buffers.
+ */
+#define DM_BUFIO_MIN_BUFFERS 8
+
+#define DM_BUFIO_MEMORY_PERCENT 2
+#define DM_BUFIO_VMALLOC_PERCENT 25
+#define DM_BUFIO_WRITEBACK_PERCENT 75
+
+/*
+ * Check buffer ages in this interval (seconds)
+ */
+#define DM_BUFIO_WORK_TIMER_SECS 10
+
+/*
+ * Free buffers when they are older than this (seconds)
+ */
+#define DM_BUFIO_DEFAULT_AGE_SECS 60
+
+/*
+ * The number of bvec entries that are embedded directly in the buffer.
+ * If the chunk size is larger, dm-io is used to do the io.
+ */
+#define DM_BUFIO_INLINE_VECS 16
+
+/*
+ * Buffer hash
+ */
+#define DM_BUFIO_HASH_BITS 20
+#define DM_BUFIO_HASH(block) \
+ ((((block) >> DM_BUFIO_HASH_BITS) ^ (block)) & \
+ ((1 << DM_BUFIO_HASH_BITS) - 1))
+
+/*
+ * Don't try to use kmem_cache_alloc for blocks larger than this.
+ * For explanation, see alloc_buffer_data below.
+ */
+#define DM_BUFIO_BLOCK_SIZE_SLAB_LIMIT (PAGE_SIZE >> 1)
+#define DM_BUFIO_BLOCK_SIZE_GFP_LIMIT (PAGE_SIZE << (MAX_ORDER - 1))
+
+/*
+ * dm_buffer->list_mode
+ */
+#define LIST_CLEAN 0
+#define LIST_DIRTY 1
+#define LIST_SIZE 2
+
+/*
+ * Linking of buffers:
+ * All buffers are linked to cache_hash with their hash_list field.
+ *
+ * Clean buffers that are not being written (B_WRITING not set)
+ * are linked to lru[LIST_CLEAN] with their lru_list field.
+ *
+ * Dirty and clean buffers that are being written are linked to
+ * lru[LIST_DIRTY] with their lru_list field. When the write
+ * finishes, the buffer cannot be relinked immediately (because we
+ * are in an interrupt context and relinking requires process
+ * context), so some clean-not-writing buffers can be held on
+ * dirty_lru too. They are later added to lru in the process
+ * context.
+ */
+struct dm_bufio_client {
+ struct mutex lock;
+
+ struct list_head lru[LIST_SIZE];
+ unsigned long n_buffers[LIST_SIZE];
+
+ struct block_device *bdev;
+ unsigned block_size;
+ unsigned char sectors_per_block_bits;
+ unsigned char pages_per_block_bits;
+ unsigned char blocks_per_page_bits;
+ unsigned aux_size;
+ void (*alloc_callback)(struct dm_buffer *);
+ void (*write_callback)(struct dm_buffer *);
+
+ struct dm_io_client *dm_io;
+
+ struct list_head reserved_buffers;
+ unsigned need_reserved_buffers;
+
+ struct hlist_head *cache_hash;
+ wait_queue_head_t free_buffer_wait;
+
+ int async_write_error;
+
+ struct list_head client_list;
+ struct shrinker shrinker;
+};
+
+/*
+ * Buffer state bits.
+ */
+#define B_READING 0
+#define B_WRITING 1
+#define B_DIRTY 2
+
+/*
+ * Describes how the block was allocated:
+ * kmem_cache_alloc(), __get_free_pages() or vmalloc().
+ * See the comment at alloc_buffer_data.
+ */
+enum data_mode {
+ DATA_MODE_SLAB = 0,
+ DATA_MODE_GET_FREE_PAGES = 1,
+ DATA_MODE_VMALLOC = 2,
+ DATA_MODE_LIMIT = 3
+};
+
+struct dm_buffer {
+ struct hlist_node hash_list;
+ struct list_head lru_list;
+ sector_t block;
+ void *data;
+ enum data_mode data_mode;
+ unsigned char list_mode; /* LIST_* */
+ unsigned hold_count;
+ int read_error;
+ int write_error;
+ unsigned long state;
+ unsigned long last_accessed;
+ struct dm_bufio_client *c;
+ struct bio bio;
+ struct bio_vec bio_vec[DM_BUFIO_INLINE_VECS];
+};
+
+/*----------------------------------------------------------------*/
+
+static struct kmem_cache *dm_bufio_caches[PAGE_SHIFT - SECTOR_SHIFT];
+static char *dm_bufio_cache_names[PAGE_SHIFT - SECTOR_SHIFT];
+
+static inline int dm_bufio_cache_index(struct dm_bufio_client *c)
+{
+ unsigned ret = c->blocks_per_page_bits - 1;
+
+ BUG_ON(ret >= ARRAY_SIZE(dm_bufio_caches));
+
+ return ret;
+}
+
+#define DM_BUFIO_CACHE(c) (dm_bufio_caches[dm_bufio_cache_index(c)])
+#define DM_BUFIO_CACHE_NAME(c) (dm_bufio_cache_names[dm_bufio_cache_index(c)])
+
+#define dm_bufio_in_request() (!!current->bio_list)
+
+static void dm_bufio_lock(struct dm_bufio_client *c)
+{
+ mutex_lock_nested(&c->lock, dm_bufio_in_request());
+}
+
+static int dm_bufio_trylock(struct dm_bufio_client *c)
+{
+ return mutex_trylock(&c->lock);
+}
+
+static void dm_bufio_unlock(struct dm_bufio_client *c)
+{
+ mutex_unlock(&c->lock);
+}
+
+/*
+ * FIXME Move to sched.h?
+ */
+#ifdef CONFIG_PREEMPT_VOLUNTARY
+# define dm_bufio_cond_resched() \
+do { \
+ if (unlikely(need_resched())) \
+ _cond_resched(); \
+} while (0)
+#else
+# define dm_bufio_cond_resched() do { } while (0)
+#endif
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Default cache size: available memory divided by the ratio.
+ */
+static unsigned long dm_bufio_default_cache_size;
+
+/*
+ * Total cache size set by the user.
+ */
+static unsigned long dm_bufio_cache_size;
+
+/*
+ * A copy of dm_bufio_cache_size because dm_bufio_cache_size can change
+ * at any time. If it disagrees, the user has changed cache size.
+ */
+static unsigned long dm_bufio_cache_size_latch;
+
+static DEFINE_SPINLOCK(param_spinlock);
+
+/*
+ * Buffers are freed after this timeout
+ */
+static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS;
+
+static unsigned long dm_bufio_peak_allocated;
+static unsigned long dm_bufio_allocated_kmem_cache;
+static unsigned long dm_bufio_allocated_get_free_pages;
+static unsigned long dm_bufio_allocated_vmalloc;
+static unsigned long dm_bufio_current_allocated;
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Per-client cache: dm_bufio_cache_size / dm_bufio_client_count
+ */
+static unsigned long dm_bufio_cache_size_per_client;
+
+/*
+ * The current number of clients.
+ */
+static int dm_bufio_client_count;
+
+/*
+ * The list of all clients.
+ */
+static LIST_HEAD(dm_bufio_all_clients);
+
+/*
+ * This mutex protects dm_bufio_cache_size_latch,
+ * dm_bufio_cache_size_per_client and dm_bufio_client_count
+ */
+static DEFINE_MUTEX(dm_bufio_clients_lock);
+
+/*----------------------------------------------------------------*/
+
+static void adjust_total_allocated(enum data_mode data_mode, long diff)
+{
+ static unsigned long * const class_ptr[DATA_MODE_LIMIT] = {
+ &dm_bufio_allocated_kmem_cache,
+ &dm_bufio_allocated_get_free_pages,
+ &dm_bufio_allocated_vmalloc,
+ };
+
+ spin_lock(&param_spinlock);
+
+ *class_ptr[data_mode] += diff;
+
+ dm_bufio_current_allocated += diff;
+
+ if (dm_bufio_current_allocated > dm_bufio_peak_allocated)
+ dm_bufio_peak_allocated = dm_bufio_current_allocated;
+
+ spin_unlock(&param_spinlock);
+}
+
+/*
+ * Change the number of clients and recalculate per-client limit.
+ */
+static void __cache_size_refresh(void)
+{
+ BUG_ON(!mutex_is_locked(&dm_bufio_clients_lock));
+ BUG_ON(dm_bufio_client_count < 0);
+
+ dm_bufio_cache_size_latch = dm_bufio_cache_size;
+
+ barrier();
+
+ /*
+ * Use default if set to 0 and report the actual cache size used.
+ */
+ if (!dm_bufio_cache_size_latch) {
+ (void)cmpxchg(&dm_bufio_cache_size, 0,
+ dm_bufio_default_cache_size);
+ dm_bufio_cache_size_latch = dm_bufio_default_cache_size;
+ }
+
+ dm_bufio_cache_size_per_client = dm_bufio_cache_size_latch /
+ (dm_bufio_client_count ? : 1);
+}
+
+/*
+ * Allocating buffer data.
+ *
+ * Small buffers are allocated with kmem_cache, to use space optimally.
+ *
+ * For large buffers, we choose between get_free_pages and vmalloc.
+ * Each has advantages and disadvantages.
+ *
+ * __get_free_pages can randomly fail if the memory is fragmented.
+ * __vmalloc won't randomly fail, but vmalloc space is limited (it may be
+ * as low as 128M) so using it for caching is not appropriate.
+ *
+ * If the allocation may fail we use __get_free_pages. Memory fragmentation
+ * won't have a fatal effect here, but it just causes flushes of some other
+ * buffers and more I/O will be performed. Don't use __get_free_pages if it
+ * always fails (i.e. order >= MAX_ORDER).
+ *
+ * If the allocation shouldn't fail we use __vmalloc. This is only for the
+ * initial reserve allocation, so there's no risk of wasting all vmalloc
+ * space.
+ */
+static void *alloc_buffer_data(struct dm_bufio_client *c, gfp_t gfp_mask,
+ enum data_mode *data_mode)
+{
+ if (c->block_size <= DM_BUFIO_BLOCK_SIZE_SLAB_LIMIT) {
+ *data_mode = DATA_MODE_SLAB;
+ return kmem_cache_alloc(DM_BUFIO_CACHE(c), gfp_mask);
+ }
+
+ if (c->block_size <= DM_BUFIO_BLOCK_SIZE_GFP_LIMIT &&
+ gfp_mask & __GFP_NORETRY) {
+ *data_mode = DATA_MODE_GET_FREE_PAGES;
+ return (void *)__get_free_pages(gfp_mask,
+ c->pages_per_block_bits);
+ }
+
+ *data_mode = DATA_MODE_VMALLOC;
+ return __vmalloc(c->block_size, gfp_mask, PAGE_KERNEL);
+}
+
+/*
+ * Free buffer's data.
+ */
+static void free_buffer_data(struct dm_bufio_client *c,
+ void *data, enum data_mode data_mode)
+{
+ switch (data_mode) {
+ case DATA_MODE_SLAB:
+ kmem_cache_free(DM_BUFIO_CACHE(c), data);
+ break;
+
+ case DATA_MODE_GET_FREE_PAGES:
+ free_pages((unsigned long)data, c->pages_per_block_bits);
+ break;
+
+ case DATA_MODE_VMALLOC:
+ vfree(data);
+ break;
+
+ default:
+ DMCRIT("dm_bufio_free_buffer_data: bad data mode: %d",
+ data_mode);
+ BUG();
+ }
+}
+
+/*
+ * Allocate buffer and its data.
+ */
+static struct dm_buffer *alloc_buffer(struct dm_bufio_client *c, gfp_t gfp_mask)
+{
+ struct dm_buffer *b = kmalloc(sizeof(struct dm_buffer) + c->aux_size,
+ gfp_mask);
+
+ if (!b)
+ return NULL;
+
+ b->c = c;
+
+ b->data = alloc_buffer_data(c, gfp_mask, &b->data_mode);
+ if (!b->data) {
+ kfree(b);
+ return NULL;
+ }
+
+ adjust_total_allocated(b->data_mode, (long)c->block_size);
+
+ return b;
+}
+
+/*
+ * Free buffer and its data.
+ */
+static void free_buffer(struct dm_buffer *b)
+{
+ struct dm_bufio_client *c = b->c;
+
+ adjust_total_allocated(b->data_mode, -(long)c->block_size);
+
+ free_buffer_data(c, b->data, b->data_mode);
+ kfree(b);
+}
+
+/*
+ * Link buffer to the hash list and clean or dirty queue.
+ */
+static void __link_buffer(struct dm_buffer *b, sector_t block, int dirty)
+{
+ struct dm_bufio_client *c = b->c;
+
+ c->n_buffers[dirty]++;
+ b->block = block;
+ b->list_mode = dirty;
+ list_add(&b->lru_list, &c->lru[dirty]);
+ hlist_add_head(&b->hash_list, &c->cache_hash[DM_BUFIO_HASH(block)]);
+ b->last_accessed = jiffies;
+}
+
+/*
+ * Unlink buffer from the hash list and dirty or clean queue.
+ */
+static void __unlink_buffer(struct dm_buffer *b)
+{
+ struct dm_bufio_client *c = b->c;
+
+ BUG_ON(!c->n_buffers[b->list_mode]);
+
+ c->n_buffers[b->list_mode]--;
+ hlist_del(&b->hash_list);
+ list_del(&b->lru_list);
+}
+
+/*
+ * Place the buffer to the head of dirty or clean LRU queue.
+ */
+static void __relink_lru(struct dm_buffer *b, int dirty)
+{
+ struct dm_bufio_client *c = b->c;
+
+ BUG_ON(!c->n_buffers[b->list_mode]);
+
+ c->n_buffers[b->list_mode]--;
+ c->n_buffers[dirty]++;
+ b->list_mode = dirty;
+ list_del(&b->lru_list);
+ list_add(&b->lru_list, &c->lru[dirty]);
+}
+
+/*----------------------------------------------------------------
+ * Submit I/O on the buffer.
+ *
+ * Bio interface is faster but it has some problems:
+ * the vector list is limited (increasing this limit increases
+ * memory-consumption per buffer, so it is not viable);
+ *
+ * the memory must be direct-mapped, not vmalloced;
+ *
+ * the I/O driver can reject requests spuriously if it thinks that
+ * the requests are too big for the device or if they cross a
+ * controller-defined memory boundary.
+ *
+ * If the buffer is small enough (up to DM_BUFIO_INLINE_VECS pages) and
+ * it is not vmalloced, try using the bio interface.
+ *
+ * If the buffer is big, if it is vmalloced or if the underlying device
+ * rejects the bio because it is too large, use dm-io layer to do the I/O.
+ * The dm-io layer splits the I/O into multiple requests, avoiding the above
+ * shortcomings.
+ *--------------------------------------------------------------*/
+
+/*
+ * dm-io completion routine. It just calls b->bio.bi_end_io, pretending
+ * that the request was handled directly with bio interface.
+ */
+static void dmio_complete(unsigned long error, void *context)
+{
+ struct dm_buffer *b = context;
+
+ b->bio.bi_end_io(&b->bio, error ? -EIO : 0);
+}
+
+static void use_dmio(struct dm_buffer *b, int rw, sector_t block,
+ bio_end_io_t *end_io)
+{
+ int r;
+ struct dm_io_request io_req = {
+ .bi_rw = rw,
+ .notify.fn = dmio_complete,
+ .notify.context = b,
+ .client = b->c->dm_io,
+ };
+ struct dm_io_region region = {
+ .bdev = b->c->bdev,
+ .sector = block << b->c->sectors_per_block_bits,
+ .count = b->c->block_size >> SECTOR_SHIFT,
+ };
+
+ if (b->data_mode != DATA_MODE_VMALLOC) {
+ io_req.mem.type = DM_IO_KMEM;
+ io_req.mem.ptr.addr = b->data;
+ } else {
+ io_req.mem.type = DM_IO_VMA;
+ io_req.mem.ptr.vma = b->data;
+ }
+
+ b->bio.bi_end_io = end_io;
+
+ r = dm_io(&io_req, 1, &region, NULL);
+ if (r)
+ end_io(&b->bio, r);
+}
+
+static void use_inline_bio(struct dm_buffer *b, int rw, sector_t block,
+ bio_end_io_t *end_io)
+{
+ char *ptr;
+ int len;
+
+ bio_init(&b->bio);
+ b->bio.bi_io_vec = b->bio_vec;
+ b->bio.bi_max_vecs = DM_BUFIO_INLINE_VECS;
+ b->bio.bi_sector = block << b->c->sectors_per_block_bits;
+ b->bio.bi_bdev = b->c->bdev;
+ b->bio.bi_end_io = end_io;
+
+ /*
+ * We assume that if len >= PAGE_SIZE ptr is page-aligned.
+ * If len < PAGE_SIZE the buffer doesn't cross page boundary.
+ */
+ ptr = b->data;
+ len = b->c->block_size;
+
+ if (len >= PAGE_SIZE)
+ BUG_ON((unsigned long)ptr & (PAGE_SIZE - 1));
+ else
+ BUG_ON((unsigned long)ptr & (len - 1));
+
+ do {
+ if (!bio_add_page(&b->bio, virt_to_page(ptr),
+ len < PAGE_SIZE ? len : PAGE_SIZE,
+ virt_to_phys(ptr) & (PAGE_SIZE - 1))) {
+ BUG_ON(b->c->block_size <= PAGE_SIZE);
+ use_dmio(b, rw, block, end_io);
+ return;
+ }
+
+ len -= PAGE_SIZE;
+ ptr += PAGE_SIZE;
+ } while (len > 0);
+
+ submit_bio(rw, &b->bio);
+}
+
+static void submit_io(struct dm_buffer *b, int rw, sector_t block,
+ bio_end_io_t *end_io)
+{
+ if (rw == WRITE && b->c->write_callback)
+ b->c->write_callback(b);
+
+ if (b->c->block_size <= DM_BUFIO_INLINE_VECS * PAGE_SIZE &&
+ b->data_mode != DATA_MODE_VMALLOC)
+ use_inline_bio(b, rw, block, end_io);
+ else
+ use_dmio(b, rw, block, end_io);
+}
+
+/*----------------------------------------------------------------
+ * Writing dirty buffers
+ *--------------------------------------------------------------*/
+
+/*
+ * The endio routine for write.
+ *
+ * Set the error, clear B_WRITING bit and wake anyone who was waiting on
+ * it.
+ */
+static void write_endio(struct bio *bio, int error)
+{
+ struct dm_buffer *b = container_of(bio, struct dm_buffer, bio);
+
+ b->write_error = error;
+ if (error) {
+ struct dm_bufio_client *c = b->c;
+ (void)cmpxchg(&c->async_write_error, 0, error);
+ }
+
+ BUG_ON(!test_bit(B_WRITING, &b->state));
+
+ smp_mb__before_clear_bit();
+ clear_bit(B_WRITING, &b->state);
+ smp_mb__after_clear_bit();
+
+ wake_up_bit(&b->state, B_WRITING);
+}
+
+/*
+ * This function is called when wait_on_bit is actually waiting.
+ */
+static int do_io_schedule(void *word)
+{
+ io_schedule();
+
+ return 0;
+}
+
+/*
+ * Initiate a write on a dirty buffer, but don't wait for it.
+ *
+ * - If the buffer is not dirty, exit.
+ * - If there some previous write going on, wait for it to finish (we can't
+ * have two writes on the same buffer simultaneously).
+ * - Submit our write and don't wait on it. We set B_WRITING indicating
+ * that there is a write in progress.
+ */
+static void __write_dirty_buffer(struct dm_buffer *b)
+{
+ if (!test_bit(B_DIRTY, &b->state))
+ return;
+
+ clear_bit(B_DIRTY, &b->state);
+ wait_on_bit_lock(&b->state, B_WRITING,
+ do_io_schedule, TASK_UNINTERRUPTIBLE);
+
+ submit_io(b, WRITE, b->block, write_endio);
+}
+
+/*
+ * Wait until any activity on the buffer finishes. Possibly write the
+ * buffer if it is dirty. When this function finishes, there is no I/O
+ * running on the buffer and the buffer is not dirty.
+ */
+static void __make_buffer_clean(struct dm_buffer *b)
+{
+ BUG_ON(b->hold_count);
+
+ if (!b->state) /* fast case */
+ return;
+
+ wait_on_bit(&b->state, B_READING, do_io_schedule, TASK_UNINTERRUPTIBLE);
+ __write_dirty_buffer(b);
+ wait_on_bit(&b->state, B_WRITING, do_io_schedule, TASK_UNINTERRUPTIBLE);
+}
+
+/*
+ * Find some buffer that is not held by anybody, clean it, unlink it and
+ * return it.
+ */
+static struct dm_buffer *__get_unclaimed_buffer(struct dm_bufio_client *c)
+{
+ struct dm_buffer *b;
+
+ list_for_each_entry_reverse(b, &c->lru[LIST_CLEAN], lru_list) {
+ BUG_ON(test_bit(B_WRITING, &b->state));
+ BUG_ON(test_bit(B_DIRTY, &b->state));
+
+ if (!b->hold_count) {
+ __make_buffer_clean(b);
+ __unlink_buffer(b);
+ return b;
+ }
+ dm_bufio_cond_resched();
+ }
+
+ list_for_each_entry_reverse(b, &c->lru[LIST_DIRTY], lru_list) {
+ BUG_ON(test_bit(B_READING, &b->state));
+
+ if (!b->hold_count) {
+ __make_buffer_clean(b);
+ __unlink_buffer(b);
+ return b;
+ }
+ dm_bufio_cond_resched();
+ }
+
+ return NULL;
+}
+
+/*
+ * Wait until some other threads free some buffer or release hold count on
+ * some buffer.
+ *
+ * This function is entered with c->lock held, drops it and regains it
+ * before exiting.
+ */
+static void __wait_for_free_buffer(struct dm_bufio_client *c)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&c->free_buffer_wait, &wait);
+ set_task_state(current, TASK_UNINTERRUPTIBLE);
+ dm_bufio_unlock(c);
+
+ io_schedule();
+
+ set_task_state(current, TASK_RUNNING);
+ remove_wait_queue(&c->free_buffer_wait, &wait);
+
+ dm_bufio_lock(c);
+}
+
+/*
+ * Allocate a new buffer. If the allocation is not possible, wait until
+ * some other thread frees a buffer.
+ *
+ * May drop the lock and regain it.
+ */
+static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client *c)
+{
+ struct dm_buffer *b;
+
+ /*
+ * dm-bufio is resistant to allocation failures (it just keeps
+ * one buffer reserved in cases all the allocations fail).
+ * So set flags to not try too hard:
+ * GFP_NOIO: don't recurse into the I/O layer
+ * __GFP_NORETRY: don't retry and rather return failure
+ * __GFP_NOMEMALLOC: don't use emergency reserves
+ * __GFP_NOWARN: don't print a warning in case of failure
+ *
+ * For debugging, if we set the cache size to 1, no new buffers will
+ * be allocated.
+ */
+ while (1) {
+ if (dm_bufio_cache_size_latch != 1) {
+ b = alloc_buffer(c, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
+ if (b)
+ return b;
+ }
+
+ if (!list_empty(&c->reserved_buffers)) {
+ b = list_entry(c->reserved_buffers.next,
+ struct dm_buffer, lru_list);
+ list_del(&b->lru_list);
+ c->need_reserved_buffers++;
+
+ return b;
+ }
+
+ b = __get_unclaimed_buffer(c);
+ if (b)
+ return b;
+
+ __wait_for_free_buffer(c);
+ }
+}
+
+static struct dm_buffer *__alloc_buffer_wait(struct dm_bufio_client *c)
+{
+ struct dm_buffer *b = __alloc_buffer_wait_no_callback(c);
+
+ if (c->alloc_callback)
+ c->alloc_callback(b);
+
+ return b;
+}
+
+/*
+ * Free a buffer and wake other threads waiting for free buffers.
+ */
+static void __free_buffer_wake(struct dm_buffer *b)
+{
+ struct dm_bufio_client *c = b->c;
+
+ if (!c->need_reserved_buffers)
+ free_buffer(b);
+ else {
+ list_add(&b->lru_list, &c->reserved_buffers);
+ c->need_reserved_buffers--;
+ }
+
+ wake_up(&c->free_buffer_wait);
+}
+
+static void __write_dirty_buffers_async(struct dm_bufio_client *c, int no_wait)
+{
+ struct dm_buffer *b, *tmp;
+
+ list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_DIRTY], lru_list) {
+ BUG_ON(test_bit(B_READING, &b->state));
+
+ if (!test_bit(B_DIRTY, &b->state) &&
+ !test_bit(B_WRITING, &b->state)) {
+ __relink_lru(b, LIST_CLEAN);
+ continue;
+ }
+
+ if (no_wait && test_bit(B_WRITING, &b->state))
+ return;
+
+ __write_dirty_buffer(b);
+ dm_bufio_cond_resched();
+ }
+}
+
+/*
+ * Get writeback threshold and buffer limit for a given client.
+ */
+static void __get_memory_limit(struct dm_bufio_client *c,
+ unsigned long *threshold_buffers,
+ unsigned long *limit_buffers)
+{
+ unsigned long buffers;
+
+ if (dm_bufio_cache_size != dm_bufio_cache_size_latch) {
+ mutex_lock(&dm_bufio_clients_lock);
+ __cache_size_refresh();
+ mutex_unlock(&dm_bufio_clients_lock);
+ }
+
+ buffers = dm_bufio_cache_size_per_client >>
+ (c->sectors_per_block_bits + SECTOR_SHIFT);
+
+ if (buffers < DM_BUFIO_MIN_BUFFERS)
+ buffers = DM_BUFIO_MIN_BUFFERS;
+
+ *limit_buffers = buffers;
+ *threshold_buffers = buffers * DM_BUFIO_WRITEBACK_PERCENT / 100;
+}
+
+/*
+ * Check if we're over watermark.
+ * If we are over threshold_buffers, start freeing buffers.
+ * If we're over "limit_buffers", block until we get under the limit.
+ */
+static void __check_watermark(struct dm_bufio_client *c)
+{
+ unsigned long threshold_buffers, limit_buffers;
+
+ __get_memory_limit(c, &threshold_buffers, &limit_buffers);
+
+ while (c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY] >
+ limit_buffers) {
+
+ struct dm_buffer *b = __get_unclaimed_buffer(c);
+
+ if (!b)
+ return;
+
+ __free_buffer_wake(b);
+ dm_bufio_cond_resched();
+ }
+
+ if (c->n_buffers[LIST_DIRTY] > threshold_buffers)
+ __write_dirty_buffers_async(c, 1);
+}
+
+/*
+ * Find a buffer in the hash.
+ */
+static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block)
+{
+ struct dm_buffer *b;
+ struct hlist_node *hn;
+
+ hlist_for_each_entry(b, hn, &c->cache_hash[DM_BUFIO_HASH(block)],
+ hash_list) {
+ dm_bufio_cond_resched();
+ if (b->block == block)
+ return b;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------
+ * Getting a buffer
+ *--------------------------------------------------------------*/
+
+enum new_flag {
+ NF_FRESH = 0,
+ NF_READ = 1,
+ NF_GET = 2
+};
+
+static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
+ enum new_flag nf, struct dm_buffer **bp,
+ int *need_submit)
+{
+ struct dm_buffer *b, *new_b = NULL;
+
+ *need_submit = 0;
+
+ b = __find(c, block);
+ if (b) {
+ b->hold_count++;
+ __relink_lru(b, test_bit(B_DIRTY, &b->state) ||
+ test_bit(B_WRITING, &b->state));
+ return b;
+ }
+
+ if (nf == NF_GET)
+ return NULL;
+
+ new_b = __alloc_buffer_wait(c);
+
+ /*
+ * We've had a period where the mutex was unlocked, so need to
+ * recheck the hash table.
+ */
+ b = __find(c, block);
+ if (b) {
+ __free_buffer_wake(new_b);
+ b->hold_count++;
+ __relink_lru(b, test_bit(B_DIRTY, &b->state) ||
+ test_bit(B_WRITING, &b->state));
+ return b;
+ }
+
+ __check_watermark(c);
+
+ b = new_b;
+ b->hold_count = 1;
+ b->read_error = 0;
+ b->write_error = 0;
+ __link_buffer(b, block, LIST_CLEAN);
+
+ if (nf == NF_FRESH) {
+ b->state = 0;
+ return b;
+ }
+
+ b->state = 1 << B_READING;
+ *need_submit = 1;
+
+ return b;
+}
+
+/*
+ * The endio routine for reading: set the error, clear the bit and wake up
+ * anyone waiting on the buffer.
+ */
+static void read_endio(struct bio *bio, int error)
+{
+ struct dm_buffer *b = container_of(bio, struct dm_buffer, bio);
+
+ b->read_error = error;
+
+ BUG_ON(!test_bit(B_READING, &b->state));
+
+ smp_mb__before_clear_bit();
+ clear_bit(B_READING, &b->state);
+ smp_mb__after_clear_bit();
+
+ wake_up_bit(&b->state, B_READING);
+}
+
+/*
+ * A common routine for dm_bufio_new and dm_bufio_read. Operation of these
+ * functions is similar except that dm_bufio_new doesn't read the
+ * buffer from the disk (assuming that the caller overwrites all the data
+ * and uses dm_bufio_mark_buffer_dirty to write new data back).
+ */
+static void *new_read(struct dm_bufio_client *c, sector_t block,
+ enum new_flag nf, struct dm_buffer **bp)
+{
+ int need_submit;
+ struct dm_buffer *b;
+
+ dm_bufio_lock(c);
+ b = __bufio_new(c, block, nf, bp, &need_submit);
+ dm_bufio_unlock(c);
+
+ if (!b || IS_ERR(b))
+ return b;
+
+ if (need_submit)
+ submit_io(b, READ, b->block, read_endio);
+
+ wait_on_bit(&b->state, B_READING, do_io_schedule, TASK_UNINTERRUPTIBLE);
+
+ if (b->read_error) {
+ int error = b->read_error;
+
+ dm_bufio_release(b);
+
+ return ERR_PTR(error);
+ }
+
+ *bp = b;
+
+ return b->data;
+}
+
+void *dm_bufio_get(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp)
+{
+ return new_read(c, block, NF_GET, bp);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get);
+
+void *dm_bufio_read(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp)
+{
+ BUG_ON(dm_bufio_in_request());
+
+ return new_read(c, block, NF_READ, bp);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_read);
+
+void *dm_bufio_new(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp)
+{
+ BUG_ON(dm_bufio_in_request());
+
+ return new_read(c, block, NF_FRESH, bp);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_new);
+
+void dm_bufio_release(struct dm_buffer *b)
+{
+ struct dm_bufio_client *c = b->c;
+
+ dm_bufio_lock(c);
+
+ BUG_ON(test_bit(B_READING, &b->state));
+ BUG_ON(!b->hold_count);
+
+ b->hold_count--;
+ if (!b->hold_count) {
+ wake_up(&c->free_buffer_wait);
+
+ /*
+ * If there were errors on the buffer, and the buffer is not
+ * to be written, free the buffer. There is no point in caching
+ * invalid buffer.
+ */
+ if ((b->read_error || b->write_error) &&
+ !test_bit(B_WRITING, &b->state) &&
+ !test_bit(B_DIRTY, &b->state)) {
+ __unlink_buffer(b);
+ __free_buffer_wake(b);
+ }
+ }
+
+ dm_bufio_unlock(c);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_release);
+
+void dm_bufio_mark_buffer_dirty(struct dm_buffer *b)
+{
+ struct dm_bufio_client *c = b->c;
+
+ dm_bufio_lock(c);
+
+ if (!test_and_set_bit(B_DIRTY, &b->state))
+ __relink_lru(b, LIST_DIRTY);
+
+ dm_bufio_unlock(c);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_mark_buffer_dirty);
+
+void dm_bufio_write_dirty_buffers_async(struct dm_bufio_client *c)
+{
+ BUG_ON(dm_bufio_in_request());
+
+ dm_bufio_lock(c);
+ __write_dirty_buffers_async(c, 0);
+ dm_bufio_unlock(c);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_write_dirty_buffers_async);
+
+/*
+ * For performance, it is essential that the buffers are written asynchronously
+ * and simultaneously (so that the block layer can merge the writes) and then
+ * waited upon.
+ *
+ * Finally, we flush hardware disk cache.
+ */
+int dm_bufio_write_dirty_buffers(struct dm_bufio_client *c)
+{
+ int a, f;
+ unsigned long buffers_processed = 0;
+ struct dm_buffer *b, *tmp;
+
+ dm_bufio_lock(c);
+ __write_dirty_buffers_async(c, 0);
+
+again:
+ list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_DIRTY], lru_list) {
+ int dropped_lock = 0;
+
+ if (buffers_processed < c->n_buffers[LIST_DIRTY])
+ buffers_processed++;
+
+ BUG_ON(test_bit(B_READING, &b->state));
+
+ if (test_bit(B_WRITING, &b->state)) {
+ if (buffers_processed < c->n_buffers[LIST_DIRTY]) {
+ dropped_lock = 1;
+ b->hold_count++;
+ dm_bufio_unlock(c);
+ wait_on_bit(&b->state, B_WRITING,
+ do_io_schedule,
+ TASK_UNINTERRUPTIBLE);
+ dm_bufio_lock(c);
+ b->hold_count--;
+ } else
+ wait_on_bit(&b->state, B_WRITING,
+ do_io_schedule,
+ TASK_UNINTERRUPTIBLE);
+ }
+
+ if (!test_bit(B_DIRTY, &b->state) &&
+ !test_bit(B_WRITING, &b->state))
+ __relink_lru(b, LIST_CLEAN);
+
+ dm_bufio_cond_resched();
+
+ /*
+ * If we dropped the lock, the list is no longer consistent,
+ * so we must restart the search.
+ *
+ * In the most common case, the buffer just processed is
+ * relinked to the clean list, so we won't loop scanning the
+ * same buffer again and again.
+ *
+ * This may livelock if there is another thread simultaneously
+ * dirtying buffers, so we count the number of buffers walked
+ * and if it exceeds the total number of buffers, it means that
+ * someone is doing some writes simultaneously with us. In
+ * this case, stop, dropping the lock.
+ */
+ if (dropped_lock)
+ goto again;
+ }
+ wake_up(&c->free_buffer_wait);
+ dm_bufio_unlock(c);
+
+ a = xchg(&c->async_write_error, 0);
+ f = dm_bufio_issue_flush(c);
+ if (a)
+ return a;
+
+ return f;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_write_dirty_buffers);
+
+/*
+ * Use dm-io to send and empty barrier flush the device.
+ */
+int dm_bufio_issue_flush(struct dm_bufio_client *c)
+{
+ struct dm_io_request io_req = {
+ .bi_rw = REQ_FLUSH,
+ .mem.type = DM_IO_KMEM,
+ .mem.ptr.addr = NULL,
+ .client = c->dm_io,
+ };
+ struct dm_io_region io_reg = {
+ .bdev = c->bdev,
+ .sector = 0,
+ .count = 0,
+ };
+
+ BUG_ON(dm_bufio_in_request());
+
+ return dm_io(&io_req, 1, &io_reg, NULL);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_issue_flush);
+
+/*
+ * We first delete any other buffer that may be at that new location.
+ *
+ * Then, we write the buffer to the original location if it was dirty.
+ *
+ * Then, if we are the only one who is holding the buffer, relink the buffer
+ * in the hash queue for the new location.
+ *
+ * If there was someone else holding the buffer, we write it to the new
+ * location but not relink it, because that other user needs to have the buffer
+ * at the same place.
+ */
+void dm_bufio_release_move(struct dm_buffer *b, sector_t new_block)
+{
+ struct dm_bufio_client *c = b->c;
+ struct dm_buffer *new;
+
+ BUG_ON(dm_bufio_in_request());
+
+ dm_bufio_lock(c);
+
+retry:
+ new = __find(c, new_block);
+ if (new) {
+ if (new->hold_count) {
+ __wait_for_free_buffer(c);
+ goto retry;
+ }
+
+ /*
+ * FIXME: Is there any point waiting for a write that's going
+ * to be overwritten in a bit?
+ */
+ __make_buffer_clean(new);
+ __unlink_buffer(new);
+ __free_buffer_wake(new);
+ }
+
+ BUG_ON(!b->hold_count);
+ BUG_ON(test_bit(B_READING, &b->state));
+
+ __write_dirty_buffer(b);
+ if (b->hold_count == 1) {
+ wait_on_bit(&b->state, B_WRITING,
+ do_io_schedule, TASK_UNINTERRUPTIBLE);
+ set_bit(B_DIRTY, &b->state);
+ __unlink_buffer(b);
+ __link_buffer(b, new_block, LIST_DIRTY);
+ } else {
+ sector_t old_block;
+ wait_on_bit_lock(&b->state, B_WRITING,
+ do_io_schedule, TASK_UNINTERRUPTIBLE);
+ /*
+ * Relink buffer to "new_block" so that write_callback
+ * sees "new_block" as a block number.
+ * After the write, link the buffer back to old_block.
+ * All this must be done in bufio lock, so that block number
+ * change isn't visible to other threads.
+ */
+ old_block = b->block;
+ __unlink_buffer(b);
+ __link_buffer(b, new_block, b->list_mode);
+ submit_io(b, WRITE, new_block, write_endio);
+ wait_on_bit(&b->state, B_WRITING,
+ do_io_schedule, TASK_UNINTERRUPTIBLE);
+ __unlink_buffer(b);
+ __link_buffer(b, old_block, b->list_mode);
+ }
+
+ dm_bufio_unlock(c);
+ dm_bufio_release(b);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_release_move);
+
+unsigned dm_bufio_get_block_size(struct dm_bufio_client *c)
+{
+ return c->block_size;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_block_size);
+
+sector_t dm_bufio_get_device_size(struct dm_bufio_client *c)
+{
+ return i_size_read(c->bdev->bd_inode) >>
+ (SECTOR_SHIFT + c->sectors_per_block_bits);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_device_size);
+
+sector_t dm_bufio_get_block_number(struct dm_buffer *b)
+{
+ return b->block;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_block_number);
+
+void *dm_bufio_get_block_data(struct dm_buffer *b)
+{
+ return b->data;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_block_data);
+
+void *dm_bufio_get_aux_data(struct dm_buffer *b)
+{
+ return b + 1;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_aux_data);
+
+struct dm_bufio_client *dm_bufio_get_client(struct dm_buffer *b)
+{
+ return b->c;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_client);
+
+static void drop_buffers(struct dm_bufio_client *c)
+{
+ struct dm_buffer *b;
+ int i;
+
+ BUG_ON(dm_bufio_in_request());
+
+ /*
+ * An optimization so that the buffers are not written one-by-one.
+ */
+ dm_bufio_write_dirty_buffers_async(c);
+
+ dm_bufio_lock(c);
+
+ while ((b = __get_unclaimed_buffer(c)))
+ __free_buffer_wake(b);
+
+ for (i = 0; i < LIST_SIZE; i++)
+ list_for_each_entry(b, &c->lru[i], lru_list)
+ DMERR("leaked buffer %llx, hold count %u, list %d",
+ (unsigned long long)b->block, b->hold_count, i);
+
+ for (i = 0; i < LIST_SIZE; i++)
+ BUG_ON(!list_empty(&c->lru[i]));
+
+ dm_bufio_unlock(c);
+}
+
+/*
+ * Test if the buffer is unused and too old, and commit it.
+ * At if noio is set, we must not do any I/O because we hold
+ * dm_bufio_clients_lock and we would risk deadlock if the I/O gets rerouted to
+ * different bufio client.
+ */
+static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
+ unsigned long max_jiffies)
+{
+ if (jiffies - b->last_accessed < max_jiffies)
+ return 1;
+
+ if (!(gfp & __GFP_IO)) {
+ if (test_bit(B_READING, &b->state) ||
+ test_bit(B_WRITING, &b->state) ||
+ test_bit(B_DIRTY, &b->state))
+ return 1;
+ }
+
+ if (b->hold_count)
+ return 1;
+
+ __make_buffer_clean(b);
+ __unlink_buffer(b);
+ __free_buffer_wake(b);
+
+ return 0;
+}
+
+static void __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
+ struct shrink_control *sc)
+{
+ int l;
+ struct dm_buffer *b, *tmp;
+
+ for (l = 0; l < LIST_SIZE; l++) {
+ list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list)
+ if (!__cleanup_old_buffer(b, sc->gfp_mask, 0) &&
+ !--nr_to_scan)
+ return;
+ dm_bufio_cond_resched();
+ }
+}
+
+static int shrink(struct shrinker *shrinker, struct shrink_control *sc)
+{
+ struct dm_bufio_client *c =
+ container_of(shrinker, struct dm_bufio_client, shrinker);
+ unsigned long r;
+ unsigned long nr_to_scan = sc->nr_to_scan;
+
+ if (sc->gfp_mask & __GFP_IO)
+ dm_bufio_lock(c);
+ else if (!dm_bufio_trylock(c))
+ return !nr_to_scan ? 0 : -1;
+
+ if (nr_to_scan)
+ __scan(c, nr_to_scan, sc);
+
+ r = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY];
+ if (r > INT_MAX)
+ r = INT_MAX;
+
+ dm_bufio_unlock(c);
+
+ return r;
+}
+
+/*
+ * Create the buffering interface
+ */
+struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsigned block_size,
+ unsigned reserved_buffers, unsigned aux_size,
+ void (*alloc_callback)(struct dm_buffer *),
+ void (*write_callback)(struct dm_buffer *))
+{
+ int r;
+ struct dm_bufio_client *c;
+ unsigned i;
+
+ BUG_ON(block_size < 1 << SECTOR_SHIFT ||
+ (block_size & (block_size - 1)));
+
+ c = kmalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ r = -ENOMEM;
+ goto bad_client;
+ }
+ c->cache_hash = vmalloc(sizeof(struct hlist_head) << DM_BUFIO_HASH_BITS);
+ if (!c->cache_hash) {
+ r = -ENOMEM;
+ goto bad_hash;
+ }
+
+ c->bdev = bdev;
+ c->block_size = block_size;
+ c->sectors_per_block_bits = ffs(block_size) - 1 - SECTOR_SHIFT;
+ c->pages_per_block_bits = (ffs(block_size) - 1 >= PAGE_SHIFT) ?
+ ffs(block_size) - 1 - PAGE_SHIFT : 0;
+ c->blocks_per_page_bits = (ffs(block_size) - 1 < PAGE_SHIFT ?
+ PAGE_SHIFT - (ffs(block_size) - 1) : 0);
+
+ c->aux_size = aux_size;
+ c->alloc_callback = alloc_callback;
+ c->write_callback = write_callback;
+
+ for (i = 0; i < LIST_SIZE; i++) {
+ INIT_LIST_HEAD(&c->lru[i]);
+ c->n_buffers[i] = 0;
+ }
+
+ for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++)
+ INIT_HLIST_HEAD(&c->cache_hash[i]);
+
+ mutex_init(&c->lock);
+ INIT_LIST_HEAD(&c->reserved_buffers);
+ c->need_reserved_buffers = reserved_buffers;
+
+ init_waitqueue_head(&c->free_buffer_wait);
+ c->async_write_error = 0;
+
+ c->dm_io = dm_io_client_create();
+ if (IS_ERR(c->dm_io)) {
+ r = PTR_ERR(c->dm_io);
+ goto bad_dm_io;
+ }
+
+ mutex_lock(&dm_bufio_clients_lock);
+ if (c->blocks_per_page_bits) {
+ if (!DM_BUFIO_CACHE_NAME(c)) {
+ DM_BUFIO_CACHE_NAME(c) = kasprintf(GFP_KERNEL, "dm_bufio_cache-%u", c->block_size);
+ if (!DM_BUFIO_CACHE_NAME(c)) {
+ r = -ENOMEM;
+ mutex_unlock(&dm_bufio_clients_lock);
+ goto bad_cache;
+ }
+ }
+
+ if (!DM_BUFIO_CACHE(c)) {
+ DM_BUFIO_CACHE(c) = kmem_cache_create(DM_BUFIO_CACHE_NAME(c),
+ c->block_size,
+ c->block_size, 0, NULL);
+ if (!DM_BUFIO_CACHE(c)) {
+ r = -ENOMEM;
+ mutex_unlock(&dm_bufio_clients_lock);
+ goto bad_cache;
+ }
+ }
+ }
+ mutex_unlock(&dm_bufio_clients_lock);
+
+ while (c->need_reserved_buffers) {
+ struct dm_buffer *b = alloc_buffer(c, GFP_KERNEL);
+
+ if (!b) {
+ r = -ENOMEM;
+ goto bad_buffer;
+ }
+ __free_buffer_wake(b);
+ }
+
+ mutex_lock(&dm_bufio_clients_lock);
+ dm_bufio_client_count++;
+ list_add(&c->client_list, &dm_bufio_all_clients);
+ __cache_size_refresh();
+ mutex_unlock(&dm_bufio_clients_lock);
+
+ c->shrinker.shrink = shrink;
+ c->shrinker.seeks = 1;
+ c->shrinker.batch = 0;
+ register_shrinker(&c->shrinker);
+
+ return c;
+
+bad_buffer:
+bad_cache:
+ while (!list_empty(&c->reserved_buffers)) {
+ struct dm_buffer *b = list_entry(c->reserved_buffers.next,
+ struct dm_buffer, lru_list);
+ list_del(&b->lru_list);
+ free_buffer(b);
+ }
+ dm_io_client_destroy(c->dm_io);
+bad_dm_io:
+ vfree(c->cache_hash);
+bad_hash:
+ kfree(c);
+bad_client:
+ return ERR_PTR(r);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_client_create);
+
+/*
+ * Free the buffering interface.
+ * It is required that there are no references on any buffers.
+ */
+void dm_bufio_client_destroy(struct dm_bufio_client *c)
+{
+ unsigned i;
+
+ drop_buffers(c);
+
+ unregister_shrinker(&c->shrinker);
+
+ mutex_lock(&dm_bufio_clients_lock);
+
+ list_del(&c->client_list);
+ dm_bufio_client_count--;
+ __cache_size_refresh();
+
+ mutex_unlock(&dm_bufio_clients_lock);
+
+ for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++)
+ BUG_ON(!hlist_empty(&c->cache_hash[i]));
+
+ BUG_ON(c->need_reserved_buffers);
+
+ while (!list_empty(&c->reserved_buffers)) {
+ struct dm_buffer *b = list_entry(c->reserved_buffers.next,
+ struct dm_buffer, lru_list);
+ list_del(&b->lru_list);
+ free_buffer(b);
+ }
+
+ for (i = 0; i < LIST_SIZE; i++)
+ if (c->n_buffers[i])
+ DMERR("leaked buffer count %d: %ld", i, c->n_buffers[i]);
+
+ for (i = 0; i < LIST_SIZE; i++)
+ BUG_ON(c->n_buffers[i]);
+
+ dm_io_client_destroy(c->dm_io);
+ vfree(c->cache_hash);
+ kfree(c);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_client_destroy);
+
+static void cleanup_old_buffers(void)
+{
+ unsigned long max_age = dm_bufio_max_age;
+ struct dm_bufio_client *c;
+
+ barrier();
+
+ if (max_age > ULONG_MAX / HZ)
+ max_age = ULONG_MAX / HZ;
+
+ mutex_lock(&dm_bufio_clients_lock);
+ list_for_each_entry(c, &dm_bufio_all_clients, client_list) {
+ if (!dm_bufio_trylock(c))
+ continue;
+
+ while (!list_empty(&c->lru[LIST_CLEAN])) {
+ struct dm_buffer *b;
+ b = list_entry(c->lru[LIST_CLEAN].prev,
+ struct dm_buffer, lru_list);
+ if (__cleanup_old_buffer(b, 0, max_age * HZ))
+ break;
+ dm_bufio_cond_resched();
+ }
+
+ dm_bufio_unlock(c);
+ dm_bufio_cond_resched();
+ }
+ mutex_unlock(&dm_bufio_clients_lock);
+}
+
+static struct workqueue_struct *dm_bufio_wq;
+static struct delayed_work dm_bufio_work;
+
+static void work_fn(struct work_struct *w)
+{
+ cleanup_old_buffers();
+
+ queue_delayed_work(dm_bufio_wq, &dm_bufio_work,
+ DM_BUFIO_WORK_TIMER_SECS * HZ);
+}
+
+/*----------------------------------------------------------------
+ * Module setup
+ *--------------------------------------------------------------*/
+
+/*
+ * This is called only once for the whole dm_bufio module.
+ * It initializes memory limit.
+ */
+static int __init dm_bufio_init(void)
+{
+ __u64 mem;
+
+ memset(&dm_bufio_caches, 0, sizeof dm_bufio_caches);
+ memset(&dm_bufio_cache_names, 0, sizeof dm_bufio_cache_names);
+
+ mem = (__u64)((totalram_pages - totalhigh_pages) *
+ DM_BUFIO_MEMORY_PERCENT / 100) << PAGE_SHIFT;
+
+ if (mem > ULONG_MAX)
+ mem = ULONG_MAX;
+
+#ifdef CONFIG_MMU
+ /*
+ * Get the size of vmalloc space the same way as VMALLOC_TOTAL
+ * in fs/proc/internal.h
+ */
+ if (mem > (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100)
+ mem = (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100;
+#endif
+
+ dm_bufio_default_cache_size = mem;
+
+ mutex_lock(&dm_bufio_clients_lock);
+ __cache_size_refresh();
+ mutex_unlock(&dm_bufio_clients_lock);
+
+ dm_bufio_wq = create_singlethread_workqueue("dm_bufio_cache");
+ if (!dm_bufio_wq)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&dm_bufio_work, work_fn);
+ queue_delayed_work(dm_bufio_wq, &dm_bufio_work,
+ DM_BUFIO_WORK_TIMER_SECS * HZ);
+
+ return 0;
+}
+
+/*
+ * This is called once when unloading the dm_bufio module.
+ */
+static void __exit dm_bufio_exit(void)
+{
+ int bug = 0;
+ int i;
+
+ cancel_delayed_work_sync(&dm_bufio_work);
+ destroy_workqueue(dm_bufio_wq);
+
+ for (i = 0; i < ARRAY_SIZE(dm_bufio_caches); i++) {
+ struct kmem_cache *kc = dm_bufio_caches[i];
+
+ if (kc)
+ kmem_cache_destroy(kc);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dm_bufio_cache_names); i++)
+ kfree(dm_bufio_cache_names[i]);
+
+ if (dm_bufio_client_count) {
+ DMCRIT("%s: dm_bufio_client_count leaked: %d",
+ __func__, dm_bufio_client_count);
+ bug = 1;
+ }
+
+ if (dm_bufio_current_allocated) {
+ DMCRIT("%s: dm_bufio_current_allocated leaked: %lu",
+ __func__, dm_bufio_current_allocated);
+ bug = 1;
+ }
+
+ if (dm_bufio_allocated_get_free_pages) {
+ DMCRIT("%s: dm_bufio_allocated_get_free_pages leaked: %lu",
+ __func__, dm_bufio_allocated_get_free_pages);
+ bug = 1;
+ }
+
+ if (dm_bufio_allocated_vmalloc) {
+ DMCRIT("%s: dm_bufio_vmalloc leaked: %lu",
+ __func__, dm_bufio_allocated_vmalloc);
+ bug = 1;
+ }
+
+ if (bug)
+ BUG();
+}
+
+module_init(dm_bufio_init)
+module_exit(dm_bufio_exit)
+
+module_param_named(max_cache_size_bytes, dm_bufio_cache_size, ulong, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_cache_size_bytes, "Size of metadata cache");
+
+module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds");
+
+module_param_named(peak_allocated_bytes, dm_bufio_peak_allocated, ulong, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(peak_allocated_bytes, "Tracks the maximum allocated memory");
+
+module_param_named(allocated_kmem_cache_bytes, dm_bufio_allocated_kmem_cache, ulong, S_IRUGO);
+MODULE_PARM_DESC(allocated_kmem_cache_bytes, "Memory allocated with kmem_cache_alloc");
+
+module_param_named(allocated_get_free_pages_bytes, dm_bufio_allocated_get_free_pages, ulong, S_IRUGO);
+MODULE_PARM_DESC(allocated_get_free_pages_bytes, "Memory allocated with get_free_pages");
+
+module_param_named(allocated_vmalloc_bytes, dm_bufio_allocated_vmalloc, ulong, S_IRUGO);
+MODULE_PARM_DESC(allocated_vmalloc_bytes, "Memory allocated with vmalloc");
+
+module_param_named(current_allocated_bytes, dm_bufio_current_allocated, ulong, S_IRUGO);
+MODULE_PARM_DESC(current_allocated_bytes, "Memory currently used by the cache");
+
+MODULE_AUTHOR("Mikulas Patocka <dm-devel@redhat.com>");
+MODULE_DESCRIPTION(DM_NAME " buffered I/O library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-bufio.h b/drivers/md/dm-bufio.h
new file mode 100644
index 000000000000..5c4c3a04e381
--- /dev/null
+++ b/drivers/md/dm-bufio.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2009-2011 Red Hat, Inc.
+ *
+ * Author: Mikulas Patocka <mpatocka@redhat.com>
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_BUFIO_H
+#define DM_BUFIO_H
+
+#include <linux/blkdev.h>
+#include <linux/types.h>
+
+/*----------------------------------------------------------------*/
+
+struct dm_bufio_client;
+struct dm_buffer;
+
+/*
+ * Create a buffered IO cache on a given device
+ */
+struct dm_bufio_client *
+dm_bufio_client_create(struct block_device *bdev, unsigned block_size,
+ unsigned reserved_buffers, unsigned aux_size,
+ void (*alloc_callback)(struct dm_buffer *),
+ void (*write_callback)(struct dm_buffer *));
+
+/*
+ * Release a buffered IO cache.
+ */
+void dm_bufio_client_destroy(struct dm_bufio_client *c);
+
+/*
+ * WARNING: to avoid deadlocks, these conditions are observed:
+ *
+ * - At most one thread can hold at most "reserved_buffers" simultaneously.
+ * - Each other threads can hold at most one buffer.
+ * - Threads which call only dm_bufio_get can hold unlimited number of
+ * buffers.
+ */
+
+/*
+ * Read a given block from disk. Returns pointer to data. Returns a
+ * pointer to dm_buffer that can be used to release the buffer or to make
+ * it dirty.
+ */
+void *dm_bufio_read(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp);
+
+/*
+ * Like dm_bufio_read, but return buffer from cache, don't read
+ * it. If the buffer is not in the cache, return NULL.
+ */
+void *dm_bufio_get(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp);
+
+/*
+ * Like dm_bufio_read, but don't read anything from the disk. It is
+ * expected that the caller initializes the buffer and marks it dirty.
+ */
+void *dm_bufio_new(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp);
+
+/*
+ * Release a reference obtained with dm_bufio_{read,get,new}. The data
+ * pointer and dm_buffer pointer is no longer valid after this call.
+ */
+void dm_bufio_release(struct dm_buffer *b);
+
+/*
+ * Mark a buffer dirty. It should be called after the buffer is modified.
+ *
+ * In case of memory pressure, the buffer may be written after
+ * dm_bufio_mark_buffer_dirty, but before dm_bufio_write_dirty_buffers. So
+ * dm_bufio_write_dirty_buffers guarantees that the buffer is on-disk but
+ * the actual writing may occur earlier.
+ */
+void dm_bufio_mark_buffer_dirty(struct dm_buffer *b);
+
+/*
+ * Initiate writing of dirty buffers, without waiting for completion.
+ */
+void dm_bufio_write_dirty_buffers_async(struct dm_bufio_client *c);
+
+/*
+ * Write all dirty buffers. Guarantees that all dirty buffers created prior
+ * to this call are on disk when this call exits.
+ */
+int dm_bufio_write_dirty_buffers(struct dm_bufio_client *c);
+
+/*
+ * Send an empty write barrier to the device to flush hardware disk cache.
+ */
+int dm_bufio_issue_flush(struct dm_bufio_client *c);
+
+/*
+ * Like dm_bufio_release but also move the buffer to the new
+ * block. dm_bufio_write_dirty_buffers is needed to commit the new block.
+ */
+void dm_bufio_release_move(struct dm_buffer *b, sector_t new_block);
+
+unsigned dm_bufio_get_block_size(struct dm_bufio_client *c);
+sector_t dm_bufio_get_device_size(struct dm_bufio_client *c);
+sector_t dm_bufio_get_block_number(struct dm_buffer *b);
+void *dm_bufio_get_block_data(struct dm_buffer *b);
+void *dm_bufio_get_aux_data(struct dm_buffer *b);
+struct dm_bufio_client *dm_bufio_get_client(struct dm_buffer *b);
+
+/*----------------------------------------------------------------*/
+
+#endif
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index c8827ffd85bb..8c2a000cf3f5 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -19,7 +19,7 @@
#include <linux/workqueue.h>
#include <linux/backing-dev.h>
#include <linux/percpu.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/scatterlist.h>
#include <asm/page.h>
#include <asm/unaligned.h>
@@ -30,7 +30,6 @@
#include <linux/device-mapper.h>
#define DM_MSG_PREFIX "crypt"
-#define MESG_STR(x) x, sizeof(x)
/*
* context holding the current state of a multi-part conversion
@@ -239,7 +238,7 @@ static int crypt_iv_plain_gen(struct crypt_config *cc, u8 *iv,
struct dm_crypt_request *dmreq)
{
memset(iv, 0, cc->iv_size);
- *(u32 *)iv = cpu_to_le32(dmreq->iv_sector & 0xffffffff);
+ *(__le32 *)iv = cpu_to_le32(dmreq->iv_sector & 0xffffffff);
return 0;
}
@@ -248,7 +247,7 @@ static int crypt_iv_plain64_gen(struct crypt_config *cc, u8 *iv,
struct dm_crypt_request *dmreq)
{
memset(iv, 0, cc->iv_size);
- *(u64 *)iv = cpu_to_le64(dmreq->iv_sector);
+ *(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
return 0;
}
@@ -415,7 +414,7 @@ static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv,
struct crypto_cipher *essiv_tfm = this_crypt_config(cc)->iv_private;
memset(iv, 0, cc->iv_size);
- *(u64 *)iv = cpu_to_le64(dmreq->iv_sector);
+ *(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
crypto_cipher_encrypt_one(essiv_tfm, iv, iv);
return 0;
@@ -1575,11 +1574,17 @@ bad_mem:
static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
struct crypt_config *cc;
- unsigned int key_size;
+ unsigned int key_size, opt_params;
unsigned long long tmpll;
int ret;
+ struct dm_arg_set as;
+ const char *opt_string;
+
+ static struct dm_arg _args[] = {
+ {0, 1, "Invalid number of feature args"},
+ };
- if (argc != 5) {
+ if (argc < 5) {
ti->error = "Not enough arguments";
return -EINVAL;
}
@@ -1648,6 +1653,30 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
cc->start = tmpll;
+ argv += 5;
+ argc -= 5;
+
+ /* Optional parameters */
+ if (argc) {
+ as.argc = argc;
+ as.argv = argv;
+
+ ret = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
+ if (ret)
+ goto bad;
+
+ opt_string = dm_shift_arg(&as);
+
+ if (opt_params == 1 && opt_string &&
+ !strcasecmp(opt_string, "allow_discards"))
+ ti->num_discard_requests = 1;
+ else if (opt_params) {
+ ret = -EINVAL;
+ ti->error = "Invalid feature arguments";
+ goto bad;
+ }
+ }
+
ret = -ENOMEM;
cc->io_queue = alloc_workqueue("kcryptd_io",
WQ_NON_REENTRANT|
@@ -1669,6 +1698,8 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
ti->num_flush_requests = 1;
+ ti->discard_zeroes_data_unsupported = 1;
+
return 0;
bad:
@@ -1682,9 +1713,16 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
struct dm_crypt_io *io;
struct crypt_config *cc;
- if (bio->bi_rw & REQ_FLUSH) {
+ /*
+ * If bio is REQ_FLUSH or REQ_DISCARD, just bypass crypt queues.
+ * - for REQ_FLUSH device-mapper core ensures that no IO is in-flight
+ * - for REQ_DISCARD caller must use flush if IO ordering matters
+ */
+ if (unlikely(bio->bi_rw & (REQ_FLUSH | REQ_DISCARD))) {
cc = ti->private;
bio->bi_bdev = cc->dev->bdev;
+ if (bio_sectors(bio))
+ bio->bi_sector = cc->start + dm_target_offset(ti, bio->bi_sector);
return DM_MAPIO_REMAPPED;
}
@@ -1727,6 +1765,10 @@ static int crypt_status(struct dm_target *ti, status_type_t type,
DMEMIT(" %llu %s %llu", (unsigned long long)cc->iv_offset,
cc->dev->name, (unsigned long long)cc->start);
+
+ if (ti->num_discard_requests)
+ DMEMIT(" 1 allow_discards");
+
break;
}
return 0;
@@ -1770,12 +1812,12 @@ static int crypt_message(struct dm_target *ti, unsigned argc, char **argv)
if (argc < 2)
goto error;
- if (!strnicmp(argv[0], MESG_STR("key"))) {
+ if (!strcasecmp(argv[0], "key")) {
if (!test_bit(DM_CRYPT_SUSPENDED, &cc->flags)) {
DMWARN("not suspended during key manipulation.");
return -EINVAL;
}
- if (argc == 3 && !strnicmp(argv[1], MESG_STR("set"))) {
+ if (argc == 3 && !strcasecmp(argv[1], "set")) {
ret = crypt_set_key(cc, argv[2]);
if (ret)
return ret;
@@ -1783,7 +1825,7 @@ static int crypt_message(struct dm_target *ti, unsigned argc, char **argv)
ret = cc->iv_gen_ops->init(cc);
return ret;
}
- if (argc == 2 && !strnicmp(argv[1], MESG_STR("wipe"))) {
+ if (argc == 2 && !strcasecmp(argv[1], "wipe")) {
if (cc->iv_gen_ops && cc->iv_gen_ops->wipe) {
ret = cc->iv_gen_ops->wipe(cc);
if (ret)
@@ -1823,7 +1865,7 @@ static int crypt_iterate_devices(struct dm_target *ti,
static struct target_type crypt_target = {
.name = "crypt",
- .version = {1, 10, 0},
+ .version = {1, 11, 0},
.module = THIS_MODULE,
.ctr = crypt_ctr,
.dtr = crypt_dtr,
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c
index 0bdb201c2c2a..042e71996569 100644
--- a/drivers/md/dm-exception-store.c
+++ b/drivers/md/dm-exception-store.c
@@ -11,6 +11,7 @@
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
+#include <linux/module.h>
#include <linux/slab.h>
#define DM_MSG_PREFIX "snapshot exception stores"
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index ea790623c30b..f84c08029b21 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2003 Sistina Software (UK) Limited.
- * Copyright (C) 2004, 2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
@@ -15,6 +15,9 @@
#define DM_MSG_PREFIX "flakey"
+#define all_corrupt_bio_flags_match(bio, fc) \
+ (((bio)->bi_rw & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
+
/*
* Flakey: Used for testing only, simulates intermittent,
* catastrophic device failure.
@@ -25,60 +28,191 @@ struct flakey_c {
sector_t start;
unsigned up_interval;
unsigned down_interval;
+ unsigned long flags;
+ unsigned corrupt_bio_byte;
+ unsigned corrupt_bio_rw;
+ unsigned corrupt_bio_value;
+ unsigned corrupt_bio_flags;
+};
+
+enum feature_flag_bits {
+ DROP_WRITES
};
+static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
+ struct dm_target *ti)
+{
+ int r;
+ unsigned argc;
+ const char *arg_name;
+
+ static struct dm_arg _args[] = {
+ {0, 6, "Invalid number of feature args"},
+ {1, UINT_MAX, "Invalid corrupt bio byte"},
+ {0, 255, "Invalid corrupt value to write into bio byte (0-255)"},
+ {0, UINT_MAX, "Invalid corrupt bio flags mask"},
+ };
+
+ /* No feature arguments supplied. */
+ if (!as->argc)
+ return 0;
+
+ r = dm_read_arg_group(_args, as, &argc, &ti->error);
+ if (r)
+ return r;
+
+ while (argc) {
+ arg_name = dm_shift_arg(as);
+ argc--;
+
+ /*
+ * drop_writes
+ */
+ if (!strcasecmp(arg_name, "drop_writes")) {
+ if (test_and_set_bit(DROP_WRITES, &fc->flags)) {
+ ti->error = "Feature drop_writes duplicated";
+ return -EINVAL;
+ }
+
+ continue;
+ }
+
+ /*
+ * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
+ */
+ if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
+ if (!argc) {
+ ti->error = "Feature corrupt_bio_byte requires parameters";
+ return -EINVAL;
+ }
+
+ r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error);
+ if (r)
+ return r;
+ argc--;
+
+ /*
+ * Direction r or w?
+ */
+ arg_name = dm_shift_arg(as);
+ if (!strcasecmp(arg_name, "w"))
+ fc->corrupt_bio_rw = WRITE;
+ else if (!strcasecmp(arg_name, "r"))
+ fc->corrupt_bio_rw = READ;
+ else {
+ ti->error = "Invalid corrupt bio direction (r or w)";
+ return -EINVAL;
+ }
+ argc--;
+
+ /*
+ * Value of byte (0-255) to write in place of correct one.
+ */
+ r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error);
+ if (r)
+ return r;
+ argc--;
+
+ /*
+ * Only corrupt bios with these flags set.
+ */
+ r = dm_read_arg(_args + 3, as, &fc->corrupt_bio_flags, &ti->error);
+ if (r)
+ return r;
+ argc--;
+
+ continue;
+ }
+
+ ti->error = "Unrecognised flakey feature requested";
+ return -EINVAL;
+ }
+
+ if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
+ ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/*
- * Construct a flakey mapping: <dev_path> <offset> <up interval> <down interval>
+ * Construct a flakey mapping:
+ * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
+ *
+ * Feature args:
+ * [drop_writes]
+ * [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>]
+ *
+ * Nth_byte starts from 1 for the first byte.
+ * Direction is r for READ or w for WRITE.
+ * bio_flags is ignored if 0.
*/
static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
+ static struct dm_arg _args[] = {
+ {0, UINT_MAX, "Invalid up interval"},
+ {0, UINT_MAX, "Invalid down interval"},
+ };
+
+ int r;
struct flakey_c *fc;
- unsigned long long tmp;
+ unsigned long long tmpll;
+ struct dm_arg_set as;
+ const char *devname;
- if (argc != 4) {
- ti->error = "dm-flakey: Invalid argument count";
+ as.argc = argc;
+ as.argv = argv;
+
+ if (argc < 4) {
+ ti->error = "Invalid argument count";
return -EINVAL;
}
- fc = kmalloc(sizeof(*fc), GFP_KERNEL);
+ fc = kzalloc(sizeof(*fc), GFP_KERNEL);
if (!fc) {
- ti->error = "dm-flakey: Cannot allocate linear context";
+ ti->error = "Cannot allocate linear context";
return -ENOMEM;
}
fc->start_time = jiffies;
- if (sscanf(argv[1], "%llu", &tmp) != 1) {
- ti->error = "dm-flakey: Invalid device sector";
+ devname = dm_shift_arg(&as);
+
+ if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) {
+ ti->error = "Invalid device sector";
goto bad;
}
- fc->start = tmp;
+ fc->start = tmpll;
- if (sscanf(argv[2], "%u", &fc->up_interval) != 1) {
- ti->error = "dm-flakey: Invalid up interval";
+ r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
+ if (r)
goto bad;
- }
- if (sscanf(argv[3], "%u", &fc->down_interval) != 1) {
- ti->error = "dm-flakey: Invalid down interval";
+ r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
+ if (r)
goto bad;
- }
if (!(fc->up_interval + fc->down_interval)) {
- ti->error = "dm-flakey: Total (up + down) interval is zero";
+ ti->error = "Total (up + down) interval is zero";
goto bad;
}
if (fc->up_interval + fc->down_interval < fc->up_interval) {
- ti->error = "dm-flakey: Interval overflow";
+ ti->error = "Interval overflow";
goto bad;
}
- if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &fc->dev)) {
- ti->error = "dm-flakey: Device lookup failed";
+ r = parse_features(&as, fc, ti);
+ if (r)
+ goto bad;
+
+ if (dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev)) {
+ ti->error = "Device lookup failed";
goto bad;
}
ti->num_flush_requests = 1;
+ ti->num_discard_requests = 1;
ti->private = fc;
return 0;
@@ -99,7 +233,7 @@ static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)
{
struct flakey_c *fc = ti->private;
- return fc->start + (bi_sector - ti->begin);
+ return fc->start + dm_target_offset(ti, bi_sector);
}
static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
@@ -111,6 +245,25 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
bio->bi_sector = flakey_map_sector(ti, bio->bi_sector);
}
+static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
+{
+ unsigned bio_bytes = bio_cur_bytes(bio);
+ char *data = bio_data(bio);
+
+ /*
+ * Overwrite the Nth byte of the data returned.
+ */
+ if (data && bio_bytes >= fc->corrupt_bio_byte) {
+ data[fc->corrupt_bio_byte - 1] = fc->corrupt_bio_value;
+
+ DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
+ "(rw=%c bi_rw=%lu bi_sector=%llu cur_bytes=%u)\n",
+ bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
+ (bio_data_dir(bio) == WRITE) ? 'w' : 'r',
+ bio->bi_rw, (unsigned long long)bio->bi_sector, bio_bytes);
+ }
+}
+
static int flakey_map(struct dm_target *ti, struct bio *bio,
union map_info *map_context)
{
@@ -119,18 +272,71 @@ static int flakey_map(struct dm_target *ti, struct bio *bio,
/* Are we alive ? */
elapsed = (jiffies - fc->start_time) / HZ;
- if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval)
+ if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
+ /*
+ * Flag this bio as submitted while down.
+ */
+ map_context->ll = 1;
+
+ /*
+ * Map reads as normal.
+ */
+ if (bio_data_dir(bio) == READ)
+ goto map_bio;
+
+ /*
+ * Drop writes?
+ */
+ if (test_bit(DROP_WRITES, &fc->flags)) {
+ bio_endio(bio, 0);
+ return DM_MAPIO_SUBMITTED;
+ }
+
+ /*
+ * Corrupt matching writes.
+ */
+ if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == WRITE)) {
+ if (all_corrupt_bio_flags_match(bio, fc))
+ corrupt_bio_data(bio, fc);
+ goto map_bio;
+ }
+
+ /*
+ * By default, error all I/O.
+ */
return -EIO;
+ }
+map_bio:
flakey_map_bio(ti, bio);
return DM_MAPIO_REMAPPED;
}
+static int flakey_end_io(struct dm_target *ti, struct bio *bio,
+ int error, union map_info *map_context)
+{
+ struct flakey_c *fc = ti->private;
+ unsigned bio_submitted_while_down = map_context->ll;
+
+ /*
+ * Corrupt successful READs while in down state.
+ * If flags were specified, only corrupt those that match.
+ */
+ if (!error && bio_submitted_while_down &&
+ (bio_data_dir(bio) == READ) && (fc->corrupt_bio_rw == READ) &&
+ all_corrupt_bio_flags_match(bio, fc))
+ corrupt_bio_data(bio, fc);
+
+ return error;
+}
+
static int flakey_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen)
{
+ unsigned sz = 0;
struct flakey_c *fc = ti->private;
+ unsigned drop_writes;
switch (type) {
case STATUSTYPE_INFO:
@@ -138,9 +344,22 @@ static int flakey_status(struct dm_target *ti, status_type_t type,
break;
case STATUSTYPE_TABLE:
- snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name,
- (unsigned long long)fc->start, fc->up_interval,
- fc->down_interval);
+ DMEMIT("%s %llu %u %u ", fc->dev->name,
+ (unsigned long long)fc->start, fc->up_interval,
+ fc->down_interval);
+
+ drop_writes = test_bit(DROP_WRITES, &fc->flags);
+ DMEMIT("%u ", drop_writes + (fc->corrupt_bio_byte > 0) * 5);
+
+ if (drop_writes)
+ DMEMIT("drop_writes ");
+
+ if (fc->corrupt_bio_byte)
+ DMEMIT("corrupt_bio_byte %u %c %u %u ",
+ fc->corrupt_bio_byte,
+ (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
+ fc->corrupt_bio_value, fc->corrupt_bio_flags);
+
break;
}
return 0;
@@ -177,11 +396,12 @@ static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_
static struct target_type flakey_target = {
.name = "flakey",
- .version = {1, 1, 0},
+ .version = {1, 2, 0},
.module = THIS_MODULE,
.ctr = flakey_ctr,
.dtr = flakey_dtr,
.map = flakey_map,
+ .end_io = flakey_end_io,
.status = flakey_status,
.ioctl = flakey_ioctl,
.merge = flakey_merge,
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 2067288f61f9..ad2eba40e319 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -38,6 +38,8 @@ struct io {
struct dm_io_client *client;
io_notify_fn callback;
void *context;
+ void *vma_invalidate_address;
+ unsigned long vma_invalidate_size;
} __attribute__((aligned(DM_IO_MAX_REGIONS)));
static struct kmem_cache *_dm_io_cache;
@@ -116,6 +118,10 @@ static void dec_count(struct io *io, unsigned int region, int error)
set_bit(region, &io->error_bits);
if (atomic_dec_and_test(&io->count)) {
+ if (io->vma_invalidate_size)
+ invalidate_kernel_vmap_range(io->vma_invalidate_address,
+ io->vma_invalidate_size);
+
if (io->sleeper)
wake_up_process(io->sleeper);
@@ -159,6 +165,9 @@ struct dpages {
unsigned context_u;
void *context_ptr;
+
+ void *vma_invalidate_address;
+ unsigned long vma_invalidate_size;
};
/*
@@ -377,6 +386,9 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
io->sleeper = current;
io->client = client;
+ io->vma_invalidate_address = dp->vma_invalidate_address;
+ io->vma_invalidate_size = dp->vma_invalidate_size;
+
dispatch_io(rw, num_regions, where, dp, io, 1);
while (1) {
@@ -415,13 +427,21 @@ static int async_io(struct dm_io_client *client, unsigned int num_regions,
io->callback = fn;
io->context = context;
+ io->vma_invalidate_address = dp->vma_invalidate_address;
+ io->vma_invalidate_size = dp->vma_invalidate_size;
+
dispatch_io(rw, num_regions, where, dp, io, 0);
return 0;
}
-static int dp_init(struct dm_io_request *io_req, struct dpages *dp)
+static int dp_init(struct dm_io_request *io_req, struct dpages *dp,
+ unsigned long size)
{
/* Set up dpages based on memory type */
+
+ dp->vma_invalidate_address = NULL;
+ dp->vma_invalidate_size = 0;
+
switch (io_req->mem.type) {
case DM_IO_PAGE_LIST:
list_dp_init(dp, io_req->mem.ptr.pl, io_req->mem.offset);
@@ -432,6 +452,11 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp)
break;
case DM_IO_VMA:
+ flush_kernel_vmap_range(io_req->mem.ptr.vma, size);
+ if ((io_req->bi_rw & RW_MASK) == READ) {
+ dp->vma_invalidate_address = io_req->mem.ptr.vma;
+ dp->vma_invalidate_size = size;
+ }
vm_dp_init(dp, io_req->mem.ptr.vma);
break;
@@ -460,7 +485,7 @@ int dm_io(struct dm_io_request *io_req, unsigned num_regions,
int r;
struct dpages dp;
- r = dp_init(io_req, &dp);
+ r = dp_init(io_req, &dp, (unsigned long)where->count << SECTOR_SHIFT);
if (r)
return r;
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 4cacdad2270a..31c2dc25886d 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -128,6 +128,24 @@ static struct hash_cell *__get_uuid_cell(const char *str)
return NULL;
}
+static struct hash_cell *__get_dev_cell(uint64_t dev)
+{
+ struct mapped_device *md;
+ struct hash_cell *hc;
+
+ md = dm_get_md(huge_decode_dev(dev));
+ if (!md)
+ return NULL;
+
+ hc = dm_get_mdptr(md);
+ if (!hc) {
+ dm_put(md);
+ return NULL;
+ }
+
+ return hc;
+}
+
/*-----------------------------------------------------------------
* Inserting, removing and renaming a device.
*---------------------------------------------------------------*/
@@ -718,25 +736,45 @@ static int dev_create(struct dm_ioctl *param, size_t param_size)
*/
static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
{
- struct mapped_device *md;
- void *mdptr = NULL;
+ struct hash_cell *hc = NULL;
- if (*param->uuid)
- return __get_uuid_cell(param->uuid);
+ if (*param->uuid) {
+ if (*param->name || param->dev)
+ return NULL;
- if (*param->name)
- return __get_name_cell(param->name);
+ hc = __get_uuid_cell(param->uuid);
+ if (!hc)
+ return NULL;
+ } else if (*param->name) {
+ if (param->dev)
+ return NULL;
- md = dm_get_md(huge_decode_dev(param->dev));
- if (!md)
- goto out;
+ hc = __get_name_cell(param->name);
+ if (!hc)
+ return NULL;
+ } else if (param->dev) {
+ hc = __get_dev_cell(param->dev);
+ if (!hc)
+ return NULL;
+ } else
+ return NULL;
- mdptr = dm_get_mdptr(md);
- if (!mdptr)
- dm_put(md);
+ /*
+ * Sneakily write in both the name and the uuid
+ * while we have the cell.
+ */
+ strlcpy(param->name, hc->name, sizeof(param->name));
+ if (hc->uuid)
+ strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
+ else
+ param->uuid[0] = '\0';
-out:
- return mdptr;
+ if (hc->new_map)
+ param->flags |= DM_INACTIVE_PRESENT_FLAG;
+ else
+ param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+ return hc;
}
static struct mapped_device *find_device(struct dm_ioctl *param)
@@ -746,24 +784,8 @@ static struct mapped_device *find_device(struct dm_ioctl *param)
down_read(&_hash_lock);
hc = __find_device_hash_cell(param);
- if (hc) {
+ if (hc)
md = hc->md;
-
- /*
- * Sneakily write in both the name and the uuid
- * while we have the cell.
- */
- strlcpy(param->name, hc->name, sizeof(param->name));
- if (hc->uuid)
- strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
- else
- param->uuid[0] = '\0';
-
- if (hc->new_map)
- param->flags |= DM_INACTIVE_PRESENT_FLAG;
- else
- param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
- }
up_read(&_hash_lock);
return md;
@@ -1193,6 +1215,7 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
struct hash_cell *hc;
struct dm_table *t;
struct mapped_device *md;
+ struct target_type *immutable_target_type;
md = find_device(param);
if (!md)
@@ -1208,6 +1231,16 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
goto out;
}
+ immutable_target_type = dm_get_immutable_target_type(md);
+ if (immutable_target_type &&
+ (immutable_target_type != dm_table_get_immutable_target_type(t))) {
+ DMWARN("can't replace immutable target type %s",
+ immutable_target_type->name);
+ dm_table_destroy(t);
+ r = -EINVAL;
+ goto out;
+ }
+
/* Protect md->type and md->queue against concurrent table loads. */
dm_lock_md_type(md);
if (dm_get_md_type(md) == DM_TYPE_NONE)
@@ -1402,6 +1435,11 @@ static int target_message(struct dm_ioctl *param, size_t param_size)
goto out;
}
+ if (!argc) {
+ DMWARN("Empty message received.");
+ goto out;
+ }
+
table = dm_get_live_table(md);
if (!table)
goto out_argv;
diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c
index 819e37eaaeba..bed444c93d8d 100644
--- a/drivers/md/dm-kcopyd.c
+++ b/drivers/md/dm-kcopyd.c
@@ -10,7 +10,7 @@
*/
#include <linux/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
#include <linux/init.h>
@@ -66,6 +66,8 @@ struct dm_kcopyd_client {
struct list_head pages_jobs;
};
+static struct page_list zero_page_list;
+
static void wake(struct dm_kcopyd_client *kc)
{
queue_work(kc->kcopyd_wq, &kc->kcopyd_work);
@@ -224,8 +226,6 @@ struct kcopyd_job {
unsigned int num_dests;
struct dm_io_region dests[DM_KCOPYD_MAX_REGIONS];
- sector_t offset;
- unsigned int nr_pages;
struct page_list *pages;
/*
@@ -256,6 +256,9 @@ int __init dm_kcopyd_init(void)
if (!_job_cache)
return -ENOMEM;
+ zero_page_list.next = &zero_page_list;
+ zero_page_list.page = ZERO_PAGE(0);
+
return 0;
}
@@ -324,7 +327,7 @@ static int run_complete_job(struct kcopyd_job *job)
dm_kcopyd_notify_fn fn = job->fn;
struct dm_kcopyd_client *kc = job->kc;
- if (job->pages)
+ if (job->pages && job->pages != &zero_page_list)
kcopyd_put_pages(kc, job->pages);
/*
* If this is the master job, the sub jobs have already
@@ -380,7 +383,7 @@ static int run_io_job(struct kcopyd_job *job)
.bi_rw = job->rw,
.mem.type = DM_IO_PAGE_LIST,
.mem.ptr.pl = job->pages,
- .mem.offset = job->offset,
+ .mem.offset = 0,
.notify.fn = complete_io,
.notify.context = job,
.client = job->kc->io_client,
@@ -397,10 +400,9 @@ static int run_io_job(struct kcopyd_job *job)
static int run_pages_job(struct kcopyd_job *job)
{
int r;
+ unsigned nr_pages = dm_div_up(job->dests[0].count, PAGE_SIZE >> 9);
- job->nr_pages = dm_div_up(job->dests[0].count + job->offset,
- PAGE_SIZE >> 9);
- r = kcopyd_get_pages(job->kc, job->nr_pages, &job->pages);
+ r = kcopyd_get_pages(job->kc, nr_pages, &job->pages);
if (!r) {
/* this job is ready for io */
push(&job->kc->io_jobs, job);
@@ -487,6 +489,8 @@ static void dispatch_job(struct kcopyd_job *job)
atomic_inc(&kc->nr_jobs);
if (unlikely(!job->source.count))
push(&kc->complete_jobs, job);
+ else if (job->pages == &zero_page_list)
+ push(&kc->io_jobs, job);
else
push(&kc->pages_jobs, job);
wake(kc);
@@ -595,16 +599,20 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
job->flags = flags;
job->read_err = 0;
job->write_err = 0;
- job->rw = READ;
-
- job->source = *from;
job->num_dests = num_dests;
memcpy(&job->dests, dests, sizeof(*dests) * num_dests);
- job->offset = 0;
- job->nr_pages = 0;
- job->pages = NULL;
+ if (from) {
+ job->source = *from;
+ job->pages = NULL;
+ job->rw = READ;
+ } else {
+ memset(&job->source, 0, sizeof job->source);
+ job->source.count = job->dests[0].count;
+ job->pages = &zero_page_list;
+ job->rw = WRITE;
+ }
job->fn = fn;
job->context = context;
@@ -622,6 +630,46 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
}
EXPORT_SYMBOL(dm_kcopyd_copy);
+int dm_kcopyd_zero(struct dm_kcopyd_client *kc,
+ unsigned num_dests, struct dm_io_region *dests,
+ unsigned flags, dm_kcopyd_notify_fn fn, void *context)
+{
+ return dm_kcopyd_copy(kc, NULL, num_dests, dests, flags, fn, context);
+}
+EXPORT_SYMBOL(dm_kcopyd_zero);
+
+void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc,
+ dm_kcopyd_notify_fn fn, void *context)
+{
+ struct kcopyd_job *job;
+
+ job = mempool_alloc(kc->job_pool, GFP_NOIO);
+
+ memset(job, 0, sizeof(struct kcopyd_job));
+ job->kc = kc;
+ job->fn = fn;
+ job->context = context;
+ job->master_job = job;
+
+ atomic_inc(&kc->nr_jobs);
+
+ return job;
+}
+EXPORT_SYMBOL(dm_kcopyd_prepare_callback);
+
+void dm_kcopyd_do_callback(void *j, int read_err, unsigned long write_err)
+{
+ struct kcopyd_job *job = j;
+ struct dm_kcopyd_client *kc = job->kc;
+
+ job->read_err = read_err;
+ job->write_err = write_err;
+
+ push(&kc->complete_jobs, job);
+ wake(kc);
+}
+EXPORT_SYMBOL(dm_kcopyd_do_callback);
+
/*
* Cancels a kcopyd job, eg. someone might be deactivating a
* mirror.
diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c
index aa2e0c374ab3..9429159d9ee3 100644
--- a/drivers/md/dm-log-userspace-base.c
+++ b/drivers/md/dm-log-userspace-base.c
@@ -9,6 +9,7 @@
#include <linux/dm-dirty-log.h>
#include <linux/device-mapper.h>
#include <linux/dm-log-userspace.h>
+#include <linux/module.h>
#include "dm-log-userspace-transfer.h"
@@ -30,6 +31,7 @@ struct flush_entry {
struct log_c {
struct dm_target *ti;
+ struct dm_dev *log_dev;
uint32_t region_size;
region_t region_count;
uint64_t luid;
@@ -146,7 +148,7 @@ static int build_constructor_string(struct dm_target *ti,
* <UUID> <other args>
* Where 'other args' is the userspace implementation specific log
* arguments. An example might be:
- * <UUID> clustered_disk <arg count> <log dev> <region_size> [[no]sync]
+ * <UUID> clustered-disk <arg count> <log dev> <region_size> [[no]sync]
*
* So, this module will strip off the <UUID> for identification purposes
* when communicating with userspace about a log; but will pass on everything
@@ -161,13 +163,15 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
struct log_c *lc = NULL;
uint64_t rdata;
size_t rdata_size = sizeof(rdata);
+ char *devices_rdata = NULL;
+ size_t devices_rdata_size = DM_NAME_LEN;
if (argc < 3) {
DMWARN("Too few arguments to userspace dirty log");
return -EINVAL;
}
- lc = kmalloc(sizeof(*lc), GFP_KERNEL);
+ lc = kzalloc(sizeof(*lc), GFP_KERNEL);
if (!lc) {
DMWARN("Unable to allocate userspace log context.");
return -ENOMEM;
@@ -195,9 +199,19 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
return str_size;
}
- /* Send table string */
+ devices_rdata = kzalloc(devices_rdata_size, GFP_KERNEL);
+ if (!devices_rdata) {
+ DMERR("Failed to allocate memory for device information");
+ r = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Send table string and get back any opened device.
+ */
r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_CTR,
- ctr_str, str_size, NULL, NULL);
+ ctr_str, str_size,
+ devices_rdata, &devices_rdata_size);
if (r < 0) {
if (r == -ESRCH)
@@ -220,7 +234,20 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
lc->region_size = (uint32_t)rdata;
lc->region_count = dm_sector_div_up(ti->len, lc->region_size);
+ if (devices_rdata_size) {
+ if (devices_rdata[devices_rdata_size - 1] != '\0') {
+ DMERR("DM_ULOG_CTR device return string not properly terminated");
+ r = -EINVAL;
+ goto out;
+ }
+ r = dm_get_device(ti, devices_rdata,
+ dm_table_get_mode(ti->table), &lc->log_dev);
+ if (r)
+ DMERR("Failed to register %s with device-mapper",
+ devices_rdata);
+ }
out:
+ kfree(devices_rdata);
if (r) {
kfree(lc);
kfree(ctr_str);
@@ -241,6 +268,9 @@ static void userspace_dtr(struct dm_dirty_log *log)
NULL, 0,
NULL, NULL);
+ if (lc->log_dev)
+ dm_put_device(lc->ti, lc->log_dev);
+
kfree(lc->usr_argv_str);
kfree(lc);
@@ -394,8 +424,7 @@ static int flush_by_group(struct log_c *lc, struct list_head *flush_list)
group[count] = fe->region;
count++;
- list_del(&fe->list);
- list_add(&fe->list, &tmp_list);
+ list_move(&fe->list, &tmp_list);
type = fe->type;
if (count >= MAX_FLUSH_GROUP_COUNT)
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c
index 948e3f4925bf..3b52bb72bd1f 100644
--- a/drivers/md/dm-log.c
+++ b/drivers/md/dm-log.c
@@ -197,15 +197,21 @@ EXPORT_SYMBOL(dm_dirty_log_destroy);
#define MIRROR_DISK_VERSION 2
#define LOG_OFFSET 2
-struct log_header {
- uint32_t magic;
+struct log_header_disk {
+ __le32 magic;
/*
* Simple, incrementing version. no backward
* compatibility.
*/
+ __le32 version;
+ __le64 nr_regions;
+} __packed;
+
+struct log_header_core {
+ uint32_t magic;
uint32_t version;
- sector_t nr_regions;
+ uint64_t nr_regions;
};
struct log_c {
@@ -239,10 +245,10 @@ struct log_c {
int log_dev_failed;
int log_dev_flush_failed;
struct dm_dev *log_dev;
- struct log_header header;
+ struct log_header_core header;
struct dm_io_region header_location;
- struct log_header *disk_header;
+ struct log_header_disk *disk_header;
};
/*
@@ -251,34 +257,34 @@ struct log_c {
*/
static inline int log_test_bit(uint32_t *bs, unsigned bit)
{
- return test_bit_le(bit, (unsigned long *) bs) ? 1 : 0;
+ return test_bit_le(bit, bs) ? 1 : 0;
}
static inline void log_set_bit(struct log_c *l,
uint32_t *bs, unsigned bit)
{
- __test_and_set_bit_le(bit, (unsigned long *) bs);
+ __set_bit_le(bit, bs);
l->touched_cleaned = 1;
}
static inline void log_clear_bit(struct log_c *l,
uint32_t *bs, unsigned bit)
{
- __test_and_clear_bit_le(bit, (unsigned long *) bs);
+ __clear_bit_le(bit, bs);
l->touched_dirtied = 1;
}
/*----------------------------------------------------------------
* Header IO
*--------------------------------------------------------------*/
-static void header_to_disk(struct log_header *core, struct log_header *disk)
+static void header_to_disk(struct log_header_core *core, struct log_header_disk *disk)
{
disk->magic = cpu_to_le32(core->magic);
disk->version = cpu_to_le32(core->version);
disk->nr_regions = cpu_to_le64(core->nr_regions);
}
-static void header_from_disk(struct log_header *core, struct log_header *disk)
+static void header_from_disk(struct log_header_core *core, struct log_header_disk *disk)
{
core->magic = le32_to_cpu(disk->magic);
core->version = le32_to_cpu(disk->version);
@@ -486,7 +492,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
memset(lc->sync_bits, (sync == NOSYNC) ? -1 : 0, bitset_size);
lc->sync_count = (sync == NOSYNC) ? region_count : 0;
- lc->recovering_bits = vmalloc(bitset_size);
+ lc->recovering_bits = vzalloc(bitset_size);
if (!lc->recovering_bits) {
DMWARN("couldn't allocate sync bitset");
vfree(lc->sync_bits);
@@ -498,7 +504,6 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
kfree(lc);
return -ENOMEM;
}
- memset(lc->recovering_bits, 0, bitset_size);
lc->sync_search = 0;
log->context = lc;
@@ -739,8 +744,7 @@ static int core_get_resync_work(struct dm_dirty_log *log, region_t *region)
return 0;
do {
- *region = find_next_zero_bit_le(
- (unsigned long *) lc->sync_bits,
+ *region = find_next_zero_bit_le(lc->sync_bits,
lc->region_count,
lc->sync_search);
lc->sync_search = *region + 1;
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index aa4e570c2cb5..5e0090ef4182 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -19,10 +19,9 @@
#include <linux/time.h>
#include <linux/workqueue.h>
#include <scsi/scsi_dh.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#define DM_MSG_PREFIX "multipath"
-#define MESG_STR(x) x, sizeof(x)
#define DM_PG_INIT_DELAY_MSECS 2000
#define DM_PG_INIT_DELAY_DEFAULT ((unsigned) -1)
@@ -505,80 +504,29 @@ static void trigger_event(struct work_struct *work)
* <#paths> <#per-path selector args>
* [<path> [<arg>]* ]+ ]+
*---------------------------------------------------------------*/
-struct param {
- unsigned min;
- unsigned max;
- char *error;
-};
-
-static int read_param(struct param *param, char *str, unsigned *v, char **error)
-{
- if (!str ||
- (sscanf(str, "%u", v) != 1) ||
- (*v < param->min) ||
- (*v > param->max)) {
- *error = param->error;
- return -EINVAL;
- }
-
- return 0;
-}
-
-struct arg_set {
- unsigned argc;
- char **argv;
-};
-
-static char *shift(struct arg_set *as)
-{
- char *r;
-
- if (as->argc) {
- as->argc--;
- r = *as->argv;
- as->argv++;
- return r;
- }
-
- return NULL;
-}
-
-static void consume(struct arg_set *as, unsigned n)
-{
- BUG_ON (as->argc < n);
- as->argc -= n;
- as->argv += n;
-}
-
-static int parse_path_selector(struct arg_set *as, struct priority_group *pg,
+static int parse_path_selector(struct dm_arg_set *as, struct priority_group *pg,
struct dm_target *ti)
{
int r;
struct path_selector_type *pst;
unsigned ps_argc;
- static struct param _params[] = {
+ static struct dm_arg _args[] = {
{0, 1024, "invalid number of path selector args"},
};
- pst = dm_get_path_selector(shift(as));
+ pst = dm_get_path_selector(dm_shift_arg(as));
if (!pst) {
ti->error = "unknown path selector type";
return -EINVAL;
}
- r = read_param(_params, shift(as), &ps_argc, &ti->error);
+ r = dm_read_arg_group(_args, as, &ps_argc, &ti->error);
if (r) {
dm_put_path_selector(pst);
return -EINVAL;
}
- if (ps_argc > as->argc) {
- dm_put_path_selector(pst);
- ti->error = "not enough arguments for path selector";
- return -EINVAL;
- }
-
r = pst->create(&pg->ps, ps_argc, as->argv);
if (r) {
dm_put_path_selector(pst);
@@ -587,12 +535,12 @@ static int parse_path_selector(struct arg_set *as, struct priority_group *pg,
}
pg->ps.type = pst;
- consume(as, ps_argc);
+ dm_consume_args(as, ps_argc);
return 0;
}
-static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
+static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps,
struct dm_target *ti)
{
int r;
@@ -609,7 +557,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
if (!p)
return ERR_PTR(-ENOMEM);
- r = dm_get_device(ti, shift(as), dm_table_get_mode(ti->table),
+ r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table),
&p->path.dev);
if (r) {
ti->error = "error getting device";
@@ -660,16 +608,16 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
return ERR_PTR(r);
}
-static struct priority_group *parse_priority_group(struct arg_set *as,
+static struct priority_group *parse_priority_group(struct dm_arg_set *as,
struct multipath *m)
{
- static struct param _params[] = {
+ static struct dm_arg _args[] = {
{1, 1024, "invalid number of paths"},
{0, 1024, "invalid number of selector args"}
};
int r;
- unsigned i, nr_selector_args, nr_params;
+ unsigned i, nr_selector_args, nr_args;
struct priority_group *pg;
struct dm_target *ti = m->ti;
@@ -693,26 +641,26 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
/*
* read the paths
*/
- r = read_param(_params, shift(as), &pg->nr_pgpaths, &ti->error);
+ r = dm_read_arg(_args, as, &pg->nr_pgpaths, &ti->error);
if (r)
goto bad;
- r = read_param(_params + 1, shift(as), &nr_selector_args, &ti->error);
+ r = dm_read_arg(_args + 1, as, &nr_selector_args, &ti->error);
if (r)
goto bad;
- nr_params = 1 + nr_selector_args;
+ nr_args = 1 + nr_selector_args;
for (i = 0; i < pg->nr_pgpaths; i++) {
struct pgpath *pgpath;
- struct arg_set path_args;
+ struct dm_arg_set path_args;
- if (as->argc < nr_params) {
+ if (as->argc < nr_args) {
ti->error = "not enough path parameters";
r = -EINVAL;
goto bad;
}
- path_args.argc = nr_params;
+ path_args.argc = nr_args;
path_args.argv = as->argv;
pgpath = parse_path(&path_args, &pg->ps, ti);
@@ -723,7 +671,7 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
pgpath->pg = pg;
list_add_tail(&pgpath->list, &pg->pgpaths);
- consume(as, nr_params);
+ dm_consume_args(as, nr_args);
}
return pg;
@@ -733,28 +681,23 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
return ERR_PTR(r);
}
-static int parse_hw_handler(struct arg_set *as, struct multipath *m)
+static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m)
{
unsigned hw_argc;
int ret;
struct dm_target *ti = m->ti;
- static struct param _params[] = {
+ static struct dm_arg _args[] = {
{0, 1024, "invalid number of hardware handler args"},
};
- if (read_param(_params, shift(as), &hw_argc, &ti->error))
+ if (dm_read_arg_group(_args, as, &hw_argc, &ti->error))
return -EINVAL;
if (!hw_argc)
return 0;
- if (hw_argc > as->argc) {
- ti->error = "not enough arguments for hardware handler";
- return -EINVAL;
- }
-
- m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL);
+ m->hw_handler_name = kstrdup(dm_shift_arg(as), GFP_KERNEL);
request_module("scsi_dh_%s", m->hw_handler_name);
if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
ti->error = "unknown hardware handler type";
@@ -778,7 +721,7 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m)
for (i = 0, p+=j+1; i <= hw_argc - 2; i++, p+=j+1)
j = sprintf(p, "%s", as->argv[i]);
}
- consume(as, hw_argc - 1);
+ dm_consume_args(as, hw_argc - 1);
return 0;
fail:
@@ -787,20 +730,20 @@ fail:
return ret;
}
-static int parse_features(struct arg_set *as, struct multipath *m)
+static int parse_features(struct dm_arg_set *as, struct multipath *m)
{
int r;
unsigned argc;
struct dm_target *ti = m->ti;
- const char *param_name;
+ const char *arg_name;
- static struct param _params[] = {
+ static struct dm_arg _args[] = {
{0, 5, "invalid number of feature args"},
{1, 50, "pg_init_retries must be between 1 and 50"},
{0, 60000, "pg_init_delay_msecs must be between 0 and 60000"},
};
- r = read_param(_params, shift(as), &argc, &ti->error);
+ r = dm_read_arg_group(_args, as, &argc, &ti->error);
if (r)
return -EINVAL;
@@ -808,26 +751,24 @@ static int parse_features(struct arg_set *as, struct multipath *m)
return 0;
do {
- param_name = shift(as);
+ arg_name = dm_shift_arg(as);
argc--;
- if (!strnicmp(param_name, MESG_STR("queue_if_no_path"))) {
+ if (!strcasecmp(arg_name, "queue_if_no_path")) {
r = queue_if_no_path(m, 1, 0);
continue;
}
- if (!strnicmp(param_name, MESG_STR("pg_init_retries")) &&
+ if (!strcasecmp(arg_name, "pg_init_retries") &&
(argc >= 1)) {
- r = read_param(_params + 1, shift(as),
- &m->pg_init_retries, &ti->error);
+ r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error);
argc--;
continue;
}
- if (!strnicmp(param_name, MESG_STR("pg_init_delay_msecs")) &&
+ if (!strcasecmp(arg_name, "pg_init_delay_msecs") &&
(argc >= 1)) {
- r = read_param(_params + 2, shift(as),
- &m->pg_init_delay_msecs, &ti->error);
+ r = dm_read_arg(_args + 2, as, &m->pg_init_delay_msecs, &ti->error);
argc--;
continue;
}
@@ -842,15 +783,15 @@ static int parse_features(struct arg_set *as, struct multipath *m)
static int multipath_ctr(struct dm_target *ti, unsigned int argc,
char **argv)
{
- /* target parameters */
- static struct param _params[] = {
+ /* target arguments */
+ static struct dm_arg _args[] = {
{0, 1024, "invalid number of priority groups"},
{0, 1024, "invalid initial priority group number"},
};
int r;
struct multipath *m;
- struct arg_set as;
+ struct dm_arg_set as;
unsigned pg_count = 0;
unsigned next_pg_num;
@@ -871,11 +812,11 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
if (r)
goto bad;
- r = read_param(_params, shift(&as), &m->nr_priority_groups, &ti->error);
+ r = dm_read_arg(_args, &as, &m->nr_priority_groups, &ti->error);
if (r)
goto bad;
- r = read_param(_params + 1, shift(&as), &next_pg_num, &ti->error);
+ r = dm_read_arg(_args + 1, &as, &next_pg_num, &ti->error);
if (r)
goto bad;
@@ -1505,10 +1446,10 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
}
if (argc == 1) {
- if (!strnicmp(argv[0], MESG_STR("queue_if_no_path"))) {
+ if (!strcasecmp(argv[0], "queue_if_no_path")) {
r = queue_if_no_path(m, 1, 0);
goto out;
- } else if (!strnicmp(argv[0], MESG_STR("fail_if_no_path"))) {
+ } else if (!strcasecmp(argv[0], "fail_if_no_path")) {
r = queue_if_no_path(m, 0, 0);
goto out;
}
@@ -1519,18 +1460,18 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
goto out;
}
- if (!strnicmp(argv[0], MESG_STR("disable_group"))) {
+ if (!strcasecmp(argv[0], "disable_group")) {
r = bypass_pg_num(m, argv[1], 1);
goto out;
- } else if (!strnicmp(argv[0], MESG_STR("enable_group"))) {
+ } else if (!strcasecmp(argv[0], "enable_group")) {
r = bypass_pg_num(m, argv[1], 0);
goto out;
- } else if (!strnicmp(argv[0], MESG_STR("switch_group"))) {
+ } else if (!strcasecmp(argv[0], "switch_group")) {
r = switch_pg_num(m, argv[1]);
goto out;
- } else if (!strnicmp(argv[0], MESG_STR("reinstate_path")))
+ } else if (!strcasecmp(argv[0], "reinstate_path"))
action = reinstate_path;
- else if (!strnicmp(argv[0], MESG_STR("fail_path")))
+ else if (!strcasecmp(argv[0], "fail_path"))
action = fail_path;
else {
DMWARN("Unrecognised multipath message received.");
diff --git a/drivers/md/dm-path-selector.c b/drivers/md/dm-path-selector.c
index 42c04f04a0c4..fa0ccc585cb4 100644
--- a/drivers/md/dm-path-selector.c
+++ b/drivers/md/dm-path-selector.c
@@ -10,6 +10,7 @@
*/
#include <linux/device-mapper.h>
+#include <linux/module.h>
#include "dm-path-selector.h"
diff --git a/drivers/md/dm-queue-length.c b/drivers/md/dm-queue-length.c
index f92b6cea9d9c..03a837aa5ce6 100644
--- a/drivers/md/dm-queue-length.c
+++ b/drivers/md/dm-queue-length.c
@@ -20,7 +20,7 @@
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/module.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#define DM_MSG_PREFIX "multipath queue-length"
#define QL_MIN_IO 128
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index e5d8904fc8f6..c2907d836e4e 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -6,21 +6,22 @@
*/
#include <linux/slab.h>
+#include <linux/module.h>
#include "md.h"
+#include "raid1.h"
#include "raid5.h"
-#include "dm.h"
#include "bitmap.h"
+#include <linux/device-mapper.h>
+
#define DM_MSG_PREFIX "raid"
/*
- * If the MD doesn't support MD_SYNC_STATE_FORCED yet, then
- * make it so the flag doesn't set anything.
+ * The following flags are used by dm-raid.c to set up the array state.
+ * They must be cleared before md_run is called.
*/
-#ifndef MD_SYNC_STATE_FORCED
-#define MD_SYNC_STATE_FORCED 0
-#endif
+#define FirstUse 10 /* rdev flag */
struct raid_dev {
/*
@@ -37,26 +38,27 @@ struct raid_dev {
*/
struct dm_dev *meta_dev;
struct dm_dev *data_dev;
- struct mdk_rdev_s rdev;
+ struct md_rdev rdev;
};
/*
* Flags for rs->print_flags field.
*/
-#define DMPF_DAEMON_SLEEP 0x1
-#define DMPF_MAX_WRITE_BEHIND 0x2
-#define DMPF_SYNC 0x4
-#define DMPF_NOSYNC 0x8
-#define DMPF_STRIPE_CACHE 0x10
-#define DMPF_MIN_RECOVERY_RATE 0x20
-#define DMPF_MAX_RECOVERY_RATE 0x40
-
+#define DMPF_SYNC 0x1
+#define DMPF_NOSYNC 0x2
+#define DMPF_REBUILD 0x4
+#define DMPF_DAEMON_SLEEP 0x8
+#define DMPF_MIN_RECOVERY_RATE 0x10
+#define DMPF_MAX_RECOVERY_RATE 0x20
+#define DMPF_MAX_WRITE_BEHIND 0x40
+#define DMPF_STRIPE_CACHE 0x80
+#define DMPF_REGION_SIZE 0X100
struct raid_set {
struct dm_target *ti;
uint64_t print_flags;
- struct mddev_s md;
+ struct mddev md;
struct raid_type *raid_type;
struct dm_target_callbacks callbacks;
@@ -72,6 +74,7 @@ static struct raid_type {
const unsigned level; /* RAID level. */
const unsigned algorithm; /* RAID algorithm. */
} raid_types[] = {
+ {"raid1", "RAID1 (mirroring)", 0, 2, 1, 0 /* NONE */},
{"raid4", "RAID4 (dedicated parity disk)", 1, 2, 5, ALGORITHM_PARITY_0},
{"raid5_la", "RAID5 (left asymmetric)", 1, 2, 5, ALGORITHM_LEFT_ASYMMETRIC},
{"raid5_ra", "RAID5 (right asymmetric)", 1, 2, 5, ALGORITHM_RIGHT_ASYMMETRIC},
@@ -105,7 +108,8 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra
}
sectors_per_dev = ti->len;
- if (sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) {
+ if ((raid_type->level > 1) &&
+ sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) {
ti->error = "Target length not divisible by number of data devices";
return ERR_PTR(-EINVAL);
}
@@ -147,9 +151,16 @@ static void context_free(struct raid_set *rs)
{
int i;
- for (i = 0; i < rs->md.raid_disks; i++)
+ for (i = 0; i < rs->md.raid_disks; i++) {
+ if (rs->dev[i].meta_dev)
+ dm_put_device(rs->ti, rs->dev[i].meta_dev);
+ if (rs->dev[i].rdev.sb_page)
+ put_page(rs->dev[i].rdev.sb_page);
+ rs->dev[i].rdev.sb_page = NULL;
+ rs->dev[i].rdev.sb_loaded = 0;
if (rs->dev[i].data_dev)
dm_put_device(rs->ti, rs->dev[i].data_dev);
+ }
kfree(rs);
}
@@ -159,7 +170,16 @@ static void context_free(struct raid_set *rs)
* <meta_dev>: meta device name or '-' if missing
* <data_dev>: data device name or '-' if missing
*
- * This code parses those words.
+ * The following are permitted:
+ * - -
+ * - <data_dev>
+ * <meta_dev> <data_dev>
+ *
+ * The following is not allowed:
+ * <meta_dev> -
+ *
+ * This code parses those words. If there is a failure,
+ * the caller must use context_free to unwind the operations.
*/
static int dev_parms(struct raid_set *rs, char **argv)
{
@@ -182,8 +202,16 @@ static int dev_parms(struct raid_set *rs, char **argv)
rs->dev[i].rdev.mddev = &rs->md;
if (strcmp(argv[0], "-")) {
- rs->ti->error = "Metadata devices not supported";
- return -EINVAL;
+ ret = dm_get_device(rs->ti, argv[0],
+ dm_table_get_mode(rs->ti->table),
+ &rs->dev[i].meta_dev);
+ rs->ti->error = "RAID metadata device lookup failure";
+ if (ret)
+ return ret;
+
+ rs->dev[i].rdev.sb_page = alloc_page(GFP_KERNEL);
+ if (!rs->dev[i].rdev.sb_page)
+ return -ENOMEM;
}
if (!strcmp(argv[1], "-")) {
@@ -193,6 +221,10 @@ static int dev_parms(struct raid_set *rs, char **argv)
return -EINVAL;
}
+ rs->ti->error = "No data device supplied with metadata device";
+ if (rs->dev[i].meta_dev)
+ return -EINVAL;
+
continue;
}
@@ -204,6 +236,10 @@ static int dev_parms(struct raid_set *rs, char **argv)
return ret;
}
+ if (rs->dev[i].meta_dev) {
+ metadata_available = 1;
+ rs->dev[i].rdev.meta_bdev = rs->dev[i].meta_dev->bdev;
+ }
rs->dev[i].rdev.bdev = rs->dev[i].data_dev->bdev;
list_add(&rs->dev[i].rdev.same_set, &rs->md.disks);
if (!test_bit(In_sync, &rs->dev[i].rdev.flags))
@@ -235,33 +271,109 @@ static int dev_parms(struct raid_set *rs, char **argv)
}
/*
+ * validate_region_size
+ * @rs
+ * @region_size: region size in sectors. If 0, pick a size (4MiB default).
+ *
+ * Set rs->md.bitmap_info.chunksize (which really refers to 'region size').
+ * Ensure that (ti->len/region_size < 2^21) - required by MD bitmap.
+ *
+ * Returns: 0 on success, -EINVAL on failure.
+ */
+static int validate_region_size(struct raid_set *rs, unsigned long region_size)
+{
+ unsigned long min_region_size = rs->ti->len / (1 << 21);
+
+ if (!region_size) {
+ /*
+ * Choose a reasonable default. All figures in sectors.
+ */
+ if (min_region_size > (1 << 13)) {
+ DMINFO("Choosing default region size of %lu sectors",
+ region_size);
+ region_size = min_region_size;
+ } else {
+ DMINFO("Choosing default region size of 4MiB");
+ region_size = 1 << 13; /* sectors */
+ }
+ } else {
+ /*
+ * Validate user-supplied value.
+ */
+ if (region_size > rs->ti->len) {
+ rs->ti->error = "Supplied region size is too large";
+ return -EINVAL;
+ }
+
+ if (region_size < min_region_size) {
+ DMERR("Supplied region_size (%lu sectors) below minimum (%lu)",
+ region_size, min_region_size);
+ rs->ti->error = "Supplied region size is too small";
+ return -EINVAL;
+ }
+
+ if (!is_power_of_2(region_size)) {
+ rs->ti->error = "Region size is not a power of 2";
+ return -EINVAL;
+ }
+
+ if (region_size < rs->md.chunk_sectors) {
+ rs->ti->error = "Region size is smaller than the chunk size";
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Convert sectors to bytes.
+ */
+ rs->md.bitmap_info.chunksize = (region_size << 9);
+
+ return 0;
+}
+
+/*
* Possible arguments are...
- * RAID456:
* <chunk_size> [optional_args]
*
- * Optional args:
- * [[no]sync] Force or prevent recovery of the entire array
+ * Argument definitions
+ * <chunk_size> The number of sectors per disk that
+ * will form the "stripe"
+ * [[no]sync] Force or prevent recovery of the
+ * entire array
* [rebuild <idx>] Rebuild the drive indicated by the index
- * [daemon_sleep <ms>] Time between bitmap daemon work to clear bits
+ * [daemon_sleep <ms>] Time between bitmap daemon work to
+ * clear bits
* [min_recovery_rate <kB/sec/disk>] Throttle RAID initialization
* [max_recovery_rate <kB/sec/disk>] Throttle RAID initialization
+ * [write_mostly <idx>] Indicate a write mostly drive via index
* [max_write_behind <sectors>] See '-write-behind=' (man mdadm)
* [stripe_cache <sectors>] Stripe cache size for higher RAIDs
+ * [region_size <sectors>] Defines granularity of bitmap
*/
static int parse_raid_params(struct raid_set *rs, char **argv,
unsigned num_raid_params)
{
unsigned i, rebuild_cnt = 0;
- unsigned long value;
+ unsigned long value, region_size = 0;
char *key;
/*
* First, parse the in-order required arguments
+ * "chunk_size" is the only argument of this type.
*/
- if ((strict_strtoul(argv[0], 10, &value) < 0) ||
- !is_power_of_2(value) || (value < 8)) {
+ if ((strict_strtoul(argv[0], 10, &value) < 0)) {
rs->ti->error = "Bad chunk size";
return -EINVAL;
+ } else if (rs->raid_type->level == 1) {
+ if (value)
+ DMERR("Ignoring chunk size parameter for RAID 1");
+ value = 0;
+ } else if (!is_power_of_2(value)) {
+ rs->ti->error = "Chunk size must be a power of 2";
+ return -EINVAL;
+ } else if (value < 8) {
+ rs->ti->error = "Chunk size value is too small";
+ return -EINVAL;
}
rs->md.new_chunk_sectors = rs->md.chunk_sectors = value;
@@ -269,22 +381,39 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
num_raid_params--;
/*
- * Second, parse the unordered optional arguments
+ * We set each individual device as In_sync with a completed
+ * 'recovery_offset'. If there has been a device failure or
+ * replacement then one of the following cases applies:
+ *
+ * 1) User specifies 'rebuild'.
+ * - Device is reset when param is read.
+ * 2) A new device is supplied.
+ * - No matching superblock found, resets device.
+ * 3) Device failure was transient and returns on reload.
+ * - Failure noticed, resets device for bitmap replay.
+ * 4) Device hadn't completed recovery after previous failure.
+ * - Superblock is read and overrides recovery_offset.
+ *
+ * What is found in the superblocks of the devices is always
+ * authoritative, unless 'rebuild' or '[no]sync' was specified.
*/
- for (i = 0; i < rs->md.raid_disks; i++)
+ for (i = 0; i < rs->md.raid_disks; i++) {
set_bit(In_sync, &rs->dev[i].rdev.flags);
+ rs->dev[i].rdev.recovery_offset = MaxSector;
+ }
+ /*
+ * Second, parse the unordered optional arguments
+ */
for (i = 0; i < num_raid_params; i++) {
- if (!strcmp(argv[i], "nosync")) {
+ if (!strcasecmp(argv[i], "nosync")) {
rs->md.recovery_cp = MaxSector;
rs->print_flags |= DMPF_NOSYNC;
- rs->md.flags |= MD_SYNC_STATE_FORCED;
continue;
}
- if (!strcmp(argv[i], "sync")) {
+ if (!strcasecmp(argv[i], "sync")) {
rs->md.recovery_cp = 0;
rs->print_flags |= DMPF_SYNC;
- rs->md.flags |= MD_SYNC_STATE_FORCED;
continue;
}
@@ -300,9 +429,13 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
return -EINVAL;
}
- if (!strcmp(key, "rebuild")) {
- if (++rebuild_cnt > rs->raid_type->parity_devs) {
- rs->ti->error = "Too many rebuild drives given";
+ if (!strcasecmp(key, "rebuild")) {
+ rebuild_cnt++;
+ if (((rs->raid_type->level != 1) &&
+ (rebuild_cnt > rs->raid_type->parity_devs)) ||
+ ((rs->raid_type->level == 1) &&
+ (rebuild_cnt > (rs->md.raid_disks - 1)))) {
+ rs->ti->error = "Too many rebuild devices specified for given RAID type";
return -EINVAL;
}
if (value > rs->md.raid_disks) {
@@ -311,7 +444,22 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
}
clear_bit(In_sync, &rs->dev[value].rdev.flags);
rs->dev[value].rdev.recovery_offset = 0;
- } else if (!strcmp(key, "max_write_behind")) {
+ rs->print_flags |= DMPF_REBUILD;
+ } else if (!strcasecmp(key, "write_mostly")) {
+ if (rs->raid_type->level != 1) {
+ rs->ti->error = "write_mostly option is only valid for RAID1";
+ return -EINVAL;
+ }
+ if (value >= rs->md.raid_disks) {
+ rs->ti->error = "Invalid write_mostly drive index given";
+ return -EINVAL;
+ }
+ set_bit(WriteMostly, &rs->dev[value].rdev.flags);
+ } else if (!strcasecmp(key, "max_write_behind")) {
+ if (rs->raid_type->level != 1) {
+ rs->ti->error = "max_write_behind option is only valid for RAID1";
+ return -EINVAL;
+ }
rs->print_flags |= DMPF_MAX_WRITE_BEHIND;
/*
@@ -324,14 +472,14 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
return -EINVAL;
}
rs->md.bitmap_info.max_write_behind = value;
- } else if (!strcmp(key, "daemon_sleep")) {
+ } else if (!strcasecmp(key, "daemon_sleep")) {
rs->print_flags |= DMPF_DAEMON_SLEEP;
if (!value || (value > MAX_SCHEDULE_TIMEOUT)) {
rs->ti->error = "daemon sleep period out of range";
return -EINVAL;
}
rs->md.bitmap_info.daemon_sleep = value;
- } else if (!strcmp(key, "stripe_cache")) {
+ } else if (!strcasecmp(key, "stripe_cache")) {
rs->print_flags |= DMPF_STRIPE_CACHE;
/*
@@ -348,20 +496,23 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
rs->ti->error = "Bad stripe_cache size";
return -EINVAL;
}
- } else if (!strcmp(key, "min_recovery_rate")) {
+ } else if (!strcasecmp(key, "min_recovery_rate")) {
rs->print_flags |= DMPF_MIN_RECOVERY_RATE;
if (value > INT_MAX) {
rs->ti->error = "min_recovery_rate out of range";
return -EINVAL;
}
rs->md.sync_speed_min = (int)value;
- } else if (!strcmp(key, "max_recovery_rate")) {
+ } else if (!strcasecmp(key, "max_recovery_rate")) {
rs->print_flags |= DMPF_MAX_RECOVERY_RATE;
if (value > INT_MAX) {
rs->ti->error = "max_recovery_rate out of range";
return -EINVAL;
}
rs->md.sync_speed_max = (int)value;
+ } else if (!strcasecmp(key, "region_size")) {
+ rs->print_flags |= DMPF_REGION_SIZE;
+ region_size = value;
} else {
DMERR("Unable to parse RAID parameter: %s", key);
rs->ti->error = "Unable to parse RAID parameters";
@@ -369,6 +520,19 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
}
}
+ if (validate_region_size(rs, region_size))
+ return -EINVAL;
+
+ if (rs->md.chunk_sectors)
+ rs->ti->split_io = rs->md.chunk_sectors;
+ else
+ rs->ti->split_io = region_size;
+
+ if (rs->md.chunk_sectors)
+ rs->ti->split_io = rs->md.chunk_sectors;
+ else
+ rs->ti->split_io = region_size;
+
/* Assume there are no metadata devices until the drives are parsed */
rs->md.persistent = 0;
rs->md.external = 1;
@@ -387,17 +551,351 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits)
{
struct raid_set *rs = container_of(cb, struct raid_set, callbacks);
+ if (rs->raid_type->level == 1)
+ return md_raid1_congested(&rs->md, bits);
+
return md_raid5_congested(&rs->md, bits);
}
/*
+ * This structure is never routinely used by userspace, unlike md superblocks.
+ * Devices with this superblock should only ever be accessed via device-mapper.
+ */
+#define DM_RAID_MAGIC 0x64526D44
+struct dm_raid_superblock {
+ __le32 magic; /* "DmRd" */
+ __le32 features; /* Used to indicate possible future changes */
+
+ __le32 num_devices; /* Number of devices in this array. (Max 64) */
+ __le32 array_position; /* The position of this drive in the array */
+
+ __le64 events; /* Incremented by md when superblock updated */
+ __le64 failed_devices; /* Bit field of devices to indicate failures */
+
+ /*
+ * This offset tracks the progress of the repair or replacement of
+ * an individual drive.
+ */
+ __le64 disk_recovery_offset;
+
+ /*
+ * This offset tracks the progress of the initial array
+ * synchronisation/parity calculation.
+ */
+ __le64 array_resync_offset;
+
+ /*
+ * RAID characteristics
+ */
+ __le32 level;
+ __le32 layout;
+ __le32 stripe_sectors;
+
+ __u8 pad[452]; /* Round struct to 512 bytes. */
+ /* Always set to 0 when writing. */
+} __packed;
+
+static int read_disk_sb(struct md_rdev *rdev, int size)
+{
+ BUG_ON(!rdev->sb_page);
+
+ if (rdev->sb_loaded)
+ return 0;
+
+ if (!sync_page_io(rdev, 0, size, rdev->sb_page, READ, 1)) {
+ DMERR("Failed to read device superblock");
+ return -EINVAL;
+ }
+
+ rdev->sb_loaded = 1;
+
+ return 0;
+}
+
+static void super_sync(struct mddev *mddev, struct md_rdev *rdev)
+{
+ struct md_rdev *r, *t;
+ uint64_t failed_devices;
+ struct dm_raid_superblock *sb;
+
+ sb = page_address(rdev->sb_page);
+ failed_devices = le64_to_cpu(sb->failed_devices);
+
+ rdev_for_each(r, t, mddev)
+ if ((r->raid_disk >= 0) && test_bit(Faulty, &r->flags))
+ failed_devices |= (1ULL << r->raid_disk);
+
+ memset(sb, 0, sizeof(*sb));
+
+ sb->magic = cpu_to_le32(DM_RAID_MAGIC);
+ sb->features = cpu_to_le32(0); /* No features yet */
+
+ sb->num_devices = cpu_to_le32(mddev->raid_disks);
+ sb->array_position = cpu_to_le32(rdev->raid_disk);
+
+ sb->events = cpu_to_le64(mddev->events);
+ sb->failed_devices = cpu_to_le64(failed_devices);
+
+ sb->disk_recovery_offset = cpu_to_le64(rdev->recovery_offset);
+ sb->array_resync_offset = cpu_to_le64(mddev->recovery_cp);
+
+ sb->level = cpu_to_le32(mddev->level);
+ sb->layout = cpu_to_le32(mddev->layout);
+ sb->stripe_sectors = cpu_to_le32(mddev->chunk_sectors);
+}
+
+/*
+ * super_load
+ *
+ * This function creates a superblock if one is not found on the device
+ * and will decide which superblock to use if there's a choice.
+ *
+ * Return: 1 if use rdev, 0 if use refdev, -Exxx otherwise
+ */
+static int super_load(struct md_rdev *rdev, struct md_rdev *refdev)
+{
+ int ret;
+ struct dm_raid_superblock *sb;
+ struct dm_raid_superblock *refsb;
+ uint64_t events_sb, events_refsb;
+
+ rdev->sb_start = 0;
+ rdev->sb_size = sizeof(*sb);
+
+ ret = read_disk_sb(rdev, rdev->sb_size);
+ if (ret)
+ return ret;
+
+ sb = page_address(rdev->sb_page);
+ if (sb->magic != cpu_to_le32(DM_RAID_MAGIC)) {
+ super_sync(rdev->mddev, rdev);
+
+ set_bit(FirstUse, &rdev->flags);
+
+ /* Force writing of superblocks to disk */
+ set_bit(MD_CHANGE_DEVS, &rdev->mddev->flags);
+
+ /* Any superblock is better than none, choose that if given */
+ return refdev ? 0 : 1;
+ }
+
+ if (!refdev)
+ return 1;
+
+ events_sb = le64_to_cpu(sb->events);
+
+ refsb = page_address(refdev->sb_page);
+ events_refsb = le64_to_cpu(refsb->events);
+
+ return (events_sb > events_refsb) ? 1 : 0;
+}
+
+static int super_init_validation(struct mddev *mddev, struct md_rdev *rdev)
+{
+ int role;
+ struct raid_set *rs = container_of(mddev, struct raid_set, md);
+ uint64_t events_sb;
+ uint64_t failed_devices;
+ struct dm_raid_superblock *sb;
+ uint32_t new_devs = 0;
+ uint32_t rebuilds = 0;
+ struct md_rdev *r, *t;
+ struct dm_raid_superblock *sb2;
+
+ sb = page_address(rdev->sb_page);
+ events_sb = le64_to_cpu(sb->events);
+ failed_devices = le64_to_cpu(sb->failed_devices);
+
+ /*
+ * Initialise to 1 if this is a new superblock.
+ */
+ mddev->events = events_sb ? : 1;
+
+ /*
+ * Reshaping is not currently allowed
+ */
+ if ((le32_to_cpu(sb->level) != mddev->level) ||
+ (le32_to_cpu(sb->layout) != mddev->layout) ||
+ (le32_to_cpu(sb->stripe_sectors) != mddev->chunk_sectors)) {
+ DMERR("Reshaping arrays not yet supported.");
+ return -EINVAL;
+ }
+
+ /* We can only change the number of devices in RAID1 right now */
+ if ((rs->raid_type->level != 1) &&
+ (le32_to_cpu(sb->num_devices) != mddev->raid_disks)) {
+ DMERR("Reshaping arrays not yet supported.");
+ return -EINVAL;
+ }
+
+ if (!(rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC)))
+ mddev->recovery_cp = le64_to_cpu(sb->array_resync_offset);
+
+ /*
+ * During load, we set FirstUse if a new superblock was written.
+ * There are two reasons we might not have a superblock:
+ * 1) The array is brand new - in which case, all of the
+ * devices must have their In_sync bit set. Also,
+ * recovery_cp must be 0, unless forced.
+ * 2) This is a new device being added to an old array
+ * and the new device needs to be rebuilt - in which
+ * case the In_sync bit will /not/ be set and
+ * recovery_cp must be MaxSector.
+ */
+ rdev_for_each(r, t, mddev) {
+ if (!test_bit(In_sync, &r->flags)) {
+ if (!test_bit(FirstUse, &r->flags))
+ DMERR("Superblock area of "
+ "rebuild device %d should have been "
+ "cleared.", r->raid_disk);
+ set_bit(FirstUse, &r->flags);
+ rebuilds++;
+ } else if (test_bit(FirstUse, &r->flags))
+ new_devs++;
+ }
+
+ if (!rebuilds) {
+ if (new_devs == mddev->raid_disks) {
+ DMINFO("Superblocks created for new array");
+ set_bit(MD_ARRAY_FIRST_USE, &mddev->flags);
+ } else if (new_devs) {
+ DMERR("New device injected "
+ "into existing array without 'rebuild' "
+ "parameter specified");
+ return -EINVAL;
+ }
+ } else if (new_devs) {
+ DMERR("'rebuild' devices cannot be "
+ "injected into an array with other first-time devices");
+ return -EINVAL;
+ } else if (mddev->recovery_cp != MaxSector) {
+ DMERR("'rebuild' specified while array is not in-sync");
+ return -EINVAL;
+ }
+
+ /*
+ * Now we set the Faulty bit for those devices that are
+ * recorded in the superblock as failed.
+ */
+ rdev_for_each(r, t, mddev) {
+ if (!r->sb_page)
+ continue;
+ sb2 = page_address(r->sb_page);
+ sb2->failed_devices = 0;
+
+ /*
+ * Check for any device re-ordering.
+ */
+ if (!test_bit(FirstUse, &r->flags) && (r->raid_disk >= 0)) {
+ role = le32_to_cpu(sb2->array_position);
+ if (role != r->raid_disk) {
+ if (rs->raid_type->level != 1) {
+ rs->ti->error = "Cannot change device "
+ "positions in RAID array";
+ return -EINVAL;
+ }
+ DMINFO("RAID1 device #%d now at position #%d",
+ role, r->raid_disk);
+ }
+
+ /*
+ * Partial recovery is performed on
+ * returning failed devices.
+ */
+ if (failed_devices & (1 << role))
+ set_bit(Faulty, &r->flags);
+ }
+ }
+
+ return 0;
+}
+
+static int super_validate(struct mddev *mddev, struct md_rdev *rdev)
+{
+ struct dm_raid_superblock *sb = page_address(rdev->sb_page);
+
+ /*
+ * If mddev->events is not set, we know we have not yet initialized
+ * the array.
+ */
+ if (!mddev->events && super_init_validation(mddev, rdev))
+ return -EINVAL;
+
+ mddev->bitmap_info.offset = 4096 >> 9; /* Enable bitmap creation */
+ rdev->mddev->bitmap_info.default_offset = 4096 >> 9;
+ if (!test_bit(FirstUse, &rdev->flags)) {
+ rdev->recovery_offset = le64_to_cpu(sb->disk_recovery_offset);
+ if (rdev->recovery_offset != MaxSector)
+ clear_bit(In_sync, &rdev->flags);
+ }
+
+ /*
+ * If a device comes back, set it as not In_sync and no longer faulty.
+ */
+ if (test_bit(Faulty, &rdev->flags)) {
+ clear_bit(Faulty, &rdev->flags);
+ clear_bit(In_sync, &rdev->flags);
+ rdev->saved_raid_disk = rdev->raid_disk;
+ rdev->recovery_offset = 0;
+ }
+
+ clear_bit(FirstUse, &rdev->flags);
+
+ return 0;
+}
+
+/*
+ * Analyse superblocks and select the freshest.
+ */
+static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
+{
+ int ret;
+ struct md_rdev *rdev, *freshest, *tmp;
+ struct mddev *mddev = &rs->md;
+
+ freshest = NULL;
+ rdev_for_each(rdev, tmp, mddev) {
+ if (!rdev->meta_bdev)
+ continue;
+
+ ret = super_load(rdev, freshest);
+
+ switch (ret) {
+ case 1:
+ freshest = rdev;
+ break;
+ case 0:
+ break;
+ default:
+ ti->error = "Failed to load superblock";
+ return ret;
+ }
+ }
+
+ if (!freshest)
+ return 0;
+
+ /*
+ * Validation of the freshest device provides the source of
+ * validation for the remaining devices.
+ */
+ ti->error = "Unable to assemble array: Invalid superblocks";
+ if (super_validate(mddev, freshest))
+ return -EINVAL;
+
+ rdev_for_each(rdev, tmp, mddev)
+ if ((rdev != freshest) && super_validate(mddev, rdev))
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
* Construct a RAID4/5/6 mapping:
* Args:
* <raid_type> <#raid_params> <raid_params> \
* <#raid_devs> { <meta_dev1> <dev1> .. <meta_devN> <devN> }
*
- * ** metadata devices are not supported yet, use '-' instead **
- *
* <raid_params> varies by <raid_type>. See 'parse_raid_params' for
* details on possible <raid_params>.
*/
@@ -465,8 +963,12 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
if (ret)
goto bad;
+ rs->md.sync_super = super_sync;
+ ret = analyse_superblocks(ti, rs);
+ if (ret)
+ goto bad;
+
INIT_WORK(&rs->md.event_work, do_table_event);
- ti->split_io = rs->md.chunk_sectors;
ti->private = rs;
mutex_lock(&rs->md.reconfig_mutex);
@@ -482,6 +984,7 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
rs->callbacks.congested_fn = raid_is_congested;
dm_table_add_target_callbacks(ti->table, &rs->callbacks);
+ mddev_suspend(&rs->md);
return 0;
bad:
@@ -502,7 +1005,7 @@ static void raid_dtr(struct dm_target *ti)
static int raid_map(struct dm_target *ti, struct bio *bio, union map_info *map_context)
{
struct raid_set *rs = ti->private;
- mddev_t *mddev = &rs->md;
+ struct mddev *mddev = &rs->md;
mddev->pers->make_request(mddev, bio);
@@ -515,30 +1018,56 @@ static int raid_status(struct dm_target *ti, status_type_t type,
struct raid_set *rs = ti->private;
unsigned raid_param_cnt = 1; /* at least 1 for chunksize */
unsigned sz = 0;
- int i;
+ int i, array_in_sync = 0;
sector_t sync;
switch (type) {
case STATUSTYPE_INFO:
DMEMIT("%s %d ", rs->raid_type->name, rs->md.raid_disks);
- for (i = 0; i < rs->md.raid_disks; i++) {
- if (test_bit(Faulty, &rs->dev[i].rdev.flags))
- DMEMIT("D");
- else if (test_bit(In_sync, &rs->dev[i].rdev.flags))
- DMEMIT("A");
- else
- DMEMIT("a");
- }
-
if (test_bit(MD_RECOVERY_RUNNING, &rs->md.recovery))
sync = rs->md.curr_resync_completed;
else
sync = rs->md.recovery_cp;
- if (sync > rs->md.resync_max_sectors)
+ if (sync >= rs->md.resync_max_sectors) {
+ array_in_sync = 1;
sync = rs->md.resync_max_sectors;
+ } else {
+ /*
+ * The array may be doing an initial sync, or it may
+ * be rebuilding individual components. If all the
+ * devices are In_sync, then it is the array that is
+ * being initialized.
+ */
+ for (i = 0; i < rs->md.raid_disks; i++)
+ if (!test_bit(In_sync, &rs->dev[i].rdev.flags))
+ array_in_sync = 1;
+ }
+ /*
+ * Status characters:
+ * 'D' = Dead/Failed device
+ * 'a' = Alive but not in-sync
+ * 'A' = Alive and in-sync
+ */
+ for (i = 0; i < rs->md.raid_disks; i++) {
+ if (test_bit(Faulty, &rs->dev[i].rdev.flags))
+ DMEMIT("D");
+ else if (!array_in_sync ||
+ !test_bit(In_sync, &rs->dev[i].rdev.flags))
+ DMEMIT("a");
+ else
+ DMEMIT("A");
+ }
+ /*
+ * In-sync ratio:
+ * The in-sync ratio shows the progress of:
+ * - Initializing the array
+ * - Rebuilding a subset of devices of the array
+ * The user can distinguish between the two by referring
+ * to the status characters.
+ */
DMEMIT(" %llu/%llu",
(unsigned long long) sync,
(unsigned long long) rs->md.resync_max_sectors);
@@ -546,12 +1075,17 @@ static int raid_status(struct dm_target *ti, status_type_t type,
break;
case STATUSTYPE_TABLE:
/* The string you would use to construct this array */
- for (i = 0; i < rs->md.raid_disks; i++)
- if (rs->dev[i].data_dev &&
+ for (i = 0; i < rs->md.raid_disks; i++) {
+ if ((rs->print_flags & DMPF_REBUILD) &&
+ rs->dev[i].data_dev &&
!test_bit(In_sync, &rs->dev[i].rdev.flags))
- raid_param_cnt++; /* for rebuilds */
+ raid_param_cnt += 2; /* for rebuilds */
+ if (rs->dev[i].data_dev &&
+ test_bit(WriteMostly, &rs->dev[i].rdev.flags))
+ raid_param_cnt += 2;
+ }
- raid_param_cnt += (hweight64(rs->print_flags) * 2);
+ raid_param_cnt += (hweight64(rs->print_flags & ~DMPF_REBUILD) * 2);
if (rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC))
raid_param_cnt--;
@@ -565,7 +1099,8 @@ static int raid_status(struct dm_target *ti, status_type_t type,
DMEMIT(" nosync");
for (i = 0; i < rs->md.raid_disks; i++)
- if (rs->dev[i].data_dev &&
+ if ((rs->print_flags & DMPF_REBUILD) &&
+ rs->dev[i].data_dev &&
!test_bit(In_sync, &rs->dev[i].rdev.flags))
DMEMIT(" rebuild %u", i);
@@ -579,21 +1114,33 @@ static int raid_status(struct dm_target *ti, status_type_t type,
if (rs->print_flags & DMPF_MAX_RECOVERY_RATE)
DMEMIT(" max_recovery_rate %d", rs->md.sync_speed_max);
+ for (i = 0; i < rs->md.raid_disks; i++)
+ if (rs->dev[i].data_dev &&
+ test_bit(WriteMostly, &rs->dev[i].rdev.flags))
+ DMEMIT(" write_mostly %u", i);
+
if (rs->print_flags & DMPF_MAX_WRITE_BEHIND)
DMEMIT(" max_write_behind %lu",
rs->md.bitmap_info.max_write_behind);
if (rs->print_flags & DMPF_STRIPE_CACHE) {
- raid5_conf_t *conf = rs->md.private;
+ struct r5conf *conf = rs->md.private;
/* convert from kiB to sectors */
DMEMIT(" stripe_cache %d",
conf ? conf->max_nr_stripes * 2 : 0);
}
+ if (rs->print_flags & DMPF_REGION_SIZE)
+ DMEMIT(" region_size %lu",
+ rs->md.bitmap_info.chunksize >> 9);
+
DMEMIT(" %d", rs->md.raid_disks);
for (i = 0; i < rs->md.raid_disks; i++) {
- DMEMIT(" -"); /* metadata device */
+ if (rs->dev[i].meta_dev)
+ DMEMIT(" %s", rs->dev[i].meta_dev->name);
+ else
+ DMEMIT(" -");
if (rs->dev[i].data_dev)
DMEMIT(" %s", rs->dev[i].data_dev->name);
@@ -626,7 +1173,7 @@ static void raid_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct raid_set *rs = ti->private;
unsigned chunk_size = rs->md.chunk_sectors << 9;
- raid5_conf_t *conf = rs->md.private;
+ struct r5conf *conf = rs->md.private;
blk_limits_io_min(limits, chunk_size);
blk_limits_io_opt(limits, chunk_size * (conf->raid_disks - conf->max_degraded));
@@ -650,12 +1197,13 @@ static void raid_resume(struct dm_target *ti)
{
struct raid_set *rs = ti->private;
+ bitmap_load(&rs->md);
mddev_resume(&rs->md);
}
static struct target_type raid_target = {
.name = "raid",
- .version = {1, 0, 0},
+ .version = {1, 1, 0},
.module = THIS_MODULE,
.ctr = raid_ctr,
.dtr = raid_dtr,
diff --git a/drivers/md/dm-round-robin.c b/drivers/md/dm-round-robin.c
index 24752f449bef..27f1d423b76c 100644
--- a/drivers/md/dm-round-robin.c
+++ b/drivers/md/dm-round-robin.c
@@ -14,6 +14,7 @@
#include "dm-path-selector.h"
#include <linux/slab.h>
+#include <linux/module.h>
#define DM_MSG_PREFIX "multipath round-robin"
diff --git a/drivers/md/dm-service-time.c b/drivers/md/dm-service-time.c
index 9c6c2e47ad62..59883bd78214 100644
--- a/drivers/md/dm-service-time.c
+++ b/drivers/md/dm-service-time.c
@@ -12,6 +12,7 @@
#include "dm-path-selector.h"
#include <linux/slab.h>
+#include <linux/module.h>
#define DM_MSG_PREFIX "multipath service-time"
#define ST_MIN_IO 1
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 135c2f1fdbfc..3ac415675b6c 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -10,6 +10,7 @@
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/dm-io.h>
@@ -58,25 +59,30 @@
#define NUM_SNAPSHOT_HDR_CHUNKS 1
struct disk_header {
- uint32_t magic;
+ __le32 magic;
/*
* Is this snapshot valid. There is no way of recovering
* an invalid snapshot.
*/
- uint32_t valid;
+ __le32 valid;
/*
* Simple, incrementing version. no backward
* compatibility.
*/
- uint32_t version;
+ __le32 version;
/* In sectors */
- uint32_t chunk_size;
-};
+ __le32 chunk_size;
+} __packed;
struct disk_exception {
+ __le64 old_chunk;
+ __le64 new_chunk;
+} __packed;
+
+struct core_exception {
uint64_t old_chunk;
uint64_t new_chunk;
};
@@ -169,10 +175,9 @@ static int alloc_area(struct pstore *ps)
if (!ps->area)
goto err_area;
- ps->zero_area = vmalloc(len);
+ ps->zero_area = vzalloc(len);
if (!ps->zero_area)
goto err_zero_area;
- memset(ps->zero_area, 0, len);
ps->header_area = vmalloc(len);
if (!ps->header_area)
@@ -396,32 +401,32 @@ static struct disk_exception *get_exception(struct pstore *ps, uint32_t index)
}
static void read_exception(struct pstore *ps,
- uint32_t index, struct disk_exception *result)
+ uint32_t index, struct core_exception *result)
{
- struct disk_exception *e = get_exception(ps, index);
+ struct disk_exception *de = get_exception(ps, index);
/* copy it */
- result->old_chunk = le64_to_cpu(e->old_chunk);
- result->new_chunk = le64_to_cpu(e->new_chunk);
+ result->old_chunk = le64_to_cpu(de->old_chunk);
+ result->new_chunk = le64_to_cpu(de->new_chunk);
}
static void write_exception(struct pstore *ps,
- uint32_t index, struct disk_exception *de)
+ uint32_t index, struct core_exception *e)
{
- struct disk_exception *e = get_exception(ps, index);
+ struct disk_exception *de = get_exception(ps, index);
/* copy it */
- e->old_chunk = cpu_to_le64(de->old_chunk);
- e->new_chunk = cpu_to_le64(de->new_chunk);
+ de->old_chunk = cpu_to_le64(e->old_chunk);
+ de->new_chunk = cpu_to_le64(e->new_chunk);
}
static void clear_exception(struct pstore *ps, uint32_t index)
{
- struct disk_exception *e = get_exception(ps, index);
+ struct disk_exception *de = get_exception(ps, index);
/* clear it */
- e->old_chunk = 0;
- e->new_chunk = 0;
+ de->old_chunk = 0;
+ de->new_chunk = 0;
}
/*
@@ -437,13 +442,13 @@ static int insert_exceptions(struct pstore *ps,
{
int r;
unsigned int i;
- struct disk_exception de;
+ struct core_exception e;
/* presume the area is full */
*full = 1;
for (i = 0; i < ps->exceptions_per_area; i++) {
- read_exception(ps, i, &de);
+ read_exception(ps, i, &e);
/*
* If the new_chunk is pointing at the start of
@@ -451,7 +456,7 @@ static int insert_exceptions(struct pstore *ps,
* is we know that we've hit the end of the
* exceptions. Therefore the area is not full.
*/
- if (de.new_chunk == 0LL) {
+ if (e.new_chunk == 0LL) {
ps->current_committed = i;
*full = 0;
break;
@@ -460,13 +465,13 @@ static int insert_exceptions(struct pstore *ps,
/*
* Keep track of the start of the free chunks.
*/
- if (ps->next_free <= de.new_chunk)
- ps->next_free = de.new_chunk + 1;
+ if (ps->next_free <= e.new_chunk)
+ ps->next_free = e.new_chunk + 1;
/*
* Otherwise we add the exception to the snapshot.
*/
- r = callback(callback_context, de.old_chunk, de.new_chunk);
+ r = callback(callback_context, e.old_chunk, e.new_chunk);
if (r)
return r;
}
@@ -563,7 +568,7 @@ static int persistent_read_metadata(struct dm_exception_store *store,
ps->exceptions_per_area = (ps->store->chunk_size << SECTOR_SHIFT) /
sizeof(struct disk_exception);
ps->callbacks = dm_vcalloc(ps->exceptions_per_area,
- sizeof(*ps->callbacks));
+ sizeof(*ps->callbacks));
if (!ps->callbacks)
return -ENOMEM;
@@ -641,12 +646,12 @@ static void persistent_commit_exception(struct dm_exception_store *store,
{
unsigned int i;
struct pstore *ps = get_info(store);
- struct disk_exception de;
+ struct core_exception ce;
struct commit_callback *cb;
- de.old_chunk = e->old_chunk;
- de.new_chunk = e->new_chunk;
- write_exception(ps, ps->current_committed++, &de);
+ ce.old_chunk = e->old_chunk;
+ ce.new_chunk = e->new_chunk;
+ write_exception(ps, ps->current_committed++, &ce);
/*
* Add the callback to the back of the array. This code
@@ -670,7 +675,7 @@ static void persistent_commit_exception(struct dm_exception_store *store,
* If we completely filled the current area, then wipe the next one.
*/
if ((ps->current_committed == ps->exceptions_per_area) &&
- zero_disk_area(ps, ps->current_area + 1))
+ zero_disk_area(ps, ps->current_area + 1))
ps->valid = 0;
/*
@@ -701,7 +706,7 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
chunk_t *last_new_chunk)
{
struct pstore *ps = get_info(store);
- struct disk_exception de;
+ struct core_exception ce;
int nr_consecutive;
int r;
@@ -722,9 +727,9 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
ps->current_committed = ps->exceptions_per_area;
}
- read_exception(ps, ps->current_committed - 1, &de);
- *last_old_chunk = de.old_chunk;
- *last_new_chunk = de.new_chunk;
+ read_exception(ps, ps->current_committed - 1, &ce);
+ *last_old_chunk = ce.old_chunk;
+ *last_new_chunk = ce.new_chunk;
/*
* Find number of consecutive chunks within the current area,
@@ -733,9 +738,9 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
for (nr_consecutive = 1; nr_consecutive < ps->current_committed;
nr_consecutive++) {
read_exception(ps, ps->current_committed - 1 - nr_consecutive,
- &de);
- if (de.old_chunk != *last_old_chunk - nr_consecutive ||
- de.new_chunk != *last_new_chunk - nr_consecutive)
+ &ce);
+ if (ce.old_chunk != *last_old_chunk - nr_consecutive ||
+ ce.new_chunk != *last_new_chunk - nr_consecutive)
break;
}
@@ -753,7 +758,7 @@ static int persistent_commit_merge(struct dm_exception_store *store,
for (i = 0; i < nr_merged; i++)
clear_exception(ps, ps->current_committed - 1 - i);
- r = area_io(ps, WRITE);
+ r = area_io(ps, WRITE_FLUSH_FUA);
if (r < 0)
return r;
diff --git a/drivers/md/dm-snap-transient.c b/drivers/md/dm-snap-transient.c
index a0898a66a2f8..1ce9a2586e41 100644
--- a/drivers/md/dm-snap-transient.c
+++ b/drivers/md/dm-snap-transient.c
@@ -10,6 +10,7 @@
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/dm-io.h>
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 9ecff5f3023a..6f758870fc19 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -30,16 +30,6 @@ static const char dm_snapshot_merge_target_name[] = "snapshot-merge";
((ti)->type->name == dm_snapshot_merge_target_name)
/*
- * The percentage increment we will wake up users at
- */
-#define WAKE_UP_PERCENT 5
-
-/*
- * kcopyd priority of snapshot operations
- */
-#define SNAPSHOT_COPY_PRIORITY 2
-
-/*
* The size of the mempool used to track chunks in use.
*/
#define MIN_IOS 256
@@ -180,6 +170,13 @@ struct dm_snap_pending_exception {
* kcopyd.
*/
int started;
+
+ /*
+ * For writing a complete chunk, bypassing the copy.
+ */
+ struct bio *full_bio;
+ bio_end_io_t *full_bio_end_io;
+ void *full_bio_private;
};
/*
@@ -1055,8 +1052,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s) {
- ti->error = "Cannot allocate snapshot context private "
- "structure";
+ ti->error = "Cannot allocate private snapshot structure";
r = -ENOMEM;
goto bad;
}
@@ -1380,6 +1376,7 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
struct dm_snapshot *s = pe->snap;
struct bio *origin_bios = NULL;
struct bio *snapshot_bios = NULL;
+ struct bio *full_bio = NULL;
int error = 0;
if (!success) {
@@ -1415,10 +1412,15 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
*/
dm_insert_exception(&s->complete, e);
- out:
+out:
dm_remove_exception(&pe->e);
snapshot_bios = bio_list_get(&pe->snapshot_bios);
origin_bios = bio_list_get(&pe->origin_bios);
+ full_bio = pe->full_bio;
+ if (full_bio) {
+ full_bio->bi_end_io = pe->full_bio_end_io;
+ full_bio->bi_private = pe->full_bio_private;
+ }
free_pending_exception(pe);
increment_pending_exceptions_done_count();
@@ -1426,10 +1428,15 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
up_write(&s->lock);
/* Submit any pending write bios */
- if (error)
+ if (error) {
+ if (full_bio)
+ bio_io_error(full_bio);
error_bios(snapshot_bios);
- else
+ } else {
+ if (full_bio)
+ bio_endio(full_bio, 0);
flush_bios(snapshot_bios);
+ }
retry_origin_bios(s, origin_bios);
}
@@ -1480,8 +1487,33 @@ static void start_copy(struct dm_snap_pending_exception *pe)
dest.count = src.count;
/* Hand over to kcopyd */
- dm_kcopyd_copy(s->kcopyd_client,
- &src, 1, &dest, 0, copy_callback, pe);
+ dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, copy_callback, pe);
+}
+
+static void full_bio_end_io(struct bio *bio, int error)
+{
+ void *callback_data = bio->bi_private;
+
+ dm_kcopyd_do_callback(callback_data, 0, error ? 1 : 0);
+}
+
+static void start_full_bio(struct dm_snap_pending_exception *pe,
+ struct bio *bio)
+{
+ struct dm_snapshot *s = pe->snap;
+ void *callback_data;
+
+ pe->full_bio = bio;
+ pe->full_bio_end_io = bio->bi_end_io;
+ pe->full_bio_private = bio->bi_private;
+
+ callback_data = dm_kcopyd_prepare_callback(s->kcopyd_client,
+ copy_callback, pe);
+
+ bio->bi_end_io = full_bio_end_io;
+ bio->bi_private = callback_data;
+
+ generic_make_request(bio);
}
static struct dm_snap_pending_exception *
@@ -1519,6 +1551,7 @@ __find_pending_exception(struct dm_snapshot *s,
bio_list_init(&pe->origin_bios);
bio_list_init(&pe->snapshot_bios);
pe->started = 0;
+ pe->full_bio = NULL;
if (s->store->type->prepare_exception(s->store, &pe->e)) {
free_pending_exception(pe);
@@ -1612,10 +1645,19 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
}
remap_exception(s, &pe->e, bio, chunk);
- bio_list_add(&pe->snapshot_bios, bio);
r = DM_MAPIO_SUBMITTED;
+ if (!pe->started &&
+ bio->bi_size == (s->store->chunk_size << SECTOR_SHIFT)) {
+ pe->started = 1;
+ up_write(&s->lock);
+ start_full_bio(pe, bio);
+ goto out;
+ }
+
+ bio_list_add(&pe->snapshot_bios, bio);
+
if (!pe->started) {
/* this is protected by snap->lock */
pe->started = 1;
@@ -1628,9 +1670,9 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
map_context->ptr = track_chunk(s, chunk);
}
- out_unlock:
+out_unlock:
up_write(&s->lock);
- out:
+out:
return r;
}
@@ -1974,7 +2016,7 @@ static int __origin_write(struct list_head *snapshots, sector_t sector,
pe_to_start_now = pe;
}
- next_snapshot:
+next_snapshot:
up_write(&snap->lock);
if (pe_to_start_now) {
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 451c3bb176d2..8e9132130142 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -17,7 +17,7 @@
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/delay.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#define DM_MSG_PREFIX "table"
@@ -54,8 +54,9 @@ struct dm_table {
sector_t *highs;
struct dm_target *targets;
- unsigned discards_supported:1;
+ struct target_type *immutable_target_type;
unsigned integrity_supported:1;
+ unsigned singleton:1;
/*
* Indicates the rw permissions for the new logical
@@ -154,12 +155,11 @@ void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size)
return NULL;
size = nmemb * elem_size;
- addr = vmalloc(size);
- if (addr)
- memset(addr, 0, size);
+ addr = vzalloc(size);
return addr;
}
+EXPORT_SYMBOL(dm_vcalloc);
/*
* highs, and targets are managed as dynamic arrays during a
@@ -209,7 +209,6 @@ int dm_table_create(struct dm_table **result, fmode_t mode,
INIT_LIST_HEAD(&t->devices);
INIT_LIST_HEAD(&t->target_callbacks);
atomic_set(&t->holders, 0);
- t->discards_supported = 1;
if (!num_targets)
num_targets = KEYS_PER_NODE;
@@ -281,6 +280,7 @@ void dm_table_get(struct dm_table *t)
{
atomic_inc(&t->holders);
}
+EXPORT_SYMBOL(dm_table_get);
void dm_table_put(struct dm_table *t)
{
@@ -290,6 +290,7 @@ void dm_table_put(struct dm_table *t)
smp_mb__before_atomic_dec();
atomic_dec(&t->holders);
}
+EXPORT_SYMBOL(dm_table_put);
/*
* Checks to see if we need to extend highs or targets.
@@ -455,13 +456,14 @@ static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
* Add a device to the list, or just increment the usage count if
* it's already present.
*/
-static int __table_get_device(struct dm_table *t, struct dm_target *ti,
- const char *path, fmode_t mode, struct dm_dev **result)
+int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
+ struct dm_dev **result)
{
int r;
dev_t uninitialized_var(dev);
struct dm_dev_internal *dd;
unsigned int major, minor;
+ struct dm_table *t = ti->table;
BUG_ON(!t);
@@ -509,6 +511,7 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti,
*result = &dd->dm_dev;
return 0;
}
+EXPORT_SYMBOL(dm_get_device);
int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
sector_t start, sector_t len, void *data)
@@ -539,23 +542,15 @@ int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
* If not we'll force DM to use PAGE_SIZE or
* smaller I/O, just to be safe.
*/
-
- if (q->merge_bvec_fn && !ti->type->merge)
+ if (dm_queue_merge_is_compulsory(q) && !ti->type->merge)
blk_limits_max_hw_sectors(limits,
(unsigned int) (PAGE_SIZE >> 9));
return 0;
}
EXPORT_SYMBOL_GPL(dm_set_device_limits);
-int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
- struct dm_dev **result)
-{
- return __table_get_device(ti->table, ti, path, mode, result);
-}
-
-
/*
- * Decrement a devices use count and remove it if necessary.
+ * Decrement a device's use count and remove it if necessary.
*/
void dm_put_device(struct dm_target *ti, struct dm_dev *d)
{
@@ -568,6 +563,7 @@ void dm_put_device(struct dm_target *ti, struct dm_dev *d)
kfree(dd);
}
}
+EXPORT_SYMBOL(dm_put_device);
/*
* Checks to see if the target joins onto the end of the table.
@@ -746,6 +742,12 @@ int dm_table_add_target(struct dm_table *t, const char *type,
char **argv;
struct dm_target *tgt;
+ if (t->singleton) {
+ DMERR("%s: target type %s must appear alone in table",
+ dm_device_name(t->md), t->targets->type->name);
+ return -EINVAL;
+ }
+
if ((r = check_space(t)))
return r;
@@ -764,6 +766,36 @@ int dm_table_add_target(struct dm_table *t, const char *type,
return -EINVAL;
}
+ if (dm_target_needs_singleton(tgt->type)) {
+ if (t->num_targets) {
+ DMERR("%s: target type %s must appear alone in table",
+ dm_device_name(t->md), type);
+ return -EINVAL;
+ }
+ t->singleton = 1;
+ }
+
+ if (dm_target_always_writeable(tgt->type) && !(t->mode & FMODE_WRITE)) {
+ DMERR("%s: target type %s may not be included in read-only tables",
+ dm_device_name(t->md), type);
+ return -EINVAL;
+ }
+
+ if (t->immutable_target_type) {
+ if (t->immutable_target_type != tgt->type) {
+ DMERR("%s: immutable target type %s cannot be mixed with other target types",
+ dm_device_name(t->md), t->immutable_target_type->name);
+ return -EINVAL;
+ }
+ } else if (dm_target_is_immutable(tgt->type)) {
+ if (t->num_targets) {
+ DMERR("%s: immutable target type %s cannot be mixed with other target types",
+ dm_device_name(t->md), tgt->type->name);
+ return -EINVAL;
+ }
+ t->immutable_target_type = tgt->type;
+ }
+
tgt->table = t;
tgt->begin = start;
tgt->len = len;
@@ -791,8 +823,9 @@ int dm_table_add_target(struct dm_table *t, const char *type,
t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
- if (!tgt->num_discard_requests)
- t->discards_supported = 0;
+ if (!tgt->num_discard_requests && tgt->discards_supported)
+ DMWARN("%s: %s: ignoring discards_supported because num_discard_requests is zero.",
+ dm_device_name(t->md), type);
return 0;
@@ -802,6 +835,63 @@ int dm_table_add_target(struct dm_table *t, const char *type,
return r;
}
+/*
+ * Target argument parsing helpers.
+ */
+static int validate_next_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
+ unsigned *value, char **error, unsigned grouped)
+{
+ const char *arg_str = dm_shift_arg(arg_set);
+
+ if (!arg_str ||
+ (sscanf(arg_str, "%u", value) != 1) ||
+ (*value < arg->min) ||
+ (*value > arg->max) ||
+ (grouped && arg_set->argc < *value)) {
+ *error = arg->error;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int dm_read_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
+ unsigned *value, char **error)
+{
+ return validate_next_arg(arg, arg_set, value, error, 0);
+}
+EXPORT_SYMBOL(dm_read_arg);
+
+int dm_read_arg_group(struct dm_arg *arg, struct dm_arg_set *arg_set,
+ unsigned *value, char **error)
+{
+ return validate_next_arg(arg, arg_set, value, error, 1);
+}
+EXPORT_SYMBOL(dm_read_arg_group);
+
+const char *dm_shift_arg(struct dm_arg_set *as)
+{
+ char *r;
+
+ if (as->argc) {
+ as->argc--;
+ r = *as->argv;
+ as->argv++;
+ return r;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(dm_shift_arg);
+
+void dm_consume_args(struct dm_arg_set *as, unsigned num_args)
+{
+ BUG_ON(as->argc < num_args);
+ as->argc -= num_args;
+ as->argv += num_args;
+}
+EXPORT_SYMBOL(dm_consume_args);
+
static int dm_table_set_type(struct dm_table *t)
{
unsigned i;
@@ -863,6 +953,11 @@ unsigned dm_table_get_type(struct dm_table *t)
return t->type;
}
+struct target_type *dm_table_get_immutable_target_type(struct dm_table *t)
+{
+ return t->immutable_target_type;
+}
+
bool dm_table_request_based(struct dm_table *t)
{
return dm_table_get_type(t) == DM_TYPE_REQUEST_BASED;
@@ -1077,11 +1172,13 @@ void dm_table_event(struct dm_table *t)
t->event_fn(t->event_context);
mutex_unlock(&_event_lock);
}
+EXPORT_SYMBOL(dm_table_event);
sector_t dm_table_get_size(struct dm_table *t)
{
return t->num_targets ? (t->highs[t->num_targets - 1] + 1) : 0;
}
+EXPORT_SYMBOL(dm_table_get_size);
struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index)
{
@@ -1184,19 +1281,97 @@ static void dm_table_set_integrity(struct dm_table *t)
return;
template_disk = dm_table_get_integrity_disk(t, true);
- if (!template_disk &&
- blk_integrity_is_initialized(dm_disk(t->md))) {
+ if (template_disk)
+ blk_integrity_register(dm_disk(t->md),
+ blk_get_integrity(template_disk));
+ else if (blk_integrity_is_initialized(dm_disk(t->md)))
DMWARN("%s: device no longer has a valid integrity profile",
dm_device_name(t->md));
- return;
+ else
+ DMWARN("%s: unable to establish an integrity profile",
+ dm_device_name(t->md));
+}
+
+static int device_flush_capable(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ unsigned flush = (*(unsigned *)data);
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+
+ return q && (q->flush_flags & flush);
+}
+
+static bool dm_table_supports_flush(struct dm_table *t, unsigned flush)
+{
+ struct dm_target *ti;
+ unsigned i = 0;
+
+ /*
+ * Require at least one underlying device to support flushes.
+ * t->devices includes internal dm devices such as mirror logs
+ * so we need to use iterate_devices here, which targets
+ * supporting flushes must provide.
+ */
+ while (i < dm_table_get_num_targets(t)) {
+ ti = dm_table_get_target(t, i++);
+
+ if (!ti->num_flush_requests)
+ continue;
+
+ if (ti->type->iterate_devices &&
+ ti->type->iterate_devices(ti, device_flush_capable, &flush))
+ return 1;
+ }
+
+ return 0;
+}
+
+static bool dm_table_discard_zeroes_data(struct dm_table *t)
+{
+ struct dm_target *ti;
+ unsigned i = 0;
+
+ /* Ensure that all targets supports discard_zeroes_data. */
+ while (i < dm_table_get_num_targets(t)) {
+ ti = dm_table_get_target(t, i++);
+
+ if (ti->discard_zeroes_data_unsupported)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int device_is_nonrot(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+
+ return q && blk_queue_nonrot(q);
+}
+
+static bool dm_table_is_nonrot(struct dm_table *t)
+{
+ struct dm_target *ti;
+ unsigned i = 0;
+
+ /* Ensure that all underlying device are non-rotational. */
+ while (i < dm_table_get_num_targets(t)) {
+ ti = dm_table_get_target(t, i++);
+
+ if (!ti->type->iterate_devices ||
+ !ti->type->iterate_devices(ti, device_is_nonrot, NULL))
+ return 0;
}
- blk_integrity_register(dm_disk(t->md),
- blk_get_integrity(template_disk));
+
+ return 1;
}
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
struct queue_limits *limits)
{
+ unsigned flush = 0;
+
/*
* Copy table's limits to the DM device's request_queue
*/
@@ -1207,6 +1382,21 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
else
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ if (dm_table_supports_flush(t, REQ_FLUSH)) {
+ flush |= REQ_FLUSH;
+ if (dm_table_supports_flush(t, REQ_FUA))
+ flush |= REQ_FUA;
+ }
+ blk_queue_flush(q, flush);
+
+ if (!dm_table_discard_zeroes_data(t))
+ q->limits.discard_zeroes_data = 0;
+
+ if (dm_table_is_nonrot(t))
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
+ else
+ queue_flag_clear_unlocked(QUEUE_FLAG_NONROT, q);
+
dm_table_set_integrity(t);
/*
@@ -1237,6 +1427,7 @@ fmode_t dm_table_get_mode(struct dm_table *t)
{
return t->mode;
}
+EXPORT_SYMBOL(dm_table_get_mode);
static void suspend_targets(struct dm_table *t, unsigned postsuspend)
{
@@ -1345,6 +1536,7 @@ struct mapped_device *dm_table_get_md(struct dm_table *t)
{
return t->md;
}
+EXPORT_SYMBOL(dm_table_get_md);
static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev,
sector_t start, sector_t len, void *data)
@@ -1359,19 +1551,19 @@ bool dm_table_supports_discards(struct dm_table *t)
struct dm_target *ti;
unsigned i = 0;
- if (!t->discards_supported)
- return 0;
-
/*
* Unless any target used by the table set discards_supported,
* require at least one underlying device to support discards.
* t->devices includes internal dm devices such as mirror logs
* so we need to use iterate_devices here, which targets
- * supporting discard must provide.
+ * supporting discard selectively must provide.
*/
while (i < dm_table_get_num_targets(t)) {
ti = dm_table_get_target(t, i++);
+ if (!ti->num_discard_requests)
+ continue;
+
if (ti->discards_supported)
return 1;
@@ -1382,13 +1574,3 @@ bool dm_table_supports_discards(struct dm_table *t)
return 0;
}
-
-EXPORT_SYMBOL(dm_vcalloc);
-EXPORT_SYMBOL(dm_get_device);
-EXPORT_SYMBOL(dm_put_device);
-EXPORT_SYMBOL(dm_table_event);
-EXPORT_SYMBOL(dm_table_get_size);
-EXPORT_SYMBOL(dm_table_get_mode);
-EXPORT_SYMBOL(dm_table_get_md);
-EXPORT_SYMBOL(dm_table_put);
-EXPORT_SYMBOL(dm_table_get);
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
new file mode 100644
index 000000000000..59c4f0446ffa
--- /dev/null
+++ b/drivers/md/dm-thin-metadata.c
@@ -0,0 +1,1391 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-thin-metadata.h"
+#include "persistent-data/dm-btree.h"
+#include "persistent-data/dm-space-map.h"
+#include "persistent-data/dm-space-map-disk.h"
+#include "persistent-data/dm-transaction-manager.h"
+
+#include <linux/list.h>
+#include <linux/device-mapper.h>
+#include <linux/workqueue.h>
+
+/*--------------------------------------------------------------------------
+ * As far as the metadata goes, there is:
+ *
+ * - A superblock in block zero, taking up fewer than 512 bytes for
+ * atomic writes.
+ *
+ * - A space map managing the metadata blocks.
+ *
+ * - A space map managing the data blocks.
+ *
+ * - A btree mapping our internal thin dev ids onto struct disk_device_details.
+ *
+ * - A hierarchical btree, with 2 levels which effectively maps (thin
+ * dev id, virtual block) -> block_time. Block time is a 64-bit
+ * field holding the time in the low 24 bits, and block in the top 48
+ * bits.
+ *
+ * BTrees consist solely of btree_nodes, that fill a block. Some are
+ * internal nodes, as such their values are a __le64 pointing to other
+ * nodes. Leaf nodes can store data of any reasonable size (ie. much
+ * smaller than the block size). The nodes consist of the header,
+ * followed by an array of keys, followed by an array of values. We have
+ * to binary search on the keys so they're all held together to help the
+ * cpu cache.
+ *
+ * Space maps have 2 btrees:
+ *
+ * - One maps a uint64_t onto a struct index_entry. Which points to a
+ * bitmap block, and has some details about how many free entries there
+ * are etc.
+ *
+ * - The bitmap blocks have a header (for the checksum). Then the rest
+ * of the block is pairs of bits. With the meaning being:
+ *
+ * 0 - ref count is 0
+ * 1 - ref count is 1
+ * 2 - ref count is 2
+ * 3 - ref count is higher than 2
+ *
+ * - If the count is higher than 2 then the ref count is entered in a
+ * second btree that directly maps the block_address to a uint32_t ref
+ * count.
+ *
+ * The space map metadata variant doesn't have a bitmaps btree. Instead
+ * it has one single blocks worth of index_entries. This avoids
+ * recursive issues with the bitmap btree needing to allocate space in
+ * order to insert. With a small data block size such as 64k the
+ * metadata support data devices that are hundreds of terrabytes.
+ *
+ * The space maps allocate space linearly from front to back. Space that
+ * is freed in a transaction is never recycled within that transaction.
+ * To try and avoid fragmenting _free_ space the allocator always goes
+ * back and fills in gaps.
+ *
+ * All metadata io is in THIN_METADATA_BLOCK_SIZE sized/aligned chunks
+ * from the block manager.
+ *--------------------------------------------------------------------------*/
+
+#define DM_MSG_PREFIX "thin metadata"
+
+#define THIN_SUPERBLOCK_MAGIC 27022010
+#define THIN_SUPERBLOCK_LOCATION 0
+#define THIN_VERSION 1
+#define THIN_METADATA_CACHE_SIZE 64
+#define SECTOR_TO_BLOCK_SHIFT 3
+
+/* This should be plenty */
+#define SPACE_MAP_ROOT_SIZE 128
+
+/*
+ * Little endian on-disk superblock and device details.
+ */
+struct thin_disk_superblock {
+ __le32 csum; /* Checksum of superblock except for this field. */
+ __le32 flags;
+ __le64 blocknr; /* This block number, dm_block_t. */
+
+ __u8 uuid[16];
+ __le64 magic;
+ __le32 version;
+ __le32 time;
+
+ __le64 trans_id;
+
+ /*
+ * Root held by userspace transactions.
+ */
+ __le64 held_root;
+
+ __u8 data_space_map_root[SPACE_MAP_ROOT_SIZE];
+ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
+
+ /*
+ * 2-level btree mapping (dev_id, (dev block, time)) -> data block
+ */
+ __le64 data_mapping_root;
+
+ /*
+ * Device detail root mapping dev_id -> device_details
+ */
+ __le64 device_details_root;
+
+ __le32 data_block_size; /* In 512-byte sectors. */
+
+ __le32 metadata_block_size; /* In 512-byte sectors. */
+ __le64 metadata_nr_blocks;
+
+ __le32 compat_flags;
+ __le32 compat_ro_flags;
+ __le32 incompat_flags;
+} __packed;
+
+struct disk_device_details {
+ __le64 mapped_blocks;
+ __le64 transaction_id; /* When created. */
+ __le32 creation_time;
+ __le32 snapshotted_time;
+} __packed;
+
+struct dm_pool_metadata {
+ struct hlist_node hash;
+
+ struct block_device *bdev;
+ struct dm_block_manager *bm;
+ struct dm_space_map *metadata_sm;
+ struct dm_space_map *data_sm;
+ struct dm_transaction_manager *tm;
+ struct dm_transaction_manager *nb_tm;
+
+ /*
+ * Two-level btree.
+ * First level holds thin_dev_t.
+ * Second level holds mappings.
+ */
+ struct dm_btree_info info;
+
+ /*
+ * Non-blocking version of the above.
+ */
+ struct dm_btree_info nb_info;
+
+ /*
+ * Just the top level for deleting whole devices.
+ */
+ struct dm_btree_info tl_info;
+
+ /*
+ * Just the bottom level for creating new devices.
+ */
+ struct dm_btree_info bl_info;
+
+ /*
+ * Describes the device details btree.
+ */
+ struct dm_btree_info details_info;
+
+ struct rw_semaphore root_lock;
+ uint32_t time;
+ int need_commit;
+ dm_block_t root;
+ dm_block_t details_root;
+ struct list_head thin_devices;
+ uint64_t trans_id;
+ unsigned long flags;
+ sector_t data_block_size;
+};
+
+struct dm_thin_device {
+ struct list_head list;
+ struct dm_pool_metadata *pmd;
+ dm_thin_id id;
+
+ int open_count;
+ int changed;
+ uint64_t mapped_blocks;
+ uint64_t transaction_id;
+ uint32_t creation_time;
+ uint32_t snapshotted_time;
+};
+
+/*----------------------------------------------------------------
+ * superblock validator
+ *--------------------------------------------------------------*/
+
+#define SUPERBLOCK_CSUM_XOR 160774
+
+static void sb_prepare_for_write(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct thin_disk_superblock *disk_super = dm_block_data(b);
+
+ disk_super->blocknr = cpu_to_le64(dm_block_location(b));
+ disk_super->csum = cpu_to_le32(dm_bm_checksum(&disk_super->flags,
+ block_size - sizeof(__le32),
+ SUPERBLOCK_CSUM_XOR));
+}
+
+static int sb_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct thin_disk_superblock *disk_super = dm_block_data(b);
+ __le32 csum_le;
+
+ if (dm_block_location(b) != le64_to_cpu(disk_super->blocknr)) {
+ DMERR("sb_check failed: blocknr %llu: "
+ "wanted %llu", le64_to_cpu(disk_super->blocknr),
+ (unsigned long long)dm_block_location(b));
+ return -ENOTBLK;
+ }
+
+ if (le64_to_cpu(disk_super->magic) != THIN_SUPERBLOCK_MAGIC) {
+ DMERR("sb_check failed: magic %llu: "
+ "wanted %llu", le64_to_cpu(disk_super->magic),
+ (unsigned long long)THIN_SUPERBLOCK_MAGIC);
+ return -EILSEQ;
+ }
+
+ csum_le = cpu_to_le32(dm_bm_checksum(&disk_super->flags,
+ block_size - sizeof(__le32),
+ SUPERBLOCK_CSUM_XOR));
+ if (csum_le != disk_super->csum) {
+ DMERR("sb_check failed: csum %u: wanted %u",
+ le32_to_cpu(csum_le), le32_to_cpu(disk_super->csum));
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+static struct dm_block_validator sb_validator = {
+ .name = "superblock",
+ .prepare_for_write = sb_prepare_for_write,
+ .check = sb_check
+};
+
+/*----------------------------------------------------------------
+ * Methods for the btree value types
+ *--------------------------------------------------------------*/
+
+static uint64_t pack_block_time(dm_block_t b, uint32_t t)
+{
+ return (b << 24) | t;
+}
+
+static void unpack_block_time(uint64_t v, dm_block_t *b, uint32_t *t)
+{
+ *b = v >> 24;
+ *t = v & ((1 << 24) - 1);
+}
+
+static void data_block_inc(void *context, void *value_le)
+{
+ struct dm_space_map *sm = context;
+ __le64 v_le;
+ uint64_t b;
+ uint32_t t;
+
+ memcpy(&v_le, value_le, sizeof(v_le));
+ unpack_block_time(le64_to_cpu(v_le), &b, &t);
+ dm_sm_inc_block(sm, b);
+}
+
+static void data_block_dec(void *context, void *value_le)
+{
+ struct dm_space_map *sm = context;
+ __le64 v_le;
+ uint64_t b;
+ uint32_t t;
+
+ memcpy(&v_le, value_le, sizeof(v_le));
+ unpack_block_time(le64_to_cpu(v_le), &b, &t);
+ dm_sm_dec_block(sm, b);
+}
+
+static int data_block_equal(void *context, void *value1_le, void *value2_le)
+{
+ __le64 v1_le, v2_le;
+ uint64_t b1, b2;
+ uint32_t t;
+
+ memcpy(&v1_le, value1_le, sizeof(v1_le));
+ memcpy(&v2_le, value2_le, sizeof(v2_le));
+ unpack_block_time(le64_to_cpu(v1_le), &b1, &t);
+ unpack_block_time(le64_to_cpu(v2_le), &b2, &t);
+
+ return b1 == b2;
+}
+
+static void subtree_inc(void *context, void *value)
+{
+ struct dm_btree_info *info = context;
+ __le64 root_le;
+ uint64_t root;
+
+ memcpy(&root_le, value, sizeof(root_le));
+ root = le64_to_cpu(root_le);
+ dm_tm_inc(info->tm, root);
+}
+
+static void subtree_dec(void *context, void *value)
+{
+ struct dm_btree_info *info = context;
+ __le64 root_le;
+ uint64_t root;
+
+ memcpy(&root_le, value, sizeof(root_le));
+ root = le64_to_cpu(root_le);
+ if (dm_btree_del(info, root))
+ DMERR("btree delete failed\n");
+}
+
+static int subtree_equal(void *context, void *value1_le, void *value2_le)
+{
+ __le64 v1_le, v2_le;
+ memcpy(&v1_le, value1_le, sizeof(v1_le));
+ memcpy(&v2_le, value2_le, sizeof(v2_le));
+
+ return v1_le == v2_le;
+}
+
+/*----------------------------------------------------------------*/
+
+static int superblock_all_zeroes(struct dm_block_manager *bm, int *result)
+{
+ int r;
+ unsigned i;
+ struct dm_block *b;
+ __le64 *data_le, zero = cpu_to_le64(0);
+ unsigned block_size = dm_bm_block_size(bm) / sizeof(__le64);
+
+ /*
+ * We can't use a validator here - it may be all zeroes.
+ */
+ r = dm_bm_read_lock(bm, THIN_SUPERBLOCK_LOCATION, NULL, &b);
+ if (r)
+ return r;
+
+ data_le = dm_block_data(b);
+ *result = 1;
+ for (i = 0; i < block_size; i++) {
+ if (data_le[i] != zero) {
+ *result = 0;
+ break;
+ }
+ }
+
+ return dm_bm_unlock(b);
+}
+
+static int init_pmd(struct dm_pool_metadata *pmd,
+ struct dm_block_manager *bm,
+ dm_block_t nr_blocks, int create)
+{
+ int r;
+ struct dm_space_map *sm, *data_sm;
+ struct dm_transaction_manager *tm;
+ struct dm_block *sblock;
+
+ if (create) {
+ r = dm_tm_create_with_sm(bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, &tm, &sm, &sblock);
+ if (r < 0) {
+ DMERR("tm_create_with_sm failed");
+ return r;
+ }
+
+ data_sm = dm_sm_disk_create(tm, nr_blocks);
+ if (IS_ERR(data_sm)) {
+ DMERR("sm_disk_create failed");
+ r = PTR_ERR(data_sm);
+ goto bad;
+ }
+ } else {
+ struct thin_disk_superblock *disk_super = NULL;
+ size_t space_map_root_offset =
+ offsetof(struct thin_disk_superblock, metadata_space_map_root);
+
+ r = dm_tm_open_with_sm(bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, space_map_root_offset,
+ SPACE_MAP_ROOT_SIZE, &tm, &sm, &sblock);
+ if (r < 0) {
+ DMERR("tm_open_with_sm failed");
+ return r;
+ }
+
+ disk_super = dm_block_data(sblock);
+ data_sm = dm_sm_disk_open(tm, disk_super->data_space_map_root,
+ sizeof(disk_super->data_space_map_root));
+ if (IS_ERR(data_sm)) {
+ DMERR("sm_disk_open failed");
+ r = PTR_ERR(data_sm);
+ goto bad;
+ }
+ }
+
+
+ r = dm_tm_unlock(tm, sblock);
+ if (r < 0) {
+ DMERR("couldn't unlock superblock");
+ goto bad_data_sm;
+ }
+
+ pmd->bm = bm;
+ pmd->metadata_sm = sm;
+ pmd->data_sm = data_sm;
+ pmd->tm = tm;
+ pmd->nb_tm = dm_tm_create_non_blocking_clone(tm);
+ if (!pmd->nb_tm) {
+ DMERR("could not create clone tm");
+ r = -ENOMEM;
+ goto bad_data_sm;
+ }
+
+ pmd->info.tm = tm;
+ pmd->info.levels = 2;
+ pmd->info.value_type.context = pmd->data_sm;
+ pmd->info.value_type.size = sizeof(__le64);
+ pmd->info.value_type.inc = data_block_inc;
+ pmd->info.value_type.dec = data_block_dec;
+ pmd->info.value_type.equal = data_block_equal;
+
+ memcpy(&pmd->nb_info, &pmd->info, sizeof(pmd->nb_info));
+ pmd->nb_info.tm = pmd->nb_tm;
+
+ pmd->tl_info.tm = tm;
+ pmd->tl_info.levels = 1;
+ pmd->tl_info.value_type.context = &pmd->info;
+ pmd->tl_info.value_type.size = sizeof(__le64);
+ pmd->tl_info.value_type.inc = subtree_inc;
+ pmd->tl_info.value_type.dec = subtree_dec;
+ pmd->tl_info.value_type.equal = subtree_equal;
+
+ pmd->bl_info.tm = tm;
+ pmd->bl_info.levels = 1;
+ pmd->bl_info.value_type.context = pmd->data_sm;
+ pmd->bl_info.value_type.size = sizeof(__le64);
+ pmd->bl_info.value_type.inc = data_block_inc;
+ pmd->bl_info.value_type.dec = data_block_dec;
+ pmd->bl_info.value_type.equal = data_block_equal;
+
+ pmd->details_info.tm = tm;
+ pmd->details_info.levels = 1;
+ pmd->details_info.value_type.context = NULL;
+ pmd->details_info.value_type.size = sizeof(struct disk_device_details);
+ pmd->details_info.value_type.inc = NULL;
+ pmd->details_info.value_type.dec = NULL;
+ pmd->details_info.value_type.equal = NULL;
+
+ pmd->root = 0;
+
+ init_rwsem(&pmd->root_lock);
+ pmd->time = 0;
+ pmd->need_commit = 0;
+ pmd->details_root = 0;
+ pmd->trans_id = 0;
+ pmd->flags = 0;
+ INIT_LIST_HEAD(&pmd->thin_devices);
+
+ return 0;
+
+bad_data_sm:
+ dm_sm_destroy(data_sm);
+bad:
+ dm_tm_destroy(tm);
+ dm_sm_destroy(sm);
+
+ return r;
+}
+
+static int __begin_transaction(struct dm_pool_metadata *pmd)
+{
+ int r;
+ u32 features;
+ struct thin_disk_superblock *disk_super;
+ struct dm_block *sblock;
+
+ /*
+ * __maybe_commit_transaction() resets these
+ */
+ WARN_ON(pmd->need_commit);
+
+ /*
+ * We re-read the superblock every time. Shouldn't need to do this
+ * really.
+ */
+ r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, &sblock);
+ if (r)
+ return r;
+
+ disk_super = dm_block_data(sblock);
+ pmd->time = le32_to_cpu(disk_super->time);
+ pmd->root = le64_to_cpu(disk_super->data_mapping_root);
+ pmd->details_root = le64_to_cpu(disk_super->device_details_root);
+ pmd->trans_id = le64_to_cpu(disk_super->trans_id);
+ pmd->flags = le32_to_cpu(disk_super->flags);
+ pmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
+
+ features = le32_to_cpu(disk_super->incompat_flags) & ~THIN_FEATURE_INCOMPAT_SUPP;
+ if (features) {
+ DMERR("could not access metadata due to "
+ "unsupported optional features (%lx).",
+ (unsigned long)features);
+ r = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Check for read-only metadata to skip the following RDWR checks.
+ */
+ if (get_disk_ro(pmd->bdev->bd_disk))
+ goto out;
+
+ features = le32_to_cpu(disk_super->compat_ro_flags) & ~THIN_FEATURE_COMPAT_RO_SUPP;
+ if (features) {
+ DMERR("could not access metadata RDWR due to "
+ "unsupported optional features (%lx).",
+ (unsigned long)features);
+ r = -EINVAL;
+ }
+
+out:
+ dm_bm_unlock(sblock);
+ return r;
+}
+
+static int __write_changed_details(struct dm_pool_metadata *pmd)
+{
+ int r;
+ struct dm_thin_device *td, *tmp;
+ struct disk_device_details details;
+ uint64_t key;
+
+ list_for_each_entry_safe(td, tmp, &pmd->thin_devices, list) {
+ if (!td->changed)
+ continue;
+
+ key = td->id;
+
+ details.mapped_blocks = cpu_to_le64(td->mapped_blocks);
+ details.transaction_id = cpu_to_le64(td->transaction_id);
+ details.creation_time = cpu_to_le32(td->creation_time);
+ details.snapshotted_time = cpu_to_le32(td->snapshotted_time);
+ __dm_bless_for_disk(&details);
+
+ r = dm_btree_insert(&pmd->details_info, pmd->details_root,
+ &key, &details, &pmd->details_root);
+ if (r)
+ return r;
+
+ if (td->open_count)
+ td->changed = 0;
+ else {
+ list_del(&td->list);
+ kfree(td);
+ }
+
+ pmd->need_commit = 1;
+ }
+
+ return 0;
+}
+
+static int __commit_transaction(struct dm_pool_metadata *pmd)
+{
+ /*
+ * FIXME: Associated pool should be made read-only on failure.
+ */
+ int r;
+ size_t metadata_len, data_len;
+ struct thin_disk_superblock *disk_super;
+ struct dm_block *sblock;
+
+ /*
+ * We need to know if the thin_disk_superblock exceeds a 512-byte sector.
+ */
+ BUILD_BUG_ON(sizeof(struct thin_disk_superblock) > 512);
+
+ r = __write_changed_details(pmd);
+ if (r < 0)
+ goto out;
+
+ if (!pmd->need_commit)
+ goto out;
+
+ r = dm_sm_commit(pmd->data_sm);
+ if (r < 0)
+ goto out;
+
+ r = dm_tm_pre_commit(pmd->tm);
+ if (r < 0)
+ goto out;
+
+ r = dm_sm_root_size(pmd->metadata_sm, &metadata_len);
+ if (r < 0)
+ goto out;
+
+ r = dm_sm_root_size(pmd->metadata_sm, &data_len);
+ if (r < 0)
+ goto out;
+
+ r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, &sblock);
+ if (r)
+ goto out;
+
+ disk_super = dm_block_data(sblock);
+ disk_super->time = cpu_to_le32(pmd->time);
+ disk_super->data_mapping_root = cpu_to_le64(pmd->root);
+ disk_super->device_details_root = cpu_to_le64(pmd->details_root);
+ disk_super->trans_id = cpu_to_le64(pmd->trans_id);
+ disk_super->flags = cpu_to_le32(pmd->flags);
+
+ r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
+ metadata_len);
+ if (r < 0)
+ goto out_locked;
+
+ r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
+ data_len);
+ if (r < 0)
+ goto out_locked;
+
+ r = dm_tm_commit(pmd->tm, sblock);
+ if (!r)
+ pmd->need_commit = 0;
+
+out:
+ return r;
+
+out_locked:
+ dm_bm_unlock(sblock);
+ return r;
+}
+
+struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
+ sector_t data_block_size)
+{
+ int r;
+ struct thin_disk_superblock *disk_super;
+ struct dm_pool_metadata *pmd;
+ sector_t bdev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
+ struct dm_block_manager *bm;
+ int create;
+ struct dm_block *sblock;
+
+ pmd = kmalloc(sizeof(*pmd), GFP_KERNEL);
+ if (!pmd) {
+ DMERR("could not allocate metadata struct");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /*
+ * Max hex locks:
+ * 3 for btree insert +
+ * 2 for btree lookup used within space map
+ */
+ bm = dm_block_manager_create(bdev, THIN_METADATA_BLOCK_SIZE,
+ THIN_METADATA_CACHE_SIZE, 5);
+ if (!bm) {
+ DMERR("could not create block manager");
+ kfree(pmd);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ r = superblock_all_zeroes(bm, &create);
+ if (r) {
+ dm_block_manager_destroy(bm);
+ kfree(pmd);
+ return ERR_PTR(r);
+ }
+
+
+ r = init_pmd(pmd, bm, 0, create);
+ if (r) {
+ dm_block_manager_destroy(bm);
+ kfree(pmd);
+ return ERR_PTR(r);
+ }
+ pmd->bdev = bdev;
+
+ if (!create) {
+ r = __begin_transaction(pmd);
+ if (r < 0)
+ goto bad;
+ return pmd;
+ }
+
+ /*
+ * Create.
+ */
+ r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, &sblock);
+ if (r)
+ goto bad;
+
+ disk_super = dm_block_data(sblock);
+ disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC);
+ disk_super->version = cpu_to_le32(THIN_VERSION);
+ disk_super->time = 0;
+ disk_super->metadata_block_size = cpu_to_le32(THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
+ disk_super->metadata_nr_blocks = cpu_to_le64(bdev_size >> SECTOR_TO_BLOCK_SHIFT);
+ disk_super->data_block_size = cpu_to_le32(data_block_size);
+
+ r = dm_bm_unlock(sblock);
+ if (r < 0)
+ goto bad;
+
+ r = dm_btree_empty(&pmd->info, &pmd->root);
+ if (r < 0)
+ goto bad;
+
+ r = dm_btree_empty(&pmd->details_info, &pmd->details_root);
+ if (r < 0) {
+ DMERR("couldn't create devices root");
+ goto bad;
+ }
+
+ pmd->flags = 0;
+ pmd->need_commit = 1;
+ r = dm_pool_commit_metadata(pmd);
+ if (r < 0) {
+ DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
+ __func__, r);
+ goto bad;
+ }
+
+ return pmd;
+
+bad:
+ if (dm_pool_metadata_close(pmd) < 0)
+ DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
+ return ERR_PTR(r);
+}
+
+int dm_pool_metadata_close(struct dm_pool_metadata *pmd)
+{
+ int r;
+ unsigned open_devices = 0;
+ struct dm_thin_device *td, *tmp;
+
+ down_read(&pmd->root_lock);
+ list_for_each_entry_safe(td, tmp, &pmd->thin_devices, list) {
+ if (td->open_count)
+ open_devices++;
+ else {
+ list_del(&td->list);
+ kfree(td);
+ }
+ }
+ up_read(&pmd->root_lock);
+
+ if (open_devices) {
+ DMERR("attempt to close pmd when %u device(s) are still open",
+ open_devices);
+ return -EBUSY;
+ }
+
+ r = __commit_transaction(pmd);
+ if (r < 0)
+ DMWARN("%s: __commit_transaction() failed, error = %d",
+ __func__, r);
+
+ dm_tm_destroy(pmd->tm);
+ dm_tm_destroy(pmd->nb_tm);
+ dm_block_manager_destroy(pmd->bm);
+ dm_sm_destroy(pmd->metadata_sm);
+ dm_sm_destroy(pmd->data_sm);
+ kfree(pmd);
+
+ return 0;
+}
+
+static int __open_device(struct dm_pool_metadata *pmd,
+ dm_thin_id dev, int create,
+ struct dm_thin_device **td)
+{
+ int r, changed = 0;
+ struct dm_thin_device *td2;
+ uint64_t key = dev;
+ struct disk_device_details details_le;
+
+ /*
+ * Check the device isn't already open.
+ */
+ list_for_each_entry(td2, &pmd->thin_devices, list)
+ if (td2->id == dev) {
+ td2->open_count++;
+ *td = td2;
+ return 0;
+ }
+
+ /*
+ * Check the device exists.
+ */
+ r = dm_btree_lookup(&pmd->details_info, pmd->details_root,
+ &key, &details_le);
+ if (r) {
+ if (r != -ENODATA || !create)
+ return r;
+
+ changed = 1;
+ details_le.mapped_blocks = 0;
+ details_le.transaction_id = cpu_to_le64(pmd->trans_id);
+ details_le.creation_time = cpu_to_le32(pmd->time);
+ details_le.snapshotted_time = cpu_to_le32(pmd->time);
+ }
+
+ *td = kmalloc(sizeof(**td), GFP_NOIO);
+ if (!*td)
+ return -ENOMEM;
+
+ (*td)->pmd = pmd;
+ (*td)->id = dev;
+ (*td)->open_count = 1;
+ (*td)->changed = changed;
+ (*td)->mapped_blocks = le64_to_cpu(details_le.mapped_blocks);
+ (*td)->transaction_id = le64_to_cpu(details_le.transaction_id);
+ (*td)->creation_time = le32_to_cpu(details_le.creation_time);
+ (*td)->snapshotted_time = le32_to_cpu(details_le.snapshotted_time);
+
+ list_add(&(*td)->list, &pmd->thin_devices);
+
+ return 0;
+}
+
+static void __close_device(struct dm_thin_device *td)
+{
+ --td->open_count;
+}
+
+static int __create_thin(struct dm_pool_metadata *pmd,
+ dm_thin_id dev)
+{
+ int r;
+ dm_block_t dev_root;
+ uint64_t key = dev;
+ struct disk_device_details details_le;
+ struct dm_thin_device *td;
+ __le64 value;
+
+ r = dm_btree_lookup(&pmd->details_info, pmd->details_root,
+ &key, &details_le);
+ if (!r)
+ return -EEXIST;
+
+ /*
+ * Create an empty btree for the mappings.
+ */
+ r = dm_btree_empty(&pmd->bl_info, &dev_root);
+ if (r)
+ return r;
+
+ /*
+ * Insert it into the main mapping tree.
+ */
+ value = cpu_to_le64(dev_root);
+ __dm_bless_for_disk(&value);
+ r = dm_btree_insert(&pmd->tl_info, pmd->root, &key, &value, &pmd->root);
+ if (r) {
+ dm_btree_del(&pmd->bl_info, dev_root);
+ return r;
+ }
+
+ r = __open_device(pmd, dev, 1, &td);
+ if (r) {
+ __close_device(td);
+ dm_btree_remove(&pmd->tl_info, pmd->root, &key, &pmd->root);
+ dm_btree_del(&pmd->bl_info, dev_root);
+ return r;
+ }
+ td->changed = 1;
+ __close_device(td);
+
+ return r;
+}
+
+int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+ r = __create_thin(pmd, dev);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+static int __set_snapshot_details(struct dm_pool_metadata *pmd,
+ struct dm_thin_device *snap,
+ dm_thin_id origin, uint32_t time)
+{
+ int r;
+ struct dm_thin_device *td;
+
+ r = __open_device(pmd, origin, 0, &td);
+ if (r)
+ return r;
+
+ td->changed = 1;
+ td->snapshotted_time = time;
+
+ snap->mapped_blocks = td->mapped_blocks;
+ snap->snapshotted_time = time;
+ __close_device(td);
+
+ return 0;
+}
+
+static int __create_snap(struct dm_pool_metadata *pmd,
+ dm_thin_id dev, dm_thin_id origin)
+{
+ int r;
+ dm_block_t origin_root;
+ uint64_t key = origin, dev_key = dev;
+ struct dm_thin_device *td;
+ struct disk_device_details details_le;
+ __le64 value;
+
+ /* check this device is unused */
+ r = dm_btree_lookup(&pmd->details_info, pmd->details_root,
+ &dev_key, &details_le);
+ if (!r)
+ return -EEXIST;
+
+ /* find the mapping tree for the origin */
+ r = dm_btree_lookup(&pmd->tl_info, pmd->root, &key, &value);
+ if (r)
+ return r;
+ origin_root = le64_to_cpu(value);
+
+ /* clone the origin, an inc will do */
+ dm_tm_inc(pmd->tm, origin_root);
+
+ /* insert into the main mapping tree */
+ value = cpu_to_le64(origin_root);
+ __dm_bless_for_disk(&value);
+ key = dev;
+ r = dm_btree_insert(&pmd->tl_info, pmd->root, &key, &value, &pmd->root);
+ if (r) {
+ dm_tm_dec(pmd->tm, origin_root);
+ return r;
+ }
+
+ pmd->time++;
+
+ r = __open_device(pmd, dev, 1, &td);
+ if (r)
+ goto bad;
+
+ r = __set_snapshot_details(pmd, td, origin, pmd->time);
+ if (r)
+ goto bad;
+
+ __close_device(td);
+ return 0;
+
+bad:
+ __close_device(td);
+ dm_btree_remove(&pmd->tl_info, pmd->root, &key, &pmd->root);
+ dm_btree_remove(&pmd->details_info, pmd->details_root,
+ &key, &pmd->details_root);
+ return r;
+}
+
+int dm_pool_create_snap(struct dm_pool_metadata *pmd,
+ dm_thin_id dev,
+ dm_thin_id origin)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+ r = __create_snap(pmd, dev, origin);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+static int __delete_device(struct dm_pool_metadata *pmd, dm_thin_id dev)
+{
+ int r;
+ uint64_t key = dev;
+ struct dm_thin_device *td;
+
+ /* TODO: failure should mark the transaction invalid */
+ r = __open_device(pmd, dev, 0, &td);
+ if (r)
+ return r;
+
+ if (td->open_count > 1) {
+ __close_device(td);
+ return -EBUSY;
+ }
+
+ list_del(&td->list);
+ kfree(td);
+ r = dm_btree_remove(&pmd->details_info, pmd->details_root,
+ &key, &pmd->details_root);
+ if (r)
+ return r;
+
+ r = dm_btree_remove(&pmd->tl_info, pmd->root, &key, &pmd->root);
+ if (r)
+ return r;
+
+ pmd->need_commit = 1;
+
+ return 0;
+}
+
+int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd,
+ dm_thin_id dev)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+ r = __delete_device(pmd, dev);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd,
+ uint64_t current_id,
+ uint64_t new_id)
+{
+ down_write(&pmd->root_lock);
+ if (pmd->trans_id != current_id) {
+ up_write(&pmd->root_lock);
+ DMERR("mismatched transaction id");
+ return -EINVAL;
+ }
+
+ pmd->trans_id = new_id;
+ pmd->need_commit = 1;
+ up_write(&pmd->root_lock);
+
+ return 0;
+}
+
+int dm_pool_get_metadata_transaction_id(struct dm_pool_metadata *pmd,
+ uint64_t *result)
+{
+ down_read(&pmd->root_lock);
+ *result = pmd->trans_id;
+ up_read(&pmd->root_lock);
+
+ return 0;
+}
+
+static int __get_held_metadata_root(struct dm_pool_metadata *pmd,
+ dm_block_t *result)
+{
+ int r;
+ struct thin_disk_superblock *disk_super;
+ struct dm_block *sblock;
+
+ r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, &sblock);
+ if (r)
+ return r;
+
+ disk_super = dm_block_data(sblock);
+ *result = le64_to_cpu(disk_super->held_root);
+
+ return dm_bm_unlock(sblock);
+}
+
+int dm_pool_get_held_metadata_root(struct dm_pool_metadata *pmd,
+ dm_block_t *result)
+{
+ int r;
+
+ down_read(&pmd->root_lock);
+ r = __get_held_metadata_root(pmd, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev,
+ struct dm_thin_device **td)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+ r = __open_device(pmd, dev, 0, td);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_close_thin_device(struct dm_thin_device *td)
+{
+ down_write(&td->pmd->root_lock);
+ __close_device(td);
+ up_write(&td->pmd->root_lock);
+
+ return 0;
+}
+
+dm_thin_id dm_thin_dev_id(struct dm_thin_device *td)
+{
+ return td->id;
+}
+
+static int __snapshotted_since(struct dm_thin_device *td, uint32_t time)
+{
+ return td->snapshotted_time > time;
+}
+
+int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
+ int can_block, struct dm_thin_lookup_result *result)
+{
+ int r;
+ uint64_t block_time = 0;
+ __le64 value;
+ struct dm_pool_metadata *pmd = td->pmd;
+ dm_block_t keys[2] = { td->id, block };
+
+ if (can_block) {
+ down_read(&pmd->root_lock);
+ r = dm_btree_lookup(&pmd->info, pmd->root, keys, &value);
+ if (!r)
+ block_time = le64_to_cpu(value);
+ up_read(&pmd->root_lock);
+
+ } else if (down_read_trylock(&pmd->root_lock)) {
+ r = dm_btree_lookup(&pmd->nb_info, pmd->root, keys, &value);
+ if (!r)
+ block_time = le64_to_cpu(value);
+ up_read(&pmd->root_lock);
+
+ } else
+ return -EWOULDBLOCK;
+
+ if (!r) {
+ dm_block_t exception_block;
+ uint32_t exception_time;
+ unpack_block_time(block_time, &exception_block,
+ &exception_time);
+ result->block = exception_block;
+ result->shared = __snapshotted_since(td, exception_time);
+ }
+
+ return r;
+}
+
+static int __insert(struct dm_thin_device *td, dm_block_t block,
+ dm_block_t data_block)
+{
+ int r, inserted;
+ __le64 value;
+ struct dm_pool_metadata *pmd = td->pmd;
+ dm_block_t keys[2] = { td->id, block };
+
+ pmd->need_commit = 1;
+ value = cpu_to_le64(pack_block_time(data_block, pmd->time));
+ __dm_bless_for_disk(&value);
+
+ r = dm_btree_insert_notify(&pmd->info, pmd->root, keys, &value,
+ &pmd->root, &inserted);
+ if (r)
+ return r;
+
+ if (inserted) {
+ td->mapped_blocks++;
+ td->changed = 1;
+ }
+
+ return 0;
+}
+
+int dm_thin_insert_block(struct dm_thin_device *td, dm_block_t block,
+ dm_block_t data_block)
+{
+ int r;
+
+ down_write(&td->pmd->root_lock);
+ r = __insert(td, block, data_block);
+ up_write(&td->pmd->root_lock);
+
+ return r;
+}
+
+static int __remove(struct dm_thin_device *td, dm_block_t block)
+{
+ int r;
+ struct dm_pool_metadata *pmd = td->pmd;
+ dm_block_t keys[2] = { td->id, block };
+
+ r = dm_btree_remove(&pmd->info, pmd->root, keys, &pmd->root);
+ if (r)
+ return r;
+
+ pmd->need_commit = 1;
+
+ return 0;
+}
+
+int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block)
+{
+ int r;
+
+ down_write(&td->pmd->root_lock);
+ r = __remove(td, block);
+ up_write(&td->pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+
+ r = dm_sm_new_block(pmd->data_sm, result);
+ pmd->need_commit = 1;
+
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_commit_metadata(struct dm_pool_metadata *pmd)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+
+ r = __commit_transaction(pmd);
+ if (r <= 0)
+ goto out;
+
+ /*
+ * Open the next transaction.
+ */
+ r = __begin_transaction(pmd);
+out:
+ up_write(&pmd->root_lock);
+ return r;
+}
+
+int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd, dm_block_t *result)
+{
+ int r;
+
+ down_read(&pmd->root_lock);
+ r = dm_sm_get_nr_free(pmd->data_sm, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
+ dm_block_t *result)
+{
+ int r;
+
+ down_read(&pmd->root_lock);
+ r = dm_sm_get_nr_free(pmd->metadata_sm, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_get_metadata_dev_size(struct dm_pool_metadata *pmd,
+ dm_block_t *result)
+{
+ int r;
+
+ down_read(&pmd->root_lock);
+ r = dm_sm_get_nr_blocks(pmd->metadata_sm, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_get_data_block_size(struct dm_pool_metadata *pmd, sector_t *result)
+{
+ down_read(&pmd->root_lock);
+ *result = pmd->data_block_size;
+ up_read(&pmd->root_lock);
+
+ return 0;
+}
+
+int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result)
+{
+ int r;
+
+ down_read(&pmd->root_lock);
+ r = dm_sm_get_nr_blocks(pmd->data_sm, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_thin_get_mapped_count(struct dm_thin_device *td, dm_block_t *result)
+{
+ struct dm_pool_metadata *pmd = td->pmd;
+
+ down_read(&pmd->root_lock);
+ *result = td->mapped_blocks;
+ up_read(&pmd->root_lock);
+
+ return 0;
+}
+
+static int __highest_block(struct dm_thin_device *td, dm_block_t *result)
+{
+ int r;
+ __le64 value_le;
+ dm_block_t thin_root;
+ struct dm_pool_metadata *pmd = td->pmd;
+
+ r = dm_btree_lookup(&pmd->tl_info, pmd->root, &td->id, &value_le);
+ if (r)
+ return r;
+
+ thin_root = le64_to_cpu(value_le);
+
+ return dm_btree_find_highest_key(&pmd->bl_info, thin_root, result);
+}
+
+int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
+ dm_block_t *result)
+{
+ int r;
+ struct dm_pool_metadata *pmd = td->pmd;
+
+ down_read(&pmd->root_lock);
+ r = __highest_block(td, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+static int __resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
+{
+ int r;
+ dm_block_t old_count;
+
+ r = dm_sm_get_nr_blocks(pmd->data_sm, &old_count);
+ if (r)
+ return r;
+
+ if (new_count == old_count)
+ return 0;
+
+ if (new_count < old_count) {
+ DMERR("cannot reduce size of data device");
+ return -EINVAL;
+ }
+
+ r = dm_sm_extend(pmd->data_sm, new_count - old_count);
+ if (!r)
+ pmd->need_commit = 1;
+
+ return r;
+}
+
+int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+ r = __resize_data_dev(pmd, new_count);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h
new file mode 100644
index 000000000000..859c16896877
--- /dev/null
+++ b/drivers/md/dm-thin-metadata.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2010-2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_THIN_METADATA_H
+#define DM_THIN_METADATA_H
+
+#include "persistent-data/dm-block-manager.h"
+
+#define THIN_METADATA_BLOCK_SIZE 4096
+
+/*----------------------------------------------------------------*/
+
+struct dm_pool_metadata;
+struct dm_thin_device;
+
+/*
+ * Device identifier
+ */
+typedef uint64_t dm_thin_id;
+
+/*
+ * Reopens or creates a new, empty metadata volume.
+ */
+struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
+ sector_t data_block_size);
+
+int dm_pool_metadata_close(struct dm_pool_metadata *pmd);
+
+/*
+ * Compat feature flags. Any incompat flags beyond the ones
+ * specified below will prevent use of the thin metadata.
+ */
+#define THIN_FEATURE_COMPAT_SUPP 0UL
+#define THIN_FEATURE_COMPAT_RO_SUPP 0UL
+#define THIN_FEATURE_INCOMPAT_SUPP 0UL
+
+/*
+ * Device creation/deletion.
+ */
+int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev);
+
+/*
+ * An internal snapshot.
+ *
+ * You can only snapshot a quiesced origin i.e. one that is either
+ * suspended or not instanced at all.
+ */
+int dm_pool_create_snap(struct dm_pool_metadata *pmd, dm_thin_id dev,
+ dm_thin_id origin);
+
+/*
+ * Deletes a virtual device from the metadata. It _is_ safe to call this
+ * when that device is open. Operations on that device will just start
+ * failing. You still need to call close() on the device.
+ */
+int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd,
+ dm_thin_id dev);
+
+/*
+ * Commits _all_ metadata changes: device creation, deletion, mapping
+ * updates.
+ */
+int dm_pool_commit_metadata(struct dm_pool_metadata *pmd);
+
+/*
+ * Set/get userspace transaction id.
+ */
+int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd,
+ uint64_t current_id,
+ uint64_t new_id);
+
+int dm_pool_get_metadata_transaction_id(struct dm_pool_metadata *pmd,
+ uint64_t *result);
+
+/*
+ * Hold/get root for userspace transaction.
+ */
+int dm_pool_hold_metadata_root(struct dm_pool_metadata *pmd);
+
+int dm_pool_get_held_metadata_root(struct dm_pool_metadata *pmd,
+ dm_block_t *result);
+
+/*
+ * Actions on a single virtual device.
+ */
+
+/*
+ * Opening the same device more than once will fail with -EBUSY.
+ */
+int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev,
+ struct dm_thin_device **td);
+
+int dm_pool_close_thin_device(struct dm_thin_device *td);
+
+dm_thin_id dm_thin_dev_id(struct dm_thin_device *td);
+
+struct dm_thin_lookup_result {
+ dm_block_t block;
+ int shared;
+};
+
+/*
+ * Returns:
+ * -EWOULDBLOCK iff @can_block is set and would block.
+ * -ENODATA iff that mapping is not present.
+ * 0 success
+ */
+int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
+ int can_block, struct dm_thin_lookup_result *result);
+
+/*
+ * Obtain an unused block.
+ */
+int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result);
+
+/*
+ * Insert or remove block.
+ */
+int dm_thin_insert_block(struct dm_thin_device *td, dm_block_t block,
+ dm_block_t data_block);
+
+int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block);
+
+/*
+ * Queries.
+ */
+int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
+ dm_block_t *highest_mapped);
+
+int dm_thin_get_mapped_count(struct dm_thin_device *td, dm_block_t *result);
+
+int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd,
+ dm_block_t *result);
+
+int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
+ dm_block_t *result);
+
+int dm_pool_get_metadata_dev_size(struct dm_pool_metadata *pmd,
+ dm_block_t *result);
+
+int dm_pool_get_data_block_size(struct dm_pool_metadata *pmd, sector_t *result);
+
+int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result);
+
+/*
+ * Returns -ENOSPC if the new size is too small and already allocated
+ * blocks would be lost.
+ */
+int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_size);
+
+/*----------------------------------------------------------------*/
+
+#endif
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
new file mode 100644
index 000000000000..c3087575fef0
--- /dev/null
+++ b/drivers/md/dm-thin.c
@@ -0,0 +1,2428 @@
+/*
+ * Copyright (C) 2011 Red Hat UK.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-thin-metadata.h"
+
+#include <linux/device-mapper.h>
+#include <linux/dm-io.h>
+#include <linux/dm-kcopyd.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#define DM_MSG_PREFIX "thin"
+
+/*
+ * Tunable constants
+ */
+#define ENDIO_HOOK_POOL_SIZE 10240
+#define DEFERRED_SET_SIZE 64
+#define MAPPING_POOL_SIZE 1024
+#define PRISON_CELLS 1024
+
+/*
+ * The block size of the device holding pool data must be
+ * between 64KB and 1GB.
+ */
+#define DATA_DEV_BLOCK_SIZE_MIN_SECTORS (64 * 1024 >> SECTOR_SHIFT)
+#define DATA_DEV_BLOCK_SIZE_MAX_SECTORS (1024 * 1024 * 1024 >> SECTOR_SHIFT)
+
+/*
+ * The metadata device is currently limited in size. The limitation is
+ * checked lower down in dm-space-map-metadata, but we also check it here
+ * so we can fail early.
+ *
+ * We have one block of index, which can hold 255 index entries. Each
+ * index entry contains allocation info about 16k metadata blocks.
+ */
+#define METADATA_DEV_MAX_SECTORS (255 * (1 << 14) * (THIN_METADATA_BLOCK_SIZE / (1 << SECTOR_SHIFT)))
+
+/*
+ * Device id is restricted to 24 bits.
+ */
+#define MAX_DEV_ID ((1 << 24) - 1)
+
+/*
+ * How do we handle breaking sharing of data blocks?
+ * =================================================
+ *
+ * We use a standard copy-on-write btree to store the mappings for the
+ * devices (note I'm talking about copy-on-write of the metadata here, not
+ * the data). When you take an internal snapshot you clone the root node
+ * of the origin btree. After this there is no concept of an origin or a
+ * snapshot. They are just two device trees that happen to point to the
+ * same data blocks.
+ *
+ * When we get a write in we decide if it's to a shared data block using
+ * some timestamp magic. If it is, we have to break sharing.
+ *
+ * Let's say we write to a shared block in what was the origin. The
+ * steps are:
+ *
+ * i) plug io further to this physical block. (see bio_prison code).
+ *
+ * ii) quiesce any read io to that shared data block. Obviously
+ * including all devices that share this block. (see deferred_set code)
+ *
+ * iii) copy the data block to a newly allocate block. This step can be
+ * missed out if the io covers the block. (schedule_copy).
+ *
+ * iv) insert the new mapping into the origin's btree
+ * (process_prepared_mappings). This act of inserting breaks some
+ * sharing of btree nodes between the two devices. Breaking sharing only
+ * effects the btree of that specific device. Btrees for the other
+ * devices that share the block never change. The btree for the origin
+ * device as it was after the last commit is untouched, ie. we're using
+ * persistent data structures in the functional programming sense.
+ *
+ * v) unplug io to this physical block, including the io that triggered
+ * the breaking of sharing.
+ *
+ * Steps (ii) and (iii) occur in parallel.
+ *
+ * The metadata _doesn't_ need to be committed before the io continues. We
+ * get away with this because the io is always written to a _new_ block.
+ * If there's a crash, then:
+ *
+ * - The origin mapping will point to the old origin block (the shared
+ * one). This will contain the data as it was before the io that triggered
+ * the breaking of sharing came in.
+ *
+ * - The snap mapping still points to the old block. As it would after
+ * the commit.
+ *
+ * The downside of this scheme is the timestamp magic isn't perfect, and
+ * will continue to think that data block in the snapshot device is shared
+ * even after the write to the origin has broken sharing. I suspect data
+ * blocks will typically be shared by many different devices, so we're
+ * breaking sharing n + 1 times, rather than n, where n is the number of
+ * devices that reference this data block. At the moment I think the
+ * benefits far, far outweigh the disadvantages.
+ */
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Sometimes we can't deal with a bio straight away. We put them in prison
+ * where they can't cause any mischief. Bios are put in a cell identified
+ * by a key, multiple bios can be in the same cell. When the cell is
+ * subsequently unlocked the bios become available.
+ */
+struct bio_prison;
+
+struct cell_key {
+ int virtual;
+ dm_thin_id dev;
+ dm_block_t block;
+};
+
+struct cell {
+ struct hlist_node list;
+ struct bio_prison *prison;
+ struct cell_key key;
+ unsigned count;
+ struct bio_list bios;
+};
+
+struct bio_prison {
+ spinlock_t lock;
+ mempool_t *cell_pool;
+
+ unsigned nr_buckets;
+ unsigned hash_mask;
+ struct hlist_head *cells;
+};
+
+static uint32_t calc_nr_buckets(unsigned nr_cells)
+{
+ uint32_t n = 128;
+
+ nr_cells /= 4;
+ nr_cells = min(nr_cells, 8192u);
+
+ while (n < nr_cells)
+ n <<= 1;
+
+ return n;
+}
+
+/*
+ * @nr_cells should be the number of cells you want in use _concurrently_.
+ * Don't confuse it with the number of distinct keys.
+ */
+static struct bio_prison *prison_create(unsigned nr_cells)
+{
+ unsigned i;
+ uint32_t nr_buckets = calc_nr_buckets(nr_cells);
+ size_t len = sizeof(struct bio_prison) +
+ (sizeof(struct hlist_head) * nr_buckets);
+ struct bio_prison *prison = kmalloc(len, GFP_KERNEL);
+
+ if (!prison)
+ return NULL;
+
+ spin_lock_init(&prison->lock);
+ prison->cell_pool = mempool_create_kmalloc_pool(nr_cells,
+ sizeof(struct cell));
+ if (!prison->cell_pool) {
+ kfree(prison);
+ return NULL;
+ }
+
+ prison->nr_buckets = nr_buckets;
+ prison->hash_mask = nr_buckets - 1;
+ prison->cells = (struct hlist_head *) (prison + 1);
+ for (i = 0; i < nr_buckets; i++)
+ INIT_HLIST_HEAD(prison->cells + i);
+
+ return prison;
+}
+
+static void prison_destroy(struct bio_prison *prison)
+{
+ mempool_destroy(prison->cell_pool);
+ kfree(prison);
+}
+
+static uint32_t hash_key(struct bio_prison *prison, struct cell_key *key)
+{
+ const unsigned long BIG_PRIME = 4294967291UL;
+ uint64_t hash = key->block * BIG_PRIME;
+
+ return (uint32_t) (hash & prison->hash_mask);
+}
+
+static int keys_equal(struct cell_key *lhs, struct cell_key *rhs)
+{
+ return (lhs->virtual == rhs->virtual) &&
+ (lhs->dev == rhs->dev) &&
+ (lhs->block == rhs->block);
+}
+
+static struct cell *__search_bucket(struct hlist_head *bucket,
+ struct cell_key *key)
+{
+ struct cell *cell;
+ struct hlist_node *tmp;
+
+ hlist_for_each_entry(cell, tmp, bucket, list)
+ if (keys_equal(&cell->key, key))
+ return cell;
+
+ return NULL;
+}
+
+/*
+ * This may block if a new cell needs allocating. You must ensure that
+ * cells will be unlocked even if the calling thread is blocked.
+ *
+ * Returns the number of entries in the cell prior to the new addition
+ * or < 0 on failure.
+ */
+static int bio_detain(struct bio_prison *prison, struct cell_key *key,
+ struct bio *inmate, struct cell **ref)
+{
+ int r;
+ unsigned long flags;
+ uint32_t hash = hash_key(prison, key);
+ struct cell *uninitialized_var(cell), *cell2 = NULL;
+
+ BUG_ON(hash > prison->nr_buckets);
+
+ spin_lock_irqsave(&prison->lock, flags);
+ cell = __search_bucket(prison->cells + hash, key);
+
+ if (!cell) {
+ /*
+ * Allocate a new cell
+ */
+ spin_unlock_irqrestore(&prison->lock, flags);
+ cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO);
+ spin_lock_irqsave(&prison->lock, flags);
+
+ /*
+ * We've been unlocked, so we have to double check that
+ * nobody else has inserted this cell in the meantime.
+ */
+ cell = __search_bucket(prison->cells + hash, key);
+
+ if (!cell) {
+ cell = cell2;
+ cell2 = NULL;
+
+ cell->prison = prison;
+ memcpy(&cell->key, key, sizeof(cell->key));
+ cell->count = 0;
+ bio_list_init(&cell->bios);
+ hlist_add_head(&cell->list, prison->cells + hash);
+ }
+ }
+
+ r = cell->count++;
+ bio_list_add(&cell->bios, inmate);
+ spin_unlock_irqrestore(&prison->lock, flags);
+
+ if (cell2)
+ mempool_free(cell2, prison->cell_pool);
+
+ *ref = cell;
+
+ return r;
+}
+
+/*
+ * @inmates must have been initialised prior to this call
+ */
+static void __cell_release(struct cell *cell, struct bio_list *inmates)
+{
+ struct bio_prison *prison = cell->prison;
+
+ hlist_del(&cell->list);
+
+ if (inmates)
+ bio_list_merge(inmates, &cell->bios);
+
+ mempool_free(cell, prison->cell_pool);
+}
+
+static void cell_release(struct cell *cell, struct bio_list *bios)
+{
+ unsigned long flags;
+ struct bio_prison *prison = cell->prison;
+
+ spin_lock_irqsave(&prison->lock, flags);
+ __cell_release(cell, bios);
+ spin_unlock_irqrestore(&prison->lock, flags);
+}
+
+/*
+ * There are a couple of places where we put a bio into a cell briefly
+ * before taking it out again. In these situations we know that no other
+ * bio may be in the cell. This function releases the cell, and also does
+ * a sanity check.
+ */
+static void cell_release_singleton(struct cell *cell, struct bio *bio)
+{
+ struct bio_prison *prison = cell->prison;
+ struct bio_list bios;
+ struct bio *b;
+ unsigned long flags;
+
+ bio_list_init(&bios);
+
+ spin_lock_irqsave(&prison->lock, flags);
+ __cell_release(cell, &bios);
+ spin_unlock_irqrestore(&prison->lock, flags);
+
+ b = bio_list_pop(&bios);
+ BUG_ON(b != bio);
+ BUG_ON(!bio_list_empty(&bios));
+}
+
+static void cell_error(struct cell *cell)
+{
+ struct bio_prison *prison = cell->prison;
+ struct bio_list bios;
+ struct bio *bio;
+ unsigned long flags;
+
+ bio_list_init(&bios);
+
+ spin_lock_irqsave(&prison->lock, flags);
+ __cell_release(cell, &bios);
+ spin_unlock_irqrestore(&prison->lock, flags);
+
+ while ((bio = bio_list_pop(&bios)))
+ bio_io_error(bio);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * We use the deferred set to keep track of pending reads to shared blocks.
+ * We do this to ensure the new mapping caused by a write isn't performed
+ * until these prior reads have completed. Otherwise the insertion of the
+ * new mapping could free the old block that the read bios are mapped to.
+ */
+
+struct deferred_set;
+struct deferred_entry {
+ struct deferred_set *ds;
+ unsigned count;
+ struct list_head work_items;
+};
+
+struct deferred_set {
+ spinlock_t lock;
+ unsigned current_entry;
+ unsigned sweeper;
+ struct deferred_entry entries[DEFERRED_SET_SIZE];
+};
+
+static void ds_init(struct deferred_set *ds)
+{
+ int i;
+
+ spin_lock_init(&ds->lock);
+ ds->current_entry = 0;
+ ds->sweeper = 0;
+ for (i = 0; i < DEFERRED_SET_SIZE; i++) {
+ ds->entries[i].ds = ds;
+ ds->entries[i].count = 0;
+ INIT_LIST_HEAD(&ds->entries[i].work_items);
+ }
+}
+
+static struct deferred_entry *ds_inc(struct deferred_set *ds)
+{
+ unsigned long flags;
+ struct deferred_entry *entry;
+
+ spin_lock_irqsave(&ds->lock, flags);
+ entry = ds->entries + ds->current_entry;
+ entry->count++;
+ spin_unlock_irqrestore(&ds->lock, flags);
+
+ return entry;
+}
+
+static unsigned ds_next(unsigned index)
+{
+ return (index + 1) % DEFERRED_SET_SIZE;
+}
+
+static void __sweep(struct deferred_set *ds, struct list_head *head)
+{
+ while ((ds->sweeper != ds->current_entry) &&
+ !ds->entries[ds->sweeper].count) {
+ list_splice_init(&ds->entries[ds->sweeper].work_items, head);
+ ds->sweeper = ds_next(ds->sweeper);
+ }
+
+ if ((ds->sweeper == ds->current_entry) && !ds->entries[ds->sweeper].count)
+ list_splice_init(&ds->entries[ds->sweeper].work_items, head);
+}
+
+static void ds_dec(struct deferred_entry *entry, struct list_head *head)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&entry->ds->lock, flags);
+ BUG_ON(!entry->count);
+ --entry->count;
+ __sweep(entry->ds, head);
+ spin_unlock_irqrestore(&entry->ds->lock, flags);
+}
+
+/*
+ * Returns 1 if deferred or 0 if no pending items to delay job.
+ */
+static int ds_add_work(struct deferred_set *ds, struct list_head *work)
+{
+ int r = 1;
+ unsigned long flags;
+ unsigned next_entry;
+
+ spin_lock_irqsave(&ds->lock, flags);
+ if ((ds->sweeper == ds->current_entry) &&
+ !ds->entries[ds->current_entry].count)
+ r = 0;
+ else {
+ list_add(work, &ds->entries[ds->current_entry].work_items);
+ next_entry = ds_next(ds->current_entry);
+ if (!ds->entries[next_entry].count)
+ ds->current_entry = next_entry;
+ }
+ spin_unlock_irqrestore(&ds->lock, flags);
+
+ return r;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Key building.
+ */
+static void build_data_key(struct dm_thin_device *td,
+ dm_block_t b, struct cell_key *key)
+{
+ key->virtual = 0;
+ key->dev = dm_thin_dev_id(td);
+ key->block = b;
+}
+
+static void build_virtual_key(struct dm_thin_device *td, dm_block_t b,
+ struct cell_key *key)
+{
+ key->virtual = 1;
+ key->dev = dm_thin_dev_id(td);
+ key->block = b;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * A pool device ties together a metadata device and a data device. It
+ * also provides the interface for creating and destroying internal
+ * devices.
+ */
+struct new_mapping;
+struct pool {
+ struct list_head list;
+ struct dm_target *ti; /* Only set if a pool target is bound */
+
+ struct mapped_device *pool_md;
+ struct block_device *md_dev;
+ struct dm_pool_metadata *pmd;
+
+ uint32_t sectors_per_block;
+ unsigned block_shift;
+ dm_block_t offset_mask;
+ dm_block_t low_water_blocks;
+
+ unsigned zero_new_blocks:1;
+ unsigned low_water_triggered:1; /* A dm event has been sent */
+ unsigned no_free_space:1; /* A -ENOSPC warning has been issued */
+
+ struct bio_prison *prison;
+ struct dm_kcopyd_client *copier;
+
+ struct workqueue_struct *wq;
+ struct work_struct worker;
+
+ unsigned ref_count;
+
+ spinlock_t lock;
+ struct bio_list deferred_bios;
+ struct bio_list deferred_flush_bios;
+ struct list_head prepared_mappings;
+
+ struct bio_list retry_on_resume_list;
+
+ struct deferred_set ds; /* FIXME: move to thin_c */
+
+ struct new_mapping *next_mapping;
+ mempool_t *mapping_pool;
+ mempool_t *endio_hook_pool;
+};
+
+/*
+ * Target context for a pool.
+ */
+struct pool_c {
+ struct dm_target *ti;
+ struct pool *pool;
+ struct dm_dev *data_dev;
+ struct dm_dev *metadata_dev;
+ struct dm_target_callbacks callbacks;
+
+ dm_block_t low_water_blocks;
+ unsigned zero_new_blocks:1;
+};
+
+/*
+ * Target context for a thin.
+ */
+struct thin_c {
+ struct dm_dev *pool_dev;
+ dm_thin_id dev_id;
+
+ struct pool *pool;
+ struct dm_thin_device *td;
+};
+
+/*----------------------------------------------------------------*/
+
+/*
+ * A global list of pools that uses a struct mapped_device as a key.
+ */
+static struct dm_thin_pool_table {
+ struct mutex mutex;
+ struct list_head pools;
+} dm_thin_pool_table;
+
+static void pool_table_init(void)
+{
+ mutex_init(&dm_thin_pool_table.mutex);
+ INIT_LIST_HEAD(&dm_thin_pool_table.pools);
+}
+
+static void __pool_table_insert(struct pool *pool)
+{
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+ list_add(&pool->list, &dm_thin_pool_table.pools);
+}
+
+static void __pool_table_remove(struct pool *pool)
+{
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+ list_del(&pool->list);
+}
+
+static struct pool *__pool_table_lookup(struct mapped_device *md)
+{
+ struct pool *pool = NULL, *tmp;
+
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+
+ list_for_each_entry(tmp, &dm_thin_pool_table.pools, list) {
+ if (tmp->pool_md == md) {
+ pool = tmp;
+ break;
+ }
+ }
+
+ return pool;
+}
+
+static struct pool *__pool_table_lookup_metadata_dev(struct block_device *md_dev)
+{
+ struct pool *pool = NULL, *tmp;
+
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+
+ list_for_each_entry(tmp, &dm_thin_pool_table.pools, list) {
+ if (tmp->md_dev == md_dev) {
+ pool = tmp;
+ break;
+ }
+ }
+
+ return pool;
+}
+
+/*----------------------------------------------------------------*/
+
+static void __requeue_bio_list(struct thin_c *tc, struct bio_list *master)
+{
+ struct bio *bio;
+ struct bio_list bios;
+
+ bio_list_init(&bios);
+ bio_list_merge(&bios, master);
+ bio_list_init(master);
+
+ while ((bio = bio_list_pop(&bios))) {
+ if (dm_get_mapinfo(bio)->ptr == tc)
+ bio_endio(bio, DM_ENDIO_REQUEUE);
+ else
+ bio_list_add(master, bio);
+ }
+}
+
+static void requeue_io(struct thin_c *tc)
+{
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ __requeue_bio_list(tc, &pool->deferred_bios);
+ __requeue_bio_list(tc, &pool->retry_on_resume_list);
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+/*
+ * This section of code contains the logic for processing a thin device's IO.
+ * Much of the code depends on pool object resources (lists, workqueues, etc)
+ * but most is exclusively called from the thin target rather than the thin-pool
+ * target.
+ */
+
+static dm_block_t get_bio_block(struct thin_c *tc, struct bio *bio)
+{
+ return bio->bi_sector >> tc->pool->block_shift;
+}
+
+static void remap(struct thin_c *tc, struct bio *bio, dm_block_t block)
+{
+ struct pool *pool = tc->pool;
+
+ bio->bi_bdev = tc->pool_dev->bdev;
+ bio->bi_sector = (block << pool->block_shift) +
+ (bio->bi_sector & pool->offset_mask);
+}
+
+static void remap_and_issue(struct thin_c *tc, struct bio *bio,
+ dm_block_t block)
+{
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+
+ remap(tc, bio, block);
+
+ /*
+ * Batch together any FUA/FLUSH bios we find and then issue
+ * a single commit for them in process_deferred_bios().
+ */
+ if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) {
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_add(&pool->deferred_flush_bios, bio);
+ spin_unlock_irqrestore(&pool->lock, flags);
+ } else
+ generic_make_request(bio);
+}
+
+/*
+ * wake_worker() is used when new work is queued and when pool_resume is
+ * ready to continue deferred IO processing.
+ */
+static void wake_worker(struct pool *pool)
+{
+ queue_work(pool->wq, &pool->worker);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Bio endio functions.
+ */
+struct endio_hook {
+ struct thin_c *tc;
+ bio_end_io_t *saved_bi_end_io;
+ struct deferred_entry *entry;
+};
+
+struct new_mapping {
+ struct list_head list;
+
+ int prepared;
+
+ struct thin_c *tc;
+ dm_block_t virt_block;
+ dm_block_t data_block;
+ struct cell *cell;
+ int err;
+
+ /*
+ * If the bio covers the whole area of a block then we can avoid
+ * zeroing or copying. Instead this bio is hooked. The bio will
+ * still be in the cell, so care has to be taken to avoid issuing
+ * the bio twice.
+ */
+ struct bio *bio;
+ bio_end_io_t *saved_bi_end_io;
+};
+
+static void __maybe_add_mapping(struct new_mapping *m)
+{
+ struct pool *pool = m->tc->pool;
+
+ if (list_empty(&m->list) && m->prepared) {
+ list_add(&m->list, &pool->prepared_mappings);
+ wake_worker(pool);
+ }
+}
+
+static void copy_complete(int read_err, unsigned long write_err, void *context)
+{
+ unsigned long flags;
+ struct new_mapping *m = context;
+ struct pool *pool = m->tc->pool;
+
+ m->err = read_err || write_err ? -EIO : 0;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ m->prepared = 1;
+ __maybe_add_mapping(m);
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+static void overwrite_endio(struct bio *bio, int err)
+{
+ unsigned long flags;
+ struct new_mapping *m = dm_get_mapinfo(bio)->ptr;
+ struct pool *pool = m->tc->pool;
+
+ m->err = err;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ m->prepared = 1;
+ __maybe_add_mapping(m);
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+static void shared_read_endio(struct bio *bio, int err)
+{
+ struct list_head mappings;
+ struct new_mapping *m, *tmp;
+ struct endio_hook *h = dm_get_mapinfo(bio)->ptr;
+ unsigned long flags;
+ struct pool *pool = h->tc->pool;
+
+ bio->bi_end_io = h->saved_bi_end_io;
+ bio_endio(bio, err);
+
+ INIT_LIST_HEAD(&mappings);
+ ds_dec(h->entry, &mappings);
+
+ spin_lock_irqsave(&pool->lock, flags);
+ list_for_each_entry_safe(m, tmp, &mappings, list) {
+ list_del(&m->list);
+ INIT_LIST_HEAD(&m->list);
+ __maybe_add_mapping(m);
+ }
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ mempool_free(h, pool->endio_hook_pool);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Workqueue.
+ */
+
+/*
+ * Prepared mapping jobs.
+ */
+
+/*
+ * This sends the bios in the cell back to the deferred_bios list.
+ */
+static void cell_defer(struct thin_c *tc, struct cell *cell,
+ dm_block_t data_block)
+{
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ cell_release(cell, &pool->deferred_bios);
+ spin_unlock_irqrestore(&tc->pool->lock, flags);
+
+ wake_worker(pool);
+}
+
+/*
+ * Same as cell_defer above, except it omits one particular detainee,
+ * a write bio that covers the block and has already been processed.
+ */
+static void cell_defer_except(struct thin_c *tc, struct cell *cell,
+ struct bio *exception)
+{
+ struct bio_list bios;
+ struct bio *bio;
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+
+ bio_list_init(&bios);
+ cell_release(cell, &bios);
+
+ spin_lock_irqsave(&pool->lock, flags);
+ while ((bio = bio_list_pop(&bios)))
+ if (bio != exception)
+ bio_list_add(&pool->deferred_bios, bio);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ wake_worker(pool);
+}
+
+static void process_prepared_mapping(struct new_mapping *m)
+{
+ struct thin_c *tc = m->tc;
+ struct bio *bio;
+ int r;
+
+ bio = m->bio;
+ if (bio)
+ bio->bi_end_io = m->saved_bi_end_io;
+
+ if (m->err) {
+ cell_error(m->cell);
+ return;
+ }
+
+ /*
+ * Commit the prepared block into the mapping btree.
+ * Any I/O for this block arriving after this point will get
+ * remapped to it directly.
+ */
+ r = dm_thin_insert_block(tc->td, m->virt_block, m->data_block);
+ if (r) {
+ DMERR("dm_thin_insert_block() failed");
+ cell_error(m->cell);
+ return;
+ }
+
+ /*
+ * Release any bios held while the block was being provisioned.
+ * If we are processing a write bio that completely covers the block,
+ * we already processed it so can ignore it now when processing
+ * the bios in the cell.
+ */
+ if (bio) {
+ cell_defer_except(tc, m->cell, bio);
+ bio_endio(bio, 0);
+ } else
+ cell_defer(tc, m->cell, m->data_block);
+
+ list_del(&m->list);
+ mempool_free(m, tc->pool->mapping_pool);
+}
+
+static void process_prepared_mappings(struct pool *pool)
+{
+ unsigned long flags;
+ struct list_head maps;
+ struct new_mapping *m, *tmp;
+
+ INIT_LIST_HEAD(&maps);
+ spin_lock_irqsave(&pool->lock, flags);
+ list_splice_init(&pool->prepared_mappings, &maps);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ list_for_each_entry_safe(m, tmp, &maps, list)
+ process_prepared_mapping(m);
+}
+
+/*
+ * Deferred bio jobs.
+ */
+static int io_overwrites_block(struct pool *pool, struct bio *bio)
+{
+ return ((bio_data_dir(bio) == WRITE) &&
+ !(bio->bi_sector & pool->offset_mask)) &&
+ (bio->bi_size == (pool->sectors_per_block << SECTOR_SHIFT));
+}
+
+static void save_and_set_endio(struct bio *bio, bio_end_io_t **save,
+ bio_end_io_t *fn)
+{
+ *save = bio->bi_end_io;
+ bio->bi_end_io = fn;
+}
+
+static int ensure_next_mapping(struct pool *pool)
+{
+ if (pool->next_mapping)
+ return 0;
+
+ pool->next_mapping = mempool_alloc(pool->mapping_pool, GFP_ATOMIC);
+
+ return pool->next_mapping ? 0 : -ENOMEM;
+}
+
+static struct new_mapping *get_next_mapping(struct pool *pool)
+{
+ struct new_mapping *r = pool->next_mapping;
+
+ BUG_ON(!pool->next_mapping);
+
+ pool->next_mapping = NULL;
+
+ return r;
+}
+
+static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
+ dm_block_t data_origin, dm_block_t data_dest,
+ struct cell *cell, struct bio *bio)
+{
+ int r;
+ struct pool *pool = tc->pool;
+ struct new_mapping *m = get_next_mapping(pool);
+
+ INIT_LIST_HEAD(&m->list);
+ m->prepared = 0;
+ m->tc = tc;
+ m->virt_block = virt_block;
+ m->data_block = data_dest;
+ m->cell = cell;
+ m->err = 0;
+ m->bio = NULL;
+
+ ds_add_work(&pool->ds, &m->list);
+
+ /*
+ * IO to pool_dev remaps to the pool target's data_dev.
+ *
+ * If the whole block of data is being overwritten, we can issue the
+ * bio immediately. Otherwise we use kcopyd to clone the data first.
+ */
+ if (io_overwrites_block(pool, bio)) {
+ m->bio = bio;
+ save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
+ dm_get_mapinfo(bio)->ptr = m;
+ remap_and_issue(tc, bio, data_dest);
+ } else {
+ struct dm_io_region from, to;
+
+ from.bdev = tc->pool_dev->bdev;
+ from.sector = data_origin * pool->sectors_per_block;
+ from.count = pool->sectors_per_block;
+
+ to.bdev = tc->pool_dev->bdev;
+ to.sector = data_dest * pool->sectors_per_block;
+ to.count = pool->sectors_per_block;
+
+ r = dm_kcopyd_copy(pool->copier, &from, 1, &to,
+ 0, copy_complete, m);
+ if (r < 0) {
+ mempool_free(m, pool->mapping_pool);
+ DMERR("dm_kcopyd_copy() failed");
+ cell_error(cell);
+ }
+ }
+}
+
+static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
+ dm_block_t data_block, struct cell *cell,
+ struct bio *bio)
+{
+ struct pool *pool = tc->pool;
+ struct new_mapping *m = get_next_mapping(pool);
+
+ INIT_LIST_HEAD(&m->list);
+ m->prepared = 0;
+ m->tc = tc;
+ m->virt_block = virt_block;
+ m->data_block = data_block;
+ m->cell = cell;
+ m->err = 0;
+ m->bio = NULL;
+
+ /*
+ * If the whole block of data is being overwritten or we are not
+ * zeroing pre-existing data, we can issue the bio immediately.
+ * Otherwise we use kcopyd to zero the data first.
+ */
+ if (!pool->zero_new_blocks)
+ process_prepared_mapping(m);
+
+ else if (io_overwrites_block(pool, bio)) {
+ m->bio = bio;
+ save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
+ dm_get_mapinfo(bio)->ptr = m;
+ remap_and_issue(tc, bio, data_block);
+
+ } else {
+ int r;
+ struct dm_io_region to;
+
+ to.bdev = tc->pool_dev->bdev;
+ to.sector = data_block * pool->sectors_per_block;
+ to.count = pool->sectors_per_block;
+
+ r = dm_kcopyd_zero(pool->copier, 1, &to, 0, copy_complete, m);
+ if (r < 0) {
+ mempool_free(m, pool->mapping_pool);
+ DMERR("dm_kcopyd_zero() failed");
+ cell_error(cell);
+ }
+ }
+}
+
+static int alloc_data_block(struct thin_c *tc, dm_block_t *result)
+{
+ int r;
+ dm_block_t free_blocks;
+ unsigned long flags;
+ struct pool *pool = tc->pool;
+
+ r = dm_pool_get_free_block_count(pool->pmd, &free_blocks);
+ if (r)
+ return r;
+
+ if (free_blocks <= pool->low_water_blocks && !pool->low_water_triggered) {
+ DMWARN("%s: reached low water mark, sending event.",
+ dm_device_name(pool->pool_md));
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->low_water_triggered = 1;
+ spin_unlock_irqrestore(&pool->lock, flags);
+ dm_table_event(pool->ti->table);
+ }
+
+ if (!free_blocks) {
+ if (pool->no_free_space)
+ return -ENOSPC;
+ else {
+ /*
+ * Try to commit to see if that will free up some
+ * more space.
+ */
+ r = dm_pool_commit_metadata(pool->pmd);
+ if (r) {
+ DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
+ __func__, r);
+ return r;
+ }
+
+ r = dm_pool_get_free_block_count(pool->pmd, &free_blocks);
+ if (r)
+ return r;
+
+ /*
+ * If we still have no space we set a flag to avoid
+ * doing all this checking and return -ENOSPC.
+ */
+ if (!free_blocks) {
+ DMWARN("%s: no free space available.",
+ dm_device_name(pool->pool_md));
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->no_free_space = 1;
+ spin_unlock_irqrestore(&pool->lock, flags);
+ return -ENOSPC;
+ }
+ }
+ }
+
+ r = dm_pool_alloc_data_block(pool->pmd, result);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+/*
+ * If we have run out of space, queue bios until the device is
+ * resumed, presumably after having been reloaded with more space.
+ */
+static void retry_on_resume(struct bio *bio)
+{
+ struct thin_c *tc = dm_get_mapinfo(bio)->ptr;
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_add(&pool->retry_on_resume_list, bio);
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+static void no_space(struct cell *cell)
+{
+ struct bio *bio;
+ struct bio_list bios;
+
+ bio_list_init(&bios);
+ cell_release(cell, &bios);
+
+ while ((bio = bio_list_pop(&bios)))
+ retry_on_resume(bio);
+}
+
+static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block,
+ struct cell_key *key,
+ struct dm_thin_lookup_result *lookup_result,
+ struct cell *cell)
+{
+ int r;
+ dm_block_t data_block;
+
+ r = alloc_data_block(tc, &data_block);
+ switch (r) {
+ case 0:
+ schedule_copy(tc, block, lookup_result->block,
+ data_block, cell, bio);
+ break;
+
+ case -ENOSPC:
+ no_space(cell);
+ break;
+
+ default:
+ DMERR("%s: alloc_data_block() failed, error = %d", __func__, r);
+ cell_error(cell);
+ break;
+ }
+}
+
+static void process_shared_bio(struct thin_c *tc, struct bio *bio,
+ dm_block_t block,
+ struct dm_thin_lookup_result *lookup_result)
+{
+ struct cell *cell;
+ struct pool *pool = tc->pool;
+ struct cell_key key;
+
+ /*
+ * If cell is already occupied, then sharing is already in the process
+ * of being broken so we have nothing further to do here.
+ */
+ build_data_key(tc->td, lookup_result->block, &key);
+ if (bio_detain(pool->prison, &key, bio, &cell))
+ return;
+
+ if (bio_data_dir(bio) == WRITE)
+ break_sharing(tc, bio, block, &key, lookup_result, cell);
+ else {
+ struct endio_hook *h;
+ h = mempool_alloc(pool->endio_hook_pool, GFP_NOIO);
+
+ h->tc = tc;
+ h->entry = ds_inc(&pool->ds);
+ save_and_set_endio(bio, &h->saved_bi_end_io, shared_read_endio);
+ dm_get_mapinfo(bio)->ptr = h;
+
+ cell_release_singleton(cell, bio);
+ remap_and_issue(tc, bio, lookup_result->block);
+ }
+}
+
+static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block,
+ struct cell *cell)
+{
+ int r;
+ dm_block_t data_block;
+
+ /*
+ * Remap empty bios (flushes) immediately, without provisioning.
+ */
+ if (!bio->bi_size) {
+ cell_release_singleton(cell, bio);
+ remap_and_issue(tc, bio, 0);
+ return;
+ }
+
+ /*
+ * Fill read bios with zeroes and complete them immediately.
+ */
+ if (bio_data_dir(bio) == READ) {
+ zero_fill_bio(bio);
+ cell_release_singleton(cell, bio);
+ bio_endio(bio, 0);
+ return;
+ }
+
+ r = alloc_data_block(tc, &data_block);
+ switch (r) {
+ case 0:
+ schedule_zero(tc, block, data_block, cell, bio);
+ break;
+
+ case -ENOSPC:
+ no_space(cell);
+ break;
+
+ default:
+ DMERR("%s: alloc_data_block() failed, error = %d", __func__, r);
+ cell_error(cell);
+ break;
+ }
+}
+
+static void process_bio(struct thin_c *tc, struct bio *bio)
+{
+ int r;
+ dm_block_t block = get_bio_block(tc, bio);
+ struct cell *cell;
+ struct cell_key key;
+ struct dm_thin_lookup_result lookup_result;
+
+ /*
+ * If cell is already occupied, then the block is already
+ * being provisioned so we have nothing further to do here.
+ */
+ build_virtual_key(tc->td, block, &key);
+ if (bio_detain(tc->pool->prison, &key, bio, &cell))
+ return;
+
+ r = dm_thin_find_block(tc->td, block, 1, &lookup_result);
+ switch (r) {
+ case 0:
+ /*
+ * We can release this cell now. This thread is the only
+ * one that puts bios into a cell, and we know there were
+ * no preceding bios.
+ */
+ /*
+ * TODO: this will probably have to change when discard goes
+ * back in.
+ */
+ cell_release_singleton(cell, bio);
+
+ if (lookup_result.shared)
+ process_shared_bio(tc, bio, block, &lookup_result);
+ else
+ remap_and_issue(tc, bio, lookup_result.block);
+ break;
+
+ case -ENODATA:
+ provision_block(tc, bio, block, cell);
+ break;
+
+ default:
+ DMERR("dm_thin_find_block() failed, error = %d", r);
+ bio_io_error(bio);
+ break;
+ }
+}
+
+static void process_deferred_bios(struct pool *pool)
+{
+ unsigned long flags;
+ struct bio *bio;
+ struct bio_list bios;
+ int r;
+
+ bio_list_init(&bios);
+
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_merge(&bios, &pool->deferred_bios);
+ bio_list_init(&pool->deferred_bios);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ while ((bio = bio_list_pop(&bios))) {
+ struct thin_c *tc = dm_get_mapinfo(bio)->ptr;
+ /*
+ * If we've got no free new_mapping structs, and processing
+ * this bio might require one, we pause until there are some
+ * prepared mappings to process.
+ */
+ if (ensure_next_mapping(pool)) {
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_merge(&pool->deferred_bios, &bios);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ break;
+ }
+ process_bio(tc, bio);
+ }
+
+ /*
+ * If there are any deferred flush bios, we must commit
+ * the metadata before issuing them.
+ */
+ bio_list_init(&bios);
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_merge(&bios, &pool->deferred_flush_bios);
+ bio_list_init(&pool->deferred_flush_bios);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ if (bio_list_empty(&bios))
+ return;
+
+ r = dm_pool_commit_metadata(pool->pmd);
+ if (r) {
+ DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
+ __func__, r);
+ while ((bio = bio_list_pop(&bios)))
+ bio_io_error(bio);
+ return;
+ }
+
+ while ((bio = bio_list_pop(&bios)))
+ generic_make_request(bio);
+}
+
+static void do_worker(struct work_struct *ws)
+{
+ struct pool *pool = container_of(ws, struct pool, worker);
+
+ process_prepared_mappings(pool);
+ process_deferred_bios(pool);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Mapping functions.
+ */
+
+/*
+ * Called only while mapping a thin bio to hand it over to the workqueue.
+ */
+static void thin_defer_bio(struct thin_c *tc, struct bio *bio)
+{
+ unsigned long flags;
+ struct pool *pool = tc->pool;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_add(&pool->deferred_bios, bio);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ wake_worker(pool);
+}
+
+/*
+ * Non-blocking function called from the thin target's map function.
+ */
+static int thin_bio_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ int r;
+ struct thin_c *tc = ti->private;
+ dm_block_t block = get_bio_block(tc, bio);
+ struct dm_thin_device *td = tc->td;
+ struct dm_thin_lookup_result result;
+
+ /*
+ * Save the thin context for easy access from the deferred bio later.
+ */
+ map_context->ptr = tc;
+
+ if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) {
+ thin_defer_bio(tc, bio);
+ return DM_MAPIO_SUBMITTED;
+ }
+
+ r = dm_thin_find_block(td, block, 0, &result);
+
+ /*
+ * Note that we defer readahead too.
+ */
+ switch (r) {
+ case 0:
+ if (unlikely(result.shared)) {
+ /*
+ * We have a race condition here between the
+ * result.shared value returned by the lookup and
+ * snapshot creation, which may cause new
+ * sharing.
+ *
+ * To avoid this always quiesce the origin before
+ * taking the snap. You want to do this anyway to
+ * ensure a consistent application view
+ * (i.e. lockfs).
+ *
+ * More distant ancestors are irrelevant. The
+ * shared flag will be set in their case.
+ */
+ thin_defer_bio(tc, bio);
+ r = DM_MAPIO_SUBMITTED;
+ } else {
+ remap(tc, bio, result.block);
+ r = DM_MAPIO_REMAPPED;
+ }
+ break;
+
+ case -ENODATA:
+ /*
+ * In future, the failed dm_thin_find_block above could
+ * provide the hint to load the metadata into cache.
+ */
+ case -EWOULDBLOCK:
+ thin_defer_bio(tc, bio);
+ r = DM_MAPIO_SUBMITTED;
+ break;
+ }
+
+ return r;
+}
+
+static int pool_is_congested(struct dm_target_callbacks *cb, int bdi_bits)
+{
+ int r;
+ unsigned long flags;
+ struct pool_c *pt = container_of(cb, struct pool_c, callbacks);
+
+ spin_lock_irqsave(&pt->pool->lock, flags);
+ r = !bio_list_empty(&pt->pool->retry_on_resume_list);
+ spin_unlock_irqrestore(&pt->pool->lock, flags);
+
+ if (!r) {
+ struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
+ r = bdi_congested(&q->backing_dev_info, bdi_bits);
+ }
+
+ return r;
+}
+
+static void __requeue_bios(struct pool *pool)
+{
+ bio_list_merge(&pool->deferred_bios, &pool->retry_on_resume_list);
+ bio_list_init(&pool->retry_on_resume_list);
+}
+
+/*----------------------------------------------------------------
+ * Binding of control targets to a pool object
+ *--------------------------------------------------------------*/
+static int bind_control_target(struct pool *pool, struct dm_target *ti)
+{
+ struct pool_c *pt = ti->private;
+
+ pool->ti = ti;
+ pool->low_water_blocks = pt->low_water_blocks;
+ pool->zero_new_blocks = pt->zero_new_blocks;
+
+ return 0;
+}
+
+static void unbind_control_target(struct pool *pool, struct dm_target *ti)
+{
+ if (pool->ti == ti)
+ pool->ti = NULL;
+}
+
+/*----------------------------------------------------------------
+ * Pool creation
+ *--------------------------------------------------------------*/
+static void __pool_destroy(struct pool *pool)
+{
+ __pool_table_remove(pool);
+
+ if (dm_pool_metadata_close(pool->pmd) < 0)
+ DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
+
+ prison_destroy(pool->prison);
+ dm_kcopyd_client_destroy(pool->copier);
+
+ if (pool->wq)
+ destroy_workqueue(pool->wq);
+
+ if (pool->next_mapping)
+ mempool_free(pool->next_mapping, pool->mapping_pool);
+ mempool_destroy(pool->mapping_pool);
+ mempool_destroy(pool->endio_hook_pool);
+ kfree(pool);
+}
+
+static struct pool *pool_create(struct mapped_device *pool_md,
+ struct block_device *metadata_dev,
+ unsigned long block_size, char **error)
+{
+ int r;
+ void *err_p;
+ struct pool *pool;
+ struct dm_pool_metadata *pmd;
+
+ pmd = dm_pool_metadata_open(metadata_dev, block_size);
+ if (IS_ERR(pmd)) {
+ *error = "Error creating metadata object";
+ return (struct pool *)pmd;
+ }
+
+ pool = kmalloc(sizeof(*pool), GFP_KERNEL);
+ if (!pool) {
+ *error = "Error allocating memory for pool";
+ err_p = ERR_PTR(-ENOMEM);
+ goto bad_pool;
+ }
+
+ pool->pmd = pmd;
+ pool->sectors_per_block = block_size;
+ pool->block_shift = ffs(block_size) - 1;
+ pool->offset_mask = block_size - 1;
+ pool->low_water_blocks = 0;
+ pool->zero_new_blocks = 1;
+ pool->prison = prison_create(PRISON_CELLS);
+ if (!pool->prison) {
+ *error = "Error creating pool's bio prison";
+ err_p = ERR_PTR(-ENOMEM);
+ goto bad_prison;
+ }
+
+ pool->copier = dm_kcopyd_client_create();
+ if (IS_ERR(pool->copier)) {
+ r = PTR_ERR(pool->copier);
+ *error = "Error creating pool's kcopyd client";
+ err_p = ERR_PTR(r);
+ goto bad_kcopyd_client;
+ }
+
+ /*
+ * Create singlethreaded workqueue that will service all devices
+ * that use this metadata.
+ */
+ pool->wq = alloc_ordered_workqueue("dm-" DM_MSG_PREFIX, WQ_MEM_RECLAIM);
+ if (!pool->wq) {
+ *error = "Error creating pool's workqueue";
+ err_p = ERR_PTR(-ENOMEM);
+ goto bad_wq;
+ }
+
+ INIT_WORK(&pool->worker, do_worker);
+ spin_lock_init(&pool->lock);
+ bio_list_init(&pool->deferred_bios);
+ bio_list_init(&pool->deferred_flush_bios);
+ INIT_LIST_HEAD(&pool->prepared_mappings);
+ pool->low_water_triggered = 0;
+ pool->no_free_space = 0;
+ bio_list_init(&pool->retry_on_resume_list);
+ ds_init(&pool->ds);
+
+ pool->next_mapping = NULL;
+ pool->mapping_pool =
+ mempool_create_kmalloc_pool(MAPPING_POOL_SIZE, sizeof(struct new_mapping));
+ if (!pool->mapping_pool) {
+ *error = "Error creating pool's mapping mempool";
+ err_p = ERR_PTR(-ENOMEM);
+ goto bad_mapping_pool;
+ }
+
+ pool->endio_hook_pool =
+ mempool_create_kmalloc_pool(ENDIO_HOOK_POOL_SIZE, sizeof(struct endio_hook));
+ if (!pool->endio_hook_pool) {
+ *error = "Error creating pool's endio_hook mempool";
+ err_p = ERR_PTR(-ENOMEM);
+ goto bad_endio_hook_pool;
+ }
+ pool->ref_count = 1;
+ pool->pool_md = pool_md;
+ pool->md_dev = metadata_dev;
+ __pool_table_insert(pool);
+
+ return pool;
+
+bad_endio_hook_pool:
+ mempool_destroy(pool->mapping_pool);
+bad_mapping_pool:
+ destroy_workqueue(pool->wq);
+bad_wq:
+ dm_kcopyd_client_destroy(pool->copier);
+bad_kcopyd_client:
+ prison_destroy(pool->prison);
+bad_prison:
+ kfree(pool);
+bad_pool:
+ if (dm_pool_metadata_close(pmd))
+ DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
+
+ return err_p;
+}
+
+static void __pool_inc(struct pool *pool)
+{
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+ pool->ref_count++;
+}
+
+static void __pool_dec(struct pool *pool)
+{
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+ BUG_ON(!pool->ref_count);
+ if (!--pool->ref_count)
+ __pool_destroy(pool);
+}
+
+static struct pool *__pool_find(struct mapped_device *pool_md,
+ struct block_device *metadata_dev,
+ unsigned long block_size, char **error)
+{
+ struct pool *pool = __pool_table_lookup_metadata_dev(metadata_dev);
+
+ if (pool) {
+ if (pool->pool_md != pool_md)
+ return ERR_PTR(-EBUSY);
+ __pool_inc(pool);
+
+ } else {
+ pool = __pool_table_lookup(pool_md);
+ if (pool) {
+ if (pool->md_dev != metadata_dev)
+ return ERR_PTR(-EINVAL);
+ __pool_inc(pool);
+
+ } else
+ pool = pool_create(pool_md, metadata_dev, block_size, error);
+ }
+
+ return pool;
+}
+
+/*----------------------------------------------------------------
+ * Pool target methods
+ *--------------------------------------------------------------*/
+static void pool_dtr(struct dm_target *ti)
+{
+ struct pool_c *pt = ti->private;
+
+ mutex_lock(&dm_thin_pool_table.mutex);
+
+ unbind_control_target(pt->pool, ti);
+ __pool_dec(pt->pool);
+ dm_put_device(ti, pt->metadata_dev);
+ dm_put_device(ti, pt->data_dev);
+ kfree(pt);
+
+ mutex_unlock(&dm_thin_pool_table.mutex);
+}
+
+struct pool_features {
+ unsigned zero_new_blocks:1;
+};
+
+static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
+ struct dm_target *ti)
+{
+ int r;
+ unsigned argc;
+ const char *arg_name;
+
+ static struct dm_arg _args[] = {
+ {0, 1, "Invalid number of pool feature arguments"},
+ };
+
+ /*
+ * No feature arguments supplied.
+ */
+ if (!as->argc)
+ return 0;
+
+ r = dm_read_arg_group(_args, as, &argc, &ti->error);
+ if (r)
+ return -EINVAL;
+
+ while (argc && !r) {
+ arg_name = dm_shift_arg(as);
+ argc--;
+
+ if (!strcasecmp(arg_name, "skip_block_zeroing")) {
+ pf->zero_new_blocks = 0;
+ continue;
+ }
+
+ ti->error = "Unrecognised pool feature requested";
+ r = -EINVAL;
+ }
+
+ return r;
+}
+
+/*
+ * thin-pool <metadata dev> <data dev>
+ * <data block size (sectors)>
+ * <low water mark (blocks)>
+ * [<#feature args> [<arg>]*]
+ *
+ * Optional feature arguments are:
+ * skip_block_zeroing: skips the zeroing of newly-provisioned blocks.
+ */
+static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
+{
+ int r;
+ struct pool_c *pt;
+ struct pool *pool;
+ struct pool_features pf;
+ struct dm_arg_set as;
+ struct dm_dev *data_dev;
+ unsigned long block_size;
+ dm_block_t low_water_blocks;
+ struct dm_dev *metadata_dev;
+ sector_t metadata_dev_size;
+
+ /*
+ * FIXME Remove validation from scope of lock.
+ */
+ mutex_lock(&dm_thin_pool_table.mutex);
+
+ if (argc < 4) {
+ ti->error = "Invalid argument count";
+ r = -EINVAL;
+ goto out_unlock;
+ }
+ as.argc = argc;
+ as.argv = argv;
+
+ r = dm_get_device(ti, argv[0], FMODE_READ | FMODE_WRITE, &metadata_dev);
+ if (r) {
+ ti->error = "Error opening metadata block device";
+ goto out_unlock;
+ }
+
+ metadata_dev_size = i_size_read(metadata_dev->bdev->bd_inode) >> SECTOR_SHIFT;
+ if (metadata_dev_size > METADATA_DEV_MAX_SECTORS) {
+ ti->error = "Metadata device is too large";
+ r = -EINVAL;
+ goto out_metadata;
+ }
+
+ r = dm_get_device(ti, argv[1], FMODE_READ | FMODE_WRITE, &data_dev);
+ if (r) {
+ ti->error = "Error getting data device";
+ goto out_metadata;
+ }
+
+ if (kstrtoul(argv[2], 10, &block_size) || !block_size ||
+ block_size < DATA_DEV_BLOCK_SIZE_MIN_SECTORS ||
+ block_size > DATA_DEV_BLOCK_SIZE_MAX_SECTORS ||
+ !is_power_of_2(block_size)) {
+ ti->error = "Invalid block size";
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (kstrtoull(argv[3], 10, (unsigned long long *)&low_water_blocks)) {
+ ti->error = "Invalid low water mark";
+ r = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Set default pool features.
+ */
+ memset(&pf, 0, sizeof(pf));
+ pf.zero_new_blocks = 1;
+
+ dm_consume_args(&as, 4);
+ r = parse_pool_features(&as, &pf, ti);
+ if (r)
+ goto out;
+
+ pt = kzalloc(sizeof(*pt), GFP_KERNEL);
+ if (!pt) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev,
+ block_size, &ti->error);
+ if (IS_ERR(pool)) {
+ r = PTR_ERR(pool);
+ goto out_free_pt;
+ }
+
+ pt->pool = pool;
+ pt->ti = ti;
+ pt->metadata_dev = metadata_dev;
+ pt->data_dev = data_dev;
+ pt->low_water_blocks = low_water_blocks;
+ pt->zero_new_blocks = pf.zero_new_blocks;
+ ti->num_flush_requests = 1;
+ ti->num_discard_requests = 0;
+ ti->private = pt;
+
+ pt->callbacks.congested_fn = pool_is_congested;
+ dm_table_add_target_callbacks(ti->table, &pt->callbacks);
+
+ mutex_unlock(&dm_thin_pool_table.mutex);
+
+ return 0;
+
+out_free_pt:
+ kfree(pt);
+out:
+ dm_put_device(ti, data_dev);
+out_metadata:
+ dm_put_device(ti, metadata_dev);
+out_unlock:
+ mutex_unlock(&dm_thin_pool_table.mutex);
+
+ return r;
+}
+
+static int pool_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ int r;
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+ unsigned long flags;
+
+ /*
+ * As this is a singleton target, ti->begin is always zero.
+ */
+ spin_lock_irqsave(&pool->lock, flags);
+ bio->bi_bdev = pt->data_dev->bdev;
+ r = DM_MAPIO_REMAPPED;
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ return r;
+}
+
+/*
+ * Retrieves the number of blocks of the data device from
+ * the superblock and compares it to the actual device size,
+ * thus resizing the data device in case it has grown.
+ *
+ * This both copes with opening preallocated data devices in the ctr
+ * being followed by a resume
+ * -and-
+ * calling the resume method individually after userspace has
+ * grown the data device in reaction to a table event.
+ */
+static int pool_preresume(struct dm_target *ti)
+{
+ int r;
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+ dm_block_t data_size, sb_data_size;
+
+ /*
+ * Take control of the pool object.
+ */
+ r = bind_control_target(pool, ti);
+ if (r)
+ return r;
+
+ data_size = ti->len >> pool->block_shift;
+ r = dm_pool_get_data_dev_size(pool->pmd, &sb_data_size);
+ if (r) {
+ DMERR("failed to retrieve data device size");
+ return r;
+ }
+
+ if (data_size < sb_data_size) {
+ DMERR("pool target too small, is %llu blocks (expected %llu)",
+ data_size, sb_data_size);
+ return -EINVAL;
+
+ } else if (data_size > sb_data_size) {
+ r = dm_pool_resize_data_dev(pool->pmd, data_size);
+ if (r) {
+ DMERR("failed to resize data device");
+ return r;
+ }
+
+ r = dm_pool_commit_metadata(pool->pmd);
+ if (r) {
+ DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
+ __func__, r);
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static void pool_resume(struct dm_target *ti)
+{
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->low_water_triggered = 0;
+ pool->no_free_space = 0;
+ __requeue_bios(pool);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ wake_worker(pool);
+}
+
+static void pool_postsuspend(struct dm_target *ti)
+{
+ int r;
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+
+ flush_workqueue(pool->wq);
+
+ r = dm_pool_commit_metadata(pool->pmd);
+ if (r < 0) {
+ DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
+ __func__, r);
+ /* FIXME: invalidate device? error the next FUA or FLUSH bio ?*/
+ }
+}
+
+static int check_arg_count(unsigned argc, unsigned args_required)
+{
+ if (argc != args_required) {
+ DMWARN("Message received with %u arguments instead of %u.",
+ argc, args_required);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int read_dev_id(char *arg, dm_thin_id *dev_id, int warning)
+{
+ if (!kstrtoull(arg, 10, (unsigned long long *)dev_id) &&
+ *dev_id <= MAX_DEV_ID)
+ return 0;
+
+ if (warning)
+ DMWARN("Message received with invalid device id: %s", arg);
+
+ return -EINVAL;
+}
+
+static int process_create_thin_mesg(unsigned argc, char **argv, struct pool *pool)
+{
+ dm_thin_id dev_id;
+ int r;
+
+ r = check_arg_count(argc, 2);
+ if (r)
+ return r;
+
+ r = read_dev_id(argv[1], &dev_id, 1);
+ if (r)
+ return r;
+
+ r = dm_pool_create_thin(pool->pmd, dev_id);
+ if (r) {
+ DMWARN("Creation of new thinly-provisioned device with id %s failed.",
+ argv[1]);
+ return r;
+ }
+
+ return 0;
+}
+
+static int process_create_snap_mesg(unsigned argc, char **argv, struct pool *pool)
+{
+ dm_thin_id dev_id;
+ dm_thin_id origin_dev_id;
+ int r;
+
+ r = check_arg_count(argc, 3);
+ if (r)
+ return r;
+
+ r = read_dev_id(argv[1], &dev_id, 1);
+ if (r)
+ return r;
+
+ r = read_dev_id(argv[2], &origin_dev_id, 1);
+ if (r)
+ return r;
+
+ r = dm_pool_create_snap(pool->pmd, dev_id, origin_dev_id);
+ if (r) {
+ DMWARN("Creation of new snapshot %s of device %s failed.",
+ argv[1], argv[2]);
+ return r;
+ }
+
+ return 0;
+}
+
+static int process_delete_mesg(unsigned argc, char **argv, struct pool *pool)
+{
+ dm_thin_id dev_id;
+ int r;
+
+ r = check_arg_count(argc, 2);
+ if (r)
+ return r;
+
+ r = read_dev_id(argv[1], &dev_id, 1);
+ if (r)
+ return r;
+
+ r = dm_pool_delete_thin_device(pool->pmd, dev_id);
+ if (r)
+ DMWARN("Deletion of thin device %s failed.", argv[1]);
+
+ return r;
+}
+
+static int process_set_transaction_id_mesg(unsigned argc, char **argv, struct pool *pool)
+{
+ dm_thin_id old_id, new_id;
+ int r;
+
+ r = check_arg_count(argc, 3);
+ if (r)
+ return r;
+
+ if (kstrtoull(argv[1], 10, (unsigned long long *)&old_id)) {
+ DMWARN("set_transaction_id message: Unrecognised id %s.", argv[1]);
+ return -EINVAL;
+ }
+
+ if (kstrtoull(argv[2], 10, (unsigned long long *)&new_id)) {
+ DMWARN("set_transaction_id message: Unrecognised new id %s.", argv[2]);
+ return -EINVAL;
+ }
+
+ r = dm_pool_set_metadata_transaction_id(pool->pmd, old_id, new_id);
+ if (r) {
+ DMWARN("Failed to change transaction id from %s to %s.",
+ argv[1], argv[2]);
+ return r;
+ }
+
+ return 0;
+}
+
+/*
+ * Messages supported:
+ * create_thin <dev_id>
+ * create_snap <dev_id> <origin_id>
+ * delete <dev_id>
+ * trim <dev_id> <new_size_in_sectors>
+ * set_transaction_id <current_trans_id> <new_trans_id>
+ */
+static int pool_message(struct dm_target *ti, unsigned argc, char **argv)
+{
+ int r = -EINVAL;
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+
+ if (!strcasecmp(argv[0], "create_thin"))
+ r = process_create_thin_mesg(argc, argv, pool);
+
+ else if (!strcasecmp(argv[0], "create_snap"))
+ r = process_create_snap_mesg(argc, argv, pool);
+
+ else if (!strcasecmp(argv[0], "delete"))
+ r = process_delete_mesg(argc, argv, pool);
+
+ else if (!strcasecmp(argv[0], "set_transaction_id"))
+ r = process_set_transaction_id_mesg(argc, argv, pool);
+
+ else
+ DMWARN("Unrecognised thin pool target message received: %s", argv[0]);
+
+ if (!r) {
+ r = dm_pool_commit_metadata(pool->pmd);
+ if (r)
+ DMERR("%s message: dm_pool_commit_metadata() failed, error = %d",
+ argv[0], r);
+ }
+
+ return r;
+}
+
+/*
+ * Status line is:
+ * <transaction id> <used metadata sectors>/<total metadata sectors>
+ * <used data sectors>/<total data sectors> <held metadata root>
+ */
+static int pool_status(struct dm_target *ti, status_type_t type,
+ char *result, unsigned maxlen)
+{
+ int r;
+ unsigned sz = 0;
+ uint64_t transaction_id;
+ dm_block_t nr_free_blocks_data;
+ dm_block_t nr_free_blocks_metadata;
+ dm_block_t nr_blocks_data;
+ dm_block_t nr_blocks_metadata;
+ dm_block_t held_root;
+ char buf[BDEVNAME_SIZE];
+ char buf2[BDEVNAME_SIZE];
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+
+ switch (type) {
+ case STATUSTYPE_INFO:
+ r = dm_pool_get_metadata_transaction_id(pool->pmd,
+ &transaction_id);
+ if (r)
+ return r;
+
+ r = dm_pool_get_free_metadata_block_count(pool->pmd,
+ &nr_free_blocks_metadata);
+ if (r)
+ return r;
+
+ r = dm_pool_get_metadata_dev_size(pool->pmd, &nr_blocks_metadata);
+ if (r)
+ return r;
+
+ r = dm_pool_get_free_block_count(pool->pmd,
+ &nr_free_blocks_data);
+ if (r)
+ return r;
+
+ r = dm_pool_get_data_dev_size(pool->pmd, &nr_blocks_data);
+ if (r)
+ return r;
+
+ r = dm_pool_get_held_metadata_root(pool->pmd, &held_root);
+ if (r)
+ return r;
+
+ DMEMIT("%llu %llu/%llu %llu/%llu ",
+ (unsigned long long)transaction_id,
+ (unsigned long long)(nr_blocks_metadata - nr_free_blocks_metadata),
+ (unsigned long long)nr_blocks_metadata,
+ (unsigned long long)(nr_blocks_data - nr_free_blocks_data),
+ (unsigned long long)nr_blocks_data);
+
+ if (held_root)
+ DMEMIT("%llu", held_root);
+ else
+ DMEMIT("-");
+
+ break;
+
+ case STATUSTYPE_TABLE:
+ DMEMIT("%s %s %lu %llu ",
+ format_dev_t(buf, pt->metadata_dev->bdev->bd_dev),
+ format_dev_t(buf2, pt->data_dev->bdev->bd_dev),
+ (unsigned long)pool->sectors_per_block,
+ (unsigned long long)pt->low_water_blocks);
+
+ DMEMIT("%u ", !pool->zero_new_blocks);
+
+ if (!pool->zero_new_blocks)
+ DMEMIT("skip_block_zeroing ");
+ break;
+ }
+
+ return 0;
+}
+
+static int pool_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ struct pool_c *pt = ti->private;
+
+ return fn(ti, pt->data_dev, 0, ti->len, data);
+}
+
+static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
+ struct bio_vec *biovec, int max_size)
+{
+ struct pool_c *pt = ti->private;
+ struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
+
+ if (!q->merge_bvec_fn)
+ return max_size;
+
+ bvm->bi_bdev = pt->data_dev->bdev;
+
+ return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
+}
+
+static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
+{
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+
+ blk_limits_io_min(limits, 0);
+ blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
+}
+
+static struct target_type pool_target = {
+ .name = "thin-pool",
+ .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
+ DM_TARGET_IMMUTABLE,
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = pool_ctr,
+ .dtr = pool_dtr,
+ .map = pool_map,
+ .postsuspend = pool_postsuspend,
+ .preresume = pool_preresume,
+ .resume = pool_resume,
+ .message = pool_message,
+ .status = pool_status,
+ .merge = pool_merge,
+ .iterate_devices = pool_iterate_devices,
+ .io_hints = pool_io_hints,
+};
+
+/*----------------------------------------------------------------
+ * Thin target methods
+ *--------------------------------------------------------------*/
+static void thin_dtr(struct dm_target *ti)
+{
+ struct thin_c *tc = ti->private;
+
+ mutex_lock(&dm_thin_pool_table.mutex);
+
+ __pool_dec(tc->pool);
+ dm_pool_close_thin_device(tc->td);
+ dm_put_device(ti, tc->pool_dev);
+ kfree(tc);
+
+ mutex_unlock(&dm_thin_pool_table.mutex);
+}
+
+/*
+ * Thin target parameters:
+ *
+ * <pool_dev> <dev_id>
+ *
+ * pool_dev: the path to the pool (eg, /dev/mapper/my_pool)
+ * dev_id: the internal device identifier
+ */
+static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
+{
+ int r;
+ struct thin_c *tc;
+ struct dm_dev *pool_dev;
+ struct mapped_device *pool_md;
+
+ mutex_lock(&dm_thin_pool_table.mutex);
+
+ if (argc != 2) {
+ ti->error = "Invalid argument count";
+ r = -EINVAL;
+ goto out_unlock;
+ }
+
+ tc = ti->private = kzalloc(sizeof(*tc), GFP_KERNEL);
+ if (!tc) {
+ ti->error = "Out of memory";
+ r = -ENOMEM;
+ goto out_unlock;
+ }
+
+ r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &pool_dev);
+ if (r) {
+ ti->error = "Error opening pool device";
+ goto bad_pool_dev;
+ }
+ tc->pool_dev = pool_dev;
+
+ if (read_dev_id(argv[1], (unsigned long long *)&tc->dev_id, 0)) {
+ ti->error = "Invalid device id";
+ r = -EINVAL;
+ goto bad_common;
+ }
+
+ pool_md = dm_get_md(tc->pool_dev->bdev->bd_dev);
+ if (!pool_md) {
+ ti->error = "Couldn't get pool mapped device";
+ r = -EINVAL;
+ goto bad_common;
+ }
+
+ tc->pool = __pool_table_lookup(pool_md);
+ if (!tc->pool) {
+ ti->error = "Couldn't find pool object";
+ r = -EINVAL;
+ goto bad_pool_lookup;
+ }
+ __pool_inc(tc->pool);
+
+ r = dm_pool_open_thin_device(tc->pool->pmd, tc->dev_id, &tc->td);
+ if (r) {
+ ti->error = "Couldn't open thin internal device";
+ goto bad_thin_open;
+ }
+
+ ti->split_io = tc->pool->sectors_per_block;
+ ti->num_flush_requests = 1;
+ ti->num_discard_requests = 0;
+ ti->discards_supported = 0;
+
+ dm_put(pool_md);
+
+ mutex_unlock(&dm_thin_pool_table.mutex);
+
+ return 0;
+
+bad_thin_open:
+ __pool_dec(tc->pool);
+bad_pool_lookup:
+ dm_put(pool_md);
+bad_common:
+ dm_put_device(ti, tc->pool_dev);
+bad_pool_dev:
+ kfree(tc);
+out_unlock:
+ mutex_unlock(&dm_thin_pool_table.mutex);
+
+ return r;
+}
+
+static int thin_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ bio->bi_sector -= ti->begin;
+
+ return thin_bio_map(ti, bio, map_context);
+}
+
+static void thin_postsuspend(struct dm_target *ti)
+{
+ if (dm_noflush_suspending(ti))
+ requeue_io((struct thin_c *)ti->private);
+}
+
+/*
+ * <nr mapped sectors> <highest mapped sector>
+ */
+static int thin_status(struct dm_target *ti, status_type_t type,
+ char *result, unsigned maxlen)
+{
+ int r;
+ ssize_t sz = 0;
+ dm_block_t mapped, highest;
+ char buf[BDEVNAME_SIZE];
+ struct thin_c *tc = ti->private;
+
+ if (!tc->td)
+ DMEMIT("-");
+ else {
+ switch (type) {
+ case STATUSTYPE_INFO:
+ r = dm_thin_get_mapped_count(tc->td, &mapped);
+ if (r)
+ return r;
+
+ r = dm_thin_get_highest_mapped_block(tc->td, &highest);
+ if (r < 0)
+ return r;
+
+ DMEMIT("%llu ", mapped * tc->pool->sectors_per_block);
+ if (r)
+ DMEMIT("%llu", ((highest + 1) *
+ tc->pool->sectors_per_block) - 1);
+ else
+ DMEMIT("-");
+ break;
+
+ case STATUSTYPE_TABLE:
+ DMEMIT("%s %lu",
+ format_dev_t(buf, tc->pool_dev->bdev->bd_dev),
+ (unsigned long) tc->dev_id);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int thin_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ dm_block_t blocks;
+ struct thin_c *tc = ti->private;
+
+ /*
+ * We can't call dm_pool_get_data_dev_size() since that blocks. So
+ * we follow a more convoluted path through to the pool's target.
+ */
+ if (!tc->pool->ti)
+ return 0; /* nothing is bound */
+
+ blocks = tc->pool->ti->len >> tc->pool->block_shift;
+ if (blocks)
+ return fn(ti, tc->pool_dev, 0, tc->pool->sectors_per_block * blocks, data);
+
+ return 0;
+}
+
+static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
+{
+ struct thin_c *tc = ti->private;
+
+ blk_limits_io_min(limits, 0);
+ blk_limits_io_opt(limits, tc->pool->sectors_per_block << SECTOR_SHIFT);
+}
+
+static struct target_type thin_target = {
+ .name = "thin",
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = thin_ctr,
+ .dtr = thin_dtr,
+ .map = thin_map,
+ .postsuspend = thin_postsuspend,
+ .status = thin_status,
+ .iterate_devices = thin_iterate_devices,
+ .io_hints = thin_io_hints,
+};
+
+/*----------------------------------------------------------------*/
+
+static int __init dm_thin_init(void)
+{
+ int r;
+
+ pool_table_init();
+
+ r = dm_register_target(&thin_target);
+ if (r)
+ return r;
+
+ r = dm_register_target(&pool_target);
+ if (r)
+ dm_unregister_target(&thin_target);
+
+ return r;
+}
+
+static void dm_thin_exit(void)
+{
+ dm_unregister_target(&thin_target);
+ dm_unregister_target(&pool_target);
+}
+
+module_init(dm_thin_init);
+module_exit(dm_thin_exit);
+
+MODULE_DESCRIPTION(DM_NAME "device-mapper thin provisioning target");
+MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-uevent.c b/drivers/md/dm-uevent.c
index 6b1e3b61b25e..8efe033bab55 100644
--- a/drivers/md/dm-uevent.c
+++ b/drivers/md/dm-uevent.c
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/dm-ioctl.h>
+#include <linux/export.h>
#include "dm.h"
#include "dm-uevent.h"
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 0cf68b478878..4720f68f817e 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -25,6 +25,16 @@
#define DM_MSG_PREFIX "core"
+#ifdef CONFIG_PRINTK
+/*
+ * ratelimit state to be used in DMXXX_LIMIT().
+ */
+DEFINE_RATELIMIT_STATE(dm_ratelimit_state,
+ DEFAULT_RATELIMIT_INTERVAL,
+ DEFAULT_RATELIMIT_BURST);
+EXPORT_SYMBOL(dm_ratelimit_state);
+#endif
+
/*
* Cookies are numeric values sent with CHANGE and REMOVE
* uevents while resuming, removing or renaming the device.
@@ -37,6 +47,8 @@ static const char *_name = DM_NAME;
static unsigned int major = 0;
static unsigned int _major = 0;
+static DEFINE_IDR(_minor_idr);
+
static DEFINE_SPINLOCK(_minor_lock);
/*
* For bio-based dm.
@@ -109,6 +121,7 @@ EXPORT_SYMBOL_GPL(dm_get_rq_mapinfo);
#define DMF_FREEING 3
#define DMF_DELETING 4
#define DMF_NOFLUSH_SUSPENDING 5
+#define DMF_MERGE_IS_OPTIONAL 6
/*
* Work processed by per-device workqueue.
@@ -127,6 +140,8 @@ struct mapped_device {
/* Protect queue and type against concurrent access. */
struct mutex type_lock;
+ struct target_type *immutable_target_type;
+
struct gendisk *disk;
char name[16];
@@ -177,9 +192,6 @@ struct mapped_device {
/* forced geometry settings */
struct hd_geometry geometry;
- /* For saving the address of __make_request for request based dm */
- make_request_fn *saved_make_request_fn;
-
/* sysfs handle */
struct kobject kobj;
@@ -313,6 +325,12 @@ static void __exit dm_exit(void)
while (i--)
_exits[i]();
+
+ /*
+ * Should be empty by this point.
+ */
+ idr_remove_all(&_minor_idr);
+ idr_destroy(&_minor_idr);
}
/*
@@ -1171,7 +1189,8 @@ static int __clone_and_map_discard(struct clone_info *ci)
/*
* Even though the device advertised discard support,
- * reconfiguration might have changed that since the
+ * that does not mean every target supports it, and
+ * reconfiguration might also have changed that since the
* check was performed.
*/
if (!ti->num_discard_requests)
@@ -1381,7 +1400,7 @@ out:
* The request function that just remaps the bio built up by
* dm_merge_bvec.
*/
-static int _dm_request(struct request_queue *q, struct bio *bio)
+static void _dm_request(struct request_queue *q, struct bio *bio)
{
int rw = bio_data_dir(bio);
struct mapped_device *md = q->queuedata;
@@ -1402,19 +1421,12 @@ static int _dm_request(struct request_queue *q, struct bio *bio)
queue_io(md, bio);
else
bio_io_error(bio);
- return 0;
+ return;
}
__split_and_process_bio(md, bio);
up_read(&md->io_lock);
- return 0;
-}
-
-static int dm_make_request(struct request_queue *q, struct bio *bio)
-{
- struct mapped_device *md = q->queuedata;
-
- return md->saved_make_request_fn(q, bio); /* call __make_request() */
+ return;
}
static int dm_request_based(struct mapped_device *md)
@@ -1422,14 +1434,14 @@ static int dm_request_based(struct mapped_device *md)
return blk_queue_stackable(md->queue);
}
-static int dm_request(struct request_queue *q, struct bio *bio)
+static void dm_request(struct request_queue *q, struct bio *bio)
{
struct mapped_device *md = q->queuedata;
if (dm_request_based(md))
- return dm_make_request(q, bio);
-
- return _dm_request(q, bio);
+ blk_queue_bio(q, bio);
+ else
+ _dm_request(q, bio);
}
void dm_dispatch_request(struct request *rq)
@@ -1705,8 +1717,6 @@ static int dm_any_congested(void *congested_data, int bdi_bits)
/*-----------------------------------------------------------------
* An IDR is used to keep track of allocated minor numbers.
*---------------------------------------------------------------*/
-static DEFINE_IDR(_minor_idr);
-
static void free_minor(int minor)
{
spin_lock(&_minor_lock);
@@ -1800,7 +1810,6 @@ static void dm_init_md_queue(struct mapped_device *md)
blk_queue_make_request(md->queue, dm_request);
blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
blk_queue_merge_bvec(md->queue, dm_merge_bvec);
- blk_queue_flush(md->queue, REQ_FLUSH | REQ_FUA);
}
/*
@@ -1986,6 +1995,59 @@ static void __set_size(struct mapped_device *md, sector_t size)
}
/*
+ * Return 1 if the queue has a compulsory merge_bvec_fn function.
+ *
+ * If this function returns 0, then the device is either a non-dm
+ * device without a merge_bvec_fn, or it is a dm device that is
+ * able to split any bios it receives that are too big.
+ */
+int dm_queue_merge_is_compulsory(struct request_queue *q)
+{
+ struct mapped_device *dev_md;
+
+ if (!q->merge_bvec_fn)
+ return 0;
+
+ if (q->make_request_fn == dm_request) {
+ dev_md = q->queuedata;
+ if (test_bit(DMF_MERGE_IS_OPTIONAL, &dev_md->flags))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int dm_device_merge_is_compulsory(struct dm_target *ti,
+ struct dm_dev *dev, sector_t start,
+ sector_t len, void *data)
+{
+ struct block_device *bdev = dev->bdev;
+ struct request_queue *q = bdev_get_queue(bdev);
+
+ return dm_queue_merge_is_compulsory(q);
+}
+
+/*
+ * Return 1 if it is acceptable to ignore merge_bvec_fn based
+ * on the properties of the underlying devices.
+ */
+static int dm_table_merge_is_optional(struct dm_table *table)
+{
+ unsigned i = 0;
+ struct dm_target *ti;
+
+ while (i < dm_table_get_num_targets(table)) {
+ ti = dm_table_get_target(table, i++);
+
+ if (ti->type->iterate_devices &&
+ ti->type->iterate_devices(ti, dm_device_merge_is_compulsory, NULL))
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
* Returns old map, which caller must destroy.
*/
static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
@@ -1995,6 +2057,7 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
struct request_queue *q = md->queue;
sector_t size;
unsigned long flags;
+ int merge_is_optional;
size = dm_table_get_size(t);
@@ -2020,10 +2083,18 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
__bind_mempools(md, t);
+ merge_is_optional = dm_table_merge_is_optional(t);
+
write_lock_irqsave(&md->map_lock, flags);
old_map = md->map;
md->map = t;
+ md->immutable_target_type = dm_table_get_immutable_target_type(t);
+
dm_table_set_restrictions(t, q, limits);
+ if (merge_is_optional)
+ set_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
+ else
+ clear_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
write_unlock_irqrestore(&md->map_lock, flags);
return old_map;
@@ -2089,6 +2160,11 @@ unsigned dm_get_md_type(struct mapped_device *md)
return md->type;
}
+struct target_type *dm_get_immutable_target_type(struct mapped_device *md)
+{
+ return md->immutable_target_type;
+}
+
/*
* Fully initialize a request-based queue (->elevator, ->request_fn, etc).
*/
@@ -2105,7 +2181,6 @@ static int dm_init_request_based_queue(struct mapped_device *md)
return 0;
md->queue = q;
- md->saved_make_request_fn = md->queue->make_request_fn;
dm_init_md_queue(md);
blk_queue_softirq_done(md->queue, dm_softirq_done);
blk_queue_prep_rq(md->queue, dm_prep_fn);
@@ -2164,6 +2239,7 @@ struct mapped_device *dm_get_md(dev_t dev)
return md;
}
+EXPORT_SYMBOL_GPL(dm_get_md);
void *dm_get_mdptr(struct mapped_device *md)
{
@@ -2249,7 +2325,6 @@ static int dm_wait_for_completion(struct mapped_device *md, int interruptible)
while (1) {
set_current_state(interruptible);
- smp_mb();
if (!md_in_flight(md))
break;
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 1aaf16746da8..b7dacd59d8d7 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -60,16 +60,20 @@ int dm_table_resume_targets(struct dm_table *t);
int dm_table_any_congested(struct dm_table *t, int bdi_bits);
int dm_table_any_busy_target(struct dm_table *t);
unsigned dm_table_get_type(struct dm_table *t);
+struct target_type *dm_table_get_immutable_target_type(struct dm_table *t);
bool dm_table_request_based(struct dm_table *t);
bool dm_table_supports_discards(struct dm_table *t);
int dm_table_alloc_md_mempools(struct dm_table *t);
void dm_table_free_md_mempools(struct dm_table *t);
struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t);
+int dm_queue_merge_is_compulsory(struct request_queue *q);
+
void dm_lock_md_type(struct mapped_device *md);
void dm_unlock_md_type(struct mapped_device *md);
void dm_set_md_type(struct mapped_device *md, unsigned type);
unsigned dm_get_md_type(struct mapped_device *md);
+struct target_type *dm_get_immutable_target_type(struct mapped_device *md);
int dm_setup_md_queue(struct mapped_device *md);
diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c
index 23078dabb6df..feb2c3c7bb44 100644
--- a/drivers/md/faulty.c
+++ b/drivers/md/faulty.c
@@ -63,6 +63,7 @@
#define MaxFault 50
#include <linux/blkdev.h>
+#include <linux/module.h>
#include <linux/raid/md_u.h>
#include <linux/slab.h>
#include "md.h"
@@ -81,16 +82,16 @@ static void faulty_fail(struct bio *bio, int error)
bio_io_error(b);
}
-typedef struct faulty_conf {
+struct faulty_conf {
int period[Modes];
atomic_t counters[Modes];
sector_t faults[MaxFault];
int modes[MaxFault];
int nfaults;
- mdk_rdev_t *rdev;
-} conf_t;
+ struct md_rdev *rdev;
+};
-static int check_mode(conf_t *conf, int mode)
+static int check_mode(struct faulty_conf *conf, int mode)
{
if (conf->period[mode] == 0 &&
atomic_read(&conf->counters[mode]) <= 0)
@@ -105,7 +106,7 @@ static int check_mode(conf_t *conf, int mode)
return 0;
}
-static int check_sector(conf_t *conf, sector_t start, sector_t end, int dir)
+static int check_sector(struct faulty_conf *conf, sector_t start, sector_t end, int dir)
{
/* If we find a ReadFixable sector, we fix it ... */
int i;
@@ -129,7 +130,7 @@ static int check_sector(conf_t *conf, sector_t start, sector_t end, int dir)
return 0;
}
-static void add_sector(conf_t *conf, sector_t start, int mode)
+static void add_sector(struct faulty_conf *conf, sector_t start, int mode)
{
int i;
int n = conf->nfaults;
@@ -169,9 +170,9 @@ static void add_sector(conf_t *conf, sector_t start, int mode)
conf->nfaults = n+1;
}
-static int make_request(mddev_t *mddev, struct bio *bio)
+static void make_request(struct mddev *mddev, struct bio *bio)
{
- conf_t *conf = mddev->private;
+ struct faulty_conf *conf = mddev->private;
int failit = 0;
if (bio_data_dir(bio) == WRITE) {
@@ -181,7 +182,7 @@ static int make_request(mddev_t *mddev, struct bio *bio)
* just fail immediately
*/
bio_endio(bio, -EIO);
- return 0;
+ return;
}
if (check_sector(conf, bio->bi_sector, bio->bi_sector+(bio->bi_size>>9),
@@ -211,20 +212,20 @@ static int make_request(mddev_t *mddev, struct bio *bio)
}
if (failit) {
struct bio *b = bio_clone_mddev(bio, GFP_NOIO, mddev);
+
b->bi_bdev = conf->rdev->bdev;
b->bi_private = bio;
b->bi_end_io = faulty_fail;
- generic_make_request(b);
- return 0;
- } else {
+ bio = b;
+ } else
bio->bi_bdev = conf->rdev->bdev;
- return 1;
- }
+
+ generic_make_request(bio);
}
-static void status(struct seq_file *seq, mddev_t *mddev)
+static void status(struct seq_file *seq, struct mddev *mddev)
{
- conf_t *conf = mddev->private;
+ struct faulty_conf *conf = mddev->private;
int n;
if ((n=atomic_read(&conf->counters[WriteTransient])) != 0)
@@ -255,11 +256,11 @@ static void status(struct seq_file *seq, mddev_t *mddev)
}
-static int reshape(mddev_t *mddev)
+static int reshape(struct mddev *mddev)
{
int mode = mddev->new_layout & ModeMask;
int count = mddev->new_layout >> ModeShift;
- conf_t *conf = mddev->private;
+ struct faulty_conf *conf = mddev->private;
if (mddev->new_layout < 0)
return 0;
@@ -284,7 +285,7 @@ static int reshape(mddev_t *mddev)
return 0;
}
-static sector_t faulty_size(mddev_t *mddev, sector_t sectors, int raid_disks)
+static sector_t faulty_size(struct mddev *mddev, sector_t sectors, int raid_disks)
{
WARN_ONCE(raid_disks,
"%s does not support generic reshape\n", __func__);
@@ -295,11 +296,11 @@ static sector_t faulty_size(mddev_t *mddev, sector_t sectors, int raid_disks)
return sectors;
}
-static int run(mddev_t *mddev)
+static int run(struct mddev *mddev)
{
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
int i;
- conf_t *conf;
+ struct faulty_conf *conf;
if (md_check_no_bitmap(mddev))
return -EINVAL;
@@ -325,16 +326,16 @@ static int run(mddev_t *mddev)
return 0;
}
-static int stop(mddev_t *mddev)
+static int stop(struct mddev *mddev)
{
- conf_t *conf = mddev->private;
+ struct faulty_conf *conf = mddev->private;
kfree(conf);
mddev->private = NULL;
return 0;
}
-static struct mdk_personality faulty_personality =
+static struct md_personality faulty_personality =
{
.name = "faulty",
.level = LEVEL_FAULTY,
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index abfb59a61ede..c3273efd08cb 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -19,6 +19,7 @@
#include <linux/blkdev.h>
#include <linux/raid/md_u.h>
#include <linux/seq_file.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include "md.h"
#include "linear.h"
@@ -26,10 +27,10 @@
/*
* find which device holds a particular offset
*/
-static inline dev_info_t *which_dev(mddev_t *mddev, sector_t sector)
+static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector)
{
int lo, mid, hi;
- linear_conf_t *conf;
+ struct linear_conf *conf;
lo = 0;
hi = mddev->raid_disks - 1;
@@ -63,8 +64,8 @@ static int linear_mergeable_bvec(struct request_queue *q,
struct bvec_merge_data *bvm,
struct bio_vec *biovec)
{
- mddev_t *mddev = q->queuedata;
- dev_info_t *dev0;
+ struct mddev *mddev = q->queuedata;
+ struct dev_info *dev0;
unsigned long maxsectors, bio_sectors = bvm->bi_size >> 9;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
@@ -89,8 +90,8 @@ static int linear_mergeable_bvec(struct request_queue *q,
static int linear_congested(void *data, int bits)
{
- mddev_t *mddev = data;
- linear_conf_t *conf;
+ struct mddev *mddev = data;
+ struct linear_conf *conf;
int i, ret = 0;
if (mddev_congested(mddev, bits))
@@ -108,9 +109,9 @@ static int linear_congested(void *data, int bits)
return ret;
}
-static sector_t linear_size(mddev_t *mddev, sector_t sectors, int raid_disks)
+static sector_t linear_size(struct mddev *mddev, sector_t sectors, int raid_disks)
{
- linear_conf_t *conf;
+ struct linear_conf *conf;
sector_t array_sectors;
rcu_read_lock();
@@ -123,13 +124,13 @@ static sector_t linear_size(mddev_t *mddev, sector_t sectors, int raid_disks)
return array_sectors;
}
-static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks)
+static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks)
{
- linear_conf_t *conf;
- mdk_rdev_t *rdev;
+ struct linear_conf *conf;
+ struct md_rdev *rdev;
int i, cnt;
- conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(dev_info_t),
+ conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(struct dev_info),
GFP_KERNEL);
if (!conf)
return NULL;
@@ -139,7 +140,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks)
list_for_each_entry(rdev, &mddev->disks, same_set) {
int j = rdev->raid_disk;
- dev_info_t *disk = conf->disks + j;
+ struct dev_info *disk = conf->disks + j;
sector_t sectors;
if (j < 0 || j >= raid_disks || disk->rdev) {
@@ -194,9 +195,9 @@ out:
return NULL;
}
-static int linear_run (mddev_t *mddev)
+static int linear_run (struct mddev *mddev)
{
- linear_conf_t *conf;
+ struct linear_conf *conf;
if (md_check_no_bitmap(mddev))
return -EINVAL;
@@ -213,13 +214,7 @@ static int linear_run (mddev_t *mddev)
return md_integrity_register(mddev);
}
-static void free_conf(struct rcu_head *head)
-{
- linear_conf_t *conf = container_of(head, linear_conf_t, rcu);
- kfree(conf);
-}
-
-static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
+static int linear_add(struct mddev *mddev, struct md_rdev *rdev)
{
/* Adding a drive to a linear array allows the array to grow.
* It is permitted if the new drive has a matching superblock
@@ -229,7 +224,7 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
* The current one is never freed until the array is stopped.
* This avoids races.
*/
- linear_conf_t *newconf, *oldconf;
+ struct linear_conf *newconf, *oldconf;
if (rdev->saved_raid_disk != mddev->raid_disks)
return -EINVAL;
@@ -247,13 +242,13 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
set_capacity(mddev->gendisk, mddev->array_sectors);
revalidate_disk(mddev->gendisk);
- call_rcu(&oldconf->rcu, free_conf);
+ kfree_rcu(oldconf, rcu);
return 0;
}
-static int linear_stop (mddev_t *mddev)
+static int linear_stop (struct mddev *mddev)
{
- linear_conf_t *conf = mddev->private;
+ struct linear_conf *conf = mddev->private;
/*
* We do not require rcu protection here since
@@ -270,14 +265,14 @@ static int linear_stop (mddev_t *mddev)
return 0;
}
-static int linear_make_request (mddev_t *mddev, struct bio *bio)
+static void linear_make_request(struct mddev *mddev, struct bio *bio)
{
- dev_info_t *tmp_dev;
+ struct dev_info *tmp_dev;
sector_t start_sector;
if (unlikely(bio->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bio);
- return 0;
+ return;
}
rcu_read_lock();
@@ -299,7 +294,7 @@ static int linear_make_request (mddev_t *mddev, struct bio *bio)
(unsigned long long)start_sector);
rcu_read_unlock();
bio_io_error(bio);
- return 0;
+ return;
}
if (unlikely(bio->bi_sector + (bio->bi_size >> 9) >
tmp_dev->end_sector)) {
@@ -313,30 +308,27 @@ static int linear_make_request (mddev_t *mddev, struct bio *bio)
bp = bio_split(bio, end_sector - bio->bi_sector);
- if (linear_make_request(mddev, &bp->bio1))
- generic_make_request(&bp->bio1);
- if (linear_make_request(mddev, &bp->bio2))
- generic_make_request(&bp->bio2);
+ linear_make_request(mddev, &bp->bio1);
+ linear_make_request(mddev, &bp->bio2);
bio_pair_release(bp);
- return 0;
+ return;
}
bio->bi_bdev = tmp_dev->rdev->bdev;
bio->bi_sector = bio->bi_sector - start_sector
+ tmp_dev->rdev->data_offset;
rcu_read_unlock();
-
- return 1;
+ generic_make_request(bio);
}
-static void linear_status (struct seq_file *seq, mddev_t *mddev)
+static void linear_status (struct seq_file *seq, struct mddev *mddev)
{
seq_printf(seq, " %dk rounding", mddev->chunk_sectors / 2);
}
-static struct mdk_personality linear_personality =
+static struct md_personality linear_personality =
{
.name = "linear",
.level = LEVEL_LINEAR,
diff --git a/drivers/md/linear.h b/drivers/md/linear.h
index 0ce29b61605a..b685ddd7d7f7 100644
--- a/drivers/md/linear.h
+++ b/drivers/md/linear.h
@@ -2,20 +2,14 @@
#define _LINEAR_H
struct dev_info {
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
sector_t end_sector;
};
-typedef struct dev_info dev_info_t;
-
-struct linear_private_data
+struct linear_conf
{
- sector_t array_sectors;
- dev_info_t disks[0];
struct rcu_head rcu;
+ sector_t array_sectors;
+ struct dev_info disks[0];
};
-
-
-typedef struct linear_private_data linear_conf_t;
-
#endif
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 91e31e260b4a..84acfe7d10e4 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -44,6 +44,7 @@
#include <linux/hdreg.h>
#include <linux/proc_fs.h>
#include <linux/random.h>
+#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/file.h>
#include <linux/compat.h>
@@ -54,13 +55,15 @@
#include "md.h"
#include "bitmap.h"
-#define DEBUG 0
-#define dprintk(x...) ((void)(DEBUG && printk(x)))
-
#ifndef MODULE
static void autostart_arrays(int part);
#endif
+/* pers_list is a list of registered personalities protected
+ * by pers_lock.
+ * pers_lock does extra service to protect accesses to
+ * mddev->thread when the mutex cannot be held.
+ */
static LIST_HEAD(pers_list);
static DEFINE_SPINLOCK(pers_lock);
@@ -93,13 +96,13 @@ static struct workqueue_struct *md_misc_wq;
static int sysctl_speed_limit_min = 1000;
static int sysctl_speed_limit_max = 200000;
-static inline int speed_min(mddev_t *mddev)
+static inline int speed_min(struct mddev *mddev)
{
return mddev->sync_speed_min ?
mddev->sync_speed_min : sysctl_speed_limit_min;
}
-static inline int speed_max(mddev_t *mddev)
+static inline int speed_max(struct mddev *mddev)
{
return mddev->sync_speed_max ?
mddev->sync_speed_max : sysctl_speed_limit_max;
@@ -155,7 +158,7 @@ static int start_readonly;
static void mddev_bio_destructor(struct bio *bio)
{
- mddev_t *mddev, **mddevp;
+ struct mddev *mddev, **mddevp;
mddevp = (void*)bio;
mddev = mddevp[-1];
@@ -164,10 +167,10 @@ static void mddev_bio_destructor(struct bio *bio)
}
struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
- mddev_t *mddev)
+ struct mddev *mddev)
{
struct bio *b;
- mddev_t **mddevp;
+ struct mddev **mddevp;
if (!mddev || !mddev->bio_set)
return bio_alloc(gfp_mask, nr_iovecs);
@@ -184,10 +187,10 @@ struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
EXPORT_SYMBOL_GPL(bio_alloc_mddev);
struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,
- mddev_t *mddev)
+ struct mddev *mddev)
{
struct bio *b;
- mddev_t **mddevp;
+ struct mddev **mddevp;
if (!mddev || !mddev->bio_set)
return bio_clone(bio, gfp_mask);
@@ -215,6 +218,55 @@ struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,
}
EXPORT_SYMBOL_GPL(bio_clone_mddev);
+void md_trim_bio(struct bio *bio, int offset, int size)
+{
+ /* 'bio' is a cloned bio which we need to trim to match
+ * the given offset and size.
+ * This requires adjusting bi_sector, bi_size, and bi_io_vec
+ */
+ int i;
+ struct bio_vec *bvec;
+ int sofar = 0;
+
+ size <<= 9;
+ if (offset == 0 && size == bio->bi_size)
+ return;
+
+ bio->bi_sector += offset;
+ bio->bi_size = size;
+ offset <<= 9;
+ clear_bit(BIO_SEG_VALID, &bio->bi_flags);
+
+ while (bio->bi_idx < bio->bi_vcnt &&
+ bio->bi_io_vec[bio->bi_idx].bv_len <= offset) {
+ /* remove this whole bio_vec */
+ offset -= bio->bi_io_vec[bio->bi_idx].bv_len;
+ bio->bi_idx++;
+ }
+ if (bio->bi_idx < bio->bi_vcnt) {
+ bio->bi_io_vec[bio->bi_idx].bv_offset += offset;
+ bio->bi_io_vec[bio->bi_idx].bv_len -= offset;
+ }
+ /* avoid any complications with bi_idx being non-zero*/
+ if (bio->bi_idx) {
+ memmove(bio->bi_io_vec, bio->bi_io_vec+bio->bi_idx,
+ (bio->bi_vcnt - bio->bi_idx) * sizeof(struct bio_vec));
+ bio->bi_vcnt -= bio->bi_idx;
+ bio->bi_idx = 0;
+ }
+ /* Make sure vcnt and last bv are not too big */
+ bio_for_each_segment(bvec, bio, i) {
+ if (sofar + bvec->bv_len > size)
+ bvec->bv_len = size - sofar;
+ if (bvec->bv_len == 0) {
+ bio->bi_vcnt = i;
+ break;
+ }
+ sofar += bvec->bv_len;
+ }
+}
+EXPORT_SYMBOL_GPL(md_trim_bio);
+
/*
* We have a system wide 'event count' that is incremented
* on any 'interesting' event, and readers of /proc/mdstat
@@ -227,7 +279,7 @@ EXPORT_SYMBOL_GPL(bio_clone_mddev);
*/
static DECLARE_WAIT_QUEUE_HEAD(md_event_waiters);
static atomic_t md_event_count;
-void md_new_event(mddev_t *mddev)
+void md_new_event(struct mddev *mddev)
{
atomic_inc(&md_event_count);
wake_up(&md_event_waiters);
@@ -237,7 +289,7 @@ EXPORT_SYMBOL_GPL(md_new_event);
/* Alternate version that can be called from interrupts
* when calling sysfs_notify isn't needed.
*/
-static void md_new_event_inintr(mddev_t *mddev)
+static void md_new_event_inintr(struct mddev *mddev)
{
atomic_inc(&md_event_count);
wake_up(&md_event_waiters);
@@ -258,19 +310,19 @@ static DEFINE_SPINLOCK(all_mddevs_lock);
* Any code which breaks out of this loop while own
* a reference to the current mddev and must mddev_put it.
*/
-#define for_each_mddev(mddev,tmp) \
+#define for_each_mddev(_mddev,_tmp) \
\
for (({ spin_lock(&all_mddevs_lock); \
- tmp = all_mddevs.next; \
- mddev = NULL;}); \
- ({ if (tmp != &all_mddevs) \
- mddev_get(list_entry(tmp, mddev_t, all_mddevs));\
+ _tmp = all_mddevs.next; \
+ _mddev = NULL;}); \
+ ({ if (_tmp != &all_mddevs) \
+ mddev_get(list_entry(_tmp, struct mddev, all_mddevs));\
spin_unlock(&all_mddevs_lock); \
- if (mddev) mddev_put(mddev); \
- mddev = list_entry(tmp, mddev_t, all_mddevs); \
- tmp != &all_mddevs;}); \
+ if (_mddev) mddev_put(_mddev); \
+ _mddev = list_entry(_tmp, struct mddev, all_mddevs); \
+ _tmp != &all_mddevs;}); \
({ spin_lock(&all_mddevs_lock); \
- tmp = tmp->next;}) \
+ _tmp = _tmp->next;}) \
)
@@ -281,18 +333,17 @@ static DEFINE_SPINLOCK(all_mddevs_lock);
* call has finished, the bio has been linked into some internal structure
* and so is visible to ->quiesce(), so we don't need the refcount any more.
*/
-static int md_make_request(struct request_queue *q, struct bio *bio)
+static void md_make_request(struct request_queue *q, struct bio *bio)
{
const int rw = bio_data_dir(bio);
- mddev_t *mddev = q->queuedata;
- int rv;
+ struct mddev *mddev = q->queuedata;
int cpu;
unsigned int sectors;
if (mddev == NULL || mddev->pers == NULL
|| !mddev->ready) {
bio_io_error(bio);
- return 0;
+ return;
}
smp_rmb(); /* Ensure implications of 'active' are visible */
rcu_read_lock();
@@ -317,7 +368,7 @@ static int md_make_request(struct request_queue *q, struct bio *bio)
* go away inside make_request
*/
sectors = bio_sectors(bio);
- rv = mddev->pers->make_request(mddev, bio);
+ mddev->pers->make_request(mddev, bio);
cpu = part_stat_lock();
part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]);
@@ -326,8 +377,6 @@ static int md_make_request(struct request_queue *q, struct bio *bio)
if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended)
wake_up(&mddev->sb_wait);
-
- return rv;
}
/* mddev_suspend makes sure no new requests are submitted
@@ -336,7 +385,7 @@ static int md_make_request(struct request_queue *q, struct bio *bio)
* Once ->stop is called and completes, the module will be completely
* unused.
*/
-void mddev_suspend(mddev_t *mddev)
+void mddev_suspend(struct mddev *mddev)
{
BUG_ON(mddev->suspended);
mddev->suspended = 1;
@@ -346,7 +395,7 @@ void mddev_suspend(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(mddev_suspend);
-void mddev_resume(mddev_t *mddev)
+void mddev_resume(struct mddev *mddev)
{
mddev->suspended = 0;
wake_up(&mddev->sb_wait);
@@ -357,7 +406,7 @@ void mddev_resume(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(mddev_resume);
-int mddev_congested(mddev_t *mddev, int bits)
+int mddev_congested(struct mddev *mddev, int bits)
{
return mddev->suspended;
}
@@ -369,8 +418,8 @@ EXPORT_SYMBOL(mddev_congested);
static void md_end_flush(struct bio *bio, int err)
{
- mdk_rdev_t *rdev = bio->bi_private;
- mddev_t *mddev = rdev->mddev;
+ struct md_rdev *rdev = bio->bi_private;
+ struct mddev *mddev = rdev->mddev;
rdev_dec_pending(rdev, mddev);
@@ -385,8 +434,8 @@ static void md_submit_flush_data(struct work_struct *ws);
static void submit_flushes(struct work_struct *ws)
{
- mddev_t *mddev = container_of(ws, mddev_t, flush_work);
- mdk_rdev_t *rdev;
+ struct mddev *mddev = container_of(ws, struct mddev, flush_work);
+ struct md_rdev *rdev;
INIT_WORK(&mddev->flush_work, md_submit_flush_data);
atomic_set(&mddev->flush_pending, 1);
@@ -418,7 +467,7 @@ static void submit_flushes(struct work_struct *ws)
static void md_submit_flush_data(struct work_struct *ws)
{
- mddev_t *mddev = container_of(ws, mddev_t, flush_work);
+ struct mddev *mddev = container_of(ws, struct mddev, flush_work);
struct bio *bio = mddev->flush_bio;
if (bio->bi_size == 0)
@@ -426,15 +475,14 @@ static void md_submit_flush_data(struct work_struct *ws)
bio_endio(bio, 0);
else {
bio->bi_rw &= ~REQ_FLUSH;
- if (mddev->pers->make_request(mddev, bio))
- generic_make_request(bio);
+ mddev->pers->make_request(mddev, bio);
}
mddev->flush_bio = NULL;
wake_up(&mddev->sb_wait);
}
-void md_flush_request(mddev_t *mddev, struct bio *bio)
+void md_flush_request(struct mddev *mddev, struct bio *bio)
{
spin_lock_irq(&mddev->write_lock);
wait_event_lock_irq(mddev->sb_wait,
@@ -458,7 +506,7 @@ EXPORT_SYMBOL(md_flush_request);
*/
struct md_plug_cb {
struct blk_plug_cb cb;
- mddev_t *mddev;
+ struct mddev *mddev;
};
static void plugger_unplug(struct blk_plug_cb *cb)
@@ -472,7 +520,7 @@ static void plugger_unplug(struct blk_plug_cb *cb)
/* Check that an unplug wakeup will come shortly.
* If not, wakeup the md thread immediately
*/
-int mddev_check_plugged(mddev_t *mddev)
+int mddev_check_plugged(struct mddev *mddev)
{
struct blk_plug *plug = current->plug;
struct md_plug_cb *mdcb;
@@ -504,7 +552,7 @@ int mddev_check_plugged(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(mddev_check_plugged);
-static inline mddev_t *mddev_get(mddev_t *mddev)
+static inline struct mddev *mddev_get(struct mddev *mddev)
{
atomic_inc(&mddev->active);
return mddev;
@@ -512,7 +560,7 @@ static inline mddev_t *mddev_get(mddev_t *mddev)
static void mddev_delayed_delete(struct work_struct *ws);
-static void mddev_put(mddev_t *mddev)
+static void mddev_put(struct mddev *mddev)
{
struct bio_set *bs = NULL;
@@ -541,7 +589,7 @@ static void mddev_put(mddev_t *mddev)
bioset_free(bs);
}
-void mddev_init(mddev_t *mddev)
+void mddev_init(struct mddev *mddev)
{
mutex_init(&mddev->open_mutex);
mutex_init(&mddev->reconfig_mutex);
@@ -564,9 +612,9 @@ void mddev_init(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(mddev_init);
-static mddev_t * mddev_find(dev_t unit)
+static struct mddev * mddev_find(dev_t unit)
{
- mddev_t *mddev, *new = NULL;
+ struct mddev *mddev, *new = NULL;
if (unit && MAJOR(unit) != MD_MAJOR)
unit &= ~((1<<MdpMinorShift)-1);
@@ -638,24 +686,24 @@ static mddev_t * mddev_find(dev_t unit)
goto retry;
}
-static inline int mddev_lock(mddev_t * mddev)
+static inline int mddev_lock(struct mddev * mddev)
{
return mutex_lock_interruptible(&mddev->reconfig_mutex);
}
-static inline int mddev_is_locked(mddev_t *mddev)
+static inline int mddev_is_locked(struct mddev *mddev)
{
return mutex_is_locked(&mddev->reconfig_mutex);
}
-static inline int mddev_trylock(mddev_t * mddev)
+static inline int mddev_trylock(struct mddev * mddev)
{
return mutex_trylock(&mddev->reconfig_mutex);
}
static struct attribute_group md_redundancy_group;
-static void mddev_unlock(mddev_t * mddev)
+static void mddev_unlock(struct mddev * mddev)
{
if (mddev->to_remove) {
/* These cannot be removed under reconfig_mutex as
@@ -690,12 +738,17 @@ static void mddev_unlock(mddev_t * mddev)
} else
mutex_unlock(&mddev->reconfig_mutex);
+ /* As we've dropped the mutex we need a spinlock to
+ * make sure the thread doesn't disappear
+ */
+ spin_lock(&pers_lock);
md_wakeup_thread(mddev->thread);
+ spin_unlock(&pers_lock);
}
-static mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr)
+static struct md_rdev * find_rdev_nr(struct mddev *mddev, int nr)
{
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
list_for_each_entry(rdev, &mddev->disks, same_set)
if (rdev->desc_nr == nr)
@@ -704,9 +757,9 @@ static mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr)
return NULL;
}
-static mdk_rdev_t * find_rdev(mddev_t * mddev, dev_t dev)
+static struct md_rdev * find_rdev(struct mddev * mddev, dev_t dev)
{
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
list_for_each_entry(rdev, &mddev->disks, same_set)
if (rdev->bdev->bd_dev == dev)
@@ -715,9 +768,9 @@ static mdk_rdev_t * find_rdev(mddev_t * mddev, dev_t dev)
return NULL;
}
-static struct mdk_personality *find_pers(int level, char *clevel)
+static struct md_personality *find_pers(int level, char *clevel)
{
- struct mdk_personality *pers;
+ struct md_personality *pers;
list_for_each_entry(pers, &pers_list, list) {
if (level != LEVEL_NONE && pers->level == level)
return pers;
@@ -728,13 +781,13 @@ static struct mdk_personality *find_pers(int level, char *clevel)
}
/* return the offset of the super block in 512byte sectors */
-static inline sector_t calc_dev_sboffset(mdk_rdev_t *rdev)
+static inline sector_t calc_dev_sboffset(struct md_rdev *rdev)
{
sector_t num_sectors = i_size_read(rdev->bdev->bd_inode) / 512;
return MD_NEW_SIZE_SECTORS(num_sectors);
}
-static int alloc_disk_sb(mdk_rdev_t * rdev)
+static int alloc_disk_sb(struct md_rdev * rdev)
{
if (rdev->sb_page)
MD_BUG();
@@ -748,7 +801,7 @@ static int alloc_disk_sb(mdk_rdev_t * rdev)
return 0;
}
-static void free_disk_sb(mdk_rdev_t * rdev)
+static void free_disk_sb(struct md_rdev * rdev)
{
if (rdev->sb_page) {
put_page(rdev->sb_page);
@@ -757,13 +810,17 @@ static void free_disk_sb(mdk_rdev_t * rdev)
rdev->sb_start = 0;
rdev->sectors = 0;
}
+ if (rdev->bb_page) {
+ put_page(rdev->bb_page);
+ rdev->bb_page = NULL;
+ }
}
static void super_written(struct bio *bio, int error)
{
- mdk_rdev_t *rdev = bio->bi_private;
- mddev_t *mddev = rdev->mddev;
+ struct md_rdev *rdev = bio->bi_private;
+ struct mddev *mddev = rdev->mddev;
if (error || !test_bit(BIO_UPTODATE, &bio->bi_flags)) {
printk("md: super_written gets error=%d, uptodate=%d\n",
@@ -777,7 +834,7 @@ static void super_written(struct bio *bio, int error)
bio_put(bio);
}
-void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
+void md_super_write(struct mddev *mddev, struct md_rdev *rdev,
sector_t sector, int size, struct page *page)
{
/* write first size bytes of page to sector of rdev
@@ -795,10 +852,10 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
bio->bi_end_io = super_written;
atomic_inc(&mddev->pending_writes);
- submit_bio(REQ_WRITE | REQ_SYNC | REQ_FLUSH | REQ_FUA, bio);
+ submit_bio(WRITE_FLUSH_FUA, bio);
}
-void md_super_wait(mddev_t *mddev)
+void md_super_wait(struct mddev *mddev)
{
/* wait for all superblock writes that were scheduled to complete */
DEFINE_WAIT(wq);
@@ -816,7 +873,7 @@ static void bi_complete(struct bio *bio, int error)
complete((struct completion*)bio->bi_private);
}
-int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size,
+int sync_page_io(struct md_rdev *rdev, sector_t sector, int size,
struct page *page, int rw, bool metadata_op)
{
struct bio *bio = bio_alloc_mddev(GFP_NOIO, 1, rdev->mddev);
@@ -844,7 +901,7 @@ int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size,
}
EXPORT_SYMBOL_GPL(sync_page_io);
-static int read_disk_sb(mdk_rdev_t * rdev, int size)
+static int read_disk_sb(struct md_rdev * rdev, int size)
{
char b[BDEVNAME_SIZE];
if (!rdev->sb_page) {
@@ -951,7 +1008,7 @@ static unsigned int calc_sb_csum(mdp_super_t * sb)
* We rely on user-space to write the initial superblock, and support
* reading and updating of superblocks.
* Interface methods are:
- * int load_super(mdk_rdev_t *dev, mdk_rdev_t *refdev, int minor_version)
+ * int load_super(struct md_rdev *dev, struct md_rdev *refdev, int minor_version)
* loads and validates a superblock on dev.
* if refdev != NULL, compare superblocks on both devices
* Return:
@@ -961,13 +1018,13 @@ static unsigned int calc_sb_csum(mdp_super_t * sb)
* -EINVAL superblock incompatible or invalid
* -othererror e.g. -EIO
*
- * int validate_super(mddev_t *mddev, mdk_rdev_t *dev)
+ * int validate_super(struct mddev *mddev, struct md_rdev *dev)
* Verify that dev is acceptable into mddev.
* The first time, mddev->raid_disks will be 0, and data from
* dev should be merged in. Subsequent calls check that dev
* is new enough. Return 0 or -EINVAL
*
- * void sync_super(mddev_t *mddev, mdk_rdev_t *dev)
+ * void sync_super(struct mddev *mddev, struct md_rdev *dev)
* Update the superblock for rdev with data in mddev
* This does not write to disc.
*
@@ -976,11 +1033,11 @@ static unsigned int calc_sb_csum(mdp_super_t * sb)
struct super_type {
char *name;
struct module *owner;
- int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev,
+ int (*load_super)(struct md_rdev *rdev, struct md_rdev *refdev,
int minor_version);
- int (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev);
- void (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev);
- unsigned long long (*rdev_size_change)(mdk_rdev_t *rdev,
+ int (*validate_super)(struct mddev *mddev, struct md_rdev *rdev);
+ void (*sync_super)(struct mddev *mddev, struct md_rdev *rdev);
+ unsigned long long (*rdev_size_change)(struct md_rdev *rdev,
sector_t num_sectors);
};
@@ -992,7 +1049,7 @@ struct super_type {
* has a bitmap. Otherwise, it returns 0.
*
*/
-int md_check_no_bitmap(mddev_t *mddev)
+int md_check_no_bitmap(struct mddev *mddev)
{
if (!mddev->bitmap_info.file && !mddev->bitmap_info.offset)
return 0;
@@ -1005,7 +1062,7 @@ EXPORT_SYMBOL(md_check_no_bitmap);
/*
* load_super for 0.90.0
*/
-static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
+static int super_90_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_version)
{
char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
mdp_super_t *sb;
@@ -1025,7 +1082,7 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
ret = -EINVAL;
bdevname(rdev->bdev, b);
- sb = (mdp_super_t*)page_address(rdev->sb_page);
+ sb = page_address(rdev->sb_page);
if (sb->md_magic != MD_SB_MAGIC) {
printk(KERN_ERR "md: invalid raid superblock magic on %s\n",
@@ -1054,6 +1111,7 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
rdev->preferred_minor = sb->md_minor;
rdev->data_offset = 0;
rdev->sb_size = MD_SB_BYTES;
+ rdev->badblocks.shift = -1;
if (sb->level == LEVEL_MULTIPATH)
rdev->desc_nr = -1;
@@ -1064,7 +1122,7 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
ret = 1;
} else {
__u64 ev1, ev2;
- mdp_super_t *refsb = (mdp_super_t*)page_address(refdev->sb_page);
+ mdp_super_t *refsb = page_address(refdev->sb_page);
if (!uuid_equal(refsb, sb)) {
printk(KERN_WARNING "md: %s has different UUID to %s\n",
b, bdevname(refdev->bdev,b2));
@@ -1084,8 +1142,11 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
ret = 0;
}
rdev->sectors = rdev->sb_start;
+ /* Limit to 4TB as metadata cannot record more than that */
+ if (rdev->sectors >= (2ULL << 32))
+ rdev->sectors = (2ULL << 32) - 2;
- if (rdev->sectors < sb->size * 2 && sb->level > 1)
+ if (rdev->sectors < ((sector_t)sb->size) * 2 && sb->level >= 1)
/* "this cannot possibly happen" ... */
ret = -EINVAL;
@@ -1096,10 +1157,10 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
/*
* validate_super for 0.90.0
*/
-static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
+static int super_90_validate(struct mddev *mddev, struct md_rdev *rdev)
{
mdp_disk_t *desc;
- mdp_super_t *sb = (mdp_super_t *)page_address(rdev->sb_page);
+ mdp_super_t *sb = page_address(rdev->sb_page);
__u64 ev1 = md_event(sb);
rdev->raid_disk = -1;
@@ -1119,7 +1180,7 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
mddev->clevel[0] = 0;
mddev->layout = sb->layout;
mddev->raid_disks = sb->raid_disks;
- mddev->dev_sectors = sb->size * 2;
+ mddev->dev_sectors = ((sector_t)sb->size) * 2;
mddev->events = ev1;
mddev->bitmap_info.offset = 0;
mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9;
@@ -1208,10 +1269,10 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
/*
* sync_super for 0.90.0
*/
-static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
+static void super_90_sync(struct mddev *mddev, struct md_rdev *rdev)
{
mdp_super_t *sb;
- mdk_rdev_t *rdev2;
+ struct md_rdev *rdev2;
int next_spare = mddev->raid_disks;
@@ -1230,7 +1291,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
rdev->sb_size = MD_SB_BYTES;
- sb = (mdp_super_t*)page_address(rdev->sb_page);
+ sb = page_address(rdev->sb_page);
memset(sb, 0, sizeof(*sb));
@@ -1352,7 +1413,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
* rdev_size_change for 0.90.0
*/
static unsigned long long
-super_90_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
+super_90_rdev_size_change(struct md_rdev *rdev, sector_t num_sectors)
{
if (num_sectors && num_sectors < rdev->mddev->dev_sectors)
return 0; /* component must fit device */
@@ -1361,6 +1422,11 @@ super_90_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
rdev->sb_start = calc_dev_sboffset(rdev);
if (!num_sectors || num_sectors > rdev->sb_start)
num_sectors = rdev->sb_start;
+ /* Limit to 4TB as metadata cannot record more than that.
+ * 4TB == 2^32 KB, or 2*2^32 sectors.
+ */
+ if (num_sectors >= (2ULL << 32))
+ num_sectors = (2ULL << 32) - 2;
md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
rdev->sb_page);
md_super_wait(rdev->mddev);
@@ -1395,7 +1461,9 @@ static __le32 calc_sb_1_csum(struct mdp_superblock_1 * sb)
return cpu_to_le32(csum);
}
-static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
+static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
+ int acknowledged);
+static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_version)
{
struct mdp_superblock_1 *sb;
int ret;
@@ -1435,7 +1503,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
if (ret) return ret;
- sb = (struct mdp_superblock_1*)page_address(rdev->sb_page);
+ sb = page_address(rdev->sb_page);
if (sb->magic != cpu_to_le32(MD_SB_MAGIC) ||
sb->major_version != cpu_to_le32(1) ||
@@ -1473,12 +1541,52 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
else
rdev->desc_nr = le32_to_cpu(sb->dev_number);
+ if (!rdev->bb_page) {
+ rdev->bb_page = alloc_page(GFP_KERNEL);
+ if (!rdev->bb_page)
+ return -ENOMEM;
+ }
+ if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BAD_BLOCKS) &&
+ rdev->badblocks.count == 0) {
+ /* need to load the bad block list.
+ * Currently we limit it to one page.
+ */
+ s32 offset;
+ sector_t bb_sector;
+ u64 *bbp;
+ int i;
+ int sectors = le16_to_cpu(sb->bblog_size);
+ if (sectors > (PAGE_SIZE / 512))
+ return -EINVAL;
+ offset = le32_to_cpu(sb->bblog_offset);
+ if (offset == 0)
+ return -EINVAL;
+ bb_sector = (long long)offset;
+ if (!sync_page_io(rdev, bb_sector, sectors << 9,
+ rdev->bb_page, READ, true))
+ return -EIO;
+ bbp = (u64 *)page_address(rdev->bb_page);
+ rdev->badblocks.shift = sb->bblog_shift;
+ for (i = 0 ; i < (sectors << (9-3)) ; i++, bbp++) {
+ u64 bb = le64_to_cpu(*bbp);
+ int count = bb & (0x3ff);
+ u64 sector = bb >> 10;
+ sector <<= sb->bblog_shift;
+ count <<= sb->bblog_shift;
+ if (bb + 1 == 0)
+ break;
+ if (md_set_badblocks(&rdev->badblocks,
+ sector, count, 1) == 0)
+ return -EINVAL;
+ }
+ } else if (sb->bblog_offset == 0)
+ rdev->badblocks.shift = -1;
+
if (!refdev) {
ret = 1;
} else {
__u64 ev1, ev2;
- struct mdp_superblock_1 *refsb =
- (struct mdp_superblock_1*)page_address(refdev->sb_page);
+ struct mdp_superblock_1 *refsb = page_address(refdev->sb_page);
if (memcmp(sb->set_uuid, refsb->set_uuid, 16) != 0 ||
sb->level != refsb->level ||
@@ -1511,9 +1619,9 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
return ret;
}
-static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
+static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
{
- struct mdp_superblock_1 *sb = (struct mdp_superblock_1*)page_address(rdev->sb_page);
+ struct mdp_superblock_1 *sb = page_address(rdev->sb_page);
__u64 ev1 = le64_to_cpu(sb->events);
rdev->raid_disk = -1;
@@ -1612,20 +1720,19 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
return 0;
}
-static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
+static void super_1_sync(struct mddev *mddev, struct md_rdev *rdev)
{
struct mdp_superblock_1 *sb;
- mdk_rdev_t *rdev2;
+ struct md_rdev *rdev2;
int max_dev, i;
/* make rdev->sb match mddev and rdev data. */
- sb = (struct mdp_superblock_1*)page_address(rdev->sb_page);
+ sb = page_address(rdev->sb_page);
sb->feature_map = 0;
sb->pad0 = 0;
sb->recovery_offset = cpu_to_le64(0);
memset(sb->pad1, 0, sizeof(sb->pad1));
- memset(sb->pad2, 0, sizeof(sb->pad2));
memset(sb->pad3, 0, sizeof(sb->pad3));
sb->utime = cpu_to_le64((__u64)mddev->utime);
@@ -1643,6 +1750,11 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
sb->level = cpu_to_le32(mddev->level);
sb->layout = cpu_to_le32(mddev->layout);
+ if (test_bit(WriteMostly, &rdev->flags))
+ sb->devflags |= WriteMostly1;
+ else
+ sb->devflags &= ~WriteMostly1;
+
if (mddev->bitmap && mddev->bitmap_info.file == NULL) {
sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_info.offset);
sb->feature_map = cpu_to_le32(MD_FEATURE_BITMAP_OFFSET);
@@ -1665,6 +1777,40 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
sb->new_chunk = cpu_to_le32(mddev->new_chunk_sectors);
}
+ if (rdev->badblocks.count == 0)
+ /* Nothing to do for bad blocks*/ ;
+ else if (sb->bblog_offset == 0)
+ /* Cannot record bad blocks on this device */
+ md_error(mddev, rdev);
+ else {
+ struct badblocks *bb = &rdev->badblocks;
+ u64 *bbp = (u64 *)page_address(rdev->bb_page);
+ u64 *p = bb->page;
+ sb->feature_map |= cpu_to_le32(MD_FEATURE_BAD_BLOCKS);
+ if (bb->changed) {
+ unsigned seq;
+
+retry:
+ seq = read_seqbegin(&bb->lock);
+
+ memset(bbp, 0xff, PAGE_SIZE);
+
+ for (i = 0 ; i < bb->count ; i++) {
+ u64 internal_bb = *p++;
+ u64 store_bb = ((BB_OFFSET(internal_bb) << 10)
+ | BB_LEN(internal_bb));
+ *bbp++ = cpu_to_le64(store_bb);
+ }
+ if (read_seqretry(&bb->lock, seq))
+ goto retry;
+
+ bb->sector = (rdev->sb_start +
+ (int)le32_to_cpu(sb->bblog_offset));
+ bb->size = le16_to_cpu(sb->bblog_size);
+ bb->changed = 0;
+ }
+ }
+
max_dev = 0;
list_for_each_entry(rdev2, &mddev->disks, same_set)
if (rdev2->desc_nr+1 > max_dev)
@@ -1699,7 +1845,7 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
}
static unsigned long long
-super_1_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
+super_1_rdev_size_change(struct md_rdev *rdev, sector_t num_sectors)
{
struct mdp_superblock_1 *sb;
sector_t max_sectors;
@@ -1724,7 +1870,7 @@ super_1_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
num_sectors = max_sectors;
rdev->sb_start = sb_start;
}
- sb = (struct mdp_superblock_1 *) page_address(rdev->sb_page);
+ sb = page_address(rdev->sb_page);
sb->data_size = cpu_to_le64(num_sectors);
sb->super_offset = rdev->sb_start;
sb->sb_csum = calc_sb_1_csum(sb);
@@ -1753,7 +1899,7 @@ static struct super_type super_types[] = {
},
};
-static void sync_super(mddev_t *mddev, mdk_rdev_t *rdev)
+static void sync_super(struct mddev *mddev, struct md_rdev *rdev)
{
if (mddev->sync_super) {
mddev->sync_super(mddev, rdev);
@@ -1765,9 +1911,9 @@ static void sync_super(mddev_t *mddev, mdk_rdev_t *rdev)
super_types[mddev->major_version].sync_super(mddev, rdev);
}
-static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2)
+static int match_mddev_units(struct mddev *mddev1, struct mddev *mddev2)
{
- mdk_rdev_t *rdev, *rdev2;
+ struct md_rdev *rdev, *rdev2;
rcu_read_lock();
rdev_for_each_rcu(rdev, mddev1)
@@ -1790,9 +1936,9 @@ static LIST_HEAD(pending_raid_disks);
* from the array. It only succeeds if all working and active component devices
* are integrity capable with matching profiles.
*/
-int md_integrity_register(mddev_t *mddev)
+int md_integrity_register(struct mddev *mddev)
{
- mdk_rdev_t *rdev, *reference = NULL;
+ struct md_rdev *rdev, *reference = NULL;
if (list_empty(&mddev->disks))
return 0; /* nothing to do */
@@ -1837,7 +1983,7 @@ int md_integrity_register(mddev_t *mddev)
EXPORT_SYMBOL(md_integrity_register);
/* Disable data integrity if non-capable/non-matching disk is being added */
-void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
+void md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev)
{
struct blk_integrity *bi_rdev = bdev_get_integrity(rdev->bdev);
struct blk_integrity *bi_mddev = blk_get_integrity(mddev->gendisk);
@@ -1854,7 +2000,7 @@ void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
}
EXPORT_SYMBOL(md_integrity_add_rdev);
-static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
+static int bind_rdev_to_array(struct md_rdev * rdev, struct mddev * mddev)
{
char b[BDEVNAME_SIZE];
struct kobject *ko;
@@ -1922,7 +2068,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
bd_link_disk_holder(rdev->bdev, mddev->gendisk);
/* May as well allow recovery to be retried once */
- mddev->recovery_disabled = 0;
+ mddev->recovery_disabled++;
return 0;
@@ -1934,12 +2080,12 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
static void md_delayed_delete(struct work_struct *ws)
{
- mdk_rdev_t *rdev = container_of(ws, mdk_rdev_t, del_work);
+ struct md_rdev *rdev = container_of(ws, struct md_rdev, del_work);
kobject_del(&rdev->kobj);
kobject_put(&rdev->kobj);
}
-static void unbind_rdev_from_array(mdk_rdev_t * rdev)
+static void unbind_rdev_from_array(struct md_rdev * rdev)
{
char b[BDEVNAME_SIZE];
if (!rdev->mddev) {
@@ -1953,6 +2099,9 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev)
sysfs_remove_link(&rdev->kobj, "block");
sysfs_put(rdev->sysfs_state);
rdev->sysfs_state = NULL;
+ kfree(rdev->badblocks.page);
+ rdev->badblocks.count = 0;
+ rdev->badblocks.page = NULL;
/* We need to delay this, otherwise we can deadlock when
* writing to 'remove' to "dev/state". We also need
* to delay it due to rcu usage.
@@ -1968,14 +2117,14 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev)
* otherwise reused by a RAID array (or any other kernel
* subsystem), by bd_claiming the device.
*/
-static int lock_rdev(mdk_rdev_t *rdev, dev_t dev, int shared)
+static int lock_rdev(struct md_rdev *rdev, dev_t dev, int shared)
{
int err = 0;
struct block_device *bdev;
char b[BDEVNAME_SIZE];
bdev = blkdev_get_by_dev(dev, FMODE_READ|FMODE_WRITE|FMODE_EXCL,
- shared ? (mdk_rdev_t *)lock_rdev : rdev);
+ shared ? (struct md_rdev *)lock_rdev : rdev);
if (IS_ERR(bdev)) {
printk(KERN_ERR "md: could not open %s.\n",
__bdevname(dev, b));
@@ -1985,7 +2134,7 @@ static int lock_rdev(mdk_rdev_t *rdev, dev_t dev, int shared)
return err;
}
-static void unlock_rdev(mdk_rdev_t *rdev)
+static void unlock_rdev(struct md_rdev *rdev)
{
struct block_device *bdev = rdev->bdev;
rdev->bdev = NULL;
@@ -1996,7 +2145,7 @@ static void unlock_rdev(mdk_rdev_t *rdev)
void md_autodetect_dev(dev_t dev);
-static void export_rdev(mdk_rdev_t * rdev)
+static void export_rdev(struct md_rdev * rdev)
{
char b[BDEVNAME_SIZE];
printk(KERN_INFO "md: export_rdev(%s)\n",
@@ -2012,15 +2161,15 @@ static void export_rdev(mdk_rdev_t * rdev)
kobject_put(&rdev->kobj);
}
-static void kick_rdev_from_array(mdk_rdev_t * rdev)
+static void kick_rdev_from_array(struct md_rdev * rdev)
{
unbind_rdev_from_array(rdev);
export_rdev(rdev);
}
-static void export_array(mddev_t *mddev)
+static void export_array(struct mddev *mddev)
{
- mdk_rdev_t *rdev, *tmp;
+ struct md_rdev *rdev, *tmp;
rdev_for_each(rdev, tmp, mddev) {
if (!rdev->mddev) {
@@ -2116,7 +2265,7 @@ static void print_sb_1(struct mdp_superblock_1 *sb)
);
}
-static void print_rdev(mdk_rdev_t *rdev, int major_version)
+static void print_rdev(struct md_rdev *rdev, int major_version)
{
char b[BDEVNAME_SIZE];
printk(KERN_INFO "md: rdev %s, Sect:%08llu F:%d S:%d DN:%u\n",
@@ -2127,10 +2276,10 @@ static void print_rdev(mdk_rdev_t *rdev, int major_version)
printk(KERN_INFO "md: rdev superblock (MJ:%d):\n", major_version);
switch (major_version) {
case 0:
- print_sb_90((mdp_super_t*)page_address(rdev->sb_page));
+ print_sb_90(page_address(rdev->sb_page));
break;
case 1:
- print_sb_1((struct mdp_superblock_1 *)page_address(rdev->sb_page));
+ print_sb_1(page_address(rdev->sb_page));
break;
}
} else
@@ -2140,8 +2289,8 @@ static void print_rdev(mdk_rdev_t *rdev, int major_version)
static void md_print_devices(void)
{
struct list_head *tmp;
- mdk_rdev_t *rdev;
- mddev_t *mddev;
+ struct md_rdev *rdev;
+ struct mddev *mddev;
char b[BDEVNAME_SIZE];
printk("\n");
@@ -2166,7 +2315,7 @@ static void md_print_devices(void)
}
-static void sync_sbs(mddev_t * mddev, int nospares)
+static void sync_sbs(struct mddev * mddev, int nospares)
{
/* Update each superblock (in-memory image), but
* if we are allowed to, skip spares which already
@@ -2174,7 +2323,7 @@ static void sync_sbs(mddev_t * mddev, int nospares)
* (which would mean they aren't being marked as dirty
* with the rest of the array)
*/
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
list_for_each_entry(rdev, &mddev->disks, same_set) {
if (rdev->sb_events == mddev->events ||
(nospares &&
@@ -2189,11 +2338,12 @@ static void sync_sbs(mddev_t * mddev, int nospares)
}
}
-static void md_update_sb(mddev_t * mddev, int force_change)
+static void md_update_sb(struct mddev * mddev, int force_change)
{
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
int sync_req;
int nospares = 0;
+ int any_badblocks_changed = 0;
repeat:
/* First make sure individual recovery_offsets are correct */
@@ -2208,8 +2358,18 @@ repeat:
if (!mddev->persistent) {
clear_bit(MD_CHANGE_CLEAN, &mddev->flags);
clear_bit(MD_CHANGE_DEVS, &mddev->flags);
- if (!mddev->external)
+ if (!mddev->external) {
clear_bit(MD_CHANGE_PENDING, &mddev->flags);
+ list_for_each_entry(rdev, &mddev->disks, same_set) {
+ if (rdev->badblocks.changed) {
+ md_ack_all_badblocks(&rdev->badblocks);
+ md_error(mddev, rdev);
+ }
+ clear_bit(Blocked, &rdev->flags);
+ clear_bit(BlockedBadBlocks, &rdev->flags);
+ wake_up(&rdev->blocked_wait);
+ }
+ }
wake_up(&mddev->sb_wait);
return;
}
@@ -2265,34 +2425,50 @@ repeat:
MD_BUG();
mddev->events --;
}
+
+ list_for_each_entry(rdev, &mddev->disks, same_set) {
+ if (rdev->badblocks.changed)
+ any_badblocks_changed++;
+ if (test_bit(Faulty, &rdev->flags))
+ set_bit(FaultRecorded, &rdev->flags);
+ }
+
sync_sbs(mddev, nospares);
spin_unlock_irq(&mddev->write_lock);
- dprintk(KERN_INFO
- "md: updating %s RAID superblock on device (in sync %d)\n",
- mdname(mddev),mddev->in_sync);
+ pr_debug("md: updating %s RAID superblock on device (in sync %d)\n",
+ mdname(mddev), mddev->in_sync);
bitmap_update_sb(mddev->bitmap);
list_for_each_entry(rdev, &mddev->disks, same_set) {
char b[BDEVNAME_SIZE];
- dprintk(KERN_INFO "md: ");
+
if (rdev->sb_loaded != 1)
continue; /* no noise on spare devices */
- if (test_bit(Faulty, &rdev->flags))
- dprintk("(skipping faulty ");
- dprintk("%s ", bdevname(rdev->bdev,b));
- if (!test_bit(Faulty, &rdev->flags)) {
+ if (!test_bit(Faulty, &rdev->flags) &&
+ rdev->saved_raid_disk == -1) {
md_super_write(mddev,rdev,
rdev->sb_start, rdev->sb_size,
rdev->sb_page);
- dprintk(KERN_INFO "(write) %s's sb offset: %llu\n",
- bdevname(rdev->bdev,b),
- (unsigned long long)rdev->sb_start);
+ pr_debug("md: (write) %s's sb offset: %llu\n",
+ bdevname(rdev->bdev, b),
+ (unsigned long long)rdev->sb_start);
rdev->sb_events = mddev->events;
+ if (rdev->badblocks.size) {
+ md_super_write(mddev, rdev,
+ rdev->badblocks.sector,
+ rdev->badblocks.size << 9,
+ rdev->bb_page);
+ rdev->badblocks.size = 0;
+ }
+
+ } else if (test_bit(Faulty, &rdev->flags))
+ pr_debug("md: %s (skipping faulty)\n",
+ bdevname(rdev->bdev, b));
+ else
+ pr_debug("(skipping incremental s/r ");
- } else
- dprintk(")\n");
if (mddev->level == LEVEL_MULTIPATH)
/* only need to write one superblock... */
break;
@@ -2313,6 +2489,15 @@ repeat:
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
sysfs_notify(&mddev->kobj, NULL, "sync_completed");
+ list_for_each_entry(rdev, &mddev->disks, same_set) {
+ if (test_and_clear_bit(FaultRecorded, &rdev->flags))
+ clear_bit(Blocked, &rdev->flags);
+
+ if (any_badblocks_changed)
+ md_ack_all_badblocks(&rdev->badblocks);
+ clear_bit(BlockedBadBlocks, &rdev->flags);
+ wake_up(&rdev->blocked_wait);
+ }
}
/* words written to sysfs files may, or may not, be \n terminated.
@@ -2337,17 +2522,18 @@ static int cmd_match(const char *cmd, const char *str)
struct rdev_sysfs_entry {
struct attribute attr;
- ssize_t (*show)(mdk_rdev_t *, char *);
- ssize_t (*store)(mdk_rdev_t *, const char *, size_t);
+ ssize_t (*show)(struct md_rdev *, char *);
+ ssize_t (*store)(struct md_rdev *, const char *, size_t);
};
static ssize_t
-state_show(mdk_rdev_t *rdev, char *page)
+state_show(struct md_rdev *rdev, char *page)
{
char *sep = "";
size_t len = 0;
- if (test_bit(Faulty, &rdev->flags)) {
+ if (test_bit(Faulty, &rdev->flags) ||
+ rdev->badblocks.unacked_exist) {
len+= sprintf(page+len, "%sfaulty",sep);
sep = ",";
}
@@ -2359,7 +2545,8 @@ state_show(mdk_rdev_t *rdev, char *page)
len += sprintf(page+len, "%swrite_mostly",sep);
sep = ",";
}
- if (test_bit(Blocked, &rdev->flags)) {
+ if (test_bit(Blocked, &rdev->flags) ||
+ rdev->badblocks.unacked_exist) {
len += sprintf(page+len, "%sblocked", sep);
sep = ",";
}
@@ -2368,30 +2555,39 @@ state_show(mdk_rdev_t *rdev, char *page)
len += sprintf(page+len, "%sspare", sep);
sep = ",";
}
+ if (test_bit(WriteErrorSeen, &rdev->flags)) {
+ len += sprintf(page+len, "%swrite_error", sep);
+ sep = ",";
+ }
return len+sprintf(page+len, "\n");
}
static ssize_t
-state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+state_store(struct md_rdev *rdev, const char *buf, size_t len)
{
/* can write
- * faulty - simulates and error
+ * faulty - simulates an error
* remove - disconnects the device
* writemostly - sets write_mostly
* -writemostly - clears write_mostly
- * blocked - sets the Blocked flag
- * -blocked - clears the Blocked flag
+ * blocked - sets the Blocked flags
+ * -blocked - clears the Blocked and possibly simulates an error
* insync - sets Insync providing device isn't active
+ * write_error - sets WriteErrorSeen
+ * -write_error - clears WriteErrorSeen
*/
int err = -EINVAL;
if (cmd_match(buf, "faulty") && rdev->mddev->pers) {
md_error(rdev->mddev, rdev);
- err = 0;
+ if (test_bit(Faulty, &rdev->flags))
+ err = 0;
+ else
+ err = -EBUSY;
} else if (cmd_match(buf, "remove")) {
if (rdev->raid_disk >= 0)
err = -EBUSY;
else {
- mddev_t *mddev = rdev->mddev;
+ struct mddev *mddev = rdev->mddev;
kick_rdev_from_array(rdev);
if (mddev->pers)
md_update_sb(mddev, 1);
@@ -2408,7 +2604,15 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
set_bit(Blocked, &rdev->flags);
err = 0;
} else if (cmd_match(buf, "-blocked")) {
+ if (!test_bit(Faulty, &rdev->flags) &&
+ rdev->badblocks.unacked_exist) {
+ /* metadata handler doesn't understand badblocks,
+ * so we need to fail the device
+ */
+ md_error(rdev->mddev, rdev);
+ }
clear_bit(Blocked, &rdev->flags);
+ clear_bit(BlockedBadBlocks, &rdev->flags);
wake_up(&rdev->blocked_wait);
set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery);
md_wakeup_thread(rdev->mddev->thread);
@@ -2417,6 +2621,12 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
} else if (cmd_match(buf, "insync") && rdev->raid_disk == -1) {
set_bit(In_sync, &rdev->flags);
err = 0;
+ } else if (cmd_match(buf, "write_error")) {
+ set_bit(WriteErrorSeen, &rdev->flags);
+ err = 0;
+ } else if (cmd_match(buf, "-write_error")) {
+ clear_bit(WriteErrorSeen, &rdev->flags);
+ err = 0;
}
if (!err)
sysfs_notify_dirent_safe(rdev->sysfs_state);
@@ -2426,13 +2636,13 @@ static struct rdev_sysfs_entry rdev_state =
__ATTR(state, S_IRUGO|S_IWUSR, state_show, state_store);
static ssize_t
-errors_show(mdk_rdev_t *rdev, char *page)
+errors_show(struct md_rdev *rdev, char *page)
{
return sprintf(page, "%d\n", atomic_read(&rdev->corrected_errors));
}
static ssize_t
-errors_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+errors_store(struct md_rdev *rdev, const char *buf, size_t len)
{
char *e;
unsigned long n = simple_strtoul(buf, &e, 10);
@@ -2446,7 +2656,7 @@ static struct rdev_sysfs_entry rdev_errors =
__ATTR(errors, S_IRUGO|S_IWUSR, errors_show, errors_store);
static ssize_t
-slot_show(mdk_rdev_t *rdev, char *page)
+slot_show(struct md_rdev *rdev, char *page)
{
if (rdev->raid_disk < 0)
return sprintf(page, "none\n");
@@ -2455,11 +2665,10 @@ slot_show(mdk_rdev_t *rdev, char *page)
}
static ssize_t
-slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+slot_store(struct md_rdev *rdev, const char *buf, size_t len)
{
char *e;
int err;
- char nm[20];
int slot = simple_strtoul(buf, &e, 10);
if (strncmp(buf, "none", 4)==0)
slot = -1;
@@ -2482,13 +2691,12 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
hot_remove_disk(rdev->mddev, rdev->raid_disk);
if (err)
return err;
- sprintf(nm, "rd%d", rdev->raid_disk);
- sysfs_remove_link(&rdev->mddev->kobj, nm);
+ sysfs_unlink_rdev(rdev->mddev, rdev);
rdev->raid_disk = -1;
set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery);
md_wakeup_thread(rdev->mddev->thread);
} else if (rdev->mddev->pers) {
- mdk_rdev_t *rdev2;
+ struct md_rdev *rdev2;
/* Activating a spare .. or possibly reactivating
* if we ever get bitmaps working here.
*/
@@ -2515,6 +2723,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
rdev->saved_raid_disk = slot;
else
rdev->saved_raid_disk = -1;
+ clear_bit(In_sync, &rdev->flags);
err = rdev->mddev->pers->
hot_add_disk(rdev->mddev, rdev);
if (err) {
@@ -2522,8 +2731,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
return err;
} else
sysfs_notify_dirent_safe(rdev->sysfs_state);
- sprintf(nm, "rd%d", rdev->raid_disk);
- if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm))
+ if (sysfs_link_rdev(rdev->mddev, rdev))
/* failure here is OK */;
/* don't wakeup anyone, leave that to userspace. */
} else {
@@ -2545,13 +2753,13 @@ static struct rdev_sysfs_entry rdev_slot =
__ATTR(slot, S_IRUGO|S_IWUSR, slot_show, slot_store);
static ssize_t
-offset_show(mdk_rdev_t *rdev, char *page)
+offset_show(struct md_rdev *rdev, char *page)
{
return sprintf(page, "%llu\n", (unsigned long long)rdev->data_offset);
}
static ssize_t
-offset_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+offset_store(struct md_rdev *rdev, const char *buf, size_t len)
{
char *e;
unsigned long long offset = simple_strtoull(buf, &e, 10);
@@ -2571,7 +2779,7 @@ static struct rdev_sysfs_entry rdev_offset =
__ATTR(offset, S_IRUGO|S_IWUSR, offset_show, offset_store);
static ssize_t
-rdev_size_show(mdk_rdev_t *rdev, char *page)
+rdev_size_show(struct md_rdev *rdev, char *page)
{
return sprintf(page, "%llu\n", (unsigned long long)rdev->sectors / 2);
}
@@ -2606,9 +2814,9 @@ static int strict_blocks_to_sectors(const char *buf, sector_t *sectors)
}
static ssize_t
-rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+rdev_size_store(struct md_rdev *rdev, const char *buf, size_t len)
{
- mddev_t *my_mddev = rdev->mddev;
+ struct mddev *my_mddev = rdev->mddev;
sector_t oldsectors = rdev->sectors;
sector_t sectors;
@@ -2634,13 +2842,13 @@ rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
* a deadlock. We have already changed rdev->sectors, and if
* we have to change it back, we will have the lock again.
*/
- mddev_t *mddev;
+ struct mddev *mddev;
int overlap = 0;
struct list_head *tmp;
mddev_unlock(my_mddev);
for_each_mddev(mddev, tmp) {
- mdk_rdev_t *rdev2;
+ struct md_rdev *rdev2;
mddev_lock(mddev);
list_for_each_entry(rdev2, &mddev->disks, same_set)
@@ -2677,7 +2885,7 @@ static struct rdev_sysfs_entry rdev_size =
__ATTR(size, S_IRUGO|S_IWUSR, rdev_size_show, rdev_size_store);
-static ssize_t recovery_start_show(mdk_rdev_t *rdev, char *page)
+static ssize_t recovery_start_show(struct md_rdev *rdev, char *page)
{
unsigned long long recovery_start = rdev->recovery_offset;
@@ -2688,7 +2896,7 @@ static ssize_t recovery_start_show(mdk_rdev_t *rdev, char *page)
return sprintf(page, "%llu\n", recovery_start);
}
-static ssize_t recovery_start_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+static ssize_t recovery_start_store(struct md_rdev *rdev, const char *buf, size_t len)
{
unsigned long long recovery_start;
@@ -2712,6 +2920,39 @@ static ssize_t recovery_start_store(mdk_rdev_t *rdev, const char *buf, size_t le
static struct rdev_sysfs_entry rdev_recovery_start =
__ATTR(recovery_start, S_IRUGO|S_IWUSR, recovery_start_show, recovery_start_store);
+
+static ssize_t
+badblocks_show(struct badblocks *bb, char *page, int unack);
+static ssize_t
+badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack);
+
+static ssize_t bb_show(struct md_rdev *rdev, char *page)
+{
+ return badblocks_show(&rdev->badblocks, page, 0);
+}
+static ssize_t bb_store(struct md_rdev *rdev, const char *page, size_t len)
+{
+ int rv = badblocks_store(&rdev->badblocks, page, len, 0);
+ /* Maybe that ack was all we needed */
+ if (test_and_clear_bit(BlockedBadBlocks, &rdev->flags))
+ wake_up(&rdev->blocked_wait);
+ return rv;
+}
+static struct rdev_sysfs_entry rdev_bad_blocks =
+__ATTR(bad_blocks, S_IRUGO|S_IWUSR, bb_show, bb_store);
+
+
+static ssize_t ubb_show(struct md_rdev *rdev, char *page)
+{
+ return badblocks_show(&rdev->badblocks, page, 1);
+}
+static ssize_t ubb_store(struct md_rdev *rdev, const char *page, size_t len)
+{
+ return badblocks_store(&rdev->badblocks, page, len, 1);
+}
+static struct rdev_sysfs_entry rdev_unack_bad_blocks =
+__ATTR(unacknowledged_bad_blocks, S_IRUGO|S_IWUSR, ubb_show, ubb_store);
+
static struct attribute *rdev_default_attrs[] = {
&rdev_state.attr,
&rdev_errors.attr,
@@ -2719,14 +2960,16 @@ static struct attribute *rdev_default_attrs[] = {
&rdev_offset.attr,
&rdev_size.attr,
&rdev_recovery_start.attr,
+ &rdev_bad_blocks.attr,
+ &rdev_unack_bad_blocks.attr,
NULL,
};
static ssize_t
rdev_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
{
struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr);
- mdk_rdev_t *rdev = container_of(kobj, mdk_rdev_t, kobj);
- mddev_t *mddev = rdev->mddev;
+ struct md_rdev *rdev = container_of(kobj, struct md_rdev, kobj);
+ struct mddev *mddev = rdev->mddev;
ssize_t rv;
if (!entry->show)
@@ -2748,9 +2991,9 @@ rdev_attr_store(struct kobject *kobj, struct attribute *attr,
const char *page, size_t length)
{
struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr);
- mdk_rdev_t *rdev = container_of(kobj, mdk_rdev_t, kobj);
+ struct md_rdev *rdev = container_of(kobj, struct md_rdev, kobj);
ssize_t rv;
- mddev_t *mddev = rdev->mddev;
+ struct mddev *mddev = rdev->mddev;
if (!entry->store)
return -EIO;
@@ -2769,7 +3012,7 @@ rdev_attr_store(struct kobject *kobj, struct attribute *attr,
static void rdev_free(struct kobject *ko)
{
- mdk_rdev_t *rdev = container_of(ko, mdk_rdev_t, kobj);
+ struct md_rdev *rdev = container_of(ko, struct md_rdev, kobj);
kfree(rdev);
}
static const struct sysfs_ops rdev_sysfs_ops = {
@@ -2782,7 +3025,7 @@ static struct kobj_type rdev_ktype = {
.default_attrs = rdev_default_attrs,
};
-void md_rdev_init(mdk_rdev_t *rdev)
+int md_rdev_init(struct md_rdev *rdev)
{
rdev->desc_nr = -1;
rdev->saved_raid_disk = -1;
@@ -2792,12 +3035,27 @@ void md_rdev_init(mdk_rdev_t *rdev)
rdev->sb_events = 0;
rdev->last_read_error.tv_sec = 0;
rdev->last_read_error.tv_nsec = 0;
+ rdev->sb_loaded = 0;
+ rdev->bb_page = NULL;
atomic_set(&rdev->nr_pending, 0);
atomic_set(&rdev->read_errors, 0);
atomic_set(&rdev->corrected_errors, 0);
INIT_LIST_HEAD(&rdev->same_set);
init_waitqueue_head(&rdev->blocked_wait);
+
+ /* Add space to store bad block list.
+ * This reserves the space even on arrays where it cannot
+ * be used - I wonder if that matters
+ */
+ rdev->badblocks.count = 0;
+ rdev->badblocks.shift = 0;
+ rdev->badblocks.page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ seqlock_init(&rdev->badblocks.lock);
+ if (rdev->badblocks.page == NULL)
+ return -ENOMEM;
+
+ return 0;
}
EXPORT_SYMBOL_GPL(md_rdev_init);
/*
@@ -2810,11 +3068,11 @@ EXPORT_SYMBOL_GPL(md_rdev_init);
*
* a faulty rdev _never_ has rdev->sb set.
*/
-static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_minor)
+static struct md_rdev *md_import_device(dev_t newdev, int super_format, int super_minor)
{
char b[BDEVNAME_SIZE];
int err;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
sector_t size;
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
@@ -2823,8 +3081,11 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
return ERR_PTR(-ENOMEM);
}
- md_rdev_init(rdev);
- if ((err = alloc_disk_sb(rdev)))
+ err = md_rdev_init(rdev);
+ if (err)
+ goto abort_free;
+ err = alloc_disk_sb(rdev);
+ if (err)
goto abort_free;
err = lock_rdev(rdev, newdev, super_format == -2);
@@ -2860,15 +3121,17 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
goto abort_free;
}
}
+ if (super_format == -1)
+ /* hot-add for 0.90, or non-persistent: so no badblocks */
+ rdev->badblocks.shift = -1;
return rdev;
abort_free:
- if (rdev->sb_page) {
- if (rdev->bdev)
- unlock_rdev(rdev);
- free_disk_sb(rdev);
- }
+ if (rdev->bdev)
+ unlock_rdev(rdev);
+ free_disk_sb(rdev);
+ kfree(rdev->badblocks.page);
kfree(rdev);
return ERR_PTR(err);
}
@@ -2878,10 +3141,10 @@ abort_free:
*/
-static void analyze_sbs(mddev_t * mddev)
+static void analyze_sbs(struct mddev * mddev)
{
int i;
- mdk_rdev_t *rdev, *freshest, *tmp;
+ struct md_rdev *rdev, *freshest, *tmp;
char b[BDEVNAME_SIZE];
freshest = NULL;
@@ -2981,13 +3244,13 @@ int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale)
static void md_safemode_timeout(unsigned long data);
static ssize_t
-safe_delay_show(mddev_t *mddev, char *page)
+safe_delay_show(struct mddev *mddev, char *page)
{
int msec = (mddev->safemode_delay*1000)/HZ;
return sprintf(page, "%d.%03d\n", msec/1000, msec%1000);
}
static ssize_t
-safe_delay_store(mddev_t *mddev, const char *cbuf, size_t len)
+safe_delay_store(struct mddev *mddev, const char *cbuf, size_t len)
{
unsigned long msec;
@@ -3009,9 +3272,9 @@ static struct md_sysfs_entry md_safe_delay =
__ATTR(safe_mode_delay, S_IRUGO|S_IWUSR,safe_delay_show, safe_delay_store);
static ssize_t
-level_show(mddev_t *mddev, char *page)
+level_show(struct mddev *mddev, char *page)
{
- struct mdk_personality *p = mddev->pers;
+ struct md_personality *p = mddev->pers;
if (p)
return sprintf(page, "%s\n", p->name);
else if (mddev->clevel[0])
@@ -3023,14 +3286,14 @@ level_show(mddev_t *mddev, char *page)
}
static ssize_t
-level_store(mddev_t *mddev, const char *buf, size_t len)
+level_store(struct mddev *mddev, const char *buf, size_t len)
{
char clevel[16];
ssize_t rv = len;
- struct mdk_personality *pers;
+ struct md_personality *pers;
long level;
void *priv;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
if (mddev->pers == NULL) {
if (len == 0)
@@ -3149,15 +3412,13 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
}
list_for_each_entry(rdev, &mddev->disks, same_set) {
- char nm[20];
if (rdev->raid_disk < 0)
continue;
if (rdev->new_raid_disk >= mddev->raid_disks)
rdev->new_raid_disk = -1;
if (rdev->new_raid_disk == rdev->raid_disk)
continue;
- sprintf(nm, "rd%d", rdev->raid_disk);
- sysfs_remove_link(&mddev->kobj, nm);
+ sysfs_unlink_rdev(mddev, rdev);
}
list_for_each_entry(rdev, &mddev->disks, same_set) {
if (rdev->raid_disk < 0)
@@ -3168,11 +3429,10 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
if (rdev->raid_disk < 0)
clear_bit(In_sync, &rdev->flags);
else {
- char nm[20];
- sprintf(nm, "rd%d", rdev->raid_disk);
- if(sysfs_create_link(&mddev->kobj, &rdev->kobj, nm))
- printk("md: cannot register %s for %s after level change\n",
- nm, mdname(mddev));
+ if (sysfs_link_rdev(mddev, rdev))
+ printk(KERN_WARNING "md: cannot register rd%d"
+ " for %s after level change\n",
+ rdev->raid_disk, mdname(mddev));
}
}
@@ -3207,7 +3467,7 @@ __ATTR(level, S_IRUGO|S_IWUSR, level_show, level_store);
static ssize_t
-layout_show(mddev_t *mddev, char *page)
+layout_show(struct mddev *mddev, char *page)
{
/* just a number, not meaningful for all levels */
if (mddev->reshape_position != MaxSector &&
@@ -3218,7 +3478,7 @@ layout_show(mddev_t *mddev, char *page)
}
static ssize_t
-layout_store(mddev_t *mddev, const char *buf, size_t len)
+layout_store(struct mddev *mddev, const char *buf, size_t len)
{
char *e;
unsigned long n = simple_strtoul(buf, &e, 10);
@@ -3248,7 +3508,7 @@ __ATTR(layout, S_IRUGO|S_IWUSR, layout_show, layout_store);
static ssize_t
-raid_disks_show(mddev_t *mddev, char *page)
+raid_disks_show(struct mddev *mddev, char *page)
{
if (mddev->raid_disks == 0)
return 0;
@@ -3259,10 +3519,10 @@ raid_disks_show(mddev_t *mddev, char *page)
return sprintf(page, "%d\n", mddev->raid_disks);
}
-static int update_raid_disks(mddev_t *mddev, int raid_disks);
+static int update_raid_disks(struct mddev *mddev, int raid_disks);
static ssize_t
-raid_disks_store(mddev_t *mddev, const char *buf, size_t len)
+raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
{
char *e;
int rv = 0;
@@ -3285,7 +3545,7 @@ static struct md_sysfs_entry md_raid_disks =
__ATTR(raid_disks, S_IRUGO|S_IWUSR, raid_disks_show, raid_disks_store);
static ssize_t
-chunk_size_show(mddev_t *mddev, char *page)
+chunk_size_show(struct mddev *mddev, char *page)
{
if (mddev->reshape_position != MaxSector &&
mddev->chunk_sectors != mddev->new_chunk_sectors)
@@ -3296,7 +3556,7 @@ chunk_size_show(mddev_t *mddev, char *page)
}
static ssize_t
-chunk_size_store(mddev_t *mddev, const char *buf, size_t len)
+chunk_size_store(struct mddev *mddev, const char *buf, size_t len)
{
char *e;
unsigned long n = simple_strtoul(buf, &e, 10);
@@ -3325,7 +3585,7 @@ static struct md_sysfs_entry md_chunk_size =
__ATTR(chunk_size, S_IRUGO|S_IWUSR, chunk_size_show, chunk_size_store);
static ssize_t
-resync_start_show(mddev_t *mddev, char *page)
+resync_start_show(struct mddev *mddev, char *page)
{
if (mddev->recovery_cp == MaxSector)
return sprintf(page, "none\n");
@@ -3333,7 +3593,7 @@ resync_start_show(mddev_t *mddev, char *page)
}
static ssize_t
-resync_start_store(mddev_t *mddev, const char *buf, size_t len)
+resync_start_store(struct mddev *mddev, const char *buf, size_t len)
{
char *e;
unsigned long long n = simple_strtoull(buf, &e, 10);
@@ -3403,7 +3663,7 @@ static int match_word(const char *word, char **list)
}
static ssize_t
-array_state_show(mddev_t *mddev, char *page)
+array_state_show(struct mddev *mddev, char *page)
{
enum array_state st = inactive;
@@ -3436,13 +3696,13 @@ array_state_show(mddev_t *mddev, char *page)
return sprintf(page, "%s\n", array_states[st]);
}
-static int do_md_stop(mddev_t * mddev, int ro, int is_open);
-static int md_set_readonly(mddev_t * mddev, int is_open);
-static int do_md_run(mddev_t * mddev);
-static int restart_array(mddev_t *mddev);
+static int do_md_stop(struct mddev * mddev, int ro, int is_open);
+static int md_set_readonly(struct mddev * mddev, int is_open);
+static int do_md_run(struct mddev * mddev);
+static int restart_array(struct mddev *mddev);
static ssize_t
-array_state_store(mddev_t *mddev, const char *buf, size_t len)
+array_state_store(struct mddev *mddev, const char *buf, size_t len)
{
int err = -EINVAL;
enum array_state st = match_word(buf, array_states);
@@ -3536,13 +3796,13 @@ static struct md_sysfs_entry md_array_state =
__ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store);
static ssize_t
-max_corrected_read_errors_show(mddev_t *mddev, char *page) {
+max_corrected_read_errors_show(struct mddev *mddev, char *page) {
return sprintf(page, "%d\n",
atomic_read(&mddev->max_corr_read_errors));
}
static ssize_t
-max_corrected_read_errors_store(mddev_t *mddev, const char *buf, size_t len)
+max_corrected_read_errors_store(struct mddev *mddev, const char *buf, size_t len)
{
char *e;
unsigned long n = simple_strtoul(buf, &e, 10);
@@ -3559,13 +3819,13 @@ __ATTR(max_read_errors, S_IRUGO|S_IWUSR, max_corrected_read_errors_show,
max_corrected_read_errors_store);
static ssize_t
-null_show(mddev_t *mddev, char *page)
+null_show(struct mddev *mddev, char *page)
{
return -EINVAL;
}
static ssize_t
-new_dev_store(mddev_t *mddev, const char *buf, size_t len)
+new_dev_store(struct mddev *mddev, const char *buf, size_t len)
{
/* buf must be %d:%d\n? giving major and minor numbers */
/* The new device is added to the array.
@@ -3578,7 +3838,7 @@ new_dev_store(mddev_t *mddev, const char *buf, size_t len)
int major = simple_strtoul(buf, &e, 10);
int minor;
dev_t dev;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
int err;
if (!*buf || *e != ':' || !e[1] || e[1] == '\n')
@@ -3596,8 +3856,9 @@ new_dev_store(mddev_t *mddev, const char *buf, size_t len)
rdev = md_import_device(dev, mddev->major_version,
mddev->minor_version);
if (!IS_ERR(rdev) && !list_empty(&mddev->disks)) {
- mdk_rdev_t *rdev0 = list_entry(mddev->disks.next,
- mdk_rdev_t, same_set);
+ struct md_rdev *rdev0
+ = list_entry(mddev->disks.next,
+ struct md_rdev, same_set);
err = super_types[mddev->major_version]
.load_super(rdev, rdev0, mddev->minor_version);
if (err < 0)
@@ -3621,7 +3882,7 @@ static struct md_sysfs_entry md_new_device =
__ATTR(new_dev, S_IWUSR, null_show, new_dev_store);
static ssize_t
-bitmap_store(mddev_t *mddev, const char *buf, size_t len)
+bitmap_store(struct mddev *mddev, const char *buf, size_t len)
{
char *end;
unsigned long chunk, end_chunk;
@@ -3650,16 +3911,16 @@ static struct md_sysfs_entry md_bitmap =
__ATTR(bitmap_set_bits, S_IWUSR, null_show, bitmap_store);
static ssize_t
-size_show(mddev_t *mddev, char *page)
+size_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%llu\n",
(unsigned long long)mddev->dev_sectors / 2);
}
-static int update_size(mddev_t *mddev, sector_t num_sectors);
+static int update_size(struct mddev *mddev, sector_t num_sectors);
static ssize_t
-size_store(mddev_t *mddev, const char *buf, size_t len)
+size_store(struct mddev *mddev, const char *buf, size_t len)
{
/* If array is inactive, we can reduce the component size, but
* not increase it (except from 0).
@@ -3694,7 +3955,7 @@ __ATTR(component_size, S_IRUGO|S_IWUSR, size_show, size_store);
* or N.M for internally known formats
*/
static ssize_t
-metadata_show(mddev_t *mddev, char *page)
+metadata_show(struct mddev *mddev, char *page)
{
if (mddev->persistent)
return sprintf(page, "%d.%d\n",
@@ -3706,7 +3967,7 @@ metadata_show(mddev_t *mddev, char *page)
}
static ssize_t
-metadata_store(mddev_t *mddev, const char *buf, size_t len)
+metadata_store(struct mddev *mddev, const char *buf, size_t len)
{
int major, minor;
char *e;
@@ -3760,7 +4021,7 @@ static struct md_sysfs_entry md_metadata =
__ATTR(metadata_version, S_IRUGO|S_IWUSR, metadata_show, metadata_store);
static ssize_t
-action_show(mddev_t *mddev, char *page)
+action_show(struct mddev *mddev, char *page)
{
char *type = "idle";
if (test_bit(MD_RECOVERY_FROZEN, &mddev->recovery))
@@ -3782,10 +4043,10 @@ action_show(mddev_t *mddev, char *page)
return sprintf(page, "%s\n", type);
}
-static void reap_sync_thread(mddev_t *mddev);
+static void reap_sync_thread(struct mddev *mddev);
static ssize_t
-action_store(mddev_t *mddev, const char *page, size_t len)
+action_store(struct mddev *mddev, const char *page, size_t len)
{
if (!mddev->pers || !mddev->pers->sync_request)
return -EINVAL;
@@ -3831,7 +4092,7 @@ action_store(mddev_t *mddev, const char *page, size_t len)
}
static ssize_t
-mismatch_cnt_show(mddev_t *mddev, char *page)
+mismatch_cnt_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%llu\n",
(unsigned long long) mddev->resync_mismatches);
@@ -3844,14 +4105,14 @@ __ATTR(sync_action, S_IRUGO|S_IWUSR, action_show, action_store);
static struct md_sysfs_entry md_mismatches = __ATTR_RO(mismatch_cnt);
static ssize_t
-sync_min_show(mddev_t *mddev, char *page)
+sync_min_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%d (%s)\n", speed_min(mddev),
mddev->sync_speed_min ? "local": "system");
}
static ssize_t
-sync_min_store(mddev_t *mddev, const char *buf, size_t len)
+sync_min_store(struct mddev *mddev, const char *buf, size_t len)
{
int min;
char *e;
@@ -3870,14 +4131,14 @@ static struct md_sysfs_entry md_sync_min =
__ATTR(sync_speed_min, S_IRUGO|S_IWUSR, sync_min_show, sync_min_store);
static ssize_t
-sync_max_show(mddev_t *mddev, char *page)
+sync_max_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%d (%s)\n", speed_max(mddev),
mddev->sync_speed_max ? "local": "system");
}
static ssize_t
-sync_max_store(mddev_t *mddev, const char *buf, size_t len)
+sync_max_store(struct mddev *mddev, const char *buf, size_t len)
{
int max;
char *e;
@@ -3896,20 +4157,20 @@ static struct md_sysfs_entry md_sync_max =
__ATTR(sync_speed_max, S_IRUGO|S_IWUSR, sync_max_show, sync_max_store);
static ssize_t
-degraded_show(mddev_t *mddev, char *page)
+degraded_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%d\n", mddev->degraded);
}
static struct md_sysfs_entry md_degraded = __ATTR_RO(degraded);
static ssize_t
-sync_force_parallel_show(mddev_t *mddev, char *page)
+sync_force_parallel_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%d\n", mddev->parallel_resync);
}
static ssize_t
-sync_force_parallel_store(mddev_t *mddev, const char *buf, size_t len)
+sync_force_parallel_store(struct mddev *mddev, const char *buf, size_t len)
{
long n;
@@ -3933,7 +4194,7 @@ __ATTR(sync_force_parallel, S_IRUGO|S_IWUSR,
sync_force_parallel_show, sync_force_parallel_store);
static ssize_t
-sync_speed_show(mddev_t *mddev, char *page)
+sync_speed_show(struct mddev *mddev, char *page)
{
unsigned long resync, dt, db;
if (mddev->curr_resync == 0)
@@ -3948,7 +4209,7 @@ sync_speed_show(mddev_t *mddev, char *page)
static struct md_sysfs_entry md_sync_speed = __ATTR_RO(sync_speed);
static ssize_t
-sync_completed_show(mddev_t *mddev, char *page)
+sync_completed_show(struct mddev *mddev, char *page)
{
unsigned long long max_sectors, resync;
@@ -3967,13 +4228,13 @@ sync_completed_show(mddev_t *mddev, char *page)
static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed);
static ssize_t
-min_sync_show(mddev_t *mddev, char *page)
+min_sync_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%llu\n",
(unsigned long long)mddev->resync_min);
}
static ssize_t
-min_sync_store(mddev_t *mddev, const char *buf, size_t len)
+min_sync_store(struct mddev *mddev, const char *buf, size_t len)
{
unsigned long long min;
if (strict_strtoull(buf, 10, &min))
@@ -3998,7 +4259,7 @@ static struct md_sysfs_entry md_min_sync =
__ATTR(sync_min, S_IRUGO|S_IWUSR, min_sync_show, min_sync_store);
static ssize_t
-max_sync_show(mddev_t *mddev, char *page)
+max_sync_show(struct mddev *mddev, char *page)
{
if (mddev->resync_max == MaxSector)
return sprintf(page, "max\n");
@@ -4007,7 +4268,7 @@ max_sync_show(mddev_t *mddev, char *page)
(unsigned long long)mddev->resync_max);
}
static ssize_t
-max_sync_store(mddev_t *mddev, const char *buf, size_t len)
+max_sync_store(struct mddev *mddev, const char *buf, size_t len)
{
if (strncmp(buf, "max", 3) == 0)
mddev->resync_max = MaxSector;
@@ -4038,13 +4299,13 @@ static struct md_sysfs_entry md_max_sync =
__ATTR(sync_max, S_IRUGO|S_IWUSR, max_sync_show, max_sync_store);
static ssize_t
-suspend_lo_show(mddev_t *mddev, char *page)
+suspend_lo_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_lo);
}
static ssize_t
-suspend_lo_store(mddev_t *mddev, const char *buf, size_t len)
+suspend_lo_store(struct mddev *mddev, const char *buf, size_t len)
{
char *e;
unsigned long long new = simple_strtoull(buf, &e, 10);
@@ -4072,13 +4333,13 @@ __ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store);
static ssize_t
-suspend_hi_show(mddev_t *mddev, char *page)
+suspend_hi_show(struct mddev *mddev, char *page)
{
return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_hi);
}
static ssize_t
-suspend_hi_store(mddev_t *mddev, const char *buf, size_t len)
+suspend_hi_store(struct mddev *mddev, const char *buf, size_t len)
{
char *e;
unsigned long long new = simple_strtoull(buf, &e, 10);
@@ -4105,7 +4366,7 @@ static struct md_sysfs_entry md_suspend_hi =
__ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store);
static ssize_t
-reshape_position_show(mddev_t *mddev, char *page)
+reshape_position_show(struct mddev *mddev, char *page)
{
if (mddev->reshape_position != MaxSector)
return sprintf(page, "%llu\n",
@@ -4115,7 +4376,7 @@ reshape_position_show(mddev_t *mddev, char *page)
}
static ssize_t
-reshape_position_store(mddev_t *mddev, const char *buf, size_t len)
+reshape_position_store(struct mddev *mddev, const char *buf, size_t len)
{
char *e;
unsigned long long new = simple_strtoull(buf, &e, 10);
@@ -4136,7 +4397,7 @@ __ATTR(reshape_position, S_IRUGO|S_IWUSR, reshape_position_show,
reshape_position_store);
static ssize_t
-array_size_show(mddev_t *mddev, char *page)
+array_size_show(struct mddev *mddev, char *page)
{
if (mddev->external_size)
return sprintf(page, "%llu\n",
@@ -4146,7 +4407,7 @@ array_size_show(mddev_t *mddev, char *page)
}
static ssize_t
-array_size_store(mddev_t *mddev, const char *buf, size_t len)
+array_size_store(struct mddev *mddev, const char *buf, size_t len)
{
sector_t sectors;
@@ -4221,7 +4482,7 @@ static ssize_t
md_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
{
struct md_sysfs_entry *entry = container_of(attr, struct md_sysfs_entry, attr);
- mddev_t *mddev = container_of(kobj, struct mddev_s, kobj);
+ struct mddev *mddev = container_of(kobj, struct mddev, kobj);
ssize_t rv;
if (!entry->show)
@@ -4239,7 +4500,7 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
const char *page, size_t length)
{
struct md_sysfs_entry *entry = container_of(attr, struct md_sysfs_entry, attr);
- mddev_t *mddev = container_of(kobj, struct mddev_s, kobj);
+ struct mddev *mddev = container_of(kobj, struct mddev, kobj);
ssize_t rv;
if (!entry->store)
@@ -4258,7 +4519,7 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
static void md_free(struct kobject *ko)
{
- mddev_t *mddev = container_of(ko, mddev_t, kobj);
+ struct mddev *mddev = container_of(ko, struct mddev, kobj);
if (mddev->sysfs_state)
sysfs_put(mddev->sysfs_state);
@@ -4287,7 +4548,7 @@ int mdp_major = 0;
static void mddev_delayed_delete(struct work_struct *ws)
{
- mddev_t *mddev = container_of(ws, mddev_t, del_work);
+ struct mddev *mddev = container_of(ws, struct mddev, del_work);
sysfs_remove_group(&mddev->kobj, &md_bitmap_group);
kobject_del(&mddev->kobj);
@@ -4297,7 +4558,7 @@ static void mddev_delayed_delete(struct work_struct *ws)
static int md_alloc(dev_t dev, char *name)
{
static DEFINE_MUTEX(disks_mutex);
- mddev_t *mddev = mddev_find(dev);
+ struct mddev *mddev = mddev_find(dev);
struct gendisk *disk;
int partitioned;
int shift;
@@ -4324,7 +4585,7 @@ static int md_alloc(dev_t dev, char *name)
if (name) {
/* Need to ensure that 'name' is not a duplicate.
*/
- mddev_t *mddev2;
+ struct mddev *mddev2;
spin_lock(&all_mddevs_lock);
list_for_each_entry(mddev2, &all_mddevs, all_mddevs)
@@ -4425,7 +4686,7 @@ static int add_named_array(const char *val, struct kernel_param *kp)
static void md_safemode_timeout(unsigned long data)
{
- mddev_t *mddev = (mddev_t *) data;
+ struct mddev *mddev = (struct mddev *) data;
if (!atomic_read(&mddev->writes_pending)) {
mddev->safemode = 1;
@@ -4437,11 +4698,11 @@ static void md_safemode_timeout(unsigned long data)
static int start_dirty_degraded;
-int md_run(mddev_t *mddev)
+int md_run(struct mddev *mddev)
{
int err;
- mdk_rdev_t *rdev;
- struct mdk_personality *pers;
+ struct md_rdev *rdev;
+ struct md_personality *pers;
if (list_empty(&mddev->disks))
/* cannot run an array with no devices.. */
@@ -4504,7 +4765,8 @@ int md_run(mddev_t *mddev)
}
if (mddev->bio_set == NULL)
- mddev->bio_set = bioset_create(BIO_POOL_SIZE, sizeof(mddev));
+ mddev->bio_set = bioset_create(BIO_POOL_SIZE,
+ sizeof(struct mddev *));
spin_lock(&pers_lock);
pers = find_pers(mddev->level, mddev->clevel);
@@ -4539,7 +4801,7 @@ int md_run(mddev_t *mddev)
* configuration.
*/
char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
- mdk_rdev_t *rdev2;
+ struct md_rdev *rdev2;
int warned = 0;
list_for_each_entry(rdev, &mddev->disks, same_set)
@@ -4621,12 +4883,9 @@ int md_run(mddev_t *mddev)
smp_wmb();
mddev->ready = 1;
list_for_each_entry(rdev, &mddev->disks, same_set)
- if (rdev->raid_disk >= 0) {
- char nm[20];
- sprintf(nm, "rd%d", rdev->raid_disk);
- if (sysfs_create_link(&mddev->kobj, &rdev->kobj, nm))
+ if (rdev->raid_disk >= 0)
+ if (sysfs_link_rdev(mddev, rdev))
/* failure here is OK */;
- }
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
@@ -4641,7 +4900,7 @@ int md_run(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(md_run);
-static int do_md_run(mddev_t *mddev)
+static int do_md_run(struct mddev *mddev)
{
int err;
@@ -4665,7 +4924,7 @@ out:
return err;
}
-static int restart_array(mddev_t *mddev)
+static int restart_array(struct mddev *mddev)
{
struct gendisk *disk = mddev->gendisk;
@@ -4715,7 +4974,7 @@ void restore_bitmap_write_access(struct file *file)
spin_unlock(&inode->i_lock);
}
-static void md_clean(mddev_t *mddev)
+static void md_clean(struct mddev *mddev)
{
mddev->array_sectors = 0;
mddev->external_size = 0;
@@ -4758,7 +5017,7 @@ static void md_clean(mddev_t *mddev)
mddev->bitmap_info.max_write_behind = 0;
}
-static void __md_stop_writes(mddev_t *mddev)
+static void __md_stop_writes(struct mddev *mddev)
{
if (mddev->sync_thread) {
set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
@@ -4778,7 +5037,7 @@ static void __md_stop_writes(mddev_t *mddev)
}
}
-void md_stop_writes(mddev_t *mddev)
+void md_stop_writes(struct mddev *mddev)
{
mddev_lock(mddev);
__md_stop_writes(mddev);
@@ -4786,7 +5045,7 @@ void md_stop_writes(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(md_stop_writes);
-void md_stop(mddev_t *mddev)
+void md_stop(struct mddev *mddev)
{
mddev->ready = 0;
mddev->pers->stop(mddev);
@@ -4798,7 +5057,7 @@ void md_stop(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(md_stop);
-static int md_set_readonly(mddev_t *mddev, int is_open)
+static int md_set_readonly(struct mddev *mddev, int is_open)
{
int err = 0;
mutex_lock(&mddev->open_mutex);
@@ -4828,10 +5087,10 @@ out:
* 0 - completely stop and dis-assemble array
* 2 - stop but do not disassemble array
*/
-static int do_md_stop(mddev_t * mddev, int mode, int is_open)
+static int do_md_stop(struct mddev * mddev, int mode, int is_open)
{
struct gendisk *disk = mddev->gendisk;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
mutex_lock(&mddev->open_mutex);
if (atomic_read(&mddev->openers) > is_open ||
@@ -4854,11 +5113,8 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
sysfs_notify_dirent_safe(mddev->sysfs_state);
list_for_each_entry(rdev, &mddev->disks, same_set)
- if (rdev->raid_disk >= 0) {
- char nm[20];
- sprintf(nm, "rd%d", rdev->raid_disk);
- sysfs_remove_link(&mddev->kobj, nm);
- }
+ if (rdev->raid_disk >= 0)
+ sysfs_unlink_rdev(mddev, rdev);
set_capacity(disk, 0);
mutex_unlock(&mddev->open_mutex);
@@ -4897,9 +5153,9 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
}
#ifndef MODULE
-static void autorun_array(mddev_t *mddev)
+static void autorun_array(struct mddev *mddev)
{
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
int err;
if (list_empty(&mddev->disks))
@@ -4934,8 +5190,8 @@ static void autorun_array(mddev_t *mddev)
*/
static void autorun_devices(int part)
{
- mdk_rdev_t *rdev0, *rdev, *tmp;
- mddev_t *mddev;
+ struct md_rdev *rdev0, *rdev, *tmp;
+ struct mddev *mddev;
char b[BDEVNAME_SIZE];
printk(KERN_INFO "md: autorun ...\n");
@@ -4944,7 +5200,7 @@ static void autorun_devices(int part)
dev_t dev;
LIST_HEAD(candidates);
rdev0 = list_entry(pending_raid_disks.next,
- mdk_rdev_t, same_set);
+ struct md_rdev, same_set);
printk(KERN_INFO "md: considering %s ...\n",
bdevname(rdev0->bdev,b));
@@ -5030,11 +5286,11 @@ static int get_version(void __user * arg)
return 0;
}
-static int get_array_info(mddev_t * mddev, void __user * arg)
+static int get_array_info(struct mddev * mddev, void __user * arg)
{
mdu_array_info_t info;
int nr,working,insync,failed,spare;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
nr=working=insync=failed=spare=0;
list_for_each_entry(rdev, &mddev->disks, same_set) {
@@ -5083,7 +5339,7 @@ static int get_array_info(mddev_t * mddev, void __user * arg)
return 0;
}
-static int get_bitmap_file(mddev_t * mddev, void __user * arg)
+static int get_bitmap_file(struct mddev * mddev, void __user * arg)
{
mdu_bitmap_file_t *file = NULL; /* too big for stack allocation */
char *ptr, *buf = NULL;
@@ -5123,10 +5379,10 @@ out:
return err;
}
-static int get_disk_info(mddev_t * mddev, void __user * arg)
+static int get_disk_info(struct mddev * mddev, void __user * arg)
{
mdu_disk_info_t info;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
if (copy_from_user(&info, arg, sizeof(info)))
return -EFAULT;
@@ -5157,10 +5413,10 @@ static int get_disk_info(mddev_t * mddev, void __user * arg)
return 0;
}
-static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
+static int add_new_disk(struct mddev * mddev, mdu_disk_info_t *info)
{
char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
dev_t dev = MKDEV(info->major,info->minor);
if (info->major != MAJOR(dev) || info->minor != MINOR(dev))
@@ -5177,8 +5433,9 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
return PTR_ERR(rdev);
}
if (!list_empty(&mddev->disks)) {
- mdk_rdev_t *rdev0 = list_entry(mddev->disks.next,
- mdk_rdev_t, same_set);
+ struct md_rdev *rdev0
+ = list_entry(mddev->disks.next,
+ struct md_rdev, same_set);
err = super_types[mddev->major_version]
.load_super(rdev, rdev0, mddev->minor_version);
if (err < 0) {
@@ -5328,10 +5585,10 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
return 0;
}
-static int hot_remove_disk(mddev_t * mddev, dev_t dev)
+static int hot_remove_disk(struct mddev * mddev, dev_t dev)
{
char b[BDEVNAME_SIZE];
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
rdev = find_rdev(mddev, dev);
if (!rdev)
@@ -5351,11 +5608,11 @@ busy:
return -EBUSY;
}
-static int hot_add_disk(mddev_t * mddev, dev_t dev)
+static int hot_add_disk(struct mddev * mddev, dev_t dev)
{
char b[BDEVNAME_SIZE];
int err;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
if (!mddev->pers)
return -ENODEV;
@@ -5425,7 +5682,7 @@ abort_export:
return err;
}
-static int set_bitmap_file(mddev_t *mddev, int fd)
+static int set_bitmap_file(struct mddev *mddev, int fd)
{
int err;
@@ -5498,7 +5755,7 @@ static int set_bitmap_file(mddev_t *mddev, int fd)
* The minor and patch _version numbers are also kept incase the
* super_block handler wishes to interpret them.
*/
-static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
+static int set_array_info(struct mddev * mddev, mdu_array_info_t *info)
{
if (info->raid_disks == 0) {
@@ -5568,7 +5825,7 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
return 0;
}
-void md_set_array_sectors(mddev_t *mddev, sector_t array_sectors)
+void md_set_array_sectors(struct mddev *mddev, sector_t array_sectors)
{
WARN(!mddev_is_locked(mddev), "%s: unlocked mddev!\n", __func__);
@@ -5579,9 +5836,9 @@ void md_set_array_sectors(mddev_t *mddev, sector_t array_sectors)
}
EXPORT_SYMBOL(md_set_array_sectors);
-static int update_size(mddev_t *mddev, sector_t num_sectors)
+static int update_size(struct mddev *mddev, sector_t num_sectors)
{
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
int rv;
int fit = (num_sectors == 0);
@@ -5617,7 +5874,7 @@ static int update_size(mddev_t *mddev, sector_t num_sectors)
return rv;
}
-static int update_raid_disks(mddev_t *mddev, int raid_disks)
+static int update_raid_disks(struct mddev *mddev, int raid_disks)
{
int rv;
/* change the number of raid disks */
@@ -5645,7 +5902,7 @@ static int update_raid_disks(mddev_t *mddev, int raid_disks)
* Any differences that cannot be handled will cause an error.
* Normally, only one change can be managed at a time.
*/
-static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
+static int update_array_info(struct mddev *mddev, mdu_array_info_t *info)
{
int rv = 0;
int cnt = 0;
@@ -5738,9 +5995,9 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
return rv;
}
-static int set_disk_faulty(mddev_t *mddev, dev_t dev)
+static int set_disk_faulty(struct mddev *mddev, dev_t dev)
{
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
if (mddev->pers == NULL)
return -ENODEV;
@@ -5750,6 +6007,8 @@ static int set_disk_faulty(mddev_t *mddev, dev_t dev)
return -ENODEV;
md_error(mddev, rdev);
+ if (!test_bit(Faulty, &rdev->flags))
+ return -EBUSY;
return 0;
}
@@ -5761,7 +6020,7 @@ static int set_disk_faulty(mddev_t *mddev, dev_t dev)
*/
static int md_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
- mddev_t *mddev = bdev->bd_disk->private_data;
+ struct mddev *mddev = bdev->bd_disk->private_data;
geo->heads = 2;
geo->sectors = 4;
@@ -5774,7 +6033,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
{
int err = 0;
void __user *argp = (void __user *)arg;
- mddev_t *mddev = NULL;
+ struct mddev *mddev = NULL;
int ro;
if (!capable(CAP_SYS_ADMIN))
@@ -6037,7 +6296,7 @@ static int md_open(struct block_device *bdev, fmode_t mode)
* Succeed if we can lock the mddev, which confirms that
* it isn't being stopped right now.
*/
- mddev_t *mddev = mddev_find(bdev->bd_dev);
+ struct mddev *mddev = mddev_find(bdev->bd_dev);
int err;
if (mddev->gendisk != bdev->bd_disk) {
@@ -6066,7 +6325,7 @@ static int md_open(struct block_device *bdev, fmode_t mode)
static int md_release(struct gendisk *disk, fmode_t mode)
{
- mddev_t *mddev = disk->private_data;
+ struct mddev *mddev = disk->private_data;
BUG_ON(!mddev);
atomic_dec(&mddev->openers);
@@ -6077,14 +6336,14 @@ static int md_release(struct gendisk *disk, fmode_t mode)
static int md_media_changed(struct gendisk *disk)
{
- mddev_t *mddev = disk->private_data;
+ struct mddev *mddev = disk->private_data;
return mddev->changed;
}
static int md_revalidate(struct gendisk *disk)
{
- mddev_t *mddev = disk->private_data;
+ struct mddev *mddev = disk->private_data;
mddev->changed = 0;
return 0;
@@ -6105,7 +6364,7 @@ static const struct block_device_operations md_fops =
static int md_thread(void * arg)
{
- mdk_thread_t *thread = arg;
+ struct md_thread *thread = arg;
/*
* md_thread is a 'system-thread', it's priority should be very
@@ -6144,21 +6403,21 @@ static int md_thread(void * arg)
return 0;
}
-void md_wakeup_thread(mdk_thread_t *thread)
+void md_wakeup_thread(struct md_thread *thread)
{
if (thread) {
- dprintk("md: waking up MD thread %s.\n", thread->tsk->comm);
+ pr_debug("md: waking up MD thread %s.\n", thread->tsk->comm);
set_bit(THREAD_WAKEUP, &thread->flags);
wake_up(&thread->wqueue);
}
}
-mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev,
+struct md_thread *md_register_thread(void (*run) (struct mddev *), struct mddev *mddev,
const char *name)
{
- mdk_thread_t *thread;
+ struct md_thread *thread;
- thread = kzalloc(sizeof(mdk_thread_t), GFP_KERNEL);
+ thread = kzalloc(sizeof(struct md_thread), GFP_KERNEL);
if (!thread)
return NULL;
@@ -6178,17 +6437,24 @@ mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev,
return thread;
}
-void md_unregister_thread(mdk_thread_t *thread)
+void md_unregister_thread(struct md_thread **threadp)
{
+ struct md_thread *thread = *threadp;
if (!thread)
return;
- dprintk("interrupting MD-thread pid %d\n", task_pid_nr(thread->tsk));
+ pr_debug("interrupting MD-thread pid %d\n", task_pid_nr(thread->tsk));
+ /* Locking ensures that mddev_unlock does not wake_up a
+ * non-existent thread
+ */
+ spin_lock(&pers_lock);
+ *threadp = NULL;
+ spin_unlock(&pers_lock);
kthread_stop(thread->tsk);
kfree(thread);
}
-void md_error(mddev_t *mddev, mdk_rdev_t *rdev)
+void md_error(struct mddev *mddev, struct md_rdev *rdev)
{
if (!mddev) {
MD_BUG();
@@ -6198,18 +6464,7 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev)
if (!rdev || test_bit(Faulty, &rdev->flags))
return;
- if (mddev->external)
- set_bit(Blocked, &rdev->flags);
-/*
- dprintk("md_error dev:%s, rdev:(%d:%d), (caller: %p,%p,%p,%p).\n",
- mdname(mddev),
- MAJOR(rdev->bdev->bd_dev), MINOR(rdev->bdev->bd_dev),
- __builtin_return_address(0),__builtin_return_address(1),
- __builtin_return_address(2),__builtin_return_address(3));
-*/
- if (!mddev->pers)
- return;
- if (!mddev->pers->error_handler)
+ if (!mddev->pers || !mddev->pers->error_handler)
return;
mddev->pers->error_handler(mddev,rdev);
if (mddev->degraded)
@@ -6228,7 +6483,7 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev)
static void status_unused(struct seq_file *seq)
{
int i = 0;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
seq_printf(seq, "unused devices: ");
@@ -6245,7 +6500,7 @@ static void status_unused(struct seq_file *seq)
}
-static void status_resync(struct seq_file *seq, mddev_t * mddev)
+static void status_resync(struct seq_file *seq, struct mddev * mddev)
{
sector_t max_sectors, resync, res;
unsigned long dt, db;
@@ -6336,7 +6591,7 @@ static void *md_seq_start(struct seq_file *seq, loff_t *pos)
{
struct list_head *tmp;
loff_t l = *pos;
- mddev_t *mddev;
+ struct mddev *mddev;
if (l >= 0x10000)
return NULL;
@@ -6347,7 +6602,7 @@ static void *md_seq_start(struct seq_file *seq, loff_t *pos)
spin_lock(&all_mddevs_lock);
list_for_each(tmp,&all_mddevs)
if (!l--) {
- mddev = list_entry(tmp, mddev_t, all_mddevs);
+ mddev = list_entry(tmp, struct mddev, all_mddevs);
mddev_get(mddev);
spin_unlock(&all_mddevs_lock);
return mddev;
@@ -6361,7 +6616,7 @@ static void *md_seq_start(struct seq_file *seq, loff_t *pos)
static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct list_head *tmp;
- mddev_t *next_mddev, *mddev = v;
+ struct mddev *next_mddev, *mddev = v;
++*pos;
if (v == (void*)2)
@@ -6373,7 +6628,7 @@ static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos)
else
tmp = mddev->all_mddevs.next;
if (tmp != &all_mddevs)
- next_mddev = mddev_get(list_entry(tmp,mddev_t,all_mddevs));
+ next_mddev = mddev_get(list_entry(tmp,struct mddev,all_mddevs));
else {
next_mddev = (void*)2;
*pos = 0x10000;
@@ -6388,26 +6643,21 @@ static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static void md_seq_stop(struct seq_file *seq, void *v)
{
- mddev_t *mddev = v;
+ struct mddev *mddev = v;
if (mddev && v != (void*)1 && v != (void*)2)
mddev_put(mddev);
}
-struct mdstat_info {
- int event;
-};
-
static int md_seq_show(struct seq_file *seq, void *v)
{
- mddev_t *mddev = v;
+ struct mddev *mddev = v;
sector_t sectors;
- mdk_rdev_t *rdev;
- struct mdstat_info *mi = seq->private;
+ struct md_rdev *rdev;
struct bitmap *bitmap;
if (v == (void*)1) {
- struct mdk_personality *pers;
+ struct md_personality *pers;
seq_printf(seq, "Personalities : ");
spin_lock(&pers_lock);
list_for_each_entry(pers, &pers_list, list)
@@ -6415,7 +6665,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
spin_unlock(&pers_lock);
seq_printf(seq, "\n");
- mi->event = atomic_read(&md_event_count);
+ seq->poll_event = atomic_read(&md_event_count);
return 0;
}
if (v == (void*)2) {
@@ -6527,26 +6777,21 @@ static const struct seq_operations md_seq_ops = {
static int md_seq_open(struct inode *inode, struct file *file)
{
+ struct seq_file *seq;
int error;
- struct mdstat_info *mi = kmalloc(sizeof(*mi), GFP_KERNEL);
- if (mi == NULL)
- return -ENOMEM;
error = seq_open(file, &md_seq_ops);
if (error)
- kfree(mi);
- else {
- struct seq_file *p = file->private_data;
- p->private = mi;
- mi->event = atomic_read(&md_event_count);
- }
+ return error;
+
+ seq = file->private_data;
+ seq->poll_event = atomic_read(&md_event_count);
return error;
}
static unsigned int mdstat_poll(struct file *filp, poll_table *wait)
{
- struct seq_file *m = filp->private_data;
- struct mdstat_info *mi = m->private;
+ struct seq_file *seq = filp->private_data;
int mask;
poll_wait(filp, &md_event_waiters, wait);
@@ -6554,7 +6799,7 @@ static unsigned int mdstat_poll(struct file *filp, poll_table *wait)
/* always allow read */
mask = POLLIN | POLLRDNORM;
- if (mi->event != atomic_read(&md_event_count))
+ if (seq->poll_event != atomic_read(&md_event_count))
mask |= POLLERR | POLLPRI;
return mask;
}
@@ -6568,7 +6813,7 @@ static const struct file_operations md_seq_fops = {
.poll = mdstat_poll,
};
-int register_md_personality(struct mdk_personality *p)
+int register_md_personality(struct md_personality *p)
{
spin_lock(&pers_lock);
list_add_tail(&p->list, &pers_list);
@@ -6577,7 +6822,7 @@ int register_md_personality(struct mdk_personality *p)
return 0;
}
-int unregister_md_personality(struct mdk_personality *p)
+int unregister_md_personality(struct md_personality *p)
{
printk(KERN_INFO "md: %s personality unregistered\n", p->name);
spin_lock(&pers_lock);
@@ -6586,9 +6831,9 @@ int unregister_md_personality(struct mdk_personality *p)
return 0;
}
-static int is_mddev_idle(mddev_t *mddev, int init)
+static int is_mddev_idle(struct mddev *mddev, int init)
{
- mdk_rdev_t * rdev;
+ struct md_rdev * rdev;
int idle;
int curr_events;
@@ -6630,7 +6875,7 @@ static int is_mddev_idle(mddev_t *mddev, int init)
return idle;
}
-void md_done_sync(mddev_t *mddev, int blocks, int ok)
+void md_done_sync(struct mddev *mddev, int blocks, int ok)
{
/* another "blocks" (512byte) blocks have been synced */
atomic_sub(blocks, &mddev->recovery_active);
@@ -6648,7 +6893,7 @@ void md_done_sync(mddev_t *mddev, int blocks, int ok)
* in superblock) before writing, schedule a superblock update
* and wait for it to complete.
*/
-void md_write_start(mddev_t *mddev, struct bio *bi)
+void md_write_start(struct mddev *mddev, struct bio *bi)
{
int did_change = 0;
if (bio_data_dir(bi) != WRITE)
@@ -6683,7 +6928,7 @@ void md_write_start(mddev_t *mddev, struct bio *bi)
!test_bit(MD_CHANGE_PENDING, &mddev->flags));
}
-void md_write_end(mddev_t *mddev)
+void md_write_end(struct mddev *mddev)
{
if (atomic_dec_and_test(&mddev->writes_pending)) {
if (mddev->safemode == 2)
@@ -6702,7 +6947,7 @@ void md_write_end(mddev_t *mddev)
* In the ->external case MD_CHANGE_CLEAN can not be cleared until mddev->lock
* is dropped, so return -EAGAIN after notifying userspace.
*/
-int md_allow_write(mddev_t *mddev)
+int md_allow_write(struct mddev *mddev)
{
if (!mddev->pers)
return 0;
@@ -6734,9 +6979,9 @@ EXPORT_SYMBOL_GPL(md_allow_write);
#define SYNC_MARKS 10
#define SYNC_MARK_STEP (3*HZ)
-void md_do_sync(mddev_t *mddev)
+void md_do_sync(struct mddev *mddev)
{
- mddev_t *mddev2;
+ struct mddev *mddev2;
unsigned int currspeed = 0,
window;
sector_t max_sectors,j, io_sectors;
@@ -6746,7 +6991,7 @@ void md_do_sync(mddev_t *mddev)
struct list_head *tmp;
sector_t last_check;
int skipped = 0;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
char *desc;
/* just incase thread restarts... */
@@ -6943,11 +7188,14 @@ void md_do_sync(mddev_t *mddev)
atomic_add(sectors, &mddev->recovery_active);
}
+ if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
+ break;
+
j += sectors;
if (j>1) mddev->curr_resync = j;
mddev->curr_mark_cnt = io_sectors;
if (last_check == 0)
- /* this is the earliers that rebuilt will be
+ /* this is the earliest that rebuild will be
* visible in /proc/mdstat
*/
md_new_event(mddev);
@@ -6956,10 +7204,6 @@ void md_do_sync(mddev_t *mddev)
continue;
last_check = io_sectors;
-
- if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
- break;
-
repeat:
if (time_after_eq(jiffies, mark[last_mark] + SYNC_MARK_STEP )) {
/* step marks */
@@ -7062,9 +7306,9 @@ void md_do_sync(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(md_do_sync);
-static int remove_and_add_spares(mddev_t *mddev)
+static int remove_and_add_spares(struct mddev *mddev)
{
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
int spares = 0;
mddev->curr_resync_completed = 0;
@@ -7077,29 +7321,23 @@ static int remove_and_add_spares(mddev_t *mddev)
atomic_read(&rdev->nr_pending)==0) {
if (mddev->pers->hot_remove_disk(
mddev, rdev->raid_disk)==0) {
- char nm[20];
- sprintf(nm,"rd%d", rdev->raid_disk);
- sysfs_remove_link(&mddev->kobj, nm);
+ sysfs_unlink_rdev(mddev, rdev);
rdev->raid_disk = -1;
}
}
- if (mddev->degraded && !mddev->recovery_disabled) {
+ if (mddev->degraded) {
list_for_each_entry(rdev, &mddev->disks, same_set) {
if (rdev->raid_disk >= 0 &&
!test_bit(In_sync, &rdev->flags) &&
- !test_bit(Faulty, &rdev->flags) &&
- !test_bit(Blocked, &rdev->flags))
+ !test_bit(Faulty, &rdev->flags))
spares++;
if (rdev->raid_disk < 0
&& !test_bit(Faulty, &rdev->flags)) {
rdev->recovery_offset = 0;
if (mddev->pers->
hot_add_disk(mddev, rdev) == 0) {
- char nm[20];
- sprintf(nm, "rd%d", rdev->raid_disk);
- if (sysfs_create_link(&mddev->kobj,
- &rdev->kobj, nm))
+ if (sysfs_link_rdev(mddev, rdev))
/* failure here is OK */;
spares++;
md_new_event(mddev);
@@ -7112,13 +7350,12 @@ static int remove_and_add_spares(mddev_t *mddev)
return spares;
}
-static void reap_sync_thread(mddev_t *mddev)
+static void reap_sync_thread(struct mddev *mddev)
{
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
/* resync has finished, collect result */
- md_unregister_thread(mddev->sync_thread);
- mddev->sync_thread = NULL;
+ md_unregister_thread(&mddev->sync_thread);
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
/* success...*/
@@ -7130,15 +7367,19 @@ static void reap_sync_thread(mddev_t *mddev)
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
mddev->pers->finish_reshape)
mddev->pers->finish_reshape(mddev);
- md_update_sb(mddev, 1);
- /* if array is no-longer degraded, then any saved_raid_disk
- * information must be scrapped
+ /* If array is no-longer degraded, then any saved_raid_disk
+ * information must be scrapped. Also if any device is now
+ * In_sync we must scrape the saved_raid_disk for that device
+ * do the superblock for an incrementally recovered device
+ * written out.
*/
- if (!mddev->degraded)
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ list_for_each_entry(rdev, &mddev->disks, same_set)
+ if (!mddev->degraded ||
+ test_bit(In_sync, &rdev->flags))
rdev->saved_raid_disk = -1;
+ md_update_sb(mddev, 1);
clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
clear_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
@@ -7148,6 +7389,8 @@ static void reap_sync_thread(mddev_t *mddev)
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
sysfs_notify_dirent_safe(mddev->sysfs_action);
md_new_event(mddev);
+ if (mddev->event_work.func)
+ queue_work(md_misc_wq, &mddev->event_work);
}
/*
@@ -7172,7 +7415,7 @@ static void reap_sync_thread(mddev_t *mddev)
* 5/ If array is degraded, try to add spares devices
* 6/ If array has spares or is not in-sync, start a resync thread.
*/
-void md_check_recovery(mddev_t *mddev)
+void md_check_recovery(struct mddev *mddev)
{
if (mddev->suspended)
return;
@@ -7180,9 +7423,6 @@ void md_check_recovery(mddev_t *mddev)
if (mddev->bitmap)
bitmap_daemon_work(mddev);
- if (mddev->ro)
- return;
-
if (signal_pending(current)) {
if (mddev->pers->sync_request && !mddev->external) {
printk(KERN_INFO "md: %s in immediate safe mode\n",
@@ -7211,7 +7451,7 @@ void md_check_recovery(mddev_t *mddev)
/* Only thing we do on a ro array is remove
* failed devices.
*/
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
list_for_each_entry(rdev, &mddev->disks, same_set)
if (rdev->raid_disk >= 0 &&
!test_bit(Blocked, &rdev->flags) &&
@@ -7219,9 +7459,7 @@ void md_check_recovery(mddev_t *mddev)
atomic_read(&rdev->nr_pending)==0) {
if (mddev->pers->hot_remove_disk(
mddev, rdev->raid_disk)==0) {
- char nm[20];
- sprintf(nm,"rd%d", rdev->raid_disk);
- sysfs_remove_link(&mddev->kobj, nm);
+ sysfs_unlink_rdev(mddev, rdev);
rdev->raid_disk = -1;
}
}
@@ -7337,27 +7575,515 @@ void md_check_recovery(mddev_t *mddev)
}
}
-void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
+void md_wait_for_blocked_rdev(struct md_rdev *rdev, struct mddev *mddev)
{
sysfs_notify_dirent_safe(rdev->sysfs_state);
wait_event_timeout(rdev->blocked_wait,
- !test_bit(Blocked, &rdev->flags),
+ !test_bit(Blocked, &rdev->flags) &&
+ !test_bit(BlockedBadBlocks, &rdev->flags),
msecs_to_jiffies(5000));
rdev_dec_pending(rdev, mddev);
}
EXPORT_SYMBOL(md_wait_for_blocked_rdev);
+
+/* Bad block management.
+ * We can record which blocks on each device are 'bad' and so just
+ * fail those blocks, or that stripe, rather than the whole device.
+ * Entries in the bad-block table are 64bits wide. This comprises:
+ * Length of bad-range, in sectors: 0-511 for lengths 1-512
+ * Start of bad-range, sector offset, 54 bits (allows 8 exbibytes)
+ * A 'shift' can be set so that larger blocks are tracked and
+ * consequently larger devices can be covered.
+ * 'Acknowledged' flag - 1 bit. - the most significant bit.
+ *
+ * Locking of the bad-block table uses a seqlock so md_is_badblock
+ * might need to retry if it is very unlucky.
+ * We will sometimes want to check for bad blocks in a bi_end_io function,
+ * so we use the write_seqlock_irq variant.
+ *
+ * When looking for a bad block we specify a range and want to
+ * know if any block in the range is bad. So we binary-search
+ * to the last range that starts at-or-before the given endpoint,
+ * (or "before the sector after the target range")
+ * then see if it ends after the given start.
+ * We return
+ * 0 if there are no known bad blocks in the range
+ * 1 if there are known bad block which are all acknowledged
+ * -1 if there are bad blocks which have not yet been acknowledged in metadata.
+ * plus the start/length of the first bad section we overlap.
+ */
+int md_is_badblock(struct badblocks *bb, sector_t s, int sectors,
+ sector_t *first_bad, int *bad_sectors)
+{
+ int hi;
+ int lo = 0;
+ u64 *p = bb->page;
+ int rv = 0;
+ sector_t target = s + sectors;
+ unsigned seq;
+
+ if (bb->shift > 0) {
+ /* round the start down, and the end up */
+ s >>= bb->shift;
+ target += (1<<bb->shift) - 1;
+ target >>= bb->shift;
+ sectors = target - s;
+ }
+ /* 'target' is now the first block after the bad range */
+
+retry:
+ seq = read_seqbegin(&bb->lock);
+
+ hi = bb->count;
+
+ /* Binary search between lo and hi for 'target'
+ * i.e. for the last range that starts before 'target'
+ */
+ /* INVARIANT: ranges before 'lo' and at-or-after 'hi'
+ * are known not to be the last range before target.
+ * VARIANT: hi-lo is the number of possible
+ * ranges, and decreases until it reaches 1
+ */
+ while (hi - lo > 1) {
+ int mid = (lo + hi) / 2;
+ sector_t a = BB_OFFSET(p[mid]);
+ if (a < target)
+ /* This could still be the one, earlier ranges
+ * could not. */
+ lo = mid;
+ else
+ /* This and later ranges are definitely out. */
+ hi = mid;
+ }
+ /* 'lo' might be the last that started before target, but 'hi' isn't */
+ if (hi > lo) {
+ /* need to check all range that end after 's' to see if
+ * any are unacknowledged.
+ */
+ while (lo >= 0 &&
+ BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) {
+ if (BB_OFFSET(p[lo]) < target) {
+ /* starts before the end, and finishes after
+ * the start, so they must overlap
+ */
+ if (rv != -1 && BB_ACK(p[lo]))
+ rv = 1;
+ else
+ rv = -1;
+ *first_bad = BB_OFFSET(p[lo]);
+ *bad_sectors = BB_LEN(p[lo]);
+ }
+ lo--;
+ }
+ }
+
+ if (read_seqretry(&bb->lock, seq))
+ goto retry;
+
+ return rv;
+}
+EXPORT_SYMBOL_GPL(md_is_badblock);
+
+/*
+ * Add a range of bad blocks to the table.
+ * This might extend the table, or might contract it
+ * if two adjacent ranges can be merged.
+ * We binary-search to find the 'insertion' point, then
+ * decide how best to handle it.
+ */
+static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
+ int acknowledged)
+{
+ u64 *p;
+ int lo, hi;
+ int rv = 1;
+
+ if (bb->shift < 0)
+ /* badblocks are disabled */
+ return 0;
+
+ if (bb->shift) {
+ /* round the start down, and the end up */
+ sector_t next = s + sectors;
+ s >>= bb->shift;
+ next += (1<<bb->shift) - 1;
+ next >>= bb->shift;
+ sectors = next - s;
+ }
+
+ write_seqlock_irq(&bb->lock);
+
+ p = bb->page;
+ lo = 0;
+ hi = bb->count;
+ /* Find the last range that starts at-or-before 's' */
+ while (hi - lo > 1) {
+ int mid = (lo + hi) / 2;
+ sector_t a = BB_OFFSET(p[mid]);
+ if (a <= s)
+ lo = mid;
+ else
+ hi = mid;
+ }
+ if (hi > lo && BB_OFFSET(p[lo]) > s)
+ hi = lo;
+
+ if (hi > lo) {
+ /* we found a range that might merge with the start
+ * of our new range
+ */
+ sector_t a = BB_OFFSET(p[lo]);
+ sector_t e = a + BB_LEN(p[lo]);
+ int ack = BB_ACK(p[lo]);
+ if (e >= s) {
+ /* Yes, we can merge with a previous range */
+ if (s == a && s + sectors >= e)
+ /* new range covers old */
+ ack = acknowledged;
+ else
+ ack = ack && acknowledged;
+
+ if (e < s + sectors)
+ e = s + sectors;
+ if (e - a <= BB_MAX_LEN) {
+ p[lo] = BB_MAKE(a, e-a, ack);
+ s = e;
+ } else {
+ /* does not all fit in one range,
+ * make p[lo] maximal
+ */
+ if (BB_LEN(p[lo]) != BB_MAX_LEN)
+ p[lo] = BB_MAKE(a, BB_MAX_LEN, ack);
+ s = a + BB_MAX_LEN;
+ }
+ sectors = e - s;
+ }
+ }
+ if (sectors && hi < bb->count) {
+ /* 'hi' points to the first range that starts after 's'.
+ * Maybe we can merge with the start of that range */
+ sector_t a = BB_OFFSET(p[hi]);
+ sector_t e = a + BB_LEN(p[hi]);
+ int ack = BB_ACK(p[hi]);
+ if (a <= s + sectors) {
+ /* merging is possible */
+ if (e <= s + sectors) {
+ /* full overlap */
+ e = s + sectors;
+ ack = acknowledged;
+ } else
+ ack = ack && acknowledged;
+
+ a = s;
+ if (e - a <= BB_MAX_LEN) {
+ p[hi] = BB_MAKE(a, e-a, ack);
+ s = e;
+ } else {
+ p[hi] = BB_MAKE(a, BB_MAX_LEN, ack);
+ s = a + BB_MAX_LEN;
+ }
+ sectors = e - s;
+ lo = hi;
+ hi++;
+ }
+ }
+ if (sectors == 0 && hi < bb->count) {
+ /* we might be able to combine lo and hi */
+ /* Note: 's' is at the end of 'lo' */
+ sector_t a = BB_OFFSET(p[hi]);
+ int lolen = BB_LEN(p[lo]);
+ int hilen = BB_LEN(p[hi]);
+ int newlen = lolen + hilen - (s - a);
+ if (s >= a && newlen < BB_MAX_LEN) {
+ /* yes, we can combine them */
+ int ack = BB_ACK(p[lo]) && BB_ACK(p[hi]);
+ p[lo] = BB_MAKE(BB_OFFSET(p[lo]), newlen, ack);
+ memmove(p + hi, p + hi + 1,
+ (bb->count - hi - 1) * 8);
+ bb->count--;
+ }
+ }
+ while (sectors) {
+ /* didn't merge (it all).
+ * Need to add a range just before 'hi' */
+ if (bb->count >= MD_MAX_BADBLOCKS) {
+ /* No room for more */
+ rv = 0;
+ break;
+ } else {
+ int this_sectors = sectors;
+ memmove(p + hi + 1, p + hi,
+ (bb->count - hi) * 8);
+ bb->count++;
+
+ if (this_sectors > BB_MAX_LEN)
+ this_sectors = BB_MAX_LEN;
+ p[hi] = BB_MAKE(s, this_sectors, acknowledged);
+ sectors -= this_sectors;
+ s += this_sectors;
+ }
+ }
+
+ bb->changed = 1;
+ if (!acknowledged)
+ bb->unacked_exist = 1;
+ write_sequnlock_irq(&bb->lock);
+
+ return rv;
+}
+
+int rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
+ int acknowledged)
+{
+ int rv = md_set_badblocks(&rdev->badblocks,
+ s + rdev->data_offset, sectors, acknowledged);
+ if (rv) {
+ /* Make sure they get written out promptly */
+ set_bit(MD_CHANGE_CLEAN, &rdev->mddev->flags);
+ md_wakeup_thread(rdev->mddev->thread);
+ }
+ return rv;
+}
+EXPORT_SYMBOL_GPL(rdev_set_badblocks);
+
+/*
+ * Remove a range of bad blocks from the table.
+ * This may involve extending the table if we spilt a region,
+ * but it must not fail. So if the table becomes full, we just
+ * drop the remove request.
+ */
+static int md_clear_badblocks(struct badblocks *bb, sector_t s, int sectors)
+{
+ u64 *p;
+ int lo, hi;
+ sector_t target = s + sectors;
+ int rv = 0;
+
+ if (bb->shift > 0) {
+ /* When clearing we round the start up and the end down.
+ * This should not matter as the shift should align with
+ * the block size and no rounding should ever be needed.
+ * However it is better the think a block is bad when it
+ * isn't than to think a block is not bad when it is.
+ */
+ s += (1<<bb->shift) - 1;
+ s >>= bb->shift;
+ target >>= bb->shift;
+ sectors = target - s;
+ }
+
+ write_seqlock_irq(&bb->lock);
+
+ p = bb->page;
+ lo = 0;
+ hi = bb->count;
+ /* Find the last range that starts before 'target' */
+ while (hi - lo > 1) {
+ int mid = (lo + hi) / 2;
+ sector_t a = BB_OFFSET(p[mid]);
+ if (a < target)
+ lo = mid;
+ else
+ hi = mid;
+ }
+ if (hi > lo) {
+ /* p[lo] is the last range that could overlap the
+ * current range. Earlier ranges could also overlap,
+ * but only this one can overlap the end of the range.
+ */
+ if (BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > target) {
+ /* Partial overlap, leave the tail of this range */
+ int ack = BB_ACK(p[lo]);
+ sector_t a = BB_OFFSET(p[lo]);
+ sector_t end = a + BB_LEN(p[lo]);
+
+ if (a < s) {
+ /* we need to split this range */
+ if (bb->count >= MD_MAX_BADBLOCKS) {
+ rv = 0;
+ goto out;
+ }
+ memmove(p+lo+1, p+lo, (bb->count - lo) * 8);
+ bb->count++;
+ p[lo] = BB_MAKE(a, s-a, ack);
+ lo++;
+ }
+ p[lo] = BB_MAKE(target, end - target, ack);
+ /* there is no longer an overlap */
+ hi = lo;
+ lo--;
+ }
+ while (lo >= 0 &&
+ BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) {
+ /* This range does overlap */
+ if (BB_OFFSET(p[lo]) < s) {
+ /* Keep the early parts of this range. */
+ int ack = BB_ACK(p[lo]);
+ sector_t start = BB_OFFSET(p[lo]);
+ p[lo] = BB_MAKE(start, s - start, ack);
+ /* now low doesn't overlap, so.. */
+ break;
+ }
+ lo--;
+ }
+ /* 'lo' is strictly before, 'hi' is strictly after,
+ * anything between needs to be discarded
+ */
+ if (hi - lo > 1) {
+ memmove(p+lo+1, p+hi, (bb->count - hi) * 8);
+ bb->count -= (hi - lo - 1);
+ }
+ }
+
+ bb->changed = 1;
+out:
+ write_sequnlock_irq(&bb->lock);
+ return rv;
+}
+
+int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors)
+{
+ return md_clear_badblocks(&rdev->badblocks,
+ s + rdev->data_offset,
+ sectors);
+}
+EXPORT_SYMBOL_GPL(rdev_clear_badblocks);
+
+/*
+ * Acknowledge all bad blocks in a list.
+ * This only succeeds if ->changed is clear. It is used by
+ * in-kernel metadata updates
+ */
+void md_ack_all_badblocks(struct badblocks *bb)
+{
+ if (bb->page == NULL || bb->changed)
+ /* no point even trying */
+ return;
+ write_seqlock_irq(&bb->lock);
+
+ if (bb->changed == 0) {
+ u64 *p = bb->page;
+ int i;
+ for (i = 0; i < bb->count ; i++) {
+ if (!BB_ACK(p[i])) {
+ sector_t start = BB_OFFSET(p[i]);
+ int len = BB_LEN(p[i]);
+ p[i] = BB_MAKE(start, len, 1);
+ }
+ }
+ bb->unacked_exist = 0;
+ }
+ write_sequnlock_irq(&bb->lock);
+}
+EXPORT_SYMBOL_GPL(md_ack_all_badblocks);
+
+/* sysfs access to bad-blocks list.
+ * We present two files.
+ * 'bad-blocks' lists sector numbers and lengths of ranges that
+ * are recorded as bad. The list is truncated to fit within
+ * the one-page limit of sysfs.
+ * Writing "sector length" to this file adds an acknowledged
+ * bad block list.
+ * 'unacknowledged-bad-blocks' lists bad blocks that have not yet
+ * been acknowledged. Writing to this file adds bad blocks
+ * without acknowledging them. This is largely for testing.
+ */
+
+static ssize_t
+badblocks_show(struct badblocks *bb, char *page, int unack)
+{
+ size_t len;
+ int i;
+ u64 *p = bb->page;
+ unsigned seq;
+
+ if (bb->shift < 0)
+ return 0;
+
+retry:
+ seq = read_seqbegin(&bb->lock);
+
+ len = 0;
+ i = 0;
+
+ while (len < PAGE_SIZE && i < bb->count) {
+ sector_t s = BB_OFFSET(p[i]);
+ unsigned int length = BB_LEN(p[i]);
+ int ack = BB_ACK(p[i]);
+ i++;
+
+ if (unack && ack)
+ continue;
+
+ len += snprintf(page+len, PAGE_SIZE-len, "%llu %u\n",
+ (unsigned long long)s << bb->shift,
+ length << bb->shift);
+ }
+ if (unack && len == 0)
+ bb->unacked_exist = 0;
+
+ if (read_seqretry(&bb->lock, seq))
+ goto retry;
+
+ return len;
+}
+
+#define DO_DEBUG 1
+
+static ssize_t
+badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack)
+{
+ unsigned long long sector;
+ int length;
+ char newline;
+#ifdef DO_DEBUG
+ /* Allow clearing via sysfs *only* for testing/debugging.
+ * Normally only a successful write may clear a badblock
+ */
+ int clear = 0;
+ if (page[0] == '-') {
+ clear = 1;
+ page++;
+ }
+#endif /* DO_DEBUG */
+
+ switch (sscanf(page, "%llu %d%c", &sector, &length, &newline)) {
+ case 3:
+ if (newline != '\n')
+ return -EINVAL;
+ case 2:
+ if (length <= 0)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+#ifdef DO_DEBUG
+ if (clear) {
+ md_clear_badblocks(bb, sector, length);
+ return len;
+ }
+#endif /* DO_DEBUG */
+ if (md_set_badblocks(bb, sector, length, !unack))
+ return len;
+ else
+ return -ENOSPC;
+}
+
static int md_notify_reboot(struct notifier_block *this,
unsigned long code, void *x)
{
struct list_head *tmp;
- mddev_t *mddev;
+ struct mddev *mddev;
+ int need_delay = 0;
if ((code == SYS_DOWN) || (code == SYS_HALT) || (code == SYS_POWER_OFF)) {
printk(KERN_INFO "md: stopping all md devices.\n");
- for_each_mddev(mddev, tmp)
+ for_each_mddev(mddev, tmp) {
if (mddev_trylock(mddev)) {
/* Force a switch to readonly even array
* appears to still be in use. Hence
@@ -7366,13 +8092,16 @@ static int md_notify_reboot(struct notifier_block *this,
md_set_readonly(mddev, 100);
mddev_unlock(mddev);
}
+ need_delay = 1;
+ }
/*
* certain more exotic SCSI devices are known to be
* volatile wrt too early system reboots. While the
* right place to handle this issue is the given
* driver, we do want to have a safe RAID driver ...
*/
- mdelay(1000*1);
+ if (need_delay)
+ mdelay(1000*1);
}
return NOTIFY_DONE;
}
@@ -7385,7 +8114,7 @@ static struct notifier_block md_notifier = {
static void md_geninit(void)
{
- dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t));
+ pr_debug("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t));
proc_create("mdstat", S_IRUGO, NULL, &md_seq_fops);
}
@@ -7460,7 +8189,7 @@ void md_autodetect_dev(dev_t dev)
static void autostart_arrays(int part)
{
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
struct detected_devices_node *node_detected_dev;
dev_t dev;
int i_scanned, i_passed;
@@ -7500,7 +8229,7 @@ static void autostart_arrays(int part)
static __exit void md_exit(void)
{
- mddev_t *mddev;
+ struct mddev *mddev;
struct list_head *tmp;
blk_unregister_region(MKDEV(MD_MAJOR,0), 1U << MINORBITS);
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 1c26c7a08ae6..cf742d9306ec 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -1,5 +1,5 @@
/*
- md_k.h : kernel internal structure of the Linux MD driver
+ md.h : kernel internal structure of the Linux MD driver
Copyright (C) 1996-98 Ingo Molnar, Gadi Oxman
This program is free software; you can redistribute it and/or modify
@@ -26,18 +26,21 @@
#define MaxSector (~(sector_t)0)
-typedef struct mddev_s mddev_t;
-typedef struct mdk_rdev_s mdk_rdev_t;
+/* Bad block numbers are stored sorted in a single page.
+ * 64bits is used for each block or extent.
+ * 54 bits are sector number, 9 bits are extent size,
+ * 1 bit is an 'acknowledged' flag.
+ */
+#define MD_MAX_BADBLOCKS (PAGE_SIZE/8)
/*
* MD's 'extended' device
*/
-struct mdk_rdev_s
-{
+struct md_rdev {
struct list_head same_set; /* RAID devices within the same set */
sector_t sectors; /* Device size (in 512bytes sectors) */
- mddev_t *mddev; /* RAID array if running */
+ struct mddev *mddev; /* RAID array if running */
int last_events; /* IO event timestamp */
/*
@@ -48,7 +51,7 @@ struct mdk_rdev_s
struct block_device *meta_bdev;
struct block_device *bdev; /* block device handle */
- struct page *sb_page;
+ struct page *sb_page, *bb_page;
int sb_loaded;
__u64 sb_events;
sector_t data_offset; /* start of data in array */
@@ -74,9 +77,29 @@ struct mdk_rdev_s
#define In_sync 2 /* device is in_sync with rest of array */
#define WriteMostly 4 /* Avoid reading if at all possible */
#define AutoDetected 7 /* added by auto-detect */
-#define Blocked 8 /* An error occurred on an externally
- * managed array, don't allow writes
+#define Blocked 8 /* An error occurred but has not yet
+ * been acknowledged by the metadata
+ * handler, so don't allow writes
* until it is cleared */
+#define WriteErrorSeen 9 /* A write error has been seen on this
+ * device
+ */
+#define FaultRecorded 10 /* Intermediate state for clearing
+ * Blocked. The Fault is/will-be
+ * recorded in the metadata, but that
+ * metadata hasn't been stored safely
+ * on disk yet.
+ */
+#define BlockedBadBlocks 11 /* A writer is blocked because they
+ * found an unacknowledged bad-block.
+ * This can safely be cleared at any
+ * time, and the writer will re-check.
+ * It may be set at any time, and at
+ * worst the writer will timeout and
+ * re-check. So setting it as
+ * accurately as possible is good, but
+ * not absolutely critical.
+ */
wait_queue_head_t blocked_wait;
int desc_nr; /* descriptor index in the superblock */
@@ -111,12 +134,57 @@ struct mdk_rdev_s
struct sysfs_dirent *sysfs_state; /* handle for 'state'
* sysfs entry */
+
+ struct badblocks {
+ int count; /* count of bad blocks */
+ int unacked_exist; /* there probably are unacknowledged
+ * bad blocks. This is only cleared
+ * when a read discovers none
+ */
+ int shift; /* shift from sectors to block size
+ * a -ve shift means badblocks are
+ * disabled.*/
+ u64 *page; /* badblock list */
+ int changed;
+ seqlock_t lock;
+
+ sector_t sector;
+ sector_t size; /* in sectors */
+ } badblocks;
};
-struct mddev_s
+#define BB_LEN_MASK (0x00000000000001FFULL)
+#define BB_OFFSET_MASK (0x7FFFFFFFFFFFFE00ULL)
+#define BB_ACK_MASK (0x8000000000000000ULL)
+#define BB_MAX_LEN 512
+#define BB_OFFSET(x) (((x) & BB_OFFSET_MASK) >> 9)
+#define BB_LEN(x) (((x) & BB_LEN_MASK) + 1)
+#define BB_ACK(x) (!!((x) & BB_ACK_MASK))
+#define BB_MAKE(a, l, ack) (((a)<<9) | ((l)-1) | ((u64)(!!(ack)) << 63))
+
+extern int md_is_badblock(struct badblocks *bb, sector_t s, int sectors,
+ sector_t *first_bad, int *bad_sectors);
+static inline int is_badblock(struct md_rdev *rdev, sector_t s, int sectors,
+ sector_t *first_bad, int *bad_sectors)
{
+ if (unlikely(rdev->badblocks.count)) {
+ int rv = md_is_badblock(&rdev->badblocks, rdev->data_offset + s,
+ sectors,
+ first_bad, bad_sectors);
+ if (rv)
+ *first_bad -= rdev->data_offset;
+ return rv;
+ }
+ return 0;
+}
+extern int rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
+ int acknowledged);
+extern int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors);
+extern void md_ack_all_badblocks(struct badblocks *bb);
+
+struct mddev {
void *private;
- struct mdk_personality *pers;
+ struct md_personality *pers;
dev_t unit;
int md_minor;
struct list_head disks;
@@ -183,8 +251,8 @@ struct mddev_s
atomic_t plug_cnt; /* If device is expecting
* more bios soon.
*/
- struct mdk_thread_s *thread; /* management thread */
- struct mdk_thread_s *sync_thread; /* doing resync or reconstruct */
+ struct md_thread *thread; /* management thread */
+ struct md_thread *sync_thread; /* doing resync or reconstruct */
sector_t curr_resync; /* last block scheduled */
/* As resync requests can complete out of order, we cannot easily track
* how much resync has been completed. So we occasionally pause until
@@ -239,9 +307,12 @@ struct mddev_s
#define MD_RECOVERY_FROZEN 9
unsigned long recovery;
- int recovery_disabled; /* if we detect that recovery
- * will always fail, set this
- * so we don't loop trying */
+ /* If a RAID personality determines that recovery (of a particular
+ * device) will fail due to a read error on the source device, it
+ * takes a copy of this number and does not attempt recovery again
+ * until this number changes.
+ */
+ int recovery_disabled;
int in_sync; /* know to not need resync */
/* 'open_mutex' avoids races between 'md_open' and 'do_md_stop', so
@@ -304,11 +375,6 @@ struct mddev_s
* hot-adding a bitmap. It should
* eventually be settable by sysfs.
*/
- /* When md is serving under dm, it might use a
- * dirty_log to store the bits.
- */
- struct dm_dirty_log *log;
-
struct mutex mutex;
unsigned long chunksize;
unsigned long daemon_sleep; /* how many jiffies between updates? */
@@ -331,11 +397,11 @@ struct mddev_s
atomic_t flush_pending;
struct work_struct flush_work;
struct work_struct event_work; /* used by dm to report failure event */
- void (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev);
+ void (*sync_super)(struct mddev *mddev, struct md_rdev *rdev);
};
-static inline void rdev_dec_pending(mdk_rdev_t *rdev, mddev_t *mddev)
+static inline void rdev_dec_pending(struct md_rdev *rdev, struct mddev *mddev)
{
int faulty = test_bit(Faulty, &rdev->flags);
if (atomic_dec_and_test(&rdev->nr_pending) && faulty)
@@ -347,35 +413,35 @@ static inline void md_sync_acct(struct block_device *bdev, unsigned long nr_sect
atomic_add(nr_sectors, &bdev->bd_contains->bd_disk->sync_io);
}
-struct mdk_personality
+struct md_personality
{
char *name;
int level;
struct list_head list;
struct module *owner;
- int (*make_request)(mddev_t *mddev, struct bio *bio);
- int (*run)(mddev_t *mddev);
- int (*stop)(mddev_t *mddev);
- void (*status)(struct seq_file *seq, mddev_t *mddev);
+ void (*make_request)(struct mddev *mddev, struct bio *bio);
+ int (*run)(struct mddev *mddev);
+ int (*stop)(struct mddev *mddev);
+ void (*status)(struct seq_file *seq, struct mddev *mddev);
/* error_handler must set ->faulty and clear ->in_sync
* if appropriate, and should abort recovery if needed
*/
- void (*error_handler)(mddev_t *mddev, mdk_rdev_t *rdev);
- int (*hot_add_disk) (mddev_t *mddev, mdk_rdev_t *rdev);
- int (*hot_remove_disk) (mddev_t *mddev, int number);
- int (*spare_active) (mddev_t *mddev);
- sector_t (*sync_request)(mddev_t *mddev, sector_t sector_nr, int *skipped, int go_faster);
- int (*resize) (mddev_t *mddev, sector_t sectors);
- sector_t (*size) (mddev_t *mddev, sector_t sectors, int raid_disks);
- int (*check_reshape) (mddev_t *mddev);
- int (*start_reshape) (mddev_t *mddev);
- void (*finish_reshape) (mddev_t *mddev);
+ void (*error_handler)(struct mddev *mddev, struct md_rdev *rdev);
+ int (*hot_add_disk) (struct mddev *mddev, struct md_rdev *rdev);
+ int (*hot_remove_disk) (struct mddev *mddev, int number);
+ int (*spare_active) (struct mddev *mddev);
+ sector_t (*sync_request)(struct mddev *mddev, sector_t sector_nr, int *skipped, int go_faster);
+ int (*resize) (struct mddev *mddev, sector_t sectors);
+ sector_t (*size) (struct mddev *mddev, sector_t sectors, int raid_disks);
+ int (*check_reshape) (struct mddev *mddev);
+ int (*start_reshape) (struct mddev *mddev);
+ void (*finish_reshape) (struct mddev *mddev);
/* quiesce moves between quiescence states
* 0 - fully active
* 1 - no new requests allowed
* others - reserved
*/
- void (*quiesce) (mddev_t *mddev, int state);
+ void (*quiesce) (struct mddev *mddev, int state);
/* takeover is used to transition an array from one
* personality to another. The new personality must be able
* to handle the data in the current layout.
@@ -385,14 +451,14 @@ struct mdk_personality
* This needs to be installed and then ->run used to activate the
* array.
*/
- void *(*takeover) (mddev_t *mddev);
+ void *(*takeover) (struct mddev *mddev);
};
struct md_sysfs_entry {
struct attribute attr;
- ssize_t (*show)(mddev_t *, char *);
- ssize_t (*store)(mddev_t *, const char *, size_t);
+ ssize_t (*show)(struct mddev *, char *);
+ ssize_t (*store)(struct mddev *, const char *, size_t);
};
extern struct attribute_group md_bitmap_group;
@@ -408,11 +474,25 @@ static inline void sysfs_notify_dirent_safe(struct sysfs_dirent *sd)
sysfs_notify_dirent(sd);
}
-static inline char * mdname (mddev_t * mddev)
+static inline char * mdname (struct mddev * mddev)
{
return mddev->gendisk ? mddev->gendisk->disk_name : "mdX";
}
+static inline int sysfs_link_rdev(struct mddev *mddev, struct md_rdev *rdev)
+{
+ char nm[20];
+ sprintf(nm, "rd%d", rdev->raid_disk);
+ return sysfs_create_link(&mddev->kobj, &rdev->kobj, nm);
+}
+
+static inline void sysfs_unlink_rdev(struct mddev *mddev, struct md_rdev *rdev)
+{
+ char nm[20];
+ sprintf(nm, "rd%d", rdev->raid_disk);
+ sysfs_remove_link(&mddev->kobj, nm);
+}
+
/*
* iterates through some rdev ringlist. It's safe to remove the
* current 'rdev'. Dont touch 'tmp' though.
@@ -429,14 +509,14 @@ static inline char * mdname (mddev_t * mddev)
#define rdev_for_each_rcu(rdev, mddev) \
list_for_each_entry_rcu(rdev, &((mddev)->disks), same_set)
-typedef struct mdk_thread_s {
- void (*run) (mddev_t *mddev);
- mddev_t *mddev;
+struct md_thread {
+ void (*run) (struct mddev *mddev);
+ struct mddev *mddev;
wait_queue_head_t wqueue;
unsigned long flags;
struct task_struct *tsk;
unsigned long timeout;
-} mdk_thread_t;
+};
#define THREAD_WAKEUP 0
@@ -471,47 +551,50 @@ static inline void safe_put_page(struct page *p)
if (p) put_page(p);
}
-extern int register_md_personality(struct mdk_personality *p);
-extern int unregister_md_personality(struct mdk_personality *p);
-extern mdk_thread_t * md_register_thread(void (*run) (mddev_t *mddev),
- mddev_t *mddev, const char *name);
-extern void md_unregister_thread(mdk_thread_t *thread);
-extern void md_wakeup_thread(mdk_thread_t *thread);
-extern void md_check_recovery(mddev_t *mddev);
-extern void md_write_start(mddev_t *mddev, struct bio *bi);
-extern void md_write_end(mddev_t *mddev);
-extern void md_done_sync(mddev_t *mddev, int blocks, int ok);
-extern void md_error(mddev_t *mddev, mdk_rdev_t *rdev);
-
-extern int mddev_congested(mddev_t *mddev, int bits);
-extern void md_flush_request(mddev_t *mddev, struct bio *bio);
-extern void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
+extern int register_md_personality(struct md_personality *p);
+extern int unregister_md_personality(struct md_personality *p);
+extern struct md_thread *md_register_thread(
+ void (*run)(struct mddev *mddev),
+ struct mddev *mddev,
+ const char *name);
+extern void md_unregister_thread(struct md_thread **threadp);
+extern void md_wakeup_thread(struct md_thread *thread);
+extern void md_check_recovery(struct mddev *mddev);
+extern void md_write_start(struct mddev *mddev, struct bio *bi);
+extern void md_write_end(struct mddev *mddev);
+extern void md_done_sync(struct mddev *mddev, int blocks, int ok);
+extern void md_error(struct mddev *mddev, struct md_rdev *rdev);
+
+extern int mddev_congested(struct mddev *mddev, int bits);
+extern void md_flush_request(struct mddev *mddev, struct bio *bio);
+extern void md_super_write(struct mddev *mddev, struct md_rdev *rdev,
sector_t sector, int size, struct page *page);
-extern void md_super_wait(mddev_t *mddev);
-extern int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size,
+extern void md_super_wait(struct mddev *mddev);
+extern int sync_page_io(struct md_rdev *rdev, sector_t sector, int size,
struct page *page, int rw, bool metadata_op);
-extern void md_do_sync(mddev_t *mddev);
-extern void md_new_event(mddev_t *mddev);
-extern int md_allow_write(mddev_t *mddev);
-extern void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev);
-extern void md_set_array_sectors(mddev_t *mddev, sector_t array_sectors);
-extern int md_check_no_bitmap(mddev_t *mddev);
-extern int md_integrity_register(mddev_t *mddev);
-extern void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev);
+extern void md_do_sync(struct mddev *mddev);
+extern void md_new_event(struct mddev *mddev);
+extern int md_allow_write(struct mddev *mddev);
+extern void md_wait_for_blocked_rdev(struct md_rdev *rdev, struct mddev *mddev);
+extern void md_set_array_sectors(struct mddev *mddev, sector_t array_sectors);
+extern int md_check_no_bitmap(struct mddev *mddev);
+extern int md_integrity_register(struct mddev *mddev);
+extern void md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev);
extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale);
extern void restore_bitmap_write_access(struct file *file);
-extern void mddev_init(mddev_t *mddev);
-extern int md_run(mddev_t *mddev);
-extern void md_stop(mddev_t *mddev);
-extern void md_stop_writes(mddev_t *mddev);
-extern void md_rdev_init(mdk_rdev_t *rdev);
+extern void mddev_init(struct mddev *mddev);
+extern int md_run(struct mddev *mddev);
+extern void md_stop(struct mddev *mddev);
+extern void md_stop_writes(struct mddev *mddev);
+extern int md_rdev_init(struct md_rdev *rdev);
-extern void mddev_suspend(mddev_t *mddev);
-extern void mddev_resume(mddev_t *mddev);
+extern void mddev_suspend(struct mddev *mddev);
+extern void mddev_resume(struct mddev *mddev);
extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,
- mddev_t *mddev);
+ struct mddev *mddev);
extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
- mddev_t *mddev);
-extern int mddev_check_plugged(mddev_t *mddev);
+ struct mddev *mddev);
+extern int mddev_check_plugged(struct mddev *mddev);
+extern void md_trim_bio(struct bio *bio, int offset, int size);
#endif /* _MD_MD_H */
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index 3535c23af288..5899246fa37e 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -20,6 +20,7 @@
*/
#include <linux/blkdev.h>
+#include <linux/module.h>
#include <linux/raid/md_u.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
@@ -31,7 +32,7 @@
#define NR_RESERVED_BUFS 32
-static int multipath_map (multipath_conf_t *conf)
+static int multipath_map (struct mpconf *conf)
{
int i, disks = conf->raid_disks;
@@ -42,7 +43,7 @@ static int multipath_map (multipath_conf_t *conf)
rcu_read_lock();
for (i = 0; i < disks; i++) {
- mdk_rdev_t *rdev = rcu_dereference(conf->multipaths[i].rdev);
+ struct md_rdev *rdev = rcu_dereference(conf->multipaths[i].rdev);
if (rdev && test_bit(In_sync, &rdev->flags)) {
atomic_inc(&rdev->nr_pending);
rcu_read_unlock();
@@ -58,8 +59,8 @@ static int multipath_map (multipath_conf_t *conf)
static void multipath_reschedule_retry (struct multipath_bh *mp_bh)
{
unsigned long flags;
- mddev_t *mddev = mp_bh->mddev;
- multipath_conf_t *conf = mddev->private;
+ struct mddev *mddev = mp_bh->mddev;
+ struct mpconf *conf = mddev->private;
spin_lock_irqsave(&conf->device_lock, flags);
list_add(&mp_bh->retry_list, &conf->retry_list);
@@ -76,7 +77,7 @@ static void multipath_reschedule_retry (struct multipath_bh *mp_bh)
static void multipath_end_bh_io (struct multipath_bh *mp_bh, int err)
{
struct bio *bio = mp_bh->master_bio;
- multipath_conf_t *conf = mp_bh->mddev->private;
+ struct mpconf *conf = mp_bh->mddev->private;
bio_endio(bio, err);
mempool_free(mp_bh, conf->pool);
@@ -86,8 +87,8 @@ static void multipath_end_request(struct bio *bio, int error)
{
int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
struct multipath_bh *mp_bh = bio->bi_private;
- multipath_conf_t *conf = mp_bh->mddev->private;
- mdk_rdev_t *rdev = conf->multipaths[mp_bh->path].rdev;
+ struct mpconf *conf = mp_bh->mddev->private;
+ struct md_rdev *rdev = conf->multipaths[mp_bh->path].rdev;
if (uptodate)
multipath_end_bh_io(mp_bh, 0);
@@ -106,15 +107,15 @@ static void multipath_end_request(struct bio *bio, int error)
rdev_dec_pending(rdev, conf->mddev);
}
-static int multipath_make_request(mddev_t *mddev, struct bio * bio)
+static void multipath_make_request(struct mddev *mddev, struct bio * bio)
{
- multipath_conf_t *conf = mddev->private;
+ struct mpconf *conf = mddev->private;
struct multipath_bh * mp_bh;
struct multipath_info *multipath;
if (unlikely(bio->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bio);
- return 0;
+ return;
}
mp_bh = mempool_alloc(conf->pool, GFP_NOIO);
@@ -126,7 +127,7 @@ static int multipath_make_request(mddev_t *mddev, struct bio * bio)
if (mp_bh->path < 0) {
bio_endio(bio, -EIO);
mempool_free(mp_bh, conf->pool);
- return 0;
+ return;
}
multipath = conf->multipaths + mp_bh->path;
@@ -137,12 +138,12 @@ static int multipath_make_request(mddev_t *mddev, struct bio * bio)
mp_bh->bio.bi_end_io = multipath_end_request;
mp_bh->bio.bi_private = mp_bh;
generic_make_request(&mp_bh->bio);
- return 0;
+ return;
}
-static void multipath_status (struct seq_file *seq, mddev_t *mddev)
+static void multipath_status (struct seq_file *seq, struct mddev *mddev)
{
- multipath_conf_t *conf = mddev->private;
+ struct mpconf *conf = mddev->private;
int i;
seq_printf (seq, " [%d/%d] [", conf->raid_disks,
@@ -156,8 +157,8 @@ static void multipath_status (struct seq_file *seq, mddev_t *mddev)
static int multipath_congested(void *data, int bits)
{
- mddev_t *mddev = data;
- multipath_conf_t *conf = mddev->private;
+ struct mddev *mddev = data;
+ struct mpconf *conf = mddev->private;
int i, ret = 0;
if (mddev_congested(mddev, bits))
@@ -165,7 +166,7 @@ static int multipath_congested(void *data, int bits)
rcu_read_lock();
for (i = 0; i < mddev->raid_disks ; i++) {
- mdk_rdev_t *rdev = rcu_dereference(conf->multipaths[i].rdev);
+ struct md_rdev *rdev = rcu_dereference(conf->multipaths[i].rdev);
if (rdev && !test_bit(Faulty, &rdev->flags)) {
struct request_queue *q = bdev_get_queue(rdev->bdev);
@@ -183,9 +184,9 @@ static int multipath_congested(void *data, int bits)
/*
* Careful, this can execute in IRQ contexts as well!
*/
-static void multipath_error (mddev_t *mddev, mdk_rdev_t *rdev)
+static void multipath_error (struct mddev *mddev, struct md_rdev *rdev)
{
- multipath_conf_t *conf = mddev->private;
+ struct mpconf *conf = mddev->private;
char b[BDEVNAME_SIZE];
if (conf->raid_disks - mddev->degraded <= 1) {
@@ -218,7 +219,7 @@ static void multipath_error (mddev_t *mddev, mdk_rdev_t *rdev)
conf->raid_disks - mddev->degraded);
}
-static void print_multipath_conf (multipath_conf_t *conf)
+static void print_multipath_conf (struct mpconf *conf)
{
int i;
struct multipath_info *tmp;
@@ -242,9 +243,9 @@ static void print_multipath_conf (multipath_conf_t *conf)
}
-static int multipath_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
+static int multipath_add_disk(struct mddev *mddev, struct md_rdev *rdev)
{
- multipath_conf_t *conf = mddev->private;
+ struct mpconf *conf = mddev->private;
struct request_queue *q;
int err = -EEXIST;
int path;
@@ -291,11 +292,11 @@ static int multipath_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
return err;
}
-static int multipath_remove_disk(mddev_t *mddev, int number)
+static int multipath_remove_disk(struct mddev *mddev, int number)
{
- multipath_conf_t *conf = mddev->private;
+ struct mpconf *conf = mddev->private;
int err = 0;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
struct multipath_info *p = conf->multipaths + number;
print_multipath_conf(conf);
@@ -335,12 +336,12 @@ abort:
* 3. Performs writes following reads for array syncronising.
*/
-static void multipathd (mddev_t *mddev)
+static void multipathd (struct mddev *mddev)
{
struct multipath_bh *mp_bh;
struct bio *bio;
unsigned long flags;
- multipath_conf_t *conf = mddev->private;
+ struct mpconf *conf = mddev->private;
struct list_head *head = &conf->retry_list;
md_check_recovery(mddev);
@@ -379,7 +380,7 @@ static void multipathd (mddev_t *mddev)
spin_unlock_irqrestore(&conf->device_lock, flags);
}
-static sector_t multipath_size(mddev_t *mddev, sector_t sectors, int raid_disks)
+static sector_t multipath_size(struct mddev *mddev, sector_t sectors, int raid_disks)
{
WARN_ONCE(sectors || raid_disks,
"%s does not support generic reshape\n", __func__);
@@ -387,12 +388,12 @@ static sector_t multipath_size(mddev_t *mddev, sector_t sectors, int raid_disks)
return mddev->dev_sectors;
}
-static int multipath_run (mddev_t *mddev)
+static int multipath_run (struct mddev *mddev)
{
- multipath_conf_t *conf;
+ struct mpconf *conf;
int disk_idx;
struct multipath_info *disk;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
int working_disks;
if (md_check_no_bitmap(mddev))
@@ -409,7 +410,7 @@ static int multipath_run (mddev_t *mddev)
* should be freed in multipath_stop()]
*/
- conf = kzalloc(sizeof(multipath_conf_t), GFP_KERNEL);
+ conf = kzalloc(sizeof(struct mpconf), GFP_KERNEL);
mddev->private = conf;
if (!conf) {
printk(KERN_ERR
@@ -510,12 +511,11 @@ out:
}
-static int multipath_stop (mddev_t *mddev)
+static int multipath_stop (struct mddev *mddev)
{
- multipath_conf_t *conf = mddev->private;
+ struct mpconf *conf = mddev->private;
- md_unregister_thread(mddev->thread);
- mddev->thread = NULL;
+ md_unregister_thread(&mddev->thread);
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
mempool_destroy(conf->pool);
kfree(conf->multipaths);
@@ -524,7 +524,7 @@ static int multipath_stop (mddev_t *mddev)
return 0;
}
-static struct mdk_personality multipath_personality =
+static struct md_personality multipath_personality =
{
.name = "multipath",
.level = LEVEL_MULTIPATH,
diff --git a/drivers/md/multipath.h b/drivers/md/multipath.h
index 3c5a45eb5f8a..717c60f62898 100644
--- a/drivers/md/multipath.h
+++ b/drivers/md/multipath.h
@@ -2,11 +2,11 @@
#define _MULTIPATH_H
struct multipath_info {
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
};
-struct multipath_private_data {
- mddev_t *mddev;
+struct mpconf {
+ struct mddev *mddev;
struct multipath_info *multipaths;
int raid_disks;
spinlock_t device_lock;
@@ -15,8 +15,6 @@ struct multipath_private_data {
mempool_t *pool;
};
-typedef struct multipath_private_data multipath_conf_t;
-
/*
* this is our 'private' 'collective' MULTIPATH buffer head.
* it contains information about what kind of IO operations were started
@@ -24,7 +22,7 @@ typedef struct multipath_private_data multipath_conf_t;
*/
struct multipath_bh {
- mddev_t *mddev;
+ struct mddev *mddev;
struct bio *master_bio;
struct bio bio;
int path;
diff --git a/drivers/md/persistent-data/Kconfig b/drivers/md/persistent-data/Kconfig
new file mode 100644
index 000000000000..ceb359050a59
--- /dev/null
+++ b/drivers/md/persistent-data/Kconfig
@@ -0,0 +1,8 @@
+config DM_PERSISTENT_DATA
+ tristate
+ depends on BLK_DEV_DM && EXPERIMENTAL
+ select LIBCRC32C
+ select DM_BUFIO
+ ---help---
+ Library providing immutable on-disk data structure support for
+ device-mapper targets such as the thin provisioning target.
diff --git a/drivers/md/persistent-data/Makefile b/drivers/md/persistent-data/Makefile
new file mode 100644
index 000000000000..cfa95f662230
--- /dev/null
+++ b/drivers/md/persistent-data/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_DM_PERSISTENT_DATA) += dm-persistent-data.o
+dm-persistent-data-objs := \
+ dm-block-manager.o \
+ dm-space-map-checker.o \
+ dm-space-map-common.o \
+ dm-space-map-disk.o \
+ dm-space-map-metadata.o \
+ dm-transaction-manager.o \
+ dm-btree.o \
+ dm-btree-remove.o \
+ dm-btree-spine.o
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
new file mode 100644
index 000000000000..0317ecdc6e53
--- /dev/null
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+#include "dm-block-manager.h"
+#include "dm-persistent-data-internal.h"
+#include "../dm-bufio.h"
+
+#include <linux/crc32c.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/rwsem.h>
+#include <linux/device-mapper.h>
+#include <linux/stacktrace.h>
+
+#define DM_MSG_PREFIX "block manager"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * This is a read/write semaphore with a couple of differences.
+ *
+ * i) There is a restriction on the number of concurrent read locks that
+ * may be held at once. This is just an implementation detail.
+ *
+ * ii) Recursive locking attempts are detected and return EINVAL. A stack
+ * trace is also emitted for the previous lock aquisition.
+ *
+ * iii) Priority is given to write locks.
+ */
+#define MAX_HOLDERS 4
+#define MAX_STACK 10
+
+typedef unsigned long stack_entries[MAX_STACK];
+
+struct block_lock {
+ spinlock_t lock;
+ __s32 count;
+ struct list_head waiters;
+ struct task_struct *holders[MAX_HOLDERS];
+
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ struct stack_trace traces[MAX_HOLDERS];
+ stack_entries entries[MAX_HOLDERS];
+#endif
+};
+
+struct waiter {
+ struct list_head list;
+ struct task_struct *task;
+ int wants_write;
+};
+
+static unsigned __find_holder(struct block_lock *lock,
+ struct task_struct *task)
+{
+ unsigned i;
+
+ for (i = 0; i < MAX_HOLDERS; i++)
+ if (lock->holders[i] == task)
+ break;
+
+ BUG_ON(i == MAX_HOLDERS);
+ return i;
+}
+
+/* call this *after* you increment lock->count */
+static void __add_holder(struct block_lock *lock, struct task_struct *task)
+{
+ unsigned h = __find_holder(lock, NULL);
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ struct stack_trace *t;
+#endif
+
+ get_task_struct(task);
+ lock->holders[h] = task;
+
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ t = lock->traces + h;
+ t->nr_entries = 0;
+ t->max_entries = MAX_STACK;
+ t->entries = lock->entries[h];
+ t->skip = 2;
+ save_stack_trace(t);
+#endif
+}
+
+/* call this *before* you decrement lock->count */
+static void __del_holder(struct block_lock *lock, struct task_struct *task)
+{
+ unsigned h = __find_holder(lock, task);
+ lock->holders[h] = NULL;
+ put_task_struct(task);
+}
+
+static int __check_holder(struct block_lock *lock)
+{
+ unsigned i;
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ static struct stack_trace t;
+ static stack_entries entries;
+#endif
+
+ for (i = 0; i < MAX_HOLDERS; i++) {
+ if (lock->holders[i] == current) {
+ DMERR("recursive lock detected in pool metadata");
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ DMERR("previously held here:");
+ print_stack_trace(lock->traces + i, 4);
+
+ DMERR("subsequent aquisition attempted here:");
+ t.nr_entries = 0;
+ t.max_entries = MAX_STACK;
+ t.entries = entries;
+ t.skip = 3;
+ save_stack_trace(&t);
+ print_stack_trace(&t, 4);
+#endif
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void __wait(struct waiter *w)
+{
+ for (;;) {
+ set_task_state(current, TASK_UNINTERRUPTIBLE);
+
+ if (!w->task)
+ break;
+
+ schedule();
+ }
+
+ set_task_state(current, TASK_RUNNING);
+}
+
+static void __wake_waiter(struct waiter *w)
+{
+ struct task_struct *task;
+
+ list_del(&w->list);
+ task = w->task;
+ smp_mb();
+ w->task = NULL;
+ wake_up_process(task);
+}
+
+/*
+ * We either wake a few readers or a single writer.
+ */
+static void __wake_many(struct block_lock *lock)
+{
+ struct waiter *w, *tmp;
+
+ BUG_ON(lock->count < 0);
+ list_for_each_entry_safe(w, tmp, &lock->waiters, list) {
+ if (lock->count >= MAX_HOLDERS)
+ return;
+
+ if (w->wants_write) {
+ if (lock->count > 0)
+ return; /* still read locked */
+
+ lock->count = -1;
+ __add_holder(lock, w->task);
+ __wake_waiter(w);
+ return;
+ }
+
+ lock->count++;
+ __add_holder(lock, w->task);
+ __wake_waiter(w);
+ }
+}
+
+static void bl_init(struct block_lock *lock)
+{
+ int i;
+
+ spin_lock_init(&lock->lock);
+ lock->count = 0;
+ INIT_LIST_HEAD(&lock->waiters);
+ for (i = 0; i < MAX_HOLDERS; i++)
+ lock->holders[i] = NULL;
+}
+
+static int __available_for_read(struct block_lock *lock)
+{
+ return lock->count >= 0 &&
+ lock->count < MAX_HOLDERS &&
+ list_empty(&lock->waiters);
+}
+
+static int bl_down_read(struct block_lock *lock)
+{
+ int r;
+ struct waiter w;
+
+ spin_lock(&lock->lock);
+ r = __check_holder(lock);
+ if (r) {
+ spin_unlock(&lock->lock);
+ return r;
+ }
+
+ if (__available_for_read(lock)) {
+ lock->count++;
+ __add_holder(lock, current);
+ spin_unlock(&lock->lock);
+ return 0;
+ }
+
+ get_task_struct(current);
+
+ w.task = current;
+ w.wants_write = 0;
+ list_add_tail(&w.list, &lock->waiters);
+ spin_unlock(&lock->lock);
+
+ __wait(&w);
+ put_task_struct(current);
+ return 0;
+}
+
+static int bl_down_read_nonblock(struct block_lock *lock)
+{
+ int r;
+
+ spin_lock(&lock->lock);
+ r = __check_holder(lock);
+ if (r)
+ goto out;
+
+ if (__available_for_read(lock)) {
+ lock->count++;
+ __add_holder(lock, current);
+ r = 0;
+ } else
+ r = -EWOULDBLOCK;
+
+out:
+ spin_unlock(&lock->lock);
+ return r;
+}
+
+static void bl_up_read(struct block_lock *lock)
+{
+ spin_lock(&lock->lock);
+ BUG_ON(lock->count <= 0);
+ __del_holder(lock, current);
+ --lock->count;
+ if (!list_empty(&lock->waiters))
+ __wake_many(lock);
+ spin_unlock(&lock->lock);
+}
+
+static int bl_down_write(struct block_lock *lock)
+{
+ int r;
+ struct waiter w;
+
+ spin_lock(&lock->lock);
+ r = __check_holder(lock);
+ if (r) {
+ spin_unlock(&lock->lock);
+ return r;
+ }
+
+ if (lock->count == 0 && list_empty(&lock->waiters)) {
+ lock->count = -1;
+ __add_holder(lock, current);
+ spin_unlock(&lock->lock);
+ return 0;
+ }
+
+ get_task_struct(current);
+ w.task = current;
+ w.wants_write = 1;
+
+ /*
+ * Writers given priority. We know there's only one mutator in the
+ * system, so ignoring the ordering reversal.
+ */
+ list_add(&w.list, &lock->waiters);
+ spin_unlock(&lock->lock);
+
+ __wait(&w);
+ put_task_struct(current);
+
+ return 0;
+}
+
+static void bl_up_write(struct block_lock *lock)
+{
+ spin_lock(&lock->lock);
+ __del_holder(lock, current);
+ lock->count = 0;
+ if (!list_empty(&lock->waiters))
+ __wake_many(lock);
+ spin_unlock(&lock->lock);
+}
+
+static void report_recursive_bug(dm_block_t b, int r)
+{
+ if (r == -EINVAL)
+ DMERR("recursive acquisition of block %llu requested.",
+ (unsigned long long) b);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Block manager is currently implemented using dm-bufio. struct
+ * dm_block_manager and struct dm_block map directly onto a couple of
+ * structs in the bufio interface. I want to retain the freedom to move
+ * away from bufio in the future. So these structs are just cast within
+ * this .c file, rather than making it through to the public interface.
+ */
+static struct dm_buffer *to_buffer(struct dm_block *b)
+{
+ return (struct dm_buffer *) b;
+}
+
+static struct dm_bufio_client *to_bufio(struct dm_block_manager *bm)
+{
+ return (struct dm_bufio_client *) bm;
+}
+
+dm_block_t dm_block_location(struct dm_block *b)
+{
+ return dm_bufio_get_block_number(to_buffer(b));
+}
+EXPORT_SYMBOL_GPL(dm_block_location);
+
+void *dm_block_data(struct dm_block *b)
+{
+ return dm_bufio_get_block_data(to_buffer(b));
+}
+EXPORT_SYMBOL_GPL(dm_block_data);
+
+struct buffer_aux {
+ struct dm_block_validator *validator;
+ struct block_lock lock;
+ int write_locked;
+};
+
+static void dm_block_manager_alloc_callback(struct dm_buffer *buf)
+{
+ struct buffer_aux *aux = dm_bufio_get_aux_data(buf);
+ aux->validator = NULL;
+ bl_init(&aux->lock);
+}
+
+static void dm_block_manager_write_callback(struct dm_buffer *buf)
+{
+ struct buffer_aux *aux = dm_bufio_get_aux_data(buf);
+ if (aux->validator) {
+ aux->validator->prepare_for_write(aux->validator, (struct dm_block *) buf,
+ dm_bufio_get_block_size(dm_bufio_get_client(buf)));
+ }
+}
+
+/*----------------------------------------------------------------
+ * Public interface
+ *--------------------------------------------------------------*/
+struct dm_block_manager *dm_block_manager_create(struct block_device *bdev,
+ unsigned block_size,
+ unsigned cache_size,
+ unsigned max_held_per_thread)
+{
+ return (struct dm_block_manager *)
+ dm_bufio_client_create(bdev, block_size, max_held_per_thread,
+ sizeof(struct buffer_aux),
+ dm_block_manager_alloc_callback,
+ dm_block_manager_write_callback);
+}
+EXPORT_SYMBOL_GPL(dm_block_manager_create);
+
+void dm_block_manager_destroy(struct dm_block_manager *bm)
+{
+ return dm_bufio_client_destroy(to_bufio(bm));
+}
+EXPORT_SYMBOL_GPL(dm_block_manager_destroy);
+
+unsigned dm_bm_block_size(struct dm_block_manager *bm)
+{
+ return dm_bufio_get_block_size(to_bufio(bm));
+}
+EXPORT_SYMBOL_GPL(dm_bm_block_size);
+
+dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm)
+{
+ return dm_bufio_get_device_size(to_bufio(bm));
+}
+
+static int dm_bm_validate_buffer(struct dm_block_manager *bm,
+ struct dm_buffer *buf,
+ struct buffer_aux *aux,
+ struct dm_block_validator *v)
+{
+ if (unlikely(!aux->validator)) {
+ int r;
+ if (!v)
+ return 0;
+ r = v->check(v, (struct dm_block *) buf, dm_bufio_get_block_size(to_bufio(bm)));
+ if (unlikely(r))
+ return r;
+ aux->validator = v;
+ } else {
+ if (unlikely(aux->validator != v)) {
+ DMERR("validator mismatch (old=%s vs new=%s) for block %llu",
+ aux->validator->name, v ? v->name : "NULL",
+ (unsigned long long)
+ dm_bufio_get_block_number(buf));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ struct buffer_aux *aux;
+ void *p;
+ int r;
+
+ p = dm_bufio_read(to_bufio(bm), b, (struct dm_buffer **) result);
+ if (unlikely(IS_ERR(p)))
+ return PTR_ERR(p);
+
+ aux = dm_bufio_get_aux_data(to_buffer(*result));
+ r = bl_down_read(&aux->lock);
+ if (unlikely(r)) {
+ dm_bufio_release(to_buffer(*result));
+ report_recursive_bug(b, r);
+ return r;
+ }
+
+ aux->write_locked = 0;
+
+ r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
+ if (unlikely(r)) {
+ bl_up_read(&aux->lock);
+ dm_bufio_release(to_buffer(*result));
+ return r;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dm_bm_read_lock);
+
+int dm_bm_write_lock(struct dm_block_manager *bm,
+ dm_block_t b, struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ struct buffer_aux *aux;
+ void *p;
+ int r;
+
+ p = dm_bufio_read(to_bufio(bm), b, (struct dm_buffer **) result);
+ if (unlikely(IS_ERR(p)))
+ return PTR_ERR(p);
+
+ aux = dm_bufio_get_aux_data(to_buffer(*result));
+ r = bl_down_write(&aux->lock);
+ if (r) {
+ dm_bufio_release(to_buffer(*result));
+ report_recursive_bug(b, r);
+ return r;
+ }
+
+ aux->write_locked = 1;
+
+ r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
+ if (unlikely(r)) {
+ bl_up_write(&aux->lock);
+ dm_bufio_release(to_buffer(*result));
+ return r;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dm_bm_write_lock);
+
+int dm_bm_read_try_lock(struct dm_block_manager *bm,
+ dm_block_t b, struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ struct buffer_aux *aux;
+ void *p;
+ int r;
+
+ p = dm_bufio_get(to_bufio(bm), b, (struct dm_buffer **) result);
+ if (unlikely(IS_ERR(p)))
+ return PTR_ERR(p);
+ if (unlikely(!p))
+ return -EWOULDBLOCK;
+
+ aux = dm_bufio_get_aux_data(to_buffer(*result));
+ r = bl_down_read_nonblock(&aux->lock);
+ if (r < 0) {
+ dm_bufio_release(to_buffer(*result));
+ report_recursive_bug(b, r);
+ return r;
+ }
+ aux->write_locked = 0;
+
+ r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
+ if (unlikely(r)) {
+ bl_up_read(&aux->lock);
+ dm_bufio_release(to_buffer(*result));
+ return r;
+ }
+
+ return 0;
+}
+
+int dm_bm_write_lock_zero(struct dm_block_manager *bm,
+ dm_block_t b, struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ int r;
+ struct buffer_aux *aux;
+ void *p;
+
+ p = dm_bufio_new(to_bufio(bm), b, (struct dm_buffer **) result);
+ if (unlikely(IS_ERR(p)))
+ return PTR_ERR(p);
+
+ memset(p, 0, dm_bm_block_size(bm));
+
+ aux = dm_bufio_get_aux_data(to_buffer(*result));
+ r = bl_down_write(&aux->lock);
+ if (r) {
+ dm_bufio_release(to_buffer(*result));
+ return r;
+ }
+
+ aux->write_locked = 1;
+ aux->validator = v;
+
+ return 0;
+}
+
+int dm_bm_unlock(struct dm_block *b)
+{
+ struct buffer_aux *aux;
+ aux = dm_bufio_get_aux_data(to_buffer(b));
+
+ if (aux->write_locked) {
+ dm_bufio_mark_buffer_dirty(to_buffer(b));
+ bl_up_write(&aux->lock);
+ } else
+ bl_up_read(&aux->lock);
+
+ dm_bufio_release(to_buffer(b));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dm_bm_unlock);
+
+int dm_bm_unlock_move(struct dm_block *b, dm_block_t n)
+{
+ struct buffer_aux *aux;
+
+ aux = dm_bufio_get_aux_data(to_buffer(b));
+
+ if (aux->write_locked) {
+ dm_bufio_mark_buffer_dirty(to_buffer(b));
+ bl_up_write(&aux->lock);
+ } else
+ bl_up_read(&aux->lock);
+
+ dm_bufio_release_move(to_buffer(b), n);
+ return 0;
+}
+
+int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
+ struct dm_block *superblock)
+{
+ int r;
+
+ r = dm_bufio_write_dirty_buffers(to_bufio(bm));
+ if (unlikely(r))
+ return r;
+ r = dm_bufio_issue_flush(to_bufio(bm));
+ if (unlikely(r))
+ return r;
+
+ dm_bm_unlock(superblock);
+
+ r = dm_bufio_write_dirty_buffers(to_bufio(bm));
+ if (unlikely(r))
+ return r;
+ r = dm_bufio_issue_flush(to_bufio(bm));
+ if (unlikely(r))
+ return r;
+
+ return 0;
+}
+
+u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor)
+{
+ return crc32c(~(u32) 0, data, len) ^ init_xor;
+}
+EXPORT_SYMBOL_GPL(dm_bm_checksum);
+
+/*----------------------------------------------------------------*/
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
+MODULE_DESCRIPTION("Immutable metadata library for dm");
+
+/*----------------------------------------------------------------*/
diff --git a/drivers/md/persistent-data/dm-block-manager.h b/drivers/md/persistent-data/dm-block-manager.h
new file mode 100644
index 000000000000..924833d2dfa6
--- /dev/null
+++ b/drivers/md/persistent-data/dm-block-manager.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _LINUX_DM_BLOCK_MANAGER_H
+#define _LINUX_DM_BLOCK_MANAGER_H
+
+#include <linux/types.h>
+#include <linux/blkdev.h>
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Block number.
+ */
+typedef uint64_t dm_block_t;
+struct dm_block;
+
+dm_block_t dm_block_location(struct dm_block *b);
+void *dm_block_data(struct dm_block *b);
+
+/*----------------------------------------------------------------*/
+
+/*
+ * @name should be a unique identifier for the block manager, no longer
+ * than 32 chars.
+ *
+ * @max_held_per_thread should be the maximum number of locks, read or
+ * write, that an individual thread holds at any one time.
+ */
+struct dm_block_manager;
+struct dm_block_manager *dm_block_manager_create(
+ struct block_device *bdev, unsigned block_size,
+ unsigned cache_size, unsigned max_held_per_thread);
+void dm_block_manager_destroy(struct dm_block_manager *bm);
+
+unsigned dm_bm_block_size(struct dm_block_manager *bm);
+dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm);
+
+/*----------------------------------------------------------------*/
+
+/*
+ * The validator allows the caller to verify newly-read data and modify
+ * the data just before writing, e.g. to calculate checksums. It's
+ * important to be consistent with your use of validators. The only time
+ * you can change validators is if you call dm_bm_write_lock_zero.
+ */
+struct dm_block_validator {
+ const char *name;
+ void (*prepare_for_write)(struct dm_block_validator *v, struct dm_block *b, size_t block_size);
+
+ /*
+ * Return 0 if the checksum is valid or < 0 on error.
+ */
+ int (*check)(struct dm_block_validator *v, struct dm_block *b, size_t block_size);
+};
+
+/*----------------------------------------------------------------*/
+
+/*
+ * You can have multiple concurrent readers or a single writer holding a
+ * block lock.
+ */
+
+/*
+ * dm_bm_lock() locks a block and returns through @result a pointer to
+ * memory that holds a copy of that block. If you have write-locked the
+ * block then any changes you make to memory pointed to by @result will be
+ * written back to the disk sometime after dm_bm_unlock is called.
+ */
+int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+int dm_bm_write_lock(struct dm_block_manager *bm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+/*
+ * The *_try_lock variants return -EWOULDBLOCK if the block isn't
+ * available immediately.
+ */
+int dm_bm_read_try_lock(struct dm_block_manager *bm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+/*
+ * Use dm_bm_write_lock_zero() when you know you're going to
+ * overwrite the block completely. It saves a disk read.
+ */
+int dm_bm_write_lock_zero(struct dm_block_manager *bm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+int dm_bm_unlock(struct dm_block *b);
+
+/*
+ * An optimisation; we often want to copy a block's contents to a new
+ * block. eg, as part of the shadowing operation. It's far better for
+ * bufio to do this move behind the scenes than hold 2 locks and memcpy the
+ * data.
+ */
+int dm_bm_unlock_move(struct dm_block *b, dm_block_t n);
+
+/*
+ * It's a common idiom to have a superblock that should be committed last.
+ *
+ * @superblock should be write-locked on entry. It will be unlocked during
+ * this function. All dirty blocks are guaranteed to be written and flushed
+ * before the superblock.
+ *
+ * This method always blocks.
+ */
+int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
+ struct dm_block *superblock);
+
+u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor);
+
+/*----------------------------------------------------------------*/
+
+#endif /* _LINUX_DM_BLOCK_MANAGER_H */
diff --git a/drivers/md/persistent-data/dm-btree-internal.h b/drivers/md/persistent-data/dm-btree-internal.h
new file mode 100644
index 000000000000..d279c768f8f1
--- /dev/null
+++ b/drivers/md/persistent-data/dm-btree-internal.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_BTREE_INTERNAL_H
+#define DM_BTREE_INTERNAL_H
+
+#include "dm-btree.h"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * We'll need 2 accessor functions for n->csum and n->blocknr
+ * to support dm-btree-spine.c in that case.
+ */
+
+enum node_flags {
+ INTERNAL_NODE = 1,
+ LEAF_NODE = 1 << 1
+};
+
+/*
+ * Every btree node begins with this structure. Make sure it's a multiple
+ * of 8-bytes in size, otherwise the 64bit keys will be mis-aligned.
+ */
+struct node_header {
+ __le32 csum;
+ __le32 flags;
+ __le64 blocknr; /* Block this node is supposed to live in. */
+
+ __le32 nr_entries;
+ __le32 max_entries;
+ __le32 value_size;
+ __le32 padding;
+} __packed;
+
+struct node {
+ struct node_header header;
+ __le64 keys[0];
+} __packed;
+
+
+void inc_children(struct dm_transaction_manager *tm, struct node *n,
+ struct dm_btree_value_type *vt);
+
+int new_block(struct dm_btree_info *info, struct dm_block **result);
+int unlock_block(struct dm_btree_info *info, struct dm_block *b);
+
+/*
+ * Spines keep track of the rolling locks. There are 2 variants, read-only
+ * and one that uses shadowing. These are separate structs to allow the
+ * type checker to spot misuse, for example accidentally calling read_lock
+ * on a shadow spine.
+ */
+struct ro_spine {
+ struct dm_btree_info *info;
+
+ int count;
+ struct dm_block *nodes[2];
+};
+
+void init_ro_spine(struct ro_spine *s, struct dm_btree_info *info);
+int exit_ro_spine(struct ro_spine *s);
+int ro_step(struct ro_spine *s, dm_block_t new_child);
+struct node *ro_node(struct ro_spine *s);
+
+struct shadow_spine {
+ struct dm_btree_info *info;
+
+ int count;
+ struct dm_block *nodes[2];
+
+ dm_block_t root;
+};
+
+void init_shadow_spine(struct shadow_spine *s, struct dm_btree_info *info);
+int exit_shadow_spine(struct shadow_spine *s);
+
+int shadow_step(struct shadow_spine *s, dm_block_t b,
+ struct dm_btree_value_type *vt);
+
+/*
+ * The spine must have at least one entry before calling this.
+ */
+struct dm_block *shadow_current(struct shadow_spine *s);
+
+/*
+ * The spine must have at least two entries before calling this.
+ */
+struct dm_block *shadow_parent(struct shadow_spine *s);
+
+int shadow_has_parent(struct shadow_spine *s);
+
+int shadow_root(struct shadow_spine *s);
+
+/*
+ * Some inlines.
+ */
+static inline __le64 *key_ptr(struct node *n, uint32_t index)
+{
+ return n->keys + index;
+}
+
+static inline void *value_base(struct node *n)
+{
+ return &n->keys[le32_to_cpu(n->header.max_entries)];
+}
+
+/*
+ * FIXME: Now that value size is stored in node we don't need the third parm.
+ */
+static inline void *value_ptr(struct node *n, uint32_t index, size_t value_size)
+{
+ BUG_ON(value_size != le32_to_cpu(n->header.value_size));
+ return value_base(n) + (value_size * index);
+}
+
+/*
+ * Assumes the values are suitably-aligned and converts to core format.
+ */
+static inline uint64_t value64(struct node *n, uint32_t index)
+{
+ __le64 *values_le = value_base(n);
+
+ return le64_to_cpu(values_le[index]);
+}
+
+/*
+ * Searching for a key within a single node.
+ */
+int lower_bound(struct node *n, uint64_t key);
+
+extern struct dm_block_validator btree_node_validator;
+
+#endif /* DM_BTREE_INTERNAL_H */
diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c
new file mode 100644
index 000000000000..023fbc2d389e
--- /dev/null
+++ b/drivers/md/persistent-data/dm-btree-remove.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-btree.h"
+#include "dm-btree-internal.h"
+#include "dm-transaction-manager.h"
+
+#include <linux/export.h>
+
+/*
+ * Removing an entry from a btree
+ * ==============================
+ *
+ * A very important constraint for our btree is that no node, except the
+ * root, may have fewer than a certain number of entries.
+ * (MIN_ENTRIES <= nr_entries <= MAX_ENTRIES).
+ *
+ * Ensuring this is complicated by the way we want to only ever hold the
+ * locks on 2 nodes concurrently, and only change nodes in a top to bottom
+ * fashion.
+ *
+ * Each node may have a left or right sibling. When decending the spine,
+ * if a node contains only MIN_ENTRIES then we try and increase this to at
+ * least MIN_ENTRIES + 1. We do this in the following ways:
+ *
+ * [A] No siblings => this can only happen if the node is the root, in which
+ * case we copy the childs contents over the root.
+ *
+ * [B] No left sibling
+ * ==> rebalance(node, right sibling)
+ *
+ * [C] No right sibling
+ * ==> rebalance(left sibling, node)
+ *
+ * [D] Both siblings, total_entries(left, node, right) <= DEL_THRESHOLD
+ * ==> delete node adding it's contents to left and right
+ *
+ * [E] Both siblings, total_entries(left, node, right) > DEL_THRESHOLD
+ * ==> rebalance(left, node, right)
+ *
+ * After these operations it's possible that the our original node no
+ * longer contains the desired sub tree. For this reason this rebalancing
+ * is performed on the children of the current node. This also avoids
+ * having a special case for the root.
+ *
+ * Once this rebalancing has occurred we can then step into the child node
+ * for internal nodes. Or delete the entry for leaf nodes.
+ */
+
+/*
+ * Some little utilities for moving node data around.
+ */
+static void node_shift(struct node *n, int shift)
+{
+ uint32_t nr_entries = le32_to_cpu(n->header.nr_entries);
+ uint32_t value_size = le32_to_cpu(n->header.value_size);
+
+ if (shift < 0) {
+ shift = -shift;
+ BUG_ON(shift > nr_entries);
+ BUG_ON((void *) key_ptr(n, shift) >= value_ptr(n, shift, value_size));
+ memmove(key_ptr(n, 0),
+ key_ptr(n, shift),
+ (nr_entries - shift) * sizeof(__le64));
+ memmove(value_ptr(n, 0, value_size),
+ value_ptr(n, shift, value_size),
+ (nr_entries - shift) * value_size);
+ } else {
+ BUG_ON(nr_entries + shift > le32_to_cpu(n->header.max_entries));
+ memmove(key_ptr(n, shift),
+ key_ptr(n, 0),
+ nr_entries * sizeof(__le64));
+ memmove(value_ptr(n, shift, value_size),
+ value_ptr(n, 0, value_size),
+ nr_entries * value_size);
+ }
+}
+
+static void node_copy(struct node *left, struct node *right, int shift)
+{
+ uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
+ uint32_t value_size = le32_to_cpu(left->header.value_size);
+ BUG_ON(value_size != le32_to_cpu(right->header.value_size));
+
+ if (shift < 0) {
+ shift = -shift;
+ BUG_ON(nr_left + shift > le32_to_cpu(left->header.max_entries));
+ memcpy(key_ptr(left, nr_left),
+ key_ptr(right, 0),
+ shift * sizeof(__le64));
+ memcpy(value_ptr(left, nr_left, value_size),
+ value_ptr(right, 0, value_size),
+ shift * value_size);
+ } else {
+ BUG_ON(shift > le32_to_cpu(right->header.max_entries));
+ memcpy(key_ptr(right, 0),
+ key_ptr(left, nr_left - shift),
+ shift * sizeof(__le64));
+ memcpy(value_ptr(right, 0, value_size),
+ value_ptr(left, nr_left - shift, value_size),
+ shift * value_size);
+ }
+}
+
+/*
+ * Delete a specific entry from a leaf node.
+ */
+static void delete_at(struct node *n, unsigned index)
+{
+ unsigned nr_entries = le32_to_cpu(n->header.nr_entries);
+ unsigned nr_to_copy = nr_entries - (index + 1);
+ uint32_t value_size = le32_to_cpu(n->header.value_size);
+ BUG_ON(index >= nr_entries);
+
+ if (nr_to_copy) {
+ memmove(key_ptr(n, index),
+ key_ptr(n, index + 1),
+ nr_to_copy * sizeof(__le64));
+
+ memmove(value_ptr(n, index, value_size),
+ value_ptr(n, index + 1, value_size),
+ nr_to_copy * value_size);
+ }
+
+ n->header.nr_entries = cpu_to_le32(nr_entries - 1);
+}
+
+static unsigned del_threshold(struct node *n)
+{
+ return le32_to_cpu(n->header.max_entries) / 3;
+}
+
+static unsigned merge_threshold(struct node *n)
+{
+ /*
+ * The extra one is because we know we're potentially going to
+ * delete an entry.
+ */
+ return 2 * (le32_to_cpu(n->header.max_entries) / 3) + 1;
+}
+
+struct child {
+ unsigned index;
+ struct dm_block *block;
+ struct node *n;
+};
+
+static struct dm_btree_value_type le64_type = {
+ .context = NULL,
+ .size = sizeof(__le64),
+ .inc = NULL,
+ .dec = NULL,
+ .equal = NULL
+};
+
+static int init_child(struct dm_btree_info *info, struct node *parent,
+ unsigned index, struct child *result)
+{
+ int r, inc;
+ dm_block_t root;
+
+ result->index = index;
+ root = value64(parent, index);
+
+ r = dm_tm_shadow_block(info->tm, root, &btree_node_validator,
+ &result->block, &inc);
+ if (r)
+ return r;
+
+ result->n = dm_block_data(result->block);
+
+ if (inc)
+ inc_children(info->tm, result->n, &le64_type);
+
+ *((__le64 *) value_ptr(parent, index, sizeof(__le64))) =
+ cpu_to_le64(dm_block_location(result->block));
+
+ return 0;
+}
+
+static int exit_child(struct dm_btree_info *info, struct child *c)
+{
+ return dm_tm_unlock(info->tm, c->block);
+}
+
+static void shift(struct node *left, struct node *right, int count)
+{
+ if (!count)
+ return;
+
+ if (count > 0) {
+ node_shift(right, count);
+ node_copy(left, right, count);
+ } else {
+ node_copy(left, right, count);
+ node_shift(right, count);
+ }
+
+ left->header.nr_entries =
+ cpu_to_le32(le32_to_cpu(left->header.nr_entries) - count);
+ BUG_ON(le32_to_cpu(left->header.nr_entries) > le32_to_cpu(left->header.max_entries));
+
+ right->header.nr_entries =
+ cpu_to_le32(le32_to_cpu(right->header.nr_entries) + count);
+ BUG_ON(le32_to_cpu(right->header.nr_entries) > le32_to_cpu(right->header.max_entries));
+}
+
+static void __rebalance2(struct dm_btree_info *info, struct node *parent,
+ struct child *l, struct child *r)
+{
+ struct node *left = l->n;
+ struct node *right = r->n;
+ uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
+ uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
+
+ if (nr_left + nr_right <= merge_threshold(left)) {
+ /*
+ * Merge
+ */
+ node_copy(left, right, -nr_right);
+ left->header.nr_entries = cpu_to_le32(nr_left + nr_right);
+ delete_at(parent, r->index);
+
+ /*
+ * We need to decrement the right block, but not it's
+ * children, since they're still referenced by left.
+ */
+ dm_tm_dec(info->tm, dm_block_location(r->block));
+ } else {
+ /*
+ * Rebalance.
+ */
+ unsigned target_left = (nr_left + nr_right) / 2;
+ unsigned shift_ = nr_left - target_left;
+ BUG_ON(le32_to_cpu(left->header.max_entries) <= nr_left - shift_);
+ BUG_ON(le32_to_cpu(right->header.max_entries) <= nr_right + shift_);
+ shift(left, right, nr_left - target_left);
+ *key_ptr(parent, r->index) = right->keys[0];
+ }
+}
+
+static int rebalance2(struct shadow_spine *s, struct dm_btree_info *info,
+ unsigned left_index)
+{
+ int r;
+ struct node *parent;
+ struct child left, right;
+
+ parent = dm_block_data(shadow_current(s));
+
+ r = init_child(info, parent, left_index, &left);
+ if (r)
+ return r;
+
+ r = init_child(info, parent, left_index + 1, &right);
+ if (r) {
+ exit_child(info, &left);
+ return r;
+ }
+
+ __rebalance2(info, parent, &left, &right);
+
+ r = exit_child(info, &left);
+ if (r) {
+ exit_child(info, &right);
+ return r;
+ }
+
+ return exit_child(info, &right);
+}
+
+static void __rebalance3(struct dm_btree_info *info, struct node *parent,
+ struct child *l, struct child *c, struct child *r)
+{
+ struct node *left = l->n;
+ struct node *center = c->n;
+ struct node *right = r->n;
+
+ uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
+ uint32_t nr_center = le32_to_cpu(center->header.nr_entries);
+ uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
+ uint32_t max_entries = le32_to_cpu(left->header.max_entries);
+
+ unsigned target;
+
+ BUG_ON(left->header.max_entries != center->header.max_entries);
+ BUG_ON(center->header.max_entries != right->header.max_entries);
+
+ if (((nr_left + nr_center + nr_right) / 2) < merge_threshold(center)) {
+ /*
+ * Delete center node:
+ *
+ * We dump as many entries from center as possible into
+ * left, then the rest in right, then rebalance2. This
+ * wastes some cpu, but I want something simple atm.
+ */
+ unsigned shift = min(max_entries - nr_left, nr_center);
+
+ BUG_ON(nr_left + shift > max_entries);
+ node_copy(left, center, -shift);
+ left->header.nr_entries = cpu_to_le32(nr_left + shift);
+
+ if (shift != nr_center) {
+ shift = nr_center - shift;
+ BUG_ON((nr_right + shift) >= max_entries);
+ node_shift(right, shift);
+ node_copy(center, right, shift);
+ right->header.nr_entries = cpu_to_le32(nr_right + shift);
+ }
+ *key_ptr(parent, r->index) = right->keys[0];
+
+ delete_at(parent, c->index);
+ r->index--;
+
+ dm_tm_dec(info->tm, dm_block_location(c->block));
+ __rebalance2(info, parent, l, r);
+
+ return;
+ }
+
+ /*
+ * Rebalance
+ */
+ target = (nr_left + nr_center + nr_right) / 3;
+ BUG_ON(target > max_entries);
+
+ /*
+ * Adjust the left node
+ */
+ shift(left, center, nr_left - target);
+
+ /*
+ * Adjust the right node
+ */
+ shift(center, right, target - nr_right);
+ *key_ptr(parent, c->index) = center->keys[0];
+ *key_ptr(parent, r->index) = right->keys[0];
+}
+
+static int rebalance3(struct shadow_spine *s, struct dm_btree_info *info,
+ unsigned left_index)
+{
+ int r;
+ struct node *parent = dm_block_data(shadow_current(s));
+ struct child left, center, right;
+
+ /*
+ * FIXME: fill out an array?
+ */
+ r = init_child(info, parent, left_index, &left);
+ if (r)
+ return r;
+
+ r = init_child(info, parent, left_index + 1, &center);
+ if (r) {
+ exit_child(info, &left);
+ return r;
+ }
+
+ r = init_child(info, parent, left_index + 2, &right);
+ if (r) {
+ exit_child(info, &left);
+ exit_child(info, &center);
+ return r;
+ }
+
+ __rebalance3(info, parent, &left, &center, &right);
+
+ r = exit_child(info, &left);
+ if (r) {
+ exit_child(info, &center);
+ exit_child(info, &right);
+ return r;
+ }
+
+ r = exit_child(info, &center);
+ if (r) {
+ exit_child(info, &right);
+ return r;
+ }
+
+ r = exit_child(info, &right);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int get_nr_entries(struct dm_transaction_manager *tm,
+ dm_block_t b, uint32_t *result)
+{
+ int r;
+ struct dm_block *block;
+ struct node *n;
+
+ r = dm_tm_read_lock(tm, b, &btree_node_validator, &block);
+ if (r)
+ return r;
+
+ n = dm_block_data(block);
+ *result = le32_to_cpu(n->header.nr_entries);
+
+ return dm_tm_unlock(tm, block);
+}
+
+static int rebalance_children(struct shadow_spine *s,
+ struct dm_btree_info *info, uint64_t key)
+{
+ int i, r, has_left_sibling, has_right_sibling;
+ uint32_t child_entries;
+ struct node *n;
+
+ n = dm_block_data(shadow_current(s));
+
+ if (le32_to_cpu(n->header.nr_entries) == 1) {
+ struct dm_block *child;
+ dm_block_t b = value64(n, 0);
+
+ r = dm_tm_read_lock(info->tm, b, &btree_node_validator, &child);
+ if (r)
+ return r;
+
+ memcpy(n, dm_block_data(child),
+ dm_bm_block_size(dm_tm_get_bm(info->tm)));
+ r = dm_tm_unlock(info->tm, child);
+ if (r)
+ return r;
+
+ dm_tm_dec(info->tm, dm_block_location(child));
+ return 0;
+ }
+
+ i = lower_bound(n, key);
+ if (i < 0)
+ return -ENODATA;
+
+ r = get_nr_entries(info->tm, value64(n, i), &child_entries);
+ if (r)
+ return r;
+
+ if (child_entries > del_threshold(n))
+ return 0;
+
+ has_left_sibling = i > 0;
+ has_right_sibling = i < (le32_to_cpu(n->header.nr_entries) - 1);
+
+ if (!has_left_sibling)
+ r = rebalance2(s, info, i);
+
+ else if (!has_right_sibling)
+ r = rebalance2(s, info, i - 1);
+
+ else
+ r = rebalance3(s, info, i - 1);
+
+ return r;
+}
+
+static int do_leaf(struct node *n, uint64_t key, unsigned *index)
+{
+ int i = lower_bound(n, key);
+
+ if ((i < 0) ||
+ (i >= le32_to_cpu(n->header.nr_entries)) ||
+ (le64_to_cpu(n->keys[i]) != key))
+ return -ENODATA;
+
+ *index = i;
+
+ return 0;
+}
+
+/*
+ * Prepares for removal from one level of the hierarchy. The caller must
+ * call delete_at() to remove the entry at index.
+ */
+static int remove_raw(struct shadow_spine *s, struct dm_btree_info *info,
+ struct dm_btree_value_type *vt, dm_block_t root,
+ uint64_t key, unsigned *index)
+{
+ int i = *index, r;
+ struct node *n;
+
+ for (;;) {
+ r = shadow_step(s, root, vt);
+ if (r < 0)
+ break;
+
+ /*
+ * We have to patch up the parent node, ugly, but I don't
+ * see a way to do this automatically as part of the spine
+ * op.
+ */
+ if (shadow_has_parent(s)) {
+ __le64 location = cpu_to_le64(dm_block_location(shadow_current(s)));
+ memcpy(value_ptr(dm_block_data(shadow_parent(s)), i, sizeof(__le64)),
+ &location, sizeof(__le64));
+ }
+
+ n = dm_block_data(shadow_current(s));
+
+ if (le32_to_cpu(n->header.flags) & LEAF_NODE)
+ return do_leaf(n, key, index);
+
+ r = rebalance_children(s, info, key);
+ if (r)
+ break;
+
+ n = dm_block_data(shadow_current(s));
+ if (le32_to_cpu(n->header.flags) & LEAF_NODE)
+ return do_leaf(n, key, index);
+
+ i = lower_bound(n, key);
+
+ /*
+ * We know the key is present, or else
+ * rebalance_children would have returned
+ * -ENODATA
+ */
+ root = value64(n, i);
+ }
+
+ return r;
+}
+
+int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, dm_block_t *new_root)
+{
+ unsigned level, last_level = info->levels - 1;
+ int index = 0, r = 0;
+ struct shadow_spine spine;
+ struct node *n;
+
+ init_shadow_spine(&spine, info);
+ for (level = 0; level < info->levels; level++) {
+ r = remove_raw(&spine, info,
+ (level == last_level ?
+ &info->value_type : &le64_type),
+ root, keys[level], (unsigned *)&index);
+ if (r < 0)
+ break;
+
+ n = dm_block_data(shadow_current(&spine));
+ if (level != last_level) {
+ root = value64(n, index);
+ continue;
+ }
+
+ BUG_ON(index < 0 || index >= le32_to_cpu(n->header.nr_entries));
+
+ if (info->value_type.dec)
+ info->value_type.dec(info->value_type.context,
+ value_ptr(n, index, info->value_type.size));
+
+ delete_at(n, index);
+ }
+
+ *new_root = shadow_root(&spine);
+ exit_shadow_spine(&spine);
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(dm_btree_remove);
diff --git a/drivers/md/persistent-data/dm-btree-spine.c b/drivers/md/persistent-data/dm-btree-spine.c
new file mode 100644
index 000000000000..d9a7912ee8ee
--- /dev/null
+++ b/drivers/md/persistent-data/dm-btree-spine.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-btree-internal.h"
+#include "dm-transaction-manager.h"
+
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "btree spine"
+
+/*----------------------------------------------------------------*/
+
+#define BTREE_CSUM_XOR 121107
+
+static int node_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size);
+
+static void node_prepare_for_write(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct node *n = dm_block_data(b);
+ struct node_header *h = &n->header;
+
+ h->blocknr = cpu_to_le64(dm_block_location(b));
+ h->csum = cpu_to_le32(dm_bm_checksum(&h->flags,
+ block_size - sizeof(__le32),
+ BTREE_CSUM_XOR));
+
+ BUG_ON(node_check(v, b, 4096));
+}
+
+static int node_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct node *n = dm_block_data(b);
+ struct node_header *h = &n->header;
+ size_t value_size;
+ __le32 csum_disk;
+ uint32_t flags;
+
+ if (dm_block_location(b) != le64_to_cpu(h->blocknr)) {
+ DMERR("node_check failed blocknr %llu wanted %llu",
+ le64_to_cpu(h->blocknr), dm_block_location(b));
+ return -ENOTBLK;
+ }
+
+ csum_disk = cpu_to_le32(dm_bm_checksum(&h->flags,
+ block_size - sizeof(__le32),
+ BTREE_CSUM_XOR));
+ if (csum_disk != h->csum) {
+ DMERR("node_check failed csum %u wanted %u",
+ le32_to_cpu(csum_disk), le32_to_cpu(h->csum));
+ return -EILSEQ;
+ }
+
+ value_size = le32_to_cpu(h->value_size);
+
+ if (sizeof(struct node_header) +
+ (sizeof(__le64) + value_size) * le32_to_cpu(h->max_entries) > block_size) {
+ DMERR("node_check failed: max_entries too large");
+ return -EILSEQ;
+ }
+
+ if (le32_to_cpu(h->nr_entries) > le32_to_cpu(h->max_entries)) {
+ DMERR("node_check failed, too many entries");
+ return -EILSEQ;
+ }
+
+ /*
+ * The node must be either INTERNAL or LEAF.
+ */
+ flags = le32_to_cpu(h->flags);
+ if (!(flags & INTERNAL_NODE) && !(flags & LEAF_NODE)) {
+ DMERR("node_check failed, node is neither INTERNAL or LEAF");
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+struct dm_block_validator btree_node_validator = {
+ .name = "btree_node",
+ .prepare_for_write = node_prepare_for_write,
+ .check = node_check
+};
+
+/*----------------------------------------------------------------*/
+
+static int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
+ struct dm_block **result)
+{
+ return dm_tm_read_lock(info->tm, b, &btree_node_validator, result);
+}
+
+static int bn_shadow(struct dm_btree_info *info, dm_block_t orig,
+ struct dm_btree_value_type *vt,
+ struct dm_block **result)
+{
+ int r, inc;
+
+ r = dm_tm_shadow_block(info->tm, orig, &btree_node_validator,
+ result, &inc);
+ if (!r && inc)
+ inc_children(info->tm, dm_block_data(*result), vt);
+
+ return r;
+}
+
+int new_block(struct dm_btree_info *info, struct dm_block **result)
+{
+ return dm_tm_new_block(info->tm, &btree_node_validator, result);
+}
+
+int unlock_block(struct dm_btree_info *info, struct dm_block *b)
+{
+ return dm_tm_unlock(info->tm, b);
+}
+
+/*----------------------------------------------------------------*/
+
+void init_ro_spine(struct ro_spine *s, struct dm_btree_info *info)
+{
+ s->info = info;
+ s->count = 0;
+ s->nodes[0] = NULL;
+ s->nodes[1] = NULL;
+}
+
+int exit_ro_spine(struct ro_spine *s)
+{
+ int r = 0, i;
+
+ for (i = 0; i < s->count; i++) {
+ int r2 = unlock_block(s->info, s->nodes[i]);
+ if (r2 < 0)
+ r = r2;
+ }
+
+ return r;
+}
+
+int ro_step(struct ro_spine *s, dm_block_t new_child)
+{
+ int r;
+
+ if (s->count == 2) {
+ r = unlock_block(s->info, s->nodes[0]);
+ if (r < 0)
+ return r;
+ s->nodes[0] = s->nodes[1];
+ s->count--;
+ }
+
+ r = bn_read_lock(s->info, new_child, s->nodes + s->count);
+ if (!r)
+ s->count++;
+
+ return r;
+}
+
+struct node *ro_node(struct ro_spine *s)
+{
+ struct dm_block *block;
+
+ BUG_ON(!s->count);
+ block = s->nodes[s->count - 1];
+
+ return dm_block_data(block);
+}
+
+/*----------------------------------------------------------------*/
+
+void init_shadow_spine(struct shadow_spine *s, struct dm_btree_info *info)
+{
+ s->info = info;
+ s->count = 0;
+}
+
+int exit_shadow_spine(struct shadow_spine *s)
+{
+ int r = 0, i;
+
+ for (i = 0; i < s->count; i++) {
+ int r2 = unlock_block(s->info, s->nodes[i]);
+ if (r2 < 0)
+ r = r2;
+ }
+
+ return r;
+}
+
+int shadow_step(struct shadow_spine *s, dm_block_t b,
+ struct dm_btree_value_type *vt)
+{
+ int r;
+
+ if (s->count == 2) {
+ r = unlock_block(s->info, s->nodes[0]);
+ if (r < 0)
+ return r;
+ s->nodes[0] = s->nodes[1];
+ s->count--;
+ }
+
+ r = bn_shadow(s->info, b, vt, s->nodes + s->count);
+ if (!r) {
+ if (!s->count)
+ s->root = dm_block_location(s->nodes[0]);
+
+ s->count++;
+ }
+
+ return r;
+}
+
+struct dm_block *shadow_current(struct shadow_spine *s)
+{
+ BUG_ON(!s->count);
+
+ return s->nodes[s->count - 1];
+}
+
+struct dm_block *shadow_parent(struct shadow_spine *s)
+{
+ BUG_ON(s->count != 2);
+
+ return s->count == 2 ? s->nodes[0] : NULL;
+}
+
+int shadow_has_parent(struct shadow_spine *s)
+{
+ return s->count >= 2;
+}
+
+int shadow_root(struct shadow_spine *s)
+{
+ return s->root;
+}
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c
new file mode 100644
index 000000000000..bd1e7ffbe26c
--- /dev/null
+++ b/drivers/md/persistent-data/dm-btree.c
@@ -0,0 +1,805 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-btree-internal.h"
+#include "dm-space-map.h"
+#include "dm-transaction-manager.h"
+
+#include <linux/export.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "btree"
+
+/*----------------------------------------------------------------
+ * Array manipulation
+ *--------------------------------------------------------------*/
+static void memcpy_disk(void *dest, const void *src, size_t len)
+ __dm_written_to_disk(src)
+{
+ memcpy(dest, src, len);
+ __dm_unbless_for_disk(src);
+}
+
+static void array_insert(void *base, size_t elt_size, unsigned nr_elts,
+ unsigned index, void *elt)
+ __dm_written_to_disk(elt)
+{
+ if (index < nr_elts)
+ memmove(base + (elt_size * (index + 1)),
+ base + (elt_size * index),
+ (nr_elts - index) * elt_size);
+
+ memcpy_disk(base + (elt_size * index), elt, elt_size);
+}
+
+/*----------------------------------------------------------------*/
+
+/* makes the assumption that no two keys are the same. */
+static int bsearch(struct node *n, uint64_t key, int want_hi)
+{
+ int lo = -1, hi = le32_to_cpu(n->header.nr_entries);
+
+ while (hi - lo > 1) {
+ int mid = lo + ((hi - lo) / 2);
+ uint64_t mid_key = le64_to_cpu(n->keys[mid]);
+
+ if (mid_key == key)
+ return mid;
+
+ if (mid_key < key)
+ lo = mid;
+ else
+ hi = mid;
+ }
+
+ return want_hi ? hi : lo;
+}
+
+int lower_bound(struct node *n, uint64_t key)
+{
+ return bsearch(n, key, 0);
+}
+
+void inc_children(struct dm_transaction_manager *tm, struct node *n,
+ struct dm_btree_value_type *vt)
+{
+ unsigned i;
+ uint32_t nr_entries = le32_to_cpu(n->header.nr_entries);
+
+ if (le32_to_cpu(n->header.flags) & INTERNAL_NODE)
+ for (i = 0; i < nr_entries; i++)
+ dm_tm_inc(tm, value64(n, i));
+ else if (vt->inc)
+ for (i = 0; i < nr_entries; i++)
+ vt->inc(vt->context,
+ value_ptr(n, i, vt->size));
+}
+
+static int insert_at(size_t value_size, struct node *node, unsigned index,
+ uint64_t key, void *value)
+ __dm_written_to_disk(value)
+{
+ uint32_t nr_entries = le32_to_cpu(node->header.nr_entries);
+ __le64 key_le = cpu_to_le64(key);
+
+ if (index > nr_entries ||
+ index >= le32_to_cpu(node->header.max_entries)) {
+ DMERR("too many entries in btree node for insert");
+ __dm_unbless_for_disk(value);
+ return -ENOMEM;
+ }
+
+ __dm_bless_for_disk(&key_le);
+
+ array_insert(node->keys, sizeof(*node->keys), nr_entries, index, &key_le);
+ array_insert(value_base(node), value_size, nr_entries, index, value);
+ node->header.nr_entries = cpu_to_le32(nr_entries + 1);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * We want 3n entries (for some n). This works more nicely for repeated
+ * insert remove loops than (2n + 1).
+ */
+static uint32_t calc_max_entries(size_t value_size, size_t block_size)
+{
+ uint32_t total, n;
+ size_t elt_size = sizeof(uint64_t) + value_size; /* key + value */
+
+ block_size -= sizeof(struct node_header);
+ total = block_size / elt_size;
+ n = total / 3; /* rounds down */
+
+ return 3 * n;
+}
+
+int dm_btree_empty(struct dm_btree_info *info, dm_block_t *root)
+{
+ int r;
+ struct dm_block *b;
+ struct node *n;
+ size_t block_size;
+ uint32_t max_entries;
+
+ r = new_block(info, &b);
+ if (r < 0)
+ return r;
+
+ block_size = dm_bm_block_size(dm_tm_get_bm(info->tm));
+ max_entries = calc_max_entries(info->value_type.size, block_size);
+
+ n = dm_block_data(b);
+ memset(n, 0, block_size);
+ n->header.flags = cpu_to_le32(LEAF_NODE);
+ n->header.nr_entries = cpu_to_le32(0);
+ n->header.max_entries = cpu_to_le32(max_entries);
+ n->header.value_size = cpu_to_le32(info->value_type.size);
+
+ *root = dm_block_location(b);
+ return unlock_block(info, b);
+}
+EXPORT_SYMBOL_GPL(dm_btree_empty);
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Deletion uses a recursive algorithm, since we have limited stack space
+ * we explicitly manage our own stack on the heap.
+ */
+#define MAX_SPINE_DEPTH 64
+struct frame {
+ struct dm_block *b;
+ struct node *n;
+ unsigned level;
+ unsigned nr_children;
+ unsigned current_child;
+};
+
+struct del_stack {
+ struct dm_transaction_manager *tm;
+ int top;
+ struct frame spine[MAX_SPINE_DEPTH];
+};
+
+static int top_frame(struct del_stack *s, struct frame **f)
+{
+ if (s->top < 0) {
+ DMERR("btree deletion stack empty");
+ return -EINVAL;
+ }
+
+ *f = s->spine + s->top;
+
+ return 0;
+}
+
+static int unprocessed_frames(struct del_stack *s)
+{
+ return s->top >= 0;
+}
+
+static int push_frame(struct del_stack *s, dm_block_t b, unsigned level)
+{
+ int r;
+ uint32_t ref_count;
+
+ if (s->top >= MAX_SPINE_DEPTH - 1) {
+ DMERR("btree deletion stack out of memory");
+ return -ENOMEM;
+ }
+
+ r = dm_tm_ref(s->tm, b, &ref_count);
+ if (r)
+ return r;
+
+ if (ref_count > 1)
+ /*
+ * This is a shared node, so we can just decrement it's
+ * reference counter and leave the children.
+ */
+ dm_tm_dec(s->tm, b);
+
+ else {
+ struct frame *f = s->spine + ++s->top;
+
+ r = dm_tm_read_lock(s->tm, b, &btree_node_validator, &f->b);
+ if (r) {
+ s->top--;
+ return r;
+ }
+
+ f->n = dm_block_data(f->b);
+ f->level = level;
+ f->nr_children = le32_to_cpu(f->n->header.nr_entries);
+ f->current_child = 0;
+ }
+
+ return 0;
+}
+
+static void pop_frame(struct del_stack *s)
+{
+ struct frame *f = s->spine + s->top--;
+
+ dm_tm_dec(s->tm, dm_block_location(f->b));
+ dm_tm_unlock(s->tm, f->b);
+}
+
+int dm_btree_del(struct dm_btree_info *info, dm_block_t root)
+{
+ int r;
+ struct del_stack *s;
+
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+ s->tm = info->tm;
+ s->top = -1;
+
+ r = push_frame(s, root, 1);
+ if (r)
+ goto out;
+
+ while (unprocessed_frames(s)) {
+ uint32_t flags;
+ struct frame *f;
+ dm_block_t b;
+
+ r = top_frame(s, &f);
+ if (r)
+ goto out;
+
+ if (f->current_child >= f->nr_children) {
+ pop_frame(s);
+ continue;
+ }
+
+ flags = le32_to_cpu(f->n->header.flags);
+ if (flags & INTERNAL_NODE) {
+ b = value64(f->n, f->current_child);
+ f->current_child++;
+ r = push_frame(s, b, f->level);
+ if (r)
+ goto out;
+
+ } else if (f->level != (info->levels - 1)) {
+ b = value64(f->n, f->current_child);
+ f->current_child++;
+ r = push_frame(s, b, f->level + 1);
+ if (r)
+ goto out;
+
+ } else {
+ if (info->value_type.dec) {
+ unsigned i;
+
+ for (i = 0; i < f->nr_children; i++)
+ info->value_type.dec(info->value_type.context,
+ value_ptr(f->n, i, info->value_type.size));
+ }
+ f->current_child = f->nr_children;
+ }
+ }
+
+out:
+ kfree(s);
+ return r;
+}
+EXPORT_SYMBOL_GPL(dm_btree_del);
+
+/*----------------------------------------------------------------*/
+
+static int btree_lookup_raw(struct ro_spine *s, dm_block_t block, uint64_t key,
+ int (*search_fn)(struct node *, uint64_t),
+ uint64_t *result_key, void *v, size_t value_size)
+{
+ int i, r;
+ uint32_t flags, nr_entries;
+
+ do {
+ r = ro_step(s, block);
+ if (r < 0)
+ return r;
+
+ i = search_fn(ro_node(s), key);
+
+ flags = le32_to_cpu(ro_node(s)->header.flags);
+ nr_entries = le32_to_cpu(ro_node(s)->header.nr_entries);
+ if (i < 0 || i >= nr_entries)
+ return -ENODATA;
+
+ if (flags & INTERNAL_NODE)
+ block = value64(ro_node(s), i);
+
+ } while (!(flags & LEAF_NODE));
+
+ *result_key = le64_to_cpu(ro_node(s)->keys[i]);
+ memcpy(v, value_ptr(ro_node(s), i, value_size), value_size);
+
+ return 0;
+}
+
+int dm_btree_lookup(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value_le)
+{
+ unsigned level, last_level = info->levels - 1;
+ int r = -ENODATA;
+ uint64_t rkey;
+ __le64 internal_value_le;
+ struct ro_spine spine;
+
+ init_ro_spine(&spine, info);
+ for (level = 0; level < info->levels; level++) {
+ size_t size;
+ void *value_p;
+
+ if (level == last_level) {
+ value_p = value_le;
+ size = info->value_type.size;
+
+ } else {
+ value_p = &internal_value_le;
+ size = sizeof(uint64_t);
+ }
+
+ r = btree_lookup_raw(&spine, root, keys[level],
+ lower_bound, &rkey,
+ value_p, size);
+
+ if (!r) {
+ if (rkey != keys[level]) {
+ exit_ro_spine(&spine);
+ return -ENODATA;
+ }
+ } else {
+ exit_ro_spine(&spine);
+ return r;
+ }
+
+ root = le64_to_cpu(internal_value_le);
+ }
+ exit_ro_spine(&spine);
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(dm_btree_lookup);
+
+/*
+ * Splits a node by creating a sibling node and shifting half the nodes
+ * contents across. Assumes there is a parent node, and it has room for
+ * another child.
+ *
+ * Before:
+ * +--------+
+ * | Parent |
+ * +--------+
+ * |
+ * v
+ * +----------+
+ * | A ++++++ |
+ * +----------+
+ *
+ *
+ * After:
+ * +--------+
+ * | Parent |
+ * +--------+
+ * | |
+ * v +------+
+ * +---------+ |
+ * | A* +++ | v
+ * +---------+ +-------+
+ * | B +++ |
+ * +-------+
+ *
+ * Where A* is a shadow of A.
+ */
+static int btree_split_sibling(struct shadow_spine *s, dm_block_t root,
+ unsigned parent_index, uint64_t key)
+{
+ int r;
+ size_t size;
+ unsigned nr_left, nr_right;
+ struct dm_block *left, *right, *parent;
+ struct node *ln, *rn, *pn;
+ __le64 location;
+
+ left = shadow_current(s);
+
+ r = new_block(s->info, &right);
+ if (r < 0)
+ return r;
+
+ ln = dm_block_data(left);
+ rn = dm_block_data(right);
+
+ nr_left = le32_to_cpu(ln->header.nr_entries) / 2;
+ nr_right = le32_to_cpu(ln->header.nr_entries) - nr_left;
+
+ ln->header.nr_entries = cpu_to_le32(nr_left);
+
+ rn->header.flags = ln->header.flags;
+ rn->header.nr_entries = cpu_to_le32(nr_right);
+ rn->header.max_entries = ln->header.max_entries;
+ rn->header.value_size = ln->header.value_size;
+ memcpy(rn->keys, ln->keys + nr_left, nr_right * sizeof(rn->keys[0]));
+
+ size = le32_to_cpu(ln->header.flags) & INTERNAL_NODE ?
+ sizeof(uint64_t) : s->info->value_type.size;
+ memcpy(value_ptr(rn, 0, size), value_ptr(ln, nr_left, size),
+ size * nr_right);
+
+ /*
+ * Patch up the parent
+ */
+ parent = shadow_parent(s);
+
+ pn = dm_block_data(parent);
+ location = cpu_to_le64(dm_block_location(left));
+ __dm_bless_for_disk(&location);
+ memcpy_disk(value_ptr(pn, parent_index, sizeof(__le64)),
+ &location, sizeof(__le64));
+
+ location = cpu_to_le64(dm_block_location(right));
+ __dm_bless_for_disk(&location);
+
+ r = insert_at(sizeof(__le64), pn, parent_index + 1,
+ le64_to_cpu(rn->keys[0]), &location);
+ if (r)
+ return r;
+
+ if (key < le64_to_cpu(rn->keys[0])) {
+ unlock_block(s->info, right);
+ s->nodes[1] = left;
+ } else {
+ unlock_block(s->info, left);
+ s->nodes[1] = right;
+ }
+
+ return 0;
+}
+
+/*
+ * Splits a node by creating two new children beneath the given node.
+ *
+ * Before:
+ * +----------+
+ * | A ++++++ |
+ * +----------+
+ *
+ *
+ * After:
+ * +------------+
+ * | A (shadow) |
+ * +------------+
+ * | |
+ * +------+ +----+
+ * | |
+ * v v
+ * +-------+ +-------+
+ * | B +++ | | C +++ |
+ * +-------+ +-------+
+ */
+static int btree_split_beneath(struct shadow_spine *s, uint64_t key)
+{
+ int r;
+ size_t size;
+ unsigned nr_left, nr_right;
+ struct dm_block *left, *right, *new_parent;
+ struct node *pn, *ln, *rn;
+ __le64 val;
+
+ new_parent = shadow_current(s);
+
+ r = new_block(s->info, &left);
+ if (r < 0)
+ return r;
+
+ r = new_block(s->info, &right);
+ if (r < 0) {
+ /* FIXME: put left */
+ return r;
+ }
+
+ pn = dm_block_data(new_parent);
+ ln = dm_block_data(left);
+ rn = dm_block_data(right);
+
+ nr_left = le32_to_cpu(pn->header.nr_entries) / 2;
+ nr_right = le32_to_cpu(pn->header.nr_entries) - nr_left;
+
+ ln->header.flags = pn->header.flags;
+ ln->header.nr_entries = cpu_to_le32(nr_left);
+ ln->header.max_entries = pn->header.max_entries;
+ ln->header.value_size = pn->header.value_size;
+
+ rn->header.flags = pn->header.flags;
+ rn->header.nr_entries = cpu_to_le32(nr_right);
+ rn->header.max_entries = pn->header.max_entries;
+ rn->header.value_size = pn->header.value_size;
+
+ memcpy(ln->keys, pn->keys, nr_left * sizeof(pn->keys[0]));
+ memcpy(rn->keys, pn->keys + nr_left, nr_right * sizeof(pn->keys[0]));
+
+ size = le32_to_cpu(pn->header.flags) & INTERNAL_NODE ?
+ sizeof(__le64) : s->info->value_type.size;
+ memcpy(value_ptr(ln, 0, size), value_ptr(pn, 0, size), nr_left * size);
+ memcpy(value_ptr(rn, 0, size), value_ptr(pn, nr_left, size),
+ nr_right * size);
+
+ /* new_parent should just point to l and r now */
+ pn->header.flags = cpu_to_le32(INTERNAL_NODE);
+ pn->header.nr_entries = cpu_to_le32(2);
+ pn->header.max_entries = cpu_to_le32(
+ calc_max_entries(sizeof(__le64),
+ dm_bm_block_size(
+ dm_tm_get_bm(s->info->tm))));
+ pn->header.value_size = cpu_to_le32(sizeof(__le64));
+
+ val = cpu_to_le64(dm_block_location(left));
+ __dm_bless_for_disk(&val);
+ pn->keys[0] = ln->keys[0];
+ memcpy_disk(value_ptr(pn, 0, sizeof(__le64)), &val, sizeof(__le64));
+
+ val = cpu_to_le64(dm_block_location(right));
+ __dm_bless_for_disk(&val);
+ pn->keys[1] = rn->keys[0];
+ memcpy_disk(value_ptr(pn, 1, sizeof(__le64)), &val, sizeof(__le64));
+
+ /*
+ * rejig the spine. This is ugly, since it knows too
+ * much about the spine
+ */
+ if (s->nodes[0] != new_parent) {
+ unlock_block(s->info, s->nodes[0]);
+ s->nodes[0] = new_parent;
+ }
+ if (key < le64_to_cpu(rn->keys[0])) {
+ unlock_block(s->info, right);
+ s->nodes[1] = left;
+ } else {
+ unlock_block(s->info, left);
+ s->nodes[1] = right;
+ }
+ s->count = 2;
+
+ return 0;
+}
+
+static int btree_insert_raw(struct shadow_spine *s, dm_block_t root,
+ struct dm_btree_value_type *vt,
+ uint64_t key, unsigned *index)
+{
+ int r, i = *index, top = 1;
+ struct node *node;
+
+ for (;;) {
+ r = shadow_step(s, root, vt);
+ if (r < 0)
+ return r;
+
+ node = dm_block_data(shadow_current(s));
+
+ /*
+ * We have to patch up the parent node, ugly, but I don't
+ * see a way to do this automatically as part of the spine
+ * op.
+ */
+ if (shadow_has_parent(s) && i >= 0) { /* FIXME: second clause unness. */
+ __le64 location = cpu_to_le64(dm_block_location(shadow_current(s)));
+
+ __dm_bless_for_disk(&location);
+ memcpy_disk(value_ptr(dm_block_data(shadow_parent(s)), i, sizeof(uint64_t)),
+ &location, sizeof(__le64));
+ }
+
+ node = dm_block_data(shadow_current(s));
+
+ if (node->header.nr_entries == node->header.max_entries) {
+ if (top)
+ r = btree_split_beneath(s, key);
+ else
+ r = btree_split_sibling(s, root, i, key);
+
+ if (r < 0)
+ return r;
+ }
+
+ node = dm_block_data(shadow_current(s));
+
+ i = lower_bound(node, key);
+
+ if (le32_to_cpu(node->header.flags) & LEAF_NODE)
+ break;
+
+ if (i < 0) {
+ /* change the bounds on the lowest key */
+ node->keys[0] = cpu_to_le64(key);
+ i = 0;
+ }
+
+ root = value64(node, i);
+ top = 0;
+ }
+
+ if (i < 0 || le64_to_cpu(node->keys[i]) != key)
+ i++;
+
+ *index = i;
+ return 0;
+}
+
+static int insert(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value, dm_block_t *new_root,
+ int *inserted)
+ __dm_written_to_disk(value)
+{
+ int r, need_insert;
+ unsigned level, index = -1, last_level = info->levels - 1;
+ dm_block_t block = root;
+ struct shadow_spine spine;
+ struct node *n;
+ struct dm_btree_value_type le64_type;
+
+ le64_type.context = NULL;
+ le64_type.size = sizeof(__le64);
+ le64_type.inc = NULL;
+ le64_type.dec = NULL;
+ le64_type.equal = NULL;
+
+ init_shadow_spine(&spine, info);
+
+ for (level = 0; level < (info->levels - 1); level++) {
+ r = btree_insert_raw(&spine, block, &le64_type, keys[level], &index);
+ if (r < 0)
+ goto bad;
+
+ n = dm_block_data(shadow_current(&spine));
+ need_insert = ((index >= le32_to_cpu(n->header.nr_entries)) ||
+ (le64_to_cpu(n->keys[index]) != keys[level]));
+
+ if (need_insert) {
+ dm_block_t new_tree;
+ __le64 new_le;
+
+ r = dm_btree_empty(info, &new_tree);
+ if (r < 0)
+ goto bad;
+
+ new_le = cpu_to_le64(new_tree);
+ __dm_bless_for_disk(&new_le);
+
+ r = insert_at(sizeof(uint64_t), n, index,
+ keys[level], &new_le);
+ if (r)
+ goto bad;
+ }
+
+ if (level < last_level)
+ block = value64(n, index);
+ }
+
+ r = btree_insert_raw(&spine, block, &info->value_type,
+ keys[level], &index);
+ if (r < 0)
+ goto bad;
+
+ n = dm_block_data(shadow_current(&spine));
+ need_insert = ((index >= le32_to_cpu(n->header.nr_entries)) ||
+ (le64_to_cpu(n->keys[index]) != keys[level]));
+
+ if (need_insert) {
+ if (inserted)
+ *inserted = 1;
+
+ r = insert_at(info->value_type.size, n, index,
+ keys[level], value);
+ if (r)
+ goto bad_unblessed;
+ } else {
+ if (inserted)
+ *inserted = 0;
+
+ if (info->value_type.dec &&
+ (!info->value_type.equal ||
+ !info->value_type.equal(
+ info->value_type.context,
+ value_ptr(n, index, info->value_type.size),
+ value))) {
+ info->value_type.dec(info->value_type.context,
+ value_ptr(n, index, info->value_type.size));
+ }
+ memcpy_disk(value_ptr(n, index, info->value_type.size),
+ value, info->value_type.size);
+ }
+
+ *new_root = shadow_root(&spine);
+ exit_shadow_spine(&spine);
+
+ return 0;
+
+bad:
+ __dm_unbless_for_disk(value);
+bad_unblessed:
+ exit_shadow_spine(&spine);
+ return r;
+}
+
+int dm_btree_insert(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value, dm_block_t *new_root)
+ __dm_written_to_disk(value)
+{
+ return insert(info, root, keys, value, new_root, NULL);
+}
+EXPORT_SYMBOL_GPL(dm_btree_insert);
+
+int dm_btree_insert_notify(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value, dm_block_t *new_root,
+ int *inserted)
+ __dm_written_to_disk(value)
+{
+ return insert(info, root, keys, value, new_root, inserted);
+}
+EXPORT_SYMBOL_GPL(dm_btree_insert_notify);
+
+/*----------------------------------------------------------------*/
+
+static int find_highest_key(struct ro_spine *s, dm_block_t block,
+ uint64_t *result_key, dm_block_t *next_block)
+{
+ int i, r;
+ uint32_t flags;
+
+ do {
+ r = ro_step(s, block);
+ if (r < 0)
+ return r;
+
+ flags = le32_to_cpu(ro_node(s)->header.flags);
+ i = le32_to_cpu(ro_node(s)->header.nr_entries);
+ if (!i)
+ return -ENODATA;
+ else
+ i--;
+
+ *result_key = le64_to_cpu(ro_node(s)->keys[i]);
+ if (next_block || flags & INTERNAL_NODE)
+ block = value64(ro_node(s), i);
+
+ } while (flags & INTERNAL_NODE);
+
+ if (next_block)
+ *next_block = block;
+ return 0;
+}
+
+int dm_btree_find_highest_key(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *result_keys)
+{
+ int r = 0, count = 0, level;
+ struct ro_spine spine;
+
+ init_ro_spine(&spine, info);
+ for (level = 0; level < info->levels; level++) {
+ r = find_highest_key(&spine, root, result_keys + level,
+ level == info->levels - 1 ? NULL : &root);
+ if (r == -ENODATA) {
+ r = 0;
+ break;
+
+ } else if (r)
+ break;
+
+ count++;
+ }
+ exit_ro_spine(&spine);
+
+ return r ? r : count;
+}
+EXPORT_SYMBOL_GPL(dm_btree_find_highest_key);
diff --git a/drivers/md/persistent-data/dm-btree.h b/drivers/md/persistent-data/dm-btree.h
new file mode 100644
index 000000000000..ae02c84410ff
--- /dev/null
+++ b/drivers/md/persistent-data/dm-btree.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+#ifndef _LINUX_DM_BTREE_H
+#define _LINUX_DM_BTREE_H
+
+#include "dm-block-manager.h"
+
+struct dm_transaction_manager;
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Annotations used to check on-disk metadata is handled as little-endian.
+ */
+#ifdef __CHECKER__
+# define __dm_written_to_disk(x) __releases(x)
+# define __dm_reads_from_disk(x) __acquires(x)
+# define __dm_bless_for_disk(x) __acquire(x)
+# define __dm_unbless_for_disk(x) __release(x)
+#else
+# define __dm_written_to_disk(x)
+# define __dm_reads_from_disk(x)
+# define __dm_bless_for_disk(x)
+# define __dm_unbless_for_disk(x)
+#endif
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Manipulates hierarchical B+ trees with 64-bit keys and arbitrary-sized
+ * values.
+ */
+
+/*
+ * Infomation about the values stored within the btree.
+ */
+struct dm_btree_value_type {
+ void *context;
+
+ /*
+ * The size in bytes of each value.
+ */
+ uint32_t size;
+
+ /*
+ * Any of these methods can be safely set to NULL if you do not
+ * need the corresponding feature.
+ */
+
+ /*
+ * The btree is making a duplicate of the value, for instance
+ * because previously-shared btree nodes have now diverged.
+ * @value argument is the new copy that the copy function may modify.
+ * (Probably it just wants to increment a reference count
+ * somewhere.) This method is _not_ called for insertion of a new
+ * value: It is assumed the ref count is already 1.
+ */
+ void (*inc)(void *context, void *value);
+
+ /*
+ * This value is being deleted. The btree takes care of freeing
+ * the memory pointed to by @value. Often the del function just
+ * needs to decrement a reference count somewhere.
+ */
+ void (*dec)(void *context, void *value);
+
+ /*
+ * A test for equality between two values. When a value is
+ * overwritten with a new one, the old one has the dec method
+ * called _unless_ the new and old value are deemed equal.
+ */
+ int (*equal)(void *context, void *value1, void *value2);
+};
+
+/*
+ * The shape and contents of a btree.
+ */
+struct dm_btree_info {
+ struct dm_transaction_manager *tm;
+
+ /*
+ * Number of nested btrees. (Not the depth of a single tree.)
+ */
+ unsigned levels;
+ struct dm_btree_value_type value_type;
+};
+
+/*
+ * Set up an empty tree. O(1).
+ */
+int dm_btree_empty(struct dm_btree_info *info, dm_block_t *root);
+
+/*
+ * Delete a tree. O(n) - this is the slow one! It can also block, so
+ * please don't call it on an IO path.
+ */
+int dm_btree_del(struct dm_btree_info *info, dm_block_t root);
+
+/*
+ * All the lookup functions return -ENODATA if the key cannot be found.
+ */
+
+/*
+ * Tries to find a key that matches exactly. O(ln(n))
+ */
+int dm_btree_lookup(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value_le);
+
+/*
+ * Insertion (or overwrite an existing value). O(ln(n))
+ */
+int dm_btree_insert(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value, dm_block_t *new_root)
+ __dm_written_to_disk(value);
+
+/*
+ * A variant of insert that indicates whether it actually inserted or just
+ * overwrote. Useful if you're keeping track of the number of entries in a
+ * tree.
+ */
+int dm_btree_insert_notify(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value, dm_block_t *new_root,
+ int *inserted)
+ __dm_written_to_disk(value);
+
+/*
+ * Remove a key if present. This doesn't remove empty sub trees. Normally
+ * subtrees represent a separate entity, like a snapshot map, so this is
+ * correct behaviour. O(ln(n)).
+ */
+int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, dm_block_t *new_root);
+
+/*
+ * Returns < 0 on failure. Otherwise the number of key entries that have
+ * been filled out. Remember trees can have zero entries, and as such have
+ * no highest key.
+ */
+int dm_btree_find_highest_key(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *result_keys);
+
+#endif /* _LINUX_DM_BTREE_H */
diff --git a/drivers/md/persistent-data/dm-persistent-data-internal.h b/drivers/md/persistent-data/dm-persistent-data-internal.h
new file mode 100644
index 000000000000..c49e26fff36c
--- /dev/null
+++ b/drivers/md/persistent-data/dm-persistent-data-internal.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _DM_PERSISTENT_DATA_INTERNAL_H
+#define _DM_PERSISTENT_DATA_INTERNAL_H
+
+#include "dm-block-manager.h"
+
+static inline unsigned dm_hash_block(dm_block_t b, unsigned hash_mask)
+{
+ const unsigned BIG_PRIME = 4294967291UL;
+
+ return (((unsigned) b) * BIG_PRIME) & hash_mask;
+}
+
+#endif /* _PERSISTENT_DATA_INTERNAL_H */
diff --git a/drivers/md/persistent-data/dm-space-map-checker.c b/drivers/md/persistent-data/dm-space-map-checker.c
new file mode 100644
index 000000000000..50ed53bf4aa2
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-checker.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-space-map-checker.h"
+
+#include <linux/device-mapper.h>
+#include <linux/export.h>
+
+#ifdef CONFIG_DM_DEBUG_SPACE_MAPS
+
+#define DM_MSG_PREFIX "space map checker"
+
+/*----------------------------------------------------------------*/
+
+struct count_array {
+ dm_block_t nr;
+ dm_block_t nr_free;
+
+ uint32_t *counts;
+};
+
+static int ca_get_count(struct count_array *ca, dm_block_t b, uint32_t *count)
+{
+ if (b >= ca->nr)
+ return -EINVAL;
+
+ *count = ca->counts[b];
+ return 0;
+}
+
+static int ca_count_more_than_one(struct count_array *ca, dm_block_t b, int *r)
+{
+ if (b >= ca->nr)
+ return -EINVAL;
+
+ *r = ca->counts[b] > 1;
+ return 0;
+}
+
+static int ca_set_count(struct count_array *ca, dm_block_t b, uint32_t count)
+{
+ uint32_t old_count;
+
+ if (b >= ca->nr)
+ return -EINVAL;
+
+ old_count = ca->counts[b];
+
+ if (!count && old_count)
+ ca->nr_free++;
+
+ else if (count && !old_count)
+ ca->nr_free--;
+
+ ca->counts[b] = count;
+ return 0;
+}
+
+static int ca_inc_block(struct count_array *ca, dm_block_t b)
+{
+ if (b >= ca->nr)
+ return -EINVAL;
+
+ ca_set_count(ca, b, ca->counts[b] + 1);
+ return 0;
+}
+
+static int ca_dec_block(struct count_array *ca, dm_block_t b)
+{
+ if (b >= ca->nr)
+ return -EINVAL;
+
+ BUG_ON(ca->counts[b] == 0);
+ ca_set_count(ca, b, ca->counts[b] - 1);
+ return 0;
+}
+
+static int ca_create(struct count_array *ca, struct dm_space_map *sm)
+{
+ int r;
+ dm_block_t nr_blocks;
+
+ r = dm_sm_get_nr_blocks(sm, &nr_blocks);
+ if (r)
+ return r;
+
+ ca->nr = nr_blocks;
+ ca->nr_free = nr_blocks;
+ ca->counts = kzalloc(sizeof(*ca->counts) * nr_blocks, GFP_KERNEL);
+ if (!ca->counts)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int ca_load(struct count_array *ca, struct dm_space_map *sm)
+{
+ int r;
+ uint32_t count;
+ dm_block_t nr_blocks, i;
+
+ r = dm_sm_get_nr_blocks(sm, &nr_blocks);
+ if (r)
+ return r;
+
+ BUG_ON(ca->nr != nr_blocks);
+
+ DMWARN("Loading debug space map from disk. This may take some time");
+ for (i = 0; i < nr_blocks; i++) {
+ r = dm_sm_get_count(sm, i, &count);
+ if (r) {
+ DMERR("load failed");
+ return r;
+ }
+
+ ca_set_count(ca, i, count);
+ }
+ DMWARN("Load complete");
+
+ return 0;
+}
+
+static int ca_extend(struct count_array *ca, dm_block_t extra_blocks)
+{
+ dm_block_t nr_blocks = ca->nr + extra_blocks;
+ uint32_t *counts = kzalloc(sizeof(*counts) * nr_blocks, GFP_KERNEL);
+ if (!counts)
+ return -ENOMEM;
+
+ memcpy(counts, ca->counts, sizeof(*counts) * ca->nr);
+ kfree(ca->counts);
+ ca->nr = nr_blocks;
+ ca->nr_free += extra_blocks;
+ ca->counts = counts;
+ return 0;
+}
+
+static int ca_commit(struct count_array *old, struct count_array *new)
+{
+ if (old->nr != new->nr) {
+ BUG_ON(old->nr > new->nr);
+ ca_extend(old, new->nr - old->nr);
+ }
+
+ BUG_ON(old->nr != new->nr);
+ old->nr_free = new->nr_free;
+ memcpy(old->counts, new->counts, sizeof(*old->counts) * old->nr);
+ return 0;
+}
+
+static void ca_destroy(struct count_array *ca)
+{
+ kfree(ca->counts);
+}
+
+/*----------------------------------------------------------------*/
+
+struct sm_checker {
+ struct dm_space_map sm;
+
+ struct count_array old_counts;
+ struct count_array counts;
+
+ struct dm_space_map *real_sm;
+};
+
+static void sm_checker_destroy(struct dm_space_map *sm)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+
+ dm_sm_destroy(smc->real_sm);
+ ca_destroy(&smc->old_counts);
+ ca_destroy(&smc->counts);
+ kfree(smc);
+}
+
+static int sm_checker_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ int r = dm_sm_get_nr_blocks(smc->real_sm, count);
+ if (!r)
+ BUG_ON(smc->old_counts.nr != *count);
+ return r;
+}
+
+static int sm_checker_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ int r = dm_sm_get_nr_free(smc->real_sm, count);
+ if (!r) {
+ /*
+ * Slow, but we know it's correct.
+ */
+ dm_block_t b, n = 0;
+ for (b = 0; b < smc->old_counts.nr; b++)
+ if (smc->old_counts.counts[b] == 0 &&
+ smc->counts.counts[b] == 0)
+ n++;
+
+ if (n != *count)
+ DMERR("free block counts differ, checker %u, sm-disk:%u",
+ (unsigned) n, (unsigned) *count);
+ }
+ return r;
+}
+
+static int sm_checker_new_block(struct dm_space_map *sm, dm_block_t *b)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ int r = dm_sm_new_block(smc->real_sm, b);
+
+ if (!r) {
+ BUG_ON(*b >= smc->old_counts.nr);
+ BUG_ON(smc->old_counts.counts[*b] != 0);
+ BUG_ON(*b >= smc->counts.nr);
+ BUG_ON(smc->counts.counts[*b] != 0);
+ ca_set_count(&smc->counts, *b, 1);
+ }
+
+ return r;
+}
+
+static int sm_checker_inc_block(struct dm_space_map *sm, dm_block_t b)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ int r = dm_sm_inc_block(smc->real_sm, b);
+ int r2 = ca_inc_block(&smc->counts, b);
+ BUG_ON(r != r2);
+ return r;
+}
+
+static int sm_checker_dec_block(struct dm_space_map *sm, dm_block_t b)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ int r = dm_sm_dec_block(smc->real_sm, b);
+ int r2 = ca_dec_block(&smc->counts, b);
+ BUG_ON(r != r2);
+ return r;
+}
+
+static int sm_checker_get_count(struct dm_space_map *sm, dm_block_t b, uint32_t *result)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ uint32_t result2 = 0;
+ int r = dm_sm_get_count(smc->real_sm, b, result);
+ int r2 = ca_get_count(&smc->counts, b, &result2);
+
+ BUG_ON(r != r2);
+ if (!r)
+ BUG_ON(*result != result2);
+ return r;
+}
+
+static int sm_checker_count_more_than_one(struct dm_space_map *sm, dm_block_t b, int *result)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ int result2 = 0;
+ int r = dm_sm_count_is_more_than_one(smc->real_sm, b, result);
+ int r2 = ca_count_more_than_one(&smc->counts, b, &result2);
+
+ BUG_ON(r != r2);
+ if (!r)
+ BUG_ON(!(*result) && result2);
+ return r;
+}
+
+static int sm_checker_set_count(struct dm_space_map *sm, dm_block_t b, uint32_t count)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ uint32_t old_rc;
+ int r = dm_sm_set_count(smc->real_sm, b, count);
+ int r2;
+
+ BUG_ON(b >= smc->counts.nr);
+ old_rc = smc->counts.counts[b];
+ r2 = ca_set_count(&smc->counts, b, count);
+ BUG_ON(r != r2);
+
+ return r;
+}
+
+static int sm_checker_commit(struct dm_space_map *sm)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ int r;
+
+ r = dm_sm_commit(smc->real_sm);
+ if (r)
+ return r;
+
+ r = ca_commit(&smc->old_counts, &smc->counts);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int sm_checker_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ int r = dm_sm_extend(smc->real_sm, extra_blocks);
+ if (r)
+ return r;
+
+ return ca_extend(&smc->counts, extra_blocks);
+}
+
+static int sm_checker_root_size(struct dm_space_map *sm, size_t *result)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ return dm_sm_root_size(smc->real_sm, result);
+}
+
+static int sm_checker_copy_root(struct dm_space_map *sm, void *copy_to_here_le, size_t len)
+{
+ struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
+ return dm_sm_copy_root(smc->real_sm, copy_to_here_le, len);
+}
+
+/*----------------------------------------------------------------*/
+
+static struct dm_space_map ops_ = {
+ .destroy = sm_checker_destroy,
+ .get_nr_blocks = sm_checker_get_nr_blocks,
+ .get_nr_free = sm_checker_get_nr_free,
+ .inc_block = sm_checker_inc_block,
+ .dec_block = sm_checker_dec_block,
+ .new_block = sm_checker_new_block,
+ .get_count = sm_checker_get_count,
+ .count_is_more_than_one = sm_checker_count_more_than_one,
+ .set_count = sm_checker_set_count,
+ .commit = sm_checker_commit,
+ .extend = sm_checker_extend,
+ .root_size = sm_checker_root_size,
+ .copy_root = sm_checker_copy_root
+};
+
+struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm)
+{
+ int r;
+ struct sm_checker *smc;
+
+ if (!sm)
+ return NULL;
+
+ smc = kmalloc(sizeof(*smc), GFP_KERNEL);
+ if (!smc)
+ return NULL;
+
+ memcpy(&smc->sm, &ops_, sizeof(smc->sm));
+ r = ca_create(&smc->old_counts, sm);
+ if (r) {
+ kfree(smc);
+ return NULL;
+ }
+
+ r = ca_create(&smc->counts, sm);
+ if (r) {
+ ca_destroy(&smc->old_counts);
+ kfree(smc);
+ return NULL;
+ }
+
+ smc->real_sm = sm;
+
+ r = ca_load(&smc->counts, sm);
+ if (r) {
+ ca_destroy(&smc->counts);
+ ca_destroy(&smc->old_counts);
+ kfree(smc);
+ return NULL;
+ }
+
+ r = ca_commit(&smc->old_counts, &smc->counts);
+ if (r) {
+ ca_destroy(&smc->counts);
+ ca_destroy(&smc->old_counts);
+ kfree(smc);
+ return NULL;
+ }
+
+ return &smc->sm;
+}
+EXPORT_SYMBOL_GPL(dm_sm_checker_create);
+
+struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm)
+{
+ int r;
+ struct sm_checker *smc;
+
+ if (!sm)
+ return NULL;
+
+ smc = kmalloc(sizeof(*smc), GFP_KERNEL);
+ if (!smc)
+ return NULL;
+
+ memcpy(&smc->sm, &ops_, sizeof(smc->sm));
+ r = ca_create(&smc->old_counts, sm);
+ if (r) {
+ kfree(smc);
+ return NULL;
+ }
+
+ r = ca_create(&smc->counts, sm);
+ if (r) {
+ ca_destroy(&smc->old_counts);
+ kfree(smc);
+ return NULL;
+ }
+
+ smc->real_sm = sm;
+ return &smc->sm;
+}
+EXPORT_SYMBOL_GPL(dm_sm_checker_create_fresh);
+
+/*----------------------------------------------------------------*/
+
+#else
+
+struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm)
+{
+ return sm;
+}
+EXPORT_SYMBOL_GPL(dm_sm_checker_create);
+
+struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm)
+{
+ return sm;
+}
+EXPORT_SYMBOL_GPL(dm_sm_checker_create_fresh);
+
+/*----------------------------------------------------------------*/
+
+#endif
diff --git a/drivers/md/persistent-data/dm-space-map-checker.h b/drivers/md/persistent-data/dm-space-map-checker.h
new file mode 100644
index 000000000000..444dccf6688c
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-checker.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef SNAPSHOTS_SPACE_MAP_CHECKER_H
+#define SNAPSHOTS_SPACE_MAP_CHECKER_H
+
+#include "dm-space-map.h"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * This space map wraps a real on-disk space map, and verifies all of its
+ * operations. It uses a lot of memory, so only use if you have a specific
+ * problem that you're debugging.
+ *
+ * Ownership of @sm passes.
+ */
+struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm);
+struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm);
+
+/*----------------------------------------------------------------*/
+
+#endif
diff --git a/drivers/md/persistent-data/dm-space-map-common.c b/drivers/md/persistent-data/dm-space-map-common.c
new file mode 100644
index 000000000000..df2494c06cdc
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-common.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-space-map-common.h"
+#include "dm-transaction-manager.h"
+
+#include <linux/bitops.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "space map common"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Index validator.
+ */
+#define INDEX_CSUM_XOR 160478
+
+static void index_prepare_for_write(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct disk_metadata_index *mi_le = dm_block_data(b);
+
+ mi_le->blocknr = cpu_to_le64(dm_block_location(b));
+ mi_le->csum = cpu_to_le32(dm_bm_checksum(&mi_le->padding,
+ block_size - sizeof(__le32),
+ INDEX_CSUM_XOR));
+}
+
+static int index_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct disk_metadata_index *mi_le = dm_block_data(b);
+ __le32 csum_disk;
+
+ if (dm_block_location(b) != le64_to_cpu(mi_le->blocknr)) {
+ DMERR("index_check failed blocknr %llu wanted %llu",
+ le64_to_cpu(mi_le->blocknr), dm_block_location(b));
+ return -ENOTBLK;
+ }
+
+ csum_disk = cpu_to_le32(dm_bm_checksum(&mi_le->padding,
+ block_size - sizeof(__le32),
+ INDEX_CSUM_XOR));
+ if (csum_disk != mi_le->csum) {
+ DMERR("index_check failed csum %u wanted %u",
+ le32_to_cpu(csum_disk), le32_to_cpu(mi_le->csum));
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+static struct dm_block_validator index_validator = {
+ .name = "index",
+ .prepare_for_write = index_prepare_for_write,
+ .check = index_check
+};
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Bitmap validator
+ */
+#define BITMAP_CSUM_XOR 240779
+
+static void bitmap_prepare_for_write(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct disk_bitmap_header *disk_header = dm_block_data(b);
+
+ disk_header->blocknr = cpu_to_le64(dm_block_location(b));
+ disk_header->csum = cpu_to_le32(dm_bm_checksum(&disk_header->not_used,
+ block_size - sizeof(__le32),
+ BITMAP_CSUM_XOR));
+}
+
+static int bitmap_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct disk_bitmap_header *disk_header = dm_block_data(b);
+ __le32 csum_disk;
+
+ if (dm_block_location(b) != le64_to_cpu(disk_header->blocknr)) {
+ DMERR("bitmap check failed blocknr %llu wanted %llu",
+ le64_to_cpu(disk_header->blocknr), dm_block_location(b));
+ return -ENOTBLK;
+ }
+
+ csum_disk = cpu_to_le32(dm_bm_checksum(&disk_header->not_used,
+ block_size - sizeof(__le32),
+ BITMAP_CSUM_XOR));
+ if (csum_disk != disk_header->csum) {
+ DMERR("bitmap check failed csum %u wanted %u",
+ le32_to_cpu(csum_disk), le32_to_cpu(disk_header->csum));
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+static struct dm_block_validator dm_sm_bitmap_validator = {
+ .name = "sm_bitmap",
+ .prepare_for_write = bitmap_prepare_for_write,
+ .check = bitmap_check
+};
+
+/*----------------------------------------------------------------*/
+
+#define ENTRIES_PER_WORD 32
+#define ENTRIES_SHIFT 5
+
+static void *dm_bitmap_data(struct dm_block *b)
+{
+ return dm_block_data(b) + sizeof(struct disk_bitmap_header);
+}
+
+#define WORD_MASK_HIGH 0xAAAAAAAAAAAAAAAAULL
+
+static unsigned bitmap_word_used(void *addr, unsigned b)
+{
+ __le64 *words_le = addr;
+ __le64 *w_le = words_le + (b >> ENTRIES_SHIFT);
+
+ uint64_t bits = le64_to_cpu(*w_le);
+ uint64_t mask = (bits + WORD_MASK_HIGH + 1) & WORD_MASK_HIGH;
+
+ return !(~bits & mask);
+}
+
+static unsigned sm_lookup_bitmap(void *addr, unsigned b)
+{
+ __le64 *words_le = addr;
+ __le64 *w_le = words_le + (b >> ENTRIES_SHIFT);
+ unsigned hi, lo;
+
+ b = (b & (ENTRIES_PER_WORD - 1)) << 1;
+ hi = !!test_bit_le(b, (void *) w_le);
+ lo = !!test_bit_le(b + 1, (void *) w_le);
+ return (hi << 1) | lo;
+}
+
+static void sm_set_bitmap(void *addr, unsigned b, unsigned val)
+{
+ __le64 *words_le = addr;
+ __le64 *w_le = words_le + (b >> ENTRIES_SHIFT);
+
+ b = (b & (ENTRIES_PER_WORD - 1)) << 1;
+
+ if (val & 2)
+ __set_bit_le(b, (void *) w_le);
+ else
+ __clear_bit_le(b, (void *) w_le);
+
+ if (val & 1)
+ __set_bit_le(b + 1, (void *) w_le);
+ else
+ __clear_bit_le(b + 1, (void *) w_le);
+}
+
+static int sm_find_free(void *addr, unsigned begin, unsigned end,
+ unsigned *result)
+{
+ while (begin < end) {
+ if (!(begin & (ENTRIES_PER_WORD - 1)) &&
+ bitmap_word_used(addr, begin)) {
+ begin += ENTRIES_PER_WORD;
+ continue;
+ }
+
+ if (!sm_lookup_bitmap(addr, begin)) {
+ *result = begin;
+ return 0;
+ }
+
+ begin++;
+ }
+
+ return -ENOSPC;
+}
+
+/*----------------------------------------------------------------*/
+
+static int sm_ll_init(struct ll_disk *ll, struct dm_transaction_manager *tm)
+{
+ ll->tm = tm;
+
+ ll->bitmap_info.tm = tm;
+ ll->bitmap_info.levels = 1;
+
+ /*
+ * Because the new bitmap blocks are created via a shadow
+ * operation, the old entry has already had its reference count
+ * decremented and we don't need the btree to do any bookkeeping.
+ */
+ ll->bitmap_info.value_type.size = sizeof(struct disk_index_entry);
+ ll->bitmap_info.value_type.inc = NULL;
+ ll->bitmap_info.value_type.dec = NULL;
+ ll->bitmap_info.value_type.equal = NULL;
+
+ ll->ref_count_info.tm = tm;
+ ll->ref_count_info.levels = 1;
+ ll->ref_count_info.value_type.size = sizeof(uint32_t);
+ ll->ref_count_info.value_type.inc = NULL;
+ ll->ref_count_info.value_type.dec = NULL;
+ ll->ref_count_info.value_type.equal = NULL;
+
+ ll->block_size = dm_bm_block_size(dm_tm_get_bm(tm));
+
+ if (ll->block_size > (1 << 30)) {
+ DMERR("block size too big to hold bitmaps");
+ return -EINVAL;
+ }
+
+ ll->entries_per_block = (ll->block_size - sizeof(struct disk_bitmap_header)) *
+ ENTRIES_PER_BYTE;
+ ll->nr_blocks = 0;
+ ll->bitmap_root = 0;
+ ll->ref_count_root = 0;
+
+ return 0;
+}
+
+int sm_ll_extend(struct ll_disk *ll, dm_block_t extra_blocks)
+{
+ int r;
+ dm_block_t i, nr_blocks, nr_indexes;
+ unsigned old_blocks, blocks;
+
+ nr_blocks = ll->nr_blocks + extra_blocks;
+ old_blocks = dm_sector_div_up(ll->nr_blocks, ll->entries_per_block);
+ blocks = dm_sector_div_up(nr_blocks, ll->entries_per_block);
+
+ nr_indexes = dm_sector_div_up(nr_blocks, ll->entries_per_block);
+ if (nr_indexes > ll->max_entries(ll)) {
+ DMERR("space map too large");
+ return -EINVAL;
+ }
+
+ for (i = old_blocks; i < blocks; i++) {
+ struct dm_block *b;
+ struct disk_index_entry idx;
+
+ r = dm_tm_new_block(ll->tm, &dm_sm_bitmap_validator, &b);
+ if (r < 0)
+ return r;
+ idx.blocknr = cpu_to_le64(dm_block_location(b));
+
+ r = dm_tm_unlock(ll->tm, b);
+ if (r < 0)
+ return r;
+
+ idx.nr_free = cpu_to_le32(ll->entries_per_block);
+ idx.none_free_before = 0;
+
+ r = ll->save_ie(ll, i, &idx);
+ if (r < 0)
+ return r;
+ }
+
+ ll->nr_blocks = nr_blocks;
+ return 0;
+}
+
+int sm_ll_lookup_bitmap(struct ll_disk *ll, dm_block_t b, uint32_t *result)
+{
+ int r;
+ dm_block_t index = b;
+ struct disk_index_entry ie_disk;
+ struct dm_block *blk;
+
+ b = do_div(index, ll->entries_per_block);
+ r = ll->load_ie(ll, index, &ie_disk);
+ if (r < 0)
+ return r;
+
+ r = dm_tm_read_lock(ll->tm, le64_to_cpu(ie_disk.blocknr),
+ &dm_sm_bitmap_validator, &blk);
+ if (r < 0)
+ return r;
+
+ *result = sm_lookup_bitmap(dm_bitmap_data(blk), b);
+
+ return dm_tm_unlock(ll->tm, blk);
+}
+
+int sm_ll_lookup(struct ll_disk *ll, dm_block_t b, uint32_t *result)
+{
+ __le32 le_rc;
+ int r = sm_ll_lookup_bitmap(ll, b, result);
+
+ if (r)
+ return r;
+
+ if (*result != 3)
+ return r;
+
+ r = dm_btree_lookup(&ll->ref_count_info, ll->ref_count_root, &b, &le_rc);
+ if (r < 0)
+ return r;
+
+ *result = le32_to_cpu(le_rc);
+
+ return r;
+}
+
+int sm_ll_find_free_block(struct ll_disk *ll, dm_block_t begin,
+ dm_block_t end, dm_block_t *result)
+{
+ int r;
+ struct disk_index_entry ie_disk;
+ dm_block_t i, index_begin = begin;
+ dm_block_t index_end = dm_sector_div_up(end, ll->entries_per_block);
+
+ /*
+ * FIXME: Use shifts
+ */
+ begin = do_div(index_begin, ll->entries_per_block);
+ end = do_div(end, ll->entries_per_block);
+
+ for (i = index_begin; i < index_end; i++, begin = 0) {
+ struct dm_block *blk;
+ unsigned position;
+ uint32_t bit_end;
+
+ r = ll->load_ie(ll, i, &ie_disk);
+ if (r < 0)
+ return r;
+
+ if (le32_to_cpu(ie_disk.nr_free) == 0)
+ continue;
+
+ r = dm_tm_read_lock(ll->tm, le64_to_cpu(ie_disk.blocknr),
+ &dm_sm_bitmap_validator, &blk);
+ if (r < 0)
+ return r;
+
+ bit_end = (i == index_end - 1) ? end : ll->entries_per_block;
+
+ r = sm_find_free(dm_bitmap_data(blk),
+ max_t(unsigned, begin, le32_to_cpu(ie_disk.none_free_before)),
+ bit_end, &position);
+ if (r == -ENOSPC) {
+ /*
+ * This might happen because we started searching
+ * part way through the bitmap.
+ */
+ dm_tm_unlock(ll->tm, blk);
+ continue;
+
+ } else if (r < 0) {
+ dm_tm_unlock(ll->tm, blk);
+ return r;
+ }
+
+ r = dm_tm_unlock(ll->tm, blk);
+ if (r < 0)
+ return r;
+
+ *result = i * ll->entries_per_block + (dm_block_t) position;
+ return 0;
+ }
+
+ return -ENOSPC;
+}
+
+int sm_ll_insert(struct ll_disk *ll, dm_block_t b,
+ uint32_t ref_count, enum allocation_event *ev)
+{
+ int r;
+ uint32_t bit, old;
+ struct dm_block *nb;
+ dm_block_t index = b;
+ struct disk_index_entry ie_disk;
+ void *bm_le;
+ int inc;
+
+ bit = do_div(index, ll->entries_per_block);
+ r = ll->load_ie(ll, index, &ie_disk);
+ if (r < 0)
+ return r;
+
+ r = dm_tm_shadow_block(ll->tm, le64_to_cpu(ie_disk.blocknr),
+ &dm_sm_bitmap_validator, &nb, &inc);
+ if (r < 0) {
+ DMERR("dm_tm_shadow_block() failed");
+ return r;
+ }
+ ie_disk.blocknr = cpu_to_le64(dm_block_location(nb));
+
+ bm_le = dm_bitmap_data(nb);
+ old = sm_lookup_bitmap(bm_le, bit);
+
+ if (ref_count <= 2) {
+ sm_set_bitmap(bm_le, bit, ref_count);
+
+ r = dm_tm_unlock(ll->tm, nb);
+ if (r < 0)
+ return r;
+
+#if 0
+ /* FIXME: dm_btree_remove doesn't handle this yet */
+ if (old > 2) {
+ r = dm_btree_remove(&ll->ref_count_info,
+ ll->ref_count_root,
+ &b, &ll->ref_count_root);
+ if (r)
+ return r;
+ }
+#endif
+
+ } else {
+ __le32 le_rc = cpu_to_le32(ref_count);
+
+ sm_set_bitmap(bm_le, bit, 3);
+ r = dm_tm_unlock(ll->tm, nb);
+ if (r < 0)
+ return r;
+
+ __dm_bless_for_disk(&le_rc);
+ r = dm_btree_insert(&ll->ref_count_info, ll->ref_count_root,
+ &b, &le_rc, &ll->ref_count_root);
+ if (r < 0) {
+ DMERR("ref count insert failed");
+ return r;
+ }
+ }
+
+ if (ref_count && !old) {
+ *ev = SM_ALLOC;
+ ll->nr_allocated++;
+ ie_disk.nr_free = cpu_to_le32(le32_to_cpu(ie_disk.nr_free) - 1);
+ if (le32_to_cpu(ie_disk.none_free_before) == bit)
+ ie_disk.none_free_before = cpu_to_le32(bit + 1);
+
+ } else if (old && !ref_count) {
+ *ev = SM_FREE;
+ ll->nr_allocated--;
+ ie_disk.nr_free = cpu_to_le32(le32_to_cpu(ie_disk.nr_free) + 1);
+ ie_disk.none_free_before = cpu_to_le32(min(le32_to_cpu(ie_disk.none_free_before), bit));
+ }
+
+ return ll->save_ie(ll, index, &ie_disk);
+}
+
+int sm_ll_inc(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev)
+{
+ int r;
+ uint32_t rc;
+
+ r = sm_ll_lookup(ll, b, &rc);
+ if (r)
+ return r;
+
+ return sm_ll_insert(ll, b, rc + 1, ev);
+}
+
+int sm_ll_dec(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev)
+{
+ int r;
+ uint32_t rc;
+
+ r = sm_ll_lookup(ll, b, &rc);
+ if (r)
+ return r;
+
+ if (!rc)
+ return -EINVAL;
+
+ return sm_ll_insert(ll, b, rc - 1, ev);
+}
+
+int sm_ll_commit(struct ll_disk *ll)
+{
+ return ll->commit(ll);
+}
+
+/*----------------------------------------------------------------*/
+
+static int metadata_ll_load_ie(struct ll_disk *ll, dm_block_t index,
+ struct disk_index_entry *ie)
+{
+ memcpy(ie, ll->mi_le.index + index, sizeof(*ie));
+ return 0;
+}
+
+static int metadata_ll_save_ie(struct ll_disk *ll, dm_block_t index,
+ struct disk_index_entry *ie)
+{
+ memcpy(ll->mi_le.index + index, ie, sizeof(*ie));
+ return 0;
+}
+
+static int metadata_ll_init_index(struct ll_disk *ll)
+{
+ int r;
+ struct dm_block *b;
+
+ r = dm_tm_new_block(ll->tm, &index_validator, &b);
+ if (r < 0)
+ return r;
+
+ memcpy(dm_block_data(b), &ll->mi_le, sizeof(ll->mi_le));
+ ll->bitmap_root = dm_block_location(b);
+
+ return dm_tm_unlock(ll->tm, b);
+}
+
+static int metadata_ll_open(struct ll_disk *ll)
+{
+ int r;
+ struct dm_block *block;
+
+ r = dm_tm_read_lock(ll->tm, ll->bitmap_root,
+ &index_validator, &block);
+ if (r)
+ return r;
+
+ memcpy(&ll->mi_le, dm_block_data(block), sizeof(ll->mi_le));
+ return dm_tm_unlock(ll->tm, block);
+}
+
+static dm_block_t metadata_ll_max_entries(struct ll_disk *ll)
+{
+ return MAX_METADATA_BITMAPS;
+}
+
+static int metadata_ll_commit(struct ll_disk *ll)
+{
+ int r, inc;
+ struct dm_block *b;
+
+ r = dm_tm_shadow_block(ll->tm, ll->bitmap_root, &index_validator, &b, &inc);
+ if (r)
+ return r;
+
+ memcpy(dm_block_data(b), &ll->mi_le, sizeof(ll->mi_le));
+ ll->bitmap_root = dm_block_location(b);
+
+ return dm_tm_unlock(ll->tm, b);
+}
+
+int sm_ll_new_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm)
+{
+ int r;
+
+ r = sm_ll_init(ll, tm);
+ if (r < 0)
+ return r;
+
+ ll->load_ie = metadata_ll_load_ie;
+ ll->save_ie = metadata_ll_save_ie;
+ ll->init_index = metadata_ll_init_index;
+ ll->open_index = metadata_ll_open;
+ ll->max_entries = metadata_ll_max_entries;
+ ll->commit = metadata_ll_commit;
+
+ ll->nr_blocks = 0;
+ ll->nr_allocated = 0;
+
+ r = ll->init_index(ll);
+ if (r < 0)
+ return r;
+
+ r = dm_btree_empty(&ll->ref_count_info, &ll->ref_count_root);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sm_ll_open_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm,
+ void *root_le, size_t len)
+{
+ int r;
+ struct disk_sm_root *smr = root_le;
+
+ if (len < sizeof(struct disk_sm_root)) {
+ DMERR("sm_metadata root too small");
+ return -ENOMEM;
+ }
+
+ r = sm_ll_init(ll, tm);
+ if (r < 0)
+ return r;
+
+ ll->load_ie = metadata_ll_load_ie;
+ ll->save_ie = metadata_ll_save_ie;
+ ll->init_index = metadata_ll_init_index;
+ ll->open_index = metadata_ll_open;
+ ll->max_entries = metadata_ll_max_entries;
+ ll->commit = metadata_ll_commit;
+
+ ll->nr_blocks = le64_to_cpu(smr->nr_blocks);
+ ll->nr_allocated = le64_to_cpu(smr->nr_allocated);
+ ll->bitmap_root = le64_to_cpu(smr->bitmap_root);
+ ll->ref_count_root = le64_to_cpu(smr->ref_count_root);
+
+ return ll->open_index(ll);
+}
+
+/*----------------------------------------------------------------*/
+
+static int disk_ll_load_ie(struct ll_disk *ll, dm_block_t index,
+ struct disk_index_entry *ie)
+{
+ return dm_btree_lookup(&ll->bitmap_info, ll->bitmap_root, &index, ie);
+}
+
+static int disk_ll_save_ie(struct ll_disk *ll, dm_block_t index,
+ struct disk_index_entry *ie)
+{
+ __dm_bless_for_disk(ie);
+ return dm_btree_insert(&ll->bitmap_info, ll->bitmap_root,
+ &index, ie, &ll->bitmap_root);
+}
+
+static int disk_ll_init_index(struct ll_disk *ll)
+{
+ return dm_btree_empty(&ll->bitmap_info, &ll->bitmap_root);
+}
+
+static int disk_ll_open(struct ll_disk *ll)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static dm_block_t disk_ll_max_entries(struct ll_disk *ll)
+{
+ return -1ULL;
+}
+
+static int disk_ll_commit(struct ll_disk *ll)
+{
+ return 0;
+}
+
+int sm_ll_new_disk(struct ll_disk *ll, struct dm_transaction_manager *tm)
+{
+ int r;
+
+ r = sm_ll_init(ll, tm);
+ if (r < 0)
+ return r;
+
+ ll->load_ie = disk_ll_load_ie;
+ ll->save_ie = disk_ll_save_ie;
+ ll->init_index = disk_ll_init_index;
+ ll->open_index = disk_ll_open;
+ ll->max_entries = disk_ll_max_entries;
+ ll->commit = disk_ll_commit;
+
+ ll->nr_blocks = 0;
+ ll->nr_allocated = 0;
+
+ r = ll->init_index(ll);
+ if (r < 0)
+ return r;
+
+ r = dm_btree_empty(&ll->ref_count_info, &ll->ref_count_root);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sm_ll_open_disk(struct ll_disk *ll, struct dm_transaction_manager *tm,
+ void *root_le, size_t len)
+{
+ int r;
+ struct disk_sm_root *smr = root_le;
+
+ if (len < sizeof(struct disk_sm_root)) {
+ DMERR("sm_metadata root too small");
+ return -ENOMEM;
+ }
+
+ r = sm_ll_init(ll, tm);
+ if (r < 0)
+ return r;
+
+ ll->load_ie = disk_ll_load_ie;
+ ll->save_ie = disk_ll_save_ie;
+ ll->init_index = disk_ll_init_index;
+ ll->open_index = disk_ll_open;
+ ll->max_entries = disk_ll_max_entries;
+ ll->commit = disk_ll_commit;
+
+ ll->nr_blocks = le64_to_cpu(smr->nr_blocks);
+ ll->nr_allocated = le64_to_cpu(smr->nr_allocated);
+ ll->bitmap_root = le64_to_cpu(smr->bitmap_root);
+ ll->ref_count_root = le64_to_cpu(smr->ref_count_root);
+
+ return ll->open_index(ll);
+}
+
+/*----------------------------------------------------------------*/
diff --git a/drivers/md/persistent-data/dm-space-map-common.h b/drivers/md/persistent-data/dm-space-map-common.h
new file mode 100644
index 000000000000..8f220821a9a9
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-common.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_SPACE_MAP_COMMON_H
+#define DM_SPACE_MAP_COMMON_H
+
+#include "dm-btree.h"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Low level disk format
+ *
+ * Bitmap btree
+ * ------------
+ *
+ * Each value stored in the btree is an index_entry. This points to a
+ * block that is used as a bitmap. Within the bitmap hold 2 bits per
+ * entry, which represent UNUSED = 0, REF_COUNT = 1, REF_COUNT = 2 and
+ * REF_COUNT = many.
+ *
+ * Refcount btree
+ * --------------
+ *
+ * Any entry that has a ref count higher than 2 gets entered in the ref
+ * count tree. The leaf values for this tree is the 32-bit ref count.
+ */
+
+struct disk_index_entry {
+ __le64 blocknr;
+ __le32 nr_free;
+ __le32 none_free_before;
+} __packed;
+
+
+#define MAX_METADATA_BITMAPS 255
+struct disk_metadata_index {
+ __le32 csum;
+ __le32 padding;
+ __le64 blocknr;
+
+ struct disk_index_entry index[MAX_METADATA_BITMAPS];
+} __packed;
+
+struct ll_disk;
+
+typedef int (*load_ie_fn)(struct ll_disk *ll, dm_block_t index, struct disk_index_entry *result);
+typedef int (*save_ie_fn)(struct ll_disk *ll, dm_block_t index, struct disk_index_entry *ie);
+typedef int (*init_index_fn)(struct ll_disk *ll);
+typedef int (*open_index_fn)(struct ll_disk *ll);
+typedef dm_block_t (*max_index_entries_fn)(struct ll_disk *ll);
+typedef int (*commit_fn)(struct ll_disk *ll);
+
+struct ll_disk {
+ struct dm_transaction_manager *tm;
+ struct dm_btree_info bitmap_info;
+ struct dm_btree_info ref_count_info;
+
+ uint32_t block_size;
+ uint32_t entries_per_block;
+ dm_block_t nr_blocks;
+ dm_block_t nr_allocated;
+
+ /*
+ * bitmap_root may be a btree root or a simple index.
+ */
+ dm_block_t bitmap_root;
+
+ dm_block_t ref_count_root;
+
+ struct disk_metadata_index mi_le;
+ load_ie_fn load_ie;
+ save_ie_fn save_ie;
+ init_index_fn init_index;
+ open_index_fn open_index;
+ max_index_entries_fn max_entries;
+ commit_fn commit;
+};
+
+struct disk_sm_root {
+ __le64 nr_blocks;
+ __le64 nr_allocated;
+ __le64 bitmap_root;
+ __le64 ref_count_root;
+} __packed;
+
+#define ENTRIES_PER_BYTE 4
+
+struct disk_bitmap_header {
+ __le32 csum;
+ __le32 not_used;
+ __le64 blocknr;
+} __packed;
+
+enum allocation_event {
+ SM_NONE,
+ SM_ALLOC,
+ SM_FREE,
+};
+
+/*----------------------------------------------------------------*/
+
+int sm_ll_extend(struct ll_disk *ll, dm_block_t extra_blocks);
+int sm_ll_lookup_bitmap(struct ll_disk *ll, dm_block_t b, uint32_t *result);
+int sm_ll_lookup(struct ll_disk *ll, dm_block_t b, uint32_t *result);
+int sm_ll_find_free_block(struct ll_disk *ll, dm_block_t begin,
+ dm_block_t end, dm_block_t *result);
+int sm_ll_insert(struct ll_disk *ll, dm_block_t b, uint32_t ref_count, enum allocation_event *ev);
+int sm_ll_inc(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev);
+int sm_ll_dec(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev);
+int sm_ll_commit(struct ll_disk *ll);
+
+int sm_ll_new_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm);
+int sm_ll_open_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm,
+ void *root_le, size_t len);
+
+int sm_ll_new_disk(struct ll_disk *ll, struct dm_transaction_manager *tm);
+int sm_ll_open_disk(struct ll_disk *ll, struct dm_transaction_manager *tm,
+ void *root_le, size_t len);
+
+/*----------------------------------------------------------------*/
+
+#endif /* DM_SPACE_MAP_COMMON_H */
diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c
new file mode 100644
index 000000000000..fc469ba9f627
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-disk.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-space-map-checker.h"
+#include "dm-space-map-common.h"
+#include "dm-space-map-disk.h"
+#include "dm-space-map.h"
+#include "dm-transaction-manager.h"
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "space map disk"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Space map interface.
+ */
+struct sm_disk {
+ struct dm_space_map sm;
+
+ struct ll_disk ll;
+ struct ll_disk old_ll;
+
+ dm_block_t begin;
+ dm_block_t nr_allocated_this_transaction;
+};
+
+static void sm_disk_destroy(struct dm_space_map *sm)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ kfree(smd);
+}
+
+static int sm_disk_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ return sm_ll_extend(&smd->ll, extra_blocks);
+}
+
+static int sm_disk_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+ *count = smd->old_ll.nr_blocks;
+
+ return 0;
+}
+
+static int sm_disk_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+ *count = (smd->old_ll.nr_blocks - smd->old_ll.nr_allocated) - smd->nr_allocated_this_transaction;
+
+ return 0;
+}
+
+static int sm_disk_get_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t *result)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+ return sm_ll_lookup(&smd->ll, b, result);
+}
+
+static int sm_disk_count_is_more_than_one(struct dm_space_map *sm, dm_block_t b,
+ int *result)
+{
+ int r;
+ uint32_t count;
+
+ r = sm_disk_get_count(sm, b, &count);
+ if (r)
+ return r;
+
+ return count > 1;
+}
+
+static int sm_disk_set_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t count)
+{
+ int r;
+ uint32_t old_count;
+ enum allocation_event ev;
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ r = sm_ll_insert(&smd->ll, b, count, &ev);
+ if (!r) {
+ switch (ev) {
+ case SM_NONE:
+ break;
+
+ case SM_ALLOC:
+ /*
+ * This _must_ be free in the prior transaction
+ * otherwise we've lost atomicity.
+ */
+ smd->nr_allocated_this_transaction++;
+ break;
+
+ case SM_FREE:
+ /*
+ * It's only free if it's also free in the last
+ * transaction.
+ */
+ r = sm_ll_lookup(&smd->old_ll, b, &old_count);
+ if (r)
+ return r;
+
+ if (!old_count)
+ smd->nr_allocated_this_transaction--;
+ break;
+ }
+ }
+
+ return r;
+}
+
+static int sm_disk_inc_block(struct dm_space_map *sm, dm_block_t b)
+{
+ int r;
+ enum allocation_event ev;
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ r = sm_ll_inc(&smd->ll, b, &ev);
+ if (!r && (ev == SM_ALLOC))
+ /*
+ * This _must_ be free in the prior transaction
+ * otherwise we've lost atomicity.
+ */
+ smd->nr_allocated_this_transaction++;
+
+ return r;
+}
+
+static int sm_disk_dec_block(struct dm_space_map *sm, dm_block_t b)
+{
+ int r;
+ uint32_t old_count;
+ enum allocation_event ev;
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ r = sm_ll_dec(&smd->ll, b, &ev);
+ if (!r && (ev == SM_FREE)) {
+ /*
+ * It's only free if it's also free in the last
+ * transaction.
+ */
+ r = sm_ll_lookup(&smd->old_ll, b, &old_count);
+ if (r)
+ return r;
+
+ if (!old_count)
+ smd->nr_allocated_this_transaction--;
+ }
+
+ return r;
+}
+
+static int sm_disk_new_block(struct dm_space_map *sm, dm_block_t *b)
+{
+ int r;
+ enum allocation_event ev;
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ /* FIXME: we should loop round a couple of times */
+ r = sm_ll_find_free_block(&smd->old_ll, smd->begin, smd->old_ll.nr_blocks, b);
+ if (r)
+ return r;
+
+ smd->begin = *b + 1;
+ r = sm_ll_inc(&smd->ll, *b, &ev);
+ if (!r) {
+ BUG_ON(ev != SM_ALLOC);
+ smd->nr_allocated_this_transaction++;
+ }
+
+ return r;
+}
+
+static int sm_disk_commit(struct dm_space_map *sm)
+{
+ int r;
+ dm_block_t nr_free;
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ r = sm_disk_get_nr_free(sm, &nr_free);
+ if (r)
+ return r;
+
+ r = sm_ll_commit(&smd->ll);
+ if (r)
+ return r;
+
+ memcpy(&smd->old_ll, &smd->ll, sizeof(smd->old_ll));
+ smd->begin = 0;
+ smd->nr_allocated_this_transaction = 0;
+
+ r = sm_disk_get_nr_free(sm, &nr_free);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int sm_disk_root_size(struct dm_space_map *sm, size_t *result)
+{
+ *result = sizeof(struct disk_sm_root);
+
+ return 0;
+}
+
+static int sm_disk_copy_root(struct dm_space_map *sm, void *where_le, size_t max)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+ struct disk_sm_root root_le;
+
+ root_le.nr_blocks = cpu_to_le64(smd->ll.nr_blocks);
+ root_le.nr_allocated = cpu_to_le64(smd->ll.nr_allocated);
+ root_le.bitmap_root = cpu_to_le64(smd->ll.bitmap_root);
+ root_le.ref_count_root = cpu_to_le64(smd->ll.ref_count_root);
+
+ if (max < sizeof(root_le))
+ return -ENOSPC;
+
+ memcpy(where_le, &root_le, sizeof(root_le));
+
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+
+static struct dm_space_map ops = {
+ .destroy = sm_disk_destroy,
+ .extend = sm_disk_extend,
+ .get_nr_blocks = sm_disk_get_nr_blocks,
+ .get_nr_free = sm_disk_get_nr_free,
+ .get_count = sm_disk_get_count,
+ .count_is_more_than_one = sm_disk_count_is_more_than_one,
+ .set_count = sm_disk_set_count,
+ .inc_block = sm_disk_inc_block,
+ .dec_block = sm_disk_dec_block,
+ .new_block = sm_disk_new_block,
+ .commit = sm_disk_commit,
+ .root_size = sm_disk_root_size,
+ .copy_root = sm_disk_copy_root
+};
+
+static struct dm_space_map *dm_sm_disk_create_real(
+ struct dm_transaction_manager *tm,
+ dm_block_t nr_blocks)
+{
+ int r;
+ struct sm_disk *smd;
+
+ smd = kmalloc(sizeof(*smd), GFP_KERNEL);
+ if (!smd)
+ return ERR_PTR(-ENOMEM);
+
+ smd->begin = 0;
+ smd->nr_allocated_this_transaction = 0;
+ memcpy(&smd->sm, &ops, sizeof(smd->sm));
+
+ r = sm_ll_new_disk(&smd->ll, tm);
+ if (r)
+ goto bad;
+
+ r = sm_ll_extend(&smd->ll, nr_blocks);
+ if (r)
+ goto bad;
+
+ r = sm_disk_commit(&smd->sm);
+ if (r)
+ goto bad;
+
+ return &smd->sm;
+
+bad:
+ kfree(smd);
+ return ERR_PTR(r);
+}
+
+struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
+ dm_block_t nr_blocks)
+{
+ struct dm_space_map *sm = dm_sm_disk_create_real(tm, nr_blocks);
+ return dm_sm_checker_create_fresh(sm);
+}
+EXPORT_SYMBOL_GPL(dm_sm_disk_create);
+
+static struct dm_space_map *dm_sm_disk_open_real(
+ struct dm_transaction_manager *tm,
+ void *root_le, size_t len)
+{
+ int r;
+ struct sm_disk *smd;
+
+ smd = kmalloc(sizeof(*smd), GFP_KERNEL);
+ if (!smd)
+ return ERR_PTR(-ENOMEM);
+
+ smd->begin = 0;
+ smd->nr_allocated_this_transaction = 0;
+ memcpy(&smd->sm, &ops, sizeof(smd->sm));
+
+ r = sm_ll_open_disk(&smd->ll, tm, root_le, len);
+ if (r)
+ goto bad;
+
+ r = sm_disk_commit(&smd->sm);
+ if (r)
+ goto bad;
+
+ return &smd->sm;
+
+bad:
+ kfree(smd);
+ return ERR_PTR(r);
+}
+
+struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm,
+ void *root_le, size_t len)
+{
+ return dm_sm_checker_create(
+ dm_sm_disk_open_real(tm, root_le, len));
+}
+EXPORT_SYMBOL_GPL(dm_sm_disk_open);
+
+/*----------------------------------------------------------------*/
diff --git a/drivers/md/persistent-data/dm-space-map-disk.h b/drivers/md/persistent-data/dm-space-map-disk.h
new file mode 100644
index 000000000000..447a0a9a2d9f
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-disk.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _LINUX_DM_SPACE_MAP_DISK_H
+#define _LINUX_DM_SPACE_MAP_DISK_H
+
+#include "dm-block-manager.h"
+
+struct dm_space_map;
+struct dm_transaction_manager;
+
+/*
+ * Unfortunately we have to use two-phase construction due to the cycle
+ * between the tm and sm.
+ */
+struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
+ dm_block_t nr_blocks);
+
+struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm,
+ void *root, size_t len);
+
+#endif /* _LINUX_DM_SPACE_MAP_DISK_H */
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c
new file mode 100644
index 000000000000..e89ae5e7a519
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-metadata.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-space-map.h"
+#include "dm-space-map-common.h"
+#include "dm-space-map-metadata.h"
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "space map metadata"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Space map interface.
+ *
+ * The low level disk format is written using the standard btree and
+ * transaction manager. This means that performing disk operations may
+ * cause us to recurse into the space map in order to allocate new blocks.
+ * For this reason we have a pool of pre-allocated blocks large enough to
+ * service any metadata_ll_disk operation.
+ */
+
+/*
+ * FIXME: we should calculate this based on the size of the device.
+ * Only the metadata space map needs this functionality.
+ */
+#define MAX_RECURSIVE_ALLOCATIONS 1024
+
+enum block_op_type {
+ BOP_INC,
+ BOP_DEC
+};
+
+struct block_op {
+ enum block_op_type type;
+ dm_block_t block;
+};
+
+struct sm_metadata {
+ struct dm_space_map sm;
+
+ struct ll_disk ll;
+ struct ll_disk old_ll;
+
+ dm_block_t begin;
+
+ unsigned recursion_count;
+ unsigned allocated_this_transaction;
+ unsigned nr_uncommitted;
+ struct block_op uncommitted[MAX_RECURSIVE_ALLOCATIONS];
+};
+
+static int add_bop(struct sm_metadata *smm, enum block_op_type type, dm_block_t b)
+{
+ struct block_op *op;
+
+ if (smm->nr_uncommitted == MAX_RECURSIVE_ALLOCATIONS) {
+ DMERR("too many recursive allocations");
+ return -ENOMEM;
+ }
+
+ op = smm->uncommitted + smm->nr_uncommitted++;
+ op->type = type;
+ op->block = b;
+
+ return 0;
+}
+
+static int commit_bop(struct sm_metadata *smm, struct block_op *op)
+{
+ int r = 0;
+ enum allocation_event ev;
+
+ switch (op->type) {
+ case BOP_INC:
+ r = sm_ll_inc(&smm->ll, op->block, &ev);
+ break;
+
+ case BOP_DEC:
+ r = sm_ll_dec(&smm->ll, op->block, &ev);
+ break;
+ }
+
+ return r;
+}
+
+static void in(struct sm_metadata *smm)
+{
+ smm->recursion_count++;
+}
+
+static int out(struct sm_metadata *smm)
+{
+ int r = 0;
+
+ /*
+ * If we're not recursing then very bad things are happening.
+ */
+ if (!smm->recursion_count) {
+ DMERR("lost track of recursion depth");
+ return -ENOMEM;
+ }
+
+ if (smm->recursion_count == 1 && smm->nr_uncommitted) {
+ while (smm->nr_uncommitted && !r) {
+ smm->nr_uncommitted--;
+ r = commit_bop(smm, smm->uncommitted +
+ smm->nr_uncommitted);
+ if (r)
+ break;
+ }
+ }
+
+ smm->recursion_count--;
+
+ return r;
+}
+
+/*
+ * When using the out() function above, we often want to combine an error
+ * code for the operation run in the recursive context with that from
+ * out().
+ */
+static int combine_errors(int r1, int r2)
+{
+ return r1 ? r1 : r2;
+}
+
+static int recursing(struct sm_metadata *smm)
+{
+ return smm->recursion_count;
+}
+
+static void sm_metadata_destroy(struct dm_space_map *sm)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ kfree(smm);
+}
+
+static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
+{
+ DMERR("doesn't support extend");
+ return -EINVAL;
+}
+
+static int sm_metadata_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ *count = smm->ll.nr_blocks;
+
+ return 0;
+}
+
+static int sm_metadata_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ *count = smm->old_ll.nr_blocks - smm->old_ll.nr_allocated -
+ smm->allocated_this_transaction;
+
+ return 0;
+}
+
+static int sm_metadata_get_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t *result)
+{
+ int r, i;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+ unsigned adjustment = 0;
+
+ /*
+ * We may have some uncommitted adjustments to add. This list
+ * should always be really short.
+ */
+ for (i = 0; i < smm->nr_uncommitted; i++) {
+ struct block_op *op = smm->uncommitted + i;
+
+ if (op->block != b)
+ continue;
+
+ switch (op->type) {
+ case BOP_INC:
+ adjustment++;
+ break;
+
+ case BOP_DEC:
+ adjustment--;
+ break;
+ }
+ }
+
+ r = sm_ll_lookup(&smm->ll, b, result);
+ if (r)
+ return r;
+
+ *result += adjustment;
+
+ return 0;
+}
+
+static int sm_metadata_count_is_more_than_one(struct dm_space_map *sm,
+ dm_block_t b, int *result)
+{
+ int r, i, adjustment = 0;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+ uint32_t rc;
+
+ /*
+ * We may have some uncommitted adjustments to add. This list
+ * should always be really short.
+ */
+ for (i = 0; i < smm->nr_uncommitted; i++) {
+ struct block_op *op = smm->uncommitted + i;
+
+ if (op->block != b)
+ continue;
+
+ switch (op->type) {
+ case BOP_INC:
+ adjustment++;
+ break;
+
+ case BOP_DEC:
+ adjustment--;
+ break;
+ }
+ }
+
+ if (adjustment > 1) {
+ *result = 1;
+ return 0;
+ }
+
+ r = sm_ll_lookup_bitmap(&smm->ll, b, &rc);
+ if (r)
+ return r;
+
+ if (rc == 3)
+ /*
+ * We err on the side of caution, and always return true.
+ */
+ *result = 1;
+ else
+ *result = rc + adjustment > 1;
+
+ return 0;
+}
+
+static int sm_metadata_set_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t count)
+{
+ int r, r2;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ if (smm->recursion_count) {
+ DMERR("cannot recurse set_count()");
+ return -EINVAL;
+ }
+
+ in(smm);
+ r = sm_ll_insert(&smm->ll, b, count, &ev);
+ r2 = out(smm);
+
+ return combine_errors(r, r2);
+}
+
+static int sm_metadata_inc_block(struct dm_space_map *sm, dm_block_t b)
+{
+ int r, r2 = 0;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ if (recursing(smm))
+ r = add_bop(smm, BOP_INC, b);
+ else {
+ in(smm);
+ r = sm_ll_inc(&smm->ll, b, &ev);
+ r2 = out(smm);
+ }
+
+ return combine_errors(r, r2);
+}
+
+static int sm_metadata_dec_block(struct dm_space_map *sm, dm_block_t b)
+{
+ int r, r2 = 0;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ if (recursing(smm))
+ r = add_bop(smm, BOP_DEC, b);
+ else {
+ in(smm);
+ r = sm_ll_dec(&smm->ll, b, &ev);
+ r2 = out(smm);
+ }
+
+ return combine_errors(r, r2);
+}
+
+static int sm_metadata_new_block_(struct dm_space_map *sm, dm_block_t *b)
+{
+ int r, r2 = 0;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ r = sm_ll_find_free_block(&smm->old_ll, smm->begin, smm->old_ll.nr_blocks, b);
+ if (r)
+ return r;
+
+ smm->begin = *b + 1;
+
+ if (recursing(smm))
+ r = add_bop(smm, BOP_INC, *b);
+ else {
+ in(smm);
+ r = sm_ll_inc(&smm->ll, *b, &ev);
+ r2 = out(smm);
+ }
+
+ if (!r)
+ smm->allocated_this_transaction++;
+
+ return combine_errors(r, r2);
+}
+
+static int sm_metadata_new_block(struct dm_space_map *sm, dm_block_t *b)
+{
+ int r = sm_metadata_new_block_(sm, b);
+ if (r)
+ DMERR("out of metadata space");
+ return r;
+}
+
+static int sm_metadata_commit(struct dm_space_map *sm)
+{
+ int r;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ r = sm_ll_commit(&smm->ll);
+ if (r)
+ return r;
+
+ memcpy(&smm->old_ll, &smm->ll, sizeof(smm->old_ll));
+ smm->begin = 0;
+ smm->allocated_this_transaction = 0;
+
+ return 0;
+}
+
+static int sm_metadata_root_size(struct dm_space_map *sm, size_t *result)
+{
+ *result = sizeof(struct disk_sm_root);
+
+ return 0;
+}
+
+static int sm_metadata_copy_root(struct dm_space_map *sm, void *where_le, size_t max)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+ struct disk_sm_root root_le;
+
+ root_le.nr_blocks = cpu_to_le64(smm->ll.nr_blocks);
+ root_le.nr_allocated = cpu_to_le64(smm->ll.nr_allocated);
+ root_le.bitmap_root = cpu_to_le64(smm->ll.bitmap_root);
+ root_le.ref_count_root = cpu_to_le64(smm->ll.ref_count_root);
+
+ if (max < sizeof(root_le))
+ return -ENOSPC;
+
+ memcpy(where_le, &root_le, sizeof(root_le));
+
+ return 0;
+}
+
+static struct dm_space_map ops = {
+ .destroy = sm_metadata_destroy,
+ .extend = sm_metadata_extend,
+ .get_nr_blocks = sm_metadata_get_nr_blocks,
+ .get_nr_free = sm_metadata_get_nr_free,
+ .get_count = sm_metadata_get_count,
+ .count_is_more_than_one = sm_metadata_count_is_more_than_one,
+ .set_count = sm_metadata_set_count,
+ .inc_block = sm_metadata_inc_block,
+ .dec_block = sm_metadata_dec_block,
+ .new_block = sm_metadata_new_block,
+ .commit = sm_metadata_commit,
+ .root_size = sm_metadata_root_size,
+ .copy_root = sm_metadata_copy_root
+};
+
+/*----------------------------------------------------------------*/
+
+/*
+ * When a new space map is created that manages its own space. We use
+ * this tiny bootstrap allocator.
+ */
+static void sm_bootstrap_destroy(struct dm_space_map *sm)
+{
+}
+
+static int sm_bootstrap_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
+{
+ DMERR("boostrap doesn't support extend");
+
+ return -EINVAL;
+}
+
+static int sm_bootstrap_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ return smm->ll.nr_blocks;
+}
+
+static int sm_bootstrap_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ *count = smm->ll.nr_blocks - smm->begin;
+
+ return 0;
+}
+
+static int sm_bootstrap_get_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t *result)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ return b < smm->begin ? 1 : 0;
+}
+
+static int sm_bootstrap_count_is_more_than_one(struct dm_space_map *sm,
+ dm_block_t b, int *result)
+{
+ *result = 0;
+
+ return 0;
+}
+
+static int sm_bootstrap_set_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t count)
+{
+ DMERR("boostrap doesn't support set_count");
+
+ return -EINVAL;
+}
+
+static int sm_bootstrap_new_block(struct dm_space_map *sm, dm_block_t *b)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ /*
+ * We know the entire device is unused.
+ */
+ if (smm->begin == smm->ll.nr_blocks)
+ return -ENOSPC;
+
+ *b = smm->begin++;
+
+ return 0;
+}
+
+static int sm_bootstrap_inc_block(struct dm_space_map *sm, dm_block_t b)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ return add_bop(smm, BOP_INC, b);
+}
+
+static int sm_bootstrap_dec_block(struct dm_space_map *sm, dm_block_t b)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ return add_bop(smm, BOP_DEC, b);
+}
+
+static int sm_bootstrap_commit(struct dm_space_map *sm)
+{
+ return 0;
+}
+
+static int sm_bootstrap_root_size(struct dm_space_map *sm, size_t *result)
+{
+ DMERR("boostrap doesn't support root_size");
+
+ return -EINVAL;
+}
+
+static int sm_bootstrap_copy_root(struct dm_space_map *sm, void *where,
+ size_t max)
+{
+ DMERR("boostrap doesn't support copy_root");
+
+ return -EINVAL;
+}
+
+static struct dm_space_map bootstrap_ops = {
+ .destroy = sm_bootstrap_destroy,
+ .extend = sm_bootstrap_extend,
+ .get_nr_blocks = sm_bootstrap_get_nr_blocks,
+ .get_nr_free = sm_bootstrap_get_nr_free,
+ .get_count = sm_bootstrap_get_count,
+ .count_is_more_than_one = sm_bootstrap_count_is_more_than_one,
+ .set_count = sm_bootstrap_set_count,
+ .inc_block = sm_bootstrap_inc_block,
+ .dec_block = sm_bootstrap_dec_block,
+ .new_block = sm_bootstrap_new_block,
+ .commit = sm_bootstrap_commit,
+ .root_size = sm_bootstrap_root_size,
+ .copy_root = sm_bootstrap_copy_root
+};
+
+/*----------------------------------------------------------------*/
+
+struct dm_space_map *dm_sm_metadata_init(void)
+{
+ struct sm_metadata *smm;
+
+ smm = kmalloc(sizeof(*smm), GFP_KERNEL);
+ if (!smm)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(&smm->sm, &ops, sizeof(smm->sm));
+
+ return &smm->sm;
+}
+
+int dm_sm_metadata_create(struct dm_space_map *sm,
+ struct dm_transaction_manager *tm,
+ dm_block_t nr_blocks,
+ dm_block_t superblock)
+{
+ int r;
+ dm_block_t i;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ smm->begin = superblock + 1;
+ smm->recursion_count = 0;
+ smm->allocated_this_transaction = 0;
+ smm->nr_uncommitted = 0;
+
+ memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm));
+
+ r = sm_ll_new_metadata(&smm->ll, tm);
+ if (r)
+ return r;
+
+ r = sm_ll_extend(&smm->ll, nr_blocks);
+ if (r)
+ return r;
+
+ memcpy(&smm->sm, &ops, sizeof(smm->sm));
+
+ /*
+ * Now we need to update the newly created data structures with the
+ * allocated blocks that they were built from.
+ */
+ for (i = superblock; !r && i < smm->begin; i++)
+ r = sm_ll_inc(&smm->ll, i, &ev);
+
+ if (r)
+ return r;
+
+ return sm_metadata_commit(sm);
+}
+
+int dm_sm_metadata_open(struct dm_space_map *sm,
+ struct dm_transaction_manager *tm,
+ void *root_le, size_t len)
+{
+ int r;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ r = sm_ll_open_metadata(&smm->ll, tm, root_le, len);
+ if (r)
+ return r;
+
+ smm->begin = 0;
+ smm->recursion_count = 0;
+ smm->allocated_this_transaction = 0;
+ smm->nr_uncommitted = 0;
+
+ memcpy(&smm->old_ll, &smm->ll, sizeof(smm->old_ll));
+ return 0;
+}
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.h b/drivers/md/persistent-data/dm-space-map-metadata.h
new file mode 100644
index 000000000000..39bba0801cf2
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-metadata.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_SPACE_MAP_METADATA_H
+#define DM_SPACE_MAP_METADATA_H
+
+#include "dm-transaction-manager.h"
+
+/*
+ * Unfortunately we have to use two-phase construction due to the cycle
+ * between the tm and sm.
+ */
+struct dm_space_map *dm_sm_metadata_init(void);
+
+/*
+ * Create a fresh space map.
+ */
+int dm_sm_metadata_create(struct dm_space_map *sm,
+ struct dm_transaction_manager *tm,
+ dm_block_t nr_blocks,
+ dm_block_t superblock);
+
+/*
+ * Open from a previously-recorded root.
+ */
+int dm_sm_metadata_open(struct dm_space_map *sm,
+ struct dm_transaction_manager *tm,
+ void *root_le, size_t len);
+
+#endif /* DM_SPACE_MAP_METADATA_H */
diff --git a/drivers/md/persistent-data/dm-space-map.h b/drivers/md/persistent-data/dm-space-map.h
new file mode 100644
index 000000000000..1cbfc6b1638a
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _LINUX_DM_SPACE_MAP_H
+#define _LINUX_DM_SPACE_MAP_H
+
+#include "dm-block-manager.h"
+
+/*
+ * struct dm_space_map keeps a record of how many times each block in a device
+ * is referenced. It needs to be fixed on disk as part of the transaction.
+ */
+struct dm_space_map {
+ void (*destroy)(struct dm_space_map *sm);
+
+ /*
+ * You must commit before allocating the newly added space.
+ */
+ int (*extend)(struct dm_space_map *sm, dm_block_t extra_blocks);
+
+ /*
+ * Extensions do not appear in this count until after commit has
+ * been called.
+ */
+ int (*get_nr_blocks)(struct dm_space_map *sm, dm_block_t *count);
+
+ /*
+ * Space maps must never allocate a block from the previous
+ * transaction, in case we need to rollback. This complicates the
+ * semantics of get_nr_free(), it should return the number of blocks
+ * that are available for allocation _now_. For instance you may
+ * have blocks with a zero reference count that will not be
+ * available for allocation until after the next commit.
+ */
+ int (*get_nr_free)(struct dm_space_map *sm, dm_block_t *count);
+
+ int (*get_count)(struct dm_space_map *sm, dm_block_t b, uint32_t *result);
+ int (*count_is_more_than_one)(struct dm_space_map *sm, dm_block_t b,
+ int *result);
+ int (*set_count)(struct dm_space_map *sm, dm_block_t b, uint32_t count);
+
+ int (*commit)(struct dm_space_map *sm);
+
+ int (*inc_block)(struct dm_space_map *sm, dm_block_t b);
+ int (*dec_block)(struct dm_space_map *sm, dm_block_t b);
+
+ /*
+ * new_block will increment the returned block.
+ */
+ int (*new_block)(struct dm_space_map *sm, dm_block_t *b);
+
+ /*
+ * The root contains all the information needed to fix the space map.
+ * Generally this info is small, so squirrel it away in a disk block
+ * along with other info.
+ */
+ int (*root_size)(struct dm_space_map *sm, size_t *result);
+ int (*copy_root)(struct dm_space_map *sm, void *copy_to_here_le, size_t len);
+};
+
+/*----------------------------------------------------------------*/
+
+static inline void dm_sm_destroy(struct dm_space_map *sm)
+{
+ sm->destroy(sm);
+}
+
+static inline int dm_sm_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
+{
+ return sm->extend(sm, extra_blocks);
+}
+
+static inline int dm_sm_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
+{
+ return sm->get_nr_blocks(sm, count);
+}
+
+static inline int dm_sm_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
+{
+ return sm->get_nr_free(sm, count);
+}
+
+static inline int dm_sm_get_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t *result)
+{
+ return sm->get_count(sm, b, result);
+}
+
+static inline int dm_sm_count_is_more_than_one(struct dm_space_map *sm,
+ dm_block_t b, int *result)
+{
+ return sm->count_is_more_than_one(sm, b, result);
+}
+
+static inline int dm_sm_set_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t count)
+{
+ return sm->set_count(sm, b, count);
+}
+
+static inline int dm_sm_commit(struct dm_space_map *sm)
+{
+ return sm->commit(sm);
+}
+
+static inline int dm_sm_inc_block(struct dm_space_map *sm, dm_block_t b)
+{
+ return sm->inc_block(sm, b);
+}
+
+static inline int dm_sm_dec_block(struct dm_space_map *sm, dm_block_t b)
+{
+ return sm->dec_block(sm, b);
+}
+
+static inline int dm_sm_new_block(struct dm_space_map *sm, dm_block_t *b)
+{
+ return sm->new_block(sm, b);
+}
+
+static inline int dm_sm_root_size(struct dm_space_map *sm, size_t *result)
+{
+ return sm->root_size(sm, result);
+}
+
+static inline int dm_sm_copy_root(struct dm_space_map *sm, void *copy_to_here_le, size_t len)
+{
+ return sm->copy_root(sm, copy_to_here_le, len);
+}
+
+#endif /* _LINUX_DM_SPACE_MAP_H */
diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c
new file mode 100644
index 000000000000..6f8d38747d7f
--- /dev/null
+++ b/drivers/md/persistent-data/dm-transaction-manager.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+#include "dm-transaction-manager.h"
+#include "dm-space-map.h"
+#include "dm-space-map-checker.h"
+#include "dm-space-map-disk.h"
+#include "dm-space-map-metadata.h"
+#include "dm-persistent-data-internal.h"
+
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "transaction manager"
+
+/*----------------------------------------------------------------*/
+
+struct shadow_info {
+ struct hlist_node hlist;
+ dm_block_t where;
+};
+
+/*
+ * It would be nice if we scaled with the size of transaction.
+ */
+#define HASH_SIZE 256
+#define HASH_MASK (HASH_SIZE - 1)
+
+struct dm_transaction_manager {
+ int is_clone;
+ struct dm_transaction_manager *real;
+
+ struct dm_block_manager *bm;
+ struct dm_space_map *sm;
+
+ spinlock_t lock;
+ struct hlist_head buckets[HASH_SIZE];
+};
+
+/*----------------------------------------------------------------*/
+
+static int is_shadow(struct dm_transaction_manager *tm, dm_block_t b)
+{
+ int r = 0;
+ unsigned bucket = dm_hash_block(b, HASH_MASK);
+ struct shadow_info *si;
+ struct hlist_node *n;
+
+ spin_lock(&tm->lock);
+ hlist_for_each_entry(si, n, tm->buckets + bucket, hlist)
+ if (si->where == b) {
+ r = 1;
+ break;
+ }
+ spin_unlock(&tm->lock);
+
+ return r;
+}
+
+/*
+ * This can silently fail if there's no memory. We're ok with this since
+ * creating redundant shadows causes no harm.
+ */
+static void insert_shadow(struct dm_transaction_manager *tm, dm_block_t b)
+{
+ unsigned bucket;
+ struct shadow_info *si;
+
+ si = kmalloc(sizeof(*si), GFP_NOIO);
+ if (si) {
+ si->where = b;
+ bucket = dm_hash_block(b, HASH_MASK);
+ spin_lock(&tm->lock);
+ hlist_add_head(&si->hlist, tm->buckets + bucket);
+ spin_unlock(&tm->lock);
+ }
+}
+
+static void wipe_shadow_table(struct dm_transaction_manager *tm)
+{
+ struct shadow_info *si;
+ struct hlist_node *n, *tmp;
+ struct hlist_head *bucket;
+ int i;
+
+ spin_lock(&tm->lock);
+ for (i = 0; i < HASH_SIZE; i++) {
+ bucket = tm->buckets + i;
+ hlist_for_each_entry_safe(si, n, tmp, bucket, hlist)
+ kfree(si);
+
+ INIT_HLIST_HEAD(bucket);
+ }
+
+ spin_unlock(&tm->lock);
+}
+
+/*----------------------------------------------------------------*/
+
+static struct dm_transaction_manager *dm_tm_create(struct dm_block_manager *bm,
+ struct dm_space_map *sm)
+{
+ int i;
+ struct dm_transaction_manager *tm;
+
+ tm = kmalloc(sizeof(*tm), GFP_KERNEL);
+ if (!tm)
+ return ERR_PTR(-ENOMEM);
+
+ tm->is_clone = 0;
+ tm->real = NULL;
+ tm->bm = bm;
+ tm->sm = sm;
+
+ spin_lock_init(&tm->lock);
+ for (i = 0; i < HASH_SIZE; i++)
+ INIT_HLIST_HEAD(tm->buckets + i);
+
+ return tm;
+}
+
+struct dm_transaction_manager *dm_tm_create_non_blocking_clone(struct dm_transaction_manager *real)
+{
+ struct dm_transaction_manager *tm;
+
+ tm = kmalloc(sizeof(*tm), GFP_KERNEL);
+ if (tm) {
+ tm->is_clone = 1;
+ tm->real = real;
+ }
+
+ return tm;
+}
+EXPORT_SYMBOL_GPL(dm_tm_create_non_blocking_clone);
+
+void dm_tm_destroy(struct dm_transaction_manager *tm)
+{
+ kfree(tm);
+}
+EXPORT_SYMBOL_GPL(dm_tm_destroy);
+
+int dm_tm_pre_commit(struct dm_transaction_manager *tm)
+{
+ int r;
+
+ if (tm->is_clone)
+ return -EWOULDBLOCK;
+
+ r = dm_sm_commit(tm->sm);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dm_tm_pre_commit);
+
+int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root)
+{
+ if (tm->is_clone)
+ return -EWOULDBLOCK;
+
+ wipe_shadow_table(tm);
+
+ return dm_bm_flush_and_unlock(tm->bm, root);
+}
+EXPORT_SYMBOL_GPL(dm_tm_commit);
+
+int dm_tm_new_block(struct dm_transaction_manager *tm,
+ struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ int r;
+ dm_block_t new_block;
+
+ if (tm->is_clone)
+ return -EWOULDBLOCK;
+
+ r = dm_sm_new_block(tm->sm, &new_block);
+ if (r < 0)
+ return r;
+
+ r = dm_bm_write_lock_zero(tm->bm, new_block, v, result);
+ if (r < 0) {
+ dm_sm_dec_block(tm->sm, new_block);
+ return r;
+ }
+
+ /*
+ * New blocks count as shadows in that they don't need to be
+ * shadowed again.
+ */
+ insert_shadow(tm, new_block);
+
+ return 0;
+}
+
+static int __shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
+ struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ int r;
+ dm_block_t new;
+ struct dm_block *orig_block;
+
+ r = dm_sm_new_block(tm->sm, &new);
+ if (r < 0)
+ return r;
+
+ r = dm_sm_dec_block(tm->sm, orig);
+ if (r < 0)
+ return r;
+
+ r = dm_bm_read_lock(tm->bm, orig, v, &orig_block);
+ if (r < 0)
+ return r;
+
+ r = dm_bm_unlock_move(orig_block, new);
+ if (r < 0) {
+ dm_bm_unlock(orig_block);
+ return r;
+ }
+
+ return dm_bm_write_lock(tm->bm, new, v, result);
+}
+
+int dm_tm_shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
+ struct dm_block_validator *v, struct dm_block **result,
+ int *inc_children)
+{
+ int r;
+
+ if (tm->is_clone)
+ return -EWOULDBLOCK;
+
+ r = dm_sm_count_is_more_than_one(tm->sm, orig, inc_children);
+ if (r < 0)
+ return r;
+
+ if (is_shadow(tm, orig) && !*inc_children)
+ return dm_bm_write_lock(tm->bm, orig, v, result);
+
+ r = __shadow_block(tm, orig, v, result);
+ if (r < 0)
+ return r;
+ insert_shadow(tm, dm_block_location(*result));
+
+ return r;
+}
+
+int dm_tm_read_lock(struct dm_transaction_manager *tm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **blk)
+{
+ if (tm->is_clone)
+ return dm_bm_read_try_lock(tm->real->bm, b, v, blk);
+
+ return dm_bm_read_lock(tm->bm, b, v, blk);
+}
+
+int dm_tm_unlock(struct dm_transaction_manager *tm, struct dm_block *b)
+{
+ return dm_bm_unlock(b);
+}
+EXPORT_SYMBOL_GPL(dm_tm_unlock);
+
+void dm_tm_inc(struct dm_transaction_manager *tm, dm_block_t b)
+{
+ /*
+ * The non-blocking clone doesn't support this.
+ */
+ BUG_ON(tm->is_clone);
+
+ dm_sm_inc_block(tm->sm, b);
+}
+EXPORT_SYMBOL_GPL(dm_tm_inc);
+
+void dm_tm_dec(struct dm_transaction_manager *tm, dm_block_t b)
+{
+ /*
+ * The non-blocking clone doesn't support this.
+ */
+ BUG_ON(tm->is_clone);
+
+ dm_sm_dec_block(tm->sm, b);
+}
+EXPORT_SYMBOL_GPL(dm_tm_dec);
+
+int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b,
+ uint32_t *result)
+{
+ if (tm->is_clone)
+ return -EWOULDBLOCK;
+
+ return dm_sm_get_count(tm->sm, b, result);
+}
+
+struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm)
+{
+ return tm->bm;
+}
+
+/*----------------------------------------------------------------*/
+
+static int dm_tm_create_internal(struct dm_block_manager *bm,
+ dm_block_t sb_location,
+ struct dm_block_validator *sb_validator,
+ size_t root_offset, size_t root_max_len,
+ struct dm_transaction_manager **tm,
+ struct dm_space_map **sm,
+ struct dm_block **sblock,
+ int create)
+{
+ int r;
+ struct dm_space_map *inner;
+
+ inner = dm_sm_metadata_init();
+ if (IS_ERR(inner))
+ return PTR_ERR(inner);
+
+ *tm = dm_tm_create(bm, inner);
+ if (IS_ERR(*tm)) {
+ dm_sm_destroy(inner);
+ return PTR_ERR(*tm);
+ }
+
+ if (create) {
+ r = dm_bm_write_lock_zero(dm_tm_get_bm(*tm), sb_location,
+ sb_validator, sblock);
+ if (r < 0) {
+ DMERR("couldn't lock superblock");
+ goto bad1;
+ }
+
+ r = dm_sm_metadata_create(inner, *tm, dm_bm_nr_blocks(bm),
+ sb_location);
+ if (r) {
+ DMERR("couldn't create metadata space map");
+ goto bad2;
+ }
+
+ *sm = dm_sm_checker_create(inner);
+ if (!*sm)
+ goto bad2;
+
+ } else {
+ r = dm_bm_write_lock(dm_tm_get_bm(*tm), sb_location,
+ sb_validator, sblock);
+ if (r < 0) {
+ DMERR("couldn't lock superblock");
+ goto bad1;
+ }
+
+ r = dm_sm_metadata_open(inner, *tm,
+ dm_block_data(*sblock) + root_offset,
+ root_max_len);
+ if (r) {
+ DMERR("couldn't open metadata space map");
+ goto bad2;
+ }
+
+ *sm = dm_sm_checker_create(inner);
+ if (!*sm)
+ goto bad2;
+ }
+
+ return 0;
+
+bad2:
+ dm_tm_unlock(*tm, *sblock);
+bad1:
+ dm_tm_destroy(*tm);
+ dm_sm_destroy(inner);
+ return r;
+}
+
+int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
+ struct dm_block_validator *sb_validator,
+ struct dm_transaction_manager **tm,
+ struct dm_space_map **sm, struct dm_block **sblock)
+{
+ return dm_tm_create_internal(bm, sb_location, sb_validator,
+ 0, 0, tm, sm, sblock, 1);
+}
+EXPORT_SYMBOL_GPL(dm_tm_create_with_sm);
+
+int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
+ struct dm_block_validator *sb_validator,
+ size_t root_offset, size_t root_max_len,
+ struct dm_transaction_manager **tm,
+ struct dm_space_map **sm, struct dm_block **sblock)
+{
+ return dm_tm_create_internal(bm, sb_location, sb_validator, root_offset,
+ root_max_len, tm, sm, sblock, 0);
+}
+EXPORT_SYMBOL_GPL(dm_tm_open_with_sm);
+
+/*----------------------------------------------------------------*/
diff --git a/drivers/md/persistent-data/dm-transaction-manager.h b/drivers/md/persistent-data/dm-transaction-manager.h
new file mode 100644
index 000000000000..6da784871db4
--- /dev/null
+++ b/drivers/md/persistent-data/dm-transaction-manager.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _LINUX_DM_TRANSACTION_MANAGER_H
+#define _LINUX_DM_TRANSACTION_MANAGER_H
+
+#include "dm-block-manager.h"
+
+struct dm_transaction_manager;
+struct dm_space_map;
+
+/*----------------------------------------------------------------*/
+
+/*
+ * This manages the scope of a transaction. It also enforces immutability
+ * of the on-disk data structures by limiting access to writeable blocks.
+ *
+ * Clients should not fiddle with the block manager directly.
+ */
+
+void dm_tm_destroy(struct dm_transaction_manager *tm);
+
+/*
+ * The non-blocking version of a transaction manager is intended for use in
+ * fast path code that needs to do lookups e.g. a dm mapping function.
+ * You create the non-blocking variant from a normal tm. The interface is
+ * the same, except that most functions will just return -EWOULDBLOCK.
+ * Methods that return void yet may block should not be called on a clone
+ * viz. dm_tm_inc, dm_tm_dec. Call dm_tm_destroy() as you would with a normal
+ * tm when you've finished with it. You may not destroy the original prior
+ * to clones.
+ */
+struct dm_transaction_manager *dm_tm_create_non_blocking_clone(struct dm_transaction_manager *real);
+
+/*
+ * We use a 2-phase commit here.
+ *
+ * i) In the first phase the block manager is told to start flushing, and
+ * the changes to the space map are written to disk. You should interrogate
+ * your particular space map to get detail of its root node etc. to be
+ * included in your superblock.
+ *
+ * ii) @root will be committed last. You shouldn't use more than the
+ * first 512 bytes of @root if you wish the transaction to survive a power
+ * failure. You *must* have a write lock held on @root for both stage (i)
+ * and (ii). The commit will drop the write lock.
+ */
+int dm_tm_pre_commit(struct dm_transaction_manager *tm);
+int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root);
+
+/*
+ * These methods are the only way to get hold of a writeable block.
+ */
+
+/*
+ * dm_tm_new_block() is pretty self-explanatory. Make sure you do actually
+ * write to the whole of @data before you unlock, otherwise you could get
+ * a data leak. (The other option is for tm_new_block() to zero new blocks
+ * before handing them out, which will be redundant in most, if not all,
+ * cases).
+ * Zeroes the new block and returns with write lock held.
+ */
+int dm_tm_new_block(struct dm_transaction_manager *tm,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+/*
+ * dm_tm_shadow_block() allocates a new block and copies the data from @orig
+ * to it. It then decrements the reference count on original block. Use
+ * this to update the contents of a block in a data structure, don't
+ * confuse this with a clone - you shouldn't access the orig block after
+ * this operation. Because the tm knows the scope of the transaction it
+ * can optimise requests for a shadow of a shadow to a no-op. Don't forget
+ * to unlock when you've finished with the shadow.
+ *
+ * The @inc_children flag is used to tell the caller whether it needs to
+ * adjust reference counts for children. (Data in the block may refer to
+ * other blocks.)
+ *
+ * Shadowing implicitly drops a reference on @orig so you must not have
+ * it locked when you call this.
+ */
+int dm_tm_shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
+ struct dm_block_validator *v,
+ struct dm_block **result, int *inc_children);
+
+/*
+ * Read access. You can lock any block you want. If there's a write lock
+ * on it outstanding then it'll block.
+ */
+int dm_tm_read_lock(struct dm_transaction_manager *tm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+int dm_tm_unlock(struct dm_transaction_manager *tm, struct dm_block *b);
+
+/*
+ * Functions for altering the reference count of a block directly.
+ */
+void dm_tm_inc(struct dm_transaction_manager *tm, dm_block_t b);
+
+void dm_tm_dec(struct dm_transaction_manager *tm, dm_block_t b);
+
+int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b,
+ uint32_t *result);
+
+struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm);
+
+/*
+ * A little utility that ties the knot by producing a transaction manager
+ * that has a space map managed by the transaction manager...
+ *
+ * Returns a tm that has an open transaction to write the new disk sm.
+ * Caller should store the new sm root and commit.
+ */
+int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
+ struct dm_block_validator *sb_validator,
+ struct dm_transaction_manager **tm,
+ struct dm_space_map **sm, struct dm_block **sblock);
+
+int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
+ struct dm_block_validator *sb_validator,
+ size_t root_offset, size_t root_max_len,
+ struct dm_transaction_manager **tm,
+ struct dm_space_map **sm, struct dm_block **sblock);
+
+#endif /* _LINUX_DM_TRANSACTION_MANAGER_H */
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index e86bf3682e1e..7294bd115e34 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -20,6 +20,7 @@
#include <linux/blkdev.h>
#include <linux/seq_file.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include "md.h"
#include "raid0.h"
@@ -27,9 +28,9 @@
static int raid0_congested(void *data, int bits)
{
- mddev_t *mddev = data;
- raid0_conf_t *conf = mddev->private;
- mdk_rdev_t **devlist = conf->devlist;
+ struct mddev *mddev = data;
+ struct r0conf *conf = mddev->private;
+ struct md_rdev **devlist = conf->devlist;
int raid_disks = conf->strip_zone[0].nb_dev;
int i, ret = 0;
@@ -47,52 +48,53 @@ static int raid0_congested(void *data, int bits)
/*
* inform the user of the raid configuration
*/
-static void dump_zones(mddev_t *mddev)
+static void dump_zones(struct mddev *mddev)
{
- int j, k, h;
+ int j, k;
sector_t zone_size = 0;
sector_t zone_start = 0;
char b[BDEVNAME_SIZE];
- raid0_conf_t *conf = mddev->private;
+ struct r0conf *conf = mddev->private;
int raid_disks = conf->strip_zone[0].nb_dev;
- printk(KERN_INFO "******* %s configuration *********\n",
- mdname(mddev));
- h = 0;
+ printk(KERN_INFO "md: RAID0 configuration for %s - %d zone%s\n",
+ mdname(mddev),
+ conf->nr_strip_zones, conf->nr_strip_zones==1?"":"s");
for (j = 0; j < conf->nr_strip_zones; j++) {
- printk(KERN_INFO "zone%d=[", j);
+ printk(KERN_INFO "md: zone%d=[", j);
for (k = 0; k < conf->strip_zone[j].nb_dev; k++)
- printk(KERN_CONT "%s/",
+ printk(KERN_CONT "%s%s", k?"/":"",
bdevname(conf->devlist[j*raid_disks
+ k]->bdev, b));
printk(KERN_CONT "]\n");
zone_size = conf->strip_zone[j].zone_end - zone_start;
- printk(KERN_INFO " zone offset=%llukb "
- "device offset=%llukb size=%llukb\n",
+ printk(KERN_INFO " zone-offset=%10lluKB, "
+ "device-offset=%10lluKB, size=%10lluKB\n",
(unsigned long long)zone_start>>1,
(unsigned long long)conf->strip_zone[j].dev_start>>1,
(unsigned long long)zone_size>>1);
zone_start = conf->strip_zone[j].zone_end;
}
- printk(KERN_INFO "**********************************\n\n");
+ printk(KERN_INFO "\n");
}
-static int create_strip_zones(mddev_t *mddev, raid0_conf_t **private_conf)
+static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
{
int i, c, err;
sector_t curr_zone_end, sectors;
- mdk_rdev_t *smallest, *rdev1, *rdev2, *rdev, **dev;
+ struct md_rdev *smallest, *rdev1, *rdev2, *rdev, **dev;
struct strip_zone *zone;
int cnt;
char b[BDEVNAME_SIZE];
- raid0_conf_t *conf = kzalloc(sizeof(*conf), GFP_KERNEL);
+ char b2[BDEVNAME_SIZE];
+ struct r0conf *conf = kzalloc(sizeof(*conf), GFP_KERNEL);
if (!conf)
return -ENOMEM;
list_for_each_entry(rdev1, &mddev->disks, same_set) {
- printk(KERN_INFO "md/raid0:%s: looking at %s\n",
- mdname(mddev),
- bdevname(rdev1->bdev, b));
+ pr_debug("md/raid0:%s: looking at %s\n",
+ mdname(mddev),
+ bdevname(rdev1->bdev, b));
c = 0;
/* round size to chunk_size */
@@ -101,16 +103,16 @@ static int create_strip_zones(mddev_t *mddev, raid0_conf_t **private_conf)
rdev1->sectors = sectors * mddev->chunk_sectors;
list_for_each_entry(rdev2, &mddev->disks, same_set) {
- printk(KERN_INFO "md/raid0:%s: comparing %s(%llu)",
- mdname(mddev),
- bdevname(rdev1->bdev,b),
- (unsigned long long)rdev1->sectors);
- printk(KERN_CONT " with %s(%llu)\n",
- bdevname(rdev2->bdev,b),
- (unsigned long long)rdev2->sectors);
+ pr_debug("md/raid0:%s: comparing %s(%llu)"
+ " with %s(%llu)\n",
+ mdname(mddev),
+ bdevname(rdev1->bdev,b),
+ (unsigned long long)rdev1->sectors,
+ bdevname(rdev2->bdev,b2),
+ (unsigned long long)rdev2->sectors);
if (rdev2 == rdev1) {
- printk(KERN_INFO "md/raid0:%s: END\n",
- mdname(mddev));
+ pr_debug("md/raid0:%s: END\n",
+ mdname(mddev));
break;
}
if (rdev2->sectors == rdev1->sectors) {
@@ -118,30 +120,30 @@ static int create_strip_zones(mddev_t *mddev, raid0_conf_t **private_conf)
* Not unique, don't count it as a new
* group
*/
- printk(KERN_INFO "md/raid0:%s: EQUAL\n",
- mdname(mddev));
+ pr_debug("md/raid0:%s: EQUAL\n",
+ mdname(mddev));
c = 1;
break;
}
- printk(KERN_INFO "md/raid0:%s: NOT EQUAL\n",
- mdname(mddev));
+ pr_debug("md/raid0:%s: NOT EQUAL\n",
+ mdname(mddev));
}
if (!c) {
- printk(KERN_INFO "md/raid0:%s: ==> UNIQUE\n",
- mdname(mddev));
+ pr_debug("md/raid0:%s: ==> UNIQUE\n",
+ mdname(mddev));
conf->nr_strip_zones++;
- printk(KERN_INFO "md/raid0:%s: %d zones\n",
- mdname(mddev), conf->nr_strip_zones);
+ pr_debug("md/raid0:%s: %d zones\n",
+ mdname(mddev), conf->nr_strip_zones);
}
}
- printk(KERN_INFO "md/raid0:%s: FINAL %d zones\n",
- mdname(mddev), conf->nr_strip_zones);
+ pr_debug("md/raid0:%s: FINAL %d zones\n",
+ mdname(mddev), conf->nr_strip_zones);
err = -ENOMEM;
conf->strip_zone = kzalloc(sizeof(struct strip_zone)*
conf->nr_strip_zones, GFP_KERNEL);
if (!conf->strip_zone)
goto abort;
- conf->devlist = kzalloc(sizeof(mdk_rdev_t*)*
+ conf->devlist = kzalloc(sizeof(struct md_rdev*)*
conf->nr_strip_zones*mddev->raid_disks,
GFP_KERNEL);
if (!conf->devlist)
@@ -218,44 +220,45 @@ static int create_strip_zones(mddev_t *mddev, raid0_conf_t **private_conf)
zone = conf->strip_zone + i;
dev = conf->devlist + i * mddev->raid_disks;
- printk(KERN_INFO "md/raid0:%s: zone %d\n",
- mdname(mddev), i);
+ pr_debug("md/raid0:%s: zone %d\n", mdname(mddev), i);
zone->dev_start = smallest->sectors;
smallest = NULL;
c = 0;
for (j=0; j<cnt; j++) {
rdev = conf->devlist[j];
- printk(KERN_INFO "md/raid0:%s: checking %s ...",
- mdname(mddev),
- bdevname(rdev->bdev, b));
if (rdev->sectors <= zone->dev_start) {
- printk(KERN_CONT " nope.\n");
+ pr_debug("md/raid0:%s: checking %s ... nope\n",
+ mdname(mddev),
+ bdevname(rdev->bdev, b));
continue;
}
- printk(KERN_CONT " contained as device %d\n", c);
+ pr_debug("md/raid0:%s: checking %s ..."
+ " contained as device %d\n",
+ mdname(mddev),
+ bdevname(rdev->bdev, b), c);
dev[c] = rdev;
c++;
if (!smallest || rdev->sectors < smallest->sectors) {
smallest = rdev;
- printk(KERN_INFO "md/raid0:%s: (%llu) is smallest!.\n",
- mdname(mddev),
- (unsigned long long)rdev->sectors);
+ pr_debug("md/raid0:%s: (%llu) is smallest!.\n",
+ mdname(mddev),
+ (unsigned long long)rdev->sectors);
}
}
zone->nb_dev = c;
sectors = (smallest->sectors - zone->dev_start) * c;
- printk(KERN_INFO "md/raid0:%s: zone->nb_dev: %d, sectors: %llu\n",
- mdname(mddev),
- zone->nb_dev, (unsigned long long)sectors);
+ pr_debug("md/raid0:%s: zone->nb_dev: %d, sectors: %llu\n",
+ mdname(mddev),
+ zone->nb_dev, (unsigned long long)sectors);
curr_zone_end += sectors;
zone->zone_end = curr_zone_end;
- printk(KERN_INFO "md/raid0:%s: current zone start: %llu\n",
- mdname(mddev),
- (unsigned long long)smallest->sectors);
+ pr_debug("md/raid0:%s: current zone start: %llu\n",
+ mdname(mddev),
+ (unsigned long long)smallest->sectors);
}
mddev->queue->backing_dev_info.congested_fn = raid0_congested;
mddev->queue->backing_dev_info.congested_data = mddev;
@@ -275,7 +278,7 @@ static int create_strip_zones(mddev_t *mddev, raid0_conf_t **private_conf)
blk_queue_io_opt(mddev->queue,
(mddev->chunk_sectors << 9) * mddev->raid_disks);
- printk(KERN_INFO "md/raid0:%s: done.\n", mdname(mddev));
+ pr_debug("md/raid0:%s: done.\n", mdname(mddev));
*private_conf = conf;
return 0;
@@ -299,7 +302,7 @@ static int raid0_mergeable_bvec(struct request_queue *q,
struct bvec_merge_data *bvm,
struct bio_vec *biovec)
{
- mddev_t *mddev = q->queuedata;
+ struct mddev *mddev = q->queuedata;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
int max;
unsigned int chunk_sectors = mddev->chunk_sectors;
@@ -318,10 +321,10 @@ static int raid0_mergeable_bvec(struct request_queue *q,
return max;
}
-static sector_t raid0_size(mddev_t *mddev, sector_t sectors, int raid_disks)
+static sector_t raid0_size(struct mddev *mddev, sector_t sectors, int raid_disks)
{
sector_t array_sectors = 0;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
WARN_ONCE(sectors || raid_disks,
"%s does not support generic reshape\n", __func__);
@@ -332,9 +335,9 @@ static sector_t raid0_size(mddev_t *mddev, sector_t sectors, int raid_disks)
return array_sectors;
}
-static int raid0_run(mddev_t *mddev)
+static int raid0_run(struct mddev *mddev)
{
- raid0_conf_t *conf;
+ struct r0conf *conf;
int ret;
if (mddev->chunk_sectors == 0) {
@@ -382,9 +385,9 @@ static int raid0_run(mddev_t *mddev)
return md_integrity_register(mddev);
}
-static int raid0_stop(mddev_t *mddev)
+static int raid0_stop(struct mddev *mddev)
{
- raid0_conf_t *conf = mddev->private;
+ struct r0conf *conf = mddev->private;
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
kfree(conf->strip_zone);
@@ -397,7 +400,7 @@ static int raid0_stop(mddev_t *mddev)
/* Find the zone which holds a particular offset
* Update *sectorp to be an offset in that zone
*/
-static struct strip_zone *find_zone(struct raid0_private_data *conf,
+static struct strip_zone *find_zone(struct r0conf *conf,
sector_t *sectorp)
{
int i;
@@ -417,12 +420,12 @@ static struct strip_zone *find_zone(struct raid0_private_data *conf,
* remaps the bio to the target device. we separate two flows.
* power 2 flow and a general flow for the sake of perfromance
*/
-static mdk_rdev_t *map_sector(mddev_t *mddev, struct strip_zone *zone,
+static struct md_rdev *map_sector(struct mddev *mddev, struct strip_zone *zone,
sector_t sector, sector_t *sector_offset)
{
unsigned int sect_in_chunk;
sector_t chunk;
- raid0_conf_t *conf = mddev->private;
+ struct r0conf *conf = mddev->private;
int raid_disks = conf->strip_zone[0].nb_dev;
unsigned int chunk_sects = mddev->chunk_sectors;
@@ -453,7 +456,7 @@ static mdk_rdev_t *map_sector(mddev_t *mddev, struct strip_zone *zone,
/*
* Is io distribute over 1 or more chunks ?
*/
-static inline int is_io_in_chunk_boundary(mddev_t *mddev,
+static inline int is_io_in_chunk_boundary(struct mddev *mddev,
unsigned int chunk_sects, struct bio *bio)
{
if (likely(is_power_of_2(chunk_sects))) {
@@ -466,16 +469,16 @@ static inline int is_io_in_chunk_boundary(mddev_t *mddev,
}
}
-static int raid0_make_request(mddev_t *mddev, struct bio *bio)
+static void raid0_make_request(struct mddev *mddev, struct bio *bio)
{
unsigned int chunk_sects;
sector_t sector_offset;
struct strip_zone *zone;
- mdk_rdev_t *tmp_dev;
+ struct md_rdev *tmp_dev;
if (unlikely(bio->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bio);
- return 0;
+ return;
}
chunk_sects = mddev->chunk_sectors;
@@ -495,13 +498,10 @@ static int raid0_make_request(mddev_t *mddev, struct bio *bio)
else
bp = bio_split(bio, chunk_sects -
sector_div(sector, chunk_sects));
- if (raid0_make_request(mddev, &bp->bio1))
- generic_make_request(&bp->bio1);
- if (raid0_make_request(mddev, &bp->bio2))
- generic_make_request(&bp->bio2);
-
+ raid0_make_request(mddev, &bp->bio1);
+ raid0_make_request(mddev, &bp->bio2);
bio_pair_release(bp);
- return 0;
+ return;
}
sector_offset = bio->bi_sector;
@@ -511,10 +511,9 @@ static int raid0_make_request(mddev_t *mddev, struct bio *bio)
bio->bi_bdev = tmp_dev->bdev;
bio->bi_sector = sector_offset + zone->dev_start +
tmp_dev->data_offset;
- /*
- * Let the main block layer submit the IO and resolve recursion:
- */
- return 1;
+
+ generic_make_request(bio);
+ return;
bad_map:
printk("md/raid0:%s: make_request bug: can't convert block across chunks"
@@ -523,46 +522,19 @@ bad_map:
(unsigned long long)bio->bi_sector, bio->bi_size >> 10);
bio_io_error(bio);
- return 0;
+ return;
}
-static void raid0_status(struct seq_file *seq, mddev_t *mddev)
+static void raid0_status(struct seq_file *seq, struct mddev *mddev)
{
-#undef MD_DEBUG
-#ifdef MD_DEBUG
- int j, k, h;
- char b[BDEVNAME_SIZE];
- raid0_conf_t *conf = mddev->private;
- int raid_disks = conf->strip_zone[0].nb_dev;
-
- sector_t zone_size;
- sector_t zone_start = 0;
- h = 0;
-
- for (j = 0; j < conf->nr_strip_zones; j++) {
- seq_printf(seq, " z%d", j);
- seq_printf(seq, "=[");
- for (k = 0; k < conf->strip_zone[j].nb_dev; k++)
- seq_printf(seq, "%s/", bdevname(
- conf->devlist[j*raid_disks + k]
- ->bdev, b));
-
- zone_size = conf->strip_zone[j].zone_end - zone_start;
- seq_printf(seq, "] ze=%lld ds=%lld s=%lld\n",
- (unsigned long long)zone_start>>1,
- (unsigned long long)conf->strip_zone[j].dev_start>>1,
- (unsigned long long)zone_size>>1);
- zone_start = conf->strip_zone[j].zone_end;
- }
-#endif
seq_printf(seq, " %dk chunks", mddev->chunk_sectors / 2);
return;
}
-static void *raid0_takeover_raid45(mddev_t *mddev)
+static void *raid0_takeover_raid45(struct mddev *mddev)
{
- mdk_rdev_t *rdev;
- raid0_conf_t *priv_conf;
+ struct md_rdev *rdev;
+ struct r0conf *priv_conf;
if (mddev->degraded != 1) {
printk(KERN_ERR "md/raid0:%s: raid5 must be degraded! Degraded disks: %d\n",
@@ -593,9 +565,9 @@ static void *raid0_takeover_raid45(mddev_t *mddev)
return priv_conf;
}
-static void *raid0_takeover_raid10(mddev_t *mddev)
+static void *raid0_takeover_raid10(struct mddev *mddev)
{
- raid0_conf_t *priv_conf;
+ struct r0conf *priv_conf;
/* Check layout:
* - far_copies must be 1
@@ -634,9 +606,9 @@ static void *raid0_takeover_raid10(mddev_t *mddev)
return priv_conf;
}
-static void *raid0_takeover_raid1(mddev_t *mddev)
+static void *raid0_takeover_raid1(struct mddev *mddev)
{
- raid0_conf_t *priv_conf;
+ struct r0conf *priv_conf;
/* Check layout:
* - (N - 1) mirror drives must be already faulty
@@ -660,7 +632,7 @@ static void *raid0_takeover_raid1(mddev_t *mddev)
return priv_conf;
}
-static void *raid0_takeover(mddev_t *mddev)
+static void *raid0_takeover(struct mddev *mddev)
{
/* raid0 can take over:
* raid4 - if all data disks are active.
@@ -691,11 +663,11 @@ static void *raid0_takeover(mddev_t *mddev)
return ERR_PTR(-EINVAL);
}
-static void raid0_quiesce(mddev_t *mddev, int state)
+static void raid0_quiesce(struct mddev *mddev, int state)
{
}
-static struct mdk_personality raid0_personality=
+static struct md_personality raid0_personality=
{
.name = "raid0",
.level = 0,
diff --git a/drivers/md/raid0.h b/drivers/md/raid0.h
index 91f8e876ee64..0884bba8df4c 100644
--- a/drivers/md/raid0.h
+++ b/drivers/md/raid0.h
@@ -1,20 +1,16 @@
#ifndef _RAID0_H
#define _RAID0_H
-struct strip_zone
-{
+struct strip_zone {
sector_t zone_end; /* Start of the next zone (in sectors) */
sector_t dev_start; /* Zone offset in real dev (in sectors) */
int nb_dev; /* # of devices attached to the zone */
};
-struct raid0_private_data
-{
+struct r0conf {
struct strip_zone *strip_zone;
- mdk_rdev_t **devlist; /* lists of rdevs, pointed to by strip_zone->dev */
+ struct md_rdev **devlist; /* lists of rdevs, pointed to by strip_zone->dev */
int nr_strip_zones;
};
-typedef struct raid0_private_data raid0_conf_t;
-
#endif
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index f7431b6d8447..ede2461e79c5 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -34,31 +34,31 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/blkdev.h>
+#include <linux/module.h>
#include <linux/seq_file.h>
+#include <linux/ratelimit.h>
#include "md.h"
#include "raid1.h"
#include "bitmap.h"
-#define DEBUG 0
-#if DEBUG
-#define PRINTK(x...) printk(x)
-#else
-#define PRINTK(x...)
-#endif
-
/*
* Number of guaranteed r1bios in case of extreme VM load:
*/
#define NR_RAID1_BIOS 256
+/* When there are this many requests queue to be written by
+ * the raid1 thread, we become 'congested' to provide back-pressure
+ * for writeback.
+ */
+static int max_queued_requests = 1024;
-static void allow_barrier(conf_t *conf);
-static void lower_barrier(conf_t *conf);
+static void allow_barrier(struct r1conf *conf);
+static void lower_barrier(struct r1conf *conf);
static void * r1bio_pool_alloc(gfp_t gfp_flags, void *data)
{
struct pool_info *pi = data;
- int size = offsetof(r1bio_t, bios[pi->raid_disks]);
+ int size = offsetof(struct r1bio, bios[pi->raid_disks]);
/* allocate a r1bio with room for raid_disks entries in the bios array */
return kzalloc(size, gfp_flags);
@@ -79,7 +79,7 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
{
struct pool_info *pi = data;
struct page *page;
- r1bio_t *r1_bio;
+ struct r1bio *r1_bio;
struct bio *bio;
int i, j;
@@ -145,7 +145,7 @@ static void r1buf_pool_free(void *__r1_bio, void *data)
{
struct pool_info *pi = data;
int i,j;
- r1bio_t *r1bio = __r1_bio;
+ struct r1bio *r1bio = __r1_bio;
for (i = 0; i < RESYNC_PAGES; i++)
for (j = pi->raid_disks; j-- ;) {
@@ -160,35 +160,29 @@ static void r1buf_pool_free(void *__r1_bio, void *data)
r1bio_pool_free(r1bio, data);
}
-static void put_all_bios(conf_t *conf, r1bio_t *r1_bio)
+static void put_all_bios(struct r1conf *conf, struct r1bio *r1_bio)
{
int i;
for (i = 0; i < conf->raid_disks; i++) {
struct bio **bio = r1_bio->bios + i;
- if (*bio && *bio != IO_BLOCKED)
+ if (!BIO_SPECIAL(*bio))
bio_put(*bio);
*bio = NULL;
}
}
-static void free_r1bio(r1bio_t *r1_bio)
+static void free_r1bio(struct r1bio *r1_bio)
{
- conf_t *conf = r1_bio->mddev->private;
-
- /*
- * Wake up any possible resync thread that waits for the device
- * to go idle.
- */
- allow_barrier(conf);
+ struct r1conf *conf = r1_bio->mddev->private;
put_all_bios(conf, r1_bio);
mempool_free(r1_bio, conf->r1bio_pool);
}
-static void put_buf(r1bio_t *r1_bio)
+static void put_buf(struct r1bio *r1_bio)
{
- conf_t *conf = r1_bio->mddev->private;
+ struct r1conf *conf = r1_bio->mddev->private;
int i;
for (i=0; i<conf->raid_disks; i++) {
@@ -202,11 +196,11 @@ static void put_buf(r1bio_t *r1_bio)
lower_barrier(conf);
}
-static void reschedule_retry(r1bio_t *r1_bio)
+static void reschedule_retry(struct r1bio *r1_bio)
{
unsigned long flags;
- mddev_t *mddev = r1_bio->mddev;
- conf_t *conf = mddev->private;
+ struct mddev *mddev = r1_bio->mddev;
+ struct r1conf *conf = mddev->private;
spin_lock_irqsave(&conf->device_lock, flags);
list_add(&r1_bio->retry_list, &conf->retry_list);
@@ -222,20 +216,46 @@ static void reschedule_retry(r1bio_t *r1_bio)
* operation and are ready to return a success/failure code to the buffer
* cache layer.
*/
-static void raid_end_bio_io(r1bio_t *r1_bio)
+static void call_bio_endio(struct r1bio *r1_bio)
+{
+ struct bio *bio = r1_bio->master_bio;
+ int done;
+ struct r1conf *conf = r1_bio->mddev->private;
+
+ if (bio->bi_phys_segments) {
+ unsigned long flags;
+ spin_lock_irqsave(&conf->device_lock, flags);
+ bio->bi_phys_segments--;
+ done = (bio->bi_phys_segments == 0);
+ spin_unlock_irqrestore(&conf->device_lock, flags);
+ } else
+ done = 1;
+
+ if (!test_bit(R1BIO_Uptodate, &r1_bio->state))
+ clear_bit(BIO_UPTODATE, &bio->bi_flags);
+ if (done) {
+ bio_endio(bio, 0);
+ /*
+ * Wake up any possible resync thread that waits for the device
+ * to go idle.
+ */
+ allow_barrier(conf);
+ }
+}
+
+static void raid_end_bio_io(struct r1bio *r1_bio)
{
struct bio *bio = r1_bio->master_bio;
/* if nobody has done the final endio yet, do it now */
if (!test_and_set_bit(R1BIO_Returned, &r1_bio->state)) {
- PRINTK(KERN_DEBUG "raid1: sync end %s on sectors %llu-%llu\n",
- (bio_data_dir(bio) == WRITE) ? "write" : "read",
- (unsigned long long) bio->bi_sector,
- (unsigned long long) bio->bi_sector +
- (bio->bi_size >> 9) - 1);
+ pr_debug("raid1: sync end %s on sectors %llu-%llu\n",
+ (bio_data_dir(bio) == WRITE) ? "write" : "read",
+ (unsigned long long) bio->bi_sector,
+ (unsigned long long) bio->bi_sector +
+ (bio->bi_size >> 9) - 1);
- bio_endio(bio,
- test_bit(R1BIO_Uptodate, &r1_bio->state) ? 0 : -EIO);
+ call_bio_endio(r1_bio);
}
free_r1bio(r1_bio);
}
@@ -243,20 +263,38 @@ static void raid_end_bio_io(r1bio_t *r1_bio)
/*
* Update disk head position estimator based on IRQ completion info.
*/
-static inline void update_head_pos(int disk, r1bio_t *r1_bio)
+static inline void update_head_pos(int disk, struct r1bio *r1_bio)
{
- conf_t *conf = r1_bio->mddev->private;
+ struct r1conf *conf = r1_bio->mddev->private;
conf->mirrors[disk].head_position =
r1_bio->sector + (r1_bio->sectors);
}
+/*
+ * Find the disk number which triggered given bio
+ */
+static int find_bio_disk(struct r1bio *r1_bio, struct bio *bio)
+{
+ int mirror;
+ int raid_disks = r1_bio->mddev->raid_disks;
+
+ for (mirror = 0; mirror < raid_disks; mirror++)
+ if (r1_bio->bios[mirror] == bio)
+ break;
+
+ BUG_ON(mirror == raid_disks);
+ update_head_pos(mirror, r1_bio);
+
+ return mirror;
+}
+
static void raid1_end_read_request(struct bio *bio, int error)
{
int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
- r1bio_t *r1_bio = bio->bi_private;
+ struct r1bio *r1_bio = bio->bi_private;
int mirror;
- conf_t *conf = r1_bio->mddev->private;
+ struct r1conf *conf = r1_bio->mddev->private;
mirror = r1_bio->read_disk;
/*
@@ -287,62 +325,73 @@ static void raid1_end_read_request(struct bio *bio, int error)
* oops, read error:
*/
char b[BDEVNAME_SIZE];
- if (printk_ratelimit())
- printk(KERN_ERR "md/raid1:%s: %s: rescheduling sector %llu\n",
- mdname(conf->mddev),
- bdevname(conf->mirrors[mirror].rdev->bdev,b), (unsigned long long)r1_bio->sector);
+ printk_ratelimited(
+ KERN_ERR "md/raid1:%s: %s: "
+ "rescheduling sector %llu\n",
+ mdname(conf->mddev),
+ bdevname(conf->mirrors[mirror].rdev->bdev,
+ b),
+ (unsigned long long)r1_bio->sector);
+ set_bit(R1BIO_ReadError, &r1_bio->state);
reschedule_retry(r1_bio);
}
rdev_dec_pending(conf->mirrors[mirror].rdev, conf->mddev);
}
-static void r1_bio_write_done(r1bio_t *r1_bio)
+static void close_write(struct r1bio *r1_bio)
{
- if (atomic_dec_and_test(&r1_bio->remaining))
- {
- /* it really is the end of this request */
- if (test_bit(R1BIO_BehindIO, &r1_bio->state)) {
- /* free extra copy of the data pages */
- int i = r1_bio->behind_page_count;
- while (i--)
- safe_put_page(r1_bio->behind_pages[i]);
- kfree(r1_bio->behind_pages);
- r1_bio->behind_pages = NULL;
- }
- /* clear the bitmap if all writes complete successfully */
- bitmap_endwrite(r1_bio->mddev->bitmap, r1_bio->sector,
- r1_bio->sectors,
- !test_bit(R1BIO_Degraded, &r1_bio->state),
- test_bit(R1BIO_BehindIO, &r1_bio->state));
- md_write_end(r1_bio->mddev);
- raid_end_bio_io(r1_bio);
+ /* it really is the end of this request */
+ if (test_bit(R1BIO_BehindIO, &r1_bio->state)) {
+ /* free extra copy of the data pages */
+ int i = r1_bio->behind_page_count;
+ while (i--)
+ safe_put_page(r1_bio->behind_bvecs[i].bv_page);
+ kfree(r1_bio->behind_bvecs);
+ r1_bio->behind_bvecs = NULL;
+ }
+ /* clear the bitmap if all writes complete successfully */
+ bitmap_endwrite(r1_bio->mddev->bitmap, r1_bio->sector,
+ r1_bio->sectors,
+ !test_bit(R1BIO_Degraded, &r1_bio->state),
+ test_bit(R1BIO_BehindIO, &r1_bio->state));
+ md_write_end(r1_bio->mddev);
+}
+
+static void r1_bio_write_done(struct r1bio *r1_bio)
+{
+ if (!atomic_dec_and_test(&r1_bio->remaining))
+ return;
+
+ if (test_bit(R1BIO_WriteError, &r1_bio->state))
+ reschedule_retry(r1_bio);
+ else {
+ close_write(r1_bio);
+ if (test_bit(R1BIO_MadeGood, &r1_bio->state))
+ reschedule_retry(r1_bio);
+ else
+ raid_end_bio_io(r1_bio);
}
}
static void raid1_end_write_request(struct bio *bio, int error)
{
int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
- r1bio_t *r1_bio = bio->bi_private;
+ struct r1bio *r1_bio = bio->bi_private;
int mirror, behind = test_bit(R1BIO_BehindIO, &r1_bio->state);
- conf_t *conf = r1_bio->mddev->private;
+ struct r1conf *conf = r1_bio->mddev->private;
struct bio *to_put = NULL;
-
- for (mirror = 0; mirror < conf->raid_disks; mirror++)
- if (r1_bio->bios[mirror] == bio)
- break;
+ mirror = find_bio_disk(r1_bio, bio);
/*
* 'one mirror IO has finished' event handler:
*/
- r1_bio->bios[mirror] = NULL;
- to_put = bio;
if (!uptodate) {
- md_error(r1_bio->mddev, conf->mirrors[mirror].rdev);
- /* an I/O failed, we can't clear the bitmap */
- set_bit(R1BIO_Degraded, &r1_bio->state);
- } else
+ set_bit(WriteErrorSeen,
+ &conf->mirrors[mirror].rdev->flags);
+ set_bit(R1BIO_WriteError, &r1_bio->state);
+ } else {
/*
* Set R1BIO_Uptodate in our master bio, so that we
* will return a good error code for to the higher
@@ -353,9 +402,21 @@ static void raid1_end_write_request(struct bio *bio, int error)
* to user-side. So if something waits for IO, then it
* will wait for the 'master' bio.
*/
+ sector_t first_bad;
+ int bad_sectors;
+
+ r1_bio->bios[mirror] = NULL;
+ to_put = bio;
set_bit(R1BIO_Uptodate, &r1_bio->state);
- update_head_pos(mirror, r1_bio);
+ /* Maybe we can clear some bad blocks. */
+ if (is_badblock(conf->mirrors[mirror].rdev,
+ r1_bio->sector, r1_bio->sectors,
+ &first_bad, &bad_sectors)) {
+ r1_bio->bios[mirror] = IO_MADE_GOOD;
+ set_bit(R1BIO_MadeGood, &r1_bio->state);
+ }
+ }
if (behind) {
if (test_bit(WriteMostly, &conf->mirrors[mirror].rdev->flags))
@@ -373,15 +434,18 @@ static void raid1_end_write_request(struct bio *bio, int error)
/* Maybe we can return now */
if (!test_and_set_bit(R1BIO_Returned, &r1_bio->state)) {
struct bio *mbio = r1_bio->master_bio;
- PRINTK(KERN_DEBUG "raid1: behind end write sectors %llu-%llu\n",
- (unsigned long long) mbio->bi_sector,
- (unsigned long long) mbio->bi_sector +
- (mbio->bi_size >> 9) - 1);
- bio_endio(mbio, 0);
+ pr_debug("raid1: behind end write sectors"
+ " %llu-%llu\n",
+ (unsigned long long) mbio->bi_sector,
+ (unsigned long long) mbio->bi_sector +
+ (mbio->bi_size >> 9) - 1);
+ call_bio_endio(r1_bio);
}
}
}
- rdev_dec_pending(conf->mirrors[mirror].rdev, conf->mddev);
+ if (r1_bio->bios[mirror] == NULL)
+ rdev_dec_pending(conf->mirrors[mirror].rdev,
+ conf->mddev);
/*
* Let's see if all mirrored write operations have finished
@@ -408,15 +472,16 @@ static void raid1_end_write_request(struct bio *bio, int error)
*
* The rdev for the device selected will have nr_pending incremented.
*/
-static int read_balance(conf_t *conf, r1bio_t *r1_bio)
+static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sectors)
{
const sector_t this_sector = r1_bio->sector;
- const int sectors = r1_bio->sectors;
+ int sectors;
+ int best_good_sectors;
int start_disk;
int best_disk;
int i;
sector_t best_dist;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
int choose_first;
rcu_read_lock();
@@ -426,8 +491,11 @@ static int read_balance(conf_t *conf, r1bio_t *r1_bio)
* We take the first readable disk when above the resync window.
*/
retry:
+ sectors = r1_bio->sectors;
best_disk = -1;
best_dist = MaxSector;
+ best_good_sectors = 0;
+
if (conf->mddev->recovery_cp < MaxSector &&
(this_sector + sectors >= conf->next_resync)) {
choose_first = 1;
@@ -439,6 +507,9 @@ static int read_balance(conf_t *conf, r1bio_t *r1_bio)
for (i = 0 ; i < conf->raid_disks ; i++) {
sector_t dist;
+ sector_t first_bad;
+ int bad_sectors;
+
int disk = start_disk + i;
if (disk >= conf->raid_disks)
disk -= conf->raid_disks;
@@ -461,6 +532,35 @@ static int read_balance(conf_t *conf, r1bio_t *r1_bio)
/* This is a reasonable device to use. It might
* even be best.
*/
+ if (is_badblock(rdev, this_sector, sectors,
+ &first_bad, &bad_sectors)) {
+ if (best_dist < MaxSector)
+ /* already have a better device */
+ continue;
+ if (first_bad <= this_sector) {
+ /* cannot read here. If this is the 'primary'
+ * device, then we must not read beyond
+ * bad_sectors from another device..
+ */
+ bad_sectors -= (this_sector - first_bad);
+ if (choose_first && sectors > bad_sectors)
+ sectors = bad_sectors;
+ if (best_good_sectors > sectors)
+ best_good_sectors = sectors;
+
+ } else {
+ sector_t good_sectors = first_bad - this_sector;
+ if (good_sectors > best_good_sectors) {
+ best_good_sectors = good_sectors;
+ best_disk = disk;
+ }
+ if (choose_first)
+ break;
+ }
+ continue;
+ } else
+ best_good_sectors = sectors;
+
dist = abs(this_sector - conf->mirrors[disk].head_position);
if (choose_first
/* Don't change to another disk for sequential reads */
@@ -489,22 +589,28 @@ static int read_balance(conf_t *conf, r1bio_t *r1_bio)
rdev_dec_pending(rdev, conf->mddev);
goto retry;
}
+ sectors = best_good_sectors;
conf->next_seq_sect = this_sector + sectors;
conf->last_used = best_disk;
}
rcu_read_unlock();
+ *max_sectors = sectors;
return best_disk;
}
-int md_raid1_congested(mddev_t *mddev, int bits)
+int md_raid1_congested(struct mddev *mddev, int bits)
{
- conf_t *conf = mddev->private;
+ struct r1conf *conf = mddev->private;
int i, ret = 0;
+ if ((bits & (1 << BDI_async_congested)) &&
+ conf->pending_count >= max_queued_requests)
+ return 1;
+
rcu_read_lock();
for (i = 0; i < mddev->raid_disks; i++) {
- mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev);
+ struct md_rdev *rdev = rcu_dereference(conf->mirrors[i].rdev);
if (rdev && !test_bit(Faulty, &rdev->flags)) {
struct request_queue *q = bdev_get_queue(rdev->bdev);
@@ -526,13 +632,13 @@ EXPORT_SYMBOL_GPL(md_raid1_congested);
static int raid1_congested(void *data, int bits)
{
- mddev_t *mddev = data;
+ struct mddev *mddev = data;
return mddev_congested(mddev, bits) ||
md_raid1_congested(mddev, bits);
}
-static void flush_pending_writes(conf_t *conf)
+static void flush_pending_writes(struct r1conf *conf)
{
/* Any writes that have been queued but are awaiting
* bitmap updates get flushed here.
@@ -542,10 +648,12 @@ static void flush_pending_writes(conf_t *conf)
if (conf->pending_bio_list.head) {
struct bio *bio;
bio = bio_list_get(&conf->pending_bio_list);
+ conf->pending_count = 0;
spin_unlock_irq(&conf->device_lock);
/* flush any pending bitmap writes to
* disk before proceeding w/ I/O */
bitmap_unplug(conf->mddev->bitmap);
+ wake_up(&conf->wait_barrier);
while (bio) { /* submit pending writes */
struct bio *next = bio->bi_next;
@@ -580,7 +688,7 @@ static void flush_pending_writes(conf_t *conf)
*/
#define RESYNC_DEPTH 32
-static void raise_barrier(conf_t *conf)
+static void raise_barrier(struct r1conf *conf)
{
spin_lock_irq(&conf->resync_lock);
@@ -599,7 +707,7 @@ static void raise_barrier(conf_t *conf)
spin_unlock_irq(&conf->resync_lock);
}
-static void lower_barrier(conf_t *conf)
+static void lower_barrier(struct r1conf *conf)
{
unsigned long flags;
BUG_ON(conf->barrier <= 0);
@@ -609,7 +717,7 @@ static void lower_barrier(conf_t *conf)
wake_up(&conf->wait_barrier);
}
-static void wait_barrier(conf_t *conf)
+static void wait_barrier(struct r1conf *conf)
{
spin_lock_irq(&conf->resync_lock);
if (conf->barrier) {
@@ -623,7 +731,7 @@ static void wait_barrier(conf_t *conf)
spin_unlock_irq(&conf->resync_lock);
}
-static void allow_barrier(conf_t *conf)
+static void allow_barrier(struct r1conf *conf)
{
unsigned long flags;
spin_lock_irqsave(&conf->resync_lock, flags);
@@ -632,7 +740,7 @@ static void allow_barrier(conf_t *conf)
wake_up(&conf->wait_barrier);
}
-static void freeze_array(conf_t *conf)
+static void freeze_array(struct r1conf *conf)
{
/* stop syncio and normal IO and wait for everything to
* go quite.
@@ -655,7 +763,7 @@ static void freeze_array(conf_t *conf)
flush_pending_writes(conf));
spin_unlock_irq(&conf->resync_lock);
}
-static void unfreeze_array(conf_t *conf)
+static void unfreeze_array(struct r1conf *conf)
{
/* reverse the effect of the freeze */
spin_lock_irq(&conf->resync_lock);
@@ -668,51 +776,55 @@ static void unfreeze_array(conf_t *conf)
/* duplicate the data pages for behind I/O
*/
-static void alloc_behind_pages(struct bio *bio, r1bio_t *r1_bio)
+static void alloc_behind_pages(struct bio *bio, struct r1bio *r1_bio)
{
int i;
struct bio_vec *bvec;
- struct page **pages = kzalloc(bio->bi_vcnt * sizeof(struct page*),
+ struct bio_vec *bvecs = kzalloc(bio->bi_vcnt * sizeof(struct bio_vec),
GFP_NOIO);
- if (unlikely(!pages))
+ if (unlikely(!bvecs))
return;
bio_for_each_segment(bvec, bio, i) {
- pages[i] = alloc_page(GFP_NOIO);
- if (unlikely(!pages[i]))
+ bvecs[i] = *bvec;
+ bvecs[i].bv_page = alloc_page(GFP_NOIO);
+ if (unlikely(!bvecs[i].bv_page))
goto do_sync_io;
- memcpy(kmap(pages[i]) + bvec->bv_offset,
- kmap(bvec->bv_page) + bvec->bv_offset, bvec->bv_len);
- kunmap(pages[i]);
+ memcpy(kmap(bvecs[i].bv_page) + bvec->bv_offset,
+ kmap(bvec->bv_page) + bvec->bv_offset, bvec->bv_len);
+ kunmap(bvecs[i].bv_page);
kunmap(bvec->bv_page);
}
- r1_bio->behind_pages = pages;
+ r1_bio->behind_bvecs = bvecs;
r1_bio->behind_page_count = bio->bi_vcnt;
set_bit(R1BIO_BehindIO, &r1_bio->state);
return;
do_sync_io:
for (i = 0; i < bio->bi_vcnt; i++)
- if (pages[i])
- put_page(pages[i]);
- kfree(pages);
- PRINTK("%dB behind alloc failed, doing sync I/O\n", bio->bi_size);
+ if (bvecs[i].bv_page)
+ put_page(bvecs[i].bv_page);
+ kfree(bvecs);
+ pr_debug("%dB behind alloc failed, doing sync I/O\n", bio->bi_size);
}
-static int make_request(mddev_t *mddev, struct bio * bio)
+static void make_request(struct mddev *mddev, struct bio * bio)
{
- conf_t *conf = mddev->private;
- mirror_info_t *mirror;
- r1bio_t *r1_bio;
+ struct r1conf *conf = mddev->private;
+ struct mirror_info *mirror;
+ struct r1bio *r1_bio;
struct bio *read_bio;
- int i, targets = 0, disks;
+ int i, disks;
struct bitmap *bitmap;
unsigned long flags;
const int rw = bio_data_dir(bio);
const unsigned long do_sync = (bio->bi_rw & REQ_SYNC);
const unsigned long do_flush_fua = (bio->bi_rw & (REQ_FLUSH | REQ_FUA));
- mdk_rdev_t *blocked_rdev;
+ struct md_rdev *blocked_rdev;
int plugged;
+ int first_clone;
+ int sectors_handled;
+ int max_sectors;
/*
* Register the new request and wait if the reconstruction
@@ -759,16 +871,29 @@ static int make_request(mddev_t *mddev, struct bio * bio)
r1_bio->mddev = mddev;
r1_bio->sector = bio->bi_sector;
+ /* We might need to issue multiple reads to different
+ * devices if there are bad blocks around, so we keep
+ * track of the number of reads in bio->bi_phys_segments.
+ * If this is 0, there is only one r1_bio and no locking
+ * will be needed when requests complete. If it is
+ * non-zero, then it is the number of not-completed requests.
+ */
+ bio->bi_phys_segments = 0;
+ clear_bit(BIO_SEG_VALID, &bio->bi_flags);
+
if (rw == READ) {
/*
* read balancing logic:
*/
- int rdisk = read_balance(conf, r1_bio);
+ int rdisk;
+
+read_again:
+ rdisk = read_balance(conf, r1_bio, &max_sectors);
if (rdisk < 0) {
/* couldn't find anywhere to read from */
raid_end_bio_io(r1_bio);
- return 0;
+ return;
}
mirror = conf->mirrors + rdisk;
@@ -784,6 +909,8 @@ static int make_request(mddev_t *mddev, struct bio * bio)
r1_bio->read_disk = rdisk;
read_bio = bio_clone_mddev(bio, GFP_NOIO, mddev);
+ md_trim_bio(read_bio, r1_bio->sector - bio->bi_sector,
+ max_sectors);
r1_bio->bios[rdisk] = read_bio;
@@ -793,16 +920,57 @@ static int make_request(mddev_t *mddev, struct bio * bio)
read_bio->bi_rw = READ | do_sync;
read_bio->bi_private = r1_bio;
- generic_make_request(read_bio);
- return 0;
+ if (max_sectors < r1_bio->sectors) {
+ /* could not read all from this device, so we will
+ * need another r1_bio.
+ */
+
+ sectors_handled = (r1_bio->sector + max_sectors
+ - bio->bi_sector);
+ r1_bio->sectors = max_sectors;
+ spin_lock_irq(&conf->device_lock);
+ if (bio->bi_phys_segments == 0)
+ bio->bi_phys_segments = 2;
+ else
+ bio->bi_phys_segments++;
+ spin_unlock_irq(&conf->device_lock);
+ /* Cannot call generic_make_request directly
+ * as that will be queued in __make_request
+ * and subsequent mempool_alloc might block waiting
+ * for it. So hand bio over to raid1d.
+ */
+ reschedule_retry(r1_bio);
+
+ r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO);
+
+ r1_bio->master_bio = bio;
+ r1_bio->sectors = (bio->bi_size >> 9) - sectors_handled;
+ r1_bio->state = 0;
+ r1_bio->mddev = mddev;
+ r1_bio->sector = bio->bi_sector + sectors_handled;
+ goto read_again;
+ } else
+ generic_make_request(read_bio);
+ return;
}
/*
* WRITE:
*/
- /* first select target devices under spinlock and
+ if (conf->pending_count >= max_queued_requests) {
+ md_wakeup_thread(mddev->thread);
+ wait_event(conf->wait_barrier,
+ conf->pending_count < max_queued_requests);
+ }
+ /* first select target devices under rcu_lock and
* inc refcount on their rdev. Record them by setting
* bios[x] to bio
+ * If there are known/acknowledged bad blocks on any device on
+ * which we have seen a write error, we want to avoid writing those
+ * blocks.
+ * This potentially requires several writes to write around
+ * the bad blocks. Each set of writes gets it's own r1bio
+ * with a set of bios attached.
*/
plugged = mddev_check_plugged(mddev);
@@ -810,24 +978,64 @@ static int make_request(mddev_t *mddev, struct bio * bio)
retry_write:
blocked_rdev = NULL;
rcu_read_lock();
+ max_sectors = r1_bio->sectors;
for (i = 0; i < disks; i++) {
- mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev);
+ struct md_rdev *rdev = rcu_dereference(conf->mirrors[i].rdev);
if (rdev && unlikely(test_bit(Blocked, &rdev->flags))) {
atomic_inc(&rdev->nr_pending);
blocked_rdev = rdev;
break;
}
- if (rdev && !test_bit(Faulty, &rdev->flags)) {
- atomic_inc(&rdev->nr_pending);
- if (test_bit(Faulty, &rdev->flags)) {
+ r1_bio->bios[i] = NULL;
+ if (!rdev || test_bit(Faulty, &rdev->flags)) {
+ set_bit(R1BIO_Degraded, &r1_bio->state);
+ continue;
+ }
+
+ atomic_inc(&rdev->nr_pending);
+ if (test_bit(WriteErrorSeen, &rdev->flags)) {
+ sector_t first_bad;
+ int bad_sectors;
+ int is_bad;
+
+ is_bad = is_badblock(rdev, r1_bio->sector,
+ max_sectors,
+ &first_bad, &bad_sectors);
+ if (is_bad < 0) {
+ /* mustn't write here until the bad block is
+ * acknowledged*/
+ set_bit(BlockedBadBlocks, &rdev->flags);
+ blocked_rdev = rdev;
+ break;
+ }
+ if (is_bad && first_bad <= r1_bio->sector) {
+ /* Cannot write here at all */
+ bad_sectors -= (r1_bio->sector - first_bad);
+ if (bad_sectors < max_sectors)
+ /* mustn't write more than bad_sectors
+ * to other devices yet
+ */
+ max_sectors = bad_sectors;
rdev_dec_pending(rdev, mddev);
- r1_bio->bios[i] = NULL;
- } else {
- r1_bio->bios[i] = bio;
- targets++;
+ /* We don't set R1BIO_Degraded as that
+ * only applies if the disk is
+ * missing, so it might be re-added,
+ * and we want to know to recover this
+ * chunk.
+ * In this case the device is here,
+ * and the fact that this chunk is not
+ * in-sync is recorded in the bad
+ * block log
+ */
+ continue;
}
- } else
- r1_bio->bios[i] = NULL;
+ if (is_bad) {
+ int good_sectors = first_bad - r1_bio->sector;
+ if (good_sectors < max_sectors)
+ max_sectors = good_sectors;
+ }
+ }
+ r1_bio->bios[i] = bio;
}
rcu_read_unlock();
@@ -838,51 +1046,57 @@ static int make_request(mddev_t *mddev, struct bio * bio)
for (j = 0; j < i; j++)
if (r1_bio->bios[j])
rdev_dec_pending(conf->mirrors[j].rdev, mddev);
-
+ r1_bio->state = 0;
allow_barrier(conf);
md_wait_for_blocked_rdev(blocked_rdev, mddev);
wait_barrier(conf);
goto retry_write;
}
- BUG_ON(targets == 0); /* we never fail the last device */
-
- if (targets < conf->raid_disks) {
- /* array is degraded, we will not clear the bitmap
- * on I/O completion (see raid1_end_write_request) */
- set_bit(R1BIO_Degraded, &r1_bio->state);
+ if (max_sectors < r1_bio->sectors) {
+ /* We are splitting this write into multiple parts, so
+ * we need to prepare for allocating another r1_bio.
+ */
+ r1_bio->sectors = max_sectors;
+ spin_lock_irq(&conf->device_lock);
+ if (bio->bi_phys_segments == 0)
+ bio->bi_phys_segments = 2;
+ else
+ bio->bi_phys_segments++;
+ spin_unlock_irq(&conf->device_lock);
}
-
- /* do behind I/O ?
- * Not if there are too many, or cannot allocate memory,
- * or a reader on WriteMostly is waiting for behind writes
- * to flush */
- if (bitmap &&
- (atomic_read(&bitmap->behind_writes)
- < mddev->bitmap_info.max_write_behind) &&
- !waitqueue_active(&bitmap->behind_wait))
- alloc_behind_pages(bio, r1_bio);
+ sectors_handled = r1_bio->sector + max_sectors - bio->bi_sector;
atomic_set(&r1_bio->remaining, 1);
atomic_set(&r1_bio->behind_remaining, 0);
- bitmap_startwrite(bitmap, bio->bi_sector, r1_bio->sectors,
- test_bit(R1BIO_BehindIO, &r1_bio->state));
+ first_clone = 1;
for (i = 0; i < disks; i++) {
struct bio *mbio;
if (!r1_bio->bios[i])
continue;
mbio = bio_clone_mddev(bio, GFP_NOIO, mddev);
- r1_bio->bios[i] = mbio;
-
- mbio->bi_sector = r1_bio->sector + conf->mirrors[i].rdev->data_offset;
- mbio->bi_bdev = conf->mirrors[i].rdev->bdev;
- mbio->bi_end_io = raid1_end_write_request;
- mbio->bi_rw = WRITE | do_flush_fua | do_sync;
- mbio->bi_private = r1_bio;
-
- if (r1_bio->behind_pages) {
+ md_trim_bio(mbio, r1_bio->sector - bio->bi_sector, max_sectors);
+
+ if (first_clone) {
+ /* do behind I/O ?
+ * Not if there are too many, or cannot
+ * allocate memory, or a reader on WriteMostly
+ * is waiting for behind writes to flush */
+ if (bitmap &&
+ (atomic_read(&bitmap->behind_writes)
+ < mddev->bitmap_info.max_write_behind) &&
+ !waitqueue_active(&bitmap->behind_wait))
+ alloc_behind_pages(mbio, r1_bio);
+
+ bitmap_startwrite(bitmap, r1_bio->sector,
+ r1_bio->sectors,
+ test_bit(R1BIO_BehindIO,
+ &r1_bio->state));
+ first_clone = 0;
+ }
+ if (r1_bio->behind_bvecs) {
struct bio_vec *bvec;
int j;
@@ -894,16 +1108,43 @@ static int make_request(mddev_t *mddev, struct bio * bio)
* them all
*/
__bio_for_each_segment(bvec, mbio, j, 0)
- bvec->bv_page = r1_bio->behind_pages[j];
+ bvec->bv_page = r1_bio->behind_bvecs[j].bv_page;
if (test_bit(WriteMostly, &conf->mirrors[i].rdev->flags))
atomic_inc(&r1_bio->behind_remaining);
}
+ r1_bio->bios[i] = mbio;
+
+ mbio->bi_sector = (r1_bio->sector +
+ conf->mirrors[i].rdev->data_offset);
+ mbio->bi_bdev = conf->mirrors[i].rdev->bdev;
+ mbio->bi_end_io = raid1_end_write_request;
+ mbio->bi_rw = WRITE | do_flush_fua | do_sync;
+ mbio->bi_private = r1_bio;
+
atomic_inc(&r1_bio->remaining);
spin_lock_irqsave(&conf->device_lock, flags);
bio_list_add(&conf->pending_bio_list, mbio);
+ conf->pending_count++;
spin_unlock_irqrestore(&conf->device_lock, flags);
}
+ /* Mustn't call r1_bio_write_done before this next test,
+ * as it could result in the bio being freed.
+ */
+ if (sectors_handled < (bio->bi_size >> 9)) {
+ r1_bio_write_done(r1_bio);
+ /* We need another r1_bio. It has already been counted
+ * in bio->bi_phys_segments
+ */
+ r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO);
+ r1_bio->master_bio = bio;
+ r1_bio->sectors = (bio->bi_size >> 9) - sectors_handled;
+ r1_bio->state = 0;
+ r1_bio->mddev = mddev;
+ r1_bio->sector = bio->bi_sector + sectors_handled;
+ goto retry_write;
+ }
+
r1_bio_write_done(r1_bio);
/* In case raid1d snuck in to freeze_array */
@@ -911,20 +1152,18 @@ static int make_request(mddev_t *mddev, struct bio * bio)
if (do_sync || !bitmap || !plugged)
md_wakeup_thread(mddev->thread);
-
- return 0;
}
-static void status(struct seq_file *seq, mddev_t *mddev)
+static void status(struct seq_file *seq, struct mddev *mddev)
{
- conf_t *conf = mddev->private;
+ struct r1conf *conf = mddev->private;
int i;
seq_printf(seq, " [%d/%d] [", conf->raid_disks,
conf->raid_disks - mddev->degraded);
rcu_read_lock();
for (i = 0; i < conf->raid_disks; i++) {
- mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev);
+ struct md_rdev *rdev = rcu_dereference(conf->mirrors[i].rdev);
seq_printf(seq, "%s",
rdev && test_bit(In_sync, &rdev->flags) ? "U" : "_");
}
@@ -933,10 +1172,10 @@ static void status(struct seq_file *seq, mddev_t *mddev)
}
-static void error(mddev_t *mddev, mdk_rdev_t *rdev)
+static void error(struct mddev *mddev, struct md_rdev *rdev)
{
char b[BDEVNAME_SIZE];
- conf_t *conf = mddev->private;
+ struct r1conf *conf = mddev->private;
/*
* If it is not operational, then we have already marked it as dead
@@ -952,9 +1191,10 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
* However don't try a recovery from this drive as
* it is very likely to fail.
*/
- mddev->recovery_disabled = 1;
+ conf->recovery_disabled = mddev->recovery_disabled;
return;
}
+ set_bit(Blocked, &rdev->flags);
if (test_and_clear_bit(In_sync, &rdev->flags)) {
unsigned long flags;
spin_lock_irqsave(&conf->device_lock, flags);
@@ -975,7 +1215,7 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
mdname(mddev), conf->raid_disks - mddev->degraded);
}
-static void print_conf(conf_t *conf)
+static void print_conf(struct r1conf *conf)
{
int i;
@@ -990,7 +1230,7 @@ static void print_conf(conf_t *conf)
rcu_read_lock();
for (i = 0; i < conf->raid_disks; i++) {
char b[BDEVNAME_SIZE];
- mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev);
+ struct md_rdev *rdev = rcu_dereference(conf->mirrors[i].rdev);
if (rdev)
printk(KERN_DEBUG " disk %d, wo:%d, o:%d, dev:%s\n",
i, !test_bit(In_sync, &rdev->flags),
@@ -1000,7 +1240,7 @@ static void print_conf(conf_t *conf)
rcu_read_unlock();
}
-static void close_sync(conf_t *conf)
+static void close_sync(struct r1conf *conf)
{
wait_barrier(conf);
allow_barrier(conf);
@@ -1009,10 +1249,10 @@ static void close_sync(conf_t *conf)
conf->r1buf_pool = NULL;
}
-static int raid1_spare_active(mddev_t *mddev)
+static int raid1_spare_active(struct mddev *mddev)
{
int i;
- conf_t *conf = mddev->private;
+ struct r1conf *conf = mddev->private;
int count = 0;
unsigned long flags;
@@ -1022,12 +1262,12 @@ static int raid1_spare_active(mddev_t *mddev)
* Called under mddev lock, so rcu protection not needed.
*/
for (i = 0; i < conf->raid_disks; i++) {
- mdk_rdev_t *rdev = conf->mirrors[i].rdev;
+ struct md_rdev *rdev = conf->mirrors[i].rdev;
if (rdev
&& !test_bit(Faulty, &rdev->flags)
&& !test_and_set_bit(In_sync, &rdev->flags)) {
count++;
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
}
}
spin_lock_irqsave(&conf->device_lock, flags);
@@ -1039,15 +1279,18 @@ static int raid1_spare_active(mddev_t *mddev)
}
-static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
+static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev)
{
- conf_t *conf = mddev->private;
+ struct r1conf *conf = mddev->private;
int err = -EEXIST;
int mirror = 0;
- mirror_info_t *p;
+ struct mirror_info *p;
int first = 0;
int last = mddev->raid_disks - 1;
+ if (mddev->recovery_disabled == conf->recovery_disabled)
+ return -EBUSY;
+
if (rdev->raid_disk >= 0)
first = last = rdev->raid_disk;
@@ -1084,12 +1327,12 @@ static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
return err;
}
-static int raid1_remove_disk(mddev_t *mddev, int number)
+static int raid1_remove_disk(struct mddev *mddev, int number)
{
- conf_t *conf = mddev->private;
+ struct r1conf *conf = mddev->private;
int err = 0;
- mdk_rdev_t *rdev;
- mirror_info_t *p = conf->mirrors+ number;
+ struct md_rdev *rdev;
+ struct mirror_info *p = conf->mirrors+ number;
print_conf(conf);
rdev = p->rdev;
@@ -1103,7 +1346,7 @@ static int raid1_remove_disk(mddev_t *mddev, int number)
* is not possible.
*/
if (!test_bit(Faulty, &rdev->flags) &&
- !mddev->recovery_disabled &&
+ mddev->recovery_disabled != conf->recovery_disabled &&
mddev->degraded < conf->raid_disks) {
err = -EBUSY;
goto abort;
@@ -1127,14 +1370,10 @@ abort:
static void end_sync_read(struct bio *bio, int error)
{
- r1bio_t *r1_bio = bio->bi_private;
- int i;
+ struct r1bio *r1_bio = bio->bi_private;
+
+ update_head_pos(r1_bio->read_disk, r1_bio);
- for (i=r1_bio->mddev->raid_disks; i--; )
- if (r1_bio->bios[i] == bio)
- break;
- BUG_ON(i < 0);
- update_head_pos(i, r1_bio);
/*
* we have read a block, now it needs to be re-written,
* or re-read if the read failed.
@@ -1150,17 +1389,15 @@ static void end_sync_read(struct bio *bio, int error)
static void end_sync_write(struct bio *bio, int error)
{
int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
- r1bio_t *r1_bio = bio->bi_private;
- mddev_t *mddev = r1_bio->mddev;
- conf_t *conf = mddev->private;
- int i;
+ struct r1bio *r1_bio = bio->bi_private;
+ struct mddev *mddev = r1_bio->mddev;
+ struct r1conf *conf = mddev->private;
int mirror=0;
+ sector_t first_bad;
+ int bad_sectors;
+
+ mirror = find_bio_disk(r1_bio, bio);
- for (i = 0; i < conf->raid_disks; i++)
- if (r1_bio->bios[i] == bio) {
- mirror = i;
- break;
- }
if (!uptodate) {
sector_t sync_blocks = 0;
sector_t s = r1_bio->sector;
@@ -1172,19 +1409,47 @@ static void end_sync_write(struct bio *bio, int error)
s += sync_blocks;
sectors_to_go -= sync_blocks;
} while (sectors_to_go > 0);
- md_error(mddev, conf->mirrors[mirror].rdev);
- }
-
- update_head_pos(mirror, r1_bio);
+ set_bit(WriteErrorSeen,
+ &conf->mirrors[mirror].rdev->flags);
+ set_bit(R1BIO_WriteError, &r1_bio->state);
+ } else if (is_badblock(conf->mirrors[mirror].rdev,
+ r1_bio->sector,
+ r1_bio->sectors,
+ &first_bad, &bad_sectors) &&
+ !is_badblock(conf->mirrors[r1_bio->read_disk].rdev,
+ r1_bio->sector,
+ r1_bio->sectors,
+ &first_bad, &bad_sectors)
+ )
+ set_bit(R1BIO_MadeGood, &r1_bio->state);
if (atomic_dec_and_test(&r1_bio->remaining)) {
- sector_t s = r1_bio->sectors;
- put_buf(r1_bio);
- md_done_sync(mddev, s, uptodate);
+ int s = r1_bio->sectors;
+ if (test_bit(R1BIO_MadeGood, &r1_bio->state) ||
+ test_bit(R1BIO_WriteError, &r1_bio->state))
+ reschedule_retry(r1_bio);
+ else {
+ put_buf(r1_bio);
+ md_done_sync(mddev, s, uptodate);
+ }
}
}
-static int fix_sync_read_error(r1bio_t *r1_bio)
+static int r1_sync_page_io(struct md_rdev *rdev, sector_t sector,
+ int sectors, struct page *page, int rw)
+{
+ if (sync_page_io(rdev, sector, sectors << 9, page, rw, false))
+ /* success */
+ return 1;
+ if (rw == WRITE)
+ set_bit(WriteErrorSeen, &rdev->flags);
+ /* need to record an error - either for the block or the device */
+ if (!rdev_set_badblocks(rdev, sector, sectors, 0))
+ md_error(rdev->mddev, rdev);
+ return 0;
+}
+
+static int fix_sync_read_error(struct r1bio *r1_bio)
{
/* Try some synchronous reads of other devices to get
* good data, much like with normal read errors. Only
@@ -1193,9 +1458,12 @@ static int fix_sync_read_error(r1bio_t *r1_bio)
* We don't need to freeze the array, because being in an
* active sync request, there is no normal IO, and
* no overlapping syncs.
+ * We don't need to check is_badblock() again as we
+ * made sure that anything with a bad block in range
+ * will have bi_end_io clear.
*/
- mddev_t *mddev = r1_bio->mddev;
- conf_t *conf = mddev->private;
+ struct mddev *mddev = r1_bio->mddev;
+ struct r1conf *conf = mddev->private;
struct bio *bio = r1_bio->bios[r1_bio->read_disk];
sector_t sect = r1_bio->sector;
int sectors = r1_bio->sectors;
@@ -1205,7 +1473,7 @@ static int fix_sync_read_error(r1bio_t *r1_bio)
int s = sectors;
int d = r1_bio->read_disk;
int success = 0;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
int start;
if (s > (PAGE_SIZE>>9))
@@ -1217,9 +1485,7 @@ static int fix_sync_read_error(r1bio_t *r1_bio)
* active, and resync is currently active
*/
rdev = conf->mirrors[d].rdev;
- if (sync_page_io(rdev,
- sect,
- s<<9,
+ if (sync_page_io(rdev, sect, s<<9,
bio->bi_io_vec[idx].bv_page,
READ, false)) {
success = 1;
@@ -1233,16 +1499,37 @@ static int fix_sync_read_error(r1bio_t *r1_bio)
if (!success) {
char b[BDEVNAME_SIZE];
- /* Cannot read from anywhere, array is toast */
- md_error(mddev, conf->mirrors[r1_bio->read_disk].rdev);
+ int abort = 0;
+ /* Cannot read from anywhere, this block is lost.
+ * Record a bad block on each device. If that doesn't
+ * work just disable and interrupt the recovery.
+ * Don't fail devices as that won't really help.
+ */
printk(KERN_ALERT "md/raid1:%s: %s: unrecoverable I/O read error"
" for block %llu\n",
mdname(mddev),
bdevname(bio->bi_bdev, b),
(unsigned long long)r1_bio->sector);
- md_done_sync(mddev, r1_bio->sectors, 0);
- put_buf(r1_bio);
- return 0;
+ for (d = 0; d < conf->raid_disks; d++) {
+ rdev = conf->mirrors[d].rdev;
+ if (!rdev || test_bit(Faulty, &rdev->flags))
+ continue;
+ if (!rdev_set_badblocks(rdev, sect, s, 0))
+ abort = 1;
+ }
+ if (abort) {
+ conf->recovery_disabled =
+ mddev->recovery_disabled;
+ set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+ md_done_sync(mddev, r1_bio->sectors, 0);
+ put_buf(r1_bio);
+ return 0;
+ }
+ /* Try next page */
+ sectors -= s;
+ sect += s;
+ idx++;
+ continue;
}
start = d;
@@ -1254,16 +1541,12 @@ static int fix_sync_read_error(r1bio_t *r1_bio)
if (r1_bio->bios[d]->bi_end_io != end_sync_read)
continue;
rdev = conf->mirrors[d].rdev;
- if (sync_page_io(rdev,
- sect,
- s<<9,
- bio->bi_io_vec[idx].bv_page,
- WRITE, false) == 0) {
+ if (r1_sync_page_io(rdev, sect, s,
+ bio->bi_io_vec[idx].bv_page,
+ WRITE) == 0) {
r1_bio->bios[d]->bi_end_io = NULL;
rdev_dec_pending(rdev, mddev);
- md_error(mddev, rdev);
- } else
- atomic_add(s, &rdev->corrected_errors);
+ }
}
d = start;
while (d != r1_bio->read_disk) {
@@ -1273,12 +1556,10 @@ static int fix_sync_read_error(r1bio_t *r1_bio)
if (r1_bio->bios[d]->bi_end_io != end_sync_read)
continue;
rdev = conf->mirrors[d].rdev;
- if (sync_page_io(rdev,
- sect,
- s<<9,
- bio->bi_io_vec[idx].bv_page,
- READ, false) == 0)
- md_error(mddev, rdev);
+ if (r1_sync_page_io(rdev, sect, s,
+ bio->bi_io_vec[idx].bv_page,
+ READ) != 0)
+ atomic_add(s, &rdev->corrected_errors);
}
sectors -= s;
sect += s;
@@ -1289,7 +1570,7 @@ static int fix_sync_read_error(r1bio_t *r1_bio)
return 1;
}
-static int process_checks(r1bio_t *r1_bio)
+static int process_checks(struct r1bio *r1_bio)
{
/* We have read all readable devices. If we haven't
* got the block, then there is no hope left.
@@ -1298,8 +1579,8 @@ static int process_checks(r1bio_t *r1_bio)
* If any blocks failed to read, then we need to
* attempt an over-write
*/
- mddev_t *mddev = r1_bio->mddev;
- conf_t *conf = mddev->private;
+ struct mddev *mddev = r1_bio->mddev;
+ struct r1conf *conf = mddev->private;
int primary;
int i;
@@ -1371,9 +1652,9 @@ static int process_checks(r1bio_t *r1_bio)
return 0;
}
-static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio)
+static void sync_request_write(struct mddev *mddev, struct r1bio *r1_bio)
{
- conf_t *conf = mddev->private;
+ struct r1conf *conf = mddev->private;
int i;
int disks = conf->raid_disks;
struct bio *bio, *wbio;
@@ -1420,19 +1701,19 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio)
*
* 1. Retries failed read operations on working mirrors.
* 2. Updates the raid superblock when problems encounter.
- * 3. Performs writes following reads for array syncronising.
+ * 3. Performs writes following reads for array synchronising.
*/
-static void fix_read_error(conf_t *conf, int read_disk,
+static void fix_read_error(struct r1conf *conf, int read_disk,
sector_t sect, int sectors)
{
- mddev_t *mddev = conf->mddev;
+ struct mddev *mddev = conf->mddev;
while(sectors) {
int s = sectors;
int d = read_disk;
int success = 0;
int start;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
if (s > (PAGE_SIZE>>9))
s = PAGE_SIZE >> 9;
@@ -1443,9 +1724,14 @@ static void fix_read_error(conf_t *conf, int read_disk,
* which is the thread that might remove
* a device. If raid1d ever becomes multi-threaded....
*/
+ sector_t first_bad;
+ int bad_sectors;
+
rdev = conf->mirrors[d].rdev;
if (rdev &&
test_bit(In_sync, &rdev->flags) &&
+ is_badblock(rdev, sect, s,
+ &first_bad, &bad_sectors) == 0 &&
sync_page_io(rdev, sect, s<<9,
conf->tmppage, READ, false))
success = 1;
@@ -1457,8 +1743,10 @@ static void fix_read_error(conf_t *conf, int read_disk,
} while (!success && d != read_disk);
if (!success) {
- /* Cannot read from anywhere -- bye bye array */
- md_error(mddev, conf->mirrors[read_disk].rdev);
+ /* Cannot read from anywhere - mark it bad */
+ struct md_rdev *rdev = conf->mirrors[read_disk].rdev;
+ if (!rdev_set_badblocks(rdev, sect, s, 0))
+ md_error(mddev, rdev);
break;
}
/* write it back and re-read */
@@ -1469,13 +1757,9 @@ static void fix_read_error(conf_t *conf, int read_disk,
d--;
rdev = conf->mirrors[d].rdev;
if (rdev &&
- test_bit(In_sync, &rdev->flags)) {
- if (sync_page_io(rdev, sect, s<<9,
- conf->tmppage, WRITE, false)
- == 0)
- /* Well, this device is dead */
- md_error(mddev, rdev);
- }
+ test_bit(In_sync, &rdev->flags))
+ r1_sync_page_io(rdev, sect, s,
+ conf->tmppage, WRITE);
}
d = start;
while (d != read_disk) {
@@ -1486,12 +1770,8 @@ static void fix_read_error(conf_t *conf, int read_disk,
rdev = conf->mirrors[d].rdev;
if (rdev &&
test_bit(In_sync, &rdev->flags)) {
- if (sync_page_io(rdev, sect, s<<9,
- conf->tmppage, READ, false)
- == 0)
- /* Well, this device is dead */
- md_error(mddev, rdev);
- else {
+ if (r1_sync_page_io(rdev, sect, s,
+ conf->tmppage, READ)) {
atomic_add(s, &rdev->corrected_errors);
printk(KERN_INFO
"md/raid1:%s: read error corrected "
@@ -1508,21 +1788,255 @@ static void fix_read_error(conf_t *conf, int read_disk,
}
}
-static void raid1d(mddev_t *mddev)
+static void bi_complete(struct bio *bio, int error)
+{
+ complete((struct completion *)bio->bi_private);
+}
+
+static int submit_bio_wait(int rw, struct bio *bio)
+{
+ struct completion event;
+ rw |= REQ_SYNC;
+
+ init_completion(&event);
+ bio->bi_private = &event;
+ bio->bi_end_io = bi_complete;
+ submit_bio(rw, bio);
+ wait_for_completion(&event);
+
+ return test_bit(BIO_UPTODATE, &bio->bi_flags);
+}
+
+static int narrow_write_error(struct r1bio *r1_bio, int i)
+{
+ struct mddev *mddev = r1_bio->mddev;
+ struct r1conf *conf = mddev->private;
+ struct md_rdev *rdev = conf->mirrors[i].rdev;
+ int vcnt, idx;
+ struct bio_vec *vec;
+
+ /* bio has the data to be written to device 'i' where
+ * we just recently had a write error.
+ * We repeatedly clone the bio and trim down to one block,
+ * then try the write. Where the write fails we record
+ * a bad block.
+ * It is conceivable that the bio doesn't exactly align with
+ * blocks. We must handle this somehow.
+ *
+ * We currently own a reference on the rdev.
+ */
+
+ int block_sectors;
+ sector_t sector;
+ int sectors;
+ int sect_to_write = r1_bio->sectors;
+ int ok = 1;
+
+ if (rdev->badblocks.shift < 0)
+ return 0;
+
+ block_sectors = 1 << rdev->badblocks.shift;
+ sector = r1_bio->sector;
+ sectors = ((sector + block_sectors)
+ & ~(sector_t)(block_sectors - 1))
+ - sector;
+
+ if (test_bit(R1BIO_BehindIO, &r1_bio->state)) {
+ vcnt = r1_bio->behind_page_count;
+ vec = r1_bio->behind_bvecs;
+ idx = 0;
+ while (vec[idx].bv_page == NULL)
+ idx++;
+ } else {
+ vcnt = r1_bio->master_bio->bi_vcnt;
+ vec = r1_bio->master_bio->bi_io_vec;
+ idx = r1_bio->master_bio->bi_idx;
+ }
+ while (sect_to_write) {
+ struct bio *wbio;
+ if (sectors > sect_to_write)
+ sectors = sect_to_write;
+ /* Write at 'sector' for 'sectors'*/
+
+ wbio = bio_alloc_mddev(GFP_NOIO, vcnt, mddev);
+ memcpy(wbio->bi_io_vec, vec, vcnt * sizeof(struct bio_vec));
+ wbio->bi_sector = r1_bio->sector;
+ wbio->bi_rw = WRITE;
+ wbio->bi_vcnt = vcnt;
+ wbio->bi_size = r1_bio->sectors << 9;
+ wbio->bi_idx = idx;
+
+ md_trim_bio(wbio, sector - r1_bio->sector, sectors);
+ wbio->bi_sector += rdev->data_offset;
+ wbio->bi_bdev = rdev->bdev;
+ if (submit_bio_wait(WRITE, wbio) == 0)
+ /* failure! */
+ ok = rdev_set_badblocks(rdev, sector,
+ sectors, 0)
+ && ok;
+
+ bio_put(wbio);
+ sect_to_write -= sectors;
+ sector += sectors;
+ sectors = block_sectors;
+ }
+ return ok;
+}
+
+static void handle_sync_write_finished(struct r1conf *conf, struct r1bio *r1_bio)
+{
+ int m;
+ int s = r1_bio->sectors;
+ for (m = 0; m < conf->raid_disks ; m++) {
+ struct md_rdev *rdev = conf->mirrors[m].rdev;
+ struct bio *bio = r1_bio->bios[m];
+ if (bio->bi_end_io == NULL)
+ continue;
+ if (test_bit(BIO_UPTODATE, &bio->bi_flags) &&
+ test_bit(R1BIO_MadeGood, &r1_bio->state)) {
+ rdev_clear_badblocks(rdev, r1_bio->sector, s);
+ }
+ if (!test_bit(BIO_UPTODATE, &bio->bi_flags) &&
+ test_bit(R1BIO_WriteError, &r1_bio->state)) {
+ if (!rdev_set_badblocks(rdev, r1_bio->sector, s, 0))
+ md_error(conf->mddev, rdev);
+ }
+ }
+ put_buf(r1_bio);
+ md_done_sync(conf->mddev, s, 1);
+}
+
+static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio)
+{
+ int m;
+ for (m = 0; m < conf->raid_disks ; m++)
+ if (r1_bio->bios[m] == IO_MADE_GOOD) {
+ struct md_rdev *rdev = conf->mirrors[m].rdev;
+ rdev_clear_badblocks(rdev,
+ r1_bio->sector,
+ r1_bio->sectors);
+ rdev_dec_pending(rdev, conf->mddev);
+ } else if (r1_bio->bios[m] != NULL) {
+ /* This drive got a write error. We need to
+ * narrow down and record precise write
+ * errors.
+ */
+ if (!narrow_write_error(r1_bio, m)) {
+ md_error(conf->mddev,
+ conf->mirrors[m].rdev);
+ /* an I/O failed, we can't clear the bitmap */
+ set_bit(R1BIO_Degraded, &r1_bio->state);
+ }
+ rdev_dec_pending(conf->mirrors[m].rdev,
+ conf->mddev);
+ }
+ if (test_bit(R1BIO_WriteError, &r1_bio->state))
+ close_write(r1_bio);
+ raid_end_bio_io(r1_bio);
+}
+
+static void handle_read_error(struct r1conf *conf, struct r1bio *r1_bio)
{
- r1bio_t *r1_bio;
+ int disk;
+ int max_sectors;
+ struct mddev *mddev = conf->mddev;
struct bio *bio;
+ char b[BDEVNAME_SIZE];
+ struct md_rdev *rdev;
+
+ clear_bit(R1BIO_ReadError, &r1_bio->state);
+ /* we got a read error. Maybe the drive is bad. Maybe just
+ * the block and we can fix it.
+ * We freeze all other IO, and try reading the block from
+ * other devices. When we find one, we re-write
+ * and check it that fixes the read error.
+ * This is all done synchronously while the array is
+ * frozen
+ */
+ if (mddev->ro == 0) {
+ freeze_array(conf);
+ fix_read_error(conf, r1_bio->read_disk,
+ r1_bio->sector, r1_bio->sectors);
+ unfreeze_array(conf);
+ } else
+ md_error(mddev, conf->mirrors[r1_bio->read_disk].rdev);
+
+ bio = r1_bio->bios[r1_bio->read_disk];
+ bdevname(bio->bi_bdev, b);
+read_more:
+ disk = read_balance(conf, r1_bio, &max_sectors);
+ if (disk == -1) {
+ printk(KERN_ALERT "md/raid1:%s: %s: unrecoverable I/O"
+ " read error for block %llu\n",
+ mdname(mddev), b, (unsigned long long)r1_bio->sector);
+ raid_end_bio_io(r1_bio);
+ } else {
+ const unsigned long do_sync
+ = r1_bio->master_bio->bi_rw & REQ_SYNC;
+ if (bio) {
+ r1_bio->bios[r1_bio->read_disk] =
+ mddev->ro ? IO_BLOCKED : NULL;
+ bio_put(bio);
+ }
+ r1_bio->read_disk = disk;
+ bio = bio_clone_mddev(r1_bio->master_bio, GFP_NOIO, mddev);
+ md_trim_bio(bio, r1_bio->sector - bio->bi_sector, max_sectors);
+ r1_bio->bios[r1_bio->read_disk] = bio;
+ rdev = conf->mirrors[disk].rdev;
+ printk_ratelimited(KERN_ERR
+ "md/raid1:%s: redirecting sector %llu"
+ " to other mirror: %s\n",
+ mdname(mddev),
+ (unsigned long long)r1_bio->sector,
+ bdevname(rdev->bdev, b));
+ bio->bi_sector = r1_bio->sector + rdev->data_offset;
+ bio->bi_bdev = rdev->bdev;
+ bio->bi_end_io = raid1_end_read_request;
+ bio->bi_rw = READ | do_sync;
+ bio->bi_private = r1_bio;
+ if (max_sectors < r1_bio->sectors) {
+ /* Drat - have to split this up more */
+ struct bio *mbio = r1_bio->master_bio;
+ int sectors_handled = (r1_bio->sector + max_sectors
+ - mbio->bi_sector);
+ r1_bio->sectors = max_sectors;
+ spin_lock_irq(&conf->device_lock);
+ if (mbio->bi_phys_segments == 0)
+ mbio->bi_phys_segments = 2;
+ else
+ mbio->bi_phys_segments++;
+ spin_unlock_irq(&conf->device_lock);
+ generic_make_request(bio);
+ bio = NULL;
+
+ r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO);
+
+ r1_bio->master_bio = mbio;
+ r1_bio->sectors = (mbio->bi_size >> 9)
+ - sectors_handled;
+ r1_bio->state = 0;
+ set_bit(R1BIO_ReadError, &r1_bio->state);
+ r1_bio->mddev = mddev;
+ r1_bio->sector = mbio->bi_sector + sectors_handled;
+
+ goto read_more;
+ } else
+ generic_make_request(bio);
+ }
+}
+
+static void raid1d(struct mddev *mddev)
+{
+ struct r1bio *r1_bio;
unsigned long flags;
- conf_t *conf = mddev->private;
+ struct r1conf *conf = mddev->private;
struct list_head *head = &conf->retry_list;
- mdk_rdev_t *rdev;
struct blk_plug plug;
md_check_recovery(mddev);
blk_start_plug(&plug);
for (;;) {
- char b[BDEVNAME_SIZE];
if (atomic_read(&mddev->plug_cnt) == 0)
flush_pending_writes(conf);
@@ -1532,75 +2046,39 @@ static void raid1d(mddev_t *mddev)
spin_unlock_irqrestore(&conf->device_lock, flags);
break;
}
- r1_bio = list_entry(head->prev, r1bio_t, retry_list);
+ r1_bio = list_entry(head->prev, struct r1bio, retry_list);
list_del(head->prev);
conf->nr_queued--;
spin_unlock_irqrestore(&conf->device_lock, flags);
mddev = r1_bio->mddev;
conf = mddev->private;
- if (test_bit(R1BIO_IsSync, &r1_bio->state))
- sync_request_write(mddev, r1_bio);
- else {
- int disk;
-
- /* we got a read error. Maybe the drive is bad. Maybe just
- * the block and we can fix it.
- * We freeze all other IO, and try reading the block from
- * other devices. When we find one, we re-write
- * and check it that fixes the read error.
- * This is all done synchronously while the array is
- * frozen
+ if (test_bit(R1BIO_IsSync, &r1_bio->state)) {
+ if (test_bit(R1BIO_MadeGood, &r1_bio->state) ||
+ test_bit(R1BIO_WriteError, &r1_bio->state))
+ handle_sync_write_finished(conf, r1_bio);
+ else
+ sync_request_write(mddev, r1_bio);
+ } else if (test_bit(R1BIO_MadeGood, &r1_bio->state) ||
+ test_bit(R1BIO_WriteError, &r1_bio->state))
+ handle_write_finished(conf, r1_bio);
+ else if (test_bit(R1BIO_ReadError, &r1_bio->state))
+ handle_read_error(conf, r1_bio);
+ else
+ /* just a partial read to be scheduled from separate
+ * context
*/
- if (mddev->ro == 0) {
- freeze_array(conf);
- fix_read_error(conf, r1_bio->read_disk,
- r1_bio->sector,
- r1_bio->sectors);
- unfreeze_array(conf);
- } else
- md_error(mddev,
- conf->mirrors[r1_bio->read_disk].rdev);
-
- bio = r1_bio->bios[r1_bio->read_disk];
- if ((disk=read_balance(conf, r1_bio)) == -1) {
- printk(KERN_ALERT "md/raid1:%s: %s: unrecoverable I/O"
- " read error for block %llu\n",
- mdname(mddev),
- bdevname(bio->bi_bdev,b),
- (unsigned long long)r1_bio->sector);
- raid_end_bio_io(r1_bio);
- } else {
- const unsigned long do_sync = r1_bio->master_bio->bi_rw & REQ_SYNC;
- r1_bio->bios[r1_bio->read_disk] =
- mddev->ro ? IO_BLOCKED : NULL;
- r1_bio->read_disk = disk;
- bio_put(bio);
- bio = bio_clone_mddev(r1_bio->master_bio,
- GFP_NOIO, mddev);
- r1_bio->bios[r1_bio->read_disk] = bio;
- rdev = conf->mirrors[disk].rdev;
- if (printk_ratelimit())
- printk(KERN_ERR "md/raid1:%s: redirecting sector %llu to"
- " other mirror: %s\n",
- mdname(mddev),
- (unsigned long long)r1_bio->sector,
- bdevname(rdev->bdev,b));
- bio->bi_sector = r1_bio->sector + rdev->data_offset;
- bio->bi_bdev = rdev->bdev;
- bio->bi_end_io = raid1_end_read_request;
- bio->bi_rw = READ | do_sync;
- bio->bi_private = r1_bio;
- generic_make_request(bio);
- }
- }
+ generic_make_request(r1_bio->bios[r1_bio->read_disk]);
+
cond_resched();
+ if (mddev->flags & ~(1<<MD_CHANGE_PENDING))
+ md_check_recovery(mddev);
}
blk_finish_plug(&plug);
}
-static int init_resync(conf_t *conf)
+static int init_resync(struct r1conf *conf)
{
int buffs;
@@ -1624,10 +2102,10 @@ static int init_resync(conf_t *conf)
* that can be installed to exclude normal IO requests.
*/
-static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, int go_faster)
+static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipped, int go_faster)
{
- conf_t *conf = mddev->private;
- r1bio_t *r1_bio;
+ struct r1conf *conf = mddev->private;
+ struct r1bio *r1_bio;
struct bio *bio;
sector_t max_sector, nr_sectors;
int disk = -1;
@@ -1636,6 +2114,8 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
int write_targets = 0, read_targets = 0;
sector_t sync_blocks;
int still_degraded = 0;
+ int good_sectors = RESYNC_SECTORS;
+ int min_bad = 0; /* number of sectors that are bad in all devices */
if (!conf->r1buf_pool)
if (init_resync(conf))
@@ -1705,14 +2185,13 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
set_bit(R1BIO_IsSync, &r1_bio->state);
for (i=0; i < conf->raid_disks; i++) {
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
bio = r1_bio->bios[i];
/* take from bio_init */
bio->bi_next = NULL;
bio->bi_flags &= ~(BIO_POOL_MASK-1);
bio->bi_flags |= 1 << BIO_UPTODATE;
- bio->bi_comp_cpu = -1;
bio->bi_rw = READ;
bio->bi_vcnt = 0;
bio->bi_idx = 0;
@@ -1723,36 +2202,89 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
rdev = rcu_dereference(conf->mirrors[i].rdev);
if (rdev == NULL ||
- test_bit(Faulty, &rdev->flags)) {
+ test_bit(Faulty, &rdev->flags)) {
still_degraded = 1;
- continue;
} else if (!test_bit(In_sync, &rdev->flags)) {
bio->bi_rw = WRITE;
bio->bi_end_io = end_sync_write;
write_targets ++;
} else {
/* may need to read from here */
- bio->bi_rw = READ;
- bio->bi_end_io = end_sync_read;
- if (test_bit(WriteMostly, &rdev->flags)) {
- if (wonly < 0)
- wonly = i;
- } else {
- if (disk < 0)
- disk = i;
+ sector_t first_bad = MaxSector;
+ int bad_sectors;
+
+ if (is_badblock(rdev, sector_nr, good_sectors,
+ &first_bad, &bad_sectors)) {
+ if (first_bad > sector_nr)
+ good_sectors = first_bad - sector_nr;
+ else {
+ bad_sectors -= (sector_nr - first_bad);
+ if (min_bad == 0 ||
+ min_bad > bad_sectors)
+ min_bad = bad_sectors;
+ }
+ }
+ if (sector_nr < first_bad) {
+ if (test_bit(WriteMostly, &rdev->flags)) {
+ if (wonly < 0)
+ wonly = i;
+ } else {
+ if (disk < 0)
+ disk = i;
+ }
+ bio->bi_rw = READ;
+ bio->bi_end_io = end_sync_read;
+ read_targets++;
}
- read_targets++;
}
- atomic_inc(&rdev->nr_pending);
- bio->bi_sector = sector_nr + rdev->data_offset;
- bio->bi_bdev = rdev->bdev;
- bio->bi_private = r1_bio;
+ if (bio->bi_end_io) {
+ atomic_inc(&rdev->nr_pending);
+ bio->bi_sector = sector_nr + rdev->data_offset;
+ bio->bi_bdev = rdev->bdev;
+ bio->bi_private = r1_bio;
+ }
}
rcu_read_unlock();
if (disk < 0)
disk = wonly;
r1_bio->read_disk = disk;
+ if (read_targets == 0 && min_bad > 0) {
+ /* These sectors are bad on all InSync devices, so we
+ * need to mark them bad on all write targets
+ */
+ int ok = 1;
+ for (i = 0 ; i < conf->raid_disks ; i++)
+ if (r1_bio->bios[i]->bi_end_io == end_sync_write) {
+ struct md_rdev *rdev =
+ rcu_dereference(conf->mirrors[i].rdev);
+ ok = rdev_set_badblocks(rdev, sector_nr,
+ min_bad, 0
+ ) && ok;
+ }
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
+ *skipped = 1;
+ put_buf(r1_bio);
+
+ if (!ok) {
+ /* Cannot record the badblocks, so need to
+ * abort the resync.
+ * If there are multiple read targets, could just
+ * fail the really bad ones ???
+ */
+ conf->recovery_disabled = mddev->recovery_disabled;
+ set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+ return 0;
+ } else
+ return min_bad;
+
+ }
+ if (min_bad > 0 && min_bad < good_sectors) {
+ /* only resync enough to reach the next bad->good
+ * transition */
+ good_sectors = min_bad;
+ }
+
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) && read_targets > 0)
/* extra read targets are also write targets */
write_targets += read_targets-1;
@@ -1769,6 +2301,8 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
if (max_sector > mddev->resync_max)
max_sector = mddev->resync_max; /* Don't do IO beyond here */
+ if (max_sector > sector_nr + good_sectors)
+ max_sector = sector_nr + good_sectors;
nr_sectors = 0;
sync_blocks = 0;
do {
@@ -1839,7 +2373,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
return nr_sectors;
}
-static sector_t raid1_size(mddev_t *mddev, sector_t sectors, int raid_disks)
+static sector_t raid1_size(struct mddev *mddev, sector_t sectors, int raid_disks)
{
if (sectors)
return sectors;
@@ -1847,15 +2381,15 @@ static sector_t raid1_size(mddev_t *mddev, sector_t sectors, int raid_disks)
return mddev->dev_sectors;
}
-static conf_t *setup_conf(mddev_t *mddev)
+static struct r1conf *setup_conf(struct mddev *mddev)
{
- conf_t *conf;
+ struct r1conf *conf;
int i;
- mirror_info_t *disk;
- mdk_rdev_t *rdev;
+ struct mirror_info *disk;
+ struct md_rdev *rdev;
int err = -ENOMEM;
- conf = kzalloc(sizeof(conf_t), GFP_KERNEL);
+ conf = kzalloc(sizeof(struct r1conf), GFP_KERNEL);
if (!conf)
goto abort;
@@ -1900,6 +2434,8 @@ static conf_t *setup_conf(mddev_t *mddev)
init_waitqueue_head(&conf->wait_barrier);
bio_list_init(&conf->pending_bio_list);
+ conf->pending_count = 0;
+ conf->recovery_disabled = mddev->recovery_disabled - 1;
conf->last_used = -1;
for (i = 0; i < conf->raid_disks; i++) {
@@ -1948,11 +2484,11 @@ static conf_t *setup_conf(mddev_t *mddev)
return ERR_PTR(err);
}
-static int run(mddev_t *mddev)
+static int run(struct mddev *mddev)
{
- conf_t *conf;
+ struct r1conf *conf;
int i;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
if (mddev->level != 1) {
printk(KERN_ERR "md/raid1:%s: raid level not set to mirroring (%d)\n",
@@ -2028,9 +2564,9 @@ static int run(mddev_t *mddev)
return md_integrity_register(mddev);
}
-static int stop(mddev_t *mddev)
+static int stop(struct mddev *mddev)
{
- conf_t *conf = mddev->private;
+ struct r1conf *conf = mddev->private;
struct bitmap *bitmap = mddev->bitmap;
/* wait for behind writes to complete */
@@ -2045,8 +2581,7 @@ static int stop(mddev_t *mddev)
raise_barrier(conf);
lower_barrier(conf);
- md_unregister_thread(mddev->thread);
- mddev->thread = NULL;
+ md_unregister_thread(&mddev->thread);
if (conf->r1bio_pool)
mempool_destroy(conf->r1bio_pool);
kfree(conf->mirrors);
@@ -2056,7 +2591,7 @@ static int stop(mddev_t *mddev)
return 0;
}
-static int raid1_resize(mddev_t *mddev, sector_t sectors)
+static int raid1_resize(struct mddev *mddev, sector_t sectors)
{
/* no resync is happening, and there is enough space
* on all devices, so we can resize.
@@ -2080,7 +2615,7 @@ static int raid1_resize(mddev_t *mddev, sector_t sectors)
return 0;
}
-static int raid1_reshape(mddev_t *mddev)
+static int raid1_reshape(struct mddev *mddev)
{
/* We need to:
* 1/ resize the r1bio_pool
@@ -2095,8 +2630,8 @@ static int raid1_reshape(mddev_t *mddev)
*/
mempool_t *newpool, *oldpool;
struct pool_info *newpoolinfo;
- mirror_info_t *newmirrors;
- conf_t *conf = mddev->private;
+ struct mirror_info *newmirrors;
+ struct r1conf *conf = mddev->private;
int cnt, raid_disks;
unsigned long flags;
int d, d2, err;
@@ -2152,20 +2687,15 @@ static int raid1_reshape(mddev_t *mddev)
conf->r1bio_pool = newpool;
for (d = d2 = 0; d < conf->raid_disks; d++) {
- mdk_rdev_t *rdev = conf->mirrors[d].rdev;
+ struct md_rdev *rdev = conf->mirrors[d].rdev;
if (rdev && rdev->raid_disk != d2) {
- char nm[20];
- sprintf(nm, "rd%d", rdev->raid_disk);
- sysfs_remove_link(&mddev->kobj, nm);
+ sysfs_unlink_rdev(mddev, rdev);
rdev->raid_disk = d2;
- sprintf(nm, "rd%d", rdev->raid_disk);
- sysfs_remove_link(&mddev->kobj, nm);
- if (sysfs_create_link(&mddev->kobj,
- &rdev->kobj, nm))
+ sysfs_unlink_rdev(mddev, rdev);
+ if (sysfs_link_rdev(mddev, rdev))
printk(KERN_WARNING
- "md/raid1:%s: cannot register "
- "%s\n",
- mdname(mddev), nm);
+ "md/raid1:%s: cannot register rd%d\n",
+ mdname(mddev), rdev->raid_disk);
}
if (rdev)
newmirrors[d2++].rdev = rdev;
@@ -2191,9 +2721,9 @@ static int raid1_reshape(mddev_t *mddev)
return 0;
}
-static void raid1_quiesce(mddev_t *mddev, int state)
+static void raid1_quiesce(struct mddev *mddev, int state)
{
- conf_t *conf = mddev->private;
+ struct r1conf *conf = mddev->private;
switch(state) {
case 2: /* wake for suspend */
@@ -2208,13 +2738,13 @@ static void raid1_quiesce(mddev_t *mddev, int state)
}
}
-static void *raid1_takeover(mddev_t *mddev)
+static void *raid1_takeover(struct mddev *mddev)
{
/* raid1 can take over:
* raid5 with 2 devices, any layout or chunk size
*/
if (mddev->level == 5 && mddev->raid_disks == 2) {
- conf_t *conf;
+ struct r1conf *conf;
mddev->new_level = 1;
mddev->new_layout = 0;
mddev->new_chunk_sectors = 0;
@@ -2226,7 +2756,7 @@ static void *raid1_takeover(mddev_t *mddev)
return ERR_PTR(-EINVAL);
}
-static struct mdk_personality raid1_personality =
+static struct md_personality raid1_personality =
{
.name = "raid1",
.level = 1,
@@ -2264,3 +2794,5 @@ MODULE_DESCRIPTION("RAID1 (mirroring) personality for MD");
MODULE_ALIAS("md-personality-3"); /* RAID1 */
MODULE_ALIAS("md-raid1");
MODULE_ALIAS("md-level-1");
+
+module_param(max_queued_requests, int, S_IRUGO|S_IWUSR);
diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
index e743a64fac4f..c732b6cce935 100644
--- a/drivers/md/raid1.h
+++ b/drivers/md/raid1.h
@@ -1,10 +1,8 @@
#ifndef _RAID1_H
#define _RAID1_H
-typedef struct mirror_info mirror_info_t;
-
struct mirror_info {
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
sector_t head_position;
};
@@ -17,55 +15,82 @@ struct mirror_info {
*/
struct pool_info {
- mddev_t *mddev;
+ struct mddev *mddev;
int raid_disks;
};
-
-typedef struct r1bio_s r1bio_t;
-
-struct r1_private_data_s {
- mddev_t *mddev;
- mirror_info_t *mirrors;
+struct r1conf {
+ struct mddev *mddev;
+ struct mirror_info *mirrors;
int raid_disks;
+
+ /* When choose the best device for a read (read_balance())
+ * we try to keep sequential reads one the same device
+ * using 'last_used' and 'next_seq_sect'
+ */
int last_used;
sector_t next_seq_sect;
+ /* During resync, read_balancing is only allowed on the part
+ * of the array that has been resynced. 'next_resync' tells us
+ * where that is.
+ */
+ sector_t next_resync;
+
spinlock_t device_lock;
+ /* list of 'struct r1bio' that need to be processed by raid1d,
+ * whether to retry a read, writeout a resync or recovery
+ * block, or anything else.
+ */
struct list_head retry_list;
- /* queue pending writes and submit them on unplug */
- struct bio_list pending_bio_list;
- /* for use when syncing mirrors: */
+ /* queue pending writes to be submitted on unplug */
+ struct bio_list pending_bio_list;
+ int pending_count;
+ /* for use when syncing mirrors:
+ * We don't allow both normal IO and resync/recovery IO at
+ * the same time - resync/recovery can only happen when there
+ * is no other IO. So when either is active, the other has to wait.
+ * See more details description in raid1.c near raise_barrier().
+ */
+ wait_queue_head_t wait_barrier;
spinlock_t resync_lock;
int nr_pending;
int nr_waiting;
int nr_queued;
int barrier;
- sector_t next_resync;
- int fullsync; /* set to 1 if a full sync is needed,
- * (fresh device added).
- * Cleared when a sync completes.
- */
- wait_queue_head_t wait_barrier;
+ /* Set to 1 if a full sync is needed, (fresh device added).
+ * Cleared when a sync completes.
+ */
+ int fullsync;
+ /* When the same as mddev->recovery_disabled we don't allow
+ * recovery to be attempted as we expect a read error.
+ */
+ int recovery_disabled;
+
+
+ /* poolinfo contains information about the content of the
+ * mempools - it changes when the array grows or shrinks
+ */
struct pool_info *poolinfo;
+ mempool_t *r1bio_pool;
+ mempool_t *r1buf_pool;
+ /* temporary buffer to synchronous IO when attempting to repair
+ * a read error.
+ */
struct page *tmppage;
- mempool_t *r1bio_pool;
- mempool_t *r1buf_pool;
/* When taking over an array from a different personality, we store
* the new thread here until we fully activate the array.
*/
- struct mdk_thread_s *thread;
+ struct md_thread *thread;
};
-typedef struct r1_private_data_s conf_t;
-
/*
* this is our 'private' RAID1 bio.
*
@@ -73,7 +98,7 @@ typedef struct r1_private_data_s conf_t;
* for this RAID1 operation, and about their status:
*/
-struct r1bio_s {
+struct r1bio {
atomic_t remaining; /* 'have we finished' count,
* used from IRQ handlers
*/
@@ -83,7 +108,7 @@ struct r1bio_s {
sector_t sector;
int sectors;
unsigned long state;
- mddev_t *mddev;
+ struct mddev *mddev;
/*
* original bio going to /dev/mdx
*/
@@ -95,7 +120,7 @@ struct r1bio_s {
struct list_head retry_list;
/* Next two are only valid when R1BIO_BehindIO is set */
- struct page **behind_pages;
+ struct bio_vec *behind_bvecs;
int behind_page_count;
/*
* if the IO is in WRITE direction, then multiple bios are used.
@@ -110,13 +135,24 @@ struct r1bio_s {
* correct the read error. To keep track of bad blocks on a per-bio
* level, we store IO_BLOCKED in the appropriate 'bios' pointer
*/
-#define IO_BLOCKED ((struct bio*)1)
+#define IO_BLOCKED ((struct bio *)1)
+/* When we successfully write to a known bad-block, we need to remove the
+ * bad-block marking which must be done from process context. So we record
+ * the success by setting bios[n] to IO_MADE_GOOD
+ */
+#define IO_MADE_GOOD ((struct bio *)2)
+
+#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
/* bits for r1bio.state */
#define R1BIO_Uptodate 0
#define R1BIO_IsSync 1
#define R1BIO_Degraded 2
#define R1BIO_BehindIO 3
+/* Set ReadError on bios that experience a readerror so that
+ * raid1d knows what to do with them.
+ */
+#define R1BIO_ReadError 4
/* For write-behind requests, we call bi_end_io when
* the last non-write-behind device completes, providing
* any write was successful. Otherwise we call when
@@ -125,7 +161,12 @@ struct r1bio_s {
* Record that bi_end_io was called with this flag...
*/
#define R1BIO_Returned 6
+/* If a write for this request means we can clear some
+ * known-bad-block records, we set this flag
+ */
+#define R1BIO_MadeGood 7
+#define R1BIO_WriteError 8
-extern int md_raid1_congested(mddev_t *mddev, int bits);
+extern int md_raid1_congested(struct mddev *mddev, int bits);
#endif
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 6e846688962f..685ddf325ee4 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -21,7 +21,9 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/blkdev.h>
+#include <linux/module.h>
#include <linux/seq_file.h>
+#include <linux/ratelimit.h>
#include "md.h"
#include "raid10.h"
#include "raid0.h"
@@ -57,13 +59,19 @@
*/
#define NR_RAID10_BIOS 256
-static void allow_barrier(conf_t *conf);
-static void lower_barrier(conf_t *conf);
+/* When there are this many requests queue to be written by
+ * the raid10 thread, we become 'congested' to provide back-pressure
+ * for writeback.
+ */
+static int max_queued_requests = 1024;
+
+static void allow_barrier(struct r10conf *conf);
+static void lower_barrier(struct r10conf *conf);
static void * r10bio_pool_alloc(gfp_t gfp_flags, void *data)
{
- conf_t *conf = data;
- int size = offsetof(struct r10bio_s, devs[conf->copies]);
+ struct r10conf *conf = data;
+ int size = offsetof(struct r10bio, devs[conf->copies]);
/* allocate a r10bio with room for raid_disks entries in the bios array */
return kzalloc(size, gfp_flags);
@@ -91,9 +99,9 @@ static void r10bio_pool_free(void *r10_bio, void *data)
*/
static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
{
- conf_t *conf = data;
+ struct r10conf *conf = data;
struct page *page;
- r10bio_t *r10_bio;
+ struct r10bio *r10_bio;
struct bio *bio;
int i, j;
int nalloc;
@@ -123,7 +131,14 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
for (j = 0 ; j < nalloc; j++) {
bio = r10_bio->devs[j].bio;
for (i = 0; i < RESYNC_PAGES; i++) {
- page = alloc_page(gfp_flags);
+ if (j == 1 && !test_bit(MD_RECOVERY_SYNC,
+ &conf->mddev->recovery)) {
+ /* we can share bv_page's during recovery */
+ struct bio *rbio = r10_bio->devs[0].bio;
+ page = rbio->bi_io_vec[i].bv_page;
+ get_page(page);
+ } else
+ page = alloc_page(gfp_flags);
if (unlikely(!page))
goto out_free_pages;
@@ -150,8 +165,8 @@ out_free_bio:
static void r10buf_pool_free(void *__r10_bio, void *data)
{
int i;
- conf_t *conf = data;
- r10bio_t *r10bio = __r10_bio;
+ struct r10conf *conf = data;
+ struct r10bio *r10bio = __r10_bio;
int j;
for (j=0; j < conf->copies; j++) {
@@ -167,46 +182,40 @@ static void r10buf_pool_free(void *__r10_bio, void *data)
r10bio_pool_free(r10bio, conf);
}
-static void put_all_bios(conf_t *conf, r10bio_t *r10_bio)
+static void put_all_bios(struct r10conf *conf, struct r10bio *r10_bio)
{
int i;
for (i = 0; i < conf->copies; i++) {
struct bio **bio = & r10_bio->devs[i].bio;
- if (*bio && *bio != IO_BLOCKED)
+ if (!BIO_SPECIAL(*bio))
bio_put(*bio);
*bio = NULL;
}
}
-static void free_r10bio(r10bio_t *r10_bio)
+static void free_r10bio(struct r10bio *r10_bio)
{
- conf_t *conf = r10_bio->mddev->private;
-
- /*
- * Wake up any possible resync thread that waits for the device
- * to go idle.
- */
- allow_barrier(conf);
+ struct r10conf *conf = r10_bio->mddev->private;
put_all_bios(conf, r10_bio);
mempool_free(r10_bio, conf->r10bio_pool);
}
-static void put_buf(r10bio_t *r10_bio)
+static void put_buf(struct r10bio *r10_bio)
{
- conf_t *conf = r10_bio->mddev->private;
+ struct r10conf *conf = r10_bio->mddev->private;
mempool_free(r10_bio, conf->r10buf_pool);
lower_barrier(conf);
}
-static void reschedule_retry(r10bio_t *r10_bio)
+static void reschedule_retry(struct r10bio *r10_bio)
{
unsigned long flags;
- mddev_t *mddev = r10_bio->mddev;
- conf_t *conf = mddev->private;
+ struct mddev *mddev = r10_bio->mddev;
+ struct r10conf *conf = mddev->private;
spin_lock_irqsave(&conf->device_lock, flags);
list_add(&r10_bio->retry_list, &conf->retry_list);
@@ -224,32 +233,70 @@ static void reschedule_retry(r10bio_t *r10_bio)
* operation and are ready to return a success/failure code to the buffer
* cache layer.
*/
-static void raid_end_bio_io(r10bio_t *r10_bio)
+static void raid_end_bio_io(struct r10bio *r10_bio)
{
struct bio *bio = r10_bio->master_bio;
+ int done;
+ struct r10conf *conf = r10_bio->mddev->private;
- bio_endio(bio,
- test_bit(R10BIO_Uptodate, &r10_bio->state) ? 0 : -EIO);
+ if (bio->bi_phys_segments) {
+ unsigned long flags;
+ spin_lock_irqsave(&conf->device_lock, flags);
+ bio->bi_phys_segments--;
+ done = (bio->bi_phys_segments == 0);
+ spin_unlock_irqrestore(&conf->device_lock, flags);
+ } else
+ done = 1;
+ if (!test_bit(R10BIO_Uptodate, &r10_bio->state))
+ clear_bit(BIO_UPTODATE, &bio->bi_flags);
+ if (done) {
+ bio_endio(bio, 0);
+ /*
+ * Wake up any possible resync thread that waits for the device
+ * to go idle.
+ */
+ allow_barrier(conf);
+ }
free_r10bio(r10_bio);
}
/*
* Update disk head position estimator based on IRQ completion info.
*/
-static inline void update_head_pos(int slot, r10bio_t *r10_bio)
+static inline void update_head_pos(int slot, struct r10bio *r10_bio)
{
- conf_t *conf = r10_bio->mddev->private;
+ struct r10conf *conf = r10_bio->mddev->private;
conf->mirrors[r10_bio->devs[slot].devnum].head_position =
r10_bio->devs[slot].addr + (r10_bio->sectors);
}
+/*
+ * Find the disk number which triggered given bio
+ */
+static int find_bio_disk(struct r10conf *conf, struct r10bio *r10_bio,
+ struct bio *bio, int *slotp)
+{
+ int slot;
+
+ for (slot = 0; slot < conf->copies; slot++)
+ if (r10_bio->devs[slot].bio == bio)
+ break;
+
+ BUG_ON(slot == conf->copies);
+ update_head_pos(slot, r10_bio);
+
+ if (slotp)
+ *slotp = slot;
+ return r10_bio->devs[slot].devnum;
+}
+
static void raid10_end_read_request(struct bio *bio, int error)
{
int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
- r10bio_t *r10_bio = bio->bi_private;
+ struct r10bio *r10_bio = bio->bi_private;
int slot, dev;
- conf_t *conf = r10_bio->mddev->private;
+ struct r10conf *conf = r10_bio->mddev->private;
slot = r10_bio->read_slot;
@@ -277,34 +324,60 @@ static void raid10_end_read_request(struct bio *bio, int error)
* oops, read error - keep the refcount on the rdev
*/
char b[BDEVNAME_SIZE];
- if (printk_ratelimit())
- printk(KERN_ERR "md/raid10:%s: %s: rescheduling sector %llu\n",
- mdname(conf->mddev),
- bdevname(conf->mirrors[dev].rdev->bdev,b), (unsigned long long)r10_bio->sector);
+ printk_ratelimited(KERN_ERR
+ "md/raid10:%s: %s: rescheduling sector %llu\n",
+ mdname(conf->mddev),
+ bdevname(conf->mirrors[dev].rdev->bdev, b),
+ (unsigned long long)r10_bio->sector);
+ set_bit(R10BIO_ReadError, &r10_bio->state);
reschedule_retry(r10_bio);
}
}
+static void close_write(struct r10bio *r10_bio)
+{
+ /* clear the bitmap if all writes complete successfully */
+ bitmap_endwrite(r10_bio->mddev->bitmap, r10_bio->sector,
+ r10_bio->sectors,
+ !test_bit(R10BIO_Degraded, &r10_bio->state),
+ 0);
+ md_write_end(r10_bio->mddev);
+}
+
+static void one_write_done(struct r10bio *r10_bio)
+{
+ if (atomic_dec_and_test(&r10_bio->remaining)) {
+ if (test_bit(R10BIO_WriteError, &r10_bio->state))
+ reschedule_retry(r10_bio);
+ else {
+ close_write(r10_bio);
+ if (test_bit(R10BIO_MadeGood, &r10_bio->state))
+ reschedule_retry(r10_bio);
+ else
+ raid_end_bio_io(r10_bio);
+ }
+ }
+}
+
static void raid10_end_write_request(struct bio *bio, int error)
{
int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
- r10bio_t *r10_bio = bio->bi_private;
- int slot, dev;
- conf_t *conf = r10_bio->mddev->private;
+ struct r10bio *r10_bio = bio->bi_private;
+ int dev;
+ int dec_rdev = 1;
+ struct r10conf *conf = r10_bio->mddev->private;
+ int slot;
- for (slot = 0; slot < conf->copies; slot++)
- if (r10_bio->devs[slot].bio == bio)
- break;
- dev = r10_bio->devs[slot].devnum;
+ dev = find_bio_disk(conf, r10_bio, bio, &slot);
/*
* this branch is our 'one mirror IO has finished' event handler:
*/
if (!uptodate) {
- md_error(r10_bio->mddev, conf->mirrors[dev].rdev);
- /* an I/O failed, we can't clear the bitmap */
- set_bit(R10BIO_Degraded, &r10_bio->state);
- } else
+ set_bit(WriteErrorSeen, &conf->mirrors[dev].rdev->flags);
+ set_bit(R10BIO_WriteError, &r10_bio->state);
+ dec_rdev = 0;
+ } else {
/*
* Set R10BIO_Uptodate in our master bio, so that
* we will return a good error code for to the higher
@@ -314,26 +387,31 @@ static void raid10_end_write_request(struct bio *bio, int error)
* user-side. So if something waits for IO, then it will
* wait for the 'master' bio.
*/
+ sector_t first_bad;
+ int bad_sectors;
+
set_bit(R10BIO_Uptodate, &r10_bio->state);
- update_head_pos(slot, r10_bio);
+ /* Maybe we can clear some bad blocks. */
+ if (is_badblock(conf->mirrors[dev].rdev,
+ r10_bio->devs[slot].addr,
+ r10_bio->sectors,
+ &first_bad, &bad_sectors)) {
+ bio_put(bio);
+ r10_bio->devs[slot].bio = IO_MADE_GOOD;
+ dec_rdev = 0;
+ set_bit(R10BIO_MadeGood, &r10_bio->state);
+ }
+ }
/*
*
* Let's see if all mirrored write operations have finished
* already.
*/
- if (atomic_dec_and_test(&r10_bio->remaining)) {
- /* clear the bitmap if all writes complete successfully */
- bitmap_endwrite(r10_bio->mddev->bitmap, r10_bio->sector,
- r10_bio->sectors,
- !test_bit(R10BIO_Degraded, &r10_bio->state),
- 0);
- md_write_end(r10_bio->mddev);
- raid_end_bio_io(r10_bio);
- }
-
- rdev_dec_pending(conf->mirrors[dev].rdev, conf->mddev);
+ one_write_done(r10_bio);
+ if (dec_rdev)
+ rdev_dec_pending(conf->mirrors[dev].rdev, conf->mddev);
}
@@ -362,7 +440,7 @@ static void raid10_end_write_request(struct bio *bio, int error)
* sector offset to a virtual address
*/
-static void raid10_find_phys(conf_t *conf, r10bio_t *r10bio)
+static void raid10_find_phys(struct r10conf *conf, struct r10bio *r10bio)
{
int n,f;
sector_t sector;
@@ -410,7 +488,7 @@ static void raid10_find_phys(conf_t *conf, r10bio_t *r10bio)
BUG_ON(slot != conf->copies);
}
-static sector_t raid10_find_virt(conf_t *conf, sector_t sector, int dev)
+static sector_t raid10_find_virt(struct r10conf *conf, sector_t sector, int dev)
{
sector_t offset, chunk, vchunk;
@@ -451,7 +529,7 @@ static int raid10_mergeable_bvec(struct request_queue *q,
struct bvec_merge_data *bvm,
struct bio_vec *biovec)
{
- mddev_t *mddev = q->queuedata;
+ struct mddev *mddev = q->queuedata;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
int max;
unsigned int chunk_sectors = mddev->chunk_sectors;
@@ -484,21 +562,24 @@ static int raid10_mergeable_bvec(struct request_queue *q,
* FIXME: possibly should rethink readbalancing and do it differently
* depending on near_copies / far_copies geometry.
*/
-static int read_balance(conf_t *conf, r10bio_t *r10_bio)
+static int read_balance(struct r10conf *conf, struct r10bio *r10_bio, int *max_sectors)
{
const sector_t this_sector = r10_bio->sector;
int disk, slot;
- const int sectors = r10_bio->sectors;
+ int sectors = r10_bio->sectors;
+ int best_good_sectors;
sector_t new_distance, best_dist;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
int do_balance;
int best_slot;
raid10_find_phys(conf, r10_bio);
rcu_read_lock();
retry:
+ sectors = r10_bio->sectors;
best_slot = -1;
best_dist = MaxSector;
+ best_good_sectors = 0;
do_balance = 1;
/*
* Check if we can balance. We can balance on the whole
@@ -511,6 +592,10 @@ retry:
do_balance = 0;
for (slot = 0; slot < conf->copies ; slot++) {
+ sector_t first_bad;
+ int bad_sectors;
+ sector_t dev_sector;
+
if (r10_bio->devs[slot].bio == IO_BLOCKED)
continue;
disk = r10_bio->devs[slot].devnum;
@@ -520,6 +605,37 @@ retry:
if (!test_bit(In_sync, &rdev->flags))
continue;
+ dev_sector = r10_bio->devs[slot].addr;
+ if (is_badblock(rdev, dev_sector, sectors,
+ &first_bad, &bad_sectors)) {
+ if (best_dist < MaxSector)
+ /* Already have a better slot */
+ continue;
+ if (first_bad <= dev_sector) {
+ /* Cannot read here. If this is the
+ * 'primary' device, then we must not read
+ * beyond 'bad_sectors' from another device.
+ */
+ bad_sectors -= (dev_sector - first_bad);
+ if (!do_balance && sectors > bad_sectors)
+ sectors = bad_sectors;
+ if (best_good_sectors > sectors)
+ best_good_sectors = sectors;
+ } else {
+ sector_t good_sectors =
+ first_bad - dev_sector;
+ if (good_sectors > best_good_sectors) {
+ best_good_sectors = good_sectors;
+ best_slot = slot;
+ }
+ if (!do_balance)
+ /* Must read from here */
+ break;
+ }
+ continue;
+ } else
+ best_good_sectors = sectors;
+
if (!do_balance)
break;
@@ -561,21 +677,26 @@ retry:
} else
disk = -1;
rcu_read_unlock();
+ *max_sectors = best_good_sectors;
return disk;
}
static int raid10_congested(void *data, int bits)
{
- mddev_t *mddev = data;
- conf_t *conf = mddev->private;
+ struct mddev *mddev = data;
+ struct r10conf *conf = mddev->private;
int i, ret = 0;
+ if ((bits & (1 << BDI_async_congested)) &&
+ conf->pending_count >= max_queued_requests)
+ return 1;
+
if (mddev_congested(mddev, bits))
return 1;
rcu_read_lock();
for (i = 0; i < conf->raid_disks && ret == 0; i++) {
- mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev);
+ struct md_rdev *rdev = rcu_dereference(conf->mirrors[i].rdev);
if (rdev && !test_bit(Faulty, &rdev->flags)) {
struct request_queue *q = bdev_get_queue(rdev->bdev);
@@ -586,7 +707,7 @@ static int raid10_congested(void *data, int bits)
return ret;
}
-static void flush_pending_writes(conf_t *conf)
+static void flush_pending_writes(struct r10conf *conf)
{
/* Any writes that have been queued but are awaiting
* bitmap updates get flushed here.
@@ -596,10 +717,12 @@ static void flush_pending_writes(conf_t *conf)
if (conf->pending_bio_list.head) {
struct bio *bio;
bio = bio_list_get(&conf->pending_bio_list);
+ conf->pending_count = 0;
spin_unlock_irq(&conf->device_lock);
/* flush any pending bitmap writes to disk
* before proceeding w/ I/O */
bitmap_unplug(conf->mddev->bitmap);
+ wake_up(&conf->wait_barrier);
while (bio) { /* submit pending writes */
struct bio *next = bio->bi_next;
@@ -633,7 +756,7 @@ static void flush_pending_writes(conf_t *conf)
* lower_barrier when the particular background IO completes.
*/
-static void raise_barrier(conf_t *conf, int force)
+static void raise_barrier(struct r10conf *conf, int force)
{
BUG_ON(force && !conf->barrier);
spin_lock_irq(&conf->resync_lock);
@@ -653,7 +776,7 @@ static void raise_barrier(conf_t *conf, int force)
spin_unlock_irq(&conf->resync_lock);
}
-static void lower_barrier(conf_t *conf)
+static void lower_barrier(struct r10conf *conf)
{
unsigned long flags;
spin_lock_irqsave(&conf->resync_lock, flags);
@@ -662,7 +785,7 @@ static void lower_barrier(conf_t *conf)
wake_up(&conf->wait_barrier);
}
-static void wait_barrier(conf_t *conf)
+static void wait_barrier(struct r10conf *conf)
{
spin_lock_irq(&conf->resync_lock);
if (conf->barrier) {
@@ -676,7 +799,7 @@ static void wait_barrier(conf_t *conf)
spin_unlock_irq(&conf->resync_lock);
}
-static void allow_barrier(conf_t *conf)
+static void allow_barrier(struct r10conf *conf)
{
unsigned long flags;
spin_lock_irqsave(&conf->resync_lock, flags);
@@ -685,7 +808,7 @@ static void allow_barrier(conf_t *conf)
wake_up(&conf->wait_barrier);
}
-static void freeze_array(conf_t *conf)
+static void freeze_array(struct r10conf *conf)
{
/* stop syncio and normal IO and wait for everything to
* go quiet.
@@ -710,7 +833,7 @@ static void freeze_array(conf_t *conf)
spin_unlock_irq(&conf->resync_lock);
}
-static void unfreeze_array(conf_t *conf)
+static void unfreeze_array(struct r10conf *conf)
{
/* reverse the effect of the freeze */
spin_lock_irq(&conf->resync_lock);
@@ -720,11 +843,11 @@ static void unfreeze_array(conf_t *conf)
spin_unlock_irq(&conf->resync_lock);
}
-static int make_request(mddev_t *mddev, struct bio * bio)
+static void make_request(struct mddev *mddev, struct bio * bio)
{
- conf_t *conf = mddev->private;
- mirror_info_t *mirror;
- r10bio_t *r10_bio;
+ struct r10conf *conf = mddev->private;
+ struct mirror_info *mirror;
+ struct r10bio *r10_bio;
struct bio *read_bio;
int i;
int chunk_sects = conf->chunk_mask + 1;
@@ -732,12 +855,14 @@ static int make_request(mddev_t *mddev, struct bio * bio)
const unsigned long do_sync = (bio->bi_rw & REQ_SYNC);
const unsigned long do_fua = (bio->bi_rw & REQ_FUA);
unsigned long flags;
- mdk_rdev_t *blocked_rdev;
+ struct md_rdev *blocked_rdev;
int plugged;
+ int sectors_handled;
+ int max_sectors;
if (unlikely(bio->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bio);
- return 0;
+ return;
}
/* If this request crosses a chunk boundary, we need to
@@ -769,10 +894,8 @@ static int make_request(mddev_t *mddev, struct bio * bio)
conf->nr_waiting++;
spin_unlock_irq(&conf->resync_lock);
- if (make_request(mddev, &bp->bio1))
- generic_make_request(&bp->bio1);
- if (make_request(mddev, &bp->bio2))
- generic_make_request(&bp->bio2);
+ make_request(mddev, &bp->bio1);
+ make_request(mddev, &bp->bio2);
spin_lock_irq(&conf->resync_lock);
conf->nr_waiting--;
@@ -780,14 +903,14 @@ static int make_request(mddev_t *mddev, struct bio * bio)
spin_unlock_irq(&conf->resync_lock);
bio_pair_release(bp);
- return 0;
+ return;
bad_map:
printk("md/raid10:%s: make_request bug: can't convert block across chunks"
" or bigger than %dk %llu %d\n", mdname(mddev), chunk_sects/2,
(unsigned long long)bio->bi_sector, bio->bi_size >> 10);
bio_io_error(bio);
- return 0;
+ return;
}
md_write_start(mddev, bio);
@@ -808,19 +931,35 @@ static int make_request(mddev_t *mddev, struct bio * bio)
r10_bio->sector = bio->bi_sector;
r10_bio->state = 0;
+ /* We might need to issue multiple reads to different
+ * devices if there are bad blocks around, so we keep
+ * track of the number of reads in bio->bi_phys_segments.
+ * If this is 0, there is only one r10_bio and no locking
+ * will be needed when the request completes. If it is
+ * non-zero, then it is the number of not-completed requests.
+ */
+ bio->bi_phys_segments = 0;
+ clear_bit(BIO_SEG_VALID, &bio->bi_flags);
+
if (rw == READ) {
/*
* read balancing logic:
*/
- int disk = read_balance(conf, r10_bio);
- int slot = r10_bio->read_slot;
+ int disk;
+ int slot;
+
+read_again:
+ disk = read_balance(conf, r10_bio, &max_sectors);
+ slot = r10_bio->read_slot;
if (disk < 0) {
raid_end_bio_io(r10_bio);
- return 0;
+ return;
}
mirror = conf->mirrors + disk;
read_bio = bio_clone_mddev(bio, GFP_NOIO, mddev);
+ md_trim_bio(read_bio, r10_bio->sector - bio->bi_sector,
+ max_sectors);
r10_bio->devs[slot].bio = read_bio;
@@ -831,38 +970,124 @@ static int make_request(mddev_t *mddev, struct bio * bio)
read_bio->bi_rw = READ | do_sync;
read_bio->bi_private = r10_bio;
- generic_make_request(read_bio);
- return 0;
+ if (max_sectors < r10_bio->sectors) {
+ /* Could not read all from this device, so we will
+ * need another r10_bio.
+ */
+ sectors_handled = (r10_bio->sectors + max_sectors
+ - bio->bi_sector);
+ r10_bio->sectors = max_sectors;
+ spin_lock_irq(&conf->device_lock);
+ if (bio->bi_phys_segments == 0)
+ bio->bi_phys_segments = 2;
+ else
+ bio->bi_phys_segments++;
+ spin_unlock(&conf->device_lock);
+ /* Cannot call generic_make_request directly
+ * as that will be queued in __generic_make_request
+ * and subsequent mempool_alloc might block
+ * waiting for it. so hand bio over to raid10d.
+ */
+ reschedule_retry(r10_bio);
+
+ r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
+
+ r10_bio->master_bio = bio;
+ r10_bio->sectors = ((bio->bi_size >> 9)
+ - sectors_handled);
+ r10_bio->state = 0;
+ r10_bio->mddev = mddev;
+ r10_bio->sector = bio->bi_sector + sectors_handled;
+ goto read_again;
+ } else
+ generic_make_request(read_bio);
+ return;
}
/*
* WRITE:
*/
+ if (conf->pending_count >= max_queued_requests) {
+ md_wakeup_thread(mddev->thread);
+ wait_event(conf->wait_barrier,
+ conf->pending_count < max_queued_requests);
+ }
/* first select target devices under rcu_lock and
* inc refcount on their rdev. Record them by setting
* bios[x] to bio
+ * If there are known/acknowledged bad blocks on any device
+ * on which we have seen a write error, we want to avoid
+ * writing to those blocks. This potentially requires several
+ * writes to write around the bad blocks. Each set of writes
+ * gets its own r10_bio with a set of bios attached. The number
+ * of r10_bios is recored in bio->bi_phys_segments just as with
+ * the read case.
*/
plugged = mddev_check_plugged(mddev);
raid10_find_phys(conf, r10_bio);
- retry_write:
+retry_write:
blocked_rdev = NULL;
rcu_read_lock();
+ max_sectors = r10_bio->sectors;
+
for (i = 0; i < conf->copies; i++) {
int d = r10_bio->devs[i].devnum;
- mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[d].rdev);
+ struct md_rdev *rdev = rcu_dereference(conf->mirrors[d].rdev);
if (rdev && unlikely(test_bit(Blocked, &rdev->flags))) {
atomic_inc(&rdev->nr_pending);
blocked_rdev = rdev;
break;
}
- if (rdev && !test_bit(Faulty, &rdev->flags)) {
- atomic_inc(&rdev->nr_pending);
- r10_bio->devs[i].bio = bio;
- } else {
- r10_bio->devs[i].bio = NULL;
+ r10_bio->devs[i].bio = NULL;
+ if (!rdev || test_bit(Faulty, &rdev->flags)) {
set_bit(R10BIO_Degraded, &r10_bio->state);
+ continue;
+ }
+ if (test_bit(WriteErrorSeen, &rdev->flags)) {
+ sector_t first_bad;
+ sector_t dev_sector = r10_bio->devs[i].addr;
+ int bad_sectors;
+ int is_bad;
+
+ is_bad = is_badblock(rdev, dev_sector,
+ max_sectors,
+ &first_bad, &bad_sectors);
+ if (is_bad < 0) {
+ /* Mustn't write here until the bad block
+ * is acknowledged
+ */
+ atomic_inc(&rdev->nr_pending);
+ set_bit(BlockedBadBlocks, &rdev->flags);
+ blocked_rdev = rdev;
+ break;
+ }
+ if (is_bad && first_bad <= dev_sector) {
+ /* Cannot write here at all */
+ bad_sectors -= (dev_sector - first_bad);
+ if (bad_sectors < max_sectors)
+ /* Mustn't write more than bad_sectors
+ * to other devices yet
+ */
+ max_sectors = bad_sectors;
+ /* We don't set R10BIO_Degraded as that
+ * only applies if the disk is missing,
+ * so it might be re-added, and we want to
+ * know to recover this chunk.
+ * In this case the device is here, and the
+ * fact that this chunk is not in-sync is
+ * recorded in the bad block log.
+ */
+ continue;
+ }
+ if (is_bad) {
+ int good_sectors = first_bad - dev_sector;
+ if (good_sectors < max_sectors)
+ max_sectors = good_sectors;
+ }
}
+ r10_bio->devs[i].bio = bio;
+ atomic_inc(&rdev->nr_pending);
}
rcu_read_unlock();
@@ -882,8 +1107,22 @@ static int make_request(mddev_t *mddev, struct bio * bio)
goto retry_write;
}
+ if (max_sectors < r10_bio->sectors) {
+ /* We are splitting this into multiple parts, so
+ * we need to prepare for allocating another r10_bio.
+ */
+ r10_bio->sectors = max_sectors;
+ spin_lock_irq(&conf->device_lock);
+ if (bio->bi_phys_segments == 0)
+ bio->bi_phys_segments = 2;
+ else
+ bio->bi_phys_segments++;
+ spin_unlock_irq(&conf->device_lock);
+ }
+ sectors_handled = r10_bio->sector + max_sectors - bio->bi_sector;
+
atomic_set(&r10_bio->remaining, 1);
- bitmap_startwrite(mddev->bitmap, bio->bi_sector, r10_bio->sectors, 0);
+ bitmap_startwrite(mddev->bitmap, r10_bio->sector, r10_bio->sectors, 0);
for (i = 0; i < conf->copies; i++) {
struct bio *mbio;
@@ -892,10 +1131,12 @@ static int make_request(mddev_t *mddev, struct bio * bio)
continue;
mbio = bio_clone_mddev(bio, GFP_NOIO, mddev);
+ md_trim_bio(mbio, r10_bio->sector - bio->bi_sector,
+ max_sectors);
r10_bio->devs[i].bio = mbio;
- mbio->bi_sector = r10_bio->devs[i].addr+
- conf->mirrors[d].rdev->data_offset;
+ mbio->bi_sector = (r10_bio->devs[i].addr+
+ conf->mirrors[d].rdev->data_offset);
mbio->bi_bdev = conf->mirrors[d].rdev->bdev;
mbio->bi_end_io = raid10_end_write_request;
mbio->bi_rw = WRITE | do_sync | do_fua;
@@ -904,30 +1145,41 @@ static int make_request(mddev_t *mddev, struct bio * bio)
atomic_inc(&r10_bio->remaining);
spin_lock_irqsave(&conf->device_lock, flags);
bio_list_add(&conf->pending_bio_list, mbio);
+ conf->pending_count++;
spin_unlock_irqrestore(&conf->device_lock, flags);
}
- if (atomic_dec_and_test(&r10_bio->remaining)) {
- /* This matches the end of raid10_end_write_request() */
- bitmap_endwrite(r10_bio->mddev->bitmap, r10_bio->sector,
- r10_bio->sectors,
- !test_bit(R10BIO_Degraded, &r10_bio->state),
- 0);
- md_write_end(mddev);
- raid_end_bio_io(r10_bio);
+ /* Don't remove the bias on 'remaining' (one_write_done) until
+ * after checking if we need to go around again.
+ */
+
+ if (sectors_handled < (bio->bi_size >> 9)) {
+ one_write_done(r10_bio);
+ /* We need another r10_bio. It has already been counted
+ * in bio->bi_phys_segments.
+ */
+ r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
+
+ r10_bio->master_bio = bio;
+ r10_bio->sectors = (bio->bi_size >> 9) - sectors_handled;
+
+ r10_bio->mddev = mddev;
+ r10_bio->sector = bio->bi_sector + sectors_handled;
+ r10_bio->state = 0;
+ goto retry_write;
}
+ one_write_done(r10_bio);
/* In case raid10d snuck in to freeze_array */
wake_up(&conf->wait_barrier);
if (do_sync || !mddev->bitmap || !plugged)
md_wakeup_thread(mddev->thread);
- return 0;
}
-static void status(struct seq_file *seq, mddev_t *mddev)
+static void status(struct seq_file *seq, struct mddev *mddev)
{
- conf_t *conf = mddev->private;
+ struct r10conf *conf = mddev->private;
int i;
if (conf->near_copies < conf->raid_disks)
@@ -949,10 +1201,34 @@ static void status(struct seq_file *seq, mddev_t *mddev)
seq_printf(seq, "]");
}
-static void error(mddev_t *mddev, mdk_rdev_t *rdev)
+/* check if there are enough drives for
+ * every block to appear on atleast one.
+ * Don't consider the device numbered 'ignore'
+ * as we might be about to remove it.
+ */
+static int enough(struct r10conf *conf, int ignore)
+{
+ int first = 0;
+
+ do {
+ int n = conf->copies;
+ int cnt = 0;
+ while (n--) {
+ if (conf->mirrors[first].rdev &&
+ first != ignore)
+ cnt++;
+ first = (first+1) % conf->raid_disks;
+ }
+ if (cnt == 0)
+ return 0;
+ } while (first != 0);
+ return 1;
+}
+
+static void error(struct mddev *mddev, struct md_rdev *rdev)
{
char b[BDEVNAME_SIZE];
- conf_t *conf = mddev->private;
+ struct r10conf *conf = mddev->private;
/*
* If it is not operational, then we have already marked it as dead
@@ -961,13 +1237,9 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
* else mark the drive as failed
*/
if (test_bit(In_sync, &rdev->flags)
- && conf->raid_disks-mddev->degraded == 1)
+ && !enough(conf, rdev->raid_disk))
/*
* Don't fail the drive, just return an IO error.
- * The test should really be more sophisticated than
- * "working_disks == 1", but it isn't critical, and
- * can wait until we do more sophisticated "is the drive
- * really dead" tests...
*/
return;
if (test_and_clear_bit(In_sync, &rdev->flags)) {
@@ -980,6 +1252,7 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
*/
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
}
+ set_bit(Blocked, &rdev->flags);
set_bit(Faulty, &rdev->flags);
set_bit(MD_CHANGE_DEVS, &mddev->flags);
printk(KERN_ALERT
@@ -989,10 +1262,10 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
mdname(mddev), conf->raid_disks - mddev->degraded);
}
-static void print_conf(conf_t *conf)
+static void print_conf(struct r10conf *conf)
{
int i;
- mirror_info_t *tmp;
+ struct mirror_info *tmp;
printk(KERN_DEBUG "RAID10 conf printout:\n");
if (!conf) {
@@ -1013,7 +1286,7 @@ static void print_conf(conf_t *conf)
}
}
-static void close_sync(conf_t *conf)
+static void close_sync(struct r10conf *conf)
{
wait_barrier(conf);
allow_barrier(conf);
@@ -1022,32 +1295,11 @@ static void close_sync(conf_t *conf)
conf->r10buf_pool = NULL;
}
-/* check if there are enough drives for
- * every block to appear on atleast one
- */
-static int enough(conf_t *conf)
-{
- int first = 0;
-
- do {
- int n = conf->copies;
- int cnt = 0;
- while (n--) {
- if (conf->mirrors[first].rdev)
- cnt++;
- first = (first+1) % conf->raid_disks;
- }
- if (cnt == 0)
- return 0;
- } while (first != 0);
- return 1;
-}
-
-static int raid10_spare_active(mddev_t *mddev)
+static int raid10_spare_active(struct mddev *mddev)
{
int i;
- conf_t *conf = mddev->private;
- mirror_info_t *tmp;
+ struct r10conf *conf = mddev->private;
+ struct mirror_info *tmp;
int count = 0;
unsigned long flags;
@@ -1073,12 +1325,11 @@ static int raid10_spare_active(mddev_t *mddev)
}
-static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
+static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
{
- conf_t *conf = mddev->private;
+ struct r10conf *conf = mddev->private;
int err = -EEXIST;
int mirror;
- mirror_info_t *p;
int first = 0;
int last = conf->raid_disks - 1;
@@ -1087,55 +1338,59 @@ static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
* very different from resync
*/
return -EBUSY;
- if (!enough(conf))
+ if (!enough(conf, -1))
return -EINVAL;
if (rdev->raid_disk >= 0)
first = last = rdev->raid_disk;
- if (rdev->saved_raid_disk >= 0 &&
- rdev->saved_raid_disk >= first &&
+ if (rdev->saved_raid_disk >= first &&
conf->mirrors[rdev->saved_raid_disk].rdev == NULL)
mirror = rdev->saved_raid_disk;
else
mirror = first;
- for ( ; mirror <= last ; mirror++)
- if ( !(p=conf->mirrors+mirror)->rdev) {
-
- disk_stack_limits(mddev->gendisk, rdev->bdev,
- rdev->data_offset << 9);
- /* as we don't honour merge_bvec_fn, we must
- * never risk violating it, so limit
- * ->max_segments to one lying with a single
- * page, as a one page request is never in
- * violation.
- */
- if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
- blk_queue_max_segments(mddev->queue, 1);
- blk_queue_segment_boundary(mddev->queue,
- PAGE_CACHE_SIZE - 1);
- }
+ for ( ; mirror <= last ; mirror++) {
+ struct mirror_info *p = &conf->mirrors[mirror];
+ if (p->recovery_disabled == mddev->recovery_disabled)
+ continue;
+ if (p->rdev)
+ continue;
- p->head_position = 0;
- rdev->raid_disk = mirror;
- err = 0;
- if (rdev->saved_raid_disk != mirror)
- conf->fullsync = 1;
- rcu_assign_pointer(p->rdev, rdev);
- break;
+ disk_stack_limits(mddev->gendisk, rdev->bdev,
+ rdev->data_offset << 9);
+ /* as we don't honour merge_bvec_fn, we must
+ * never risk violating it, so limit
+ * ->max_segments to one lying with a single
+ * page, as a one page request is never in
+ * violation.
+ */
+ if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
+ blk_queue_max_segments(mddev->queue, 1);
+ blk_queue_segment_boundary(mddev->queue,
+ PAGE_CACHE_SIZE - 1);
}
+ p->head_position = 0;
+ p->recovery_disabled = mddev->recovery_disabled - 1;
+ rdev->raid_disk = mirror;
+ err = 0;
+ if (rdev->saved_raid_disk != mirror)
+ conf->fullsync = 1;
+ rcu_assign_pointer(p->rdev, rdev);
+ break;
+ }
+
md_integrity_add_rdev(rdev, mddev);
print_conf(conf);
return err;
}
-static int raid10_remove_disk(mddev_t *mddev, int number)
+static int raid10_remove_disk(struct mddev *mddev, int number)
{
- conf_t *conf = mddev->private;
+ struct r10conf *conf = mddev->private;
int err = 0;
- mdk_rdev_t *rdev;
- mirror_info_t *p = conf->mirrors+ number;
+ struct md_rdev *rdev;
+ struct mirror_info *p = conf->mirrors+ number;
print_conf(conf);
rdev = p->rdev;
@@ -1149,7 +1404,8 @@ static int raid10_remove_disk(mddev_t *mddev, int number)
* is not possible.
*/
if (!test_bit(Faulty, &rdev->flags) &&
- enough(conf)) {
+ mddev->recovery_disabled != p->recovery_disabled &&
+ enough(conf, -1)) {
err = -EBUSY;
goto abort;
}
@@ -1172,26 +1428,20 @@ abort:
static void end_sync_read(struct bio *bio, int error)
{
- r10bio_t *r10_bio = bio->bi_private;
- conf_t *conf = r10_bio->mddev->private;
- int i,d;
+ struct r10bio *r10_bio = bio->bi_private;
+ struct r10conf *conf = r10_bio->mddev->private;
+ int d;
- for (i=0; i<conf->copies; i++)
- if (r10_bio->devs[i].bio == bio)
- break;
- BUG_ON(i == conf->copies);
- update_head_pos(i, r10_bio);
- d = r10_bio->devs[i].devnum;
+ d = find_bio_disk(conf, r10_bio, bio, NULL);
if (test_bit(BIO_UPTODATE, &bio->bi_flags))
set_bit(R10BIO_Uptodate, &r10_bio->state);
- else {
+ else
+ /* The write handler will notice the lack of
+ * R10BIO_Uptodate and record any errors etc
+ */
atomic_add(r10_bio->sectors,
&conf->mirrors[d].rdev->corrected_errors);
- if (!test_bit(MD_RECOVERY_SYNC, &conf->mddev->recovery))
- md_error(r10_bio->mddev,
- conf->mirrors[d].rdev);
- }
/* for reconstruct, we always reschedule after a read.
* for resync, only after all reads
@@ -1206,40 +1456,60 @@ static void end_sync_read(struct bio *bio, int error)
}
}
-static void end_sync_write(struct bio *bio, int error)
+static void end_sync_request(struct r10bio *r10_bio)
{
- int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
- r10bio_t *r10_bio = bio->bi_private;
- mddev_t *mddev = r10_bio->mddev;
- conf_t *conf = mddev->private;
- int i,d;
-
- for (i = 0; i < conf->copies; i++)
- if (r10_bio->devs[i].bio == bio)
- break;
- d = r10_bio->devs[i].devnum;
-
- if (!uptodate)
- md_error(mddev, conf->mirrors[d].rdev);
+ struct mddev *mddev = r10_bio->mddev;
- 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 */
sector_t s = r10_bio->sectors;
- put_buf(r10_bio);
+ if (test_bit(R10BIO_MadeGood, &r10_bio->state) ||
+ test_bit(R10BIO_WriteError, &r10_bio->state))
+ reschedule_retry(r10_bio);
+ else
+ put_buf(r10_bio);
md_done_sync(mddev, s, 1);
break;
} else {
- r10bio_t *r10_bio2 = (r10bio_t *)r10_bio->master_bio;
- put_buf(r10_bio);
+ struct r10bio *r10_bio2 = (struct r10bio *)r10_bio->master_bio;
+ if (test_bit(R10BIO_MadeGood, &r10_bio->state) ||
+ test_bit(R10BIO_WriteError, &r10_bio->state))
+ reschedule_retry(r10_bio);
+ else
+ put_buf(r10_bio);
r10_bio = r10_bio2;
}
}
}
+static void end_sync_write(struct bio *bio, int error)
+{
+ int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
+ struct r10bio *r10_bio = bio->bi_private;
+ struct mddev *mddev = r10_bio->mddev;
+ struct r10conf *conf = mddev->private;
+ int d;
+ sector_t first_bad;
+ int bad_sectors;
+ int slot;
+
+ d = find_bio_disk(conf, r10_bio, bio, &slot);
+
+ if (!uptodate) {
+ set_bit(WriteErrorSeen, &conf->mirrors[d].rdev->flags);
+ set_bit(R10BIO_WriteError, &r10_bio->state);
+ } else if (is_badblock(conf->mirrors[d].rdev,
+ r10_bio->devs[slot].addr,
+ r10_bio->sectors,
+ &first_bad, &bad_sectors))
+ set_bit(R10BIO_MadeGood, &r10_bio->state);
+
+ rdev_dec_pending(conf->mirrors[d].rdev, mddev);
+
+ end_sync_request(r10_bio);
+}
+
/*
* Note: sync and recover and handled very differently for raid10
* This code is for resync.
@@ -1256,9 +1526,9 @@ static void end_sync_write(struct bio *bio, int error)
* We check if all blocks are in-sync and only write to blocks that
* aren't in sync
*/
-static void sync_request_write(mddev_t *mddev, r10bio_t *r10_bio)
+static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
{
- conf_t *conf = mddev->private;
+ struct r10conf *conf = mddev->private;
int i, first;
struct bio *tbio, *fbio;
@@ -1299,11 +1569,12 @@ static void sync_request_write(mddev_t *mddev, r10bio_t *r10_bio)
if (j == vcnt)
continue;
mddev->resync_mismatches += r10_bio->sectors;
+ if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
+ /* Don't fix anything. */
+ continue;
}
- if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
- /* Don't fix anything. */
- continue;
- /* Ok, we need to write this bio
+ /* Ok, we need to write this bio, either to correct an
+ * inconsistency or to correct an unreadable block.
* First we need to fixup bv_offset, bv_len and
* bi_vecs, as the read request might have corrupted these
*/
@@ -1355,32 +1626,107 @@ done:
* The second for writing.
*
*/
+static void fix_recovery_read_error(struct r10bio *r10_bio)
+{
+ /* We got a read error during recovery.
+ * We repeat the read in smaller page-sized sections.
+ * If a read succeeds, write it to the new device or record
+ * a bad block if we cannot.
+ * If a read fails, record a bad block on both old and
+ * new devices.
+ */
+ struct mddev *mddev = r10_bio->mddev;
+ struct r10conf *conf = mddev->private;
+ struct bio *bio = r10_bio->devs[0].bio;
+ sector_t sect = 0;
+ int sectors = r10_bio->sectors;
+ int idx = 0;
+ int dr = r10_bio->devs[0].devnum;
+ int dw = r10_bio->devs[1].devnum;
+
+ while (sectors) {
+ int s = sectors;
+ struct md_rdev *rdev;
+ sector_t addr;
+ int ok;
+
+ if (s > (PAGE_SIZE>>9))
+ s = PAGE_SIZE >> 9;
-static void recovery_request_write(mddev_t *mddev, r10bio_t *r10_bio)
+ rdev = conf->mirrors[dr].rdev;
+ addr = r10_bio->devs[0].addr + sect,
+ ok = sync_page_io(rdev,
+ addr,
+ s << 9,
+ bio->bi_io_vec[idx].bv_page,
+ READ, false);
+ if (ok) {
+ rdev = conf->mirrors[dw].rdev;
+ addr = r10_bio->devs[1].addr + sect;
+ ok = sync_page_io(rdev,
+ addr,
+ s << 9,
+ bio->bi_io_vec[idx].bv_page,
+ WRITE, false);
+ if (!ok)
+ set_bit(WriteErrorSeen, &rdev->flags);
+ }
+ if (!ok) {
+ /* We don't worry if we cannot set a bad block -
+ * it really is bad so there is no loss in not
+ * recording it yet
+ */
+ rdev_set_badblocks(rdev, addr, s, 0);
+
+ if (rdev != conf->mirrors[dw].rdev) {
+ /* need bad block on destination too */
+ struct md_rdev *rdev2 = conf->mirrors[dw].rdev;
+ addr = r10_bio->devs[1].addr + sect;
+ ok = rdev_set_badblocks(rdev2, addr, s, 0);
+ if (!ok) {
+ /* just abort the recovery */
+ printk(KERN_NOTICE
+ "md/raid10:%s: recovery aborted"
+ " due to read error\n",
+ mdname(mddev));
+
+ conf->mirrors[dw].recovery_disabled
+ = mddev->recovery_disabled;
+ set_bit(MD_RECOVERY_INTR,
+ &mddev->recovery);
+ break;
+ }
+ }
+ }
+
+ sectors -= s;
+ sect += s;
+ idx++;
+ }
+}
+
+static void recovery_request_write(struct mddev *mddev, struct r10bio *r10_bio)
{
- conf_t *conf = mddev->private;
- int i, d;
- struct bio *bio, *wbio;
+ struct r10conf *conf = mddev->private;
+ int d;
+ struct bio *wbio;
+ if (!test_bit(R10BIO_Uptodate, &r10_bio->state)) {
+ fix_recovery_read_error(r10_bio);
+ end_sync_request(r10_bio);
+ return;
+ }
- /* move the pages across to the second bio
+ /*
+ * share the pages with the first bio
* and submit the write request
*/
- bio = r10_bio->devs[0].bio;
wbio = r10_bio->devs[1].bio;
- for (i=0; i < wbio->bi_vcnt; i++) {
- struct page *p = bio->bi_io_vec[i].bv_page;
- bio->bi_io_vec[i].bv_page = wbio->bi_io_vec[i].bv_page;
- wbio->bi_io_vec[i].bv_page = p;
- }
d = r10_bio->devs[1].devnum;
atomic_inc(&conf->mirrors[d].rdev->nr_pending);
md_sync_acct(conf->mirrors[d].rdev->bdev, wbio->bi_size >> 9);
- if (test_bit(R10BIO_Uptodate, &r10_bio->state))
- generic_make_request(wbio);
- else
- bio_endio(wbio, -EIO);
+ generic_make_request(wbio);
}
@@ -1390,7 +1736,7 @@ static void recovery_request_write(mddev_t *mddev, r10bio_t *r10_bio)
* since the last recorded read error.
*
*/
-static void check_decay_read_errors(mddev_t *mddev, mdk_rdev_t *rdev)
+static void check_decay_read_errors(struct mddev *mddev, struct md_rdev *rdev)
{
struct timespec cur_time_mon;
unsigned long hours_since_last;
@@ -1421,6 +1767,26 @@ static void check_decay_read_errors(mddev_t *mddev, mdk_rdev_t *rdev)
atomic_set(&rdev->read_errors, read_errors >> hours_since_last);
}
+static int r10_sync_page_io(struct md_rdev *rdev, sector_t sector,
+ int sectors, struct page *page, int rw)
+{
+ sector_t first_bad;
+ int bad_sectors;
+
+ if (is_badblock(rdev, sector, sectors, &first_bad, &bad_sectors)
+ && (rw == READ || test_bit(WriteErrorSeen, &rdev->flags)))
+ return -1;
+ if (sync_page_io(rdev, sector, sectors << 9, page, rw, false))
+ /* success */
+ return 1;
+ if (rw == WRITE)
+ set_bit(WriteErrorSeen, &rdev->flags);
+ /* need to record an error - either for the block or the device */
+ if (!rdev_set_badblocks(rdev, sector, sectors, 0))
+ md_error(rdev->mddev, rdev);
+ return 0;
+}
+
/*
* This is a kernel thread which:
*
@@ -1429,11 +1795,11 @@ static void check_decay_read_errors(mddev_t *mddev, mdk_rdev_t *rdev)
* 3. Performs writes following reads for array synchronising.
*/
-static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
+static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10bio *r10_bio)
{
int sect = 0; /* Offset from r10_bio->sector */
int sectors = r10_bio->sectors;
- mdk_rdev_t*rdev;
+ struct md_rdev*rdev;
int max_read_errors = atomic_read(&mddev->max_corr_read_errors);
int d = r10_bio->devs[r10_bio->read_slot].devnum;
@@ -1476,10 +1842,15 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
rcu_read_lock();
do {
+ sector_t first_bad;
+ int bad_sectors;
+
d = r10_bio->devs[sl].devnum;
rdev = rcu_dereference(conf->mirrors[d].rdev);
if (rdev &&
- test_bit(In_sync, &rdev->flags)) {
+ test_bit(In_sync, &rdev->flags) &&
+ is_badblock(rdev, r10_bio->devs[sl].addr + sect, s,
+ &first_bad, &bad_sectors) == 0) {
atomic_inc(&rdev->nr_pending);
rcu_read_unlock();
success = sync_page_io(rdev,
@@ -1499,9 +1870,19 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
rcu_read_unlock();
if (!success) {
- /* Cannot read from anywhere -- bye bye array */
+ /* Cannot read from anywhere, just mark the block
+ * as bad on the first device to discourage future
+ * reads.
+ */
int dn = r10_bio->devs[r10_bio->read_slot].devnum;
- md_error(mddev, conf->mirrors[dn].rdev);
+ rdev = conf->mirrors[dn].rdev;
+
+ if (!rdev_set_badblocks(
+ rdev,
+ r10_bio->devs[r10_bio->read_slot].addr
+ + sect,
+ s, 0))
+ md_error(mddev, rdev);
break;
}
@@ -1516,80 +1897,82 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
sl--;
d = r10_bio->devs[sl].devnum;
rdev = rcu_dereference(conf->mirrors[d].rdev);
- if (rdev &&
- test_bit(In_sync, &rdev->flags)) {
- atomic_inc(&rdev->nr_pending);
- rcu_read_unlock();
- atomic_add(s, &rdev->corrected_errors);
- if (sync_page_io(rdev,
- r10_bio->devs[sl].addr +
- sect,
- s<<9, conf->tmppage, WRITE, false)
- == 0) {
- /* Well, this device is dead */
- printk(KERN_NOTICE
- "md/raid10:%s: read correction "
- "write failed"
- " (%d sectors at %llu on %s)\n",
- mdname(mddev), s,
- (unsigned long long)(
- sect + rdev->data_offset),
- bdevname(rdev->bdev, b));
- printk(KERN_NOTICE "md/raid10:%s: %s: failing "
- "drive\n",
- mdname(mddev),
- bdevname(rdev->bdev, b));
- md_error(mddev, rdev);
- }
- rdev_dec_pending(rdev, mddev);
- rcu_read_lock();
+ if (!rdev ||
+ !test_bit(In_sync, &rdev->flags))
+ continue;
+
+ atomic_inc(&rdev->nr_pending);
+ rcu_read_unlock();
+ if (r10_sync_page_io(rdev,
+ r10_bio->devs[sl].addr +
+ sect,
+ s<<9, conf->tmppage, WRITE)
+ == 0) {
+ /* Well, this device is dead */
+ printk(KERN_NOTICE
+ "md/raid10:%s: read correction "
+ "write failed"
+ " (%d sectors at %llu on %s)\n",
+ mdname(mddev), s,
+ (unsigned long long)(
+ sect + rdev->data_offset),
+ bdevname(rdev->bdev, b));
+ printk(KERN_NOTICE "md/raid10:%s: %s: failing "
+ "drive\n",
+ mdname(mddev),
+ bdevname(rdev->bdev, b));
}
+ rdev_dec_pending(rdev, mddev);
+ rcu_read_lock();
}
sl = start;
while (sl != r10_bio->read_slot) {
+ char b[BDEVNAME_SIZE];
if (sl==0)
sl = conf->copies;
sl--;
d = r10_bio->devs[sl].devnum;
rdev = rcu_dereference(conf->mirrors[d].rdev);
- if (rdev &&
- test_bit(In_sync, &rdev->flags)) {
- char b[BDEVNAME_SIZE];
- atomic_inc(&rdev->nr_pending);
- rcu_read_unlock();
- if (sync_page_io(rdev,
- r10_bio->devs[sl].addr +
- sect,
- s<<9, conf->tmppage,
- READ, false) == 0) {
- /* Well, this device is dead */
- printk(KERN_NOTICE
- "md/raid10:%s: unable to read back "
- "corrected sectors"
- " (%d sectors at %llu on %s)\n",
- mdname(mddev), s,
- (unsigned long long)(
- sect + rdev->data_offset),
- bdevname(rdev->bdev, b));
- printk(KERN_NOTICE "md/raid10:%s: %s: failing drive\n",
- mdname(mddev),
- bdevname(rdev->bdev, b));
-
- md_error(mddev, rdev);
- } else {
- printk(KERN_INFO
- "md/raid10:%s: read error corrected"
- " (%d sectors at %llu on %s)\n",
- mdname(mddev), s,
- (unsigned long long)(
- sect + rdev->data_offset),
- bdevname(rdev->bdev, b));
- }
+ if (!rdev ||
+ !test_bit(In_sync, &rdev->flags))
+ continue;
- rdev_dec_pending(rdev, mddev);
- rcu_read_lock();
+ atomic_inc(&rdev->nr_pending);
+ rcu_read_unlock();
+ switch (r10_sync_page_io(rdev,
+ r10_bio->devs[sl].addr +
+ sect,
+ s<<9, conf->tmppage,
+ READ)) {
+ case 0:
+ /* Well, this device is dead */
+ printk(KERN_NOTICE
+ "md/raid10:%s: unable to read back "
+ "corrected sectors"
+ " (%d sectors at %llu on %s)\n",
+ mdname(mddev), s,
+ (unsigned long long)(
+ sect + rdev->data_offset),
+ bdevname(rdev->bdev, b));
+ printk(KERN_NOTICE "md/raid10:%s: %s: failing "
+ "drive\n",
+ mdname(mddev),
+ bdevname(rdev->bdev, b));
+ break;
+ case 1:
+ printk(KERN_INFO
+ "md/raid10:%s: read error corrected"
+ " (%d sectors at %llu on %s)\n",
+ mdname(mddev), s,
+ (unsigned long long)(
+ sect + rdev->data_offset),
+ bdevname(rdev->bdev, b));
+ atomic_add(s, &rdev->corrected_errors);
}
+
+ rdev_dec_pending(rdev, mddev);
+ rcu_read_lock();
}
rcu_read_unlock();
@@ -1598,21 +1981,254 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
}
}
-static void raid10d(mddev_t *mddev)
+static void bi_complete(struct bio *bio, int error)
{
- r10bio_t *r10_bio;
+ complete((struct completion *)bio->bi_private);
+}
+
+static int submit_bio_wait(int rw, struct bio *bio)
+{
+ struct completion event;
+ rw |= REQ_SYNC;
+
+ init_completion(&event);
+ bio->bi_private = &event;
+ bio->bi_end_io = bi_complete;
+ submit_bio(rw, bio);
+ wait_for_completion(&event);
+
+ return test_bit(BIO_UPTODATE, &bio->bi_flags);
+}
+
+static int narrow_write_error(struct r10bio *r10_bio, int i)
+{
+ struct bio *bio = r10_bio->master_bio;
+ struct mddev *mddev = r10_bio->mddev;
+ struct r10conf *conf = mddev->private;
+ struct md_rdev *rdev = conf->mirrors[r10_bio->devs[i].devnum].rdev;
+ /* bio has the data to be written to slot 'i' where
+ * we just recently had a write error.
+ * We repeatedly clone the bio and trim down to one block,
+ * then try the write. Where the write fails we record
+ * a bad block.
+ * It is conceivable that the bio doesn't exactly align with
+ * blocks. We must handle this.
+ *
+ * We currently own a reference to the rdev.
+ */
+
+ int block_sectors;
+ sector_t sector;
+ int sectors;
+ int sect_to_write = r10_bio->sectors;
+ int ok = 1;
+
+ if (rdev->badblocks.shift < 0)
+ return 0;
+
+ block_sectors = 1 << rdev->badblocks.shift;
+ sector = r10_bio->sector;
+ sectors = ((r10_bio->sector + block_sectors)
+ & ~(sector_t)(block_sectors - 1))
+ - sector;
+
+ while (sect_to_write) {
+ struct bio *wbio;
+ if (sectors > sect_to_write)
+ sectors = sect_to_write;
+ /* Write at 'sector' for 'sectors' */
+ wbio = bio_clone_mddev(bio, GFP_NOIO, mddev);
+ md_trim_bio(wbio, sector - bio->bi_sector, sectors);
+ wbio->bi_sector = (r10_bio->devs[i].addr+
+ rdev->data_offset+
+ (sector - r10_bio->sector));
+ wbio->bi_bdev = rdev->bdev;
+ if (submit_bio_wait(WRITE, wbio) == 0)
+ /* Failure! */
+ ok = rdev_set_badblocks(rdev, sector,
+ sectors, 0)
+ && ok;
+
+ bio_put(wbio);
+ sect_to_write -= sectors;
+ sector += sectors;
+ sectors = block_sectors;
+ }
+ return ok;
+}
+
+static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
+{
+ int slot = r10_bio->read_slot;
+ int mirror = r10_bio->devs[slot].devnum;
struct bio *bio;
+ struct r10conf *conf = mddev->private;
+ struct md_rdev *rdev;
+ char b[BDEVNAME_SIZE];
+ unsigned long do_sync;
+ int max_sectors;
+
+ /* we got a read error. Maybe the drive is bad. Maybe just
+ * the block and we can fix it.
+ * We freeze all other IO, and try reading the block from
+ * other devices. When we find one, we re-write
+ * and check it that fixes the read error.
+ * This is all done synchronously while the array is
+ * frozen.
+ */
+ if (mddev->ro == 0) {
+ freeze_array(conf);
+ fix_read_error(conf, mddev, r10_bio);
+ unfreeze_array(conf);
+ }
+ rdev_dec_pending(conf->mirrors[mirror].rdev, mddev);
+
+ bio = r10_bio->devs[slot].bio;
+ bdevname(bio->bi_bdev, b);
+ r10_bio->devs[slot].bio =
+ mddev->ro ? IO_BLOCKED : NULL;
+read_more:
+ mirror = read_balance(conf, r10_bio, &max_sectors);
+ if (mirror == -1) {
+ printk(KERN_ALERT "md/raid10:%s: %s: unrecoverable I/O"
+ " read error for block %llu\n",
+ mdname(mddev), b,
+ (unsigned long long)r10_bio->sector);
+ raid_end_bio_io(r10_bio);
+ bio_put(bio);
+ return;
+ }
+
+ do_sync = (r10_bio->master_bio->bi_rw & REQ_SYNC);
+ if (bio)
+ bio_put(bio);
+ slot = r10_bio->read_slot;
+ rdev = conf->mirrors[mirror].rdev;
+ printk_ratelimited(
+ KERN_ERR
+ "md/raid10:%s: %s: redirecting"
+ "sector %llu to another mirror\n",
+ mdname(mddev),
+ bdevname(rdev->bdev, b),
+ (unsigned long long)r10_bio->sector);
+ bio = bio_clone_mddev(r10_bio->master_bio,
+ GFP_NOIO, mddev);
+ md_trim_bio(bio,
+ r10_bio->sector - bio->bi_sector,
+ max_sectors);
+ r10_bio->devs[slot].bio = bio;
+ bio->bi_sector = r10_bio->devs[slot].addr
+ + rdev->data_offset;
+ bio->bi_bdev = rdev->bdev;
+ bio->bi_rw = READ | do_sync;
+ bio->bi_private = r10_bio;
+ bio->bi_end_io = raid10_end_read_request;
+ if (max_sectors < r10_bio->sectors) {
+ /* Drat - have to split this up more */
+ struct bio *mbio = r10_bio->master_bio;
+ int sectors_handled =
+ r10_bio->sector + max_sectors
+ - mbio->bi_sector;
+ r10_bio->sectors = max_sectors;
+ spin_lock_irq(&conf->device_lock);
+ if (mbio->bi_phys_segments == 0)
+ mbio->bi_phys_segments = 2;
+ else
+ mbio->bi_phys_segments++;
+ spin_unlock_irq(&conf->device_lock);
+ generic_make_request(bio);
+ bio = NULL;
+
+ r10_bio = mempool_alloc(conf->r10bio_pool,
+ GFP_NOIO);
+ r10_bio->master_bio = mbio;
+ r10_bio->sectors = (mbio->bi_size >> 9)
+ - sectors_handled;
+ r10_bio->state = 0;
+ set_bit(R10BIO_ReadError,
+ &r10_bio->state);
+ r10_bio->mddev = mddev;
+ r10_bio->sector = mbio->bi_sector
+ + sectors_handled;
+
+ goto read_more;
+ } else
+ generic_make_request(bio);
+}
+
+static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
+{
+ /* Some sort of write request has finished and it
+ * succeeded in writing where we thought there was a
+ * bad block. So forget the bad block.
+ * Or possibly if failed and we need to record
+ * a bad block.
+ */
+ int m;
+ struct md_rdev *rdev;
+
+ if (test_bit(R10BIO_IsSync, &r10_bio->state) ||
+ test_bit(R10BIO_IsRecover, &r10_bio->state)) {
+ for (m = 0; m < conf->copies; m++) {
+ int dev = r10_bio->devs[m].devnum;
+ rdev = conf->mirrors[dev].rdev;
+ if (r10_bio->devs[m].bio == NULL)
+ continue;
+ if (test_bit(BIO_UPTODATE,
+ &r10_bio->devs[m].bio->bi_flags)) {
+ rdev_clear_badblocks(
+ rdev,
+ r10_bio->devs[m].addr,
+ r10_bio->sectors);
+ } else {
+ if (!rdev_set_badblocks(
+ rdev,
+ r10_bio->devs[m].addr,
+ r10_bio->sectors, 0))
+ md_error(conf->mddev, rdev);
+ }
+ }
+ put_buf(r10_bio);
+ } else {
+ for (m = 0; m < conf->copies; m++) {
+ int dev = r10_bio->devs[m].devnum;
+ struct bio *bio = r10_bio->devs[m].bio;
+ rdev = conf->mirrors[dev].rdev;
+ if (bio == IO_MADE_GOOD) {
+ rdev_clear_badblocks(
+ rdev,
+ r10_bio->devs[m].addr,
+ r10_bio->sectors);
+ rdev_dec_pending(rdev, conf->mddev);
+ } else if (bio != NULL &&
+ !test_bit(BIO_UPTODATE, &bio->bi_flags)) {
+ if (!narrow_write_error(r10_bio, m)) {
+ md_error(conf->mddev, rdev);
+ set_bit(R10BIO_Degraded,
+ &r10_bio->state);
+ }
+ rdev_dec_pending(rdev, conf->mddev);
+ }
+ }
+ if (test_bit(R10BIO_WriteError,
+ &r10_bio->state))
+ close_write(r10_bio);
+ raid_end_bio_io(r10_bio);
+ }
+}
+
+static void raid10d(struct mddev *mddev)
+{
+ struct r10bio *r10_bio;
unsigned long flags;
- conf_t *conf = mddev->private;
+ struct r10conf *conf = mddev->private;
struct list_head *head = &conf->retry_list;
- mdk_rdev_t *rdev;
struct blk_plug plug;
md_check_recovery(mddev);
blk_start_plug(&plug);
for (;;) {
- char b[BDEVNAME_SIZE];
flush_pending_writes(conf);
@@ -1621,77 +2237,39 @@ static void raid10d(mddev_t *mddev)
spin_unlock_irqrestore(&conf->device_lock, flags);
break;
}
- r10_bio = list_entry(head->prev, r10bio_t, retry_list);
+ r10_bio = list_entry(head->prev, struct r10bio, retry_list);
list_del(head->prev);
conf->nr_queued--;
spin_unlock_irqrestore(&conf->device_lock, flags);
mddev = r10_bio->mddev;
conf = mddev->private;
- if (test_bit(R10BIO_IsSync, &r10_bio->state))
+ if (test_bit(R10BIO_MadeGood, &r10_bio->state) ||
+ test_bit(R10BIO_WriteError, &r10_bio->state))
+ handle_write_completed(conf, r10_bio);
+ else if (test_bit(R10BIO_IsSync, &r10_bio->state))
sync_request_write(mddev, r10_bio);
else if (test_bit(R10BIO_IsRecover, &r10_bio->state))
recovery_request_write(mddev, r10_bio);
+ else if (test_bit(R10BIO_ReadError, &r10_bio->state))
+ handle_read_error(mddev, r10_bio);
else {
- int slot = r10_bio->read_slot;
- int mirror = r10_bio->devs[slot].devnum;
- /* we got a read error. Maybe the drive is bad. Maybe just
- * the block and we can fix it.
- * We freeze all other IO, and try reading the block from
- * other devices. When we find one, we re-write
- * and check it that fixes the read error.
- * This is all done synchronously while the array is
- * frozen.
+ /* just a partial read to be scheduled from a
+ * separate context
*/
- if (mddev->ro == 0) {
- freeze_array(conf);
- fix_read_error(conf, mddev, r10_bio);
- unfreeze_array(conf);
- }
- rdev_dec_pending(conf->mirrors[mirror].rdev, mddev);
-
- bio = r10_bio->devs[slot].bio;
- r10_bio->devs[slot].bio =
- mddev->ro ? IO_BLOCKED : NULL;
- mirror = read_balance(conf, r10_bio);
- if (mirror == -1) {
- printk(KERN_ALERT "md/raid10:%s: %s: unrecoverable I/O"
- " read error for block %llu\n",
- mdname(mddev),
- bdevname(bio->bi_bdev,b),
- (unsigned long long)r10_bio->sector);
- raid_end_bio_io(r10_bio);
- bio_put(bio);
- } else {
- const unsigned long do_sync = (r10_bio->master_bio->bi_rw & REQ_SYNC);
- bio_put(bio);
- slot = r10_bio->read_slot;
- rdev = conf->mirrors[mirror].rdev;
- if (printk_ratelimit())
- printk(KERN_ERR "md/raid10:%s: %s: redirecting sector %llu to"
- " another mirror\n",
- mdname(mddev),
- bdevname(rdev->bdev,b),
- (unsigned long long)r10_bio->sector);
- bio = bio_clone_mddev(r10_bio->master_bio,
- GFP_NOIO, mddev);
- r10_bio->devs[slot].bio = bio;
- bio->bi_sector = r10_bio->devs[slot].addr
- + rdev->data_offset;
- bio->bi_bdev = rdev->bdev;
- bio->bi_rw = READ | do_sync;
- bio->bi_private = r10_bio;
- bio->bi_end_io = raid10_end_read_request;
- generic_make_request(bio);
- }
+ int slot = r10_bio->read_slot;
+ generic_make_request(r10_bio->devs[slot].bio);
}
+
cond_resched();
+ if (mddev->flags & ~(1<<MD_CHANGE_PENDING))
+ md_check_recovery(mddev);
}
blk_finish_plug(&plug);
}
-static int init_resync(conf_t *conf)
+static int init_resync(struct r10conf *conf)
{
int buffs;
@@ -1736,17 +2314,16 @@ static int init_resync(conf_t *conf)
*
*/
-static sector_t sync_request(mddev_t *mddev, sector_t sector_nr,
+static sector_t sync_request(struct mddev *mddev, sector_t sector_nr,
int *skipped, int go_faster)
{
- conf_t *conf = mddev->private;
- r10bio_t *r10_bio;
+ struct r10conf *conf = mddev->private;
+ struct r10bio *r10_bio;
struct bio *biolist = NULL, *bio;
sector_t max_sector, nr_sectors;
int i;
int max_sync;
sector_t sync_blocks;
-
sector_t sectors_skipped = 0;
int chunks_skipped = 0;
@@ -1828,14 +2405,15 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr,
max_sync = RESYNC_PAGES << (PAGE_SHIFT-9);
if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
/* recovery... the complicated one */
- int j, k;
+ int j;
r10_bio = NULL;
for (i=0 ; i<conf->raid_disks; i++) {
int still_degraded;
- r10bio_t *rb2;
+ struct r10bio *rb2;
sector_t sect;
int must_sync;
+ int any_working;
if (conf->mirrors[i].rdev == NULL ||
test_bit(In_sync, &conf->mirrors[i].rdev->flags))
@@ -1887,19 +2465,42 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr,
must_sync = bitmap_start_sync(mddev->bitmap, sect,
&sync_blocks, still_degraded);
+ any_working = 0;
for (j=0; j<conf->copies;j++) {
+ int k;
int d = r10_bio->devs[j].devnum;
+ sector_t from_addr, to_addr;
+ struct md_rdev *rdev;
+ sector_t sector, first_bad;
+ int bad_sectors;
if (!conf->mirrors[d].rdev ||
!test_bit(In_sync, &conf->mirrors[d].rdev->flags))
continue;
/* This is where we read from */
+ any_working = 1;
+ rdev = conf->mirrors[d].rdev;
+ sector = r10_bio->devs[j].addr;
+
+ if (is_badblock(rdev, sector, max_sync,
+ &first_bad, &bad_sectors)) {
+ if (first_bad > sector)
+ max_sync = first_bad - sector;
+ else {
+ bad_sectors -= (sector
+ - first_bad);
+ if (max_sync > bad_sectors)
+ max_sync = bad_sectors;
+ continue;
+ }
+ }
bio = r10_bio->devs[0].bio;
bio->bi_next = biolist;
biolist = bio;
bio->bi_private = r10_bio;
bio->bi_end_io = end_sync_read;
bio->bi_rw = READ;
- bio->bi_sector = r10_bio->devs[j].addr +
+ from_addr = r10_bio->devs[j].addr;
+ bio->bi_sector = from_addr +
conf->mirrors[d].rdev->data_offset;
bio->bi_bdev = conf->mirrors[d].rdev->bdev;
atomic_inc(&conf->mirrors[d].rdev->nr_pending);
@@ -1916,33 +2517,55 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr,
bio->bi_private = r10_bio;
bio->bi_end_io = end_sync_write;
bio->bi_rw = WRITE;
- bio->bi_sector = r10_bio->devs[k].addr +
+ to_addr = r10_bio->devs[k].addr;
+ bio->bi_sector = to_addr +
conf->mirrors[i].rdev->data_offset;
bio->bi_bdev = conf->mirrors[i].rdev->bdev;
r10_bio->devs[0].devnum = d;
+ r10_bio->devs[0].addr = from_addr;
r10_bio->devs[1].devnum = i;
+ r10_bio->devs[1].addr = to_addr;
break;
}
if (j == conf->copies) {
- /* Cannot recover, so abort the recovery */
+ /* Cannot recover, so abort the recovery or
+ * record a bad block */
put_buf(r10_bio);
if (rb2)
atomic_dec(&rb2->remaining);
r10_bio = rb2;
- if (!test_and_set_bit(MD_RECOVERY_INTR,
- &mddev->recovery))
- printk(KERN_INFO "md/raid10:%s: insufficient "
- "working devices for recovery.\n",
- mdname(mddev));
+ if (any_working) {
+ /* problem is that there are bad blocks
+ * on other device(s)
+ */
+ int k;
+ for (k = 0; k < conf->copies; k++)
+ if (r10_bio->devs[k].devnum == i)
+ break;
+ if (!rdev_set_badblocks(
+ conf->mirrors[i].rdev,
+ r10_bio->devs[k].addr,
+ max_sync, 0))
+ any_working = 0;
+ }
+ if (!any_working) {
+ if (!test_and_set_bit(MD_RECOVERY_INTR,
+ &mddev->recovery))
+ printk(KERN_INFO "md/raid10:%s: insufficient "
+ "working devices for recovery.\n",
+ mdname(mddev));
+ conf->mirrors[i].recovery_disabled
+ = mddev->recovery_disabled;
+ }
break;
}
}
if (biolist == NULL) {
while (r10_bio) {
- r10bio_t *rb2 = r10_bio;
- r10_bio = (r10bio_t*) rb2->master_bio;
+ struct r10bio *rb2 = r10_bio;
+ r10_bio = (struct r10bio*) rb2->master_bio;
rb2->master_bio = NULL;
put_buf(rb2);
}
@@ -1979,12 +2602,28 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr,
for (i=0; i<conf->copies; i++) {
int d = r10_bio->devs[i].devnum;
+ sector_t first_bad, sector;
+ int bad_sectors;
+
bio = r10_bio->devs[i].bio;
bio->bi_end_io = NULL;
clear_bit(BIO_UPTODATE, &bio->bi_flags);
if (conf->mirrors[d].rdev == NULL ||
test_bit(Faulty, &conf->mirrors[d].rdev->flags))
continue;
+ sector = r10_bio->devs[i].addr;
+ if (is_badblock(conf->mirrors[d].rdev,
+ sector, max_sync,
+ &first_bad, &bad_sectors)) {
+ if (first_bad > sector)
+ max_sync = first_bad - sector;
+ else {
+ bad_sectors -= (sector - first_bad);
+ if (max_sync > bad_sectors)
+ max_sync = max_sync;
+ continue;
+ }
+ }
atomic_inc(&conf->mirrors[d].rdev->nr_pending);
atomic_inc(&r10_bio->remaining);
bio->bi_next = biolist;
@@ -1992,7 +2631,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr,
bio->bi_private = r10_bio;
bio->bi_end_io = end_sync_read;
bio->bi_rw = READ;
- bio->bi_sector = r10_bio->devs[i].addr +
+ bio->bi_sector = sector +
conf->mirrors[d].rdev->data_offset;
bio->bi_bdev = conf->mirrors[d].rdev->bdev;
count++;
@@ -2079,7 +2718,8 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr,
return sectors_skipped + nr_sectors;
giveup:
/* There is nowhere to write, so all non-sync
- * drives must be failed, so try the next chunk...
+ * drives must be failed or in resync, all drives
+ * have a bad block, so try the next chunk...
*/
if (sector_nr + max_sync < max_sector)
max_sector = sector_nr + max_sync;
@@ -2091,10 +2731,10 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr,
}
static sector_t
-raid10_size(mddev_t *mddev, sector_t sectors, int raid_disks)
+raid10_size(struct mddev *mddev, sector_t sectors, int raid_disks)
{
sector_t size;
- conf_t *conf = mddev->private;
+ struct r10conf *conf = mddev->private;
if (!raid_disks)
raid_disks = conf->raid_disks;
@@ -2110,9 +2750,9 @@ raid10_size(mddev_t *mddev, sector_t sectors, int raid_disks)
}
-static conf_t *setup_conf(mddev_t *mddev)
+static struct r10conf *setup_conf(struct mddev *mddev)
{
- conf_t *conf = NULL;
+ struct r10conf *conf = NULL;
int nc, fc, fo;
sector_t stride, size;
int err = -EINVAL;
@@ -2137,7 +2777,7 @@ static conf_t *setup_conf(mddev_t *mddev)
}
err = -ENOMEM;
- conf = kzalloc(sizeof(conf_t), GFP_KERNEL);
+ conf = kzalloc(sizeof(struct r10conf), GFP_KERNEL);
if (!conf)
goto out;
@@ -2213,12 +2853,12 @@ static conf_t *setup_conf(mddev_t *mddev)
return ERR_PTR(err);
}
-static int run(mddev_t *mddev)
+static int run(struct mddev *mddev)
{
- conf_t *conf;
+ struct r10conf *conf;
int i, disk_idx, chunk_size;
- mirror_info_t *disk;
- mdk_rdev_t *rdev;
+ struct mirror_info *disk;
+ struct md_rdev *rdev;
sector_t size;
/*
@@ -2249,6 +2889,7 @@ static int run(mddev_t *mddev)
(conf->raid_disks / conf->near_copies));
list_for_each_entry(rdev, &mddev->disks, same_set) {
+
disk_idx = rdev->raid_disk;
if (disk_idx >= conf->raid_disks
|| disk_idx < 0)
@@ -2271,7 +2912,7 @@ static int run(mddev_t *mddev)
disk->head_position = 0;
}
/* need to check that every block has at least one working mirror */
- if (!enough(conf)) {
+ if (!enough(conf, -1)) {
printk(KERN_ERR "md/raid10:%s: not enough operational mirrors.\n",
mdname(mddev));
goto out_free_conf;
@@ -2289,6 +2930,7 @@ static int run(mddev_t *mddev)
if (disk->rdev)
conf->fullsync = 1;
}
+ disk->recovery_disabled = mddev->recovery_disabled - 1;
}
if (mddev->recovery_cp != MaxSector)
@@ -2331,7 +2973,7 @@ static int run(mddev_t *mddev)
return 0;
out_free_conf:
- md_unregister_thread(mddev->thread);
+ md_unregister_thread(&mddev->thread);
if (conf->r10bio_pool)
mempool_destroy(conf->r10bio_pool);
safe_put_page(conf->tmppage);
@@ -2342,15 +2984,14 @@ out:
return -EIO;
}
-static int stop(mddev_t *mddev)
+static int stop(struct mddev *mddev)
{
- conf_t *conf = mddev->private;
+ struct r10conf *conf = mddev->private;
raise_barrier(conf, 0);
lower_barrier(conf);
- md_unregister_thread(mddev->thread);
- mddev->thread = NULL;
+ md_unregister_thread(&mddev->thread);
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
if (conf->r10bio_pool)
mempool_destroy(conf->r10bio_pool);
@@ -2360,9 +3001,9 @@ static int stop(mddev_t *mddev)
return 0;
}
-static void raid10_quiesce(mddev_t *mddev, int state)
+static void raid10_quiesce(struct mddev *mddev, int state)
{
- conf_t *conf = mddev->private;
+ struct r10conf *conf = mddev->private;
switch(state) {
case 1:
@@ -2374,10 +3015,10 @@ static void raid10_quiesce(mddev_t *mddev, int state)
}
}
-static void *raid10_takeover_raid0(mddev_t *mddev)
+static void *raid10_takeover_raid0(struct mddev *mddev)
{
- mdk_rdev_t *rdev;
- conf_t *conf;
+ struct md_rdev *rdev;
+ struct r10conf *conf;
if (mddev->degraded > 0) {
printk(KERN_ERR "md/raid10:%s: Error: degraded raid0!\n",
@@ -2406,17 +3047,17 @@ static void *raid10_takeover_raid0(mddev_t *mddev)
return conf;
}
-static void *raid10_takeover(mddev_t *mddev)
+static void *raid10_takeover(struct mddev *mddev)
{
- struct raid0_private_data *raid0_priv;
+ struct r0conf *raid0_conf;
/* raid10 can take over:
* raid0 - providing it has only two drives
*/
if (mddev->level == 0) {
/* for raid0 takeover only one zone is supported */
- raid0_priv = mddev->private;
- if (raid0_priv->nr_strip_zones > 1) {
+ raid0_conf = mddev->private;
+ if (raid0_conf->nr_strip_zones > 1) {
printk(KERN_ERR "md/raid10:%s: cannot takeover raid 0"
" with more than one zone.\n",
mdname(mddev));
@@ -2427,7 +3068,7 @@ static void *raid10_takeover(mddev_t *mddev)
return ERR_PTR(-EINVAL);
}
-static struct mdk_personality raid10_personality =
+static struct md_personality raid10_personality =
{
.name = "raid10",
.level = 10,
@@ -2463,3 +3104,5 @@ MODULE_DESCRIPTION("RAID10 (striped mirror) personality for MD");
MODULE_ALIAS("md-personality-9"); /* RAID10 */
MODULE_ALIAS("md-raid10");
MODULE_ALIAS("md-level-10");
+
+module_param(max_queued_requests, int, S_IRUGO|S_IWUSR);
diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index 944b1104d3b4..7facfdf841f4 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -1,18 +1,19 @@
#ifndef _RAID10_H
#define _RAID10_H
-typedef struct mirror_info mirror_info_t;
-
struct mirror_info {
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
sector_t head_position;
+ int recovery_disabled; /* matches
+ * mddev->recovery_disabled
+ * when we shouldn't try
+ * recovering this device.
+ */
};
-typedef struct r10bio_s r10bio_t;
-
-struct r10_private_data_s {
- mddev_t *mddev;
- mirror_info_t *mirrors;
+struct r10conf {
+ struct mddev *mddev;
+ struct mirror_info *mirrors;
int raid_disks;
spinlock_t device_lock;
@@ -41,7 +42,7 @@ struct r10_private_data_s {
struct list_head retry_list;
/* queue pending writes and submit them on unplug */
struct bio_list pending_bio_list;
-
+ int pending_count;
spinlock_t resync_lock;
int nr_pending;
@@ -63,11 +64,9 @@ struct r10_private_data_s {
/* When taking over an array from a different personality, we store
* the new thread here until we fully activate the array.
*/
- struct mdk_thread_s *thread;
+ struct md_thread *thread;
};
-typedef struct r10_private_data_s conf_t;
-
/*
* this is our 'private' RAID10 bio.
*
@@ -75,14 +74,14 @@ typedef struct r10_private_data_s conf_t;
* for this RAID10 operation, and about their status:
*/
-struct r10bio_s {
+struct r10bio {
atomic_t remaining; /* 'have we finished' count,
* used from IRQ handlers
*/
sector_t sector; /* virtual sector number */
int sectors;
unsigned long state;
- mddev_t *mddev;
+ struct mddev *mddev;
/*
* original bio going to /dev/mdx
*/
@@ -113,10 +112,26 @@ struct r10bio_s {
* level, we store IO_BLOCKED in the appropriate 'bios' pointer
*/
#define IO_BLOCKED ((struct bio*)1)
+/* When we successfully write to a known bad-block, we need to remove the
+ * bad-block marking which must be done from process context. So we record
+ * the success by setting devs[n].bio to IO_MADE_GOOD
+ */
+#define IO_MADE_GOOD ((struct bio *)2)
+
+#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
/* bits for r10bio.state */
#define R10BIO_Uptodate 0
#define R10BIO_IsSync 1
#define R10BIO_IsRecover 2
#define R10BIO_Degraded 3
+/* Set ReadError on bios that experience a read error
+ * so that raid10d knows what to do with them.
+ */
+#define R10BIO_ReadError 4
+/* If a write for this request means we can clear some
+ * known-bad-block records, we set this flag.
+ */
+#define R10BIO_MadeGood 5
+#define R10BIO_WriteError 6
#endif
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index b72edf35ec54..297e26092178 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -47,10 +47,12 @@
#include <linux/kthread.h>
#include <linux/raid/pq.h>
#include <linux/async_tx.h>
+#include <linux/module.h>
#include <linux/async.h>
#include <linux/seq_file.h>
#include <linux/cpu.h>
#include <linux/slab.h>
+#include <linux/ratelimit.h>
#include "md.h"
#include "raid5.h"
#include "raid0.h"
@@ -69,7 +71,11 @@
#define NR_HASH (PAGE_SIZE / sizeof(struct hlist_head))
#define HASH_MASK (NR_HASH - 1)
-#define stripe_hash(conf, sect) (&((conf)->stripe_hashtbl[((sect) >> STRIPE_SHIFT) & HASH_MASK]))
+static inline struct hlist_head *stripe_hash(struct r5conf *conf, sector_t sect)
+{
+ int hash = (sect >> STRIPE_SHIFT) & HASH_MASK;
+ return &conf->stripe_hashtbl[hash];
+}
/* bio's attached to a stripe+device for I/O are linked together in bi_sector
* order without overlap. There may be several bio's per stripe+device, and
@@ -77,26 +83,17 @@
* When walking this list for a particular stripe+device, we must never proceed
* beyond a bio that extends past this device, as the next bio might no longer
* be valid.
- * This macro is used to determine the 'next' bio in the list, given the sector
+ * This function is used to determine the 'next' bio in the list, given the sector
* of the current stripe+device
*/
-#define r5_next_bio(bio, sect) ( ( (bio)->bi_sector + ((bio)->bi_size>>9) < sect + STRIPE_SECTORS) ? (bio)->bi_next : NULL)
-/*
- * The following can be used to debug the driver
- */
-#define RAID5_PARANOIA 1
-#if RAID5_PARANOIA && defined(CONFIG_SMP)
-# define CHECK_DEVLOCK() assert_spin_locked(&conf->device_lock)
-#else
-# define CHECK_DEVLOCK()
-#endif
-
-#ifdef DEBUG
-#define inline
-#define __inline__
-#endif
-
-#define printk_rl(args...) ((void) (printk_ratelimit() && printk(args)))
+static inline struct bio *r5_next_bio(struct bio *bio, sector_t sector)
+{
+ int sectors = bio->bi_size >> 9;
+ if (bio->bi_sector + sectors < sector + STRIPE_SECTORS)
+ return bio->bi_next;
+ else
+ return NULL;
+}
/*
* We maintain a biased count of active stripes in the bottom 16 bits of
@@ -184,7 +181,7 @@ static void return_io(struct bio *return_bi)
}
}
-static void print_raid5_conf (raid5_conf_t *conf);
+static void print_raid5_conf (struct r5conf *conf);
static int stripe_operations_active(struct stripe_head *sh)
{
@@ -193,7 +190,7 @@ static int stripe_operations_active(struct stripe_head *sh)
test_bit(STRIPE_COMPUTE_RUN, &sh->state);
}
-static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh)
+static void __release_stripe(struct r5conf *conf, struct stripe_head *sh)
{
if (atomic_dec_and_test(&sh->count)) {
BUG_ON(!list_empty(&sh->lru));
@@ -229,7 +226,7 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh)
static void release_stripe(struct stripe_head *sh)
{
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
unsigned long flags;
spin_lock_irqsave(&conf->device_lock, flags);
@@ -245,25 +242,23 @@ static inline void remove_hash(struct stripe_head *sh)
hlist_del_init(&sh->hash);
}
-static inline void insert_hash(raid5_conf_t *conf, struct stripe_head *sh)
+static inline void insert_hash(struct r5conf *conf, struct stripe_head *sh)
{
struct hlist_head *hp = stripe_hash(conf, sh->sector);
pr_debug("insert_hash(), stripe %llu\n",
(unsigned long long)sh->sector);
- CHECK_DEVLOCK();
hlist_add_head(&sh->hash, hp);
}
/* find an idle stripe, make sure it is unhashed, and return it. */
-static struct stripe_head *get_free_stripe(raid5_conf_t *conf)
+static struct stripe_head *get_free_stripe(struct r5conf *conf)
{
struct stripe_head *sh = NULL;
struct list_head *first;
- CHECK_DEVLOCK();
if (list_empty(&conf->inactive_list))
goto out;
first = conf->inactive_list.next;
@@ -307,19 +302,18 @@ static int grow_buffers(struct stripe_head *sh)
}
static void raid5_build_block(struct stripe_head *sh, int i, int previous);
-static void stripe_set_idx(sector_t stripe, raid5_conf_t *conf, int previous,
+static void stripe_set_idx(sector_t stripe, struct r5conf *conf, int previous,
struct stripe_head *sh);
static void init_stripe(struct stripe_head *sh, sector_t sector, int previous)
{
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
int i;
BUG_ON(atomic_read(&sh->count) != 0);
BUG_ON(test_bit(STRIPE_HANDLE, &sh->state));
BUG_ON(stripe_operations_active(sh));
- CHECK_DEVLOCK();
pr_debug("init_stripe called, stripe %llu\n",
(unsigned long long)sh->sector);
@@ -341,7 +335,7 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int previous)
(unsigned long long)sh->sector, i, dev->toread,
dev->read, dev->towrite, dev->written,
test_bit(R5_LOCKED, &dev->flags));
- BUG();
+ WARN_ON(1);
}
dev->flags = 0;
raid5_build_block(sh, i, previous);
@@ -349,13 +343,12 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int previous)
insert_hash(conf, sh);
}
-static struct stripe_head *__find_stripe(raid5_conf_t *conf, sector_t sector,
+static struct stripe_head *__find_stripe(struct r5conf *conf, sector_t sector,
short generation)
{
struct stripe_head *sh;
struct hlist_node *hn;
- CHECK_DEVLOCK();
pr_debug("__find_stripe, sector %llu\n", (unsigned long long)sector);
hlist_for_each_entry(sh, hn, stripe_hash(conf, sector), hash)
if (sh->sector == sector && sh->generation == generation)
@@ -377,7 +370,7 @@ static struct stripe_head *__find_stripe(raid5_conf_t *conf, sector_t sector,
* of the two sections, and some non-in_sync devices may
* be insync in the section most affected by failed devices.
*/
-static int has_failed(raid5_conf_t *conf)
+static int has_failed(struct r5conf *conf)
{
int degraded;
int i;
@@ -387,7 +380,7 @@ static int has_failed(raid5_conf_t *conf)
rcu_read_lock();
degraded = 0;
for (i = 0; i < conf->previous_raid_disks; i++) {
- mdk_rdev_t *rdev = rcu_dereference(conf->disks[i].rdev);
+ struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
if (!rdev || test_bit(Faulty, &rdev->flags))
degraded++;
else if (test_bit(In_sync, &rdev->flags))
@@ -411,7 +404,7 @@ static int has_failed(raid5_conf_t *conf)
rcu_read_lock();
degraded = 0;
for (i = 0; i < conf->raid_disks; i++) {
- mdk_rdev_t *rdev = rcu_dereference(conf->disks[i].rdev);
+ struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
if (!rdev || test_bit(Faulty, &rdev->flags))
degraded++;
else if (test_bit(In_sync, &rdev->flags))
@@ -432,7 +425,7 @@ static int has_failed(raid5_conf_t *conf)
}
static struct stripe_head *
-get_active_stripe(raid5_conf_t *conf, sector_t sector,
+get_active_stripe(struct r5conf *conf, sector_t sector,
int previous, int noblock, int noquiesce)
{
struct stripe_head *sh;
@@ -492,7 +485,7 @@ raid5_end_write_request(struct bio *bi, int error);
static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
{
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
int i, disks = sh->disks;
might_sleep();
@@ -500,7 +493,7 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
for (i = disks; i--; ) {
int rw;
struct bio *bi;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
if (test_and_clear_bit(R5_Wantwrite, &sh->dev[i].flags)) {
if (test_and_clear_bit(R5_WantFUA, &sh->dev[i].flags))
rw = WRITE_FUA;
@@ -527,6 +520,36 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
atomic_inc(&rdev->nr_pending);
rcu_read_unlock();
+ /* We have already checked bad blocks for reads. Now
+ * need to check for writes.
+ */
+ while ((rw & WRITE) && rdev &&
+ test_bit(WriteErrorSeen, &rdev->flags)) {
+ sector_t first_bad;
+ int bad_sectors;
+ int bad = is_badblock(rdev, sh->sector, STRIPE_SECTORS,
+ &first_bad, &bad_sectors);
+ if (!bad)
+ break;
+
+ if (bad < 0) {
+ set_bit(BlockedBadBlocks, &rdev->flags);
+ if (!conf->mddev->external &&
+ conf->mddev->flags) {
+ /* It is very unlikely, but we might
+ * still need to write out the
+ * bad block log - better give it
+ * a chance*/
+ md_check_recovery(conf->mddev);
+ }
+ md_wait_for_blocked_rdev(rdev, conf->mddev);
+ } else {
+ /* Acknowledged bad block - skip the write */
+ rdev_dec_pending(rdev, conf->mddev);
+ rdev = NULL;
+ }
+ }
+
if (rdev) {
if (s->syncing || s->expanding || s->expanded)
md_sync_acct(rdev->bdev, STRIPE_SECTORS);
@@ -548,10 +571,6 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
bi->bi_io_vec[0].bv_offset = 0;
bi->bi_size = STRIPE_SIZE;
bi->bi_next = NULL;
- if ((rw & WRITE) &&
- test_bit(R5_ReWrite, &sh->dev[i].flags))
- atomic_add(STRIPE_SECTORS,
- &rdev->corrected_errors);
generic_make_request(bi);
} else {
if (rw & WRITE)
@@ -625,7 +644,7 @@ static void ops_complete_biofill(void *stripe_head_ref)
{
struct stripe_head *sh = stripe_head_ref;
struct bio *return_bi = NULL;
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
int i;
pr_debug("%s: stripe %llu\n", __func__,
@@ -670,7 +689,7 @@ static void ops_complete_biofill(void *stripe_head_ref)
static void ops_run_biofill(struct stripe_head *sh)
{
struct dma_async_tx_descriptor *tx = NULL;
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
struct async_submit_ctl submit;
int i;
@@ -1020,12 +1039,12 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx)
if (test_and_clear_bit(R5_Wantdrain, &dev->flags)) {
struct bio *wbi;
- spin_lock(&sh->lock);
+ spin_lock_irq(&sh->raid_conf->device_lock);
chosen = dev->towrite;
dev->towrite = NULL;
BUG_ON(dev->written);
wbi = dev->written = chosen;
- spin_unlock(&sh->lock);
+ spin_unlock_irq(&sh->raid_conf->device_lock);
while (wbi && wbi->bi_sector <
dev->sector + STRIPE_SECTORS) {
@@ -1221,7 +1240,7 @@ static void __raid_run_ops(struct stripe_head *sh, unsigned long ops_request)
{
int overlap_clear = 0, i, disks = sh->disks;
struct dma_async_tx_descriptor *tx = NULL;
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
int level = conf->level;
struct raid5_percpu *percpu;
unsigned long cpu;
@@ -1312,15 +1331,14 @@ static void raid_run_ops(struct stripe_head *sh, unsigned long ops_request)
#define raid_run_ops __raid_run_ops
#endif
-static int grow_one_stripe(raid5_conf_t *conf)
+static int grow_one_stripe(struct r5conf *conf)
{
struct stripe_head *sh;
- sh = kmem_cache_alloc(conf->slab_cache, GFP_KERNEL);
+ sh = kmem_cache_zalloc(conf->slab_cache, GFP_KERNEL);
if (!sh)
return 0;
- memset(sh, 0, sizeof(*sh) + (conf->pool_size-1)*sizeof(struct r5dev));
+
sh->raid_conf = conf;
- spin_lock_init(&sh->lock);
#ifdef CONFIG_MULTICORE_RAID456
init_waitqueue_head(&sh->ops.wait_for_ops);
#endif
@@ -1338,7 +1356,7 @@ static int grow_one_stripe(raid5_conf_t *conf)
return 1;
}
-static int grow_stripes(raid5_conf_t *conf, int num)
+static int grow_stripes(struct r5conf *conf, int num)
{
struct kmem_cache *sc;
int devs = max(conf->raid_disks, conf->previous_raid_disks);
@@ -1387,7 +1405,7 @@ static size_t scribble_len(int num)
return len;
}
-static int resize_stripes(raid5_conf_t *conf, int newsize)
+static int resize_stripes(struct r5conf *conf, int newsize)
{
/* Make all the stripes able to hold 'newsize' devices.
* New slots in each stripe get 'page' set to a new page.
@@ -1435,14 +1453,11 @@ static int resize_stripes(raid5_conf_t *conf, int newsize)
return -ENOMEM;
for (i = conf->max_nr_stripes; i; i--) {
- nsh = kmem_cache_alloc(sc, GFP_KERNEL);
+ nsh = kmem_cache_zalloc(sc, GFP_KERNEL);
if (!nsh)
break;
- memset(nsh, 0, sizeof(*nsh) + (newsize-1)*sizeof(struct r5dev));
-
nsh->raid_conf = conf;
- spin_lock_init(&nsh->lock);
#ifdef CONFIG_MULTICORE_RAID456
init_waitqueue_head(&nsh->ops.wait_for_ops);
#endif
@@ -1535,7 +1550,7 @@ static int resize_stripes(raid5_conf_t *conf, int newsize)
return err;
}
-static int drop_one_stripe(raid5_conf_t *conf)
+static int drop_one_stripe(struct r5conf *conf)
{
struct stripe_head *sh;
@@ -1551,7 +1566,7 @@ static int drop_one_stripe(raid5_conf_t *conf)
return 1;
}
-static void shrink_stripes(raid5_conf_t *conf)
+static void shrink_stripes(struct r5conf *conf)
{
while (drop_one_stripe(conf))
;
@@ -1564,11 +1579,11 @@ static void shrink_stripes(raid5_conf_t *conf)
static void raid5_end_read_request(struct bio * bi, int error)
{
struct stripe_head *sh = bi->bi_private;
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
int disks = sh->disks, i;
int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);
char b[BDEVNAME_SIZE];
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
for (i=0 ; i<disks; i++)
@@ -1587,12 +1602,15 @@ static void raid5_end_read_request(struct bio * bi, int error)
set_bit(R5_UPTODATE, &sh->dev[i].flags);
if (test_bit(R5_ReadError, &sh->dev[i].flags)) {
rdev = conf->disks[i].rdev;
- printk_rl(KERN_INFO "md/raid:%s: read error corrected"
- " (%lu sectors at %llu on %s)\n",
- mdname(conf->mddev), STRIPE_SECTORS,
- (unsigned long long)(sh->sector
- + rdev->data_offset),
- bdevname(rdev->bdev, b));
+ printk_ratelimited(
+ KERN_INFO
+ "md/raid:%s: read error corrected"
+ " (%lu sectors at %llu on %s)\n",
+ mdname(conf->mddev), STRIPE_SECTORS,
+ (unsigned long long)(sh->sector
+ + rdev->data_offset),
+ bdevname(rdev->bdev, b));
+ atomic_add(STRIPE_SECTORS, &rdev->corrected_errors);
clear_bit(R5_ReadError, &sh->dev[i].flags);
clear_bit(R5_ReWrite, &sh->dev[i].flags);
}
@@ -1606,22 +1624,24 @@ static void raid5_end_read_request(struct bio * bi, int error)
clear_bit(R5_UPTODATE, &sh->dev[i].flags);
atomic_inc(&rdev->read_errors);
if (conf->mddev->degraded >= conf->max_degraded)
- printk_rl(KERN_WARNING
- "md/raid:%s: read error not correctable "
- "(sector %llu on %s).\n",
- mdname(conf->mddev),
- (unsigned long long)(sh->sector
- + rdev->data_offset),
- bdn);
+ printk_ratelimited(
+ KERN_WARNING
+ "md/raid:%s: read error not correctable "
+ "(sector %llu on %s).\n",
+ mdname(conf->mddev),
+ (unsigned long long)(sh->sector
+ + rdev->data_offset),
+ bdn);
else if (test_bit(R5_ReWrite, &sh->dev[i].flags))
/* Oh, no!!! */
- printk_rl(KERN_WARNING
- "md/raid:%s: read error NOT corrected!! "
- "(sector %llu on %s).\n",
- mdname(conf->mddev),
- (unsigned long long)(sh->sector
- + rdev->data_offset),
- bdn);
+ printk_ratelimited(
+ KERN_WARNING
+ "md/raid:%s: read error NOT corrected!! "
+ "(sector %llu on %s).\n",
+ mdname(conf->mddev),
+ (unsigned long long)(sh->sector
+ + rdev->data_offset),
+ bdn);
else if (atomic_read(&rdev->read_errors)
> conf->max_nr_stripes)
printk(KERN_WARNING
@@ -1646,9 +1666,11 @@ static void raid5_end_read_request(struct bio * bi, int error)
static void raid5_end_write_request(struct bio *bi, int error)
{
struct stripe_head *sh = bi->bi_private;
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
int disks = sh->disks, i;
int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);
+ sector_t first_bad;
+ int bad_sectors;
for (i=0 ; i<disks; i++)
if (bi == &sh->dev[i].req)
@@ -1662,8 +1684,12 @@ static void raid5_end_write_request(struct bio *bi, int error)
return;
}
- if (!uptodate)
- md_error(conf->mddev, conf->disks[i].rdev);
+ if (!uptodate) {
+ set_bit(WriteErrorSeen, &conf->disks[i].rdev->flags);
+ set_bit(R5_WriteError, &sh->dev[i].flags);
+ } else if (is_badblock(conf->disks[i].rdev, sh->sector, STRIPE_SECTORS,
+ &first_bad, &bad_sectors))
+ set_bit(R5_MadeGood, &sh->dev[i].flags);
rdev_dec_pending(conf->disks[i].rdev, conf->mddev);
@@ -1694,10 +1720,10 @@ static void raid5_build_block(struct stripe_head *sh, int i, int previous)
dev->sector = compute_blocknr(sh, i, previous);
}
-static void error(mddev_t *mddev, mdk_rdev_t *rdev)
+static void error(struct mddev *mddev, struct md_rdev *rdev)
{
char b[BDEVNAME_SIZE];
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
pr_debug("raid456: error called\n");
if (test_and_clear_bit(In_sync, &rdev->flags)) {
@@ -1710,6 +1736,7 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
*/
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
}
+ set_bit(Blocked, &rdev->flags);
set_bit(Faulty, &rdev->flags);
set_bit(MD_CHANGE_DEVS, &mddev->flags);
printk(KERN_ALERT
@@ -1725,7 +1752,7 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
* Input: a 'big' sector number,
* Output: index of the data and parity disk, and the sector # in them.
*/
-static sector_t raid5_compute_sector(raid5_conf_t *conf, sector_t r_sector,
+static sector_t raid5_compute_sector(struct r5conf *conf, sector_t r_sector,
int previous, int *dd_idx,
struct stripe_head *sh)
{
@@ -1760,7 +1787,7 @@ static sector_t raid5_compute_sector(raid5_conf_t *conf, sector_t r_sector,
/*
* Select the parity disk based on the user selected algorithm.
*/
- pd_idx = qd_idx = ~0;
+ pd_idx = qd_idx = -1;
switch(conf->level) {
case 4:
pd_idx = data_disks;
@@ -1930,7 +1957,7 @@ static sector_t raid5_compute_sector(raid5_conf_t *conf, sector_t r_sector,
static sector_t compute_blocknr(struct stripe_head *sh, int i, int previous)
{
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
int raid_disks = sh->disks;
int data_disks = raid_disks - conf->max_degraded;
sector_t new_sector = sh->sector, check;
@@ -2055,7 +2082,7 @@ schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s,
int rcw, int expand)
{
int i, pd_idx = sh->pd_idx, disks = sh->disks;
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
int level = conf->level;
if (rcw) {
@@ -2140,15 +2167,14 @@ schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s,
static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, int forwrite)
{
struct bio **bip;
- raid5_conf_t *conf = sh->raid_conf;
+ struct r5conf *conf = sh->raid_conf;
int firstwrite=0;
- pr_debug("adding bh b#%llu to stripe s#%llu\n",
+ pr_debug("adding bi b#%llu to stripe s#%llu\n",
(unsigned long long)bi->bi_sector,
(unsigned long long)sh->sector);
- spin_lock(&sh->lock);
spin_lock_irq(&conf->device_lock);
if (forwrite) {
bip = &sh->dev[dd_idx].towrite;
@@ -2169,19 +2195,6 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
bi->bi_next = *bip;
*bip = bi;
bi->bi_phys_segments++;
- spin_unlock_irq(&conf->device_lock);
- spin_unlock(&sh->lock);
-
- pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n",
- (unsigned long long)bi->bi_sector,
- (unsigned long long)sh->sector, dd_idx);
-
- if (conf->mddev->bitmap && firstwrite) {
- bitmap_startwrite(conf->mddev->bitmap, sh->sector,
- STRIPE_SECTORS, 0);
- sh->bm_seq = conf->seq_flush+1;
- set_bit(STRIPE_BIT_DELAY, &sh->state);
- }
if (forwrite) {
/* check if page is covered */
@@ -2196,18 +2209,29 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
if (sector >= sh->dev[dd_idx].sector + STRIPE_SECTORS)
set_bit(R5_OVERWRITE, &sh->dev[dd_idx].flags);
}
+ spin_unlock_irq(&conf->device_lock);
+
+ pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n",
+ (unsigned long long)(*bip)->bi_sector,
+ (unsigned long long)sh->sector, dd_idx);
+
+ if (conf->mddev->bitmap && firstwrite) {
+ bitmap_startwrite(conf->mddev->bitmap, sh->sector,
+ STRIPE_SECTORS, 0);
+ sh->bm_seq = conf->seq_flush+1;
+ set_bit(STRIPE_BIT_DELAY, &sh->state);
+ }
return 1;
overlap:
set_bit(R5_Overlap, &sh->dev[dd_idx].flags);
spin_unlock_irq(&conf->device_lock);
- spin_unlock(&sh->lock);
return 0;
}
-static void end_reshape(raid5_conf_t *conf);
+static void end_reshape(struct r5conf *conf);
-static void stripe_set_idx(sector_t stripe, raid5_conf_t *conf, int previous,
+static void stripe_set_idx(sector_t stripe, struct r5conf *conf, int previous,
struct stripe_head *sh)
{
int sectors_per_chunk =
@@ -2224,7 +2248,7 @@ static void stripe_set_idx(sector_t stripe, raid5_conf_t *conf, int previous,
}
static void
-handle_failed_stripe(raid5_conf_t *conf, struct stripe_head *sh,
+handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
struct stripe_head_state *s, int disks,
struct bio **return_bi)
{
@@ -2234,13 +2258,22 @@ handle_failed_stripe(raid5_conf_t *conf, struct stripe_head *sh,
int bitmap_end = 0;
if (test_bit(R5_ReadError, &sh->dev[i].flags)) {
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
rcu_read_lock();
rdev = rcu_dereference(conf->disks[i].rdev);
if (rdev && test_bit(In_sync, &rdev->flags))
- /* multiple read failures in one stripe */
- md_error(conf->mddev, rdev);
+ atomic_inc(&rdev->nr_pending);
+ else
+ rdev = NULL;
rcu_read_unlock();
+ if (rdev) {
+ if (!rdev_set_badblocks(
+ rdev,
+ sh->sector,
+ STRIPE_SECTORS, 0))
+ md_error(conf->mddev, rdev);
+ rdev_dec_pending(rdev, conf->mddev);
+ }
}
spin_lock_irq(&conf->device_lock);
/* fail all writes first */
@@ -2308,6 +2341,10 @@ handle_failed_stripe(raid5_conf_t *conf, struct stripe_head *sh,
if (bitmap_end)
bitmap_endwrite(conf->mddev->bitmap, sh->sector,
STRIPE_SECTORS, 0, 0);
+ /* If we were in the middle of a write the parity block might
+ * still be locked - so just clear all R5_LOCKED flags
+ */
+ clear_bit(R5_LOCKED, &sh->dev[i].flags);
}
if (test_and_clear_bit(STRIPE_FULL_WRITE, &sh->state))
@@ -2315,109 +2352,73 @@ handle_failed_stripe(raid5_conf_t *conf, struct stripe_head *sh,
md_wakeup_thread(conf->mddev->thread);
}
-/* fetch_block5 - checks the given member device to see if its data needs
- * to be read or computed to satisfy a request.
- *
- * Returns 1 when no more member devices need to be checked, otherwise returns
- * 0 to tell the loop in handle_stripe_fill5 to continue
- */
-static int fetch_block5(struct stripe_head *sh, struct stripe_head_state *s,
- int disk_idx, int disks)
-{
- struct r5dev *dev = &sh->dev[disk_idx];
- struct r5dev *failed_dev = &sh->dev[s->failed_num];
-
- /* is the data in this block needed, and can we get it? */
- if (!test_bit(R5_LOCKED, &dev->flags) &&
- !test_bit(R5_UPTODATE, &dev->flags) &&
- (dev->toread ||
- (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) ||
- s->syncing || s->expanding ||
- (s->failed &&
- (failed_dev->toread ||
- (failed_dev->towrite &&
- !test_bit(R5_OVERWRITE, &failed_dev->flags)))))) {
- /* We would like to get this block, possibly by computing it,
- * otherwise read it if the backing disk is insync
- */
- if ((s->uptodate == disks - 1) &&
- (s->failed && disk_idx == s->failed_num)) {
- set_bit(STRIPE_COMPUTE_RUN, &sh->state);
- set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request);
- set_bit(R5_Wantcompute, &dev->flags);
- sh->ops.target = disk_idx;
- sh->ops.target2 = -1;
- s->req_compute = 1;
- /* Careful: from this point on 'uptodate' is in the eye
- * of raid_run_ops which services 'compute' operations
- * before writes. R5_Wantcompute flags a block that will
- * be R5_UPTODATE by the time it is needed for a
- * subsequent operation.
- */
- s->uptodate++;
- return 1; /* uptodate + compute == disks */
- } else if (test_bit(R5_Insync, &dev->flags)) {
- set_bit(R5_LOCKED, &dev->flags);
- set_bit(R5_Wantread, &dev->flags);
- s->locked++;
- pr_debug("Reading block %d (sync=%d)\n", disk_idx,
- s->syncing);
- }
- }
-
- return 0;
-}
-
-/**
- * handle_stripe_fill5 - read or compute data to satisfy pending requests.
- */
-static void handle_stripe_fill5(struct stripe_head *sh,
- struct stripe_head_state *s, int disks)
+static void
+handle_failed_sync(struct r5conf *conf, struct stripe_head *sh,
+ struct stripe_head_state *s)
{
+ int abort = 0;
int i;
- /* look for blocks to read/compute, skip this if a compute
- * is already in flight, or if the stripe contents are in the
- * midst of changing due to a write
+ md_done_sync(conf->mddev, STRIPE_SECTORS, 0);
+ clear_bit(STRIPE_SYNCING, &sh->state);
+ s->syncing = 0;
+ /* There is nothing more to do for sync/check/repair.
+ * For recover we need to record a bad block on all
+ * non-sync devices, or abort the recovery
*/
- if (!test_bit(STRIPE_COMPUTE_RUN, &sh->state) && !sh->check_state &&
- !sh->reconstruct_state)
- for (i = disks; i--; )
- if (fetch_block5(sh, s, i, disks))
- break;
- set_bit(STRIPE_HANDLE, &sh->state);
+ if (!test_bit(MD_RECOVERY_RECOVER, &conf->mddev->recovery))
+ return;
+ /* During recovery devices cannot be removed, so locking and
+ * refcounting of rdevs is not needed
+ */
+ for (i = 0; i < conf->raid_disks; i++) {
+ struct md_rdev *rdev = conf->disks[i].rdev;
+ if (!rdev
+ || test_bit(Faulty, &rdev->flags)
+ || test_bit(In_sync, &rdev->flags))
+ continue;
+ if (!rdev_set_badblocks(rdev, sh->sector,
+ STRIPE_SECTORS, 0))
+ abort = 1;
+ }
+ if (abort) {
+ conf->recovery_disabled = conf->mddev->recovery_disabled;
+ set_bit(MD_RECOVERY_INTR, &conf->mddev->recovery);
+ }
}
-/* fetch_block6 - checks the given member device to see if its data needs
+/* fetch_block - checks the given member device to see if its data needs
* to be read or computed to satisfy a request.
*
* Returns 1 when no more member devices need to be checked, otherwise returns
- * 0 to tell the loop in handle_stripe_fill6 to continue
+ * 0 to tell the loop in handle_stripe_fill to continue
*/
-static int fetch_block6(struct stripe_head *sh, struct stripe_head_state *s,
- struct r6_state *r6s, int disk_idx, int disks)
+static int fetch_block(struct stripe_head *sh, struct stripe_head_state *s,
+ int disk_idx, int disks)
{
struct r5dev *dev = &sh->dev[disk_idx];
- struct r5dev *fdev[2] = { &sh->dev[r6s->failed_num[0]],
- &sh->dev[r6s->failed_num[1]] };
+ struct r5dev *fdev[2] = { &sh->dev[s->failed_num[0]],
+ &sh->dev[s->failed_num[1]] };
+ /* is the data in this block needed, and can we get it? */
if (!test_bit(R5_LOCKED, &dev->flags) &&
!test_bit(R5_UPTODATE, &dev->flags) &&
(dev->toread ||
(dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) ||
s->syncing || s->expanding ||
- (s->failed >= 1 &&
- (fdev[0]->toread || s->to_write)) ||
- (s->failed >= 2 &&
- (fdev[1]->toread || s->to_write)))) {
+ (s->failed >= 1 && fdev[0]->toread) ||
+ (s->failed >= 2 && fdev[1]->toread) ||
+ (sh->raid_conf->level <= 5 && s->failed && fdev[0]->towrite &&
+ !test_bit(R5_OVERWRITE, &fdev[0]->flags)) ||
+ (sh->raid_conf->level == 6 && s->failed && s->to_write))) {
/* we would like to get this block, possibly by computing it,
* otherwise read it if the backing disk is insync
*/
BUG_ON(test_bit(R5_Wantcompute, &dev->flags));
BUG_ON(test_bit(R5_Wantread, &dev->flags));
if ((s->uptodate == disks - 1) &&
- (s->failed && (disk_idx == r6s->failed_num[0] ||
- disk_idx == r6s->failed_num[1]))) {
+ (s->failed && (disk_idx == s->failed_num[0] ||
+ disk_idx == s->failed_num[1]))) {
/* have disk failed, and we're requested to fetch it;
* do compute it
*/
@@ -2429,6 +2430,12 @@ static int fetch_block6(struct stripe_head *sh, struct stripe_head_state *s,
sh->ops.target = disk_idx;
sh->ops.target2 = -1; /* no 2nd target */
s->req_compute = 1;
+ /* Careful: from this point on 'uptodate' is in the eye
+ * of raid_run_ops which services 'compute' operations
+ * before writes. R5_Wantcompute flags a block that will
+ * be R5_UPTODATE by the time it is needed for a
+ * subsequent operation.
+ */
s->uptodate++;
return 1;
} else if (s->uptodate == disks-2 && s->failed >= 2) {
@@ -2469,11 +2476,11 @@ static int fetch_block6(struct stripe_head *sh, struct stripe_head_state *s,
}
/**
- * handle_stripe_fill6 - read or compute data to satisfy pending requests.
+ * handle_stripe_fill - read or compute data to satisfy pending requests.
*/
-static void handle_stripe_fill6(struct stripe_head *sh,
- struct stripe_head_state *s, struct r6_state *r6s,
- int disks)
+static void handle_stripe_fill(struct stripe_head *sh,
+ struct stripe_head_state *s,
+ int disks)
{
int i;
@@ -2484,7 +2491,7 @@ static void handle_stripe_fill6(struct stripe_head *sh,
if (!test_bit(STRIPE_COMPUTE_RUN, &sh->state) && !sh->check_state &&
!sh->reconstruct_state)
for (i = disks; i--; )
- if (fetch_block6(sh, s, r6s, i, disks))
+ if (fetch_block(sh, s, i, disks))
break;
set_bit(STRIPE_HANDLE, &sh->state);
}
@@ -2495,7 +2502,7 @@ static void handle_stripe_fill6(struct stripe_head *sh,
* Note that if we 'wrote' to a failed drive, it will be UPTODATE, but
* never LOCKED, so we don't need to test 'failed' directly.
*/
-static void handle_stripe_clean_event(raid5_conf_t *conf,
+static void handle_stripe_clean_event(struct r5conf *conf,
struct stripe_head *sh, int disks, struct bio **return_bi)
{
int i;
@@ -2540,11 +2547,19 @@ static void handle_stripe_clean_event(raid5_conf_t *conf,
md_wakeup_thread(conf->mddev->thread);
}
-static void handle_stripe_dirtying5(raid5_conf_t *conf,
- struct stripe_head *sh, struct stripe_head_state *s, int disks)
+static void handle_stripe_dirtying(struct r5conf *conf,
+ struct stripe_head *sh,
+ struct stripe_head_state *s,
+ int disks)
{
int rmw = 0, rcw = 0, i;
- for (i = disks; i--; ) {
+ if (conf->max_degraded == 2) {
+ /* RAID6 requires 'rcw' in current implementation
+ * Calculate the real rcw later - for now fake it
+ * look like rcw is cheaper
+ */
+ rcw = 1; rmw = 2;
+ } else for (i = disks; i--; ) {
/* would I have to read this buffer for read_modify_write */
struct r5dev *dev = &sh->dev[i];
if ((dev->towrite || i == sh->pd_idx) &&
@@ -2591,16 +2606,19 @@ static void handle_stripe_dirtying5(raid5_conf_t *conf,
}
}
}
- if (rcw <= rmw && rcw > 0)
+ if (rcw <= rmw && rcw > 0) {
/* want reconstruct write, but need to get some data */
+ rcw = 0;
for (i = disks; i--; ) {
struct r5dev *dev = &sh->dev[i];
if (!test_bit(R5_OVERWRITE, &dev->flags) &&
- i != sh->pd_idx &&
+ i != sh->pd_idx && i != sh->qd_idx &&
!test_bit(R5_LOCKED, &dev->flags) &&
!(test_bit(R5_UPTODATE, &dev->flags) ||
- test_bit(R5_Wantcompute, &dev->flags)) &&
- test_bit(R5_Insync, &dev->flags)) {
+ test_bit(R5_Wantcompute, &dev->flags))) {
+ rcw++;
+ if (!test_bit(R5_Insync, &dev->flags))
+ continue; /* it's a failed drive */
if (
test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
pr_debug("Read_old block "
@@ -2614,6 +2632,7 @@ static void handle_stripe_dirtying5(raid5_conf_t *conf,
}
}
}
+ }
/* now if nothing is locked, and if we have enough data,
* we can start a write request
*/
@@ -2630,54 +2649,7 @@ static void handle_stripe_dirtying5(raid5_conf_t *conf,
schedule_reconstruction(sh, s, rcw == 0, 0);
}
-static void handle_stripe_dirtying6(raid5_conf_t *conf,
- struct stripe_head *sh, struct stripe_head_state *s,
- struct r6_state *r6s, int disks)
-{
- int rcw = 0, pd_idx = sh->pd_idx, i;
- int qd_idx = sh->qd_idx;
-
- set_bit(STRIPE_HANDLE, &sh->state);
- for (i = disks; i--; ) {
- struct r5dev *dev = &sh->dev[i];
- /* check if we haven't enough data */
- if (!test_bit(R5_OVERWRITE, &dev->flags) &&
- i != pd_idx && i != qd_idx &&
- !test_bit(R5_LOCKED, &dev->flags) &&
- !(test_bit(R5_UPTODATE, &dev->flags) ||
- test_bit(R5_Wantcompute, &dev->flags))) {
- rcw++;
- if (!test_bit(R5_Insync, &dev->flags))
- continue; /* it's a failed drive */
-
- if (
- test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
- pr_debug("Read_old stripe %llu "
- "block %d for Reconstruct\n",
- (unsigned long long)sh->sector, i);
- set_bit(R5_LOCKED, &dev->flags);
- set_bit(R5_Wantread, &dev->flags);
- s->locked++;
- } else {
- pr_debug("Request delayed stripe %llu "
- "block %d for Reconstruct\n",
- (unsigned long long)sh->sector, i);
- set_bit(STRIPE_DELAYED, &sh->state);
- set_bit(STRIPE_HANDLE, &sh->state);
- }
- }
- }
- /* now if nothing is locked, and if we have enough data, we can start a
- * write request
- */
- if ((s->req_compute || !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) &&
- s->locked == 0 && rcw == 0 &&
- !test_bit(STRIPE_BIT_DELAY, &sh->state)) {
- schedule_reconstruction(sh, s, 1, 0);
- }
-}
-
-static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh,
+static void handle_parity_checks5(struct r5conf *conf, struct stripe_head *sh,
struct stripe_head_state *s, int disks)
{
struct r5dev *dev = NULL;
@@ -2695,7 +2667,7 @@ static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh,
s->uptodate--;
break;
}
- dev = &sh->dev[s->failed_num];
+ dev = &sh->dev[s->failed_num[0]];
/* fall through */
case check_state_compute_result:
sh->check_state = check_state_idle;
@@ -2765,9 +2737,9 @@ static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh,
}
-static void handle_parity_checks6(raid5_conf_t *conf, struct stripe_head *sh,
+static void handle_parity_checks6(struct r5conf *conf, struct stripe_head *sh,
struct stripe_head_state *s,
- struct r6_state *r6s, int disks)
+ int disks)
{
int pd_idx = sh->pd_idx;
int qd_idx = sh->qd_idx;
@@ -2786,14 +2758,14 @@ static void handle_parity_checks6(raid5_conf_t *conf, struct stripe_head *sh,
switch (sh->check_state) {
case check_state_idle:
/* start a new check operation if there are < 2 failures */
- if (s->failed == r6s->q_failed) {
+ if (s->failed == s->q_failed) {
/* The only possible failed device holds Q, so it
* makes sense to check P (If anything else were failed,
* we would have used P to recreate it).
*/
sh->check_state = check_state_run;
}
- if (!r6s->q_failed && s->failed < 2) {
+ if (!s->q_failed && s->failed < 2) {
/* Q is not failed, and we didn't use it to generate
* anything, so it makes sense to check it
*/
@@ -2835,13 +2807,13 @@ static void handle_parity_checks6(raid5_conf_t *conf, struct stripe_head *sh,
*/
BUG_ON(s->uptodate < disks - 1); /* We don't need Q to recover */
if (s->failed == 2) {
- dev = &sh->dev[r6s->failed_num[1]];
+ dev = &sh->dev[s->failed_num[1]];
s->locked++;
set_bit(R5_LOCKED, &dev->flags);
set_bit(R5_Wantwrite, &dev->flags);
}
if (s->failed >= 1) {
- dev = &sh->dev[r6s->failed_num[0]];
+ dev = &sh->dev[s->failed_num[0]];
s->locked++;
set_bit(R5_LOCKED, &dev->flags);
set_bit(R5_Wantwrite, &dev->flags);
@@ -2928,8 +2900,7 @@ static void handle_parity_checks6(raid5_conf_t *conf, struct stripe_head *sh,
}
}
-static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh,
- struct r6_state *r6s)
+static void handle_stripe_expansion(struct r5conf *conf, struct stripe_head *sh)
{
int i;
@@ -2971,7 +2942,7 @@ static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh,
set_bit(R5_UPTODATE, &sh2->dev[dd_idx].flags);
for (j = 0; j < conf->raid_disks; j++)
if (j != sh2->pd_idx &&
- (!r6s || j != sh2->qd_idx) &&
+ j != sh2->qd_idx &&
!test_bit(R5_Expanded, &sh2->dev[j].flags))
break;
if (j == conf->raid_disks) {
@@ -3006,43 +2977,35 @@ static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh,
*
*/
-static void handle_stripe5(struct stripe_head *sh)
+static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
{
- raid5_conf_t *conf = sh->raid_conf;
- int disks = sh->disks, i;
- struct bio *return_bi = NULL;
- struct stripe_head_state s;
+ struct r5conf *conf = sh->raid_conf;
+ int disks = sh->disks;
struct r5dev *dev;
- mdk_rdev_t *blocked_rdev = NULL;
- int prexor;
- int dec_preread_active = 0;
-
- memset(&s, 0, sizeof(s));
- pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d check:%d "
- "reconstruct:%d\n", (unsigned long long)sh->sector, sh->state,
- atomic_read(&sh->count), sh->pd_idx, sh->check_state,
- sh->reconstruct_state);
+ int i;
- spin_lock(&sh->lock);
- clear_bit(STRIPE_HANDLE, &sh->state);
- clear_bit(STRIPE_DELAYED, &sh->state);
+ memset(s, 0, sizeof(*s));
- s.syncing = test_bit(STRIPE_SYNCING, &sh->state);
- s.expanding = test_bit(STRIPE_EXPAND_SOURCE, &sh->state);
- s.expanded = test_bit(STRIPE_EXPAND_READY, &sh->state);
+ s->syncing = test_bit(STRIPE_SYNCING, &sh->state);
+ s->expanding = test_bit(STRIPE_EXPAND_SOURCE, &sh->state);
+ s->expanded = test_bit(STRIPE_EXPAND_READY, &sh->state);
+ s->failed_num[0] = -1;
+ s->failed_num[1] = -1;
/* Now to look around and see what can be done */
rcu_read_lock();
+ spin_lock_irq(&conf->device_lock);
for (i=disks; i--; ) {
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
+ sector_t first_bad;
+ int bad_sectors;
+ int is_bad = 0;
dev = &sh->dev[i];
- pr_debug("check %d: state 0x%lx toread %p read %p write %p "
- "written %p\n", i, dev->flags, dev->toread, dev->read,
- dev->towrite, dev->written);
-
- /* maybe we can request a biofill operation
+ pr_debug("check %d: state 0x%lx read %p write %p written %p\n",
+ i, dev->flags, dev->toread, dev->towrite, dev->written);
+ /* maybe we can reply to a read
*
* new wantfill requests are only permitted while
* ops_complete_biofill is guaranteed to be inactive
@@ -3052,37 +3015,74 @@ static void handle_stripe5(struct stripe_head *sh)
set_bit(R5_Wantfill, &dev->flags);
/* now count some things */
- if (test_bit(R5_LOCKED, &dev->flags)) s.locked++;
- if (test_bit(R5_UPTODATE, &dev->flags)) s.uptodate++;
- if (test_bit(R5_Wantcompute, &dev->flags)) s.compute++;
+ if (test_bit(R5_LOCKED, &dev->flags))
+ s->locked++;
+ if (test_bit(R5_UPTODATE, &dev->flags))
+ s->uptodate++;
+ if (test_bit(R5_Wantcompute, &dev->flags)) {
+ s->compute++;
+ BUG_ON(s->compute > 2);
+ }
if (test_bit(R5_Wantfill, &dev->flags))
- s.to_fill++;
+ s->to_fill++;
else if (dev->toread)
- s.to_read++;
+ s->to_read++;
if (dev->towrite) {
- s.to_write++;
+ s->to_write++;
if (!test_bit(R5_OVERWRITE, &dev->flags))
- s.non_overwrite++;
+ s->non_overwrite++;
}
if (dev->written)
- s.written++;
+ s->written++;
rdev = rcu_dereference(conf->disks[i].rdev);
- if (blocked_rdev == NULL &&
- rdev && unlikely(test_bit(Blocked, &rdev->flags))) {
- blocked_rdev = rdev;
- atomic_inc(&rdev->nr_pending);
+ if (rdev) {
+ is_bad = is_badblock(rdev, sh->sector, STRIPE_SECTORS,
+ &first_bad, &bad_sectors);
+ if (s->blocked_rdev == NULL
+ && (test_bit(Blocked, &rdev->flags)
+ || is_bad < 0)) {
+ if (is_bad < 0)
+ set_bit(BlockedBadBlocks,
+ &rdev->flags);
+ s->blocked_rdev = rdev;
+ atomic_inc(&rdev->nr_pending);
+ }
}
clear_bit(R5_Insync, &dev->flags);
if (!rdev)
/* Not in-sync */;
- else if (test_bit(In_sync, &rdev->flags))
+ else if (is_bad) {
+ /* also not in-sync */
+ if (!test_bit(WriteErrorSeen, &rdev->flags)) {
+ /* treat as in-sync, but with a read error
+ * which we can now try to correct
+ */
+ set_bit(R5_Insync, &dev->flags);
+ set_bit(R5_ReadError, &dev->flags);
+ }
+ } else if (test_bit(In_sync, &rdev->flags))
set_bit(R5_Insync, &dev->flags);
- else {
- /* could be in-sync depending on recovery/reshape status */
+ else if (!test_bit(Faulty, &rdev->flags)) {
+ /* in sync if before recovery_offset */
if (sh->sector + STRIPE_SECTORS <= rdev->recovery_offset)
set_bit(R5_Insync, &dev->flags);
}
+ if (test_bit(R5_WriteError, &dev->flags)) {
+ clear_bit(R5_Insync, &dev->flags);
+ if (!test_bit(Faulty, &rdev->flags)) {
+ s->handle_bad_blocks = 1;
+ atomic_inc(&rdev->nr_pending);
+ } else
+ clear_bit(R5_WriteError, &dev->flags);
+ }
+ if (test_bit(R5_MadeGood, &dev->flags)) {
+ if (!test_bit(Faulty, &rdev->flags)) {
+ s->handle_bad_blocks = 1;
+ atomic_inc(&rdev->nr_pending);
+ } else
+ clear_bit(R5_MadeGood, &dev->flags);
+ }
if (!test_bit(R5_Insync, &dev->flags)) {
/* The ReadError flag will just be confusing now */
clear_bit(R5_ReadError, &dev->flags);
@@ -3091,313 +3091,60 @@ static void handle_stripe5(struct stripe_head *sh)
if (test_bit(R5_ReadError, &dev->flags))
clear_bit(R5_Insync, &dev->flags);
if (!test_bit(R5_Insync, &dev->flags)) {
- s.failed++;
- s.failed_num = i;
+ if (s->failed < 2)
+ s->failed_num[s->failed] = i;
+ s->failed++;
}
}
+ spin_unlock_irq(&conf->device_lock);
rcu_read_unlock();
+}
- if (unlikely(blocked_rdev)) {
- if (s.syncing || s.expanding || s.expanded ||
- s.to_write || s.written) {
- set_bit(STRIPE_HANDLE, &sh->state);
- goto unlock;
- }
- /* There is nothing for the blocked_rdev to block */
- rdev_dec_pending(blocked_rdev, conf->mddev);
- blocked_rdev = NULL;
- }
-
- if (s.to_fill && !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) {
- set_bit(STRIPE_OP_BIOFILL, &s.ops_request);
- set_bit(STRIPE_BIOFILL_RUN, &sh->state);
- }
-
- pr_debug("locked=%d uptodate=%d to_read=%d"
- " to_write=%d failed=%d failed_num=%d\n",
- s.locked, s.uptodate, s.to_read, s.to_write,
- s.failed, s.failed_num);
- /* check if the array has lost two devices and, if so, some requests might
- * need to be failed
- */
- if (s.failed > 1 && s.to_read+s.to_write+s.written)
- handle_failed_stripe(conf, sh, &s, disks, &return_bi);
- if (s.failed > 1 && s.syncing) {
- md_done_sync(conf->mddev, STRIPE_SECTORS,0);
- clear_bit(STRIPE_SYNCING, &sh->state);
- s.syncing = 0;
- }
-
- /* might be able to return some write requests if the parity block
- * is safe, or on a failed drive
- */
- dev = &sh->dev[sh->pd_idx];
- if ( s.written &&
- ((test_bit(R5_Insync, &dev->flags) &&
- !test_bit(R5_LOCKED, &dev->flags) &&
- test_bit(R5_UPTODATE, &dev->flags)) ||
- (s.failed == 1 && s.failed_num == sh->pd_idx)))
- handle_stripe_clean_event(conf, sh, disks, &return_bi);
-
- /* Now we might consider reading some blocks, either to check/generate
- * parity, or to satisfy requests
- * or to load a block that is being partially written.
- */
- if (s.to_read || s.non_overwrite ||
- (s.syncing && (s.uptodate + s.compute < disks)) || s.expanding)
- handle_stripe_fill5(sh, &s, disks);
-
- /* Now we check to see if any write operations have recently
- * completed
- */
- prexor = 0;
- if (sh->reconstruct_state == reconstruct_state_prexor_drain_result)
- prexor = 1;
- if (sh->reconstruct_state == reconstruct_state_drain_result ||
- sh->reconstruct_state == reconstruct_state_prexor_drain_result) {
- sh->reconstruct_state = reconstruct_state_idle;
-
- /* All the 'written' buffers and the parity block are ready to
- * be written back to disk
- */
- BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags));
- for (i = disks; i--; ) {
- dev = &sh->dev[i];
- if (test_bit(R5_LOCKED, &dev->flags) &&
- (i == sh->pd_idx || dev->written)) {
- pr_debug("Writing block %d\n", i);
- set_bit(R5_Wantwrite, &dev->flags);
- if (prexor)
- continue;
- if (!test_bit(R5_Insync, &dev->flags) ||
- (i == sh->pd_idx && s.failed == 0))
- set_bit(STRIPE_INSYNC, &sh->state);
- }
- }
- if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
- dec_preread_active = 1;
- }
-
- /* Now to consider new write requests and what else, if anything
- * should be read. We do not handle new writes when:
- * 1/ A 'write' operation (copy+xor) is already in flight.
- * 2/ A 'check' operation is in flight, as it may clobber the parity
- * block.
- */
- if (s.to_write && !sh->reconstruct_state && !sh->check_state)
- handle_stripe_dirtying5(conf, sh, &s, disks);
-
- /* maybe we need to check and possibly fix the parity for this stripe
- * Any reads will already have been scheduled, so we just see if enough
- * data is available. The parity check is held off while parity
- * dependent operations are in flight.
- */
- if (sh->check_state ||
- (s.syncing && s.locked == 0 &&
- !test_bit(STRIPE_COMPUTE_RUN, &sh->state) &&
- !test_bit(STRIPE_INSYNC, &sh->state)))
- handle_parity_checks5(conf, sh, &s, disks);
-
- if (s.syncing && s.locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) {
- md_done_sync(conf->mddev, STRIPE_SECTORS,1);
- clear_bit(STRIPE_SYNCING, &sh->state);
- }
-
- /* If the failed drive is just a ReadError, then we might need to progress
- * the repair/check process
- */
- if (s.failed == 1 && !conf->mddev->ro &&
- test_bit(R5_ReadError, &sh->dev[s.failed_num].flags)
- && !test_bit(R5_LOCKED, &sh->dev[s.failed_num].flags)
- && test_bit(R5_UPTODATE, &sh->dev[s.failed_num].flags)
- ) {
- dev = &sh->dev[s.failed_num];
- if (!test_bit(R5_ReWrite, &dev->flags)) {
- set_bit(R5_Wantwrite, &dev->flags);
- set_bit(R5_ReWrite, &dev->flags);
- set_bit(R5_LOCKED, &dev->flags);
- s.locked++;
- } else {
- /* let's read it back */
- set_bit(R5_Wantread, &dev->flags);
- set_bit(R5_LOCKED, &dev->flags);
- s.locked++;
- }
- }
-
- /* Finish reconstruct operations initiated by the expansion process */
- if (sh->reconstruct_state == reconstruct_state_result) {
- struct stripe_head *sh2
- = get_active_stripe(conf, sh->sector, 1, 1, 1);
- if (sh2 && test_bit(STRIPE_EXPAND_SOURCE, &sh2->state)) {
- /* sh cannot be written until sh2 has been read.
- * so arrange for sh to be delayed a little
- */
- set_bit(STRIPE_DELAYED, &sh->state);
- set_bit(STRIPE_HANDLE, &sh->state);
- if (!test_and_set_bit(STRIPE_PREREAD_ACTIVE,
- &sh2->state))
- atomic_inc(&conf->preread_active_stripes);
- release_stripe(sh2);
- goto unlock;
- }
- if (sh2)
- release_stripe(sh2);
-
- sh->reconstruct_state = reconstruct_state_idle;
- clear_bit(STRIPE_EXPANDING, &sh->state);
- for (i = conf->raid_disks; i--; ) {
- set_bit(R5_Wantwrite, &sh->dev[i].flags);
- set_bit(R5_LOCKED, &sh->dev[i].flags);
- s.locked++;
- }
- }
+static void handle_stripe(struct stripe_head *sh)
+{
+ struct stripe_head_state s;
+ struct r5conf *conf = sh->raid_conf;
+ int i;
+ int prexor;
+ int disks = sh->disks;
+ struct r5dev *pdev, *qdev;
- if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) &&
- !sh->reconstruct_state) {
- /* Need to write out all blocks after computing parity */
- sh->disks = conf->raid_disks;
- stripe_set_idx(sh->sector, conf, 0, sh);
- schedule_reconstruction(sh, &s, 1, 1);
- } else if (s.expanded && !sh->reconstruct_state && s.locked == 0) {
- clear_bit(STRIPE_EXPAND_READY, &sh->state);
- atomic_dec(&conf->reshape_stripes);
- wake_up(&conf->wait_for_overlap);
- md_done_sync(conf->mddev, STRIPE_SECTORS, 1);
+ clear_bit(STRIPE_HANDLE, &sh->state);
+ if (test_and_set_bit_lock(STRIPE_ACTIVE, &sh->state)) {
+ /* already being handled, ensure it gets handled
+ * again when current action finishes */
+ set_bit(STRIPE_HANDLE, &sh->state);
+ return;
}
- if (s.expanding && s.locked == 0 &&
- !test_bit(STRIPE_COMPUTE_RUN, &sh->state))
- handle_stripe_expansion(conf, sh, NULL);
-
- unlock:
- spin_unlock(&sh->lock);
-
- /* wait for this device to become unblocked */
- if (unlikely(blocked_rdev))
- md_wait_for_blocked_rdev(blocked_rdev, conf->mddev);
-
- if (s.ops_request)
- raid_run_ops(sh, s.ops_request);
-
- ops_run_io(sh, &s);
-
- if (dec_preread_active) {
- /* We delay this until after ops_run_io so that if make_request
- * is waiting on a flush, it won't continue until the writes
- * have actually been submitted.
- */
- atomic_dec(&conf->preread_active_stripes);
- if (atomic_read(&conf->preread_active_stripes) <
- IO_THRESHOLD)
- md_wakeup_thread(conf->mddev->thread);
+ if (test_and_clear_bit(STRIPE_SYNC_REQUESTED, &sh->state)) {
+ set_bit(STRIPE_SYNCING, &sh->state);
+ clear_bit(STRIPE_INSYNC, &sh->state);
}
- return_io(return_bi);
-}
-
-static void handle_stripe6(struct stripe_head *sh)
-{
- raid5_conf_t *conf = sh->raid_conf;
- int disks = sh->disks;
- struct bio *return_bi = NULL;
- int i, pd_idx = sh->pd_idx, qd_idx = sh->qd_idx;
- struct stripe_head_state s;
- struct r6_state r6s;
- struct r5dev *dev, *pdev, *qdev;
- mdk_rdev_t *blocked_rdev = NULL;
- int dec_preread_active = 0;
+ clear_bit(STRIPE_DELAYED, &sh->state);
pr_debug("handling stripe %llu, state=%#lx cnt=%d, "
"pd_idx=%d, qd_idx=%d\n, check:%d, reconstruct:%d\n",
(unsigned long long)sh->sector, sh->state,
- atomic_read(&sh->count), pd_idx, qd_idx,
+ atomic_read(&sh->count), sh->pd_idx, sh->qd_idx,
sh->check_state, sh->reconstruct_state);
- memset(&s, 0, sizeof(s));
- spin_lock(&sh->lock);
- clear_bit(STRIPE_HANDLE, &sh->state);
- clear_bit(STRIPE_DELAYED, &sh->state);
-
- s.syncing = test_bit(STRIPE_SYNCING, &sh->state);
- s.expanding = test_bit(STRIPE_EXPAND_SOURCE, &sh->state);
- s.expanded = test_bit(STRIPE_EXPAND_READY, &sh->state);
- /* Now to look around and see what can be done */
+ analyse_stripe(sh, &s);
- rcu_read_lock();
- for (i=disks; i--; ) {
- mdk_rdev_t *rdev;
- dev = &sh->dev[i];
-
- pr_debug("check %d: state 0x%lx read %p write %p written %p\n",
- i, dev->flags, dev->toread, dev->towrite, dev->written);
- /* maybe we can reply to a read
- *
- * new wantfill requests are only permitted while
- * ops_complete_biofill is guaranteed to be inactive
- */
- if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread &&
- !test_bit(STRIPE_BIOFILL_RUN, &sh->state))
- set_bit(R5_Wantfill, &dev->flags);
-
- /* now count some things */
- if (test_bit(R5_LOCKED, &dev->flags)) s.locked++;
- if (test_bit(R5_UPTODATE, &dev->flags)) s.uptodate++;
- if (test_bit(R5_Wantcompute, &dev->flags)) {
- s.compute++;
- BUG_ON(s.compute > 2);
- }
-
- if (test_bit(R5_Wantfill, &dev->flags)) {
- s.to_fill++;
- } else if (dev->toread)
- s.to_read++;
- if (dev->towrite) {
- s.to_write++;
- if (!test_bit(R5_OVERWRITE, &dev->flags))
- s.non_overwrite++;
- }
- if (dev->written)
- s.written++;
- rdev = rcu_dereference(conf->disks[i].rdev);
- if (blocked_rdev == NULL &&
- rdev && unlikely(test_bit(Blocked, &rdev->flags))) {
- blocked_rdev = rdev;
- atomic_inc(&rdev->nr_pending);
- }
- clear_bit(R5_Insync, &dev->flags);
- if (!rdev)
- /* Not in-sync */;
- else if (test_bit(In_sync, &rdev->flags))
- set_bit(R5_Insync, &dev->flags);
- else {
- /* in sync if before recovery_offset */
- if (sh->sector + STRIPE_SECTORS <= rdev->recovery_offset)
- set_bit(R5_Insync, &dev->flags);
- }
- if (!test_bit(R5_Insync, &dev->flags)) {
- /* The ReadError flag will just be confusing now */
- clear_bit(R5_ReadError, &dev->flags);
- clear_bit(R5_ReWrite, &dev->flags);
- }
- if (test_bit(R5_ReadError, &dev->flags))
- clear_bit(R5_Insync, &dev->flags);
- if (!test_bit(R5_Insync, &dev->flags)) {
- if (s.failed < 2)
- r6s.failed_num[s.failed] = i;
- s.failed++;
- }
+ if (s.handle_bad_blocks) {
+ set_bit(STRIPE_HANDLE, &sh->state);
+ goto finish;
}
- rcu_read_unlock();
- if (unlikely(blocked_rdev)) {
+ if (unlikely(s.blocked_rdev)) {
if (s.syncing || s.expanding || s.expanded ||
s.to_write || s.written) {
set_bit(STRIPE_HANDLE, &sh->state);
- goto unlock;
+ goto finish;
}
/* There is nothing for the blocked_rdev to block */
- rdev_dec_pending(blocked_rdev, conf->mddev);
- blocked_rdev = NULL;
+ rdev_dec_pending(s.blocked_rdev, conf->mddev);
+ s.blocked_rdev = NULL;
}
if (s.to_fill && !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) {
@@ -3408,83 +3155,92 @@ static void handle_stripe6(struct stripe_head *sh)
pr_debug("locked=%d uptodate=%d to_read=%d"
" to_write=%d failed=%d failed_num=%d,%d\n",
s.locked, s.uptodate, s.to_read, s.to_write, s.failed,
- r6s.failed_num[0], r6s.failed_num[1]);
- /* check if the array has lost >2 devices and, if so, some requests
- * might need to be failed
+ s.failed_num[0], s.failed_num[1]);
+ /* check if the array has lost more than max_degraded devices and,
+ * if so, some requests might need to be failed.
*/
- if (s.failed > 2 && s.to_read+s.to_write+s.written)
- handle_failed_stripe(conf, sh, &s, disks, &return_bi);
- if (s.failed > 2 && s.syncing) {
- md_done_sync(conf->mddev, STRIPE_SECTORS,0);
- clear_bit(STRIPE_SYNCING, &sh->state);
- s.syncing = 0;
+ if (s.failed > conf->max_degraded) {
+ sh->check_state = 0;
+ sh->reconstruct_state = 0;
+ if (s.to_read+s.to_write+s.written)
+ handle_failed_stripe(conf, sh, &s, disks, &s.return_bi);
+ if (s.syncing)
+ handle_failed_sync(conf, sh, &s);
}
/*
* might be able to return some write requests if the parity blocks
* are safe, or on a failed drive
*/
- pdev = &sh->dev[pd_idx];
- r6s.p_failed = (s.failed >= 1 && r6s.failed_num[0] == pd_idx)
- || (s.failed >= 2 && r6s.failed_num[1] == pd_idx);
- qdev = &sh->dev[qd_idx];
- r6s.q_failed = (s.failed >= 1 && r6s.failed_num[0] == qd_idx)
- || (s.failed >= 2 && r6s.failed_num[1] == qd_idx);
-
- if ( s.written &&
- ( r6s.p_failed || ((test_bit(R5_Insync, &pdev->flags)
+ pdev = &sh->dev[sh->pd_idx];
+ s.p_failed = (s.failed >= 1 && s.failed_num[0] == sh->pd_idx)
+ || (s.failed >= 2 && s.failed_num[1] == sh->pd_idx);
+ qdev = &sh->dev[sh->qd_idx];
+ s.q_failed = (s.failed >= 1 && s.failed_num[0] == sh->qd_idx)
+ || (s.failed >= 2 && s.failed_num[1] == sh->qd_idx)
+ || conf->level < 6;
+
+ if (s.written &&
+ (s.p_failed || ((test_bit(R5_Insync, &pdev->flags)
&& !test_bit(R5_LOCKED, &pdev->flags)
&& test_bit(R5_UPTODATE, &pdev->flags)))) &&
- ( r6s.q_failed || ((test_bit(R5_Insync, &qdev->flags)
+ (s.q_failed || ((test_bit(R5_Insync, &qdev->flags)
&& !test_bit(R5_LOCKED, &qdev->flags)
&& test_bit(R5_UPTODATE, &qdev->flags)))))
- handle_stripe_clean_event(conf, sh, disks, &return_bi);
+ handle_stripe_clean_event(conf, sh, disks, &s.return_bi);
/* Now we might consider reading some blocks, either to check/generate
* parity, or to satisfy requests
* or to load a block that is being partially written.
*/
- if (s.to_read || s.non_overwrite || (s.to_write && s.failed) ||
- (s.syncing && (s.uptodate + s.compute < disks)) || s.expanding)
- handle_stripe_fill6(sh, &s, &r6s, disks);
+ if (s.to_read || s.non_overwrite
+ || (conf->level == 6 && s.to_write && s.failed)
+ || (s.syncing && (s.uptodate + s.compute < disks)) || s.expanding)
+ handle_stripe_fill(sh, &s, disks);
/* Now we check to see if any write operations have recently
* completed
*/
- if (sh->reconstruct_state == reconstruct_state_drain_result) {
-
+ prexor = 0;
+ if (sh->reconstruct_state == reconstruct_state_prexor_drain_result)
+ prexor = 1;
+ if (sh->reconstruct_state == reconstruct_state_drain_result ||
+ sh->reconstruct_state == reconstruct_state_prexor_drain_result) {
sh->reconstruct_state = reconstruct_state_idle;
- /* All the 'written' buffers and the parity blocks are ready to
+
+ /* All the 'written' buffers and the parity block are ready to
* be written back to disk
*/
BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags));
- BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[qd_idx].flags));
+ BUG_ON(sh->qd_idx >= 0 &&
+ !test_bit(R5_UPTODATE, &sh->dev[sh->qd_idx].flags));
for (i = disks; i--; ) {
- dev = &sh->dev[i];
+ struct r5dev *dev = &sh->dev[i];
if (test_bit(R5_LOCKED, &dev->flags) &&
- (i == sh->pd_idx || i == qd_idx ||
- dev->written)) {
+ (i == sh->pd_idx || i == sh->qd_idx ||
+ dev->written)) {
pr_debug("Writing block %d\n", i);
- BUG_ON(!test_bit(R5_UPTODATE, &dev->flags));
set_bit(R5_Wantwrite, &dev->flags);
+ if (prexor)
+ continue;
if (!test_bit(R5_Insync, &dev->flags) ||
- ((i == sh->pd_idx || i == qd_idx) &&
- s.failed == 0))
+ ((i == sh->pd_idx || i == sh->qd_idx) &&
+ s.failed == 0))
set_bit(STRIPE_INSYNC, &sh->state);
}
}
if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
- dec_preread_active = 1;
+ s.dec_preread_active = 1;
}
/* Now to consider new write requests and what else, if anything
* should be read. We do not handle new writes when:
- * 1/ A 'write' operation (copy+gen_syndrome) is already in flight.
+ * 1/ A 'write' operation (copy+xor) is already in flight.
* 2/ A 'check' operation is in flight, as it may clobber the parity
* block.
*/
if (s.to_write && !sh->reconstruct_state && !sh->check_state)
- handle_stripe_dirtying6(conf, sh, &s, &r6s, disks);
+ handle_stripe_dirtying(conf, sh, &s, disks);
/* maybe we need to check and possibly fix the parity for this stripe
* Any reads will already have been scheduled, so we just see if enough
@@ -3494,20 +3250,24 @@ static void handle_stripe6(struct stripe_head *sh)
if (sh->check_state ||
(s.syncing && s.locked == 0 &&
!test_bit(STRIPE_COMPUTE_RUN, &sh->state) &&
- !test_bit(STRIPE_INSYNC, &sh->state)))
- handle_parity_checks6(conf, sh, &s, &r6s, disks);
+ !test_bit(STRIPE_INSYNC, &sh->state))) {
+ if (conf->level == 6)
+ handle_parity_checks6(conf, sh, &s, disks);
+ else
+ handle_parity_checks5(conf, sh, &s, disks);
+ }
if (s.syncing && s.locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) {
- md_done_sync(conf->mddev, STRIPE_SECTORS,1);
+ md_done_sync(conf->mddev, STRIPE_SECTORS, 1);
clear_bit(STRIPE_SYNCING, &sh->state);
}
/* If the failed drives are just a ReadError, then we might need
* to progress the repair/check process
*/
- if (s.failed <= 2 && !conf->mddev->ro)
+ if (s.failed <= conf->max_degraded && !conf->mddev->ro)
for (i = 0; i < s.failed; i++) {
- dev = &sh->dev[r6s.failed_num[i]];
+ struct r5dev *dev = &sh->dev[s.failed_num[i]];
if (test_bit(R5_ReadError, &dev->flags)
&& !test_bit(R5_LOCKED, &dev->flags)
&& test_bit(R5_UPTODATE, &dev->flags)
@@ -3526,8 +3286,26 @@ static void handle_stripe6(struct stripe_head *sh)
}
}
+
/* Finish reconstruct operations initiated by the expansion process */
if (sh->reconstruct_state == reconstruct_state_result) {
+ struct stripe_head *sh_src
+ = get_active_stripe(conf, sh->sector, 1, 1, 1);
+ if (sh_src && test_bit(STRIPE_EXPAND_SOURCE, &sh_src->state)) {
+ /* sh cannot be written until sh_src has been read.
+ * so arrange for sh to be delayed a little
+ */
+ set_bit(STRIPE_DELAYED, &sh->state);
+ set_bit(STRIPE_HANDLE, &sh->state);
+ if (!test_and_set_bit(STRIPE_PREREAD_ACTIVE,
+ &sh_src->state))
+ atomic_inc(&conf->preread_active_stripes);
+ release_stripe(sh_src);
+ goto finish;
+ }
+ if (sh_src)
+ release_stripe(sh_src);
+
sh->reconstruct_state = reconstruct_state_idle;
clear_bit(STRIPE_EXPANDING, &sh->state);
for (i = conf->raid_disks; i--; ) {
@@ -3539,24 +3317,7 @@ static void handle_stripe6(struct stripe_head *sh)
if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) &&
!sh->reconstruct_state) {
- struct stripe_head *sh2
- = get_active_stripe(conf, sh->sector, 1, 1, 1);
- if (sh2 && test_bit(STRIPE_EXPAND_SOURCE, &sh2->state)) {
- /* sh cannot be written until sh2 has been read.
- * so arrange for sh to be delayed a little
- */
- set_bit(STRIPE_DELAYED, &sh->state);
- set_bit(STRIPE_HANDLE, &sh->state);
- if (!test_and_set_bit(STRIPE_PREREAD_ACTIVE,
- &sh2->state))
- atomic_inc(&conf->preread_active_stripes);
- release_stripe(sh2);
- goto unlock;
- }
- if (sh2)
- release_stripe(sh2);
-
- /* Need to write out all blocks after computing P&Q */
+ /* Need to write out all blocks after computing parity */
sh->disks = conf->raid_disks;
stripe_set_idx(sh->sector, conf, 0, sh);
schedule_reconstruction(sh, &s, 1, 1);
@@ -3569,22 +3330,39 @@ static void handle_stripe6(struct stripe_head *sh)
if (s.expanding && s.locked == 0 &&
!test_bit(STRIPE_COMPUTE_RUN, &sh->state))
- handle_stripe_expansion(conf, sh, &r6s);
-
- unlock:
- spin_unlock(&sh->lock);
+ handle_stripe_expansion(conf, sh);
+finish:
/* wait for this device to become unblocked */
- if (unlikely(blocked_rdev))
- md_wait_for_blocked_rdev(blocked_rdev, conf->mddev);
+ if (conf->mddev->external && unlikely(s.blocked_rdev))
+ md_wait_for_blocked_rdev(s.blocked_rdev, conf->mddev);
+
+ if (s.handle_bad_blocks)
+ for (i = disks; i--; ) {
+ struct md_rdev *rdev;
+ struct r5dev *dev = &sh->dev[i];
+ if (test_and_clear_bit(R5_WriteError, &dev->flags)) {
+ /* We own a safe reference to the rdev */
+ rdev = conf->disks[i].rdev;
+ if (!rdev_set_badblocks(rdev, sh->sector,
+ STRIPE_SECTORS, 0))
+ md_error(conf->mddev, rdev);
+ rdev_dec_pending(rdev, conf->mddev);
+ }
+ if (test_and_clear_bit(R5_MadeGood, &dev->flags)) {
+ rdev = conf->disks[i].rdev;
+ rdev_clear_badblocks(rdev, sh->sector,
+ STRIPE_SECTORS);
+ rdev_dec_pending(rdev, conf->mddev);
+ }
+ }
if (s.ops_request)
raid_run_ops(sh, s.ops_request);
ops_run_io(sh, &s);
-
- if (dec_preread_active) {
+ if (s.dec_preread_active) {
/* We delay this until after ops_run_io so that if make_request
* is waiting on a flush, it won't continue until the writes
* have actually been submitted.
@@ -3595,18 +3373,12 @@ static void handle_stripe6(struct stripe_head *sh)
md_wakeup_thread(conf->mddev->thread);
}
- return_io(return_bi);
-}
+ return_io(s.return_bi);
-static void handle_stripe(struct stripe_head *sh)
-{
- if (sh->raid_conf->level == 6)
- handle_stripe6(sh);
- else
- handle_stripe5(sh);
+ clear_bit_unlock(STRIPE_ACTIVE, &sh->state);
}
-static void raid5_activate_delayed(raid5_conf_t *conf)
+static void raid5_activate_delayed(struct r5conf *conf)
{
if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) {
while (!list_empty(&conf->delayed_list)) {
@@ -3622,7 +3394,7 @@ static void raid5_activate_delayed(raid5_conf_t *conf)
}
}
-static void activate_bit_delay(raid5_conf_t *conf)
+static void activate_bit_delay(struct r5conf *conf)
{
/* device_lock is held */
struct list_head head;
@@ -3636,9 +3408,9 @@ static void activate_bit_delay(raid5_conf_t *conf)
}
}
-int md_raid5_congested(mddev_t *mddev, int bits)
+int md_raid5_congested(struct mddev *mddev, int bits)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
/* No difference between reads and writes. Just check
* how busy the stripe_cache is
@@ -3657,7 +3429,7 @@ EXPORT_SYMBOL_GPL(md_raid5_congested);
static int raid5_congested(void *data, int bits)
{
- mddev_t *mddev = data;
+ struct mddev *mddev = data;
return mddev_congested(mddev, bits) ||
md_raid5_congested(mddev, bits);
@@ -3670,7 +3442,7 @@ static int raid5_mergeable_bvec(struct request_queue *q,
struct bvec_merge_data *bvm,
struct bio_vec *biovec)
{
- mddev_t *mddev = q->queuedata;
+ struct mddev *mddev = q->queuedata;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
int max;
unsigned int chunk_sectors = mddev->chunk_sectors;
@@ -3690,7 +3462,7 @@ static int raid5_mergeable_bvec(struct request_queue *q,
}
-static int in_chunk_boundary(mddev_t *mddev, struct bio *bio)
+static int in_chunk_boundary(struct mddev *mddev, struct bio *bio)
{
sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev);
unsigned int chunk_sectors = mddev->chunk_sectors;
@@ -3706,7 +3478,7 @@ static int in_chunk_boundary(mddev_t *mddev, struct bio *bio)
* add bio to the retry LIFO ( in O(1) ... we are in interrupt )
* later sampled by raid5d.
*/
-static void add_bio_to_retry(struct bio *bi,raid5_conf_t *conf)
+static void add_bio_to_retry(struct bio *bi,struct r5conf *conf)
{
unsigned long flags;
@@ -3720,7 +3492,7 @@ static void add_bio_to_retry(struct bio *bi,raid5_conf_t *conf)
}
-static struct bio *remove_bio_from_retry(raid5_conf_t *conf)
+static struct bio *remove_bio_from_retry(struct r5conf *conf)
{
struct bio *bi;
@@ -3753,10 +3525,10 @@ static struct bio *remove_bio_from_retry(raid5_conf_t *conf)
static void raid5_align_endio(struct bio *bi, int error)
{
struct bio* raid_bi = bi->bi_private;
- mddev_t *mddev;
- raid5_conf_t *conf;
+ struct mddev *mddev;
+ struct r5conf *conf;
int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
bio_put(bi);
@@ -3800,12 +3572,12 @@ static int bio_fits_rdev(struct bio *bi)
}
-static int chunk_aligned_read(mddev_t *mddev, struct bio * raid_bio)
+static int chunk_aligned_read(struct mddev *mddev, struct bio * raid_bio)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
int dd_idx;
struct bio* align_bi;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
if (!in_chunk_boundary(mddev, raid_bio)) {
pr_debug("chunk_aligned_read : non aligned\n");
@@ -3833,6 +3605,9 @@ static int chunk_aligned_read(mddev_t *mddev, struct bio * raid_bio)
rcu_read_lock();
rdev = rcu_dereference(conf->disks[dd_idx].rdev);
if (rdev && test_bit(In_sync, &rdev->flags)) {
+ sector_t first_bad;
+ int bad_sectors;
+
atomic_inc(&rdev->nr_pending);
rcu_read_unlock();
raid_bio->bi_next = (void*)rdev;
@@ -3840,8 +3615,10 @@ static int chunk_aligned_read(mddev_t *mddev, struct bio * raid_bio)
align_bi->bi_flags &= ~(1 << BIO_SEG_VALID);
align_bi->bi_sector += rdev->data_offset;
- if (!bio_fits_rdev(align_bi)) {
- /* too big in some way */
+ if (!bio_fits_rdev(align_bi) ||
+ is_badblock(rdev, align_bi->bi_sector, align_bi->bi_size>>9,
+ &first_bad, &bad_sectors)) {
+ /* too big in some way, or has a known bad block */
bio_put(align_bi);
rdev_dec_pending(rdev, mddev);
return 0;
@@ -3873,7 +3650,7 @@ static int chunk_aligned_read(mddev_t *mddev, struct bio * raid_bio)
* head of the hold_list has changed, i.e. the head was promoted to the
* handle_list.
*/
-static struct stripe_head *__get_priority_stripe(raid5_conf_t *conf)
+static struct stripe_head *__get_priority_stripe(struct r5conf *conf)
{
struct stripe_head *sh;
@@ -3916,9 +3693,9 @@ static struct stripe_head *__get_priority_stripe(raid5_conf_t *conf)
return sh;
}
-static int make_request(mddev_t *mddev, struct bio * bi)
+static void make_request(struct mddev *mddev, struct bio * bi)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
int dd_idx;
sector_t new_sector;
sector_t logical_sector, last_sector;
@@ -3929,7 +3706,7 @@ static int make_request(mddev_t *mddev, struct bio * bi)
if (unlikely(bi->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bi);
- return 0;
+ return;
}
md_write_start(mddev, bi);
@@ -3937,7 +3714,7 @@ static int make_request(mddev_t *mddev, struct bio * bi)
if (rw == READ &&
mddev->reshape_position == MaxSector &&
chunk_aligned_read(mddev,bi))
- return 0;
+ return;
logical_sector = bi->bi_sector & ~((sector_t)STRIPE_SECTORS-1);
last_sector = bi->bi_sector + (bi->bi_size>>9);
@@ -4016,7 +3793,7 @@ static int make_request(mddev_t *mddev, struct bio * bi)
}
}
- if (bio_data_dir(bi) == WRITE &&
+ if (rw == WRITE &&
logical_sector >= mddev->suspend_lo &&
logical_sector < mddev->suspend_hi) {
release_stripe(sh);
@@ -4034,7 +3811,7 @@ static int make_request(mddev_t *mddev, struct bio * bi)
}
if (test_bit(STRIPE_EXPANDING, &sh->state) ||
- !add_stripe_bio(sh, bi, dd_idx, (bi->bi_rw&RW_MASK))) {
+ !add_stripe_bio(sh, bi, dd_idx, rw)) {
/* Stripe is busy expanding or
* add failed due to overlap. Flush everything
* and wait a while
@@ -4072,13 +3849,11 @@ static int make_request(mddev_t *mddev, struct bio * bi)
bio_endio(bi, 0);
}
-
- return 0;
}
-static sector_t raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks);
+static sector_t raid5_size(struct mddev *mddev, sector_t sectors, int raid_disks);
-static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped)
+static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr, int *skipped)
{
/* reshaping is quite different to recovery/resync so it is
* handled quite separately ... here.
@@ -4089,7 +3864,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped
* As the reads complete, handle_stripe will copy the data
* into the destination stripe and release that stripe.
*/
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
struct stripe_head *sh;
sector_t first_sector, last_sector;
int raid_disks = conf->previous_raid_disks;
@@ -4296,9 +4071,9 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped
}
/* FIXME go_faster isn't used */
-static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, int go_faster)
+static inline sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipped, int go_faster)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
struct stripe_head *sh;
sector_t max_sector = mddev->dev_sectors;
sector_t sync_blocks;
@@ -4375,10 +4150,7 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski
bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, still_degraded);
- spin_lock(&sh->lock);
- set_bit(STRIPE_SYNCING, &sh->state);
- clear_bit(STRIPE_INSYNC, &sh->state);
- spin_unlock(&sh->lock);
+ set_bit(STRIPE_SYNC_REQUESTED, &sh->state);
handle_stripe(sh);
release_stripe(sh);
@@ -4386,7 +4158,7 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski
return STRIPE_SECTORS;
}
-static int retry_aligned_read(raid5_conf_t *conf, struct bio *raid_bio)
+static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
{
/* We may not be able to submit a whole bio at once as there
* may not be enough stripe_heads available.
@@ -4458,10 +4230,10 @@ static int retry_aligned_read(raid5_conf_t *conf, struct bio *raid_bio)
* During the scan, completed stripes are saved for us by the interrupt
* handler, so that they will not have to wait for our next wakeup.
*/
-static void raid5d(mddev_t *mddev)
+static void raid5d(struct mddev *mddev)
{
struct stripe_head *sh;
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
int handled;
struct blk_plug plug;
@@ -4509,6 +4281,9 @@ static void raid5d(mddev_t *mddev)
release_stripe(sh);
cond_resched();
+ if (mddev->flags & ~(1<<MD_CHANGE_PENDING))
+ md_check_recovery(mddev);
+
spin_lock_irq(&conf->device_lock);
}
pr_debug("%d stripes handled\n", handled);
@@ -4522,9 +4297,9 @@ static void raid5d(mddev_t *mddev)
}
static ssize_t
-raid5_show_stripe_cache_size(mddev_t *mddev, char *page)
+raid5_show_stripe_cache_size(struct mddev *mddev, char *page)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
if (conf)
return sprintf(page, "%d\n", conf->max_nr_stripes);
else
@@ -4532,9 +4307,9 @@ raid5_show_stripe_cache_size(mddev_t *mddev, char *page)
}
int
-raid5_set_cache_size(mddev_t *mddev, int size)
+raid5_set_cache_size(struct mddev *mddev, int size)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
int err;
if (size <= 16 || size > 32768)
@@ -4558,9 +4333,9 @@ raid5_set_cache_size(mddev_t *mddev, int size)
EXPORT_SYMBOL(raid5_set_cache_size);
static ssize_t
-raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len)
+raid5_store_stripe_cache_size(struct mddev *mddev, const char *page, size_t len)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
unsigned long new;
int err;
@@ -4583,9 +4358,9 @@ raid5_stripecache_size = __ATTR(stripe_cache_size, S_IRUGO | S_IWUSR,
raid5_store_stripe_cache_size);
static ssize_t
-raid5_show_preread_threshold(mddev_t *mddev, char *page)
+raid5_show_preread_threshold(struct mddev *mddev, char *page)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
if (conf)
return sprintf(page, "%d\n", conf->bypass_threshold);
else
@@ -4593,9 +4368,9 @@ raid5_show_preread_threshold(mddev_t *mddev, char *page)
}
static ssize_t
-raid5_store_preread_threshold(mddev_t *mddev, const char *page, size_t len)
+raid5_store_preread_threshold(struct mddev *mddev, const char *page, size_t len)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
unsigned long new;
if (len >= PAGE_SIZE)
return -EINVAL;
@@ -4617,9 +4392,9 @@ raid5_preread_bypass_threshold = __ATTR(preread_bypass_threshold,
raid5_store_preread_threshold);
static ssize_t
-stripe_cache_active_show(mddev_t *mddev, char *page)
+stripe_cache_active_show(struct mddev *mddev, char *page)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
if (conf)
return sprintf(page, "%d\n", atomic_read(&conf->active_stripes));
else
@@ -4641,9 +4416,9 @@ static struct attribute_group raid5_attrs_group = {
};
static sector_t
-raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks)
+raid5_size(struct mddev *mddev, sector_t sectors, int raid_disks)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
if (!sectors)
sectors = mddev->dev_sectors;
@@ -4656,7 +4431,7 @@ raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks)
return sectors * (raid_disks - conf->max_degraded);
}
-static void raid5_free_percpu(raid5_conf_t *conf)
+static void raid5_free_percpu(struct r5conf *conf)
{
struct raid5_percpu *percpu;
unsigned long cpu;
@@ -4678,7 +4453,7 @@ static void raid5_free_percpu(raid5_conf_t *conf)
free_percpu(conf->percpu);
}
-static void free_conf(raid5_conf_t *conf)
+static void free_conf(struct r5conf *conf)
{
shrink_stripes(conf);
raid5_free_percpu(conf);
@@ -4691,7 +4466,7 @@ static void free_conf(raid5_conf_t *conf)
static int raid456_cpu_notify(struct notifier_block *nfb, unsigned long action,
void *hcpu)
{
- raid5_conf_t *conf = container_of(nfb, raid5_conf_t, cpu_notify);
+ struct r5conf *conf = container_of(nfb, struct r5conf, cpu_notify);
long cpu = (long)hcpu;
struct raid5_percpu *percpu = per_cpu_ptr(conf->percpu, cpu);
@@ -4726,7 +4501,7 @@ static int raid456_cpu_notify(struct notifier_block *nfb, unsigned long action,
}
#endif
-static int raid5_alloc_percpu(raid5_conf_t *conf)
+static int raid5_alloc_percpu(struct r5conf *conf)
{
unsigned long cpu;
struct page *spare_page;
@@ -4768,11 +4543,11 @@ static int raid5_alloc_percpu(raid5_conf_t *conf)
return err;
}
-static raid5_conf_t *setup_conf(mddev_t *mddev)
+static struct r5conf *setup_conf(struct mddev *mddev)
{
- raid5_conf_t *conf;
+ struct r5conf *conf;
int raid_disk, memory, max_disks;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
struct disk_info *disk;
if (mddev->new_level != 5
@@ -4804,7 +4579,7 @@ static raid5_conf_t *setup_conf(mddev_t *mddev)
return ERR_PTR(-EINVAL);
}
- conf = kzalloc(sizeof(raid5_conf_t), GFP_KERNEL);
+ conf = kzalloc(sizeof(struct r5conf), GFP_KERNEL);
if (conf == NULL)
goto abort;
spin_lock_init(&conf->device_lock);
@@ -4819,6 +4594,7 @@ static raid5_conf_t *setup_conf(mddev_t *mddev)
atomic_set(&conf->preread_active_stripes, 0);
atomic_set(&conf->active_aligned_reads, 0);
conf->bypass_threshold = BYPASS_THRESHOLD;
+ conf->recovery_disabled = mddev->recovery_disabled - 1;
conf->raid_disks = mddev->raid_disks;
if (mddev->reshape_position == MaxSector)
@@ -4933,12 +4709,12 @@ static int only_parity(int raid_disk, int algo, int raid_disks, int max_degraded
return 0;
}
-static int run(mddev_t *mddev)
+static int run(struct mddev *mddev)
{
- raid5_conf_t *conf;
+ struct r5conf *conf;
int working_disks = 0;
int dirty_parity_disks = 0;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
sector_t reshape_offset = 0;
if (mddev->recovery_cp != MaxSector)
@@ -5162,23 +4938,19 @@ static int run(mddev_t *mddev)
return 0;
abort:
- md_unregister_thread(mddev->thread);
- mddev->thread = NULL;
- if (conf) {
- print_raid5_conf(conf);
- free_conf(conf);
- }
+ md_unregister_thread(&mddev->thread);
+ print_raid5_conf(conf);
+ free_conf(conf);
mddev->private = NULL;
printk(KERN_ALERT "md/raid:%s: failed to run raid set.\n", mdname(mddev));
return -EIO;
}
-static int stop(mddev_t *mddev)
+static int stop(struct mddev *mddev)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
- md_unregister_thread(mddev->thread);
- mddev->thread = NULL;
+ md_unregister_thread(&mddev->thread);
if (mddev->queue)
mddev->queue->backing_dev_info.congested_fn = NULL;
free_conf(conf);
@@ -5187,44 +4959,9 @@ static int stop(mddev_t *mddev)
return 0;
}
-#ifdef DEBUG
-static void print_sh(struct seq_file *seq, struct stripe_head *sh)
-{
- int i;
-
- seq_printf(seq, "sh %llu, pd_idx %d, state %ld.\n",
- (unsigned long long)sh->sector, sh->pd_idx, sh->state);
- seq_printf(seq, "sh %llu, count %d.\n",
- (unsigned long long)sh->sector, atomic_read(&sh->count));
- seq_printf(seq, "sh %llu, ", (unsigned long long)sh->sector);
- for (i = 0; i < sh->disks; i++) {
- seq_printf(seq, "(cache%d: %p %ld) ",
- i, sh->dev[i].page, sh->dev[i].flags);
- }
- seq_printf(seq, "\n");
-}
-
-static void printall(struct seq_file *seq, raid5_conf_t *conf)
-{
- struct stripe_head *sh;
- struct hlist_node *hn;
- int i;
-
- spin_lock_irq(&conf->device_lock);
- for (i = 0; i < NR_HASH; i++) {
- hlist_for_each_entry(sh, hn, &conf->stripe_hashtbl[i], hash) {
- if (sh->raid_conf != conf)
- continue;
- print_sh(seq, sh);
- }
- }
- spin_unlock_irq(&conf->device_lock);
-}
-#endif
-
-static void status(struct seq_file *seq, mddev_t *mddev)
+static void status(struct seq_file *seq, struct mddev *mddev)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
int i;
seq_printf(seq, " level %d, %dk chunk, algorithm %d", mddev->level,
@@ -5235,13 +4972,9 @@ static void status(struct seq_file *seq, mddev_t *mddev)
conf->disks[i].rdev &&
test_bit(In_sync, &conf->disks[i].rdev->flags) ? "U" : "_");
seq_printf (seq, "]");
-#ifdef DEBUG
- seq_printf (seq, "\n");
- printall(seq, conf);
-#endif
}
-static void print_raid5_conf (raid5_conf_t *conf)
+static void print_raid5_conf (struct r5conf *conf)
{
int i;
struct disk_info *tmp;
@@ -5265,10 +4998,10 @@ static void print_raid5_conf (raid5_conf_t *conf)
}
}
-static int raid5_spare_active(mddev_t *mddev)
+static int raid5_spare_active(struct mddev *mddev)
{
int i;
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
struct disk_info *tmp;
int count = 0;
unsigned long flags;
@@ -5290,11 +5023,11 @@ static int raid5_spare_active(mddev_t *mddev)
return count;
}
-static int raid5_remove_disk(mddev_t *mddev, int number)
+static int raid5_remove_disk(struct mddev *mddev, int number)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
int err = 0;
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
struct disk_info *p = conf->disks + number;
print_raid5_conf(conf);
@@ -5313,6 +5046,7 @@ static int raid5_remove_disk(mddev_t *mddev, int number)
* isn't possible.
*/
if (!test_bit(Faulty, &rdev->flags) &&
+ mddev->recovery_disabled != conf->recovery_disabled &&
!has_failed(conf) &&
number < conf->raid_disks) {
err = -EBUSY;
@@ -5332,15 +5066,18 @@ abort:
return err;
}
-static int raid5_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
+static int raid5_add_disk(struct mddev *mddev, struct md_rdev *rdev)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
int err = -EEXIST;
int disk;
struct disk_info *p;
int first = 0;
int last = conf->raid_disks - 1;
+ if (mddev->recovery_disabled == conf->recovery_disabled)
+ return -EBUSY;
+
if (has_failed(conf))
/* no point adding a device */
return -EINVAL;
@@ -5372,7 +5109,7 @@ static int raid5_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
return err;
}
-static int raid5_resize(mddev_t *mddev, sector_t sectors)
+static int raid5_resize(struct mddev *mddev, sector_t sectors)
{
/* no resync is happening, and there is enough space
* on all devices, so we can resize.
@@ -5399,7 +5136,7 @@ static int raid5_resize(mddev_t *mddev, sector_t sectors)
return 0;
}
-static int check_stripe_cache(mddev_t *mddev)
+static int check_stripe_cache(struct mddev *mddev)
{
/* Can only proceed if there are plenty of stripe_heads.
* We need a minimum of one full stripe,, and for sensible progress
@@ -5409,7 +5146,7 @@ static int check_stripe_cache(mddev_t *mddev)
* If the chunk size is greater, user-space should request more
* stripe_heads first.
*/
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
if (((mddev->chunk_sectors << 9) / STRIPE_SIZE) * 4
> conf->max_nr_stripes ||
((mddev->new_chunk_sectors << 9) / STRIPE_SIZE) * 4
@@ -5423,9 +5160,9 @@ static int check_stripe_cache(mddev_t *mddev)
return 1;
}
-static int check_reshape(mddev_t *mddev)
+static int check_reshape(struct mddev *mddev)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
if (mddev->delta_disks == 0 &&
mddev->new_layout == mddev->layout &&
@@ -5455,10 +5192,10 @@ static int check_reshape(mddev_t *mddev)
return resize_stripes(conf, conf->raid_disks + mddev->delta_disks);
}
-static int raid5_start_reshape(mddev_t *mddev)
+static int raid5_start_reshape(struct mddev *mddev)
{
- raid5_conf_t *conf = mddev->private;
- mdk_rdev_t *rdev;
+ struct r5conf *conf = mddev->private;
+ struct md_rdev *rdev;
int spares = 0;
unsigned long flags;
@@ -5519,16 +5256,14 @@ static int raid5_start_reshape(mddev_t *mddev)
if (rdev->raid_disk < 0 &&
!test_bit(Faulty, &rdev->flags)) {
if (raid5_add_disk(mddev, rdev) == 0) {
- char nm[20];
if (rdev->raid_disk
>= conf->previous_raid_disks) {
set_bit(In_sync, &rdev->flags);
added_devices++;
} else
rdev->recovery_offset = 0;
- sprintf(nm, "rd%d", rdev->raid_disk);
- if (sysfs_create_link(&mddev->kobj,
- &rdev->kobj, nm))
+
+ if (sysfs_link_rdev(mddev, rdev))
/* Failure here is OK */;
}
} else if (rdev->raid_disk >= conf->previous_raid_disks
@@ -5574,7 +5309,7 @@ static int raid5_start_reshape(mddev_t *mddev)
/* This is called from the reshape thread and should make any
* changes needed in 'conf'
*/
-static void end_reshape(raid5_conf_t *conf)
+static void end_reshape(struct r5conf *conf)
{
if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) {
@@ -5601,9 +5336,9 @@ static void end_reshape(raid5_conf_t *conf)
/* This is called from the raid5d thread with mddev_lock held.
* It makes config changes to the device.
*/
-static void raid5_finish_reshape(mddev_t *mddev)
+static void raid5_finish_reshape(struct mddev *mddev)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
@@ -5622,11 +5357,9 @@ static void raid5_finish_reshape(mddev_t *mddev)
for (d = conf->raid_disks ;
d < conf->raid_disks - mddev->delta_disks;
d++) {
- mdk_rdev_t *rdev = conf->disks[d].rdev;
+ struct md_rdev *rdev = conf->disks[d].rdev;
if (rdev && raid5_remove_disk(mddev, d) == 0) {
- char nm[20];
- sprintf(nm, "rd%d", rdev->raid_disk);
- sysfs_remove_link(&mddev->kobj, nm);
+ sysfs_unlink_rdev(mddev, rdev);
rdev->raid_disk = -1;
}
}
@@ -5638,9 +5371,9 @@ static void raid5_finish_reshape(mddev_t *mddev)
}
}
-static void raid5_quiesce(mddev_t *mddev, int state)
+static void raid5_quiesce(struct mddev *mddev, int state)
{
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
switch(state) {
case 2: /* resume for a suspend */
@@ -5674,20 +5407,20 @@ static void raid5_quiesce(mddev_t *mddev, int state)
}
-static void *raid45_takeover_raid0(mddev_t *mddev, int level)
+static void *raid45_takeover_raid0(struct mddev *mddev, int level)
{
- struct raid0_private_data *raid0_priv = mddev->private;
+ struct r0conf *raid0_conf = mddev->private;
sector_t sectors;
/* for raid0 takeover only one zone is supported */
- if (raid0_priv->nr_strip_zones > 1) {
+ if (raid0_conf->nr_strip_zones > 1) {
printk(KERN_ERR "md/raid:%s: cannot takeover raid0 with more than one zone.\n",
mdname(mddev));
return ERR_PTR(-EINVAL);
}
- sectors = raid0_priv->strip_zone[0].zone_end;
- sector_div(sectors, raid0_priv->strip_zone[0].nb_dev);
+ sectors = raid0_conf->strip_zone[0].zone_end;
+ sector_div(sectors, raid0_conf->strip_zone[0].nb_dev);
mddev->dev_sectors = sectors;
mddev->new_level = level;
mddev->new_layout = ALGORITHM_PARITY_N;
@@ -5701,7 +5434,7 @@ static void *raid45_takeover_raid0(mddev_t *mddev, int level)
}
-static void *raid5_takeover_raid1(mddev_t *mddev)
+static void *raid5_takeover_raid1(struct mddev *mddev)
{
int chunksect;
@@ -5728,7 +5461,7 @@ static void *raid5_takeover_raid1(mddev_t *mddev)
return setup_conf(mddev);
}
-static void *raid5_takeover_raid6(mddev_t *mddev)
+static void *raid5_takeover_raid6(struct mddev *mddev)
{
int new_layout;
@@ -5762,14 +5495,14 @@ static void *raid5_takeover_raid6(mddev_t *mddev)
}
-static int raid5_check_reshape(mddev_t *mddev)
+static int raid5_check_reshape(struct mddev *mddev)
{
/* For a 2-drive array, the layout and chunk size can be changed
* immediately as not restriping is needed.
* For larger arrays we record the new value - after validation
* to be used by a reshape pass.
*/
- raid5_conf_t *conf = mddev->private;
+ struct r5conf *conf = mddev->private;
int new_chunk = mddev->new_chunk_sectors;
if (mddev->new_layout >= 0 && !algorithm_valid_raid5(mddev->new_layout))
@@ -5802,7 +5535,7 @@ static int raid5_check_reshape(mddev_t *mddev)
return check_reshape(mddev);
}
-static int raid6_check_reshape(mddev_t *mddev)
+static int raid6_check_reshape(struct mddev *mddev)
{
int new_chunk = mddev->new_chunk_sectors;
@@ -5822,7 +5555,7 @@ static int raid6_check_reshape(mddev_t *mddev)
return check_reshape(mddev);
}
-static void *raid5_takeover(mddev_t *mddev)
+static void *raid5_takeover(struct mddev *mddev)
{
/* raid5 can take over:
* raid0 - if there is only one strip zone - make it a raid4 layout
@@ -5845,7 +5578,7 @@ static void *raid5_takeover(mddev_t *mddev)
return ERR_PTR(-EINVAL);
}
-static void *raid4_takeover(mddev_t *mddev)
+static void *raid4_takeover(struct mddev *mddev)
{
/* raid4 can take over:
* raid0 - if there is only one strip zone
@@ -5862,9 +5595,9 @@ static void *raid4_takeover(mddev_t *mddev)
return ERR_PTR(-EINVAL);
}
-static struct mdk_personality raid5_personality;
+static struct md_personality raid5_personality;
-static void *raid6_takeover(mddev_t *mddev)
+static void *raid6_takeover(struct mddev *mddev)
{
/* Currently can only take over a raid5. We map the
* personality to an equivalent raid6 personality
@@ -5911,7 +5644,7 @@ static void *raid6_takeover(mddev_t *mddev)
}
-static struct mdk_personality raid6_personality =
+static struct md_personality raid6_personality =
{
.name = "raid6",
.level = 6,
@@ -5933,7 +5666,7 @@ static struct mdk_personality raid6_personality =
.quiesce = raid5_quiesce,
.takeover = raid6_takeover,
};
-static struct mdk_personality raid5_personality =
+static struct md_personality raid5_personality =
{
.name = "raid5",
.level = 5,
@@ -5956,7 +5689,7 @@ static struct mdk_personality raid5_personality =
.takeover = raid5_takeover,
};
-static struct mdk_personality raid4_personality =
+static struct md_personality raid4_personality =
{
.name = "raid4",
.level = 4,
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 3ca77a2613ba..e10c5531f9c5 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -6,11 +6,11 @@
/*
*
- * Each stripe contains one buffer per disc. Each buffer can be in
+ * Each stripe contains one buffer per device. Each buffer can be in
* one of a number of states stored in "flags". Changes between
- * these states happen *almost* exclusively under a per-stripe
- * spinlock. Some very specific changes can happen in bi_end_io, and
- * these are not protected by the spin lock.
+ * these states happen *almost* exclusively under the protection of the
+ * STRIPE_ACTIVE flag. Some very specific changes can happen in bi_end_io, and
+ * these are not protected by STRIPE_ACTIVE.
*
* The flag bits that are used to represent these states are:
* R5_UPTODATE and R5_LOCKED
@@ -76,12 +76,10 @@
* block and the cached buffer are successfully written, any buffer on
* a written list can be returned with b_end_io.
*
- * The write list and read list both act as fifos. The read list is
- * protected by the device_lock. The write and written lists are
- * protected by the stripe lock. The device_lock, which can be
- * claimed while the stipe lock is held, is only for list
- * manipulations and will only be held for a very short time. It can
- * be claimed from interrupts.
+ * The write list and read list both act as fifos. The read list,
+ * write list and written list are protected by the device_lock.
+ * The device_lock is only for list manipulations and will only be
+ * held for a very short time. It can be claimed from interrupts.
*
*
* Stripes in the stripe cache can be on one of two lists (or on
@@ -96,7 +94,6 @@
*
* The inactive_list, handle_list and hash bucket lists are all protected by the
* device_lock.
- * - stripes on the inactive_list never have their stripe_lock held.
* - stripes have a reference counter. If count==0, they are on a list.
* - If a stripe might need handling, STRIPE_HANDLE is set.
* - When refcount reaches zero, then if STRIPE_HANDLE it is put on
@@ -116,10 +113,10 @@
* attach a request to an active stripe (add_stripe_bh())
* lockdev attach-buffer unlockdev
* handle a stripe (handle_stripe())
- * lockstripe clrSTRIPE_HANDLE ...
+ * setSTRIPE_ACTIVE, clrSTRIPE_HANDLE ...
* (lockdev check-buffers unlockdev) ..
* change-state ..
- * record io/ops needed unlockstripe schedule io/ops
+ * record io/ops needed clearSTRIPE_ACTIVE schedule io/ops
* release an active stripe (release_stripe())
* lockdev if (!--cnt) { if STRIPE_HANDLE, add to handle_list else add to inactive-list } unlockdev
*
@@ -128,8 +125,7 @@
* on a cached buffer, and plus one if the stripe is undergoing stripe
* operations.
*
- * Stripe operations are performed outside the stripe lock,
- * the stripe operations are:
+ * The stripe operations are:
* -copying data between the stripe cache and user application buffers
* -computing blocks to save a disk access, or to recover a missing block
* -updating the parity on a write operation (reconstruct write and
@@ -159,7 +155,8 @@
*/
/*
- * Operations state - intermediate states that are visible outside of sh->lock
+ * Operations state - intermediate states that are visible outside of
+ * STRIPE_ACTIVE.
* In general _idle indicates nothing is running, _run indicates a data
* processing operation is active, and _result means the data processing result
* is stable and can be acted upon. For simple operations like biofill and
@@ -200,7 +197,7 @@ enum reconstruct_states {
struct stripe_head {
struct hlist_node hash;
struct list_head lru; /* inactive_list or handle_list */
- struct raid5_private_data *raid_conf;
+ struct r5conf *raid_conf;
short generation; /* increments with every
* reshape */
sector_t sector; /* sector of this row */
@@ -209,7 +206,6 @@ struct stripe_head {
short ddf_layout;/* use DDF ordering to calculate Q */
unsigned long state; /* state flags */
atomic_t count; /* nr of active thread/requests */
- spinlock_t lock;
int bm_seq; /* sequence number for bitmap flushes */
int disks; /* disks in stripe */
enum check_states check_state;
@@ -240,19 +236,20 @@ struct stripe_head {
};
/* stripe_head_state - collects and tracks the dynamic state of a stripe_head
- * for handle_stripe. It is only valid under spin_lock(sh->lock);
+ * for handle_stripe.
*/
struct stripe_head_state {
int syncing, expanding, expanded;
int locked, uptodate, to_read, to_write, failed, written;
int to_fill, compute, req_compute, non_overwrite;
- int failed_num;
+ int failed_num[2];
+ int p_failed, q_failed;
+ int dec_preread_active;
unsigned long ops_request;
-};
-/* r6_state - extra state data only relevant to r6 */
-struct r6_state {
- int p_failed, q_failed, failed_num[2];
+ struct bio *return_bi;
+ struct md_rdev *blocked_rdev;
+ int handle_bad_blocks;
};
/* Flags */
@@ -268,14 +265,16 @@ struct r6_state {
#define R5_ReWrite 9 /* have tried to over-write the readerror */
#define R5_Expanded 10 /* This block now has post-expand data */
-#define R5_Wantcompute 11 /* compute_block in progress treat as
- * uptodate
- */
-#define R5_Wantfill 12 /* dev->toread contains a bio that needs
- * filling
- */
-#define R5_Wantdrain 13 /* dev->towrite needs to be drained */
-#define R5_WantFUA 14 /* Write should be FUA */
+#define R5_Wantcompute 11 /* compute_block in progress treat as
+ * uptodate
+ */
+#define R5_Wantfill 12 /* dev->toread contains a bio that needs
+ * filling
+ */
+#define R5_Wantdrain 13 /* dev->towrite needs to be drained */
+#define R5_WantFUA 14 /* Write should be FUA */
+#define R5_WriteError 15 /* got a write error - need to record it */
+#define R5_MadeGood 16 /* A bad block has been fixed by writing to it*/
/*
* Write method
*/
@@ -289,21 +288,25 @@ struct r6_state {
/*
* Stripe state
*/
-#define STRIPE_HANDLE 2
-#define STRIPE_SYNCING 3
-#define STRIPE_INSYNC 4
-#define STRIPE_PREREAD_ACTIVE 5
-#define STRIPE_DELAYED 6
-#define STRIPE_DEGRADED 7
-#define STRIPE_BIT_DELAY 8
-#define STRIPE_EXPANDING 9
-#define STRIPE_EXPAND_SOURCE 10
-#define STRIPE_EXPAND_READY 11
-#define STRIPE_IO_STARTED 12 /* do not count towards 'bypass_count' */
-#define STRIPE_FULL_WRITE 13 /* all blocks are set to be overwritten */
-#define STRIPE_BIOFILL_RUN 14
-#define STRIPE_COMPUTE_RUN 15
-#define STRIPE_OPS_REQ_PENDING 16
+enum {
+ STRIPE_ACTIVE,
+ STRIPE_HANDLE,
+ STRIPE_SYNC_REQUESTED,
+ STRIPE_SYNCING,
+ STRIPE_INSYNC,
+ STRIPE_PREREAD_ACTIVE,
+ STRIPE_DELAYED,
+ STRIPE_DEGRADED,
+ STRIPE_BIT_DELAY,
+ STRIPE_EXPANDING,
+ STRIPE_EXPAND_SOURCE,
+ STRIPE_EXPAND_READY,
+ STRIPE_IO_STARTED, /* do not count towards 'bypass_count' */
+ STRIPE_FULL_WRITE, /* all blocks are set to be overwritten */
+ STRIPE_BIOFILL_RUN,
+ STRIPE_COMPUTE_RUN,
+ STRIPE_OPS_REQ_PENDING,
+};
/*
* Operation request flags
@@ -336,17 +339,17 @@ struct r6_state {
* PREREAD_ACTIVE.
* In stripe_handle, if we find pre-reading is necessary, we do it if
* PREREAD_ACTIVE is set, else we set DELAYED which will send it to the delayed queue.
- * HANDLE gets cleared if stripe_handle leave nothing locked.
+ * HANDLE gets cleared if stripe_handle leaves nothing locked.
*/
struct disk_info {
- mdk_rdev_t *rdev;
+ struct md_rdev *rdev;
};
-struct raid5_private_data {
+struct r5conf {
struct hlist_head *stripe_hashtbl;
- mddev_t *mddev;
+ struct mddev *mddev;
struct disk_info *spare;
int chunk_sectors;
int level, algorithm;
@@ -399,7 +402,7 @@ struct raid5_private_data {
* (fresh device added).
* Cleared when a sync completes.
*/
-
+ int recovery_disabled;
/* per cpu variables */
struct raid5_percpu {
struct page *spare_page; /* Used when checking P/Q in raid6 */
@@ -433,11 +436,9 @@ struct raid5_private_data {
/* When taking over an array from a different personality, we store
* the new thread here until we fully activate the array.
*/
- struct mdk_thread_s *thread;
+ struct md_thread *thread;
};
-typedef struct raid5_private_data raid5_conf_t;
-
/*
* Our supported algorithms
*/
@@ -500,7 +501,7 @@ static inline int algorithm_is_DDF(int layout)
return layout >= 8 && layout <= 10;
}
-extern int md_raid5_congested(mddev_t *mddev, int bits);
-extern void md_raid5_kick_device(raid5_conf_t *conf);
-extern int raid5_set_cache_size(mddev_t *mddev, int size);
+extern int md_raid5_congested(struct mddev *mddev, int bits);
+extern void md_raid5_kick_device(struct r5conf *conf);
+extern int raid5_set_cache_size(struct mddev *mddev, int size);
#endif
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 6995940b633a..9575db429df4 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -68,7 +68,6 @@ config VIDEO_V4L2_SUBDEV_API
config DVB_CORE
tristate "DVB for Linux"
- depends on NET && INET
select CRC32
help
DVB core utility functions for device handling, software fallbacks etc.
@@ -85,6 +84,19 @@ config DVB_CORE
If unsure say N.
+config DVB_NET
+ bool "DVB Network Support"
+ default (NET && INET)
+ depends on NET && INET && DVB_CORE
+ help
+ This option enables DVB Network Support which is a part of the DVB
+ standard. It is used, for example, by automatic firmware updates used
+ on Set-Top-Boxes. It can also be used to access the Internet via the
+ DVB card, if the network provider supports it.
+
+ You may want to disable the network support on embedded devices. If
+ unsure say Y.
+
config VIDEO_MEDIA
tristate
default (DVB_CORE && (VIDEO_DEV = n)) || (VIDEO_DEV && (DVB_CORE = n)) || (DVB_CORE && VIDEO_DEV)
diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c
index 9af2140b57a4..d6b1cf66042d 100644
--- a/drivers/media/common/saa7146_core.c
+++ b/drivers/media/common/saa7146_core.c
@@ -18,7 +18,10 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <media/saa7146.h>
+#include <linux/module.h>
LIST_HEAD(saa7146_devices);
DEFINE_MUTEX(saa7146_devices_lock);
@@ -35,10 +38,9 @@ static void dump_registers(struct saa7146_dev* dev)
{
int i = 0;
- INFO((" @ %li jiffies:\n",jiffies));
- for(i = 0; i <= 0x148; i+=4) {
- printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i));
- }
+ pr_info(" @ %li jiffies:\n", jiffies);
+ for (i = 0; i <= 0x148; i += 4)
+ pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i));
}
#endif
@@ -72,9 +74,8 @@ static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
if (saa7146_read(dev, MC2) & 2)
break;
if (err) {
- printk(KERN_ERR "%s: %s timed out while waiting for "
- "registers getting programmed\n",
- dev->name, __func__);
+ pr_err("%s: %s timed out while waiting for registers getting programmed\n",
+ dev->name, __func__);
return -ETIMEDOUT;
}
msleep(1);
@@ -88,8 +89,8 @@ static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
break;
saa7146_read(dev, MC2);
if (err) {
- DEB_S(("%s: %s timed out while waiting for transfer "
- "completion\n", dev->name, __func__));
+ DEB_S("%s: %s timed out while waiting for transfer completion\n",
+ dev->name, __func__);
return -ETIMEDOUT;
}
msleep(1);
@@ -109,9 +110,8 @@ static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
if (saa7146_read(dev, MC2) & 2)
break;
if (!loops--) {
- printk(KERN_ERR "%s: %s timed out while waiting for "
- "registers getting programmed\n",
- dev->name, __func__);
+ pr_err("%s: %s timed out while waiting for registers getting programmed\n",
+ dev->name, __func__);
return -ETIMEDOUT;
}
udelay(1);
@@ -124,8 +124,8 @@ static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
break;
saa7146_read(dev, MC2);
if (!loops--) {
- DEB_S(("%s: %s timed out while waiting for transfer "
- "completion\n", dev->name, __func__));
+ DEB_S("%s: %s timed out while waiting for transfer completion\n",
+ dev->name, __func__);
return -ETIMEDOUT;
}
udelay(5);
@@ -264,7 +264,9 @@ int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt
ptr = pt->cpu;
for (i = 0; i < sglen; i++, list++) {
/*
- printk("i:%d, adr:0x%08x, len:%d, offset:%d\n", i,sg_dma_address(list), sg_dma_len(list), list->offset);
+ pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n",
+ i, sg_dma_address(list), sg_dma_len(list),
+ list->offset);
*/
for (p = 0; p * 4096 < list->length; p++, ptr++) {
*ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);
@@ -281,9 +283,9 @@ int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt
/*
ptr = pt->cpu;
- printk("offset: %d\n",pt->offset);
+ pr_debug("offset: %d\n", pt->offset);
for(i=0;i<5;i++) {
- printk("ptr1 %d: 0x%08x\n",i,ptr[i]);
+ pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]);
}
*/
return 0;
@@ -314,7 +316,7 @@ static irqreturn_t interrupt_hw(int irq, void *dev_id)
}
}
if (0 != (isr & (MASK_27))) {
- DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
if (dev->vv_data && dev->vv_callback)
dev->vv_callback(dev,isr);
isr &= ~MASK_27;
@@ -333,14 +335,15 @@ static irqreturn_t interrupt_hw(int irq, void *dev_id)
} else {
u32 psr = saa7146_read(dev, PSR);
u32 ssr = saa7146_read(dev, SSR);
- printk(KERN_WARNING "%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
- dev->name, isr, psr, ssr);
+ pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
+ dev->name, isr, psr, ssr);
}
isr &= ~(MASK_16|MASK_17);
}
if( 0 != isr ) {
- ERR(("warning: interrupt enabled, but not handled properly.(0x%08x)\n",isr));
- ERR(("disabling interrupt source(s)!\n"));
+ ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n",
+ isr);
+ ERR("disabling interrupt source(s)!\n");
SAA7146_IER_DISABLE(dev,isr);
}
saa7146_write(dev, ISR, ack_isr);
@@ -360,15 +363,15 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
/* clear out mem for sure */
dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
if (!dev) {
- ERR(("out of memory.\n"));
+ ERR("out of memory\n");
goto out;
}
- DEB_EE(("pci:%p\n",pci));
+ DEB_EE("pci:%p\n", pci);
err = pci_enable_device(pci);
if (err < 0) {
- ERR(("pci_enable_device() failed.\n"));
+ ERR("pci_enable_device() failed\n");
goto err_free;
}
@@ -389,7 +392,7 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
dev->mem = ioremap(pci_resource_start(pci, 0),
pci_resource_len(pci, 0));
if (!dev->mem) {
- ERR(("ioremap() failed.\n"));
+ ERR("ioremap() failed\n");
err = -ENODEV;
goto err_release;
}
@@ -414,7 +417,7 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED | IRQF_DISABLED,
dev->name, dev);
if (err < 0) {
- ERR(("request_irq() failed.\n"));
+ ERR("request_irq() failed\n");
goto err_unmap;
}
@@ -444,7 +447,9 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
/* create a nice device name */
sprintf(dev->name, "saa7146 (%d)", saa7146_num);
- INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision, pci->irq, pci->subsystem_vendor, pci->subsystem_device));
+ pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n",
+ dev->mem, dev->revision, pci->irq,
+ pci->subsystem_vendor, pci->subsystem_device);
dev->ext = ext;
mutex_init(&dev->v4l2_lock);
@@ -464,12 +469,12 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
err = -ENODEV;
if (ext->probe && ext->probe(dev)) {
- DEB_D(("ext->probe() failed for %p. skipping device.\n",dev));
+ DEB_D("ext->probe() failed for %p. skipping device.\n", dev);
goto err_free_i2c;
}
if (ext->attach(dev, pci_ext)) {
- DEB_D(("ext->attach() failed for %p. skipping device.\n",dev));
+ DEB_D("ext->attach() failed for %p. skipping device.\n", dev);
goto err_free_i2c;
}
/* V4L extensions will set the pci drvdata to the v4l2_device in the
@@ -521,7 +526,7 @@ static void saa7146_remove_one(struct pci_dev *pdev)
{ NULL, 0 }
}, *p;
- DEB_EE(("dev:%p\n",dev));
+ DEB_EE("dev:%p\n", dev);
dev->ext->detach(dev);
/* Zero the PCI drvdata after use. */
@@ -552,21 +557,21 @@ static void saa7146_remove_one(struct pci_dev *pdev)
int saa7146_register_extension(struct saa7146_extension* ext)
{
- DEB_EE(("ext:%p\n",ext));
+ DEB_EE("ext:%p\n", ext);
ext->driver.name = ext->name;
ext->driver.id_table = ext->pci_tbl;
ext->driver.probe = saa7146_init_one;
ext->driver.remove = saa7146_remove_one;
- printk("saa7146: register extension '%s'.\n",ext->name);
+ pr_info("register extension '%s'\n", ext->name);
return pci_register_driver(&ext->driver);
}
int saa7146_unregister_extension(struct saa7146_extension* ext)
{
- DEB_EE(("ext:%p\n",ext));
- printk("saa7146: unregister extension '%s'.\n",ext->name);
+ DEB_EE("ext:%p\n", ext);
+ pr_info("unregister extension '%s'\n", ext->name);
pci_unregister_driver(&ext->driver);
return 0;
}
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 1bd3dd762c6b..71f8e018e564 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -1,4 +1,7 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <media/saa7146_vv.h>
+#include <linux/module.h>
/****************************************************************************/
/* resource management functions, shamelessly stolen from saa7134 driver */
@@ -9,21 +12,23 @@ int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
struct saa7146_vv *vv = dev->vv_data;
if (fh->resources & bit) {
- DEB_D(("already allocated! want: 0x%02x, cur:0x%02x\n",bit,vv->resources));
+ DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
+ bit, vv->resources);
/* have it already allocated */
return 1;
}
/* is it free? */
if (vv->resources & bit) {
- DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit));
+ DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
+ vv->resources, bit);
/* no, someone else uses it */
return 0;
}
/* it's free, grab it */
- fh->resources |= bit;
+ fh->resources |= bit;
vv->resources |= bit;
- DEB_D(("res: get 0x%02x, cur:0x%02x\n",bit,vv->resources));
+ DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
return 1;
}
@@ -34,9 +39,9 @@ void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
BUG_ON((fh->resources & bits) != bits);
- fh->resources &= ~bits;
+ fh->resources &= ~bits;
vv->resources &= ~bits;
- DEB_D(("res: put 0x%02x, cur:0x%02x\n",bits,vv->resources));
+ DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
}
@@ -47,7 +52,7 @@ void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
struct saa7146_buf *buf)
{
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- DEB_EE(("dev:%p, buf:%p\n",dev,buf));
+ DEB_EE("dev:%p, buf:%p\n", dev, buf);
BUG_ON(in_interrupt());
@@ -66,18 +71,19 @@ int saa7146_buffer_queue(struct saa7146_dev *dev,
struct saa7146_buf *buf)
{
assert_spin_locked(&dev->slock);
- DEB_EE(("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf));
+ DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
BUG_ON(!q);
if (NULL == q->curr) {
q->curr = buf;
- DEB_D(("immediately activating buffer %p\n", buf));
+ DEB_D("immediately activating buffer %p\n", buf);
buf->activate(dev,buf,NULL);
} else {
list_add_tail(&buf->vb.queue,&q->queue);
buf->vb.state = VIDEOBUF_QUEUED;
- DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf));
+ DEB_D("adding buffer %p to queue. (active buffer present)\n",
+ buf);
}
return 0;
}
@@ -87,14 +93,14 @@ void saa7146_buffer_finish(struct saa7146_dev *dev,
int state)
{
assert_spin_locked(&dev->slock);
- DEB_EE(("dev:%p, dmaq:%p, state:%d\n", dev, q, state));
- DEB_EE(("q->curr:%p\n",q->curr));
+ DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
+ DEB_EE("q->curr:%p\n", q->curr);
BUG_ON(!q->curr);
/* finish current buffer */
if (NULL == q->curr) {
- DEB_D(("aiii. no current buffer\n"));
+ DEB_D("aiii. no current buffer\n");
return;
}
@@ -112,7 +118,7 @@ void saa7146_buffer_next(struct saa7146_dev *dev,
BUG_ON(!q);
- DEB_INT(("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi));
+ DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
assert_spin_locked(&dev->slock);
if (!list_empty(&q->queue)) {
@@ -122,10 +128,11 @@ void saa7146_buffer_next(struct saa7146_dev *dev,
if (!list_empty(&q->queue))
next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
q->curr = buf;
- DEB_INT(("next buffer: buf:%p, prev:%p, next:%p\n", buf, q->queue.prev,q->queue.next));
+ DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
+ buf, q->queue.prev, q->queue.next);
buf->activate(dev,buf,next);
} else {
- DEB_INT(("no next buffer. stopping.\n"));
+ DEB_INT("no next buffer. stopping.\n");
if( 0 != vbi ) {
/* turn off video-dma3 */
saa7146_write(dev,MC1, MASK_20);
@@ -162,11 +169,11 @@ void saa7146_buffer_timeout(unsigned long data)
struct saa7146_dev *dev = q->dev;
unsigned long flags;
- DEB_EE(("dev:%p, dmaq:%p\n", dev, q));
+ DEB_EE("dev:%p, dmaq:%p\n", dev, q);
spin_lock_irqsave(&dev->slock,flags);
if (q->curr) {
- DEB_D(("timeout on %p\n", q->curr));
+ DEB_D("timeout on %p\n", q->curr);
saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
}
@@ -194,12 +201,12 @@ static int fops_open(struct file *file)
enum v4l2_buf_type type;
- DEB_EE(("file:%p, dev:%s\n", file, video_device_node_name(vdev)));
+ DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
if (mutex_lock_interruptible(&saa7146_devices_lock))
return -ERESTARTSYS;
- DEB_D(("using: %p\n",dev));
+ DEB_D("using: %p\n", dev);
type = vdev->vfl_type == VFL_TYPE_GRABBER
? V4L2_BUF_TYPE_VIDEO_CAPTURE
@@ -207,7 +214,7 @@ static int fops_open(struct file *file)
/* check if an extension is registered */
if( NULL == dev->ext ) {
- DEB_S(("no extension registered for this device.\n"));
+ DEB_S("no extension registered for this device\n");
result = -ENODEV;
goto out;
}
@@ -215,7 +222,7 @@ static int fops_open(struct file *file)
/* allocate per open data */
fh = kzalloc(sizeof(*fh),GFP_KERNEL);
if (NULL == fh) {
- DEB_S(("cannot allocate memory for per open data.\n"));
+ DEB_S("cannot allocate memory for per open data\n");
result = -ENOMEM;
goto out;
}
@@ -225,13 +232,13 @@ static int fops_open(struct file *file)
fh->type = type;
if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- DEB_S(("initializing vbi...\n"));
+ DEB_S("initializing vbi...\n");
if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
result = saa7146_vbi_uops.open(dev,file);
if (dev->ext_vv_data->vbi_fops.open)
dev->ext_vv_data->vbi_fops.open(file);
} else {
- DEB_S(("initializing video...\n"));
+ DEB_S("initializing video...\n");
result = saa7146_video_uops.open(dev,file);
}
@@ -259,7 +266,7 @@ static int fops_release(struct file *file)
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
- DEB_EE(("file:%p\n", file));
+ DEB_EE("file:%p\n", file);
if (mutex_lock_interruptible(&saa7146_devices_lock))
return -ERESTARTSYS;
@@ -289,12 +296,14 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma)
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
- DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",file, vma));
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
+ file, vma);
q = &fh->video_q;
break;
}
case V4L2_BUF_TYPE_VBI_CAPTURE: {
- DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",file, vma));
+ DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
+ file, vma);
q = &fh->vbi_q;
break;
}
@@ -312,14 +321,14 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
struct videobuf_buffer *buf = NULL;
struct videobuf_queue *q;
- DEB_EE(("file:%p, poll:%p\n",file, wait));
+ DEB_EE("file:%p, poll:%p\n", file, wait);
if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
if( 0 == fh->vbi_q.streaming )
return videobuf_poll_stream(file, &fh->vbi_q, wait);
q = &fh->vbi_q;
} else {
- DEB_D(("using video queue.\n"));
+ DEB_D("using video queue\n");
q = &fh->video_q;
}
@@ -327,17 +336,17 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
if (!buf) {
- DEB_D(("buf == NULL!\n"));
+ DEB_D("buf == NULL!\n");
return POLLERR;
}
poll_wait(file, &buf->done, wait);
if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
- DEB_D(("poll succeeded!\n"));
+ DEB_D("poll succeeded!\n");
return POLLIN|POLLRDNORM;
}
- DEB_D(("nothing to poll for, buf->state:%d\n",buf->state));
+ DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
return 0;
}
@@ -346,18 +355,20 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof
struct saa7146_fh *fh = file->private_data;
switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
-// DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count));
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+/*
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun",
+ file, data, (unsigned long)count);
+*/
return saa7146_video_uops.read(file,data,count,ppos);
- }
- case V4L2_BUF_TYPE_VBI_CAPTURE: {
-// DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count));
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+/*
+ DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
+ file, data, (unsigned long)count);
+*/
if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
return saa7146_vbi_uops.read(file,data,count,ppos);
- else
- return -EINVAL;
- }
- break;
+ return -EINVAL;
default:
BUG();
return 0;
@@ -398,22 +409,22 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status)
{
u32 isr = status;
- DEB_INT(("dev:%p, isr:0x%08x\n",dev,(u32)status));
+ DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
if (0 != (isr & (MASK_27))) {
- DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
saa7146_video_uops.irq_done(dev,isr);
}
if (0 != (isr & (MASK_28))) {
u32 mc2 = saa7146_read(dev, MC2);
if( 0 != (mc2 & MASK_15)) {
- DEB_INT(("irq: RPS1 vbi workaround (0x%08x).\n",isr));
+ DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
wake_up(&dev->vv_data->vbi_wq);
saa7146_write(dev,MC2, MASK_31);
return;
}
- DEB_INT(("irq: RPS1 (0x%08x).\n",isr));
+ DEB_INT("irq: RPS1 (0x%08x)\n", isr);
saa7146_vbi_uops.irq_done(dev,isr);
}
}
@@ -429,13 +440,13 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
if (vv == NULL) {
- ERR(("out of memory. aborting.\n"));
+ ERR("out of memory. aborting.\n");
return -ENOMEM;
}
ext_vv->ops = saa7146_video_ioctl_ops;
ext_vv->core_ops = &saa7146_video_ioctl_ops;
- DEB_EE(("dev:%p\n",dev));
+ DEB_EE("dev:%p\n", dev);
/* set default values for video parts of the saa7146 */
saa7146_write(dev, BCS_CTRL, 0x80400040);
@@ -450,7 +461,7 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
if( NULL == vv->d_clipping.cpu_addr ) {
- ERR(("out of memory. aborting.\n"));
+ ERR("out of memory. aborting.\n");
kfree(vv);
return -1;
}
@@ -471,7 +482,7 @@ int saa7146_vv_release(struct saa7146_dev* dev)
{
struct saa7146_vv *vv = dev->vv_data;
- DEB_EE(("dev:%p\n",dev));
+ DEB_EE("dev:%p\n", dev);
v4l2_device_unregister(&dev->v4l2_dev);
pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
@@ -490,7 +501,7 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
int err;
int i;
- DEB_EE(("dev:%p, name:'%s', type:%d\n",dev,name,type));
+ DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
// released by vfd->release
vfd = video_device_alloc();
@@ -509,13 +520,13 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
err = video_register_device(vfd, type, -1);
if (err < 0) {
- ERR(("cannot register v4l2 device. skipping.\n"));
+ ERR("cannot register v4l2 device. skipping.\n");
video_device_release(vfd);
return err;
}
- INFO(("%s: registered device %s [v4l2]\n",
- dev->name, video_device_node_name(vfd)));
+ pr_info("%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(vfd));
*vid = vfd;
return 0;
@@ -524,7 +535,7 @@ EXPORT_SYMBOL_GPL(saa7146_register_device);
int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
{
- DEB_EE(("dev:%p\n",dev));
+ DEB_EE("dev:%p\n", dev);
video_unregister_device(*vid);
*vid = NULL;
diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c
index 1d1d8d200755..bc1f545c95cb 100644
--- a/drivers/media/common/saa7146_hlp.c
+++ b/drivers/media/common/saa7146_hlp.c
@@ -1,4 +1,7 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
+#include <linux/export.h>
#include <media/saa7146_vv.h>
static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format)
@@ -711,8 +714,8 @@ static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa71
int depth = sfmt->depth;
- DEB_CAP(("[size=%dx%d,fields=%s]\n",
- width,height,v4l2_field_names[field]));
+ DEB_CAP("[size=%dx%d,fields=%s]\n",
+ width, height, v4l2_field_names[field]);
if( bytesperline != 0) {
vdma1.pitch = bytesperline*2;
@@ -837,8 +840,8 @@ static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa71
BUG_ON(0 == buf->pt[1].dma);
BUG_ON(0 == buf->pt[2].dma);
- DEB_CAP(("[size=%dx%d,fields=%s]\n",
- width,height,v4l2_field_names[field]));
+ DEB_CAP("[size=%dx%d,fields=%s]\n",
+ width, height, v4l2_field_names[field]);
/* fixme: look at bytesperline! */
@@ -998,12 +1001,12 @@ void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struc
struct saa7146_vv *vv = dev->vv_data;
u32 vdma1_prot_addr;
- DEB_CAP(("buf:%p, next:%p\n",buf,next));
+ DEB_CAP("buf:%p, next:%p\n", buf, next);
vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1);
if( 0 == vdma1_prot_addr ) {
/* clear out beginning of streaming bit (rps register 0)*/
- DEB_CAP(("forcing sync to new frame\n"));
+ DEB_CAP("forcing sync to new frame\n");
saa7146_write(dev, MC2, MASK_27 );
}
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c
index b2ba9dc0dd6d..22027198129d 100644
--- a/drivers/media/common/saa7146_i2c.c
+++ b/drivers/media/common/saa7146_i2c.c
@@ -1,8 +1,10 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <media/saa7146_vv.h>
static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
{
-//fm DEB_I2C(("'%s'.\n", adapter->name));
+ /* DEB_I2C("'%s'\n", adapter->name); */
return I2C_FUNC_I2C
| I2C_FUNC_SMBUS_QUICK
@@ -14,9 +16,7 @@ static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
static inline u32 saa7146_i2c_status(struct saa7146_dev *dev)
{
u32 iicsta = saa7146_read(dev, I2C_STATUS);
-/*
- DEB_I2C(("status: 0x%08x\n",iicsta));
-*/
+ /* DEB_I2C("status: 0x%08x\n", iicsta); */
return iicsta;
}
@@ -39,10 +39,11 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op)
plus one extra byte to address the device */
mem = 1 + ((mem-1) / 3);
- /* we assume that op points to a memory of at least SAA7146_I2C_MEM bytes
- size. if we exceed this limit... */
- if ( (4*mem) > SAA7146_I2C_MEM ) {
-//fm DEB_I2C(("cannot prepare i2c-message.\n"));
+ /* we assume that op points to a memory of at least
+ * SAA7146_I2C_MEM bytes size. if we exceed this limit...
+ */
+ if ((4 * mem) > SAA7146_I2C_MEM) {
+ /* DEB_I2C("cannot prepare i2c-message\n"); */
return -ENOMEM;
}
@@ -123,7 +124,7 @@ static int saa7146_i2c_reset(struct saa7146_dev *dev)
if ( 0 != ( status & SAA7146_I2C_BUSY) ) {
/* yes, kill ongoing operation */
- DEB_I2C(("busy_state detected.\n"));
+ DEB_I2C("busy_state detected\n");
/* set "ABORT-OPERATION"-bit (bit 7)*/
saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
@@ -141,7 +142,7 @@ static int saa7146_i2c_reset(struct saa7146_dev *dev)
if ( dev->i2c_bitrate != status ) {
- DEB_I2C(("error_state detected. status:0x%08x\n",status));
+ DEB_I2C("error_state detected. status:0x%08x\n", status);
/* Repeat the abort operation. This seems to be necessary
after serious protocol errors caused by e.g. the SAA7740 */
@@ -164,7 +165,7 @@ static int saa7146_i2c_reset(struct saa7146_dev *dev)
/* if any error is still present, a fatal error has occurred ... */
status = saa7146_i2c_status(dev);
if ( dev->i2c_bitrate != status ) {
- DEB_I2C(("fatal error. status:0x%08x\n",status));
+ DEB_I2C("fatal error. status:0x%08x\n", status);
return -1;
}
@@ -181,7 +182,8 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
unsigned long timeout;
/* write out i2c-command */
- DEB_I2C(("before: 0x%08x (status: 0x%08x), %d\n",*dword,saa7146_read(dev, I2C_STATUS), dev->i2c_op));
+ DEB_I2C("before: 0x%08x (status: 0x%08x), %d\n",
+ *dword, saa7146_read(dev, I2C_STATUS), dev->i2c_op);
if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
@@ -202,7 +204,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
/* a signal arrived */
return -ERESTARTSYS;
- printk(KERN_WARNING "%s %s [irq]: timed out waiting for end of xfer\n",
+ pr_warn("%s %s [irq]: timed out waiting for end of xfer\n",
dev->name, __func__);
return -EIO;
}
@@ -220,7 +222,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
break;
}
if (time_after(jiffies,timeout)) {
- printk(KERN_WARNING "%s %s: timed out waiting for MC2\n",
+ pr_warn("%s %s: timed out waiting for MC2\n",
dev->name, __func__);
return -EIO;
}
@@ -237,7 +239,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
/* this is normal when probing the bus
* (no answer from nonexisistant device...)
*/
- printk(KERN_WARNING "%s %s [poll]: timed out waiting for end of xfer\n",
+ pr_warn("%s %s [poll]: timed out waiting for end of xfer\n",
dev->name, __func__);
return -EIO;
}
@@ -257,24 +259,24 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
if ( 0 == (status & SAA7146_I2C_ERR) ||
0 == (status & SAA7146_I2C_BUSY) ) {
/* it may take some time until ERR goes high - ignore */
- DEB_I2C(("unexpected i2c status %04x\n", status));
+ DEB_I2C("unexpected i2c status %04x\n", status);
}
if( 0 != (status & SAA7146_I2C_SPERR) ) {
- DEB_I2C(("error due to invalid start/stop condition.\n"));
+ DEB_I2C("error due to invalid start/stop condition\n");
}
if( 0 != (status & SAA7146_I2C_DTERR) ) {
- DEB_I2C(("error in data transmission.\n"));
+ DEB_I2C("error in data transmission\n");
}
if( 0 != (status & SAA7146_I2C_DRERR) ) {
- DEB_I2C(("error when receiving data.\n"));
+ DEB_I2C("error when receiving data\n");
}
if( 0 != (status & SAA7146_I2C_AL) ) {
- DEB_I2C(("error because arbitration lost.\n"));
+ DEB_I2C("error because arbitration lost\n");
}
/* we handle address-errors here */
if( 0 != (status & SAA7146_I2C_APERR) ) {
- DEB_I2C(("error in address phase.\n"));
+ DEB_I2C("error in address phase\n");
return -EREMOTEIO;
}
@@ -284,7 +286,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
/* read back data, just in case we were reading ... */
*dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER));
- DEB_I2C(("after: 0x%08x\n",*dword));
+ DEB_I2C("after: 0x%08x\n", *dword);
return 0;
}
@@ -299,7 +301,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
return -ERESTARTSYS;
for(i=0;i<num;i++) {
- DEB_I2C(("msg:%d/%d\n",i+1,num));
+ DEB_I2C("msg:%d/%d\n", i+1, num);
}
/* prepare the message(s), get number of u32s to transfer */
@@ -316,7 +318,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
/* reset the i2c-device if necessary */
err = saa7146_i2c_reset(dev);
if ( 0 > err ) {
- DEB_I2C(("could not reset i2c-device.\n"));
+ DEB_I2C("could not reset i2c-device\n");
goto out;
}
@@ -336,7 +338,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
address error and trust the saa7146 address error detection. */
if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags))
goto out;
- DEB_I2C(("error while sending message(s). starting again.\n"));
+ DEB_I2C("error while sending message(s). starting again\n");
break;
}
}
@@ -356,13 +358,13 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
/* if any things had to be read, get the results */
if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
- DEB_I2C(("could not cleanup i2c-message.\n"));
+ DEB_I2C("could not cleanup i2c-message\n");
err = -1;
goto out;
}
/* return the number of delivered messages */
- DEB_I2C(("transmission successful. (msg:%d).\n",err));
+ DEB_I2C("transmission successful. (msg:%d)\n", err);
out:
/* another bug in revision 0: the i2c-registers get uploaded randomly by other
uploads, so we better clear them out before continuing */
@@ -370,7 +372,7 @@ out:
__le32 zero = 0;
saa7146_i2c_reset(dev);
if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) {
- INFO(("revision 0 error. this should never happen.\n"));
+ pr_info("revision 0 error. this should never happen\n");
}
}
@@ -400,7 +402,7 @@ static struct i2c_algorithm saa7146_algo = {
int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate)
{
- DEB_EE(("bitrate: 0x%08x\n",bitrate));
+ DEB_EE("bitrate: 0x%08x\n", bitrate);
/* enable i2c-port pins */
saa7146_write(dev, MC1, (MASK_08 | MASK_24));
diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c
index afe85801d6ca..b2e718343739 100644
--- a/drivers/media/common/saa7146_vbi.c
+++ b/drivers/media/common/saa7146_vbi.c
@@ -14,7 +14,7 @@ static int vbi_workaround(struct saa7146_dev *dev)
DECLARE_WAITQUEUE(wait, current);
- DEB_VBI(("dev:%p\n",dev));
+ DEB_VBI("dev:%p\n", dev);
/* once again, a bug in the saa7146: the brs acquisition
is buggy and especially the BXO-counter does not work
@@ -40,14 +40,14 @@ static int vbi_workaround(struct saa7146_dev *dev)
WRITE_RPS1(0xc000008c);
/* wait for vbi_a or vbi_b*/
if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
- DEB_D(("...using port b\n"));
+ DEB_D("...using port b\n");
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B);
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B);
/*
WRITE_RPS1(CMD_PAUSE | MASK_09);
*/
} else {
- DEB_D(("...using port a\n"));
+ DEB_D("...using port a\n");
WRITE_RPS1(CMD_PAUSE | MASK_10);
}
/* upload brs */
@@ -103,7 +103,7 @@ static int vbi_workaround(struct saa7146_dev *dev)
schedule();
- DEB_VBI(("brs bug workaround %d/1.\n",i));
+ DEB_VBI("brs bug workaround %d/1\n", i);
remove_wait_queue(&vv->vbi_wq, &wait);
current->state = TASK_RUNNING;
@@ -116,7 +116,8 @@ static int vbi_workaround(struct saa7146_dev *dev)
if(signal_pending(current)) {
- DEB_VBI(("aborted (rps:0x%08x).\n",saa7146_read(dev,RPS_ADDR1)));
+ DEB_VBI("aborted (rps:0x%08x)\n",
+ saa7146_read(dev, RPS_ADDR1));
/* stop rps1 for sure */
saa7146_write(dev, MC1, MASK_29);
@@ -207,7 +208,7 @@ static int buffer_activate(struct saa7146_dev *dev,
struct saa7146_vv *vv = dev->vv_data;
buf->vb.state = VIDEOBUF_ACTIVE;
- DEB_VBI(("dev:%p, buf:%p, next:%p\n",dev,buf,next));
+ DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next);
saa7146_set_vbi_capture(dev,buf,next);
mod_timer(&vv->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
@@ -228,10 +229,10 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e
llength = vbi_pixel_to_capture;
size = lines * llength;
- DEB_VBI(("vb:%p\n",vb));
+ DEB_VBI("vb:%p\n", vb);
if (0 != buf->vb.baddr && buf->vb.bsize < size) {
- DEB_VBI(("size mismatch.\n"));
+ DEB_VBI("size mismatch\n");
return -EINVAL;
}
@@ -263,7 +264,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e
return 0;
oops:
- DEB_VBI(("error out.\n"));
+ DEB_VBI("error out\n");
saa7146_dma_free(dev,q,buf);
return err;
@@ -279,7 +280,7 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned
*size = lines * llength;
*count = 2;
- DEB_VBI(("count:%d, size:%d\n",*count,*size));
+ DEB_VBI("count:%d, size:%d\n", *count, *size);
return 0;
}
@@ -292,7 +293,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
- DEB_VBI(("vb:%p\n",vb));
+ DEB_VBI("vb:%p\n", vb);
saa7146_buffer_queue(dev,&vv->vbi_q,buf);
}
@@ -303,7 +304,7 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_dev *dev = fh->dev;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
- DEB_VBI(("vb:%p\n",vb));
+ DEB_VBI("vb:%p\n", vb);
saa7146_dma_free(dev,q,buf);
}
@@ -321,7 +322,7 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file)
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
unsigned long flags;
- DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
spin_lock_irqsave(&dev->slock,flags);
@@ -354,14 +355,14 @@ static void vbi_read_timeout(unsigned long data)
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
- DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
vbi_stop(fh, file);
}
static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
{
- DEB_VBI(("dev:%p\n",dev));
+ DEB_VBI("dev:%p\n", dev);
INIT_LIST_HEAD(&vv->vbi_q.queue);
@@ -380,11 +381,11 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file)
u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
int ret = 0;
- DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS);
if (0 == ret) {
- DEB_S(("cannot get vbi RESOURCE_DMA3_BRS resource\n"));
+ DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n");
return -EBUSY;
}
@@ -425,7 +426,7 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file)
saa7146_write(dev, BRS_CTRL, 0x00000001);
if (0 != (ret = vbi_workaround(dev))) {
- DEB_VBI(("vbi workaround failed!\n"));
+ DEB_VBI("vbi workaround failed!\n");
/* return ret;*/
}
}
@@ -439,7 +440,7 @@ static void vbi_close(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_vv *vv = dev->vv_data;
- DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
if( fh == vv->vbi_streaming ) {
vbi_stop(fh, file);
@@ -453,13 +454,13 @@ static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
spin_lock(&dev->slock);
if (vv->vbi_q.curr) {
- DEB_VBI(("dev:%p, curr:%p\n",dev,vv->vbi_q.curr));
+ DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_q.curr);
/* this must be += 2, one count for each field */
vv->vbi_fieldcount+=2;
vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount;
saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE);
} else {
- DEB_VBI(("dev:%p\n",dev));
+ DEB_VBI("dev:%p\n", dev);
}
saa7146_buffer_next(dev,&vv->vbi_q,1);
@@ -473,7 +474,7 @@ static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff
struct saa7146_vv *vv = dev->vv_data;
ssize_t ret = 0;
- DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
if( NULL == vv->vbi_streaming ) {
// fixme: check if dma3 is available
@@ -482,7 +483,8 @@ static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff
}
if( fh != vv->vbi_streaming ) {
- DEB_VBI(("open %p is already using vbi capture.",vv->vbi_streaming));
+ DEB_VBI("open %p is already using vbi capture\n",
+ vv->vbi_streaming);
return -EBUSY;
}
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index 9aafa4e969a8..ce30533fd972 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -1,5 +1,8 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <media/saa7146_vv.h>
#include <media/v4l2-chip-ident.h>
+#include <linux/module.h>
static int max_memory = 32;
@@ -94,7 +97,7 @@ struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fou
}
}
- DEB_D(("unknown pixelformat:'%4.4s'\n",(char *)&fourcc));
+ DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
return NULL;
}
@@ -107,32 +110,32 @@ int saa7146_start_preview(struct saa7146_fh *fh)
struct v4l2_format fmt;
int ret = 0, err = 0;
- DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
/* check if we have overlay informations */
if( NULL == fh->ov.fh ) {
- DEB_D(("no overlay data available. try S_FMT first.\n"));
+ DEB_D("no overlay data available. try S_FMT first.\n");
return -EAGAIN;
}
/* check if streaming capture is running */
if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D(("streaming capture is active.\n"));
+ DEB_D("streaming capture is active\n");
return -EBUSY;
}
/* check if overlay is running */
if (IS_OVERLAY_ACTIVE(fh) != 0) {
if (vv->video_fh == fh) {
- DEB_D(("overlay is already active.\n"));
+ DEB_D("overlay is already active\n");
return 0;
}
- DEB_D(("overlay is already active in another open.\n"));
+ DEB_D("overlay is already active in another open\n");
return -EBUSY;
}
if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
- DEB_D(("cannot get necessary overlay resources\n"));
+ DEB_D("cannot get necessary overlay resources\n");
return -EBUSY;
}
@@ -145,13 +148,13 @@ int saa7146_start_preview(struct saa7146_fh *fh)
fh->ov.win = fmt.fmt.win;
vv->ov_data = &fh->ov;
- DEB_D(("%dx%d+%d+%d %s field=%s\n",
- fh->ov.win.w.width,fh->ov.win.w.height,
- fh->ov.win.w.left,fh->ov.win.w.top,
- vv->ov_fmt->name,v4l2_field_names[fh->ov.win.field]));
+ DEB_D("%dx%d+%d+%d %s field=%s\n",
+ fh->ov.win.w.width, fh->ov.win.w.height,
+ fh->ov.win.w.left, fh->ov.win.w.top,
+ vv->ov_fmt->name, v4l2_field_names[fh->ov.win.field]);
if (0 != (ret = saa7146_enable_overlay(fh))) {
- DEB_D(("enabling overlay failed: %d\n",ret));
+ DEB_D("enabling overlay failed: %d\n", ret);
saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
return ret;
}
@@ -168,22 +171,22 @@ int saa7146_stop_preview(struct saa7146_fh *fh)
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
- DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
/* check if streaming capture is running */
if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D(("streaming capture is active.\n"));
+ DEB_D("streaming capture is active\n");
return -EBUSY;
}
/* check if overlay is running at all */
if ((vv->video_status & STATUS_OVERLAY) == 0) {
- DEB_D(("no active overlay.\n"));
+ DEB_D("no active overlay\n");
return 0;
}
if (vv->video_fh != fh) {
- DEB_D(("overlay is active, but in another open.\n"));
+ DEB_D("overlay is active, but in another open\n");
return -EBUSY;
}
@@ -268,7 +271,7 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu
int length = dma->sglen;
struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
- DEB_EE(("dev:%p, buf:%p, sg_len:%d\n",dev,buf,length));
+ DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length);
if( 0 != IS_PLANAR(sfmt->trans)) {
struct saa7146_pgtable *pt1 = &buf->pt[0];
@@ -288,7 +291,8 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu
m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
o1 = size%PAGE_SIZE;
o2 = (size+(size/4))%PAGE_SIZE;
- DEB_CAP(("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",size,m1,m2,m3,o1,o2));
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
break;
}
case 16: {
@@ -298,7 +302,8 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu
m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1;
o1 = size%PAGE_SIZE;
o2 = (size+(size/2))%PAGE_SIZE;
- DEB_CAP(("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",size,m1,m2,m3,o1,o2));
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
break;
}
default: {
@@ -387,23 +392,23 @@ static int video_begin(struct saa7146_fh *fh)
unsigned int resource;
int ret = 0, err = 0;
- DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
if ((vv->video_status & STATUS_CAPTURE) != 0) {
if (vv->video_fh == fh) {
- DEB_S(("already capturing.\n"));
+ DEB_S("already capturing\n");
return 0;
}
- DEB_S(("already capturing in another open.\n"));
+ DEB_S("already capturing in another open\n");
return -EBUSY;
}
if ((vv->video_status & STATUS_OVERLAY) != 0) {
- DEB_S(("warning: suspending overlay video for streaming capture.\n"));
+ DEB_S("warning: suspending overlay video for streaming capture\n");
vv->ov_suspend = vv->video_fh;
err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
if (0 != err) {
- DEB_D(("suspending video failed. aborting\n"));
+ DEB_D("suspending video failed. aborting\n");
return err;
}
}
@@ -420,7 +425,7 @@ static int video_begin(struct saa7146_fh *fh)
ret = saa7146_res_get(fh, resource);
if (0 == ret) {
- DEB_S(("cannot get capture resource %d\n",resource));
+ DEB_S("cannot get capture resource %d\n", resource);
if (vv->ov_suspend != NULL) {
saa7146_start_preview(vv->ov_suspend);
vv->ov_suspend = NULL;
@@ -448,15 +453,15 @@ static int video_end(struct saa7146_fh *fh, struct file *file)
unsigned long flags;
unsigned int resource;
u32 dmas = 0;
- DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
- DEB_S(("not capturing.\n"));
+ DEB_S("not capturing\n");
return 0;
}
if (vv->video_fh != fh) {
- DEB_S(("capturing, but in another open.\n"));
+ DEB_S("capturing, but in another open\n");
return -EBUSY;
}
@@ -530,7 +535,7 @@ static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_format *fmt;
- DEB_EE(("VIDIOC_S_FBUF\n"));
+ DEB_EE("VIDIOC_S_FBUF\n");
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
return -EPERM;
@@ -542,13 +547,13 @@ static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f
/* planar formats are not allowed for overlay video, clipping and video dma would clash */
if (fmt->flags & FORMAT_IS_PLANAR)
- DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",
- (char *)&fmt->pixelformat));
+ DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n",
+ (char *)&fmt->pixelformat);
/* check if overlay is running */
if (IS_OVERLAY_ACTIVE(fh) != 0) {
if (vv->video_fh != fh) {
- DEB_D(("refusing to change framebuffer informations while overlay is active in another open.\n"));
+ DEB_D("refusing to change framebuffer informations while overlay is active in another open\n");
return -EBUSY;
}
}
@@ -559,7 +564,7 @@ static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f
if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) {
vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8;
- DEB_D(("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline));
+ DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline);
}
return 0;
}
@@ -588,7 +593,7 @@ static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *
if (ctrl == NULL)
return -EINVAL;
- DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n", c->id));
+ DEB_EE("VIDIOC_QUERYCTRL: id:%d\n", c->id);
*c = *ctrl;
return 0;
}
@@ -607,25 +612,25 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
case V4L2_CID_BRIGHTNESS:
value = saa7146_read(dev, BCS_CTRL);
c->value = 0xff & (value >> 24);
- DEB_D(("V4L2_CID_BRIGHTNESS: %d\n", c->value));
+ DEB_D("V4L2_CID_BRIGHTNESS: %d\n", c->value);
break;
case V4L2_CID_CONTRAST:
value = saa7146_read(dev, BCS_CTRL);
c->value = 0x7f & (value >> 16);
- DEB_D(("V4L2_CID_CONTRAST: %d\n", c->value));
+ DEB_D("V4L2_CID_CONTRAST: %d\n", c->value);
break;
case V4L2_CID_SATURATION:
value = saa7146_read(dev, BCS_CTRL);
c->value = 0x7f & (value >> 0);
- DEB_D(("V4L2_CID_SATURATION: %d\n", c->value));
+ DEB_D("V4L2_CID_SATURATION: %d\n", c->value);
break;
case V4L2_CID_VFLIP:
c->value = vv->vflip;
- DEB_D(("V4L2_CID_VFLIP: %d\n", c->value));
+ DEB_D("V4L2_CID_VFLIP: %d\n", c->value);
break;
case V4L2_CID_HFLIP:
c->value = vv->hflip;
- DEB_D(("V4L2_CID_HFLIP: %d\n", c->value));
+ DEB_D("V4L2_CID_HFLIP: %d\n", c->value);
break;
default:
return -EINVAL;
@@ -641,7 +646,7 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
ctrl = ctrl_by_id(c->id);
if (NULL == ctrl) {
- DEB_D(("unknown control %d\n", c->id));
+ DEB_D("unknown control %d\n", c->id);
return -EINVAL;
}
@@ -686,14 +691,14 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
case V4L2_CID_HFLIP:
/* fixme: we can support changing VFLIP and HFLIP here... */
if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D(("V4L2_CID_HFLIP while active capture.\n"));
+ DEB_D("V4L2_CID_HFLIP while active capture\n");
return -EBUSY;
}
vv->hflip = c->value;
break;
case V4L2_CID_VFLIP:
if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D(("V4L2_CID_VFLIP while active capture.\n"));
+ DEB_D("V4L2_CID_VFLIP while active capture\n");
return -EBUSY;
}
vv->vflip = c->value;
@@ -748,7 +753,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma
int maxw, maxh;
int calc_bpl;
- DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh));
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat);
if (NULL == fmt)
@@ -777,7 +782,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma
vv->last_field = V4L2_FIELD_INTERLACED;
break;
default:
- DEB_D(("no known field mode '%d'.\n", field));
+ DEB_D("no known field mode '%d'\n", field);
return -EINVAL;
}
@@ -796,8 +801,9 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma
f->fmt.pix.bytesperline = calc_bpl;
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
- DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n", f->fmt.pix.width,
- f->fmt.pix.height, f->fmt.pix.bytesperline, f->fmt.pix.sizeimage));
+ DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
return 0;
}
@@ -811,22 +817,23 @@ static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_f
enum v4l2_field field;
int maxw, maxh;
- DEB_EE(("dev:%p\n", dev));
+ DEB_EE("dev:%p\n", dev);
if (NULL == vv->ov_fb.base) {
- DEB_D(("no fb base set.\n"));
+ DEB_D("no fb base set\n");
return -EINVAL;
}
if (NULL == vv->ov_fmt) {
- DEB_D(("no fb fmt set.\n"));
+ DEB_D("no fb fmt set\n");
return -EINVAL;
}
if (win->w.width < 48 || win->w.height < 32) {
- DEB_D(("min width/height. (%d,%d)\n", win->w.width, win->w.height));
+ DEB_D("min width/height. (%d,%d)\n",
+ win->w.width, win->w.height);
return -EINVAL;
}
if (win->clipcount > 16) {
- DEB_D(("clipcount too big.\n"));
+ DEB_D("clipcount too big\n");
return -EINVAL;
}
@@ -848,7 +855,7 @@ static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_f
case V4L2_FIELD_INTERLACED:
break;
default:
- DEB_D(("no known field mode '%d'.\n", field));
+ DEB_D("no known field mode '%d'\n", field);
return -EINVAL;
}
@@ -868,16 +875,17 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_forma
struct saa7146_vv *vv = dev->vv_data;
int err;
- DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh));
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_EE(("streaming capture is active\n"));
+ DEB_EE("streaming capture is active\n");
return -EBUSY;
}
err = vidioc_try_fmt_vid_cap(file, fh, f);
if (0 != err)
return err;
fh->video_fmt = f->fmt.pix;
- DEB_EE(("set to pixelformat '%4.4s'\n", (char *)&fh->video_fmt.pixelformat));
+ DEB_EE("set to pixelformat '%4.4s'\n",
+ (char *)&fh->video_fmt.pixelformat);
return 0;
}
@@ -888,7 +896,7 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_f
struct saa7146_vv *vv = dev->vv_data;
int err;
- DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh));
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh);
err = vidioc_try_fmt_vid_overlay(file, fh, f);
if (0 != err)
return err;
@@ -931,7 +939,7 @@ static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
if (e->index < 0 )
return -EINVAL;
if( e->index < dev->ext_vv_data->num_stds ) {
- DEB_EE(("VIDIOC_ENUMSTD: index:%d\n",e->index));
+ DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index);
v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name);
return 0;
}
@@ -946,10 +954,10 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
int found = 0;
int err, i;
- DEB_EE(("VIDIOC_S_STD\n"));
+ DEB_EE("VIDIOC_S_STD\n");
if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
- DEB_D(("cannot change video standard while streaming capture is active\n"));
+ DEB_D("cannot change video standard while streaming capture is active\n");
return -EBUSY;
}
@@ -957,7 +965,7 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
vv->ov_suspend = vv->video_fh;
err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
if (0 != err) {
- DEB_D(("suspending video failed. aborting\n"));
+ DEB_D("suspending video failed. aborting\n");
return err;
}
}
@@ -978,11 +986,11 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
}
if (!found) {
- DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
+ DEB_EE("VIDIOC_S_STD: standard not found\n");
return -EINVAL;
}
- DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name));
+ DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name);
return 0;
}
@@ -990,7 +998,7 @@ static int vidioc_overlay(struct file *file, void *fh, unsigned int on)
{
int err;
- DEB_D(("VIDIOC_OVERLAY on:%d\n", on));
+ DEB_D("VIDIOC_OVERLAY on:%d\n", on);
if (on)
err = saa7146_start_preview(fh);
else
@@ -1047,7 +1055,7 @@ static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type typ
struct saa7146_fh *fh = __fh;
int err;
- DEB_D(("VIDIOC_STREAMON, type:%d\n", type));
+ DEB_D("VIDIOC_STREAMON, type:%d\n", type);
err = video_begin(fh);
if (err)
@@ -1066,18 +1074,18 @@ static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type ty
struct saa7146_vv *vv = dev->vv_data;
int err;
- DEB_D(("VIDIOC_STREAMOFF, type:%d\n", type));
+ DEB_D("VIDIOC_STREAMOFF, type:%d\n", type);
/* ugly: we need to copy some checks from video_end(),
because videobuf_streamoff() relies on the capture running.
check and fix this */
if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
- DEB_S(("not capturing.\n"));
+ DEB_S("not capturing\n");
return 0;
}
if (vv->video_fh != fh) {
- DEB_S(("capturing, but in another open.\n"));
+ DEB_S("capturing, but in another open\n");
return -EBUSY;
}
@@ -1087,7 +1095,7 @@ static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type ty
else if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
err = videobuf_streamoff(&fh->vbi_q);
if (0 != err) {
- DEB_D(("warning: videobuf_streamoff() failed.\n"));
+ DEB_D("warning: videobuf_streamoff() failed\n");
video_end(fh, file);
} else {
err = video_end(fh, file);
@@ -1174,25 +1182,27 @@ static int buffer_prepare(struct videobuf_queue *q,
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
int size,err = 0;
- DEB_CAP(("vbuf:%p\n",vb));
+ DEB_CAP("vbuf:%p\n", vb);
/* sanity checks */
if (fh->video_fmt.width < 48 ||
fh->video_fmt.height < 32 ||
fh->video_fmt.width > vv->standard->h_max_out ||
fh->video_fmt.height > vv->standard->v_max_out) {
- DEB_D(("w (%d) / h (%d) out of bounds.\n",fh->video_fmt.width,fh->video_fmt.height));
+ DEB_D("w (%d) / h (%d) out of bounds\n",
+ fh->video_fmt.width, fh->video_fmt.height);
return -EINVAL;
}
size = fh->video_fmt.sizeimage;
if (0 != buf->vb.baddr && buf->vb.bsize < size) {
- DEB_D(("size mismatch.\n"));
+ DEB_D("size mismatch\n");
return -EINVAL;
}
- DEB_CAP(("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
- fh->video_fmt.width,fh->video_fmt.height,size,v4l2_field_names[fh->video_fmt.field]));
+ DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
+ fh->video_fmt.width, fh->video_fmt.height,
+ size, v4l2_field_names[fh->video_fmt.field]);
if (buf->vb.width != fh->video_fmt.width ||
buf->vb.bytesperline != fh->video_fmt.bytesperline ||
buf->vb.height != fh->video_fmt.height ||
@@ -1238,7 +1248,7 @@ static int buffer_prepare(struct videobuf_queue *q,
return 0;
oops:
- DEB_D(("error out.\n"));
+ DEB_D("error out\n");
saa7146_dma_free(dev,q,buf);
return err;
@@ -1259,7 +1269,7 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned
*count = (max_memory*1048576) / *size;
}
- DEB_CAP(("%d buffers, %d bytes each.\n",*count,*size));
+ DEB_CAP("%d buffers, %d bytes each\n", *count, *size);
return 0;
}
@@ -1272,7 +1282,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
- DEB_CAP(("vbuf:%p\n",vb));
+ DEB_CAP("vbuf:%p\n", vb);
saa7146_buffer_queue(fh->dev,&vv->video_q,buf);
}
@@ -1283,7 +1293,7 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_dev *dev = fh->dev;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
- DEB_CAP(("vbuf:%p\n",vb));
+ DEB_CAP("vbuf:%p\n", vb);
saa7146_dma_free(dev,q,buf);
@@ -1347,18 +1357,14 @@ static void video_close(struct saa7146_dev *dev, struct file *file)
struct saa7146_fh *fh = file->private_data;
struct saa7146_vv *vv = dev->vv_data;
struct videobuf_queue *q = &fh->video_q;
- int err;
- if (IS_CAPTURE_ACTIVE(fh) != 0) {
- err = video_end(fh, file);
- } else if (IS_OVERLAY_ACTIVE(fh) != 0) {
- err = saa7146_stop_preview(fh);
- }
+ if (IS_CAPTURE_ACTIVE(fh) != 0)
+ video_end(fh, file);
+ else if (IS_OVERLAY_ACTIVE(fh) != 0)
+ saa7146_stop_preview(fh);
videobuf_stop(q);
-
/* hmm, why is this function declared void? */
- /* return err */
}
@@ -1368,7 +1374,7 @@ static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
struct saa7146_dmaqueue *q = &vv->video_q;
spin_lock(&dev->slock);
- DEB_CAP(("called.\n"));
+ DEB_CAP("called\n");
/* only finish the buffer if we have one... */
if( NULL != q->curr ) {
@@ -1386,15 +1392,15 @@ static ssize_t video_read(struct file *file, char __user *data, size_t count, lo
struct saa7146_vv *vv = dev->vv_data;
ssize_t ret = 0;
- DEB_EE(("called.\n"));
+ DEB_EE("called\n");
if ((vv->video_status & STATUS_CAPTURE) != 0) {
/* fixme: should we allow read() captures while streaming capture? */
if (vv->video_fh == fh) {
- DEB_S(("already capturing.\n"));
+ DEB_S("already capturing\n");
return -EBUSY;
}
- DEB_S(("already capturing in another open.\n"));
+ DEB_S("already capturing in another open\n");
return -EBUSY;
}
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
index 22d3ca36370e..996302ae210e 100644
--- a/drivers/media/common/tuners/Kconfig
+++ b/drivers/media/common/tuners/Kconfig
@@ -23,6 +23,7 @@ config MEDIA_TUNER
depends on VIDEO_MEDIA && I2C
select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE
@@ -152,6 +153,15 @@ config MEDIA_TUNER_XC5000
This device is only used inside a SiP called together with a
demodulator for now.
+config MEDIA_TUNER_XC4000
+ tristate "Xceive XC4000 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A driver for the silicon tuner XC4000 from Xceive.
+ This device is only used inside a SiP called together with a
+ demodulator for now.
+
config MEDIA_TUNER_MXL5005S
tristate "MaxLinear MSL5005S silicon tuner"
depends on VIDEO_MEDIA && I2C
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
index 2cb4f5327843..196c12a55f9a 100644
--- a/drivers/media/common/tuners/Makefile
+++ b/drivers/media/common/tuners/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_MEDIA_TUNER_TDA9887) += tda9887.o
obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o
obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o
obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o
+obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o
obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o
obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o
obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o
@@ -27,5 +28,5 @@ obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/mt20xx.c b/drivers/media/common/tuners/mt20xx.c
index d0e70e10a717..0e74e97e0d1a 100644
--- a/drivers/media/common/tuners/mt20xx.c
+++ b/drivers/media/common/tuners/mt20xx.c
@@ -430,11 +430,10 @@ static void mt2050_set_antenna(struct dvb_frontend *fe, unsigned char antenna)
{
struct microtune_priv *priv = fe->tuner_priv;
unsigned char buf[2];
- int ret;
buf[0] = 6;
buf[1] = antenna ? 0x11 : 0x10;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
tuner_dbg("mt2050: enabled antenna connector %d\n", antenna);
}
@@ -574,21 +573,20 @@ static int mt2050_init(struct dvb_frontend *fe)
{
struct microtune_priv *priv = fe->tuner_priv;
unsigned char buf[2];
- int ret;
- buf[0]=6;
- buf[1]=0x10;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); // power
+ buf[0] = 6;
+ buf[1] = 0x10;
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* power */
- buf[0]=0x0f;
- buf[1]=0x0f;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); // m1lo
+ buf[0] = 0x0f;
+ buf[1] = 0x0f;
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* m1lo */
- buf[0]=0x0d;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
- tuner_i2c_xfer_recv(&priv->i2c_props,buf,1);
+ buf[0] = 0x0d;
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 1);
+ tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1);
- tuner_dbg("mt2050: sro is %x\n",buf[0]);
+ tuner_dbg("mt2050: sro is %x\n", buf[0]);
memcpy(&fe->ops.tuner_ops, &mt2050_tuner_ops, sizeof(struct dvb_tuner_ops));
diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c
index 56fe75c94deb..54be9e6faaaf 100644
--- a/drivers/media/common/tuners/mxl5005s.c
+++ b/drivers/media/common/tuners/mxl5005s.c
@@ -309,7 +309,6 @@ static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum,
static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val);
static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum,
u8 *RegVal, int *count);
-static u32 MXL_GetXtalInt(u32 Xtal_Freq);
static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq);
static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe);
static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe);
@@ -2307,14 +2306,6 @@ static u16 MXL_IFSynthInit(struct dvb_frontend *fe)
return status ;
}
-static u32 MXL_GetXtalInt(u32 Xtal_Freq)
-{
- if ((Xtal_Freq % 1000000) == 0)
- return (Xtal_Freq / 10000);
- else
- return (((Xtal_Freq / 1000000) + 1)*100);
-}
-
static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
{
struct mxl5005s_state *state = fe->tuner_priv;
@@ -2324,13 +2315,10 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
u32 Kdbl_RF = 2;
u32 tg_divval;
u32 tg_lo;
- u32 Xtal_Int;
u32 Fref_TG;
u32 Fvco;
- Xtal_Int = MXL_GetXtalInt(state->Fxtal);
-
state->RF_IN = RF_Freq;
MXL_SynthRFTGLO_Calc(fe);
@@ -2779,6 +2767,16 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
tg_lo = (((Fmax/10 - Fvco)/100)*32) / ((Fmax-Fmin)/1000)+8;
/* below equation is same as above but much harder to debug.
+ *
+ * static u32 MXL_GetXtalInt(u32 Xtal_Freq)
+ * {
+ * if ((Xtal_Freq % 1000000) == 0)
+ * return (Xtal_Freq / 10000);
+ * else
+ * return (((Xtal_Freq / 1000000) + 1)*100);
+ * }
+ *
+ * u32 Xtal_Int = MXL_GetXtalInt(state->Fxtal);
* tg_lo = ( ((Fmax/10000 * Xtal_Int)/100) -
* ((state->TG_LO/10000)*divider_val *
* (state->Fxtal/10000)/100) )*32/((Fmax-Fmin)/10000 *
diff --git a/drivers/media/common/tuners/tda18212.c b/drivers/media/common/tuners/tda18212.c
index 1f1db20d46b1..e29cc2bc113a 100644
--- a/drivers/media/common/tuners/tda18212.c
+++ b/drivers/media/common/tuners/tda18212.c
@@ -18,7 +18,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "tda18212_priv.h"
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "tda18212.h"
+
+struct tda18212_priv {
+ struct tda18212_config *cfg;
+ struct i2c_adapter *i2c;
+};
+
+#define dbg(fmt, arg...) \
+do { \
+ if (debug) \
+ pr_info("%s: " fmt, __func__, ##arg); \
+} while (0)
static int debug;
module_param(debug, int, 0644);
@@ -46,7 +59,8 @@ static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
if (ret == 1) {
ret = 0;
} else {
- warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
+ pr_warn("i2c wr failed ret:%d reg:%02x len:%d\n",
+ ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
@@ -77,7 +91,8 @@ static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
memcpy(val, buf, len);
ret = 0;
} else {
- warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
+ pr_warn("i2c rd failed ret:%d reg:%02x len:%d\n",
+ ret, reg, len);
ret = -EREMOTEIO;
}
@@ -129,8 +144,8 @@ static int tda18212_set_params(struct dvb_frontend *fe,
{ 0x92, 0x53, 0x03 }, /* DVB-C */
};
- dbg("%s: delsys=%d RF=%d BW=%d", __func__,
- c->delivery_system, c->frequency, c->bandwidth_hz);
+ dbg("delsys=%d RF=%d BW=%d\n",
+ c->delivery_system, c->frequency, c->bandwidth_hz);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
@@ -196,7 +211,7 @@ exit:
return ret;
error:
- dbg("%s: failed:%d", __func__, ret);
+ dbg("failed:%d\n", ret);
goto exit;
}
@@ -245,13 +260,13 @@ struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
- dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);
+ dbg("ret:%d chip ID:%02x\n", ret, val);
if (ret || val != 0xc7) {
kfree(priv);
return NULL;
}
- info("NXP TDA18212HN successfully identified.");
+ pr_info("NXP TDA18212HN successfully identified\n");
memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
sizeof(struct dvb_tuner_ops));
diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c
index aae40e52af5b..39c645787b62 100644
--- a/drivers/media/common/tuners/tda18271-common.c
+++ b/drivers/media/common/tuners/tda18271-common.c
@@ -676,10 +676,28 @@ fail:
return ret;
}
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+int _tda_printk(struct tda18271_priv *state, const char *level,
+ const char *func, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int rtn;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (state)
+ rtn = printk("%s%s: [%d-%04x|%c] %pV",
+ level, func, i2c_adapter_id(state->i2c_props.adap),
+ state->i2c_props.addr,
+ (state->role == TDA18271_MASTER) ? 'M' : 'S',
+ &vaf);
+ else
+ rtn = printk("%s%s: %pV", level, func, &vaf);
+
+ va_end(args);
+
+ return rtn;
+}
diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
index 57022e88e338..63cc4004e211 100644
--- a/drivers/media/common/tuners/tda18271-fe.c
+++ b/drivers/media/common/tuners/tda18271-fe.c
@@ -1230,7 +1230,7 @@ static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg)
return 0;
}
-static struct dvb_tuner_ops tda18271_tuner_ops = {
+static const struct dvb_tuner_ops tda18271_tuner_ops = {
.info = {
.name = "NXP TDA18271HD",
.frequency_min = 45000000,
diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h
index 9589ab0576d2..94340f47562b 100644
--- a/drivers/media/common/tuners/tda18271-priv.h
+++ b/drivers/media/common/tuners/tda18271-priv.h
@@ -136,29 +136,26 @@ extern int tda18271_debug;
#define DBG_ADV 8
#define DBG_CAL 16
-#define tda_printk(st, kern, fmt, arg...) do {\
- if (st) { \
- struct tda18271_priv *state = st; \
- printk(kern "%s: [%d-%04x|%s] " fmt, __func__, \
- i2c_adapter_id(state->i2c_props.adap), \
- state->i2c_props.addr, \
- (state->role == TDA18271_MASTER) \
- ? "M" : "S", ##arg); \
- } else \
- printk(kern "%s: " fmt, __func__, ##arg); \
+__attribute__((format(printf, 4, 5)))
+int _tda_printk(struct tda18271_priv *state, const char *level,
+ const char *func, const char *fmt, ...);
+
+#define tda_printk(st, lvl, fmt, arg...) \
+ _tda_printk(st, lvl, __func__, fmt, ##arg)
+
+#define tda_dprintk(st, lvl, fmt, arg...) \
+do { \
+ if (tda18271_debug & lvl) \
+ tda_printk(st, KERN_DEBUG, fmt, ##arg); \
} while (0)
-#define tda_dprintk(st, lvl, fmt, arg...) do {\
- if (tda18271_debug & lvl) \
- tda_printk(st, KERN_DEBUG, fmt, ##arg); } while (0)
-
-#define tda_info(fmt, arg...) printk(KERN_INFO fmt, ##arg)
-#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg)
-#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg)
-#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg)
-#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg)
-#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg)
-#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg)
+#define tda_info(fmt, arg...) pr_info(fmt, ##arg)
+#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg)
+#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg)
+#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg)
+#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg)
+#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg)
+#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg)
#define tda_fail(ret) \
({ \
diff --git a/drivers/media/common/tuners/tda827x.c b/drivers/media/common/tuners/tda827x.c
index b21b6ea68b25..e0d5b43772b8 100644
--- a/drivers/media/common/tuners/tda827x.c
+++ b/drivers/media/common/tuners/tda827x.c
@@ -176,7 +176,7 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
if_freq = 5000000;
break;
}
- tuner_freq = params->frequency + if_freq;
+ tuner_freq = params->frequency;
i = 0;
while (tda827x_table[i].lomax < tuner_freq) {
@@ -185,6 +185,8 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
i++;
}
+ tuner_freq += if_freq;
+
N = ((tuner_freq + 125000) / 250000) << (tda827x_table[i].spd + 2);
buf[0] = 0;
buf[1] = (N>>8) | 0x40;
@@ -540,7 +542,7 @@ static int tda827xa_set_params(struct dvb_frontend *fe,
if_freq = 5000000;
break;
}
- tuner_freq = params->frequency + if_freq;
+ tuner_freq = params->frequency;
if (fe->ops.info.type == FE_QAM) {
dprintk("%s select tda827xa_dvbc\n", __func__);
@@ -554,6 +556,8 @@ static int tda827xa_set_params(struct dvb_frontend *fe,
i++;
}
+ tuner_freq += if_freq;
+
N = ((tuner_freq + 31250) / 62500) << frequency_map[i].spd;
buf[0] = 0; // subaddress
buf[1] = N >> 8;
diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c
index afba6dc5e080..e13683bab6b3 100644
--- a/drivers/media/common/tuners/tuner-types.c
+++ b/drivers/media/common/tuners/tuner-types.c
@@ -5,6 +5,7 @@
*/
#include <linux/i2c.h>
+#include <linux/module.h>
#include <media/tuner.h>
#include <media/tuner-types.h>
@@ -1805,6 +1806,10 @@ struct tunertype tuners[] = {
.name = "Xceive 5000 tuner",
/* see xc5000.c for details */
},
+ [TUNER_XC4000] = { /* Xceive 4000 */
+ .name = "Xceive 4000 tuner",
+ /* see xc4000.c for details */
+ },
[TUNER_TCL_MF02GIP_5N] = { /* TCL tuner MF02GIP-5N-E */
.name = "TCL tuner MF02GIP-5N-E",
.params = tuner_tcl_mf02gip_5n_params,
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index 16fba6b59616..3acbaa04e1b3 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -614,6 +614,13 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,
p += len;
size -= len;
}
+
+ /* silently fail if the frontend doesn't support I2C flush */
+ rc = do_tuner_callback(fe, XC2028_I2C_FLUSH, 0);
+ if ((rc < 0) && (rc != -EINVAL)) {
+ tuner_err("error executing flush: %d\n", rc);
+ return rc;
+ }
}
return 0;
}
@@ -933,11 +940,16 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
* that xc2028 will be in a safe state.
* Maybe this might also be needed for DTV.
*/
- if (new_type == V4L2_TUNER_ANALOG_TV) {
+ switch (new_type) {
+ case V4L2_TUNER_ANALOG_TV:
rc = send_seq(priv, {0x00, 0x00});
- /* Analog modes require offset = 0 */
- } else {
+ /* Analog mode requires offset = 0 */
+ break;
+ case V4L2_TUNER_RADIO:
+ /* Radio mode requires offset = 0 */
+ break;
+ case V4L2_TUNER_DIGITAL_TV:
/*
* Digital modes require an offset to adjust to the
* proper frequency. The offset depends on what
diff --git a/drivers/media/common/tuners/tuner-xc2028.h b/drivers/media/common/tuners/tuner-xc2028.h
index 9778c96a5006..9ebfb2d0ff14 100644
--- a/drivers/media/common/tuners/tuner-xc2028.h
+++ b/drivers/media/common/tuners/tuner-xc2028.h
@@ -54,6 +54,7 @@ struct xc2028_config {
/* xc2028 commands for callback */
#define XC2028_TUNER_RESET 0
#define XC2028_RESET_CLK 1
+#define XC2028_I2C_FLUSH 2
#if defined(CONFIG_MEDIA_TUNER_XC2028) || (defined(CONFIG_MEDIA_TUNER_XC2028_MODULE) && defined(MODULE))
extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/common/tuners/xc4000.c b/drivers/media/common/tuners/xc4000.c
new file mode 100644
index 000000000000..634f4d9b6c63
--- /dev/null
+++ b/drivers/media/common/tuners/xc4000.c
@@ -0,0 +1,1691 @@
+/*
+ * Driver for Xceive XC4000 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2007 Xceive Corporation
+ * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
+ * Copyright (c) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ * Copyright (c) 2009 Davide Ferri <d.ferri@zero11.it>
+ * Copyright (c) 2010 Istvan Varga <istvan_v@mailbox.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <asm/unaligned.h>
+
+#include "dvb_frontend.h"
+
+#include "xc4000.h"
+#include "tuner-i2c.h"
+#include "tuner-xc2028-types.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debugging level (0 to 2, default: 0 (off)).");
+
+static int no_poweroff;
+module_param(no_poweroff, int, 0644);
+MODULE_PARM_DESC(no_poweroff, "Power management (1: disabled, 2: enabled, "
+ "0 (default): use device-specific default mode).");
+
+static int audio_std;
+module_param(audio_std, int, 0644);
+MODULE_PARM_DESC(audio_std, "Audio standard. XC4000 audio decoder explicitly "
+ "needs to know what audio standard is needed for some video standards "
+ "with audio A2 or NICAM. The valid settings are a sum of:\n"
+ " 1: use NICAM/B or A2/B instead of NICAM/A or A2/A\n"
+ " 2: use A2 instead of NICAM or BTSC\n"
+ " 4: use SECAM/K3 instead of K1\n"
+ " 8: use PAL-D/K audio for SECAM-D/K\n"
+ "16: use FM radio input 1 instead of input 2\n"
+ "32: use mono audio (the lower three bits are ignored)");
+
+static char firmware_name[30];
+module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
+MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
+ "default firmware name.");
+
+static DEFINE_MUTEX(xc4000_list_mutex);
+static LIST_HEAD(hybrid_tuner_instance_list);
+
+#define dprintk(level, fmt, arg...) if (debug >= level) \
+ printk(KERN_INFO "%s: " fmt, "xc4000", ## arg)
+
+/* struct for storing firmware table */
+struct firmware_description {
+ unsigned int type;
+ v4l2_std_id id;
+ __u16 int_freq;
+ unsigned char *ptr;
+ unsigned int size;
+};
+
+struct firmware_properties {
+ unsigned int type;
+ v4l2_std_id id;
+ v4l2_std_id std_req;
+ __u16 int_freq;
+ unsigned int scode_table;
+ int scode_nr;
+};
+
+struct xc4000_priv {
+ struct tuner_i2c_props i2c_props;
+ struct list_head hybrid_tuner_instance_list;
+ struct firmware_description *firm;
+ int firm_size;
+ u32 if_khz;
+ u32 freq_hz;
+ u32 bandwidth;
+ u8 video_standard;
+ u8 rf_mode;
+ u8 default_pm;
+ u8 dvb_amplitude;
+ u8 set_smoothedcvbs;
+ u8 ignore_i2c_write_errors;
+ __u16 firm_version;
+ struct firmware_properties cur_fw;
+ __u16 hwmodel;
+ __u16 hwvers;
+ struct mutex lock;
+};
+
+#define XC4000_AUDIO_STD_B 1
+#define XC4000_AUDIO_STD_A2 2
+#define XC4000_AUDIO_STD_K3 4
+#define XC4000_AUDIO_STD_L 8
+#define XC4000_AUDIO_STD_INPUT1 16
+#define XC4000_AUDIO_STD_MONO 32
+
+#define XC4000_DEFAULT_FIRMWARE "dvb-fe-xc4000-1.4.fw"
+
+/* Misc Defines */
+#define MAX_TV_STANDARD 24
+#define XC_MAX_I2C_WRITE_LENGTH 64
+#define XC_POWERED_DOWN 0x80000000U
+
+/* Signal Types */
+#define XC_RF_MODE_AIR 0
+#define XC_RF_MODE_CABLE 1
+
+/* Product id */
+#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000
+#define XC_PRODUCT_ID_XC4000 0x0FA0
+#define XC_PRODUCT_ID_XC4100 0x1004
+
+/* Registers (Write-only) */
+#define XREG_INIT 0x00
+#define XREG_VIDEO_MODE 0x01
+#define XREG_AUDIO_MODE 0x02
+#define XREG_RF_FREQ 0x03
+#define XREG_D_CODE 0x04
+#define XREG_DIRECTSITTING_MODE 0x05
+#define XREG_SEEK_MODE 0x06
+#define XREG_POWER_DOWN 0x08
+#define XREG_SIGNALSOURCE 0x0A
+#define XREG_SMOOTHEDCVBS 0x0E
+#define XREG_AMPLITUDE 0x10
+
+/* Registers (Read-only) */
+#define XREG_ADC_ENV 0x00
+#define XREG_QUALITY 0x01
+#define XREG_FRAME_LINES 0x02
+#define XREG_HSYNC_FREQ 0x03
+#define XREG_LOCK 0x04
+#define XREG_FREQ_ERROR 0x05
+#define XREG_SNR 0x06
+#define XREG_VERSION 0x07
+#define XREG_PRODUCT_ID 0x08
+
+/*
+ Basic firmware description. This will remain with
+ the driver for documentation purposes.
+
+ This represents an I2C firmware file encoded as a
+ string of unsigned char. Format is as follows:
+
+ char[0 ]=len0_MSB -> len = len_MSB * 256 + len_LSB
+ char[1 ]=len0_LSB -> length of first write transaction
+ char[2 ]=data0 -> first byte to be sent
+ char[3 ]=data1
+ char[4 ]=data2
+ char[ ]=...
+ char[M ]=dataN -> last byte to be sent
+ char[M+1]=len1_MSB -> len = len_MSB * 256 + len_LSB
+ char[M+2]=len1_LSB -> length of second write transaction
+ char[M+3]=data0
+ char[M+4]=data1
+ ...
+ etc.
+
+ The [len] value should be interpreted as follows:
+
+ len= len_MSB _ len_LSB
+ len=1111_1111_1111_1111 : End of I2C_SEQUENCE
+ len=0000_0000_0000_0000 : Reset command: Do hardware reset
+ len=0NNN_NNNN_NNNN_NNNN : Normal transaction: number of bytes = {1:32767)
+ len=1WWW_WWWW_WWWW_WWWW : Wait command: wait for {1:32767} ms
+
+ For the RESET and WAIT commands, the two following bytes will contain
+ immediately the length of the following transaction.
+*/
+
+struct XC_TV_STANDARD {
+ const char *Name;
+ u16 audio_mode;
+ u16 video_mode;
+ u16 int_freq;
+};
+
+/* Tuner standards */
+#define XC4000_MN_NTSC_PAL_BTSC 0
+#define XC4000_MN_NTSC_PAL_A2 1
+#define XC4000_MN_NTSC_PAL_EIAJ 2
+#define XC4000_MN_NTSC_PAL_Mono 3
+#define XC4000_BG_PAL_A2 4
+#define XC4000_BG_PAL_NICAM 5
+#define XC4000_BG_PAL_MONO 6
+#define XC4000_I_PAL_NICAM 7
+#define XC4000_I_PAL_NICAM_MONO 8
+#define XC4000_DK_PAL_A2 9
+#define XC4000_DK_PAL_NICAM 10
+#define XC4000_DK_PAL_MONO 11
+#define XC4000_DK_SECAM_A2DK1 12
+#define XC4000_DK_SECAM_A2LDK3 13
+#define XC4000_DK_SECAM_A2MONO 14
+#define XC4000_DK_SECAM_NICAM 15
+#define XC4000_L_SECAM_NICAM 16
+#define XC4000_LC_SECAM_NICAM 17
+#define XC4000_DTV6 18
+#define XC4000_DTV8 19
+#define XC4000_DTV7_8 20
+#define XC4000_DTV7 21
+#define XC4000_FM_Radio_INPUT2 22
+#define XC4000_FM_Radio_INPUT1 23
+
+static struct XC_TV_STANDARD xc4000_standard[MAX_TV_STANDARD] = {
+ {"M/N-NTSC/PAL-BTSC", 0x0000, 0x80A0, 4500},
+ {"M/N-NTSC/PAL-A2", 0x0000, 0x80A0, 4600},
+ {"M/N-NTSC/PAL-EIAJ", 0x0040, 0x80A0, 4500},
+ {"M/N-NTSC/PAL-Mono", 0x0078, 0x80A0, 4500},
+ {"B/G-PAL-A2", 0x0000, 0x8159, 5640},
+ {"B/G-PAL-NICAM", 0x0004, 0x8159, 5740},
+ {"B/G-PAL-MONO", 0x0078, 0x8159, 5500},
+ {"I-PAL-NICAM", 0x0080, 0x8049, 6240},
+ {"I-PAL-NICAM-MONO", 0x0078, 0x8049, 6000},
+ {"D/K-PAL-A2", 0x0000, 0x8049, 6380},
+ {"D/K-PAL-NICAM", 0x0080, 0x8049, 6200},
+ {"D/K-PAL-MONO", 0x0078, 0x8049, 6500},
+ {"D/K-SECAM-A2 DK1", 0x0000, 0x8049, 6340},
+ {"D/K-SECAM-A2 L/DK3", 0x0000, 0x8049, 6000},
+ {"D/K-SECAM-A2 MONO", 0x0078, 0x8049, 6500},
+ {"D/K-SECAM-NICAM", 0x0080, 0x8049, 6200},
+ {"L-SECAM-NICAM", 0x8080, 0x0009, 6200},
+ {"L'-SECAM-NICAM", 0x8080, 0x4009, 6200},
+ {"DTV6", 0x00C0, 0x8002, 0},
+ {"DTV8", 0x00C0, 0x800B, 0},
+ {"DTV7/8", 0x00C0, 0x801B, 0},
+ {"DTV7", 0x00C0, 0x8007, 0},
+ {"FM Radio-INPUT2", 0x0008, 0x9800, 10700},
+ {"FM Radio-INPUT1", 0x0008, 0x9000, 10700}
+};
+
+static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val);
+static int xc4000_tuner_reset(struct dvb_frontend *fe);
+static void xc_debug_dump(struct xc4000_priv *priv);
+
+static int xc_send_i2c_data(struct xc4000_priv *priv, u8 *buf, int len)
+{
+ struct i2c_msg msg = { .addr = priv->i2c_props.addr,
+ .flags = 0, .buf = buf, .len = len };
+ if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
+ if (priv->ignore_i2c_write_errors == 0) {
+ printk(KERN_ERR "xc4000: I2C write failed (len=%i)\n",
+ len);
+ if (len == 4) {
+ printk(KERN_ERR "bytes %02x %02x %02x %02x\n", buf[0],
+ buf[1], buf[2], buf[3]);
+ }
+ return -EREMOTEIO;
+ }
+ }
+ return 0;
+}
+
+static int xc4000_tuner_reset(struct dvb_frontend *fe)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (fe->callback) {
+ ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
+ fe->dvb->priv :
+ priv->i2c_props.adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER,
+ XC4000_TUNER_RESET, 0);
+ if (ret) {
+ printk(KERN_ERR "xc4000: reset failed\n");
+ return -EREMOTEIO;
+ }
+ } else {
+ printk(KERN_ERR "xc4000: no tuner reset callback function, "
+ "fatal\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int xc_write_reg(struct xc4000_priv *priv, u16 regAddr, u16 i2cData)
+{
+ u8 buf[4];
+ int result;
+
+ buf[0] = (regAddr >> 8) & 0xFF;
+ buf[1] = regAddr & 0xFF;
+ buf[2] = (i2cData >> 8) & 0xFF;
+ buf[3] = i2cData & 0xFF;
+ result = xc_send_i2c_data(priv, buf, 4);
+
+ return result;
+}
+
+static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+
+ int i, nbytes_to_send, result;
+ unsigned int len, pos, index;
+ u8 buf[XC_MAX_I2C_WRITE_LENGTH];
+
+ index = 0;
+ while ((i2c_sequence[index] != 0xFF) ||
+ (i2c_sequence[index + 1] != 0xFF)) {
+ len = i2c_sequence[index] * 256 + i2c_sequence[index+1];
+ if (len == 0x0000) {
+ /* RESET command */
+ /* NOTE: this is ignored, as the reset callback was */
+ /* already called by check_firmware() */
+ index += 2;
+ } else if (len & 0x8000) {
+ /* WAIT command */
+ msleep(len & 0x7FFF);
+ index += 2;
+ } else {
+ /* Send i2c data whilst ensuring individual transactions
+ * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes.
+ */
+ index += 2;
+ buf[0] = i2c_sequence[index];
+ buf[1] = i2c_sequence[index + 1];
+ pos = 2;
+ while (pos < len) {
+ if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2)
+ nbytes_to_send =
+ XC_MAX_I2C_WRITE_LENGTH;
+ else
+ nbytes_to_send = (len - pos + 2);
+ for (i = 2; i < nbytes_to_send; i++) {
+ buf[i] = i2c_sequence[index + pos +
+ i - 2];
+ }
+ result = xc_send_i2c_data(priv, buf,
+ nbytes_to_send);
+
+ if (result != 0)
+ return result;
+
+ pos += nbytes_to_send - 2;
+ }
+ index += len;
+ }
+ }
+ return 0;
+}
+
+static int xc_set_tv_standard(struct xc4000_priv *priv,
+ u16 video_mode, u16 audio_mode)
+{
+ int ret;
+ dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, video_mode, audio_mode);
+ dprintk(1, "%s() Standard = %s\n",
+ __func__,
+ xc4000_standard[priv->video_standard].Name);
+
+ /* Don't complain when the request fails because of i2c stretching */
+ priv->ignore_i2c_write_errors = 1;
+
+ ret = xc_write_reg(priv, XREG_VIDEO_MODE, video_mode);
+ if (ret == 0)
+ ret = xc_write_reg(priv, XREG_AUDIO_MODE, audio_mode);
+
+ priv->ignore_i2c_write_errors = 0;
+
+ return ret;
+}
+
+static int xc_set_signal_source(struct xc4000_priv *priv, u16 rf_mode)
+{
+ dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode,
+ rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE");
+
+ if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE)) {
+ rf_mode = XC_RF_MODE_CABLE;
+ printk(KERN_ERR
+ "%s(), Invalid mode, defaulting to CABLE",
+ __func__);
+ }
+ return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode);
+}
+
+static const struct dvb_tuner_ops xc4000_tuner_ops;
+
+static int xc_set_rf_frequency(struct xc4000_priv *priv, u32 freq_hz)
+{
+ u16 freq_code;
+
+ dprintk(1, "%s(%u)\n", __func__, freq_hz);
+
+ if ((freq_hz > xc4000_tuner_ops.info.frequency_max) ||
+ (freq_hz < xc4000_tuner_ops.info.frequency_min))
+ return -EINVAL;
+
+ freq_code = (u16)(freq_hz / 15625);
+
+ /* WAS: Starting in firmware version 1.1.44, Xceive recommends using the
+ FINERFREQ for all normal tuning (the doc indicates reg 0x03 should
+ only be used for fast scanning for channel lock) */
+ /* WAS: XREG_FINERFREQ */
+ return xc_write_reg(priv, XREG_RF_FREQ, freq_code);
+}
+
+static int xc_get_adc_envelope(struct xc4000_priv *priv, u16 *adc_envelope)
+{
+ return xc4000_readreg(priv, XREG_ADC_ENV, adc_envelope);
+}
+
+static int xc_get_frequency_error(struct xc4000_priv *priv, u32 *freq_error_hz)
+{
+ int result;
+ u16 regData;
+ u32 tmp;
+
+ result = xc4000_readreg(priv, XREG_FREQ_ERROR, &regData);
+ if (result != 0)
+ return result;
+
+ tmp = (u32)regData & 0xFFFFU;
+ tmp = (tmp < 0x8000U ? tmp : 0x10000U - tmp);
+ (*freq_error_hz) = tmp * 15625;
+ return result;
+}
+
+static int xc_get_lock_status(struct xc4000_priv *priv, u16 *lock_status)
+{
+ return xc4000_readreg(priv, XREG_LOCK, lock_status);
+}
+
+static int xc_get_version(struct xc4000_priv *priv,
+ u8 *hw_majorversion, u8 *hw_minorversion,
+ u8 *fw_majorversion, u8 *fw_minorversion)
+{
+ u16 data;
+ int result;
+
+ result = xc4000_readreg(priv, XREG_VERSION, &data);
+ if (result != 0)
+ return result;
+
+ (*hw_majorversion) = (data >> 12) & 0x0F;
+ (*hw_minorversion) = (data >> 8) & 0x0F;
+ (*fw_majorversion) = (data >> 4) & 0x0F;
+ (*fw_minorversion) = data & 0x0F;
+
+ return 0;
+}
+
+static int xc_get_hsync_freq(struct xc4000_priv *priv, u32 *hsync_freq_hz)
+{
+ u16 regData;
+ int result;
+
+ result = xc4000_readreg(priv, XREG_HSYNC_FREQ, &regData);
+ if (result != 0)
+ return result;
+
+ (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100;
+ return result;
+}
+
+static int xc_get_frame_lines(struct xc4000_priv *priv, u16 *frame_lines)
+{
+ return xc4000_readreg(priv, XREG_FRAME_LINES, frame_lines);
+}
+
+static int xc_get_quality(struct xc4000_priv *priv, u16 *quality)
+{
+ return xc4000_readreg(priv, XREG_QUALITY, quality);
+}
+
+static u16 xc_wait_for_lock(struct xc4000_priv *priv)
+{
+ u16 lock_state = 0;
+ int watchdog_count = 40;
+
+ while ((lock_state == 0) && (watchdog_count > 0)) {
+ xc_get_lock_status(priv, &lock_state);
+ if (lock_state != 1) {
+ msleep(5);
+ watchdog_count--;
+ }
+ }
+ return lock_state;
+}
+
+static int xc_tune_channel(struct xc4000_priv *priv, u32 freq_hz)
+{
+ int found = 1;
+ int result;
+
+ dprintk(1, "%s(%u)\n", __func__, freq_hz);
+
+ /* Don't complain when the request fails because of i2c stretching */
+ priv->ignore_i2c_write_errors = 1;
+ result = xc_set_rf_frequency(priv, freq_hz);
+ priv->ignore_i2c_write_errors = 0;
+
+ if (result != 0)
+ return 0;
+
+ /* wait for lock only in analog TV mode */
+ if ((priv->cur_fw.type & (FM | DTV6 | DTV7 | DTV78 | DTV8)) == 0) {
+ if (xc_wait_for_lock(priv) != 1)
+ found = 0;
+ }
+
+ /* Wait for stats to stabilize.
+ * Frame Lines needs two frame times after initial lock
+ * before it is valid.
+ */
+ msleep(debug ? 100 : 10);
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ return found;
+}
+
+static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val)
+{
+ u8 buf[2] = { reg >> 8, reg & 0xff };
+ u8 bval[2] = { 0, 0 };
+ struct i2c_msg msg[2] = {
+ { .addr = priv->i2c_props.addr,
+ .flags = 0, .buf = &buf[0], .len = 2 },
+ { .addr = priv->i2c_props.addr,
+ .flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
+ };
+
+ if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
+ printk(KERN_ERR "xc4000: I2C read failed\n");
+ return -EREMOTEIO;
+ }
+
+ *val = (bval[0] << 8) | bval[1];
+ return 0;
+}
+
+#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
+static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
+{
+ if (type & BASE)
+ printk(KERN_CONT "BASE ");
+ if (type & INIT1)
+ printk(KERN_CONT "INIT1 ");
+ if (type & F8MHZ)
+ printk(KERN_CONT "F8MHZ ");
+ if (type & MTS)
+ printk(KERN_CONT "MTS ");
+ if (type & D2620)
+ printk(KERN_CONT "D2620 ");
+ if (type & D2633)
+ printk(KERN_CONT "D2633 ");
+ if (type & DTV6)
+ printk(KERN_CONT "DTV6 ");
+ if (type & QAM)
+ printk(KERN_CONT "QAM ");
+ if (type & DTV7)
+ printk(KERN_CONT "DTV7 ");
+ if (type & DTV78)
+ printk(KERN_CONT "DTV78 ");
+ if (type & DTV8)
+ printk(KERN_CONT "DTV8 ");
+ if (type & FM)
+ printk(KERN_CONT "FM ");
+ if (type & INPUT1)
+ printk(KERN_CONT "INPUT1 ");
+ if (type & LCD)
+ printk(KERN_CONT "LCD ");
+ if (type & NOGD)
+ printk(KERN_CONT "NOGD ");
+ if (type & MONO)
+ printk(KERN_CONT "MONO ");
+ if (type & ATSC)
+ printk(KERN_CONT "ATSC ");
+ if (type & IF)
+ printk(KERN_CONT "IF ");
+ if (type & LG60)
+ printk(KERN_CONT "LG60 ");
+ if (type & ATI638)
+ printk(KERN_CONT "ATI638 ");
+ if (type & OREN538)
+ printk(KERN_CONT "OREN538 ");
+ if (type & OREN36)
+ printk(KERN_CONT "OREN36 ");
+ if (type & TOYOTA388)
+ printk(KERN_CONT "TOYOTA388 ");
+ if (type & TOYOTA794)
+ printk(KERN_CONT "TOYOTA794 ");
+ if (type & DIBCOM52)
+ printk(KERN_CONT "DIBCOM52 ");
+ if (type & ZARLINK456)
+ printk(KERN_CONT "ZARLINK456 ");
+ if (type & CHINA)
+ printk(KERN_CONT "CHINA ");
+ if (type & F6MHZ)
+ printk(KERN_CONT "F6MHZ ");
+ if (type & INPUT2)
+ printk(KERN_CONT "INPUT2 ");
+ if (type & SCODE)
+ printk(KERN_CONT "SCODE ");
+ if (type & HAS_IF)
+ printk(KERN_CONT "HAS_IF_%d ", int_freq);
+}
+
+static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int i, best_i = -1;
+ unsigned int best_nr_diffs = 255U;
+
+ if (!priv->firm) {
+ printk(KERN_ERR "Error! firmware not loaded\n");
+ return -EINVAL;
+ }
+
+ if (((type & ~SCODE) == 0) && (*id == 0))
+ *id = V4L2_STD_PAL;
+
+ /* Seek for generic video standard match */
+ for (i = 0; i < priv->firm_size; i++) {
+ v4l2_std_id id_diff_mask =
+ (priv->firm[i].id ^ (*id)) & (*id);
+ unsigned int type_diff_mask =
+ (priv->firm[i].type ^ type)
+ & (BASE_TYPES | DTV_TYPES | LCD | NOGD | MONO | SCODE);
+ unsigned int nr_diffs;
+
+ if (type_diff_mask
+ & (BASE | INIT1 | FM | DTV6 | DTV7 | DTV78 | DTV8 | SCODE))
+ continue;
+
+ nr_diffs = hweight64(id_diff_mask) + hweight32(type_diff_mask);
+ if (!nr_diffs) /* Supports all the requested standards */
+ goto found;
+
+ if (nr_diffs < best_nr_diffs) {
+ best_nr_diffs = nr_diffs;
+ best_i = i;
+ }
+ }
+
+ /* FIXME: Would make sense to seek for type "hint" match ? */
+ if (best_i < 0) {
+ i = -ENOENT;
+ goto ret;
+ }
+
+ if (best_nr_diffs > 0U) {
+ printk(KERN_WARNING
+ "Selecting best matching firmware (%u bits differ) for "
+ "type=(%x), id %016llx:\n",
+ best_nr_diffs, type, (unsigned long long)*id);
+ i = best_i;
+ }
+
+found:
+ *id = priv->firm[i].id;
+
+ret:
+ if (debug) {
+ printk(KERN_DEBUG "%s firmware for type=",
+ (i < 0) ? "Can't find" : "Found");
+ dump_firm_type(type);
+ printk(KERN_DEBUG "(%x), id %016llx.\n", type, (unsigned long long)*id);
+ }
+ return i;
+}
+
+static int load_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int pos, rc;
+ unsigned char *p;
+
+ pos = seek_firmware(fe, type, id);
+ if (pos < 0)
+ return pos;
+
+ p = priv->firm[pos].ptr;
+
+ /* Don't complain when the request fails because of i2c stretching */
+ priv->ignore_i2c_write_errors = 1;
+
+ rc = xc_load_i2c_sequence(fe, p);
+
+ priv->ignore_i2c_write_errors = 0;
+
+ return rc;
+}
+
+static int xc4000_fwupload(struct dvb_frontend *fe)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ const struct firmware *fw = NULL;
+ const unsigned char *p, *endp;
+ int rc = 0;
+ int n, n_array;
+ char name[33];
+ const char *fname;
+
+ if (firmware_name[0] != '\0')
+ fname = firmware_name;
+ else
+ fname = XC4000_DEFAULT_FIRMWARE;
+
+ dprintk(1, "Reading firmware %s\n", fname);
+ rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent);
+ if (rc < 0) {
+ if (rc == -ENOENT)
+ printk(KERN_ERR "Error: firmware %s not found.\n", fname);
+ else
+ printk(KERN_ERR "Error %d while requesting firmware %s\n",
+ rc, fname);
+
+ return rc;
+ }
+ p = fw->data;
+ endp = p + fw->size;
+
+ if (fw->size < sizeof(name) - 1 + 2 + 2) {
+ printk(KERN_ERR "Error: firmware file %s has invalid size!\n",
+ fname);
+ goto corrupt;
+ }
+
+ memcpy(name, p, sizeof(name) - 1);
+ name[sizeof(name) - 1] = '\0';
+ p += sizeof(name) - 1;
+
+ priv->firm_version = get_unaligned_le16(p);
+ p += 2;
+
+ n_array = get_unaligned_le16(p);
+ p += 2;
+
+ dprintk(1, "Loading %d firmware images from %s, type: %s, ver %d.%d\n",
+ n_array, fname, name,
+ priv->firm_version >> 8, priv->firm_version & 0xff);
+
+ priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL);
+ if (priv->firm == NULL) {
+ printk(KERN_ERR "Not enough memory to load firmware file.\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ priv->firm_size = n_array;
+
+ n = -1;
+ while (p < endp) {
+ __u32 type, size;
+ v4l2_std_id id;
+ __u16 int_freq = 0;
+
+ n++;
+ if (n >= n_array) {
+ printk(KERN_ERR "More firmware images in file than "
+ "were expected!\n");
+ goto corrupt;
+ }
+
+ /* Checks if there's enough bytes to read */
+ if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))
+ goto header;
+
+ type = get_unaligned_le32(p);
+ p += sizeof(type);
+
+ id = get_unaligned_le64(p);
+ p += sizeof(id);
+
+ if (type & HAS_IF) {
+ int_freq = get_unaligned_le16(p);
+ p += sizeof(int_freq);
+ if (endp - p < sizeof(size))
+ goto header;
+ }
+
+ size = get_unaligned_le32(p);
+ p += sizeof(size);
+
+ if (!size || size > endp - p) {
+ printk(KERN_ERR "Firmware type (%x), id %llx is corrupted (size=%d, expected %d)\n",
+ type, (unsigned long long)id,
+ (unsigned)(endp - p), size);
+ goto corrupt;
+ }
+
+ priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
+ if (priv->firm[n].ptr == NULL) {
+ printk(KERN_ERR "Not enough memory to load firmware file.\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ if (debug) {
+ printk(KERN_DEBUG "Reading firmware type ");
+ dump_firm_type_and_int_freq(type, int_freq);
+ printk(KERN_DEBUG "(%x), id %llx, size=%d.\n",
+ type, (unsigned long long)id, size);
+ }
+
+ memcpy(priv->firm[n].ptr, p, size);
+ priv->firm[n].type = type;
+ priv->firm[n].id = id;
+ priv->firm[n].size = size;
+ priv->firm[n].int_freq = int_freq;
+
+ p += size;
+ }
+
+ if (n + 1 != priv->firm_size) {
+ printk(KERN_ERR "Firmware file is incomplete!\n");
+ goto corrupt;
+ }
+
+ goto done;
+
+header:
+ printk(KERN_ERR "Firmware header is incomplete!\n");
+corrupt:
+ rc = -EINVAL;
+ printk(KERN_ERR "Error: firmware file is corrupted!\n");
+
+done:
+ release_firmware(fw);
+ if (rc == 0)
+ dprintk(1, "Firmware files loaded.\n");
+
+ return rc;
+}
+
+static int load_scode(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id, __u16 int_freq, int scode)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int pos, rc;
+ unsigned char *p;
+ u8 scode_buf[13];
+ u8 indirect_mode[5];
+
+ dprintk(1, "%s called int_freq=%d\n", __func__, int_freq);
+
+ if (!int_freq) {
+ pos = seek_firmware(fe, type, id);
+ if (pos < 0)
+ return pos;
+ } else {
+ for (pos = 0; pos < priv->firm_size; pos++) {
+ if ((priv->firm[pos].int_freq == int_freq) &&
+ (priv->firm[pos].type & HAS_IF))
+ break;
+ }
+ if (pos == priv->firm_size)
+ return -ENOENT;
+ }
+
+ p = priv->firm[pos].ptr;
+
+ if (priv->firm[pos].size != 12 * 16 || scode >= 16)
+ return -EINVAL;
+ p += 12 * scode;
+
+ if (debug) {
+ tuner_info("Loading SCODE for type=");
+ dump_firm_type_and_int_freq(priv->firm[pos].type,
+ priv->firm[pos].int_freq);
+ printk(KERN_CONT "(%x), id %016llx.\n", priv->firm[pos].type,
+ (unsigned long long)*id);
+ }
+
+ scode_buf[0] = 0x00;
+ memcpy(&scode_buf[1], p, 12);
+
+ /* Enter direct-mode */
+ rc = xc_write_reg(priv, XREG_DIRECTSITTING_MODE, 0);
+ if (rc < 0) {
+ printk(KERN_ERR "failed to put device into direct mode!\n");
+ return -EIO;
+ }
+
+ rc = xc_send_i2c_data(priv, scode_buf, 13);
+ if (rc != 0) {
+ /* Even if the send failed, make sure we set back to indirect
+ mode */
+ printk(KERN_ERR "Failed to set scode %d\n", rc);
+ }
+
+ /* Switch back to indirect-mode */
+ memset(indirect_mode, 0, sizeof(indirect_mode));
+ indirect_mode[4] = 0x88;
+ xc_send_i2c_data(priv, indirect_mode, sizeof(indirect_mode));
+ msleep(10);
+
+ return 0;
+}
+
+static int check_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id std, __u16 int_freq)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ struct firmware_properties new_fw;
+ int rc = 0, is_retry = 0;
+ u16 hwmodel;
+ v4l2_std_id std0;
+ u8 hw_major, hw_minor, fw_major, fw_minor;
+
+ dprintk(1, "%s called\n", __func__);
+
+ if (!priv->firm) {
+ rc = xc4000_fwupload(fe);
+ if (rc < 0)
+ return rc;
+ }
+
+retry:
+ new_fw.type = type;
+ new_fw.id = std;
+ new_fw.std_req = std;
+ new_fw.scode_table = SCODE;
+ new_fw.scode_nr = 0;
+ new_fw.int_freq = int_freq;
+
+ dprintk(1, "checking firmware, user requested type=");
+ if (debug) {
+ dump_firm_type(new_fw.type);
+ printk(KERN_CONT "(%x), id %016llx, ", new_fw.type,
+ (unsigned long long)new_fw.std_req);
+ if (!int_freq)
+ printk(KERN_CONT "scode_tbl ");
+ else
+ printk(KERN_CONT "int_freq %d, ", new_fw.int_freq);
+ printk(KERN_CONT "scode_nr %d\n", new_fw.scode_nr);
+ }
+
+ /* No need to reload base firmware if it matches */
+ if (priv->cur_fw.type & BASE) {
+ dprintk(1, "BASE firmware not changed.\n");
+ goto skip_base;
+ }
+
+ /* Updating BASE - forget about all currently loaded firmware */
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+
+ /* Reset is needed before loading firmware */
+ rc = xc4000_tuner_reset(fe);
+ if (rc < 0)
+ goto fail;
+
+ /* BASE firmwares are all std0 */
+ std0 = 0;
+ rc = load_firmware(fe, BASE, &std0);
+ if (rc < 0) {
+ printk(KERN_ERR "Error %d while loading base firmware\n", rc);
+ goto fail;
+ }
+
+ /* Load INIT1, if needed */
+ dprintk(1, "Load init1 firmware, if exists\n");
+
+ rc = load_firmware(fe, BASE | INIT1, &std0);
+ if (rc == -ENOENT)
+ rc = load_firmware(fe, BASE | INIT1, &std0);
+ if (rc < 0 && rc != -ENOENT) {
+ tuner_err("Error %d while loading init1 firmware\n",
+ rc);
+ goto fail;
+ }
+
+skip_base:
+ /*
+ * No need to reload standard specific firmware if base firmware
+ * was not reloaded and requested video standards have not changed.
+ */
+ if (priv->cur_fw.type == (BASE | new_fw.type) &&
+ priv->cur_fw.std_req == std) {
+ dprintk(1, "Std-specific firmware already loaded.\n");
+ goto skip_std_specific;
+ }
+
+ /* Reloading std-specific firmware forces a SCODE update */
+ priv->cur_fw.scode_table = 0;
+
+ /* Load the standard firmware */
+ rc = load_firmware(fe, new_fw.type, &new_fw.id);
+
+ if (rc < 0)
+ goto fail;
+
+skip_std_specific:
+ if (priv->cur_fw.scode_table == new_fw.scode_table &&
+ priv->cur_fw.scode_nr == new_fw.scode_nr) {
+ dprintk(1, "SCODE firmware already loaded.\n");
+ goto check_device;
+ }
+
+ /* Load SCODE firmware, if exists */
+ rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
+ new_fw.int_freq, new_fw.scode_nr);
+ if (rc != 0)
+ dprintk(1, "load scode failed %d\n", rc);
+
+check_device:
+ rc = xc4000_readreg(priv, XREG_PRODUCT_ID, &hwmodel);
+
+ if (xc_get_version(priv, &hw_major, &hw_minor, &fw_major,
+ &fw_minor) != 0) {
+ printk(KERN_ERR "Unable to read tuner registers.\n");
+ goto fail;
+ }
+
+ dprintk(1, "Device is Xceive %d version %d.%d, "
+ "firmware version %d.%d\n",
+ hwmodel, hw_major, hw_minor, fw_major, fw_minor);
+
+ /* Check firmware version against what we downloaded. */
+ if (priv->firm_version != ((fw_major << 8) | fw_minor)) {
+ printk(KERN_WARNING
+ "Incorrect readback of firmware version %d.%d.\n",
+ fw_major, fw_minor);
+ goto fail;
+ }
+
+ /* Check that the tuner hardware model remains consistent over time. */
+ if (priv->hwmodel == 0 &&
+ (hwmodel == XC_PRODUCT_ID_XC4000 ||
+ hwmodel == XC_PRODUCT_ID_XC4100)) {
+ priv->hwmodel = hwmodel;
+ priv->hwvers = (hw_major << 8) | hw_minor;
+ } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
+ priv->hwvers != ((hw_major << 8) | hw_minor)) {
+ printk(KERN_WARNING
+ "Read invalid device hardware information - tuner "
+ "hung?\n");
+ goto fail;
+ }
+
+ memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
+
+ /*
+ * By setting BASE in cur_fw.type only after successfully loading all
+ * firmwares, we can:
+ * 1. Identify that BASE firmware with type=0 has been loaded;
+ * 2. Tell whether BASE firmware was just changed the next time through.
+ */
+ priv->cur_fw.type |= BASE;
+
+ return 0;
+
+fail:
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+ if (!is_retry) {
+ msleep(50);
+ is_retry = 1;
+ dprintk(1, "Retrying firmware load\n");
+ goto retry;
+ }
+
+ if (rc == -ENOENT)
+ rc = -EINVAL;
+ return rc;
+}
+
+static void xc_debug_dump(struct xc4000_priv *priv)
+{
+ u16 adc_envelope;
+ u32 freq_error_hz = 0;
+ u16 lock_status;
+ u32 hsync_freq_hz = 0;
+ u16 frame_lines;
+ u16 quality;
+ u8 hw_majorversion = 0, hw_minorversion = 0;
+ u8 fw_majorversion = 0, fw_minorversion = 0;
+
+ xc_get_adc_envelope(priv, &adc_envelope);
+ dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);
+
+ xc_get_frequency_error(priv, &freq_error_hz);
+ dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz);
+
+ xc_get_lock_status(priv, &lock_status);
+ dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n",
+ lock_status);
+
+ xc_get_version(priv, &hw_majorversion, &hw_minorversion,
+ &fw_majorversion, &fw_minorversion);
+ dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x\n",
+ hw_majorversion, hw_minorversion,
+ fw_majorversion, fw_minorversion);
+
+ if (priv->video_standard < XC4000_DTV6) {
+ xc_get_hsync_freq(priv, &hsync_freq_hz);
+ dprintk(1, "*** Horizontal sync frequency = %d Hz\n",
+ hsync_freq_hz);
+
+ xc_get_frame_lines(priv, &frame_lines);
+ dprintk(1, "*** Frame lines = %d\n", frame_lines);
+ }
+
+ xc_get_quality(priv, &quality);
+ dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality);
+}
+
+static int xc4000_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ unsigned int type;
+ int ret = -EREMOTEIO;
+
+ dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency);
+
+ mutex_lock(&priv->lock);
+
+ if (fe->ops.info.type == FE_ATSC) {
+ dprintk(1, "%s() ATSC\n", __func__);
+ switch (params->u.vsb.modulation) {
+ case VSB_8:
+ case VSB_16:
+ dprintk(1, "%s() VSB modulation\n", __func__);
+ priv->rf_mode = XC_RF_MODE_AIR;
+ priv->freq_hz = params->frequency - 1750000;
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->video_standard = XC4000_DTV6;
+ type = DTV6;
+ break;
+ case QAM_64:
+ case QAM_256:
+ case QAM_AUTO:
+ dprintk(1, "%s() QAM modulation\n", __func__);
+ priv->rf_mode = XC_RF_MODE_CABLE;
+ priv->freq_hz = params->frequency - 1750000;
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->video_standard = XC4000_DTV6;
+ type = DTV6;
+ break;
+ default:
+ ret = -EINVAL;
+ goto fail;
+ }
+ } else if (fe->ops.info.type == FE_OFDM) {
+ dprintk(1, "%s() OFDM\n", __func__);
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->video_standard = XC4000_DTV6;
+ priv->freq_hz = params->frequency - 1750000;
+ type = DTV6;
+ break;
+ case BANDWIDTH_7_MHZ:
+ priv->bandwidth = BANDWIDTH_7_MHZ;
+ priv->video_standard = XC4000_DTV7;
+ priv->freq_hz = params->frequency - 2250000;
+ type = DTV7;
+ break;
+ case BANDWIDTH_8_MHZ:
+ priv->bandwidth = BANDWIDTH_8_MHZ;
+ priv->video_standard = XC4000_DTV8;
+ priv->freq_hz = params->frequency - 2750000;
+ type = DTV8;
+ break;
+ case BANDWIDTH_AUTO:
+ if (params->frequency < 400000000) {
+ priv->bandwidth = BANDWIDTH_7_MHZ;
+ priv->freq_hz = params->frequency - 2250000;
+ } else {
+ priv->bandwidth = BANDWIDTH_8_MHZ;
+ priv->freq_hz = params->frequency - 2750000;
+ }
+ priv->video_standard = XC4000_DTV7_8;
+ type = DTV78;
+ break;
+ default:
+ printk(KERN_ERR "xc4000 bandwidth not set!\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+ priv->rf_mode = XC_RF_MODE_AIR;
+ } else {
+ printk(KERN_ERR "xc4000 modulation type not supported!\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ dprintk(1, "%s() frequency=%d (compensated)\n",
+ __func__, priv->freq_hz);
+
+ /* Make sure the correct firmware type is loaded */
+ if (check_firmware(fe, type, 0, priv->if_khz) != 0)
+ goto fail;
+
+ ret = xc_set_signal_source(priv, priv->rf_mode);
+ if (ret != 0) {
+ printk(KERN_ERR "xc4000: xc_set_signal_source(%d) failed\n",
+ priv->rf_mode);
+ goto fail;
+ } else {
+ u16 video_mode, audio_mode;
+ video_mode = xc4000_standard[priv->video_standard].video_mode;
+ audio_mode = xc4000_standard[priv->video_standard].audio_mode;
+ if (type == DTV6 && priv->firm_version != 0x0102)
+ video_mode |= 0x0001;
+ ret = xc_set_tv_standard(priv, video_mode, audio_mode);
+ if (ret != 0) {
+ printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n");
+ /* DJH - do not return when it fails... */
+ /* goto fail; */
+ }
+ }
+
+ if (xc_write_reg(priv, XREG_D_CODE, 0) == 0)
+ ret = 0;
+ if (priv->dvb_amplitude != 0) {
+ if (xc_write_reg(priv, XREG_AMPLITUDE,
+ (priv->firm_version != 0x0102 ||
+ priv->dvb_amplitude != 134 ?
+ priv->dvb_amplitude : 132)) != 0)
+ ret = -EREMOTEIO;
+ }
+ if (priv->set_smoothedcvbs != 0) {
+ if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0)
+ ret = -EREMOTEIO;
+ }
+ if (ret != 0) {
+ printk(KERN_ERR "xc4000: setting registers failed\n");
+ /* goto fail; */
+ }
+
+ xc_tune_channel(priv, priv->freq_hz);
+
+ ret = 0;
+
+fail:
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int xc4000_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ unsigned int type = 0;
+ int ret = -EREMOTEIO;
+
+ if (params->mode == V4L2_TUNER_RADIO) {
+ dprintk(1, "%s() frequency=%d (in units of 62.5Hz)\n",
+ __func__, params->frequency);
+
+ mutex_lock(&priv->lock);
+
+ params->std = 0;
+ priv->freq_hz = params->frequency * 125L / 2;
+
+ if (audio_std & XC4000_AUDIO_STD_INPUT1) {
+ priv->video_standard = XC4000_FM_Radio_INPUT1;
+ type = FM | INPUT1;
+ } else {
+ priv->video_standard = XC4000_FM_Radio_INPUT2;
+ type = FM | INPUT2;
+ }
+
+ goto tune_channel;
+ }
+
+ dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
+ __func__, params->frequency);
+
+ mutex_lock(&priv->lock);
+
+ /* params->frequency is in units of 62.5khz */
+ priv->freq_hz = params->frequency * 62500;
+
+ params->std &= V4L2_STD_ALL;
+ /* if std is not defined, choose one */
+ if (!params->std)
+ params->std = V4L2_STD_PAL_BG;
+
+ if (audio_std & XC4000_AUDIO_STD_MONO)
+ type = MONO;
+
+ if (params->std & V4L2_STD_MN) {
+ params->std = V4L2_STD_MN;
+ if (audio_std & XC4000_AUDIO_STD_MONO) {
+ priv->video_standard = XC4000_MN_NTSC_PAL_Mono;
+ } else if (audio_std & XC4000_AUDIO_STD_A2) {
+ params->std |= V4L2_STD_A2;
+ priv->video_standard = XC4000_MN_NTSC_PAL_A2;
+ } else {
+ params->std |= V4L2_STD_BTSC;
+ priv->video_standard = XC4000_MN_NTSC_PAL_BTSC;
+ }
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_PAL_BG) {
+ params->std = V4L2_STD_PAL_BG;
+ if (audio_std & XC4000_AUDIO_STD_MONO) {
+ priv->video_standard = XC4000_BG_PAL_MONO;
+ } else if (!(audio_std & XC4000_AUDIO_STD_A2)) {
+ if (!(audio_std & XC4000_AUDIO_STD_B)) {
+ params->std |= V4L2_STD_NICAM_A;
+ priv->video_standard = XC4000_BG_PAL_NICAM;
+ } else {
+ params->std |= V4L2_STD_NICAM_B;
+ priv->video_standard = XC4000_BG_PAL_NICAM;
+ }
+ } else {
+ if (!(audio_std & XC4000_AUDIO_STD_B)) {
+ params->std |= V4L2_STD_A2_A;
+ priv->video_standard = XC4000_BG_PAL_A2;
+ } else {
+ params->std |= V4L2_STD_A2_B;
+ priv->video_standard = XC4000_BG_PAL_A2;
+ }
+ }
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_PAL_I) {
+ /* default to NICAM audio standard */
+ params->std = V4L2_STD_PAL_I | V4L2_STD_NICAM;
+ if (audio_std & XC4000_AUDIO_STD_MONO)
+ priv->video_standard = XC4000_I_PAL_NICAM_MONO;
+ else
+ priv->video_standard = XC4000_I_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_PAL_DK) {
+ params->std = V4L2_STD_PAL_DK;
+ if (audio_std & XC4000_AUDIO_STD_MONO) {
+ priv->video_standard = XC4000_DK_PAL_MONO;
+ } else if (audio_std & XC4000_AUDIO_STD_A2) {
+ params->std |= V4L2_STD_A2;
+ priv->video_standard = XC4000_DK_PAL_A2;
+ } else {
+ params->std |= V4L2_STD_NICAM;
+ priv->video_standard = XC4000_DK_PAL_NICAM;
+ }
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_SECAM_DK) {
+ /* default to A2 audio standard */
+ params->std = V4L2_STD_SECAM_DK | V4L2_STD_A2;
+ if (audio_std & XC4000_AUDIO_STD_L) {
+ type = 0;
+ priv->video_standard = XC4000_DK_SECAM_NICAM;
+ } else if (audio_std & XC4000_AUDIO_STD_MONO) {
+ priv->video_standard = XC4000_DK_SECAM_A2MONO;
+ } else if (audio_std & XC4000_AUDIO_STD_K3) {
+ params->std |= V4L2_STD_SECAM_K3;
+ priv->video_standard = XC4000_DK_SECAM_A2LDK3;
+ } else {
+ priv->video_standard = XC4000_DK_SECAM_A2DK1;
+ }
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_SECAM_L) {
+ /* default to NICAM audio standard */
+ type = 0;
+ params->std = V4L2_STD_SECAM_L | V4L2_STD_NICAM;
+ priv->video_standard = XC4000_L_SECAM_NICAM;
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_SECAM_LC) {
+ /* default to NICAM audio standard */
+ type = 0;
+ params->std = V4L2_STD_SECAM_LC | V4L2_STD_NICAM;
+ priv->video_standard = XC4000_LC_SECAM_NICAM;
+ goto tune_channel;
+ }
+
+tune_channel:
+ /* FIXME: it could be air. */
+ priv->rf_mode = XC_RF_MODE_CABLE;
+
+ if (check_firmware(fe, type, params->std,
+ xc4000_standard[priv->video_standard].int_freq) != 0)
+ goto fail;
+
+ ret = xc_set_signal_source(priv, priv->rf_mode);
+ if (ret != 0) {
+ printk(KERN_ERR
+ "xc4000: xc_set_signal_source(%d) failed\n",
+ priv->rf_mode);
+ goto fail;
+ } else {
+ u16 video_mode, audio_mode;
+ video_mode = xc4000_standard[priv->video_standard].video_mode;
+ audio_mode = xc4000_standard[priv->video_standard].audio_mode;
+ if (priv->video_standard < XC4000_BG_PAL_A2) {
+ if (type & NOGD)
+ video_mode &= 0xFF7F;
+ } else if (priv->video_standard < XC4000_I_PAL_NICAM) {
+ if (priv->firm_version == 0x0102)
+ video_mode &= 0xFEFF;
+ if (audio_std & XC4000_AUDIO_STD_B)
+ video_mode |= 0x0080;
+ }
+ ret = xc_set_tv_standard(priv, video_mode, audio_mode);
+ if (ret != 0) {
+ printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n");
+ goto fail;
+ }
+ }
+
+ if (xc_write_reg(priv, XREG_D_CODE, 0) == 0)
+ ret = 0;
+ if (xc_write_reg(priv, XREG_AMPLITUDE, 1) != 0)
+ ret = -EREMOTEIO;
+ if (priv->set_smoothedcvbs != 0) {
+ if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0)
+ ret = -EREMOTEIO;
+ }
+ if (ret != 0) {
+ printk(KERN_ERR "xc4000: setting registers failed\n");
+ goto fail;
+ }
+
+ xc_tune_channel(priv, priv->freq_hz);
+
+ ret = 0;
+
+fail:
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+
+ *freq = priv->freq_hz;
+
+ if (debug) {
+ mutex_lock(&priv->lock);
+ if ((priv->cur_fw.type
+ & (BASE | FM | DTV6 | DTV7 | DTV78 | DTV8)) == BASE) {
+ u16 snr = 0;
+ if (xc4000_readreg(priv, XREG_SNR, &snr) == 0) {
+ mutex_unlock(&priv->lock);
+ dprintk(1, "%s() freq = %u, SNR = %d\n",
+ __func__, *freq, snr);
+ return 0;
+ }
+ }
+ mutex_unlock(&priv->lock);
+ }
+
+ dprintk(1, "%s()\n", __func__);
+
+ return 0;
+}
+
+static int xc4000_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+
+ *bw = priv->bandwidth;
+ return 0;
+}
+
+static int xc4000_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ u16 lock_status = 0;
+
+ mutex_lock(&priv->lock);
+
+ if (priv->cur_fw.type & BASE)
+ xc_get_lock_status(priv, &lock_status);
+
+ *status = (lock_status == 1 ?
+ TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO : 0);
+ if (priv->cur_fw.type & (DTV6 | DTV7 | DTV78 | DTV8))
+ *status &= (~TUNER_STATUS_STEREO);
+
+ mutex_unlock(&priv->lock);
+
+ dprintk(2, "%s() lock_status = %d\n", __func__, lock_status);
+
+ return 0;
+}
+
+static int xc4000_sleep(struct dvb_frontend *fe)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int ret = 0;
+
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&priv->lock);
+
+ /* Avoid firmware reload on slow devices */
+ if ((no_poweroff == 2 ||
+ (no_poweroff == 0 && priv->default_pm != 0)) &&
+ (priv->cur_fw.type & BASE) != 0) {
+ /* force reset and firmware reload */
+ priv->cur_fw.type = XC_POWERED_DOWN;
+
+ if (xc_write_reg(priv, XREG_POWER_DOWN, 0) != 0) {
+ printk(KERN_ERR
+ "xc4000: %s() unable to shutdown tuner\n",
+ __func__);
+ ret = -EREMOTEIO;
+ }
+ msleep(20);
+ }
+
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int xc4000_init(struct dvb_frontend *fe)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ return 0;
+}
+
+static int xc4000_release(struct dvb_frontend *fe)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&xc4000_list_mutex);
+
+ if (priv)
+ hybrid_tuner_release_state(priv);
+
+ mutex_unlock(&xc4000_list_mutex);
+
+ fe->tuner_priv = NULL;
+
+ return 0;
+}
+
+static const struct dvb_tuner_ops xc4000_tuner_ops = {
+ .info = {
+ .name = "Xceive XC4000",
+ .frequency_min = 1000000,
+ .frequency_max = 1023000000,
+ .frequency_step = 50000,
+ },
+
+ .release = xc4000_release,
+ .init = xc4000_init,
+ .sleep = xc4000_sleep,
+
+ .set_params = xc4000_set_params,
+ .set_analog_params = xc4000_set_analog_params,
+ .get_frequency = xc4000_get_frequency,
+ .get_bandwidth = xc4000_get_bandwidth,
+ .get_status = xc4000_get_status
+};
+
+struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct xc4000_config *cfg)
+{
+ struct xc4000_priv *priv = NULL;
+ int instance;
+ u16 id = 0;
+
+ dprintk(1, "%s(%d-%04x)\n", __func__,
+ i2c ? i2c_adapter_id(i2c) : -1,
+ cfg ? cfg->i2c_address : -1);
+
+ mutex_lock(&xc4000_list_mutex);
+
+ instance = hybrid_tuner_request_state(struct xc4000_priv, priv,
+ hybrid_tuner_instance_list,
+ i2c, cfg->i2c_address, "xc4000");
+ switch (instance) {
+ case 0:
+ goto fail;
+ break;
+ case 1:
+ /* new tuner instance */
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ /* set default configuration */
+ priv->if_khz = 4560;
+ priv->default_pm = 0;
+ priv->dvb_amplitude = 134;
+ priv->set_smoothedcvbs = 1;
+ mutex_init(&priv->lock);
+ fe->tuner_priv = priv;
+ break;
+ default:
+ /* existing tuner instance */
+ fe->tuner_priv = priv;
+ break;
+ }
+
+ if (cfg->if_khz != 0) {
+ /* copy configuration if provided by the caller */
+ priv->if_khz = cfg->if_khz;
+ priv->default_pm = cfg->default_pm;
+ priv->dvb_amplitude = cfg->dvb_amplitude;
+ priv->set_smoothedcvbs = cfg->set_smoothedcvbs;
+ }
+
+ /* Check if firmware has been loaded. It is possible that another
+ instance of the driver has loaded the firmware.
+ */
+
+ if (instance == 1) {
+ if (xc4000_readreg(priv, XREG_PRODUCT_ID, &id) != 0)
+ goto fail;
+ } else {
+ id = ((priv->cur_fw.type & BASE) != 0 ?
+ priv->hwmodel : XC_PRODUCT_ID_FW_NOT_LOADED);
+ }
+
+ switch (id) {
+ case XC_PRODUCT_ID_XC4000:
+ case XC_PRODUCT_ID_XC4100:
+ printk(KERN_INFO
+ "xc4000: Successfully identified at address 0x%02x\n",
+ cfg->i2c_address);
+ printk(KERN_INFO
+ "xc4000: Firmware has been loaded previously\n");
+ break;
+ case XC_PRODUCT_ID_FW_NOT_LOADED:
+ printk(KERN_INFO
+ "xc4000: Successfully identified at address 0x%02x\n",
+ cfg->i2c_address);
+ printk(KERN_INFO
+ "xc4000: Firmware has not been loaded previously\n");
+ break;
+ default:
+ printk(KERN_ERR
+ "xc4000: Device not found at addr 0x%02x (0x%x)\n",
+ cfg->i2c_address, id);
+ goto fail;
+ }
+
+ mutex_unlock(&xc4000_list_mutex);
+
+ memcpy(&fe->ops.tuner_ops, &xc4000_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ if (instance == 1) {
+ int ret;
+ mutex_lock(&priv->lock);
+ ret = xc4000_fwupload(fe);
+ mutex_unlock(&priv->lock);
+ if (ret != 0)
+ goto fail2;
+ }
+
+ return fe;
+fail:
+ mutex_unlock(&xc4000_list_mutex);
+fail2:
+ xc4000_release(fe);
+ return NULL;
+}
+EXPORT_SYMBOL(xc4000_attach);
+
+MODULE_AUTHOR("Steven Toth, Davide Ferri");
+MODULE_DESCRIPTION("Xceive xc4000 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/xc4000.h b/drivers/media/common/tuners/xc4000.h
new file mode 100644
index 000000000000..e6a44d151cbd
--- /dev/null
+++ b/drivers/media/common/tuners/xc4000.h
@@ -0,0 +1,67 @@
+/*
+ * Driver for Xceive XC4000 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __XC4000_H__
+#define __XC4000_H__
+
+#include <linux/firmware.h>
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct xc4000_config {
+ u8 i2c_address;
+ /* if non-zero, power management is enabled by default */
+ u8 default_pm;
+ /* value to be written to XREG_AMPLITUDE in DVB-T mode (0: no write) */
+ u8 dvb_amplitude;
+ /* if non-zero, register 0x0E is set to filter analog TV video output */
+ u8 set_smoothedcvbs;
+ /* IF for DVB-T */
+ u32 if_khz;
+};
+
+/* xc4000 callback command */
+#define XC4000_TUNER_RESET 0
+
+/* For each bridge framework, when it attaches either analog or digital,
+ * it has to store a reference back to its _core equivalent structure,
+ * so that it can service the hardware by steering gpio's etc.
+ * Each bridge implementation is different so cast devptr accordingly.
+ * The xc4000 driver cares not for this value, other than ensuring
+ * it's passed back to a bridge during tuner_callback().
+ */
+
+#if defined(CONFIG_MEDIA_TUNER_XC4000) || (defined(CONFIG_MEDIA_TUNER_XC4000_MODULE) && defined(MODULE))
+extern struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct xc4000_config *cfg);
+#else
+static inline struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct xc4000_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index ee214c3b63d7..f6e40b3a44cc 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -80,6 +80,10 @@ comment "Supported nGene Adapters"
depends on DVB_CORE && PCI && I2C
source "drivers/media/dvb/ngene/Kconfig"
+comment "Supported ddbridge ('Octopus') Adapters"
+ depends on DVB_CORE && PCI && I2C
+ source "drivers/media/dvb/ddbridge/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 a1a08758a6f2..b2cefe637a64 100644
--- a/drivers/media/dvb/Makefile
+++ b/drivers/media/dvb/Makefile
@@ -15,6 +15,7 @@ obj-y := dvb-core/ \
dm1105/ \
pt1/ \
mantis/ \
- ngene/
+ ngene/ \
+ ddbridge/
obj-$(CONFIG_DVB_FIREDTV) += firewire/
diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile
index b97cf7208a18..3d04a8dba99e 100644
--- a/drivers/media/dvb/b2c2/Makefile
+++ b/drivers/media/dvb/b2c2/Makefile
@@ -12,5 +12,5 @@ obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o
b2c2-flexcop-usb-objs = flexcop-usb.o
obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
-EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/common/tuners/
diff --git a/drivers/media/dvb/b2c2/flexcop-common.h b/drivers/media/dvb/b2c2/flexcop-common.h
index 9e2148a19967..437912e49824 100644
--- a/drivers/media/dvb/b2c2/flexcop-common.h
+++ b/drivers/media/dvb/b2c2/flexcop-common.h
@@ -6,6 +6,7 @@
#ifndef __FLEXCOP_COMMON_H__
#define __FLEXCOP_COMMON_H__
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/mutex.h>
diff --git a/drivers/media/dvb/bt8xx/Makefile b/drivers/media/dvb/bt8xx/Makefile
index d98f1d49ffa8..0713b3af2050 100644
--- a/drivers/media/dvb/bt8xx/Makefile
+++ b/drivers/media/dvb/bt8xx/Makefile
@@ -1,6 +1,6 @@
obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += -Idrivers/media/video/bt8xx
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video/bt8xx
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
index 1e1106dcd063..521d69104982 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -892,7 +892,7 @@ static int __devinit dvb_bt8xx_probe(struct bttv_sub_device *sub)
if (!(bttv_pci_dev = bttv_get_pcidev(card->bttv_nr))) {
printk("dvb_bt8xx: no pci device for card %d\n", card->bttv_nr);
kfree(card);
- return -EFAULT;
+ return -ENODEV;
}
if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) {
@@ -902,7 +902,7 @@ static int __devinit dvb_bt8xx_probe(struct bttv_sub_device *sub)
"installed, try removing it.\n");
kfree(card);
- return -EFAULT;
+ return -ENODEV;
}
mutex_init(&card->bt->gpio_lock);
diff --git a/drivers/media/dvb/ddbridge/Kconfig b/drivers/media/dvb/ddbridge/Kconfig
new file mode 100644
index 000000000000..d099e1a12c85
--- /dev/null
+++ b/drivers/media/dvb/ddbridge/Kconfig
@@ -0,0 +1,18 @@
+config DVB_DDBRIDGE
+ tristate "Digital Devices bridge support"
+ depends on DVB_CORE && PCI && I2C
+ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+ select DVB_STV6110x if !DVB_FE_CUSTOMISE
+ select DVB_STV090x if !DVB_FE_CUSTOMISE
+ select DVB_DRXK if !DVB_FE_CUSTOMISE
+ select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
+ ---help---
+ Support for cards with the Digital Devices PCI express bridge:
+ - Octopus PCIe Bridge
+ - Octopus mini PCIe Bridge
+ - Octopus LE
+ - DuoFlex S2 Octopus
+ - DuoFlex CT Octopus
+ - cineS2(v6)
+
+ Say Y if you own such a card and want to use it.
diff --git a/drivers/media/dvb/ddbridge/Makefile b/drivers/media/dvb/ddbridge/Makefile
new file mode 100644
index 000000000000..38019bafb862
--- /dev/null
+++ b/drivers/media/dvb/ddbridge/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the ddbridge device driver
+#
+
+ddbridge-objs := ddbridge-core.o
+
+obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o
+
+ccflags-y += -Idrivers/media/dvb/dvb-core/
+ccflags-y += -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/common/tuners/
+
+# For the staging CI driver cxd2099
+ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c
new file mode 100644
index 000000000000..ba9a643b9c6a
--- /dev/null
+++ b/drivers/media/dvb/ddbridge/ddbridge-core.c
@@ -0,0 +1,1722 @@
+/*
+ * ddbridge.c: Digital Devices PCIe bridge driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/swab.h>
+#include <linux/vmalloc.h>
+#include "ddbridge.h"
+
+#include "ddbridge-regs.h"
+
+#include "tda18271c2dd.h"
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "lnbh24.h"
+#include "drxk.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* MSI had problems with lost interrupts, fixed but needs testing */
+#undef CONFIG_PCI_MSI
+
+/******************************************************************************/
+
+static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+ struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1 } };
+ return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = &reg, .len = 1 },
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1 } };
+ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+ u16 reg, u8 *val)
+{
+ u8 msg[2] = {reg>>8, reg&0xff};
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = msg, .len = 2},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1} };
+ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
+{
+ struct ddb *dev = i2c->dev;
+ int stat;
+ u32 val;
+
+ i2c->done = 0;
+ ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND);
+ stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ);
+ if (stat <= 0) {
+ printk(KERN_ERR "I2C timeout\n");
+ { /* MSI debugging*/
+ u32 istat = ddbreadl(INTERRUPT_STATUS);
+ printk(KERN_ERR "IRS %08x\n", istat);
+ ddbwritel(istat, INTERRUPT_ACK);
+ }
+ return -EIO;
+ }
+ val = ddbreadl(i2c->regs+I2C_COMMAND);
+ if (val & 0x70000)
+ return -EIO;
+ return 0;
+}
+
+static int ddb_i2c_master_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg msg[], int num)
+{
+ struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter);
+ struct ddb *dev = i2c->dev;
+ u8 addr = 0;
+
+ if (num)
+ addr = msg[0].addr;
+
+ if (num == 2 && msg[1].flags & I2C_M_RD &&
+ !(msg[0].flags & I2C_M_RD)) {
+ memcpy_toio(dev->regs + I2C_TASKMEM_BASE + i2c->wbuf,
+ msg[0].buf, msg[0].len);
+ ddbwritel(msg[0].len|(msg[1].len << 16),
+ i2c->regs+I2C_TASKLENGTH);
+ if (!ddb_i2c_cmd(i2c, addr, 1)) {
+ memcpy_fromio(msg[1].buf,
+ dev->regs + I2C_TASKMEM_BASE + i2c->rbuf,
+ msg[1].len);
+ return num;
+ }
+ }
+
+ if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+ ddbcpyto(I2C_TASKMEM_BASE + i2c->wbuf, msg[0].buf, msg[0].len);
+ ddbwritel(msg[0].len, i2c->regs + I2C_TASKLENGTH);
+ if (!ddb_i2c_cmd(i2c, addr, 2))
+ return num;
+ }
+ if (num == 1 && (msg[0].flags & I2C_M_RD)) {
+ ddbwritel(msg[0].len << 16, i2c->regs + I2C_TASKLENGTH);
+ if (!ddb_i2c_cmd(i2c, addr, 3)) {
+ ddbcpyfrom(msg[0].buf,
+ I2C_TASKMEM_BASE + i2c->rbuf, msg[0].len);
+ return num;
+ }
+ }
+ return -EIO;
+}
+
+
+static u32 ddb_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+struct i2c_algorithm ddb_i2c_algo = {
+ .master_xfer = ddb_i2c_master_xfer,
+ .functionality = ddb_i2c_functionality,
+};
+
+static void ddb_i2c_release(struct ddb *dev)
+{
+ int i;
+ struct ddb_i2c *i2c;
+ struct i2c_adapter *adap;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ i2c = &dev->i2c[i];
+ adap = &i2c->adap;
+ i2c_del_adapter(adap);
+ }
+}
+
+static int ddb_i2c_init(struct ddb *dev)
+{
+ int i, j, stat = 0;
+ struct ddb_i2c *i2c;
+ struct i2c_adapter *adap;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ i2c = &dev->i2c[i];
+ i2c->dev = dev;
+ i2c->nr = i;
+ i2c->wbuf = i * (I2C_TASKMEM_SIZE / 4);
+ i2c->rbuf = i2c->wbuf + (I2C_TASKMEM_SIZE / 8);
+ i2c->regs = 0x80 + i * 0x20;
+ ddbwritel(I2C_SPEED_100, i2c->regs + I2C_TIMING);
+ ddbwritel((i2c->rbuf << 16) | i2c->wbuf,
+ i2c->regs + I2C_TASKADDRESS);
+ init_waitqueue_head(&i2c->wq);
+
+ adap = &i2c->adap;
+ i2c_set_adapdata(adap, i2c);
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+ adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG;
+#else
+#ifdef I2C_CLASS_TV_ANALOG
+ adap->class = I2C_CLASS_TV_ANALOG;
+#endif
+#endif
+ strcpy(adap->name, "ddbridge");
+ adap->algo = &ddb_i2c_algo;
+ adap->algo_data = (void *)i2c;
+ adap->dev.parent = &dev->pdev->dev;
+ stat = i2c_add_adapter(adap);
+ if (stat)
+ break;
+ }
+ if (stat)
+ for (j = 0; j < i; j++) {
+ i2c = &dev->i2c[j];
+ adap = &i2c->adap;
+ i2c_del_adapter(adap);
+ }
+ return stat;
+}
+
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+#if 0
+static void set_table(struct ddb *dev, u32 off,
+ dma_addr_t *pbuf, u32 num)
+{
+ u32 i, base;
+ u64 mem;
+
+ base = DMA_BASE_ADDRESS_TABLE + off;
+ for (i = 0; i < num; i++) {
+ mem = pbuf[i];
+ ddbwritel(mem & 0xffffffff, base + i * 8);
+ ddbwritel(mem >> 32, base + i * 8 + 4);
+ }
+}
+#endif
+
+static void ddb_address_table(struct ddb *dev)
+{
+ u32 i, j, base;
+ u64 mem;
+ dma_addr_t *pbuf;
+
+ for (i = 0; i < dev->info->port_num * 2; i++) {
+ base = DMA_BASE_ADDRESS_TABLE + i * 0x100;
+ pbuf = dev->input[i].pbuf;
+ for (j = 0; j < dev->input[i].dma_buf_num; j++) {
+ mem = pbuf[j];
+ ddbwritel(mem & 0xffffffff, base + j * 8);
+ ddbwritel(mem >> 32, base + j * 8 + 4);
+ }
+ }
+ for (i = 0; i < dev->info->port_num; i++) {
+ base = DMA_BASE_ADDRESS_TABLE + 0x800 + i * 0x100;
+ pbuf = dev->output[i].pbuf;
+ for (j = 0; j < dev->output[i].dma_buf_num; j++) {
+ mem = pbuf[j];
+ ddbwritel(mem & 0xffffffff, base + j * 8);
+ ddbwritel(mem >> 32, base + j * 8 + 4);
+ }
+ }
+}
+
+static void io_free(struct pci_dev *pdev, u8 **vbuf,
+ dma_addr_t *pbuf, u32 size, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ if (vbuf[i]) {
+ pci_free_consistent(pdev, size, vbuf[i], pbuf[i]);
+ vbuf[i] = 0;
+ }
+ }
+}
+
+static int io_alloc(struct pci_dev *pdev, u8 **vbuf,
+ dma_addr_t *pbuf, u32 size, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ vbuf[i] = pci_alloc_consistent(pdev, size, &pbuf[i]);
+ if (!vbuf[i])
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int ddb_buffers_alloc(struct ddb *dev)
+{
+ int i;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ switch (port->class) {
+ case DDB_PORT_TUNER:
+ if (io_alloc(dev->pdev, port->input[0]->vbuf,
+ port->input[0]->pbuf,
+ port->input[0]->dma_buf_size,
+ port->input[0]->dma_buf_num) < 0)
+ return -1;
+ if (io_alloc(dev->pdev, port->input[1]->vbuf,
+ port->input[1]->pbuf,
+ port->input[1]->dma_buf_size,
+ port->input[1]->dma_buf_num) < 0)
+ return -1;
+ break;
+ case DDB_PORT_CI:
+ if (io_alloc(dev->pdev, port->input[0]->vbuf,
+ port->input[0]->pbuf,
+ port->input[0]->dma_buf_size,
+ port->input[0]->dma_buf_num) < 0)
+ return -1;
+ if (io_alloc(dev->pdev, port->output->vbuf,
+ port->output->pbuf,
+ port->output->dma_buf_size,
+ port->output->dma_buf_num) < 0)
+ return -1;
+ break;
+ default:
+ break;
+ }
+ }
+ ddb_address_table(dev);
+ return 0;
+}
+
+static void ddb_buffers_free(struct ddb *dev)
+{
+ int i;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ io_free(dev->pdev, port->input[0]->vbuf,
+ port->input[0]->pbuf,
+ port->input[0]->dma_buf_size,
+ port->input[0]->dma_buf_num);
+ io_free(dev->pdev, port->input[1]->vbuf,
+ port->input[1]->pbuf,
+ port->input[1]->dma_buf_size,
+ port->input[1]->dma_buf_num);
+ io_free(dev->pdev, port->output->vbuf,
+ port->output->pbuf,
+ port->output->dma_buf_size,
+ port->output->dma_buf_num);
+ }
+}
+
+static void ddb_input_start(struct ddb_input *input)
+{
+ struct ddb *dev = input->port->dev;
+
+ spin_lock_irq(&input->lock);
+ input->cbuf = 0;
+ input->coff = 0;
+
+ /* reset */
+ ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+ ddbwritel(2, TS_INPUT_CONTROL(input->nr));
+ ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+
+ ddbwritel((1 << 16) |
+ (input->dma_buf_num << 11) |
+ (input->dma_buf_size >> 7),
+ DMA_BUFFER_SIZE(input->nr));
+ ddbwritel(0, DMA_BUFFER_ACK(input->nr));
+
+ ddbwritel(1, DMA_BASE_WRITE);
+ ddbwritel(3, DMA_BUFFER_CONTROL(input->nr));
+ ddbwritel(9, TS_INPUT_CONTROL(input->nr));
+ input->running = 1;
+ spin_unlock_irq(&input->lock);
+}
+
+static void ddb_input_stop(struct ddb_input *input)
+{
+ struct ddb *dev = input->port->dev;
+
+ spin_lock_irq(&input->lock);
+ ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+ ddbwritel(0, DMA_BUFFER_CONTROL(input->nr));
+ input->running = 0;
+ spin_unlock_irq(&input->lock);
+}
+
+static void ddb_output_start(struct ddb_output *output)
+{
+ struct ddb *dev = output->port->dev;
+
+ spin_lock_irq(&output->lock);
+ output->cbuf = 0;
+ output->coff = 0;
+ ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+ ddbwritel(2, TS_OUTPUT_CONTROL(output->nr));
+ ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+ ddbwritel(0x3c, TS_OUTPUT_CONTROL(output->nr));
+ ddbwritel((1 << 16) |
+ (output->dma_buf_num << 11) |
+ (output->dma_buf_size >> 7),
+ DMA_BUFFER_SIZE(output->nr + 8));
+ ddbwritel(0, DMA_BUFFER_ACK(output->nr + 8));
+
+ ddbwritel(1, DMA_BASE_READ);
+ ddbwritel(3, DMA_BUFFER_CONTROL(output->nr + 8));
+ /* ddbwritel(0xbd, TS_OUTPUT_CONTROL(output->nr)); */
+ ddbwritel(0x1d, TS_OUTPUT_CONTROL(output->nr));
+ output->running = 1;
+ spin_unlock_irq(&output->lock);
+}
+
+static void ddb_output_stop(struct ddb_output *output)
+{
+ struct ddb *dev = output->port->dev;
+
+ spin_lock_irq(&output->lock);
+ ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+ ddbwritel(0, DMA_BUFFER_CONTROL(output->nr + 8));
+ output->running = 0;
+ spin_unlock_irq(&output->lock);
+}
+
+static u32 ddb_output_free(struct ddb_output *output)
+{
+ u32 idx, off, stat = output->stat;
+ s32 diff;
+
+ idx = (stat >> 11) & 0x1f;
+ off = (stat & 0x7ff) << 7;
+
+ if (output->cbuf != idx) {
+ if ((((output->cbuf + 1) % output->dma_buf_num) == idx) &&
+ (output->dma_buf_size - output->coff <= 188))
+ return 0;
+ return 188;
+ }
+ diff = off - output->coff;
+ if (diff <= 0 || diff > 188)
+ return 188;
+ return 0;
+}
+
+static ssize_t ddb_output_write(struct ddb_output *output,
+ const u8 *buf, size_t count)
+{
+ struct ddb *dev = output->port->dev;
+ u32 idx, off, stat = output->stat;
+ u32 left = count, len;
+
+ idx = (stat >> 11) & 0x1f;
+ off = (stat & 0x7ff) << 7;
+
+ while (left) {
+ len = output->dma_buf_size - output->coff;
+ if ((((output->cbuf + 1) % output->dma_buf_num) == idx) &&
+ (off == 0)) {
+ if (len <= 188)
+ break;
+ len -= 188;
+ }
+ if (output->cbuf == idx) {
+ if (off > output->coff) {
+#if 1
+ len = off - output->coff;
+ len -= (len % 188);
+ if (len <= 188)
+
+#endif
+ break;
+ len -= 188;
+ }
+ }
+ if (len > left)
+ len = left;
+ if (copy_from_user(output->vbuf[output->cbuf] + output->coff,
+ buf, len))
+ return -EIO;
+ left -= len;
+ buf += len;
+ output->coff += len;
+ if (output->coff == output->dma_buf_size) {
+ output->coff = 0;
+ output->cbuf = ((output->cbuf + 1) % output->dma_buf_num);
+ }
+ ddbwritel((output->cbuf << 11) | (output->coff >> 7),
+ DMA_BUFFER_ACK(output->nr + 8));
+ }
+ return count - left;
+}
+
+static u32 ddb_input_avail(struct ddb_input *input)
+{
+ struct ddb *dev = input->port->dev;
+ u32 idx, off, stat = input->stat;
+ u32 ctrl = ddbreadl(DMA_BUFFER_CONTROL(input->nr));
+
+ idx = (stat >> 11) & 0x1f;
+ off = (stat & 0x7ff) << 7;
+
+ if (ctrl & 4) {
+ printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl);
+ ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr));
+ return 0;
+ }
+ if (input->cbuf != idx)
+ return 188;
+ return 0;
+}
+
+static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count)
+{
+ struct ddb *dev = input->port->dev;
+ u32 left = count;
+ u32 idx, free, stat = input->stat;
+ int ret;
+
+ idx = (stat >> 11) & 0x1f;
+
+ while (left) {
+ if (input->cbuf == idx)
+ return count - left;
+ free = input->dma_buf_size - input->coff;
+ if (free > left)
+ free = left;
+ ret = copy_to_user(buf, input->vbuf[input->cbuf] +
+ input->coff, free);
+ if (ret)
+ return -EFAULT;
+ input->coff += free;
+ if (input->coff == input->dma_buf_size) {
+ input->coff = 0;
+ input->cbuf = (input->cbuf+1) % input->dma_buf_num;
+ }
+ left -= free;
+ ddbwritel((input->cbuf << 11) | (input->coff >> 7),
+ DMA_BUFFER_ACK(input->nr));
+ }
+ return count;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+#if 0
+static struct ddb_input *fe2input(struct ddb *dev, struct dvb_frontend *fe)
+{
+ int i;
+
+ for (i = 0; i < dev->info->port_num * 2; i++) {
+ if (dev->input[i].fe == fe)
+ return &dev->input[i];
+ }
+ return NULL;
+}
+#endif
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct ddb_input *input = fe->sec_priv;
+ struct ddb_port *port = input->port;
+ int status;
+
+ if (enable) {
+ mutex_lock(&port->i2c_gate_lock);
+ status = input->gate_ctrl(fe, 1);
+ } else {
+ status = input->gate_ctrl(fe, 0);
+ mutex_unlock(&port->i2c_gate_lock);
+ }
+ return status;
+}
+
+static int demod_attach_drxk(struct ddb_input *input)
+{
+ struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct dvb_frontend *fe;
+ struct drxk_config config;
+
+ memset(&config, 0, sizeof(config));
+ config.adr = 0x29 + (input->nr & 1);
+
+ fe = input->fe = dvb_attach(drxk_attach, &config, i2c, &input->fe2);
+ if (!input->fe) {
+ printk(KERN_ERR "No DRXK found!\n");
+ return -ENODEV;
+ }
+ fe->sec_priv = input;
+ input->gate_ctrl = fe->ops.i2c_gate_ctrl;
+ fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+ return 0;
+}
+
+static int tuner_attach_tda18271(struct ddb_input *input)
+{
+ struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct dvb_frontend *fe;
+
+ if (input->fe->ops.i2c_gate_ctrl)
+ input->fe->ops.i2c_gate_ctrl(input->fe, 1);
+ fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60);
+ if (!fe) {
+ printk(KERN_ERR "No TDA18271 found!\n");
+ return -ENODEV;
+ }
+ if (input->fe->ops.i2c_gate_ctrl)
+ input->fe->ops.i2c_gate_ctrl(input->fe, 0);
+ return 0;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static struct stv090x_config stv0900 = {
+ .device = STV0900,
+ .demod_mode = STV090x_DUAL,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 27000000,
+ .address = 0x69,
+
+ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+ .repeater_level = STV090x_RPTLEVEL_16,
+
+ .adc1_range = STV090x_ADC_1Vpp,
+ .adc2_range = STV090x_ADC_1Vpp,
+
+ .diseqc_envelope_mode = true,
+};
+
+static struct stv090x_config stv0900_aa = {
+ .device = STV0900,
+ .demod_mode = STV090x_DUAL,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 27000000,
+ .address = 0x68,
+
+ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+ .repeater_level = STV090x_RPTLEVEL_16,
+
+ .adc1_range = STV090x_ADC_1Vpp,
+ .adc2_range = STV090x_ADC_1Vpp,
+
+ .diseqc_envelope_mode = true,
+};
+
+static struct stv6110x_config stv6110a = {
+ .addr = 0x60,
+ .refclk = 27000000,
+ .clk_div = 1,
+};
+
+static struct stv6110x_config stv6110b = {
+ .addr = 0x63,
+ .refclk = 27000000,
+ .clk_div = 1,
+};
+
+static int demod_attach_stv0900(struct ddb_input *input, int type)
+{
+ struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
+
+ input->fe = dvb_attach(stv090x_attach, feconf, i2c,
+ (input->nr & 1) ? STV090x_DEMODULATOR_1
+ : STV090x_DEMODULATOR_0);
+ if (!input->fe) {
+ printk(KERN_ERR "No STV0900 found!\n");
+ return -ENODEV;
+ }
+ if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0,
+ 0, (input->nr & 1) ?
+ (0x09 - type) : (0x0b - type))) {
+ printk(KERN_ERR "No LNBH24 found!\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int tuner_attach_stv6110(struct ddb_input *input, int type)
+{
+ struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
+ struct stv6110x_config *tunerconf = (input->nr & 1) ?
+ &stv6110b : &stv6110a;
+ struct stv6110x_devctl *ctl;
+
+ ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c);
+ if (!ctl) {
+ printk(KERN_ERR "No STV6110X found!\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "attach tuner input %d adr %02x\n",
+ input->nr, tunerconf->addr);
+
+ feconf->tuner_init = ctl->tuner_init;
+ feconf->tuner_sleep = ctl->tuner_sleep;
+ feconf->tuner_set_mode = ctl->tuner_set_mode;
+ feconf->tuner_set_frequency = ctl->tuner_set_frequency;
+ feconf->tuner_get_frequency = ctl->tuner_get_frequency;
+ feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+ feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+ feconf->tuner_set_bbgain = ctl->tuner_set_bbgain;
+ feconf->tuner_get_bbgain = ctl->tuner_get_bbgain;
+ feconf->tuner_set_refclk = ctl->tuner_set_refclk;
+ feconf->tuner_get_status = ctl->tuner_get_status;
+
+ return 0;
+}
+
+static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
+ int (*start_feed)(struct dvb_demux_feed *),
+ int (*stop_feed)(struct dvb_demux_feed *),
+ void *priv)
+{
+ dvbdemux->priv = priv;
+
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = start_feed;
+ dvbdemux->stop_feed = stop_feed;
+ dvbdemux->write_to_decoder = NULL;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING);
+ return dvb_dmx_init(dvbdemux);
+}
+
+static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
+ struct dvb_demux *dvbdemux,
+ struct dmx_frontend *hw_frontend,
+ struct dmx_frontend *mem_frontend,
+ struct dvb_adapter *dvb_adapter)
+{
+ int ret;
+
+ dmxdev->filternum = 256;
+ dmxdev->demux = &dvbdemux->dmx;
+ dmxdev->capabilities = 0;
+ ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
+ if (ret < 0)
+ return ret;
+
+ hw_frontend->source = DMX_FRONTEND_0;
+ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
+ mem_frontend->source = DMX_MEMORY_FE;
+ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
+ return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
+}
+
+static int start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct ddb_input *input = dvbdmx->priv;
+
+ if (!input->users)
+ ddb_input_start(input);
+
+ return ++input->users;
+}
+
+static int stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct ddb_input *input = dvbdmx->priv;
+
+ if (--input->users)
+ return input->users;
+
+ ddb_input_stop(input);
+ return 0;
+}
+
+
+static void dvb_input_detach(struct ddb_input *input)
+{
+ struct dvb_adapter *adap = &input->adap;
+ struct dvb_demux *dvbdemux = &input->demux;
+
+ switch (input->attached) {
+ case 5:
+ if (input->fe2)
+ dvb_unregister_frontend(input->fe2);
+ if (input->fe) {
+ dvb_unregister_frontend(input->fe);
+ dvb_frontend_detach(input->fe);
+ input->fe = NULL;
+ }
+ case 4:
+ dvb_net_release(&input->dvbnet);
+
+ case 3:
+ dvbdemux->dmx.close(&dvbdemux->dmx);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+ &input->hw_frontend);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+ &input->mem_frontend);
+ dvb_dmxdev_release(&input->dmxdev);
+
+ case 2:
+ dvb_dmx_release(&input->demux);
+
+ case 1:
+ dvb_unregister_adapter(adap);
+ }
+ input->attached = 0;
+}
+
+static int dvb_input_attach(struct ddb_input *input)
+{
+ int ret;
+ struct ddb_port *port = input->port;
+ struct dvb_adapter *adap = &input->adap;
+ struct dvb_demux *dvbdemux = &input->demux;
+
+ ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE,
+ &input->port->dev->pdev->dev,
+ adapter_nr);
+ if (ret < 0) {
+ printk(KERN_ERR "ddbridge: Could not register adapter."
+ "Check if you enabled enough adapters in dvb-core!\n");
+ return ret;
+ }
+ input->attached = 1;
+
+ ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
+ start_feed,
+ stop_feed, input);
+ if (ret < 0)
+ return ret;
+ input->attached = 2;
+
+ ret = my_dvb_dmxdev_ts_card_init(&input->dmxdev, &input->demux,
+ &input->hw_frontend,
+ &input->mem_frontend, adap);
+ if (ret < 0)
+ return ret;
+ input->attached = 3;
+
+ ret = dvb_net_init(adap, &input->dvbnet, input->dmxdev.demux);
+ if (ret < 0)
+ return ret;
+ input->attached = 4;
+
+ input->fe = 0;
+ switch (port->type) {
+ case DDB_TUNER_DVBS_ST:
+ if (demod_attach_stv0900(input, 0) < 0)
+ return -ENODEV;
+ if (tuner_attach_stv6110(input, 0) < 0)
+ return -ENODEV;
+ if (input->fe) {
+ if (dvb_register_frontend(adap, input->fe) < 0)
+ return -ENODEV;
+ }
+ break;
+ case DDB_TUNER_DVBS_ST_AA:
+ if (demod_attach_stv0900(input, 1) < 0)
+ return -ENODEV;
+ if (tuner_attach_stv6110(input, 1) < 0)
+ return -ENODEV;
+ if (input->fe) {
+ if (dvb_register_frontend(adap, input->fe) < 0)
+ return -ENODEV;
+ }
+ break;
+ case DDB_TUNER_DVBCT_TR:
+ if (demod_attach_drxk(input) < 0)
+ return -ENODEV;
+ if (tuner_attach_tda18271(input) < 0)
+ return -ENODEV;
+ if (input->fe) {
+ if (dvb_register_frontend(adap, input->fe) < 0)
+ return -ENODEV;
+ }
+ if (input->fe2) {
+ if (dvb_register_frontend(adap, input->fe2) < 0)
+ return -ENODEV;
+ input->fe2->tuner_priv = input->fe->tuner_priv;
+ memcpy(&input->fe2->ops.tuner_ops,
+ &input->fe->ops.tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ }
+ break;
+ }
+ input->attached = 5;
+ return 0;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+
+static ssize_t ts_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct ddb_output *output = dvbdev->priv;
+ size_t left = count;
+ int stat;
+
+ while (left) {
+ if (ddb_output_free(output) < 188) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ if (wait_event_interruptible(
+ output->wq, ddb_output_free(output) >= 188) < 0)
+ break;
+ }
+ stat = ddb_output_write(output, buf, left);
+ if (stat < 0)
+ break;
+ buf += stat;
+ left -= stat;
+ }
+ return (left == count) ? -EAGAIN : (count - left);
+}
+
+static ssize_t ts_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct ddb_output *output = dvbdev->priv;
+ struct ddb_input *input = output->port->input[0];
+ int left, read;
+
+ count -= count % 188;
+ left = count;
+ while (left) {
+ if (ddb_input_avail(input) < 188) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ if (wait_event_interruptible(
+ input->wq, ddb_input_avail(input) >= 188) < 0)
+ break;
+ }
+ read = ddb_input_read(input, buf, left);
+ if (read < 0)
+ return read;
+ left -= read;
+ buf += read;
+ }
+ return (left == count) ? -EAGAIN : (count - left);
+}
+
+static unsigned int ts_poll(struct file *file, poll_table *wait)
+{
+ /*
+ struct dvb_device *dvbdev = file->private_data;
+ struct ddb_output *output = dvbdev->priv;
+ struct ddb_input *input = output->port->input[0];
+ */
+ unsigned int mask = 0;
+
+#if 0
+ if (data_avail_to_read)
+ mask |= POLLIN | POLLRDNORM;
+ if (data_avail_to_write)
+ mask |= POLLOUT | POLLWRNORM;
+
+ poll_wait(file, &read_queue, wait);
+ poll_wait(file, &write_queue, wait);
+#endif
+ return mask;
+}
+
+static const struct file_operations ci_fops = {
+ .owner = THIS_MODULE,
+ .read = ts_read,
+ .write = ts_write,
+ .open = dvb_generic_open,
+ .release = dvb_generic_release,
+ .poll = ts_poll,
+ .mmap = 0,
+};
+
+static struct dvb_device dvbdev_ci = {
+ .priv = 0,
+ .readers = -1,
+ .writers = -1,
+ .users = -1,
+ .fops = &ci_fops,
+};
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void input_tasklet(unsigned long data)
+{
+ struct ddb_input *input = (struct ddb_input *) data;
+ struct ddb *dev = input->port->dev;
+
+ spin_lock(&input->lock);
+ if (!input->running) {
+ spin_unlock(&input->lock);
+ return;
+ }
+ input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr));
+
+ if (input->port->class == DDB_PORT_TUNER) {
+ if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))
+ printk(KERN_ERR "Overflow input %d\n", input->nr);
+ while (input->cbuf != ((input->stat >> 11) & 0x1f)
+ || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) {
+ dvb_dmx_swfilter_packets(&input->demux,
+ input->vbuf[input->cbuf],
+ input->dma_buf_size / 188);
+
+ input->cbuf = (input->cbuf + 1) % input->dma_buf_num;
+ ddbwritel((input->cbuf << 11),
+ DMA_BUFFER_ACK(input->nr));
+ input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr));
+ }
+ }
+ if (input->port->class == DDB_PORT_CI)
+ wake_up(&input->wq);
+ spin_unlock(&input->lock);
+}
+
+static void output_tasklet(unsigned long data)
+{
+ struct ddb_output *output = (struct ddb_output *) data;
+ struct ddb *dev = output->port->dev;
+
+ spin_lock(&output->lock);
+ if (!output->running) {
+ spin_unlock(&output->lock);
+ return;
+ }
+ output->stat = ddbreadl(DMA_BUFFER_CURRENT(output->nr + 8));
+ wake_up(&output->wq);
+ spin_unlock(&output->lock);
+}
+
+
+struct cxd2099_cfg cxd_cfg = {
+ .bitrate = 62000,
+ .adr = 0x40,
+ .polarity = 1,
+ .clock_mode = 1,
+};
+
+static int ddb_ci_attach(struct ddb_port *port)
+{
+ int ret;
+
+ ret = dvb_register_adapter(&port->output->adap,
+ "DDBridge",
+ THIS_MODULE,
+ &port->dev->pdev->dev,
+ adapter_nr);
+ if (ret < 0)
+ return ret;
+ port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap);
+ if (!port->en) {
+ dvb_unregister_adapter(&port->output->adap);
+ return -ENODEV;
+ }
+ ddb_input_start(port->input[0]);
+ ddb_output_start(port->output);
+ dvb_ca_en50221_init(&port->output->adap,
+ port->en, 0, 1);
+ ret = dvb_register_device(&port->output->adap, &port->output->dev,
+ &dvbdev_ci, (void *) port->output,
+ DVB_DEVICE_SEC);
+ return ret;
+}
+
+static int ddb_port_attach(struct ddb_port *port)
+{
+ int ret = 0;
+
+ switch (port->class) {
+ case DDB_PORT_TUNER:
+ ret = dvb_input_attach(port->input[0]);
+ if (ret < 0)
+ break;
+ ret = dvb_input_attach(port->input[1]);
+ break;
+ case DDB_PORT_CI:
+ ret = ddb_ci_attach(port);
+ break;
+ default:
+ break;
+ }
+ if (ret < 0)
+ printk(KERN_ERR "port_attach on port %d failed\n", port->nr);
+ return ret;
+}
+
+static int ddb_ports_attach(struct ddb *dev)
+{
+ int i, ret = 0;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ ret = ddb_port_attach(port);
+ if (ret < 0)
+ break;
+ }
+ return ret;
+}
+
+static void ddb_ports_detach(struct ddb *dev)
+{
+ int i;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ switch (port->class) {
+ case DDB_PORT_TUNER:
+ dvb_input_detach(port->input[0]);
+ dvb_input_detach(port->input[1]);
+ break;
+ case DDB_PORT_CI:
+ if (port->output->dev)
+ dvb_unregister_device(port->output->dev);
+ if (port->en) {
+ ddb_input_stop(port->input[0]);
+ ddb_output_stop(port->output);
+ dvb_ca_en50221_release(port->en);
+ kfree(port->en);
+ port->en = 0;
+ dvb_unregister_adapter(&port->output->adap);
+ }
+ break;
+ }
+ }
+}
+
+/****************************************************************************/
+/****************************************************************************/
+
+static int port_has_ci(struct ddb_port *port)
+{
+ u8 val;
+ return i2c_read_reg(&port->i2c->adap, 0x40, 0, &val) ? 0 : 1;
+}
+
+static int port_has_stv0900(struct ddb_port *port)
+{
+ u8 val;
+ if (i2c_read_reg16(&port->i2c->adap, 0x69, 0xf100, &val) < 0)
+ return 0;
+ return 1;
+}
+
+static int port_has_stv0900_aa(struct ddb_port *port)
+{
+ u8 val;
+ if (i2c_read_reg16(&port->i2c->adap, 0x68, 0xf100, &val) < 0)
+ return 0;
+ return 1;
+}
+
+static int port_has_drxks(struct ddb_port *port)
+{
+ u8 val;
+ if (i2c_read(&port->i2c->adap, 0x29, &val) < 0)
+ return 0;
+ if (i2c_read(&port->i2c->adap, 0x2a, &val) < 0)
+ return 0;
+ return 1;
+}
+
+static void ddb_port_probe(struct ddb_port *port)
+{
+ struct ddb *dev = port->dev;
+ char *modname = "NO MODULE";
+
+ port->class = DDB_PORT_NONE;
+
+ if (port_has_ci(port)) {
+ modname = "CI";
+ port->class = DDB_PORT_CI;
+ ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+ } else if (port_has_stv0900(port)) {
+ modname = "DUAL DVB-S2";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DVBS_ST;
+ ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
+ } else if (port_has_stv0900_aa(port)) {
+ modname = "DUAL DVB-S2";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DVBS_ST_AA;
+ ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
+ } else if (port_has_drxks(port)) {
+ modname = "DUAL DVB-C/T";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DVBCT_TR;
+ ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+ }
+ printk(KERN_INFO "Port %d (TAB %d): %s\n",
+ port->nr, port->nr+1, modname);
+}
+
+static void ddb_input_init(struct ddb_port *port, int nr)
+{
+ struct ddb *dev = port->dev;
+ struct ddb_input *input = &dev->input[nr];
+
+ input->nr = nr;
+ input->port = port;
+ input->dma_buf_num = INPUT_DMA_BUFS;
+ input->dma_buf_size = INPUT_DMA_SIZE;
+ ddbwritel(0, TS_INPUT_CONTROL(nr));
+ ddbwritel(2, TS_INPUT_CONTROL(nr));
+ ddbwritel(0, TS_INPUT_CONTROL(nr));
+ ddbwritel(0, DMA_BUFFER_ACK(nr));
+ tasklet_init(&input->tasklet, input_tasklet, (unsigned long) input);
+ spin_lock_init(&input->lock);
+ init_waitqueue_head(&input->wq);
+}
+
+static void ddb_output_init(struct ddb_port *port, int nr)
+{
+ struct ddb *dev = port->dev;
+ struct ddb_output *output = &dev->output[nr];
+ output->nr = nr;
+ output->port = port;
+ output->dma_buf_num = OUTPUT_DMA_BUFS;
+ output->dma_buf_size = OUTPUT_DMA_SIZE;
+
+ ddbwritel(0, TS_OUTPUT_CONTROL(nr));
+ ddbwritel(2, TS_OUTPUT_CONTROL(nr));
+ ddbwritel(0, TS_OUTPUT_CONTROL(nr));
+ tasklet_init(&output->tasklet, output_tasklet, (unsigned long) output);
+ init_waitqueue_head(&output->wq);
+}
+
+static void ddb_ports_init(struct ddb *dev)
+{
+ int i;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ port->dev = dev;
+ port->nr = i;
+ port->i2c = &dev->i2c[i];
+ port->input[0] = &dev->input[2 * i];
+ port->input[1] = &dev->input[2 * i + 1];
+ port->output = &dev->output[i];
+
+ mutex_init(&port->i2c_gate_lock);
+ ddb_port_probe(port);
+ ddb_input_init(port, 2 * i);
+ ddb_input_init(port, 2 * i + 1);
+ ddb_output_init(port, i);
+ }
+}
+
+static void ddb_ports_release(struct ddb *dev)
+{
+ int i;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ port->dev = dev;
+ tasklet_kill(&port->input[0]->tasklet);
+ tasklet_kill(&port->input[1]->tasklet);
+ tasklet_kill(&port->output->tasklet);
+ }
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void irq_handle_i2c(struct ddb *dev, int n)
+{
+ struct ddb_i2c *i2c = &dev->i2c[n];
+
+ i2c->done = 1;
+ wake_up(&i2c->wq);
+}
+
+static irqreturn_t irq_handler(int irq, void *dev_id)
+{
+ struct ddb *dev = (struct ddb *) dev_id;
+ u32 s = ddbreadl(INTERRUPT_STATUS);
+
+ if (!s)
+ return IRQ_NONE;
+
+ do {
+ ddbwritel(s, INTERRUPT_ACK);
+
+ if (s & 0x00000001)
+ irq_handle_i2c(dev, 0);
+ if (s & 0x00000002)
+ irq_handle_i2c(dev, 1);
+ if (s & 0x00000004)
+ irq_handle_i2c(dev, 2);
+ if (s & 0x00000008)
+ irq_handle_i2c(dev, 3);
+
+ if (s & 0x00000100)
+ tasklet_schedule(&dev->input[0].tasklet);
+ if (s & 0x00000200)
+ tasklet_schedule(&dev->input[1].tasklet);
+ if (s & 0x00000400)
+ tasklet_schedule(&dev->input[2].tasklet);
+ if (s & 0x00000800)
+ tasklet_schedule(&dev->input[3].tasklet);
+ if (s & 0x00001000)
+ tasklet_schedule(&dev->input[4].tasklet);
+ if (s & 0x00002000)
+ tasklet_schedule(&dev->input[5].tasklet);
+ if (s & 0x00004000)
+ tasklet_schedule(&dev->input[6].tasklet);
+ if (s & 0x00008000)
+ tasklet_schedule(&dev->input[7].tasklet);
+
+ if (s & 0x00010000)
+ tasklet_schedule(&dev->output[0].tasklet);
+ if (s & 0x00020000)
+ tasklet_schedule(&dev->output[1].tasklet);
+ if (s & 0x00040000)
+ tasklet_schedule(&dev->output[2].tasklet);
+ if (s & 0x00080000)
+ tasklet_schedule(&dev->output[3].tasklet);
+
+ /* if (s & 0x000f0000) printk(KERN_DEBUG "%08x\n", istat); */
+ } while ((s = ddbreadl(INTERRUPT_STATUS)));
+
+ return IRQ_HANDLED;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
+{
+ u32 data, shift;
+
+ if (wlen > 4)
+ ddbwritel(1, SPI_CONTROL);
+ while (wlen > 4) {
+ /* FIXME: check for big-endian */
+ data = swab32(*(u32 *)wbuf);
+ wbuf += 4;
+ wlen -= 4;
+ ddbwritel(data, SPI_DATA);
+ while (ddbreadl(SPI_CONTROL) & 0x0004)
+ ;
+ }
+
+ if (rlen)
+ ddbwritel(0x0001 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL);
+ else
+ ddbwritel(0x0003 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL);
+
+ data = 0;
+ shift = ((4 - wlen) * 8);
+ while (wlen) {
+ data <<= 8;
+ data |= *wbuf;
+ wlen--;
+ wbuf++;
+ }
+ if (shift)
+ data <<= shift;
+ ddbwritel(data, SPI_DATA);
+ while (ddbreadl(SPI_CONTROL) & 0x0004)
+ ;
+
+ if (!rlen) {
+ ddbwritel(0, SPI_CONTROL);
+ return 0;
+ }
+ if (rlen > 4)
+ ddbwritel(1, SPI_CONTROL);
+
+ while (rlen > 4) {
+ ddbwritel(0xffffffff, SPI_DATA);
+ while (ddbreadl(SPI_CONTROL) & 0x0004)
+ ;
+ data = ddbreadl(SPI_DATA);
+ *(u32 *) rbuf = swab32(data);
+ rbuf += 4;
+ rlen -= 4;
+ }
+ ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL);
+ ddbwritel(0xffffffff, SPI_DATA);
+ while (ddbreadl(SPI_CONTROL) & 0x0004)
+ ;
+
+ data = ddbreadl(SPI_DATA);
+ ddbwritel(0, SPI_CONTROL);
+
+ if (rlen < 4)
+ data <<= ((4 - rlen) * 8);
+
+ while (rlen > 0) {
+ *rbuf = ((data >> 24) & 0xff);
+ data <<= 8;
+ rbuf++;
+ rlen--;
+ }
+ return 0;
+}
+
+#define DDB_MAGIC 'd'
+
+struct ddb_flashio {
+ __u8 *write_buf;
+ __u32 write_len;
+ __u8 *read_buf;
+ __u32 read_len;
+};
+
+#define IOCTL_DDB_FLASHIO _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio)
+
+#define DDB_NAME "ddbridge"
+
+static u32 ddb_num;
+static struct ddb *ddbs[32];
+static struct class *ddb_class;
+static int ddb_major;
+
+static int ddb_open(struct inode *inode, struct file *file)
+{
+ struct ddb *dev = ddbs[iminor(inode)];
+
+ file->private_data = dev;
+ return 0;
+}
+
+static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct ddb *dev = file->private_data;
+ void *parg = (void *)arg;
+ int res;
+
+ switch (cmd) {
+ case IOCTL_DDB_FLASHIO:
+ {
+ struct ddb_flashio fio;
+ u8 *rbuf, *wbuf;
+
+ if (copy_from_user(&fio, parg, sizeof(fio)))
+ return -EFAULT;
+
+ if (fio.write_len > 1028 || fio.read_len > 1028)
+ return -EINVAL;
+ if (fio.write_len + fio.read_len > 1028)
+ return -EINVAL;
+
+ wbuf = &dev->iobuf[0];
+ rbuf = wbuf + fio.write_len;
+
+ if (copy_from_user(wbuf, fio.write_buf, fio.write_len))
+ return -EFAULT;
+ res = flashio(dev, wbuf, fio.write_len, rbuf, fio.read_len);
+ if (res)
+ return res;
+ if (copy_to_user(fio.read_buf, rbuf, fio.read_len))
+ return -EFAULT;
+ break;
+ }
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static const struct file_operations ddb_fops = {
+ .unlocked_ioctl = ddb_ioctl,
+ .open = ddb_open,
+};
+
+static char *ddb_devnode(struct device *device, mode_t *mode)
+{
+ struct ddb *dev = dev_get_drvdata(device);
+
+ return kasprintf(GFP_KERNEL, "ddbridge/card%d", dev->nr);
+}
+
+static int ddb_class_create(void)
+{
+ ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops);
+ if (ddb_major < 0)
+ return ddb_major;
+
+ ddb_class = class_create(THIS_MODULE, DDB_NAME);
+ if (IS_ERR(ddb_class)) {
+ unregister_chrdev(ddb_major, DDB_NAME);
+ return -1;
+ }
+ ddb_class->devnode = ddb_devnode;
+ return 0;
+}
+
+static void ddb_class_destroy(void)
+{
+ class_destroy(ddb_class);
+ unregister_chrdev(ddb_major, DDB_NAME);
+}
+
+static int ddb_device_create(struct ddb *dev)
+{
+ dev->nr = ddb_num++;
+ dev->ddb_dev = device_create(ddb_class, NULL,
+ MKDEV(ddb_major, dev->nr),
+ dev, "ddbridge%d", dev->nr);
+ ddbs[dev->nr] = dev;
+ if (IS_ERR(dev->ddb_dev))
+ return -1;
+ return 0;
+}
+
+static void ddb_device_destroy(struct ddb *dev)
+{
+ ddb_num--;
+ if (IS_ERR(dev->ddb_dev))
+ return;
+ device_destroy(ddb_class, MKDEV(ddb_major, 0));
+}
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void ddb_unmap(struct ddb *dev)
+{
+ if (dev->regs)
+ iounmap(dev->regs);
+ vfree(dev);
+}
+
+
+static void __devexit ddb_remove(struct pci_dev *pdev)
+{
+ struct ddb *dev = (struct ddb *) pci_get_drvdata(pdev);
+
+ ddb_ports_detach(dev);
+ ddb_i2c_release(dev);
+
+ ddbwritel(0, INTERRUPT_ENABLE);
+ free_irq(dev->pdev->irq, dev);
+#ifdef CONFIG_PCI_MSI
+ if (dev->msi)
+ pci_disable_msi(dev->pdev);
+#endif
+ ddb_ports_release(dev);
+ ddb_buffers_free(dev);
+ ddb_device_destroy(dev);
+
+ ddb_unmap(dev);
+ pci_set_drvdata(pdev, 0);
+ pci_disable_device(pdev);
+}
+
+
+static int __devinit ddb_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct ddb *dev;
+ int stat = 0;
+ int irq_flag = IRQF_SHARED;
+
+ if (pci_enable_device(pdev) < 0)
+ return -ENODEV;
+
+ dev = vmalloc(sizeof(struct ddb));
+ if (dev == NULL)
+ return -ENOMEM;
+ memset(dev, 0, sizeof(struct ddb));
+
+ dev->pdev = pdev;
+ pci_set_drvdata(pdev, dev);
+ dev->info = (struct ddb_info *) id->driver_data;
+ printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name);
+
+ dev->regs = ioremap(pci_resource_start(dev->pdev, 0),
+ pci_resource_len(dev->pdev, 0));
+ if (!dev->regs) {
+ stat = -ENOMEM;
+ goto fail;
+ }
+ printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4));
+
+#ifdef CONFIG_PCI_MSI
+ if (pci_msi_enabled())
+ stat = pci_enable_msi(dev->pdev);
+ if (stat) {
+ printk(KERN_INFO ": MSI not available.\n");
+ } else {
+ irq_flag = 0;
+ dev->msi = 1;
+ }
+#endif
+ stat = request_irq(dev->pdev->irq, irq_handler,
+ irq_flag, "DDBridge", (void *) dev);
+ if (stat < 0)
+ goto fail1;
+ ddbwritel(0, DMA_BASE_WRITE);
+ ddbwritel(0, DMA_BASE_READ);
+ ddbwritel(0xffffffff, INTERRUPT_ACK);
+ ddbwritel(0xfff0f, INTERRUPT_ENABLE);
+ ddbwritel(0, MSI1_ENABLE);
+
+ if (ddb_i2c_init(dev) < 0)
+ goto fail1;
+ ddb_ports_init(dev);
+ if (ddb_buffers_alloc(dev) < 0) {
+ printk(KERN_INFO ": Could not allocate buffer memory\n");
+ goto fail2;
+ }
+ if (ddb_ports_attach(dev) < 0)
+ goto fail3;
+ ddb_device_create(dev);
+ return 0;
+
+fail3:
+ ddb_ports_detach(dev);
+ printk(KERN_ERR "fail3\n");
+ ddb_ports_release(dev);
+fail2:
+ printk(KERN_ERR "fail2\n");
+ ddb_buffers_free(dev);
+fail1:
+ printk(KERN_ERR "fail1\n");
+ if (dev->msi)
+ pci_disable_msi(dev->pdev);
+ free_irq(dev->pdev->irq, dev);
+fail:
+ printk(KERN_ERR "fail\n");
+ ddb_unmap(dev);
+ pci_set_drvdata(pdev, 0);
+ pci_disable_device(pdev);
+ return -1;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static struct ddb_info ddb_none = {
+ .type = DDB_NONE,
+ .name = "Digital Devices PCIe bridge",
+};
+
+static struct ddb_info ddb_octopus = {
+ .type = DDB_OCTOPUS,
+ .name = "Digital Devices Octopus DVB adapter",
+ .port_num = 4,
+};
+
+static struct ddb_info ddb_octopus_le = {
+ .type = DDB_OCTOPUS,
+ .name = "Digital Devices Octopus LE DVB adapter",
+ .port_num = 2,
+};
+
+static struct ddb_info ddb_v6 = {
+ .type = DDB_OCTOPUS,
+ .name = "Digital Devices Cine S2 V6 DVB adapter",
+ .port_num = 3,
+};
+
+#define DDVID 0xdd01 /* Digital Devices Vendor ID */
+
+#define DDB_ID(_vend, _dev, _subvend, _subdev, _driverdata) { \
+ .vendor = _vend, .device = _dev, \
+ .subvendor = _subvend, .subdevice = _subdev, \
+ .driver_data = (unsigned long)&_driverdata }
+
+static const struct pci_device_id ddb_id_tbl[] __devinitdata = {
+ DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus),
+ DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus),
+ DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le),
+ DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus),
+ DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6),
+ /* in case sub-ids got deleted in flash */
+ DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, ddb_id_tbl);
+
+
+static struct pci_driver ddb_pci_driver = {
+ .name = "DDBridge",
+ .id_table = ddb_id_tbl,
+ .probe = ddb_probe,
+ .remove = ddb_remove,
+};
+
+static __init int module_init_ddbridge(void)
+{
+ printk(KERN_INFO "Digital Devices PCIE bridge driver, "
+ "Copyright (C) 2010-11 Digital Devices GmbH\n");
+ if (ddb_class_create())
+ return -1;
+ return pci_register_driver(&ddb_pci_driver);
+}
+
+static __exit void module_exit_ddbridge(void)
+{
+ pci_unregister_driver(&ddb_pci_driver);
+ ddb_class_destroy();
+}
+
+module_init(module_init_ddbridge);
+module_exit(module_exit_ddbridge);
+
+MODULE_DESCRIPTION("Digital Devices PCIe Bridge");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.5");
diff --git a/drivers/media/dvb/ddbridge/ddbridge-regs.h b/drivers/media/dvb/ddbridge/ddbridge-regs.h
new file mode 100644
index 000000000000..a3ccb318b500
--- /dev/null
+++ b/drivers/media/dvb/ddbridge/ddbridge-regs.h
@@ -0,0 +1,151 @@
+/*
+ * ddbridge-regs.h: Digital Devices PCIe bridge driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* DD-DVBBridgeV1.h 273 2010-09-17 05:03:16Z manfred */
+
+/* Register Definitions */
+
+#define CUR_REGISTERMAP_VERSION 0x10000
+
+#define HARDWARE_VERSION 0x00
+#define REGISTERMAP_VERSION 0x04
+
+/* ------------------------------------------------------------------------- */
+/* SPI Controller */
+
+#define SPI_CONTROL 0x10
+#define SPI_DATA 0x14
+
+/* ------------------------------------------------------------------------- */
+
+/* Interrupt controller */
+/* How many MSI's are available depends on HW (Min 2 max 8) */
+/* How many are usable also depends on Host platform */
+
+#define INTERRUPT_BASE (0x40)
+
+#define INTERRUPT_ENABLE (INTERRUPT_BASE + 0x00)
+#define MSI0_ENABLE (INTERRUPT_BASE + 0x00)
+#define MSI1_ENABLE (INTERRUPT_BASE + 0x04)
+#define MSI2_ENABLE (INTERRUPT_BASE + 0x08)
+#define MSI3_ENABLE (INTERRUPT_BASE + 0x0C)
+#define MSI4_ENABLE (INTERRUPT_BASE + 0x10)
+#define MSI5_ENABLE (INTERRUPT_BASE + 0x14)
+#define MSI6_ENABLE (INTERRUPT_BASE + 0x18)
+#define MSI7_ENABLE (INTERRUPT_BASE + 0x1C)
+
+#define INTERRUPT_STATUS (INTERRUPT_BASE + 0x20)
+#define INTERRUPT_ACK (INTERRUPT_BASE + 0x20)
+
+#define INTMASK_I2C1 (0x00000001)
+#define INTMASK_I2C2 (0x00000002)
+#define INTMASK_I2C3 (0x00000004)
+#define INTMASK_I2C4 (0x00000008)
+
+#define INTMASK_CIRQ1 (0x00000010)
+#define INTMASK_CIRQ2 (0x00000020)
+#define INTMASK_CIRQ3 (0x00000040)
+#define INTMASK_CIRQ4 (0x00000080)
+
+#define INTMASK_TSINPUT1 (0x00000100)
+#define INTMASK_TSINPUT2 (0x00000200)
+#define INTMASK_TSINPUT3 (0x00000400)
+#define INTMASK_TSINPUT4 (0x00000800)
+#define INTMASK_TSINPUT5 (0x00001000)
+#define INTMASK_TSINPUT6 (0x00002000)
+#define INTMASK_TSINPUT7 (0x00004000)
+#define INTMASK_TSINPUT8 (0x00008000)
+
+#define INTMASK_TSOUTPUT1 (0x00010000)
+#define INTMASK_TSOUTPUT2 (0x00020000)
+#define INTMASK_TSOUTPUT3 (0x00040000)
+#define INTMASK_TSOUTPUT4 (0x00080000)
+
+/* ------------------------------------------------------------------------- */
+/* I2C Master Controller */
+
+#define I2C_BASE (0x80) /* Byte offset */
+
+#define I2C_COMMAND (0x00)
+#define I2C_TIMING (0x04)
+#define I2C_TASKLENGTH (0x08) /* High read, low write */
+#define I2C_TASKADDRESS (0x0C) /* High read, low write */
+
+#define I2C_MONITOR (0x1C)
+
+#define I2C_BASE_1 (I2C_BASE + 0x00)
+#define I2C_BASE_2 (I2C_BASE + 0x20)
+#define I2C_BASE_3 (I2C_BASE + 0x40)
+#define I2C_BASE_4 (I2C_BASE + 0x60)
+
+#define I2C_BASE_N(i) (I2C_BASE + (i) * 0x20)
+
+#define I2C_TASKMEM_BASE (0x1000) /* Byte offset */
+#define I2C_TASKMEM_SIZE (0x1000)
+
+#define I2C_SPEED_400 (0x04030404)
+#define I2C_SPEED_200 (0x09080909)
+#define I2C_SPEED_154 (0x0C0B0C0C)
+#define I2C_SPEED_100 (0x13121313)
+#define I2C_SPEED_77 (0x19181919)
+#define I2C_SPEED_50 (0x27262727)
+
+
+/* ------------------------------------------------------------------------- */
+/* DMA Controller */
+
+#define DMA_BASE_WRITE (0x100)
+#define DMA_BASE_READ (0x140)
+
+#define DMA_CONTROL (0x00) /* 64 */
+#define DMA_ERROR (0x04) /* 65 ( only read instance ) */
+
+#define DMA_DIAG_CONTROL (0x1C) /* 71 */
+#define DMA_DIAG_PACKETCOUNTER_LOW (0x20) /* 72 */
+#define DMA_DIAG_PACKETCOUNTER_HIGH (0x24) /* 73 */
+#define DMA_DIAG_TIMECOUNTER_LOW (0x28) /* 74 */
+#define DMA_DIAG_TIMECOUNTER_HIGH (0x2C) /* 75 */
+#define DMA_DIAG_RECHECKCOUNTER (0x30) /* 76 ( Split completions on read ) */
+#define DMA_DIAG_WAITTIMEOUTINIT (0x34) /* 77 */
+#define DMA_DIAG_WAITOVERFLOWCOUNTER (0x38) /* 78 */
+#define DMA_DIAG_WAITCOUNTER (0x3C) /* 79 */
+
+/* ------------------------------------------------------------------------- */
+/* DMA Buffer */
+
+#define TS_INPUT_BASE (0x200)
+#define TS_INPUT_CONTROL(i) (TS_INPUT_BASE + (i) * 16 + 0x00)
+
+#define TS_OUTPUT_BASE (0x280)
+#define TS_OUTPUT_CONTROL(i) (TS_OUTPUT_BASE + (i) * 16 + 0x00)
+
+#define DMA_BUFFER_BASE (0x300)
+
+#define DMA_BUFFER_CONTROL(i) (DMA_BUFFER_BASE + (i) * 16 + 0x00)
+#define DMA_BUFFER_ACK(i) (DMA_BUFFER_BASE + (i) * 16 + 0x04)
+#define DMA_BUFFER_CURRENT(i) (DMA_BUFFER_BASE + (i) * 16 + 0x08)
+#define DMA_BUFFER_SIZE(i) (DMA_BUFFER_BASE + (i) * 16 + 0x0c)
+
+#define DMA_BASE_ADDRESS_TABLE (0x2000)
+#define DMA_BASE_ADDRESS_TABLE_ENTRIES (512)
+
diff --git a/drivers/media/dvb/ddbridge/ddbridge.h b/drivers/media/dvb/ddbridge/ddbridge.h
new file mode 100644
index 000000000000..6d14893218f4
--- /dev/null
+++ b/drivers/media/dvb/ddbridge/ddbridge.h
@@ -0,0 +1,187 @@
+/*
+ * ddbridge.h: Digital Devices PCIe bridge driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _DDBRIDGE_H_
+#define _DDBRIDGE_H_
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <asm/dma.h>
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/ca.h>
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <linux/socket.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_ringbuffer.h"
+#include "dvb_ca_en50221.h"
+#include "dvb_net.h"
+#include "cxd2099.h"
+
+#define DDB_MAX_I2C 4
+#define DDB_MAX_PORT 4
+#define DDB_MAX_INPUT 8
+#define DDB_MAX_OUTPUT 4
+
+struct ddb_info {
+ int type;
+#define DDB_NONE 0
+#define DDB_OCTOPUS 1
+ char *name;
+ int port_num;
+ u32 port_type[DDB_MAX_PORT];
+};
+
+/* DMA_SIZE MUST be divisible by 188 and 128 !!! */
+
+#define INPUT_DMA_MAX_BUFS 32 /* hardware table limit */
+#define INPUT_DMA_BUFS 8
+#define INPUT_DMA_SIZE (128*47*21)
+
+#define OUTPUT_DMA_MAX_BUFS 32
+#define OUTPUT_DMA_BUFS 8
+#define OUTPUT_DMA_SIZE (128*47*21)
+
+struct ddb;
+struct ddb_port;
+
+struct ddb_input {
+ struct ddb_port *port;
+ u32 nr;
+ int attached;
+
+ dma_addr_t pbuf[INPUT_DMA_MAX_BUFS];
+ u8 *vbuf[INPUT_DMA_MAX_BUFS];
+ u32 dma_buf_num;
+ u32 dma_buf_size;
+
+ struct tasklet_struct tasklet;
+ spinlock_t lock;
+ wait_queue_head_t wq;
+ int running;
+ u32 stat;
+ u32 cbuf;
+ u32 coff;
+
+ struct dvb_adapter adap;
+ struct dvb_device *dev;
+ struct dvb_frontend *fe;
+ struct dvb_frontend *fe2;
+ struct dmxdev dmxdev;
+ struct dvb_demux demux;
+ struct dvb_net dvbnet;
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ int users;
+ int (*gate_ctrl)(struct dvb_frontend *, int);
+};
+
+struct ddb_output {
+ struct ddb_port *port;
+ u32 nr;
+ dma_addr_t pbuf[OUTPUT_DMA_MAX_BUFS];
+ u8 *vbuf[OUTPUT_DMA_MAX_BUFS];
+ u32 dma_buf_num;
+ u32 dma_buf_size;
+ struct tasklet_struct tasklet;
+ spinlock_t lock;
+ wait_queue_head_t wq;
+ int running;
+ u32 stat;
+ u32 cbuf;
+ u32 coff;
+
+ struct dvb_adapter adap;
+ struct dvb_device *dev;
+};
+
+struct ddb_i2c {
+ struct ddb *dev;
+ u32 nr;
+ struct i2c_adapter adap;
+ struct i2c_adapter adap2;
+ u32 regs;
+ u32 rbuf;
+ u32 wbuf;
+ int done;
+ wait_queue_head_t wq;
+};
+
+struct ddb_port {
+ struct ddb *dev;
+ u32 nr;
+ struct ddb_i2c *i2c;
+ struct mutex i2c_gate_lock;
+ u32 class;
+#define DDB_PORT_NONE 0
+#define DDB_PORT_CI 1
+#define DDB_PORT_TUNER 2
+ u32 type;
+#define DDB_TUNER_NONE 0
+#define DDB_TUNER_DVBS_ST 1
+#define DDB_TUNER_DVBS_ST_AA 2
+#define DDB_TUNER_DVBCT_TR 16
+#define DDB_TUNER_DVBCT_ST 17
+ u32 adr;
+
+ struct ddb_input *input[2];
+ struct ddb_output *output;
+ struct dvb_ca_en50221 *en;
+};
+
+struct ddb {
+ struct pci_dev *pdev;
+ unsigned char *regs;
+ struct ddb_port port[DDB_MAX_PORT];
+ struct ddb_i2c i2c[DDB_MAX_I2C];
+ struct ddb_input input[DDB_MAX_INPUT];
+ struct ddb_output output[DDB_MAX_OUTPUT];
+
+ struct device *ddb_dev;
+ int nr;
+ u8 iobuf[1028];
+
+ struct ddb_info *info;
+ int msi;
+};
+
+/****************************************************************************/
+
+#define ddbwritel(_val, _adr) writel((_val), \
+ (char *) (dev->regs+(_adr)))
+#define ddbreadl(_adr) readl((char *) (dev->regs+(_adr)))
+#define ddbcpyto(_adr, _src, _count) memcpy_toio((char *) \
+ (dev->regs+(_adr)), (_src), (_count))
+#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), (char *) \
+ (dev->regs+(_adr)), (_count))
+
+/****************************************************************************/
+
+#endif
diff --git a/drivers/media/dvb/dm1105/Makefile b/drivers/media/dvb/dm1105/Makefile
index 8ac28b0546af..95a008b71fe5 100644
--- a/drivers/media/dvb/dm1105/Makefile
+++ b/drivers/media/dvb/dm1105/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_DVB_DM1105) += dm1105.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
index b2b0c45f32a9..55e6533f15e9 100644
--- a/drivers/media/dvb/dm1105/dm1105.c
+++ b/drivers/media/dvb/dm1105/dm1105.c
@@ -22,6 +22,7 @@
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
diff --git a/drivers/media/dvb/dvb-core/Makefile b/drivers/media/dvb/dvb-core/Makefile
index 0b5182835cc8..8f22bcd7c1f9 100644
--- a/drivers/media/dvb/dvb-core/Makefile
+++ b/drivers/media/dvb/dvb-core/Makefile
@@ -2,8 +2,10 @@
# Makefile for the kernel DVB device drivers.
#
+dvb-net-$(CONFIG_DVB_NET) := dvb_net.o
+
dvb-core-objs := dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \
dvb_ca_en50221.o dvb_frontend.o \
- dvb_net.o dvb_ringbuffer.o dvb_math.o
+ $(dvb-net-y) dvb_ringbuffer.o dvb_math.o
obj-$(CONFIG_DVB_CORE) += dvb-core.o
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 98278041d75f..2c0acdb4d811 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -149,30 +149,25 @@ static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
dprintk ("%s\n", __func__);
- if (mutex_lock_interruptible (&events->mtx))
- return;
+ if ((status & FE_HAS_LOCK) && fe->ops.get_frontend)
+ fe->ops.get_frontend(fe, &fepriv->parameters_out);
- wp = (events->eventw + 1) % MAX_EVENT;
+ mutex_lock(&events->mtx);
+ wp = (events->eventw + 1) % MAX_EVENT;
if (wp == events->eventr) {
events->overflow = 1;
events->eventr = (events->eventr + 1) % MAX_EVENT;
}
e = &events->events[events->eventw];
-
- if (status & FE_HAS_LOCK)
- if (fe->ops.get_frontend)
- fe->ops.get_frontend(fe, &fepriv->parameters_out);
-
+ e->status = status;
e->parameters = fepriv->parameters_out;
events->eventw = wp;
mutex_unlock(&events->mtx);
- e->status = status;
-
wake_up_interruptible (&events->wait_queue);
}
@@ -207,19 +202,24 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe,
return ret;
}
- if (mutex_lock_interruptible (&events->mtx))
- return -ERESTARTSYS;
-
- memcpy (event, &events->events[events->eventr],
- sizeof(struct dvb_frontend_event));
-
+ mutex_lock(&events->mtx);
+ *event = events->events[events->eventr];
events->eventr = (events->eventr + 1) % MAX_EVENT;
-
mutex_unlock(&events->mtx);
return 0;
}
+static void dvb_frontend_clear_events(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_fe_events *events = &fepriv->events;
+
+ mutex_lock(&events->mtx);
+ events->eventr = events->eventw;
+ mutex_unlock(&events->mtx);
+}
+
static void dvb_frontend_init(struct dvb_frontend *fe)
{
dprintk ("DVB: initialising adapter %i frontend %i (%s)...\n",
@@ -537,7 +537,6 @@ static int dvb_frontend_thread(void *data)
{
struct dvb_frontend *fe = data;
struct dvb_frontend_private *fepriv = fe->frontend_priv;
- unsigned long timeout;
fe_status_t s;
enum dvbfe_algo algo;
@@ -558,7 +557,7 @@ static int dvb_frontend_thread(void *data)
while (1) {
up(&fepriv->sem); /* is locked when we enter the thread... */
restart:
- timeout = wait_event_interruptible_timeout(fepriv->wait_queue,
+ wait_event_interruptible_timeout(fepriv->wait_queue,
dvb_frontend_should_wakeup(fe) || kthread_should_stop()
|| freezing(current),
fepriv->delay);
@@ -577,12 +576,10 @@ restart:
if (fepriv->reinitialise) {
dvb_frontend_init(fe);
- if (fepriv->tone != -1) {
+ if (fe->ops.set_tone && fepriv->tone != -1)
fe->ops.set_tone(fe, fepriv->tone);
- }
- if (fepriv->voltage != -1) {
+ if (fe->ops.set_voltage && fepriv->voltage != -1)
fe->ops.set_voltage(fe, fepriv->voltage);
- }
fepriv->reinitialise = 0;
}
@@ -904,7 +901,7 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
.buffer = b \
}
-static struct dtv_cmds_h dtv_cmds[] = {
+static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
_DTV_CMD(DTV_TUNE, 1, 0),
_DTV_CMD(DTV_CLEAR, 1, 0),
@@ -966,6 +963,7 @@ static struct dtv_cmds_h dtv_cmds[] = {
_DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 0, 0),
_DTV_CMD(DTV_ISDBS_TS_ID, 1, 0),
+ _DTV_CMD(DTV_DVBT2_PLP_ID, 1, 0),
/* Get */
_DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1),
@@ -1018,6 +1016,29 @@ static int is_legacy_delivery_system(fe_delivery_system_t s)
return 0;
}
+/* Initialize the cache with some default values derived from the
+ * legacy frontend_info structure.
+ */
+static void dtv_property_cache_init(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ switch (fe->ops.info.type) {
+ case FE_QPSK:
+ c->modulation = QPSK; /* implied for DVB-S in legacy API */
+ c->rolloff = ROLLOFF_35;/* implied for DVB-S */
+ c->delivery_system = SYS_DVBS;
+ break;
+ case FE_QAM:
+ c->delivery_system = SYS_DVBC_ANNEX_AC;
+ break;
+ case FE_OFDM:
+ c->delivery_system = SYS_DVBT;
+ break;
+ case FE_ATSC:
+ break;
+ }
+}
+
/* Synchronise the legacy tuning parameters into the cache, so that demodulator
* drivers can use a single set_frontend tuning function, regardless of whether
* it's being used for the legacy or new API, reducing code and complexity.
@@ -1031,17 +1052,13 @@ static void dtv_property_cache_sync(struct dvb_frontend *fe,
switch (fe->ops.info.type) {
case FE_QPSK:
- c->modulation = QPSK; /* implied for DVB-S in legacy API */
- c->rolloff = ROLLOFF_35;/* implied for DVB-S */
c->symbol_rate = p->u.qpsk.symbol_rate;
c->fec_inner = p->u.qpsk.fec_inner;
- c->delivery_system = SYS_DVBS;
break;
case FE_QAM:
c->symbol_rate = p->u.qam.symbol_rate;
c->fec_inner = p->u.qam.fec_inner;
c->modulation = p->u.qam.modulation;
- c->delivery_system = SYS_DVBC_ANNEX_AC;
break;
case FE_OFDM:
if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
@@ -1059,7 +1076,6 @@ static void dtv_property_cache_sync(struct dvb_frontend *fe,
c->transmission_mode = p->u.ofdm.transmission_mode;
c->guard_interval = p->u.ofdm.guard_interval;
c->hierarchy = p->u.ofdm.hierarchy_information;
- c->delivery_system = SYS_DVBT;
break;
case FE_ATSC:
c->modulation = p->u.vsb.modulation;
@@ -1131,16 +1147,13 @@ static void dtv_property_adv_params_sync(struct dvb_frontend *fe)
p->frequency = c->frequency;
p->inversion = c->inversion;
- switch(c->modulation) {
- case PSK_8:
- case APSK_16:
- case APSK_32:
- case QPSK:
+ if (c->delivery_system == SYS_DSS ||
+ c->delivery_system == SYS_DVBS ||
+ c->delivery_system == SYS_DVBS2 ||
+ c->delivery_system == SYS_ISDBS ||
+ c->delivery_system == SYS_TURBO) {
p->u.qpsk.symbol_rate = c->symbol_rate;
p->u.qpsk.fec_inner = c->fec_inner;
- break;
- default:
- break;
}
/* Fake out a generic DVB-T request so we pass validation in the ioctl */
@@ -1823,9 +1836,17 @@ static int dvb_frontend_ioctl_legacy(struct file *file,
memcpy (&fepriv->parameters_in, parg,
sizeof (struct dvb_frontend_parameters));
+ dtv_property_cache_init(fe, c);
dtv_property_cache_sync(fe, c, &fepriv->parameters_in);
}
+ /*
+ * Initialize output parameters to match the values given by
+ * the user. FE_SET_FRONTEND triggers an initial frontend event
+ * with status = 0, which copies output parameters to userspace.
+ */
+ fepriv->parameters_out = fepriv->parameters_in;
+
memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
memcpy(&fetunesettings.parameters, parg,
sizeof (struct dvb_frontend_parameters));
@@ -1883,8 +1904,9 @@ static int dvb_frontend_ioctl_legacy(struct file *file,
/* Request the search algorithm to search */
fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
- dvb_frontend_wakeup(fe);
+ dvb_frontend_clear_events(fe);
dvb_frontend_add_event(fe, 0);
+ dvb_frontend_wakeup(fe);
fepriv->status = 0;
err = 0;
break;
@@ -1988,6 +2010,14 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) {
if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0)
goto err0;
+
+ /* If we took control of the bus, we need to force
+ reinitialization. This is because many ts_bus_ctrl()
+ functions strobe the RESET pin on the demod, and if the
+ frontend thread already exists then the dvb_init() routine
+ won't get called (which is what usually does initial
+ register configuration). */
+ fepriv->reinitialise = 1;
}
if ((ret = dvb_generic_open (inode, file)) < 0)
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index 5590eb6eb408..67bbfa728016 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -209,6 +209,7 @@ struct dvb_tuner_ops {
int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency);
int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
+ int (*get_if_frequency)(struct dvb_frontend *fe, u32 *frequency);
#define TUNER_STATUS_LOCKED 1
#define TUNER_STATUS_STEREO 2
diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
index 51752a9ef7a4..93d9869e0f15 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.c
+++ b/drivers/media/dvb/dvb-core/dvb_net.c
@@ -1230,7 +1230,7 @@ static const struct net_device_ops dvb_netdev_ops = {
.ndo_open = dvb_net_open,
.ndo_stop = dvb_net_stop,
.ndo_start_xmit = dvb_net_tx,
- .ndo_set_multicast_list = dvb_net_set_multicast_list,
+ .ndo_set_rx_mode = dvb_net_set_multicast_list,
.ndo_set_mac_address = dvb_net_set_mac,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/media/dvb/dvb-core/dvb_net.h b/drivers/media/dvb/dvb-core/dvb_net.h
index 3a3126cae03b..1e53acd50cf4 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.h
+++ b/drivers/media/dvb/dvb-core/dvb_net.h
@@ -32,6 +32,8 @@
#define DVB_NET_DEVICES_MAX 10
+#ifdef CONFIG_DVB_NET
+
struct dvb_net {
struct dvb_device *dvbdev;
struct net_device *device[DVB_NET_DEVICES_MAX];
@@ -40,8 +42,25 @@ struct dvb_net {
struct dmx_demux *demux;
};
-
void dvb_net_release(struct dvb_net *);
int dvb_net_init(struct dvb_adapter *, struct dvb_net *, struct dmx_demux *);
+#else
+
+struct dvb_net {
+ struct dvb_device *dvbdev;
+};
+
+static inline void dvb_net_release(struct dvb_net *dvbnet)
+{
+}
+
+static inline int dvb_net_init(struct dvb_adapter *adap,
+ struct dvb_net *dvbnet, struct dmx_demux *dmx)
+{
+ return 0;
+}
+
+#endif /* ifdef CONFIG_DVB_NET */
+
#endif
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index e85304c59a2b..58257165761e 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -81,6 +81,7 @@ config DVB_USB_DIB0700
select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
help
Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
@@ -257,6 +258,19 @@ config DVB_USB_AF9005_REMOTE
Say Y here to support the default remote control decoding for the
Afatech AF9005 based receiver.
+config DVB_USB_PCTV452E
+ tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600"
+ depends on DVB_USB
+ select TTPCI_EEPROM
+ select DVB_LNBP22 if !DVB_FE_CUSTOMISE
+ select DVB_STB0899 if !DVB_FE_CUSTOMISE
+ select DVB_STB6100 if !DVB_FE_CUSTOMISE
+ help
+ Support for external USB adapter designed by Pinnacle,
+ shipped under the brand name 'PCTV HDTV Pro USB'.
+ Also supports TT Connect S2-3600/3650 cards.
+ Say Y if you own such a device and want to use it.
+
config DVB_USB_DW2102
tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support"
depends on DVB_USB
@@ -373,3 +387,18 @@ config DVB_USB_TECHNISAT_USB2
select DVB_STV6110x if !DVB_FE_CUSTOMISE
help
Say Y here to support the Technisat USB2 DVB-S/S2 device
+
+config DVB_USB_IT913X
+ tristate "it913x driver"
+ depends on DVB_USB
+ select DVB_IT913X_FE
+ help
+ Say Y here to support the it913x device
+
+config DVB_USB_MXL111SF
+ tristate "MxL111SF DTV USB2.0 support"
+ depends on DVB_USB
+ select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
+ select VIDEO_TVEEPROM
+ help
+ Say Y here to support the MxL111SF USB2.0 DTV receiver.
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 4bac13da0c39..26c8b9e57050 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -64,6 +64,9 @@ obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o
dvb-usb-anysee-objs = anysee.o
obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o
+dvb-usb-pctv452e-objs = pctv452e.o
+obj-$(CONFIG_DVB_USB_PCTV452E) += dvb-usb-pctv452e.o
+
dvb-usb-dw2102-objs = dw2102.o
obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o
@@ -94,7 +97,16 @@ obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o
dvb-usb-technisat-usb2-objs = technisat-usb2.o
obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+dvb-usb-it913x-objs := it913x.o
+obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o
+
+dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o
+obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
+obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
+obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
+
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
# due to tuner-xc3028
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/common/tuners
+EXTRA_CFLAGS += -Idrivers/media/dvb/ttpci
diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c
index b95a95e17840..2aef3c89e9fa 100644
--- a/drivers/media/dvb/dvb-usb/a800.c
+++ b/drivers/media/dvb/dvb-usb/a800.c
@@ -127,6 +127,8 @@ static struct dvb_usb_device_properties a800_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.streaming_ctrl = dibusb2_0_streaming_ctrl,
@@ -147,7 +149,7 @@ static struct dvb_usb_device_properties a800_properties = {
}
}
},
-
+ }},
.size_of_priv = sizeof(struct dibusb_state),
},
},
diff --git a/drivers/media/dvb/dvb-usb/af9005-fe.c b/drivers/media/dvb/dvb-usb/af9005-fe.c
index 6ad94745bbdd..3263e9749d09 100644
--- a/drivers/media/dvb/dvb-usb/af9005-fe.c
+++ b/drivers/media/dvb/dvb-usb/af9005-fe.c
@@ -63,11 +63,9 @@ static int af9005_write_word_agc(struct dvb_usb_device *d, u16 reghi,
u16 reglo, u8 pos, u8 len, u16 value)
{
int ret;
- u8 temp;
if ((ret = af9005_write_ofdm_register(d, reglo, (u8) (value & 0xff))))
return ret;
- temp = (u8) ((value & 0x0300) >> 8);
return af9005_write_register_bits(d, reghi, pos, len,
(u8) ((value & 0x300) >> 8));
}
diff --git a/drivers/media/dvb/dvb-usb/af9005-remote.c b/drivers/media/dvb/dvb-usb/af9005-remote.c
index c3bc64ed405c..7e3961d0db6b 100644
--- a/drivers/media/dvb/dvb-usb/af9005-remote.c
+++ b/drivers/media/dvb/dvb-usb/af9005-remote.c
@@ -21,7 +21,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * see Documentation/dvb/REDME.dvb-usb for more information
+ * see Documentation/dvb/README.dvb-usb for more information
*/
#include "af9005.h"
/* debug */
diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/dvb/dvb-usb/af9005.c
index 51f6439dcfd5..bd51a764351b 100644
--- a/drivers/media/dvb/dvb-usb/af9005.c
+++ b/drivers/media/dvb/dvb-usb/af9005.c
@@ -19,7 +19,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * see Documentation/dvb/REDME.dvb-usb for more information
+ * see Documentation/dvb/README.dvb-usb for more information
*/
#include "af9005.h"
@@ -815,7 +815,7 @@ static int af9005_frontend_attach(struct dvb_usb_adapter *adap)
debug_dump(buf, 8, printk);
}
}
- adap->fe = af9005_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = af9005_fe_attach(adap->dev);
return 0;
}
@@ -999,6 +999,8 @@ static struct dvb_usb_device_properties af9005_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps =
DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1018,6 +1020,7 @@ static struct dvb_usb_device_properties af9005_properties = {
}
}
},
+ }},
}
},
.power_ctrl = af9005_power_ctrl,
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index 100ebc37e99e..c6c275bac08e 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -91,7 +91,6 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
case GET_CONFIG:
case READ_MEMORY:
case RECONNECT_USB:
- case GET_IR_CODE:
write = 0;
break;
case READ_I2C:
@@ -164,13 +163,6 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
deb_xfer("<<< ");
debug_dump(buf, act_len, deb_xfer);
- /* remote controller query status is 1 if remote code is not received */
- if (req->cmd == GET_IR_CODE && buf[1] == 1) {
- buf[1] = 0; /* clear command "error" status */
- memset(&buf[2], 0, req->data_len);
- buf[3] = 1; /* no remote code received mark */
- }
-
/* check status */
if (buf[1]) {
err("command failed:%d", buf[1]);
@@ -292,6 +284,10 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
}
if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+ if (msg[i].len > 3 || msg[i+1].len > 61) {
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
if (msg[i].addr ==
af9015_af9013_config[0].demod_address)
req.cmd = READ_MEMORY;
@@ -306,12 +302,16 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
ret = af9015_ctrl_msg(d, &req);
i += 2;
} else if (msg[i].flags & I2C_M_RD) {
- ret = -EINVAL;
+ if (msg[i].len > 61) {
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
if (msg[i].addr ==
- af9015_af9013_config[0].demod_address)
+ af9015_af9013_config[0].demod_address) {
+ ret = -EINVAL;
goto error;
- else
- req.cmd = READ_I2C;
+ }
+ req.cmd = READ_I2C;
req.i2c_addr = msg[i].addr;
req.addr = addr;
req.mbox = mbox;
@@ -321,6 +321,10 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
ret = af9015_ctrl_msg(d, &req);
i += 1;
} else {
+ if (msg[i].len > 21) {
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
if (msg[i].addr ==
af9015_af9013_config[0].demod_address)
req.cmd = WRITE_MEMORY;
@@ -735,30 +739,37 @@ static const struct af9015_rc_setup af9015_rc_setup_hashes[] = {
{ 0xb8feb708, RC_MAP_MSI_DIGIVOX_II },
{ 0xa3703d00, RC_MAP_ALINK_DTU_M },
{ 0x9b7dc64e, RC_MAP_TOTAL_MEDIA_IN_HAND }, /* MYGICTV U718 */
+ { 0x5d49e3db, RC_MAP_DIGITTRADE }, /* LC-Power LC-USB-DVBT */
{ }
};
static const struct af9015_rc_setup af9015_rc_setup_usbids[] = {
- { (USB_VID_TERRATEC << 16) + USB_PID_TERRATEC_CINERGY_T_STICK_RC,
+ { (USB_VID_TERRATEC << 16) | USB_PID_TERRATEC_CINERGY_T_STICK_RC,
RC_MAP_TERRATEC_SLIM_2 },
- { (USB_VID_TERRATEC << 16) + USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC,
+ { (USB_VID_TERRATEC << 16) | USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC,
RC_MAP_TERRATEC_SLIM },
- { (USB_VID_VISIONPLUS << 16) + USB_PID_AZUREWAVE_AD_TU700,
+ { (USB_VID_VISIONPLUS << 16) | USB_PID_AZUREWAVE_AD_TU700,
RC_MAP_AZUREWAVE_AD_TU700 },
- { (USB_VID_VISIONPLUS << 16) + USB_PID_TINYTWIN,
+ { (USB_VID_VISIONPLUS << 16) | USB_PID_TINYTWIN,
RC_MAP_AZUREWAVE_AD_TU700 },
- { (USB_VID_MSI_2 << 16) + USB_PID_MSI_DIGI_VOX_MINI_III,
+ { (USB_VID_MSI_2 << 16) | USB_PID_MSI_DIGI_VOX_MINI_III,
RC_MAP_MSI_DIGIVOX_III },
- { (USB_VID_LEADTEK << 16) + USB_PID_WINFAST_DTV_DONGLE_GOLD,
+ { (USB_VID_MSI_2 << 16) | USB_PID_MSI_DIGIVOX_DUO,
+ RC_MAP_MSI_DIGIVOX_III },
+ { (USB_VID_LEADTEK << 16) | USB_PID_WINFAST_DTV_DONGLE_GOLD,
+ RC_MAP_LEADTEK_Y04G0051 },
+ { (USB_VID_LEADTEK << 16) | USB_PID_WINFAST_DTV2000DS,
RC_MAP_LEADTEK_Y04G0051 },
- { (USB_VID_AVERMEDIA << 16) + USB_PID_AVERMEDIA_VOLAR_X,
+ { (USB_VID_AVERMEDIA << 16) | USB_PID_AVERMEDIA_VOLAR_X,
RC_MAP_AVERMEDIA_M135A },
- { (USB_VID_AFATECH << 16) + USB_PID_TREKSTOR_DVBT,
+ { (USB_VID_AFATECH << 16) | USB_PID_TREKSTOR_DVBT,
RC_MAP_TREKSTOR },
- { (USB_VID_KWORLD_2 << 16) + USB_PID_TINYTWIN_2,
+ { (USB_VID_KWORLD_2 << 16) | USB_PID_TINYTWIN_2,
RC_MAP_DIGITALNOW_TINYTWIN },
- { (USB_VID_GTEK << 16) + USB_PID_TINYTWIN_3,
+ { (USB_VID_GTEK << 16) | USB_PID_TINYTWIN_3,
RC_MAP_DIGITALNOW_TINYTWIN },
+ { (USB_VID_KWORLD_2 << 16) | USB_PID_SVEON_STV22,
+ RC_MAP_MSI_DIGIVOX_III },
{ }
};
@@ -850,13 +861,13 @@ static int af9015_read_config(struct usb_device *udev)
for (i = 0; i < af9015_properties_count; i++) {
/* USB1.1 set smaller buffersize and disable 2nd adapter */
if (udev->speed == USB_SPEED_FULL) {
- af9015_properties[i].adapter[0].stream.u.bulk.buffersize
+ af9015_properties[i].adapter[0].fe[0].stream.u.bulk.buffersize
= TS_USB11_FRAME_SIZE;
/* disable 2nd adapter because we don't have
PID-filters */
af9015_config.dual_mode = 0;
} else {
- af9015_properties[i].adapter[0].stream.u.bulk.buffersize
+ af9015_properties[i].adapter[0].fe[0].stream.u.bulk.buffersize
= TS_USB20_FRAME_SIZE;
}
}
@@ -1082,44 +1093,11 @@ error:
return ret;
}
-/* init 2nd I2C adapter */
-static int af9015_i2c_init(struct dvb_usb_device *d)
-{
- int ret;
- struct af9015_state *state = d->priv;
- deb_info("%s:\n", __func__);
-
- strncpy(state->i2c_adap.name, d->desc->name,
- sizeof(state->i2c_adap.name));
- state->i2c_adap.algo = d->props.i2c_algo;
- state->i2c_adap.algo_data = NULL;
- state->i2c_adap.dev.parent = &d->udev->dev;
-
- i2c_set_adapdata(&state->i2c_adap, d);
-
- ret = i2c_add_adapter(&state->i2c_adap);
- if (ret < 0)
- err("could not add i2c adapter");
-
- return ret;
-}
-
static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
{
int ret;
- struct af9015_state *state = adap->dev->priv;
- struct i2c_adapter *i2c_adap;
-
- if (adap->id == 0) {
- /* select I2C adapter */
- i2c_adap = &adap->dev->i2c_adap;
-
- deb_info("%s: init I2C\n", __func__);
- ret = af9015_i2c_init(adap->dev);
- } else {
- /* select I2C adapter */
- i2c_adap = &state->i2c_adap;
+ if (adap->id == 1) {
/* copy firmware to 2nd demodulator */
if (af9015_config.dual_mode) {
ret = af9015_copy_firmware(adap->dev);
@@ -1135,10 +1113,10 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
}
/* attach demodulator */
- adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
- i2c_adap);
+ adap->fe_adap[0].fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
+ &adap->dev->i2c_adap);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static struct mt2060_config af9015_mt2060_config = {
@@ -1206,57 +1184,56 @@ static struct mxl5007t_config af9015_mxl5007t_config = {
static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
{
- struct af9015_state *state = adap->dev->priv;
- struct i2c_adapter *i2c_adap;
int ret;
deb_info("%s:\n", __func__);
- /* select I2C adapter */
- if (adap->id == 0)
- i2c_adap = &adap->dev->i2c_adap;
- else
- i2c_adap = &state->i2c_adap;
-
switch (af9015_af9013_config[adap->id].tuner) {
case AF9013_TUNER_MT2060:
case AF9013_TUNER_MT2060_2:
- ret = dvb_attach(mt2060_attach, adap->fe, i2c_adap,
+ ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&af9015_mt2060_config,
af9015_config.mt2060_if1[adap->id])
== NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_QT1010:
case AF9013_TUNER_QT1010A:
- ret = dvb_attach(qt1010_attach, adap->fe, i2c_adap,
+ ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&af9015_qt1010_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_TDA18271:
- ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, i2c_adap,
+ ret = dvb_attach(tda18271_attach, adap->fe_adap[0].fe, 0xc0,
+ &adap->dev->i2c_adap,
&af9015_tda18271_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_TDA18218:
- ret = dvb_attach(tda18218_attach, adap->fe, i2c_adap,
+ ret = dvb_attach(tda18218_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap,
&af9015_tda18218_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5003D:
- ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+ ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap,
&af9015_mxl5003_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5005D:
case AF9013_TUNER_MXL5005R:
- ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+ ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap,
&af9015_mxl5005_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_ENV77H11D5:
- ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0, i2c_adap,
+ ret = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0xc0,
+ &adap->dev->i2c_adap,
DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MC44S803:
- ret = dvb_attach(mc44s803_attach, adap->fe, i2c_adap,
+ ret = dvb_attach(mc44s803_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap,
&af9015_mc44s803_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5007T:
- ret = dvb_attach(mxl5007t_attach, adap->fe, i2c_adap,
+ ret = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap,
0xc0, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_UNKNOWN:
@@ -1309,6 +1286,7 @@ static struct usb_device_id af9015_usb_table[] = {
USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC)},
/* 35 */{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)},
{USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22)},
{0},
};
MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1328,6 +1306,8 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1343,8 +1323,11 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.count = 6,
.endpoint = 0x84,
},
+ }},
},
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach =
af9015_af9013_frontend_attach,
.tuner_attach = af9015_tuner_attach,
@@ -1359,6 +1342,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
}
}
},
+ }},
}
},
@@ -1456,6 +1440,8 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1471,8 +1457,11 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.count = 6,
.endpoint = 0x84,
},
+ }},
},
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach =
af9015_af9013_frontend_attach,
.tuner_attach = af9015_tuner_attach,
@@ -1487,6 +1476,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
}
}
},
+ }},
}
},
@@ -1502,7 +1492,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.i2c_algo = &af9015_i2c_algo,
- .num_device_descs = 9, /* check max from dvb-usb.h */
+ .num_device_descs = 10, /* check max from dvb-usb.h */
.devices = {
{
.name = "Xtensions XD-380",
@@ -1554,6 +1544,11 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.cold_ids = {&af9015_usb_table[20], NULL},
.warm_ids = {NULL},
},
+ {
+ .name = "Sveon STV22 Dual USB DVB-T Tuner HDTV",
+ .cold_ids = {&af9015_usb_table[37], NULL},
+ .warm_ids = {NULL},
+ },
}
}, {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
@@ -1568,6 +1563,8 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1583,8 +1580,11 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.count = 6,
.endpoint = 0x84,
},
+ }},
},
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach =
af9015_af9013_frontend_attach,
.tuner_attach = af9015_tuner_attach,
@@ -1599,6 +1599,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
}
}
},
+ }},
}
},
@@ -1704,33 +1705,11 @@ static int af9015_usb_probe(struct usb_interface *intf,
return ret;
}
-static void af9015_i2c_exit(struct dvb_usb_device *d)
-{
- struct af9015_state *state = d->priv;
- deb_info("%s:\n", __func__);
-
- /* remove 2nd I2C adapter */
- if (d->state & DVB_USB_STATE_I2C)
- i2c_del_adapter(&state->i2c_adap);
-}
-
-static void af9015_usb_device_exit(struct usb_interface *intf)
-{
- struct dvb_usb_device *d = usb_get_intfdata(intf);
- deb_info("%s:\n", __func__);
-
- /* remove 2nd I2C adapter */
- if (d != NULL && d->desc != NULL)
- af9015_i2c_exit(d);
-
- dvb_usb_device_exit(intf);
-}
-
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver af9015_usb_driver = {
.name = "dvb_usb_af9015",
.probe = af9015_usb_probe,
- .disconnect = af9015_usb_device_exit,
+ .disconnect = dvb_usb_device_exit,
.id_table = af9015_usb_table,
};
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
index beb3004f00ba..6252ea6c1904 100644
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -99,7 +99,6 @@ enum af9015_ir_mode {
};
struct af9015_state {
- struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */
u8 rc_repeat;
u32 rc_keycode;
u8 rc_last[4];
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index 7c327b54308e..5f2278b73ee9 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -347,15 +347,17 @@ static struct isl6423_config anysee_isl6423_config = {
* PCB: ?
* parts: DNOS404ZH102A(MT352, DTT7579(?))
*
- * E30 VID=04b4 PID=861f HW=2 FW=2.1 Product=????????
- * PCB: ?
+ * E30 VID=04b4 PID=861f HW=2 FW=2.1 "anysee-T(LP)"
+ * PCB: PCB 507T (rev1.61)
* parts: DNOS404ZH103A(ZL10353, DTT7579(?))
+ * OEA=0a OEB=00 OEC=00 OED=ff OEE=00
+ * IOA=45 IOB=ff IOC=00 IOD=ff IOE=00
*
* E30 Plus VID=04b4 PID=861f HW=6 FW=1.0 "anysee"
* PCB: 507CD (rev1.1)
* parts: DNOS404ZH103A(ZL10353, DTT7579(?)), CST56I01
- * OEA=80 OEB=00 OEC=00 OED=ff OEF=fe
- * IOA=4f IOB=ff IOC=00 IOD=06 IOF=01
+ * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe
+ * IOA=4f IOB=ff IOC=00 IOD=06 IOE=01
* IOD[0] ZL10353 1=enabled
* IOA[7] TS 0=enabled
* tuner is not behind ZL10353 I2C-gate (no care if gate disabled or not)
@@ -363,30 +365,30 @@ static struct isl6423_config anysee_isl6423_config = {
* E30 C Plus VID=04b4 PID=861f HW=10 FW=1.0 "anysee-DC(LP)"
* PCB: 507DC (rev0.2)
* parts: TDA10023, DTOS403IH102B TM, CST56I01
- * OEA=80 OEB=00 OEC=00 OED=ff OEF=fe
- * IOA=4f IOB=ff IOC=00 IOD=26 IOF=01
+ * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe
+ * IOA=4f IOB=ff IOC=00 IOD=26 IOE=01
* IOD[0] TDA10023 1=enabled
*
* E30 S2 Plus VID=04b4 PID=861f HW=11 FW=0.1 "anysee-S2(LP)"
* PCB: 507SI (rev2.1)
* parts: BS2N10WCC01(CX24116, CX24118), ISL6423, TDA8024
- * OEA=80 OEB=00 OEC=ff OED=ff OEF=fe
- * IOA=4d IOB=ff IOC=00 IOD=26 IOF=01
+ * OEA=80 OEB=00 OEC=ff OED=ff OEE=fe
+ * IOA=4d IOB=ff IOC=00 IOD=26 IOE=01
* IOD[0] CX24116 1=enabled
*
* E30 C Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)"
* PCB: 507FA (rev0.4)
* parts: TDA10023, DTOS403IH102B TM, TDA8024
- * OEA=80 OEB=00 OEC=ff OED=ff OEF=ff
- * IOA=4d IOB=ff IOC=00 IOD=00 IOF=c0
+ * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff
+ * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0
* IOD[5] TDA10023 1=enabled
* IOE[0] tuner 1=enabled
*
* E30 Combo Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)"
* PCB: 507FA (rev1.1)
* parts: ZL10353, TDA10023, DTOS403IH102B TM, TDA8024
- * OEA=80 OEB=00 OEC=ff OED=ff OEF=ff
- * IOA=4d IOB=ff IOC=00 IOD=00 IOF=c0
+ * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff
+ * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0
* DVB-C:
* IOD[5] TDA10023 1=enabled
* IOE[0] tuner 1=enabled
@@ -398,8 +400,8 @@ static struct isl6423_config anysee_isl6423_config = {
* E7 TC VID=1c73 PID=861f HW=18 FW=0.7 AMTCI=0.5 "anysee-E7TC(LP)"
* PCB: 508TC (rev0.6)
* parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212)
- * OEA=80 OEB=00 OEC=03 OED=f7 OEF=ff
- * IOA=4d IOB=00 IOC=cc IOD=48 IOF=e4
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4
* IOA[7] TS 1=enabled
* IOE[4] TDA18212 1=enabled
* DVB-C:
@@ -414,13 +416,144 @@ static struct isl6423_config anysee_isl6423_config = {
* E7 S2 VID=1c73 PID=861f HW=19 FW=0.4 AMTCI=0.5 "anysee-E7S2(LP)"
* PCB: 508S2 (rev0.7)
* parts: DNBU10512IST(STV0903, STV6110), ISL6423
- * OEA=80 OEB=00 OEC=03 OED=f7 OEF=ff
- * IOA=4d IOB=00 IOC=c4 IOD=08 IOF=e4
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4
* IOA[7] TS 1=enabled
* IOE[5] STV0903 1=enabled
*
+ * E7 PTC VID=1c73 PID=861f HW=21 FW=0.1 AMTCI=?? "anysee-E7PTC(LP)"
+ * PCB: 508PTC (rev0.5)
+ * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212)
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4
+ * IOA[7] TS 1=enabled
+ * IOE[4] TDA18212 1=enabled
+ * DVB-C:
+ * IOD[6] ZL10353 0=disabled
+ * IOD[5] TDA10023 1=enabled
+ * IOE[0] IF 1=enabled
+ * DVB-T:
+ * IOD[5] TDA10023 0=disabled
+ * IOD[6] ZL10353 1=enabled
+ * IOE[0] IF 0=enabled
+ *
+ * E7 S2 VID=1c73 PID=861f HW=22 FW=0.1 AMTCI=?? "anysee-E7PS2(LP)"
+ * PCB: 508PS2 (rev0.4)
+ * parts: DNBU10512IST(STV0903, STV6110), ISL6423
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4
+ * IOA[7] TS 1=enabled
+ * IOE[5] STV0903 1=enabled
*/
+static int anysee_frontend_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct anysee_state *state = adap->dev->priv;
+ int ret;
+
+ deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+
+ /* no frontend sleep control */
+ if (onoff == 0)
+ return 0;
+
+ switch (state->hw) {
+ case ANYSEE_HW_507FA: /* 15 */
+ /* E30 Combo Plus */
+ /* E30 C Plus */
+
+ if ((fe->id ^ dvb_usb_anysee_delsys) == 0) {
+ /* disable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
+ 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C tuner on IOE[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+ } else {
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
+ 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T tuner on IOE[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+ }
+
+ break;
+ case ANYSEE_HW_508TC: /* 18 */
+ case ANYSEE_HW_508PTC: /* 21 */
+ /* E7 TC */
+ /* E7 PTC */
+
+ if ((fe->id ^ dvb_usb_anysee_delsys) == 0) {
+ /* disable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6),
+ 0x40);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
+ 0x20);
+ if (ret)
+ goto error;
+
+ /* enable IF route on IOE[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+ } else {
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
+ 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6),
+ 0x40);
+ if (ret)
+ goto error;
+
+ /* enable IF route on IOE[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+ }
+
+ break;
+ default:
+ ret = 0;
+ }
+
+error:
+ return ret;
+}
+
static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
{
int ret;
@@ -441,41 +574,54 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
}
};
- /* Check which hardware we have.
- * We must do this call two times to get reliable values (hw bug).
- */
- ret = anysee_get_hw_info(adap->dev, hw_info);
- if (ret)
- goto error;
+ /* detect hardware only once */
+ if (adap->fe_adap[0].fe == NULL) {
+ /* Check which hardware we have.
+ * We must do this call two times to get reliable values (hw bug).
+ */
+ ret = anysee_get_hw_info(adap->dev, hw_info);
+ if (ret)
+ goto error;
- ret = anysee_get_hw_info(adap->dev, hw_info);
- if (ret)
- goto error;
+ ret = anysee_get_hw_info(adap->dev, hw_info);
+ if (ret)
+ goto error;
- /* Meaning of these info bytes are guessed. */
- info("firmware version:%d.%d hardware id:%d",
- hw_info[1], hw_info[2], hw_info[0]);
+ /* Meaning of these info bytes are guessed. */
+ info("firmware version:%d.%d hardware id:%d",
+ hw_info[1], hw_info[2], hw_info[0]);
- state->hw = hw_info[0];
+ state->hw = hw_info[0];
+ }
+
+ /* set current frondend ID for devices having two frondends */
+ if (adap->fe_adap[0].fe)
+ state->fe_id++;
switch (state->hw) {
- case ANYSEE_HW_02: /* 2 */
+ case ANYSEE_HW_507T: /* 2 */
/* E30 */
+ if (state->fe_id)
+ break;
+
/* attach demod */
- adap->fe = dvb_attach(mt352_attach, &anysee_mt352_config,
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach, &anysee_mt352_config,
&adap->dev->i2c_adap);
- if (adap->fe)
+ if (adap->fe_adap[0].fe)
break;
/* attach demod */
- adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
&adap->dev->i2c_adap);
break;
case ANYSEE_HW_507CD: /* 6 */
/* E30 Plus */
+ if (state->fe_id)
+ break;
+
/* enable DVB-T demod on IOD[0] */
ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01);
if (ret)
@@ -487,33 +633,39 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
goto error;
/* attach demod */
- adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
- &adap->dev->i2c_adap);
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach,
+ &anysee_zl10353_config, &adap->dev->i2c_adap);
break;
case ANYSEE_HW_507DC: /* 10 */
/* E30 C Plus */
+ if (state->fe_id)
+ break;
+
/* enable DVB-C demod on IOD[0] */
ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01);
if (ret)
goto error;
/* attach demod */
- adap->fe = dvb_attach(tda10023_attach, &anysee_tda10023_config,
- &adap->dev->i2c_adap, 0x48);
+ adap->fe_adap[0].fe = dvb_attach(tda10023_attach,
+ &anysee_tda10023_config, &adap->dev->i2c_adap, 0x48);
break;
case ANYSEE_HW_507SI: /* 11 */
/* E30 S2 Plus */
+ if (state->fe_id)
+ break;
+
/* enable DVB-S/S2 demod on IOD[0] */
ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01);
if (ret)
goto error;
/* attach demod */
- adap->fe = dvb_attach(cx24116_attach, &anysee_cx24116_config,
+ adap->fe_adap[0].fe = dvb_attach(cx24116_attach, &anysee_cx24116_config,
&adap->dev->i2c_adap);
break;
@@ -539,118 +691,117 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
if (ret)
goto error;
- if (dvb_usb_anysee_delsys) {
- /* disable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
- 0x20);
+ if ((state->fe_id ^ dvb_usb_anysee_delsys) == 0) {
+ /* disable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0),
+ 0x01);
if (ret)
goto error;
- /* enable DVB-T demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0),
- 0x01);
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
+ 0x20);
if (ret)
goto error;
/* attach demod */
if (tmp == 0xc7) {
/* TDA18212 config */
- adap->fe = dvb_attach(zl10353_attach,
- &anysee_zl10353_tda18212_config2,
- &adap->dev->i2c_adap);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(
+ tda10023_attach,
+ &anysee_tda10023_tda18212_config,
+ &adap->dev->i2c_adap, 0x48);
} else {
/* PLL config */
- adap->fe = dvb_attach(zl10353_attach,
- &anysee_zl10353_config,
- &adap->dev->i2c_adap);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(
+ tda10023_attach,
+ &anysee_tda10023_config,
+ &adap->dev->i2c_adap, 0x48);
}
} else {
- /* disable DVB-T demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0),
- 0x01);
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
+ 0x20);
if (ret)
goto error;
- /* enable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
- 0x20);
+ /* enable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0),
+ 0x01);
if (ret)
goto error;
/* attach demod */
if (tmp == 0xc7) {
/* TDA18212 config */
- adap->fe = dvb_attach(tda10023_attach,
- &anysee_tda10023_tda18212_config,
- &adap->dev->i2c_adap, 0x48);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(
+ zl10353_attach,
+ &anysee_zl10353_tda18212_config2,
+ &adap->dev->i2c_adap);
} else {
/* PLL config */
- adap->fe = dvb_attach(tda10023_attach,
- &anysee_tda10023_config,
- &adap->dev->i2c_adap, 0x48);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(
+ zl10353_attach,
+ &anysee_zl10353_config,
+ &adap->dev->i2c_adap);
}
}
break;
case ANYSEE_HW_508TC: /* 18 */
+ case ANYSEE_HW_508PTC: /* 21 */
/* E7 TC */
+ /* E7 PTC */
/* enable transport stream on IOA[7] */
ret = anysee_wr_reg_mask(adap->dev, REG_IOA, (1 << 7), 0x80);
if (ret)
goto error;
- if (dvb_usb_anysee_delsys) {
- /* disable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
- 0x20);
- if (ret)
- goto error;
-
- /* enable DVB-T demod on IOD[6] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6),
+ if ((state->fe_id ^ dvb_usb_anysee_delsys) == 0) {
+ /* disable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6),
0x40);
if (ret)
goto error;
- /* enable IF route on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0),
- 0x01);
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
+ 0x20);
if (ret)
goto error;
/* attach demod */
- adap->fe = dvb_attach(zl10353_attach,
- &anysee_zl10353_tda18212_config,
- &adap->dev->i2c_adap);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(tda10023_attach,
+ &anysee_tda10023_tda18212_config,
+ &adap->dev->i2c_adap, 0x48);
} else {
- /* disable DVB-T demod on IOD[6] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6),
- 0x40);
- if (ret)
- goto error;
-
- /* enable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
0x20);
if (ret)
goto error;
- /* enable IF route on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0),
- 0x01);
+ /* enable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6),
+ 0x40);
if (ret)
goto error;
/* attach demod */
- adap->fe = dvb_attach(tda10023_attach,
- &anysee_tda10023_tda18212_config,
- &adap->dev->i2c_adap, 0x48);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(zl10353_attach,
+ &anysee_zl10353_tda18212_config,
+ &adap->dev->i2c_adap);
}
break;
case ANYSEE_HW_508S2: /* 19 */
+ case ANYSEE_HW_508PS2: /* 22 */
/* E7 S2 */
+ /* E7 PS2 */
+
+ if (state->fe_id)
+ break;
/* enable transport stream on IOA[7] */
ret = anysee_wr_reg_mask(adap->dev, REG_IOA, (1 << 7), 0x80);
@@ -663,13 +814,13 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
goto error;
/* attach demod */
- adap->fe = dvb_attach(stv0900_attach, &anysee_stv0900_config,
+ adap->fe_adap[0].fe = dvb_attach(stv0900_attach, &anysee_stv0900_config,
&adap->dev->i2c_adap, 0);
break;
}
- if (!adap->fe) {
+ if (!adap->fe_adap[0].fe) {
/* we have no frontend :-( */
ret = -ENODEV;
err("Unsupported Anysee version. " \
@@ -684,14 +835,14 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
struct anysee_state *state = adap->dev->priv;
struct dvb_frontend *fe;
int ret;
- deb_info("%s:\n", __func__);
+ deb_info("%s: fe=%d\n", __func__, state->fe_id);
switch (state->hw) {
- case ANYSEE_HW_02: /* 2 */
+ case ANYSEE_HW_507T: /* 2 */
/* E30 */
/* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc2 >> 1),
+ fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, (0xc2 >> 1),
NULL, DVB_PLL_THOMSON_DTT7579);
break;
@@ -699,7 +850,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
/* E30 Plus */
/* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc2 >> 1),
+ fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, (0xc2 >> 1),
&adap->dev->i2c_adap, DVB_PLL_THOMSON_DTT7579);
break;
@@ -707,7 +858,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
/* E30 C Plus */
/* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc0 >> 1),
+ fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, (0xc0 >> 1),
&adap->dev->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A);
break;
@@ -715,28 +866,14 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
/* E30 S2 Plus */
/* attach LNB controller */
- fe = dvb_attach(isl6423_attach, adap->fe, &adap->dev->i2c_adap,
- &anysee_isl6423_config);
+ fe = dvb_attach(isl6423_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap, &anysee_isl6423_config);
break;
case ANYSEE_HW_507FA: /* 15 */
/* E30 Combo Plus */
/* E30 C Plus */
- if (dvb_usb_anysee_delsys) {
- /* enable DVB-T tuner on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0),
- 0x01);
- if (ret)
- goto error;
- } else {
- /* enable DVB-C tuner on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0),
- 0x01);
- if (ret)
- goto error;
- }
-
/* Try first attach TDA18212 silicon tuner on IOE[4], if that
* fails attach old simple PLL. */
@@ -746,8 +883,8 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
goto error;
/* attach tuner */
- fe = dvb_attach(tda18212_attach, adap->fe, &adap->dev->i2c_adap,
- &anysee_tda18212_config);
+ fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe,
+ &adap->dev->i2c_adap, &anysee_tda18212_config);
if (fe)
break;
@@ -757,12 +894,15 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
goto error;
/* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc0 >> 1),
- &adap->dev->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A);
+ fe = dvb_attach(dvb_pll_attach, adap->fe_adap[state->fe_id].fe,
+ (0xc0 >> 1), &adap->dev->i2c_adap,
+ DVB_PLL_SAMSUNG_DTOS403IH102A);
break;
case ANYSEE_HW_508TC: /* 18 */
+ case ANYSEE_HW_508PTC: /* 21 */
/* E7 TC */
+ /* E7 PTC */
/* enable tuner on IOE[4] */
ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 4), 0x10);
@@ -770,20 +910,22 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
goto error;
/* attach tuner */
- fe = dvb_attach(tda18212_attach, adap->fe, &adap->dev->i2c_adap,
- &anysee_tda18212_config);
+ fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe,
+ &adap->dev->i2c_adap, &anysee_tda18212_config);
break;
case ANYSEE_HW_508S2: /* 19 */
+ case ANYSEE_HW_508PS2: /* 22 */
/* E7 S2 */
+ /* E7 PS2 */
/* attach tuner */
- fe = dvb_attach(stv6110_attach, adap->fe,
+ fe = dvb_attach(stv6110_attach, adap->fe_adap[0].fe,
&anysee_stv6110_config, &adap->dev->i2c_adap);
if (fe) {
/* attach LNB controller */
- fe = dvb_attach(isl6423_attach, adap->fe,
+ fe = dvb_attach(isl6423_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, &anysee_isl6423_config);
}
@@ -885,6 +1027,23 @@ static struct dvb_usb_device_properties anysee_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 2,
+ .frontend_ctrl = anysee_frontend_ctrl,
+ .fe = {{
+ .streaming_ctrl = anysee_streaming_ctrl,
+ .frontend_attach = anysee_frontend_attach,
+ .tuner_attach = anysee_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 8,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = (16*512),
+ }
+ }
+ },
+ }, {
.streaming_ctrl = anysee_streaming_ctrl,
.frontend_attach = anysee_frontend_attach,
.tuner_attach = anysee_tuner_attach,
@@ -898,6 +1057,7 @@ static struct dvb_usb_device_properties anysee_properties = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/anysee.h b/drivers/media/dvb/dvb-usb/anysee.h
index a7673aa1e007..57ee500b8c0e 100644
--- a/drivers/media/dvb/dvb-usb/anysee.h
+++ b/drivers/media/dvb/dvb-usb/anysee.h
@@ -59,15 +59,18 @@ enum cmd {
struct anysee_state {
u8 hw; /* PCB ID */
u8 seq;
+ u8 fe_id:1; /* frondend ID */
};
-#define ANYSEE_HW_02 2 /* E30 */
-#define ANYSEE_HW_507CD 6 /* E30 Plus */
-#define ANYSEE_HW_507DC 10 /* E30 C Plus */
-#define ANYSEE_HW_507SI 11 /* E30 S2 Plus */
-#define ANYSEE_HW_507FA 15 /* E30 Combo Plus / E30 C Plus */
-#define ANYSEE_HW_508TC 18 /* E7 TC */
-#define ANYSEE_HW_508S2 19 /* E7 S2 */
+#define ANYSEE_HW_507T 2 /* E30 */
+#define ANYSEE_HW_507CD 6 /* E30 Plus */
+#define ANYSEE_HW_507DC 10 /* E30 C Plus */
+#define ANYSEE_HW_507SI 11 /* E30 S2 Plus */
+#define ANYSEE_HW_507FA 15 /* E30 Combo Plus / E30 C Plus */
+#define ANYSEE_HW_508TC 18 /* E7 TC */
+#define ANYSEE_HW_508S2 19 /* E7 S2 */
+#define ANYSEE_HW_508PTC 21 /* E7 PTC Plus */
+#define ANYSEE_HW_508PS2 22 /* E7 PS2 Plus */
#define REG_IOA 0x80 /* Port A (bit addressable) */
#define REG_IOB 0x90 /* Port B (bit addressable) */
diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c
index 2351077ff2b3..b77994967b9b 100644
--- a/drivers/media/dvb/dvb-usb/au6610.c
+++ b/drivers/media/dvb/dvb-usb/au6610.c
@@ -140,9 +140,9 @@ static struct zl10353_config au6610_zl10353_config = {
static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
&adap->dev->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
return 0;
@@ -155,7 +155,7 @@ static struct qt1010_config au6610_qt1010_config = {
static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach,
- adap->fe, &adap->dev->i2c_adap,
+ adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&au6610_qt1010_config) == NULL ? -ENODEV : 0;
}
@@ -204,6 +204,8 @@ static struct dvb_usb_device_properties au6610_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = au6610_zl10353_frontend_attach,
.tuner_attach = au6610_qt1010_tuner_attach,
@@ -219,6 +221,7 @@ static struct dvb_usb_device_properties au6610_properties = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c
index 57e2444d51ab..bf67b4dfd82b 100644
--- a/drivers/media/dvb/dvb-usb/az6027.c
+++ b/drivers/media/dvb/dvb-usb/az6027.c
@@ -40,7 +40,6 @@ static const struct stb0899_s1_reg az6027_stb0899_s1_init_1[] = {
{ STB0899_DISRX_ST0 , 0x04 },
{ STB0899_DISRX_ST1 , 0x00 },
{ STB0899_DISPARITY , 0x00 },
- { STB0899_DISFIFO , 0x00 },
{ STB0899_DISSTATUS , 0x20 },
{ STB0899_DISF22 , 0x99 },
{ STB0899_DISF22RX , 0xa8 },
@@ -782,7 +781,6 @@ static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
u8 buf;
- int ret;
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct i2c_msg i2c_msg = {
@@ -800,17 +798,17 @@ static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
switch (voltage) {
case SEC_VOLTAGE_13:
buf = 1;
- ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
+ i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
break;
case SEC_VOLTAGE_18:
buf = 2;
- ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
+ i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
break;
case SEC_VOLTAGE_OFF:
buf = 0;
- ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
+ i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
break;
default:
@@ -910,16 +908,16 @@ static int az6027_frontend_attach(struct dvb_usb_adapter *adap)
az6027_frontend_reset(adap);
deb_info("adap = %p, dev = %p\n", adap, adap->dev);
- adap->fe = stb0899_attach(&az6027_stb0899_config, &adap->dev->i2c_adap);
+ adap->fe_adap[0].fe = stb0899_attach(&az6027_stb0899_config, &adap->dev->i2c_adap);
- if (adap->fe) {
+ if (adap->fe_adap[0].fe) {
deb_info("found STB0899 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb0899_config.demod_address);
- if (stb6100_attach(adap->fe, &az6027_stb6100_config, &adap->dev->i2c_adap)) {
+ if (stb6100_attach(adap->fe_adap[0].fe, &az6027_stb6100_config, &adap->dev->i2c_adap)) {
deb_info("found STB6100 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb6100_config.tuner_address);
- adap->fe->ops.set_voltage = az6027_set_voltage;
+ adap->fe_adap[0].fe->ops.set_voltage = az6027_set_voltage;
az6027_ci_init(adap);
} else {
- adap->fe = NULL;
+ adap->fe_adap[0].fe = NULL;
}
} else
warn("no front-end attached\n");
@@ -954,7 +952,6 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i = 0, j = 0, len = 0;
- int ret;
u16 index;
u16 value;
int length;
@@ -990,7 +987,7 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n
index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff));
value = msg[i].addr + (msg[i].len << 8);
length = msg[i + 1].len + 6;
- ret = az6027_usb_in_op(d, req, value, index, data, length);
+ az6027_usb_in_op(d, req, value, index, data, length);
len = msg[i + 1].len;
for (j = 0; j < len; j++)
msg[i + 1].buf[j] = data[j + 5];
@@ -1017,7 +1014,7 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n
index = 0x0;
value = msg[i].addr;
length = msg[i].len + 6;
- ret = az6027_usb_in_op(d, req, value, index, data, length);
+ az6027_usb_in_op(d, req, value, index, data, length);
len = msg[i].len;
for (j = 0; j < len; j++)
msg[i].buf[j] = data[j + 5];
@@ -1106,6 +1103,8 @@ static struct dvb_usb_device_properties az6027_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = az6027_streaming_ctrl,
.frontend_attach = az6027_frontend_attach,
@@ -1120,6 +1119,7 @@ static struct dvb_usb_device_properties az6027_properties = {
}
}
},
+ }},
}
},
/*
diff --git a/drivers/media/dvb/dvb-usb/ce6230.c b/drivers/media/dvb/dvb-usb/ce6230.c
index 6d1a3041540d..57afb5a9157e 100644
--- a/drivers/media/dvb/dvb-usb/ce6230.c
+++ b/drivers/media/dvb/dvb-usb/ce6230.c
@@ -186,9 +186,9 @@ static struct zl10353_config ce6230_zl10353_config = {
static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
{
deb_info("%s:\n", __func__);
- adap->fe = dvb_attach(zl10353_attach, &ce6230_zl10353_config,
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &ce6230_zl10353_config,
&adap->dev->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
return 0;
}
@@ -214,7 +214,7 @@ static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
{
int ret;
deb_info("%s:\n", __func__);
- ret = dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap,
+ ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&ce6230_mxl5003s_config) == NULL ? -ENODEV : 0;
return ret;
}
@@ -273,6 +273,8 @@ static struct dvb_usb_device_properties ce6230_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = ce6230_zl10353_frontend_attach,
.tuner_attach = ce6230_mxl5003s_tuner_attach,
.stream = {
@@ -285,6 +287,7 @@ static struct dvb_usb_device_properties ce6230_properties = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c
index 16f2ce2bc15a..f9d905002ec9 100644
--- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c
+++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c
@@ -69,7 +69,7 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap)
char state[3];
int ret;
- adap->fe = cinergyt2_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = cinergyt2_fe_attach(adap->dev);
ret = dvb_usb_generic_rw(adap->dev, query, sizeof(query), state,
sizeof(state), 0);
@@ -198,6 +198,8 @@ static struct dvb_usb_device_properties cinergyt2_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cinergyt2_streaming_ctrl,
.frontend_attach = cinergyt2_frontend_attach,
@@ -212,6 +214,7 @@ static struct dvb_usb_device_properties cinergyt2_properties = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index acb5fb2d2e73..9f2a02c48377 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -347,7 +347,7 @@ static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d)
static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d)
{
- struct usb_data_stream_properties *p = &d->props.adapter[0].stream;
+ struct usb_data_stream_properties *p = &d->props.adapter[0].fe[0].stream;
const int timeout = 100;
const int junk_len = p->u.bulk.buffersize;
u8 *junk;
@@ -725,7 +725,7 @@ static struct max2165_config mygica_d689_max2165_cfg = {
/* Callbacks for DVB USB */
static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(simple_tuner_attach, adap->fe,
+ dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, 0x61,
TUNER_PHILIPS_FMD1216ME_MK3);
return 0;
@@ -733,27 +733,27 @@ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x61,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61,
NULL, DVB_PLL_THOMSON_DTT7579);
return 0;
}
static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, DVB_PLL_LG_Z201);
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_LG_Z201);
return 0;
}
static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x60,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60,
NULL, DVB_PLL_THOMSON_DTT7579);
return 0;
}
static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(simple_tuner_attach, adap->fe,
+ dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, 0x61, TUNER_LG_TDVS_H06XF);
return 0;
}
@@ -795,9 +795,9 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
};
/* FIXME: generalize & move to common area */
- adap->fe->callback = dvico_bluebird_xc2028_callback;
+ adap->fe_adap[0].fe->callback = dvico_bluebird_xc2028_callback;
- fe = dvb_attach(xc2028_attach, adap->fe, &cfg);
+ fe = dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &cfg);
if (fe == NULL || fe->ops.tuner_ops.set_config == NULL)
return -EIO;
@@ -808,7 +808,7 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(mxl5005s_attach, adap->fe,
+ dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, &aver_a868r_tuner);
return 0;
}
@@ -816,7 +816,7 @@ static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dvb_frontend *fe;
- fe = dvb_attach(mxl5005s_attach, adap->fe,
+ fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, &d680_dmb_tuner);
return (fe == NULL) ? -EIO : 0;
}
@@ -824,7 +824,7 @@ static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap)
static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dvb_frontend *fe;
- fe = dvb_attach(max2165_attach, adap->fe,
+ fe = dvb_attach(max2165_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, &mygica_d689_max2165_cfg);
return (fe == NULL) ? -EIO : 0;
}
@@ -837,8 +837,9 @@ static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1);
- if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config,
- &adap->dev->i2c_adap)) != NULL)
+ adap->fe_adap[0].fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
return -EIO;
@@ -851,8 +852,10 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
- if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config,
- &adap->dev->i2c_adap)) != NULL)
+ adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach,
+ &cxusb_lgdt3303_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
return -EIO;
@@ -860,9 +863,9 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config,
+ adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config,
&adap->dev->i2c_adap);
- if (adap->fe != NULL)
+ if (adap->fe_adap[0].fe != NULL)
return 0;
return -EIO;
@@ -876,8 +879,9 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
- if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config,
- &adap->dev->i2c_adap)) != NULL)
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_mt352_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
return -EIO;
@@ -890,11 +894,15 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
- if (((adap->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config,
- &adap->dev->i2c_adap)) != NULL) ||
- ((adap->fe = dvb_attach(zl10353_attach,
- &cxusb_zl10353_dee1601_config,
- &adap->dev->i2c_adap)) != NULL))
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_dee1601_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
+ return 0;
+
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach,
+ &cxusb_zl10353_dee1601_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
return -EIO;
@@ -917,9 +925,11 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1);
cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1);
- if ((adap->fe = dvb_attach(zl10353_attach,
- &cxusb_zl10353_xc3028_config_no_i2c_gate,
- &adap->dev->i2c_adap)) == NULL)
+ adap->fe_adap[0].fe =
+ dvb_attach(zl10353_attach,
+ &cxusb_zl10353_xc3028_config_no_i2c_gate,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) == NULL)
return -EIO;
/* try to determine if there is no IR decoder on the I2C bus */
@@ -1031,9 +1041,9 @@ static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
&cxusb_dualdig4_rev2_config);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
@@ -1084,15 +1094,15 @@ static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
struct i2c_adapter *tun_i2c =
- dib7000p_get_i2c_master(adap->fe,
+ dib7000p_get_i2c_master(adap->fe_adap[0].fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c,
&dib7070p_dib0070_config) == NULL)
return -ENODEV;
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7070_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7070_set_param_override;
return 0;
}
@@ -1108,14 +1118,16 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1);
cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1);
- if ((adap->fe = dvb_attach(zl10353_attach,
- &cxusb_zl10353_xc3028_config,
- &adap->dev->i2c_adap)) != NULL)
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach,
+ &cxusb_zl10353_xc3028_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
- if ((adap->fe = dvb_attach(mt352_attach,
- &cxusb_mt352_xc3028_config,
- &adap->dev->i2c_adap)) != NULL)
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach,
+ &cxusb_mt352_xc3028_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
return -EIO;
@@ -1150,7 +1162,7 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
usb_clear_halt(d->udev,
usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
usb_clear_halt(d->udev,
- usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint));
+ usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint));
/* Drain USB pipes to avoid hang after reboot */
for (n = 0; n < 5; n++) {
@@ -1172,8 +1184,8 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
msleep(100);
/* Attach frontend */
- adap->fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap);
- if (adap->fe == NULL)
+ adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap);
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
@@ -1207,7 +1219,7 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap)
usb_clear_halt(d->udev,
usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
usb_clear_halt(d->udev,
- usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint));
+ usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint));
/* Reset the tuner */
@@ -1223,9 +1235,9 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap)
msleep(100);
/* Attach frontend */
- adap->fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg,
+ adap->fe_adap[0].fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg,
&d->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
@@ -1383,6 +1395,8 @@ static struct dvb_usb_device_properties cxusb_medion_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_cx22702_frontend_attach,
.tuner_attach = cxusb_fmd1216me_tuner_attach,
@@ -1397,7 +1411,7 @@ static struct dvb_usb_device_properties cxusb_medion_properties = {
}
}
},
-
+ }},
},
},
.power_ctrl = cxusb_power_ctrl,
@@ -1429,6 +1443,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_lgdt3303_frontend_attach,
.tuner_attach = cxusb_lgh064f_tuner_attach,
@@ -1444,6 +1460,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = {
}
}
},
+ }},
},
},
@@ -1483,6 +1500,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_dee1601_frontend_attach,
.tuner_attach = cxusb_dee1601_tuner_attach,
@@ -1497,6 +1516,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = {
}
}
},
+ }},
},
},
@@ -1544,6 +1564,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_mt352_frontend_attach,
.tuner_attach = cxusb_lgz201_tuner_attach,
@@ -1559,6 +1581,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = {
}
}
},
+ }},
},
},
.power_ctrl = cxusb_bluebird_power_ctrl,
@@ -1596,6 +1619,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_mt352_frontend_attach,
.tuner_attach = cxusb_dtt7579_tuner_attach,
@@ -1611,6 +1636,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
}
}
},
+ }},
},
},
.power_ctrl = cxusb_bluebird_power_ctrl,
@@ -1645,6 +1671,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_dualdig4_frontend_attach,
.tuner_attach = cxusb_dvico_xc3028_tuner_attach,
@@ -1659,6 +1687,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = {
}
}
},
+ }},
},
},
@@ -1695,6 +1724,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_nano2_frontend_attach,
.tuner_attach = cxusb_dvico_xc3028_tuner_attach,
@@ -1709,6 +1740,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = {
}
}
},
+ }},
},
},
@@ -1747,6 +1779,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_nano2_frontend_attach,
.tuner_attach = cxusb_dvico_xc3028_tuner_attach,
@@ -1761,6 +1795,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope
}
}
},
+ }},
},
},
@@ -1796,6 +1831,8 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_aver_streaming_ctrl,
.frontend_attach = cxusb_aver_lgdt3303_frontend_attach,
.tuner_attach = cxusb_mxl5003s_tuner_attach,
@@ -1810,7 +1847,7 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = {
}
}
},
-
+ }},
},
},
.power_ctrl = cxusb_aver_power_ctrl,
@@ -1839,10 +1876,12 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = {
.num_adapters = 1,
.adapter = {
{
+ .size_of_priv = sizeof(struct dib0700_adapter_state),
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_dualdig4_rev2_frontend_attach,
.tuner_attach = cxusb_dualdig4_rev2_tuner_attach,
- .size_of_priv = sizeof(struct dib0700_adapter_state),
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
@@ -1854,6 +1893,7 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = {
}
}
},
+ }},
},
},
@@ -1889,6 +1929,8 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_d680_dmb_streaming_ctrl,
.frontend_attach = cxusb_d680_dmb_frontend_attach,
.tuner_attach = cxusb_d680_dmb_tuner_attach,
@@ -1904,6 +1946,7 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = {
}
}
},
+ }},
},
},
@@ -1940,6 +1983,8 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_d680_dmb_streaming_ctrl,
.frontend_attach = cxusb_mygica_d689_frontend_attach,
.tuner_attach = cxusb_mygica_d689_tuner_attach,
@@ -1955,6 +2000,7 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = {
}
}
},
+ }},
},
},
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 5eb91b4f8fd0..156cbfc9c79d 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -30,6 +30,11 @@ int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
struct dib0700_state *st = d->priv;
int ret;
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
+
ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
REQUEST_GET_VERSION,
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
@@ -46,6 +51,7 @@ int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
if (fwtype != NULL)
*fwtype = (st->buf[12] << 24) | (st->buf[13] << 16) |
(st->buf[14] << 8) | st->buf[15];
+ mutex_unlock(&d->usb_mutex);
return ret;
}
@@ -108,7 +114,12 @@ int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen
int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val)
{
struct dib0700_state *st = d->priv;
- s16 ret;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
st->buf[0] = REQUEST_SET_GPIO;
st->buf[1] = gpio;
@@ -116,6 +127,7 @@ int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_
ret = dib0700_ctrl_wr(d, st->buf, 3);
+ mutex_unlock(&d->usb_mutex);
return ret;
}
@@ -125,6 +137,11 @@ static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
int ret;
if (st->fw_version >= 0x10201) {
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
+
st->buf[0] = REQUEST_SET_USB_XFER_LEN;
st->buf[1] = (nb_ts_packets >> 8) & 0xff;
st->buf[2] = nb_ts_packets & 0xff;
@@ -132,6 +149,7 @@ static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets);
ret = dib0700_ctrl_wr(d, st->buf, 3);
+ mutex_unlock(&d->usb_mutex);
} else {
deb_info("this firmware does not allow to change the USB xfer len\n");
ret = -EIO;
@@ -208,6 +226,10 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
} else {
/* Write request */
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
st->buf[0] = REQUEST_NEW_I2C_WRITE;
st->buf[1] = msg[i].addr << 1;
st->buf[2] = (en_start << 7) | (en_stop << 6) |
@@ -227,6 +249,7 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
USB_TYPE_VENDOR | USB_DIR_OUT,
0, 0, st->buf, msg[i].len + 4,
USB_CTRL_GET_TIMEOUT);
+ mutex_unlock(&d->usb_mutex);
if (result < 0) {
deb_info("i2c write error (status = %d)\n", result);
break;
@@ -249,6 +272,10 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
for (i = 0; i < num; i++) {
/* fill in the address */
@@ -279,6 +306,7 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
break;
}
}
+ mutex_unlock(&d->usb_mutex);
mutex_unlock(&d->i2c_mutex);
return i;
@@ -337,7 +365,12 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
u16 pll_loopdiv, u16 free_div, u16 dsuScaler)
{
struct dib0700_state *st = d->priv;
- s16 ret;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
st->buf[0] = REQUEST_SET_CLOCK;
st->buf[1] = (en_pll << 7) | (pll_src << 6) |
@@ -352,6 +385,7 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
st->buf[9] = dsuScaler & 0xff; /* LSB */
ret = dib0700_ctrl_wr(d, st->buf, 10);
+ mutex_unlock(&d->usb_mutex);
return ret;
}
@@ -360,10 +394,16 @@ int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
{
struct dib0700_state *st = d->priv;
u16 divider;
+ int ret;
if (scl_kHz == 0)
return -EINVAL;
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
+
st->buf[0] = REQUEST_SET_I2C_PARAM;
divider = (u16) (30000 / scl_kHz);
st->buf[1] = 0;
@@ -379,7 +419,11 @@ int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
deb_info("setting I2C speed: %04x %04x %04x (%d kHz).",
(st->buf[2] << 8) | (st->buf[3]), (st->buf[4] << 8) |
st->buf[5], (st->buf[6] << 8) | st->buf[7], scl_kHz);
- return dib0700_ctrl_wr(d, st->buf, 8);
+
+ ret = dib0700_ctrl_wr(d, st->buf, 8);
+ mutex_unlock(&d->usb_mutex);
+
+ return ret;
}
@@ -484,13 +528,13 @@ int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw
for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters;
adap_num++) {
if (fw_version >= 0x10201) {
- dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = 188*nb_packet_buffer_size;
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 188*nb_packet_buffer_size;
} else {
/* for fw version older than 1.20.1,
* the buffersize has to be n times 512 */
- dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512;
- if (dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize < 512)
- dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = 512;
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512;
+ if (dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize < 512)
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 512;
}
}
}
@@ -515,6 +559,11 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
}
}
+ if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
+
st->buf[0] = REQUEST_ENABLE_VIDEO;
/* this bit gives a kind of command,
* rather than enabling something or not */
@@ -530,25 +579,28 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);
st->channel_state &= ~0x3;
- if ((adap->stream.props.endpoint != 2)
- && (adap->stream.props.endpoint != 3)) {
- deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->stream.props.endpoint);
+ if ((adap->fe_adap[0].stream.props.endpoint != 2)
+ && (adap->fe_adap[0].stream.props.endpoint != 3)) {
+ deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->fe_adap[0].stream.props.endpoint);
if (onoff)
st->channel_state |= 1 << (adap->id);
else
st->channel_state |= 1 << ~(adap->id);
} else {
if (onoff)
- st->channel_state |= 1 << (adap->stream.props.endpoint-2);
+ st->channel_state |= 1 << (adap->fe_adap[0].stream.props.endpoint-2);
else
- st->channel_state |= 1 << (3-adap->stream.props.endpoint);
+ st->channel_state |= 1 << (3-adap->fe_adap[0].stream.props.endpoint);
}
st->buf[2] |= st->channel_state;
deb_info("data for streaming: %x %x\n", st->buf[1], st->buf[2]);
- return dib0700_ctrl_wr(adap->dev, st->buf, 4);
+ ret = dib0700_ctrl_wr(adap->dev, st->buf, 4);
+ mutex_unlock(&adap->dev->usb_mutex);
+
+ return ret;
}
int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
@@ -557,6 +609,11 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
struct dib0700_state *st = d->priv;
int new_proto, ret;
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
+
st->buf[0] = REQUEST_SET_RC;
st->buf[1] = 0;
st->buf[2] = 0;
@@ -567,23 +624,29 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
else if (rc_type == RC_TYPE_NEC)
new_proto = 0;
else if (rc_type == RC_TYPE_RC6) {
- if (st->fw_version < 0x10200)
- return -EINVAL;
+ if (st->fw_version < 0x10200) {
+ ret = -EINVAL;
+ goto out;
+ }
new_proto = 2;
- } else
- return -EINVAL;
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
st->buf[1] = new_proto;
ret = dib0700_ctrl_wr(d, st->buf, 3);
if (ret < 0) {
err("ir protocol setup failed");
- return ret;
+ goto out;
}
d->props.rc.core.protocol = rc_type;
+out:
+ mutex_unlock(&d->usb_mutex);
return ret;
}
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index c519ad5eb731..f313182eb9d5 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -17,6 +17,7 @@
#include "mt2266.h"
#include "tuner-xc2028.h"
#include "xc5000.h"
+#include "xc4000.h"
#include "s5h1411.h"
#include "dib0070.h"
#include "dib0090.h"
@@ -100,7 +101,7 @@ static int bristol_frontend_attach(struct dvb_usb_adapter *adap)
}
}
st->mt2060_if1[adap->id] = 1220;
- return (adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap,
+ return (adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap,
(10 + adap->id) << 1, &bristol_dib3000mc_config[adap->id])) == NULL ? -ENODEV : 0;
}
@@ -117,15 +118,16 @@ static int eeprom_read(struct i2c_adapter *adap,u8 adrs,u8 *pval)
static int bristol_tuner_attach(struct dvb_usb_adapter *adap)
{
struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap;
- struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1);
+ struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1);
s8 a;
int if1=1220;
if (adap->dev->udev->descriptor.idVendor == cpu_to_le16(USB_VID_HAUPPAUGE) &&
adap->dev->udev->descriptor.idProduct == cpu_to_le16(USB_PID_HAUPPAUGE_NOVA_T_500_2)) {
if (!eeprom_read(prim_i2c,0x59 + adap->id,&a)) if1=1220+a;
}
- return dvb_attach(mt2060_attach,adap->fe, tun_i2c,&bristol_mt2060_config[adap->id],
- if1) == NULL ? -ENODEV : 0;
+ return dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c,
+ &bristol_mt2060_config[adap->id], if1) == NULL ?
+ -ENODEV : 0;
}
/* STK7700D: Pinnacle/Terratec/Hauppauge Dual DVB-T Diversity */
@@ -278,10 +280,12 @@ static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap)
}
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1),
- &stk7700d_dib7000p_mt2266_config[adap->id]);
+ adap->fe_adap[0].fe =
+ dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,
+ 0x80 + (adap->id << 1),
+ &stk7700d_dib7000p_mt2266_config[adap->id]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap)
@@ -305,17 +309,19 @@ static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap)
}
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1),
- &stk7700d_dib7000p_mt2266_config[adap->id]);
+ adap->fe_adap[0].fe =
+ dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,
+ 0x80 + (adap->id << 1),
+ &stk7700d_dib7000p_mt2266_config[adap->id]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap)
{
struct i2c_adapter *tun_i2c;
- tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
- return dvb_attach(mt2266_attach, adap->fe, tun_i2c,
+ tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ return dvb_attach(mt2266_attach, adap->fe_adap[0].fe, tun_i2c,
&stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0;
}
@@ -395,8 +401,8 @@ static int stk7700ph_xc3028_callback(void *ptr, int component,
switch (command) {
case XC2028_TUNER_RESET:
/* Send the tuner in then out of reset */
- dib7000p_set_gpio(adap->fe, 8, 0, 0); msleep(10);
- dib7000p_set_gpio(adap->fe, 8, 0, 1);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0); msleep(10);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1);
break;
case XC2028_RESET_CLK:
break;
@@ -446,25 +452,25 @@ static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
&stk7700ph_dib7700_xc3028_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap)
{
struct i2c_adapter *tun_i2c;
- tun_i2c = dib7000p_get_i2c_master(adap->fe,
+ tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
stk7700ph_xc3028_config.i2c_adap = tun_i2c;
/* FIXME: generalize & move to common area */
- adap->fe->callback = stk7700ph_xc3028_callback;
+ adap->fe_adap[0].fe->callback = stk7700ph_xc3028_callback;
- return dvb_attach(xc2028_attach, adap->fe, &stk7700ph_xc3028_config)
+ return dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &stk7700ph_xc3028_config)
== NULL ? -ENODEV : 0;
}
@@ -684,12 +690,12 @@ static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap)
st->mt2060_if1[0] = 1220;
if (dib7000pc_detection(&adap->dev->i2c_adap)) {
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config);
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config);
st->is_dib7000pc = 1;
} else
- adap->fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config);
+ adap->fe_adap[0].fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static struct mt2060_config stk7700p_mt2060_config = {
@@ -708,11 +714,11 @@ static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap)
if (!eeprom_read(prim_i2c,0x58,&a)) if1=1220+a;
}
if (st->is_dib7000pc)
- tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
else
- tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ tun_i2c = dib7000m_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
- return dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk7700p_mt2060_config,
+ return dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk7700p_mt2060_config,
if1) == NULL ? -ENODEV : 0;
}
@@ -842,33 +848,33 @@ static int dib7770_set_param_override(struct dvb_frontend *fe,
static int dib7770p_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe,
+ struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
- &dib7770p_dib0070_config) == NULL)
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c,
+ &dib7770p_dib0070_config) == NULL)
return -ENODEV;
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7770_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7770_set_param_override;
return 0;
}
static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
if (adap->id == 0) {
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, &dib7070p_dib0070_config[0]) == NULL)
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[0]) == NULL)
return -ENODEV;
} else {
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, &dib7070p_dib0070_config[1]) == NULL)
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[1]) == NULL)
return -ENODEV;
}
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7070_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7070_set_param_override;
return 0;
}
@@ -877,26 +883,26 @@ static int stk7700p_pid_filter(struct dvb_usb_adapter *adapter, int index,
{
struct dib0700_state *st = adapter->dev->priv;
if (st->is_dib7000pc)
- return dib7000p_pid_filter(adapter->fe, index, pid, onoff);
- return dib7000m_pid_filter(adapter->fe, index, pid, onoff);
+ return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff);
+ return dib7000m_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff);
}
static int stk7700p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
{
struct dib0700_state *st = adapter->dev->priv;
if (st->is_dib7000pc)
- return dib7000p_pid_filter_ctrl(adapter->fe, onoff);
- return dib7000m_pid_filter_ctrl(adapter->fe, onoff);
+ return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff);
+ return dib7000m_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff);
}
static int stk70x0p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff)
{
- return dib7000p_pid_filter(adapter->fe, index, pid, onoff);
+ return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff);
}
static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
{
- return dib7000p_pid_filter_ctrl(adapter->fe, onoff);
+ return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff);
}
static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = {
@@ -954,9 +960,9 @@ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
&dib7070p_dib7000p_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* STK7770P */
@@ -1006,9 +1012,9 @@ static int stk7770p_frontend_attach(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
&dib7770p_dib7000p_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* DIB807x generic */
@@ -1224,34 +1230,34 @@ static int dib807x_set_param_override(struct dvb_frontend *fe,
static int dib807x_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe,
+ struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
if (adap->id == 0) {
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c,
&dib807x_dib0070_config[0]) == NULL)
return -ENODEV;
} else {
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c,
&dib807x_dib0070_config[1]) == NULL)
return -ENODEV;
}
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib807x_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib807x_set_param_override;
return 0;
}
static int stk80xx_pid_filter(struct dvb_usb_adapter *adapter, int index,
u16 pid, int onoff)
{
- return dib8000_pid_filter(adapter->fe, index, pid, onoff);
+ return dib8000_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff);
}
static int stk80xx_pid_filter_ctrl(struct dvb_usb_adapter *adapter,
int onoff)
{
- return dib8000_pid_filter_ctrl(adapter->fe, onoff);
+ return dib8000_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff);
}
/* STK807x */
@@ -1275,10 +1281,10 @@ static int stk807x_frontend_attach(struct dvb_usb_adapter *adap)
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
0x80);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
&dib807x_dib8000_config[0]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* STK807xPVR */
@@ -1304,10 +1310,10 @@ static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap)
/* initialize IC 0 */
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x80);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
&dib807x_dib8000_config[0]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap)
@@ -1315,10 +1321,10 @@ static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap)
/* initialize IC 1 */
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x82);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82,
+ adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82,
&dib807x_dib8000_config[1]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* STK8096GP */
@@ -1545,13 +1551,13 @@ static int dib8096_set_param_override(struct dvb_frontend *fe,
static int dib809x_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL)
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL)
return -ENODEV;
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override;
return 0;
}
@@ -1574,30 +1580,30 @@ static int stk809x_frontend_attach(struct dvb_usb_adapter *adap)
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]);
+ adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int nim8096md_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
struct i2c_adapter *tun_i2c;
- struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe, 1);
+ struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe_adap[0].fe, 1);
if (fe_slave) {
tun_i2c = dib8000_get_i2c_master(fe_slave, DIBX000_I2C_INTERFACE_TUNER, 1);
if (dvb_attach(dib0090_register, fe_slave, tun_i2c, &dib809x_dib0090_config) == NULL)
return -ENODEV;
- fe_slave->dvb = adap->fe->dvb;
+ fe_slave->dvb = adap->fe_adap[0].fe->dvb;
fe_slave->ops.tuner_ops.set_params = dib8096_set_param_override;
}
- tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL)
+ tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL)
return -ENODEV;
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override;
return 0;
}
@@ -1625,12 +1631,12 @@ static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap)
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]);
- if (adap->fe == NULL)
+ adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]);
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
fe_slave = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]);
- dib8000_set_slave_frontend(adap->fe, fe_slave);
+ dib8000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave);
return fe_slave == NULL ? -ENODEV : 0;
}
@@ -1638,12 +1644,12 @@ static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap)
/* STK9090M */
static int dib90x0_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff)
{
- return dib9000_fw_pid_filter(adapter->fe, index, pid, onoff);
+ return dib9000_fw_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff);
}
static int dib90x0_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
{
- return dib9000_fw_pid_filter_ctrl(adapter->fe, onoff);
+ return dib9000_fw_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff);
}
static int dib90x0_tuner_reset(struct dvb_frontend *fe, int onoff)
@@ -1855,15 +1861,15 @@ static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap)
stk9090m_config.microcode_B_fe_size = state->frontend_firmware->size;
stk9090m_config.microcode_B_fe_buffer = state->frontend_firmware->data;
- adap->fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config);
+ adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int dib9090_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *state = adap->priv;
- struct i2c_adapter *i2c = dib9000_get_tuner_interface(adap->fe);
+ struct i2c_adapter *i2c = dib9000_get_tuner_interface(adap->fe_adap[0].fe);
u16 data_dib190[10] = {
1, 0x1374,
2, 0x01a2,
@@ -1872,13 +1878,13 @@ static int dib9090_tuner_attach(struct dvb_usb_adapter *adap)
8, 0x0486,
};
- if (dvb_attach(dib0090_fw_register, adap->fe, i2c, &dib9090_dib0090_config) == NULL)
+ if (dvb_attach(dib0090_fw_register, adap->fe_adap[0].fe, i2c, &dib9090_dib0090_config) == NULL)
return -ENODEV;
- i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0);
+ i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0);
if (dib01x0_pmu_update(i2c, data_dib190, 10) != 0)
return -ENODEV;
dib0700_set_i2c_speed(adap->dev, 2000);
- if (dib9000_firmware_post_pll_init(adap->fe) < 0)
+ if (dib9000_firmware_post_pll_init(adap->fe_adap[0].fe) < 0)
return -ENODEV;
release_firmware(state->frontend_firmware);
return 0;
@@ -1924,16 +1930,16 @@ static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap)
nim9090md_config[1].microcode_B_fe_buffer = state->frontend_firmware->data;
dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, 0x80);
- adap->fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]);
+ adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
- i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0);
+ i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0);
dib9000_i2c_enumeration(i2c, 1, 0x12, 0x82);
fe_slave = dvb_attach(dib9000_attach, i2c, 0x82, &nim9090md_config[1]);
- dib9000_set_slave_frontend(adap->fe, fe_slave);
+ dib9000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave);
return fe_slave == NULL ? -ENODEV : 0;
}
@@ -1950,26 +1956,26 @@ static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap)
0, 0x00ef,
8, 0x0406,
};
- i2c = dib9000_get_tuner_interface(adap->fe);
- if (dvb_attach(dib0090_fw_register, adap->fe, i2c, &nim9090md_dib0090_config[0]) == NULL)
+ i2c = dib9000_get_tuner_interface(adap->fe_adap[0].fe);
+ if (dvb_attach(dib0090_fw_register, adap->fe_adap[0].fe, i2c, &nim9090md_dib0090_config[0]) == NULL)
return -ENODEV;
- i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0);
+ i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0);
if (dib01x0_pmu_update(i2c, data_dib190, 10) < 0)
return -ENODEV;
dib0700_set_i2c_speed(adap->dev, 2000);
- if (dib9000_firmware_post_pll_init(adap->fe) < 0)
+ if (dib9000_firmware_post_pll_init(adap->fe_adap[0].fe) < 0)
return -ENODEV;
- fe_slave = dib9000_get_slave_frontend(adap->fe, 1);
+ fe_slave = dib9000_get_slave_frontend(adap->fe_adap[0].fe, 1);
if (fe_slave != NULL) {
- i2c = dib9000_get_component_bus_interface(adap->fe);
+ i2c = dib9000_get_component_bus_interface(adap->fe_adap[0].fe);
dib9000_set_i2c_adapter(fe_slave, i2c);
i2c = dib9000_get_tuner_interface(fe_slave);
if (dvb_attach(dib0090_fw_register, fe_slave, i2c, &nim9090md_dib0090_config[1]) == NULL)
return -ENODEV;
- fe_slave->dvb = adap->fe->dvb;
- dib9000_fw_set_component_bus_speed(adap->fe, 2000);
+ fe_slave->dvb = adap->fe_adap[0].fe->dvb;
+ dib9000_fw_set_component_bus_speed(adap->fe_adap[0].fe, 2000);
if (dib9000_firmware_post_pll_init(fe_slave) < 0)
return -ENODEV;
}
@@ -2392,23 +2398,23 @@ static int nim7090_frontend_attach(struct dvb_usb_adapter *adap)
err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__);
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config);
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int nim7090_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe);
+ struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &nim7090_dib0090_config) == NULL)
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &nim7090_dib0090_config) == NULL)
return -ENODEV;
- dib7000p_set_gpio(adap->fe, 8, 0, 1);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1);
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup;
return 0;
}
@@ -2438,11 +2444,11 @@ static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap)
}
dib0700_set_i2c_speed(adap->dev, 340);
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]);
- if (adap->fe == NULL)
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]);
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
- dib7090_slave_reset(adap->fe);
+ dib7090_slave_reset(adap->fe_adap[0].fe);
return 0;
}
@@ -2451,50 +2457,50 @@ static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap)
{
struct i2c_adapter *i2c;
- if (adap->dev->adapter[0].fe == NULL) {
+ if (adap->dev->adapter[0].fe_adap[0].fe == NULL) {
err("the master dib7090 has to be initialized first");
return -ENODEV; /* the master device has not been initialized */
}
- i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1);
+ i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1);
if (dib7000p_i2c_enumeration(i2c, 1, 0x10, &tfe7090pvr_dib7000p_config[1]) != 0) {
err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__);
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]);
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]);
dib0700_set_i2c_speed(adap->dev, 200);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int tfe7090pvr_tuner0_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe);
+ struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL)
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL)
return -ENODEV;
- dib7000p_set_gpio(adap->fe, 8, 0, 1);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1);
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup;
return 0;
}
static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe);
+ struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL)
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL)
return -ENODEV;
- dib7000p_set_gpio(adap->fe, 8, 0, 1);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1);
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup;
return 0;
}
@@ -2554,14 +2560,14 @@ static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]);
- return adap->fe == NULL ? -ENODEV : 0;
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]);
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap)
{
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]);
- return adap->fe == NULL ? -ENODEV : 0;
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]);
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* S5H1411 */
@@ -2616,9 +2622,9 @@ static int s5h1411_frontend_attach(struct dvb_usb_adapter *adap)
dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1);
/* GPIOs are initialized, do the attach */
- adap->fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config,
+ adap->fe_adap[0].fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config,
&adap->dev->i2c_adap);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int dib0700_xc5000_tuner_callback(void *priv, int component,
@@ -2648,13 +2654,163 @@ static struct xc5000_config s5h1411_xc5000_tunerconfig = {
static int xc5000_tuner_attach(struct dvb_usb_adapter *adap)
{
/* FIXME: generalize & move to common area */
- adap->fe->callback = dib0700_xc5000_tuner_callback;
+ adap->fe_adap[0].fe->callback = dib0700_xc5000_tuner_callback;
- return dvb_attach(xc5000_attach, adap->fe, &adap->dev->i2c_adap,
+ return dvb_attach(xc5000_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&s5h1411_xc5000_tunerconfig)
== NULL ? -ENODEV : 0;
}
+static int dib0700_xc4000_tuner_callback(void *priv, int component,
+ int command, int arg)
+{
+ struct dvb_usb_adapter *adap = priv;
+
+ if (command == XC4000_TUNER_RESET) {
+ /* Reset the tuner */
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0);
+ msleep(10);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1);
+ } else {
+ err("xc4000: unknown tuner callback command: %d\n", command);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct dibx000_agc_config stk7700p_7000p_xc4000_agc_config = {
+ .band_caps = BAND_UHF | BAND_VHF,
+ .setup = 0x64,
+ .inv_gain = 0x02c8,
+ .time_stabiliz = 0x15,
+ .alpha_level = 0x00,
+ .thlock = 0x76,
+ .wbd_inv = 0x01,
+ .wbd_ref = 0x0b33,
+ .wbd_sel = 0x00,
+ .wbd_alpha = 0x02,
+ .agc1_max = 0x00,
+ .agc1_min = 0x00,
+ .agc2_max = 0x9b26,
+ .agc2_min = 0x26ca,
+ .agc1_pt1 = 0x00,
+ .agc1_pt2 = 0x00,
+ .agc1_pt3 = 0x00,
+ .agc1_slope1 = 0x00,
+ .agc1_slope2 = 0x00,
+ .agc2_pt1 = 0x00,
+ .agc2_pt2 = 0x80,
+ .agc2_slope1 = 0x1d,
+ .agc2_slope2 = 0x1d,
+ .alpha_mant = 0x11,
+ .alpha_exp = 0x1b,
+ .beta_mant = 0x17,
+ .beta_exp = 0x33,
+ .perform_agc_softsplit = 0x00,
+};
+
+static struct dibx000_bandwidth_config stk7700p_xc4000_pll_config = {
+ 60000, 30000, /* internal, sampling */
+ 1, 8, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass */
+ 0, 0, 1, 1, 0, /* misc: refdiv, bypclk_div, IO_CLK_en_core, */
+ /* ADClkSrc, modulo */
+ (3 << 14) | (1 << 12) | 524, /* sad_cfg: refsel, sel, freq_15k */
+ 39370534, /* ifreq */
+ 20452225, /* timf */
+ 30000000 /* xtal */
+};
+
+/* FIXME: none of these inputs are validated yet */
+static struct dib7000p_config pctv_340e_config = {
+ .output_mpeg2_in_188_bytes = 1,
+
+ .agc_config_count = 1,
+ .agc = &stk7700p_7000p_xc4000_agc_config,
+ .bw = &stk7700p_xc4000_pll_config,
+
+ .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS,
+};
+
+/* PCTV 340e GPIOs map:
+ dib0700:
+ GPIO2 - CX25843 sleep
+ GPIO3 - CS5340 reset
+ GPIO5 - IRD
+ GPIO6 - Power Supply
+ GPIO8 - LNA (1=off 0=on)
+ GPIO10 - CX25843 reset
+ dib7000:
+ GPIO8 - xc4000 reset
+ */
+static int pctv340e_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_state *st = adap->dev->priv;
+
+ /* Power Supply on */
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0);
+ msleep(50);
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+ msleep(100); /* Allow power supply to settle before probing */
+
+ /* cx25843 reset */
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+ msleep(1); /* cx25843 datasheet say 350us required */
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+
+ /* LNA off for now */
+ dib0700_set_gpio(adap->dev, GPIO8, GPIO_OUT, 1);
+
+ /* Put the CX25843 to sleep for now since we're in digital mode */
+ dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1);
+
+ /* FIXME: not verified yet */
+ dib0700_ctrl_clock(adap->dev, 72, 1);
+
+ msleep(500);
+
+ if (dib7000pc_detection(&adap->dev->i2c_adap) == 0) {
+ /* Demodulator not found for some reason? */
+ return -ENODEV;
+ }
+
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x12,
+ &pctv_340e_config);
+ st->is_dib7000pc = 1;
+
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
+}
+
+static struct xc4000_config dib7000p_xc4000_tunerconfig = {
+ .i2c_address = 0x61,
+ .default_pm = 1,
+ .dvb_amplitude = 0,
+ .set_smoothedcvbs = 0,
+ .if_khz = 5400
+};
+
+static int xc4000_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct i2c_adapter *tun_i2c;
+
+ /* The xc4000 is not on the main i2c bus */
+ tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe,
+ DIBX000_I2C_INTERFACE_TUNER, 1);
+ if (tun_i2c == NULL) {
+ printk(KERN_ERR "Could not reach tuner i2c bus\n");
+ return 0;
+ }
+
+ /* Setup the reset callback */
+ adap->fe_adap[0].fe->callback = dib0700_xc4000_tuner_callback;
+
+ return dvb_attach(xc4000_attach, adap->fe_adap[0].fe, tun_i2c,
+ &dib7000p_xc4000_tunerconfig)
+ == NULL ? -ENODEV : 0;
+}
+
static struct lgdt3305_config hcw_lgdt3305_config = {
.i2c_addr = 0x0e,
.mpeg_mode = LGDT3305_MPEG_PARALLEL,
@@ -2706,16 +2862,16 @@ static int lgdt3305_frontend_attach(struct dvb_usb_adapter *adap)
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
msleep(30);
- adap->fe = dvb_attach(lgdt3305_attach,
+ adap->fe_adap[0].fe = dvb_attach(lgdt3305_attach,
&hcw_lgdt3305_config,
&adap->dev->i2c_adap);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int mxl5007t_tuner_attach(struct dvb_usb_adapter *adap)
{
- return dvb_attach(mxl5007t_attach, adap->fe,
+ return dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, 0x60,
&hcw_mxl5007t_config) == NULL ? -ENODEV : 0;
}
@@ -2802,6 +2958,8 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090PVR) },
{ USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2) },
/* 75 */{ USB_DEVICE(USB_VID_MEDION, USB_PID_CREATIX_CTX1921) },
+ { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV340E) },
+ { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV340E_SE) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -2836,6 +2994,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk7700p_pid_filter,
@@ -2844,6 +3004,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = stk7700p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ }},
},
},
@@ -2897,15 +3058,21 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = bristol_frontend_attach,
.tuner_attach = bristol_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ }},
}, {
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = bristol_frontend_attach,
.tuner_attach = bristol_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
+ }},
}
},
@@ -2931,6 +3098,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -2939,7 +3108,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = stk7700d_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ }},
}, {
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -2948,6 +3120,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = stk7700d_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
+ }},
}
},
@@ -2990,6 +3163,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -2998,6 +3173,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = stk7700d_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ }},
},
},
@@ -3032,6 +3208,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3040,7 +3218,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
},
},
@@ -3108,6 +3286,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3116,7 +3296,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
},
},
@@ -3152,6 +3332,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3160,9 +3342,11 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
}, {
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3171,7 +3355,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
}
},
@@ -3220,6 +3404,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3228,9 +3414,11 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
}, {
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3239,7 +3427,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
}
},
@@ -3267,6 +3455,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3275,7 +3465,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = stk7700ph_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct
dib0700_adapter_state),
},
@@ -3335,11 +3525,13 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = s5h1411_frontend_attach,
.tuner_attach = xc5000_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct
dib0700_adapter_state),
},
@@ -3371,11 +3563,13 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = lgdt3305_frontend_attach,
.tuner_attach = mxl5007t_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct
dib0700_adapter_state),
},
@@ -3397,6 +3591,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3405,7 +3601,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7770p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3447,6 +3643,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk80xx_pid_filter,
@@ -3455,7 +3653,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib807x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3491,6 +3689,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk80xx_pid_filter,
@@ -3499,11 +3699,13 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib807x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk80xx_pid_filter,
@@ -3512,7 +3714,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib807x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3540,6 +3742,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3549,7 +3753,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib809x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3577,6 +3781,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3586,7 +3792,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib9090_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3614,6 +3820,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3623,7 +3831,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = nim8096md_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3651,6 +3859,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3660,7 +3870,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = nim9090md_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3688,6 +3898,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3697,7 +3909,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = nim7090_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3725,6 +3937,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3734,11 +3948,13 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = tfe7090pvr_tuner0_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3748,7 +3964,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = tfe7090pvr_tuner1_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3772,6 +3988,43 @@ struct dvb_usb_device_properties dib0700_devices[] = {
RC_TYPE_NEC,
.change_protocol = dib0700_change_protocol,
},
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {{
+ .frontend_attach = pctv340e_frontend_attach,
+ .tuner_attach = xc4000_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ }},
+ .size_of_priv = sizeof(struct
+ dib0700_adapter_state),
+ },
+ },
+
+ .num_device_descs = 2,
+ .devices = {
+ { "Pinnacle PCTV 340e HD Pro USB Stick",
+ { &dib0700_usb_id_table[76], NULL },
+ { NULL },
+ },
+ { "Pinnacle PCTV Hybrid Stick Solo",
+ { &dib0700_usb_id_table[77], NULL },
+ { NULL },
+ },
+ },
+ .rc.core = {
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
+ .module_name = "dib0700",
+ .rc_query = dib0700_rc_query_old_firmware,
+ .allowed_protos = RC_TYPE_RC5 |
+ RC_TYPE_RC6 |
+ RC_TYPE_NEC,
+ .change_protocol = dib0700_change_protocol,
+ },
},
};
diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c
index 4c2a689c820e..a76bbb29ca36 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-common.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-common.c
@@ -23,7 +23,7 @@ int dibusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
if (st->ops.fifo_ctrl != NULL)
- if (st->ops.fifo_ctrl(adap->fe,onoff)) {
+ if (st->ops.fifo_ctrl(adap->fe_adap[0].fe, onoff)) {
err("error while controlling the fifo of the demod.");
return -ENODEV;
}
@@ -37,7 +37,8 @@ int dibusb_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onof
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
if (st->ops.pid_ctrl != NULL)
- st->ops.pid_ctrl(adap->fe,index,pid,onoff);
+ st->ops.pid_ctrl(adap->fe_adap[0].fe,
+ index, pid, onoff);
}
return 0;
}
@@ -48,7 +49,7 @@ int dibusb_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
if (st->ops.pid_parse != NULL)
- if (st->ops.pid_parse(adap->fe,onoff) < 0)
+ if (st->ops.pid_parse(adap->fe_adap[0].fe, onoff) < 0)
err("could not handle pid_parser");
}
return 0;
@@ -254,8 +255,16 @@ int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap)
msleep(1000);
}
- if ((adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000P_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL ||
- (adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000MC_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL) {
+ adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach,
+ &adap->dev->i2c_adap,
+ DEFAULT_DIB3000P_I2C_ADDRESS,
+ &mod3000p_dib3000p_config);
+ if ((adap->fe_adap[0].fe) == NULL)
+ adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach,
+ &adap->dev->i2c_adap,
+ DEFAULT_DIB3000MC_I2C_ADDRESS,
+ &mod3000p_dib3000p_config);
+ if ((adap->fe_adap[0].fe) != NULL) {
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
st->ops.pid_parse = dib3000mc_pid_parse;
@@ -309,15 +318,15 @@ int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap)
}
}
- tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1);
- if (dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) {
+ tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1);
+ if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) {
/* not found - use panasonic pll parameters */
- if (dvb_attach(dvb_pll_attach, adap->fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL)
+ if (dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL)
return -ENOMEM;
} else {
st->mt2060_present = 1;
/* set the correct parameters for the dib3000p */
- dib3000mc_set_config(adap->fe, &stk3000p_dib3000p_config);
+ dib3000mc_set_config(adap->fe_adap[0].fe, &stk3000p_dib3000p_config);
}
return 0;
}
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c
index 04d91bdd3562..7270791f8340 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mb.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c
@@ -31,11 +31,12 @@ static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_adapter *adap)
demod_cfg.demod_address = 0x8;
- if ((adap->fe = dvb_attach(dib3000mb_attach, &demod_cfg,
- &adap->dev->i2c_adap, &st->ops)) == NULL)
+ adap->fe_adap[0].fe = dvb_attach(dib3000mb_attach, &demod_cfg,
+ &adap->dev->i2c_adap, &st->ops);
+ if ((adap->fe_adap[0].fe) == NULL)
return -ENODEV;
- adap->fe->ops.i2c_gate_ctrl = dib3000mb_i2c_gate_ctrl;
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl = dib3000mb_i2c_gate_ctrl;
return 0;
}
@@ -46,7 +47,7 @@ static int dibusb_thomson_tuner_attach(struct dvb_usb_adapter *adap)
st->tuner_addr = 0x61;
- dvb_attach(dvb_pll_attach, adap->fe, 0x61, &adap->dev->i2c_adap,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap,
DVB_PLL_TUA6010XS);
return 0;
}
@@ -57,7 +58,7 @@ static int dibusb_panasonic_tuner_attach(struct dvb_usb_adapter *adap)
st->tuner_addr = 0x60;
- dvb_attach(dvb_pll_attach, adap->fe, 0x60, &adap->dev->i2c_adap,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap,
DVB_PLL_TDA665X);
return 0;
}
@@ -78,16 +79,16 @@ static int dibusb_tuner_probe_and_attach(struct dvb_usb_adapter *adap)
/* the Panasonic sits on I2C addrass 0x60, the Thomson on 0x61 */
msg[0].addr = msg[1].addr = st->tuner_addr = 0x60;
- if (adap->fe->ops.i2c_gate_ctrl)
- adap->fe->ops.i2c_gate_ctrl(adap->fe,1);
+ if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1);
if (i2c_transfer(&adap->dev->i2c_adap, msg, 2) != 2) {
err("tuner i2c write failed.");
ret = -EREMOTEIO;
}
- if (adap->fe->ops.i2c_gate_ctrl)
- adap->fe->ops.i2c_gate_ctrl(adap->fe,0);
+ if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0);
if (b2[0] == 0xfe) {
info("This device has the Thomson Cable onboard. Which is default.");
@@ -185,6 +186,8 @@ static struct dvb_usb_device_properties dibusb1_1_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
@@ -205,6 +208,7 @@ static struct dvb_usb_device_properties dibusb1_1_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
@@ -272,6 +276,8 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER,
.pid_filter_count = 16,
@@ -292,6 +298,7 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct dibusb_state),
},
},
@@ -338,6 +345,8 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
@@ -358,6 +367,7 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
@@ -398,6 +408,8 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
@@ -417,6 +429,7 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c
index c1d9094b61e5..9c165e2569d4 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mc.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c
@@ -57,6 +57,8 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.streaming_ctrl = dibusb2_0_streaming_ctrl,
@@ -76,6 +78,7 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c
index f6344cdd360f..f7184111aa64 100644
--- a/drivers/media/dvb/dvb-usb/digitv.c
+++ b/drivers/media/dvb/dvb-usb/digitv.c
@@ -137,11 +137,16 @@ static int digitv_frontend_attach(struct dvb_usb_adapter *adap)
{
struct digitv_state *st = adap->dev->priv;
- if ((adap->fe = dvb_attach(mt352_attach, &digitv_mt352_config, &adap->dev->i2c_adap)) != NULL) {
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach, &digitv_mt352_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL) {
st->is_nxt6000 = 0;
return 0;
}
- if ((adap->fe = dvb_attach(nxt6000_attach, &digitv_nxt6000_config, &adap->dev->i2c_adap)) != NULL) {
+ adap->fe_adap[0].fe = dvb_attach(nxt6000_attach,
+ &digitv_nxt6000_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL) {
st->is_nxt6000 = 1;
return 0;
}
@@ -152,11 +157,11 @@ static int digitv_tuner_attach(struct dvb_usb_adapter *adap)
{
struct digitv_state *st = adap->dev->priv;
- if (!dvb_attach(dvb_pll_attach, adap->fe, 0x60, NULL, DVB_PLL_TDED4))
+ if (!dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, NULL, DVB_PLL_TDED4))
return -ENODEV;
if (st->is_nxt6000)
- adap->fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params;
return 0;
}
@@ -292,6 +297,8 @@ static struct dvb_usb_device_properties digitv_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = digitv_frontend_attach,
.tuner_attach = digitv_tuner_attach,
@@ -306,6 +313,7 @@ static struct dvb_usb_device_properties digitv_properties = {
}
}
},
+ }},
}
},
.identify_state = digitv_identify_state,
diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c
index ecd86eca2548..106dfd55ff9c 100644
--- a/drivers/media/dvb/dvb-usb/dtt200u.c
+++ b/drivers/media/dvb/dvb-usb/dtt200u.c
@@ -90,7 +90,7 @@ static int dtt200u_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
static int dtt200u_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = dtt200u_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = dtt200u_fe_attach(adap->dev);
return 0;
}
@@ -140,6 +140,8 @@ static struct dvb_usb_device_properties dtt200u_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
@@ -157,6 +159,7 @@ static struct dvb_usb_device_properties dtt200u_properties = {
}
}
},
+ }},
}
},
.power_ctrl = dtt200u_power_ctrl,
@@ -187,6 +190,8 @@ static struct dvb_usb_device_properties wt220u_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
@@ -204,6 +209,7 @@ static struct dvb_usb_device_properties wt220u_properties = {
}
}
},
+ }},
}
},
.power_ctrl = dtt200u_power_ctrl,
@@ -234,6 +240,8 @@ static struct dvb_usb_device_properties wt220u_fc_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
@@ -251,6 +259,7 @@ static struct dvb_usb_device_properties wt220u_fc_properties = {
}
}
},
+ }},
}
},
.power_ctrl = dtt200u_power_ctrl,
@@ -281,6 +290,8 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
@@ -298,6 +309,7 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = {
}
}
},
+ }},
}
},
.power_ctrl = dtt200u_power_ctrl,
diff --git a/drivers/media/dvb/dvb-usb/dtv5100.c b/drivers/media/dvb/dvb-usb/dtv5100.c
index 078ce92ca436..7373132163d2 100644
--- a/drivers/media/dvb/dvb-usb/dtv5100.c
+++ b/drivers/media/dvb/dvb-usb/dtv5100.c
@@ -115,13 +115,13 @@ static struct zl10353_config dtv5100_zl10353_config = {
static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config,
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config,
&adap->dev->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
/* disable i2c gate, or it won't work... is this safe? */
- adap->fe->ops.i2c_gate_ctrl = NULL;
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL;
return 0;
}
@@ -133,7 +133,7 @@ static struct qt1010_config dtv5100_qt1010_config = {
static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach,
- adap->fe, &adap->dev->i2c_adap,
+ adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&dtv5100_qt1010_config) == NULL ? -ENODEV : 0;
}
@@ -180,6 +180,8 @@ static struct dvb_usb_device_properties dtv5100_properties = {
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = dtv5100_frontend_attach,
.tuner_attach = dtv5100_tuner_attach,
@@ -193,6 +195,7 @@ static struct dvb_usb_device_properties dtv5100_properties = {
}
}
},
+ }},
} },
.i2c_algo = &dtv5100_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
index b3cb626ed56e..ba4a7517354f 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
@@ -17,15 +17,20 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
if (adap == NULL)
return -ENODEV;
+ if ((adap->active_fe < 0) ||
+ (adap->active_fe >= adap->num_frontends_initialized)) {
+ return -EINVAL;
+ }
+
newfeedcount = adap->feedcount + (onoff ? 1 : -1);
/* stop feed before setting a new pid if there will be no pid anymore */
if (newfeedcount == 0) {
deb_ts("stop feeding\n");
- usb_urb_kill(&adap->stream);
+ usb_urb_kill(&adap->fe_adap[adap->active_fe].stream);
- if (adap->props.streaming_ctrl != NULL) {
- ret = adap->props.streaming_ctrl(adap, 0);
+ if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) {
+ ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 0);
if (ret < 0) {
err("error while stopping stream.");
return ret;
@@ -36,36 +41,37 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
adap->feedcount = newfeedcount;
/* activate the pid on the device specific pid_filter */
- deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n",adap->pid_filtering ?
- "yes" : "no", dvbdmxfeed->pid,dvbdmxfeed->pid,dvbdmxfeed->index,onoff ?
- "on" : "off");
- if (adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER &&
- adap->pid_filtering &&
- adap->props.pid_filter != NULL)
- adap->props.pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid,onoff);
+ deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n",
+ adap->fe_adap[adap->active_fe].pid_filtering ?
+ "yes" : "no", dvbdmxfeed->pid, dvbdmxfeed->pid,
+ dvbdmxfeed->index, onoff ? "on" : "off");
+ if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
+ adap->fe_adap[adap->active_fe].pid_filtering &&
+ adap->props.fe[adap->active_fe].pid_filter != NULL)
+ adap->props.fe[adap->active_fe].pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid, onoff);
/* start the feed if this was the first feed and there is still a feed
* for reception.
*/
if (adap->feedcount == onoff && adap->feedcount > 0) {
deb_ts("submitting all URBs\n");
- usb_urb_submit(&adap->stream);
+ usb_urb_submit(&adap->fe_adap[adap->active_fe].stream);
deb_ts("controlling pid parser\n");
- if (adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER &&
- adap->props.caps &
+ if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
+ adap->props.fe[adap->active_fe].caps &
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF &&
- adap->props.pid_filter_ctrl != NULL) {
- ret = adap->props.pid_filter_ctrl(adap,
- adap->pid_filtering);
+ adap->props.fe[adap->active_fe].pid_filter_ctrl != NULL) {
+ ret = adap->props.fe[adap->active_fe].pid_filter_ctrl(adap,
+ adap->fe_adap[adap->active_fe].pid_filtering);
if (ret < 0) {
err("could not handle pid_parser");
return ret;
}
}
deb_ts("start feeding\n");
- if (adap->props.streaming_ctrl != NULL) {
- ret = adap->props.streaming_ctrl(adap, 1);
+ if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) {
+ ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 1);
if (ret < 0) {
err("error while enabling fifo.");
return ret;
@@ -90,6 +96,7 @@ static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
{
+ int i;
int ret = dvb_register_adapter(&adap->dvb_adap, adap->dev->desc->name,
adap->dev->owner, &adap->dev->udev->dev,
adapter_nums);
@@ -112,7 +119,12 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
adap->demux.priv = adap;
- adap->demux.feednum = adap->demux.filternum = adap->max_feed_count;
+ adap->demux.filternum = 0;
+ for (i = 0; i < adap->props.num_frontends; i++) {
+ if (adap->demux.filternum < adap->fe_adap[i].max_feed_count)
+ adap->demux.filternum = adap->fe_adap[i].max_feed_count;
+ }
+ adap->demux.feednum = adap->demux.filternum;
adap->demux.start_feed = dvb_usb_start_feed;
adap->demux.stop_feed = dvb_usb_stop_feed;
adap->demux.write_to_decoder = NULL;
@@ -156,14 +168,33 @@ int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap)
return 0;
}
+static int dvb_usb_set_active_fe(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+
+ int ret = (adap->props.frontend_ctrl) ?
+ adap->props.frontend_ctrl(fe, onoff) : 0;
+
+ if (ret < 0) {
+ err("frontend_ctrl request failed");
+ return ret;
+ }
+ if (onoff)
+ adap->active_fe = fe->id;
+
+ return 0;
+}
+
static int dvb_usb_fe_wakeup(struct dvb_frontend *fe)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
dvb_usb_device_power_ctrl(adap->dev, 1);
- if (adap->fe_init)
- adap->fe_init(fe);
+ dvb_usb_set_active_fe(fe, 1);
+
+ if (adap->fe_adap[fe->id].fe_init)
+ adap->fe_adap[fe->id].fe_init(fe);
return 0;
}
@@ -172,45 +203,81 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
- if (adap->fe_sleep)
- adap->fe_sleep(fe);
+ if (adap->fe_adap[fe->id].fe_sleep)
+ adap->fe_adap[fe->id].fe_sleep(fe);
+
+ dvb_usb_set_active_fe(fe, 0);
return dvb_usb_device_power_ctrl(adap->dev, 0);
}
int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap)
{
- if (adap->props.frontend_attach == NULL) {
- err("strange: '%s' #%d doesn't want to attach a frontend.",adap->dev->desc->name, adap->id);
- return 0;
- }
+ int ret, i;
- /* re-assign sleep and wakeup functions */
- if (adap->props.frontend_attach(adap) == 0 && adap->fe != NULL) {
- adap->fe_init = adap->fe->ops.init; adap->fe->ops.init = dvb_usb_fe_wakeup;
- adap->fe_sleep = adap->fe->ops.sleep; adap->fe->ops.sleep = dvb_usb_fe_sleep;
+ /* register all given adapter frontends */
+ for (i = 0; i < adap->props.num_frontends; i++) {
- if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) {
- err("Frontend registration failed.");
- dvb_frontend_detach(adap->fe);
- adap->fe = NULL;
- return -ENODEV;
+ if (adap->props.fe[i].frontend_attach == NULL) {
+ err("strange: '%s' #%d,%d "
+ "doesn't want to attach a frontend.",
+ adap->dev->desc->name, adap->id, i);
+
+ return 0;
+ }
+
+ ret = adap->props.fe[i].frontend_attach(adap);
+ if (ret || adap->fe_adap[i].fe == NULL) {
+ /* only print error when there is no FE at all */
+ if (i == 0)
+ err("no frontend was attached by '%s'",
+ adap->dev->desc->name);
+
+ return 0;
+ }
+
+ adap->fe_adap[i].fe->id = i;
+
+ /* re-assign sleep and wakeup functions */
+ adap->fe_adap[i].fe_init = adap->fe_adap[i].fe->ops.init;
+ adap->fe_adap[i].fe->ops.init = dvb_usb_fe_wakeup;
+ adap->fe_adap[i].fe_sleep = adap->fe_adap[i].fe->ops.sleep;
+ adap->fe_adap[i].fe->ops.sleep = dvb_usb_fe_sleep;
+
+ if (dvb_register_frontend(&adap->dvb_adap, adap->fe_adap[i].fe)) {
+ err("Frontend %d registration failed.", i);
+ dvb_frontend_detach(adap->fe_adap[i].fe);
+ adap->fe_adap[i].fe = NULL;
+ /* In error case, do not try register more FEs,
+ * still leaving already registered FEs alive. */
+ if (i == 0)
+ return -ENODEV;
+ else
+ return 0;
}
/* only attach the tuner if the demod is there */
- if (adap->props.tuner_attach != NULL)
- adap->props.tuner_attach(adap);
- } else
- err("no frontend was attached by '%s'",adap->dev->desc->name);
+ if (adap->props.fe[i].tuner_attach != NULL)
+ adap->props.fe[i].tuner_attach(adap);
+
+ adap->num_frontends_initialized++;
+ }
return 0;
}
int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap)
{
- if (adap->fe != NULL) {
- dvb_unregister_frontend(adap->fe);
- dvb_frontend_detach(adap->fe);
+ int i = adap->num_frontends_initialized - 1;
+
+ /* unregister all given adapter frontends */
+ for (; i >= 0; i--) {
+ if (adap->fe_adap[i].fe != NULL) {
+ dvb_unregister_frontend(adap->fe_adap[i].fe);
+ dvb_frontend_detach(adap->fe_adap[i].fe);
+ }
}
+ adap->num_frontends_initialized = 0;
+
return 0;
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 21b15495d2d7..2d08c9b5128a 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -37,6 +37,7 @@
#define USB_VID_HAUPPAUGE 0x2040
#define USB_VID_HYPER_PALTEK 0x1025
#define USB_VID_INTEL 0x8086
+#define USB_VID_ITETECH 0x048d
#define USB_VID_KWORLD 0xeb2a
#define USB_VID_KWORLD_2 0x1b80
#define USB_VID_KYE 0x0458
@@ -126,6 +127,7 @@
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
#define USB_PID_INTEL_CE9500 0x9500
+#define USB_PID_ITETECH_IT9135 0x9135
#define USB_PID_KWORLD_399U 0xe399
#define USB_PID_KWORLD_399U_2 0xe400
#define USB_PID_KWORLD_395U 0xe396
@@ -136,6 +138,7 @@
#define USB_PID_KWORLD_PC160_2T 0xc160
#define USB_PID_KWORLD_PC160_T 0xc161
#define USB_PID_KWORLD_UB383_T 0xe383
+#define USB_PID_KWORLD_UB499_2T_T09 0xe409
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055
@@ -230,6 +233,8 @@
#define USB_PID_PINNACLE_PCTV310E 0x3211
#define USB_PID_PINNACLE_PCTV801E 0x023a
#define USB_PID_PINNACLE_PCTV801E_SE 0x023b
+#define USB_PID_PINNACLE_PCTV340E 0x023d
+#define USB_PID_PINNACLE_PCTV340E_SE 0x023e
#define USB_PID_PINNACLE_PCTV73A 0x0243
#define USB_PID_PINNACLE_PCTV73ESE 0x0245
#define USB_PID_PINNACLE_PCTV74E 0x0246
@@ -238,6 +243,9 @@
#define USB_PID_PCTV_200E 0x020e
#define USB_PID_PCTV_400E 0x020f
#define USB_PID_PCTV_450E 0x0222
+#define USB_PID_PCTV_452E 0x021f
+#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007
+#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a
#define USB_PID_NEBULA_DIGITV 0x0201
#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820
#define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500
@@ -313,6 +321,7 @@
#define USB_PID_FRIIO_WHITE 0x0001
#define USB_PID_TVWAY_PLUS 0x0002
#define USB_PID_SVEON_STV20 0xe39d
+#define USB_PID_SVEON_STV22 0xe401
#define USB_PID_AZUREWAVE_AZ6027 0x3275
#define USB_PID_TERRATEC_DVBS2CI_V1 0x10a4
#define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/drivers/media/dvb/dvb-usb/dvb-usb-init.c
index 2e3ea0fa28e0..169196ec2d4e 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-init.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-init.c
@@ -29,7 +29,7 @@ MODULE_PARM_DESC(force_pid_filter_usage, "force all dvb-usb-devices to use a PID
static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs)
{
struct dvb_usb_adapter *adap;
- int ret, n;
+ int ret, n, o;
for (n = 0; n < d->props.num_adapters; n++) {
adap = &d->adapter[n];
@@ -38,31 +38,42 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs)
memcpy(&adap->props, &d->props.adapter[n], sizeof(struct dvb_usb_adapter_properties));
+ for (o = 0; o < adap->props.num_frontends; o++) {
+ struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o];
/* speed - when running at FULL speed we need a HW PID filter */
- if (d->udev->speed == USB_SPEED_FULL && !(adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER)) {
+ if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) {
err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)");
return -ENODEV;
}
- if ((d->udev->speed == USB_SPEED_FULL && adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER) ||
- (adap->props.caps & DVB_USB_ADAP_NEED_PID_FILTERING)) {
- info("will use the device's hardware PID filter (table count: %d).", adap->props.pid_filter_count);
- adap->pid_filtering = 1;
- adap->max_feed_count = adap->props.pid_filter_count;
+ if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) ||
+ (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) {
+ info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count);
+ adap->fe_adap[o].pid_filtering = 1;
+ adap->fe_adap[o].max_feed_count = props->pid_filter_count;
} else {
info("will pass the complete MPEG2 transport stream to the software demuxer.");
- adap->pid_filtering = 0;
- adap->max_feed_count = 255;
+ adap->fe_adap[o].pid_filtering = 0;
+ adap->fe_adap[o].max_feed_count = 255;
}
- if (!adap->pid_filtering &&
+ if (!adap->fe_adap[o].pid_filtering &&
dvb_usb_force_pid_filter_usage &&
- adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER) {
+ props->caps & DVB_USB_ADAP_HAS_PID_FILTER) {
info("pid filter enabled by module option.");
- adap->pid_filtering = 1;
- adap->max_feed_count = adap->props.pid_filter_count;
+ adap->fe_adap[o].pid_filtering = 1;
+ adap->fe_adap[o].max_feed_count = props->pid_filter_count;
}
+ if (props->size_of_priv > 0) {
+ adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL);
+ if (adap->fe_adap[o].priv == NULL) {
+ err("no memory for priv for adapter %d fe %d.", n, o);
+ return -ENOMEM;
+ }
+ }
+ }
+
if (adap->props.size_of_priv > 0) {
adap->priv = kzalloc(adap->props.size_of_priv, GFP_KERNEL);
if (adap->priv == NULL) {
@@ -77,6 +88,10 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs)
return ret;
}
+ /* use exclusive FE lock if there is multiple shared FEs */
+ if (adap->fe_adap[1].fe)
+ adap->dvb_adap.mfe_shared = 1;
+
d->num_adapters_initialized++;
d->state |= DVB_USB_STATE_DVB;
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
index bb46ba6a3573..53a5c30b51b2 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
@@ -82,16 +82,28 @@ static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buffer
int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap)
{
- adap->stream.udev = adap->dev->udev;
- if (adap->props.caps & DVB_USB_ADAP_RECEIVES_204_BYTE_TS)
- adap->stream.complete = dvb_usb_data_complete_204;
- else
- adap->stream.complete = dvb_usb_data_complete;
- adap->stream.user_priv = adap;
- return usb_urb_init(&adap->stream, &adap->props.stream);
+ int i, ret = 0;
+ for (i = 0; i < adap->props.num_frontends; i++) {
+
+ adap->fe_adap[i].stream.udev = adap->dev->udev;
+ if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_204_BYTE_TS)
+ adap->fe_adap[i].stream.complete =
+ dvb_usb_data_complete_204;
+ else
+ adap->fe_adap[i].stream.complete = dvb_usb_data_complete;
+ adap->fe_adap[i].stream.user_priv = adap;
+ ret = usb_urb_init(&adap->fe_adap[i].stream,
+ &adap->props.fe[i].stream);
+ if (ret < 0)
+ break;
+ }
+ return ret;
}
int dvb_usb_adapter_stream_exit(struct dvb_usb_adapter *adap)
{
- return usb_urb_exit(&adap->stream);
+ int i;
+ for (i = 0; i < adap->props.num_frontends; i++)
+ usb_urb_exit(&adap->fe_adap[i].stream);
+ return 0;
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h
index 76a80968482a..6d7d13f9ce68 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb.h
@@ -85,7 +85,7 @@ static inline u8 rc5_data(struct rc_map_table *key)
return key->scancode & 0xff;
}
-static inline u8 rc5_scan(struct rc_map_table *key)
+static inline u16 rc5_scan(struct rc_map_table *key)
{
return key->scancode & 0xffff;
}
@@ -124,6 +124,8 @@ struct usb_data_stream_properties {
* @caps: capabilities of the DVB USB device.
* @pid_filter_count: number of PID filter position in the optional hardware
* PID-filter.
+ * @num_frontends: number of frontends of the DVB USB adapter.
+ * @frontend_ctrl: called to power on/off active frontend.
* @streaming_ctrl: called to start and stop the MPEG2-TS streaming of the
* device (not URB submitting/killing).
* @pid_filter_ctrl: called to en/disable the PID filter, if any.
@@ -134,7 +136,7 @@ struct usb_data_stream_properties {
* pll_desc and pll_init_buf of struct dvb_usb_device).
* @stream: configuration of the USB streaming
*/
-struct dvb_usb_adapter_properties {
+struct dvb_usb_adapter_fe_properties {
#define DVB_USB_ADAP_HAS_PID_FILTER 0x01
#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02
#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04
@@ -152,9 +154,18 @@ struct dvb_usb_adapter_properties {
struct usb_data_stream_properties stream;
int size_of_priv;
+};
+
+#define MAX_NO_OF_FE_PER_ADAP 2
+struct dvb_usb_adapter_properties {
+ int size_of_priv;
+ int (*frontend_ctrl) (struct dvb_frontend *, int);
int (*fe_ioctl_override) (struct dvb_frontend *,
unsigned int, void *, unsigned int);
+
+ int num_frontends;
+ struct dvb_usb_adapter_fe_properties fe[MAX_NO_OF_FE_PER_ADAP];
};
/**
@@ -345,6 +356,20 @@ struct usb_data_stream {
*
* @stream: the usb data stream.
*/
+struct dvb_usb_fe_adapter {
+ struct dvb_frontend *fe;
+
+ int (*fe_init) (struct dvb_frontend *);
+ int (*fe_sleep) (struct dvb_frontend *);
+
+ struct usb_data_stream stream;
+
+ int pid_filtering;
+ int max_feed_count;
+
+ void *priv;
+};
+
struct dvb_usb_adapter {
struct dvb_usb_device *dev;
struct dvb_usb_adapter_properties props;
@@ -356,20 +381,16 @@ struct dvb_usb_adapter {
u8 id;
int feedcount;
- int pid_filtering;
/* dvb */
struct dvb_adapter dvb_adap;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dvb_net dvb_net;
- struct dvb_frontend *fe;
- int max_feed_count;
-
- int (*fe_init) (struct dvb_frontend *);
- int (*fe_sleep) (struct dvb_frontend *);
- struct usb_data_stream stream;
+ struct dvb_usb_fe_adapter fe_adap[MAX_NO_OF_FE_PER_ADAP];
+ int active_fe;
+ int num_frontends_initialized;
void *priv;
};
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c
index 058b2318abed..f103ec1fe82e 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/dvb/dvb-usb/dw2102.c
@@ -992,18 +992,18 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
struct dvb_tuner_ops *tuner_ops = NULL;
if (demod_probe & 4) {
- d->fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config,
+ d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config,
&d->dev->i2c_adap, 0);
- if (d->fe != NULL) {
- if (dvb_attach(stb6100_attach, d->fe,
+ if (d->fe_adap[0].fe != NULL) {
+ if (dvb_attach(stb6100_attach, d->fe_adap[0].fe,
&dw2104a_stb6100_config,
&d->dev->i2c_adap)) {
- tuner_ops = &d->fe->ops.tuner_ops;
+ tuner_ops = &d->fe_adap[0].fe->ops.tuner_ops;
tuner_ops->set_frequency = stb6100_set_freq;
tuner_ops->get_frequency = stb6100_get_freq;
tuner_ops->set_bandwidth = stb6100_set_bandw;
tuner_ops->get_bandwidth = stb6100_get_bandw;
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached STV0900+STB6100!\n");
return 0;
}
@@ -1011,13 +1011,13 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
}
if (demod_probe & 2) {
- d->fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config,
+ d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config,
&d->dev->i2c_adap, 0);
- if (d->fe != NULL) {
- if (dvb_attach(stv6110_attach, d->fe,
+ if (d->fe_adap[0].fe != NULL) {
+ if (dvb_attach(stv6110_attach, d->fe_adap[0].fe,
&dw2104_stv6110_config,
&d->dev->i2c_adap)) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached STV0900+STV6110A!\n");
return 0;
}
@@ -1025,19 +1025,19 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
}
if (demod_probe & 1) {
- d->fe = dvb_attach(cx24116_attach, &dw2104_config,
+ d->fe_adap[0].fe = dvb_attach(cx24116_attach, &dw2104_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ if (d->fe_adap[0].fe != NULL) {
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached cx24116!\n");
return 0;
}
}
- d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
+ d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ if (d->fe_adap[0].fe != NULL) {
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached DS3000!\n");
return 0;
}
@@ -1053,22 +1053,22 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
{
if (dw2102_properties.i2c_algo == &dw2102_serit_i2c_algo) {
/*dw2102_properties.adapter->tuner_attach = NULL;*/
- d->fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config,
+ d->fe_adap[0].fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ if (d->fe_adap[0].fe != NULL) {
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached si21xx!\n");
return 0;
}
}
if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) {
- d->fe = dvb_attach(stv0288_attach, &earda_config,
+ d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- if (dvb_attach(stb6000_attach, d->fe, 0x61,
+ if (d->fe_adap[0].fe != NULL) {
+ if (dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61,
&d->dev->i2c_adap)) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached stv0288!\n");
return 0;
}
@@ -1077,10 +1077,10 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
if (dw2102_properties.i2c_algo == &dw2102_i2c_algo) {
/*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/
- d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
+ d->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ if (d->fe_adap[0].fe != NULL) {
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached stv0299!\n");
return 0;
}
@@ -1090,9 +1090,9 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
{
- d->fe = dvb_attach(tda10023_attach, &dw3101_tda10023_config,
+ d->fe_adap[0].fe = dvb_attach(tda10023_attach, &dw3101_tda10023_config,
&d->dev->i2c_adap, 0x48);
- if (d->fe != NULL) {
+ if (d->fe_adap[0].fe != NULL) {
info("Attached tda10023!\n");
return 0;
}
@@ -1101,12 +1101,12 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
static int zl100313_frontend_attach(struct dvb_usb_adapter *d)
{
- d->fe = dvb_attach(mt312_attach, &zl313_config,
+ d->fe_adap[0].fe = dvb_attach(mt312_attach, &zl313_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- if (dvb_attach(zl10039_attach, d->fe, 0x60,
+ if (d->fe_adap[0].fe != NULL) {
+ if (dvb_attach(zl10039_attach, d->fe_adap[0].fe, 0x60,
&d->dev->i2c_adap)) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached zl100313+zl10039!\n");
return 0;
}
@@ -1119,16 +1119,16 @@ static int stv0288_frontend_attach(struct dvb_usb_adapter *d)
{
u8 obuf[] = {7, 1};
- d->fe = dvb_attach(stv0288_attach, &earda_config,
+ d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config,
&d->dev->i2c_adap);
- if (d->fe == NULL)
+ if (d->fe_adap[0].fe == NULL)
return -EIO;
- if (NULL == dvb_attach(stb6000_attach, d->fe, 0x61, &d->dev->i2c_adap))
+ if (NULL == dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61, &d->dev->i2c_adap))
return -EIO;
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
@@ -1143,14 +1143,14 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d)
struct s6x0_state *st = (struct s6x0_state *)d->dev->priv;
u8 obuf[] = {7, 1};
- d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
+ d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
&d->dev->i2c_adap);
- if (d->fe == NULL)
+ if (d->fe_adap[0].fe == NULL)
return -EIO;
- st->old_set_voltage = d->fe->ops.set_voltage;
- d->fe->ops.set_voltage = s660_set_voltage;
+ st->old_set_voltage = d->fe_adap[0].fe->ops.set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = s660_set_voltage;
dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
@@ -1163,12 +1163,12 @@ static int prof_7500_frontend_attach(struct dvb_usb_adapter *d)
{
u8 obuf[] = {7, 1};
- d->fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config,
+ d->fe_adap[0].fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config,
&d->dev->i2c_adap, 0);
- if (d->fe == NULL)
+ if (d->fe_adap[0].fe == NULL)
return -EIO;
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
@@ -1204,9 +1204,9 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d)
if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0)
err("command 0x51 transfer failed.");
- d->fe = dvb_attach(ds3000_attach, &su3000_ds3000_config,
+ d->fe_adap[0].fe = dvb_attach(ds3000_attach, &su3000_ds3000_config,
&d->dev->i2c_adap);
- if (d->fe == NULL)
+ if (d->fe_adap[0].fe == NULL)
return -EIO;
info("Attached DS3000!\n");
@@ -1216,14 +1216,14 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d)
static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x60,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60,
&adap->dev->i2c_adap, DVB_PLL_OPERA1);
return 0;
}
static int dw3101_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x60,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60,
&adap->dev->i2c_adap, DVB_PLL_TUA6034);
return 0;
@@ -1535,7 +1535,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
DW210X_READ_MSG);
if ((reset16[0] == 0xa1) || (reset16[0] == 0x80)) {
dw2102_properties.i2c_algo = &dw2102_i2c_algo;
- dw2102_properties.adapter->tuner_attach = &dw2102_tuner_attach;
+ dw2102_properties.adapter->fe[0].tuner_attach = &dw2102_tuner_attach;
break;
} else {
/* check STV0288 frontend */
@@ -1591,6 +1591,8 @@ static struct dvb_usb_device_properties dw2102_properties = {
.read_mac_address = dw210x_read_mac_address,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = dw2102_frontend_attach,
.stream = {
.type = USB_BULK,
@@ -1602,6 +1604,7 @@ static struct dvb_usb_device_properties dw2102_properties = {
}
}
},
+ }},
}
},
.num_device_descs = 3,
@@ -1642,6 +1645,8 @@ static struct dvb_usb_device_properties dw2104_properties = {
.read_mac_address = dw210x_read_mac_address,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = dw2104_frontend_attach,
.stream = {
.type = USB_BULK,
@@ -1653,6 +1658,7 @@ static struct dvb_usb_device_properties dw2104_properties = {
}
}
},
+ }},
}
},
.num_device_descs = 2,
@@ -1689,6 +1695,8 @@ static struct dvb_usb_device_properties dw3101_properties = {
.read_mac_address = dw210x_read_mac_address,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = dw3101_frontend_attach,
.tuner_attach = dw3101_tuner_attach,
.stream = {
@@ -1701,6 +1709,7 @@ static struct dvb_usb_device_properties dw3101_properties = {
}
}
},
+ }},
}
},
.num_device_descs = 1,
@@ -1733,6 +1742,8 @@ static struct dvb_usb_device_properties s6x0_properties = {
.read_mac_address = s6x0_read_mac_address,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = zl100313_frontend_attach,
.stream = {
.type = USB_BULK,
@@ -1744,6 +1755,7 @@ static struct dvb_usb_device_properties s6x0_properties = {
}
}
},
+ }},
}
},
.num_device_descs = 1,
@@ -1810,6 +1822,8 @@ static struct dvb_usb_device_properties su3000_properties = {
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = su3000_streaming_ctrl,
.frontend_attach = su3000_frontend_attach,
.stream = {
@@ -1822,6 +1836,7 @@ static struct dvb_usb_device_properties su3000_properties = {
}
}
}
+ }},
}
},
.num_device_descs = 3,
@@ -1855,7 +1870,7 @@ static int dw2102_probe(struct usb_interface *intf,
p1100->devices[0] = d1100;
p1100->rc.legacy.rc_map_table = rc_map_tbs_table;
p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table);
- p1100->adapter->frontend_attach = stv0288_frontend_attach;
+ p1100->adapter->fe[0].frontend_attach = stv0288_frontend_attach;
s660 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL);
if (!s660) {
@@ -1869,7 +1884,7 @@ static int dw2102_probe(struct usb_interface *intf,
s660->devices[0] = d660;
s660->devices[1] = d480_1;
s660->devices[2] = d480_2;
- s660->adapter->frontend_attach = ds3000_frontend_attach;
+ s660->adapter->fe[0].frontend_attach = ds3000_frontend_attach;
p7500 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL);
if (!p7500) {
@@ -1883,7 +1898,7 @@ static int dw2102_probe(struct usb_interface *intf,
p7500->devices[0] = d7500;
p7500->rc.legacy.rc_map_table = rc_map_tbs_table;
p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table);
- p7500->adapter->frontend_attach = prof_7500_frontend_attach;
+ p7500->adapter->fe[0].frontend_attach = prof_7500_frontend_attach;
if (0 == dvb_usb_device_init(intf, &dw2102_properties,
THIS_MODULE, NULL, adapter_nr) ||
diff --git a/drivers/media/dvb/dvb-usb/ec168.c b/drivers/media/dvb/dvb-usb/ec168.c
index 1ba3e5dbee10..78442fe4aa5e 100644
--- a/drivers/media/dvb/dvb-usb/ec168.c
+++ b/drivers/media/dvb/dvb-usb/ec168.c
@@ -200,9 +200,9 @@ static struct ec100_config ec168_ec100_config = {
static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap)
{
deb_info("%s:\n", __func__);
- adap->fe = dvb_attach(ec100_attach, &ec168_ec100_config,
+ adap->fe_adap[0].fe = dvb_attach(ec100_attach, &ec168_ec100_config,
&adap->dev->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
return 0;
@@ -228,7 +228,7 @@ static struct mxl5005s_config ec168_mxl5003s_config = {
static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
{
deb_info("%s:\n", __func__);
- return dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap,
+ return dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&ec168_mxl5003s_config) == NULL ? -ENODEV : 0;
}
@@ -382,6 +382,8 @@ static struct dvb_usb_device_properties ec168_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = ec168_streaming_ctrl,
.frontend_attach = ec168_ec100_frontend_attach,
.tuner_attach = ec168_mxl5003s_tuner_attach,
@@ -395,6 +397,7 @@ static struct dvb_usb_device_properties ec168_properties = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/friio.c b/drivers/media/dvb/dvb-usb/friio.c
index 76159aed9bb0..b092dc2137cd 100644
--- a/drivers/media/dvb/dvb-usb/friio.c
+++ b/drivers/media/dvb/dvb-usb/friio.c
@@ -403,8 +403,8 @@ static int friio_frontend_attach(struct dvb_usb_adapter *adap)
if (friio_initialize(adap->dev) < 0)
return -EIO;
- adap->fe = jdvbt90502_attach(adap->dev);
- if (adap->fe == NULL)
+ adap->fe_adap[0].fe = jdvbt90502_attach(adap->dev);
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
@@ -473,6 +473,8 @@ static struct dvb_usb_device_properties friio_properties = {
/* caps:0 => no pid filter, 188B TS packet */
/* GL861 has a HW pid filter, but no info available. */
{
+ .num_frontends = 1,
+ .fe = {{
.caps = 0,
.frontend_attach = friio_frontend_attach,
@@ -490,6 +492,7 @@ static struct dvb_usb_device_properties friio_properties = {
}
}
},
+ }},
}
},
.i2c_algo = &gl861_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c
index 6f596ed41761..63681df244c4 100644
--- a/drivers/media/dvb/dvb-usb/gl861.c
+++ b/drivers/media/dvb/dvb-usb/gl861.c
@@ -103,9 +103,9 @@ static struct zl10353_config gl861_zl10353_config = {
static int gl861_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
&adap->dev->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
@@ -118,7 +118,7 @@ static struct qt1010_config gl861_qt1010_config = {
static int gl861_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach,
- adap->fe, &adap->dev->i2c_adap,
+ adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&gl861_qt1010_config) == NULL ? -ENODEV : 0;
}
@@ -167,6 +167,8 @@ static struct dvb_usb_device_properties gl861_properties = {
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = gl861_frontend_attach,
.tuner_attach = gl861_tuner_attach,
@@ -181,6 +183,7 @@ static struct dvb_usb_device_properties gl861_properties = {
}
}
},
+ }},
} },
.i2c_algo = &gl861_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
index 60d11e57e7d0..5426267980c7 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
@@ -144,19 +144,25 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
cmd[6] = (freq >> 16) & 0xff;
cmd[7] = (freq >> 24) & 0xff;
+ /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */
+ if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8)
+ c->delivery_system = SYS_TURBO;
+
switch (c->delivery_system) {
case SYS_DVBS:
- /* Allow QPSK and 8PSK (even for DVB-S) */
- if (c->modulation != QPSK && c->modulation != PSK_8) {
+ if (c->modulation != QPSK) {
deb_fe("%s: unsupported modulation selected (%d)\n",
__func__, c->modulation);
return -EOPNOTSUPP;
}
c->fec_inner = FEC_AUTO;
break;
- case SYS_DVBS2:
+ case SYS_DVBS2: /* kept for backwards compatibility */
deb_fe("%s: DVB-S2 delivery system selected\n", __func__);
break;
+ case SYS_TURBO:
+ deb_fe("%s: Turbo-FEC delivery system selected\n", __func__);
+ break;
default:
deb_fe("%s: unsupported delivery system selected (%d)\n",
@@ -189,7 +195,10 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
default:
cmd[9] = 5; break;
}
- cmd[8] = ADV_MOD_DVB_QPSK;
+ if (c->delivery_system == SYS_TURBO)
+ cmd[8] = ADV_MOD_TURBO_QPSK;
+ else
+ cmd[8] = ADV_MOD_DVB_QPSK;
break;
case PSK_8: /* PSK_8 is for compatibility with DN */
cmd[8] = ADV_MOD_TURBO_8PSK;
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c
index 1cb3d9a66e02..5f71284703d0 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk.c
@@ -230,7 +230,7 @@ static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
static int gp8psk_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = gp8psk_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = gp8psk_fe_attach(adap->dev);
return 0;
}
@@ -268,6 +268,8 @@ static struct dvb_usb_device_properties gp8psk_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = gp8psk_streaming_ctrl,
.frontend_attach = gp8psk_frontend_attach,
/* parameter for the MPEG2-data transfer */
@@ -281,6 +283,7 @@ static struct dvb_usb_device_properties gp8psk_properties = {
}
}
},
+ }},
}
},
.power_ctrl = gp8psk_power_ctrl,
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.h b/drivers/media/dvb/dvb-usb/gp8psk.h
index 831749a518cb..ed32b9da4843 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.h
+++ b/drivers/media/dvb/dvb-usb/gp8psk.h
@@ -78,9 +78,6 @@ extern int dvb_usb_gp8psk_debug;
#define ADV_MOD_DVB_BPSK 9 /* DVB-S BPSK */
#define GET_USB_SPEED 0x07
- #define USB_SPEED_LOW 0
- #define USB_SPEED_FULL 1
- #define USB_SPEED_HIGH 2
#define RESET_FX2 0x13
diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c
new file mode 100644
index 000000000000..c46226187143
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/it913x.c
@@ -0,0 +1,702 @@
+/* DVB USB compliant linux driver for IT9137
+ *
+ * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
+ * IT9137 (C) ITE Tech Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/dvb/it9137.txt for firmware information
+ *
+ */
+#define DVB_USB_LOG_PREFIX "it913x"
+
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+#include "dvb-usb.h"
+#include "it913x-fe.h"
+
+/* debug */
+static int dvb_usb_it913x_debug;
+#define l_dprintk(var, level, args...) do { \
+ if ((var >= level)) \
+ printk(KERN_DEBUG DVB_USB_LOG_PREFIX ": " args); \
+} while (0)
+
+#define deb_info(level, args...) l_dprintk(dvb_usb_it913x_debug, level, args)
+#define debug_data_snipet(level, name, p) \
+ deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
+ *p, *(p+1), *(p+2), *(p+3), *(p+4), \
+ *(p+5), *(p+6), *(p+7));
+
+
+module_param_named(debug, dvb_usb_it913x_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."
+ DVB_USB_DEBUG_STATUS);
+
+static int pid_filter;
+module_param_named(pid, pid_filter, int, 0644);
+MODULE_PARM_DESC(pid, "set default 0=on 1=off");
+
+int cmd_counter;
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct it913x_state {
+ u8 id;
+};
+
+struct ite_config {
+ u8 chip_ver;
+ u16 chip_type;
+ u32 firmware;
+ u8 tuner_id_0;
+ u8 tuner_id_1;
+ u8 dual_mode;
+};
+
+struct ite_config it913x_config;
+
+static int it913x_bulk_write(struct usb_device *dev,
+ u8 *snd, int len, u8 pipe)
+{
+ int ret, actual_l;
+
+ ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe),
+ snd, len , &actual_l, 100);
+ return ret;
+}
+
+static int it913x_bulk_read(struct usb_device *dev,
+ u8 *rev, int len, u8 pipe)
+{
+ int ret, actual_l;
+
+ ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe),
+ rev, len , &actual_l, 200);
+ return ret;
+}
+
+static u16 check_sum(u8 *p, u8 len)
+{
+ u16 sum = 0;
+ u8 i = 1;
+ while (i < len)
+ sum += (i++ & 1) ? (*p++) << 8 : *p++;
+ return ~sum;
+}
+
+static int it913x_io(struct usb_device *udev, u8 mode, u8 pro,
+ u8 cmd, u32 reg, u8 addr, u8 *data, u8 len)
+{
+ int ret = 0, i, buf_size = 1;
+ u8 *buff;
+ u8 rlen;
+ u16 chk_sum;
+
+ buff = kzalloc(256, GFP_KERNEL);
+ if (!buff) {
+ info("USB Buffer Failed");
+ return -ENOMEM;
+ }
+
+ buff[buf_size++] = pro;
+ buff[buf_size++] = cmd;
+ buff[buf_size++] = cmd_counter;
+
+ switch (mode) {
+ case READ_LONG:
+ case WRITE_LONG:
+ buff[buf_size++] = len;
+ buff[buf_size++] = 2;
+ buff[buf_size++] = (reg >> 24);
+ buff[buf_size++] = (reg >> 16) & 0xff;
+ buff[buf_size++] = (reg >> 8) & 0xff;
+ buff[buf_size++] = reg & 0xff;
+ break;
+ case READ_SHORT:
+ buff[buf_size++] = addr;
+ break;
+ case WRITE_SHORT:
+ buff[buf_size++] = len;
+ buff[buf_size++] = addr;
+ buff[buf_size++] = (reg >> 8) & 0xff;
+ buff[buf_size++] = reg & 0xff;
+ break;
+ case READ_DATA:
+ case WRITE_DATA:
+ break;
+ case WRITE_CMD:
+ mode = 7;
+ break;
+ default:
+ kfree(buff);
+ return -EINVAL;
+ }
+
+ if (mode & 1) {
+ for (i = 0; i < len ; i++)
+ buff[buf_size++] = data[i];
+ }
+ chk_sum = check_sum(&buff[1], buf_size);
+
+ buff[buf_size++] = chk_sum >> 8;
+ buff[0] = buf_size;
+ buff[buf_size++] = (chk_sum & 0xff);
+
+ ret = it913x_bulk_write(udev, buff, buf_size , 0x02);
+
+ ret |= it913x_bulk_read(udev, buff, (mode & 1) ?
+ 5 : len + 5 , 0x01);
+
+ rlen = (mode & 0x1) ? 0x1 : len;
+
+ if (mode & 1)
+ ret |= buff[2];
+ else
+ memcpy(data, &buff[3], rlen);
+
+ cmd_counter++;
+
+ kfree(buff);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_wr_reg(struct usb_device *udev, u8 pro, u32 reg , u8 data)
+{
+ int ret;
+ u8 b[1];
+ b[0] = data;
+ ret = it913x_io(udev, WRITE_LONG, pro,
+ CMD_DEMOD_WRITE, reg, 0, b, sizeof(b));
+
+ return ret;
+}
+
+static int it913x_read_reg(struct usb_device *udev, u32 reg)
+{
+ int ret;
+ u8 data[1];
+
+ ret = it913x_io(udev, READ_LONG, DEV_0,
+ CMD_DEMOD_READ, reg, 0, &data[0], 1);
+
+ return (ret < 0) ? ret : data[0];
+}
+
+static u32 it913x_query(struct usb_device *udev, u8 pro)
+{
+ int ret;
+ u8 data[4];
+ ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ,
+ 0x1222, 0, &data[0], 3);
+
+ it913x_config.chip_ver = data[0];
+ it913x_config.chip_type = (u16)(data[2] << 8) + data[1];
+
+ info("Chip Version=%02x Chip Type=%04x", it913x_config.chip_ver,
+ it913x_config.chip_type);
+
+ ret |= it913x_io(udev, READ_SHORT, pro,
+ CMD_QUERYINFO, 0, 0x1, &data[0], 4);
+
+ it913x_config.firmware = (data[0] << 24) + (data[1] << 16) +
+ (data[2] << 8) + data[3];
+
+ return (ret < 0) ? 0 : it913x_config.firmware;
+}
+
+static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ int ret = 0;
+ u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
+
+ if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
+ return -EAGAIN;
+ deb_info(1, "PID_C (%02x)", onoff);
+
+ if (!onoff)
+ ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1);
+
+ mutex_unlock(&adap->dev->i2c_mutex);
+ return ret;
+}
+
+static int it913x_pid_filter(struct dvb_usb_adapter *adap,
+ int index, u16 pid, int onoff)
+{
+ struct usb_device *udev = adap->dev->udev;
+ int ret = 0;
+ u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
+
+ if (pid_filter > 0)
+ return 0;
+
+ if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
+ return -EAGAIN;
+ deb_info(1, "PID_F (%02x)", onoff);
+ if (onoff) {
+ ret = it913x_wr_reg(udev, pro, PID_EN, 0x1);
+
+ ret |= it913x_wr_reg(udev, pro, PID_LSB, (u8)(pid & 0xff));
+
+ ret |= it913x_wr_reg(udev, pro, PID_MSB, (u8)(pid >> 8));
+
+ ret |= it913x_wr_reg(udev, pro, PID_INX_EN, (u8)onoff);
+
+ ret |= it913x_wr_reg(udev, pro, PID_INX, (u8)(index & 0x1f));
+
+ }
+
+ mutex_unlock(&adap->dev->i2c_mutex);
+ return 0;
+}
+
+
+static int it913x_return_status(struct usb_device *udev)
+{
+ u32 firm = 0;
+
+ firm = it913x_query(udev, DEV_0);
+ if (firm > 0)
+ info("Firmware Version %d", firm);
+
+ return (firm > 0) ? firm : 0;
+}
+
+static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ static u8 data[256];
+ int ret;
+ u32 reg;
+ u8 pro;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ debug_data_snipet(1, "Message out", msg[0].buf);
+ deb_info(2, "num of messages %d address %02x", num, msg[0].addr);
+
+ pro = (msg[0].addr & 0x2) ? DEV_0_DMOD : 0x0;
+ pro |= (msg[0].addr & 0x20) ? DEV_1 : DEV_0;
+ memcpy(data, msg[0].buf, msg[0].len);
+ reg = (data[0] << 24) + (data[1] << 16) +
+ (data[2] << 8) + data[3];
+ if (num == 2) {
+ ret = it913x_io(d->udev, READ_LONG, pro,
+ CMD_DEMOD_READ, reg, 0, data, msg[1].len);
+ memcpy(msg[1].buf, data, msg[1].len);
+ } else
+ ret = it913x_io(d->udev, WRITE_LONG, pro, CMD_DEMOD_WRITE,
+ reg, 0, &data[4], msg[0].len - 4);
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+
+static u32 it913x_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm it913x_i2c_algo = {
+ .master_xfer = it913x_i2c_xfer,
+ .functionality = it913x_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+#define IT913X_POLL 250
+static int it913x_rc_query(struct dvb_usb_device *d)
+{
+ u8 ibuf[4];
+ int ret;
+ u32 key;
+ /* Avoid conflict with frontends*/
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ ret = it913x_io(d->udev, READ_LONG, PRO_LINK, CMD_IR_GET,
+ 0, 0, &ibuf[0], sizeof(ibuf));
+
+ if ((ibuf[2] + ibuf[3]) == 0xff) {
+ key = ibuf[2];
+ key += ibuf[0] << 8;
+ deb_info(1, "INT Key =%08x", key);
+ if (d->rc_dev != NULL)
+ rc_keydown(d->rc_dev, key, 0);
+ }
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+static int it913x_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ int ret = 0, firm_no;
+ u8 reg, remote;
+
+ firm_no = it913x_return_status(udev);
+
+ /* checnk for dual mode */
+ it913x_config.dual_mode = it913x_read_reg(udev, 0x49c5);
+
+ /* TODO different remotes */
+ remote = it913x_read_reg(udev, 0x49ac); /* Remote */
+ if (remote == 0)
+ props->rc.core.rc_codes = NULL;
+
+ /* TODO at the moment tuner_id is always assigned to 0x38 */
+ it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0);
+
+ info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode
+ , remote, it913x_config.tuner_id_0);
+
+ if (firm_no > 0) {
+ *cold = 0;
+ return 0;
+ }
+
+ if (it913x_config.dual_mode) {
+ it913x_config.tuner_id_1 = it913x_read_reg(udev, 0x49e0);
+ ret = it913x_wr_reg(udev, DEV_0, GPIOH1_EN, 0x1);
+ ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_ON, 0x1);
+ ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1);
+ msleep(50);
+ ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x0);
+ msleep(50);
+ reg = it913x_read_reg(udev, GPIOH1_O);
+ if (reg == 0) {
+ ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1);
+ ret |= it913x_return_status(udev);
+ if (ret != 0)
+ ret = it913x_wr_reg(udev, DEV_0,
+ GPIOH1_O, 0x0);
+ }
+ props->num_adapters = 2;
+ } else
+ props->num_adapters = 1;
+
+ reg = it913x_read_reg(udev, IO_MUX_POWER_CLK);
+
+ if (it913x_config.dual_mode) {
+ ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR);
+ ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x1);
+ } else {
+ ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, 0x0);
+ ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x0);
+ }
+
+ *cold = 1;
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ int ret = 0;
+ u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
+
+ if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
+ return -EAGAIN;
+ deb_info(1, "STM (%02x)", onoff);
+
+ if (!onoff)
+ ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1);
+
+
+ mutex_unlock(&adap->dev->i2c_mutex);
+
+ return ret;
+}
+
+
+static int it913x_download_firmware(struct usb_device *udev,
+ const struct firmware *fw)
+{
+ int ret = 0, i;
+ u8 packet_size, dlen;
+ u8 *fw_data;
+
+ packet_size = 0x29;
+
+ ret = it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_100);
+
+ info("FRM Starting Firmware Download");
+ /* This uses scatter write firmware headers follow */
+ /* 03 XX 00 XX = chip number? */
+
+ for (i = 0; i < fw->size; i += packet_size) {
+ if (i > 0)
+ packet_size = 0x39;
+ fw_data = (u8 *)(fw->data + i);
+ dlen = ((i + packet_size) > fw->size)
+ ? (fw->size - i) : packet_size;
+ ret |= it913x_io(udev, WRITE_DATA, DEV_0,
+ CMD_SCATTER_WRITE, 0, 0, fw_data, dlen);
+ udelay(1000);
+ }
+
+ ret |= it913x_io(udev, WRITE_CMD, DEV_0,
+ CMD_BOOT, 0, 0, NULL, 0);
+
+ msleep(100);
+
+ if (ret < 0)
+ info("FRM Firmware Download Failed (%04x)" , ret);
+ else
+ info("FRM Firmware Download Completed - Resetting Device");
+
+ ret |= it913x_return_status(udev);
+
+ msleep(30);
+
+ ret |= it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400);
+
+ /* Tuner function */
+ if (it913x_config.dual_mode)
+ ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0);
+
+ ret |= it913x_wr_reg(udev, DEV_0, PADODPU, 0x0);
+ ret |= it913x_wr_reg(udev, DEV_0, AGC_O_D, 0x0);
+ if (it913x_config.dual_mode) {
+ ret |= it913x_wr_reg(udev, DEV_1, PADODPU, 0x0);
+ ret |= it913x_wr_reg(udev, DEV_1, AGC_O_D, 0x0);
+ }
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_name(struct dvb_usb_adapter *adap)
+{
+ const char *desc = adap->dev->desc->name;
+ char *fe_name[] = {"_1", "_2", "_3", "_4"};
+ char *name = adap->fe_adap[0].fe->ops.info.name;
+
+ strlcpy(name, desc, 128);
+ strlcat(name, fe_name[adap->id], 128);
+
+ return 0;
+}
+
+static int it913x_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct usb_device *udev = adap->dev->udev;
+ int ret = 0;
+ u8 adf = it913x_read_reg(udev, IO_MUX_POWER_CLK);
+ u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5);
+ u16 ep_size = adap->props.fe[0].stream.u.bulk.buffersize;
+ u8 tuner_id, tuner_type;
+
+ if (adap->id == 0)
+ tuner_id = it913x_config.tuner_id_0;
+ else
+ tuner_id = it913x_config.tuner_id_1;
+
+ /* TODO we always use IT9137 possible references here*/
+ /* Documentation suggests don't care */
+ switch (tuner_id) {
+ case 0x51:
+ case 0x52:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ default:
+ case 0x38:
+ tuner_type = IT9137;
+ }
+
+ adap->fe_adap[0].fe = dvb_attach(it913x_fe_attach,
+ &adap->dev->i2c_adap, adap_addr, adf, tuner_type);
+
+ if (adap->id == 0 && adap->fe_adap[0].fe) {
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x1);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_SW_RST, 0x1);
+ ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x0f);
+ ret = it913x_wr_reg(udev, DEV_0, EP0_TX_NAK, 0x1b);
+ ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x2f);
+ ret = it913x_wr_reg(udev, DEV_0, EP4_TX_LEN_LSB,
+ ep_size & 0xff);
+ ret = it913x_wr_reg(udev, DEV_0, EP4_TX_LEN_MSB, ep_size >> 8);
+ ret = it913x_wr_reg(udev, DEV_0, EP4_MAX_PKT, 0x80);
+ } else if (adap->id == 1 && adap->fe_adap[0].fe) {
+ ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x6f);
+ ret = it913x_wr_reg(udev, DEV_0, EP5_TX_LEN_LSB,
+ ep_size & 0xff);
+ ret = it913x_wr_reg(udev, DEV_0, EP5_TX_LEN_MSB, ep_size >> 8);
+ ret = it913x_wr_reg(udev, DEV_0, EP5_MAX_PKT, 0x80);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_EN, 0x1);
+ ret = it913x_wr_reg(udev, DEV_1_DMOD, MP2IF_SERIAL, 0x1);
+ ret = it913x_wr_reg(udev, DEV_1, TOP_HOSTB_SER_MODE, 0x1);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, TSIS_ENABLE, 0x1);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x0);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_SW_RST, 0x0);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_HALF_PSB, 0x0);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF_STOP_EN, 0x1);
+ ret = it913x_wr_reg(udev, DEV_1_DMOD, MPEG_FULL_SPEED, 0x0);
+ ret = it913x_wr_reg(udev, DEV_1_DMOD, MP2IF_STOP_EN, 0x0);
+ } else
+ return -ENODEV;
+
+ ret = it913x_name(adap);
+
+ return ret;
+}
+
+/* DVB USB Driver */
+static struct dvb_usb_device_properties it913x_properties;
+
+static int it913x_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ cmd_counter = 0;
+ if (0 == dvb_usb_device_init(intf, &it913x_properties,
+ THIS_MODULE, NULL, adapter_nr)) {
+ info("DEV registering device driver");
+ return 0;
+ }
+
+ info("DEV it913x Error");
+ return -ENODEV;
+
+}
+
+static struct usb_device_id it913x_table[] = {
+ { USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09) },
+ { USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135) },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, it913x_table);
+
+static struct dvb_usb_device_properties it913x_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .download_firmware = it913x_download_firmware,
+ .firmware = "dvb-usb-it9137-01.fw",
+ .no_reconnect = 1,
+ .size_of_priv = sizeof(struct it913x_state),
+ .num_adapters = 2,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER|
+ DVB_USB_ADAP_NEED_PID_FILTERING|
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .streaming_ctrl = it913x_streaming_ctrl,
+ .pid_filter_count = 31,
+ .pid_filter = it913x_pid_filter,
+ .pid_filter_ctrl = it913x_pid_filter_ctrl,
+ .frontend_attach = it913x_frontend_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 10,
+ .endpoint = 0x04,
+ .u = {/* Keep Low if PID filter on */
+ .bulk = {
+ .buffersize = 3584,
+
+ }
+ }
+ }
+ }},
+ },
+ {
+ .num_frontends = 1,
+ .fe = {{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER|
+ DVB_USB_ADAP_NEED_PID_FILTERING|
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .streaming_ctrl = it913x_streaming_ctrl,
+ .pid_filter_count = 31,
+ .pid_filter = it913x_pid_filter,
+ .pid_filter_ctrl = it913x_pid_filter_ctrl,
+ .frontend_attach = it913x_frontend_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x05,
+ .u = {
+ .bulk = {
+ .buffersize = 3584,
+
+ }
+ }
+ }
+ }},
+ }
+ },
+ .identify_state = it913x_identify_state,
+ .rc.core = {
+ .protocol = RC_TYPE_NEC,
+ .module_name = "it913x",
+ .rc_query = it913x_rc_query,
+ .rc_interval = IT913X_POLL,
+ .allowed_protos = RC_TYPE_NEC,
+ .rc_codes = RC_MAP_KWORLD_315U,
+ },
+ .i2c_algo = &it913x_i2c_algo,
+ .num_device_descs = 2,
+ .devices = {
+ { "Kworld UB499-2T T09(IT9137)",
+ { &it913x_table[0], NULL },
+ },
+ { "ITE 9135 Generic",
+ { &it913x_table[1], NULL },
+ },
+ }
+};
+
+static struct usb_driver it913x_driver = {
+ .name = "it913x",
+ .probe = it913x_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = it913x_table,
+};
+
+/* module stuff */
+static int __init it913x_module_init(void)
+{
+ int result = usb_register(&it913x_driver);
+ if (result) {
+ err("usb_register failed. Error number %d", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit it913x_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&it913x_driver);
+}
+
+module_init(it913x_module_init);
+module_exit(it913x_module_exit);
+
+MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
+MODULE_DESCRIPTION("it913x USB 2 Driver");
+MODULE_VERSION("1.07");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c
index 37b146961ae2..b9228240f5ce 100644
--- a/drivers/media/dvb/dvb-usb/lmedm04.c
+++ b/drivers/media/dvb/dvb-usb/lmedm04.c
@@ -162,7 +162,7 @@ static int lme2510_usb_talk(struct dvb_usb_device *d,
int ret = 0;
if (st->usb_buffer == NULL) {
- st->usb_buffer = kmalloc(512, GFP_KERNEL);
+ st->usb_buffer = kmalloc(64, GFP_KERNEL);
if (st->usb_buffer == NULL) {
info("MEM Error no memory");
return -ENOMEM;
@@ -175,8 +175,8 @@ static int lme2510_usb_talk(struct dvb_usb_device *d,
if (ret < 0)
return -EAGAIN;
- /* the read/write capped at 512 */
- memcpy(buff, wbuf, (wlen > 512) ? 512 : wlen);
+ /* the read/write capped at 64 */
+ memcpy(buff, wbuf, (wlen < 64) ? wlen : 64);
ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01));
@@ -186,8 +186,8 @@ static int lme2510_usb_talk(struct dvb_usb_device *d,
ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01));
- ret |= lme2510_bulk_read(d->udev, buff, (rlen > 512) ?
- 512 : rlen , 0x01);
+ ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ?
+ rlen : 64 , 0x01);
if (rlen > 0)
memcpy(rbuf, buff, rlen);
@@ -333,7 +333,7 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap)
if (lme_int->lme_urb == NULL)
return -ENOMEM;
- lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 5000, GFP_ATOMIC,
+ lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 128, GFP_ATOMIC,
&lme_int->lme_urb->transfer_dma);
if (lme_int->buffer == NULL)
@@ -343,10 +343,10 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap)
adap->dev->udev,
usb_rcvintpipe(adap->dev->udev, 0xa),
lme_int->buffer,
- 4096,
+ 128,
lme2510_int_response,
adap,
- 11);
+ 8);
lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@@ -580,7 +580,7 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct lme2510_state *st = d->priv;
- static u8 obuf[64], ibuf[512];
+ static u8 obuf[64], ibuf[64];
int i, read, read_o;
u16 len;
u8 gate = st->i2c_gate;
@@ -621,7 +621,7 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
len = msg[i].len+3;
}
- if (lme2510_msg(d, obuf, len, ibuf, 512) < 0) {
+ if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) {
deb_info(1, "i2c transfer failed.");
return -EAGAIN;
}
@@ -941,7 +941,7 @@ static int lme_name(struct dvb_usb_adapter *adap)
const char *desc = adap->dev->desc->name;
char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395",
" SHARP:BS2F7HZ0194"};
- char *name = adap->fe->ops.info.name;
+ char *name = adap->fe_adap[0].fe->ops.info.name;
strlcpy(name, desc, 128);
strlcat(name, fe_name[st->tuner_config], 128);
@@ -958,10 +958,10 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
st->i2c_talk_onoff = 1;
st->i2c_gate = 4;
- adap->fe = dvb_attach(tda10086_attach, &tda10086_config,
+ adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config,
&adap->dev->i2c_adap);
- if (adap->fe) {
+ if (adap->fe_adap[0].fe) {
info("TUN Found Frontend TDA10086");
st->i2c_tuner_gate_w = 4;
st->i2c_tuner_gate_r = 4;
@@ -975,9 +975,9 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
}
st->i2c_gate = 4;
- adap->fe = dvb_attach(stv0299_attach, &sharp_z0194_config,
+ adap->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194_config,
&adap->dev->i2c_adap);
- if (adap->fe) {
+ if (adap->fe_adap[0].fe) {
info("FE Found Stv0299");
st->i2c_tuner_gate_w = 4;
st->i2c_tuner_gate_r = 5;
@@ -991,9 +991,9 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
}
st->i2c_gate = 5;
- adap->fe = dvb_attach(stv0288_attach, &lme_config,
+ adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config,
&adap->dev->i2c_adap);
- if (adap->fe) {
+ if (adap->fe_adap[0].fe) {
info("FE Found Stv0288");
st->i2c_tuner_gate_w = 4;
st->i2c_tuner_gate_r = 5;
@@ -1010,15 +1010,15 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
end: if (ret) {
- if (adap->fe) {
- dvb_frontend_detach(adap->fe);
- adap->fe = NULL;
+ if (adap->fe_adap[0].fe) {
+ dvb_frontend_detach(adap->fe_adap[0].fe);
+ adap->fe_adap[0].fe = NULL;
}
adap->dev->props.rc.core.rc_codes = NULL;
return -ENODEV;
}
- adap->fe->ops.set_voltage = dm04_lme2510_set_voltage;
+ adap->fe_adap[0].fe->ops.set_voltage = dm04_lme2510_set_voltage;
ret = lme_name(adap);
return ret;
}
@@ -1031,17 +1031,17 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap)
switch (st->tuner_config) {
case TUNER_LG:
- if (dvb_attach(tda826x_attach, adap->fe, 0xc0,
+ if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0xc0,
&adap->dev->i2c_adap, 1))
ret = st->tuner_config;
break;
case TUNER_S7395:
- if (dvb_attach(ix2505v_attach , adap->fe, &lme_tuner,
+ if (dvb_attach(ix2505v_attach , adap->fe_adap[0].fe, &lme_tuner,
&adap->dev->i2c_adap))
ret = st->tuner_config;
break;
case TUNER_S0194:
- if (dvb_attach(dvb_pll_attach , adap->fe, 0xc0,
+ if (dvb_attach(dvb_pll_attach , adap->fe_adap[0].fe, 0xc0,
&adap->dev->i2c_adap, DVB_PLL_OPERA1))
ret = st->tuner_config;
break;
@@ -1145,6 +1145,8 @@ static struct dvb_usb_device_properties lme2510_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER|
DVB_USB_ADAP_NEED_PID_FILTERING|
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1166,6 +1168,7 @@ static struct dvb_usb_device_properties lme2510_properties = {
}
}
}
+ }},
}
},
.rc.core = {
@@ -1193,6 +1196,8 @@ static struct dvb_usb_device_properties lme2510c_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER|
DVB_USB_ADAP_NEED_PID_FILTERING|
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1214,6 +1219,7 @@ static struct dvb_usb_device_properties lme2510c_properties = {
}
}
}
+ }},
}
},
.rc.core = {
@@ -1241,7 +1247,7 @@ static void *lme2510_exit_int(struct dvb_usb_device *d)
void *buffer = NULL;
if (adap != NULL) {
- lme2510_kill_urb(&adap->stream);
+ lme2510_kill_urb(&adap->fe_adap[0].stream);
adap->feedcount = 0;
}
@@ -1255,7 +1261,7 @@ static void *lme2510_exit_int(struct dvb_usb_device *d)
if (st->lme_urb != NULL) {
usb_kill_urb(st->lme_urb);
- usb_free_coherent(d->udev, 5000, st->buffer,
+ usb_free_coherent(d->udev, 128, st->buffer,
st->lme_urb->transfer_dma);
info("Interrupt Service Stopped");
}
@@ -1306,5 +1312,5 @@ module_exit(lme2510_module_exit);
MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0");
-MODULE_VERSION("1.88");
+MODULE_VERSION("1.90");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c
index 9456792f219b..a1e1287c949e 100644
--- a/drivers/media/dvb/dvb-usb/m920x.c
+++ b/drivers/media/dvb/dvb-usb/m920x.c
@@ -86,12 +86,12 @@ static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq)
}
for (i = 0; i < d->props.num_adapters; i++)
- flags |= d->adapter[i].props.caps;
+ flags |= d->adapter[i].props.fe[0].caps;
/* Some devices(Dposh) might crash if we attempt touch at all. */
if (flags & DVB_USB_ADAP_HAS_PID_FILTER) {
for (i = 0; i < d->props.num_adapters; i++) {
- epi = d->adapter[i].props.stream.endpoint - 0x81;
+ epi = d->adapter[i].props.fe[0].stream.endpoint - 0x81;
if (epi < 0 || epi >= M9206_MAX_ADAPTERS) {
printk(KERN_INFO "m920x: Unexpected adapter endpoint!\n");
@@ -292,7 +292,7 @@ static int m920x_update_filters(struct dvb_usb_adapter *adap)
struct m920x_state *m = adap->dev->priv;
int enabled = m->filtering_enabled[adap->id];
int i, ret = 0, filter = 0;
- int ep = adap->props.stream.endpoint;
+ int ep = adap->props.fe[0].stream.endpoint;
for (i = 0; i < M9206_MAX_FILTERS; i++)
if (m->filters[adap->id][i] == 8192)
@@ -501,9 +501,10 @@ static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if ((adap->fe = dvb_attach(mt352_attach,
- &m920x_mt352_config,
- &adap->dev->i2c_adap)) == NULL)
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach,
+ &m920x_mt352_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) == NULL)
return -EIO;
return 0;
@@ -513,9 +514,10 @@ static int m920x_tda10046_08_frontend_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if ((adap->fe = dvb_attach(tda10046_attach,
- &m920x_tda10046_08_config,
- &adap->dev->i2c_adap)) == NULL)
+ adap->fe_adap[0].fe = dvb_attach(tda10046_attach,
+ &m920x_tda10046_08_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) == NULL)
return -EIO;
return 0;
@@ -525,9 +527,10 @@ static int m920x_tda10046_0b_frontend_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if ((adap->fe = dvb_attach(tda10046_attach,
- &m920x_tda10046_0b_config,
- &adap->dev->i2c_adap)) == NULL)
+ adap->fe_adap[0].fe = dvb_attach(tda10046_attach,
+ &m920x_tda10046_0b_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) == NULL)
return -EIO;
return 0;
@@ -537,7 +540,7 @@ static int m920x_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if (dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap, &m920x_qt1010_config) == NULL)
+ if (dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &m920x_qt1010_config) == NULL)
return -ENODEV;
return 0;
@@ -547,7 +550,7 @@ static int m920x_tda8275_60_tuner_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if (dvb_attach(tda827x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, NULL) == NULL)
+ if (dvb_attach(tda827x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, NULL) == NULL)
return -ENODEV;
return 0;
@@ -557,7 +560,7 @@ static int m920x_tda8275_61_tuner_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if (dvb_attach(tda827x_attach, adap->fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL)
+ if (dvb_attach(tda827x_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL)
return -ENODEV;
return 0;
@@ -565,7 +568,7 @@ static int m920x_tda8275_61_tuner_attach(struct dvb_usb_adapter *adap)
static int m920x_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(simple_tuner_attach, adap->fe,
+ dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, 0x61,
TUNER_PHILIPS_FMD1216ME_MK3);
return 0;
@@ -807,6 +810,9 @@ static struct dvb_usb_device_properties megasky_properties = {
.identify_state = m920x_identify_state,
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -827,6 +833,7 @@ static struct dvb_usb_device_properties megasky_properties = {
}
}
},
+ }},
}},
.i2c_algo = &m920x_i2c_algo,
@@ -851,6 +858,9 @@ static struct dvb_usb_device_properties digivox_mini_ii_properties = {
.identify_state = m920x_identify_state,
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -871,6 +881,7 @@ static struct dvb_usb_device_properties digivox_mini_ii_properties = {
}
}
},
+ }},
}},
.i2c_algo = &m920x_i2c_algo,
@@ -910,6 +921,9 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = {
.identify_state = m920x_identify_state,
.num_adapters = 2,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -929,7 +943,11 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = {
.buffersize = 512,
}
}
+ }},
}},{
+ .num_frontends = 1,
+ .fe = {{
+
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -949,6 +967,7 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = {
.buffersize = 512,
}
}
+ }},
},
}},
.i2c_algo = &m920x_i2c_algo,
@@ -974,6 +993,8 @@ static struct dvb_usb_device_properties dposh_properties = {
.identify_state = m920x_identify_state,
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
/* Hardware pid filters don't work with this device/firmware */
.frontend_attach = m920x_mt352_frontend_attach,
@@ -989,6 +1010,7 @@ static struct dvb_usb_device_properties dposh_properties = {
}
}
},
+ }},
}},
.i2c_algo = &m920x_i2c_algo,
@@ -1019,6 +1041,9 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = {
.identify_state = m920x_identify_state,
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1041,6 +1066,7 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = {
}
}
},
+ }},
} },
.i2c_algo = &m920x_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.c b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c
new file mode 100644
index 000000000000..d1f58371c711
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c
@@ -0,0 +1,614 @@
+/*
+ * mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mxl111sf-demod.h"
+#include "mxl111sf-reg.h"
+
+/* debug */
+static int mxl111sf_demod_debug;
+module_param_named(debug, mxl111sf_demod_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define mxl_dbg(fmt, arg...) \
+ if (mxl111sf_demod_debug) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+/* ------------------------------------------------------------------------ */
+
+struct mxl111sf_demod_state {
+ struct mxl111sf_state *mxl_state;
+
+ struct mxl111sf_demod_config *cfg;
+
+ struct dvb_frontend fe;
+};
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state,
+ u8 addr, u8 *data)
+{
+ return (state->cfg->read_reg) ?
+ state->cfg->read_reg(state->mxl_state, addr, data) :
+ -EINVAL;
+}
+
+static int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state,
+ u8 addr, u8 data)
+{
+ return (state->cfg->write_reg) ?
+ state->cfg->write_reg(state->mxl_state, addr, data) :
+ -EINVAL;
+}
+
+static
+int mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
+{
+ return (state->cfg->program_regs) ?
+ state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
+ -EINVAL;
+}
+
+/* ------------------------------------------------------------------------ */
+/* TPS */
+
+static
+int mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state,
+ fe_code_rate_t *code_rate)
+{
+ u8 val;
+ int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val);
+ /* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch (val & V6_CODE_RATE_TPS_MASK) {
+ case 0:
+ *code_rate = FEC_1_2;
+ break;
+ case 1:
+ *code_rate = FEC_2_3;
+ break;
+ case 2:
+ *code_rate = FEC_3_4;
+ break;
+ case 3:
+ *code_rate = FEC_5_6;
+ break;
+ case 4:
+ *code_rate = FEC_7_8;
+ break;
+ }
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_constellation(struct mxl111sf_demod_state *state,
+ fe_modulation_t *constellation)
+{
+ u8 val;
+ int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val);
+ /* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) {
+ case 0:
+ *constellation = QPSK;
+ break;
+ case 1:
+ *constellation = QAM_16;
+ break;
+ case 2:
+ *constellation = QAM_64;
+ break;
+ }
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state,
+ fe_transmit_mode_t *fft_mode)
+{
+ u8 val;
+ int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val);
+ /* FFT Mode, 00:2K, 01:8K, 10:4K */
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) {
+ case 0:
+ *fft_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ *fft_mode = TRANSMISSION_MODE_8K;
+ break;
+ case 2:
+ *fft_mode = TRANSMISSION_MODE_4K;
+ break;
+ }
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state,
+ fe_guard_interval_t *guard)
+{
+ u8 val;
+ int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val);
+ /* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch ((val & V6_PARAM_GI_MASK) >> 4) {
+ case 0:
+ *guard = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ *guard = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ *guard = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ *guard = GUARD_INTERVAL_1_4;
+ break;
+ }
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state,
+ fe_hierarchy_t *hierarchy)
+{
+ u8 val;
+ int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val);
+ /* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) {
+ case 0:
+ *hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ *hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ *hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ *hierarchy = HIERARCHY_4;
+ break;
+ }
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+/* LOCKS */
+
+static
+int mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state,
+ int *sync_lock)
+{
+ u8 val = 0;
+ int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+ *sync_lock = (val & SYNC_LOCK_MASK) >> 4;
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state,
+ int *rs_lock)
+{
+ u8 val = 0;
+ int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+ *rs_lock = (val & RS_LOCK_DET_MASK) >> 3;
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state,
+ int *tps_lock)
+{
+ u8 val = 0;
+ int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+ *tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6;
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state,
+ int *fec_lock)
+{
+ u8 val = 0;
+ int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+ *fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4;
+fail:
+ return ret;
+}
+
+#if 0
+static
+int mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state,
+ int *cp_lock)
+{
+ u8 val = 0;
+ int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+ *cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2;
+fail:
+ return ret;
+}
+#endif
+
+static int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state)
+{
+ return mxl111sf_demod_write_reg(state, 0x0e, 0xff);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_demod_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ int ret = 0;
+
+ struct mxl111sf_reg_ctrl_info phy_pll_patch[] = {
+ {0x00, 0xff, 0x01}, /* change page to 1 */
+ {0x40, 0xff, 0x05},
+ {0x40, 0xff, 0x01},
+ {0x41, 0xff, 0xca},
+ {0x41, 0xff, 0xc0},
+ {0x00, 0xff, 0x00}, /* change page to 0 */
+ {0, 0, 0}
+ };
+
+ mxl_dbg("()");
+
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe, param);
+ if (mxl_fail(ret))
+ goto fail;
+ msleep(50);
+ }
+ ret = mxl111sf_demod_program_regs(state, phy_pll_patch);
+ mxl_fail(ret);
+ msleep(50);
+ ret = mxl1x1sf_demod_reset_irq_status(state);
+ mxl_fail(ret);
+ msleep(100);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#if 0
+/* resets TS Packet error count */
+/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */
+static
+int mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state)
+{
+ struct mxl111sf_reg_ctrl_info reset_per_count[] = {
+ {0x20, 0x01, 0x01},
+ {0x20, 0x01, 0x00},
+ {0, 0, 0}
+ };
+ return mxl111sf_demod_program_regs(state, reset_per_count);
+}
+#endif
+
+/* returns TS Packet error count */
+/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */
+static int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ u32 fec_per_count, fec_per_scale;
+ u8 val;
+ int ret;
+
+ *ucblocks = 0;
+
+ /* FEC_PER_COUNT Register */
+ ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+
+ fec_per_count = val;
+
+ /* FEC_PER_SCALE Register */
+ ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+
+ val &= V6_FEC_PER_SCALE_MASK;
+ val *= 4;
+
+ fec_per_scale = 1 << val;
+
+ fec_per_count *= fec_per_scale;
+
+ *ucblocks = fec_per_count;
+fail:
+ return ret;
+}
+
+#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS
+/* FIXME: leaving this enabled breaks the build on some architectures,
+ * and we shouldn't have any floating point math in the kernel, anyway.
+ *
+ * These macros need to be re-written, but it's harmless to simply
+ * return zero for now. */
+#define CALCULATE_BER(avg_errors, count) \
+ ((u32)(avg_errors * 4)/(count*64*188*8))
+#define CALCULATE_SNR(data) \
+ ((u32)((10 * (u32)data / 64) - 2.5))
+#else
+#define CALCULATE_BER(avg_errors, count) 0
+#define CALCULATE_SNR(data) 0
+#endif
+
+static int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ u8 val1, val2, val3;
+ int ret;
+
+ *ber = 0;
+
+ ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *ber = CALCULATE_BER((val1 | (val2 << 8)), val3);
+fail:
+ return ret;
+}
+
+static int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state,
+ u16 *snr)
+{
+ u8 val1, val2;
+ int ret;
+
+ *snr = 0;
+
+ ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8));
+fail:
+ return ret;
+}
+
+static int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+
+ int ret = mxl111sf_demod_calc_snr(state, snr);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *snr /= 10; /* 0.1 dB */
+fail:
+ return ret;
+}
+
+static int mxl111sf_demod_read_status(struct dvb_frontend *fe,
+ fe_status_t *status)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ int ret, locked, cr_lock, sync_lock, fec_lock;
+
+ *status = 0;
+
+ ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (locked)
+ *status |= FE_HAS_SIGNAL;
+ if (cr_lock)
+ *status |= FE_HAS_CARRIER;
+ if (sync_lock)
+ *status |= FE_HAS_SYNC;
+ if (fec_lock) /* false positives? */
+ *status |= FE_HAS_VITERBI;
+
+ if ((locked) && (cr_lock) && (sync_lock))
+ *status |= FE_HAS_LOCK;
+fail:
+ return ret;
+}
+
+static int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ fe_modulation_t constellation;
+ u16 snr;
+
+ mxl111sf_demod_calc_snr(state, &snr);
+ mxl1x1sf_demod_get_tps_constellation(state, &constellation);
+
+ switch (constellation) {
+ case QPSK:
+ *signal_strength = (snr >= 1300) ?
+ min(65535, snr * 44) : snr * 38;
+ break;
+ case QAM_16:
+ *signal_strength = (snr >= 1500) ?
+ min(65535, snr * 38) : snr * 33;
+ break;
+ case QAM_64:
+ *signal_strength = (snr >= 2000) ?
+ min(65535, snr * 29) : snr * 25;
+ break;
+ default:
+ *signal_strength = 0;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+
+ mxl_dbg("()");
+#if 0
+ p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF;
+#endif
+ if (fe->ops.tuner_ops.get_bandwidth)
+ fe->ops.tuner_ops.get_bandwidth(fe, &p->u.ofdm.bandwidth);
+ if (fe->ops.tuner_ops.get_frequency)
+ fe->ops.tuner_ops.get_frequency(fe, &p->frequency);
+ mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_HP);
+ mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_LP);
+ mxl1x1sf_demod_get_tps_constellation(state, &p->u.ofdm.constellation);
+ mxl1x1sf_demod_get_tps_guard_fft_mode(state,
+ &p->u.ofdm.transmission_mode);
+ mxl1x1sf_demod_get_tps_guard_interval(state,
+ &p->u.ofdm.guard_interval);
+ mxl1x1sf_demod_get_tps_hierarchy(state,
+ &p->u.ofdm.hierarchy_information);
+
+ return 0;
+}
+
+static
+int mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static void mxl111sf_demod_release(struct dvb_frontend *fe)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ mxl_dbg("()");
+ kfree(state);
+ fe->demodulator_priv = NULL;
+}
+
+static struct dvb_frontend_ops mxl111sf_demod_ops = {
+
+ .info = {
+ .name = "MaxLinear MxL111SF DVB-T demodulator",
+ .type = FE_OFDM,
+ .frequency_min = 177000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 166666,
+ .caps = 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 | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
+ },
+ .release = mxl111sf_demod_release,
+#if 0
+ .init = mxl111sf_init,
+ .i2c_gate_ctrl = mxl111sf_i2c_gate_ctrl,
+#endif
+ .set_frontend = mxl111sf_demod_set_frontend,
+ .get_frontend = mxl111sf_demod_get_frontend,
+ .get_tune_settings = mxl111sf_demod_get_tune_settings,
+ .read_status = mxl111sf_demod_read_status,
+ .read_signal_strength = mxl111sf_demod_read_signal_strength,
+ .read_ber = mxl111sf_demod_read_ber,
+ .read_snr = mxl111sf_demod_read_snr,
+ .read_ucblocks = mxl111sf_demod_read_ucblocks,
+};
+
+struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
+ struct mxl111sf_demod_config *cfg)
+{
+ struct mxl111sf_demod_state *state = NULL;
+
+ mxl_dbg("()");
+
+ state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+
+ state->mxl_state = mxl_state;
+ state->cfg = cfg;
+
+ memcpy(&state->fe.ops, &mxl111sf_demod_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ state->fe.demodulator_priv = state;
+ return &state->fe;
+}
+EXPORT_SYMBOL_GPL(mxl111sf_demod_attach);
+
+MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver");
+MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.h b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h
new file mode 100644
index 000000000000..432706ae5274
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h
@@ -0,0 +1,55 @@
+/*
+ * mxl111sf-demod.h - driver for the MaxLinear MXL111SF DVB-T demodulator
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MXL111SF_DEMOD_H__
+#define __MXL111SF_DEMOD_H__
+
+#include "dvb_frontend.h"
+#include "mxl111sf.h"
+
+struct mxl111sf_demod_config {
+ int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data);
+ int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data);
+ int (*program_regs)(struct mxl111sf_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
+};
+
+#if defined(CONFIG_DVB_USB_MXL111SF) || \
+ (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE))
+extern
+struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
+ struct mxl111sf_demod_config *cfg);
+#else
+static inline
+struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
+ struct mxl111sf_demod_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_USB_MXL111SF */
+
+#endif /* __MXL111SF_DEMOD_H__ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c
new file mode 100644
index 000000000000..e4121cb8f5ef
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c
@@ -0,0 +1,763 @@
+/*
+ * mxl111sf-gpio.c - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mxl111sf-gpio.h"
+#include "mxl111sf-i2c.h"
+#include "mxl111sf.h"
+
+/* ------------------------------------------------------------------------- */
+
+#define MXL_GPIO_MUX_REG_0 0x84
+#define MXL_GPIO_MUX_REG_1 0x89
+#define MXL_GPIO_MUX_REG_2 0x82
+
+#define MXL_GPIO_DIR_INPUT 0
+#define MXL_GPIO_DIR_OUTPUT 1
+
+
+static int mxl111sf_set_gpo_state(struct mxl111sf_state *state, u8 pin, u8 val)
+{
+ int ret;
+ u8 tmp;
+
+ mxl_debug_adv("(%d, %d)", pin, val);
+
+ if ((pin > 0) && (pin < 8)) {
+ ret = mxl111sf_read_reg(state, 0x19, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ tmp &= ~(1 << (pin - 1));
+ tmp |= (val << (pin - 1));
+ ret = mxl111sf_write_reg(state, 0x19, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ } else if (pin <= 10) {
+ if (pin == 0)
+ pin += 7;
+ ret = mxl111sf_read_reg(state, 0x30, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ tmp &= ~(1 << (pin - 3));
+ tmp |= (val << (pin - 3));
+ ret = mxl111sf_write_reg(state, 0x30, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ } else
+ ret = -EINVAL;
+fail:
+ return ret;
+}
+
+static int mxl111sf_get_gpi_state(struct mxl111sf_state *state, u8 pin, u8 *val)
+{
+ int ret;
+ u8 tmp;
+
+ mxl_debug("(0x%02x)", pin);
+
+ *val = 0;
+
+ switch (pin) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ ret = mxl111sf_read_reg(state, 0x23, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ *val = (tmp >> (pin + 4)) & 0x01;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ ret = mxl111sf_read_reg(state, 0x2f, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ *val = (tmp >> pin) & 0x01;
+ break;
+ case 8:
+ case 9:
+ case 10:
+ ret = mxl111sf_read_reg(state, 0x22, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ *val = (tmp >> (pin - 3)) & 0x01;
+ break;
+ default:
+ return -EINVAL; /* invalid pin */
+ }
+fail:
+ return ret;
+}
+
+struct mxl_gpio_cfg {
+ u8 pin;
+ u8 dir;
+ u8 val;
+};
+
+static int mxl111sf_config_gpio_pins(struct mxl111sf_state *state,
+ struct mxl_gpio_cfg *gpio_cfg)
+{
+ int ret;
+ u8 tmp;
+
+ mxl_debug_adv("(%d, %d)", gpio_cfg->pin, gpio_cfg->dir);
+
+ switch (gpio_cfg->pin) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_0, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ tmp &= ~(1 << (gpio_cfg->pin + 4));
+ tmp |= (gpio_cfg->dir << (gpio_cfg->pin + 4));
+ ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_0, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_1, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ tmp &= ~(1 << gpio_cfg->pin);
+ tmp |= (gpio_cfg->dir << gpio_cfg->pin);
+ ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_1, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ break;
+ case 8:
+ case 9:
+ case 10:
+ ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_2, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ tmp &= ~(1 << (gpio_cfg->pin - 3));
+ tmp |= (gpio_cfg->dir << (gpio_cfg->pin - 3));
+ ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_2, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ break;
+ default:
+ return -EINVAL; /* invalid pin */
+ }
+
+ ret = (MXL_GPIO_DIR_OUTPUT == gpio_cfg->dir) ?
+ mxl111sf_set_gpo_state(state,
+ gpio_cfg->pin, gpio_cfg->val) :
+ mxl111sf_get_gpi_state(state,
+ gpio_cfg->pin, &gpio_cfg->val);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+static int mxl111sf_hw_do_set_gpio(struct mxl111sf_state *state,
+ int gpio, int direction, int val)
+{
+ struct mxl_gpio_cfg gpio_config = {
+ .pin = gpio,
+ .dir = direction,
+ .val = val,
+ };
+
+ mxl_debug("(%d, %d, %d)", gpio, direction, val);
+
+ return mxl111sf_config_gpio_pins(state, &gpio_config);
+}
+
+/* ------------------------------------------------------------------------- */
+
+#define PIN_MUX_MPEG_MODE_MASK 0x40 /* 0x17 <6> */
+#define PIN_MUX_MPEG_PAR_EN_MASK 0x01 /* 0x18 <0> */
+#define PIN_MUX_MPEG_SER_EN_MASK 0x02 /* 0x18 <1> */
+#define PIN_MUX_MPG_IN_MUX_MASK 0x80 /* 0x3D <7> */
+#define PIN_MUX_BT656_ENABLE_MASK 0x04 /* 0x12 <2> */
+#define PIN_MUX_I2S_ENABLE_MASK 0x40 /* 0x15 <6> */
+#define PIN_MUX_SPI_MODE_MASK 0x10 /* 0x3D <4> */
+#define PIN_MUX_MCLK_EN_CTRL_MASK 0x10 /* 0x82 <4> */
+#define PIN_MUX_MPSYN_EN_CTRL_MASK 0x20 /* 0x82 <5> */
+#define PIN_MUX_MDVAL_EN_CTRL_MASK 0x40 /* 0x82 <6> */
+#define PIN_MUX_MPERR_EN_CTRL_MASK 0x80 /* 0x82 <7> */
+#define PIN_MUX_MDAT_EN_0_MASK 0x10 /* 0x84 <4> */
+#define PIN_MUX_MDAT_EN_1_MASK 0x20 /* 0x84 <5> */
+#define PIN_MUX_MDAT_EN_2_MASK 0x40 /* 0x84 <6> */
+#define PIN_MUX_MDAT_EN_3_MASK 0x80 /* 0x84 <7> */
+#define PIN_MUX_MDAT_EN_4_MASK 0x10 /* 0x89 <4> */
+#define PIN_MUX_MDAT_EN_5_MASK 0x20 /* 0x89 <5> */
+#define PIN_MUX_MDAT_EN_6_MASK 0x40 /* 0x89 <6> */
+#define PIN_MUX_MDAT_EN_7_MASK 0x80 /* 0x89 <7> */
+
+int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state,
+ enum mxl111sf_mux_config pin_mux_config)
+{
+ u8 r12, r15, r17, r18, r3D, r82, r84, r89;
+ int ret;
+
+ mxl_debug("(%d)", pin_mux_config);
+
+ ret = mxl111sf_read_reg(state, 0x17, &r17);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x18, &r18);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x12, &r12);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x15, &r15);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x82, &r82);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x84, &r84);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x89, &r89);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x3D, &r3D);
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch (pin_mux_config) {
+ case PIN_MUX_TS_OUT_PARALLEL:
+ /* mpeg_mode = 1 */
+ r17 |= PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 1 */
+ r18 |= PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 1 */
+ r82 |= PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 1 */
+ r82 |= PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 1 */
+ r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 1 */
+ r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0xF */
+ r84 |= 0xF0;
+ /* mdat_en_ctrl[7:4] = 0xF */
+ r89 |= 0xF0;
+ break;
+ case PIN_MUX_TS_OUT_SERIAL:
+ /* mpeg_mode = 1 */
+ r17 |= PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 1 */
+ r18 |= PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 1 */
+ r82 |= PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 1 */
+ r82 |= PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 1 */
+ r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 1 */
+ r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0xF */
+ r84 |= 0xF0;
+ /* mdat_en_ctrl[7:4] = 0xF */
+ r89 |= 0xF0;
+ break;
+ case PIN_MUX_GPIO_MODE:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_TS_SERIAL_IN_MODE_0:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 1 */
+ r18 |= PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_TS_SERIAL_IN_MODE_1:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 1 */
+ r18 |= PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 1 */
+ r3D |= PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_TS_SPI_IN_MODE_1:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 1 */
+ r18 |= PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 1 */
+ r3D |= PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 1 */
+ r15 |= PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 1 */
+ r3D |= PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_TS_SPI_IN_MODE_0:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 1 */
+ r18 |= PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 1 */
+ r15 |= PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 1 */
+ r3D |= PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_TS_PARALLEL_IN:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 1 */
+ r18 |= PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_BT656_I2S_MODE:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 1 */
+ r12 |= PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 1 */
+ r15 |= PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_DEFAULT:
+ default:
+ /* mpeg_mode = 1 */
+ r17 |= PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ }
+
+ ret = mxl111sf_write_reg(state, 0x17, r17);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x18, r18);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x12, r12);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x15, r15);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x82, r82);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x84, r84);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x89, r89);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x3D, r3D);
+ if (mxl_fail(ret))
+ goto fail;
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int mxl111sf_hw_set_gpio(struct mxl111sf_state *state, int gpio, int val)
+{
+ return mxl111sf_hw_do_set_gpio(state, gpio, MXL_GPIO_DIR_OUTPUT, val);
+}
+
+static int mxl111sf_hw_gpio_initialize(struct mxl111sf_state *state)
+{
+ u8 gpioval = 0x07; /* write protect enabled, signal LEDs off */
+ int i, ret;
+
+ mxl_debug("()");
+
+ for (i = 3; i < 8; i++) {
+ ret = mxl111sf_hw_set_gpio(state, i, (gpioval >> i) & 0x01);
+ if (mxl_fail(ret))
+ break;
+ }
+
+ return ret;
+}
+
+#define PCA9534_I2C_ADDR (0x40 >> 1)
+static int pca9534_set_gpio(struct mxl111sf_state *state, int gpio, int val)
+{
+ u8 w[2] = { 1, 0 };
+ u8 r = 0;
+ struct i2c_msg msg[] = {
+ { .addr = PCA9534_I2C_ADDR,
+ .flags = 0, .buf = w, .len = 1 },
+ { .addr = PCA9534_I2C_ADDR,
+ .flags = I2C_M_RD, .buf = &r, .len = 1 },
+ };
+
+ mxl_debug("(%d, %d)", gpio, val);
+
+ /* read current GPIO levels from flip-flop */
+ i2c_transfer(&state->d->i2c_adap, msg, 2);
+
+ /* prepare write buffer with current GPIO levels */
+ msg[0].len = 2;
+#if 0
+ w[0] = 1;
+#endif
+ w[1] = r;
+
+ /* clear the desired GPIO */
+ w[1] &= ~(1 << gpio);
+
+ /* set the desired GPIO value */
+ w[1] |= ((val ? 1 : 0) << gpio);
+
+ /* write new GPIO levels to flip-flop */
+ i2c_transfer(&state->d->i2c_adap, &msg[0], 1);
+
+ return 0;
+}
+
+static int pca9534_init_port_expander(struct mxl111sf_state *state)
+{
+ u8 w[2] = { 1, 0x07 }; /* write protect enabled, signal LEDs off */
+
+ struct i2c_msg msg = {
+ .addr = PCA9534_I2C_ADDR,
+ .flags = 0, .buf = w, .len = 2
+ };
+
+ mxl_debug("()");
+
+ i2c_transfer(&state->d->i2c_adap, &msg, 1);
+
+ /* configure all pins as outputs */
+ w[0] = 3;
+ w[1] = 0;
+
+ i2c_transfer(&state->d->i2c_adap, &msg, 1);
+
+ return 0;
+}
+
+int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val)
+{
+ mxl_debug("(%d, %d)", gpio, val);
+
+ switch (state->gpio_port_expander) {
+ default:
+ mxl_printk(KERN_ERR,
+ "gpio_port_expander undefined, assuming PCA9534");
+ /* fall-thru */
+ case mxl111sf_PCA9534:
+ return pca9534_set_gpio(state, gpio, val);
+ case mxl111sf_gpio_hw:
+ return mxl111sf_hw_set_gpio(state, gpio, val);
+ }
+}
+
+static int mxl111sf_probe_port_expander(struct mxl111sf_state *state)
+{
+ int ret;
+ u8 w = 1;
+ u8 r = 0;
+ struct i2c_msg msg[] = {
+ { .flags = 0, .buf = &w, .len = 1 },
+ { .flags = I2C_M_RD, .buf = &r, .len = 1 },
+ };
+
+ mxl_debug("()");
+
+ msg[0].addr = 0x70 >> 1;
+ msg[1].addr = 0x70 >> 1;
+
+ /* read current GPIO levels from flip-flop */
+ ret = i2c_transfer(&state->d->i2c_adap, msg, 2);
+ if (ret == 2) {
+ state->port_expander_addr = msg[0].addr;
+ state->gpio_port_expander = mxl111sf_PCA9534;
+ mxl_debug("found port expander at 0x%02x",
+ state->port_expander_addr);
+ return 0;
+ }
+
+ msg[0].addr = 0x40 >> 1;
+ msg[1].addr = 0x40 >> 1;
+
+ ret = i2c_transfer(&state->d->i2c_adap, msg, 2);
+ if (ret == 2) {
+ state->port_expander_addr = msg[0].addr;
+ state->gpio_port_expander = mxl111sf_PCA9534;
+ mxl_debug("found port expander at 0x%02x",
+ state->port_expander_addr);
+ return 0;
+ }
+ state->port_expander_addr = 0xff;
+ state->gpio_port_expander = mxl111sf_gpio_hw;
+ mxl_debug("using hardware gpio");
+ return 0;
+}
+
+int mxl111sf_init_port_expander(struct mxl111sf_state *state)
+{
+ mxl_debug("()");
+
+ if (0x00 == state->port_expander_addr)
+ mxl111sf_probe_port_expander(state);
+
+ switch (state->gpio_port_expander) {
+ default:
+ mxl_printk(KERN_ERR,
+ "gpio_port_expander undefined, assuming PCA9534");
+ /* fall-thru */
+ case mxl111sf_PCA9534:
+ return pca9534_init_port_expander(state);
+ case mxl111sf_gpio_hw:
+ return mxl111sf_hw_gpio_initialize(state);
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+
+int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode)
+{
+/* GPO:
+ * 3 - ATSC/MH# | 1 = ATSC transport, 0 = MH transport | default 0
+ * 4 - ATSC_RST## | 1 = ATSC enable, 0 = ATSC Reset | default 0
+ * 5 - ATSC_EN | 1 = ATSC power enable, 0 = ATSC power off | default 0
+ * 6 - MH_RESET# | 1 = MH enable, 0 = MH Reset | default 0
+ * 7 - MH_EN | 1 = MH power enable, 0 = MH power off | default 0
+ */
+ mxl_debug("(%d)", mode);
+
+ switch (mode) {
+ case MXL111SF_GPIO_MOD_MH:
+ mxl111sf_set_gpio(state, 4, 0);
+ mxl111sf_set_gpio(state, 5, 0);
+ msleep(50);
+ mxl111sf_set_gpio(state, 7, 1);
+ msleep(50);
+ mxl111sf_set_gpio(state, 6, 1);
+ msleep(50);
+
+ mxl111sf_set_gpio(state, 3, 0);
+ break;
+ case MXL111SF_GPIO_MOD_ATSC:
+ mxl111sf_set_gpio(state, 6, 0);
+ mxl111sf_set_gpio(state, 7, 0);
+ msleep(50);
+ mxl111sf_set_gpio(state, 5, 1);
+ msleep(50);
+ mxl111sf_set_gpio(state, 4, 1);
+ msleep(50);
+ mxl111sf_set_gpio(state, 3, 1);
+ break;
+ default: /* DVBT / STANDBY */
+ mxl111sf_init_port_expander(state);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h
new file mode 100644
index 000000000000..0220f54299a5
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h
@@ -0,0 +1,56 @@
+/*
+ * mxl111sf-gpio.h - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _DVB_USB_MXL111SF_GPIO_H_
+#define _DVB_USB_MXL111SF_GPIO_H_
+
+#include "mxl111sf.h"
+
+int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val);
+int mxl111sf_init_port_expander(struct mxl111sf_state *state);
+
+#define MXL111SF_GPIO_MOD_DVBT 0
+#define MXL111SF_GPIO_MOD_MH 1
+#define MXL111SF_GPIO_MOD_ATSC 2
+int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode);
+
+enum mxl111sf_mux_config {
+ PIN_MUX_DEFAULT = 0,
+ PIN_MUX_TS_OUT_PARALLEL,
+ PIN_MUX_TS_OUT_SERIAL,
+ PIN_MUX_GPIO_MODE,
+ PIN_MUX_TS_SERIAL_IN_MODE_0,
+ PIN_MUX_TS_SERIAL_IN_MODE_1,
+ PIN_MUX_TS_SPI_IN_MODE_0,
+ PIN_MUX_TS_SPI_IN_MODE_1,
+ PIN_MUX_TS_PARALLEL_IN,
+ PIN_MUX_BT656_I2S_MODE,
+};
+
+int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state,
+ enum mxl111sf_mux_config pin_mux_config);
+
+#endif /* _DVB_USB_MXL111SF_GPIO_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c
new file mode 100644
index 000000000000..34434557ef65
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c
@@ -0,0 +1,850 @@
+/*
+ * mxl111sf-i2c.c - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mxl111sf-i2c.h"
+#include "mxl111sf.h"
+
+/* SW-I2C ----------------------------------------------------------------- */
+
+#define SW_I2C_ADDR 0x1a
+#define SW_I2C_EN 0x02
+#define SW_SCL_OUT 0x04
+#define SW_SDA_OUT 0x08
+#define SW_SDA_IN 0x04
+
+#define SW_I2C_BUSY_ADDR 0x2f
+#define SW_I2C_BUSY 0x02
+
+static int mxl111sf_i2c_bitbang_sendbyte(struct mxl111sf_state *state,
+ u8 byte)
+{
+ int i, ret;
+ u8 data = 0;
+
+ mxl_i2c("(0x%02x)", byte);
+
+ ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ for (i = 0; i < 8; i++) {
+
+ data = (byte & (0x80 >> i)) ? SW_SDA_OUT : 0;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | data | SW_SCL_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | data);
+ if (mxl_fail(ret))
+ goto fail;
+ }
+
+ /* last bit was 0 so we need to release SDA */
+ if (!(byte & 1)) {
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+ }
+
+ /* CLK high for ACK readback */
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* drop the CLK after getting ACK, SDA will go high right away */
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (data & SW_SDA_IN)
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static int mxl111sf_i2c_bitbang_recvbyte(struct mxl111sf_state *state,
+ u8 *pbyte)
+{
+ int i, ret;
+ u8 byte = 0;
+ u8 data = 0;
+
+ mxl_i2c("()");
+
+ *pbyte = 0;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ for (i = 0; i < 8; i++) {
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN |
+ SW_SCL_OUT | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (data & SW_SDA_IN)
+ byte |= (0x80 >> i);
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+ }
+ *pbyte = byte;
+fail:
+ return ret;
+}
+
+static int mxl111sf_i2c_start(struct mxl111sf_state *state)
+{
+ int ret;
+
+ mxl_i2c("()");
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN); /* start */
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+static int mxl111sf_i2c_stop(struct mxl111sf_state *state)
+{
+ int ret;
+
+ mxl_i2c("()");
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN); /* stop */
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_SCL_OUT | SW_SDA_OUT);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+static int mxl111sf_i2c_ack(struct mxl111sf_state *state)
+{
+ int ret;
+ u8 b = 0;
+
+ mxl_i2c("()");
+
+ ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &b);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* pull SDA low */
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+static int mxl111sf_i2c_nack(struct mxl111sf_state *state)
+{
+ int ret;
+
+ mxl_i2c("()");
+
+ /* SDA high to signal last byte read from slave */
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_i2c_sw_xfer_msg(struct mxl111sf_state *state,
+ struct i2c_msg *msg)
+{
+ int i, ret;
+
+ mxl_i2c("()");
+
+ if (msg->flags & I2C_M_RD) {
+
+ ret = mxl111sf_i2c_start(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_i2c_bitbang_sendbyte(state,
+ (msg->addr << 1) | 0x01);
+ if (mxl_fail(ret)) {
+ mxl111sf_i2c_stop(state);
+ goto fail;
+ }
+
+ for (i = 0; i < msg->len; i++) {
+ ret = mxl111sf_i2c_bitbang_recvbyte(state,
+ &msg->buf[i]);
+ if (mxl_fail(ret)) {
+ mxl111sf_i2c_stop(state);
+ goto fail;
+ }
+
+ if (i < msg->len - 1)
+ mxl111sf_i2c_ack(state);
+ }
+
+ mxl111sf_i2c_nack(state);
+
+ ret = mxl111sf_i2c_stop(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ } else {
+
+ ret = mxl111sf_i2c_start(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_i2c_bitbang_sendbyte(state,
+ (msg->addr << 1) & 0xfe);
+ if (mxl_fail(ret)) {
+ mxl111sf_i2c_stop(state);
+ goto fail;
+ }
+
+ for (i = 0; i < msg->len; i++) {
+ ret = mxl111sf_i2c_bitbang_sendbyte(state,
+ msg->buf[i]);
+ if (mxl_fail(ret)) {
+ mxl111sf_i2c_stop(state);
+ goto fail;
+ }
+ }
+
+ /* FIXME: we only want to do this on the last transaction */
+ mxl111sf_i2c_stop(state);
+ }
+fail:
+ return ret;
+}
+
+/* HW-I2C ----------------------------------------------------------------- */
+
+#define USB_WRITE_I2C_CMD 0x99
+#define USB_READ_I2C_CMD 0xdd
+#define USB_END_I2C_CMD 0xfe
+
+#define USB_WRITE_I2C_CMD_LEN 26
+#define USB_READ_I2C_CMD_LEN 24
+
+#define I2C_MUX_REG 0x30
+#define I2C_CONTROL_REG 0x00
+#define I2C_SLAVE_ADDR_REG 0x08
+#define I2C_DATA_REG 0x0c
+#define I2C_INT_STATUS_REG 0x10
+
+static int mxl111sf_i2c_send_data(struct mxl111sf_state *state,
+ u8 index, u8 *wdata)
+{
+ int ret = mxl111sf_ctrl_msg(state->d, wdata[0],
+ &wdata[1], 25, NULL, 0);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+static int mxl111sf_i2c_get_data(struct mxl111sf_state *state,
+ u8 index, u8 *wdata, u8 *rdata)
+{
+ int ret = mxl111sf_ctrl_msg(state->d, wdata[0],
+ &wdata[1], 25, rdata, 24);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+static u8 mxl111sf_i2c_check_status(struct mxl111sf_state *state)
+{
+ u8 status = 0;
+ u8 buf[26];
+
+ mxl_i2c_adv("()");
+
+ buf[0] = USB_READ_I2C_CMD;
+ buf[1] = 0x00;
+
+ buf[2] = I2C_INT_STATUS_REG;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+
+ buf[5] = USB_END_I2C_CMD;
+
+ mxl111sf_i2c_get_data(state, 0, buf, buf);
+
+ if (buf[1] & 0x04)
+ status = 1;
+
+ return status;
+}
+
+static u8 mxl111sf_i2c_check_fifo(struct mxl111sf_state *state)
+{
+ u8 status = 0;
+ u8 buf[26];
+
+ mxl_i2c("()");
+
+ buf[0] = USB_READ_I2C_CMD;
+ buf[1] = 0x00;
+
+ buf[2] = I2C_MUX_REG;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+
+ buf[5] = I2C_INT_STATUS_REG;
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+ buf[8] = USB_END_I2C_CMD;
+
+ mxl111sf_i2c_get_data(state, 0, buf, buf);
+
+ if (0x08 == (buf[1] & 0x08))
+ status = 1;
+
+ if ((buf[5] & 0x02) == 0x02)
+ mxl_i2c("(buf[5] & 0x02) == 0x02"); /* FIXME */
+
+ return status;
+}
+
+static int mxl111sf_i2c_readagain(struct mxl111sf_state *state,
+ u8 count, u8 *rbuf)
+{
+ u8 i2c_w_data[26];
+ u8 i2c_r_data[24];
+ u8 i = 0;
+ u8 fifo_status = 0;
+ int status = 0;
+
+ mxl_i2c("read %d bytes", count);
+
+ while ((fifo_status == 0) && (i++ < 5))
+ fifo_status = mxl111sf_i2c_check_fifo(state);
+
+ i2c_w_data[0] = 0xDD;
+ i2c_w_data[1] = 0x00;
+
+ for (i = 2; i < 26; i++)
+ i2c_w_data[i] = 0xFE;
+
+ for (i = 0; i < count; i++) {
+ i2c_w_data[2+(i*3)] = 0x0C;
+ i2c_w_data[3+(i*3)] = 0x00;
+ i2c_w_data[4+(i*3)] = 0x00;
+ }
+
+ mxl111sf_i2c_get_data(state, 0, i2c_w_data, i2c_r_data);
+
+ /* Check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("error!");
+ } else {
+ for (i = 0; i < count; i++) {
+ rbuf[i] = i2c_r_data[(i*3)+1];
+ mxl_i2c("%02x\t %02x",
+ i2c_r_data[(i*3)+1],
+ i2c_r_data[(i*3)+2]);
+ }
+
+ status = 1;
+ }
+
+ return status;
+}
+
+#define HWI2C400 1
+static int mxl111sf_i2c_hw_xfer_msg(struct mxl111sf_state *state,
+ struct i2c_msg *msg)
+{
+ int i, k, ret = 0;
+ u16 index = 0;
+ u8 buf[26];
+ u8 i2c_r_data[24];
+ u16 block_len;
+ u16 left_over_len;
+ u8 rd_status[8];
+ u8 ret_status;
+ u8 readbuff[26];
+
+ mxl_i2c("addr: 0x%02x, read buff len: %d, write buff len: %d",
+ msg->addr, (msg->flags & I2C_M_RD) ? msg->len : 0,
+ (!(msg->flags & I2C_M_RD)) ? msg->len : 0);
+
+ for (index = 0; index < 26; index++)
+ buf[index] = USB_END_I2C_CMD;
+
+ /* command to indicate data payload is destined for I2C interface */
+ buf[0] = USB_WRITE_I2C_CMD;
+ buf[1] = 0x00;
+
+ /* enable I2C interface */
+ buf[2] = I2C_MUX_REG;
+ buf[3] = 0x80;
+ buf[4] = 0x00;
+
+ /* enable I2C interface */
+ buf[5] = I2C_MUX_REG;
+ buf[6] = 0x81;
+ buf[7] = 0x00;
+
+ /* set Timeout register on I2C interface */
+ buf[8] = 0x14;
+ buf[9] = 0xff;
+ buf[10] = 0x00;
+#if 0
+ /* enable Interrupts on I2C interface */
+ buf[8] = 0x24;
+ buf[9] = 0xF7;
+ buf[10] = 0x00;
+#endif
+ buf[11] = 0x24;
+ buf[12] = 0xF7;
+ buf[13] = 0x00;
+
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* write data on I2C bus */
+ if (!(msg->flags & I2C_M_RD) && (msg->len > 0)) {
+ mxl_i2c("%d\t%02x", msg->len, msg->buf[0]);
+
+ /* control register on I2C interface to initialize I2C bus */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x5E;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+
+ /* I2C Slave device Address */
+ buf[5] = I2C_SLAVE_ADDR_REG;
+ buf[6] = (msg->addr);
+ buf[7] = 0x00;
+ buf[8] = USB_END_I2C_CMD;
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* check for slave device status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK writing slave address %02x",
+ msg->addr);
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x4E;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* I2C interface can do I2C operations in block of 8 bytes of
+ I2C data. calculation to figure out number of blocks of i2c
+ data required to program */
+ block_len = (msg->len / 8);
+ left_over_len = (msg->len % 8);
+ index = 0;
+
+ mxl_i2c("block_len %d, left_over_len %d",
+ block_len, left_over_len);
+
+ for (index = 0; index < block_len; index++) {
+ for (i = 0; i < 8; i++) {
+ /* write data on I2C interface */
+ buf[2+(i*3)] = I2C_DATA_REG;
+ buf[3+(i*3)] = msg->buf[(index*8)+i];
+ buf[4+(i*3)] = 0x00;
+ }
+
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK writing slave address %02x",
+ msg->addr);
+
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x4E;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ }
+
+ if (left_over_len) {
+ for (k = 0; k < 26; k++)
+ buf[k] = USB_END_I2C_CMD;
+
+ buf[0] = 0x99;
+ buf[1] = 0x00;
+
+ for (i = 0; i < left_over_len; i++) {
+ buf[2+(i*3)] = I2C_DATA_REG;
+ buf[3+(i*3)] = msg->buf[(index*8)+i];
+ mxl_i2c("index = %d %d data %d",
+ index, i, msg->buf[(index*8)+i]);
+ buf[4+(i*3)] = 0x00;
+ }
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK writing slave address %02x",
+ msg->addr);
+
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x4E;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ }
+
+ /* issue I2C STOP after write */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x4E;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+
+ }
+
+ /* read data from I2C bus */
+ if ((msg->flags & I2C_M_RD) && (msg->len > 0)) {
+ mxl_i2c("read buf len %d", msg->len);
+
+ /* command to indicate data payload is
+ destined for I2C interface */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xDF;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+
+ /* I2C xfer length */
+ buf[5] = 0x14;
+ buf[6] = (msg->len & 0xFF);
+ buf[7] = 0;
+
+ /* I2C slave device Address */
+ buf[8] = I2C_SLAVE_ADDR_REG;
+ buf[9] = msg->addr;
+ buf[10] = 0x00;
+ buf[11] = USB_END_I2C_CMD;
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK reading slave address %02x",
+ msg->addr);
+
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xC7;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* I2C interface can do I2C operations in block of 8 bytes of
+ I2C data. calculation to figure out number of blocks of
+ i2c data required to program */
+ block_len = ((msg->len) / 8);
+ left_over_len = ((msg->len) % 8);
+ index = 0;
+
+ mxl_i2c("block_len %d, left_over_len %d",
+ block_len, left_over_len);
+
+ /* command to read data from I2C interface */
+ buf[0] = USB_READ_I2C_CMD;
+ buf[1] = 0x00;
+
+ for (index = 0; index < block_len; index++) {
+ /* setup I2C read request packet on I2C interface */
+ for (i = 0; i < 8; i++) {
+ buf[2+(i*3)] = I2C_DATA_REG;
+ buf[3+(i*3)] = 0x00;
+ buf[4+(i*3)] = 0x00;
+ }
+
+ ret = mxl111sf_i2c_get_data(state, 0, buf, i2c_r_data);
+
+ /* check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK reading slave address %02x",
+ msg->addr);
+
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xC7;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* copy data from i2c data payload to read buffer */
+ for (i = 0; i < 8; i++) {
+ rd_status[i] = i2c_r_data[(i*3)+2];
+
+ if (rd_status[i] == 0x04) {
+ if (i < 7) {
+ mxl_i2c("i2c fifo empty!"
+ " @ %d", i);
+ msg->buf[(index*8)+i] =
+ i2c_r_data[(i*3)+1];
+ /* read again */
+ ret_status =
+ mxl111sf_i2c_readagain(
+ state, 8-(i+1),
+ readbuff);
+ if (ret_status == 1) {
+ for (k = 0;
+ k < 8-(i+1);
+ k++) {
+
+ msg->buf[(index*8)+(k+i+1)] =
+ readbuff[k];
+ mxl_i2c("read data: %02x\t %02x",
+ msg->buf[(index*8)+(k+i)],
+ (index*8)+(k+i));
+ mxl_i2c("read data: %02x\t %02x",
+ msg->buf[(index*8)+(k+i+1)],
+ readbuff[k]);
+
+ }
+ goto stop_copy;
+ } else {
+ mxl_i2c("readagain "
+ "ERROR!");
+ }
+ } else {
+ msg->buf[(index*8)+i] =
+ i2c_r_data[(i*3)+1];
+ }
+ } else {
+ msg->buf[(index*8)+i] =
+ i2c_r_data[(i*3)+1];
+ }
+ }
+stop_copy:
+ ;
+
+ }
+
+ if (left_over_len) {
+ for (k = 0; k < 26; k++)
+ buf[k] = USB_END_I2C_CMD;
+
+ buf[0] = 0xDD;
+ buf[1] = 0x00;
+
+ for (i = 0; i < left_over_len; i++) {
+ buf[2+(i*3)] = I2C_DATA_REG;
+ buf[3+(i*3)] = 0x00;
+ buf[4+(i*3)] = 0x00;
+ }
+ ret = mxl111sf_i2c_get_data(state, 0, buf,
+ i2c_r_data);
+
+ /* check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK reading slave address %02x",
+ msg->addr);
+
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xC7;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ for (i = 0; i < left_over_len; i++) {
+ msg->buf[(block_len*8)+i] =
+ i2c_r_data[(i*3)+1];
+ mxl_i2c("read data: %02x\t %02x",
+ i2c_r_data[(i*3)+1],
+ i2c_r_data[(i*3)+2]);
+ }
+ }
+
+ /* indicate I2C interface to issue NACK
+ after next I2C read op */
+ buf[0] = USB_WRITE_I2C_CMD;
+ buf[1] = 0x00;
+
+ /* control register */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x17;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+
+ buf[5] = USB_END_I2C_CMD;
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* control register */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xC7;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+
+ }
+exit:
+ /* STOP and disable I2C MUX */
+ buf[0] = USB_WRITE_I2C_CMD;
+ buf[1] = 0x00;
+
+ /* de-initilize I2C BUS */
+ buf[5] = USB_END_I2C_CMD;
+ mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* Control Register */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xDF;
+ buf[4] = 0x03;
+
+ /* disable I2C interface */
+ buf[5] = I2C_MUX_REG;
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+
+ /* de-initilize I2C BUS */
+ buf[8] = USB_END_I2C_CMD;
+ mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* disable I2C interface */
+ buf[2] = I2C_MUX_REG;
+ buf[3] = 0x81;
+ buf[4] = 0x00;
+
+ /* disable I2C interface */
+ buf[5] = I2C_MUX_REG;
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+
+ /* disable I2C interface */
+ buf[8] = I2C_MUX_REG;
+ buf[9] = 0x00;
+ buf[10] = 0x00;
+
+ buf[11] = USB_END_I2C_CMD;
+ mxl111sf_i2c_send_data(state, 0, buf);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int mxl111sf_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct mxl111sf_state *state = d->priv;
+ int hwi2c = (state->chip_rev > MXL111SF_V6);
+ int i, ret;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ ret = (hwi2c) ?
+ mxl111sf_i2c_hw_xfer_msg(state, &msg[i]) :
+ mxl111sf_i2c_sw_xfer_msg(state, &msg[i]);
+ if (mxl_fail(ret)) {
+ mxl_debug_adv("failed with error %d on i2c "
+ "transaction %d of %d, %sing %d bytes "
+ "to/from 0x%02x", ret, i+1, num,
+ (msg[i].flags & I2C_M_RD) ?
+ "read" : "writ",
+ msg[i].len, msg[i].addr);
+
+ break;
+ }
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return i == num ? num : -EREMOTEIO;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h
new file mode 100644
index 000000000000..a57a45ffb9e4
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h
@@ -0,0 +1,35 @@
+/*
+ * mxl111sf-i2c.h - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _DVB_USB_MXL111SF_I2C_H_
+#define _DVB_USB_MXL111SF_I2C_H_
+
+#include <linux/i2c.h>
+
+int mxl111sf_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msg[], int num);
+
+#endif /* _DVB_USB_MXL111SF_I2C_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-phy.c b/drivers/media/dvb/dvb-usb/mxl111sf-phy.c
new file mode 100644
index 000000000000..b741b3a7a325
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-phy.c
@@ -0,0 +1,343 @@
+/*
+ * mxl111sf-phy.c - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mxl111sf-phy.h"
+#include "mxl111sf-reg.h"
+
+int mxl111sf_init_tuner_demod(struct mxl111sf_state *state)
+{
+ struct mxl111sf_reg_ctrl_info mxl_111_overwrite_default[] = {
+ {0x07, 0xff, 0x0c},
+ {0x58, 0xff, 0x9d},
+ {0x09, 0xff, 0x00},
+ {0x06, 0xff, 0x06},
+ {0xc8, 0xff, 0x40}, /* ED_LE_WIN_OLD = 0 */
+ {0x8d, 0x01, 0x01}, /* NEGATE_Q */
+ {0x32, 0xff, 0xac}, /* DIG_RFREFSELECT = 12 */
+ {0x42, 0xff, 0x43}, /* DIG_REG_AMP = 4 */
+ {0x74, 0xff, 0xc4}, /* SSPUR_FS_PRIO = 4 */
+ {0x71, 0xff, 0xe6}, /* SPUR_ROT_PRIO_VAL = 1 */
+ {0x83, 0xff, 0x64}, /* INF_FILT1_THD_SC = 100 */
+ {0x85, 0xff, 0x64}, /* INF_FILT2_THD_SC = 100 */
+ {0x88, 0xff, 0xf0}, /* INF_THD = 240 */
+ {0x6f, 0xf0, 0xb0}, /* DFE_DLY = 11 */
+ {0x00, 0xff, 0x01}, /* Change to page 1 */
+ {0x81, 0xff, 0x11}, /* DSM_FERR_BYPASS = 1 */
+ {0xf4, 0xff, 0x07}, /* DIG_FREQ_CORR = 1 */
+ {0xd4, 0x1f, 0x0f}, /* SPUR_TEST_NOISE_TH = 15 */
+ {0xd6, 0xff, 0x0c}, /* SPUR_TEST_NOISE_PAPR = 12 */
+ {0x00, 0xff, 0x00}, /* Change to page 0 */
+ {0, 0, 0}
+ };
+
+ mxl_debug("()");
+
+ return mxl111sf_ctrl_program_regs(state, mxl_111_overwrite_default);
+}
+
+int mxl1x1sf_soft_reset(struct mxl111sf_state *state)
+{
+ int ret;
+ mxl_debug("()");
+
+ ret = mxl111sf_write_reg(state, 0xff, 0x00); /* AIC */
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x02, 0x01); /* get out of reset */
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode)
+{
+ int ret;
+
+ mxl_debug("(%s)", MXL_SOC_MODE == mode ?
+ "MXL_SOC_MODE" : "MXL_TUNER_MODE");
+
+ /* set device mode */
+ ret = mxl111sf_write_reg(state, 0x03,
+ MXL_SOC_MODE == mode ? 0x01 : 0x00);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg_mask(state,
+ 0x7d, 0x40, MXL_SOC_MODE == mode ?
+ 0x00 : /* enable impulse noise filter,
+ INF_BYP = 0 */
+ 0x40); /* disable impulse noise filter,
+ INF_BYP = 1 */
+ if (mxl_fail(ret))
+ goto fail;
+
+ state->device_mode = mode;
+fail:
+ return ret;
+}
+
+/* power up tuner */
+int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff)
+{
+ mxl_debug("(%d)", onoff);
+
+ return mxl111sf_write_reg(state, 0x01, onoff ? 0x01 : 0x00);
+}
+
+int mxl111sf_disable_656_port(struct mxl111sf_state *state)
+{
+ mxl_debug("()");
+
+ return mxl111sf_write_reg_mask(state, 0x12, 0x04, 0x00);
+}
+
+int mxl111sf_enable_usb_output(struct mxl111sf_state *state)
+{
+ mxl_debug("()");
+
+ return mxl111sf_write_reg_mask(state, 0x17, 0x40, 0x00);
+}
+
+/* initialize TSIF as input port of MxL1X1SF for MPEG2 data transfer */
+int mxl111sf_config_mpeg_in(struct mxl111sf_state *state,
+ unsigned int parallel_serial,
+ unsigned int msb_lsb_1st,
+ unsigned int clock_phase,
+ unsigned int mpeg_valid_pol,
+ unsigned int mpeg_sync_pol)
+{
+ int ret;
+ u8 mode, tmp;
+
+ mxl_debug("(%u,%u,%u,%u,%u)", parallel_serial, msb_lsb_1st,
+ clock_phase, mpeg_valid_pol, mpeg_sync_pol);
+
+ /* Enable PIN MUX */
+ ret = mxl111sf_write_reg(state, V6_PIN_MUX_MODE_REG, V6_ENABLE_PIN_MUX);
+ mxl_fail(ret);
+
+ /* Configure MPEG Clock phase */
+ mxl111sf_read_reg(state, V6_MPEG_IN_CLK_INV_REG, &mode);
+
+ if (clock_phase == TSIF_NORMAL)
+ mode &= ~V6_INVERTED_CLK_PHASE;
+ else
+ mode |= V6_INVERTED_CLK_PHASE;
+
+ ret = mxl111sf_write_reg(state, V6_MPEG_IN_CLK_INV_REG, mode);
+ mxl_fail(ret);
+
+ /* Configure data input mode, MPEG Valid polarity, MPEG Sync polarity
+ * Get current configuration */
+ ret = mxl111sf_read_reg(state, V6_MPEG_IN_CTRL_REG, &mode);
+ mxl_fail(ret);
+
+ /* Data Input mode */
+ if (parallel_serial == TSIF_INPUT_PARALLEL) {
+ /* Disable serial mode */
+ mode &= ~V6_MPEG_IN_DATA_SERIAL;
+
+ /* Enable Parallel mode */
+ mode |= V6_MPEG_IN_DATA_PARALLEL;
+ } else {
+ /* Disable Parallel mode */
+ mode &= ~V6_MPEG_IN_DATA_PARALLEL;
+
+ /* Enable Serial Mode */
+ mode |= V6_MPEG_IN_DATA_SERIAL;
+
+ /* If serial interface is chosen, configure
+ MSB or LSB order in transmission */
+ ret = mxl111sf_read_reg(state,
+ V6_MPEG_INOUT_BIT_ORDER_CTRL_REG,
+ &tmp);
+ mxl_fail(ret);
+
+ if (msb_lsb_1st == MPEG_SER_MSB_FIRST_ENABLED)
+ tmp |= V6_MPEG_SER_MSB_FIRST;
+ else
+ tmp &= ~V6_MPEG_SER_MSB_FIRST;
+
+ ret = mxl111sf_write_reg(state,
+ V6_MPEG_INOUT_BIT_ORDER_CTRL_REG,
+ tmp);
+ mxl_fail(ret);
+ }
+
+ /* MPEG Sync polarity */
+ if (mpeg_sync_pol == TSIF_NORMAL)
+ mode &= ~V6_INVERTED_MPEG_SYNC;
+ else
+ mode |= V6_INVERTED_MPEG_SYNC;
+
+ /* MPEG Valid polarity */
+ if (mpeg_valid_pol == 0)
+ mode &= ~V6_INVERTED_MPEG_VALID;
+ else
+ mode |= V6_INVERTED_MPEG_VALID;
+
+ ret = mxl111sf_write_reg(state, V6_MPEG_IN_CTRL_REG, mode);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size)
+{
+ static struct mxl111sf_reg_ctrl_info init_i2s[] = {
+ {0x1b, 0xff, 0x1e}, /* pin mux mode, Choose 656/I2S input */
+ {0x15, 0x60, 0x60}, /* Enable I2S */
+ {0x17, 0xe0, 0x20}, /* Input, MPEG MODE USB,
+ Inverted 656 Clock, I2S_SOFT_RESET,
+ 0 : Normal operation, 1 : Reset State */
+#if 0
+ {0x12, 0x01, 0x00}, /* AUDIO_IRQ_CLR (Overflow Indicator) */
+#endif
+ {0x00, 0xff, 0x02}, /* Change to Control Page */
+ {0x26, 0x0d, 0x0d}, /* I2S_MODE & BT656_SRC_SEL for FPGA only */
+ {0x00, 0xff, 0x00},
+ {0, 0, 0}
+ };
+ int ret;
+
+ mxl_debug("(0x%02x)", sample_size);
+
+ ret = mxl111sf_ctrl_program_regs(state, init_i2s);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, V6_I2S_NUM_SAMPLES_REG, sample_size);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+int mxl111sf_disable_i2s_port(struct mxl111sf_state *state)
+{
+ static struct mxl111sf_reg_ctrl_info disable_i2s[] = {
+ {0x15, 0x40, 0x00},
+ {0, 0, 0}
+ };
+
+ mxl_debug("()");
+
+ return mxl111sf_ctrl_program_regs(state, disable_i2s);
+}
+
+int mxl111sf_config_i2s(struct mxl111sf_state *state,
+ u8 msb_start_pos, u8 data_width)
+{
+ int ret;
+ u8 tmp;
+
+ mxl_debug("(0x%02x, 0x%02x)", msb_start_pos, data_width);
+
+ ret = mxl111sf_read_reg(state, V6_I2S_STREAM_START_BIT_REG, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+
+ tmp &= 0xe0;
+ tmp |= msb_start_pos;
+ ret = mxl111sf_write_reg(state, V6_I2S_STREAM_START_BIT_REG, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_read_reg(state, V6_I2S_STREAM_END_BIT_REG, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+
+ tmp &= 0xe0;
+ tmp |= data_width;
+ ret = mxl111sf_write_reg(state, V6_I2S_STREAM_END_BIT_REG, tmp);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff)
+{
+ u8 val;
+ int ret;
+
+ mxl_debug("(%d)", onoff);
+
+ ret = mxl111sf_write_reg(state, 0x00, 0x02);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_read_reg(state, V8_SPI_MODE_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (onoff)
+ val |= 0x04;
+ else
+ val &= ~0x04;
+
+ ret = mxl111sf_write_reg(state, V8_SPI_MODE_REG, val);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, 0x00, 0x00);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+int mxl111sf_idac_config(struct mxl111sf_state *state,
+ u8 control_mode, u8 current_setting,
+ u8 current_value, u8 hysteresis_value)
+{
+ int ret;
+ u8 val;
+ /* current value will be set for both automatic & manual IDAC control */
+ val = current_value;
+
+ if (control_mode == IDAC_MANUAL_CONTROL) {
+ /* enable manual control of IDAC */
+ val |= IDAC_MANUAL_CONTROL_BIT_MASK;
+
+ if (current_setting == IDAC_CURRENT_SINKING_ENABLE)
+ /* enable current sinking in manual mode */
+ val |= IDAC_CURRENT_SINKING_BIT_MASK;
+ else
+ /* disable current sinking in manual mode */
+ val &= ~IDAC_CURRENT_SINKING_BIT_MASK;
+ } else {
+ /* disable manual control of IDAC */
+ val &= ~IDAC_MANUAL_CONTROL_BIT_MASK;
+
+ /* set hysteresis value reg: 0x0B<5:0> */
+ ret = mxl111sf_write_reg(state, V6_IDAC_HYSTERESIS_REG,
+ (hysteresis_value & 0x3F));
+ mxl_fail(ret);
+ }
+
+ ret = mxl111sf_write_reg(state, V6_IDAC_SETTINGS_REG, val);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-phy.h b/drivers/media/dvb/dvb-usb/mxl111sf-phy.h
new file mode 100644
index 000000000000..f0756071d347
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-phy.h
@@ -0,0 +1,53 @@
+/*
+ * mxl111sf-phy.h - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _DVB_USB_MXL111SF_PHY_H_
+#define _DVB_USB_MXL111SF_PHY_H_
+
+#include "mxl111sf.h"
+
+int mxl1x1sf_soft_reset(struct mxl111sf_state *state);
+int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode);
+int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff);
+int mxl111sf_disable_656_port(struct mxl111sf_state *state);
+int mxl111sf_init_tuner_demod(struct mxl111sf_state *state);
+int mxl111sf_enable_usb_output(struct mxl111sf_state *state);
+int mxl111sf_config_mpeg_in(struct mxl111sf_state *state,
+ unsigned int parallel_serial,
+ unsigned int msb_lsb_1st,
+ unsigned int clock_phase,
+ unsigned int mpeg_valid_pol,
+ unsigned int mpeg_sync_pol);
+int mxl111sf_config_i2s(struct mxl111sf_state *state,
+ u8 msb_start_pos, u8 data_width);
+int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size);
+int mxl111sf_disable_i2s_port(struct mxl111sf_state *state);
+int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff);
+int mxl111sf_idac_config(struct mxl111sf_state *state,
+ u8 control_mode, u8 current_setting,
+ u8 current_value, u8 hysteresis_value);
+
+#endif /* _DVB_USB_MXL111SF_PHY_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-reg.h b/drivers/media/dvb/dvb-usb/mxl111sf-reg.h
new file mode 100644
index 000000000000..17831b0fb9db
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-reg.h
@@ -0,0 +1,179 @@
+/*
+ * mxl111sf-reg.h - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _DVB_USB_MXL111SF_REG_H_
+#define _DVB_USB_MXL111SF_REG_H_
+
+#define CHIP_ID_REG 0xFC
+#define TOP_CHIP_REV_ID_REG 0xFA
+
+#define V6_SNR_RB_LSB_REG 0x27
+#define V6_SNR_RB_MSB_REG 0x28
+
+#define V6_N_ACCUMULATE_REG 0x11
+#define V6_RS_AVG_ERRORS_LSB_REG 0x2C
+#define V6_RS_AVG_ERRORS_MSB_REG 0x2D
+
+#define V6_IRQ_STATUS_REG 0x24
+#define IRQ_MASK_FEC_LOCK 0x10
+
+#define V6_SYNC_LOCK_REG 0x28
+#define SYNC_LOCK_MASK 0x10
+
+#define V6_RS_LOCK_DET_REG 0x28
+#define RS_LOCK_DET_MASK 0x08
+
+#define V6_INITACQ_NODETECT_REG 0x20
+#define V6_FORCE_NFFT_CPSIZE_REG 0x20
+
+#define V6_CODE_RATE_TPS_REG 0x29
+#define V6_CODE_RATE_TPS_MASK 0x07
+
+
+#define V6_CP_LOCK_DET_REG 0x28
+#define V6_CP_LOCK_DET_MASK 0x04
+
+#define V6_TPS_HIERACHY_REG 0x29
+#define V6_TPS_HIERARCHY_INFO_MASK 0x40
+
+#define V6_MODORDER_TPS_REG 0x2A
+#define V6_PARAM_CONSTELLATION_MASK 0x30
+
+#define V6_MODE_TPS_REG 0x2A
+#define V6_PARAM_FFT_MODE_MASK 0x0C
+
+
+#define V6_CP_TPS_REG 0x29
+#define V6_PARAM_GI_MASK 0x30
+
+#define V6_TPS_LOCK_REG 0x2A
+#define V6_PARAM_TPS_LOCK_MASK 0x40
+
+#define V6_FEC_PER_COUNT_REG 0x2E
+#define V6_FEC_PER_SCALE_REG 0x2B
+#define V6_FEC_PER_SCALE_MASK 0x03
+#define V6_FEC_PER_CLR_REG 0x20
+#define V6_FEC_PER_CLR_MASK 0x01
+
+#define V6_PIN_MUX_MODE_REG 0x1B
+#define V6_ENABLE_PIN_MUX 0x1E
+
+#define V6_I2S_NUM_SAMPLES_REG 0x16
+
+#define V6_MPEG_IN_CLK_INV_REG 0x17
+#define V6_MPEG_IN_CTRL_REG 0x18
+
+#define V6_INVERTED_CLK_PHASE 0x20
+#define V6_MPEG_IN_DATA_PARALLEL 0x01
+#define V6_MPEG_IN_DATA_SERIAL 0x02
+
+#define V6_INVERTED_MPEG_SYNC 0x04
+#define V6_INVERTED_MPEG_VALID 0x08
+
+#define TSIF_INPUT_PARALLEL 0
+#define TSIF_INPUT_SERIAL 1
+#define TSIF_NORMAL 0
+
+#define V6_MPEG_INOUT_BIT_ORDER_CTRL_REG 0x19
+#define V6_MPEG_SER_MSB_FIRST 0x80
+#define MPEG_SER_MSB_FIRST_ENABLED 0x01
+
+#define V6_656_I2S_BUFF_STATUS_REG 0x2F
+#define V6_656_OVERFLOW_MASK_BIT 0x08
+#define V6_I2S_OVERFLOW_MASK_BIT 0x01
+
+#define V6_I2S_STREAM_START_BIT_REG 0x14
+#define V6_I2S_STREAM_END_BIT_REG 0x15
+#define I2S_RIGHT_JUSTIFIED 0
+#define I2S_LEFT_JUSTIFIED 1
+#define I2S_DATA_FORMAT 2
+
+#define V6_TUNER_LOOP_THRU_CONTROL_REG 0x09
+#define V6_ENABLE_LOOP_THRU 0x01
+
+#define TOTAL_NUM_IF_OUTPUT_FREQ 16
+
+#define TUNER_NORMAL_IF_SPECTRUM 0x0
+#define TUNER_INVERT_IF_SPECTRUM 0x10
+
+#define V6_TUNER_IF_SEL_REG 0x06
+#define V6_TUNER_IF_FCW_REG 0x3C
+#define V6_TUNER_IF_FCW_BYP_REG 0x3D
+#define V6_RF_LOCK_STATUS_REG 0x23
+
+#define NUM_DIG_TV_CHANNEL 1000
+
+#define V6_DIG_CLK_FREQ_SEL_REG 0x07
+#define V6_REF_SYNTH_INT_REG 0x5C
+#define V6_REF_SYNTH_REMAIN_REG 0x58
+#define V6_DIG_RFREFSELECT_REG 0x32
+#define V6_XTAL_CLK_OUT_GAIN_REG 0x31
+#define V6_TUNER_LOOP_THRU_CTRL_REG 0x09
+#define V6_DIG_XTAL_ENABLE_REG 0x06
+#define V6_DIG_XTAL_BIAS_REG 0x66
+#define V6_XTAL_CAP_REG 0x08
+
+#define V6_GPO_CTRL_REG 0x18
+#define MXL_GPO_0 0x00
+#define MXL_GPO_1 0x01
+#define V6_GPO_0_MASK 0x10
+#define V6_GPO_1_MASK 0x20
+
+#define V6_111SF_GPO_CTRL_REG 0x19
+#define MXL_111SF_GPO_1 0x00
+#define MXL_111SF_GPO_2 0x01
+#define MXL_111SF_GPO_3 0x02
+#define MXL_111SF_GPO_4 0x03
+#define MXL_111SF_GPO_5 0x04
+#define MXL_111SF_GPO_6 0x05
+#define MXL_111SF_GPO_7 0x06
+
+#define MXL_111SF_GPO_0_MASK 0x01
+#define MXL_111SF_GPO_1_MASK 0x02
+#define MXL_111SF_GPO_2_MASK 0x04
+#define MXL_111SF_GPO_3_MASK 0x08
+#define MXL_111SF_GPO_4_MASK 0x10
+#define MXL_111SF_GPO_5_MASK 0x20
+#define MXL_111SF_GPO_6_MASK 0x40
+
+#define V6_ATSC_CONFIG_REG 0x0A
+
+#define MXL_MODE_REG 0x03
+#define START_TUNE_REG 0x1C
+
+#define V6_IDAC_HYSTERESIS_REG 0x0B
+#define V6_IDAC_SETTINGS_REG 0x0C
+#define IDAC_MANUAL_CONTROL 1
+#define IDAC_CURRENT_SINKING_ENABLE 1
+#define IDAC_MANUAL_CONTROL_BIT_MASK 0x80
+#define IDAC_CURRENT_SINKING_BIT_MASK 0x40
+
+#define V8_SPI_MODE_REG 0xE9
+
+#define V6_DIG_RF_PWR_LSB_REG 0x46
+#define V6_DIG_RF_PWR_MSB_REG 0x47
+
+#endif /* _DVB_USB_MXL111SF_REG_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
new file mode 100644
index 000000000000..a6341058c4e7
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
@@ -0,0 +1,476 @@
+/*
+ * mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mxl111sf-tuner.h"
+#include "mxl111sf-phy.h"
+#include "mxl111sf-reg.h"
+
+/* debug */
+static int mxl111sf_tuner_debug;
+module_param_named(debug, mxl111sf_tuner_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define mxl_dbg(fmt, arg...) \
+ if (mxl111sf_tuner_debug) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+/* ------------------------------------------------------------------------ */
+
+struct mxl111sf_tuner_state {
+ struct mxl111sf_state *mxl_state;
+
+ struct mxl111sf_tuner_config *cfg;
+
+ u32 frequency;
+ u32 bandwidth;
+};
+
+static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state,
+ u8 addr, u8 *data)
+{
+ return (state->cfg->read_reg) ?
+ state->cfg->read_reg(state->mxl_state, addr, data) :
+ -EINVAL;
+}
+
+static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state,
+ u8 addr, u8 data)
+{
+ return (state->cfg->write_reg) ?
+ state->cfg->write_reg(state->mxl_state, addr, data) :
+ -EINVAL;
+}
+
+static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
+{
+ return (state->cfg->program_regs) ?
+ state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
+ -EINVAL;
+}
+
+static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state,
+ int onoff)
+{
+ return (state->cfg->top_master_ctrl) ?
+ state->cfg->top_master_ctrl(state->mxl_state, onoff) :
+ -EINVAL;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = {
+ {0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3,
+ DIG_MODEINDEX, _A, _CSF, */
+ {0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */
+ {0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */
+ {0, 0, 0}
+};
+
+/* ------------------------------------------------------------------------ */
+
+static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq,
+ u8 bw)
+{
+ u8 filt_bw;
+
+ /* set channel bandwidth */
+ switch (bw) {
+ case 0: /* ATSC */
+ filt_bw = 25;
+ break;
+ case 1: /* QAM */
+ filt_bw = 69;
+ break;
+ case 6:
+ filt_bw = 21;
+ break;
+ case 7:
+ filt_bw = 42;
+ break;
+ case 8:
+ filt_bw = 63;
+ break;
+ default:
+ err("%s: invalid bandwidth setting!", __func__);
+ return NULL;
+ }
+
+ /* calculate RF channel */
+ freq /= 1000000;
+
+ freq *= 64;
+#if 0
+ /* do round */
+ freq += 0.5;
+#endif
+ /* set bandwidth */
+ mxl_phy_tune_rf[0].data = filt_bw;
+
+ /* set RF */
+ mxl_phy_tune_rf[1].data = (freq & 0xff);
+ mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff;
+
+ /* start tune */
+ return mxl_phy_tune_rf;
+}
+
+static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state)
+{
+ int ret;
+ u8 ctrl;
+#if 0
+ u16 iffcw;
+ u32 if_freq;
+#endif
+ mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)",
+ state->cfg->invert_spectrum, state->cfg->if_freq);
+
+ /* set IF polarity */
+ ctrl = state->cfg->invert_spectrum;
+
+ ctrl |= state->cfg->if_freq;
+
+ ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, ctrl);
+ if (mxl_fail(ret))
+ goto fail;
+
+#if 0
+ if_freq /= 1000000;
+
+ /* do round */
+ if_freq += 0.5;
+
+ if (MXL_IF_LO == state->cfg->if_freq) {
+ ctrl = 0x08;
+ iffcw = (u16)(if_freq / (108 * 4096));
+ } else if (MXL_IF_HI == state->cfg->if_freq) {
+ ctrl = 0x08;
+ iffcw = (u16)(if_freq / (216 * 4096));
+ } else {
+ ctrl = 0;
+ iffcw = 0;
+ }
+
+ ctrl |= (iffcw >> 8);
+#endif
+ ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, &ctrl);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ctrl &= 0xf0;
+ ctrl |= 0x90;
+
+ ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, ctrl);
+ if (mxl_fail(ret))
+ goto fail;
+
+#if 0
+ ctrl = iffcw & 0x00ff;
+#endif
+ ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, ctrl);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ static struct mxl111sf_reg_ctrl_info *reg_ctrl_array;
+ int ret;
+ u8 mxl_mode;
+
+ mxl_dbg("(freq = %d, bw = 0x%x)", freq, bw);
+
+ /* stop tune */
+ ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 0);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* check device mode */
+ ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, &mxl_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* Fill out registers for channel tune */
+ reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw);
+ if (!reg_ctrl_array)
+ return -EINVAL;
+
+ ret = mxl111sf_tuner_program_regs(state, reg_ctrl_array);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) {
+ /* IF tuner mode only */
+ mxl1x1sf_tuner_top_master_ctrl(state, 0);
+ mxl1x1sf_tuner_top_master_ctrl(state, 1);
+ mxl1x1sf_tuner_set_if_output_freq(state);
+ }
+
+ ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (state->cfg->ant_hunt)
+ state->cfg->ant_hunt(fe);
+fail:
+ return ret;
+}
+
+static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state,
+ int *rf_synth_lock,
+ int *ref_synth_lock)
+{
+ int ret;
+ u8 data;
+
+ *rf_synth_lock = 0;
+ *ref_synth_lock = 0;
+
+ ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, &data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0;
+ *rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0;
+fail:
+ return ret;
+}
+
+#if 0
+static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state,
+ int onoff)
+{
+ return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG,
+ onoff ? 1 : 0);
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_tuner_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int ret;
+ u8 bw;
+
+ mxl_dbg("()");
+
+ if (fe->ops.info.type == FE_ATSC) {
+ switch (params->u.vsb.modulation) {
+ case VSB_8:
+ case VSB_16:
+ bw = 0; /* ATSC */
+ break;
+ case QAM_64:
+ case QAM_256:
+ bw = 1; /* US CABLE */
+ break;
+ default:
+ err("%s: modulation not set!", __func__);
+ return -EINVAL;
+ }
+ } else if (fe->ops.info.type == FE_OFDM) {
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ bw = 6;
+ break;
+ case BANDWIDTH_7_MHZ:
+ bw = 7;
+ break;
+ case BANDWIDTH_8_MHZ:
+ bw = 8;
+ break;
+ default:
+ err("%s: bandwidth not set!", __func__);
+ return -EINVAL;
+ }
+ } else {
+ err("%s: modulation type not supported!", __func__);
+ return -EINVAL;
+ }
+ ret = mxl1x1sf_tune_rf(fe, params->frequency, bw);
+ if (mxl_fail(ret))
+ goto fail;
+
+ state->frequency = params->frequency;
+ state->bandwidth = (fe->ops.info.type == FE_OFDM) ?
+ params->u.ofdm.bandwidth : 0;
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#if 0
+static int mxl111sf_tuner_init(struct dvb_frontend *fe)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int ret;
+
+ /* wake from standby handled by usb driver */
+
+ return ret;
+}
+
+static int mxl111sf_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int ret;
+
+ /* enter standby mode handled by usb driver */
+
+ return ret;
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int rf_locked, ref_locked, ret;
+
+ *status = 0;
+
+ ret = mxl1x1sf_tuner_get_lock_status(state, &rf_locked, &ref_locked);
+ if (mxl_fail(ret))
+ goto fail;
+ mxl_info("%s%s", rf_locked ? "rf locked " : "",
+ ref_locked ? "ref locked" : "");
+
+ if ((rf_locked) || (ref_locked))
+ *status |= TUNER_STATUS_LOCKED;
+fail:
+ return ret;
+}
+
+static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ u8 val1, val2;
+ int ret;
+
+ *strength = 0;
+
+ ret = mxl111sf_tuner_write_reg(state, 0x00, 0x02);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, &val1);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, &val2);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *strength = val1 | ((val2 & 0x07) << 8);
+fail:
+ ret = mxl111sf_tuner_write_reg(state, 0x00, 0x00);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ *frequency = state->frequency;
+ return 0;
+}
+
+static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ *bandwidth = state->bandwidth;
+ return 0;
+}
+
+static int mxl111sf_tuner_release(struct dvb_frontend *fe)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ mxl_dbg("()");
+ kfree(state);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = {
+ .info = {
+ .name = "MaxLinear MxL111SF",
+#if 0
+ .frequency_min = ,
+ .frequency_max = ,
+ .frequency_step = ,
+#endif
+ },
+#if 0
+ .init = mxl111sf_tuner_init,
+ .sleep = mxl111sf_tuner_sleep,
+#endif
+ .set_params = mxl111sf_tuner_set_params,
+ .get_status = mxl111sf_tuner_get_status,
+ .get_rf_strength = mxl111sf_get_rf_strength,
+ .get_frequency = mxl111sf_tuner_get_frequency,
+ .get_bandwidth = mxl111sf_tuner_get_bandwidth,
+ .release = mxl111sf_tuner_release,
+};
+
+struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
+ struct mxl111sf_state *mxl_state,
+ struct mxl111sf_tuner_config *cfg)
+{
+ struct mxl111sf_tuner_state *state = NULL;
+
+ mxl_dbg("()");
+
+ state = kzalloc(sizeof(struct mxl111sf_tuner_state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+
+ state->mxl_state = mxl_state;
+ state->cfg = cfg;
+
+ memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = state;
+ return fe;
+}
+EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach);
+
+MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver");
+MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h
new file mode 100644
index 000000000000..ff333960b184
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h
@@ -0,0 +1,89 @@
+/*
+ * mxl111sf-tuner.h - driver for the MaxLinear MXL111SF CMOS tuner
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MXL111SF_TUNER_H__
+#define __MXL111SF_TUNER_H__
+
+#include "dvb_frontend.h"
+
+#include "mxl111sf.h"
+
+enum mxl_if_freq {
+#if 0
+ MXL_IF_LO = 0x00, /* other IF < 9MHz */
+#endif
+ MXL_IF_4_0 = 0x01, /* 4.0 MHz */
+ MXL_IF_4_5 = 0x02, /* 4.5 MHz */
+ MXL_IF_4_57 = 0x03, /* 4.57 MHz */
+ MXL_IF_5_0 = 0x04, /* 5.0 MHz */
+ MXL_IF_5_38 = 0x05, /* 5.38 MHz */
+ MXL_IF_6_0 = 0x06, /* 6.0 MHz */
+ MXL_IF_6_28 = 0x07, /* 6.28 MHz */
+ MXL_IF_7_2 = 0x08, /* 7.2 MHz */
+ MXL_IF_35_25 = 0x09, /* 35.25 MHz */
+ MXL_IF_36 = 0x0a, /* 36 MHz */
+ MXL_IF_36_15 = 0x0b, /* 36.15 MHz */
+ MXL_IF_44 = 0x0c, /* 44 MHz */
+#if 0
+ MXL_IF_HI = 0x0f, /* other IF > 35 MHz and < 45 MHz */
+#endif
+};
+
+struct mxl111sf_tuner_config {
+ enum mxl_if_freq if_freq;
+ unsigned int invert_spectrum:1;
+
+ int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data);
+ int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data);
+ int (*program_regs)(struct mxl111sf_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
+ int (*top_master_ctrl)(struct mxl111sf_state *state, int onoff);
+ int (*ant_hunt)(struct dvb_frontend *fe);
+};
+
+/* ------------------------------------------------------------------------ */
+
+#if defined(CONFIG_DVB_USB_MXL111SF) || \
+ (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE))
+extern
+struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
+ struct mxl111sf_state *mxl_state,
+ struct mxl111sf_tuner_config *cfg);
+#else
+static inline
+struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
+ struct mxl111sf_state *mxl_state
+ struct mxl111sf_tuner_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* __MXL111SF_TUNER_H__ */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c
new file mode 100644
index 000000000000..b5c98da5d9e2
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf.c
@@ -0,0 +1,1086 @@
+/*
+ * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/i2c.h>
+
+#include "mxl111sf.h"
+#include "mxl111sf-reg.h"
+#include "mxl111sf-phy.h"
+#include "mxl111sf-i2c.h"
+#include "mxl111sf-gpio.h"
+
+#include "mxl111sf-demod.h"
+#include "mxl111sf-tuner.h"
+
+#include "lgdt3305.h"
+
+int dvb_usb_mxl111sf_debug;
+module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level "
+ "(1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able)).");
+
+int dvb_usb_mxl111sf_isoc;
+module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644);
+MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc).");
+
+#define ANT_PATH_AUTO 0
+#define ANT_PATH_EXTERNAL 1
+#define ANT_PATH_INTERNAL 2
+
+int dvb_usb_mxl111sf_rfswitch =
+#if 0
+ ANT_PATH_AUTO;
+#else
+ ANT_PATH_EXTERNAL;
+#endif
+
+module_param_named(rfswitch, dvb_usb_mxl111sf_rfswitch, int, 0644);
+MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define deb_info(args...) dprintk(dvb_usb_mxl111sf_debug, 0x13, args)
+#define deb_reg(args...) dprintk(dvb_usb_mxl111sf_debug, 0x08, args)
+#define deb_adv(args...) dprintk(dvb_usb_mxl111sf_debug, MXL_ADV_DBG, args)
+
+int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
+ u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+ int wo = (rbuf == NULL || rlen == 0); /* write-only */
+ int ret;
+ u8 sndbuf[1+wlen];
+
+ deb_adv("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen);
+
+ memset(sndbuf, 0, 1+wlen);
+
+ sndbuf[0] = cmd;
+ memcpy(&sndbuf[1], wbuf, wlen);
+
+ ret = (wo) ? dvb_usb_generic_write(d, sndbuf, 1+wlen) :
+ dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#define MXL_CMD_REG_READ 0xaa
+#define MXL_CMD_REG_WRITE 0x55
+
+int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data)
+{
+ u8 buf[2];
+ int ret;
+
+ ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_READ, &addr, 1, buf, 2);
+ if (mxl_fail(ret)) {
+ mxl_debug("error reading reg: 0x%02x", addr);
+ goto fail;
+ }
+
+ if (buf[0] == addr)
+ *data = buf[1];
+ else {
+ err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x",
+ addr, buf[0], buf[1]);
+ ret = -EINVAL;
+ }
+
+ deb_reg("R: (0x%02x, 0x%02x)\n", addr, *data);
+fail:
+ return ret;
+}
+
+int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data)
+{
+ u8 buf[] = { addr, data };
+ int ret;
+
+ deb_reg("W: (0x%02x, 0x%02x)\n", addr, data);
+
+ ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0);
+ if (mxl_fail(ret))
+ err("error writing reg: 0x%02x, val: 0x%02x", addr, data);
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
+ u8 addr, u8 mask, u8 data)
+{
+ int ret;
+ u8 val;
+
+ if (mask != 0xff) {
+ ret = mxl111sf_read_reg(state, addr, &val);
+#if 1
+ /* dont know why this usually errors out on the first try */
+ if (mxl_fail(ret))
+ err("error writing addr: 0x%02x, mask: 0x%02x, "
+ "data: 0x%02x, retrying...", addr, mask, data);
+
+ ret = mxl111sf_read_reg(state, addr, &val);
+#endif
+ if (mxl_fail(ret))
+ goto fail;
+ }
+ val &= ~mask;
+ val |= data;
+
+ ret = mxl111sf_write_reg(state, addr, val);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
+{
+ int i, ret = 0;
+
+ for (i = 0; ctrl_reg_info[i].addr |
+ ctrl_reg_info[i].mask |
+ ctrl_reg_info[i].data; i++) {
+
+ ret = mxl111sf_write_reg_mask(state,
+ ctrl_reg_info[i].addr,
+ ctrl_reg_info[i].mask,
+ ctrl_reg_info[i].data);
+ if (mxl_fail(ret)) {
+ err("failed on reg #%d (0x%02x)", i,
+ ctrl_reg_info[i].addr);
+ break;
+ }
+ }
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl1x1sf_get_chip_info(struct mxl111sf_state *state)
+{
+ int ret;
+ u8 id, ver;
+ char *mxl_chip, *mxl_rev;
+
+ if ((state->chip_id) && (state->chip_ver))
+ return 0;
+
+ ret = mxl111sf_read_reg(state, CHIP_ID_REG, &id);
+ if (mxl_fail(ret))
+ goto fail;
+ state->chip_id = id;
+
+ ret = mxl111sf_read_reg(state, TOP_CHIP_REV_ID_REG, &ver);
+ if (mxl_fail(ret))
+ goto fail;
+ state->chip_ver = ver;
+
+ switch (id) {
+ case 0x61:
+ mxl_chip = "MxL101SF";
+ break;
+ case 0x63:
+ mxl_chip = "MxL111SF";
+ break;
+ default:
+ mxl_chip = "UNKNOWN MxL1X1";
+ break;
+ }
+ switch (ver) {
+ case 0x36:
+ state->chip_rev = MXL111SF_V6;
+ mxl_rev = "v6";
+ break;
+ case 0x08:
+ state->chip_rev = MXL111SF_V8_100;
+ mxl_rev = "v8_100";
+ break;
+ case 0x18:
+ state->chip_rev = MXL111SF_V8_200;
+ mxl_rev = "v8_200";
+ break;
+ default:
+ state->chip_rev = 0;
+ mxl_rev = "UNKNOWN REVISION";
+ break;
+ }
+ info("%s detected, %s (0x%x)", mxl_chip, mxl_rev, ver);
+fail:
+ return ret;
+}
+
+#define get_chip_info(state) \
+({ \
+ int ___ret; \
+ ___ret = mxl1x1sf_get_chip_info(state); \
+ if (mxl_fail(___ret)) { \
+ mxl_debug("failed to get chip info" \
+ " on first probe attempt"); \
+ ___ret = mxl1x1sf_get_chip_info(state); \
+ if (mxl_fail(___ret)) \
+ err("failed to get chip info during probe"); \
+ else \
+ mxl_debug("probe needed a retry " \
+ "in order to succeed."); \
+ } \
+ ___ret; \
+})
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ /* power control depends on which adapter is being woken:
+ * save this for init, instead, via mxl111sf_adap_fe_init */
+ return 0;
+}
+
+static int mxl111sf_adap_fe_init(struct dvb_frontend *fe)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe->id].priv;
+
+ int err;
+
+ /* exit if we didnt initialize the driver yet */
+ if (!state->chip_id) {
+ mxl_debug("driver not yet initialized, exit.");
+ goto fail;
+ }
+
+ deb_info("%s()\n", __func__);
+
+ mutex_lock(&state->fe_lock);
+
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ err = mxl1x1sf_soft_reset(state);
+ mxl_fail(err);
+ err = mxl111sf_init_tuner_demod(state);
+ mxl_fail(err);
+ err = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+
+ mxl_fail(err);
+ mxl111sf_enable_usb_output(state);
+ mxl_fail(err);
+ mxl1x1sf_top_master_ctrl(state, 1);
+ mxl_fail(err);
+
+ if ((MXL111SF_GPIO_MOD_DVBT != adap_state->gpio_mode) &&
+ (state->chip_rev > MXL111SF_V6)) {
+ mxl111sf_config_pin_mux_modes(state,
+ PIN_MUX_TS_SPI_IN_MODE_1);
+ mxl_fail(err);
+ }
+ err = mxl111sf_init_port_expander(state);
+ if (!mxl_fail(err)) {
+ state->gpio_mode = adap_state->gpio_mode;
+ err = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ mxl_fail(err);
+#if 0
+ err = fe->ops.init(fe);
+#endif
+ msleep(100); /* add short delay after enabling
+ * the demod before touching it */
+ }
+
+ return (adap_state->fe_init) ? adap_state->fe_init(fe) : 0;
+fail:
+ return -ENODEV;
+}
+
+static int mxl111sf_adap_fe_sleep(struct dvb_frontend *fe)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe->id].priv;
+ int err;
+
+ /* exit if we didnt initialize the driver yet */
+ if (!state->chip_id) {
+ mxl_debug("driver not yet initialized, exit.");
+ goto fail;
+ }
+
+ deb_info("%s()\n", __func__);
+
+ err = (adap_state->fe_sleep) ? adap_state->fe_sleep(fe) : 0;
+
+ mutex_unlock(&state->fe_lock);
+
+ return err;
+fail:
+ return -ENODEV;
+}
+
+
+static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[adap->active_fe].priv;
+ int ret = 0;
+ u8 tmp;
+
+ deb_info("%s(%d)\n", __func__, onoff);
+
+ if (onoff) {
+ ret = mxl111sf_enable_usb_output(state);
+ mxl_fail(ret);
+ ret = mxl111sf_config_mpeg_in(state, 1, 1,
+ adap_state->ep6_clockphase,
+ 0, 0);
+ mxl_fail(ret);
+ } else {
+ ret = mxl111sf_disable_656_port(state);
+ mxl_fail(ret);
+ }
+
+ mxl111sf_read_reg(state, 0x12, &tmp);
+ tmp &= ~0x04;
+ mxl111sf_write_reg(state, 0x12, tmp);
+
+ return ret;
+}
+
+static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int ret = 0;
+
+ deb_info("%s(%d)\n", __func__, onoff);
+
+ if (onoff) {
+ ret = mxl111sf_enable_usb_output(state);
+ mxl_fail(ret);
+ }
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct lgdt3305_config hauppauge_lgdt3305_config = {
+ .i2c_addr = 0xb2 >> 1,
+ .mpeg_mode = LGDT3305_MPEG_SERIAL,
+ .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .qam_if_khz = 6000,
+ .vsb_if_khz = 6000,
+};
+
+static int mxl111sf_lgdt3305_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int fe_id = adap->num_frontends_initialized;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_ATSC;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_TUNER_MODE;
+ adap_state->ep6_clockphase = 1;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_init_port_expander(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ adap->fe_adap[fe_id].fe = dvb_attach(lgdt3305_attach,
+ &hauppauge_lgdt3305_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe_adap[fe_id].fe) {
+ adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
+ adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
+ adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static struct mxl111sf_demod_config mxl_demod_config = {
+ .read_reg = mxl111sf_read_reg,
+ .write_reg = mxl111sf_write_reg,
+ .program_regs = mxl111sf_ctrl_program_regs,
+};
+
+static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int fe_id = adap->num_frontends_initialized;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_DVBT;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_SOC_MODE;
+ adap_state->ep6_clockphase = 1;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* dont care if this fails */
+ mxl111sf_init_port_expander(state);
+
+ adap->fe_adap[fe_id].fe = dvb_attach(mxl111sf_demod_attach, state,
+ &mxl_demod_config);
+ if (adap->fe_adap[fe_id].fe) {
+ adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
+ adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
+ adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state,
+ int antpath)
+{
+ return mxl111sf_idac_config(state, 1, 1,
+ (antpath == ANT_PATH_INTERNAL) ?
+ 0x3f : 0x00, 0);
+}
+
+#define DbgAntHunt(x, pwr0, pwr1, pwr2, pwr3) \
+ err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \
+ __func__, __LINE__, \
+ (ANT_PATH_EXTERNAL == x) ? "EXTERNAL" : "INTERNAL", \
+ pwr0, pwr1, pwr2, pwr3)
+
+#define ANT_HUNT_SLEEP 90
+#define ANT_EXT_TWEAK 0
+
+static int mxl111sf_ant_hunt(struct dvb_frontend *fe)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+
+ int antctrl = dvb_usb_mxl111sf_rfswitch;
+
+ u16 rxPwrA, rxPwr0, rxPwr1, rxPwr2;
+
+ /* FIXME: must force EXTERNAL for QAM - done elsewhere */
+ mxl111sf_set_ant_path(state, antctrl == ANT_PATH_AUTO ?
+ ANT_PATH_EXTERNAL : antctrl);
+
+ if (antctrl == ANT_PATH_AUTO) {
+#if 0
+ msleep(ANT_HUNT_SLEEP);
+#endif
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwrA);
+
+ mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
+ msleep(ANT_HUNT_SLEEP);
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr0);
+
+ mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
+ msleep(ANT_HUNT_SLEEP);
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr1);
+
+ mxl111sf_set_ant_path(state, ANT_PATH_INTERNAL);
+ msleep(ANT_HUNT_SLEEP);
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr2);
+
+ if (rxPwr1+ANT_EXT_TWEAK >= rxPwr2) {
+ /* return with EXTERNAL enabled */
+ mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
+ DbgAntHunt(ANT_PATH_EXTERNAL, rxPwrA,
+ rxPwr0, rxPwr1, rxPwr2);
+ } else {
+ /* return with INTERNAL enabled */
+ DbgAntHunt(ANT_PATH_INTERNAL, rxPwrA,
+ rxPwr0, rxPwr1, rxPwr2);
+ }
+ }
+ return 0;
+}
+
+static struct mxl111sf_tuner_config mxl_tuner_config = {
+ .if_freq = MXL_IF_6_0, /* applies to external IF output, only */
+ .invert_spectrum = 0,
+ .read_reg = mxl111sf_read_reg,
+ .write_reg = mxl111sf_write_reg,
+ .program_regs = mxl111sf_ctrl_program_regs,
+ .top_master_ctrl = mxl1x1sf_top_master_ctrl,
+ .ant_hunt = mxl111sf_ant_hunt,
+};
+
+static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int fe_id = adap->num_frontends_initialized;
+
+ deb_adv("%s()\n", __func__);
+
+ if (NULL != dvb_attach(mxl111sf_tuner_attach,
+ adap->fe_adap[fe_id].fe, state,
+ &mxl_tuner_config))
+ return 0;
+
+ return -EIO;
+}
+
+static int mxl111sf_fe_ioctl_override(struct dvb_frontend *fe,
+ unsigned int cmd, void *parg,
+ unsigned int stage)
+{
+ int err = 0;
+
+ switch (stage) {
+ case DVB_FE_IOCTL_PRE:
+
+ switch (cmd) {
+ case FE_READ_SIGNAL_STRENGTH:
+ err = fe->ops.tuner_ops.get_rf_strength(fe, parg);
+ /* If no error occurs, prevent dvb-core from handling
+ * this IOCTL, otherwise return the error */
+ if (0 == err)
+ err = 1;
+ break;
+ }
+ break;
+
+ case DVB_FE_IOCTL_POST:
+ /* no post-ioctl handling required */
+ break;
+ }
+ return err;
+};
+
+static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+struct i2c_algorithm mxl111sf_i2c_algo = {
+ .master_xfer = mxl111sf_i2c_xfer,
+ .functionality = mxl111sf_i2c_func,
+#ifdef NEED_ALGO_CONTROL
+ .algo_control = dummy_algo_control,
+#endif
+};
+
+static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties;
+static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties;
+
+static int mxl111sf_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct dvb_usb_device *d = NULL;
+
+ deb_adv("%s()\n", __func__);
+
+ if (((dvb_usb_mxl111sf_isoc) &&
+ (0 == dvb_usb_device_init(intf,
+ &mxl111sf_dvbt_isoc_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_atsc_isoc_properties,
+ THIS_MODULE, &d, adapter_nr))) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_dvbt_bulk_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_atsc_bulk_properties,
+ THIS_MODULE, &d, adapter_nr) || 0) {
+
+ struct mxl111sf_state *state = d->priv;
+ static u8 eeprom[256];
+ struct i2c_client c;
+ int ret;
+
+ ret = get_chip_info(state);
+ if (mxl_fail(ret))
+ err("failed to get chip info during probe");
+
+ mutex_init(&state->fe_lock);
+
+ if (state->chip_rev > MXL111SF_V6)
+ mxl111sf_config_pin_mux_modes(state,
+ PIN_MUX_TS_SPI_IN_MODE_1);
+
+ c.adapter = &d->i2c_adap;
+ c.addr = 0xa0 >> 1;
+
+ ret = tveeprom_read(&c, eeprom, sizeof(eeprom));
+ if (mxl_fail(ret))
+ return 0;
+ tveeprom_hauppauge_analog(&c, &state->tv,
+ (0x84 == eeprom[0xa0]) ?
+ eeprom + 0xa0 : eeprom + 0x80);
+#if 0
+ switch (state->tv.model) {
+ case 117001:
+ case 126001:
+ case 138001:
+ break;
+ default:
+ printk(KERN_WARNING "%s: warning: "
+ "unknown hauppauge model #%d\n",
+ __func__, state->tv.model);
+ }
+#endif
+ return 0;
+ }
+ err("Your device is not yet supported by this driver. "
+ "See kernellabs.com for more info");
+ return -EINVAL;
+}
+
+static struct usb_device_id mxl111sf_table[] = {
+/* 0 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc600) }, /* ATSC+ IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc601) }, /* ATSC */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc602) }, /* + */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc603) }, /* ATSC+ */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc604) }, /* DVBT */
+/* 5 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc609) }, /* ATSC IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60a) }, /* + IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60b) }, /* ATSC+ IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60c) }, /* DVBT IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc653) }, /* ATSC+ */
+/*10 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc65b) }, /* ATSC+ IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb700) }, /* ATSC+ sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb701) }, /* ATSC sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb702) }, /* + sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb703) }, /* ATSC+ sw */
+/*15 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb704) }, /* DVBT sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb753) }, /* ATSC+ sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb763) }, /* ATSC+ no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb764) }, /* DVBT no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd853) }, /* ATSC+ sw */
+/*20 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd854) }, /* DVBT sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd863) }, /* ATSC+ no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd864) }, /* DVBT no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d3) }, /* ATSC+ sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d4) }, /* DVBT sw */
+/*25 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e3) }, /* ATSC+ no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e4) }, /* DVBT no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8ff) }, /* ATSC+ */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc612) }, /* + */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc613) }, /* ATSC+ */
+/*30 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61a) }, /* + IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61b) }, /* ATSC+ IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb757) }, /* ATSC+DVBT sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb767) }, /* ATSC+DVBT no */
+ {} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, mxl111sf_table);
+
+
+#define MXL111SF_EP4_BULK_STREAMING_CONFIG \
+ .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \
+ .stream = { \
+ .type = USB_BULK, \
+ .count = 5, \
+ .endpoint = 0x04, \
+ .u = { \
+ .bulk = { \
+ .buffersize = 8192, \
+ } \
+ } \
+ }
+
+/* FIXME: works for v6 but not v8 silicon */
+#define MXL111SF_EP4_ISOC_STREAMING_CONFIG \
+ .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \
+ .stream = { \
+ .type = USB_ISOC, \
+ .count = 5, \
+ .endpoint = 0x04, \
+ .u = { \
+ .isoc = { \
+ .framesperurb = 96, \
+ /* FIXME: v6 SILICON: */ \
+ .framesize = 564, \
+ .interval = 1, \
+ } \
+ } \
+ }
+
+#define MXL111SF_EP6_BULK_STREAMING_CONFIG \
+ .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \
+ .stream = { \
+ .type = USB_BULK, \
+ .count = 5, \
+ .endpoint = 0x06, \
+ .u = { \
+ .bulk = { \
+ .buffersize = 8192, \
+ } \
+ } \
+ }
+
+/* FIXME */
+#define MXL111SF_EP6_ISOC_STREAMING_CONFIG \
+ .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \
+ .stream = { \
+ .type = USB_ISOC, \
+ .count = 5, \
+ .endpoint = 0x06, \
+ .u = { \
+ .isoc = { \
+ .framesperurb = 24, \
+ .framesize = 3072, \
+ .interval = 1, \
+ } \
+ } \
+ }
+
+#define MXL111SF_DEFAULT_DEVICE_PROPERTIES \
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER, \
+ .usb_ctrl = DEVICE_SPECIFIC, \
+ /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t), \
+ EP6 BULK transfer (atsc/qam), \
+ use usb alt setting 2 for EP4 BULK transfer (dvb-t), \
+ EP6 ISOC transfer (atsc/qam), \
+ */ \
+ .power_ctrl = mxl111sf_power_ctrl, \
+ .i2c_algo = &mxl111sf_i2c_algo, \
+ .generic_bulk_ctrl_endpoint = MXL_EP2_REG_WRITE, \
+ .generic_bulk_ctrl_endpoint_response = MXL_EP1_REG_READ, \
+ .size_of_priv = sizeof(struct mxl111sf_state)
+
+static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 1,
+ .fe = {{
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_BULK_STREAMING_CONFIG,
+ } },
+ },
+ },
+ .num_device_descs = 4,
+ .devices = {
+ { "Hauppauge 126xxx DVBT (bulk)",
+ { NULL },
+ { &mxl111sf_table[4], &mxl111sf_table[8],
+ NULL },
+ },
+ { "Hauppauge 117xxx DVBT (bulk)",
+ { NULL },
+ { &mxl111sf_table[15], &mxl111sf_table[18],
+ NULL },
+ },
+ { "Hauppauge 138xxx DVBT (bulk)",
+ { NULL },
+ { &mxl111sf_table[20], &mxl111sf_table[22],
+ &mxl111sf_table[24], &mxl111sf_table[26],
+ NULL },
+ },
+ { "Hauppauge 126xxx (tp-bulk)",
+ { NULL },
+ { &mxl111sf_table[28], &mxl111sf_table[30],
+ NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 1,
+ .fe = {{
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_ISOC_STREAMING_CONFIG,
+ } },
+ },
+ },
+ .num_device_descs = 4,
+ .devices = {
+ { "Hauppauge 126xxx DVBT (isoc)",
+ { NULL },
+ { &mxl111sf_table[4], &mxl111sf_table[8],
+ NULL },
+ },
+ { "Hauppauge 117xxx DVBT (isoc)",
+ { NULL },
+ { &mxl111sf_table[15], &mxl111sf_table[18],
+ NULL },
+ },
+ { "Hauppauge 138xxx DVBT (isoc)",
+ { NULL },
+ { &mxl111sf_table[20], &mxl111sf_table[22],
+ &mxl111sf_table[24], &mxl111sf_table[26],
+ NULL },
+ },
+ { "Hauppauge 126xxx (tp-isoc)",
+ { NULL },
+ { &mxl111sf_table[28], &mxl111sf_table[30],
+ NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 2,
+ .fe = {{
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_BULK_STREAMING_CONFIG,
+ },
+ {
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_BULK_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 6,
+ .devices = {
+ { "Hauppauge 126xxx ATSC (bulk)",
+ { NULL },
+ { &mxl111sf_table[1], &mxl111sf_table[5],
+ NULL },
+ },
+ { "Hauppauge 117xxx ATSC (bulk)",
+ { NULL },
+ { &mxl111sf_table[12],
+ NULL },
+ },
+ { "Hauppauge 126xxx ATSC+ (bulk)",
+ { NULL },
+ { &mxl111sf_table[0], &mxl111sf_table[3],
+ &mxl111sf_table[7], &mxl111sf_table[9],
+ &mxl111sf_table[10], NULL },
+ },
+ { "Hauppauge 117xxx ATSC+ (bulk)",
+ { NULL },
+ { &mxl111sf_table[11], &mxl111sf_table[14],
+ &mxl111sf_table[16], &mxl111sf_table[17],
+ &mxl111sf_table[32], &mxl111sf_table[33],
+ NULL },
+ },
+ { "Hauppauge Mercury (tp-bulk)",
+ { NULL },
+ { &mxl111sf_table[19], &mxl111sf_table[21],
+ &mxl111sf_table[23], &mxl111sf_table[25],
+ &mxl111sf_table[27], NULL },
+ },
+ { "Hauppauge WinTV-Aero-M",
+ { NULL },
+ { &mxl111sf_table[29], &mxl111sf_table[31],
+ NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 2,
+ .fe = {{
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_ISOC_STREAMING_CONFIG,
+ },
+ {
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_ISOC_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 6,
+ .devices = {
+ { "Hauppauge 126xxx ATSC (isoc)",
+ { NULL },
+ { &mxl111sf_table[1], &mxl111sf_table[5],
+ NULL },
+ },
+ { "Hauppauge 117xxx ATSC (isoc)",
+ { NULL },
+ { &mxl111sf_table[12],
+ NULL },
+ },
+ { "Hauppauge 126xxx ATSC+ (isoc)",
+ { NULL },
+ { &mxl111sf_table[0], &mxl111sf_table[3],
+ &mxl111sf_table[7], &mxl111sf_table[9],
+ &mxl111sf_table[10], NULL },
+ },
+ { "Hauppauge 117xxx ATSC+ (isoc)",
+ { NULL },
+ { &mxl111sf_table[11], &mxl111sf_table[14],
+ &mxl111sf_table[16], &mxl111sf_table[17],
+ &mxl111sf_table[32], &mxl111sf_table[33],
+ NULL },
+ },
+ { "Hauppauge Mercury (tp-isoc)",
+ { NULL },
+ { &mxl111sf_table[19], &mxl111sf_table[21],
+ &mxl111sf_table[23], &mxl111sf_table[25],
+ &mxl111sf_table[27], NULL },
+ },
+ { "Hauppauge WinTV-Aero-M (tp-isoc)",
+ { NULL },
+ { &mxl111sf_table[29], &mxl111sf_table[31],
+ NULL },
+ },
+ }
+};
+
+static struct usb_driver mxl111sf_driver = {
+ .name = "dvb_usb_mxl111sf",
+ .probe = mxl111sf_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = mxl111sf_table,
+};
+
+static int __init mxl111sf_module_init(void)
+{
+ int result = usb_register(&mxl111sf_driver);
+ if (result) {
+ err("usb_register failed. Error number %d", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit mxl111sf_module_exit(void)
+{
+ usb_deregister(&mxl111sf_driver);
+}
+
+module_init(mxl111sf_module_init);
+module_exit(mxl111sf_module_exit);
+
+MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
+MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.h b/drivers/media/dvb/dvb-usb/mxl111sf.h
new file mode 100644
index 000000000000..364d89f826bd
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+
+#ifndef _DVB_USB_MXL111SF_H_
+#define _DVB_USB_MXL111SF_H_
+
+#ifdef DVB_USB_LOG_PREFIX
+#undef DVB_USB_LOG_PREFIX
+#endif
+#define DVB_USB_LOG_PREFIX "mxl111sf"
+#include "dvb-usb.h"
+#include <media/tveeprom.h>
+
+#define MXL_EP1_REG_READ 1
+#define MXL_EP2_REG_WRITE 2
+#define MXL_EP3_INTERRUPT 3
+#define MXL_EP4_MPEG2 4
+#define MXL_EP5_I2S 5
+#define MXL_EP6_656 6
+#define MXL_EP6_MPEG2 6
+
+#ifdef USING_ENUM_mxl111sf_current_mode
+enum mxl111sf_current_mode {
+ mxl_mode_dvbt = MXL_EP4_MPEG2,
+ mxl_mode_mh = MXL_EP5_I2S,
+ mxl_mode_atsc = MXL_EP6_MPEG2,
+};
+#endif
+
+enum mxl111sf_gpio_port_expander {
+ mxl111sf_gpio_hw,
+ mxl111sf_PCA9534,
+};
+
+struct mxl111sf_state {
+ struct dvb_usb_device *d;
+
+ enum mxl111sf_gpio_port_expander gpio_port_expander;
+ u8 port_expander_addr;
+
+ u8 chip_id;
+ u8 chip_ver;
+#define MXL111SF_V6 1
+#define MXL111SF_V8_100 2
+#define MXL111SF_V8_200 3
+ u8 chip_rev;
+
+#ifdef USING_ENUM_mxl111sf_current_mode
+ enum mxl111sf_current_mode current_mode;
+#endif
+
+#define MXL_TUNER_MODE 0
+#define MXL_SOC_MODE 1
+#define MXL_DEV_MODE_MASK 0x01
+#if 1
+ int device_mode;
+#endif
+ /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t),
+ EP5 BULK transfer (atsc-mh),
+ EP6 BULK transfer (atsc/qam),
+ use usb alt setting 2 for EP4 BULK transfer (dvb-t),
+ EP5 ISOC transfer (atsc-mh),
+ EP6 ISOC transfer (atsc/qam),
+ */
+ int alt_mode;
+ int gpio_mode;
+ struct tveeprom tv;
+
+ struct mutex fe_lock;
+};
+
+struct mxl111sf_adap_state {
+ int alt_mode;
+ int gpio_mode;
+ int device_mode;
+ int ep6_clockphase;
+ int (*fe_init)(struct dvb_frontend *);
+ int (*fe_sleep)(struct dvb_frontend *);
+};
+
+int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data);
+int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data);
+
+struct mxl111sf_reg_ctrl_info {
+ u8 addr;
+ u8 mask;
+ u8 data;
+};
+
+int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
+ u8 addr, u8 mask, u8 data);
+int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
+
+/* needed for hardware i2c functions in mxl111sf-i2c.c:
+ * mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */
+int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
+ u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen);
+
+#define mxl_printk(kern, fmt, arg...) \
+ printk(kern "%s: " fmt "\n", __func__, ##arg)
+
+#define mxl_info(fmt, arg...) \
+ mxl_printk(KERN_INFO, fmt, ##arg)
+
+extern int dvb_usb_mxl111sf_debug;
+#define mxl_debug(fmt, arg...) \
+ if (dvb_usb_mxl111sf_debug) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+#define MXL_I2C_DBG 0x04
+#define MXL_ADV_DBG 0x10
+#define mxl_debug_adv(fmt, arg...) \
+ if (dvb_usb_mxl111sf_debug & MXL_ADV_DBG) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+#define mxl_i2c(fmt, arg...) \
+ if (dvb_usb_mxl111sf_debug & MXL_I2C_DBG) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+#define mxl_i2c_adv(fmt, arg...) \
+ if ((dvb_usb_mxl111sf_debug & (MXL_I2C_DBG | MXL_ADV_DBG)) == \
+ (MXL_I2C_DBG | MXL_ADV_DBG)) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+/* The following allows the mxl_fail() macro defined below to work
+ * in externel modules, such as mxl111sf-tuner.ko, even though
+ * dvb_usb_mxl111sf_debug is not defined within those modules */
+#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__))
+#define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG
+#else
+#define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug
+#endif
+
+#define mxl_fail(ret) \
+({ \
+ int __ret; \
+ __ret = (ret < 0); \
+ if ((__ret) && (MXL_ADV_DEBUG_ENABLED & MXL_ADV_DBG)) \
+ mxl_printk(KERN_ERR, "error %d on line %d", \
+ ret, __LINE__); \
+ __ret; \
+})
+
+#endif /* _DVB_USB_MXL111SF_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
index bc350e982b72..21384da6570e 100644
--- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c
+++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
@@ -166,6 +166,8 @@ static struct dvb_usb_device_properties nova_t_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -186,7 +188,7 @@ static struct dvb_usb_device_properties nova_t_properties = {
}
}
},
-
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c
index 2e4fab7215f5..98fd9a6092b7 100644
--- a/drivers/media/dvb/dvb-usb/opera1.c
+++ b/drivers/media/dvb/dvb-usb/opera1.c
@@ -263,10 +263,10 @@ static struct stv0299_config opera1_stv0299_config = {
static int opera1_frontend_attach(struct dvb_usb_adapter *d)
{
- if ((d->fe =
- dvb_attach(stv0299_attach, &opera1_stv0299_config,
- &d->dev->i2c_adap)) != NULL) {
- d->fe->ops.set_voltage = opera1_set_voltage;
+ d->fe_adap[0].fe = dvb_attach(stv0299_attach, &opera1_stv0299_config,
+ &d->dev->i2c_adap);
+ if ((d->fe_adap[0].fe) != NULL) {
+ d->fe_adap[0].fe->ops.set_voltage = opera1_set_voltage;
return 0;
}
info("not attached stv0299");
@@ -276,7 +276,7 @@ static int opera1_frontend_attach(struct dvb_usb_adapter *d)
static int opera1_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(
- dvb_pll_attach, adap->fe, 0xc0>>1,
+ dvb_pll_attach, adap->fe_adap[0].fe, 0xc0>>1,
&adap->dev->i2c_adap, DVB_PLL_OPERA1
);
return 0;
@@ -516,6 +516,8 @@ static struct dvb_usb_device_properties opera1_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = opera1_frontend_attach,
.streaming_ctrl = opera1_streaming_ctrl,
.tuner_attach = opera1_tuner_attach,
@@ -535,6 +537,7 @@ static struct dvb_usb_device_properties opera1_properties = {
}
}
},
+ }},
}
},
.num_device_descs = 1,
diff --git a/drivers/media/dvb/dvb-usb/pctv452e.c b/drivers/media/dvb/dvb-usb/pctv452e.c
new file mode 100644
index 000000000000..f9aec5cb6e77
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/pctv452e.c
@@ -0,0 +1,1079 @@
+/*
+ * PCTV 452e DVB driver
+ *
+ * Copyright (c) 2006-2008 Dominik Kuhlen <dkuhlen@gmx.net>
+ *
+ * TT connect S2-3650-CI Common Interface support, MAC readout
+ * Copyright (C) 2008 Michael H. Schimek <mschimek@gmx.at>
+ *
+ * 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.
+ */
+
+/* dvb usb framework */
+#define DVB_USB_LOG_PREFIX "pctv452e"
+#include "dvb-usb.h"
+
+/* Demodulator */
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+/* Tuner */
+#include "stb6100.h"
+#include "stb6100_cfg.h"
+/* FE Power */
+#include "lnbp22.h"
+
+#include "dvb_ca_en50221.h"
+#include "ttpci-eeprom.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define ISOC_INTERFACE_ALTERNATIVE 3
+
+#define SYNC_BYTE_OUT 0xaa
+#define SYNC_BYTE_IN 0x55
+
+/* guessed: (copied from ttusb-budget) */
+#define PCTV_CMD_RESET 0x15
+/* command to poll IR receiver */
+#define PCTV_CMD_IR 0x1b
+/* command to send I2C */
+#define PCTV_CMD_I2C 0x31
+
+#define I2C_ADDR_STB0899 (0xd0 >> 1)
+#define I2C_ADDR_STB6100 (0xc0 >> 1)
+#define I2C_ADDR_LNBP22 (0x10 >> 1)
+#define I2C_ADDR_24C16 (0xa0 >> 1)
+#define I2C_ADDR_24C64 (0xa2 >> 1)
+
+
+/* pctv452e sends us this amount of data for each issued usb-command */
+#define PCTV_ANSWER_LEN 64
+/* Wait up to 1000ms for device */
+#define PCTV_TIMEOUT 1000
+
+
+#define PCTV_LED_GPIO STB0899_GPIO01
+#define PCTV_LED_GREEN 0x82
+#define PCTV_LED_ORANGE 0x02
+
+#define ci_dbg(format, arg...) \
+do { \
+ if (0) \
+ printk(KERN_DEBUG DVB_USB_LOG_PREFIX \
+ ": " format "\n" , ## arg); \
+} while (0)
+
+enum {
+ TT3650_CMD_CI_TEST = 0x40,
+ TT3650_CMD_CI_RD_CTRL,
+ TT3650_CMD_CI_WR_CTRL,
+ TT3650_CMD_CI_RD_ATTR,
+ TT3650_CMD_CI_WR_ATTR,
+ TT3650_CMD_CI_RESET,
+ TT3650_CMD_CI_SET_VIDEO_PORT
+};
+
+
+static struct stb0899_postproc pctv45e_postproc[] = {
+ { PCTV_LED_GPIO, STB0899_GPIOPULLUP },
+ { 0, 0 }
+};
+
+/*
+ * stores all private variables for communication with the PCTV452e DVB-S2
+ */
+struct pctv452e_state {
+ struct dvb_ca_en50221 ca;
+ struct mutex ca_mutex;
+
+ u8 c; /* transaction counter, wraps around... */
+ u8 initialized; /* set to 1 if 0x15 has been sent */
+ u16 last_rc_key;
+};
+
+static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data,
+ unsigned int write_len, unsigned int read_len)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 buf[64];
+ u8 id;
+ unsigned int rlen;
+ int ret;
+
+ BUG_ON(NULL == data && 0 != (write_len | read_len));
+ BUG_ON(write_len > 64 - 4);
+ BUG_ON(read_len > 64 - 4);
+
+ id = state->c++;
+
+ buf[0] = SYNC_BYTE_OUT;
+ buf[1] = id;
+ buf[2] = cmd;
+ buf[3] = write_len;
+
+ memcpy(buf + 4, data, write_len);
+
+ rlen = (read_len > 0) ? 64 : 0;
+ ret = dvb_usb_generic_rw(d, buf, 4 + write_len,
+ buf, rlen, /* delay_ms */ 0);
+ if (0 != ret)
+ goto failed;
+
+ ret = -EIO;
+ if (SYNC_BYTE_IN != buf[0] || id != buf[1])
+ goto failed;
+
+ memcpy(data, buf + 4, read_len);
+
+ return 0;
+
+failed:
+ err("CI error %d; %02X %02X %02X -> %02X %02X %02X.",
+ ret, SYNC_BYTE_OUT, id, cmd, buf[0], buf[1], buf[2]);
+
+ return ret;
+}
+
+static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca,
+ u8 cmd, u8 *data, unsigned int write_len,
+ unsigned int read_len)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ int ret;
+
+ mutex_lock(&state->ca_mutex);
+ ret = tt3650_ci_msg(d, cmd, data, write_len, read_len);
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address)
+{
+ u8 buf[3];
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3);
+
+ ci_dbg("%s %04x -> %d 0x%02x",
+ __func__, address, ret, buf[2]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[2];
+}
+
+static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address, u8 value)
+{
+ u8 buf[3];
+
+ ci_dbg("%s %d 0x%04x 0x%02x",
+ __func__, slot, address, value);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+ buf[2] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3);
+}
+
+static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address)
+{
+ u8 buf[2];
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = address & 3;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2);
+
+ ci_dbg("%s 0x%02x -> %d 0x%02x",
+ __func__, address, ret, buf[1]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[1];
+}
+
+static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address,
+ u8 value)
+{
+ u8 buf[2];
+
+ ci_dbg("%s %d 0x%02x 0x%02x",
+ __func__, slot, address, value);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = address;
+ buf[1] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2);
+}
+
+static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca,
+ int slot,
+ int enable)
+{
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%s %d %d", __func__, slot, enable);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ enable = !!enable;
+ buf[0] = enable;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+ if (ret < 0)
+ return ret;
+
+ if (enable != buf[0]) {
+ err("CI not %sabled.", enable ? "en" : "dis");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, /* enable */ 0);
+}
+
+static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, /* enable */ 1);
+}
+
+static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%s %d", __func__, slot);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = 0;
+
+ mutex_lock(&state->ca_mutex);
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (0 != ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 1;
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (0 != ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 0; /* FTA */
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+
+ failed:
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca,
+ int slot,
+ int open)
+{
+ u8 buf[1];
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1);
+ if (0 != ret)
+ return ret;
+
+ if (1 == buf[0])
+ return DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+
+ return 0;
+
+}
+
+static void tt3650_ci_uninit(struct dvb_usb_device *d)
+{
+ struct pctv452e_state *state;
+
+ ci_dbg("%s", __func__);
+
+ if (NULL == d)
+ return;
+
+ state = (struct pctv452e_state *)d->priv;
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ /* Error ignored. */
+ tt3650_ci_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0);
+
+ dvb_ca_en50221_release(&state->ca);
+
+ memset(&state->ca, 0, sizeof(state->ca));
+}
+
+static int tt3650_ci_init(struct dvb_usb_adapter *a)
+{
+ struct dvb_usb_device *d = a->dev;
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ int ret;
+
+ ci_dbg("%s", __func__);
+
+ mutex_init(&state->ca_mutex);
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem;
+ state->ca.read_cam_control = tt3650_ci_read_cam_control;
+ state->ca.write_cam_control = tt3650_ci_write_cam_control;
+ state->ca.slot_reset = tt3650_ci_slot_reset;
+ state->ca.slot_shutdown = tt3650_ci_slot_shutdown;
+ state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable;
+ state->ca.poll_slot_status = tt3650_ci_poll_slot_status;
+ state->ca.data = d;
+
+ ret = dvb_ca_en50221_init(&a->dvb_adap,
+ &state->ca,
+ /* flags */ 0,
+ /* n_slots */ 1);
+ if (0 != ret) {
+ err("Cannot initialize CI: Error %d.", ret);
+ memset(&state->ca, 0, sizeof(state->ca));
+ return ret;
+ }
+
+ info("CI initialized.");
+
+ return 0;
+}
+
+#define CMD_BUFFER_SIZE 0x28
+static int pctv452e_i2c_msg(struct dvb_usb_device *d, u8 addr,
+ const u8 *snd_buf, u8 snd_len,
+ u8 *rcv_buf, u8 rcv_len)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 buf[64];
+ u8 id;
+ int ret;
+
+ id = state->c++;
+
+ ret = -EINVAL;
+ if (snd_len > 64 - 7 || rcv_len > 64 - 7)
+ goto failed;
+
+ buf[0] = SYNC_BYTE_OUT;
+ buf[1] = id;
+ buf[2] = PCTV_CMD_I2C;
+ buf[3] = snd_len + 3;
+ buf[4] = addr << 1;
+ buf[5] = snd_len;
+ buf[6] = rcv_len;
+
+ memcpy(buf + 7, snd_buf, snd_len);
+
+ ret = dvb_usb_generic_rw(d, buf, 7 + snd_len,
+ buf, /* rcv_len */ 64,
+ /* delay_ms */ 0);
+ if (ret < 0)
+ goto failed;
+
+ /* TT USB protocol error. */
+ ret = -EIO;
+ if (SYNC_BYTE_IN != buf[0] || id != buf[1])
+ goto failed;
+
+ /* I2C device didn't respond as expected. */
+ ret = -EREMOTEIO;
+ if (buf[5] < snd_len || buf[6] < rcv_len)
+ goto failed;
+
+ memcpy(rcv_buf, buf + 7, rcv_len);
+
+ return rcv_len;
+
+failed:
+ err("I2C error %d; %02X %02X %02X %02X %02X -> "
+ "%02X %02X %02X %02X %02X.",
+ ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len,
+ buf[0], buf[1], buf[4], buf[5], buf[6]);
+
+ return ret;
+}
+
+static int pctv452e_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg,
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adapter);
+ int i;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf;
+ int ret;
+
+ if (msg[i].flags & I2C_M_RD) {
+ addr = msg[i].addr;
+ snd_buf = NULL;
+ snd_len = 0;
+ rcv_buf = msg[i].buf;
+ rcv_len = msg[i].len;
+ } else {
+ addr = msg[i].addr;
+ snd_buf = msg[i].buf;
+ snd_len = msg[i].len;
+ rcv_buf = NULL;
+ rcv_len = 0;
+ }
+
+ ret = pctv452e_i2c_msg(d, addr, snd_buf, snd_len, rcv_buf,
+ rcv_len);
+ if (ret < rcv_len)
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+static u32 pctv452e_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 b0[] = { 0xaa, 0, PCTV_CMD_RESET, 1, 0 };
+ u8 rx[PCTV_ANSWER_LEN];
+ int ret;
+
+ info("%s: %d\n", __func__, i);
+
+ if (!i)
+ return 0;
+
+ if (state->initialized)
+ return 0;
+
+ /* hmm where shoud this should go? */
+ ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE);
+ if (ret != 0)
+ info("%s: Warning set interface returned: %d\n",
+ __func__, ret);
+
+ /* this is a one-time initialization, dont know where to put */
+ b0[1] = state->c++;
+ /* reset board */
+ ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0);
+ if (ret)
+ return ret;
+
+ b0[1] = state->c++;
+ b0[4] = 1;
+ /* reset board (again?) */
+ ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0);
+ if (ret)
+ return ret;
+
+ state->initialized = 1;
+
+ return 0;
+}
+
+static int pctv452e_rc_query(struct dvb_usb_device *d)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 b[CMD_BUFFER_SIZE];
+ u8 rx[PCTV_ANSWER_LEN];
+ int ret, i;
+ u8 id = state->c++;
+
+ /* prepare command header */
+ b[0] = SYNC_BYTE_OUT;
+ b[1] = id;
+ b[2] = PCTV_CMD_IR;
+ b[3] = 0;
+
+ /* send ir request */
+ ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0);
+ if (ret != 0)
+ return ret;
+
+ if (debug > 3) {
+ info("%s: read: %2d: %02x %02x %02x: ", __func__,
+ ret, rx[0], rx[1], rx[2]);
+ for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++)
+ info(" %02x", rx[i+3]);
+
+ info("\n");
+ }
+
+ if ((rx[3] == 9) && (rx[12] & 0x01)) {
+ /* got a "press" event */
+ state->last_rc_key = (rx[7] << 8) | rx[6];
+ if (debug > 2)
+ info("%s: cmd=0x%02x sys=0x%02x\n",
+ __func__, rx[6], rx[7]);
+
+ rc_keydown(d->rc_dev, state->last_rc_key, 0);
+ } else if (state->last_rc_key) {
+ rc_keyup(d->rc_dev);
+ state->last_rc_key = 0;
+ }
+
+ return 0;
+}
+
+static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+ const u8 mem_addr[] = { 0x1f, 0xcc };
+ u8 encoded_mac[20];
+ int ret;
+
+ ret = -EAGAIN;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ goto failed;
+
+ ret = pctv452e_i2c_msg(d, I2C_ADDR_24C16,
+ mem_addr + 1, /* snd_len */ 1,
+ encoded_mac, /* rcv_len */ 20);
+ if (-EREMOTEIO == ret)
+ /* Caution! A 24C16 interprets 0xA2 0x1F 0xCC as a
+ byte write if /WC is low. */
+ ret = pctv452e_i2c_msg(d, I2C_ADDR_24C64,
+ mem_addr, 2,
+ encoded_mac, 20);
+
+ mutex_unlock(&d->i2c_mutex);
+
+ if (20 != ret)
+ goto failed;
+
+ ret = ttpci_eeprom_decode_mac(mac, encoded_mac);
+ if (0 != ret)
+ goto failed;
+
+ return 0;
+
+failed:
+ memset(mac, 0, 6);
+
+ return ret;
+}
+
+static const struct stb0899_s1_reg pctv452e_init_dev[] = {
+ { STB0899_DISCNTRL1, 0x26 },
+ { STB0899_DISCNTRL2, 0x80 },
+ { STB0899_DISRX_ST0, 0x04 },
+ { STB0899_DISRX_ST1, 0x20 },
+ { STB0899_DISPARITY, 0x00 },
+ { STB0899_DISFIFO, 0x00 },
+ { STB0899_DISF22, 0x99 },
+ { STB0899_DISF22RX, 0x85 }, /* 0xa8 */
+ { STB0899_ACRPRESC, 0x11 },
+ { STB0899_ACRDIV1, 0x0a },
+ { STB0899_ACRDIV2, 0x05 },
+ { STB0899_DACR1 , 0x00 },
+ { STB0899_DACR2 , 0x00 },
+ { STB0899_OUTCFG, 0x00 },
+ { STB0899_MODECFG, 0x00 }, /* Inversion */
+ { STB0899_IRQMSK_3, 0xf3 },
+ { STB0899_IRQMSK_2, 0xfc },
+ { STB0899_IRQMSK_1, 0xff },
+ { STB0899_IRQMSK_0, 0xff },
+ { STB0899_I2CCFG, 0x88 },
+ { STB0899_I2CRPT, 0x58 },
+ { STB0899_GPIO00CFG, 0x82 },
+ { STB0899_GPIO01CFG, 0x82 }, /* LED: 0x02 green, 0x82 orange */
+ { STB0899_GPIO02CFG, 0x82 },
+ { STB0899_GPIO03CFG, 0x82 },
+ { STB0899_GPIO04CFG, 0x82 },
+ { STB0899_GPIO05CFG, 0x82 },
+ { STB0899_GPIO06CFG, 0x82 },
+ { STB0899_GPIO07CFG, 0x82 },
+ { STB0899_GPIO08CFG, 0x82 },
+ { STB0899_GPIO09CFG, 0x82 },
+ { STB0899_GPIO10CFG, 0x82 },
+ { STB0899_GPIO11CFG, 0x82 },
+ { STB0899_GPIO12CFG, 0x82 },
+ { STB0899_GPIO13CFG, 0x82 },
+ { STB0899_GPIO14CFG, 0x82 },
+ { STB0899_GPIO15CFG, 0x82 },
+ { STB0899_GPIO16CFG, 0x82 },
+ { STB0899_GPIO17CFG, 0x82 },
+ { STB0899_GPIO18CFG, 0x82 },
+ { STB0899_GPIO19CFG, 0x82 },
+ { STB0899_GPIO20CFG, 0x82 },
+ { STB0899_SDATCFG, 0xb8 },
+ { STB0899_SCLTCFG, 0xba },
+ { STB0899_AGCRFCFG, 0x1c }, /* 0x11 DVB-S; 0x1c DVB-S2 (1c, rjkm) */
+ { STB0899_GPIO22, 0x82 },
+ { STB0899_GPIO21, 0x91 },
+ { STB0899_DIRCLKCFG, 0x82 },
+ { STB0899_CLKOUT27CFG, 0x7e },
+ { STB0899_STDBYCFG, 0x82 },
+ { STB0899_CS0CFG, 0x82 },
+ { STB0899_CS1CFG, 0x82 },
+ { STB0899_DISEQCOCFG, 0x20 },
+ { STB0899_NCOARSE, 0x15 }, /* 0x15 27Mhz, F/3 198MHz, F/6 108MHz */
+ { STB0899_SYNTCTRL, 0x00 }, /* 0x00 CLKI, 0x02 XTALI */
+ { STB0899_FILTCTRL, 0x00 },
+ { STB0899_SYSCTRL, 0x00 },
+ { STB0899_STOPCLK1, 0x20 }, /* orig: 0x00 budget-ci: 0x20 */
+ { STB0899_STOPCLK2, 0x00 },
+ { STB0899_INTBUFCTRL, 0x0a },
+ { STB0899_AGC2I1, 0x00 },
+ { STB0899_AGC2I2, 0x00 },
+ { STB0899_AGCIQIN, 0x00 },
+ { STB0899_TSTRES, 0x40 }, /* rjkm */
+ { 0xffff, 0xff },
+};
+
+static const struct stb0899_s1_reg pctv452e_init_s1_demod[] = {
+ { STB0899_DEMOD, 0x00 },
+ { STB0899_RCOMPC, 0xc9 },
+ { STB0899_AGC1CN, 0x01 },
+ { STB0899_AGC1REF, 0x10 },
+ { STB0899_RTC, 0x23 },
+ { STB0899_TMGCFG, 0x4e },
+ { STB0899_AGC2REF, 0x34 },
+ { STB0899_TLSR, 0x84 },
+ { STB0899_CFD, 0xf7 },
+ { STB0899_ACLC, 0x87 },
+ { STB0899_BCLC, 0x94 },
+ { STB0899_EQON, 0x41 },
+ { STB0899_LDT, 0xf1 },
+ { STB0899_LDT2, 0xe3 },
+ { STB0899_EQUALREF, 0xb4 },
+ { STB0899_TMGRAMP, 0x10 },
+ { STB0899_TMGTHD, 0x30 },
+ { STB0899_IDCCOMP, 0xfd },
+ { STB0899_QDCCOMP, 0xff },
+ { STB0899_POWERI, 0x0c },
+ { STB0899_POWERQ, 0x0f },
+ { STB0899_RCOMP, 0x6c },
+ { STB0899_AGCIQIN, 0x80 },
+ { STB0899_AGC2I1, 0x06 },
+ { STB0899_AGC2I2, 0x00 },
+ { STB0899_TLIR, 0x30 },
+ { STB0899_RTF, 0x7f },
+ { STB0899_DSTATUS, 0x00 },
+ { STB0899_LDI, 0xbc },
+ { STB0899_CFRM, 0xea },
+ { STB0899_CFRL, 0x31 },
+ { STB0899_NIRM, 0x2b },
+ { STB0899_NIRL, 0x80 },
+ { STB0899_ISYMB, 0x1d },
+ { STB0899_QSYMB, 0xa6 },
+ { STB0899_SFRH, 0x2f },
+ { STB0899_SFRM, 0x68 },
+ { STB0899_SFRL, 0x40 },
+ { STB0899_SFRUPH, 0x2f },
+ { STB0899_SFRUPM, 0x68 },
+ { STB0899_SFRUPL, 0x40 },
+ { STB0899_EQUAI1, 0x02 },
+ { STB0899_EQUAQ1, 0xff },
+ { STB0899_EQUAI2, 0x04 },
+ { STB0899_EQUAQ2, 0x05 },
+ { STB0899_EQUAI3, 0x02 },
+ { STB0899_EQUAQ3, 0xfd },
+ { STB0899_EQUAI4, 0x03 },
+ { STB0899_EQUAQ4, 0x07 },
+ { STB0899_EQUAI5, 0x08 },
+ { STB0899_EQUAQ5, 0xf5 },
+ { STB0899_DSTATUS2, 0x00 },
+ { STB0899_VSTATUS, 0x00 },
+ { STB0899_VERROR, 0x86 },
+ { STB0899_IQSWAP, 0x2a },
+ { STB0899_ECNT1M, 0x00 },
+ { STB0899_ECNT1L, 0x00 },
+ { STB0899_ECNT2M, 0x00 },
+ { STB0899_ECNT2L, 0x00 },
+ { STB0899_ECNT3M, 0x0a },
+ { STB0899_ECNT3L, 0xad },
+ { STB0899_FECAUTO1, 0x06 },
+ { STB0899_FECM, 0x01 },
+ { STB0899_VTH12, 0xb0 },
+ { STB0899_VTH23, 0x7a },
+ { STB0899_VTH34, 0x58 },
+ { STB0899_VTH56, 0x38 },
+ { STB0899_VTH67, 0x34 },
+ { STB0899_VTH78, 0x24 },
+ { STB0899_PRVIT, 0xff },
+ { STB0899_VITSYNC, 0x19 },
+ { STB0899_RSULC, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC, 0x42 },
+ { STB0899_RSLLC, 0x41 },
+ { STB0899_TSLPL, 0x12 },
+ { STB0899_TSCFGH, 0x0c },
+ { STB0899_TSCFGM, 0x00 },
+ { STB0899_TSCFGL, 0x00 },
+ { STB0899_TSOUT, 0x69 }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL, 0x00 },
+ { STB0899_TSINHDELH, 0x02 },
+ { STB0899_TSINHDELM, 0x00 },
+ { STB0899_TSINHDELL, 0x00 },
+ { STB0899_TSLLSTKM, 0x1b },
+ { STB0899_TSLLSTKL, 0xb3 },
+ { STB0899_TSULSTKM, 0x00 },
+ { STB0899_TSULSTKL, 0x00 },
+ { STB0899_PCKLENUL, 0xbc },
+ { STB0899_PCKLENLL, 0xcc },
+ { STB0899_RSPCKLEN, 0xbd },
+ { STB0899_TSSTATUS, 0x90 },
+ { STB0899_ERRCTRL1, 0xb6 },
+ { STB0899_ERRCTRL2, 0x95 },
+ { STB0899_ERRCTRL3, 0x8d },
+ { STB0899_DMONMSK1, 0x27 },
+ { STB0899_DMONMSK0, 0x03 },
+ { STB0899_DEMAPVIT, 0x5c },
+ { STB0899_PLPARM, 0x19 },
+ { STB0899_PDELCTRL, 0x48 },
+ { STB0899_PDELCTRL2, 0x00 },
+ { STB0899_BBHCTRL1, 0x00 },
+ { STB0899_BBHCTRL2, 0x00 },
+ { STB0899_HYSTTHRESH, 0x77 },
+ { STB0899_MATCSTM, 0x00 },
+ { STB0899_MATCSTL, 0x00 },
+ { STB0899_UPLCSTM, 0x00 },
+ { STB0899_UPLCSTL, 0x00 },
+ { STB0899_DFLCSTM, 0x00 },
+ { STB0899_DFLCSTL, 0x00 },
+ { STB0899_SYNCCST, 0x00 },
+ { STB0899_SYNCDCSTM, 0x00 },
+ { STB0899_SYNCDCSTL, 0x00 },
+ { STB0899_ISI_ENTRY, 0x00 },
+ { STB0899_ISI_BIT_EN, 0x00 },
+ { STB0899_MATSTRM, 0xf0 },
+ { STB0899_MATSTRL, 0x02 },
+ { STB0899_UPLSTRM, 0x45 },
+ { STB0899_UPLSTRL, 0x60 },
+ { STB0899_DFLSTRM, 0xe3 },
+ { STB0899_DFLSTRL, 0x00 },
+ { STB0899_SYNCSTR, 0x47 },
+ { STB0899_SYNCDSTRM, 0x05 },
+ { STB0899_SYNCDSTRL, 0x18 },
+ { STB0899_CFGPDELSTATUS1, 0x19 },
+ { STB0899_CFGPDELSTATUS2, 0x2b },
+ { STB0899_BBFERRORM, 0x00 },
+ { STB0899_BBFERRORL, 0x01 },
+ { STB0899_UPKTERRORM, 0x00 },
+ { STB0899_UPKTERRORL, 0x00 },
+ { 0xffff, 0xff },
+};
+
+static struct stb0899_config stb0899_config = {
+ .init_dev = pctv452e_init_dev,
+ .init_s2_demod = stb0899_s2_init_2,
+ .init_s1_demod = pctv452e_init_s1_demod,
+ .init_s2_fec = stb0899_s2_init_4,
+ .init_tst = stb0899_s1_init_5,
+
+ .demod_address = I2C_ADDR_STB0899, /* I2C Address */
+ .block_sync_mode = STB0899_SYNC_FORCED, /* ? */
+
+ .xtal_freq = 27000000, /* Assume Hz ? */
+ .inversion = IQ_SWAP_ON, /* ? */
+
+ .lo_clk = 76500000,
+ .hi_clk = 99000000,
+
+ .ts_output_mode = 0, /* Use parallel mode */
+ .clock_polarity = 0,
+ .data_clk_parity = 0,
+ .fec_mode = 0,
+
+ .esno_ave = STB0899_DVBS2_ESNO_AVE,
+ .esno_quant = STB0899_DVBS2_ESNO_QUANT,
+ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
+ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
+ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
+ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
+ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
+ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
+ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
+
+ .tuner_get_frequency = stb6100_get_frequency,
+ .tuner_set_frequency = stb6100_set_frequency,
+ .tuner_set_bandwidth = stb6100_set_bandwidth,
+ .tuner_get_bandwidth = stb6100_get_bandwidth,
+ .tuner_set_rfsiggain = NULL,
+
+ /* helper for switching LED green/orange */
+ .postproc = pctv45e_postproc
+};
+
+static struct stb6100_config stb6100_config = {
+ .tuner_address = I2C_ADDR_STB6100,
+ .refclock = 27000000
+};
+
+
+static struct i2c_algorithm pctv452e_i2c_algo = {
+ .master_xfer = pctv452e_i2c_xfer,
+ .functionality = pctv452e_i2c_func
+};
+
+static int pctv452e_frontend_attach(struct dvb_usb_adapter *a)
+{
+ struct usb_device_id *id;
+
+ a->fe_adap[0].fe = dvb_attach(stb0899_attach, &stb0899_config,
+ &a->dev->i2c_adap);
+ if (!a->fe_adap[0].fe)
+ return -ENODEV;
+ if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe,
+ &a->dev->i2c_adap)) == 0)
+ err("Cannot attach lnbp22\n");
+
+ id = a->dev->desc->warm_ids[0];
+ if (USB_VID_TECHNOTREND == id->idVendor
+ && USB_PID_TECHNOTREND_CONNECT_S2_3650_CI == id->idProduct)
+ /* Error ignored. */
+ tt3650_ci_init(a);
+
+ return 0;
+}
+
+static int pctv452e_tuner_attach(struct dvb_usb_adapter *a)
+{
+ if (!a->fe_adap[0].fe)
+ return -ENODEV;
+ if (dvb_attach(stb6100_attach, a->fe_adap[0].fe, &stb6100_config,
+ &a->dev->i2c_adap) == 0) {
+ err("%s failed\n", __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static struct usb_device_id pctv452e_usb_table[] = {
+ {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_452E)},
+ {USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_S2_3600)},
+ {USB_DEVICE(USB_VID_TECHNOTREND,
+ USB_PID_TECHNOTREND_CONNECT_S2_3650_CI)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, pctv452e_usb_table);
+
+static struct dvb_usb_device_properties pctv452e_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */
+ .usb_ctrl = DEVICE_SPECIFIC,
+
+ .size_of_priv = sizeof(struct pctv452e_state),
+
+ .power_ctrl = pctv452e_power_ctrl,
+
+ .rc.core = {
+ .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
+ .allowed_protos = RC_TYPE_UNKNOWN,
+ .rc_query = pctv452e_rc_query,
+ .rc_interval = 100,
+ },
+
+ .num_adapters = 1,
+ .adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+ .frontend_attach = pctv452e_frontend_attach,
+ .tuner_attach = pctv452e_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 4,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1
+ }
+ }
+ },
+ } },
+ } },
+
+ .i2c_algo = &pctv452e_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 1, /* allow generice rw function */
+
+ .num_device_descs = 1,
+ .devices = {
+ { .name = "PCTV HDTV USB",
+ .cold_ids = { NULL, NULL }, /* this is a warm only device */
+ .warm_ids = { &pctv452e_usb_table[0], NULL }
+ },
+ { 0 },
+ }
+};
+
+static struct dvb_usb_device_properties tt_connect_s2_3600_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */
+ .usb_ctrl = DEVICE_SPECIFIC,
+
+ .size_of_priv = sizeof(struct pctv452e_state),
+
+ .power_ctrl = pctv452e_power_ctrl,
+ .read_mac_address = pctv452e_read_mac_address,
+
+ .rc.core = {
+ .rc_codes = RC_MAP_TT_1500,
+ .allowed_protos = RC_TYPE_UNKNOWN,
+ .rc_query = pctv452e_rc_query,
+ .rc_interval = 100,
+ },
+
+ .num_adapters = 1,
+ .adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+ .frontend_attach = pctv452e_frontend_attach,
+ .tuner_attach = pctv452e_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 7,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1
+ }
+ }
+ },
+
+ } },
+ } },
+
+ .i2c_algo = &pctv452e_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 1, /* allow generic rw function*/
+
+ .num_device_descs = 2,
+ .devices = {
+ { .name = "Technotrend TT Connect S2-3600",
+ .cold_ids = { NULL, NULL }, /* this is a warm only device */
+ .warm_ids = { &pctv452e_usb_table[1], NULL }
+ },
+ { .name = "Technotrend TT Connect S2-3650-CI",
+ .cold_ids = { NULL, NULL },
+ .warm_ids = { &pctv452e_usb_table[2], NULL }
+ },
+ { 0 },
+ }
+};
+
+static void pctv452e_usb_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+
+ tt3650_ci_uninit(d);
+ dvb_usb_device_exit(intf);
+}
+
+static int pctv452e_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ if (0 == dvb_usb_device_init(intf, &pctv452e_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &tt_connect_s2_3600_properties,
+ THIS_MODULE, NULL, adapter_nr))
+ return 0;
+
+ return -ENODEV;
+}
+
+static struct usb_driver pctv452e_usb_driver = {
+ .name = "pctv452e",
+ .probe = pctv452e_usb_probe,
+ .disconnect = pctv452e_usb_disconnect,
+ .id_table = pctv452e_usb_table,
+};
+
+static int __init pctv452e_usb_init(void)
+{
+ int ret = usb_register(&pctv452e_usb_driver);
+ if (ret)
+ err("%s: usb_register failed! Error %d", __FILE__, ret);
+
+ return ret;
+}
+
+static void __exit pctv452e_usb_exit(void)
+{
+ usb_deregister(&pctv452e_usb_driver);
+}
+
+module_init(pctv452e_usb_init);
+module_exit(pctv452e_usb_exit);
+
+MODULE_AUTHOR("Dominik Kuhlen <dkuhlen@gmx.net>");
+MODULE_AUTHOR("Andre Weidemann <Andre.Weidemann@web.de>");
+MODULE_AUTHOR("Michael H. Schimek <mschimek@gmx.at>");
+MODULE_DESCRIPTION("Pinnacle PCTV HDTV USB DVB / TT connect S2-3600 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/technisat-usb2.c b/drivers/media/dvb/dvb-usb/technisat-usb2.c
index 08f8842ad280..0998fe961950 100644
--- a/drivers/media/dvb/dvb-usb/technisat-usb2.c
+++ b/drivers/media/dvb/dvb-usb/technisat-usb2.c
@@ -292,7 +292,7 @@ static void technisat_usb2_green_led_control(struct work_struct *work)
{
struct technisat_usb2_state *state =
container_of(work, struct technisat_usb2_state, green_led_work.work);
- struct dvb_frontend *fe = state->dev->adapter[0].fe;
+ struct dvb_frontend *fe = state->dev->adapter[0].fe_adap[0].fe;
if (state->power_state == 0)
goto schedule;
@@ -505,14 +505,14 @@ static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a)
struct usb_device *udev = a->dev->udev;
int ret;
- a->fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config,
+ a->fe_adap[0].fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config,
&a->dev->i2c_adap, STV090x_DEMODULATOR_0);
- if (a->fe) {
+ if (a->fe_adap[0].fe) {
struct stv6110x_devctl *ctl;
ctl = dvb_attach(stv6110x_attach,
- a->fe,
+ a->fe_adap[0].fe,
&technisat_usb2_stv6110x_config,
&a->dev->i2c_adap);
@@ -532,8 +532,8 @@ static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a)
/* call the init function once to initialize
tuner's clock output divider and demod's
master clock */
- if (a->fe->ops.init)
- a->fe->ops.init(a->fe);
+ if (a->fe_adap[0].fe->ops.init)
+ a->fe_adap[0].fe->ops.init(a->fe_adap[0].fe);
if (mutex_lock_interruptible(&a->dev->i2c_mutex) < 0)
return -EAGAIN;
@@ -548,20 +548,20 @@ static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a)
if (ret != 0)
err("could not set IF_CLK to external");
- a->fe->ops.set_voltage = technisat_usb2_set_voltage;
+ a->fe_adap[0].fe->ops.set_voltage = technisat_usb2_set_voltage;
/* if everything was successful assign a nice name to the frontend */
- strlcpy(a->fe->ops.info.name, a->dev->desc->name,
- sizeof(a->fe->ops.info.name));
+ strlcpy(a->fe_adap[0].fe->ops.info.name, a->dev->desc->name,
+ sizeof(a->fe_adap[0].fe->ops.info.name));
} else {
- dvb_frontend_detach(a->fe);
- a->fe = NULL;
+ dvb_frontend_detach(a->fe_adap[0].fe);
+ a->fe_adap[0].fe = NULL;
}
}
technisat_usb2_set_led_timer(a->dev, 1, 1);
- return a->fe == NULL ? -ENODEV : 0;
+ return a->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* Remote control */
@@ -697,6 +697,8 @@ static struct dvb_usb_device_properties technisat_usb2_devices = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = technisat_usb2_frontend_attach,
.stream = {
@@ -711,7 +713,7 @@ static struct dvb_usb_device_properties technisat_usb2_devices = {
}
}
},
-
+ }},
.size_of_priv = 0,
},
},
@@ -765,10 +767,8 @@ static void technisat_usb2_disconnect(struct usb_interface *intf)
/* work and stuff was only created when the device is is hot-state */
if (dev != NULL) {
struct technisat_usb2_state *state = dev->priv;
- if (state != NULL) {
+ if (state != NULL)
cancel_delayed_work_sync(&state->green_led_work);
- flush_scheduled_work();
- }
}
dvb_usb_device_exit(intf);
diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c
index 0d4709ff9cbb..ea4eab8b3965 100644
--- a/drivers/media/dvb/dvb-usb/ttusb2.c
+++ b/drivers/media/dvb/dvb-usb/ttusb2.c
@@ -30,18 +30,43 @@
#include "tda826x.h"
#include "tda10086.h"
#include "tda1002x.h"
+#include "tda10048.h"
#include "tda827x.h"
#include "lnbp21.h"
+/* CA */
+#include "dvb_ca_en50221.h"
/* debug */
static int dvb_usb_ttusb2_debug;
#define deb_info(args...) dprintk(dvb_usb_ttusb2_debug,0x01,args)
module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS);
+static int dvb_usb_ttusb2_debug_ci;
+module_param_named(debug_ci,dvb_usb_ttusb2_debug_ci, int, 0644);
+MODULE_PARM_DESC(debug_ci, "set debugging ci." DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+#define ci_dbg(format, arg...) \
+do { \
+ if (dvb_usb_ttusb2_debug_ci) \
+ printk(KERN_DEBUG DVB_USB_LOG_PREFIX \
+ ": %s " format "\n" , __func__, ## arg); \
+} while (0)
+
+enum {
+ TT3650_CMD_CI_TEST = 0x40,
+ TT3650_CMD_CI_RD_CTRL,
+ TT3650_CMD_CI_WR_CTRL,
+ TT3650_CMD_CI_RD_ATTR,
+ TT3650_CMD_CI_WR_ATTR,
+ TT3650_CMD_CI_RESET,
+ TT3650_CMD_CI_SET_VIDEO_PORT
+};
+
struct ttusb2_state {
+ struct dvb_ca_en50221 ca;
+ struct mutex ca_mutex;
u8 id;
u16 last_rc_key;
};
@@ -78,11 +103,260 @@ static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd,
return 0;
}
+/* ci */
+static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len)
+{
+ int ret;
+ u8 rx[60];/* (64 -4) */
+ ret = ttusb2_msg(d, cmd, data, write_len, rx, read_len);
+ if (!ret)
+ memcpy(data, rx, read_len);
+ return ret;
+}
+
+static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len)
+{
+ struct dvb_usb_device *d = ca->data;
+ struct ttusb2_state *state = d->priv;
+ int ret;
+
+ mutex_lock(&state->ca_mutex);
+ ret = tt3650_ci_msg(d, cmd, data, write_len, read_len);
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+{
+ u8 buf[3];
+ int ret = 0;
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3);
+
+ ci_dbg("%04x -> %d 0x%02x", address, ret, buf[2]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[2];
+}
+
+static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
+{
+ u8 buf[3];
+
+ ci_dbg("%d 0x%04x 0x%02x", slot, address, value);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+ buf[2] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3);
+}
+
+static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
+{
+ u8 buf[2];
+ int ret;
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = address & 3;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2);
+
+ ci_dbg("0x%02x -> %d 0x%02x", address, ret, buf[1]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[1];
+}
+
+static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
+{
+ u8 buf[2];
+
+ ci_dbg("%d 0x%02x 0x%02x", slot, address, value);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = address;
+ buf[1] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2);
+}
+
+static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, int slot, int enable)
+{
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%d %d", slot, enable);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = enable;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+ if (ret < 0)
+ return ret;
+
+ if (enable != buf[0]) {
+ err("CI not %sabled.", enable ? "en" : "dis");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, 0);
+}
+
+static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, 1);
+}
+
+static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = ca->data;
+ struct ttusb2_state *state = d->priv;
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%d", slot);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = 0;
+
+ mutex_lock(&state->ca_mutex);
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 1;
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 0; /* FTA */
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+
+ msleep(1100);
+
+ failed:
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ u8 buf[1];
+ int ret;
+
+ if (slot)
+ return -EINVAL;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1);
+ if (ret)
+ return ret;
+
+ if (1 == buf[0]) {
+ return DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+ }
+ return 0;
+}
+
+static void tt3650_ci_uninit(struct dvb_usb_device *d)
+{
+ struct ttusb2_state *state;
+
+ ci_dbg("");
+
+ if (NULL == d)
+ return;
+
+ state = d->priv;
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ dvb_ca_en50221_release(&state->ca);
+
+ memset(&state->ca, 0, sizeof(state->ca));
+}
+
+static int tt3650_ci_init(struct dvb_usb_adapter *a)
+{
+ struct dvb_usb_device *d = a->dev;
+ struct ttusb2_state *state = d->priv;
+ int ret;
+
+ ci_dbg("");
+
+ mutex_init(&state->ca_mutex);
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem;
+ state->ca.read_cam_control = tt3650_ci_read_cam_control;
+ state->ca.write_cam_control = tt3650_ci_write_cam_control;
+ state->ca.slot_reset = tt3650_ci_slot_reset;
+ state->ca.slot_shutdown = tt3650_ci_slot_shutdown;
+ state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable;
+ state->ca.poll_slot_status = tt3650_ci_poll_slot_status;
+ state->ca.data = d;
+
+ ret = dvb_ca_en50221_init(&a->dvb_adap,
+ &state->ca,
+ /* flags */ 0,
+ /* n_slots */ 1);
+ if (ret) {
+ err("Cannot initialize CI: Error %d.", ret);
+ memset(&state->ca, 0, sizeof(state->ca));
+ return ret;
+ }
+
+ info("CI initialized.");
+
+ return 0;
+}
+
static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
static u8 obuf[60], ibuf[60];
- int i,read;
+ int i, write_read, read;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
@@ -91,28 +365,35 @@ static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num
warn("more than 2 i2c messages at a time is not handled yet. TODO.");
for (i = 0; i < num; i++) {
- read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
+ write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
+ read = msg[i].flags & I2C_M_RD;
- obuf[0] = (msg[i].addr << 1) | read;
- obuf[1] = msg[i].len;
+ obuf[0] = (msg[i].addr << 1) | (write_read | read);
+ if (read)
+ obuf[1] = 0;
+ else
+ obuf[1] = msg[i].len;
/* read request */
- if (read)
+ if (write_read)
obuf[2] = msg[i+1].len;
+ else if (read)
+ obuf[2] = msg[i].len;
else
obuf[2] = 0;
- memcpy(&obuf[3],msg[i].buf,msg[i].len);
+ memcpy(&obuf[3], msg[i].buf, msg[i].len);
if (ttusb2_msg(d, CMD_I2C_XFER, obuf, msg[i].len+3, ibuf, obuf[2] + 3) < 0) {
err("i2c transfer failed.");
break;
}
- if (read) {
- memcpy(msg[i+1].buf,&ibuf[3],msg[i+1].len);
+ if (write_read) {
+ memcpy(msg[i+1].buf, &ibuf[3], msg[i+1].len);
i++;
- }
+ } else if (read)
+ memcpy(msg[i].buf, &ibuf[3], msg[i].len);
}
mutex_unlock(&d->i2c_mutex);
@@ -190,12 +471,31 @@ static struct tda10023_config tda10023_config = {
.deltaf = 0xa511,
};
+static struct tda10048_config tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_4000,
+ .dtv7_if_freq_khz = TDA10048_IF_4500,
+ .dtv8_if_freq_khz = TDA10048_IF_5000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .no_firmware = 1,
+ .set_pll = true ,
+ .pll_m = 5,
+ .pll_n = 3,
+ .pll_p = 0,
+};
+
+static struct tda827x_config tda827x_config = {
+ .config = 0,
+};
+
static int ttusb2_frontend_tda10086_attach(struct dvb_usb_adapter *adap)
{
if (usb_set_interface(adap->dev->udev,0,3) < 0)
err("set interface to alts=3 failed");
- if ((adap->fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) {
+ if ((adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) {
deb_info("TDA10086 attach failed\n");
return -ENODEV;
}
@@ -203,20 +503,57 @@ static int ttusb2_frontend_tda10086_attach(struct dvb_usb_adapter *adap)
return 0;
}
+static int ttusb2_ct3650_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+
+ return adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, enable);
+}
+
static int ttusb2_frontend_tda10023_attach(struct dvb_usb_adapter *adap)
{
if (usb_set_interface(adap->dev->udev, 0, 3) < 0)
err("set interface to alts=3 failed");
- if ((adap->fe = dvb_attach(tda10023_attach, &tda10023_config, &adap->dev->i2c_adap, 0x48)) == NULL) {
- deb_info("TDA10023 attach failed\n");
- return -ENODEV;
+
+ if (adap->fe_adap[0].fe == NULL) {
+ /* FE 0 DVB-C */
+ adap->fe_adap[0].fe = dvb_attach(tda10023_attach,
+ &tda10023_config, &adap->dev->i2c_adap, 0x48);
+
+ if (adap->fe_adap[0].fe == NULL) {
+ deb_info("TDA10023 attach failed\n");
+ return -ENODEV;
+ }
+ tt3650_ci_init(adap);
+ } else {
+ adap->fe_adap[1].fe = dvb_attach(tda10048_attach,
+ &tda10048_config, &adap->dev->i2c_adap);
+
+ if (adap->fe_adap[1].fe == NULL) {
+ deb_info("TDA10048 attach failed\n");
+ return -ENODEV;
+ }
+
+ /* tuner is behind TDA10023 I2C-gate */
+ adap->fe_adap[1].fe->ops.i2c_gate_ctrl = ttusb2_ct3650_i2c_gate_ctrl;
+
}
+
return 0;
}
static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap)
{
- if (dvb_attach(tda827x_attach, adap->fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL) {
+ struct dvb_frontend *fe;
+
+ /* MFE: select correct FE to attach tuner since that's called twice */
+ if (adap->fe_adap[1].fe == NULL)
+ fe = adap->fe_adap[0].fe;
+ else
+ fe = adap->fe_adap[1].fe;
+
+ /* attach tuner */
+ if (dvb_attach(tda827x_attach, fe, 0x61, &adap->dev->i2c_adap, &tda827x_config) == NULL) {
printk(KERN_ERR "%s: No tda827x found!\n", __func__);
return -ENODEV;
}
@@ -225,12 +562,12 @@ static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap)
static int ttusb2_tuner_tda826x_attach(struct dvb_usb_adapter *adap)
{
- if (dvb_attach(tda826x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) {
+ if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) {
deb_info("TDA8263 attach failed\n");
return -ENODEV;
}
- if (dvb_attach(lnbp21_attach, adap->fe, &adap->dev->i2c_adap, 0, 0) == NULL) {
+ if (dvb_attach(lnbp21_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0, 0) == NULL) {
deb_info("LNBP21 attach failed\n");
return -ENODEV;
}
@@ -242,6 +579,14 @@ static struct dvb_usb_device_properties ttusb2_properties;
static struct dvb_usb_device_properties ttusb2_properties_s2400;
static struct dvb_usb_device_properties ttusb2_properties_ct3650;
+static void ttusb2_usb_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+
+ tt3650_ci_uninit(d);
+ dvb_usb_device_exit(intf);
+}
+
static int ttusb2_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -277,6 +622,8 @@ static struct dvb_usb_device_properties ttusb2_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = NULL, // ttusb2_streaming_ctrl,
.frontend_attach = ttusb2_frontend_tda10086_attach,
@@ -295,6 +642,7 @@ static struct dvb_usb_device_properties ttusb2_properties = {
}
}
}
+ }},
}
},
@@ -329,6 +677,8 @@ static struct dvb_usb_device_properties ttusb2_properties_s2400 = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = NULL,
.frontend_attach = ttusb2_frontend_tda10086_attach,
@@ -347,6 +697,7 @@ static struct dvb_usb_device_properties ttusb2_properties_s2400 = {
}
}
}
+ }},
}
},
@@ -383,6 +734,27 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 2,
+ .fe = {{
+ .streaming_ctrl = NULL,
+
+ .frontend_attach = ttusb2_frontend_tda10023_attach,
+ .tuner_attach = ttusb2_tuner_tda827x_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1,
+ }
+ }
+ }
+ }, {
.streaming_ctrl = NULL,
.frontend_attach = ttusb2_frontend_tda10023_attach,
@@ -401,6 +773,7 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = {
}
}
}
+ }},
},
},
@@ -422,7 +795,7 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = {
static struct usb_driver ttusb2_driver = {
.name = "dvb_usb_ttusb2",
.probe = ttusb2_probe,
- .disconnect = dvb_usb_device_exit,
+ .disconnect = ttusb2_usb_disconnect,
.id_table = ttusb2_table,
};
diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c
index 118aab1a3e54..463673a5c2b2 100644
--- a/drivers/media/dvb/dvb-usb/umt-010.c
+++ b/drivers/media/dvb/dvb-usb/umt-010.c
@@ -60,14 +60,14 @@ static int umt_mt352_frontend_attach(struct dvb_usb_adapter *adap)
umt_config.demod_init = umt_mt352_demod_init;
umt_config.demod_address = 0xf;
- adap->fe = dvb_attach(mt352_attach, &umt_config, &adap->dev->i2c_adap);
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach, &umt_config, &adap->dev->i2c_adap);
return 0;
}
static int umt_tuner_attach (struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, DVB_PLL_TUA6034);
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_TUA6034);
return 0;
}
@@ -100,6 +100,8 @@ static struct dvb_usb_device_properties umt_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = dibusb2_0_streaming_ctrl,
.frontend_attach = umt_mt352_frontend_attach,
.tuner_attach = umt_tuner_attach,
@@ -115,7 +117,7 @@ static struct dvb_usb_device_properties umt_properties = {
}
}
},
-
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
diff --git a/drivers/media/dvb/dvb-usb/usb-urb.c b/drivers/media/dvb/dvb-usb/usb-urb.c
index 86d68933b6b4..d62ee0f5a165 100644
--- a/drivers/media/dvb/dvb-usb/usb-urb.c
+++ b/drivers/media/dvb/dvb-usb/usb-urb.c
@@ -148,7 +148,7 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream)
if (!stream->urb_list[i]) {
deb_mem("not enough memory for urb_alloc_urb!.\n");
for (j = 0; j < i; j++)
- usb_free_urb(stream->urb_list[i]);
+ usb_free_urb(stream->urb_list[j]);
return -ENOMEM;
}
usb_fill_bulk_urb( stream->urb_list[i], stream->udev,
@@ -181,7 +181,7 @@ static int usb_isoc_urb_init(struct usb_data_stream *stream)
if (!stream->urb_list[i]) {
deb_mem("not enough memory for urb_alloc_urb!\n");
for (j = 0; j < i; j++)
- usb_free_urb(stream->urb_list[i]);
+ usb_free_urb(stream->urb_list[j]);
return -ENOMEM;
}
diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c
index 54355f84a98f..45e31f224814 100644
--- a/drivers/media/dvb/dvb-usb/vp702x.c
+++ b/drivers/media/dvb/dvb-usb/vp702x.c
@@ -320,7 +320,7 @@ static int vp702x_frontend_attach(struct dvb_usb_adapter *adap)
vp702x_init_pid_filter(adap);
- adap->fe = vp702x_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = vp702x_fe_attach(adap->dev);
vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 1, 7, NULL, 0);
return 0;
@@ -383,6 +383,8 @@ static struct dvb_usb_device_properties vp702x_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS,
.streaming_ctrl = vp702x_streaming_ctrl,
@@ -399,6 +401,7 @@ static struct dvb_usb_device_properties vp702x_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct vp702x_adapter_state),
}
},
diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c
index 3db89e3cb0bb..90873af5682e 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.c
+++ b/drivers/media/dvb/dvb-usb/vp7045.c
@@ -214,7 +214,7 @@ static int vp7045_frontend_attach(struct dvb_usb_adapter *adap)
/* Dump the EEPROM */
/* vp7045_read_eeprom(d,buf, 255, FX2_ID_ADDR); */
- adap->fe = vp7045_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = vp7045_fe_attach(adap->dev);
return 0;
}
@@ -224,26 +224,8 @@ static struct dvb_usb_device_properties vp7045_properties;
static int vp7045_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- struct dvb_usb_device *d;
- int ret = dvb_usb_device_init(intf, &vp7045_properties,
- THIS_MODULE, &d, adapter_nr);
- if (ret)
- return ret;
-
- d->priv = kmalloc(20, GFP_KERNEL);
- if (!d->priv) {
- dvb_usb_device_exit(intf);
- return -ENOMEM;
- }
-
- return ret;
-}
-
-static void vp7045_usb_disconnect(struct usb_interface *intf)
-{
- struct dvb_usb_device *d = usb_get_intfdata(intf);
- kfree(d->priv);
- dvb_usb_device_exit(intf);
+ return dvb_usb_device_init(intf, &vp7045_properties,
+ THIS_MODULE, NULL, adapter_nr);
}
static struct usb_device_id vp7045_usb_table [] = {
@@ -258,11 +240,13 @@ MODULE_DEVICE_TABLE(usb, vp7045_usb_table);
static struct dvb_usb_device_properties vp7045_properties = {
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-vp7045-01.fw",
- .size_of_priv = sizeof(u8 *),
+ .size_of_priv = 20,
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = vp7045_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
@@ -275,6 +259,7 @@ static struct dvb_usb_device_properties vp7045_properties = {
}
}
},
+ }},
}
},
.power_ctrl = vp7045_power_ctrl,
@@ -305,7 +290,7 @@ static struct dvb_usb_device_properties vp7045_properties = {
static struct usb_driver vp7045_usb_driver = {
.name = "dvb_usb_vp7045",
.probe = vp7045_usb_probe,
- .disconnect = vp7045_usb_disconnect,
+ .disconnect = dvb_usb_device_exit,
.id_table = vp7045_usb_table,
};
diff --git a/drivers/media/dvb/dvb-usb/vp7045.h b/drivers/media/dvb/dvb-usb/vp7045.h
index 969688f85267..cf5ec46f8bb1 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.h
+++ b/drivers/media/dvb/dvb-usb/vp7045.h
@@ -36,9 +36,6 @@
#define Tuner_Power_OFF 0
#define GET_USB_SPEED 0x07
- #define USB_SPEED_LOW 0
- #define USB_SPEED_FULL 1
- #define USB_SPEED_HIGH 2
#define LOCK_TUNER_COMMAND 0x09
diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c
index 21c52e3b522e..489ae8245867 100644
--- a/drivers/media/dvb/firewire/firedtv-avc.c
+++ b/drivers/media/dvb/firewire/firedtv-avc.c
@@ -1208,7 +1208,7 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
if (r->response != AVC_RESPONSE_ACCEPTED) {
dev_err(fdtv->device,
"CA PMT failed with response 0x%x\n", r->response);
- ret = -EFAULT;
+ ret = -EACCES;
}
out:
mutex_unlock(&fdtv->avc_mutex);
diff --git a/drivers/media/dvb/firewire/firedtv-ci.c b/drivers/media/dvb/firewire/firedtv-ci.c
index 8ffb565f0704..e5ebdbfe8c19 100644
--- a/drivers/media/dvb/firewire/firedtv-ci.c
+++ b/drivers/media/dvb/firewire/firedtv-ci.c
@@ -45,11 +45,6 @@ static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat)
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;
@@ -65,12 +60,14 @@ static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg)
{
struct firedtv_tuner_status stat;
struct ca_slot_info *slot = arg;
+ int err;
- if (avc_tuner_status(fdtv, &stat))
- return -EFAULT;
+ err = avc_tuner_status(fdtv, &stat);
+ if (err)
+ return err;
if (slot->num != 0)
- return -EFAULT;
+ return -EACCES;
slot->type = CA_CI;
slot->flags = fdtv_get_ca_flags(&stat);
@@ -81,21 +78,21 @@ 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;
+ return avc_ca_app_info(fdtv, reply->msg, &reply->length);
}
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;
+ return avc_ca_info(fdtv, reply->msg, &reply->length);
}
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;
+ return avc_ca_get_mmi(fdtv, reply->msg, &reply->length);
}
static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg)
@@ -111,14 +108,15 @@ static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg)
err = fdtv_ca_info(fdtv, arg);
break;
default:
- if (avc_tuner_status(fdtv, &stat))
- err = -EFAULT;
- else if (stat.ca_mmi == 1)
+ err = avc_tuner_status(fdtv, &stat);
+ if (err)
+ break;
+ 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;
+ err = -EACCES;
}
}
fdtv->ca_last_command = 0;
@@ -141,7 +139,7 @@ static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
data_length = msg->msg[3];
}
- return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length) ? -EFAULT : 0;
+ return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
}
static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg)
@@ -170,7 +168,7 @@ static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg)
default:
dev_err(fdtv->device, "unhandled CA message 0x%08x\n",
fdtv->ca_last_command);
- err = -EFAULT;
+ err = -EACCES;
}
return err;
}
@@ -184,7 +182,7 @@ static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg)
switch (cmd) {
case CA_RESET:
- err = fdtv_ca_reset(fdtv);
+ err = avc_ca_reset(fdtv);
break;
case CA_GET_CAP:
err = fdtv_ca_get_caps(arg);
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 44b816f2601e..4a2d2e6c91ab 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -49,6 +49,27 @@ config DVB_STV6110x
help
A Silicon tuner that supports DVB-S and DVB-S2 modes
+comment "Multistandard (cable + terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_DRXK
+ tristate "Micronas DRXK based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ Micronas DRX-K DVB-C/T demodulator.
+
+ Say Y when you want to support this frontend.
+
+config DVB_TDA18271C2DD
+ tristate "NXP TDA18271C2 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ NXP TDA18271 silicon tuner.
+
+ Say Y when you want to support this tuner.
+
comment "DVB-S (satellite) frontends"
depends on DVB_CORE
@@ -215,6 +236,13 @@ config DVB_MB86A16
A DVB-S/DSS Direct Conversion reveiver.
Say Y when you want to support this frontend.
+config DVB_TDA10071
+ tristate "NXP TDA10071"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ Say Y when you want to support this frontend.
+
comment "DVB-T (terrestrial) frontends"
depends on DVB_CORE
@@ -579,6 +607,16 @@ config DVB_LNBP21
help
An SEC control chips.
+config DVB_LNBP22
+ tristate "LNBP22 SEC controllers"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ LNB power supply and control voltage
+ regulator chip with step-up converter
+ and I2C interface.
+ Say Y when you want to support this chip.
+
config DVB_ISL6405
tristate "ISL6405 SEC controller"
depends on DVB_CORE && I2C
@@ -600,6 +638,11 @@ config DVB_ISL6423
help
A SEC controller chip from Intersil
+config DVB_A8293
+ tristate "Allegro A8293"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+
config DVB_LGS8GL5
tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)"
depends on DVB_CORE && I2C
@@ -640,6 +683,14 @@ config DVB_IX2505V
help
A DVB-S tuner module. Say Y when you want to support this frontend.
+config DVB_IT913X_FE
+ tristate "it913x frontend and it9137 tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-T tuner module.
+ Say Y when you want to support this frontend.
+
comment "Tools to develop new frontends"
config DVB_DUMMY_FE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 2f3a6f736d64..f639f6781551 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -2,14 +2,15 @@
# Makefile for the kernel DVB frontend device drivers.
#
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
-EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+ccflags-y += -Idrivers/media/dvb/dvb-core/
+ccflags-y += -Idrivers/media/common/tuners/
stb0899-objs = stb0899_drv.o stb0899_algo.o
stv0900-objs = stv0900_core.o stv0900_sw.o
au8522-objs = au8522_dig.o au8522_decoder.o
drxd-objs = drxd_firm.o drxd_hard.o
cxd2820r-objs = cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o
+drxk-objs := drxk_hard.o
obj-$(CONFIG_DVB_PLL) += dvb-pll.o
obj-$(CONFIG_DVB_STV0299) += stv0299.o
@@ -51,6 +52,7 @@ obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o
obj-$(CONFIG_DVB_CX24123) += cx24123.o
obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
+obj-$(CONFIG_DVB_LNBP22) += lnbp22.o
obj-$(CONFIG_DVB_ISL6405) += isl6405.o
obj-$(CONFIG_DVB_ISL6421) += isl6421.o
obj-$(CONFIG_DVB_TDA10086) += tda10086.o
@@ -88,4 +90,9 @@ obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o
obj-$(CONFIG_DVB_IX2505V) += ix2505v.o
obj-$(CONFIG_DVB_STV0367) += stv0367.o
obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
+obj-$(CONFIG_DVB_DRXK) += drxk.o
+obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
+obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
+obj-$(CONFIG_DVB_A8293) += a8293.o
+obj-$(CONFIG_DVB_TDA10071) += tda10071.o
diff --git a/drivers/media/dvb/frontends/a8293.c b/drivers/media/dvb/frontends/a8293.c
new file mode 100644
index 000000000000..bb56497e940a
--- /dev/null
+++ b/drivers/media/dvb/frontends/a8293.c
@@ -0,0 +1,184 @@
+/*
+ * Allegro A8293 SEC driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dvb_frontend.h"
+#include "a8293.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define LOG_PREFIX "a8293"
+
+#undef dbg
+#define dbg(f, arg...) \
+ if (debug) \
+ printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+
+struct a8293_priv {
+ struct i2c_adapter *i2c;
+ const struct a8293_config *cfg;
+ u8 reg[2];
+};
+
+static int a8293_i2c(struct a8293_priv *priv, u8 *val, int len, bool rd)
+{
+ int ret;
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ if (rd)
+ msg[0].flags = I2C_M_RD;
+ else
+ msg[0].flags = 0;
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ warn("i2c failed=%d rd=%d", ret, rd);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+static int a8293_wr(struct a8293_priv *priv, u8 *val, int len)
+{
+ return a8293_i2c(priv, val, len, 0);
+}
+
+static int a8293_rd(struct a8293_priv *priv, u8 *val, int len)
+{
+ return a8293_i2c(priv, val, len, 1);
+}
+
+static int a8293_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t fe_sec_voltage)
+{
+ struct a8293_priv *priv = fe->sec_priv;
+ int ret;
+
+ dbg("%s: fe_sec_voltage=%d", __func__, fe_sec_voltage);
+
+ switch (fe_sec_voltage) {
+ case SEC_VOLTAGE_OFF:
+ /* ENB=0 */
+ priv->reg[0] = 0x10;
+ break;
+ case SEC_VOLTAGE_13:
+ /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/
+ priv->reg[0] = 0x31;
+ break;
+ case SEC_VOLTAGE_18:
+ /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/
+ priv->reg[0] = 0x38;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ };
+
+ ret = a8293_wr(priv, &priv->reg[0], 1);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static void a8293_release_sec(struct dvb_frontend *fe)
+{
+ dbg("%s:", __func__);
+
+ a8293_set_voltage(fe, SEC_VOLTAGE_OFF);
+
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
+}
+
+struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct a8293_config *cfg)
+{
+ int ret;
+ struct a8293_priv *priv = NULL;
+ u8 buf[2];
+
+ /* allocate memory for the internal priv */
+ priv = kzalloc(sizeof(struct a8293_priv), GFP_KERNEL);
+ if (priv == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ priv->cfg = cfg;
+ fe->sec_priv = priv;
+
+ /* check if the SEC is there */
+ ret = a8293_rd(priv, buf, 2);
+ if (ret)
+ goto err;
+
+ /* ENB=0 */
+ priv->reg[0] = 0x10;
+ ret = a8293_wr(priv, &priv->reg[1], 1);
+ if (ret)
+ goto err;
+
+ /* TMODE=0, TGATE=1 */
+ priv->reg[1] = 0x82;
+ ret = a8293_wr(priv, &priv->reg[1], 1);
+ if (ret)
+ goto err;
+
+ info("Allegro A8293 SEC attached.");
+
+ fe->ops.release_sec = a8293_release_sec;
+
+ /* override frontend ops */
+ fe->ops.set_voltage = a8293_set_voltage;
+
+ return fe;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(a8293_attach);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Allegro A8293 SEC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18212_priv.h b/drivers/media/dvb/frontends/a8293.h
index 9adff9356b73..ed29e5504f76 100644
--- a/drivers/media/common/tuners/tda18212_priv.h
+++ b/drivers/media/dvb/frontends/a8293.h
@@ -1,5 +1,5 @@
/*
- * NXP TDA18212HN silicon tuner driver
+ * Allegro A8293 SEC driver
*
* Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
*
@@ -18,27 +18,24 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef TDA18212_PRIV_H
-#define TDA18212_PRIV_H
+#ifndef A8293_H
+#define A8293_H
-#include "tda18212.h"
-
-#define LOG_PREFIX "tda18212"
-
-#undef dbg
-#define dbg(f, arg...) \
- if (debug) \
- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-struct tda18212_priv {
- struct tda18212_config *cfg;
- struct i2c_adapter *i2c;
+struct a8293_config {
+ u8 i2c_addr;
};
+#if defined(CONFIG_DVB_A8293) || \
+ (defined(CONFIG_DVB_A8293_MODULE) && defined(MODULE))
+extern struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct a8293_config *cfg);
+#else
+static inline struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct a8293_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
#endif
+
+#endif /* A8293_H */
diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c
index b537891a4cc9..2b248c12f404 100644
--- a/drivers/media/dvb/frontends/au8522_decoder.c
+++ b/drivers/media/dvb/frontends/au8522_decoder.c
@@ -692,7 +692,7 @@ static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
/* Interrogate the decoder to see if we are getting a real signal */
lock_status = au8522_readreg(state, 0x00);
if (lock_status == 0xa2)
- vt->signal = 0x01;
+ vt->signal = 0xffff;
else
vt->signal = 0x00;
diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c
index e9ee55592fd3..c341d57d5e81 100644
--- a/drivers/media/dvb/frontends/cx24113.c
+++ b/drivers/media/dvb/frontends/cx24113.c
@@ -31,8 +31,8 @@
static int debug;
-#define info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
-#define err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0)
+#define cx_info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
+#define cx_err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0)
#define dprintk(args...) \
do { \
@@ -341,7 +341,7 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
} while (N < 6 && R < 3);
if (N < 6) {
- err("strange frequency: N < 6\n");
+ cx_err("strange frequency: N < 6\n");
return;
}
F = freq_hz;
@@ -563,7 +563,7 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
kzalloc(sizeof(struct cx24113_state), GFP_KERNEL);
int rc;
if (state == NULL) {
- err("Unable to kzalloc\n");
+ cx_err("Unable to kzalloc\n");
goto error;
}
@@ -571,7 +571,7 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
state->config = config;
state->i2c = i2c;
- info("trying to detect myself\n");
+ cx_info("trying to detect myself\n");
/* making a dummy read, because of some expected troubles
* after power on */
@@ -579,24 +579,24 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
rc = cx24113_readreg(state, 0x00);
if (rc < 0) {
- info("CX24113 not found.\n");
+ cx_info("CX24113 not found.\n");
goto error;
}
state->rev = rc;
switch (rc) {
case 0x43:
- info("detected CX24113 variant\n");
+ cx_info("detected CX24113 variant\n");
break;
case REV_CX24113:
- info("successfully detected\n");
+ cx_info("successfully detected\n");
break;
default:
- err("unsupported device id: %x\n", state->rev);
+ cx_err("unsupported device id: %x\n", state->rev);
goto error;
}
state->ver = cx24113_readreg(state, 0x01);
- info("version: %x\n", state->ver);
+ cx_info("version: %x\n", state->ver);
/* create dvb_frontend */
memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops,
diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c
index 95c6465b87a1..ccd05255d527 100644
--- a/drivers/media/dvb/frontends/cx24116.c
+++ b/drivers/media/dvb/frontends/cx24116.c
@@ -1452,11 +1452,7 @@ tuned: /* Set/Reset B/W */
cmd.args[0x00] = CMD_BANDWIDTH;
cmd.args[0x01] = 0x00;
cmd.len = 0x02;
- ret = cx24116_cmd_execute(fe, &cmd);
- if (ret != 0)
- return ret;
-
- return ret;
+ return cx24116_cmd_execute(fe, &cmd);
}
static int cx24116_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *params,
diff --git a/drivers/media/dvb/frontends/cxd2820r.h b/drivers/media/dvb/frontends/cxd2820r.h
index ad17845123d9..03cab7b547fb 100644
--- a/drivers/media/dvb/frontends/cxd2820r.h
+++ b/drivers/media/dvb/frontends/cxd2820r.h
@@ -55,13 +55,13 @@ struct cxd2820r_config {
* Default: 0
* Values: 0, 1
*/
- int if_agc_polarity:1;
+ bool if_agc_polarity;
/* Spectrum inversion.
* Default: 0
* Values: 0, 1
*/
- int spec_inv:1;
+ bool spec_inv;
/* IFs for all used modes.
* Default: none, must set
@@ -93,9 +93,6 @@ extern struct dvb_frontend *cxd2820r_attach(
struct i2c_adapter *i2c,
struct dvb_frontend *fe
);
-extern struct i2c_adapter *cxd2820r_get_tuner_i2c_adapter(
- struct dvb_frontend *fe
-);
#else
static inline struct dvb_frontend *cxd2820r_attach(
const struct cxd2820r_config *config,
@@ -106,12 +103,6 @@ static inline struct dvb_frontend *cxd2820r_attach(
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
-static inline struct i2c_adapter *cxd2820r_get_tuner_i2c_adapter(
- struct dvb_frontend *fe
-)
-{
- return NULL;
-}
#endif
diff --git a/drivers/media/dvb/frontends/cxd2820r_c.c b/drivers/media/dvb/frontends/cxd2820r_c.c
index 3c07d400731d..b85f5011e344 100644
--- a/drivers/media/dvb/frontends/cxd2820r_c.c
+++ b/drivers/media/dvb/frontends/cxd2820r_c.c
@@ -335,4 +335,3 @@ int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,
return 0;
}
-
diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c
index 0779f69db793..036480f967b7 100644
--- a/drivers/media/dvb/frontends/cxd2820r_core.c
+++ b/drivers/media/dvb/frontends/cxd2820r_core.c
@@ -314,6 +314,8 @@ static int cxd2820r_set_frontend(struct dvb_frontend *fe,
} else if (c->delivery_system == SYS_DVBT2) {
/* DVB-T => DVB-T2 */
ret = cxd2820r_sleep_t(fe);
+ if (ret)
+ break;
ret = cxd2820r_set_frontend_t2(fe, p);
}
break;
@@ -324,6 +326,8 @@ static int cxd2820r_set_frontend(struct dvb_frontend *fe,
} else if (c->delivery_system == SYS_DVBT) {
/* DVB-T2 => DVB-T */
ret = cxd2820r_sleep_t2(fe);
+ if (ret)
+ break;
ret = cxd2820r_set_frontend_t(fe, p);
}
break;
@@ -723,62 +727,22 @@ static void cxd2820r_release(struct dvb_frontend *fe)
struct cxd2820r_priv *priv = fe->demodulator_priv;
dbg("%s", __func__);
- if (fe->ops.info.type == FE_OFDM) {
- i2c_del_adapter(&priv->tuner_i2c_adapter);
+ if (fe->ops.info.type == FE_OFDM)
kfree(priv);
- }
return;
}
-static u32 cxd2820r_tuner_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static int cxd2820r_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg msg[], int num)
-{
- struct cxd2820r_priv *priv = i2c_get_adapdata(i2c_adap);
- u8 obuf[msg[0].len + 2];
- struct i2c_msg msg2[2] = {
- {
- .addr = priv->cfg.i2c_address,
- .flags = 0,
- .len = sizeof(obuf),
- .buf = obuf,
- }, {
- .addr = priv->cfg.i2c_address,
- .flags = I2C_M_RD,
- .len = msg[1].len,
- .buf = msg[1].buf,
- }
- };
-
- obuf[0] = 0x09;
- obuf[1] = (msg[0].addr << 1);
- if (num == 2) { /* I2C read */
- obuf[1] = (msg[0].addr << 1) | I2C_M_RD; /* I2C RD flag */
- msg2[0].len = sizeof(obuf) - 1; /* maybe HW bug ? */
- }
- memcpy(&obuf[2], msg[0].buf, msg[0].len);
-
- return i2c_transfer(priv->i2c, msg2, num);
-}
-
-static struct i2c_algorithm cxd2820r_tuner_i2c_algo = {
- .master_xfer = cxd2820r_tuner_i2c_xfer,
- .functionality = cxd2820r_tuner_i2c_func,
-};
-
-struct i2c_adapter *cxd2820r_get_tuner_i2c_adapter(struct dvb_frontend *fe)
+static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct cxd2820r_priv *priv = fe->demodulator_priv;
- return &priv->tuner_i2c_adapter;
+ dbg("%s: %d", __func__, enable);
+
+ /* Bit 0 of reg 0xdb in bank 0x00 controls I2C repeater */
+ return cxd2820r_wr_reg_mask(priv, 0xdb, enable ? 1 : 0, 0x1);
}
-EXPORT_SYMBOL(cxd2820r_get_tuner_i2c_adapter);
-static struct dvb_frontend_ops cxd2820r_ops[2];
+static const struct dvb_frontend_ops cxd2820r_ops[2];
struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg,
struct i2c_adapter *i2c, struct dvb_frontend *fe)
@@ -817,18 +781,6 @@ struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg,
priv->fe[0].demodulator_priv = priv;
priv->fe[1].demodulator_priv = priv;
- /* create tuner i2c adapter */
- strlcpy(priv->tuner_i2c_adapter.name,
- "CXD2820R tuner I2C adapter",
- sizeof(priv->tuner_i2c_adapter.name));
- priv->tuner_i2c_adapter.algo = &cxd2820r_tuner_i2c_algo;
- priv->tuner_i2c_adapter.algo_data = NULL;
- i2c_set_adapdata(&priv->tuner_i2c_adapter, priv);
- if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) {
- err("tuner I2C bus could not be initialized");
- goto error;
- }
-
return &priv->fe[0];
} else {
@@ -844,7 +796,7 @@ error:
}
EXPORT_SYMBOL(cxd2820r_attach);
-static struct dvb_frontend_ops cxd2820r_ops[2] = {
+static const struct dvb_frontend_ops cxd2820r_ops[2] = {
{
/* DVB-T/T2 */
.info = {
@@ -869,6 +821,7 @@ static struct dvb_frontend_ops cxd2820r_ops[2] = {
.sleep = cxd2820r_sleep,
.get_tune_settings = cxd2820r_get_tune_settings,
+ .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl,
.get_frontend = cxd2820r_get_frontend,
@@ -897,6 +850,7 @@ static struct dvb_frontend_ops cxd2820r_ops[2] = {
.sleep = cxd2820r_sleep,
.get_tune_settings = cxd2820r_get_tune_settings,
+ .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl,
.set_frontend = cxd2820r_set_frontend,
.get_frontend = cxd2820r_get_frontend,
diff --git a/drivers/media/dvb/frontends/cxd2820r_priv.h b/drivers/media/dvb/frontends/cxd2820r_priv.h
index 25adbeefa6d3..95539134efdb 100644
--- a/drivers/media/dvb/frontends/cxd2820r_priv.h
+++ b/drivers/media/dvb/frontends/cxd2820r_priv.h
@@ -50,18 +50,17 @@ struct cxd2820r_priv {
struct i2c_adapter *i2c;
struct dvb_frontend fe[2];
struct cxd2820r_config cfg;
- struct i2c_adapter tuner_i2c_adapter;
struct mutex fe_lock; /* FE lock */
int active_fe:2; /* FE lock, -1=NONE, 0=DVB-T/T2, 1=DVB-C */
- int ber_running:1;
+ bool ber_running;
u8 bank[2];
u8 gpio[3];
fe_delivery_system_t delivery_system;
- int last_tune_failed:1; /* for switch between T and T2 tune */
+ bool last_tune_failed; /* for switch between T and T2 tune */
};
/* cxd2820r_core.c */
diff --git a/drivers/media/dvb/frontends/cxd2820r_t.c b/drivers/media/dvb/frontends/cxd2820r_t.c
index 6582564c930c..a04f9c810101 100644
--- a/drivers/media/dvb/frontends/cxd2820r_t.c
+++ b/drivers/media/dvb/frontends/cxd2820r_t.c
@@ -446,4 +446,3 @@ int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe,
return 0;
}
-
diff --git a/drivers/media/dvb/frontends/cxd2820r_t2.c b/drivers/media/dvb/frontends/cxd2820r_t2.c
index c47b35c8acf1..6548588309f7 100644
--- a/drivers/media/dvb/frontends/cxd2820r_t2.c
+++ b/drivers/media/dvb/frontends/cxd2820r_t2.c
@@ -420,4 +420,3 @@ int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe,
return 0;
}
-
diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb/frontends/dib0070.c
index 1d47d4da7d4c..dc1cb17a6ea7 100644
--- a/drivers/media/dvb/frontends/dib0070.c
+++ b/drivers/media/dvb/frontends/dib0070.c
@@ -27,6 +27,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
#include "dvb_frontend.h"
@@ -78,10 +79,18 @@ struct dib0070_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[3];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
-static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg)
+static u16 dib0070_read_reg(struct dib0070_state *state, u8 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = reg;
memset(state->msg, 0, 2 * sizeof(struct i2c_msg));
@@ -96,13 +105,23 @@ static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg)
if (i2c_transfer(state->i2c, state->msg, 2) != 2) {
printk(KERN_WARNING "DiB0070 I2C read failed\n");
- return 0;
- }
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ ret = 0;
+ } else
+ ret = (state->i2c_read_buffer[0] << 8)
+ | state->i2c_read_buffer[1];
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
state->i2c_write_buffer[0] = reg;
state->i2c_write_buffer[1] = val >> 8;
state->i2c_write_buffer[2] = val & 0xff;
@@ -115,9 +134,12 @@ static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
if (i2c_transfer(state->i2c, state->msg, 1) != 1) {
printk(KERN_WARNING "DiB0070 I2C write failed\n");
- return -EREMOTEIO;
- }
- return 0;
+ ret = -EREMOTEIO;
+ } else
+ ret = 0;
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
#define HARD_RESET(state) do { \
@@ -734,6 +756,7 @@ struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter
state->cfg = cfg;
state->i2c = i2c;
state->fe = fe;
+ mutex_init(&state->i2c_buffer_lock);
fe->tuner_priv = state;
if (dib0070_reset(fe) != 0)
diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c
index c9c935ae41e4..b174d1c78583 100644
--- a/drivers/media/dvb/frontends/dib0090.c
+++ b/drivers/media/dvb/frontends/dib0090.c
@@ -27,6 +27,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
#include "dvb_frontend.h"
@@ -196,6 +197,7 @@ struct dib0090_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[3];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
struct dib0090_fw_state {
@@ -208,10 +210,18 @@ struct dib0090_fw_state {
struct i2c_msg msg;
u8 i2c_write_buffer[2];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = reg;
memset(state->msg, 0, 2 * sizeof(struct i2c_msg));
@@ -226,14 +236,24 @@ static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg)
if (i2c_transfer(state->i2c, state->msg, 2) != 2) {
printk(KERN_WARNING "DiB0090 I2C read failed\n");
- return 0;
- }
+ ret = 0;
+ } else
+ ret = (state->i2c_read_buffer[0] << 8)
+ | state->i2c_read_buffer[1];
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
state->i2c_write_buffer[0] = reg & 0xff;
state->i2c_write_buffer[1] = val >> 8;
state->i2c_write_buffer[2] = val & 0xff;
@@ -246,13 +266,23 @@ static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val)
if (i2c_transfer(state->i2c, state->msg, 1) != 1) {
printk(KERN_WARNING "DiB0090 I2C write failed\n");
- return -EREMOTEIO;
- }
- return 0;
+ ret = -EREMOTEIO;
+ } else
+ ret = 0;
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = reg;
memset(&state->msg, 0, sizeof(struct i2c_msg));
@@ -262,13 +292,24 @@ static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg)
state->msg.len = 2;
if (i2c_transfer(state->i2c, &state->msg, 1) != 1) {
printk(KERN_WARNING "DiB0090 I2C read failed\n");
- return 0;
- }
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ ret = 0;
+ } else
+ ret = (state->i2c_read_buffer[0] << 8)
+ | state->i2c_read_buffer[1];
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
state->i2c_write_buffer[0] = val >> 8;
state->i2c_write_buffer[1] = val & 0xff;
@@ -279,9 +320,12 @@ static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val)
state->msg.len = 2;
if (i2c_transfer(state->i2c, &state->msg, 1) != 1) {
printk(KERN_WARNING "DiB0090 I2C write failed\n");
- return -EREMOTEIO;
- }
- return 0;
+ ret = -EREMOTEIO;
+ } else
+ ret = 0;
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
#define HARD_RESET(state) do { if (cfg->reset) { if (cfg->sleep) cfg->sleep(fe, 0); msleep(10); cfg->reset(fe, 1); msleep(10); cfg->reset(fe, 0); msleep(10); } } while (0)
@@ -2440,6 +2484,7 @@ struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapte
st->config = config;
st->i2c = i2c;
st->fe = fe;
+ mutex_init(&st->i2c_buffer_lock);
fe->tuner_priv = st;
if (config->wbd == NULL)
@@ -2471,6 +2516,7 @@ struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_ada
st->config = config;
st->i2c = i2c;
st->fe = fe;
+ mutex_init(&st->i2c_buffer_lock);
fe->tuner_priv = st;
if (dib0090_fw_reset_digital(fe, st->config) != 0)
diff --git a/drivers/media/dvb/frontends/dib3000.h b/drivers/media/dvb/frontends/dib3000.h
index ba917359fa65..404f63a6f26b 100644
--- a/drivers/media/dvb/frontends/dib3000.h
+++ b/drivers/media/dvb/frontends/dib3000.h
@@ -17,7 +17,7 @@
* Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
* sources, on which this driver (and the dvb-dibusb) are based.
*
- * see Documentation/dvb/README.dibusb for more information
+ * see Documentation/dvb/README.dvb-usb for more information
*
*/
diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c
index e80c59796368..437904cbf3e6 100644
--- a/drivers/media/dvb/frontends/dib3000mb.c
+++ b/drivers/media/dvb/frontends/dib3000mb.c
@@ -17,7 +17,7 @@
* Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
* sources, on which this driver (and the dvb-dibusb) are based.
*
- * see Documentation/dvb/README.dibusb for more information
+ * see Documentation/dvb/README.dvb-usb for more information
*
*/
diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c
index 79cb1c20df24..dbb76d75c932 100644
--- a/drivers/media/dvb/frontends/dib7000m.c
+++ b/drivers/media/dvb/frontends/dib7000m.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
#include "dvb_frontend.h"
@@ -55,6 +56,7 @@ struct dib7000m_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[4];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
enum dib7000m_power_mode {
@@ -69,6 +71,13 @@ enum dib7000m_power_mode {
static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = (reg >> 8) | 0x80;
state->i2c_write_buffer[1] = reg & 0xff;
@@ -85,11 +94,21 @@ static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg)
if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2)
dprintk("i2c read error on %d",reg);
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ mutex_unlock(&state->i2c_buffer_lock);
+
+ return ret;
}
static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
state->i2c_write_buffer[0] = (reg >> 8) & 0xff;
state->i2c_write_buffer[1] = reg & 0xff;
state->i2c_write_buffer[2] = (val >> 8) & 0xff;
@@ -101,7 +120,10 @@ static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val)
state->msg[0].buf = state->i2c_write_buffer;
state->msg[0].len = 4;
- return i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0;
+ ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ?
+ -EREMOTEIO : 0);
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static void dib7000m_write_tab(struct dib7000m_state *state, u16 *buf)
{
@@ -1385,6 +1407,7 @@ struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
demod = &st->demod;
demod->demodulator_priv = st;
memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops));
+ mutex_init(&st->i2c_buffer_lock);
st->timf_default = cfg->bw->timf;
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
index 0c9f40c2a251..ce8534ff142e 100644
--- a/drivers/media/dvb/frontends/dib7000p.c
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
#include "dvb_math.h"
#include "dvb_frontend.h"
@@ -68,6 +69,7 @@ struct dib7000p_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[4];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
enum dib7000p_power_mode {
@@ -81,6 +83,13 @@ static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff);
static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = reg >> 8;
state->i2c_write_buffer[1] = reg & 0xff;
@@ -97,11 +106,20 @@ static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg)
if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2)
dprintk("i2c read error on %d", reg);
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
state->i2c_write_buffer[0] = (reg >> 8) & 0xff;
state->i2c_write_buffer[1] = reg & 0xff;
state->i2c_write_buffer[2] = (val >> 8) & 0xff;
@@ -113,7 +131,10 @@ static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val)
state->msg[0].buf = state->i2c_write_buffer;
state->msg[0].len = 4;
- return i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0;
+ ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ?
+ -EREMOTEIO : 0);
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static void dib7000p_write_tab(struct dib7000p_state *state, u16 * buf)
@@ -1577,8 +1598,8 @@ int dib7000pc_detection(struct i2c_adapter *i2c_adap)
return -ENOMEM;
rx = kzalloc(2*sizeof(u8), GFP_KERNEL);
if (!rx) {
- goto rx_memory_error;
ret = -ENOMEM;
+ goto rx_memory_error;
}
msg[0].buf = tx;
@@ -1646,6 +1667,7 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau
return -ENOMEM;
dpst->i2c_adap = i2c;
+ mutex_init(&dpst->i2c_buffer_lock);
for (k = no_of_demods - 1; k >= 0; k--) {
dpst->cfg = cfg[k];
@@ -2324,6 +2346,7 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
demod = &st->demod;
demod->demodulator_priv = st;
memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops));
+ mutex_init(&st->i2c_buffer_lock);
dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */
@@ -2333,8 +2356,14 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
st->version = dib7000p_read_word(st, 897);
/* FIXME: make sure the dev.parent field is initialized, or else
- request_firmware() will hit an OOPS (this should be moved somewhere
- more common) */
+ request_firmware() will hit an OOPS (this should be moved somewhere
+ more common) */
+ st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent;
+
+ /* FIXME: make sure the dev.parent field is initialized, or else
+ request_firmware() will hit an OOPS (this should be moved somewhere
+ more common) */
+ st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent;
dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr);
diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c
index 7d2ea112ae2b..fe284d5292f5 100644
--- a/drivers/media/dvb/frontends/dib8000.c
+++ b/drivers/media/dvb/frontends/dib8000.c
@@ -10,6 +10,8 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
+
#include "dvb_math.h"
#include "dvb_frontend.h"
@@ -37,6 +39,7 @@ struct i2c_device {
u8 addr;
u8 *i2c_write_buffer;
u8 *i2c_read_buffer;
+ struct mutex *i2c_buffer_lock;
};
struct dib8000_state {
@@ -77,6 +80,7 @@ struct dib8000_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[4];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
enum dib8000_power_mode {
@@ -86,24 +90,39 @@ enum dib8000_power_mode {
static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg)
{
+ u16 ret;
struct i2c_msg msg[2] = {
- {.addr = i2c->addr >> 1, .flags = 0,
- .buf = i2c->i2c_write_buffer, .len = 2},
- {.addr = i2c->addr >> 1, .flags = I2C_M_RD,
- .buf = i2c->i2c_read_buffer, .len = 2},
+ {.addr = i2c->addr >> 1, .flags = 0, .len = 2},
+ {.addr = i2c->addr >> 1, .flags = I2C_M_RD, .len = 2},
};
+ if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
+ msg[0].buf = i2c->i2c_write_buffer;
msg[0].buf[0] = reg >> 8;
msg[0].buf[1] = reg & 0xff;
+ msg[1].buf = i2c->i2c_read_buffer;
if (i2c_transfer(i2c->adap, msg, 2) != 2)
dprintk("i2c read error on %d", reg);
- return (msg[1].buf[0] << 8) | msg[1].buf[1];
+ ret = (msg[1].buf[0] << 8) | msg[1].buf[1];
+ mutex_unlock(i2c->i2c_buffer_lock);
+ return ret;
}
static u16 dib8000_read_word(struct dib8000_state *state, u16 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = reg >> 8;
state->i2c_write_buffer[1] = reg & 0xff;
@@ -120,7 +139,10 @@ static u16 dib8000_read_word(struct dib8000_state *state, u16 reg)
if (i2c_transfer(state->i2c.adap, state->msg, 2) != 2)
dprintk("i2c read error on %d", reg);
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ mutex_unlock(&state->i2c_buffer_lock);
+
+ return ret;
}
static u32 dib8000_read32(struct dib8000_state *state, u16 reg)
@@ -135,22 +157,35 @@ static u32 dib8000_read32(struct dib8000_state *state, u16 reg)
static int dib8000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val)
{
- struct i2c_msg msg = {.addr = i2c->addr >> 1, .flags = 0,
- .buf = i2c->i2c_write_buffer, .len = 4};
+ struct i2c_msg msg = {.addr = i2c->addr >> 1, .flags = 0, .len = 4};
int ret = 0;
+ if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
+ msg.buf = i2c->i2c_write_buffer;
msg.buf[0] = (reg >> 8) & 0xff;
msg.buf[1] = reg & 0xff;
msg.buf[2] = (val >> 8) & 0xff;
msg.buf[3] = val & 0xff;
ret = i2c_transfer(i2c->adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+ mutex_unlock(i2c->i2c_buffer_lock);
return ret;
}
static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
state->i2c_write_buffer[0] = (reg >> 8) & 0xff;
state->i2c_write_buffer[1] = reg & 0xff;
state->i2c_write_buffer[2] = (val >> 8) & 0xff;
@@ -162,7 +197,11 @@ static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val)
state->msg[0].buf = state->i2c_write_buffer;
state->msg[0].len = 4;
- return i2c_transfer(state->i2c.adap, state->msg, 1) != 1 ? -EREMOTEIO : 0;
+ ret = (i2c_transfer(state->i2c.adap, state->msg, 1) != 1 ?
+ -EREMOTEIO : 0);
+ mutex_unlock(&state->i2c_buffer_lock);
+
+ return ret;
}
static const s16 coeff_2k_sb_1seg_dqpsk[8] = {
@@ -2434,8 +2473,15 @@ int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 defau
if (!client.i2c_read_buffer) {
dprintk("%s: not enough memory", __func__);
ret = -ENOMEM;
- goto error_memory;
+ goto error_memory_read;
+ }
+ client.i2c_buffer_lock = kzalloc(sizeof(struct mutex), GFP_KERNEL);
+ if (!client.i2c_buffer_lock) {
+ dprintk("%s: not enough memory", __func__);
+ ret = -ENOMEM;
+ goto error_memory_lock;
}
+ mutex_init(client.i2c_buffer_lock);
for (k = no_of_demods - 1; k >= 0; k--) {
/* designated i2c address */
@@ -2476,8 +2522,10 @@ int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 defau
}
error:
+ kfree(client.i2c_buffer_lock);
+error_memory_lock:
kfree(client.i2c_read_buffer);
-error_memory:
+error_memory_read:
kfree(client.i2c_write_buffer);
return ret;
@@ -2581,6 +2629,8 @@ struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, s
state->i2c.addr = i2c_addr;
state->i2c.i2c_write_buffer = state->i2c_write_buffer;
state->i2c.i2c_read_buffer = state->i2c_read_buffer;
+ mutex_init(&state->i2c_buffer_lock);
+ state->i2c.i2c_buffer_lock = &state->i2c_buffer_lock;
state->gpio_val = cfg->gpio_val;
state->gpio_dir = cfg->gpio_dir;
diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c
index a0855883b5ce..660f80661ed4 100644
--- a/drivers/media/dvb/frontends/dib9000.c
+++ b/drivers/media/dvb/frontends/dib9000.c
@@ -38,6 +38,15 @@ struct i2c_device {
#define DibInitLock(lock) mutex_init(lock)
#define DibFreeLock(lock)
+struct dib9000_pid_ctrl {
+#define DIB9000_PID_FILTER_CTRL 0
+#define DIB9000_PID_FILTER 1
+ u8 cmd;
+ u8 id;
+ u16 pid;
+ u8 onoff;
+};
+
struct dib9000_state {
struct i2c_device i2c;
@@ -99,6 +108,10 @@ struct dib9000_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[255];
u8 i2c_read_buffer[255];
+ DIB_LOCK demod_lock;
+ u8 get_frontend_internal;
+ struct dib9000_pid_ctrl pid_ctrl[10];
+ s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */
};
static const u32 fe_info[44] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1167,8 +1180,8 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe, struct dvb_frontend_p
DibAcquireLock(&state->platform.risc.mem_mbx_lock);
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
- goto error;
ret = -EIO;
+ goto error;
}
dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION,
@@ -1743,19 +1756,56 @@ EXPORT_SYMBOL(dib9000_set_gpio);
int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
{
struct dib9000_state *state = fe->demodulator_priv;
- u16 val = dib9000_read_word(state, 294 + 1) & 0xffef;
+ u16 val;
+ int ret;
+
+ if ((state->pid_ctrl_index != -2) && (state->pid_ctrl_index < 9)) {
+ /* postpone the pid filtering cmd */
+ dprintk("pid filter cmd postpone");
+ state->pid_ctrl_index++;
+ state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER_CTRL;
+ state->pid_ctrl[state->pid_ctrl_index].onoff = onoff;
+ return 0;
+ }
+
+ DibAcquireLock(&state->demod_lock);
+
+ val = dib9000_read_word(state, 294 + 1) & 0xffef;
val |= (onoff & 0x1) << 4;
dprintk("PID filter enabled %d", onoff);
- return dib9000_write_word(state, 294 + 1, val);
+ ret = dib9000_write_word(state, 294 + 1, val);
+ DibReleaseLock(&state->demod_lock);
+ return ret;
+
}
EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl);
int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
{
struct dib9000_state *state = fe->demodulator_priv;
+ int ret;
+
+ if (state->pid_ctrl_index != -2) {
+ /* postpone the pid filtering cmd */
+ dprintk("pid filter postpone");
+ if (state->pid_ctrl_index < 9) {
+ state->pid_ctrl_index++;
+ state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER;
+ state->pid_ctrl[state->pid_ctrl_index].id = id;
+ state->pid_ctrl[state->pid_ctrl_index].pid = pid;
+ state->pid_ctrl[state->pid_ctrl_index].onoff = onoff;
+ } else
+ dprintk("can not add any more pid ctrl cmd");
+ return 0;
+ }
+
+ DibAcquireLock(&state->demod_lock);
dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
- return dib9000_write_word(state, 300 + 1 + id, onoff ? (1 << 13) | pid : 0);
+ ret = dib9000_write_word(state, 300 + 1 + id,
+ onoff ? (1 << 13) | pid : 0);
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
EXPORT_SYMBOL(dib9000_fw_pid_filter);
@@ -1778,6 +1828,7 @@ static void dib9000_release(struct dvb_frontend *demod)
DibFreeLock(&state->platform.risc.mbx_lock);
DibFreeLock(&state->platform.risc.mem_lock);
DibFreeLock(&state->platform.risc.mem_mbx_lock);
+ DibFreeLock(&state->demod_lock);
dibx000_exit_i2c_master(&st->i2c_master);
i2c_del_adapter(&st->tuner_adap);
@@ -1795,14 +1846,19 @@ static int dib9000_sleep(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend;
- int ret;
+ int ret = 0;
+ DibAcquireLock(&state->demod_lock);
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]);
if (ret < 0)
- return ret;
+ goto error;
}
- return dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0);
+ ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0);
+
+error:
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
@@ -1816,7 +1872,10 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend, sub_index_frontend;
fe_status_t stat;
- int ret;
+ int ret = 0;
+
+ if (state->get_frontend_internal == 0)
+ DibAcquireLock(&state->demod_lock);
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
@@ -1846,14 +1905,15 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
state->fe[index_frontend]->dtv_property_cache.rolloff;
}
}
- return 0;
+ ret = 0;
+ goto return_value;
}
}
/* get the channel from master chip */
ret = dib9000_fw_get_channel(fe, fep);
if (ret != 0)
- return ret;
+ goto return_value;
/* synchronize the cache with the other frontends */
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
@@ -1866,8 +1926,12 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
state->fe[index_frontend]->dtv_property_cache.code_rate_LP = fe->dtv_property_cache.code_rate_LP;
state->fe[index_frontend]->dtv_property_cache.rolloff = fe->dtv_property_cache.rolloff;
}
+ ret = 0;
- return 0;
+return_value:
+ if (state->get_frontend_internal == 0)
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
static int dib9000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
@@ -1912,6 +1976,10 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
dprintk("dib9000: must specify bandwidth ");
return 0;
}
+
+ state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */
+ DibAcquireLock(&state->demod_lock);
+
fe->dtv_property_cache.delivery_system = SYS_DVBT;
/* set the master status */
@@ -1974,13 +2042,18 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
/* check the tune result */
if (exit_condition == 1) { /* tune failed */
dprintk("tune failed");
+ DibReleaseLock(&state->demod_lock);
+ /* tune failed; put all the pid filtering cmd to junk */
+ state->pid_ctrl_index = -1;
return 0;
}
dprintk("tune success on frontend%i", index_frontend_success);
/* synchronize all the channel cache */
+ state->get_frontend_internal = 1;
dib9000_get_frontend(state->fe[0], fep);
+ state->get_frontend_internal = 0;
/* retune the other frontends with the found channel */
channel_status.status = CHANNEL_STATUS_PARAMETERS_SET;
@@ -2025,6 +2098,28 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
/* turn off the diversity for the last frontend */
dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0);
+ DibReleaseLock(&state->demod_lock);
+ if (state->pid_ctrl_index >= 0) {
+ u8 index_pid_filter_cmd;
+ u8 pid_ctrl_index = state->pid_ctrl_index;
+
+ state->pid_ctrl_index = -2;
+ for (index_pid_filter_cmd = 0;
+ index_pid_filter_cmd <= pid_ctrl_index;
+ index_pid_filter_cmd++) {
+ if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER_CTRL)
+ dib9000_fw_pid_filter_ctrl(state->fe[0],
+ state->pid_ctrl[index_pid_filter_cmd].onoff);
+ else if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER)
+ dib9000_fw_pid_filter(state->fe[0],
+ state->pid_ctrl[index_pid_filter_cmd].id,
+ state->pid_ctrl[index_pid_filter_cmd].pid,
+ state->pid_ctrl[index_pid_filter_cmd].onoff);
+ }
+ }
+ /* do not postpone any more the pid filtering */
+ state->pid_ctrl_index = -2;
+
return 0;
}
@@ -2041,6 +2136,7 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
u8 index_frontend;
u16 lock = 0, lock_slave = 0;
+ DibAcquireLock(&state->demod_lock);
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
lock_slave |= dib9000_read_lock(state->fe[index_frontend]);
@@ -2059,6 +2155,8 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
if ((lock & 0x0008) || (lock_slave & 0x0008))
*stat |= FE_HAS_LOCK;
+ DibReleaseLock(&state->demod_lock);
+
return 0;
}
@@ -2066,10 +2164,15 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
{
struct dib9000_state *state = fe->demodulator_priv;
u16 *c;
+ int ret = 0;
+ DibAcquireLock(&state->demod_lock);
DibAcquireLock(&state->platform.risc.mem_mbx_lock);
- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
- return -EIO;
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ ret = -EIO;
+ goto error;
+ }
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR,
state->i2c_read_buffer, 16 * 2);
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
@@ -2077,7 +2180,10 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
c = (u16 *)state->i2c_read_buffer;
*ber = c[10] << 16 | c[11];
- return 0;
+
+error:
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
@@ -2086,7 +2192,9 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
u8 index_frontend;
u16 *c = (u16 *)state->i2c_read_buffer;
u16 val;
+ int ret = 0;
+ DibAcquireLock(&state->demod_lock);
*strength = 0;
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val);
@@ -2097,8 +2205,10 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
}
DibAcquireLock(&state->platform.risc.mem_mbx_lock);
- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
- return -EIO;
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ ret = -EIO;
+ goto error;
+ }
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
@@ -2107,7 +2217,10 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
*strength = 65535;
else
*strength += val;
- return 0;
+
+error:
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
static u32 dib9000_get_snr(struct dvb_frontend *fe)
@@ -2151,6 +2264,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
u8 index_frontend;
u32 snr_master;
+ DibAcquireLock(&state->demod_lock);
snr_master = dib9000_get_snr(fe);
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
snr_master += dib9000_get_snr(state->fe[index_frontend]);
@@ -2161,6 +2275,8 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
} else
*snr = 0;
+ DibReleaseLock(&state->demod_lock);
+
return 0;
}
@@ -2168,15 +2284,22 @@ static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
{
struct dib9000_state *state = fe->demodulator_priv;
u16 *c = (u16 *)state->i2c_read_buffer;
+ int ret = 0;
+ DibAcquireLock(&state->demod_lock);
DibAcquireLock(&state->platform.risc.mem_mbx_lock);
- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
- return -EIO;
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ ret = -EIO;
+ goto error;
+ }
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
*unc = c[12];
- return 0;
+
+error:
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, u8 first_addr)
@@ -2322,6 +2445,10 @@ struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, c
DibInitLock(&st->platform.risc.mbx_lock);
DibInitLock(&st->platform.risc.mem_lock);
DibInitLock(&st->platform.risc.mem_mbx_lock);
+ DibInitLock(&st->demod_lock);
+ st->get_frontend_internal = 0;
+
+ st->pid_ctrl_index = -2;
st->fe[0] = fe;
fe->demodulator_priv = st;
diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c
index dc5d17a67579..43be7238311e 100644
--- a/drivers/media/dvb/frontends/dibx000_common.c
+++ b/drivers/media/dvb/frontends/dibx000_common.c
@@ -1,4 +1,6 @@
#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
#include "dibx000_common.h"
@@ -10,6 +12,13 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
mst->i2c_write_buffer[0] = (reg >> 8) & 0xff;
mst->i2c_write_buffer[1] = reg & 0xff;
mst->i2c_write_buffer[2] = (val >> 8) & 0xff;
@@ -21,11 +30,21 @@ static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val)
mst->msg[0].buf = mst->i2c_write_buffer;
mst->msg[0].len = 4;
- return i2c_transfer(mst->i2c_adap, mst->msg, 1) != 1 ? -EREMOTEIO : 0;
+ ret = i2c_transfer(mst->i2c_adap, mst->msg, 1) != 1 ? -EREMOTEIO : 0;
+ mutex_unlock(&mst->i2c_buffer_lock);
+
+ return ret;
}
static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
mst->i2c_write_buffer[0] = reg >> 8;
mst->i2c_write_buffer[1] = reg & 0xff;
@@ -42,7 +61,10 @@ static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg)
if (i2c_transfer(mst->i2c_adap, mst->msg, 2) != 2)
dprintk("i2c read error on %d", reg);
- return (mst->i2c_read_buffer[0] << 8) | mst->i2c_read_buffer[1];
+ ret = (mst->i2c_read_buffer[0] << 8) | mst->i2c_read_buffer[1];
+ mutex_unlock(&mst->i2c_buffer_lock);
+
+ return ret;
}
static int dibx000_is_i2c_done(struct dibx000_i2c_master *mst)
@@ -257,6 +279,7 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msg[], int num)
{
struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
+ int ret;
if (num > 32) {
dprintk("%s: too much I2C message to be transmitted (%i).\
@@ -264,10 +287,15 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap,
return -ENOMEM;
}
- memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num));
-
dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_6_7);
+ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
+ memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num));
+
/* open the gate */
dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1);
mst->msg[0].addr = mst->i2c_addr;
@@ -282,7 +310,11 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap,
mst->msg[num + 1].buf = &mst->i2c_write_buffer[4];
mst->msg[num + 1].len = 4;
- return i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? num : -EIO;
+ ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ?
+ num : -EIO);
+
+ mutex_unlock(&mst->i2c_buffer_lock);
+ return ret;
}
static struct i2c_algorithm dibx000_i2c_gated_gpio67_algo = {
@@ -294,6 +326,7 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msg[], int num)
{
struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
+ int ret;
if (num > 32) {
dprintk("%s: too much I2C message to be transmitted (%i).\
@@ -301,10 +334,14 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap,
return -ENOMEM;
}
- memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num));
-
dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER);
+ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+ memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num));
+
/* open the gate */
dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1);
mst->msg[0].addr = mst->i2c_addr;
@@ -319,7 +356,10 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap,
mst->msg[num + 1].buf = &mst->i2c_write_buffer[4];
mst->msg[num + 1].len = 4;
- return i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? num : -EIO;
+ ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ?
+ num : -EIO);
+ mutex_unlock(&mst->i2c_buffer_lock);
+ return ret;
}
static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = {
@@ -390,8 +430,18 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap,
int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev,
struct i2c_adapter *i2c_adap, u8 i2c_addr)
{
- u8 tx[4];
- struct i2c_msg m = {.addr = i2c_addr >> 1,.buf = tx,.len = 4 };
+ int ret;
+
+ mutex_init(&mst->i2c_buffer_lock);
+ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+ memset(mst->msg, 0, sizeof(struct i2c_msg));
+ mst->msg[0].addr = i2c_addr >> 1;
+ mst->msg[0].flags = 0;
+ mst->msg[0].buf = mst->i2c_write_buffer;
+ mst->msg[0].len = 4;
mst->device_rev = device_rev;
mst->i2c_adap = i2c_adap;
@@ -431,9 +481,12 @@ int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev,
"DiBX000: could not initialize the master i2c_adapter\n");
/* initialize the i2c-master by closing the gate */
- dibx000_i2c_gate_ctrl(mst, tx, 0, 0);
+ dibx000_i2c_gate_ctrl(mst, mst->i2c_write_buffer, 0, 0);
+
+ ret = (i2c_transfer(i2c_adap, mst->msg, 1) == 1);
+ mutex_unlock(&mst->i2c_buffer_lock);
- return i2c_transfer(i2c_adap, &m, 1) == 1;
+ return ret;
}
EXPORT_SYMBOL(dibx000_init_i2c_master);
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h
index f031165c0459..5e011474be43 100644
--- a/drivers/media/dvb/frontends/dibx000_common.h
+++ b/drivers/media/dvb/frontends/dibx000_common.h
@@ -33,6 +33,7 @@ struct dibx000_i2c_master {
struct i2c_msg msg[34];
u8 i2c_write_buffer[8];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst,
diff --git a/drivers/media/dvb/frontends/drxd_hard.c b/drivers/media/dvb/frontends/drxd_hard.c
index ea4c1c361d2b..88e46f4cdbb2 100644
--- a/drivers/media/dvb/frontends/drxd_hard.c
+++ b/drivers/media/dvb/frontends/drxd_hard.c
@@ -28,7 +28,6 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/version.h>
#include <asm/div64.h>
#include "dvb_frontend.h"
@@ -233,7 +232,7 @@ static int i2c_read(struct i2c_adapter *adap,
return 0;
}
-inline u32 MulDiv32(u32 a, u32 b, u32 c)
+static inline u32 MulDiv32(u32 a, u32 b, u32 c)
{
u64 tmp64;
@@ -890,10 +889,15 @@ static int ReadIFAgc(struct drxd_state *state, u32 * pValue)
u32 R2 = state->if_agc_cfg.R2;
u32 R3 = state->if_agc_cfg.R3;
- u32 Vmax = (3300 * R2) / (R1 + R2);
- u32 Rpar = (R2 * R3) / (R3 + R2);
- u32 Vmin = (3300 * Rpar) / (R1 + Rpar);
- u32 Vout = Vmin + ((Vmax - Vmin) * Value) / 1024;
+ u32 Vmax, Rpar, Vmin, Vout;
+
+ if (R2 == 0 && (R1 == 0 || R3 == 0))
+ return 0;
+
+ Vmax = (3300 * R2) / (R1 + R2);
+ Rpar = (R2 * R3) / (R3 + R2);
+ Vmin = (3300 * Rpar) / (R1 + Rpar);
+ Vout = Vmin + ((Vmax - Vmin) * Value) / 1024;
*pValue = Vout;
}
@@ -910,14 +914,16 @@ static int load_firmware(struct drxd_state *state, const char *fw_name)
return -EIO;
}
- state->microcode = kzalloc(fw->size, GFP_KERNEL);
+ state->microcode = kmalloc(fw->size, GFP_KERNEL);
if (state->microcode == NULL) {
- printk(KERN_ERR "drxd: firmware load failure: nomemory\n");
+ release_firmware(fw);
+ printk(KERN_ERR "drxd: firmware load failure: no memory\n");
return -ENOMEM;
}
memcpy(state->microcode, fw->data, fw->size);
state->microcode_length = fw->size;
+ release_firmware(fw);
return 0;
}
@@ -925,16 +931,15 @@ static int DownloadMicrocode(struct drxd_state *state,
const u8 *pMCImage, u32 Length)
{
u8 *pSrc;
- u16 Flags;
u32 Address;
u16 nBlocks;
u16 BlockSize;
- u16 BlockCRC;
u32 offset = 0;
int i, status = 0;
pSrc = (u8 *) pMCImage;
- Flags = (pSrc[0] << 8) | pSrc[1];
+ /* We're not using Flags */
+ /* Flags = (pSrc[0] << 8) | pSrc[1]; */
pSrc += sizeof(u16);
offset += sizeof(u16);
nBlocks = (pSrc[0] << 8) | pSrc[1];
@@ -951,11 +956,13 @@ static int DownloadMicrocode(struct drxd_state *state,
pSrc += sizeof(u16);
offset += sizeof(u16);
- Flags = (pSrc[0] << 8) | pSrc[1];
+ /* We're not using Flags */
+ /* u16 Flags = (pSrc[0] << 8) | pSrc[1]; */
pSrc += sizeof(u16);
offset += sizeof(u16);
- BlockCRC = (pSrc[0] << 8) | pSrc[1];
+ /* We're not using BlockCRC */
+ /* u16 BlockCRC = (pSrc[0] << 8) | pSrc[1]; */
pSrc += sizeof(u16);
offset += sizeof(u16);
diff --git a/drivers/media/dvb/frontends/drxk.h b/drivers/media/dvb/frontends/drxk.h
new file mode 100644
index 000000000000..58baf419560c
--- /dev/null
+++ b/drivers/media/dvb/frontends/drxk.h
@@ -0,0 +1,47 @@
+#ifndef _DRXK_H_
+#define _DRXK_H_
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+/**
+ * struct drxk_config - Configure the initial parameters for DRX-K
+ *
+ * adr: I2C Address of the DRX-K
+ * single_master: Device is on the single master mode
+ * no_i2c_bridge: Don't switch the I2C bridge to talk with tuner
+ * antenna_gpio: GPIO bit used to control the antenna
+ * antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1
+ * means that 1=DVBC, 0 = DVBT. Zero means the opposite.
+ * microcode_name: Name of the firmware file with the microcode
+ *
+ * On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
+ * UIO-3.
+ */
+struct drxk_config {
+ u8 adr;
+ bool single_master;
+ bool no_i2c_bridge;
+
+ bool antenna_dvbt;
+ u16 antenna_gpio;
+
+ const char *microcode_name;
+};
+
+#if defined(CONFIG_DVB_DRXK) || (defined(CONFIG_DVB_DRXK_MODULE) \
+ && defined(MODULE))
+extern struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+ struct i2c_adapter *i2c,
+ struct dvb_frontend **fe_t);
+#else
+static inline struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+ struct i2c_adapter *i2c,
+ struct dvb_frontend **fe_t)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c
new file mode 100644
index 000000000000..f6431ef827dc
--- /dev/null
+++ b/drivers/media/dvb/frontends/drxk_hard.c
@@ -0,0 +1,6462 @@
+/*
+ * drxk_hard: DRX-K DVB-C/T demodulator driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/version.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "drxk.h"
+#include "drxk_hard.h"
+
+static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode);
+static int PowerDownQAM(struct drxk_state *state);
+static int SetDVBTStandard(struct drxk_state *state,
+ enum OperationMode oMode);
+static int SetQAMStandard(struct drxk_state *state,
+ enum OperationMode oMode);
+static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
+ s32 tunerFreqOffset);
+static int SetDVBTStandard(struct drxk_state *state,
+ enum OperationMode oMode);
+static int DVBTStart(struct drxk_state *state);
+static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz,
+ s32 tunerFreqOffset);
+static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus);
+static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus);
+static int SwitchAntennaToQAM(struct drxk_state *state);
+static int SwitchAntennaToDVBT(struct drxk_state *state);
+
+static bool IsDVBT(struct drxk_state *state)
+{
+ return state->m_OperationMode == OM_DVBT;
+}
+
+static bool IsQAM(struct drxk_state *state)
+{
+ return state->m_OperationMode == OM_QAM_ITU_A ||
+ state->m_OperationMode == OM_QAM_ITU_B ||
+ state->m_OperationMode == OM_QAM_ITU_C;
+}
+
+bool IsA1WithPatchCode(struct drxk_state *state)
+{
+ return state->m_DRXK_A1_PATCH_CODE;
+}
+
+bool IsA1WithRomCode(struct drxk_state *state)
+{
+ return state->m_DRXK_A1_ROM_CODE;
+}
+
+#define NOA1ROM 0
+
+#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0)
+#define DRXDAP_FASI_LONG_FORMAT(addr) (((addr) & 0xFC30FF80) != 0)
+
+#define DEFAULT_MER_83 165
+#define DEFAULT_MER_93 250
+
+#ifndef DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH
+#define DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH (0x02)
+#endif
+
+#ifndef DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH
+#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03)
+#endif
+
+#ifndef DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH
+#define DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH (0x06)
+#endif
+
+#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700
+#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500
+
+#ifndef DRXK_KI_RAGC_ATV
+#define DRXK_KI_RAGC_ATV 4
+#endif
+#ifndef DRXK_KI_IAGC_ATV
+#define DRXK_KI_IAGC_ATV 6
+#endif
+#ifndef DRXK_KI_DAGC_ATV
+#define DRXK_KI_DAGC_ATV 7
+#endif
+
+#ifndef DRXK_KI_RAGC_QAM
+#define DRXK_KI_RAGC_QAM 3
+#endif
+#ifndef DRXK_KI_IAGC_QAM
+#define DRXK_KI_IAGC_QAM 4
+#endif
+#ifndef DRXK_KI_DAGC_QAM
+#define DRXK_KI_DAGC_QAM 7
+#endif
+#ifndef DRXK_KI_RAGC_DVBT
+#define DRXK_KI_RAGC_DVBT (IsA1WithPatchCode(state) ? 3 : 2)
+#endif
+#ifndef DRXK_KI_IAGC_DVBT
+#define DRXK_KI_IAGC_DVBT (IsA1WithPatchCode(state) ? 4 : 2)
+#endif
+#ifndef DRXK_KI_DAGC_DVBT
+#define DRXK_KI_DAGC_DVBT (IsA1WithPatchCode(state) ? 10 : 7)
+#endif
+
+#ifndef DRXK_AGC_DAC_OFFSET
+#define DRXK_AGC_DAC_OFFSET (0x800)
+#endif
+
+#ifndef DRXK_BANDWIDTH_8MHZ_IN_HZ
+#define DRXK_BANDWIDTH_8MHZ_IN_HZ (0x8B8249L)
+#endif
+
+#ifndef DRXK_BANDWIDTH_7MHZ_IN_HZ
+#define DRXK_BANDWIDTH_7MHZ_IN_HZ (0x7A1200L)
+#endif
+
+#ifndef DRXK_BANDWIDTH_6MHZ_IN_HZ
+#define DRXK_BANDWIDTH_6MHZ_IN_HZ (0x68A1B6L)
+#endif
+
+#ifndef DRXK_QAM_SYMBOLRATE_MAX
+#define DRXK_QAM_SYMBOLRATE_MAX (7233000)
+#endif
+
+#define DRXK_BL_ROM_OFFSET_TAPS_DVBT 56
+#define DRXK_BL_ROM_OFFSET_TAPS_ITU_A 64
+#define DRXK_BL_ROM_OFFSET_TAPS_ITU_C 0x5FE0
+#define DRXK_BL_ROM_OFFSET_TAPS_BG 24
+#define DRXK_BL_ROM_OFFSET_TAPS_DKILLP 32
+#define DRXK_BL_ROM_OFFSET_TAPS_NTSC 40
+#define DRXK_BL_ROM_OFFSET_TAPS_FM 48
+#define DRXK_BL_ROM_OFFSET_UCODE 0
+
+#define DRXK_BLC_TIMEOUT 100
+
+#define DRXK_BLCC_NR_ELEMENTS_TAPS 2
+#define DRXK_BLCC_NR_ELEMENTS_UCODE 6
+
+#define DRXK_BLDC_NR_ELEMENTS_TAPS 28
+
+#ifndef DRXK_OFDM_NE_NOTCH_WIDTH
+#define DRXK_OFDM_NE_NOTCH_WIDTH (4)
+#endif
+
+#define DRXK_QAM_SL_SIG_POWER_QAM16 (40960)
+#define DRXK_QAM_SL_SIG_POWER_QAM32 (20480)
+#define DRXK_QAM_SL_SIG_POWER_QAM64 (43008)
+#define DRXK_QAM_SL_SIG_POWER_QAM128 (20992)
+#define DRXK_QAM_SL_SIG_POWER_QAM256 (43520)
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+#define dprintk(level, fmt, arg...) do { \
+if (debug >= level) \
+ printk(KERN_DEBUG "drxk: %s" fmt, __func__, ## arg); \
+} while (0)
+
+
+static inline u32 MulDiv32(u32 a, u32 b, u32 c)
+{
+ u64 tmp64;
+
+ tmp64 = (u64) a * (u64) b;
+ do_div(tmp64, c);
+
+ return (u32) tmp64;
+}
+
+inline u32 Frac28a(u32 a, u32 c)
+{
+ int i = 0;
+ u32 Q1 = 0;
+ u32 R0 = 0;
+
+ R0 = (a % c) << 4; /* 32-28 == 4 shifts possible at max */
+ Q1 = a / c; /* integer part, only the 4 least significant bits
+ will be visible in the result */
+
+ /* division using radix 16, 7 nibbles in the result */
+ for (i = 0; i < 7; i++) {
+ Q1 = (Q1 << 4) | (R0 / c);
+ R0 = (R0 % c) << 4;
+ }
+ /* rounding */
+ if ((R0 >> 3) >= c)
+ Q1++;
+
+ return Q1;
+}
+
+static u32 Log10Times100(u32 x)
+{
+ static const u8 scale = 15;
+ static const u8 indexWidth = 5;
+ u8 i = 0;
+ u32 y = 0;
+ u32 d = 0;
+ u32 k = 0;
+ u32 r = 0;
+ /*
+ log2lut[n] = (1<<scale) * 200 * log2(1.0 + ((1.0/(1<<INDEXWIDTH)) * n))
+ 0 <= n < ((1<<INDEXWIDTH)+1)
+ */
+
+ static const u32 log2lut[] = {
+ 0, /* 0.000000 */
+ 290941, /* 290941.300628 */
+ 573196, /* 573196.476418 */
+ 847269, /* 847269.179851 */
+ 1113620, /* 1113620.489452 */
+ 1372674, /* 1372673.576986 */
+ 1624818, /* 1624817.752104 */
+ 1870412, /* 1870411.981536 */
+ 2109788, /* 2109787.962654 */
+ 2343253, /* 2343252.817465 */
+ 2571091, /* 2571091.461923 */
+ 2793569, /* 2793568.696416 */
+ 3010931, /* 3010931.055901 */
+ 3223408, /* 3223408.452106 */
+ 3431216, /* 3431215.635215 */
+ 3634553, /* 3634553.498355 */
+ 3833610, /* 3833610.244726 */
+ 4028562, /* 4028562.434393 */
+ 4219576, /* 4219575.925308 */
+ 4406807, /* 4406806.721144 */
+ 4590402, /* 4590401.736809 */
+ 4770499, /* 4770499.491025 */
+ 4947231, /* 4947230.734179 */
+ 5120719, /* 5120719.018555 */
+ 5291081, /* 5291081.217197 */
+ 5458428, /* 5458427.996830 */
+ 5622864, /* 5622864.249668 */
+ 5784489, /* 5784489.488298 */
+ 5943398, /* 5943398.207380 */
+ 6099680, /* 6099680.215452 */
+ 6253421, /* 6253420.939751 */
+ 6404702, /* 6404701.706649 */
+ 6553600, /* 6553600.000000 */
+ };
+
+
+ if (x == 0)
+ return 0;
+
+ /* Scale x (normalize) */
+ /* computing y in log(x/y) = log(x) - log(y) */
+ if ((x & ((0xffffffff) << (scale + 1))) == 0) {
+ for (k = scale; k > 0; k--) {
+ if (x & (((u32) 1) << scale))
+ break;
+ x <<= 1;
+ }
+ } else {
+ for (k = scale; k < 31; k++) {
+ if ((x & (((u32) (-1)) << (scale + 1))) == 0)
+ break;
+ x >>= 1;
+ }
+ }
+ /*
+ Now x has binary point between bit[scale] and bit[scale-1]
+ and 1.0 <= x < 2.0 */
+
+ /* correction for divison: log(x) = log(x/y)+log(y) */
+ y = k * ((((u32) 1) << scale) * 200);
+
+ /* remove integer part */
+ x &= ((((u32) 1) << scale) - 1);
+ /* get index */
+ i = (u8) (x >> (scale - indexWidth));
+ /* compute delta (x - a) */
+ d = x & ((((u32) 1) << (scale - indexWidth)) - 1);
+ /* compute log, multiplication (d* (..)) must be within range ! */
+ y += log2lut[i] +
+ ((d * (log2lut[i + 1] - log2lut[i])) >> (scale - indexWidth));
+ /* Conver to log10() */
+ y /= 108853; /* (log2(10) << scale) */
+ r = (y >> 1);
+ /* rounding */
+ if (y & ((u32) 1))
+ r++;
+ return r;
+}
+
+/****************************************************************************/
+/* I2C **********************************************************************/
+/****************************************************************************/
+
+static int i2c_read1(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+ struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1}
+ };
+
+ return i2c_transfer(adapter, msgs, 1);
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+{
+ int status;
+ struct i2c_msg msg = {
+ .addr = adr, .flags = 0, .buf = data, .len = len };
+
+ dprintk(3, ":");
+ if (debug > 2) {
+ int i;
+ for (i = 0; i < len; i++)
+ printk(KERN_CONT " %02x", data[i]);
+ printk(KERN_CONT "\n");
+ }
+ status = i2c_transfer(adap, &msg, 1);
+ if (status >= 0 && status != 1)
+ status = -EIO;
+
+ if (status < 0)
+ printk(KERN_ERR "drxk: i2c write error at addr 0x%02x\n", adr);
+
+ return status;
+}
+
+static int i2c_read(struct i2c_adapter *adap,
+ u8 adr, u8 *msg, int len, u8 *answ, int alen)
+{
+ int status;
+ struct i2c_msg msgs[2] = {
+ {.addr = adr, .flags = 0,
+ .buf = msg, .len = len},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = answ, .len = alen}
+ };
+
+ status = i2c_transfer(adap, msgs, 2);
+ if (status != 2) {
+ if (debug > 2)
+ printk(KERN_CONT ": ERROR!\n");
+ if (status >= 0)
+ status = -EIO;
+
+ printk(KERN_ERR "drxk: i2c read error at addr 0x%02x\n", adr);
+ return status;
+ }
+ if (debug > 2) {
+ int i;
+ dprintk(2, ": read from ");
+ for (i = 0; i < len; i++)
+ printk(KERN_CONT " %02x", msg[i]);
+ printk(KERN_CONT "Value = ");
+ for (i = 0; i < alen; i++)
+ printk(KERN_CONT " %02x", answ[i]);
+ printk(KERN_CONT "\n");
+ }
+ return 0;
+}
+
+static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags)
+{
+ int status;
+ u8 adr = state->demod_address, mm1[4], mm2[2], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm1[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm1[1] = ((reg >> 16) & 0xFF);
+ mm1[2] = ((reg >> 24) & 0xFF) | flags;
+ mm1[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm1[0] = ((reg << 1) & 0xFF);
+ mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
+ status = i2c_read(state->i2c, adr, mm1, len, mm2, 2);
+ if (status < 0)
+ return status;
+ if (data)
+ *data = mm2[0] | (mm2[1] << 8);
+
+ return 0;
+}
+
+static int read16(struct drxk_state *state, u32 reg, u16 *data)
+{
+ return read16_flags(state, reg, data, 0);
+}
+
+static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags)
+{
+ int status;
+ u8 adr = state->demod_address, mm1[4], mm2[4], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm1[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm1[1] = ((reg >> 16) & 0xFF);
+ mm1[2] = ((reg >> 24) & 0xFF) | flags;
+ mm1[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm1[0] = ((reg << 1) & 0xFF);
+ mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
+ status = i2c_read(state->i2c, adr, mm1, len, mm2, 4);
+ if (status < 0)
+ return status;
+ if (data)
+ *data = mm2[0] | (mm2[1] << 8) |
+ (mm2[2] << 16) | (mm2[3] << 24);
+
+ return 0;
+}
+
+static int read32(struct drxk_state *state, u32 reg, u32 *data)
+{
+ return read32_flags(state, reg, data, 0);
+}
+
+static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags)
+{
+ u8 adr = state->demod_address, mm[6], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm[1] = ((reg >> 16) & 0xFF);
+ mm[2] = ((reg >> 24) & 0xFF) | flags;
+ mm[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm[0] = ((reg << 1) & 0xFF);
+ mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ mm[len] = data & 0xff;
+ mm[len + 1] = (data >> 8) & 0xff;
+
+ dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags);
+ return i2c_write(state->i2c, adr, mm, len + 2);
+}
+
+static int write16(struct drxk_state *state, u32 reg, u16 data)
+{
+ return write16_flags(state, reg, data, 0);
+}
+
+static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags)
+{
+ u8 adr = state->demod_address, mm[8], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm[1] = ((reg >> 16) & 0xFF);
+ mm[2] = ((reg >> 24) & 0xFF) | flags;
+ mm[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm[0] = ((reg << 1) & 0xFF);
+ mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ mm[len] = data & 0xff;
+ mm[len + 1] = (data >> 8) & 0xff;
+ mm[len + 2] = (data >> 16) & 0xff;
+ mm[len + 3] = (data >> 24) & 0xff;
+ dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags);
+
+ return i2c_write(state->i2c, adr, mm, len + 4);
+}
+
+static int write32(struct drxk_state *state, u32 reg, u32 data)
+{
+ return write32_flags(state, reg, data, 0);
+}
+
+static int write_block(struct drxk_state *state, u32 Address,
+ const int BlockSize, const u8 pBlock[])
+{
+ int status = 0, BlkSize = BlockSize;
+ u8 Flags = 0;
+
+ if (state->single_master)
+ Flags |= 0xC0;
+
+ while (BlkSize > 0) {
+ int Chunk = BlkSize > state->m_ChunkSize ?
+ state->m_ChunkSize : BlkSize;
+ u8 *AdrBuf = &state->Chunk[0];
+ u32 AdrLength = 0;
+
+ if (DRXDAP_FASI_LONG_FORMAT(Address) || (Flags != 0)) {
+ AdrBuf[0] = (((Address << 1) & 0xFF) | 0x01);
+ AdrBuf[1] = ((Address >> 16) & 0xFF);
+ AdrBuf[2] = ((Address >> 24) & 0xFF);
+ AdrBuf[3] = ((Address >> 7) & 0xFF);
+ AdrBuf[2] |= Flags;
+ AdrLength = 4;
+ if (Chunk == state->m_ChunkSize)
+ Chunk -= 2;
+ } else {
+ AdrBuf[0] = ((Address << 1) & 0xFF);
+ AdrBuf[1] = (((Address >> 16) & 0x0F) |
+ ((Address >> 18) & 0xF0));
+ AdrLength = 2;
+ }
+ memcpy(&state->Chunk[AdrLength], pBlock, Chunk);
+ dprintk(2, "(0x%08x, 0x%02x)\n", Address, Flags);
+ if (debug > 1) {
+ int i;
+ if (pBlock)
+ for (i = 0; i < Chunk; i++)
+ printk(KERN_CONT " %02x", pBlock[i]);
+ printk(KERN_CONT "\n");
+ }
+ status = i2c_write(state->i2c, state->demod_address,
+ &state->Chunk[0], Chunk + AdrLength);
+ if (status < 0) {
+ printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n",
+ __func__, Address);
+ break;
+ }
+ pBlock += Chunk;
+ Address += (Chunk >> 1);
+ BlkSize -= Chunk;
+ }
+ return status;
+}
+
+#ifndef DRXK_MAX_RETRIES_POWERUP
+#define DRXK_MAX_RETRIES_POWERUP 20
+#endif
+
+int PowerUpDevice(struct drxk_state *state)
+{
+ int status;
+ u8 data = 0;
+ u16 retryCount = 0;
+
+ dprintk(1, "\n");
+
+ status = i2c_read1(state->i2c, state->demod_address, &data);
+ if (status < 0) {
+ do {
+ data = 0;
+ status = i2c_write(state->i2c, state->demod_address,
+ &data, 1);
+ msleep(10);
+ retryCount++;
+ if (status < 0)
+ continue;
+ status = i2c_read1(state->i2c, state->demod_address,
+ &data);
+ } while (status < 0 &&
+ (retryCount < DRXK_MAX_RETRIES_POWERUP));
+ if (status < 0 && retryCount >= DRXK_MAX_RETRIES_POWERUP)
+ goto error;
+ }
+
+ /* Make sure all clk domains are active */
+ status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_NONE);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+ /* Enable pll lock tests */
+ status = write16(state, SIO_CC_PLL_LOCK__A, 1);
+ if (status < 0)
+ goto error;
+
+ state->m_currentPowerMode = DRX_POWER_UP;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+
+static int init_state(struct drxk_state *state)
+{
+ /*
+ * FIXME: most (all?) of the values bellow should be moved into
+ * struct drxk_config, as they are probably board-specific
+ */
+ u32 ulVSBIfAgcMode = DRXK_AGC_CTRL_AUTO;
+ u32 ulVSBIfAgcOutputLevel = 0;
+ u32 ulVSBIfAgcMinLevel = 0;
+ u32 ulVSBIfAgcMaxLevel = 0x7FFF;
+ u32 ulVSBIfAgcSpeed = 3;
+
+ u32 ulVSBRfAgcMode = DRXK_AGC_CTRL_AUTO;
+ u32 ulVSBRfAgcOutputLevel = 0;
+ u32 ulVSBRfAgcMinLevel = 0;
+ u32 ulVSBRfAgcMaxLevel = 0x7FFF;
+ u32 ulVSBRfAgcSpeed = 3;
+ u32 ulVSBRfAgcTop = 9500;
+ u32 ulVSBRfAgcCutOffCurrent = 4000;
+
+ u32 ulATVIfAgcMode = DRXK_AGC_CTRL_AUTO;
+ u32 ulATVIfAgcOutputLevel = 0;
+ u32 ulATVIfAgcMinLevel = 0;
+ u32 ulATVIfAgcMaxLevel = 0;
+ u32 ulATVIfAgcSpeed = 3;
+
+ u32 ulATVRfAgcMode = DRXK_AGC_CTRL_OFF;
+ u32 ulATVRfAgcOutputLevel = 0;
+ u32 ulATVRfAgcMinLevel = 0;
+ u32 ulATVRfAgcMaxLevel = 0;
+ u32 ulATVRfAgcTop = 9500;
+ u32 ulATVRfAgcCutOffCurrent = 4000;
+ u32 ulATVRfAgcSpeed = 3;
+
+ u32 ulQual83 = DEFAULT_MER_83;
+ u32 ulQual93 = DEFAULT_MER_93;
+
+ u32 ulDVBTStaticTSClock = 1;
+ u32 ulDVBCStaticTSClock = 1;
+
+ u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
+ u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
+
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+ u32 ulGPIOCfg = 0x0113;
+ u32 ulSerialMode = 1;
+ u32 ulInvertTSClock = 0;
+ u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH;
+ u32 ulTSClockkStrength = DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH;
+ u32 ulDVBTBitrate = 50000000;
+ u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8;
+
+ u32 ulInsertRSByte = 0;
+
+ u32 ulRfMirror = 1;
+ u32 ulPowerDown = 0;
+
+ dprintk(1, "\n");
+
+ state->m_hasLNA = false;
+ state->m_hasDVBT = false;
+ state->m_hasDVBC = false;
+ state->m_hasATV = false;
+ state->m_hasOOB = false;
+ state->m_hasAudio = false;
+
+ state->m_ChunkSize = 124;
+
+ state->m_oscClockFreq = 0;
+ state->m_smartAntInverted = false;
+ state->m_bPDownOpenBridge = false;
+
+ /* real system clock frequency in kHz */
+ state->m_sysClockFreq = 151875;
+ /* Timing div, 250ns/Psys */
+ /* Timing div, = (delay (nano seconds) * sysclk (kHz))/ 1000 */
+ state->m_HICfgTimingDiv = ((state->m_sysClockFreq / 1000) *
+ HI_I2C_DELAY) / 1000;
+ /* Clipping */
+ if (state->m_HICfgTimingDiv > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M)
+ state->m_HICfgTimingDiv = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M;
+ state->m_HICfgWakeUpKey = (state->demod_address << 1);
+ /* port/bridge/power down ctrl */
+ state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
+
+ state->m_bPowerDown = (ulPowerDown != 0);
+
+ state->m_DRXK_A1_PATCH_CODE = false;
+ state->m_DRXK_A1_ROM_CODE = false;
+ state->m_DRXK_A2_ROM_CODE = false;
+ state->m_DRXK_A3_ROM_CODE = false;
+ state->m_DRXK_A2_PATCH_CODE = false;
+ state->m_DRXK_A3_PATCH_CODE = false;
+
+ /* Init AGC and PGA parameters */
+ /* VSB IF */
+ state->m_vsbIfAgcCfg.ctrlMode = (ulVSBIfAgcMode);
+ state->m_vsbIfAgcCfg.outputLevel = (ulVSBIfAgcOutputLevel);
+ state->m_vsbIfAgcCfg.minOutputLevel = (ulVSBIfAgcMinLevel);
+ state->m_vsbIfAgcCfg.maxOutputLevel = (ulVSBIfAgcMaxLevel);
+ state->m_vsbIfAgcCfg.speed = (ulVSBIfAgcSpeed);
+ state->m_vsbPgaCfg = 140;
+
+ /* VSB RF */
+ state->m_vsbRfAgcCfg.ctrlMode = (ulVSBRfAgcMode);
+ state->m_vsbRfAgcCfg.outputLevel = (ulVSBRfAgcOutputLevel);
+ state->m_vsbRfAgcCfg.minOutputLevel = (ulVSBRfAgcMinLevel);
+ state->m_vsbRfAgcCfg.maxOutputLevel = (ulVSBRfAgcMaxLevel);
+ state->m_vsbRfAgcCfg.speed = (ulVSBRfAgcSpeed);
+ state->m_vsbRfAgcCfg.top = (ulVSBRfAgcTop);
+ state->m_vsbRfAgcCfg.cutOffCurrent = (ulVSBRfAgcCutOffCurrent);
+ state->m_vsbPreSawCfg.reference = 0x07;
+ state->m_vsbPreSawCfg.usePreSaw = true;
+
+ state->m_Quality83percent = DEFAULT_MER_83;
+ state->m_Quality93percent = DEFAULT_MER_93;
+ if (ulQual93 <= 500 && ulQual83 < ulQual93) {
+ state->m_Quality83percent = ulQual83;
+ state->m_Quality93percent = ulQual93;
+ }
+
+ /* ATV IF */
+ state->m_atvIfAgcCfg.ctrlMode = (ulATVIfAgcMode);
+ state->m_atvIfAgcCfg.outputLevel = (ulATVIfAgcOutputLevel);
+ state->m_atvIfAgcCfg.minOutputLevel = (ulATVIfAgcMinLevel);
+ state->m_atvIfAgcCfg.maxOutputLevel = (ulATVIfAgcMaxLevel);
+ state->m_atvIfAgcCfg.speed = (ulATVIfAgcSpeed);
+
+ /* ATV RF */
+ state->m_atvRfAgcCfg.ctrlMode = (ulATVRfAgcMode);
+ state->m_atvRfAgcCfg.outputLevel = (ulATVRfAgcOutputLevel);
+ state->m_atvRfAgcCfg.minOutputLevel = (ulATVRfAgcMinLevel);
+ state->m_atvRfAgcCfg.maxOutputLevel = (ulATVRfAgcMaxLevel);
+ state->m_atvRfAgcCfg.speed = (ulATVRfAgcSpeed);
+ state->m_atvRfAgcCfg.top = (ulATVRfAgcTop);
+ state->m_atvRfAgcCfg.cutOffCurrent = (ulATVRfAgcCutOffCurrent);
+ state->m_atvPreSawCfg.reference = 0x04;
+ state->m_atvPreSawCfg.usePreSaw = true;
+
+
+ /* DVBT RF */
+ state->m_dvbtRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF;
+ state->m_dvbtRfAgcCfg.outputLevel = 0;
+ state->m_dvbtRfAgcCfg.minOutputLevel = 0;
+ state->m_dvbtRfAgcCfg.maxOutputLevel = 0xFFFF;
+ state->m_dvbtRfAgcCfg.top = 0x2100;
+ state->m_dvbtRfAgcCfg.cutOffCurrent = 4000;
+ state->m_dvbtRfAgcCfg.speed = 1;
+
+
+ /* DVBT IF */
+ state->m_dvbtIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO;
+ state->m_dvbtIfAgcCfg.outputLevel = 0;
+ state->m_dvbtIfAgcCfg.minOutputLevel = 0;
+ state->m_dvbtIfAgcCfg.maxOutputLevel = 9000;
+ state->m_dvbtIfAgcCfg.top = 13424;
+ state->m_dvbtIfAgcCfg.cutOffCurrent = 0;
+ state->m_dvbtIfAgcCfg.speed = 3;
+ state->m_dvbtIfAgcCfg.FastClipCtrlDelay = 30;
+ state->m_dvbtIfAgcCfg.IngainTgtMax = 30000;
+ /* state->m_dvbtPgaCfg = 140; */
+
+ state->m_dvbtPreSawCfg.reference = 4;
+ state->m_dvbtPreSawCfg.usePreSaw = false;
+
+ /* QAM RF */
+ state->m_qamRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF;
+ state->m_qamRfAgcCfg.outputLevel = 0;
+ state->m_qamRfAgcCfg.minOutputLevel = 6023;
+ state->m_qamRfAgcCfg.maxOutputLevel = 27000;
+ state->m_qamRfAgcCfg.top = 0x2380;
+ state->m_qamRfAgcCfg.cutOffCurrent = 4000;
+ state->m_qamRfAgcCfg.speed = 3;
+
+ /* QAM IF */
+ state->m_qamIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO;
+ state->m_qamIfAgcCfg.outputLevel = 0;
+ state->m_qamIfAgcCfg.minOutputLevel = 0;
+ state->m_qamIfAgcCfg.maxOutputLevel = 9000;
+ state->m_qamIfAgcCfg.top = 0x0511;
+ state->m_qamIfAgcCfg.cutOffCurrent = 0;
+ state->m_qamIfAgcCfg.speed = 3;
+ state->m_qamIfAgcCfg.IngainTgtMax = 5119;
+ state->m_qamIfAgcCfg.FastClipCtrlDelay = 50;
+
+ state->m_qamPgaCfg = 140;
+ state->m_qamPreSawCfg.reference = 4;
+ state->m_qamPreSawCfg.usePreSaw = false;
+
+ state->m_OperationMode = OM_NONE;
+ state->m_DrxkState = DRXK_UNINITIALIZED;
+
+ /* MPEG output configuration */
+ state->m_enableMPEGOutput = true; /* If TRUE; enable MPEG ouput */
+ state->m_insertRSByte = false; /* If TRUE; insert RS byte */
+ state->m_enableParallel = true; /* If TRUE;
+ parallel out otherwise serial */
+ state->m_invertDATA = false; /* If TRUE; invert DATA signals */
+ state->m_invertERR = false; /* If TRUE; invert ERR signal */
+ state->m_invertSTR = false; /* If TRUE; invert STR signals */
+ state->m_invertVAL = false; /* If TRUE; invert VAL signals */
+ state->m_invertCLK = (ulInvertTSClock != 0); /* If TRUE; invert CLK signals */
+ state->m_DVBTStaticCLK = (ulDVBTStaticTSClock != 0);
+ state->m_DVBCStaticCLK = (ulDVBCStaticTSClock != 0);
+ /* If TRUE; static MPEG clockrate will be used;
+ otherwise clockrate will adapt to the bitrate of the TS */
+
+ state->m_DVBTBitrate = ulDVBTBitrate;
+ state->m_DVBCBitrate = ulDVBCBitrate;
+
+ state->m_TSDataStrength = (ulTSDataStrength & 0x07);
+ state->m_TSClockkStrength = (ulTSClockkStrength & 0x07);
+
+ /* Maximum bitrate in b/s in case static clockrate is selected */
+ state->m_mpegTsStaticBitrate = 19392658;
+ state->m_disableTEIhandling = false;
+
+ if (ulInsertRSByte)
+ state->m_insertRSByte = true;
+
+ state->m_MpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
+ if (ulMpegLockTimeOut < 10000)
+ state->m_MpegLockTimeOut = ulMpegLockTimeOut;
+ state->m_DemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
+ if (ulDemodLockTimeOut < 10000)
+ state->m_DemodLockTimeOut = ulDemodLockTimeOut;
+
+ /* QAM defaults */
+ state->m_Constellation = DRX_CONSTELLATION_AUTO;
+ state->m_qamInterleaveMode = DRXK_QAM_I12_J17;
+ state->m_fecRsPlen = 204 * 8; /* fecRsPlen annex A */
+ state->m_fecRsPrescale = 1;
+
+ state->m_sqiSpeed = DRXK_DVBT_SQI_SPEED_MEDIUM;
+ state->m_agcFastClipCtrlDelay = 0;
+
+ state->m_GPIOCfg = (ulGPIOCfg);
+
+ state->m_bPowerDown = false;
+ state->m_currentPowerMode = DRX_POWER_DOWN;
+
+ state->m_enableParallel = (ulSerialMode == 0);
+
+ state->m_rfmirror = (ulRfMirror == 0);
+ state->m_IfAgcPol = false;
+ return 0;
+}
+
+static int DRXX_Open(struct drxk_state *state)
+{
+ int status = 0;
+ u32 jtag = 0;
+ u16 bid = 0;
+ u16 key = 0;
+
+ dprintk(1, "\n");
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+ /* Check device id */
+ status = read16(state, SIO_TOP_COMM_KEY__A, &key);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
+ if (status < 0)
+ goto error;
+ status = read32(state, SIO_TOP_JTAGID_LO__A, &jtag);
+ if (status < 0)
+ goto error;
+ status = read16(state, SIO_PDR_UIO_IN_HI__A, &bid);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, key);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int GetDeviceCapabilities(struct drxk_state *state)
+{
+ u16 sioPdrOhwCfg = 0;
+ u32 sioTopJtagidLo = 0;
+ int status;
+ const char *spin = "";
+
+ dprintk(1, "\n");
+
+ /* driver 0.9.0 */
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA);
+ if (status < 0)
+ goto error;
+ status = read16(state, SIO_PDR_OHW_CFG__A, &sioPdrOhwCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+ if (status < 0)
+ goto error;
+
+ switch ((sioPdrOhwCfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) {
+ case 0:
+ /* ignore (bypass ?) */
+ break;
+ case 1:
+ /* 27 MHz */
+ state->m_oscClockFreq = 27000;
+ break;
+ case 2:
+ /* 20.25 MHz */
+ state->m_oscClockFreq = 20250;
+ break;
+ case 3:
+ /* 4 MHz */
+ state->m_oscClockFreq = 20250;
+ break;
+ default:
+ printk(KERN_ERR "drxk: Clock Frequency is unkonwn\n");
+ return -EINVAL;
+ }
+ /*
+ Determine device capabilities
+ Based on pinning v14
+ */
+ status = read32(state, SIO_TOP_JTAGID_LO__A, &sioTopJtagidLo);
+ if (status < 0)
+ goto error;
+ /* driver 0.9.0 */
+ switch ((sioTopJtagidLo >> 29) & 0xF) {
+ case 0:
+ state->m_deviceSpin = DRXK_SPIN_A1;
+ spin = "A1";
+ break;
+ case 2:
+ state->m_deviceSpin = DRXK_SPIN_A2;
+ spin = "A2";
+ break;
+ case 3:
+ state->m_deviceSpin = DRXK_SPIN_A3;
+ spin = "A3";
+ break;
+ default:
+ state->m_deviceSpin = DRXK_SPIN_UNKNOWN;
+ status = -EINVAL;
+ printk(KERN_ERR "drxk: Spin unknown\n");
+ goto error2;
+ }
+ switch ((sioTopJtagidLo >> 12) & 0xFF) {
+ case 0x13:
+ /* typeId = DRX3913K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = false;
+ state->m_hasAudio = false;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = true;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = false;
+ state->m_hasGPIO1 = false;
+ state->m_hasIRQN = false;
+ break;
+ case 0x15:
+ /* typeId = DRX3915K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = false;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = false;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x16:
+ /* typeId = DRX3916K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = false;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = false;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x18:
+ /* typeId = DRX3918K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = true;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = false;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x21:
+ /* typeId = DRX3921K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = true;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = true;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x23:
+ /* typeId = DRX3923K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = true;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = true;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x25:
+ /* typeId = DRX3925K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = true;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = true;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x26:
+ /* typeId = DRX3926K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = false;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = true;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ default:
+ printk(KERN_ERR "drxk: DeviceID 0x%02x not supported\n",
+ ((sioTopJtagidLo >> 12) & 0xFF));
+ status = -EINVAL;
+ goto error2;
+ }
+
+ printk(KERN_INFO
+ "drxk: detected a drx-39%02xk, spin %s, xtal %d.%03d MHz\n",
+ ((sioTopJtagidLo >> 12) & 0xFF), spin,
+ state->m_oscClockFreq / 1000,
+ state->m_oscClockFreq % 1000);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+error2:
+ return status;
+}
+
+static int HI_Command(struct drxk_state *state, u16 cmd, u16 *pResult)
+{
+ int status;
+ bool powerdown_cmd;
+
+ dprintk(1, "\n");
+
+ /* Write command */
+ status = write16(state, SIO_HI_RA_RAM_CMD__A, cmd);
+ if (status < 0)
+ goto error;
+ if (cmd == SIO_HI_RA_RAM_CMD_RESET)
+ msleep(1);
+
+ powerdown_cmd =
+ (bool) ((cmd == SIO_HI_RA_RAM_CMD_CONFIG) &&
+ ((state->m_HICfgCtrl) &
+ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) ==
+ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ);
+ if (powerdown_cmd == false) {
+ /* Wait until command rdy */
+ u32 retryCount = 0;
+ u16 waitCmd;
+
+ do {
+ msleep(1);
+ retryCount += 1;
+ status = read16(state, SIO_HI_RA_RAM_CMD__A,
+ &waitCmd);
+ } while ((status < 0) && (retryCount < DRXK_MAX_RETRIES)
+ && (waitCmd != 0));
+ if (status < 0)
+ goto error;
+ status = read16(state, SIO_HI_RA_RAM_RES__A, pResult);
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int HI_CfgCommand(struct drxk_state *state)
+{
+ int status;
+
+ dprintk(1, "\n");
+
+ mutex_lock(&state->mutex);
+
+ status = write16(state, SIO_HI_RA_RAM_PAR_6__A, state->m_HICfgTimeout);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_5__A, state->m_HICfgCtrl);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_4__A, state->m_HICfgWakeUpKey);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_3__A, state->m_HICfgBridgeDelay);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A, state->m_HICfgTimingDiv);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
+ if (status < 0)
+ goto error;
+ status = HI_Command(state, SIO_HI_RA_RAM_CMD_CONFIG, 0);
+ if (status < 0)
+ goto error;
+
+ state->m_HICfgCtrl &= ~SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+error:
+ mutex_unlock(&state->mutex);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int InitHI(struct drxk_state *state)
+{
+ dprintk(1, "\n");
+
+ state->m_HICfgWakeUpKey = (state->demod_address << 1);
+ state->m_HICfgTimeout = 0x96FF;
+ /* port/bridge/power down ctrl */
+ state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
+
+ return HI_CfgCommand(state);
+}
+
+static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable)
+{
+ int status = -1;
+ u16 sioPdrMclkCfg = 0;
+ u16 sioPdrMdxCfg = 0;
+
+ dprintk(1, "\n");
+
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+
+ /* MPEG TS pad configuration */
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA);
+ if (status < 0)
+ goto error;
+
+ if (mpegEnable == false) {
+ /* Set MPEG TS pads to inputmode */
+ status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MCLK_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD0_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ } else {
+ /* Enable MPEG output */
+ sioPdrMdxCfg =
+ ((state->m_TSDataStrength <<
+ SIO_PDR_MD0_CFG_DRIVE__B) | 0x0003);
+ sioPdrMclkCfg = ((state->m_TSClockkStrength <<
+ SIO_PDR_MCLK_CFG_DRIVE__B) |
+ 0x0003);
+
+ status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000); /* Disable */
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000); /* Disable */
+ if (status < 0)
+ goto error;
+ if (state->m_enableParallel == true) {
+ /* paralel -> enable MD1 to MD7 */
+ status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD2_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD3_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD4_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD5_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD6_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD7_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ } else {
+ sioPdrMdxCfg = ((state->m_TSDataStrength <<
+ SIO_PDR_MD0_CFG_DRIVE__B)
+ | 0x0003);
+ /* serial -> disable MD1 to MD7 */
+ status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ }
+ status = write16(state, SIO_PDR_MCLK_CFG__A, sioPdrMclkCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD0_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ }
+ /* Enable MB output over MPEG pads and ctl input */
+ status = write16(state, SIO_PDR_MON_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ /* Write nomagic word to enable pdr reg write */
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int MPEGTSDisable(struct drxk_state *state)
+{
+ dprintk(1, "\n");
+
+ return MPEGTSConfigurePins(state, false);
+}
+
+static int BLChainCmd(struct drxk_state *state,
+ u16 romOffset, u16 nrOfElements, u32 timeOut)
+{
+ u16 blStatus = 0;
+ int status;
+ unsigned long end;
+
+ dprintk(1, "\n");
+ mutex_lock(&state->mutex);
+ status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_CHAIN);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_CHAIN_ADDR__A, romOffset);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_CHAIN_LEN__A, nrOfElements);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
+ if (status < 0)
+ goto error;
+
+ end = jiffies + msecs_to_jiffies(timeOut);
+ do {
+ msleep(1);
+ status = read16(state, SIO_BL_STATUS__A, &blStatus);
+ if (status < 0)
+ goto error;
+ } while ((blStatus == 0x1) &&
+ ((time_is_after_jiffies(end))));
+
+ if (blStatus == 0x1) {
+ printk(KERN_ERR "drxk: SIO not ready\n");
+ status = -EINVAL;
+ goto error2;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+error2:
+ mutex_unlock(&state->mutex);
+ return status;
+}
+
+
+static int DownloadMicrocode(struct drxk_state *state,
+ const u8 pMCImage[], u32 Length)
+{
+ const u8 *pSrc = pMCImage;
+ u16 Flags;
+ u16 Drain;
+ u32 Address;
+ u16 nBlocks;
+ u16 BlockSize;
+ u16 BlockCRC;
+ u32 offset = 0;
+ u32 i;
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ /* down the drain (we don care about MAGIC_WORD) */
+ Drain = (pSrc[0] << 8) | pSrc[1];
+ pSrc += sizeof(u16);
+ offset += sizeof(u16);
+ nBlocks = (pSrc[0] << 8) | pSrc[1];
+ pSrc += sizeof(u16);
+ offset += sizeof(u16);
+
+ for (i = 0; i < nBlocks; i += 1) {
+ Address = (pSrc[0] << 24) | (pSrc[1] << 16) |
+ (pSrc[2] << 8) | pSrc[3];
+ pSrc += sizeof(u32);
+ offset += sizeof(u32);
+
+ BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16);
+ pSrc += sizeof(u16);
+ offset += sizeof(u16);
+
+ Flags = (pSrc[0] << 8) | pSrc[1];
+ pSrc += sizeof(u16);
+ offset += sizeof(u16);
+
+ BlockCRC = (pSrc[0] << 8) | pSrc[1];
+ pSrc += sizeof(u16);
+ offset += sizeof(u16);
+
+ if (offset + BlockSize > Length) {
+ printk(KERN_ERR "drxk: Firmware is corrupted.\n");
+ return -EINVAL;
+ }
+
+ status = write_block(state, Address, BlockSize, pSrc);
+ if (status < 0) {
+ printk(KERN_ERR "drxk: Error %d while loading firmware\n", status);
+ break;
+ }
+ pSrc += BlockSize;
+ offset += BlockSize;
+ }
+ return status;
+}
+
+static int DVBTEnableOFDMTokenRing(struct drxk_state *state, bool enable)
+{
+ int status;
+ u16 data = 0;
+ u16 desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_ON;
+ u16 desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED;
+ unsigned long end;
+
+ dprintk(1, "\n");
+
+ if (enable == false) {
+ desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF;
+ desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN;
+ }
+
+ status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
+ if (status >= 0 && data == desiredStatus) {
+ /* tokenring already has correct status */
+ return status;
+ }
+ /* Disable/enable dvbt tokenring bridge */
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, desiredCtrl);
+
+ end = jiffies + msecs_to_jiffies(DRXK_OFDM_TR_SHUTDOWN_TIMEOUT);
+ do {
+ status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
+ if ((status >= 0 && data == desiredStatus) || time_is_after_jiffies(end))
+ break;
+ msleep(1);
+ } while (1);
+ if (data != desiredStatus) {
+ printk(KERN_ERR "drxk: SIO not ready\n");
+ return -EINVAL;
+ }
+ return status;
+}
+
+static int MPEGTSStop(struct drxk_state *state)
+{
+ int status = 0;
+ u16 fecOcSncMode = 0;
+ u16 fecOcIprMode = 0;
+
+ dprintk(1, "\n");
+
+ /* Gracefull shutdown (byte boundaries) */
+ status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode);
+ if (status < 0)
+ goto error;
+ fecOcSncMode |= FEC_OC_SNC_MODE_SHUTDOWN__M;
+ status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode);
+ if (status < 0)
+ goto error;
+
+ /* Suppress MCLK during absence of data */
+ status = read16(state, FEC_OC_IPR_MODE__A, &fecOcIprMode);
+ if (status < 0)
+ goto error;
+ fecOcIprMode |= FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M;
+ status = write16(state, FEC_OC_IPR_MODE__A, fecOcIprMode);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int scu_command(struct drxk_state *state,
+ u16 cmd, u8 parameterLen,
+ u16 *parameter, u8 resultLen, u16 *result)
+{
+#if (SCU_RAM_PARAM_0__A - SCU_RAM_PARAM_15__A) != 15
+#error DRXK register mapping no longer compatible with this routine!
+#endif
+ u16 curCmd = 0;
+ int status = -EINVAL;
+ unsigned long end;
+ u8 buffer[34];
+ int cnt = 0, ii;
+ const char *p;
+ char errname[30];
+
+ dprintk(1, "\n");
+
+ if ((cmd == 0) || ((parameterLen > 0) && (parameter == NULL)) ||
+ ((resultLen > 0) && (result == NULL)))
+ goto error;
+
+ mutex_lock(&state->mutex);
+
+ /* assume that the command register is ready
+ since it is checked afterwards */
+ for (ii = parameterLen - 1; ii >= 0; ii -= 1) {
+ buffer[cnt++] = (parameter[ii] & 0xFF);
+ buffer[cnt++] = ((parameter[ii] >> 8) & 0xFF);
+ }
+ buffer[cnt++] = (cmd & 0xFF);
+ buffer[cnt++] = ((cmd >> 8) & 0xFF);
+
+ write_block(state, SCU_RAM_PARAM_0__A -
+ (parameterLen - 1), cnt, buffer);
+ /* Wait until SCU has processed command */
+ end = jiffies + msecs_to_jiffies(DRXK_MAX_WAITTIME);
+ do {
+ msleep(1);
+ status = read16(state, SCU_RAM_COMMAND__A, &curCmd);
+ if (status < 0)
+ goto error;
+ } while (!(curCmd == DRX_SCU_READY) && (time_is_after_jiffies(end)));
+ if (curCmd != DRX_SCU_READY) {
+ printk(KERN_ERR "drxk: SCU not ready\n");
+ status = -EIO;
+ goto error2;
+ }
+ /* read results */
+ if ((resultLen > 0) && (result != NULL)) {
+ s16 err;
+ int ii;
+
+ for (ii = resultLen - 1; ii >= 0; ii -= 1) {
+ status = read16(state, SCU_RAM_PARAM_0__A - ii, &result[ii]);
+ if (status < 0)
+ goto error;
+ }
+
+ /* Check if an error was reported by SCU */
+ err = (s16)result[0];
+ if (err >= 0)
+ goto error;
+
+ /* check for the known error codes */
+ switch (err) {
+ case SCU_RESULT_UNKCMD:
+ p = "SCU_RESULT_UNKCMD";
+ break;
+ case SCU_RESULT_UNKSTD:
+ p = "SCU_RESULT_UNKSTD";
+ break;
+ case SCU_RESULT_SIZE:
+ p = "SCU_RESULT_SIZE";
+ break;
+ case SCU_RESULT_INVPAR:
+ p = "SCU_RESULT_INVPAR";
+ break;
+ default: /* Other negative values are errors */
+ sprintf(errname, "ERROR: %d\n", err);
+ p = errname;
+ }
+ printk(KERN_ERR "drxk: %s while sending cmd 0x%04x with params:", p, cmd);
+ print_hex_dump_bytes("drxk: ", DUMP_PREFIX_NONE, buffer, cnt);
+ status = -EINVAL;
+ goto error2;
+ }
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+error2:
+ mutex_unlock(&state->mutex);
+ return status;
+}
+
+static int SetIqmAf(struct drxk_state *state, bool active)
+{
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ /* Configure IQM */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+
+ if (!active) {
+ data |= (IQM_AF_STDBY_STDBY_ADC_STANDBY
+ | IQM_AF_STDBY_STDBY_AMP_STANDBY
+ | IQM_AF_STDBY_STDBY_PD_STANDBY
+ | IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY
+ | IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY);
+ } else {
+ data &= ((~IQM_AF_STDBY_STDBY_ADC_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_AMP_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_PD_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY)
+ );
+ }
+ status = write16(state, IQM_AF_STDBY__A, data);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int CtrlPowerMode(struct drxk_state *state, enum DRXPowerMode *mode)
+{
+ int status = 0;
+ u16 sioCcPwdMode = 0;
+
+ dprintk(1, "\n");
+
+ /* Check arguments */
+ if (mode == NULL)
+ return -EINVAL;
+
+ switch (*mode) {
+ case DRX_POWER_UP:
+ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_NONE;
+ break;
+ case DRXK_POWER_DOWN_OFDM:
+ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OFDM;
+ break;
+ case DRXK_POWER_DOWN_CORE:
+ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_CLOCK;
+ break;
+ case DRXK_POWER_DOWN_PLL:
+ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_PLL;
+ break;
+ case DRX_POWER_DOWN:
+ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OSC;
+ break;
+ default:
+ /* Unknow sleep mode */
+ return -EINVAL;
+ }
+
+ /* If already in requested power mode, do nothing */
+ if (state->m_currentPowerMode == *mode)
+ return 0;
+
+ /* For next steps make sure to start from DRX_POWER_UP mode */
+ if (state->m_currentPowerMode != DRX_POWER_UP) {
+ status = PowerUpDevice(state);
+ if (status < 0)
+ goto error;
+ status = DVBTEnableOFDMTokenRing(state, true);
+ if (status < 0)
+ goto error;
+ }
+
+ if (*mode == DRX_POWER_UP) {
+ /* Restore analog & pin configuartion */
+ } else {
+ /* Power down to requested mode */
+ /* Backup some register settings */
+ /* Set pins with possible pull-ups connected
+ to them in input mode */
+ /* Analog power down */
+ /* ADC power down */
+ /* Power down device */
+ /* stop all comm_exec */
+ /* Stop and power down previous standard */
+ switch (state->m_OperationMode) {
+ case OM_DVBT:
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = PowerDownDVBT(state, false);
+ if (status < 0)
+ goto error;
+ break;
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = PowerDownQAM(state);
+ if (status < 0)
+ goto error;
+ break;
+ default:
+ break;
+ }
+ status = DVBTEnableOFDMTokenRing(state, false);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_PWD_MODE__A, sioCcPwdMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+
+ if (*mode != DRXK_POWER_DOWN_OFDM) {
+ state->m_HICfgCtrl |=
+ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+ status = HI_CfgCommand(state);
+ if (status < 0)
+ goto error;
+ }
+ }
+ state->m_currentPowerMode = *mode;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode)
+{
+ enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
+ u16 cmdResult = 0;
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ status = read16(state, SCU_COMM_EXEC__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == SCU_COMM_EXEC_ACTIVE) {
+ /* Send OFDM stop command */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+ /* Send OFDM reset command */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+ }
+
+ /* Reset datapath for OFDM, processors first */
+ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+ if (status < 0)
+ goto error;
+
+ /* powerdown AFE */
+ status = SetIqmAf(state, false);
+ if (status < 0)
+ goto error;
+
+ /* powerdown to OFDM mode */
+ if (setPowerMode) {
+ status = CtrlPowerMode(state, &powerMode);
+ if (status < 0)
+ goto error;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SetOperationMode(struct drxk_state *state,
+ enum OperationMode oMode)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /*
+ Stop and power down previous standard
+ TODO investigate total power down instead of partial
+ power down depending on "previous" standard.
+ */
+
+ /* disable HW lock indicator */
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+
+ /* Device is already at the required mode */
+ if (state->m_OperationMode == oMode)
+ return 0;
+
+ switch (state->m_OperationMode) {
+ /* OM_NONE was added for start up */
+ case OM_NONE:
+ break;
+ case OM_DVBT:
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = PowerDownDVBT(state, true);
+ if (status < 0)
+ goto error;
+ state->m_OperationMode = OM_NONE;
+ break;
+ case OM_QAM_ITU_A: /* fallthrough */
+ case OM_QAM_ITU_C:
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = PowerDownQAM(state);
+ if (status < 0)
+ goto error;
+ state->m_OperationMode = OM_NONE;
+ break;
+ case OM_QAM_ITU_B:
+ default:
+ status = -EINVAL;
+ goto error;
+ }
+
+ /*
+ Power up new standard
+ */
+ switch (oMode) {
+ case OM_DVBT:
+ state->m_OperationMode = oMode;
+ status = SetDVBTStandard(state, oMode);
+ if (status < 0)
+ goto error;
+ break;
+ case OM_QAM_ITU_A: /* fallthrough */
+ case OM_QAM_ITU_C:
+ state->m_OperationMode = oMode;
+ status = SetQAMStandard(state, oMode);
+ if (status < 0)
+ goto error;
+ break;
+ case OM_QAM_ITU_B:
+ default:
+ status = -EINVAL;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int Start(struct drxk_state *state, s32 offsetFreq,
+ s32 IntermediateFrequency)
+{
+ int status = -EINVAL;
+
+ u16 IFreqkHz;
+ s32 OffsetkHz = offsetFreq / 1000;
+
+ dprintk(1, "\n");
+ if (state->m_DrxkState != DRXK_STOPPED &&
+ state->m_DrxkState != DRXK_DTV_STARTED)
+ goto error;
+
+ state->m_bMirrorFreqSpect = (state->param.inversion == INVERSION_ON);
+
+ if (IntermediateFrequency < 0) {
+ state->m_bMirrorFreqSpect = !state->m_bMirrorFreqSpect;
+ IntermediateFrequency = -IntermediateFrequency;
+ }
+
+ switch (state->m_OperationMode) {
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ IFreqkHz = (IntermediateFrequency / 1000);
+ status = SetQAM(state, IFreqkHz, OffsetkHz);
+ if (status < 0)
+ goto error;
+ state->m_DrxkState = DRXK_DTV_STARTED;
+ break;
+ case OM_DVBT:
+ IFreqkHz = (IntermediateFrequency / 1000);
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = SetDVBT(state, IFreqkHz, OffsetkHz);
+ if (status < 0)
+ goto error;
+ status = DVBTStart(state);
+ if (status < 0)
+ goto error;
+ state->m_DrxkState = DRXK_DTV_STARTED;
+ break;
+ default:
+ break;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int ShutDown(struct drxk_state *state)
+{
+ dprintk(1, "\n");
+
+ MPEGTSStop(state);
+ return 0;
+}
+
+static int GetLockStatus(struct drxk_state *state, u32 *pLockStatus,
+ u32 Time)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ if (pLockStatus == NULL)
+ goto error;
+
+ *pLockStatus = NOT_LOCKED;
+
+ /* define the SCU command code */
+ switch (state->m_OperationMode) {
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_B:
+ case OM_QAM_ITU_C:
+ status = GetQAMLockStatus(state, pLockStatus);
+ break;
+ case OM_DVBT:
+ status = GetDVBTLockStatus(state, pLockStatus);
+ break;
+ default:
+ break;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int MPEGTSStart(struct drxk_state *state)
+{
+ int status;
+
+ u16 fecOcSncMode = 0;
+
+ /* Allow OC to sync again */
+ status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode);
+ if (status < 0)
+ goto error;
+ fecOcSncMode &= ~FEC_OC_SNC_MODE_SHUTDOWN__M;
+ status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_UNLOCK__A, 1);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int MPEGTSDtoInit(struct drxk_state *state)
+{
+ int status;
+
+ dprintk(1, "\n");
+
+ /* Rate integration settings */
+ status = write16(state, FEC_OC_RCN_CTL_STEP_LO__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_RCN_CTL_STEP_HI__A, 0x000C);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_RCN_GAIN__A, 0x000A);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_AVR_PARM_A__A, 0x0008);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_AVR_PARM_B__A, 0x0006);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_HI_MARGIN__A, 0x0680);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_LO_MARGIN__A, 0x0080);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_COUNT__A, 0x03F4);
+ if (status < 0)
+ goto error;
+
+ /* Additional configuration */
+ status = write16(state, FEC_OC_OCR_INVERT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_LWM__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_HWM__A, 12);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int MPEGTSDtoSetup(struct drxk_state *state,
+ enum OperationMode oMode)
+{
+ int status;
+
+ u16 fecOcRegMode = 0; /* FEC_OC_MODE register value */
+ u16 fecOcRegIprMode = 0; /* FEC_OC_IPR_MODE register value */
+ u16 fecOcDtoMode = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fecOcFctMode = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fecOcDtoPeriod = 2; /* FEC_OC_IPR_INVERT register value */
+ u16 fecOcDtoBurstLen = 188; /* FEC_OC_IPR_INVERT register value */
+ u32 fecOcRcnCtlRate = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fecOcTmdMode = 0;
+ u16 fecOcTmdIntUpdRate = 0;
+ u32 maxBitRate = 0;
+ bool staticCLK = false;
+
+ dprintk(1, "\n");
+
+ /* Check insertion of the Reed-Solomon parity bytes */
+ status = read16(state, FEC_OC_MODE__A, &fecOcRegMode);
+ if (status < 0)
+ goto error;
+ status = read16(state, FEC_OC_IPR_MODE__A, &fecOcRegIprMode);
+ if (status < 0)
+ goto error;
+ fecOcRegMode &= (~FEC_OC_MODE_PARITY__M);
+ fecOcRegIprMode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M);
+ if (state->m_insertRSByte == true) {
+ /* enable parity symbol forward */
+ fecOcRegMode |= FEC_OC_MODE_PARITY__M;
+ /* MVAL disable during parity bytes */
+ fecOcRegIprMode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M;
+ /* TS burst length to 204 */
+ fecOcDtoBurstLen = 204;
+ }
+
+ /* Check serial or parrallel output */
+ fecOcRegIprMode &= (~(FEC_OC_IPR_MODE_SERIAL__M));
+ if (state->m_enableParallel == false) {
+ /* MPEG data output is serial -> set ipr_mode[0] */
+ fecOcRegIprMode |= FEC_OC_IPR_MODE_SERIAL__M;
+ }
+
+ switch (oMode) {
+ case OM_DVBT:
+ maxBitRate = state->m_DVBTBitrate;
+ fecOcTmdMode = 3;
+ fecOcRcnCtlRate = 0xC00000;
+ staticCLK = state->m_DVBTStaticCLK;
+ break;
+ case OM_QAM_ITU_A: /* fallthrough */
+ case OM_QAM_ITU_C:
+ fecOcTmdMode = 0x0004;
+ fecOcRcnCtlRate = 0xD2B4EE; /* good for >63 Mb/s */
+ maxBitRate = state->m_DVBCBitrate;
+ staticCLK = state->m_DVBCStaticCLK;
+ break;
+ default:
+ status = -EINVAL;
+ } /* switch (standard) */
+ if (status < 0)
+ goto error;
+
+ /* Configure DTO's */
+ if (staticCLK) {
+ u32 bitRate = 0;
+
+ /* Rational DTO for MCLK source (static MCLK rate),
+ Dynamic DTO for optimal grouping
+ (avoid intra-packet gaps),
+ DTO offset enable to sync TS burst with MSTRT */
+ fecOcDtoMode = (FEC_OC_DTO_MODE_DYNAMIC__M |
+ FEC_OC_DTO_MODE_OFFSET_ENABLE__M);
+ fecOcFctMode = (FEC_OC_FCT_MODE_RAT_ENA__M |
+ FEC_OC_FCT_MODE_VIRT_ENA__M);
+
+ /* Check user defined bitrate */
+ bitRate = maxBitRate;
+ if (bitRate > 75900000UL) { /* max is 75.9 Mb/s */
+ bitRate = 75900000UL;
+ }
+ /* Rational DTO period:
+ dto_period = (Fsys / bitrate) - 2
+
+ Result should be floored,
+ to make sure >= requested bitrate
+ */
+ fecOcDtoPeriod = (u16) (((state->m_sysClockFreq)
+ * 1000) / bitRate);
+ if (fecOcDtoPeriod <= 2)
+ fecOcDtoPeriod = 0;
+ else
+ fecOcDtoPeriod -= 2;
+ fecOcTmdIntUpdRate = 8;
+ } else {
+ /* (commonAttr->staticCLK == false) => dynamic mode */
+ fecOcDtoMode = FEC_OC_DTO_MODE_DYNAMIC__M;
+ fecOcFctMode = FEC_OC_FCT_MODE__PRE;
+ fecOcTmdIntUpdRate = 5;
+ }
+
+ /* Write appropriate registers with requested configuration */
+ status = write16(state, FEC_OC_DTO_BURST_LEN__A, fecOcDtoBurstLen);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_DTO_PERIOD__A, fecOcDtoPeriod);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_DTO_MODE__A, fecOcDtoMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_FCT_MODE__A, fecOcFctMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_MODE__A, fecOcRegMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_IPR_MODE__A, fecOcRegIprMode);
+ if (status < 0)
+ goto error;
+
+ /* Rate integration settings */
+ status = write32(state, FEC_OC_RCN_CTL_RATE_LO__A, fecOcRcnCtlRate);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_INT_UPD_RATE__A, fecOcTmdIntUpdRate);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_MODE__A, fecOcTmdMode);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int MPEGTSConfigurePolarity(struct drxk_state *state)
+{
+ u16 fecOcRegIprInvert = 0;
+
+ /* Data mask for the output data byte */
+ u16 InvertDataMask =
+ FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M |
+ FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M |
+ FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M |
+ FEC_OC_IPR_INVERT_MD1__M | FEC_OC_IPR_INVERT_MD0__M;
+
+ dprintk(1, "\n");
+
+ /* Control selective inversion of output bits */
+ fecOcRegIprInvert &= (~(InvertDataMask));
+ if (state->m_invertDATA == true)
+ fecOcRegIprInvert |= InvertDataMask;
+ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MERR__M));
+ if (state->m_invertERR == true)
+ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MERR__M;
+ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MSTRT__M));
+ if (state->m_invertSTR == true)
+ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MSTRT__M;
+ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MVAL__M));
+ if (state->m_invertVAL == true)
+ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MVAL__M;
+ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MCLK__M));
+ if (state->m_invertCLK == true)
+ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MCLK__M;
+
+ return write16(state, FEC_OC_IPR_INVERT__A, fecOcRegIprInvert);
+}
+
+#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000
+
+static int SetAgcRf(struct drxk_state *state,
+ struct SCfgAgc *pAgcCfg, bool isDTV)
+{
+ int status = -EINVAL;
+ u16 data = 0;
+ struct SCfgAgc *pIfAgcSettings;
+
+ dprintk(1, "\n");
+
+ if (pAgcCfg == NULL)
+ goto error;
+
+ switch (pAgcCfg->ctrlMode) {
+ case DRXK_AGC_CTRL_AUTO:
+ /* Enable RF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+
+ /* Enable SCU RF AGC loop */
+ data &= ~SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+
+ /* Polarity */
+ if (state->m_RfAgcPol)
+ data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Set speed (using complementary reduction value) */
+ status = read16(state, SCU_RAM_AGC_KI_RED__A, &data);
+ if (status < 0)
+ goto error;
+
+ data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M;
+ data |= (~(pAgcCfg->speed <<
+ SCU_RAM_AGC_KI_RED_RAGC_RED__B)
+ & SCU_RAM_AGC_KI_RED_RAGC_RED__M);
+
+ status = write16(state, SCU_RAM_AGC_KI_RED__A, data);
+ if (status < 0)
+ goto error;
+
+ if (IsDVBT(state))
+ pIfAgcSettings = &state->m_dvbtIfAgcCfg;
+ else if (IsQAM(state))
+ pIfAgcSettings = &state->m_qamIfAgcCfg;
+ else
+ pIfAgcSettings = &state->m_atvIfAgcCfg;
+ if (pIfAgcSettings == NULL) {
+ status = -EINVAL;
+ goto error;
+ }
+
+ /* Set TOP, only if IF-AGC is in AUTO mode */
+ if (pIfAgcSettings->ctrlMode == DRXK_AGC_CTRL_AUTO)
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->top);
+ if (status < 0)
+ goto error;
+
+ /* Cut-Off current */
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, pAgcCfg->cutOffCurrent);
+ if (status < 0)
+ goto error;
+
+ /* Max. output level */
+ status = write16(state, SCU_RAM_AGC_RF_MAX__A, pAgcCfg->maxOutputLevel);
+ if (status < 0)
+ goto error;
+
+ break;
+
+ case DRXK_AGC_CTRL_USER:
+ /* Enable RF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU RF AGC loop */
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+ if (state->m_RfAgcPol)
+ data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* SCU c.o.c. to 0, enabling full control range */
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* Write value to output pin */
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, pAgcCfg->outputLevel);
+ if (status < 0)
+ goto error;
+ break;
+
+ case DRXK_AGC_CTRL_OFF:
+ /* Disable RF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data |= IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU RF AGC loop */
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+ break;
+
+ default:
+ status = -EINVAL;
+
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000
+
+static int SetAgcIf(struct drxk_state *state,
+ struct SCfgAgc *pAgcCfg, bool isDTV)
+{
+ u16 data = 0;
+ int status = 0;
+ struct SCfgAgc *pRfAgcSettings;
+
+ dprintk(1, "\n");
+
+ switch (pAgcCfg->ctrlMode) {
+ case DRXK_AGC_CTRL_AUTO:
+
+ /* Enable IF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+
+ /* Enable SCU IF AGC loop */
+ data &= ~SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+
+ /* Polarity */
+ if (state->m_IfAgcPol)
+ data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Set speed (using complementary reduction value) */
+ status = read16(state, SCU_RAM_AGC_KI_RED__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M;
+ data |= (~(pAgcCfg->speed <<
+ SCU_RAM_AGC_KI_RED_IAGC_RED__B)
+ & SCU_RAM_AGC_KI_RED_IAGC_RED__M);
+
+ status = write16(state, SCU_RAM_AGC_KI_RED__A, data);
+ if (status < 0)
+ goto error;
+
+ if (IsQAM(state))
+ pRfAgcSettings = &state->m_qamRfAgcCfg;
+ else
+ pRfAgcSettings = &state->m_atvRfAgcCfg;
+ if (pRfAgcSettings == NULL)
+ return -1;
+ /* Restore TOP */
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pRfAgcSettings->top);
+ if (status < 0)
+ goto error;
+ break;
+
+ case DRXK_AGC_CTRL_USER:
+
+ /* Enable IF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU IF AGC loop */
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+
+ /* Polarity */
+ if (state->m_IfAgcPol)
+ data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Write value to output pin */
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->outputLevel);
+ if (status < 0)
+ goto error;
+ break;
+
+ case DRXK_AGC_CTRL_OFF:
+
+ /* Disable If AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data |= IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU IF AGC loop */
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+ break;
+ } /* switch (agcSettingsIf->ctrlMode) */
+
+ /* always set the top to support
+ configurations without if-loop */
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, pAgcCfg->top);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int ReadIFAgc(struct drxk_state *state, u32 *pValue)
+{
+ u16 agcDacLvl;
+ int status;
+ u16 Level = 0;
+
+ dprintk(1, "\n");
+
+ status = read16(state, IQM_AF_AGC_IF__A, &agcDacLvl);
+ if (status < 0) {
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+ }
+
+ *pValue = 0;
+
+ if (agcDacLvl > DRXK_AGC_DAC_OFFSET)
+ Level = agcDacLvl - DRXK_AGC_DAC_OFFSET;
+ if (Level < 14000)
+ *pValue = (14000 - Level) / 4;
+ else
+ *pValue = 0;
+
+ return status;
+}
+
+static int GetQAMSignalToNoise(struct drxk_state *state,
+ s32 *pSignalToNoise)
+{
+ int status = 0;
+ u16 qamSlErrPower = 0; /* accum. error between
+ raw and sliced symbols */
+ u32 qamSlSigPower = 0; /* used for MER, depends of
+ QAM constellation */
+ u32 qamSlMer = 0; /* QAM MER */
+
+ dprintk(1, "\n");
+
+ /* MER calculation */
+
+ /* get the register value needed for MER */
+ status = read16(state, QAM_SL_ERR_POWER__A, &qamSlErrPower);
+ if (status < 0) {
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return -EINVAL;
+ }
+
+ switch (state->param.u.qam.modulation) {
+ case QAM_16:
+ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM16 << 2;
+ break;
+ case QAM_32:
+ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM32 << 2;
+ break;
+ case QAM_64:
+ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM64 << 2;
+ break;
+ case QAM_128:
+ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM128 << 2;
+ break;
+ default:
+ case QAM_256:
+ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM256 << 2;
+ break;
+ }
+
+ if (qamSlErrPower > 0) {
+ qamSlMer = Log10Times100(qamSlSigPower) -
+ Log10Times100((u32) qamSlErrPower);
+ }
+ *pSignalToNoise = qamSlMer;
+
+ return status;
+}
+
+static int GetDVBTSignalToNoise(struct drxk_state *state,
+ s32 *pSignalToNoise)
+{
+ int status;
+ u16 regData = 0;
+ u32 EqRegTdSqrErrI = 0;
+ u32 EqRegTdSqrErrQ = 0;
+ u16 EqRegTdSqrErrExp = 0;
+ u16 EqRegTdTpsPwrOfs = 0;
+ u16 EqRegTdReqSmbCnt = 0;
+ u32 tpsCnt = 0;
+ u32 SqrErrIQ = 0;
+ u32 a = 0;
+ u32 b = 0;
+ u32 c = 0;
+ u32 iMER = 0;
+ u16 transmissionParams = 0;
+
+ dprintk(1, "\n");
+
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_PWR_OFS__A, &EqRegTdTpsPwrOfs);
+ if (status < 0)
+ goto error;
+ status = read16(state, OFDM_EQ_TOP_TD_REQ_SMB_CNT__A, &EqRegTdReqSmbCnt);
+ if (status < 0)
+ goto error;
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_EXP__A, &EqRegTdSqrErrExp);
+ if (status < 0)
+ goto error;
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_I__A, &regData);
+ if (status < 0)
+ goto error;
+ /* Extend SQR_ERR_I operational range */
+ EqRegTdSqrErrI = (u32) regData;
+ if ((EqRegTdSqrErrExp > 11) &&
+ (EqRegTdSqrErrI < 0x00000FFFUL)) {
+ EqRegTdSqrErrI += 0x00010000UL;
+ }
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_Q__A, &regData);
+ if (status < 0)
+ goto error;
+ /* Extend SQR_ERR_Q operational range */
+ EqRegTdSqrErrQ = (u32) regData;
+ if ((EqRegTdSqrErrExp > 11) &&
+ (EqRegTdSqrErrQ < 0x00000FFFUL))
+ EqRegTdSqrErrQ += 0x00010000UL;
+
+ status = read16(state, OFDM_SC_RA_RAM_OP_PARAM__A, &transmissionParams);
+ if (status < 0)
+ goto error;
+
+ /* Check input data for MER */
+
+ /* MER calculation (in 0.1 dB) without math.h */
+ if ((EqRegTdTpsPwrOfs == 0) || (EqRegTdReqSmbCnt == 0))
+ iMER = 0;
+ else if ((EqRegTdSqrErrI + EqRegTdSqrErrQ) == 0) {
+ /* No error at all, this must be the HW reset value
+ * Apparently no first measurement yet
+ * Set MER to 0.0 */
+ iMER = 0;
+ } else {
+ SqrErrIQ = (EqRegTdSqrErrI + EqRegTdSqrErrQ) <<
+ EqRegTdSqrErrExp;
+ if ((transmissionParams &
+ OFDM_SC_RA_RAM_OP_PARAM_MODE__M)
+ == OFDM_SC_RA_RAM_OP_PARAM_MODE_2K)
+ tpsCnt = 17;
+ else
+ tpsCnt = 68;
+
+ /* IMER = 100 * log10 (x)
+ where x = (EqRegTdTpsPwrOfs^2 *
+ EqRegTdReqSmbCnt * tpsCnt)/SqrErrIQ
+
+ => IMER = a + b -c
+ where a = 100 * log10 (EqRegTdTpsPwrOfs^2)
+ b = 100 * log10 (EqRegTdReqSmbCnt * tpsCnt)
+ c = 100 * log10 (SqrErrIQ)
+ */
+
+ /* log(x) x = 9bits * 9bits->18 bits */
+ a = Log10Times100(EqRegTdTpsPwrOfs *
+ EqRegTdTpsPwrOfs);
+ /* log(x) x = 16bits * 7bits->23 bits */
+ b = Log10Times100(EqRegTdReqSmbCnt * tpsCnt);
+ /* log(x) x = (16bits + 16bits) << 15 ->32 bits */
+ c = Log10Times100(SqrErrIQ);
+
+ iMER = a + b;
+ /* No negative MER, clip to zero */
+ if (iMER > c)
+ iMER -= c;
+ else
+ iMER = 0;
+ }
+ *pSignalToNoise = iMER;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int GetSignalToNoise(struct drxk_state *state, s32 *pSignalToNoise)
+{
+ dprintk(1, "\n");
+
+ *pSignalToNoise = 0;
+ switch (state->m_OperationMode) {
+ case OM_DVBT:
+ return GetDVBTSignalToNoise(state, pSignalToNoise);
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ return GetQAMSignalToNoise(state, pSignalToNoise);
+ default:
+ break;
+ }
+ return 0;
+}
+
+#if 0
+static int GetDVBTQuality(struct drxk_state *state, s32 *pQuality)
+{
+ /* SNR Values for quasi errorfree reception rom Nordig 2.2 */
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ static s32 QE_SN[] = {
+ 51, /* QPSK 1/2 */
+ 69, /* QPSK 2/3 */
+ 79, /* QPSK 3/4 */
+ 89, /* QPSK 5/6 */
+ 97, /* QPSK 7/8 */
+ 108, /* 16-QAM 1/2 */
+ 131, /* 16-QAM 2/3 */
+ 146, /* 16-QAM 3/4 */
+ 156, /* 16-QAM 5/6 */
+ 160, /* 16-QAM 7/8 */
+ 165, /* 64-QAM 1/2 */
+ 187, /* 64-QAM 2/3 */
+ 202, /* 64-QAM 3/4 */
+ 216, /* 64-QAM 5/6 */
+ 225, /* 64-QAM 7/8 */
+ };
+
+ *pQuality = 0;
+
+ do {
+ s32 SignalToNoise = 0;
+ u16 Constellation = 0;
+ u16 CodeRate = 0;
+ u32 SignalToNoiseRel;
+ u32 BERQuality;
+
+ status = GetDVBTSignalToNoise(state, &SignalToNoise);
+ if (status < 0)
+ break;
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_CONST__A, &Constellation);
+ if (status < 0)
+ break;
+ Constellation &= OFDM_EQ_TOP_TD_TPS_CONST__M;
+
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_CODE_HP__A, &CodeRate);
+ if (status < 0)
+ break;
+ CodeRate &= OFDM_EQ_TOP_TD_TPS_CODE_HP__M;
+
+ if (Constellation > OFDM_EQ_TOP_TD_TPS_CONST_64QAM ||
+ CodeRate > OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8)
+ break;
+ SignalToNoiseRel = SignalToNoise -
+ QE_SN[Constellation * 5 + CodeRate];
+ BERQuality = 100;
+
+ if (SignalToNoiseRel < -70)
+ *pQuality = 0;
+ else if (SignalToNoiseRel < 30)
+ *pQuality = ((SignalToNoiseRel + 70) *
+ BERQuality) / 100;
+ else
+ *pQuality = BERQuality;
+ } while (0);
+ return 0;
+};
+
+static int GetDVBCQuality(struct drxk_state *state, s32 *pQuality)
+{
+ int status = 0;
+ *pQuality = 0;
+
+ dprintk(1, "\n");
+
+ do {
+ u32 SignalToNoise = 0;
+ u32 BERQuality = 100;
+ u32 SignalToNoiseRel = 0;
+
+ status = GetQAMSignalToNoise(state, &SignalToNoise);
+ if (status < 0)
+ break;
+
+ switch (state->param.u.qam.modulation) {
+ case QAM_16:
+ SignalToNoiseRel = SignalToNoise - 200;
+ break;
+ case QAM_32:
+ SignalToNoiseRel = SignalToNoise - 230;
+ break; /* Not in NorDig */
+ case QAM_64:
+ SignalToNoiseRel = SignalToNoise - 260;
+ break;
+ case QAM_128:
+ SignalToNoiseRel = SignalToNoise - 290;
+ break;
+ default:
+ case QAM_256:
+ SignalToNoiseRel = SignalToNoise - 320;
+ break;
+ }
+
+ if (SignalToNoiseRel < -70)
+ *pQuality = 0;
+ else if (SignalToNoiseRel < 30)
+ *pQuality = ((SignalToNoiseRel + 70) *
+ BERQuality) / 100;
+ else
+ *pQuality = BERQuality;
+ } while (0);
+
+ return status;
+}
+
+static int GetQuality(struct drxk_state *state, s32 *pQuality)
+{
+ dprintk(1, "\n");
+
+ switch (state->m_OperationMode) {
+ case OM_DVBT:
+ return GetDVBTQuality(state, pQuality);
+ case OM_QAM_ITU_A:
+ return GetDVBCQuality(state, pQuality);
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+/* Free data ram in SIO HI */
+#define SIO_HI_RA_RAM_USR_BEGIN__A 0x420040
+#define SIO_HI_RA_RAM_USR_END__A 0x420060
+
+#define DRXK_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A)
+#define DRXK_HI_ATOMIC_BUF_END (SIO_HI_RA_RAM_USR_BEGIN__A + 7)
+#define DRXK_HI_ATOMIC_READ SIO_HI_RA_RAM_PAR_3_ACP_RW_READ
+#define DRXK_HI_ATOMIC_WRITE SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE
+
+#define DRXDAP_FASI_ADDR2BLOCK(addr) (((addr) >> 22) & 0x3F)
+#define DRXDAP_FASI_ADDR2BANK(addr) (((addr) >> 16) & 0x3F)
+#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr) & 0x7FFF)
+
+static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ goto error;
+ if (state->m_DrxkState == DRXK_POWERED_DOWN)
+ goto error;
+
+ if (state->no_i2c_bridge)
+ return 0;
+
+ status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
+ if (status < 0)
+ goto error;
+ if (bEnableBridge) {
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED);
+ if (status < 0)
+ goto error;
+ } else {
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN);
+ if (status < 0)
+ goto error;
+ }
+
+ status = HI_Command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, 0);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SetPreSaw(struct drxk_state *state,
+ struct SCfgPreSaw *pPreSawCfg)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ if ((pPreSawCfg == NULL)
+ || (pPreSawCfg->reference > IQM_AF_PDREF__M))
+ goto error;
+
+ status = write16(state, IQM_AF_PDREF__A, pPreSawCfg->reference);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int BLDirectCmd(struct drxk_state *state, u32 targetAddr,
+ u16 romOffset, u16 nrOfElements, u32 timeOut)
+{
+ u16 blStatus = 0;
+ u16 offset = (u16) ((targetAddr >> 0) & 0x00FFFF);
+ u16 blockbank = (u16) ((targetAddr >> 16) & 0x000FFF);
+ int status;
+ unsigned long end;
+
+ dprintk(1, "\n");
+
+ mutex_lock(&state->mutex);
+ status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_DIRECT);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_TGT_HDR__A, blockbank);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_TGT_ADDR__A, offset);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_SRC_ADDR__A, romOffset);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_SRC_LEN__A, nrOfElements);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
+ if (status < 0)
+ goto error;
+
+ end = jiffies + msecs_to_jiffies(timeOut);
+ do {
+ status = read16(state, SIO_BL_STATUS__A, &blStatus);
+ if (status < 0)
+ goto error;
+ } while ((blStatus == 0x1) && time_is_after_jiffies(end));
+ if (blStatus == 0x1) {
+ printk(KERN_ERR "drxk: SIO not ready\n");
+ status = -EINVAL;
+ goto error2;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+error2:
+ mutex_unlock(&state->mutex);
+ return status;
+
+}
+
+static int ADCSyncMeasurement(struct drxk_state *state, u16 *count)
+{
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ /* Start measurement */
+ status = write16(state, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_START_LOCK__A, 1);
+ if (status < 0)
+ goto error;
+
+ *count = 0;
+ status = read16(state, IQM_AF_PHASE0__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == 127)
+ *count = *count + 1;
+ status = read16(state, IQM_AF_PHASE1__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == 127)
+ *count = *count + 1;
+ status = read16(state, IQM_AF_PHASE2__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == 127)
+ *count = *count + 1;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int ADCSynchronization(struct drxk_state *state)
+{
+ u16 count = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ status = ADCSyncMeasurement(state, &count);
+ if (status < 0)
+ goto error;
+
+ if (count == 1) {
+ /* Try sampling on a diffrent edge */
+ u16 clkNeg = 0;
+
+ status = read16(state, IQM_AF_CLKNEG__A, &clkNeg);
+ if (status < 0)
+ goto error;
+ if ((clkNeg | IQM_AF_CLKNEG_CLKNEGDATA__M) ==
+ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) {
+ clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
+ clkNeg |=
+ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG;
+ } else {
+ clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
+ clkNeg |=
+ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS;
+ }
+ status = write16(state, IQM_AF_CLKNEG__A, clkNeg);
+ if (status < 0)
+ goto error;
+ status = ADCSyncMeasurement(state, &count);
+ if (status < 0)
+ goto error;
+ }
+
+ if (count < 2)
+ status = -EINVAL;
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SetFrequencyShifter(struct drxk_state *state,
+ u16 intermediateFreqkHz,
+ s32 tunerFreqOffset, bool isDTV)
+{
+ bool selectPosImage = false;
+ u32 rfFreqResidual = tunerFreqOffset;
+ u32 fmFrequencyShift = 0;
+ bool tunerMirror = !state->m_bMirrorFreqSpect;
+ u32 adcFreq;
+ bool adcFlip;
+ int status;
+ u32 ifFreqActual;
+ u32 samplingFrequency = (u32) (state->m_sysClockFreq / 3);
+ u32 frequencyShift;
+ bool imageToSelect;
+
+ dprintk(1, "\n");
+
+ /*
+ Program frequency shifter
+ No need to account for mirroring on RF
+ */
+ if (isDTV) {
+ if ((state->m_OperationMode == OM_QAM_ITU_A) ||
+ (state->m_OperationMode == OM_QAM_ITU_C) ||
+ (state->m_OperationMode == OM_DVBT))
+ selectPosImage = true;
+ else
+ selectPosImage = false;
+ }
+ if (tunerMirror)
+ /* tuner doesn't mirror */
+ ifFreqActual = intermediateFreqkHz +
+ rfFreqResidual + fmFrequencyShift;
+ else
+ /* tuner mirrors */
+ ifFreqActual = intermediateFreqkHz -
+ rfFreqResidual - fmFrequencyShift;
+ if (ifFreqActual > samplingFrequency / 2) {
+ /* adc mirrors */
+ adcFreq = samplingFrequency - ifFreqActual;
+ adcFlip = true;
+ } else {
+ /* adc doesn't mirror */
+ adcFreq = ifFreqActual;
+ adcFlip = false;
+ }
+
+ frequencyShift = adcFreq;
+ imageToSelect = state->m_rfmirror ^ tunerMirror ^
+ adcFlip ^ selectPosImage;
+ state->m_IqmFsRateOfs =
+ Frac28a((frequencyShift), samplingFrequency);
+
+ if (imageToSelect)
+ state->m_IqmFsRateOfs = ~state->m_IqmFsRateOfs + 1;
+
+ /* Program frequency shifter with tuner offset compensation */
+ /* frequencyShift += tunerFreqOffset; TODO */
+ status = write32(state, IQM_FS_RATE_OFS_LO__A,
+ state->m_IqmFsRateOfs);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int InitAGC(struct drxk_state *state, bool isDTV)
+{
+ u16 ingainTgt = 0;
+ u16 ingainTgtMin = 0;
+ u16 ingainTgtMax = 0;
+ u16 clpCyclen = 0;
+ u16 clpSumMin = 0;
+ u16 clpDirTo = 0;
+ u16 snsSumMin = 0;
+ u16 snsSumMax = 0;
+ u16 clpSumMax = 0;
+ u16 snsDirTo = 0;
+ u16 kiInnergainMin = 0;
+ u16 ifIaccuHiTgt = 0;
+ u16 ifIaccuHiTgtMin = 0;
+ u16 ifIaccuHiTgtMax = 0;
+ u16 data = 0;
+ u16 fastClpCtrlDelay = 0;
+ u16 clpCtrlMode = 0;
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ /* Common settings */
+ snsSumMax = 1023;
+ ifIaccuHiTgtMin = 2047;
+ clpCyclen = 500;
+ clpSumMax = 1023;
+
+ /* AGCInit() not available for DVBT; init done in microcode */
+ if (!IsQAM(state)) {
+ printk(KERN_ERR "drxk: %s: mode %d is not DVB-C\n", __func__, state->m_OperationMode);
+ return -EINVAL;
+ }
+
+ /* FIXME: Analog TV AGC require different settings */
+
+ /* Standard specific settings */
+ clpSumMin = 8;
+ clpDirTo = (u16) -9;
+ clpCtrlMode = 0;
+ snsSumMin = 8;
+ snsDirTo = (u16) -9;
+ kiInnergainMin = (u16) -1030;
+ ifIaccuHiTgtMax = 0x2380;
+ ifIaccuHiTgt = 0x2380;
+ ingainTgtMin = 0x0511;
+ ingainTgt = 0x0511;
+ ingainTgtMax = 5119;
+ fastClpCtrlDelay = state->m_qamIfAgcCfg.FastClipCtrlDelay;
+
+ status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, fastClpCtrlDelay);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_CLP_CTRL_MODE__A, clpCtrlMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT__A, ingainTgt);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, ingainTgtMin);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingainTgtMax);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, ifIaccuHiTgtMin);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, ifIaccuHiTgtMax);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_LO__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_LO__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_SUM_MAX__A, clpSumMax);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_SUM_MAX__A, snsSumMax);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, kiInnergainMin);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, ifIaccuHiTgt);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_CYCLEN__A, clpCyclen);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MAX__A, 1023);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MIN__A, (u16) -1023);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_SUM_MIN__A, clpSumMin);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_SUM_MIN__A, snsSumMin);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_DIR_TO__A, clpDirTo);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_DIR_TO__A, snsDirTo);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MIN__A, 0x0117);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MAX__A, 0x0657);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_SUM__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_CYCCNT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_DIR_WD__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_DIR_STP__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_SUM__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_CYCCNT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_DIR_WD__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_DIR_STP__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_CYCLEN__A, 500);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_CYCLEN__A, 500);
+ if (status < 0)
+ goto error;
+
+ /* Initialize inner-loop KI gain factors */
+ status = read16(state, SCU_RAM_AGC_KI__A, &data);
+ if (status < 0)
+ goto error;
+
+ data = 0x0657;
+ data &= ~SCU_RAM_AGC_KI_RF__M;
+ data |= (DRXK_KI_RAGC_QAM << SCU_RAM_AGC_KI_RF__B);
+ data &= ~SCU_RAM_AGC_KI_IF__M;
+ data |= (DRXK_KI_IAGC_QAM << SCU_RAM_AGC_KI_IF__B);
+
+ status = write16(state, SCU_RAM_AGC_KI__A, data);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int DVBTQAMGetAccPktErr(struct drxk_state *state, u16 *packetErr)
+{
+ int status;
+
+ dprintk(1, "\n");
+ if (packetErr == NULL)
+ status = write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0);
+ else
+ status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, packetErr);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int DVBTScCommand(struct drxk_state *state,
+ u16 cmd, u16 subcmd,
+ u16 param0, u16 param1, u16 param2,
+ u16 param3, u16 param4)
+{
+ u16 curCmd = 0;
+ u16 errCode = 0;
+ u16 retryCnt = 0;
+ u16 scExec = 0;
+ int status;
+
+ dprintk(1, "\n");
+ status = read16(state, OFDM_SC_COMM_EXEC__A, &scExec);
+ if (scExec != 1) {
+ /* SC is not running */
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Wait until sc is ready to receive command */
+ retryCnt = 0;
+ do {
+ msleep(1);
+ status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd);
+ retryCnt++;
+ } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES));
+ if (retryCnt >= DRXK_MAX_RETRIES && (status < 0))
+ goto error;
+
+ /* Write sub-command */
+ switch (cmd) {
+ /* All commands using sub-cmd */
+ case OFDM_SC_RA_RAM_CMD_PROC_START:
+ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+ status = write16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, subcmd);
+ if (status < 0)
+ goto error;
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ /* Write needed parameters and the command */
+ switch (cmd) {
+ /* All commands using 5 parameters */
+ /* All commands using 4 parameters */
+ /* All commands using 3 parameters */
+ /* All commands using 2 parameters */
+ case OFDM_SC_RA_RAM_CMD_PROC_START:
+ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+ status = write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1);
+ /* All commands using 1 parameters */
+ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
+ case OFDM_SC_RA_RAM_CMD_USER_IO:
+ status = write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0);
+ /* All commands using 0 parameters */
+ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
+ case OFDM_SC_RA_RAM_CMD_NULL:
+ /* Write command */
+ status = write16(state, OFDM_SC_RA_RAM_CMD__A, cmd);
+ break;
+ default:
+ /* Unknown command */
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Wait until sc is ready processing command */
+ retryCnt = 0;
+ do {
+ msleep(1);
+ status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd);
+ retryCnt++;
+ } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES));
+ if (retryCnt >= DRXK_MAX_RETRIES && (status < 0))
+ goto error;
+
+ /* Check for illegal cmd */
+ status = read16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, &errCode);
+ if (errCode == 0xFFFF) {
+ /* illegal command */
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Retreive results parameters from SC */
+ switch (cmd) {
+ /* All commands yielding 5 results */
+ /* All commands yielding 4 results */
+ /* All commands yielding 3 results */
+ /* All commands yielding 2 results */
+ /* All commands yielding 1 result */
+ case OFDM_SC_RA_RAM_CMD_USER_IO:
+ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
+ status = read16(state, OFDM_SC_RA_RAM_PARAM0__A, &(param0));
+ /* All commands yielding 0 results */
+ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
+ case OFDM_SC_RA_RAM_CMD_SET_TIMER:
+ case OFDM_SC_RA_RAM_CMD_PROC_START:
+ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+ case OFDM_SC_RA_RAM_CMD_NULL:
+ break;
+ default:
+ /* Unknown command */
+ status = -EINVAL;
+ break;
+ } /* switch (cmd->cmd) */
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int PowerUpDVBT(struct drxk_state *state)
+{
+ enum DRXPowerMode powerMode = DRX_POWER_UP;
+ int status;
+
+ dprintk(1, "\n");
+ status = CtrlPowerMode(state, &powerMode);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int DVBTCtrlSetIncEnable(struct drxk_state *state, bool *enabled)
+{
+ int status;
+
+ dprintk(1, "\n");
+ if (*enabled == true)
+ status = write16(state, IQM_CF_BYPASSDET__A, 0);
+ else
+ status = write16(state, IQM_CF_BYPASSDET__A, 1);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+#define DEFAULT_FR_THRES_8K 4000
+static int DVBTCtrlSetFrEnable(struct drxk_state *state, bool *enabled)
+{
+
+ int status;
+
+ dprintk(1, "\n");
+ if (*enabled == true) {
+ /* write mask to 1 */
+ status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A,
+ DEFAULT_FR_THRES_8K);
+ } else {
+ /* write mask to 0 */
+ status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, 0);
+ }
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int DVBTCtrlSetEchoThreshold(struct drxk_state *state,
+ struct DRXKCfgDvbtEchoThres_t *echoThres)
+{
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+ status = read16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, &data);
+ if (status < 0)
+ goto error;
+
+ switch (echoThres->fftMode) {
+ case DRX_FFTMODE_2K:
+ data &= ~OFDM_SC_RA_RAM_ECHO_THRES_2K__M;
+ data |= ((echoThres->threshold <<
+ OFDM_SC_RA_RAM_ECHO_THRES_2K__B)
+ & (OFDM_SC_RA_RAM_ECHO_THRES_2K__M));
+ break;
+ case DRX_FFTMODE_8K:
+ data &= ~OFDM_SC_RA_RAM_ECHO_THRES_8K__M;
+ data |= ((echoThres->threshold <<
+ OFDM_SC_RA_RAM_ECHO_THRES_8K__B)
+ & (OFDM_SC_RA_RAM_ECHO_THRES_8K__M));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ status = write16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, data);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int DVBTCtrlSetSqiSpeed(struct drxk_state *state,
+ enum DRXKCfgDvbtSqiSpeed *speed)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ switch (*speed) {
+ case DRXK_DVBT_SQI_SPEED_FAST:
+ case DRXK_DVBT_SQI_SPEED_MEDIUM:
+ case DRXK_DVBT_SQI_SPEED_SLOW:
+ break;
+ default:
+ goto error;
+ }
+ status = write16(state, SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A,
+ (u16) *speed);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Activate DVBT specific presets
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*
+* Called in DVBTSetStandard
+*
+*/
+static int DVBTActivatePresets(struct drxk_state *state)
+{
+ int status;
+ bool setincenable = false;
+ bool setfrenable = true;
+
+ struct DRXKCfgDvbtEchoThres_t echoThres2k = { 0, DRX_FFTMODE_2K };
+ struct DRXKCfgDvbtEchoThres_t echoThres8k = { 0, DRX_FFTMODE_8K };
+
+ dprintk(1, "\n");
+ status = DVBTCtrlSetIncEnable(state, &setincenable);
+ if (status < 0)
+ goto error;
+ status = DVBTCtrlSetFrEnable(state, &setfrenable);
+ if (status < 0)
+ goto error;
+ status = DVBTCtrlSetEchoThreshold(state, &echoThres2k);
+ if (status < 0)
+ goto error;
+ status = DVBTCtrlSetEchoThreshold(state, &echoThres8k);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, state->m_dvbtIfAgcCfg.IngainTgtMax);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Initialize channelswitch-independent settings for DVBT.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*
+* For ROM code channel filter taps are loaded from the bootloader. For microcode
+* the DVB-T taps from the drxk_filters.h are used.
+*/
+static int SetDVBTStandard(struct drxk_state *state,
+ enum OperationMode oMode)
+{
+ u16 cmdResult = 0;
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ PowerUpDVBT(state);
+ /* added antenna switch */
+ SwitchAntennaToDVBT(state);
+ /* send OFDM reset command */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ /* send OFDM setenv command */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ /* reset datapath for OFDM, processors first */
+ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+ if (status < 0)
+ goto error;
+
+ /* IQM setup */
+ /* synchronize on ofdstate->m_festart */
+ status = write16(state, IQM_AF_UPD_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ /* window size for clipping ADC detection */
+ status = write16(state, IQM_AF_CLP_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ /* window size for for sense pre-SAW detection */
+ status = write16(state, IQM_AF_SNS_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ /* sense threshold for sense pre-SAW detection */
+ status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
+ if (status < 0)
+ goto error;
+ status = SetIqmAf(state, true);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_AF_AGC_RF__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* Impulse noise cruncher setup */
+ status = write16(state, IQM_AF_INC_LCT__A, 0); /* crunch in IQM_CF */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DET_LCT__A, 0); /* detect in IQM_CF */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_WND_LEN__A, 3); /* peak detector window length */
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_RC_STRETCH__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_OUT_ENA__A, 0x4); /* enable output 2 */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DS_ENA__A, 0x4); /* decimate output 2 */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_SCALE__A, 1600);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_SCALE_SH__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* virtual clipping threshold for clipping ADC detection */
+ status = write16(state, IQM_AF_CLP_TH__A, 448);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DATATH__A, 495); /* crunching threshold */
+ if (status < 0)
+ goto error;
+
+ status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_DVBT, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_CF_PKDTH__A, 2); /* peak detector threshold */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_POW_MEAS_LEN__A, 2);
+ if (status < 0)
+ goto error;
+ /* enable power measurement interrupt */
+ status = write16(state, IQM_CF_COMM_INT_MSK__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* IQM will not be reset from here, sync ADC and update/init AGC */
+ status = ADCSynchronization(state);
+ if (status < 0)
+ goto error;
+ status = SetPreSaw(state, &state->m_dvbtPreSawCfg);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ status = SetAgcRf(state, &state->m_dvbtRfAgcCfg, true);
+ if (status < 0)
+ goto error;
+ status = SetAgcIf(state, &state->m_dvbtIfAgcCfg, true);
+ if (status < 0)
+ goto error;
+
+ /* Set Noise Estimation notch width and enable DC fix */
+ status = read16(state, OFDM_SC_RA_RAM_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M;
+ status = write16(state, OFDM_SC_RA_RAM_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ if (!state->m_DRXK_A3_ROM_CODE) {
+ /* AGCInit() is not done for DVBT, so set agcFastClipCtrlDelay */
+ status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, state->m_dvbtIfAgcCfg.FastClipCtrlDelay);
+ if (status < 0)
+ goto error;
+ }
+
+ /* OFDM_SC setup */
+#ifdef COMPILE_FOR_NONRT
+ status = write16(state, OFDM_SC_RA_RAM_BE_OPT_DELAY__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A, 2);
+ if (status < 0)
+ goto error;
+#endif
+
+ /* FEC setup */
+ status = write16(state, FEC_DI_INPUT_CTL__A, 1); /* OFDM input */
+ if (status < 0)
+ goto error;
+
+
+#ifdef COMPILE_FOR_NONRT
+ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x400);
+ if (status < 0)
+ goto error;
+#else
+ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x1000);
+ if (status < 0)
+ goto error;
+#endif
+ status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, 0x0001);
+ if (status < 0)
+ goto error;
+
+ /* Setup MPEG bus */
+ status = MPEGTSDtoSetup(state, OM_DVBT);
+ if (status < 0)
+ goto error;
+ /* Set DVBT Presets */
+ status = DVBTActivatePresets(state);
+ if (status < 0)
+ goto error;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+/**
+* \brief Start dvbt demodulating for channel.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*/
+static int DVBTStart(struct drxk_state *state)
+{
+ u16 param1;
+ int status;
+ /* DRXKOfdmScCmd_t scCmd; */
+
+ dprintk(1, "\n");
+ /* Start correct processes to get in lock */
+ /* DRXK: OFDM_SC_RA_RAM_PROC_LOCKTRACK is no longer in mapfile! */
+ param1 = OFDM_SC_RA_RAM_LOCKTRACK_MIN;
+ status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_PROC_START, 0, OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M, param1, 0, 0, 0);
+ if (status < 0)
+ goto error;
+ /* Start FEC OC */
+ status = MPEGTSStart(state);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+
+/*============================================================================*/
+
+/**
+* \brief Set up dvbt demodulator for channel.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+* // original DVBTSetChannel()
+*/
+static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz,
+ s32 tunerFreqOffset)
+{
+ u16 cmdResult = 0;
+ u16 transmissionParams = 0;
+ u16 operationMode = 0;
+ u32 iqmRcRateOfs = 0;
+ u32 bandwidth = 0;
+ u16 param1;
+ int status;
+
+ dprintk(1, "IF =%d, TFO = %d\n", IntermediateFreqkHz, tunerFreqOffset);
+
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ /* Stop processors */
+ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ /* Mandatory fix, always stop CP, required to set spl offset back to
+ hardware default (is set to 0 by ucode during pilot detection */
+ status = write16(state, OFDM_CP_COMM_EXEC__A, OFDM_CP_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ /*== Write channel settings to device =====================================*/
+
+ /* mode */
+ switch (state->param.u.ofdm.transmission_mode) {
+ case TRANSMISSION_MODE_AUTO:
+ default:
+ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M;
+ /* fall through , try first guess DRX_FFTMODE_8K */
+ case TRANSMISSION_MODE_8K:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K;
+ break;
+ case TRANSMISSION_MODE_2K:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_2K;
+ break;
+ }
+
+ /* guard */
+ switch (state->param.u.ofdm.guard_interval) {
+ default:
+ case GUARD_INTERVAL_AUTO:
+ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M;
+ /* fall through , try first guess DRX_GUARD_1DIV4 */
+ case GUARD_INTERVAL_1_4:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4;
+ break;
+ case GUARD_INTERVAL_1_32:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_32;
+ break;
+ case GUARD_INTERVAL_1_16:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_16;
+ break;
+ case GUARD_INTERVAL_1_8:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_8;
+ break;
+ }
+
+ /* hierarchy */
+ switch (state->param.u.ofdm.hierarchy_information) {
+ case HIERARCHY_AUTO:
+ case HIERARCHY_NONE:
+ default:
+ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_HIER__M;
+ /* fall through , try first guess SC_RA_RAM_OP_PARAM_HIER_NO */
+ /* transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_NO; */
+ /* break; */
+ case HIERARCHY_1:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1;
+ break;
+ case HIERARCHY_2:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A2;
+ break;
+ case HIERARCHY_4:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A4;
+ break;
+ }
+
+
+ /* constellation */
+ switch (state->param.u.ofdm.constellation) {
+ case QAM_AUTO:
+ default:
+ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M;
+ /* fall through , try first guess DRX_CONSTELLATION_QAM64 */
+ case QAM_64:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64;
+ break;
+ case QPSK:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK;
+ break;
+ case QAM_16:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16;
+ break;
+ }
+#if 0
+ /* No hierachical channels support in BDA */
+ /* Priority (only for hierarchical channels) */
+ switch (channel->priority) {
+ case DRX_PRIORITY_LOW:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO;
+ WR16(devAddr, OFDM_EC_SB_PRIOR__A,
+ OFDM_EC_SB_PRIOR_LO);
+ break;
+ case DRX_PRIORITY_HIGH:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
+ WR16(devAddr, OFDM_EC_SB_PRIOR__A,
+ OFDM_EC_SB_PRIOR_HI));
+ break;
+ case DRX_PRIORITY_UNKNOWN: /* fall through */
+ default:
+ status = -EINVAL;
+ goto error;
+ }
+#else
+ /* Set Priorty high */
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
+ status = write16(state, OFDM_EC_SB_PRIOR__A, OFDM_EC_SB_PRIOR_HI);
+ if (status < 0)
+ goto error;
+#endif
+
+ /* coderate */
+ switch (state->param.u.ofdm.code_rate_HP) {
+ case FEC_AUTO:
+ default:
+ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M;
+ /* fall through , try first guess DRX_CODERATE_2DIV3 */
+ case FEC_2_3:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3;
+ break;
+ case FEC_1_2:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2;
+ break;
+ case FEC_3_4:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4;
+ break;
+ case FEC_5_6:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6;
+ break;
+ case FEC_7_8:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8;
+ break;
+ }
+
+ /* SAW filter selection: normaly not necesarry, but if wanted
+ the application can select a SAW filter via the driver by using UIOs */
+ /* First determine real bandwidth (Hz) */
+ /* Also set delay for impulse noise cruncher */
+ /* Also set parameters for EC_OC fix, note EC_OC_REG_TMD_HIL_MAR is changed
+ by SC for fix for some 8K,1/8 guard but is restored by InitEC and ResetEC
+ functions */
+ switch (state->param.u.ofdm.bandwidth) {
+ case BANDWIDTH_AUTO:
+ case BANDWIDTH_8_MHZ:
+ bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ;
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3052);
+ if (status < 0)
+ goto error;
+ /* cochannel protection for PAL 8 MHz */
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+ if (status < 0)
+ goto error;
+ break;
+ case BANDWIDTH_7_MHZ:
+ bandwidth = DRXK_BANDWIDTH_7MHZ_IN_HZ;
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3491);
+ if (status < 0)
+ goto error;
+ /* cochannel protection for PAL 7 MHz */
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+ if (status < 0)
+ goto error;
+ break;
+ case BANDWIDTH_6_MHZ:
+ bandwidth = DRXK_BANDWIDTH_6MHZ_IN_HZ;
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 4073);
+ if (status < 0)
+ goto error;
+ /* cochannel protection for NTSC 6 MHz */
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 19);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 19);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 14);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+ if (status < 0)
+ goto error;
+ break;
+ default:
+ status = -EINVAL;
+ goto error;
+ }
+
+ if (iqmRcRateOfs == 0) {
+ /* Now compute IQM_RC_RATE_OFS
+ (((SysFreq/BandWidth)/2)/2) -1) * 2^23)
+ =>
+ ((SysFreq / BandWidth) * (2^21)) - (2^23)
+ */
+ /* (SysFreq / BandWidth) * (2^28) */
+ /* assert (MAX(sysClk)/MIN(bandwidth) < 16)
+ => assert(MAX(sysClk) < 16*MIN(bandwidth))
+ => assert(109714272 > 48000000) = true so Frac 28 can be used */
+ iqmRcRateOfs = Frac28a((u32)
+ ((state->m_sysClockFreq *
+ 1000) / 3), bandwidth);
+ /* (SysFreq / BandWidth) * (2^21), rounding before truncating */
+ if ((iqmRcRateOfs & 0x7fL) >= 0x40)
+ iqmRcRateOfs += 0x80L;
+ iqmRcRateOfs = iqmRcRateOfs >> 7;
+ /* ((SysFreq / BandWidth) * (2^21)) - (2^23) */
+ iqmRcRateOfs = iqmRcRateOfs - (1 << 23);
+ }
+
+ iqmRcRateOfs &=
+ ((((u32) IQM_RC_RATE_OFS_HI__M) <<
+ IQM_RC_RATE_OFS_LO__W) | IQM_RC_RATE_OFS_LO__M);
+ status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRateOfs);
+ if (status < 0)
+ goto error;
+
+ /* Bandwidth setting done */
+
+#if 0
+ status = DVBTSetFrequencyShift(demod, channel, tunerOffset);
+ if (status < 0)
+ goto error;
+#endif
+ status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true);
+ if (status < 0)
+ goto error;
+
+ /*== Start SC, write channel settings to SC ===============================*/
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* Enable SC after setting all other parameters */
+ status = write16(state, OFDM_SC_COMM_STATE__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_COMM_EXEC__A, 1);
+ if (status < 0)
+ goto error;
+
+
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ /* Write SC parameter registers, set all AUTO flags in operation mode */
+ param1 = (OFDM_SC_RA_RAM_OP_AUTO_MODE__M |
+ OFDM_SC_RA_RAM_OP_AUTO_GUARD__M |
+ OFDM_SC_RA_RAM_OP_AUTO_CONST__M |
+ OFDM_SC_RA_RAM_OP_AUTO_HIER__M |
+ OFDM_SC_RA_RAM_OP_AUTO_RATE__M);
+ status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM,
+ 0, transmissionParams, param1, 0, 0, 0);
+ if (status < 0)
+ goto error;
+
+ if (!state->m_DRXK_A3_ROM_CODE)
+ status = DVBTCtrlSetSqiSpeed(state, &state->m_sqiSpeed);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+
+/*============================================================================*/
+
+/**
+* \brief Retreive lock status .
+* \param demod Pointer to demodulator instance.
+* \param lockStat Pointer to lock status structure.
+* \return DRXStatus_t.
+*
+*/
+static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus)
+{
+ int status;
+ const u16 mpeg_lock_mask = (OFDM_SC_RA_RAM_LOCK_MPEG__M |
+ OFDM_SC_RA_RAM_LOCK_FEC__M);
+ const u16 fec_lock_mask = (OFDM_SC_RA_RAM_LOCK_FEC__M);
+ const u16 demod_lock_mask = OFDM_SC_RA_RAM_LOCK_DEMOD__M;
+
+ u16 ScRaRamLock = 0;
+ u16 ScCommExec = 0;
+
+ dprintk(1, "\n");
+
+ *pLockStatus = NOT_LOCKED;
+ /* driver 0.9.0 */
+ /* Check if SC is running */
+ status = read16(state, OFDM_SC_COMM_EXEC__A, &ScCommExec);
+ if (status < 0)
+ goto end;
+ if (ScCommExec == OFDM_SC_COMM_EXEC_STOP)
+ goto end;
+
+ status = read16(state, OFDM_SC_RA_RAM_LOCK__A, &ScRaRamLock);
+ if (status < 0)
+ goto end;
+
+ if ((ScRaRamLock & mpeg_lock_mask) == mpeg_lock_mask)
+ *pLockStatus = MPEG_LOCK;
+ else if ((ScRaRamLock & fec_lock_mask) == fec_lock_mask)
+ *pLockStatus = FEC_LOCK;
+ else if ((ScRaRamLock & demod_lock_mask) == demod_lock_mask)
+ *pLockStatus = DEMOD_LOCK;
+ else if (ScRaRamLock & OFDM_SC_RA_RAM_LOCK_NODVBT__M)
+ *pLockStatus = NEVER_LOCK;
+end:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int PowerUpQAM(struct drxk_state *state)
+{
+ enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
+ int status;
+
+ dprintk(1, "\n");
+ status = CtrlPowerMode(state, &powerMode);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+
+/** Power Down QAM */
+static int PowerDownQAM(struct drxk_state *state)
+{
+ u16 data = 0;
+ u16 cmdResult;
+ int status = 0;
+
+ dprintk(1, "\n");
+ status = read16(state, SCU_COMM_EXEC__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == SCU_COMM_EXEC_ACTIVE) {
+ /*
+ STOP demodulator
+ QAM and HW blocks
+ */
+ /* stop all comstate->m_exec */
+ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+ }
+ /* powerdown AFE */
+ status = SetIqmAf(state, false);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Setup of the QAM Measurement intervals for signal quality
+* \param demod instance of demod.
+* \param constellation current constellation.
+* \return DRXStatus_t.
+*
+* NOTE:
+* Take into account that for certain settings the errorcounters can overflow.
+* The implementation does not check this.
+*
+*/
+static int SetQAMMeasurement(struct drxk_state *state,
+ enum EDrxkConstellation constellation,
+ u32 symbolRate)
+{
+ u32 fecBitsDesired = 0; /* BER accounting period */
+ u32 fecRsPeriodTotal = 0; /* Total period */
+ u16 fecRsPrescale = 0; /* ReedSolomon Measurement Prescale */
+ u16 fecRsPeriod = 0; /* Value for corresponding I2C register */
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ fecRsPrescale = 1;
+ /* fecBitsDesired = symbolRate [kHz] *
+ FrameLenght [ms] *
+ (constellation + 1) *
+ SyncLoss (== 1) *
+ ViterbiLoss (==1)
+ */
+ switch (constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ fecBitsDesired = 4 * symbolRate;
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ fecBitsDesired = 5 * symbolRate;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ fecBitsDesired = 6 * symbolRate;
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ fecBitsDesired = 7 * symbolRate;
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ fecBitsDesired = 8 * symbolRate;
+ break;
+ default:
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ fecBitsDesired /= 1000; /* symbolRate [Hz] -> symbolRate [kHz] */
+ fecBitsDesired *= 500; /* meas. period [ms] */
+
+ /* Annex A/C: bits/RsPeriod = 204 * 8 = 1632 */
+ /* fecRsPeriodTotal = fecBitsDesired / 1632 */
+ fecRsPeriodTotal = (fecBitsDesired / 1632UL) + 1; /* roughly ceil */
+
+ /* fecRsPeriodTotal = fecRsPrescale * fecRsPeriod */
+ fecRsPrescale = 1 + (u16) (fecRsPeriodTotal >> 16);
+ if (fecRsPrescale == 0) {
+ /* Divide by zero (though impossible) */
+ status = -EINVAL;
+ if (status < 0)
+ goto error;
+ }
+ fecRsPeriod =
+ ((u16) fecRsPeriodTotal +
+ (fecRsPrescale >> 1)) / fecRsPrescale;
+
+ /* write corresponding registers */
+ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, fecRsPeriod);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, fecRsPrescale);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_FAIL_PERIOD__A, fecRsPeriod);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SetQAM16(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 13517);
+ if (status < 0)
+ goto error;
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM16);
+ if (status < 0)
+ goto error;
+
+ /* QAM Loop Controller Coeficients */
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 32);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 140);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 95);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 120);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 230);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 105);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 24);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 220);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -65);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -127);
+ if (status < 0)
+ goto error;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM32 specific setup
+* \param demod instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM32(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 6707);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM32);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 90);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 170);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 140);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) -8);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) -16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -26);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -56);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -86);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM64 specific setup
+* \param demod instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM64(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13336);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12618);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 11988);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13809);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13809);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15609);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM64);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 30);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 110);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 200);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 95);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 15);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 141);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -45);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -80);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM128 specific setup
+* \param demod: instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM128(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6564);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6598);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6394);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6409);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6656);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 7238);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Slicer Settings */
+
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM128);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 120);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 64);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 140);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 5);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12);
+ if (status < 0)
+ goto error;
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 65);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -1);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -23);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM256 specific setup
+* \param demod: instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM256(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 11502);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12084);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 12543);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 12931);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13629);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15385);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM256);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 250);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 125);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 150);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 110);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 74);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 18);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 13);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -8);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+
+/*============================================================================*/
+/**
+* \brief Reset QAM block.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+static int QAMResetQAM(struct drxk_state *state)
+{
+ int status;
+ u16 cmdResult;
+
+ dprintk(1, "\n");
+ /* Stop QAM comstate->m_exec */
+ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Set QAM symbolrate.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+static int QAMSetSymbolrate(struct drxk_state *state)
+{
+ u32 adcFrequency = 0;
+ u32 symbFreq = 0;
+ u32 iqmRcRate = 0;
+ u16 ratesel = 0;
+ u32 lcSymbRate = 0;
+ int status;
+
+ dprintk(1, "\n");
+ /* Select & calculate correct IQM rate */
+ adcFrequency = (state->m_sysClockFreq * 1000) / 3;
+ ratesel = 0;
+ /* printk(KERN_DEBUG "drxk: SR %d\n", state->param.u.qam.symbol_rate); */
+ if (state->param.u.qam.symbol_rate <= 1188750)
+ ratesel = 3;
+ else if (state->param.u.qam.symbol_rate <= 2377500)
+ ratesel = 2;
+ else if (state->param.u.qam.symbol_rate <= 4755000)
+ ratesel = 1;
+ status = write16(state, IQM_FD_RATESEL__A, ratesel);
+ if (status < 0)
+ goto error;
+
+ /*
+ IqmRcRate = ((Fadc / (symbolrate * (4<<ratesel))) - 1) * (1<<23)
+ */
+ symbFreq = state->param.u.qam.symbol_rate * (1 << ratesel);
+ if (symbFreq == 0) {
+ /* Divide by zero */
+ status = -EINVAL;
+ goto error;
+ }
+ iqmRcRate = (adcFrequency / symbFreq) * (1 << 21) +
+ (Frac28a((adcFrequency % symbFreq), symbFreq) >> 7) -
+ (1 << 23);
+ status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRate);
+ if (status < 0)
+ goto error;
+ state->m_iqmRcRate = iqmRcRate;
+ /*
+ LcSymbFreq = round (.125 * symbolrate / adcFreq * (1<<15))
+ */
+ symbFreq = state->param.u.qam.symbol_rate;
+ if (adcFrequency == 0) {
+ /* Divide by zero */
+ status = -EINVAL;
+ goto error;
+ }
+ lcSymbRate = (symbFreq / adcFrequency) * (1 << 12) +
+ (Frac28a((symbFreq % adcFrequency), adcFrequency) >>
+ 16);
+ if (lcSymbRate > 511)
+ lcSymbRate = 511;
+ status = write16(state, QAM_LC_SYMBOL_FREQ__A, (u16) lcSymbRate);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Get QAM lock status.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+
+static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus)
+{
+ int status;
+ u16 Result[2] = { 0, 0 };
+
+ dprintk(1, "\n");
+ *pLockStatus = NOT_LOCKED;
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2,
+ Result);
+ if (status < 0)
+ printk(KERN_ERR "drxk: %s status = %08x\n", __func__, status);
+
+ if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) {
+ /* 0x0000 NOT LOCKED */
+ } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_LOCKED) {
+ /* 0x4000 DEMOD LOCKED */
+ *pLockStatus = DEMOD_LOCK;
+ } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK) {
+ /* 0x8000 DEMOD + FEC LOCKED (system lock) */
+ *pLockStatus = MPEG_LOCK;
+ } else {
+ /* 0xC000 NEVER LOCKED */
+ /* (system will never be able to lock to the signal) */
+ /* TODO: check this, intermediate & standard specific lock states are not
+ taken into account here */
+ *pLockStatus = NEVER_LOCK;
+ }
+ return status;
+}
+
+#define QAM_MIRROR__M 0x03
+#define QAM_MIRROR_NORMAL 0x00
+#define QAM_MIRRORED 0x01
+#define QAM_MIRROR_AUTO_ON 0x02
+#define QAM_LOCKRANGE__M 0x10
+#define QAM_LOCKRANGE_NORMAL 0x10
+
+static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
+ s32 tunerFreqOffset)
+{
+ int status;
+ u16 setParamParameters[4] = { 0, 0, 0, 0 };
+ u16 cmdResult;
+
+ dprintk(1, "\n");
+ /*
+ * STEP 1: reset demodulator
+ * resets FEC DI and FEC RS
+ * resets QAM block
+ * resets SCU variables
+ */
+ status = write16(state, FEC_DI_COMM_EXEC__A, FEC_DI_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_RS_COMM_EXEC__A, FEC_RS_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = QAMResetQAM(state);
+ if (status < 0)
+ goto error;
+
+ /*
+ * STEP 2: configure demodulator
+ * -set params; resets IQM,QAM,FEC HW; initializes some
+ * SCU variables
+ */
+ status = QAMSetSymbolrate(state);
+ if (status < 0)
+ goto error;
+
+ /* Set params */
+ switch (state->param.u.qam.modulation) {
+ case QAM_256:
+ state->m_Constellation = DRX_CONSTELLATION_QAM256;
+ break;
+ case QAM_AUTO:
+ case QAM_64:
+ state->m_Constellation = DRX_CONSTELLATION_QAM64;
+ break;
+ case QAM_16:
+ state->m_Constellation = DRX_CONSTELLATION_QAM16;
+ break;
+ case QAM_32:
+ state->m_Constellation = DRX_CONSTELLATION_QAM32;
+ break;
+ case QAM_128:
+ state->m_Constellation = DRX_CONSTELLATION_QAM128;
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ if (status < 0)
+ goto error;
+ setParamParameters[0] = state->m_Constellation; /* constellation */
+ setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */
+ if (state->m_OperationMode == OM_QAM_ITU_C)
+ setParamParameters[2] = QAM_TOP_ANNEX_C;
+ else
+ setParamParameters[2] = QAM_TOP_ANNEX_A;
+ setParamParameters[3] |= (QAM_MIRROR_AUTO_ON);
+ /* Env parameters */
+ /* check for LOCKRANGE Extented */
+ /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */
+
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 4, setParamParameters, 1, &cmdResult);
+ if (status < 0) {
+ /* Fall-back to the simpler call */
+ if (state->m_OperationMode == OM_QAM_ITU_C)
+ setParamParameters[0] = QAM_TOP_ANNEX_C;
+ else
+ setParamParameters[0] = QAM_TOP_ANNEX_A;
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 1, setParamParameters, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ setParamParameters[0] = state->m_Constellation; /* constellation */
+ setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 2, setParamParameters, 1, &cmdResult);
+ }
+ if (status < 0)
+ goto error;
+
+ /*
+ * STEP 3: enable the system in a mode where the ADC provides valid
+ * signal setup constellation independent registers
+ */
+#if 0
+ status = SetFrequency(channel, tunerFreqOffset));
+ if (status < 0)
+ goto error;
+#endif
+ status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true);
+ if (status < 0)
+ goto error;
+
+ /* Setup BER measurement */
+ status = SetQAMMeasurement(state, state->m_Constellation, state->param.u. qam.symbol_rate);
+ if (status < 0)
+ goto error;
+
+ /* Reset default values */
+ status = write16(state, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE);
+ if (status < 0)
+ goto error;
+
+ /* Reset default LC values */
+ status = write16(state, QAM_LC_RATE_LIMIT__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_LPF_FACTORP__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_LPF_FACTORI__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_MODE__A, 7);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_LC_QUAL_TAB0__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB1__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB2__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB3__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB4__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB5__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB6__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB8__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB9__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB10__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB12__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB15__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB16__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB20__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB25__A, 4);
+ if (status < 0)
+ goto error;
+
+ /* Mirroring, QAM-block starting point not inverted */
+ status = write16(state, QAM_SY_SP_INV__A, QAM_SY_SP_INV_SPECTRUM_INV_DIS);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ /* STEP 4: constellation specific setup */
+ switch (state->param.u.qam.modulation) {
+ case QAM_16:
+ status = SetQAM16(state);
+ break;
+ case QAM_32:
+ status = SetQAM32(state);
+ break;
+ case QAM_AUTO:
+ case QAM_64:
+ status = SetQAM64(state);
+ break;
+ case QAM_128:
+ status = SetQAM128(state);
+ break;
+ case QAM_256:
+ status = SetQAM256(state);
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* Re-configure MPEG output, requires knowledge of channel bitrate */
+ /* extAttr->currentChannel.constellation = channel->constellation; */
+ /* extAttr->currentChannel.symbolrate = channel->symbolrate; */
+ status = MPEGTSDtoSetup(state, state->m_OperationMode);
+ if (status < 0)
+ goto error;
+
+ /* Start processes */
+ status = MPEGTSStart(state);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ /* update global DRXK data container */
+/*? extAttr->qamInterleaveMode = DRXK_QAM_I12_J17; */
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SetQAMStandard(struct drxk_state *state,
+ enum OperationMode oMode)
+{
+ int status;
+#ifdef DRXK_QAM_TAPS
+#define DRXK_QAMA_TAPS_SELECT
+#include "drxk_filters.h"
+#undef DRXK_QAMA_TAPS_SELECT
+#endif
+
+ dprintk(1, "\n");
+
+ /* added antenna switch */
+ SwitchAntennaToQAM(state);
+
+ /* Ensure correct power-up mode */
+ status = PowerUpQAM(state);
+ if (status < 0)
+ goto error;
+ /* Reset QAM block */
+ status = QAMResetQAM(state);
+ if (status < 0)
+ goto error;
+
+ /* Setup IQM */
+
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
+ if (status < 0)
+ goto error;
+
+ /* Upload IQM Channel Filter settings by
+ boot loader from ROM table */
+ switch (oMode) {
+ case OM_QAM_ITU_A:
+ status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_ITU_A, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ break;
+ case OM_QAM_ITU_C:
+ status = BLDirectCmd(state, IQM_CF_TAP_RE0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ if (status < 0)
+ goto error;
+ status = BLDirectCmd(state, IQM_CF_TAP_IM0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ break;
+ default:
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_CF_OUT_ENA__A, (1 << IQM_CF_OUT_ENA_QAM__B));
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_SYMMETRIC__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_MIDTAP__A, ((1 << IQM_CF_MIDTAP_RE__B) | (1 << IQM_CF_MIDTAP_IM__B)));
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_RC_STRETCH__A, 21);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_CLP_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_CLP_TH__A, 448);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_SNS_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_POW_MEAS_LEN__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_FS_ADJ_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_RC_ADJ_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_ADJ_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_UPD_SEL__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* IQM Impulse Noise Processing Unit */
+ status = write16(state, IQM_CF_CLP_VAL__A, 500);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DATATH__A, 1000);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_BYPASSDET__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DET_LCT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_WND_LEN__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_PKDTH__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_INC_BYPASS__A, 1);
+ if (status < 0)
+ goto error;
+
+ /* turn on IQMAF. Must be done before setAgc**() */
+ status = SetIqmAf(state, true);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_START_LOCK__A, 0x01);
+ if (status < 0)
+ goto error;
+
+ /* IQM will not be reset from here, sync ADC and update/init AGC */
+ status = ADCSynchronization(state);
+ if (status < 0)
+ goto error;
+
+ /* Set the FSM step period */
+ status = write16(state, SCU_RAM_QAM_FSM_STEP_PERIOD__A, 2000);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ /* No more resets of the IQM, current standard correctly set =>
+ now AGCs can be configured. */
+
+ status = InitAGC(state, true);
+ if (status < 0)
+ goto error;
+ status = SetPreSaw(state, &(state->m_qamPreSawCfg));
+ if (status < 0)
+ goto error;
+
+ /* Configure AGC's */
+ status = SetAgcRf(state, &(state->m_qamRfAgcCfg), true);
+ if (status < 0)
+ goto error;
+ status = SetAgcIf(state, &(state->m_qamIfAgcCfg), true);
+ if (status < 0)
+ goto error;
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int WriteGPIO(struct drxk_state *state)
+{
+ int status;
+ u16 value = 0;
+
+ dprintk(1, "\n");
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+
+ /* Write magic word to enable pdr reg write */
+ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
+ if (status < 0)
+ goto error;
+
+ if (state->m_hasSAWSW) {
+ if (state->UIO_mask & 0x0001) { /* UIO-1 */
+ /* write to io pad configuration register - output mode */
+ status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
+ if (status < 0)
+ goto error;
+
+ /* use corresponding bit in io data output registar */
+ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+ if (status < 0)
+ goto error;
+ if ((state->m_GPIO & 0x0001) == 0)
+ value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */
+ else
+ value |= 0x8000; /* write one to 15th bit - 1st UIO */
+ /* write back to io data output register */
+ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+ if (status < 0)
+ goto error;
+ }
+ if (state->UIO_mask & 0x0002) { /* UIO-2 */
+ /* write to io pad configuration register - output mode */
+ status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
+ if (status < 0)
+ goto error;
+
+ /* use corresponding bit in io data output registar */
+ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+ if (status < 0)
+ goto error;
+ if ((state->m_GPIO & 0x0002) == 0)
+ value &= 0xBFFF; /* write zero to 14th bit - 2st UIO */
+ else
+ value |= 0x4000; /* write one to 14th bit - 2st UIO */
+ /* write back to io data output register */
+ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+ if (status < 0)
+ goto error;
+ }
+ if (state->UIO_mask & 0x0004) { /* UIO-3 */
+ /* write to io pad configuration register - output mode */
+ status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
+ if (status < 0)
+ goto error;
+
+ /* use corresponding bit in io data output registar */
+ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+ if (status < 0)
+ goto error;
+ if ((state->m_GPIO & 0x0004) == 0)
+ value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */
+ else
+ value |= 0x0004; /* write one to 2nd bit - 3rd UIO */
+ /* write back to io data output register */
+ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+ if (status < 0)
+ goto error;
+ }
+ }
+ /* Write magic word to disable pdr reg write */
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SwitchAntennaToQAM(struct drxk_state *state)
+{
+ int status = 0;
+ bool gpio_state;
+
+ dprintk(1, "\n");
+
+ if (!state->antenna_gpio)
+ return 0;
+
+ gpio_state = state->m_GPIO & state->antenna_gpio;
+
+ if (state->antenna_dvbt ^ gpio_state) {
+ /* Antenna is on DVB-T mode. Switch */
+ if (state->antenna_dvbt)
+ state->m_GPIO &= ~state->antenna_gpio;
+ else
+ state->m_GPIO |= state->antenna_gpio;
+ status = WriteGPIO(state);
+ }
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SwitchAntennaToDVBT(struct drxk_state *state)
+{
+ int status = 0;
+ bool gpio_state;
+
+ dprintk(1, "\n");
+
+ if (!state->antenna_gpio)
+ return 0;
+
+ gpio_state = state->m_GPIO & state->antenna_gpio;
+
+ if (!(state->antenna_dvbt ^ gpio_state)) {
+ /* Antenna is on DVB-C mode. Switch */
+ if (state->antenna_dvbt)
+ state->m_GPIO |= state->antenna_gpio;
+ else
+ state->m_GPIO &= ~state->antenna_gpio;
+ status = WriteGPIO(state);
+ }
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+
+static int PowerDownDevice(struct drxk_state *state)
+{
+ /* Power down to requested mode */
+ /* Backup some register settings */
+ /* Set pins with possible pull-ups connected to them in input mode */
+ /* Analog power down */
+ /* ADC power down */
+ /* Power down device */
+ int status;
+
+ dprintk(1, "\n");
+ if (state->m_bPDownOpenBridge) {
+ /* Open I2C bridge before power down of DRXK */
+ status = ConfigureI2CBridge(state, true);
+ if (status < 0)
+ goto error;
+ }
+ /* driver 0.9.0 */
+ status = DVBTEnableOFDMTokenRing(state, false);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_CLOCK);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+ state->m_HICfgCtrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+ status = HI_CfgCommand(state);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int load_microcode(struct drxk_state *state, const char *mc_name)
+{
+ const struct firmware *fw = NULL;
+ int err = 0;
+
+ dprintk(1, "\n");
+
+ err = request_firmware(&fw, mc_name, state->i2c->dev.parent);
+ if (err < 0) {
+ printk(KERN_ERR
+ "drxk: Could not load firmware file %s.\n", mc_name);
+ printk(KERN_INFO
+ "drxk: Copy %s to your hotplug directory!\n", mc_name);
+ return err;
+ }
+ err = DownloadMicrocode(state, fw->data, fw->size);
+ release_firmware(fw);
+ return err;
+}
+
+static int init_drxk(struct drxk_state *state)
+{
+ int status = 0;
+ enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
+ u16 driverVersion;
+
+ dprintk(1, "\n");
+ if ((state->m_DrxkState == DRXK_UNINITIALIZED)) {
+ status = PowerUpDevice(state);
+ if (status < 0)
+ goto error;
+ status = DRXX_Open(state);
+ if (status < 0)
+ goto error;
+ /* Soft reset of OFDM-, sys- and osc-clockdomain */
+ status = write16(state, SIO_CC_SOFT_RST__A, SIO_CC_SOFT_RST_OFDM__M | SIO_CC_SOFT_RST_SYS__M | SIO_CC_SOFT_RST_OSC__M);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+ /* TODO is this needed, if yes how much delay in worst case scenario */
+ msleep(1);
+ state->m_DRXK_A3_PATCH_CODE = true;
+ status = GetDeviceCapabilities(state);
+ if (status < 0)
+ goto error;
+
+ /* Bridge delay, uses oscilator clock */
+ /* Delay = (delay (nano seconds) * oscclk (kHz))/ 1000 */
+ /* SDA brdige delay */
+ state->m_HICfgBridgeDelay =
+ (u16) ((state->m_oscClockFreq / 1000) *
+ HI_I2C_BRIDGE_DELAY) / 1000;
+ /* Clipping */
+ if (state->m_HICfgBridgeDelay >
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M) {
+ state->m_HICfgBridgeDelay =
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M;
+ }
+ /* SCL bridge delay, same as SDA for now */
+ state->m_HICfgBridgeDelay +=
+ state->m_HICfgBridgeDelay <<
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B;
+
+ status = InitHI(state);
+ if (status < 0)
+ goto error;
+ /* disable various processes */
+#if NOA1ROM
+ if (!(state->m_DRXK_A1_ROM_CODE)
+ && !(state->m_DRXK_A2_ROM_CODE))
+#endif
+ {
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+ }
+
+ /* disable MPEG port */
+ status = MPEGTSDisable(state);
+ if (status < 0)
+ goto error;
+
+ /* Stop AUD and SCU */
+ status = write16(state, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ /* enable token-ring bus through OFDM block for possible ucode upload */
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_ON);
+ if (status < 0)
+ goto error;
+
+ /* include boot loader section */
+ status = write16(state, SIO_BL_COMM_EXEC__A, SIO_BL_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = BLChainCmd(state, 0, 6, 100);
+ if (status < 0)
+ goto error;
+
+ if (!state->microcode_name)
+ load_microcode(state, "drxk_a3.mc");
+ else
+ load_microcode(state, state->microcode_name);
+
+ /* disable token-ring bus through OFDM block for possible ucode upload */
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF);
+ if (status < 0)
+ goto error;
+
+ /* Run SCU for a little while to initialize microcode version numbers */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = DRXX_Open(state);
+ if (status < 0)
+ goto error;
+ /* added for test */
+ msleep(30);
+
+ powerMode = DRXK_POWER_DOWN_OFDM;
+ status = CtrlPowerMode(state, &powerMode);
+ if (status < 0)
+ goto error;
+
+ /* Stamp driver version number in SCU data RAM in BCD code
+ Done to enable field application engineers to retreive drxdriver version
+ via I2C from SCU RAM.
+ Not using SCU command interface for SCU register access since no
+ microcode may be present.
+ */
+ driverVersion =
+ (((DRXK_VERSION_MAJOR / 100) % 10) << 12) +
+ (((DRXK_VERSION_MAJOR / 10) % 10) << 8) +
+ ((DRXK_VERSION_MAJOR % 10) << 4) +
+ (DRXK_VERSION_MINOR % 10);
+ status = write16(state, SCU_RAM_DRIVER_VER_HI__A, driverVersion);
+ if (status < 0)
+ goto error;
+ driverVersion =
+ (((DRXK_VERSION_PATCH / 1000) % 10) << 12) +
+ (((DRXK_VERSION_PATCH / 100) % 10) << 8) +
+ (((DRXK_VERSION_PATCH / 10) % 10) << 4) +
+ (DRXK_VERSION_PATCH % 10);
+ status = write16(state, SCU_RAM_DRIVER_VER_LO__A, driverVersion);
+ if (status < 0)
+ goto error;
+
+ printk(KERN_INFO "DRXK driver version %d.%d.%d\n",
+ DRXK_VERSION_MAJOR, DRXK_VERSION_MINOR,
+ DRXK_VERSION_PATCH);
+
+ /* Dirty fix of default values for ROM/PATCH microcode
+ Dirty because this fix makes it impossible to setup suitable values
+ before calling DRX_Open. This solution requires changes to RF AGC speed
+ to be done via the CTRL function after calling DRX_Open */
+
+ /* m_dvbtRfAgcCfg.speed = 3; */
+
+ /* Reset driver debug flags to 0 */
+ status = write16(state, SCU_RAM_DRIVER_DEBUG__A, 0);
+ if (status < 0)
+ goto error;
+ /* driver 0.9.0 */
+ /* Setup FEC OC:
+ NOTE: No more full FEC resets allowed afterwards!! */
+ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ /* MPEGTS functions are still the same */
+ status = MPEGTSDtoInit(state);
+ if (status < 0)
+ goto error;
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = MPEGTSConfigurePolarity(state);
+ if (status < 0)
+ goto error;
+ status = MPEGTSConfigurePins(state, state->m_enableMPEGOutput);
+ if (status < 0)
+ goto error;
+ /* added: configure GPIO */
+ status = WriteGPIO(state);
+ if (status < 0)
+ goto error;
+
+ state->m_DrxkState = DRXK_STOPPED;
+
+ if (state->m_bPowerDown) {
+ status = PowerDownDevice(state);
+ if (status < 0)
+ goto error;
+ state->m_DrxkState = DRXK_POWERED_DOWN;
+ } else
+ state->m_DrxkState = DRXK_STOPPED;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static void drxk_c_release(struct dvb_frontend *fe)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "\n");
+ kfree(state);
+}
+
+static int drxk_c_init(struct dvb_frontend *fe)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "\n");
+ if (mutex_trylock(&state->ctlock) == 0)
+ return -EBUSY;
+ SetOperationMode(state, OM_QAM_ITU_A);
+ return 0;
+}
+
+static int drxk_c_sleep(struct dvb_frontend *fe)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "\n");
+ ShutDown(state);
+ mutex_unlock(&state->ctlock);
+ return 0;
+}
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "%s\n", enable ? "enable" : "disable");
+ return ConfigureI2CBridge(state, enable ? true : false);
+}
+
+static int drxk_set_parameters(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ u32 IF;
+
+ dprintk(1, "\n");
+
+ if (!fe->ops.tuner_ops.get_if_frequency) {
+ printk(KERN_ERR
+ "drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n");
+ return -EINVAL;
+ }
+
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, p);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ state->param = *p;
+ fe->ops.tuner_ops.get_if_frequency(fe, &IF);
+ Start(state, 0, IF);
+
+ /* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */
+
+ return 0;
+}
+
+static int drxk_c_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ dprintk(1, "\n");
+ return 0;
+}
+
+static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ u32 stat;
+
+ dprintk(1, "\n");
+ *status = 0;
+ GetLockStatus(state, &stat, 0);
+ if (stat == MPEG_LOCK)
+ *status |= 0x1f;
+ if (stat == FEC_LOCK)
+ *status |= 0x0f;
+ if (stat == DEMOD_LOCK)
+ *status |= 0x07;
+ return 0;
+}
+
+static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ dprintk(1, "\n");
+
+ *ber = 0;
+ return 0;
+}
+
+static int drxk_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ u32 val = 0;
+
+ dprintk(1, "\n");
+ ReadIFAgc(state, &val);
+ *strength = val & 0xffff;
+ return 0;
+}
+
+static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ s32 snr2;
+
+ dprintk(1, "\n");
+ GetSignalToNoise(state, &snr2);
+ *snr = snr2 & 0xffff;
+ return 0;
+}
+
+static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ u16 err;
+
+ dprintk(1, "\n");
+ DVBTQAMGetAccPktErr(state, &err);
+ *ucblocks = (u32) err;
+ return 0;
+}
+
+static int drxk_c_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings
+ *sets)
+{
+ dprintk(1, "\n");
+ sets->min_delay_ms = 3000;
+ sets->max_drift = 0;
+ sets->step_size = 0;
+ return 0;
+}
+
+static void drxk_t_release(struct dvb_frontend *fe)
+{
+ /*
+ * There's nothing to release here, as the state struct
+ * is already freed by drxk_c_release.
+ */
+}
+
+static int drxk_t_init(struct dvb_frontend *fe)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "\n");
+ if (mutex_trylock(&state->ctlock) == 0)
+ return -EBUSY;
+ SetOperationMode(state, OM_DVBT);
+ return 0;
+}
+
+static int drxk_t_sleep(struct dvb_frontend *fe)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "\n");
+ mutex_unlock(&state->ctlock);
+ return 0;
+}
+
+static int drxk_t_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ dprintk(1, "\n");
+
+ return 0;
+}
+
+static struct dvb_frontend_ops drxk_c_ops = {
+ .info = {
+ .name = "DRXK DVB-C",
+ .type = FE_QAM,
+ .frequency_stepsize = 62500,
+ .frequency_min = 47000000,
+ .frequency_max = 862000000,
+ .symbol_rate_min = 870000,
+ .symbol_rate_max = 11700000,
+ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO},
+ .release = drxk_c_release,
+ .init = drxk_c_init,
+ .sleep = drxk_c_sleep,
+ .i2c_gate_ctrl = drxk_gate_ctrl,
+
+ .set_frontend = drxk_set_parameters,
+ .get_frontend = drxk_c_get_frontend,
+ .get_tune_settings = drxk_c_get_tune_settings,
+
+ .read_status = drxk_read_status,
+ .read_ber = drxk_read_ber,
+ .read_signal_strength = drxk_read_signal_strength,
+ .read_snr = drxk_read_snr,
+ .read_ucblocks = drxk_read_ucblocks,
+};
+
+static struct dvb_frontend_ops drxk_t_ops = {
+ .info = {
+ .name = "DRXK DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 47125000,
+ .frequency_max = 865000000,
+ .frequency_stepsize = 166667,
+ .frequency_tolerance = 0,
+ .caps = 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_QAM_16 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_MUTE_TS},
+ .release = drxk_t_release,
+ .init = drxk_t_init,
+ .sleep = drxk_t_sleep,
+ .i2c_gate_ctrl = drxk_gate_ctrl,
+
+ .set_frontend = drxk_set_parameters,
+ .get_frontend = drxk_t_get_frontend,
+
+ .read_status = drxk_read_status,
+ .read_ber = drxk_read_ber,
+ .read_signal_strength = drxk_read_signal_strength,
+ .read_snr = drxk_read_snr,
+ .read_ucblocks = drxk_read_ucblocks,
+};
+
+struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+ struct i2c_adapter *i2c,
+ struct dvb_frontend **fe_t)
+{
+ struct drxk_state *state = NULL;
+ u8 adr = config->adr;
+
+ dprintk(1, "\n");
+ state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ state->i2c = i2c;
+ state->demod_address = adr;
+ state->single_master = config->single_master;
+ state->microcode_name = config->microcode_name;
+ state->no_i2c_bridge = config->no_i2c_bridge;
+ state->antenna_gpio = config->antenna_gpio;
+ state->antenna_dvbt = config->antenna_dvbt;
+
+ /* NOTE: as more UIO bits will be used, add them to the mask */
+ state->UIO_mask = config->antenna_gpio;
+
+ /* Default gpio to DVB-C */
+ if (!state->antenna_dvbt && state->antenna_gpio)
+ state->m_GPIO |= state->antenna_gpio;
+ else
+ state->m_GPIO &= ~state->antenna_gpio;
+
+ mutex_init(&state->mutex);
+ mutex_init(&state->ctlock);
+
+ memcpy(&state->c_frontend.ops, &drxk_c_ops,
+ sizeof(struct dvb_frontend_ops));
+ memcpy(&state->t_frontend.ops, &drxk_t_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->c_frontend.demodulator_priv = state;
+ state->t_frontend.demodulator_priv = state;
+
+ init_state(state);
+ if (init_drxk(state) < 0)
+ goto error;
+ *fe_t = &state->t_frontend;
+
+ return &state->c_frontend;
+
+error:
+ printk(KERN_ERR "drxk: not found\n");
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(drxk_attach);
+
+MODULE_DESCRIPTION("DRX-K driver");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/drxk_hard.h b/drivers/media/dvb/frontends/drxk_hard.h
new file mode 100644
index 000000000000..a05c32eecdcc
--- /dev/null
+++ b/drivers/media/dvb/frontends/drxk_hard.h
@@ -0,0 +1,348 @@
+#include "drxk_map.h"
+
+#define DRXK_VERSION_MAJOR 0
+#define DRXK_VERSION_MINOR 9
+#define DRXK_VERSION_PATCH 4300
+
+#define HI_I2C_DELAY 42
+#define HI_I2C_BRIDGE_DELAY 350
+#define DRXK_MAX_RETRIES 100
+
+#define DRIVER_4400 1
+
+#define DRXX_JTAGID 0x039210D9
+#define DRXX_J_JTAGID 0x239310D9
+#define DRXX_K_JTAGID 0x039210D9
+
+#define DRX_UNKNOWN 254
+#define DRX_AUTO 255
+
+#define DRX_SCU_READY 0
+#define DRXK_MAX_WAITTIME (200)
+#define SCU_RESULT_OK 0
+#define SCU_RESULT_SIZE -4
+#define SCU_RESULT_INVPAR -3
+#define SCU_RESULT_UNKSTD -2
+#define SCU_RESULT_UNKCMD -1
+
+#ifndef DRXK_OFDM_TR_SHUTDOWN_TIMEOUT
+#define DRXK_OFDM_TR_SHUTDOWN_TIMEOUT (200)
+#endif
+
+#define DRXK_8VSB_MPEG_BIT_RATE 19392658UL /*bps*/
+#define DRXK_DVBT_MPEG_BIT_RATE 32000000UL /*bps*/
+#define DRXK_QAM16_MPEG_BIT_RATE 27000000UL /*bps*/
+#define DRXK_QAM32_MPEG_BIT_RATE 33000000UL /*bps*/
+#define DRXK_QAM64_MPEG_BIT_RATE 40000000UL /*bps*/
+#define DRXK_QAM128_MPEG_BIT_RATE 46000000UL /*bps*/
+#define DRXK_QAM256_MPEG_BIT_RATE 52000000UL /*bps*/
+#define DRXK_MAX_MPEG_BIT_RATE 52000000UL /*bps*/
+
+#define IQM_CF_OUT_ENA_OFDM__M 0x4
+#define IQM_FS_ADJ_SEL_B_QAM 0x1
+#define IQM_FS_ADJ_SEL_B_OFF 0x0
+#define IQM_FS_ADJ_SEL_B_VSB 0x2
+#define IQM_RC_ADJ_SEL_B_OFF 0x0
+#define IQM_RC_ADJ_SEL_B_QAM 0x1
+#define IQM_RC_ADJ_SEL_B_VSB 0x2
+
+enum OperationMode {
+ OM_NONE,
+ OM_QAM_ITU_A,
+ OM_QAM_ITU_B,
+ OM_QAM_ITU_C,
+ OM_DVBT
+};
+
+enum DRXPowerMode {
+ DRX_POWER_UP = 0,
+ DRX_POWER_MODE_1,
+ DRX_POWER_MODE_2,
+ DRX_POWER_MODE_3,
+ DRX_POWER_MODE_4,
+ DRX_POWER_MODE_5,
+ DRX_POWER_MODE_6,
+ DRX_POWER_MODE_7,
+ DRX_POWER_MODE_8,
+
+ DRX_POWER_MODE_9,
+ DRX_POWER_MODE_10,
+ DRX_POWER_MODE_11,
+ DRX_POWER_MODE_12,
+ DRX_POWER_MODE_13,
+ DRX_POWER_MODE_14,
+ DRX_POWER_MODE_15,
+ DRX_POWER_MODE_16,
+ DRX_POWER_DOWN = 255
+};
+
+
+/** /brief Intermediate power mode for DRXK, power down OFDM clock domain */
+#ifndef DRXK_POWER_DOWN_OFDM
+#define DRXK_POWER_DOWN_OFDM DRX_POWER_MODE_1
+#endif
+
+/** /brief Intermediate power mode for DRXK, power down core (sysclk) */
+#ifndef DRXK_POWER_DOWN_CORE
+#define DRXK_POWER_DOWN_CORE DRX_POWER_MODE_9
+#endif
+
+/** /brief Intermediate power mode for DRXK, power down pll (only osc runs) */
+#ifndef DRXK_POWER_DOWN_PLL
+#define DRXK_POWER_DOWN_PLL DRX_POWER_MODE_10
+#endif
+
+
+enum AGC_CTRL_MODE { DRXK_AGC_CTRL_AUTO = 0, DRXK_AGC_CTRL_USER, DRXK_AGC_CTRL_OFF };
+enum EDrxkState { DRXK_UNINITIALIZED = 0, DRXK_STOPPED, DRXK_DTV_STARTED, DRXK_ATV_STARTED, DRXK_POWERED_DOWN };
+enum EDrxkCoefArrayIndex {
+ DRXK_COEF_IDX_MN = 0,
+ DRXK_COEF_IDX_FM ,
+ DRXK_COEF_IDX_L ,
+ DRXK_COEF_IDX_LP ,
+ DRXK_COEF_IDX_BG ,
+ DRXK_COEF_IDX_DK ,
+ DRXK_COEF_IDX_I ,
+ DRXK_COEF_IDX_MAX
+};
+enum EDrxkSifAttenuation {
+ DRXK_SIF_ATTENUATION_0DB,
+ DRXK_SIF_ATTENUATION_3DB,
+ DRXK_SIF_ATTENUATION_6DB,
+ DRXK_SIF_ATTENUATION_9DB
+};
+enum EDrxkConstellation {
+ DRX_CONSTELLATION_BPSK = 0,
+ DRX_CONSTELLATION_QPSK,
+ DRX_CONSTELLATION_PSK8,
+ DRX_CONSTELLATION_QAM16,
+ DRX_CONSTELLATION_QAM32,
+ DRX_CONSTELLATION_QAM64,
+ DRX_CONSTELLATION_QAM128,
+ DRX_CONSTELLATION_QAM256,
+ DRX_CONSTELLATION_QAM512,
+ DRX_CONSTELLATION_QAM1024,
+ DRX_CONSTELLATION_UNKNOWN = DRX_UNKNOWN,
+ DRX_CONSTELLATION_AUTO = DRX_AUTO
+};
+enum EDrxkInterleaveMode {
+ DRXK_QAM_I12_J17 = 16,
+ DRXK_QAM_I_UNKNOWN = DRX_UNKNOWN
+};
+enum {
+ DRXK_SPIN_A1 = 0,
+ DRXK_SPIN_A2,
+ DRXK_SPIN_A3,
+ DRXK_SPIN_UNKNOWN
+};
+
+enum DRXKCfgDvbtSqiSpeed {
+ DRXK_DVBT_SQI_SPEED_FAST = 0,
+ DRXK_DVBT_SQI_SPEED_MEDIUM,
+ DRXK_DVBT_SQI_SPEED_SLOW,
+ DRXK_DVBT_SQI_SPEED_UNKNOWN = DRX_UNKNOWN
+} ;
+
+enum DRXFftmode_t {
+ DRX_FFTMODE_2K = 0,
+ DRX_FFTMODE_4K,
+ DRX_FFTMODE_8K,
+ DRX_FFTMODE_UNKNOWN = DRX_UNKNOWN,
+ DRX_FFTMODE_AUTO = DRX_AUTO
+};
+
+enum DRXMPEGStrWidth_t {
+ DRX_MPEG_STR_WIDTH_1,
+ DRX_MPEG_STR_WIDTH_8
+};
+
+enum DRXQamLockRange_t {
+ DRX_QAM_LOCKRANGE_NORMAL,
+ DRX_QAM_LOCKRANGE_EXTENDED
+};
+
+struct DRXKCfgDvbtEchoThres_t {
+ u16 threshold;
+ enum DRXFftmode_t fftMode;
+} ;
+
+struct SCfgAgc {
+ enum AGC_CTRL_MODE ctrlMode; /* off, user, auto */
+ u16 outputLevel; /* range dependent on AGC */
+ u16 minOutputLevel; /* range dependent on AGC */
+ u16 maxOutputLevel; /* range dependent on AGC */
+ u16 speed; /* range dependent on AGC */
+ u16 top; /* rf-agc take over point */
+ u16 cutOffCurrent; /* rf-agc is accelerated if output current
+ is below cut-off current */
+ u16 IngainTgtMax;
+ u16 FastClipCtrlDelay;
+};
+
+struct SCfgPreSaw {
+ u16 reference; /* pre SAW reference value, range 0 .. 31 */
+ bool usePreSaw; /* TRUE algorithms must use pre SAW sense */
+};
+
+struct DRXKOfdmScCmd_t {
+ u16 cmd; /**< Command number */
+ u16 subcmd; /**< Sub-command parameter*/
+ u16 param0; /**< General purpous param */
+ u16 param1; /**< General purpous param */
+ u16 param2; /**< General purpous param */
+ u16 param3; /**< General purpous param */
+ u16 param4; /**< General purpous param */
+};
+
+struct drxk_state {
+ struct dvb_frontend c_frontend;
+ struct dvb_frontend t_frontend;
+ struct dvb_frontend_parameters param;
+ struct device *dev;
+
+ struct i2c_adapter *i2c;
+ u8 demod_address;
+ void *priv;
+
+ struct mutex mutex;
+ struct mutex ctlock;
+
+ u32 m_Instance; /**< Channel 1,2,3 or 4 */
+
+ int m_ChunkSize;
+ u8 Chunk[256];
+
+ bool m_hasLNA;
+ bool m_hasDVBT;
+ bool m_hasDVBC;
+ bool m_hasAudio;
+ bool m_hasATV;
+ bool m_hasOOB;
+ bool m_hasSAWSW; /**< TRUE if mat_tx is available */
+ bool m_hasGPIO1; /**< TRUE if mat_rx is available */
+ bool m_hasGPIO2; /**< TRUE if GPIO is available */
+ bool m_hasIRQN; /**< TRUE if IRQN is available */
+ u16 m_oscClockFreq;
+ u16 m_HICfgTimingDiv;
+ u16 m_HICfgBridgeDelay;
+ u16 m_HICfgWakeUpKey;
+ u16 m_HICfgTimeout;
+ u16 m_HICfgCtrl;
+ s32 m_sysClockFreq; /**< system clock frequency in kHz */
+
+ enum EDrxkState m_DrxkState; /**< State of Drxk (init,stopped,started) */
+ enum OperationMode m_OperationMode; /**< digital standards */
+ struct SCfgAgc m_vsbRfAgcCfg; /**< settings for VSB RF-AGC */
+ struct SCfgAgc m_vsbIfAgcCfg; /**< settings for VSB IF-AGC */
+ u16 m_vsbPgaCfg; /**< settings for VSB PGA */
+ struct SCfgPreSaw m_vsbPreSawCfg; /**< settings for pre SAW sense */
+ s32 m_Quality83percent; /**< MER level (*0.1 dB) for 83% quality indication */
+ s32 m_Quality93percent; /**< MER level (*0.1 dB) for 93% quality indication */
+ bool m_smartAntInverted;
+ bool m_bDebugEnableBridge;
+ bool m_bPDownOpenBridge; /**< only open DRXK bridge before power-down once it has been accessed */
+ bool m_bPowerDown; /**< Power down when not used */
+
+ u32 m_IqmFsRateOfs; /**< frequency shift as written to DRXK register (28bit fixpoint) */
+
+ bool m_enableMPEGOutput; /**< If TRUE, enable MPEG output */
+ bool m_insertRSByte; /**< If TRUE, insert RS byte */
+ bool m_enableParallel; /**< If TRUE, parallel out otherwise serial */
+ bool m_invertDATA; /**< If TRUE, invert DATA signals */
+ bool m_invertERR; /**< If TRUE, invert ERR signal */
+ bool m_invertSTR; /**< If TRUE, invert STR signals */
+ bool m_invertVAL; /**< If TRUE, invert VAL signals */
+ bool m_invertCLK; /**< If TRUE, invert CLK signals */
+ bool m_DVBCStaticCLK;
+ bool m_DVBTStaticCLK; /**< If TRUE, static MPEG clockrate will
+ be used, otherwise clockrate will
+ adapt to the bitrate of the TS */
+ u32 m_DVBTBitrate;
+ u32 m_DVBCBitrate;
+
+ u8 m_TSDataStrength;
+ u8 m_TSClockkStrength;
+
+ enum DRXMPEGStrWidth_t m_widthSTR; /**< MPEG start width */
+ u32 m_mpegTsStaticBitrate; /**< Maximum bitrate in b/s in case
+ static clockrate is selected */
+
+ /* LARGE_INTEGER m_StartTime; */ /**< Contains the time of the last demod start */
+ s32 m_MpegLockTimeOut; /**< WaitForLockStatus Timeout (counts from start time) */
+ s32 m_DemodLockTimeOut; /**< WaitForLockStatus Timeout (counts from start time) */
+
+ bool m_disableTEIhandling;
+
+ bool m_RfAgcPol;
+ bool m_IfAgcPol;
+
+ struct SCfgAgc m_atvRfAgcCfg; /**< settings for ATV RF-AGC */
+ struct SCfgAgc m_atvIfAgcCfg; /**< settings for ATV IF-AGC */
+ struct SCfgPreSaw m_atvPreSawCfg; /**< settings for ATV pre SAW sense */
+ bool m_phaseCorrectionBypass;
+ s16 m_atvTopVidPeak;
+ u16 m_atvTopNoiseTh;
+ enum EDrxkSifAttenuation m_sifAttenuation;
+ bool m_enableCVBSOutput;
+ bool m_enableSIFOutput;
+ bool m_bMirrorFreqSpect;
+ enum EDrxkConstellation m_Constellation; /**< Constellation type of the channel */
+ u32 m_CurrSymbolRate; /**< Current QAM symbol rate */
+ struct SCfgAgc m_qamRfAgcCfg; /**< settings for QAM RF-AGC */
+ struct SCfgAgc m_qamIfAgcCfg; /**< settings for QAM IF-AGC */
+ u16 m_qamPgaCfg; /**< settings for QAM PGA */
+ struct SCfgPreSaw m_qamPreSawCfg; /**< settings for QAM pre SAW sense */
+ enum EDrxkInterleaveMode m_qamInterleaveMode; /**< QAM Interleave mode */
+ u16 m_fecRsPlen;
+ u16 m_fecRsPrescale;
+
+ enum DRXKCfgDvbtSqiSpeed m_sqiSpeed;
+
+ u16 m_GPIO;
+ u16 m_GPIOCfg;
+
+ struct SCfgAgc m_dvbtRfAgcCfg; /**< settings for QAM RF-AGC */
+ struct SCfgAgc m_dvbtIfAgcCfg; /**< settings for QAM IF-AGC */
+ struct SCfgPreSaw m_dvbtPreSawCfg; /**< settings for QAM pre SAW sense */
+
+ u16 m_agcFastClipCtrlDelay;
+ bool m_adcCompPassed;
+ u16 m_adcCompCoef[64];
+ u16 m_adcState;
+
+ u8 *m_microcode;
+ int m_microcode_length;
+ bool m_DRXK_A1_PATCH_CODE;
+ bool m_DRXK_A1_ROM_CODE;
+ bool m_DRXK_A2_ROM_CODE;
+ bool m_DRXK_A3_ROM_CODE;
+ bool m_DRXK_A2_PATCH_CODE;
+ bool m_DRXK_A3_PATCH_CODE;
+
+ bool m_rfmirror;
+ u8 m_deviceSpin;
+ u32 m_iqmRcRate;
+
+ enum DRXPowerMode m_currentPowerMode;
+
+ /*
+ * Configurable parameters at the driver. They stores the values found
+ * at struct drxk_config.
+ */
+
+ u16 UIO_mask; /* Bits used by UIO */
+
+ bool single_master;
+ bool no_i2c_bridge;
+ bool antenna_dvbt;
+ u16 antenna_gpio;
+
+ const char *microcode_name;
+};
+
+#define NEVER_LOCK 0
+#define NOT_LOCKED 1
+#define DEMOD_LOCK 2
+#define FEC_LOCK 3
+#define MPEG_LOCK 4
+
diff --git a/drivers/media/dvb/frontends/drxk_map.h b/drivers/media/dvb/frontends/drxk_map.h
new file mode 100644
index 000000000000..9b11a8328869
--- /dev/null
+++ b/drivers/media/dvb/frontends/drxk_map.h
@@ -0,0 +1,449 @@
+#define AUD_COMM_EXEC__A 0x1000000
+#define AUD_COMM_EXEC_STOP 0x0
+#define FEC_COMM_EXEC__A 0x1C00000
+#define FEC_COMM_EXEC_STOP 0x0
+#define FEC_COMM_EXEC_ACTIVE 0x1
+#define FEC_DI_COMM_EXEC__A 0x1C20000
+#define FEC_DI_COMM_EXEC_STOP 0x0
+#define FEC_DI_INPUT_CTL__A 0x1C20016
+#define FEC_RS_COMM_EXEC__A 0x1C30000
+#define FEC_RS_COMM_EXEC_STOP 0x0
+#define FEC_RS_MEASUREMENT_PERIOD__A 0x1C30012
+#define FEC_RS_MEASUREMENT_PRESCALE__A 0x1C30013
+#define FEC_OC_MODE__A 0x1C40011
+#define FEC_OC_MODE_PARITY__M 0x1
+#define FEC_OC_DTO_MODE__A 0x1C40014
+#define FEC_OC_DTO_MODE_DYNAMIC__M 0x1
+#define FEC_OC_DTO_MODE_OFFSET_ENABLE__M 0x4
+#define FEC_OC_DTO_PERIOD__A 0x1C40015
+#define FEC_OC_DTO_BURST_LEN__A 0x1C40018
+#define FEC_OC_FCT_MODE__A 0x1C4001A
+#define FEC_OC_FCT_MODE__PRE 0x0
+#define FEC_OC_FCT_MODE_RAT_ENA__M 0x1
+#define FEC_OC_FCT_MODE_VIRT_ENA__M 0x2
+#define FEC_OC_TMD_MODE__A 0x1C4001E
+#define FEC_OC_TMD_COUNT__A 0x1C4001F
+#define FEC_OC_TMD_HI_MARGIN__A 0x1C40020
+#define FEC_OC_TMD_LO_MARGIN__A 0x1C40021
+#define FEC_OC_TMD_INT_UPD_RATE__A 0x1C40023
+#define FEC_OC_AVR_PARM_A__A 0x1C40026
+#define FEC_OC_AVR_PARM_B__A 0x1C40027
+#define FEC_OC_RCN_GAIN__A 0x1C4002E
+#define FEC_OC_RCN_CTL_RATE_LO__A 0x1C40030
+#define FEC_OC_RCN_CTL_STEP_LO__A 0x1C40032
+#define FEC_OC_RCN_CTL_STEP_HI__A 0x1C40033
+#define FEC_OC_SNC_MODE__A 0x1C40040
+#define FEC_OC_SNC_MODE_SHUTDOWN__M 0x10
+#define FEC_OC_SNC_LWM__A 0x1C40041
+#define FEC_OC_SNC_HWM__A 0x1C40042
+#define FEC_OC_SNC_UNLOCK__A 0x1C40043
+#define FEC_OC_SNC_FAIL_PERIOD__A 0x1C40046
+#define FEC_OC_IPR_MODE__A 0x1C40048
+#define FEC_OC_IPR_MODE_SERIAL__M 0x1
+#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M 0x4
+#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__M 0x10
+#define FEC_OC_IPR_INVERT__A 0x1C40049
+#define FEC_OC_IPR_INVERT_MD0__M 0x1
+#define FEC_OC_IPR_INVERT_MD1__M 0x2
+#define FEC_OC_IPR_INVERT_MD2__M 0x4
+#define FEC_OC_IPR_INVERT_MD3__M 0x8
+#define FEC_OC_IPR_INVERT_MD4__M 0x10
+#define FEC_OC_IPR_INVERT_MD5__M 0x20
+#define FEC_OC_IPR_INVERT_MD6__M 0x40
+#define FEC_OC_IPR_INVERT_MD7__M 0x80
+#define FEC_OC_IPR_INVERT_MERR__M 0x100
+#define FEC_OC_IPR_INVERT_MSTRT__M 0x200
+#define FEC_OC_IPR_INVERT_MVAL__M 0x400
+#define FEC_OC_IPR_INVERT_MCLK__M 0x800
+#define FEC_OC_OCR_INVERT__A 0x1C40052
+#define IQM_COMM_EXEC__A 0x1800000
+#define IQM_COMM_EXEC_B_STOP 0x0
+#define IQM_COMM_EXEC_B_ACTIVE 0x1
+#define IQM_FS_RATE_OFS_LO__A 0x1820010
+#define IQM_FS_ADJ_SEL__A 0x1820014
+#define IQM_FS_ADJ_SEL_B_OFF 0x0
+#define IQM_FS_ADJ_SEL_B_QAM 0x1
+#define IQM_FS_ADJ_SEL_B_VSB 0x2
+#define IQM_FD_RATESEL__A 0x1830010
+#define IQM_RC_RATE_OFS_LO__A 0x1840010
+#define IQM_RC_RATE_OFS_LO__W 16
+#define IQM_RC_RATE_OFS_LO__M 0xFFFF
+#define IQM_RC_RATE_OFS_HI__M 0xFF
+#define IQM_RC_ADJ_SEL__A 0x1840014
+#define IQM_RC_ADJ_SEL_B_OFF 0x0
+#define IQM_RC_ADJ_SEL_B_QAM 0x1
+#define IQM_RC_ADJ_SEL_B_VSB 0x2
+#define IQM_RC_STRETCH__A 0x1840016
+#define IQM_CF_COMM_INT_MSK__A 0x1860006
+#define IQM_CF_SYMMETRIC__A 0x1860010
+#define IQM_CF_MIDTAP__A 0x1860011
+#define IQM_CF_MIDTAP_RE__B 0
+#define IQM_CF_MIDTAP_IM__B 1
+#define IQM_CF_OUT_ENA__A 0x1860012
+#define IQM_CF_OUT_ENA_QAM__B 1
+#define IQM_CF_OUT_ENA_OFDM__M 0x4
+#define IQM_CF_ADJ_SEL__A 0x1860013
+#define IQM_CF_SCALE__A 0x1860014
+#define IQM_CF_SCALE_SH__A 0x1860015
+#define IQM_CF_SCALE_SH__PRE 0x0
+#define IQM_CF_POW_MEAS_LEN__A 0x1860017
+#define IQM_CF_DS_ENA__A 0x1860019
+#define IQM_CF_TAP_RE0__A 0x1860020
+#define IQM_CF_TAP_IM0__A 0x1860040
+#define IQM_CF_CLP_VAL__A 0x1860060
+#define IQM_CF_DATATH__A 0x1860061
+#define IQM_CF_PKDTH__A 0x1860062
+#define IQM_CF_WND_LEN__A 0x1860063
+#define IQM_CF_DET_LCT__A 0x1860064
+#define IQM_CF_BYPASSDET__A 0x1860067
+#define IQM_AF_COMM_EXEC__A 0x1870000
+#define IQM_AF_COMM_EXEC_ACTIVE 0x1
+#define IQM_AF_CLKNEG__A 0x1870012
+#define IQM_AF_CLKNEG_CLKNEGDATA__M 0x2
+#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS 0x0
+#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG 0x2
+#define IQM_AF_START_LOCK__A 0x187001B
+#define IQM_AF_PHASE0__A 0x187001C
+#define IQM_AF_PHASE1__A 0x187001D
+#define IQM_AF_PHASE2__A 0x187001E
+#define IQM_AF_CLP_LEN__A 0x1870023
+#define IQM_AF_CLP_TH__A 0x1870024
+#define IQM_AF_SNS_LEN__A 0x1870026
+#define IQM_AF_AGC_IF__A 0x1870028
+#define IQM_AF_AGC_RF__A 0x1870029
+#define IQM_AF_PDREF__A 0x187002B
+#define IQM_AF_PDREF__M 0x1F
+#define IQM_AF_STDBY__A 0x187002C
+#define IQM_AF_STDBY_STDBY_ADC_STANDBY 0x2
+#define IQM_AF_STDBY_STDBY_AMP_STANDBY 0x4
+#define IQM_AF_STDBY_STDBY_PD_STANDBY 0x8
+#define IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY 0x10
+#define IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY 0x20
+#define IQM_AF_AMUX__A 0x187002D
+#define IQM_AF_AMUX_SIGNAL2ADC 0x1
+#define IQM_AF_UPD_SEL__A 0x187002F
+#define IQM_AF_INC_LCT__A 0x1870034
+#define IQM_AF_INC_BYPASS__A 0x1870036
+#define OFDM_CP_COMM_EXEC__A 0x2800000
+#define OFDM_CP_COMM_EXEC_STOP 0x0
+#define OFDM_EC_SB_PRIOR__A 0x3410013
+#define OFDM_EC_SB_PRIOR_HI 0x0
+#define OFDM_EC_SB_PRIOR_LO 0x1
+#define OFDM_EQ_TOP_TD_TPS_CONST__A 0x3010054
+#define OFDM_EQ_TOP_TD_TPS_CONST__M 0x3
+#define OFDM_EQ_TOP_TD_TPS_CONST_64QAM 0x2
+#define OFDM_EQ_TOP_TD_TPS_CODE_HP__A 0x3010056
+#define OFDM_EQ_TOP_TD_TPS_CODE_HP__M 0x7
+#define OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8 0x4
+#define OFDM_EQ_TOP_TD_SQR_ERR_I__A 0x301005E
+#define OFDM_EQ_TOP_TD_SQR_ERR_Q__A 0x301005F
+#define OFDM_EQ_TOP_TD_SQR_ERR_EXP__A 0x3010060
+#define OFDM_EQ_TOP_TD_REQ_SMB_CNT__A 0x3010061
+#define OFDM_EQ_TOP_TD_TPS_PWR_OFS__A 0x3010062
+#define OFDM_LC_COMM_EXEC__A 0x3800000
+#define OFDM_LC_COMM_EXEC_STOP 0x0
+#define OFDM_SC_COMM_EXEC__A 0x3C00000
+#define OFDM_SC_COMM_EXEC_STOP 0x0
+#define OFDM_SC_COMM_STATE__A 0x3C00001
+#define OFDM_SC_RA_RAM_PARAM0__A 0x3C20040
+#define OFDM_SC_RA_RAM_PARAM1__A 0x3C20041
+#define OFDM_SC_RA_RAM_CMD_ADDR__A 0x3C20042
+#define OFDM_SC_RA_RAM_CMD__A 0x3C20043
+#define OFDM_SC_RA_RAM_CMD_NULL 0x0
+#define OFDM_SC_RA_RAM_CMD_PROC_START 0x1
+#define OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM 0x3
+#define OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM 0x4
+#define OFDM_SC_RA_RAM_CMD_GET_OP_PARAM 0x5
+#define OFDM_SC_RA_RAM_CMD_USER_IO 0x6
+#define OFDM_SC_RA_RAM_CMD_SET_TIMER 0x7
+#define OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING 0x8
+#define OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M 0x1
+#define OFDM_SC_RA_RAM_LOCKTRACK_MIN 0x1
+#define OFDM_SC_RA_RAM_OP_PARAM__A 0x3C20048
+#define OFDM_SC_RA_RAM_OP_PARAM_MODE__M 0x3
+#define OFDM_SC_RA_RAM_OP_PARAM_MODE_2K 0x0
+#define OFDM_SC_RA_RAM_OP_PARAM_MODE_8K 0x1
+#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_32 0x0
+#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_16 0x4
+#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_8 0x8
+#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_4 0xC
+#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK 0x0
+#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16 0x10
+#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64 0x20
+#define OFDM_SC_RA_RAM_OP_PARAM_HIER_NO 0x0
+#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A1 0x40
+#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A2 0x80
+#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A4 0xC0
+#define OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2 0x0
+#define OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3 0x200
+#define OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4 0x400
+#define OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6 0x600
+#define OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8 0x800
+#define OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI 0x0
+#define OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO 0x1000
+#define OFDM_SC_RA_RAM_OP_AUTO_MODE__M 0x1
+#define OFDM_SC_RA_RAM_OP_AUTO_GUARD__M 0x2
+#define OFDM_SC_RA_RAM_OP_AUTO_CONST__M 0x4
+#define OFDM_SC_RA_RAM_OP_AUTO_HIER__M 0x8
+#define OFDM_SC_RA_RAM_OP_AUTO_RATE__M 0x10
+#define OFDM_SC_RA_RAM_LOCK__A 0x3C2004B
+#define OFDM_SC_RA_RAM_LOCK_DEMOD__M 0x1
+#define OFDM_SC_RA_RAM_LOCK_FEC__M 0x2
+#define OFDM_SC_RA_RAM_LOCK_MPEG__M 0x4
+#define OFDM_SC_RA_RAM_LOCK_NODVBT__M 0x8
+#define OFDM_SC_RA_RAM_BE_OPT_DELAY__A 0x3C2004D
+#define OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A 0x3C2004E
+#define OFDM_SC_RA_RAM_ECHO_THRES__A 0x3C2004F
+#define OFDM_SC_RA_RAM_ECHO_THRES_8K__B 0
+#define OFDM_SC_RA_RAM_ECHO_THRES_8K__M 0xFF
+#define OFDM_SC_RA_RAM_ECHO_THRES_2K__B 8
+#define OFDM_SC_RA_RAM_ECHO_THRES_2K__M 0xFF00
+#define OFDM_SC_RA_RAM_CONFIG__A 0x3C20050
+#define OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M 0x800
+#define OFDM_SC_RA_RAM_FR_THRES_8K__A 0x3C2007D
+#define OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A 0x3C200E0
+#define OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A 0x3C200E1
+#define OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A 0x3C200E3
+#define OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A 0x3C200E4
+#define OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A 0x3C200F8
+#define QAM_COMM_EXEC__A 0x1400000
+#define QAM_COMM_EXEC_STOP 0x0
+#define QAM_COMM_EXEC_ACTIVE 0x1
+#define QAM_TOP_ANNEX_A 0x0
+#define QAM_TOP_ANNEX_C 0x2
+#define QAM_SL_ERR_POWER__A 0x1430017
+#define QAM_DQ_QUAL_FUN0__A 0x1440018
+#define QAM_DQ_QUAL_FUN1__A 0x1440019
+#define QAM_DQ_QUAL_FUN2__A 0x144001A
+#define QAM_DQ_QUAL_FUN3__A 0x144001B
+#define QAM_DQ_QUAL_FUN4__A 0x144001C
+#define QAM_DQ_QUAL_FUN5__A 0x144001D
+#define QAM_LC_MODE__A 0x1450010
+#define QAM_LC_QUAL_TAB0__A 0x1450018
+#define QAM_LC_QUAL_TAB1__A 0x1450019
+#define QAM_LC_QUAL_TAB2__A 0x145001A
+#define QAM_LC_QUAL_TAB3__A 0x145001B
+#define QAM_LC_QUAL_TAB4__A 0x145001C
+#define QAM_LC_QUAL_TAB5__A 0x145001D
+#define QAM_LC_QUAL_TAB6__A 0x145001E
+#define QAM_LC_QUAL_TAB8__A 0x145001F
+#define QAM_LC_QUAL_TAB9__A 0x1450020
+#define QAM_LC_QUAL_TAB10__A 0x1450021
+#define QAM_LC_QUAL_TAB12__A 0x1450022
+#define QAM_LC_QUAL_TAB15__A 0x1450023
+#define QAM_LC_QUAL_TAB16__A 0x1450024
+#define QAM_LC_QUAL_TAB20__A 0x1450025
+#define QAM_LC_QUAL_TAB25__A 0x1450026
+#define QAM_LC_LPF_FACTORP__A 0x1450028
+#define QAM_LC_LPF_FACTORI__A 0x1450029
+#define QAM_LC_RATE_LIMIT__A 0x145002A
+#define QAM_LC_SYMBOL_FREQ__A 0x145002B
+#define QAM_SY_TIMEOUT__A 0x1470011
+#define QAM_SY_TIMEOUT__PRE 0x3A98
+#define QAM_SY_SYNC_LWM__A 0x1470012
+#define QAM_SY_SYNC_AWM__A 0x1470013
+#define QAM_SY_SYNC_HWM__A 0x1470014
+#define QAM_SY_SP_INV__A 0x1470017
+#define QAM_SY_SP_INV_SPECTRUM_INV_DIS 0x0
+#define SCU_COMM_EXEC__A 0x800000
+#define SCU_COMM_EXEC_STOP 0x0
+#define SCU_COMM_EXEC_ACTIVE 0x1
+#define SCU_COMM_EXEC_HOLD 0x2
+#define SCU_RAM_DRIVER_DEBUG__A 0x831EBF
+#define SCU_RAM_QAM_FSM_STEP_PERIOD__A 0x831EC4
+#define SCU_RAM_GPIO__A 0x831EC7
+#define SCU_RAM_GPIO_HW_LOCK_IND_DISABLE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE__A 0x831EC8
+#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__A 0x831ECB
+#define SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A 0x831F05
+#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A 0x831F15
+#define SCU_RAM_AGC_KI_CYCLEN__A 0x831F17
+#define SCU_RAM_AGC_SNS_CYCLEN__A 0x831F18
+#define SCU_RAM_AGC_RF_SNS_DEV_MAX__A 0x831F19
+#define SCU_RAM_AGC_RF_SNS_DEV_MIN__A 0x831F1A
+#define SCU_RAM_AGC_RF_MAX__A 0x831F1B
+#define SCU_RAM_AGC_CONFIG__A 0x831F24
+#define SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M 0x1
+#define SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M 0x2
+#define SCU_RAM_AGC_CONFIG_INV_IF_POL__M 0x100
+#define SCU_RAM_AGC_CONFIG_INV_RF_POL__M 0x200
+#define SCU_RAM_AGC_KI__A 0x831F25
+#define SCU_RAM_AGC_KI_RF__B 4
+#define SCU_RAM_AGC_KI_RF__M 0xF0
+#define SCU_RAM_AGC_KI_IF__B 8
+#define SCU_RAM_AGC_KI_IF__M 0xF00
+#define SCU_RAM_AGC_KI_RED__A 0x831F26
+#define SCU_RAM_AGC_KI_RED_RAGC_RED__B 2
+#define SCU_RAM_AGC_KI_RED_RAGC_RED__M 0xC
+#define SCU_RAM_AGC_KI_RED_IAGC_RED__B 4
+#define SCU_RAM_AGC_KI_RED_IAGC_RED__M 0x30
+#define SCU_RAM_AGC_KI_INNERGAIN_MIN__A 0x831F27
+#define SCU_RAM_AGC_KI_MINGAIN__A 0x831F28
+#define SCU_RAM_AGC_KI_MAXGAIN__A 0x831F29
+#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__A 0x831F2A
+#define SCU_RAM_AGC_KI_MIN__A 0x831F2B
+#define SCU_RAM_AGC_KI_MAX__A 0x831F2C
+#define SCU_RAM_AGC_CLP_SUM__A 0x831F2D
+#define SCU_RAM_AGC_CLP_SUM_MIN__A 0x831F2E
+#define SCU_RAM_AGC_CLP_SUM_MAX__A 0x831F2F
+#define SCU_RAM_AGC_CLP_CYCLEN__A 0x831F30
+#define SCU_RAM_AGC_CLP_CYCCNT__A 0x831F31
+#define SCU_RAM_AGC_CLP_DIR_TO__A 0x831F32
+#define SCU_RAM_AGC_CLP_DIR_WD__A 0x831F33
+#define SCU_RAM_AGC_CLP_DIR_STP__A 0x831F34
+#define SCU_RAM_AGC_SNS_SUM__A 0x831F35
+#define SCU_RAM_AGC_SNS_SUM_MIN__A 0x831F36
+#define SCU_RAM_AGC_SNS_SUM_MAX__A 0x831F37
+#define SCU_RAM_AGC_SNS_CYCCNT__A 0x831F38
+#define SCU_RAM_AGC_SNS_DIR_TO__A 0x831F39
+#define SCU_RAM_AGC_SNS_DIR_WD__A 0x831F3A
+#define SCU_RAM_AGC_SNS_DIR_STP__A 0x831F3B
+#define SCU_RAM_AGC_INGAIN_TGT__A 0x831F3D
+#define SCU_RAM_AGC_INGAIN_TGT_MIN__A 0x831F3E
+#define SCU_RAM_AGC_INGAIN_TGT_MAX__A 0x831F3F
+#define SCU_RAM_AGC_IF_IACCU_HI__A 0x831F40
+#define SCU_RAM_AGC_IF_IACCU_LO__A 0x831F41
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT__A 0x831F42
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A 0x831F43
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A 0x831F44
+#define SCU_RAM_AGC_RF_IACCU_HI__A 0x831F45
+#define SCU_RAM_AGC_RF_IACCU_LO__A 0x831F46
+#define SCU_RAM_AGC_RF_IACCU_HI_CO__A 0x831F47
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A 0x831F84
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A 0x831F85
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A 0x831F86
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A 0x831F87
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A 0x831F88
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A 0x831F89
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A 0x831F8A
+#define SCU_RAM_QAM_FSM_RTH__A 0x831F8E
+#define SCU_RAM_QAM_FSM_FTH__A 0x831F8F
+#define SCU_RAM_QAM_FSM_PTH__A 0x831F90
+#define SCU_RAM_QAM_FSM_MTH__A 0x831F91
+#define SCU_RAM_QAM_FSM_CTH__A 0x831F92
+#define SCU_RAM_QAM_FSM_QTH__A 0x831F93
+#define SCU_RAM_QAM_FSM_RATE_LIM__A 0x831F94
+#define SCU_RAM_QAM_FSM_FREQ_LIM__A 0x831F95
+#define SCU_RAM_QAM_FSM_COUNT_LIM__A 0x831F96
+#define SCU_RAM_QAM_LC_CA_COARSE__A 0x831F97
+#define SCU_RAM_QAM_LC_CA_FINE__A 0x831F99
+#define SCU_RAM_QAM_LC_CP_COARSE__A 0x831F9A
+#define SCU_RAM_QAM_LC_CP_MEDIUM__A 0x831F9B
+#define SCU_RAM_QAM_LC_CP_FINE__A 0x831F9C
+#define SCU_RAM_QAM_LC_CI_COARSE__A 0x831F9D
+#define SCU_RAM_QAM_LC_CI_MEDIUM__A 0x831F9E
+#define SCU_RAM_QAM_LC_CI_FINE__A 0x831F9F
+#define SCU_RAM_QAM_LC_EP_COARSE__A 0x831FA0
+#define SCU_RAM_QAM_LC_EP_MEDIUM__A 0x831FA1
+#define SCU_RAM_QAM_LC_EP_FINE__A 0x831FA2
+#define SCU_RAM_QAM_LC_EI_COARSE__A 0x831FA3
+#define SCU_RAM_QAM_LC_EI_MEDIUM__A 0x831FA4
+#define SCU_RAM_QAM_LC_EI_FINE__A 0x831FA5
+#define SCU_RAM_QAM_LC_CF_COARSE__A 0x831FA6
+#define SCU_RAM_QAM_LC_CF_MEDIUM__A 0x831FA7
+#define SCU_RAM_QAM_LC_CF_FINE__A 0x831FA8
+#define SCU_RAM_QAM_LC_CF1_COARSE__A 0x831FA9
+#define SCU_RAM_QAM_LC_CF1_MEDIUM__A 0x831FAA
+#define SCU_RAM_QAM_LC_CF1_FINE__A 0x831FAB
+#define SCU_RAM_QAM_SL_SIG_POWER__A 0x831FAC
+#define SCU_RAM_QAM_EQ_CMA_RAD0__A 0x831FAD
+#define SCU_RAM_QAM_EQ_CMA_RAD1__A 0x831FAE
+#define SCU_RAM_QAM_EQ_CMA_RAD2__A 0x831FAF
+#define SCU_RAM_QAM_EQ_CMA_RAD3__A 0x831FB0
+#define SCU_RAM_QAM_EQ_CMA_RAD4__A 0x831FB1
+#define SCU_RAM_QAM_EQ_CMA_RAD5__A 0x831FB2
+#define SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED 0x4000
+#define SCU_RAM_QAM_LOCKED_LOCKED_LOCKED 0x8000
+#define SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK 0xC000
+#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A 0x831FEA
+#define SCU_RAM_DRIVER_VER_HI__A 0x831FEB
+#define SCU_RAM_DRIVER_VER_LO__A 0x831FEC
+#define SCU_RAM_PARAM_15__A 0x831FED
+#define SCU_RAM_PARAM_0__A 0x831FFC
+#define SCU_RAM_COMMAND__A 0x831FFD
+#define SCU_RAM_COMMAND_CMD_DEMOD_RESET 0x1
+#define SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV 0x2
+#define SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM 0x3
+#define SCU_RAM_COMMAND_CMD_DEMOD_START 0x4
+#define SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK 0x5
+#define SCU_RAM_COMMAND_CMD_DEMOD_STOP 0x9
+#define SCU_RAM_COMMAND_STANDARD_QAM 0x200
+#define SCU_RAM_COMMAND_STANDARD_OFDM 0x400
+#define SIO_TOP_COMM_KEY__A 0x41000F
+#define SIO_TOP_COMM_KEY_KEY 0xFABA
+#define SIO_TOP_JTAGID_LO__A 0x410012
+#define SIO_HI_RA_RAM_RES__A 0x420031
+#define SIO_HI_RA_RAM_CMD__A 0x420032
+#define SIO_HI_RA_RAM_CMD_RESET 0x2
+#define SIO_HI_RA_RAM_CMD_CONFIG 0x3
+#define SIO_HI_RA_RAM_CMD_BRDCTRL 0x7
+#define SIO_HI_RA_RAM_PAR_1__A 0x420033
+#define SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY 0x3945
+#define SIO_HI_RA_RAM_PAR_2__A 0x420034
+#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__M 0x7F
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN 0x0
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED 0x4
+#define SIO_HI_RA_RAM_PAR_3__A 0x420035
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M 0x7F
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B 7
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW_READ 0x0
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE 0x8
+#define SIO_HI_RA_RAM_PAR_4__A 0x420036
+#define SIO_HI_RA_RAM_PAR_5__A 0x420037
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE 0x1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M 0x8
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ 0x8
+#define SIO_HI_RA_RAM_PAR_6__A 0x420038
+#define SIO_CC_PLL_LOCK__A 0x450012
+#define SIO_CC_PWD_MODE__A 0x450015
+#define SIO_CC_PWD_MODE_LEVEL_NONE 0x0
+#define SIO_CC_PWD_MODE_LEVEL_OFDM 0x1
+#define SIO_CC_PWD_MODE_LEVEL_CLOCK 0x2
+#define SIO_CC_PWD_MODE_LEVEL_PLL 0x3
+#define SIO_CC_PWD_MODE_LEVEL_OSC 0x4
+#define SIO_CC_SOFT_RST__A 0x450016
+#define SIO_CC_SOFT_RST_OFDM__M 0x1
+#define SIO_CC_SOFT_RST_SYS__M 0x2
+#define SIO_CC_SOFT_RST_OSC__M 0x4
+#define SIO_CC_UPDATE__A 0x450017
+#define SIO_CC_UPDATE_KEY 0xFABA
+#define SIO_OFDM_SH_OFDM_RING_ENABLE__A 0x470010
+#define SIO_OFDM_SH_OFDM_RING_ENABLE_OFF 0x0
+#define SIO_OFDM_SH_OFDM_RING_ENABLE_ON 0x1
+#define SIO_OFDM_SH_OFDM_RING_STATUS__A 0x470012
+#define SIO_OFDM_SH_OFDM_RING_STATUS_DOWN 0x0
+#define SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED 0x1
+#define SIO_BL_COMM_EXEC__A 0x480000
+#define SIO_BL_COMM_EXEC_ACTIVE 0x1
+#define SIO_BL_STATUS__A 0x480010
+#define SIO_BL_MODE__A 0x480011
+#define SIO_BL_MODE_DIRECT 0x0
+#define SIO_BL_MODE_CHAIN 0x1
+#define SIO_BL_ENABLE__A 0x480012
+#define SIO_BL_ENABLE_ON 0x1
+#define SIO_BL_TGT_HDR__A 0x480014
+#define SIO_BL_TGT_ADDR__A 0x480015
+#define SIO_BL_SRC_ADDR__A 0x480016
+#define SIO_BL_SRC_LEN__A 0x480017
+#define SIO_BL_CHAIN_ADDR__A 0x480018
+#define SIO_BL_CHAIN_LEN__A 0x480019
+#define SIO_PDR_MON_CFG__A 0x7F0010
+#define SIO_PDR_UIO_IN_HI__A 0x7F0015
+#define SIO_PDR_UIO_OUT_LO__A 0x7F0016
+#define SIO_PDR_OHW_CFG__A 0x7F001F
+#define SIO_PDR_OHW_CFG_FREF_SEL__M 0x3
+#define SIO_PDR_MSTRT_CFG__A 0x7F0025
+#define SIO_PDR_MERR_CFG__A 0x7F0026
+#define SIO_PDR_MCLK_CFG__A 0x7F0028
+#define SIO_PDR_MCLK_CFG_DRIVE__B 3
+#define SIO_PDR_MVAL_CFG__A 0x7F0029
+#define SIO_PDR_MD0_CFG__A 0x7F002A
+#define SIO_PDR_MD0_CFG_DRIVE__B 3
+#define SIO_PDR_MD1_CFG__A 0x7F002B
+#define SIO_PDR_MD2_CFG__A 0x7F002C
+#define SIO_PDR_MD3_CFG__A 0x7F002D
+#define SIO_PDR_MD4_CFG__A 0x7F002F
+#define SIO_PDR_MD5_CFG__A 0x7F0030
+#define SIO_PDR_MD6_CFG__A 0x7F0031
+#define SIO_PDR_MD7_CFG__A 0x7F0032
+#define SIO_PDR_SMA_TX_CFG__A 0x7F0038
diff --git a/drivers/media/dvb/frontends/it913x-fe-priv.h b/drivers/media/dvb/frontends/it913x-fe-priv.h
new file mode 100644
index 000000000000..1c6fb4b66255
--- /dev/null
+++ b/drivers/media/dvb/frontends/it913x-fe-priv.h
@@ -0,0 +1,336 @@
+
+struct it913xset { u32 pro;
+ u32 address;
+ u8 reg[15];
+ u8 count;
+};
+
+struct adctable { u32 adcFrequency;
+ u32 bandwidth;
+ u32 coeff_1_2048;
+ u32 coeff_1_4096;
+ u32 coeff_1_8191;
+ u32 coeff_1_8192;
+ u32 coeff_1_8193;
+ u32 coeff_2_2k;
+ u32 coeff_2_4k;
+ u32 coeff_2_8k;
+ u16 bfsfcw_fftinx_ratio;
+ u16 fftinx_bfsfcw_ratio;
+};
+
+/* clock and coeff tables only table 3 is used with IT9137*/
+/* TODO other tables relate AF9035 may be removed */
+static struct adctable tab1[] = {
+ { 20156250, BANDWIDTH_6_MHZ,
+ 0x02b8ba6e, 0x015c5d37, 0x00ae340d, 0x00ae2e9b, 0x00ae292a,
+ 0x015c5d37, 0x00ae2e9b, 0x0057174e, 0x02f1, 0x015c },
+ { 20156250, BANDWIDTH_7_MHZ,
+ 0x032cd980, 0x01966cc0, 0x00cb3cba, 0x00cb3660, 0x00cb3007,
+ 0x01966cc0, 0x00cb3660, 0x00659b30, 0x0285, 0x0196 },
+ { 20156250, BANDWIDTH_8_MHZ,
+ 0x03a0f893, 0x01d07c49, 0x00e84567, 0x00e83e25, 0x00e836e3,
+ 0x01d07c49, 0x00e83e25, 0x00741f12, 0x0234, 0x01d0 },
+ { 20156250, BANDWIDTH_5_MHZ,
+ 0x02449b5c, 0x01224dae, 0x00912b60, 0x009126d7, 0x0091224e,
+ 0x01224dae, 0x009126d7, 0x0048936b, 0x0387, 0x0122 }
+};
+
+static struct adctable tab2[] = {
+ { 20187500, BANDWIDTH_6_MHZ,
+ 0x02b7a654, 0x015bd32a, 0x00adef04, 0x00ade995, 0x00ade426,
+ 0x015bd32a, 0x00ade995, 0x0056f4ca, 0x02f2, 0x015c },
+ { 20187500, BANDWIDTH_7_MHZ,
+ 0x032b9761, 0x0195cbb1, 0x00caec30, 0x00cae5d8, 0x00cadf81,
+ 0x0195cbb1, 0x00cae5d8, 0x006572ec, 0x0286, 0x0196 },
+ { 20187500, BANDWIDTH_8_MHZ,
+ 0x039f886f, 0x01cfc438, 0x00e7e95b, 0x00e7e21c, 0x00e7dadd,
+ 0x01cfc438, 0x00e7e21c, 0x0073f10e, 0x0235, 0x01d0 },
+ { 20187500, BANDWIDTH_5_MHZ,
+ 0x0243b546, 0x0121daa3, 0x0090f1d9, 0x0090ed51, 0x0090e8ca,
+ 0x0121daa3, 0x0090ed51, 0x004876a9, 0x0388, 0x0122 }
+
+};
+
+static struct adctable tab3[] = {
+ { 20250000, BANDWIDTH_6_MHZ,
+ 0x02b580ad, 0x015ac057, 0x00ad6597, 0x00ad602b, 0x00ad5ac1,
+ 0x015ac057, 0x00ad602b, 0x0056b016, 0x02f4, 0x015b },
+ { 20250000, BANDWIDTH_7_MHZ,
+ 0x03291620, 0x01948b10, 0x00ca4bda, 0x00ca4588, 0x00ca3f36,
+ 0x01948b10, 0x00ca4588, 0x006522c4, 0x0288, 0x0195 },
+ { 20250000, BANDWIDTH_8_MHZ,
+ 0x039cab92, 0x01ce55c9, 0x00e7321e, 0x00e72ae4, 0x00e723ab,
+ 0x01ce55c9, 0x00e72ae4, 0x00739572, 0x0237, 0x01ce },
+ { 20250000, BANDWIDTH_5_MHZ,
+ 0x0241eb3b, 0x0120f59e, 0x00907f53, 0x00907acf, 0x0090764b,
+ 0x0120f59e, 0x00907acf, 0x00483d67, 0x038b, 0x0121 }
+
+};
+
+static struct adctable tab4[] = {
+ { 20583333, BANDWIDTH_6_MHZ,
+ 0x02aa4598, 0x015522cc, 0x00aa96bb, 0x00aa9166, 0x00aa8c12,
+ 0x015522cc, 0x00aa9166, 0x005548b3, 0x0300, 0x0155 },
+ { 20583333, BANDWIDTH_7_MHZ,
+ 0x031bfbdc, 0x018dfdee, 0x00c7052f, 0x00c6fef7, 0x00c6f8bf,
+ 0x018dfdee, 0x00c6fef7, 0x00637f7b, 0x0293, 0x018e },
+ { 20583333, BANDWIDTH_8_MHZ,
+ 0x038db21f, 0x01c6d910, 0x00e373a3, 0x00e36c88, 0x00e3656d,
+ 0x01c6d910, 0x00e36c88, 0x0071b644, 0x0240, 0x01c7 },
+ { 20583333, BANDWIDTH_5_MHZ,
+ 0x02388f54, 0x011c47aa, 0x008e2846, 0x008e23d5, 0x008e1f64,
+ 0x011c47aa, 0x008e23d5, 0x004711ea, 0x039a, 0x011c }
+
+};
+
+static struct adctable tab5[] = {
+ { 20416667, BANDWIDTH_6_MHZ,
+ 0x02afd765, 0x0157ebb3, 0x00abfb39, 0x00abf5d9, 0x00abf07a,
+ 0x0157ebb3, 0x00abf5d9, 0x0055faed, 0x02fa, 0x0158 },
+ { 20416667, BANDWIDTH_7_MHZ,
+ 0x03227b4b, 0x01913da6, 0x00c8a518, 0x00c89ed3, 0x00c8988e,
+ 0x01913da6, 0x00c89ed3, 0x00644f69, 0x028d, 0x0191 },
+ { 20416667, BANDWIDTH_8_MHZ,
+ 0x03951f32, 0x01ca8f99, 0x00e54ef7, 0x00e547cc, 0x00e540a2,
+ 0x01ca8f99, 0x00e547cc, 0x0072a3e6, 0x023c, 0x01cb },
+ { 20416667, BANDWIDTH_5_MHZ,
+ 0x023d337f, 0x011e99c0, 0x008f515a, 0x008f4ce0, 0x008f4865,
+ 0x011e99c0, 0x008f4ce0, 0x0047a670, 0x0393, 0x011f }
+
+};
+
+static struct adctable tab6[] = {
+ { 20480000, BANDWIDTH_6_MHZ,
+ 0x02adb6db, 0x0156db6e, 0x00ab7312, 0x00ab6db7, 0x00ab685c,
+ 0x0156db6e, 0x00ab6db7, 0x0055b6db, 0x02fd, 0x0157 },
+ { 20480000, BANDWIDTH_7_MHZ,
+ 0x03200000, 0x01900000, 0x00c80640, 0x00c80000, 0x00c7f9c0,
+ 0x01900000, 0x00c80000, 0x00640000, 0x028f, 0x0190 },
+ { 20480000, BANDWIDTH_8_MHZ,
+ 0x03924925, 0x01c92492, 0x00e4996e, 0x00e49249, 0x00e48b25,
+ 0x01c92492, 0x00e49249, 0x00724925, 0x023d, 0x01c9 },
+ { 20480000, BANDWIDTH_5_MHZ,
+ 0x023b6db7, 0x011db6db, 0x008edfe5, 0x008edb6e, 0x008ed6f7,
+ 0x011db6db, 0x008edb6e, 0x00476db7, 0x0396, 0x011e }
+};
+
+static struct adctable tab7[] = {
+ { 20500000, BANDWIDTH_6_MHZ,
+ 0x02ad0b99, 0x015685cc, 0x00ab4840, 0x00ab42e6, 0x00ab3d8c,
+ 0x015685cc, 0x00ab42e6, 0x0055a173, 0x02fd, 0x0157 },
+ { 20500000, BANDWIDTH_7_MHZ,
+ 0x031f3832, 0x018f9c19, 0x00c7d44b, 0x00c7ce0c, 0x00c7c7ce,
+ 0x018f9c19, 0x00c7ce0c, 0x0063e706, 0x0290, 0x0190 },
+ { 20500000, BANDWIDTH_8_MHZ,
+ 0x039164cb, 0x01c8b266, 0x00e46056, 0x00e45933, 0x00e45210,
+ 0x01c8b266, 0x00e45933, 0x00722c99, 0x023e, 0x01c9 },
+ { 20500000, BANDWIDTH_5_MHZ,
+ 0x023adeff, 0x011d6f80, 0x008ebc36, 0x008eb7c0, 0x008eb34a,
+ 0x011d6f80, 0x008eb7c0, 0x00475be0, 0x0396, 0x011d }
+
+};
+
+static struct adctable tab8[] = {
+ { 20625000, BANDWIDTH_6_MHZ,
+ 0x02a8e4bd, 0x0154725e, 0x00aa3e81, 0x00aa392f, 0x00aa33de,
+ 0x0154725e, 0x00aa392f, 0x00551c98, 0x0302, 0x0154 },
+ { 20625000, BANDWIDTH_7_MHZ,
+ 0x031a6032, 0x018d3019, 0x00c69e41, 0x00c6980c, 0x00c691d8,
+ 0x018d3019, 0x00c6980c, 0x00634c06, 0x0294, 0x018d },
+ { 20625000, BANDWIDTH_8_MHZ,
+ 0x038bdba6, 0x01c5edd3, 0x00e2fe02, 0x00e2f6ea, 0x00e2efd2,
+ 0x01c5edd3, 0x00e2f6ea, 0x00717b75, 0x0242, 0x01c6 },
+ { 20625000, BANDWIDTH_5_MHZ,
+ 0x02376948, 0x011bb4a4, 0x008ddec1, 0x008dda52, 0x008dd5e3,
+ 0x011bb4a4, 0x008dda52, 0x0046ed29, 0x039c, 0x011c }
+
+};
+
+struct table {
+ u32 xtal;
+ struct adctable *table;
+};
+
+static struct table fe_clockTable[] = {
+ {12000000, tab3}, /* FPGA */
+ {16384000, tab6}, /* 16.38MHz */
+ {20480000, tab6}, /* 20.48MHz */
+ {36000000, tab3}, /* 36.00MHz */
+ {30000000, tab1}, /* 30.00MHz */
+ {26000000, tab4}, /* 26.00MHz */
+ {28000000, tab5}, /* 28.00MHz */
+ {32000000, tab7}, /* 32.00MHz */
+ {34000000, tab2}, /* 34.00MHz */
+ {24000000, tab1}, /* 24.00MHz */
+ {22000000, tab8}, /* 22.00MHz */
+ {12000000, tab3} /* 12.00MHz */
+};
+
+/* fe get */
+fe_code_rate_t fe_code[] = {
+ FEC_1_2,
+ FEC_2_3,
+ FEC_3_4,
+ FEC_5_6,
+ FEC_7_8,
+ FEC_NONE,
+};
+
+fe_guard_interval_t fe_gi[] = {
+ GUARD_INTERVAL_1_32,
+ GUARD_INTERVAL_1_16,
+ GUARD_INTERVAL_1_8,
+ GUARD_INTERVAL_1_4,
+};
+
+fe_hierarchy_t fe_hi[] = {
+ HIERARCHY_NONE,
+ HIERARCHY_1,
+ HIERARCHY_2,
+ HIERARCHY_4,
+};
+
+fe_transmit_mode_t fe_mode[] = {
+ TRANSMISSION_MODE_2K,
+ TRANSMISSION_MODE_8K,
+ TRANSMISSION_MODE_4K,
+};
+
+fe_modulation_t fe_con[] = {
+ QPSK,
+ QAM_16,
+ QAM_64,
+};
+
+/* Standard demodulator functions */
+static struct it913xset set_solo_fe[] = {
+ {PRO_LINK, DVBT_INTEN, {0x04}, 0x01},
+ {PRO_LINK, DVBT_ENABLE, {0x05}, 0x01},
+ {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01},
+ {PRO_LINK, HOSTB_MPEG_SER_MODE, {0x00}, 0x01},
+ {PRO_LINK, HOSTB_MPEG_PAR_MODE, {0x00}, 0x01},
+ {PRO_DMOD, DCA_UPPER_CHIP, {0x00}, 0x01},
+ {PRO_LINK, HOSTB_DCA_UPPER, {0x00}, 0x01},
+ {PRO_DMOD, DCA_LOWER_CHIP, {0x00}, 0x01},
+ {PRO_LINK, HOSTB_DCA_LOWER, {0x00}, 0x01},
+ {PRO_DMOD, DCA_PLATCH, {0x00}, 0x01},
+ {PRO_DMOD, DCA_FPGA_LATCH, {0x00}, 0x01},
+ {PRO_DMOD, DCA_STAND_ALONE, {0x01}, 0x01},
+ {PRO_DMOD, DCA_ENABLE, {0x00}, 0x01},
+ {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01},
+ {PRO_DMOD, BFS_FCW, {0x00, 0x00, 0x00}, 0x03},
+ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
+};
+
+
+static struct it913xset init_1[] = {
+ {PRO_LINK, LOCK3_OUT, {0x01}, 0x01},
+ {PRO_LINK, PADMISCDRSR, {0x01}, 0x01},
+ {PRO_LINK, PADMISCDR2, {0x00}, 0x01},
+ {PRO_LINK, PADMISCDR4, {0x00}, 0x01}, /* Power up */
+ {PRO_LINK, PADMISCDR8, {0x00}, 0x01},
+ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
+};
+
+/* ---------IT9137 0x38 tuner init---------- */
+static struct it913xset it9137_set[] = {
+ {PRO_DMOD, 0x0043, {0x00}, 0x01},
+ {PRO_DMOD, 0x0046, {0x38}, 0x01},
+ {PRO_DMOD, 0x0051, {0x01}, 0x01},
+ {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02},
+ {PRO_DMOD, 0x0068, {0x0a}, 0x01},
+ {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03},
+ {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xc8, 0x01}, 0x05},
+ {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02},
+ {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x02, 0x0a, 0x03, 0xc8, 0xb8,
+ 0xd0, 0xc3, 0x01 }, 0x0a},
+ {PRO_DMOD, 0x008e, {0x01}, 0x01},
+ {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05},
+ {PRO_DMOD, 0x0099, {0x01}, 0x01},
+ {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02},
+ {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02},
+ {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04},
+ {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02},
+ {PRO_DMOD, 0x00b0, {0x01}, 0x01},
+ {PRO_DMOD, 0x00b3, {0x02, 0x32}, 0x02},
+ {PRO_DMOD, 0x00b6, {0x14}, 0x01},
+ {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03},
+ {PRO_DMOD, 0x00c4, {0x00}, 0x01},
+ {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02},
+ {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03},
+ {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03},
+ {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03},
+ {PRO_DMOD, 0x00fc, { 0x02, 0x02, 0x02, 0x09, 0x50, 0x7b, 0x77,
+ 0x00, 0x02, 0xc8, 0x05, 0x7b }, 0x0c},
+ {PRO_DMOD, 0x0109, {0x02}, 0x01},
+ {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02},
+ {PRO_DMOD, 0x011a, {0xc8, 0x7b, 0xbc, 0xa0}, 0x04},
+ {PRO_DMOD, 0x0122, {0x02, 0x18, 0xc3}, 0x03},
+ {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02},
+ {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04},
+ {PRO_DMOD, 0x0137, {0x01, 0x00, 0x07, 0x00, 0x06}, 0x05},
+ {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xc8}, 0x04},
+ {PRO_DMOD, 0xf130, {0x04}, 0x01},
+ {PRO_DMOD, 0xf132, {0x04}, 0x01},
+ {PRO_DMOD, 0xf144, {0x1a}, 0x01},
+ {PRO_DMOD, 0xf146, {0x00}, 0x01},
+ {PRO_DMOD, 0xf14a, {0x01}, 0x01},
+ {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02},
+ {PRO_DMOD, 0xf14f, {0x04}, 0x01},
+ {PRO_DMOD, 0xf158, {0x7f}, 0x01},
+ {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02},
+ {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02},
+ {PRO_DMOD, 0xf163, {0x05}, 0x01},
+ {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03},
+ {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02},
+ {PRO_DMOD, 0xf183, {0x01}, 0x01},
+ {PRO_DMOD, 0xf19d, {0x40}, 0x01},
+ {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02},
+ {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02},
+ {PRO_DMOD, 0xf204, {0x10}, 0x01},
+ {PRO_DMOD, 0xf214, {0x00}, 0x01},
+ {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04},
+ {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05},
+ {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04},
+ {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03},
+ {PRO_DMOD, 0xf55f, {0x0a}, 0x01},
+ {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02},
+ {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02},
+ {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03},
+ {PRO_DMOD, 0xf5f8, {0x01}, 0x01},
+ {PRO_DMOD, 0xf5fd, {0x01}, 0x01},
+ {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17,
+ 0x1f }, 0x08},
+ {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04},
+ {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04},
+ {PRO_DMOD, 0xf78b, {0x01}, 0x01},
+ {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03},
+ {PRO_DMOD, 0xf905, {0x01}, 0x01},
+ {PRO_DMOD, 0xfb06, {0x03}, 0x01},
+ {PRO_DMOD, 0xfd8b, {0x00}, 0x01},
+ {PRO_LINK, GPIOH5_EN, {0x01}, 0x01},
+ {PRO_LINK, GPIOH5_ON, {0x01}, 0x01},
+ {PRO_LINK, GPIOH5_O, {0x00}, 0x01},
+ {PRO_LINK, GPIOH5_O, {0x01}, 0x01},
+ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
+};
+
+static struct it913xset it9137_tuner_off[] = {
+ {PRO_DMOD, 0xfba8, {0x01}, 0x01}, /* Tuner Clock Off */
+ {PRO_DMOD, 0xec40, {0x00}, 0x01}, /* Power Down Tuner */
+ {PRO_DMOD, 0xec02, {0x3f, 0x1f, 0x3f, 0x3f}, 0x04},
+ {PRO_DMOD, 0xec3f, {0x01}, 0x01},
+ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
+};
+
+static struct it913xset set_it9137_template[] = {
+ {PRO_DMOD, 0xee06, {0x00}, 0x01},
+ {PRO_DMOD, 0xec56, {0x00}, 0x01},
+ {PRO_DMOD, 0xec4c, {0x00}, 0x01},
+ {PRO_DMOD, 0xec4d, {0x00}, 0x01},
+ {PRO_DMOD, 0xec4e, {0x00}, 0x01},
+ {PRO_DMOD, 0xec4f, {0x00}, 0x01},
+ {PRO_DMOD, 0xec50, {0x00}, 0x01},
+ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
+};
diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c
new file mode 100644
index 000000000000..d4bd24eb4700
--- /dev/null
+++ b/drivers/media/dvb/frontends/it913x-fe.c
@@ -0,0 +1,839 @@
+/*
+ * Driver for it913x-fe Frontend
+ *
+ * with support for on chip it9137 integral tuner
+ *
+ * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
+ * IT9137 Copyright (C) ITE Tech Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "dvb_frontend.h"
+#include "it913x-fe.h"
+#include "it913x-fe-priv.h"
+
+static int it913x_debug;
+
+module_param_named(debug, it913x_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define dprintk(level, args...) do { \
+ if (level & it913x_debug) \
+ printk(KERN_DEBUG "it913x-fe: " args); \
+} while (0)
+
+#define deb_info(args...) dprintk(0x01, args)
+#define debug_data_snipet(level, name, p) \
+ dprintk(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
+ *p, *(p+1), *(p+2), *(p+3), *(p+4), \
+ *(p+5), *(p+6), *(p+7));
+
+struct it913x_fe_state {
+ struct dvb_frontend frontend;
+ struct i2c_adapter *i2c_adap;
+ u8 i2c_addr;
+ u32 frequency;
+ u8 adf;
+ u32 crystalFrequency;
+ u32 adcFrequency;
+ u8 tuner_type;
+ struct adctable *table;
+ fe_status_t it913x_status;
+ u16 tun_xtal;
+ u8 tun_fdiv;
+ u8 tun_clk_mode;
+ u32 tun_fn_min;
+};
+
+static int it913x_read_reg(struct it913x_fe_state *state,
+ u32 reg, u8 *data, u8 count)
+{
+ int ret;
+ u8 pro = PRO_DMOD; /* All reads from demodulator */
+ u8 b[4];
+ struct i2c_msg msg[2] = {
+ { .addr = state->i2c_addr + (pro << 1), .flags = 0,
+ .buf = b, .len = sizeof(b) },
+ { .addr = state->i2c_addr + (pro << 1), .flags = I2C_M_RD,
+ .buf = data, .len = count }
+ };
+ b[0] = (u8) reg >> 24;
+ b[1] = (u8)(reg >> 16) & 0xff;
+ b[2] = (u8)(reg >> 8) & 0xff;
+ b[3] = (u8) reg & 0xff;
+
+ ret = i2c_transfer(state->i2c_adap, msg, 2);
+
+ return ret;
+}
+
+static int it913x_read_reg_u8(struct it913x_fe_state *state, u32 reg)
+{
+ int ret;
+ u8 b[1];
+ ret = it913x_read_reg(state, reg, &b[0], sizeof(b));
+ return (ret < 0) ? -ENODEV : b[0];
+}
+
+static int it913x_write(struct it913x_fe_state *state,
+ u8 pro, u32 reg, u8 buf[], u8 count)
+{
+ u8 b[256];
+ struct i2c_msg msg[1] = {
+ { .addr = state->i2c_addr + (pro << 1), .flags = 0,
+ .buf = b, .len = count + 4 }
+ };
+ int ret;
+
+ b[0] = (u8) reg >> 24;
+ b[1] = (u8)(reg >> 16) & 0xff;
+ b[2] = (u8)(reg >> 8) & 0xff;
+ b[3] = (u8) reg & 0xff;
+ memcpy(&b[4], buf, count);
+
+ ret = i2c_transfer(state->i2c_adap, msg, 1);
+
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int it913x_write_reg(struct it913x_fe_state *state,
+ u8 pro, u32 reg, u32 data)
+{
+ int ret;
+ u8 b[4];
+ u8 s;
+
+ b[0] = data >> 24;
+ b[1] = (data >> 16) & 0xff;
+ b[2] = (data >> 8) & 0xff;
+ b[3] = data & 0xff;
+ /* expand write as needed */
+ if (data < 0x100)
+ s = 3;
+ else if (data < 0x1000)
+ s = 2;
+ else if (data < 0x100000)
+ s = 1;
+ else
+ s = 0;
+
+ ret = it913x_write(state, pro, reg, &b[s], sizeof(b) - s);
+
+ return ret;
+}
+
+static int it913x_fe_script_loader(struct it913x_fe_state *state,
+ struct it913xset *loadscript)
+{
+ int ret, i;
+ if (loadscript == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < 1000; ++i) {
+ if (loadscript[i].pro == 0xff)
+ break;
+ ret = it913x_write(state, loadscript[i].pro,
+ loadscript[i].address,
+ loadscript[i].reg, loadscript[i].count);
+ if (ret < 0)
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int it913x_init_tuner(struct it913x_fe_state *state)
+{
+ int ret, i, reg;
+ u8 val, nv_val;
+ u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2};
+ u8 b[2];
+
+ reg = it913x_read_reg_u8(state, 0xec86);
+ switch (reg) {
+ case 0:
+ state->tun_clk_mode = reg;
+ state->tun_xtal = 2000;
+ state->tun_fdiv = 3;
+ val = 16;
+ break;
+ case -ENODEV:
+ return -ENODEV;
+ case 1:
+ default:
+ state->tun_clk_mode = reg;
+ state->tun_xtal = 640;
+ state->tun_fdiv = 1;
+ val = 6;
+ break;
+ }
+
+ reg = it913x_read_reg_u8(state, 0xed03);
+
+ if (reg < 0)
+ return -ENODEV;
+ else if (reg < sizeof(nv))
+ nv_val = nv[reg];
+ else
+ nv_val = 2;
+
+ for (i = 0; i < 50; i++) {
+ ret = it913x_read_reg(state, 0xed23, &b[0], sizeof(b));
+ reg = (b[1] << 8) + b[0];
+ if (reg > 0)
+ break;
+ if (ret < 0)
+ return -ENODEV;
+ udelay(2000);
+ }
+ state->tun_fn_min = state->tun_xtal * reg;
+ state->tun_fn_min /= (state->tun_fdiv * nv_val);
+ deb_info("Tuner fn_min %d", state->tun_fn_min);
+
+ for (i = 0; i < 50; i++) {
+ reg = it913x_read_reg_u8(state, 0xec82);
+ if (reg > 0)
+ break;
+ if (reg < 0)
+ return -ENODEV;
+ udelay(2000);
+ }
+
+ return it913x_write_reg(state, PRO_DMOD, 0xed81, val);
+}
+
+static int it9137_set_tuner(struct it913x_fe_state *state,
+ enum fe_bandwidth bandwidth, u32 frequency_m)
+{
+ struct it913xset *set_tuner = set_it9137_template;
+ int ret, reg;
+ u32 frequency = frequency_m / 1000;
+ u32 freq, temp_f, tmp;
+ u16 iqik_m_cal;
+ u16 n_div;
+ u8 n;
+ u8 l_band;
+ u8 lna_band;
+ u8 bw;
+
+ deb_info("Tuner Frequency %d Bandwidth %d", frequency, bandwidth);
+
+ if (frequency >= 51000 && frequency <= 440000) {
+ l_band = 0;
+ lna_band = 0;
+ } else if (frequency > 440000 && frequency <= 484000) {
+ l_band = 1;
+ lna_band = 1;
+ } else if (frequency > 484000 && frequency <= 533000) {
+ l_band = 1;
+ lna_band = 2;
+ } else if (frequency > 533000 && frequency <= 587000) {
+ l_band = 1;
+ lna_band = 3;
+ } else if (frequency > 587000 && frequency <= 645000) {
+ l_band = 1;
+ lna_band = 4;
+ } else if (frequency > 645000 && frequency <= 710000) {
+ l_band = 1;
+ lna_band = 5;
+ } else if (frequency > 710000 && frequency <= 782000) {
+ l_band = 1;
+ lna_band = 6;
+ } else if (frequency > 782000 && frequency <= 860000) {
+ l_band = 1;
+ lna_band = 7;
+ } else if (frequency > 1450000 && frequency <= 1492000) {
+ l_band = 1;
+ lna_band = 0;
+ } else if (frequency > 1660000 && frequency <= 1685000) {
+ l_band = 1;
+ lna_band = 1;
+ } else
+ return -EINVAL;
+ set_tuner[0].reg[0] = lna_band;
+
+ if (bandwidth == BANDWIDTH_5_MHZ)
+ bw = 0;
+ else if (bandwidth == BANDWIDTH_6_MHZ)
+ bw = 2;
+ else if (bandwidth == BANDWIDTH_7_MHZ)
+ bw = 4;
+ else if (bandwidth == BANDWIDTH_8_MHZ)
+ bw = 6;
+ else
+ bw = 6;
+
+ set_tuner[1].reg[0] = bw;
+ set_tuner[2].reg[0] = 0xa0 | (l_band << 3);
+
+ if (frequency > 53000 && frequency <= 74000) {
+ n_div = 48;
+ n = 0;
+ } else if (frequency > 74000 && frequency <= 111000) {
+ n_div = 32;
+ n = 1;
+ } else if (frequency > 111000 && frequency <= 148000) {
+ n_div = 24;
+ n = 2;
+ } else if (frequency > 148000 && frequency <= 222000) {
+ n_div = 16;
+ n = 3;
+ } else if (frequency > 222000 && frequency <= 296000) {
+ n_div = 12;
+ n = 4;
+ } else if (frequency > 296000 && frequency <= 445000) {
+ n_div = 8;
+ n = 5;
+ } else if (frequency > 445000 && frequency <= state->tun_fn_min) {
+ n_div = 6;
+ n = 6;
+ } else if (frequency > state->tun_fn_min && frequency <= 950000) {
+ n_div = 4;
+ n = 7;
+ } else if (frequency > 1450000 && frequency <= 1680000) {
+ n_div = 2;
+ n = 0;
+ } else
+ return -EINVAL;
+
+ reg = it913x_read_reg_u8(state, 0xed81);
+ iqik_m_cal = (u16)reg * n_div;
+
+ if (reg < 0x20) {
+ if (state->tun_clk_mode == 0)
+ iqik_m_cal = (iqik_m_cal * 9) >> 5;
+ else
+ iqik_m_cal >>= 1;
+ } else {
+ iqik_m_cal = 0x40 - iqik_m_cal;
+ if (state->tun_clk_mode == 0)
+ iqik_m_cal = ~((iqik_m_cal * 9) >> 5);
+ else
+ iqik_m_cal = ~(iqik_m_cal >> 1);
+ }
+
+ temp_f = frequency * (u32)n_div * (u32)state->tun_fdiv;
+ freq = temp_f / state->tun_xtal;
+ tmp = freq * state->tun_xtal;
+
+ if ((temp_f - tmp) >= (state->tun_xtal >> 1))
+ freq++;
+
+ freq += (u32) n << 13;
+ /* Frequency OMEGA_IQIK_M_CAL_MID*/
+ temp_f = freq + (u32)iqik_m_cal;
+
+ set_tuner[3].reg[0] = temp_f & 0xff;
+ set_tuner[4].reg[0] = (temp_f >> 8) & 0xff;
+
+ deb_info("High Frequency = %04x", temp_f);
+
+ /* Lower frequency */
+ set_tuner[5].reg[0] = freq & 0xff;
+ set_tuner[6].reg[0] = (freq >> 8) & 0xff;
+
+ deb_info("low Frequency = %04x", freq);
+
+ ret = it913x_fe_script_loader(state, set_tuner);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_fe_select_bw(struct it913x_fe_state *state,
+ enum fe_bandwidth bandwidth, u32 adcFrequency)
+{
+ int ret, i;
+ u8 buffer[256];
+ u32 coeff[8];
+ u16 bfsfcw_fftinx_ratio;
+ u16 fftinx_bfsfcw_ratio;
+ u8 count;
+ u8 bw;
+ u8 adcmultiplier;
+
+ deb_info("Bandwidth %d Adc %d", bandwidth, adcFrequency);
+
+ if (bandwidth == BANDWIDTH_5_MHZ)
+ bw = 3;
+ else if (bandwidth == BANDWIDTH_6_MHZ)
+ bw = 0;
+ else if (bandwidth == BANDWIDTH_7_MHZ)
+ bw = 1;
+ else if (bandwidth == BANDWIDTH_8_MHZ)
+ bw = 2;
+ else
+ bw = 2;
+
+ ret = it913x_write_reg(state, PRO_DMOD, REG_BW, bw);
+
+ if (state->table == NULL)
+ return -EINVAL;
+
+ /* In write order */
+ coeff[0] = state->table[bw].coeff_1_2048;
+ coeff[1] = state->table[bw].coeff_2_2k;
+ coeff[2] = state->table[bw].coeff_1_8191;
+ coeff[3] = state->table[bw].coeff_1_8192;
+ coeff[4] = state->table[bw].coeff_1_8193;
+ coeff[5] = state->table[bw].coeff_2_8k;
+ coeff[6] = state->table[bw].coeff_1_4096;
+ coeff[7] = state->table[bw].coeff_2_4k;
+ bfsfcw_fftinx_ratio = state->table[bw].bfsfcw_fftinx_ratio;
+ fftinx_bfsfcw_ratio = state->table[bw].fftinx_bfsfcw_ratio;
+
+ /* ADC multiplier */
+ ret = it913x_read_reg_u8(state, ADC_X_2);
+ if (ret < 0)
+ return -EINVAL;
+
+ adcmultiplier = ret;
+
+ count = 0;
+
+ /* Build Buffer for COEFF Registers */
+ for (i = 0; i < 8; i++) {
+ if (adcmultiplier == 1)
+ coeff[i] /= 2;
+ buffer[count++] = (coeff[i] >> 24) & 0x3;
+ buffer[count++] = (coeff[i] >> 16) & 0xff;
+ buffer[count++] = (coeff[i] >> 8) & 0xff;
+ buffer[count++] = coeff[i] & 0xff;
+ }
+
+ /* bfsfcw_fftinx_ratio register 0x21-0x22 */
+ buffer[count++] = bfsfcw_fftinx_ratio & 0xff;
+ buffer[count++] = (bfsfcw_fftinx_ratio >> 8) & 0xff;
+ /* fftinx_bfsfcw_ratio register 0x23-0x24 */
+ buffer[count++] = fftinx_bfsfcw_ratio & 0xff;
+ buffer[count++] = (fftinx_bfsfcw_ratio >> 8) & 0xff;
+ /* start at COEFF_1_2048 and write through to fftinx_bfsfcw_ratio*/
+ ret = it913x_write(state, PRO_DMOD, COEFF_1_2048, buffer, count);
+
+ for (i = 0; i < 42; i += 8)
+ debug_data_snipet(0x1, "Buffer", &buffer[i]);
+
+ return ret;
+}
+
+
+
+static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret, i;
+ fe_status_t old_status = state->it913x_status;
+ *status = 0;
+
+ if (state->it913x_status == 0) {
+ ret = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
+ if (ret == 0x1) {
+ *status |= FE_HAS_SIGNAL;
+ for (i = 0; i < 40; i++) {
+ ret = it913x_read_reg_u8(state, MP2IF_SYNC_LK);
+ if (ret == 0x1)
+ break;
+ msleep(25);
+ }
+ if (ret == 0x1)
+ *status |= FE_HAS_CARRIER
+ | FE_HAS_VITERBI
+ | FE_HAS_SYNC;
+ state->it913x_status = *status;
+ }
+ }
+
+ if (state->it913x_status & FE_HAS_SYNC) {
+ ret = it913x_read_reg_u8(state, TPSD_LOCK);
+ if (ret == 0x1)
+ *status |= FE_HAS_LOCK
+ | state->it913x_status;
+ else
+ state->it913x_status = 0;
+ if (old_status != state->it913x_status)
+ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, ret);
+ }
+
+ return 0;
+}
+
+static int it913x_fe_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
+ /*SIGNAL_LEVEL always returns 100%! so using FE_HAS_SIGNAL as switch*/
+ if (state->it913x_status & FE_HAS_SIGNAL)
+ ret = (ret * 0xff) / 0x64;
+ else
+ ret = 0x0;
+ ret |= ret << 0x8;
+ *strength = ret;
+ return 0;
+}
+
+static int it913x_fe_read_snr(struct dvb_frontend *fe, u16* snr)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret = it913x_read_reg_u8(state, SIGNAL_QUALITY);
+ ret = (ret * 0xff) / 0x64;
+ ret |= (ret << 0x8);
+ *snr = ~ret;
+ return 0;
+}
+
+static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+ return 0;
+}
+
+static int it913x_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static int it913x_fe_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret;
+ u8 reg[8];
+
+ ret = it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg));
+
+ if (reg[3] < 3)
+ p->u.ofdm.constellation = fe_con[reg[3]];
+
+ if (reg[0] < 3)
+ p->u.ofdm.transmission_mode = fe_mode[reg[0]];
+
+ if (reg[1] < 4)
+ p->u.ofdm.guard_interval = fe_gi[reg[1]];
+
+ if (reg[2] < 4)
+ p->u.ofdm.hierarchy_information = fe_hi[reg[2]];
+
+ p->u.ofdm.code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE;
+ p->u.ofdm.code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE;
+
+ return 0;
+}
+
+static int it913x_fe_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret, i;
+ u8 empty_ch, last_ch;
+
+ state->it913x_status = 0;
+
+ /* Set bw*/
+ ret = it913x_fe_select_bw(state, p->u.ofdm.bandwidth,
+ state->adcFrequency);
+
+ /* Training Mode Off */
+ ret = it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0);
+
+ /* Clear Empty Channel */
+ ret = it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0);
+
+ /* Clear bits */
+ ret = it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0);
+ /* LED on */
+ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
+ /* Select Band*/
+ if ((p->frequency >= 51000000) && (p->frequency <= 230000000))
+ i = 0;
+ else if ((p->frequency >= 350000000) && (p->frequency <= 900000000))
+ i = 1;
+ else if ((p->frequency >= 1450000000) && (p->frequency <= 1680000000))
+ i = 2;
+ else
+ return -EOPNOTSUPP;
+
+ ret = it913x_write_reg(state, PRO_DMOD, FREE_BAND, i);
+
+ deb_info("Frontend Set Tuner Type %02x", state->tuner_type);
+ switch (state->tuner_type) {
+ case IT9137: /* Tuner type 0x38 */
+ ret = it9137_set_tuner(state,
+ p->u.ofdm.bandwidth, p->frequency);
+ break;
+ default:
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, p);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ break;
+ }
+ /* LED off */
+ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
+ /* Trigger ofsm */
+ ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
+ last_ch = 2;
+ for (i = 0; i < 40; ++i) {
+ empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
+ if (last_ch == 1 && empty_ch == 1)
+ break;
+ if (last_ch == 2 && empty_ch == 2)
+ return 0;
+ last_ch = empty_ch;
+ msleep(25);
+ }
+ for (i = 0; i < 40; ++i) {
+ if (it913x_read_reg_u8(state, D_TPSD_LOCK) == 1)
+ break;
+ msleep(25);
+ }
+
+ state->frequency = p->frequency;
+ return 0;
+}
+
+static int it913x_fe_suspend(struct it913x_fe_state *state)
+{
+ int ret, i;
+ u8 b;
+
+ ret = it913x_write_reg(state, PRO_DMOD, SUSPEND_FLAG, 0x1);
+
+ ret |= it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
+
+ for (i = 0; i < 128; i++) {
+ ret = it913x_read_reg(state, SUSPEND_FLAG, &b, 1);
+ if (ret < 0)
+ return -ENODEV;
+ if (b == 0)
+ break;
+
+ }
+
+ ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x8);
+ /* Turn LED off */
+ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
+
+ ret |= it913x_fe_script_loader(state, it9137_tuner_off);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+/* Power sequence */
+/* Power Up Tuner on -> Frontend suspend off -> Tuner clk on */
+/* Power Down Frontend suspend on -> Tuner clk off -> Tuner off */
+
+static int it913x_fe_sleep(struct dvb_frontend *fe)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ return it913x_fe_suspend(state);
+}
+
+static u32 compute_div(u32 a, u32 b, u32 x)
+{
+ u32 res = 0;
+ u32 c = 0;
+ u32 i = 0;
+
+ if (a > b) {
+ c = a / b;
+ a = a - c * b;
+ }
+
+ for (i = 0; i < x; i++) {
+ if (a >= b) {
+ res += 1;
+ a -= b;
+ }
+ a <<= 1;
+ res <<= 1;
+ }
+
+ res = (c << x) + res;
+
+ return res;
+}
+
+static int it913x_fe_start(struct it913x_fe_state *state)
+{
+ struct it913xset *set_fe;
+ struct it913xset *set_mode;
+ int ret;
+ u8 adf = (state->adf & 0xf);
+ u32 adc, xtal;
+ u8 b[4];
+
+ ret = it913x_init_tuner(state);
+
+ if (adf < 12) {
+ state->crystalFrequency = fe_clockTable[adf].xtal ;
+ state->table = fe_clockTable[adf].table;
+ state->adcFrequency = state->table->adcFrequency;
+
+ adc = compute_div(state->adcFrequency, 1000000ul, 19ul);
+ xtal = compute_div(state->crystalFrequency, 1000000ul, 19ul);
+
+ } else
+ return -EINVAL;
+
+ deb_info("Xtal Freq :%d Adc Freq :%d Adc %08x Xtal %08x",
+ state->crystalFrequency, state->adcFrequency, adc, xtal);
+
+ /* Set LED indicator on GPIOH3 */
+ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_EN, 0x1);
+ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_ON, 0x1);
+ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
+
+ ret |= it913x_write_reg(state, PRO_LINK, 0xf641, state->tuner_type);
+ ret |= it913x_write_reg(state, PRO_DMOD, 0xf5ca, 0x01);
+ ret |= it913x_write_reg(state, PRO_DMOD, 0xf715, 0x01);
+
+ b[0] = xtal & 0xff;
+ b[1] = (xtal >> 8) & 0xff;
+ b[2] = (xtal >> 16) & 0xff;
+ b[3] = (xtal >> 24);
+ ret |= it913x_write(state, PRO_DMOD, XTAL_CLK, b , 4);
+
+ b[0] = adc & 0xff;
+ b[1] = (adc >> 8) & 0xff;
+ b[2] = (adc >> 16) & 0xff;
+ ret |= it913x_write(state, PRO_DMOD, ADC_FREQ, b, 3);
+
+ switch (state->tuner_type) {
+ case IT9137: /* Tuner type 0x38 */
+ set_fe = it9137_set;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set the demod */
+ ret = it913x_fe_script_loader(state, set_fe);
+ /* Always solo frontend */
+ set_mode = set_solo_fe;
+ ret |= it913x_fe_script_loader(state, set_mode);
+
+ ret |= it913x_fe_suspend(state);
+ return 0;
+}
+
+static int it913x_fe_init(struct dvb_frontend *fe)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret = 0;
+ /* Power Up Tuner - common all versions */
+ ret = it913x_write_reg(state, PRO_DMOD, 0xec40, 0x1);
+
+ ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x0);
+
+ ret |= it913x_fe_script_loader(state, init_1);
+
+ switch (state->tuner_type) {
+ case IT9137:
+ ret |= it913x_write_reg(state, PRO_DMOD, 0xfba8, 0x0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static void it913x_fe_release(struct dvb_frontend *fe)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops it913x_fe_ofdm_ops;
+
+struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap,
+ u8 i2c_addr, u8 adf, u8 type)
+{
+ struct it913x_fe_state *state = NULL;
+ int ret;
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct it913x_fe_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ state->i2c_adap = i2c_adap;
+ state->i2c_addr = i2c_addr;
+ state->adf = adf;
+ state->tuner_type = type;
+
+ ret = it913x_fe_start(state);
+ if (ret < 0)
+ goto error;
+
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &it913x_fe_ofdm_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ return &state->frontend;
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(it913x_fe_attach);
+
+static struct dvb_frontend_ops it913x_fe_ofdm_ops = {
+
+ .info = {
+ .name = "it913x-fe DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 51000000,
+ .frequency_max = 1680000000,
+ .frequency_stepsize = 62500,
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = it913x_fe_release,
+
+ .init = it913x_fe_init,
+ .sleep = it913x_fe_sleep,
+
+ .set_frontend = it913x_fe_set_frontend,
+ .get_frontend = it913x_fe_get_frontend,
+
+ .read_status = it913x_fe_read_status,
+ .read_signal_strength = it913x_fe_read_signal_strength,
+ .read_snr = it913x_fe_read_snr,
+ .read_ber = it913x_fe_read_ber,
+ .read_ucblocks = it913x_fe_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("it913x Frontend and it9137 tuner");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
+MODULE_VERSION("1.07");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/it913x-fe.h b/drivers/media/dvb/frontends/it913x-fe.h
new file mode 100644
index 000000000000..9d97f32e690b
--- /dev/null
+++ b/drivers/media/dvb/frontends/it913x-fe.h
@@ -0,0 +1,196 @@
+/*
+ * Driver for it913x Frontend
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef IT913X_FE_H
+#define IT913X_FE_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+#if defined(CONFIG_DVB_IT913X_FE) || (defined(CONFIG_DVB_IT913X_FE_MODULE) && \
+defined(MODULE))
+extern struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap,
+ u8 i2c_addr, u8 adf, u8 type);
+#else
+static inline struct dvb_frontend *it913x_fe_attach(
+ struct i2c_adapter *i2c_adap, u8 i2c_addr, u8 adf, u8 type)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_IT913X_FE */
+#define I2C_BASE_ADDR 0x10
+#define DEV_0 0x0
+#define DEV_1 0x10
+#define PRO_LINK 0x0
+#define PRO_DMOD 0x1
+#define DEV_0_DMOD (PRO_DMOD << 0x7)
+#define DEV_1_DMOD (DEV_0_DMOD | DEV_1)
+#define CHIP2_I2C_ADDR 0x3a
+
+#define AFE_MEM0 0xfb24
+
+#define MP2_SW_RST 0xf99d
+#define MP2IF2_SW_RST 0xf9a4
+
+#define PADODPU 0xd827
+#define THIRDODPU 0xd828
+#define AGC_O_D 0xd829
+
+#define EP0_TX_EN 0xdd11
+#define EP0_TX_NAK 0xdd13
+#define EP4_TX_LEN_LSB 0xdd88
+#define EP4_TX_LEN_MSB 0xdd89
+#define EP4_MAX_PKT 0xdd0c
+#define EP5_TX_LEN_LSB 0xdd8a
+#define EP5_TX_LEN_MSB 0xdd8b
+#define EP5_MAX_PKT 0xdd0d
+
+#define IO_MUX_POWER_CLK 0xd800
+#define CLK_O_EN 0xd81a
+#define I2C_CLK 0xf103
+#define I2C_CLK_100 0x7
+#define I2C_CLK_400 0x1a
+
+#define D_TPSD_LOCK 0xf5a9
+#define MP2IF2_EN 0xf9a3
+#define MP2IF_SERIAL 0xf985
+#define TSIS_ENABLE 0xf9cd
+#define MP2IF2_HALF_PSB 0xf9a5
+#define MP2IF_STOP_EN 0xf9b5
+#define MPEG_FULL_SPEED 0xf990
+#define TOP_HOSTB_SER_MODE 0xd91c
+
+#define PID_RST 0xf992
+#define PID_EN 0xf993
+#define PID_INX_EN 0xf994
+#define PID_INX 0xf995
+#define PID_LSB 0xf996
+#define PID_MSB 0xf997
+
+#define MP2IF_MPEG_PAR_MODE 0xf986
+#define DCA_UPPER_CHIP 0xf731
+#define DCA_LOWER_CHIP 0xf732
+#define DCA_PLATCH 0xf730
+#define DCA_FPGA_LATCH 0xf778
+#define DCA_STAND_ALONE 0xf73c
+#define DCA_ENABLE 0xf776
+
+#define DVBT_INTEN 0xf41f
+#define DVBT_ENABLE 0xf41a
+#define HOSTB_DCA_LOWER 0xd91f
+#define HOSTB_MPEG_PAR_MODE 0xd91b
+#define HOSTB_MPEG_SER_MODE 0xd91c
+#define HOSTB_MPEG_SER_DO7 0xd91d
+#define HOSTB_DCA_UPPER 0xd91e
+#define PADMISCDR2 0xd830
+#define PADMISCDR4 0xd831
+#define PADMISCDR8 0xd832
+#define PADMISCDRSR 0xd833
+#define LOCK3_OUT 0xd8fd
+
+#define GPIOH1_O 0xd8af
+#define GPIOH1_EN 0xd8b0
+#define GPIOH1_ON 0xd8b1
+#define GPIOH3_O 0xd8b3
+#define GPIOH3_EN 0xd8b4
+#define GPIOH3_ON 0xd8b5
+#define GPIOH5_O 0xd8bb
+#define GPIOH5_EN 0xd8bc
+#define GPIOH5_ON 0xd8bd
+
+#define AFE_MEM0 0xfb24
+
+#define REG_TPSD_TX_MODE 0xf900
+#define REG_TPSD_GI 0xf901
+#define REG_TPSD_HIER 0xf902
+#define REG_TPSD_CONST 0xf903
+#define REG_BW 0xf904
+#define REG_PRIV 0xf905
+#define REG_TPSD_HP_CODE 0xf906
+#define REG_TPSD_LP_CODE 0xf907
+
+#define MP2IF_SYNC_LK 0xf999
+#define ADC_FREQ 0xf1cd
+
+#define TRIGGER_OFSM 0x0000
+/* COEFF Registers start at 0x0001 to 0x0020 */
+#define COEFF_1_2048 0x0001
+#define XTAL_CLK 0x0025
+#define BFS_FCW 0x0029
+#define TPSD_LOCK 0x003c
+#define TRAINING_MODE 0x0040
+#define ADC_X_2 0x0045
+#define TUNER_ID 0x0046
+#define EMPTY_CHANNEL_STATUS 0x0047
+#define SIGNAL_LEVEL 0x0048
+#define SIGNAL_QUALITY 0x0049
+#define EST_SIGNAL_LEVEL 0x004a
+#define FREE_BAND 0x004b
+#define SUSPEND_FLAG 0x004c
+/* Build in tuners */
+#define IT9137 0x38
+
+enum {
+ CMD_DEMOD_READ = 0,
+ CMD_DEMOD_WRITE,
+ CMD_TUNER_READ,
+ CMD_TUNER_WRITE,
+ CMD_REG_EEPROM_READ,
+ CMD_REG_EEPROM_WRITE,
+ CMD_DATA_READ,
+ CMD_VAR_READ = 8,
+ CMD_VAR_WRITE,
+ CMD_PLATFORM_GET,
+ CMD_PLATFORM_SET,
+ CMD_IP_CACHE,
+ CMD_IP_ADD,
+ CMD_IP_REMOVE,
+ CMD_PID_ADD,
+ CMD_PID_REMOVE,
+ CMD_SIPSI_GET,
+ CMD_SIPSI_MPE_RESET,
+ CMD_H_PID_ADD = 0x15,
+ CMD_H_PID_REMOVE,
+ CMD_ABORT,
+ CMD_IR_GET,
+ CMD_IR_SET,
+ CMD_FW_DOWNLOAD = 0x21,
+ CMD_QUERYINFO,
+ CMD_BOOT,
+ CMD_FW_DOWNLOAD_BEGIN,
+ CMD_FW_DOWNLOAD_END,
+ CMD_RUN_CODE,
+ CMD_SCATTER_READ = 0x28,
+ CMD_SCATTER_WRITE,
+ CMD_GENERIC_READ,
+ CMD_GENERIC_WRITE
+};
+
+enum {
+ READ_LONG,
+ WRITE_LONG,
+ READ_SHORT,
+ WRITE_SHORT,
+ READ_DATA,
+ WRITE_DATA,
+ WRITE_CMD,
+};
+
+#endif /* IT913X_FE_H */
diff --git a/drivers/media/dvb/frontends/itd1000.c b/drivers/media/dvb/frontends/itd1000.c
index f7a40a18777a..aa9ccb821fa5 100644
--- a/drivers/media/dvb/frontends/itd1000.c
+++ b/drivers/media/dvb/frontends/itd1000.c
@@ -35,21 +35,18 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-#define deb(args...) do { \
+#define itd_dbg(args...) do { \
if (debug) { \
printk(KERN_DEBUG "ITD1000: " args);\
- printk("\n"); \
} \
} while (0)
-#define warn(args...) do { \
+#define itd_warn(args...) do { \
printk(KERN_WARNING "ITD1000: " args); \
- printk("\n"); \
} while (0)
-#define info(args...) do { \
+#define itd_info(args...) do { \
printk(KERN_INFO "ITD1000: " args); \
- printk("\n"); \
} while (0)
/* don't write more than one byte with flexcop behind */
@@ -62,7 +59,7 @@ static int itd1000_write_regs(struct itd1000_state *state, u8 reg, u8 v[], u8 le
buf[0] = reg;
memcpy(&buf[1], v, len);
- /* deb("wr %02x: %02x", reg, v[0]); */
+ /* itd_dbg("wr %02x: %02x\n", reg, v[0]); */
if (i2c_transfer(state->i2c, &msg, 1) != 1) {
printk(KERN_WARNING "itd1000 I2C write failed\n");
@@ -83,7 +80,7 @@ static int itd1000_read_reg(struct itd1000_state *state, u8 reg)
itd1000_write_regs(state, (reg - 1) & 0xff, &state->shadow[(reg - 1) & 0xff], 1);
if (i2c_transfer(state->i2c, msg, 2) != 2) {
- warn("itd1000 I2C read failed");
+ itd_warn("itd1000 I2C read failed\n");
return -EREMOTEIO;
}
return val;
@@ -127,14 +124,14 @@ static void itd1000_set_lpf_bw(struct itd1000_state *state, u32 symbol_rate)
u8 bbgvmin = itd1000_read_reg(state, BBGVMIN) & 0xf0;
u8 bw = itd1000_read_reg(state, BW) & 0xf0;
- deb("symbol_rate = %d", symbol_rate);
+ itd_dbg("symbol_rate = %d\n", symbol_rate);
/* not sure what is that ? - starting to download the table */
itd1000_write_reg(state, CON1, con1 | (1 << 1));
for (i = 0; i < ARRAY_SIZE(itd1000_lpf_pga); i++)
if (symbol_rate < itd1000_lpf_pga[i].symbol_rate) {
- deb("symrate: index: %d pgaext: %x, bbgvmin: %x", i, itd1000_lpf_pga[i].pgaext, itd1000_lpf_pga[i].bbgvmin);
+ itd_dbg("symrate: index: %d pgaext: %x, bbgvmin: %x\n", i, itd1000_lpf_pga[i].pgaext, itd1000_lpf_pga[i].bbgvmin);
itd1000_write_reg(state, PLLFH, pllfh | (itd1000_lpf_pga[i].pgaext << 4));
itd1000_write_reg(state, BBGVMIN, bbgvmin | (itd1000_lpf_pga[i].bbgvmin));
itd1000_write_reg(state, BW, bw | (i & 0x0f));
@@ -182,7 +179,7 @@ static void itd1000_set_vco(struct itd1000_state *state, u32 freq_khz)
adcout = itd1000_read_reg(state, PLLLOCK) & 0x0f;
- deb("VCO: %dkHz: %d -> ADCOUT: %d %02x", freq_khz, itd1000_vcorg[i].vcorg, adcout, vco_chp1_i2c);
+ itd_dbg("VCO: %dkHz: %d -> ADCOUT: %d %02x\n", freq_khz, itd1000_vcorg[i].vcorg, adcout, vco_chp1_i2c);
if (adcout > 13) {
if (!(itd1000_vcorg[i].vcorg == 7 || itd1000_vcorg[i].vcorg == 15))
@@ -232,7 +229,7 @@ static void itd1000_set_lo(struct itd1000_state *state, u32 freq_khz)
pllf = (u32) tmp;
state->frequency = ((plln * 1000) + (pllf * 1000)/1048576) * 2*FREF;
- deb("frequency: %dkHz (wanted) %dkHz (set), PLLF = %d, PLLN = %d", freq_khz, state->frequency, pllf, plln);
+ itd_dbg("frequency: %dkHz (wanted) %dkHz (set), PLLF = %d, PLLN = %d\n", freq_khz, state->frequency, pllf, plln);
itd1000_write_reg(state, PLLNH, 0x80); /* PLLNH */;
itd1000_write_reg(state, PLLNL, plln & 0xff);
@@ -242,7 +239,7 @@ static void itd1000_set_lo(struct itd1000_state *state, u32 freq_khz)
for (i = 0; i < ARRAY_SIZE(itd1000_fre_values); i++) {
if (freq_khz <= itd1000_fre_values[i].freq) {
- deb("fre_values: %d", i);
+ itd_dbg("fre_values: %d\n", i);
itd1000_write_reg(state, RFTR, itd1000_fre_values[i].values[0]);
for (j = 0; j < 9; j++)
itd1000_write_reg(state, RFST1+j, itd1000_fre_values[i].values[j+1]);
@@ -382,7 +379,7 @@ struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter
kfree(state);
return NULL;
}
- info("successfully identified (ID: %d)", i);
+ itd_info("successfully identified (ID: %d)\n", i);
memset(state->shadow, 0xff, sizeof(state->shadow));
for (i = 0x65; i < 0x9c; i++)
diff --git a/drivers/media/dvb/frontends/lnbp22.c b/drivers/media/dvb/frontends/lnbp22.c
new file mode 100644
index 000000000000..84ad0390a4a1
--- /dev/null
+++ b/drivers/media/dvb/frontends/lnbp22.c
@@ -0,0 +1,148 @@
+/*
+ * lnbp22.h - driver for lnb supply and control ic lnbp22
+ *
+ * Copyright (C) 2006 Dominik Kuhlen
+ * Based on lnbp21 driver
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ *
+ * 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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org
+ */
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "lnbp22.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+
+#define dprintk(lvl, arg...) if (debug >= (lvl)) printk(arg)
+
+struct lnbp22 {
+ u8 config[4];
+ struct i2c_adapter *i2c;
+};
+
+static int lnbp22_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct lnbp22 *lnbp22 = (struct lnbp22 *)fe->sec_priv;
+ struct i2c_msg msg = {
+ .addr = 0x08,
+ .flags = 0,
+ .buf = (char *)&lnbp22->config,
+ .len = sizeof(lnbp22->config),
+ };
+
+ dprintk(1, "%s: %d (18V=%d 13V=%d)\n", __func__, voltage,
+ SEC_VOLTAGE_18, SEC_VOLTAGE_13);
+
+ lnbp22->config[3] = 0x60; /* Power down */
+ switch (voltage) {
+ case SEC_VOLTAGE_OFF:
+ break;
+ case SEC_VOLTAGE_13:
+ lnbp22->config[3] |= LNBP22_EN;
+ break;
+ case SEC_VOLTAGE_18:
+ lnbp22->config[3] |= (LNBP22_EN | LNBP22_VSEL);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ dprintk(1, "%s: 0x%02x)\n", __func__, lnbp22->config[3]);
+ return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static int lnbp22_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
+{
+ struct lnbp22 *lnbp22 = (struct lnbp22 *) fe->sec_priv;
+ struct i2c_msg msg = {
+ .addr = 0x08,
+ .flags = 0,
+ .buf = (char *)&lnbp22->config,
+ .len = sizeof(lnbp22->config),
+ };
+
+ dprintk(1, "%s: %d\n", __func__, (int)arg);
+ if (arg)
+ lnbp22->config[3] |= LNBP22_LLC;
+ else
+ lnbp22->config[3] &= ~LNBP22_LLC;
+
+ return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static void lnbp22_release(struct dvb_frontend *fe)
+{
+ dprintk(1, "%s\n", __func__);
+ /* LNBP power off */
+ lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF);
+
+ /* free data */
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
+}
+
+struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c)
+{
+ struct lnbp22 *lnbp22 = kmalloc(sizeof(struct lnbp22), GFP_KERNEL);
+ if (!lnbp22)
+ return NULL;
+
+ /* default configuration */
+ lnbp22->config[0] = 0x00; /* ? */
+ lnbp22->config[1] = 0x28; /* ? */
+ lnbp22->config[2] = 0x48; /* ? */
+ lnbp22->config[3] = 0x60; /* Power down */
+ lnbp22->i2c = i2c;
+ fe->sec_priv = lnbp22;
+
+ /* detect if it is present or not */
+ if (lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF)) {
+ dprintk(0, "%s LNBP22 not found\n", __func__);
+ kfree(lnbp22);
+ fe->sec_priv = NULL;
+ return NULL;
+ }
+
+ /* install release callback */
+ fe->ops.release_sec = lnbp22_release;
+
+ /* override frontend ops */
+ fe->ops.set_voltage = lnbp22_set_voltage;
+ fe->ops.enable_high_lnb_voltage = lnbp22_enable_high_lnb_voltage;
+
+ return fe;
+}
+EXPORT_SYMBOL(lnbp22_attach);
+
+MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp22");
+MODULE_AUTHOR("Dominik Kuhlen");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/lnbp22.h b/drivers/media/dvb/frontends/lnbp22.h
new file mode 100644
index 000000000000..63e2dec7e68a
--- /dev/null
+++ b/drivers/media/dvb/frontends/lnbp22.h
@@ -0,0 +1,57 @@
+/*
+ * lnbp22.h - driver for lnb supply and control ic lnbp22
+ *
+ * Copyright (C) 2006 Dominik Kuhlen
+ * Based on lnbp21.h
+ *
+ * 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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org
+ */
+
+#ifndef _LNBP22_H
+#define _LNBP22_H
+
+/* Enable */
+#define LNBP22_EN 0x10
+/* Voltage selection */
+#define LNBP22_VSEL 0x02
+/* Plus 1 Volt Bit */
+#define LNBP22_LLC 0x01
+
+#include <linux/dvb/frontend.h>
+
+#if defined(CONFIG_DVB_LNBP22) || \
+ (defined(CONFIG_DVB_LNBP22_MODULE) && defined(MODULE))
+/*
+ * override_set and override_clear control which system register bits (above)
+ * to always set & clear
+ */
+extern struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LNBP22 */
+
+#endif /* _LNBP22_H */
diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c
index a763ec756f7f..6599b8fea9e9 100644
--- a/drivers/media/dvb/frontends/nxt6000.c
+++ b/drivers/media/dvb/frontends/nxt6000.c
@@ -50,7 +50,7 @@ static int nxt6000_writereg(struct nxt6000_state* state, u8 reg, u8 data)
if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n", reg, data, ret);
- return (ret != 1) ? -EFAULT : 0;
+ return (ret != 1) ? -EIO : 0;
}
static u8 nxt6000_readreg(struct nxt6000_state* state, u8 reg)
diff --git a/drivers/media/dvb/frontends/s5h1420.c b/drivers/media/dvb/frontends/s5h1420.c
index 17f8cdf8afef..3879d2e378aa 100644
--- a/drivers/media/dvb/frontends/s5h1420.c
+++ b/drivers/media/dvb/frontends/s5h1420.c
@@ -634,7 +634,7 @@ static int s5h1420_set_frontend(struct dvb_frontend* fe,
struct s5h1420_state* state = fe->demodulator_priv;
int frequency_delta;
struct dvb_frontend_tune_settings fesettings;
- uint8_t clock_settting;
+ uint8_t clock_setting;
dprintk("enter %s\n", __func__);
@@ -684,19 +684,19 @@ static int s5h1420_set_frontend(struct dvb_frontend* fe,
switch (state->fclk) {
default:
case 88000000:
- clock_settting = 80;
+ clock_setting = 80;
break;
case 86000000:
- clock_settting = 78;
+ clock_setting = 78;
break;
case 80000000:
- clock_settting = 72;
+ clock_setting = 72;
break;
case 59000000:
- clock_settting = 51;
+ clock_setting = 51;
break;
case 44000000:
- clock_settting = 36;
+ clock_setting = 36;
break;
}
dprintk("pll01: %d, ToneFreq: %d\n", state->fclk/1000000 - 8, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32));
diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c
index d70eee00f33a..117a56926dca 100644
--- a/drivers/media/dvb/frontends/stb0899_algo.c
+++ b/drivers/media/dvb/frontends/stb0899_algo.c
@@ -358,6 +358,9 @@ static enum stb0899_status stb0899_check_data(struct stb0899_state *state)
else
dataTime = 500;
+ /* clear previous failed END_LOOPVIT */
+ stb0899_read_reg(state, STB0899_VSTATUS);
+
stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */
while (1) {
/* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */
diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c
index 37a222d9ddb3..8408ef877b4b 100644
--- a/drivers/media/dvb/frontends/stb0899_drv.c
+++ b/drivers/media/dvb/frontends/stb0899_drv.c
@@ -706,7 +706,7 @@ static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_ma
stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
for (i = 0; i < cmd->msg_len; i++) {
/* wait for FIFO empty */
- if (stb0899_wait_diseqc_fifo_empty(state, 10) < 0)
+ if (stb0899_wait_diseqc_fifo_empty(state, 100) < 0)
return -ETIMEDOUT;
stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]);
@@ -1426,9 +1426,9 @@ static void stb0899_set_iterations(struct stb0899_state *state)
if (iter_scale > config->ldpc_max_iter)
iter_scale = config->ldpc_max_iter;
- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, MAX_ITER);
+ reg = STB0899_READ_S2REG(STB0899_S2FEC, MAX_ITER);
STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale);
- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg);
+ stb0899_write_s2reg(state, STB0899_S2FEC, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg);
}
static enum dvbfe_search stb0899_search(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c
index 8e0cfadba688..0aa3962ff18b 100644
--- a/drivers/media/dvb/frontends/stv0288.c
+++ b/drivers/media/dvb/frontends/stv0288.c
@@ -127,6 +127,11 @@ static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate)
if ((srate < 1000000) || (srate > 45000000))
return -EINVAL;
+ stv0288_writeregI(state, 0x22, 0);
+ stv0288_writeregI(state, 0x23, 0);
+ stv0288_writeregI(state, 0x2b, 0xff);
+ stv0288_writeregI(state, 0x2c, 0xf7);
+
temp = (unsigned int)srate / 1000;
temp = temp * 32768;
@@ -461,6 +466,7 @@ static int stv0288_set_frontend(struct dvb_frontend *fe,
char tm;
unsigned char tda[3];
+ u8 reg, time_out = 0;
dprintk("%s : FE_SET_FRONTEND\n", __func__);
@@ -488,22 +494,29 @@ static int stv0288_set_frontend(struct dvb_frontend *fe,
/* Carrier lock control register */
stv0288_writeregI(state, 0x15, 0xc5);
- tda[0] = 0x2b; /* CFRM */
tda[2] = 0x0; /* CFRL */
- for (tm = -6; tm < 7;) {
+ for (tm = -9; tm < 7;) {
/* Viterbi status */
- if (stv0288_readreg(state, 0x24) & 0x8)
- break;
-
- tda[2] += 40;
- if (tda[2] < 40)
+ reg = stv0288_readreg(state, 0x24);
+ if (reg & 0x8)
+ break;
+ if (reg & 0x80) {
+ time_out++;
+ if (time_out > 10)
+ break;
+ tda[2] += 40;
+ if (tda[2] < 40)
+ tm++;
+ } else {
tm++;
+ tda[2] = 0;
+ time_out = 0;
+ }
tda[1] = (unsigned char)tm;
stv0288_writeregI(state, 0x2b, tda[1]);
stv0288_writeregI(state, 0x2c, tda[2]);
udelay(30);
}
-
state->tuner_frequency = c->frequency;
state->fec_inner = FEC_AUTO;
state->symbol_rate = c->symbol_rate;
diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c
index 52d8712411e5..ebda41936b90 100644
--- a/drivers/media/dvb/frontends/stv090x.c
+++ b/drivers/media/dvb/frontends/stv090x.c
@@ -3463,9 +3463,15 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron
static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct stv090x_state *state = fe->demodulator_priv;
- u32 reg;
+ u32 reg, dstatus;
u8 search_state;
+ *status = 0;
+
+ dstatus = STV090x_READ_DEMOD(state, DSTATUS);
+ if (STV090x_GETFIELD_Px(dstatus, CAR_LOCK_FIELD))
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
+
reg = STV090x_READ_DEMOD(state, DMDSTATE);
search_state = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD);
@@ -3474,41 +3480,30 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
case 1: /* first PLH detected */
default:
dprintk(FE_DEBUG, 1, "Status: Unlocked (Searching ..)");
- *status = 0;
break;
case 2: /* DVB-S2 mode */
dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2");
- reg = STV090x_READ_DEMOD(state, DSTATUS);
- if (STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD)) {
+ if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) {
reg = STV090x_READ_DEMOD(state, PDELSTATUS1);
if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) {
+ *status |= FE_HAS_VITERBI;
reg = STV090x_READ_DEMOD(state, TSSTATUS);
- if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) {
- *status = FE_HAS_SIGNAL |
- FE_HAS_CARRIER |
- FE_HAS_VITERBI |
- FE_HAS_SYNC |
- FE_HAS_LOCK;
- }
+ if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD))
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
}
}
break;
case 3: /* DVB-S1/legacy mode */
dprintk(FE_DEBUG, 1, "Delivery system: DVB-S");
- reg = STV090x_READ_DEMOD(state, DSTATUS);
- if (STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD)) {
+ if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) {
reg = STV090x_READ_DEMOD(state, VSTATUSVIT);
if (STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD)) {
+ *status |= FE_HAS_VITERBI;
reg = STV090x_READ_DEMOD(state, TSSTATUS);
- if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) {
- *status = FE_HAS_SIGNAL |
- FE_HAS_CARRIER |
- FE_HAS_VITERBI |
- FE_HAS_SYNC |
- FE_HAS_LOCK;
- }
+ if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD))
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
}
}
break;
diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb/frontends/tda10048.c
index 93f6a75c238e..7f105946a434 100644
--- a/drivers/media/dvb/frontends/tda10048.c
+++ b/drivers/media/dvb/frontends/tda10048.c
@@ -206,15 +206,16 @@ static struct init_tab {
static struct pll_tab {
u32 clk_freq_khz;
u32 if_freq_khz;
- u8 m, n, p;
} pll_tab[] = {
- { TDA10048_CLK_4000, TDA10048_IF_36130, 10, 0, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_3300, 10, 3, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_3500, 10, 3, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_3800, 10, 3, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_4000, 10, 3, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_4300, 10, 3, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_36130, 10, 3, 0 },
+ { TDA10048_CLK_4000, TDA10048_IF_36130 },
+ { TDA10048_CLK_16000, TDA10048_IF_3300 },
+ { TDA10048_CLK_16000, TDA10048_IF_3500 },
+ { TDA10048_CLK_16000, TDA10048_IF_3800 },
+ { TDA10048_CLK_16000, TDA10048_IF_4000 },
+ { TDA10048_CLK_16000, TDA10048_IF_4300 },
+ { TDA10048_CLK_16000, TDA10048_IF_4500 },
+ { TDA10048_CLK_16000, TDA10048_IF_5000 },
+ { TDA10048_CLK_16000, TDA10048_IF_36130 },
};
static int tda10048_writereg(struct tda10048_state *state, u8 reg, u8 data)
@@ -460,9 +461,6 @@ static int tda10048_set_if(struct dvb_frontend *fe, enum fe_bandwidth bw)
state->freq_if_hz = pll_tab[i].if_freq_khz * 1000;
state->xtal_hz = pll_tab[i].clk_freq_khz * 1000;
- state->pll_mfactor = pll_tab[i].m;
- state->pll_nfactor = pll_tab[i].n;
- state->pll_pfactor = pll_tab[i].p;
break;
}
}
@@ -781,6 +779,10 @@ static int tda10048_init(struct dvb_frontend *fe)
dprintk(1, "%s()\n", __func__);
+ /* PLL */
+ init_tab[4].data = (u8)(state->pll_mfactor);
+ init_tab[5].data = (u8)(state->pll_nfactor) | 0x40;
+
/* Apply register defaults */
for (i = 0; i < ARRAY_SIZE(init_tab); i++)
tda10048_writereg(state, init_tab[i].reg, init_tab[i].data);
@@ -1123,7 +1125,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config,
/* setup the state and clone the config */
memcpy(&state->config, config, sizeof(*config));
state->i2c = i2c;
- state->fwloaded = 0;
+ state->fwloaded = config->no_firmware;
state->bandwidth = BANDWIDTH_8_MHZ;
/* check if the demod is present */
@@ -1135,6 +1137,17 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config,
sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
+ /* set pll */
+ if (config->set_pll) {
+ state->pll_mfactor = config->pll_m;
+ state->pll_nfactor = config->pll_n;
+ state->pll_pfactor = config->pll_p;
+ } else {
+ state->pll_mfactor = 10;
+ state->pll_nfactor = 3;
+ state->pll_pfactor = 0;
+ }
+
/* Establish any defaults the the user didn't pass */
tda10048_establish_defaults(&state->frontend);
diff --git a/drivers/media/dvb/frontends/tda10048.h b/drivers/media/dvb/frontends/tda10048.h
index 8828ceaf74bb..fb2ef5ac9487 100644
--- a/drivers/media/dvb/frontends/tda10048.h
+++ b/drivers/media/dvb/frontends/tda10048.h
@@ -51,6 +51,7 @@ struct tda10048_config {
#define TDA10048_IF_4300 4300
#define TDA10048_IF_4500 4500
#define TDA10048_IF_4750 4750
+#define TDA10048_IF_5000 5000
#define TDA10048_IF_36130 36130
u16 dtv6_if_freq_khz;
u16 dtv7_if_freq_khz;
@@ -62,6 +63,13 @@ struct tda10048_config {
/* Disable I2C gate access */
u8 disable_gate_access;
+
+ bool no_firmware;
+
+ bool set_pll;
+ u8 pll_m;
+ u8 pll_p;
+ u8 pll_n;
};
#if defined(CONFIG_DVB_TDA10048) || \
diff --git a/drivers/media/dvb/frontends/tda10071.c b/drivers/media/dvb/frontends/tda10071.c
new file mode 100644
index 000000000000..0c37434d19e2
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10071.c
@@ -0,0 +1,1269 @@
+/*
+ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "tda10071_priv.h"
+
+int tda10071_debug;
+module_param_named(debug, tda10071_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+static struct dvb_frontend_ops tda10071_ops;
+
+/* write multiple registers */
+static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
+ int len)
+{
+ int ret;
+ u8 buf[len+1];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_address,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple registers */
+static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
+ int len)
+{
+ int ret;
+ u8 buf[len];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg.i2c_address,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg.i2c_address,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ memcpy(val, buf, len);
+ ret = 0;
+ } else {
+ warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* write single register */
+static int tda10071_wr_reg(struct tda10071_priv *priv, u8 reg, u8 val)
+{
+ return tda10071_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int tda10071_rd_reg(struct tda10071_priv *priv, u8 reg, u8 *val)
+{
+ return tda10071_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+int tda10071_wr_reg_mask(struct tda10071_priv *priv, u8 reg, u8 val, u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = tda10071_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return tda10071_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register with mask */
+int tda10071_rd_reg_mask(struct tda10071_priv *priv, u8 reg, u8 *val, u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = tda10071_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+/* execute firmware command */
+static int tda10071_cmd_execute(struct tda10071_priv *priv,
+ struct tda10071_cmd *cmd)
+{
+ int ret, i;
+ u8 tmp;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ /* write cmd and args for firmware */
+ ret = tda10071_wr_regs(priv, 0x00, cmd->args, cmd->len);
+ if (ret)
+ goto error;
+
+ /* start cmd execution */
+ ret = tda10071_wr_reg(priv, 0x1f, 1);
+ if (ret)
+ goto error;
+
+ /* wait cmd execution terminate */
+ for (i = 1000, tmp = 1; i && tmp; i--) {
+ ret = tda10071_rd_reg(priv, 0x1f, &tmp);
+ if (ret)
+ goto error;
+
+ usleep_range(200, 5000);
+ }
+
+ dbg("%s: loop=%d", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_set_tone(struct dvb_frontend *fe,
+ fe_sec_tone_mode_t fe_sec_tone_mode)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret;
+ u8 tone;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dbg("%s: tone_mode=%d", __func__, fe_sec_tone_mode);
+
+ switch (fe_sec_tone_mode) {
+ case SEC_TONE_ON:
+ tone = 1;
+ break;
+ case SEC_TONE_OFF:
+ tone = 0;
+ break;
+ default:
+ dbg("%s: invalid fe_sec_tone_mode", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ cmd.args[0x00] = CMD_LNB_PCB_CONFIG;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = 0x00;
+ cmd.args[0x03] = 0x00;
+ cmd.args[0x04] = tone;
+ cmd.len = 0x05;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t fe_sec_voltage)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret;
+ u8 voltage;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dbg("%s: voltage=%d", __func__, fe_sec_voltage);
+
+ switch (fe_sec_voltage) {
+ case SEC_VOLTAGE_13:
+ voltage = 0;
+ break;
+ case SEC_VOLTAGE_18:
+ voltage = 1;
+ break;
+ case SEC_VOLTAGE_OFF:
+ voltage = 0;
+ break;
+ default:
+ dbg("%s: invalid fe_sec_voltage", __func__);
+ ret = -EINVAL;
+ goto error;
+ };
+
+ cmd.args[0x00] = CMD_LNB_SET_DC_LEVEL;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = voltage;
+ cmd.len = 0x03;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *diseqc_cmd)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ u8 tmp;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dbg("%s: msg_len=%d", __func__, diseqc_cmd->msg_len);
+
+ if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 16) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* wait LNB TX */
+ for (i = 500, tmp = 0; i && !tmp; i--) {
+ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01);
+ if (ret)
+ goto error;
+
+ usleep_range(10000, 20000);
+ }
+
+ dbg("%s: loop=%d", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01);
+ if (ret)
+ goto error;
+
+ cmd.args[0x00] = CMD_LNB_SEND_DISEQC;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = 0;
+ cmd.args[0x03] = 0;
+ cmd.args[0x04] = 2;
+ cmd.args[0x05] = 0;
+ cmd.args[0x06] = diseqc_cmd->msg_len;
+ memcpy(&cmd.args[0x07], diseqc_cmd->msg, diseqc_cmd->msg_len);
+ cmd.len = 0x07 + diseqc_cmd->msg_len;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
+ struct dvb_diseqc_slave_reply *reply)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ u8 tmp;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dbg("%s:", __func__);
+
+ /* wait LNB RX */
+ for (i = 500, tmp = 0; i && !tmp; i--) {
+ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x02);
+ if (ret)
+ goto error;
+
+ usleep_range(10000, 20000);
+ }
+
+ dbg("%s: loop=%d", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* reply len */
+ ret = tda10071_rd_reg(priv, 0x46, &tmp);
+ if (ret)
+ goto error;
+
+ reply->msg_len = tmp & 0x1f; /* [4:0] */;
+ if (reply->msg_len > sizeof(reply->msg))
+ reply->msg_len = sizeof(reply->msg); /* truncate API max */
+
+ /* read reply */
+ cmd.args[0x00] = CMD_LNB_UPDATE_REPLY;
+ cmd.args[0x01] = 0;
+ cmd.len = 0x02;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_rd_regs(priv, cmd.len, reply->msg, reply->msg_len);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t fe_sec_mini_cmd)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ u8 tmp, burst;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dbg("%s: fe_sec_mini_cmd=%d", __func__, fe_sec_mini_cmd);
+
+ switch (fe_sec_mini_cmd) {
+ case SEC_MINI_A:
+ burst = 0;
+ break;
+ case SEC_MINI_B:
+ burst = 1;
+ break;
+ default:
+ dbg("%s: invalid fe_sec_mini_cmd", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* wait LNB TX */
+ for (i = 500, tmp = 0; i && !tmp; i--) {
+ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01);
+ if (ret)
+ goto error;
+
+ usleep_range(10000, 20000);
+ }
+
+ dbg("%s: loop=%d", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01);
+ if (ret)
+ goto error;
+
+ cmd.args[0x00] = CMD_LNB_SEND_TONEBURST;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = burst;
+ cmd.len = 0x03;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+
+ *status = 0;
+
+ if (!priv->warm) {
+ ret = 0;
+ goto error;
+ }
+
+ ret = tda10071_rd_reg(priv, 0x39, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp & 0x01) /* tuner PLL */
+ *status |= FE_HAS_SIGNAL;
+ if (tmp & 0x02) /* demod PLL */
+ *status |= FE_HAS_CARRIER;
+ if (tmp & 0x04) /* viterbi or LDPC*/
+ *status |= FE_HAS_VITERBI;
+ if (tmp & 0x08) /* RS or BCH */
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+ priv->fe_status = *status;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *snr = 0;
+ ret = 0;
+ goto error;
+ }
+
+ ret = tda10071_rd_regs(priv, 0x3a, buf, 2);
+ if (ret)
+ goto error;
+
+ /* Es/No dBx10 */
+ *snr = buf[0] << 8 | buf[1];
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret;
+ u8 tmp;
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *strength = 0;
+ ret = 0;
+ goto error;
+ }
+
+ cmd.args[0x00] = CMD_GET_AGCACC;
+ cmd.args[0x01] = 0;
+ cmd.len = 0x02;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ /* input power estimate dBm */
+ ret = tda10071_rd_reg(priv, 0x50, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp < 181)
+ tmp = 181; /* -75 dBm */
+ else if (tmp > 236)
+ tmp = 236; /* -20 dBm */
+
+ /* scale value to 0x0000-0xffff */
+ *strength = (tmp-181) * 0xffff / (236-181);
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i, len;
+ u8 tmp, reg, buf[8];
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *ber = priv->ber = 0;
+ ret = 0;
+ goto error;
+ }
+
+ switch (priv->delivery_system) {
+ case SYS_DVBS:
+ reg = 0x4c;
+ len = 8;
+ i = 1;
+ break;
+ case SYS_DVBS2:
+ reg = 0x4d;
+ len = 4;
+ i = 0;
+ break;
+ default:
+ *ber = priv->ber = 0;
+ return 0;
+ }
+
+ ret = tda10071_rd_reg(priv, reg, &tmp);
+ if (ret)
+ goto error;
+
+ if (priv->meas_count[i] == tmp) {
+ dbg("%s: meas not ready=%02x", __func__, tmp);
+ *ber = priv->ber;
+ return 0;
+ } else {
+ priv->meas_count[i] = tmp;
+ }
+
+ cmd.args[0x00] = CMD_BER_UPDATE_COUNTERS;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = i;
+ cmd.len = 0x03;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_rd_regs(priv, cmd.len, buf, len);
+ if (ret)
+ goto error;
+
+ if (priv->delivery_system == SYS_DVBS) {
+ *ber = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ priv->ucb += (buf[4] << 8) | buf[5];
+ } else {
+ *ber = (buf[0] << 8) | buf[1];
+ }
+ priv->ber = *ber;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ int ret = 0;
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *ucblocks = 0;
+ goto error;
+ }
+
+ /* UCB is updated when BER is read. Assume BER is read anyway. */
+
+ *ucblocks = priv->ucb;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u8 mode, rolloff, pilot, inversion, div;
+
+ dbg("%s: delivery_system=%d modulation=%d frequency=%d " \
+ "symbol_rate=%d inversion=%d pilot=%d rolloff=%d", __func__,
+ c->delivery_system, c->modulation, c->frequency,
+ c->symbol_rate, c->inversion, c->pilot, c->rolloff);
+
+ priv->delivery_system = SYS_UNDEFINED;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ switch (c->inversion) {
+ case INVERSION_OFF:
+ inversion = 1;
+ break;
+ case INVERSION_ON:
+ inversion = 0;
+ break;
+ case INVERSION_AUTO:
+ /* 2 = auto; try first on then off
+ * 3 = auto; try first off then on */
+ inversion = 3;
+ break;
+ default:
+ dbg("%s: invalid inversion", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ rolloff = 0;
+ pilot = 2;
+ break;
+ case SYS_DVBS2:
+ switch (c->rolloff) {
+ case ROLLOFF_20:
+ rolloff = 2;
+ break;
+ case ROLLOFF_25:
+ rolloff = 1;
+ break;
+ case ROLLOFF_35:
+ rolloff = 0;
+ break;
+ case ROLLOFF_AUTO:
+ default:
+ dbg("%s: invalid rolloff", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ switch (c->pilot) {
+ case PILOT_OFF:
+ pilot = 0;
+ break;
+ case PILOT_ON:
+ pilot = 1;
+ break;
+ case PILOT_AUTO:
+ pilot = 2;
+ break;
+ default:
+ dbg("%s: invalid pilot", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+ break;
+ default:
+ dbg("%s: invalid delivery_system", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ for (i = 0, mode = 0xff; i < ARRAY_SIZE(TDA10071_MODCOD); i++) {
+ if (c->delivery_system == TDA10071_MODCOD[i].delivery_system &&
+ c->modulation == TDA10071_MODCOD[i].modulation &&
+ c->fec_inner == TDA10071_MODCOD[i].fec) {
+ mode = TDA10071_MODCOD[i].val;
+ dbg("%s: mode found=%02x", __func__, mode);
+ break;
+ }
+ }
+
+ if (mode == 0xff) {
+ dbg("%s: invalid parameter combination", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (c->symbol_rate <= 5000000)
+ div = 14;
+ else
+ div = 4;
+
+ ret = tda10071_wr_reg(priv, 0x81, div);
+ if (ret)
+ goto error;
+
+ ret = tda10071_wr_reg(priv, 0xe3, div);
+ if (ret)
+ goto error;
+
+ cmd.args[0x00] = CMD_CHANGE_CHANNEL;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = mode;
+ cmd.args[0x03] = (c->frequency >> 16) & 0xff;
+ cmd.args[0x04] = (c->frequency >> 8) & 0xff;
+ cmd.args[0x05] = (c->frequency >> 0) & 0xff;
+ cmd.args[0x06] = ((c->symbol_rate / 1000) >> 8) & 0xff;
+ cmd.args[0x07] = ((c->symbol_rate / 1000) >> 0) & 0xff;
+ cmd.args[0x08] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff;
+ cmd.args[0x09] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff;
+ cmd.args[0x0a] = rolloff;
+ cmd.args[0x0b] = inversion;
+ cmd.args[0x0c] = pilot;
+ cmd.args[0x0d] = 0x00;
+ cmd.args[0x0e] = 0x00;
+ cmd.len = 0x0f;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ priv->delivery_system = c->delivery_system;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u8 buf[5], tmp;
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ ret = tda10071_rd_regs(priv, 0x30, buf, 5);
+ if (ret)
+ goto error;
+
+ tmp = buf[0] & 0x3f;
+ for (i = 0; i < ARRAY_SIZE(TDA10071_MODCOD); i++) {
+ if (tmp == TDA10071_MODCOD[i].val) {
+ c->modulation = TDA10071_MODCOD[i].modulation;
+ c->fec_inner = TDA10071_MODCOD[i].fec;
+ c->delivery_system = TDA10071_MODCOD[i].delivery_system;
+ }
+ }
+
+ switch ((buf[1] >> 0) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ switch ((buf[1] >> 7) & 0x01) {
+ case 0:
+ c->pilot = PILOT_OFF;
+ break;
+ case 1:
+ c->pilot = PILOT_ON;
+ break;
+ }
+
+ c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0);
+
+ ret = tda10071_rd_regs(priv, 0x52, buf, 3);
+ if (ret)
+ goto error;
+
+ c->symbol_rate = (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_init(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i, len, remaining, fw_size;
+ const struct firmware *fw;
+ u8 *fw_file = TDA10071_DEFAULT_FIRMWARE;
+ u8 tmp, buf[4];
+ struct tda10071_reg_val_mask tab[] = {
+ { 0xcd, 0x00, 0x07 },
+ { 0x80, 0x00, 0x02 },
+ { 0xcd, 0x00, 0xc0 },
+ { 0xce, 0x00, 0x1b },
+ { 0x9d, 0x00, 0x01 },
+ { 0x9d, 0x00, 0x02 },
+ { 0x9e, 0x00, 0x01 },
+ { 0x87, 0x00, 0x80 },
+ { 0xce, 0x00, 0x08 },
+ { 0xce, 0x00, 0x10 },
+ };
+ struct tda10071_reg_val_mask tab2[] = {
+ { 0xf1, 0x70, 0xff },
+ { 0x88, priv->cfg.pll_multiplier, 0x3f },
+ { 0x89, 0x00, 0x10 },
+ { 0x89, 0x10, 0x10 },
+ { 0xc0, 0x01, 0x01 },
+ { 0xc0, 0x00, 0x01 },
+ { 0xe0, 0xff, 0xff },
+ { 0xe0, 0x00, 0xff },
+ { 0x96, 0x1e, 0x7e },
+ { 0x8b, 0x08, 0x08 },
+ { 0x8b, 0x00, 0x08 },
+ { 0x8f, 0x1a, 0x7e },
+ { 0x8c, 0x68, 0xff },
+ { 0x8d, 0x08, 0xff },
+ { 0x8e, 0x4c, 0xff },
+ { 0x8f, 0x01, 0x01 },
+ { 0x8b, 0x04, 0x04 },
+ { 0x8b, 0x00, 0x04 },
+ { 0x87, 0x05, 0x07 },
+ { 0x80, 0x00, 0x20 },
+ { 0xc8, 0x01, 0xff },
+ { 0xb4, 0x47, 0xff },
+ { 0xb5, 0x9c, 0xff },
+ { 0xb6, 0x7d, 0xff },
+ { 0xba, 0x00, 0x03 },
+ { 0xb7, 0x47, 0xff },
+ { 0xb8, 0x9c, 0xff },
+ { 0xb9, 0x7d, 0xff },
+ { 0xba, 0x00, 0x0c },
+ { 0xc8, 0x00, 0xff },
+ { 0xcd, 0x00, 0x04 },
+ { 0xcd, 0x00, 0x20 },
+ { 0xe8, 0x02, 0xff },
+ { 0xcf, 0x20, 0xff },
+ { 0x9b, 0xd7, 0xff },
+ { 0x9a, 0x01, 0x03 },
+ { 0xa8, 0x05, 0x0f },
+ { 0xa8, 0x65, 0xf0 },
+ { 0xa6, 0xa0, 0xf0 },
+ { 0x9d, 0x50, 0xfc },
+ { 0x9e, 0x20, 0xe0 },
+ { 0xa3, 0x1c, 0x7c },
+ { 0xd5, 0x03, 0x03 },
+ };
+
+ /* firmware status */
+ ret = tda10071_rd_reg(priv, 0x51, &tmp);
+ if (ret)
+ goto error;
+
+ if (!tmp) {
+ /* warm state - wake up device from sleep */
+ priv->warm = 1;
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = tda10071_wr_reg_mask(priv, tab[i].reg,
+ tab[i].val, tab[i].mask);
+ if (ret)
+ goto error;
+ }
+
+ cmd.args[0x00] = CMD_SET_SLEEP_MODE;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = 0;
+ cmd.len = 0x03;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+ } else {
+ /* cold state - try to download firmware */
+ priv->warm = 0;
+
+ /* request the firmware, this will block and timeout */
+ ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
+ if (ret) {
+ err("did not find the firmware file. (%s) "
+ "Please see linux/Documentation/dvb/ for more" \
+ " details on firmware-problems. (%d)",
+ fw_file, ret);
+ goto error;
+ }
+
+ /* init */
+ for (i = 0; i < ARRAY_SIZE(tab2); i++) {
+ ret = tda10071_wr_reg_mask(priv, tab2[i].reg,
+ tab2[i].val, tab2[i].mask);
+ if (ret)
+ goto error_release_firmware;
+ }
+
+ /* download firmware */
+ ret = tda10071_wr_reg(priv, 0xe0, 0x7f);
+ if (ret)
+ goto error_release_firmware;
+
+ ret = tda10071_wr_reg(priv, 0xf7, 0x81);
+ if (ret)
+ goto error_release_firmware;
+
+ ret = tda10071_wr_reg(priv, 0xf8, 0x00);
+ if (ret)
+ goto error_release_firmware;
+
+ ret = tda10071_wr_reg(priv, 0xf9, 0x00);
+ if (ret)
+ goto error_release_firmware;
+
+ info("found a '%s' in cold state, will try to load a firmware",
+ tda10071_ops.info.name);
+
+ info("downloading firmware from file '%s'", fw_file);
+
+ /* do not download last byte */
+ fw_size = fw->size - 1;
+
+ for (remaining = fw_size; remaining > 0;
+ remaining -= (priv->cfg.i2c_wr_max - 1)) {
+ len = remaining;
+ if (len > (priv->cfg.i2c_wr_max - 1))
+ len = (priv->cfg.i2c_wr_max - 1);
+
+ ret = tda10071_wr_regs(priv, 0xfa,
+ (u8 *) &fw->data[fw_size - remaining], len);
+ if (ret) {
+ err("firmware download failed=%d", ret);
+ if (ret)
+ goto error_release_firmware;
+ }
+ }
+ release_firmware(fw);
+
+ ret = tda10071_wr_reg(priv, 0xf7, 0x0c);
+ if (ret)
+ goto error;
+
+ ret = tda10071_wr_reg(priv, 0xe0, 0x00);
+ if (ret)
+ goto error;
+
+ /* wait firmware start */
+ msleep(250);
+
+ /* firmware status */
+ ret = tda10071_rd_reg(priv, 0x51, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp) {
+ info("firmware did not run");
+ ret = -EFAULT;
+ goto error;
+ } else {
+ priv->warm = 1;
+ }
+
+ cmd.args[0x00] = CMD_GET_FW_VERSION;
+ cmd.len = 0x01;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_rd_regs(priv, cmd.len, buf, 4);
+ if (ret)
+ goto error;
+
+ info("firmware version %d.%d.%d.%d",
+ buf[0], buf[1], buf[2], buf[3]);
+ info("found a '%s' in warm state.", tda10071_ops.info.name);
+
+ ret = tda10071_rd_regs(priv, 0x81, buf, 2);
+ if (ret)
+ goto error;
+
+ cmd.args[0x00] = CMD_DEMOD_INIT;
+ cmd.args[0x01] = ((priv->cfg.xtal / 1000) >> 8) & 0xff;
+ cmd.args[0x02] = ((priv->cfg.xtal / 1000) >> 0) & 0xff;
+ cmd.args[0x03] = buf[0];
+ cmd.args[0x04] = buf[1];
+ cmd.args[0x05] = priv->cfg.pll_multiplier;
+ cmd.args[0x06] = priv->cfg.spec_inv;
+ cmd.args[0x07] = 0x00;
+ cmd.len = 0x08;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ cmd.args[0x00] = CMD_TUNER_INIT;
+ cmd.args[0x01] = 0x00;
+ cmd.args[0x02] = 0x00;
+ cmd.args[0x03] = 0x00;
+ cmd.args[0x04] = 0x00;
+ cmd.args[0x05] = 0x14;
+ cmd.args[0x06] = 0x00;
+ cmd.args[0x07] = 0x03;
+ cmd.args[0x08] = 0x02;
+ cmd.args[0x09] = 0x02;
+ cmd.args[0x0a] = 0x00;
+ cmd.args[0x0b] = 0x00;
+ cmd.args[0x0c] = 0x00;
+ cmd.args[0x0d] = 0x00;
+ cmd.args[0x0e] = 0x00;
+ cmd.len = 0x0f;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ cmd.args[0x00] = CMD_MPEG_CONFIG;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = priv->cfg.ts_mode;
+ cmd.args[0x03] = 0x00;
+ cmd.args[0x04] = 0x04;
+ cmd.args[0x05] = 0x00;
+ cmd.len = 0x06;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_wr_reg_mask(priv, 0xf0, 0x01, 0x01);
+ if (ret)
+ goto error;
+
+ cmd.args[0x00] = CMD_LNB_CONFIG;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = 150;
+ cmd.args[0x03] = 3;
+ cmd.args[0x04] = 22;
+ cmd.args[0x05] = 1;
+ cmd.args[0x06] = 1;
+ cmd.args[0x07] = 30;
+ cmd.args[0x08] = 30;
+ cmd.args[0x09] = 30;
+ cmd.args[0x0a] = 30;
+ cmd.len = 0x0b;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ cmd.args[0x00] = CMD_BER_CONTROL;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = 14;
+ cmd.args[0x03] = 14;
+ cmd.len = 0x04;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+ }
+
+ return ret;
+error_release_firmware:
+ release_firmware(fw);
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_sleep(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ struct tda10071_reg_val_mask tab[] = {
+ { 0xcd, 0x07, 0x07 },
+ { 0x80, 0x02, 0x02 },
+ { 0xcd, 0xc0, 0xc0 },
+ { 0xce, 0x1b, 0x1b },
+ { 0x9d, 0x01, 0x01 },
+ { 0x9d, 0x02, 0x02 },
+ { 0x9e, 0x01, 0x01 },
+ { 0x87, 0x80, 0x80 },
+ { 0xce, 0x08, 0x08 },
+ { 0xce, 0x10, 0x10 },
+ };
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ cmd.args[0x00] = CMD_SET_SLEEP_MODE;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = 1;
+ cmd.len = 0x03;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = tda10071_wr_reg_mask(priv, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret)
+ goto error;
+ }
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 8000;
+ s->step_size = 0;
+ s->max_drift = 0;
+
+ return 0;
+}
+
+static void tda10071_release(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ kfree(priv);
+}
+
+struct dvb_frontend *tda10071_attach(const struct tda10071_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct tda10071_priv *priv = NULL;
+ u8 tmp;
+
+ /* allocate memory for the internal priv */
+ priv = kzalloc(sizeof(struct tda10071_priv), GFP_KERNEL);
+ if (priv == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ memcpy(&priv->cfg, config, sizeof(struct tda10071_config));
+
+ /* chip ID */
+ ret = tda10071_rd_reg(priv, 0xff, &tmp);
+ if (ret || tmp != 0x0f)
+ goto error;
+
+ /* chip type */
+ ret = tda10071_rd_reg(priv, 0xdd, &tmp);
+ if (ret || tmp != 0x00)
+ goto error;
+
+ /* chip version */
+ ret = tda10071_rd_reg(priv, 0xfe, &tmp);
+ if (ret || tmp != 0x01)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ return &priv->fe;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(tda10071_attach);
+
+static struct dvb_frontend_ops tda10071_ops = {
+ .info = {
+ .name = "NXP TDA10071",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_8_9 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_RECOVER |
+ FE_CAN_2G_MODULATION
+ },
+
+ .release = tda10071_release,
+
+ .get_tune_settings = tda10071_get_tune_settings,
+
+ .init = tda10071_init,
+ .sleep = tda10071_sleep,
+
+ .set_frontend = tda10071_set_frontend,
+ .get_frontend = tda10071_get_frontend,
+
+ .read_status = tda10071_read_status,
+ .read_snr = tda10071_read_snr,
+ .read_signal_strength = tda10071_read_signal_strength,
+ .read_ber = tda10071_read_ber,
+ .read_ucblocks = tda10071_read_ucblocks,
+
+ .diseqc_send_master_cmd = tda10071_diseqc_send_master_cmd,
+ .diseqc_recv_slave_reply = tda10071_diseqc_recv_slave_reply,
+ .diseqc_send_burst = tda10071_diseqc_send_burst,
+
+ .set_tone = tda10071_set_tone,
+ .set_voltage = tda10071_set_voltage,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda10071.h b/drivers/media/dvb/frontends/tda10071.h
new file mode 100644
index 000000000000..21163c4b555c
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10071.h
@@ -0,0 +1,81 @@
+/*
+ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TDA10071_H
+#define TDA10071_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda10071_config {
+ /* Demodulator I2C address.
+ * Default: none, must set
+ * Values: 0x55,
+ */
+ u8 i2c_address;
+
+ /* Max bytes I2C provider can write at once.
+ * Note: Buffer is taken from the stack currently!
+ * Default: none, must set
+ * Values:
+ */
+ u16 i2c_wr_max;
+
+ /* TS output mode.
+ * Default: TDA10071_TS_SERIAL
+ * Values:
+ */
+#define TDA10071_TS_SERIAL 0
+#define TDA10071_TS_PARALLEL 1
+ u8 ts_mode;
+
+ /* Input spectrum inversion.
+ * Default: 0
+ * Values: 0, 1
+ */
+ bool spec_inv;
+
+ /* Xtal frequency Hz
+ * Default: none, must set
+ * Values:
+ */
+ u32 xtal;
+
+ /* PLL multiplier.
+ * Default: none, must set
+ * Values:
+ */
+ u8 pll_multiplier;
+};
+
+
+#if defined(CONFIG_DVB_TDA10071) || \
+ (defined(CONFIG_DVB_TDA10071_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda10071_attach(
+ const struct tda10071_config *config, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *tda10071_attach(
+ const struct tda10071_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* TDA10071_H */
diff --git a/drivers/media/dvb/frontends/tda10071_priv.h b/drivers/media/dvb/frontends/tda10071_priv.h
new file mode 100644
index 000000000000..93c5e6317f07
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10071_priv.h
@@ -0,0 +1,122 @@
+/*
+ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TDA10071_PRIV
+#define TDA10071_PRIV
+
+#include "dvb_frontend.h"
+#include "tda10071.h"
+#include <linux/firmware.h>
+
+#define LOG_PREFIX "tda10071"
+
+#undef dbg
+#define dbg(f, arg...) \
+ if (tda10071_debug) \
+ printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+struct tda10071_priv {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct tda10071_config cfg;
+
+ u8 meas_count[2];
+ u32 ber;
+ u32 ucb;
+ fe_status_t fe_status;
+ fe_delivery_system_t delivery_system;
+ bool warm; /* FW running */
+};
+
+static struct tda10071_modcod {
+ fe_delivery_system_t delivery_system;
+ fe_modulation_t modulation;
+ fe_code_rate_t fec;
+ u8 val;
+} TDA10071_MODCOD[] = {
+ /* NBC-QPSK */
+ { SYS_DVBS2, QPSK, FEC_AUTO, 0x00 },
+ { SYS_DVBS2, QPSK, FEC_1_2, 0x04 },
+ { SYS_DVBS2, QPSK, FEC_3_5, 0x05 },
+ { SYS_DVBS2, QPSK, FEC_2_3, 0x06 },
+ { SYS_DVBS2, QPSK, FEC_3_4, 0x07 },
+ { SYS_DVBS2, QPSK, FEC_4_5, 0x08 },
+ { SYS_DVBS2, QPSK, FEC_5_6, 0x09 },
+ { SYS_DVBS2, QPSK, FEC_8_9, 0x0a },
+ { SYS_DVBS2, QPSK, FEC_9_10, 0x0b },
+ /* 8PSK */
+ { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c },
+ { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d },
+ { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e },
+ { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f },
+ { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 },
+ { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 },
+ /* QPSK */
+ { SYS_DVBS, QPSK, FEC_AUTO, 0x2d },
+ { SYS_DVBS, QPSK, FEC_1_2, 0x2e },
+ { SYS_DVBS, QPSK, FEC_2_3, 0x2f },
+ { SYS_DVBS, QPSK, FEC_3_4, 0x30 },
+ { SYS_DVBS, QPSK, FEC_5_6, 0x31 },
+ { SYS_DVBS, QPSK, FEC_7_8, 0x32 },
+};
+
+struct tda10071_reg_val_mask {
+ u8 reg;
+ u8 val;
+ u8 mask;
+};
+
+/* firmware filename */
+#define TDA10071_DEFAULT_FIRMWARE "dvb-fe-tda10071.fw"
+
+/* firmware commands */
+#define CMD_DEMOD_INIT 0x10
+#define CMD_CHANGE_CHANNEL 0x11
+#define CMD_MPEG_CONFIG 0x13
+#define CMD_TUNER_INIT 0x15
+#define CMD_GET_AGCACC 0x1a
+
+#define CMD_LNB_CONFIG 0x20
+#define CMD_LNB_SEND_DISEQC 0x21
+#define CMD_LNB_SET_DC_LEVEL 0x22
+#define CMD_LNB_PCB_CONFIG 0x23
+#define CMD_LNB_SEND_TONEBURST 0x24
+#define CMD_LNB_UPDATE_REPLY 0x25
+
+#define CMD_GET_FW_VERSION 0x35
+#define CMD_SET_SLEEP_MODE 0x36
+#define CMD_BER_CONTROL 0x3e
+#define CMD_BER_UPDATE_COUNTERS 0x3f
+
+/* firmare command struct */
+#define TDA10071_ARGLEN 0x1e
+struct tda10071_cmd {
+ u8 args[TDA10071_ARGLEN];
+ u8 len;
+};
+
+
+#endif /* TDA10071_PRIV */
diff --git a/drivers/media/dvb/frontends/tda18271c2dd.c b/drivers/media/dvb/frontends/tda18271c2dd.c
new file mode 100644
index 000000000000..1b1bf200c55c
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271c2dd.c
@@ -0,0 +1,1251 @@
+/*
+ * tda18271c2dd: Driver for the TDA18271C2 tuner
+ *
+ * Copyright (C) 2010 Digital Devices GmbH
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/version.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+
+struct SStandardParam {
+ s32 m_IFFrequency;
+ u32 m_BandWidth;
+ u8 m_EP3_4_0;
+ u8 m_EB22;
+};
+
+struct SMap {
+ u32 m_Frequency;
+ u8 m_Param;
+};
+
+struct SMapI {
+ u32 m_Frequency;
+ s32 m_Param;
+};
+
+struct SMap2 {
+ u32 m_Frequency;
+ u8 m_Param1;
+ u8 m_Param2;
+};
+
+struct SRFBandMap {
+ u32 m_RF_max;
+ u32 m_RF1_Default;
+ u32 m_RF2_Default;
+ u32 m_RF3_Default;
+};
+
+enum ERegister {
+ ID = 0,
+ TM,
+ PL,
+ EP1, EP2, EP3, EP4, EP5,
+ CPD, CD1, CD2, CD3,
+ MPD, MD1, MD2, MD3,
+ EB1, EB2, EB3, EB4, EB5, EB6, EB7, EB8, EB9, EB10,
+ EB11, EB12, EB13, EB14, EB15, EB16, EB17, EB18, EB19, EB20,
+ EB21, EB22, EB23,
+ NUM_REGS
+};
+
+struct tda_state {
+ struct i2c_adapter *i2c;
+ u8 adr;
+
+ u32 m_Frequency;
+ u32 IF;
+
+ u8 m_IFLevelAnalog;
+ u8 m_IFLevelDigital;
+ u8 m_IFLevelDVBC;
+ u8 m_IFLevelDVBT;
+
+ u8 m_EP4;
+ u8 m_EP3_Standby;
+
+ bool m_bMaster;
+
+ s32 m_SettlingTime;
+
+ u8 m_Regs[NUM_REGS];
+
+ /* Tracking filter settings for band 0..6 */
+ u32 m_RF1[7];
+ s32 m_RF_A1[7];
+ s32 m_RF_B1[7];
+ u32 m_RF2[7];
+ s32 m_RF_A2[7];
+ s32 m_RF_B2[7];
+ u32 m_RF3[7];
+
+ u8 m_TMValue_RFCal; /* Calibration temperatur */
+
+ bool m_bFMInput; /* true to use Pin 8 for FM Radio */
+
+};
+
+static int PowerScan(struct tda_state *state,
+ u8 RFBand, u32 RF_in,
+ u32 *pRF_Out, bool *pbcal);
+
+static int i2c_readn(struct i2c_adapter *adapter, u8 adr, u8 *data, int len)
+{
+ struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD,
+ .buf = data, .len = len} };
+ return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+{
+ struct i2c_msg msg = {.addr = adr, .flags = 0,
+ .buf = data, .len = len};
+
+ if (i2c_transfer(adap, &msg, 1) != 1) {
+ printk(KERN_ERR "tda18271c2dd: i2c write error at addr %i\n", adr);
+ return -1;
+ }
+ return 0;
+}
+
+static int WriteRegs(struct tda_state *state,
+ u8 SubAddr, u8 *Regs, u16 nRegs)
+{
+ u8 data[nRegs+1];
+
+ data[0] = SubAddr;
+ memcpy(data + 1, Regs, nRegs);
+ return i2c_write(state->i2c, state->adr, data, nRegs+1);
+}
+
+static int WriteReg(struct tda_state *state, u8 SubAddr, u8 Reg)
+{
+ u8 msg[2] = {SubAddr, Reg};
+
+ return i2c_write(state->i2c, state->adr, msg, 2);
+}
+
+static int Read(struct tda_state *state, u8 * Regs)
+{
+ return i2c_readn(state->i2c, state->adr, Regs, 16);
+}
+
+static int ReadExtented(struct tda_state *state, u8 * Regs)
+{
+ return i2c_readn(state->i2c, state->adr, Regs, NUM_REGS);
+}
+
+static int UpdateRegs(struct tda_state *state, u8 RegFrom, u8 RegTo)
+{
+ return WriteRegs(state, RegFrom,
+ &state->m_Regs[RegFrom], RegTo-RegFrom+1);
+}
+static int UpdateReg(struct tda_state *state, u8 Reg)
+{
+ return WriteReg(state, Reg, state->m_Regs[Reg]);
+}
+
+#include "tda18271c2dd_maps.h"
+
+static void reset(struct tda_state *state)
+{
+ u32 ulIFLevelAnalog = 0;
+ u32 ulIFLevelDigital = 2;
+ u32 ulIFLevelDVBC = 7;
+ u32 ulIFLevelDVBT = 6;
+ u32 ulXTOut = 0;
+ u32 ulStandbyMode = 0x06; /* Send in stdb, but leave osc on */
+ u32 ulSlave = 0;
+ u32 ulFMInput = 0;
+ u32 ulSettlingTime = 100;
+
+ state->m_Frequency = 0;
+ state->m_SettlingTime = 100;
+ state->m_IFLevelAnalog = (ulIFLevelAnalog & 0x07) << 2;
+ state->m_IFLevelDigital = (ulIFLevelDigital & 0x07) << 2;
+ state->m_IFLevelDVBC = (ulIFLevelDVBC & 0x07) << 2;
+ state->m_IFLevelDVBT = (ulIFLevelDVBT & 0x07) << 2;
+
+ state->m_EP4 = 0x20;
+ if (ulXTOut != 0)
+ state->m_EP4 |= 0x40;
+
+ state->m_EP3_Standby = ((ulStandbyMode & 0x07) << 5) | 0x0F;
+ state->m_bMaster = (ulSlave == 0);
+
+ state->m_SettlingTime = ulSettlingTime;
+
+ state->m_bFMInput = (ulFMInput == 2);
+}
+
+static bool SearchMap1(struct SMap Map[],
+ u32 Frequency, u8 *pParam)
+{
+ int i = 0;
+
+ while ((Map[i].m_Frequency != 0) && (Frequency > Map[i].m_Frequency))
+ i += 1;
+ if (Map[i].m_Frequency == 0)
+ return false;
+ *pParam = Map[i].m_Param;
+ return true;
+}
+
+static bool SearchMap2(struct SMapI Map[],
+ u32 Frequency, s32 *pParam)
+{
+ int i = 0;
+
+ while ((Map[i].m_Frequency != 0) &&
+ (Frequency > Map[i].m_Frequency))
+ i += 1;
+ if (Map[i].m_Frequency == 0)
+ return false;
+ *pParam = Map[i].m_Param;
+ return true;
+}
+
+static bool SearchMap3(struct SMap2 Map[], u32 Frequency,
+ u8 *pParam1, u8 *pParam2)
+{
+ int i = 0;
+
+ while ((Map[i].m_Frequency != 0) &&
+ (Frequency > Map[i].m_Frequency))
+ i += 1;
+ if (Map[i].m_Frequency == 0)
+ return false;
+ *pParam1 = Map[i].m_Param1;
+ *pParam2 = Map[i].m_Param2;
+ return true;
+}
+
+static bool SearchMap4(struct SRFBandMap Map[],
+ u32 Frequency, u8 *pRFBand)
+{
+ int i = 0;
+
+ while (i < 7 && (Frequency > Map[i].m_RF_max))
+ i += 1;
+ if (i == 7)
+ return false;
+ *pRFBand = i;
+ return true;
+}
+
+static int ThermometerRead(struct tda_state *state, u8 *pTM_Value)
+{
+ int status = 0;
+
+ do {
+ u8 Regs[16];
+ state->m_Regs[TM] |= 0x10;
+ status = UpdateReg(state, TM);
+ if (status < 0)
+ break;
+ status = Read(state, Regs);
+ if (status < 0)
+ break;
+ if (((Regs[TM] & 0x0F) == 0 && (Regs[TM] & 0x20) == 0x20) ||
+ ((Regs[TM] & 0x0F) == 8 && (Regs[TM] & 0x20) == 0x00)) {
+ state->m_Regs[TM] ^= 0x20;
+ status = UpdateReg(state, TM);
+ if (status < 0)
+ break;
+ msleep(10);
+ status = Read(state, Regs);
+ if (status < 0)
+ break;
+ }
+ *pTM_Value = (Regs[TM] & 0x20)
+ ? m_Thermometer_Map_2[Regs[TM] & 0x0F]
+ : m_Thermometer_Map_1[Regs[TM] & 0x0F] ;
+ state->m_Regs[TM] &= ~0x10; /* Thermometer off */
+ status = UpdateReg(state, TM);
+ if (status < 0)
+ break;
+ state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 ????????? */
+ status = UpdateReg(state, EP4);
+ if (status < 0)
+ break;
+ } while (0);
+
+ return status;
+}
+
+static int StandBy(struct tda_state *state)
+{
+ int status = 0;
+ do {
+ state->m_Regs[EB12] &= ~0x20; /* PD_AGC1_Det = 0 */
+ status = UpdateReg(state, EB12);
+ if (status < 0)
+ break;
+ state->m_Regs[EB18] &= ~0x83; /* AGC1_loop_off = 0, AGC1_Gain = 6 dB */
+ status = UpdateReg(state, EB18);
+ if (status < 0)
+ break;
+ state->m_Regs[EB21] |= 0x03; /* AGC2_Gain = -6 dB */
+ state->m_Regs[EP3] = state->m_EP3_Standby;
+ status = UpdateReg(state, EP3);
+ if (status < 0)
+ break;
+ state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LP_Fc[2] = 0 */
+ status = UpdateRegs(state, EB21, EB23);
+ if (status < 0)
+ break;
+ } while (0);
+ return status;
+}
+
+static int CalcMainPLL(struct tda_state *state, u32 freq)
+{
+
+ u8 PostDiv;
+ u8 Div;
+ u64 OscFreq;
+ u32 MainDiv;
+
+ if (!SearchMap3(m_Main_PLL_Map, freq, &PostDiv, &Div))
+ return -EINVAL;
+
+ OscFreq = (u64) freq * (u64) Div;
+ OscFreq *= (u64) 16384;
+ do_div(OscFreq, (u64)16000000);
+ MainDiv = OscFreq;
+
+ state->m_Regs[MPD] = PostDiv & 0x77;
+ state->m_Regs[MD1] = ((MainDiv >> 16) & 0x7F);
+ state->m_Regs[MD2] = ((MainDiv >> 8) & 0xFF);
+ state->m_Regs[MD3] = (MainDiv & 0xFF);
+
+ return UpdateRegs(state, MPD, MD3);
+}
+
+static int CalcCalPLL(struct tda_state *state, u32 freq)
+{
+ u8 PostDiv;
+ u8 Div;
+ u64 OscFreq;
+ u32 CalDiv;
+
+ if (!SearchMap3(m_Cal_PLL_Map, freq, &PostDiv, &Div))
+ return -EINVAL;
+
+ OscFreq = (u64)freq * (u64)Div;
+ /* CalDiv = u32( OscFreq * 16384 / 16000000 ); */
+ OscFreq *= (u64)16384;
+ do_div(OscFreq, (u64)16000000);
+ CalDiv = OscFreq;
+
+ state->m_Regs[CPD] = PostDiv;
+ state->m_Regs[CD1] = ((CalDiv >> 16) & 0xFF);
+ state->m_Regs[CD2] = ((CalDiv >> 8) & 0xFF);
+ state->m_Regs[CD3] = (CalDiv & 0xFF);
+
+ return UpdateRegs(state, CPD, CD3);
+}
+
+static int CalibrateRF(struct tda_state *state,
+ u8 RFBand, u32 freq, s32 *pCprog)
+{
+ int status = 0;
+ u8 Regs[NUM_REGS];
+ do {
+ u8 BP_Filter = 0;
+ u8 GainTaper = 0;
+ u8 RFC_K = 0;
+ u8 RFC_M = 0;
+
+ state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 */
+ status = UpdateReg(state, EP4);
+ if (status < 0)
+ break;
+ state->m_Regs[EB18] |= 0x03; /* AGC1_Gain = 3 */
+ status = UpdateReg(state, EB18);
+ if (status < 0)
+ break;
+
+ /* Switching off LT (as datasheet says) causes calibration on C1 to fail */
+ /* (Readout of Cprog is allways 255) */
+ if (state->m_Regs[ID] != 0x83) /* C1: ID == 83, C2: ID == 84 */
+ state->m_Regs[EP3] |= 0x40; /* SM_LT = 1 */
+
+ if (!(SearchMap1(m_BP_Filter_Map, freq, &BP_Filter) &&
+ SearchMap1(m_GainTaper_Map, freq, &GainTaper) &&
+ SearchMap3(m_KM_Map, freq, &RFC_K, &RFC_M)))
+ return -EINVAL;
+
+ state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | BP_Filter;
+ state->m_Regs[EP2] = (RFBand << 5) | GainTaper;
+
+ state->m_Regs[EB13] = (state->m_Regs[EB13] & ~0x7C) | (RFC_K << 4) | (RFC_M << 2);
+
+ status = UpdateRegs(state, EP1, EP3);
+ if (status < 0)
+ break;
+ status = UpdateReg(state, EB13);
+ if (status < 0)
+ break;
+
+ state->m_Regs[EB4] |= 0x20; /* LO_ForceSrce = 1 */
+ status = UpdateReg(state, EB4);
+ if (status < 0)
+ break;
+
+ state->m_Regs[EB7] |= 0x20; /* CAL_ForceSrce = 1 */
+ status = UpdateReg(state, EB7);
+ if (status < 0)
+ break;
+
+ state->m_Regs[EB14] = 0; /* RFC_Cprog = 0 */
+ status = UpdateReg(state, EB14);
+ if (status < 0)
+ break;
+
+ state->m_Regs[EB20] &= ~0x20; /* ForceLock = 0; */
+ status = UpdateReg(state, EB20);
+ if (status < 0)
+ break;
+
+ state->m_Regs[EP4] |= 0x03; /* CAL_Mode = 3 */
+ status = UpdateRegs(state, EP4, EP5);
+ if (status < 0)
+ break;
+
+ status = CalcCalPLL(state, freq);
+ if (status < 0)
+ break;
+ status = CalcMainPLL(state, freq + 1000000);
+ if (status < 0)
+ break;
+
+ msleep(5);
+ status = UpdateReg(state, EP2);
+ if (status < 0)
+ break;
+ status = UpdateReg(state, EP1);
+ if (status < 0)
+ break;
+ status = UpdateReg(state, EP2);
+ if (status < 0)
+ break;
+ status = UpdateReg(state, EP1);
+ if (status < 0)
+ break;
+
+ state->m_Regs[EB4] &= ~0x20; /* LO_ForceSrce = 0 */
+ status = UpdateReg(state, EB4);
+ if (status < 0)
+ break;
+
+ state->m_Regs[EB7] &= ~0x20; /* CAL_ForceSrce = 0 */
+ status = UpdateReg(state, EB7);
+ if (status < 0)
+ break;
+ msleep(10);
+
+ state->m_Regs[EB20] |= 0x20; /* ForceLock = 1; */
+ status = UpdateReg(state, EB20);
+ if (status < 0)
+ break;
+ msleep(60);
+
+ state->m_Regs[EP4] &= ~0x03; /* CAL_Mode = 0 */
+ state->m_Regs[EP3] &= ~0x40; /* SM_LT = 0 */
+ state->m_Regs[EB18] &= ~0x03; /* AGC1_Gain = 0 */
+ status = UpdateReg(state, EB18);
+ if (status < 0)
+ break;
+ status = UpdateRegs(state, EP3, EP4);
+ if (status < 0)
+ break;
+ status = UpdateReg(state, EP1);
+ if (status < 0)
+ break;
+
+ status = ReadExtented(state, Regs);
+ if (status < 0)
+ break;
+
+ *pCprog = Regs[EB14];
+
+ } while (0);
+ return status;
+}
+
+static int RFTrackingFiltersInit(struct tda_state *state,
+ u8 RFBand)
+{
+ int status = 0;
+
+ u32 RF1 = m_RF_Band_Map[RFBand].m_RF1_Default;
+ u32 RF2 = m_RF_Band_Map[RFBand].m_RF2_Default;
+ u32 RF3 = m_RF_Band_Map[RFBand].m_RF3_Default;
+ bool bcal = false;
+
+ s32 Cprog_cal1 = 0;
+ s32 Cprog_table1 = 0;
+ s32 Cprog_cal2 = 0;
+ s32 Cprog_table2 = 0;
+ s32 Cprog_cal3 = 0;
+ s32 Cprog_table3 = 0;
+
+ state->m_RF_A1[RFBand] = 0;
+ state->m_RF_B1[RFBand] = 0;
+ state->m_RF_A2[RFBand] = 0;
+ state->m_RF_B2[RFBand] = 0;
+
+ do {
+ status = PowerScan(state, RFBand, RF1, &RF1, &bcal);
+ if (status < 0)
+ break;
+ if (bcal) {
+ status = CalibrateRF(state, RFBand, RF1, &Cprog_cal1);
+ if (status < 0)
+ break;
+ }
+ SearchMap2(m_RF_Cal_Map, RF1, &Cprog_table1);
+ if (!bcal)
+ Cprog_cal1 = Cprog_table1;
+ state->m_RF_B1[RFBand] = Cprog_cal1 - Cprog_table1;
+ /* state->m_RF_A1[RF_Band] = ???? */
+
+ if (RF2 == 0)
+ break;
+
+ status = PowerScan(state, RFBand, RF2, &RF2, &bcal);
+ if (status < 0)
+ break;
+ if (bcal) {
+ status = CalibrateRF(state, RFBand, RF2, &Cprog_cal2);
+ if (status < 0)
+ break;
+ }
+ SearchMap2(m_RF_Cal_Map, RF2, &Cprog_table2);
+ if (!bcal)
+ Cprog_cal2 = Cprog_table2;
+
+ state->m_RF_A1[RFBand] =
+ (Cprog_cal2 - Cprog_table2 - Cprog_cal1 + Cprog_table1) /
+ ((s32)(RF2) - (s32)(RF1));
+
+ if (RF3 == 0)
+ break;
+
+ status = PowerScan(state, RFBand, RF3, &RF3, &bcal);
+ if (status < 0)
+ break;
+ if (bcal) {
+ status = CalibrateRF(state, RFBand, RF3, &Cprog_cal3);
+ if (status < 0)
+ break;
+ }
+ SearchMap2(m_RF_Cal_Map, RF3, &Cprog_table3);
+ if (!bcal)
+ Cprog_cal3 = Cprog_table3;
+ state->m_RF_A2[RFBand] = (Cprog_cal3 - Cprog_table3 - Cprog_cal2 + Cprog_table2) / ((s32)(RF3) - (s32)(RF2));
+ state->m_RF_B2[RFBand] = Cprog_cal2 - Cprog_table2;
+
+ } while (0);
+
+ state->m_RF1[RFBand] = RF1;
+ state->m_RF2[RFBand] = RF2;
+ state->m_RF3[RFBand] = RF3;
+
+#if 0
+ printk(KERN_ERR "tda18271c2dd: %s %d RF1 = %d A1 = %d B1 = %d RF2 = %d A2 = %d B2 = %d RF3 = %d\n", __func__,
+ RFBand, RF1, state->m_RF_A1[RFBand], state->m_RF_B1[RFBand], RF2,
+ state->m_RF_A2[RFBand], state->m_RF_B2[RFBand], RF3);
+#endif
+
+ return status;
+}
+
+static int PowerScan(struct tda_state *state,
+ u8 RFBand, u32 RF_in, u32 *pRF_Out, bool *pbcal)
+{
+ int status = 0;
+ do {
+ u8 Gain_Taper = 0;
+ s32 RFC_Cprog = 0;
+ u8 CID_Target = 0;
+ u8 CountLimit = 0;
+ u32 freq_MainPLL;
+ u8 Regs[NUM_REGS];
+ u8 CID_Gain;
+ s32 Count = 0;
+ int sign = 1;
+ bool wait = false;
+
+ if (!(SearchMap2(m_RF_Cal_Map, RF_in, &RFC_Cprog) &&
+ SearchMap1(m_GainTaper_Map, RF_in, &Gain_Taper) &&
+ SearchMap3(m_CID_Target_Map, RF_in, &CID_Target, &CountLimit))) {
+
+ printk(KERN_ERR "tda18271c2dd: %s Search map failed\n", __func__);
+ return -EINVAL;
+ }
+
+ state->m_Regs[EP2] = (RFBand << 5) | Gain_Taper;
+ state->m_Regs[EB14] = (RFC_Cprog);
+ status = UpdateReg(state, EP2);
+ if (status < 0)
+ break;
+ status = UpdateReg(state, EB14);
+ if (status < 0)
+ break;
+
+ freq_MainPLL = RF_in + 1000000;
+ status = CalcMainPLL(state, freq_MainPLL);
+ if (status < 0)
+ break;
+ msleep(5);
+ state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x03) | 1; /* CAL_mode = 1 */
+ status = UpdateReg(state, EP4);
+ if (status < 0)
+ break;
+ status = UpdateReg(state, EP2); /* Launch power measurement */
+ if (status < 0)
+ break;
+ status = ReadExtented(state, Regs);
+ if (status < 0)
+ break;
+ CID_Gain = Regs[EB10] & 0x3F;
+ state->m_Regs[ID] = Regs[ID]; /* Chip version, (needed for C1 workarround in CalibrateRF) */
+
+ *pRF_Out = RF_in;
+
+ while (CID_Gain < CID_Target) {
+ freq_MainPLL = RF_in + sign * Count + 1000000;
+ status = CalcMainPLL(state, freq_MainPLL);
+ if (status < 0)
+ break;
+ msleep(wait ? 5 : 1);
+ wait = false;
+ status = UpdateReg(state, EP2); /* Launch power measurement */
+ if (status < 0)
+ break;
+ status = ReadExtented(state, Regs);
+ if (status < 0)
+ break;
+ CID_Gain = Regs[EB10] & 0x3F;
+ Count += 200000;
+
+ if (Count < CountLimit * 100000)
+ continue;
+ if (sign < 0)
+ break;
+
+ sign = -sign;
+ Count = 200000;
+ wait = true;
+ }
+ status = status;
+ if (status < 0)
+ break;
+ if (CID_Gain >= CID_Target) {
+ *pbcal = true;
+ *pRF_Out = freq_MainPLL - 1000000;
+ } else
+ *pbcal = false;
+ } while (0);
+
+ return status;
+}
+
+static int PowerScanInit(struct tda_state *state)
+{
+ int status = 0;
+ do {
+ state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | 0x12;
+ state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x1F); /* If level = 0, Cal mode = 0 */
+ status = UpdateRegs(state, EP3, EP4);
+ if (status < 0)
+ break;
+ state->m_Regs[EB18] = (state->m_Regs[EB18] & ~0x03); /* AGC 1 Gain = 0 */
+ status = UpdateReg(state, EB18);
+ if (status < 0)
+ break;
+ state->m_Regs[EB21] = (state->m_Regs[EB21] & ~0x03); /* AGC 2 Gain = 0 (Datasheet = 3) */
+ state->m_Regs[EB23] = (state->m_Regs[EB23] | 0x06); /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */
+ status = UpdateRegs(state, EB21, EB23);
+ if (status < 0)
+ break;
+ } while (0);
+ return status;
+}
+
+static int CalcRFFilterCurve(struct tda_state *state)
+{
+ int status = 0;
+ do {
+ msleep(200); /* Temperature stabilisation */
+ status = PowerScanInit(state);
+ if (status < 0)
+ break;
+ status = RFTrackingFiltersInit(state, 0);
+ if (status < 0)
+ break;
+ status = RFTrackingFiltersInit(state, 1);
+ if (status < 0)
+ break;
+ status = RFTrackingFiltersInit(state, 2);
+ if (status < 0)
+ break;
+ status = RFTrackingFiltersInit(state, 3);
+ if (status < 0)
+ break;
+ status = RFTrackingFiltersInit(state, 4);
+ if (status < 0)
+ break;
+ status = RFTrackingFiltersInit(state, 5);
+ if (status < 0)
+ break;
+ status = RFTrackingFiltersInit(state, 6);
+ if (status < 0)
+ break;
+ status = ThermometerRead(state, &state->m_TMValue_RFCal); /* also switches off Cal mode !!! */
+ if (status < 0)
+ break;
+ } while (0);
+
+ return status;
+}
+
+static int FixedContentsI2CUpdate(struct tda_state *state)
+{
+ static u8 InitRegs[] = {
+ 0x08, 0x80, 0xC6,
+ 0xDF, 0x16, 0x60, 0x80,
+ 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFC, 0x01, 0x84, 0x41,
+ 0x01, 0x84, 0x40, 0x07,
+ 0x00, 0x00, 0x96, 0x3F,
+ 0xC1, 0x00, 0x8F, 0x00,
+ 0x00, 0x8C, 0x00, 0x20,
+ 0xB3, 0x48, 0xB0,
+ };
+ int status = 0;
+ memcpy(&state->m_Regs[TM], InitRegs, EB23 - TM + 1);
+ do {
+ status = UpdateRegs(state, TM, EB23);
+ if (status < 0)
+ break;
+
+ /* AGC1 gain setup */
+ state->m_Regs[EB17] = 0x00;
+ status = UpdateReg(state, EB17);
+ if (status < 0)
+ break;
+ state->m_Regs[EB17] = 0x03;
+ status = UpdateReg(state, EB17);
+ if (status < 0)
+ break;
+ state->m_Regs[EB17] = 0x43;
+ status = UpdateReg(state, EB17);
+ if (status < 0)
+ break;
+ state->m_Regs[EB17] = 0x4C;
+ status = UpdateReg(state, EB17);
+ if (status < 0)
+ break;
+
+ /* IRC Cal Low band */
+ state->m_Regs[EP3] = 0x1F;
+ state->m_Regs[EP4] = 0x66;
+ state->m_Regs[EP5] = 0x81;
+ state->m_Regs[CPD] = 0xCC;
+ state->m_Regs[CD1] = 0x6C;
+ state->m_Regs[CD2] = 0x00;
+ state->m_Regs[CD3] = 0x00;
+ state->m_Regs[MPD] = 0xC5;
+ state->m_Regs[MD1] = 0x77;
+ state->m_Regs[MD2] = 0x08;
+ state->m_Regs[MD3] = 0x00;
+ status = UpdateRegs(state, EP2, MD3); /* diff between sw and datasheet (ep3-md3) */
+ if (status < 0)
+ break;
+
+#if 0
+ state->m_Regs[EB4] = 0x61; /* missing in sw */
+ status = UpdateReg(state, EB4);
+ if (status < 0)
+ break;
+ msleep(1);
+ state->m_Regs[EB4] = 0x41;
+ status = UpdateReg(state, EB4);
+ if (status < 0)
+ break;
+#endif
+
+ msleep(5);
+ status = UpdateReg(state, EP1);
+ if (status < 0)
+ break;
+ msleep(5);
+
+ state->m_Regs[EP5] = 0x85;
+ state->m_Regs[CPD] = 0xCB;
+ state->m_Regs[CD1] = 0x66;
+ state->m_Regs[CD2] = 0x70;
+ status = UpdateRegs(state, EP3, CD3);
+ if (status < 0)
+ break;
+ msleep(5);
+ status = UpdateReg(state, EP2);
+ if (status < 0)
+ break;
+ msleep(30);
+
+ /* IRC Cal mid band */
+ state->m_Regs[EP5] = 0x82;
+ state->m_Regs[CPD] = 0xA8;
+ state->m_Regs[CD2] = 0x00;
+ state->m_Regs[MPD] = 0xA1; /* Datasheet = 0xA9 */
+ state->m_Regs[MD1] = 0x73;
+ state->m_Regs[MD2] = 0x1A;
+ status = UpdateRegs(state, EP3, MD3);
+ if (status < 0)
+ break;
+
+ msleep(5);
+ status = UpdateReg(state, EP1);
+ if (status < 0)
+ break;
+ msleep(5);
+
+ state->m_Regs[EP5] = 0x86;
+ state->m_Regs[CPD] = 0xA8;
+ state->m_Regs[CD1] = 0x66;
+ state->m_Regs[CD2] = 0xA0;
+ status = UpdateRegs(state, EP3, CD3);
+ if (status < 0)
+ break;
+ msleep(5);
+ status = UpdateReg(state, EP2);
+ if (status < 0)
+ break;
+ msleep(30);
+
+ /* IRC Cal high band */
+ state->m_Regs[EP5] = 0x83;
+ state->m_Regs[CPD] = 0x98;
+ state->m_Regs[CD1] = 0x65;
+ state->m_Regs[CD2] = 0x00;
+ state->m_Regs[MPD] = 0x91; /* Datasheet = 0x91 */
+ state->m_Regs[MD1] = 0x71;
+ state->m_Regs[MD2] = 0xCD;
+ status = UpdateRegs(state, EP3, MD3);
+ if (status < 0)
+ break;
+ msleep(5);
+ status = UpdateReg(state, EP1);
+ if (status < 0)
+ break;
+ msleep(5);
+ state->m_Regs[EP5] = 0x87;
+ state->m_Regs[CD1] = 0x65;
+ state->m_Regs[CD2] = 0x50;
+ status = UpdateRegs(state, EP3, CD3);
+ if (status < 0)
+ break;
+ msleep(5);
+ status = UpdateReg(state, EP2);
+ if (status < 0)
+ break;
+ msleep(30);
+
+ /* Back to normal */
+ state->m_Regs[EP4] = 0x64;
+ status = UpdateReg(state, EP4);
+ if (status < 0)
+ break;
+ status = UpdateReg(state, EP1);
+ if (status < 0)
+ break;
+
+ } while (0);
+ return status;
+}
+
+static int InitCal(struct tda_state *state)
+{
+ int status = 0;
+
+ do {
+ status = FixedContentsI2CUpdate(state);
+ if (status < 0)
+ break;
+ status = CalcRFFilterCurve(state);
+ if (status < 0)
+ break;
+ status = StandBy(state);
+ if (status < 0)
+ break;
+ /* m_bInitDone = true; */
+ } while (0);
+ return status;
+};
+
+static int RFTrackingFiltersCorrection(struct tda_state *state,
+ u32 Frequency)
+{
+ int status = 0;
+ s32 Cprog_table;
+ u8 RFBand;
+ u8 dCoverdT;
+
+ if (!SearchMap2(m_RF_Cal_Map, Frequency, &Cprog_table) ||
+ !SearchMap4(m_RF_Band_Map, Frequency, &RFBand) ||
+ !SearchMap1(m_RF_Cal_DC_Over_DT_Map, Frequency, &dCoverdT))
+
+ return -EINVAL;
+
+ do {
+ u8 TMValue_Current;
+ u32 RF1 = state->m_RF1[RFBand];
+ u32 RF2 = state->m_RF1[RFBand];
+ u32 RF3 = state->m_RF1[RFBand];
+ s32 RF_A1 = state->m_RF_A1[RFBand];
+ s32 RF_B1 = state->m_RF_B1[RFBand];
+ s32 RF_A2 = state->m_RF_A2[RFBand];
+ s32 RF_B2 = state->m_RF_B2[RFBand];
+ s32 Capprox = 0;
+ int TComp;
+
+ state->m_Regs[EP3] &= ~0xE0; /* Power up */
+ status = UpdateReg(state, EP3);
+ if (status < 0)
+ break;
+
+ status = ThermometerRead(state, &TMValue_Current);
+ if (status < 0)
+ break;
+
+ if (RF3 == 0 || Frequency < RF2)
+ Capprox = RF_A1 * ((s32)(Frequency) - (s32)(RF1)) + RF_B1 + Cprog_table;
+ else
+ Capprox = RF_A2 * ((s32)(Frequency) - (s32)(RF2)) + RF_B2 + Cprog_table;
+
+ TComp = (int)(dCoverdT) * ((int)(TMValue_Current) - (int)(state->m_TMValue_RFCal))/1000;
+
+ Capprox += TComp;
+
+ if (Capprox < 0)
+ Capprox = 0;
+ else if (Capprox > 255)
+ Capprox = 255;
+
+
+ /* TODO Temperature compensation. There is defenitely a scale factor */
+ /* missing in the datasheet, so leave it out for now. */
+ state->m_Regs[EB14] = Capprox;
+
+ status = UpdateReg(state, EB14);
+ if (status < 0)
+ break;
+
+ } while (0);
+ return status;
+}
+
+static int ChannelConfiguration(struct tda_state *state,
+ u32 Frequency, int Standard)
+{
+
+ s32 IntermediateFrequency = m_StandardTable[Standard].m_IFFrequency;
+ int status = 0;
+
+ u8 BP_Filter = 0;
+ u8 RF_Band = 0;
+ u8 GainTaper = 0;
+ u8 IR_Meas = 0;
+
+ state->IF = IntermediateFrequency;
+ /* printk("tda18271c2dd: %s Freq = %d Standard = %d IF = %d\n", __func__, Frequency, Standard, IntermediateFrequency); */
+ /* get values from tables */
+
+ if (!(SearchMap1(m_BP_Filter_Map, Frequency, &BP_Filter) &&
+ SearchMap1(m_GainTaper_Map, Frequency, &GainTaper) &&
+ SearchMap1(m_IR_Meas_Map, Frequency, &IR_Meas) &&
+ SearchMap4(m_RF_Band_Map, Frequency, &RF_Band))) {
+
+ printk(KERN_ERR "tda18271c2dd: %s SearchMap failed\n", __func__);
+ return -EINVAL;
+ }
+
+ do {
+ state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | m_StandardTable[Standard].m_EP3_4_0;
+ state->m_Regs[EP3] &= ~0x04; /* switch RFAGC to high speed mode */
+
+ /* m_EP4 default for XToutOn, CAL_Mode (0) */
+ state->m_Regs[EP4] = state->m_EP4 | ((Standard > HF_AnalogMax) ? state->m_IFLevelDigital : state->m_IFLevelAnalog);
+ /* state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; */
+ if (Standard <= HF_AnalogMax)
+ state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelAnalog;
+ else if (Standard <= HF_ATSC)
+ state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBT;
+ else if (Standard <= HF_DVBC)
+ state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBC;
+ else
+ state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital;
+
+ if ((Standard == HF_FM_Radio) && state->m_bFMInput)
+ state->m_Regs[EP4] |= 80;
+
+ state->m_Regs[MPD] &= ~0x80;
+ if (Standard > HF_AnalogMax)
+ state->m_Regs[MPD] |= 0x80; /* Add IF_notch for digital */
+
+ state->m_Regs[EB22] = m_StandardTable[Standard].m_EB22;
+
+ /* Note: This is missing from flowchart in TDA18271 specification ( 1.5 MHz cutoff for FM ) */
+ if (Standard == HF_FM_Radio)
+ state->m_Regs[EB23] |= 0x06; /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */
+ else
+ state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LPFc[2] = 0 */
+
+ status = UpdateRegs(state, EB22, EB23);
+ if (status < 0)
+ break;
+
+ state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | 0x40 | BP_Filter; /* Dis_Power_level = 1, Filter */
+ state->m_Regs[EP5] = (state->m_Regs[EP5] & ~0x07) | IR_Meas;
+ state->m_Regs[EP2] = (RF_Band << 5) | GainTaper;
+
+ state->m_Regs[EB1] = (state->m_Regs[EB1] & ~0x07) |
+ (state->m_bMaster ? 0x04 : 0x00); /* CALVCO_FortLOn = MS */
+ /* AGC1_always_master = 0 */
+ /* AGC_firstn = 0 */
+ status = UpdateReg(state, EB1);
+ if (status < 0)
+ break;
+
+ if (state->m_bMaster) {
+ status = CalcMainPLL(state, Frequency + IntermediateFrequency);
+ if (status < 0)
+ break;
+ status = UpdateRegs(state, TM, EP5);
+ if (status < 0)
+ break;
+ state->m_Regs[EB4] |= 0x20; /* LO_forceSrce = 1 */
+ status = UpdateReg(state, EB4);
+ if (status < 0)
+ break;
+ msleep(1);
+ state->m_Regs[EB4] &= ~0x20; /* LO_forceSrce = 0 */
+ status = UpdateReg(state, EB4);
+ if (status < 0)
+ break;
+ } else {
+ u8 PostDiv = 0;
+ u8 Div;
+ status = CalcCalPLL(state, Frequency + IntermediateFrequency);
+ if (status < 0)
+ break;
+
+ SearchMap3(m_Cal_PLL_Map, Frequency + IntermediateFrequency, &PostDiv, &Div);
+ state->m_Regs[MPD] = (state->m_Regs[MPD] & ~0x7F) | (PostDiv & 0x77);
+ status = UpdateReg(state, MPD);
+ if (status < 0)
+ break;
+ status = UpdateRegs(state, TM, EP5);
+ if (status < 0)
+ break;
+
+ state->m_Regs[EB7] |= 0x20; /* CAL_forceSrce = 1 */
+ status = UpdateReg(state, EB7);
+ if (status < 0)
+ break;
+ msleep(1);
+ state->m_Regs[EB7] &= ~0x20; /* CAL_forceSrce = 0 */
+ status = UpdateReg(state, EB7);
+ if (status < 0)
+ break;
+ }
+ msleep(20);
+ if (Standard != HF_FM_Radio)
+ state->m_Regs[EP3] |= 0x04; /* RFAGC to normal mode */
+ status = UpdateReg(state, EP3);
+ if (status < 0)
+ break;
+
+ } while (0);
+ return status;
+}
+
+static int sleep(struct dvb_frontend *fe)
+{
+ struct tda_state *state = fe->tuner_priv;
+
+ StandBy(state);
+ return 0;
+}
+
+static int init(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+static int release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+/*
+ * As defined on EN 300 429 Annex A and on ITU-T J.83 annex A, the DVB-C
+ * roll-off factor is 0.15.
+ * According with the specs, the amount of the needed bandwith is given by:
+ * Bw = Symbol_rate * (1 + 0.15)
+ * As such, the maximum symbol rate supported by 6 MHz is
+ * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
+ *NOTE: For ITU-T J.83 Annex C, the roll-off factor is 0.13. So:
+ * max_symbol_rate = 6 MHz / 1.13 = 5309735 Baud
+ * That means that an adjustment is needed for Japan,
+ * but, as currently DRX-K is hardcoded to Annex A, let's stick
+ * with 0.15 roll-off factor.
+ */
+#define MAX_SYMBOL_RATE_6MHz 5217391
+
+static int set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct tda_state *state = fe->tuner_priv;
+ int status = 0;
+ int Standard;
+
+ state->m_Frequency = params->frequency;
+
+ if (fe->ops.info.type == FE_OFDM)
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ Standard = HF_DVBT_6MHZ;
+ break;
+ case BANDWIDTH_7_MHZ:
+ Standard = HF_DVBT_7MHZ;
+ break;
+ default:
+ case BANDWIDTH_8_MHZ:
+ Standard = HF_DVBT_8MHZ;
+ break;
+ }
+ else if (fe->ops.info.type == FE_QAM) {
+ if (params->u.qam.symbol_rate <= MAX_SYMBOL_RATE_6MHz)
+ Standard = HF_DVBC_6MHZ;
+ else
+ Standard = HF_DVBC_8MHZ;
+ } else
+ return -EINVAL;
+ do {
+ status = RFTrackingFiltersCorrection(state, params->frequency);
+ if (status < 0)
+ break;
+ status = ChannelConfiguration(state, params->frequency, Standard);
+ if (status < 0)
+ break;
+
+ msleep(state->m_SettlingTime); /* Allow AGC's to settle down */
+ } while (0);
+ return status;
+}
+
+#if 0
+static int GetSignalStrength(s32 *pSignalStrength, u32 RFAgc, u32 IFAgc)
+{
+ if (IFAgc < 500) {
+ /* Scale this from 0 to 50000 */
+ *pSignalStrength = IFAgc * 100;
+ } else {
+ /* Scale range 500-1500 to 50000-80000 */
+ *pSignalStrength = 50000 + (IFAgc - 500) * 30;
+ }
+
+ return 0;
+}
+#endif
+
+static int get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tda_state *state = fe->tuner_priv;
+
+ *frequency = state->IF;
+ return 0;
+}
+
+static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ /* struct tda_state *state = fe->tuner_priv; */
+ /* *bandwidth = priv->bandwidth; */
+ return 0;
+}
+
+
+static struct dvb_tuner_ops tuner_ops = {
+ .info = {
+ .name = "NXP TDA18271C2D",
+ .frequency_min = 47125000,
+ .frequency_max = 865000000,
+ .frequency_step = 62500
+ },
+ .init = init,
+ .sleep = sleep,
+ .set_params = set_params,
+ .release = release,
+ .get_if_frequency = get_if_frequency,
+ .get_bandwidth = get_bandwidth,
+};
+
+struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, u8 adr)
+{
+ struct tda_state *state;
+
+ state = kzalloc(sizeof(struct tda_state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ fe->tuner_priv = state;
+ state->adr = adr;
+ state->i2c = i2c;
+ memcpy(&fe->ops.tuner_ops, &tuner_ops, sizeof(struct dvb_tuner_ops));
+ reset(state);
+ InitCal(state);
+
+ return fe;
+}
+EXPORT_SYMBOL_GPL(tda18271c2dd_attach);
+
+MODULE_DESCRIPTION("TDA18271C2 driver");
+MODULE_AUTHOR("DD");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda18271c2dd.h b/drivers/media/dvb/frontends/tda18271c2dd.h
new file mode 100644
index 000000000000..1389c74e12ce
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271c2dd.h
@@ -0,0 +1,16 @@
+#ifndef _TDA18271C2DD_H_
+#define _TDA18271C2DD_H_
+#if defined(CONFIG_DVB_TDA18271C2DD) || (defined(CONFIG_DVB_TDA18271C2DD_MODULE) \
+ && defined(MODULE))
+struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, u8 adr);
+#else
+static inline struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, u8 adr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/tda18271c2dd_maps.h b/drivers/media/dvb/frontends/tda18271c2dd_maps.h
new file mode 100644
index 000000000000..b87661b9df14
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271c2dd_maps.h
@@ -0,0 +1,814 @@
+enum HF_S {
+ HF_None = 0, HF_B, HF_DK, HF_G, HF_I, HF_L, HF_L1, HF_MN, HF_FM_Radio,
+ HF_AnalogMax, HF_DVBT_6MHZ, HF_DVBT_7MHZ, HF_DVBT_8MHZ,
+ HF_DVBT, HF_ATSC, HF_DVBC_6MHZ, HF_DVBC_7MHZ,
+ HF_DVBC_8MHZ, HF_DVBC
+};
+
+struct SStandardParam m_StandardTable[] = {
+ { 0, 0, 0x00, 0x00 }, /* HF_None */
+ { 6000000, 7000000, 0x1D, 0x2C }, /* HF_B, */
+ { 6900000, 8000000, 0x1E, 0x2C }, /* HF_DK, */
+ { 7100000, 8000000, 0x1E, 0x2C }, /* HF_G, */
+ { 7250000, 8000000, 0x1E, 0x2C }, /* HF_I, */
+ { 6900000, 8000000, 0x1E, 0x2C }, /* HF_L, */
+ { 1250000, 8000000, 0x1E, 0x2C }, /* HF_L1, */
+ { 5400000, 6000000, 0x1C, 0x2C }, /* HF_MN, */
+ { 1250000, 500000, 0x18, 0x2C }, /* HF_FM_Radio, */
+ { 0, 0, 0x00, 0x00 }, /* HF_AnalogMax (Unused) */
+ { 3300000, 6000000, 0x1C, 0x58 }, /* HF_DVBT_6MHZ */
+ { 3500000, 7000000, 0x1C, 0x37 }, /* HF_DVBT_7MHZ */
+ { 4000000, 8000000, 0x1D, 0x37 }, /* HF_DVBT_8MHZ */
+ { 0, 0, 0x00, 0x00 }, /* HF_DVBT (Unused) */
+ { 5000000, 6000000, 0x1C, 0x37 }, /* HF_ATSC (center = 3.25 MHz) */
+ { 4000000, 6000000, 0x1D, 0x58 }, /* HF_DVBC_6MHZ (Chicago) */
+ { 4500000, 7000000, 0x1E, 0x37 }, /* HF_DVBC_7MHZ (not documented by NXP) */
+ { 5000000, 8000000, 0x1F, 0x37 }, /* HF_DVBC_8MHZ */
+ { 0, 0, 0x00, 0x00 }, /* HF_DVBC (Unused) */
+};
+
+struct SMap m_BP_Filter_Map[] = {
+ { 62000000, 0x00 },
+ { 84000000, 0x01 },
+ { 100000000, 0x02 },
+ { 140000000, 0x03 },
+ { 170000000, 0x04 },
+ { 180000000, 0x05 },
+ { 865000000, 0x06 },
+ { 0, 0x00 }, /* Table End */
+};
+
+static struct SMapI m_RF_Cal_Map[] = {
+ { 41000000, 0x0F },
+ { 43000000, 0x1C },
+ { 45000000, 0x2F },
+ { 46000000, 0x39 },
+ { 47000000, 0x40 },
+ { 47900000, 0x50 },
+ { 49100000, 0x16 },
+ { 50000000, 0x18 },
+ { 51000000, 0x20 },
+ { 53000000, 0x28 },
+ { 55000000, 0x2B },
+ { 56000000, 0x32 },
+ { 57000000, 0x35 },
+ { 58000000, 0x3E },
+ { 59000000, 0x43 },
+ { 60000000, 0x4E },
+ { 61100000, 0x55 },
+ { 63000000, 0x0F },
+ { 64000000, 0x11 },
+ { 65000000, 0x12 },
+ { 66000000, 0x15 },
+ { 67000000, 0x16 },
+ { 68000000, 0x17 },
+ { 70000000, 0x19 },
+ { 71000000, 0x1C },
+ { 72000000, 0x1D },
+ { 73000000, 0x1F },
+ { 74000000, 0x20 },
+ { 75000000, 0x21 },
+ { 76000000, 0x24 },
+ { 77000000, 0x25 },
+ { 78000000, 0x27 },
+ { 80000000, 0x28 },
+ { 81000000, 0x29 },
+ { 82000000, 0x2D },
+ { 83000000, 0x2E },
+ { 84000000, 0x2F },
+ { 85000000, 0x31 },
+ { 86000000, 0x33 },
+ { 87000000, 0x34 },
+ { 88000000, 0x35 },
+ { 89000000, 0x37 },
+ { 90000000, 0x38 },
+ { 91000000, 0x39 },
+ { 93000000, 0x3C },
+ { 94000000, 0x3E },
+ { 95000000, 0x3F },
+ { 96000000, 0x40 },
+ { 97000000, 0x42 },
+ { 99000000, 0x45 },
+ { 100000000, 0x46 },
+ { 102000000, 0x48 },
+ { 103000000, 0x4A },
+ { 105000000, 0x4D },
+ { 106000000, 0x4E },
+ { 107000000, 0x50 },
+ { 108000000, 0x51 },
+ { 110000000, 0x54 },
+ { 111000000, 0x56 },
+ { 112000000, 0x57 },
+ { 113000000, 0x58 },
+ { 114000000, 0x59 },
+ { 115000000, 0x5C },
+ { 116000000, 0x5D },
+ { 117000000, 0x5F },
+ { 119000000, 0x60 },
+ { 120000000, 0x64 },
+ { 121000000, 0x65 },
+ { 122000000, 0x66 },
+ { 123000000, 0x68 },
+ { 124000000, 0x69 },
+ { 125000000, 0x6C },
+ { 126000000, 0x6D },
+ { 127000000, 0x6E },
+ { 128000000, 0x70 },
+ { 129000000, 0x71 },
+ { 130000000, 0x75 },
+ { 131000000, 0x77 },
+ { 132000000, 0x78 },
+ { 133000000, 0x7B },
+ { 134000000, 0x7E },
+ { 135000000, 0x81 },
+ { 136000000, 0x82 },
+ { 137000000, 0x87 },
+ { 138000000, 0x88 },
+ { 139000000, 0x8D },
+ { 140000000, 0x8E },
+ { 141000000, 0x91 },
+ { 142000000, 0x95 },
+ { 143000000, 0x9A },
+ { 144000000, 0x9D },
+ { 145000000, 0xA1 },
+ { 146000000, 0xA2 },
+ { 147000000, 0xA4 },
+ { 148000000, 0xA9 },
+ { 149000000, 0xAE },
+ { 150000000, 0xB0 },
+ { 151000000, 0xB1 },
+ { 152000000, 0xB7 },
+ { 152600000, 0xBD },
+ { 154000000, 0x20 },
+ { 155000000, 0x22 },
+ { 156000000, 0x24 },
+ { 157000000, 0x25 },
+ { 158000000, 0x27 },
+ { 159000000, 0x29 },
+ { 160000000, 0x2C },
+ { 161000000, 0x2D },
+ { 163000000, 0x2E },
+ { 164000000, 0x2F },
+ { 164700000, 0x30 },
+ { 166000000, 0x11 },
+ { 167000000, 0x12 },
+ { 168000000, 0x13 },
+ { 169000000, 0x14 },
+ { 170000000, 0x15 },
+ { 172000000, 0x16 },
+ { 173000000, 0x17 },
+ { 174000000, 0x18 },
+ { 175000000, 0x1A },
+ { 176000000, 0x1B },
+ { 178000000, 0x1D },
+ { 179000000, 0x1E },
+ { 180000000, 0x1F },
+ { 181000000, 0x20 },
+ { 182000000, 0x21 },
+ { 183000000, 0x22 },
+ { 184000000, 0x24 },
+ { 185000000, 0x25 },
+ { 186000000, 0x26 },
+ { 187000000, 0x27 },
+ { 188000000, 0x29 },
+ { 189000000, 0x2A },
+ { 190000000, 0x2C },
+ { 191000000, 0x2D },
+ { 192000000, 0x2E },
+ { 193000000, 0x2F },
+ { 194000000, 0x30 },
+ { 195000000, 0x33 },
+ { 196000000, 0x35 },
+ { 198000000, 0x36 },
+ { 200000000, 0x38 },
+ { 201000000, 0x3C },
+ { 202000000, 0x3D },
+ { 203500000, 0x3E },
+ { 206000000, 0x0E },
+ { 208000000, 0x0F },
+ { 212000000, 0x10 },
+ { 216000000, 0x11 },
+ { 217000000, 0x12 },
+ { 218000000, 0x13 },
+ { 220000000, 0x14 },
+ { 222000000, 0x15 },
+ { 225000000, 0x16 },
+ { 228000000, 0x17 },
+ { 231000000, 0x18 },
+ { 234000000, 0x19 },
+ { 235000000, 0x1A },
+ { 236000000, 0x1B },
+ { 237000000, 0x1C },
+ { 240000000, 0x1D },
+ { 242000000, 0x1E },
+ { 244000000, 0x1F },
+ { 247000000, 0x20 },
+ { 249000000, 0x21 },
+ { 252000000, 0x22 },
+ { 253000000, 0x23 },
+ { 254000000, 0x24 },
+ { 256000000, 0x25 },
+ { 259000000, 0x26 },
+ { 262000000, 0x27 },
+ { 264000000, 0x28 },
+ { 267000000, 0x29 },
+ { 269000000, 0x2A },
+ { 271000000, 0x2B },
+ { 273000000, 0x2C },
+ { 275000000, 0x2D },
+ { 277000000, 0x2E },
+ { 279000000, 0x2F },
+ { 282000000, 0x30 },
+ { 284000000, 0x31 },
+ { 286000000, 0x32 },
+ { 287000000, 0x33 },
+ { 290000000, 0x34 },
+ { 293000000, 0x35 },
+ { 295000000, 0x36 },
+ { 297000000, 0x37 },
+ { 300000000, 0x38 },
+ { 303000000, 0x39 },
+ { 305000000, 0x3A },
+ { 306000000, 0x3B },
+ { 307000000, 0x3C },
+ { 310000000, 0x3D },
+ { 312000000, 0x3E },
+ { 315000000, 0x3F },
+ { 318000000, 0x40 },
+ { 320000000, 0x41 },
+ { 323000000, 0x42 },
+ { 324000000, 0x43 },
+ { 325000000, 0x44 },
+ { 327000000, 0x45 },
+ { 331000000, 0x46 },
+ { 334000000, 0x47 },
+ { 337000000, 0x48 },
+ { 339000000, 0x49 },
+ { 340000000, 0x4A },
+ { 341000000, 0x4B },
+ { 343000000, 0x4C },
+ { 345000000, 0x4D },
+ { 349000000, 0x4E },
+ { 352000000, 0x4F },
+ { 353000000, 0x50 },
+ { 355000000, 0x51 },
+ { 357000000, 0x52 },
+ { 359000000, 0x53 },
+ { 361000000, 0x54 },
+ { 362000000, 0x55 },
+ { 364000000, 0x56 },
+ { 368000000, 0x57 },
+ { 370000000, 0x58 },
+ { 372000000, 0x59 },
+ { 375000000, 0x5A },
+ { 376000000, 0x5B },
+ { 377000000, 0x5C },
+ { 379000000, 0x5D },
+ { 382000000, 0x5E },
+ { 384000000, 0x5F },
+ { 385000000, 0x60 },
+ { 386000000, 0x61 },
+ { 388000000, 0x62 },
+ { 390000000, 0x63 },
+ { 393000000, 0x64 },
+ { 394000000, 0x65 },
+ { 396000000, 0x66 },
+ { 397000000, 0x67 },
+ { 398000000, 0x68 },
+ { 400000000, 0x69 },
+ { 402000000, 0x6A },
+ { 403000000, 0x6B },
+ { 407000000, 0x6C },
+ { 408000000, 0x6D },
+ { 409000000, 0x6E },
+ { 410000000, 0x6F },
+ { 411000000, 0x70 },
+ { 412000000, 0x71 },
+ { 413000000, 0x72 },
+ { 414000000, 0x73 },
+ { 417000000, 0x74 },
+ { 418000000, 0x75 },
+ { 420000000, 0x76 },
+ { 422000000, 0x77 },
+ { 423000000, 0x78 },
+ { 424000000, 0x79 },
+ { 427000000, 0x7A },
+ { 428000000, 0x7B },
+ { 429000000, 0x7D },
+ { 432000000, 0x7F },
+ { 434000000, 0x80 },
+ { 435000000, 0x81 },
+ { 436000000, 0x83 },
+ { 437000000, 0x84 },
+ { 438000000, 0x85 },
+ { 439000000, 0x86 },
+ { 440000000, 0x87 },
+ { 441000000, 0x88 },
+ { 442000000, 0x89 },
+ { 445000000, 0x8A },
+ { 446000000, 0x8B },
+ { 447000000, 0x8C },
+ { 448000000, 0x8E },
+ { 449000000, 0x8F },
+ { 450000000, 0x90 },
+ { 452000000, 0x91 },
+ { 453000000, 0x93 },
+ { 454000000, 0x94 },
+ { 456000000, 0x96 },
+ { 457800000, 0x98 },
+ { 461000000, 0x11 },
+ { 468000000, 0x12 },
+ { 472000000, 0x13 },
+ { 473000000, 0x14 },
+ { 474000000, 0x15 },
+ { 481000000, 0x16 },
+ { 486000000, 0x17 },
+ { 491000000, 0x18 },
+ { 498000000, 0x19 },
+ { 499000000, 0x1A },
+ { 501000000, 0x1B },
+ { 506000000, 0x1C },
+ { 511000000, 0x1D },
+ { 516000000, 0x1E },
+ { 520000000, 0x1F },
+ { 521000000, 0x20 },
+ { 525000000, 0x21 },
+ { 529000000, 0x22 },
+ { 533000000, 0x23 },
+ { 539000000, 0x24 },
+ { 541000000, 0x25 },
+ { 547000000, 0x26 },
+ { 549000000, 0x27 },
+ { 551000000, 0x28 },
+ { 556000000, 0x29 },
+ { 561000000, 0x2A },
+ { 563000000, 0x2B },
+ { 565000000, 0x2C },
+ { 569000000, 0x2D },
+ { 571000000, 0x2E },
+ { 577000000, 0x2F },
+ { 580000000, 0x30 },
+ { 582000000, 0x31 },
+ { 584000000, 0x32 },
+ { 588000000, 0x33 },
+ { 591000000, 0x34 },
+ { 596000000, 0x35 },
+ { 598000000, 0x36 },
+ { 603000000, 0x37 },
+ { 604000000, 0x38 },
+ { 606000000, 0x39 },
+ { 612000000, 0x3A },
+ { 615000000, 0x3B },
+ { 617000000, 0x3C },
+ { 621000000, 0x3D },
+ { 622000000, 0x3E },
+ { 625000000, 0x3F },
+ { 632000000, 0x40 },
+ { 633000000, 0x41 },
+ { 634000000, 0x42 },
+ { 642000000, 0x43 },
+ { 643000000, 0x44 },
+ { 647000000, 0x45 },
+ { 650000000, 0x46 },
+ { 652000000, 0x47 },
+ { 657000000, 0x48 },
+ { 661000000, 0x49 },
+ { 662000000, 0x4A },
+ { 665000000, 0x4B },
+ { 667000000, 0x4C },
+ { 670000000, 0x4D },
+ { 673000000, 0x4E },
+ { 676000000, 0x4F },
+ { 677000000, 0x50 },
+ { 681000000, 0x51 },
+ { 683000000, 0x52 },
+ { 686000000, 0x53 },
+ { 688000000, 0x54 },
+ { 689000000, 0x55 },
+ { 691000000, 0x56 },
+ { 695000000, 0x57 },
+ { 698000000, 0x58 },
+ { 703000000, 0x59 },
+ { 704000000, 0x5A },
+ { 705000000, 0x5B },
+ { 707000000, 0x5C },
+ { 710000000, 0x5D },
+ { 712000000, 0x5E },
+ { 717000000, 0x5F },
+ { 718000000, 0x60 },
+ { 721000000, 0x61 },
+ { 722000000, 0x62 },
+ { 723000000, 0x63 },
+ { 725000000, 0x64 },
+ { 727000000, 0x65 },
+ { 730000000, 0x66 },
+ { 732000000, 0x67 },
+ { 735000000, 0x68 },
+ { 740000000, 0x69 },
+ { 741000000, 0x6A },
+ { 742000000, 0x6B },
+ { 743000000, 0x6C },
+ { 745000000, 0x6D },
+ { 747000000, 0x6E },
+ { 748000000, 0x6F },
+ { 750000000, 0x70 },
+ { 752000000, 0x71 },
+ { 754000000, 0x72 },
+ { 757000000, 0x73 },
+ { 758000000, 0x74 },
+ { 760000000, 0x75 },
+ { 763000000, 0x76 },
+ { 764000000, 0x77 },
+ { 766000000, 0x78 },
+ { 767000000, 0x79 },
+ { 768000000, 0x7A },
+ { 773000000, 0x7B },
+ { 774000000, 0x7C },
+ { 776000000, 0x7D },
+ { 777000000, 0x7E },
+ { 778000000, 0x7F },
+ { 779000000, 0x80 },
+ { 781000000, 0x81 },
+ { 783000000, 0x82 },
+ { 784000000, 0x83 },
+ { 785000000, 0x84 },
+ { 786000000, 0x85 },
+ { 793000000, 0x86 },
+ { 794000000, 0x87 },
+ { 795000000, 0x88 },
+ { 797000000, 0x89 },
+ { 799000000, 0x8A },
+ { 801000000, 0x8B },
+ { 802000000, 0x8C },
+ { 803000000, 0x8D },
+ { 804000000, 0x8E },
+ { 810000000, 0x90 },
+ { 811000000, 0x91 },
+ { 812000000, 0x92 },
+ { 814000000, 0x93 },
+ { 816000000, 0x94 },
+ { 817000000, 0x96 },
+ { 818000000, 0x97 },
+ { 820000000, 0x98 },
+ { 821000000, 0x99 },
+ { 822000000, 0x9A },
+ { 828000000, 0x9B },
+ { 829000000, 0x9D },
+ { 830000000, 0x9F },
+ { 831000000, 0xA0 },
+ { 833000000, 0xA1 },
+ { 835000000, 0xA2 },
+ { 836000000, 0xA3 },
+ { 837000000, 0xA4 },
+ { 838000000, 0xA6 },
+ { 840000000, 0xA8 },
+ { 842000000, 0xA9 },
+ { 845000000, 0xAA },
+ { 846000000, 0xAB },
+ { 847000000, 0xAD },
+ { 848000000, 0xAE },
+ { 852000000, 0xAF },
+ { 853000000, 0xB0 },
+ { 858000000, 0xB1 },
+ { 860000000, 0xB2 },
+ { 861000000, 0xB3 },
+ { 862000000, 0xB4 },
+ { 863000000, 0xB6 },
+ { 864000000, 0xB8 },
+ { 865000000, 0xB9 },
+ { 0, 0x00 }, /* Table End */
+};
+
+
+static struct SMap2 m_KM_Map[] = {
+ { 47900000, 3, 2 },
+ { 61100000, 3, 1 },
+ { 350000000, 3, 0 },
+ { 720000000, 2, 1 },
+ { 865000000, 3, 3 },
+ { 0, 0x00 }, /* Table End */
+};
+
+static struct SMap2 m_Main_PLL_Map[] = {
+ { 33125000, 0x57, 0xF0 },
+ { 35500000, 0x56, 0xE0 },
+ { 38188000, 0x55, 0xD0 },
+ { 41375000, 0x54, 0xC0 },
+ { 45125000, 0x53, 0xB0 },
+ { 49688000, 0x52, 0xA0 },
+ { 55188000, 0x51, 0x90 },
+ { 62125000, 0x50, 0x80 },
+ { 66250000, 0x47, 0x78 },
+ { 71000000, 0x46, 0x70 },
+ { 76375000, 0x45, 0x68 },
+ { 82750000, 0x44, 0x60 },
+ { 90250000, 0x43, 0x58 },
+ { 99375000, 0x42, 0x50 },
+ { 110375000, 0x41, 0x48 },
+ { 124250000, 0x40, 0x40 },
+ { 132500000, 0x37, 0x3C },
+ { 142000000, 0x36, 0x38 },
+ { 152750000, 0x35, 0x34 },
+ { 165500000, 0x34, 0x30 },
+ { 180500000, 0x33, 0x2C },
+ { 198750000, 0x32, 0x28 },
+ { 220750000, 0x31, 0x24 },
+ { 248500000, 0x30, 0x20 },
+ { 265000000, 0x27, 0x1E },
+ { 284000000, 0x26, 0x1C },
+ { 305500000, 0x25, 0x1A },
+ { 331000000, 0x24, 0x18 },
+ { 361000000, 0x23, 0x16 },
+ { 397500000, 0x22, 0x14 },
+ { 441500000, 0x21, 0x12 },
+ { 497000000, 0x20, 0x10 },
+ { 530000000, 0x17, 0x0F },
+ { 568000000, 0x16, 0x0E },
+ { 611000000, 0x15, 0x0D },
+ { 662000000, 0x14, 0x0C },
+ { 722000000, 0x13, 0x0B },
+ { 795000000, 0x12, 0x0A },
+ { 883000000, 0x11, 0x09 },
+ { 994000000, 0x10, 0x08 },
+ { 0, 0x00, 0x00 }, /* Table End */
+};
+
+static struct SMap2 m_Cal_PLL_Map[] = {
+ { 33813000, 0xDD, 0xD0 },
+ { 36625000, 0xDC, 0xC0 },
+ { 39938000, 0xDB, 0xB0 },
+ { 43938000, 0xDA, 0xA0 },
+ { 48813000, 0xD9, 0x90 },
+ { 54938000, 0xD8, 0x80 },
+ { 62813000, 0xD3, 0x70 },
+ { 67625000, 0xCD, 0x68 },
+ { 73250000, 0xCC, 0x60 },
+ { 79875000, 0xCB, 0x58 },
+ { 87875000, 0xCA, 0x50 },
+ { 97625000, 0xC9, 0x48 },
+ { 109875000, 0xC8, 0x40 },
+ { 125625000, 0xC3, 0x38 },
+ { 135250000, 0xBD, 0x34 },
+ { 146500000, 0xBC, 0x30 },
+ { 159750000, 0xBB, 0x2C },
+ { 175750000, 0xBA, 0x28 },
+ { 195250000, 0xB9, 0x24 },
+ { 219750000, 0xB8, 0x20 },
+ { 251250000, 0xB3, 0x1C },
+ { 270500000, 0xAD, 0x1A },
+ { 293000000, 0xAC, 0x18 },
+ { 319500000, 0xAB, 0x16 },
+ { 351500000, 0xAA, 0x14 },
+ { 390500000, 0xA9, 0x12 },
+ { 439500000, 0xA8, 0x10 },
+ { 502500000, 0xA3, 0x0E },
+ { 541000000, 0x9D, 0x0D },
+ { 586000000, 0x9C, 0x0C },
+ { 639000000, 0x9B, 0x0B },
+ { 703000000, 0x9A, 0x0A },
+ { 781000000, 0x99, 0x09 },
+ { 879000000, 0x98, 0x08 },
+ { 0, 0x00, 0x00 }, /* Table End */
+};
+
+static struct SMap m_GainTaper_Map[] = {
+ { 45400000, 0x1F },
+ { 45800000, 0x1E },
+ { 46200000, 0x1D },
+ { 46700000, 0x1C },
+ { 47100000, 0x1B },
+ { 47500000, 0x1A },
+ { 47900000, 0x19 },
+ { 49600000, 0x17 },
+ { 51200000, 0x16 },
+ { 52900000, 0x15 },
+ { 54500000, 0x14 },
+ { 56200000, 0x13 },
+ { 57800000, 0x12 },
+ { 59500000, 0x11 },
+ { 61100000, 0x10 },
+ { 67600000, 0x0D },
+ { 74200000, 0x0C },
+ { 80700000, 0x0B },
+ { 87200000, 0x0A },
+ { 93800000, 0x09 },
+ { 100300000, 0x08 },
+ { 106900000, 0x07 },
+ { 113400000, 0x06 },
+ { 119900000, 0x05 },
+ { 126500000, 0x04 },
+ { 133000000, 0x03 },
+ { 139500000, 0x02 },
+ { 146100000, 0x01 },
+ { 152600000, 0x00 },
+ { 154300000, 0x1F },
+ { 156100000, 0x1E },
+ { 157800000, 0x1D },
+ { 159500000, 0x1C },
+ { 161200000, 0x1B },
+ { 163000000, 0x1A },
+ { 164700000, 0x19 },
+ { 170200000, 0x17 },
+ { 175800000, 0x16 },
+ { 181300000, 0x15 },
+ { 186900000, 0x14 },
+ { 192400000, 0x13 },
+ { 198000000, 0x12 },
+ { 203500000, 0x11 },
+ { 216200000, 0x14 },
+ { 228900000, 0x13 },
+ { 241600000, 0x12 },
+ { 254400000, 0x11 },
+ { 267100000, 0x10 },
+ { 279800000, 0x0F },
+ { 292500000, 0x0E },
+ { 305200000, 0x0D },
+ { 317900000, 0x0C },
+ { 330700000, 0x0B },
+ { 343400000, 0x0A },
+ { 356100000, 0x09 },
+ { 368800000, 0x08 },
+ { 381500000, 0x07 },
+ { 394200000, 0x06 },
+ { 406900000, 0x05 },
+ { 419700000, 0x04 },
+ { 432400000, 0x03 },
+ { 445100000, 0x02 },
+ { 457800000, 0x01 },
+ { 476300000, 0x19 },
+ { 494800000, 0x18 },
+ { 513300000, 0x17 },
+ { 531800000, 0x16 },
+ { 550300000, 0x15 },
+ { 568900000, 0x14 },
+ { 587400000, 0x13 },
+ { 605900000, 0x12 },
+ { 624400000, 0x11 },
+ { 642900000, 0x10 },
+ { 661400000, 0x0F },
+ { 679900000, 0x0E },
+ { 698400000, 0x0D },
+ { 716900000, 0x0C },
+ { 735400000, 0x0B },
+ { 753900000, 0x0A },
+ { 772500000, 0x09 },
+ { 791000000, 0x08 },
+ { 809500000, 0x07 },
+ { 828000000, 0x06 },
+ { 846500000, 0x05 },
+ { 865000000, 0x04 },
+ { 0, 0x00 }, /* Table End */
+};
+
+static struct SMap m_RF_Cal_DC_Over_DT_Map[] = {
+ { 47900000, 0x00 },
+ { 55000000, 0x00 },
+ { 61100000, 0x0A },
+ { 64000000, 0x0A },
+ { 82000000, 0x14 },
+ { 84000000, 0x19 },
+ { 119000000, 0x1C },
+ { 124000000, 0x20 },
+ { 129000000, 0x2A },
+ { 134000000, 0x32 },
+ { 139000000, 0x39 },
+ { 144000000, 0x3E },
+ { 149000000, 0x3F },
+ { 152600000, 0x40 },
+ { 154000000, 0x40 },
+ { 164700000, 0x41 },
+ { 203500000, 0x32 },
+ { 353000000, 0x19 },
+ { 356000000, 0x1A },
+ { 359000000, 0x1B },
+ { 363000000, 0x1C },
+ { 366000000, 0x1D },
+ { 369000000, 0x1E },
+ { 373000000, 0x1F },
+ { 376000000, 0x20 },
+ { 379000000, 0x21 },
+ { 383000000, 0x22 },
+ { 386000000, 0x23 },
+ { 389000000, 0x24 },
+ { 393000000, 0x25 },
+ { 396000000, 0x26 },
+ { 399000000, 0x27 },
+ { 402000000, 0x28 },
+ { 404000000, 0x29 },
+ { 407000000, 0x2A },
+ { 409000000, 0x2B },
+ { 412000000, 0x2C },
+ { 414000000, 0x2D },
+ { 417000000, 0x2E },
+ { 419000000, 0x2F },
+ { 422000000, 0x30 },
+ { 424000000, 0x31 },
+ { 427000000, 0x32 },
+ { 429000000, 0x33 },
+ { 432000000, 0x34 },
+ { 434000000, 0x35 },
+ { 437000000, 0x36 },
+ { 439000000, 0x37 },
+ { 442000000, 0x38 },
+ { 444000000, 0x39 },
+ { 447000000, 0x3A },
+ { 449000000, 0x3B },
+ { 457800000, 0x3C },
+ { 465000000, 0x0F },
+ { 477000000, 0x12 },
+ { 483000000, 0x14 },
+ { 502000000, 0x19 },
+ { 508000000, 0x1B },
+ { 519000000, 0x1C },
+ { 522000000, 0x1D },
+ { 524000000, 0x1E },
+ { 534000000, 0x1F },
+ { 549000000, 0x20 },
+ { 554000000, 0x22 },
+ { 584000000, 0x24 },
+ { 589000000, 0x26 },
+ { 658000000, 0x27 },
+ { 664000000, 0x2C },
+ { 669000000, 0x2D },
+ { 699000000, 0x2E },
+ { 704000000, 0x30 },
+ { 709000000, 0x31 },
+ { 714000000, 0x32 },
+ { 724000000, 0x33 },
+ { 729000000, 0x36 },
+ { 739000000, 0x38 },
+ { 744000000, 0x39 },
+ { 749000000, 0x3B },
+ { 754000000, 0x3C },
+ { 759000000, 0x3D },
+ { 764000000, 0x3E },
+ { 769000000, 0x3F },
+ { 774000000, 0x40 },
+ { 779000000, 0x41 },
+ { 784000000, 0x43 },
+ { 789000000, 0x46 },
+ { 794000000, 0x48 },
+ { 799000000, 0x4B },
+ { 804000000, 0x4F },
+ { 809000000, 0x54 },
+ { 814000000, 0x59 },
+ { 819000000, 0x5D },
+ { 824000000, 0x61 },
+ { 829000000, 0x68 },
+ { 834000000, 0x6E },
+ { 839000000, 0x75 },
+ { 844000000, 0x7E },
+ { 849000000, 0x82 },
+ { 854000000, 0x84 },
+ { 859000000, 0x8F },
+ { 865000000, 0x9A },
+ { 0, 0x00 }, /* Table End */
+};
+
+
+static struct SMap m_IR_Meas_Map[] = {
+ { 200000000, 0x05 },
+ { 400000000, 0x06 },
+ { 865000000, 0x07 },
+ { 0, 0x00 }, /* Table End */
+};
+
+static struct SMap2 m_CID_Target_Map[] = {
+ { 46000000, 0x04, 18 },
+ { 52200000, 0x0A, 15 },
+ { 70100000, 0x01, 40 },
+ { 136800000, 0x18, 40 },
+ { 156700000, 0x18, 40 },
+ { 186250000, 0x0A, 40 },
+ { 230000000, 0x0A, 40 },
+ { 345000000, 0x18, 40 },
+ { 426000000, 0x0E, 40 },
+ { 489500000, 0x1E, 40 },
+ { 697500000, 0x32, 40 },
+ { 842000000, 0x3A, 40 },
+ { 0, 0x00, 0 }, /* Table End */
+};
+
+static struct SRFBandMap m_RF_Band_Map[7] = {
+ { 47900000, 46000000, 0, 0},
+ { 61100000, 52200000, 0, 0},
+ { 152600000, 70100000, 136800000, 0},
+ { 164700000, 156700000, 0, 0},
+ { 203500000, 186250000, 0, 0},
+ { 457800000, 230000000, 345000000, 426000000},
+ { 865000000, 489500000, 697500000, 842000000},
+};
+
+u8 m_Thermometer_Map_1[16] = {
+ 60, 62, 66, 64,
+ 74, 72, 68, 70,
+ 90, 88, 84, 86,
+ 76, 78, 82, 80,
+};
+
+u8 m_Thermometer_Map_2[16] = {
+ 92, 94, 98, 96,
+ 106, 104, 100, 102,
+ 122, 120, 116, 118,
+ 108, 110, 114, 112,
+};
diff --git a/drivers/media/dvb/mantis/Makefile b/drivers/media/dvb/mantis/Makefile
index 98dc5cd258ac..ec8116dcb368 100644
--- a/drivers/media/dvb/mantis/Makefile
+++ b/drivers/media/dvb/mantis/Makefile
@@ -25,4 +25,4 @@ obj-$(CONFIG_MANTIS_CORE) += mantis_core.o
obj-$(CONFIG_DVB_MANTIS) += mantis.o
obj-$(CONFIG_DVB_HOPPER) += hopper.o
-EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/mantis/hopper_cards.c b/drivers/media/dvb/mantis/hopper_cards.c
index 1402062f2c89..71622f65c037 100644
--- a/drivers/media/dvb/mantis/hopper_cards.c
+++ b/drivers/media/dvb/mantis/hopper_cards.c
@@ -65,7 +65,7 @@ static int devs;
static irqreturn_t hopper_irq_handler(int irq, void *dev_id)
{
- u32 stat = 0, mask = 0, lstat = 0, mstat = 0;
+ u32 stat = 0, mask = 0, lstat = 0;
u32 rst_stat = 0, rst_mask = 0;
struct mantis_pci *mantis;
@@ -80,7 +80,7 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id)
stat = mmread(MANTIS_INT_STAT);
mask = mmread(MANTIS_INT_MASK);
- mstat = lstat = stat & ~MANTIS_INT_RISCSTAT;
+ lstat = stat & ~MANTIS_INT_RISCSTAT;
if (!(stat & mask))
return IRQ_NONE;
@@ -126,7 +126,7 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id)
}
if (stat & MANTIS_INT_RISCI) {
dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]);
- mantis->finished_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
+ mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
tasklet_schedule(&mantis->tasklet);
}
if (stat & MANTIS_INT_I2CDONE) {
diff --git a/drivers/media/dvb/mantis/mantis_ca.c b/drivers/media/dvb/mantis/mantis_ca.c
index 330216febd78..3d7046909009 100644
--- a/drivers/media/dvb/mantis/mantis_ca.c
+++ b/drivers/media/dvb/mantis/mantis_ca.c
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
+#include <asm/io.h>
#include "dmxdev.h"
#include "dvbdev.h"
diff --git a/drivers/media/dvb/mantis/mantis_cards.c b/drivers/media/dvb/mantis/mantis_cards.c
index 05cbb9d95727..c2bb90b3e529 100644
--- a/drivers/media/dvb/mantis/mantis_cards.c
+++ b/drivers/media/dvb/mantis/mantis_cards.c
@@ -73,7 +73,7 @@ static char *label[10] = {
static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
{
- u32 stat = 0, mask = 0, lstat = 0, mstat = 0;
+ u32 stat = 0, mask = 0, lstat = 0;
u32 rst_stat = 0, rst_mask = 0;
struct mantis_pci *mantis;
@@ -88,7 +88,7 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
stat = mmread(MANTIS_INT_STAT);
mask = mmread(MANTIS_INT_MASK);
- mstat = lstat = stat & ~MANTIS_INT_RISCSTAT;
+ lstat = stat & ~MANTIS_INT_RISCSTAT;
if (!(stat & mask))
return IRQ_NONE;
@@ -134,7 +134,7 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
}
if (stat & MANTIS_INT_RISCI) {
dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]);
- mantis->finished_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
+ mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
tasklet_schedule(&mantis->tasklet);
}
if (stat & MANTIS_INT_I2CDONE) {
diff --git a/drivers/media/dvb/mantis/mantis_common.h b/drivers/media/dvb/mantis/mantis_common.h
index bd400d21b81f..f2410cf0a6bf 100644
--- a/drivers/media/dvb/mantis/mantis_common.h
+++ b/drivers/media/dvb/mantis/mantis_common.h
@@ -21,6 +21,7 @@
#ifndef __MANTIS_COMMON_H
#define __MANTIS_COMMON_H
+#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
@@ -122,11 +123,8 @@ struct mantis_pci {
unsigned int num;
/* RISC Core */
- u32 finished_block;
+ u32 busy_block;
u32 last_block;
- u32 line_bytes;
- u32 line_count;
- u32 risc_pos;
u8 *buf_cpu;
dma_addr_t buf_dma;
u32 *risc_cpu;
diff --git a/drivers/media/dvb/mantis/mantis_dma.c b/drivers/media/dvb/mantis/mantis_dma.c
index 46202a4012aa..c61ca7d3daea 100644
--- a/drivers/media/dvb/mantis/mantis_dma.c
+++ b/drivers/media/dvb/mantis/mantis_dma.c
@@ -43,13 +43,17 @@
#define RISC_IRQ (0x01 << 24)
#define RISC_STATUS(status) ((((~status) & 0x0f) << 20) | ((status & 0x0f) << 16))
-#define RISC_FLUSH() (mantis->risc_pos = 0)
-#define RISC_INSTR(opcode) (mantis->risc_cpu[mantis->risc_pos++] = cpu_to_le32(opcode))
+#define RISC_FLUSH(risc_pos) (risc_pos = 0)
+#define RISC_INSTR(risc_pos, opcode) (mantis->risc_cpu[risc_pos++] = cpu_to_le32(opcode))
#define MANTIS_BUF_SIZE (64 * 1024)
-#define MANTIS_BLOCK_BYTES (MANTIS_BUF_SIZE >> 4)
-#define MANTIS_BLOCK_COUNT (1 << 4)
-#define MANTIS_RISC_SIZE PAGE_SIZE
+#define MANTIS_BLOCK_BYTES (MANTIS_BUF_SIZE / 4)
+#define MANTIS_DMA_TR_BYTES (2 * 1024) /* upper limit: 4095 bytes. */
+#define MANTIS_BLOCK_COUNT (MANTIS_BUF_SIZE / MANTIS_BLOCK_BYTES)
+
+#define MANTIS_DMA_TR_UNITS (MANTIS_BLOCK_BYTES / MANTIS_DMA_TR_BYTES)
+/* MANTIS_BUF_SIZE / MANTIS_DMA_TR_UNITS must not exceed MANTIS_RISC_SIZE (4k RISC cmd buffer) */
+#define MANTIS_RISC_SIZE PAGE_SIZE /* RISC program must fit here. */
int mantis_dma_exit(struct mantis_pci *mantis)
{
@@ -124,27 +128,6 @@ err:
return -ENOMEM;
}
-static inline int mantis_calc_lines(struct mantis_pci *mantis)
-{
- mantis->line_bytes = MANTIS_BLOCK_BYTES;
- mantis->line_count = MANTIS_BLOCK_COUNT;
-
- while (mantis->line_bytes > 4095) {
- mantis->line_bytes >>= 1;
- mantis->line_count <<= 1;
- }
-
- dprintk(MANTIS_DEBUG, 1, "Mantis RISC block bytes=[%d], line bytes=[%d], line count=[%d]",
- MANTIS_BLOCK_BYTES, mantis->line_bytes, mantis->line_count);
-
- if (mantis->line_count > 255) {
- dprintk(MANTIS_ERROR, 1, "Buffer size error");
- return -EINVAL;
- }
-
- return 0;
-}
-
int mantis_dma_init(struct mantis_pci *mantis)
{
int err = 0;
@@ -158,12 +141,6 @@ int mantis_dma_init(struct mantis_pci *mantis)
goto err;
}
- err = mantis_calc_lines(mantis);
- if (err < 0) {
- dprintk(MANTIS_ERROR, 1, "Mantis calc lines failed");
-
- goto err;
- }
return 0;
err:
@@ -174,31 +151,32 @@ EXPORT_SYMBOL_GPL(mantis_dma_init);
static inline void mantis_risc_program(struct mantis_pci *mantis)
{
u32 buf_pos = 0;
- u32 line;
+ u32 line, step;
+ u32 risc_pos;
dprintk(MANTIS_DEBUG, 1, "Mantis create RISC program");
- RISC_FLUSH();
-
- dprintk(MANTIS_DEBUG, 1, "risc len lines %u, bytes per line %u",
- mantis->line_count, mantis->line_bytes);
-
- for (line = 0; line < mantis->line_count; line++) {
- dprintk(MANTIS_DEBUG, 1, "RISC PROG line=[%d]", line);
- if (!(buf_pos % MANTIS_BLOCK_BYTES)) {
- RISC_INSTR(RISC_WRITE |
- RISC_IRQ |
- RISC_STATUS(((buf_pos / MANTIS_BLOCK_BYTES) +
- (MANTIS_BLOCK_COUNT - 1)) %
- MANTIS_BLOCK_COUNT) |
- mantis->line_bytes);
- } else {
- RISC_INSTR(RISC_WRITE | mantis->line_bytes);
- }
- RISC_INSTR(mantis->buf_dma + buf_pos);
- buf_pos += mantis->line_bytes;
+ RISC_FLUSH(risc_pos);
+
+ dprintk(MANTIS_DEBUG, 1, "risc len lines %u, bytes per line %u, bytes per DMA tr %u",
+ MANTIS_BLOCK_COUNT, MANTIS_BLOCK_BYTES, MANTIS_DMA_TR_BYTES);
+
+ for (line = 0; line < MANTIS_BLOCK_COUNT; line++) {
+ for (step = 0; step < MANTIS_DMA_TR_UNITS; step++) {
+ dprintk(MANTIS_DEBUG, 1, "RISC PROG line=[%d], step=[%d]", line, step);
+ if (step == 0) {
+ RISC_INSTR(risc_pos, RISC_WRITE |
+ RISC_IRQ |
+ RISC_STATUS(line) |
+ MANTIS_DMA_TR_BYTES);
+ } else {
+ RISC_INSTR(risc_pos, RISC_WRITE | MANTIS_DMA_TR_BYTES);
+ }
+ RISC_INSTR(risc_pos, mantis->buf_dma + buf_pos);
+ buf_pos += MANTIS_DMA_TR_BYTES;
+ }
}
- RISC_INSTR(RISC_JUMP);
- RISC_INSTR(mantis->risc_dma);
+ RISC_INSTR(risc_pos, RISC_JUMP);
+ RISC_INSTR(risc_pos, mantis->risc_dma);
}
void mantis_dma_start(struct mantis_pci *mantis)
@@ -210,7 +188,7 @@ void mantis_dma_start(struct mantis_pci *mantis)
mmwrite(mmread(MANTIS_GPIF_ADDR) | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR);
mmwrite(0, MANTIS_DMA_CTL);
- mantis->last_block = mantis->finished_block = 0;
+ mantis->last_block = mantis->busy_block = 0;
mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_RISCI, MANTIS_INT_MASK);
@@ -245,9 +223,9 @@ void mantis_dma_xfer(unsigned long data)
struct mantis_pci *mantis = (struct mantis_pci *) data;
struct mantis_hwconfig *config = mantis->hwconfig;
- while (mantis->last_block != mantis->finished_block) {
+ while (mantis->last_block != mantis->busy_block) {
dprintk(MANTIS_DEBUG, 1, "last block=[%d] finished block=[%d]",
- mantis->last_block, mantis->finished_block);
+ mantis->last_block, mantis->busy_block);
(config->ts_size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter)
(&mantis->demux, &mantis->buf_cpu[mantis->last_block * MANTIS_BLOCK_BYTES], MANTIS_BLOCK_BYTES);
diff --git a/drivers/media/dvb/mantis/mantis_evm.c b/drivers/media/dvb/mantis/mantis_evm.c
index 9f73c2cfc9ea..36f2256ebb0e 100644
--- a/drivers/media/dvb/mantis/mantis_evm.c
+++ b/drivers/media/dvb/mantis/mantis_evm.c
@@ -23,6 +23,7 @@
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
+#include <asm/io.h>
#include "dmxdev.h"
#include "dvbdev.h"
diff --git a/drivers/media/dvb/mantis/mantis_hif.c b/drivers/media/dvb/mantis/mantis_hif.c
index 5772ebb3a69e..672cf4d2462d 100644
--- a/drivers/media/dvb/mantis/mantis_hif.c
+++ b/drivers/media/dvb/mantis/mantis_hif.c
@@ -23,6 +23,7 @@
#include <linux/sched.h>
#include <linux/interrupt.h>
+#include <asm/io.h>
#include "dmxdev.h"
#include "dvbdev.h"
diff --git a/drivers/media/dvb/mantis/mantis_ioc.c b/drivers/media/dvb/mantis/mantis_ioc.c
index 479086dbb9a8..24fcdc63d6d5 100644
--- a/drivers/media/dvb/mantis/mantis_ioc.c
+++ b/drivers/media/dvb/mantis/mantis_ioc.c
@@ -24,6 +24,7 @@
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
+#include <asm/io.h>
#include "dmxdev.h"
#include "dvbdev.h"
diff --git a/drivers/media/dvb/mantis/mantis_pcmcia.c b/drivers/media/dvb/mantis/mantis_pcmcia.c
index 5cb545b913f6..2f188c089666 100644
--- a/drivers/media/dvb/mantis/mantis_pcmcia.c
+++ b/drivers/media/dvb/mantis/mantis_pcmcia.c
@@ -23,6 +23,7 @@
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
+#include <asm/io.h>
#include "dmxdev.h"
#include "dvbdev.h"
diff --git a/drivers/media/dvb/mantis/mantis_uart.c b/drivers/media/dvb/mantis/mantis_uart.c
index f807c8ba26e4..18340dafa426 100644
--- a/drivers/media/dvb/mantis/mantis_uart.c
+++ b/drivers/media/dvb/mantis/mantis_uart.c
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/spinlock.h>
+#include <asm/io.h>
#include <linux/signal.h>
#include <linux/sched.h>
diff --git a/drivers/media/dvb/mantis/mantis_vp1034.c b/drivers/media/dvb/mantis/mantis_vp1034.c
index 26bc0cbe84d4..430ae84ce528 100644
--- a/drivers/media/dvb/mantis/mantis_vp1034.c
+++ b/drivers/media/dvb/mantis/mantis_vp1034.c
@@ -21,6 +21,7 @@
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
+#include <asm/io.h>
#include "dmxdev.h"
#include "dvbdev.h"
diff --git a/drivers/media/dvb/mantis/mantis_vp1041.c b/drivers/media/dvb/mantis/mantis_vp1041.c
index 38a436ca2fdf..07aa887a4b4a 100644
--- a/drivers/media/dvb/mantis/mantis_vp1041.c
+++ b/drivers/media/dvb/mantis/mantis_vp1041.c
@@ -51,7 +51,6 @@ static const struct stb0899_s1_reg vp1041_stb0899_s1_init_1[] = {
{ STB0899_DISRX_ST0 , 0x04 },
{ STB0899_DISRX_ST1 , 0x00 },
{ STB0899_DISPARITY , 0x00 },
- { STB0899_DISFIFO , 0x00 },
{ STB0899_DISSTATUS , 0x20 },
{ STB0899_DISF22 , 0x99 },
{ STB0899_DISF22RX , 0xa8 },
diff --git a/drivers/media/dvb/ngene/Kconfig b/drivers/media/dvb/ngene/Kconfig
index cec242b7c00d..64c84702ba5c 100644
--- a/drivers/media/dvb/ngene/Kconfig
+++ b/drivers/media/dvb/ngene/Kconfig
@@ -5,6 +5,8 @@ config DVB_NGENE
select DVB_STV6110x if !DVB_FE_CUSTOMISE
select DVB_STV090x if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_DRXK if !DVB_FE_CUSTOMISE
+ select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE
---help---
Support for Micronas PCI express cards with nGene bridge.
diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile
index 2bc96874d044..13ebeffb705f 100644
--- a/drivers/media/dvb/ngene/Makefile
+++ b/drivers/media/dvb/ngene/Makefile
@@ -6,9 +6,9 @@ ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o
obj-$(CONFIG_DVB_NGENE) += ngene.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/
-EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+ccflags-y += -Idrivers/media/dvb/dvb-core/
+ccflags-y += -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/common/tuners/
# For the staging CI driver cxd2099
-EXTRA_CFLAGS += -Idrivers/staging/cxd2099/
+ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c
index fcf4be901ec8..056419228363 100644
--- a/drivers/media/dvb/ngene/ngene-cards.c
+++ b/drivers/media/dvb/ngene/ngene-cards.c
@@ -40,6 +40,8 @@
#include "lnbh24.h"
#include "lgdt330x.h"
#include "mt2131.h"
+#include "tda18271c2dd.h"
+#include "drxk.h"
/****************************************************************************/
@@ -83,6 +85,49 @@ static int tuner_attach_stv6110(struct ngene_channel *chan)
}
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct ngene_channel *chan = fe->sec_priv;
+ int status;
+
+ if (enable) {
+ down(&chan->dev->pll_mutex);
+ status = chan->gate_ctrl(fe, 1);
+ } else {
+ status = chan->gate_ctrl(fe, 0);
+ up(&chan->dev->pll_mutex);
+ }
+ return status;
+}
+
+static int tuner_attach_tda18271(struct ngene_channel *chan)
+{
+ struct i2c_adapter *i2c;
+ struct dvb_frontend *fe;
+
+ i2c = &chan->dev->channel[0].i2c_adapter;
+ if (chan->fe->ops.i2c_gate_ctrl)
+ chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
+ fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60);
+ if (chan->fe->ops.i2c_gate_ctrl)
+ chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
+ if (!fe) {
+ printk(KERN_ERR "No TDA18271 found!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int tuner_attach_probe(struct ngene_channel *chan)
+{
+ if (chan->demod_type == 0)
+ return tuner_attach_stv6110(chan);
+ if (chan->demod_type == 1)
+ return tuner_attach_tda18271(chan);
+ return -EINVAL;
+}
+
static int demod_attach_stv0900(struct ngene_channel *chan)
{
struct i2c_adapter *i2c;
@@ -130,6 +175,60 @@ static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock)
up(&chan->dev->pll_mutex);
}
+static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+ struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1 } };
+ return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+ u16 reg, u8 *val)
+{
+ u8 msg[2] = {reg>>8, reg&0xff};
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = msg, .len = 2},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1} };
+ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int port_has_stv0900(struct i2c_adapter *i2c, int port)
+{
+ u8 val;
+ if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0)
+ return 0;
+ return 1;
+}
+
+static int port_has_drxk(struct i2c_adapter *i2c, int port)
+{
+ u8 val;
+
+ if (i2c_read(i2c, 0x29+port, &val) < 0)
+ return 0;
+ return 1;
+}
+
+static int demod_attach_drxk(struct ngene_channel *chan,
+ struct i2c_adapter *i2c)
+{
+ struct drxk_config config;
+
+ memset(&config, 0, sizeof(config));
+ config.adr = 0x29 + (chan->number ^ 2);
+
+ chan->fe = dvb_attach(drxk_attach, &config, i2c, &chan->fe2);
+ if (!chan->fe) {
+ printk(KERN_ERR "No DRXK found!\n");
+ return -ENODEV;
+ }
+ chan->fe->sec_priv = chan;
+ chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
+ chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+ return 0;
+}
+
static int cineS2_probe(struct ngene_channel *chan)
{
struct i2c_adapter *i2c;
@@ -144,43 +243,42 @@ static int cineS2_probe(struct ngene_channel *chan)
else
i2c = &chan->dev->channel[1].i2c_adapter;
- fe_conf = chan->dev->card_info->fe_config[chan->number];
- i2c_msg.addr = fe_conf->address;
-
- /* probe demod */
- i2c_msg.len = 2;
- buf[0] = 0xf1;
- buf[1] = 0x00;
- rc = i2c_transfer(i2c, &i2c_msg, 1);
- if (rc != 1)
- return -ENODEV;
-
- /* demod found, attach it */
- rc = demod_attach_stv0900(chan);
- if (rc < 0 || chan->number < 2)
- return rc;
-
- /* demod #2: reprogram outputs DPN1 & DPN2 */
- i2c_msg.len = 3;
- buf[0] = 0xf1;
- switch (chan->number) {
- case 2:
- buf[1] = 0x5c;
- buf[2] = 0xc2;
- break;
- case 3:
- buf[1] = 0x61;
- buf[2] = 0xcc;
- break;
- default:
+ if (port_has_stv0900(i2c, chan->number)) {
+ chan->demod_type = 0;
+ fe_conf = chan->dev->card_info->fe_config[chan->number];
+ /* demod found, attach it */
+ rc = demod_attach_stv0900(chan);
+ if (rc < 0 || chan->number < 2)
+ return rc;
+
+ /* demod #2: reprogram outputs DPN1 & DPN2 */
+ i2c_msg.addr = fe_conf->address;
+ i2c_msg.len = 3;
+ buf[0] = 0xf1;
+ switch (chan->number) {
+ case 2:
+ buf[1] = 0x5c;
+ buf[2] = 0xc2;
+ break;
+ case 3:
+ buf[1] = 0x61;
+ buf[2] = 0xcc;
+ break;
+ default:
+ return -ENODEV;
+ }
+ rc = i2c_transfer(i2c, &i2c_msg, 1);
+ if (rc != 1) {
+ printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
+ return -EIO;
+ }
+ } else if (port_has_drxk(i2c, chan->number^2)) {
+ chan->demod_type = 1;
+ demod_attach_drxk(chan, i2c);
+ } else {
+ printk(KERN_ERR "No demod found on chan %d\n", chan->number);
return -ENODEV;
}
- rc = i2c_transfer(i2c, &i2c_msg, 1);
- if (rc != 1) {
- printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
- return -EIO;
- }
-
return 0;
}
@@ -306,7 +404,7 @@ static struct ngene_info ngene_info_satixS2v2 = {
.io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
NGENE_IO_TSOUT},
.demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
.fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
.tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
.lnb = {0x0a, 0x08, 0x0b, 0x09},
@@ -321,7 +419,7 @@ static struct ngene_info ngene_info_cineS2v5 = {
.io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
NGENE_IO_TSOUT},
.demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
.fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
.tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
.lnb = {0x0a, 0x08, 0x0b, 0x09},
@@ -331,13 +429,13 @@ static struct ngene_info ngene_info_cineS2v5 = {
};
-static struct ngene_info ngene_info_duoFlexS2 = {
+static struct ngene_info ngene_info_duoFlex = {
.type = NGENE_SIDEWINDER,
- .name = "Digital Devices DuoFlex S2 miniPCIe",
+ .name = "Digital Devices DuoFlex PCIe or miniPCIe",
.io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
NGENE_IO_TSOUT},
.demod_attach = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe},
- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110},
+ .tuner_attach = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe},
.fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
.tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
.lnb = {0x0a, 0x08, 0x0b, 0x09},
@@ -385,8 +483,8 @@ static const struct pci_device_id ngene_id_tbl[] __devinitdata = {
NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2),
NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2),
NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5),
- NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlexS2),
- NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlexS2),
+ NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex),
+ NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
NGENE_ID(0x1461, 0x062e, ngene_info_m780),
{0}
};
diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c
index 6927c726ce35..f129a9303f80 100644
--- a/drivers/media/dvb/ngene/ngene-core.c
+++ b/drivers/media/dvb/ngene/ngene-core.c
@@ -41,7 +41,7 @@
#include "ngene.h"
-static int one_adapter = 1;
+static int one_adapter;
module_param(one_adapter, int, 0444);
MODULE_PARM_DESC(one_adapter, "Use only one adapter.");
@@ -461,7 +461,7 @@ static u8 TSFeatureDecoderSetup[8 * 5] = {
0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,
0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */
0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */
- 0x72, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */
+ 0x72, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */
0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */
};
@@ -507,7 +507,7 @@ void FillTSBuffer(void *Buffer, int Length, u32 Flags)
{
u32 *ptr = Buffer;
- memset(Buffer, 0xff, Length);
+ memset(Buffer, TS_FILLER, Length);
while (Length > 0) {
if (Flags & DF_SWAP32)
*ptr = 0x471FFF10;
@@ -1443,6 +1443,9 @@ static void release_channel(struct ngene_channel *chan)
chan->ci_dev = NULL;
}
+ if (chan->fe2)
+ dvb_unregister_frontend(chan->fe2);
+
if (chan->fe) {
dvb_unregister_frontend(chan->fe);
dvb_frontend_detach(chan->fe);
@@ -1534,6 +1537,14 @@ static int init_channel(struct ngene_channel *chan)
goto err;
chan->has_demux = true;
}
+ if (chan->fe2) {
+ if (dvb_register_frontend(adapter, chan->fe2) < 0)
+ goto err;
+ chan->fe2->tuner_priv = chan->fe->tuner_priv;
+ memcpy(&chan->fe2->ops.tuner_ops,
+ &chan->fe->ops.tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ }
if (chan->has_demux) {
ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
@@ -1571,11 +1582,18 @@ static int init_channels(struct ngene *dev)
return 0;
}
+static struct cxd2099_cfg cxd_cfg = {
+ .bitrate = 62000,
+ .adr = 0x40,
+ .polarity = 0,
+ .clock_mode = 0,
+};
+
static void cxd_attach(struct ngene *dev)
{
struct ngene_ci *ci = &dev->ci;
- ci->en = cxd2099_attach(0x40, dev, &dev->channel[0].i2c_adapter);
+ ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter);
ci->dev = dev;
return;
}
diff --git a/drivers/media/dvb/ngene/ngene-dvb.c b/drivers/media/dvb/ngene/ngene-dvb.c
index 0b4943233166..fcb16a615aab 100644
--- a/drivers/media/dvb/ngene/ngene-dvb.c
+++ b/drivers/media/dvb/ngene/ngene-dvb.c
@@ -118,6 +118,16 @@ static void swap_buffer(u32 *p, u32 len)
}
}
+/* start of filler packet */
+static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER };
+
+/* #define DEBUG_CI_XFER */
+#ifdef DEBUG_CI_XFER
+static u32 ok;
+static u32 overflow;
+static u32 stripped;
+#endif
+
void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
{
struct ngene_channel *chan = priv;
@@ -126,21 +136,41 @@ void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
if (flags & DF_SWAP32)
swap_buffer(buf, len);
+
if (dev->ci.en && chan->number == 2) {
- if (dvb_ringbuffer_free(&dev->tsin_rbuf) > len) {
- dvb_ringbuffer_write(&dev->tsin_rbuf, buf, len);
- wake_up_interruptible(&dev->tsin_rbuf.queue);
+ while (len >= 188) {
+ if (memcmp(buf, fill_ts, sizeof fill_ts) != 0) {
+ if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) {
+ dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188);
+ wake_up(&dev->tsin_rbuf.queue);
+#ifdef DEBUG_CI_XFER
+ ok++;
+#endif
+ }
+#ifdef DEBUG_CI_XFER
+ else
+ overflow++;
+#endif
+ }
+#ifdef DEBUG_CI_XFER
+ else
+ stripped++;
+
+ if (ok % 100 == 0 && overflow)
+ printk(KERN_WARNING "%s: ok %u overflow %u dropped %u\n", __func__, ok, overflow, stripped);
+#endif
+ buf += 188;
+ len -= 188;
}
- return 0;
+ return NULL;
}
- if (chan->users > 0) {
+
+ if (chan->users > 0)
dvb_dmx_swfilter(&chan->demux, buf, len);
- }
+
return NULL;
}
-u8 fill_ts[188] = { 0x47, 0x1f, 0xff, 0x10 };
-
void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
{
struct ngene_channel *chan = priv;
diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h
index 40fce9e3ae66..5443dc0caea5 100644
--- a/drivers/media/dvb/ngene/ngene.h
+++ b/drivers/media/dvb/ngene/ngene.h
@@ -641,8 +641,11 @@ struct ngene_channel {
int mode;
bool has_adapter;
bool has_demux;
+ int demod_type;
+ int (*gate_ctrl)(struct dvb_frontend *, int);
struct dvb_frontend *fe;
+ struct dvb_frontend *fe2;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dvb_net dvbnet;
@@ -786,6 +789,8 @@ struct ngene {
u8 uart_rbuf[UART_RBUF_LEN];
int uart_rp, uart_wp;
+#define TS_FILLER 0x6f
+
u8 *tsout_buf;
#define TSOUT_BUF_SIZE (512*188*8)
struct dvb_ringbuffer tsout_rbuf;
@@ -852,7 +857,7 @@ struct ngene_info {
};
#ifdef NGENE_V4L
-struct ngene_format{
+struct ngene_format {
char *name;
int fourcc; /* video4linux 2 */
int btformat; /* BT848_COLOR_FMT_* */
diff --git a/drivers/media/dvb/pluto2/Makefile b/drivers/media/dvb/pluto2/Makefile
index 7ac128724df8..700822350ec5 100644
--- a/drivers/media/dvb/pluto2/Makefile
+++ b/drivers/media/dvb/pluto2/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_DVB_PLUTO2) += pluto2.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c
index 7cb79ec685f0..80fb51004461 100644
--- a/drivers/media/dvb/pluto2/pluto2.c
+++ b/drivers/media/dvb/pluto2/pluto2.c
@@ -26,6 +26,7 @@
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
diff --git a/drivers/media/dvb/pt1/Makefile b/drivers/media/dvb/pt1/Makefile
index a66da17bbe31..d80d8e8e7c57 100644
--- a/drivers/media/dvb/pt1/Makefile
+++ b/drivers/media/dvb/pt1/Makefile
@@ -2,4 +2,4 @@ earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o
obj-$(CONFIG_DVB_PT1) += earth-pt1.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-core -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/siano/Makefile b/drivers/media/dvb/siano/Makefile
index c54140b5ab5a..f233b57c86fb 100644
--- a/drivers/media/dvb/siano/Makefile
+++ b/drivers/media/dvb/siano/Makefile
@@ -5,7 +5,7 @@ obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o
obj-$(CONFIG_SMS_USB_DRV) += smsusb.o
obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c
index af121db88ea0..680c781c8dd6 100644
--- a/drivers/media/dvb/siano/sms-cards.c
+++ b/drivers/media/dvb/siano/sms-cards.c
@@ -19,6 +19,7 @@
#include "sms-cards.h"
#include "smsir.h"
+#include <linux/module.h>
static int sms_dbg;
module_param_named(cards_dbg, sms_dbg, int, 0644);
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
index 78765ed28063..7331e8450d1a 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/dvb/siano/smscoreapi.c
@@ -1147,7 +1147,7 @@ static int smscore_validate_client(struct smscore_device_t *coredev,
if (!client) {
sms_err("bad parameter.");
- return -EFAULT;
+ return -EINVAL;
}
registered_client = smscore_find_client(coredev, data_type, id);
if (registered_client == client)
diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h
index 8ecadecaa9d0..c592ae090397 100644
--- a/drivers/media/dvb/siano/smscoreapi.h
+++ b/drivers/media/dvb/siano/smscoreapi.h
@@ -22,7 +22,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __SMS_CORE_API_H__
#define __SMS_CORE_API_H__
-#include <linux/version.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/mm.h>
diff --git a/drivers/media/dvb/siano/smsendian.c b/drivers/media/dvb/siano/smsendian.c
index 457b6d02ef85..e2657c2f0109 100644
--- a/drivers/media/dvb/siano/smsendian.c
+++ b/drivers/media/dvb/siano/smsendian.c
@@ -19,6 +19,7 @@
****************************************************************/
+#include <linux/export.h>
#include <asm/byteorder.h>
#include "smsendian.h"
diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c
index e57d38b0197c..91f8c8291e2b 100644
--- a/drivers/media/dvb/siano/smssdio.c
+++ b/drivers/media/dvb/siano/smssdio.c
@@ -39,6 +39,7 @@
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
+#include <linux/module.h>
#include "smscoreapi.h"
#include "sms-cards.h"
diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c
index 0c8164a2cc36..51c7121b321a 100644
--- a/drivers/media/dvb/siano/smsusb.c
+++ b/drivers/media/dvb/siano/smsusb.c
@@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <linux/usb.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "smscoreapi.h"
#include "sms-cards.h"
diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile
index 8a4d5bb20a5b..f6e869372e30 100644
--- a/drivers/media/dvb/ttpci/Makefile
+++ b/drivers/media/dvb/ttpci/Makefile
@@ -17,5 +17,5 @@ obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o
obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o
obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
index cdd31cae46c4..ee8ee1d481fa 100644
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -25,6 +25,8 @@
* the project's page is at http://www.linuxtv.org/
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/delay.h>
@@ -253,7 +255,7 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh)
switch (av7110->current_input) {
case 1:
- dprintk(1, "switching SAA7113 to Analog Tuner Input.\n");
+ dprintk(1, "switching SAA7113 to Analog Tuner Input\n");
msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source
msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source
msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source
@@ -263,7 +265,7 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh)
if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
if (ves1820_writereg(dev, 0x09, 0x0f, 0x60))
- dprintk(1, "setting band in demodulator failed.\n");
+ dprintk(1, "setting band in demodulator failed\n");
} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD)
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF)
@@ -272,17 +274,17 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh)
dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
break;
case 2:
- dprintk(1, "switching SAA7113 to Video AV CVBS Input.\n");
+ dprintk(1, "switching SAA7113 to Video AV CVBS Input\n");
if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1)
dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
break;
case 3:
- dprintk(1, "switching SAA7113 to Video AV Y/C Input.\n");
+ dprintk(1, "switching SAA7113 to Video AV Y/C Input\n");
if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1)
dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
break;
default:
- dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input.\n");
+ dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n");
}
} else {
adswitch = 0;
@@ -299,7 +301,7 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh)
if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
if (ves1820_writereg(dev, 0x09, 0x0f, 0x20))
- dprintk(1, "setting band in demodulator failed.\n");
+ dprintk(1, "setting band in demodulator failed\n");
} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
@@ -413,7 +415,7 @@ static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
- dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x.\n", f->frequency);
+ dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency);
if (!av7110->analog_tuner_flags || av7110->current_input != 1)
return -EINVAL;
@@ -429,7 +431,7 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
- dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x.\n", f->frequency);
+ dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency);
if (!av7110->analog_tuner_flags || av7110->current_input != 1)
return -EINVAL;
@@ -689,12 +691,12 @@ int av7110_init_analog_module(struct av7110 *av7110)
if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 &&
i2c_writereg(av7110, 0x80, 0x0, 0) == 1) {
- printk("dvb-ttpci: DVB-C analog module @ card %d detected, initializing MSP3400\n",
+ pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n",
av7110->dvb_adapter.num);
av7110->adac_type = DVB_ADAC_MSP34x0;
} else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 &&
i2c_writereg(av7110, 0x84, 0x0, 0) == 1) {
- printk("dvb-ttpci: DVB-C analog module @ card %d detected, initializing MSP3415\n",
+ pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n",
av7110->dvb_adapter.num);
av7110->adac_type = DVB_ADAC_MSP34x5;
} else
@@ -715,7 +717,7 @@ int av7110_init_analog_module(struct av7110 *av7110)
msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART
if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) {
- INFO(("saa7113 not accessible.\n"));
+ pr_info("saa7113 not accessible\n");
} else {
u8 *i = saa7113_init_regs;
@@ -733,7 +735,7 @@ int av7110_init_analog_module(struct av7110 *av7110)
/* setup for DVB by default */
if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20))
- dprintk(1, "setting band in demodulator failed.\n");
+ dprintk(1, "setting band in demodulator failed\n");
} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
@@ -797,7 +799,7 @@ int av7110_init_v4l(struct av7110 *av7110)
ret = saa7146_vv_init(dev, vv_data);
if (ret) {
- ERR(("cannot init capture device. skipping.\n"));
+ ERR("cannot init capture device. skipping\n");
return -ENODEV;
}
vv_data->ops.vidioc_enum_input = vidioc_enum_input;
@@ -814,12 +816,12 @@ int av7110_init_v4l(struct av7110 *av7110)
vv_data->ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out;
if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) {
- ERR(("cannot register capture device. skipping.\n"));
+ ERR("cannot register capture device. skipping\n");
saa7146_vv_release(dev);
return -ENODEV;
}
if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI))
- ERR(("cannot register vbi v4l2 device. skipping.\n"));
+ ERR("cannot register vbi v4l2 device. skipping\n");
return 0;
}
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index e957d7690bcc..78d32f7e49fc 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -33,6 +33,8 @@
* the project's page is at http://www.linuxtv.org/
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "budget.h"
#include "stv0299.h"
#include "stb0899_drv.h"
@@ -149,7 +151,7 @@ static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int ad
result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1);
if (result == -ETIMEDOUT) {
ciintf_slot_shutdown(ca, slot);
- printk(KERN_INFO "budget-av: cam ejected 1\n");
+ pr_info("cam ejected 1\n");
}
return result;
}
@@ -168,7 +170,7 @@ static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int a
result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1);
if (result == -ETIMEDOUT) {
ciintf_slot_shutdown(ca, slot);
- printk(KERN_INFO "budget-av: cam ejected 2\n");
+ pr_info("cam ejected 2\n");
}
return result;
}
@@ -187,7 +189,7 @@ static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 addre
result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0);
if (result == -ETIMEDOUT) {
ciintf_slot_shutdown(ca, slot);
- printk(KERN_INFO "budget-av: cam ejected 3\n");
+ pr_info("cam ejected 3\n");
return -ETIMEDOUT;
}
return result;
@@ -207,7 +209,7 @@ static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 addr
result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0);
if (result == -ETIMEDOUT) {
ciintf_slot_shutdown(ca, slot);
- printk(KERN_INFO "budget-av: cam ejected 5\n");
+ pr_info("cam ejected 5\n");
}
return result;
}
@@ -289,7 +291,7 @@ static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open
if (saa7146_read(saa, PSR) & MASK_06) {
if (budget_av->slot_status == SLOTSTATUS_NONE) {
budget_av->slot_status = SLOTSTATUS_PRESENT;
- printk(KERN_INFO "budget-av: cam inserted A\n");
+ pr_info("cam inserted A\n");
}
}
saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
@@ -306,11 +308,11 @@ static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open
result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1);
if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) {
budget_av->slot_status = SLOTSTATUS_PRESENT;
- printk(KERN_INFO "budget-av: cam inserted B\n");
+ pr_info("cam inserted B\n");
} else if (result < 0) {
if (budget_av->slot_status != SLOTSTATUS_NONE) {
ciintf_slot_shutdown(ca, slot);
- printk(KERN_INFO "budget-av: cam ejected 5\n");
+ pr_info("cam ejected 5\n");
return 0;
}
}
@@ -365,11 +367,11 @@ static int ciintf_init(struct budget_av *budget_av)
if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter,
&budget_av->ca, 0, 1)) != 0) {
- printk(KERN_ERR "budget-av: ci initialisation failed.\n");
+ pr_err("ci initialisation failed\n");
goto error;
}
- printk(KERN_INFO "budget-av: ci interface initialised.\n");
+ pr_info("ci interface initialised\n");
return 0;
error:
@@ -896,7 +898,6 @@ static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = {
{ STB0899_DISRX_ST0 , 0x04 },
{ STB0899_DISRX_ST1 , 0x00 },
{ STB0899_DISPARITY , 0x00 },
- { STB0899_DISFIFO , 0x00 },
{ STB0899_DISSTATUS , 0x20 },
{ STB0899_DISF22 , 0x8c },
{ STB0899_DISF22RX , 0x9a },
@@ -1197,6 +1198,7 @@ static u8 read_pwm(struct budget_av *budget_av)
#define SUBID_DVBC_KNC1 0x0020
#define SUBID_DVBC_KNC1_PLUS 0x0021
#define SUBID_DVBC_KNC1_MK3 0x0022
+#define SUBID_DVBC_KNC1_TDA10024 0x0028
#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023
#define SUBID_DVBC_CINERGY1200 0x1156
#define SUBID_DVBC_CINERGY1200_MK3 0x1176
@@ -1316,6 +1318,7 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBC_EASYWATCH_MK3:
case SUBID_DVBC_CINERGY1200_MK3:
case SUBID_DVBC_KNC1_MK3:
+ case SUBID_DVBC_KNC1_TDA10024:
case SUBID_DVBC_KNC1_PLUS_MK3:
budget_av->reinitialise_demod = 1;
budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
@@ -1343,8 +1346,7 @@ static void frontend_init(struct budget_av *budget_av)
}
if (fe == NULL) {
- printk(KERN_ERR "budget-av: A frontend driver was not found "
- "for device [%04x:%04x] subsystem [%04x:%04x]\n",
+ pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
saa->pci->vendor,
saa->pci->device,
saa->pci->subsystem_vendor,
@@ -1356,7 +1358,7 @@ static void frontend_init(struct budget_av *budget_av)
if (dvb_register_frontend(&budget_av->budget.dvb_adapter,
budget_av->budget.dvb_frontend)) {
- printk(KERN_ERR "budget-av: Frontend registration failed!\n");
+ pr_err("Frontend registration failed!\n");
dvb_frontend_detach(budget_av->budget.dvb_frontend);
budget_av->budget.dvb_frontend = NULL;
}
@@ -1414,7 +1416,7 @@ static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
{
- dprintk(1, "VIDIOC_ENUMINPUT %d.\n", i->index);
+ dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index);
if (i->index >= KNC1_INPUTS)
return -EINVAL;
memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
@@ -1428,7 +1430,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
*i = budget_av->cur_input;
- dprintk(1, "VIDIOC_G_INPUT %d.\n", *i);
+ dprintk(1, "VIDIOC_G_INPUT %d\n", *i);
return 0;
}
@@ -1437,7 +1439,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct budget_av *budget_av = (struct budget_av *)dev->ext_priv;
- dprintk(1, "VIDIOC_S_INPUT %d.\n", input);
+ dprintk(1, "VIDIOC_S_INPUT %d\n", input);
return saa7113_setinput(budget_av, input);
}
@@ -1476,7 +1478,7 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
if (0 != saa7146_vv_init(dev, &vv_data)) {
/* fixme: proper cleanup here */
- ERR(("cannot init vv subsystem.\n"));
+ ERR("cannot init vv subsystem\n");
return err;
}
vv_data.ops.vidioc_enum_input = vidioc_enum_input;
@@ -1485,7 +1487,7 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) {
/* fixme: proper cleanup here */
- ERR(("cannot register capture v4l2 device.\n"));
+ ERR("cannot register capture v4l2 device\n");
saa7146_vv_release(dev);
return err;
}
@@ -1502,13 +1504,12 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
mac = budget_av->budget.dvb_adapter.proposed_mac;
if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) {
- printk(KERN_ERR "KNC1-%d: Could not read MAC from KNC1 card\n",
+ pr_err("KNC1-%d: Could not read MAC from KNC1 card\n",
budget_av->budget.dvb_adapter.num);
memset(mac, 0, 6);
} else {
- printk(KERN_INFO "KNC1-%d: MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
- budget_av->budget.dvb_adapter.num,
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ pr_info("KNC1-%d: MAC addr = %pM\n",
+ budget_av->budget.dvb_adapter.num, mac);
}
budget_av->budget.dvb_adapter.priv = budget_av;
@@ -1558,6 +1559,7 @@ MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);
MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP);
MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3);
+MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024);
MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3);
MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
@@ -1587,6 +1589,7 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022),
+ MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028),
MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023),
MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031),
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index 926f299b5225..ca02e9722172 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -1053,7 +1053,6 @@ static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = {
{ STB0899_DISRX_ST0 , 0x04 },
{ STB0899_DISRX_ST1 , 0x00 },
{ STB0899_DISPARITY , 0x00 },
- { STB0899_DISFIFO , 0x00 },
{ STB0899_DISSTATUS , 0x20 },
{ STB0899_DISF22 , 0x8c },
{ STB0899_DISF22RX , 0x9a },
diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c
index 37666d4edab6..37d02fe09137 100644
--- a/drivers/media/dvb/ttpci/budget-core.c
+++ b/drivers/media/dvb/ttpci/budget-core.c
@@ -110,6 +110,7 @@ static int start_ts_capture(struct budget *budget)
break;
case BUDGET_CIN1200C_MK3:
case BUDGET_KNC1C_MK3:
+ case BUDGET_KNC1C_TDA10024:
case BUDGET_KNC1CP_MK3:
if (budget->video_port == BUDGET_VIDEO_PORTA) {
saa7146_write(dev, DD1_INIT, 0x06000200);
@@ -434,6 +435,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
case BUDGET_KNC1CP:
case BUDGET_CIN1200C:
case BUDGET_KNC1C_MK3:
+ case BUDGET_KNC1C_TDA10024:
case BUDGET_KNC1CP_MK3:
case BUDGET_CIN1200C_MK3:
budget->buffer_width = TS_WIDTH_DVBC;
diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h
index 3ad0c6789ba7..3d8a806c20bb 100644
--- a/drivers/media/dvb/ttpci/budget.h
+++ b/drivers/media/dvb/ttpci/budget.h
@@ -104,6 +104,7 @@ static struct saa7146_pci_extension_data x_var = { \
#define BUDGET_KNC1C_MK3 16
#define BUDGET_KNC1CP_MK3 17
#define BUDGET_KNC1S2 18
+#define BUDGET_KNC1C_TDA10024 19
#define BUDGET_VIDEO_PORTA 0
#define BUDGET_VIDEO_PORTB 1
diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.c b/drivers/media/dvb/ttpci/ttpci-eeprom.c
index 7dd54b3026a2..32d43156c548 100644
--- a/drivers/media/dvb/ttpci/ttpci-eeprom.c
+++ b/drivers/media/dvb/ttpci/ttpci-eeprom.c
@@ -85,6 +85,35 @@ static int getmac_tt(u8 * decodedMAC, u8 * encodedMAC)
return 0;
}
+int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC)
+{
+ u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c,
+ 0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6,
+ 0x1d, 0x36, 0x64, 0x78};
+ u8 data[20];
+ int i;
+
+ memcpy(data, encodedMAC, 20);
+
+ for (i = 0; i < 20; i++)
+ data[i] ^= xor[i];
+ for (i = 0; i < 10; i++)
+ data[i] = ((data[2 * i + 1] << 8) | data[2 * i])
+ >> ((data[2 * i + 1] >> 6) & 3);
+
+ if (check_mac_tt(data))
+ return -ENODEV;
+
+ decodedMAC[0] = data[2];
+ decodedMAC[1] = data[1];
+ decodedMAC[2] = data[0];
+ decodedMAC[3] = data[6];
+ decodedMAC[4] = data[5];
+ decodedMAC[5] = data[4];
+ return 0;
+}
+EXPORT_SYMBOL(ttpci_eeprom_decode_mac);
+
static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encodedMAC)
{
int ret;
diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.h b/drivers/media/dvb/ttpci/ttpci-eeprom.h
index e2dc6cfe205c..dcc33d5a5cb1 100644
--- a/drivers/media/dvb/ttpci/ttpci-eeprom.h
+++ b/drivers/media/dvb/ttpci/ttpci-eeprom.h
@@ -28,6 +28,7 @@
#include <linux/types.h>
#include <linux/i2c.h>
+extern int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC);
extern int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *propsed_mac);
#endif
diff --git a/drivers/media/dvb/ttusb-budget/Makefile b/drivers/media/dvb/ttusb-budget/Makefile
index fbe2b9514c21..8d6c4acb7f1d 100644
--- a/drivers/media/dvb/ttusb-budget/Makefile
+++ b/drivers/media/dvb/ttusb-budget/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_DVB_TTUSB_BUDGET) += dvb-ttusb-budget.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/ttusb-dec/Makefile b/drivers/media/dvb/ttusb-dec/Makefile
index 2d70a8269391..ed28b5384d20 100644
--- a/drivers/media/dvb/ttusb-dec/Makefile
+++ b/drivers/media/dvb/ttusb-dec/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_DVB_TTUSB_DEC) += ttusb_dec.o ttusbdecfe.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+ccflags-y += -Idrivers/media/dvb/dvb-core/
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index 16b70b4412f7..6edc9ba81203 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -23,6 +23,7 @@
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/media.h>
+#include <linux/export.h>
#include <media/media-device.h>
#include <media/media-devnode.h>
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index e4c97fd6f05a..ccd5f0d8a012 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -168,7 +168,7 @@ config RADIO_MAXIRADIO
config RADIO_MIROPCM20
tristate "miroSOUND PCM20 radio"
- depends on ISA && VIDEO_V4L2 && SND
+ depends on ISA && ISA_DMA_API && VIDEO_V4L2 && SND
select SND_ISA
select SND_MIRO
---help---
@@ -201,7 +201,7 @@ config RADIO_SF16FMI
config RADIO_SF16FMR2
tristate "SF16FMR2 Radio"
- depends on ISA && VIDEO_V4L2
+ depends on ISA && VIDEO_V4L2 && SND
---help---
Choose Y here if you have one of these FM radio cards.
@@ -426,7 +426,6 @@ config RADIO_TIMBERDALE
config RADIO_WL1273
tristate "Texas Instruments WL1273 I2C FM Radio"
depends on I2C && VIDEO_V4L2
- select MFD_CORE
select MFD_WL1273_CORE
select FW_LOADER
---help---
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index f484a6e04eb2..390daf94d847 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -27,4 +27,4 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
obj-$(CONFIG_RADIO_WL128X) += wl128x/
-EXTRA_CFLAGS += -Isound
+ccflags-y += -Isound
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c
index 3d8cc425fa6b..25e58cbf35f0 100644
--- a/drivers/media/radio/dsbr100.c
+++ b/drivers/media/radio/dsbr100.c
@@ -102,10 +102,7 @@
/*
* Version Information
*/
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
-
-#define DRIVER_VERSION "v0.46"
-#define RADIO_VERSION KERNEL_VERSION(0, 4, 6)
+#define DRIVER_VERSION "0.4.7"
#define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
@@ -335,7 +332,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "dsbr100", sizeof(v->driver));
strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card));
usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER;
return 0;
}
@@ -647,3 +643,4 @@ module_exit (dsbr100_exit);
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 4ce10dbeadd8..1c3f8440a55c 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -33,7 +33,6 @@
#include <linux/ioport.h> /* request_region */
#include <linux/delay.h> /* msleep */
#include <linux/videodev2.h> /* kernel radio structs */
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -41,6 +40,7 @@
MODULE_AUTHOR("M.Kirkwood");
MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
#ifndef CONFIG_RADIO_RTRACK_PORT
#define CONFIG_RADIO_RTRACK_PORT -1
@@ -53,8 +53,6 @@ module_param(io, int, 0);
MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
module_param(radio_nr, int, 0);
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
struct rtrack
{
struct v4l2_device v4l2_dev;
@@ -223,7 +221,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-aimslab", sizeof(v->driver));
strlcpy(v->card, "RadioTrack", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
index dd8a6ab0d437..eed7b0840734 100644
--- a/drivers/media/radio/radio-aztech.c
+++ b/drivers/media/radio/radio-aztech.c
@@ -30,7 +30,6 @@
#include <linux/ioport.h> /* request_region */
#include <linux/delay.h> /* udelay */
#include <linux/videodev2.h> /* kernel radio structs */
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -38,6 +37,7 @@
MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
MODULE_DESCRIPTION("A driver for the Aztech radio card.");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
@@ -53,8 +53,6 @@ module_param(io, int, 0);
module_param(radio_nr, int, 0);
MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
struct aztech
{
struct v4l2_device v4l2_dev;
@@ -188,7 +186,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-aztech", sizeof(v->driver));
strlcpy(v->card, "Aztech Radio", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index bc9ad0897c55..16a089fad909 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -30,7 +30,6 @@
* Changed API to V4L2
*/
-#include <linux/version.h>
#include <linux/module.h> /* Modules */
#include <linux/init.h> /* Initdata */
#include <linux/ioport.h> /* request_region */
@@ -46,6 +45,7 @@
MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.3.4");
static int io = -1; /* default to isapnp activation */
static int radio_nr = -1;
@@ -54,8 +54,6 @@ module_param(io, int, 0);
MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
module_param(radio_nr, int, 0);
-#define CADET_VERSION KERNEL_VERSION(0, 3, 3)
-
#define RDS_BUFFER 256
#define RDS_RX_FLAG 1
#define MBS_RX_FLAG 2
@@ -361,7 +359,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "ADS Cadet", sizeof(v->driver));
strlcpy(v->card, "ADS Cadet", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = CADET_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
return 0;
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 259936422e49..edadc8449a3d 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -21,21 +21,19 @@
#include <linux/ioport.h> /* request_region */
#include <linux/delay.h> /* udelay */
#include <linux/videodev2.h> /* kernel radio structs */
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/mutex.h>
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 3)
-
/*
* Module info.
*/
-MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>");
+MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>");
MODULE_DESCRIPTION("A driver for the GemTek Radio card.");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.4");
/*
* Module params.
@@ -387,7 +385,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-gemtek", sizeof(v->driver));
strlcpy(v->card, "GemTek", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
index e83e84003025..f872a54cf3d9 100644
--- a/drivers/media/radio/radio-maxiradio.c
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -40,15 +40,18 @@
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/videodev2.h>
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/io.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#define DRIVER_VERSION "0.7.8"
+
+
MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
static int radio_nr = -1;
module_param(radio_nr, int, 0);
@@ -58,10 +61,6 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
-#define DRIVER_VERSION "0.77"
-
-#define RADIO_VERSION KERNEL_VERSION(0, 7, 7)
-
#define dprintk(dev, num, fmt, arg...) \
v4l2_dbg(num, debug, &dev->v4l2_dev, fmt, ## arg)
@@ -195,7 +194,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-maxiradio", sizeof(v->driver));
strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof(v->card));
snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index b3a635b95820..1742bd8110bd 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -63,18 +63,17 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <linux/usb.h>
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/mutex.h>
/* driver and module definitions */
#define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>"
#define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"
-#define DRIVER_VERSION "0.11"
-#define RADIO_VERSION KERNEL_VERSION(0, 1, 1)
+#define DRIVER_VERSION "0.1.2"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
#define USB_AMRADIO_VENDOR 0x07ca
#define USB_AMRADIO_PRODUCT 0xb800
@@ -301,7 +300,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-mr800", sizeof(v->driver));
strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));
usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER;
return 0;
}
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index 8d6ea591bd18..3628be617ee9 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -15,7 +15,6 @@
#include <linux/delay.h> /* udelay */
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/mutex.h>
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -23,6 +22,7 @@
MODULE_AUTHOR("Ben Pfaff");
MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
#ifndef CONFIG_RADIO_RTRACK2_PORT
#define CONFIG_RADIO_RTRACK2_PORT -1
@@ -35,8 +35,6 @@ module_param(io, int, 0);
MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
module_param(radio_nr, int, 0);
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
struct rtrack2
{
struct v4l2_device v4l2_dev;
@@ -121,7 +119,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver));
strlcpy(v->card, "RadioTrack II", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index b5a5f89e238a..22c5743bf9db 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -16,7 +16,6 @@
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
-#include <linux/version.h>
#include <linux/kernel.h> /* __setup */
#include <linux/module.h> /* Modules */
#include <linux/init.h> /* Initdata */
@@ -32,6 +31,7 @@
MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
MODULE_DESCRIPTION("A driver for the SF16-FMI and SF16-FMP radio.");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
static int io = -1;
static int radio_nr = -1;
@@ -40,8 +40,6 @@ module_param(io, int, 0);
MODULE_PARM_DESC(io, "I/O address of the SF16-FMI or SF16-FMP card (0x284 or 0x384)");
module_param(radio_nr, int, 0);
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
struct fmi
{
struct v4l2_device v4l2_dev;
@@ -134,7 +132,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver));
strlcpy(v->card, "SF16-FMx radio", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index 87bad7678d92..2dd485996ba8 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -1,441 +1,209 @@
-/* SF16FMR2 radio driver for Linux radio support
- * heavily based on fmi driver...
- * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+/* SF16-FMR2 radio driver for Linux
+ * Copyright (c) 2011 Ondrej Zary
*
- * Notes on the hardware
- *
- * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
- * No volume control - only mute/unmute - you have to use line volume
- *
- * For read stereo/mono you must wait 0.1 sec after set frequency and
- * card unmuted so I set frequency on unmute
- * Signal handling seem to work only on autoscanning (not implemented)
- *
- * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ * but almost nothing remained here after conversion to generic TEA575x
+ * implementation
*/
+#include <linux/delay.h>
#include <linux/module.h> /* Modules */
#include <linux/init.h> /* Initdata */
#include <linux/ioport.h> /* request_region */
-#include <linux/delay.h> /* udelay */
-#include <linux/videodev2.h> /* kernel radio structs */
-#include <linux/mutex.h>
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/io.h> /* outb, outb_p */
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <sound/tea575x-tuner.h>
-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
MODULE_LICENSE("GPL");
-static int io = 0x384;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
-
-#define AUD_VOL_INDEX 1
-
-#undef DEBUG
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define debug_print(s) printk s
-#else
-# define debug_print(s)
-#endif
-
-/* this should be static vars for module size */
-struct fmr2
-{
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- struct mutex lock;
+struct fmr2 {
int io;
- int curvol; /* 0-15 */
- int mute;
- int stereo; /* card is producing stereo audio */
- unsigned long curfreq; /* freq in kHz */
- int card_type;
+ struct snd_tea575x tea;
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *balance;
};
+/* the port is hardwired so no need to support multiple cards */
+#define FMR2_PORT 0x384
static struct fmr2 fmr2_card;
-/* hw precision is 12.5 kHz
- * It is only useful to give freq in interval of 200 (=0.0125Mhz),
- * other bits will be truncated
- */
-#define RSF16_ENCODE(x) ((x) / 200 + 856)
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
-
-static inline void wait(int n, int io)
-{
- for (; n; --n)
- inb(io);
-}
-
-static void outbits(int bits, unsigned int data, int nWait, int io)
-{
- int bit;
-
- for (; --bits >= 0;) {
- bit = (data >> bits) & 1;
- outb(bit, io);
- wait(nWait, io);
- outb(bit | 2, io);
- wait(nWait, io);
- outb(bit, io);
- wait(nWait, io);
- }
-}
-
-static inline void fmr2_mute(int io)
-{
- outb(0x00, io);
- wait(4, io);
-}
-
-static inline void fmr2_unmute(int io)
-{
- outb(0x04, io);
- wait(4, io);
-}
-
-static inline int fmr2_stereo_mode(int io)
-{
- int n = inb(io);
-
- outb(6, io);
- inb(io);
- n = ((n >> 3) & 1) ^ 1;
- debug_print((KERN_DEBUG "stereo: %d\n", n));
- return n;
-}
-
-static int fmr2_product_info(struct fmr2 *dev)
-{
- int n = inb(dev->io);
-
- n &= 0xC1;
- if (n == 0) {
- /* this should support volume set */
- dev->card_type = 12;
- return 0;
- }
- /* not volume (mine is 11) */
- dev->card_type = (n == 128) ? 11 : 0;
- return n;
-}
+/* TEA575x tuner pins */
+#define STR_DATA (1 << 0)
+#define STR_CLK (1 << 1)
+#define STR_WREN (1 << 2)
+#define STR_MOST (1 << 3)
+/* PT2254A/TC9154A volume control pins */
+#define PT_ST (1 << 4)
+#define PT_CK (1 << 5)
+#define PT_DATA (1 << 6)
+/* volume control presence pin */
+#define FMR2_HASVOL (1 << 7)
-static inline int fmr2_getsigstr(struct fmr2 *dev)
+static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
- /* !!! works only if scanning freq */
- int res = 0xffff;
-
- outb(5, dev->io);
- wait(4, dev->io);
- if (!(inb(dev->io) & 1))
- res = 0;
- debug_print((KERN_DEBUG "signal: %d\n", res));
- return res;
-}
-
-/* set frequency and unmute card */
-static int fmr2_setfreq(struct fmr2 *dev)
-{
- unsigned long freq = dev->curfreq;
-
- fmr2_mute(dev->io);
-
- /* 0x42 for mono output
- * 0x102 forward scanning
- * 0x182 scansione avanti
- */
- outbits(9, 0x2, 3, dev->io);
- outbits(16, RSF16_ENCODE(freq), 2, dev->io);
-
- fmr2_unmute(dev->io);
+ struct fmr2 *fmr2 = tea->private_data;
+ u8 bits = 0;
- /* wait 0.11 sec */
- msleep(110);
+ bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+ bits |= (pins & TEA575X_CLK) ? STR_CLK : 0;
+ /* WRITE_ENABLE is inverted, DATA must be high during read */
+ bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
- /* NOTE if mute this stop radio
- you must set freq on unmute */
- dev->stereo = fmr2_stereo_mode(dev->io);
- return 0;
-}
-
-/* !!! not tested, in my card this doesn't work !!! */
-static int fmr2_setvolume(struct fmr2 *dev)
-{
- int vol[16] = { 0x021, 0x084, 0x090, 0x104,
- 0x110, 0x204, 0x210, 0x402,
- 0x404, 0x408, 0x410, 0x801,
- 0x802, 0x804, 0x808, 0x810 };
- int i, a;
- int n = vol[dev->curvol & 0x0f];
-
- if (dev->card_type != 11)
- return 1;
-
- for (i = 12; --i >= 0; ) {
- a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
- outb(a | 4, dev->io);
- wait(4, dev->io);
- outb(a | 0x24, dev->io);
- wait(4, dev->io);
- outb(a | 4, dev->io);
- wait(4, dev->io);
- }
- for (i = 6; --i >= 0; ) {
- a = ((0x18 >> i) & 1) << 6;
- outb(a | 4, dev->io);
- wait(4, dev->io);
- outb(a | 0x24, dev->io);
- wait(4, dev->io);
- outb(a | 4, dev->io);
- wait(4, dev->io);
- }
- wait(4, dev->io);
- outb(0x14, dev->io);
- return 0;
+ outb(bits, fmr2->io);
}
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
+static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
{
- strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
- strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = RADIO_VERSION;
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct fmr2 *fmr2 = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
+ struct fmr2 *fmr2 = tea->private_data;
+ u8 bits = inb(fmr2->io);
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
-
- v->rangelow = RSF16_MINFREQ;
- v->rangehigh = RSF16_MAXFREQ;
- v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
- V4L2_TUNER_SUB_MONO;
- v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_STEREO;
- mutex_lock(&fmr2->lock);
- v->signal = fmr2_getsigstr(fmr2);
- mutex_unlock(&fmr2->lock);
- return 0;
+ return (bits & STR_DATA) ? TEA575X_DATA : 0 |
+ (bits & STR_MOST) ? TEA575X_MOST : 0;
}
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
+static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{
- return v->index ? -EINVAL : 0;
}
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct fmr2 *fmr2 = video_drvdata(file);
+static struct snd_tea575x_ops fmr2_tea_ops = {
+ .set_pins = fmr2_tea575x_set_pins,
+ .get_pins = fmr2_tea575x_get_pins,
+ .set_direction = fmr2_tea575x_set_direction,
+};
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- if (f->frequency < RSF16_MINFREQ ||
- f->frequency > RSF16_MAXFREQ)
- return -EINVAL;
- /* rounding in steps of 200 to match the freq
- that will be used */
- fmr2->curfreq = (f->frequency / 200) * 200;
-
- /* set card freq (if not muted) */
- if (fmr2->curvol && !fmr2->mute) {
- mutex_lock(&fmr2->lock);
- fmr2_setfreq(fmr2);
- mutex_unlock(&fmr2->lock);
+/* TC9154A/PT2254A volume control */
+
+/* 18-bit shift register bit definitions */
+#define TC9154A_ATT_MAJ_0DB (1 << 0)
+#define TC9154A_ATT_MAJ_10DB (1 << 1)
+#define TC9154A_ATT_MAJ_20DB (1 << 2)
+#define TC9154A_ATT_MAJ_30DB (1 << 3)
+#define TC9154A_ATT_MAJ_40DB (1 << 4)
+#define TC9154A_ATT_MAJ_50DB (1 << 5)
+#define TC9154A_ATT_MAJ_60DB (1 << 6)
+
+#define TC9154A_ATT_MIN_0DB (1 << 7)
+#define TC9154A_ATT_MIN_2DB (1 << 8)
+#define TC9154A_ATT_MIN_4DB (1 << 9)
+#define TC9154A_ATT_MIN_6DB (1 << 10)
+#define TC9154A_ATT_MIN_8DB (1 << 11)
+/* bit 12 is ignored */
+#define TC9154A_CHANNEL_LEFT (1 << 13)
+#define TC9154A_CHANNEL_RIGHT (1 << 14)
+/* bits 15, 16, 17 must be 0 */
+
+#define TC9154A_ATT_MAJ(x) (1 << x)
+#define TC9154A_ATT_MIN(x) (1 << (7 + x))
+
+static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
+{
+ if (!fmr2->tea.mute)
+ pins |= STR_WREN;
+
+ outb(pins, fmr2->io);
+}
+
+static void tc9154a_set_attenuation(struct fmr2 *fmr2, int att, u32 channel)
+{
+ int i;
+ u32 reg;
+ u8 bit;
+
+ reg = TC9154A_ATT_MAJ(att / 10) | TC9154A_ATT_MIN((att % 10) / 2);
+ reg |= channel;
+ /* write 18-bit shift register, LSB first */
+ for (i = 0; i < 18; i++) {
+ bit = reg & (1 << i) ? PT_DATA : 0;
+ tc9154a_set_pins(fmr2, bit);
+ udelay(5);
+ tc9154a_set_pins(fmr2, bit | PT_CK);
+ udelay(5);
+ tc9154a_set_pins(fmr2, bit);
}
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct fmr2 *fmr2 = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = fmr2->curfreq;
- return 0;
-}
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct fmr2 *fmr2 = video_drvdata(file);
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- /* Only card_type == 11 implements volume */
- if (fmr2->card_type == 11)
- return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- }
- return -EINVAL;
+ /* latch register data */
+ udelay(5);
+ tc9154a_set_pins(fmr2, PT_ST);
+ udelay(5);
+ tc9154a_set_pins(fmr2, 0);
}
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+static int fmr2_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct fmr2 *fmr2 = video_drvdata(file);
+ struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
+ struct fmr2 *fmr2 = tea->private_data;
+ int volume, balance, left, right;
switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = fmr2->mute;
- return 0;
case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = fmr2->curvol;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct fmr2 *fmr2 = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- fmr2->mute = ctrl->value;
+ volume = ctrl->val;
+ balance = fmr2->balance->cur.val;
break;
- case V4L2_CID_AUDIO_VOLUME:
- fmr2->curvol = ctrl->value;
+ case V4L2_CID_AUDIO_BALANCE:
+ balance = ctrl->val;
+ volume = fmr2->volume->cur.val;
break;
default:
return -EINVAL;
}
-#ifdef DEBUG
- if (fmr2->curvol && !fmr2->mute)
- printk(KERN_DEBUG "unmute\n");
- else
- printk(KERN_DEBUG "mute\n");
-#endif
-
- mutex_lock(&fmr2->lock);
- if (fmr2->curvol && !fmr2->mute) {
- fmr2_setvolume(fmr2);
- /* Set frequency and unmute card */
- fmr2_setfreq(fmr2);
- } else
- fmr2_mute(fmr2->io);
- mutex_unlock(&fmr2->lock);
- return 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
+ left = right = volume;
+ if (balance < 0)
+ right = max(0, right + balance);
+ if (balance > 0)
+ left = max(0, left - balance);
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
+ tc9154a_set_attenuation(fmr2, abs(left - 68), TC9154A_CHANNEL_LEFT);
+ tc9154a_set_attenuation(fmr2, abs(right - 68), TC9154A_CHANNEL_RIGHT);
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
return 0;
}
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
+static const struct v4l2_ctrl_ops fmr2_ctrl_ops = {
+ .s_ctrl = fmr2_s_ctrl,
+};
+
+static int fmr2_tea_ext_init(struct snd_tea575x *tea)
{
- return a->index ? -EINVAL : 0;
-}
+ struct fmr2 *fmr2 = tea->private_data;
-static const struct v4l2_file_operations fmr2_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
-};
+ if (inb(fmr2->io) & FMR2_HASVOL) {
+ fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56);
+ fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0);
+ if (tea->ctrl_handler.error) {
+ printk(KERN_ERR "radio-sf16fmr2: can't initialize contrls\n");
+ return tea->ctrl_handler.error;
+ }
+ }
-static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
-};
+ return 0;
+}
static int __init fmr2_init(void)
{
struct fmr2 *fmr2 = &fmr2_card;
- struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
- int res;
- strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
- fmr2->io = io;
- fmr2->stereo = 1;
- mutex_init(&fmr2->lock);
+ fmr2->io = FMR2_PORT;
- if (!request_region(fmr2->io, 2, "sf16fmr2")) {
- v4l2_err(v4l2_dev, "request_region failed!\n");
+ if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+ printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io);
return -EBUSY;
}
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(fmr2->io, 2);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
+ fmr2->tea.private_data = fmr2;
+ fmr2->tea.ops = &fmr2_tea_ops;
+ fmr2->tea.ext_init = fmr2_tea_ext_init;
+ strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
+ strcpy(fmr2->tea.bus_info, "ISA");
- strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
- fmr2->vdev.v4l2_dev = v4l2_dev;
- fmr2->vdev.fops = &fmr2_fops;
- fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
- fmr2->vdev.release = video_device_release_empty;
- video_set_drvdata(&fmr2->vdev, fmr2);
-
- /* mute card - prevents noisy bootups */
- fmr2_mute(fmr2->io);
- fmr2_product_info(fmr2);
-
- if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
+ if (snd_tea575x_init(&fmr2->tea)) {
+ printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n");
release_region(fmr2->io, 2);
- return -EINVAL;
+ return -ENODEV;
}
- v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
- debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+ printk(KERN_INFO "radio-sf16fmr2: SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
return 0;
}
@@ -443,22 +211,9 @@ static void __exit fmr2_exit(void)
{
struct fmr2 *fmr2 = &fmr2_card;
- video_unregister_device(&fmr2->vdev);
- v4l2_device_unregister(&fmr2->v4l2_dev);
+ snd_tea575x_exit(&fmr2->tea);
release_region(fmr2->io, 2);
}
module_init(fmr2_init);
module_exit(fmr2_exit);
-
-#ifndef MODULE
-
-static int __init fmr2_setup_io(char *str)
-{
- get_option(&str, &io);
- return 1;
-}
-
-__setup("sf16fmr2=", fmr2_setup_io);
-
-#endif
diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c
index 444b4cf7e65c..d1fab5885061 100644
--- a/drivers/media/radio/radio-si4713.c
+++ b/drivers/media/radio/radio-si4713.c
@@ -92,10 +92,6 @@ static int radio_si4713_s_audout(struct file *file, void *priv,
static int radio_si4713_querycap(struct file *file, void *priv,
struct v4l2_capability *capability)
{
- struct radio_si4713_device *rsdev;
-
- rsdev = video_get_drvdata(video_devdata(file));
-
strlcpy(capability->driver, "radio-si4713", sizeof(capability->driver));
strlcpy(capability->card, "Silicon Labs Si4713 Modulator",
sizeof(capability->card));
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index 0e71d816c725..db20904d01f0 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -39,10 +39,8 @@
#include <linux/i2c.h> /* I2C */
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
-#define DRIVER_VERSION "v0.01"
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 1)
+#define DRIVER_VERSION "0.0.2"
#define DRIVER_AUTHOR "Fabio Belavenuto <belavenuto@gmail.com>"
#define DRIVER_DESC "A driver for the TEA5764 radio chip for EZX Phones."
@@ -130,8 +128,10 @@ struct tea5764_write_regs {
u16 rdsbbl; /* PAUSEDET & RDSBBL */
} __attribute__ ((packed));
-#ifndef RADIO_TEA5764_XTAL
+#ifdef CONFIG_RADIO_TEA5764_XTAL
#define RADIO_TEA5764_XTAL 1
+#else
+#define RADIO_TEA5764_XTAL 0
#endif
static int radio_nr = -1;
@@ -300,7 +300,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->card, dev->name, sizeof(v->card));
snprintf(v->bus_info, sizeof(v->bus_info),
"I2C:%s", dev_name(&dev->dev));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
@@ -595,8 +594,9 @@ static void __exit tea5764_exit(void)
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
-module_param(use_xtal, int, 1);
+module_param(use_xtal, int, 0);
MODULE_PARM_DESC(use_xtal, "Chip have a xtal connected in board");
module_param(radio_nr, int, 0);
MODULE_PARM_DESC(radio_nr, "video4linux device number to use");
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
index a32663917059..f2ed9cc3cf3b 100644
--- a/drivers/media/radio/radio-terratec.c
+++ b/drivers/media/radio/radio-terratec.c
@@ -29,7 +29,6 @@
#include <linux/ioport.h> /* request_region */
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/mutex.h>
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -37,6 +36,7 @@
MODULE_AUTHOR("R.OFFERMANNS & others");
MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
#ifndef CONFIG_RADIO_TERRATEC_PORT
#define CONFIG_RADIO_TERRATEC_PORT 0x590
@@ -49,8 +49,6 @@ module_param(io, int, 0);
MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
module_param(radio_nr, int, 0);
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
static struct v4l2_queryctrl radio_qctrl[] = {
{
.id = V4L2_CID_AUDIO_MUTE,
@@ -205,7 +203,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-terratec", sizeof(v->driver));
strlcpy(v->card, "ActiveRadio", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
index a185610b376b..3e9209f84e09 100644
--- a/drivers/media/radio/radio-timb.c
+++ b/drivers/media/radio/radio-timb.c
@@ -16,7 +16,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/version.h>
#include <linux/io.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
@@ -24,6 +23,7 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/module.h>
#include <media/timb_radio.h>
#define DRIVER_NAME "timb-radio"
@@ -44,7 +44,6 @@ static int timbradio_vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, DRIVER_NAME, sizeof(v->driver));
strlcpy(v->card, "Timberdale Radio", sizeof(v->card));
snprintf(v->bus_info, sizeof(v->bus_info), "platform:"DRIVER_NAME);
- v->version = KERNEL_VERSION(0, 0, 1);
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
@@ -245,4 +244,5 @@ module_exit(timbradio_exit);
MODULE_DESCRIPTION("Timberdale Radio driver");
MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.0.2");
MODULE_ALIAS("platform:"DRIVER_NAME);
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
index 22fa9cc28abe..b3f45a019d82 100644
--- a/drivers/media/radio/radio-trust.c
+++ b/drivers/media/radio/radio-trust.c
@@ -19,7 +19,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/videodev2.h>
#include <linux/io.h>
#include <media/v4l2-device.h>
@@ -28,6 +27,7 @@
MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
@@ -42,8 +42,6 @@ module_param(io, int, 0);
MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
module_param(radio_nr, int, 0);
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
struct trust {
struct v4l2_device v4l2_dev;
struct video_device vdev;
@@ -196,7 +194,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-trust", sizeof(v->driver));
strlcpy(v->card, "Trust FM Radio", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
index 8dbbf08f2207..398726abc0c8 100644
--- a/drivers/media/radio/radio-typhoon.c
+++ b/drivers/media/radio/radio-typhoon.c
@@ -31,15 +31,17 @@
#include <linux/module.h> /* Modules */
#include <linux/init.h> /* Initdata */
#include <linux/ioport.h> /* request_region */
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#define DRIVER_VERSION "0.1.2"
+
MODULE_AUTHOR("Dr. Henrik Seidel");
MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
#ifndef CONFIG_RADIO_TYPHOON_PORT
#define CONFIG_RADIO_TYPHOON_PORT -1
@@ -61,9 +63,7 @@ static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ;
module_param(mutefreq, ulong, 0);
MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
-#define RADIO_VERSION KERNEL_VERSION(0, 1, 1)
-
-#define BANNER "Typhoon Radio Card driver v0.1.1\n"
+#define BANNER "Typhoon Radio Card driver v" DRIVER_VERSION "\n"
struct typhoon {
struct v4l2_device v4l2_dev;
@@ -171,7 +171,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index 459f7272d326..8aa4968d57bc 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -23,6 +23,7 @@
#include <linux/interrupt.h>
#include <linux/mfd/wl1273-core.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -1382,7 +1383,7 @@ static int wl1273_fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
- ctrl->cur.val = wl1273_fm_get_tx_ctune(radio);
+ ctrl->val = wl1273_fm_get_tx_ctune(radio);
break;
default:
@@ -2109,7 +2110,7 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev)
V4L2_CID_TUNE_ANTENNA_CAPACITOR,
0, 255, 1, 255);
if (ctrl)
- ctrl->is_volatile = 1;
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
if (radio->ctrl_handler.error) {
r = radio->ctrl_handler.error;
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
index af99c5bd88c1..f5613b948203 100644
--- a/drivers/media/radio/radio-zoltrix.c
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -35,7 +35,6 @@
#include <linux/delay.h> /* udelay, msleep */
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/mutex.h>
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -43,6 +42,7 @@
MODULE_AUTHOR("C.van Schaik");
MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
#ifndef CONFIG_RADIO_ZOLTRIX_PORT
#define CONFIG_RADIO_ZOLTRIX_PORT -1
@@ -55,8 +55,6 @@ module_param(io, int, 0);
MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
module_param(radio_nr, int, 0);
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
struct zoltrix {
struct v4l2_device v4l2_dev;
struct video_device vdev;
@@ -228,7 +226,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver));
strlcpy(v->card, "Zoltrix Radio", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->version = RADIO_VERSION;
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index a2a67772c42c..fd3541b0e91c 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -24,10 +24,9 @@
/* driver definitions */
#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>";
-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 1)
#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers"
-#define DRIVER_VERSION "1.0.1"
+#define DRIVER_VERSION "1.0.2"
/* kernel includes */
#include <linux/i2c.h>
@@ -248,7 +247,6 @@ int si470x_vidioc_querycap(struct file *file, void *priv,
{
strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
- capability->version = DRIVER_KERNEL_VERSION;
capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
V4L2_CAP_TUNER | V4L2_CAP_RADIO;
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index 392e84fe90ef..a6ad707fae96 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -29,7 +29,6 @@
/* driver definitions */
#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10)
#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
#define DRIVER_VERSION "1.0.10"
@@ -396,7 +395,6 @@ int si470x_disconnect_check(struct si470x_device *radio)
static void si470x_int_in_callback(struct urb *urb)
{
struct si470x_device *radio = urb->context;
- unsigned char buf[RDS_REPORT_SIZE];
int retval;
unsigned char regnr;
unsigned char blocknum;
@@ -424,7 +422,6 @@ static void si470x_int_in_callback(struct urb *urb)
if (urb->actual_length > 0) {
/* Update RDS registers with URB data */
- buf[0] = RDS_REPORT;
for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
radio->registers[STATUSRSSI + regnr] =
get_unaligned_be16(&radio->int_in_buffer[
@@ -626,7 +623,6 @@ int si470x_vidioc_querycap(struct file *file, void *priv,
strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
usb_make_path(radio->usbdev, capability->bus_info,
sizeof(capability->bus_info));
- capability->version = DRIVER_KERNEL_VERSION;
capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
@@ -699,7 +695,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
radio->videodev = video_device_alloc();
if (!radio->videodev) {
retval = -ENOMEM;
- goto err_intbuffer;
+ goto err_urb;
}
memcpy(radio->videodev, &si470x_viddev_template,
sizeof(si470x_viddev_template));
@@ -790,6 +786,8 @@ err_all:
kfree(radio->buffer);
err_video:
video_device_release(radio->videodev);
+err_urb:
+ usb_free_urb(radio->int_in_urb);
err_intbuffer:
kfree(radio->int_in_buffer);
err_radio:
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index 68da001b09dc..f300a55ed85c 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -32,7 +32,6 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/input.h>
-#include <linux/version.h>
#include <linux/videodev2.h>
#include <linux/mutex.h>
#include <media/v4l2-common.h>
diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c
index deca2e06ff22..27aba936fb2b 100644
--- a/drivers/media/radio/si4713-i2c.c
+++ b/drivers/media/radio/si4713-i2c.c
@@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/module.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
@@ -1033,7 +1034,7 @@ static int si4713_write_econtrol_string(struct si4713_device *sdev,
char ps_name[MAX_RDS_PS_NAME + 1];
len = control->size - 1;
- if (len > MAX_RDS_PS_NAME) {
+ if (len < 0 || len > MAX_RDS_PS_NAME) {
rval = -ERANGE;
goto exit;
}
@@ -1057,7 +1058,7 @@ static int si4713_write_econtrol_string(struct si4713_device *sdev,
char radio_text[MAX_RDS_RADIO_TEXT + 1];
len = control->size - 1;
- if (len > MAX_RDS_RADIO_TEXT) {
+ if (len < 0 || len > MAX_RDS_RADIO_TEXT) {
rval = -ERANGE;
goto exit;
}
diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h
index 1a45a5d847b0..d84ad9dad323 100644
--- a/drivers/media/radio/wl128x/fmdrv.h
+++ b/drivers/media/radio/wl128x/fmdrv.h
@@ -28,14 +28,11 @@
#include <sound/core.h>
#include <sound/initval.h>
#include <linux/timer.h>
-#include <linux/version.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
-#define FM_DRV_VERSION "0.10"
-/* Should match with FM_DRV_VERSION */
-#define FM_DRV_RADIO_VERSION KERNEL_VERSION(0, 0, 1)
+#define FM_DRV_VERSION "0.1.1"
#define FM_DRV_NAME "ti_fmdrv"
#define FM_DRV_CARD_SHORT_NAME "TI FM Radio"
#define FM_DRV_CARD_LONG_NAME "Texas Instruments FM Radio"
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
index 87010724f914..4f5c43d2566c 100644
--- a/drivers/media/radio/wl128x/fmdrv_v4l2.c
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -28,6 +28,8 @@
*
*/
+#include <linux/export.h>
+
#include "fmdrv.h"
#include "fmdrv_v4l2.h"
#include "fmdrv_common.h"
@@ -84,12 +86,14 @@ static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf,
ret = copy_from_user(&rds, buf, sizeof(rds));
fmdbg("(%d)type: %d, text %s, af %d\n",
ret, rds.text_type, rds.text, rds.af_freq);
+ if (ret)
+ return -EFAULT;
fmdev = video_drvdata(file);
fm_tx_set_radio_text(fmdev, rds.text, rds.text_type);
fm_tx_set_af(fmdev, rds.af_freq);
- return 0;
+ return sizeof(rds);
}
static u32 fm_v4l2_fops_poll(struct file *file, struct poll_table_struct *pts)
@@ -175,7 +179,6 @@ static int fm_v4l2_vidioc_querycap(struct file *file, void *priv,
strlcpy(capability->card, FM_DRV_CARD_SHORT_NAME,
sizeof(capability->card));
sprintf(capability->bus_info, "UART");
- capability->version = FM_DRV_RADIO_VERSION;
capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
V4L2_CAP_RADIO | V4L2_CAP_MODULATOR |
V4L2_CAP_AUDIO | V4L2_CAP_READWRITE |
@@ -191,7 +194,7 @@ static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
- ctrl->cur.val = fm_tx_get_tune_cap_val(fmdev);
+ ctrl->val = fm_tx_get_tune_cap_val(fmdev);
break;
default:
fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id);
@@ -403,7 +406,7 @@ static int fm_v4l2_vidioc_s_hw_freq_seek(struct file *file, void *priv,
static int fm_v4l2_vidioc_g_modulator(struct file *file, void *priv,
struct v4l2_modulator *mod)
{
- struct fmdev *fmdev = video_drvdata(file);;
+ struct fmdev *fmdev = video_drvdata(file);
if (mod->index != 0)
return -EINVAL;
@@ -558,7 +561,7 @@ int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr)
255, 1, 255);
if (ctrl)
- ctrl->is_volatile = 1;
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
return 0;
}
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 7d4bbc226d06..aeb7f43dfb65 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -4,8 +4,8 @@ menuconfig RC_CORE
default INPUT
---help---
Enable support for Remote Controllers on Linux. This is
- needed in order to support several video capture adapters.
- Currently, all supported devices use InfraRed.
+ needed in order to support several video capture adapters,
+ standalone IR receivers/transmitters, and RF receivers.
Enable this option if you have a video capture board even
if you don't need IR, as otherwise, you may not be able to
@@ -87,6 +87,17 @@ config IR_RC5_SZ_DECODER
uses an IR protocol that is almost standard RC-5, but not quite,
as it uses an additional bit).
+config IR_MCE_KBD_DECODER
+ tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol"
+ depends on RC_CORE
+ select BITREVERSE
+ default y
+
+ ---help---
+ Enable this option if you have a Microsoft Remote Keyboard for
+ Windows Media Center Edition, which you would like to use with
+ a raw IR receiver in your system.
+
config IR_LIRC_CODEC
tristate "Enable IR to LIRC bridge"
depends on RC_CORE
@@ -97,6 +108,25 @@ config IR_LIRC_CODEC
Enable this option to pass raw IR to and from userspace via
the LIRC interface.
+config RC_ATI_REMOTE
+ tristate "ATI / X10 based USB RF remote controls"
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ select USB
+ help
+ Say Y here if you want to use an X10 based USB remote control.
+ These are RF remotes with USB receivers.
+
+ Such devices include the ATI remote that comes with many of ATI's
+ All-In-Wonder video cards, the X10 "Lola" remote, NVIDIA RF remote,
+ Medion RF remote, and SnapStream FireFly remote.
+
+ This driver provides mouse pointer, left and right mouse buttons,
+ and maps all the other remote buttons to keypress events.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ati_remote.
+
config IR_ENE
tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)"
depends on PNP
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 52830e5f4eaa..2156e786b557 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -10,9 +10,11 @@ obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o
obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o
obj-$(CONFIG_IR_RC5_SZ_DECODER) += ir-rc5-sz-decoder.o
+obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o
obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
# stand-alone IR receivers/transmitters
+obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_IR_IMON) += imon.o
obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
diff --git a/drivers/input/misc/ati_remote.c b/drivers/media/rc/ati_remote.c
index bce57129afba..303f22ea04c0 100644
--- a/drivers/input/misc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -1,6 +1,7 @@
/*
* USB ATI Remote support
*
+ * Copyright (c) 2011 Anssi Hannula <anssi.hannula@iki.fi>
* Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
* Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
*
@@ -90,9 +91,11 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/usb/input.h>
#include <linux/wait.h>
#include <linux/jiffies.h>
+#include <media/rc-core.h>
/*
* Module and Version Information, Module Parameters
@@ -104,8 +107,9 @@
#define ATI_REMOTE_PRODUCT_ID 0x0004
#define NVIDIA_REMOTE_PRODUCT_ID 0x0005
#define MEDION_REMOTE_PRODUCT_ID 0x0006
+#define FIREFLY_REMOTE_PRODUCT_ID 0x0008
-#define DRIVER_VERSION "2.2.1"
+#define DRIVER_VERSION "2.2.1"
#define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>"
#define DRIVER_DESC "ATI/X10 RF USB Remote Control"
@@ -139,16 +143,21 @@ static int repeat_delay = REPEAT_DELAY;
module_param(repeat_delay, int, 0644);
MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec");
+static bool mouse = true;
+module_param(mouse, bool, 0444);
+MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes");
+
#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
#undef err
#define err(format, arg...) printk(KERN_ERR format , ## arg)
static struct usb_device_id ati_remote_table[] = {
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID) },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID) },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_MEDION_X10 },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_SNAPSTREAM_FIREFLY },
{} /* Terminating entry */
};
@@ -167,6 +176,7 @@ static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
struct ati_remote {
struct input_dev *idev;
+ struct rc_dev *rdev;
struct usb_device *udev;
struct usb_interface *interface;
@@ -186,11 +196,16 @@ struct ati_remote {
unsigned int repeat_count;
- char name[NAME_BUFSIZE];
- char phys[NAME_BUFSIZE];
+ char rc_name[NAME_BUFSIZE];
+ char rc_phys[NAME_BUFSIZE];
+ char mouse_name[NAME_BUFSIZE];
+ char mouse_phys[NAME_BUFSIZE];
wait_queue_head_t wait;
int send_flags;
+
+ int users; /* 0-2, users are rc and input */
+ struct mutex open_mutex;
};
/* "Kinds" of messages sent from the hardware to the driver. */
@@ -233,64 +248,11 @@ static const struct {
{KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
{KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
- /* keyboard. */
- {KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
- {KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
- {KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
- {KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
- {KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
- {KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
- {KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
- {KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
- {KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
- {KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
- {KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
- {KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
- {KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
- {KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
- {KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
- {KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
-
- /* "special" keys */
- {KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* "check" */
- {KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* "menu" */
- {KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* Power */
- {KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_TV, 1}, /* TV */
- {KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_DVD, 1}, /* DVD */
- {KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* WEB */
- {KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* "book" */
- {KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* "hand" */
- {KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* "timer" */
- {KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* "max" */
- {KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* left */
- {KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* right */
- {KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* down */
- {KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* up */
- {KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_OK, 1}, /* "OK" */
- {KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */
- {KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1}, /* VOL - */
- {KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1}, /* MUTE */
- {KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELUP, 1}, /* CH + */
- {KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */
- {KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1}, /* ( o) red */
- {KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */
- {KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */
- {KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */
- {KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */
- {KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */
- {KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */
- {KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */
- {KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */
- {KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */
- {KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */
- {KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */
-
+ /* Non-mouse events are handled by rc-core */
{KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0}
};
/* Local function prototypes */
-static int ati_remote_open (struct input_dev *inputdev);
-static void ati_remote_close (struct input_dev *inputdev);
static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
static void ati_remote_irq_out (struct urb *urb);
static void ati_remote_irq_in (struct urb *urb);
@@ -313,9 +275,10 @@ static struct usb_driver ati_remote_driver = {
static void ati_remote_dump(struct device *dev, unsigned char *data,
unsigned int len)
{
- if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00))
- dev_warn(dev, "Weird byte 0x%02x\n", data[0]);
- else if (len == 4)
+ if (len == 1) {
+ if (data[0] != (unsigned char)0xff && data[0] != 0x00)
+ dev_warn(dev, "Weird byte 0x%02x\n", data[0]);
+ } else if (len == 4)
dev_warn(dev, "Weird key %02x %02x %02x %02x\n",
data[0], data[1], data[2], data[3]);
else
@@ -326,29 +289,60 @@ static void ati_remote_dump(struct device *dev, unsigned char *data,
/*
* ati_remote_open
*/
-static int ati_remote_open(struct input_dev *inputdev)
+static int ati_remote_open(struct ati_remote *ati_remote)
{
- struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+ int err = 0;
+
+ mutex_lock(&ati_remote->open_mutex);
+
+ if (ati_remote->users++ != 0)
+ goto out; /* one was already active */
/* On first open, submit the read urb which was set up previously. */
ati_remote->irq_urb->dev = ati_remote->udev;
if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
dev_err(&ati_remote->interface->dev,
"%s: usb_submit_urb failed!\n", __func__);
- return -EIO;
+ err = -EIO;
}
- return 0;
+out: mutex_unlock(&ati_remote->open_mutex);
+ return err;
}
/*
* ati_remote_close
*/
-static void ati_remote_close(struct input_dev *inputdev)
+static void ati_remote_close(struct ati_remote *ati_remote)
+{
+ mutex_lock(&ati_remote->open_mutex);
+ if (--ati_remote->users == 0)
+ usb_kill_urb(ati_remote->irq_urb);
+ mutex_unlock(&ati_remote->open_mutex);
+}
+
+static int ati_remote_input_open(struct input_dev *inputdev)
{
struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+ return ati_remote_open(ati_remote);
+}
- usb_kill_urb(ati_remote->irq_urb);
+static void ati_remote_input_close(struct input_dev *inputdev)
+{
+ struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+ ati_remote_close(ati_remote);
+}
+
+static int ati_remote_rc_open(struct rc_dev *rdev)
+{
+ struct ati_remote *ati_remote = rdev->priv;
+ return ati_remote_open(ati_remote);
+}
+
+static void ati_remote_rc_close(struct rc_dev *rdev)
+{
+ struct ati_remote *ati_remote = rdev->priv;
+ ati_remote_close(ati_remote);
}
/*
@@ -413,10 +407,8 @@ static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2)
/*
* Decide if the table entry matches the remote input.
*/
- if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) &&
- ((((ati_remote_tbl[i].data1 >> 4) -
- (d1 >> 4) + rem) & 0x0f) == 0x0f) &&
- (ati_remote_tbl[i].data2 == d2))
+ if (ati_remote_tbl[i].data1 == d1 &&
+ ati_remote_tbl[i].data2 == d2)
return i;
}
@@ -468,8 +460,10 @@ static void ati_remote_input_report(struct urb *urb)
struct ati_remote *ati_remote = urb->context;
unsigned char *data= ati_remote->inbuf;
struct input_dev *dev = ati_remote->idev;
- int index, acc;
+ int index = -1;
+ int acc;
int remote_num;
+ unsigned char scancode[2];
/* Deal with strange looking inputs */
if ( (urb->actual_length != 4) || (data[0] != 0x14) ||
@@ -481,26 +475,41 @@ static void ati_remote_input_report(struct urb *urb)
/* Mask unwanted remote channels. */
/* note: remote_num is 0-based, channel 1 on remote == 0 here */
remote_num = (data[3] >> 4) & 0x0f;
- if (channel_mask & (1 << (remote_num + 1))) {
+ if (channel_mask & (1 << (remote_num + 1))) {
dbginfo(&ati_remote->interface->dev,
"Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
remote_num, data[1], data[2], channel_mask);
return;
}
- /* Look up event code index in translation table */
- index = ati_remote_event_lookup(remote_num, data[1], data[2]);
- if (index < 0) {
- dev_warn(&ati_remote->interface->dev,
- "Unknown input from channel 0x%02x: data %02x,%02x\n",
- remote_num, data[1], data[2]);
- return;
- }
- dbginfo(&ati_remote->interface->dev,
- "channel 0x%02x; data %02x,%02x; index %d; keycode %d\n",
- remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
+ scancode[0] = (((data[1] - ((remote_num + 1) << 4)) & 0xf0) | (data[1] & 0x0f));
- if (ati_remote_tbl[index].kind == KIND_LITERAL) {
+ /*
+ * Some devices (e.g. SnapStream Firefly) use 8080 as toggle code,
+ * so we have to clear them. The first bit is a bit tricky as the
+ * "non-toggled" state depends on remote_num, so we xor it with the
+ * second bit which is only used for toggle.
+ */
+ scancode[0] ^= (data[2] & 0x80);
+
+ scancode[1] = data[2] & ~0x80;
+
+ /* Look up event code index in mouse translation table. */
+ index = ati_remote_event_lookup(remote_num, scancode[0], scancode[1]);
+
+ if (index >= 0) {
+ dbginfo(&ati_remote->interface->dev,
+ "channel 0x%02x; mouse data %02x,%02x; index %d; keycode %d\n",
+ remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
+ if (!dev)
+ return; /* no mouse device */
+ } else
+ dbginfo(&ati_remote->interface->dev,
+ "channel 0x%02x; key data %02x,%02x, scancode %02x,%02x\n",
+ remote_num, data[1], data[2], scancode[0], scancode[1]);
+
+
+ if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) {
input_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code,
ati_remote_tbl[index].value);
@@ -510,7 +519,7 @@ static void ati_remote_input_report(struct urb *urb)
return;
}
- if (ati_remote_tbl[index].kind == KIND_FILTERED) {
+ if (index < 0 || ati_remote_tbl[index].kind == KIND_FILTERED) {
unsigned long now = jiffies;
/* Filter duplicate events which happen "too close" together. */
@@ -538,6 +547,20 @@ static void ati_remote_input_report(struct urb *urb)
msecs_to_jiffies(repeat_delay))))
return;
+ if (index < 0) {
+ /* Not a mouse event, hand it to rc-core. */
+ u32 rc_code = (scancode[0] << 8) | scancode[1];
+
+ /*
+ * We don't use the rc-core repeat handling yet as
+ * it would cause ghost repeats which would be a
+ * regression for this driver.
+ */
+ rc_keydown_notimeout(ati_remote->rdev, rc_code,
+ data[2]);
+ rc_keyup(ati_remote->rdev);
+ return;
+ }
input_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code, 1);
@@ -630,7 +653,7 @@ static int ati_remote_alloc_buffers(struct usb_device *udev,
return -1;
ati_remote->outbuf = usb_alloc_coherent(udev, DATA_BUFSIZE, GFP_ATOMIC,
- &ati_remote->outbuf_dma);
+ &ati_remote->outbuf_dma);
if (!ati_remote->outbuf)
return -1;
@@ -675,14 +698,33 @@ static void ati_remote_input_init(struct ati_remote *ati_remote)
input_set_drvdata(idev, ati_remote);
- idev->open = ati_remote_open;
- idev->close = ati_remote_close;
+ idev->open = ati_remote_input_open;
+ idev->close = ati_remote_input_close;
- idev->name = ati_remote->name;
- idev->phys = ati_remote->phys;
+ idev->name = ati_remote->mouse_name;
+ idev->phys = ati_remote->mouse_phys;
usb_to_input_id(ati_remote->udev, &idev->id);
- idev->dev.parent = &ati_remote->udev->dev;
+ idev->dev.parent = &ati_remote->interface->dev;
+}
+
+static void ati_remote_rc_init(struct ati_remote *ati_remote)
+{
+ struct rc_dev *rdev = ati_remote->rdev;
+
+ rdev->priv = ati_remote;
+ rdev->driver_type = RC_DRIVER_SCANCODE;
+ rdev->allowed_protos = RC_TYPE_OTHER;
+ rdev->driver_name = "ati_remote";
+
+ rdev->open = ati_remote_rc_open;
+ rdev->close = ati_remote_rc_close;
+
+ rdev->input_name = ati_remote->rc_name;
+ rdev->input_phys = ati_remote->rc_phys;
+
+ usb_to_input_id(ati_remote->udev, &rdev->input_id);
+ rdev->dev.parent = &ati_remote->interface->dev;
}
static int ati_remote_initialize(struct ati_remote *ati_remote)
@@ -735,6 +777,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
struct ati_remote *ati_remote;
struct input_dev *input_dev;
+ struct rc_dev *rc_dev;
int err = -ENOMEM;
if (iface_host->desc.bNumEndpoints != 2) {
@@ -755,8 +798,8 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
}
ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!ati_remote || !input_dev)
+ rc_dev = rc_allocate_device();
+ if (!ati_remote || !rc_dev)
goto fail1;
/* Allocate URB buffers, URBs */
@@ -766,44 +809,78 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
ati_remote->endpoint_in = endpoint_in;
ati_remote->endpoint_out = endpoint_out;
ati_remote->udev = udev;
- ati_remote->idev = input_dev;
+ ati_remote->rdev = rc_dev;
ati_remote->interface = interface;
- usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys));
- strlcat(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
+ usb_make_path(udev, ati_remote->rc_phys, sizeof(ati_remote->rc_phys));
+ strlcpy(ati_remote->mouse_phys, ati_remote->rc_phys,
+ sizeof(ati_remote->mouse_phys));
+
+ strlcat(ati_remote->rc_phys, "/input0", sizeof(ati_remote->rc_phys));
+ strlcat(ati_remote->mouse_phys, "/input1", sizeof(ati_remote->mouse_phys));
if (udev->manufacturer)
- strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name));
+ strlcpy(ati_remote->rc_name, udev->manufacturer,
+ sizeof(ati_remote->rc_name));
if (udev->product)
- snprintf(ati_remote->name, sizeof(ati_remote->name),
- "%s %s", ati_remote->name, udev->product);
+ snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name),
+ "%s %s", ati_remote->rc_name, udev->product);
- if (!strlen(ati_remote->name))
- snprintf(ati_remote->name, sizeof(ati_remote->name),
+ if (!strlen(ati_remote->rc_name))
+ snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name),
DRIVER_DESC "(%04x,%04x)",
le16_to_cpu(ati_remote->udev->descriptor.idVendor),
le16_to_cpu(ati_remote->udev->descriptor.idProduct));
- ati_remote_input_init(ati_remote);
+ snprintf(ati_remote->mouse_name, sizeof(ati_remote->mouse_name),
+ "%s mouse", ati_remote->rc_name);
+
+ if (id->driver_info)
+ rc_dev->map_name = (const char *)id->driver_info;
+ else
+ rc_dev->map_name = RC_MAP_ATI_X10;
+
+ ati_remote_rc_init(ati_remote);
+ mutex_init(&ati_remote->open_mutex);
/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
err = ati_remote_initialize(ati_remote);
if (err)
goto fail3;
- /* Set up and register input device */
- err = input_register_device(ati_remote->idev);
+ /* Set up and register rc device */
+ err = rc_register_device(ati_remote->rdev);
if (err)
goto fail3;
+ /* use our delay for rc_dev */
+ ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay;
+
+ /* Set up and register mouse input device */
+ if (mouse) {
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail4;
+
+ ati_remote->idev = input_dev;
+ ati_remote_input_init(ati_remote);
+ err = input_register_device(input_dev);
+
+ if (err)
+ goto fail5;
+ }
+
usb_set_intfdata(interface, ati_remote);
return 0;
+ fail5: input_free_device(input_dev);
+ fail4: rc_unregister_device(rc_dev);
+ rc_dev = NULL;
fail3: usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
fail2: ati_remote_free_buffers(ati_remote);
- fail1: input_free_device(input_dev);
+ fail1: rc_free_device(rc_dev);
kfree(ati_remote);
return err;
}
@@ -824,7 +901,9 @@ static void ati_remote_disconnect(struct usb_interface *interface)
usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
- input_unregister_device(ati_remote->idev);
+ if (ati_remote->idev)
+ input_unregister_device(ati_remote->idev);
+ rc_unregister_device(ati_remote->rdev);
ati_remote_free_buffers(ati_remote);
kfree(ati_remote);
}
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index a43ed6c41bfc..cf10ecf5acec 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -30,6 +30,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pnp.h>
@@ -118,31 +120,31 @@ static int ene_hw_detect(struct ene_device *dev)
dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 2 : 4;
if (hw_revision == 0xFF) {
- ene_warn("device seems to be disabled");
- ene_warn("send a mail to lirc-list@lists.sourceforge.net");
- ene_warn("please attach output of acpidump and dmidecode");
+ pr_warn("device seems to be disabled\n");
+ pr_warn("send a mail to lirc-list@lists.sourceforge.net\n");
+ pr_warn("please attach output of acpidump and dmidecode\n");
return -ENODEV;
}
- ene_notice("chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x",
- chip_major, chip_minor, old_ver, hw_revision);
+ pr_notice("chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x\n",
+ chip_major, chip_minor, old_ver, hw_revision);
- ene_notice("PLL freq = %d", dev->pll_freq);
+ pr_notice("PLL freq = %d\n", dev->pll_freq);
if (chip_major == 0x33) {
- ene_warn("chips 0x33xx aren't supported");
+ pr_warn("chips 0x33xx aren't supported\n");
return -ENODEV;
}
if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_C;
- ene_notice("KB3926C detected");
+ pr_notice("KB3926C detected\n");
} else if (old_ver == 0x24 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_B;
- ene_notice("KB3926B detected");
+ pr_notice("KB3926B detected\n");
} else {
dev->hw_revision = ENE_HW_D;
- ene_notice("KB3926D or higher detected");
+ pr_notice("KB3926D or higher detected\n");
}
/* detect features hardware supports */
@@ -152,7 +154,7 @@ static int ene_hw_detect(struct ene_device *dev)
fw_reg1 = ene_read_reg(dev, ENE_FW1);
fw_reg2 = ene_read_reg(dev, ENE_FW2);
- ene_notice("Firmware regs: %02x %02x", fw_reg1, fw_reg2);
+ pr_notice("Firmware regs: %02x %02x\n", fw_reg1, fw_reg2);
dev->hw_use_gpio_0a = !!(fw_reg2 & ENE_FW2_GP0A);
dev->hw_learning_and_tx_capable = !!(fw_reg2 & ENE_FW2_LEARNING);
@@ -161,30 +163,29 @@ static int ene_hw_detect(struct ene_device *dev)
if (dev->hw_learning_and_tx_capable)
dev->hw_fan_input = !!(fw_reg2 & ENE_FW2_FAN_INPUT);
- ene_notice("Hardware features:");
+ pr_notice("Hardware features:\n");
if (dev->hw_learning_and_tx_capable) {
- ene_notice("* Supports transmitting & learning mode");
- ene_notice(" This feature is rare and therefore,");
- ene_notice(" you are welcome to test it,");
- ene_notice(" and/or contact the author via:");
- ene_notice(" lirc-list@lists.sourceforge.net");
- ene_notice(" or maximlevitsky@gmail.com");
+ pr_notice("* Supports transmitting & learning mode\n");
+ pr_notice(" This feature is rare and therefore,\n");
+ pr_notice(" you are welcome to test it,\n");
+ pr_notice(" and/or contact the author via:\n");
+ pr_notice(" lirc-list@lists.sourceforge.net\n");
+ pr_notice(" or maximlevitsky@gmail.com\n");
- ene_notice("* Uses GPIO %s for IR raw input",
- dev->hw_use_gpio_0a ? "40" : "0A");
+ pr_notice("* Uses GPIO %s for IR raw input\n",
+ dev->hw_use_gpio_0a ? "40" : "0A");
if (dev->hw_fan_input)
- ene_notice("* Uses unused fan feedback input as source"
- " of demodulated IR data");
+ pr_notice("* Uses unused fan feedback input as source of demodulated IR data\n");
}
if (!dev->hw_fan_input)
- ene_notice("* Uses GPIO %s for IR demodulated input",
- dev->hw_use_gpio_0a ? "0A" : "40");
+ pr_notice("* Uses GPIO %s for IR demodulated input\n",
+ dev->hw_use_gpio_0a ? "0A" : "40");
if (dev->hw_extra_buffer)
- ene_notice("* Uses new style input buffer");
+ pr_notice("* Uses new style input buffer\n");
return 0;
}
@@ -215,13 +216,13 @@ static void ene_rx_setup_hw_buffer(struct ene_device *dev)
dev->buffer_len = dev->extra_buf1_len + dev->extra_buf2_len + 8;
- ene_notice("Hardware uses 2 extended buffers:");
- ene_notice(" 0x%04x - len : %d", dev->extra_buf1_address,
- dev->extra_buf1_len);
- ene_notice(" 0x%04x - len : %d", dev->extra_buf2_address,
- dev->extra_buf2_len);
+ pr_notice("Hardware uses 2 extended buffers:\n");
+ pr_notice(" 0x%04x - len : %d\n",
+ dev->extra_buf1_address, dev->extra_buf1_len);
+ pr_notice(" 0x%04x - len : %d\n",
+ dev->extra_buf2_address, dev->extra_buf2_len);
- ene_notice("Total buffer len = %d", dev->buffer_len);
+ pr_notice("Total buffer len = %d\n", dev->buffer_len);
if (dev->buffer_len > 64 || dev->buffer_len < 16)
goto error;
@@ -240,7 +241,7 @@ static void ene_rx_setup_hw_buffer(struct ene_device *dev)
ene_set_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
return;
error:
- ene_warn("Error validating extra buffers, device probably won't work");
+ pr_warn("Error validating extra buffers, device probably won't work\n");
dev->hw_extra_buffer = false;
ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
}
@@ -588,7 +589,7 @@ static void ene_tx_enable(struct ene_device *dev)
dbg("TX: Transmitter #2 is connected");
if (!(fwreg2 & (ENE_FW2_EMMITER1_CONN | ENE_FW2_EMMITER2_CONN)))
- ene_warn("TX: transmitter cable isn't connected!");
+ pr_warn("TX: transmitter cable isn't connected!\n");
/* disable receive on revc */
if (dev->hw_revision == ENE_HW_C)
@@ -615,7 +616,7 @@ static void ene_tx_sample(struct ene_device *dev)
bool pulse = dev->tx_sample_pulse;
if (!dev->tx_buffer) {
- ene_warn("TX: BUG: attempt to transmit NULL buffer");
+ pr_warn("TX: BUG: attempt to transmit NULL buffer\n");
return;
}
@@ -953,13 +954,13 @@ static void ene_set_idle(struct rc_dev *rdev, bool idle)
}
/* outside interface: transmit */
-static int ene_transmit(struct rc_dev *rdev, int *buf, u32 n)
+static int ene_transmit(struct rc_dev *rdev, unsigned *buf, unsigned n)
{
struct ene_device *dev = rdev->priv;
unsigned long flags;
dev->tx_buffer = buf;
- dev->tx_len = n / sizeof(int);
+ dev->tx_len = n;
dev->tx_pos = 0;
dev->tx_reg = 0;
dev->tx_done = 0;
@@ -1049,7 +1050,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
dev->hw_learning_and_tx_capable = true;
setup_timer(&dev->tx_sim_timer, ene_tx_irqsim,
(long unsigned int)dev);
- ene_warn("Simulation of TX activated");
+ pr_warn("Simulation of TX activated\n");
}
if (!dev->hw_learning_and_tx_capable)
@@ -1089,7 +1090,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
if (error < 0)
goto error;
- ene_notice("driver has been successfully loaded");
+ pr_notice("driver has been successfully loaded\n");
return 0;
error:
if (dev && dev->irq >= 0)
diff --git a/drivers/media/rc/ene_ir.h b/drivers/media/rc/ene_ir.h
index 337a41d4450b..fd108d90f750 100644
--- a/drivers/media/rc/ene_ir.h
+++ b/drivers/media/rc/ene_ir.h
@@ -182,20 +182,11 @@
#define ENE_HW_C 2 /* 3926C */
#define ENE_HW_D 3 /* 3926D or later */
-#define ene_printk(level, text, ...) \
- printk(level ENE_DRIVER_NAME ": " text "\n", ## __VA_ARGS__)
-
-#define ene_notice(text, ...) ene_printk(KERN_NOTICE, text, ## __VA_ARGS__)
-#define ene_warn(text, ...) ene_printk(KERN_WARNING, text, ## __VA_ARGS__)
-
-
-#define __dbg(level, format, ...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG ENE_DRIVER_NAME \
- ": " format "\n", ## __VA_ARGS__); \
- } while (0)
-
+#define __dbg(level, format, ...) \
+do { \
+ if (debug >= level) \
+ pr_debug(format "\n", ## __VA_ARGS__); \
+} while (0)
#define dbg(format, ...) __dbg(1, format, ## __VA_ARGS__)
#define dbg_verbose(format, ...) __dbg(2, format, ## __VA_ARGS__)
@@ -235,7 +226,7 @@ struct ene_device {
bool tx_sample_pulse; /* current sample is pulse */
/* TX buffer */
- int *tx_buffer; /* input samples buffer*/
+ unsigned *tx_buffer; /* input samples buffer*/
int tx_pos; /* position in that bufer */
int tx_len; /* current len of tx buffer */
int tx_done; /* done transmitting */
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 6bc35eeb653b..6ed96465137a 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -34,6 +34,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
#include <linux/input.h>
#include <linux/usb.h>
@@ -516,19 +517,19 @@ static int send_packet(struct imon_context *ictx)
if (retval) {
ictx->tx.busy = false;
smp_rmb(); /* ensure later readers know we're not busy */
- pr_err("error submitting urb(%d)\n", retval);
+ pr_err_ratelimited("error submitting urb(%d)\n", retval);
} else {
/* Wait for transmission to complete (or abort) */
mutex_unlock(&ictx->lock);
retval = wait_for_completion_interruptible(
&ictx->tx.finished);
if (retval)
- pr_err("task interrupted\n");
+ pr_err_ratelimited("task interrupted\n");
mutex_lock(&ictx->lock);
retval = ictx->tx.status;
if (retval)
- pr_err("packet tx failed (%d)\n", retval);
+ pr_err_ratelimited("packet tx failed (%d)\n", retval);
}
kfree(control_req);
@@ -830,20 +831,20 @@ static ssize_t vfd_write(struct file *file, const char *buf,
ictx = file->private_data;
if (!ictx) {
- pr_err("no context for device\n");
+ pr_err_ratelimited("no context for device\n");
return -ENODEV;
}
mutex_lock(&ictx->lock);
if (!ictx->dev_present_intf0) {
- pr_err("no iMON device present\n");
+ pr_err_ratelimited("no iMON device present\n");
retval = -ENODEV;
goto exit;
}
if (n_bytes <= 0 || n_bytes > 32) {
- pr_err("invalid payload size\n");
+ pr_err_ratelimited("invalid payload size\n");
retval = -EINVAL;
goto exit;
}
@@ -869,7 +870,7 @@ static ssize_t vfd_write(struct file *file, const char *buf,
retval = send_packet(ictx);
if (retval) {
- pr_err("send packet failed for packet #%d\n", seq / 2);
+ pr_err_ratelimited("send packet #%d failed\n", seq / 2);
goto exit;
} else {
seq += 2;
@@ -883,7 +884,7 @@ static ssize_t vfd_write(struct file *file, const char *buf,
ictx->usb_tx_buf[7] = (unsigned char) seq;
retval = send_packet(ictx);
if (retval)
- pr_err("send packet failed for packet #%d\n", seq / 2);
+ pr_err_ratelimited("send packet #%d failed\n", seq / 2);
exit:
mutex_unlock(&ictx->lock);
@@ -912,20 +913,21 @@ static ssize_t lcd_write(struct file *file, const char *buf,
ictx = file->private_data;
if (!ictx) {
- pr_err("no context for device\n");
+ pr_err_ratelimited("no context for device\n");
return -ENODEV;
}
mutex_lock(&ictx->lock);
if (!ictx->display_supported) {
- pr_err("no iMON display present\n");
+ pr_err_ratelimited("no iMON display present\n");
retval = -ENODEV;
goto exit;
}
if (n_bytes != 8) {
- pr_err("invalid payload size: %d (expected 8)\n", (int)n_bytes);
+ pr_err_ratelimited("invalid payload size: %d (expected 8)\n",
+ (int)n_bytes);
retval = -EINVAL;
goto exit;
}
@@ -937,7 +939,7 @@ static ssize_t lcd_write(struct file *file, const char *buf,
retval = send_packet(ictx);
if (retval) {
- pr_err("send packet failed!\n");
+ pr_err_ratelimited("send packet failed!\n");
goto exit;
} else {
dev_dbg(ictx->dev, "%s: write %d bytes to LCD\n",
@@ -1656,7 +1658,7 @@ static void usb_rx_callback_intf0(struct urb *urb)
return;
ictx = (struct imon_context *)urb->context;
- if (!ictx)
+ if (!ictx || !ictx->dev_present_intf0)
return;
switch (urb->status) {
@@ -1688,7 +1690,7 @@ static void usb_rx_callback_intf1(struct urb *urb)
return;
ictx = (struct imon_context *)urb->context;
- if (!ictx)
+ if (!ictx || !ictx->dev_present_intf1)
return;
switch (urb->status) {
@@ -2116,7 +2118,6 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
ictx->dev = dev;
ictx->usbdev_intf0 = usb_get_dev(interface_to_usbdev(intf));
- ictx->dev_present_intf0 = true;
ictx->rx_urb_intf0 = rx_urb;
ictx->tx_urb = tx_urb;
ictx->rf_device = false;
@@ -2155,6 +2156,8 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
goto rdev_setup_failed;
}
+ ictx->dev_present_intf0 = true;
+
mutex_unlock(&ictx->lock);
return ictx;
@@ -2198,7 +2201,6 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
}
ictx->usbdev_intf1 = usb_get_dev(interface_to_usbdev(intf));
- ictx->dev_present_intf1 = true;
ictx->rx_urb_intf1 = rx_urb;
ret = -ENODEV;
@@ -2227,6 +2229,8 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
goto urb_submit_failed;
}
+ ictx->dev_present_intf1 = true;
+
mutex_unlock(&ictx->lock);
return ictx;
diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c
index 624449afaa61..035668e27f6b 100644
--- a/drivers/media/rc/ir-jvc-decoder.c
+++ b/drivers/media/rc/ir-jvc-decoder.c
@@ -13,6 +13,7 @@
*/
#include <linux/bitrev.h>
+#include <linux/module.h>
#include "rc-core-priv.h"
#define JVC_NBITS 16 /* dev(8) + func(8) */
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index 1c5cc65ea1e1..5faba2a2fdd3 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -14,6 +14,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
+#include <linux/module.h>
#include <media/lirc.h>
#include <media/lirc_dev.h>
#include <media/rc-core.h>
@@ -98,24 +99,24 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev)
return 0;
}
-static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf,
+static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
size_t n, loff_t *ppos)
{
struct lirc_codec *lirc;
struct rc_dev *dev;
- int *txbuf; /* buffer with values to transmit */
- int ret = 0;
+ unsigned int *txbuf; /* buffer with values to transmit */
+ ssize_t ret = 0;
size_t count;
lirc = lirc_get_pdata(file);
if (!lirc)
return -EFAULT;
- if (n % sizeof(int))
+ if (n < sizeof(unsigned) || n % sizeof(unsigned))
return -EINVAL;
- count = n / sizeof(int);
- if (count > LIRCBUF_SIZE || count % 2 == 0 || n % sizeof(int) != 0)
+ count = n / sizeof(unsigned);
+ if (count > LIRCBUF_SIZE || count % 2 == 0)
return -EINVAL;
txbuf = memdup_user(buf, n);
@@ -129,7 +130,10 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf,
}
if (dev->tx_ir)
- ret = dev->tx_ir(dev, txbuf, (u32)n);
+ ret = dev->tx_ir(dev, txbuf, count);
+
+ if (ret > 0)
+ ret *= sizeof(unsigned);
out:
kfree(txbuf);
@@ -137,10 +141,11 @@ out:
}
static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
- unsigned long __user arg)
+ unsigned long arg)
{
struct lirc_codec *lirc;
struct rc_dev *dev;
+ u32 __user *argp = (u32 __user *)(arg);
int ret = 0;
__u32 val = 0, tmp;
@@ -153,7 +158,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
return -EFAULT;
if (_IOC_DIR(cmd) & _IOC_WRITE) {
- ret = get_user(val, (__u32 *)arg);
+ ret = get_user(val, argp);
if (ret)
return ret;
}
@@ -262,7 +267,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
}
if (_IOC_DIR(cmd) & _IOC_READ)
- ret = put_user(val, (__u32 *)arg);
+ ret = put_user(val, argp);
return ret;
}
diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
new file mode 100644
index 000000000000..3784ebf80ec7
--- /dev/null
+++ b/drivers/media/rc/ir-mce_kbd-decoder.c
@@ -0,0 +1,449 @@
+/* ir-mce_kbd-decoder.c - A decoder for the RC6-ish keyboard/mouse IR protocol
+ * used by the Microsoft Remote Keyboard for Windows Media Center Edition,
+ * referred to by Microsoft's Windows Media Center remote specification docs
+ * as "an internal protocol called MCIR-2".
+ *
+ * Copyright (C) 2011 by Jarod Wilson <jarod@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+
+#include "rc-core-priv.h"
+
+/*
+ * This decoder currently supports:
+ * - MCIR-2 29-bit IR signals used for mouse movement and buttons
+ * - MCIR-2 32-bit IR signals used for standard keyboard keys
+ *
+ * The media keys on the keyboard send RC-6 signals that are inditinguishable
+ * from the keys of the same name on the stock MCE remote, and will be handled
+ * by the standard RC-6 decoder, and be made available to the system via the
+ * input device for the remote, rather than the keyboard/mouse one.
+ */
+
+#define MCIR2_UNIT 333333 /* ns */
+#define MCIR2_HEADER_NBITS 5
+#define MCIR2_MOUSE_NBITS 29
+#define MCIR2_KEYBOARD_NBITS 32
+#define MCIR2_PREFIX_PULSE (8 * MCIR2_UNIT)
+#define MCIR2_PREFIX_SPACE (1 * MCIR2_UNIT)
+#define MCIR2_MAX_LEN (3 * MCIR2_UNIT)
+#define MCIR2_BIT_START (1 * MCIR2_UNIT)
+#define MCIR2_BIT_END (1 * MCIR2_UNIT)
+#define MCIR2_BIT_0 (1 * MCIR2_UNIT)
+#define MCIR2_BIT_SET (2 * MCIR2_UNIT)
+#define MCIR2_MODE_MASK 0xf /* for the header bits */
+#define MCIR2_KEYBOARD_HEADER 0x4
+#define MCIR2_MOUSE_HEADER 0x1
+#define MCIR2_MASK_KEYS_START 0xe0
+
+enum mce_kbd_mode {
+ MCIR2_MODE_KEYBOARD,
+ MCIR2_MODE_MOUSE,
+ MCIR2_MODE_UNKNOWN,
+};
+
+enum mce_kbd_state {
+ STATE_INACTIVE,
+ STATE_HEADER_BIT_START,
+ STATE_HEADER_BIT_END,
+ STATE_BODY_BIT_START,
+ STATE_BODY_BIT_END,
+ STATE_FINISHED,
+};
+
+static unsigned char kbd_keycodes[256] = {
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_A,
+ KEY_B, KEY_C, KEY_D, KEY_E, KEY_F,
+ KEY_G, KEY_H, KEY_I, KEY_J, KEY_K,
+ KEY_L, KEY_M, KEY_N, KEY_O, KEY_P,
+ KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U,
+ KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z,
+ KEY_1, KEY_2, KEY_3, KEY_4, KEY_5,
+ KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
+ KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE,
+ KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH,
+ KEY_RESERVED, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA,
+ KEY_DOT, KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2,
+ KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7,
+ KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12,
+ KEY_SYSRQ, KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME,
+ KEY_PAGEUP, KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT,
+ KEY_LEFT, KEY_DOWN, KEY_UP, KEY_NUMLOCK, KEY_KPSLASH,
+ KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, KEY_KPENTER, KEY_KP1,
+ KEY_KP2, KEY_KP3, KEY_KP4, KEY_KP5, KEY_KP6,
+ KEY_KP7, KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT,
+ KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13,
+ KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18,
+ KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23,
+ KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT,
+ KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY,
+ KEY_PASTE, KEY_FIND, KEY_MUTE, KEY_VOLUMEUP, KEY_VOLUMEDOWN,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_KPCOMMA, KEY_RESERVED,
+ KEY_RO, KEY_KATAKANAHIRAGANA, KEY_YEN, KEY_HENKAN, KEY_MUHENKAN,
+ KEY_KPJPCOMMA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_HANGUEL,
+ KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, KEY_ZENKAKUHANKAKU, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTCTRL,
+ KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT,
+ KEY_RIGHTALT, KEY_RIGHTMETA, KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG,
+ KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE,
+ KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND,
+ KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP, KEY_COFFEE,
+ KEY_REFRESH, KEY_CALC, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED
+};
+
+static void mce_kbd_rx_timeout(unsigned long data)
+{
+ struct mce_kbd_dec *mce_kbd = (struct mce_kbd_dec *)data;
+ int i;
+ unsigned char maskcode;
+
+ IR_dprintk(2, "timer callback clearing all keys\n");
+
+ for (i = 0; i < 7; i++) {
+ maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
+ input_report_key(mce_kbd->idev, maskcode, 0);
+ }
+
+ for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
+ input_report_key(mce_kbd->idev, kbd_keycodes[i], 0);
+}
+
+static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data)
+{
+ switch (data->header & MCIR2_MODE_MASK) {
+ case MCIR2_KEYBOARD_HEADER:
+ return MCIR2_MODE_KEYBOARD;
+ case MCIR2_MOUSE_HEADER:
+ return MCIR2_MODE_MOUSE;
+ default:
+ return MCIR2_MODE_UNKNOWN;
+ }
+}
+
+static void ir_mce_kbd_process_keyboard_data(struct input_dev *idev,
+ u32 scancode)
+{
+ u8 keydata = (scancode >> 8) & 0xff;
+ u8 shiftmask = scancode & 0xff;
+ unsigned char keycode, maskcode;
+ int i, keystate;
+
+ IR_dprintk(1, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n",
+ keydata, shiftmask);
+
+ for (i = 0; i < 7; i++) {
+ maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
+ if (shiftmask & (1 << i))
+ keystate = 1;
+ else
+ keystate = 0;
+ input_report_key(idev, maskcode, keystate);
+ }
+
+ if (keydata) {
+ keycode = kbd_keycodes[keydata];
+ input_report_key(idev, keycode, 1);
+ } else {
+ for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
+ input_report_key(idev, kbd_keycodes[i], 0);
+ }
+}
+
+static void ir_mce_kbd_process_mouse_data(struct input_dev *idev, u32 scancode)
+{
+ /* raw mouse coordinates */
+ u8 xdata = (scancode >> 7) & 0x7f;
+ u8 ydata = (scancode >> 14) & 0x7f;
+ int x, y;
+ /* mouse buttons */
+ bool right = scancode & 0x40;
+ bool left = scancode & 0x20;
+
+ if (xdata & 0x40)
+ x = -((~xdata & 0x7f) + 1);
+ else
+ x = xdata;
+
+ if (ydata & 0x40)
+ y = -((~ydata & 0x7f) + 1);
+ else
+ y = ydata;
+
+ IR_dprintk(1, "mouse: x = %d, y = %d, btns = %s%s\n",
+ x, y, left ? "L" : "", right ? "R" : "");
+
+ input_report_rel(idev, REL_X, x);
+ input_report_rel(idev, REL_Y, y);
+
+ input_report_key(idev, BTN_LEFT, left);
+ input_report_key(idev, BTN_RIGHT, right);
+}
+
+/**
+ * ir_mce_kbd_decode() - Decode one mce_kbd pulse or space
+ * @dev: the struct rc_dev descriptor of the device
+ * @ev: the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+ struct mce_kbd_dec *data = &dev->raw->mce_kbd;
+ u32 scancode;
+ unsigned long delay;
+
+ if (!(dev->raw->enabled_protocols & RC_TYPE_MCE_KBD))
+ return 0;
+
+ if (!is_timing_event(ev)) {
+ if (ev.reset)
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2))
+ goto out;
+
+again:
+ IR_dprintk(2, "started at state %i (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+ if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2))
+ return 0;
+
+ switch (data->state) {
+
+ case STATE_INACTIVE:
+ if (!ev.pulse)
+ break;
+
+ /* Note: larger margin on first pulse since each MCIR2_UNIT
+ is quite short and some hardware takes some time to
+ adjust to the signal */
+ if (!eq_margin(ev.duration, MCIR2_PREFIX_PULSE, MCIR2_UNIT))
+ break;
+
+ data->state = STATE_HEADER_BIT_START;
+ data->count = 0;
+ data->header = 0;
+ return 0;
+
+ case STATE_HEADER_BIT_START:
+ if (geq_margin(ev.duration, MCIR2_MAX_LEN, MCIR2_UNIT / 2))
+ break;
+
+ data->header <<= 1;
+ if (ev.pulse)
+ data->header |= 1;
+ data->count++;
+ data->state = STATE_HEADER_BIT_END;
+ return 0;
+
+ case STATE_HEADER_BIT_END:
+ if (!is_transition(&ev, &dev->raw->prev_ev))
+ break;
+
+ decrease_duration(&ev, MCIR2_BIT_END);
+
+ if (data->count != MCIR2_HEADER_NBITS) {
+ data->state = STATE_HEADER_BIT_START;
+ goto again;
+ }
+
+ switch (mce_kbd_mode(data)) {
+ case MCIR2_MODE_KEYBOARD:
+ data->wanted_bits = MCIR2_KEYBOARD_NBITS;
+ break;
+ case MCIR2_MODE_MOUSE:
+ data->wanted_bits = MCIR2_MOUSE_NBITS;
+ break;
+ default:
+ IR_dprintk(1, "not keyboard or mouse data\n");
+ goto out;
+ }
+
+ data->count = 0;
+ data->body = 0;
+ data->state = STATE_BODY_BIT_START;
+ goto again;
+
+ case STATE_BODY_BIT_START:
+ if (geq_margin(ev.duration, MCIR2_MAX_LEN, MCIR2_UNIT / 2))
+ break;
+
+ data->body <<= 1;
+ if (ev.pulse)
+ data->body |= 1;
+ data->count++;
+ data->state = STATE_BODY_BIT_END;
+ return 0;
+
+ case STATE_BODY_BIT_END:
+ if (!is_transition(&ev, &dev->raw->prev_ev))
+ break;
+
+ if (data->count == data->wanted_bits)
+ data->state = STATE_FINISHED;
+ else
+ data->state = STATE_BODY_BIT_START;
+
+ decrease_duration(&ev, MCIR2_BIT_END);
+ goto again;
+
+ case STATE_FINISHED:
+ if (ev.pulse)
+ break;
+
+ switch (data->wanted_bits) {
+ case MCIR2_KEYBOARD_NBITS:
+ scancode = data->body & 0xffff;
+ IR_dprintk(1, "keyboard data 0x%08x\n", data->body);
+ if (dev->timeout)
+ delay = usecs_to_jiffies(dev->timeout / 1000);
+ else
+ delay = msecs_to_jiffies(100);
+ mod_timer(&data->rx_timeout, jiffies + delay);
+ /* Pass data to keyboard buffer parser */
+ ir_mce_kbd_process_keyboard_data(data->idev, scancode);
+ break;
+ case MCIR2_MOUSE_NBITS:
+ scancode = data->body & 0x1fffff;
+ IR_dprintk(1, "mouse data 0x%06x\n", scancode);
+ /* Pass data to mouse buffer parser */
+ ir_mce_kbd_process_mouse_data(data->idev, scancode);
+ break;
+ default:
+ IR_dprintk(1, "not keyboard or mouse data\n");
+ goto out;
+ }
+
+ data->state = STATE_INACTIVE;
+ input_sync(data->idev);
+ return 0;
+ }
+
+out:
+ IR_dprintk(1, "failed at state %i (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ data->state = STATE_INACTIVE;
+ input_sync(data->idev);
+ return -EINVAL;
+}
+
+static int ir_mce_kbd_register(struct rc_dev *dev)
+{
+ struct mce_kbd_dec *mce_kbd = &dev->raw->mce_kbd;
+ struct input_dev *idev;
+ int i, ret;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ snprintf(mce_kbd->name, sizeof(mce_kbd->name),
+ "MCE IR Keyboard/Mouse (%s)", dev->driver_name);
+ strlcat(mce_kbd->phys, "/input0", sizeof(mce_kbd->phys));
+
+ idev->name = mce_kbd->name;
+ idev->phys = mce_kbd->phys;
+
+ /* Keyboard bits */
+ set_bit(EV_KEY, idev->evbit);
+ set_bit(EV_REP, idev->evbit);
+ for (i = 0; i < sizeof(kbd_keycodes); i++)
+ set_bit(kbd_keycodes[i], idev->keybit);
+
+ /* Mouse bits */
+ set_bit(EV_REL, idev->evbit);
+ set_bit(REL_X, idev->relbit);
+ set_bit(REL_Y, idev->relbit);
+ set_bit(BTN_LEFT, idev->keybit);
+ set_bit(BTN_RIGHT, idev->keybit);
+
+ /* Report scancodes too */
+ set_bit(EV_MSC, idev->evbit);
+ set_bit(MSC_SCAN, idev->mscbit);
+
+ setup_timer(&mce_kbd->rx_timeout, mce_kbd_rx_timeout,
+ (unsigned long)mce_kbd);
+
+ input_set_drvdata(idev, mce_kbd);
+
+#if 0
+ /* Adding this reference means two input devices are associated with
+ * this rc-core device, which ir-keytable doesn't cope with yet */
+ idev->dev.parent = &dev->dev;
+#endif
+
+ ret = input_register_device(idev);
+ if (ret < 0) {
+ input_free_device(idev);
+ return -EIO;
+ }
+
+ mce_kbd->idev = idev;
+
+ return 0;
+}
+
+static int ir_mce_kbd_unregister(struct rc_dev *dev)
+{
+ struct mce_kbd_dec *mce_kbd = &dev->raw->mce_kbd;
+ struct input_dev *idev = mce_kbd->idev;
+
+ del_timer_sync(&mce_kbd->rx_timeout);
+ input_unregister_device(idev);
+
+ return 0;
+}
+
+static struct ir_raw_handler mce_kbd_handler = {
+ .protocols = RC_TYPE_MCE_KBD,
+ .decode = ir_mce_kbd_decode,
+ .raw_register = ir_mce_kbd_register,
+ .raw_unregister = ir_mce_kbd_unregister,
+};
+
+static int __init ir_mce_kbd_decode_init(void)
+{
+ ir_raw_handler_register(&mce_kbd_handler);
+
+ printk(KERN_INFO "IR MCE Keyboard/mouse protocol handler initialized\n");
+ return 0;
+}
+
+static void __exit ir_mce_kbd_decode_exit(void)
+{
+ ir_raw_handler_unregister(&mce_kbd_handler);
+}
+
+module_init(ir_mce_kbd_decode_init);
+module_exit(ir_mce_kbd_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_DESCRIPTION("MCE Keyboard/mouse IR protocol decoder");
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 63ee722dbd02..17f8db00435a 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -13,6 +13,7 @@
*/
#include <linux/bitrev.h>
+#include <linux/module.h>
#include "rc-core-priv.h"
#define NEC_NBITS 32
diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c
index 423ed45d6c55..2e5cd3100b64 100644
--- a/drivers/media/rc/ir-raw.c
+++ b/drivers/media/rc/ir-raw.c
@@ -12,8 +12,10 @@
* GNU General Public License for more details.
*/
+#include <linux/export.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
+#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/freezer.h>
#include "rc-core-priv.h"
@@ -355,6 +357,7 @@ static void init_decoders(struct work_struct *work)
load_rc6_decode();
load_jvc_decode();
load_sony_decode();
+ load_mce_kbd_decode();
load_lirc_codec();
/* If needed, we may later add some init code. In this case,
diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
index ebdba5539916..9ab663a507a4 100644
--- a/drivers/media/rc/ir-rc5-decoder.c
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -21,6 +21,7 @@
*/
#include "rc-core-priv.h"
+#include <linux/module.h>
#define RC5_NBITS 14
#define RC5X_NBITS 20
diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c
index 90aa8868629a..ec8d4a2e2c5a 100644
--- a/drivers/media/rc/ir-rc5-sz-decoder.c
+++ b/drivers/media/rc/ir-rc5-sz-decoder.c
@@ -21,6 +21,7 @@
*/
#include "rc-core-priv.h"
+#include <linux/module.h>
#define RC5_SZ_NBITS 15
#define RC5_UNIT 888888 /* ns */
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index 755dafa3871b..140fb67e2f89 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -13,6 +13,7 @@
*/
#include "rc-core-priv.h"
+#include <linux/module.h>
/*
* This decoder currently supports:
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
index a92de80c48db..d5e2b50aff1f 100644
--- a/drivers/media/rc/ir-sony-decoder.c
+++ b/drivers/media/rc/ir-sony-decoder.c
@@ -13,6 +13,7 @@
*/
#include <linux/bitrev.h>
+#include <linux/module.h>
#include "rc-core-priv.h"
#define SONY_UNIT 600000 /* ns */
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index ecd3d0280768..682009d76cdf 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -42,7 +42,6 @@
#include <linux/bitops.h>
#include <media/rc-core.h>
#include <linux/pci_ids.h>
-#include <linux/delay.h>
#include "ite-cir.h"
@@ -383,7 +382,7 @@ static int ite_set_tx_duty_cycle(struct rc_dev *rcdev, u32 duty_cycle)
/* transmit out IR pulses; what you get here is a batch of alternating
* pulse/space/pulse/space lengths that we should write out completely through
* the FIFO, blocking on a full FIFO */
-static int ite_tx_ir(struct rc_dev *rcdev, int *txbuf, u32 n)
+static int ite_tx_ir(struct rc_dev *rcdev, unsigned *txbuf, unsigned n)
{
unsigned long flags;
struct ite_dev *dev = rcdev->priv;
@@ -399,9 +398,6 @@ static int ite_tx_ir(struct rc_dev *rcdev, int *txbuf, u32 n)
/* clear the array just in case */
memset(last_sent, 0, ARRAY_SIZE(last_sent));
- /* n comes in bytes; convert to ints */
- n /= sizeof(int);
-
spin_lock_irqsave(&dev->lock, flags);
/* let everybody know we're now transmitting */
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index b57fc83fb4d2..36e4d5e8dd6a 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-apac-viewcomp.o \
rc-asus-pc39.o \
rc-ati-tv-wonder-hd-600.o \
+ rc-ati-x10.o \
rc-avermedia-a16d.o \
rc-avermedia.o \
rc-avermedia-cardbus.o \
@@ -47,6 +48,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-lirc.o \
rc-lme2510.o \
rc-manli.o \
+ rc-medion-x10.o \
rc-msi-digivox-ii.o \
rc-msi-digivox-iii.o \
rc-msi-tvanywhere.o \
@@ -70,6 +72,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-hauppauge.o \
rc-rc6-mce.o \
rc-real-audio-220-32-keys.o \
+ rc-snapstream-firefly.o \
rc-streamzap.o \
rc-tbs-nec.o \
rc-technisat-usb2.o \
diff --git a/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c b/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
index 9a8752fdcca1..b0e42df7ff82 100644
--- a/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
+++ b/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* ADS Tech Instant TV DVB-T PCI Remote */
diff --git a/drivers/media/rc/keymaps/rc-alink-dtu-m.c b/drivers/media/rc/keymaps/rc-alink-dtu-m.c
index fe652e928dc0..4e6ade8e616f 100644
--- a/drivers/media/rc/keymaps/rc-alink-dtu-m.c
+++ b/drivers/media/rc/keymaps/rc-alink-dtu-m.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* A-Link DTU(m) slim remote, 6 rows, 3 columns. */
static struct rc_map_table alink_dtu_m[] = {
diff --git a/drivers/media/rc/keymaps/rc-anysee.c b/drivers/media/rc/keymaps/rc-anysee.c
index 884f1b51a8ee..c735fe10a390 100644
--- a/drivers/media/rc/keymaps/rc-anysee.c
+++ b/drivers/media/rc/keymaps/rc-anysee.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table anysee[] = {
{ 0x0800, KEY_0 },
diff --git a/drivers/media/rc/keymaps/rc-apac-viewcomp.c b/drivers/media/rc/keymaps/rc-apac-viewcomp.c
index 7af188209ff9..8c92ff95f94d 100644
--- a/drivers/media/rc/keymaps/rc-apac-viewcomp.c
+++ b/drivers/media/rc/keymaps/rc-apac-viewcomp.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Attila Kondoros <attila.kondoros@chello.hu> */
diff --git a/drivers/media/rc/keymaps/rc-asus-pc39.c b/drivers/media/rc/keymaps/rc-asus-pc39.c
index b2481154491b..2caf2117759b 100644
--- a/drivers/media/rc/keymaps/rc-asus-pc39.c
+++ b/drivers/media/rc/keymaps/rc-asus-pc39.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Marc Fargas <telenieko@telenieko.com>
diff --git a/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c b/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
index f766b24b0158..2031224a2027 100644
--- a/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
+++ b/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* ATI TV Wonder HD 600 USB
Devin Heitmueller <devin.heitmueller@gmail.com>
diff --git a/drivers/media/rc/keymaps/rc-ati-x10.c b/drivers/media/rc/keymaps/rc-ati-x10.c
new file mode 100644
index 000000000000..e1b8b2605c48
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-ati-x10.c
@@ -0,0 +1,104 @@
+/*
+ * ATI X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * This file is based on the static generic keytable previously found in
+ * ati_remote.c, which is
+ * Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ * Copyright (c) 2002 Vladimir Dergachev
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table ati_x10[] = {
+ { 0xd20d, KEY_1 },
+ { 0xd30e, KEY_2 },
+ { 0xd40f, KEY_3 },
+ { 0xd510, KEY_4 },
+ { 0xd611, KEY_5 },
+ { 0xd712, KEY_6 },
+ { 0xd813, KEY_7 },
+ { 0xd914, KEY_8 },
+ { 0xda15, KEY_9 },
+ { 0xdc17, KEY_0 },
+ { 0xc500, KEY_A },
+ { 0xc601, KEY_B },
+ { 0xde19, KEY_C },
+ { 0xe01b, KEY_D },
+ { 0xe621, KEY_E },
+ { 0xe823, KEY_F },
+
+ { 0xdd18, KEY_KPENTER }, /* "check" */
+ { 0xdb16, KEY_MENU }, /* "menu" */
+ { 0xc702, KEY_POWER }, /* Power */
+ { 0xc803, KEY_TV }, /* TV */
+ { 0xc904, KEY_DVD }, /* DVD */
+ { 0xca05, KEY_WWW }, /* WEB */
+ { 0xcb06, KEY_BOOKMARKS }, /* "book" */
+ { 0xcc07, KEY_EDIT }, /* "hand" */
+ { 0xe11c, KEY_COFFEE }, /* "timer" */
+ { 0xe520, KEY_FRONT }, /* "max" */
+ { 0xe21d, KEY_LEFT }, /* left */
+ { 0xe41f, KEY_RIGHT }, /* right */
+ { 0xe722, KEY_DOWN }, /* down */
+ { 0xdf1a, KEY_UP }, /* up */
+ { 0xe31e, KEY_OK }, /* "OK" */
+ { 0xce09, KEY_VOLUMEDOWN }, /* VOL + */
+ { 0xcd08, KEY_VOLUMEUP }, /* VOL - */
+ { 0xcf0a, KEY_MUTE }, /* MUTE */
+ { 0xd00b, KEY_CHANNELUP }, /* CH + */
+ { 0xd10c, KEY_CHANNELDOWN },/* CH - */
+ { 0xec27, KEY_RECORD }, /* ( o) red */
+ { 0xea25, KEY_PLAY }, /* ( >) */
+ { 0xe924, KEY_REWIND }, /* (<<) */
+ { 0xeb26, KEY_FORWARD }, /* (>>) */
+ { 0xed28, KEY_STOP }, /* ([]) */
+ { 0xee29, KEY_PAUSE }, /* ('') */
+ { 0xf02b, KEY_PREVIOUS }, /* (<-) */
+ { 0xef2a, KEY_NEXT }, /* (>+) */
+ { 0xf22d, KEY_INFO }, /* PLAYING */
+ { 0xf32e, KEY_HOME }, /* TOP */
+ { 0xf42f, KEY_END }, /* END */
+ { 0xf530, KEY_SELECT }, /* SELECT */
+};
+
+static struct rc_map_list ati_x10_map = {
+ .map = {
+ .scan = ati_x10,
+ .size = ARRAY_SIZE(ati_x10),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_ATI_X10,
+ }
+};
+
+static int __init init_rc_map_ati_x10(void)
+{
+ return rc_map_register(&ati_x10_map);
+}
+
+static void __exit exit_rc_map_ati_x10(void)
+{
+ rc_map_unregister(&ati_x10_map);
+}
+
+module_init(init_rc_map_ati_x10)
+module_exit(exit_rc_map_ati_x10)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-a16d.c b/drivers/media/rc/keymaps/rc-avermedia-a16d.c
index ec9beeebd410..894939ac17f2 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-a16d.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-a16d.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table avermedia_a16d[] = {
{ 0x20, KEY_LIST},
diff --git a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
index 22f54d413a35..d2aaf5b9e39f 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Oldrich Jedlicka <oldium.pro@seznam.cz> */
diff --git a/drivers/media/rc/keymaps/rc-avermedia-dvbt.c b/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
index c25809d4c813..dc2baf062398 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Matt Jesson <dvb@jesson.eclipse.co.uk */
diff --git a/drivers/media/rc/keymaps/rc-avermedia-m135a.c b/drivers/media/rc/keymaps/rc-avermedia-m135a.c
index 3d2cbe4e5e46..04269d31fa19 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-m135a.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-m135a.c
@@ -10,6 +10,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Avermedia M135A with RM-JX and RM-K6 remote controls
diff --git a/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c b/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
index 8cd7f28808bd..e83b1a1939bf 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
@@ -9,6 +9,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Avermedia M733A with IR model RM-K6
diff --git a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c
index 9d68af217d8b..8344bcc595be 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Initial keytable is from Jose Alberto Reguero <jareguero@telefonica.net>
and Felipe Morales Moreno <felipe.morales.moreno@gmail.com> */
diff --git a/drivers/media/rc/keymaps/rc-avermedia.c b/drivers/media/rc/keymaps/rc-avermedia.c
index edfa71506d3e..c6063dfcd507 100644
--- a/drivers/media/rc/keymaps/rc-avermedia.c
+++ b/drivers/media/rc/keymaps/rc-avermedia.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Alex Hermann <gaaf@gmx.net> */
diff --git a/drivers/media/rc/keymaps/rc-avertv-303.c b/drivers/media/rc/keymaps/rc-avertv-303.c
index 32e94988dc94..14f78451e64e 100644
--- a/drivers/media/rc/keymaps/rc-avertv-303.c
+++ b/drivers/media/rc/keymaps/rc-avertv-303.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* AVERTV STUDIO 303 Remote */
diff --git a/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c b/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c
index c3f6d62ac892..ea7f2d0f31eb 100644
--- a/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c
+++ b/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table azurewave_ad_tu700[] = {
{ 0x0000, KEY_TAB }, /* Tab */
diff --git a/drivers/media/rc/keymaps/rc-behold-columbus.c b/drivers/media/rc/keymaps/rc-behold-columbus.c
index 8bf058f67f0c..086b4b1f19e1 100644
--- a/drivers/media/rc/keymaps/rc-behold-columbus.c
+++ b/drivers/media/rc/keymaps/rc-behold-columbus.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Beholder Intl. Ltd. 2008
* Dmitry Belimov d.belimov@google.com
diff --git a/drivers/media/rc/keymaps/rc-behold.c b/drivers/media/rc/keymaps/rc-behold.c
index c909a234c776..0877e3480941 100644
--- a/drivers/media/rc/keymaps/rc-behold.c
+++ b/drivers/media/rc/keymaps/rc-behold.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Igor Kuznetsov <igk72@ya.ru>
diff --git a/drivers/media/rc/keymaps/rc-budget-ci-old.c b/drivers/media/rc/keymaps/rc-budget-ci-old.c
index 2f66e4310d20..8311e092c098 100644
--- a/drivers/media/rc/keymaps/rc-budget-ci-old.c
+++ b/drivers/media/rc/keymaps/rc-budget-ci-old.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* From reading the following remotes:
diff --git a/drivers/media/rc/keymaps/rc-cinergy-1400.c b/drivers/media/rc/keymaps/rc-cinergy-1400.c
index 284534b67e7d..0c87fbaf99ab 100644
--- a/drivers/media/rc/keymaps/rc-cinergy-1400.c
+++ b/drivers/media/rc/keymaps/rc-cinergy-1400.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Cinergy 1400 DVB-T */
diff --git a/drivers/media/rc/keymaps/rc-cinergy.c b/drivers/media/rc/keymaps/rc-cinergy.c
index cf3a6bfb190c..309e9e3fb6f3 100644
--- a/drivers/media/rc/keymaps/rc-cinergy.c
+++ b/drivers/media/rc/keymaps/rc-cinergy.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table cinergy[] = {
{ 0x00, KEY_0 },
diff --git a/drivers/media/rc/keymaps/rc-dib0700-nec.c b/drivers/media/rc/keymaps/rc-dib0700-nec.c
index 7a5f5300caf9..4d13a7f2e5c3 100644
--- a/drivers/media/rc/keymaps/rc-dib0700-nec.c
+++ b/drivers/media/rc/keymaps/rc-dib0700-nec.c
@@ -16,6 +16,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table dib0700_nec_table[] = {
/* Key codes for the Pixelview SBTVD remote */
diff --git a/drivers/media/rc/keymaps/rc-dib0700-rc5.c b/drivers/media/rc/keymaps/rc-dib0700-rc5.c
index 4af12e45dfba..ba81d9697cfc 100644
--- a/drivers/media/rc/keymaps/rc-dib0700-rc5.c
+++ b/drivers/media/rc/keymaps/rc-dib0700-rc5.c
@@ -16,6 +16,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table dib0700_rc5_table[] = {
/* Key codes for the tiny Pinnacle remote*/
diff --git a/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c b/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c
index f68b450f5593..bed78acb9198 100644
--- a/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c
+++ b/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table digitalnow_tinytwin[] = {
{ 0x0000, KEY_MUTE }, /* [symbol speaker] */
diff --git a/drivers/media/rc/keymaps/rc-digittrade.c b/drivers/media/rc/keymaps/rc-digittrade.c
index 21d49871f2a3..a3b97a1fe223 100644
--- a/drivers/media/rc/keymaps/rc-digittrade.c
+++ b/drivers/media/rc/keymaps/rc-digittrade.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Digittrade DVB-T USB Stick remote controller. */
/* Imported from af9015.h.
diff --git a/drivers/media/rc/keymaps/rc-dm1105-nec.c b/drivers/media/rc/keymaps/rc-dm1105-nec.c
index d024fbf88bc4..67fc9fb0c007 100644
--- a/drivers/media/rc/keymaps/rc-dm1105-nec.c
+++ b/drivers/media/rc/keymaps/rc-dm1105-nec.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* DVBWorld remotes
Igor M. Liplianin <liplianin@me.by>
diff --git a/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c b/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
index 82c0200029af..91ea91de9179 100644
--- a/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
+++ b/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* DigitalNow DNTV Live DVB-T Remote */
diff --git a/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c b/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
index 015e99de06de..fd680d4d3eb6 100644
--- a/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
+++ b/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* DigitalNow DNTV Live! DVB-T Pro Remote */
diff --git a/drivers/media/rc/keymaps/rc-em-terratec.c b/drivers/media/rc/keymaps/rc-em-terratec.c
index 269d4299da34..d1fcd64c0f90 100644
--- a/drivers/media/rc/keymaps/rc-em-terratec.c
+++ b/drivers/media/rc/keymaps/rc-em-terratec.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table em_terratec[] = {
{ 0x01, KEY_CHANNEL },
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c b/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
index e388698a0697..2fe45e41fe49 100644
--- a/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
+++ b/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Encore ENLTV-FM v5.3
Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv.c b/drivers/media/rc/keymaps/rc-encore-enltv.c
index e56ac6e9670a..223de75a6d1c 100644
--- a/drivers/media/rc/keymaps/rc-encore-enltv.c
+++ b/drivers/media/rc/keymaps/rc-encore-enltv.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Encore ENLTV-FM - black plastic, white front cover with white glowing buttons
Juan Pablo Sormani <sorman@gmail.com> */
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv2.c b/drivers/media/rc/keymaps/rc-encore-enltv2.c
index b6264f1bc4c1..669cbff22b7e 100644
--- a/drivers/media/rc/keymaps/rc-encore-enltv2.c
+++ b/drivers/media/rc/keymaps/rc-encore-enltv2.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Encore ENLTV2-FM - silver plastic - "Wand Media" written at the botton
Mauro Carvalho Chehab <mchehab@infradead.org> */
diff --git a/drivers/media/rc/keymaps/rc-evga-indtube.c b/drivers/media/rc/keymaps/rc-evga-indtube.c
index a2bf24f6dfbb..2c647fc25916 100644
--- a/drivers/media/rc/keymaps/rc-evga-indtube.c
+++ b/drivers/media/rc/keymaps/rc-evga-indtube.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* EVGA inDtube
Devin Heitmueller <devin.heitmueller@gmail.com>
diff --git a/drivers/media/rc/keymaps/rc-eztv.c b/drivers/media/rc/keymaps/rc-eztv.c
index 1e8e5b2d6e36..76921445c1d9 100644
--- a/drivers/media/rc/keymaps/rc-eztv.c
+++ b/drivers/media/rc/keymaps/rc-eztv.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Alfons Geser <a.geser@cox.net>
* updates from Job D. R. Borges <jobdrb@ig.com.br> */
diff --git a/drivers/media/rc/keymaps/rc-flydvb.c b/drivers/media/rc/keymaps/rc-flydvb.c
index a8b0f66edaa9..3a6bba311b08 100644
--- a/drivers/media/rc/keymaps/rc-flydvb.c
+++ b/drivers/media/rc/keymaps/rc-flydvb.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table flydvb[] = {
{ 0x01, KEY_ZOOM }, /* Full Screen */
diff --git a/drivers/media/rc/keymaps/rc-flyvideo.c b/drivers/media/rc/keymaps/rc-flyvideo.c
index 5bbe68371756..bf9da584643b 100644
--- a/drivers/media/rc/keymaps/rc-flyvideo.c
+++ b/drivers/media/rc/keymaps/rc-flyvideo.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table flyvideo[] = {
{ 0x0f, KEY_0 },
diff --git a/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c b/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
index c80b25c65b57..2f0970fe7832 100644
--- a/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
+++ b/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* DViCO FUSION HDTV MCE remote */
diff --git a/drivers/media/rc/keymaps/rc-gadmei-rm008z.c b/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
index 068c9ead98dd..0e98ec467c34 100644
--- a/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
+++ b/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* GADMEI UTV330+ RM008Z remote
Shine Liu <shinel@foxmail.com>
diff --git a/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c b/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
index cdbbed467926..a2e2faa1d1b3 100644
--- a/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
+++ b/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Remote control for the Genius TVGO A11MCE
diff --git a/drivers/media/rc/keymaps/rc-gotview7135.c b/drivers/media/rc/keymaps/rc-gotview7135.c
index a38bdde8c140..864614e19314 100644
--- a/drivers/media/rc/keymaps/rc-gotview7135.c
+++ b/drivers/media/rc/keymaps/rc-gotview7135.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Mike Baikov <mike@baikov.com> */
diff --git a/drivers/media/rc/keymaps/rc-hauppauge.c b/drivers/media/rc/keymaps/rc-hauppauge.c
index cd3db7779772..e51c6163378b 100644
--- a/drivers/media/rc/keymaps/rc-hauppauge.c
+++ b/drivers/media/rc/keymaps/rc-hauppauge.c
@@ -17,6 +17,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Hauppauge:the newer, gray remotes (seems there are multiple
diff --git a/drivers/media/rc/keymaps/rc-imon-mce.c b/drivers/media/rc/keymaps/rc-imon-mce.c
index 0ea2aa190d81..124c7228ba8c 100644
--- a/drivers/media/rc/keymaps/rc-imon-mce.c
+++ b/drivers/media/rc/keymaps/rc-imon-mce.c
@@ -10,6 +10,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* mce-mode imon mce remote key table */
static struct rc_map_table imon_mce[] = {
diff --git a/drivers/media/rc/keymaps/rc-imon-pad.c b/drivers/media/rc/keymaps/rc-imon-pad.c
index 75d3843fdc30..999c6295c70e 100644
--- a/drivers/media/rc/keymaps/rc-imon-pad.c
+++ b/drivers/media/rc/keymaps/rc-imon-pad.c
@@ -10,6 +10,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* standard imon remote key table, which isn't really entirely
diff --git a/drivers/media/rc/keymaps/rc-iodata-bctv7e.c b/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
index 1f59e163f75d..34540dfc3df5 100644
--- a/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
+++ b/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* IO-DATA BCTV7E Remote */
diff --git a/drivers/media/rc/keymaps/rc-kaiomy.c b/drivers/media/rc/keymaps/rc-kaiomy.c
index f31dc5c1ad9c..4264a787c150 100644
--- a/drivers/media/rc/keymaps/rc-kaiomy.c
+++ b/drivers/media/rc/keymaps/rc-kaiomy.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Kaiomy TVnPC U2
Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/rc/keymaps/rc-kworld-315u.c b/drivers/media/rc/keymaps/rc-kworld-315u.c
index 7f33edb47244..e48cd267dda6 100644
--- a/drivers/media/rc/keymaps/rc-kworld-315u.c
+++ b/drivers/media/rc/keymaps/rc-kworld-315u.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Kworld 315U
*/
diff --git a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
index 7fa17a369f2d..32998d6b787d 100644
--- a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
+++ b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Kworld Plus TV Analog Lite PCI IR
Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c b/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c
index 8faa54ff16e6..03d762d986ee 100644
--- a/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c
+++ b/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table leadtek_y04g0051[] = {
{ 0x0300, KEY_POWER2 },
diff --git a/drivers/media/rc/keymaps/rc-lirc.c b/drivers/media/rc/keymaps/rc-lirc.c
index e8e23e233c39..fbf08fa6f46e 100644
--- a/drivers/media/rc/keymaps/rc-lirc.c
+++ b/drivers/media/rc/keymaps/rc-lirc.c
@@ -10,6 +10,7 @@
*/
#include <media/rc-core.h>
+#include <linux/module.h>
static struct rc_map_table lirc[] = {
{ },
diff --git a/drivers/media/rc/keymaps/rc-lme2510.c b/drivers/media/rc/keymaps/rc-lme2510.c
index 129d3f9a461d..51f18bb50a37 100644
--- a/drivers/media/rc/keymaps/rc-lme2510.c
+++ b/drivers/media/rc/keymaps/rc-lme2510.c
@@ -10,6 +10,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table lme2510_rc[] = {
diff --git a/drivers/media/rc/keymaps/rc-manli.c b/drivers/media/rc/keymaps/rc-manli.c
index 23b2d04e7a9f..e7038bb71bf6 100644
--- a/drivers/media/rc/keymaps/rc-manli.c
+++ b/drivers/media/rc/keymaps/rc-manli.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Michael Tokarev <mjt@tls.msk.ru>
keytable is used by MANLI MTV00[0x0c] and BeholdTV 40[13] at
diff --git a/drivers/media/rc/keymaps/rc-medion-x10.c b/drivers/media/rc/keymaps/rc-medion-x10.c
new file mode 100644
index 000000000000..09e2cc01d110
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-medion-x10.c
@@ -0,0 +1,117 @@
+/*
+ * Medion X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * This file is based on a keytable provided by
+ * Jan Losinski <losinski@wh2.tu-dresden.de>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10[] = {
+ { 0xf12c, KEY_TV }, /* TV */
+ { 0xf22d, KEY_VCR }, /* VCR */
+ { 0xc904, KEY_DVD }, /* DVD */
+ { 0xcb06, KEY_AUDIO }, /* MUSIC */
+
+ { 0xf32e, KEY_RADIO }, /* RADIO */
+ { 0xca05, KEY_DIRECTORY }, /* PHOTO */
+ { 0xf42f, KEY_INFO }, /* TV-PREVIEW */
+ { 0xf530, KEY_LIST }, /* CHANNEL-LST */
+
+ { 0xe01b, KEY_SETUP }, /* SETUP */
+ { 0xf631, KEY_VIDEO }, /* VIDEO DESKTOP */
+
+ { 0xcd08, KEY_VOLUMEDOWN }, /* VOL - */
+ { 0xce09, KEY_VOLUMEUP }, /* VOL + */
+ { 0xd00b, KEY_CHANNELUP }, /* CHAN + */
+ { 0xd10c, KEY_CHANNELDOWN }, /* CHAN - */
+ { 0xc500, KEY_MUTE }, /* MUTE */
+
+ { 0xf732, KEY_RED }, /* red */
+ { 0xf833, KEY_GREEN }, /* green */
+ { 0xf934, KEY_YELLOW }, /* yellow */
+ { 0xfa35, KEY_BLUE }, /* blue */
+ { 0xdb16, KEY_TEXT }, /* TXT */
+
+ { 0xd20d, KEY_1 },
+ { 0xd30e, KEY_2 },
+ { 0xd40f, KEY_3 },
+ { 0xd510, KEY_4 },
+ { 0xd611, KEY_5 },
+ { 0xd712, KEY_6 },
+ { 0xd813, KEY_7 },
+ { 0xd914, KEY_8 },
+ { 0xda15, KEY_9 },
+ { 0xdc17, KEY_0 },
+ { 0xe11c, KEY_SEARCH }, /* TV/RAD, CH SRC */
+ { 0xe520, KEY_DELETE }, /* DELETE */
+
+ { 0xfb36, KEY_KEYBOARD }, /* RENAME */
+ { 0xdd18, KEY_SCREEN }, /* SNAPSHOT */
+
+ { 0xdf1a, KEY_UP }, /* up */
+ { 0xe722, KEY_DOWN }, /* down */
+ { 0xe21d, KEY_LEFT }, /* left */
+ { 0xe41f, KEY_RIGHT }, /* right */
+ { 0xe31e, KEY_OK }, /* OK */
+
+ { 0xfc37, KEY_SELECT }, /* ACQUIRE IMAGE */
+ { 0xfd38, KEY_EDIT }, /* EDIT IMAGE */
+
+ { 0xe924, KEY_REWIND }, /* rewind (<<) */
+ { 0xea25, KEY_PLAY }, /* play ( >) */
+ { 0xeb26, KEY_FORWARD }, /* forward (>>) */
+ { 0xec27, KEY_RECORD }, /* record ( o) */
+ { 0xed28, KEY_STOP }, /* stop ([]) */
+ { 0xee29, KEY_PAUSE }, /* pause ('') */
+
+ { 0xe621, KEY_PREVIOUS }, /* prev */
+ { 0xfe39, KEY_SWITCHVIDEOMODE }, /* F SCR */
+ { 0xe823, KEY_NEXT }, /* next */
+ { 0xde19, KEY_MENU }, /* MENU */
+ { 0xff3a, KEY_LANGUAGE }, /* AUDIO */
+
+ { 0xc702, KEY_POWER }, /* POWER */
+};
+
+static struct rc_map_list medion_x10_map = {
+ .map = {
+ .scan = medion_x10,
+ .size = ARRAY_SIZE(medion_x10),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_MEDION_X10,
+ }
+};
+
+static int __init init_rc_map_medion_x10(void)
+{
+ return rc_map_register(&medion_x10_map);
+}
+
+static void __exit exit_rc_map_medion_x10(void)
+{
+ rc_map_unregister(&medion_x10_map);
+}
+
+module_init(init_rc_map_medion_x10)
+module_exit(exit_rc_map_medion_x10)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-msi-digivox-ii.c b/drivers/media/rc/keymaps/rc-msi-digivox-ii.c
index 7b9a01b6e4cf..c64e9e30045d 100644
--- a/drivers/media/rc/keymaps/rc-msi-digivox-ii.c
+++ b/drivers/media/rc/keymaps/rc-msi-digivox-ii.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table msi_digivox_ii[] = {
{ 0x0002, KEY_2 },
diff --git a/drivers/media/rc/keymaps/rc-msi-digivox-iii.c b/drivers/media/rc/keymaps/rc-msi-digivox-iii.c
index ae9d06b39157..303a0b73175b 100644
--- a/drivers/media/rc/keymaps/rc-msi-digivox-iii.c
+++ b/drivers/media/rc/keymaps/rc-msi-digivox-iii.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* MSI DIGIVOX mini III */
/* Uses NEC extended 0x61d6. */
diff --git a/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c b/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
index 8e9969d1239b..c393d8a50bca 100644
--- a/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
+++ b/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
Keycodes for remote on the MSI TV@nywhere Plus. The controller IC on the card
diff --git a/drivers/media/rc/keymaps/rc-msi-tvanywhere.c b/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
index fdd213ff1adf..a7003d3a3c8a 100644
--- a/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
+++ b/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* MSI TV@nywhere MASTER remote */
diff --git a/drivers/media/rc/keymaps/rc-nebula.c b/drivers/media/rc/keymaps/rc-nebula.c
index ddae20e9cd96..3f0ddd7afd30 100644
--- a/drivers/media/rc/keymaps/rc-nebula.c
+++ b/drivers/media/rc/keymaps/rc-nebula.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table nebula[] = {
{ 0x00, KEY_0 },
diff --git a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
index 26f114c5c0de..f3b86c8db679 100644
--- a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
+++ b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Terratec Cinergy Hybrid T USB XS FM
Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/rc/keymaps/rc-norwood.c b/drivers/media/rc/keymaps/rc-norwood.c
index f9f2fa2819b8..9e65f07157ab 100644
--- a/drivers/media/rc/keymaps/rc-norwood.c
+++ b/drivers/media/rc/keymaps/rc-norwood.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Norwood Micro (non-Pro) TV Tuner
By Peter Naulls <peter@chocky.org>
diff --git a/drivers/media/rc/keymaps/rc-npgtech.c b/drivers/media/rc/keymaps/rc-npgtech.c
index 4aa588bf6d69..65d0cfc3c33b 100644
--- a/drivers/media/rc/keymaps/rc-npgtech.c
+++ b/drivers/media/rc/keymaps/rc-npgtech.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table npgtech[] = {
{ 0x1d, KEY_SWITCHVIDEOMODE }, /* switch inputs */
diff --git a/drivers/media/rc/keymaps/rc-pctv-sedna.c b/drivers/media/rc/keymaps/rc-pctv-sedna.c
index 7cdef6e6cc0f..bf2cbdfe2e32 100644
--- a/drivers/media/rc/keymaps/rc-pctv-sedna.c
+++ b/drivers/media/rc/keymaps/rc-pctv-sedna.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Mapping for the 28 key remote control as seen at
http://www.sednacomputer.com/photo/cardbus-tv.jpg
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-color.c b/drivers/media/rc/keymaps/rc-pinnacle-color.c
index 23b8c505c6aa..b46cd8fe6438 100644
--- a/drivers/media/rc/keymaps/rc-pinnacle-color.c
+++ b/drivers/media/rc/keymaps/rc-pinnacle-color.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table pinnacle_color[] = {
{ 0x59, KEY_MUTE },
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-grey.c b/drivers/media/rc/keymaps/rc-pinnacle-grey.c
index 6ba8c368d10a..d525df9ad868 100644
--- a/drivers/media/rc/keymaps/rc-pinnacle-grey.c
+++ b/drivers/media/rc/keymaps/rc-pinnacle-grey.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table pinnacle_grey[] = {
{ 0x3a, KEY_0 },
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c b/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
index 8d558ae63456..a4603d035374 100644
--- a/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
+++ b/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Pinnacle PCTV HD 800i mini remote */
@@ -20,6 +21,7 @@ static struct rc_map_table pinnacle_pctv_hd[] = {
{ 0x0701, KEY_MENU }, /* Pinnacle logo */
{ 0x0739, KEY_POWER },
{ 0x0703, KEY_VOLUMEUP },
+ { 0x0705, KEY_OK },
{ 0x0709, KEY_VOLUMEDOWN },
{ 0x0706, KEY_CHANNELUP },
{ 0x070c, KEY_CHANNELDOWN },
diff --git a/drivers/media/rc/keymaps/rc-pixelview-002t.c b/drivers/media/rc/keymaps/rc-pixelview-002t.c
index e5ab071f635a..33eb64333c6f 100644
--- a/drivers/media/rc/keymaps/rc-pixelview-002t.c
+++ b/drivers/media/rc/keymaps/rc-pixelview-002t.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Keytable for 002-T IR remote provided together with Pixelview
diff --git a/drivers/media/rc/keymaps/rc-pixelview-mk12.c b/drivers/media/rc/keymaps/rc-pixelview-mk12.c
index 125fc3949c15..21f4dd25c2ec 100644
--- a/drivers/media/rc/keymaps/rc-pixelview-mk12.c
+++ b/drivers/media/rc/keymaps/rc-pixelview-mk12.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Keytable for MK-F12 IR remote provided together with Pixelview
diff --git a/drivers/media/rc/keymaps/rc-pixelview-new.c b/drivers/media/rc/keymaps/rc-pixelview-new.c
index bd78d6ac1e16..f944ad2cac2b 100644
--- a/drivers/media/rc/keymaps/rc-pixelview-new.c
+++ b/drivers/media/rc/keymaps/rc-pixelview-new.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/rc/keymaps/rc-pixelview.c b/drivers/media/rc/keymaps/rc-pixelview.c
index 06187e7db446..a6020eea7b95 100644
--- a/drivers/media/rc/keymaps/rc-pixelview.c
+++ b/drivers/media/rc/keymaps/rc-pixelview.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table pixelview[] = {
diff --git a/drivers/media/rc/keymaps/rc-powercolor-real-angel.c b/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
index 5f9d546a86c4..e74c571a5e44 100644
--- a/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
+++ b/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Remote control for Powercolor Real Angel 330
diff --git a/drivers/media/rc/keymaps/rc-proteus-2309.c b/drivers/media/rc/keymaps/rc-proteus-2309.c
index 8a3a643879d4..adee8035ce96 100644
--- a/drivers/media/rc/keymaps/rc-proteus-2309.c
+++ b/drivers/media/rc/keymaps/rc-proteus-2309.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Michal Majchrowicz <mmajchrowicz@gmail.com> */
diff --git a/drivers/media/rc/keymaps/rc-purpletv.c b/drivers/media/rc/keymaps/rc-purpletv.c
index ef90296bfd68..722597a20e4a 100644
--- a/drivers/media/rc/keymaps/rc-purpletv.c
+++ b/drivers/media/rc/keymaps/rc-purpletv.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table purpletv[] = {
{ 0x03, KEY_POWER },
diff --git a/drivers/media/rc/keymaps/rc-pv951.c b/drivers/media/rc/keymaps/rc-pv951.c
index 5e8beee94de4..0105d63c07a9 100644
--- a/drivers/media/rc/keymaps/rc-pv951.c
+++ b/drivers/media/rc/keymaps/rc-pv951.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Mark Phalan <phalanm@o2.ie> */
diff --git a/drivers/media/rc/keymaps/rc-rc6-mce.c b/drivers/media/rc/keymaps/rc-rc6-mce.c
index 01b69bcc8666..753e43ec787b 100644
--- a/drivers/media/rc/keymaps/rc-rc6-mce.c
+++ b/drivers/media/rc/keymaps/rc-rc6-mce.c
@@ -13,6 +13,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table rc6_mce[] = {
@@ -29,7 +30,7 @@ static struct rc_map_table rc6_mce[] = {
{ 0x800f040a, KEY_DELETE },
{ 0x800f040b, KEY_ENTER },
- { 0x800f040c, KEY_POWER }, /* PC Power */
+ { 0x800f040c, KEY_SLEEP }, /* Formerly PC Power */
{ 0x800f040d, KEY_MEDIA }, /* Windows MCE button */
{ 0x800f040e, KEY_MUTE },
{ 0x800f040f, KEY_INFO },
@@ -44,7 +45,6 @@ static struct rc_map_table rc6_mce[] = {
{ 0x800f0416, KEY_PLAY },
{ 0x800f0417, KEY_RECORD },
{ 0x800f0418, KEY_PAUSE },
- { 0x800f046e, KEY_PLAYPAUSE },
{ 0x800f0419, KEY_STOP },
{ 0x800f041a, KEY_NEXT },
{ 0x800f041b, KEY_PREVIOUS },
diff --git a/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c b/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
index 6813d1102118..073694d50f49 100644
--- a/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
+++ b/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Zogis Real Audio 220 - 32 keys IR */
diff --git a/drivers/media/rc/keymaps/rc-snapstream-firefly.c b/drivers/media/rc/keymaps/rc-snapstream-firefly.c
new file mode 100644
index 000000000000..ef146520931c
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-snapstream-firefly.c
@@ -0,0 +1,107 @@
+/*
+ * SnapStream Firefly X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table snapstream_firefly[] = {
+ { 0xf12c, KEY_ZOOM }, /* Maximize */
+ { 0xc702, KEY_CLOSE },
+
+ { 0xd20d, KEY_1 },
+ { 0xd30e, KEY_2 },
+ { 0xd40f, KEY_3 },
+ { 0xd510, KEY_4 },
+ { 0xd611, KEY_5 },
+ { 0xd712, KEY_6 },
+ { 0xd813, KEY_7 },
+ { 0xd914, KEY_8 },
+ { 0xda15, KEY_9 },
+ { 0xdc17, KEY_0 },
+ { 0xdb16, KEY_BACK },
+ { 0xdd18, KEY_KPENTER }, /* ent */
+
+ { 0xce09, KEY_VOLUMEUP },
+ { 0xcd08, KEY_VOLUMEDOWN },
+ { 0xcf0a, KEY_MUTE },
+ { 0xd00b, KEY_CHANNELUP },
+ { 0xd10c, KEY_CHANNELDOWN },
+ { 0xc500, KEY_VENDOR }, /* firefly */
+
+ { 0xf32e, KEY_INFO },
+ { 0xf42f, KEY_OPTION },
+
+ { 0xe21d, KEY_LEFT },
+ { 0xe41f, KEY_RIGHT },
+ { 0xe722, KEY_DOWN },
+ { 0xdf1a, KEY_UP },
+ { 0xe31e, KEY_OK },
+
+ { 0xe11c, KEY_MENU },
+ { 0xe520, KEY_EXIT },
+
+ { 0xec27, KEY_RECORD },
+ { 0xea25, KEY_PLAY },
+ { 0xed28, KEY_STOP },
+ { 0xe924, KEY_REWIND },
+ { 0xeb26, KEY_FORWARD },
+ { 0xee29, KEY_PAUSE },
+ { 0xf02b, KEY_PREVIOUS },
+ { 0xef2a, KEY_NEXT },
+
+ { 0xcb06, KEY_AUDIO }, /* Music */
+ { 0xca05, KEY_IMAGES }, /* Photos */
+ { 0xc904, KEY_DVD },
+ { 0xc803, KEY_TV },
+ { 0xcc07, KEY_VIDEO },
+
+ { 0xc601, KEY_HELP },
+ { 0xf22d, KEY_MODE }, /* Mouse */
+
+ { 0xde19, KEY_A },
+ { 0xe01b, KEY_B },
+ { 0xe621, KEY_C },
+ { 0xe823, KEY_D },
+};
+
+static struct rc_map_list snapstream_firefly_map = {
+ .map = {
+ .scan = snapstream_firefly,
+ .size = ARRAY_SIZE(snapstream_firefly),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_SNAPSTREAM_FIREFLY,
+ }
+};
+
+static int __init init_rc_map_snapstream_firefly(void)
+{
+ return rc_map_register(&snapstream_firefly_map);
+}
+
+static void __exit exit_rc_map_snapstream_firefly(void)
+{
+ rc_map_unregister(&snapstream_firefly_map);
+}
+
+module_init(init_rc_map_snapstream_firefly)
+module_exit(exit_rc_map_snapstream_firefly)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-streamzap.c b/drivers/media/rc/keymaps/rc-streamzap.c
index 92cc10d2f9cd..f9a07578d985 100644
--- a/drivers/media/rc/keymaps/rc-streamzap.c
+++ b/drivers/media/rc/keymaps/rc-streamzap.c
@@ -10,6 +10,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table streamzap[] = {
/*
diff --git a/drivers/media/rc/keymaps/rc-tbs-nec.c b/drivers/media/rc/keymaps/rc-tbs-nec.c
index 7242ee66f6e0..5039be782bc5 100644
--- a/drivers/media/rc/keymaps/rc-tbs-nec.c
+++ b/drivers/media/rc/keymaps/rc-tbs-nec.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table tbs_nec[] = {
{ 0x84, KEY_POWER2}, /* power */
diff --git a/drivers/media/rc/keymaps/rc-technisat-usb2.c b/drivers/media/rc/keymaps/rc-technisat-usb2.c
index 4afe5774f192..f9733bb289d6 100644
--- a/drivers/media/rc/keymaps/rc-technisat-usb2.c
+++ b/drivers/media/rc/keymaps/rc-technisat-usb2.c
@@ -30,6 +30,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table technisat_usb2[] = {
{0x0a0c, KEY_POWER},
diff --git a/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
index bc38e34b9fda..53629fb0151f 100644
--- a/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
+++ b/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Terratec Cinergy Hybrid T USB XS
Devin Heitmueller <dheitmueller@linuxtv.org>
diff --git a/drivers/media/rc/keymaps/rc-terratec-slim-2.c b/drivers/media/rc/keymaps/rc-terratec-slim-2.c
index 44093918cf03..4c149ef712dc 100644
--- a/drivers/media/rc/keymaps/rc-terratec-slim-2.c
+++ b/drivers/media/rc/keymaps/rc-terratec-slim-2.c
@@ -20,6 +20,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* TerraTec slim remote, 6 rows, 3 columns.
diff --git a/drivers/media/rc/keymaps/rc-terratec-slim.c b/drivers/media/rc/keymaps/rc-terratec-slim.c
index 1abafa5fd303..3d8a19cdb5a2 100644
--- a/drivers/media/rc/keymaps/rc-terratec-slim.c
+++ b/drivers/media/rc/keymaps/rc-terratec-slim.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* TerraTec slim remote, 7 rows, 4 columns. */
/* Uses NEC extended 0x02bd. */
diff --git a/drivers/media/rc/keymaps/rc-tevii-nec.c b/drivers/media/rc/keymaps/rc-tevii-nec.c
index ef5ba3f32735..f2c3b75d8580 100644
--- a/drivers/media/rc/keymaps/rc-tevii-nec.c
+++ b/drivers/media/rc/keymaps/rc-tevii-nec.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table tevii_nec[] = {
{ 0x0a, KEY_POWER2},
diff --git a/drivers/media/rc/keymaps/rc-tivo.c b/drivers/media/rc/keymaps/rc-tivo.c
index 98ad085531fd..454e06295692 100644
--- a/drivers/media/rc/keymaps/rc-tivo.c
+++ b/drivers/media/rc/keymaps/rc-tivo.c
@@ -9,6 +9,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Initial mapping is for the TiVo remote included in the Nero LiquidTV bundle,
diff --git a/drivers/media/rc/keymaps/rc-total-media-in-hand.c b/drivers/media/rc/keymaps/rc-total-media-in-hand.c
index 20ac4e19fb3f..5b9f9ec13680 100644
--- a/drivers/media/rc/keymaps/rc-total-media-in-hand.c
+++ b/drivers/media/rc/keymaps/rc-total-media-in-hand.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Uses NEC extended 0x02bd */
static struct rc_map_table total_media_in_hand[] = {
diff --git a/drivers/media/rc/keymaps/rc-trekstor.c b/drivers/media/rc/keymaps/rc-trekstor.c
index f8190ead2e32..f9a2e0fabb9f 100644
--- a/drivers/media/rc/keymaps/rc-trekstor.c
+++ b/drivers/media/rc/keymaps/rc-trekstor.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* TrekStor DVB-T USB Stick remote controller. */
/* Imported from af9015.h.
diff --git a/drivers/media/rc/keymaps/rc-tt-1500.c b/drivers/media/rc/keymaps/rc-tt-1500.c
index 295f3738e301..caeff85603e3 100644
--- a/drivers/media/rc/keymaps/rc-tt-1500.c
+++ b/drivers/media/rc/keymaps/rc-tt-1500.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* for the Technotrend 1500 bundled remotes (grey and black): */
diff --git a/drivers/media/rc/keymaps/rc-twinhan1027.c b/drivers/media/rc/keymaps/rc-twinhan1027.c
index 8bf8df64b081..509299b90c90 100644
--- a/drivers/media/rc/keymaps/rc-twinhan1027.c
+++ b/drivers/media/rc/keymaps/rc-twinhan1027.c
@@ -1,4 +1,5 @@
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table twinhan_vp1027[] = {
{ 0x16, KEY_POWER2 },
diff --git a/drivers/media/rc/keymaps/rc-videomate-m1f.c b/drivers/media/rc/keymaps/rc-videomate-m1f.c
index 4994d405c0a1..3bd1de1f585c 100644
--- a/drivers/media/rc/keymaps/rc-videomate-m1f.c
+++ b/drivers/media/rc/keymaps/rc-videomate-m1f.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table videomate_m1f[] = {
{ 0x01, KEY_POWER },
diff --git a/drivers/media/rc/keymaps/rc-videomate-s350.c b/drivers/media/rc/keymaps/rc-videomate-s350.c
index 9e474a6024e5..8bfc3e8d909d 100644
--- a/drivers/media/rc/keymaps/rc-videomate-s350.c
+++ b/drivers/media/rc/keymaps/rc-videomate-s350.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table videomate_s350[] = {
{ 0x00, KEY_TV},
diff --git a/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c b/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
index 5f2a46e1f8f6..390ce9431b35 100644
--- a/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
+++ b/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table videomate_tv_pvr[] = {
{ 0x14, KEY_MUTE },
diff --git a/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c b/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
index bd8d021f40aa..2852bf705064 100644
--- a/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
+++ b/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Leadtek Winfast TV USB II Deluxe remote
Magnus Alm <magnus.alm@gmail.com>
diff --git a/drivers/media/rc/keymaps/rc-winfast.c b/drivers/media/rc/keymaps/rc-winfast.c
index d8a34c14676a..2df1cba23600 100644
--- a/drivers/media/rc/keymaps/rc-winfast.c
+++ b/drivers/media/rc/keymaps/rc-winfast.c
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Table for Leadtek Winfast Remote Controls - used by both bttv and cx88 */
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 06dfe0957b5e..60d3c1e09712 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -1,7 +1,7 @@
/*
* Driver for USB Windows Media Center Ed. eHome Infrared Transceivers
*
- * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com>
+ * Copyright (c) 2010-2011, Jarod Wilson <jarod@redhat.com>
*
* Based on the original lirc_mceusb and lirc_mceusb2 drivers, by Dan
* Conti, Martin Blatter and Daniel Melander, the latter of which was
@@ -15,6 +15,11 @@
* Jon Smirl, which included enhancements and simplifications to the
* incoming IR buffer parsing routines.
*
+ * Updated in July of 2011 with the aid of Microsoft's official
+ * remote/transceiver requirements and specification document, found at
+ * download.microsoft.com, title
+ * Windows-Media-Center-RC-IR-Collection-Green-Button-Specification-03-08-2011-V2.pdf
+ *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,10 +42,11 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
+#include <linux/pm_wakeup.h>
#include <media/rc-core.h>
-#define DRIVER_VERSION "1.91"
-#define DRIVER_AUTHOR "Jarod Wilson <jarod@wilsonet.com>"
+#define DRIVER_VERSION "1.92"
+#define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>"
#define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \
"device driver"
#define DRIVER_NAME "mceusb"
@@ -63,43 +69,90 @@
#define MCE_PULSE_MASK 0x7f /* Pulse mask */
#define MCE_MAX_PULSE_LENGTH 0x7f /* Longest transmittable pulse symbol */
-#define MCE_HW_CMD_HEADER 0xff /* MCE hardware command header */
-#define MCE_COMMAND_HEADER 0x9f /* MCE command header */
-#define MCE_COMMAND_MASK 0xe0 /* Mask out command bits */
-#define MCE_COMMAND_NULL 0x00 /* These show up various places... */
-/* if buf[i] & MCE_COMMAND_MASK == 0x80 and buf[i] != MCE_COMMAND_HEADER,
- * then we're looking at a raw IR data sample */
-#define MCE_COMMAND_IRDATA 0x80
-#define MCE_PACKET_LENGTH_MASK 0x1f /* Packet length mask */
-
-/* Sub-commands, which follow MCE_COMMAND_HEADER or MCE_HW_CMD_HEADER */
+/*
+ * The interface between the host and the IR hardware is command-response
+ * based. All commands and responses have a consistent format, where a lead
+ * byte always identifies the type of data following it. The lead byte has
+ * a port value in the 3 highest bits and a length value in the 5 lowest
+ * bits.
+ *
+ * The length field is overloaded, with a value of 11111 indicating that the
+ * following byte is a command or response code, and the length of the entire
+ * message is determined by the code. If the length field is not 11111, then
+ * it specifies the number of bytes of port data that follow.
+ */
+#define MCE_CMD 0x1f
+#define MCE_PORT_IR 0x4 /* (0x4 << 5) | MCE_CMD = 0x9f */
+#define MCE_PORT_SYS 0x7 /* (0x7 << 5) | MCE_CMD = 0xff */
+#define MCE_PORT_SER 0x6 /* 0xc0 thru 0xdf flush & 0x1f bytes */
+#define MCE_PORT_MASK 0xe0 /* Mask out command bits */
+
+/* Command port headers */
+#define MCE_CMD_PORT_IR 0x9f /* IR-related cmd/rsp */
+#define MCE_CMD_PORT_SYS 0xff /* System (non-IR) device cmd/rsp */
+
+/* Commands that set device state (2-4 bytes in length) */
+#define MCE_CMD_RESET 0xfe /* Reset device, 2 bytes */
+#define MCE_CMD_RESUME 0xaa /* Resume device after error, 2 bytes */
+#define MCE_CMD_SETIRCFS 0x06 /* Set tx carrier, 4 bytes */
+#define MCE_CMD_SETIRTIMEOUT 0x0c /* Set timeout, 4 bytes */
+#define MCE_CMD_SETIRTXPORTS 0x08 /* Set tx ports, 3 bytes */
+#define MCE_CMD_SETIRRXPORTEN 0x14 /* Set rx ports, 3 bytes */
+#define MCE_CMD_FLASHLED 0x23 /* Flash receiver LED, 2 bytes */
+
+/* Commands that query device state (all 2 bytes, unless noted) */
+#define MCE_CMD_GETIRCFS 0x07 /* Get carrier */
+#define MCE_CMD_GETIRTIMEOUT 0x0d /* Get timeout */
+#define MCE_CMD_GETIRTXPORTS 0x13 /* Get tx ports */
+#define MCE_CMD_GETIRRXPORTEN 0x15 /* Get rx ports */
+#define MCE_CMD_GETPORTSTATUS 0x11 /* Get tx port status, 3 bytes */
+#define MCE_CMD_GETIRNUMPORTS 0x16 /* Get number of ports */
+#define MCE_CMD_GETWAKESOURCE 0x17 /* Get wake source */
+#define MCE_CMD_GETEMVER 0x22 /* Get emulator interface version */
+#define MCE_CMD_GETDEVDETAILS 0x21 /* Get device details (em ver2 only) */
+#define MCE_CMD_GETWAKESUPPORT 0x20 /* Get wake details (em ver2 only) */
+#define MCE_CMD_GETWAKEVERSION 0x18 /* Get wake pattern (em ver2 only) */
+
+/* Misc commands */
+#define MCE_CMD_NOP 0xff /* No operation */
+
+/* Responses to commands (non-error cases) */
+#define MCE_RSP_EQIRCFS 0x06 /* tx carrier, 4 bytes */
+#define MCE_RSP_EQIRTIMEOUT 0x0c /* rx timeout, 4 bytes */
+#define MCE_RSP_GETWAKESOURCE 0x17 /* wake source, 3 bytes */
+#define MCE_RSP_EQIRTXPORTS 0x08 /* tx port mask, 3 bytes */
+#define MCE_RSP_EQIRRXPORTEN 0x14 /* rx port mask, 3 bytes */
+#define MCE_RSP_GETPORTSTATUS 0x11 /* tx port status, 7 bytes */
+#define MCE_RSP_EQIRRXCFCNT 0x15 /* rx carrier count, 4 bytes */
+#define MCE_RSP_EQIRNUMPORTS 0x16 /* number of ports, 4 bytes */
+#define MCE_RSP_EQWAKESUPPORT 0x20 /* wake capabilities, 3 bytes */
+#define MCE_RSP_EQWAKEVERSION 0x18 /* wake pattern details, 6 bytes */
+#define MCE_RSP_EQDEVDETAILS 0x21 /* device capabilities, 3 bytes */
+#define MCE_RSP_EQEMVER 0x22 /* emulator interface ver, 3 bytes */
+#define MCE_RSP_FLASHLED 0x23 /* success flashing LED, 2 bytes */
+
+/* Responses to error cases, must send MCE_CMD_RESUME to clear them */
+#define MCE_RSP_CMD_ILLEGAL 0xfe /* illegal command for port, 2 bytes */
+#define MCE_RSP_TX_TIMEOUT 0x81 /* tx timed out, 2 bytes */
+
+/* Misc commands/responses not defined in the MCE remote/transceiver spec */
#define MCE_CMD_SIG_END 0x01 /* End of signal */
#define MCE_CMD_PING 0x03 /* Ping device */
#define MCE_CMD_UNKNOWN 0x04 /* Unknown */
#define MCE_CMD_UNKNOWN2 0x05 /* Unknown */
-#define MCE_CMD_S_CARRIER 0x06 /* Set TX carrier frequency */
-#define MCE_CMD_G_CARRIER 0x07 /* Get TX carrier frequency */
-#define MCE_CMD_S_TXMASK 0x08 /* Set TX port bitmask */
#define MCE_CMD_UNKNOWN3 0x09 /* Unknown */
#define MCE_CMD_UNKNOWN4 0x0a /* Unknown */
#define MCE_CMD_G_REVISION 0x0b /* Get hw/sw revision */
-#define MCE_CMD_S_TIMEOUT 0x0c /* Set RX timeout value */
-#define MCE_CMD_G_TIMEOUT 0x0d /* Get RX timeout value */
#define MCE_CMD_UNKNOWN5 0x0e /* Unknown */
#define MCE_CMD_UNKNOWN6 0x0f /* Unknown */
-#define MCE_CMD_G_RXPORTSTS 0x11 /* Get RX port status */
-#define MCE_CMD_G_TXMASK 0x13 /* Set TX port bitmask */
-#define MCE_CMD_S_RXSENSOR 0x14 /* Set RX sensor (std/learning) */
-#define MCE_CMD_G_RXSENSOR 0x15 /* Get RX sensor (std/learning) */
-#define MCE_RSP_PULSE_COUNT 0x15 /* RX pulse count (only if learning) */
-#define MCE_CMD_TX_PORTS 0x16 /* Get number of TX ports */
-#define MCE_CMD_G_WAKESRC 0x17 /* Get wake source */
-#define MCE_CMD_UNKNOWN7 0x18 /* Unknown */
#define MCE_CMD_UNKNOWN8 0x19 /* Unknown */
#define MCE_CMD_UNKNOWN9 0x1b /* Unknown */
-#define MCE_CMD_DEVICE_RESET 0xaa /* Reset the hardware */
-#define MCE_RSP_CMD_INVALID 0xfe /* Invalid command issued */
+#define MCE_CMD_NULL 0x00 /* These show up various places... */
+/* if buf[i] & MCE_PORT_MASK == 0x80 and buf[i] != MCE_CMD_PORT_IR,
+ * then we're looking at a raw IR data sample */
+#define MCE_COMMAND_IRDATA 0x80
+#define MCE_PACKET_LENGTH_MASK 0x1f /* Packet length mask */
/* module parameters */
#ifdef CONFIG_USB_DEBUG
@@ -388,48 +441,37 @@ struct mceusb_dev {
char name[128];
char phys[64];
enum mceusb_model_type model;
+
+ bool need_reset; /* flag to issue a device resume cmd */
+ u8 emver; /* emulator interface version */
+ u8 num_txports; /* number of transmit ports */
+ u8 num_rxports; /* number of receive sensors */
+ u8 txports_cabled; /* bitmask of transmitters with cable */
+ u8 rxports_active; /* bitmask of active receive sensors */
};
-/*
- * MCE Device Command Strings
- * Device command responses vary from device to device...
- * - DEVICE_RESET resets the hardware to its default state
- * - GET_REVISION fetches the hardware/software revision, common
- * replies are ff 0b 45 ff 1b 08 and ff 0b 50 ff 1b 42
- * - GET_CARRIER_FREQ gets the carrier mode and frequency of the
- * device, with replies in the form of 9f 06 MM FF, where MM is 0-3,
- * meaning clk of 10000000, 2500000, 625000 or 156250, and FF is
- * ((clk / frequency) - 1)
- * - GET_RX_TIMEOUT fetches the receiver timeout in units of 50us,
- * response in the form of 9f 0c msb lsb
- * - GET_TX_BITMASK fetches the transmitter bitmask, replies in
- * the form of 9f 08 bm, where bm is the bitmask
- * - GET_RX_SENSOR fetches the RX sensor setting -- long-range
- * general use one or short-range learning one, in the form of
- * 9f 14 ss, where ss is either 01 for long-range or 02 for short
- * - SET_CARRIER_FREQ sets a new carrier mode and frequency
- * - SET_TX_BITMASK sets the transmitter bitmask
- * - SET_RX_TIMEOUT sets the receiver timeout
- * - SET_RX_SENSOR sets which receiver sensor to use
- */
-static char DEVICE_RESET[] = {MCE_COMMAND_NULL, MCE_HW_CMD_HEADER,
- MCE_CMD_DEVICE_RESET};
-static char GET_REVISION[] = {MCE_HW_CMD_HEADER, MCE_CMD_G_REVISION};
-static char GET_UNKNOWN[] = {MCE_HW_CMD_HEADER, MCE_CMD_UNKNOWN7};
-static char GET_UNKNOWN2[] = {MCE_COMMAND_HEADER, MCE_CMD_UNKNOWN2};
-static char GET_CARRIER_FREQ[] = {MCE_COMMAND_HEADER, MCE_CMD_G_CARRIER};
-static char GET_RX_TIMEOUT[] = {MCE_COMMAND_HEADER, MCE_CMD_G_TIMEOUT};
-static char GET_TX_BITMASK[] = {MCE_COMMAND_HEADER, MCE_CMD_G_TXMASK};
-static char GET_RX_SENSOR[] = {MCE_COMMAND_HEADER, MCE_CMD_G_RXSENSOR};
+/* MCE Device Command Strings, generally a port and command pair */
+static char DEVICE_RESUME[] = {MCE_CMD_NULL, MCE_CMD_PORT_SYS,
+ MCE_CMD_RESUME};
+static char GET_REVISION[] = {MCE_CMD_PORT_SYS, MCE_CMD_G_REVISION};
+static char GET_EMVER[] = {MCE_CMD_PORT_SYS, MCE_CMD_GETEMVER};
+static char GET_WAKEVERSION[] = {MCE_CMD_PORT_SYS, MCE_CMD_GETWAKEVERSION};
+static char FLASH_LED[] = {MCE_CMD_PORT_SYS, MCE_CMD_FLASHLED};
+static char GET_UNKNOWN2[] = {MCE_CMD_PORT_IR, MCE_CMD_UNKNOWN2};
+static char GET_CARRIER_FREQ[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRCFS};
+static char GET_RX_TIMEOUT[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRTIMEOUT};
+static char GET_NUM_PORTS[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRNUMPORTS};
+static char GET_TX_BITMASK[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRTXPORTS};
+static char GET_RX_SENSOR[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRRXPORTEN};
/* sub in desired values in lower byte or bytes for full command */
/* FIXME: make use of these for transmit.
-static char SET_CARRIER_FREQ[] = {MCE_COMMAND_HEADER,
- MCE_CMD_S_CARRIER, 0x00, 0x00};
-static char SET_TX_BITMASK[] = {MCE_COMMAND_HEADER, MCE_CMD_S_TXMASK, 0x00};
-static char SET_RX_TIMEOUT[] = {MCE_COMMAND_HEADER,
- MCE_CMD_S_TIMEOUT, 0x00, 0x00};
-static char SET_RX_SENSOR[] = {MCE_COMMAND_HEADER,
- MCE_CMD_S_RXSENSOR, 0x00};
+static char SET_CARRIER_FREQ[] = {MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRCFS, 0x00, 0x00};
+static char SET_TX_BITMASK[] = {MCE_CMD_PORT_IR, MCE_CMD_SETIRTXPORTS, 0x00};
+static char SET_RX_TIMEOUT[] = {MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRTIMEOUT, 0x00, 0x00};
+static char SET_RX_SENSOR[] = {MCE_CMD_PORT_IR,
+ MCE_RSP_EQIRRXPORTEN, 0x00};
*/
static int mceusb_cmdsize(u8 cmd, u8 subcmd)
@@ -437,27 +479,33 @@ static int mceusb_cmdsize(u8 cmd, u8 subcmd)
int datasize = 0;
switch (cmd) {
- case MCE_COMMAND_NULL:
- if (subcmd == MCE_HW_CMD_HEADER)
+ case MCE_CMD_NULL:
+ if (subcmd == MCE_CMD_PORT_SYS)
datasize = 1;
break;
- case MCE_HW_CMD_HEADER:
+ case MCE_CMD_PORT_SYS:
switch (subcmd) {
+ case MCE_RSP_EQWAKEVERSION:
+ datasize = 4;
+ break;
case MCE_CMD_G_REVISION:
datasize = 2;
break;
+ case MCE_RSP_EQWAKESUPPORT:
+ datasize = 1;
+ break;
}
- case MCE_COMMAND_HEADER:
+ case MCE_CMD_PORT_IR:
switch (subcmd) {
case MCE_CMD_UNKNOWN:
- case MCE_CMD_S_CARRIER:
- case MCE_CMD_S_TIMEOUT:
- case MCE_RSP_PULSE_COUNT:
+ case MCE_RSP_EQIRCFS:
+ case MCE_RSP_EQIRTIMEOUT:
+ case MCE_RSP_EQIRRXCFCNT:
datasize = 2;
break;
case MCE_CMD_SIG_END:
- case MCE_CMD_S_TXMASK:
- case MCE_CMD_S_RXSENSOR:
+ case MCE_RSP_EQIRTXPORTS:
+ case MCE_RSP_EQIRRXPORTEN:
datasize = 1;
break;
}
@@ -470,9 +518,10 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
{
char codes[USB_BUFLEN * 3 + 1];
char inout[9];
- u8 cmd, subcmd, data1, data2;
+ u8 cmd, subcmd, data1, data2, data3, data4, data5;
struct device *dev = ir->dev;
int i, start, skip = 0;
+ u32 carrier, period;
if (!debug)
return;
@@ -500,18 +549,28 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
subcmd = buf[start + 1] & 0xff;
data1 = buf[start + 2] & 0xff;
data2 = buf[start + 3] & 0xff;
+ data3 = buf[start + 4] & 0xff;
+ data4 = buf[start + 5] & 0xff;
+ data5 = buf[start + 6] & 0xff;
switch (cmd) {
- case MCE_COMMAND_NULL:
- if ((subcmd == MCE_HW_CMD_HEADER) &&
- (data1 == MCE_CMD_DEVICE_RESET))
- dev_info(dev, "Device reset requested\n");
+ case MCE_CMD_NULL:
+ if (subcmd == MCE_CMD_NULL)
+ break;
+ if ((subcmd == MCE_CMD_PORT_SYS) &&
+ (data1 == MCE_CMD_RESUME))
+ dev_info(dev, "Device resume requested\n");
else
dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
cmd, subcmd);
break;
- case MCE_HW_CMD_HEADER:
+ case MCE_CMD_PORT_SYS:
switch (subcmd) {
+ case MCE_RSP_EQEMVER:
+ if (!out)
+ dev_info(dev, "Emulator interface version %x\n",
+ data1);
+ break;
case MCE_CMD_G_REVISION:
if (len == 2)
dev_info(dev, "Get hw/sw rev?\n");
@@ -520,21 +579,35 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
"0x%02x 0x%02x\n", data1, data2,
buf[start + 4], buf[start + 5]);
break;
- case MCE_CMD_DEVICE_RESET:
- dev_info(dev, "Device reset requested\n");
+ case MCE_CMD_RESUME:
+ dev_info(dev, "Device resume requested\n");
+ break;
+ case MCE_RSP_CMD_ILLEGAL:
+ dev_info(dev, "Illegal PORT_SYS command\n");
+ break;
+ case MCE_RSP_EQWAKEVERSION:
+ if (!out)
+ dev_info(dev, "Wake version, proto: 0x%02x, "
+ "payload: 0x%02x, address: 0x%02x, "
+ "version: 0x%02x\n",
+ data1, data2, data3, data4);
break;
- case MCE_RSP_CMD_INVALID:
- dev_info(dev, "Previous command not supported\n");
+ case MCE_RSP_GETPORTSTATUS:
+ if (!out)
+ /* We use data1 + 1 here, to match hw labels */
+ dev_info(dev, "TX port %d: blaster is%s connected\n",
+ data1 + 1, data4 ? " not" : "");
+ break;
+ case MCE_CMD_FLASHLED:
+ dev_info(dev, "Attempting to flash LED\n");
break;
- case MCE_CMD_UNKNOWN7:
- case MCE_CMD_UNKNOWN9:
default:
dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
cmd, subcmd);
break;
}
break;
- case MCE_COMMAND_HEADER:
+ case MCE_CMD_PORT_IR:
switch (subcmd) {
case MCE_CMD_SIG_END:
dev_info(dev, "End of signal\n");
@@ -546,46 +619,55 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n",
data1, data2);
break;
- case MCE_CMD_S_CARRIER:
- dev_info(dev, "%s carrier mode and freq of "
- "0x%02x 0x%02x\n", inout, data1, data2);
+ case MCE_RSP_EQIRCFS:
+ period = DIV_ROUND_CLOSEST(
+ (1 << data1 * 2) * (data2 + 1), 10);
+ if (!period)
+ break;
+ carrier = (1000 * 1000) / period;
+ dev_info(dev, "%s carrier of %u Hz (period %uus)\n",
+ inout, carrier, period);
break;
- case MCE_CMD_G_CARRIER:
+ case MCE_CMD_GETIRCFS:
dev_info(dev, "Get carrier mode and freq\n");
break;
- case MCE_CMD_S_TXMASK:
+ case MCE_RSP_EQIRTXPORTS:
dev_info(dev, "%s transmit blaster mask of 0x%02x\n",
inout, data1);
break;
- case MCE_CMD_S_TIMEOUT:
- /* value is in units of 50us, so x*50/100 or x/2 ms */
+ case MCE_RSP_EQIRTIMEOUT:
+ /* value is in units of 50us, so x*50/1000 ms */
+ period = ((data1 << 8) | data2) * MCE_TIME_UNIT / 1000;
dev_info(dev, "%s receive timeout of %d ms\n",
- inout, ((data1 << 8) | data2) / 2);
+ inout, period);
break;
- case MCE_CMD_G_TIMEOUT:
+ case MCE_CMD_GETIRTIMEOUT:
dev_info(dev, "Get receive timeout\n");
break;
- case MCE_CMD_G_TXMASK:
+ case MCE_CMD_GETIRTXPORTS:
dev_info(dev, "Get transmit blaster mask\n");
break;
- case MCE_CMD_S_RXSENSOR:
+ case MCE_RSP_EQIRRXPORTEN:
dev_info(dev, "%s %s-range receive sensor in use\n",
inout, data1 == 0x02 ? "short" : "long");
break;
- case MCE_CMD_G_RXSENSOR:
- /* aka MCE_RSP_PULSE_COUNT */
+ case MCE_CMD_GETIRRXPORTEN:
+ /* aka MCE_RSP_EQIRRXCFCNT */
if (out)
dev_info(dev, "Get receive sensor\n");
else if (ir->learning_enabled)
dev_info(dev, "RX pulse count: %d\n",
((data1 << 8) | data2));
break;
- case MCE_RSP_CMD_INVALID:
- dev_info(dev, "Error! Hardware is likely wedged...\n");
+ case MCE_RSP_EQIRNUMPORTS:
+ if (out)
+ break;
+ dev_info(dev, "Num TX ports: %x, num RX ports: %x\n",
+ data1, data2);
+ break;
+ case MCE_RSP_CMD_ILLEGAL:
+ dev_info(dev, "Illegal PORT_IR command\n");
break;
- case MCE_CMD_UNKNOWN2:
- case MCE_CMD_UNKNOWN3:
- case MCE_CMD_UNKNOWN5:
default:
dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
cmd, subcmd);
@@ -598,8 +680,8 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
if (cmd == MCE_IRDATA_TRAILER)
dev_info(dev, "End of raw IR data\n");
- else if ((cmd != MCE_COMMAND_HEADER) &&
- ((cmd & MCE_COMMAND_MASK) == MCE_COMMAND_IRDATA))
+ else if ((cmd != MCE_CMD_PORT_IR) &&
+ ((cmd & MCE_PORT_MASK) == MCE_COMMAND_IRDATA))
dev_info(dev, "Raw IR data, %d pulse/space samples\n", ir->rem);
}
@@ -615,9 +697,6 @@ static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
if (ir) {
len = urb->actual_length;
- mce_dbg(ir->dev, "callback called (status=%d len=%d)\n",
- urb->status, len);
-
mceusb_dev_printdata(ir, urb->transfer_buffer, 0, len, true);
}
@@ -682,7 +761,16 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size)
{
+ int rsize = sizeof(DEVICE_RESUME);
+
+ if (ir->need_reset) {
+ ir->need_reset = false;
+ mce_request_packet(ir, DEVICE_RESUME, rsize, MCEUSB_TX);
+ msleep(10);
+ }
+
mce_request_packet(ir, data, size, MCEUSB_TX);
+ msleep(10);
}
static void mce_flush_rx_buffer(struct mceusb_dev *ir, int size)
@@ -691,26 +779,24 @@ static void mce_flush_rx_buffer(struct mceusb_dev *ir, int size)
}
/* Send data out the IR blaster port(s) */
-static int mceusb_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
+static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{
struct mceusb_dev *ir = dev->priv;
int i, ret = 0;
- int count, cmdcount = 0;
+ int cmdcount = 0;
unsigned char *cmdbuf; /* MCE command buffer */
long signal_duration = 0; /* Singnal length in us */
struct timeval start_time, end_time;
do_gettimeofday(&start_time);
- count = n / sizeof(int);
-
- cmdbuf = kzalloc(sizeof(int) * MCE_CMDBUF_SIZE, GFP_KERNEL);
+ cmdbuf = kzalloc(sizeof(unsigned) * MCE_CMDBUF_SIZE, GFP_KERNEL);
if (!cmdbuf)
return -ENOMEM;
/* MCE tx init header */
- cmdbuf[cmdcount++] = MCE_COMMAND_HEADER;
- cmdbuf[cmdcount++] = MCE_CMD_S_TXMASK;
+ cmdbuf[cmdcount++] = MCE_CMD_PORT_IR;
+ cmdbuf[cmdcount++] = MCE_CMD_SETIRTXPORTS;
cmdbuf[cmdcount++] = ir->tx_mask;
/* Generate mce packet data */
@@ -773,7 +859,7 @@ static int mceusb_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
out:
kfree(cmdbuf);
- return ret ? ret : n;
+ return ret ? ret : count;
}
/* Sets active IR outputs -- mce devices typically have two */
@@ -796,8 +882,8 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
struct mceusb_dev *ir = dev->priv;
int clk = 10000000;
int prescaler = 0, divisor = 0;
- unsigned char cmdbuf[4] = { MCE_COMMAND_HEADER,
- MCE_CMD_S_CARRIER, 0x00, 0x00 };
+ unsigned char cmdbuf[4] = { MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRCFS, 0x00, 0x00 };
/* Carrier has changed */
if (ir->carrier != carrier) {
@@ -845,17 +931,34 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index)
u8 lo = ir->buf_in[index + 2] & 0xff;
switch (ir->buf_in[index]) {
+ /* the one and only 5-byte return value command */
+ case MCE_RSP_GETPORTSTATUS:
+ if ((ir->buf_in[index + 4] & 0xff) == 0x00)
+ ir->txports_cabled |= 1 << hi;
+ break;
+
/* 2-byte return value commands */
- case MCE_CMD_S_TIMEOUT:
- ir->rc->timeout = US_TO_NS((hi << 8 | lo) / 2);
+ case MCE_RSP_EQIRTIMEOUT:
+ ir->rc->timeout = US_TO_NS((hi << 8 | lo) * MCE_TIME_UNIT);
+ break;
+ case MCE_RSP_EQIRNUMPORTS:
+ ir->num_txports = hi;
+ ir->num_rxports = lo;
break;
/* 1-byte return value commands */
- case MCE_CMD_S_TXMASK:
+ case MCE_RSP_EQEMVER:
+ ir->emver = hi;
+ break;
+ case MCE_RSP_EQIRTXPORTS:
ir->tx_mask = hi;
break;
- case MCE_CMD_S_RXSENSOR:
- ir->learning_enabled = (hi == 0x02);
+ case MCE_RSP_EQIRRXPORTEN:
+ ir->learning_enabled = ((hi & 0x02) == 0x02);
+ ir->rxports_active = hi;
+ break;
+ case MCE_RSP_CMD_ILLEGAL:
+ ir->need_reset = true;
break;
default:
break;
@@ -904,8 +1007,8 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
/* decode mce packets of the form (84),AA,BB,CC,DD */
/* IR data packets can span USB messages - rem */
ir->cmd = ir->buf_in[i];
- if ((ir->cmd == MCE_COMMAND_HEADER) ||
- ((ir->cmd & MCE_COMMAND_MASK) !=
+ if ((ir->cmd == MCE_CMD_PORT_IR) ||
+ ((ir->cmd & MCE_PORT_MASK) !=
MCE_COMMAND_IRDATA)) {
ir->parser_state = SUBCMD;
continue;
@@ -970,6 +1073,13 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
usb_submit_urb(urb, GFP_ATOMIC);
}
+static void mceusb_get_emulator_version(struct mceusb_dev *ir)
+{
+ /* If we get no reply or an illegal command reply, its ver 1, says MS */
+ ir->emver = 1;
+ mce_async_out(ir, GET_EMVER, sizeof(GET_EMVER));
+}
+
static void mceusb_gen1_init(struct mceusb_dev *ir)
{
int ret;
@@ -1012,8 +1122,8 @@ static void mceusb_gen1_init(struct mceusb_dev *ir)
0x0000, 0x0100, NULL, 0, HZ * 3);
mce_dbg(dev, "%s - retC = %d\n", __func__, ret);
- /* device reset */
- mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
+ /* device resume */
+ mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME));
/* get hw/sw revision? */
mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION));
@@ -1023,23 +1133,36 @@ static void mceusb_gen1_init(struct mceusb_dev *ir)
static void mceusb_gen2_init(struct mceusb_dev *ir)
{
- /* device reset */
- mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
+ /* device resume */
+ mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME));
/* get hw/sw revision? */
mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION));
- /* unknown what the next two actually return... */
- mce_async_out(ir, GET_UNKNOWN, sizeof(GET_UNKNOWN));
+ /* get wake version (protocol, key, address) */
+ mce_async_out(ir, GET_WAKEVERSION, sizeof(GET_WAKEVERSION));
+
+ /* unknown what this one actually returns... */
mce_async_out(ir, GET_UNKNOWN2, sizeof(GET_UNKNOWN2));
}
static void mceusb_get_parameters(struct mceusb_dev *ir)
{
+ int i;
+ unsigned char cmdbuf[3] = { MCE_CMD_PORT_SYS,
+ MCE_CMD_GETPORTSTATUS, 0x00 };
+
+ /* defaults, if the hardware doesn't support querying */
+ ir->num_txports = 2;
+ ir->num_rxports = 2;
+
+ /* get number of tx and rx ports */
+ mce_async_out(ir, GET_NUM_PORTS, sizeof(GET_NUM_PORTS));
+
/* get the carrier and frequency */
mce_async_out(ir, GET_CARRIER_FREQ, sizeof(GET_CARRIER_FREQ));
- if (!ir->flags.no_tx)
+ if (ir->num_txports && !ir->flags.no_tx)
/* get the transmitter bitmask */
mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK));
@@ -1048,6 +1171,19 @@ static void mceusb_get_parameters(struct mceusb_dev *ir)
/* get receiver sensor setting */
mce_async_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR));
+
+ for (i = 0; i < ir->num_txports; i++) {
+ cmdbuf[2] = i;
+ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+ }
+}
+
+static void mceusb_flash_led(struct mceusb_dev *ir)
+{
+ if (ir->emver < 2)
+ return;
+
+ mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED));
}
static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
@@ -1078,7 +1214,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
rc->priv = ir;
rc->driver_type = RC_DRIVER_IR_RAW;
rc->allowed_protos = RC_TYPE_ALL;
- rc->timeout = US_TO_NS(1000);
+ rc->timeout = MS_TO_NS(100);
if (!ir->flags.no_tx) {
rc->s_tx_mask = mceusb_set_tx_mask;
rc->s_tx_carrier = mceusb_set_tx_carrier;
@@ -1221,6 +1357,9 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
mce_dbg(&intf->dev, "Flushing receive buffers\n");
mce_flush_rx_buffer(ir, maxp);
+ /* figure out which firmware/emulator version this hardware has */
+ mceusb_get_emulator_version(ir);
+
/* initialize device */
if (ir->flags.microsoft_gen1)
mceusb_gen1_init(ir);
@@ -1229,13 +1368,23 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
mceusb_get_parameters(ir);
+ mceusb_flash_led(ir);
+
if (!ir->flags.no_tx)
mceusb_set_tx_mask(ir->rc, MCE_DEFAULT_TX_MASK);
usb_set_intfdata(intf, ir);
- dev_info(&intf->dev, "Registered %s on usb%d:%d\n", name,
- dev->bus->busnum, dev->devnum);
+ /* enable wake via this device */
+ device_set_wakeup_capable(ir->dev, true);
+ device_set_wakeup_enable(ir->dev, true);
+
+ dev_info(&intf->dev, "Registered %s with mce emulator interface "
+ "version %x\n", name, ir->emver);
+ dev_info(&intf->dev, "%x tx ports (0x%x cabled) and "
+ "%x rx sensors (0x%x active)\n",
+ ir->num_txports, ir->txports_cabled,
+ ir->num_rxports, ir->rxports_active);
return 0;
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 565f24c20d77..144f3f55d765 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -546,24 +546,18 @@ static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier)
* number may larger than TXFCONT (0xff). So in interrupt_handler, it has to
* set TXFCONT as 0xff, until buf_count less than 0xff.
*/
-static int nvt_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
+static int nvt_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned n)
{
struct nvt_dev *nvt = dev->priv;
unsigned long flags;
- size_t cur_count;
unsigned int i;
u8 iren;
int ret;
spin_lock_irqsave(&nvt->tx.lock, flags);
- if (n >= TX_BUF_LEN) {
- nvt->tx.buf_count = cur_count = TX_BUF_LEN;
- ret = TX_BUF_LEN;
- } else {
- nvt->tx.buf_count = cur_count = n;
- ret = n;
- }
+ ret = min((unsigned)(TX_BUF_LEN / sizeof(unsigned)), n);
+ nvt->tx.buf_count = (ret * sizeof(unsigned));
memcpy(nvt->tx.buf, txbuf, nvt->tx.buf_count);
@@ -624,7 +618,6 @@ static void nvt_dump_rx_buf(struct nvt_dev *nvt)
static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
{
DEFINE_IR_RAW_EVENT(rawir);
- unsigned int count;
u32 carrier;
u8 sample;
int i;
@@ -637,65 +630,38 @@ static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
if (nvt->carrier_detect_enabled)
carrier = nvt_rx_carrier_detect(nvt);
- count = nvt->pkts;
- nvt_dbg_verbose("Processing buffer of len %d", count);
+ nvt_dbg_verbose("Processing buffer of len %d", nvt->pkts);
init_ir_raw_event(&rawir);
- for (i = 0; i < count; i++) {
- nvt->pkts--;
+ for (i = 0; i < nvt->pkts; i++) {
sample = nvt->buf[i];
rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);
rawir.duration = US_TO_NS((sample & BUF_LEN_MASK)
* SAMPLE_PERIOD);
- if ((sample & BUF_LEN_MASK) == BUF_LEN_MASK) {
- if (nvt->rawir.pulse == rawir.pulse)
- nvt->rawir.duration += rawir.duration;
- else {
- nvt->rawir.duration = rawir.duration;
- nvt->rawir.pulse = rawir.pulse;
- }
- continue;
- }
-
- rawir.duration += nvt->rawir.duration;
-
- init_ir_raw_event(&nvt->rawir);
- nvt->rawir.duration = 0;
- nvt->rawir.pulse = rawir.pulse;
-
- if (sample == BUF_PULSE_BIT)
- rawir.pulse = false;
+ nvt_dbg("Storing %s with duration %d",
+ rawir.pulse ? "pulse" : "space", rawir.duration);
- if (rawir.duration) {
- nvt_dbg("Storing %s with duration %d",
- rawir.pulse ? "pulse" : "space",
- rawir.duration);
-
- ir_raw_event_store_with_filter(nvt->rdev, &rawir);
- }
+ ir_raw_event_store_with_filter(nvt->rdev, &rawir);
/*
* BUF_PULSE_BIT indicates end of IR data, BUF_REPEAT_BYTE
* indicates end of IR signal, but new data incoming. In both
* cases, it means we're ready to call ir_raw_event_handle
*/
- if ((sample == BUF_PULSE_BIT) && nvt->pkts) {
+ if ((sample == BUF_PULSE_BIT) && (i + 1 < nvt->pkts)) {
nvt_dbg("Calling ir_raw_event_handle (signal end)\n");
ir_raw_event_handle(nvt->rdev);
}
}
+ nvt->pkts = 0;
+
nvt_dbg("Calling ir_raw_event_handle (buffer empty)\n");
ir_raw_event_handle(nvt->rdev);
- if (nvt->pkts) {
- nvt_dbg("Odd, pkts should be 0 now... (its %u)", nvt->pkts);
- nvt->pkts = 0;
- }
-
nvt_dbg_verbose("%s done", __func__);
}
@@ -1054,7 +1020,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
spin_lock_init(&nvt->nvt_lock);
spin_lock_init(&nvt->tx.lock);
- init_ir_raw_event(&nvt->rawir);
ret = -EBUSY;
/* now claim resources */
@@ -1110,7 +1075,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
rdev->dev.parent = &pdev->dev;
rdev->driver_name = NVT_DRIVER_NAME;
rdev->map_name = RC_MAP_RC6_MCE;
- rdev->timeout = US_TO_NS(1000);
+ rdev->timeout = MS_TO_NS(100);
/* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD);
#if 0
diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h
index 1241fc89a36c..0d5e0872a2ea 100644
--- a/drivers/media/rc/nuvoton-cir.h
+++ b/drivers/media/rc/nuvoton-cir.h
@@ -67,7 +67,6 @@ static int debug;
struct nvt_dev {
struct pnp_dev *pdev;
struct rc_dev *rdev;
- struct ir_raw_event rawir;
spinlock_t nvt_lock;
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index 873b38789751..c6ca870e8b7e 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -84,6 +84,17 @@ struct ir_raw_event_ctrl {
unsigned count;
unsigned wanted_bits;
} rc5_sz;
+ struct mce_kbd_dec {
+ struct input_dev *idev;
+ struct timer_list rx_timeout;
+ char name[64];
+ char phys[64];
+ int state;
+ u8 header;
+ u32 body;
+ unsigned count;
+ unsigned wanted_bits;
+ } mce_kbd;
struct lirc_codec {
struct rc_dev *dev;
struct lirc_driver *drv;
@@ -151,42 +162,49 @@ void ir_raw_init(void);
#ifdef CONFIG_IR_NEC_DECODER_MODULE
#define load_nec_decode() request_module("ir-nec-decoder")
#else
-#define load_nec_decode() 0
+static inline void load_nec_decode(void) { }
#endif
/* from ir-rc5-decoder.c */
#ifdef CONFIG_IR_RC5_DECODER_MODULE
#define load_rc5_decode() request_module("ir-rc5-decoder")
#else
-#define load_rc5_decode() 0
+static inline void load_rc5_decode(void) { }
#endif
/* from ir-rc6-decoder.c */
#ifdef CONFIG_IR_RC6_DECODER_MODULE
#define load_rc6_decode() request_module("ir-rc6-decoder")
#else
-#define load_rc6_decode() 0
+static inline void load_rc6_decode(void) { }
#endif
/* from ir-jvc-decoder.c */
#ifdef CONFIG_IR_JVC_DECODER_MODULE
#define load_jvc_decode() request_module("ir-jvc-decoder")
#else
-#define load_jvc_decode() 0
+static inline void load_jvc_decode(void) { }
#endif
/* from ir-sony-decoder.c */
#ifdef CONFIG_IR_SONY_DECODER_MODULE
#define load_sony_decode() request_module("ir-sony-decoder")
#else
-#define load_sony_decode() 0
+static inline void load_sony_decode(void) { }
+#endif
+
+/* from ir-mce_kbd-decoder.c */
+#ifdef CONFIG_IR_MCE_KBD_DECODER_MODULE
+#define load_mce_kbd_decode() request_module("ir-mce_kbd-decoder")
+#else
+static inline void load_mce_kbd_decode(void) { }
#endif
/* from ir-lirc-codec.c */
#ifdef CONFIG_IR_LIRC_CODEC_MODULE
#define load_lirc_codec() request_module("ir-lirc-codec")
#else
-#define load_lirc_codec() 0
+static inline void load_lirc_codec(void) { }
#endif
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index cc846b2619cf..efc6a514348a 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -101,21 +101,14 @@ static int loop_set_rx_carrier_range(struct rc_dev *dev, u32 min, u32 max)
return 0;
}
-static int loop_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
+static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{
struct loopback_dev *lodev = dev->priv;
u32 rxmask;
- unsigned count;
unsigned total_duration = 0;
unsigned i;
DEFINE_IR_RAW_EVENT(rawir);
- if (n == 0 || n % sizeof(int)) {
- dprintk("invalid tx buffer size\n");
- return -EINVAL;
- }
-
- count = n / sizeof(int);
for (i = 0; i < count; i++)
total_duration += abs(txbuf[i]);
@@ -142,7 +135,7 @@ static int loop_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
for (i = 0; i < count; i++) {
rawir.pulse = i % 2 ? false : true;
- rawir.duration = abs(txbuf[i]) * 1000;
+ rawir.duration = txbuf[i] * 1000;
if (rawir.duration)
ir_raw_event_store_with_filter(dev, &rawir);
}
@@ -158,7 +151,7 @@ out:
/* Lirc expects this function to take as long as the total duration */
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(usecs_to_jiffies(total_duration));
- return n;
+ return count;
}
static void loop_set_idle(struct rc_dev *dev, bool enable)
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 3186ac7c2c10..29f900065d8a 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -18,6 +18,7 @@
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/device.h>
+#include <linux/module.h>
#include "rc-core-priv.h"
/* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */
@@ -735,6 +736,7 @@ static struct {
{ RC_TYPE_JVC, "jvc" },
{ RC_TYPE_SONY, "sony" },
{ RC_TYPE_RC5_SZ, "rc-5-sz" },
+ { RC_TYPE_MCE_KBD, "mce_kbd" },
{ RC_TYPE_LIRC, "lirc" },
{ RC_TYPE_OTHER, "other" },
};
@@ -927,10 +929,6 @@ out:
static void rc_dev_release(struct device *device)
{
- struct rc_dev *dev = to_rc_dev(device);
-
- kfree(dev);
- module_put(THIS_MODULE);
}
#define ADD_HOTPLUG_VAR(fmt, val...) \
@@ -944,6 +942,9 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
{
struct rc_dev *dev = to_rc_dev(device);
+ if (!dev || !dev->input_dev)
+ return -ENODEV;
+
if (dev->rc_map.name)
ADD_HOTPLUG_VAR("NAME=%s", dev->rc_map.name);
if (dev->driver_name)
@@ -1012,10 +1013,16 @@ EXPORT_SYMBOL_GPL(rc_allocate_device);
void rc_free_device(struct rc_dev *dev)
{
- if (dev) {
+ if (!dev)
+ return;
+
+ if (dev->input_dev)
input_free_device(dev->input_dev);
- put_device(&dev->dev);
- }
+
+ put_device(&dev->dev);
+
+ kfree(dev);
+ module_put(THIS_MODULE);
}
EXPORT_SYMBOL_GPL(rc_free_device);
@@ -1099,7 +1106,6 @@ int rc_register_device(struct rc_dev *dev)
if (rc < 0)
goto out_input;
}
- mutex_unlock(&dev->lock);
if (dev->change_protocol) {
rc = dev->change_protocol(dev, rc_map->rc_type);
@@ -1107,6 +1113,8 @@ int rc_register_device(struct rc_dev *dev)
goto out_raw;
}
+ mutex_unlock(&dev->lock);
+
IR_dprintk(1, "Registered rc%ld (driver: %s, remote: %s, mode %s)\n",
dev->devno,
dev->driver_name ? dev->driver_name : "unknown",
@@ -1141,14 +1149,18 @@ void rc_unregister_device(struct rc_dev *dev)
if (dev->driver_type == RC_DRIVER_IR_RAW)
ir_raw_event_unregister(dev);
+ /* Freeing the table should also call the stop callback */
+ ir_free_table(&dev->rc_map);
+ IR_dprintk(1, "Freed keycode table\n");
+
input_unregister_device(dev->input_dev);
dev->input_dev = NULL;
- ir_free_table(&dev->rc_map);
- IR_dprintk(1, "Freed keycode table\n");
+ device_del(&dev->dev);
- device_unregister(&dev->dev);
+ rc_free_device(dev);
}
+
EXPORT_SYMBOL_GPL(rc_unregister_device);
/*
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 5147767ccb78..61287fcca61a 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -195,19 +195,13 @@ struct redrat3_dev {
dma_addr_t dma_in;
dma_addr_t dma_out;
- /* true if write urb is busy */
- bool write_busy;
- /* wait for the write to finish */
- struct completion write_finished;
-
/* locks this structure */
struct mutex lock;
/* rx signal timeout timer */
struct timer_list rx_timeout;
+ u32 hw_timeout;
- /* Is the device currently receiving? */
- bool recv_in_progress;
/* is the detector enabled*/
bool det_enabled;
/* Is the device currently transmitting?*/
@@ -414,20 +408,10 @@ static u32 redrat3_us_to_len(u32 microsec)
}
-/* timer callback to send long trailing space on receive timeout */
+/* timer callback to send reset event */
static void redrat3_rx_timeout(unsigned long data)
{
struct redrat3_dev *rr3 = (struct redrat3_dev *)data;
- DEFINE_IR_RAW_EVENT(rawir);
-
- rawir.pulse = false;
- rawir.duration = rr3->rc->timeout;
- rr3_dbg(rr3->dev, "storing trailing space with duration %d\n",
- rawir.duration);
- ir_raw_event_store_with_filter(rr3->rc, &rawir);
-
- rr3_dbg(rr3->dev, "calling ir_raw_event_handle\n");
- ir_raw_event_handle(rr3->rc);
rr3_dbg(rr3->dev, "calling ir_raw_event_reset\n");
ir_raw_event_reset(rr3->rc);
@@ -438,7 +422,7 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3)
DEFINE_IR_RAW_EVENT(rawir);
struct redrat3_signal_header header;
struct device *dev;
- int i;
+ int i, trailer = 0;
unsigned long delay;
u32 mod_freq, single_len;
u16 *len_vals;
@@ -464,7 +448,8 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3)
if (!(header.length >= RR3_HEADER_LENGTH))
dev_warn(dev, "read returned less than rr3 header len\n");
- delay = usecs_to_jiffies(rr3->rc->timeout / 1000);
+ /* Make sure we reset the IR kfifo after a bit of inactivity */
+ delay = usecs_to_jiffies(rr3->hw_timeout);
mod_timer(&rr3->rx_timeout, jiffies + delay);
memcpy(&tmp32, sig_data + RR3_PAUSE_OFFSET, sizeof(tmp32));
@@ -506,9 +491,6 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3)
u16 val = len_vals[data_vals[i]];
single_len = redrat3_len_to_us((u32)be16_to_cpu(val));
- /* cap the value to IR_MAX_DURATION */
- single_len &= IR_MAX_DURATION;
-
/* we should always get pulse/space/pulse/space samples */
if (i % 2)
rawir.pulse = false;
@@ -516,6 +498,12 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3)
rawir.pulse = true;
rawir.duration = US_TO_NS(single_len);
+ /* Save initial pulse length to fudge trailer */
+ if (i == 0)
+ trailer = rawir.duration;
+ /* cap the value to IR_MAX_DURATION */
+ rawir.duration &= IR_MAX_DURATION;
+
rr3_dbg(dev, "storing %s with duration %d (i: %d)\n",
rawir.pulse ? "pulse" : "space", rawir.duration, i);
ir_raw_event_store_with_filter(rr3->rc, &rawir);
@@ -525,7 +513,10 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3)
if (i % 2) {
rawir.pulse = false;
/* this duration is made up, and may not be ideal... */
- rawir.duration = rr3->rc->timeout / 2;
+ if (trailer < US_TO_NS(1000))
+ rawir.duration = US_TO_NS(2800);
+ else
+ rawir.duration = trailer;
rr3_dbg(dev, "storing trailing space with duration %d\n",
rawir.duration);
ir_raw_event_store_with_filter(rr3->rc, &rawir);
@@ -629,36 +620,31 @@ static inline void redrat3_delete(struct redrat3_dev *rr3,
kfree(rr3);
}
-static u32 redrat3_get_timeout(struct device *dev,
- struct rc_dev *rc, struct usb_device *udev)
+static u32 redrat3_get_timeout(struct redrat3_dev *rr3)
{
u32 *tmp;
- u32 timeout = MS_TO_NS(150); /* a sane default, if things go haywire */
+ u32 timeout = MS_TO_US(150); /* a sane default, if things go haywire */
int len, ret, pipe;
len = sizeof(*tmp);
tmp = kzalloc(len, GFP_KERNEL);
if (!tmp) {
- dev_warn(dev, "Memory allocation faillure\n");
+ dev_warn(rr3->dev, "Memory allocation faillure\n");
return timeout;
}
- pipe = usb_rcvctrlpipe(udev, 0);
- ret = usb_control_msg(udev, pipe, RR3_GET_IR_PARAM,
+ pipe = usb_rcvctrlpipe(rr3->udev, 0);
+ ret = usb_control_msg(rr3->udev, pipe, RR3_GET_IR_PARAM,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
RR3_IR_IO_SIG_TIMEOUT, 0, tmp, len, HZ * 5);
if (ret != len) {
- dev_warn(dev, "Failed to read timeout from hardware\n");
+ dev_warn(rr3->dev, "Failed to read timeout from hardware\n");
return timeout;
}
- timeout = US_TO_NS(redrat3_len_to_us(be32_to_cpu(*tmp)));
- if (timeout < rc->min_timeout)
- timeout = rc->min_timeout;
- else if (timeout > rc->max_timeout)
- timeout = rc->max_timeout;
+ timeout = redrat3_len_to_us(be32_to_cpu(*tmp));
- rr3_dbg(dev, "Got timeout of %d ms\n", timeout / (1000 * 1000));
+ rr3_dbg(rr3->dev, "Got timeout of %d ms\n", timeout / 1000);
return timeout;
}
@@ -1110,9 +1096,7 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
rc->priv = rr3;
rc->driver_type = RC_DRIVER_IR_RAW;
rc->allowed_protos = RC_TYPE_ALL;
- rc->min_timeout = MS_TO_NS(RR3_RX_MIN_TIMEOUT);
- rc->max_timeout = MS_TO_NS(RR3_RX_MAX_TIMEOUT);
- rc->timeout = redrat3_get_timeout(dev, rc, rr3->udev);
+ rc->timeout = US_TO_NS(2750);
rc->tx_ir = redrat3_transmit_ir;
rc->s_tx_carrier = redrat3_set_tx_carrier;
rc->driver_name = DRIVER_NAME;
@@ -1186,7 +1170,7 @@ static int __devinit redrat3_dev_probe(struct usb_interface *intf,
rr3 = kzalloc(sizeof(*rr3), GFP_KERNEL);
if (rr3 == NULL) {
dev_err(dev, "Memory allocation failure\n");
- goto error;
+ goto no_endpoints;
}
rr3->dev = &intf->dev;
@@ -1242,6 +1226,9 @@ static int __devinit redrat3_dev_probe(struct usb_interface *intf,
if (retval < 0)
goto error;
+ /* store current hardware timeout, in us, will use for kfifo resets */
+ rr3->hw_timeout = redrat3_get_timeout(rr3);
+
/* default.. will get overridden by any sends with a freq defined */
rr3->carrier = 38000;
@@ -1280,6 +1267,7 @@ static void __devexit redrat3_dev_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
rc_unregister_device(rr3->rc);
+ del_timer_sync(&rr3->rx_timeout);
redrat3_delete(rr3, udev);
rr3_ftr(&intf->dev, "RedRat3 IR Transceiver now disconnected\n");
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 5d06b899e859..13f54b51194a 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -6,8 +6,8 @@
* could probably support others (Winbond WEC102X, NatSemi, etc)
* with minor modifications.
*
- * Original Author: David Härdeman <david@hardeman.nu>
- * Copyright (C) 2009 - 2010 David Härdeman <david@hardeman.nu>
+ * Original Author: David Härdeman <david@hardeman.nu>
+ * Copyright (C) 2009 - 2011 David Härdeman <david@hardeman.nu>
*
* Dedicated to my daughter Matilda, without whose loving attention this
* driver would have been finished in half the time and with a fraction
@@ -41,6 +41,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/pnp.h>
#include <linux/interrupt.h>
@@ -577,16 +579,12 @@ wbcir_txmask(struct rc_dev *dev, u32 mask)
}
static int
-wbcir_tx(struct rc_dev *dev, int *buf, u32 bufsize)
+wbcir_tx(struct rc_dev *dev, unsigned *buf, unsigned count)
{
struct wbcir_data *data = dev->priv;
- u32 count;
unsigned i;
unsigned long flags;
- /* bufsize has been sanity checked by the caller */
- count = bufsize / sizeof(int);
-
/* Not sure if this is possible, but better safe than sorry */
spin_lock_irqsave(&data->spinlock, flags);
if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
@@ -876,18 +874,8 @@ wbcir_init_hw(struct wbcir_data *data)
/* prescaler 1.0, tx/rx fifo lvl 16 */
outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2);
- /* Set baud divisor to generate one byte per bit/cell */
- switch (protocol) {
- case IR_PROTOCOL_RC5:
- outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL);
- break;
- case IR_PROTOCOL_RC6:
- outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL);
- break;
- case IR_PROTOCOL_NEC:
- outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL);
- break;
- }
+ /* Set baud divisor to sample every 10 us */
+ outb(0x0F, data->sbase + WBCIR_REG_SP3_BGDL);
outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
/* Set CEIR mode */
@@ -896,9 +884,9 @@ wbcir_init_hw(struct wbcir_data *data)
inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
- /* Disable RX demod, run-length encoding/decoding, set freq span */
+ /* Disable RX demod, enable run-length enc/dec, set freq span */
wbcir_select_bank(data, WBCIR_BANK_7);
- outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG);
+ outb(0x90, data->sbase + WBCIR_REG_SP3_RCCFG);
/* Disable timer */
wbcir_select_bank(data, WBCIR_BANK_4);
@@ -1169,12 +1157,12 @@ wbcir_init(void)
case IR_PROTOCOL_RC6:
break;
default:
- printk(KERN_ERR DRVNAME ": Invalid power-on protocol\n");
+ pr_err("Invalid power-on protocol\n");
}
ret = pnp_register_driver(&wbcir_driver);
if (ret)
- printk(KERN_ERR DRVNAME ": Unable to register driver\n");
+ pr_err("Unable to register driver\n");
return ret;
}
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index bb53de7fe408..b303a3f8a9f8 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -467,6 +467,20 @@ config VIDEO_OV7670
OV7670 VGA camera. It currently only works with the M88ALP01
controller.
+config VIDEO_MT9P031
+ tristate "Aptina MT9P031 support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Aptina
+ (Micron) mt9p031 5 Mpixel camera.
+
+config VIDEO_MT9T001
+ tristate "Aptina MT9T001 support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Aptina
+ (Micron) mt0t001 3 Mpixel camera.
+
config VIDEO_MT9V011
tristate "Micron mt9v011 sensor support"
depends on I2C && VIDEO_V4L2
@@ -489,6 +503,36 @@ config VIDEO_TCM825X
This is a driver for the Toshiba TCM825x VGA camera sensor.
It is used for example in Nokia N800.
+config VIDEO_SR030PC30
+ tristate "Siliconfile SR030PC30 sensor support"
+ depends on I2C && VIDEO_V4L2
+ ---help---
+ This driver supports SR030PC30 VGA camera from Siliconfile
+
+config VIDEO_NOON010PC30
+ tristate "Siliconfile NOON010PC30 sensor support"
+ depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This driver supports NOON010PC30 CIF camera from Siliconfile
+
+source "drivers/media/video/m5mols/Kconfig"
+
+config VIDEO_S5K6AA
+ tristate "Samsung S5K6AAFX sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
+ camera sensor with an embedded SoC image signal processor.
+
+comment "Flash devices"
+
+config VIDEO_ADP1653
+ tristate "ADP1653 flash support"
+ depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+ ---help---
+ This is a driver for the ADP1653 flash controller. It is used for
+ example in Nokia N900.
+
comment "Video improvement chips"
config VIDEO_UPD64031A
@@ -699,6 +743,8 @@ source "drivers/media/video/cx88/Kconfig"
source "drivers/media/video/cx23885/Kconfig"
+source "drivers/media/video/cx25821/Kconfig"
+
source "drivers/media/video/au0828/Kconfig"
source "drivers/media/video/ivtv/Kconfig"
@@ -707,6 +753,8 @@ source "drivers/media/video/cx18/Kconfig"
source "drivers/media/video/saa7164/Kconfig"
+source "drivers/media/video/marvell-ccic/Kconfig"
+
config VIDEO_M32R_AR
tristate "AR devices"
depends on M32R && VIDEO_V4L2
@@ -726,21 +774,6 @@ config VIDEO_M32R_AR_M64278
To compile this driver as a module, choose M here: the
module will be called arv.
-config VIDEO_CAFE_CCIC
- tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
- depends on PCI && I2C && VIDEO_V4L2
- select VIDEO_OV7670
- ---help---
- This is a video4linux2 driver for the Marvell 88ALP01 integrated
- CMOS camera controller. This is the controller found on first-
- generation OLPC systems.
-
-config VIDEO_SR030PC30
- tristate "SR030PC30 VGA camera sensor support"
- depends on I2C && VIDEO_V4L2
- ---help---
- This driver supports SR030PC30 VGA camera from Siliconfile
-
config VIDEO_VIA_CAMERA
tristate "VIAFB camera controller support"
depends on FB_VIA
@@ -751,18 +784,9 @@ config VIDEO_VIA_CAMERA
Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems
with ov7670 sensors.
-config VIDEO_NOON010PC30
- tristate "NOON010PC30 CIF camera sensor support"
- depends on I2C && VIDEO_V4L2
- ---help---
- This driver supports NOON010PC30 CIF camera from Siliconfile
-
-source "drivers/media/video/m5mols/Kconfig"
-
config VIDEO_OMAP3
tristate "OMAP 3 Camera support (EXPERIMENTAL)"
- select OMAP_IOMMU
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL
+ depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL
---help---
Driver for an OMAP 3 camera controller.
@@ -846,6 +870,12 @@ config SOC_CAMERA_OV2640
help
This is a ov2640 camera driver
+config SOC_CAMERA_OV5642
+ tristate "ov5642 camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a V4L2 camera driver for the OmniVision OV5642 sensor
+
config SOC_CAMERA_OV6650
tristate "ov6650 sensor support"
depends on SOC_CAMERA && I2C
@@ -941,8 +971,9 @@ config VIDEO_MX2
Interface
config VIDEO_SAMSUNG_S5P_FIMC
- tristate "Samsung S5P and EXYNOS4 camera host interface driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ tristate "Samsung S5P and EXYNOS4 camera interface driver (EXPERIMENTAL)"
+ depends on VIDEO_V4L2 && I2C && PLAT_S5P && PM_RUNTIME && \
+ VIDEO_V4L2_SUBDEV_API && EXPERIMENTAL
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
---help---
@@ -952,6 +983,14 @@ config VIDEO_SAMSUNG_S5P_FIMC
To compile this driver as a module, choose M here: the
module will be called s5p-fimc.
+config VIDEO_ATMEL_ISI
+ tristate "ATMEL Image Sensor Interface (ISI) support"
+ depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This module makes the ATMEL Image Sensor Interface available
+ as a v4l2 device.
+
config VIDEO_S5P_MIPI_CSIS
tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver"
depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P && VIDEO_V4L2_SUBDEV_API
@@ -961,6 +1000,8 @@ config VIDEO_S5P_MIPI_CSIS
To compile this driver as a module, choose M here: the
module will be called s5p-csis.
+source "drivers/media/video/s5p-tv/Kconfig"
+
#
# USB Multimedia device configuration
#
@@ -986,6 +1027,8 @@ source "drivers/media/video/tlg2300/Kconfig"
source "drivers/media/video/cx231xx/Kconfig"
+source "drivers/media/video/tm6000/Kconfig"
+
source "drivers/media/video/usbvision/Kconfig"
source "drivers/media/video/et61x251/Kconfig"
@@ -1056,4 +1099,12 @@ config VIDEO_MEM2MEM_TESTDEV
framework.
+config VIDEO_SAMSUNG_S5P_MFC
+ tristate "Samsung S5P MFC 5.1 Video Codec"
+ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ select VIDEOBUF2_DMA_CONTIG
+ default n
+ help
+ MFC 5.1 driver for V4L2.
+
endif # V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index f0fecd6f6a33..117f9c4b4cb9 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -65,11 +65,15 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
+obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
+obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
+obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
+obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
@@ -78,6 +82,7 @@ obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o
obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o
+obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o
obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o
obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o
@@ -100,9 +105,11 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
+obj-$(CONFIG_VIDEO_CX25821) += cx25821/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
+obj-$(CONFIG_VIDEO_TM6000) += tm6000/
obj-$(CONFIG_VIDEO_MXB) += mxb.o
obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
@@ -127,7 +134,8 @@ obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
-obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
+obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
+obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
@@ -166,8 +174,11 @@ obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
+obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
@@ -184,6 +195,6 @@ obj-y += davinci/
obj-$(CONFIG_ARCH_OMAP) += omap/
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
new file mode 100644
index 000000000000..12eedf4d515a
--- /dev/null
+++ b/drivers/media/video/adp1653.c
@@ -0,0 +1,506 @@
+/*
+ * drivers/media/video/adp1653.c
+ *
+ * Copyright (C) 2008--2011 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * Contributors:
+ * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Tuukka Toivonen <tuukkat76@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * TODO:
+ * - fault interrupt handling
+ * - hardware strobe
+ * - power doesn't need to be ON if all lights are off
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <media/adp1653.h>
+#include <media/v4l2-device.h>
+
+#define TIMEOUT_MAX 820000
+#define TIMEOUT_STEP 54600
+#define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
+ * TIMEOUT_STEP)
+#define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
+ / TIMEOUT_STEP)
+#define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP)
+
+/* Write values into ADP1653 registers. */
+static int adp1653_update_hw(struct adp1653_flash *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ u8 out_sel;
+ u8 config = 0;
+ int rval;
+
+ out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
+ flash->indicator_intensity->val)
+ << ADP1653_REG_OUT_SEL_ILED_SHIFT;
+
+ switch (flash->led_mode->val) {
+ case V4L2_FLASH_LED_MODE_NONE:
+ break;
+ case V4L2_FLASH_LED_MODE_FLASH:
+ /* Flash mode, light on with strobe, duration from timer */
+ config = ADP1653_REG_CONFIG_TMR_CFG;
+ config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val)
+ << ADP1653_REG_CONFIG_TMR_SET_SHIFT;
+ break;
+ case V4L2_FLASH_LED_MODE_TORCH:
+ /* Torch mode, light immediately on, duration indefinite */
+ out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
+ flash->torch_intensity->val)
+ << ADP1653_REG_OUT_SEL_HPLED_SHIFT;
+ break;
+ }
+
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
+ if (rval < 0)
+ return rval;
+
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config);
+ if (rval < 0)
+ return rval;
+
+ return 0;
+}
+
+static int adp1653_get_fault(struct adp1653_flash *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int fault;
+ int rval;
+
+ fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
+ if (IS_ERR_VALUE(fault))
+ return fault;
+
+ flash->fault |= fault;
+
+ if (!flash->fault)
+ return 0;
+
+ /* Clear faults. */
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+
+ flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
+
+ rval = adp1653_update_hw(flash);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+
+ return flash->fault;
+}
+
+static int adp1653_strobe(struct adp1653_flash *flash, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
+ flash->indicator_intensity->val)
+ << ADP1653_REG_OUT_SEL_ILED_SHIFT;
+ int rval;
+
+ if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH)
+ return -EBUSY;
+
+ if (!enable)
+ return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL,
+ out_sel);
+
+ out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
+ flash->flash_intensity->val)
+ << ADP1653_REG_OUT_SEL_HPLED_SHIFT;
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
+ if (rval)
+ return rval;
+
+ /* Software strobe using i2c */
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE,
+ ADP1653_REG_SW_STROBE_SW_STROBE);
+ if (rval)
+ return rval;
+ return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0);
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 controls
+ */
+
+static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct adp1653_flash *flash =
+ container_of(ctrl->handler, struct adp1653_flash, ctrls);
+ int rval;
+
+ rval = adp1653_get_fault(flash);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+
+ ctrl->cur.val = 0;
+
+ if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
+ if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
+ if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
+ if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
+
+ flash->fault = 0;
+
+ return 0;
+}
+
+static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct adp1653_flash *flash =
+ container_of(ctrl->handler, struct adp1653_flash, ctrls);
+ int rval;
+
+ rval = adp1653_get_fault(flash);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+ if ((rval & (ADP1653_REG_FAULT_FLT_SCP |
+ ADP1653_REG_FAULT_FLT_OT |
+ ADP1653_REG_FAULT_FLT_OV)) &&
+ (ctrl->id == V4L2_CID_FLASH_STROBE ||
+ ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY ||
+ ctrl->id == V4L2_CID_FLASH_LED_MODE))
+ return -EBUSY;
+
+ switch (ctrl->id) {
+ case V4L2_CID_FLASH_STROBE:
+ return adp1653_strobe(flash, 1);
+ case V4L2_CID_FLASH_STROBE_STOP:
+ return adp1653_strobe(flash, 0);
+ }
+
+ return adp1653_update_hw(flash);
+}
+
+static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
+ .g_volatile_ctrl = adp1653_get_ctrl,
+ .s_ctrl = adp1653_set_ctrl,
+};
+
+static int adp1653_init_controls(struct adp1653_flash *flash)
+{
+ struct v4l2_ctrl *fault;
+
+ v4l2_ctrl_handler_init(&flash->ctrls, 9);
+
+ flash->led_mode =
+ v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_LED_MODE,
+ V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0);
+ v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_SOURCE,
+ V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0);
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
+ flash->flash_timeout =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN,
+ flash->platform_data->max_flash_timeout,
+ TIMEOUT_STEP,
+ flash->platform_data->max_flash_timeout);
+ flash->flash_intensity =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_INTENSITY,
+ ADP1653_FLASH_INTENSITY_MIN,
+ flash->platform_data->max_flash_intensity,
+ 1, flash->platform_data->max_flash_intensity);
+ flash->torch_intensity =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_TORCH_INTENSITY,
+ ADP1653_TORCH_INTENSITY_MIN,
+ flash->platform_data->max_torch_intensity,
+ ADP1653_FLASH_INTENSITY_STEP,
+ flash->platform_data->max_torch_intensity);
+ flash->indicator_intensity =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_INDICATOR_INTENSITY,
+ ADP1653_INDICATOR_INTENSITY_MIN,
+ flash->platform_data->max_indicator_intensity,
+ ADP1653_INDICATOR_INTENSITY_STEP,
+ ADP1653_INDICATOR_INTENSITY_MIN);
+ fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_FAULT, 0,
+ V4L2_FLASH_FAULT_OVER_VOLTAGE
+ | V4L2_FLASH_FAULT_OVER_TEMPERATURE
+ | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
+
+ if (flash->ctrls.error)
+ return flash->ctrls.error;
+
+ fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ flash->subdev.ctrl_handler = &flash->ctrls;
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static int
+adp1653_init_device(struct adp1653_flash *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int rval;
+
+ /* Clear FAULT register by writing zero to OUT_SEL */
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
+ if (rval < 0) {
+ dev_err(&client->dev, "failed writing fault register\n");
+ return -EIO;
+ }
+
+ mutex_lock(&flash->ctrls.lock);
+ /* Reset faults before reading new ones. */
+ flash->fault = 0;
+ rval = adp1653_get_fault(flash);
+ mutex_unlock(&flash->ctrls.lock);
+ if (rval > 0) {
+ dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
+ return -EIO;
+ }
+
+ mutex_lock(&flash->ctrls.lock);
+ rval = adp1653_update_hw(flash);
+ mutex_unlock(&flash->ctrls.lock);
+ if (rval) {
+ dev_err(&client->dev,
+ "adp1653_update_hw failed at %s\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+__adp1653_set_power(struct adp1653_flash *flash, int on)
+{
+ int ret;
+
+ ret = flash->platform_data->power(&flash->subdev, on);
+ if (ret < 0)
+ return ret;
+
+ if (!on)
+ return 0;
+
+ ret = adp1653_init_device(flash);
+ if (ret < 0)
+ flash->platform_data->power(&flash->subdev, 0);
+
+ return ret;
+}
+
+static int
+adp1653_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+ int ret = 0;
+
+ mutex_lock(&flash->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (flash->power_count == !on) {
+ ret = __adp1653_set_power(flash, !!on);
+ if (ret < 0)
+ goto done;
+ }
+
+ /* Update the power count. */
+ flash->power_count += on ? 1 : -1;
+ WARN_ON(flash->power_count < 0);
+
+done:
+ mutex_unlock(&flash->power_lock);
+ return ret;
+}
+
+static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return adp1653_set_power(sd, 1);
+}
+
+static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return adp1653_set_power(sd, 0);
+}
+
+static const struct v4l2_subdev_core_ops adp1653_core_ops = {
+ .s_power = adp1653_set_power,
+};
+
+static const struct v4l2_subdev_ops adp1653_ops = {
+ .core = &adp1653_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
+ .open = adp1653_open,
+ .close = adp1653_close,
+};
+
+/* --------------------------------------------------------------------------
+ * I2C driver
+ */
+#ifdef CONFIG_PM
+
+static int adp1653_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+ if (!flash->power_count)
+ return 0;
+
+ return __adp1653_set_power(flash, 0);
+}
+
+static int adp1653_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+ if (!flash->power_count)
+ return 0;
+
+ return __adp1653_set_power(flash, 1);
+}
+
+#else
+
+#define adp1653_suspend NULL
+#define adp1653_resume NULL
+
+#endif /* CONFIG_PM */
+
+static int adp1653_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct adp1653_flash *flash;
+ int ret;
+
+ /* we couldn't work without platform data */
+ if (client->dev.platform_data == NULL)
+ return -ENODEV;
+
+ flash = kzalloc(sizeof(*flash), GFP_KERNEL);
+ if (flash == NULL)
+ return -ENOMEM;
+
+ flash->platform_data = client->dev.platform_data;
+
+ mutex_init(&flash->power_lock);
+
+ v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
+ flash->subdev.internal_ops = &adp1653_internal_ops;
+ flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ ret = adp1653_init_controls(flash);
+ if (ret)
+ goto free_and_quit;
+
+ ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
+ if (ret < 0)
+ goto free_and_quit;
+
+ flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+
+ return 0;
+
+free_and_quit:
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ kfree(flash);
+ return ret;
+}
+
+static int __exit adp1653_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+ v4l2_device_unregister_subdev(&flash->subdev);
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ media_entity_cleanup(&flash->subdev.entity);
+ kfree(flash);
+ return 0;
+}
+
+static const struct i2c_device_id adp1653_id_table[] = {
+ { ADP1653_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
+
+static struct dev_pm_ops adp1653_pm_ops = {
+ .suspend = adp1653_suspend,
+ .resume = adp1653_resume,
+};
+
+static struct i2c_driver adp1653_i2c_driver = {
+ .driver = {
+ .name = ADP1653_NAME,
+ .pm = &adp1653_pm_ops,
+ },
+ .probe = adp1653_probe,
+ .remove = __exit_p(adp1653_remove),
+ .id_table = adp1653_id_table,
+};
+
+static int __init adp1653_init(void)
+{
+ int rval;
+
+ rval = i2c_add_driver(&adp1653_i2c_driver);
+ if (rval)
+ printk(KERN_ALERT "%s: failed at i2c_add_driver\n", __func__);
+
+ return rval;
+}
+
+static void __exit adp1653_exit(void)
+{
+ i2c_del_driver(&adp1653_i2c_driver);
+}
+
+module_init(adp1653_init);
+module_exit(adp1653_exit);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c
index d2327dbb473f..206078eca853 100644
--- a/drivers/media/video/adv7175.c
+++ b/drivers/media/video/adv7175.c
@@ -61,6 +61,11 @@ static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd)
static char *inputs[] = { "pass_through", "play_back", "color_bar" };
+static enum v4l2_mbus_pixelcode adv7175_codes[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_UYVY8_1X16,
+};
+
/* ----------------------------------------------------------------------- */
static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value)
@@ -296,6 +301,60 @@ static int adv7175_s_routing(struct v4l2_subdev *sd,
return 0;
}
+static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(adv7175_codes))
+ return -EINVAL;
+
+ *code = adv7175_codes[index];
+ return 0;
+}
+
+static int adv7175_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u8 val = adv7175_read(sd, 0x7);
+
+ if ((val & 0x40) == (1 << 6))
+ mf->code = V4L2_MBUS_FMT_UYVY8_1X16;
+ else
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+
+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ mf->width = 0;
+ mf->height = 0;
+ mf->field = V4L2_FIELD_ANY;
+
+ return 0;
+}
+
+static int adv7175_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u8 val = adv7175_read(sd, 0x7);
+ int ret;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ val &= ~0x40;
+ break;
+
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ val |= 0x40;
+ break;
+
+ default:
+ v4l2_dbg(1, debug, sd,
+ "illegal v4l2_mbus_framefmt code: %d\n", mf->code);
+ return -EINVAL;
+ }
+
+ ret = adv7175_write(sd, 0x7, val);
+
+ return ret;
+}
+
static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -324,6 +383,9 @@ static const struct v4l2_subdev_core_ops adv7175_core_ops = {
static const struct v4l2_subdev_video_ops adv7175_video_ops = {
.s_std_output = adv7175_s_std_output,
.s_routing = adv7175_s_routing,
+ .s_mbus_fmt = adv7175_s_fmt,
+ .g_mbus_fmt = adv7175_g_fmt,
+ .enum_mbus_fmt = adv7175_enum_fmt,
};
static const struct v4l2_subdev_ops adv7175_ops = {
diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c
index b388654d48cd..53c496c00fb6 100644
--- a/drivers/media/video/ak881x.c
+++ b/drivers/media/video/ak881x.c
@@ -13,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
+#include <linux/module.h>
#include <media/ak881x.h>
#include <media/v4l2-chip-ident.h>
diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c
index f989f2820d88..b6ed44aebe30 100644
--- a/drivers/media/video/arv.c
+++ b/drivers/media/video/arv.c
@@ -27,7 +27,6 @@
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/sched.h>
-#include <linux/version.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
@@ -54,7 +53,7 @@
*/
#define USE_INT 0 /* Don't modify */
-#define VERSION "0.04"
+#define VERSION "0.0.5"
#define ar_inl(addr) inl((unsigned long)(addr))
#define ar_outl(val, addr) outl((unsigned long)(val), (unsigned long)(addr))
@@ -404,7 +403,6 @@ static int ar_querycap(struct file *file, void *priv,
strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver));
strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card));
strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info));
- vcap->version = KERNEL_VERSION(0, 0, 4);
vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
return 0;
}
@@ -879,3 +877,4 @@ module_exit(ar_cleanup_module);
MODULE_AUTHOR("Takeo Takahashi <takahashi.takeo@renesas.com>");
MODULE_DESCRIPTION("Colour AR M64278(VGA) for Video4Linux");
MODULE_LICENSE("GPL");
+MODULE_VERSION(VERSION);
diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c
new file mode 100644
index 000000000000..8c775c59e120
--- /dev/null
+++ b/drivers/media/video/atmel-isi.c
@@ -0,0 +1,1068 @@
+/*
+ * Copyright (c) 2011 Atmel Corporation
+ * Josh Wu, <josh.wu@atmel.com>
+ *
+ * Based on previous work by Lars Haring, <lars.haring@atmel.com>
+ * and Sedji Gaouaou
+ * Based on the bttv driver for Bt848 with respective copyright holders
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <media/atmel-isi.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define MAX_BUFFER_NUM 32
+#define MAX_SUPPORT_WIDTH 2048
+#define MAX_SUPPORT_HEIGHT 2048
+#define VID_LIMIT_BYTES (16 * 1024 * 1024)
+#define MIN_FRAME_RATE 15
+#define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE)
+
+/* ISI states */
+enum {
+ ISI_STATE_IDLE = 0,
+ ISI_STATE_READY,
+ ISI_STATE_WAIT_SOF,
+};
+
+/* Frame buffer descriptor */
+struct fbd {
+ /* Physical address of the frame buffer */
+ u32 fb_address;
+ /* DMA Control Register(only in HISI2) */
+ u32 dma_ctrl;
+ /* Physical address of the next fbd */
+ u32 next_fbd_address;
+};
+
+static void set_dma_ctrl(struct fbd *fb_desc, u32 ctrl)
+{
+ fb_desc->dma_ctrl = ctrl;
+}
+
+struct isi_dma_desc {
+ struct list_head list;
+ struct fbd *p_fbd;
+ u32 fbd_phys;
+};
+
+/* Frame buffer data */
+struct frame_buffer {
+ struct vb2_buffer vb;
+ struct isi_dma_desc *p_dma_desc;
+ struct list_head list;
+};
+
+struct atmel_isi {
+ /* Protects the access of variables shared with the ISR */
+ spinlock_t lock;
+ void __iomem *regs;
+
+ int sequence;
+ /* State of the ISI module in capturing mode */
+ int state;
+
+ /* Wait queue for waiting for SOF */
+ wait_queue_head_t vsync_wq;
+
+ struct vb2_alloc_ctx *alloc_ctx;
+
+ /* Allocate descriptors for dma buffer use */
+ struct fbd *p_fb_descriptors;
+ u32 fb_descriptors_phys;
+ struct list_head dma_desc_head;
+ struct isi_dma_desc dma_desc[MAX_BUFFER_NUM];
+
+ struct completion complete;
+ struct clk *pclk;
+ unsigned int irq;
+
+ struct isi_platform_data *pdata;
+ u16 width_flags; /* max 12 bits */
+
+ struct list_head video_buffer_list;
+ struct frame_buffer *active;
+
+ struct soc_camera_device *icd;
+ struct soc_camera_host soc_host;
+};
+
+static void isi_writel(struct atmel_isi *isi, u32 reg, u32 val)
+{
+ writel(val, isi->regs + reg);
+}
+static u32 isi_readl(struct atmel_isi *isi, u32 reg)
+{
+ return readl(isi->regs + reg);
+}
+
+static int configure_geometry(struct atmel_isi *isi, u32 width,
+ u32 height, enum v4l2_mbus_pixelcode code)
+{
+ u32 cfg2, cr;
+
+ switch (code) {
+ /* YUV, including grey */
+ case V4L2_MBUS_FMT_Y8_1X8:
+ cr = ISI_CFG2_GRAYSCALE;
+ break;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ cr = ISI_CFG2_YCC_SWAP_MODE_3;
+ break;
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ cr = ISI_CFG2_YCC_SWAP_MODE_2;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ cr = ISI_CFG2_YCC_SWAP_MODE_1;
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ cr = ISI_CFG2_YCC_SWAP_DEFAULT;
+ break;
+ /* RGB, TODO */
+ default:
+ return -EINVAL;
+ }
+
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+
+ cfg2 = isi_readl(isi, ISI_CFG2);
+ cfg2 |= cr;
+ /* Set width */
+ cfg2 &= ~(ISI_CFG2_IM_HSIZE_MASK);
+ cfg2 |= ((width - 1) << ISI_CFG2_IM_HSIZE_OFFSET) &
+ ISI_CFG2_IM_HSIZE_MASK;
+ /* Set height */
+ cfg2 &= ~(ISI_CFG2_IM_VSIZE_MASK);
+ cfg2 |= ((height - 1) << ISI_CFG2_IM_VSIZE_OFFSET)
+ & ISI_CFG2_IM_VSIZE_MASK;
+ isi_writel(isi, ISI_CFG2, cfg2);
+
+ return 0;
+}
+
+static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi)
+{
+ if (isi->active) {
+ struct vb2_buffer *vb = &isi->active->vb;
+ struct frame_buffer *buf = isi->active;
+
+ list_del_init(&buf->list);
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.sequence = isi->sequence++;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ if (list_empty(&isi->video_buffer_list)) {
+ isi->active = NULL;
+ } else {
+ /* start next dma frame. */
+ isi->active = list_entry(isi->video_buffer_list.next,
+ struct frame_buffer, list);
+ isi_writel(isi, ISI_DMA_C_DSCR,
+ isi->active->p_dma_desc->fbd_phys);
+ isi_writel(isi, ISI_DMA_C_CTRL,
+ ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
+ isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+ }
+ return IRQ_HANDLED;
+}
+
+/* ISI interrupt service routine */
+static irqreturn_t isi_interrupt(int irq, void *dev_id)
+{
+ struct atmel_isi *isi = dev_id;
+ u32 status, mask, pending;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock(&isi->lock);
+
+ status = isi_readl(isi, ISI_STATUS);
+ mask = isi_readl(isi, ISI_INTMASK);
+ pending = status & mask;
+
+ if (pending & ISI_CTRL_SRST) {
+ complete(&isi->complete);
+ isi_writel(isi, ISI_INTDIS, ISI_CTRL_SRST);
+ ret = IRQ_HANDLED;
+ } else if (pending & ISI_CTRL_DIS) {
+ complete(&isi->complete);
+ isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS);
+ ret = IRQ_HANDLED;
+ } else {
+ if ((pending & ISI_SR_VSYNC) &&
+ (isi->state == ISI_STATE_IDLE)) {
+ isi->state = ISI_STATE_READY;
+ wake_up_interruptible(&isi->vsync_wq);
+ ret = IRQ_HANDLED;
+ }
+ if (likely(pending & ISI_SR_CXFR_DONE))
+ ret = atmel_isi_handle_streaming(isi);
+ }
+
+ spin_unlock(&isi->lock);
+ return ret;
+}
+
+#define WAIT_ISI_RESET 1
+#define WAIT_ISI_DISABLE 0
+static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset)
+{
+ unsigned long timeout;
+ /*
+ * The reset or disable will only succeed if we have a
+ * pixel clock from the camera.
+ */
+ init_completion(&isi->complete);
+
+ if (wait_reset) {
+ isi_writel(isi, ISI_INTEN, ISI_CTRL_SRST);
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_SRST);
+ } else {
+ isi_writel(isi, ISI_INTEN, ISI_CTRL_DIS);
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+ }
+
+ timeout = wait_for_completion_timeout(&isi->complete,
+ msecs_to_jiffies(100));
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ unsigned long size;
+ int ret, bytes_per_line;
+
+ /* Reset ISI */
+ ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET);
+ if (ret < 0) {
+ dev_err(icd->parent, "Reset ISI timed out\n");
+ return ret;
+ }
+ /* Disable all interrupts */
+ isi_writel(isi, ISI_INTDIS, ~0UL);
+
+ bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ size = bytes_per_line * icd->user_height;
+
+ if (!*nbuffers || *nbuffers > MAX_BUFFER_NUM)
+ *nbuffers = MAX_BUFFER_NUM;
+
+ if (size * *nbuffers > VID_LIMIT_BYTES)
+ *nbuffers = VID_LIMIT_BYTES / size;
+
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = isi->alloc_ctx;
+
+ isi->sequence = 0;
+ isi->active = NULL;
+
+ dev_dbg(icd->parent, "%s, count=%d, size=%ld\n", __func__,
+ *nbuffers, size);
+
+ return 0;
+}
+
+static int buffer_init(struct vb2_buffer *vb)
+{
+ struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+
+ buf->p_dma_desc = NULL;
+ INIT_LIST_HEAD(&buf->list);
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ unsigned long size;
+ struct isi_dma_desc *desc;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ size = bytes_per_line * icd->user_height;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(icd->parent, "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(&buf->vb, 0, size);
+
+ if (!buf->p_dma_desc) {
+ if (list_empty(&isi->dma_desc_head)) {
+ dev_err(icd->parent, "Not enough dma descriptors.\n");
+ return -EINVAL;
+ } else {
+ /* Get an available descriptor */
+ desc = list_entry(isi->dma_desc_head.next,
+ struct isi_dma_desc, list);
+ /* Delete the descriptor since now it is used */
+ list_del_init(&desc->list);
+
+ /* Initialize the dma descriptor */
+ desc->p_fbd->fb_address =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ desc->p_fbd->next_fbd_address = 0;
+ set_dma_ctrl(desc->p_fbd, ISI_DMA_CTRL_WB);
+
+ buf->p_dma_desc = desc;
+ }
+ }
+ return 0;
+}
+
+static void buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+
+ /* This descriptor is available now and we add to head list */
+ if (buf->p_dma_desc)
+ list_add(&buf->p_dma_desc->list, &isi->dma_desc_head);
+}
+
+static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)
+{
+ u32 ctrl, cfg1;
+
+ cfg1 = isi_readl(isi, ISI_CFG1);
+ /* Enable irq: cxfr for the codec path, pxfr for the preview path */
+ isi_writel(isi, ISI_INTEN,
+ ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE);
+
+ /* Check if already in a frame */
+ if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) {
+ dev_err(isi->icd->parent, "Already in frame handling.\n");
+ return;
+ }
+
+ isi_writel(isi, ISI_DMA_C_DSCR, buffer->p_dma_desc->fbd_phys);
+ isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
+ isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+
+ /* Enable linked list */
+ cfg1 |= isi->pdata->frate | ISI_CFG1_DISCR;
+
+ /* Enable codec path and ISI */
+ ctrl = ISI_CTRL_CDC | ISI_CTRL_EN;
+ isi_writel(isi, ISI_CTRL, ctrl);
+ isi_writel(isi, ISI_CFG1, cfg1);
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&isi->lock, flags);
+ list_add_tail(&buf->list, &isi->video_buffer_list);
+
+ if (isi->active == NULL) {
+ isi->active = buf;
+ if (vb2_is_streaming(vb->vb2_queue))
+ start_dma(isi, buf);
+ }
+ spin_unlock_irqrestore(&isi->lock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+
+ u32 sr = 0;
+ int ret;
+
+ spin_lock_irq(&isi->lock);
+ isi->state = ISI_STATE_IDLE;
+ /* Clear any pending SOF interrupt */
+ sr = isi_readl(isi, ISI_STATUS);
+ /* Enable VSYNC interrupt for SOF */
+ isi_writel(isi, ISI_INTEN, ISI_SR_VSYNC);
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_EN);
+ spin_unlock_irq(&isi->lock);
+
+ dev_dbg(icd->parent, "Waiting for SOF\n");
+ ret = wait_event_interruptible(isi->vsync_wq,
+ isi->state != ISI_STATE_IDLE);
+ if (ret)
+ goto err;
+
+ if (isi->state != ISI_STATE_READY) {
+ ret = -EIO;
+ goto err;
+ }
+
+ spin_lock_irq(&isi->lock);
+ isi->state = ISI_STATE_WAIT_SOF;
+ isi_writel(isi, ISI_INTDIS, ISI_SR_VSYNC);
+ if (count)
+ start_dma(isi, isi->active);
+ spin_unlock_irq(&isi->lock);
+
+ return 0;
+err:
+ isi->active = NULL;
+ isi->sequence = 0;
+ INIT_LIST_HEAD(&isi->video_buffer_list);
+ return ret;
+}
+
+/* abort streaming and wait for last buffer */
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct frame_buffer *buf, *node;
+ int ret = 0;
+ unsigned long timeout;
+
+ spin_lock_irq(&isi->lock);
+ isi->active = NULL;
+ /* Release all active buffers */
+ list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) {
+ list_del_init(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irq(&isi->lock);
+
+ timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ;
+ /* Wait until the end of the current frame. */
+ while ((isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) &&
+ time_before(jiffies, timeout))
+ msleep(1);
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(icd->parent,
+ "Timeout waiting for finishing codec request\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Disable interrupts */
+ isi_writel(isi, ISI_INTDIS,
+ ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE);
+
+ /* Disable ISI and wait for it is done */
+ ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE);
+ if (ret < 0)
+ dev_err(icd->parent, "Disable ISI timed out\n");
+
+ return ret;
+}
+
+static struct vb2_ops isi_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_init = buffer_init,
+ .buf_prepare = buffer_prepare,
+ .buf_cleanup = buffer_cleanup,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = soc_camera_unlock,
+ .wait_finish = soc_camera_lock,
+};
+
+/* ------------------------------------------------------------------
+ SOC camera operations for the device
+ ------------------------------------------------------------------*/
+static int isi_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP;
+ q->drv_priv = icd;
+ q->buf_struct_size = sizeof(struct frame_buffer);
+ q->ops = &isi_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+
+ return vb2_queue_init(q);
+}
+
+static int isi_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ dev_dbg(icd->parent, "Plan to set format %dx%d\n",
+ pix->width, pix->height);
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ ret = configure_geometry(isi, pix->width, pix->height, xlate->code);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
+
+ dev_dbg(icd->parent, "Finally set format %dx%d\n",
+ pix->width, pix->height);
+
+ return ret;
+}
+
+static int isi_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ u32 pixfmt = pix->pixelformat;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (pixfmt && !xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* limit to Atmel ISI hardware capabilities */
+ if (pix->height > MAX_SUPPORT_HEIGHT)
+ pix->height = MAX_SUPPORT_HEIGHT;
+ if (pix->width > MAX_SUPPORT_WIDTH)
+ pix->width = MAX_SUPPORT_WIDTH;
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->colorspace = mf.colorspace;
+
+ switch (mf.field) {
+ case V4L2_FIELD_ANY:
+ pix->field = V4L2_FIELD_NONE;
+ break;
+ case V4L2_FIELD_NONE:
+ break;
+ default:
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf.field);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct soc_mbus_pixelfmt isi_camera_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "Packed YUV422 16 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+};
+
+/* This will be corrected as we get more formats */
+static bool isi_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+#define ISI_BUS_PARAM (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
+static int isi_camera_try_bus_param(struct soc_camera_device *icd,
+ unsigned char buswidth)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ ISI_BUS_PARAM);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, ISI_BUS_PARAM);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
+
+ if ((1 << (buswidth - 1)) & isi->width_flags)
+ return 0;
+ return -EINVAL;
+}
+
+
+static int isi_camera_get_formats(struct soc_camera_device *icd,
+ unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int formats = 0, ret;
+ /* sensor format */
+ enum v4l2_mbus_pixelcode code;
+ /* soc camera host format */
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_err(icd->parent,
+ "Invalid format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ /* This also checks support for the requested bits-per-sample */
+ ret = isi_camera_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0) {
+ dev_err(icd->parent,
+ "Fail to try the bus parameters.\n");
+ return 0;
+ }
+
+ switch (code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &isi_camera_formats[0];
+ xlate->code = code;
+ xlate++;
+ dev_dbg(icd->parent, "Providing format %s using code %d\n",
+ isi_camera_formats[0].name, code);
+ }
+ break;
+ default:
+ if (!isi_camera_packing_supported(fmt))
+ return 0;
+ if (xlate)
+ dev_dbg(icd->parent,
+ "Providing format %s in pass-through mode\n",
+ fmt->name);
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ }
+
+ return formats;
+}
+
+/* Called with .video_lock held */
+static int isi_camera_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ int ret;
+
+ if (isi->icd)
+ return -EBUSY;
+
+ ret = clk_enable(isi->pclk);
+ if (ret)
+ return ret;
+
+ isi->icd = icd;
+ dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
+ icd->devnum);
+ return 0;
+}
+/* Called with .video_lock held */
+static void isi_camera_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+
+ BUG_ON(icd != isi->icd);
+
+ clk_disable(isi->pclk);
+ isi->icd = NULL;
+
+ dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+static unsigned int isi_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int isi_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "atmel-isi");
+ strcpy(cap->card, "Atmel Image Sensor Interface");
+ cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING);
+ return 0;
+}
+
+static int isi_camera_set_bus_param(struct soc_camera_device *icd, u32 pixfmt)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ int ret;
+ u32 cfg1 = 0;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ ISI_BUS_PARAM);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, ISI_BUS_PARAM);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = ISI_BUS_PARAM;
+ }
+ dev_dbg(icd->parent, "Flags cam: 0x%x host: 0x%x common: 0x%lx\n",
+ cfg.flags, ISI_BUS_PARAM, common_flags);
+
+ /* Make choises, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (isi->pdata->hsync_act_low)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (isi->pdata->vsync_act_low)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (isi->pdata->pclk_act_falling)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ /* set bus param for ISI */
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW;
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING;
+
+ if (isi->pdata->has_emb_sync)
+ cfg1 |= ISI_CFG1_EMB_SYNC;
+ if (isi->pdata->isi_full_mode)
+ cfg1 |= ISI_CFG1_FULL_MODE;
+
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+ isi_writel(isi, ISI_CFG1, cfg1);
+
+ return 0;
+}
+
+static struct soc_camera_host_ops isi_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = isi_camera_add_device,
+ .remove = isi_camera_remove_device,
+ .set_fmt = isi_camera_set_fmt,
+ .try_fmt = isi_camera_try_fmt,
+ .get_formats = isi_camera_get_formats,
+ .init_videobuf2 = isi_camera_init_videobuf,
+ .poll = isi_camera_poll,
+ .querycap = isi_camera_querycap,
+ .set_bus_param = isi_camera_set_bus_param,
+};
+
+/* -----------------------------------------------------------------------*/
+static int __devexit atmel_isi_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct atmel_isi *isi = container_of(soc_host,
+ struct atmel_isi, soc_host);
+
+ free_irq(isi->irq, isi);
+ soc_camera_host_unregister(soc_host);
+ vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct fbd) * MAX_BUFFER_NUM,
+ isi->p_fb_descriptors,
+ isi->fb_descriptors_phys);
+
+ iounmap(isi->regs);
+ clk_put(isi->pclk);
+ kfree(isi);
+
+ return 0;
+}
+
+static int __devinit atmel_isi_probe(struct platform_device *pdev)
+{
+ unsigned int irq;
+ struct atmel_isi *isi;
+ struct clk *pclk;
+ struct resource *regs;
+ int ret, i;
+ struct device *dev = &pdev->dev;
+ struct soc_camera_host *soc_host;
+ struct isi_platform_data *pdata;
+
+ pdata = dev->platform_data;
+ if (!pdata || !pdata->data_width_flags) {
+ dev_err(&pdev->dev,
+ "No config available for Atmel ISI\n");
+ return -EINVAL;
+ }
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs)
+ return -ENXIO;
+
+ pclk = clk_get(&pdev->dev, "isi_clk");
+ if (IS_ERR(pclk))
+ return PTR_ERR(pclk);
+
+ isi = kzalloc(sizeof(struct atmel_isi), GFP_KERNEL);
+ if (!isi) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "Can't allocate interface!\n");
+ goto err_alloc_isi;
+ }
+
+ isi->pclk = pclk;
+ isi->pdata = pdata;
+ isi->active = NULL;
+ spin_lock_init(&isi->lock);
+ init_waitqueue_head(&isi->vsync_wq);
+ INIT_LIST_HEAD(&isi->video_buffer_list);
+ INIT_LIST_HEAD(&isi->dma_desc_head);
+
+ isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct fbd) * MAX_BUFFER_NUM,
+ &isi->fb_descriptors_phys,
+ GFP_KERNEL);
+ if (!isi->p_fb_descriptors) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "Can't allocate descriptors!\n");
+ goto err_alloc_descriptors;
+ }
+
+ for (i = 0; i < MAX_BUFFER_NUM; i++) {
+ isi->dma_desc[i].p_fbd = isi->p_fb_descriptors + i;
+ isi->dma_desc[i].fbd_phys = isi->fb_descriptors_phys +
+ i * sizeof(struct fbd);
+ list_add(&isi->dma_desc[i].list, &isi->dma_desc_head);
+ }
+
+ isi->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(isi->alloc_ctx)) {
+ ret = PTR_ERR(isi->alloc_ctx);
+ goto err_alloc_ctx;
+ }
+
+ isi->regs = ioremap(regs->start, resource_size(regs));
+ if (!isi->regs) {
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ if (pdata->data_width_flags & ISI_DATAWIDTH_8)
+ isi->width_flags = 1 << 7;
+ if (pdata->data_width_flags & ISI_DATAWIDTH_10)
+ isi->width_flags |= 1 << 9;
+
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto err_req_irq;
+ }
+
+ ret = request_irq(irq, isi_interrupt, 0, "isi", isi);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
+ goto err_req_irq;
+ }
+ isi->irq = irq;
+
+ soc_host = &isi->soc_host;
+ soc_host->drv_name = "isi-camera";
+ soc_host->ops = &isi_soc_camera_host_ops;
+ soc_host->priv = isi;
+ soc_host->v4l2_dev.dev = &pdev->dev;
+ soc_host->nr = pdev->id;
+
+ ret = soc_camera_host_register(soc_host);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to register soc camera host\n");
+ goto err_register_soc_camera_host;
+ }
+ return 0;
+
+err_register_soc_camera_host:
+ free_irq(isi->irq, isi);
+err_req_irq:
+ iounmap(isi->regs);
+err_ioremap:
+ vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
+err_alloc_ctx:
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct fbd) * MAX_BUFFER_NUM,
+ isi->p_fb_descriptors,
+ isi->fb_descriptors_phys);
+err_alloc_descriptors:
+ kfree(isi);
+err_alloc_isi:
+ clk_put(isi->pclk);
+
+ return ret;
+}
+
+static struct platform_driver atmel_isi_driver = {
+ .probe = atmel_isi_probe,
+ .remove = __devexit_p(atmel_isi_remove),
+ .driver = {
+ .name = "atmel_isi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init atmel_isi_init_module(void)
+{
+ return platform_driver_probe(&atmel_isi_driver, &atmel_isi_probe);
+}
+
+static void __exit atmel_isi_exit(void)
+{
+ platform_driver_unregister(&atmel_isi_driver);
+}
+module_init(atmel_isi_init_module);
+module_exit(atmel_isi_exit);
+
+MODULE_AUTHOR("Josh Wu <josh.wu@atmel.com>");
+MODULE_DESCRIPTION("The V4L2 driver for Atmel Linux");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile
index 5c7f2f7d9805..bd22223f8d9f 100644
--- a/drivers/media/video/au0828/Makefile
+++ b/drivers/media/video/au0828/Makefile
@@ -2,8 +2,8 @@ au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-vid
obj-$(CONFIG_VIDEO_AU0828) += au0828.o
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/au0828/au0828-core.c b/drivers/media/video/au0828/au0828-core.c
index ca342e4c61fc..1e4ce5068ec2 100644
--- a/drivers/media/video/au0828/au0828-core.c
+++ b/drivers/media/video/au0828/au0828-core.c
@@ -292,3 +292,4 @@ module_exit(au0828_exit);
MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products");
MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.2");
diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c
index c03eb29a9ee6..0b3e481ffe8c 100644
--- a/drivers/media/video/au0828/au0828-video.c
+++ b/drivers/media/video/au0828/au0828-video.c
@@ -33,7 +33,6 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/suspend.h>
-#include <linux/version.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-chip-ident.h>
@@ -43,8 +42,6 @@
static DEFINE_MUTEX(au0828_sysfs_lock);
-#define AU0828_VERSION_CODE KERNEL_VERSION(0, 0, 1)
-
/* ------------------------------------------------------------------
Videobuf operations
------------------------------------------------------------------*/
@@ -1254,8 +1251,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->card, dev->board.name, sizeof(cap->card));
strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
- cap->version = AU0828_VERSION_CODE;
-
/*set the device capabilities */
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_VBI_CAPTURE |
diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c
index f87204461cb4..859eabf57978 100644
--- a/drivers/media/video/bt819.c
+++ b/drivers/media/video/bt819.c
@@ -229,7 +229,7 @@ static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
if (pstd)
*pstd = std;
if (pstatus)
- *pstatus = status;
+ *pstatus = res;
v4l2_dbg(1, debug, sd, "get status %x\n", status);
return 0;
diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile
index e415f6fc447c..3f9a2b22d3d4 100644
--- a/drivers/media/video/bt8xx/Makefile
+++ b/drivers/media/video/bt8xx/Makefile
@@ -8,6 +8,6 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
obj-$(CONFIG_VIDEO_BT848) += bttv.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 3c9e6c7e7b52..5939021d8eba 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -25,6 +25,8 @@
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/kmod.h>
@@ -2892,13 +2894,10 @@ void __devinit bttv_idcard(struct bttv *btv)
{
unsigned int gpiobits;
int i,type;
- unsigned short tmp;
/* read PCI subsystem ID */
- pci_read_config_word(btv->c.pci, PCI_SUBSYSTEM_ID, &tmp);
- btv->cardid = tmp << 16;
- pci_read_config_word(btv->c.pci, PCI_SUBSYSTEM_VENDOR_ID, &tmp);
- btv->cardid |= tmp;
+ btv->cardid = btv->c.pci->subsystem_device << 16;
+ btv->cardid |= btv->c.pci->subsystem_vendor;
if (0 != btv->cardid && 0xffffffff != btv->cardid) {
/* look for the card */
@@ -2908,19 +2907,17 @@ void __devinit bttv_idcard(struct bttv *btv)
if (type != -1) {
/* found it */
- printk(KERN_INFO "bttv%d: detected: %s [card=%d], "
- "PCI subsystem ID is %04x:%04x\n",
- btv->c.nr,cards[type].name,cards[type].cardnr,
- btv->cardid & 0xffff,
- (btv->cardid >> 16) & 0xffff);
+ pr_info("%d: detected: %s [card=%d], PCI subsystem ID is %04x:%04x\n",
+ btv->c.nr, cards[type].name, cards[type].cardnr,
+ btv->cardid & 0xffff,
+ (btv->cardid >> 16) & 0xffff);
btv->c.type = cards[type].cardnr;
} else {
/* 404 */
- printk(KERN_INFO "bttv%d: subsystem: %04x:%04x (UNKNOWN)\n",
- btv->c.nr, btv->cardid & 0xffff,
- (btv->cardid >> 16) & 0xffff);
- printk(KERN_DEBUG "please mail id, board name and "
- "the correct card= insmod option to linux-media@vger.kernel.org\n");
+ pr_info("%d: subsystem: %04x:%04x (UNKNOWN)\n",
+ btv->c.nr, btv->cardid & 0xffff,
+ (btv->cardid >> 16) & 0xffff);
+ pr_debug("please mail id, board name and the correct card= insmod option to linux-media@vger.kernel.org\n");
}
}
@@ -2929,10 +2926,10 @@ void __devinit bttv_idcard(struct bttv *btv)
btv->c.type=card[btv->c.nr];
/* print which card config we are using */
- printk(KERN_INFO "bttv%d: using: %s [card=%d,%s]\n",btv->c.nr,
- bttv_tvcards[btv->c.type].name, btv->c.type,
- card[btv->c.nr] < bttv_num_tvcards
- ? "insmod option" : "autodetected");
+ pr_info("%d: using: %s [card=%d,%s]\n",
+ btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type,
+ card[btv->c.nr] < bttv_num_tvcards
+ ? "insmod option" : "autodetected");
/* overwrite gpio stuff ?? */
if (UNSET == audioall && UNSET == audiomux[0])
@@ -2951,12 +2948,13 @@ void __devinit bttv_idcard(struct bttv *btv)
}
}
bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits;
- printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=",
- btv->c.nr,bttv_tvcards[btv->c.type].gpiomask);
+ pr_info("%d: gpio config override: mask=0x%x, mux=",
+ btv->c.nr, bttv_tvcards[btv->c.type].gpiomask);
for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
- printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]);
+ pr_cont("%s0x%x",
+ i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]);
}
- printk("\n");
+ pr_cont("\n");
}
/*
@@ -2977,8 +2975,8 @@ static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256])
if (-1 != type) {
btv->c.type = type;
- printk("bttv%d: detected by eeprom: %s [card=%d]\n",
- btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type);
+ pr_info("%d: detected by eeprom: %s [card=%d]\n",
+ btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type);
}
}
@@ -3022,7 +3020,7 @@ static void flyvideo_gpio(struct bttv *btv)
tuner_type = 3; /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */
break;
default:
- printk(KERN_INFO "bttv%d: FlyVideo_gpio: unknown tuner type.\n", btv->c.nr);
+ pr_info("%d: FlyVideo_gpio: unknown tuner type\n", btv->c.nr);
break;
}
@@ -3039,13 +3037,13 @@ static void flyvideo_gpio(struct bttv *btv)
if (is_capture_only)
tuner_type = TUNER_ABSENT; /* No tuner present */
- printk(KERN_INFO "bttv%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n",
- btv->c.nr, has_radio ? "yes" : "no ",
- has_remote ? "yes" : "no ", tuner_type, gpio);
- printk(KERN_INFO "bttv%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n",
- btv->c.nr, is_lr90 ? "yes" : "no ",
- has_tda9820_tda9821 ? "yes" : "no ",
- is_capture_only ? "yes" : "no ");
+ pr_info("%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n",
+ btv->c.nr, has_radio ? "yes" : "no",
+ has_remote ? "yes" : "no", tuner_type, gpio);
+ pr_info("%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n",
+ btv->c.nr, is_lr90 ? "yes" : "no",
+ has_tda9820_tda9821 ? "yes" : "no",
+ is_capture_only ? "yes" : "no");
if (tuner_type != UNSET) /* only set if known tuner autodetected, else let insmod option through */
btv->tuner_type = tuner_type;
@@ -3094,12 +3092,11 @@ static void miro_pinnacle_gpio(struct bttv *btv)
if (btv->c.type == BTTV_BOARD_PINNACLE)
btv->c.type = BTTV_BOARD_PINNACLEPRO;
}
- printk(KERN_INFO
- "bttv%d: miro: id=%d tuner=%d radio=%s stereo=%s\n",
- btv->c.nr, id+1, btv->tuner_type,
- !btv->has_radio ? "no" :
- (btv->has_matchbox ? "matchbox" : "fmtuner"),
- (-1 == msp) ? "no" : "yes");
+ pr_info("%d: miro: id=%d tuner=%d radio=%s stereo=%s\n",
+ btv->c.nr, id+1, btv->tuner_type,
+ !btv->has_radio ? "no" :
+ (btv->has_matchbox ? "matchbox" : "fmtuner"),
+ (-1 == msp) ? "no" : "yes");
} else {
/* new cards with microtune tuner */
id = 63 - id;
@@ -3141,9 +3138,8 @@ static void miro_pinnacle_gpio(struct bttv *btv)
}
if (-1 != msp)
btv->c.type = BTTV_BOARD_PINNACLEPRO;
- printk(KERN_INFO
- "bttv%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n",
- btv->c.nr, id, info, btv->has_radio ? "yes" : "no");
+ pr_info("%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n",
+ btv->c.nr, id, info, btv->has_radio ? "yes" : "no");
btv->tuner_type = TUNER_MT2032;
}
}
@@ -3205,7 +3201,7 @@ static void gvc1100_muxsel(struct bttv *btv, unsigned int input)
static void init_lmlbt4x(struct bttv *btv)
{
- printk(KERN_DEBUG "LMLBT4x init\n");
+ pr_debug("LMLBT4x init\n");
btwrite(0x000000, BT848_GPIO_REG_INP);
gpio_inout(0xffffff, 0x0006C0);
gpio_write(0x000000);
@@ -3249,7 +3245,7 @@ static void bttv_reset_audio(struct bttv *btv)
return;
if (bttv_debug)
- printk("bttv%d: BT878A ARESET\n",btv->c.nr);
+ pr_debug("%d: BT878A ARESET\n", btv->c.nr);
btwrite((1<<7), 0x058);
udelay(10);
btwrite( 0, 0x058);
@@ -3352,7 +3348,8 @@ void __devinit bttv_init_card2(struct bttv *btv)
case BTTV_BOARD_MAGICTVIEW061:
if (btv->cardid == 0x3002144f) {
btv->has_radio=1;
- printk("bttv%d: radio detected by subsystem id (CPH05x)\n",btv->c.nr);
+ pr_info("%d: radio detected by subsystem id (CPH05x)\n",
+ btv->c.nr);
}
break;
case BTTV_BOARD_STB2:
@@ -3441,17 +3438,16 @@ void __devinit bttv_init_card2(struct bttv *btv)
btv->tuner_type = tuner[btv->c.nr];
if (btv->tuner_type == TUNER_ABSENT)
- printk(KERN_INFO "bttv%d: tuner absent\n", btv->c.nr);
- else if(btv->tuner_type == UNSET)
- printk(KERN_WARNING "bttv%d: tuner type unset\n", btv->c.nr);
+ pr_info("%d: tuner absent\n", btv->c.nr);
+ else if (btv->tuner_type == UNSET)
+ pr_warn("%d: tuner type unset\n", btv->c.nr);
else
- printk(KERN_INFO "bttv%d: tuner type=%d\n", btv->c.nr,
- btv->tuner_type);
+ pr_info("%d: tuner type=%d\n", btv->c.nr, btv->tuner_type);
if (autoload != UNSET) {
- printk(KERN_WARNING "bttv%d: the autoload option is obsolete.\n", btv->c.nr);
- printk(KERN_WARNING "bttv%d: use option msp3400, tda7432 or tvaudio to\n", btv->c.nr);
- printk(KERN_WARNING "bttv%d: override which audio module should be used.\n", btv->c.nr);
+ pr_warn("%d: the autoload option is obsolete\n", btv->c.nr);
+ pr_warn("%d: use option msp3400, tda7432 or tvaudio to override which audio module should be used\n",
+ btv->c.nr);
}
if (UNSET == btv->tuner_type)
@@ -3544,8 +3540,7 @@ void __devinit bttv_init_card2(struct bttv *btv)
}
default:
- printk(KERN_WARNING "bttv%d: unknown audiodev value!\n",
- btv->c.nr);
+ pr_warn("%d: unknown audiodev value!\n", btv->c.nr);
return;
}
@@ -3588,8 +3583,7 @@ void __devinit bttv_init_card2(struct bttv *btv)
return;
no_audio:
- printk(KERN_WARNING "bttv%d: audio absent, no audio device found!\n",
- btv->c.nr);
+ pr_warn("%d: audio absent, no audio device found!\n", btv->c.nr);
}
@@ -3642,19 +3636,19 @@ static void modtec_eeprom(struct bttv *btv)
{
if( strncmp(&(eeprom_data[0x1e]),"Temic 4066 FY5",14) ==0) {
btv->tuner_type=TUNER_TEMIC_4066FY5_PAL_I;
- printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n",
- btv->c.nr,&eeprom_data[0x1e]);
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
} else if (strncmp(&(eeprom_data[0x1e]),"Alps TSBB5",10) ==0) {
btv->tuner_type=TUNER_ALPS_TSBB5_PAL_I;
- printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n",
- btv->c.nr,&eeprom_data[0x1e]);
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
} else if (strncmp(&(eeprom_data[0x1e]),"Philips FM1246",14) ==0) {
btv->tuner_type=TUNER_PHILIPS_NTSC;
- printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n",
- btv->c.nr,&eeprom_data[0x1e]);
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
} else {
- printk("bttv%d: Modtec: Unknown TunerString: %s\n",
- btv->c.nr,&eeprom_data[0x1e]);
+ pr_info("%d: Modtec: Unknown TunerString: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
}
}
@@ -3666,7 +3660,7 @@ static void __devinit hauppauge_eeprom(struct bttv *btv)
btv->tuner_type = tv.tuner_type;
btv->has_radio = tv.has_radio;
- printk("bttv%d: Hauppauge eeprom indicates model#%d\n",
+ pr_info("%d: Hauppauge eeprom indicates model#%d\n",
btv->c.nr, tv.model);
/*
@@ -3674,7 +3668,7 @@ static void __devinit hauppauge_eeprom(struct bttv *btv)
* type based on model #.
*/
if(tv.model == 64900) {
- printk("bttv%d: Switching board type from %s to %s\n",
+ pr_info("%d: Switching board type from %s to %s\n",
btv->c.nr,
bttv_tvcards[btv->c.type].name,
bttv_tvcards[BTTV_BOARD_HAUPPAUGE_IMPACTVCB].name);
@@ -3701,8 +3695,7 @@ static int terratec_active_radio_upgrade(struct bttv *btv)
freq=88000/62.5;
tea5757_write(btv, 5 * freq + 0x358); /* write 0x1ed8 */
if (0x1ed8 == tea5757_read(btv)) {
- printk("bttv%d: Terratec Active Radio Upgrade found.\n",
- btv->c.nr);
+ pr_info("%d: Terratec Active Radio Upgrade found\n", btv->c.nr);
btv->has_radio = 1;
btv->has_saa6588 = 1;
btv->has_matchbox = 1;
@@ -3774,13 +3767,12 @@ static int __devinit pvr_boot(struct bttv *btv)
rc = request_firmware(&fw_entry, "hcwamc.rbf", &btv->c.pci->dev);
if (rc != 0) {
- printk(KERN_WARNING "bttv%d: no altera firmware [via hotplug]\n",
- btv->c.nr);
+ pr_warn("%d: no altera firmware [via hotplug]\n", btv->c.nr);
return rc;
}
rc = pvr_altera_load(btv, fw_entry->data, fw_entry->size);
- printk(KERN_INFO "bttv%d: altera firmware upload %s\n",
- btv->c.nr, (rc < 0) ? "failed" : "ok");
+ pr_info("%d: altera firmware upload %s\n",
+ btv->c.nr, (rc < 0) ? "failed" : "ok");
release_firmware(fw_entry);
return rc;
}
@@ -3876,29 +3868,27 @@ static void __devinit osprey_eeprom(struct bttv *btv, const u8 ee[256])
break;
default:
/* unknown...leave generic, but get serial # */
- printk(KERN_INFO "bttv%d: "
- "osprey eeprom: unknown card type 0x%04x\n",
- btv->c.nr, type);
+ pr_info("%d: osprey eeprom: unknown card type 0x%04x\n",
+ btv->c.nr, type);
break;
}
serial = get_unaligned_be32((__be32 *)(ee+6));
}
- printk(KERN_INFO "bttv%d: osprey eeprom: card=%d '%s' serial=%u\n",
- btv->c.nr, cardid,
- cardid>0 ? bttv_tvcards[cardid].name : "Unknown", serial);
+ pr_info("%d: osprey eeprom: card=%d '%s' serial=%u\n",
+ btv->c.nr, cardid,
+ cardid > 0 ? bttv_tvcards[cardid].name : "Unknown", serial);
if (cardid<0 || btv->c.type == cardid)
return;
/* card type isn't set correctly */
if (card[btv->c.nr] < bttv_num_tvcards) {
- printk(KERN_WARNING "bttv%d: osprey eeprom: "
- "Not overriding user specified card type\n", btv->c.nr);
+ pr_warn("%d: osprey eeprom: Not overriding user specified card type\n",
+ btv->c.nr);
} else {
- printk(KERN_INFO "bttv%d: osprey eeprom: "
- "Changing card type from %d to %d\n", btv->c.nr,
- btv->c.type, cardid);
+ pr_info("%d: osprey eeprom: Changing card type from %d to %d\n",
+ btv->c.nr, btv->c.type, cardid);
btv->c.type = cardid;
}
}
@@ -3941,14 +3931,14 @@ static void __devinit avermedia_eeprom(struct bttv *btv)
if (tuner_format == 0x09)
tuner_type = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */
- printk(KERN_INFO "bttv%d: Avermedia eeprom[0x%02x%02x]: tuner=",
+ pr_info("%d: Avermedia eeprom[0x%02x%02x]: tuner=",
btv->c.nr, eeprom_data[0x41], eeprom_data[0x42]);
if (tuner_type) {
btv->tuner_type = tuner_type;
- printk(KERN_CONT "%d", tuner_type);
+ pr_cont("%d", tuner_type);
} else
- printk(KERN_CONT "Unknown type");
- printk(KERN_CONT " radio:%s remote control:%s\n",
+ pr_cont("Unknown type");
+ pr_cont(" radio:%s remote control:%s\n",
tuner_tv_fm ? "yes" : "no",
btv->has_remote ? "yes" : "no");
}
@@ -3996,8 +3986,8 @@ static void __devinit boot_msp34xx(struct bttv *btv, int pin)
if (bttv_gpio)
bttv_gpio_tracking(btv,"msp34xx");
if (bttv_verbose)
- printk(KERN_INFO "bttv%d: Hauppauge/Voodoo msp34xx: reset line "
- "init [%d]\n", btv->c.nr, pin);
+ pr_info("%d: Hauppauge/Voodoo msp34xx: reset line init [%d]\n",
+ btv->c.nr, pin);
}
/* ----------------------------------------------------------------------- */
@@ -4037,7 +4027,7 @@ static void __devinit init_PXC200(struct bttv *btv)
btwrite(BT848_ADC_RESERVED|BT848_ADC_AGC_EN, BT848_ADC);
/* Initialise MAX517 DAC */
- printk(KERN_INFO "Setting DAC reference voltage level ...\n");
+ pr_info("Setting DAC reference voltage level ...\n");
bttv_I2CWrite(btv,0x5E,0,0x80,1);
/* Initialise 12C508 PIC */
@@ -4046,7 +4036,7 @@ static void __devinit init_PXC200(struct bttv *btv)
* argument so the numbers are different */
- printk(KERN_INFO "Initialising 12C508 PIC chip ...\n");
+ pr_info("Initialising 12C508 PIC chip ...\n");
/* First of all, enable the clock line. This is used in the PXC200-F */
val = btread(BT848_GPIO_DMA_CTL);
@@ -4065,13 +4055,12 @@ static void __devinit init_PXC200(struct bttv *btv)
for (i = 0; i < ARRAY_SIZE(vals); i++) {
tmp=bttv_I2CWrite(btv,0x1E,0,vals[i],1);
if (tmp != -1) {
- printk(KERN_INFO
- "I2C Write(%2.2x) = %i\nI2C Read () = %2.2x\n\n",
+ pr_info("I2C Write(%2.2x) = %i\nI2C Read () = %2.2x\n\n",
vals[i],tmp,bttv_I2CRead(btv,0x1F,NULL));
}
}
- printk(KERN_INFO "PXC200 Initialised.\n");
+ pr_info("PXC200 Initialised\n");
}
@@ -4110,8 +4099,7 @@ init_RTV24 (struct bttv *btv)
uint32_t dataRead = 0;
long watchdog_value = 0x0E;
- printk (KERN_INFO
- "bttv%d: Adlink RTV-24 initialisation in progress ...\n",
+ pr_info("%d: Adlink RTV-24 initialisation in progress ...\n",
btv->c.nr);
btwrite (0x00c3feff, BT848_GPIO_OUT_EN);
@@ -4125,8 +4113,7 @@ init_RTV24 (struct bttv *btv)
dataRead = btread (BT848_GPIO_DATA);
if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 1)) {
- printk (KERN_INFO
- "bttv%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n",
+ pr_info("%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n",
btv->c.nr, dataRead);
}
@@ -4139,15 +4126,13 @@ init_RTV24 (struct bttv *btv)
dataRead = btread (BT848_GPIO_DATA);
if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 0)) {
- printk (KERN_INFO
- "bttv%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n",
+ pr_info("%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n",
btv->c.nr, dataRead);
return;
}
- printk (KERN_INFO
- "bttv%d: Adlink RTV-24 initialisation complete.\n", btv->c.nr);
+ pr_info("%d: Adlink RTV-24 initialisation complete\n", btv->c.nr);
}
@@ -4264,22 +4249,25 @@ static int tea5757_read(struct bttv *btv)
while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout))
schedule();
if (bus_in(btv,btv->mbox_data)) {
- printk(KERN_WARNING "bttv%d: tea5757: read timeout\n",btv->c.nr);
+ pr_warn("%d: tea5757: read timeout\n", btv->c.nr);
return -1;
}
- dprintk("bttv%d: tea5757:",btv->c.nr);
+ dprintk("%d: tea5757:", btv->c.nr);
for (i = 0; i < 24; i++) {
udelay(5);
bus_high(btv,btv->mbox_clk);
udelay(5);
- dprintk("%c",(bus_in(btv,btv->mbox_most) == 0)?'T':'-');
+ dprintk_cont("%c",
+ bus_in(btv, btv->mbox_most) == 0 ? 'T' : '-');
bus_low(btv,btv->mbox_clk);
value <<= 1;
value |= (bus_in(btv,btv->mbox_data) == 0)?0:1; /* MSB first */
- dprintk("%c", (bus_in(btv,btv->mbox_most) == 0)?'S':'M');
+ dprintk_cont("%c",
+ bus_in(btv, btv->mbox_most) == 0 ? 'S' : 'M');
}
- dprintk("\nbttv%d: tea5757: read 0x%X\n", btv->c.nr, value);
+ dprintk_cont("\n");
+ dprintk("%d: tea5757: read 0x%X\n", btv->c.nr, value);
return value;
}
@@ -4298,7 +4286,7 @@ static int tea5757_write(struct bttv *btv, int value)
if (bttv_gpio)
bttv_gpio_tracking(btv,"tea5757 write");
- dprintk("bttv%d: tea5757: write 0x%X\n", btv->c.nr, value);
+ dprintk("%d: tea5757: write 0x%X\n", btv->c.nr, value);
bus_low(btv,btv->mbox_clk);
bus_high(btv,btv->mbox_we);
for (i = 0; i < 25; i++) {
@@ -4550,7 +4538,7 @@ static void picolo_tetra_init(struct bttv *btv)
static void picolo_tetra_muxsel (struct bttv* btv, unsigned int input)
{
- dprintk (KERN_DEBUG "bttv%d : picolo_tetra_muxsel => input = %d\n",btv->c.nr,input);
+ dprintk("%d : picolo_tetra_muxsel => input = %d\n", btv->c.nr, input);
/*Just set the right path in the analog multiplexers : channel 1 -> 4 ==> Analog Mux ==> MUX0*/
/*GPIO[20]&GPIO[21] used to choose the right input*/
btwrite (input<<20,BT848_GPIO_DATA);
@@ -4595,7 +4583,7 @@ static void ivc120_muxsel(struct bttv *btv, unsigned int input)
int key = input % 4;
int matrix = input / 4;
- dprintk("bttv%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n",
+ dprintk("%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n",
btv->c.nr, input, matrix, key);
/* Handles the input selection on the TDA8540's */
@@ -4652,15 +4640,17 @@ static void PXC200_muxsel(struct bttv *btv, unsigned int input)
buf[1]=0;
rc=bttv_I2CWrite(btv,(PX_I2C_PIC<<1),buf[0],buf[1],1);
if (rc) {
- printk(KERN_DEBUG "bttv%d: PXC200_muxsel: pic cfg write failed:%d\n", btv->c.nr,rc);
+ pr_debug("%d: PXC200_muxsel: pic cfg write failed:%d\n",
+ btv->c.nr, rc);
/* not PXC ? do nothing */
- return;
+ return;
}
rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),NULL);
if (!(rc & PX_CFG_PXC200F)) {
- printk(KERN_DEBUG "bttv%d: PXC200_muxsel: not PXC200F rc:%d \n", btv->c.nr,rc);
- return;
+ pr_debug("%d: PXC200_muxsel: not PXC200F rc:%d\n",
+ btv->c.nr, rc);
+ return;
}
@@ -4699,7 +4689,7 @@ static void PXC200_muxsel(struct bttv *btv, unsigned int input)
else /* older device */
btand(~BT848_IFORM_MUXSEL,BT848_IFORM);
- printk(KERN_DEBUG "bttv%d: setting input channel to:%d\n", btv->c.nr,(int)mux);
+ pr_debug("%d: setting input channel to:%d\n", btv->c.nr, (int)mux);
}
static void phytec_muxsel(struct bttv *btv, unsigned int input)
@@ -4850,29 +4840,27 @@ void __init bttv_check_chipset(void)
/* print warnings about any quirks found */
if (triton1)
- printk(KERN_INFO "bttv: Host bridge needs ETBF enabled.\n");
+ pr_info("Host bridge needs ETBF enabled\n");
if (vsfx)
- printk(KERN_INFO "bttv: Host bridge needs VSFX enabled.\n");
+ pr_info("Host bridge needs VSFX enabled\n");
if (pcipci_fail) {
- printk(KERN_INFO "bttv: bttv and your chipset may not work "
- "together.\n");
+ pr_info("bttv and your chipset may not work together\n");
if (!no_overlay) {
- printk(KERN_INFO "bttv: overlay will be disabled.\n");
+ pr_info("overlay will be disabled\n");
no_overlay = 1;
} else {
- printk(KERN_INFO "bttv: overlay forced. Use this "
- "option at your own risk.\n");
+ pr_info("overlay forced. Use this option at your own risk.\n");
}
}
if (UNSET != latency)
- printk(KERN_INFO "bttv: pci latency fixup [%d]\n",latency);
+ pr_info("pci latency fixup [%d]\n", latency);
while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82441, dev))) {
unsigned char b;
pci_read_config_byte(dev, 0x53, &b);
if (bttv_debug)
- printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "
- "bufcon=0x%02x\n",b);
+ pr_info("Host bridge: 82441FX Natoma, bufcon=0x%02x\n",
+ b);
}
}
@@ -4885,12 +4873,13 @@ int __devinit bttv_handle_chipset(struct bttv *btv)
if (bttv_verbose) {
if (triton1)
- printk(KERN_INFO "bttv%d: enabling ETBF (430FX/VP3 compatibilty)\n",btv->c.nr);
+ pr_info("%d: enabling ETBF (430FX/VP3 compatibility)\n",
+ btv->c.nr);
if (vsfx && btv->id >= 878)
- printk(KERN_INFO "bttv%d: enabling VSFX\n",btv->c.nr);
+ pr_info("%d: enabling VSFX\n", btv->c.nr);
if (UNSET != latency)
- printk(KERN_INFO "bttv%d: setting pci timer to %d\n",
- btv->c.nr,latency);
+ pr_info("%d: setting pci timer to %d\n",
+ btv->c.nr, latency);
}
if (btv->id < 878) {
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 834a48394bce..3dd06607aec2 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -34,6 +34,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
@@ -57,6 +59,7 @@
#include <media/saa6588.h>
+#define BTTV_VERSION "0.9.19"
unsigned int bttv_num; /* number of Bt848s in use */
struct bttv *bttvs[BTTV_MAX];
@@ -163,6 +166,7 @@ MODULE_PARM_DESC(radio_nr, "radio device numbers");
MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
MODULE_LICENSE("GPL");
+MODULE_VERSION(BTTV_VERSION);
/* ----------------------------------------------------------------------- */
/* sysfs */
@@ -940,8 +944,8 @@ static
void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits)
{
if ((fh->resources & bits) != bits) {
- /* trying to free ressources not allocated by us ... */
- printk("bttv: BUG! (btres)\n");
+ /* trying to free resources not allocated by us ... */
+ pr_err("BUG! (btres)\n");
}
fh->resources &= ~bits;
btv->resources &= ~bits;
@@ -998,7 +1002,7 @@ static void set_pll(struct bttv *btv)
return;
if (btv->pll.pll_ofreq == btv->pll.pll_current) {
- dprintk("bttv%d: PLL: no change required\n",btv->c.nr);
+ dprintk("%d: PLL: no change required\n", btv->c.nr);
return;
}
@@ -1006,21 +1010,23 @@ static void set_pll(struct bttv *btv)
/* no PLL needed */
if (btv->pll.pll_current == 0)
return;
- bttv_printk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n",
- btv->c.nr,btv->pll.pll_ifreq);
+ if (bttv_verbose)
+ pr_info("%d: PLL can sleep, using XTAL (%d)\n",
+ btv->c.nr, btv->pll.pll_ifreq);
btwrite(0x00,BT848_TGCTRL);
btwrite(0x00,BT848_PLL_XCI);
btv->pll.pll_current = 0;
return;
}
- bttv_printk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->c.nr,
- btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+ if (bttv_verbose)
+ pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n",
+ btv->c.nr,
+ btv->pll.pll_ifreq, btv->pll.pll_ofreq);
set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
for (i=0; i<10; i++) {
/* Let other people run while the PLL stabilizes */
- bttv_printk(".");
msleep(10);
if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
@@ -1028,12 +1034,14 @@ static void set_pll(struct bttv *btv)
} else {
btwrite(0x08,BT848_TGCTRL);
btv->pll.pll_current = btv->pll.pll_ofreq;
- bttv_printk(" ok\n");
+ if (bttv_verbose)
+ pr_info("PLL set ok\n");
return;
}
}
btv->pll.pll_current = -1;
- bttv_printk("failed\n");
+ if (bttv_verbose)
+ pr_info("Setting PLL failed\n");
return;
}
@@ -1045,7 +1053,7 @@ static void bt848A_set_timing(struct bttv *btv)
int fsc = bttv_tvnorms[btv->tvnorm].Fsc;
if (btv->input == btv->dig) {
- dprintk("bttv%d: load digital timing table (table_idx=%d)\n",
+ dprintk("%d: load digital timing table (table_idx=%d)\n",
btv->c.nr,table_idx);
/* timing change...reset timing generator address */
@@ -1074,7 +1082,7 @@ static void bt848_bright(struct bttv *btv, int bright)
{
int value;
- // printk("bttv: set bright: %d\n",bright); // DEBUG
+ // printk("set bright: %d\n", bright); // DEBUG
btv->bright = bright;
/* We want -128 to 127 we get 0-65535 */
@@ -1148,8 +1156,7 @@ video_mux(struct bttv *btv, unsigned int input)
}
mux = bttv_muxsel(btv, input);
btaor(mux<<5, ~(3<<5), BT848_IFORM);
- dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n",
- btv->c.nr,input,mux);
+ dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux);
/* card specific hook */
if(bttv_tvcards[btv->c.type].muxsel_hook)
@@ -1438,7 +1445,7 @@ static void bttv_reinit_bt848(struct bttv *btv)
unsigned long flags;
if (bttv_verbose)
- printk(KERN_INFO "bttv%d: reset, reinitialize\n",btv->c.nr);
+ pr_info("%d: reset, reinitialize\n", btv->c.nr);
spin_lock_irqsave(&btv->s_lock,flags);
btv->errors=0;
bttv_set_dma(btv,0);
@@ -1620,8 +1627,8 @@ void bttv_gpio_tracking(struct bttv *btv, char *comment)
unsigned int outbits, data;
outbits = btread(BT848_GPIO_OUT_EN);
data = btread(BT848_GPIO_DATA);
- printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
- btv->c.nr,outbits,data & outbits, data & ~outbits, comment);
+ pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+ btv->c.nr, outbits, data & outbits, data & ~outbits, comment);
}
static void bttv_field_count(struct bttv *btv)
@@ -1666,7 +1673,7 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
unsigned long flags;
int retval = 0;
- dprintk("switch_overlay: enter [new=%p]\n",new);
+ dprintk("switch_overlay: enter [new=%p]\n", new);
if (new)
new->vb.state = VIDEOBUF_DONE;
spin_lock_irqsave(&btv->s_lock,flags);
@@ -1676,7 +1683,8 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
bttv_set_dma(btv, 0x03);
spin_unlock_irqrestore(&btv->s_lock,flags);
if (NULL != old) {
- dprintk("switch_overlay: old=%p state is %d\n",old,old->vb.state);
+ dprintk("switch_overlay: old=%p state is %d\n",
+ old, old->vb.state);
bttv_dma_free(&fh->cap,btv, old);
kfree(old);
}
@@ -2027,11 +2035,11 @@ static int bttv_log_status(struct file *file, void *f)
struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
- printk(KERN_INFO "bttv%d: ======== START STATUS CARD #%d ========\n",
- btv->c.nr, btv->c.nr);
+ pr_info("%d: ======== START STATUS CARD #%d ========\n",
+ btv->c.nr, btv->c.nr);
bttv_call_all(btv, core, log_status);
- printk(KERN_INFO "bttv%d: ======== END STATUS CARD #%d ========\n",
- btv->c.nr, btv->c.nr);
+ pr_info("%d: ======== END STATUS CARD #%d ========\n",
+ btv->c.nr, btv->c.nr);
return 0;
}
@@ -2596,7 +2604,7 @@ static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
struct bttv *btv = fh->btv;
if (no_overlay > 0) {
- printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
@@ -2616,7 +2624,6 @@ static int bttv_querycap(struct file *file, void *priv,
strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info),
"PCI:%s", pci_name(btv->c.pci));
- cap->version = BTTV_VERSION_CODE;
cap->capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_VBI_CAPTURE |
@@ -2672,7 +2679,7 @@ static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv,
int rc;
if (no_overlay > 0) {
- printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
@@ -2713,7 +2720,7 @@ static int bttv_overlay(struct file *file, void *f, unsigned int on)
return -EINVAL;
}
if (unlikely(!fh->ov.setup_ok)) {
- dprintk("bttv%d: overlay: !setup_ok\n", btv->c.nr);
+ dprintk("%d: overlay: !setup_ok\n", btv->c.nr);
retval = -EINVAL;
}
if (retval)
@@ -3090,8 +3097,8 @@ static ssize_t bttv_read(struct file *file, char __user *data,
if (fh->btv->errors)
bttv_reinit_bt848(fh->btv);
- dprintk("bttv%d: read count=%d type=%s\n",
- fh->btv->c.nr,(int)count,v4l2_type_names[fh->type]);
+ dprintk("%d: read count=%d type=%s\n",
+ fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]);
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
@@ -3173,7 +3180,7 @@ static int bttv_open(struct file *file)
struct bttv_fh *fh;
enum v4l2_buf_type type = 0;
- dprintk(KERN_DEBUG "bttv: open dev=%s\n", video_device_node_name(vdev));
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
if (vdev->vfl_type == VFL_TYPE_GRABBER) {
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -3184,8 +3191,8 @@ static int bttv_open(struct file *file)
return -ENODEV;
}
- dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n",
- btv->c.nr,v4l2_type_names[type]);
+ dprintk("%d: open called (type=%s)\n",
+ btv->c.nr, v4l2_type_names[type]);
/* allocate per filehandle data */
fh = kmalloc(sizeof(*fh), GFP_KERNEL);
@@ -3287,7 +3294,7 @@ bttv_mmap(struct file *file, struct vm_area_struct *vma)
{
struct bttv_fh *fh = file->private_data;
- dprintk("bttv%d: mmap type=%s 0x%lx+%ld\n",
+ dprintk("%d: mmap type=%s 0x%lx+%ld\n",
fh->btv->c.nr, v4l2_type_names[fh->type],
vma->vm_start, vma->vm_end - vma->vm_start);
return videobuf_mmap_mapper(bttv_queue(fh),vma);
@@ -3369,9 +3376,9 @@ static int radio_open(struct file *file)
struct bttv *btv = video_drvdata(file);
struct bttv_fh *fh;
- dprintk("bttv: open dev=%s\n", video_device_node_name(vdev));
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
- dprintk("bttv%d: open called (radio)\n",btv->c.nr);
+ dprintk("%d: open called (radio)\n", btv->c.nr);
/* allocate per filehandle data */
fh = kmalloc(sizeof(*fh), GFP_KERNEL);
@@ -3416,7 +3423,6 @@ static int radio_querycap(struct file *file, void *priv,
strcpy(cap->driver, "bttv");
strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
- cap->version = BTTV_VERSION_CODE;
cap->capabilities = V4L2_CAP_TUNER;
return 0;
@@ -3616,12 +3622,12 @@ static int bttv_risc_decode(u32 risc)
};
int i;
- printk("0x%08x [ %s", risc,
+ pr_cont("0x%08x [ %s", risc,
instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
if (risc & (1 << (i + 12)))
- printk(" %s",bits[i]);
- printk(" count=%d ]\n", risc & 0xfff);
+ pr_cont(" %s", bits[i]);
+ pr_cont(" count=%d ]\n", risc & 0xfff);
return incr[risc >> 28] ? incr[risc >> 28] : 1;
}
@@ -3630,16 +3636,18 @@ static void bttv_risc_disasm(struct bttv *btv,
{
unsigned int i,j,n;
- printk("%s: risc disasm: %p [dma=0x%08lx]\n",
- btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
+ pr_info("%s: risc disasm: %p [dma=0x%08lx]\n",
+ btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
for (i = 0; i < (risc->size >> 2); i += n) {
- printk("%s: 0x%lx: ", btv->c.v4l2_dev.name,
- (unsigned long)(risc->dma + (i<<2)));
+ pr_info("%s: 0x%lx: ",
+ btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + (i<<2)));
n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
for (j = 1; j < n; j++)
- printk("%s: 0x%lx: 0x%08x [ arg #%d ]\n",
- btv->c.v4l2_dev.name, (unsigned long)(risc->dma + ((i+j)<<2)),
- risc->cpu[i+j], j);
+ pr_info("%s: 0x%lx: 0x%08x [ arg #%d ]\n",
+ btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + ((i+j)<<2)),
+ risc->cpu[i+j], j);
if (0 == risc->cpu[i])
break;
}
@@ -3647,17 +3655,18 @@ static void bttv_risc_disasm(struct bttv *btv,
static void bttv_print_riscaddr(struct bttv *btv)
{
- printk(" main: %08Lx\n",
- (unsigned long long)btv->main.dma);
- printk(" vbi : o=%08Lx e=%08Lx\n",
- btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
- btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
- printk(" cap : o=%08Lx e=%08Lx\n",
- btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0,
- btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
- printk(" scr : o=%08Lx e=%08Lx\n",
- btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
- btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
+ pr_info(" main: %08llx\n", (unsigned long long)btv->main.dma);
+ pr_info(" vbi : o=%08llx e=%08llx\n",
+ btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+ btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
+ pr_info(" cap : o=%08llx e=%08llx\n",
+ btv->curr.top
+ ? (unsigned long long)btv->curr.top->top.dma : 0,
+ btv->curr.bottom
+ ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+ pr_info(" scr : o=%08llx e=%08llx\n",
+ btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
+ btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
bttv_risc_disasm(btv, &btv->main);
}
@@ -3690,34 +3699,34 @@ static void bttv_print_irqbits(u32 print, u32 mark)
{
unsigned int i;
- printk("bits:");
+ pr_cont("bits:");
for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
if (print & (1 << i))
- printk(" %s",irq_name[i]);
+ pr_cont(" %s", irq_name[i]);
if (mark & (1 << i))
- printk("*");
+ pr_cont("*");
}
}
static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
{
- printk("bttv%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
- btv->c.nr,
- (unsigned long)btv->main.dma,
- (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
- (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
- (unsigned long)rc);
+ pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
+ btv->c.nr,
+ (unsigned long)btv->main.dma,
+ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
+ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
+ (unsigned long)rc);
if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
- printk("bttv%d: Oh, there (temporarely?) is no input signal. "
- "Ok, then this is harmless, don't worry ;)\n",
- btv->c.nr);
+ pr_notice("%d: Oh, there (temporarily?) is no input signal. "
+ "Ok, then this is harmless, don't worry ;)\n",
+ btv->c.nr);
return;
}
- printk("bttv%d: Uhm. Looks like we have unusual high IRQ latencies.\n",
- btv->c.nr);
- printk("bttv%d: Lets try to catch the culpit red-handed ...\n",
- btv->c.nr);
+ pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n",
+ btv->c.nr);
+ pr_notice("%d: Lets try to catch the culpit red-handed ...\n",
+ btv->c.nr);
dump_stack();
}
@@ -3798,9 +3807,9 @@ bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
}
}
- dprintk("bttv%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
- btv->c.nr,set->top, set->bottom,
- btv->screen,set->frame_irq,set->top_irq);
+ dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
+ btv->c.nr, set->top, set->bottom,
+ btv->screen, set->frame_irq, set->top_irq);
return 0;
}
@@ -3815,7 +3824,8 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
if (wakeup->top == wakeup->bottom) {
if (NULL != wakeup->top && curr->top != wakeup->top) {
if (irq_debug > 1)
- printk("bttv%d: wakeup: both=%p\n",btv->c.nr,wakeup->top);
+ pr_debug("%d: wakeup: both=%p\n",
+ btv->c.nr, wakeup->top);
wakeup->top->vb.ts = ts;
wakeup->top->vb.field_count = btv->field_count;
wakeup->top->vb.state = state;
@@ -3824,7 +3834,8 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
} else {
if (NULL != wakeup->top && curr->top != wakeup->top) {
if (irq_debug > 1)
- printk("bttv%d: wakeup: top=%p\n",btv->c.nr,wakeup->top);
+ pr_debug("%d: wakeup: top=%p\n",
+ btv->c.nr, wakeup->top);
wakeup->top->vb.ts = ts;
wakeup->top->vb.field_count = btv->field_count;
wakeup->top->vb.state = state;
@@ -3832,7 +3843,8 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
}
if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
if (irq_debug > 1)
- printk("bttv%d: wakeup: bottom=%p\n",btv->c.nr,wakeup->bottom);
+ pr_debug("%d: wakeup: bottom=%p\n",
+ btv->c.nr, wakeup->bottom);
wakeup->bottom->vb.ts = ts;
wakeup->bottom->vb.field_count = btv->field_count;
wakeup->bottom->vb.state = state;
@@ -3866,11 +3878,11 @@ static void bttv_irq_timeout(unsigned long data)
unsigned long flags;
if (bttv_verbose) {
- printk(KERN_INFO "bttv%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
- btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
- btread(BT848_RISC_COUNT));
+ pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
+ btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
+ btread(BT848_RISC_COUNT));
bttv_print_irqbits(btread(BT848_INT_STAT),0);
- printk("\n");
+ pr_cont("\n");
}
spin_lock_irqsave(&btv->s_lock,flags);
@@ -4033,21 +4045,23 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
dstat=btread(BT848_DSTATUS);
if (irq_debug) {
- printk(KERN_DEBUG "bttv%d: irq loop=%d fc=%d "
- "riscs=%x, riscc=%08x, ",
- btv->c.nr, count, btv->field_count,
- stat>>28, btread(BT848_RISC_COUNT));
+ pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ",
+ btv->c.nr, count, btv->field_count,
+ stat>>28, btread(BT848_RISC_COUNT));
bttv_print_irqbits(stat,astat);
if (stat & BT848_INT_HLOCK)
- printk(" HLOC => %s", (dstat & BT848_DSTATUS_HLOC)
- ? "yes" : "no");
+ pr_cont(" HLOC => %s",
+ dstat & BT848_DSTATUS_HLOC
+ ? "yes" : "no");
if (stat & BT848_INT_VPRES)
- printk(" PRES => %s", (dstat & BT848_DSTATUS_PRES)
- ? "yes" : "no");
+ pr_cont(" PRES => %s",
+ dstat & BT848_DSTATUS_PRES
+ ? "yes" : "no");
if (stat & BT848_INT_FMTCHG)
- printk(" NUML => %s", (dstat & BT848_DSTATUS_NUML)
- ? "625" : "525");
- printk("\n");
+ pr_cont(" NUML => %s",
+ dstat & BT848_DSTATUS_NUML
+ ? "625" : "525");
+ pr_cont("\n");
}
if (astat&BT848_INT_VSYNC)
@@ -4075,18 +4089,19 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
audio_mute(btv, btv->mute); /* trigger automute */
if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
- printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->c.nr,
- (astat & BT848_INT_SCERR) ? "SCERR" : "",
- (astat & BT848_INT_OCERR) ? "OCERR" : "",
- btread(BT848_RISC_COUNT));
+ pr_info("%d: %s%s @ %08x,",
+ btv->c.nr,
+ (astat & BT848_INT_SCERR) ? "SCERR" : "",
+ (astat & BT848_INT_OCERR) ? "OCERR" : "",
+ btread(BT848_RISC_COUNT));
bttv_print_irqbits(stat,astat);
- printk("\n");
+ pr_cont("\n");
if (bttv_debug)
bttv_print_riscaddr(btv);
}
if (fdsr && astat & BT848_INT_FDSR) {
- printk(KERN_INFO "bttv%d: FDSR @ %08x\n",
- btv->c.nr,btread(BT848_RISC_COUNT));
+ pr_info("%d: FDSR @ %08x\n",
+ btv->c.nr, btread(BT848_RISC_COUNT));
if (bttv_debug)
bttv_print_riscaddr(btv);
}
@@ -4097,11 +4112,11 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
if (count > 8 || !(astat & BT848_INT_GPINT)) {
btwrite(0, BT848_INT_MASK);
- printk(KERN_ERR
- "bttv%d: IRQ lockup, cleared int mask [", btv->c.nr);
+ pr_err("%d: IRQ lockup, cleared int mask [",
+ btv->c.nr);
} else {
- printk(KERN_ERR
- "bttv%d: IRQ lockup, clearing GPINT from int mask [", btv->c.nr);
+ pr_err("%d: IRQ lockup, clearing GPINT from int mask [",
+ btv->c.nr);
btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
BT848_INT_MASK);
@@ -4109,7 +4124,7 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
bttv_print_irqbits(stat,astat);
- printk("]\n");
+ pr_cont("]\n");
}
}
btv->irq_total++;
@@ -4171,7 +4186,7 @@ static void bttv_unregister_video(struct bttv *btv)
static int __devinit bttv_register_video(struct bttv *btv)
{
if (no_overlay > 0)
- printk("bttv: Overlay support disabled.\n");
+ pr_notice("Overlay support disabled\n");
/* video */
btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
@@ -4181,12 +4196,11 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
video_nr[btv->c.nr]) < 0)
goto err;
- printk(KERN_INFO "bttv%d: registered device %s\n",
- btv->c.nr, video_device_node_name(btv->video_dev));
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->video_dev));
if (device_create_file(&btv->video_dev->dev,
&dev_attr_card)<0) {
- printk(KERN_ERR "bttv%d: device_create_file 'card' "
- "failed\n", btv->c.nr);
+ pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
goto err;
}
@@ -4198,8 +4212,8 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
vbi_nr[btv->c.nr]) < 0)
goto err;
- printk(KERN_INFO "bttv%d: registered device %s\n",
- btv->c.nr, video_device_node_name(btv->vbi_dev));
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->vbi_dev));
if (!btv->has_radio)
return 0;
@@ -4210,8 +4224,8 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
radio_nr[btv->c.nr]) < 0)
goto err;
- printk(KERN_INFO "bttv%d: registered device %s\n",
- btv->c.nr, video_device_node_name(btv->radio_dev));
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->radio_dev));
/* all done */
return 0;
@@ -4244,10 +4258,10 @@ static int __devinit bttv_probe(struct pci_dev *dev,
if (bttv_num == BTTV_MAX)
return -ENOMEM;
- printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);
+ pr_info("Bt8xx card found (%d)\n", bttv_num);
bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
if (btv == NULL) {
- printk(KERN_ERR "bttv: out of memory.\n");
+ pr_err("out of memory\n");
return -ENOMEM;
}
btv->c.nr = bttv_num;
@@ -4277,21 +4291,19 @@ static int __devinit bttv_probe(struct pci_dev *dev,
btv->c.pci = dev;
btv->id = dev->device;
if (pci_enable_device(dev)) {
- printk(KERN_WARNING "bttv%d: Can't enable device.\n",
- btv->c.nr);
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
return -EIO;
}
if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
- printk(KERN_WARNING "bttv%d: No suitable DMA available.\n",
- btv->c.nr);
+ pr_warn("%d: No suitable DMA available\n", btv->c.nr);
return -EIO;
}
if (!request_mem_region(pci_resource_start(dev,0),
pci_resource_len(dev,0),
btv->c.v4l2_dev.name)) {
- printk(KERN_WARNING "bttv%d: can't request iomem (0x%llx).\n",
- btv->c.nr,
- (unsigned long long)pci_resource_start(dev,0));
+ pr_warn("%d: can't request iomem (0x%llx)\n",
+ btv->c.nr,
+ (unsigned long long)pci_resource_start(dev, 0));
return -EBUSY;
}
pci_set_master(dev);
@@ -4299,22 +4311,21 @@ static int __devinit bttv_probe(struct pci_dev *dev,
result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
if (result < 0) {
- printk(KERN_WARNING "bttv%d: v4l2_device_register() failed\n", btv->c.nr);
+ pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr);
goto fail0;
}
btv->revision = dev->revision;
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
- printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %s, ",
- bttv_num,btv->id, btv->revision, pci_name(dev));
- printk("irq: %d, latency: %d, mmio: 0x%llx\n",
- btv->c.pci->irq, lat,
- (unsigned long long)pci_resource_start(dev,0));
+ pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n",
+ bttv_num, btv->id, btv->revision, pci_name(dev),
+ btv->c.pci->irq, lat,
+ (unsigned long long)pci_resource_start(dev, 0));
schedule();
btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
if (NULL == btv->bt848_mmio) {
- printk("bttv%d: ioremap() failed\n", btv->c.nr);
+ pr_err("%d: ioremap() failed\n", btv->c.nr);
result = -EIO;
goto fail1;
}
@@ -4327,8 +4338,8 @@ static int __devinit bttv_probe(struct pci_dev *dev,
result = request_irq(btv->c.pci->irq, bttv_irq,
IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
if (result < 0) {
- printk(KERN_ERR "bttv%d: can't get IRQ %d\n",
- bttv_num,btv->c.pci->irq);
+ pr_err("%d: can't get IRQ %d\n",
+ bttv_num, btv->c.pci->irq);
goto fail1;
}
@@ -4433,7 +4444,7 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev)
struct bttv *btv = to_bttv(v4l2_dev);
if (bttv_verbose)
- printk("bttv%d: unloading\n",btv->c.nr);
+ pr_info("%d: unloading\n", btv->c.nr);
if (bttv_tvcards[btv->c.type].has_dvb)
flush_request_modules(btv);
@@ -4481,7 +4492,7 @@ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
struct bttv_buffer_set idle;
unsigned long flags;
- dprintk("bttv%d: suspend %d\n", btv->c.nr, state.event);
+ dprintk("%d: suspend %d\n", btv->c.nr, state.event);
/* stop dma + irqs */
spin_lock_irqsave(&btv->s_lock,flags);
@@ -4517,14 +4528,13 @@ static int bttv_resume(struct pci_dev *pci_dev)
unsigned long flags;
int err;
- dprintk("bttv%d: resume\n", btv->c.nr);
+ dprintk("%d: resume\n", btv->c.nr);
/* restore pci state */
if (btv->state.disabled) {
err=pci_enable_device(pci_dev);
if (err) {
- printk(KERN_WARNING "bttv%d: Can't enable device.\n",
- btv->c.nr);
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
return err;
}
btv->state.disabled = 0;
@@ -4532,8 +4542,7 @@ static int bttv_resume(struct pci_dev *pci_dev)
err=pci_set_power_state(pci_dev, PCI_D0);
if (err) {
pci_disable_device(pci_dev);
- printk(KERN_WARNING "bttv%d: Can't enable device.\n",
- btv->c.nr);
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
btv->state.disabled = 1;
return err;
}
@@ -4585,28 +4594,21 @@ static int __init bttv_init_module(void)
bttv_num = 0;
- printk(KERN_INFO "bttv: driver version %d.%d.%d loaded\n",
- (BTTV_VERSION_CODE >> 16) & 0xff,
- (BTTV_VERSION_CODE >> 8) & 0xff,
- BTTV_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO "bttv: snapshot date %04d-%02d-%02d\n",
- SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+ pr_info("driver version %s loaded\n", BTTV_VERSION);
if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
gbuffers = 2;
if (gbufsize > BTTV_MAX_FBUF)
gbufsize = BTTV_MAX_FBUF;
gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
if (bttv_verbose)
- printk(KERN_INFO "bttv: using %d buffers with %dk (%d pages) each for capture\n",
- gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
+ pr_info("using %d buffers with %dk (%d pages) each for capture\n",
+ gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
bttv_check_chipset();
ret = bus_register(&bttv_sub_bus_type);
if (ret < 0) {
- printk(KERN_WARNING "bttv: bus_register error: %d\n", ret);
+ pr_warn("bus_register error: %d\n", ret);
return ret;
}
ret = pci_register_driver(&bttv_pci_driver);
diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c
index 13ce72c04b33..922e8233fd0b 100644
--- a/drivers/media/video/bt8xx/bttv-gpio.c
+++ b/drivers/media/video/bt8xx/bttv-gpio.c
@@ -26,6 +26,8 @@
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -99,7 +101,7 @@ int bttv_sub_add_device(struct bttv_core *core, char *name)
kfree(sub);
return err;
}
- printk("bttv%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev));
+ pr_info("%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev));
list_add_tail(&sub->list,&core->subs);
return 0;
}
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index d49b675045fe..e3952af7e56e 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -27,6 +27,8 @@
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -154,9 +156,7 @@ bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
if (retval == 0)
goto eio;
if (i2c_debug) {
- printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
- if (!(xmit & BT878_I2C_NOSTOP))
- printk(" >\n");
+ pr_cont(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
}
for (cnt = 1; cnt < msg->len; cnt++ ) {
@@ -170,19 +170,18 @@ bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
goto err;
if (retval == 0)
goto eio;
- if (i2c_debug) {
- printk(" %02x", msg->buf[cnt]);
- if (!(xmit & BT878_I2C_NOSTOP))
- printk(" >\n");
- }
+ if (i2c_debug)
+ pr_cont(" %02x", msg->buf[cnt]);
}
+ if (!(xmit & BT878_I2C_NOSTOP))
+ pr_cont(">\n");
return msg->len;
eio:
retval = -EIO;
err:
if (i2c_debug)
- printk(" ERR: %d\n",retval);
+ pr_cont(" ERR: %d\n",retval);
return retval;
}
@@ -193,7 +192,7 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
u32 cnt;
int retval;
- for(cnt = 0; cnt < msg->len; cnt++) {
+ for (cnt = 0; cnt < msg->len; cnt++) {
xmit = (msg->addr << 25) | (1 << 24) | I2C_HW;
if (cnt < msg->len-1)
xmit |= BT848_I2C_W3B;
@@ -201,6 +200,12 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
xmit |= BT878_I2C_NOSTOP;
if (cnt)
xmit |= BT878_I2C_NOSTART;
+
+ if (i2c_debug) {
+ if (!(xmit & BT878_I2C_NOSTART))
+ pr_cont(" <R %02x", (msg->addr << 1) +1);
+ }
+
btwrite(xmit, BT848_I2C);
retval = bttv_i2c_wait_done(btv);
if (retval < 0)
@@ -209,20 +214,20 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
goto eio;
msg->buf[cnt] = ((u32)btread(BT848_I2C) >> 8) & 0xff;
if (i2c_debug) {
- if (!(xmit & BT878_I2C_NOSTART))
- printk(" <R %02x", (msg->addr << 1) +1);
- printk(" =%02x", msg->buf[cnt]);
- if (!(xmit & BT878_I2C_NOSTOP))
- printk(" >\n");
+ pr_cont(" =%02x", msg->buf[cnt]);
}
+ if (i2c_debug && !(xmit & BT878_I2C_NOSTOP))
+ pr_cont(" >\n");
}
+
+
return msg->len;
eio:
retval = -EIO;
err:
if (i2c_debug)
- printk(" ERR: %d\n",retval);
+ pr_cont(" ERR: %d\n",retval);
return retval;
}
@@ -234,7 +239,8 @@ static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int
int i;
if (i2c_debug)
- printk("bt-i2c:");
+ pr_debug("bt-i2c:");
+
btwrite(BT848_INT_I2CDONE|BT848_INT_RACK, BT848_INT_STAT);
for (i = 0 ; i < num; i++) {
if (msgs[i].flags & I2C_M_RD) {
@@ -271,20 +277,20 @@ int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for)
if (0 != btv->i2c_rc)
return -1;
if (bttv_verbose && NULL != probe_for)
- printk(KERN_INFO "bttv%d: i2c: checking for %s @ 0x%02x... ",
- btv->c.nr,probe_for,addr);
+ pr_info("%d: i2c: checking for %s @ 0x%02x... ",
+ btv->c.nr, probe_for, addr);
btv->i2c_client.addr = addr >> 1;
if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) {
if (NULL != probe_for) {
if (bttv_verbose)
- printk("not found\n");
+ pr_cont("not found\n");
} else
- printk(KERN_WARNING "bttv%d: i2c read 0x%x: error\n",
- btv->c.nr,addr);
+ pr_warn("%d: i2c read 0x%x: error\n",
+ btv->c.nr, addr);
return -1;
}
if (bttv_verbose && NULL != probe_for)
- printk("found\n");
+ pr_cont("found\n");
return buffer;
}
@@ -335,8 +341,8 @@ static void do_i2c_scan(char *name, struct i2c_client *c)
rc = i2c_master_recv(c,&buf,0);
if (rc < 0)
continue;
- printk("%s: i2c scan: found device @ 0x%x [%s]\n",
- name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ pr_info("%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
}
}
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
index 677d70c0e1ce..ef4c7cd41982 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -36,9 +38,10 @@ static int ir_rc5_remote_gap = 885;
module_param(ir_rc5_remote_gap, int, 0644);
#undef dprintk
-#define dprintk(arg...) do { \
- if (ir_debug >= 1) \
- printk(arg); \
+#define dprintk(fmt, ...) \
+do { \
+ if (ir_debug >= 1) \
+ pr_info(fmt, ##__VA_ARGS__); \
} while (0)
#define DEVNAME "bttv-input"
@@ -62,7 +65,7 @@ static void ir_handle_key(struct bttv *btv)
/* extract data */
data = ir_extract_bits(gpio, ir->mask_keycode);
- dprintk(KERN_INFO DEVNAME ": irq gpio=0x%x code=%d | %s%s%s\n",
+ dprintk("irq gpio=0x%x code=%d | %s%s%s\n",
gpio, data,
ir->polling ? "poll" : "irq",
(gpio & ir->mask_keydown) ? " down" : "",
@@ -96,7 +99,7 @@ static void ir_enltv_handle_key(struct bttv *btv)
keyup = (gpio & ir->mask_keyup) ? 1 << 31 : 0;
if ((ir->last_gpio & 0x7f) != data) {
- dprintk(KERN_INFO DEVNAME ": gpio=0x%x code=%d | %s\n",
+ dprintk("gpio=0x%x code=%d | %s\n",
gpio, data,
(gpio & ir->mask_keyup) ? " up" : "up/down");
@@ -107,7 +110,7 @@ static void ir_enltv_handle_key(struct bttv *btv)
if ((ir->last_gpio & 1 << 31) == keyup)
return;
- dprintk(KERN_INFO DEVNAME ":(cnt) gpio=0x%x code=%d | %s\n",
+ dprintk("(cnt) gpio=0x%x code=%d | %s\n",
gpio, data,
(gpio & ir->mask_keyup) ? " up" : "down");
@@ -177,13 +180,12 @@ static u32 bttv_rc5_decode(unsigned int code)
rc5 |= 1;
break;
case 3:
- dprintk(KERN_INFO DEVNAME ":rc5_decode(%x) bad code\n",
+ dprintk("rc5_decode(%x) bad code\n",
org_code);
return 0;
}
}
- dprintk(KERN_INFO DEVNAME ":"
- "code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
+ dprintk("code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
"instr=%x\n", rc5, org_code, RC5_START(rc5),
RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
return rc5;
@@ -212,20 +214,20 @@ static void bttv_rc5_timer_end(unsigned long data)
/* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */
if (gap < 28000) {
- dprintk(KERN_INFO DEVNAME ": spurious timer_end\n");
+ dprintk("spurious timer_end\n");
return;
}
if (ir->last_bit < 20) {
/* ignore spurious codes (caused by light/other remotes) */
- dprintk(KERN_INFO DEVNAME ": short code: %x\n", ir->code);
+ dprintk("short code: %x\n", ir->code);
} else {
ir->code = (ir->code << ir->shift_by) | 1;
rc5 = bttv_rc5_decode(ir->code);
/* two start bits? */
if (RC5_START(rc5) != ir->start) {
- printk(KERN_INFO DEVNAME ":"
+ pr_info(DEVNAME ":"
" rc5 start bits invalid: %u\n", RC5_START(rc5));
/* right address? */
@@ -235,8 +237,7 @@ static void bttv_rc5_timer_end(unsigned long data)
/* Good code */
rc_keydown(ir->dev, instr, toggle);
- dprintk(KERN_INFO DEVNAME ":"
- " instruction %x, toggle %x\n",
+ dprintk("instruction %x, toggle %x\n",
instr, toggle);
}
}
@@ -265,7 +266,7 @@ static int bttv_rc5_irq(struct bttv *btv)
tv.tv_usec - ir->base_time.tv_usec;
}
- dprintk(KERN_INFO DEVNAME ": RC5 IRQ: gap %d us for %s\n",
+ dprintk("RC5 IRQ: gap %d us for %s\n",
gap, (gpio & 0x20) ? "mark" : "space");
/* remote IRQ? */
@@ -340,14 +341,14 @@ static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
/* poll IR chip */
if (1 != i2c_master_recv(ir->c, &b, 1)) {
- dprintk(KERN_INFO DEVNAME ": read error\n");
+ dprintk("read error\n");
return -EIO;
}
/* ignore 0xaa */
if (b==0xaa)
return 0;
- dprintk(KERN_INFO DEVNAME ": key %02x\n", b);
+ dprintk("key %02x\n", b);
/*
* NOTE:
@@ -517,7 +518,7 @@ int bttv_input_init(struct bttv *btv)
break;
}
if (NULL == ir_codes) {
- dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n", btv->c.type);
+ dprintk("Ooops: IR config error [card=%d]\n", btv->c.type);
err = -ENODEV;
goto err_out_free;
}
diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c
index 9b57d091da48..82cc47d2e3fa 100644
--- a/drivers/media/video/bt8xx/bttv-risc.c
+++ b/drivers/media/video/bt8xx/bttv-risc.c
@@ -24,6 +24,8 @@
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -473,8 +475,7 @@ bttv_set_dma(struct bttv *btv, int override)
capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00; /* vbi data */
capctl |= override;
- d2printk(KERN_DEBUG
- "bttv%d: capctl=%x lirq=%d top=%08Lx/%08Lx even=%08Lx/%08Lx\n",
+ d2printk("%d: capctl=%x lirq=%d top=%08llx/%08llx even=%08llx/%08llx\n",
btv->c.nr,capctl,btv->loop_irq,
btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0,
@@ -517,8 +518,8 @@ bttv_risc_init_main(struct bttv *btv)
if ((rc = btcx_riscmem_alloc(btv->c.pci,&btv->main,PAGE_SIZE)) < 0)
return rc;
- dprintk(KERN_DEBUG "bttv%d: risc main @ %08Lx\n",
- btv->c.nr,(unsigned long long)btv->main.dma);
+ dprintk("%d: risc main @ %08llx\n",
+ btv->c.nr, (unsigned long long)btv->main.dma);
btv->main.cpu[0] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
BT848_FIFO_STATUS_VRE);
@@ -557,12 +558,12 @@ bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc,
unsigned long next = btv->main.dma + ((slot+2) << 2);
if (NULL == risc) {
- d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=NULL\n",
- btv->c.nr,risc,slot);
+ d2printk("%d: risc=%p slot[%d]=NULL\n", btv->c.nr, risc, slot);
btv->main.cpu[slot+1] = cpu_to_le32(next);
} else {
- d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=%08Lx irq=%d\n",
- btv->c.nr,risc,slot,(unsigned long long)risc->dma,irqflags);
+ d2printk("%d: risc=%p slot[%d]=%08llx irq=%d\n",
+ btv->c.nr, risc, slot,
+ (unsigned long long)risc->dma, irqflags);
cmd = BT848_RISC_JUMP;
if (irqflags) {
cmd |= BT848_RISC_IRQ;
@@ -708,8 +709,7 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm;
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- dprintk(KERN_DEBUG
- "bttv%d: buffer field: %s format: %s size: %dx%d\n",
+ dprintk("%d: buffer field: %s format: %s size: %dx%d\n",
btv->c.nr, v4l2_field_names[buf->vb.field],
buf->fmt->name, buf->vb.width, buf->vb.height);
@@ -870,10 +870,9 @@ bttv_overlay_risc(struct bttv *btv,
struct bttv_buffer *buf)
{
/* check interleave, bottom+top fields */
- dprintk(KERN_DEBUG
- "bttv%d: overlay fields: %s format: %s size: %dx%d\n",
+ dprintk("%d: overlay fields: %s format: %s size: %dx%d\n",
btv->c.nr, v4l2_field_names[buf->vb.field],
- fmt->name,ov->w.width,ov->w.height);
+ fmt->name, ov->w.width, ov->w.height);
/* calculate geometry */
bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height,
diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c
index e79a402fa6cd..b433267d9aa9 100644
--- a/drivers/media/video/bt8xx/bttv-vbi.c
+++ b/drivers/media/video/bt8xx/bttv-vbi.c
@@ -23,6 +23,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -65,8 +67,11 @@ MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)");
#ifdef dprintk
# undef dprintk
#endif
-#define dprintk(fmt, arg...) if (vbi_debug) \
- printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->c.nr , ## arg)
+#define dprintk(fmt, ...) \
+do { \
+ if (vbi_debug) \
+ pr_debug("%d: " fmt, btv->c.nr, ##__VA_ARGS__); \
+} while (0)
#define IMAGE_SIZE(fmt) \
(((fmt)->count[0] + (fmt)->count[1]) * (fmt)->samples_per_line)
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index 9b776faf0741..db943a8d580d 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -25,9 +25,6 @@
#ifndef _BTTVP_H_
#define _BTTVP_H_
-#include <linux/version.h>
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,18)
-
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/i2c.h>
@@ -313,9 +310,21 @@ extern unsigned int bttv_gpio;
extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
extern int init_bttv_i2c(struct bttv *btv);
-#define bttv_printk if (bttv_verbose) printk
-#define dprintk if (bttv_debug >= 1) printk
-#define d2printk if (bttv_debug >= 2) printk
+#define dprintk(fmt, ...) \
+do { \
+ if (bttv_debug >= 1) \
+ pr_debug(fmt, ##__VA_ARGS__); \
+} while (0)
+#define dprintk_cont(fmt, ...) \
+do { \
+ if (bttv_debug >= 1) \
+ pr_cont(fmt, ##__VA_ARGS__); \
+} while (0)
+#define d2printk(fmt, ...) \
+do { \
+ if (bttv_debug >= 2) \
+ printk(fmt, ##__VA_ARGS__); \
+} while (0)
#define BTTV_MAX_FBUF 0x208000
#define BTTV_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c
index c1193506131c..f09df9dffaae 100644
--- a/drivers/media/video/bw-qcam.c
+++ b/drivers/media/video/bw-qcam.c
@@ -71,7 +71,6 @@ OTHER DEALINGS IN THE SOFTWARE.
#include <linux/mm.h>
#include <linux/parport.h>
#include <linux/sched.h>
-#include <linux/version.h>
#include <linux/videodev2.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
@@ -647,7 +646,6 @@ static int qcam_querycap(struct file *file, void *priv,
strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, "B&W Quickcam", sizeof(vcap->card));
strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
- vcap->version = KERNEL_VERSION(0, 0, 2);
vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
return 0;
}
@@ -895,6 +893,7 @@ static struct qcam *qcam_init(struct parport *port)
if (v4l2_device_register(NULL, v4l2_dev) < 0) {
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ kfree(qcam);
return NULL;
}
@@ -1092,3 +1091,4 @@ module_init(init_bw_qcams);
module_exit(exit_bw_qcams);
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c
index 24fc00965a12..cd8ff0473184 100644
--- a/drivers/media/video/c-qcam.c
+++ b/drivers/media/video/c-qcam.c
@@ -35,7 +35,6 @@
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/jiffies.h>
-#include <linux/version.h>
#include <linux/videodev2.h>
#include <asm/uaccess.h>
#include <media/v4l2-device.h>
@@ -517,7 +516,6 @@ static int qcam_querycap(struct file *file, void *priv,
strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card));
strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
- vcap->version = KERNEL_VERSION(0, 0, 3);
vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
return 0;
}
@@ -752,6 +750,7 @@ static struct qcam *qcam_init(struct parport *port)
if (v4l2_device_register(NULL, v4l2_dev) < 0) {
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ kfree(qcam);
return NULL;
}
@@ -886,6 +885,7 @@ static void __exit cqcam_cleanup(void)
MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
MODULE_DESCRIPTION(BANNER);
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.4");
module_init(cqcam_init);
module_exit(cqcam_cleanup);
diff --git a/drivers/media/video/cafe_ccic-regs.h b/drivers/media/video/cafe_ccic-regs.h
deleted file mode 100644
index 8e2a87cdc791..000000000000
--- a/drivers/media/video/cafe_ccic-regs.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Register definitions for the m88alp01 camera interface. Offsets in bytes
- * as given in the spec.
- *
- * Copyright 2006 One Laptop Per Child Association, Inc.
- *
- * Written by Jonathan Corbet, corbet@lwn.net.
- *
- * This file may be distributed under the terms of the GNU General
- * Public License, version 2.
- */
-#define REG_Y0BAR 0x00
-#define REG_Y1BAR 0x04
-#define REG_Y2BAR 0x08
-/* ... */
-
-#define REG_IMGPITCH 0x24 /* Image pitch register */
-#define IMGP_YP_SHFT 2 /* Y pitch params */
-#define IMGP_YP_MASK 0x00003ffc /* Y pitch field */
-#define IMGP_UVP_SHFT 18 /* UV pitch (planar) */
-#define IMGP_UVP_MASK 0x3ffc0000
-#define REG_IRQSTATRAW 0x28 /* RAW IRQ Status */
-#define IRQ_EOF0 0x00000001 /* End of frame 0 */
-#define IRQ_EOF1 0x00000002 /* End of frame 1 */
-#define IRQ_EOF2 0x00000004 /* End of frame 2 */
-#define IRQ_SOF0 0x00000008 /* Start of frame 0 */
-#define IRQ_SOF1 0x00000010 /* Start of frame 1 */
-#define IRQ_SOF2 0x00000020 /* Start of frame 2 */
-#define IRQ_OVERFLOW 0x00000040 /* FIFO overflow */
-#define IRQ_TWSIW 0x00010000 /* TWSI (smbus) write */
-#define IRQ_TWSIR 0x00020000 /* TWSI read */
-#define IRQ_TWSIE 0x00040000 /* TWSI error */
-#define TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE)
-#define FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2)
-#define ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW)
-#define REG_IRQMASK 0x2c /* IRQ mask - same bits as IRQSTAT */
-#define REG_IRQSTAT 0x30 /* IRQ status / clear */
-
-#define REG_IMGSIZE 0x34 /* Image size */
-#define IMGSZ_V_MASK 0x1fff0000
-#define IMGSZ_V_SHIFT 16
-#define IMGSZ_H_MASK 0x00003fff
-#define REG_IMGOFFSET 0x38 /* IMage offset */
-
-#define REG_CTRL0 0x3c /* Control 0 */
-#define C0_ENABLE 0x00000001 /* Makes the whole thing go */
-
-/* Mask for all the format bits */
-#define C0_DF_MASK 0x00fffffc /* Bits 2-23 */
-
-/* RGB ordering */
-#define C0_RGB4_RGBX 0x00000000
-#define C0_RGB4_XRGB 0x00000004
-#define C0_RGB4_BGRX 0x00000008
-#define C0_RGB4_XBGR 0x0000000c
-#define C0_RGB5_RGGB 0x00000000
-#define C0_RGB5_GRBG 0x00000004
-#define C0_RGB5_GBRG 0x00000008
-#define C0_RGB5_BGGR 0x0000000c
-
-/* Spec has two fields for DIN and DOUT, but they must match, so
- combine them here. */
-#define C0_DF_YUV 0x00000000 /* Data is YUV */
-#define C0_DF_RGB 0x000000a0 /* ... RGB */
-#define C0_DF_BAYER 0x00000140 /* ... Bayer */
-/* 8-8-8 must be missing from the below - ask */
-#define C0_RGBF_565 0x00000000
-#define C0_RGBF_444 0x00000800
-#define C0_RGB_BGR 0x00001000 /* Blue comes first */
-#define C0_YUV_PLANAR 0x00000000 /* YUV 422 planar format */
-#define C0_YUV_PACKED 0x00008000 /* YUV 422 packed */
-#define C0_YUV_420PL 0x0000a000 /* YUV 420 planar */
-/* Think that 420 packed must be 111 - ask */
-#define C0_YUVE_YUYV 0x00000000 /* Y1CbY0Cr */
-#define C0_YUVE_YVYU 0x00010000 /* Y1CrY0Cb */
-#define C0_YUVE_VYUY 0x00020000 /* CrY1CbY0 */
-#define C0_YUVE_UYVY 0x00030000 /* CbY1CrY0 */
-#define C0_YUVE_XYUV 0x00000000 /* 420: .YUV */
-#define C0_YUVE_XYVU 0x00010000 /* 420: .YVU */
-#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */
-#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */
-/* Bayer bits 18,19 if needed */
-#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */
-#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */
-#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */
-#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */
-#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
-#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */
-#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
-
-
-#define REG_CTRL1 0x40 /* Control 1 */
-#define C1_444ALPHA 0x00f00000 /* Alpha field in RGB444 */
-#define C1_ALPHA_SHFT 20
-#define C1_DMAB32 0x00000000 /* 32-byte DMA burst */
-#define C1_DMAB16 0x02000000 /* 16-byte DMA burst */
-#define C1_DMAB64 0x04000000 /* 64-byte DMA burst */
-#define C1_DMAB_MASK 0x06000000
-#define C1_TWOBUFS 0x08000000 /* Use only two DMA buffers */
-#define C1_PWRDWN 0x10000000 /* Power down */
-
-#define REG_CLKCTRL 0x88 /* Clock control */
-#define CLK_DIV_MASK 0x0000ffff /* Upper bits RW "reserved" */
-
-#define REG_GPR 0xb4 /* General purpose register. This
- controls inputs to the power and reset
- pins on the OV7670 used with OLPC;
- other deployments could differ. */
-#define GPR_C1EN 0x00000020 /* Pad 1 (power down) enable */
-#define GPR_C0EN 0x00000010 /* Pad 0 (reset) enable */
-#define GPR_C1 0x00000002 /* Control 1 value */
-/*
- * Control 0 is wired to reset on OLPC machines. For ov7x sensors,
- * it is active low, for 0v6x, instead, it's active high. What
- * fun.
- */
-#define GPR_C0 0x00000001 /* Control 0 value */
-
-#define REG_TWSIC0 0xb8 /* TWSI (smbus) control 0 */
-#define TWSIC0_EN 0x00000001 /* TWSI enable */
-#define TWSIC0_MODE 0x00000002 /* 1 = 16-bit, 0 = 8-bit */
-#define TWSIC0_SID 0x000003fc /* Slave ID */
-#define TWSIC0_SID_SHIFT 2
-#define TWSIC0_CLKDIV 0x0007fc00 /* Clock divider */
-#define TWSIC0_MASKACK 0x00400000 /* Mask ack from sensor */
-#define TWSIC0_OVMAGIC 0x00800000 /* Make it work on OV sensors */
-
-#define REG_TWSIC1 0xbc /* TWSI control 1 */
-#define TWSIC1_DATA 0x0000ffff /* Data to/from camchip */
-#define TWSIC1_ADDR 0x00ff0000 /* Address (register) */
-#define TWSIC1_ADDR_SHIFT 16
-#define TWSIC1_READ 0x01000000 /* Set for read op */
-#define TWSIC1_WSTAT 0x02000000 /* Write status */
-#define TWSIC1_RVALID 0x04000000 /* Read data valid */
-#define TWSIC1_ERROR 0x08000000 /* Something screwed up */
-
-
-#define REG_UBAR 0xc4 /* Upper base address register */
-
-/*
- * Here's the weird global control registers which are said to live
- * way up here.
- */
-#define REG_GL_CSR 0x3004 /* Control/status register */
-#define GCSR_SRS 0x00000001 /* SW Reset set */
-#define GCSR_SRC 0x00000002 /* SW Reset clear */
-#define GCSR_MRS 0x00000004 /* Master reset set */
-#define GCSR_MRC 0x00000008 /* HW Reset clear */
-#define GCSR_CCIC_EN 0x00004000 /* CCIC Clock enable */
-#define REG_GL_IMASK 0x300c /* Interrupt mask register */
-#define GIMSK_CCIC_EN 0x00000004 /* CCIC Interrupt enable */
-
-#define REG_GL_FCR 0x3038 /* GPIO functional control register */
-#define GFCR_GPIO_ON 0x08 /* Camera GPIO enabled */
-#define REG_GL_GPIOR 0x315c /* GPIO register */
-#define GGPIO_OUT 0x80000 /* GPIO output */
-#define GGPIO_VAL 0x00008 /* Output pin value */
-
-#define REG_LEN REG_GL_IMASK + 4
-
-
-/*
- * Useful stuff that probably belongs somewhere global.
- */
-#define VGA_WIDTH 640
-#define VGA_HEIGHT 480
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
deleted file mode 100644
index 664703398493..000000000000
--- a/drivers/media/video/cafe_ccic.c
+++ /dev/null
@@ -1,2267 +0,0 @@
-/*
- * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe"
- * multifunction chip. Currently works with the Omnivision OV7670
- * sensor.
- *
- * The data sheet for this device can be found at:
- * http://www.marvell.com/products/pc_connectivity/88alp01/
- *
- * Copyright 2006 One Laptop Per Child Association, Inc.
- * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
- *
- * Written by Jonathan Corbet, corbet@lwn.net.
- *
- * v4l2_device/v4l2_subdev conversion by:
- * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
- *
- * Note: this conversion is untested! Please contact the linux-media
- * mailinglist if you can test this, together with the test results.
- *
- * This file may be distributed under the terms of the GNU General
- * Public License, version 2.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/dmi.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/list.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/jiffies.h>
-#include <linux/vmalloc.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-
-#include "ov7670.h"
-#include "cafe_ccic-regs.h"
-
-#define CAFE_VERSION 0x000002
-
-
-/*
- * Parameters.
- */
-MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
-MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("Video");
-
-/*
- * Internal DMA buffer management. Since the controller cannot do S/G I/O,
- * we must have physically contiguous buffers to bring frames into.
- * These parameters control how many buffers we use, whether we
- * allocate them at load time (better chance of success, but nails down
- * memory) or when somebody tries to use the camera (riskier), and,
- * for load-time allocation, how big they should be.
- *
- * The controller can cycle through three buffers. We could use
- * more by flipping pointers around, but it probably makes little
- * sense.
- */
-
-#define MAX_DMA_BUFS 3
-static int alloc_bufs_at_read;
-module_param(alloc_bufs_at_read, bool, 0444);
-MODULE_PARM_DESC(alloc_bufs_at_read,
- "Non-zero value causes DMA buffers to be allocated when the "
- "video capture device is read, rather than at module load "
- "time. This saves memory, but decreases the chances of "
- "successfully getting those buffers.");
-
-static int n_dma_bufs = 3;
-module_param(n_dma_bufs, uint, 0644);
-MODULE_PARM_DESC(n_dma_bufs,
- "The number of DMA buffers to allocate. Can be either two "
- "(saves memory, makes timing tighter) or three.");
-
-static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2; /* Worst case */
-module_param(dma_buf_size, uint, 0444);
-MODULE_PARM_DESC(dma_buf_size,
- "The size of the allocated DMA buffers. If actual operating "
- "parameters require larger buffers, an attempt to reallocate "
- "will be made.");
-
-static int min_buffers = 1;
-module_param(min_buffers, uint, 0644);
-MODULE_PARM_DESC(min_buffers,
- "The minimum number of streaming I/O buffers we are willing "
- "to work with.");
-
-static int max_buffers = 10;
-module_param(max_buffers, uint, 0644);
-MODULE_PARM_DESC(max_buffers,
- "The maximum number of streaming I/O buffers an application "
- "will be allowed to allocate. These buffers are big and live "
- "in vmalloc space.");
-
-static int flip;
-module_param(flip, bool, 0444);
-MODULE_PARM_DESC(flip,
- "If set, the sensor will be instructed to flip the image "
- "vertically.");
-
-
-enum cafe_state {
- S_NOTREADY, /* Not yet initialized */
- S_IDLE, /* Just hanging around */
- S_FLAKED, /* Some sort of problem */
- S_SINGLEREAD, /* In read() */
- S_SPECREAD, /* Speculative read (for future read()) */
- S_STREAMING /* Streaming data */
-};
-
-/*
- * Tracking of streaming I/O buffers.
- */
-struct cafe_sio_buffer {
- struct list_head list;
- struct v4l2_buffer v4lbuf;
- char *buffer; /* Where it lives in kernel space */
- int mapcount;
- struct cafe_camera *cam;
-};
-
-/*
- * A description of one of our devices.
- * Locking: controlled by s_mutex. Certain fields, however, require
- * the dev_lock spinlock; they are marked as such by comments.
- * dev_lock is also required for access to device registers.
- */
-struct cafe_camera
-{
- struct v4l2_device v4l2_dev;
- enum cafe_state state;
- unsigned long flags; /* Buffer status, mainly (dev_lock) */
- int users; /* How many open FDs */
- struct file *owner; /* Who has data access (v4l2) */
-
- /*
- * Subsystem structures.
- */
- struct pci_dev *pdev;
- struct video_device vdev;
- struct i2c_adapter i2c_adapter;
- struct v4l2_subdev *sensor;
- unsigned short sensor_addr;
-
- unsigned char __iomem *regs;
- struct list_head dev_list; /* link to other devices */
-
- /* DMA buffers */
- unsigned int nbufs; /* How many are alloc'd */
- int next_buf; /* Next to consume (dev_lock) */
- unsigned int dma_buf_size; /* allocated size */
- void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */
- dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */
- unsigned int specframes; /* Unconsumed spec frames (dev_lock) */
- unsigned int sequence; /* Frame sequence number */
- unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual buffers */
-
- /* Streaming buffers */
- unsigned int n_sbufs; /* How many we have */
- struct cafe_sio_buffer *sb_bufs; /* The array of housekeeping structs */
- struct list_head sb_avail; /* Available for data (we own) (dev_lock) */
- struct list_head sb_full; /* With data (user space owns) (dev_lock) */
- struct tasklet_struct s_tasklet;
-
- /* Current operating parameters */
- u32 sensor_type; /* Currently ov7670 only */
- struct v4l2_pix_format pix_format;
- enum v4l2_mbus_pixelcode mbus_code;
-
- /* Locks */
- struct mutex s_mutex; /* Access to this structure */
- spinlock_t dev_lock; /* Access to device */
-
- /* Misc */
- wait_queue_head_t smbus_wait; /* Waiting on i2c events */
- wait_queue_head_t iowait; /* Waiting on frame data */
-};
-
-/*
- * Status flags. Always manipulated with bit operations.
- */
-#define CF_BUF0_VALID 0 /* Buffers valid - first three */
-#define CF_BUF1_VALID 1
-#define CF_BUF2_VALID 2
-#define CF_DMA_ACTIVE 3 /* A frame is incoming */
-#define CF_CONFIG_NEEDED 4 /* Must configure hardware */
-
-#define sensor_call(cam, o, f, args...) \
- v4l2_subdev_call(cam->sensor, o, f, ##args)
-
-static inline struct cafe_camera *to_cam(struct v4l2_device *dev)
-{
- return container_of(dev, struct cafe_camera, v4l2_dev);
-}
-
-static struct cafe_format_struct {
- __u8 *desc;
- __u32 pixelformat;
- int bpp; /* Bytes per pixel */
- enum v4l2_mbus_pixelcode mbus_code;
-} cafe_formats[] = {
- {
- .desc = "YUYV 4:2:2",
- .pixelformat = V4L2_PIX_FMT_YUYV,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
- .bpp = 2,
- },
- {
- .desc = "RGB 444",
- .pixelformat = V4L2_PIX_FMT_RGB444,
- .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
- .bpp = 2,
- },
- {
- .desc = "RGB 565",
- .pixelformat = V4L2_PIX_FMT_RGB565,
- .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
- .bpp = 2,
- },
- {
- .desc = "Raw RGB Bayer",
- .pixelformat = V4L2_PIX_FMT_SBGGR8,
- .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,
- .bpp = 1
- },
-};
-#define N_CAFE_FMTS ARRAY_SIZE(cafe_formats)
-
-static struct cafe_format_struct *cafe_find_format(u32 pixelformat)
-{
- unsigned i;
-
- for (i = 0; i < N_CAFE_FMTS; i++)
- if (cafe_formats[i].pixelformat == pixelformat)
- return cafe_formats + i;
- /* Not found? Then return the first format. */
- return cafe_formats;
-}
-
-/*
- * Start over with DMA buffers - dev_lock needed.
- */
-static void cafe_reset_buffers(struct cafe_camera *cam)
-{
- int i;
-
- cam->next_buf = -1;
- for (i = 0; i < cam->nbufs; i++)
- clear_bit(i, &cam->flags);
- cam->specframes = 0;
-}
-
-static inline int cafe_needs_config(struct cafe_camera *cam)
-{
- return test_bit(CF_CONFIG_NEEDED, &cam->flags);
-}
-
-static void cafe_set_config_needed(struct cafe_camera *cam, int needed)
-{
- if (needed)
- set_bit(CF_CONFIG_NEEDED, &cam->flags);
- else
- clear_bit(CF_CONFIG_NEEDED, &cam->flags);
-}
-
-
-
-
-/*
- * Debugging and related.
- */
-#define cam_err(cam, fmt, arg...) \
- dev_err(&(cam)->pdev->dev, fmt, ##arg);
-#define cam_warn(cam, fmt, arg...) \
- dev_warn(&(cam)->pdev->dev, fmt, ##arg);
-#define cam_dbg(cam, fmt, arg...) \
- dev_dbg(&(cam)->pdev->dev, fmt, ##arg);
-
-
-/* ---------------------------------------------------------------------*/
-
-/*
- * Device register I/O
- */
-static inline void cafe_reg_write(struct cafe_camera *cam, unsigned int reg,
- unsigned int val)
-{
- iowrite32(val, cam->regs + reg);
-}
-
-static inline unsigned int cafe_reg_read(struct cafe_camera *cam,
- unsigned int reg)
-{
- return ioread32(cam->regs + reg);
-}
-
-
-static inline void cafe_reg_write_mask(struct cafe_camera *cam, unsigned int reg,
- unsigned int val, unsigned int mask)
-{
- unsigned int v = cafe_reg_read(cam, reg);
-
- v = (v & ~mask) | (val & mask);
- cafe_reg_write(cam, reg, v);
-}
-
-static inline void cafe_reg_clear_bit(struct cafe_camera *cam,
- unsigned int reg, unsigned int val)
-{
- cafe_reg_write_mask(cam, reg, 0, val);
-}
-
-static inline void cafe_reg_set_bit(struct cafe_camera *cam,
- unsigned int reg, unsigned int val)
-{
- cafe_reg_write_mask(cam, reg, val, val);
-}
-
-
-
-/* -------------------------------------------------------------------- */
-/*
- * The I2C/SMBUS interface to the camera itself starts here. The
- * controller handles SMBUS itself, presenting a relatively simple register
- * interface; all we have to do is to tell it where to route the data.
- */
-#define CAFE_SMBUS_TIMEOUT (HZ) /* generous */
-
-static int cafe_smbus_write_done(struct cafe_camera *cam)
-{
- unsigned long flags;
- int c1;
-
- /*
- * We must delay after the interrupt, or the controller gets confused
- * and never does give us good status. Fortunately, we don't do this
- * often.
- */
- udelay(20);
- spin_lock_irqsave(&cam->dev_lock, flags);
- c1 = cafe_reg_read(cam, REG_TWSIC1);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT;
-}
-
-static int cafe_smbus_write_data(struct cafe_camera *cam,
- u16 addr, u8 command, u8 value)
-{
- unsigned int rval;
- unsigned long flags;
-
- spin_lock_irqsave(&cam->dev_lock, flags);
- rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
- rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
- /*
- * Marvell sez set clkdiv to all 1's for now.
- */
- rval |= TWSIC0_CLKDIV;
- cafe_reg_write(cam, REG_TWSIC0, rval);
- (void) cafe_reg_read(cam, REG_TWSIC1); /* force write */
- rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
- cafe_reg_write(cam, REG_TWSIC1, rval);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
-
- /* Unfortunately, reading TWSIC1 too soon after sending a command
- * causes the device to die.
- * Use a busy-wait because we often send a large quantity of small
- * commands at-once; using msleep() would cause a lot of context
- * switches which take longer than 2ms, resulting in a noticeable
- * boot-time and capture-start delays.
- */
- mdelay(2);
-
- /*
- * Another sad fact is that sometimes, commands silently complete but
- * cafe_smbus_write_done() never becomes aware of this.
- * This happens at random and appears to possible occur with any
- * command.
- * We don't understand why this is. We work around this issue
- * with the timeout in the wait below, assuming that all commands
- * complete within the timeout.
- */
- wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam),
- CAFE_SMBUS_TIMEOUT);
-
- spin_lock_irqsave(&cam->dev_lock, flags);
- rval = cafe_reg_read(cam, REG_TWSIC1);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
-
- if (rval & TWSIC1_WSTAT) {
- cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr,
- command, value);
- return -EIO;
- }
- if (rval & TWSIC1_ERROR) {
- cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr,
- command, value);
- return -EIO;
- }
- return 0;
-}
-
-
-
-static int cafe_smbus_read_done(struct cafe_camera *cam)
-{
- unsigned long flags;
- int c1;
-
- /*
- * We must delay after the interrupt, or the controller gets confused
- * and never does give us good status. Fortunately, we don't do this
- * often.
- */
- udelay(20);
- spin_lock_irqsave(&cam->dev_lock, flags);
- c1 = cafe_reg_read(cam, REG_TWSIC1);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- return c1 & (TWSIC1_RVALID|TWSIC1_ERROR);
-}
-
-
-
-static int cafe_smbus_read_data(struct cafe_camera *cam,
- u16 addr, u8 command, u8 *value)
-{
- unsigned int rval;
- unsigned long flags;
-
- spin_lock_irqsave(&cam->dev_lock, flags);
- rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
- rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
- /*
- * Marvel sez set clkdiv to all 1's for now.
- */
- rval |= TWSIC0_CLKDIV;
- cafe_reg_write(cam, REG_TWSIC0, rval);
- (void) cafe_reg_read(cam, REG_TWSIC1); /* force write */
- rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
- cafe_reg_write(cam, REG_TWSIC1, rval);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
-
- wait_event_timeout(cam->smbus_wait,
- cafe_smbus_read_done(cam), CAFE_SMBUS_TIMEOUT);
- spin_lock_irqsave(&cam->dev_lock, flags);
- rval = cafe_reg_read(cam, REG_TWSIC1);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
-
- if (rval & TWSIC1_ERROR) {
- cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command);
- return -EIO;
- }
- if (! (rval & TWSIC1_RVALID)) {
- cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr,
- command);
- return -EIO;
- }
- *value = rval & 0xff;
- return 0;
-}
-
-/*
- * Perform a transfer over SMBUS. This thing is called under
- * the i2c bus lock, so we shouldn't race with ourselves...
- */
-static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
- unsigned short flags, char rw, u8 command,
- int size, union i2c_smbus_data *data)
-{
- struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter);
- struct cafe_camera *cam = to_cam(v4l2_dev);
- int ret = -EINVAL;
-
- /*
- * This interface would appear to only do byte data ops. OK
- * it can do word too, but the cam chip has no use for that.
- */
- if (size != I2C_SMBUS_BYTE_DATA) {
- cam_err(cam, "funky xfer size %d\n", size);
- return -EINVAL;
- }
-
- if (rw == I2C_SMBUS_WRITE)
- ret = cafe_smbus_write_data(cam, addr, command, data->byte);
- else if (rw == I2C_SMBUS_READ)
- ret = cafe_smbus_read_data(cam, addr, command, &data->byte);
- return ret;
-}
-
-
-static void cafe_smbus_enable_irq(struct cafe_camera *cam)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cam->dev_lock, flags);
- cafe_reg_set_bit(cam, REG_IRQMASK, TWSIIRQS);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
-}
-
-static u32 cafe_smbus_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_SMBUS_READ_BYTE_DATA |
- I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
-}
-
-static struct i2c_algorithm cafe_smbus_algo = {
- .smbus_xfer = cafe_smbus_xfer,
- .functionality = cafe_smbus_func
-};
-
-/* Somebody is on the bus */
-static void cafe_ctlr_stop_dma(struct cafe_camera *cam);
-static void cafe_ctlr_power_down(struct cafe_camera *cam);
-
-static int cafe_smbus_setup(struct cafe_camera *cam)
-{
- struct i2c_adapter *adap = &cam->i2c_adapter;
- int ret;
-
- cafe_smbus_enable_irq(cam);
- adap->owner = THIS_MODULE;
- adap->algo = &cafe_smbus_algo;
- strcpy(adap->name, "cafe_ccic");
- adap->dev.parent = &cam->pdev->dev;
- i2c_set_adapdata(adap, &cam->v4l2_dev);
- ret = i2c_add_adapter(adap);
- if (ret)
- printk(KERN_ERR "Unable to register cafe i2c adapter\n");
- return ret;
-}
-
-static void cafe_smbus_shutdown(struct cafe_camera *cam)
-{
- i2c_del_adapter(&cam->i2c_adapter);
-}
-
-
-/* ------------------------------------------------------------------- */
-/*
- * Deal with the controller.
- */
-
-/*
- * Do everything we think we need to have the interface operating
- * according to the desired format.
- */
-static void cafe_ctlr_dma(struct cafe_camera *cam)
-{
- /*
- * Store the first two Y buffers (we aren't supporting
- * planar formats for now, so no UV bufs). Then either
- * set the third if it exists, or tell the controller
- * to just use two.
- */
- cafe_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]);
- cafe_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]);
- if (cam->nbufs > 2) {
- cafe_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]);
- cafe_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
- }
- else
- cafe_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
- cafe_reg_write(cam, REG_UBAR, 0); /* 32 bits only for now */
-}
-
-static void cafe_ctlr_image(struct cafe_camera *cam)
-{
- int imgsz;
- struct v4l2_pix_format *fmt = &cam->pix_format;
-
- imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
- (fmt->bytesperline & IMGSZ_H_MASK);
- cafe_reg_write(cam, REG_IMGSIZE, imgsz);
- cafe_reg_write(cam, REG_IMGOFFSET, 0);
- /* YPITCH just drops the last two bits */
- cafe_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline,
- IMGP_YP_MASK);
- /*
- * Tell the controller about the image format we are using.
- */
- switch (cam->pix_format.pixelformat) {
- case V4L2_PIX_FMT_YUYV:
- cafe_reg_write_mask(cam, REG_CTRL0,
- C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV,
- C0_DF_MASK);
- break;
-
- case V4L2_PIX_FMT_RGB444:
- cafe_reg_write_mask(cam, REG_CTRL0,
- C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
- C0_DF_MASK);
- /* Alpha value? */
- break;
-
- case V4L2_PIX_FMT_RGB565:
- cafe_reg_write_mask(cam, REG_CTRL0,
- C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
- C0_DF_MASK);
- break;
-
- default:
- cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
- break;
- }
- /*
- * Make sure it knows we want to use hsync/vsync.
- */
- cafe_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
- C0_SIFM_MASK);
-}
-
-
-/*
- * Configure the controller for operation; caller holds the
- * device mutex.
- */
-static int cafe_ctlr_configure(struct cafe_camera *cam)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cam->dev_lock, flags);
- cafe_ctlr_dma(cam);
- cafe_ctlr_image(cam);
- cafe_set_config_needed(cam, 0);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- return 0;
-}
-
-static void cafe_ctlr_irq_enable(struct cafe_camera *cam)
-{
- /*
- * Clear any pending interrupts, since we do not
- * expect to have I/O active prior to enabling.
- */
- cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS);
- cafe_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS);
-}
-
-static void cafe_ctlr_irq_disable(struct cafe_camera *cam)
-{
- cafe_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS);
-}
-
-/*
- * Make the controller start grabbing images. Everything must
- * be set up before doing this.
- */
-static void cafe_ctlr_start(struct cafe_camera *cam)
-{
- /* set_bit performs a read, so no other barrier should be
- needed here */
- cafe_reg_set_bit(cam, REG_CTRL0, C0_ENABLE);
-}
-
-static void cafe_ctlr_stop(struct cafe_camera *cam)
-{
- cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
-}
-
-static void cafe_ctlr_init(struct cafe_camera *cam)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cam->dev_lock, flags);
- /*
- * Added magic to bring up the hardware on the B-Test board
- */
- cafe_reg_write(cam, 0x3038, 0x8);
- cafe_reg_write(cam, 0x315c, 0x80008);
- /*
- * Go through the dance needed to wake the device up.
- * Note that these registers are global and shared
- * with the NAND and SD devices. Interaction between the
- * three still needs to be examined.
- */
- cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */
- cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC);
- cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS);
- /*
- * Here we must wait a bit for the controller to come around.
- */
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- msleep(5);
- spin_lock_irqsave(&cam->dev_lock, flags);
-
- cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
- cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN);
- /*
- * Make sure it's not powered down.
- */
- cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
- /*
- * Turn off the enable bit. It sure should be off anyway,
- * but it's good to be sure.
- */
- cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
- /*
- * Mask all interrupts.
- */
- cafe_reg_write(cam, REG_IRQMASK, 0);
- /*
- * Clock the sensor appropriately. Controller clock should
- * be 48MHz, sensor "typical" value is half that.
- */
- cafe_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
-}
-
-
-/*
- * Stop the controller, and don't return until we're really sure that no
- * further DMA is going on.
- */
-static void cafe_ctlr_stop_dma(struct cafe_camera *cam)
-{
- unsigned long flags;
-
- /*
- * Theory: stop the camera controller (whether it is operating
- * or not). Delay briefly just in case we race with the SOF
- * interrupt, then wait until no DMA is active.
- */
- spin_lock_irqsave(&cam->dev_lock, flags);
- cafe_ctlr_stop(cam);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- mdelay(1);
- wait_event_timeout(cam->iowait,
- !test_bit(CF_DMA_ACTIVE, &cam->flags), HZ);
- if (test_bit(CF_DMA_ACTIVE, &cam->flags))
- cam_err(cam, "Timeout waiting for DMA to end\n");
- /* This would be bad news - what now? */
- spin_lock_irqsave(&cam->dev_lock, flags);
- cam->state = S_IDLE;
- cafe_ctlr_irq_disable(cam);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
-}
-
-/*
- * Power up and down.
- */
-static void cafe_ctlr_power_up(struct cafe_camera *cam)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cam->dev_lock, flags);
- cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
- /*
- * Part one of the sensor dance: turn the global
- * GPIO signal on.
- */
- cafe_reg_write(cam, REG_GL_FCR, GFCR_GPIO_ON);
- cafe_reg_write(cam, REG_GL_GPIOR, GGPIO_OUT|GGPIO_VAL);
- /*
- * Put the sensor into operational mode (assumes OLPC-style
- * wiring). Control 0 is reset - set to 1 to operate.
- * Control 1 is power down, set to 0 to operate.
- */
- cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
-/* mdelay(1); */ /* Marvell says 1ms will do it */
- cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
-/* mdelay(1); */ /* Enough? */
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- msleep(5); /* Just to be sure */
-}
-
-static void cafe_ctlr_power_down(struct cafe_camera *cam)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cam->dev_lock, flags);
- cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1);
- cafe_reg_write(cam, REG_GL_FCR, GFCR_GPIO_ON);
- cafe_reg_write(cam, REG_GL_GPIOR, GGPIO_OUT);
- cafe_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
-}
-
-/* -------------------------------------------------------------------- */
-/*
- * Communications with the sensor.
- */
-
-static int __cafe_cam_reset(struct cafe_camera *cam)
-{
- return sensor_call(cam, core, reset, 0);
-}
-
-/*
- * We have found the sensor on the i2c. Let's try to have a
- * conversation.
- */
-static int cafe_cam_init(struct cafe_camera *cam)
-{
- struct v4l2_dbg_chip_ident chip;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- if (cam->state != S_NOTREADY)
- cam_warn(cam, "Cam init with device in funky state %d",
- cam->state);
- ret = __cafe_cam_reset(cam);
- if (ret)
- goto out;
- chip.ident = V4L2_IDENT_NONE;
- chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR;
- chip.match.addr = cam->sensor_addr;
- ret = sensor_call(cam, core, g_chip_ident, &chip);
- if (ret)
- goto out;
- cam->sensor_type = chip.ident;
- if (cam->sensor_type != V4L2_IDENT_OV7670) {
- cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type);
- ret = -EINVAL;
- goto out;
- }
-/* Get/set parameters? */
- ret = 0;
- cam->state = S_IDLE;
- out:
- cafe_ctlr_power_down(cam);
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-/*
- * Configure the sensor to match the parameters we have. Caller should
- * hold s_mutex
- */
-static int cafe_cam_set_flip(struct cafe_camera *cam)
-{
- struct v4l2_control ctrl;
-
- memset(&ctrl, 0, sizeof(ctrl));
- ctrl.id = V4L2_CID_VFLIP;
- ctrl.value = flip;
- return sensor_call(cam, core, s_ctrl, &ctrl);
-}
-
-
-static int cafe_cam_configure(struct cafe_camera *cam)
-{
- struct v4l2_mbus_framefmt mbus_fmt;
- int ret;
-
- v4l2_fill_mbus_format(&mbus_fmt, &cam->pix_format, cam->mbus_code);
- ret = sensor_call(cam, core, init, 0);
- if (ret == 0)
- ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt);
- /*
- * OV7670 does weird things if flip is set *before* format...
- */
- ret += cafe_cam_set_flip(cam);
- return ret;
-}
-
-/* -------------------------------------------------------------------- */
-/*
- * DMA buffer management. These functions need s_mutex held.
- */
-
-/* FIXME: this is inefficient as hell, since dma_alloc_coherent just
- * does a get_free_pages() call, and we waste a good chunk of an orderN
- * allocation. Should try to allocate the whole set in one chunk.
- */
-static int cafe_alloc_dma_bufs(struct cafe_camera *cam, int loadtime)
-{
- int i;
-
- cafe_set_config_needed(cam, 1);
- if (loadtime)
- cam->dma_buf_size = dma_buf_size;
- else
- cam->dma_buf_size = cam->pix_format.sizeimage;
- if (n_dma_bufs > 3)
- n_dma_bufs = 3;
-
- cam->nbufs = 0;
- for (i = 0; i < n_dma_bufs; i++) {
- cam->dma_bufs[i] = dma_alloc_coherent(&cam->pdev->dev,
- cam->dma_buf_size, cam->dma_handles + i,
- GFP_KERNEL);
- if (cam->dma_bufs[i] == NULL) {
- cam_warn(cam, "Failed to allocate DMA buffer\n");
- break;
- }
- /* For debug, remove eventually */
- memset(cam->dma_bufs[i], 0xcc, cam->dma_buf_size);
- (cam->nbufs)++;
- }
-
- switch (cam->nbufs) {
- case 1:
- dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size,
- cam->dma_bufs[0], cam->dma_handles[0]);
- cam->nbufs = 0;
- case 0:
- cam_err(cam, "Insufficient DMA buffers, cannot operate\n");
- return -ENOMEM;
-
- case 2:
- if (n_dma_bufs > 2)
- cam_warn(cam, "Will limp along with only 2 buffers\n");
- break;
- }
- return 0;
-}
-
-static void cafe_free_dma_bufs(struct cafe_camera *cam)
-{
- int i;
-
- for (i = 0; i < cam->nbufs; i++) {
- dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size,
- cam->dma_bufs[i], cam->dma_handles[i]);
- cam->dma_bufs[i] = NULL;
- }
- cam->nbufs = 0;
-}
-
-
-
-
-
-/* ----------------------------------------------------------------------- */
-/*
- * Here starts the V4L2 interface code.
- */
-
-/*
- * Read an image from the device.
- */
-static ssize_t cafe_deliver_buffer(struct cafe_camera *cam,
- char __user *buffer, size_t len, loff_t *pos)
-{
- int bufno;
- unsigned long flags;
-
- spin_lock_irqsave(&cam->dev_lock, flags);
- if (cam->next_buf < 0) {
- cam_err(cam, "deliver_buffer: No next buffer\n");
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- return -EIO;
- }
- bufno = cam->next_buf;
- clear_bit(bufno, &cam->flags);
- if (++(cam->next_buf) >= cam->nbufs)
- cam->next_buf = 0;
- if (! test_bit(cam->next_buf, &cam->flags))
- cam->next_buf = -1;
- cam->specframes = 0;
- spin_unlock_irqrestore(&cam->dev_lock, flags);
-
- if (len > cam->pix_format.sizeimage)
- len = cam->pix_format.sizeimage;
- if (copy_to_user(buffer, cam->dma_bufs[bufno], len))
- return -EFAULT;
- (*pos) += len;
- return len;
-}
-
-/*
- * Get everything ready, and start grabbing frames.
- */
-static int cafe_read_setup(struct cafe_camera *cam, enum cafe_state state)
-{
- int ret;
- unsigned long flags;
-
- /*
- * Configuration. If we still don't have DMA buffers,
- * make one last, desperate attempt.
- */
- if (cam->nbufs == 0)
- if (cafe_alloc_dma_bufs(cam, 0))
- return -ENOMEM;
-
- if (cafe_needs_config(cam)) {
- cafe_cam_configure(cam);
- ret = cafe_ctlr_configure(cam);
- if (ret)
- return ret;
- }
-
- /*
- * Turn it loose.
- */
- spin_lock_irqsave(&cam->dev_lock, flags);
- cafe_reset_buffers(cam);
- cafe_ctlr_irq_enable(cam);
- cam->state = state;
- cafe_ctlr_start(cam);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- return 0;
-}
-
-
-static ssize_t cafe_v4l_read(struct file *filp,
- char __user *buffer, size_t len, loff_t *pos)
-{
- struct cafe_camera *cam = filp->private_data;
- int ret = 0;
-
- /*
- * Perhaps we're in speculative read mode and already
- * have data?
- */
- mutex_lock(&cam->s_mutex);
- if (cam->state == S_SPECREAD) {
- if (cam->next_buf >= 0) {
- ret = cafe_deliver_buffer(cam, buffer, len, pos);
- if (ret != 0)
- goto out_unlock;
- }
- } else if (cam->state == S_FLAKED || cam->state == S_NOTREADY) {
- ret = -EIO;
- goto out_unlock;
- } else if (cam->state != S_IDLE) {
- ret = -EBUSY;
- goto out_unlock;
- }
-
- /*
- * v4l2: multiple processes can open the device, but only
- * one gets to grab data from it.
- */
- if (cam->owner && cam->owner != filp) {
- ret = -EBUSY;
- goto out_unlock;
- }
- cam->owner = filp;
-
- /*
- * Do setup if need be.
- */
- if (cam->state != S_SPECREAD) {
- ret = cafe_read_setup(cam, S_SINGLEREAD);
- if (ret)
- goto out_unlock;
- }
- /*
- * Wait for something to happen. This should probably
- * be interruptible (FIXME).
- */
- wait_event_timeout(cam->iowait, cam->next_buf >= 0, HZ);
- if (cam->next_buf < 0) {
- cam_err(cam, "read() operation timed out\n");
- cafe_ctlr_stop_dma(cam);
- ret = -EIO;
- goto out_unlock;
- }
- /*
- * Give them their data and we should be done.
- */
- ret = cafe_deliver_buffer(cam, buffer, len, pos);
-
- out_unlock:
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-
-
-
-
-
-
-
-/*
- * Streaming I/O support.
- */
-
-
-
-static int cafe_vidioc_streamon(struct file *filp, void *priv,
- enum v4l2_buf_type type)
-{
- struct cafe_camera *cam = filp->private_data;
- int ret = -EINVAL;
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- goto out;
- mutex_lock(&cam->s_mutex);
- if (cam->state != S_IDLE || cam->n_sbufs == 0)
- goto out_unlock;
-
- cam->sequence = 0;
- ret = cafe_read_setup(cam, S_STREAMING);
-
- out_unlock:
- mutex_unlock(&cam->s_mutex);
- out:
- return ret;
-}
-
-
-static int cafe_vidioc_streamoff(struct file *filp, void *priv,
- enum v4l2_buf_type type)
-{
- struct cafe_camera *cam = filp->private_data;
- int ret = -EINVAL;
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- goto out;
- mutex_lock(&cam->s_mutex);
- if (cam->state != S_STREAMING)
- goto out_unlock;
-
- cafe_ctlr_stop_dma(cam);
- ret = 0;
-
- out_unlock:
- mutex_unlock(&cam->s_mutex);
- out:
- return ret;
-}
-
-
-
-static int cafe_setup_siobuf(struct cafe_camera *cam, int index)
-{
- struct cafe_sio_buffer *buf = cam->sb_bufs + index;
-
- INIT_LIST_HEAD(&buf->list);
- buf->v4lbuf.length = PAGE_ALIGN(cam->pix_format.sizeimage);
- buf->buffer = vmalloc_user(buf->v4lbuf.length);
- if (buf->buffer == NULL)
- return -ENOMEM;
- buf->mapcount = 0;
- buf->cam = cam;
-
- buf->v4lbuf.index = index;
- buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf->v4lbuf.field = V4L2_FIELD_NONE;
- buf->v4lbuf.memory = V4L2_MEMORY_MMAP;
- /*
- * Offset: must be 32-bit even on a 64-bit system. videobuf-dma-sg
- * just uses the length times the index, but the spec warns
- * against doing just that - vma merging problems. So we
- * leave a gap between each pair of buffers.
- */
- buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length;
- return 0;
-}
-
-static int cafe_free_sio_buffers(struct cafe_camera *cam)
-{
- int i;
-
- /*
- * If any buffers are mapped, we cannot free them at all.
- */
- for (i = 0; i < cam->n_sbufs; i++)
- if (cam->sb_bufs[i].mapcount > 0)
- return -EBUSY;
- /*
- * OK, let's do it.
- */
- for (i = 0; i < cam->n_sbufs; i++)
- vfree(cam->sb_bufs[i].buffer);
- cam->n_sbufs = 0;
- kfree(cam->sb_bufs);
- cam->sb_bufs = NULL;
- INIT_LIST_HEAD(&cam->sb_avail);
- INIT_LIST_HEAD(&cam->sb_full);
- return 0;
-}
-
-
-
-static int cafe_vidioc_reqbufs(struct file *filp, void *priv,
- struct v4l2_requestbuffers *req)
-{
- struct cafe_camera *cam = filp->private_data;
- int ret = 0; /* Silence warning */
-
- /*
- * Make sure it's something we can do. User pointers could be
- * implemented without great pain, but that's not been done yet.
- */
- if (req->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
- /*
- * If they ask for zero buffers, they really want us to stop streaming
- * (if it's happening) and free everything. Should we check owner?
- */
- mutex_lock(&cam->s_mutex);
- if (req->count == 0) {
- if (cam->state == S_STREAMING)
- cafe_ctlr_stop_dma(cam);
- ret = cafe_free_sio_buffers (cam);
- goto out;
- }
- /*
- * Device needs to be idle and working. We *could* try to do the
- * right thing in S_SPECREAD by shutting things down, but it
- * probably doesn't matter.
- */
- if (cam->state != S_IDLE || (cam->owner && cam->owner != filp)) {
- ret = -EBUSY;
- goto out;
- }
- cam->owner = filp;
-
- if (req->count < min_buffers)
- req->count = min_buffers;
- else if (req->count > max_buffers)
- req->count = max_buffers;
- if (cam->n_sbufs > 0) {
- ret = cafe_free_sio_buffers(cam);
- if (ret)
- goto out;
- }
-
- cam->sb_bufs = kzalloc(req->count*sizeof(struct cafe_sio_buffer),
- GFP_KERNEL);
- if (cam->sb_bufs == NULL) {
- ret = -ENOMEM;
- goto out;
- }
- for (cam->n_sbufs = 0; cam->n_sbufs < req->count; (cam->n_sbufs++)) {
- ret = cafe_setup_siobuf(cam, cam->n_sbufs);
- if (ret)
- break;
- }
-
- if (cam->n_sbufs == 0) /* no luck at all - ret already set */
- kfree(cam->sb_bufs);
- req->count = cam->n_sbufs; /* In case of partial success */
-
- out:
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-
-static int cafe_vidioc_querybuf(struct file *filp, void *priv,
- struct v4l2_buffer *buf)
-{
- struct cafe_camera *cam = filp->private_data;
- int ret = -EINVAL;
-
- mutex_lock(&cam->s_mutex);
- if (buf->index >= cam->n_sbufs)
- goto out;
- *buf = cam->sb_bufs[buf->index].v4lbuf;
- ret = 0;
- out:
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-static int cafe_vidioc_qbuf(struct file *filp, void *priv,
- struct v4l2_buffer *buf)
-{
- struct cafe_camera *cam = filp->private_data;
- struct cafe_sio_buffer *sbuf;
- int ret = -EINVAL;
- unsigned long flags;
-
- mutex_lock(&cam->s_mutex);
- if (buf->index >= cam->n_sbufs)
- goto out;
- sbuf = cam->sb_bufs + buf->index;
- if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) {
- ret = 0; /* Already queued?? */
- goto out;
- }
- if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_DONE) {
- /* Spec doesn't say anything, seems appropriate tho */
- ret = -EBUSY;
- goto out;
- }
- sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED;
- spin_lock_irqsave(&cam->dev_lock, flags);
- list_add(&sbuf->list, &cam->sb_avail);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- ret = 0;
- out:
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-static int cafe_vidioc_dqbuf(struct file *filp, void *priv,
- struct v4l2_buffer *buf)
-{
- struct cafe_camera *cam = filp->private_data;
- struct cafe_sio_buffer *sbuf;
- int ret = -EINVAL;
- unsigned long flags;
-
- mutex_lock(&cam->s_mutex);
- if (cam->state != S_STREAMING)
- goto out_unlock;
- if (list_empty(&cam->sb_full) && filp->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- goto out_unlock;
- }
-
- while (list_empty(&cam->sb_full) && cam->state == S_STREAMING) {
- mutex_unlock(&cam->s_mutex);
- if (wait_event_interruptible(cam->iowait,
- !list_empty(&cam->sb_full))) {
- ret = -ERESTARTSYS;
- goto out;
- }
- mutex_lock(&cam->s_mutex);
- }
-
- if (cam->state != S_STREAMING)
- ret = -EINTR;
- else {
- spin_lock_irqsave(&cam->dev_lock, flags);
- /* Should probably recheck !list_empty() here */
- sbuf = list_entry(cam->sb_full.next,
- struct cafe_sio_buffer, list);
- list_del_init(&sbuf->list);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE;
- *buf = sbuf->v4lbuf;
- ret = 0;
- }
-
- out_unlock:
- mutex_unlock(&cam->s_mutex);
- out:
- return ret;
-}
-
-
-
-static void cafe_v4l_vm_open(struct vm_area_struct *vma)
-{
- struct cafe_sio_buffer *sbuf = vma->vm_private_data;
- /*
- * Locking: done under mmap_sem, so we don't need to
- * go back to the camera lock here.
- */
- sbuf->mapcount++;
-}
-
-
-static void cafe_v4l_vm_close(struct vm_area_struct *vma)
-{
- struct cafe_sio_buffer *sbuf = vma->vm_private_data;
-
- mutex_lock(&sbuf->cam->s_mutex);
- sbuf->mapcount--;
- /* Docs say we should stop I/O too... */
- if (sbuf->mapcount == 0)
- sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED;
- mutex_unlock(&sbuf->cam->s_mutex);
-}
-
-static const struct vm_operations_struct cafe_v4l_vm_ops = {
- .open = cafe_v4l_vm_open,
- .close = cafe_v4l_vm_close
-};
-
-
-static int cafe_v4l_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- struct cafe_camera *cam = filp->private_data;
- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
- int ret = -EINVAL;
- int i;
- struct cafe_sio_buffer *sbuf = NULL;
-
- if (! (vma->vm_flags & VM_WRITE) || ! (vma->vm_flags & VM_SHARED))
- return -EINVAL;
- /*
- * Find the buffer they are looking for.
- */
- mutex_lock(&cam->s_mutex);
- for (i = 0; i < cam->n_sbufs; i++)
- if (cam->sb_bufs[i].v4lbuf.m.offset == offset) {
- sbuf = cam->sb_bufs + i;
- break;
- }
- if (sbuf == NULL)
- goto out;
-
- ret = remap_vmalloc_range(vma, sbuf->buffer, 0);
- if (ret)
- goto out;
- vma->vm_flags |= VM_DONTEXPAND;
- vma->vm_private_data = sbuf;
- vma->vm_ops = &cafe_v4l_vm_ops;
- sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED;
- cafe_v4l_vm_open(vma);
- ret = 0;
- out:
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-
-
-static int cafe_v4l_open(struct file *filp)
-{
- struct cafe_camera *cam = video_drvdata(filp);
-
- filp->private_data = cam;
-
- mutex_lock(&cam->s_mutex);
- if (cam->users == 0) {
- cafe_ctlr_power_up(cam);
- __cafe_cam_reset(cam);
- cafe_set_config_needed(cam, 1);
- /* FIXME make sure this is complete */
- }
- (cam->users)++;
- mutex_unlock(&cam->s_mutex);
- return 0;
-}
-
-
-static int cafe_v4l_release(struct file *filp)
-{
- struct cafe_camera *cam = filp->private_data;
-
- mutex_lock(&cam->s_mutex);
- (cam->users)--;
- if (filp == cam->owner) {
- cafe_ctlr_stop_dma(cam);
- cafe_free_sio_buffers(cam);
- cam->owner = NULL;
- }
- if (cam->users == 0) {
- cafe_ctlr_power_down(cam);
- if (alloc_bufs_at_read)
- cafe_free_dma_bufs(cam);
- }
- mutex_unlock(&cam->s_mutex);
- return 0;
-}
-
-
-
-static unsigned int cafe_v4l_poll(struct file *filp,
- struct poll_table_struct *pt)
-{
- struct cafe_camera *cam = filp->private_data;
-
- poll_wait(filp, &cam->iowait, pt);
- if (cam->next_buf >= 0)
- return POLLIN | POLLRDNORM;
- return 0;
-}
-
-
-
-static int cafe_vidioc_queryctrl(struct file *filp, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct cafe_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, core, queryctrl, qc);
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-
-static int cafe_vidioc_g_ctrl(struct file *filp, void *priv,
- struct v4l2_control *ctrl)
-{
- struct cafe_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, core, g_ctrl, ctrl);
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-
-static int cafe_vidioc_s_ctrl(struct file *filp, void *priv,
- struct v4l2_control *ctrl)
-{
- struct cafe_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, core, s_ctrl, ctrl);
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-
-
-
-
-static int cafe_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- strcpy(cap->driver, "cafe_ccic");
- strcpy(cap->card, "cafe_ccic");
- cap->version = CAFE_VERSION;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
- return 0;
-}
-
-
-/*
- * The default format we use until somebody says otherwise.
- */
-static const struct v4l2_pix_format cafe_def_pix_format = {
- .width = VGA_WIDTH,
- .height = VGA_HEIGHT,
- .pixelformat = V4L2_PIX_FMT_YUYV,
- .field = V4L2_FIELD_NONE,
- .bytesperline = VGA_WIDTH*2,
- .sizeimage = VGA_WIDTH*VGA_HEIGHT*2,
-};
-
-static const enum v4l2_mbus_pixelcode cafe_def_mbus_code =
- V4L2_MBUS_FMT_YUYV8_2X8;
-
-static int cafe_vidioc_enum_fmt_vid_cap(struct file *filp,
- void *priv, struct v4l2_fmtdesc *fmt)
-{
- if (fmt->index >= N_CAFE_FMTS)
- return -EINVAL;
- strlcpy(fmt->description, cafe_formats[fmt->index].desc,
- sizeof(fmt->description));
- fmt->pixelformat = cafe_formats[fmt->index].pixelformat;
- return 0;
-}
-
-static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
- struct v4l2_format *fmt)
-{
- struct cafe_camera *cam = priv;
- struct cafe_format_struct *f;
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
- struct v4l2_mbus_framefmt mbus_fmt;
- int ret;
-
- f = cafe_find_format(pix->pixelformat);
- pix->pixelformat = f->pixelformat;
- v4l2_fill_mbus_format(&mbus_fmt, pix, f->mbus_code);
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
- mutex_unlock(&cam->s_mutex);
- v4l2_fill_pix_format(pix, &mbus_fmt);
- pix->bytesperline = pix->width * f->bpp;
- pix->sizeimage = pix->height * pix->bytesperline;
- return ret;
-}
-
-static int cafe_vidioc_s_fmt_vid_cap(struct file *filp, void *priv,
- struct v4l2_format *fmt)
-{
- struct cafe_camera *cam = priv;
- struct cafe_format_struct *f;
- int ret;
-
- /*
- * Can't do anything if the device is not idle
- * Also can't if there are streaming buffers in place.
- */
- if (cam->state != S_IDLE || cam->n_sbufs > 0)
- return -EBUSY;
-
- f = cafe_find_format(fmt->fmt.pix.pixelformat);
-
- /*
- * See if the formatting works in principle.
- */
- ret = cafe_vidioc_try_fmt_vid_cap(filp, priv, fmt);
- if (ret)
- return ret;
- /*
- * Now we start to change things for real, so let's do it
- * under lock.
- */
- mutex_lock(&cam->s_mutex);
- cam->pix_format = fmt->fmt.pix;
- cam->mbus_code = f->mbus_code;
-
- /*
- * Make sure we have appropriate DMA buffers.
- */
- ret = -ENOMEM;
- if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage)
- cafe_free_dma_bufs(cam);
- if (cam->nbufs == 0) {
- if (cafe_alloc_dma_bufs(cam, 0))
- goto out;
- }
- /*
- * It looks like this might work, so let's program the sensor.
- */
- ret = cafe_cam_configure(cam);
- if (! ret)
- ret = cafe_ctlr_configure(cam);
- out:
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-/*
- * Return our stored notion of how the camera is/should be configured.
- * The V4l2 spec wants us to be smarter, and actually get this from
- * the camera (and not mess with it at open time). Someday.
- */
-static int cafe_vidioc_g_fmt_vid_cap(struct file *filp, void *priv,
- struct v4l2_format *f)
-{
- struct cafe_camera *cam = priv;
-
- f->fmt.pix = cam->pix_format;
- return 0;
-}
-
-/*
- * We only have one input - the sensor - so minimize the nonsense here.
- */
-static int cafe_vidioc_enum_input(struct file *filp, void *priv,
- struct v4l2_input *input)
-{
- if (input->index != 0)
- return -EINVAL;
-
- input->type = V4L2_INPUT_TYPE_CAMERA;
- input->std = V4L2_STD_ALL; /* Not sure what should go here */
- strcpy(input->name, "Camera");
- return 0;
-}
-
-static int cafe_vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int cafe_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- if (i != 0)
- return -EINVAL;
- return 0;
-}
-
-/* from vivi.c */
-static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
-{
- return 0;
-}
-
-/*
- * G/S_PARM. Most of this is done by the sensor, but we are
- * the level which controls the number of read buffers.
- */
-static int cafe_vidioc_g_parm(struct file *filp, void *priv,
- struct v4l2_streamparm *parms)
-{
- struct cafe_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, video, g_parm, parms);
- mutex_unlock(&cam->s_mutex);
- parms->parm.capture.readbuffers = n_dma_bufs;
- return ret;
-}
-
-static int cafe_vidioc_s_parm(struct file *filp, void *priv,
- struct v4l2_streamparm *parms)
-{
- struct cafe_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, video, s_parm, parms);
- mutex_unlock(&cam->s_mutex);
- parms->parm.capture.readbuffers = n_dma_bufs;
- return ret;
-}
-
-static int cafe_vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct cafe_camera *cam = priv;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (v4l2_chip_match_host(&chip->match)) {
- chip->ident = V4L2_IDENT_CAFE;
- return 0;
- }
- return sensor_call(cam, core, g_chip_ident, chip);
-}
-
-static int cafe_vidioc_enum_framesizes(struct file *filp, void *priv,
- struct v4l2_frmsizeenum *sizes)
-{
- struct cafe_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, video, enum_framesizes, sizes);
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-static int cafe_vidioc_enum_frameintervals(struct file *filp, void *priv,
- struct v4l2_frmivalenum *interval)
-{
- struct cafe_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, video, enum_frameintervals, interval);
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int cafe_vidioc_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct cafe_camera *cam = priv;
-
- if (v4l2_chip_match_host(&reg->match)) {
- reg->val = cafe_reg_read(cam, reg->reg);
- reg->size = 4;
- return 0;
- }
- return sensor_call(cam, core, g_register, reg);
-}
-
-static int cafe_vidioc_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct cafe_camera *cam = priv;
-
- if (v4l2_chip_match_host(&reg->match)) {
- cafe_reg_write(cam, reg->reg, reg->val);
- return 0;
- }
- return sensor_call(cam, core, s_register, reg);
-}
-#endif
-
-/*
- * This template device holds all of those v4l2 methods; we
- * clone it for specific real devices.
- */
-
-static const struct v4l2_file_operations cafe_v4l_fops = {
- .owner = THIS_MODULE,
- .open = cafe_v4l_open,
- .release = cafe_v4l_release,
- .read = cafe_v4l_read,
- .poll = cafe_v4l_poll,
- .mmap = cafe_v4l_mmap,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops cafe_v4l_ioctl_ops = {
- .vidioc_querycap = cafe_vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = cafe_vidioc_enum_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = cafe_vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = cafe_vidioc_s_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = cafe_vidioc_g_fmt_vid_cap,
- .vidioc_enum_input = cafe_vidioc_enum_input,
- .vidioc_g_input = cafe_vidioc_g_input,
- .vidioc_s_input = cafe_vidioc_s_input,
- .vidioc_s_std = cafe_vidioc_s_std,
- .vidioc_reqbufs = cafe_vidioc_reqbufs,
- .vidioc_querybuf = cafe_vidioc_querybuf,
- .vidioc_qbuf = cafe_vidioc_qbuf,
- .vidioc_dqbuf = cafe_vidioc_dqbuf,
- .vidioc_streamon = cafe_vidioc_streamon,
- .vidioc_streamoff = cafe_vidioc_streamoff,
- .vidioc_queryctrl = cafe_vidioc_queryctrl,
- .vidioc_g_ctrl = cafe_vidioc_g_ctrl,
- .vidioc_s_ctrl = cafe_vidioc_s_ctrl,
- .vidioc_g_parm = cafe_vidioc_g_parm,
- .vidioc_s_parm = cafe_vidioc_s_parm,
- .vidioc_enum_framesizes = cafe_vidioc_enum_framesizes,
- .vidioc_enum_frameintervals = cafe_vidioc_enum_frameintervals,
- .vidioc_g_chip_ident = cafe_vidioc_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = cafe_vidioc_g_register,
- .vidioc_s_register = cafe_vidioc_s_register,
-#endif
-};
-
-static struct video_device cafe_v4l_template = {
- .name = "cafe",
- .tvnorms = V4L2_STD_NTSC_M,
- .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */
-
- .fops = &cafe_v4l_fops,
- .ioctl_ops = &cafe_v4l_ioctl_ops,
- .release = video_device_release_empty,
-};
-
-
-/* ---------------------------------------------------------------------- */
-/*
- * Interrupt handler stuff
- */
-
-
-
-static void cafe_frame_tasklet(unsigned long data)
-{
- struct cafe_camera *cam = (struct cafe_camera *) data;
- int i;
- unsigned long flags;
- struct cafe_sio_buffer *sbuf;
-
- spin_lock_irqsave(&cam->dev_lock, flags);
- for (i = 0; i < cam->nbufs; i++) {
- int bufno = cam->next_buf;
- if (bufno < 0) { /* "will never happen" */
- cam_err(cam, "No valid bufs in tasklet!\n");
- break;
- }
- if (++(cam->next_buf) >= cam->nbufs)
- cam->next_buf = 0;
- if (! test_bit(bufno, &cam->flags))
- continue;
- if (list_empty(&cam->sb_avail))
- break; /* Leave it valid, hope for better later */
- clear_bit(bufno, &cam->flags);
- sbuf = list_entry(cam->sb_avail.next,
- struct cafe_sio_buffer, list);
- /*
- * Drop the lock during the big copy. This *should* be safe...
- */
- spin_unlock_irqrestore(&cam->dev_lock, flags);
- memcpy(sbuf->buffer, cam->dma_bufs[bufno],
- cam->pix_format.sizeimage);
- sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage;
- sbuf->v4lbuf.sequence = cam->buf_seq[bufno];
- sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
- sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
- spin_lock_irqsave(&cam->dev_lock, flags);
- list_move_tail(&sbuf->list, &cam->sb_full);
- }
- if (! list_empty(&cam->sb_full))
- wake_up(&cam->iowait);
- spin_unlock_irqrestore(&cam->dev_lock, flags);
-}
-
-
-
-static void cafe_frame_complete(struct cafe_camera *cam, int frame)
-{
- /*
- * Basic frame housekeeping.
- */
- if (test_bit(frame, &cam->flags) && printk_ratelimit())
- cam_err(cam, "Frame overrun on %d, frames lost\n", frame);
- set_bit(frame, &cam->flags);
- clear_bit(CF_DMA_ACTIVE, &cam->flags);
- if (cam->next_buf < 0)
- cam->next_buf = frame;
- cam->buf_seq[frame] = ++(cam->sequence);
-
- switch (cam->state) {
- /*
- * If in single read mode, try going speculative.
- */
- case S_SINGLEREAD:
- cam->state = S_SPECREAD;
- cam->specframes = 0;
- wake_up(&cam->iowait);
- break;
-
- /*
- * If we are already doing speculative reads, and nobody is
- * reading them, just stop.
- */
- case S_SPECREAD:
- if (++(cam->specframes) >= cam->nbufs) {
- cafe_ctlr_stop(cam);
- cafe_ctlr_irq_disable(cam);
- cam->state = S_IDLE;
- }
- wake_up(&cam->iowait);
- break;
- /*
- * For the streaming case, we defer the real work to the
- * camera tasklet.
- *
- * FIXME: if the application is not consuming the buffers,
- * we should eventually put things on hold and restart in
- * vidioc_dqbuf().
- */
- case S_STREAMING:
- tasklet_schedule(&cam->s_tasklet);
- break;
-
- default:
- cam_err(cam, "Frame interrupt in non-operational state\n");
- break;
- }
-}
-
-
-
-
-static void cafe_frame_irq(struct cafe_camera *cam, unsigned int irqs)
-{
- unsigned int frame;
-
- cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */
- /*
- * Handle any frame completions. There really should
- * not be more than one of these, or we have fallen
- * far behind.
- */
- for (frame = 0; frame < cam->nbufs; frame++)
- if (irqs & (IRQ_EOF0 << frame))
- cafe_frame_complete(cam, frame);
- /*
- * If a frame starts, note that we have DMA active. This
- * code assumes that we won't get multiple frame interrupts
- * at once; may want to rethink that.
- */
- if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2))
- set_bit(CF_DMA_ACTIVE, &cam->flags);
-}
-
-
-
-static irqreturn_t cafe_irq(int irq, void *data)
-{
- struct cafe_camera *cam = data;
- unsigned int irqs;
-
- spin_lock(&cam->dev_lock);
- irqs = cafe_reg_read(cam, REG_IRQSTAT);
- if ((irqs & ALLIRQS) == 0) {
- spin_unlock(&cam->dev_lock);
- return IRQ_NONE;
- }
- if (irqs & FRAMEIRQS)
- cafe_frame_irq(cam, irqs);
- if (irqs & TWSIIRQS) {
- cafe_reg_write(cam, REG_IRQSTAT, TWSIIRQS);
- wake_up(&cam->smbus_wait);
- }
- spin_unlock(&cam->dev_lock);
- return IRQ_HANDLED;
-}
-
-
-/* -------------------------------------------------------------------------- */
-/*
- * PCI interface stuff.
- */
-
-static const struct dmi_system_id olpc_xo1_dmi[] = {
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "OLPC"),
- DMI_MATCH(DMI_PRODUCT_NAME, "XO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "1"),
- },
- },
- { }
-};
-
-static int cafe_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- int ret;
- struct cafe_camera *cam;
- struct ov7670_config sensor_cfg = {
- /* This controller only does SMBUS */
- .use_smbus = true,
-
- /*
- * Exclude QCIF mode, because it only captures a tiny portion
- * of the sensor FOV
- */
- .min_width = 320,
- .min_height = 240,
- };
- struct i2c_board_info ov7670_info = {
- .type = "ov7670",
- .addr = 0x42,
- .platform_data = &sensor_cfg,
- };
-
- /*
- * Start putting together one of our big camera structures.
- */
- ret = -ENOMEM;
- cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL);
- if (cam == NULL)
- goto out;
- ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev);
- if (ret)
- goto out_free;
-
- mutex_init(&cam->s_mutex);
- spin_lock_init(&cam->dev_lock);
- cam->state = S_NOTREADY;
- cafe_set_config_needed(cam, 1);
- init_waitqueue_head(&cam->smbus_wait);
- init_waitqueue_head(&cam->iowait);
- cam->pdev = pdev;
- cam->pix_format = cafe_def_pix_format;
- cam->mbus_code = cafe_def_mbus_code;
- INIT_LIST_HEAD(&cam->dev_list);
- INIT_LIST_HEAD(&cam->sb_avail);
- INIT_LIST_HEAD(&cam->sb_full);
- tasklet_init(&cam->s_tasklet, cafe_frame_tasklet, (unsigned long) cam);
- /*
- * Get set up on the PCI bus.
- */
- ret = pci_enable_device(pdev);
- if (ret)
- goto out_unreg;
- pci_set_master(pdev);
-
- ret = -EIO;
- cam->regs = pci_iomap(pdev, 0, 0);
- if (! cam->regs) {
- printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n");
- goto out_unreg;
- }
- ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
- if (ret)
- goto out_iounmap;
- /*
- * Initialize the controller and leave it powered up. It will
- * stay that way until the sensor driver shows up.
- */
- cafe_ctlr_init(cam);
- cafe_ctlr_power_up(cam);
- /*
- * Set up I2C/SMBUS communications. We have to drop the mutex here
- * because the sensor could attach in this call chain, leading to
- * unsightly deadlocks.
- */
- ret = cafe_smbus_setup(cam);
- if (ret)
- goto out_freeirq;
-
- /* Apply XO-1 clock speed */
- if (dmi_check_system(olpc_xo1_dmi))
- sensor_cfg.clock_speed = 45;
-
- cam->sensor_addr = ov7670_info.addr;
- cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, &cam->i2c_adapter,
- &ov7670_info, NULL);
- if (cam->sensor == NULL) {
- ret = -ENODEV;
- goto out_smbus;
- }
-
- ret = cafe_cam_init(cam);
- if (ret)
- goto out_smbus;
-
- /*
- * Get the v4l2 setup done.
- */
- mutex_lock(&cam->s_mutex);
- cam->vdev = cafe_v4l_template;
- cam->vdev.debug = 0;
-/* cam->vdev.debug = V4L2_DEBUG_IOCTL_ARG;*/
- cam->vdev.v4l2_dev = &cam->v4l2_dev;
- ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
- if (ret)
- goto out_unlock;
- video_set_drvdata(&cam->vdev, cam);
-
- /*
- * If so requested, try to get our DMA buffers now.
- */
- if (!alloc_bufs_at_read) {
- if (cafe_alloc_dma_bufs(cam, 1))
- cam_warn(cam, "Unable to alloc DMA buffers at load"
- " will try again later.");
- }
-
- mutex_unlock(&cam->s_mutex);
- return 0;
-
-out_unlock:
- mutex_unlock(&cam->s_mutex);
-out_smbus:
- cafe_smbus_shutdown(cam);
-out_freeirq:
- cafe_ctlr_power_down(cam);
- free_irq(pdev->irq, cam);
-out_iounmap:
- pci_iounmap(pdev, cam->regs);
-out_free:
- v4l2_device_unregister(&cam->v4l2_dev);
-out_unreg:
- kfree(cam);
-out:
- return ret;
-}
-
-
-/*
- * Shut down an initialized device
- */
-static void cafe_shutdown(struct cafe_camera *cam)
-{
-/* FIXME: Make sure we take care of everything here */
- if (cam->n_sbufs > 0)
- /* What if they are still mapped? Shouldn't be, but... */
- cafe_free_sio_buffers(cam);
- cafe_ctlr_stop_dma(cam);
- cafe_ctlr_power_down(cam);
- cafe_smbus_shutdown(cam);
- cafe_free_dma_bufs(cam);
- free_irq(cam->pdev->irq, cam);
- pci_iounmap(cam->pdev, cam->regs);
- video_unregister_device(&cam->vdev);
-}
-
-
-static void cafe_pci_remove(struct pci_dev *pdev)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct cafe_camera *cam = to_cam(v4l2_dev);
-
- if (cam == NULL) {
- printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev);
- return;
- }
- mutex_lock(&cam->s_mutex);
- if (cam->users > 0)
- cam_warn(cam, "Removing a device with users!\n");
- cafe_shutdown(cam);
- v4l2_device_unregister(&cam->v4l2_dev);
- kfree(cam);
-/* No unlock - it no longer exists */
-}
-
-
-#ifdef CONFIG_PM
-/*
- * Basic power management.
- */
-static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct cafe_camera *cam = to_cam(v4l2_dev);
- int ret;
- enum cafe_state cstate;
-
- ret = pci_save_state(pdev);
- if (ret)
- return ret;
- cstate = cam->state; /* HACK - stop_dma sets to idle */
- cafe_ctlr_stop_dma(cam);
- cafe_ctlr_power_down(cam);
- pci_disable_device(pdev);
- cam->state = cstate;
- return 0;
-}
-
-
-static int cafe_pci_resume(struct pci_dev *pdev)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct cafe_camera *cam = to_cam(v4l2_dev);
- int ret = 0;
-
- pci_restore_state(pdev);
- ret = pci_enable_device(pdev);
-
- if (ret) {
- cam_warn(cam, "Unable to re-enable device on resume!\n");
- return ret;
- }
- cafe_ctlr_init(cam);
-
- mutex_lock(&cam->s_mutex);
- if (cam->users > 0) {
- cafe_ctlr_power_up(cam);
- __cafe_cam_reset(cam);
- } else {
- cafe_ctlr_power_down(cam);
- }
- mutex_unlock(&cam->s_mutex);
-
- set_bit(CF_CONFIG_NEEDED, &cam->flags);
- if (cam->state == S_SPECREAD)
- cam->state = S_IDLE; /* Don't bother restarting */
- else if (cam->state == S_SINGLEREAD || cam->state == S_STREAMING)
- ret = cafe_read_setup(cam, cam->state);
- return ret;
-}
-
-#endif /* CONFIG_PM */
-
-
-static struct pci_device_id cafe_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_MARVELL,
- PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, cafe_ids);
-
-static struct pci_driver cafe_pci_driver = {
- .name = "cafe1000-ccic",
- .id_table = cafe_ids,
- .probe = cafe_pci_probe,
- .remove = cafe_pci_remove,
-#ifdef CONFIG_PM
- .suspend = cafe_pci_suspend,
- .resume = cafe_pci_resume,
-#endif
-};
-
-
-
-
-static int __init cafe_init(void)
-{
- int ret;
-
- printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n",
- CAFE_VERSION);
- ret = pci_register_driver(&cafe_pci_driver);
- if (ret) {
- printk(KERN_ERR "Unable to register cafe_ccic driver\n");
- goto out;
- }
- ret = 0;
-
- out:
- return ret;
-}
-
-
-static void __exit cafe_exit(void)
-{
- pci_unregister_driver(&cafe_pci_driver);
-}
-
-module_init(cafe_init);
-module_exit(cafe_exit);
diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h
index 6d6d1843791c..ab252188981b 100644
--- a/drivers/media/video/cpia2/cpia2.h
+++ b/drivers/media/video/cpia2/cpia2.h
@@ -31,7 +31,6 @@
#ifndef __CPIA2_H__
#define __CPIA2_H__
-#include <linux/version.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/usb.h>
@@ -43,10 +42,6 @@
/* define for verbose debug output */
//#define _CPIA2_DEBUG_
-#define CPIA2_MAJ_VER 3
-#define CPIA2_MIN_VER 0
-#define CPIA2_PATCH_VER 0
-
/***
* Image defines
***/
diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c
index dc5b07a20f69..59c797c15277 100644
--- a/drivers/media/video/cpia2/cpia2_usb.c
+++ b/drivers/media/video/cpia2/cpia2_usb.c
@@ -31,6 +31,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/usb.h>
+#include <linux/module.h>
#include "cpia2.h"
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index 40eb6326e48a..077eb1db80a1 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -29,8 +29,7 @@
* Alan Cox <alan@lxorguk.ukuu.org.uk>
****************************************************************************/
-#include <linux/version.h>
-
+#define CPIA_VERSION "3.0.1"
#include <linux/module.h>
#include <linux/time.h>
@@ -80,6 +79,7 @@ MODULE_AUTHOR("Steve Miller (STMicroelectronics) <steve.miller@st.com>");
MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras");
MODULE_SUPPORTED_DEVICE("video");
MODULE_LICENSE("GPL");
+MODULE_VERSION(CPIA_VERSION);
#define ABOUT "V4L-Driver for Vision CPiA2 based cameras"
@@ -465,9 +465,6 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v
if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0)
memset(vc->bus_info,0, sizeof(vc->bus_info));
- vc->version = KERNEL_VERSION(CPIA2_MAJ_VER, CPIA2_MIN_VER,
- CPIA2_PATCH_VER);
-
vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING;
@@ -1558,8 +1555,8 @@ static void __init check_parameters(void)
*****************************************************************************/
static int __init cpia2_init(void)
{
- LOG("%s v%d.%d.%d\n",
- ABOUT, CPIA2_MAJ_VER, CPIA2_MIN_VER, CPIA2_PATCH_VER);
+ LOG("%s v%s\n",
+ ABOUT, CPIA_VERSION);
check_parameters();
cpia2_usb_init();
return 0;
@@ -1579,4 +1576,3 @@ static void __exit cpia2_exit(void)
module_init(cpia2_init);
module_exit(cpia2_exit);
-
diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile
index 2fadd9ded340..a86bab5893ef 100644
--- a/drivers/media/video/cx18/Makefile
+++ b/drivers/media/video/cx18/Makefile
@@ -8,6 +8,6 @@ cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o
obj-$(CONFIG_VIDEO_CX18) += cx18.o
obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c
index d50d69da387b..a1e6c2a32478 100644
--- a/drivers/media/video/cx18/cx18-alsa-main.c
+++ b/drivers/media/video/cx18/cx18-alsa-main.c
@@ -192,6 +192,7 @@ static int snd_cx18_init(struct v4l2_device *v4l2_dev)
err_exit_free:
if (sc != NULL)
snd_card_free(sc);
+ kfree(cxsc);
err_exit:
return ret;
}
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 9e2f870f4258..c6ff32a6137c 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -1085,6 +1085,8 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
setup.addr = ADDR_UNSET;
setup.type = cx->options.tuner;
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ if (cx->options.radio > 0)
+ setup.mode_mask |= T_RADIO;
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
cx18_reset_tuner_gpio : NULL;
cx18_call_all(cx, tuner, s_type_addr, &setup);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 086427288de8..b9a94fc5146d 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -25,7 +25,6 @@
#ifndef CX18_DRIVER_H
#define CX18_DRIVER_H
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -410,6 +409,7 @@ struct cx18_stream {
/* Videobuf for YUV video */
u32 pixelformat;
+ u32 vb_bytes_per_frame;
struct list_head vb_capture; /* video capture queue */
spinlock_t vb_lock;
struct timer_list vb_timeout;
@@ -431,10 +431,6 @@ struct cx18_open_id {
u32 open_id;
int type;
struct cx18 *cx;
-
- struct videobuf_queue vbuf_q;
- spinlock_t s_lock; /* Protect vbuf_q */
- enum v4l2_buf_type vb_type;
};
static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh)
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 07411f34885a..14cb961c22bd 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -784,8 +784,6 @@ int cx18_v4l2_close(struct file *filp)
cx18_release_stream(s);
} else {
cx18_stop_capture(id, 0);
- if (id->type == CX18_ENC_STREAM_TYPE_YUV)
- videobuf_mmap_free(&id->vbuf_q);
}
kfree(id);
mutex_unlock(&cx->serialize_lock);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index e80134f52ef5..66b1c15c3541 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -160,12 +160,7 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
pixfmt->priv = 0;
if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
pixfmt->pixelformat = s->pixelformat;
- /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
- UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
- if (s->pixelformat == V4L2_PIX_FMT_HM12)
- pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;
- else
- pixfmt->sizeimage = pixfmt->height * 720 * 2;
+ pixfmt->sizeimage = s->vb_bytes_per_frame;
pixfmt->bytesperline = 720;
} else {
pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
@@ -296,6 +291,12 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
return -EBUSY;
s->pixelformat = fmt->fmt.pix.pixelformat;
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = h * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = h * 720 * 2;
mbus_fmt.width = cx->cxhdl.width = w;
mbus_fmt.height = cx->cxhdl.height = h;
@@ -463,14 +464,16 @@ static int cx18_s_register(struct file *file, void *fh,
static int cx18_querycap(struct file *file, void *fh,
struct v4l2_capability *vcap)
{
- struct cx18 *cx = fh2id(fh)->cx;
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
snprintf(vcap->bus_info, sizeof(vcap->bus_info),
"PCI:%s", pci_name(cx->pci_dev));
- vcap->version = CX18_DRIVER_VERSION; /* version */
vcap->capabilities = cx->v4l2_cap; /* capabilities */
+ if (id->type == CX18_ENC_STREAM_TYPE_YUV)
+ vcap->capabilities |= V4L2_CAP_STREAMING;
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index c07191e09fcb..0c7796e76ac0 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -196,7 +196,7 @@ static void cx18_mdl_send_to_videobuf(struct cx18_stream *s,
}
/* If we've filled the buffer as per the callers res then dispatch it */
- if (vb_buf->bytes_used >= (vb_buf->vb.width * vb_buf->vb.height * 2)) {
+ if (vb_buf->bytes_used >= s->vb_bytes_per_frame) {
dispatch = 1;
vb_buf->bytes_used = 0;
}
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
index 05fe6bdbe062..b63fdfaac49e 100644
--- a/drivers/media/video/cx18/cx18-mailbox.h
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -69,7 +69,7 @@ struct cx18_mailbox {
/* Each command can have up to 6 arguments */
u32 args[MAX_MB_ARGUMENTS];
/* The return code can be one of the codes in the file cx23418.h. If the
- command is completed successfuly, the error will be ERR_SYS_SUCCESS.
+ command is completed successfully, the error will be ERR_SYS_SUCCESS.
If it is pending, the code is ERR_SYS_PENDING. If it failed, the error
code would indicate the task from which the error originated and will
be one of the errors in cx23418.h. In that case, the following
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 852f420fd271..638cca156b58 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -138,6 +138,12 @@ static int cx18_prepare_buffer(struct videobuf_queue *q,
buf->tvnorm = cx->std;
s->pixelformat = pixelformat;
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = height * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = height * 720 * 2;
cx18_dma_free(q, s, buf);
}
@@ -154,6 +160,12 @@ static int cx18_prepare_buffer(struct videobuf_queue *q,
buf->tvnorm = cx->std;
s->pixelformat = pixelformat;
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = height * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = height * 720 * 2;
rc = videobuf_iolock(q, &buf->vb, NULL);
if (rc != 0)
goto fail;
@@ -287,6 +299,7 @@ static void cx18_stream_init(struct cx18 *cx, int type)
/* Assume the previous pixel default */
s->pixelformat = V4L2_PIX_FMT_HM12;
+ s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2;
}
}
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
index cd189b6bbe20..fed48b6bb67b 100644
--- a/drivers/media/video/cx18/cx18-version.h
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -23,12 +23,6 @@
#define CX18_VERSION_H
#define CX18_DRIVER_NAME "cx18"
-#define CX18_DRIVER_VERSION_MAJOR 1
-#define CX18_DRIVER_VERSION_MINOR 5
-#define CX18_DRIVER_VERSION_PATCHLEVEL 0
-
-#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
-#define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \
- CX18_DRIVER_VERSION_MINOR, CX18_DRIVER_VERSION_PATCHLEVEL)
+#define CX18_VERSION "1.5.1"
#endif
diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile
index 2c2484355449..b3348975c7c2 100644
--- a/drivers/media/video/cx231xx/Makefile
+++ b/drivers/media/video/cx231xx/Makefile
@@ -8,9 +8,9 @@ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o
obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o
obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-usb
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-usb
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
index 8d7813415760..53ff26e7abf7 100644
--- a/drivers/media/video/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
@@ -355,6 +355,8 @@ int cx231xx_afe_update_power_control(struct cx231xx *dev,
case CX231XX_BOARD_HAUPPAUGE_EXETER:
case CX231XX_BOARD_HAUPPAUGE_USBLIVE2:
case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
FLD_PWRDN_ENABLE_PLL)) {
@@ -1733,6 +1735,8 @@ int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard)
break;
case CX231XX_BOARD_CNXT_RDE_253S:
case CX231XX_BOARD_CNXT_RDU_253S:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
func_mode = 0x01;
break;
default:
diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c
index 22703815a31f..53dae2a8272d 100644
--- a/drivers/media/video/cx231xx/cx231xx-cards.c
+++ b/drivers/media/video/cx231xx/cx231xx-cards.c
@@ -387,6 +387,7 @@ struct cx231xx_board cx231xx_boards[] = {
.norm = V4L2_STD_NTSC,
.no_alt_vanc = 1,
.external_av = 1,
+ .dont_use_port_3 = 1,
.input = {{
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
@@ -532,6 +533,76 @@ struct cx231xx_board cx231xx_boards[] = {
.gpio = NULL,
} },
},
+ [CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL] = {
+ .name = "Hauppauge WinTV USB2 FM (PAL)",
+ .tuner_type = TUNER_NXP_TDA18271,
+ .tuner_addr = 0x60,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .norm = V4L2_STD_PAL,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
+ [CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC] = {
+ .name = "Hauppauge WinTV USB2 FM (NTSC)",
+ .tuner_type = TUNER_NXP_TDA18271,
+ .tuner_addr = 0x60,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .norm = V4L2_STD_NTSC,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
};
const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
@@ -553,6 +624,10 @@ struct usb_device_id cx231xx_id_table[] = {
.driver_info = CX231XX_BOARD_CNXT_RDE_250},
{USB_DEVICE(0x0572, 0x58A0),
.driver_info = CX231XX_BOARD_CNXT_RDU_250},
+ {USB_DEVICE(0x2040, 0xb110),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL},
+ {USB_DEVICE(0x2040, 0xb111),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC},
{USB_DEVICE(0x2040, 0xb120),
.driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
{USB_DEVICE(0x2040, 0xb140),
@@ -1051,6 +1126,9 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
if (assoc_desc->bFirstInterface != ifnum) {
cx231xx_err(DRIVER_NAME ": Not found "
"matching IAD interface\n");
+ cx231xx_devused &= ~(1 << nr);
+ kfree(dev);
+ dev = NULL;
return -ENODEV;
}
diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c
index abe500feb7dd..d4457f9488ee 100644
--- a/drivers/media/video/cx231xx/cx231xx-core.c
+++ b/drivers/media/video/cx231xx/cx231xx-core.c
@@ -742,6 +742,8 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
case CX231XX_BOARD_CNXT_RDU_253S:
case CX231XX_BOARD_HAUPPAUGE_EXETER:
case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
break;
default:
@@ -1381,6 +1383,8 @@ int cx231xx_dev_init(struct cx231xx *dev)
case CX231XX_BOARD_CNXT_RDU_253S:
case CX231XX_BOARD_HAUPPAUGE_EXETER:
case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
break;
default:
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
index a69c24d8db06..6e81f970dc7d 100644
--- a/drivers/media/video/cx231xx/cx231xx-video.c
+++ b/drivers/media/video/cx231xx/cx231xx-video.c
@@ -29,7 +29,6 @@
#include <linux/bitmap.h>
#include <linux/usb.h>
#include <linux/i2c.h>
-#include <linux/version.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/slab.h>
@@ -45,7 +44,7 @@
#include "cx231xx.h"
#include "cx231xx-vbi.h"
-#define CX231XX_VERSION_CODE KERNEL_VERSION(0, 0, 1)
+#define CX231XX_VERSION "0.0.2"
#define DRIVER_AUTHOR "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
#define DRIVER_DESC "Conexant cx231xx based USB video device driver"
@@ -70,6 +69,7 @@ do {\
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+MODULE_VERSION(CX231XX_VERSION);
static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
@@ -1179,7 +1179,8 @@ static int vidioc_enum_input(struct file *file, void *priv,
{
struct cx231xx_fh *fh = priv;
struct cx231xx *dev = fh->dev;
- unsigned int n;
+ u32 gen_stat;
+ unsigned int ret, n;
n = i->index;
if (n >= MAX_CX231XX_INPUT)
@@ -1198,6 +1199,18 @@ static int vidioc_enum_input(struct file *file, void *priv,
i->std = dev->vdev->tvnorms;
+ /* If they are asking about the active input, read signal status */
+ if (n == dev->video_input) {
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ GEN_STAT, 2, &gen_stat, 4);
+ if (ret > 0) {
+ if ((gen_stat & FLD_VPRES) == 0x00)
+ i->status |= V4L2_IN_ST_NO_SIGNAL;
+ if ((gen_stat & FLD_HLOCK) == 0x00)
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ }
+ }
+
return 0;
}
@@ -1869,8 +1882,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->version = CX231XX_VERSION_CODE;
-
cap->capabilities = V4L2_CAP_VBI_CAPTURE |
#if 0
V4L2_CAP_SLICED_VBI_CAPTURE |
@@ -2057,7 +2068,6 @@ static int radio_querycap(struct file *file, void *priv,
strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->version = CX231XX_VERSION_CODE;
cap->capabilities = V4L2_CAP_TUNER;
return 0;
}
@@ -2570,11 +2580,8 @@ int cx231xx_register_analog_devices(struct cx231xx *dev)
{
int ret;
- cx231xx_info("%s: v4l2 driver version %d.%d.%d\n",
- dev->name,
- (CX231XX_VERSION_CODE >> 16) & 0xff,
- (CX231XX_VERSION_CODE >> 8) & 0xff,
- CX231XX_VERSION_CODE & 0xff);
+ cx231xx_info("%s: v4l2 driver version %s\n",
+ dev->name, CX231XX_VERSION);
/* set default norm */
/*dev->norm = cx231xx_video_template.current_norm; */
diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h
index 46dd84067816..2000bc64c497 100644
--- a/drivers/media/video/cx231xx/cx231xx.h
+++ b/drivers/media/video/cx231xx/cx231xx.h
@@ -43,7 +43,7 @@
#include "cx231xx-conf-reg.h"
#define DRIVER_NAME "cx231xx"
-#define PWR_SLEEP_INTERVAL 5
+#define PWR_SLEEP_INTERVAL 10
/* I2C addresses for control block in Cx231xx */
#define AFE_DEVICE_ADDRESS 0x60
@@ -67,6 +67,8 @@
#define CX231XX_BOARD_PV_XCAPTURE_USB 11
#define CX231XX_BOARD_KWORLD_UB430_USB_HYBRID 12
#define CX231XX_BOARD_ICONBIT_U100 13
+#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL 14
+#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15
/* Limits minimum and default number of buffers */
#define CX231XX_MIN_BUF 4
@@ -112,7 +114,6 @@
V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK)
-#define CX231xx_VERSION_CODE KERNEL_VERSION(0, 0, 2)
#define SLEEP_S5H1432 30
#define CX23417_OSC_EN 8
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index caab1bfb79e2..b391e9bda877 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -38,7 +38,7 @@ config VIDEO_CX23885
config MEDIA_ALTERA_CI
tristate "Altera FPGA based CI module"
depends on VIDEO_CX23885 && DVB_CORE
- select STAPL_ALTERA
+ select ALTERA_STAPL
---help---
An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card.
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
index 23293c7b6ac7..f81f2796a0f9 100644
--- a/drivers/media/video/cx23885/Makefile
+++ b/drivers/media/video/cx23885/Makefile
@@ -2,14 +2,14 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
- cx23885-f300.o
+ cx23885-f300.o cx23885-alsa.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/cx23885/altera-ci.c b/drivers/media/video/cx23885/altera-ci.c
index 678539b2acfa..1fa8927f0d36 100644
--- a/drivers/media/video/cx23885/altera-ci.c
+++ b/drivers/media/video/cx23885/altera-ci.c
@@ -52,7 +52,6 @@
* | DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0|
* +-------+-------+-------+-------+-------+-------+-------+-------+
*/
-#include <linux/version.h>
#include <media/videobuf-dma-sg.h>
#include <media/videobuf-dvb.h>
#include "altera-ci.h"
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
index 9a98dc55f657..67c4a59bd882 100644
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -1359,7 +1359,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->card, cx23885_boards[tsport->dev->board].name,
sizeof(cap->card));
sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->version = CX23885_VERSION_CODE;
cap->capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
diff --git a/drivers/media/video/cx23885/cx23885-alsa.c b/drivers/media/video/cx23885/cx23885-alsa.c
new file mode 100644
index 000000000000..795169237e70
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-alsa.c
@@ -0,0 +1,535 @@
+/*
+ *
+ * Support for CX23885 analog audio capture
+ *
+ * (c) 2008 Mijhail Moreyra <mijhail.moreyra@gmail.com>
+ * Adapted from cx88-alsa.c
+ * (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+
+#include <asm/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+#include <sound/tlv.h>
+
+
+#include "cx23885.h"
+#include "cx23885-reg.h"
+
+#define AUDIO_SRAM_CHANNEL SRAM_CH07
+
+#define dprintk(level, fmt, arg...) if (audio_debug >= level) \
+ printk(KERN_INFO "%s: " fmt, chip->dev->name , ## arg)
+
+#define dprintk_core(level, fmt, arg...) if (audio_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, chip->dev->name , ## arg)
+
+/****************************************************************************
+ Module global static vars
+ ****************************************************************************/
+
+static unsigned int disable_analog_audio;
+module_param(disable_analog_audio, int, 0644);
+MODULE_PARM_DESC(disable_analog_audio, "disable analog audio ALSA driver");
+
+static unsigned int audio_debug;
+module_param(audio_debug, int, 0644);
+MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]");
+
+/****************************************************************************
+ Board specific funtions
+ ****************************************************************************/
+
+/* Constants taken from cx88-reg.h */
+#define AUD_INT_DN_RISCI1 (1 << 0)
+#define AUD_INT_UP_RISCI1 (1 << 1)
+#define AUD_INT_RDS_DN_RISCI1 (1 << 2)
+#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */
+#define AUD_INT_UP_RISCI2 (1 << 5)
+#define AUD_INT_RDS_DN_RISCI2 (1 << 6)
+#define AUD_INT_DN_SYNC (1 << 12)
+#define AUD_INT_UP_SYNC (1 << 13)
+#define AUD_INT_RDS_DN_SYNC (1 << 14)
+#define AUD_INT_OPC_ERR (1 << 16)
+#define AUD_INT_BER_IRQ (1 << 20)
+#define AUD_INT_MCHG_IRQ (1 << 21)
+#define GP_COUNT_CONTROL_RESET 0x3
+
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int cx23885_start_audio_dma(struct cx23885_audio_dev *chip)
+{
+ struct cx23885_audio_buffer *buf = chip->buf;
+ struct cx23885_dev *dev = chip->dev;
+ struct sram_channel *audio_ch =
+ &dev->sram_channels[AUDIO_SRAM_CHANNEL];
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
+ cx_clear(AUD_INT_DMA_CTL, 0x11);
+
+ /* setup fifo + format - out channel */
+ cx23885_sram_channel_setup(chip->dev, audio_ch, buf->bpl,
+ buf->risc.dma);
+
+ /* sets bpl size */
+ cx_write(AUD_INT_A_LNGTH, buf->bpl);
+
+ /* This is required to get good audio (1 seems to be ok) */
+ cx_write(AUD_INT_A_MODE, 1);
+
+ /* reset counter */
+ cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+ atomic_set(&chip->count, 0);
+
+ dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d "
+ "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start+12)>>1,
+ chip->num_periods, buf->bpl * chip->num_periods);
+
+ /* Enables corresponding bits at AUD_INT_STAT */
+ cx_write(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI1);
+
+ /* Clean any pending interrupt bits already set */
+ cx_write(AUDIO_INT_INT_STAT, ~0);
+
+ /* enable audio irqs */
+ cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */
+ cx_set(AUD_INT_DMA_CTL, 0x11); /* audio downstream FIFO and
+ RISC enable */
+ if (audio_debug)
+ cx23885_sram_channel_dump(chip->dev, audio_ch);
+
+ return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int cx23885_stop_audio_dma(struct cx23885_audio_dev *chip)
+{
+ struct cx23885_dev *dev = chip->dev;
+ dprintk(1, "Stopping audio DMA\n");
+
+ /* stop dma */
+ cx_clear(AUD_INT_DMA_CTL, 0x11);
+
+ /* disable irqs */
+ cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT);
+ cx_clear(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI1);
+
+ if (audio_debug)
+ cx23885_sram_channel_dump(chip->dev,
+ &dev->sram_channels[AUDIO_SRAM_CHANNEL]);
+
+ return 0;
+}
+
+/*
+ * BOARD Specific: Handles audio IRQ
+ */
+int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask)
+{
+ struct cx23885_audio_dev *chip = dev->audio_dev;
+
+ if (0 == (status & mask))
+ return 0;
+
+ cx_write(AUDIO_INT_INT_STAT, status);
+
+ /* risc op code error */
+ if (status & AUD_INT_OPC_ERR) {
+ printk(KERN_WARNING "%s/1: Audio risc op code error\n",
+ dev->name);
+ cx_clear(AUD_INT_DMA_CTL, 0x11);
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[AUDIO_SRAM_CHANNEL]);
+ }
+ if (status & AUD_INT_DN_SYNC) {
+ dprintk(1, "Downstream sync error\n");
+ cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+ return 1;
+ }
+ /* risc1 downstream */
+ if (status & AUD_INT_DN_RISCI1) {
+ atomic_set(&chip->count, cx_read(AUD_INT_A_GPCNT));
+ snd_pcm_period_elapsed(chip->substream);
+ }
+ /* FIXME: Any other status should deserve a special handling? */
+
+ return 1;
+}
+
+static int dsp_buffer_free(struct cx23885_audio_dev *chip)
+{
+ BUG_ON(!chip->dma_size);
+
+ dprintk(2, "Freeing buffer\n");
+ videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
+ videobuf_dma_free(chip->dma_risc);
+ btcx_riscmem_free(chip->pci, &chip->buf->risc);
+ kfree(chip->buf);
+
+ chip->dma_risc = NULL;
+ chip->dma_size = 0;
+
+ return 0;
+}
+
+/****************************************************************************
+ ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE 4096
+
+static struct snd_pcm_hardware snd_cx23885_digital_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* Analog audio output will be full of clicks and pops if there
+ are not exactly four lines in the SRAM FIFO buffer. */
+ .period_bytes_min = DEFAULT_FIFO_SIZE/4,
+ .period_bytes_max = DEFAULT_FIFO_SIZE/4,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .buffer_bytes_max = (1024*1024),
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_cx23885_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ if (!chip) {
+ printk(KERN_ERR "BUG: cx23885 can't find device struct."
+ " Can't proceed with open\n");
+ return -ENODEV;
+ }
+
+ err = snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto _error;
+
+ chip->substream = substream;
+
+ runtime->hw = snd_cx23885_digital_hw;
+
+ if (chip->dev->sram_channels[AUDIO_SRAM_CHANNEL].fifo_size !=
+ DEFAULT_FIFO_SIZE) {
+ unsigned int bpl = chip->dev->
+ sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 4;
+ bpl &= ~7; /* must be multiple of 8 */
+ runtime->hw.period_bytes_min = bpl;
+ runtime->hw.period_bytes_max = bpl;
+ }
+
+ return 0;
+_error:
+ dprintk(1, "Error opening PCM!\n");
+ return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_cx23885_close(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_cx23885_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct videobuf_dmabuf *dma;
+
+ struct cx23885_audio_buffer *buf;
+ int ret;
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ chip->period_size = params_period_bytes(hw_params);
+ chip->num_periods = params_periods(hw_params);
+ chip->dma_size = chip->period_size * params_periods(hw_params);
+
+ BUG_ON(!chip->dma_size);
+ BUG_ON(chip->num_periods & (chip->num_periods-1));
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (NULL == buf)
+ return -ENOMEM;
+
+ buf->bpl = chip->period_size;
+
+ dma = &buf->dma;
+ videobuf_dma_init(dma);
+ ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+ (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
+ if (ret < 0)
+ goto error;
+
+ ret = videobuf_dma_map(&chip->pci->dev, dma);
+ if (ret < 0)
+ goto error;
+
+ ret = cx23885_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
+ chip->period_size, chip->num_periods, 1);
+ if (ret < 0)
+ goto error;
+
+ /* Loop back to start of program */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ chip->buf = buf;
+ chip->dma_risc = dma;
+
+ substream->runtime->dma_area = chip->dma_risc->vaddr;
+ substream->runtime->dma_bytes = chip->dma_size;
+ substream->runtime->dma_addr = 0;
+
+ return 0;
+
+error:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_cx23885_hw_free(struct snd_pcm_substream *substream)
+{
+
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_cx23885_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * trigger callback
+ */
+static int snd_cx23885_card_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ /* Local interrupts are already disabled by ALSA */
+ spin_lock(&chip->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err = cx23885_start_audio_dma(chip);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ err = cx23885_stop_audio_dma(chip);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ spin_unlock(&chip->lock);
+
+ return err;
+}
+
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_cx23885_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u16 count;
+
+ count = atomic_read(&chip->count);
+
+ return runtime->period_size * (count & (runtime->periods-1));
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+static struct page *snd_cx23885_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ void *pageptr = substream->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_cx23885_pcm_ops = {
+ .open = snd_cx23885_pcm_open,
+ .close = snd_cx23885_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cx23885_hw_params,
+ .hw_free = snd_cx23885_hw_free,
+ .prepare = snd_cx23885_prepare,
+ .trigger = snd_cx23885_card_trigger,
+ .pointer = snd_cx23885_pointer,
+ .page = snd_cx23885_page,
+};
+
+/*
+ * create a PCM device
+ */
+static int snd_cx23885_pcm(struct cx23885_audio_dev *chip, int device,
+ char *name)
+{
+ int err;
+ struct snd_pcm *pcm;
+
+ err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = chip;
+ strcpy(pcm->name, name);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx23885_pcm_ops);
+
+ return 0;
+}
+
+/****************************************************************************
+ Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * Alsa Constructor - Component probe
+ */
+
+struct cx23885_audio_dev *cx23885_audio_register(struct cx23885_dev *dev)
+{
+ struct snd_card *card;
+ struct cx23885_audio_dev *chip;
+ int err;
+
+ if (disable_analog_audio)
+ return NULL;
+
+ if (dev->sram_channels[AUDIO_SRAM_CHANNEL].cmds_start == 0) {
+ printk(KERN_WARNING "%s(): Missing SRAM channel configuration "
+ "for analog TV Audio\n", __func__);
+ return NULL;
+ }
+
+ err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(struct cx23885_audio_dev), &card);
+ if (err < 0)
+ goto error;
+
+ chip = (struct cx23885_audio_dev *) card->private_data;
+ chip->dev = dev;
+ chip->pci = dev->pci;
+ chip->card = card;
+ spin_lock_init(&chip->lock);
+
+ snd_card_set_dev(card, &dev->pci->dev);
+
+ err = snd_cx23885_pcm(chip, 0, "CX23885 Digital");
+ if (err < 0)
+ goto error;
+
+ strcpy(card->driver, "CX23885");
+ sprintf(card->shortname, "Conexant CX23885");
+ sprintf(card->longname, "%s at %s", card->shortname, dev->name);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dprintk(0, "registered ALSA audio device\n");
+
+ return chip;
+
+error:
+ snd_card_free(card);
+ printk(KERN_ERR "%s(): Failed to register analog "
+ "audio adapter\n", __func__);
+
+ return NULL;
+}
+
+/*
+ * ALSA destructor
+ */
+void cx23885_audio_unregister(struct cx23885_dev *dev)
+{
+ struct cx23885_audio_dev *chip = dev->audio_dev;
+
+ snd_card_free(chip->card);
+}
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 934185cca758..c3cf08945e4c 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -25,15 +25,21 @@
#include <linux/delay.h>
#include <media/cx25840.h>
#include <linux/firmware.h>
+#include <misc/altera.h>
-#include "../../../staging/altera-stapl/altera.h"
#include "cx23885.h"
#include "tuner-xc2028.h"
+#include "netup-eeprom.h"
#include "netup-init.h"
#include "altera-ci.h"
+#include "xc4000.h"
#include "xc5000.h"
#include "cx23888-ir.h"
+static unsigned int netup_card_rev = 1;
+module_param(netup_card_rev, int, 0644);
+MODULE_PARM_DESC(netup_card_rev,
+ "NetUP Dual DVB-T/C CI card revision");
static unsigned int enable_885_ir;
module_param(enable_885_ir, int, 0644);
MODULE_PARM_DESC(enable_885_ir,
@@ -100,12 +106,14 @@ struct cx23885_board cx23885_boards[] = {
.vmux = CX25840_VIN7_CH3 |
CX25840_VIN5_CH2 |
CX25840_VIN2_CH1,
+ .amux = CX25840_AUDIO8,
.gpio0 = 0,
}, {
.type = CX23885_VMUX_COMPOSITE1,
.vmux = CX25840_VIN7_CH3 |
CX25840_VIN4_CH2 |
CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
.gpio0 = 0,
}, {
.type = CX23885_VMUX_SVIDEO,
@@ -113,6 +121,7 @@ struct cx23885_board cx23885_boards[] = {
CX25840_VIN4_CH2 |
CX25840_VIN8_CH1 |
CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
.gpio0 = 0,
} },
},
@@ -147,7 +156,30 @@ struct cx23885_board cx23885_boards[] = {
},
[CX23885_BOARD_HAUPPAUGE_HVR1500] = {
.name = "Hauppauge WinTV-HVR1500",
+ .porta = CX23885_ANALOG_VIDEO,
.portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61, /* 0xc2 >> 1 */
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .gpio0 = 0,
+ } },
},
[CX23885_BOARD_HAUPPAUGE_HVR1200] = {
.name = "Hauppauge WinTV-HVR1200",
@@ -175,6 +207,34 @@ struct cx23885_board cx23885_boards[] = {
.name = "Leadtek Winfast PxDVR3200 H",
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000] = {
+ .name = "Leadtek Winfast PxDVR3200 H XC4000",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_XC4000,
+ .tuner_addr = 0x61,
+ .radio_type = TUNER_XC4000,
+ .radio_addr = 0x61,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN2_CH1 |
+ CX25840_VIN5_CH2 |
+ CX25840_NONE0_CH3,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE1,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ }, {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_VIN7_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN8_CH3 |
+ CX25840_COMPONENT_ON,
+ } },
+ },
[CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = {
.name = "Compro VideoMate E650F",
.portc = CX23885_MPEG_DVB,
@@ -353,6 +413,31 @@ struct cx23885_board cx23885_boards[] = {
.vmux = CX25840_COMPOSITE1,
} },
},
+ [CX23885_BOARD_MPX885] = {
+ .name = "MPX-885",
+ .porta = CX23885_ANALOG_VIDEO,
+ .input = {{
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE1,
+ .amux = CX25840_AUDIO6,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE2,
+ .vmux = CX25840_COMPOSITE2,
+ .amux = CX25840_AUDIO6,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE3,
+ .vmux = CX25840_COMPOSITE3,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE4,
+ .vmux = CX25840_COMPOSITE4,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ } },
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -433,6 +518,10 @@ struct cx23885_subid cx23885_subids[] = {
.subdevice = 0x6681,
.card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
}, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f39,
+ .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000,
+ }, {
.subvendor = 0x185b,
.subdevice = 0xe800,
.card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F,
@@ -749,6 +838,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg)
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
@@ -909,6 +999,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
cx_set(GP0_IO, 0x000f000f);
break;
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
@@ -1097,12 +1188,19 @@ int cx23885_ir_init(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1200:
case CX23885_BOARD_HAUPPAUGE_HVR1400:
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
case CX23885_BOARD_HAUPPAUGE_HVR1275:
case CX23885_BOARD_HAUPPAUGE_HVR1255:
case CX23885_BOARD_HAUPPAUGE_HVR1210:
/* FIXME: Implement me */
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ ret = cx23888_ir_probe(dev);
+ if (ret)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
ret = cx23888_ir_probe(dev);
@@ -1156,6 +1254,7 @@ int cx23885_ir_init(struct cx23885_dev *dev)
void cx23885_ir_fini(struct cx23885_dev *dev)
{
switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
cx23885_irq_remove(dev, PCI_MSK_IR);
@@ -1199,6 +1298,7 @@ int netup_jtag_io(void *device, int tms, int tdi, int read_tdo)
void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
{
switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
if (dev->sd_ir)
@@ -1325,6 +1425,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1700:
case CX23885_BOARD_HAUPPAUGE_HVR1400:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_HAUPPAUGE_HVR1270:
case CX23885_BOARD_HAUPPAUGE_HVR1275:
@@ -1353,16 +1454,20 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
case CX23885_BOARD_HAUPPAUGE_HVR1700:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_MYGICA_X8506:
case CX23885_BOARD_MAGICPRO_PROHDTVE2:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_MPX885:
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", 0x88 >> 1, NULL);
@@ -1383,6 +1488,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
const struct firmware *fw;
const char *filename = "dvb-netup-altera-01.fw";
char *action = "configure";
+ static struct netup_card_info cinfo;
struct altera_config netup_config = {
.dev = dev,
.action = action,
@@ -1391,6 +1497,21 @@ void cx23885_card_setup(struct cx23885_dev *dev)
netup_initialize(dev);
+ netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
+ if (netup_card_rev)
+ cinfo.rev = netup_card_rev;
+
+ switch (cinfo.rev) {
+ case 0x4:
+ filename = "dvb-netup-altera-04.fw";
+ break;
+ default:
+ filename = "dvb-netup-altera-01.fw";
+ break;
+ }
+ printk(KERN_INFO "NetUP card rev=0x%x fw_filename=%s\n",
+ cinfo.rev, filename);
+
ret = request_firmware(&fw, filename, &dev->pci->dev);
if (ret != 0)
printk(KERN_ERR "did not find the firmware file. (%s) "
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index 64d9b2136ff6..40e68b22015e 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -42,6 +42,7 @@
MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
MODULE_LICENSE("GPL");
+MODULE_VERSION(CX23885_VERSION);
static unsigned int debug;
module_param(debug, int, 0644);
@@ -53,7 +54,7 @@ MODULE_PARM_DESC(card, "card type");
#define dprintk(level, fmt, arg...)\
do { if (debug >= level)\
- printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
} while (0)
static unsigned int cx23885_devcount;
@@ -154,12 +155,12 @@ static struct sram_channel cx23885_sram_channels[] = {
.cnt2_reg = DMA5_CNT2,
},
[SRAM_CH07] = {
- .name = "ch7",
- .cmds_start = 0x0,
- .ctrl_start = 0x0,
- .cdt = 0x0,
- .fifo_start = 0x0,
- .fifo_size = 0x0,
+ .name = "TV Audio",
+ .cmds_start = 0x10190,
+ .ctrl_start = 0x10480,
+ .cdt = 0x10a00,
+ .fifo_start = 0x7000,
+ .fifo_size = 0x1000,
.ptr1_reg = DMA6_PTR1,
.ptr2_reg = DMA6_PTR2,
.cnt1_reg = DMA6_CNT1,
@@ -1081,10 +1082,10 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev)
static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist,
unsigned int offset, u32 sync_line,
unsigned int bpl, unsigned int padding,
- unsigned int lines)
+ unsigned int lines, unsigned int lpi)
{
struct scatterlist *sg;
- unsigned int line, todo;
+ unsigned int line, todo, sol;
/* sync instruction */
if (sync_line != NO_SYNC_LINE)
@@ -1097,16 +1098,22 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist,
offset -= sg_dma_len(sg);
sg++;
}
+
+ if (lpi && line > 0 && !(line % lpi))
+ sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
+ else
+ sol = RISC_SOL;
+
if (bpl <= sg_dma_len(sg)-offset) {
/* fits into current chunk */
- *(rp++) = cpu_to_le32(RISC_WRITE|RISC_SOL|RISC_EOL|bpl);
+ *(rp++) = cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl);
*(rp++) = cpu_to_le32(sg_dma_address(sg)+offset);
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
offset += bpl;
} else {
/* scanline needs to be split */
todo = bpl;
- *(rp++) = cpu_to_le32(RISC_WRITE|RISC_SOL|
+ *(rp++) = cpu_to_le32(RISC_WRITE|sol|
(sg_dma_len(sg)-offset));
*(rp++) = cpu_to_le32(sg_dma_address(sg)+offset);
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
@@ -1163,10 +1170,10 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
rp = risc->cpu;
if (UNSET != top_offset)
rp = cx23885_risc_field(rp, sglist, top_offset, 0,
- bpl, padding, lines);
+ bpl, padding, lines, 0);
if (UNSET != bottom_offset)
rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200,
- bpl, padding, lines);
+ bpl, padding, lines, 0);
/* save pointer to jmp instruction address */
risc->jmp = rp;
@@ -1174,11 +1181,11 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
return 0;
}
-static int cx23885_risc_databuffer(struct pci_dev *pci,
+int cx23885_risc_databuffer(struct pci_dev *pci,
struct btcx_riscmem *risc,
struct scatterlist *sglist,
unsigned int bpl,
- unsigned int lines)
+ unsigned int lines, unsigned int lpi)
{
u32 instructions;
__le32 *rp;
@@ -1198,7 +1205,8 @@ static int cx23885_risc_databuffer(struct pci_dev *pci,
/* write risc instructions */
rp = risc->cpu;
- rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines);
+ rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE,
+ bpl, 0, lines, lpi);
/* save pointer to jmp instruction address */
risc->jmp = rp;
@@ -1206,6 +1214,54 @@ static int cx23885_risc_databuffer(struct pci_dev *pci,
return 0;
}
+int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int top_offset,
+ unsigned int bottom_offset, unsigned int bpl,
+ unsigned int padding, unsigned int lines)
+{
+ u32 instructions, fields;
+ __le32 *rp;
+ int rc;
+
+ fields = 0;
+ if (UNSET != top_offset)
+ fields++;
+ if (UNSET != bottom_offset)
+ fields++;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Padding
+ can cause next bpl to start close to a page border. First DMA
+ region may be smaller than PAGE_SIZE */
+ /* write and jump need and extra dword */
+ instructions = fields * (1 + ((bpl + padding) * lines)
+ / PAGE_SIZE + lines);
+ instructions += 2;
+ rc = btcx_riscmem_alloc(pci, risc, instructions*12);
+ if (rc < 0)
+ return rc;
+ /* write risc instructions */
+ rp = risc->cpu;
+
+ /* Sync to line 6, so US CC line 21 will appear in line '12'
+ * in the userland vbi payload */
+ if (UNSET != top_offset)
+ rp = cx23885_risc_field(rp, sglist, top_offset, 6,
+ bpl, padding, lines, 0);
+
+ if (UNSET != bottom_offset)
+ rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x207,
+ bpl, padding, lines, 0);
+
+
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ return 0;
+}
+
+
int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
u32 reg, u32 mask, u32 value)
{
@@ -1516,7 +1572,7 @@ int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
goto fail;
cx23885_risc_databuffer(dev->pci, &buf->risc,
videobuf_to_dma(&buf->vb)->sglist,
- buf->vb.width, buf->vb.height);
+ buf->vb.width, buf->vb.height, 0);
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
@@ -1740,15 +1796,19 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
struct cx23885_tsport *ts2 = &dev->ts2;
u32 pci_status, pci_mask;
u32 vida_status, vida_mask;
+ u32 audint_status, audint_mask;
u32 ts1_status, ts1_mask;
u32 ts2_status, ts2_mask;
int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
+ int audint_count = 0;
bool subdev_handled;
pci_status = cx_read(PCI_INT_STAT);
pci_mask = cx23885_irq_get_mask(dev);
vida_status = cx_read(VID_A_INT_STAT);
vida_mask = cx_read(VID_A_INT_MSK);
+ audint_status = cx_read(AUDIO_INT_INT_STAT);
+ audint_mask = cx_read(AUDIO_INT_INT_MSK);
ts1_status = cx_read(VID_B_INT_STAT);
ts1_mask = cx_read(VID_B_INT_MSK);
ts2_status = cx_read(VID_C_INT_STAT);
@@ -1758,12 +1818,15 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
goto out;
vida_count = cx_read(VID_A_GPCNT);
+ audint_count = cx_read(AUD_INT_A_GPCNT);
ts1_count = cx_read(ts1->reg_gpcnt);
ts2_count = cx_read(ts2->reg_gpcnt);
dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n",
pci_status, pci_mask);
dprintk(7, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n",
vida_status, vida_mask, vida_count);
+ dprintk(7, "audint_status: 0x%08x audint_mask: 0x%08x count: 0x%x\n",
+ audint_status, audint_mask, audint_count);
dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n",
ts1_status, ts1_mask, ts1_count);
dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n",
@@ -1860,6 +1923,9 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
if (vida_status)
handled += cx23885_video_irq(dev, vida_status);
+ if (audint_status)
+ handled += cx23885_audio_irq(dev, audint_status, audint_mask);
+
if (pci_status & PCI_MSK_IR) {
subdev_handled = false;
v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
@@ -2060,12 +2126,8 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
goto fail_irq;
}
- if (!pci_enable_msi(pci_dev))
- err = request_irq(pci_dev->irq, cx23885_irq,
- IRQF_DISABLED, dev->name, dev);
- else
- err = request_irq(pci_dev->irq, cx23885_irq,
- IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+ err = request_irq(pci_dev->irq, cx23885_irq,
+ IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
if (err < 0) {
printk(KERN_ERR "%s: can't get IRQ %d\n",
dev->name, pci_dev->irq);
@@ -2114,7 +2176,6 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
/* unregister stuff */
free_irq(pci_dev->irq, dev);
- pci_disable_msi(pci_dev);
cx23885_dev_unregister(dev);
v4l2_device_unregister(v4l2_dev);
@@ -2152,14 +2213,8 @@ static struct pci_driver cx23885_pci_driver = {
static int __init cx23885_init(void)
{
- printk(KERN_INFO "cx23885 driver version %d.%d.%d loaded\n",
- (CX23885_VERSION_CODE >> 16) & 0xff,
- (CX23885_VERSION_CODE >> 8) & 0xff,
- CX23885_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO "cx23885: snapshot date %04d-%02d-%02d\n",
- SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+ printk(KERN_INFO "cx23885 driver version %s loaded\n",
+ CX23885_VERSION);
return pci_register_driver(&cx23885_pci_driver);
}
@@ -2170,5 +2225,3 @@ static void __exit cx23885_fini(void)
module_init(cx23885_init);
module_exit(cx23885_fini);
-
-/* ----------------------------------------------------------- */
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 3c315f94cc85..bcb45be44bb2 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -37,6 +37,7 @@
#include "tda8290.h"
#include "tda18271.h"
#include "lgdt330x.h"
+#include "xc4000.h"
#include "xc5000.h"
#include "max2165.h"
#include "tda10048.h"
@@ -843,7 +844,7 @@ static int dvb_register(struct cx23885_tsport *port)
static struct xc2028_ctrl ctl = {
.fname = XC3028L_DEFAULT_FIRMWARE,
.max_len = 64,
- .demod = 5000,
+ .demod = XC3028_FE_DIBCOM52,
/* This is true for all demods with
v36 firmware? */
.type = XC2028_D2633,
@@ -921,6 +922,26 @@ static int dvb_register(struct cx23885_tsport *port)
fe->ops.tuner_ops.set_config(fe, &ctl);
}
break;
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ i2c_bus = &dev->i2c_bus[0];
+
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc4000_config cfg = {
+ .i2c_address = 0x61,
+ .default_pm = 0,
+ .dvb_amplitude = 134,
+ .set_smoothedcvbs = 1,
+ .if_khz = 4560
+ };
+
+ fe = dvb_attach(xc4000_attach, fe0->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, &cfg);
+ }
+ break;
case CX23885_BOARD_TBS_6920:
i2c_bus = &dev->i2c_bus[1];
@@ -1249,7 +1270,7 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port)
* implement MFE support.
*/
fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
- if (fe0->dvb.frontend)
+ if (fe0 && fe0->dvb.frontend)
videobuf_dvb_unregister_bus(&port->frontends);
switch (port->dev->board) {
diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c
index 307ff543c254..0ff7a9e98f3e 100644
--- a/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/drivers/media/video/cx23885/cx23885-i2c.c
@@ -287,6 +287,7 @@ static char *i2c_devs[128] = {
[0x32 >> 1] = "cx24227",
[0x88 >> 1] = "cx25837",
[0x84 >> 1] = "tda8295",
+ [0x98 >> 1] = "flatiron",
[0xa0 >> 1] = "eeprom",
[0xc0 >> 1] = "tuner/mt2131/tda8275",
[0xc2 >> 1] = "tuner/mt2131/tda8275/xc5000/xc3028",
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
index e97cafd83984..ce765e3f77bd 100644
--- a/drivers/media/video/cx23885/cx23885-input.c
+++ b/drivers/media/video/cx23885/cx23885-input.c
@@ -82,6 +82,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
return;
switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_TEVII_S470:
@@ -133,6 +134,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
@@ -229,6 +231,9 @@ static void cx23885_input_ir_stop(struct cx23885_dev *dev)
v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
}
+ flush_work_sync(&dev->cx25840_work);
+ flush_work_sync(&dev->ir_rx_work);
+ flush_work_sync(&dev->ir_tx_work);
}
static void cx23885_input_ir_close(struct rc_dev *rc)
@@ -257,6 +262,7 @@ int cx23885_input_init(struct cx23885_dev *dev)
return -ENODEV;
switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h
index c87ac682ebbe..a99936e0cbc2 100644
--- a/drivers/media/video/cx23885/cx23885-reg.h
+++ b/drivers/media/video/cx23885/cx23885-reg.h
@@ -203,6 +203,7 @@ Channel manager Data Structure entry = 20 DWORD
#define SD2_BIAS_CTRL 0x0000000A
#define AMP_BIAS_CTRL 0x0000000C
#define CH_PWR_CTRL1 0x0000000E
+#define FLD_CH_SEL (1 << 3)
#define CH_PWR_CTRL2 0x0000000F
#define DSM_STATUS1 0x00000010
#define DSM_STATUS2 0x00000011
@@ -271,7 +272,9 @@ Channel manager Data Structure entry = 20 DWORD
#define VID_BC_MSK_OPC_ERR (1 << 16)
#define VID_BC_MSK_SYNC (1 << 12)
#define VID_BC_MSK_OF (1 << 8)
+#define VID_BC_MSK_VBI_RISCI2 (1 << 5)
#define VID_BC_MSK_RISCI2 (1 << 4)
+#define VID_BC_MSK_VBI_RISCI1 (1 << 1)
#define VID_BC_MSK_RISCI1 1
#define VID_C_INT_MSK 0x00040040
diff --git a/drivers/media/video/cx23885/cx23885-vbi.c b/drivers/media/video/cx23885/cx23885-vbi.c
index c0b60382ad13..a1154f035bc1 100644
--- a/drivers/media/video/cx23885/cx23885-vbi.c
+++ b/drivers/media/video/cx23885/cx23885-vbi.c
@@ -41,6 +41,12 @@ MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
/* ------------------------------------------------------------------ */
+#define VBI_LINE_LENGTH 1440
+#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */
+#define NTSC_VBI_END_LINE 21
+#define NTSC_VBI_LINES (NTSC_VBI_END_LINE - NTSC_VBI_START_LINE + 1)
+
+
int cx23885_vbi_fmt(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -49,43 +55,86 @@ int cx23885_vbi_fmt(struct file *file, void *priv,
if (dev->tvnorm & V4L2_STD_525_60) {
/* ntsc */
- f->fmt.vbi.sampling_rate = 28636363;
+ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+ f->fmt.vbi.sampling_rate = 27000000;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 0;
+ f->fmt.vbi.flags = 0;
f->fmt.vbi.start[0] = 10;
- f->fmt.vbi.start[1] = 273;
-
+ f->fmt.vbi.count[0] = 17;
+ f->fmt.vbi.start[1] = 263 + 10 + 1;
+ f->fmt.vbi.count[1] = 17;
} else if (dev->tvnorm & V4L2_STD_625_50) {
/* pal */
f->fmt.vbi.sampling_rate = 35468950;
f->fmt.vbi.start[0] = 7 - 1;
f->fmt.vbi.start[1] = 319 - 1;
}
+
return 0;
}
+/* We're given the Video Interrupt status register.
+ * The cx23885_video_irq() func has already validated
+ * the potential error bits, we just need to
+ * deal with vbi payload and return indication if
+ * we actually processed any payload.
+ */
+int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status)
+{
+ u32 count;
+ int handled = 0;
+
+ if (status & VID_BC_MSK_VBI_RISCI1) {
+ dprintk(1, "%s() VID_BC_MSK_VBI_RISCI1\n", __func__);
+ spin_lock(&dev->slock);
+ count = cx_read(VID_A_GPCNT);
+ cx23885_video_wakeup(dev, &dev->vbiq, count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ if (status & VID_BC_MSK_VBI_RISCI2) {
+ dprintk(1, "%s() VID_BC_MSK_VBI_RISCI2\n", __func__);
+ dprintk(2, "stopper vbi\n");
+ spin_lock(&dev->slock);
+ cx23885_restart_vbi_queue(dev, &dev->vbiq);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ return handled;
+}
+
static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
struct cx23885_dmaqueue *q,
struct cx23885_buffer *buf)
{
+ dprintk(1, "%s()\n", __func__);
+
/* setup fifo + format */
cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02],
buf->vb.width, buf->risc.dma);
/* reset counter */
+ cx_write(VID_A_GPCNT_CTL, 3);
+ cx_write(VID_A_VBI_CTRL, 3);
+ cx_write(VBI_A_GPCNT_CTL, 3);
q->count = 1;
- /* enable irqs */
+ /* enable irq */
cx23885_irq_add_enable(dev, 0x01);
cx_set(VID_A_INT_MSK, 0x000022);
/* start dma */
cx_set(DEV_CNTRL2, (1<<5));
- cx_set(VID_A_DMA_CTL, 0x00000022);
+ cx_set(VID_A_DMA_CTL, 0x22); /* FIFO and RISC enable */
return 0;
}
-static int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
struct cx23885_dmaqueue *q)
{
struct cx23885_buffer *buf;
@@ -102,7 +151,7 @@ static int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
buf = list_entry(item, struct cx23885_buffer, vb.queue);
buf->count = q->count++;
}
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30));
return 0;
}
@@ -113,8 +162,7 @@ void cx23885_vbi_timeout(unsigned long data)
struct cx23885_buffer *buf;
unsigned long flags;
- cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH02]);
-
+ /* Stop the VBI engine */
cx_clear(VID_A_DMA_CTL, 0x22);
spin_lock_irqsave(&dev->slock, flags);
@@ -132,7 +180,7 @@ void cx23885_vbi_timeout(unsigned long data)
}
/* ------------------------------------------------------------------ */
-#define VBI_LINE_LENGTH 2048
+#define VBI_LINE_LENGTH 1440
#define VBI_LINE_COUNT 17
static int
@@ -173,7 +221,7 @@ vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
rc = videobuf_iolock(q, &buf->vb, NULL);
if (0 != rc)
goto fail;
- cx23885_risc_buffer(dev->pci, &buf->risc,
+ cx23885_risc_vbibuffer(dev->pci, &buf->risc,
dma->sglist,
0, buf->vb.width * buf->vb.height,
buf->vb.width, 0,
@@ -207,7 +255,7 @@ vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
cx23885_start_vbi_dma(dev, q, buf);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30));
dprintk(2, "[%p/%d] vbi_queue - first active\n",
buf, buf->vb.i);
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index ee57f6bedbe3..e730b9263016 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -37,6 +37,8 @@
#include "cx23885-ioctl.h"
#include "tuner-xc2028.h"
+#include <media/cx25840.h>
+
MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
MODULE_LICENSE("GPL");
@@ -69,14 +71,14 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
#define dprintk(level, fmt, arg...)\
do { if (video_debug >= level)\
- printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
} while (0)
/* ------------------------------------------------------------------- */
/* static data */
#define FORMAT_FLAGS_PACKED 0x01
-
+#if 0
static struct cx23885_fmt formats[] = {
{
.name = "8 bpp, gray",
@@ -130,6 +132,23 @@ static struct cx23885_fmt formats[] = {
.flags = FORMAT_FLAGS_PACKED,
},
};
+#else
+static struct cx23885_fmt formats[] = {
+ {
+#if 0
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+#endif
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }
+};
+#endif
static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
{
@@ -139,7 +158,12 @@ static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
if (formats[i].fourcc == fourcc)
return formats+i;
- printk(KERN_ERR "%s(0x%08x) NOT FOUND\n", __func__, fourcc);
+ printk(KERN_ERR "%s(%c%c%c%c) NOT FOUND\n", __func__,
+ (fourcc & 0xff),
+ ((fourcc >> 8) & 0xff),
+ ((fourcc >> 16) & 0xff),
+ ((fourcc >> 24) & 0xff)
+ );
return NULL;
}
@@ -171,7 +195,7 @@ static struct cx23885_ctrl cx23885_ctls[] = {
.id = V4L2_CID_CONTRAST,
.name = "Contrast",
.minimum = 0,
- .maximum = 0xff,
+ .maximum = 0x7f,
.step = 1,
.default_value = 0x3f,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -184,10 +208,10 @@ static struct cx23885_ctrl cx23885_ctls[] = {
.v = {
.id = V4L2_CID_HUE,
.name = "Hue",
- .minimum = 0,
- .maximum = 0xff,
+ .minimum = -127,
+ .maximum = 128,
.step = 1,
- .default_value = 0x7f,
+ .default_value = 0x0,
.type = V4L2_CTRL_TYPE_INTEGER,
},
.off = 128,
@@ -202,9 +226,9 @@ static struct cx23885_ctrl cx23885_ctls[] = {
.id = V4L2_CID_SATURATION,
.name = "Saturation",
.minimum = 0,
- .maximum = 0xff,
+ .maximum = 0x7f,
.step = 1,
- .default_value = 0x7f,
+ .default_value = 0x3f,
.type = V4L2_CTRL_TYPE_INTEGER,
},
.off = 0,
@@ -258,8 +282,8 @@ static const u32 *ctrl_classes[] = {
NULL
};
-static void cx23885_video_wakeup(struct cx23885_dev *dev,
- struct cx23885_dmaqueue *q, u32 count)
+void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count)
{
struct cx23885_buffer *buf;
int bc;
@@ -393,6 +417,71 @@ static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
mutex_unlock(&dev->lock);
}
+static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
+{
+ /* 8 bit registers, 8 bit values */
+ u8 buf[] = { reg, data };
+
+ struct i2c_msg msg = { .addr = 0x98 >> 1,
+ .flags = 0, .buf = buf, .len = 2 };
+
+ return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
+}
+
+static u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg)
+{
+ /* 8 bit registers, 8 bit values */
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+
+ struct i2c_msg msg[] = {
+ { .addr = 0x98 >> 1, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = 0x98 >> 1, .flags = I2C_M_RD, .buf = b1, .len = 1 }
+ };
+
+ ret = i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg[0], 2);
+ if (ret != 2)
+ printk(KERN_ERR "%s() error\n", __func__);
+
+ return b1[0];
+}
+
+static void cx23885_flatiron_dump(struct cx23885_dev *dev)
+{
+ int i;
+ dprintk(1, "Flatiron dump\n");
+ for (i = 0; i < 0x24; i++) {
+ dprintk(1, "FI[%02x] = %02x\n", i,
+ cx23885_flatiron_read(dev, i));
+ }
+}
+
+static int cx23885_flatiron_mux(struct cx23885_dev *dev, int input)
+{
+ u8 val;
+ dprintk(1, "%s(input = %d)\n", __func__, input);
+
+ if (input == 1)
+ val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) & ~FLD_CH_SEL;
+ else if (input == 2)
+ val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) | FLD_CH_SEL;
+ else
+ return -EINVAL;
+
+ val |= 0x20; /* Enable clock to delta-sigma and dec filter */
+
+ cx23885_flatiron_write(dev, CH_PWR_CTRL1, val);
+
+ /* Wake up */
+ cx23885_flatiron_write(dev, CH_PWR_CTRL2, 0);
+
+ if (video_debug)
+ cx23885_flatiron_dump(dev);
+
+ return 0;
+}
+
static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
{
dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
@@ -413,27 +502,59 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
v4l2_subdev_call(dev->sd_cx25840, video, s_routing,
INPUT(input)->vmux, 0, 0);
+ if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) ||
+ (dev->board == CX23885_BOARD_MPX885)) {
+ /* Configure audio routing */
+ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing,
+ INPUT(input)->amux, 0, 0);
+
+ if (INPUT(input)->amux == CX25840_AUDIO7)
+ cx23885_flatiron_mux(dev, 1);
+ else if (INPUT(input)->amux == CX25840_AUDIO6)
+ cx23885_flatiron_mux(dev, 2);
+ }
+
return 0;
}
-/* ------------------------------------------------------------------ */
-static int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width,
- unsigned int height, enum v4l2_field field)
+static int cx23885_audio_mux(struct cx23885_dev *dev, unsigned int input)
{
- dprintk(1, "%s()\n", __func__);
+ dprintk(1, "%s(input=%d)\n", __func__, input);
+
+ /* The baseband video core of the cx23885 has two audio inputs.
+ * LR1 and LR2. In almost every single case so far only HVR1xxx
+ * cards we've only ever supported LR1. Time to support LR2,
+ * which is available via the optional white breakout header on
+ * the board.
+ * We'll use a could of existing enums in the card struct to allow
+ * devs to specify which baseband input they need, or just default
+ * to what we've always used.
+ */
+ if (INPUT(input)->amux == CX25840_AUDIO7)
+ cx23885_flatiron_mux(dev, 1);
+ else if (INPUT(input)->amux == CX25840_AUDIO6)
+ cx23885_flatiron_mux(dev, 2);
+ else {
+ /* Not specifically defined, assume the default. */
+ cx23885_flatiron_mux(dev, 1);
+ }
+
return 0;
}
+/* ------------------------------------------------------------------ */
static int cx23885_start_video_dma(struct cx23885_dev *dev,
struct cx23885_dmaqueue *q,
struct cx23885_buffer *buf)
{
dprintk(1, "%s()\n", __func__);
+ /* Stop the dma/fifo before we tamper with it's risc programs */
+ cx_clear(VID_A_DMA_CTL, 0x11);
+
/* setup fifo + format */
cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
buf->bpl, buf->risc.dma);
- cx23885_set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field);
/* reset counter */
cx_write(VID_A_GPCNT_CTL, 3);
@@ -748,7 +869,7 @@ static int video_open(struct file *file)
fh->type = type;
fh->width = 320;
fh->height = 240;
- fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops,
&dev->pci->dev, &dev->slock,
@@ -757,6 +878,14 @@ static int video_open(struct file *file)
sizeof(struct cx23885_buffer),
fh, NULL);
+ videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct cx23885_buffer),
+ fh, NULL);
+
+
dprintk(1, "post videobuf_queue_init()\n");
return 0;
@@ -884,8 +1013,9 @@ static int cx23885_get_control(struct cx23885_dev *dev,
static int cx23885_set_control(struct cx23885_dev *dev,
struct v4l2_control *ctl)
{
- dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)"
- " (disabled - no action)\n", __func__);
+ dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__);
+ call_all(dev, core, s_ctrl, ctl);
+
return 0;
}
@@ -1000,7 +1130,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->card, cx23885_boards[dev->board].name,
sizeof(cap->card));
sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
- cap->version = CX23885_VERSION_CODE;
cap->capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
@@ -1060,13 +1189,22 @@ static int vidioc_streamon(struct file *file, void *priv,
struct cx23885_dev *dev = fh->dev;
dprintk(1, "%s()\n", __func__);
- if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
return -EINVAL;
if (unlikely(i != fh->type))
return -EINVAL;
if (unlikely(!res_get(dev, fh, get_resource(fh))))
return -EBUSY;
+
+ /* Don't start VBI streaming unless vida streaming
+ * has already started.
+ */
+ if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) &&
+ ((cx_read(VID_A_DMA_CTL) & 0x11) == 0))
+ return -EINVAL;
+
return videobuf_streamon(get_queue(fh));
}
@@ -1077,7 +1215,8 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
int err, res;
dprintk(1, "%s()\n", __func__);
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
return -EINVAL;
if (i != fh->type)
return -EINVAL;
@@ -1120,7 +1259,7 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
dprintk(1, "%s()\n", __func__);
n = i->index;
- if (n >= 4)
+ if (n >= MAX_CX23885_INPUT)
return -EINVAL;
if (0 == INPUT(n)->type)
@@ -1134,6 +1273,11 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
i->type = V4L2_INPUT_TYPE_TUNER;
i->std = CX23885_NORMS;
}
+
+ /* Two selectable audio inputs for non-tv inputs */
+ if (INPUT(n)->type != CX23885_VMUX_TELEVISION)
+ i->audioset = 0x3;
+
return 0;
}
@@ -1160,13 +1304,20 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
dprintk(1, "%s(%d)\n", __func__, i);
- if (i >= 4) {
+ if (i >= MAX_CX23885_INPUT) {
dprintk(1, "%s() -EINVAL\n", __func__);
return -EINVAL;
}
+ if (INPUT(i)->type == 0)
+ return -EINVAL;
+
mutex_lock(&dev->lock);
cx23885_video_mux(dev, i);
+
+ /* By default establish the default audio input for the card also */
+ /* Caller is free to use VIDIOC_S_AUDIO to override afterwards */
+ cx23885_audio_mux(dev, i);
mutex_unlock(&dev->lock);
return 0;
}
@@ -1186,6 +1337,64 @@ static int vidioc_log_status(struct file *file, void *priv)
return 0;
}
+static int cx23885_query_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ static const char *iname[] = {
+ [0] = "Baseband L/R 1",
+ [1] = "Baseband L/R 2",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __func__);
+
+ n = i->index;
+ if (n >= 2)
+ return -EINVAL;
+
+ memset(i, 0, sizeof(*i));
+ i->index = n;
+ strcpy(i->name, iname[n]);
+ i->capability = V4L2_AUDCAP_STEREO;
+ i->mode = V4L2_AUDMODE_AVL;
+ return 0;
+
+}
+
+static int vidioc_enum_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ return cx23885_query_audinput(file, priv, i);
+}
+
+static int vidioc_g_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ i->index = dev->audinput;
+ dprintk(1, "%s(input=%d)\n", __func__, i->index);
+
+ return cx23885_query_audinput(file, priv, i);
+}
+
+static int vidioc_s_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ if (i->index >= 2)
+ return -EINVAL;
+
+ dprintk(1, "%s(%d)\n", __func__, i->index);
+
+ dev->audinput = i->index;
+
+ /* Skip the audio defaults from the cards struct, caller wants
+ * directly touch the audio mux hardware. */
+ cx23885_flatiron_mux(dev, dev->audinput + 1);
+ return 0;
+}
+
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qctrl)
{
@@ -1222,10 +1431,8 @@ static int vidioc_g_tuner(struct file *file, void *priv,
return -EINVAL;
strcpy(t->name, "Television");
- t->type = V4L2_TUNER_ANALOG_TV;
- t->capability = V4L2_TUNER_CAP_NORM;
- t->rangehigh = 0xffffffffUL;
- t->signal = 0xffff ; /* LOCKED */
+
+ call_all(dev, tuner, g_tuner, t);
return 0;
}
@@ -1238,6 +1445,9 @@ static int vidioc_s_tuner(struct file *file, void *priv,
return -EINVAL;
if (0 != t->index)
return -EINVAL;
+ /* Update the A/V core */
+ call_all(dev, tuner, s_tuner, t);
+
return 0;
}
@@ -1303,10 +1513,6 @@ static void cx23885_vid_timeout(unsigned long data)
struct cx23885_buffer *buf;
unsigned long flags;
- cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]);
-
- cx_clear(VID_A_DMA_CTL, 0x11);
-
spin_lock_irqsave(&dev->slock, flags);
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next,
@@ -1314,7 +1520,7 @@ static void cx23885_vid_timeout(unsigned long data)
list_del(&buf->vb.queue);
buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
- printk(KERN_ERR "%s/0: [%p/%d] timeout - dma=0x%08lx\n",
+ printk(KERN_ERR "%s: [%p/%d] timeout - dma=0x%08lx\n",
dev->name, buf, buf->vb.i,
(unsigned long)buf->risc.dma);
}
@@ -1330,27 +1536,43 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
mask = cx_read(VID_A_INT_MSK);
if (0 == (status & mask))
return handled;
+
cx_write(VID_A_INT_STAT, status);
- dprintk(2, "%s() status = 0x%08x\n", __func__, status);
- /* risc op code error */
- if (status & (1 << 16)) {
- printk(KERN_WARNING "%s/0: video risc op code error\n",
- dev->name);
- cx_clear(VID_A_DMA_CTL, 0x11);
- cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]);
+ /* risc op code error, fifo overflow or line sync detection error */
+ if ((status & VID_BC_MSK_OPC_ERR) ||
+ (status & VID_BC_MSK_SYNC) ||
+ (status & VID_BC_MSK_OF)) {
+
+ if (status & VID_BC_MSK_OPC_ERR) {
+ dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n",
+ VID_BC_MSK_OPC_ERR);
+ printk(KERN_WARNING "%s: video risc op code error\n",
+ dev->name);
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[SRAM_CH01]);
+ }
+
+ if (status & VID_BC_MSK_SYNC)
+ dprintk(7, " (VID_BC_MSK_SYNC 0x%08x) "
+ "video lines miss-match\n",
+ VID_BC_MSK_SYNC);
+
+ if (status & VID_BC_MSK_OF)
+ dprintk(7, " (VID_BC_MSK_OF 0x%08x) fifo overflow\n",
+ VID_BC_MSK_OF);
+
}
- /* risc1 y */
- if (status & 0x01) {
+ /* Video */
+ if (status & VID_BC_MSK_RISCI1) {
spin_lock(&dev->slock);
count = cx_read(VID_A_GPCNT);
cx23885_video_wakeup(dev, &dev->vidq, count);
spin_unlock(&dev->slock);
handled++;
}
- /* risc2 y */
- if (status & 0x10) {
+ if (status & VID_BC_MSK_RISCI2) {
dprintk(2, "stopper video\n");
spin_lock(&dev->slock);
cx23885_restart_video_queue(dev, &dev->vidq);
@@ -1358,6 +1580,9 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
handled++;
}
+ /* Allow the VBI framework to process it's payload */
+ handled += cx23885_vbi_irq(dev, status);
+
return handled;
}
@@ -1406,6 +1631,9 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_g_register = cx23885_g_register,
.vidioc_s_register = cx23885_s_register,
#endif
+ .vidioc_enumaudio = vidioc_enum_audinput,
+ .vidioc_g_audio = vidioc_g_audinput,
+ .vidioc_s_audio = vidioc_s_audinput,
};
static struct video_device cx23885_vbi_template;
@@ -1430,6 +1658,14 @@ void cx23885_video_unregister(struct cx23885_dev *dev)
dprintk(1, "%s()\n", __func__);
cx23885_irq_remove(dev, 0x01);
+ if (dev->vbi_dev) {
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ btcx_riscmem_free(dev->pci, &dev->vbiq.stopper);
+ }
if (dev->video_dev) {
if (video_is_registered(dev->video_dev))
video_unregister_device(dev->video_dev);
@@ -1439,6 +1675,9 @@ void cx23885_video_unregister(struct cx23885_dev *dev)
btcx_riscmem_free(dev->pci, &dev->vidq.stopper);
}
+
+ if (dev->audio_dev)
+ cx23885_audio_unregister(dev);
}
int cx23885_video_register(struct cx23885_dev *dev)
@@ -1464,7 +1703,14 @@ int cx23885_video_register(struct cx23885_dev *dev)
cx23885_risc_stopper(dev->pci, &dev->vidq.stopper,
VID_A_DMA_CTL, 0x11, 0x00);
- /* Don't enable VBI yet */
+ /* init vbi dma queues */
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
+ dev->vbiq.timeout.function = cx23885_vbi_timeout;
+ dev->vbiq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vbiq.timeout);
+ cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper,
+ VID_A_DMA_CTL, 0x22, 0x00);
cx23885_irq_add_enable(dev, 0x01);
@@ -1505,8 +1751,7 @@ int cx23885_video_register(struct cx23885_dev *dev)
}
}
-
- /* register v4l devices */
+ /* register Video device */
dev->video_dev = cx23885_vdev_init(dev, dev->pci,
&cx23885_video_template, "video");
err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER,
@@ -1516,13 +1761,31 @@ int cx23885_video_register(struct cx23885_dev *dev)
dev->name);
goto fail_unreg;
}
- printk(KERN_INFO "%s/0: registered device %s [v4l2]\n",
+ printk(KERN_INFO "%s: registered device %s [v4l2]\n",
dev->name, video_device_node_name(dev->video_dev));
+
+ /* register VBI device */
+ dev->vbi_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_vbi_template, "vbi");
+ err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register vbi device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->vbi_dev));
+
+ /* Register ALSA audio device */
+ dev->audio_dev = cx23885_audio_register(dev);
+
/* initial device configuration */
mutex_lock(&dev->lock);
cx23885_set_tvnorm(dev, dev->tvnorm);
init_controls(dev);
cx23885_video_mux(dev, 0);
+ cx23885_audio_mux(dev, 0);
mutex_unlock(&dev->lock);
return 0;
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index c186473fc570..b49036fe3ffd 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -36,10 +36,9 @@
#include "cx23885-reg.h"
#include "media/cx2341x.h"
-#include <linux/version.h>
#include <linux/mutex.h>
-#define CX23885_VERSION_CODE KERNEL_VERSION(0, 0, 2)
+#define CX23885_VERSION "0.0.3"
#define UNSET (-1U)
@@ -86,6 +85,8 @@
#define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28
#define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID 29
#define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30
+#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000 31
+#define CX23885_BOARD_MPX885 32
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
@@ -192,6 +193,7 @@ struct cx23885_buffer {
struct cx23885_input {
enum cx23885_itype type;
unsigned int vmux;
+ unsigned int amux;
u32 gpio0, gpio1, gpio2, gpio3;
};
@@ -318,6 +320,34 @@ struct cx23885_kernel_ir {
struct rc_dev *rc;
};
+struct cx23885_audio_buffer {
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ struct videobuf_dmabuf dma;
+};
+
+struct cx23885_audio_dev {
+ struct cx23885_dev *dev;
+
+ struct pci_dev *pci;
+
+ struct snd_card *card;
+
+ spinlock_t lock;
+
+ atomic_t count;
+
+ unsigned int dma_size;
+ unsigned int period_size;
+ unsigned int num_periods;
+
+ struct videobuf_dmabuf *dma_risc;
+
+ struct cx23885_audio_buffer *buf;
+
+ struct snd_pcm_substream *substream;
+};
+
struct cx23885_dev {
atomic_t refcount;
struct v4l2_device v4l2_dev;
@@ -362,6 +392,7 @@ struct cx23885_dev {
/* Analog video */
u32 resources;
unsigned int input;
+ unsigned int audinput; /* Selectable audio input */
u32 tvaudio;
v4l2_std_id tvnorm;
unsigned int tuner_type;
@@ -400,6 +431,9 @@ struct cx23885_dev {
atomic_t v4l_reader_count;
struct cx23885_tvnorm encodernorm;
+ /* Analog raw audio */
+ struct cx23885_audio_dev *audio_dev;
+
};
static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
@@ -478,6 +512,11 @@ extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
unsigned int top_offset, unsigned int bottom_offset,
unsigned int bpl, unsigned int padding, unsigned int lines);
+extern int cx23885_risc_vbibuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc, struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+
void cx23885_cancel_buffers(struct cx23885_tsport *port);
extern int cx23885_restart_queue(struct cx23885_tsport *port,
@@ -533,6 +572,8 @@ extern void cx23885_free_buffer(struct videobuf_queue *q,
extern int cx23885_video_register(struct cx23885_dev *dev);
extern void cx23885_video_unregister(struct cx23885_dev *dev);
extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status);
+extern void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count);
/* ----------------------------------------------------------- */
/* cx23885-vbi.c */
@@ -540,6 +581,9 @@ extern int cx23885_vbi_fmt(struct file *file, void *priv,
struct v4l2_format *f);
extern void cx23885_vbi_timeout(unsigned long data);
extern struct videobuf_queue_ops cx23885_vbi_qops;
+extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q);
+extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status);
/* cx23885-i2c.c */
extern int cx23885_i2c_register(struct cx23885_i2c *bus);
@@ -563,6 +607,18 @@ extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask);
extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask);
extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput);
+/* ----------------------------------------------------------- */
+/* cx23885-alsa.c */
+extern struct cx23885_audio_dev *cx23885_audio_register(
+ struct cx23885_dev *dev);
+extern void cx23885_audio_unregister(struct cx23885_dev *dev);
+extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask);
+extern int cx23885_risc_databuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int bpl,
+ unsigned int lines,
+ unsigned int lpi);
/* ----------------------------------------------------------- */
/* tv norms */
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c
index e37be6fcf67d..bb1ce346425d 100644
--- a/drivers/media/video/cx23885/cx23888-ir.c
+++ b/drivers/media/video/cx23885/cx23888-ir.c
@@ -673,7 +673,7 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
unsigned int i, n;
union cx23888_ir_fifo_rec *p;
- unsigned u, v;
+ unsigned u, v, w;
n = count / sizeof(union cx23888_ir_fifo_rec)
* sizeof(union cx23888_ir_fifo_rec);
@@ -692,11 +692,12 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
/* Assume RTO was because of no IR light input */
u = 0;
- v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
+ w = 1;
} else {
u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
if (invert)
u = u ? 0 : 1;
+ w = 0;
}
v = (unsigned) pulse_width_count_to_ns(
@@ -707,9 +708,12 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
init_ir_raw_event(&p->ir_core_data);
p->ir_core_data.pulse = u;
p->ir_core_data.duration = v;
+ p->ir_core_data.timeout = w;
- v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n",
- v, u ? "mark" : "space");
+ v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s %s\n",
+ v, u ? "mark" : "space", w ? "(timed out)" : "");
+ if (w)
+ v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
}
return 0;
}
diff --git a/drivers/staging/cx25821/Kconfig b/drivers/media/video/cx25821/Kconfig
index 5f6b54213713..5f6b54213713 100644
--- a/drivers/staging/cx25821/Kconfig
+++ b/drivers/media/video/cx25821/Kconfig
diff --git a/drivers/staging/cx25821/Makefile b/drivers/media/video/cx25821/Makefile
index aedde18c68f9..aedde18c68f9 100644
--- a/drivers/staging/cx25821/Makefile
+++ b/drivers/media/video/cx25821/Makefile
diff --git a/drivers/staging/cx25821/cx25821-alsa.c b/drivers/media/video/cx25821/cx25821-alsa.c
index ebdba7c65bc5..09e99de5fd21 100644
--- a/drivers/staging/cx25821/cx25821-alsa.c
+++ b/drivers/media/video/cx25821/cx25821-alsa.c
@@ -708,7 +708,7 @@ static int cx25821_audio_initdev(struct cx25821_dev *dev)
chip->irq = dev->pci->irq;
err = request_irq(dev->pci->irq, cx25821_irq,
- IRQF_SHARED | IRQF_DISABLED, chip->dev->name, chip);
+ IRQF_SHARED, chip->dev->name, chip);
if (err < 0) {
pr_err("ERROR %s: can't get IRQ %d for ALSA\n",
diff --git a/drivers/staging/cx25821/cx25821-audio-upstream.c b/drivers/media/video/cx25821/cx25821-audio-upstream.c
index 0f9ca777bd4d..c20d6dece154 100644
--- a/drivers/staging/cx25821/cx25821-audio-upstream.c
+++ b/drivers/media/video/cx25821/cx25821-audio-upstream.c
@@ -107,7 +107,7 @@ static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev,
{
unsigned int line;
struct sram_channel *sram_ch =
- dev->channels[dev->_audio_upstream_channel_select].sram_channels;
+ dev->channels[dev->_audio_upstream_channel].sram_channels;
int offset = 0;
/* scan lines */
@@ -349,10 +349,8 @@ static void cx25821_audioups_handler(struct work_struct *work)
return;
}
- cx25821_get_audio_data(dev,
- dev->channels[dev->
- _audio_upstream_channel_select].
- sram_channels);
+ cx25821_get_audio_data(dev, dev->channels[dev->_audio_upstream_channel].
+ sram_channels);
}
int cx25821_openfile_audio(struct cx25821_dev *dev,
@@ -540,13 +538,9 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num,
AUDIO_RISC_DMA_BUF_SIZE;
rp = cx25821_risc_field_upstream_audio(dev,
- dev->
- _risc_virt_start_addr
- + 1,
- dev->
- _audiodata_buf_phys_addr,
- AUDIO_LINE_SIZE,
- FIFO_DISABLE);
+ dev->_risc_virt_start_addr + 1,
+ dev->_audiodata_buf_phys_addr,
+ AUDIO_LINE_SIZE, FIFO_DISABLE);
if (USE_RISC_NOOP_AUDIO) {
for (i = 0; i < NUM_NO_OPS; i++) {
@@ -555,8 +549,7 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num,
}
}
/* Jump to 2nd Audio Frame */
- *(rp++) =
- cpu_to_le32(RISC_JUMP | RISC_IRQ1 |
+ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 |
RISC_CNT_RESET);
*(rp++) = cpu_to_le32(risc_phys_jump_addr);
*(rp++) = cpu_to_le32(0);
@@ -604,18 +597,15 @@ static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id)
if (!dev)
return -1;
- sram_ch = dev->channels[dev->_audio_upstream_channel_select].sram_channels;
+ sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels;
msk_stat = cx_read(sram_ch->int_mstat);
audio_status = cx_read(sram_ch->int_stat);
/* Only deal with our interrupt */
if (audio_status) {
- handled =
- cx25821_audio_upstream_irq(dev,
- dev->
- _audio_upstream_channel_select,
- audio_status);
+ handled = cx25821_audio_upstream_irq(dev,
+ dev->_audio_upstream_channel, audio_status);
}
if (handled < 0)
@@ -690,7 +680,7 @@ int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev,
err =
request_irq(dev->pci->irq, cx25821_upstream_irq_audio,
- IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+ IRQF_SHARED, dev->name, dev);
if (err < 0) {
pr_err("%s: can't get upstream IRQ %d\n",
dev->name, dev->pci->irq);
@@ -726,7 +716,7 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select)
return 0;
}
- dev->_audio_upstream_channel_select = channel_select;
+ dev->_audio_upstream_channel = channel_select;
sram_ch = dev->channels[channel_select].sram_channels;
/* Work queue */
@@ -770,9 +760,8 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select)
memcpy(dev->_audiofilename, _defaultAudioName, str_length + 1);
}
- retval =
- cx25821_sram_channel_setup_upstream_audio(dev, sram_ch, _line_size,
- 0);
+ retval = cx25821_sram_channel_setup_upstream_audio(dev, sram_ch,
+ _line_size, 0);
dev->audio_upstream_riscbuf_size =
AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS +
@@ -780,8 +769,8 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select)
dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS;
/* Allocating buffers and prepare RISC program */
- retval =
- cx25821_audio_upstream_buffer_prepare(dev, sram_ch, _line_size);
+ retval = cx25821_audio_upstream_buffer_prepare(dev, sram_ch,
+ _line_size);
if (retval < 0) {
pr_err("%s: Failed to set up Audio upstream buffers!\n",
dev->name);
diff --git a/drivers/staging/cx25821/cx25821-audio-upstream.h b/drivers/media/video/cx25821/cx25821-audio-upstream.h
index 668a4f11e80b..af2ae7c5815a 100644
--- a/drivers/staging/cx25821/cx25821-audio-upstream.h
+++ b/drivers/media/video/cx25821/cx25821-audio-upstream.h
@@ -46,11 +46,16 @@
#define USE_RISC_NOOP_AUDIO 1
#ifdef USE_RISC_NOOP_AUDIO
-#define AUDIO_RISC_DMA_BUF_SIZE (LINES_PER_AUDIO_BUFFER*RISC_READ_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
+#define AUDIO_RISC_DMA_BUF_SIZE \
+ (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS * DWORD_SIZE + \
+ RISC_JUMP_INSTRUCTION_SIZE)
#endif
#ifndef USE_RISC_NOOP_AUDIO
-#define AUDIO_RISC_DMA_BUF_SIZE (LINES_PER_AUDIO_BUFFER*RISC_READ_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
+#define AUDIO_RISC_DMA_BUF_SIZE \
+ (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
#endif
static int _line_size;
diff --git a/drivers/staging/cx25821/cx25821-audio.h b/drivers/media/video/cx25821/cx25821-audio.h
index 27717253227d..8eb55b7b88cb 100644
--- a/drivers/staging/cx25821/cx25821-audio.h
+++ b/drivers/media/video/cx25821/cx25821-audio.h
@@ -36,13 +36,15 @@
*/
#ifndef USE_RISC_NOOP
#define MAX_BUFFER_PROGRAM_SIZE \
- (2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE*4)
+ (2 * LINES_PER_BUFFER * RISC_WRITE_INSTRUCTION_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE * 4)
#endif
/* MAE 12 July 2005 Try to use NOOP RISC instruction instead */
#ifdef USE_RISC_NOOP
#define MAX_BUFFER_PROGRAM_SIZE \
- (2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_NOOP_INSTRUCTION_SIZE*4)
+ (2 * LINES_PER_BUFFER * RISC_WRITE_INSTRUCTION_SIZE + \
+ RISC_NOOP_INSTRUCTION_SIZE * 4)
#endif
/* Sizes of various instructions in bytes. Used when adding instructions. */
diff --git a/drivers/staging/cx25821/cx25821-biffuncs.h b/drivers/media/video/cx25821/cx25821-biffuncs.h
index 9326a7c729ec..9326a7c729ec 100644
--- a/drivers/staging/cx25821/cx25821-biffuncs.h
+++ b/drivers/media/video/cx25821/cx25821-biffuncs.h
diff --git a/drivers/staging/cx25821/cx25821-cards.c b/drivers/media/video/cx25821/cx25821-cards.c
index 94e8d685c889..6ace60313b49 100644
--- a/drivers/staging/cx25821/cx25821-cards.c
+++ b/drivers/media/video/cx25821/cx25821-cards.c
@@ -36,17 +36,17 @@
struct cx25821_board cx25821_boards[] = {
[UNKNOWN_BOARD] = {
- .name = "UNKNOWN/GENERIC",
- /* Ensure safe default for unknown boards */
- .clk_freq = 0,
- },
+ .name = "UNKNOWN/GENERIC",
+ /* Ensure safe default for unknown boards */
+ .clk_freq = 0,
+ },
[CX25821_BOARD] = {
- .name = "CX25821",
- .portb = CX25821_RAW,
- .portc = CX25821_264,
- .input[0].type = CX25821_VMUX_COMPOSITE,
- },
+ .name = "CX25821",
+ .portb = CX25821_RAW,
+ .portc = CX25821_264,
+ .input[0].type = CX25821_VMUX_COMPOSITE,
+ },
};
@@ -54,10 +54,10 @@ const unsigned int cx25821_bcount = ARRAY_SIZE(cx25821_boards);
struct cx25821_subid cx25821_subids[] = {
{
- .subvendor = 0x14f1,
- .subdevice = 0x0920,
- .card = CX25821_BOARD,
- },
+ .subvendor = 0x14f1,
+ .subdevice = 0x0920,
+ .card = CX25821_BOARD,
+ },
};
void cx25821_card_setup(struct cx25821_dev *dev)
diff --git a/drivers/staging/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c
index 523ac5e16c1b..a7fa38f9594e 100644
--- a/drivers/staging/cx25821/cx25821-core.c
+++ b/drivers/media/video/cx25821/cx25821-core.c
@@ -50,270 +50,270 @@ EXPORT_SYMBOL(cx25821_devlist);
struct sram_channel cx25821_sram_channels[] = {
[SRAM_CH00] = {
- .i = SRAM_CH00,
- .name = "VID A",
- .cmds_start = VID_A_DOWN_CMDS,
- .ctrl_start = VID_A_IQ,
- .cdt = VID_A_CDT,
- .fifo_start = VID_A_DOWN_CLUSTER_1,
- .fifo_size = (VID_CLUSTER_SIZE << 2),
- .ptr1_reg = DMA1_PTR1,
- .ptr2_reg = DMA1_PTR2,
- .cnt1_reg = DMA1_CNT1,
- .cnt2_reg = DMA1_CNT2,
- .int_msk = VID_A_INT_MSK,
- .int_stat = VID_A_INT_STAT,
- .int_mstat = VID_A_INT_MSTAT,
- .dma_ctl = VID_DST_A_DMA_CTL,
- .gpcnt_ctl = VID_DST_A_GPCNT_CTL,
- .gpcnt = VID_DST_A_GPCNT,
- .vip_ctl = VID_DST_A_VIP_CTL,
- .pix_frmt = VID_DST_A_PIX_FRMT,
- },
+ .i = SRAM_CH00,
+ .name = "VID A",
+ .cmds_start = VID_A_DOWN_CMDS,
+ .ctrl_start = VID_A_IQ,
+ .cdt = VID_A_CDT,
+ .fifo_start = VID_A_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA1_PTR1,
+ .ptr2_reg = DMA1_PTR2,
+ .cnt1_reg = DMA1_CNT1,
+ .cnt2_reg = DMA1_CNT2,
+ .int_msk = VID_A_INT_MSK,
+ .int_stat = VID_A_INT_STAT,
+ .int_mstat = VID_A_INT_MSTAT,
+ .dma_ctl = VID_DST_A_DMA_CTL,
+ .gpcnt_ctl = VID_DST_A_GPCNT_CTL,
+ .gpcnt = VID_DST_A_GPCNT,
+ .vip_ctl = VID_DST_A_VIP_CTL,
+ .pix_frmt = VID_DST_A_PIX_FRMT,
+ },
[SRAM_CH01] = {
- .i = SRAM_CH01,
- .name = "VID B",
- .cmds_start = VID_B_DOWN_CMDS,
- .ctrl_start = VID_B_IQ,
- .cdt = VID_B_CDT,
- .fifo_start = VID_B_DOWN_CLUSTER_1,
- .fifo_size = (VID_CLUSTER_SIZE << 2),
- .ptr1_reg = DMA2_PTR1,
- .ptr2_reg = DMA2_PTR2,
- .cnt1_reg = DMA2_CNT1,
- .cnt2_reg = DMA2_CNT2,
- .int_msk = VID_B_INT_MSK,
- .int_stat = VID_B_INT_STAT,
- .int_mstat = VID_B_INT_MSTAT,
- .dma_ctl = VID_DST_B_DMA_CTL,
- .gpcnt_ctl = VID_DST_B_GPCNT_CTL,
- .gpcnt = VID_DST_B_GPCNT,
- .vip_ctl = VID_DST_B_VIP_CTL,
- .pix_frmt = VID_DST_B_PIX_FRMT,
- },
+ .i = SRAM_CH01,
+ .name = "VID B",
+ .cmds_start = VID_B_DOWN_CMDS,
+ .ctrl_start = VID_B_IQ,
+ .cdt = VID_B_CDT,
+ .fifo_start = VID_B_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA2_PTR1,
+ .ptr2_reg = DMA2_PTR2,
+ .cnt1_reg = DMA2_CNT1,
+ .cnt2_reg = DMA2_CNT2,
+ .int_msk = VID_B_INT_MSK,
+ .int_stat = VID_B_INT_STAT,
+ .int_mstat = VID_B_INT_MSTAT,
+ .dma_ctl = VID_DST_B_DMA_CTL,
+ .gpcnt_ctl = VID_DST_B_GPCNT_CTL,
+ .gpcnt = VID_DST_B_GPCNT,
+ .vip_ctl = VID_DST_B_VIP_CTL,
+ .pix_frmt = VID_DST_B_PIX_FRMT,
+ },
[SRAM_CH02] = {
- .i = SRAM_CH02,
- .name = "VID C",
- .cmds_start = VID_C_DOWN_CMDS,
- .ctrl_start = VID_C_IQ,
- .cdt = VID_C_CDT,
- .fifo_start = VID_C_DOWN_CLUSTER_1,
- .fifo_size = (VID_CLUSTER_SIZE << 2),
- .ptr1_reg = DMA3_PTR1,
- .ptr2_reg = DMA3_PTR2,
- .cnt1_reg = DMA3_CNT1,
- .cnt2_reg = DMA3_CNT2,
- .int_msk = VID_C_INT_MSK,
- .int_stat = VID_C_INT_STAT,
- .int_mstat = VID_C_INT_MSTAT,
- .dma_ctl = VID_DST_C_DMA_CTL,
- .gpcnt_ctl = VID_DST_C_GPCNT_CTL,
- .gpcnt = VID_DST_C_GPCNT,
- .vip_ctl = VID_DST_C_VIP_CTL,
- .pix_frmt = VID_DST_C_PIX_FRMT,
- },
+ .i = SRAM_CH02,
+ .name = "VID C",
+ .cmds_start = VID_C_DOWN_CMDS,
+ .ctrl_start = VID_C_IQ,
+ .cdt = VID_C_CDT,
+ .fifo_start = VID_C_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA3_PTR1,
+ .ptr2_reg = DMA3_PTR2,
+ .cnt1_reg = DMA3_CNT1,
+ .cnt2_reg = DMA3_CNT2,
+ .int_msk = VID_C_INT_MSK,
+ .int_stat = VID_C_INT_STAT,
+ .int_mstat = VID_C_INT_MSTAT,
+ .dma_ctl = VID_DST_C_DMA_CTL,
+ .gpcnt_ctl = VID_DST_C_GPCNT_CTL,
+ .gpcnt = VID_DST_C_GPCNT,
+ .vip_ctl = VID_DST_C_VIP_CTL,
+ .pix_frmt = VID_DST_C_PIX_FRMT,
+ },
[SRAM_CH03] = {
- .i = SRAM_CH03,
- .name = "VID D",
- .cmds_start = VID_D_DOWN_CMDS,
- .ctrl_start = VID_D_IQ,
- .cdt = VID_D_CDT,
- .fifo_start = VID_D_DOWN_CLUSTER_1,
- .fifo_size = (VID_CLUSTER_SIZE << 2),
- .ptr1_reg = DMA4_PTR1,
- .ptr2_reg = DMA4_PTR2,
- .cnt1_reg = DMA4_CNT1,
- .cnt2_reg = DMA4_CNT2,
- .int_msk = VID_D_INT_MSK,
- .int_stat = VID_D_INT_STAT,
- .int_mstat = VID_D_INT_MSTAT,
- .dma_ctl = VID_DST_D_DMA_CTL,
- .gpcnt_ctl = VID_DST_D_GPCNT_CTL,
- .gpcnt = VID_DST_D_GPCNT,
- .vip_ctl = VID_DST_D_VIP_CTL,
- .pix_frmt = VID_DST_D_PIX_FRMT,
- },
+ .i = SRAM_CH03,
+ .name = "VID D",
+ .cmds_start = VID_D_DOWN_CMDS,
+ .ctrl_start = VID_D_IQ,
+ .cdt = VID_D_CDT,
+ .fifo_start = VID_D_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA4_PTR1,
+ .ptr2_reg = DMA4_PTR2,
+ .cnt1_reg = DMA4_CNT1,
+ .cnt2_reg = DMA4_CNT2,
+ .int_msk = VID_D_INT_MSK,
+ .int_stat = VID_D_INT_STAT,
+ .int_mstat = VID_D_INT_MSTAT,
+ .dma_ctl = VID_DST_D_DMA_CTL,
+ .gpcnt_ctl = VID_DST_D_GPCNT_CTL,
+ .gpcnt = VID_DST_D_GPCNT,
+ .vip_ctl = VID_DST_D_VIP_CTL,
+ .pix_frmt = VID_DST_D_PIX_FRMT,
+ },
[SRAM_CH04] = {
- .i = SRAM_CH04,
- .name = "VID E",
- .cmds_start = VID_E_DOWN_CMDS,
- .ctrl_start = VID_E_IQ,
- .cdt = VID_E_CDT,
- .fifo_start = VID_E_DOWN_CLUSTER_1,
- .fifo_size = (VID_CLUSTER_SIZE << 2),
- .ptr1_reg = DMA5_PTR1,
- .ptr2_reg = DMA5_PTR2,
- .cnt1_reg = DMA5_CNT1,
- .cnt2_reg = DMA5_CNT2,
- .int_msk = VID_E_INT_MSK,
- .int_stat = VID_E_INT_STAT,
- .int_mstat = VID_E_INT_MSTAT,
- .dma_ctl = VID_DST_E_DMA_CTL,
- .gpcnt_ctl = VID_DST_E_GPCNT_CTL,
- .gpcnt = VID_DST_E_GPCNT,
- .vip_ctl = VID_DST_E_VIP_CTL,
- .pix_frmt = VID_DST_E_PIX_FRMT,
- },
+ .i = SRAM_CH04,
+ .name = "VID E",
+ .cmds_start = VID_E_DOWN_CMDS,
+ .ctrl_start = VID_E_IQ,
+ .cdt = VID_E_CDT,
+ .fifo_start = VID_E_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA5_PTR1,
+ .ptr2_reg = DMA5_PTR2,
+ .cnt1_reg = DMA5_CNT1,
+ .cnt2_reg = DMA5_CNT2,
+ .int_msk = VID_E_INT_MSK,
+ .int_stat = VID_E_INT_STAT,
+ .int_mstat = VID_E_INT_MSTAT,
+ .dma_ctl = VID_DST_E_DMA_CTL,
+ .gpcnt_ctl = VID_DST_E_GPCNT_CTL,
+ .gpcnt = VID_DST_E_GPCNT,
+ .vip_ctl = VID_DST_E_VIP_CTL,
+ .pix_frmt = VID_DST_E_PIX_FRMT,
+ },
[SRAM_CH05] = {
- .i = SRAM_CH05,
- .name = "VID F",
- .cmds_start = VID_F_DOWN_CMDS,
- .ctrl_start = VID_F_IQ,
- .cdt = VID_F_CDT,
- .fifo_start = VID_F_DOWN_CLUSTER_1,
- .fifo_size = (VID_CLUSTER_SIZE << 2),
- .ptr1_reg = DMA6_PTR1,
- .ptr2_reg = DMA6_PTR2,
- .cnt1_reg = DMA6_CNT1,
- .cnt2_reg = DMA6_CNT2,
- .int_msk = VID_F_INT_MSK,
- .int_stat = VID_F_INT_STAT,
- .int_mstat = VID_F_INT_MSTAT,
- .dma_ctl = VID_DST_F_DMA_CTL,
- .gpcnt_ctl = VID_DST_F_GPCNT_CTL,
- .gpcnt = VID_DST_F_GPCNT,
- .vip_ctl = VID_DST_F_VIP_CTL,
- .pix_frmt = VID_DST_F_PIX_FRMT,
- },
+ .i = SRAM_CH05,
+ .name = "VID F",
+ .cmds_start = VID_F_DOWN_CMDS,
+ .ctrl_start = VID_F_IQ,
+ .cdt = VID_F_CDT,
+ .fifo_start = VID_F_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA6_PTR1,
+ .ptr2_reg = DMA6_PTR2,
+ .cnt1_reg = DMA6_CNT1,
+ .cnt2_reg = DMA6_CNT2,
+ .int_msk = VID_F_INT_MSK,
+ .int_stat = VID_F_INT_STAT,
+ .int_mstat = VID_F_INT_MSTAT,
+ .dma_ctl = VID_DST_F_DMA_CTL,
+ .gpcnt_ctl = VID_DST_F_GPCNT_CTL,
+ .gpcnt = VID_DST_F_GPCNT,
+ .vip_ctl = VID_DST_F_VIP_CTL,
+ .pix_frmt = VID_DST_F_PIX_FRMT,
+ },
[SRAM_CH06] = {
- .i = SRAM_CH06,
- .name = "VID G",
- .cmds_start = VID_G_DOWN_CMDS,
- .ctrl_start = VID_G_IQ,
- .cdt = VID_G_CDT,
- .fifo_start = VID_G_DOWN_CLUSTER_1,
- .fifo_size = (VID_CLUSTER_SIZE << 2),
- .ptr1_reg = DMA7_PTR1,
- .ptr2_reg = DMA7_PTR2,
- .cnt1_reg = DMA7_CNT1,
- .cnt2_reg = DMA7_CNT2,
- .int_msk = VID_G_INT_MSK,
- .int_stat = VID_G_INT_STAT,
- .int_mstat = VID_G_INT_MSTAT,
- .dma_ctl = VID_DST_G_DMA_CTL,
- .gpcnt_ctl = VID_DST_G_GPCNT_CTL,
- .gpcnt = VID_DST_G_GPCNT,
- .vip_ctl = VID_DST_G_VIP_CTL,
- .pix_frmt = VID_DST_G_PIX_FRMT,
- },
+ .i = SRAM_CH06,
+ .name = "VID G",
+ .cmds_start = VID_G_DOWN_CMDS,
+ .ctrl_start = VID_G_IQ,
+ .cdt = VID_G_CDT,
+ .fifo_start = VID_G_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA7_PTR1,
+ .ptr2_reg = DMA7_PTR2,
+ .cnt1_reg = DMA7_CNT1,
+ .cnt2_reg = DMA7_CNT2,
+ .int_msk = VID_G_INT_MSK,
+ .int_stat = VID_G_INT_STAT,
+ .int_mstat = VID_G_INT_MSTAT,
+ .dma_ctl = VID_DST_G_DMA_CTL,
+ .gpcnt_ctl = VID_DST_G_GPCNT_CTL,
+ .gpcnt = VID_DST_G_GPCNT,
+ .vip_ctl = VID_DST_G_VIP_CTL,
+ .pix_frmt = VID_DST_G_PIX_FRMT,
+ },
[SRAM_CH07] = {
- .i = SRAM_CH07,
- .name = "VID H",
- .cmds_start = VID_H_DOWN_CMDS,
- .ctrl_start = VID_H_IQ,
- .cdt = VID_H_CDT,
- .fifo_start = VID_H_DOWN_CLUSTER_1,
- .fifo_size = (VID_CLUSTER_SIZE << 2),
- .ptr1_reg = DMA8_PTR1,
- .ptr2_reg = DMA8_PTR2,
- .cnt1_reg = DMA8_CNT1,
- .cnt2_reg = DMA8_CNT2,
- .int_msk = VID_H_INT_MSK,
- .int_stat = VID_H_INT_STAT,
- .int_mstat = VID_H_INT_MSTAT,
- .dma_ctl = VID_DST_H_DMA_CTL,
- .gpcnt_ctl = VID_DST_H_GPCNT_CTL,
- .gpcnt = VID_DST_H_GPCNT,
- .vip_ctl = VID_DST_H_VIP_CTL,
- .pix_frmt = VID_DST_H_PIX_FRMT,
- },
+ .i = SRAM_CH07,
+ .name = "VID H",
+ .cmds_start = VID_H_DOWN_CMDS,
+ .ctrl_start = VID_H_IQ,
+ .cdt = VID_H_CDT,
+ .fifo_start = VID_H_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA8_PTR1,
+ .ptr2_reg = DMA8_PTR2,
+ .cnt1_reg = DMA8_CNT1,
+ .cnt2_reg = DMA8_CNT2,
+ .int_msk = VID_H_INT_MSK,
+ .int_stat = VID_H_INT_STAT,
+ .int_mstat = VID_H_INT_MSTAT,
+ .dma_ctl = VID_DST_H_DMA_CTL,
+ .gpcnt_ctl = VID_DST_H_GPCNT_CTL,
+ .gpcnt = VID_DST_H_GPCNT,
+ .vip_ctl = VID_DST_H_VIP_CTL,
+ .pix_frmt = VID_DST_H_PIX_FRMT,
+ },
[SRAM_CH08] = {
- .name = "audio from",
- .cmds_start = AUD_A_DOWN_CMDS,
- .ctrl_start = AUD_A_IQ,
- .cdt = AUD_A_CDT,
- .fifo_start = AUD_A_DOWN_CLUSTER_1,
- .fifo_size = AUDIO_CLUSTER_SIZE * 3,
- .ptr1_reg = DMA17_PTR1,
- .ptr2_reg = DMA17_PTR2,
- .cnt1_reg = DMA17_CNT1,
- .cnt2_reg = DMA17_CNT2,
- },
+ .name = "audio from",
+ .cmds_start = AUD_A_DOWN_CMDS,
+ .ctrl_start = AUD_A_IQ,
+ .cdt = AUD_A_CDT,
+ .fifo_start = AUD_A_DOWN_CLUSTER_1,
+ .fifo_size = AUDIO_CLUSTER_SIZE * 3,
+ .ptr1_reg = DMA17_PTR1,
+ .ptr2_reg = DMA17_PTR2,
+ .cnt1_reg = DMA17_CNT1,
+ .cnt2_reg = DMA17_CNT2,
+ },
[SRAM_CH09] = {
- .i = SRAM_CH09,
- .name = "VID Upstream I",
- .cmds_start = VID_I_UP_CMDS,
- .ctrl_start = VID_I_IQ,
- .cdt = VID_I_CDT,
- .fifo_start = VID_I_UP_CLUSTER_1,
- .fifo_size = (VID_CLUSTER_SIZE << 2),
- .ptr1_reg = DMA15_PTR1,
- .ptr2_reg = DMA15_PTR2,
- .cnt1_reg = DMA15_CNT1,
- .cnt2_reg = DMA15_CNT2,
- .int_msk = VID_I_INT_MSK,
- .int_stat = VID_I_INT_STAT,
- .int_mstat = VID_I_INT_MSTAT,
- .dma_ctl = VID_SRC_I_DMA_CTL,
- .gpcnt_ctl = VID_SRC_I_GPCNT_CTL,
- .gpcnt = VID_SRC_I_GPCNT,
-
- .vid_fmt_ctl = VID_SRC_I_FMT_CTL,
- .vid_active_ctl1 = VID_SRC_I_ACTIVE_CTL1,
- .vid_active_ctl2 = VID_SRC_I_ACTIVE_CTL2,
- .vid_cdt_size = VID_SRC_I_CDT_SZ,
- .irq_bit = 8,
- },
+ .i = SRAM_CH09,
+ .name = "VID Upstream I",
+ .cmds_start = VID_I_UP_CMDS,
+ .ctrl_start = VID_I_IQ,
+ .cdt = VID_I_CDT,
+ .fifo_start = VID_I_UP_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA15_PTR1,
+ .ptr2_reg = DMA15_PTR2,
+ .cnt1_reg = DMA15_CNT1,
+ .cnt2_reg = DMA15_CNT2,
+ .int_msk = VID_I_INT_MSK,
+ .int_stat = VID_I_INT_STAT,
+ .int_mstat = VID_I_INT_MSTAT,
+ .dma_ctl = VID_SRC_I_DMA_CTL,
+ .gpcnt_ctl = VID_SRC_I_GPCNT_CTL,
+ .gpcnt = VID_SRC_I_GPCNT,
+
+ .vid_fmt_ctl = VID_SRC_I_FMT_CTL,
+ .vid_active_ctl1 = VID_SRC_I_ACTIVE_CTL1,
+ .vid_active_ctl2 = VID_SRC_I_ACTIVE_CTL2,
+ .vid_cdt_size = VID_SRC_I_CDT_SZ,
+ .irq_bit = 8,
+ },
[SRAM_CH10] = {
- .i = SRAM_CH10,
- .name = "VID Upstream J",
- .cmds_start = VID_J_UP_CMDS,
- .ctrl_start = VID_J_IQ,
- .cdt = VID_J_CDT,
- .fifo_start = VID_J_UP_CLUSTER_1,
- .fifo_size = (VID_CLUSTER_SIZE << 2),
- .ptr1_reg = DMA16_PTR1,
- .ptr2_reg = DMA16_PTR2,
- .cnt1_reg = DMA16_CNT1,
- .cnt2_reg = DMA16_CNT2,
- .int_msk = VID_J_INT_MSK,
- .int_stat = VID_J_INT_STAT,
- .int_mstat = VID_J_INT_MSTAT,
- .dma_ctl = VID_SRC_J_DMA_CTL,
- .gpcnt_ctl = VID_SRC_J_GPCNT_CTL,
- .gpcnt = VID_SRC_J_GPCNT,
-
- .vid_fmt_ctl = VID_SRC_J_FMT_CTL,
- .vid_active_ctl1 = VID_SRC_J_ACTIVE_CTL1,
- .vid_active_ctl2 = VID_SRC_J_ACTIVE_CTL2,
- .vid_cdt_size = VID_SRC_J_CDT_SZ,
- .irq_bit = 9,
- },
+ .i = SRAM_CH10,
+ .name = "VID Upstream J",
+ .cmds_start = VID_J_UP_CMDS,
+ .ctrl_start = VID_J_IQ,
+ .cdt = VID_J_CDT,
+ .fifo_start = VID_J_UP_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA16_PTR1,
+ .ptr2_reg = DMA16_PTR2,
+ .cnt1_reg = DMA16_CNT1,
+ .cnt2_reg = DMA16_CNT2,
+ .int_msk = VID_J_INT_MSK,
+ .int_stat = VID_J_INT_STAT,
+ .int_mstat = VID_J_INT_MSTAT,
+ .dma_ctl = VID_SRC_J_DMA_CTL,
+ .gpcnt_ctl = VID_SRC_J_GPCNT_CTL,
+ .gpcnt = VID_SRC_J_GPCNT,
+
+ .vid_fmt_ctl = VID_SRC_J_FMT_CTL,
+ .vid_active_ctl1 = VID_SRC_J_ACTIVE_CTL1,
+ .vid_active_ctl2 = VID_SRC_J_ACTIVE_CTL2,
+ .vid_cdt_size = VID_SRC_J_CDT_SZ,
+ .irq_bit = 9,
+ },
[SRAM_CH11] = {
- .i = SRAM_CH11,
- .name = "Audio Upstream Channel B",
- .cmds_start = AUD_B_UP_CMDS,
- .ctrl_start = AUD_B_IQ,
- .cdt = AUD_B_CDT,
- .fifo_start = AUD_B_UP_CLUSTER_1,
- .fifo_size = (AUDIO_CLUSTER_SIZE * 3),
- .ptr1_reg = DMA22_PTR1,
- .ptr2_reg = DMA22_PTR2,
- .cnt1_reg = DMA22_CNT1,
- .cnt2_reg = DMA22_CNT2,
- .int_msk = AUD_B_INT_MSK,
- .int_stat = AUD_B_INT_STAT,
- .int_mstat = AUD_B_INT_MSTAT,
- .dma_ctl = AUD_INT_DMA_CTL,
- .gpcnt_ctl = AUD_B_GPCNT_CTL,
- .gpcnt = AUD_B_GPCNT,
- .aud_length = AUD_B_LNGTH,
- .aud_cfg = AUD_B_CFG,
- .fld_aud_fifo_en = FLD_AUD_SRC_B_FIFO_EN,
- .fld_aud_risc_en = FLD_AUD_SRC_B_RISC_EN,
- .irq_bit = 11,
- },
+ .i = SRAM_CH11,
+ .name = "Audio Upstream Channel B",
+ .cmds_start = AUD_B_UP_CMDS,
+ .ctrl_start = AUD_B_IQ,
+ .cdt = AUD_B_CDT,
+ .fifo_start = AUD_B_UP_CLUSTER_1,
+ .fifo_size = (AUDIO_CLUSTER_SIZE * 3),
+ .ptr1_reg = DMA22_PTR1,
+ .ptr2_reg = DMA22_PTR2,
+ .cnt1_reg = DMA22_CNT1,
+ .cnt2_reg = DMA22_CNT2,
+ .int_msk = AUD_B_INT_MSK,
+ .int_stat = AUD_B_INT_STAT,
+ .int_mstat = AUD_B_INT_MSTAT,
+ .dma_ctl = AUD_INT_DMA_CTL,
+ .gpcnt_ctl = AUD_B_GPCNT_CTL,
+ .gpcnt = AUD_B_GPCNT,
+ .aud_length = AUD_B_LNGTH,
+ .aud_cfg = AUD_B_CFG,
+ .fld_aud_fifo_en = FLD_AUD_SRC_B_FIFO_EN,
+ .fld_aud_risc_en = FLD_AUD_SRC_B_RISC_EN,
+ .irq_bit = 11,
+ },
};
EXPORT_SYMBOL(cx25821_sram_channels);
@@ -1428,7 +1428,7 @@ static int __devinit cx25821_initdev(struct pci_dev *pci_dev,
}
err =
- request_irq(pci_dev->irq, cx25821_irq, IRQF_SHARED | IRQF_DISABLED,
+ request_irq(pci_dev->irq, cx25821_irq, IRQF_SHARED,
dev->name, dev);
if (err < 0) {
@@ -1473,17 +1473,17 @@ static void __devexit cx25821_finidev(struct pci_dev *pci_dev)
kfree(dev);
}
-static struct pci_device_id cx25821_pci_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(cx25821_pci_tbl) = {
{
- /* CX25821 Athena */
- .vendor = 0x14f1,
- .device = 0x8210,
- .subvendor = 0x14f1,
- .subdevice = 0x0920,
- },
+ /* CX25821 Athena */
+ .vendor = 0x14f1,
+ .device = 0x8210,
+ .subvendor = 0x14f1,
+ .subdevice = 0x0920,
+ },
{
- /* --- end of list --- */
- }
+ /* --- end of list --- */
+ }
};
MODULE_DEVICE_TABLE(pci, cx25821_pci_tbl);
@@ -1512,7 +1512,6 @@ static void __exit cx25821_fini(void)
pci_unregister_driver(&cx25821_pci_driver);
}
-EXPORT_SYMBOL(cx25821_set_gpiopin_direction);
module_init(cx25821_init);
module_exit(cx25821_fini);
diff --git a/drivers/staging/cx25821/cx25821-gpio.c b/drivers/media/video/cx25821/cx25821-gpio.c
index 2f154b3658a1..29e43b03c85e 100644
--- a/drivers/staging/cx25821/cx25821-gpio.c
+++ b/drivers/media/video/cx25821/cx25821-gpio.c
@@ -50,6 +50,7 @@ void cx25821_set_gpiopin_direction(struct cx25821_dev *dev,
cx_write(gpio_oe_reg, value);
}
+EXPORT_SYMBOL(cx25821_set_gpiopin_direction);
static void cx25821_set_gpiopin_logicvalue(struct cx25821_dev *dev,
int pin_number, int pin_logic_value)
diff --git a/drivers/staging/cx25821/cx25821-i2c.c b/drivers/media/video/cx25821/cx25821-i2c.c
index 130dfebebe20..4d3d0ce40785 100644
--- a/drivers/staging/cx25821/cx25821-i2c.c
+++ b/drivers/media/video/cx25821/cx25821-i2c.c
@@ -370,16 +370,16 @@ int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value)
struct i2c_msg msgs[2] = {
{
- .addr = client->addr,
- .flags = 0,
- .len = 2,
- .buf = addr,
- }, {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = 4,
- .buf = buf,
- }
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = addr,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 4,
+ .buf = buf,
+ }
};
addr[0] = (reg_addr >> 8);
@@ -403,11 +403,11 @@ int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value)
struct i2c_msg msgs[1] = {
{
- .addr = client->addr,
- .flags = 0,
- .len = 6,
- .buf = buf,
- }
+ .addr = client->addr,
+ .flags = 0,
+ .len = 6,
+ .buf = buf,
+ }
};
buf[0] = reg_addr >> 8;
diff --git a/drivers/staging/cx25821/cx25821-medusa-defines.h b/drivers/media/video/cx25821/cx25821-medusa-defines.h
index 60d197f57556..60d197f57556 100644
--- a/drivers/staging/cx25821/cx25821-medusa-defines.h
+++ b/drivers/media/video/cx25821/cx25821-medusa-defines.h
diff --git a/drivers/staging/cx25821/cx25821-medusa-reg.h b/drivers/media/video/cx25821/cx25821-medusa-reg.h
index 1c1c228352d1..1c1c228352d1 100644
--- a/drivers/staging/cx25821/cx25821-medusa-reg.h
+++ b/drivers/media/video/cx25821/cx25821-medusa-reg.h
diff --git a/drivers/staging/cx25821/cx25821-medusa-video.c b/drivers/media/video/cx25821/cx25821-medusa-video.c
index fc780d0908dc..fc780d0908dc 100644
--- a/drivers/staging/cx25821/cx25821-medusa-video.c
+++ b/drivers/media/video/cx25821/cx25821-medusa-video.c
diff --git a/drivers/staging/cx25821/cx25821-medusa-video.h b/drivers/media/video/cx25821/cx25821-medusa-video.h
index 6175e0961855..6175e0961855 100644
--- a/drivers/staging/cx25821/cx25821-medusa-video.h
+++ b/drivers/media/video/cx25821/cx25821-medusa-video.h
diff --git a/drivers/staging/cx25821/cx25821-reg.h b/drivers/media/video/cx25821/cx25821-reg.h
index a3fc25a4dc0b..a3fc25a4dc0b 100644
--- a/drivers/staging/cx25821/cx25821-reg.h
+++ b/drivers/media/video/cx25821/cx25821-reg.h
diff --git a/drivers/staging/cx25821/cx25821-sram.h b/drivers/media/video/cx25821/cx25821-sram.h
index 5f05d153bc4d..5f05d153bc4d 100644
--- a/drivers/staging/cx25821/cx25821-sram.h
+++ b/drivers/media/video/cx25821/cx25821-sram.h
diff --git a/drivers/staging/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
index 655357da3d6a..2a724ddfa53f 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream-ch2.c
+++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
@@ -40,8 +40,8 @@ MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
MODULE_LICENSE("GPL");
-static int _intr_msk =
- FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR;
+static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC |
+ FLD_VID_SRC_OPC_ERR;
static __le32 *cx25821_update_riscprogram_ch2(struct cx25821_dev *dev,
__le32 *rp, unsigned int offset,
@@ -145,15 +145,16 @@ int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev,
if (dev->_isNTSC_ch2) {
odd_num_lines = singlefield_lines + 1;
risc_program_size = FRAME1_VID_PROG_SIZE;
- frame_size =
- (bpl ==
- Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 :
- FRAME_SIZE_NTSC_Y422;
+ if (bpl == Y411_LINE_SZ)
+ frame_size = FRAME_SIZE_NTSC_Y411;
+ else
+ frame_size = FRAME_SIZE_NTSC_Y422;
} else {
risc_program_size = PAL_VID_PROG_SIZE;
- frame_size =
- (bpl ==
- Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
+ if (bpl == Y411_LINE_SZ)
+ frame_size = FRAME_SIZE_PAL_Y411;
+ else
+ frame_size = FRAME_SIZE_PAL_Y422;
}
/* Virtual address of Risc buffer program */
@@ -165,30 +166,23 @@ int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev,
if (UNSET != top_offset) {
fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE;
rp = cx25821_risc_field_upstream_ch2(dev, rp,
- dev->
- _data_buf_phys_addr_ch2
- + databuf_offset,
- top_offset, 0, bpl,
- odd_num_lines,
- fifo_enable,
- ODD_FIELD);
+ dev->_data_buf_phys_addr_ch2 + databuf_offset,
+ top_offset, 0, bpl, odd_num_lines, fifo_enable,
+ ODD_FIELD);
}
fifo_enable = FIFO_DISABLE;
/* Even field */
rp = cx25821_risc_field_upstream_ch2(dev, rp,
- dev->
- _data_buf_phys_addr_ch2 +
- databuf_offset,
- bottom_offset, 0x200, bpl,
- singlefield_lines,
- fifo_enable, EVEN_FIELD);
+ dev->_data_buf_phys_addr_ch2 + databuf_offset,
+ bottom_offset, 0x200, bpl, singlefield_lines,
+ fifo_enable, EVEN_FIELD);
if (frame == 0) {
risc_flag = RISC_CNT_RESET;
- risc_phys_jump_addr =
- dev->_dma_phys_start_addr_ch2 + risc_program_size;
+ risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2 +
+ risc_program_size;
} else {
risc_flag = RISC_CNT_INC;
risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2;
@@ -510,9 +504,8 @@ static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev,
return ret;
/* Creating RISC programs */
- ret =
- cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl,
- dev->_lines_count_ch2);
+ ret = cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl,
+ dev->_lines_count_ch2);
if (ret < 0) {
pr_info("Failed creating Video Upstream Risc programs!\n");
goto error;
@@ -520,7 +513,7 @@ static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev,
return 0;
- error:
+error:
return ret;
}
@@ -565,23 +558,19 @@ int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num,
}
if (dev->_dma_virt_start_addr_ch2 != NULL) {
- line_size_in_bytes =
- (dev->_pixel_format_ch2 ==
- PIXEL_FRMT_411) ? Y411_LINE_SZ :
- Y422_LINE_SZ;
+ if (dev->_pixel_format_ch2 == PIXEL_FRMT_411)
+ line_size_in_bytes = Y411_LINE_SZ;
+ else
+ line_size_in_bytes = Y422_LINE_SZ;
risc_phys_jump_addr =
dev->_dma_phys_start_addr_ch2 +
odd_risc_prog_size;
rp = cx25821_update_riscprogram_ch2(dev,
- dev->
- _dma_virt_start_addr_ch2,
- TOP_OFFSET,
- line_size_in_bytes,
- 0x0,
- singlefield_lines,
- FIFO_DISABLE,
- ODD_FIELD);
+ dev->_dma_virt_start_addr_ch2,
+ TOP_OFFSET, line_size_in_bytes,
+ 0x0, singlefield_lines,
+ FIFO_DISABLE, ODD_FIELD);
/* Jump to Even Risc program of 1st Frame */
*(rp++) = cpu_to_le32(RISC_JUMP);
@@ -704,7 +693,7 @@ int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev,
err =
request_irq(dev->pci->irq, cx25821_upstream_irq_ch2,
- IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+ IRQF_SHARED, dev->name, dev);
if (err < 0) {
pr_err("%s: can't get upstream IRQ %d\n",
dev->name, dev->pci->irq);
@@ -719,7 +708,7 @@ int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev,
return 0;
- fail_irq:
+fail_irq:
cx25821_dev_unregister(dev);
return err;
}
@@ -805,8 +794,7 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select,
}
}
- retval =
- cx25821_sram_channel_setup_upstream(dev, sram_ch,
+ retval = cx25821_sram_channel_setup_upstream(dev, sram_ch,
dev->_line_size_ch2, 0);
/* setup fifo + format */
@@ -816,8 +804,7 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select,
dev->upstream_databuf_size_ch2 = data_frame_size * 2;
/* Allocating buffers and prepare RISC program */
- retval =
- cx25821_upstream_buffer_prepare_ch2(dev, sram_ch,
+ retval = cx25821_upstream_buffer_prepare_ch2(dev, sram_ch,
dev->_line_size_ch2);
if (retval < 0) {
pr_err("%s: Failed to set up Video upstream buffers!\n",
@@ -829,7 +816,7 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select,
return 0;
- error:
+error:
cx25821_dev_unregister(dev);
return err;
diff --git a/drivers/staging/cx25821/cx25821-video-upstream-ch2.h b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h
index 029e8305724b..d42dab59b663 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream-ch2.h
+++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h
@@ -65,37 +65,74 @@
#define USE_RISC_NOOP_VIDEO 1
#ifdef USE_RISC_NOOP_VIDEO
-#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
- RISC_SYNC_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
+#define PAL_US_VID_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE)
-#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE)
+#define PAL_VID_PROG_SIZE \
+ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE)
-#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
+#define ODD_FLD_PAL_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
-#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
- JUMP_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
+#define NTSC_US_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
-#define NTSC_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+#define NTSC_RISC_BUF_SIZE \
+ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
-#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE)
-#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
+#define FRAME1_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * \
+ 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \
+ 2 * NUM_NO_OPS * DWORD_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE \
+ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
#endif
#ifndef USE_RISC_NOOP_VIDEO
-#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
-#define PAL_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE))
-#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
-#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
-#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
-#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
-#define NTSC_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
-#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+#define PAL_US_VID_PROG_SIZE \
+ ((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define PAL_RISC_BUF_SIZE \
+ (2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE))
+
+#define PAL_VID_PROG_SIZE \
+ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE)
+
+#define ODD_FLD_PAL_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE \
+ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define NTSC_US_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+
+#define NTSC_RISC_BUF_SIZE \
+ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+
+#define FRAME1_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * \
+ 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+
#endif
diff --git a/drivers/staging/cx25821/cx25821-video-upstream.c b/drivers/media/video/cx25821/cx25821-video-upstream.c
index eb0172bf39d1..c0b80068f468 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream.c
+++ b/drivers/media/video/cx25821/cx25821-video-upstream.c
@@ -40,8 +40,8 @@ MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
MODULE_LICENSE("GPL");
-static int _intr_msk =
- FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR;
+static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC |
+ FLD_VID_SRC_OPC_ERR;
int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev,
struct sram_channel *ch,
@@ -615,14 +615,10 @@ int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num,
odd_risc_prog_size;
rp = cx25821_update_riscprogram(dev,
- dev->
- _dma_virt_start_addr,
- TOP_OFFSET,
- line_size_in_bytes,
- 0x0,
- singlefield_lines,
- FIFO_DISABLE,
- ODD_FIELD);
+ dev->_dma_virt_start_addr, TOP_OFFSET,
+ line_size_in_bytes, 0x0,
+ singlefield_lines, FIFO_DISABLE,
+ ODD_FIELD);
/* Jump to Even Risc program of 1st Frame */
*(rp++) = cpu_to_le32(RISC_JUMP);
@@ -753,7 +749,7 @@ int cx25821_start_video_dma_upstream(struct cx25821_dev *dev,
err =
request_irq(dev->pci->irq, cx25821_upstream_irq,
- IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+ IRQF_SHARED, dev->name, dev);
if (err < 0) {
pr_err("%s: can't get upstream IRQ %d\n",
dev->name, dev->pci->irq);
diff --git a/drivers/staging/cx25821/cx25821-video-upstream.h b/drivers/media/video/cx25821/cx25821-video-upstream.h
index f0b3ac0880ad..268ec8aa6a61 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream.h
+++ b/drivers/media/video/cx25821/cx25821-video-upstream.h
@@ -66,44 +66,74 @@
#define USE_RISC_NOOP_VIDEO 1
#ifdef USE_RISC_NOOP_VIDEO
-#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
- RISC_SYNC_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
+#define PAL_US_VID_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE)
-#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE)
+#define PAL_VID_PROG_SIZE \
+ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE)
-#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
+#define ODD_FLD_PAL_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
-#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
+#define ODD_FLD_NTSC_PROG_SIZE \
+ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
-#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
- JUMP_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
+#define NTSC_US_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
-#define NTSC_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+#define NTSC_RISC_BUF_SIZE \
+ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
-#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE)
+#define FRAME1_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE)
#endif
#ifndef USE_RISC_NOOP_VIDEO
-#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
- RISC_SYNC_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+#define PAL_US_VID_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE)
-#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE)
+#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE)
+
+#define PAL_VID_PROG_SIZE \
+ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE)
+
+#define ODD_FLD_PAL_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE \
+ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define NTSC_US_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
-#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+#define NTSC_RISC_BUF_SIZE \
+ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
-#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
-#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+#define FRAME1_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE)
-#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
-#define NTSC_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
-#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
- RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
#endif
diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/media/video/cx25821/cx25821-video.c
index 7a0304a85735..4d6907cda75b 100644
--- a/drivers/staging/cx25821/cx25821-video.c
+++ b/drivers/media/video/cx25821/cx25821-video.c
@@ -98,9 +98,8 @@ struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc)
{
unsigned int i;
- if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P) {
+ if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P)
return formats + 1;
- }
for (i = 0; i < ARRAY_SIZE(formats); i++)
if (formats[i].fourcc == fourcc)
@@ -110,7 +109,8 @@ struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc)
return NULL;
}
-void cx25821_dump_video_queue(struct cx25821_dev *dev, struct cx25821_dmaqueue *q)
+void cx25821_dump_video_queue(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q)
{
struct cx25821_buffer *buf;
struct list_head *item;
@@ -146,9 +146,8 @@ void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q,
/* count comes from the hw and it is 16bit wide --
* this trick handles wrap-arounds correctly for
* up to 32767 buffers in flight... */
- if ((s16) (count - buf->count) < 0) {
+ if ((s16) (count - buf->count) < 0)
break;
- }
do_gettimeofday(&buf->vb.ts);
buf->vb.state = VIDEOBUF_DONE;
@@ -161,8 +160,7 @@ void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q,
else
mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
if (bc != 1)
- pr_err("%s: %d buffers handled (should be 1)\n",
- __func__, bc);
+ pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc);
}
#ifdef TUNER_FLAG
@@ -203,24 +201,25 @@ struct video_device *cx25821_vdev_init(struct cx25821_dev *dev,
/*
static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl)
{
- int i;
+ int i;
- if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- for (i = 0; i < CX25821_CTLS; i++)
- if (cx25821_ctls[i].v.id == qctrl->id)
- break;
- if (i == CX25821_CTLS) {
- *qctrl = no_ctl;
+ if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX25821_CTLS; i++)
+ if (cx25821_ctls[i].v.id == qctrl->id)
+ break;
+ if (i == CX25821_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx25821_ctls[i].v;
return 0;
- }
- *qctrl = cx25821_ctls[i].v;
- return 0;
}
*/
/* resource management */
-int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bit)
+int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh,
+ unsigned int bit)
{
dprintk(1, "%s()\n", __func__);
if (fh->resources & bit)
@@ -229,14 +228,14 @@ int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int
/* is it free? */
mutex_lock(&dev->lock);
- if (dev->channels[fh->channel_id].resources & bit) {
+ if (dev->channels[fh->channel_id].resources & bit) {
/* no, someone else uses it */
mutex_unlock(&dev->lock);
return 0;
}
/* it's free, grab it */
fh->resources |= bit;
- dev->channels[fh->channel_id].resources |= bit;
+ dev->channels[fh->channel_id].resources |= bit;
dprintk(1, "res: get %d\n", bit);
mutex_unlock(&dev->lock);
return 1;
@@ -249,17 +248,18 @@ int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit)
int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit)
{
- return fh->dev->channels[fh->channel_id].resources & bit;
+ return fh->dev->channels[fh->channel_id].resources & bit;
}
-void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bits)
+void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh,
+ unsigned int bits)
{
BUG_ON((fh->resources & bits) != bits);
dprintk(1, "%s()\n", __func__);
mutex_lock(&dev->lock);
fh->resources &= ~bits;
- dev->channels[fh->channel_id].resources &= ~bits;
+ dev->channels[fh->channel_id].resources &= ~bits;
dprintk(1, "res: put %d\n", bits);
mutex_unlock(&dev->lock);
}
@@ -353,7 +353,7 @@ int cx25821_restart_video_queue(struct cx25821_dev *dev,
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
} else {
return 0;
}
@@ -366,11 +366,11 @@ void cx25821_vid_timeout(unsigned long data)
struct cx25821_data *timeout_data = (struct cx25821_data *)data;
struct cx25821_dev *dev = timeout_data->dev;
struct sram_channel *channel = timeout_data->channel;
- struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq;
+ struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq;
struct cx25821_buffer *buf;
unsigned long flags;
- /* cx25821_sram_channel_dump(dev, channel); */
+ /* cx25821_sram_channel_dump(dev, channel); */
cx_clear(channel->dma_ctl, 0x11);
spin_lock_irqsave(&dev->slock, flags);
@@ -392,7 +392,7 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
u32 count = 0;
int handled = 0;
u32 mask;
- struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+ struct sram_channel *channel = dev->channels[chan_num].sram_channels;
mask = cx_read(channel->int_msk);
if (0 == (status & mask))
@@ -412,8 +412,8 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
if (status & FLD_VID_DST_RISC1) {
spin_lock(&dev->slock);
count = cx_read(channel->gpcnt);
- cx25821_video_wakeup(dev,
- &dev->channels[channel->i].vidq, count);
+ cx25821_video_wakeup(dev, &dev->channels[channel->i].vidq,
+ count);
spin_unlock(&dev->slock);
handled++;
}
@@ -422,9 +422,8 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
if (status & 0x10) {
dprintk(2, "stopper video\n");
spin_lock(&dev->slock);
- cx25821_restart_video_queue(dev,
- &dev->channels[channel->i].vidq,
- channel);
+ cx25821_restart_video_queue(dev,
+ &dev->channels[channel->i].vidq, channel);
spin_unlock(&dev->slock);
handled++;
}
@@ -447,18 +446,18 @@ void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num)
{
cx_clear(PCI_INT_MSK, 1);
- if (dev->channels[chan_num].video_dev) {
- if (video_is_registered(dev->channels[chan_num].video_dev))
- video_unregister_device(
- dev->channels[chan_num].video_dev);
+ if (dev->channels[chan_num].video_dev) {
+ if (video_is_registered(dev->channels[chan_num].video_dev))
+ video_unregister_device(
+ dev->channels[chan_num].video_dev);
else
- video_device_release(
- dev->channels[chan_num].video_dev);
+ video_device_release(
+ dev->channels[chan_num].video_dev);
- dev->channels[chan_num].video_dev = NULL;
+ dev->channels[chan_num].video_dev = NULL;
- btcx_riscmem_free(dev->pci,
- &dev->channels[chan_num].vidq.stopper);
+ btcx_riscmem_free(dev->pci,
+ &dev->channels[chan_num].vidq.stopper);
pr_warn("device %d released!\n", chan_num);
}
@@ -468,74 +467,72 @@ void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num)
int cx25821_video_register(struct cx25821_dev *dev)
{
int err;
- int i;
+ int i;
- struct video_device cx25821_video_device = {
- .name = "cx25821-video",
- .fops = &video_fops,
- .minor = -1,
- .ioctl_ops = &video_ioctl_ops,
- .tvnorms = CX25821_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
- };
+ struct video_device cx25821_video_device = {
+ .name = "cx25821-video",
+ .fops = &video_fops,
+ .minor = -1,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = CX25821_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+ };
spin_lock_init(&dev->slock);
- for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) {
- cx25821_init_controls(dev, i);
+ for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) {
+ cx25821_init_controls(dev, i);
- cx25821_risc_stopper(dev->pci,
- &dev->channels[i].vidq.stopper,
- dev->channels[i].sram_channels->dma_ctl,
- 0x11, 0);
+ cx25821_risc_stopper(dev->pci, &dev->channels[i].vidq.stopper,
+ dev->channels[i].sram_channels->dma_ctl,
+ 0x11, 0);
- dev->channels[i].sram_channels = &cx25821_sram_channels[i];
- dev->channels[i].video_dev = NULL;
- dev->channels[i].resources = 0;
+ dev->channels[i].sram_channels = &cx25821_sram_channels[i];
+ dev->channels[i].video_dev = NULL;
+ dev->channels[i].resources = 0;
- cx_write(dev->channels[i].sram_channels->int_stat,
- 0xffffffff);
+ cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff);
- INIT_LIST_HEAD(&dev->channels[i].vidq.active);
- INIT_LIST_HEAD(&dev->channels[i].vidq.queued);
+ INIT_LIST_HEAD(&dev->channels[i].vidq.active);
+ INIT_LIST_HEAD(&dev->channels[i].vidq.queued);
- dev->channels[i].timeout_data.dev = dev;
- dev->channels[i].timeout_data.channel =
- &cx25821_sram_channels[i];
- dev->channels[i].vidq.timeout.function =
- cx25821_vid_timeout;
- dev->channels[i].vidq.timeout.data =
- (unsigned long)&dev->channels[i].timeout_data;
- init_timer(&dev->channels[i].vidq.timeout);
+ dev->channels[i].timeout_data.dev = dev;
+ dev->channels[i].timeout_data.channel =
+ &cx25821_sram_channels[i];
+ dev->channels[i].vidq.timeout.function =
+ cx25821_vid_timeout;
+ dev->channels[i].vidq.timeout.data =
+ (unsigned long)&dev->channels[i].timeout_data;
+ init_timer(&dev->channels[i].vidq.timeout);
- /* register v4l devices */
- dev->channels[i].video_dev = cx25821_vdev_init(dev,
- dev->pci, &cx25821_video_device, "video");
+ /* register v4l devices */
+ dev->channels[i].video_dev = cx25821_vdev_init(dev,
+ dev->pci, &cx25821_video_device, "video");
- err = video_register_device(dev->channels[i].video_dev,
- VFL_TYPE_GRABBER, video_nr[dev->nr]);
+ err = video_register_device(dev->channels[i].video_dev,
+ VFL_TYPE_GRABBER, video_nr[dev->nr]);
- if (err < 0)
- goto fail_unreg;
+ if (err < 0)
+ goto fail_unreg;
}
- /* set PCI interrupt */
+ /* set PCI interrupt */
cx_set(PCI_INT_MSK, 0xff);
/* initial device configuration */
mutex_lock(&dev->lock);
#ifdef TUNER_FLAG
- dev->tvnorm = cx25821_video_device.current_norm;
+ dev->tvnorm = cx25821_video_device.current_norm;
cx25821_set_tvnorm(dev, dev->tvnorm);
#endif
mutex_unlock(&dev->lock);
- return 0;
+ return 0;
fail_unreg:
- cx25821_video_unregister(dev, i);
+ cx25821_video_unregister(dev, i);
return err;
}
@@ -566,7 +563,7 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
u32 line0_offset, line1_offset;
struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
int bpl_local = LINE_SIZE_D1;
- int channel_opened = fh->channel_id;
+ int channel_opened = fh->channel_id;
BUG_ON(NULL == fh->fmt);
if (fh->width < 48 || fh->width > 720 ||
@@ -602,24 +599,24 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
if (init_buffer) {
channel_opened = dev->channel_opened;
- channel_opened = (channel_opened < 0
- || channel_opened > 7) ? 7 : channel_opened;
+ if (channel_opened < 0 || channel_opened > 7)
+ channel_opened = 7;
- if (dev->channels[channel_opened]
- .pixel_formats == PIXEL_FRMT_411)
+ if (dev->channels[channel_opened].pixel_formats ==
+ PIXEL_FRMT_411)
buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3;
else
buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width);
- if (dev->channels[channel_opened]
- .pixel_formats == PIXEL_FRMT_411) {
+ if (dev->channels[channel_opened].pixel_formats ==
+ PIXEL_FRMT_411) {
bpl_local = buf->bpl;
} else {
- bpl_local = buf->bpl; /* Default */
+ bpl_local = buf->bpl; /* Default */
if (channel_opened >= 0 && channel_opened <= 7) {
- if (dev->channels[channel_opened]
- .use_cif_resolution) {
+ if (dev->channels[channel_opened]
+ .use_cif_resolution) {
if (dev->tvnorm & V4L2_STD_PAL_BG
|| dev->tvnorm & V4L2_STD_PAL_DK)
bpl_local = 352 << 1;
@@ -679,12 +676,13 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
return 0;
- fail:
+fail:
cx25821_free_buffer(q, buf);
return rc;
}
-void cx25821_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+void cx25821_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf =
container_of(vb, struct cx25821_buffer, vb);
@@ -724,385 +722,381 @@ int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma)
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
- struct cx25821_buffer *buf =
+ struct cx25821_buffer *buf =
container_of(vb, struct cx25821_buffer, vb);
- struct cx25821_buffer *prev;
- struct cx25821_fh *fh = vq->priv_data;
- struct cx25821_dev *dev = fh->dev;
- struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].vidq;
-
- /* add jump to stopper */
- buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
- buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
- buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
-
- dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
-
- if (!list_empty(&q->queued)) {
- list_add_tail(&buf->vb.queue, &q->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf,
- buf->vb.i);
-
- } else if (list_empty(&q->active)) {
- list_add_tail(&buf->vb.queue, &q->active);
- cx25821_start_video_dma(dev, q, buf,
- dev->channels[fh->channel_id].
- sram_channels);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
- dprintk(2,
- "[%p/%d] buffer_queue - first active, buf cnt = %d, \
- q->count = %d\n",
- buf, buf->vb.i, buf->count, q->count);
- } else {
- prev =
- list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
- if (prev->vb.width == buf->vb.width
+ struct cx25821_buffer *prev;
+ struct cx25821_fh *fh = vq->priv_data;
+ struct cx25821_dev *dev = fh->dev;
+ struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].vidq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
+
+ if (!list_empty(&q->queued)) {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf,
+ buf->vb.i);
+
+ } else if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx25821_start_video_dma(dev, q, buf,
+ dev->channels[fh->channel_id].sram_channels);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
+ buf, buf->vb.i, buf->count, q->count);
+ } else {
+ prev = list_entry(q->active.prev, struct cx25821_buffer,
+ vb.queue);
+ if (prev->vb.width == buf->vb.width
&& prev->vb.height == buf->vb.height
&& prev->fmt == buf->fmt) {
- list_add_tail(&buf->vb.queue, &q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-
- /* 64 bit bits 63-32 */
- prev->risc.jmp[2] = cpu_to_le32(0);
- dprintk(2,
- "[%p/%d] buffer_queue - append to active, \
- buf->count=%d\n",
- buf, buf->vb.i, buf->count);
-
- } else {
- list_add_tail(&buf->vb.queue, &q->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf,
- buf->vb.i);
- }
- }
-
- if (list_empty(&q->active))
- dprintk(2, "active queue empty!\n");
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+
+ /* 64 bit bits 63-32 */
+ prev->risc.jmp[2] = cpu_to_le32(0);
+ dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n",
+ buf, buf->vb.i, buf->count);
+
+ } else {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf,
+ buf->vb.i);
+ }
+ }
+
+ if (list_empty(&q->active))
+ dprintk(2, "active queue empty!\n");
}
static struct videobuf_queue_ops cx25821_video_qops = {
- .buf_setup = cx25821_buffer_setup,
- .buf_prepare = cx25821_buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = cx25821_buffer_release,
+ .buf_setup = cx25821_buffer_setup,
+ .buf_prepare = cx25821_buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = cx25821_buffer_release,
};
static int video_open(struct file *file)
{
- struct video_device *vdev = video_devdata(file);
- struct cx25821_dev *h, *dev = video_drvdata(file);
- struct cx25821_fh *fh;
- struct list_head *list;
- int minor = video_devdata(file)->minor;
- enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- u32 pix_format;
- int ch_id = 0;
- int i;
-
- dprintk(1, "open dev=%s type=%s\n",
- video_device_node_name(vdev),
- v4l2_type_names[type]);
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh)
- return -ENOMEM;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *h, *dev = video_drvdata(file);
+ struct cx25821_fh *fh;
+ struct list_head *list;
+ int minor = video_devdata(file)->minor;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ u32 pix_format;
+ int ch_id = 0;
+ int i;
+
+ dprintk(1, "open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
mutex_lock(&cx25821_devlist_mutex);
- list_for_each(list, &cx25821_devlist)
- {
- h = list_entry(list, struct cx25821_dev, devlist);
+ list_for_each(list, &cx25821_devlist)
+ {
+ h = list_entry(list, struct cx25821_dev, devlist);
- for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) {
- if (h->channels[i].video_dev &&
+ for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) {
+ if (h->channels[i].video_dev &&
h->channels[i].video_dev->minor == minor) {
- dev = h;
- ch_id = i;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
- }
-
- if (NULL == dev) {
+ dev = h;
+ ch_id = i;
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ }
+ }
+ }
+
+ if (NULL == dev) {
mutex_unlock(&cx25821_devlist_mutex);
kfree(fh);
return -ENODEV;
- }
+ }
- file->private_data = fh;
- fh->dev = dev;
- fh->type = type;
- fh->width = 720;
- fh->channel_id = ch_id;
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->type = type;
+ fh->width = 720;
+ fh->channel_id = ch_id;
- if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
- fh->height = 576;
- else
- fh->height = 480;
+ if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
+ fh->height = 576;
+ else
+ fh->height = 480;
- dev->channel_opened = fh->channel_id;
- pix_format =
- (dev->channels[ch_id].pixel_formats ==
- PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV;
- fh->fmt = cx25821_format_by_fourcc(pix_format);
+ dev->channel_opened = fh->channel_id;
+ if (dev->channels[ch_id].pixel_formats == PIXEL_FRMT_411)
+ pix_format = V4L2_PIX_FMT_Y41P;
+ else
+ pix_format = V4L2_PIX_FMT_YUYV;
+ fh->fmt = cx25821_format_by_fourcc(pix_format);
- v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio);
+ v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio);
- videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
+ videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer), fh, NULL);
- dprintk(1, "post videobuf_queue_init()\n");
+ dprintk(1, "post videobuf_queue_init()\n");
mutex_unlock(&cx25821_devlist_mutex);
- return 0;
+ return 0;
}
static ssize_t video_read(struct file *file, char __user * data, size_t count,
loff_t *ppos)
{
- struct cx25821_fh *fh = file->private_data;
+ struct cx25821_fh *fh = file->private_data;
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (cx25821_res_locked(fh, RESOURCE_VIDEO0))
- return -EBUSY;
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (cx25821_res_locked(fh, RESOURCE_VIDEO0))
+ return -EBUSY;
- return videobuf_read_one(&fh->vidq, data, count, ppos,
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
file->f_flags & O_NONBLOCK);
- default:
- BUG();
- return 0;
- }
+ default:
+ BUG();
+ return 0;
+ }
}
static unsigned int video_poll(struct file *file,
struct poll_table_struct *wait)
{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_buffer *buf;
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_buffer *buf;
- if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
- /* streaming capture */
- if (list_empty(&fh->vidq.stream))
- return POLLERR;
- buf = list_entry(fh->vidq.stream.next,
+ if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ return POLLERR;
+ buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
- } else {
- /* read() capture */
- buf = (struct cx25821_buffer *)fh->vidq.read_buf;
- if (NULL == buf)
- return POLLERR;
- }
-
- poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) {
- if (buf->vb.state == VIDEOBUF_DONE) {
- struct cx25821_dev *dev = fh->dev;
-
- if (dev && dev->channels[fh->channel_id]
- .use_cif_resolution) {
- u8 cam_id = *((char *)buf->vb.baddr + 3);
- memcpy((char *)buf->vb.baddr,
+ } else {
+ /* read() capture */
+ buf = (struct cx25821_buffer *)fh->vidq.read_buf;
+ if (NULL == buf)
+ return POLLERR;
+ }
+
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) {
+ if (buf->vb.state == VIDEOBUF_DONE) {
+ struct cx25821_dev *dev = fh->dev;
+
+ if (dev && dev->channels[fh->channel_id]
+ .use_cif_resolution) {
+ u8 cam_id = *((char *)buf->vb.baddr + 3);
+ memcpy((char *)buf->vb.baddr,
(char *)buf->vb.baddr + (fh->width * 2),
(fh->width * 2));
- *((char *)buf->vb.baddr + 3) = cam_id;
- }
- }
+ *((char *)buf->vb.baddr + 3) = cam_id;
+ }
+ }
- return POLLIN | POLLRDNORM;
- }
+ return POLLIN | POLLRDNORM;
+ }
- return 0;
+ return 0;
}
static int video_release(struct file *file)
{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_dev *dev = fh->dev;
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
- /* stop the risc engine and fifo */
- cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */
+ /* stop the risc engine and fifo */
+ cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */
- /* stop video capture */
- if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
- videobuf_queue_cancel(&fh->vidq);
- cx25821_res_free(dev, fh, RESOURCE_VIDEO0);
- }
+ /* stop video capture */
+ if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
+ videobuf_queue_cancel(&fh->vidq);
+ cx25821_res_free(dev, fh, RESOURCE_VIDEO0);
+ }
- if (fh->vidq.read_buf) {
- cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf);
- kfree(fh->vidq.read_buf);
- }
+ if (fh->vidq.read_buf) {
+ cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
- videobuf_mmap_free(&fh->vidq);
+ videobuf_mmap_free(&fh->vidq);
- v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio);
- file->private_data = NULL;
- kfree(fh);
+ v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
- return 0;
+ return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = fh->dev;
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = fh->dev;
- if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
- return -EINVAL;
+ if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ return -EINVAL;
- if (unlikely(i != fh->type))
- return -EINVAL;
+ if (unlikely(i != fh->type))
+ return -EINVAL;
- if (unlikely(!cx25821_res_get(dev, fh,
- cx25821_get_resource(fh, RESOURCE_VIDEO0))))
- return -EBUSY;
+ if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh,
+ RESOURCE_VIDEO0))))
+ return -EBUSY;
- return videobuf_streamon(get_queue(fh));
+ return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = fh->dev;
- int err, res;
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = fh->dev;
+ int err, res;
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (i != fh->type)
- return -EINVAL;
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (i != fh->type)
+ return -EINVAL;
- res = cx25821_get_resource(fh, RESOURCE_VIDEO0);
- err = videobuf_streamoff(get_queue(fh));
- if (err < 0)
- return err;
- cx25821_res_free(dev, fh, res);
- return 0;
+ res = cx25821_get_resource(fh, RESOURCE_VIDEO0);
+ err = videobuf_streamoff(get_queue(fh));
+ if (err < 0)
+ return err;
+ cx25821_res_free(dev, fh, res);
+ return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+ struct v4l2_format *f)
{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
struct v4l2_mbus_framefmt mbus_fmt;
- int err;
- int pix_format = PIXEL_FRMT_422;
+ int err;
+ int pix_format = PIXEL_FRMT_422;
- if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id]
- .prio, fh->prio);
- if (0 != err)
- return err;
- }
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
- dprintk(2, "%s()\n", __func__);
- err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f);
+ dprintk(2, "%s()\n", __func__);
+ err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f);
- if (0 != err)
- return err;
+ if (0 != err)
+ return err;
- fh->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
- fh->vidq.field = f->fmt.pix.field;
+ fh->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->vidq.field = f->fmt.pix.field;
- /* check if width and height is valid based on set standard */
- if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm))
- fh->width = f->fmt.pix.width;
+ /* check if width and height is valid based on set standard */
+ if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm))
+ fh->width = f->fmt.pix.width;
- if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm))
- fh->height = f->fmt.pix.height;
+ if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm))
+ fh->height = f->fmt.pix.height;
- if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
- pix_format = PIXEL_FRMT_411;
- else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
- pix_format = PIXEL_FRMT_422;
- else
- return -EINVAL;
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
+ pix_format = PIXEL_FRMT_411;
+ else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+ pix_format = PIXEL_FRMT_422;
+ else
+ return -EINVAL;
- cx25821_set_pixel_format(dev, SRAM_CH00, pix_format);
+ cx25821_set_pixel_format(dev, SRAM_CH00, pix_format);
- /* check if cif resolution */
- if (fh->width == 320 || fh->width == 352)
- dev->channels[fh->channel_id].use_cif_resolution = 1;
- else
- dev->channels[fh->channel_id].use_cif_resolution = 0;
+ /* check if cif resolution */
+ if (fh->width == 320 || fh->width == 352)
+ dev->channels[fh->channel_id].use_cif_resolution = 1;
+ else
+ dev->channels[fh->channel_id].use_cif_resolution = 0;
- dev->channels[fh->channel_id].cif_width = fh->width;
- medusa_set_resolution(dev, fh->width, SRAM_CH00);
+ dev->channels[fh->channel_id].cif_width = fh->width;
+ medusa_set_resolution(dev, fh->width, SRAM_CH00);
dprintk(2, "%s(): width=%d height=%d field=%d\n", __func__, fh->width,
fh->height, fh->vidq.field);
v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
cx25821_call_all(dev, video, s_mbus_fmt, &mbus_fmt);
- return 0;
+ return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
- int ret_val = 0;
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ int ret_val = 0;
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
+ ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
- p->sequence = dev->channels[fh->channel_id].vidq.count;
+ p->sequence = dev->channels[fh->channel_id].vidq.count;
- return ret_val;
+ return ret_val;
}
static int vidioc_log_status(struct file *file, void *priv)
{
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- struct cx25821_fh *fh = priv;
- char name[32 + 2];
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct cx25821_fh *fh = priv;
+ char name[32 + 2];
- struct sram_channel *sram_ch = dev->channels[fh->channel_id]
- .sram_channels;
- u32 tmp = 0;
+ struct sram_channel *sram_ch = dev->channels[fh->channel_id]
+ .sram_channels;
+ u32 tmp = 0;
- snprintf(name, sizeof(name), "%s/2", dev->name);
+ snprintf(name, sizeof(name), "%s/2", dev->name);
pr_info("%s/2: ============ START LOG STATUS ============\n",
dev->name);
- cx25821_call_all(dev, core, log_status);
- tmp = cx_read(sram_ch->dma_ctl);
+ cx25821_call_all(dev, core, log_status);
+ tmp = cx_read(sram_ch->dma_ctl);
pr_info("Video input 0 is %s\n",
(tmp & 0x11) ? "streaming" : "stopped");
pr_info("%s/2: ============= END LOG STATUS =============\n",
dev->name);
- return 0;
+ return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- int err;
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ int err;
- if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id]
- .prio, fh->prio);
- if (0 != err)
- return err;
- }
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
- return cx25821_set_control(dev, ctl, fh->channel_id);
+ return cx25821_set_control(dev, ctl, fh->channel_id);
}
-/* VIDEO IOCTLS */
-int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
+/* VIDEO IOCTLS */
+int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
@@ -1116,7 +1110,8 @@ int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_form
return 0;
}
-int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
+int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct cx25821_fmt *fmt;
enum v4l2_field field;
@@ -1131,8 +1126,10 @@ int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fo
maxh = 576;
if (V4L2_FIELD_ANY == field) {
- field = (f->fmt.pix.height > maxh / 2)
- ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
+ if (f->fmt.pix.height > maxh / 2)
+ field = V4L2_FIELD_INTERLACED;
+ else
+ field = V4L2_FIELD_TOP;
}
switch (field) {
@@ -1162,7 +1159,8 @@ int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fo
return 0;
}
-int cx25821_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+int cx25821_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
@@ -1189,13 +1187,15 @@ int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-int cx25821_vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
+int cx25821_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
{
struct cx25821_fh *fh = priv;
return videobuf_reqbufs(get_queue(fh), p);
}
-int cx25821_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+int cx25821_vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
{
struct cx25821_fh *fh = priv;
return videobuf_querybuf(get_queue(fh), p);
@@ -1210,20 +1210,21 @@ int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev;
- struct cx25821_fh *fh = f;
+ struct cx25821_fh *fh = f;
- *p = v4l2_prio_max(&dev->channels[fh->channel_id].prio);
+ *p = v4l2_prio_max(&dev->channels[fh->channel_id].prio);
return 0;
}
-int cx25821_vidioc_s_priority(struct file *file, void *f, enum v4l2_priority prio)
+int cx25821_vidioc_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
{
struct cx25821_fh *fh = f;
struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev;
- return v4l2_prio_change(&dev->channels[fh->channel_id]
- .prio, &fh->prio, prio);
+ return v4l2_prio_change(&dev->channels[fh->channel_id].prio, &fh->prio,
+ prio);
}
#ifdef TUNER_FLAG
@@ -1236,15 +1237,14 @@ int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms)
dprintk(1, "%s()\n", __func__);
if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id]
- .prio, fh->prio);
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
if (0 != err)
return err;
}
- if (dev->tvnorm == *tvnorms) {
+ if (dev->tvnorm == *tvnorms)
return 0;
- }
mutex_lock(&dev->lock);
cx25821_set_tvnorm(dev, *tvnorms);
@@ -1258,7 +1258,7 @@ int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms)
int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i)
{
- static const char *iname[] = {
+ static const char * const iname[] = {
[CX25821_VMUX_COMPOSITE] = "Composite",
[CX25821_VMUX_SVIDEO] = "S-Video",
[CX25821_VMUX_DEBUG] = "for debug only",
@@ -1280,7 +1280,8 @@ int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i)
return 0;
}
-int cx25821_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i)
+int cx25821_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
dprintk(1, "%s()\n", __func__);
@@ -1305,13 +1306,13 @@ int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i)
dprintk(1, "%s(%d)\n", __func__, i);
if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id]
- .prio, fh->prio);
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
if (0 != err)
return err;
}
- if (i > 2) {
+ if (i >= CX25821_NR_INPUT) {
dprintk(1, "%s(): -EINVAL\n", __func__);
return -EINVAL;
}
@@ -1323,7 +1324,8 @@ int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i)
}
#ifdef TUNER_FLAG
-int cx25821_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f)
+int cx25821_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
@@ -1350,21 +1352,22 @@ int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f)
return 0;
}
-int cx25821_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f)
+int cx25821_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev;
int err;
if (fh) {
- dev = fh->dev;
- err = v4l2_prio_check(&dev->channels[fh->channel_id]
- .prio, fh->prio);
+ dev = fh->dev;
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
if (0 != err)
return err;
- } else {
- pr_err("Invalid fh pointer!\n");
- return -EINVAL;
+ } else {
+ pr_err("Invalid fh pointer!\n");
+ return -EINVAL;
}
return cx25821_set_freq(dev, f);
@@ -1426,8 +1429,8 @@ int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
int err;
if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id]
- .prio, fh->prio);
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
if (0 != err)
return err;
}
@@ -1522,10 +1525,11 @@ static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id)
return NULL;
}
-int cx25821_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctl)
+int cx25821_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- struct cx25821_fh *fh = priv;
+ struct cx25821_fh *fh = priv;
const struct v4l2_queryctrl *ctrl;
@@ -1535,16 +1539,16 @@ int cx25821_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ct
return -EINVAL;
switch (ctl->id) {
case V4L2_CID_BRIGHTNESS:
- ctl->value = dev->channels[fh->channel_id].ctl_bright;
+ ctl->value = dev->channels[fh->channel_id].ctl_bright;
break;
case V4L2_CID_HUE:
- ctl->value = dev->channels[fh->channel_id].ctl_hue;
+ ctl->value = dev->channels[fh->channel_id].ctl_hue;
break;
case V4L2_CID_CONTRAST:
- ctl->value = dev->channels[fh->channel_id].ctl_contrast;
+ ctl->value = dev->channels[fh->channel_id].ctl_contrast;
break;
case V4L2_CID_SATURATION:
- ctl->value = dev->channels[fh->channel_id].ctl_saturation;
+ ctl->value = dev->channels[fh->channel_id].ctl_saturation;
break;
}
return 0;
@@ -1578,19 +1582,19 @@ int cx25821_set_control(struct cx25821_dev *dev,
switch (ctl->id) {
case V4L2_CID_BRIGHTNESS:
- dev->channels[chan_num].ctl_bright = ctl->value;
+ dev->channels[chan_num].ctl_bright = ctl->value;
medusa_set_brightness(dev, ctl->value, chan_num);
break;
case V4L2_CID_HUE:
- dev->channels[chan_num].ctl_hue = ctl->value;
+ dev->channels[chan_num].ctl_hue = ctl->value;
medusa_set_hue(dev, ctl->value, chan_num);
break;
case V4L2_CID_CONTRAST:
- dev->channels[chan_num].ctl_contrast = ctl->value;
+ dev->channels[chan_num].ctl_contrast = ctl->value;
medusa_set_contrast(dev, ctl->value, chan_num);
break;
case V4L2_CID_SATURATION:
- dev->channels[chan_num].ctl_saturation = ctl->value;
+ dev->channels[chan_num].ctl_saturation = ctl->value;
medusa_set_saturation(dev, ctl->value, chan_num);
break;
}
@@ -1612,7 +1616,8 @@ static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num)
}
}
-int cx25821_vidioc_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cropcap)
+int cx25821_vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cropcap)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
@@ -1622,9 +1627,9 @@ int cx25821_vidioc_cropcap(struct file *file, void *priv, struct v4l2_cropcap *c
cropcap->bounds.width = 720;
cropcap->bounds.height = dev->tvnorm == V4L2_STD_PAL_BG ? 576 : 480;
cropcap->pixelaspect.numerator =
- dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10;
+ dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10;
cropcap->pixelaspect.denominator =
- dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11;
+ dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11;
cropcap->defrect = cropcap->bounds;
return 0;
}
@@ -1636,24 +1641,24 @@ int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop)
int err;
if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id].
- prio, fh->prio);
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
if (0 != err)
return err;
}
- /* cx25821_vidioc_s_crop not supported */
+ /* cx25821_vidioc_s_crop not supported */
return -EINVAL;
}
int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
{
- /* cx25821_vidioc_g_crop not supported */
+ /* cx25821_vidioc_g_crop not supported */
return -EINVAL;
}
int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm)
{
- /* medusa does not support video standard sensing of current input */
+ /* medusa does not support video standard sensing of current input */
*norm = CX25821_NORMS;
return 0;
@@ -1699,310 +1704,309 @@ int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm)
static long video_ioctl_upstream9(struct file *file, unsigned int cmd,
unsigned long arg)
{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_dev *dev = fh->dev;
- int command = 0;
- struct upstream_user_struct *data_from_user;
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ int command = 0;
+ struct upstream_user_struct *data_from_user;
- data_from_user = (struct upstream_user_struct *)arg;
+ data_from_user = (struct upstream_user_struct *)arg;
if (!data_from_user) {
pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
return 0;
}
- command = data_from_user->command;
+ command = data_from_user->command;
- if (command != UPSTREAM_START_VIDEO &&
- command != UPSTREAM_STOP_VIDEO)
- return 0;
+ if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO)
+ return 0;
- dev->input_filename = data_from_user->input_filename;
- dev->input_audiofilename = data_from_user->input_filename;
- dev->vid_stdname = data_from_user->vid_stdname;
- dev->pixel_format = data_from_user->pixel_format;
- dev->channel_select = data_from_user->channel_select;
- dev->command = data_from_user->command;
+ dev->input_filename = data_from_user->input_filename;
+ dev->input_audiofilename = data_from_user->input_filename;
+ dev->vid_stdname = data_from_user->vid_stdname;
+ dev->pixel_format = data_from_user->pixel_format;
+ dev->channel_select = data_from_user->channel_select;
+ dev->command = data_from_user->command;
- switch (command) {
- case UPSTREAM_START_VIDEO:
- cx25821_start_upstream_video_ch1(dev, data_from_user);
- break;
+ switch (command) {
+ case UPSTREAM_START_VIDEO:
+ cx25821_start_upstream_video_ch1(dev, data_from_user);
+ break;
- case UPSTREAM_STOP_VIDEO:
- cx25821_stop_upstream_video_ch1(dev);
- break;
- }
+ case UPSTREAM_STOP_VIDEO:
+ cx25821_stop_upstream_video_ch1(dev);
+ break;
+ }
- return 0;
+ return 0;
}
static long video_ioctl_upstream10(struct file *file, unsigned int cmd,
unsigned long arg)
{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_dev *dev = fh->dev;
- int command = 0;
- struct upstream_user_struct *data_from_user;
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ int command = 0;
+ struct upstream_user_struct *data_from_user;
- data_from_user = (struct upstream_user_struct *)arg;
+ data_from_user = (struct upstream_user_struct *)arg;
if (!data_from_user) {
pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
return 0;
}
- command = data_from_user->command;
+ command = data_from_user->command;
- if (command != UPSTREAM_START_VIDEO &&
- command != UPSTREAM_STOP_VIDEO)
- return 0;
+ if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO)
+ return 0;
- dev->input_filename_ch2 = data_from_user->input_filename;
- dev->input_audiofilename = data_from_user->input_filename;
- dev->vid_stdname_ch2 = data_from_user->vid_stdname;
- dev->pixel_format_ch2 = data_from_user->pixel_format;
- dev->channel_select_ch2 = data_from_user->channel_select;
- dev->command_ch2 = data_from_user->command;
+ dev->input_filename_ch2 = data_from_user->input_filename;
+ dev->input_audiofilename = data_from_user->input_filename;
+ dev->vid_stdname_ch2 = data_from_user->vid_stdname;
+ dev->pixel_format_ch2 = data_from_user->pixel_format;
+ dev->channel_select_ch2 = data_from_user->channel_select;
+ dev->command_ch2 = data_from_user->command;
- switch (command) {
- case UPSTREAM_START_VIDEO:
- cx25821_start_upstream_video_ch2(dev, data_from_user);
- break;
+ switch (command) {
+ case UPSTREAM_START_VIDEO:
+ cx25821_start_upstream_video_ch2(dev, data_from_user);
+ break;
- case UPSTREAM_STOP_VIDEO:
- cx25821_stop_upstream_video_ch2(dev);
- break;
- }
+ case UPSTREAM_STOP_VIDEO:
+ cx25821_stop_upstream_video_ch2(dev);
+ break;
+ }
- return 0;
+ return 0;
}
static long video_ioctl_upstream11(struct file *file, unsigned int cmd,
unsigned long arg)
{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_dev *dev = fh->dev;
- int command = 0;
- struct upstream_user_struct *data_from_user;
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ int command = 0;
+ struct upstream_user_struct *data_from_user;
- data_from_user = (struct upstream_user_struct *)arg;
+ data_from_user = (struct upstream_user_struct *)arg;
if (!data_from_user) {
pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
return 0;
}
- command = data_from_user->command;
+ command = data_from_user->command;
- if (command != UPSTREAM_START_AUDIO &&
- command != UPSTREAM_STOP_AUDIO)
- return 0;
+ if (command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO)
+ return 0;
- dev->input_filename = data_from_user->input_filename;
- dev->input_audiofilename = data_from_user->input_filename;
- dev->vid_stdname = data_from_user->vid_stdname;
- dev->pixel_format = data_from_user->pixel_format;
- dev->channel_select = data_from_user->channel_select;
- dev->command = data_from_user->command;
+ dev->input_filename = data_from_user->input_filename;
+ dev->input_audiofilename = data_from_user->input_filename;
+ dev->vid_stdname = data_from_user->vid_stdname;
+ dev->pixel_format = data_from_user->pixel_format;
+ dev->channel_select = data_from_user->channel_select;
+ dev->command = data_from_user->command;
- switch (command) {
- case UPSTREAM_START_AUDIO:
- cx25821_start_upstream_audio(dev, data_from_user);
- break;
+ switch (command) {
+ case UPSTREAM_START_AUDIO:
+ cx25821_start_upstream_audio(dev, data_from_user);
+ break;
- case UPSTREAM_STOP_AUDIO:
- cx25821_stop_upstream_audio(dev);
- break;
- }
+ case UPSTREAM_STOP_AUDIO:
+ cx25821_stop_upstream_audio(dev);
+ break;
+ }
- return 0;
+ return 0;
}
static long video_ioctl_set(struct file *file, unsigned int cmd,
unsigned long arg)
{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_dev *dev = fh->dev;
- struct downstream_user_struct *data_from_user;
- int command;
- int width = 720;
- int selected_channel = 0, pix_format = 0, i = 0;
- int cif_enable = 0, cif_width = 0;
- u32 value = 0;
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ struct downstream_user_struct *data_from_user;
+ int command;
+ int width = 720;
+ int selected_channel = 0, pix_format = 0, i = 0;
+ int cif_enable = 0, cif_width = 0;
+ u32 value = 0;
- data_from_user = (struct downstream_user_struct *)arg;
+ data_from_user = (struct downstream_user_struct *)arg;
if (!data_from_user) {
pr_err("%s(): User data is INVALID. Returning\n", __func__);
return 0;
}
- command = data_from_user->command;
+ command = data_from_user->command;
- if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT
+ if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT
&& command != ENABLE_CIF_RESOLUTION && command != REG_READ
&& command != REG_WRITE && command != MEDUSA_READ
&& command != MEDUSA_WRITE) {
- return 0;
- }
-
- switch (command) {
- case SET_VIDEO_STD:
- dev->tvnorm =
- !strcmp(data_from_user->vid_stdname,
- "PAL") ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M;
- medusa_set_videostandard(dev);
- break;
-
- case SET_PIXEL_FORMAT:
- selected_channel = data_from_user->decoder_select;
- pix_format = data_from_user->pixel_format;
-
- if (!(selected_channel <= 7 && selected_channel >= 0)) {
- selected_channel -= 4;
- selected_channel = selected_channel % 8;
- }
-
- if (selected_channel >= 0)
- cx25821_set_pixel_format(dev, selected_channel,
+ return 0;
+ }
+
+ switch (command) {
+ case SET_VIDEO_STD:
+ if (!strcmp(data_from_user->vid_stdname, "PAL"))
+ dev->tvnorm = V4L2_STD_PAL_BG;
+ else
+ dev->tvnorm = V4L2_STD_NTSC_M;
+ medusa_set_videostandard(dev);
+ break;
+
+ case SET_PIXEL_FORMAT:
+ selected_channel = data_from_user->decoder_select;
+ pix_format = data_from_user->pixel_format;
+
+ if (!(selected_channel <= 7 && selected_channel >= 0)) {
+ selected_channel -= 4;
+ selected_channel = selected_channel % 8;
+ }
+
+ if (selected_channel >= 0)
+ cx25821_set_pixel_format(dev, selected_channel,
pix_format);
- break;
-
- case ENABLE_CIF_RESOLUTION:
- selected_channel = data_from_user->decoder_select;
- cif_enable = data_from_user->cif_resolution_enable;
- cif_width = data_from_user->cif_width;
-
- if (cif_enable) {
- if (dev->tvnorm & V4L2_STD_PAL_BG
- || dev->tvnorm & V4L2_STD_PAL_DK)
- width = 352;
- else
- width = (cif_width == 320
- || cif_width == 352) ? cif_width : 320;
- }
-
- if (!(selected_channel <= 7 && selected_channel >= 0)) {
- selected_channel -= 4;
- selected_channel = selected_channel % 8;
- }
-
- if (selected_channel <= 7 && selected_channel >= 0) {
- dev->channels[selected_channel].
- use_cif_resolution = cif_enable;
- dev->channels[selected_channel].cif_width = width;
- } else {
- for (i = 0; i < VID_CHANNEL_NUM; i++) {
- dev->channels[i].use_cif_resolution =
- cif_enable;
- dev->channels[i].cif_width = width;
- }
- }
-
- medusa_set_resolution(dev, width, selected_channel);
- break;
- case REG_READ:
- data_from_user->reg_data = cx_read(data_from_user->reg_address);
- break;
- case REG_WRITE:
- cx_write(data_from_user->reg_address, data_from_user->reg_data);
- break;
- case MEDUSA_READ:
- value =
- cx25821_i2c_read(&dev->i2c_bus[0],
- (u16) data_from_user->reg_address,
- &data_from_user->reg_data);
- break;
- case MEDUSA_WRITE:
- cx25821_i2c_write(&dev->i2c_bus[0],
- (u16) data_from_user->reg_address,
- data_from_user->reg_data);
- break;
- }
-
- return 0;
-}
+ break;
-static long cx25821_video_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int ret = 0;
+ case ENABLE_CIF_RESOLUTION:
+ selected_channel = data_from_user->decoder_select;
+ cif_enable = data_from_user->cif_resolution_enable;
+ cif_width = data_from_user->cif_width;
+
+ if (cif_enable) {
+ if (dev->tvnorm & V4L2_STD_PAL_BG
+ || dev->tvnorm & V4L2_STD_PAL_DK) {
+ width = 352;
+ } else {
+ width = cif_width;
+ if (cif_width != 320 && cif_width != 352)
+ width = 320;
+ }
+ }
- struct cx25821_fh *fh = file->private_data;
+ if (!(selected_channel <= 7 && selected_channel >= 0)) {
+ selected_channel -= 4;
+ selected_channel = selected_channel % 8;
+ }
+
+ if (selected_channel <= 7 && selected_channel >= 0) {
+ dev->channels[selected_channel].
+ use_cif_resolution = cif_enable;
+ dev->channels[selected_channel].cif_width = width;
+ } else {
+ for (i = 0; i < VID_CHANNEL_NUM; i++) {
+ dev->channels[i].use_cif_resolution =
+ cif_enable;
+ dev->channels[i].cif_width = width;
+ }
+ }
- /* check to see if it's the video upstream */
- if (fh->channel_id == SRAM_CH09) {
- ret = video_ioctl_upstream9(file, cmd, arg);
- return ret;
- } else if (fh->channel_id == SRAM_CH10) {
- ret = video_ioctl_upstream10(file, cmd, arg);
- return ret;
- } else if (fh->channel_id == SRAM_CH11) {
- ret = video_ioctl_upstream11(file, cmd, arg);
- ret = video_ioctl_set(file, cmd, arg);
- return ret;
- }
+ medusa_set_resolution(dev, width, selected_channel);
+ break;
+ case REG_READ:
+ data_from_user->reg_data = cx_read(data_from_user->reg_address);
+ break;
+ case REG_WRITE:
+ cx_write(data_from_user->reg_address, data_from_user->reg_data);
+ break;
+ case MEDUSA_READ:
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ (u16) data_from_user->reg_address,
+ &data_from_user->reg_data);
+ break;
+ case MEDUSA_WRITE:
+ cx25821_i2c_write(&dev->i2c_bus[0],
+ (u16) data_from_user->reg_address,
+ data_from_user->reg_data);
+ break;
+ }
+
+ return 0;
+}
+
+static long cx25821_video_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+
+ struct cx25821_fh *fh = file->private_data;
+
+ /* check to see if it's the video upstream */
+ if (fh->channel_id == SRAM_CH09) {
+ ret = video_ioctl_upstream9(file, cmd, arg);
+ return ret;
+ } else if (fh->channel_id == SRAM_CH10) {
+ ret = video_ioctl_upstream10(file, cmd, arg);
+ return ret;
+ } else if (fh->channel_id == SRAM_CH11) {
+ ret = video_ioctl_upstream11(file, cmd, arg);
+ ret = video_ioctl_set(file, cmd, arg);
+ return ret;
+ }
- return video_ioctl2(file, cmd, arg);
+ return video_ioctl2(file, cmd, arg);
}
/* exported stuff */
static const struct v4l2_file_operations video_fops = {
- .owner = THIS_MODULE,
- .open = video_open,
- .release = video_release,
- .read = video_read,
- .poll = video_poll,
- .mmap = cx25821_video_mmap,
- .ioctl = cx25821_video_ioctl,
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = cx25821_video_mmap,
+ .ioctl = cx25821_video_ioctl,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
- .vidioc_querycap = cx25821_vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = cx25821_vidioc_reqbufs,
- .vidioc_querybuf = cx25821_vidioc_querybuf,
- .vidioc_qbuf = cx25821_vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_querycap = cx25821_vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = cx25821_vidioc_reqbufs,
+ .vidioc_querybuf = cx25821_vidioc_querybuf,
+ .vidioc_qbuf = cx25821_vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
- .vidioc_s_std = cx25821_vidioc_s_std,
- .vidioc_querystd = cx25821_vidioc_querystd,
+ .vidioc_s_std = cx25821_vidioc_s_std,
+ .vidioc_querystd = cx25821_vidioc_querystd,
#endif
- .vidioc_cropcap = cx25821_vidioc_cropcap,
- .vidioc_s_crop = cx25821_vidioc_s_crop,
- .vidioc_g_crop = cx25821_vidioc_g_crop,
- .vidioc_enum_input = cx25821_vidioc_enum_input,
- .vidioc_g_input = cx25821_vidioc_g_input,
- .vidioc_s_input = cx25821_vidioc_s_input,
- .vidioc_g_ctrl = cx25821_vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_queryctrl = cx25821_vidioc_queryctrl,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_log_status = vidioc_log_status,
- .vidioc_g_priority = cx25821_vidioc_g_priority,
- .vidioc_s_priority = cx25821_vidioc_s_priority,
+ .vidioc_cropcap = cx25821_vidioc_cropcap,
+ .vidioc_s_crop = cx25821_vidioc_s_crop,
+ .vidioc_g_crop = cx25821_vidioc_g_crop,
+ .vidioc_enum_input = cx25821_vidioc_enum_input,
+ .vidioc_g_input = cx25821_vidioc_g_input,
+ .vidioc_s_input = cx25821_vidioc_s_input,
+ .vidioc_g_ctrl = cx25821_vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_queryctrl = cx25821_vidioc_queryctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_g_priority = cx25821_vidioc_g_priority,
+ .vidioc_s_priority = cx25821_vidioc_s_priority,
#ifdef TUNER_FLAG
- .vidioc_g_tuner = cx25821_vidioc_g_tuner,
- .vidioc_s_tuner = cx25821_vidioc_s_tuner,
- .vidioc_g_frequency = cx25821_vidioc_g_frequency,
- .vidioc_s_frequency = cx25821_vidioc_s_frequency,
+ .vidioc_g_tuner = cx25821_vidioc_g_tuner,
+ .vidioc_s_tuner = cx25821_vidioc_s_tuner,
+ .vidioc_g_frequency = cx25821_vidioc_g_frequency,
+ .vidioc_s_frequency = cx25821_vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = cx25821_vidioc_g_register,
- .vidioc_s_register = cx25821_vidioc_s_register,
+ .vidioc_g_register = cx25821_vidioc_g_register,
+ .vidioc_s_register = cx25821_vidioc_s_register,
#endif
};
struct video_device cx25821_videoioctl_template = {
- .name = "cx25821-videoioctl",
- .fops = &video_fops,
- .ioctl_ops = &video_ioctl_ops,
- .tvnorms = CX25821_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
+ .name = "cx25821-videoioctl",
+ .fops = &video_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = CX25821_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
};
diff --git a/drivers/staging/cx25821/cx25821-video.h b/drivers/media/video/cx25821/cx25821-video.h
index f4ee8051b8b3..d0d9538ca5b3 100644
--- a/drivers/staging/cx25821/cx25821-video.h
+++ b/drivers/media/video/cx25821/cx25821-video.h
@@ -63,7 +63,7 @@ do { \
#define REG_READ 900
#define REG_WRITE 901
#define MEDUSA_READ 910
-#define MEDUSA_WRITE 911
+#define MEDUSA_WRITE 911
extern struct sram_channel *channel0;
extern struct sram_channel *channel1;
@@ -87,7 +87,7 @@ extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc);
extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM];
extern void cx25821_dump_video_queue(struct cx25821_dev *dev,
- struct cx25821_dmaqueue *q);
+ struct cx25821_dmaqueue *q);
extern void cx25821_video_wakeup(struct cx25821_dev *dev,
struct cx25821_dmaqueue *q, u32 count);
@@ -96,11 +96,11 @@ extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm);
#endif
extern int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh,
- unsigned int bit);
+ unsigned int bit);
extern int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit);
extern int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit);
extern void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh,
- unsigned int bits);
+ unsigned int bits);
extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input);
extern int cx25821_start_video_dma(struct cx25821_dev *dev,
struct cx25821_dmaqueue *q,
@@ -115,63 +115,74 @@ extern int cx25821_video_register(struct cx25821_dev *dev);
extern int cx25821_get_format_size(void);
extern int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count,
- unsigned int *size);
-extern int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field);
+ unsigned int *size);
+extern int cx25821_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field);
extern void cx25821_buffer_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb);
+ struct videobuf_buffer *vb);
extern struct videobuf_queue *get_queue(struct cx25821_fh *fh);
extern int cx25821_get_resource(struct cx25821_fh *fh, int resource);
extern int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma);
extern int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f);
+ struct v4l2_format *f);
extern int cx25821_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap);
+ struct v4l2_capability *cap);
extern int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f);
+ struct v4l2_fmtdesc *f);
extern int cx25821_vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p);
+ struct v4l2_requestbuffers *p);
extern int cx25821_vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *p);
-extern int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p);
-extern int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms);
+ struct v4l2_buffer *p);
+extern int cx25821_vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p);
+extern int cx25821_vidioc_s_std(struct file *file, void *priv,
+ v4l2_std_id *tvnorms);
extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i);
extern int cx25821_vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i);
-extern int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i);
-extern int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i);
+ struct v4l2_input *i);
+extern int cx25821_vidioc_g_input(struct file *file, void *priv,
+ unsigned int *i);
+extern int cx25821_vidioc_s_input(struct file *file, void *priv,
+ unsigned int i);
extern int cx25821_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctl);
+ struct v4l2_control *ctl);
extern int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f);
+ struct v4l2_format *f);
extern int cx25821_vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f);
+ struct v4l2_frequency *f);
extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f);
extern int cx25821_vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f);
+ struct v4l2_frequency *f);
extern int cx25821_vidioc_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg);
+ struct v4l2_dbg_register *reg);
extern int cx25821_vidioc_s_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg);
-extern int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t);
-extern int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t);
+ struct v4l2_dbg_register *reg);
+extern int cx25821_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t);
+extern int cx25821_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t);
extern int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm);
extern int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm);
-extern int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p);
+extern int cx25821_vidioc_g_priority(struct file *file, void *f,
+ enum v4l2_priority *p);
extern int cx25821_vidioc_s_priority(struct file *file, void *f,
- enum v4l2_priority prio);
+ enum v4l2_priority prio);
extern int cx25821_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qctrl);
+ struct v4l2_queryctrl *qctrl);
extern int cx25821_set_control(struct cx25821_dev *dev,
struct v4l2_control *ctrl, int chan_num);
extern int cx25821_vidioc_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *cropcap);
-extern int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop);
-extern int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop);
-
-extern int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm);
+ struct v4l2_cropcap *cropcap);
+extern int cx25821_vidioc_s_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop);
+extern int cx25821_vidioc_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop);
+
+extern int cx25821_vidioc_querystd(struct file *file, void *priv,
+ v4l2_std_id *norm);
#endif
diff --git a/drivers/staging/cx25821/cx25821.h b/drivers/media/video/cx25821/cx25821.h
index 6230243e2ccb..2d2d00932823 100644
--- a/drivers/staging/cx25821/cx25821.h
+++ b/drivers/media/video/cx25821/cx25821.h
@@ -98,6 +98,7 @@
#define CX25821_BOARD_CONEXANT_ATHENA10 1
#define MAX_VID_CHANNEL_NUM 12
#define VID_CHANNEL_NUM 8
+#define CX25821_NR_INPUT 2
struct cx25821_fmt {
char *name;
@@ -179,22 +180,24 @@ struct cx25821_input {
u32 gpio0, gpio1, gpio2, gpio3;
};
-typedef enum {
+enum port {
CX25821_UNDEFINED = 0,
CX25821_RAW,
CX25821_264
-} port_t;
+};
struct cx25821_board {
char *name;
- port_t porta, portb, portc;
+ enum port porta;
+ enum port portb;
+ enum port portc;
unsigned int tuner_type;
unsigned int radio_type;
unsigned char tuner_addr;
unsigned char radio_addr;
u32 clk_freq;
- struct cx25821_input input[2];
+ struct cx25821_input input[CX25821_NR_INPUT];
};
struct cx25821_subid {
@@ -308,7 +311,7 @@ struct cx25821_dev {
int _audiofile_status;
int _audio_lines_count;
int _audioframe_count;
- int _audio_upstream_channel_select;
+ int _audio_upstream_channel;
int _last_index_irq; /* The last interrupt index processed. */
__le32 *_risc_audio_jmp_addr;
diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile
index 2ee96d3973b8..dc40dde2e0c8 100644
--- a/drivers/media/video/cx25840/Makefile
+++ b/drivers/media/video/cx25840/Makefile
@@ -3,4 +3,4 @@ cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
-EXTRA_CFLAGS += -Idrivers/media/video
+ccflags-y += -Idrivers/media/video
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
index 34b96c7cfd62..005f11093642 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -480,6 +480,7 @@ void cx25840_audio_set_path(struct i2c_client *client)
static void set_volume(struct i2c_client *client, int volume)
{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
/* Convert the volume to msp3400 values (0-127) */
@@ -495,7 +496,14 @@ static void set_volume(struct i2c_client *client, int volume)
}
/* PATH1_VOLUME */
- cx25840_write(client, 0x8d4, 228 - (vol * 2));
+ if (is_cx2388x(state)) {
+ /* for cx23885 volume doesn't work,
+ * the calculation always results in
+ * e4 regardless.
+ */
+ cx25840_write(client, 0x8d4, volume);
+ } else
+ cx25840_write(client, 0x8d4, 228 - (vol * 2));
}
static void set_balance(struct i2c_client *client, int balance)
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index b7ee2ae70583..cd9976408ab3 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -702,6 +702,13 @@ static void cx231xx_initialize(struct i2c_client *client)
/* start microcontroller */
cx25840_and_or(client, 0x803, ~0x10, 0x10);
+
+ /* CC raw enable */
+ cx25840_write(client, 0x404, 0x0b);
+
+ /* CC on */
+ cx25840_write(client, 0x42f, 0x66);
+ cx25840_write4(client, 0x474, 0x1e1e601a);
}
/* ----------------------------------------------------------------------- */
@@ -1067,6 +1074,18 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
cx25840_write(client, 0x919, 0x01);
}
+ if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) ||
+ (aud_input == CX25840_AUDIO6))) {
+ /* Configure audio from LR1 or LR2 input */
+ cx25840_write4(client, 0x910, 0);
+ cx25840_write4(client, 0x8d0, 0x63073);
+ } else
+ if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) {
+ /* Configure audio from tuner/sif input */
+ cx25840_write4(client, 0x910, 0x12b000c9);
+ cx25840_write4(client, 0x8d0, 0x1f063870);
+ }
+
return 0;
}
diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c
index 7eb79af28aa3..13c380ebb562 100644
--- a/drivers/media/video/cx25840/cx25840-ir.c
+++ b/drivers/media/video/cx25840/cx25840-ir.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/kfifo.h>
+#include <linux/module.h>
#include <media/cx25840.h>
#include <media/rc-core.h>
@@ -668,7 +669,7 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
u16 divider;
unsigned int i, n;
union cx25840_ir_fifo_rec *p;
- unsigned u, v;
+ unsigned u, v, w;
if (ir_state == NULL)
return -ENODEV;
@@ -694,11 +695,12 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
/* Assume RTO was because of no IR light input */
u = 0;
- v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
+ w = 1;
} else {
u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
if (invert)
u = u ? 0 : 1;
+ w = 0;
}
v = (unsigned) pulse_width_count_to_ns(
@@ -709,9 +711,12 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
init_ir_raw_event(&p->ir_core_data);
p->ir_core_data.pulse = u;
p->ir_core_data.duration = v;
+ p->ir_core_data.timeout = w;
- v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s\n",
- v, u ? "mark" : "space");
+ v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s %s\n",
+ v, u ? "mark" : "space", w ? "(timed out)" : "");
+ if (w)
+ v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
}
return 0;
}
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile
index 5b7e26761f0a..c1a2785ba243 100644
--- a/drivers/media/video/cx88/Makefile
+++ b/drivers/media/video/cx88/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o
obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 423c1af8a782..68d1240f493c 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -113,6 +113,8 @@ MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards");
MODULE_AUTHOR("Ricardo Cerqueira");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
MODULE_SUPPORTED_DEVICE("{{Conexant,23881},"
"{{Conexant,23882},"
"{{Conexant,23883}");
@@ -973,14 +975,8 @@ static struct pci_driver cx88_audio_pci_driver = {
*/
static int __init cx88_audio_init(void)
{
- printk(KERN_INFO "cx2388x alsa driver version %d.%d.%d loaded\n",
- (CX88_VERSION_CODE >> 16) & 0xff,
- (CX88_VERSION_CODE >> 8) & 0xff,
- CX88_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
- SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+ printk(KERN_INFO "cx2388x alsa driver version %s loaded\n",
+ CX88_VERSION);
return pci_register_driver(&cx88_audio_pci_driver);
}
@@ -994,10 +990,3 @@ static void __exit cx88_audio_fini(void)
module_init(cx88_audio_init);
module_exit(cx88_audio_fini);
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index 11e49bbc4a66..e46446a449c0 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -42,6 +42,7 @@
MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards");
MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
static unsigned int mpegbufs = 32;
module_param(mpegbufs,int,0644);
@@ -730,7 +731,6 @@ static int vidioc_querycap (struct file *file, void *priv,
strcpy(cap->driver, "cx88_blackbird");
strlcpy(cap->card, core->board.name, sizeof(cap->card));
sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
- cap->version = CX88_VERSION_CODE;
cap->capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
@@ -1368,14 +1368,8 @@ static struct cx8802_driver cx8802_blackbird_driver = {
static int __init blackbird_init(void)
{
- printk(KERN_INFO "cx2388x blackbird driver version %d.%d.%d loaded\n",
- (CX88_VERSION_CODE >> 16) & 0xff,
- (CX88_VERSION_CODE >> 8) & 0xff,
- CX88_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
- SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+ printk(KERN_INFO "cx2388x blackbird driver version %s loaded\n",
+ CX88_VERSION);
return cx8802_register_driver(&cx8802_blackbird_driver);
}
@@ -1389,11 +1383,3 @@ module_exit(blackbird_fini);
module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644);
MODULE_PARM_DESC(debug,"enable debug messages [video]");
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
- */
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 27222c92b603..0d719faafd8a 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -28,6 +28,7 @@
#include "cx88.h"
#include "tea5767.h"
+#include "xc4000.h"
static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
@@ -2119,6 +2120,99 @@ static const struct cx88_board cx88_boards[] = {
},
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_WINFAST_DTV1800H_XC4000] = {
+ .name = "Leadtek WinFast DTV1800 H (XC4000)",
+ .tuner_type = TUNER_XC4000,
+ .radio_type = TUNER_XC4000,
+ .tuner_addr = 0x61,
+ .radio_addr = 0x61,
+ /*
+ * GPIO setting
+ *
+ * 2: mute (0=off,1=on)
+ * 12: tuner reset pin
+ * 13: audio source (0=tuner audio,1=line in)
+ * 14: FM (0=on,1=off ???)
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */
+ .gpio2 = 0x0000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_WINFAST_DTV2000H_PLUS] = {
+ .name = "Leadtek WinFast DTV2000 H PLUS",
+ .tuner_type = TUNER_XC4000,
+ .radio_type = TUNER_XC4000,
+ .tuner_addr = 0x61,
+ .radio_addr = 0x61,
+ /*
+ * GPIO
+ * 2: 1: mute audio
+ * 12: 0: reset XC4000
+ * 13: 1: audio input is line in (0: tuner)
+ * 14: 0: FM radio
+ * 16: 0: RF input is cable
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0403,
+ .gpio1 = 0xF0D7,
+ .gpio2 = 0x0101,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_CABLE,
+ .vmux = 0,
+ .gpio0 = 0x0403,
+ .gpio1 = 0xF0D7,
+ .gpio2 = 0x0100,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0403, /* was 0x0407 */
+ .gpio1 = 0xF0F7,
+ .gpio2 = 0x0101,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0403, /* was 0x0407 */
+ .gpio1 = 0xF0F7,
+ .gpio2 = 0x0101,
+ .gpio3 = 0x0000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0403,
+ .gpio1 = 0xF097,
+ .gpio2 = 0x0100,
+ .gpio3 = 0x0000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
[CX88_BOARD_PROF_7301] = {
.name = "Prof 7301 DVB-S/S2",
.tuner_type = UNSET,
@@ -2581,6 +2675,15 @@ static const struct cx88_subid cx88_subids[] = {
.subdevice = 0x6654,
.card = CX88_BOARD_WINFAST_DTV1800H,
}, {
+ /* WinFast DTV1800 H with XC4000 tuner */
+ .subvendor = 0x107d,
+ .subdevice = 0x6f38,
+ .card = CX88_BOARD_WINFAST_DTV1800H_XC4000,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f42,
+ .card = CX88_BOARD_WINFAST_DTV2000H_PLUS,
+ }, {
/* PVR2000 PAL Model [107d:6630] */
.subvendor = 0x107d,
.subdevice = 0x6630,
@@ -2846,6 +2949,23 @@ static int cx88_xc3028_winfast1800h_callback(struct cx88_core *core,
return -EINVAL;
}
+static int cx88_xc4000_winfast2000h_plus_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (command) {
+ case XC4000_TUNER_RESET:
+ /* GPIO 12 (xc4000 tuner reset) */
+ cx_set(MO_GP1_IO, 0x1010);
+ mdelay(50);
+ cx_clear(MO_GP1_IO, 0x10);
+ mdelay(75);
+ cx_set(MO_GP1_IO, 0x10);
+ mdelay(75);
+ return 0;
+ }
+ return -EINVAL;
+}
+
/* ------------------------------------------------------------------- */
/* some Divco specific stuff */
static int cx88_pv_8000gt_callback(struct cx88_core *core,
@@ -2948,6 +3068,19 @@ static int cx88_xc2028_tuner_callback(struct cx88_core *core,
return -EINVAL;
}
+static int cx88_xc4000_tuner_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ /* Board-specific callbacks */
+ switch (core->boardnr) {
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ return cx88_xc4000_winfast2000h_plus_callback(core,
+ command, arg);
+ }
+ return -EINVAL;
+}
+
/* ----------------------------------------------------------------------- */
/* Tuner callback function. Currently only needed for the Pinnacle *
* PCTV HD 800i with an xc5000 sillicon tuner. This is used for both *
@@ -3022,6 +3155,9 @@ int cx88_tuner_callback(void *priv, int component, int command, int arg)
case TUNER_XC2028:
info_printk(core, "Calling XC2028/3028 callback\n");
return cx88_xc2028_tuner_callback(core, command, arg);
+ case TUNER_XC4000:
+ info_printk(core, "Calling XC4000 callback\n");
+ return cx88_xc4000_tuner_callback(core, command, arg);
case TUNER_XC5000:
info_printk(core, "Calling XC5000 callback\n");
return cx88_xc5000_tuner_callback(core, command, arg);
@@ -3109,13 +3245,13 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core)
case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
case CX88_BOARD_WINFAST_DTV1800H:
- /* GPIO 12 (xc3028 tuner reset) */
- cx_set(MO_GP1_IO, 0x1010);
- mdelay(50);
- cx_clear(MO_GP1_IO, 0x10);
- mdelay(50);
- cx_set(MO_GP1_IO, 0x10);
- mdelay(50);
+ cx88_xc3028_winfast1800h_callback(core, XC2028_TUNER_RESET, 0);
+ break;
+
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ cx88_xc4000_winfast2000h_plus_callback(core,
+ XC4000_TUNER_RESET, 0);
break;
case CX88_BOARD_TWINHAN_VP1027_DVBS:
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index 2e145f0a5fd9..fbfdd8067937 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -759,8 +759,8 @@ int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int heig
if (nocomb)
value |= (3 << 5); // disable comb filter
- cx_write(MO_FILTER_EVEN, value);
- cx_write(MO_FILTER_ODD, value);
+ cx_andor(MO_FILTER_EVEN, 0x7ffc7f, value); /* preserve PEAKEN, PSEL */
+ cx_andor(MO_FILTER_ODD, 0x7ffc7f, value);
dprintk(1,"set_scale: filter 0x%04x\n", value);
return 0;
@@ -994,10 +994,10 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
// htotal
tmp64 = norm_htotal(norm) * (u64)vdec_clock;
do_div(tmp64, fsc8);
- htotal = (u32)tmp64 | (HLNotchFilter4xFsc << 11);
+ htotal = (u32)tmp64;
dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
htotal, cx_read(MO_HTOTAL), (u32)tmp64);
- cx_write(MO_HTOTAL, htotal);
+ cx_andor(MO_HTOTAL, 0x07ff, htotal);
// vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
// the effective vbi offset ~244 samples, the same as the Bt8x8
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index c69df7ebb6a7..cf3d33ab541b 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -41,6 +41,7 @@
#include "or51132.h"
#include "lgdt330x.h"
#include "s5h1409.h"
+#include "xc4000.h"
#include "xc5000.h"
#include "nxt200x.h"
#include "cx24123.h"
@@ -63,6 +64,7 @@ MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
static unsigned int debug;
module_param(debug, int, 0644);
@@ -605,6 +607,39 @@ static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
return 0;
}
+static int attach_xc4000(struct cx8802_dev *dev, struct xc4000_config *cfg)
+{
+ struct dvb_frontend *fe;
+ struct videobuf_dvb_frontend *fe0 = NULL;
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ if (!fe0)
+ return -EINVAL;
+
+ if (!fe0->dvb.frontend) {
+ printk(KERN_ERR "%s/2: dvb frontend not attached. "
+ "Can't attach xc4000\n",
+ dev->core->name);
+ return -EINVAL;
+ }
+
+ fe = dvb_attach(xc4000_attach, fe0->dvb.frontend, &dev->core->i2c_adap,
+ cfg);
+ if (!fe) {
+ printk(KERN_ERR "%s/2: xc4000 attach failed\n",
+ dev->core->name);
+ dvb_frontend_detach(fe0->dvb.frontend);
+ dvb_unregister_frontend(fe0->dvb.frontend);
+ fe0->dvb.frontend = NULL;
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "%s/2: xc4000 attached\n", dev->core->name);
+
+ return 0;
+}
+
static int cx24116_set_ts_param(struct dvb_frontend *fe,
int is_punctured)
{
@@ -1294,7 +1329,25 @@ static int dvb_register(struct cx8802_dev *dev)
goto frontend_detach;
}
break;
- case CX88_BOARD_GENIATECH_X8000_MT:
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &cx88_pinnacle_hybrid_pctv,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ struct xc4000_config cfg = {
+ .i2c_address = 0x61,
+ .default_pm = 0,
+ .dvb_amplitude = 134,
+ .set_smoothedcvbs = 1,
+ .if_khz = 4560
+ };
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc4000(dev, &cfg) < 0)
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_GENIATECH_X8000_MT:
dev->ts_gen_cntrl = 0x00;
fe0->dvb.frontend = dvb_attach(zl10353_attach,
@@ -1577,6 +1630,11 @@ static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv)
udelay(1000);
break;
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ /* set RF input to AIR for DVB-T (GPIO 16) */
+ cx_write(MO_GP2_IO, 0x0101);
+ break;
+
default:
err = -ENODEV;
}
@@ -1692,14 +1750,8 @@ static struct cx8802_driver cx8802_dvb_driver = {
static int __init dvb_init(void)
{
- printk(KERN_INFO "cx88/2: cx2388x dvb driver version %d.%d.%d loaded\n",
- (CX88_VERSION_CODE >> 16) & 0xff,
- (CX88_VERSION_CODE >> 8) & 0xff,
- CX88_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
- SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+ printk(KERN_INFO "cx88/2: cx2388x dvb driver version %s loaded\n",
+ CX88_VERSION);
return cx8802_register_driver(&cx8802_dvb_driver);
}
@@ -1710,10 +1762,3 @@ static void __exit dvb_fini(void)
module_init(dvb_init);
module_exit(dvb_fini);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * compile-command: "make DVB=1"
- * End:
- */
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index 3f442003623d..e614201b5ed3 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -100,6 +100,8 @@ static void cx88_ir_handle_key(struct cx88_IR *ir)
break;
case CX88_BOARD_WINFAST_DTV1000:
case CX88_BOARD_WINFAST_DTV1800H:
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900);
auxgpio = gpio;
@@ -289,6 +291,8 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
case CX88_BOARD_WINFAST_DTV2000H:
case CX88_BOARD_WINFAST_DTV2000H_J:
case CX88_BOARD_WINFAST_DTV1800H:
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
ir_codes = RC_MAP_WINFAST;
ir->gpio_addr = MO_GP0_IO;
ir->mask_keycode = 0x8f8;
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 1a7b983f8297..cd5386ee210c 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -39,6 +39,7 @@ MODULE_AUTHOR("Jelle Foks <jelle@foks.us>");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
static unsigned int debug;
module_param(debug,int,0644);
@@ -613,13 +614,17 @@ static int cx8802_request_acquire(struct cx8802_driver *drv)
core->active_type_id != drv->type_id)
return -EBUSY;
- core->input = 0;
- for (i = 0;
- i < (sizeof(core->board.input) / sizeof(struct cx88_input));
- i++) {
- if (core->board.input[i].type == CX88_VMUX_DVB) {
- core->input = i;
- break;
+ if (drv->type_id == CX88_MPEG_DVB) {
+ /* When switching to DVB, always set the input to the tuner */
+ core->last_analog_input = core->input;
+ core->input = 0;
+ for (i = 0;
+ i < (sizeof(core->board.input) / sizeof(struct cx88_input));
+ i++) {
+ if (core->board.input[i].type == CX88_VMUX_DVB) {
+ core->input = i;
+ break;
+ }
}
}
@@ -644,6 +649,12 @@ static int cx8802_request_release(struct cx8802_driver *drv)
if (drv->advise_release && --core->active_ref == 0)
{
+ if (drv->type_id == CX88_MPEG_DVB) {
+ /* If the DVB driver is releasing, reset the input
+ state to the last configured analog input */
+ core->input = core->last_analog_input;
+ }
+
drv->advise_release(drv);
core->active_type_id = CX88_BOARD_NONE;
mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO));
@@ -890,14 +901,8 @@ static struct pci_driver cx8802_pci_driver = {
static int __init cx8802_init(void)
{
- printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %d.%d.%d loaded\n",
- (CX88_VERSION_CODE >> 16) & 0xff,
- (CX88_VERSION_CODE >> 8) & 0xff,
- CX88_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
- SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+ printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %s loaded\n",
+ CX88_VERSION);
return pci_register_driver(&cx8802_pci_driver);
}
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index cef4f282e5aa..921c56d115d6 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -45,6 +45,7 @@
MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
/* ------------------------------------------------------------------ */
@@ -220,7 +221,23 @@ static const struct cx88_ctrl cx8800_ctls[] = {
.reg = MO_UV_SATURATION,
.mask = 0x00ff,
.shift = 0,
- },{
+ }, {
+ .v = {
+ .id = V4L2_CID_SHARPNESS,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 4,
+ .step = 1,
+ .default_value = 0x0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ /* NOTE: the value is converted and written to both even
+ and odd registers in the code */
+ .reg = MO_FILTER_ODD,
+ .mask = 7 << 7,
+ .shift = 7,
+ }, {
.v = {
.id = V4L2_CID_CHROMA_AGC,
.name = "Chroma AGC",
@@ -245,6 +262,20 @@ static const struct cx88_ctrl cx8800_ctls[] = {
.mask = 1 << 9,
.shift = 9,
}, {
+ .v = {
+ .id = V4L2_CID_BAND_STOP_FILTER,
+ .name = "Notch filter",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0x0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = MO_HTOTAL,
+ .mask = 3 << 11,
+ .shift = 11,
+ }, {
/* --- audio --- */
.v = {
.id = V4L2_CID_AUDIO_MUTE,
@@ -300,8 +331,10 @@ const u32 cx88_user_ctrls[] = {
V4L2_CID_AUDIO_VOLUME,
V4L2_CID_AUDIO_BALANCE,
V4L2_CID_AUDIO_MUTE,
+ V4L2_CID_SHARPNESS,
V4L2_CID_CHROMA_AGC,
V4L2_CID_COLOR_KILLER,
+ V4L2_CID_BAND_STOP_FILTER,
0
};
EXPORT_SYMBOL(cx88_user_ctrls);
@@ -962,6 +995,10 @@ int cx88_get_control (struct cx88_core *core, struct v4l2_control *ctl)
case V4L2_CID_AUDIO_VOLUME:
ctl->value = 0x3f - (value & 0x3f);
break;
+ case V4L2_CID_SHARPNESS:
+ ctl->value = ((value & 0x0200) ? (((value & 0x0180) >> 7) + 1)
+ : 0);
+ break;
default:
ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
break;
@@ -1039,6 +1076,12 @@ int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl)
}
mask=0xffff;
break;
+ case V4L2_CID_SHARPNESS:
+ /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
+ value = (ctl->value < 1 ? 0 : ((ctl->value + 3) << 7));
+ /* needs to be set for both fields */
+ cx_andor(MO_FILTER_EVEN, mask, value);
+ break;
case V4L2_CID_CHROMA_AGC:
/* Do not allow chroma AGC to be enabled for SECAM */
value = ((ctl->value - c->off) << c->shift) & c->mask;
@@ -1161,7 +1204,6 @@ static int vidioc_querycap (struct file *file, void *priv,
strcpy(cap->driver, "cx8800");
strlcpy(cap->card, core->board.name, sizeof(cap->card));
sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
- cap->version = CX88_VERSION_CODE;
cap->capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
@@ -1480,7 +1522,6 @@ static int radio_querycap (struct file *file, void *priv,
strcpy(cap->driver, "cx8800");
strlcpy(cap->card, core->board.name, sizeof(cap->card));
sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci));
- cap->version = CX88_VERSION_CODE;
cap->capabilities = V4L2_CAP_TUNER;
return 0;
}
@@ -2139,14 +2180,8 @@ static struct pci_driver cx8800_pci_driver = {
static int __init cx8800_init(void)
{
- printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %d.%d.%d loaded\n",
- (CX88_VERSION_CODE >> 16) & 0xff,
- (CX88_VERSION_CODE >> 8) & 0xff,
- CX88_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
- SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+ printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %s loaded\n",
+ CX88_VERSION);
return pci_register_driver(&cx8800_pci_driver);
}
@@ -2157,11 +2192,3 @@ static void __exit cx8800_fini(void)
module_init(cx8800_init);
module_exit(cx8800_fini);
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
- */
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index a399a8b086ba..fa8d307e1a3d 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -39,9 +39,9 @@
#include "cx88-reg.h"
#include "tuner-xc2028.h"
-#include <linux/version.h>
#include <linux/mutex.h>
-#define CX88_VERSION_CODE KERNEL_VERSION(0, 0, 8)
+
+#define CX88_VERSION "0.0.9"
#define UNSET (-1U)
@@ -242,6 +242,8 @@ extern const struct sram_channel const cx88_sram_channels[];
#define CX88_BOARD_SAMSUNG_SMT_7020 84
#define CX88_BOARD_TWINHAN_VP1027_DVBS 85
#define CX88_BOARD_TEVII_S464 86
+#define CX88_BOARD_WINFAST_DTV2000H_PLUS 87
+#define CX88_BOARD_WINFAST_DTV1800H_XC4000 88
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
@@ -375,6 +377,7 @@ struct cx88_core {
u32 audiomode_manual;
u32 audiomode_current;
u32 input;
+ u32 last_analog_input;
u32 astat;
u32 use_nicam;
unsigned long last_change;
diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig
index 6b1954035649..60a456ebdc7c 100644
--- a/drivers/media/video/davinci/Kconfig
+++ b/drivers/media/video/davinci/Kconfig
@@ -91,3 +91,26 @@ config VIDEO_ISIF
To compile this driver as a module, choose M here: the
module will be called vpfe.
+
+config VIDEO_DM644X_VPBE
+ tristate "DM644X VPBE HW module"
+ depends on ARCH_DAVINCI_DM644x
+ select VIDEO_VPSS_SYSTEM
+ select VIDEOBUF_DMA_CONTIG
+ help
+ Enables VPBE modules used for display on a DM644x
+ SoC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpbe.
+
+
+config VIDEO_VPBE_DISPLAY
+ tristate "VPBE V4L2 Display driver"
+ depends on ARCH_DAVINCI_DM644x
+ select VIDEO_DM644X_VPBE
+ help
+ Enables VPBE V4L2 Display driver on a DM644x device
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpbe_display.
diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile
index a37955745aaa..ae7dafb689ab 100644
--- a/drivers/media/video/davinci/Makefile
+++ b/drivers/media/video/davinci/Makefile
@@ -16,3 +16,5 @@ obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o
obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o
obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o
obj-$(CONFIG_VIDEO_ISIF) += isif.o
+obj-$(CONFIG_VIDEO_DM644X_VPBE) += vpbe.o vpbe_osd.o vpbe_venc.o
+obj-$(CONFIG_VIDEO_VPBE_DISPLAY) += vpbe_display.o
diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c
index c29ac88ffd78..bd443ee76fff 100644
--- a/drivers/media/video/davinci/dm355_ccdc.c
+++ b/drivers/media/video/davinci/dm355_ccdc.c
@@ -39,6 +39,7 @@
#include <linux/videodev2.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/module.h>
#include <media/davinci/dm355_ccdc.h>
#include <media/davinci/vpss.h>
diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c
index c8b32c1c7386..8051c2956478 100644
--- a/drivers/media/video/davinci/dm644x_ccdc.c
+++ b/drivers/media/video/davinci/dm644x_ccdc.c
@@ -40,6 +40,7 @@
#include <linux/gfp.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/module.h>
#include <media/davinci/dm644x_ccdc.h>
#include <media/davinci/vpss.h>
diff --git a/drivers/media/video/davinci/vpbe.c b/drivers/media/video/davinci/vpbe.c
new file mode 100644
index 000000000000..d773d30de221
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe.c
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe.h>
+#include <media/davinci/vpss.h>
+#include <media/davinci/vpbe_venc.h>
+
+#define VPBE_DEFAULT_OUTPUT "Composite"
+#define VPBE_DEFAULT_MODE "ntsc"
+
+static char *def_output = VPBE_DEFAULT_OUTPUT;
+static char *def_mode = VPBE_DEFAULT_MODE;
+static int debug;
+
+module_param(def_output, charp, S_IRUGO);
+module_param(def_mode, charp, S_IRUGO);
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(def_output, "vpbe output name (default:Composite)");
+MODULE_PARM_DESC(def_mode, "vpbe output mode name (default:ntsc");
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+MODULE_DESCRIPTION("TI DMXXX VPBE Display controller");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
+
+/**
+ * vpbe_current_encoder_info - Get config info for current encoder
+ * @vpbe_dev - vpbe device ptr
+ *
+ * Return ptr to current encoder config info
+ */
+static struct encoder_config_info*
+vpbe_current_encoder_info(struct vpbe_device *vpbe_dev)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int index = vpbe_dev->current_sd_index;
+
+ return ((index == 0) ? &cfg->venc :
+ &cfg->ext_encoders[index-1]);
+}
+
+/**
+ * vpbe_find_encoder_sd_index - Given a name find encoder sd index
+ *
+ * @vpbe_config - ptr to vpbe cfg
+ * @output_index - index used by application
+ *
+ * Return sd index of the encoder
+ */
+static int vpbe_find_encoder_sd_index(struct vpbe_config *cfg,
+ int index)
+{
+ char *encoder_name = cfg->outputs[index].subdev_name;
+ int i;
+
+ /* Venc is always first */
+ if (!strcmp(encoder_name, cfg->venc.module_name))
+ return 0;
+
+ for (i = 0; i < cfg->num_ext_encoders; i++) {
+ if (!strcmp(encoder_name,
+ cfg->ext_encoders[i].module_name))
+ return i+1;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_g_cropcap - Get crop capabilities of the display
+ * @vpbe_dev - vpbe device ptr
+ * @cropcap - cropcap is a ptr to struct v4l2_cropcap
+ *
+ * Update the crop capabilities in crop cap for current
+ * mode
+ */
+static int vpbe_g_cropcap(struct vpbe_device *vpbe_dev,
+ struct v4l2_cropcap *cropcap)
+{
+ if (NULL == cropcap)
+ return -EINVAL;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.top = 0;
+ cropcap->bounds.width = vpbe_dev->current_timings.xres;
+ cropcap->bounds.height = vpbe_dev->current_timings.yres;
+ cropcap->defrect = cropcap->bounds;
+
+ return 0;
+}
+
+/**
+ * vpbe_enum_outputs - enumerate outputs
+ * @vpbe_dev - vpbe device ptr
+ * @output - ptr to v4l2_output structure
+ *
+ * Enumerates the outputs available at the vpbe display
+ * returns the status, -EINVAL if end of output list
+ */
+static int vpbe_enum_outputs(struct vpbe_device *vpbe_dev,
+ struct v4l2_output *output)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int temp_index = output->index;
+
+ if (temp_index >= cfg->num_outputs)
+ return -EINVAL;
+
+ *output = cfg->outputs[temp_index].output;
+ output->index = temp_index;
+
+ return 0;
+}
+
+static int vpbe_get_mode_info(struct vpbe_device *vpbe_dev, char *mode)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct vpbe_enc_mode_info var;
+ int curr_output = vpbe_dev->current_out_index;
+ int i;
+
+ if (NULL == mode)
+ return -EINVAL;
+
+ for (i = 0; i < cfg->outputs[curr_output].num_modes; i++) {
+ var = cfg->outputs[curr_output].modes[i];
+ if (!strcmp(mode, var.name)) {
+ vpbe_dev->current_timings = var;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int vpbe_get_current_mode_info(struct vpbe_device *vpbe_dev,
+ struct vpbe_enc_mode_info *mode_info)
+{
+ if (NULL == mode_info)
+ return -EINVAL;
+
+ *mode_info = vpbe_dev->current_timings;
+
+ return 0;
+}
+
+static int vpbe_get_dv_preset_info(struct vpbe_device *vpbe_dev,
+ unsigned int dv_preset)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct vpbe_enc_mode_info var;
+ int curr_output = vpbe_dev->current_out_index;
+ int i;
+
+ for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
+ var = cfg->outputs[curr_output].modes[i];
+ if ((var.timings_type & VPBE_ENC_DV_PRESET) &&
+ (var.timings.dv_preset == dv_preset)) {
+ vpbe_dev->current_timings = var;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/* Get std by std id */
+static int vpbe_get_std_info(struct vpbe_device *vpbe_dev,
+ v4l2_std_id std_id)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct vpbe_enc_mode_info var;
+ int curr_output = vpbe_dev->current_out_index;
+ int i;
+
+ for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
+ var = cfg->outputs[curr_output].modes[i];
+ if ((var.timings_type & VPBE_ENC_STD) &&
+ (var.timings.std_id & std_id)) {
+ vpbe_dev->current_timings = var;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int vpbe_get_std_info_by_name(struct vpbe_device *vpbe_dev,
+ char *std_name)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct vpbe_enc_mode_info var;
+ int curr_output = vpbe_dev->current_out_index;
+ int i;
+
+ for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
+ var = cfg->outputs[curr_output].modes[i];
+ if (!strcmp(var.name, std_name)) {
+ vpbe_dev->current_timings = var;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_set_output - Set output
+ * @vpbe_dev - vpbe device ptr
+ * @index - index of output
+ *
+ * Set vpbe output to the output specified by the index
+ */
+static int vpbe_set_output(struct vpbe_device *vpbe_dev, int index)
+{
+ struct encoder_config_info *curr_enc_info =
+ vpbe_current_encoder_info(vpbe_dev);
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int enc_out_index;
+ int sd_index;
+ int ret = 0;
+
+ if (index >= cfg->num_outputs)
+ return -EINVAL;
+
+ mutex_lock(&vpbe_dev->lock);
+
+ sd_index = vpbe_dev->current_sd_index;
+ enc_out_index = cfg->outputs[index].output.index;
+ /*
+ * Currently we switch the encoder based on output selected
+ * by the application. If media controller is implemented later
+ * there is will be an API added to setup_link between venc
+ * and external encoder. So in that case below comparison always
+ * match and encoder will not be switched. But if application
+ * chose not to use media controller, then this provides current
+ * way of switching encoder at the venc output.
+ */
+ if (strcmp(curr_enc_info->module_name,
+ cfg->outputs[index].subdev_name)) {
+ /* Need to switch the encoder at the output */
+ sd_index = vpbe_find_encoder_sd_index(cfg, index);
+ if (sd_index < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (ret)
+ goto out;
+ }
+
+ /* Set output at the encoder */
+ ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
+ s_routing, 0, enc_out_index, 0);
+ if (ret)
+ goto out;
+
+ /*
+ * It is assumed that venc or extenal encoder will set a default
+ * mode in the sub device. For external encoder or LCD pannel output,
+ * we also need to set up the lcd port for the required mode. So setup
+ * the lcd port for the default mode that is configured in the board
+ * arch/arm/mach-davinci/board-dm355-evm.setup file for the external
+ * encoder.
+ */
+ ret = vpbe_get_mode_info(vpbe_dev,
+ cfg->outputs[index].default_mode);
+ if (!ret) {
+ struct osd_state *osd_device = vpbe_dev->osd_device;
+
+ osd_device->ops.set_left_margin(osd_device,
+ vpbe_dev->current_timings.left_margin);
+ osd_device->ops.set_top_margin(osd_device,
+ vpbe_dev->current_timings.upper_margin);
+ vpbe_dev->current_sd_index = sd_index;
+ vpbe_dev->current_out_index = index;
+ }
+out:
+ mutex_unlock(&vpbe_dev->lock);
+ return ret;
+}
+
+static int vpbe_set_default_output(struct vpbe_device *vpbe_dev)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < cfg->num_outputs; i++) {
+ if (!strcmp(def_output,
+ cfg->outputs[i].output.name)) {
+ ret = vpbe_set_output(vpbe_dev, i);
+ if (!ret)
+ vpbe_dev->current_out_index = i;
+ return ret;
+ }
+ }
+ return ret;
+}
+
+/**
+ * vpbe_get_output - Get output
+ * @vpbe_dev - vpbe device ptr
+ *
+ * return current vpbe output to the the index
+ */
+static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev)
+{
+ return vpbe_dev->current_out_index;
+}
+
+/**
+ * vpbe_s_dv_preset - Set the given preset timings in the encoder
+ *
+ * Sets the preset if supported by the current encoder. Return the status.
+ * 0 - success & -EINVAL on error
+ */
+static int vpbe_s_dv_preset(struct vpbe_device *vpbe_dev,
+ struct v4l2_dv_preset *dv_preset)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int out_index = vpbe_dev->current_out_index;
+ int sd_index = vpbe_dev->current_sd_index;
+ int ret;
+
+
+ if (!(cfg->outputs[out_index].output.capabilities &
+ V4L2_OUT_CAP_PRESETS))
+ return -EINVAL;
+
+ ret = vpbe_get_dv_preset_info(vpbe_dev, dv_preset->preset);
+
+ if (ret)
+ return ret;
+
+ mutex_lock(&vpbe_dev->lock);
+
+
+ ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
+ s_dv_preset, dv_preset);
+ /* set the lcd controller output for the given mode */
+ if (!ret) {
+ struct osd_state *osd_device = vpbe_dev->osd_device;
+
+ osd_device->ops.set_left_margin(osd_device,
+ vpbe_dev->current_timings.left_margin);
+ osd_device->ops.set_top_margin(osd_device,
+ vpbe_dev->current_timings.upper_margin);
+ }
+ mutex_unlock(&vpbe_dev->lock);
+
+ return ret;
+}
+
+/**
+ * vpbe_g_dv_preset - Get the preset in the current encoder
+ *
+ * Get the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_g_dv_preset(struct vpbe_device *vpbe_dev,
+ struct v4l2_dv_preset *dv_preset)
+{
+ if (vpbe_dev->current_timings.timings_type &
+ VPBE_ENC_DV_PRESET) {
+ dv_preset->preset = vpbe_dev->current_timings.timings.dv_preset;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_enum_dv_presets - Enumerate the dv presets in the current encoder
+ *
+ * Get the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_enum_dv_presets(struct vpbe_device *vpbe_dev,
+ struct v4l2_dv_enum_preset *preset_info)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int out_index = vpbe_dev->current_out_index;
+ struct vpbe_output *output = &cfg->outputs[out_index];
+ int j = 0;
+ int i;
+
+ if (!(output->output.capabilities & V4L2_OUT_CAP_PRESETS))
+ return -EINVAL;
+
+ for (i = 0; i < output->num_modes; i++) {
+ if (output->modes[i].timings_type == VPBE_ENC_DV_PRESET) {
+ if (j == preset_info->index)
+ break;
+ j++;
+ }
+ }
+
+ if (i == output->num_modes)
+ return -EINVAL;
+
+ return v4l_fill_dv_preset_info(output->modes[i].timings.dv_preset,
+ preset_info);
+}
+
+/**
+ * vpbe_s_std - Set the given standard in the encoder
+ *
+ * Sets the standard if supported by the current encoder. Return the status.
+ * 0 - success & -EINVAL on error
+ */
+static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int out_index = vpbe_dev->current_out_index;
+ int sd_index = vpbe_dev->current_sd_index;
+ int ret;
+
+ if (!(cfg->outputs[out_index].output.capabilities &
+ V4L2_OUT_CAP_STD))
+ return -EINVAL;
+
+ ret = vpbe_get_std_info(vpbe_dev, *std_id);
+ if (ret)
+ return ret;
+
+ mutex_lock(&vpbe_dev->lock);
+
+ ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
+ s_std_output, *std_id);
+ /* set the lcd controller output for the given mode */
+ if (!ret) {
+ struct osd_state *osd_device = vpbe_dev->osd_device;
+
+ osd_device->ops.set_left_margin(osd_device,
+ vpbe_dev->current_timings.left_margin);
+ osd_device->ops.set_top_margin(osd_device,
+ vpbe_dev->current_timings.upper_margin);
+ }
+ mutex_unlock(&vpbe_dev->lock);
+
+ return ret;
+}
+
+/**
+ * vpbe_g_std - Get the standard in the current encoder
+ *
+ * Get the standard in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
+{
+ struct vpbe_enc_mode_info cur_timings = vpbe_dev->current_timings;
+
+ if (cur_timings.timings_type & VPBE_ENC_STD) {
+ *std_id = cur_timings.timings.std_id;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_set_mode - Set mode in the current encoder using mode info
+ *
+ * Use the mode string to decide what timings to set in the encoder
+ * This is typically useful when fbset command is used to change the current
+ * timings by specifying a string to indicate the timings.
+ */
+static int vpbe_set_mode(struct vpbe_device *vpbe_dev,
+ struct vpbe_enc_mode_info *mode_info)
+{
+ struct vpbe_enc_mode_info *preset_mode = NULL;
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct v4l2_dv_preset dv_preset;
+ struct osd_state *osd_device;
+ int out_index = vpbe_dev->current_out_index;
+ int ret = 0;
+ int i;
+
+ if ((NULL == mode_info) || (NULL == mode_info->name))
+ return -EINVAL;
+
+ for (i = 0; i < cfg->outputs[out_index].num_modes; i++) {
+ if (!strcmp(mode_info->name,
+ cfg->outputs[out_index].modes[i].name)) {
+ preset_mode = &cfg->outputs[out_index].modes[i];
+ /*
+ * it may be one of the 3 timings type. Check and
+ * invoke right API
+ */
+ if (preset_mode->timings_type & VPBE_ENC_STD)
+ return vpbe_s_std(vpbe_dev,
+ &preset_mode->timings.std_id);
+ if (preset_mode->timings_type & VPBE_ENC_DV_PRESET) {
+ dv_preset.preset =
+ preset_mode->timings.dv_preset;
+ return vpbe_s_dv_preset(vpbe_dev, &dv_preset);
+ }
+ }
+ }
+
+ /* Only custom timing should reach here */
+ if (preset_mode == NULL)
+ return -EINVAL;
+
+ mutex_lock(&vpbe_dev->lock);
+
+ osd_device = vpbe_dev->osd_device;
+ vpbe_dev->current_timings = *preset_mode;
+ osd_device->ops.set_left_margin(osd_device,
+ vpbe_dev->current_timings.left_margin);
+ osd_device->ops.set_top_margin(osd_device,
+ vpbe_dev->current_timings.upper_margin);
+
+ mutex_unlock(&vpbe_dev->lock);
+
+ return ret;
+}
+
+static int vpbe_set_default_mode(struct vpbe_device *vpbe_dev)
+{
+ int ret;
+
+ ret = vpbe_get_std_info_by_name(vpbe_dev, def_mode);
+ if (ret)
+ return ret;
+
+ /* set the default mode in the encoder */
+ return vpbe_set_mode(vpbe_dev, &vpbe_dev->current_timings);
+}
+
+static int platform_device_get(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vpbe_device *vpbe_dev = data;
+
+ if (strcmp("vpbe-osd", pdev->name) == 0)
+ vpbe_dev->osd_device = platform_get_drvdata(pdev);
+
+ return 0;
+}
+
+/**
+ * vpbe_initialize() - Initialize the vpbe display controller
+ * @vpbe_dev - vpbe device ptr
+ *
+ * Master frame buffer device drivers calls this to initialize vpbe
+ * display controller. This will then registers v4l2 device and the sub
+ * devices and sets a current encoder sub device for display. v4l2 display
+ * device driver is the master and frame buffer display device driver is
+ * the slave. Frame buffer display driver checks the initialized during
+ * probe and exit if not initialized. Returns status.
+ */
+static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev)
+{
+ struct encoder_config_info *enc_info;
+ struct v4l2_subdev **enc_subdev;
+ struct osd_state *osd_device;
+ struct i2c_adapter *i2c_adap;
+ int output_index;
+ int num_encoders;
+ int ret = 0;
+ int err;
+ int i;
+
+ /*
+ * v4l2 abd FBDev frame buffer devices will get the vpbe_dev pointer
+ * from the platform device by iteration of platform drivers and
+ * matching with device name
+ */
+ if (NULL == vpbe_dev || NULL == dev) {
+ printk(KERN_ERR "Null device pointers.\n");
+ return -ENODEV;
+ }
+
+ if (vpbe_dev->initialized)
+ return 0;
+
+ mutex_lock(&vpbe_dev->lock);
+
+ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
+ /* We have dac clock available for platform */
+ vpbe_dev->dac_clk = clk_get(vpbe_dev->pdev, "vpss_dac");
+ if (IS_ERR(vpbe_dev->dac_clk)) {
+ ret = PTR_ERR(vpbe_dev->dac_clk);
+ goto vpbe_unlock;
+ }
+ if (clk_enable(vpbe_dev->dac_clk)) {
+ ret = -ENODEV;
+ goto vpbe_unlock;
+ }
+ }
+
+ /* first enable vpss clocks */
+ vpss_enable_clock(VPSS_VPBE_CLOCK, 1);
+
+ /* First register a v4l2 device */
+ ret = v4l2_device_register(dev, &vpbe_dev->v4l2_dev);
+ if (ret) {
+ v4l2_err(dev->driver,
+ "Unable to register v4l2 device.\n");
+ goto vpbe_fail_clock;
+ }
+ v4l2_info(&vpbe_dev->v4l2_dev, "vpbe v4l2 device registered\n");
+
+ err = bus_for_each_dev(&platform_bus_type, NULL, vpbe_dev,
+ platform_device_get);
+ if (err < 0)
+ return err;
+
+ vpbe_dev->venc = venc_sub_dev_init(&vpbe_dev->v4l2_dev,
+ vpbe_dev->cfg->venc.module_name);
+ /* register venc sub device */
+ if (vpbe_dev->venc == NULL) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "vpbe unable to init venc sub device\n");
+ ret = -ENODEV;
+ goto vpbe_fail_v4l2_device;
+ }
+ /* initialize osd device */
+ osd_device = vpbe_dev->osd_device;
+
+ if (NULL != osd_device->ops.initialize) {
+ err = osd_device->ops.initialize(osd_device);
+ if (err) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "unable to initialize the OSD device");
+ err = -ENOMEM;
+ goto vpbe_fail_v4l2_device;
+ }
+ }
+
+ /*
+ * Register any external encoders that are configured. At index 0 we
+ * store venc sd index.
+ */
+ num_encoders = vpbe_dev->cfg->num_ext_encoders + 1;
+ vpbe_dev->encoders = kmalloc(
+ sizeof(struct v4l2_subdev *)*num_encoders,
+ GFP_KERNEL);
+ if (NULL == vpbe_dev->encoders) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "unable to allocate memory for encoders sub devices");
+ ret = -ENOMEM;
+ goto vpbe_fail_v4l2_device;
+ }
+
+ i2c_adap = i2c_get_adapter(vpbe_dev->cfg->i2c_adapter_id);
+ for (i = 0; i < (vpbe_dev->cfg->num_ext_encoders + 1); i++) {
+ if (i == 0) {
+ /* venc is at index 0 */
+ enc_subdev = &vpbe_dev->encoders[i];
+ *enc_subdev = vpbe_dev->venc;
+ continue;
+ }
+ enc_info = &vpbe_dev->cfg->ext_encoders[i];
+ if (enc_info->is_i2c) {
+ enc_subdev = &vpbe_dev->encoders[i];
+ *enc_subdev = v4l2_i2c_new_subdev_board(
+ &vpbe_dev->v4l2_dev, i2c_adap,
+ &enc_info->board_info, NULL);
+ if (*enc_subdev)
+ v4l2_info(&vpbe_dev->v4l2_dev,
+ "v4l2 sub device %s registered\n",
+ enc_info->module_name);
+ else {
+ v4l2_err(&vpbe_dev->v4l2_dev, "encoder %s"
+ " failed to register",
+ enc_info->module_name);
+ ret = -ENODEV;
+ goto vpbe_fail_sd_register;
+ }
+ } else
+ v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c encoders"
+ " currently not supported");
+ }
+
+ /* set the current encoder and output to that of venc by default */
+ vpbe_dev->current_sd_index = 0;
+ vpbe_dev->current_out_index = 0;
+ output_index = 0;
+
+ mutex_unlock(&vpbe_dev->lock);
+
+ printk(KERN_NOTICE "Setting default output to %s\n", def_output);
+ ret = vpbe_set_default_output(vpbe_dev);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default output %s",
+ def_output);
+ return ret;
+ }
+
+ printk(KERN_NOTICE "Setting default mode to %s\n", def_mode);
+ ret = vpbe_set_default_mode(vpbe_dev);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default mode %s",
+ def_mode);
+ return ret;
+ }
+ vpbe_dev->initialized = 1;
+ /* TBD handling of bootargs for default output and mode */
+ return 0;
+
+vpbe_fail_sd_register:
+ kfree(vpbe_dev->encoders);
+vpbe_fail_v4l2_device:
+ v4l2_device_unregister(&vpbe_dev->v4l2_dev);
+vpbe_fail_clock:
+ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0)
+ clk_put(vpbe_dev->dac_clk);
+vpbe_unlock:
+ mutex_unlock(&vpbe_dev->lock);
+ return ret;
+}
+
+/**
+ * vpbe_deinitialize() - de-initialize the vpbe display controller
+ * @dev - Master and slave device ptr
+ *
+ * vpbe_master and slave frame buffer devices calls this to de-initialize
+ * the display controller. It is called when master and slave device
+ * driver modules are removed and no longer requires the display controller.
+ */
+static void vpbe_deinitialize(struct device *dev, struct vpbe_device *vpbe_dev)
+{
+ v4l2_device_unregister(&vpbe_dev->v4l2_dev);
+ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0)
+ clk_put(vpbe_dev->dac_clk);
+
+ kfree(vpbe_dev->encoders);
+ vpbe_dev->initialized = 0;
+ /* disable vpss clocks */
+ vpss_enable_clock(VPSS_VPBE_CLOCK, 0);
+}
+
+static struct vpbe_device_ops vpbe_dev_ops = {
+ .g_cropcap = vpbe_g_cropcap,
+ .enum_outputs = vpbe_enum_outputs,
+ .set_output = vpbe_set_output,
+ .get_output = vpbe_get_output,
+ .s_dv_preset = vpbe_s_dv_preset,
+ .g_dv_preset = vpbe_g_dv_preset,
+ .enum_dv_presets = vpbe_enum_dv_presets,
+ .s_std = vpbe_s_std,
+ .g_std = vpbe_g_std,
+ .initialize = vpbe_initialize,
+ .deinitialize = vpbe_deinitialize,
+ .get_mode_info = vpbe_get_current_mode_info,
+ .set_mode = vpbe_set_mode,
+};
+
+static __devinit int vpbe_probe(struct platform_device *pdev)
+{
+ struct vpbe_device *vpbe_dev;
+ struct vpbe_config *cfg;
+ int ret = -EINVAL;
+
+ if (pdev->dev.platform_data == NULL) {
+ v4l2_err(pdev->dev.driver, "No platform data\n");
+ return -ENODEV;
+ }
+ cfg = pdev->dev.platform_data;
+
+ if (!cfg->module_name[0] ||
+ !cfg->osd.module_name[0] ||
+ !cfg->venc.module_name[0]) {
+ v4l2_err(pdev->dev.driver, "vpbe display module names not"
+ " defined\n");
+ return ret;
+ }
+
+ vpbe_dev = kzalloc(sizeof(*vpbe_dev), GFP_KERNEL);
+ if (vpbe_dev == NULL) {
+ v4l2_err(pdev->dev.driver, "Unable to allocate memory"
+ " for vpbe_device\n");
+ return -ENOMEM;
+ }
+ vpbe_dev->cfg = cfg;
+ vpbe_dev->ops = vpbe_dev_ops;
+ vpbe_dev->pdev = &pdev->dev;
+
+ if (cfg->outputs->num_modes > 0)
+ vpbe_dev->current_timings = vpbe_dev->cfg->outputs[0].modes[0];
+ else
+ return -ENODEV;
+
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, vpbe_dev);
+ mutex_init(&vpbe_dev->lock);
+
+ return 0;
+}
+
+static int vpbe_remove(struct platform_device *device)
+{
+ struct vpbe_device *vpbe_dev = platform_get_drvdata(device);
+
+ kfree(vpbe_dev);
+
+ return 0;
+}
+
+static struct platform_driver vpbe_driver = {
+ .driver = {
+ .name = "vpbe_controller",
+ .owner = THIS_MODULE,
+ },
+ .probe = vpbe_probe,
+ .remove = vpbe_remove,
+};
+
+/**
+ * vpbe_init: initialize the vpbe driver
+ *
+ * This function registers device and driver to the kernel
+ */
+static __init int vpbe_init(void)
+{
+ return platform_driver_register(&vpbe_driver);
+}
+
+/**
+ * vpbe_cleanup : cleanup function for vpbe driver
+ *
+ * This will un-registers the device and driver to the kernel
+ */
+static void vpbe_cleanup(void)
+{
+ platform_driver_unregister(&vpbe_driver);
+}
+
+/* Function for module initialization and cleanup */
+module_init(vpbe_init);
+module_exit(vpbe_cleanup);
diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c
new file mode 100644
index 000000000000..8588a86d9b45
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_display.c
@@ -0,0 +1,1859 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+
+#include <asm/pgtable.h>
+#include <mach/cputype.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_display.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe.h>
+#include <media/davinci/vpbe_venc.h>
+#include <media/davinci/vpbe_osd.h>
+#include "vpbe_venc_regs.h"
+
+#define VPBE_DISPLAY_DRIVER "vpbe-v4l2"
+
+static int debug;
+
+#define VPBE_DEFAULT_NUM_BUFS 3
+
+module_param(debug, int, 0644);
+
+static int venc_is_second_field(struct vpbe_display *disp_dev)
+{
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ int ret;
+ int val;
+
+ ret = v4l2_subdev_call(vpbe_dev->venc,
+ core,
+ ioctl,
+ VENC_GET_FLD,
+ &val);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in getting Field ID 0\n");
+ }
+ return val;
+}
+
+static void vpbe_isr_even_field(struct vpbe_display *disp_obj,
+ struct vpbe_layer *layer)
+{
+ struct timespec timevalue;
+
+ if (layer->cur_frm == layer->next_frm)
+ return;
+ ktime_get_ts(&timevalue);
+ layer->cur_frm->ts.tv_sec = timevalue.tv_sec;
+ layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC;
+ layer->cur_frm->state = VIDEOBUF_DONE;
+ wake_up_interruptible(&layer->cur_frm->done);
+ /* Make cur_frm pointing to next_frm */
+ layer->cur_frm = layer->next_frm;
+}
+
+static void vpbe_isr_odd_field(struct vpbe_display *disp_obj,
+ struct vpbe_layer *layer)
+{
+ struct osd_state *osd_device = disp_obj->osd_device;
+ unsigned long addr;
+
+ spin_lock(&disp_obj->dma_queue_lock);
+ if (list_empty(&layer->dma_queue) ||
+ (layer->cur_frm != layer->next_frm)) {
+ spin_unlock(&disp_obj->dma_queue_lock);
+ return;
+ }
+ /*
+ * one field is displayed configure
+ * the next frame if it is available
+ * otherwise hold on current frame
+ * Get next from the buffer queue
+ */
+ layer->next_frm = list_entry(
+ layer->dma_queue.next,
+ struct videobuf_buffer,
+ queue);
+ /* Remove that from the buffer queue */
+ list_del(&layer->next_frm->queue);
+ spin_unlock(&disp_obj->dma_queue_lock);
+ /* Mark state of the frame to active */
+ layer->next_frm->state = VIDEOBUF_ACTIVE;
+ addr = videobuf_to_dma_contig(layer->next_frm);
+ osd_device->ops.start_layer(osd_device,
+ layer->layer_info.id,
+ addr,
+ disp_obj->cbcr_ofst);
+}
+
+/* interrupt service routine */
+static irqreturn_t venc_isr(int irq, void *arg)
+{
+ struct vpbe_display *disp_dev = (struct vpbe_display *)arg;
+ struct vpbe_layer *layer;
+ static unsigned last_event;
+ unsigned event = 0;
+ int fid;
+ int i;
+
+ if ((NULL == arg) || (NULL == disp_dev->dev[0]))
+ return IRQ_HANDLED;
+
+ if (venc_is_second_field(disp_dev))
+ event |= VENC_SECOND_FIELD;
+ else
+ event |= VENC_FIRST_FIELD;
+
+ if (event == (last_event & ~VENC_END_OF_FRAME)) {
+ /*
+ * If the display is non-interlaced, then we need to flag the
+ * end-of-frame event at every interrupt regardless of the
+ * value of the FIDST bit. We can conclude that the display is
+ * non-interlaced if the value of the FIDST bit is unchanged
+ * from the previous interrupt.
+ */
+ event |= VENC_END_OF_FRAME;
+ } else if (event == VENC_SECOND_FIELD) {
+ /* end-of-frame for interlaced display */
+ event |= VENC_END_OF_FRAME;
+ }
+ last_event = event;
+
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ layer = disp_dev->dev[i];
+ /* If streaming is started in this layer */
+ if (!layer->started)
+ continue;
+
+ if (layer->layer_first_int) {
+ layer->layer_first_int = 0;
+ continue;
+ }
+ /* Check the field format */
+ if ((V4L2_FIELD_NONE == layer->pix_fmt.field) &&
+ (event & VENC_END_OF_FRAME)) {
+ /* Progressive mode */
+
+ vpbe_isr_even_field(disp_dev, layer);
+ vpbe_isr_odd_field(disp_dev, layer);
+ } else {
+ /* Interlaced mode */
+
+ layer->field_id ^= 1;
+ if (event & VENC_FIRST_FIELD)
+ fid = 0;
+ else
+ fid = 1;
+
+ /*
+ * If field id does not match with store
+ * field id
+ */
+ if (fid != layer->field_id) {
+ /* Make them in sync */
+ layer->field_id = fid;
+ continue;
+ }
+ /*
+ * device field id and local field id are
+ * in sync. If this is even field
+ */
+ if (0 == fid)
+ vpbe_isr_even_field(disp_dev, layer);
+ else /* odd field */
+ vpbe_isr_odd_field(disp_dev, layer);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * vpbe_buffer_prepare()
+ * This is the callback function called from videobuf_qbuf() function
+ * the buffer is prepared and user space virtual address is converted into
+ * physical address
+ */
+static int vpbe_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned long addr;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe_buffer_prepare\n");
+
+ /* If buffer is not initialized, initialize it */
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->width = layer->pix_fmt.width;
+ vb->height = layer->pix_fmt.height;
+ vb->size = layer->pix_fmt.sizeimage;
+ vb->field = field;
+
+ ret = videobuf_iolock(q, vb, NULL);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to map \
+ user address\n");
+ return -EINVAL;
+ }
+
+ addr = videobuf_to_dma_contig(vb);
+
+ if (q->streaming) {
+ if (!IS_ALIGNED(addr, 8)) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "buffer_prepare:offset is \
+ not aligned to 32 bytes\n");
+ return -EINVAL;
+ }
+ }
+ vb->state = VIDEOBUF_PREPARED;
+ }
+ return 0;
+}
+
+/*
+ * vpbe_buffer_setup()
+ * This function allocates memory for the buffers
+ */
+static int vpbe_buffer_setup(struct videobuf_queue *q,
+ unsigned int *count,
+ unsigned int *size)
+{
+ /* Get the file handle object and layer object */
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n");
+
+ *size = layer->pix_fmt.sizeimage;
+
+ /* Store number of buffers allocated in numbuffer member */
+ if (*count < VPBE_DEFAULT_NUM_BUFS)
+ *count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS;
+
+ return 0;
+}
+
+/*
+ * vpbe_buffer_queue()
+ * This function adds the buffer to DMA queue
+ */
+static void vpbe_buffer_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ /* Get the file handle object and layer object */
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned long flags;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe_buffer_queue\n");
+
+ /* add the buffer to the DMA queue */
+ spin_lock_irqsave(&disp->dma_queue_lock, flags);
+ list_add_tail(&vb->queue, &layer->dma_queue);
+ spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
+ /* Change state of the buffer */
+ vb->state = VIDEOBUF_QUEUED;
+}
+
+/*
+ * vpbe_buffer_release()
+ * This function is called from the videobuf layer to free memory allocated to
+ * the buffers
+ */
+static void vpbe_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ /* Get the file handle object and layer object */
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe_buffer_release\n");
+
+ if (V4L2_MEMORY_USERPTR != layer->memory)
+ videobuf_dma_contig_free(q, vb);
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = vpbe_buffer_setup,
+ .buf_prepare = vpbe_buffer_prepare,
+ .buf_queue = vpbe_buffer_queue,
+ .buf_release = vpbe_buffer_release,
+};
+
+static
+struct vpbe_layer*
+_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer)
+{
+ enum vpbe_display_device_id thiswin, otherwin;
+ thiswin = layer->device_id;
+
+ otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ?
+ VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0;
+ return disp_dev->dev[otherwin];
+}
+
+static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer)
+{
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ unsigned long addr;
+ int ret;
+
+ addr = videobuf_to_dma_contig(layer->cur_frm);
+ /* Set address in the display registers */
+ osd_device->ops.start_layer(osd_device,
+ layer->layer_info.id,
+ addr,
+ disp_dev->cbcr_ofst);
+
+ ret = osd_device->ops.enable_layer(osd_device,
+ layer->layer_info.id, 0);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in enabling osd window layer 0\n");
+ return -1;
+ }
+
+ /* Enable the window */
+ layer->layer_info.enable = 1;
+ if (cfg->pixfmt == PIXFMT_NV12) {
+ struct vpbe_layer *otherlayer =
+ _vpbe_display_get_other_win_layer(disp_dev, layer);
+
+ ret = osd_device->ops.enable_layer(osd_device,
+ otherlayer->layer_info.id, 1);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in enabling osd window layer 1\n");
+ return -1;
+ }
+ otherlayer->layer_info.enable = 1;
+ }
+ return 0;
+}
+
+static void
+vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer,
+ int expected_xsize, int expected_ysize)
+{
+ struct display_layer_info *layer_info = &layer->layer_info;
+ struct v4l2_pix_format *pixfmt = &layer->pix_fmt;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ int calculated_xsize;
+ int h_exp = 0;
+ int v_exp = 0;
+ int h_scale;
+ int v_scale;
+
+ v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id;
+
+ /*
+ * Application initially set the image format. Current display
+ * size is obtained from the vpbe display controller. expected_xsize
+ * and expected_ysize are set through S_CROP ioctl. Based on this,
+ * driver will calculate the scale factors for vertical and
+ * horizontal direction so that the image is displayed scaled
+ * and expanded. Application uses expansion to display the image
+ * in a square pixel. Otherwise it is displayed using displays
+ * pixel aspect ratio.It is expected that application chooses
+ * the crop coordinates for cropped or scaled display. if crop
+ * size is less than the image size, it is displayed cropped or
+ * it is displayed scaled and/or expanded.
+ *
+ * to begin with, set the crop window same as expected. Later we
+ * will override with scaled window size
+ */
+
+ cfg->xsize = pixfmt->width;
+ cfg->ysize = pixfmt->height;
+ layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */
+ layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */
+ layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */
+ layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */
+
+ if (pixfmt->width < expected_xsize) {
+ h_scale = vpbe_dev->current_timings.xres / pixfmt->width;
+ if (h_scale < 2)
+ h_scale = 1;
+ else if (h_scale >= 4)
+ h_scale = 4;
+ else
+ h_scale = 2;
+ cfg->xsize *= h_scale;
+ if (cfg->xsize < expected_xsize) {
+ if ((standard_id & V4L2_STD_525_60) ||
+ (standard_id & V4L2_STD_625_50)) {
+ calculated_xsize = (cfg->xsize *
+ VPBE_DISPLAY_H_EXP_RATIO_N) /
+ VPBE_DISPLAY_H_EXP_RATIO_D;
+ if (calculated_xsize <= expected_xsize) {
+ h_exp = 1;
+ cfg->xsize = calculated_xsize;
+ }
+ }
+ }
+ if (h_scale == 2)
+ layer_info->h_zoom = ZOOM_X2;
+ else if (h_scale == 4)
+ layer_info->h_zoom = ZOOM_X4;
+ if (h_exp)
+ layer_info->h_exp = H_EXP_9_OVER_8;
+ } else {
+ /* no scaling, only cropping. Set display area to crop area */
+ cfg->xsize = expected_xsize;
+ }
+
+ if (pixfmt->height < expected_ysize) {
+ v_scale = expected_ysize / pixfmt->height;
+ if (v_scale < 2)
+ v_scale = 1;
+ else if (v_scale >= 4)
+ v_scale = 4;
+ else
+ v_scale = 2;
+ cfg->ysize *= v_scale;
+ if (cfg->ysize < expected_ysize) {
+ if ((standard_id & V4L2_STD_625_50)) {
+ calculated_xsize = (cfg->ysize *
+ VPBE_DISPLAY_V_EXP_RATIO_N) /
+ VPBE_DISPLAY_V_EXP_RATIO_D;
+ if (calculated_xsize <= expected_ysize) {
+ v_exp = 1;
+ cfg->ysize = calculated_xsize;
+ }
+ }
+ }
+ if (v_scale == 2)
+ layer_info->v_zoom = ZOOM_X2;
+ else if (v_scale == 4)
+ layer_info->v_zoom = ZOOM_X4;
+ if (v_exp)
+ layer_info->h_exp = V_EXP_6_OVER_5;
+ } else {
+ /* no scaling, only cropping. Set display area to crop area */
+ cfg->ysize = expected_ysize;
+ }
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "crop display xsize = %d, ysize = %d\n",
+ cfg->xsize, cfg->ysize);
+}
+
+static void vpbe_disp_adj_position(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer,
+ int top, int left)
+{
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+
+ cfg->xpos = min((unsigned int)left,
+ vpbe_dev->current_timings.xres - cfg->xsize);
+ cfg->ypos = min((unsigned int)top,
+ vpbe_dev->current_timings.yres - cfg->ysize);
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "new xpos = %d, ypos = %d\n",
+ cfg->xpos, cfg->ypos);
+}
+
+static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev,
+ struct v4l2_rect *c)
+{
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+
+ if ((c->width == 0) ||
+ ((c->width + c->left) > vpbe_dev->current_timings.xres))
+ c->width = vpbe_dev->current_timings.xres - c->left;
+
+ if ((c->height == 0) || ((c->height + c->top) >
+ vpbe_dev->current_timings.yres))
+ c->height = vpbe_dev->current_timings.yres - c->top;
+
+ /* window height must be even for interlaced display */
+ if (vpbe_dev->current_timings.interlaced)
+ c->height &= (~0x01);
+
+}
+
+/**
+ * vpbe_try_format()
+ * If user application provides width and height, and have bytesperline set
+ * to zero, driver calculates bytesperline and sizeimage based on hardware
+ * limits.
+ */
+static int vpbe_try_format(struct vpbe_display *disp_dev,
+ struct v4l2_pix_format *pixfmt, int check)
+{
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ int min_height = 1;
+ int min_width = 32;
+ int max_height;
+ int max_width;
+ int bpp;
+
+ if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) &&
+ (pixfmt->pixelformat != V4L2_PIX_FMT_NV12))
+ /* choose default as V4L2_PIX_FMT_UYVY */
+ pixfmt->pixelformat = V4L2_PIX_FMT_UYVY;
+
+ /* Check the field format */
+ if ((pixfmt->field != V4L2_FIELD_INTERLACED) &&
+ (pixfmt->field != V4L2_FIELD_NONE)) {
+ if (vpbe_dev->current_timings.interlaced)
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ else
+ pixfmt->field = V4L2_FIELD_NONE;
+ }
+
+ if (pixfmt->field == V4L2_FIELD_INTERLACED)
+ min_height = 2;
+
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
+ bpp = 1;
+ else
+ bpp = 2;
+
+ max_width = vpbe_dev->current_timings.xres;
+ max_height = vpbe_dev->current_timings.yres;
+
+ min_width /= bpp;
+
+ if (!pixfmt->width || (pixfmt->width < min_width) ||
+ (pixfmt->width > max_width)) {
+ pixfmt->width = vpbe_dev->current_timings.xres;
+ }
+
+ if (!pixfmt->height || (pixfmt->height < min_height) ||
+ (pixfmt->height > max_height)) {
+ pixfmt->height = vpbe_dev->current_timings.yres;
+ }
+
+ if (pixfmt->bytesperline < (pixfmt->width * bpp))
+ pixfmt->bytesperline = pixfmt->width * bpp;
+
+ /* Make the bytesperline 32 byte aligned */
+ pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31);
+
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height +
+ (pixfmt->bytesperline * pixfmt->height >> 1);
+ else
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+
+ return 0;
+}
+
+static int vpbe_display_g_priority(struct file *file, void *priv,
+ enum v4l2_priority *p)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+
+ *p = v4l2_prio_max(&layer->prio);
+
+ return 0;
+}
+
+static int vpbe_display_s_priority(struct file *file, void *priv,
+ enum v4l2_priority p)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ int ret;
+
+ ret = v4l2_prio_change(&layer->prio, &fh->prio, p);
+
+ return ret;
+}
+
+static int vpbe_display_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ cap->version = VPBE_DISPLAY_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver));
+ strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info));
+ strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card));
+
+ return 0;
+}
+
+static int vpbe_display_s_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ struct v4l2_rect *rect = &crop->c;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_S_CROP, layer id = %d\n", layer->device_id);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ if (rect->top < 0)
+ rect->top = 0;
+ if (rect->left < 0)
+ rect->left = 0;
+
+ vpbe_disp_check_window_params(disp_dev, rect);
+
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+
+ vpbe_disp_calculate_scale_factor(disp_dev, layer,
+ rect->width,
+ rect->height);
+ vpbe_disp_adj_position(disp_dev, layer, rect->top,
+ rect->left);
+ ret = osd_device->ops.set_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in set layer config:\n");
+ return -EINVAL;
+ }
+
+ /* apply zooming and h or v expansion */
+ osd_device->ops.set_zoom(osd_device,
+ layer->layer_info.id,
+ layer->layer_info.h_zoom,
+ layer->layer_info.v_zoom);
+ ret = osd_device->ops.set_vid_expansion(osd_device,
+ layer->layer_info.h_exp,
+ layer->layer_info.v_exp);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in set vid expansion:\n");
+ return -EINVAL;
+ }
+
+ if ((layer->layer_info.h_zoom != ZOOM_X1) ||
+ (layer->layer_info.v_zoom != ZOOM_X1) ||
+ (layer->layer_info.h_exp != H_EXP_OFF) ||
+ (layer->layer_info.v_exp != V_EXP_OFF))
+ /* Enable expansion filter */
+ osd_device->ops.set_interpolation_filter(osd_device, 1);
+ else
+ osd_device->ops.set_interpolation_filter(osd_device, 0);
+
+ return 0;
+}
+
+static int vpbe_display_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct osd_state *osd_device = fh->disp_dev->osd_device;
+ struct v4l2_rect *rect = &crop->c;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_G_CROP, layer id = %d\n",
+ layer->device_id);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
+ ret = -EINVAL;
+ }
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ rect->top = cfg->ypos;
+ rect->left = cfg->xpos;
+ rect->width = cfg->xsize;
+ rect->height = cfg->ysize;
+
+ return 0;
+}
+
+static int vpbe_display_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cropcap)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n");
+
+ cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.top = 0;
+ cropcap->bounds.width = vpbe_dev->current_timings.xres;
+ cropcap->bounds.height = vpbe_dev->current_timings.yres;
+ cropcap->pixelaspect = vpbe_dev->current_timings.aspect;
+ cropcap->defrect = cropcap->bounds;
+ return 0;
+}
+
+static int vpbe_display_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_G_FMT, layer id = %d\n",
+ layer->device_id);
+
+ /* If buffer type is video output */
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
+ return -EINVAL;
+ }
+ /* Fill in the information about format */
+ fmt->fmt.pix = layer->pix_fmt;
+
+ return 0;
+}
+
+static int vpbe_display_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned int index = 0;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_ENUM_FMT, layer id = %d\n",
+ layer->device_id);
+ if (fmt->index > 1) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n");
+ return -EINVAL;
+ }
+
+ /* Fill in the information about format */
+ index = fmt->index;
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->index = index;
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ if (index == 0) {
+ strcpy(fmt->description, "YUV 4:2:2 - UYVY");
+ fmt->pixelformat = V4L2_PIX_FMT_UYVY;
+ } else {
+ strcpy(fmt->description, "Y/CbCr 4:2:0");
+ fmt->pixelformat = V4L2_PIX_FMT_NV12;
+ }
+
+ return 0;
+}
+
+static int vpbe_display_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_S_FMT, layer id = %d\n",
+ layer->device_id);
+
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n");
+ return -EINVAL;
+ }
+ /* Check for valid pixel format */
+ ret = vpbe_try_format(disp_dev, pixfmt, 1);
+ if (ret)
+ return ret;
+
+ /* YUV420 is requested, check availability of the
+ other video window */
+
+ layer->pix_fmt = *pixfmt;
+
+ /* Get osd layer config */
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ /* Store the pixel format in the layer object */
+ cfg->xsize = pixfmt->width;
+ cfg->ysize = pixfmt->height;
+ cfg->line_length = pixfmt->bytesperline;
+ cfg->ypos = 0;
+ cfg->xpos = 0;
+ cfg->interlaced = vpbe_dev->current_timings.interlaced;
+
+ if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat)
+ cfg->pixfmt = PIXFMT_YCbCrI;
+
+ /* Change of the default pixel format for both video windows */
+ if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) {
+ struct vpbe_layer *otherlayer;
+ cfg->pixfmt = PIXFMT_NV12;
+ otherlayer = _vpbe_display_get_other_win_layer(disp_dev,
+ layer);
+ otherlayer->layer_info.config.pixfmt = PIXFMT_NV12;
+ }
+
+ /* Set the layer config in the osd window */
+ ret = osd_device->ops.set_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in S_FMT params:\n");
+ return -EINVAL;
+ }
+
+ /* Readback and fill the local copy of current pix format */
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+
+ return 0;
+}
+
+static int vpbe_display_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
+ return -EINVAL;
+ }
+
+ /* Check for valid field format */
+ return vpbe_try_format(disp_dev, pixfmt, 0);
+
+}
+
+/**
+ * vpbe_display_s_std - Set the given standard in the encoder
+ *
+ * Sets the standard if supported by the current encoder. Return the status.
+ * 0 - success & -EINVAL on error
+ */
+static int vpbe_display_s_std(struct file *file, void *priv,
+ v4l2_std_id *std_id)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n");
+
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ if (NULL != vpbe_dev->ops.s_std) {
+ ret = vpbe_dev->ops.s_std(vpbe_dev, std_id);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to set standard for sub devices\n");
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_g_std - Get the standard in the current encoder
+ *
+ * Get the standard in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_display_g_std(struct file *file, void *priv,
+ v4l2_std_id *std_id)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n");
+
+ /* Get the standard from the current encoder */
+ if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) {
+ *std_id = vpbe_dev->current_timings.timings.std_id;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_display_enum_output - enumerate outputs
+ *
+ * Enumerates the outputs available at the vpbe display
+ * returns the status, -EINVAL if end of output list
+ */
+static int vpbe_display_enum_output(struct file *file, void *priv,
+ struct v4l2_output *output)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n");
+
+ /* Enumerate outputs */
+
+ if (NULL == vpbe_dev->ops.enum_outputs)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output);
+ if (ret) {
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "Failed to enumerate outputs\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_s_output - Set output to
+ * the output specified by the index
+ */
+static int vpbe_display_s_output(struct file *file, void *priv,
+ unsigned int i)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n");
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ if (NULL == vpbe_dev->ops.set_output)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.set_output(vpbe_dev, i);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to set output for sub devices\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_g_output - Get output from subdevice
+ * for a given by the index
+ */
+static int vpbe_display_g_output(struct file *file, void *priv,
+ unsigned int *i)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n");
+ /* Get the standard from the current encoder */
+ *i = vpbe_dev->current_out_index;
+
+ return 0;
+}
+
+/**
+ * vpbe_display_enum_dv_presets - Enumerate the dv presets
+ *
+ * enum the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_enum_dv_presets(struct file *file, void *priv,
+ struct v4l2_dv_enum_preset *preset)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n");
+
+ /* Enumerate outputs */
+ if (NULL == vpbe_dev->ops.enum_dv_presets)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.enum_dv_presets(vpbe_dev, preset);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to enumerate dv presets info\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_s_dv_preset - Set the dv presets
+ *
+ * Set the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_s_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *preset)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_PRESETS\n");
+
+
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+
+ /* Set the given standard in the encoder */
+ if (NULL != vpbe_dev->ops.s_dv_preset)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to set the dv presets info\n");
+ return -EINVAL;
+ }
+ /* set the current norm to zero to be consistent. If STD is used
+ * v4l2 layer will set the norm properly on successful s_std call
+ */
+ layer->video_dev.current_norm = 0;
+
+ return 0;
+}
+
+/**
+ * vpbe_display_g_dv_preset - Set the dv presets
+ *
+ * Get the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_g_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *dv_preset)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_PRESETS\n");
+
+ /* Get the given standard in the encoder */
+
+ if (vpbe_dev->current_timings.timings_type &
+ VPBE_ENC_DV_PRESET) {
+ dv_preset->preset =
+ vpbe_dev->current_timings.timings.dv_preset;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vpbe_display_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct osd_state *osd_device = fh->disp_dev->osd_device;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_STREAMOFF,layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If io is allowed for this file handle, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+
+ /* If streaming is not started, return error */
+ if (!layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "streaming not started in layer"
+ " id = %d\n", layer->device_id);
+ return -EINVAL;
+ }
+
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+ layer->started = 0;
+ ret = videobuf_streamoff(&layer->buffer_queue);
+
+ return ret;
+}
+
+static int vpbe_display_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ int ret;
+
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_STREAMON, layerid=%d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If file handle is not allowed IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+ /* If Streaming is already started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "layer is already streaming\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Call videobuf_streamon to start streaming
+ * in videobuf
+ */
+ ret = videobuf_streamon(&layer->buffer_queue);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "error in videobuf_streamon\n");
+ return ret;
+ }
+ /* If buffer queue is empty, return error */
+ if (list_empty(&layer->dma_queue)) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
+ goto streamoff;
+ }
+ /* Get the next frame from the buffer queue */
+ layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
+ struct videobuf_buffer, queue);
+ /* Remove buffer from the buffer queue */
+ list_del(&layer->cur_frm->queue);
+ /* Mark state of the current frame to active */
+ layer->cur_frm->state = VIDEOBUF_ACTIVE;
+ /* Initialize field_id and started member */
+ layer->field_id = 0;
+
+ /* Set parameters in OSD and VENC */
+ ret = vpbe_set_osd_display_params(disp_dev, layer);
+ if (ret < 0)
+ goto streamoff;
+
+ /*
+ * if request format is yuv420 semiplanar, need to
+ * enable both video windows
+ */
+ layer->started = 1;
+
+ layer->layer_first_int = 1;
+
+ return ret;
+streamoff:
+ ret = videobuf_streamoff(&layer->buffer_queue);
+ return ret;
+}
+
+static int vpbe_display_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_DQBUF, layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+ /* If this file handle is not allowed to do IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+ if (file->f_flags & O_NONBLOCK)
+ /* Call videobuf_dqbuf for non blocking mode */
+ ret = videobuf_dqbuf(&layer->buffer_queue, buf, 1);
+ else
+ /* Call videobuf_dqbuf for blocking mode */
+ ret = videobuf_dqbuf(&layer->buffer_queue, buf, 0);
+
+ return ret;
+}
+
+static int vpbe_display_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_QBUF, layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If this file handle is not allowed to do IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+
+ return videobuf_qbuf(&layer->buffer_queue, p);
+}
+
+static int vpbe_display_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_QUERYBUF, layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* Call videobuf_querybuf to get information */
+ ret = videobuf_querybuf(&layer->buffer_queue, buf);
+
+ return ret;
+}
+
+static int vpbe_display_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req_buf)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If io users of the layer is not zero, return error */
+ if (0 != layer->io_usrs) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "not IO user\n");
+ return -EBUSY;
+ }
+ /* Initialize videobuf queue as per the buffer type */
+ videobuf_queue_dma_contig_init(&layer->buffer_queue,
+ &video_qops,
+ vpbe_dev->pdev,
+ &layer->irqlock,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ layer->pix_fmt.field,
+ sizeof(struct videobuf_buffer),
+ fh, NULL);
+
+ /* Set io allowed member of file handle to TRUE */
+ fh->io_allowed = 1;
+ /* Increment io usrs member of layer object to 1 */
+ layer->io_usrs = 1;
+ /* Store type of memory requested in layer object */
+ layer->memory = req_buf->memory;
+ /* Initialize buffer queue */
+ INIT_LIST_HEAD(&layer->dma_queue);
+ /* Allocate buffers */
+ ret = videobuf_reqbufs(&layer->buffer_queue, req_buf);
+
+ return ret;
+}
+
+/*
+ * vpbe_display_mmap()
+ * It is used to map kernel space buffers into user spaces
+ */
+static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ /* Get the layer object and file handle object */
+ struct vpbe_fh *fh = filep->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n");
+
+ return videobuf_mmap_mapper(&layer->buffer_queue, vma);
+}
+
+/* vpbe_display_poll(): It is used for select/poll system call
+ */
+static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait)
+{
+ struct vpbe_fh *fh = filep->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned int err = 0;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n");
+ if (layer->started)
+ err = videobuf_poll_stream(filep, &layer->buffer_queue, wait);
+ return err;
+}
+
+/*
+ * vpbe_display_open()
+ * It creates object of file handle structure and stores it in private_data
+ * member of filepointer
+ */
+static int vpbe_display_open(struct file *file)
+{
+ struct vpbe_fh *fh = NULL;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_display *disp_dev = layer->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ int err;
+
+ /* Allocate memory for the file handle object */
+ fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL);
+ if (fh == NULL) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe display open plane = %d\n",
+ layer->device_id);
+
+ /* store pointer to fh in private_data member of filep */
+ file->private_data = fh;
+ fh->layer = layer;
+ fh->disp_dev = disp_dev;
+
+ if (!layer->usrs) {
+
+ /* First claim the layer for this device */
+ err = osd_device->ops.request_layer(osd_device,
+ layer->layer_info.id);
+ if (err < 0) {
+ /* Couldn't get layer */
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Display Manager failed to allocate layer\n");
+ kfree(fh);
+ return -EINVAL;
+ }
+ }
+ /* Increment layer usrs counter */
+ layer->usrs++;
+ /* Set io_allowed member to false */
+ fh->io_allowed = 0;
+ /* Initialize priority of this instance to default priority */
+ fh->prio = V4L2_PRIORITY_UNSET;
+ v4l2_prio_open(&layer->prio, &fh->prio);
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe display device opened successfully\n");
+ return 0;
+}
+
+/*
+ * vpbe_display_release()
+ * This function deletes buffer queue, frees the buffers and the davinci
+ * display file * handle
+ */
+static int vpbe_display_release(struct file *file)
+{
+ /* Get the layer object and file handle object */
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_state *osd_device = disp_dev->osd_device;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n");
+
+ /* if this instance is doing IO */
+ if (fh->io_allowed) {
+ /* Reset io_usrs member of layer object */
+ layer->io_usrs = 0;
+
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+ layer->started = 0;
+ /* Free buffers allocated */
+ videobuf_queue_cancel(&layer->buffer_queue);
+ videobuf_mmap_free(&layer->buffer_queue);
+ }
+
+ /* Decrement layer usrs counter */
+ layer->usrs--;
+ /* If this file handle has initialize encoder device, reset it */
+ if (!layer->usrs) {
+ if (cfg->pixfmt == PIXFMT_NV12) {
+ struct vpbe_layer *otherlayer;
+ otherlayer =
+ _vpbe_display_get_other_win_layer(disp_dev, layer);
+ osd_device->ops.disable_layer(osd_device,
+ otherlayer->layer_info.id);
+ osd_device->ops.release_layer(osd_device,
+ otherlayer->layer_info.id);
+ }
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+ osd_device->ops.release_layer(osd_device,
+ layer->layer_info.id);
+ }
+ /* Close the priority */
+ v4l2_prio_close(&layer->prio, fh->prio);
+ file->private_data = NULL;
+
+ /* Free memory allocated to file handle object */
+ kfree(fh);
+
+ disp_dev->cbcr_ofst = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vpbe_display_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct v4l2_dbg_match *match = &reg->match;
+
+ if (match->type >= 2) {
+ v4l2_subdev_call(vpbe_dev->venc,
+ core,
+ g_register,
+ reg);
+ }
+
+ return 0;
+}
+
+static int vpbe_display_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ return 0;
+}
+#endif
+
+/* vpbe capture ioctl operations */
+static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
+ .vidioc_querycap = vpbe_display_querycap,
+ .vidioc_g_fmt_vid_out = vpbe_display_g_fmt,
+ .vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt,
+ .vidioc_s_fmt_vid_out = vpbe_display_s_fmt,
+ .vidioc_try_fmt_vid_out = vpbe_display_try_fmt,
+ .vidioc_reqbufs = vpbe_display_reqbufs,
+ .vidioc_querybuf = vpbe_display_querybuf,
+ .vidioc_qbuf = vpbe_display_qbuf,
+ .vidioc_dqbuf = vpbe_display_dqbuf,
+ .vidioc_streamon = vpbe_display_streamon,
+ .vidioc_streamoff = vpbe_display_streamoff,
+ .vidioc_cropcap = vpbe_display_cropcap,
+ .vidioc_g_crop = vpbe_display_g_crop,
+ .vidioc_s_crop = vpbe_display_s_crop,
+ .vidioc_g_priority = vpbe_display_g_priority,
+ .vidioc_s_priority = vpbe_display_s_priority,
+ .vidioc_s_std = vpbe_display_s_std,
+ .vidioc_g_std = vpbe_display_g_std,
+ .vidioc_enum_output = vpbe_display_enum_output,
+ .vidioc_s_output = vpbe_display_s_output,
+ .vidioc_g_output = vpbe_display_g_output,
+ .vidioc_s_dv_preset = vpbe_display_s_dv_preset,
+ .vidioc_g_dv_preset = vpbe_display_g_dv_preset,
+ .vidioc_enum_dv_presets = vpbe_display_enum_dv_presets,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vpbe_display_g_register,
+ .vidioc_s_register = vpbe_display_s_register,
+#endif
+};
+
+static struct v4l2_file_operations vpbe_fops = {
+ .owner = THIS_MODULE,
+ .open = vpbe_display_open,
+ .release = vpbe_display_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpbe_display_mmap,
+ .poll = vpbe_display_poll
+};
+
+static int vpbe_device_get(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vpbe_display *vpbe_disp = data;
+
+ if (strcmp("vpbe_controller", pdev->name) == 0)
+ vpbe_disp->vpbe_dev = platform_get_drvdata(pdev);
+
+ if (strcmp("vpbe-osd", pdev->name) == 0)
+ vpbe_disp->osd_device = platform_get_drvdata(pdev);
+
+ return 0;
+}
+
+static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev,
+ struct platform_device *pdev)
+{
+ struct vpbe_layer *vpbe_display_layer = NULL;
+ struct video_device *vbd = NULL;
+
+ /* Allocate memory for four plane display objects */
+
+ disp_dev->dev[i] =
+ kzalloc(sizeof(struct vpbe_layer), GFP_KERNEL);
+
+ /* If memory allocation fails, return error */
+ if (!disp_dev->dev[i]) {
+ printk(KERN_ERR "ran out of memory\n");
+ return -ENOMEM;
+ }
+ spin_lock_init(&disp_dev->dev[i]->irqlock);
+ mutex_init(&disp_dev->dev[i]->opslock);
+
+ /* Get the pointer to the layer object */
+ vpbe_display_layer = disp_dev->dev[i];
+ vbd = &vpbe_display_layer->video_dev;
+ /* Initialize field of video device */
+ vbd->release = video_device_release_empty;
+ vbd->fops = &vpbe_fops;
+ vbd->ioctl_ops = &vpbe_ioctl_ops;
+ vbd->minor = -1;
+ vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev;
+ vbd->lock = &vpbe_display_layer->opslock;
+
+ if (disp_dev->vpbe_dev->current_timings.timings_type &
+ VPBE_ENC_STD) {
+ vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50);
+ vbd->current_norm =
+ disp_dev->vpbe_dev->
+ current_timings.timings.std_id;
+ } else
+ vbd->current_norm = 0;
+
+ snprintf(vbd->name, sizeof(vbd->name),
+ "DaVinci_VPBE Display_DRIVER_V%d.%d.%d",
+ (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff,
+ (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff,
+ (VPBE_DISPLAY_VERSION_CODE) & 0xff);
+
+ vpbe_display_layer->device_id = i;
+
+ vpbe_display_layer->layer_info.id =
+ ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1);
+
+ /* Initialize prio member of layer object */
+ v4l2_prio_init(&vpbe_display_layer->prio);
+
+ return 0;
+}
+
+static __devinit int register_device(struct vpbe_layer *vpbe_display_layer,
+ struct vpbe_display *disp_dev,
+ struct platform_device *pdev) {
+ int err;
+
+ v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
+ "Trying to register VPBE display device.\n");
+ v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
+ "layer=%x,layer->video_dev=%x\n",
+ (int)vpbe_display_layer,
+ (int)&vpbe_display_layer->video_dev);
+
+ err = video_register_device(&vpbe_display_layer->video_dev,
+ VFL_TYPE_GRABBER,
+ -1);
+ if (err)
+ return -ENODEV;
+
+ vpbe_display_layer->disp_dev = disp_dev;
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, disp_dev);
+ video_set_drvdata(&vpbe_display_layer->video_dev,
+ vpbe_display_layer);
+
+ return 0;
+}
+
+
+
+/*
+ * vpbe_display_probe()
+ * This function creates device entries by register itself to the V4L2 driver
+ * and initializes fields of each layer objects
+ */
+static __devinit int vpbe_display_probe(struct platform_device *pdev)
+{
+ struct vpbe_layer *vpbe_display_layer;
+ struct vpbe_display *disp_dev;
+ struct resource *res = NULL;
+ int k;
+ int i;
+ int err;
+ int irq;
+
+ printk(KERN_DEBUG "vpbe_display_probe\n");
+ /* Allocate memory for vpbe_display */
+ disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL);
+ if (!disp_dev) {
+ printk(KERN_ERR "ran out of memory\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&disp_dev->dma_queue_lock);
+ /*
+ * Scan all the platform devices to find the vpbe
+ * controller device and get the vpbe_dev object
+ */
+ err = bus_for_each_dev(&platform_bus_type, NULL, disp_dev,
+ vpbe_device_get);
+ if (err < 0)
+ return err;
+ /* Initialize the vpbe display controller */
+ if (NULL != disp_dev->vpbe_dev->ops.initialize) {
+ err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev,
+ disp_dev->vpbe_dev);
+ if (err) {
+ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+ "Error initing vpbe\n");
+ err = -ENOMEM;
+ goto probe_out;
+ }
+ }
+
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ if (init_vpbe_layer(i, disp_dev, pdev)) {
+ err = -ENODEV;
+ goto probe_out;
+ }
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+ "Unable to get VENC interrupt resource\n");
+ err = -ENODEV;
+ goto probe_out;
+ }
+
+ irq = res->start;
+ if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER,
+ disp_dev)) {
+ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+ "Unable to request interrupt\n");
+ err = -ENODEV;
+ goto probe_out;
+ }
+
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
+ err = -ENODEV;
+ goto probe_out;
+ }
+ }
+
+ printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n");
+ return 0;
+
+probe_out:
+ free_irq(res->start, disp_dev);
+ for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
+ /* Get the pointer to the layer object */
+ vpbe_display_layer = disp_dev->dev[k];
+ /* Unregister video device */
+ if (vpbe_display_layer) {
+ video_unregister_device(
+ &vpbe_display_layer->video_dev);
+ kfree(disp_dev->dev[k]);
+ }
+ }
+ kfree(disp_dev);
+ return err;
+}
+
+/*
+ * vpbe_display_remove()
+ * It un-register hardware layer from V4L2 driver
+ */
+static int vpbe_display_remove(struct platform_device *pdev)
+{
+ struct vpbe_layer *vpbe_display_layer;
+ struct vpbe_display *disp_dev = platform_get_drvdata(pdev);
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct resource *res;
+ int i;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n");
+
+ /* unregister irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ free_irq(res->start, disp_dev);
+
+ /* deinitialize the vpbe display controller */
+ if (NULL != vpbe_dev->ops.deinitialize)
+ vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev);
+ /* un-register device */
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ /* Get the pointer to the layer object */
+ vpbe_display_layer = disp_dev->dev[i];
+ /* Unregister video device */
+ video_unregister_device(&vpbe_display_layer->video_dev);
+
+ }
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ kfree(disp_dev->dev[i]);
+ disp_dev->dev[i] = NULL;
+ }
+
+ return 0;
+}
+
+static struct platform_driver vpbe_display_driver = {
+ .driver = {
+ .name = VPBE_DISPLAY_DRIVER,
+ .owner = THIS_MODULE,
+ .bus = &platform_bus_type,
+ },
+ .probe = vpbe_display_probe,
+ .remove = __devexit_p(vpbe_display_remove),
+};
+
+/*
+ * vpbe_display_init()
+ * This function registers device and driver to the kernel, requests irq
+ * handler and allocates memory for layer objects
+ */
+static __devinit int vpbe_display_init(void)
+{
+ int err;
+
+ printk(KERN_DEBUG "vpbe_display_init\n");
+
+ /* Register driver to the kernel */
+ err = platform_driver_register(&vpbe_display_driver);
+ if (0 != err)
+ return err;
+
+ printk(KERN_DEBUG "vpbe_display_init:"
+ "VPBE V4L2 Display Driver V1.0 loaded\n");
+ return 0;
+}
+
+/*
+ * vpbe_display_cleanup()
+ * This function un-registers device and driver to the kernel, frees requested
+ * irq handler and de-allocates memory allocated for layer objects.
+ */
+static void vpbe_display_cleanup(void)
+{
+ printk(KERN_DEBUG "vpbe_display_cleanup\n");
+
+ /* platform driver unregister */
+ platform_driver_unregister(&vpbe_display_driver);
+}
+
+/* Function for module initialization and cleanup */
+module_init(vpbe_display_init);
+module_exit(vpbe_display_cleanup);
+
+MODULE_DESCRIPTION("TI DM644x/DM355/DM365 VPBE Display controller");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/video/davinci/vpbe_osd.c
new file mode 100644
index 000000000000..ceccf4302518
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_osd.c
@@ -0,0 +1,1231 @@
+/*
+ * Copyright (C) 2007-2010 Texas Instruments Inc
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ *
+ * Andy Lowe (alowe@mvista.com), MontaVista Software
+ * - Initial version
+ * Murali Karicheri (mkaricheri@gmail.com), Texas Instruments Ltd.
+ * - ported to sub device interface
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include <mach/io.h>
+#include <mach/cputype.h>
+#include <mach/hardware.h>
+
+#include <media/davinci/vpss.h>
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe_osd.h>
+
+#include <linux/io.h>
+#include "vpbe_osd_regs.h"
+
+#define MODULE_NAME VPBE_OSD_SUBDEV_NAME
+
+/* register access routines */
+static inline u32 osd_read(struct osd_state *sd, u32 offset)
+{
+ struct osd_state *osd = sd;
+
+ return readl(osd->osd_base + offset);
+}
+
+static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset)
+{
+ struct osd_state *osd = sd;
+
+ writel(val, osd->osd_base + offset);
+
+ return val;
+}
+
+static inline u32 osd_set(struct osd_state *sd, u32 mask, u32 offset)
+{
+ struct osd_state *osd = sd;
+
+ u32 addr = osd->osd_base + offset;
+ u32 val = readl(addr) | mask;
+
+ writel(val, addr);
+
+ return val;
+}
+
+static inline u32 osd_clear(struct osd_state *sd, u32 mask, u32 offset)
+{
+ struct osd_state *osd = sd;
+
+ u32 addr = osd->osd_base + offset;
+ u32 val = readl(addr) & ~mask;
+
+ writel(val, addr);
+
+ return val;
+}
+
+static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val,
+ u32 offset)
+{
+ struct osd_state *osd = sd;
+
+ u32 addr = osd->osd_base + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+
+ return new_val;
+}
+
+/* define some macros for layer and pixfmt classification */
+#define is_osd_win(layer) (((layer) == WIN_OSD0) || ((layer) == WIN_OSD1))
+#define is_vid_win(layer) (((layer) == WIN_VID0) || ((layer) == WIN_VID1))
+#define is_rgb_pixfmt(pixfmt) \
+ (((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888))
+#define is_yc_pixfmt(pixfmt) \
+ (((pixfmt) == PIXFMT_YCbCrI) || ((pixfmt) == PIXFMT_YCrCbI) || \
+ ((pixfmt) == PIXFMT_NV12))
+#define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X
+#define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5)
+
+/**
+ * _osd_dm6446_vid0_pingpong() - field inversion fix for DM6446
+ * @sd - ptr to struct osd_state
+ * @field_inversion - inversion flag
+ * @fb_base_phys - frame buffer address
+ * @lconfig - ptr to layer config
+ *
+ * This routine implements a workaround for the field signal inversion silicon
+ * erratum described in Advisory 1.3.8 for the DM6446. The fb_base_phys and
+ * lconfig parameters apply to the vid0 window. This routine should be called
+ * whenever the vid0 layer configuration or start address is modified, or when
+ * the OSD field inversion setting is modified.
+ * Returns: 1 if the ping-pong buffers need to be toggled in the vsync isr, or
+ * 0 otherwise
+ */
+static int _osd_dm6446_vid0_pingpong(struct osd_state *sd,
+ int field_inversion,
+ unsigned long fb_base_phys,
+ const struct osd_layer_config *lconfig)
+{
+ struct osd_platform_data *pdata;
+
+ pdata = (struct osd_platform_data *)sd->dev->platform_data;
+ if (pdata->field_inv_wa_enable) {
+
+ if (!field_inversion || !lconfig->interlaced) {
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR);
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_PPVWIN0ADR);
+ osd_modify(sd, OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, 0,
+ OSD_MISCCTL);
+ return 0;
+ } else {
+ unsigned miscctl = OSD_MISCCTL_PPRV;
+
+ osd_write(sd,
+ (fb_base_phys & ~0x1F) - lconfig->line_length,
+ OSD_VIDWIN0ADR);
+ osd_write(sd,
+ (fb_base_phys & ~0x1F) + lconfig->line_length,
+ OSD_PPVWIN0ADR);
+ osd_modify(sd,
+ OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, miscctl,
+ OSD_MISCCTL);
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void _osd_set_field_inversion(struct osd_state *sd, int enable)
+{
+ unsigned fsinv = 0;
+
+ if (enable)
+ fsinv = OSD_MODE_FSINV;
+
+ osd_modify(sd, OSD_MODE_FSINV, fsinv, OSD_MODE);
+}
+
+static void _osd_set_blink_attribute(struct osd_state *sd, int enable,
+ enum osd_blink_interval blink)
+{
+ u32 osdatrmd = 0;
+
+ if (enable) {
+ osdatrmd |= OSD_OSDATRMD_BLNK;
+ osdatrmd |= blink << OSD_OSDATRMD_BLNKINT_SHIFT;
+ }
+ /* caller must ensure that OSD1 is configured in attribute mode */
+ osd_modify(sd, OSD_OSDATRMD_BLNKINT | OSD_OSDATRMD_BLNK, osdatrmd,
+ OSD_OSDATRMD);
+}
+
+static void _osd_set_rom_clut(struct osd_state *sd,
+ enum osd_rom_clut rom_clut)
+{
+ if (rom_clut == ROM_CLUT0)
+ osd_clear(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL);
+ else
+ osd_set(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL);
+}
+
+static void _osd_set_palette_map(struct osd_state *sd,
+ enum osd_win_layer osdwin,
+ unsigned char pixel_value,
+ unsigned char clut_index,
+ enum osd_pix_format pixfmt)
+{
+ static const int map_2bpp[] = { 0, 5, 10, 15 };
+ static const int map_1bpp[] = { 0, 15 };
+ int bmp_offset;
+ int bmp_shift;
+ int bmp_mask;
+ int bmp_reg;
+
+ switch (pixfmt) {
+ case PIXFMT_1BPP:
+ bmp_reg = map_1bpp[pixel_value & 0x1];
+ break;
+ case PIXFMT_2BPP:
+ bmp_reg = map_2bpp[pixel_value & 0x3];
+ break;
+ case PIXFMT_4BPP:
+ bmp_reg = pixel_value & 0xf;
+ break;
+ default:
+ return;
+ }
+
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ bmp_offset = OSD_W0BMP01 + (bmp_reg >> 1) * sizeof(u32);
+ break;
+ case OSDWIN_OSD1:
+ bmp_offset = OSD_W1BMP01 + (bmp_reg >> 1) * sizeof(u32);
+ break;
+ default:
+ return;
+ }
+
+ if (bmp_reg & 1) {
+ bmp_shift = 8;
+ bmp_mask = 0xff << 8;
+ } else {
+ bmp_shift = 0;
+ bmp_mask = 0xff;
+ }
+
+ osd_modify(sd, bmp_mask, clut_index << bmp_shift, bmp_offset);
+}
+
+static void _osd_set_rec601_attenuation(struct osd_state *sd,
+ enum osd_win_layer osdwin, int enable)
+{
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ osd_modify(sd, OSD_OSDWIN0MD_ATN0E,
+ enable ? OSD_OSDWIN0MD_ATN0E : 0,
+ OSD_OSDWIN0MD);
+ break;
+ case OSDWIN_OSD1:
+ osd_modify(sd, OSD_OSDWIN1MD_ATN1E,
+ enable ? OSD_OSDWIN1MD_ATN1E : 0,
+ OSD_OSDWIN1MD);
+ break;
+ }
+}
+
+static void _osd_set_blending_factor(struct osd_state *sd,
+ enum osd_win_layer osdwin,
+ enum osd_blending_factor blend)
+{
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ osd_modify(sd, OSD_OSDWIN0MD_BLND0,
+ blend << OSD_OSDWIN0MD_BLND0_SHIFT, OSD_OSDWIN0MD);
+ break;
+ case OSDWIN_OSD1:
+ osd_modify(sd, OSD_OSDWIN1MD_BLND1,
+ blend << OSD_OSDWIN1MD_BLND1_SHIFT, OSD_OSDWIN1MD);
+ break;
+ }
+}
+
+static void _osd_enable_color_key(struct osd_state *sd,
+ enum osd_win_layer osdwin,
+ unsigned colorkey,
+ enum osd_pix_format pixfmt)
+{
+ switch (pixfmt) {
+ case PIXFMT_RGB565:
+ osd_write(sd, colorkey & OSD_TRANSPVAL_RGBTRANS,
+ OSD_TRANSPVAL);
+ break;
+ default:
+ break;
+ }
+
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ osd_set(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD);
+ break;
+ case OSDWIN_OSD1:
+ osd_set(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD);
+ break;
+ }
+}
+
+static void _osd_disable_color_key(struct osd_state *sd,
+ enum osd_win_layer osdwin)
+{
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ osd_clear(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD);
+ break;
+ case OSDWIN_OSD1:
+ osd_clear(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD);
+ break;
+ }
+}
+
+static void _osd_set_osd_clut(struct osd_state *sd,
+ enum osd_win_layer osdwin,
+ enum osd_clut clut)
+{
+ u32 winmd = 0;
+
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ if (clut == RAM_CLUT)
+ winmd |= OSD_OSDWIN0MD_CLUTS0;
+ osd_modify(sd, OSD_OSDWIN0MD_CLUTS0, winmd, OSD_OSDWIN0MD);
+ break;
+ case OSDWIN_OSD1:
+ if (clut == RAM_CLUT)
+ winmd |= OSD_OSDWIN1MD_CLUTS1;
+ osd_modify(sd, OSD_OSDWIN1MD_CLUTS1, winmd, OSD_OSDWIN1MD);
+ break;
+ }
+}
+
+static void _osd_set_zoom(struct osd_state *sd, enum osd_layer layer,
+ enum osd_zoom_factor h_zoom,
+ enum osd_zoom_factor v_zoom)
+{
+ u32 winmd = 0;
+
+ switch (layer) {
+ case WIN_OSD0:
+ winmd |= (h_zoom << OSD_OSDWIN0MD_OHZ0_SHIFT);
+ winmd |= (v_zoom << OSD_OSDWIN0MD_OVZ0_SHIFT);
+ osd_modify(sd, OSD_OSDWIN0MD_OHZ0 | OSD_OSDWIN0MD_OVZ0, winmd,
+ OSD_OSDWIN0MD);
+ break;
+ case WIN_VID0:
+ winmd |= (h_zoom << OSD_VIDWINMD_VHZ0_SHIFT);
+ winmd |= (v_zoom << OSD_VIDWINMD_VVZ0_SHIFT);
+ osd_modify(sd, OSD_VIDWINMD_VHZ0 | OSD_VIDWINMD_VVZ0, winmd,
+ OSD_VIDWINMD);
+ break;
+ case WIN_OSD1:
+ winmd |= (h_zoom << OSD_OSDWIN1MD_OHZ1_SHIFT);
+ winmd |= (v_zoom << OSD_OSDWIN1MD_OVZ1_SHIFT);
+ osd_modify(sd, OSD_OSDWIN1MD_OHZ1 | OSD_OSDWIN1MD_OVZ1, winmd,
+ OSD_OSDWIN1MD);
+ break;
+ case WIN_VID1:
+ winmd |= (h_zoom << OSD_VIDWINMD_VHZ1_SHIFT);
+ winmd |= (v_zoom << OSD_VIDWINMD_VVZ1_SHIFT);
+ osd_modify(sd, OSD_VIDWINMD_VHZ1 | OSD_VIDWINMD_VVZ1, winmd,
+ OSD_VIDWINMD);
+ break;
+ }
+}
+
+static void _osd_disable_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ switch (layer) {
+ case WIN_OSD0:
+ osd_clear(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD);
+ break;
+ case WIN_VID0:
+ osd_clear(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD);
+ break;
+ case WIN_OSD1:
+ /* disable attribute mode as well as disabling the window */
+ osd_clear(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1,
+ OSD_OSDWIN1MD);
+ break;
+ case WIN_VID1:
+ osd_clear(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD);
+ break;
+ }
+}
+
+static void osd_disable_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ if (!win->is_enabled) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return;
+ }
+ win->is_enabled = 0;
+
+ _osd_disable_layer(sd, layer);
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static void _osd_enable_attribute_mode(struct osd_state *sd)
+{
+ /* enable attribute mode for OSD1 */
+ osd_set(sd, OSD_OSDWIN1MD_OASW, OSD_OSDWIN1MD);
+}
+
+static void _osd_enable_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ switch (layer) {
+ case WIN_OSD0:
+ osd_set(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD);
+ break;
+ case WIN_VID0:
+ osd_set(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD);
+ break;
+ case WIN_OSD1:
+ /* enable OSD1 and disable attribute mode */
+ osd_modify(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1,
+ OSD_OSDWIN1MD_OACT1, OSD_OSDWIN1MD);
+ break;
+ case WIN_VID1:
+ osd_set(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD);
+ break;
+ }
+}
+
+static int osd_enable_layer(struct osd_state *sd, enum osd_layer layer,
+ int otherwin)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ struct osd_layer_config *cfg = &win->lconfig;
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ /*
+ * use otherwin flag to know this is the other vid window
+ * in YUV420 mode, if is, skip this check
+ */
+ if (!otherwin && (!win->is_allocated ||
+ !win->fb_base_phys ||
+ !cfg->line_length ||
+ !cfg->xsize ||
+ !cfg->ysize)) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return -1;
+ }
+
+ if (win->is_enabled) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return 0;
+ }
+ win->is_enabled = 1;
+
+ if (cfg->pixfmt != PIXFMT_OSD_ATTR)
+ _osd_enable_layer(sd, layer);
+ else {
+ _osd_enable_attribute_mode(sd);
+ _osd_set_blink_attribute(sd, osd->is_blinking, osd->blink);
+ }
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+
+ return 0;
+}
+
+static void _osd_start_layer(struct osd_state *sd, enum osd_layer layer,
+ unsigned long fb_base_phys,
+ unsigned long cbcr_ofst)
+{
+ switch (layer) {
+ case WIN_OSD0:
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN0ADR);
+ break;
+ case WIN_VID0:
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR);
+ break;
+ case WIN_OSD1:
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN1ADR);
+ break;
+ case WIN_VID1:
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN1ADR);
+ break;
+ }
+}
+
+static void osd_start_layer(struct osd_state *sd, enum osd_layer layer,
+ unsigned long fb_base_phys,
+ unsigned long cbcr_ofst)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ struct osd_layer_config *cfg = &win->lconfig;
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ win->fb_base_phys = fb_base_phys & ~0x1F;
+ _osd_start_layer(sd, layer, fb_base_phys, cbcr_ofst);
+
+ if (layer == WIN_VID0) {
+ osd->pingpong =
+ _osd_dm6446_vid0_pingpong(sd, osd->field_inversion,
+ win->fb_base_phys,
+ cfg);
+ }
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static void osd_get_layer_config(struct osd_state *sd, enum osd_layer layer,
+ struct osd_layer_config *lconfig)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ *lconfig = win->lconfig;
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+/**
+ * try_layer_config() - Try a specific configuration for the layer
+ * @sd - ptr to struct osd_state
+ * @layer - layer to configure
+ * @lconfig - layer configuration to try
+ *
+ * If the requested lconfig is completely rejected and the value of lconfig on
+ * exit is the current lconfig, then try_layer_config() returns 1. Otherwise,
+ * try_layer_config() returns 0. A return value of 0 does not necessarily mean
+ * that the value of lconfig on exit is identical to the value of lconfig on
+ * entry, but merely that it represents a change from the current lconfig.
+ */
+static int try_layer_config(struct osd_state *sd, enum osd_layer layer,
+ struct osd_layer_config *lconfig)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ int bad_config;
+
+ /* verify that the pixel format is compatible with the layer */
+ switch (lconfig->pixfmt) {
+ case PIXFMT_1BPP:
+ case PIXFMT_2BPP:
+ case PIXFMT_4BPP:
+ case PIXFMT_8BPP:
+ case PIXFMT_RGB565:
+ bad_config = !is_osd_win(layer);
+ break;
+ case PIXFMT_YCbCrI:
+ case PIXFMT_YCrCbI:
+ bad_config = !is_vid_win(layer);
+ break;
+ case PIXFMT_RGB888:
+ bad_config = !is_vid_win(layer);
+ break;
+ case PIXFMT_NV12:
+ bad_config = 1;
+ break;
+ case PIXFMT_OSD_ATTR:
+ bad_config = (layer != WIN_OSD1);
+ break;
+ default:
+ bad_config = 1;
+ break;
+ }
+ if (bad_config) {
+ /*
+ * The requested pixel format is incompatible with the layer,
+ * so keep the current layer configuration.
+ */
+ *lconfig = win->lconfig;
+ return bad_config;
+ }
+
+ /* DM6446: */
+ /* only one OSD window at a time can use RGB pixel formats */
+ if (is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) {
+ enum osd_pix_format pixfmt;
+ if (layer == WIN_OSD0)
+ pixfmt = osd->win[WIN_OSD1].lconfig.pixfmt;
+ else
+ pixfmt = osd->win[WIN_OSD0].lconfig.pixfmt;
+
+ if (is_rgb_pixfmt(pixfmt)) {
+ /*
+ * The other OSD window is already configured for an
+ * RGB, so keep the current layer configuration.
+ */
+ *lconfig = win->lconfig;
+ return 1;
+ }
+ }
+
+ /* DM6446: only one video window at a time can use RGB888 */
+ if (is_vid_win(layer) && lconfig->pixfmt == PIXFMT_RGB888) {
+ enum osd_pix_format pixfmt;
+
+ if (layer == WIN_VID0)
+ pixfmt = osd->win[WIN_VID1].lconfig.pixfmt;
+ else
+ pixfmt = osd->win[WIN_VID0].lconfig.pixfmt;
+
+ if (pixfmt == PIXFMT_RGB888) {
+ /*
+ * The other video window is already configured for
+ * RGB888, so keep the current layer configuration.
+ */
+ *lconfig = win->lconfig;
+ return 1;
+ }
+ }
+
+ /* window dimensions must be non-zero */
+ if (!lconfig->line_length || !lconfig->xsize || !lconfig->ysize) {
+ *lconfig = win->lconfig;
+ return 1;
+ }
+
+ /* round line_length up to a multiple of 32 */
+ lconfig->line_length = ((lconfig->line_length + 31) / 32) * 32;
+ lconfig->line_length =
+ min(lconfig->line_length, (unsigned)MAX_LINE_LENGTH);
+ lconfig->xsize = min(lconfig->xsize, (unsigned)MAX_WIN_SIZE);
+ lconfig->ysize = min(lconfig->ysize, (unsigned)MAX_WIN_SIZE);
+ lconfig->xpos = min(lconfig->xpos, (unsigned)MAX_WIN_SIZE);
+ lconfig->ypos = min(lconfig->ypos, (unsigned)MAX_WIN_SIZE);
+ lconfig->interlaced = (lconfig->interlaced != 0);
+ if (lconfig->interlaced) {
+ /* ysize and ypos must be even for interlaced displays */
+ lconfig->ysize &= ~1;
+ lconfig->ypos &= ~1;
+ }
+
+ return 0;
+}
+
+static void _osd_disable_vid_rgb888(struct osd_state *sd)
+{
+ /*
+ * The DM6446 supports RGB888 pixel format in a single video window.
+ * This routine disables RGB888 pixel format for both video windows.
+ * The caller must ensure that neither video window is currently
+ * configured for RGB888 pixel format.
+ */
+ osd_clear(sd, OSD_MISCCTL_RGBEN, OSD_MISCCTL);
+}
+
+static void _osd_enable_vid_rgb888(struct osd_state *sd,
+ enum osd_layer layer)
+{
+ /*
+ * The DM6446 supports RGB888 pixel format in a single video window.
+ * This routine enables RGB888 pixel format for the specified video
+ * window. The caller must ensure that the other video window is not
+ * currently configured for RGB888 pixel format, as this routine will
+ * disable RGB888 pixel format for the other window.
+ */
+ if (layer == WIN_VID0) {
+ osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
+ OSD_MISCCTL_RGBEN, OSD_MISCCTL);
+ } else if (layer == WIN_VID1) {
+ osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
+ OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
+ OSD_MISCCTL);
+ }
+}
+
+static void _osd_set_cbcr_order(struct osd_state *sd,
+ enum osd_pix_format pixfmt)
+{
+ /*
+ * The caller must ensure that all windows using YC pixfmt use the same
+ * Cb/Cr order.
+ */
+ if (pixfmt == PIXFMT_YCbCrI)
+ osd_clear(sd, OSD_MODE_CS, OSD_MODE);
+ else if (pixfmt == PIXFMT_YCrCbI)
+ osd_set(sd, OSD_MODE_CS, OSD_MODE);
+}
+
+static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer,
+ const struct osd_layer_config *lconfig)
+{
+ u32 winmd = 0, winmd_mask = 0, bmw = 0;
+
+ _osd_set_cbcr_order(sd, lconfig->pixfmt);
+
+ switch (layer) {
+ case WIN_OSD0:
+ winmd_mask |= OSD_OSDWIN0MD_RGB0E;
+ if (lconfig->pixfmt == PIXFMT_RGB565)
+ winmd |= OSD_OSDWIN0MD_RGB0E;
+
+ winmd_mask |= OSD_OSDWIN0MD_BMW0 | OSD_OSDWIN0MD_OFF0;
+
+ switch (lconfig->pixfmt) {
+ case PIXFMT_1BPP:
+ bmw = 0;
+ break;
+ case PIXFMT_2BPP:
+ bmw = 1;
+ break;
+ case PIXFMT_4BPP:
+ bmw = 2;
+ break;
+ case PIXFMT_8BPP:
+ bmw = 3;
+ break;
+ default:
+ break;
+ }
+ winmd |= (bmw << OSD_OSDWIN0MD_BMW0_SHIFT);
+
+ if (lconfig->interlaced)
+ winmd |= OSD_OSDWIN0MD_OFF0;
+
+ osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN0MD);
+ osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN0OFST);
+ osd_write(sd, lconfig->xpos, OSD_OSDWIN0XP);
+ osd_write(sd, lconfig->xsize, OSD_OSDWIN0XL);
+ if (lconfig->interlaced) {
+ osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN0YP);
+ osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN0YL);
+ } else {
+ osd_write(sd, lconfig->ypos, OSD_OSDWIN0YP);
+ osd_write(sd, lconfig->ysize, OSD_OSDWIN0YL);
+ }
+ break;
+ case WIN_VID0:
+ winmd_mask |= OSD_VIDWINMD_VFF0;
+ if (lconfig->interlaced)
+ winmd |= OSD_VIDWINMD_VFF0;
+
+ osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD);
+ osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN0OFST);
+ osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP);
+ osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL);
+ /*
+ * For YUV420P format the register contents are
+ * duplicated in both VID registers
+ */
+ if (lconfig->interlaced) {
+ osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN0YP);
+ osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN0YL);
+ } else {
+ osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP);
+ osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL);
+ }
+ break;
+ case WIN_OSD1:
+ /*
+ * The caller must ensure that OSD1 is disabled prior to
+ * switching from a normal mode to attribute mode or from
+ * attribute mode to a normal mode.
+ */
+ if (lconfig->pixfmt == PIXFMT_OSD_ATTR) {
+ winmd_mask |=
+ OSD_OSDWIN1MD_ATN1E | OSD_OSDWIN1MD_RGB1E |
+ OSD_OSDWIN1MD_CLUTS1 |
+ OSD_OSDWIN1MD_BLND1 | OSD_OSDWIN1MD_TE1;
+ } else {
+ winmd_mask |= OSD_OSDWIN1MD_RGB1E;
+ if (lconfig->pixfmt == PIXFMT_RGB565)
+ winmd |= OSD_OSDWIN1MD_RGB1E;
+
+ winmd_mask |= OSD_OSDWIN1MD_BMW1;
+ switch (lconfig->pixfmt) {
+ case PIXFMT_1BPP:
+ bmw = 0;
+ break;
+ case PIXFMT_2BPP:
+ bmw = 1;
+ break;
+ case PIXFMT_4BPP:
+ bmw = 2;
+ break;
+ case PIXFMT_8BPP:
+ bmw = 3;
+ break;
+ default:
+ break;
+ }
+ winmd |= (bmw << OSD_OSDWIN1MD_BMW1_SHIFT);
+ }
+
+ winmd_mask |= OSD_OSDWIN1MD_OFF1;
+ if (lconfig->interlaced)
+ winmd |= OSD_OSDWIN1MD_OFF1;
+
+ osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN1MD);
+ osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN1OFST);
+ osd_write(sd, lconfig->xpos, OSD_OSDWIN1XP);
+ osd_write(sd, lconfig->xsize, OSD_OSDWIN1XL);
+ if (lconfig->interlaced) {
+ osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN1YP);
+ osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN1YL);
+ } else {
+ osd_write(sd, lconfig->ypos, OSD_OSDWIN1YP);
+ osd_write(sd, lconfig->ysize, OSD_OSDWIN1YL);
+ }
+ break;
+ case WIN_VID1:
+ winmd_mask |= OSD_VIDWINMD_VFF1;
+ if (lconfig->interlaced)
+ winmd |= OSD_VIDWINMD_VFF1;
+
+ osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD);
+ osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN1OFST);
+ osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP);
+ osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL);
+ /*
+ * For YUV420P format the register contents are
+ * duplicated in both VID registers
+ */
+ osd_modify(sd, OSD_MISCCTL_S420D, ~OSD_MISCCTL_S420D,
+ OSD_MISCCTL);
+
+ if (lconfig->interlaced) {
+ osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN1YP);
+ osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN1YL);
+ } else {
+ osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP);
+ osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL);
+ }
+ break;
+ }
+}
+
+static int osd_set_layer_config(struct osd_state *sd, enum osd_layer layer,
+ struct osd_layer_config *lconfig)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ struct osd_layer_config *cfg = &win->lconfig;
+ unsigned long flags;
+ int reject_config;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ reject_config = try_layer_config(sd, layer, lconfig);
+ if (reject_config) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return reject_config;
+ }
+
+ /* update the current Cb/Cr order */
+ if (is_yc_pixfmt(lconfig->pixfmt))
+ osd->yc_pixfmt = lconfig->pixfmt;
+
+ /*
+ * If we are switching OSD1 from normal mode to attribute mode or from
+ * attribute mode to normal mode, then we must disable the window.
+ */
+ if (layer == WIN_OSD1) {
+ if (((lconfig->pixfmt == PIXFMT_OSD_ATTR) &&
+ (cfg->pixfmt != PIXFMT_OSD_ATTR)) ||
+ ((lconfig->pixfmt != PIXFMT_OSD_ATTR) &&
+ (cfg->pixfmt == PIXFMT_OSD_ATTR))) {
+ win->is_enabled = 0;
+ _osd_disable_layer(sd, layer);
+ }
+ }
+
+ _osd_set_layer_config(sd, layer, lconfig);
+
+ if (layer == WIN_OSD1) {
+ struct osd_osdwin_state *osdwin_state =
+ &osd->osdwin[OSDWIN_OSD1];
+
+ if ((lconfig->pixfmt != PIXFMT_OSD_ATTR) &&
+ (cfg->pixfmt == PIXFMT_OSD_ATTR)) {
+ /*
+ * We just switched OSD1 from attribute mode to normal
+ * mode, so we must initialize the CLUT select, the
+ * blend factor, transparency colorkey enable, and
+ * attenuation enable (DM6446 only) bits in the
+ * OSDWIN1MD register.
+ */
+ _osd_set_osd_clut(sd, OSDWIN_OSD1,
+ osdwin_state->clut);
+ _osd_set_blending_factor(sd, OSDWIN_OSD1,
+ osdwin_state->blend);
+ if (osdwin_state->colorkey_blending) {
+ _osd_enable_color_key(sd, OSDWIN_OSD1,
+ osdwin_state->
+ colorkey,
+ lconfig->pixfmt);
+ } else
+ _osd_disable_color_key(sd, OSDWIN_OSD1);
+ _osd_set_rec601_attenuation(sd, OSDWIN_OSD1,
+ osdwin_state->
+ rec601_attenuation);
+ } else if ((lconfig->pixfmt == PIXFMT_OSD_ATTR) &&
+ (cfg->pixfmt != PIXFMT_OSD_ATTR)) {
+ /*
+ * We just switched OSD1 from normal mode to attribute
+ * mode, so we must initialize the blink enable and
+ * blink interval bits in the OSDATRMD register.
+ */
+ _osd_set_blink_attribute(sd, osd->is_blinking,
+ osd->blink);
+ }
+ }
+
+ /*
+ * If we just switched to a 1-, 2-, or 4-bits-per-pixel bitmap format
+ * then configure a default palette map.
+ */
+ if ((lconfig->pixfmt != cfg->pixfmt) &&
+ ((lconfig->pixfmt == PIXFMT_1BPP) ||
+ (lconfig->pixfmt == PIXFMT_2BPP) ||
+ (lconfig->pixfmt == PIXFMT_4BPP))) {
+ enum osd_win_layer osdwin =
+ ((layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1);
+ struct osd_osdwin_state *osdwin_state =
+ &osd->osdwin[osdwin];
+ unsigned char clut_index;
+ unsigned char clut_entries = 0;
+
+ switch (lconfig->pixfmt) {
+ case PIXFMT_1BPP:
+ clut_entries = 2;
+ break;
+ case PIXFMT_2BPP:
+ clut_entries = 4;
+ break;
+ case PIXFMT_4BPP:
+ clut_entries = 16;
+ break;
+ default:
+ break;
+ }
+ /*
+ * The default palette map maps the pixel value to the clut
+ * index, i.e. pixel value 0 maps to clut entry 0, pixel value
+ * 1 maps to clut entry 1, etc.
+ */
+ for (clut_index = 0; clut_index < 16; clut_index++) {
+ osdwin_state->palette_map[clut_index] = clut_index;
+ if (clut_index < clut_entries) {
+ _osd_set_palette_map(sd, osdwin, clut_index,
+ clut_index,
+ lconfig->pixfmt);
+ }
+ }
+ }
+
+ *cfg = *lconfig;
+ /* DM6446: configure the RGB888 enable and window selection */
+ if (osd->win[WIN_VID0].lconfig.pixfmt == PIXFMT_RGB888)
+ _osd_enable_vid_rgb888(sd, WIN_VID0);
+ else if (osd->win[WIN_VID1].lconfig.pixfmt == PIXFMT_RGB888)
+ _osd_enable_vid_rgb888(sd, WIN_VID1);
+ else
+ _osd_disable_vid_rgb888(sd);
+
+ if (layer == WIN_VID0) {
+ osd->pingpong =
+ _osd_dm6446_vid0_pingpong(sd, osd->field_inversion,
+ win->fb_base_phys,
+ cfg);
+ }
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+
+ return 0;
+}
+
+static void osd_init_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ enum osd_win_layer osdwin;
+ struct osd_osdwin_state *osdwin_state;
+ struct osd_layer_config *cfg = &win->lconfig;
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ win->is_enabled = 0;
+ _osd_disable_layer(sd, layer);
+
+ win->h_zoom = ZOOM_X1;
+ win->v_zoom = ZOOM_X1;
+ _osd_set_zoom(sd, layer, win->h_zoom, win->v_zoom);
+
+ win->fb_base_phys = 0;
+ _osd_start_layer(sd, layer, win->fb_base_phys, 0);
+
+ cfg->line_length = 0;
+ cfg->xsize = 0;
+ cfg->ysize = 0;
+ cfg->xpos = 0;
+ cfg->ypos = 0;
+ cfg->interlaced = 0;
+ switch (layer) {
+ case WIN_OSD0:
+ case WIN_OSD1:
+ osdwin = (layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1;
+ osdwin_state = &osd->osdwin[osdwin];
+ /*
+ * Other code relies on the fact that OSD windows default to a
+ * bitmap pixel format when they are deallocated, so don't
+ * change this default pixel format.
+ */
+ cfg->pixfmt = PIXFMT_8BPP;
+ _osd_set_layer_config(sd, layer, cfg);
+ osdwin_state->clut = RAM_CLUT;
+ _osd_set_osd_clut(sd, osdwin, osdwin_state->clut);
+ osdwin_state->colorkey_blending = 0;
+ _osd_disable_color_key(sd, osdwin);
+ osdwin_state->blend = OSD_8_VID_0;
+ _osd_set_blending_factor(sd, osdwin, osdwin_state->blend);
+ osdwin_state->rec601_attenuation = 0;
+ _osd_set_rec601_attenuation(sd, osdwin,
+ osdwin_state->
+ rec601_attenuation);
+ if (osdwin == OSDWIN_OSD1) {
+ osd->is_blinking = 0;
+ osd->blink = BLINK_X1;
+ }
+ break;
+ case WIN_VID0:
+ case WIN_VID1:
+ cfg->pixfmt = osd->yc_pixfmt;
+ _osd_set_layer_config(sd, layer, cfg);
+ break;
+ }
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static void osd_release_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ if (!win->is_allocated) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+ osd_init_layer(sd, layer);
+ spin_lock_irqsave(&osd->lock, flags);
+
+ win->is_allocated = 0;
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static int osd_request_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ if (win->is_allocated) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return -1;
+ }
+ win->is_allocated = 1;
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+
+ return 0;
+}
+
+static void _osd_init(struct osd_state *sd)
+{
+ osd_write(sd, 0, OSD_MODE);
+ osd_write(sd, 0, OSD_VIDWINMD);
+ osd_write(sd, 0, OSD_OSDWIN0MD);
+ osd_write(sd, 0, OSD_OSDWIN1MD);
+ osd_write(sd, 0, OSD_RECTCUR);
+ osd_write(sd, 0, OSD_MISCCTL);
+}
+
+static void osd_set_left_margin(struct osd_state *sd, u32 val)
+{
+ osd_write(sd, val, OSD_BASEPX);
+}
+
+static void osd_set_top_margin(struct osd_state *sd, u32 val)
+{
+ osd_write(sd, val, OSD_BASEPY);
+}
+
+static int osd_initialize(struct osd_state *osd)
+{
+ if (osd == NULL)
+ return -ENODEV;
+ _osd_init(osd);
+
+ /* set default Cb/Cr order */
+ osd->yc_pixfmt = PIXFMT_YCbCrI;
+
+ _osd_set_field_inversion(osd, osd->field_inversion);
+ _osd_set_rom_clut(osd, osd->rom_clut);
+
+ osd_init_layer(osd, WIN_OSD0);
+ osd_init_layer(osd, WIN_VID0);
+ osd_init_layer(osd, WIN_OSD1);
+ osd_init_layer(osd, WIN_VID1);
+
+ return 0;
+}
+
+static const struct vpbe_osd_ops osd_ops = {
+ .initialize = osd_initialize,
+ .request_layer = osd_request_layer,
+ .release_layer = osd_release_layer,
+ .enable_layer = osd_enable_layer,
+ .disable_layer = osd_disable_layer,
+ .set_layer_config = osd_set_layer_config,
+ .get_layer_config = osd_get_layer_config,
+ .start_layer = osd_start_layer,
+ .set_left_margin = osd_set_left_margin,
+ .set_top_margin = osd_set_top_margin,
+};
+
+static int osd_probe(struct platform_device *pdev)
+{
+ struct osd_platform_data *pdata;
+ struct osd_state *osd;
+ struct resource *res;
+ int ret = 0;
+
+ osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL);
+ if (osd == NULL)
+ return -ENOMEM;
+
+ osd->dev = &pdev->dev;
+ pdata = (struct osd_platform_data *)pdev->dev.platform_data;
+ osd->vpbe_type = (enum vpbe_version)pdata->vpbe_type;
+ if (NULL == pdev->dev.platform_data) {
+ dev_err(osd->dev, "No platform data defined for OSD"
+ " sub device\n");
+ ret = -ENOENT;
+ goto free_mem;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(osd->dev, "Unable to get OSD register address map\n");
+ ret = -ENODEV;
+ goto free_mem;
+ }
+ osd->osd_base_phys = res->start;
+ osd->osd_size = resource_size(res);
+ if (!request_mem_region(osd->osd_base_phys, osd->osd_size,
+ MODULE_NAME)) {
+ dev_err(osd->dev, "Unable to reserve OSD MMIO region\n");
+ ret = -ENODEV;
+ goto free_mem;
+ }
+ osd->osd_base = (unsigned long)ioremap_nocache(res->start,
+ osd->osd_size);
+ if (!osd->osd_base) {
+ dev_err(osd->dev, "Unable to map the OSD region\n");
+ ret = -ENODEV;
+ goto release_mem_region;
+ }
+ spin_lock_init(&osd->lock);
+ osd->ops = osd_ops;
+ platform_set_drvdata(pdev, osd);
+ dev_notice(osd->dev, "OSD sub device probe success\n");
+ return ret;
+
+release_mem_region:
+ release_mem_region(osd->osd_base_phys, osd->osd_size);
+free_mem:
+ kfree(osd);
+ return ret;
+}
+
+static int osd_remove(struct platform_device *pdev)
+{
+ struct osd_state *osd = platform_get_drvdata(pdev);
+
+ iounmap((void *)osd->osd_base);
+ release_mem_region(osd->osd_base_phys, osd->osd_size);
+ kfree(osd);
+ return 0;
+}
+
+static struct platform_driver osd_driver = {
+ .probe = osd_probe,
+ .remove = osd_remove,
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int osd_init(void)
+{
+ if (platform_driver_register(&osd_driver)) {
+ printk(KERN_ERR "Unable to register davinci osd driver\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void osd_exit(void)
+{
+ platform_driver_unregister(&osd_driver);
+}
+
+module_init(osd_init);
+module_exit(osd_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DaVinci OSD Manager Driver");
+MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpbe_osd_regs.h b/drivers/media/video/davinci/vpbe_osd_regs.h
new file mode 100644
index 000000000000..584520f3af60
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_osd_regs.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2006-2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * 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 _VPBE_OSD_REGS_H
+#define _VPBE_OSD_REGS_H
+
+/* VPBE Global Registers */
+#define VPBE_PID 0x0
+#define VPBE_PCR 0x4
+
+/* VPSS CLock Registers */
+#define VPSSCLK_PID 0x00
+#define VPSSCLK_CLKCTRL 0x04
+
+/* VPSS Buffer Logic Registers */
+#define VPSSBL_PID 0x00
+#define VPSSBL_PCR 0x04
+#define VPSSBL_BCR 0x08
+#define VPSSBL_INTSTAT 0x0C
+#define VPSSBL_INTSEL 0x10
+#define VPSSBL_EVTSEL 0x14
+#define VPSSBL_MEMCTRL 0x18
+#define VPSSBL_CCDCMUX 0x1C
+
+/* DM365 ISP5 system configuration */
+#define ISP5_PID 0x0
+#define ISP5_PCCR 0x4
+#define ISP5_BCR 0x8
+#define ISP5_INTSTAT 0xC
+#define ISP5_INTSEL1 0x10
+#define ISP5_INTSEL2 0x14
+#define ISP5_INTSEL3 0x18
+#define ISP5_EVTSEL 0x1c
+#define ISP5_CCDCMUX 0x20
+
+/* VPBE On-Screen Display Subsystem Registers (OSD) */
+#define OSD_MODE 0x00
+#define OSD_VIDWINMD 0x04
+#define OSD_OSDWIN0MD 0x08
+#define OSD_OSDWIN1MD 0x0C
+#define OSD_OSDATRMD 0x0C
+#define OSD_RECTCUR 0x10
+#define OSD_VIDWIN0OFST 0x18
+#define OSD_VIDWIN1OFST 0x1C
+#define OSD_OSDWIN0OFST 0x20
+#define OSD_OSDWIN1OFST 0x24
+#define OSD_VIDWINADH 0x28
+#define OSD_VIDWIN0ADL 0x2C
+#define OSD_VIDWIN0ADR 0x2C
+#define OSD_VIDWIN1ADL 0x30
+#define OSD_VIDWIN1ADR 0x30
+#define OSD_OSDWINADH 0x34
+#define OSD_OSDWIN0ADL 0x38
+#define OSD_OSDWIN0ADR 0x38
+#define OSD_OSDWIN1ADL 0x3C
+#define OSD_OSDWIN1ADR 0x3C
+#define OSD_BASEPX 0x40
+#define OSD_BASEPY 0x44
+#define OSD_VIDWIN0XP 0x48
+#define OSD_VIDWIN0YP 0x4C
+#define OSD_VIDWIN0XL 0x50
+#define OSD_VIDWIN0YL 0x54
+#define OSD_VIDWIN1XP 0x58
+#define OSD_VIDWIN1YP 0x5C
+#define OSD_VIDWIN1XL 0x60
+#define OSD_VIDWIN1YL 0x64
+#define OSD_OSDWIN0XP 0x68
+#define OSD_OSDWIN0YP 0x6C
+#define OSD_OSDWIN0XL 0x70
+#define OSD_OSDWIN0YL 0x74
+#define OSD_OSDWIN1XP 0x78
+#define OSD_OSDWIN1YP 0x7C
+#define OSD_OSDWIN1XL 0x80
+#define OSD_OSDWIN1YL 0x84
+#define OSD_CURXP 0x88
+#define OSD_CURYP 0x8C
+#define OSD_CURXL 0x90
+#define OSD_CURYL 0x94
+#define OSD_W0BMP01 0xA0
+#define OSD_W0BMP23 0xA4
+#define OSD_W0BMP45 0xA8
+#define OSD_W0BMP67 0xAC
+#define OSD_W0BMP89 0xB0
+#define OSD_W0BMPAB 0xB4
+#define OSD_W0BMPCD 0xB8
+#define OSD_W0BMPEF 0xBC
+#define OSD_W1BMP01 0xC0
+#define OSD_W1BMP23 0xC4
+#define OSD_W1BMP45 0xC8
+#define OSD_W1BMP67 0xCC
+#define OSD_W1BMP89 0xD0
+#define OSD_W1BMPAB 0xD4
+#define OSD_W1BMPCD 0xD8
+#define OSD_W1BMPEF 0xDC
+#define OSD_VBNDRY 0xE0
+#define OSD_EXTMODE 0xE4
+#define OSD_MISCCTL 0xE8
+#define OSD_CLUTRAMYCB 0xEC
+#define OSD_CLUTRAMCR 0xF0
+#define OSD_TRANSPVAL 0xF4
+#define OSD_TRANSPVALL 0xF4
+#define OSD_TRANSPVALU 0xF8
+#define OSD_TRANSPBMPIDX 0xFC
+#define OSD_PPVWIN0ADR 0xFC
+
+/* bit definitions */
+#define VPBE_PCR_VENC_DIV (1 << 1)
+#define VPBE_PCR_CLK_OFF (1 << 0)
+
+#define VPSSBL_INTSTAT_HSSIINT (1 << 14)
+#define VPSSBL_INTSTAT_CFALDINT (1 << 13)
+#define VPSSBL_INTSTAT_IPIPE_INT5 (1 << 12)
+#define VPSSBL_INTSTAT_IPIPE_INT4 (1 << 11)
+#define VPSSBL_INTSTAT_IPIPE_INT3 (1 << 10)
+#define VPSSBL_INTSTAT_IPIPE_INT2 (1 << 9)
+#define VPSSBL_INTSTAT_IPIPE_INT1 (1 << 8)
+#define VPSSBL_INTSTAT_IPIPE_INT0 (1 << 7)
+#define VPSSBL_INTSTAT_IPIPEIFINT (1 << 6)
+#define VPSSBL_INTSTAT_OSDINT (1 << 5)
+#define VPSSBL_INTSTAT_VENCINT (1 << 4)
+#define VPSSBL_INTSTAT_H3AINT (1 << 3)
+#define VPSSBL_INTSTAT_CCDC_VDINT2 (1 << 2)
+#define VPSSBL_INTSTAT_CCDC_VDINT1 (1 << 1)
+#define VPSSBL_INTSTAT_CCDC_VDINT0 (1 << 0)
+
+/* DM365 ISP5 bit definitions */
+#define ISP5_INTSTAT_VENCINT (1 << 21)
+#define ISP5_INTSTAT_OSDINT (1 << 20)
+
+/* VMOD TVTYP options for HDMD=0 */
+#define SDTV_NTSC 0
+#define SDTV_PAL 1
+/* VMOD TVTYP options for HDMD=1 */
+#define HDTV_525P 0
+#define HDTV_625P 1
+#define HDTV_1080I 2
+#define HDTV_720P 3
+
+#define OSD_MODE_CS (1 << 15)
+#define OSD_MODE_OVRSZ (1 << 14)
+#define OSD_MODE_OHRSZ (1 << 13)
+#define OSD_MODE_EF (1 << 12)
+#define OSD_MODE_VVRSZ (1 << 11)
+#define OSD_MODE_VHRSZ (1 << 10)
+#define OSD_MODE_FSINV (1 << 9)
+#define OSD_MODE_BCLUT (1 << 8)
+#define OSD_MODE_CABG_SHIFT 0
+#define OSD_MODE_CABG (0xff << 0)
+
+#define OSD_VIDWINMD_VFINV (1 << 15)
+#define OSD_VIDWINMD_V1EFC (1 << 14)
+#define OSD_VIDWINMD_VHZ1_SHIFT 12
+#define OSD_VIDWINMD_VHZ1 (3 << 12)
+#define OSD_VIDWINMD_VVZ1_SHIFT 10
+#define OSD_VIDWINMD_VVZ1 (3 << 10)
+#define OSD_VIDWINMD_VFF1 (1 << 9)
+#define OSD_VIDWINMD_ACT1 (1 << 8)
+#define OSD_VIDWINMD_V0EFC (1 << 6)
+#define OSD_VIDWINMD_VHZ0_SHIFT 4
+#define OSD_VIDWINMD_VHZ0 (3 << 4)
+#define OSD_VIDWINMD_VVZ0_SHIFT 2
+#define OSD_VIDWINMD_VVZ0 (3 << 2)
+#define OSD_VIDWINMD_VFF0 (1 << 1)
+#define OSD_VIDWINMD_ACT0 (1 << 0)
+
+#define OSD_OSDWIN0MD_ATN0E (1 << 14)
+#define OSD_OSDWIN0MD_RGB0E (1 << 13)
+#define OSD_OSDWIN0MD_BMP0MD_SHIFT 13
+#define OSD_OSDWIN0MD_BMP0MD (3 << 13)
+#define OSD_OSDWIN0MD_CLUTS0 (1 << 12)
+#define OSD_OSDWIN0MD_OHZ0_SHIFT 10
+#define OSD_OSDWIN0MD_OHZ0 (3 << 10)
+#define OSD_OSDWIN0MD_OVZ0_SHIFT 8
+#define OSD_OSDWIN0MD_OVZ0 (3 << 8)
+#define OSD_OSDWIN0MD_BMW0_SHIFT 6
+#define OSD_OSDWIN0MD_BMW0 (3 << 6)
+#define OSD_OSDWIN0MD_BLND0_SHIFT 3
+#define OSD_OSDWIN0MD_BLND0 (7 << 3)
+#define OSD_OSDWIN0MD_TE0 (1 << 2)
+#define OSD_OSDWIN0MD_OFF0 (1 << 1)
+#define OSD_OSDWIN0MD_OACT0 (1 << 0)
+
+#define OSD_OSDWIN1MD_OASW (1 << 15)
+#define OSD_OSDWIN1MD_ATN1E (1 << 14)
+#define OSD_OSDWIN1MD_RGB1E (1 << 13)
+#define OSD_OSDWIN1MD_BMP1MD_SHIFT 13
+#define OSD_OSDWIN1MD_BMP1MD (3 << 13)
+#define OSD_OSDWIN1MD_CLUTS1 (1 << 12)
+#define OSD_OSDWIN1MD_OHZ1_SHIFT 10
+#define OSD_OSDWIN1MD_OHZ1 (3 << 10)
+#define OSD_OSDWIN1MD_OVZ1_SHIFT 8
+#define OSD_OSDWIN1MD_OVZ1 (3 << 8)
+#define OSD_OSDWIN1MD_BMW1_SHIFT 6
+#define OSD_OSDWIN1MD_BMW1 (3 << 6)
+#define OSD_OSDWIN1MD_BLND1_SHIFT 3
+#define OSD_OSDWIN1MD_BLND1 (7 << 3)
+#define OSD_OSDWIN1MD_TE1 (1 << 2)
+#define OSD_OSDWIN1MD_OFF1 (1 << 1)
+#define OSD_OSDWIN1MD_OACT1 (1 << 0)
+
+#define OSD_OSDATRMD_OASW (1 << 15)
+#define OSD_OSDATRMD_OHZA_SHIFT 10
+#define OSD_OSDATRMD_OHZA (3 << 10)
+#define OSD_OSDATRMD_OVZA_SHIFT 8
+#define OSD_OSDATRMD_OVZA (3 << 8)
+#define OSD_OSDATRMD_BLNKINT_SHIFT 6
+#define OSD_OSDATRMD_BLNKINT (3 << 6)
+#define OSD_OSDATRMD_OFFA (1 << 1)
+#define OSD_OSDATRMD_BLNK (1 << 0)
+
+#define OSD_RECTCUR_RCAD_SHIFT 8
+#define OSD_RECTCUR_RCAD (0xff << 8)
+#define OSD_RECTCUR_CLUTSR (1 << 7)
+#define OSD_RECTCUR_RCHW_SHIFT 4
+#define OSD_RECTCUR_RCHW (7 << 4)
+#define OSD_RECTCUR_RCVW_SHIFT 1
+#define OSD_RECTCUR_RCVW (7 << 1)
+#define OSD_RECTCUR_RCACT (1 << 0)
+
+#define OSD_VIDWIN0OFST_V0LO (0x1ff << 0)
+
+#define OSD_VIDWIN1OFST_V1LO (0x1ff << 0)
+
+#define OSD_OSDWIN0OFST_O0LO (0x1ff << 0)
+
+#define OSD_OSDWIN1OFST_O1LO (0x1ff << 0)
+
+#define OSD_WINOFST_AH_SHIFT 9
+
+#define OSD_VIDWIN0OFST_V0AH (0xf << 9)
+#define OSD_VIDWIN1OFST_V1AH (0xf << 9)
+#define OSD_OSDWIN0OFST_O0AH (0xf << 9)
+#define OSD_OSDWIN1OFST_O1AH (0xf << 9)
+
+#define OSD_VIDWINADH_V1AH_SHIFT 8
+#define OSD_VIDWINADH_V1AH (0x7f << 8)
+#define OSD_VIDWINADH_V0AH_SHIFT 0
+#define OSD_VIDWINADH_V0AH (0x7f << 0)
+
+#define OSD_VIDWIN0ADL_V0AL (0xffff << 0)
+
+#define OSD_VIDWIN1ADL_V1AL (0xffff << 0)
+
+#define OSD_OSDWINADH_O1AH_SHIFT 8
+#define OSD_OSDWINADH_O1AH (0x7f << 8)
+#define OSD_OSDWINADH_O0AH_SHIFT 0
+#define OSD_OSDWINADH_O0AH (0x7f << 0)
+
+#define OSD_OSDWIN0ADL_O0AL (0xffff << 0)
+
+#define OSD_OSDWIN1ADL_O1AL (0xffff << 0)
+
+#define OSD_BASEPX_BPX (0x3ff << 0)
+
+#define OSD_BASEPY_BPY (0x1ff << 0)
+
+#define OSD_VIDWIN0XP_V0X (0x7ff << 0)
+
+#define OSD_VIDWIN0YP_V0Y (0x7ff << 0)
+
+#define OSD_VIDWIN0XL_V0W (0x7ff << 0)
+
+#define OSD_VIDWIN0YL_V0H (0x7ff << 0)
+
+#define OSD_VIDWIN1XP_V1X (0x7ff << 0)
+
+#define OSD_VIDWIN1YP_V1Y (0x7ff << 0)
+
+#define OSD_VIDWIN1XL_V1W (0x7ff << 0)
+
+#define OSD_VIDWIN1YL_V1H (0x7ff << 0)
+
+#define OSD_OSDWIN0XP_W0X (0x7ff << 0)
+
+#define OSD_OSDWIN0YP_W0Y (0x7ff << 0)
+
+#define OSD_OSDWIN0XL_W0W (0x7ff << 0)
+
+#define OSD_OSDWIN0YL_W0H (0x7ff << 0)
+
+#define OSD_OSDWIN1XP_W1X (0x7ff << 0)
+
+#define OSD_OSDWIN1YP_W1Y (0x7ff << 0)
+
+#define OSD_OSDWIN1XL_W1W (0x7ff << 0)
+
+#define OSD_OSDWIN1YL_W1H (0x7ff << 0)
+
+#define OSD_CURXP_RCSX (0x7ff << 0)
+
+#define OSD_CURYP_RCSY (0x7ff << 0)
+
+#define OSD_CURXL_RCSW (0x7ff << 0)
+
+#define OSD_CURYL_RCSH (0x7ff << 0)
+
+#define OSD_EXTMODE_EXPMDSEL (1 << 15)
+#define OSD_EXTMODE_SCRNHEXP_SHIFT 13
+#define OSD_EXTMODE_SCRNHEXP (3 << 13)
+#define OSD_EXTMODE_SCRNVEXP (1 << 12)
+#define OSD_EXTMODE_OSD1BLDCHR (1 << 11)
+#define OSD_EXTMODE_OSD0BLDCHR (1 << 10)
+#define OSD_EXTMODE_ATNOSD1EN (1 << 9)
+#define OSD_EXTMODE_ATNOSD0EN (1 << 8)
+#define OSD_EXTMODE_OSDHRSZ15 (1 << 7)
+#define OSD_EXTMODE_VIDHRSZ15 (1 << 6)
+#define OSD_EXTMODE_ZMFILV1HEN (1 << 5)
+#define OSD_EXTMODE_ZMFILV1VEN (1 << 4)
+#define OSD_EXTMODE_ZMFILV0HEN (1 << 3)
+#define OSD_EXTMODE_ZMFILV0VEN (1 << 2)
+#define OSD_EXTMODE_EXPFILHEN (1 << 1)
+#define OSD_EXTMODE_EXPFILVEN (1 << 0)
+
+#define OSD_MISCCTL_BLDSEL (1 << 15)
+#define OSD_MISCCTL_S420D (1 << 14)
+#define OSD_MISCCTL_BMAPT (1 << 13)
+#define OSD_MISCCTL_DM365M (1 << 12)
+#define OSD_MISCCTL_RGBEN (1 << 7)
+#define OSD_MISCCTL_RGBWIN (1 << 6)
+#define OSD_MISCCTL_DMANG (1 << 6)
+#define OSD_MISCCTL_TMON (1 << 5)
+#define OSD_MISCCTL_RSEL (1 << 4)
+#define OSD_MISCCTL_CPBSY (1 << 3)
+#define OSD_MISCCTL_PPSW (1 << 2)
+#define OSD_MISCCTL_PPRV (1 << 1)
+
+#define OSD_CLUTRAMYCB_Y_SHIFT 8
+#define OSD_CLUTRAMYCB_Y (0xff << 8)
+#define OSD_CLUTRAMYCB_CB_SHIFT 0
+#define OSD_CLUTRAMYCB_CB (0xff << 0)
+
+#define OSD_CLUTRAMCR_CR_SHIFT 8
+#define OSD_CLUTRAMCR_CR (0xff << 8)
+#define OSD_CLUTRAMCR_CADDR_SHIFT 0
+#define OSD_CLUTRAMCR_CADDR (0xff << 0)
+
+#define OSD_TRANSPVAL_RGBTRANS (0xffff << 0)
+
+#define OSD_TRANSPVALL_RGBL (0xffff << 0)
+
+#define OSD_TRANSPVALU_Y_SHIFT 8
+#define OSD_TRANSPVALU_Y (0xff << 8)
+#define OSD_TRANSPVALU_RGBU_SHIFT 0
+#define OSD_TRANSPVALU_RGBU (0xff << 0)
+
+#define OSD_TRANSPBMPIDX_BMP1_SHIFT 8
+#define OSD_TRANSPBMPIDX_BMP1 (0xff << 8)
+#define OSD_TRANSPBMPIDX_BMP0_SHIFT 0
+#define OSD_TRANSPBMPIDX_BMP0 0xff
+
+#endif /* _DAVINCI_VPBE_H_ */
diff --git a/drivers/media/video/davinci/vpbe_venc.c b/drivers/media/video/davinci/vpbe_venc.c
new file mode 100644
index 000000000000..03a3e5c65ee7
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_venc.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <mach/mux.h>
+#include <mach/io.h>
+#include <mach/i2c.h>
+
+#include <linux/io.h>
+
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe_venc.h>
+#include <media/davinci/vpss.h>
+#include <media/v4l2-device.h>
+
+#include "vpbe_venc_regs.h"
+
+#define MODULE_NAME VPBE_VENC_SUBDEV_NAME
+
+static int debug = 2;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-2");
+
+struct venc_state {
+ struct v4l2_subdev sd;
+ struct venc_callback *callback;
+ struct venc_platform_data *pdata;
+ struct device *pdev;
+ u32 output;
+ v4l2_std_id std;
+ spinlock_t lock;
+ void __iomem *venc_base;
+ void __iomem *vdaccfg_reg;
+};
+
+static inline struct venc_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct venc_state, sd);
+}
+
+static inline u32 venc_read(struct v4l2_subdev *sd, u32 offset)
+{
+ struct venc_state *venc = to_state(sd);
+
+ return readl(venc->venc_base + offset);
+}
+
+static inline u32 venc_write(struct v4l2_subdev *sd, u32 offset, u32 val)
+{
+ struct venc_state *venc = to_state(sd);
+
+ writel(val, (venc->venc_base + offset));
+
+ return val;
+}
+
+static inline u32 venc_modify(struct v4l2_subdev *sd, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 new_val = (venc_read(sd, offset) & ~mask) | (val & mask);
+
+ venc_write(sd, offset, new_val);
+
+ return new_val;
+}
+
+static inline u32 vdaccfg_write(struct v4l2_subdev *sd, u32 val)
+{
+ struct venc_state *venc = to_state(sd);
+
+ writel(val, venc->vdaccfg_reg);
+
+ val = readl(venc->vdaccfg_reg);
+
+ return val;
+}
+
+/* This function sets the dac of the VPBE for various outputs
+ */
+static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index)
+{
+ switch (out_index) {
+ case 0:
+ v4l2_dbg(debug, 1, sd, "Setting output to Composite\n");
+ venc_write(sd, VENC_DACSEL, 0);
+ break;
+ case 1:
+ v4l2_dbg(debug, 1, sd, "Setting output to S-Video\n");
+ venc_write(sd, VENC_DACSEL, 0x210);
+ break;
+ case 2:
+ venc_write(sd, VENC_DACSEL, 0x543);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable)
+{
+ v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n");
+
+ if (benable) {
+ venc_write(sd, VENC_VMOD, 0);
+ venc_write(sd, VENC_CVBS, 0);
+ venc_write(sd, VENC_LCDOUT, 0);
+ venc_write(sd, VENC_HSPLS, 0);
+ venc_write(sd, VENC_HSTART, 0);
+ venc_write(sd, VENC_HVALID, 0);
+ venc_write(sd, VENC_HINT, 0);
+ venc_write(sd, VENC_VSPLS, 0);
+ venc_write(sd, VENC_VSTART, 0);
+ venc_write(sd, VENC_VVALID, 0);
+ venc_write(sd, VENC_VINT, 0);
+ venc_write(sd, VENC_YCCCTL, 0);
+ venc_write(sd, VENC_DACSEL, 0);
+
+ } else {
+ venc_write(sd, VENC_VMOD, 0);
+ /* disable VCLK output pin enable */
+ venc_write(sd, VENC_VIDCTL, 0x141);
+
+ /* Disable output sync pins */
+ venc_write(sd, VENC_SYNCCTL, 0);
+
+ /* Disable DCLOCK */
+ venc_write(sd, VENC_DCLKCTL, 0);
+ venc_write(sd, VENC_DRGBX1, 0x0000057C);
+
+ /* Disable LCD output control (accepting default polarity) */
+ venc_write(sd, VENC_LCDOUT, 0);
+ venc_write(sd, VENC_CMPNT, 0x100);
+ venc_write(sd, VENC_HSPLS, 0);
+ venc_write(sd, VENC_HINT, 0);
+ venc_write(sd, VENC_HSTART, 0);
+ venc_write(sd, VENC_HVALID, 0);
+
+ venc_write(sd, VENC_VSPLS, 0);
+ venc_write(sd, VENC_VINT, 0);
+ venc_write(sd, VENC_VSTART, 0);
+ venc_write(sd, VENC_VVALID, 0);
+
+ venc_write(sd, VENC_HSDLY, 0);
+ venc_write(sd, VENC_VSDLY, 0);
+
+ venc_write(sd, VENC_YCCCTL, 0);
+ venc_write(sd, VENC_VSTARTA, 0);
+
+ /* Set OSD clock and OSD Sync Adavance registers */
+ venc_write(sd, VENC_OSDCLK0, 1);
+ venc_write(sd, VENC_OSDCLK1, 2);
+ }
+}
+
+/*
+ * setting NTSC mode
+ */
+static int venc_set_ntsc(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+ struct venc_platform_data *pdata = venc->pdata;
+
+ v4l2_dbg(debug, 2, sd, "venc_set_ntsc\n");
+
+ /* Setup clock at VPSS & VENC for SD */
+ vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
+ if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0)
+ return -EINVAL;
+
+ venc_enabledigitaloutput(sd, 0);
+
+ /* to set VENC CLK DIV to 1 - final clock is 54 MHz */
+ venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
+ /* Set REC656 Mode */
+ venc_write(sd, VENC_YCCCTL, 0x1);
+ venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAFRQ);
+ venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAUPS);
+
+ venc_write(sd, VENC_VMOD, 0);
+ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+ VENC_VMOD_VIE);
+ venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
+ venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_TVTYP_SHIFT),
+ VENC_VMOD_TVTYP);
+ venc_write(sd, VENC_DACTST, 0x0);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+ return 0;
+}
+
+/*
+ * setting PAL mode
+ */
+static int venc_set_pal(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+
+ v4l2_dbg(debug, 2, sd, "venc_set_pal\n");
+
+ /* Setup clock at VPSS & VENC for SD */
+ vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
+ if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0)
+ return -EINVAL;
+
+ venc_enabledigitaloutput(sd, 0);
+
+ /* to set VENC CLK DIV to 1 - final clock is 54 MHz */
+ venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
+ /* Set REC656 Mode */
+ venc_write(sd, VENC_YCCCTL, 0x1);
+
+ venc_modify(sd, VENC_SYNCCTL, 1 << VENC_SYNCCTL_OVD_SHIFT,
+ VENC_SYNCCTL_OVD);
+ venc_write(sd, VENC_VMOD, 0);
+ venc_modify(sd, VENC_VMOD,
+ (1 << VENC_VMOD_VIE_SHIFT),
+ VENC_VMOD_VIE);
+ venc_modify(sd, VENC_VMOD,
+ (0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
+ venc_modify(sd, VENC_VMOD,
+ (1 << VENC_VMOD_TVTYP_SHIFT),
+ VENC_VMOD_TVTYP);
+ venc_write(sd, VENC_DACTST, 0x0);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+ return 0;
+}
+
+/*
+ * venc_set_480p59_94
+ *
+ * This function configures the video encoder to EDTV(525p) component setting.
+ */
+static int venc_set_480p59_94(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+ struct venc_platform_data *pdata = venc->pdata;
+
+ v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n");
+
+ /* Setup clock at VPSS & VENC for SD */
+ if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_480P59_94) < 0)
+ return -EINVAL;
+
+ venc_enabledigitaloutput(sd, 0);
+
+ venc_write(sd, VENC_OSDCLK0, 0);
+ venc_write(sd, VENC_OSDCLK1, 1);
+ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
+ VENC_VDPRO_DAFRQ);
+ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
+ VENC_VDPRO_DAUPS);
+ venc_write(sd, VENC_VMOD, 0);
+ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+ VENC_VMOD_VIE);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+ venc_modify(sd, VENC_VMOD, (HDTV_525P << VENC_VMOD_TVTYP_SHIFT),
+ VENC_VMOD_TVTYP);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
+ VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
+
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+ return 0;
+}
+
+/*
+ * venc_set_625p
+ *
+ * This function configures the video encoder to HDTV(625p) component setting
+ */
+static int venc_set_576p50(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+ struct venc_platform_data *pdata = venc->pdata;
+
+ v4l2_dbg(debug, 2, sd, "venc_set_576p50\n");
+
+ /* Setup clock at VPSS & VENC for SD */
+ if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_576P50) < 0)
+ return -EINVAL;
+
+ venc_enabledigitaloutput(sd, 0);
+
+ venc_write(sd, VENC_OSDCLK0, 0);
+ venc_write(sd, VENC_OSDCLK1, 1);
+
+ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
+ VENC_VDPRO_DAFRQ);
+ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
+ VENC_VDPRO_DAUPS);
+
+ venc_write(sd, VENC_VMOD, 0);
+ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+ VENC_VMOD_VIE);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+ venc_modify(sd, VENC_VMOD, (HDTV_625P << VENC_VMOD_TVTYP_SHIFT),
+ VENC_VMOD_TVTYP);
+
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
+ VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+ return 0;
+}
+
+static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ v4l2_dbg(debug, 1, sd, "venc_s_std_output\n");
+
+ if (norm & V4L2_STD_525_60)
+ return venc_set_ntsc(sd);
+ else if (norm & V4L2_STD_625_50)
+ return venc_set_pal(sd);
+
+ return -EINVAL;
+}
+
+static int venc_s_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *dv_preset)
+{
+ v4l2_dbg(debug, 1, sd, "venc_s_dv_preset\n");
+
+ if (dv_preset->preset == V4L2_DV_576P50)
+ return venc_set_576p50(sd);
+ else if (dv_preset->preset == V4L2_DV_480P59_94)
+ return venc_set_480p59_94(sd);
+
+ return -EINVAL;
+}
+
+static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output,
+ u32 config)
+{
+ struct venc_state *venc = to_state(sd);
+ int ret;
+
+ v4l2_dbg(debug, 1, sd, "venc_s_routing\n");
+
+ ret = venc_set_dac(sd, output);
+ if (!ret)
+ venc->output = output;
+
+ return ret;
+}
+
+static long venc_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd,
+ void *arg)
+{
+ u32 val;
+
+ switch (cmd) {
+ case VENC_GET_FLD:
+ val = venc_read(sd, VENC_VSTAT);
+ *((int *)arg) = ((val & VENC_VSTAT_FIDST) ==
+ VENC_VSTAT_FIDST);
+ break;
+ default:
+ v4l2_err(sd, "Wrong IOCTL cmd\n");
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops venc_core_ops = {
+ .ioctl = venc_ioctl,
+};
+
+static const struct v4l2_subdev_video_ops venc_video_ops = {
+ .s_routing = venc_s_routing,
+ .s_std_output = venc_s_std_output,
+ .s_dv_preset = venc_s_dv_preset,
+};
+
+static const struct v4l2_subdev_ops venc_ops = {
+ .core = &venc_core_ops,
+ .video = &venc_video_ops,
+};
+
+static int venc_initialize(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+ int ret;
+
+ /* Set default to output to composite and std to NTSC */
+ venc->output = 0;
+ venc->std = V4L2_STD_525_60;
+
+ ret = venc_s_routing(sd, 0, venc->output, 0);
+ if (ret < 0) {
+ v4l2_err(sd, "Error setting output during init\n");
+ return -EINVAL;
+ }
+
+ ret = venc_s_std_output(sd, venc->std);
+ if (ret < 0) {
+ v4l2_err(sd, "Error setting std during init\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int venc_device_get(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct venc_state **venc = data;
+
+ if (strcmp(MODULE_NAME, pdev->name) == 0)
+ *venc = platform_get_drvdata(pdev);
+
+ return 0;
+}
+
+struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev,
+ const char *venc_name)
+{
+ struct venc_state *venc;
+ int err;
+
+ err = bus_for_each_dev(&platform_bus_type, NULL, &venc,
+ venc_device_get);
+ if (venc == NULL)
+ return NULL;
+
+ v4l2_subdev_init(&venc->sd, &venc_ops);
+
+ strcpy(venc->sd.name, venc_name);
+ if (v4l2_device_register_subdev(v4l2_dev, &venc->sd) < 0) {
+ v4l2_err(v4l2_dev,
+ "vpbe unable to register venc sub device\n");
+ return NULL;
+ }
+ if (venc_initialize(&venc->sd)) {
+ v4l2_err(v4l2_dev,
+ "vpbe venc initialization failed\n");
+ return NULL;
+ }
+
+ return &venc->sd;
+}
+EXPORT_SYMBOL(venc_sub_dev_init);
+
+static int venc_probe(struct platform_device *pdev)
+{
+ struct venc_state *venc;
+ struct resource *res;
+ int ret;
+
+ venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL);
+ if (venc == NULL)
+ return -ENOMEM;
+
+ venc->pdev = &pdev->dev;
+ venc->pdata = pdev->dev.platform_data;
+ if (NULL == venc->pdata) {
+ dev_err(venc->pdev, "Unable to get platform data for"
+ " VENC sub device");
+ ret = -ENOENT;
+ goto free_mem;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(venc->pdev,
+ "Unable to get VENC register address map\n");
+ ret = -ENODEV;
+ goto free_mem;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), "venc")) {
+ dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n");
+ ret = -ENODEV;
+ goto free_mem;
+ }
+
+ venc->venc_base = ioremap_nocache(res->start, resource_size(res));
+ if (!venc->venc_base) {
+ dev_err(venc->pdev, "Unable to map VENC IO space\n");
+ ret = -ENODEV;
+ goto release_venc_mem_region;
+ }
+
+ spin_lock_init(&venc->lock);
+ platform_set_drvdata(pdev, venc);
+ dev_notice(venc->pdev, "VENC sub device probe success\n");
+ return 0;
+
+release_venc_mem_region:
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+free_mem:
+ kfree(venc);
+ return ret;
+}
+
+static int venc_remove(struct platform_device *pdev)
+{
+ struct venc_state *venc = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ iounmap((void *)venc->venc_base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(venc);
+
+ return 0;
+}
+
+static struct platform_driver venc_driver = {
+ .probe = venc_probe,
+ .remove = venc_remove,
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int venc_init(void)
+{
+ if (platform_driver_register(&venc_driver)) {
+ printk(KERN_ERR "Unable to register venc driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void venc_exit(void)
+{
+ platform_driver_unregister(&venc_driver);
+ return;
+}
+
+module_init(venc_init);
+module_exit(venc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VPBE VENC Driver");
+MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpbe_venc_regs.h b/drivers/media/video/davinci/vpbe_venc_regs.h
new file mode 100644
index 000000000000..947cb1510776
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_venc_regs.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2006-2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2..
+ *
+ * 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 _VPBE_VENC_REGS_H
+#define _VPBE_VENC_REGS_H
+
+/* VPBE Video Encoder / Digital LCD Subsystem Registers (VENC) */
+#define VENC_VMOD 0x00
+#define VENC_VIDCTL 0x04
+#define VENC_VDPRO 0x08
+#define VENC_SYNCCTL 0x0C
+#define VENC_HSPLS 0x10
+#define VENC_VSPLS 0x14
+#define VENC_HINT 0x18
+#define VENC_HSTART 0x1C
+#define VENC_HVALID 0x20
+#define VENC_VINT 0x24
+#define VENC_VSTART 0x28
+#define VENC_VVALID 0x2C
+#define VENC_HSDLY 0x30
+#define VENC_VSDLY 0x34
+#define VENC_YCCCTL 0x38
+#define VENC_RGBCTL 0x3C
+#define VENC_RGBCLP 0x40
+#define VENC_LINECTL 0x44
+#define VENC_CULLLINE 0x48
+#define VENC_LCDOUT 0x4C
+#define VENC_BRTS 0x50
+#define VENC_BRTW 0x54
+#define VENC_ACCTL 0x58
+#define VENC_PWMP 0x5C
+#define VENC_PWMW 0x60
+#define VENC_DCLKCTL 0x64
+#define VENC_DCLKPTN0 0x68
+#define VENC_DCLKPTN1 0x6C
+#define VENC_DCLKPTN2 0x70
+#define VENC_DCLKPTN3 0x74
+#define VENC_DCLKPTN0A 0x78
+#define VENC_DCLKPTN1A 0x7C
+#define VENC_DCLKPTN2A 0x80
+#define VENC_DCLKPTN3A 0x84
+#define VENC_DCLKHS 0x88
+#define VENC_DCLKHSA 0x8C
+#define VENC_DCLKHR 0x90
+#define VENC_DCLKVS 0x94
+#define VENC_DCLKVR 0x98
+#define VENC_CAPCTL 0x9C
+#define VENC_CAPDO 0xA0
+#define VENC_CAPDE 0xA4
+#define VENC_ATR0 0xA8
+#define VENC_ATR1 0xAC
+#define VENC_ATR2 0xB0
+#define VENC_VSTAT 0xB8
+#define VENC_RAMADR 0xBC
+#define VENC_RAMPORT 0xC0
+#define VENC_DACTST 0xC4
+#define VENC_YCOLVL 0xC8
+#define VENC_SCPROG 0xCC
+#define VENC_CVBS 0xDC
+#define VENC_CMPNT 0xE0
+#define VENC_ETMG0 0xE4
+#define VENC_ETMG1 0xE8
+#define VENC_ETMG2 0xEC
+#define VENC_ETMG3 0xF0
+#define VENC_DACSEL 0xF4
+#define VENC_ARGBX0 0x100
+#define VENC_ARGBX1 0x104
+#define VENC_ARGBX2 0x108
+#define VENC_ARGBX3 0x10C
+#define VENC_ARGBX4 0x110
+#define VENC_DRGBX0 0x114
+#define VENC_DRGBX1 0x118
+#define VENC_DRGBX2 0x11C
+#define VENC_DRGBX3 0x120
+#define VENC_DRGBX4 0x124
+#define VENC_VSTARTA 0x128
+#define VENC_OSDCLK0 0x12C
+#define VENC_OSDCLK1 0x130
+#define VENC_HVLDCL0 0x134
+#define VENC_HVLDCL1 0x138
+#define VENC_OSDHADV 0x13C
+#define VENC_CLKCTL 0x140
+#define VENC_GAMCTL 0x144
+#define VENC_XHINTVL 0x174
+
+/* bit definitions */
+#define VPBE_PCR_VENC_DIV (1 << 1)
+#define VPBE_PCR_CLK_OFF (1 << 0)
+
+#define VENC_VMOD_VDMD_SHIFT 12
+#define VENC_VMOD_VDMD_YCBCR16 0
+#define VENC_VMOD_VDMD_YCBCR8 1
+#define VENC_VMOD_VDMD_RGB666 2
+#define VENC_VMOD_VDMD_RGB8 3
+#define VENC_VMOD_VDMD_EPSON 4
+#define VENC_VMOD_VDMD_CASIO 5
+#define VENC_VMOD_VDMD_UDISPQVGA 6
+#define VENC_VMOD_VDMD_STNLCD 7
+#define VENC_VMOD_VIE_SHIFT 1
+#define VENC_VMOD_VDMD (7 << 12)
+#define VENC_VMOD_ITLCL (1 << 11)
+#define VENC_VMOD_ITLC (1 << 10)
+#define VENC_VMOD_NSIT (1 << 9)
+#define VENC_VMOD_HDMD (1 << 8)
+#define VENC_VMOD_TVTYP_SHIFT 6
+#define VENC_VMOD_TVTYP (3 << 6)
+#define VENC_VMOD_SLAVE (1 << 5)
+#define VENC_VMOD_VMD (1 << 4)
+#define VENC_VMOD_BLNK (1 << 3)
+#define VENC_VMOD_VIE (1 << 1)
+#define VENC_VMOD_VENC (1 << 0)
+
+/* VMOD TVTYP options for HDMD=0 */
+#define SDTV_NTSC 0
+#define SDTV_PAL 1
+/* VMOD TVTYP options for HDMD=1 */
+#define HDTV_525P 0
+#define HDTV_625P 1
+#define HDTV_1080I 2
+#define HDTV_720P 3
+
+#define VENC_VIDCTL_VCLKP (1 << 14)
+#define VENC_VIDCTL_VCLKE_SHIFT 13
+#define VENC_VIDCTL_VCLKE (1 << 13)
+#define VENC_VIDCTL_VCLKZ_SHIFT 12
+#define VENC_VIDCTL_VCLKZ (1 << 12)
+#define VENC_VIDCTL_SYDIR_SHIFT 8
+#define VENC_VIDCTL_SYDIR (1 << 8)
+#define VENC_VIDCTL_DOMD_SHIFT 4
+#define VENC_VIDCTL_DOMD (3 << 4)
+#define VENC_VIDCTL_YCDIR_SHIFT 0
+#define VENC_VIDCTL_YCDIR (1 << 0)
+
+#define VENC_VDPRO_ATYCC_SHIFT 5
+#define VENC_VDPRO_ATYCC (1 << 5)
+#define VENC_VDPRO_ATCOM_SHIFT 4
+#define VENC_VDPRO_ATCOM (1 << 4)
+#define VENC_VDPRO_DAFRQ (1 << 3)
+#define VENC_VDPRO_DAUPS (1 << 2)
+#define VENC_VDPRO_CUPS (1 << 1)
+#define VENC_VDPRO_YUPS (1 << 0)
+
+#define VENC_SYNCCTL_VPL_SHIFT 3
+#define VENC_SYNCCTL_VPL (1 << 3)
+#define VENC_SYNCCTL_HPL_SHIFT 2
+#define VENC_SYNCCTL_HPL (1 << 2)
+#define VENC_SYNCCTL_SYEV_SHIFT 1
+#define VENC_SYNCCTL_SYEV (1 << 1)
+#define VENC_SYNCCTL_SYEH_SHIFT 0
+#define VENC_SYNCCTL_SYEH (1 << 0)
+#define VENC_SYNCCTL_OVD_SHIFT 14
+#define VENC_SYNCCTL_OVD (1 << 14)
+
+#define VENC_DCLKCTL_DCKEC_SHIFT 11
+#define VENC_DCLKCTL_DCKEC (1 << 11)
+#define VENC_DCLKCTL_DCKPW_SHIFT 0
+#define VENC_DCLKCTL_DCKPW (0x3f << 0)
+
+#define VENC_VSTAT_FIDST (1 << 4)
+
+#define VENC_CMPNT_MRGB_SHIFT 14
+#define VENC_CMPNT_MRGB (1 << 14)
+
+#endif /* _VPBE_VENC_REGS_H */
diff --git a/drivers/media/video/davinci/vpif.c b/drivers/media/video/davinci/vpif.c
index 9f3bfc1eb240..af9680273ff9 100644
--- a/drivers/media/video/davinci/vpif.c
+++ b/drivers/media/video/davinci/vpif.c
@@ -422,7 +422,7 @@ static int __init vpif_probe(struct platform_device *pdev)
if (!res)
return -ENOENT;
- res_len = res->end - res->start + 1;
+ res_len = resource_size(res);
res = request_mem_region(res->start, res_len, res->name);
if (!res)
diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c
index d93ad74a34c5..49e4deb50043 100644
--- a/drivers/media/video/davinci/vpif_capture.c
+++ b/drivers/media/video/davinci/vpif_capture.c
@@ -33,7 +33,6 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/version.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -44,6 +43,7 @@
MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver");
MODULE_LICENSE("GPL");
+MODULE_VERSION(VPIF_CAPTURE_VERSION);
#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
#define vpif_dbg(level, debug, fmt, arg...) \
@@ -1677,7 +1677,6 @@ static int vpif_querycap(struct file *file, void *priv,
{
struct vpif_capture_config *config = vpif_dev->platform_data;
- cap->version = VPIF_CAPTURE_VERSION_CODE;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
strlcpy(cap->driver, "vpif capture", sizeof(cap->driver));
strlcpy(cap->bus_info, "DM646x Platform", sizeof(cap->bus_info));
@@ -2211,10 +2210,8 @@ static __init int vpif_probe(struct platform_device *pdev)
vfd->v4l2_dev = &vpif_obj.v4l2_dev;
vfd->release = video_device_release;
snprintf(vfd->name, sizeof(vfd->name),
- "DM646x_VPIFCapture_DRIVER_V%d.%d.%d",
- (VPIF_CAPTURE_VERSION_CODE >> 16) & 0xff,
- (VPIF_CAPTURE_VERSION_CODE >> 8) & 0xff,
- (VPIF_CAPTURE_VERSION_CODE) & 0xff);
+ "DM646x_VPIFCapture_DRIVER_V%s",
+ VPIF_CAPTURE_VERSION);
/* Set video_dev to the video device */
ch->video_dev = vfd;
}
diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h
index 7a4196dfdce1..064550f5ce4a 100644
--- a/drivers/media/video/davinci/vpif_capture.h
+++ b/drivers/media/video/davinci/vpif_capture.h
@@ -23,7 +23,6 @@
/* Header files */
#include <linux/videodev2.h>
-#include <linux/version.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/videobuf-core.h>
@@ -33,11 +32,7 @@
#include "vpif.h"
/* Macros */
-#define VPIF_MAJOR_RELEASE 0
-#define VPIF_MINOR_RELEASE 0
-#define VPIF_BUILD 1
-#define VPIF_CAPTURE_VERSION_CODE ((VPIF_MAJOR_RELEASE << 16) | \
- (VPIF_MINOR_RELEASE << 8) | VPIF_BUILD)
+#define VPIF_CAPTURE_VERSION "0.0.2"
#define VPIF_VALID_FIELD(field) (((V4L2_FIELD_ANY == field) || \
(V4L2_FIELD_NONE == field)) || \
diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c
index cdf659abdc2a..286f02910044 100644
--- a/drivers/media/video/davinci/vpif_display.c
+++ b/drivers/media/video/davinci/vpif_display.c
@@ -29,7 +29,6 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/version.h>
#include <linux/slab.h>
#include <asm/irq.h>
@@ -47,6 +46,7 @@
MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
MODULE_LICENSE("GPL");
+MODULE_VERSION(VPIF_DISPLAY_VERSION);
#define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
@@ -701,7 +701,6 @@ static int vpif_querycap(struct file *file, void *priv,
{
struct vpif_display_config *config = vpif_dev->platform_data;
- cap->version = VPIF_DISPLAY_VERSION_CODE;
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
strlcpy(cap->driver, "vpif display", sizeof(cap->driver));
strlcpy(cap->bus_info, "Platform", sizeof(cap->bus_info));
@@ -1740,10 +1739,8 @@ static __init int vpif_probe(struct platform_device *pdev)
vfd->v4l2_dev = &vpif_obj.v4l2_dev;
vfd->release = video_device_release;
snprintf(vfd->name, sizeof(vfd->name),
- "DM646x_VPIFDisplay_DRIVER_V%d.%d.%d",
- (VPIF_DISPLAY_VERSION_CODE >> 16) & 0xff,
- (VPIF_DISPLAY_VERSION_CODE >> 8) & 0xff,
- (VPIF_DISPLAY_VERSION_CODE) & 0xff);
+ "DM646x_VPIFDisplay_DRIVER_V%s",
+ VPIF_DISPLAY_VERSION);
/* Set video_dev to the video device */
ch->video_dev = vfd;
diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h
index b53aaa883075..5d1936dafed2 100644
--- a/drivers/media/video/davinci/vpif_display.h
+++ b/drivers/media/video/davinci/vpif_display.h
@@ -18,7 +18,6 @@
/* Header files */
#include <linux/videodev2.h>
-#include <linux/version.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/videobuf-core.h>
@@ -27,12 +26,7 @@
#include "vpif.h"
/* Macros */
-#define VPIF_MAJOR_RELEASE (0)
-#define VPIF_MINOR_RELEASE (0)
-#define VPIF_BUILD (1)
-
-#define VPIF_DISPLAY_VERSION_CODE \
- ((VPIF_MAJOR_RELEASE << 16) | (VPIF_MINOR_RELEASE << 8) | VPIF_BUILD)
+#define VPIF_DISPLAY_VERSION "0.0.2"
#define VPIF_VALID_FIELD(field) \
(((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index 3cb78f26df90..f6f622e123bd 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -3,7 +3,6 @@ config VIDEO_EM28XX
depends on VIDEO_DEV && I2C
select VIDEO_TUNER
select VIDEO_TVEEPROM
- depends on RC_CORE
select VIDEOBUF_VMALLOC
select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO
@@ -40,7 +39,20 @@ config VIDEO_EM28XX_DVB
select DVB_S921 if !DVB_FE_CUSTOMISE
select DVB_DRXD if !DVB_FE_CUSTOMISE
select DVB_CXD2820R if !DVB_FE_CUSTOMISE
+ select DVB_DRXK if !DVB_FE_CUSTOMISE
+ select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
+ select DVB_TDA10071 if !DVB_FE_CUSTOMISE
+ select DVB_A8293 if !DVB_FE_CUSTOMISE
select VIDEOBUF_DVB
---help---
This adds support for DVB cards based on the
Empiatech em28xx chips.
+
+config VIDEO_EM28XX_RC
+ bool "EM28XX Remote Controller support"
+ depends on RC_CORE
+ depends on VIDEO_EM28XX
+ depends on !(RC_CORE=m && VIDEO_EM28XX=y)
+ default y
+ ---help---
+ Enables Remote Controller support on em28xx driver.
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index d0f093d1d0df..2abdf76c5203 100644
--- a/drivers/media/video/em28xx/Makefile
+++ b/drivers/media/video/em28xx/Makefile
@@ -1,5 +1,7 @@
-em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \
- em28xx-input.o em28xx-vbi.o
+em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o
+em28xx-y += em28xx-core.o em28xx-vbi.o
+
+em28xx-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-input.o
em28xx-alsa-objs := em28xx-audio.o
@@ -7,8 +9,8 @@ obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index 3c48a72eb7de..cff0768afbf5 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -3,9 +3,9 @@
*
* Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
*
- * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Copyright (C) 2007-2011 Mauro Carvalho Chehab <mchehab@redhat.com>
* - Port to work with the in-kernel driver
- * - Several cleanups
+ * - Cleanups, fixes, alsa-controls, etc.
*
* This driver is based on my previous au600 usb pstn audio driver
* and inherits all the copyrights
@@ -41,6 +41,7 @@
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <media/v4l2-common.h>
#include "em28xx.h"
@@ -212,9 +213,12 @@ static int em28xx_init_audio_isoc(struct em28xx *dev)
for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
if (errCode) {
+ em28xx_errdev("submit of audio urb failed\n");
em28xx_deinit_isoc_audio(dev);
+ atomic_set(&dev->stream_started, 0);
return errCode;
}
+
}
return 0;
@@ -245,6 +249,7 @@ static struct snd_pcm_hardware snd_em28xx_hw_capture = {
.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_MMAP_VALID,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -276,24 +281,27 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
return -ENODEV;
}
- /* Sets volume, mute, etc */
+ runtime->hw = snd_em28xx_hw_capture;
+ if ((dev->alt == 0 || dev->audio_ifnum) && dev->adev.users == 0) {
+ if (dev->audio_ifnum)
+ dev->alt = 1;
+ else
+ dev->alt = 7;
- dev->mute = 0;
- mutex_lock(&dev->lock);
- ret = em28xx_audio_analog_set(dev);
- if (ret < 0)
- goto err;
+ dprintk("changing alternate number on interface %d to %d\n",
+ dev->audio_ifnum, dev->alt);
+ usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt);
- runtime->hw = snd_em28xx_hw_capture;
- if (dev->alt == 0 && dev->adev.users == 0) {
- int errCode;
- dev->alt = 7;
- dprintk("changing alternate number to 7\n");
- errCode = usb_set_interface(dev->udev, 0, 7);
- }
+ /* Sets volume, mute, etc */
+ dev->mute = 0;
+ mutex_lock(&dev->lock);
+ ret = em28xx_audio_analog_set(dev);
+ if (ret < 0)
+ goto err;
- dev->adev.users++;
- mutex_unlock(&dev->lock);
+ dev->adev.users++;
+ mutex_unlock(&dev->lock);
+ }
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
dev->adev.capture_pcm_substream = substream;
@@ -342,6 +350,8 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
ret = snd_pcm_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
+ if (ret < 0)
+ return ret;
format = params_format(hw_params);
rate = params_rate(hw_params);
channels = params_channels(hw_params);
@@ -393,20 +403,24 @@ static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
- int retval;
+ int retval = 0;
switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
+ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
case SNDRV_PCM_TRIGGER_START:
atomic_set(&dev->stream_started, 1);
break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
+ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
case SNDRV_PCM_TRIGGER_STOP:
- atomic_set(&dev->stream_started, 1);
+ atomic_set(&dev->stream_started, 0);
break;
default:
retval = -EINVAL;
}
schedule_work(&dev->wq_trigger);
- return 0;
+ return retval;
}
static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
@@ -432,6 +446,179 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
return vmalloc_to_page(pageptr);
}
+/*
+ * AC97 volume control support
+ */
+static int em28xx_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 2;
+ info->value.integer.min = 0;
+ info->value.integer.max = 0x1f;
+
+ return 0;
+}
+
+static int em28xx_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ u16 val = (0x1f - (value->value.integer.value[0] & 0x1f)) |
+ (0x1f - (value->value.integer.value[1] & 0x1f)) << 8;
+ int rc;
+
+ mutex_lock(&dev->lock);
+ rc = em28xx_read_ac97(dev, kcontrol->private_value);
+ if (rc < 0)
+ goto err;
+
+ val |= rc & 0x8000; /* Preserve the mute flag */
+
+ rc = em28xx_write_ac97(dev, kcontrol->private_value, val);
+ if (rc < 0)
+ goto err;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+err:
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int em28xx_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ int val;
+
+ mutex_lock(&dev->lock);
+ val = em28xx_read_ac97(dev, kcontrol->private_value);
+ mutex_unlock(&dev->lock);
+ if (val < 0)
+ return val;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+ value->value.integer.value[0] = 0x1f - (val & 0x1f);
+ value->value.integer.value[1] = 0x1f - ((val << 8) & 0x1f);
+
+ return 0;
+}
+
+static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ u16 val = value->value.integer.value[0];
+ int rc;
+
+ mutex_lock(&dev->lock);
+ rc = em28xx_read_ac97(dev, kcontrol->private_value);
+ if (rc < 0)
+ goto err;
+
+ if (val)
+ rc &= 0x1f1f;
+ else
+ rc |= 0x8000;
+
+ rc = em28xx_write_ac97(dev, kcontrol->private_value, rc);
+ if (rc < 0)
+ goto err;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+err:
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ int val;
+
+ mutex_lock(&dev->lock);
+ val = em28xx_read_ac97(dev, kcontrol->private_value);
+ mutex_unlock(&dev->lock);
+ if (val < 0)
+ return val;
+
+ if (val & 0x8000)
+ value->value.integer.value[0] = 0;
+ else
+ value->value.integer.value[0] = 1;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0);
+
+static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev,
+ char *name, int id)
+{
+ int err;
+ char ctl_name[44];
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_new tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ tmp.private_value = id,
+ tmp.name = ctl_name,
+
+ /* Add Mute Control */
+ sprintf(ctl_name, "%s Switch", name);
+ tmp.get = em28xx_vol_get_mute;
+ tmp.put = em28xx_vol_put_mute;
+ tmp.info = snd_ctl_boolean_mono_info;
+ kctl = snd_ctl_new1(&tmp, dev);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ dprintk("Added control %s for ac97 volume control 0x%04x\n",
+ ctl_name, id);
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ tmp.private_value = id,
+ tmp.name = ctl_name,
+
+ /* Add Volume Control */
+ sprintf(ctl_name, "%s Volume", name);
+ tmp.get = em28xx_vol_get;
+ tmp.put = em28xx_vol_put;
+ tmp.info = em28xx_vol_info;
+ tmp.tlv.p = em28xx_db_scale,
+ kctl = snd_ctl_new1(&tmp, dev);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ dprintk("Added control %s for ac97 volume control 0x%04x\n",
+ ctl_name, id);
+
+ return 0;
+}
+
+/*
+ * register/unregister code and data
+ */
static struct snd_pcm_ops snd_em28xx_pcm_capture = {
.open = snd_em28xx_capture_open,
.close = snd_em28xx_pcm_close,
@@ -452,17 +639,17 @@ static int em28xx_audio_init(struct em28xx *dev)
static int devnr;
int err;
- if (dev->has_alsa_audio != 1) {
+ if (!dev->has_alsa_audio || dev->audio_ifnum < 0) {
/* This device does not support the extension (in this case
the device is expecting the snd-usb-audio module or
doesn't have analog audio support at all) */
return 0;
}
- printk(KERN_INFO "em28xx-audio.c: probing for em28x1 "
- "non standard usbaudio\n");
+ printk(KERN_INFO "em28xx-audio.c: probing for em28xx Audio Vendor Class\n");
printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
"Rechberger\n");
+ printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab\n");
err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0,
&card);
@@ -488,6 +675,22 @@ static int em28xx_audio_init(struct em28xx *dev)
INIT_WORK(&dev->wq_trigger, audio_trigger);
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ em28xx_cvol_new(card, dev, "Video", AC97_VIDEO_VOL);
+ em28xx_cvol_new(card, dev, "Line In", AC97_LINEIN_VOL);
+ em28xx_cvol_new(card, dev, "Phone", AC97_PHONE_VOL);
+ em28xx_cvol_new(card, dev, "Microphone", AC97_PHONE_VOL);
+ em28xx_cvol_new(card, dev, "CD", AC97_CD_VOL);
+ em28xx_cvol_new(card, dev, "AUX", AC97_AUX_VOL);
+ em28xx_cvol_new(card, dev, "PCM", AC97_PCM_OUT_VOL);
+
+ em28xx_cvol_new(card, dev, "Master", AC97_MASTER_VOL);
+ em28xx_cvol_new(card, dev, "Line", AC97_LINE_LEVEL_VOL);
+ em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO_VOL);
+ em28xx_cvol_new(card, dev, "LFE", AC97_LFE_MASTER_VOL);
+ em28xx_cvol_new(card, dev, "Surround", AC97_SURR_MASTER_VOL);
+ }
+
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
@@ -538,7 +741,7 @@ static void __exit em28xx_alsa_unregister(void)
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
MODULE_DESCRIPTION("Em28xx Audio driver");
module_init(em28xx_alsa_register);
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 4e37375decf5..9b747c266afa 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -60,7 +60,7 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
module_param_array(card, int, NULL, 0444);
MODULE_PARM_DESC(card, "card type");
-/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
+/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */
static unsigned long em28xx_devused;
struct em28xx_hash_table {
@@ -289,7 +289,7 @@ static struct em28xx_reg_seq leadership_reset[] = {
{ -1, -1, -1, -1},
};
-/* 2013:024f PCTV Systems nanoStick T2 290e
+/* 2013:024f PCTV nanoStick T2 290e
* GPIO_6 - demod reset
* GPIO_7 - LED
*/
@@ -300,6 +300,42 @@ static struct em28xx_reg_seq pctv_290e[] = {
{-1, -1, -1, -1},
};
+#if 0
+static struct em28xx_reg_seq terratec_h5_gpio[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xf2, 0xff, 50},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 50},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq terratec_h5_digital[] = {
+ {EM2874_R80_GPIO, 0xf6, 0xff, 10},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xa6, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+#endif
+
+/* 2013:024f PCTV DVB-S2 Stick 460e
+ * GPIO_0 - POWER_ON
+ * GPIO_1 - BOOST
+ * GPIO_2 - VUV_LNB (red LED)
+ * GPIO_3 - EXT_12V
+ * GPIO_4 - INT_DEM (DEMOD GPIO_0)
+ * GPIO_5 - INT_LNB
+ * GPIO_6 - RESET_DEM
+ * GPIO_7 - LED (green LED)
+ */
+static struct em28xx_reg_seq pctv_460e[] = {
+ {EM2874_R80_GPIO, 0x01, 0xff, 50},
+ {0x0d, 0xff, 0xff, 50},
+ {EM2874_R80_GPIO, 0x41, 0xff, 50}, /* GPIO_6=1 */
+ {0x0d, 0x42, 0xff, 50},
+ {EM2874_R80_GPIO, 0x61, 0xff, 50}, /* GPIO_5=1 */
+ { -1, -1, -1, -1},
+};
+
/*
* Board definitions
*/
@@ -843,6 +879,19 @@ struct em28xx_board em28xx_boards[] = {
.gpio = terratec_cinergy_USB_XS_FR_analog,
} },
},
+ [EM2884_BOARD_TERRATEC_H5] = {
+ .name = "Terratec Cinergy H5",
+ .has_dvb = 1,
+#if 0
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x41,
+ .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */
+ .tuner_gpio = terratec_h5_gpio,
+#endif
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
[EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
.name = "Hauppauge WinTV HVR 900",
.tda9887_conf = TDA9887_PRESENT,
@@ -1259,7 +1308,7 @@ struct em28xx_board em28xx_boards[] = {
} },
},
- [EM2874_LEADERSHIP_ISDBT] = {
+ [EM2874_BOARD_LEADERSHIP_ISDBT] = {
.i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
EM28XX_I2C_CLK_WAIT_ENABLE |
EM28XX_I2C_FREQ_100_KHZ,
@@ -1319,7 +1368,6 @@ struct em28xx_board em28xx_boards[] = {
},
[EM2880_BOARD_KWORLD_DVB_305U] = {
.name = "KWorld DVB-T 305U",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_XC2028,
.tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
@@ -1770,16 +1818,27 @@ struct em28xx_board em28xx_boards[] = {
.dvb_gpio = kworld_a340_digital,
.tuner_gpio = default_tuner_gpio,
},
- /* 2013:024f PCTV Systems nanoStick T2 290e.
+ /* 2013:024f PCTV nanoStick T2 290e.
* Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */
[EM28174_BOARD_PCTV_290E] = {
+ .name = "PCTV nanoStick T2 290e",
.i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
- .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
- .name = "PCTV Systems nanoStick T2 290e",
.tuner_type = TUNER_ABSENT,
.tuner_gpio = pctv_290e,
.has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ },
+ /* 2013:024f PCTV DVB-S2 Stick 460e
+ * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */
+ [EM28174_BOARD_PCTV_460E] = {
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .name = "PCTV DVB-S2 Stick (460e)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_460e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
},
};
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
@@ -1855,13 +1914,17 @@ struct usb_device_id em28xx_id_table[] = {
{ USB_DEVICE(0x0ccd, 0x0042),
.driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
{ USB_DEVICE(0x0ccd, 0x0043),
- .driver_info = EM2870_BOARD_TERRATEC_XS },
- { USB_DEVICE(0x0ccd, 0x0047),
+ .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x10a2), /* Rev. 1 */
+ .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x10ad), /* Rev. 2 */
.driver_info = EM2880_BOARD_TERRATEC_PRODIGY_XS },
{ USB_DEVICE(0x0ccd, 0x0084),
.driver_info = EM2860_BOARD_TERRATEC_AV350 },
{ USB_DEVICE(0x0ccd, 0x0096),
.driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+ { USB_DEVICE(0x0ccd, 0x10AF),
+ .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
{ USB_DEVICE(0x0fd9, 0x0033),
.driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
{ USB_DEVICE(0x185b, 0x2870),
@@ -1910,6 +1973,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2870_BOARD_KWORLD_A340 },
{ USB_DEVICE(0x2013, 0x024f),
.driver_info = EM28174_BOARD_PCTV_290E },
+ { USB_DEVICE(0x2013, 0x024c),
+ .driver_info = EM28174_BOARD_PCTV_460E },
{ },
};
MODULE_DEVICE_TABLE(usb, em28xx_id_table);
@@ -1937,7 +2002,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = {
{0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT},
{0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC},
{0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF},
- {0x6b800080, EM2874_LEADERSHIP_ISDBT, TUNER_ABSENT},
+ {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT},
};
/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
@@ -2660,10 +2725,9 @@ void em28xx_card_setup(struct em28xx *dev)
.addr = 0xba >> 1,
.platform_data = &pdata,
};
- struct v4l2_subdev *sd;
pdata.xtal = dev->sensor_xtal;
- sd = v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap,
+ v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap,
&mt9v011_info, NULL);
}
@@ -2738,9 +2802,9 @@ static void flush_request_modules(struct em28xx *dev)
#endif /* CONFIG_MODULES */
/*
- * em28xx_realease_resources()
+ * em28xx_release_resources()
* unregisters the v4l2,i2c and usb devices
- * called when the device gets disconected or at module unload
+ * called when the device gets disconnected or at module unload
*/
void em28xx_release_resources(struct em28xx *dev)
{
@@ -2754,8 +2818,6 @@ void em28xx_release_resources(struct em28xx *dev)
em28xx_release_analog_resources(dev);
- em28xx_remove_from_devlist(dev);
-
em28xx_i2c_unregister(dev);
v4l2_device_unregister(&dev->v4l2_dev);
@@ -2763,7 +2825,7 @@ void em28xx_release_resources(struct em28xx *dev)
usb_put_dev(dev->udev);
/* Mark device as unused */
- em28xx_devused &= ~(1 << dev->devno);
+ clear_bit(dev->devno, &em28xx_devused);
};
/*
@@ -2776,7 +2838,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
{
struct em28xx *dev = *devhandle;
int retval;
- int errCode;
dev->udev = udev;
mutex_init(&dev->ctrl_urb_lock);
@@ -2842,11 +2903,25 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
em28xx_info("chip ID is em2882/em2883\n");
dev->wait_after_write = 0;
break;
+ case CHIP_ID_EM2884:
+ em28xx_info("chip ID is em2884\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
default:
em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
}
}
+ if (dev->is_audio_only) {
+ retval = em28xx_audio_setup(dev);
+ if (retval)
+ return -ENODEV;
+ em28xx_init_extension(dev);
+
+ return 0;
+ }
+
/* Prepopulate cached GPO register content */
retval = em28xx_read_reg(dev, dev->reg_gpo_num);
if (retval >= 0)
@@ -2858,7 +2933,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
/* Resets I2C speed */
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
if (retval < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req failed!"
+ em28xx_errdev("%s: em28xx_write_reg failed!"
" retval [%d]\n",
__func__, retval);
return retval;
@@ -2872,12 +2947,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
}
/* register i2c bus */
- errCode = em28xx_i2c_register(dev);
- if (errCode < 0) {
- v4l2_device_unregister(&dev->v4l2_dev);
- em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
- __func__, errCode);
- return errCode;
+ retval = em28xx_i2c_register(dev);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n",
+ __func__, retval);
+ goto unregister_dev;
}
/*
@@ -2891,11 +2965,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
em28xx_card_setup(dev);
/* Configure audio */
- errCode = em28xx_audio_setup(dev);
- if (errCode < 0) {
- v4l2_device_unregister(&dev->v4l2_dev);
- em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n",
- __func__, errCode);
+ retval = em28xx_audio_setup(dev);
+ if (retval < 0) {
+ em28xx_errdev("%s: Error while setting audio - error [%d]!\n",
+ __func__, retval);
+ goto fail;
}
/* wake i2c devices */
@@ -2909,44 +2983,47 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
if (dev->board.has_msp34xx) {
/* Send a reset to other chips via gpio */
- errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req - "
- "msp34xx(1) failed! errCode [%d]\n",
- __func__, errCode);
- return errCode;
+ retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_reg - "
+ "msp34xx(1) failed! error [%d]\n",
+ __func__, retval);
+ goto fail;
}
msleep(3);
- errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req - "
- "msp34xx(2) failed! errCode [%d]\n",
- __func__, errCode);
- return errCode;
+ retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_reg - "
+ "msp34xx(2) failed! error [%d]\n",
+ __func__, retval);
+ goto fail;
}
msleep(3);
}
- em28xx_add_into_devlist(dev);
-
retval = em28xx_register_analog_devices(dev);
if (retval < 0) {
- em28xx_release_resources(dev);
- goto fail_reg_devices;
+ goto fail;
}
- em28xx_init_extension(dev);
-
/* Save some power by putting tuner to sleep */
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
return 0;
-fail_reg_devices:
+fail:
+ em28xx_i2c_unregister(dev);
+
+unregister_dev:
+ v4l2_device_unregister(&dev->v4l2_dev);
+
return retval;
}
+/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+
/*
* em28xx_usb_probe()
* checks for supported devices
@@ -2956,19 +3033,27 @@ static int em28xx_usb_probe(struct usb_interface *interface,
{
const struct usb_endpoint_descriptor *endpoint;
struct usb_device *udev;
- struct usb_interface *uif;
struct em28xx *dev = NULL;
int retval;
- int i, nr, ifnum, isoc_pipe;
+ bool is_audio_only = false, has_audio = false;
+ int i, nr, isoc_pipe;
+ const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
char *speed;
char descr[255] = "";
udev = usb_get_dev(interface_to_usbdev(interface));
- ifnum = interface->altsetting[0].desc.bInterfaceNumber;
/* Check to see next free device and mark as used */
- nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
- em28xx_devused |= 1<<nr;
+ do {
+ nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+ if (nr >= EM28XX_MAXBOARDS) {
+ /* No free device slots */
+ printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
+ EM28XX_MAXBOARDS);
+ retval = -ENOMEM;
+ goto err_no_slot;
+ }
+ } while (test_and_set_bit(nr, &em28xx_devused));
/* Don't register audio interfaces */
if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
@@ -2979,11 +3064,23 @@ static int em28xx_usb_probe(struct usb_interface *interface,
ifnum,
interface->altsetting[0].desc.bInterfaceClass);
- em28xx_devused &= ~(1<<nr);
retval = -ENODEV;
goto err;
}
+ /* Get endpoints */
+ for (i = 0; i < interface->num_altsetting; i++) {
+ int ep;
+
+ for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
+ struct usb_host_endpoint *e;
+ e = &interface->altsetting[i].endpoint[ep];
+
+ if (e->desc.bEndpointAddress == 0x83)
+ has_audio = true;
+ }
+ }
+
endpoint = &interface->cur_altsetting->endpoint[0].desc;
/* check if the device has the iso in endpoint at the correct place */
@@ -3003,19 +3100,21 @@ static int em28xx_usb_probe(struct usb_interface *interface,
check_interface = 0;
if (!check_interface) {
- em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
- "interface %i, class %i found.\n",
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct),
- ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
-
- em28xx_err(DRIVER_NAME " This is an anciliary "
- "interface not used by the driver\n");
-
- em28xx_devused &= ~(1<<nr);
- retval = -ENODEV;
- goto err;
+ if (has_audio) {
+ is_audio_only = true;
+ } else {
+ em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
+ "interface %i, class %i found.\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting[0].desc.bInterfaceClass);
+ em28xx_err(DRIVER_NAME " This is an anciliary "
+ "interface not used by the driver\n");
+
+ retval = -ENODEV;
+ goto err;
+ }
}
}
@@ -3045,8 +3144,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
if (*descr)
strlcat(descr, " ", sizeof(descr));
- printk(DRIVER_NAME ": New device %s@ %s Mbps "
- "(%04x:%04x, interface %d, class %d)\n",
+ printk(KERN_INFO DRIVER_NAME
+ ": New device %s@ %s Mbps (%04x:%04x, interface %d, class %d)\n",
descr,
speed,
le16_to_cpu(udev->descriptor.idVendor),
@@ -3054,6 +3153,11 @@ static int em28xx_usb_probe(struct usb_interface *interface,
ifnum,
interface->altsetting->desc.bInterfaceNumber);
+ if (has_audio)
+ printk(KERN_INFO DRIVER_NAME
+ ": Audio Vendor Class interface %i found\n",
+ ifnum);
+
/*
* Make sure we have 480 Mbps of bandwidth, otherwise things like
* video stream wouldn't likely work, since 12 Mbps is generally
@@ -3063,36 +3167,29 @@ static int em28xx_usb_probe(struct usb_interface *interface,
printk(DRIVER_NAME ": Device initialization failed.\n");
printk(DRIVER_NAME ": Device must be connected to a high-speed"
" USB 2.0 port.\n");
- em28xx_devused &= ~(1<<nr);
retval = -ENODEV;
goto err;
}
- if (nr >= EM28XX_MAXBOARDS) {
- printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
- EM28XX_MAXBOARDS);
- em28xx_devused &= ~(1<<nr);
- retval = -ENOMEM;
- goto err;
- }
-
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
em28xx_err(DRIVER_NAME ": out of memory!\n");
- em28xx_devused &= ~(1<<nr);
retval = -ENOMEM;
goto err;
}
- snprintf(dev->name, 29, "em28xx #%d", nr);
+ snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr);
dev->devno = nr;
dev->model = id->driver_info;
dev->alt = -1;
+ dev->is_audio_only = is_audio_only;
+ dev->has_alsa_audio = has_audio;
+ dev->audio_ifnum = ifnum;
/* Checks if audio is provided by some interface */
for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
- uif = udev->config->interface[i];
+ struct usb_interface *uif = udev->config->interface[i];
if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
dev->has_audio_class = 1;
break;
@@ -3100,42 +3197,43 @@ static int em28xx_usb_probe(struct usb_interface *interface,
}
/* compute alternate max packet sizes */
- uif = udev->actconfig->interface[0];
-
- dev->num_alt = uif->num_altsetting;
+ dev->num_alt = interface->num_altsetting;
dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
if (dev->alt_max_pkt_size == NULL) {
em28xx_errdev("out of memory!\n");
- em28xx_devused &= ~(1<<nr);
kfree(dev);
retval = -ENOMEM;
goto err;
}
for (i = 0; i < dev->num_alt ; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
- dev->alt_max_pkt_size[i] =
- (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ u16 tmp = le16_to_cpu(interface->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
+ unsigned int size = tmp & 0x7ff;
+
+ if (udev->speed == USB_SPEED_HIGH)
+ size = size * hb_mult(tmp);
+
+ dev->alt_max_pkt_size[i] = size;
}
if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
dev->model = card[nr];
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
/* allocate device struct */
mutex_init(&dev->lock);
mutex_lock(&dev->lock);
retval = em28xx_init_dev(&dev, udev, interface, nr);
if (retval) {
- em28xx_devused &= ~(1<<dev->devno);
mutex_unlock(&dev->lock);
+ kfree(dev->alt_max_pkt_size);
kfree(dev);
goto err;
}
- /* save our data pointer in this interface device */
- usb_set_intfdata(interface, dev);
-
request_modules(dev);
/* Should be the last thing to do, to avoid newer udev's to
@@ -3143,15 +3241,26 @@ static int em28xx_usb_probe(struct usb_interface *interface,
*/
mutex_unlock(&dev->lock);
+ /*
+ * These extensions can be modules. If the modules are already
+ * loaded then we can initialise the device now, otherwise we
+ * will initialise it when the modules load instead.
+ */
+ em28xx_init_extension(dev);
+
return 0;
err:
+ clear_bit(nr, &em28xx_devused);
+
+err_no_slot:
+ usb_put_dev(udev);
return retval;
}
/*
* em28xx_usb_disconnect()
- * called when the device gets diconencted
+ * called when the device gets disconnected
* video device will be unregistered on v4l2_close in case it is still open
*/
static void em28xx_usb_disconnect(struct usb_interface *interface)
@@ -3164,6 +3273,13 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
if (!dev)
return;
+ if (dev->is_audio_only) {
+ mutex_lock(&dev->lock);
+ em28xx_close_extension(dev);
+ mutex_unlock(&dev->lock);
+ return;
+ }
+
em28xx_info("disconnecting %s\n", dev->vdev->name);
flush_request_modules(dev);
@@ -3192,10 +3308,10 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
em28xx_release_resources(dev);
}
- em28xx_close_extension(dev);
-
mutex_unlock(&dev->lock);
+ em28xx_close_extension(dev);
+
if (!dev->users) {
kfree(dev->alt_max_pkt_size);
kfree(dev);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index e33f145d867a..804a4ab47ac6 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -211,6 +211,7 @@ int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val)
{
return em28xx_write_regs(dev, reg, &val, 1);
}
+EXPORT_SYMBOL_GPL(em28xx_write_reg);
/*
* em28xx_write_reg_bits()
@@ -286,6 +287,7 @@ int em28xx_read_ac97(struct em28xx *dev, u8 reg)
return ret;
return le16_to_cpu(val);
}
+EXPORT_SYMBOL_GPL(em28xx_read_ac97);
/*
* em28xx_write_ac97()
@@ -313,13 +315,14 @@ int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val)
return 0;
}
+EXPORT_SYMBOL_GPL(em28xx_write_ac97);
-struct em28xx_vol_table {
+struct em28xx_vol_itable {
enum em28xx_amux mux;
u8 reg;
};
-static struct em28xx_vol_table inputs[] = {
+static struct em28xx_vol_itable inputs[] = {
{ EM28XX_AMUX_VIDEO, AC97_VIDEO_VOL },
{ EM28XX_AMUX_LINE_IN, AC97_LINEIN_VOL },
{ EM28XX_AMUX_PHONE, AC97_PHONE_VOL },
@@ -403,7 +406,12 @@ static int em28xx_set_audio_source(struct em28xx *dev)
return ret;
}
-static const struct em28xx_vol_table outputs[] = {
+struct em28xx_vol_otable {
+ enum em28xx_aout mux;
+ u8 reg;
+};
+
+static const struct em28xx_vol_otable outputs[] = {
{ EM28XX_AOUT_MASTER, AC97_MASTER_VOL },
{ EM28XX_AOUT_LINE, AC97_LINE_LEVEL_VOL },
{ EM28XX_AOUT_MONO, AC97_MASTER_MONO_VOL },
@@ -492,17 +500,13 @@ int em28xx_audio_setup(struct em28xx *dev)
if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874
|| dev->chip_id == CHIP_ID_EM28174) {
/* Digital only device - don't load any alsa module */
- dev->audio_mode.has_audio = 0;
- dev->has_audio_class = 0;
- dev->has_alsa_audio = 0;
+ dev->audio_mode.has_audio = false;
+ dev->has_audio_class = false;
+ dev->has_alsa_audio = false;
return 0;
}
- /* If device doesn't support Usb Audio Class, use vendor class */
- if (!dev->has_audio_class)
- dev->has_alsa_audio = 1;
-
- dev->audio_mode.has_audio = 1;
+ dev->audio_mode.has_audio = true;
/* See how this device is configured */
cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG);
@@ -512,8 +516,8 @@ int em28xx_audio_setup(struct em28xx *dev)
cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */
} else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) {
/* The device doesn't have vendor audio at all */
- dev->has_alsa_audio = 0;
- dev->audio_mode.has_audio = 0;
+ dev->has_alsa_audio = false;
+ dev->audio_mode.has_audio = false;
return 0;
} else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
EM28XX_CHIPCFG_I2S_3_SAMPRATES) {
@@ -542,8 +546,8 @@ int em28xx_audio_setup(struct em28xx *dev)
*/
em28xx_warn("AC97 chip type couldn't be determined\n");
dev->audio_mode.ac97 = EM28XX_NO_AC97;
- dev->has_alsa_audio = 0;
- dev->audio_mode.has_audio = 0;
+ dev->has_alsa_audio = false;
+ dev->audio_mode.has_audio = false;
goto init_audio;
}
@@ -615,7 +619,9 @@ int em28xx_capture_start(struct em28xx *dev, int start)
{
int rc;
- if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) {
+ if (dev->chip_id == CHIP_ID_EM2874 ||
+ dev->chip_id == CHIP_ID_EM2884 ||
+ dev->chip_id == CHIP_ID_EM28174) {
/* The Transport Stream Enable Register moved in em2874 */
if (!start) {
rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
@@ -884,6 +890,7 @@ int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio)
}
return rc;
}
+EXPORT_SYMBOL_GPL(em28xx_gpio_set);
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
{
@@ -917,7 +924,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode);
static void em28xx_irq_callback(struct urb *urb)
{
struct em28xx *dev = urb->context;
- int rc, i;
+ int i;
switch (urb->status) {
case 0: /* success */
@@ -934,7 +941,7 @@ static void em28xx_irq_callback(struct urb *urb)
/* Copy data from URB */
spin_lock(&dev->slock);
- rc = dev->isoc_ctl.isoc_copy(dev, urb);
+ dev->isoc_ctl.isoc_copy(dev, urb);
spin_unlock(&dev->slock);
/* Reset urb buffers */
@@ -1106,17 +1113,19 @@ EXPORT_SYMBOL_GPL(em28xx_init_isoc);
int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev)
{
unsigned int chip_cfg2;
- unsigned int packet_size = 564;
-
- if (dev->chip_id == CHIP_ID_EM2874) {
- /* FIXME - for now assume 564 like it was before, but the
- em2874 code should be added to return the proper value... */
- packet_size = 564;
- } else if (dev->chip_id == CHIP_ID_EM28174) {
- /* FIXME same as em2874. 564 was enough for 22 Mbit DVB-T
- but too much for 44 Mbit DVB-C. */
- packet_size = 752;
- } else {
+ unsigned int packet_size;
+
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2710:
+ case CHIP_ID_EM2750:
+ case CHIP_ID_EM2800:
+ case CHIP_ID_EM2820:
+ case CHIP_ID_EM2840:
+ case CHIP_ID_EM2860:
+ /* No DVB support */
+ return -EINVAL;
+ case CHIP_ID_EM2870:
+ case CHIP_ID_EM2883:
/* TS max packet size stored in bits 1-0 of R01 */
chip_cfg2 = em28xx_read_reg(dev, EM28XX_R01_CHIPCFG2);
switch (chip_cfg2 & EM28XX_CHIPCFG2_TS_PACKETSIZE_MASK) {
@@ -1133,9 +1142,24 @@ int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev)
packet_size = 752;
break;
}
+ break;
+ case CHIP_ID_EM2874:
+ /*
+ * FIXME: for now assumes 564 like it was before, but the
+ * em2874 code should be added to return the proper value
+ */
+ packet_size = 564;
+ break;
+ case CHIP_ID_EM2884:
+ case CHIP_ID_EM28174:
+ default:
+ /*
+ * FIXME: same as em2874. 564 was enough for 22 Mbit DVB-T
+ * but not enough for 44 Mbit DVB-C.
+ */
+ packet_size = 752;
}
- em28xx_coredbg("dvb max packet size=%d\n", packet_size);
return packet_size;
}
EXPORT_SYMBOL_GPL(em28xx_isoc_dvb_max_packetsize);
@@ -1160,25 +1184,6 @@ static LIST_HEAD(em28xx_devlist);
static DEFINE_MUTEX(em28xx_devlist_mutex);
/*
- * em28xx_realease_resources()
- * unregisters the v4l2,i2c and usb devices
- * called when the device gets disconected or at module unload
-*/
-void em28xx_remove_from_devlist(struct em28xx *dev)
-{
- mutex_lock(&em28xx_devlist_mutex);
- list_del(&dev->devlist);
- mutex_unlock(&em28xx_devlist_mutex);
-};
-
-void em28xx_add_into_devlist(struct em28xx *dev)
-{
- mutex_lock(&em28xx_devlist_mutex);
- list_add_tail(&dev->devlist, &em28xx_devlist);
- mutex_unlock(&em28xx_devlist_mutex);
-};
-
-/*
* Extension interface
*/
@@ -1193,8 +1198,8 @@ int em28xx_register_extension(struct em28xx_ops *ops)
list_for_each_entry(dev, &em28xx_devlist, devlist) {
ops->init(dev);
}
- printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
mutex_unlock(&em28xx_devlist_mutex);
+ printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
return 0;
}
EXPORT_SYMBOL(em28xx_register_extension);
@@ -1207,36 +1212,34 @@ void em28xx_unregister_extension(struct em28xx_ops *ops)
list_for_each_entry(dev, &em28xx_devlist, devlist) {
ops->fini(dev);
}
- printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
list_del(&ops->next);
mutex_unlock(&em28xx_devlist_mutex);
+ printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
}
EXPORT_SYMBOL(em28xx_unregister_extension);
void em28xx_init_extension(struct em28xx *dev)
{
- struct em28xx_ops *ops = NULL;
+ const struct em28xx_ops *ops = NULL;
mutex_lock(&em28xx_devlist_mutex);
- if (!list_empty(&em28xx_extension_devlist)) {
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->init)
- ops->init(dev);
- }
+ list_add_tail(&dev->devlist, &em28xx_devlist);
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->init)
+ ops->init(dev);
}
mutex_unlock(&em28xx_devlist_mutex);
}
void em28xx_close_extension(struct em28xx *dev)
{
- struct em28xx_ops *ops = NULL;
+ const struct em28xx_ops *ops = NULL;
mutex_lock(&em28xx_devlist_mutex);
- if (!list_empty(&em28xx_extension_devlist)) {
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->fini)
- ops->fini(dev);
- }
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->fini)
+ ops->fini(dev);
}
+ list_del(&dev->devlist);
mutex_unlock(&em28xx_devlist_mutex);
}
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index 7904ca4b6913..cef7a2d409cb 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -1,7 +1,7 @@
/*
DVB device driver for em28xx
- (c) 2008 Mauro Carvalho Chehab <mchehab@infradead.org>
+ (c) 2008-2011 Mauro Carvalho Chehab <mchehab@infradead.org>
(c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com>
- Fixes for the driver to properly work with HVR-950
@@ -40,6 +40,10 @@
#include "s921.h"
#include "drxd.h"
#include "cxd2820r.h"
+#include "tda18271c2dd.h"
+#include "drxk.h"
+#include "tda10071.h"
+#include "a8293.h"
MODULE_DESCRIPTION("driver for em28xx based DVB cards");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -73,6 +77,11 @@ struct em28xx_dvb {
struct dmx_frontend fe_hw;
struct dmx_frontend fe_mem;
struct dvb_net net;
+
+ /* Due to DRX-K - probably need changes */
+ int (*gate_ctrl)(struct dvb_frontend *, int);
+ struct semaphore pll_mutex;
+ bool dont_attach_fe1;
};
@@ -115,7 +124,7 @@ static inline void print_err_status(struct em28xx *dev,
}
}
-static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
+static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
{
int i;
@@ -148,7 +157,7 @@ static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
return 0;
}
-static int start_streaming(struct em28xx_dvb *dvb)
+static int em28xx_start_streaming(struct em28xx_dvb *dvb)
{
int rc;
struct em28xx *dev = dvb->adapter.priv;
@@ -160,13 +169,18 @@ static int start_streaming(struct em28xx_dvb *dvb)
return rc;
max_dvb_packet_size = em28xx_isoc_dvb_max_packetsize(dev);
+ if (max_dvb_packet_size < 0)
+ return max_dvb_packet_size;
+ dprintk(1, "Using %d buffers each with %d bytes\n",
+ EM28XX_DVB_NUM_BUFS,
+ max_dvb_packet_size);
return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS,
EM28XX_DVB_NUM_BUFS, max_dvb_packet_size,
- dvb_isoc_copy);
+ em28xx_dvb_isoc_copy);
}
-static int stop_streaming(struct em28xx_dvb *dvb)
+static int em28xx_stop_streaming(struct em28xx_dvb *dvb)
{
struct em28xx *dev = dvb->adapter.priv;
@@ -177,7 +191,7 @@ static int stop_streaming(struct em28xx_dvb *dvb)
return 0;
}
-static int start_feed(struct dvb_demux_feed *feed)
+static int em28xx_start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct em28xx_dvb *dvb = demux->priv;
@@ -191,7 +205,7 @@ static int start_feed(struct dvb_demux_feed *feed)
rc = dvb->nfeeds;
if (dvb->nfeeds == 1) {
- ret = start_streaming(dvb);
+ ret = em28xx_start_streaming(dvb);
if (ret < 0)
rc = ret;
}
@@ -200,7 +214,7 @@ static int start_feed(struct dvb_demux_feed *feed)
return rc;
}
-static int stop_feed(struct dvb_demux_feed *feed)
+static int em28xx_stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct em28xx_dvb *dvb = demux->priv;
@@ -210,7 +224,7 @@ static int stop_feed(struct dvb_demux_feed *feed)
dvb->nfeeds--;
if (0 == dvb->nfeeds)
- err = stop_streaming(dvb);
+ err = em28xx_stop_streaming(dvb);
mutex_unlock(&dvb->lock);
return err;
@@ -295,7 +309,80 @@ static struct drxd_config em28xx_drxd = {
.disable_i2c_gate_ctrl = 1,
};
-static int mt352_terratec_xs_init(struct dvb_frontend *fe)
+struct drxk_config terratec_h5_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .no_i2c_bridge = 1,
+ .microcode_name = "dvb-usb-terratec-h5-drxk.fw",
+};
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct em28xx_dvb *dvb = fe->sec_priv;
+ int status;
+
+ if (!dvb)
+ return -EINVAL;
+
+ if (enable) {
+ down(&dvb->pll_mutex);
+ status = dvb->gate_ctrl(fe, 1);
+ } else {
+ status = dvb->gate_ctrl(fe, 0);
+ up(&dvb->pll_mutex);
+ }
+ return status;
+}
+
+static void terratec_h5_init(struct em28xx *dev)
+{
+ int i;
+ struct em28xx_reg_seq terratec_h5_init[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xf2, 0xff, 50},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ { -1, -1, -1, -1},
+ };
+ struct em28xx_reg_seq terratec_h5_end[] = {
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xa6, 0xff, 50},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ { -1, -1, -1, -1},
+ };
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0x73, 0xaf }, 4},
+ {{ 0x04, 0x00 }, 2},
+ {{ 0x00, 0x04 }, 2},
+ {{ 0x00, 0x04, 0x00, 0x0a }, 4},
+ {{ 0x04, 0x14 }, 2},
+ {{ 0x04, 0x14, 0x00, 0x00 }, 4},
+ };
+
+ em28xx_gpio_set(dev, terratec_h5_init);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45);
+ msleep(10);
+
+ dev->i2c_client.addr = 0x82 >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+ em28xx_gpio_set(dev, terratec_h5_end);
+};
+
+static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe)
{
/* Values extracted from a USB trace of the Terratec Windows driver */
static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c };
@@ -327,7 +414,7 @@ static struct mt352_config terratec_xs_mt352_cfg = {
.demod_address = (0x1e >> 1),
.no_tuner = 1,
.if2 = 45600,
- .demod_init = mt352_terratec_xs_init,
+ .demod_init = em28xx_mt352_terratec_xs_init,
};
static struct tda10023_config em28xx_tda10023_config = {
@@ -353,11 +440,25 @@ static struct cxd2820r_config em28xx_cxd2820r_config = {
static struct tda18271_config em28xx_cxd2820r_tda18271_config = {
.output_opt = TDA18271_OUTPUT_LT_OFF,
+ .gate = TDA18271_GATE_DIGITAL,
+};
+
+static const struct tda10071_config em28xx_tda10071_config = {
+ .i2c_address = 0x55, /* (0xaa >> 1) */
+ .i2c_wr_max = 64,
+ .ts_mode = TDA10071_TS_SERIAL,
+ .spec_inv = 0,
+ .xtal = 40444000, /* 40.444 MHz */
+ .pll_multiplier = 20,
+};
+
+static const struct a8293_config em28xx_a8293_config = {
+ .i2c_addr = 0x08, /* (0x10 >> 1) */
};
/* ------------------------------------------------------------------ */
-static int attach_xc3028(u8 addr, struct em28xx *dev)
+static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)
{
struct dvb_frontend *fe;
struct xc2028_config cfg;
@@ -387,10 +488,8 @@ static int attach_xc3028(u8 addr, struct em28xx *dev)
/* ------------------------------------------------------------------ */
-static int register_dvb(struct em28xx_dvb *dvb,
- struct module *module,
- struct em28xx *dev,
- struct device *device)
+static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module,
+ struct em28xx *dev, struct device *device)
{
int result;
@@ -437,8 +536,8 @@ static int register_dvb(struct em28xx_dvb *dvb,
dvb->demux.priv = dvb;
dvb->demux.filternum = 256;
dvb->demux.feednum = 256;
- dvb->demux.start_feed = start_feed;
- dvb->demux.stop_feed = stop_feed;
+ dvb->demux.start_feed = em28xx_start_feed;
+ dvb->demux.stop_feed = em28xx_stop_feed;
result = dvb_dmx_init(&dvb->demux);
if (result < 0) {
@@ -506,7 +605,7 @@ fail_adapter:
return result;
}
-static void unregister_dvb(struct em28xx_dvb *dvb)
+static void em28xx_unregister_dvb(struct em28xx_dvb *dvb)
{
dvb_net_release(&dvb->net);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
@@ -516,15 +615,15 @@ static void unregister_dvb(struct em28xx_dvb *dvb)
if (dvb->fe[1])
dvb_unregister_frontend(dvb->fe[1]);
dvb_unregister_frontend(dvb->fe[0]);
- if (dvb->fe[1])
+ if (dvb->fe[1] && !dvb->dont_attach_fe1)
dvb_frontend_detach(dvb->fe[1]);
dvb_frontend_detach(dvb->fe[0]);
dvb_unregister_adapter(&dvb->adapter);
}
-static int dvb_init(struct em28xx *dev)
+static int em28xx_dvb_init(struct em28xx *dev)
{
- int result = 0;
+ int result = 0, mfe_shared = 0;
struct em28xx_dvb *dvb;
if (!dev->board.has_dvb) {
@@ -546,7 +645,7 @@ static int dvb_init(struct em28xx *dev)
em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
/* init frontend */
switch (dev->model) {
- case EM2874_LEADERSHIP_ISDBT:
+ case EM2874_BOARD_LEADERSHIP_ISDBT:
dvb->fe[0] = dvb_attach(s921_attach,
&sharp_isdbt, &dev->i2c_adap);
@@ -563,7 +662,7 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0] = dvb_attach(lgdt330x_attach,
&em2880_lgdt3303_dev,
&dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -572,7 +671,7 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0] = dvb_attach(zl10353_attach,
&em28xx_zl10353_with_xc3028,
&dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -583,7 +682,7 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0] = dvb_attach(zl10353_attach,
&em28xx_zl10353_xc3028_no_i2c_gate,
&dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -604,7 +703,7 @@ static int dvb_init(struct em28xx *dev)
&dev->i2c_adap);
}
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -614,7 +713,7 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0] = dvb_attach(s5h1409_attach,
&em28xx_s5h1409_with_xc3028,
&dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -635,7 +734,7 @@ static int dvb_init(struct em28xx *dev)
case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL,
&dev->i2c_adap, &dev->udev->dev);
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -668,11 +767,9 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0] = dvb_attach(cxd2820r_attach,
&em28xx_cxd2820r_config, &dev->i2c_adap, NULL);
if (dvb->fe[0]) {
- struct i2c_adapter *i2c_tuner;
- i2c_tuner = cxd2820r_get_tuner_i2c_adapter(dvb->fe[0]);
/* FE 0 attach tuner */
if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
- i2c_tuner, &em28xx_cxd2820r_tda18271_config)) {
+ &dev->i2c_adap, &em28xx_cxd2820r_tda18271_config)) {
dvb_frontend_detach(dvb->fe[0]);
result = -EINVAL;
goto out_free;
@@ -683,12 +780,59 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[1]->id = 1;
/* FE 1 attach tuner */
if (!dvb_attach(tda18271_attach, dvb->fe[1], 0x60,
- i2c_tuner, &em28xx_cxd2820r_tda18271_config)) {
+ &dev->i2c_adap, &em28xx_cxd2820r_tda18271_config)) {
dvb_frontend_detach(dvb->fe[1]);
/* leave FE 0 still active */
}
+
+ mfe_shared = 1;
}
break;
+ case EM2884_BOARD_TERRATEC_H5:
+ terratec_h5_init(dev);
+
+ dvb->dont_attach_fe1 = 1;
+
+ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap, &dvb->fe[1]);
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* FIXME: do we need a pll semaphore? */
+ dvb->fe[0]->sec_priv = dvb;
+ sema_init(&dvb->pll_mutex, 1);
+ dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl;
+ dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+ dvb->fe[1]->id = 1;
+
+ /* Attach tda18271 to DVB-C frontend */
+ if (dvb->fe[0]->ops.i2c_gate_ctrl)
+ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
+ if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap, 0x60)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ if (dvb->fe[0]->ops.i2c_gate_ctrl)
+ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0);
+
+ /* Hack - needed by drxk/tda18271c2dd */
+ dvb->fe[1]->tuner_priv = dvb->fe[0]->tuner_priv;
+ memcpy(&dvb->fe[1]->ops.tuner_ops,
+ &dvb->fe[0]->ops.tuner_ops,
+ sizeof(dvb->fe[0]->ops.tuner_ops));
+
+ break;
+ case EM28174_BOARD_PCTV_460E:
+ /* attach demod */
+ dvb->fe[0] = dvb_attach(tda10071_attach,
+ &em28xx_tda10071_config, &dev->i2c_adap);
+
+ /* attach SEC */
+ if (dvb->fe[0])
+ dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap,
+ &em28xx_a8293_config);
+ break;
default:
em28xx_errdev("/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n");
@@ -703,11 +847,14 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0]->callback = em28xx_tuner_callback;
/* register everything */
- result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
+ result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
if (result < 0)
goto out_free;
+ /* MFE lock */
+ dvb->adapter.mfe_shared = mfe_shared;
+
em28xx_info("Successfully loaded em28xx-dvb\n");
ret:
em28xx_set_mode(dev, EM28XX_SUSPEND);
@@ -720,7 +867,14 @@ out_free:
goto ret;
}
-static int dvb_fini(struct em28xx *dev)
+static inline void prevent_sleep(struct dvb_frontend_ops *ops)
+{
+ ops->set_voltage = NULL;
+ ops->sleep = NULL;
+ ops->tuner_ops.sleep = NULL;
+}
+
+static int em28xx_dvb_fini(struct em28xx *dev)
{
if (!dev->board.has_dvb) {
/* This device does not support the extension */
@@ -728,8 +882,19 @@ static int dvb_fini(struct em28xx *dev)
}
if (dev->dvb) {
- unregister_dvb(dev->dvb);
- kfree(dev->dvb);
+ struct em28xx_dvb *dvb = dev->dvb;
+
+ if (dev->state & DEV_DISCONNECTED) {
+ /* We cannot tell the device to sleep
+ * once it has been unplugged. */
+ if (dvb->fe[0])
+ prevent_sleep(&dvb->fe[0]->ops);
+ if (dvb->fe[1])
+ prevent_sleep(&dvb->fe[1]->ops);
+ }
+
+ em28xx_unregister_dvb(dvb);
+ kfree(dvb);
dev->dvb = NULL;
}
@@ -739,8 +904,8 @@ static int dvb_fini(struct em28xx *dev)
static struct em28xx_ops dvb_ops = {
.id = EM28XX_DVB,
.name = "Em28xx dvb Extension",
- .init = dvb_init,
- .fini = dvb_fini,
+ .init = em28xx_dvb_init,
+ .fini = em28xx_dvb_fini,
};
static int __init em28xx_dvb_register(void)
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index 4739fc7e6eb3..36f5a9bc8b76 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -181,16 +181,25 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr,
/*
* em28xx_i2c_send_bytes()
- * untested for more than 4 bytes
*/
static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf,
short len, int stop)
{
int wrcount = 0;
struct em28xx *dev = (struct em28xx *)data;
+ int write_timeout, ret;
wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len);
+ /* Seems to be required after a write */
+ for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0;
+ write_timeout -= 5) {
+ ret = dev->em28xx_read_reg(dev, 0x05);
+ if (!ret)
+ break;
+ msleep(5);
+ }
+
return wrcount;
}
@@ -218,9 +227,7 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr,
*/
static int em28xx_i2c_check_for_device(struct em28xx *dev, unsigned char addr)
{
- char msg;
int ret;
- msg = addr;
ret = dev->em28xx_read_reg_req(dev, 2, addr);
if (ret < 0) {
@@ -332,7 +339,9 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
struct em28xx_eeprom *em_eeprom = (void *)eedata;
int i, err, size = len, block;
- if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) {
+ if (dev->chip_id == CHIP_ID_EM2874 ||
+ dev->chip_id == CHIP_ID_EM28174 ||
+ dev->chip_id == CHIP_ID_EM2884) {
/* Empia switched to a 16-bit addressable eeprom in newer
devices. While we could certainly write a routine to read
the eeprom, there is nothing of use in there that cannot be
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index ba1ba8648c81..679da4804281 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -372,6 +372,7 @@ int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type)
ir->get_key = default_polling_getkey;
break;
case CHIP_ID_EM2874:
+ case CHIP_ID_EM28174:
ir->get_key = em2874_polling_getkey;
em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1);
break;
@@ -462,11 +463,11 @@ int em28xx_ir_fini(struct em28xx *dev)
if (!ir)
return 0;
- em28xx_ir_stop(ir->rc);
- rc_unregister_device(ir->rc);
- kfree(ir);
+ if (ir->rc)
+ rc_unregister_device(ir->rc);
/* done */
+ kfree(ir);
dev->ir = NULL;
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index e92a28ede434..66f792361b97 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -201,6 +201,7 @@ enum em28xx_chip_id {
CHIP_ID_EM2870 = 35,
CHIP_ID_EM2883 = 36,
CHIP_ID_EM2874 = 65,
+ CHIP_ID_EM2884 = 68,
CHIP_ID_EM28174 = 113,
};
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 7b6461d2d1ff..9b4557a2f6d0 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -32,7 +32,6 @@
#include <linux/bitmap.h>
#include <linux/usb.h>
#include <linux/i2c.h>
-#include <linux/version.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/slab.h>
@@ -50,7 +49,8 @@
"Sascha Sommer <saschasommer@freenet.de>"
#define DRIVER_DESC "Empia em28xx based USB video device driver"
-#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 2)
+
+#define EM28XX_VERSION "0.1.3"
#define em28xx_videodbg(fmt, arg...) do {\
if (video_debug) \
@@ -72,6 +72,7 @@ do {\
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+MODULE_VERSION(EM28XX_VERSION);
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
@@ -1155,6 +1156,21 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
return 0;
}
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
+
+ return 0;
+}
+
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
{
struct em28xx_fh *fh = priv;
@@ -1757,8 +1773,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->version = EM28XX_VERSION_CODE;
-
cap->capabilities =
V4L2_CAP_SLICED_VBI_CAPTURE |
V4L2_CAP_VIDEO_CAPTURE |
@@ -1788,6 +1802,45 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ struct em28xx_fmt *fmt;
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+
+ fmt = format_by_fourcc(fsize->pixel_format);
+ if (!fmt) {
+ em28xx_videodbg("Fourcc format (%08x) invalid.\n",
+ fsize->pixel_format);
+ return -EINVAL;
+ }
+
+ if (dev->board.is_em2800) {
+ if (fsize->index > 1)
+ return -EINVAL;
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = maxw / (1 + fsize->index);
+ fsize->discrete.height = maxh / (1 + fsize->index);
+ return 0;
+ }
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ /* Report a continuous range */
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = 48;
+ fsize->stepwise.min_height = 32;
+ fsize->stepwise.max_width = maxw;
+ fsize->stepwise.max_height = maxh;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
+ return 0;
+}
+
/* Sliced VBI ioctls */
static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
struct v4l2_format *f)
@@ -1976,7 +2029,6 @@ static int radio_querycap(struct file *file, void *priv,
strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->version = EM28XX_VERSION_CODE;
cap->capabilities = V4L2_CAP_TUNER;
return 0;
}
@@ -2202,6 +2254,7 @@ static int em28xx_v4l2_close(struct file *filp)
free the remaining resources */
if (dev->state & DEV_DISCONNECTED) {
em28xx_release_resources(dev);
+ kfree(dev->alt_max_pkt_size);
kfree(dev);
return 0;
}
@@ -2342,10 +2395,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
.vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
.vidioc_g_audio = vidioc_g_audio,
.vidioc_s_audio = vidioc_s_audio,
.vidioc_cropcap = vidioc_cropcap,
-
.vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
.vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
.vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
@@ -2355,6 +2408,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_g_std = vidioc_g_std,
+ .vidioc_querystd = vidioc_querystd,
.vidioc_s_std = vidioc_s_std,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
@@ -2450,10 +2504,8 @@ int em28xx_register_analog_devices(struct em28xx *dev)
u8 val;
int ret;
- printk(KERN_INFO "%s: v4l2 driver version %d.%d.%d\n",
- dev->name,
- (EM28XX_VERSION_CODE >> 16) & 0xff,
- (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
+ printk(KERN_INFO "%s: v4l2 driver version %s\n",
+ dev->name, EM28XX_VERSION);
/* set default norm */
dev->norm = em28xx_video_template.current_norm;
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 3cca33122450..2a2cb7ed0014 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -117,9 +117,10 @@
#define EM2800_BOARD_VC211A 74
#define EM2882_BOARD_DIKOM_DK300 75
#define EM2870_BOARD_KWORLD_A340 76
-#define EM2874_LEADERSHIP_ISDBT 77
+#define EM2874_BOARD_LEADERSHIP_ISDBT 77
#define EM28174_BOARD_PCTV_290E 78
-
+#define EM2884_BOARD_TERRATEC_H5 79
+#define EM28174_BOARD_PCTV_460E 80
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -487,6 +488,8 @@ struct em28xx {
int devno; /* marks the number of this device */
enum em28xx_chip_id chip_id;
+ int audio_ifnum;
+
struct v4l2_device v4l2_dev;
struct em28xx_board board;
@@ -503,6 +506,7 @@ struct em28xx {
unsigned int has_audio_class:1;
unsigned int has_alsa_audio:1;
+ unsigned int is_audio_only:1;
/* Controls audio streaming */
struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */
@@ -674,8 +678,6 @@ int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev);
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
void em28xx_wake_i2c(struct em28xx *dev);
-void em28xx_remove_from_devlist(struct em28xx *dev);
-void em28xx_add_into_devlist(struct em28xx *dev);
int em28xx_register_extension(struct em28xx_ops *dev);
void em28xx_unregister_extension(struct em28xx_ops *dev);
void em28xx_init_extension(struct em28xx *dev);
@@ -697,6 +699,9 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
void em28xx_release_resources(struct em28xx *dev);
/* Provided by em28xx-input.c */
+
+#ifdef CONFIG_VIDEO_EM28XX_RC
+
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
@@ -709,6 +714,20 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev);
int em28xx_ir_init(struct em28xx *dev);
int em28xx_ir_fini(struct em28xx *dev);
+#else
+
+#define em28xx_get_key_terratec NULL
+#define em28xx_get_key_em_haup NULL
+#define em28xx_get_key_pinnacle_usb_grey NULL
+#define em28xx_get_key_winfast_usbii_deluxe NULL
+
+static inline void em28xx_register_snapshot_button(struct em28xx *dev) {}
+static inline void em28xx_deregister_snapshot_button(struct em28xx *dev) {}
+static inline int em28xx_ir_init(struct em28xx *dev) { return 0; }
+static inline int em28xx_ir_fini(struct em28xx *dev) { return 0; }
+
+#endif
+
/* Provided by em28xx-vbi.c */
extern struct videobuf_queue_ops em28xx_vbi_qops;
diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h
index bf66189cb26d..337ded4a6388 100644
--- a/drivers/media/video/et61x251/et61x251.h
+++ b/drivers/media/video/et61x251/et61x251.h
@@ -21,7 +21,6 @@
#ifndef _ET61X251_H_
#define _ET61X251_H_
-#include <linux/version.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
@@ -166,45 +165,49 @@ et61x251_attach_sensor(struct et61x251_device* cam,
#undef DBG
#undef KDBG
#ifdef ET61X251_DEBUG
-# define DBG(level, fmt, args...) \
-do { \
- if (debug >= (level)) { \
- if ((level) == 1) \
- dev_err(&cam->usbdev->dev, fmt "\n", ## args); \
- else if ((level) == 2) \
- dev_info(&cam->usbdev->dev, fmt "\n", ## args); \
- else if ((level) >= 3) \
- dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
- __FILE__, __func__, __LINE__ , ## args); \
- } \
+#define DBG(level, fmt, ...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1) \
+ dev_err(&cam->usbdev->dev, fmt "\n", \
+ ##__VA_ARGS__); \
+ else if ((level) == 2) \
+ dev_info(&cam->usbdev->dev, fmt "\n", \
+ ##__VA_ARGS__); \
+ else if ((level) >= 3) \
+ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
+ __FILE__, __func__, __LINE__, \
+ ##__VA_ARGS__); \
+ } \
} while (0)
-# define KDBG(level, fmt, args...) \
-do { \
- if (debug >= (level)) { \
- if ((level) == 1 || (level) == 2) \
- pr_info("et61x251: " fmt "\n", ## args); \
- else if ((level) == 3) \
- pr_debug("sn9c102: [%s:%s:%d] " fmt "\n", __FILE__, \
- __func__, __LINE__ , ## args); \
- } \
+#define KDBG(level, fmt, ...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1 || (level) == 2) \
+ pr_info(fmt "\n", ##__VA_ARGS__); \
+ else if ((level) == 3) \
+ pr_debug("[%s:%s:%d] " fmt "\n", \
+ __FILE__, __func__, __LINE__, \
+ ##__VA_ARGS__); \
+ } \
} while (0)
-# define V4LDBG(level, name, cmd) \
-do { \
- if (debug >= (level)) \
- v4l_print_ioctl(name, cmd); \
+#define V4LDBG(level, name, cmd) \
+do { \
+ if (debug >= (level)) \
+ v4l_print_ioctl(name, cmd); \
} while (0)
#else
-# define DBG(level, fmt, args...) do {;} while(0)
-# define KDBG(level, fmt, args...) do {;} while(0)
-# define V4LDBG(level, name, cmd) do {;} while(0)
+#define DBG(level, fmt, ...) do {;} while(0)
+#define KDBG(level, fmt, ...) do {;} while(0)
+#define V4LDBG(level, name, cmd) do {;} while(0)
#endif
#undef PDBG
-#define PDBG(fmt, args...) \
-dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \
- __LINE__ , ## args)
+#define PDBG(fmt, ...) \
+ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
+ __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#undef PDBGG
-#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
+#define PDBGG(fmt, args...) do {;} while (0) /* placeholder */
#endif /* _ET61X251_H_ */
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
index a982750dcef1..d3777c86e1de 100644
--- a/drivers/media/video/et61x251/et61x251_core.c
+++ b/drivers/media/video/et61x251/et61x251_core.c
@@ -18,6 +18,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -48,8 +51,7 @@
#define ET61X251_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia"
#define ET61X251_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define ET61X251_MODULE_LICENSE "GPL"
-#define ET61X251_MODULE_VERSION "1:1.09"
-#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 9)
+#define ET61X251_MODULE_VERSION "1.1.10"
/*****************************************************************************/
@@ -1579,7 +1581,7 @@ et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_capability cap = {
.driver = "et61x251",
- .version = ET61X251_MODULE_VERSION_CODE,
+ .version = LINUX_VERSION_CODE,
.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING,
};
@@ -2480,16 +2482,8 @@ static long et61x251_ioctl_v4l2(struct file *filp,
case VIDIOC_S_PARM:
return et61x251_vidioc_s_parm(cam, arg);
- case VIDIOC_G_STD:
- case VIDIOC_S_STD:
- case VIDIOC_QUERYSTD:
- case VIDIOC_ENUMSTD:
- case VIDIOC_QUERYMENU:
- case VIDIOC_ENUM_FRAMEINTERVALS:
- return -EINVAL;
-
default:
- return -EINVAL;
+ return -ENOTTY;
}
}
diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c
index 04b7fbb310a8..ced2e167935d 100644
--- a/drivers/media/video/et61x251/et61x251_tas5130d1b.c
+++ b/drivers/media/video/et61x251/et61x251_tas5130d1b.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "et61x251_sensor.h"
diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c
index 908d7012c3f2..27cb197d0bd6 100644
--- a/drivers/media/video/fsl-viu.c
+++ b/drivers/media/video/fsl-viu.c
@@ -23,19 +23,13 @@
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
-#include <linux/version.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf-dma-contig.h>
#define DRV_NAME "fsl_viu"
-#define VIU_MAJOR_VERSION 0
-#define VIU_MINOR_VERSION 5
-#define VIU_RELEASE 0
-#define VIU_VERSION KERNEL_VERSION(VIU_MAJOR_VERSION, \
- VIU_MINOR_VERSION, \
- VIU_RELEASE)
+#define VIU_VERSION "0.5.1"
#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
@@ -610,7 +604,6 @@ static int vidioc_querycap(struct file *file, void *priv,
{
strcpy(cap->driver, "viu");
strcpy(cap->card, "viu");
- cap->version = VIU_VERSION;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING |
V4L2_CAP_VIDEO_OVERLAY |
@@ -1684,3 +1677,4 @@ module_exit(viu_exit);
MODULE_DESCRIPTION("Freescale Video-In(VIU)");
MODULE_AUTHOR("Hongjun Chen");
MODULE_LICENSE("GPL");
+MODULE_VERSION(VIU_VERSION);
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index 34ae2c299799..103af3fe5aa0 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -179,6 +179,16 @@ config USB_GSPCA_PAC7311
To compile this driver as a module, choose M here: the
module will be called gspca_pac7311.
+config USB_GSPCA_SE401
+ tristate "SE401 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ Endpoints (formerly known as AOX) se401 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_se401.
+
config USB_GSPCA_SN9C2028
tristate "SONIX Dual-Mode USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
@@ -346,6 +356,16 @@ config USB_GSPCA_T613
To compile this driver as a module, choose M here: the
module will be called gspca_t613.
+config USB_GSPCA_TOPRO
+ tristate "TOPRO USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ TP6800 and TP6810 Topro chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_topro.
+
config USB_GSPCA_TV8532
tristate "TV8532 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index 802fbe1bff4a..f345f494d0f3 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_USB_GSPCA_OV534_9) += gspca_ov534_9.o
obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o
obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o
+obj-$(CONFIG_USB_GSPCA_SE401) += gspca_se401.o
obj-$(CONFIG_USB_GSPCA_SN9C2028) += gspca_sn9c2028.o
obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o
obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
@@ -34,6 +35,7 @@ obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o
obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
+obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o
obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o
@@ -58,6 +60,7 @@ gspca_ov534_9-objs := ov534_9.o
gspca_pac207-objs := pac207.o
gspca_pac7302-objs := pac7302.o
gspca_pac7311-objs := pac7311.o
+gspca_se401-objs := se401.o
gspca_sn9c2028-objs := sn9c2028.o
gspca_sn9c20x-objs := sn9c20x.o
gspca_sonixb-objs := sonixb.o
@@ -76,6 +79,7 @@ gspca_stk014-objs := stk014.o
gspca_stv0680-objs := stv0680.o
gspca_sunplus-objs := sunplus.o
gspca_t613-objs := t613.o
+gspca_topro-objs := topro.o
gspca_tv8532-objs := tv8532.o
gspca_vc032x-objs := vc032x.o
gspca_vicam-objs := vicam.o
diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c
index a09c4709d613..6ae26160b81f 100644
--- a/drivers/media/video/gspca/benq.c
+++ b/drivers/media/video/gspca/benq.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "benq"
#include "gspca.h"
@@ -62,7 +64,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
0,
500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -84,20 +86,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static int sd_isoc_init(struct gspca_dev *gspca_dev)
-{
- int ret;
-
- ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface,
- gspca_dev->nbalt - 1);
- if (ret < 0) {
- err("usb_set_interface failed");
- return ret;
- }
-/* reg_w(gspca_dev, 0x0003, 0x0002); */
- return 0;
-}
-
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
@@ -113,7 +101,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
for (n = 0; n < 4; n++) {
urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
if (!urb) {
- err("usb_alloc_urb failed");
+ pr_err("usb_alloc_urb failed\n");
return -ENOMEM;
}
gspca_dev->urb[n] = urb;
@@ -123,7 +111,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
&urb->transfer_dma);
if (urb->transfer_buffer == NULL) {
- err("usb_alloc_coherent failed");
+ pr_err("usb_alloc_coherent failed\n");
return -ENOMEM;
}
urb->dev = gspca_dev->dev;
@@ -181,7 +169,7 @@ static void sd_isoc_irq(struct urb *urb)
if (gspca_dev->frozen)
return;
#endif
- err("urb status: %d", urb->status);
+ pr_err("urb status: %d\n", urb->status);
return;
}
@@ -209,7 +197,7 @@ static void sd_isoc_irq(struct urb *urb)
if (st == 0)
st = urb->iso_frame_desc[i].status;
if (st) {
- err("ISOC data error: [%d] status=%d",
+ pr_err("ISOC data error: [%d] status=%d\n",
i, st);
gspca_dev->last_packet_type = DISCARD_PACKET;
continue;
@@ -256,10 +244,10 @@ static void sd_isoc_irq(struct urb *urb)
/* resubmit the URBs */
st = usb_submit_urb(urb0, GFP_ATOMIC);
if (st < 0)
- err("usb_submit_urb(0) ret %d", st);
+ pr_err("usb_submit_urb(0) ret %d\n", st);
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
- err("usb_submit_urb() ret %d", st);
+ pr_err("usb_submit_urb() ret %d\n", st);
}
/* sub-driver description */
@@ -269,7 +257,6 @@ static const struct sd_desc sd_desc = {
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
- .isoc_init = sd_isoc_init,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index 8b398493f96b..4c56dbef6d92 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "conex"
#include "gspca.h"
@@ -129,7 +131,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_r: buffer overflow");
+ pr_err("reg_r: buffer overflow\n");
return;
}
#endif
@@ -169,7 +171,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_w: buffer overflow");
+ pr_err("reg_w: buffer overflow\n");
return;
}
PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c
index f2a9451eea19..f9b86b2484b0 100644
--- a/drivers/media/video/gspca/cpia1.c
+++ b/drivers/media/video/gspca/cpia1.c
@@ -26,6 +26,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "cpia1"
#include <linux/input.h>
@@ -550,8 +552,7 @@ retry:
gspca_dev->usb_buf, databytes, 1000);
if (ret < 0)
- err("usb_control_msg %02x, error %d", command[1],
- ret);
+ pr_err("usb_control_msg %02x, error %d\n", command[1], ret);
if (ret == -EPIPE && retries > 0) {
retries--;
@@ -1279,7 +1280,7 @@ static void monitor_exposure(struct gspca_dev *gspca_dev)
cmd[7] = 0;
ret = cpia_usb_transferCmd(gspca_dev, cmd);
if (ret) {
- err("ReadVPRegs(30,4,9,8) - failed: %d", ret);
+ pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret);
return;
}
exp_acc = gspca_dev->usb_buf[0];
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
index 4b2c483fce6f..0357d6d461d1 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/video/gspca/etoms.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "etoms"
#include "gspca.h"
@@ -236,7 +238,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_r: buffer overflow");
+ pr_err("reg_r: buffer overflow\n");
return;
}
#endif
@@ -274,7 +276,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_w: buffer overflow");
+ pr_err("reg_w: buffer overflow\n");
return;
}
PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
index 987b4b69d7ab..ea48200fd3a0 100644
--- a/drivers/media/video/gspca/finepix.c
+++ b/drivers/media/video/gspca/finepix.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "finepix"
#include "gspca.h"
@@ -182,7 +184,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* Init the device */
ret = command(gspca_dev, 0);
if (ret < 0) {
- err("init failed %d", ret);
+ pr_err("init failed %d\n", ret);
return ret;
}
@@ -194,14 +196,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
FPIX_MAX_TRANSFER, &len,
FPIX_TIMEOUT);
if (ret < 0) {
- err("usb_bulk_msg failed %d", ret);
+ pr_err("usb_bulk_msg failed %d\n", ret);
return ret;
}
/* Request a frame, but don't read it */
ret = command(gspca_dev, 1);
if (ret < 0) {
- err("frame request failed %d", ret);
+ pr_err("frame request failed %d\n", ret);
return ret;
}
diff --git a/drivers/media/video/gspca/gl860/Makefile b/drivers/media/video/gspca/gl860/Makefile
index 13c9403cc87d..f511eccdfd9c 100644
--- a/drivers/media/video/gspca/gl860/Makefile
+++ b/drivers/media/video/gspca/gl860/Makefile
@@ -6,5 +6,5 @@ gspca_gl860-objs := gl860.o \
gl860-ov9655.o \
gl860-mi2020.o
-EXTRA_CFLAGS += -Idrivers/media/video/gspca
+ccflags-y += -Idrivers/media/video/gspca
diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c
index e8e071aa212f..2ced3b73994f 100644
--- a/drivers/media/video/gspca/gl860/gl860.c
+++ b/drivers/media/video/gspca/gl860/gl860.c
@@ -18,6 +18,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "gspca.h"
#include "gl860.h"
@@ -572,9 +575,8 @@ int gl860_RTx(struct gspca_dev *gspca_dev,
}
if (r < 0)
- err("ctrl transfer failed %4d "
- "[p%02x r%d v%04x i%04x len%d]",
- r, pref, req, val, index, len);
+ pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n",
+ r, pref, req, val, index, len);
else if (len > 1 && r < len)
PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len);
diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h
index 49ad4acbf602..0330a0293b9c 100644
--- a/drivers/media/video/gspca/gl860/gl860.h
+++ b/drivers/media/video/gspca/gl860/gl860.h
@@ -18,7 +18,6 @@
*/
#ifndef GL860_DEV_H
#define GL860_DEV_H
-#include <linux/version.h>
#include "gspca.h"
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 08ce9948d99b..881e04c7ffe6 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -21,10 +21,11 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#define MODULE_NAME "gspca"
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define GSPCA_VERSION "2.14.0"
#include <linux/init.h>
-#include <linux/version.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
@@ -54,8 +55,7 @@
MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
MODULE_LICENSE("GPL");
-
-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 13, 0)
+MODULE_VERSION(GSPCA_VERSION);
#ifdef GSPCA_DEBUG
int gspca_debug = D_ERR | D_PROBE;
@@ -148,7 +148,7 @@ static void int_irq(struct urb *urb)
if (ret == 0) {
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0)
- err("Resubmit URB failed with error %i", ret);
+ pr_err("Resubmit URB failed with error %i\n", ret);
}
}
@@ -177,8 +177,8 @@ static int gspca_input_connect(struct gspca_dev *dev)
err = input_register_device(input_dev);
if (err) {
- err("Input device registration failed with error %i",
- err);
+ pr_err("Input device registration failed with error %i\n",
+ err);
input_dev->dev.parent = NULL;
input_free_device(input_dev);
} else {
@@ -323,8 +323,8 @@ static void fill_frame(struct gspca_dev *gspca_dev,
/* check the packet status and length */
st = urb->iso_frame_desc[i].status;
if (st) {
- err("ISOC data error: [%d] len=%d, status=%d",
- i, len, st);
+ pr_err("ISOC data error: [%d] len=%d, status=%d\n",
+ i, len, st);
gspca_dev->last_packet_type = DISCARD_PACKET;
continue;
}
@@ -346,7 +346,7 @@ resubmit:
/* resubmit the URB */
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
- err("usb_submit_urb() ret %d", st);
+ pr_err("usb_submit_urb() ret %d\n", st);
}
/*
@@ -400,7 +400,7 @@ resubmit:
if (gspca_dev->cam.bulk_nurbs != 0) {
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
- err("usb_submit_urb() ret %d", st);
+ pr_err("usb_submit_urb() ret %d\n", st);
}
}
@@ -443,8 +443,11 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
} else {
switch (gspca_dev->last_packet_type) {
case DISCARD_PACKET:
- if (packet_type == LAST_PACKET)
+ if (packet_type == LAST_PACKET) {
gspca_dev->last_packet_type = packet_type;
+ gspca_dev->image = NULL;
+ gspca_dev->image_len = 0;
+ }
return;
case LAST_PACKET:
return;
@@ -461,7 +464,7 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
} else {
/* !! image is NULL only when last pkt is LAST or DISCARD
if (gspca_dev->image == NULL) {
- err("gspca_frame_add() image == NULL");
+ pr_err("gspca_frame_add() image == NULL\n");
return;
}
*/
@@ -494,19 +497,6 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
}
EXPORT_SYMBOL(gspca_frame_add);
-static int gspca_is_compressed(__u32 format)
-{
- switch (format) {
- case V4L2_PIX_FMT_MJPEG:
- case V4L2_PIX_FMT_JPEG:
- case V4L2_PIX_FMT_SPCA561:
- case V4L2_PIX_FMT_PAC207:
- case V4L2_PIX_FMT_MR97310A:
- return 1;
- }
- return 0;
-}
-
static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
enum v4l2_memory memory, unsigned int count)
{
@@ -522,7 +512,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
count = GSPCA_MAX_FRAMES - 1;
gspca_dev->frbuf = vmalloc_32(frsz * count);
if (!gspca_dev->frbuf) {
- err("frame alloc failed");
+ pr_err("frame alloc failed\n");
return -ENOMEM;
}
gspca_dev->capt_file = file;
@@ -594,7 +584,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
return 0;
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
if (ret < 0)
- err("set alt 0 err %d", ret);
+ pr_err("set alt 0 err %d\n", ret);
return ret;
}
@@ -637,53 +627,104 @@ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
return NULL;
}
+/* compute the minimum bandwidth for the current transfer */
+static u32 which_bandwidth(struct gspca_dev *gspca_dev)
+{
+ u32 bandwidth;
+ int i;
+
+ i = gspca_dev->curr_mode;
+ bandwidth = gspca_dev->cam.cam_mode[i].sizeimage;
+
+ /* if the image is compressed, estimate the mean image size */
+ if (bandwidth < gspca_dev->cam.cam_mode[i].width *
+ gspca_dev->cam.cam_mode[i].height)
+ bandwidth /= 3;
+
+ /* estimate the frame rate */
+ if (gspca_dev->sd_desc->get_streamparm) {
+ struct v4l2_streamparm parm;
+
+ parm.parm.capture.timeperframe.denominator = 15;
+ gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm);
+ bandwidth *= parm.parm.capture.timeperframe.denominator;
+ } else {
+ bandwidth *= 15; /* 15 fps */
+ }
+
+ PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth);
+ return bandwidth;
+}
+
+/* endpoint table */
+#define MAX_ALT 16
+struct ep_tb_s {
+ u32 alt;
+ u32 bandwidth;
+};
+
/*
- * look for an input (isoc or bulk) endpoint
- *
- * The endpoint is defined by the subdriver.
- * Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep).
- * This routine may be called many times when the bandwidth is too small
- * (the bandwidth is checked on urb submit).
+ * build the table of the endpoints
+ * and compute the minimum bandwidth for the image transfer
*/
-static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
+static int build_ep_tb(struct gspca_dev *gspca_dev,
+ struct usb_interface *intf,
+ int xfer,
+ struct ep_tb_s *ep_tb)
{
- struct usb_interface *intf;
struct usb_host_endpoint *ep;
- int xfer, i, ret;
+ int i, j, nbalt, psize, found;
+ u32 bandwidth, last_bw;
- intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
- ep = NULL;
- xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
- : USB_ENDPOINT_XFER_ISOC;
- i = gspca_dev->alt; /* previous alt setting */
- if (gspca_dev->cam.reverse_alts) {
- while (++i < gspca_dev->nbalt) {
- ep = alt_xfer(&intf->altsetting[i], xfer);
- if (ep)
- break;
- }
- } else {
- while (--i >= 0) {
- ep = alt_xfer(&intf->altsetting[i], xfer);
- if (ep)
- break;
- }
- }
- if (ep == NULL) {
- err("no transfer endpoint found");
- return NULL;
- }
- PDEBUG(D_STREAM, "use alt %d ep 0x%02x",
- i, ep->desc.bEndpointAddress);
- gspca_dev->alt = i; /* memorize the current alt setting */
- if (gspca_dev->nbalt > 1) {
- ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
- if (ret < 0) {
- err("set alt %d err %d", i, ret);
- ep = NULL;
+ nbalt = intf->num_altsetting;
+ if (nbalt > MAX_ALT)
+ nbalt = MAX_ALT; /* fixme: should warn */
+
+ /* build the endpoint table */
+ i = 0;
+ last_bw = 0;
+ for (;;) {
+ ep_tb->bandwidth = 2000 * 2000 * 120;
+ found = 0;
+ for (j = 0; j < nbalt; j++) {
+ ep = alt_xfer(&intf->altsetting[j], xfer);
+ if (ep == NULL)
+ continue;
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ if (!gspca_dev->cam.bulk) /* isoc */
+ psize = (psize & 0x07ff) *
+ (1 + ((psize >> 11) & 3));
+ bandwidth = psize * ep->desc.bInterval * 1000;
+ if (gspca_dev->dev->speed == USB_SPEED_HIGH
+ || gspca_dev->dev->speed == USB_SPEED_SUPER)
+ bandwidth *= 8;
+ if (bandwidth <= last_bw)
+ continue;
+ if (bandwidth < ep_tb->bandwidth) {
+ ep_tb->bandwidth = bandwidth;
+ ep_tb->alt = j;
+ found = 1;
+ }
}
+ if (!found)
+ break;
+ PDEBUG(D_STREAM, "alt %d bandwidth %d",
+ ep_tb->alt, ep_tb->bandwidth);
+ last_bw = ep_tb->bandwidth;
+ i++;
+ ep_tb++;
+ }
+
+ /* get the requested bandwidth and start at the highest atlsetting */
+ bandwidth = which_bandwidth(gspca_dev);
+ ep_tb--;
+ while (i > 1) {
+ ep_tb--;
+ if (ep_tb->bandwidth < bandwidth)
+ break;
+ i--;
}
- return ep;
+ return i;
}
/*
@@ -728,7 +769,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
for (n = 0; n < nurbs; n++) {
urb = usb_alloc_urb(npkt, GFP_KERNEL);
if (!urb) {
- err("usb_alloc_urb failed");
+ pr_err("usb_alloc_urb failed\n");
return -ENOMEM;
}
gspca_dev->urb[n] = urb;
@@ -738,7 +779,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
&urb->transfer_dma);
if (urb->transfer_buffer == NULL) {
- err("usb_alloc_coherent failed");
+ pr_err("usb_alloc_coherent failed\n");
return -ENOMEM;
}
urb->dev = gspca_dev->dev;
@@ -749,7 +790,10 @@ static int create_urbs(struct gspca_dev *gspca_dev,
ep->desc.bEndpointAddress);
urb->transfer_flags = URB_ISO_ASAP
| URB_NO_TRANSFER_DMA_MAP;
- urb->interval = ep->desc.bInterval;
+ if (gspca_dev->dev->speed == USB_SPEED_LOW)
+ urb->interval = ep->desc.bInterval;
+ else
+ urb->interval = 1 << (ep->desc.bInterval - 1);
urb->complete = isoc_irq;
urb->number_of_packets = npkt;
for (i = 0; i < npkt; i++) {
@@ -771,9 +815,11 @@ static int create_urbs(struct gspca_dev *gspca_dev,
*/
static int gspca_init_transfer(struct gspca_dev *gspca_dev)
{
+ struct usb_interface *intf;
struct usb_host_endpoint *ep;
struct urb *urb;
- int n, ret;
+ struct ep_tb_s ep_tb[MAX_ALT];
+ int n, ret, xfer, alt, alt_idx;
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
@@ -791,30 +837,65 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
gspca_dev->usb_err = 0;
- /* set the higher alternate setting and
- * loop until urb submit succeeds */
- if (gspca_dev->cam.reverse_alts)
- gspca_dev->alt = 0;
- else
- gspca_dev->alt = gspca_dev->nbalt;
-
+ /* do the specific subdriver stuff before endpoint selection */
+ gspca_dev->alt = 0;
if (gspca_dev->sd_desc->isoc_init) {
ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
if (ret < 0)
goto unlock;
}
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+ xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
+ : USB_ENDPOINT_XFER_ISOC;
+
+ /* if the subdriver forced an altsetting, get the endpoint */
+ if (gspca_dev->alt != 0) {
+ gspca_dev->alt--; /* (previous version compatibility) */
+ ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer);
+ if (ep == NULL) {
+ pr_err("bad altsetting %d\n", gspca_dev->alt);
+ ret = -EIO;
+ goto out;
+ }
+ ep_tb[0].alt = gspca_dev->alt;
+ alt_idx = 1;
+ } else {
+
+ /* else, compute the minimum bandwidth
+ * and build the endpoint table */
+ alt_idx = build_ep_tb(gspca_dev, intf, xfer, ep_tb);
+ if (alt_idx <= 0) {
+ pr_err("no transfer endpoint found\n");
+ ret = -EIO;
+ goto unlock;
+ }
+ }
+ /* set the highest alternate setting and
+ * loop until urb submit succeeds */
gspca_input_destroy_urb(gspca_dev);
- ep = get_ep(gspca_dev);
- if (ep == NULL) {
- ret = -EIO;
- goto out;
- }
+
+ gspca_dev->alt = ep_tb[--alt_idx].alt;
+ alt = -1;
for (;;) {
+ if (alt != gspca_dev->alt) {
+ alt = gspca_dev->alt;
+ if (gspca_dev->nbalt > 1) {
+ ret = usb_set_interface(gspca_dev->dev,
+ gspca_dev->iface,
+ alt);
+ if (ret < 0) {
+ if (ret == -ENOSPC)
+ goto retry; /*fixme: ugly*/
+ pr_err("set alt %d err %d\n", alt, ret);
+ goto out;
+ }
+ }
+ }
if (!gspca_dev->cam.no_urb_create) {
- PDEBUG(D_STREAM, "init transfer alt %d",
- gspca_dev->alt);
- ret = create_urbs(gspca_dev, ep);
+ PDEBUG(D_STREAM, "init transfer alt %d", alt);
+ ret = create_urbs(gspca_dev,
+ alt_xfer(&intf->altsetting[alt], xfer));
if (ret < 0) {
destroy_urbs(gspca_dev);
goto out;
@@ -848,29 +929,35 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
break;
}
if (ret >= 0)
- break;
+ break; /* transfer is started */
+
+ /* something when wrong
+ * stop the webcam and free the transfer resources */
gspca_stream_off(gspca_dev);
if (ret != -ENOSPC) {
- err("usb_submit_urb alt %d err %d",
- gspca_dev->alt, ret);
+ pr_err("usb_submit_urb alt %d err %d\n",
+ gspca_dev->alt, ret);
goto out;
}
/* the bandwidth is not wide enough
* negotiate or try a lower alternate setting */
+retry:
PDEBUG(D_ERR|D_STREAM,
- "bandwidth not wide enough - trying again");
+ "alt %d - bandwidth not wide enough - trying again",
+ alt);
msleep(20); /* wait for kill complete */
if (gspca_dev->sd_desc->isoc_nego) {
ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
if (ret < 0)
goto out;
} else {
- ep = get_ep(gspca_dev);
- if (ep == NULL) {
+ if (alt_idx <= 0) {
+ pr_err("no transfer endpoint found\n");
ret = -EIO;
goto out;
}
+ alt = ep_tb[--alt_idx].alt;
}
}
out:
@@ -1041,7 +1128,9 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
return -EINVAL; /* no more format */
fmtdesc->pixelformat = fmt_tb[index];
- if (gspca_is_compressed(fmt_tb[index]))
+ if (gspca_dev->cam.cam_mode[i].sizeimage <
+ gspca_dev->cam.cam_mode[i].width *
+ gspca_dev->cam.cam_mode[i].height)
fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
fmtdesc->description[0] = fmtdesc->pixelformat & 0xff;
fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff;
@@ -1278,10 +1367,10 @@ static int vidioc_querycap(struct file *file, void *priv,
ret = -ENODEV;
goto out;
}
- strncpy((char *) cap->driver, gspca_dev->sd_desc->name,
+ strlcpy((char *) cap->driver, gspca_dev->sd_desc->name,
sizeof cap->driver);
if (gspca_dev->dev->product != NULL) {
- strncpy((char *) cap->card, gspca_dev->dev->product,
+ strlcpy((char *) cap->card, gspca_dev->dev->product,
sizeof cap->card);
} else {
snprintf((char *) cap->card, sizeof cap->card,
@@ -1291,7 +1380,6 @@ static int vidioc_querycap(struct file *file, void *priv,
}
usb_make_path(gspca_dev->dev, (char *) cap->bus_info,
sizeof(cap->bus_info));
- cap->version = DRIVER_VERSION_NUMBER;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_STREAMING
| V4L2_CAP_READWRITE;
@@ -1460,7 +1548,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
return -EINVAL;
input->type = V4L2_INPUT_TYPE_CAMERA;
input->status = gspca_dev->cam.input_flags;
- strncpy(input->name, gspca_dev->sd_desc->name,
+ strlcpy(input->name, gspca_dev->sd_desc->name,
sizeof input->name);
return 0;
}
@@ -2193,19 +2281,20 @@ int gspca_dev_probe2(struct usb_interface *intf,
struct usb_device *dev = interface_to_usbdev(intf);
int ret;
- PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct);
+ pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n",
+ sd_desc->name, id->idVendor, id->idProduct);
/* create the device */
if (dev_size < sizeof *gspca_dev)
dev_size = sizeof *gspca_dev;
gspca_dev = kzalloc(dev_size, GFP_KERNEL);
if (!gspca_dev) {
- err("couldn't kzalloc gspca struct");
+ pr_err("couldn't kzalloc gspca struct\n");
return -ENOMEM;
}
gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
if (!gspca_dev->usb_buf) {
- err("out of memory");
+ pr_err("out of memory\n");
ret = -ENOMEM;
goto out;
}
@@ -2262,7 +2351,7 @@ int gspca_dev_probe2(struct usb_interface *intf,
VFL_TYPE_GRABBER,
-1);
if (ret < 0) {
- err("video_register_device err %d", ret);
+ pr_err("video_register_device err %d\n", ret);
goto out;
}
@@ -2294,8 +2383,8 @@ int gspca_dev_probe(struct usb_interface *intf,
/* we don't handle multi-config cameras */
if (dev->descriptor.bNumConfigurations != 1) {
- err("%04x:%04x too many config",
- id->idVendor, id->idProduct);
+ pr_err("%04x:%04x too many config\n",
+ id->idVendor, id->idProduct);
return -ENODEV;
}
@@ -2478,10 +2567,7 @@ EXPORT_SYMBOL(gspca_auto_gain_n_exposure);
/* -- module insert / remove -- */
static int __init gspca_init(void)
{
- info("v%d.%d.%d registered",
- (DRIVER_VERSION_NUMBER >> 16) & 0xff,
- (DRIVER_VERSION_NUMBER >> 8) & 0xff,
- DRIVER_VERSION_NUMBER & 0xff);
+ pr_info("v" GSPCA_VERSION " registered\n");
return 0;
}
static void __exit gspca_exit(void)
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index 49e2fcbe81fb..e444f16e1497 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -14,11 +14,12 @@
#ifdef GSPCA_DEBUG
/* GSPCA our debug messages */
extern int gspca_debug;
-#define PDEBUG(level, fmt, args...) \
- do {\
- if (gspca_debug & (level)) \
- printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args); \
- } while (0)
+#define PDEBUG(level, fmt, ...) \
+do { \
+ if (gspca_debug & (level)) \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
+
#define D_ERR 0x01
#define D_PROBE 0x02
#define D_CONF 0x04
@@ -29,17 +30,8 @@ extern int gspca_debug;
#define D_USBO 0x00
#define D_V4L2 0x0100
#else
-#define PDEBUG(level, fmt, args...)
+#define PDEBUG(level, fmt, ...)
#endif
-#undef err
-#define err(fmt, args...) \
- printk(KERN_ERR MODULE_NAME ": " fmt "\n", ## args)
-#undef info
-#define info(fmt, args...) \
- printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args)
-#undef warn
-#define warn(fmt, args...) \
- printk(KERN_WARNING MODULE_NAME ": " fmt "\n", ## args)
#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */
/* image transfers */
diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
index 1bd9c4b542dd..8e3dabe30077 100644
--- a/drivers/media/video/gspca/jeilinj.c
+++ b/drivers/media/video/gspca/jeilinj.c
@@ -24,6 +24,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "jeilinj"
#include <linux/slab.h>
@@ -113,8 +115,8 @@ static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
usb_sndbulkpipe(gspca_dev->dev, 3),
gspca_dev->usb_buf, 2, NULL, 500);
if (retval < 0) {
- err("command write [%02x] error %d",
- gspca_dev->usb_buf[0], retval);
+ pr_err("command write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
gspca_dev->usb_err = retval;
}
}
@@ -131,8 +133,8 @@ static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
gspca_dev->usb_buf, 1, NULL, 500);
response = gspca_dev->usb_buf[0];
if (retval < 0) {
- err("read command [%02x] error %d",
- gspca_dev->usb_buf[0], retval);
+ pr_err("read command [%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
gspca_dev->usb_err = retval;
}
}
@@ -403,13 +405,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
dev->type = id->driver_info;
gspca_dev->cam.ctrls = dev->ctrls;
dev->quality = QUALITY_DEF;
- dev->ctrls[LIGHTFREQ].def = V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
- dev->ctrls[RED].def = RED_BALANCE_DEF;
- dev->ctrls[GREEN].def = GREEN_BALANCE_DEF;
- dev->ctrls[BLUE].def = BLUE_BALANCE_DEF;
- PDEBUG(D_PROBE,
- "JEILINJ camera detected"
- " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+
cam->cam_mode = jlj_mode;
cam->nmodes = ARRAY_SIZE(jlj_mode);
cam->bulk = 1;
@@ -422,7 +418,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
{
int i;
u8 *buf;
- u8 stop_commands[][2] = {
+ static u8 stop_commands[][2] = {
{0x71, 0x00},
{0x70, 0x09},
{0x71, 0x80},
diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/video/gspca/kinect.c
index 26fc206f095e..4fe51fda80f3 100644
--- a/drivers/media/video/gspca/kinect.c
+++ b/drivers/media/video/gspca/kinect.c
@@ -24,6 +24,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "kinect"
#include "gspca.h"
@@ -34,11 +36,6 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver");
MODULE_LICENSE("GPL");
-#ifdef GSPCA_DEBUG
-int gspca_debug = D_ERR | D_PROBE | D_CONF | D_STREAM | D_FRAM | D_PACK |
- D_USBI | D_USBO | D_V4L2;
-#endif
-
struct pkt_hdr {
uint8_t magic[2];
uint8_t pad;
@@ -141,7 +138,7 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
struct cam_hdr *rhdr = (void *)ibuf;
if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) {
- err("send_cmd: Invalid command length (0x%x)", cmd_len);
+ pr_err("send_cmd: Invalid command length (0x%x)\n", cmd_len);
return -1;
}
@@ -157,7 +154,7 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
PDEBUG(D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d", cmd,
sd->cam_tag, cmd_len, res);
if (res < 0) {
- err("send_cmd: Output control transfer failed (%d)", res);
+ pr_err("send_cmd: Output control transfer failed (%d)\n", res);
return res;
}
@@ -166,33 +163,35 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
} while (actual_len == 0);
PDEBUG(D_USBO, "Control reply: %d", res);
if (actual_len < sizeof(*rhdr)) {
- err("send_cmd: Input control transfer failed (%d)", res);
+ pr_err("send_cmd: Input control transfer failed (%d)\n", res);
return res;
}
actual_len -= sizeof(*rhdr);
if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) {
- err("send_cmd: Bad magic %02x %02x", rhdr->magic[0],
- rhdr->magic[1]);
+ pr_err("send_cmd: Bad magic %02x %02x\n",
+ rhdr->magic[0], rhdr->magic[1]);
return -1;
}
if (rhdr->cmd != chdr->cmd) {
- err("send_cmd: Bad cmd %02x != %02x", rhdr->cmd, chdr->cmd);
+ pr_err("send_cmd: Bad cmd %02x != %02x\n",
+ rhdr->cmd, chdr->cmd);
return -1;
}
if (rhdr->tag != chdr->tag) {
- err("send_cmd: Bad tag %04x != %04x", rhdr->tag, chdr->tag);
+ pr_err("send_cmd: Bad tag %04x != %04x\n",
+ rhdr->tag, chdr->tag);
return -1;
}
if (cpu_to_le16(rhdr->len) != (actual_len/2)) {
- err("send_cmd: Bad len %04x != %04x",
- cpu_to_le16(rhdr->len), (int)(actual_len/2));
+ pr_err("send_cmd: Bad len %04x != %04x\n",
+ cpu_to_le16(rhdr->len), (int)(actual_len/2));
return -1;
}
if (actual_len > reply_len) {
- warn("send_cmd: Data buffer is %d bytes long, but got %d bytes",
- reply_len, actual_len);
+ pr_warn("send_cmd: Data buffer is %d bytes long, but got %d bytes\n",
+ reply_len, actual_len);
memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len);
} else {
memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len);
@@ -218,8 +217,8 @@ static int write_register(struct gspca_dev *gspca_dev, uint16_t reg,
if (res < 0)
return res;
if (res != 2) {
- warn("send_cmd returned %d [%04x %04x], 0000 expected",
- res, reply[0], reply[1]);
+ pr_warn("send_cmd returned %d [%04x %04x], 0000 expected\n",
+ res, reply[0], reply[1]);
}
return 0;
}
@@ -353,8 +352,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len)
return;
if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') {
- warn("[Stream %02x] Invalid magic %02x%02x", sd->stream_flag,
- hdr->magic[0], hdr->magic[1]);
+ pr_warn("[Stream %02x] Invalid magic %02x%02x\n",
+ sd->stream_flag, hdr->magic[0], hdr->magic[1]);
return;
}
@@ -368,7 +367,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len)
gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen);
else
- warn("Packet type not recognized...");
+ pr_warn("Packet type not recognized...\n");
}
/* sub-driver description */
diff --git a/drivers/media/video/gspca/konica.c b/drivers/media/video/gspca/konica.c
index 5964691c0e95..f3f7fe0ec4b7 100644
--- a/drivers/media/video/gspca/konica.c
+++ b/drivers/media/video/gspca/konica.c
@@ -28,6 +28,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "konica"
#include <linux/input.h>
@@ -200,7 +202,7 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
0,
1000);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -221,7 +223,7 @@ static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index)
2,
1000);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -284,7 +286,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
if (!alt) {
- err("Couldn't get altsetting");
+ pr_err("Couldn't get altsetting\n");
return -EIO;
}
@@ -315,7 +317,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
le16_to_cpu(alt->endpoint[i].desc.wMaxPacketSize);
urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
if (!urb) {
- err("usb_alloc_urb failed");
+ pr_err("usb_alloc_urb failed\n");
return -ENOMEM;
}
gspca_dev->urb[n] = urb;
@@ -324,7 +326,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
GFP_KERNEL,
&urb->transfer_dma);
if (urb->transfer_buffer == NULL) {
- err("usb_buffer_alloc failed");
+ pr_err("usb_buffer_alloc failed\n");
return -ENOMEM;
}
@@ -386,7 +388,7 @@ static void sd_isoc_irq(struct urb *urb)
PDEBUG(D_ERR, "urb status: %d", urb->status);
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
- err("resubmit urb error %d", st);
+ pr_err("resubmit urb error %d\n", st);
return;
}
@@ -477,7 +479,7 @@ resubmit:
}
st = usb_submit_urb(status_urb, GFP_ATOMIC);
if (st < 0)
- err("usb_submit_urb(status_urb) ret %d", st);
+ pr_err("usb_submit_urb(status_urb) ret %d\n", st);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/gspca/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile
index bf7a19a1e6d1..7f52961f439c 100644
--- a/drivers/media/video/gspca/m5602/Makefile
+++ b/drivers/media/video/gspca/m5602/Makefile
@@ -8,4 +8,4 @@ gspca_m5602-objs := m5602_core.o \
m5602_s5k83a.o \
m5602_s5k4aa.o
-EXTRA_CFLAGS += -Idrivers/media/video/gspca
+ccflags-y += -Idrivers/media/video/gspca
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
index a7722b1aef9b..67533e5582a6 100644
--- a/drivers/media/video/gspca/m5602/m5602_core.c
+++ b/drivers/media/video/gspca/m5602/m5602_core.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_ov9650.h"
#include "m5602_ov7660.h"
#include "m5602_mt9m111.h"
@@ -192,10 +194,9 @@ static void m5602_dump_bridge(struct sd *sd)
for (i = 0; i < 0x80; i++) {
unsigned char val = 0;
m5602_read_bridge(sd, i, &val);
- info("ALi m5602 address 0x%x contains 0x%x", i, val);
+ pr_info("ALi m5602 address 0x%x contains 0x%x\n", i, val);
}
- info("Warning: The ALi m5602 webcam probably won't work "
- "until it's power cycled");
+ pr_info("Warning: The ALi m5602 webcam probably won't work until it's power cycled\n");
}
static int m5602_probe_sensor(struct sd *sd)
@@ -231,7 +232,7 @@ static int m5602_probe_sensor(struct sd *sd)
return 0;
/* More sensor probe function goes here */
- info("Failed to find a sensor");
+ pr_info("Failed to find a sensor\n");
sd->sensor = NULL;
return -ENODEV;
}
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
index 0d605a52b924..6268aa24ec5d 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_mt9m111.h"
static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
@@ -163,7 +165,7 @@ int mt9m111_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == MT9M111_SENSOR) {
- info("Forcing a %s sensor", mt9m111.name);
+ pr_info("Forcing a %s sensor\n", mt9m111.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
@@ -191,7 +193,7 @@ int mt9m111_probe(struct sd *sd)
return -ENODEV;
if ((data[0] == 0x14) && (data[1] == 0x3a)) {
- info("Detected a mt9m111 sensor");
+ pr_info("Detected a mt9m111 sensor\n");
goto sensor_found;
}
@@ -612,34 +614,34 @@ static void mt9m111_dump_registers(struct sd *sd)
{
u8 address, value[2] = {0x00, 0x00};
- info("Dumping the mt9m111 register state");
+ pr_info("Dumping the mt9m111 register state\n");
- info("Dumping the mt9m111 sensor core registers");
+ pr_info("Dumping the mt9m111 sensor core registers\n");
value[1] = MT9M111_SENSOR_CORE;
m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
m5602_read_sensor(sd, address, value, 2);
- info("register 0x%x contains 0x%x%x",
- address, value[0], value[1]);
+ pr_info("register 0x%x contains 0x%x%x\n",
+ address, value[0], value[1]);
}
- info("Dumping the mt9m111 color pipeline registers");
+ pr_info("Dumping the mt9m111 color pipeline registers\n");
value[1] = MT9M111_COLORPIPE;
m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
m5602_read_sensor(sd, address, value, 2);
- info("register 0x%x contains 0x%x%x",
- address, value[0], value[1]);
+ pr_info("register 0x%x contains 0x%x%x\n",
+ address, value[0], value[1]);
}
- info("Dumping the mt9m111 camera control registers");
+ pr_info("Dumping the mt9m111 camera control registers\n");
value[1] = MT9M111_CAMERA_CONTROL;
m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
m5602_read_sensor(sd, address, value, 2);
- info("register 0x%x contains 0x%x%x",
- address, value[0], value[1]);
+ pr_info("register 0x%x contains 0x%x%x\n",
+ address, value[0], value[1]);
}
- info("mt9m111 register state dump complete");
+ pr_info("mt9m111 register state dump complete\n");
}
diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c
index b12f60464b3b..9a14835c128f 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov7660.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov7660.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_ov7660.h"
static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
@@ -149,7 +151,7 @@ int ov7660_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == OV7660_SENSOR) {
- info("Forcing an %s sensor", ov7660.name);
+ pr_info("Forcing an %s sensor\n", ov7660.name);
goto sensor_found;
}
/* If we want to force another sensor,
@@ -180,10 +182,10 @@ int ov7660_probe(struct sd *sd)
if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1))
return -ENODEV;
- info("Sensor reported 0x%x%x", prod_id, ver_id);
+ pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id);
if ((prod_id == 0x76) && (ver_id == 0x60)) {
- info("Detected a ov7660 sensor");
+ pr_info("Detected a ov7660 sensor\n");
goto sensor_found;
}
return -ENODEV;
@@ -457,17 +459,16 @@ static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
static void ov7660_dump_registers(struct sd *sd)
{
int address;
- info("Dumping the ov7660 register state");
+ pr_info("Dumping the ov7660 register state\n");
for (address = 0; address < 0xa9; address++) {
u8 value;
m5602_read_sensor(sd, address, &value, 1);
- info("register 0x%x contains 0x%x",
- address, value);
+ pr_info("register 0x%x contains 0x%x\n", address, value);
}
- info("ov7660 register state dump complete");
+ pr_info("ov7660 register state dump complete\n");
- info("Probing for which registers that are read/write");
+ pr_info("Probing for which registers that are read/write\n");
for (address = 0; address < 0xff; address++) {
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
@@ -477,9 +478,9 @@ static void ov7660_dump_registers(struct sd *sd)
m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
- info("register 0x%x is writeable", address);
+ pr_info("register 0x%x is writeable\n", address);
else
- info("register 0x%x is read only", address);
+ pr_info("register 0x%x is read only\n", address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c
index 703d48670a24..2114a8b90ec9 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_ov9650.h"
static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
@@ -299,7 +301,7 @@ int ov9650_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == OV9650_SENSOR) {
- info("Forcing an %s sensor", ov9650.name);
+ pr_info("Forcing an %s sensor\n", ov9650.name);
goto sensor_found;
}
/* If we want to force another sensor,
@@ -330,7 +332,7 @@ int ov9650_probe(struct sd *sd)
return -ENODEV;
if ((prod_id == 0x96) && (ver_id == 0x52)) {
- info("Detected an ov9650 sensor");
+ pr_info("Detected an ov9650 sensor\n");
goto sensor_found;
}
return -ENODEV;
@@ -850,17 +852,16 @@ static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
static void ov9650_dump_registers(struct sd *sd)
{
int address;
- info("Dumping the ov9650 register state");
+ pr_info("Dumping the ov9650 register state\n");
for (address = 0; address < 0xa9; address++) {
u8 value;
m5602_read_sensor(sd, address, &value, 1);
- info("register 0x%x contains 0x%x",
- address, value);
+ pr_info("register 0x%x contains 0x%x\n", address, value);
}
- info("ov9650 register state dump complete");
+ pr_info("ov9650 register state dump complete\n");
- info("Probing for which registers that are read/write");
+ pr_info("Probing for which registers that are read/write\n");
for (address = 0; address < 0xff; address++) {
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
@@ -870,9 +871,9 @@ static void ov9650_dump_registers(struct sd *sd)
m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
- info("register 0x%x is writeable", address);
+ pr_info("register 0x%x is writeable\n", address);
else
- info("register 0x%x is read only", address);
+ pr_info("register 0x%x is read only\n", address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c
index 1febd34c2f05..b8771698cbcb 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.c
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_po1030.h"
static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
@@ -197,7 +199,7 @@ int po1030_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == PO1030_SENSOR) {
- info("Forcing a %s sensor", po1030.name);
+ pr_info("Forcing a %s sensor\n", po1030.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
@@ -221,7 +223,7 @@ int po1030_probe(struct sd *sd)
return -ENODEV;
if (dev_id_h == 0x30) {
- info("Detected a po1030 sensor");
+ pr_info("Detected a po1030 sensor\n");
goto sensor_found;
}
return -ENODEV;
@@ -267,7 +269,7 @@ int po1030_init(struct sd *sd)
break;
default:
- info("Invalid stream command, exiting init");
+ pr_info("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -733,16 +735,15 @@ static void po1030_dump_registers(struct sd *sd)
int address;
u8 value = 0;
- info("Dumping the po1030 sensor core registers");
+ pr_info("Dumping the po1030 sensor core registers\n");
for (address = 0; address < 0x7f; address++) {
m5602_read_sensor(sd, address, &value, 1);
- info("register 0x%x contains 0x%x",
- address, value);
+ pr_info("register 0x%x contains 0x%x\n", address, value);
}
- info("po1030 register state dump complete");
+ pr_info("po1030 register state dump complete\n");
- info("Probing for which registers that are read/write");
+ pr_info("Probing for which registers that are read/write\n");
for (address = 0; address < 0xff; address++) {
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
@@ -752,9 +753,9 @@ static void po1030_dump_registers(struct sd *sd)
m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
- info("register 0x%x is writeable", address);
+ pr_info("register 0x%x is writeable\n", address);
else
- info("register 0x%x is read only", address);
+ pr_info("register 0x%x is read only\n", address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
index d27280be9852..cc8ec3f7e8dc 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_s5k4aa.h"
static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
@@ -240,7 +242,7 @@ int s5k4aa_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == S5K4AA_SENSOR) {
- info("Forcing a %s sensor", s5k4aa.name);
+ pr_info("Forcing a %s sensor\n", s5k4aa.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
@@ -276,7 +278,7 @@ int s5k4aa_probe(struct sd *sd)
data, 2);
break;
default:
- info("Invalid stream command, exiting init");
+ pr_info("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -292,7 +294,7 @@ int s5k4aa_probe(struct sd *sd)
if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
return -ENODEV;
else
- info("Detected a s5k4aa sensor");
+ pr_info("Detected a s5k4aa sensor\n");
sensor_found:
sensor_settings = kmalloc(
@@ -347,7 +349,7 @@ int s5k4aa_start(struct sd *sd)
break;
default:
- err("Invalid stream command, exiting init");
+ pr_err("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -383,7 +385,7 @@ int s5k4aa_start(struct sd *sd)
break;
default:
- err("Invalid stream command, exiting init");
+ pr_err("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -447,7 +449,7 @@ int s5k4aa_init(struct sd *sd)
init_s5k4aa[i][1], data, 2);
break;
default:
- info("Invalid stream command, exiting init");
+ pr_info("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -686,20 +688,21 @@ static void s5k4aa_dump_registers(struct sd *sd)
m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
- info("Dumping the s5k4aa register state for page 0x%x", page);
+ pr_info("Dumping the s5k4aa register state for page 0x%x\n",
+ page);
for (address = 0; address <= 0xff; address++) {
u8 value = 0;
m5602_read_sensor(sd, address, &value, 1);
- info("register 0x%x contains 0x%x",
- address, value);
+ pr_info("register 0x%x contains 0x%x\n",
+ address, value);
}
}
- info("s5k4aa register state dump complete");
+ pr_info("s5k4aa register state dump complete\n");
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
- info("Probing for which registers that are "
- "read/write for page 0x%x", page);
+ pr_info("Probing for which registers that are read/write for page 0x%x\n",
+ page);
for (address = 0; address <= 0xff; address++) {
u8 old_value, ctrl_value, test_value = 0xff;
@@ -708,14 +711,16 @@ static void s5k4aa_dump_registers(struct sd *sd)
m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value)
- info("register 0x%x is writeable", address);
+ pr_info("register 0x%x is writeable\n",
+ address);
else
- info("register 0x%x is read only", address);
+ pr_info("register 0x%x is read only\n",
+ address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
}
}
- info("Read/write register probing complete");
+ pr_info("Read/write register probing complete\n");
m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
index fbd91545497a..1de743a02b02 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kthread.h>
#include "m5602_s5k83a.h"
@@ -135,7 +137,7 @@ int s5k83a_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == S5K83A_SENSOR) {
- info("Forcing a %s sensor", s5k83a.name);
+ pr_info("Forcing a %s sensor\n", s5k83a.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
@@ -168,7 +170,7 @@ int s5k83a_probe(struct sd *sd)
if ((prod_id == 0xff) || (ver_id == 0xff))
return -ENODEV;
else
- info("Detected a s5k83a sensor");
+ pr_info("Detected a s5k83a sensor\n");
sensor_found:
sens_priv = kmalloc(
@@ -227,7 +229,7 @@ int s5k83a_init(struct sd *sd)
init_s5k83a[i][1], data, 2);
break;
default:
- info("Invalid stream command, exiting init");
+ pr_info("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -273,7 +275,7 @@ static int rotation_thread_function(void *data)
s5k83a_get_rotation(sd, &reg);
if (previous_rotation != reg) {
previous_rotation = reg;
- info("Camera was flipped");
+ pr_info("Camera was flipped\n");
s5k83a_get_vflip((struct gspca_dev *) sd, &vflip);
s5k83a_get_hflip((struct gspca_dev *) sd, &hflip);
@@ -566,20 +568,20 @@ static void s5k83a_dump_registers(struct sd *sd)
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
- info("Dumping the s5k83a register state for page 0x%x", page);
+ pr_info("Dumping the s5k83a register state for page 0x%x\n",
+ page);
for (address = 0; address <= 0xff; address++) {
u8 val = 0;
m5602_read_sensor(sd, address, &val, 1);
- info("register 0x%x contains 0x%x",
- address, val);
+ pr_info("register 0x%x contains 0x%x\n", address, val);
}
}
- info("s5k83a register state dump complete");
+ pr_info("s5k83a register state dump complete\n");
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
- info("Probing for which registers that are read/write "
- "for page 0x%x", page);
+ pr_info("Probing for which registers that are read/write for page 0x%x\n",
+ page);
for (address = 0; address <= 0xff; address++) {
u8 old_val, ctrl_val, test_val = 0xff;
@@ -588,14 +590,16 @@ static void s5k83a_dump_registers(struct sd *sd)
m5602_read_sensor(sd, address, &ctrl_val, 1);
if (ctrl_val == test_val)
- info("register 0x%x is writeable", address);
+ pr_info("register 0x%x is writeable\n",
+ address);
else
- info("register 0x%x is read only", address);
+ pr_info("register 0x%x is read only\n",
+ address);
/* Restore original val */
m5602_write_sensor(sd, address, &old_val, 1);
}
}
- info("Read/write register probing complete");
+ pr_info("Read/write register probing complete\n");
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
}
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
index 0196209a948a..ef45fa575752 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/video/gspca/mars.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "mars"
#include "gspca.h"
@@ -178,8 +180,8 @@ static void reg_w(struct gspca_dev *gspca_dev,
&alen,
500); /* timeout in milliseconds */
if (ret < 0) {
- err("reg write [%02x] error %d",
- gspca_dev->usb_buf[0], ret);
+ pr_err("reg write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], ret);
gspca_dev->usb_err = ret;
}
}
diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c
index 97e507967434..473e813b680e 100644
--- a/drivers/media/video/gspca/mr97310a.c
+++ b/drivers/media/video/gspca/mr97310a.c
@@ -40,6 +40,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "mr97310a"
#include "gspca.h"
@@ -267,7 +269,7 @@ static int mr_write(struct gspca_dev *gspca_dev, int len)
usb_sndbulkpipe(gspca_dev->dev, 4),
gspca_dev->usb_buf, len, NULL, 500);
if (rc < 0)
- err("reg write [%02x] error %d",
+ pr_err("reg write [%02x] error %d\n",
gspca_dev->usb_buf[0], rc);
return rc;
}
@@ -281,7 +283,7 @@ static int mr_read(struct gspca_dev *gspca_dev, int len)
usb_rcvbulkpipe(gspca_dev->dev, 3),
gspca_dev->usb_buf, len, NULL, 500);
if (rc < 0)
- err("reg read [%02x] error %d",
+ pr_err("reg read [%02x] error %d\n",
gspca_dev->usb_buf[0], rc);
return rc;
}
@@ -540,7 +542,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor_type = 1;
break;
default:
- err("Unknown CIF Sensor id : %02x",
+ pr_err("Unknown CIF Sensor id : %02x\n",
gspca_dev->usb_buf[1]);
return -ENODEV;
}
@@ -575,10 +577,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor_type = 2;
} else if ((gspca_dev->usb_buf[0] != 0x03) &&
(gspca_dev->usb_buf[0] != 0x04)) {
- err("Unknown VGA Sensor id Byte 0: %02x",
- gspca_dev->usb_buf[0]);
- err("Defaults assumed, may not work");
- err("Please report this");
+ pr_err("Unknown VGA Sensor id Byte 0: %02x\n",
+ gspca_dev->usb_buf[0]);
+ pr_err("Defaults assumed, may not work\n");
+ pr_err("Please report this\n");
}
/* Sakar Digital color needs to be adjusted. */
if ((gspca_dev->usb_buf[0] == 0x03) &&
@@ -595,10 +597,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* Nothing to do here. */
break;
default:
- err("Unknown VGA Sensor id Byte 1: %02x",
- gspca_dev->usb_buf[1]);
- err("Defaults assumed, may not work");
- err("Please report this");
+ pr_err("Unknown VGA Sensor id Byte 1: %02x\n",
+ gspca_dev->usb_buf[1]);
+ pr_err("Defaults assumed, may not work\n");
+ pr_err("Please report this\n");
}
}
PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d",
diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c
index 8e754fd4dc5e..7681814e594f 100644
--- a/drivers/media/video/gspca/nw80x.c
+++ b/drivers/media/video/gspca/nw80x.c
@@ -20,6 +20,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "nw80x"
#include "gspca.h"
@@ -1571,7 +1573,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
len,
500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1592,7 +1594,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
0x00, index,
gspca_dev->usb_buf, len, 500);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
return;
}
@@ -1802,7 +1804,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
}
if (webcam_chip[sd->webcam] != sd->bridge) {
- err("Bad webcam type %d for NW80%d", sd->webcam, sd->bridge);
+ pr_err("Bad webcam type %d for NW80%d\n",
+ sd->webcam, sd->bridge);
gspca_dev->usb_err = -ENODEV;
return gspca_dev->usb_err;
}
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index 057e287b9152..6a01b35a9478 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -36,6 +36,9 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "ov519"
#include <linux/input.h>
@@ -134,6 +137,7 @@ enum sensors {
SEN_OV7670,
SEN_OV76BE,
SEN_OV8610,
+ SEN_OV9600,
};
/* Note this is a bit of a hack, but the w9968cf driver needs the code for all
@@ -340,6 +344,10 @@ static const unsigned ctrl_dis[] = {
(1 << EXPOSURE) |
(1 << AUTOGAIN) |
(1 << FREQ),
+[SEN_OV9600] = ((1 << NCTRL) - 1) /* no control */
+ ^ ((1 << EXPOSURE) /* but exposure */
+ | (1 << AUTOGAIN)), /* and autogain */
+
};
static const struct v4l2_pix_format ov519_vga_mode[] = {
@@ -525,6 +533,17 @@ static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
};
+static const struct v4l2_pix_format ovfx2_ov9600_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
/* Registers common to OV511 / OV518 */
#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */
@@ -1807,6 +1826,22 @@ static const struct ov_i2c_regvals norm_7660[] = {
| OV7670_COM8_AEC},
{0xa1, 0xc8}
};
+static const struct ov_i2c_regvals norm_9600[] = {
+ {0x12, 0x80},
+ {0x0c, 0x28},
+ {0x11, 0x80},
+ {0x13, 0xb5},
+ {0x14, 0x3e},
+ {0x1b, 0x04},
+ {0x24, 0xb0},
+ {0x25, 0x90},
+ {0x26, 0x94},
+ {0x35, 0x90},
+ {0x37, 0x07},
+ {0x38, 0x08},
+ {0x01, 0x8e},
+ {0x02, 0x85}
+};
/* 7670. Defaults taken from OmniVision provided data,
* as provided by Jonathan Corbet of OLPC */
@@ -2139,7 +2174,7 @@ static void reg_w(struct sd *sd, u16 index, u16 value)
sd->gspca_dev.usb_buf, 1, 500);
leave:
if (ret < 0) {
- err("reg_w %02x failed %d", index, ret);
+ pr_err("reg_w %02x failed %d\n", index, ret);
sd->gspca_dev.usb_err = ret;
return;
}
@@ -2178,7 +2213,7 @@ static int reg_r(struct sd *sd, u16 index)
PDEBUG(D_USBI, "GET %02x 0000 %04x %02x",
req, index, ret);
} else {
- err("reg_r %02x failed %d", index, ret);
+ pr_err("reg_r %02x failed %d\n", index, ret);
sd->gspca_dev.usb_err = ret;
}
@@ -2203,7 +2238,7 @@ static int reg_r8(struct sd *sd,
if (ret >= 0) {
ret = sd->gspca_dev.usb_buf[0];
} else {
- err("reg_r8 %02x failed %d", index, ret);
+ pr_err("reg_r8 %02x failed %d\n", index, ret);
sd->gspca_dev.usb_err = ret;
}
@@ -2256,7 +2291,7 @@ static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n)
0, index,
sd->gspca_dev.usb_buf, n, 500);
if (ret < 0) {
- err("reg_w32 %02x failed %d", index, ret);
+ pr_err("reg_w32 %02x failed %d\n", index, ret);
sd->gspca_dev.usb_err = ret;
}
}
@@ -2400,9 +2435,12 @@ static int ov518_i2c_r(struct sd *sd, u8 reg)
/* Initiate 2-byte write cycle */
reg_w(sd, R518_I2C_CTL, 0x03);
+ reg_r8(sd, R518_I2C_CTL);
/* Initiate 2-byte read cycle */
reg_w(sd, R518_I2C_CTL, 0x05);
+ reg_r8(sd, R518_I2C_CTL);
+
value = reg_r(sd, R51x_I2C_DATA);
PDEBUG(D_USBI, "ov518_i2c_r %02x %02x", reg, value);
return value;
@@ -2422,7 +2460,7 @@ static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value)
(u16) value, (u16) reg, NULL, 0, 500);
if (ret < 0) {
- err("ovfx2_i2c_w %02x failed %d", reg, ret);
+ pr_err("ovfx2_i2c_w %02x failed %d\n", reg, ret);
sd->gspca_dev.usb_err = ret;
}
@@ -2446,7 +2484,7 @@ static int ovfx2_i2c_r(struct sd *sd, u8 reg)
ret = sd->gspca_dev.usb_buf[0];
PDEBUG(D_USBI, "ovfx2_i2c_r %02x %02x", reg, ret);
} else {
- err("ovfx2_i2c_r %02x failed %d", reg, ret);
+ pr_err("ovfx2_i2c_r %02x failed %d\n", reg, ret);
sd->gspca_dev.usb_err = ret;
}
@@ -2686,13 +2724,13 @@ static void write_i2c_regvals(struct sd *sd,
*
***************************************************************************/
-/* This initializes the OV2x10 / OV3610 / OV3620 */
+/* This initializes the OV2x10 / OV3610 / OV3620 / OV9600 */
static void ov_hires_configure(struct sd *sd)
{
int high, low;
if (sd->bridge != BRIDGE_OVFX2) {
- err("error hires sensors only supported with ovfx2");
+ pr_err("error hires sensors only supported with ovfx2\n");
return;
}
@@ -2702,19 +2740,32 @@ static void ov_hires_configure(struct sd *sd)
high = i2c_r(sd, 0x0a);
low = i2c_r(sd, 0x0b);
/* info("%x, %x", high, low); */
- if (high == 0x96 && low == 0x40) {
- PDEBUG(D_PROBE, "Sensor is an OV2610");
- sd->sensor = SEN_OV2610;
- } else if (high == 0x96 && low == 0x41) {
- PDEBUG(D_PROBE, "Sensor is an OV2610AE");
- sd->sensor = SEN_OV2610AE;
- } else if (high == 0x36 && (low & 0x0f) == 0x00) {
- PDEBUG(D_PROBE, "Sensor is an OV3610");
- sd->sensor = SEN_OV3610;
- } else {
- err("Error unknown sensor type: %02x%02x",
- high, low);
+ switch (high) {
+ case 0x96:
+ switch (low) {
+ case 0x40:
+ PDEBUG(D_PROBE, "Sensor is a OV2610");
+ sd->sensor = SEN_OV2610;
+ return;
+ case 0x41:
+ PDEBUG(D_PROBE, "Sensor is a OV2610AE");
+ sd->sensor = SEN_OV2610AE;
+ return;
+ case 0xb1:
+ PDEBUG(D_PROBE, "Sensor is a OV9600");
+ sd->sensor = SEN_OV9600;
+ return;
+ }
+ break;
+ case 0x36:
+ if ((low & 0x0f) == 0x00) {
+ PDEBUG(D_PROBE, "Sensor is a OV3610");
+ sd->sensor = SEN_OV3610;
+ return;
+ }
+ break;
}
+ pr_err("Error unknown sensor type: %02x%02x\n", high, low);
}
/* This initializes the OV8110, OV8610 sensor. The OV8110 uses
@@ -2735,7 +2786,7 @@ static void ov8xx0_configure(struct sd *sd)
if ((rc & 3) == 1)
sd->sensor = SEN_OV8610;
else
- err("Unknown image sensor version: %d", rc & 3);
+ pr_err("Unknown image sensor version: %d\n", rc & 3);
}
/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
@@ -2792,8 +2843,8 @@ static void ov7xx0_configure(struct sd *sd)
if (high == 0x76) {
switch (low) {
case 0x30:
- err("Sensor is an OV7630/OV7635");
- err("7630 is not supported by this driver");
+ pr_err("Sensor is an OV7630/OV7635\n");
+ pr_err("7630 is not supported by this driver\n");
return;
case 0x40:
PDEBUG(D_PROBE, "Sensor is an OV7645");
@@ -2810,7 +2861,6 @@ static void ov7xx0_configure(struct sd *sd)
case 0x60:
PDEBUG(D_PROBE, "Sensor is a OV7660");
sd->sensor = SEN_OV7660;
- sd->invert_led = 0;
break;
default:
PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low);
@@ -2821,7 +2871,7 @@ static void ov7xx0_configure(struct sd *sd)
sd->sensor = SEN_OV7620;
}
} else {
- err("Unknown image sensor version: %d", rc & 3);
+ pr_err("Unknown image sensor version: %d\n", rc & 3);
}
}
@@ -2844,8 +2894,7 @@ static void ov6xx0_configure(struct sd *sd)
switch (rc) {
case 0x00:
sd->sensor = SEN_OV6630;
- warn("WARNING: Sensor is an OV66308. Your camera may have");
- warn("been misdetected in previous driver versions.");
+ pr_warn("WARNING: Sensor is an OV66308. Your camera may have been misdetected in previous driver versions.\n");
break;
case 0x01:
sd->sensor = SEN_OV6620;
@@ -2861,11 +2910,10 @@ static void ov6xx0_configure(struct sd *sd)
break;
case 0x90:
sd->sensor = SEN_OV6630;
- warn("WARNING: Sensor is an OV66307. Your camera may have");
- warn("been misdetected in previous driver versions.");
+ pr_warn("WARNING: Sensor is an OV66307. Your camera may have been misdetected in previous driver versions.\n");
break;
default:
- err("FATAL: Unknown sensor version: 0x%02x", rc);
+ pr_err("FATAL: Unknown sensor version: 0x%02x\n", rc);
return;
}
@@ -3289,7 +3337,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
case BRIDGE_OV519:
cam->cam_mode = ov519_vga_mode;
cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
- sd->invert_led = !sd->invert_led;
break;
case BRIDGE_OVFX2:
cam->cam_mode = ov519_vga_mode;
@@ -3359,7 +3406,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
} else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) {
ov_hires_configure(sd);
} else {
- err("Can't determine sensor slave IDs");
+ pr_err("Can't determine sensor slave IDs\n");
goto error;
}
@@ -3400,6 +3447,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
cam->cam_mode = ovfx2_ov3610_mode;
cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode);
break;
+ case SEN_OV9600:
+ cam->cam_mode = ovfx2_ov9600_mode;
+ cam->nmodes = ARRAY_SIZE(ovfx2_ov9600_mode);
+ break;
default:
if (sd->sif) {
cam->cam_mode = ov519_sif_mode;
@@ -3497,6 +3548,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
case SEN_OV8610:
write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610));
break;
+ case SEN_OV9600:
+ write_i2c_regvals(sd, norm_9600, ARRAY_SIZE(norm_9600));
+
+ /* enable autoexpo */
+/* i2c_w_mask(sd, 0x13, 0x05, 0x05); */
+ break;
}
return gspca_dev->usb_err;
error:
@@ -3534,7 +3591,7 @@ static void ov511_mode_init_regs(struct sd *sd)
intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
if (!alt) {
- err("Couldn't get altsetting");
+ pr_err("Couldn't get altsetting\n");
sd->gspca_dev.usb_err = -EIO;
return;
}
@@ -3657,7 +3714,7 @@ static void ov518_mode_init_regs(struct sd *sd)
intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
if (!alt) {
- err("Couldn't get altsetting");
+ pr_err("Couldn't get altsetting\n");
sd->gspca_dev.usb_err = -EIO;
return;
}
@@ -4085,6 +4142,33 @@ static void mode_init_ov_sensor_regs(struct sd *sd)
i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
break;
+ case SEN_OV9600: {
+ const struct ov_i2c_regvals *vals;
+ static const struct ov_i2c_regvals sxga_15[] = {
+ {0x11, 0x80}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
+ };
+ static const struct ov_i2c_regvals sxga_7_5[] = {
+ {0x11, 0x81}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
+ };
+ static const struct ov_i2c_regvals vga_30[] = {
+ {0x11, 0x81}, {0x14, 0x7e}, {0x24, 0x70}, {0x25, 0x60}
+ };
+ static const struct ov_i2c_regvals vga_15[] = {
+ {0x11, 0x83}, {0x14, 0x3e}, {0x24, 0x80}, {0x25, 0x70}
+ };
+
+ /* frame rates:
+ * 15fps / 7.5 fps for 1280x1024
+ * 30fps / 15fps for 640x480
+ */
+ i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0x40);
+ if (qvga)
+ vals = sd->frame_rate < 30 ? vga_15 : vga_30;
+ else
+ vals = sd->frame_rate < 15 ? sxga_7_5 : sxga_15;
+ write_i2c_regvals(sd, vals, ARRAY_SIZE(sxga_15));
+ return;
+ }
default:
return;
}
@@ -4120,6 +4204,7 @@ static void set_ov_sensor_window(struct sd *sd)
case SEN_OV2610AE:
case SEN_OV3610:
case SEN_OV7670:
+ case SEN_OV9600:
mode_init_ov_sensor_regs(sd);
return;
case SEN_OV7660:
@@ -4919,23 +5004,24 @@ static const struct sd_desc sd_desc = {
/* -- module initialisation -- */
static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF },
- {USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4052),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
{USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x041e, 0x4064),
- .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x041e, 0x4068),
+ {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x045e, 0x028c),
.driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
- {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x054c, 0x0155),
- .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 },
{USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 },
- {USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x05a9, 0x0530), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0x0519),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x05a9, 0x0530),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
{USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 },
{USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 },
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
index 0c6369b7fe18..76907eced4a1 100644
--- a/drivers/media/video/gspca/ov534.c
+++ b/drivers/media/video/gspca/ov534.c
@@ -28,6 +28,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "ov534"
#include "gspca.h"
@@ -775,7 +777,7 @@ static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
if (ret < 0) {
- err("write failed %d", ret);
+ pr_err("write failed %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -794,7 +796,7 @@ static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg)
0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
PDEBUG(D_USBI, "GET 01 0000 %04x %02x", reg, gspca_dev->usb_buf[0]);
if (ret < 0) {
- err("read failed %d", ret);
+ pr_err("read failed %d\n", ret);
gspca_dev->usb_err = ret;
}
return gspca_dev->usb_buf[0];
@@ -858,7 +860,7 @@ static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
if (!sccb_check_status(gspca_dev)) {
- err("sccb_reg_write failed");
+ pr_err("sccb_reg_write failed\n");
gspca_dev->usb_err = -EIO;
}
}
@@ -868,11 +870,11 @@ static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg)
ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
if (!sccb_check_status(gspca_dev))
- err("sccb_reg_read failed 1");
+ pr_err("sccb_reg_read failed 1\n");
ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
if (!sccb_check_status(gspca_dev))
- err("sccb_reg_read failed 2");
+ pr_err("sccb_reg_read failed 2\n");
return ov534_reg_read(gspca_dev, OV534_REG_READ);
}
diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c
index aaf5428c57f5..b3b1ea60a841 100644
--- a/drivers/media/video/gspca/ov534_9.c
+++ b/drivers/media/video/gspca/ov534_9.c
@@ -1,7 +1,7 @@
/*
- * ov534-ov965x gspca driver
+ * ov534-ov9xxx gspca driver
*
- * Copyright (C) 2009-2010 Jean-Francois Moine http://moinejf.free.fr
+ * Copyright (C) 2009-2011 Jean-Francois Moine http://moinejf.free.fr
* Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
* Copyright (C) 2008 Jim Paris <jim@jtan.com>
*
@@ -24,6 +24,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "ov534_9"
#include "gspca.h"
@@ -45,39 +47,44 @@ MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver");
MODULE_LICENSE("GPL");
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ AUTOGAIN,
+ EXPOSURE,
+ SHARPNESS,
+ SATUR,
+ LIGHTFREQ,
+ NCTRLS /* number of controls */
+};
+
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct gspca_ctrl ctrls[NCTRLS];
__u32 last_pts;
u8 last_fid;
- u8 brightness;
- u8 contrast;
- u8 autogain;
- u8 exposure;
- s8 sharpness;
- u8 satur;
- u8 freq;
+ u8 sensor;
+};
+enum sensors {
+ SENSOR_OV965x, /* ov9657 */
+ SENSOR_OV971x, /* ov9712 */
+ NSENSORS
};
/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- { /* 0 */
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setautogain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static void setsharpness(struct gspca_dev *gspca_dev);
+static void setsatur(struct gspca_dev *gspca_dev);
+static void setlightfreq(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -85,13 +92,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 15,
.step = 1,
-#define BRIGHTNESS_DEF 7
- .default_value = BRIGHTNESS_DEF,
+ .default_value = 7
},
- .set = sd_setbrightness,
- .get = sd_getbrightness,
+ .set_control = setbrightness
},
- { /* 1 */
+[CONTRAST] = {
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -99,13 +104,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 15,
.step = 1,
-#define CONTRAST_DEF 3
- .default_value = CONTRAST_DEF,
+ .default_value = 3
},
- .set = sd_setcontrast,
- .get = sd_getcontrast,
+ .set_control = setcontrast
},
- { /* 2 */
+[AUTOGAIN] = {
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -116,11 +119,9 @@ static const struct ctrl sd_ctrls[] = {
#define AUTOGAIN_DEF 1
.default_value = AUTOGAIN_DEF,
},
- .set = sd_setautogain,
- .get = sd_getautogain,
+ .set_control = setautogain
},
-#define EXPO_IDX 3
- { /* 3 */
+[EXPOSURE] = {
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -128,13 +129,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 3,
.step = 1,
-#define EXPO_DEF 0
- .default_value = EXPO_DEF,
+ .default_value = 0
},
- .set = sd_setexposure,
- .get = sd_getexposure,
+ .set_control = setexposure
},
- { /* 4 */
+[SHARPNESS] = {
{
.id = V4L2_CID_SHARPNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -142,13 +141,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = -1, /* -1 = auto */
.maximum = 4,
.step = 1,
-#define SHARPNESS_DEF -1
- .default_value = SHARPNESS_DEF,
+ .default_value = -1
},
- .set = sd_setsharpness,
- .get = sd_getsharpness,
+ .set_control = setsharpness
},
- { /* 5 */
+[SATUR] = {
{
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -156,13 +153,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 4,
.step = 1,
-#define SATUR_DEF 2
- .default_value = SATUR_DEF,
+ .default_value = 2
},
- .set = sd_setsatur,
- .get = sd_getsatur,
+ .set_control = setsatur
},
- {
+[LIGHTFREQ] = {
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.type = V4L2_CTRL_TYPE_MENU,
@@ -170,11 +165,9 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
.step = 1,
-#define FREQ_DEF 0
- .default_value = FREQ_DEF,
+ .default_value = 0
},
- .set = sd_setfreq,
- .get = sd_getfreq,
+ .set_control = setlightfreq
},
};
@@ -206,6 +199,14 @@ static const struct v4l2_pix_format ov965x_mode[] = {
.colorspace = V4L2_COLORSPACE_JPEG},
};
+static const struct v4l2_pix_format ov971x_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB
+ }
+};
+
static const u8 bridge_init[][2] = {
{0x88, 0xf8},
{0x89, 0xff},
@@ -240,7 +241,7 @@ static const u8 bridge_init[][2] = {
{0x94, 0x11},
};
-static const u8 sensor_init[][2] = {
+static const u8 ov965x_init[][2] = {
{0x12, 0x80}, /* com7 - SSCB reset */
{0x00, 0x00}, /* gain */
{0x01, 0x80}, /* blue */
@@ -450,7 +451,7 @@ static const u8 bridge_init_2[][2] = {
{0x94, 0x11},
};
-static const u8 sensor_init_2[][2] = {
+static const u8 ov965x_init_2[][2] = {
{0x3b, 0xc4},
{0x1e, 0x04}, /* mvfp */
{0x13, 0xe0}, /* com8 */
@@ -492,7 +493,65 @@ static const u8 sensor_init_2[][2] = {
{0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
};
-static const u8 sensor_start_1_vga[][2] = { /* same for qvga */
+static const u8 ov971x_init[][2] = {
+ {0x12, 0x80},
+ {0x09, 0x10},
+ {0x1e, 0x07},
+ {0x5f, 0x18},
+ {0x69, 0x04},
+ {0x65, 0x2a},
+ {0x68, 0x0a},
+ {0x39, 0x28},
+ {0x4d, 0x90},
+ {0xc1, 0x80},
+ {0x0c, 0x30},
+ {0x6d, 0x02},
+ {0x96, 0xf1},
+ {0xbc, 0x68},
+ {0x12, 0x00},
+ {0x3b, 0x00},
+ {0x97, 0x80},
+ {0x17, 0x25},
+ {0x18, 0xa2},
+ {0x19, 0x01},
+ {0x1a, 0xca},
+ {0x03, 0x0a},
+ {0x32, 0x07},
+ {0x98, 0x40}, /*{0x98, 0x00},*/
+ {0x99, 0xA0}, /*{0x99, 0x00},*/
+ {0x9a, 0x01}, /*{0x9a, 0x00},*/
+ {0x57, 0x00},
+ {0x58, 0x78}, /*{0x58, 0xc8},*/
+ {0x59, 0x50}, /*{0x59, 0xa0},*/
+ {0x4c, 0x13},
+ {0x4b, 0x36},
+ {0x3d, 0x3c},
+ {0x3e, 0x03},
+ {0xbd, 0x50}, /*{0xbd, 0xa0},*/
+ {0xbe, 0x78}, /*{0xbe, 0xc8},*/
+ {0x4e, 0x55},
+ {0x4f, 0x55},
+ {0x50, 0x55},
+ {0x51, 0x55},
+ {0x24, 0x55},
+ {0x25, 0x40},
+ {0x26, 0xa1},
+ {0x5c, 0x59},
+ {0x5d, 0x00},
+ {0x11, 0x00},
+ {0x2a, 0x98},
+ {0x2b, 0x06},
+ {0x2d, 0x00},
+ {0x2e, 0x00},
+ {0x13, 0xa5},
+ {0x14, 0x40},
+ {0x4a, 0x00},
+ {0x49, 0xce},
+ {0x22, 0x03},
+ {0x09, 0x00}
+};
+
+static const u8 ov965x_start_1_vga[][2] = { /* same for qvga */
{0x12, 0x62}, /* com7 - 30fps VGA YUV */
{0x36, 0xfa}, /* aref3 */
{0x69, 0x0a}, /* hv */
@@ -515,7 +574,7 @@ static const u8 sensor_start_1_vga[][2] = { /* same for qvga */
{0xc0, 0xaa},
};
-static const u8 sensor_start_1_svga[][2] = {
+static const u8 ov965x_start_1_svga[][2] = {
{0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */
{0x36, 0xf8}, /* aref3 */
{0x69, 0x02}, /* hv */
@@ -537,7 +596,7 @@ static const u8 sensor_start_1_svga[][2] = {
{0xc0, 0xe2},
};
-static const u8 sensor_start_1_xga[][2] = {
+static const u8 ov965x_start_1_xga[][2] = {
{0x12, 0x02}, /* com7 */
{0x36, 0xf8}, /* aref3 */
{0x69, 0x02}, /* hv */
@@ -560,7 +619,7 @@ static const u8 sensor_start_1_xga[][2] = {
{0xc0, 0xe2},
};
-static const u8 sensor_start_1_sxga[][2] = {
+static const u8 ov965x_start_1_sxga[][2] = {
{0x12, 0x02}, /* com7 */
{0x36, 0xf8}, /* aref3 */
{0x69, 0x02}, /* hv */
@@ -709,7 +768,7 @@ static const u8 bridge_start_sxga[][2] = {
{0x94, 0x11},
};
-static const u8 sensor_start_2_qvga[][2] = {
+static const u8 ov965x_start_2_qvga[][2] = {
{0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */
{0x1e, 0x04}, /* mvfp */
{0x13, 0xe0}, /* com8 */
@@ -727,7 +786,7 @@ static const u8 sensor_start_2_qvga[][2] = {
{0x3a, 0x80}, /* tslb - yuyv */
};
-static const u8 sensor_start_2_vga[][2] = {
+static const u8 ov965x_start_2_vga[][2] = {
{0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
{0x1e, 0x04}, /* mvfp */
{0x13, 0xe0}, /* com8 */
@@ -743,7 +802,7 @@ static const u8 sensor_start_2_vga[][2] = {
{0x2d, 0x00}, /* advfl */
};
-static const u8 sensor_start_2_svga[][2] = { /* same for xga */
+static const u8 ov965x_start_2_svga[][2] = { /* same for xga */
{0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
{0x1e, 0x04}, /* mvfp */
{0x13, 0xe0}, /* com8 */
@@ -757,7 +816,7 @@ static const u8 sensor_start_2_svga[][2] = { /* same for xga */
{0xa3, 0x41}, /* bd60 */
};
-static const u8 sensor_start_2_sxga[][2] = {
+static const u8 ov965x_start_2_sxga[][2] = {
{0x13, 0xe0}, /* com8 */
{0x00, 0x00},
{0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
@@ -785,7 +844,7 @@ static void reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
if (ret < 0) {
- err("reg_w failed %d", ret);
+ pr_err("reg_w failed %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -810,7 +869,7 @@ static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg)
0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
return gspca_dev->usb_buf[0];
@@ -848,7 +907,7 @@ static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
if (!sccb_check_status(gspca_dev))
- err("sccb_write failed");
+ pr_err("sccb_write failed\n");
}
static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg)
@@ -856,11 +915,11 @@ static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg)
reg_w(gspca_dev, OV534_REG_SUBADDR, reg);
reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
if (!sccb_check_status(gspca_dev))
- err("sccb_read failed 1");
+ pr_err("sccb_read failed 1\n");
reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
if (!sccb_check_status(gspca_dev))
- err("sccb_read failed 2");
+ pr_err("sccb_read failed 2\n");
return reg_r(gspca_dev, OV534_REG_READ);
}
@@ -922,7 +981,9 @@ static void setbrightness(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
- val = sd->brightness;
+ if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
+ return;
+ val = sd->ctrls[BRIGHTNESS].val;
if (val < 8)
val = 15 - val; /* f .. 8 */
else
@@ -935,8 +996,10 @@ static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ if (gspca_dev->ctrl_dis & (1 << CONTRAST))
+ return;
sccb_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */
- sd->contrast << 4);
+ sd->ctrls[CONTRAST].val << 4);
}
static void setautogain(struct gspca_dev *gspca_dev)
@@ -944,10 +1007,12 @@ static void setautogain(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
+ return;
/*fixme: should adjust agc/awb/aec by different controls */
val = sccb_read(gspca_dev, 0x13); /* com8 */
sccb_write(gspca_dev, 0xff, 0x00);
- if (sd->autogain)
+ if (sd->ctrls[AUTOGAIN].val)
val |= 0x05; /* agc & aec */
else
val &= 0xfa;
@@ -960,8 +1025,10 @@ static void setexposure(struct gspca_dev *gspca_dev)
u8 val;
static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
+ if (gspca_dev->ctrl_dis & (1 << EXPOSURE))
+ return;
sccb_write(gspca_dev, 0x10, /* aec[9:2] */
- expo[sd->exposure]);
+ expo[sd->ctrls[EXPOSURE].val]);
val = sccb_read(gspca_dev, 0x13); /* com8 */
sccb_write(gspca_dev, 0xff, 0x00);
@@ -977,7 +1044,9 @@ static void setsharpness(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
s8 val;
- val = sd->sharpness;
+ if (gspca_dev->ctrl_dis & (1 << SHARPNESS))
+ return;
+ val = sd->ctrls[SHARPNESS].val;
if (val < 0) { /* auto */
val = sccb_read(gspca_dev, 0x42); /* com17 */
sccb_write(gspca_dev, 0xff, 0x00);
@@ -1006,8 +1075,10 @@ static void setsatur(struct gspca_dev *gspca_dev)
{0x48, 0x90}
};
- val1 = matrix[sd->satur][0];
- val2 = matrix[sd->satur][1];
+ if (gspca_dev->ctrl_dis & (1 << SATUR))
+ return;
+ val1 = matrix[sd->ctrls[SATUR].val][0];
+ val2 = matrix[sd->ctrls[SATUR].val][1];
val3 = val1 + val2;
sccb_write(gspca_dev, 0x4f, val3); /* matrix coeff */
sccb_write(gspca_dev, 0x50, val3);
@@ -1022,14 +1093,16 @@ static void setsatur(struct gspca_dev *gspca_dev)
sccb_write(gspca_dev, 0x41, val1);
}
-static void setfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
+ if (gspca_dev->ctrl_dis & (1 << LIGHTFREQ))
+ return;
val = sccb_read(gspca_dev, 0x13); /* com8 */
sccb_write(gspca_dev, 0xff, 0x00);
- if (sd->freq == 0) {
+ if (sd->ctrls[LIGHTFREQ].val == 0) {
sccb_write(gspca_dev, 0x13, val & 0xdf);
return;
}
@@ -1037,7 +1110,7 @@ static void setfreq(struct gspca_dev *gspca_dev)
val = sccb_read(gspca_dev, 0x42); /* com17 */
sccb_write(gspca_dev, 0xff, 0x00);
- if (sd->freq == 1)
+ if (sd->ctrls[LIGHTFREQ].val == 1)
val |= 0x01;
else
val &= 0xfe;
@@ -1049,34 +1122,19 @@ static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam;
-
- cam = &gspca_dev->cam;
- cam->cam_mode = ov965x_mode;
- cam->nmodes = ARRAY_SIZE(ov965x_mode);
+ gspca_dev->cam.ctrls = sd->ctrls;
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
#if AUTOGAIN_DEF != 0
- sd->autogain = AUTOGAIN_DEF;
- gspca_dev->ctrl_inac |= (1 << EXPO_IDX);
-#endif
-#if EXPO_DEF != 0
- sd->exposure = EXPO_DEF;
-#endif
-#if SHARPNESS_DEF != 0
- sd->sharpness = SHARPNESS_DEF;
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE);
#endif
- sd->satur = SATUR_DEF;
- sd->freq = FREQ_DEF;
-
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
u16 sensor_id;
/* reset bridge */
@@ -1099,68 +1157,117 @@ static int sd_init(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
/* initialize */
- reg_w_array(gspca_dev, bridge_init,
- ARRAY_SIZE(bridge_init));
- sccb_w_array(gspca_dev, sensor_init,
- ARRAY_SIZE(sensor_init));
- reg_w_array(gspca_dev, bridge_init_2,
- ARRAY_SIZE(bridge_init_2));
- sccb_w_array(gspca_dev, sensor_init_2,
- ARRAY_SIZE(sensor_init_2));
- reg_w(gspca_dev, 0xe0, 0x00);
- reg_w(gspca_dev, 0xe0, 0x01);
- set_led(gspca_dev, 0);
- reg_w(gspca_dev, 0xe0, 0x00);
+ if ((sensor_id & 0xfff0) == 0x9650) {
+ sd->sensor = SENSOR_OV965x;
+
+ gspca_dev->cam.cam_mode = ov965x_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(ov965x_mode);
+
+ reg_w_array(gspca_dev, bridge_init,
+ ARRAY_SIZE(bridge_init));
+ sccb_w_array(gspca_dev, ov965x_init,
+ ARRAY_SIZE(ov965x_init));
+ reg_w_array(gspca_dev, bridge_init_2,
+ ARRAY_SIZE(bridge_init_2));
+ sccb_w_array(gspca_dev, ov965x_init_2,
+ ARRAY_SIZE(ov965x_init_2));
+ reg_w(gspca_dev, 0xe0, 0x00);
+ reg_w(gspca_dev, 0xe0, 0x01);
+ set_led(gspca_dev, 0);
+ reg_w(gspca_dev, 0xe0, 0x00);
+ } else if ((sensor_id & 0xfff0) == 0x9710) {
+ const char *p;
+ int l;
+
+ sd->sensor = SENSOR_OV971x;
+
+ gspca_dev->cam.cam_mode = ov971x_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(ov971x_mode);
+
+ /* no control yet */
+ gspca_dev->ctrl_dis = (1 << NCTRLS) - 1;
+
+ gspca_dev->cam.bulk = 1;
+ gspca_dev->cam.bulk_size = 16384;
+ gspca_dev->cam.bulk_nurbs = 2;
+
+ sccb_w_array(gspca_dev, ov971x_init,
+ ARRAY_SIZE(ov971x_init));
+
+ /* set video format on bridge processor */
+ /* access bridge processor's video format registers at: 0x00 */
+ reg_w(gspca_dev, 0x1c, 0x00);
+ /*set register: 0x00 is 'RAW8', 0x40 is 'YUV422' (YUYV?)*/
+ reg_w(gspca_dev, 0x1d, 0x00);
+
+ /* Will W. specific stuff
+ * set VSYNC to
+ * output (0x1f) if first webcam
+ * input (0x17) if 2nd or 3rd webcam */
+ p = video_device_node_name(&gspca_dev->vdev);
+ l = strlen(p) - 1;
+ if (p[l] == '0')
+ reg_w(gspca_dev, 0x56, 0x1f);
+ else
+ reg_w(gspca_dev, 0x56, 0x17);
+ } else {
+ err("Unknown sensor %04x", sensor_id);
+ return -EINVAL;
+ }
return gspca_dev->usb_err;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_OV971x)
+ return gspca_dev->usb_err;
switch (gspca_dev->curr_mode) {
case QVGA_MODE: /* 320x240 */
- sccb_w_array(gspca_dev, sensor_start_1_vga,
- ARRAY_SIZE(sensor_start_1_vga));
+ sccb_w_array(gspca_dev, ov965x_start_1_vga,
+ ARRAY_SIZE(ov965x_start_1_vga));
reg_w_array(gspca_dev, bridge_start_qvga,
ARRAY_SIZE(bridge_start_qvga));
- sccb_w_array(gspca_dev, sensor_start_2_qvga,
- ARRAY_SIZE(sensor_start_2_qvga));
+ sccb_w_array(gspca_dev, ov965x_start_2_qvga,
+ ARRAY_SIZE(ov965x_start_2_qvga));
break;
case VGA_MODE: /* 640x480 */
- sccb_w_array(gspca_dev, sensor_start_1_vga,
- ARRAY_SIZE(sensor_start_1_vga));
+ sccb_w_array(gspca_dev, ov965x_start_1_vga,
+ ARRAY_SIZE(ov965x_start_1_vga));
reg_w_array(gspca_dev, bridge_start_vga,
ARRAY_SIZE(bridge_start_vga));
- sccb_w_array(gspca_dev, sensor_start_2_vga,
- ARRAY_SIZE(sensor_start_2_vga));
+ sccb_w_array(gspca_dev, ov965x_start_2_vga,
+ ARRAY_SIZE(ov965x_start_2_vga));
break;
case SVGA_MODE: /* 800x600 */
- sccb_w_array(gspca_dev, sensor_start_1_svga,
- ARRAY_SIZE(sensor_start_1_svga));
+ sccb_w_array(gspca_dev, ov965x_start_1_svga,
+ ARRAY_SIZE(ov965x_start_1_svga));
reg_w_array(gspca_dev, bridge_start_svga,
ARRAY_SIZE(bridge_start_svga));
- sccb_w_array(gspca_dev, sensor_start_2_svga,
- ARRAY_SIZE(sensor_start_2_svga));
+ sccb_w_array(gspca_dev, ov965x_start_2_svga,
+ ARRAY_SIZE(ov965x_start_2_svga));
break;
case XGA_MODE: /* 1024x768 */
- sccb_w_array(gspca_dev, sensor_start_1_xga,
- ARRAY_SIZE(sensor_start_1_xga));
+ sccb_w_array(gspca_dev, ov965x_start_1_xga,
+ ARRAY_SIZE(ov965x_start_1_xga));
reg_w_array(gspca_dev, bridge_start_xga,
ARRAY_SIZE(bridge_start_xga));
- sccb_w_array(gspca_dev, sensor_start_2_svga,
- ARRAY_SIZE(sensor_start_2_svga));
+ sccb_w_array(gspca_dev, ov965x_start_2_svga,
+ ARRAY_SIZE(ov965x_start_2_svga));
break;
default:
/* case SXGA_MODE: * 1280x1024 */
- sccb_w_array(gspca_dev, sensor_start_1_sxga,
- ARRAY_SIZE(sensor_start_1_sxga));
+ sccb_w_array(gspca_dev, ov965x_start_1_sxga,
+ ARRAY_SIZE(ov965x_start_1_sxga));
reg_w_array(gspca_dev, bridge_start_sxga,
ARRAY_SIZE(bridge_start_sxga));
- sccb_w_array(gspca_dev, sensor_start_2_sxga,
- ARRAY_SIZE(sensor_start_2_sxga));
+ sccb_w_array(gspca_dev, ov965x_start_2_sxga,
+ ARRAY_SIZE(ov965x_start_2_sxga));
break;
}
- setfreq(gspca_dev);
+ setlightfreq(gspca_dev);
setautogain(gspca_dev);
setbrightness(gspca_dev);
setcontrast(gspca_dev);
@@ -1198,9 +1305,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
__u32 this_pts;
u8 this_fid;
int remaining_len = len;
+ int payload_len;
+ payload_len = gspca_dev->cam.bulk ? 2048 : 2040;
do {
- len = min(remaining_len, 2040);
+ len = min(remaining_len, payload_len);
/* Payloads are prefixed with a UVC-style header. We
consider a frame to start when the FID toggles, or the PTS
@@ -1262,138 +1371,6 @@ scan_next:
} while (remaining_len > 0);
}
-/* controls */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->autogain = val;
-
- if (gspca_dev->streaming) {
- if (val)
- gspca_dev->ctrl_inac |= (1 << EXPO_IDX);
- else
- gspca_dev->ctrl_inac &= ~(1 << EXPO_IDX);
- setautogain(gspca_dev);
- }
- return gspca_dev->usb_err;
-}
-
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->autogain;
- return 0;
-}
-
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->exposure = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->exposure;
- return 0;
-}
-
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->sharpness = val;
- if (gspca_dev->streaming)
- setsharpness(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->sharpness;
- return 0;
-}
-
-static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->satur = val;
- if (gspca_dev->streaming)
- setsatur(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->satur;
- return 0;
-}
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->freq = val;
- if (gspca_dev->streaming)
- setfreq(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->freq;
- return 0;
-}
-
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{
@@ -1419,7 +1396,7 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
+ .nctrls = NCTRLS,
.config = sd_config,
.init = sd_init,
.start = sd_start,
@@ -1430,6 +1407,7 @@ static const struct sd_desc sd_desc = {
/* -- module initialisation -- */
static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x05a9, 0x8065)},
{USB_DEVICE(0x06f8, 0x3003)},
{}
};
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
index 81739a2f205e..1600df152fd6 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -23,6 +23,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "pac207"
#include <linux/input.h>
@@ -178,8 +180,8 @@ static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
0x00, index,
gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
if (err < 0)
- err("Failed to write registers to index 0x%04X, error %d)",
- index, err);
+ pr_err("Failed to write registers to index 0x%04X, error %d\n",
+ index, err);
return err;
}
@@ -194,8 +196,8 @@ static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
if (err)
- err("Failed to write a register (index 0x%04X,"
- " value 0x%02X, error %d)", index, value, err);
+ pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
+ index, value, err);
return err;
}
@@ -210,8 +212,8 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
0x00, index,
gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT);
if (res < 0) {
- err("Failed to read a register (index 0x%04X, error %d)",
- index, res);
+ pr_err("Failed to read a register (index 0x%04X, error %d)\n",
+ index, res);
return res;
}
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
index 5615d7bd8304..1c44f78ff9e2 100644
--- a/drivers/media/video/gspca/pac7302.c
+++ b/drivers/media/video/gspca/pac7302.c
@@ -61,6 +61,8 @@
3 | 0x21 | sethvflip()
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "pac7302"
#include <linux/input.h>
@@ -408,8 +410,8 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
index, gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- err("reg_w_buf failed index 0x%02x, error %d",
- index, ret);
+ pr_err("reg_w_buf failed index 0x%02x, error %d\n",
+ index, ret);
gspca_dev->usb_err = ret;
}
}
@@ -431,8 +433,8 @@ static void reg_w(struct gspca_dev *gspca_dev,
0, index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w() failed index 0x%02x, value 0x%02x, error %d",
- index, value, ret);
+ pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
+ index, value, ret);
gspca_dev->usb_err = ret;
}
}
@@ -466,9 +468,8 @@ static void reg_w_page(struct gspca_dev *gspca_dev,
0, index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w_page() failed index 0x%02x, "
- "value 0x%02x, error %d",
- index, page[index], ret);
+ pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
+ index, page[index], ret);
gspca_dev->usb_err = ret;
break;
}
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index f8801b50e64f..7509d05dc065 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -49,6 +49,8 @@
for max gain, 0x14 for minimal gain.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "pac7311"
#include <linux/input.h>
@@ -276,8 +278,8 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
index, gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- err("reg_w_buf() failed index 0x%02x, error %d",
- index, ret);
+ pr_err("reg_w_buf() failed index 0x%02x, error %d\n",
+ index, ret);
gspca_dev->usb_err = ret;
}
}
@@ -299,8 +301,8 @@ static void reg_w(struct gspca_dev *gspca_dev,
0, index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w() failed index 0x%02x, value 0x%02x, error %d",
- index, value, ret);
+ pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
+ index, value, ret);
gspca_dev->usb_err = ret;
}
}
@@ -334,9 +336,8 @@ static void reg_w_page(struct gspca_dev *gspca_dev,
0, index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w_page() failed index 0x%02x, "
- "value 0x%02x, error %d",
- index, page[index], ret);
+ pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
+ index, page[index], ret);
gspca_dev->usb_err = ret;
break;
}
diff --git a/drivers/media/video/gspca/se401.c b/drivers/media/video/gspca/se401.c
new file mode 100644
index 000000000000..3b71bbcd977a
--- /dev/null
+++ b/drivers/media/video/gspca/se401.c
@@ -0,0 +1,780 @@
+/*
+ * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the v4l1 se401 driver which is:
+ *
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * 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
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "se401"
+
+#define BULK_SIZE 4096
+#define PACKET_SIZE 1024
+#define READ_REQ_SIZE 64
+#define MAX_MODES ((READ_REQ_SIZE - 6) / 4)
+/* The se401 compression algorithm uses a fixed quant factor, which
+ can be configured by setting the high nibble of the SE401_OPERATINGMODE
+ feature. This needs to exactly match what is in libv4l! */
+#define SE401_QUANT_FACT 8
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "gspca.h"
+#include "se401.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Endpoints se401");
+MODULE_LICENSE("GPL");
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ GAIN,
+ EXPOSURE,
+ FREQ,
+ NCTRL /* number of controls */
+};
+
+/* exposure change state machine states */
+enum {
+ EXPO_CHANGED,
+ EXPO_DROP_FRAME,
+ EXPO_NO_CHANGE,
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct gspca_ctrl ctrls[NCTRL];
+ struct v4l2_pix_format fmts[MAX_MODES];
+ int pixels_read;
+ int packet_read;
+ u8 packet[PACKET_SIZE];
+ u8 restart_stream;
+ u8 button_state;
+ u8 resetlevel;
+ u8 resetlevel_frame_count;
+ int resetlevel_adjust_dir;
+ int expo_change_state;
+};
+
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setgain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRL] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 15,
+ },
+ .set_control = setbrightness
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 50, /* Really 63 but > 50 is not pretty */
+ .step = 1,
+ .default_value = 25,
+ },
+ .set_control = setgain
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 32767,
+ .step = 1,
+ .default_value = 15000,
+ },
+ .set_control = setexposure
+ },
+[FREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = setexposure
+ },
+};
+
+static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value,
+ int silent)
+{
+ int err;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ err = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0), req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0, NULL, 0, 1000);
+ if (err < 0) {
+ if (!silent)
+ pr_err("write req failed req %#04x val %#04x error %d\n",
+ req, value, err);
+ gspca_dev->usb_err = err;
+ }
+}
+
+static void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent)
+{
+ int err;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ if (USB_BUF_SZ < READ_REQ_SIZE) {
+ pr_err("USB_BUF_SZ too small!!\n");
+ gspca_dev->usb_err = -ENOBUFS;
+ return;
+ }
+
+ err = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0), req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, gspca_dev->usb_buf, READ_REQ_SIZE, 1000);
+ if (err < 0) {
+ if (!silent)
+ pr_err("read req failed req %#04x error %d\n",
+ req, err);
+ gspca_dev->usb_err = err;
+ }
+}
+
+static void se401_set_feature(struct gspca_dev *gspca_dev,
+ u16 selector, u16 param)
+{
+ int err;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ err = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ SE401_REQ_SET_EXT_FEATURE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ param, selector, NULL, 0, 1000);
+ if (err < 0) {
+ pr_err("set feature failed sel %#04x param %#04x error %d\n",
+ selector, param, err);
+ gspca_dev->usb_err = err;
+ }
+}
+
+static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector)
+{
+ int err;
+
+ if (gspca_dev->usb_err < 0)
+ return gspca_dev->usb_err;
+
+ if (USB_BUF_SZ < 2) {
+ pr_err("USB_BUF_SZ too small!!\n");
+ gspca_dev->usb_err = -ENOBUFS;
+ return gspca_dev->usb_err;
+ }
+
+ err = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ SE401_REQ_GET_EXT_FEATURE,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, selector, gspca_dev->usb_buf, 2, 1000);
+ if (err < 0) {
+ pr_err("get feature failed sel %#04x error %d\n",
+ selector, err);
+ gspca_dev->usb_err = err;
+ return err;
+ }
+ return gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
+ return;
+
+ /* HDG: this does not seem to do anything on my cam */
+ se401_write_req(gspca_dev, SE401_REQ_SET_BRT,
+ sd->ctrls[BRIGHTNESS].val, 0);
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 gain = 63 - sd->ctrls[GAIN].val;
+
+ /* red color gain */
+ se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain);
+ /* green color gain */
+ se401_set_feature(gspca_dev, HV7131_REG_AGCG, gain);
+ /* blue color gain */
+ se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int integration = sd->ctrls[EXPOSURE].val << 6;
+ u8 expose_h, expose_m, expose_l;
+
+ /* Do this before the set_feature calls, for proper timing wrt
+ the interrupt driven pkt_scan. Note we may still race but that
+ is not a big issue, the expo change state machine is merely for
+ avoiding underexposed frames getting send out, if one sneaks
+ through so be it */
+ sd->expo_change_state = EXPO_CHANGED;
+
+ if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
+ integration = integration - integration % 106667;
+ if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)
+ integration = integration - integration % 88889;
+
+ expose_h = (integration >> 16);
+ expose_m = (integration >> 8);
+ expose_l = integration;
+
+ /* integration time low */
+ se401_set_feature(gspca_dev, HV7131_REG_TITL, expose_l);
+ /* integration time mid */
+ se401_set_feature(gspca_dev, HV7131_REG_TITM, expose_m);
+ /* integration time high */
+ se401_set_feature(gspca_dev, HV7131_REG_TITU, expose_h);
+}
+
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+ u8 *cd = gspca_dev->usb_buf;
+ int i, j, n;
+ int widths[MAX_MODES], heights[MAX_MODES];
+
+ /* Read the camera descriptor */
+ se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 1);
+ if (gspca_dev->usb_err) {
+ /* Sometimes after being idle for a while the se401 won't
+ respond and needs a good kicking */
+ usb_reset_device(gspca_dev->dev);
+ gspca_dev->usb_err = 0;
+ se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0);
+ }
+
+ /* Some cameras start with their LED on */
+ se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0);
+ if (gspca_dev->usb_err)
+ return gspca_dev->usb_err;
+
+ if (cd[1] != 0x41) {
+ pr_err("Wrong descriptor type\n");
+ return -ENODEV;
+ }
+
+ if (!(cd[2] & SE401_FORMAT_BAYER)) {
+ pr_err("Bayer format not supported!\n");
+ return -ENODEV;
+ }
+
+ if (cd[3])
+ pr_info("ExtraFeatures: %d\n", cd[3]);
+
+ n = cd[4] | (cd[5] << 8);
+ if (n > MAX_MODES) {
+ pr_err("Too many frame sizes\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < n ; i++) {
+ widths[i] = cd[6 + i * 4 + 0] | (cd[6 + i * 4 + 1] << 8);
+ heights[i] = cd[6 + i * 4 + 2] | (cd[6 + i * 4 + 3] << 8);
+ }
+
+ for (i = 0; i < n ; i++) {
+ sd->fmts[i].width = widths[i];
+ sd->fmts[i].height = heights[i];
+ sd->fmts[i].field = V4L2_FIELD_NONE;
+ sd->fmts[i].colorspace = V4L2_COLORSPACE_SRGB;
+ sd->fmts[i].priv = 1;
+
+ /* janggu compression only works for 1/4th or 1/16th res */
+ for (j = 0; j < n; j++) {
+ if (widths[j] / 2 == widths[i] &&
+ heights[j] / 2 == heights[i]) {
+ sd->fmts[i].priv = 2;
+ break;
+ }
+ }
+ /* 1/16th if available too is better then 1/4th, because
+ we then use a larger area of the sensor */
+ for (j = 0; j < n; j++) {
+ if (widths[j] / 4 == widths[i] &&
+ heights[j] / 4 == heights[i]) {
+ sd->fmts[i].priv = 4;
+ break;
+ }
+ }
+
+ if (sd->fmts[i].priv == 1) {
+ /* Not a 1/4th or 1/16th res, use bayer */
+ sd->fmts[i].pixelformat = V4L2_PIX_FMT_SBGGR8;
+ sd->fmts[i].bytesperline = widths[i];
+ sd->fmts[i].sizeimage = widths[i] * heights[i];
+ pr_info("Frame size: %dx%d bayer\n",
+ widths[i], heights[i]);
+ } else {
+ /* Found a match use janggu compression */
+ sd->fmts[i].pixelformat = V4L2_PIX_FMT_SE401;
+ sd->fmts[i].bytesperline = 0;
+ sd->fmts[i].sizeimage = widths[i] * heights[i] * 3;
+ pr_info("Frame size: %dx%d 1/%dth janggu\n",
+ widths[i], heights[i],
+ sd->fmts[i].priv * sd->fmts[i].priv);
+ }
+ }
+
+ cam->cam_mode = sd->fmts;
+ cam->nmodes = n;
+ cam->bulk = 1;
+ cam->bulk_size = BULK_SIZE;
+ cam->bulk_nurbs = 4;
+ cam->ctrls = sd->ctrls;
+ gspca_dev->nbalt = 1; /* Ignore the bogus isoc alt settings */
+ sd->resetlevel = 0x2d; /* Set initial resetlevel */
+
+ /* See if the camera supports brightness */
+ se401_read_req(gspca_dev, SE401_REQ_GET_BRT, 1);
+ if (gspca_dev->usb_err) {
+ gspca_dev->ctrl_dis = (1 << BRIGHTNESS);
+ gspca_dev->usb_err = 0;
+ }
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ int mode = 0;
+
+ se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 1);
+ if (gspca_dev->usb_err) {
+ /* Sometimes after being idle for a while the se401 won't
+ respond and needs a good kicking */
+ usb_reset_device(gspca_dev->dev);
+ gspca_dev->usb_err = 0;
+ se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 0);
+ }
+ se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 1, 0);
+
+ se401_set_feature(gspca_dev, HV7131_REG_MODE_B, 0x05);
+
+ /* set size + mode */
+ se401_write_req(gspca_dev, SE401_REQ_SET_WIDTH,
+ gspca_dev->width * mult, 0);
+ se401_write_req(gspca_dev, SE401_REQ_SET_HEIGHT,
+ gspca_dev->height * mult, 0);
+ /*
+ * HDG: disabled this as it does not seem to do anything
+ * se401_write_req(gspca_dev, SE401_REQ_SET_OUTPUT_MODE,
+ * SE401_FORMAT_BAYER, 0);
+ */
+
+ switch (mult) {
+ case 1: /* Raw bayer */
+ mode = 0x03; break;
+ case 2: /* 1/4th janggu */
+ mode = SE401_QUANT_FACT << 4; break;
+ case 4: /* 1/16th janggu */
+ mode = (SE401_QUANT_FACT << 4) | 0x02; break;
+ }
+ se401_set_feature(gspca_dev, SE401_OPERATINGMODE, mode);
+
+ setbrightness(gspca_dev);
+ setgain(gspca_dev);
+ setexposure(gspca_dev);
+ se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
+
+ sd->packet_read = 0;
+ sd->pixels_read = 0;
+ sd->restart_stream = 0;
+ sd->resetlevel_frame_count = 0;
+ sd->resetlevel_adjust_dir = 0;
+ sd->expo_change_state = EXPO_NO_CHANGE;
+
+ se401_write_req(gspca_dev, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, 0);
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ se401_write_req(gspca_dev, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, 0);
+ se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0);
+ se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 0, 0);
+}
+
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ unsigned int ahrc, alrc;
+ int oldreset, adjust_dir;
+
+ /* Restart the stream if requested do so by pkt_scan */
+ if (sd->restart_stream) {
+ sd_stopN(gspca_dev);
+ sd_start(gspca_dev);
+ sd->restart_stream = 0;
+ }
+
+ /* Automatically adjust sensor reset level
+ Hyundai have some really nice docs about this and other sensor
+ related stuff on their homepage: www.hei.co.kr */
+ sd->resetlevel_frame_count++;
+ if (sd->resetlevel_frame_count < 20)
+ return;
+
+ /* For some reason this normally read-only register doesn't get reset
+ to zero after reading them just once... */
+ se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH);
+ se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL);
+ se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH);
+ se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL);
+ ahrc = 256*se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH) +
+ se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL);
+ alrc = 256*se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH) +
+ se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL);
+
+ /* Not an exact science, but it seems to work pretty well... */
+ oldreset = sd->resetlevel;
+ if (alrc > 10) {
+ while (alrc >= 10 && sd->resetlevel < 63) {
+ sd->resetlevel++;
+ alrc /= 2;
+ }
+ } else if (ahrc > 20) {
+ while (ahrc >= 20 && sd->resetlevel > 0) {
+ sd->resetlevel--;
+ ahrc /= 2;
+ }
+ }
+ /* Detect ping-pong-ing and halve adjustment to avoid overshoot */
+ if (sd->resetlevel > oldreset)
+ adjust_dir = 1;
+ else
+ adjust_dir = -1;
+ if (sd->resetlevel_adjust_dir &&
+ sd->resetlevel_adjust_dir != adjust_dir)
+ sd->resetlevel = oldreset + (sd->resetlevel - oldreset) / 2;
+
+ if (sd->resetlevel != oldreset) {
+ sd->resetlevel_adjust_dir = adjust_dir;
+ se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
+ }
+
+ sd->resetlevel_frame_count = 0;
+}
+
+static void sd_complete_frame(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ switch (sd->expo_change_state) {
+ case EXPO_CHANGED:
+ /* The exposure was changed while this frame
+ was being send, so this frame is ok */
+ sd->expo_change_state = EXPO_DROP_FRAME;
+ break;
+ case EXPO_DROP_FRAME:
+ /* The exposure was changed while this frame
+ was being captured, drop it! */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ sd->expo_change_state = EXPO_NO_CHANGE;
+ break;
+ case EXPO_NO_CHANGE:
+ break;
+ }
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, len);
+}
+
+static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ int imagesize = gspca_dev->width * gspca_dev->height;
+ int i, plen, bits, pixels, info, count;
+
+ if (sd->restart_stream)
+ return;
+
+ /* Sometimes a 1024 bytes garbage bulk packet is send between frames */
+ if (gspca_dev->last_packet_type == LAST_PACKET && len == 1024) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+
+ i = 0;
+ while (i < len) {
+ /* Read header if not already be present from prev bulk pkt */
+ if (sd->packet_read < 4) {
+ count = 4 - sd->packet_read;
+ if (count > len - i)
+ count = len - i;
+ memcpy(&sd->packet[sd->packet_read], &data[i], count);
+ sd->packet_read += count;
+ i += count;
+ if (sd->packet_read < 4)
+ break;
+ }
+ bits = sd->packet[3] + (sd->packet[2] << 8);
+ pixels = sd->packet[1] + ((sd->packet[0] & 0x3f) << 8);
+ info = (sd->packet[0] & 0xc0) >> 6;
+ plen = ((bits + 47) >> 4) << 1;
+ /* Sanity checks */
+ if (plen > 1024) {
+ pr_err("invalid packet len %d restarting stream\n",
+ plen);
+ goto error;
+ }
+ if (info == 3) {
+ pr_err("unknown frame info value restarting stream\n");
+ goto error;
+ }
+
+ /* Read (remainder of) packet contents */
+ count = plen - sd->packet_read;
+ if (count > len - i)
+ count = len - i;
+ memcpy(&sd->packet[sd->packet_read], &data[i], count);
+ sd->packet_read += count;
+ i += count;
+ if (sd->packet_read < plen)
+ break;
+
+ sd->pixels_read += pixels;
+ sd->packet_read = 0;
+
+ switch (info) {
+ case 0: /* Frame data */
+ gspca_frame_add(gspca_dev, INTER_PACKET, sd->packet,
+ plen);
+ break;
+ case 1: /* EOF */
+ if (sd->pixels_read != imagesize) {
+ pr_err("frame size %d expected %d\n",
+ sd->pixels_read, imagesize);
+ goto error;
+ }
+ sd_complete_frame(gspca_dev, sd->packet, plen);
+ return; /* Discard the rest of the bulk packet !! */
+ case 2: /* SOF */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, sd->packet,
+ plen);
+ sd->pixels_read = pixels;
+ break;
+ }
+ }
+ return;
+
+error:
+ sd->restart_stream = 1;
+ /* Give userspace a 0 bytes frame, so our dq callback gets
+ called and it can restart the stream */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+}
+
+static void sd_pkt_scan_bayer(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct cam *cam = &gspca_dev->cam;
+ int imagesize = cam->cam_mode[gspca_dev->curr_mode].sizeimage;
+
+ if (gspca_dev->image_len == 0) {
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+
+ if (gspca_dev->image_len + len >= imagesize) {
+ sd_complete_frame(gspca_dev, data, len);
+ return;
+ }
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+
+ if (len == 0)
+ return;
+
+ if (mult == 1) /* mult == 1 means raw bayer */
+ sd_pkt_scan_bayer(gspca_dev, data, len);
+ else
+ sd_pkt_scan_janggu(gspca_dev, data, len);
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
+ strcpy((char *) menu->name, "NoFliker");
+ return 0;
+ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ u8 state;
+
+ if (len != 2)
+ return -EINVAL;
+
+ switch (data[0]) {
+ case 0:
+ case 1:
+ state = data[0];
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (sd->button_state != state) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, state);
+ input_sync(gspca_dev->input_dev);
+ sd->button_state = state;
+ }
+
+ return 0;
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .dq_callback = sd_dq_callback,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x03e8, 0x0004)}, /* Endpoints/Aox SE401 */
+ {USB_DEVICE(0x0471, 0x030b)}, /* Philips PCVC665K */
+ {USB_DEVICE(0x047d, 0x5001)}, /* Kensington 67014 */
+ {USB_DEVICE(0x047d, 0x5002)}, /* Kensington 6701(5/7) */
+ {USB_DEVICE(0x047d, 0x5003)}, /* Kensington 67016 */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static int sd_pre_reset(struct usb_interface *intf)
+{
+ return 0;
+}
+
+static int sd_post_reset(struct usb_interface *intf)
+{
+ return 0;
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+ .pre_reset = sd_pre_reset,
+ .post_reset = sd_post_reset,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ return usb_register(&sd_driver);
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/se401.h b/drivers/media/video/gspca/se401.h
new file mode 100644
index 000000000000..96d8ebf3cf59
--- /dev/null
+++ b/drivers/media/video/gspca/se401.h
@@ -0,0 +1,90 @@
+/*
+ * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the v4l1 se401 driver which is:
+ *
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * 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
+ *
+ */
+
+#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06
+#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41
+#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42
+#define SE401_REQ_CAPTURE_FRAME 0x43
+#define SE401_REQ_GET_BRT 0x44
+#define SE401_REQ_SET_BRT 0x45
+#define SE401_REQ_GET_WIDTH 0x4c
+#define SE401_REQ_SET_WIDTH 0x4d
+#define SE401_REQ_GET_HEIGHT 0x4e
+#define SE401_REQ_SET_HEIGHT 0x4f
+#define SE401_REQ_GET_OUTPUT_MODE 0x50
+#define SE401_REQ_SET_OUTPUT_MODE 0x51
+#define SE401_REQ_GET_EXT_FEATURE 0x52
+#define SE401_REQ_SET_EXT_FEATURE 0x53
+#define SE401_REQ_CAMERA_POWER 0x56
+#define SE401_REQ_LED_CONTROL 0x57
+#define SE401_REQ_BIOS 0xff
+
+#define SE401_BIOS_READ 0x07
+
+#define SE401_FORMAT_BAYER 0x40
+
+/* Hyundai hv7131b registers
+ 7121 and 7141 should be the same (haven't really checked...) */
+/* Mode registers: */
+#define HV7131_REG_MODE_A 0x00
+#define HV7131_REG_MODE_B 0x01
+#define HV7131_REG_MODE_C 0x02
+/* Frame registers: */
+#define HV7131_REG_FRSU 0x10
+#define HV7131_REG_FRSL 0x11
+#define HV7131_REG_FCSU 0x12
+#define HV7131_REG_FCSL 0x13
+#define HV7131_REG_FWHU 0x14
+#define HV7131_REG_FWHL 0x15
+#define HV7131_REG_FWWU 0x16
+#define HV7131_REG_FWWL 0x17
+/* Timing registers: */
+#define HV7131_REG_THBU 0x20
+#define HV7131_REG_THBL 0x21
+#define HV7131_REG_TVBU 0x22
+#define HV7131_REG_TVBL 0x23
+#define HV7131_REG_TITU 0x25
+#define HV7131_REG_TITM 0x26
+#define HV7131_REG_TITL 0x27
+#define HV7131_REG_TMCD 0x28
+/* Adjust Registers: */
+#define HV7131_REG_ARLV 0x30
+#define HV7131_REG_ARCG 0x31
+#define HV7131_REG_AGCG 0x32
+#define HV7131_REG_ABCG 0x33
+#define HV7131_REG_APBV 0x34
+#define HV7131_REG_ASLP 0x54
+/* Offset Registers: */
+#define HV7131_REG_OFSR 0x50
+#define HV7131_REG_OFSG 0x51
+#define HV7131_REG_OFSB 0x52
+/* REset level statistics registers: */
+#define HV7131_REG_LOREFNOH 0x57
+#define HV7131_REG_LOREFNOL 0x58
+#define HV7131_REG_HIREFNOH 0x59
+#define HV7131_REG_HIREFNOL 0x5a
+
+/* se401 registers */
+#define SE401_OPERATINGMODE 0x2000
diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c
index 4271f86dfe01..48aae3926a33 100644
--- a/drivers/media/video/gspca/sn9c2028.c
+++ b/drivers/media/video/gspca/sn9c2028.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sn9c2028"
#include "gspca.h"
@@ -75,8 +77,8 @@ static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
2, 0, gspca_dev->usb_buf, 6, 500);
if (rc < 0) {
- err("command write [%02x] error %d",
- gspca_dev->usb_buf[0], rc);
+ pr_err("command write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], rc);
return rc;
}
@@ -93,7 +95,7 @@ static int sn9c2028_read1(struct gspca_dev *gspca_dev)
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
1, 0, gspca_dev->usb_buf, 1, 500);
if (rc != 1) {
- err("read1 error %d", rc);
+ pr_err("read1 error %d\n", rc);
return (rc < 0) ? rc : -EIO;
}
PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]);
@@ -109,7 +111,7 @@ static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading)
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
4, 0, gspca_dev->usb_buf, 4, 500);
if (rc != 4) {
- err("read4 error %d", rc);
+ pr_err("read4 error %d\n", rc);
return (rc < 0) ? rc : -EIO;
}
memcpy(reading, gspca_dev->usb_buf, 4);
@@ -131,7 +133,7 @@ static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command)
for (i = 0; i < 256 && status < 2; i++)
status = sn9c2028_read1(gspca_dev);
if (status != 2) {
- err("long command status read error %d", status);
+ pr_err("long command status read error %d\n", status);
return (status < 0) ? status : -EIO;
}
@@ -638,7 +640,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
err_code = start_vivitar_cam(gspca_dev);
break;
default:
- err("Starting unknown camera, please report this");
+ pr_err("Starting unknown camera, please report this\n");
return -ENXIO;
}
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index c431900cd292..86e07a139a16 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/input.h>
#include "gspca.h"
@@ -1123,7 +1125,7 @@ static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
length,
500);
if (unlikely(result < 0 || result != length)) {
- err("Read register failed 0x%02X", reg);
+ pr_err("Read register failed 0x%02X\n", reg);
return -EIO;
}
return 0;
@@ -1144,7 +1146,7 @@ static int reg_w(struct gspca_dev *gspca_dev, u16 reg,
length,
500);
if (unlikely(result < 0 || result != length)) {
- err("Write register failed index 0x%02X", reg);
+ pr_err("Write register failed index 0x%02X\n", reg);
return -EIO;
}
return 0;
@@ -1275,14 +1277,14 @@ static int ov9650_init_sensor(struct gspca_dev *gspca_dev)
return -EINVAL;
if (id != 0x7fa2) {
- err("sensor id for ov9650 doesn't match (0x%04x)", id);
+ pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id);
return -ENODEV;
}
for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) {
if (i2c_w1(gspca_dev, ov9650_init[i].reg,
ov9650_init[i].val) < 0) {
- err("OV9650 sensor initialization failed");
+ pr_err("OV9650 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1299,7 +1301,7 @@ static int ov9655_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) {
if (i2c_w1(gspca_dev, ov9655_init[i].reg,
ov9655_init[i].val) < 0) {
- err("OV9655 sensor initialization failed");
+ pr_err("OV9655 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1318,7 +1320,7 @@ static int soi968_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(soi968_init); i++) {
if (i2c_w1(gspca_dev, soi968_init[i].reg,
soi968_init[i].val) < 0) {
- err("SOI968 sensor initialization failed");
+ pr_err("SOI968 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1338,7 +1340,7 @@ static int ov7660_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) {
if (i2c_w1(gspca_dev, ov7660_init[i].reg,
ov7660_init[i].val) < 0) {
- err("OV7660 sensor initialization failed");
+ pr_err("OV7660 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1355,7 +1357,7 @@ static int ov7670_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) {
if (i2c_w1(gspca_dev, ov7670_init[i].reg,
ov7670_init[i].val) < 0) {
- err("OV7670 sensor initialization failed");
+ pr_err("OV7670 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1379,14 +1381,14 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) {
if (i2c_w2(gspca_dev, mt9v011_init[i].reg,
mt9v011_init[i].val) < 0) {
- err("MT9V011 sensor initialization failed");
+ pr_err("MT9V011 sensor initialization failed\n");
return -ENODEV;
}
}
sd->hstart = 2;
sd->vstart = 2;
sd->sensor = SENSOR_MT9V011;
- info("MT9V011 sensor detected");
+ pr_info("MT9V011 sensor detected\n");
return 0;
}
@@ -1397,7 +1399,7 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) {
if (i2c_w2(gspca_dev, mt9v111_init[i].reg,
mt9v111_init[i].val) < 0) {
- err("MT9V111 sensor initialization failed");
+ pr_err("MT9V111 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1407,7 +1409,7 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
sd->hstart = 2;
sd->vstart = 2;
sd->sensor = SENSOR_MT9V111;
- info("MT9V111 sensor detected");
+ pr_info("MT9V111 sensor detected\n");
return 0;
}
@@ -1422,14 +1424,14 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) {
if (i2c_w2(gspca_dev, mt9v112_init[i].reg,
mt9v112_init[i].val) < 0) {
- err("MT9V112 sensor initialization failed");
+ pr_err("MT9V112 sensor initialization failed\n");
return -ENODEV;
}
}
sd->hstart = 6;
sd->vstart = 2;
sd->sensor = SENSOR_MT9V112;
- info("MT9V112 sensor detected");
+ pr_info("MT9V112 sensor detected\n");
return 0;
}
@@ -1443,7 +1445,7 @@ static int mt9m112_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(mt9m112_init); i++) {
if (i2c_w2(gspca_dev, mt9m112_init[i].reg,
mt9m112_init[i].val) < 0) {
- err("MT9M112 sensor initialization failed");
+ pr_err("MT9M112 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1461,7 +1463,7 @@ static int mt9m111_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) {
if (i2c_w2(gspca_dev, mt9m111_init[i].reg,
mt9m111_init[i].val) < 0) {
- err("MT9M111 sensor initialization failed");
+ pr_err("MT9M111 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1485,20 +1487,20 @@ static int mt9m001_init_sensor(struct gspca_dev *gspca_dev)
switch (id) {
case 0x8411:
case 0x8421:
- info("MT9M001 color sensor detected");
+ pr_info("MT9M001 color sensor detected\n");
break;
case 0x8431:
- info("MT9M001 mono sensor detected");
+ pr_info("MT9M001 mono sensor detected\n");
break;
default:
- err("No MT9M001 chip detected, ID = %x\n", id);
+ pr_err("No MT9M001 chip detected, ID = %x\n\n", id);
return -ENODEV;
}
for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) {
if (i2c_w2(gspca_dev, mt9m001_init[i].reg,
mt9m001_init[i].val) < 0) {
- err("MT9M001 sensor initialization failed");
+ pr_err("MT9M001 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1517,7 +1519,7 @@ static int hv7131r_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) {
if (i2c_w1(gspca_dev, hv7131r_init[i].reg,
hv7131r_init[i].val) < 0) {
- err("HV7131R Sensor initialization failed");
+ pr_err("HV7131R Sensor initialization failed\n");
return -ENODEV;
}
}
@@ -2103,7 +2105,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(bridge_init); i++) {
value = bridge_init[i][1];
if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) {
- err("Device initialization failed");
+ pr_err("Device initialization failed\n");
return -ENODEV;
}
}
@@ -2114,7 +2116,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x1006, 0x20);
if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) {
- err("Device initialization failed");
+ pr_err("Device initialization failed\n");
return -ENODEV;
}
@@ -2122,27 +2124,27 @@ static int sd_init(struct gspca_dev *gspca_dev)
case SENSOR_OV9650:
if (ov9650_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("OV9650 sensor detected");
+ pr_info("OV9650 sensor detected\n");
break;
case SENSOR_OV9655:
if (ov9655_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("OV9655 sensor detected");
+ pr_info("OV9655 sensor detected\n");
break;
case SENSOR_SOI968:
if (soi968_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("SOI968 sensor detected");
+ pr_info("SOI968 sensor detected\n");
break;
case SENSOR_OV7660:
if (ov7660_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("OV7660 sensor detected");
+ pr_info("OV7660 sensor detected\n");
break;
case SENSOR_OV7670:
if (ov7670_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("OV7670 sensor detected");
+ pr_info("OV7670 sensor detected\n");
break;
case SENSOR_MT9VPRB:
if (mt9v_init_sensor(gspca_dev) < 0)
@@ -2151,12 +2153,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
case SENSOR_MT9M111:
if (mt9m111_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("MT9M111 sensor detected");
+ pr_info("MT9M111 sensor detected\n");
break;
case SENSOR_MT9M112:
if (mt9m112_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("MT9M112 sensor detected");
+ pr_info("MT9M112 sensor detected\n");
break;
case SENSOR_MT9M001:
if (mt9m001_init_sensor(gspca_dev) < 0)
@@ -2165,10 +2167,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
case SENSOR_HV7131R:
if (hv7131r_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("HV7131R sensor detected");
+ pr_info("HV7131R sensor detected\n");
break;
default:
- info("Unsupported Sensor");
+ pr_info("Unsupported Sensor\n");
return -ENODEV;
}
@@ -2263,19 +2265,19 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (mode & SCALE_MASK) {
case SCALE_1280x1024:
scale = 0xc0;
- info("Set 1280x1024");
+ pr_info("Set 1280x1024\n");
break;
case SCALE_640x480:
scale = 0x80;
- info("Set 640x480");
+ pr_info("Set 640x480\n");
break;
case SCALE_320x240:
scale = 0x90;
- info("Set 320x240");
+ pr_info("Set 320x240\n");
break;
case SCALE_160x120:
scale = 0xa0;
- info("Set 160x120");
+ pr_info("Set 160x120\n");
break;
}
@@ -2513,7 +2515,7 @@ static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)},
{USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)},
{USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)},
- {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, LED_REVERSE)},
{USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, LED_REVERSE)},
{USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)},
{USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)},
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index 81b8a600783b..c746bf19ca14 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -1,7 +1,7 @@
/*
* Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver
*
- * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2009-2011 Jean-François Moine <http://moinejf.free.fr>
* Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
*
* This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sonixj"
#include <linux/input.h>
@@ -136,7 +138,7 @@ static void setillum(struct gspca_dev *gspca_dev);
static void setfreq(struct gspca_dev *gspca_dev);
static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
+[BRIGHTNESS] = {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -157,7 +159,7 @@ static const struct ctrl sd_ctrls[NCTRLS] = {
#define CONTRAST_MAX 127
.maximum = CONTRAST_MAX,
.step = 1,
- .default_value = 63,
+ .default_value = 20,
},
.set_control = setcontrast
},
@@ -737,7 +739,7 @@ static const u8 mi0360_sensor_init[][8] = {
{0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
- {0xd1, 0x5d, 0x2f, 0xf7, 0xB0, 0x00, 0x04, 0x10},
+ {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10},
{0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
{0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10},
@@ -1277,7 +1279,7 @@ static const u8 soi768_sensor_param1[][8] = {
/* global gain ? : 07 - change with 0x15 at the end */
{0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */
{0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x21, 0x2d, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x2d, 0x63, 0x03, 0x00, 0x00, 0x10},
/* exposure ? : 0200 - change with 0x1e at the end */
{}
};
@@ -1395,7 +1397,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
return;
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_r: buffer overflow");
+ pr_err("reg_r: buffer overflow\n");
return;
}
#endif
@@ -1408,7 +1410,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
500);
PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1432,7 +1434,7 @@ static void reg_w1(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w1 err %d", ret);
+ pr_err("reg_w1 err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1449,7 +1451,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
value, buffer[0], buffer[1]);
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_w: buffer overflow");
+ pr_err("reg_w: buffer overflow\n");
return;
}
#endif
@@ -1462,7 +1464,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1502,7 +1504,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
gspca_dev->usb_buf, 8,
500);
if (ret < 0) {
- err("i2c_w1 err %d", ret);
+ pr_err("i2c_w1 err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1527,7 +1529,7 @@ static void i2c_w8(struct gspca_dev *gspca_dev,
500);
msleep(2);
if (ret < 0) {
- err("i2c_w8 err %d", ret);
+ pr_err("i2c_w8 err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1591,7 +1593,7 @@ static void hv7131r_probe(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "Sensor HV7131R found");
return;
}
- warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x",
+ pr_warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x\n",
gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
gspca_dev->usb_buf[2]);
}
@@ -1710,7 +1712,7 @@ static void ov7648_probe(struct gspca_dev *gspca_dev)
sd->sensor = SENSOR_PO1030;
return;
}
- err("Unknown sensor %04x", val);
+ pr_err("Unknown sensor %04x\n", val);
}
/* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */
@@ -1748,7 +1750,7 @@ static void po2030n_probe(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "Sensor po2030n");
/* sd->sensor = SENSOR_PO2030N; */
} else {
- err("Unknown sensor ID %04x", val);
+ pr_err("Unknown sensor ID %04x\n", val);
}
}
@@ -2006,8 +2008,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
case SENSOR_OM6802:
expo = brightness << 2;
sd->exposure = setexposure(gspca_dev, expo);
- k2 = brightness >> 3;
- break;
+ return; /* Y offset already set */
}
reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
@@ -2019,13 +2020,13 @@ static void setcontrast(struct gspca_dev *gspca_dev)
u8 k2;
u8 contrast[6];
- k2 = sd->ctrls[CONTRAST].val * 0x30 / (CONTRAST_MAX + 1)
- + 0x10; /* 10..40 */
+ k2 = sd->ctrls[CONTRAST].val * 37 / (CONTRAST_MAX + 1)
+ + 37; /* 37..73 */
contrast[0] = (k2 + 1) / 2; /* red */
contrast[1] = 0;
contrast[2] = k2; /* green */
contrast[3] = 0;
- contrast[4] = (k2 + 1) / 5; /* blue */
+ contrast[4] = k2 / 5; /* blue */
contrast[5] = 0;
reg_w(gspca_dev, 0x84, contrast, sizeof contrast);
}
@@ -2386,7 +2387,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x01, 0x22);
msleep(100);
reg01 = SCL_SEL_OD | S_PDN_INV;
- reg17 &= MCK_SIZE_MASK;
+ reg17 &= ~MCK_SIZE_MASK;
reg17 |= 0x04; /* clock / 4 */
break;
}
@@ -2507,9 +2508,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
case SENSOR_HV7131R:
case SENSOR_MI0360:
- if (mode)
- reg01 |= SYS_SEL_48M; /* 320x240: clk 48Mhz */
- else
+ if (!mode)
reg01 &= ~SYS_SEL_48M; /* 640x480: clk 24Mhz */
reg17 &= ~MCK_SIZE_MASK;
reg17 |= 0x01; /* clock / 1 */
@@ -2532,6 +2531,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
if (!mode) { /* if 640x480 */
reg17 &= ~MCK_SIZE_MASK;
reg17 |= 0x04; /* clock / 4 */
+ } else {
+ reg01 &= ~SYS_SEL_48M; /* clk 24Mz */
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x02; /* clock / 2 */
}
break;
case SENSOR_OV7630:
diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/video/gspca/spca1528.c
index 76c006b2bc83..695673106e76 100644
--- a/drivers/media/video/gspca/spca1528.c
+++ b/drivers/media/video/gspca/spca1528.c
@@ -1,7 +1,7 @@
/*
* spca1528 subdriver
*
- * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr)
+ * Copyright (C) 2010-2011 Jean-Francois Moine (http://moinejf.free.fr)
*
* 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
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca1528"
#include "gspca.h"
@@ -171,7 +173,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index,
gspca_dev->usb_buf[0]);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -193,7 +195,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
value, index,
NULL, 0, 500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -217,21 +219,23 @@ static void reg_wb(struct gspca_dev *gspca_dev,
value, index,
gspca_dev->usb_buf, 1, 500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
static void wait_status_0(struct gspca_dev *gspca_dev)
{
- int i;
+ int i, w;
- i = 20;
+ i = 16;
+ w = 0;
do {
reg_r(gspca_dev, 0x21, 0x0000, 1);
if (gspca_dev->usb_buf[0] == 0)
return;
- msleep(30);
+ w += 15;
+ msleep(w);
} while (--i > 0);
PDEBUG(D_ERR, "wait_status_0 timeout");
gspca_dev->usb_err = -ETIME;
@@ -307,8 +311,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->color = COLOR_DEF;
sd->sharpness = SHARPNESS_DEF;
- gspca_dev->nbalt = 4; /* use alternate setting 3 */
-
return 0;
}
@@ -347,8 +349,12 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
reg_wb(gspca_dev, 0x25, 0x0000, 0x0004, mode);
reg_r(gspca_dev, 0x25, 0x0004, 1);
- reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06);
+ reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06); /* 420 */
reg_r(gspca_dev, 0x27, 0x0000, 1);
+
+/* not useful..
+ gspca_dev->alt = 4; * use alternate setting 3 */
+
return gspca_dev->usb_err;
}
@@ -361,8 +367,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
- /* the JPEG quality seems to be 82% */
- jpeg_set_qual(sd->jpeg_hdr, 82);
+ /* the JPEG quality shall be 85% */
+ jpeg_set_qual(sd->jpeg_hdr, 85);
/* set the controls */
setbrightness(gspca_dev);
@@ -377,7 +383,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* start the capture */
wait_status_0(gspca_dev);
- reg_w(gspca_dev, 0x31, 0x0000, 0x0004);
+ reg_w(gspca_dev, 0x31, 0x0000, 0x0004); /* start request */
wait_status_1(gspca_dev);
wait_status_0(gspca_dev);
msleep(200);
@@ -390,7 +396,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
{
/* stop the capture */
wait_status_0(gspca_dev);
- reg_w(gspca_dev, 0x31, 0x0000, 0x0000);
+ reg_w(gspca_dev, 0x31, 0x0000, 0x0000); /* stop request */
wait_status_1(gspca_dev);
wait_status_0(gspca_dev);
}
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
index 3e76951e3c19..bb82c94ece14 100644
--- a/drivers/media/video/gspca/spca500.c
+++ b/drivers/media/video/gspca/spca500.c
@@ -19,6 +19,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca500"
#include "gspca.h"
@@ -396,7 +398,7 @@ static int reg_w(struct gspca_dev *gspca_dev,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, NULL, 0, 500);
if (ret < 0)
- err("reg write: error %d", ret);
+ pr_err("reg write: error %d\n", ret);
return ret;
}
@@ -418,7 +420,7 @@ static int reg_r_12(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, length,
500); /* timeout */
if (ret < 0) {
- err("reg_r_12 err %d", ret);
+ pr_err("reg_r_12 err %d\n", ret);
return ret;
}
return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
index f7ef282cc600..7aaac72aee91 100644
--- a/drivers/media/video/gspca/spca501.c
+++ b/drivers/media/video/gspca/spca501.c
@@ -19,6 +19,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca501"
#include "gspca.h"
@@ -1852,7 +1854,7 @@ static int reg_write(struct usb_device *dev,
PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x",
req, index, value);
if (ret < 0)
- err("reg write: error %d", ret);
+ pr_err("reg write: error %d\n", ret);
return ret;
}
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
index e5bf865147d7..16722dc60394 100644
--- a/drivers/media/video/gspca/spca505.c
+++ b/drivers/media/video/gspca/spca505.c
@@ -19,6 +19,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca505"
#include "gspca.h"
@@ -578,7 +580,7 @@ static int reg_write(struct usb_device *dev,
PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d",
req, index, value, ret);
if (ret < 0)
- err("reg write: error %d", ret);
+ pr_err("reg write: error %d\n", ret);
return ret;
}
@@ -685,8 +687,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
return ret;
}
if (ret != 0x0101) {
- err("After vector read returns 0x%04x should be 0x0101",
- ret);
+ pr_err("After vector read returns 0x%04x should be 0x0101\n",
+ ret);
}
ret = reg_write(gspca_dev->dev, 0x06, 0x16, 0x0a);
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
index 9d0b46027b93..a44fe3d25965 100644
--- a/drivers/media/video/gspca/spca508.c
+++ b/drivers/media/video/gspca/spca508.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca508"
#include "gspca.h"
@@ -1275,7 +1277,7 @@ static int reg_write(struct usb_device *dev,
PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x",
index, value);
if (ret < 0)
- err("reg write: error %d", ret);
+ pr_err("reg write: error %d\n", ret);
return ret;
}
@@ -1297,7 +1299,7 @@ static int reg_read(struct gspca_dev *gspca_dev,
PDEBUG(D_USBI, "reg read i:%04x --> %02x",
index, gspca_dev->usb_buf[0]);
if (ret < 0) {
- err("reg_read err %d", ret);
+ pr_err("reg_read err %d\n", ret);
return ret;
}
return gspca_dev->usb_buf[0];
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
index e836e778dfb6..c82fd53cef95 100644
--- a/drivers/media/video/gspca/spca561.c
+++ b/drivers/media/video/gspca/spca561.c
@@ -20,6 +20,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca561"
#include <linux/input.h>
@@ -315,7 +317,7 @@ static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
value, index, NULL, 0, 500);
PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
if (ret < 0)
- err("reg write: error %d", ret);
+ pr_err("reg write: error %d\n", ret);
}
static void write_vector(struct gspca_dev *gspca_dev,
diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
index 5ba96aff2252..df805f798282 100644
--- a/drivers/media/video/gspca/sq905.c
+++ b/drivers/media/video/gspca/sq905.c
@@ -33,6 +33,8 @@
* drivers.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sq905"
#include <linux/workqueue.h>
@@ -123,8 +125,7 @@ static int sq905_command(struct gspca_dev *gspca_dev, u16 index)
SQ905_COMMAND, index, gspca_dev->usb_buf, 1,
SQ905_CMD_TIMEOUT);
if (ret < 0) {
- err("%s: usb_control_msg failed (%d)",
- __func__, ret);
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
return ret;
}
@@ -135,8 +136,7 @@ static int sq905_command(struct gspca_dev *gspca_dev, u16 index)
SQ905_PING, 0, gspca_dev->usb_buf, 1,
SQ905_CMD_TIMEOUT);
if (ret < 0) {
- err("%s: usb_control_msg failed 2 (%d)",
- __func__, ret);
+ pr_err("%s: usb_control_msg failed 2 (%d)\n", __func__, ret);
return ret;
}
@@ -158,7 +158,7 @@ static int sq905_ack_frame(struct gspca_dev *gspca_dev)
SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1,
SQ905_CMD_TIMEOUT);
if (ret < 0) {
- err("%s: usb_control_msg failed (%d)", __func__, ret);
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
return ret;
}
@@ -186,7 +186,7 @@ sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
if (need_lock)
mutex_unlock(&gspca_dev->usb_lock);
if (ret < 0) {
- err("%s: usb_control_msg failed (%d)", __func__, ret);
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
return ret;
}
ret = usb_bulk_msg(gspca_dev->dev,
@@ -195,8 +195,7 @@ sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
/* successful, it returns 0, otherwise negative */
if (ret < 0 || act_len != size) {
- err("bulk read fail (%d) len %d/%d",
- ret, act_len, size);
+ pr_err("bulk read fail (%d) len %d/%d\n", ret, act_len, size);
return -EIO;
}
return 0;
@@ -226,7 +225,7 @@ static void sq905_dostream(struct work_struct *work)
buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
if (!buffer) {
- err("Couldn't allocate USB buffer");
+ pr_err("Couldn't allocate USB buffer\n");
goto quit_stream;
}
diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
index 457563b7a71b..c2c056056e08 100644
--- a/drivers/media/video/gspca/sq905c.c
+++ b/drivers/media/video/gspca/sq905c.c
@@ -27,6 +27,8 @@
* and may contain code fragments from it.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sq905c"
#include <linux/workqueue.h>
@@ -95,8 +97,7 @@ static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index)
command, index, NULL, 0,
SQ905C_CMD_TIMEOUT);
if (ret < 0) {
- err("%s: usb_control_msg failed (%d)",
- __func__, ret);
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
return ret;
}
@@ -115,8 +116,7 @@ static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index,
command, index, gspca_dev->usb_buf, size,
SQ905C_CMD_TIMEOUT);
if (ret < 0) {
- err("%s: usb_control_msg failed (%d)",
- __func__, ret);
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
return ret;
}
@@ -146,7 +146,7 @@ static void sq905c_dostream(struct work_struct *work)
buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
if (!buffer) {
- err("Couldn't allocate USB buffer");
+ pr_err("Couldn't allocate USB buffer\n");
goto quit_stream;
}
diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c
index 8215d5dcd456..e4255b4905e7 100644
--- a/drivers/media/video/gspca/sq930x.c
+++ b/drivers/media/video/gspca/sq930x.c
@@ -20,6 +20,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sq930x"
#include "gspca.h"
@@ -468,7 +470,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
value, 0, gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- err("reg_r %04x failed %d", value, ret);
+ pr_err("reg_r %04x failed %d\n", value, ret);
gspca_dev->usb_err = ret;
}
}
@@ -488,7 +490,7 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
500);
msleep(30);
if (ret < 0) {
- err("reg_w %04x %04x failed %d", value, index, ret);
+ pr_err("reg_w %04x %04x failed %d\n", value, index, ret);
gspca_dev->usb_err = ret;
}
}
@@ -511,7 +513,7 @@ static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index,
1000);
msleep(30);
if (ret < 0) {
- err("reg_wb %04x %04x failed %d", value, index, ret);
+ pr_err("reg_wb %04x %04x failed %d\n", value, index, ret);
gspca_dev->usb_err = ret;
}
}
@@ -556,7 +558,7 @@ static void i2c_write(struct sd *sd,
gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
500);
if (ret < 0) {
- err("i2c_write failed %d", ret);
+ pr_err("i2c_write failed %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -575,7 +577,7 @@ static void ucbus_write(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if ((batchsize - 1) * 3 > USB_BUF_SZ) {
- err("Bug: usb_buf overflow");
+ pr_err("Bug: usb_buf overflow\n");
gspca_dev->usb_err = -ENOMEM;
return;
}
@@ -612,7 +614,7 @@ static void ucbus_write(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
500);
if (ret < 0) {
- err("ucbus_write failed %d", ret);
+ pr_err("ucbus_write failed %d\n", ret);
gspca_dev->usb_err = ret;
return;
}
@@ -688,7 +690,7 @@ static void cmos_probe(struct gspca_dev *gspca_dev)
break;
}
if (i >= ARRAY_SIZE(probe_order)) {
- err("Unknown sensor");
+ pr_err("Unknown sensor\n");
gspca_dev->usb_err = -EINVAL;
return;
}
@@ -696,7 +698,8 @@ static void cmos_probe(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_OV7660:
case SENSOR_OV9630:
- err("Sensor %s not yet treated", sensor_tb[sd->sensor].name);
+ pr_err("Sensor %s not yet treated\n",
+ sensor_tb[sd->sensor].name);
gspca_dev->usb_err = -EINVAL;
break;
}
@@ -1091,7 +1094,7 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
gspca_dev->cam.bulk_nurbs = 1;
ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);
if (ret < 0)
- err("sd_dq_callback() err %d", ret);
+ pr_err("sd_dq_callback() err %d\n", ret);
/* wait a little time, otherwise the webcam crashes */
msleep(100);
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
index 763747700f10..42a7a28a6c8b 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/video/gspca/stk014.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "stk014"
#include "gspca.h"
@@ -137,7 +139,7 @@ static u8 reg_r(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
return 0;
}
@@ -162,7 +164,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
0,
500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -192,7 +194,7 @@ static void rcv_val(struct gspca_dev *gspca_dev,
&alen,
500); /* timeout in milliseconds */
if (ret < 0) {
- err("rcv_val err %d", ret);
+ pr_err("rcv_val err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -235,7 +237,7 @@ static void snd_val(struct gspca_dev *gspca_dev,
&alen,
500); /* timeout in milliseconds */
if (ret < 0) {
- err("snd_val err %d", ret);
+ pr_err("snd_val err %d\n", ret);
gspca_dev->usb_err = ret;
} else {
if (ads == 0x003f08) {
@@ -315,7 +317,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
ret = reg_r(gspca_dev, 0x0740);
if (gspca_dev->usb_err >= 0) {
if (ret != 0xff) {
- err("init reg: 0x%02x", ret);
+ pr_err("init reg: 0x%02x\n", ret);
gspca_dev->usb_err = -EIO;
}
}
@@ -349,8 +351,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
gspca_dev->iface,
gspca_dev->alt);
if (ret < 0) {
- err("set intf %d %d failed",
- gspca_dev->iface, gspca_dev->alt);
+ pr_err("set intf %d %d failed\n",
+ gspca_dev->iface, gspca_dev->alt);
gspca_dev->usb_err = ret;
goto out;
}
diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c
index e2ef41cf72d7..4dcc7e37f9fd 100644
--- a/drivers/media/video/gspca/stv0680.c
+++ b/drivers/media/video/gspca/stv0680.c
@@ -27,6 +27,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "stv0680"
#include "gspca.h"
@@ -79,7 +81,7 @@ static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
val, 0, gspca_dev->usb_buf, size, 500);
if ((ret < 0) && (req != 0x0a))
- err("usb_control_msg error %i, request = 0x%x, error = %i",
+ pr_err("usb_control_msg error %i, request = 0x%x, error = %i\n",
set, req, ret);
return ret;
@@ -236,7 +238,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 ||
gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) {
- err("Could not get descriptor 0100.");
+ pr_err("Could not get descriptor 0100\n");
return stv0680_handle_error(gspca_dev, -EIO);
}
diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile
index 2f3c3a606ce4..5b318faf9aa8 100644
--- a/drivers/media/video/gspca/stv06xx/Makefile
+++ b/drivers/media/video/gspca/stv06xx/Makefile
@@ -6,5 +6,5 @@ gspca_stv06xx-objs := stv06xx.o \
stv06xx_pb0100.o \
stv06xx_st6422.o
-EXTRA_CFLAGS += -Idrivers/media/video/gspca
+ccflags-y += -Idrivers/media/video/gspca
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
index abf1658fa33e..b1fca7db1015 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -27,6 +27,8 @@
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/input.h>
#include "stv06xx_sensor.h"
@@ -189,7 +191,7 @@ int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value)
0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH,
STV06XX_URB_MSG_TIMEOUT);
if (err < 0) {
- err("I2C: Read error writing address: %d", err);
+ pr_err("I2C: Read error writing address: %d\n", err);
return err;
}
@@ -213,14 +215,14 @@ static void stv06xx_dump_bridge(struct sd *sd)
int i;
u8 data, buf;
- info("Dumping all stv06xx bridge registers");
+ pr_info("Dumping all stv06xx bridge registers\n");
for (i = 0x1400; i < 0x160f; i++) {
stv06xx_read_bridge(sd, i, &data);
- info("Read 0x%x from address 0x%x", data, i);
+ pr_info("Read 0x%x from address 0x%x\n", data, i);
}
- info("Testing stv06xx bridge registers for writability");
+ pr_info("Testing stv06xx bridge registers for writability\n");
for (i = 0x1400; i < 0x160f; i++) {
stv06xx_read_bridge(sd, i, &data);
buf = data;
@@ -228,12 +230,12 @@ static void stv06xx_dump_bridge(struct sd *sd)
stv06xx_write_bridge(sd, i, 0xff);
stv06xx_read_bridge(sd, i, &data);
if (data == 0xff)
- info("Register 0x%x is read/write", i);
+ pr_info("Register 0x%x is read/write\n", i);
else if (data != buf)
- info("Register 0x%x is read/write,"
- " but only partially", i);
+ pr_info("Register 0x%x is read/write, but only partially\n",
+ i);
else
- info("Register 0x%x is read-only", i);
+ pr_info("Register 0x%x is read-only\n", i);
stv06xx_write_bridge(sd, i, buf);
}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h
index e0f63c51f40d..d270a5981afe 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.h
@@ -37,6 +37,8 @@
#define STV_ISOC_ENDPOINT_ADDR 0x81
+#define STV_R 0x0509
+
#define STV_REG23 0x0423
/* Control registers of the STV0600 ASIC */
@@ -61,7 +63,9 @@
/* Refers to the CIF 352x288 and QCIF 176x144 */
/* 1: 288 lines, 2: 144 lines */
-#define STV_Y_CTRL 0x15c3
+#define STV_Y_CTRL 0x15c3
+
+#define STV_RESET 0x1620
/* 0xa: 352 columns, 0x6: 176 columns */
#define STV_X_CTRL 0x1680
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
index b8156855f2b7..a8698b7a7566 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
@@ -28,6 +28,8 @@
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "stv06xx_hdcs.h"
static const struct ctrl hdcs1x00_ctrl[] = {
@@ -428,7 +430,7 @@ static int hdcs_probe_1x00(struct sd *sd)
if (ret < 0 || sensor != 0x08)
return -ENODEV;
- info("HDCS-1000/1100 sensor detected");
+ pr_info("HDCS-1000/1100 sensor detected\n");
sd->gspca_dev.cam.cam_mode = hdcs1x00_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode);
@@ -487,7 +489,7 @@ static int hdcs_probe_1020(struct sd *sd)
if (ret < 0 || sensor != 0x10)
return -ENODEV;
- info("HDCS-1020 sensor detected");
+ pr_info("HDCS-1020 sensor detected\n");
sd->gspca_dev.cam.cam_mode = hdcs1020_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode);
@@ -601,11 +603,11 @@ static int hdcs_dump(struct sd *sd)
{
u16 reg, val;
- info("Dumping sensor registers:");
+ pr_info("Dumping sensor registers:\n");
for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) {
stv06xx_read_sensor(sd, reg, &val);
- info("reg 0x%02x = 0x%02x", reg, val);
+ pr_info("reg 0x%02x = 0x%02x\n", reg, val);
}
return 0;
}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
index 75a5b9c2f15f..26f14fc4a135 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
@@ -44,6 +44,8 @@
* PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "stv06xx_pb0100.h"
static const struct ctrl pb0100_ctrl[] = {
@@ -190,7 +192,7 @@ static int pb0100_probe(struct sd *sd)
if (!sensor_settings)
return -ENOMEM;
- info("Photobit pb0100 sensor detected");
+ pr_info("Photobit pb0100 sensor detected\n");
sd->gspca_dev.cam.cam_mode = pb0100_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode);
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
index 8a456de4970a..9940e035b3ab 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
@@ -26,6 +26,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "stv06xx_st6422.h"
/* controls */
@@ -136,7 +138,7 @@ static int st6422_probe(struct sd *sd)
if (sd->bridge != BRIDGE_ST6422)
return -ENODEV;
- info("st6422 sensor detected");
+ pr_info("st6422 sensor detected\n");
sensor_settings = kmalloc(sizeof *sensor_settings, GFP_KERNEL);
if (!sensor_settings)
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
index f8398434c328..a5c69d9ebdd4 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
@@ -27,6 +27,8 @@
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "stv06xx_vv6410.h"
static struct v4l2_pix_format vv6410_mode[] = {
@@ -112,7 +114,7 @@ static int vv6410_probe(struct sd *sd)
return -ENODEV;
if (data == 0x19) {
- info("vv6410 sensor detected");
+ pr_info("vv6410 sensor detected\n");
sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32),
GFP_KERNEL);
@@ -138,18 +140,7 @@ static int vv6410_init(struct sd *sd)
s32 *sensor_settings = sd->sensor_priv;
for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) {
- /* if NULL then len contains single value */
- if (stv_bridge_init[i].data == NULL) {
- err = stv06xx_write_bridge(sd,
- stv_bridge_init[i].start,
- stv_bridge_init[i].len);
- } else {
- int j;
- for (j = 0; j < stv_bridge_init[i].len; j++)
- err = stv06xx_write_bridge(sd,
- stv_bridge_init[i].start + j,
- stv_bridge_init[i].data[j]);
- }
+ stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data);
}
if (err < 0)
@@ -183,15 +174,6 @@ static int vv6410_start(struct sd *sd)
struct cam *cam = &sd->gspca_dev.cam;
u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
- if (priv & VV6410_CROP_TO_QVGA) {
- PDEBUG(D_CONF, "Cropping to QVGA");
- stv06xx_write_sensor(sd, VV6410_XENDH, 320 - 1);
- stv06xx_write_sensor(sd, VV6410_YENDH, 240 - 1);
- } else {
- stv06xx_write_sensor(sd, VV6410_XENDH, 360 - 1);
- stv06xx_write_sensor(sd, VV6410_YENDH, 294 - 1);
- }
-
if (priv & VV6410_SUBSAMPLE) {
PDEBUG(D_CONF, "Enabling subsampling");
stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);
@@ -201,8 +183,8 @@ static int vv6410_start(struct sd *sd)
} else {
stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00);
- stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
}
/* Turn on LED */
@@ -242,11 +224,11 @@ static int vv6410_dump(struct sd *sd)
u8 i;
int err = 0;
- info("Dumping all vv6410 sensor registers");
+ pr_info("Dumping all vv6410 sensor registers\n");
for (i = 0; i < 0xff && !err; i++) {
u16 data;
err = stv06xx_read_sensor(sd, i, &data);
- info("Register 0x%x contained 0x%x", i, data);
+ pr_info("Register 0x%x contained 0x%x\n", i, data);
}
return (err < 0) ? err : 0;
}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
index 7fe3587f5f71..a25b8873f2e6 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
@@ -211,49 +211,49 @@ const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
/* If NULL, only single value to write, stored in len */
struct stv_init {
- const u8 *data;
- u16 start;
- u8 len;
-};
-
-static const u8 x1500[] = { /* 0x1500 - 0x150f */
- 0x0b, 0xa7, 0xb7, 0x00, 0x00
-};
-
-static const u8 x1536[] = { /* 0x1536 - 0x153b */
- 0x02, 0x00, 0x60, 0x01, 0x20, 0x01
+ u16 addr;
+ u8 data;
};
static const struct stv_init stv_bridge_init[] = {
/* This reg is written twice. Some kind of reset? */
- {NULL, 0x1620, 0x80},
- {NULL, 0x1620, 0x00},
- {NULL, 0x1443, 0x00},
- {NULL, 0x1423, 0x04},
- {x1500, 0x1500, ARRAY_SIZE(x1500)},
- {x1536, 0x1536, ARRAY_SIZE(x1536)},
+ {STV_RESET, 0x80},
+ {STV_RESET, 0x00},
+ {STV_SCAN_RATE, 0x00},
+ {STV_I2C_FLUSH, 0x04},
+ {STV_REG00, 0x0b},
+ {STV_REG01, 0xa7},
+ {STV_REG02, 0xb7},
+ {STV_REG03, 0x00},
+ {STV_REG04, 0x00},
+ {0x1536, 0x02},
+ {0x1537, 0x00},
+ {0x1538, 0x60},
+ {0x1539, 0x01},
+ {0x153a, 0x20},
+ {0x153b, 0x01},
};
static const u8 vv6410_sensor_init[][2] = {
/* Setup registers */
- {VV6410_SETUP0, VV6410_SOFT_RESET},
- {VV6410_SETUP0, VV6410_LOW_POWER_MODE},
+ {VV6410_SETUP0, VV6410_SOFT_RESET},
+ {VV6410_SETUP0, VV6410_LOW_POWER_MODE},
/* Use shuffled read-out mode */
- {VV6410_SETUP1, BIT(6)},
- /* All modes to 1 */
- {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)},
- {VV6410_PINMAPPING, 0x00},
+ {VV6410_SETUP1, BIT(6)},
+ /* All modes to 1, FST, Fast QCK, Free running QCK, Free running LST, FST will qualify visible pixels */
+ {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)},
+ {VV6410_PINMAPPING, 0x00},
/* Pre-clock generator divide off */
- {VV6410_DATAFORMAT, BIT(7) | BIT(0)},
+ {VV6410_DATAFORMAT, BIT(7) | BIT(0)},
- {VV6410_CLKDIV, VV6410_CLK_DIV_2},
+ {VV6410_CLKDIV, VV6410_CLK_DIV_2},
/* System registers */
/* Enable voltage doubler */
- {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
- {VV6410_AT0, 0x00},
+ {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+ {VV6410_AT0, 0x00},
/* Power up audio, differential */
- {VV6410_AT1, BIT(4)|BIT(0)},
+ {VV6410_AT1, BIT(4) | BIT(0)},
};
#endif
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index b089c0d3ee9f..c8909772435a 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sunplus"
#include "gspca.h"
@@ -247,7 +249,6 @@ static const struct cmd spca504A_clicksmart420_init_data[] = {
{0x30, 0x0004, 0x000a},
{0xb0, 0x0001, 0x0000},
-
{0xa1, 0x0080, 0x0001},
{0x30, 0x0049, 0x0000},
{0x30, 0x0060, 0x0005},
@@ -256,8 +257,6 @@ static const struct cmd spca504A_clicksmart420_init_data[] = {
{0x00, 0x0000, 0x2000},
{0x00, 0x0013, 0x2301},
{0x00, 0x0003, 0x2000},
- {0x00, 0x0000, 0x2000},
-
};
/* clicksmart 420 open data ? */
@@ -328,7 +327,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_r: buffer overflow");
+ pr_err("reg_r: buffer overflow\n");
return;
}
#endif
@@ -343,7 +342,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
len ? gspca_dev->usb_buf : NULL, len,
500);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -368,7 +367,7 @@ static void reg_w_1(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w_1 err %d", ret);
+ pr_err("reg_w_1 err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -388,7 +387,7 @@ static void reg_w_riv(struct gspca_dev *gspca_dev,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, NULL, 0, 500);
if (ret < 0) {
- err("reg_w_riv err %d", ret);
+ pr_err("reg_w_riv err %d\n", ret);
gspca_dev->usb_err = ret;
return;
}
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index 7e762d551099..90f0877eb599 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -26,6 +26,8 @@
* Costantino Leandro
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "t613"
#include <linux/slab.h>
@@ -572,7 +574,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
tmpbuf = kmemdup(buffer, len, GFP_KERNEL);
if (!tmpbuf) {
- err("Out of memory");
+ pr_err("Out of memory\n");
return;
}
usb_control_msg(gspca_dev->dev,
@@ -598,7 +600,7 @@ static void reg_w_ixbuf(struct gspca_dev *gspca_dev,
} else {
p = tmpbuf = kmalloc(len * 2, GFP_KERNEL);
if (!tmpbuf) {
- err("Out of memory");
+ pr_err("Out of memory\n");
return;
}
}
@@ -652,7 +654,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev)
}
byte = reg_r(gspca_dev, 0x0063);
if (byte != 0x17) {
- err("Bad sensor reset %02x", byte);
+ pr_err("Bad sensor reset %02x\n", byte);
/* continue? */
}
@@ -890,7 +892,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
sd->sensor = SENSOR_OM6802;
break;
default:
- err("unknown sensor %04x", sensor_id);
+ pr_err("unknown sensor %04x\n", sensor_id);
return -EINVAL;
}
@@ -905,7 +907,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
break; /* OK */
}
if (i < 0) {
- err("Bad sensor reset %02x", test_byte);
+ pr_err("Bad sensor reset %02x\n", test_byte);
return -EIO;
}
reg_w_buf(gspca_dev, n2, sizeof n2);
@@ -1387,7 +1389,7 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
return 0;
case V4L2_CID_EFFECTS:
if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) {
- strncpy((char *) menu->name,
+ strlcpy((char *) menu->name,
effects_control[menu->index],
sizeof menu->name);
return 0;
diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c
new file mode 100644
index 000000000000..29596c59837f
--- /dev/null
+++ b/drivers/media/video/gspca/topro.c
@@ -0,0 +1,4989 @@
+/*
+ * Topro TP6800/6810 webcam driver.
+ *
+ * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr)
+ * Copyright (C) 2009 Anders Blomdell (anders.blomdell@control.lth.se)
+ * Copyright (C) 2008 Thomas Champagne (lafeuil@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "gspca.h"
+
+MODULE_DESCRIPTION("Topro TP6800/6810 gspca webcam driver");
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+ "Anders Blomdell <anders.blomdell@control.lth.se>");
+MODULE_LICENSE("GPL");
+
+static int force_sensor = -1;
+
+/* JPEG header */
+static const u8 jpeg_head[] = {
+ 0xff, 0xd8, /* jpeg */
+
+/* quantization table quality 50% */
+ 0xff, 0xdb, 0x00, 0x84, /* DQT */
+0,
+#define JPEG_QT0_OFFSET 7
+ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+1,
+#define JPEG_QT1_OFFSET 72
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+
+ /* Define Huffman table (thanks to Thomas Kaiser) */
+ 0xff, 0xc4, 0x01, 0x5e,
+ 0x00, 0x00, 0x02, 0x03,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+ 0x07, 0x05, 0x04, 0x06, 0x01, 0x00, 0x00, 0x57,
+ 0x01, 0x02, 0x03, 0x00, 0x11, 0x04, 0x12, 0x21,
+ 0x31, 0x13, 0x41, 0x51, 0x61, 0x05, 0x22, 0x32,
+ 0x14, 0x71, 0x81, 0x91, 0x15, 0x23, 0x42, 0x52,
+ 0x62, 0xa1, 0xb1, 0x06, 0x33, 0x72, 0xc1, 0xd1,
+ 0x24, 0x43, 0x53, 0x82, 0x16, 0x34, 0x92, 0xa2,
+ 0xe1, 0xf1, 0xf0, 0x07, 0x08, 0x17, 0x18, 0x25,
+ 0x26, 0x27, 0x28, 0x35, 0x36, 0x37, 0x38, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x73,
+ 0x74, 0x75, 0x76, 0x77, 0x78, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2,
+ 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5,
+ 0xe6, 0xe7, 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
+ 0xf7, 0xf8, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09, 0x11, 0x00, 0x02,
+ 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+ 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, 0x00, 0x01,
+ 0x11, 0x02, 0x21, 0x03, 0x12, 0x31, 0x41, 0x13,
+ 0x22, 0x51, 0x61, 0x04, 0x32, 0x71, 0x05, 0x14,
+ 0x23, 0x42, 0x33, 0x52, 0x81, 0x91, 0xa1, 0xb1,
+ 0xf0, 0x06, 0x15, 0xc1, 0xd1, 0xe1, 0x24, 0x43,
+ 0x62, 0xf1, 0x16, 0x25, 0x34, 0x53, 0x72, 0x82,
+ 0x92, 0x07, 0x08, 0x17, 0x18, 0x26, 0x27, 0x28,
+ 0x35, 0x36, 0x37, 0x38, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+ 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */
+ 0x08, /* data precision */
+#define JPEG_HEIGHT_OFFSET 493
+ 0x01, 0xe0, /* height */
+ 0x02, 0x80, /* width */
+ 0x03, /* component number */
+ 0x01,
+ 0x21, /* samples Y = jpeg 422 */
+ 0x00, /* quant Y */
+ 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */
+ 0x03, 0x11, 0x01,
+
+ 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */
+ 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+#define JPEG_HDR_SZ 521
+};
+
+enum e_ctrl {
+ EXPOSURE,
+ QUALITY,
+ SHARPNESS,
+ RGAIN,
+ GAIN,
+ BGAIN,
+ GAMMA,
+ AUTOGAIN,
+ NCTRLS /* number of controls */
+};
+
+#define AUTOGAIN_DEF 1
+
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ u8 framerate;
+ u8 quality; /* webcam current JPEG quality (0..16) */
+ s8 ag_cnt; /* autogain / start counter for tp6810 */
+#define AG_CNT_START 13 /* check gain every N frames */
+
+ u8 bridge;
+ u8 sensor;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+enum bridges {
+ BRIDGE_TP6800,
+ BRIDGE_TP6810,
+};
+
+enum sensors {
+ SENSOR_CX0342,
+ SENSOR_SOI763A, /* ~= ov7630 / ov7648 */
+ NSENSORS
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG}
+};
+
+/*
+ * JPEG quality
+ * index: webcam compression
+ * value: JPEG quality in %
+ */
+static const u8 jpeg_q[17] = {
+ 88, 77, 67, 57, 55, 55, 45, 45, 36, 36, 30, 30, 26, 26, 22, 22, 94
+};
+
+#define BULK_OUT_SIZE 0x20
+#if BULK_OUT_SIZE > USB_BUF_SZ
+#error "USB buffer too small"
+#endif
+
+static const u8 rates[] = {30, 20, 15, 10, 7, 5};
+static const struct framerates framerates[] = {
+ {
+ .rates = rates,
+ .nrates = ARRAY_SIZE(rates)
+ },
+ {
+ .rates = rates,
+ .nrates = ARRAY_SIZE(rates)
+ }
+};
+static const u8 rates_6810[] = {30, 15, 10, 7, 5};
+static const struct framerates framerates_6810[] = {
+ {
+ .rates = rates_6810,
+ .nrates = ARRAY_SIZE(rates_6810)
+ },
+ {
+ .rates = rates_6810,
+ .nrates = ARRAY_SIZE(rates_6810)
+ }
+};
+
+/*
+ * webcam quality in %
+ * the last value is the ultra fine quality
+ */
+
+/* TP6800 register offsets */
+#define TP6800_R10_SIF_TYPE 0x10
+#define TP6800_R11_SIF_CONTROL 0x11
+#define TP6800_R12_SIF_ADDR_S 0x12
+#define TP6800_R13_SIF_TX_DATA 0x13
+#define TP6800_R14_SIF_RX_DATA 0x14
+#define TP6800_R15_GPIO_PU 0x15
+#define TP6800_R16_GPIO_PD 0x16
+#define TP6800_R17_GPIO_IO 0x17
+#define TP6800_R18_GPIO_DATA 0x18
+#define TP6800_R19_SIF_ADDR_S2 0x19
+#define TP6800_R1A_SIF_TX_DATA2 0x1a
+#define TP6800_R1B_SIF_RX_DATA2 0x1b
+#define TP6800_R21_ENDP_1_CTL 0x21
+#define TP6800_R2F_TIMING_CFG 0x2f
+#define TP6800_R30_SENSOR_CFG 0x30
+#define TP6800_R31_PIXEL_START 0x31
+#define TP6800_R32_PIXEL_END_L 0x32
+#define TP6800_R33_PIXEL_END_H 0x33
+#define TP6800_R34_LINE_START 0x34
+#define TP6800_R35_LINE_END_L 0x35
+#define TP6800_R36_LINE_END_H 0x36
+#define TP6800_R37_FRONT_DARK_ST 0x37
+#define TP6800_R38_FRONT_DARK_END 0x38
+#define TP6800_R39_REAR_DARK_ST_L 0x39
+#define TP6800_R3A_REAR_DARK_ST_H 0x3a
+#define TP6800_R3B_REAR_DARK_END_L 0x3b
+#define TP6800_R3C_REAR_DARK_END_H 0x3c
+#define TP6800_R3D_HORIZ_DARK_LINE_L 0x3d
+#define TP6800_R3E_HORIZ_DARK_LINE_H 0x3e
+#define TP6800_R3F_FRAME_RATE 0x3f
+#define TP6800_R50 0x50
+#define TP6800_R51 0x51
+#define TP6800_R52 0x52
+#define TP6800_R53 0x53
+#define TP6800_R54_DARK_CFG 0x54
+#define TP6800_R55_GAMMA_R 0x55
+#define TP6800_R56_GAMMA_G 0x56
+#define TP6800_R57_GAMMA_B 0x57
+#define TP6800_R5C_EDGE_THRLD 0x5c
+#define TP6800_R5D_DEMOSAIC_CFG 0x5d
+#define TP6800_R78_FORMAT 0x78
+#define TP6800_R79_QUALITY 0x79
+#define TP6800_R7A_BLK_THRLD 0x7a
+
+/* CX0342 register offsets */
+
+#define CX0342_SENSOR_ID 0x00
+#define CX0342_VERSION_NO 0x01
+#define CX0342_ORG_X_L 0x02
+#define CX0342_ORG_X_H 0x03
+#define CX0342_ORG_Y_L 0x04
+#define CX0342_ORG_Y_H 0x05
+#define CX0342_STOP_X_L 0x06
+#define CX0342_STOP_X_H 0x07
+#define CX0342_STOP_Y_L 0x08
+#define CX0342_STOP_Y_H 0x09
+#define CX0342_FRAME_WIDTH_L 0x0a
+#define CX0342_FRAME_WIDTH_H 0x0b
+#define CX0342_FRAME_HEIGH_L 0x0c
+#define CX0342_FRAME_HEIGH_H 0x0d
+#define CX0342_EXPO_LINE_L 0x10
+#define CX0342_EXPO_LINE_H 0x11
+#define CX0342_EXPO_CLK_L 0x12
+#define CX0342_EXPO_CLK_H 0x13
+#define CX0342_RAW_GRGAIN_L 0x14
+#define CX0342_RAW_GRGAIN_H 0x15
+#define CX0342_RAW_GBGAIN_L 0x16
+#define CX0342_RAW_GBGAIN_H 0x17
+#define CX0342_RAW_RGAIN_L 0x18
+#define CX0342_RAW_RGAIN_H 0x19
+#define CX0342_RAW_BGAIN_L 0x1a
+#define CX0342_RAW_BGAIN_H 0x1b
+#define CX0342_GLOBAL_GAIN 0x1c
+#define CX0342_SYS_CTRL_0 0x20
+#define CX0342_SYS_CTRL_1 0x21
+#define CX0342_SYS_CTRL_2 0x22
+#define CX0342_BYPASS_MODE 0x23
+#define CX0342_SYS_CTRL_3 0x24
+#define CX0342_TIMING_EN 0x25
+#define CX0342_OUTPUT_CTRL 0x26
+#define CX0342_AUTO_ADC_CALIB 0x27
+#define CX0342_SYS_CTRL_4 0x28
+#define CX0342_ADCGN 0x30
+#define CX0342_SLPCR 0x31
+#define CX0342_SLPFN_LO 0x32
+#define CX0342_ADC_CTL 0x33
+#define CX0342_LVRST_BLBIAS 0x34
+#define CX0342_VTHSEL 0x35
+#define CX0342_RAMP_RIV 0x36
+#define CX0342_LDOSEL 0x37
+#define CX0342_CLOCK_GEN 0x40
+#define CX0342_SOFT_RESET 0x41
+#define CX0342_PLL 0x42
+#define CX0342_DR_ENH_PULSE_OFFSET_L 0x43
+#define CX0342_DR_ENH_PULSE_OFFSET_H 0x44
+#define CX0342_DR_ENH_PULSE_POS_L 0x45
+#define CX0342_DR_ENH_PULSE_POS_H 0x46
+#define CX0342_DR_ENH_PULSE_WIDTH 0x47
+#define CX0342_AS_CURRENT_CNT_L 0x48
+#define CX0342_AS_CURRENT_CNT_H 0x49
+#define CX0342_AS_PREVIOUS_CNT_L 0x4a
+#define CX0342_AS_PREVIOUS_CNT_H 0x4b
+#define CX0342_SPV_VALUE_L 0x4c
+#define CX0342_SPV_VALUE_H 0x4d
+#define CX0342_GPXLTHD_L 0x50
+#define CX0342_GPXLTHD_H 0x51
+#define CX0342_RBPXLTHD_L 0x52
+#define CX0342_RBPXLTHD_H 0x53
+#define CX0342_PLANETHD_L 0x54
+#define CX0342_PLANETHD_H 0x55
+#define CX0342_ROWDARK_TH 0x56
+#define CX0342_ROWDARK_TOL 0x57
+#define CX0342_RB_GAP_L 0x58
+#define CX0342_RB_GAP_H 0x59
+#define CX0342_G_GAP_L 0x5a
+#define CX0342_G_GAP_H 0x5b
+#define CX0342_AUTO_ROW_DARK 0x60
+#define CX0342_MANUAL_DARK_VALUE 0x61
+#define CX0342_GB_DARK_OFFSET 0x62
+#define CX0342_GR_DARK_OFFSET 0x63
+#define CX0342_RED_DARK_OFFSET 0x64
+#define CX0342_BLUE_DARK_OFFSET 0x65
+#define CX0342_DATA_SCALING_MULTI 0x66
+#define CX0342_AUTOD_Q_FRAME 0x67
+#define CX0342_AUTOD_ALLOW_VARI 0x68
+#define CX0342_AUTO_DARK_VALUE_L 0x69
+#define CX0342_AUTO_DARK_VALUE_H 0x6a
+#define CX0342_IO_CTRL_0 0x70
+#define CX0342_IO_CTRL_1 0x71
+#define CX0342_IO_CTRL_2 0x72
+#define CX0342_IDLE_CTRL 0x73
+#define CX0342_TEST_MODE 0x74
+#define CX0342_FRAME_FIX_DATA_TEST 0x75
+#define CX0342_FRAME_CNT_TEST 0x76
+#define CX0342_RST_OVERFLOW_L 0x80
+#define CX0342_RST_OVERFLOW_H 0x81
+#define CX0342_RST_UNDERFLOW_L 0x82
+#define CX0342_RST_UNDERFLOW_H 0x83
+#define CX0342_DATA_OVERFLOW_L 0x84
+#define CX0342_DATA_OVERFLOW_H 0x85
+#define CX0342_DATA_UNDERFLOW_L 0x86
+#define CX0342_DATA_UNDERFLOW_H 0x87
+#define CX0342_CHANNEL_0_0_L_irst 0x90
+#define CX0342_CHANNEL_0_0_H_irst 0x91
+#define CX0342_CHANNEL_0_1_L_irst 0x92
+#define CX0342_CHANNEL_0_1_H_irst 0x93
+#define CX0342_CHANNEL_0_2_L_irst 0x94
+#define CX0342_CHANNEL_0_2_H_irst 0x95
+#define CX0342_CHANNEL_0_3_L_irst 0x96
+#define CX0342_CHANNEL_0_3_H_irst 0x97
+#define CX0342_CHANNEL_0_4_L_irst 0x98
+#define CX0342_CHANNEL_0_4_H_irst 0x99
+#define CX0342_CHANNEL_0_5_L_irst 0x9a
+#define CX0342_CHANNEL_0_5_H_irst 0x9b
+#define CX0342_CHANNEL_0_6_L_irst 0x9c
+#define CX0342_CHANNEL_0_6_H_irst 0x9d
+#define CX0342_CHANNEL_0_7_L_irst 0x9e
+#define CX0342_CHANNEL_0_7_H_irst 0x9f
+#define CX0342_CHANNEL_1_0_L_itx 0xa0
+#define CX0342_CHANNEL_1_0_H_itx 0xa1
+#define CX0342_CHANNEL_1_1_L_itx 0xa2
+#define CX0342_CHANNEL_1_1_H_itx 0xa3
+#define CX0342_CHANNEL_1_2_L_itx 0xa4
+#define CX0342_CHANNEL_1_2_H_itx 0xa5
+#define CX0342_CHANNEL_1_3_L_itx 0xa6
+#define CX0342_CHANNEL_1_3_H_itx 0xa7
+#define CX0342_CHANNEL_1_4_L_itx 0xa8
+#define CX0342_CHANNEL_1_4_H_itx 0xa9
+#define CX0342_CHANNEL_1_5_L_itx 0xaa
+#define CX0342_CHANNEL_1_5_H_itx 0xab
+#define CX0342_CHANNEL_1_6_L_itx 0xac
+#define CX0342_CHANNEL_1_6_H_itx 0xad
+#define CX0342_CHANNEL_1_7_L_itx 0xae
+#define CX0342_CHANNEL_1_7_H_itx 0xaf
+#define CX0342_CHANNEL_2_0_L_iwl 0xb0
+#define CX0342_CHANNEL_2_0_H_iwl 0xb1
+#define CX0342_CHANNEL_2_1_L_iwl 0xb2
+#define CX0342_CHANNEL_2_1_H_iwl 0xb3
+#define CX0342_CHANNEL_2_2_L_iwl 0xb4
+#define CX0342_CHANNEL_2_2_H_iwl 0xb5
+#define CX0342_CHANNEL_2_3_L_iwl 0xb6
+#define CX0342_CHANNEL_2_3_H_iwl 0xb7
+#define CX0342_CHANNEL_2_4_L_iwl 0xb8
+#define CX0342_CHANNEL_2_4_H_iwl 0xb9
+#define CX0342_CHANNEL_2_5_L_iwl 0xba
+#define CX0342_CHANNEL_2_5_H_iwl 0xbb
+#define CX0342_CHANNEL_2_6_L_iwl 0xbc
+#define CX0342_CHANNEL_2_6_H_iwl 0xbd
+#define CX0342_CHANNEL_2_7_L_iwl 0xbe
+#define CX0342_CHANNEL_2_7_H_iwl 0xbf
+#define CX0342_CHANNEL_3_0_L_ensp 0xc0
+#define CX0342_CHANNEL_3_0_H_ensp 0xc1
+#define CX0342_CHANNEL_3_1_L_ensp 0xc2
+#define CX0342_CHANNEL_3_1_H_ensp 0xc3
+#define CX0342_CHANNEL_3_2_L_ensp 0xc4
+#define CX0342_CHANNEL_3_2_H_ensp 0xc5
+#define CX0342_CHANNEL_3_3_L_ensp 0xc6
+#define CX0342_CHANNEL_3_3_H_ensp 0xc7
+#define CX0342_CHANNEL_3_4_L_ensp 0xc8
+#define CX0342_CHANNEL_3_4_H_ensp 0xc9
+#define CX0342_CHANNEL_3_5_L_ensp 0xca
+#define CX0342_CHANNEL_3_5_H_ensp 0xcb
+#define CX0342_CHANNEL_3_6_L_ensp 0xcc
+#define CX0342_CHANNEL_3_6_H_ensp 0xcd
+#define CX0342_CHANNEL_3_7_L_ensp 0xce
+#define CX0342_CHANNEL_3_7_H_ensp 0xcf
+#define CX0342_CHANNEL_4_0_L_sela 0xd0
+#define CX0342_CHANNEL_4_0_H_sela 0xd1
+#define CX0342_CHANNEL_4_1_L_sela 0xd2
+#define CX0342_CHANNEL_4_1_H_sela 0xd3
+#define CX0342_CHANNEL_5_0_L_intla 0xe0
+#define CX0342_CHANNEL_5_0_H_intla 0xe1
+#define CX0342_CHANNEL_5_1_L_intla 0xe2
+#define CX0342_CHANNEL_5_1_H_intla 0xe3
+#define CX0342_CHANNEL_5_2_L_intla 0xe4
+#define CX0342_CHANNEL_5_2_H_intla 0xe5
+#define CX0342_CHANNEL_5_3_L_intla 0xe6
+#define CX0342_CHANNEL_5_3_H_intla 0xe7
+#define CX0342_CHANNEL_6_0_L_xa_sel_pos 0xf0
+#define CX0342_CHANNEL_6_0_H_xa_sel_pos 0xf1
+#define CX0342_CHANNEL_7_1_L_cds_pos 0xf2
+#define CX0342_CHANNEL_7_1_H_cds_pos 0xf3
+#define CX0342_SENSOR_HEIGHT_L 0xfb
+#define CX0342_SENSOR_HEIGHT_H 0xfc
+#define CX0342_SENSOR_WIDTH_L 0xfd
+#define CX0342_SENSOR_WIDTH_H 0xfe
+#define CX0342_VSYNC_HSYNC_READ 0xff
+
+struct cmd {
+ u8 reg;
+ u8 val;
+};
+
+static const u8 DQT[17][130] = {
+ /* Define quantization table (thanks to Thomas Kaiser) */
+ { /* Quality 0 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x01,
+ 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0b, 0x06,
+ 0x06, 0x0b, 0x18, 0x10, 0x0e, 0x10, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ },
+ { /* Quality 1 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x01,
+ 0x08, 0x09, 0x09, 0x0c, 0x0a, 0x0c, 0x17, 0x0d,
+ 0x0d, 0x17, 0x31, 0x21, 0x1c, 0x21, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ },
+ { /* Quality 2 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x06, 0x06, 0x06, 0x04, 0x04, 0x04,
+ 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x01,
+ 0x0c, 0x0d, 0x0d, 0x12, 0x0f, 0x12, 0x23, 0x13,
+ 0x13, 0x23, 0x4a, 0x31, 0x2a, 0x31, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ },
+ { /* Quality 3 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04,
+ 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x01,
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ },
+ { /* Quality 4 */
+ 0x00,
+ 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x0a, 0x0a, 0x0a, 0x05, 0x05, 0x05,
+ 0x05, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x01,
+ 0x11, 0x16, 0x16, 0x1e, 0x1a, 0x1e, 0x3a, 0x20,
+ 0x20, 0x3a, 0x7b, 0x52, 0x46, 0x52, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ },
+ { /* Quality 5 */
+ 0x00,
+ 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06,
+ 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x01,
+ 0x11, 0x1b, 0x1b, 0x24, 0x1f, 0x24, 0x46, 0x27,
+ 0x27, 0x46, 0x94, 0x63, 0x54, 0x63, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ },
+ { /* Quality 6 */
+ 0x00,
+ 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07,
+ 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x01,
+ 0x15, 0x1f, 0x1f, 0x2a, 0x24, 0x2a, 0x52, 0x2d,
+ 0x2d, 0x52, 0xad, 0x73, 0x62, 0x73, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ },
+ { /* Quality 7 */
+ 0x00,
+ 0x05, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08,
+ 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x01,
+ 0x15, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34,
+ 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ },
+ { /* Quality 8 */
+ 0x00,
+ 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x14, 0x14, 0x14, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x01,
+ 0x19, 0x2d, 0x2d, 0x3c, 0x34, 0x3c, 0x75, 0x41,
+ 0x41, 0x75, 0xf7, 0xa5, 0x8c, 0xa5, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ },
+ { /* Quality 9 */
+ 0x00,
+ 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x01,
+ 0x19, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e,
+ 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 10 */
+ 0x00,
+ 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x01,
+ 0x1d, 0x3f, 0x3f, 0x54, 0x49, 0x54, 0xa4, 0x5b,
+ 0x5b, 0xa4, 0xff, 0xe7, 0xc4, 0xe7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 11 */
+ 0x00,
+ 0x07, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10,
+ 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x01,
+ 0x1d, 0x48, 0x48, 0x60, 0x54, 0x60, 0xbc, 0x68,
+ 0x68, 0xbc, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 12 */
+ 0x00,
+ 0x08, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x28, 0x28, 0x28, 0x14, 0x14, 0x14,
+ 0x14, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x01,
+ 0x22, 0x5a, 0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82,
+ 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 13 */
+ 0x00,
+ 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18,
+ 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x01,
+ 0x22, 0x6c, 0x6c, 0x90, 0x7e, 0x90, 0xff, 0x9c,
+ 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 14 */
+ 0x00,
+ 0x0a, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x01,
+ 0x2a, 0x7e, 0x7e, 0xa8, 0x93, 0xa8, 0xff, 0xb6,
+ 0xb6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 15 */
+ 0x00,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20,
+ 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x01,
+ 0x2a, 0x90, 0x90, 0xc0, 0xa8, 0xc0, 0xff, 0xd0,
+ 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 16-31 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x01,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ }
+};
+
+static const struct cmd tp6810_cx_init_common[] = {
+ {0x1c, 0x00},
+ {TP6800_R10_SIF_TYPE, 0x00},
+ {0x4e, 0x00},
+ {0x4f, 0x00},
+ {TP6800_R50, 0xff},
+ {TP6800_R51, 0x03},
+ {0x00, 0x07},
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R2F_TIMING_CFG, 0x37},
+ {TP6800_R30_SENSOR_CFG, 0x10},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R52, 0x40},
+ {TP6800_R53, 0x40},
+ {TP6800_R54_DARK_CFG, 0x40},
+ {TP6800_R30_SENSOR_CFG, 0x18},
+ {0x4b, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x83},
+ {TP6800_R79_QUALITY, 0x05},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {0x7c, 0x04},
+ {0x25, 0x14},
+ {0x26, 0x0f},
+ {0x7b, 0x10},
+};
+
+static const struct cmd tp6810_ov_init_common[] = {
+ {0x1c, 0x00},
+ {TP6800_R10_SIF_TYPE, 0x00},
+ {0x4e, 0x00},
+ {0x4f, 0x00},
+ {TP6800_R50, 0xff},
+ {TP6800_R51, 0x03},
+ {0x00, 0x07},
+ {TP6800_R52, 0x40},
+ {TP6800_R53, 0x40},
+ {TP6800_R54_DARK_CFG, 0x40},
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R2F_TIMING_CFG, 0x17},
+ {TP6800_R30_SENSOR_CFG, 0x18},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x86},
+ {0x25, 0x18},
+ {0x26, 0x0f},
+ {0x7b, 0x90},
+};
+
+static const struct cmd tp6810_bridge_start[] = {
+ {0x59, 0x88},
+ {0x5a, 0x0f},
+ {0x5b, 0x4e},
+ {TP6800_R5C_EDGE_THRLD, 0x63},
+ {TP6800_R5D_DEMOSAIC_CFG, 0x00},
+ {0x03, 0x7f},
+ {0x04, 0x80},
+ {0x06, 0x00},
+ {0x00, 0x00},
+};
+
+static const struct cmd tp6810_late_start[] = {
+ {0x7d, 0x01},
+ {0xb0, 0x04},
+ {0xb1, 0x04},
+ {0xb2, 0x04},
+ {0xb3, 0x04},
+ {0xb4, 0x04},
+ {0xb5, 0x04},
+ {0xb6, 0x08},
+ {0xb7, 0x08},
+ {0xb8, 0x04},
+ {0xb9, 0x04},
+ {0xba, 0x04},
+ {0xbb, 0x04},
+ {0xbc, 0x04},
+ {0xbd, 0x08},
+ {0xbe, 0x08},
+ {0xbf, 0x08},
+ {0xc0, 0x04},
+ {0xc1, 0x04},
+ {0xc2, 0x08},
+ {0xc3, 0x08},
+ {0xc4, 0x08},
+ {0xc5, 0x08},
+ {0xc6, 0x08},
+ {0xc7, 0x13},
+ {0xc8, 0x04},
+ {0xc9, 0x08},
+ {0xca, 0x08},
+ {0xcb, 0x08},
+ {0xcc, 0x08},
+ {0xcd, 0x08},
+ {0xce, 0x13},
+ {0xcf, 0x13},
+ {0xd0, 0x08},
+ {0xd1, 0x08},
+ {0xd2, 0x08},
+ {0xd3, 0x08},
+ {0xd4, 0x08},
+ {0xd5, 0x13},
+ {0xd6, 0x13},
+ {0xd7, 0x13},
+ {0xd8, 0x08},
+ {0xd9, 0x08},
+ {0xda, 0x08},
+ {0xdb, 0x08},
+ {0xdc, 0x13},
+ {0xdd, 0x13},
+ {0xde, 0x13},
+ {0xdf, 0x13},
+ {0xe0, 0x08},
+ {0xe1, 0x08},
+ {0xe2, 0x08},
+ {0xe3, 0x13},
+ {0xe4, 0x13},
+ {0xe5, 0x13},
+ {0xe6, 0x13},
+ {0xe7, 0x13},
+ {0xe8, 0x08},
+ {0xe9, 0x08},
+ {0xea, 0x13},
+ {0xeb, 0x13},
+ {0xec, 0x13},
+ {0xed, 0x13},
+ {0xee, 0x13},
+ {0xef, 0x13},
+ {0x7d, 0x02},
+
+ /* later after isoc start */
+ {0x7d, 0x08},
+ {0x7d, 0x00},
+};
+
+static const struct cmd cx0342_timing_seq[] = {
+ {CX0342_CHANNEL_0_1_L_irst, 0x20},
+ {CX0342_CHANNEL_0_2_L_irst, 0x24},
+ {CX0342_CHANNEL_0_2_H_irst, 0x00},
+ {CX0342_CHANNEL_0_3_L_irst, 0x2f},
+ {CX0342_CHANNEL_0_3_H_irst, 0x00},
+ {CX0342_CHANNEL_1_0_L_itx, 0x02},
+ {CX0342_CHANNEL_1_0_H_itx, 0x00},
+ {CX0342_CHANNEL_1_1_L_itx, 0x20},
+ {CX0342_CHANNEL_1_1_H_itx, 0x00},
+ {CX0342_CHANNEL_1_2_L_itx, 0xe4},
+ {CX0342_CHANNEL_1_2_H_itx, 0x00},
+ {CX0342_CHANNEL_1_3_L_itx, 0xee},
+ {CX0342_CHANNEL_1_3_H_itx, 0x00},
+ {CX0342_CHANNEL_2_0_L_iwl, 0x30},
+ {CX0342_CHANNEL_2_0_H_iwl, 0x00},
+ {CX0342_CHANNEL_3_0_L_ensp, 0x34},
+ {CX0342_CHANNEL_3_1_L_ensp, 0xe2},
+ {CX0342_CHANNEL_3_1_H_ensp, 0x00},
+ {CX0342_CHANNEL_3_2_L_ensp, 0xf6},
+ {CX0342_CHANNEL_3_2_H_ensp, 0x00},
+ {CX0342_CHANNEL_3_3_L_ensp, 0xf4},
+ {CX0342_CHANNEL_3_3_H_ensp, 0x02},
+ {CX0342_CHANNEL_4_0_L_sela, 0x26},
+ {CX0342_CHANNEL_4_0_H_sela, 0x00},
+ {CX0342_CHANNEL_4_1_L_sela, 0xe2},
+ {CX0342_CHANNEL_4_1_H_sela, 0x00},
+ {CX0342_CHANNEL_5_0_L_intla, 0x26},
+ {CX0342_CHANNEL_5_1_L_intla, 0x29},
+ {CX0342_CHANNEL_5_2_L_intla, 0xf0},
+ {CX0342_CHANNEL_5_2_H_intla, 0x00},
+ {CX0342_CHANNEL_5_3_L_intla, 0xf3},
+ {CX0342_CHANNEL_5_3_H_intla, 0x00},
+ {CX0342_CHANNEL_6_0_L_xa_sel_pos, 0x24},
+ {CX0342_CHANNEL_7_1_L_cds_pos, 0x02},
+ {CX0342_TIMING_EN, 0x01},
+};
+
+/* define the JPEG header */
+static void jpeg_define(u8 *jpeg_hdr,
+ int height,
+ int width)
+{
+ memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head);
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width;
+}
+
+/* set the JPEG quality for sensor soi763a */
+static void jpeg_set_qual(u8 *jpeg_hdr,
+ int quality)
+{
+ int i, sc;
+
+ if (quality < 50)
+ sc = 5000 / quality;
+ else
+ sc = 200 - quality * 2;
+ for (i = 0; i < 64; i++) {
+ jpeg_hdr[JPEG_QT0_OFFSET + i] =
+ (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
+ jpeg_hdr[JPEG_QT1_OFFSET + i] =
+ (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x0e,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* the returned value is in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev, u8 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x0d,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1, 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ const struct cmd *p, int l)
+{
+ do {
+ reg_w(gspca_dev, p->reg, p->val);
+ p++;
+ } while (--l > 0);
+}
+
+static int i2c_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
+ reg_w(gspca_dev, TP6800_R13_SIF_TX_DATA, value);
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x01);
+ if (sd->bridge == BRIDGE_TP6800)
+ return 0;
+ msleep(5);
+ reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
+ if (gspca_dev->usb_buf[0] == 0)
+ return 0;
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ return -1; /* error */
+}
+
+static void i2c_w_buf(struct gspca_dev *gspca_dev,
+ const struct cmd *p, int l)
+{
+ do {
+ i2c_w(gspca_dev, p->reg, p->val);
+ p++;
+ } while (--l > 0);
+}
+
+static int i2c_r(struct gspca_dev *gspca_dev, u8 index, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int v;
+
+ reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x02);
+ msleep(5);
+ reg_r(gspca_dev, TP6800_R14_SIF_RX_DATA);
+ v = gspca_dev->usb_buf[0];
+ if (sd->bridge == BRIDGE_TP6800)
+ return v;
+ if (len > 1) {
+ reg_r(gspca_dev, TP6800_R1B_SIF_RX_DATA2);
+ v |= (gspca_dev->usb_buf[0] << 8);
+ }
+ reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
+ if (gspca_dev->usb_buf[0] == 0)
+ return v;
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ return -1;
+}
+
+static void bulk_w(struct gspca_dev *gspca_dev,
+ u8 tag,
+ const u8 *data,
+ int length)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int count, actual_count, ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ for (;;) {
+ count = length > BULK_OUT_SIZE - 1
+ ? BULK_OUT_SIZE - 1 : length;
+ gspca_dev->usb_buf[0] = tag;
+ memcpy(&gspca_dev->usb_buf[1], data, count);
+ ret = usb_bulk_msg(dev,
+ usb_sndbulkpipe(dev, 3),
+ gspca_dev->usb_buf, count + 1,
+ &actual_count, 500);
+ if (ret < 0) {
+ pr_err("bulk write error %d tag=%02x\n",
+ ret, tag);
+ gspca_dev->usb_err = ret;
+ return;
+ }
+ length -= count;
+ if (length <= 0)
+ break;
+ data += count;
+ }
+}
+
+static int probe_6810(struct gspca_dev *gspca_dev)
+{
+ u8 gpio;
+ int ret;
+
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ gpio = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); /* ov??? */
+ reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x00);
+ if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
+ return SENSOR_SOI763A;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x7f); /* (unknown i2c) */
+ if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
+ return -2;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x11); /* tas??? / hv??? */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return -3;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x6e); /* po??? */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return -4;
+
+ ret = i2c_r(gspca_dev, 0x01, 1);
+ if (ret > 0)
+ return -5;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5d); /* mi/mt??? */
+ ret = i2c_r(gspca_dev, 0x00, 2);
+ if (ret > 0)
+ return -6;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5c); /* mi/mt??? */
+ ret = i2c_r(gspca_dev, 0x36, 2);
+ if (ret > 0)
+ return -7;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x61); /* (unknown i2c) */
+ reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x10);
+ if (i2c_w(gspca_dev, 0xff, 0x00) >= 0)
+ return -8;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return SENSOR_CX0342;
+ return -9;
+}
+
+static void cx0342_6810_init(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init_1[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {0x25, 0x02},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x80},
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {TP6800_R18_GPIO_DATA, 0xc1},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {TP6800_R11_SIF_CONTROL, 0x00},
+ };
+ static const struct cmd reg_init_2[] = {
+ {TP6800_R78_FORMAT, 0x48},
+ {TP6800_R11_SIF_CONTROL, 0x00},
+ };
+ static const struct cmd sensor_init[] = {
+ {CX0342_OUTPUT_CTRL, 0x07},
+ {CX0342_BYPASS_MODE, 0x58},
+ {CX0342_GPXLTHD_L, 0x28},
+ {CX0342_RBPXLTHD_L, 0x28},
+ {CX0342_PLANETHD_L, 0x50},
+ {CX0342_PLANETHD_H, 0x03},
+ {CX0342_RB_GAP_L, 0xff},
+ {CX0342_RB_GAP_H, 0x07},
+ {CX0342_G_GAP_L, 0xff},
+ {CX0342_G_GAP_H, 0x07},
+ {CX0342_RST_OVERFLOW_L, 0x5c},
+ {CX0342_RST_OVERFLOW_H, 0x01},
+ {CX0342_DATA_OVERFLOW_L, 0xfc},
+ {CX0342_DATA_OVERFLOW_H, 0x03},
+ {CX0342_DATA_UNDERFLOW_L, 0x00},
+ {CX0342_DATA_UNDERFLOW_H, 0x00},
+ {CX0342_SYS_CTRL_0, 0x40},
+ {CX0342_GLOBAL_GAIN, 0x01},
+ {CX0342_CLOCK_GEN, 0x00},
+ {CX0342_SYS_CTRL_0, 0x02},
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_TIMING_EN, 0x01},
+ };
+
+ reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
+ reg_w_buf(gspca_dev, tp6810_cx_init_common,
+ ARRAY_SIZE(tp6810_cx_init_common));
+ reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
+
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 I2C addr */
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+ i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
+}
+
+static void soi763a_6810_init(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init_1[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {0x25, 0x02},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x80},
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xc1},
+ };
+ static const struct cmd reg_init_2[] = {
+ {TP6800_R78_FORMAT, 0x54},
+ };
+ static const struct cmd sensor_init[] = {
+ {0x00, 0x00},
+ {0x01, 0x80},
+ {0x02, 0x80},
+ {0x03, 0x90},
+ {0x04, 0x20},
+ {0x05, 0x20},
+ {0x06, 0x80},
+ {0x07, 0x00},
+ {0x08, 0xff},
+ {0x09, 0xff},
+ {0x0a, 0x76}, /* 7630 = soi673a */
+ {0x0b, 0x30},
+ {0x0c, 0x20},
+ {0x0d, 0x20},
+ {0x0e, 0xff},
+ {0x0f, 0xff},
+ {0x10, 0x41},
+ {0x15, 0x14},
+ {0x11, 0x40},
+ {0x12, 0x48},
+ {0x13, 0x80},
+ {0x14, 0x80},
+ {0x16, 0x03},
+ {0x28, 0xb0},
+ {0x71, 0x20},
+ {0x75, 0x8e},
+ {0x17, 0x1b},
+ {0x18, 0xbd},
+ {0x19, 0x05},
+ {0x1a, 0xf6},
+ {0x1b, 0x04},
+ {0x1c, 0x7f}, /* omnivision */
+ {0x1d, 0xa2},
+ {0x1e, 0x00},
+ {0x1f, 0x00},
+ {0x20, 0x45},
+ {0x21, 0x80},
+ {0x22, 0x80},
+ {0x23, 0xee},
+ {0x24, 0x50},
+ {0x25, 0x7a},
+ {0x26, 0xa0},
+ {0x27, 0x9a},
+ {0x29, 0x30},
+ {0x2a, 0x80},
+ {0x2b, 0x00},
+ {0x2c, 0xac},
+ {0x2d, 0x05},
+ {0x2e, 0x80},
+ {0x2f, 0x3c},
+ {0x30, 0x22},
+ {0x31, 0x00},
+ {0x32, 0x86},
+ {0x33, 0x08},
+ {0x34, 0xff},
+ {0x35, 0xff},
+ {0x36, 0xff},
+ {0x37, 0xff},
+ {0x38, 0xff},
+ {0x39, 0xff},
+ {0x3a, 0xfe},
+ {0x3b, 0xfe},
+ {0x3c, 0xfe},
+ {0x3d, 0xfe},
+ {0x3e, 0xfe},
+ {0x3f, 0x71},
+ {0x40, 0xff},
+ {0x41, 0xff},
+ {0x42, 0xff},
+ {0x43, 0xff},
+ {0x44, 0xff},
+ {0x45, 0xff},
+ {0x46, 0xff},
+ {0x47, 0xff},
+ {0x48, 0xff},
+ {0x49, 0xff},
+ {0x4a, 0xfe},
+ {0x4b, 0xff},
+ {0x4c, 0x00},
+ {0x4d, 0x00},
+ {0x4e, 0xff},
+ {0x4f, 0xff},
+ {0x50, 0xff},
+ {0x51, 0xff},
+ {0x52, 0xff},
+ {0x53, 0xff},
+ {0x54, 0xff},
+ {0x55, 0xff},
+ {0x56, 0xff},
+ {0x57, 0xff},
+ {0x58, 0xff},
+ {0x59, 0xff},
+ {0x5a, 0xff},
+ {0x5b, 0xfe},
+ {0x5c, 0xff},
+ {0x5d, 0x8f},
+ {0x5e, 0xff},
+ {0x5f, 0x8f},
+ {0x60, 0xa2},
+ {0x61, 0x4a},
+ {0x62, 0xf3},
+ {0x63, 0x75},
+ {0x64, 0xf0},
+ {0x65, 0x00},
+ {0x66, 0x55},
+ {0x67, 0x92},
+ {0x68, 0xa0},
+ {0x69, 0x4a},
+ {0x6a, 0x22},
+ {0x6b, 0x00},
+ {0x6c, 0x33},
+ {0x6d, 0x44},
+ {0x6e, 0x22},
+ {0x6f, 0x84},
+ {0x70, 0x0b},
+ {0x72, 0x10},
+ {0x73, 0x50},
+ {0x74, 0x21},
+ {0x76, 0x00},
+ {0x77, 0xa5},
+ {0x78, 0x80},
+ {0x79, 0x80},
+ {0x7a, 0x80},
+ {0x7b, 0xe2},
+ {0x7c, 0x00},
+ {0x7d, 0xf7},
+ {0x7e, 0x00},
+ {0x7f, 0x00},
+ };
+
+ reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
+ reg_w_buf(gspca_dev, tp6810_ov_init_common,
+ ARRAY_SIZE(tp6810_ov_init_common));
+ reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
+
+ i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(10);
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+}
+
+/* set the gain and exposure */
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_CX0342) {
+ int expo;
+
+ expo = (sd->ctrls[EXPOSURE].val << 2) - 1;
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H,
+ sd->ctrls[GAIN].val >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, sd->ctrls[GAIN].val);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H,
+ sd->ctrls[GAIN].val >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, sd->ctrls[GAIN].val);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
+ sd->ctrls[BGAIN].val >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, sd->ctrls[BGAIN].val);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
+ sd->ctrls[RGAIN].val >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, sd->ctrls[RGAIN].val);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
+ sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
+ return;
+ }
+
+ /* soi763a */
+ i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */
+ sd->ctrls[EXPOSURE].val);
+/* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */
+ i2c_w(gspca_dev, 0x00, /* gain */
+ sd->ctrls[GAIN].val);
+}
+
+/* set the JPEG quantization tables */
+static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* update the jpeg quantization tables */
+ PDEBUG(D_STREAM, "q %d -> %d", sd->quality, q);
+ sd->quality = q;
+ if (q > 16)
+ q = 16;
+ if (sd->sensor == SENSOR_SOI763A)
+ jpeg_set_qual(sd->jpeg_hdr, jpeg_q[q]);
+ else
+ memcpy(&sd->jpeg_hdr[JPEG_QT0_OFFSET - 1],
+ DQT[q], sizeof DQT[0]);
+}
+
+/* set the JPEG compression quality factor */
+static void setquality(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 q;
+
+ q = sd->ctrls[QUALITY].val;
+ if (q != 16)
+ q = 15 - q;
+
+ reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x00);
+ reg_w(gspca_dev, TP6800_R79_QUALITY, 0x04);
+ reg_w(gspca_dev, TP6800_R79_QUALITY, q);
+
+ /* auto quality */
+ if (q == 15 && sd->bridge == BRIDGE_TP6810) {
+ msleep(4);
+ reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x19);
+ }
+}
+
+static const u8 color_null[18] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const u8 color_gain[NSENSORS][18] = {
+[SENSOR_CX0342] =
+ {0x4c, 0x00, 0xa9, 0x00, 0x31, 0x00, /* Y R/G/B (LE values) */
+ 0xb6, 0x03, 0x6c, 0x03, 0xe0, 0x00, /* U R/G/B */
+ 0xdf, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
+[SENSOR_SOI763A] =
+ {0x4c, 0x00, 0x95, 0x00, 0x1d, 0x00, /* Y R/G/B (LE values) */
+ 0xb6, 0x03, 0x6c, 0x03, 0xd7, 0x00, /* U R/G/B */
+ 0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
+};
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int gamma;
+#define NGAMMA 6
+ static const u8 gamma_tb[NGAMMA][3][1024] = {
+ { /* gamma 0 - from tp6800 + soi763a */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 1 - from tp6810 + soi763a */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x09, 0x0a,
+ 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x22, 0x22, 0x23, 0x25, 0x26, 0x27,
+ 0x27, 0x28, 0x29, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x35,
+ 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, 0x49,
+ 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x59,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5e,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, 0x61,
+ 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x65, 0x66,
+ 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
+ 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, 0x84,
+ 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
+ 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91,
+ 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93,
+ 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x97,
+ 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
+ 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xab,
+ 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
+ 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
+ 0x05, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d,
+ 0x0e, 0x10, 0x10, 0x11, 0x12, 0x14, 0x15, 0x15,
+ 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x25,
+ 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, 0x2b, 0x2c,
+ 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x31,
+ 0x33, 0x34, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d,
+ 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48,
+ 0x48, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50,
+ 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55,
+ 0x55, 0x56, 0x56, 0x56, 0x58, 0x58, 0x59, 0x59,
+ 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60,
+ 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x65,
+ 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a,
+ 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+ 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74,
+ 0x75, 0x75, 0x75, 0x77, 0x77, 0x77, 0x78, 0x78,
+ 0x78, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d,
+ 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, 0x05, 0x07,
+ 0x08, 0x09, 0x0a, 0x0a, 0x0c, 0x0d, 0x0e, 0x0e,
+ 0x10, 0x11, 0x12, 0x12, 0x14, 0x15, 0x16, 0x16,
+ 0x17, 0x18, 0x18, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x22, 0x23, 0x23,
+ 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x29, 0x29,
+ 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, 0x30,
+ 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, 0x34, 0x35,
+ 0x35, 0x37, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a,
+ 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d,
+ 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x47,
+ 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4b,
+ 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4f,
+ 0x4f, 0x50, 0x50, 0x50, 0x52, 0x52, 0x52, 0x53,
+ 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56,
+ 0x56, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a,
+ 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
+ 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
+ 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63,
+ 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66,
+ 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e,
+ 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+ 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74,
+ 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x77, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82,
+ 0x82, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
+ 0x85, 0x86, 0x86, 0x86, 0x86, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef,
+ 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+ },
+ { /* gamma 2 */
+ {0x00, 0x01, 0x02, 0x05, 0x07, 0x08, 0x0a, 0x0c,
+ 0x0d, 0x0e, 0x10, 0x12, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x22,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
+ 0x2d, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34,
+ 0x35, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3b, 0x3b,
+ 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
+ 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x49, 0x49,
+ 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4f, 0x4f,
+ 0x50, 0x50, 0x52, 0x53, 0x53, 0x54, 0x54, 0x55,
+ 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, 0x5a, 0x5a,
+ 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63,
+ 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+ 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x70,
+ 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74,
+ 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78,
+ 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82,
+ 0x82, 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05,
+ 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e, 0x10, 0x11,
+ 0x12, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x1a,
+ 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23,
+ 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2b,
+ 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x33,
+ 0x33, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, 0x39,
+ 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3f,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44,
+ 0x45, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, 0x4a,
+ 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x58,
+ 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x63,
+ 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67,
+ 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c,
+ 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81,
+ 0x82, 0x82, 0x82, 0x82, 0x84, 0x84, 0x84, 0x84,
+ 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x01, 0x02, 0x05, 0x07, 0x08,
+ 0x09, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x26, 0x27,
+ 0x28, 0x28, 0x29, 0x2b, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x30, 0x31, 0x31, 0x33, 0x34, 0x35, 0x35, 0x37,
+ 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c,
+ 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x43, 0x43,
+ 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49,
+ 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58,
+ 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c,
+ 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68,
+ 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x73, 0x73,
+ 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7c, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80,
+ 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82,
+ 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb4, 0xb3, 0xb4, 0xb4, 0xb4,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 3 - from tp6810 + cx0342 */
+ {0x08, 0x09, 0x0c, 0x0d, 0x10, 0x11, 0x14, 0x15,
+ 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x1f, 0x20, 0x23,
+ 0x25, 0x26, 0x27, 0x28, 0x2b, 0x2c, 0x2d, 0x2f,
+ 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, 0x42, 0x43,
+ 0x44, 0x45, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4d, 0x4f, 0x50, 0x52, 0x53, 0x53,
+ 0x54, 0x55, 0x56, 0x56, 0x58, 0x59, 0x5a, 0x5a,
+ 0x5b, 0x5c, 0x5c, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
+ 0x61, 0x62, 0x63, 0x63, 0x65, 0x66, 0x66, 0x67,
+ 0x68, 0x68, 0x69, 0x69, 0x6a, 0x6c, 0x6c, 0x6d,
+ 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x73,
+ 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, 0x78,
+ 0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81,
+ 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93,
+ 0x93, 0x93, 0x94, 0x94, 0x96, 0x96, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x03, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0d, 0x10,
+ 0x11, 0x12, 0x14, 0x15, 0x17, 0x18, 0x1a, 0x1b,
+ 0x1c, 0x1e, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26,
+ 0x27, 0x28, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2f,
+ 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x37, 0x38,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
+ 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b,
+ 0x4c, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52,
+ 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x58,
+ 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c,
+ 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x66,
+ 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
+ 0x71, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84,
+ 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90,
+ 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96,
+ 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x98, 0x98,
+ 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14,
+ 0x16, 0x17, 0x18, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2d,
+ 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38,
+ 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, 0x40,
+ 0x42, 0x43, 0x44, 0x44, 0x45, 0x47, 0x48, 0x49,
+ 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4f, 0x50,
+ 0x52, 0x52, 0x53, 0x54, 0x55, 0x55, 0x56, 0x58,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e,
+ 0x5f, 0x5f, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63,
+ 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e,
+ 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x74,
+ 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, 0x79,
+ 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81,
+ 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
+ 0x92, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9,
+ 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+ },
+ { /* gamma 4 - from tp6800 + soi763a */
+ {0x11, 0x14, 0x15, 0x17, 0x1a, 0x1b, 0x1e, 0x1f,
+ 0x22, 0x23, 0x25, 0x27, 0x28, 0x2b, 0x2c, 0x2d,
+ 0x2f, 0x31, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3a,
+ 0x3b, 0x3c, 0x3d, 0x40, 0x42, 0x43, 0x44, 0x45,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f,
+ 0x50, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, 0x58,
+ 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5f, 0x60,
+ 0x61, 0x61, 0x62, 0x63, 0x65, 0x65, 0x66, 0x67,
+ 0x68, 0x68, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, 0x6e,
+ 0x6f, 0x6f, 0x70, 0x71, 0x71, 0x73, 0x74, 0x74,
+ 0x75, 0x77, 0x77, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+ 0x7b, 0x7c, 0x7c, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
+ 0x81, 0x81, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86,
+ 0x86, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8b,
+ 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x90, 0x90, 0x91,
+ 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x94, 0x96,
+ 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4,
+ 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbc,
+ 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc9,
+ 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
+ 0xd0, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x14, 0x15,
+ 0x16, 0x17, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
+ 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, 0x35,
+ 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
+ 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, 0x4c,
+ 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, 0x53, 0x54,
+ 0x54, 0x55, 0x55, 0x56, 0x58, 0x58, 0x59, 0x5a,
+ 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, 0x65,
+ 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
+ 0x69, 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73,
+ 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77,
+ 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91,
+ 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xaa, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad,
+ 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9,
+ 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
+ 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x0d, 0x10, 0x11, 0x14, 0x15, 0x17, 0x18, 0x1b,
+ 0x1c, 0x1e, 0x20, 0x22, 0x23, 0x26, 0x27, 0x28,
+ 0x29, 0x2b, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34,
+ 0x35, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
+ 0x3f, 0x40, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48,
+ 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4f, 0x50,
+ 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f,
+ 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x65, 0x65,
+ 0x66, 0x67, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x77,
+ 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
+ 0x81, 0x81, 0x82, 0x82, 0x84, 0x84, 0x85, 0x85,
+ 0x86, 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92,
+ 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 5 */
+ {0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1e, 0x20, 0x21,
+ 0x23, 0x24, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2c,
+ 0x2d, 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
+ 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x56, 0x57, 0x58, 0x59, 0x59, 0x5a, 0x5b,
+ 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
+ 0x62, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66,
+ 0x67, 0x68, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6b,
+ 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75,
+ 0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7d, 0x7d, 0x7e,
+ 0x7e, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x82,
+ 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85,
+ 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89,
+ 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d,
+ 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0,
+ 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3,
+ 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5,
+ 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5,
+ 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x0f, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f,
+ 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x44, 0x45,
+ 0x46, 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b,
+ 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4e, 0x4f, 0x50,
+ 0x50, 0x51, 0x51, 0x52, 0x53, 0x53, 0x54, 0x54,
+ 0x55, 0x55, 0x56, 0x56, 0x57, 0x58, 0x58, 0x59,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d,
+ 0x5d, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x6f, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72,
+ 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x75,
+ 0x76, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x78,
+ 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e,
+ 0x7e, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81,
+ 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83,
+ 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88,
+ 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d,
+ 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96,
+ 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98,
+ 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
+ 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea,
+ 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x13, 0x15, 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
+ 0x42, 0x43, 0x44, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e,
+ 0x4f, 0x50, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54,
+ 0x55, 0x55, 0x56, 0x57, 0x57, 0x58, 0x59, 0x59,
+ 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5f,
+ 0x5f, 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x63,
+ 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
+ 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c,
+ 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f,
+ 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83,
+ 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c,
+ 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
+ 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x95,
+ 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d,
+ 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f,
+ 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad,
+ 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1,
+ 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc8,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ },
+ };
+
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+ if (sd->bridge == BRIDGE_TP6810)
+ reg_w(gspca_dev, 0x02, 0x28);
+/* msleep(50); */
+ gamma = sd->ctrls[GAMMA].val;
+ bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024);
+ bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024);
+ bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024);
+ if (sd->bridge == BRIDGE_TP6810) {
+ int i;
+
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R55_GAMMA_R,
+ gamma_tb[gamma][0][i]);
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R56_GAMMA_G,
+ gamma_tb[gamma][1][i]);
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R57_GAMMA_B,
+ gamma_tb[gamma][2][i]);
+ reg_w(gspca_dev, 0x02, 0x28);
+ }
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+/* msleep(50); */
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ if (sd->bridge == BRIDGE_TP6800) {
+ val = sd->ctrls[SHARPNESS].val
+ | 0x08; /* grid compensation enable */
+ if (gspca_dev->width == 640)
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ else
+ val |= 0x04; /* scaling down enable */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val);
+ } else {
+ val = (sd->ctrls[SHARPNESS].val << 5) | 0x08;
+ reg_w(gspca_dev, 0x59, val);
+ }
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
+ return;
+ if (sd->ctrls[AUTOGAIN].val) {
+ sd->ag_cnt = AG_CNT_START;
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
+ } else {
+ sd->ag_cnt = -1;
+ gspca_dev->ctrl_inac &= ~((1 << EXPOSURE) | (1 << GAIN));
+ }
+}
+
+/* set the resolution for sensor cx0342 */
+static void set_resolution(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+ if (gspca_dev->width == 320) {
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
+ msleep(100);
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+ msleep(100);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x0d);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0x37);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x01);
+ } else {
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x05);
+ msleep(100);
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+ msleep(100);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x09);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0xcf);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
+ }
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
+ ARRAY_SIZE(color_gain[0]));
+ setgamma(gspca_dev);
+ setquality(gspca_dev);
+}
+
+/* convert the frame rate to a tp68x0 value */
+static int get_fr_idx(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+
+ if (sd->bridge == BRIDGE_TP6800) {
+ for (i = 0; i < ARRAY_SIZE(rates) - 1; i++) {
+ if (sd->framerate >= rates[i])
+ break;
+ }
+ i = 6 - i; /* 1 = 5fps .. 6 = 30fps */
+
+ /* 640x480 * 30 fps does not work */
+ if (i == 6 /* if 30 fps */
+ && gspca_dev->width == 640)
+ i = 0x86; /* 15 fps */
+ } else {
+ for (i = 0; i < ARRAY_SIZE(rates_6810) - 1; i++) {
+ if (sd->framerate >= rates_6810[i])
+ break;
+ }
+ i = 7 - i; /* 3 = 5fps .. 7 = 30fps */
+
+ /* 640x480 * 30 fps does not work */
+ if (i == 7 /* if 30 fps */
+ && gspca_dev->width == 640)
+ i = 6; /* 15 fps */
+ i |= 0x80; /* clock * 1 */
+ }
+ return i;
+}
+
+static void setframerate(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 fr_idx;
+
+ fr_idx = get_fr_idx(gspca_dev);
+
+ if (sd->bridge == BRIDGE_TP6810) {
+ reg_r(gspca_dev, 0x7b);
+ reg_w(gspca_dev, 0x7b,
+ sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
+ if (sd->ctrls[EXPOSURE].val >= 128)
+ fr_idx = 0xf0; /* lower frame rate */
+ }
+
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, fr_idx);
+
+ if (sd->sensor == SENSOR_CX0342)
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+}
+
+static void setrgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int rgain;
+
+ rgain = sd->ctrls[RGAIN].val;
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_CX0342) {
+ sd->ctrls[BGAIN].val = sd->ctrls[BGAIN].val
+ * val / sd->ctrls[GAIN].val;
+ if (sd->ctrls[BGAIN].val > 4095)
+ sd->ctrls[BGAIN].val = 4095;
+ sd->ctrls[RGAIN].val = sd->ctrls[RGAIN].val
+ * val / sd->ctrls[GAIN].val;
+ if (sd->ctrls[RGAIN].val > 4095)
+ sd->ctrls[RGAIN].val = 4095;
+ }
+ sd->ctrls[GAIN].val = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static void setbgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int bgain;
+
+ bgain = sd->ctrls[BGAIN].val;
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->bridge = id->driver_info;
+
+ gspca_dev->cam.cam_mode = vga_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ gspca_dev->cam.mode_framerates = sd->bridge == BRIDGE_TP6800 ?
+ framerates : framerates_6810;
+
+ sd->framerate = 30; /* default: 30 fps */
+ gspca_dev->cam.ctrls = sd->ctrls;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct cmd tp6800_preinit[] = {
+ {TP6800_R10_SIF_TYPE, 0x01}, /* sif */
+ {TP6800_R11_SIF_CONTROL, 0x01},
+ {TP6800_R15_GPIO_PU, 0x9f},
+ {TP6800_R16_GPIO_PD, 0x9f},
+ {TP6800_R17_GPIO_IO, 0x80},
+ {TP6800_R18_GPIO_DATA, 0x40}, /* LED off */
+ };
+ static const struct cmd tp6810_preinit[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R15_GPIO_PU, 0x6f},
+ {TP6800_R16_GPIO_PD, 0x40},
+ {TP6800_R17_GPIO_IO, 0x9f},
+ {TP6800_R18_GPIO_DATA, 0xc1}, /* LED off */
+ };
+
+ if (sd->bridge == BRIDGE_TP6800)
+ reg_w_buf(gspca_dev, tp6800_preinit,
+ ARRAY_SIZE(tp6800_preinit));
+ else
+ reg_w_buf(gspca_dev, tp6810_preinit,
+ ARRAY_SIZE(tp6810_preinit));
+ msleep(15);
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ PDEBUG(D_PROBE, "gpio: %02x", gspca_dev->usb_buf[0]);
+/* values:
+ * 0x80: snapshot button
+ * 0x40: LED
+ * 0x20: (bridge / sensor) reset for tp6810 ?
+ * 0x07: sensor type ?
+ */
+
+ /* guess the sensor type */
+ if (force_sensor >= 0) {
+ sd->sensor = force_sensor;
+ } else {
+ if (sd->bridge == BRIDGE_TP6800) {
+/*fixme: not sure this is working*/
+ switch (gspca_dev->usb_buf[0] & 0x07) {
+ case 0:
+ sd->sensor = SENSOR_SOI763A;
+ break;
+ case 1:
+ sd->sensor = SENSOR_CX0342;
+ break;
+ }
+ } else {
+ int sensor;
+
+ sensor = probe_6810(gspca_dev);
+ if (sensor < 0) {
+ pr_warn("Unknown sensor %d - forced to soi763a\n",
+ -sensor);
+ sensor = SENSOR_SOI763A;
+ }
+ sd->sensor = sensor;
+ }
+ }
+ if (sd->sensor == SENSOR_SOI763A) {
+ pr_info("Sensor soi763a\n");
+ sd->ctrls[GAMMA].def = sd->bridge == BRIDGE_TP6800 ? 0 : 1;
+ sd->ctrls[GAIN].max = 15;
+ sd->ctrls[GAIN].def = 3;
+ gspca_dev->ctrl_dis = (1 << RGAIN) | (1 << BGAIN);
+ if (sd->bridge == BRIDGE_TP6810) {
+ soi763a_6810_init(gspca_dev);
+#if AUTOGAIN_DEF
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
+#endif
+ } else {
+ gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
+ }
+ } else {
+ pr_info("Sensor cx0342\n");
+ if (sd->bridge == BRIDGE_TP6810) {
+ cx0342_6810_init(gspca_dev);
+#if AUTOGAIN_DEF
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
+#endif
+ } else {
+ gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
+ }
+ }
+
+ if (sd->bridge == BRIDGE_TP6810)
+ sd->ctrls[QUALITY].def = 0; /* auto quality */
+ set_dqt(gspca_dev, 0);
+ return 0;
+}
+
+/* This function is called before choosing the alt setting */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct cmd cx_sensor_init[] = {
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_EXPO_LINE_L, 0x37},
+ {CX0342_EXPO_LINE_H, 0x01},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd cx_bridge_init[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ };
+ static const struct cmd ov_sensor_init[] = {
+ {0x10, 0x75}, /* exposure */
+ {0x76, 0x03},
+ {0x00, 0x00}, /* gain */
+ };
+ static const struct cmd ov_bridge_init[] = {
+ {0x7b, 0x90},
+ {TP6800_R3F_FRAME_RATE, 0x87},
+ };
+
+ if (sd->bridge == BRIDGE_TP6800)
+ return 0;
+ if (sd->sensor == SENSOR_CX0342) {
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20);
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
+ i2c_w_buf(gspca_dev, cx_sensor_init,
+ ARRAY_SIZE(cx_sensor_init));
+ reg_w_buf(gspca_dev, cx_bridge_init,
+ ARRAY_SIZE(cx_bridge_init));
+ bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
+ reg_w(gspca_dev, 0x59, 0x40);
+ } else {
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21);
+ i2c_w_buf(gspca_dev, ov_sensor_init,
+ ARRAY_SIZE(ov_sensor_init));
+ reg_r(gspca_dev, 0x7b);
+ reg_w_buf(gspca_dev, ov_bridge_init,
+ ARRAY_SIZE(ov_bridge_init));
+ }
+ reg_w(gspca_dev, TP6800_R78_FORMAT,
+ gspca_dev->curr_mode ? 0x00 : 0x01);
+ return gspca_dev->usb_err;
+}
+
+static void set_led(struct gspca_dev *gspca_dev, int on)
+{
+ u8 data;
+
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ data = gspca_dev->usb_buf[0];
+ if (on)
+ data &= ~0x40;
+ else
+ data |= 0x40;
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, data);
+}
+
+static void cx0342_6800_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init[] = {
+/*fixme: is this usefull?*/
+ {TP6800_R17_GPIO_IO, 0x9f},
+ {TP6800_R16_GPIO_PD, 0x40},
+ {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
+ {TP6800_R50, 0x00},
+ {TP6800_R51, 0x00},
+ {TP6800_R52, 0xff},
+ {TP6800_R53, 0x03},
+ {TP6800_R54_DARK_CFG, 0x07},
+ {TP6800_R5C_EDGE_THRLD, 0x40},
+ {TP6800_R7A_BLK_THRLD, 0x40},
+ {TP6800_R2F_TIMING_CFG, 0x17},
+ {TP6800_R30_SENSOR_CFG, 0x18}, /* G1B..RG0 */
+ {TP6800_R37_FRONT_DARK_ST, 0x00},
+ {TP6800_R38_FRONT_DARK_END, 0x00},
+ {TP6800_R39_REAR_DARK_ST_L, 0x00},
+ {TP6800_R3A_REAR_DARK_ST_H, 0x00},
+ {TP6800_R3B_REAR_DARK_END_L, 0x00},
+ {TP6800_R3C_REAR_DARK_END_H, 0x00},
+ {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
+ {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
+ {TP6800_R21_ENDP_1_CTL, 0x03},
+
+ {TP6800_R31_PIXEL_START, 0x0b},
+ {TP6800_R32_PIXEL_END_L, 0x8a},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0e},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ {TP6800_R78_FORMAT, 0x00},
+ {TP6800_R12_SIF_ADDR_S, 0x20}, /* cx0342 i2c addr */
+ };
+ static const struct cmd sensor_init[] = {
+ {CX0342_OUTPUT_CTRL, 0x07},
+ {CX0342_BYPASS_MODE, 0x58},
+ {CX0342_GPXLTHD_L, 0x16},
+ {CX0342_RBPXLTHD_L, 0x16},
+ {CX0342_PLANETHD_L, 0xc0},
+ {CX0342_PLANETHD_H, 0x03},
+ {CX0342_RB_GAP_L, 0xff},
+ {CX0342_RB_GAP_H, 0x07},
+ {CX0342_G_GAP_L, 0xff},
+ {CX0342_G_GAP_H, 0x07},
+ {CX0342_RST_OVERFLOW_L, 0x5c},
+ {CX0342_RST_OVERFLOW_H, 0x01},
+ {CX0342_DATA_OVERFLOW_L, 0xfc},
+ {CX0342_DATA_OVERFLOW_H, 0x03},
+ {CX0342_DATA_UNDERFLOW_L, 0x00},
+ {CX0342_DATA_UNDERFLOW_H, 0x00},
+ {CX0342_SYS_CTRL_0, 0x40},
+ {CX0342_GLOBAL_GAIN, 0x01},
+ {CX0342_CLOCK_GEN, 0x00},
+ {CX0342_SYS_CTRL_0, 0x02},
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+ };
+
+ reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+ i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
+ reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
+ reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
+ setexposure(gspca_dev);
+ set_led(gspca_dev, 1);
+ set_resolution(gspca_dev);
+}
+
+static void cx0342_6810_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd sensor_init_2[] = {
+ {CX0342_EXPO_LINE_L, 0x6f},
+ {CX0342_EXPO_LINE_H, 0x02},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd bridge_init_2[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ {TP6800_R7A_BLK_THRLD, 0x00},
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ };
+ static const struct cmd bridge_init_3[] = {
+ {TP6800_R31_PIXEL_START, 0x08},
+ {TP6800_R32_PIXEL_END_L, 0x87},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0e},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ };
+ static const struct cmd sensor_init_3[] = {
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_EXPO_LINE_L, 0x6f},
+ {CX0342_EXPO_LINE_H, 0x02},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd bridge_init_5[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ };
+ static const struct cmd sensor_init_4[] = {
+ {CX0342_EXPO_LINE_L, 0xd3},
+ {CX0342_EXPO_LINE_H, 0x01},
+/*fixme: gains, but 00..80 only*/
+ {CX0342_RAW_GRGAIN_L, 0x40},
+ {CX0342_RAW_GBGAIN_L, 0x40},
+ {CX0342_RAW_RGAIN_L, 0x40},
+ {CX0342_RAW_BGAIN_L, 0x40},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd sensor_init_5[] = {
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ };
+
+ reg_w(gspca_dev, 0x22, gspca_dev->alt);
+ i2c_w_buf(gspca_dev, sensor_init_2, ARRAY_SIZE(sensor_init_2));
+ reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
+ reg_w_buf(gspca_dev, tp6810_cx_init_common,
+ ARRAY_SIZE(tp6810_cx_init_common));
+ reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4a, 0x7f);
+ reg_w(gspca_dev, 0x07, 0x05);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ } else {
+ reg_w(gspca_dev, 0x4a, 0xff);
+ reg_w(gspca_dev, 0x07, 0x85);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ }
+ setgamma(gspca_dev);
+ reg_w_buf(gspca_dev, tp6810_bridge_start,
+ ARRAY_SIZE(tp6810_bridge_start));
+ setsharpness(gspca_dev);
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
+ ARRAY_SIZE(color_gain[0]));
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
+ i2c_w_buf(gspca_dev, sensor_init_3, ARRAY_SIZE(sensor_init_3));
+ reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
+ i2c_w_buf(gspca_dev, sensor_init_4, ARRAY_SIZE(sensor_init_4));
+ reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
+ i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));
+
+ set_led(gspca_dev, 1);
+/* setquality(gspca_dev); */
+}
+
+static void soi763a_6800_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init[] = {
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
+
+ {TP6800_R50, 0x00},
+ {TP6800_R51, 0x00},
+ {TP6800_R52, 0xff},
+ {TP6800_R53, 0x03},
+ {TP6800_R54_DARK_CFG, 0x07},
+ {TP6800_R5C_EDGE_THRLD, 0x40},
+
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R7A_BLK_THRLD, 0x40},
+
+ {TP6800_R2F_TIMING_CFG, 0x46},
+ {TP6800_R30_SENSOR_CFG, 0x10}, /* BG1..G0R */
+ {TP6800_R37_FRONT_DARK_ST, 0x00},
+ {TP6800_R38_FRONT_DARK_END, 0x00},
+ {TP6800_R39_REAR_DARK_ST_L, 0x00},
+ {TP6800_R3A_REAR_DARK_ST_H, 0x00},
+ {TP6800_R3B_REAR_DARK_END_L, 0x00},
+ {TP6800_R3C_REAR_DARK_END_H, 0x00},
+ {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
+ {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
+ {TP6800_R21_ENDP_1_CTL, 0x03},
+
+ {TP6800_R3F_FRAME_RATE, 0x04}, /* 15 fps */
+ {TP6800_R5D_DEMOSAIC_CFG, 0x0e}, /* scale down - medium edge */
+
+ {TP6800_R31_PIXEL_START, 0x1b},
+ {TP6800_R32_PIXEL_END_L, 0x9a},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0f},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ {TP6800_R78_FORMAT, 0x01}, /* qvga */
+ {TP6800_R12_SIF_ADDR_S, 0x21}, /* soi763a i2c addr */
+ {TP6800_R1A_SIF_TX_DATA2, 0x00},
+ };
+ static const struct cmd sensor_init[] = {
+ {0x12, 0x48}, /* mirror - RGB */
+ {0x13, 0xa0}, /* clock - no AGC nor AEC */
+ {0x03, 0xa4}, /* saturation */
+ {0x04, 0x30}, /* hue */
+ {0x05, 0x88}, /* contrast */
+ {0x06, 0x60}, /* brightness */
+ {0x10, 0x41}, /* AEC */
+ {0x11, 0x40}, /* clock rate */
+ {0x13, 0xa0},
+ {0x14, 0x00}, /* 640x480 */
+ {0x15, 0x14},
+ {0x1f, 0x41},
+ {0x20, 0x80},
+ {0x23, 0xee},
+ {0x24, 0x50},
+ {0x25, 0x7a},
+ {0x26, 0x00},
+ {0x27, 0xe2},
+ {0x28, 0xb0},
+ {0x2a, 0x00},
+ {0x2b, 0x00},
+ {0x2d, 0x81},
+ {0x2f, 0x9d},
+ {0x60, 0x80},
+ {0x61, 0x00},
+ {0x62, 0x88},
+ {0x63, 0x11},
+ {0x64, 0x89},
+ {0x65, 0x00},
+ {0x67, 0x94},
+ {0x68, 0x7a},
+ {0x69, 0x0f},
+ {0x6c, 0x80},
+ {0x6d, 0x80},
+ {0x6e, 0x80},
+ {0x6f, 0xff},
+ {0x71, 0x20},
+ {0x74, 0x20},
+ {0x75, 0x86},
+ {0x77, 0xb5},
+ {0x17, 0x18}, /* H href start */
+ {0x18, 0xbf}, /* H href end */
+ {0x19, 0x03}, /* V start */
+ {0x1a, 0xf8}, /* V end */
+ {0x01, 0x80}, /* blue gain */
+ {0x02, 0x80}, /* red gain */
+ };
+
+ reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
+
+ i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(10);
+
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+
+ reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
+ reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
+
+ setsharpness(gspca_dev);
+
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
+ ARRAY_SIZE(color_gain[0]));
+
+ set_led(gspca_dev, 1);
+ setexposure(gspca_dev);
+ setquality(gspca_dev);
+ setgamma(gspca_dev);
+}
+
+static void soi763a_6810_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd bridge_init_2[] = {
+ {TP6800_R7A_BLK_THRLD, 0x00},
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ };
+ static const struct cmd bridge_init_3[] = {
+ {TP6800_R31_PIXEL_START, 0x20},
+ {TP6800_R32_PIXEL_END_L, 0x9f},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x13},
+ {TP6800_R35_LINE_END_L, 0xf8},
+ {TP6800_R36_LINE_END_H, 0x01},
+ };
+ static const struct cmd bridge_init_6[] = {
+ {0x08, 0xff},
+ {0x09, 0xff},
+ {0x0a, 0x5f},
+ {0x0b, 0x80},
+ };
+
+ reg_w(gspca_dev, 0x22, gspca_dev->alt);
+ bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
+ reg_w(gspca_dev, 0x59, 0x40);
+ setexposure(gspca_dev);
+ reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
+ reg_w_buf(gspca_dev, tp6810_ov_init_common,
+ ARRAY_SIZE(tp6810_ov_init_common));
+ reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4a, 0x7f);
+ reg_w(gspca_dev, 0x07, 0x05);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ } else {
+ reg_w(gspca_dev, 0x4a, 0xff);
+ reg_w(gspca_dev, 0x07, 0x85);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ }
+ setgamma(gspca_dev);
+ reg_w_buf(gspca_dev, tp6810_bridge_start,
+ ARRAY_SIZE(tp6810_bridge_start));
+
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4f, 0x00);
+ reg_w(gspca_dev, 0x4e, 0x7c);
+ }
+
+ reg_w(gspca_dev, 0x00, 0x00);
+
+ setsharpness(gspca_dev);
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
+ ARRAY_SIZE(color_gain[0]));
+ set_led(gspca_dev, 1);
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0);
+ setexposure(gspca_dev);
+ reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width);
+ set_dqt(gspca_dev, sd->quality);
+ if (sd->bridge == BRIDGE_TP6800) {
+ if (sd->sensor == SENSOR_CX0342)
+ cx0342_6800_start(gspca_dev);
+ else
+ soi763a_6800_start(gspca_dev);
+ } else {
+ if (sd->sensor == SENSOR_CX0342)
+ cx0342_6810_start(gspca_dev);
+ else
+ soi763a_6810_start(gspca_dev);
+ reg_w_buf(gspca_dev, tp6810_late_start,
+ ARRAY_SIZE(tp6810_late_start));
+ reg_w(gspca_dev, 0x80, 0x03);
+ reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e);
+
+ setexposure(gspca_dev);
+ setquality(gspca_dev);
+ setautogain(gspca_dev);
+ }
+
+ setframerate(gspca_dev);
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge == BRIDGE_TP6800)
+ reg_w(gspca_dev, TP6800_R2F_TIMING_CFG, 0x03);
+ set_led(gspca_dev, 0);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* the start of frame contains:
+ * ff d8
+ * ff fe
+ * width / 16
+ * height / 8
+ * quality
+ */
+ if (sd->bridge == BRIDGE_TP6810) {
+ if (*data != 0x5a) {
+/*fixme: don't discard the whole frame..*/
+ if (*data == 0xaa || *data == 0x00)
+ return;
+ if (*data > 0xc0) {
+ PDEBUG(D_FRAM, "bad frame");
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ }
+ data++;
+ len--;
+ if (*data == 0xff && data[1] == 0xd8) {
+/*fixme: there may be information in the 4 high bits*/
+ if ((data[6] & 0x0f) != sd->quality)
+ set_dqt(gspca_dev, data[6] & 0x0f);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 7, len - 7);
+ } else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) {
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, len);
+ } else {
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, len);
+ }
+ return;
+ }
+
+ switch (*data) {
+ case 0x55:
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, 0);
+
+ if (len < 8
+ || data[1] != 0xff || data[2] != 0xd8
+ || data[3] != 0xff || data[4] != 0xfe) {
+
+ /* Have only seen this with corrupt frames */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ if (data[7] != sd->quality)
+ set_dqt(gspca_dev, data[7]);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 8, len - 8);
+ break;
+ case 0xaa:
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ break;
+ case 0xcc:
+ if (data[1] != 0xff || data[2] != 0xd8)
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 1, len - 1);
+ else
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ break;
+ }
+}
+
+/* -- do autogain -- */
+/* gain setting is done in setexposure() for tp6810 */
+static void setgain(struct gspca_dev *gspca_dev) {}
+/* !! coarse_grained_expo_autogain is not used !! */
+#define exp_too_low_cnt bridge
+#define exp_too_high_cnt sensor
+
+#include "autogain_functions.h"
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, alen;
+ int luma, expo;
+
+ if (sd->ag_cnt < 0)
+ return;
+ if (--sd->ag_cnt > 5)
+ return;
+ switch (sd->ag_cnt) {
+/* case 5: */
+ default:
+ reg_w(gspca_dev, 0x7d, 0x00);
+ break;
+ case 4:
+ reg_w(gspca_dev, 0x27, 0xb0);
+ break;
+ case 3:
+ reg_w(gspca_dev, 0x0c, 0x01);
+ break;
+ case 2:
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x02),
+ gspca_dev->usb_buf,
+ 32,
+ &alen,
+ 500);
+ if (ret < 0) {
+ pr_err("bulk err %d\n", ret);
+ break;
+ }
+ /* values not used (unknown) */
+ break;
+ case 1:
+ reg_w(gspca_dev, 0x27, 0xd0);
+ break;
+ case 0:
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x02),
+ gspca_dev->usb_buf,
+ 32,
+ &alen,
+ 500);
+ if (ret < 0) {
+ pr_err("bulk err %d\n", ret);
+ break;
+ }
+ luma = ((gspca_dev->usb_buf[8] << 8) + gspca_dev->usb_buf[7] +
+ (gspca_dev->usb_buf[11] << 8) + gspca_dev->usb_buf[10] +
+ (gspca_dev->usb_buf[14] << 8) + gspca_dev->usb_buf[13] +
+ (gspca_dev->usb_buf[17] << 8) + gspca_dev->usb_buf[16] +
+ (gspca_dev->usb_buf[20] << 8) + gspca_dev->usb_buf[19] +
+ (gspca_dev->usb_buf[23] << 8) + gspca_dev->usb_buf[22] +
+ (gspca_dev->usb_buf[26] << 8) + gspca_dev->usb_buf[25] +
+ (gspca_dev->usb_buf[29] << 8) + gspca_dev->usb_buf[28])
+ / 8;
+ if (gspca_dev->width == 640)
+ luma /= 4;
+ reg_w(gspca_dev, 0x7d, 0x00);
+
+ expo = sd->ctrls[EXPOSURE].val;
+ ret = auto_gain_n_exposure(gspca_dev, luma,
+ 60, /* desired luma */
+ 6, /* dead zone */
+ 2, /* gain knee */
+ 70); /* expo knee */
+ sd->ag_cnt = AG_CNT_START;
+ if (sd->bridge == BRIDGE_TP6810) {
+ if ((expo >= 128 && sd->ctrls[EXPOSURE].val < 128)
+ || (expo < 128 && sd->ctrls[EXPOSURE].val >= 128))
+ setframerate(gspca_dev);
+ }
+ break;
+ }
+}
+
+/* get stream parameters (framerate) */
+static void sd_get_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ int fr, i;
+
+ cp->capability |= V4L2_CAP_TIMEPERFRAME;
+ tpf->numerator = 1;
+ i = get_fr_idx(gspca_dev);
+ if (i & 0x80) {
+ if (sd->bridge == BRIDGE_TP6800)
+ fr = rates[6 - (i & 0x07)];
+ else
+ fr = rates_6810[7 - (i & 0x07)];
+ } else {
+ fr = rates[6 - i];
+ }
+ tpf->denominator = fr;
+}
+
+/* set stream parameters (framerate) */
+static void sd_set_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ int fr, i;
+
+ sd->framerate = tpf->denominator / tpf->numerator;
+ if (gspca_dev->streaming)
+ setframerate(gspca_dev);
+
+ /* Return the actual framerate */
+ i = get_fr_idx(gspca_dev);
+ if (i & 0x80)
+ fr = rates_6810[7 - (i & 0x07)];
+ else
+ fr = rates[6 - i];
+ tpf->numerator = 1;
+ tpf->denominator = fr;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_SOI763A)
+ jpeg_set_qual(sd->jpeg_hdr, jcomp->quality);
+/* else
+ fixme: TODO
+*/
+ return gspca_dev->usb_err;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = jpeg_q[sd->quality];
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+static struct ctrl sd_ctrls[NCTRLS] = {
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0x01,
+ .maximum = 0xdc,
+ .step = 1,
+ .default_value = 0x4e,
+ },
+ .set_control = setexposure
+ },
+[QUALITY] = {
+ {
+ .id = V4L2_CID_PRIVATE_BASE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Compression quality",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 13,
+ },
+ .set_control = setquality
+ },
+[RGAIN] = {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red balance",
+ .minimum = 0,
+ .maximum = 4095,
+ .step = 1,
+ .default_value = 256,
+ },
+ .set_control = setrgain
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 4095,
+ .step = 1,
+ .default_value = 256,
+ },
+ .set = sd_setgain
+ },
+[BGAIN] = {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue balance",
+ .minimum = 0,
+ .maximum = 4095,
+ .step = 1,
+ .default_value = 256,
+ },
+ .set_control = setbgain
+ },
+[SHARPNESS] = {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 2,
+ },
+ .set_control = setsharpness
+ },
+[GAMMA] = {
+ {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = 0,
+ .maximum = NGAMMA - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setgamma
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = AUTOGAIN_DEF
+ },
+ .set_control = setautogain
+ },
+};
+
+static const struct sd_desc sd_desc = {
+ .name = KBUILD_MODNAME,
+ .ctrls = sd_ctrls,
+ .nctrls = NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_dq_callback,
+ .get_streamparm = sd_get_streamparm,
+ .set_streamparm = sd_set_streamparm,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06a2, 0x0003), .driver_info = BRIDGE_TP6800},
+ {USB_DEVICE(0x06a2, 0x6810), .driver_info = BRIDGE_TP6810},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(interface, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ return usb_register(&sd_driver);
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
+
+module_param(force_sensor, int, 0644);
+MODULE_PARM_DESC(force_sensor,
+ "Force sensor. 0: cx0342, 1: soi763a");
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
index 6caed734a06a..7ee2c8271dcc 100644
--- a/drivers/media/video/gspca/vc032x.c
+++ b/drivers/media/video/gspca/vc032x.c
@@ -20,6 +20,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "vc032x"
#include "gspca.h"
@@ -3169,7 +3171,7 @@ static void reg_r_i(struct gspca_dev *gspca_dev,
index, gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -3210,7 +3212,7 @@ static void reg_w_i(struct gspca_dev *gspca_dev,
value, index, NULL, 0,
500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -3235,8 +3237,7 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev,
reg_r(gspca_dev, 0xa1, 0xb33f, 1);
if (!(gspca_dev->usb_buf[0] & 0x02)) {
- err("I2c Bus Busy Wait %02x",
- gspca_dev->usb_buf[0]);
+ pr_err("I2c Bus Busy Wait %02x\n", gspca_dev->usb_buf[0]);
return 0;
}
reg_w(gspca_dev, 0xa0, address, 0xb33a);
@@ -3349,7 +3350,7 @@ static void i2c_write(struct gspca_dev *gspca_dev,
msleep(20);
} while (--retry > 0);
if (retry <= 0)
- err("i2c_write timeout");
+ pr_err("i2c_write timeout\n");
}
static void put_tab_to_reg(struct gspca_dev *gspca_dev,
@@ -3446,7 +3447,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
switch (sensor) {
case -1:
- err("Unknown sensor...");
+ pr_err("Unknown sensor...\n");
return -EINVAL;
case SENSOR_HV7131R:
PDEBUG(D_PROBE, "Find Sensor HV7131R");
diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c
index 84dfbab923b5..81dd4c99d025 100644
--- a/drivers/media/video/gspca/vicam.c
+++ b/drivers/media/video/gspca/vicam.c
@@ -26,6 +26,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "vicam"
#define HEADER_SIZE 64
@@ -117,7 +119,7 @@ static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, data, len, 1000);
if (ret < 0)
- err("control msg req %02X error %d", request, ret);
+ pr_err("control msg req %02X error %d\n", request, ret);
return ret;
}
@@ -189,8 +191,8 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
data, size, &act_len, 10000);
/* successful, it returns 0, otherwise negative */
if (ret < 0 || act_len != size) {
- err("bulk read fail (%d) len %d/%d",
- ret, act_len, size);
+ pr_err("bulk read fail (%d) len %d/%d\n",
+ ret, act_len, size);
return -EIO;
}
return 0;
@@ -216,7 +218,7 @@ static void vicam_dostream(struct work_struct *work)
HEADER_SIZE;
buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA);
if (!buffer) {
- err("Couldn't allocate USB buffer");
+ pr_err("Couldn't allocate USB buffer\n");
goto exit;
}
@@ -269,7 +271,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
ret = request_ihex_firmware(&fw, "vicam/firmware.fw",
&gspca_dev->dev->dev);
if (ret) {
- err("Failed to load \"vicam/firmware.fw\": %d\n", ret);
+ pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret);
return ret;
}
diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c
index 4a9e622e5e1b..27d2cef0692a 100644
--- a/drivers/media/video/gspca/w996Xcf.c
+++ b/drivers/media/video/gspca/w996Xcf.c
@@ -31,6 +31,8 @@
the sensor drivers to v4l2 sub drivers, and properly split of this
driver from ov519.c */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */
#define Y_QUANTABLE (&sd->jpeg_hdr[JPEG_QT0_OFFSET])
@@ -81,7 +83,7 @@ static void w9968cf_write_fsb(struct sd *sd, u16* data)
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
value, 0x06, sd->gspca_dev.usb_buf, 6, 500);
if (ret < 0) {
- err("Write FSB registers failed (%d)", ret);
+ pr_err("Write FSB registers failed (%d)\n", ret);
sd->gspca_dev.usb_err = ret;
}
}
@@ -108,7 +110,7 @@ static void w9968cf_write_sb(struct sd *sd, u16 value)
udelay(W9968CF_I2C_BUS_DELAY);
if (ret < 0) {
- err("Write SB reg [01] %04x failed", value);
+ pr_err("Write SB reg [01] %04x failed\n", value);
sd->gspca_dev.usb_err = ret;
}
}
@@ -135,7 +137,7 @@ static int w9968cf_read_sb(struct sd *sd)
ret = sd->gspca_dev.usb_buf[0] |
(sd->gspca_dev.usb_buf[1] << 8);
} else {
- err("Read SB reg [01] failed");
+ pr_err("Read SB reg [01] failed\n");
sd->gspca_dev.usb_err = ret;
}
diff --git a/drivers/media/video/gspca/xirlink_cit.c b/drivers/media/video/gspca/xirlink_cit.c
index c089a0f6f1d0..3aed42acdb5a 100644
--- a/drivers/media/video/gspca/xirlink_cit.c
+++ b/drivers/media/video/gspca/xirlink_cit.c
@@ -27,6 +27,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "xirlink-cit"
#include <linux/input.h>
@@ -800,8 +802,8 @@ static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
value, index, NULL, 0, 1000);
if (err < 0)
- err("Failed to write a register (index 0x%04X,"
- " value 0x%02X, error %d)", index, value, err);
+ pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
+ index, value, err);
return 0;
}
@@ -816,8 +818,8 @@ static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index, int verbose)
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
0x00, index, buf, 8, 1000);
if (res < 0) {
- err("Failed to read a register (index 0x%04X, error %d)",
- index, res);
+ pr_err("Failed to read a register (index 0x%04X, error %d)\n",
+ index, res);
return res;
}
@@ -1587,7 +1589,7 @@ static int cit_get_packet_size(struct gspca_dev *gspca_dev)
intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
alt = usb_altnum_to_altsetting(intf, gspca_dev->alt);
if (!alt) {
- err("Couldn't get altsetting");
+ pr_err("Couldn't get altsetting\n");
return -EIO;
}
@@ -2824,7 +2826,7 @@ static int sd_isoc_nego(struct gspca_dev *gspca_dev)
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
if (ret < 0)
- err("set alt 1 err %d", ret);
+ pr_err("set alt 1 err %d\n", ret);
return ret;
}
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 61cdd56a74a9..30ea1e479492 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "zc3xx"
#include <linux/input.h>
@@ -5666,7 +5668,7 @@ static u8 reg_r_i(struct gspca_dev *gspca_dev,
index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_r_i err %d", ret);
+ pr_err("reg_r_i err %d\n", ret);
gspca_dev->usb_err = ret;
return 0;
}
@@ -5698,7 +5700,7 @@ static void reg_w_i(struct gspca_dev *gspca_dev,
value, index, NULL, 0,
500);
if (ret < 0) {
- err("reg_w_i err %d", ret);
+ pr_err("reg_w_i err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -5724,7 +5726,7 @@ static u16 i2c_read(struct gspca_dev *gspca_dev,
msleep(20);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
if (retbyte != 0x00)
- err("i2c_r status error %02x", retbyte);
+ pr_err("i2c_r status error %02x\n", retbyte);
retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */
retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */
PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)",
@@ -5748,7 +5750,7 @@ static u8 i2c_write(struct gspca_dev *gspca_dev,
msleep(1);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
if (retbyte != 0x00)
- err("i2c_w status error %02x", retbyte);
+ pr_err("i2c_w status error %02x\n", retbyte);
PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)",
reg, valH, valL, retbyte);
return retbyte;
@@ -6497,7 +6499,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "Sensor GC0303");
break;
default:
- warn("Unknown sensor - set to TAS5130C");
+ pr_warn("Unknown sensor - set to TAS5130C\n");
sd->sensor = SENSOR_TAS5130C;
}
break;
@@ -6603,7 +6605,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
sd->sensor = SENSOR_OV7620; /* same sensor (?) */
break;
default:
- err("Unknown sensor %04x", sensor);
+ pr_err("Unknown sensor %04x\n", sensor);
return -EINVAL;
}
}
@@ -6970,6 +6972,7 @@ static const struct sd_desc sd_desc = {
};
static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x03f0, 0x1b07)},
{USB_DEVICE(0x041e, 0x041e)},
{USB_DEVICE(0x041e, 0x4017)},
{USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106},
diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile
index 3baa9f613ca3..52f057f24e39 100644
--- a/drivers/media/video/hdpvr/Makefile
+++ b/drivers/media/video/hdpvr/Makefile
@@ -2,6 +2,6 @@ hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o
obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o
-EXTRA_CFLAGS += -Idrivers/media/video
+ccflags-y += -Idrivers/media/video
-EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
index a27d93b503a5..441dacf642bb 100644
--- a/drivers/media/video/hdpvr/hdpvr-core.c
+++ b/drivers/media/video/hdpvr/hdpvr-core.c
@@ -17,7 +17,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
@@ -474,5 +474,6 @@ module_init(hdpvr_init);
module_exit(hdpvr_exit);
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.2.1");
MODULE_AUTHOR("Janne Grunau");
MODULE_DESCRIPTION("Hauppauge HD PVR driver");
diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c
index 2a1ac287591d..82e819fa91c0 100644
--- a/drivers/media/video/hdpvr/hdpvr-i2c.c
+++ b/drivers/media/video/hdpvr/hdpvr-i2c.c
@@ -17,6 +17,7 @@
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "hdpvr.h"
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
index 514aea76eaa5..087f7c08cb85 100644
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -17,7 +17,6 @@
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/mutex.h>
-#include <linux/version.h>
#include <linux/workqueue.h>
#include <linux/videodev2.h>
@@ -574,7 +573,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strcpy(cap->driver, "hdpvr");
strcpy(cap->card, "Hauppauge HD PVR");
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->version = HDPVR_VERSION;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_AUDIO |
V4L2_CAP_READWRITE;
diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h
index 072f23c570f3..d6439db1d18b 100644
--- a/drivers/media/video/hdpvr/hdpvr.h
+++ b/drivers/media/video/hdpvr/hdpvr.h
@@ -18,12 +18,6 @@
#include <media/v4l2-device.h>
#include <media/ir-kbd-i2c.h>
-#define HDPVR_MAJOR_VERSION 0
-#define HDPVR_MINOR_VERSION 2
-#define HDPVR_RELEASE 0
-#define HDPVR_VERSION \
- KERNEL_VERSION(HDPVR_MAJOR_VERSION, HDPVR_MINOR_VERSION, HDPVR_RELEASE)
-
#define HDPVR_MAX 8
#define HDPVR_I2C_MAX_SIZE 128
diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c
index cbc505a2fc29..a62322d5c0d8 100644
--- a/drivers/media/video/hexium_gemini.c
+++ b/drivers/media/video/hexium_gemini.c
@@ -21,9 +21,12 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define DEBUG_VARIABLE debug
#include <media/saa7146_vv.h>
+#include <linux/module.h>
static int debug;
module_param(debug, int, 0);
@@ -175,13 +178,14 @@ static int hexium_init_done(struct saa7146_dev *dev)
union i2c_smbus_data data;
int i = 0;
- DEB_D(("hexium_init_done called.\n"));
+ DEB_D("hexium_init_done called\n");
/* initialize the helper ics to useful values */
for (i = 0; i < sizeof(hexium_ks0127b); i++) {
data.byte = hexium_ks0127b[i];
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
- printk("hexium_gemini: hexium_init_done() failed for address 0x%02x\n", i);
+ pr_err("hexium_init_done() failed for address 0x%02x\n",
+ i);
}
}
@@ -192,7 +196,7 @@ static int hexium_set_input(struct hexium *hexium, int input)
{
union i2c_smbus_data data;
- DEB_D((".\n"));
+ DEB_D("\n");
data.byte = hexium_input_select[input].byte;
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
@@ -207,12 +211,13 @@ static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
union i2c_smbus_data data;
int i = 0;
- DEB_D((".\n"));
+ DEB_D("\n");
while (vdec[i].adr != -1) {
data.byte = vdec[i].byte;
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
- printk("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", i);
+ pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n",
+ i);
return -1;
}
i++;
@@ -222,14 +227,14 @@ static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
{
- DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
if (i->index >= HEXIUM_INPUTS)
return -EINVAL;
memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
- DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
+ DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
return 0;
}
@@ -240,7 +245,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
*input = hexium->cur_input;
- DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
+ DEB_D("VIDIOC_G_INPUT: %d\n", *input);
return 0;
}
@@ -249,7 +254,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct hexium *hexium = (struct hexium *) dev->ext_priv;
- DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
+ DEB_EE("VIDIOC_S_INPUT %d\n", input);
if (input >= HEXIUM_INPUTS)
return -EINVAL;
@@ -270,7 +275,7 @@ static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *
for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
if (hexium_controls[i].id == qc->id) {
*qc = hexium_controls[i];
- DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
+ DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id);
return 0;
}
}
@@ -293,7 +298,7 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
if (vc->id == V4L2_CID_PRIVATE_BASE) {
vc->value = hexium->cur_bw;
- DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value));
+ DEB_D("VIDIOC_G_CTRL BW:%d\n", vc->value);
return 0;
}
return -EINVAL;
@@ -316,7 +321,7 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
if (vc->id == V4L2_CID_PRIVATE_BASE)
hexium->cur_bw = vc->value;
- DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw));
+ DEB_D("VIDIOC_S_CTRL BW:%d\n", hexium->cur_bw);
if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
hexium_set_standard(hexium, hexium_pal);
@@ -351,14 +356,14 @@ static struct saa7146_ext_vv vv_data;
/* this function only gets called when the probing was successful */
static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
{
- struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ struct hexium *hexium;
int ret;
- DEB_EE((".\n"));
+ DEB_EE("\n");
hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
if (NULL == hexium) {
- printk("hexium_gemini: not enough kernel memory in hexium_attach().\n");
+ pr_err("not enough kernel memory in hexium_attach()\n");
return -ENOMEM;
}
dev->ext_priv = hexium;
@@ -371,7 +376,7 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
};
saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
- DEB_S(("cannot register i2c-device. skipping.\n"));
+ DEB_S("cannot register i2c-device. skipping.\n");
kfree(hexium);
return -EFAULT;
}
@@ -402,11 +407,11 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
vv_data.ops.vidioc_s_input = vidioc_s_input;
ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER);
if (ret < 0) {
- printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n");
+ pr_err("cannot register capture v4l2 device. skipping.\n");
return ret;
}
- printk("hexium_gemini: found 'hexium gemini' frame grabber-%d.\n", hexium_num);
+ pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num);
hexium_num++;
return 0;
@@ -416,7 +421,7 @@ static int hexium_detach(struct saa7146_dev *dev)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
- DEB_EE(("dev:%p\n", dev));
+ DEB_EE("dev:%p\n", dev);
saa7146_unregister_device(&hexium->video_dev, dev);
saa7146_vv_release(dev);
@@ -508,7 +513,7 @@ static struct saa7146_extension hexium_extension = {
static int __init hexium_init_module(void)
{
if (0 != saa7146_register_extension(&hexium_extension)) {
- DEB_S(("failed to register extension.\n"));
+ DEB_S("failed to register extension\n");
return -ENODEV;
}
diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c
index 6ad7e1c8b922..23debc967d94 100644
--- a/drivers/media/video/hexium_orion.c
+++ b/drivers/media/video/hexium_orion.c
@@ -21,9 +21,12 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define DEBUG_VARIABLE debug
#include <media/saa7146_vv.h>
+#include <linux/module.h>
static int debug;
module_param(debug, int, 0);
@@ -209,7 +212,7 @@ static int hexium_probe(struct saa7146_dev *dev)
union i2c_smbus_data data;
int err = 0;
- DEB_EE((".\n"));
+ DEB_EE("\n");
/* there are no hexium orion cards with revision 0 saa7146s */
if (0 == dev->revision) {
@@ -218,7 +221,7 @@ static int hexium_probe(struct saa7146_dev *dev)
hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
if (NULL == hexium) {
- printk("hexium_orion: hexium_probe: not enough kernel memory.\n");
+ pr_err("hexium_probe: not enough kernel memory\n");
return -ENOMEM;
}
@@ -234,7 +237,7 @@ static int hexium_probe(struct saa7146_dev *dev)
};
saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
- DEB_S(("cannot register i2c-device. skipping.\n"));
+ DEB_S("cannot register i2c-device. skipping.\n");
kfree(hexium);
return -EFAULT;
}
@@ -248,7 +251,7 @@ static int hexium_probe(struct saa7146_dev *dev)
/* detect newer Hexium Orion cards by subsystem ids */
if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) {
- printk("hexium_orion: device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs.\n");
+ pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n");
/* we store the pointer in our private data field */
dev->ext_priv = hexium;
hexium->type = HEXIUM_ORION_1SVHS_3BNC;
@@ -256,7 +259,7 @@ static int hexium_probe(struct saa7146_dev *dev)
}
if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) {
- printk("hexium_orion: device is a Hexium Orion w/ 4 BNC inputs.\n");
+ pr_info("device is a Hexium Orion w/ 4 BNC inputs\n");
/* we store the pointer in our private data field */
dev->ext_priv = hexium;
hexium->type = HEXIUM_ORION_4BNC;
@@ -266,7 +269,7 @@ static int hexium_probe(struct saa7146_dev *dev)
/* check if this is an old hexium Orion card by looking at
a saa7110 at address 0x4e */
if (0 == (err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, &data))) {
- printk("hexium_orion: device is a Hexium HV-PCI6/Orion (old).\n");
+ pr_info("device is a Hexium HV-PCI6/Orion (old)\n");
/* we store the pointer in our private data field */
dev->ext_priv = hexium;
hexium->type = HEXIUM_HV_PCI6_ORION;
@@ -288,13 +291,13 @@ static int hexium_init_done(struct saa7146_dev *dev)
union i2c_smbus_data data;
int i = 0;
- DEB_D(("hexium_init_done called.\n"));
+ DEB_D("hexium_init_done called\n");
/* initialize the helper ics to useful values */
for (i = 0; i < sizeof(hexium_saa7110); i++) {
data.byte = hexium_saa7110[i];
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
- printk("hexium_orion: failed for address 0x%02x\n", i);
+ pr_err("failed for address 0x%02x\n", i);
}
}
@@ -306,7 +309,7 @@ static int hexium_set_input(struct hexium *hexium, int input)
union i2c_smbus_data data;
int i = 0;
- DEB_D((".\n"));
+ DEB_D("\n");
for (i = 0; i < 8; i++) {
int adr = hexium_input_select[input].data[i].adr;
@@ -314,7 +317,7 @@ static int hexium_set_input(struct hexium *hexium, int input)
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) {
return -1;
}
- printk("%d: 0x%02x => 0x%02x\n",input, adr,data.byte);
+ pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte);
}
return 0;
@@ -322,14 +325,14 @@ static int hexium_set_input(struct hexium *hexium, int input)
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
{
- DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
if (i->index >= HEXIUM_INPUTS)
return -EINVAL;
memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
- DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
+ DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
return 0;
}
@@ -340,7 +343,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
*input = hexium->cur_input;
- DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
+ DEB_D("VIDIOC_G_INPUT: %d\n", *input);
return 0;
}
@@ -365,18 +368,18 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
- DEB_EE((".\n"));
+ DEB_EE("\n");
saa7146_vv_init(dev, &vv_data);
vv_data.ops.vidioc_enum_input = vidioc_enum_input;
vv_data.ops.vidioc_g_input = vidioc_g_input;
vv_data.ops.vidioc_s_input = vidioc_s_input;
if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) {
- printk("hexium_orion: cannot register capture v4l2 device. skipping.\n");
+ pr_err("cannot register capture v4l2 device. skipping.\n");
return -1;
}
- printk("hexium_orion: found 'hexium orion' frame grabber-%d.\n", hexium_num);
+ pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num);
hexium_num++;
/* the rest */
@@ -390,7 +393,7 @@ static int hexium_detach(struct saa7146_dev *dev)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
- DEB_EE(("dev:%p\n", dev));
+ DEB_EE("dev:%p\n", dev);
saa7146_unregister_device(&hexium->video_dev, dev);
saa7146_vv_release(dev);
@@ -479,7 +482,7 @@ static struct saa7146_extension extension = {
static int __init hexium_init_module(void)
{
if (0 != saa7146_register_extension(&extension)) {
- DEB_S(("failed to register extension.\n"));
+ DEB_S("failed to register extension\n");
return -ENODEV;
}
diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c
index 0382ea752e6f..eec75bb57203 100644
--- a/drivers/media/video/imx074.c
+++ b/drivers/media/video/imx074.c
@@ -12,11 +12,12 @@
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
+#include <linux/module.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
@@ -267,6 +268,17 @@ static int imx074_g_chip_ident(struct v4l2_subdev *sd,
return 0;
}
+static int imx074_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->type = V4L2_MBUS_CSI2;
+ cfg->flags = V4L2_MBUS_CSI2_2_LANE |
+ V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
.s_stream = imx074_s_stream,
.s_mbus_fmt = imx074_s_fmt,
@@ -275,6 +287,7 @@ static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
.enum_mbus_fmt = imx074_enum_fmt,
.g_crop = imx074_g_crop,
.cropcap = imx074_cropcap,
+ .g_mbus_config = imx074_g_mbus_config,
};
static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
@@ -286,28 +299,7 @@ static struct v4l2_subdev_ops imx074_subdev_ops = {
.video = &imx074_subdev_video_ops,
};
-/*
- * We have to provide soc-camera operations, but we don't have anything to say
- * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param
- */
-static unsigned long imx074_query_bus_param(struct soc_camera_device *icd)
-{
- return 0;
-}
-
-static int imx074_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return -EINVAL;
-}
-
-static struct soc_camera_ops imx074_ops = {
- .query_bus_param = imx074_query_bus_param,
- .set_bus_param = imx074_set_bus_param,
-};
-
-static int imx074_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int imx074_video_probe(struct i2c_client *client)
{
int ret;
u16 id;
@@ -417,17 +409,10 @@ static int imx074_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct imx074 *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "IMX074: missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "IMX074: missing platform data!\n");
return -EINVAL;
@@ -445,12 +430,10 @@ static int imx074_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
- icd->ops = &imx074_ops;
priv->fmt = &imx074_colour_fmts[0];
- ret = imx074_video_probe(icd, client);
+ ret = imx074_video_probe(client);
if (ret < 0) {
- icd->ops = NULL;
kfree(priv);
return ret;
}
@@ -461,10 +444,8 @@ static int imx074_probe(struct i2c_client *client,
static int imx074_remove(struct i2c_client *client)
{
struct imx074 *priv = to_imx074(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- icd->ops = NULL;
if (icl->free_bus)
icl->free_bus(icl);
kfree(priv);
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
index 26ce0d6eaee1..71ab76a5ab26 100644
--- a/drivers/media/video/ivtv/Makefile
+++ b/drivers/media/video/ivtv/Makefile
@@ -7,8 +7,8 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \
obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 0fb75524484d..41108a9a195e 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -1180,6 +1180,8 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
setup.addr = ADDR_UNSET;
setup.type = itv->options.tuner;
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ if (itv->options.radio > 0)
+ setup.mode_mask |= T_RADIO;
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
ivtv_reset_tuner_gpio : NULL;
ivtv_call_all(itv, tuner, s_type_addr, &setup);
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 84bdf0f42a8e..8f9cc17b518e 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -36,7 +36,6 @@
* using information provided by Jiun-Kuei Jung @ AVerMedia.
*/
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index a7f54b010a5c..38f052257f46 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -722,8 +722,8 @@ unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
/* If there are subscribed events, then only use the new event
API instead of the old video.h based API. */
- if (!list_empty(&id->fh.events->subscribed)) {
- poll_wait(filp, &id->fh.events->wait, wait);
+ if (!list_empty(&id->fh.subscribed)) {
+ poll_wait(filp, &id->fh.wait, wait);
/* Turn off the old-style vsync events */
clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
if (v4l2_event_pending(&id->fh))
@@ -750,6 +750,7 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait)
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ unsigned res = 0;
/* Start a capture if there is none */
if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
@@ -769,12 +770,16 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait)
/* add stream's waitq to the poll list */
IVTV_DEBUG_HI_FILE("Encoder poll\n");
poll_wait(filp, &s->waitq, wait);
+ if (v4l2_event_pending(&id->fh))
+ res |= POLLPRI;
+ else
+ poll_wait(filp, &id->fh.wait, wait);
if (s->q_full.length || s->q_io.length)
- return POLLIN | POLLRDNORM;
+ return res | POLLIN | POLLRDNORM;
if (eof)
- return POLLHUP;
- return 0;
+ return res | POLLHUP;
+ return res;
}
void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
@@ -961,10 +966,6 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
return -ENOMEM;
}
v4l2_fh_init(&item->fh, s->vdev);
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV ||
- s->type == IVTV_DEC_STREAM_TYPE_MPG) {
- res = v4l2_event_alloc(&item->fh, 60);
- }
if (res < 0) {
v4l2_fh_exit(&item->fh);
kfree(item);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 120c7d8e0895..ecafa697326e 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -757,7 +757,6 @@ static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vc
strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev));
- vcap->version = IVTV_DRIVER_VERSION; /* version */
vcap->capabilities = itv->v4l2_cap; /* capabilities */
return 0;
}
@@ -1204,9 +1203,7 @@ static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced
cap->service_lines[f][l] = set;
}
}
- return 0;
- }
- if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+ } else if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
return -EINVAL;
if (itv->is_60hz) {
@@ -1216,9 +1213,16 @@ static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced
cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
cap->service_lines[0][16] = V4L2_SLICED_VPS;
}
- return 0;
+ } else {
+ return -EINVAL;
}
- return -EINVAL;
+
+ set = 0;
+ for (f = 0; f < 2; f++)
+ for (l = 0; l < 24; l++)
+ set |= cap->service_lines[f][l];
+ cap->service_set = set;
+ return 0;
}
static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx)
@@ -1451,11 +1455,11 @@ static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscripti
switch (sub->type) {
case V4L2_EVENT_VSYNC:
case V4L2_EVENT_EOS:
- break;
+ case V4L2_EVENT_CTRL:
+ return v4l2_event_subscribe(fh, sub, 0);
default:
return -EINVAL;
}
- return v4l2_event_subscribe(fh, sub);
}
static int ivtv_log_status(struct file *file, void *fh)
diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h
index b67a4048f5aa..a20f346fcad8 100644
--- a/drivers/media/video/ivtv/ivtv-version.h
+++ b/drivers/media/video/ivtv/ivtv-version.h
@@ -21,11 +21,6 @@
#define IVTV_VERSION_H
#define IVTV_DRIVER_NAME "ivtv"
-#define IVTV_DRIVER_VERSION_MAJOR 1
-#define IVTV_DRIVER_VERSION_MINOR 4
-#define IVTV_DRIVER_VERSION_PATCHLEVEL 2
-
-#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL)
-#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL)
+#define IVTV_VERSION "1.4.3"
#endif
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
index d9471928369d..3248ac805711 100644
--- a/drivers/media/video/m5mols/m5mols_capture.c
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -18,11 +18,9 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <linux/version.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/videodev2.h>
-#include <linux/version.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index 43c68f51c5ce..05ab3700647e 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -18,10 +18,10 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <linux/version.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/videodev2.h>
+#include <linux/module.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
@@ -937,7 +937,7 @@ static int __devinit m5mols_probe(struct i2c_client *client,
return -EINVAL;
}
- if (!pdata->irq) {
+ if (!client->irq) {
dev_err(&client->dev, "Interrupt not assigned\n");
return -EINVAL;
}
@@ -974,7 +974,7 @@ static int __devinit m5mols_probe(struct i2c_client *client,
init_waitqueue_head(&info->irq_waitq);
INIT_WORK(&info->work_irq, m5mols_irq_work);
- ret = request_irq(pdata->irq, m5mols_irq_handler,
+ ret = request_irq(client->irq, m5mols_irq_handler,
IRQF_TRIGGER_RISING, MODULE_NAME, sd);
if (ret) {
dev_err(&client->dev, "Interrupt request failed: %d\n", ret);
@@ -999,7 +999,7 @@ static int __devexit m5mols_remove(struct i2c_client *client)
struct m5mols_info *info = to_m5mols(sd);
v4l2_device_unregister_subdev(sd);
- free_irq(info->pdata->irq, sd);
+ free_irq(client->irq, sd);
regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
gpio_free(info->pdata->gpio_reset);
diff --git a/drivers/media/video/marvell-ccic/Kconfig b/drivers/media/video/marvell-ccic/Kconfig
new file mode 100644
index 000000000000..bf739e3b3398
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/Kconfig
@@ -0,0 +1,23 @@
+config VIDEO_CAFE_CCIC
+ tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
+ depends on PCI && I2C && VIDEO_V4L2
+ select VIDEO_OV7670
+ select VIDEOBUF2_VMALLOC
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a video4linux2 driver for the Marvell 88ALP01 integrated
+ CMOS camera controller. This is the controller found on first-
+ generation OLPC systems.
+
+config VIDEO_MMP_CAMERA
+ tristate "Marvell Armada 610 integrated camera controller support"
+ depends on ARCH_MMP && I2C && VIDEO_V4L2
+ select VIDEO_OV7670
+ select I2C_GPIO
+ select VIDEOBUF2_DMA_SG
+ ---help---
+ This is a Video4Linux2 driver for the integrated camera
+ controller found on Marvell Armada 610 application
+ processors (and likely beyond). This is the controller found
+ in OLPC XO 1.75 systems.
+
diff --git a/drivers/media/video/marvell-ccic/Makefile b/drivers/media/video/marvell-ccic/Makefile
new file mode 100644
index 000000000000..05a792c579a2
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
+cafe_ccic-y := cafe-driver.o mcam-core.o
+
+obj-$(CONFIG_VIDEO_MMP_CAMERA) += mmp_camera.o
+mmp_camera-y := mmp-driver.o mcam-core.o
+
diff --git a/drivers/media/video/marvell-ccic/cafe-driver.c b/drivers/media/video/marvell-ccic/cafe-driver.c
new file mode 100644
index 000000000000..d030f9beae88
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/cafe-driver.c
@@ -0,0 +1,654 @@
+/*
+ * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe"
+ * multifunction chip. Currently works with the Omnivision OV7670
+ * sensor.
+ *
+ * The data sheet for this device can be found at:
+ * http://www.marvell.com/products/pc_connectivity/88alp01/
+ *
+ * Copyright 2006-11 One Laptop Per Child Association, Inc.
+ * Copyright 2006-11 Jonathan Corbet <corbet@lwn.net>
+ *
+ * Written by Jonathan Corbet, corbet@lwn.net.
+ *
+ * v4l2_device/v4l2_subdev conversion by:
+ * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "mcam-core.h"
+
+#define CAFE_VERSION 0x000002
+
+
+/*
+ * Parameters.
+ */
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("Video");
+
+
+
+
+struct cafe_camera {
+ int registered; /* Fully initialized? */
+ struct mcam_camera mcam;
+ struct pci_dev *pdev;
+ wait_queue_head_t smbus_wait; /* Waiting on i2c events */
+};
+
+/*
+ * Most of the camera controller registers are defined in mcam-core.h,
+ * but the Cafe platform has some additional registers of its own;
+ * they are described here.
+ */
+
+/*
+ * "General purpose register" has a couple of GPIOs used for sensor
+ * power and reset on OLPC XO 1.0 systems.
+ */
+#define REG_GPR 0xb4
+#define GPR_C1EN 0x00000020 /* Pad 1 (power down) enable */
+#define GPR_C0EN 0x00000010 /* Pad 0 (reset) enable */
+#define GPR_C1 0x00000002 /* Control 1 value */
+/*
+ * Control 0 is wired to reset on OLPC machines. For ov7x sensors,
+ * it is active low.
+ */
+#define GPR_C0 0x00000001 /* Control 0 value */
+
+/*
+ * These registers control the SMBUS module for communicating
+ * with the sensor.
+ */
+#define REG_TWSIC0 0xb8 /* TWSI (smbus) control 0 */
+#define TWSIC0_EN 0x00000001 /* TWSI enable */
+#define TWSIC0_MODE 0x00000002 /* 1 = 16-bit, 0 = 8-bit */
+#define TWSIC0_SID 0x000003fc /* Slave ID */
+/*
+ * Subtle trickery: the slave ID field starts with bit 2. But the
+ * Linux i2c stack wants to treat the bottommost bit as a separate
+ * read/write bit, which is why slave ID's are usually presented
+ * >>1. For consistency with that behavior, we shift over three
+ * bits instead of two.
+ */
+#define TWSIC0_SID_SHIFT 3
+#define TWSIC0_CLKDIV 0x0007fc00 /* Clock divider */
+#define TWSIC0_MASKACK 0x00400000 /* Mask ack from sensor */
+#define TWSIC0_OVMAGIC 0x00800000 /* Make it work on OV sensors */
+
+#define REG_TWSIC1 0xbc /* TWSI control 1 */
+#define TWSIC1_DATA 0x0000ffff /* Data to/from camchip */
+#define TWSIC1_ADDR 0x00ff0000 /* Address (register) */
+#define TWSIC1_ADDR_SHIFT 16
+#define TWSIC1_READ 0x01000000 /* Set for read op */
+#define TWSIC1_WSTAT 0x02000000 /* Write status */
+#define TWSIC1_RVALID 0x04000000 /* Read data valid */
+#define TWSIC1_ERROR 0x08000000 /* Something screwed up */
+
+/*
+ * Here's the weird global control registers
+ */
+#define REG_GL_CSR 0x3004 /* Control/status register */
+#define GCSR_SRS 0x00000001 /* SW Reset set */
+#define GCSR_SRC 0x00000002 /* SW Reset clear */
+#define GCSR_MRS 0x00000004 /* Master reset set */
+#define GCSR_MRC 0x00000008 /* HW Reset clear */
+#define GCSR_CCIC_EN 0x00004000 /* CCIC Clock enable */
+#define REG_GL_IMASK 0x300c /* Interrupt mask register */
+#define GIMSK_CCIC_EN 0x00000004 /* CCIC Interrupt enable */
+
+#define REG_GL_FCR 0x3038 /* GPIO functional control register */
+#define GFCR_GPIO_ON 0x08 /* Camera GPIO enabled */
+#define REG_GL_GPIOR 0x315c /* GPIO register */
+#define GGPIO_OUT 0x80000 /* GPIO output */
+#define GGPIO_VAL 0x00008 /* Output pin value */
+
+#define REG_LEN (REG_GL_IMASK + 4)
+
+
+/*
+ * Debugging and related.
+ */
+#define cam_err(cam, fmt, arg...) \
+ dev_err(&(cam)->pdev->dev, fmt, ##arg);
+#define cam_warn(cam, fmt, arg...) \
+ dev_warn(&(cam)->pdev->dev, fmt, ##arg);
+
+/* -------------------------------------------------------------------- */
+/*
+ * The I2C/SMBUS interface to the camera itself starts here. The
+ * controller handles SMBUS itself, presenting a relatively simple register
+ * interface; all we have to do is to tell it where to route the data.
+ */
+#define CAFE_SMBUS_TIMEOUT (HZ) /* generous */
+
+static inline struct cafe_camera *to_cam(struct v4l2_device *dev)
+{
+ struct mcam_camera *m = container_of(dev, struct mcam_camera, v4l2_dev);
+ return container_of(m, struct cafe_camera, mcam);
+}
+
+
+static int cafe_smbus_write_done(struct mcam_camera *mcam)
+{
+ unsigned long flags;
+ int c1;
+
+ /*
+ * We must delay after the interrupt, or the controller gets confused
+ * and never does give us good status. Fortunately, we don't do this
+ * often.
+ */
+ udelay(20);
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ c1 = mcam_reg_read(mcam, REG_TWSIC1);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+ return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT;
+}
+
+static int cafe_smbus_write_data(struct cafe_camera *cam,
+ u16 addr, u8 command, u8 value)
+{
+ unsigned int rval;
+ unsigned long flags;
+ struct mcam_camera *mcam = &cam->mcam;
+
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
+ rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
+ /*
+ * Marvell sez set clkdiv to all 1's for now.
+ */
+ rval |= TWSIC0_CLKDIV;
+ mcam_reg_write(mcam, REG_TWSIC0, rval);
+ (void) mcam_reg_read(mcam, REG_TWSIC1); /* force write */
+ rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
+ mcam_reg_write(mcam, REG_TWSIC1, rval);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+ /* Unfortunately, reading TWSIC1 too soon after sending a command
+ * causes the device to die.
+ * Use a busy-wait because we often send a large quantity of small
+ * commands at-once; using msleep() would cause a lot of context
+ * switches which take longer than 2ms, resulting in a noticeable
+ * boot-time and capture-start delays.
+ */
+ mdelay(2);
+
+ /*
+ * Another sad fact is that sometimes, commands silently complete but
+ * cafe_smbus_write_done() never becomes aware of this.
+ * This happens at random and appears to possible occur with any
+ * command.
+ * We don't understand why this is. We work around this issue
+ * with the timeout in the wait below, assuming that all commands
+ * complete within the timeout.
+ */
+ wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(mcam),
+ CAFE_SMBUS_TIMEOUT);
+
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ rval = mcam_reg_read(mcam, REG_TWSIC1);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+ if (rval & TWSIC1_WSTAT) {
+ cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr,
+ command, value);
+ return -EIO;
+ }
+ if (rval & TWSIC1_ERROR) {
+ cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr,
+ command, value);
+ return -EIO;
+ }
+ return 0;
+}
+
+
+
+static int cafe_smbus_read_done(struct mcam_camera *mcam)
+{
+ unsigned long flags;
+ int c1;
+
+ /*
+ * We must delay after the interrupt, or the controller gets confused
+ * and never does give us good status. Fortunately, we don't do this
+ * often.
+ */
+ udelay(20);
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ c1 = mcam_reg_read(mcam, REG_TWSIC1);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+ return c1 & (TWSIC1_RVALID|TWSIC1_ERROR);
+}
+
+
+
+static int cafe_smbus_read_data(struct cafe_camera *cam,
+ u16 addr, u8 command, u8 *value)
+{
+ unsigned int rval;
+ unsigned long flags;
+ struct mcam_camera *mcam = &cam->mcam;
+
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
+ rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
+ /*
+ * Marvel sez set clkdiv to all 1's for now.
+ */
+ rval |= TWSIC0_CLKDIV;
+ mcam_reg_write(mcam, REG_TWSIC0, rval);
+ (void) mcam_reg_read(mcam, REG_TWSIC1); /* force write */
+ rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
+ mcam_reg_write(mcam, REG_TWSIC1, rval);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+ wait_event_timeout(cam->smbus_wait,
+ cafe_smbus_read_done(mcam), CAFE_SMBUS_TIMEOUT);
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ rval = mcam_reg_read(mcam, REG_TWSIC1);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+ if (rval & TWSIC1_ERROR) {
+ cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command);
+ return -EIO;
+ }
+ if (!(rval & TWSIC1_RVALID)) {
+ cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr,
+ command);
+ return -EIO;
+ }
+ *value = rval & 0xff;
+ return 0;
+}
+
+/*
+ * Perform a transfer over SMBUS. This thing is called under
+ * the i2c bus lock, so we shouldn't race with ourselves...
+ */
+static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char rw, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct cafe_camera *cam = i2c_get_adapdata(adapter);
+ int ret = -EINVAL;
+
+ /*
+ * This interface would appear to only do byte data ops. OK
+ * it can do word too, but the cam chip has no use for that.
+ */
+ if (size != I2C_SMBUS_BYTE_DATA) {
+ cam_err(cam, "funky xfer size %d\n", size);
+ return -EINVAL;
+ }
+
+ if (rw == I2C_SMBUS_WRITE)
+ ret = cafe_smbus_write_data(cam, addr, command, data->byte);
+ else if (rw == I2C_SMBUS_READ)
+ ret = cafe_smbus_read_data(cam, addr, command, &data->byte);
+ return ret;
+}
+
+
+static void cafe_smbus_enable_irq(struct cafe_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->mcam.dev_lock, flags);
+ mcam_reg_set_bit(&cam->mcam, REG_IRQMASK, TWSIIRQS);
+ spin_unlock_irqrestore(&cam->mcam.dev_lock, flags);
+}
+
+static u32 cafe_smbus_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_SMBUS_READ_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+static struct i2c_algorithm cafe_smbus_algo = {
+ .smbus_xfer = cafe_smbus_xfer,
+ .functionality = cafe_smbus_func
+};
+
+static int cafe_smbus_setup(struct cafe_camera *cam)
+{
+ struct i2c_adapter *adap;
+ int ret;
+
+ adap = kzalloc(sizeof(*adap), GFP_KERNEL);
+ if (adap == NULL)
+ return -ENOMEM;
+ cam->mcam.i2c_adapter = adap;
+ cafe_smbus_enable_irq(cam);
+ adap->owner = THIS_MODULE;
+ adap->algo = &cafe_smbus_algo;
+ strcpy(adap->name, "cafe_ccic");
+ adap->dev.parent = &cam->pdev->dev;
+ i2c_set_adapdata(adap, cam);
+ ret = i2c_add_adapter(adap);
+ if (ret)
+ printk(KERN_ERR "Unable to register cafe i2c adapter\n");
+ return ret;
+}
+
+static void cafe_smbus_shutdown(struct cafe_camera *cam)
+{
+ i2c_del_adapter(cam->mcam.i2c_adapter);
+ kfree(cam->mcam.i2c_adapter);
+}
+
+
+/*
+ * Controller-level stuff
+ */
+
+static void cafe_ctlr_init(struct mcam_camera *mcam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ /*
+ * Added magic to bring up the hardware on the B-Test board
+ */
+ mcam_reg_write(mcam, 0x3038, 0x8);
+ mcam_reg_write(mcam, 0x315c, 0x80008);
+ /*
+ * Go through the dance needed to wake the device up.
+ * Note that these registers are global and shared
+ * with the NAND and SD devices. Interaction between the
+ * three still needs to be examined.
+ */
+ mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */
+ mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRC|GCSR_MRC);
+ mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRC|GCSR_MRS);
+ /*
+ * Here we must wait a bit for the controller to come around.
+ */
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+ msleep(5);
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+
+ mcam_reg_write(mcam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
+ mcam_reg_set_bit(mcam, REG_GL_IMASK, GIMSK_CCIC_EN);
+ /*
+ * Mask all interrupts.
+ */
+ mcam_reg_write(mcam, REG_IRQMASK, 0);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+}
+
+
+static void cafe_ctlr_power_up(struct mcam_camera *mcam)
+{
+ /*
+ * Part one of the sensor dance: turn the global
+ * GPIO signal on.
+ */
+ mcam_reg_write(mcam, REG_GL_FCR, GFCR_GPIO_ON);
+ mcam_reg_write(mcam, REG_GL_GPIOR, GGPIO_OUT|GGPIO_VAL);
+ /*
+ * Put the sensor into operational mode (assumes OLPC-style
+ * wiring). Control 0 is reset - set to 1 to operate.
+ * Control 1 is power down, set to 0 to operate.
+ */
+ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
+ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
+}
+
+static void cafe_ctlr_power_down(struct mcam_camera *mcam)
+{
+ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1);
+ mcam_reg_write(mcam, REG_GL_FCR, GFCR_GPIO_ON);
+ mcam_reg_write(mcam, REG_GL_GPIOR, GGPIO_OUT);
+}
+
+
+
+/*
+ * The platform interrupt handler.
+ */
+static irqreturn_t cafe_irq(int irq, void *data)
+{
+ struct cafe_camera *cam = data;
+ struct mcam_camera *mcam = &cam->mcam;
+ unsigned int irqs, handled;
+
+ spin_lock(&mcam->dev_lock);
+ irqs = mcam_reg_read(mcam, REG_IRQSTAT);
+ handled = cam->registered && mccic_irq(mcam, irqs);
+ if (irqs & TWSIIRQS) {
+ mcam_reg_write(mcam, REG_IRQSTAT, TWSIIRQS);
+ wake_up(&cam->smbus_wait);
+ handled = 1;
+ }
+ spin_unlock(&mcam->dev_lock);
+ return IRQ_RETVAL(handled);
+}
+
+
+/* -------------------------------------------------------------------------- */
+/*
+ * PCI interface stuff.
+ */
+
+static int cafe_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int ret;
+ struct cafe_camera *cam;
+ struct mcam_camera *mcam;
+
+ /*
+ * Start putting together one of our big camera structures.
+ */
+ ret = -ENOMEM;
+ cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL);
+ if (cam == NULL)
+ goto out;
+ cam->pdev = pdev;
+ mcam = &cam->mcam;
+ mcam->chip_id = V4L2_IDENT_CAFE;
+ spin_lock_init(&mcam->dev_lock);
+ init_waitqueue_head(&cam->smbus_wait);
+ mcam->plat_power_up = cafe_ctlr_power_up;
+ mcam->plat_power_down = cafe_ctlr_power_down;
+ mcam->dev = &pdev->dev;
+ /*
+ * Set the clock speed for the XO 1; I don't believe this
+ * driver has ever run anywhere else.
+ */
+ mcam->clock_speed = 45;
+ mcam->use_smbus = 1;
+ /*
+ * Vmalloc mode for buffers is traditional with this driver.
+ * We *might* be able to run DMA_contig, especially on a system
+ * with CMA in it.
+ */
+ mcam->buffer_mode = B_vmalloc;
+ /*
+ * Get set up on the PCI bus.
+ */
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto out_free;
+ pci_set_master(pdev);
+
+ ret = -EIO;
+ mcam->regs = pci_iomap(pdev, 0, 0);
+ if (!mcam->regs) {
+ printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n");
+ goto out_disable;
+ }
+ ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
+ if (ret)
+ goto out_iounmap;
+
+ /*
+ * Initialize the controller and leave it powered up. It will
+ * stay that way until the sensor driver shows up.
+ */
+ cafe_ctlr_init(mcam);
+ cafe_ctlr_power_up(mcam);
+ /*
+ * Set up I2C/SMBUS communications. We have to drop the mutex here
+ * because the sensor could attach in this call chain, leading to
+ * unsightly deadlocks.
+ */
+ ret = cafe_smbus_setup(cam);
+ if (ret)
+ goto out_pdown;
+
+ ret = mccic_register(mcam);
+ if (ret == 0) {
+ cam->registered = 1;
+ return 0;
+ }
+
+ cafe_smbus_shutdown(cam);
+out_pdown:
+ cafe_ctlr_power_down(mcam);
+ free_irq(pdev->irq, cam);
+out_iounmap:
+ pci_iounmap(pdev, mcam->regs);
+out_disable:
+ pci_disable_device(pdev);
+out_free:
+ kfree(cam);
+out:
+ return ret;
+}
+
+
+/*
+ * Shut down an initialized device
+ */
+static void cafe_shutdown(struct cafe_camera *cam)
+{
+ mccic_shutdown(&cam->mcam);
+ cafe_smbus_shutdown(cam);
+ free_irq(cam->pdev->irq, cam);
+ pci_iounmap(cam->pdev, cam->mcam.regs);
+}
+
+
+static void cafe_pci_remove(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct cafe_camera *cam = to_cam(v4l2_dev);
+
+ if (cam == NULL) {
+ printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev);
+ return;
+ }
+ cafe_shutdown(cam);
+ kfree(cam);
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * Basic power management.
+ */
+static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct cafe_camera *cam = to_cam(v4l2_dev);
+ int ret;
+
+ ret = pci_save_state(pdev);
+ if (ret)
+ return ret;
+ mccic_suspend(&cam->mcam);
+ pci_disable_device(pdev);
+ return 0;
+}
+
+
+static int cafe_pci_resume(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct cafe_camera *cam = to_cam(v4l2_dev);
+ int ret = 0;
+
+ pci_restore_state(pdev);
+ ret = pci_enable_device(pdev);
+
+ if (ret) {
+ cam_warn(cam, "Unable to re-enable device on resume!\n");
+ return ret;
+ }
+ cafe_ctlr_init(&cam->mcam);
+ return mccic_resume(&cam->mcam);
+}
+
+#endif /* CONFIG_PM */
+
+static struct pci_device_id cafe_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL,
+ PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, cafe_ids);
+
+static struct pci_driver cafe_pci_driver = {
+ .name = "cafe1000-ccic",
+ .id_table = cafe_ids,
+ .probe = cafe_pci_probe,
+ .remove = cafe_pci_remove,
+#ifdef CONFIG_PM
+ .suspend = cafe_pci_suspend,
+ .resume = cafe_pci_resume,
+#endif
+};
+
+
+
+
+static int __init cafe_init(void)
+{
+ int ret;
+
+ printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n",
+ CAFE_VERSION);
+ ret = pci_register_driver(&cafe_pci_driver);
+ if (ret) {
+ printk(KERN_ERR "Unable to register cafe_ccic driver\n");
+ goto out;
+ }
+ ret = 0;
+
+out:
+ return ret;
+}
+
+
+static void __exit cafe_exit(void)
+{
+ pci_unregister_driver(&cafe_pci_driver);
+}
+
+module_init(cafe_init);
+module_exit(cafe_exit);
diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c
new file mode 100644
index 000000000000..80ec64d2d6d8
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/mcam-core.c
@@ -0,0 +1,1846 @@
+/*
+ * The Marvell camera core. This device appears in a number of settings,
+ * so it needs platform-specific support outside of the core.
+ *
+ * Copyright 2011 Jonathan Corbet corbet@lwn.net
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/ov7670.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "mcam-core.h"
+
+/*
+ * Basic frame stats - to be deleted shortly
+ */
+static int frames;
+static int singles;
+static int delivered;
+
+#ifdef MCAM_MODE_VMALLOC
+/*
+ * Internal DMA buffer management. Since the controller cannot do S/G I/O,
+ * we must have physically contiguous buffers to bring frames into.
+ * These parameters control how many buffers we use, whether we
+ * allocate them at load time (better chance of success, but nails down
+ * memory) or when somebody tries to use the camera (riskier), and,
+ * for load-time allocation, how big they should be.
+ *
+ * The controller can cycle through three buffers. We could use
+ * more by flipping pointers around, but it probably makes little
+ * sense.
+ */
+
+static int alloc_bufs_at_read;
+module_param(alloc_bufs_at_read, bool, 0444);
+MODULE_PARM_DESC(alloc_bufs_at_read,
+ "Non-zero value causes DMA buffers to be allocated when the "
+ "video capture device is read, rather than at module load "
+ "time. This saves memory, but decreases the chances of "
+ "successfully getting those buffers. This parameter is "
+ "only used in the vmalloc buffer mode");
+
+static int n_dma_bufs = 3;
+module_param(n_dma_bufs, uint, 0644);
+MODULE_PARM_DESC(n_dma_bufs,
+ "The number of DMA buffers to allocate. Can be either two "
+ "(saves memory, makes timing tighter) or three.");
+
+static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2; /* Worst case */
+module_param(dma_buf_size, uint, 0444);
+MODULE_PARM_DESC(dma_buf_size,
+ "The size of the allocated DMA buffers. If actual operating "
+ "parameters require larger buffers, an attempt to reallocate "
+ "will be made.");
+#else /* MCAM_MODE_VMALLOC */
+static const int alloc_bufs_at_read = 0;
+static const int n_dma_bufs = 3; /* Used by S/G_PARM */
+#endif /* MCAM_MODE_VMALLOC */
+
+static int flip;
+module_param(flip, bool, 0444);
+MODULE_PARM_DESC(flip,
+ "If set, the sensor will be instructed to flip the image "
+ "vertically.");
+
+static int buffer_mode = -1;
+module_param(buffer_mode, int, 0444);
+MODULE_PARM_DESC(buffer_mode,
+ "Set the buffer mode to be used; default is to go with what "
+ "the platform driver asks for. Set to 0 for vmalloc, 1 for "
+ "DMA contiguous.");
+
+/*
+ * Status flags. Always manipulated with bit operations.
+ */
+#define CF_BUF0_VALID 0 /* Buffers valid - first three */
+#define CF_BUF1_VALID 1
+#define CF_BUF2_VALID 2
+#define CF_DMA_ACTIVE 3 /* A frame is incoming */
+#define CF_CONFIG_NEEDED 4 /* Must configure hardware */
+#define CF_SINGLE_BUFFER 5 /* Running with a single buffer */
+#define CF_SG_RESTART 6 /* SG restart needed */
+
+#define sensor_call(cam, o, f, args...) \
+ v4l2_subdev_call(cam->sensor, o, f, ##args)
+
+static struct mcam_format_struct {
+ __u8 *desc;
+ __u32 pixelformat;
+ int bpp; /* Bytes per pixel */
+ enum v4l2_mbus_pixelcode mbus_code;
+} mcam_formats[] = {
+ {
+ .desc = "YUYV 4:2:2",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ },
+ {
+ .desc = "RGB 444",
+ .pixelformat = V4L2_PIX_FMT_RGB444,
+ .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+ .bpp = 2,
+ },
+ {
+ .desc = "RGB 565",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .bpp = 2,
+ },
+ {
+ .desc = "Raw RGB Bayer",
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,
+ .bpp = 1
+ },
+};
+#define N_MCAM_FMTS ARRAY_SIZE(mcam_formats)
+
+static struct mcam_format_struct *mcam_find_format(u32 pixelformat)
+{
+ unsigned i;
+
+ for (i = 0; i < N_MCAM_FMTS; i++)
+ if (mcam_formats[i].pixelformat == pixelformat)
+ return mcam_formats + i;
+ /* Not found? Then return the first format. */
+ return mcam_formats;
+}
+
+/*
+ * The default format we use until somebody says otherwise.
+ */
+static const struct v4l2_pix_format mcam_def_pix_format = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_NONE,
+ .bytesperline = VGA_WIDTH*2,
+ .sizeimage = VGA_WIDTH*VGA_HEIGHT*2,
+};
+
+static const enum v4l2_mbus_pixelcode mcam_def_mbus_code =
+ V4L2_MBUS_FMT_YUYV8_2X8;
+
+
+/*
+ * The two-word DMA descriptor format used by the Armada 610 and like. There
+ * Is a three-word format as well (set C1_DESC_3WORD) where the third
+ * word is a pointer to the next descriptor, but we don't use it. Two-word
+ * descriptors have to be contiguous in memory.
+ */
+struct mcam_dma_desc {
+ u32 dma_addr;
+ u32 segment_len;
+};
+
+/*
+ * Our buffer type for working with videobuf2. Note that the vb2
+ * developers have decreed that struct vb2_buffer must be at the
+ * beginning of this structure.
+ */
+struct mcam_vb_buffer {
+ struct vb2_buffer vb_buf;
+ struct list_head queue;
+ struct mcam_dma_desc *dma_desc; /* Descriptor virtual address */
+ dma_addr_t dma_desc_pa; /* Descriptor physical address */
+ int dma_desc_nent; /* Number of mapped descriptors */
+};
+
+static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct mcam_vb_buffer, vb_buf);
+}
+
+/*
+ * Hand a completed buffer back to user space.
+ */
+static void mcam_buffer_done(struct mcam_camera *cam, int frame,
+ struct vb2_buffer *vbuf)
+{
+ vbuf->v4l2_buf.bytesused = cam->pix_format.sizeimage;
+ vbuf->v4l2_buf.sequence = cam->buf_seq[frame];
+ vb2_set_plane_payload(vbuf, 0, cam->pix_format.sizeimage);
+ vb2_buffer_done(vbuf, VB2_BUF_STATE_DONE);
+}
+
+
+
+/*
+ * Debugging and related.
+ */
+#define cam_err(cam, fmt, arg...) \
+ dev_err((cam)->dev, fmt, ##arg);
+#define cam_warn(cam, fmt, arg...) \
+ dev_warn((cam)->dev, fmt, ##arg);
+#define cam_dbg(cam, fmt, arg...) \
+ dev_dbg((cam)->dev, fmt, ##arg);
+
+
+/*
+ * Flag manipulation helpers
+ */
+static void mcam_reset_buffers(struct mcam_camera *cam)
+{
+ int i;
+
+ cam->next_buf = -1;
+ for (i = 0; i < cam->nbufs; i++)
+ clear_bit(i, &cam->flags);
+}
+
+static inline int mcam_needs_config(struct mcam_camera *cam)
+{
+ return test_bit(CF_CONFIG_NEEDED, &cam->flags);
+}
+
+static void mcam_set_config_needed(struct mcam_camera *cam, int needed)
+{
+ if (needed)
+ set_bit(CF_CONFIG_NEEDED, &cam->flags);
+ else
+ clear_bit(CF_CONFIG_NEEDED, &cam->flags);
+}
+
+/* ------------------------------------------------------------------- */
+/*
+ * Make the controller start grabbing images. Everything must
+ * be set up before doing this.
+ */
+static void mcam_ctlr_start(struct mcam_camera *cam)
+{
+ /* set_bit performs a read, so no other barrier should be
+ needed here */
+ mcam_reg_set_bit(cam, REG_CTRL0, C0_ENABLE);
+}
+
+static void mcam_ctlr_stop(struct mcam_camera *cam)
+{
+ mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
+}
+
+/* ------------------------------------------------------------------- */
+
+#ifdef MCAM_MODE_VMALLOC
+/*
+ * Code specific to the vmalloc buffer mode.
+ */
+
+/*
+ * Allocate in-kernel DMA buffers for vmalloc mode.
+ */
+static int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime)
+{
+ int i;
+
+ mcam_set_config_needed(cam, 1);
+ if (loadtime)
+ cam->dma_buf_size = dma_buf_size;
+ else
+ cam->dma_buf_size = cam->pix_format.sizeimage;
+ if (n_dma_bufs > 3)
+ n_dma_bufs = 3;
+
+ cam->nbufs = 0;
+ for (i = 0; i < n_dma_bufs; i++) {
+ cam->dma_bufs[i] = dma_alloc_coherent(cam->dev,
+ cam->dma_buf_size, cam->dma_handles + i,
+ GFP_KERNEL);
+ if (cam->dma_bufs[i] == NULL) {
+ cam_warn(cam, "Failed to allocate DMA buffer\n");
+ break;
+ }
+ (cam->nbufs)++;
+ }
+
+ switch (cam->nbufs) {
+ case 1:
+ dma_free_coherent(cam->dev, cam->dma_buf_size,
+ cam->dma_bufs[0], cam->dma_handles[0]);
+ cam->nbufs = 0;
+ case 0:
+ cam_err(cam, "Insufficient DMA buffers, cannot operate\n");
+ return -ENOMEM;
+
+ case 2:
+ if (n_dma_bufs > 2)
+ cam_warn(cam, "Will limp along with only 2 buffers\n");
+ break;
+ }
+ return 0;
+}
+
+static void mcam_free_dma_bufs(struct mcam_camera *cam)
+{
+ int i;
+
+ for (i = 0; i < cam->nbufs; i++) {
+ dma_free_coherent(cam->dev, cam->dma_buf_size,
+ cam->dma_bufs[i], cam->dma_handles[i]);
+ cam->dma_bufs[i] = NULL;
+ }
+ cam->nbufs = 0;
+}
+
+
+/*
+ * Set up DMA buffers when operating in vmalloc mode
+ */
+static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam)
+{
+ /*
+ * Store the first two Y buffers (we aren't supporting
+ * planar formats for now, so no UV bufs). Then either
+ * set the third if it exists, or tell the controller
+ * to just use two.
+ */
+ mcam_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]);
+ mcam_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]);
+ if (cam->nbufs > 2) {
+ mcam_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]);
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
+ } else
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
+ if (cam->chip_id == V4L2_IDENT_CAFE)
+ mcam_reg_write(cam, REG_UBAR, 0); /* 32 bits only */
+}
+
+/*
+ * Copy data out to user space in the vmalloc case
+ */
+static void mcam_frame_tasklet(unsigned long data)
+{
+ struct mcam_camera *cam = (struct mcam_camera *) data;
+ int i;
+ unsigned long flags;
+ struct mcam_vb_buffer *buf;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ for (i = 0; i < cam->nbufs; i++) {
+ int bufno = cam->next_buf;
+
+ if (cam->state != S_STREAMING || bufno < 0)
+ break; /* I/O got stopped */
+ if (++(cam->next_buf) >= cam->nbufs)
+ cam->next_buf = 0;
+ if (!test_bit(bufno, &cam->flags))
+ continue;
+ if (list_empty(&cam->buffers)) {
+ singles++;
+ break; /* Leave it valid, hope for better later */
+ }
+ delivered++;
+ clear_bit(bufno, &cam->flags);
+ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer,
+ queue);
+ list_del_init(&buf->queue);
+ /*
+ * Drop the lock during the big copy. This *should* be safe...
+ */
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ memcpy(vb2_plane_vaddr(&buf->vb_buf, 0), cam->dma_bufs[bufno],
+ cam->pix_format.sizeimage);
+ mcam_buffer_done(cam, bufno, &buf->vb_buf);
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ }
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+
+/*
+ * Make sure our allocated buffers are up to the task.
+ */
+static int mcam_check_dma_buffers(struct mcam_camera *cam)
+{
+ if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage)
+ mcam_free_dma_bufs(cam);
+ if (cam->nbufs == 0)
+ return mcam_alloc_dma_bufs(cam, 0);
+ return 0;
+}
+
+static void mcam_vmalloc_done(struct mcam_camera *cam, int frame)
+{
+ tasklet_schedule(&cam->s_tasklet);
+}
+
+#else /* MCAM_MODE_VMALLOC */
+
+static inline int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime)
+{
+ return 0;
+}
+
+static inline void mcam_free_dma_bufs(struct mcam_camera *cam)
+{
+ return;
+}
+
+static inline int mcam_check_dma_buffers(struct mcam_camera *cam)
+{
+ return 0;
+}
+
+
+
+#endif /* MCAM_MODE_VMALLOC */
+
+
+#ifdef MCAM_MODE_DMA_CONTIG
+/* ---------------------------------------------------------------------- */
+/*
+ * DMA-contiguous code.
+ */
+/*
+ * Set up a contiguous buffer for the given frame. Here also is where
+ * the underrun strategy is set: if there is no buffer available, reuse
+ * the buffer from the other BAR and set the CF_SINGLE_BUFFER flag to
+ * keep the interrupt handler from giving that buffer back to user
+ * space. In this way, we always have a buffer to DMA to and don't
+ * have to try to play games stopping and restarting the controller.
+ */
+static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame)
+{
+ struct mcam_vb_buffer *buf;
+ /*
+ * If there are no available buffers, go into single mode
+ */
+ if (list_empty(&cam->buffers)) {
+ buf = cam->vb_bufs[frame ^ 0x1];
+ cam->vb_bufs[frame] = buf;
+ mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
+ vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
+ set_bit(CF_SINGLE_BUFFER, &cam->flags);
+ singles++;
+ return;
+ }
+ /*
+ * OK, we have a buffer we can use.
+ */
+ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
+ list_del_init(&buf->queue);
+ mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
+ vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
+ cam->vb_bufs[frame] = buf;
+ clear_bit(CF_SINGLE_BUFFER, &cam->flags);
+}
+
+/*
+ * Initial B_DMA_contig setup.
+ */
+static void mcam_ctlr_dma_contig(struct mcam_camera *cam)
+{
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
+ cam->nbufs = 2;
+ mcam_set_contig_buffer(cam, 0);
+ mcam_set_contig_buffer(cam, 1);
+}
+
+/*
+ * Frame completion handling.
+ */
+static void mcam_dma_contig_done(struct mcam_camera *cam, int frame)
+{
+ struct mcam_vb_buffer *buf = cam->vb_bufs[frame];
+
+ if (!test_bit(CF_SINGLE_BUFFER, &cam->flags)) {
+ delivered++;
+ mcam_buffer_done(cam, frame, &buf->vb_buf);
+ }
+ mcam_set_contig_buffer(cam, frame);
+}
+
+#endif /* MCAM_MODE_DMA_CONTIG */
+
+#ifdef MCAM_MODE_DMA_SG
+/* ---------------------------------------------------------------------- */
+/*
+ * Scatter/gather-specific code.
+ */
+
+/*
+ * Set up the next buffer for S/G I/O; caller should be sure that
+ * the controller is stopped and a buffer is available.
+ */
+static void mcam_sg_next_buffer(struct mcam_camera *cam)
+{
+ struct mcam_vb_buffer *buf;
+
+ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
+ list_del_init(&buf->queue);
+ mcam_reg_write(cam, REG_DMA_DESC_Y, buf->dma_desc_pa);
+ mcam_reg_write(cam, REG_DESC_LEN_Y,
+ buf->dma_desc_nent*sizeof(struct mcam_dma_desc));
+ mcam_reg_write(cam, REG_DESC_LEN_U, 0);
+ mcam_reg_write(cam, REG_DESC_LEN_V, 0);
+ cam->vb_bufs[0] = buf;
+}
+
+/*
+ * Initial B_DMA_sg setup
+ */
+static void mcam_ctlr_dma_sg(struct mcam_camera *cam)
+{
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_3WORD);
+ mcam_sg_next_buffer(cam);
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
+ cam->nbufs = 3;
+}
+
+
+/*
+ * Frame completion with S/G is trickier. We can't muck with
+ * a descriptor chain on the fly, since the controller buffers it
+ * internally. So we have to actually stop and restart; Marvell
+ * says this is the way to do it.
+ *
+ * Of course, stopping is easier said than done; experience shows
+ * that the controller can start a frame *after* C0_ENABLE has been
+ * cleared. So when running in S/G mode, the controller is "stopped"
+ * on receipt of the start-of-frame interrupt. That means we can
+ * safely change the DMA descriptor array here and restart things
+ * (assuming there's another buffer waiting to go).
+ */
+static void mcam_dma_sg_done(struct mcam_camera *cam, int frame)
+{
+ struct mcam_vb_buffer *buf = cam->vb_bufs[0];
+
+ /*
+ * Very Bad Not Good Things happen if you don't clear
+ * C1_DESC_ENA before making any descriptor changes.
+ */
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA);
+ /*
+ * If we have another buffer available, put it in and
+ * restart the engine.
+ */
+ if (!list_empty(&cam->buffers)) {
+ mcam_sg_next_buffer(cam);
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
+ mcam_ctlr_start(cam);
+ /*
+ * Otherwise set CF_SG_RESTART and the controller will
+ * be restarted once another buffer shows up.
+ */
+ } else {
+ set_bit(CF_SG_RESTART, &cam->flags);
+ singles++;
+ }
+ /*
+ * Now we can give the completed frame back to user space.
+ */
+ delivered++;
+ mcam_buffer_done(cam, frame, &buf->vb_buf);
+}
+
+
+/*
+ * Scatter/gather mode requires stopping the controller between
+ * frames so we can put in a new DMA descriptor array. If no new
+ * buffer exists at frame completion, the controller is left stopped;
+ * this function is charged with gettig things going again.
+ */
+static void mcam_sg_restart(struct mcam_camera *cam)
+{
+ mcam_ctlr_dma_sg(cam);
+ mcam_ctlr_start(cam);
+ clear_bit(CF_SG_RESTART, &cam->flags);
+}
+
+#else /* MCAM_MODE_DMA_SG */
+
+static inline void mcam_sg_restart(struct mcam_camera *cam)
+{
+ return;
+}
+
+#endif /* MCAM_MODE_DMA_SG */
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Buffer-mode-independent controller code.
+ */
+
+/*
+ * Image format setup
+ */
+static void mcam_ctlr_image(struct mcam_camera *cam)
+{
+ int imgsz;
+ struct v4l2_pix_format *fmt = &cam->pix_format;
+
+ imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
+ (fmt->bytesperline & IMGSZ_H_MASK);
+ mcam_reg_write(cam, REG_IMGSIZE, imgsz);
+ mcam_reg_write(cam, REG_IMGOFFSET, 0);
+ /* YPITCH just drops the last two bits */
+ mcam_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline,
+ IMGP_YP_MASK);
+ /*
+ * Tell the controller about the image format we are using.
+ */
+ switch (cam->pix_format.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV,
+ C0_DF_MASK);
+ break;
+
+ case V4L2_PIX_FMT_RGB444:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
+ C0_DF_MASK);
+ /* Alpha value? */
+ break;
+
+ case V4L2_PIX_FMT_RGB565:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
+ C0_DF_MASK);
+ break;
+
+ default:
+ cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
+ break;
+ }
+ /*
+ * Make sure it knows we want to use hsync/vsync.
+ */
+ mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
+ C0_SIFM_MASK);
+}
+
+
+/*
+ * Configure the controller for operation; caller holds the
+ * device mutex.
+ */
+static int mcam_ctlr_configure(struct mcam_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ cam->dma_setup(cam);
+ mcam_ctlr_image(cam);
+ mcam_set_config_needed(cam, 0);
+ clear_bit(CF_SG_RESTART, &cam->flags);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return 0;
+}
+
+static void mcam_ctlr_irq_enable(struct mcam_camera *cam)
+{
+ /*
+ * Clear any pending interrupts, since we do not
+ * expect to have I/O active prior to enabling.
+ */
+ mcam_reg_write(cam, REG_IRQSTAT, FRAMEIRQS);
+ mcam_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS);
+}
+
+static void mcam_ctlr_irq_disable(struct mcam_camera *cam)
+{
+ mcam_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS);
+}
+
+
+
+static void mcam_ctlr_init(struct mcam_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ /*
+ * Make sure it's not powered down.
+ */
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
+ /*
+ * Turn off the enable bit. It sure should be off anyway,
+ * but it's good to be sure.
+ */
+ mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
+ /*
+ * Clock the sensor appropriately. Controller clock should
+ * be 48MHz, sensor "typical" value is half that.
+ */
+ mcam_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+
+/*
+ * Stop the controller, and don't return until we're really sure that no
+ * further DMA is going on.
+ */
+static void mcam_ctlr_stop_dma(struct mcam_camera *cam)
+{
+ unsigned long flags;
+
+ /*
+ * Theory: stop the camera controller (whether it is operating
+ * or not). Delay briefly just in case we race with the SOF
+ * interrupt, then wait until no DMA is active.
+ */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ clear_bit(CF_SG_RESTART, &cam->flags);
+ mcam_ctlr_stop(cam);
+ cam->state = S_IDLE;
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ msleep(40);
+ if (test_bit(CF_DMA_ACTIVE, &cam->flags))
+ cam_err(cam, "Timeout waiting for DMA to end\n");
+ /* This would be bad news - what now? */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ mcam_ctlr_irq_disable(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+/*
+ * Power up and down.
+ */
+static void mcam_ctlr_power_up(struct mcam_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ cam->plat_power_up(cam);
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ msleep(5); /* Just to be sure */
+}
+
+static void mcam_ctlr_power_down(struct mcam_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ /*
+ * School of hard knocks department: be sure we do any register
+ * twiddling on the controller *before* calling the platform
+ * power down routine.
+ */
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
+ cam->plat_power_down(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+/* -------------------------------------------------------------------- */
+/*
+ * Communications with the sensor.
+ */
+
+static int __mcam_cam_reset(struct mcam_camera *cam)
+{
+ return sensor_call(cam, core, reset, 0);
+}
+
+/*
+ * We have found the sensor on the i2c. Let's try to have a
+ * conversation.
+ */
+static int mcam_cam_init(struct mcam_camera *cam)
+{
+ struct v4l2_dbg_chip_ident chip;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ if (cam->state != S_NOTREADY)
+ cam_warn(cam, "Cam init with device in funky state %d",
+ cam->state);
+ ret = __mcam_cam_reset(cam);
+ if (ret)
+ goto out;
+ chip.ident = V4L2_IDENT_NONE;
+ chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR;
+ chip.match.addr = cam->sensor_addr;
+ ret = sensor_call(cam, core, g_chip_ident, &chip);
+ if (ret)
+ goto out;
+ cam->sensor_type = chip.ident;
+ if (cam->sensor_type != V4L2_IDENT_OV7670) {
+ cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type);
+ ret = -EINVAL;
+ goto out;
+ }
+/* Get/set parameters? */
+ ret = 0;
+ cam->state = S_IDLE;
+out:
+ mcam_ctlr_power_down(cam);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+/*
+ * Configure the sensor to match the parameters we have. Caller should
+ * hold s_mutex
+ */
+static int mcam_cam_set_flip(struct mcam_camera *cam)
+{
+ struct v4l2_control ctrl;
+
+ memset(&ctrl, 0, sizeof(ctrl));
+ ctrl.id = V4L2_CID_VFLIP;
+ ctrl.value = flip;
+ return sensor_call(cam, core, s_ctrl, &ctrl);
+}
+
+
+static int mcam_cam_configure(struct mcam_camera *cam)
+{
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ v4l2_fill_mbus_format(&mbus_fmt, &cam->pix_format, cam->mbus_code);
+ ret = sensor_call(cam, core, init, 0);
+ if (ret == 0)
+ ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt);
+ /*
+ * OV7670 does weird things if flip is set *before* format...
+ */
+ ret += mcam_cam_set_flip(cam);
+ return ret;
+}
+
+/*
+ * Get everything ready, and start grabbing frames.
+ */
+static int mcam_read_setup(struct mcam_camera *cam)
+{
+ int ret;
+ unsigned long flags;
+
+ /*
+ * Configuration. If we still don't have DMA buffers,
+ * make one last, desperate attempt.
+ */
+ if (cam->buffer_mode == B_vmalloc && cam->nbufs == 0 &&
+ mcam_alloc_dma_bufs(cam, 0))
+ return -ENOMEM;
+
+ if (mcam_needs_config(cam)) {
+ mcam_cam_configure(cam);
+ ret = mcam_ctlr_configure(cam);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Turn it loose.
+ */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ mcam_reset_buffers(cam);
+ mcam_ctlr_irq_enable(cam);
+ cam->state = S_STREAMING;
+ mcam_ctlr_start(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/*
+ * Videobuf2 interface code.
+ */
+
+static int mcam_vb_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt, unsigned int *nbufs,
+ unsigned int *num_planes, unsigned int sizes[],
+ void *alloc_ctxs[])
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vq);
+ int minbufs = (cam->buffer_mode == B_DMA_contig) ? 3 : 2;
+
+ sizes[0] = cam->pix_format.sizeimage;
+ *num_planes = 1; /* Someday we have to support planar formats... */
+ if (*nbufs < minbufs)
+ *nbufs = minbufs;
+ if (cam->buffer_mode == B_DMA_contig)
+ alloc_ctxs[0] = cam->vb_alloc_ctx;
+ return 0;
+}
+
+
+static void mcam_vb_buf_queue(struct vb2_buffer *vb)
+{
+ struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long flags;
+ int start;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ start = (cam->state == S_BUFWAIT) && !list_empty(&cam->buffers);
+ list_add(&mvb->queue, &cam->buffers);
+ if (test_bit(CF_SG_RESTART, &cam->flags))
+ mcam_sg_restart(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ if (start)
+ mcam_read_setup(cam);
+}
+
+
+/*
+ * vb2 uses these to release the mutex when waiting in dqbuf. I'm
+ * not actually sure we need to do this (I'm not sure that vb2_dqbuf() needs
+ * to be called with the mutex held), but better safe than sorry.
+ */
+static void mcam_vb_wait_prepare(struct vb2_queue *vq)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vq);
+
+ mutex_unlock(&cam->s_mutex);
+}
+
+static void mcam_vb_wait_finish(struct vb2_queue *vq)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vq);
+
+ mutex_lock(&cam->s_mutex);
+}
+
+/*
+ * These need to be called with the mutex held from vb2
+ */
+static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vq);
+
+ if (cam->state != S_IDLE) {
+ INIT_LIST_HEAD(&cam->buffers);
+ return -EINVAL;
+ }
+ cam->sequence = 0;
+ /*
+ * Videobuf2 sneakily hoards all the buffers and won't
+ * give them to us until *after* streaming starts. But
+ * we can't actually start streaming until we have a
+ * destination. So go into a wait state and hope they
+ * give us buffers soon.
+ */
+ if (cam->buffer_mode != B_vmalloc && list_empty(&cam->buffers)) {
+ cam->state = S_BUFWAIT;
+ return 0;
+ }
+ return mcam_read_setup(cam);
+}
+
+static int mcam_vb_stop_streaming(struct vb2_queue *vq)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vq);
+ unsigned long flags;
+
+ if (cam->state == S_BUFWAIT) {
+ /* They never gave us buffers */
+ cam->state = S_IDLE;
+ return 0;
+ }
+ if (cam->state != S_STREAMING)
+ return -EINVAL;
+ mcam_ctlr_stop_dma(cam);
+ /*
+ * VB2 reclaims the buffers, so we need to forget
+ * about them.
+ */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ INIT_LIST_HEAD(&cam->buffers);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return 0;
+}
+
+
+static const struct vb2_ops mcam_vb2_ops = {
+ .queue_setup = mcam_vb_queue_setup,
+ .buf_queue = mcam_vb_buf_queue,
+ .start_streaming = mcam_vb_start_streaming,
+ .stop_streaming = mcam_vb_stop_streaming,
+ .wait_prepare = mcam_vb_wait_prepare,
+ .wait_finish = mcam_vb_wait_finish,
+};
+
+
+#ifdef MCAM_MODE_DMA_SG
+/*
+ * Scatter/gather mode uses all of the above functions plus a
+ * few extras to deal with DMA mapping.
+ */
+static int mcam_vb_sg_buf_init(struct vb2_buffer *vb)
+{
+ struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+ int ndesc = cam->pix_format.sizeimage/PAGE_SIZE + 1;
+
+ mvb->dma_desc = dma_alloc_coherent(cam->dev,
+ ndesc * sizeof(struct mcam_dma_desc),
+ &mvb->dma_desc_pa, GFP_KERNEL);
+ if (mvb->dma_desc == NULL) {
+ cam_err(cam, "Unable to get DMA descriptor array\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
+{
+ struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0);
+ struct mcam_dma_desc *desc = mvb->dma_desc;
+ struct scatterlist *sg;
+ int i;
+
+ mvb->dma_desc_nent = dma_map_sg(cam->dev, sgd->sglist, sgd->num_pages,
+ DMA_FROM_DEVICE);
+ if (mvb->dma_desc_nent <= 0)
+ return -EIO; /* Not sure what's right here */
+ for_each_sg(sgd->sglist, sg, mvb->dma_desc_nent, i) {
+ desc->dma_addr = sg_dma_address(sg);
+ desc->segment_len = sg_dma_len(sg);
+ desc++;
+ }
+ return 0;
+}
+
+static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0);
+
+ dma_unmap_sg(cam->dev, sgd->sglist, sgd->num_pages, DMA_FROM_DEVICE);
+ return 0;
+}
+
+static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+ struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+ int ndesc = cam->pix_format.sizeimage/PAGE_SIZE + 1;
+
+ dma_free_coherent(cam->dev, ndesc * sizeof(struct mcam_dma_desc),
+ mvb->dma_desc, mvb->dma_desc_pa);
+}
+
+
+static const struct vb2_ops mcam_vb2_sg_ops = {
+ .queue_setup = mcam_vb_queue_setup,
+ .buf_init = mcam_vb_sg_buf_init,
+ .buf_prepare = mcam_vb_sg_buf_prepare,
+ .buf_queue = mcam_vb_buf_queue,
+ .buf_finish = mcam_vb_sg_buf_finish,
+ .buf_cleanup = mcam_vb_sg_buf_cleanup,
+ .start_streaming = mcam_vb_start_streaming,
+ .stop_streaming = mcam_vb_stop_streaming,
+ .wait_prepare = mcam_vb_wait_prepare,
+ .wait_finish = mcam_vb_wait_finish,
+};
+
+#endif /* MCAM_MODE_DMA_SG */
+
+static int mcam_setup_vb2(struct mcam_camera *cam)
+{
+ struct vb2_queue *vq = &cam->vb_queue;
+
+ memset(vq, 0, sizeof(*vq));
+ vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vq->drv_priv = cam;
+ INIT_LIST_HEAD(&cam->buffers);
+ switch (cam->buffer_mode) {
+ case B_DMA_contig:
+#ifdef MCAM_MODE_DMA_CONTIG
+ vq->ops = &mcam_vb2_ops;
+ vq->mem_ops = &vb2_dma_contig_memops;
+ cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev);
+ vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ cam->dma_setup = mcam_ctlr_dma_contig;
+ cam->frame_complete = mcam_dma_contig_done;
+#endif
+ break;
+ case B_DMA_sg:
+#ifdef MCAM_MODE_DMA_SG
+ vq->ops = &mcam_vb2_sg_ops;
+ vq->mem_ops = &vb2_dma_sg_memops;
+ vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ cam->dma_setup = mcam_ctlr_dma_sg;
+ cam->frame_complete = mcam_dma_sg_done;
+#endif
+ break;
+ case B_vmalloc:
+#ifdef MCAM_MODE_VMALLOC
+ tasklet_init(&cam->s_tasklet, mcam_frame_tasklet,
+ (unsigned long) cam);
+ vq->ops = &mcam_vb2_ops;
+ vq->mem_ops = &vb2_vmalloc_memops;
+ vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
+ vq->io_modes = VB2_MMAP;
+ cam->dma_setup = mcam_ctlr_dma_vmalloc;
+ cam->frame_complete = mcam_vmalloc_done;
+#endif
+ break;
+ }
+ return vb2_queue_init(vq);
+}
+
+static void mcam_cleanup_vb2(struct mcam_camera *cam)
+{
+ vb2_queue_release(&cam->vb_queue);
+#ifdef MCAM_MODE_DMA_CONTIG
+ if (cam->buffer_mode == B_DMA_contig)
+ vb2_dma_contig_cleanup_ctx(cam->vb_alloc_ctx);
+#endif
+}
+
+
+/* ---------------------------------------------------------------------- */
+/*
+ * The long list of V4L2 ioctl() operations.
+ */
+
+static int mcam_vidioc_streamon(struct file *filp, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_streamon(&cam->vb_queue, type);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_streamoff(struct file *filp, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_streamoff(&cam->vb_queue, type);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_reqbufs(struct file *filp, void *priv,
+ struct v4l2_requestbuffers *req)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_reqbufs(&cam->vb_queue, req);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_querybuf(struct file *filp, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_querybuf(&cam->vb_queue, buf);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+static int mcam_vidioc_qbuf(struct file *filp, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_qbuf(&cam->vb_queue, buf);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+static int mcam_vidioc_dqbuf(struct file *filp, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_dqbuf(&cam->vb_queue, buf, filp->f_flags & O_NONBLOCK);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+
+static int mcam_vidioc_queryctrl(struct file *filp, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, core, queryctrl, qc);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_g_ctrl(struct file *filp, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, core, g_ctrl, ctrl);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_s_ctrl(struct file *filp, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, core, s_ctrl, ctrl);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "marvell_ccic");
+ strcpy(cap->card, "marvell_ccic");
+ cap->version = 1;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+
+static int mcam_vidioc_enum_fmt_vid_cap(struct file *filp,
+ void *priv, struct v4l2_fmtdesc *fmt)
+{
+ if (fmt->index >= N_MCAM_FMTS)
+ return -EINVAL;
+ strlcpy(fmt->description, mcam_formats[fmt->index].desc,
+ sizeof(fmt->description));
+ fmt->pixelformat = mcam_formats[fmt->index].pixelformat;
+ return 0;
+}
+
+static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct mcam_camera *cam = priv;
+ struct mcam_format_struct *f;
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ f = mcam_find_format(pix->pixelformat);
+ pix->pixelformat = f->pixelformat;
+ v4l2_fill_mbus_format(&mbus_fmt, pix, f->mbus_code);
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
+ mutex_unlock(&cam->s_mutex);
+ v4l2_fill_pix_format(pix, &mbus_fmt);
+ pix->bytesperline = pix->width * f->bpp;
+ pix->sizeimage = pix->height * pix->bytesperline;
+ return ret;
+}
+
+static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct mcam_camera *cam = priv;
+ struct mcam_format_struct *f;
+ int ret;
+
+ /*
+ * Can't do anything if the device is not idle
+ * Also can't if there are streaming buffers in place.
+ */
+ if (cam->state != S_IDLE || cam->vb_queue.num_buffers > 0)
+ return -EBUSY;
+
+ f = mcam_find_format(fmt->fmt.pix.pixelformat);
+
+ /*
+ * See if the formatting works in principle.
+ */
+ ret = mcam_vidioc_try_fmt_vid_cap(filp, priv, fmt);
+ if (ret)
+ return ret;
+ /*
+ * Now we start to change things for real, so let's do it
+ * under lock.
+ */
+ mutex_lock(&cam->s_mutex);
+ cam->pix_format = fmt->fmt.pix;
+ cam->mbus_code = f->mbus_code;
+
+ /*
+ * Make sure we have appropriate DMA buffers.
+ */
+ if (cam->buffer_mode == B_vmalloc) {
+ ret = mcam_check_dma_buffers(cam);
+ if (ret)
+ goto out;
+ }
+ mcam_set_config_needed(cam, 1);
+ ret = 0;
+out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+/*
+ * Return our stored notion of how the camera is/should be configured.
+ * The V4l2 spec wants us to be smarter, and actually get this from
+ * the camera (and not mess with it at open time). Someday.
+ */
+static int mcam_vidioc_g_fmt_vid_cap(struct file *filp, void *priv,
+ struct v4l2_format *f)
+{
+ struct mcam_camera *cam = priv;
+
+ f->fmt.pix = cam->pix_format;
+ return 0;
+}
+
+/*
+ * We only have one input - the sensor - so minimize the nonsense here.
+ */
+static int mcam_vidioc_enum_input(struct file *filp, void *priv,
+ struct v4l2_input *input)
+{
+ if (input->index != 0)
+ return -EINVAL;
+
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->std = V4L2_STD_ALL; /* Not sure what should go here */
+ strcpy(input->name, "Camera");
+ return 0;
+}
+
+static int mcam_vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int mcam_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* from vivi.c */
+static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
+{
+ return 0;
+}
+
+/*
+ * G/S_PARM. Most of this is done by the sensor, but we are
+ * the level which controls the number of read buffers.
+ */
+static int mcam_vidioc_g_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parms)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, video, g_parm, parms);
+ mutex_unlock(&cam->s_mutex);
+ parms->parm.capture.readbuffers = n_dma_bufs;
+ return ret;
+}
+
+static int mcam_vidioc_s_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parms)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, video, s_parm, parms);
+ mutex_unlock(&cam->s_mutex);
+ parms->parm.capture.readbuffers = n_dma_bufs;
+ return ret;
+}
+
+static int mcam_vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct mcam_camera *cam = priv;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (v4l2_chip_match_host(&chip->match)) {
+ chip->ident = cam->chip_id;
+ return 0;
+ }
+ return sensor_call(cam, core, g_chip_ident, chip);
+}
+
+static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv,
+ struct v4l2_frmsizeenum *sizes)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, video, enum_framesizes, sizes);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+static int mcam_vidioc_enum_frameintervals(struct file *filp, void *priv,
+ struct v4l2_frmivalenum *interval)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, video, enum_frameintervals, interval);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mcam_vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct mcam_camera *cam = priv;
+
+ if (v4l2_chip_match_host(&reg->match)) {
+ reg->val = mcam_reg_read(cam, reg->reg);
+ reg->size = 4;
+ return 0;
+ }
+ return sensor_call(cam, core, g_register, reg);
+}
+
+static int mcam_vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct mcam_camera *cam = priv;
+
+ if (v4l2_chip_match_host(&reg->match)) {
+ mcam_reg_write(cam, reg->reg, reg->val);
+ return 0;
+ }
+ return sensor_call(cam, core, s_register, reg);
+}
+#endif
+
+static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = {
+ .vidioc_querycap = mcam_vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = mcam_vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = mcam_vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = mcam_vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = mcam_vidioc_g_fmt_vid_cap,
+ .vidioc_enum_input = mcam_vidioc_enum_input,
+ .vidioc_g_input = mcam_vidioc_g_input,
+ .vidioc_s_input = mcam_vidioc_s_input,
+ .vidioc_s_std = mcam_vidioc_s_std,
+ .vidioc_reqbufs = mcam_vidioc_reqbufs,
+ .vidioc_querybuf = mcam_vidioc_querybuf,
+ .vidioc_qbuf = mcam_vidioc_qbuf,
+ .vidioc_dqbuf = mcam_vidioc_dqbuf,
+ .vidioc_streamon = mcam_vidioc_streamon,
+ .vidioc_streamoff = mcam_vidioc_streamoff,
+ .vidioc_queryctrl = mcam_vidioc_queryctrl,
+ .vidioc_g_ctrl = mcam_vidioc_g_ctrl,
+ .vidioc_s_ctrl = mcam_vidioc_s_ctrl,
+ .vidioc_g_parm = mcam_vidioc_g_parm,
+ .vidioc_s_parm = mcam_vidioc_s_parm,
+ .vidioc_enum_framesizes = mcam_vidioc_enum_framesizes,
+ .vidioc_enum_frameintervals = mcam_vidioc_enum_frameintervals,
+ .vidioc_g_chip_ident = mcam_vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = mcam_vidioc_g_register,
+ .vidioc_s_register = mcam_vidioc_s_register,
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Our various file operations.
+ */
+static int mcam_v4l_open(struct file *filp)
+{
+ struct mcam_camera *cam = video_drvdata(filp);
+ int ret = 0;
+
+ filp->private_data = cam;
+
+ frames = singles = delivered = 0;
+ mutex_lock(&cam->s_mutex);
+ if (cam->users == 0) {
+ ret = mcam_setup_vb2(cam);
+ if (ret)
+ goto out;
+ mcam_ctlr_power_up(cam);
+ __mcam_cam_reset(cam);
+ mcam_set_config_needed(cam, 1);
+ }
+ (cam->users)++;
+out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_v4l_release(struct file *filp)
+{
+ struct mcam_camera *cam = filp->private_data;
+
+ cam_err(cam, "Release, %d frames, %d singles, %d delivered\n", frames,
+ singles, delivered);
+ mutex_lock(&cam->s_mutex);
+ (cam->users)--;
+ if (filp == cam->owner) {
+ mcam_ctlr_stop_dma(cam);
+ cam->owner = NULL;
+ }
+ if (cam->users == 0) {
+ mcam_cleanup_vb2(cam);
+ mcam_ctlr_power_down(cam);
+ if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
+ mcam_free_dma_bufs(cam);
+ }
+ mutex_unlock(&cam->s_mutex);
+ return 0;
+}
+
+static ssize_t mcam_v4l_read(struct file *filp,
+ char __user *buffer, size_t len, loff_t *pos)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_read(&cam->vb_queue, buffer, len, pos,
+ filp->f_flags & O_NONBLOCK);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+
+static unsigned int mcam_v4l_poll(struct file *filp,
+ struct poll_table_struct *pt)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_poll(&cam->vb_queue, filp, pt);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_v4l_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_mmap(&cam->vb_queue, vma);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+
+static const struct v4l2_file_operations mcam_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = mcam_v4l_open,
+ .release = mcam_v4l_release,
+ .read = mcam_v4l_read,
+ .poll = mcam_v4l_poll,
+ .mmap = mcam_v4l_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+
+/*
+ * This template device holds all of those v4l2 methods; we
+ * clone it for specific real devices.
+ */
+static struct video_device mcam_v4l_template = {
+ .name = "mcam",
+ .tvnorms = V4L2_STD_NTSC_M,
+ .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */
+
+ .fops = &mcam_v4l_fops,
+ .ioctl_ops = &mcam_v4l_ioctl_ops,
+ .release = video_device_release_empty,
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Interrupt handler stuff
+ */
+static void mcam_frame_complete(struct mcam_camera *cam, int frame)
+{
+ /*
+ * Basic frame housekeeping.
+ */
+ set_bit(frame, &cam->flags);
+ clear_bit(CF_DMA_ACTIVE, &cam->flags);
+ cam->next_buf = frame;
+ cam->buf_seq[frame] = ++(cam->sequence);
+ frames++;
+ /*
+ * "This should never happen"
+ */
+ if (cam->state != S_STREAMING)
+ return;
+ /*
+ * Process the frame and set up the next one.
+ */
+ cam->frame_complete(cam, frame);
+}
+
+
+/*
+ * The interrupt handler; this needs to be called from the
+ * platform irq handler with the lock held.
+ */
+int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
+{
+ unsigned int frame, handled = 0;
+
+ mcam_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */
+ /*
+ * Handle any frame completions. There really should
+ * not be more than one of these, or we have fallen
+ * far behind.
+ *
+ * When running in S/G mode, the frame number lacks any
+ * real meaning - there's only one descriptor array - but
+ * the controller still picks a different one to signal
+ * each time.
+ */
+ for (frame = 0; frame < cam->nbufs; frame++)
+ if (irqs & (IRQ_EOF0 << frame)) {
+ mcam_frame_complete(cam, frame);
+ handled = 1;
+ }
+ /*
+ * If a frame starts, note that we have DMA active. This
+ * code assumes that we won't get multiple frame interrupts
+ * at once; may want to rethink that.
+ */
+ if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) {
+ set_bit(CF_DMA_ACTIVE, &cam->flags);
+ handled = 1;
+ if (cam->buffer_mode == B_DMA_sg)
+ mcam_ctlr_stop(cam);
+ }
+ return handled;
+}
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Registration and such.
+ */
+static struct ov7670_config sensor_cfg = {
+ /*
+ * Exclude QCIF mode, because it only captures a tiny portion
+ * of the sensor FOV
+ */
+ .min_width = 320,
+ .min_height = 240,
+};
+
+
+int mccic_register(struct mcam_camera *cam)
+{
+ struct i2c_board_info ov7670_info = {
+ .type = "ov7670",
+ .addr = 0x42 >> 1,
+ .platform_data = &sensor_cfg,
+ };
+ int ret;
+
+ /*
+ * Validate the requested buffer mode.
+ */
+ if (buffer_mode >= 0)
+ cam->buffer_mode = buffer_mode;
+ if (cam->buffer_mode == B_DMA_sg &&
+ cam->chip_id == V4L2_IDENT_CAFE) {
+ printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, "
+ "attempting vmalloc mode instead\n");
+ cam->buffer_mode = B_vmalloc;
+ }
+ if (!mcam_buffer_mode_supported(cam->buffer_mode)) {
+ printk(KERN_ERR "marvell-cam: buffer mode %d unsupported\n",
+ cam->buffer_mode);
+ return -EINVAL;
+ }
+ /*
+ * Register with V4L
+ */
+ ret = v4l2_device_register(cam->dev, &cam->v4l2_dev);
+ if (ret)
+ return ret;
+
+ mutex_init(&cam->s_mutex);
+ cam->state = S_NOTREADY;
+ mcam_set_config_needed(cam, 1);
+ cam->pix_format = mcam_def_pix_format;
+ cam->mbus_code = mcam_def_mbus_code;
+ INIT_LIST_HEAD(&cam->buffers);
+ mcam_ctlr_init(cam);
+
+ /*
+ * Try to find the sensor.
+ */
+ sensor_cfg.clock_speed = cam->clock_speed;
+ sensor_cfg.use_smbus = cam->use_smbus;
+ cam->sensor_addr = ov7670_info.addr;
+ cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev,
+ cam->i2c_adapter, &ov7670_info, NULL);
+ if (cam->sensor == NULL) {
+ ret = -ENODEV;
+ goto out_unregister;
+ }
+
+ ret = mcam_cam_init(cam);
+ if (ret)
+ goto out_unregister;
+ /*
+ * Get the v4l2 setup done.
+ */
+ mutex_lock(&cam->s_mutex);
+ cam->vdev = mcam_v4l_template;
+ cam->vdev.debug = 0;
+ cam->vdev.v4l2_dev = &cam->v4l2_dev;
+ ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto out;
+ video_set_drvdata(&cam->vdev, cam);
+
+ /*
+ * If so requested, try to get our DMA buffers now.
+ */
+ if (cam->buffer_mode == B_vmalloc && !alloc_bufs_at_read) {
+ if (mcam_alloc_dma_bufs(cam, 1))
+ cam_warn(cam, "Unable to alloc DMA buffers at load"
+ " will try again later.");
+ }
+
+out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+out_unregister:
+ v4l2_device_unregister(&cam->v4l2_dev);
+ return ret;
+}
+
+
+void mccic_shutdown(struct mcam_camera *cam)
+{
+ /*
+ * If we have no users (and we really, really should have no
+ * users) the device will already be powered down. Trying to
+ * take it down again will wedge the machine, which is frowned
+ * upon.
+ */
+ if (cam->users > 0) {
+ cam_warn(cam, "Removing a device with users!\n");
+ mcam_ctlr_power_down(cam);
+ }
+ vb2_queue_release(&cam->vb_queue);
+ if (cam->buffer_mode == B_vmalloc)
+ mcam_free_dma_bufs(cam);
+ video_unregister_device(&cam->vdev);
+ v4l2_device_unregister(&cam->v4l2_dev);
+}
+
+/*
+ * Power management
+ */
+#ifdef CONFIG_PM
+
+void mccic_suspend(struct mcam_camera *cam)
+{
+ enum mcam_state cstate = cam->state;
+
+ mcam_ctlr_stop_dma(cam);
+ mcam_ctlr_power_down(cam);
+ cam->state = cstate;
+}
+
+int mccic_resume(struct mcam_camera *cam)
+{
+ int ret = 0;
+
+ mutex_lock(&cam->s_mutex);
+ if (cam->users > 0) {
+ mcam_ctlr_power_up(cam);
+ __mcam_cam_reset(cam);
+ } else {
+ mcam_ctlr_power_down(cam);
+ }
+ mutex_unlock(&cam->s_mutex);
+
+ set_bit(CF_CONFIG_NEEDED, &cam->flags);
+ if (cam->state == S_STREAMING)
+ ret = mcam_read_setup(cam);
+ return ret;
+}
+#endif /* CONFIG_PM */
diff --git a/drivers/media/video/marvell-ccic/mcam-core.h b/drivers/media/video/marvell-ccic/mcam-core.h
new file mode 100644
index 000000000000..917200e63255
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/mcam-core.h
@@ -0,0 +1,323 @@
+/*
+ * Marvell camera core structures.
+ *
+ * Copyright 2011 Jonathan Corbet corbet@lwn.net
+ */
+#ifndef _MCAM_CORE_H
+#define _MCAM_CORE_H
+
+#include <linux/list.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-core.h>
+
+/*
+ * Create our own symbols for the supported buffer modes, but, for now,
+ * base them entirely on which videobuf2 options have been selected.
+ */
+#if defined(CONFIG_VIDEOBUF2_VMALLOC) || defined(CONFIG_VIDEOBUF2_VMALLOC_MODULE)
+#define MCAM_MODE_VMALLOC 1
+#endif
+
+#if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) || defined(CONFIG_VIDEOBUF2_DMA_CONTIG_MODULE)
+#define MCAM_MODE_DMA_CONTIG 1
+#endif
+
+#if defined(CONFIG_VIDEOBUF2_DMA_SG) || defined(CONFIG_VIDEOBUF2_DMA_SG_MODULE)
+#define MCAM_MODE_DMA_SG 1
+#endif
+
+#if !defined(MCAM_MODE_VMALLOC) && !defined(MCAM_MODE_DMA_CONTIG) && \
+ !defined(MCAM_MODE_DMA_SG)
+#error One of the videobuf buffer modes must be selected in the config
+#endif
+
+
+enum mcam_state {
+ S_NOTREADY, /* Not yet initialized */
+ S_IDLE, /* Just hanging around */
+ S_FLAKED, /* Some sort of problem */
+ S_STREAMING, /* Streaming data */
+ S_BUFWAIT /* streaming requested but no buffers yet */
+};
+#define MAX_DMA_BUFS 3
+
+/*
+ * Different platforms work best with different buffer modes, so we
+ * let the platform pick.
+ */
+enum mcam_buffer_mode {
+ B_vmalloc = 0,
+ B_DMA_contig = 1,
+ B_DMA_sg = 2
+};
+
+/*
+ * Is a given buffer mode supported by the current kernel configuration?
+ */
+static inline int mcam_buffer_mode_supported(enum mcam_buffer_mode mode)
+{
+ switch (mode) {
+#ifdef MCAM_MODE_VMALLOC
+ case B_vmalloc:
+#endif
+#ifdef MCAM_MODE_DMA_CONTIG
+ case B_DMA_contig:
+#endif
+#ifdef MCAM_MODE_DMA_SG
+ case B_DMA_sg:
+#endif
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+/*
+ * A description of one of our devices.
+ * Locking: controlled by s_mutex. Certain fields, however, require
+ * the dev_lock spinlock; they are marked as such by comments.
+ * dev_lock is also required for access to device registers.
+ */
+struct mcam_camera {
+ /*
+ * These fields should be set by the platform code prior to
+ * calling mcam_register().
+ */
+ struct i2c_adapter *i2c_adapter;
+ unsigned char __iomem *regs;
+ spinlock_t dev_lock;
+ struct device *dev; /* For messages, dma alloc */
+ unsigned int chip_id;
+ short int clock_speed; /* Sensor clock speed, default 30 */
+ short int use_smbus; /* SMBUS or straight I2c? */
+ enum mcam_buffer_mode buffer_mode;
+ /*
+ * Callbacks from the core to the platform code.
+ */
+ void (*plat_power_up) (struct mcam_camera *cam);
+ void (*plat_power_down) (struct mcam_camera *cam);
+
+ /*
+ * Everything below here is private to the mcam core and
+ * should not be touched by the platform code.
+ */
+ struct v4l2_device v4l2_dev;
+ enum mcam_state state;
+ unsigned long flags; /* Buffer status, mainly (dev_lock) */
+ int users; /* How many open FDs */
+ struct file *owner; /* Who has data access (v4l2) */
+
+ /*
+ * Subsystem structures.
+ */
+ struct video_device vdev;
+ struct v4l2_subdev *sensor;
+ unsigned short sensor_addr;
+
+ /* Videobuf2 stuff */
+ struct vb2_queue vb_queue;
+ struct list_head buffers; /* Available frames */
+
+ unsigned int nbufs; /* How many are alloc'd */
+ int next_buf; /* Next to consume (dev_lock) */
+
+ /* DMA buffers - vmalloc mode */
+#ifdef MCAM_MODE_VMALLOC
+ unsigned int dma_buf_size; /* allocated size */
+ void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */
+ dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */
+ struct tasklet_struct s_tasklet;
+#endif
+ unsigned int sequence; /* Frame sequence number */
+ unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual bufs */
+
+ /* DMA buffers - DMA modes */
+ struct mcam_vb_buffer *vb_bufs[MAX_DMA_BUFS];
+ struct vb2_alloc_ctx *vb_alloc_ctx;
+
+ /* Mode-specific ops, set at open time */
+ void (*dma_setup)(struct mcam_camera *cam);
+ void (*frame_complete)(struct mcam_camera *cam, int frame);
+
+ /* Current operating parameters */
+ u32 sensor_type; /* Currently ov7670 only */
+ struct v4l2_pix_format pix_format;
+ enum v4l2_mbus_pixelcode mbus_code;
+
+ /* Locks */
+ struct mutex s_mutex; /* Access to this structure */
+};
+
+
+/*
+ * Register I/O functions. These are here because the platform code
+ * may legitimately need to mess with the register space.
+ */
+/*
+ * Device register I/O
+ */
+static inline void mcam_reg_write(struct mcam_camera *cam, unsigned int reg,
+ unsigned int val)
+{
+ iowrite32(val, cam->regs + reg);
+}
+
+static inline unsigned int mcam_reg_read(struct mcam_camera *cam,
+ unsigned int reg)
+{
+ return ioread32(cam->regs + reg);
+}
+
+
+static inline void mcam_reg_write_mask(struct mcam_camera *cam, unsigned int reg,
+ unsigned int val, unsigned int mask)
+{
+ unsigned int v = mcam_reg_read(cam, reg);
+
+ v = (v & ~mask) | (val & mask);
+ mcam_reg_write(cam, reg, v);
+}
+
+static inline void mcam_reg_clear_bit(struct mcam_camera *cam,
+ unsigned int reg, unsigned int val)
+{
+ mcam_reg_write_mask(cam, reg, 0, val);
+}
+
+static inline void mcam_reg_set_bit(struct mcam_camera *cam,
+ unsigned int reg, unsigned int val)
+{
+ mcam_reg_write_mask(cam, reg, val, val);
+}
+
+/*
+ * Functions for use by platform code.
+ */
+int mccic_register(struct mcam_camera *cam);
+int mccic_irq(struct mcam_camera *cam, unsigned int irqs);
+void mccic_shutdown(struct mcam_camera *cam);
+#ifdef CONFIG_PM
+void mccic_suspend(struct mcam_camera *cam);
+int mccic_resume(struct mcam_camera *cam);
+#endif
+
+/*
+ * Register definitions for the m88alp01 camera interface. Offsets in bytes
+ * as given in the spec.
+ */
+#define REG_Y0BAR 0x00
+#define REG_Y1BAR 0x04
+#define REG_Y2BAR 0x08
+/* ... */
+
+#define REG_IMGPITCH 0x24 /* Image pitch register */
+#define IMGP_YP_SHFT 2 /* Y pitch params */
+#define IMGP_YP_MASK 0x00003ffc /* Y pitch field */
+#define IMGP_UVP_SHFT 18 /* UV pitch (planar) */
+#define IMGP_UVP_MASK 0x3ffc0000
+#define REG_IRQSTATRAW 0x28 /* RAW IRQ Status */
+#define IRQ_EOF0 0x00000001 /* End of frame 0 */
+#define IRQ_EOF1 0x00000002 /* End of frame 1 */
+#define IRQ_EOF2 0x00000004 /* End of frame 2 */
+#define IRQ_SOF0 0x00000008 /* Start of frame 0 */
+#define IRQ_SOF1 0x00000010 /* Start of frame 1 */
+#define IRQ_SOF2 0x00000020 /* Start of frame 2 */
+#define IRQ_OVERFLOW 0x00000040 /* FIFO overflow */
+#define IRQ_TWSIW 0x00010000 /* TWSI (smbus) write */
+#define IRQ_TWSIR 0x00020000 /* TWSI read */
+#define IRQ_TWSIE 0x00040000 /* TWSI error */
+#define TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE)
+#define FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2)
+#define ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW)
+#define REG_IRQMASK 0x2c /* IRQ mask - same bits as IRQSTAT */
+#define REG_IRQSTAT 0x30 /* IRQ status / clear */
+
+#define REG_IMGSIZE 0x34 /* Image size */
+#define IMGSZ_V_MASK 0x1fff0000
+#define IMGSZ_V_SHIFT 16
+#define IMGSZ_H_MASK 0x00003fff
+#define REG_IMGOFFSET 0x38 /* IMage offset */
+
+#define REG_CTRL0 0x3c /* Control 0 */
+#define C0_ENABLE 0x00000001 /* Makes the whole thing go */
+
+/* Mask for all the format bits */
+#define C0_DF_MASK 0x00fffffc /* Bits 2-23 */
+
+/* RGB ordering */
+#define C0_RGB4_RGBX 0x00000000
+#define C0_RGB4_XRGB 0x00000004
+#define C0_RGB4_BGRX 0x00000008
+#define C0_RGB4_XBGR 0x0000000c
+#define C0_RGB5_RGGB 0x00000000
+#define C0_RGB5_GRBG 0x00000004
+#define C0_RGB5_GBRG 0x00000008
+#define C0_RGB5_BGGR 0x0000000c
+
+/* Spec has two fields for DIN and DOUT, but they must match, so
+ combine them here. */
+#define C0_DF_YUV 0x00000000 /* Data is YUV */
+#define C0_DF_RGB 0x000000a0 /* ... RGB */
+#define C0_DF_BAYER 0x00000140 /* ... Bayer */
+/* 8-8-8 must be missing from the below - ask */
+#define C0_RGBF_565 0x00000000
+#define C0_RGBF_444 0x00000800
+#define C0_RGB_BGR 0x00001000 /* Blue comes first */
+#define C0_YUV_PLANAR 0x00000000 /* YUV 422 planar format */
+#define C0_YUV_PACKED 0x00008000 /* YUV 422 packed */
+#define C0_YUV_420PL 0x0000a000 /* YUV 420 planar */
+/* Think that 420 packed must be 111 - ask */
+#define C0_YUVE_YUYV 0x00000000 /* Y1CbY0Cr */
+#define C0_YUVE_YVYU 0x00010000 /* Y1CrY0Cb */
+#define C0_YUVE_VYUY 0x00020000 /* CrY1CbY0 */
+#define C0_YUVE_UYVY 0x00030000 /* CbY1CrY0 */
+#define C0_YUVE_XYUV 0x00000000 /* 420: .YUV */
+#define C0_YUVE_XYVU 0x00010000 /* 420: .YVU */
+#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */
+#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */
+/* Bayer bits 18,19 if needed */
+#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */
+#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */
+#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */
+#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */
+#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
+#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */
+#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
+
+/* Bits below C1_444ALPHA are not present in Cafe */
+#define REG_CTRL1 0x40 /* Control 1 */
+#define C1_CLKGATE 0x00000001 /* Sensor clock gate */
+#define C1_DESC_ENA 0x00000100 /* DMA descriptor enable */
+#define C1_DESC_3WORD 0x00000200 /* Three-word descriptors used */
+#define C1_444ALPHA 0x00f00000 /* Alpha field in RGB444 */
+#define C1_ALPHA_SHFT 20
+#define C1_DMAB32 0x00000000 /* 32-byte DMA burst */
+#define C1_DMAB16 0x02000000 /* 16-byte DMA burst */
+#define C1_DMAB64 0x04000000 /* 64-byte DMA burst */
+#define C1_DMAB_MASK 0x06000000
+#define C1_TWOBUFS 0x08000000 /* Use only two DMA buffers */
+#define C1_PWRDWN 0x10000000 /* Power down */
+
+#define REG_CLKCTRL 0x88 /* Clock control */
+#define CLK_DIV_MASK 0x0000ffff /* Upper bits RW "reserved" */
+
+/* This appears to be a Cafe-only register */
+#define REG_UBAR 0xc4 /* Upper base address register */
+
+/* Armada 610 DMA descriptor registers */
+#define REG_DMA_DESC_Y 0x200
+#define REG_DMA_DESC_U 0x204
+#define REG_DMA_DESC_V 0x208
+#define REG_DESC_LEN_Y 0x20c /* Lengths are in bytes */
+#define REG_DESC_LEN_U 0x210
+#define REG_DESC_LEN_V 0x214
+
+/*
+ * Useful stuff that probably belongs somewhere global.
+ */
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+
+#endif /* _MCAM_CORE_H */
diff --git a/drivers/media/video/marvell-ccic/mmp-driver.c b/drivers/media/video/marvell-ccic/mmp-driver.c
new file mode 100644
index 000000000000..fb0b124b35f3
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/mmp-driver.c
@@ -0,0 +1,341 @@
+/*
+ * Support for the camera device found on Marvell MMP processors; known
+ * to work with the Armada 610 as used in the OLPC 1.75 system.
+ *
+ * Copyright 2011 Jonathan Corbet <corbet@lwn.net>
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-gpio.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/mmp-camera.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+
+#include "mcam-core.h"
+
+MODULE_ALIAS("platform:mmp-camera");
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_LICENSE("GPL");
+
+struct mmp_camera {
+ void *power_regs;
+ struct platform_device *pdev;
+ struct mcam_camera mcam;
+ struct list_head devlist;
+ int irq;
+};
+
+static inline struct mmp_camera *mcam_to_cam(struct mcam_camera *mcam)
+{
+ return container_of(mcam, struct mmp_camera, mcam);
+}
+
+/*
+ * A silly little infrastructure so we can keep track of our devices.
+ * Chances are that we will never have more than one of them, but
+ * the Armada 610 *does* have two controllers...
+ */
+
+static LIST_HEAD(mmpcam_devices);
+static struct mutex mmpcam_devices_lock;
+
+static void mmpcam_add_device(struct mmp_camera *cam)
+{
+ mutex_lock(&mmpcam_devices_lock);
+ list_add(&cam->devlist, &mmpcam_devices);
+ mutex_unlock(&mmpcam_devices_lock);
+}
+
+static void mmpcam_remove_device(struct mmp_camera *cam)
+{
+ mutex_lock(&mmpcam_devices_lock);
+ list_del(&cam->devlist);
+ mutex_unlock(&mmpcam_devices_lock);
+}
+
+/*
+ * Platform dev remove passes us a platform_device, and there's
+ * no handy unused drvdata to stash a backpointer in. So just
+ * dig it out of our list.
+ */
+static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev)
+{
+ struct mmp_camera *cam;
+
+ mutex_lock(&mmpcam_devices_lock);
+ list_for_each_entry(cam, &mmpcam_devices, devlist) {
+ if (cam->pdev == pdev) {
+ mutex_unlock(&mmpcam_devices_lock);
+ return cam;
+ }
+ }
+ mutex_unlock(&mmpcam_devices_lock);
+ return NULL;
+}
+
+
+
+
+/*
+ * Power-related registers; this almost certainly belongs
+ * somewhere else.
+ *
+ * ARMADA 610 register manual, sec 7.2.1, p1842.
+ */
+#define CPU_SUBSYS_PMU_BASE 0xd4282800
+#define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */
+#define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */
+
+/*
+ * Power control.
+ */
+static void mmpcam_power_up(struct mcam_camera *mcam)
+{
+ struct mmp_camera *cam = mcam_to_cam(mcam);
+ struct mmp_camera_platform_data *pdata;
+/*
+ * Turn on power and clocks to the controller.
+ */
+ iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR);
+ iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR);
+ mdelay(1);
+/*
+ * Provide power to the sensor.
+ */
+ mcam_reg_write(mcam, REG_CLKCTRL, 0x60000002);
+ pdata = cam->pdev->dev.platform_data;
+ gpio_set_value(pdata->sensor_power_gpio, 1);
+ mdelay(5);
+ mcam_reg_clear_bit(mcam, REG_CTRL1, 0x10000000);
+ gpio_set_value(pdata->sensor_reset_gpio, 0); /* reset is active low */
+ mdelay(5);
+ gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */
+ mdelay(5);
+}
+
+static void mmpcam_power_down(struct mcam_camera *mcam)
+{
+ struct mmp_camera *cam = mcam_to_cam(mcam);
+ struct mmp_camera_platform_data *pdata;
+/*
+ * Turn off clocks and set reset lines
+ */
+ iowrite32(0, cam->power_regs + REG_CCIC_DCGCR);
+ iowrite32(0, cam->power_regs + REG_CCIC_CRCR);
+/*
+ * Shut down the sensor.
+ */
+ pdata = cam->pdev->dev.platform_data;
+ gpio_set_value(pdata->sensor_power_gpio, 0);
+ gpio_set_value(pdata->sensor_reset_gpio, 0);
+}
+
+
+static irqreturn_t mmpcam_irq(int irq, void *data)
+{
+ struct mcam_camera *mcam = data;
+ unsigned int irqs, handled;
+
+ spin_lock(&mcam->dev_lock);
+ irqs = mcam_reg_read(mcam, REG_IRQSTAT);
+ handled = mccic_irq(mcam, irqs);
+ spin_unlock(&mcam->dev_lock);
+ return IRQ_RETVAL(handled);
+}
+
+
+static int mmpcam_probe(struct platform_device *pdev)
+{
+ struct mmp_camera *cam;
+ struct mcam_camera *mcam;
+ struct resource *res;
+ struct mmp_camera_platform_data *pdata;
+ int ret;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (cam == NULL)
+ return -ENOMEM;
+ cam->pdev = pdev;
+ INIT_LIST_HEAD(&cam->devlist);
+
+ mcam = &cam->mcam;
+ mcam->platform = MHP_Armada610;
+ mcam->plat_power_up = mmpcam_power_up;
+ mcam->plat_power_down = mmpcam_power_down;
+ mcam->dev = &pdev->dev;
+ mcam->use_smbus = 0;
+ mcam->chip_id = V4L2_IDENT_ARMADA610;
+ mcam->buffer_mode = B_DMA_sg;
+ spin_lock_init(&mcam->dev_lock);
+ /*
+ * Get our I/O memory.
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no iomem resource!\n");
+ ret = -ENODEV;
+ goto out_free;
+ }
+ mcam->regs = ioremap(res->start, resource_size(res));
+ if (mcam->regs == NULL) {
+ dev_err(&pdev->dev, "MMIO ioremap fail\n");
+ ret = -ENODEV;
+ goto out_free;
+ }
+ /*
+ * Power/clock memory is elsewhere; get it too. Perhaps this
+ * should really be managed outside of this driver?
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no power resource!\n");
+ ret = -ENODEV;
+ goto out_unmap1;
+ }
+ cam->power_regs = ioremap(res->start, resource_size(res));
+ if (cam->power_regs == NULL) {
+ dev_err(&pdev->dev, "power MMIO ioremap fail\n");
+ ret = -ENODEV;
+ goto out_unmap1;
+ }
+ /*
+ * Find the i2c adapter. This assumes, of course, that the
+ * i2c bus is already up and functioning.
+ */
+ pdata = pdev->dev.platform_data;
+ mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device);
+ if (mcam->i2c_adapter == NULL) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "No i2c adapter\n");
+ goto out_unmap2;
+ }
+ /*
+ * Sensor GPIO pins.
+ */
+ ret = gpio_request(pdata->sensor_power_gpio, "cam-power");
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get sensor power gpio %d",
+ pdata->sensor_power_gpio);
+ goto out_unmap2;
+ }
+ gpio_direction_output(pdata->sensor_power_gpio, 0);
+ ret = gpio_request(pdata->sensor_reset_gpio, "cam-reset");
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get sensor reset gpio %d",
+ pdata->sensor_reset_gpio);
+ goto out_gpio;
+ }
+ gpio_direction_output(pdata->sensor_reset_gpio, 0);
+ /*
+ * Power the device up and hand it off to the core.
+ */
+ mmpcam_power_up(mcam);
+ ret = mccic_register(mcam);
+ if (ret)
+ goto out_gpio2;
+ /*
+ * Finally, set up our IRQ now that the core is ready to
+ * deal with it.
+ */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ ret = -ENODEV;
+ goto out_unregister;
+ }
+ cam->irq = res->start;
+ ret = request_irq(cam->irq, mmpcam_irq, IRQF_SHARED,
+ "mmp-camera", mcam);
+ if (ret == 0) {
+ mmpcam_add_device(cam);
+ return 0;
+ }
+
+out_unregister:
+ mccic_shutdown(mcam);
+out_gpio2:
+ mmpcam_power_down(mcam);
+ gpio_free(pdata->sensor_reset_gpio);
+out_gpio:
+ gpio_free(pdata->sensor_power_gpio);
+out_unmap2:
+ iounmap(cam->power_regs);
+out_unmap1:
+ iounmap(mcam->regs);
+out_free:
+ kfree(cam);
+ return ret;
+}
+
+
+static int mmpcam_remove(struct mmp_camera *cam)
+{
+ struct mcam_camera *mcam = &cam->mcam;
+ struct mmp_camera_platform_data *pdata;
+
+ mmpcam_remove_device(cam);
+ free_irq(cam->irq, mcam);
+ mccic_shutdown(mcam);
+ mmpcam_power_down(mcam);
+ pdata = cam->pdev->dev.platform_data;
+ gpio_free(pdata->sensor_reset_gpio);
+ gpio_free(pdata->sensor_power_gpio);
+ iounmap(cam->power_regs);
+ iounmap(mcam->regs);
+ kfree(cam);
+ return 0;
+}
+
+static int mmpcam_platform_remove(struct platform_device *pdev)
+{
+ struct mmp_camera *cam = mmpcam_find_device(pdev);
+
+ if (cam == NULL)
+ return -ENODEV;
+ return mmpcam_remove(cam);
+}
+
+
+static struct platform_driver mmpcam_driver = {
+ .probe = mmpcam_probe,
+ .remove = mmpcam_platform_remove,
+ .driver = {
+ .name = "mmp-camera",
+ .owner = THIS_MODULE
+ }
+};
+
+
+static int __init mmpcam_init_module(void)
+{
+ mutex_init(&mmpcam_devices_lock);
+ return platform_driver_register(&mmpcam_driver);
+}
+
+static void __exit mmpcam_exit_module(void)
+{
+ platform_driver_unregister(&mmpcam_driver);
+ /*
+ * platform_driver_unregister() should have emptied the list
+ */
+ if (!list_empty(&mmpcam_devices))
+ printk(KERN_ERR "mmp_camera leaving devices behind\n");
+}
+
+module_init(mmpcam_init_module);
+module_exit(mmpcam_exit_module);
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
index b03d74e09a3c..12897e8a3314 100644
--- a/drivers/media/video/mem2mem_testdev.c
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -19,7 +19,6 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/fs.h>
-#include <linux/version.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -35,7 +34,7 @@
MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
MODULE_LICENSE("GPL");
-
+MODULE_VERSION("0.1.1");
#define MIN_W 32
#define MIN_H 32
@@ -380,7 +379,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
- cap->version = KERNEL_VERSION(0, 1, 0);
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
| V4L2_CAP_STREAMING;
@@ -740,9 +738,10 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
* Queue operations
*/
-static int m2mtest_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
- unsigned int *nplanes, unsigned long sizes[],
- void *alloc_ctxs[])
+static int m2mtest_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq);
struct m2mtest_q_data *q_data;
@@ -795,10 +794,24 @@ static void m2mtest_buf_queue(struct vb2_buffer *vb)
v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
}
+static void m2mtest_wait_prepare(struct vb2_queue *q)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ m2mtest_unlock(ctx);
+}
+
+static void m2mtest_wait_finish(struct vb2_queue *q)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ m2mtest_lock(ctx);
+}
+
static struct vb2_ops m2mtest_qops = {
.queue_setup = m2mtest_queue_setup,
.buf_prepare = m2mtest_buf_prepare,
.buf_queue = m2mtest_buf_queue,
+ .wait_prepare = m2mtest_wait_prepare,
+ .wait_finish = m2mtest_wait_finish,
};
static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index c43c81f5f978..d0f538857285 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -426,6 +426,20 @@ static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
return 0;
}
+static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ *id &= state->detected_std;
+
+ v4l_dbg(2, msp_debug, client,
+ "detected standard: %s(0x%08Lx)\n",
+ msp_standard_std_name(state->std), state->detected_std);
+
+ return 0;
+}
+
static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id)
{
struct msp_state *state = to_state(sd);
@@ -616,6 +630,10 @@ static const struct v4l2_subdev_core_ops msp_core_ops = {
.s_std = msp_s_std,
};
+static const struct v4l2_subdev_video_ops msp_video_ops = {
+ .querystd = msp_querystd,
+};
+
static const struct v4l2_subdev_tuner_ops msp_tuner_ops = {
.s_frequency = msp_s_frequency,
.g_tuner = msp_g_tuner,
@@ -630,6 +648,7 @@ static const struct v4l2_subdev_audio_ops msp_audio_ops = {
static const struct v4l2_subdev_ops msp_ops = {
.core = &msp_core_ops,
+ .video = &msp_video_ops,
.tuner = &msp_tuner_ops,
.audio = &msp_audio_ops,
};
@@ -664,6 +683,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
v4l2_i2c_subdev_init(sd, client, &msp_ops);
state->v4l2_std = V4L2_STD_NTSC;
+ state->detected_std = V4L2_STD_ALL;
state->audmode = V4L2_TUNER_MODE_STEREO;
state->input = -1;
state->i2s_mode = 0;
diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h
index 32a478e532f3..831e8db4368c 100644
--- a/drivers/media/video/msp3400-driver.h
+++ b/drivers/media/video/msp3400-driver.h
@@ -75,7 +75,7 @@ struct msp_state {
int opmode;
int std;
int mode;
- v4l2_std_id v4l2_std;
+ v4l2_std_id v4l2_std, detected_std;
int nicam_on;
int acb;
int in_scart;
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c
index 80387e2c3eca..f8b51714f2f9 100644
--- a/drivers/media/video/msp3400-kthreads.c
+++ b/drivers/media/video/msp3400-kthreads.c
@@ -37,29 +37,49 @@ static struct {
int retval;
int main, second;
char *name;
+ v4l2_std_id std;
} msp_stdlist[] = {
- { 0x0000, 0, 0, "could not detect sound standard" },
- { 0x0001, 0, 0, "autodetect start" },
- { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" },
- { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" },
- { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" },
- { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" },
- { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" },
- { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), "6.5/5.74 D/K3 Dual FM-Stereo" },
- { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" },
- { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" },
- { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" },
- { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" },
- { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" },
- { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV3)" },
- { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" },
- { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" },
- { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" },
- { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" },
- { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" },
- { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" },
- { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" },
- { -1, 0, 0, NULL }, /* EOF */
+ { 0x0000, 0, 0, "could not detect sound standard", V4L2_STD_ALL },
+ { 0x0001, 0, 0, "autodetect start", V4L2_STD_ALL },
+ { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72),
+ "4.5/4.72 M Dual FM-Stereo", V4L2_STD_MN },
+ { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875),
+ "5.5/5.74 B/G Dual FM-Stereo", V4L2_STD_BG },
+ { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125),
+ "6.5/6.25 D/K1 Dual FM-Stereo", V4L2_STD_DK },
+ { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875),
+ "6.5/6.74 D/K2 Dual FM-Stereo", V4L2_STD_DK },
+ { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5),
+ "6.5 D/K FM-Mono (HDEV3)", V4L2_STD_DK },
+ { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875),
+ "6.5/5.74 D/K3 Dual FM-Stereo", V4L2_STD_DK },
+ { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85),
+ "5.5/5.85 B/G NICAM FM", V4L2_STD_BG },
+ { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 L NICAM AM", V4L2_STD_L },
+ { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55),
+ "6.0/6.55 I NICAM FM", V4L2_STD_PAL_I },
+ { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 D/K NICAM FM", V4L2_STD_DK },
+ { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 D/K NICAM FM (HDEV2)", V4L2_STD_DK },
+ { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 D/K NICAM FM (HDEV3)", V4L2_STD_DK },
+ { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5),
+ "4.5 M BTSC-Stereo", V4L2_STD_MTS },
+ { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5),
+ "4.5 M BTSC-Mono + SAP", V4L2_STD_MTS },
+ { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5),
+ "4.5 M EIA-J Japan Stereo", V4L2_STD_NTSC_M_JP },
+ { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7),
+ "10.7 FM-Stereo Radio", V4L2_STD_ALL },
+ { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5),
+ "6.5 SAT-Mono", V4L2_STD_ALL },
+ { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20),
+ "7.02/7.20 SAT-Stereo", V4L2_STD_ALL },
+ { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2),
+ "7.2 SAT ADR", V4L2_STD_ALL },
+ { -1, 0, 0, NULL, 0 }, /* EOF */
};
static struct msp3400c_init_data_dem {
@@ -156,6 +176,16 @@ const char *msp_standard_std_name(int std)
return "unknown";
}
+static v4l2_std_id msp_standard_std(int std)
+{
+ int i;
+
+ for (i = 0; msp_stdlist[i].name != NULL; i++)
+ if (msp_stdlist[i].retval == std)
+ return msp_stdlist[i].std;
+ return V4L2_STD_ALL;
+}
+
static void msp_set_source(struct i2c_client *client, u16 src)
{
struct msp_state *state = to_state(i2c_get_clientdata(client));
@@ -479,6 +509,7 @@ int msp3400c_thread(void *data)
int count, max1, max2, val1, val2, val, i;
v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n");
+ state->detected_std = V4L2_STD_ALL;
set_freezable();
for (;;) {
v4l_dbg(2, msp_debug, client, "msp3400 thread: sleep\n");
@@ -579,6 +610,7 @@ restart:
state->main = msp3400c_carrier_detect_main[max1].cdo;
switch (max1) {
case 1: /* 5.5 */
+ state->detected_std = V4L2_STD_BG | V4L2_STD_PAL_H;
if (max2 == 0) {
/* B/G FM-stereo */
state->second = msp3400c_carrier_detect_55[max2].cdo;
@@ -596,6 +628,7 @@ restart:
break;
case 2: /* 6.0 */
/* PAL I NICAM */
+ state->detected_std = V4L2_STD_PAL_I;
state->second = MSP_CARRIER(6.552);
msp3400c_set_mode(client, MSP_MODE_FM_NICAM2);
state->nicam_on = 1;
@@ -607,22 +640,26 @@ restart:
state->second = msp3400c_carrier_detect_65[max2].cdo;
msp3400c_set_mode(client, MSP_MODE_FM_TERRA);
state->watch_stereo = 1;
+ state->detected_std = V4L2_STD_DK;
} else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) {
/* L NICAM or AM-mono */
state->second = msp3400c_carrier_detect_65[max2].cdo;
msp3400c_set_mode(client, MSP_MODE_AM_NICAM);
state->watch_stereo = 1;
+ state->detected_std = V4L2_STD_L;
} else if (max2 == 0 && state->has_nicam) {
/* D/K NICAM */
state->second = msp3400c_carrier_detect_65[max2].cdo;
msp3400c_set_mode(client, MSP_MODE_FM_NICAM1);
state->nicam_on = 1;
state->watch_stereo = 1;
+ state->detected_std = V4L2_STD_DK;
} else {
goto no_second;
}
break;
case 0: /* 4.5 */
+ state->detected_std = V4L2_STD_MN;
default:
no_second:
state->second = msp3400c_carrier_detect_main[max1].cdo;
@@ -662,6 +699,7 @@ int msp3410d_thread(void *data)
int val, i, std, count;
v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n");
+ state->detected_std = V4L2_STD_ALL;
set_freezable();
for (;;) {
v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n");
@@ -743,6 +781,8 @@ restart:
msp_stdlist[8].name : "unknown", val);
state->std = val = 0x0009;
msp_write_dem(client, 0x20, val);
+ } else {
+ state->detected_std = msp_standard_std(state->std);
}
/* set stereo */
@@ -957,6 +997,7 @@ int msp34xxg_thread(void *data)
int val, i;
v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n");
+ state->detected_std = V4L2_STD_ALL;
set_freezable();
for (;;) {
v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n");
@@ -1013,6 +1054,7 @@ unmute:
v4l_dbg(1, msp_debug, client,
"detected standard: %s (0x%04x)\n",
msp_standard_std_name(state->std), state->std);
+ state->detected_std = msp_standard_std(state->std);
if (state->std == 9) {
/* AM NICAM mode */
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index e2bbd8c35c98..e2b1029b16cd 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -12,10 +12,13 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/log2.h>
+#include <linux/module.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
/*
* mt9m001 i2c address 0x5d
@@ -84,15 +87,19 @@ static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
struct mt9m001 {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
struct v4l2_rect rect; /* Sensor window */
const struct mt9m001_datafmt *fmt;
const struct mt9m001_datafmt *fmts;
int num_fmts;
int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
- unsigned int gain;
- unsigned int exposure;
+ unsigned int total_h;
unsigned short y_skip_top; /* Lines to skip at the top */
- unsigned char autoexposure;
};
static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
@@ -165,54 +172,13 @@ static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int mt9m001_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
-
- /* Only one width bit may be set */
- if (!is_power_of_2(width_flag))
- return -EINVAL;
-
- if (icl->set_bus_param)
- return icl->set_bus_param(icl, width_flag);
-
- /*
- * Without board specific bus width settings we only support the
- * sensors native bus width
- */
- if (width_flag == SOCAM_DATAWIDTH_10)
- return 0;
-
- return -EINVAL;
-}
-
-static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- /* MT9M001 has all capture_format parameters fixed */
- unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER;
-
- if (icl->query_bus_param)
- flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
- else
- flags |= SOCAM_DATAWIDTH_10;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
struct v4l2_rect rect = a->c;
- struct soc_camera_device *icd = client->dev.platform_data;
int ret;
const u16 hblank = 9, vblank = 25;
- unsigned int total_h;
if (mt9m001->fmts == mt9m001_colour_fmts)
/*
@@ -231,7 +197,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
soc_camera_limit_side(&rect.top, &rect.height,
MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
- total_h = rect.height + mt9m001->y_skip_top + vblank;
+ mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank;
/* Blanking and start values - default... */
ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
@@ -240,7 +206,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
/*
* The caller provides a supported format, as verified per
- * call to icd->try_fmt()
+ * call to .try_mbus_fmt()
*/
if (!ret)
ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
@@ -251,17 +217,8 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
if (!ret)
ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
rect.height + mt9m001->y_skip_top - 1);
- if (!ret && mt9m001->autoexposure) {
- ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h);
- if (!ret) {
- const struct v4l2_queryctrl *qctrl =
- soc_camera_find_qctrl(icd->ops,
- V4L2_CID_EXPOSURE);
- mt9m001->exposure = (524 + (total_h - 1) *
- (qctrl->maximum - qctrl->minimum)) /
- 1048 + qctrl->minimum;
- }
- }
+ if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO)
+ ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h);
if (!ret)
mt9m001->rect = rect;
@@ -421,107 +378,48 @@ static int mt9m001_s_register(struct v4l2_subdev *sd,
}
#endif
-static const struct v4l2_queryctrl mt9m001_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 1,
- .maximum = 255,
- .step = 1,
- .default_value = 255,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }
-};
-
-static struct soc_camera_ops mt9m001_ops = {
- .set_bus_param = mt9m001_set_bus_param,
- .query_bus_param = mt9m001_query_bus_param,
- .controls = mt9m001_controls,
- .num_controls = ARRAY_SIZE(mt9m001_controls),
-};
-
-static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- int data;
+ struct mt9m001 *mt9m001 = container_of(ctrl->handler,
+ struct mt9m001, hdl);
+ s32 min, max;
switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- data = reg_read(client, MT9M001_READ_OPTIONS2);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x8000);
- break;
case V4L2_CID_EXPOSURE_AUTO:
- ctrl->value = mt9m001->autoexposure;
- break;
- case V4L2_CID_GAIN:
- ctrl->value = mt9m001->gain;
- break;
- case V4L2_CID_EXPOSURE:
- ctrl->value = mt9m001->exposure;
+ min = mt9m001->exposure->minimum;
+ max = mt9m001->exposure->maximum;
+ mt9m001->exposure->val =
+ (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min;
break;
}
return 0;
}
-static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct mt9m001 *mt9m001 = container_of(ctrl->handler,
+ struct mt9m001, hdl);
+ struct v4l2_subdev *sd = &mt9m001->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- const struct v4l2_queryctrl *qctrl;
+ struct v4l2_ctrl *exp = mt9m001->exposure;
int data;
- qctrl = soc_camera_find_qctrl(&mt9m001_ops, ctrl->id);
-
- if (!qctrl)
- return -EINVAL;
-
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
else
data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
if (data < 0)
return -EIO;
- break;
+ return 0;
+
case V4L2_CID_GAIN:
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
/* See Datasheet Table 7, Gain settings. */
- if (ctrl->value <= qctrl->default_value) {
+ if (ctrl->val <= ctrl->default_value) {
/* Pack it into 0..1 step 0.125, register values 0..8 */
- unsigned long range = qctrl->default_value - qctrl->minimum;
- data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
+ unsigned long range = ctrl->default_value - ctrl->minimum;
+ data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
dev_dbg(&client->dev, "Setting gain %d\n", data);
data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
@@ -530,8 +428,8 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
} else {
/* Pack it into 1.125..15 variable step, register values 9..67 */
/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
- unsigned long range = qctrl->maximum - qctrl->default_value - 1;
- unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
+ unsigned long range = ctrl->maximum - ctrl->default_value - 1;
+ unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
111 + range / 2) / range + 9;
if (gain <= 32)
@@ -547,70 +445,44 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
if (data < 0)
return -EIO;
}
+ return 0;
- /* Success */
- mt9m001->gain = ctrl->value;
- break;
- case V4L2_CID_EXPOSURE:
- /* mt9m001 has maximum == default */
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
- else {
- unsigned long range = qctrl->maximum - qctrl->minimum;
- unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 +
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ unsigned long range = exp->maximum - exp->minimum;
+ unsigned long shutter = ((exp->val - exp->minimum) * 1048 +
range / 2) / range + 1;
dev_dbg(&client->dev,
"Setting shutter width from %d to %lu\n",
- reg_read(client, MT9M001_SHUTTER_WIDTH),
- shutter);
+ reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
return -EIO;
- mt9m001->exposure = ctrl->value;
- mt9m001->autoexposure = 0;
- }
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- if (ctrl->value) {
+ } else {
const u16 vblank = 25;
- unsigned int total_h = mt9m001->rect.height +
+
+ mt9m001->total_h = mt9m001->rect.height +
mt9m001->y_skip_top + vblank;
- if (reg_write(client, MT9M001_SHUTTER_WIDTH,
- total_h) < 0)
+ if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0)
return -EIO;
- qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
- mt9m001->exposure = (524 + (total_h - 1) *
- (qctrl->maximum - qctrl->minimum)) /
- 1048 + qctrl->minimum;
- mt9m001->autoexposure = 1;
- } else
- mt9m001->autoexposure = 0;
- break;
+ }
+ return 0;
}
- return 0;
+ return -EINVAL;
}
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
-static int mt9m001_video_probe(struct soc_camera_device *icd,
+static int mt9m001_video_probe(struct soc_camera_link *icl,
struct i2c_client *client)
{
struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
s32 data;
unsigned long flags;
int ret;
- /*
- * We must have a parent by now. And it cannot be a wrong one.
- * So this entire test is completely redundant.
- */
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
- return -ENODEV;
-
/* Enable the chip */
data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
dev_dbg(&client->dev, "write: %d\n", data);
@@ -665,18 +537,11 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
dev_err(&client->dev, "Failed to initialise the camera\n");
/* mt9m001_init() has reset the chip, returning registers to defaults */
- mt9m001->gain = 64;
- mt9m001->exposure = 255;
-
- return ret;
+ return v4l2_ctrl_handler_setup(&mt9m001->hdl);
}
-static void mt9m001_video_remove(struct soc_camera_device *icd)
+static void mt9m001_video_remove(struct soc_camera_link *icl)
{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- dev_dbg(&icd->dev, "Video removed: %p, %p\n",
- icd->dev.parent, icd->vdev);
if (icl->free_bus)
icl->free_bus(icl);
}
@@ -691,9 +556,12 @@ static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
return 0;
}
+static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = {
+ .g_volatile_ctrl = mt9m001_g_volatile_ctrl,
+ .s_ctrl = mt9m001_s_ctrl,
+};
+
static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
- .g_ctrl = mt9m001_g_ctrl,
- .s_ctrl = mt9m001_s_ctrl,
.g_chip_ident = mt9m001_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9m001_g_register,
@@ -714,6 +582,40 @@ static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int mt9m001_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ /* MT9M001 has all capture_format parameters fixed */
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9m001_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ const struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample;
+
+ if (icl->set_bus_param)
+ return icl->set_bus_param(icl, 1 << (bps - 1));
+
+ /*
+ * Without board specific bus width settings we only support the
+ * sensors native bus width
+ */
+ return bps == 10 ? 0 : -EINVAL;
+}
+
static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
.s_stream = mt9m001_s_stream,
.s_mbus_fmt = mt9m001_s_fmt,
@@ -723,6 +625,8 @@ static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
.g_crop = mt9m001_g_crop,
.cropcap = mt9m001_cropcap,
.enum_mbus_fmt = mt9m001_enum_fmt,
+ .g_mbus_config = mt9m001_g_mbus_config,
+ .s_mbus_config = mt9m001_s_mbus_config,
};
static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
@@ -739,17 +643,10 @@ static int mt9m001_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9m001 *mt9m001;
- struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "MT9M001: missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "MT9M001 driver needs platform data\n");
return -EINVAL;
@@ -766,25 +663,40 @@ static int mt9m001_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9m001->hdl, 4);
+ v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+ mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl,
+ &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9m001->subdev.ctrl_handler = &mt9m001->hdl;
+ if (mt9m001->hdl.error) {
+ int err = mt9m001->hdl.error;
- /* Second stage probe - when a capture adapter is there */
- icd->ops = &mt9m001_ops;
+ kfree(mt9m001);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+ /* Second stage probe - when a capture adapter is there */
mt9m001->y_skip_top = 0;
mt9m001->rect.left = MT9M001_COLUMN_SKIP;
mt9m001->rect.top = MT9M001_ROW_SKIP;
mt9m001->rect.width = MT9M001_MAX_WIDTH;
mt9m001->rect.height = MT9M001_MAX_HEIGHT;
- /*
- * Simulated autoexposure. If enabled, we calculate shutter width
- * ourselves in the driver based on vertical blanking and frame width
- */
- mt9m001->autoexposure = 1;
-
- ret = mt9m001_video_probe(icd, client);
+ ret = mt9m001_video_probe(icl, client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&mt9m001->hdl);
kfree(mt9m001);
}
@@ -794,10 +706,11 @@ static int mt9m001_probe(struct i2c_client *client,
static int mt9m001_remove(struct i2c_client *client)
{
struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- icd->ops = NULL;
- mt9m001_video_remove(icd);
+ v4l2_device_unregister_subdev(&mt9m001->subdev);
+ v4l2_ctrl_handler_free(&mt9m001->hdl);
+ mt9m001_video_remove(icl);
kfree(mt9m001);
return 0;
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index ebebed929627..cf2c0fb95f2f 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -13,10 +13,13 @@
#include <linux/log2.h>
#include <linux/gpio.h>
#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/module.h>
+#include <media/soc_camera.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
/*
* MT9M111, MT9M112 and MT9M131:
@@ -63,6 +66,12 @@
#define MT9M111_RESET_RESTART_FRAME (1 << 1)
#define MT9M111_RESET_RESET_MODE (1 << 0)
+#define MT9M111_RM_FULL_POWER_RD (0 << 10)
+#define MT9M111_RM_LOW_POWER_RD (1 << 10)
+#define MT9M111_RM_COL_SKIP_4X (1 << 5)
+#define MT9M111_RM_ROW_SKIP_4X (1 << 4)
+#define MT9M111_RM_COL_SKIP_2X (1 << 3)
+#define MT9M111_RM_ROW_SKIP_2X (1 << 2)
#define MT9M111_RMB_MIRROR_COLS (1 << 1)
#define MT9M111_RMB_MIRROR_ROWS (1 << 0)
#define MT9M111_CTXT_CTRL_RESTART (1 << 15)
@@ -95,7 +104,8 @@
#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14)
#define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1)
-
+#define MT9M111_OUTFMT_FLIP_BAYER_COL (1 << 9)
+#define MT9M111_OUTFMT_FLIP_BAYER_ROW (1 << 8)
#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14)
#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10)
#define MT9M111_OUTFMT_INV_PIX_CLOCK (1 << 9)
@@ -110,9 +120,8 @@
#define MT9M111_OUTFMT_TST_RAMP_FRAME (3 << 4)
#define MT9M111_OUTFMT_SHIFT_3_UP (1 << 3)
#define MT9M111_OUTFMT_AVG_CHROMA (1 << 2)
-#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y (1 << 1)
-#define MT9M111_OUTFMT_SWAP_RGB_EVEN (1 << 1)
-#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr (1 << 0)
+#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN (1 << 1)
+#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B (1 << 0)
/*
* Camera control register addresses (0x200..0x2ff not implemented)
@@ -122,6 +131,8 @@
#define reg_write(reg, val) mt9m111_reg_write(client, MT9M111_##reg, (val))
#define reg_set(reg, val) mt9m111_reg_set(client, MT9M111_##reg, (val))
#define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val))
+#define reg_mask(reg, val, mask) mt9m111_reg_mask(client, MT9M111_##reg, \
+ (val), (mask))
#define MT9M111_MIN_DARK_ROWS 8
#define MT9M111_MIN_DARK_COLS 26
@@ -153,7 +164,11 @@ static const struct mt9m111_datafmt mt9m111_colour_fmts[] = {
{V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
{V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG},
{V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
{V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB},
{V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
{V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
};
@@ -165,22 +180,18 @@ enum mt9m111_context {
struct mt9m111 {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *gain;
int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
* from v4l2-chip-ident.h */
enum mt9m111_context context;
struct v4l2_rect rect;
+ struct mutex power_lock; /* lock to protect power_count */
+ int power_count;
const struct mt9m111_datafmt *fmt;
- unsigned int gain;
- unsigned char autoexposure;
+ int lastpage; /* PageMap cache value */
unsigned char datawidth;
unsigned int powered:1;
- unsigned int hflip:1;
- unsigned int vflip:1;
- unsigned int swap_rgb_even_odd:1;
- unsigned int swap_rgb_red_blue:1;
- unsigned int swap_yuv_y_chromas:1;
- unsigned int swap_yuv_cb_cr:1;
- unsigned int autowhitebalance:1;
};
static struct mt9m111 *to_mt9m111(const struct i2c_client *client)
@@ -192,17 +203,17 @@ static int reg_page_map_set(struct i2c_client *client, const u16 reg)
{
int ret;
u16 page;
- static int lastpage = -1; /* PageMap cache value */
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
page = (reg >> 8);
- if (page == lastpage)
+ if (page == mt9m111->lastpage)
return 0;
if (page > 2)
return -EINVAL;
ret = i2c_smbus_write_word_data(client, MT9M111_PAGE_MAP, swab16(page));
if (!ret)
- lastpage = page;
+ mt9m111->lastpage = page;
return ret;
}
@@ -248,12 +259,26 @@ static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg,
int ret;
ret = mt9m111_reg_read(client, reg);
- return mt9m111_reg_write(client, reg, ret & ~data);
+ if (ret >= 0)
+ ret = mt9m111_reg_write(client, reg, ret & ~data);
+ return ret;
}
-static int mt9m111_set_context(struct i2c_client *client,
+static int mt9m111_reg_mask(struct i2c_client *client, const u16 reg,
+ const u16 data, const u16 mask)
+{
+ int ret;
+
+ ret = mt9m111_reg_read(client, reg);
+ if (ret >= 0)
+ ret = mt9m111_reg_write(client, reg, (ret & ~mask) | data);
+ return ret;
+}
+
+static int mt9m111_set_context(struct mt9m111 *mt9m111,
enum mt9m111_context ctxt)
{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
int valB = MT9M111_CTXT_CTRL_RESTART | MT9M111_CTXT_CTRL_DEFECTCOR_B
| MT9M111_CTXT_CTRL_RESIZE_B | MT9M111_CTXT_CTRL_CTRL2_B
| MT9M111_CTXT_CTRL_GAMMA_B | MT9M111_CTXT_CTRL_READ_MODE_B
@@ -267,10 +292,10 @@ static int mt9m111_set_context(struct i2c_client *client,
return reg_write(CONTEXT_CONTROL, valA);
}
-static int mt9m111_setup_rect(struct i2c_client *client,
+static int mt9m111_setup_rect(struct mt9m111 *mt9m111,
struct v4l2_rect *rect)
{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
int ret, is_raw_format;
int width = rect->width;
int height = rect->height;
@@ -312,81 +337,9 @@ static int mt9m111_setup_rect(struct i2c_client *client,
return ret;
}
-static int mt9m111_setup_pixfmt(struct i2c_client *client, u16 outfmt)
-{
- int ret;
- u16 mask = MT9M111_OUTFMT_PROCESSED_BAYER | MT9M111_OUTFMT_RGB |
- MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_SWAP_RGB_EVEN |
- MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 |
- MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr |
- MT9M111_OUTFMT_SWAP_YCbCr_C_Y;
-
- ret = reg_read(OUTPUT_FORMAT_CTRL2_A);
- if (ret >= 0)
- ret = reg_write(OUTPUT_FORMAT_CTRL2_A, (ret & ~mask) | outfmt);
- if (!ret)
- ret = reg_read(OUTPUT_FORMAT_CTRL2_B);
- if (ret >= 0)
- ret = reg_write(OUTPUT_FORMAT_CTRL2_B, (ret & ~mask) | outfmt);
-
- return ret;
-}
-
-static int mt9m111_setfmt_bayer8(struct i2c_client *client)
-{
- return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_PROCESSED_BAYER |
- MT9M111_OUTFMT_RGB);
-}
-
-static int mt9m111_setfmt_bayer10(struct i2c_client *client)
-{
- return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_BYPASS_IFP);
-}
-
-static int mt9m111_setfmt_rgb565(struct i2c_client *client)
-{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
- int val = 0;
-
- if (mt9m111->swap_rgb_red_blue)
- val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
- if (mt9m111->swap_rgb_even_odd)
- val |= MT9M111_OUTFMT_SWAP_RGB_EVEN;
- val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
-
- return mt9m111_setup_pixfmt(client, val);
-}
-
-static int mt9m111_setfmt_rgb555(struct i2c_client *client)
-{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
- int val = 0;
-
- if (mt9m111->swap_rgb_red_blue)
- val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
- if (mt9m111->swap_rgb_even_odd)
- val |= MT9M111_OUTFMT_SWAP_RGB_EVEN;
- val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
-
- return mt9m111_setup_pixfmt(client, val);
-}
-
-static int mt9m111_setfmt_yuv(struct i2c_client *client)
-{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
- int val = 0;
-
- if (mt9m111->swap_yuv_cb_cr)
- val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
- if (mt9m111->swap_yuv_y_chromas)
- val |= MT9M111_OUTFMT_SWAP_YCbCr_C_Y;
-
- return mt9m111_setup_pixfmt(client, val);
-}
-
-static int mt9m111_enable(struct i2c_client *client)
+static int mt9m111_enable(struct mt9m111 *mt9m111)
{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
int ret;
ret = reg_set(RESET, MT9M111_RESET_CHIP_ENABLE);
@@ -395,8 +348,9 @@ static int mt9m111_enable(struct i2c_client *client)
return ret;
}
-static int mt9m111_reset(struct i2c_client *client)
+static int mt9m111_reset(struct mt9m111 *mt9m111)
{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
int ret;
ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
@@ -409,26 +363,9 @@ static int mt9m111_reset(struct i2c_client *client)
return ret;
}
-static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
-{
- return 0;
-}
-
-static int mt9m111_make_rect(struct i2c_client *client,
+static int mt9m111_make_rect(struct mt9m111 *mt9m111,
struct v4l2_rect *rect)
{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
-
if (mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
/* Bayer format - even size lengths */
@@ -444,14 +381,14 @@ static int mt9m111_make_rect(struct i2c_client *client,
soc_camera_limit_side(&rect->top, &rect->height,
MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT);
- return mt9m111_setup_rect(client, rect);
+ return mt9m111_setup_rect(mt9m111, rect);
}
static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct v4l2_rect rect = a->c;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
int ret;
dev_dbg(&client->dev, "%s left=%d, top=%d, width=%d, height=%d\n",
@@ -460,7 +397,7 @@ static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- ret = mt9m111_make_rect(client, &rect);
+ ret = mt9m111_make_rect(mt9m111, &rect);
if (!ret)
mt9m111->rect = rect;
return ret;
@@ -468,8 +405,7 @@ static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
a->c = mt9m111->rect;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -496,8 +432,7 @@ static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
static int mt9m111_g_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
mf->width = mt9m111->rect.width;
mf->height = mt9m111->rect.height;
@@ -508,51 +443,73 @@ static int mt9m111_g_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int mt9m111_set_pixfmt(struct i2c_client *client,
+static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111,
enum v4l2_mbus_pixelcode code)
{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ u16 data_outfmt2, mask_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
+ MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB |
+ MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 |
+ MT9M111_OUTFMT_RGB444x | MT9M111_OUTFMT_RGBx444 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
int ret;
switch (code) {
case V4L2_MBUS_FMT_SBGGR8_1X8:
- ret = mt9m111_setfmt_bayer8(client);
+ data_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
+ MT9M111_OUTFMT_RGB;
break;
case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
- ret = mt9m111_setfmt_bayer10(client);
+ data_outfmt2 = MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB;
break;
case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
- ret = mt9m111_setfmt_rgb555(client);
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+ break;
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
break;
case V4L2_MBUS_FMT_RGB565_2X8_LE:
- ret = mt9m111_setfmt_rgb565(client);
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
+ break;
+ case V4L2_MBUS_FMT_BGR565_2X8_BE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ break;
+ case V4L2_MBUS_FMT_BGR565_2X8_LE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
break;
case V4L2_MBUS_FMT_UYVY8_2X8:
- mt9m111->swap_yuv_y_chromas = 0;
- mt9m111->swap_yuv_cb_cr = 0;
- ret = mt9m111_setfmt_yuv(client);
+ data_outfmt2 = 0;
break;
case V4L2_MBUS_FMT_VYUY8_2X8:
- mt9m111->swap_yuv_y_chromas = 0;
- mt9m111->swap_yuv_cb_cr = 1;
- ret = mt9m111_setfmt_yuv(client);
+ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
break;
case V4L2_MBUS_FMT_YUYV8_2X8:
- mt9m111->swap_yuv_y_chromas = 1;
- mt9m111->swap_yuv_cb_cr = 0;
- ret = mt9m111_setfmt_yuv(client);
+ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
break;
case V4L2_MBUS_FMT_YVYU8_2X8:
- mt9m111->swap_yuv_y_chromas = 1;
- mt9m111->swap_yuv_cb_cr = 1;
- ret = mt9m111_setfmt_yuv(client);
+ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
break;
default:
- dev_err(&client->dev, "Pixel format not handled : %x\n",
- code);
- ret = -EINVAL;
+ dev_err(&client->dev, "Pixel format not handled: %x\n", code);
+ return -EINVAL;
}
+ ret = reg_mask(OUTPUT_FORMAT_CTRL2_A, data_outfmt2,
+ mask_outfmt2);
+ if (!ret)
+ ret = reg_mask(OUTPUT_FORMAT_CTRL2_B, data_outfmt2,
+ mask_outfmt2);
+
return ret;
}
@@ -561,7 +518,7 @@ static int mt9m111_s_fmt(struct v4l2_subdev *sd,
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
const struct mt9m111_datafmt *fmt;
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
struct v4l2_rect rect = {
.left = mt9m111->rect.left,
.top = mt9m111->rect.top,
@@ -579,9 +536,9 @@ static int mt9m111_s_fmt(struct v4l2_subdev *sd,
"%s code=%x left=%d, top=%d, width=%d, height=%d\n", __func__,
mf->code, rect.left, rect.top, rect.width, rect.height);
- ret = mt9m111_make_rect(client, &rect);
+ ret = mt9m111_make_rect(mt9m111, &rect);
if (!ret)
- ret = mt9m111_set_pixfmt(client, mf->code);
+ ret = mt9m111_set_pixfmt(mt9m111, mf->code);
if (!ret) {
mt9m111->rect = rect;
mt9m111->fmt = fmt;
@@ -594,8 +551,7 @@ static int mt9m111_s_fmt(struct v4l2_subdev *sd,
static int mt9m111_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
const struct mt9m111_datafmt *fmt;
bool bayer = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE;
@@ -635,7 +591,7 @@ static int mt9m111_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *id)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
return -EINVAL;
@@ -689,58 +645,9 @@ static int mt9m111_s_register(struct v4l2_subdev *sd,
}
#endif
-static const struct v4l2_queryctrl mt9m111_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Verticaly",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontaly",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, { /* gain = 1/32*val (=>gain=1 if val==32) */
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 63 * 2 * 2,
- .step = 1,
- .default_value = 32,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }
-};
-
-static int mt9m111_resume(struct soc_camera_device *icd);
-static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state);
-
-static struct soc_camera_ops mt9m111_ops = {
- .suspend = mt9m111_suspend,
- .resume = mt9m111_resume,
- .query_bus_param = mt9m111_query_bus_param,
- .set_bus_param = mt9m111_set_bus_param,
- .controls = mt9m111_controls,
- .num_controls = ARRAY_SIZE(mt9m111_controls),
-};
-
-static int mt9m111_set_flip(struct i2c_client *client, int flip, int mask)
+static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask)
{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
int ret;
if (mt9m111->context == HIGHPOWER) {
@@ -758,8 +665,9 @@ static int mt9m111_set_flip(struct i2c_client *client, int flip, int mask)
return ret;
}
-static int mt9m111_get_global_gain(struct i2c_client *client)
+static int mt9m111_get_global_gain(struct mt9m111 *mt9m111)
{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
int data;
data = reg_read(GLOBAL_GAIN);
@@ -769,15 +677,14 @@ static int mt9m111_get_global_gain(struct i2c_client *client)
return data;
}
-static int mt9m111_set_global_gain(struct i2c_client *client, int gain)
+static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
u16 val;
if (gain > 63 * 2 * 2)
return -EINVAL;
- mt9m111->gain = gain;
if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
val = (1 << 10) | (1 << 9) | (gain / 4);
else if ((gain >= 64) && (gain < 64 * 2))
@@ -788,173 +695,87 @@ static int mt9m111_set_global_gain(struct i2c_client *client, int gain)
return reg_write(GLOBAL_GAIN, val);
}
-static int mt9m111_set_autoexposure(struct i2c_client *client, int on)
+static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on)
{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
- int ret;
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
if (on)
- ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
- else
- ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
-
- if (!ret)
- mt9m111->autoexposure = on;
-
- return ret;
+ return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
+ return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
}
-static int mt9m111_set_autowhitebalance(struct i2c_client *client, int on)
+static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on)
{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
- int ret;
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
if (on)
- ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
- else
- ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
-
- if (!ret)
- mt9m111->autowhitebalance = on;
-
- return ret;
+ return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+ return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
}
-static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = to_mt9m111(client);
- int data;
+ struct mt9m111 *mt9m111 = container_of(ctrl->handler,
+ struct mt9m111, hdl);
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- if (mt9m111->context == HIGHPOWER)
- data = reg_read(READ_MODE_B);
- else
- data = reg_read(READ_MODE_A);
-
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & MT9M111_RMB_MIRROR_ROWS);
- break;
- case V4L2_CID_HFLIP:
- if (mt9m111->context == HIGHPOWER)
- data = reg_read(READ_MODE_B);
- else
- data = reg_read(READ_MODE_A);
-
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS);
- break;
- case V4L2_CID_GAIN:
- data = mt9m111_get_global_gain(client);
- if (data < 0)
- return data;
- ctrl->value = data;
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- ctrl->value = mt9m111->autoexposure;
- break;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ctrl->value = mt9m111->autowhitebalance;
- break;
- }
- return 0;
-}
-
-static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = to_mt9m111(client);
- const struct v4l2_queryctrl *qctrl;
- int ret;
-
- qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id);
- if (!qctrl)
- return -EINVAL;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- mt9m111->vflip = ctrl->value;
- ret = mt9m111_set_flip(client, ctrl->value,
+ return mt9m111_set_flip(mt9m111, ctrl->val,
MT9M111_RMB_MIRROR_ROWS);
- break;
case V4L2_CID_HFLIP:
- mt9m111->hflip = ctrl->value;
- ret = mt9m111_set_flip(client, ctrl->value,
+ return mt9m111_set_flip(mt9m111, ctrl->val,
MT9M111_RMB_MIRROR_COLS);
- break;
case V4L2_CID_GAIN:
- ret = mt9m111_set_global_gain(client, ctrl->value);
- break;
+ return mt9m111_set_global_gain(mt9m111, ctrl->val);
case V4L2_CID_EXPOSURE_AUTO:
- ret = mt9m111_set_autoexposure(client, ctrl->value);
- break;
+ return mt9m111_set_autoexposure(mt9m111, ctrl->val);
case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = mt9m111_set_autowhitebalance(client, ctrl->value);
- break;
- default:
- ret = -EINVAL;
+ return mt9m111_set_autowhitebalance(mt9m111, ctrl->val);
}
- return ret;
+ return -EINVAL;
}
-static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state)
+static int mt9m111_suspend(struct mt9m111 *mt9m111)
{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
- struct mt9m111 *mt9m111 = to_mt9m111(client);
-
- mt9m111->gain = mt9m111_get_global_gain(client);
+ v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111));
return 0;
}
-static int mt9m111_restore_state(struct i2c_client *client)
+static void mt9m111_restore_state(struct mt9m111 *mt9m111)
{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
-
- mt9m111_set_context(client, mt9m111->context);
- mt9m111_set_pixfmt(client, mt9m111->fmt->code);
- mt9m111_setup_rect(client, &mt9m111->rect);
- mt9m111_set_flip(client, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
- mt9m111_set_flip(client, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
- mt9m111_set_global_gain(client, mt9m111->gain);
- mt9m111_set_autoexposure(client, mt9m111->autoexposure);
- mt9m111_set_autowhitebalance(client, mt9m111->autowhitebalance);
- return 0;
+ mt9m111_set_context(mt9m111, mt9m111->context);
+ mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
+ mt9m111_setup_rect(mt9m111, &mt9m111->rect);
+ v4l2_ctrl_handler_setup(&mt9m111->hdl);
}
-static int mt9m111_resume(struct soc_camera_device *icd)
+static int mt9m111_resume(struct mt9m111 *mt9m111)
{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
- struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret = 0;
if (mt9m111->powered) {
- ret = mt9m111_enable(client);
+ ret = mt9m111_enable(mt9m111);
if (!ret)
- ret = mt9m111_reset(client);
+ ret = mt9m111_reset(mt9m111);
if (!ret)
- ret = mt9m111_restore_state(client);
+ mt9m111_restore_state(mt9m111);
}
return ret;
}
-static int mt9m111_init(struct i2c_client *client)
+static int mt9m111_init(struct mt9m111 *mt9m111)
{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
int ret;
mt9m111->context = HIGHPOWER;
- ret = mt9m111_enable(client);
+ ret = mt9m111_enable(mt9m111);
if (!ret)
- ret = mt9m111_reset(client);
+ ret = mt9m111_reset(mt9m111);
if (!ret)
- ret = mt9m111_set_context(client, mt9m111->context);
- if (!ret)
- ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure);
+ ret = mt9m111_set_context(mt9m111, mt9m111->context);
if (ret)
dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
return ret;
@@ -964,27 +785,12 @@ static int mt9m111_init(struct i2c_client *client)
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
-static int mt9m111_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int mt9m111_video_probe(struct i2c_client *client)
{
struct mt9m111 *mt9m111 = to_mt9m111(client);
s32 data;
int ret;
- /*
- * We must have a parent by now. And it cannot be a wrong one.
- * So this entire test is completely redundant.
- */
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
- return -ENODEV;
-
- mt9m111->autoexposure = 1;
- mt9m111->autowhitebalance = 1;
-
- mt9m111->swap_rgb_even_odd = 1;
- mt9m111->swap_rgb_red_blue = 1;
-
data = reg_read(CHIP_VERSION);
switch (data) {
@@ -998,23 +804,59 @@ static int mt9m111_video_probe(struct soc_camera_device *icd,
dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
break;
default:
- ret = -ENODEV;
dev_err(&client->dev,
"No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
data);
- goto ei2c;
+ return -ENODEV;
}
- ret = mt9m111_init(client);
+ ret = mt9m111_init(mt9m111);
+ if (ret)
+ return ret;
+ return v4l2_ctrl_handler_setup(&mt9m111->hdl);
+}
+
+static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
-ei2c:
+ mutex_lock(&mt9m111->power_lock);
+
+ /*
+ * If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9m111->power_count == !on) {
+ if (on) {
+ ret = mt9m111_resume(mt9m111);
+ if (ret) {
+ dev_err(&client->dev,
+ "Failed to resume the sensor: %d\n", ret);
+ goto out;
+ }
+ } else {
+ mt9m111_suspend(mt9m111);
+ }
+ }
+
+ /* Update the power count. */
+ mt9m111->power_count += on ? 1 : -1;
+ WARN_ON(mt9m111->power_count < 0);
+
+out:
+ mutex_unlock(&mt9m111->power_lock);
return ret;
}
+static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
+ .s_ctrl = mt9m111_s_ctrl,
+};
+
static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
- .g_ctrl = mt9m111_g_ctrl,
- .s_ctrl = mt9m111_s_ctrl,
.g_chip_ident = mt9m111_g_chip_ident,
+ .s_power = mt9m111_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9m111_g_register,
.s_register = mt9m111_s_register,
@@ -1031,6 +873,21 @@ static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
.s_mbus_fmt = mt9m111_s_fmt,
.g_mbus_fmt = mt9m111_g_fmt,
@@ -1039,6 +896,7 @@ static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
.g_crop = mt9m111_g_crop,
.cropcap = mt9m111_cropcap,
.enum_mbus_fmt = mt9m111_enum_fmt,
+ .g_mbus_config = mt9m111_g_mbus_config,
};
static struct v4l2_subdev_ops mt9m111_subdev_ops = {
@@ -1050,17 +908,10 @@ static int mt9m111_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9m111 *mt9m111;
- struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "mt9m111: soc-camera data missing!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "mt9m111: driver needs platform data\n");
return -EINVAL;
@@ -1077,19 +928,37 @@ static int mt9m111_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9m111->hdl, 5);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32);
+ v4l2_ctrl_new_std_menu(&mt9m111->hdl,
+ &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9m111->subdev.ctrl_handler = &mt9m111->hdl;
+ if (mt9m111->hdl.error) {
+ int err = mt9m111->hdl.error;
- /* Second stage probe - when a capture adapter is there */
- icd->ops = &mt9m111_ops;
+ kfree(mt9m111);
+ return err;
+ }
+ /* Second stage probe - when a capture adapter is there */
mt9m111->rect.left = MT9M111_MIN_DARK_COLS;
mt9m111->rect.top = MT9M111_MIN_DARK_ROWS;
mt9m111->rect.width = MT9M111_MAX_WIDTH;
mt9m111->rect.height = MT9M111_MAX_HEIGHT;
mt9m111->fmt = &mt9m111_colour_fmts[0];
+ mt9m111->lastpage = -1;
- ret = mt9m111_video_probe(icd, client);
+ ret = mt9m111_video_probe(client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&mt9m111->hdl);
kfree(mt9m111);
}
@@ -1099,9 +968,9 @@ static int mt9m111_probe(struct i2c_client *client,
static int mt9m111_remove(struct i2c_client *client)
{
struct mt9m111 *mt9m111 = to_mt9m111(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
+ v4l2_device_unregister_subdev(&mt9m111->subdev);
+ v4l2_ctrl_handler_free(&mt9m111->hdl);
kfree(mt9m111);
return 0;
diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c
new file mode 100644
index 000000000000..73c068993f05
--- /dev/null
+++ b/drivers/media/video/mt9p031.c
@@ -0,0 +1,964 @@
+/*
+ * Driver for MT9P031 CMOS Image Sensor from Aptina
+ *
+ * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com>
+ * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Based on the MT9V032 driver and Bastian Hecht's code.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <media/v4l2-subdev.h>
+#include <linux/videodev2.h>
+
+#include <media/mt9p031.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#define MT9P031_PIXEL_ARRAY_WIDTH 2752
+#define MT9P031_PIXEL_ARRAY_HEIGHT 2004
+
+#define MT9P031_CHIP_VERSION 0x00
+#define MT9P031_CHIP_VERSION_VALUE 0x1801
+#define MT9P031_ROW_START 0x01
+#define MT9P031_ROW_START_MIN 0
+#define MT9P031_ROW_START_MAX 2004
+#define MT9P031_ROW_START_DEF 54
+#define MT9P031_COLUMN_START 0x02
+#define MT9P031_COLUMN_START_MIN 0
+#define MT9P031_COLUMN_START_MAX 2750
+#define MT9P031_COLUMN_START_DEF 16
+#define MT9P031_WINDOW_HEIGHT 0x03
+#define MT9P031_WINDOW_HEIGHT_MIN 2
+#define MT9P031_WINDOW_HEIGHT_MAX 2006
+#define MT9P031_WINDOW_HEIGHT_DEF 1944
+#define MT9P031_WINDOW_WIDTH 0x04
+#define MT9P031_WINDOW_WIDTH_MIN 2
+#define MT9P031_WINDOW_WIDTH_MAX 2752
+#define MT9P031_WINDOW_WIDTH_DEF 2592
+#define MT9P031_HORIZONTAL_BLANK 0x05
+#define MT9P031_HORIZONTAL_BLANK_MIN 0
+#define MT9P031_HORIZONTAL_BLANK_MAX 4095
+#define MT9P031_VERTICAL_BLANK 0x06
+#define MT9P031_VERTICAL_BLANK_MIN 0
+#define MT9P031_VERTICAL_BLANK_MAX 4095
+#define MT9P031_VERTICAL_BLANK_DEF 25
+#define MT9P031_OUTPUT_CONTROL 0x07
+#define MT9P031_OUTPUT_CONTROL_CEN 2
+#define MT9P031_OUTPUT_CONTROL_SYN 1
+#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82
+#define MT9P031_SHUTTER_WIDTH_UPPER 0x08
+#define MT9P031_SHUTTER_WIDTH_LOWER 0x09
+#define MT9P031_SHUTTER_WIDTH_MIN 1
+#define MT9P031_SHUTTER_WIDTH_MAX 1048575
+#define MT9P031_SHUTTER_WIDTH_DEF 1943
+#define MT9P031_PLL_CONTROL 0x10
+#define MT9P031_PLL_CONTROL_PWROFF 0x0050
+#define MT9P031_PLL_CONTROL_PWRON 0x0051
+#define MT9P031_PLL_CONTROL_USEPLL 0x0052
+#define MT9P031_PLL_CONFIG_1 0x11
+#define MT9P031_PLL_CONFIG_2 0x12
+#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a
+#define MT9P031_FRAME_RESTART 0x0b
+#define MT9P031_SHUTTER_DELAY 0x0c
+#define MT9P031_RST 0x0d
+#define MT9P031_RST_ENABLE 1
+#define MT9P031_RST_DISABLE 0
+#define MT9P031_READ_MODE_1 0x1e
+#define MT9P031_READ_MODE_2 0x20
+#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15)
+#define MT9P031_READ_MODE_2_COL_MIR (1 << 14)
+#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6)
+#define MT9P031_ROW_ADDRESS_MODE 0x22
+#define MT9P031_COLUMN_ADDRESS_MODE 0x23
+#define MT9P031_GLOBAL_GAIN 0x35
+#define MT9P031_GLOBAL_GAIN_MIN 8
+#define MT9P031_GLOBAL_GAIN_MAX 1024
+#define MT9P031_GLOBAL_GAIN_DEF 8
+#define MT9P031_GLOBAL_GAIN_MULT (1 << 6)
+#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b
+#define MT9P031_TEST_PATTERN 0xa0
+#define MT9P031_TEST_PATTERN_SHIFT 3
+#define MT9P031_TEST_PATTERN_ENABLE (1 << 0)
+#define MT9P031_TEST_PATTERN_DISABLE (0 << 0)
+#define MT9P031_TEST_PATTERN_GREEN 0xa1
+#define MT9P031_TEST_PATTERN_RED 0xa2
+#define MT9P031_TEST_PATTERN_BLUE 0xa3
+
+struct mt9p031_pll_divs {
+ u32 ext_freq;
+ u32 target_freq;
+ u8 m;
+ u8 n;
+ u8 p1;
+};
+
+struct mt9p031 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_rect crop; /* Sensor window */
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_ctrl_handler ctrls;
+ struct mt9p031_platform_data *pdata;
+ struct mutex power_lock; /* lock to protect power_count */
+ int power_count;
+ u16 xskip;
+ u16 yskip;
+
+ const struct mt9p031_pll_divs *pll;
+
+ /* Registers cache */
+ u16 output_control;
+ u16 mode2;
+};
+
+static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9p031, subdev);
+}
+
+static int mt9p031_read(struct i2c_client *client, u8 reg)
+{
+ s32 data = i2c_smbus_read_word_data(client, reg);
+ return data < 0 ? data : be16_to_cpu(data);
+}
+
+static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data)
+{
+ return i2c_smbus_write_word_data(client, reg, cpu_to_be16(data));
+}
+
+static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
+ u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 value = (mt9p031->output_control & ~clear) | set;
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value);
+ if (ret < 0)
+ return ret;
+
+ mt9p031->output_control = value;
+ return 0;
+}
+
+static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 value = (mt9p031->mode2 & ~clear) | set;
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_READ_MODE_2, value);
+ if (ret < 0)
+ return ret;
+
+ mt9p031->mode2 = value;
+ return 0;
+}
+
+static int mt9p031_reset(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ /* Disable chip output, synchronous option update */
+ ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
+ 0);
+}
+
+/*
+ * This static table uses ext_freq and vdd_io values to select suitable
+ * PLL dividers m, n and p1 which have been calculated as specifiec in p36
+ * of Aptina's mt9p031 datasheet. New values should be added here.
+ */
+static const struct mt9p031_pll_divs mt9p031_divs[] = {
+ /* ext_freq target_freq m n p1 */
+ {21000000, 48000000, 26, 2, 6}
+};
+
+static int mt9p031_pll_get_divs(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt9p031_divs); i++) {
+ if (mt9p031_divs[i].ext_freq == mt9p031->pdata->ext_freq &&
+ mt9p031_divs[i].target_freq == mt9p031->pdata->target_freq) {
+ mt9p031->pll = &mt9p031_divs[i];
+ return 0;
+ }
+ }
+
+ dev_err(&client->dev, "Couldn't find PLL dividers for ext_freq = %d, "
+ "target_freq = %d\n", mt9p031->pdata->ext_freq,
+ mt9p031->pdata->target_freq);
+ return -EINVAL;
+}
+
+static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
+ (mt9p031->pll->m << 8) | (mt9p031->pll->n - 1));
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll->p1 - 1);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 2000);
+ ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON |
+ MT9P031_PLL_CONTROL_USEPLL);
+ return ret;
+}
+
+static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+
+ return mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWROFF);
+}
+
+static int mt9p031_power_on(struct mt9p031 *mt9p031)
+{
+ /* Ensure RESET_BAR is low */
+ if (mt9p031->pdata->reset) {
+ mt9p031->pdata->reset(&mt9p031->subdev, 1);
+ usleep_range(1000, 2000);
+ }
+
+ /* Emable clock */
+ if (mt9p031->pdata->set_xclk)
+ mt9p031->pdata->set_xclk(&mt9p031->subdev,
+ mt9p031->pdata->ext_freq);
+
+ /* Now RESET_BAR must be high */
+ if (mt9p031->pdata->reset) {
+ mt9p031->pdata->reset(&mt9p031->subdev, 0);
+ usleep_range(1000, 2000);
+ }
+
+ return 0;
+}
+
+static void mt9p031_power_off(struct mt9p031 *mt9p031)
+{
+ if (mt9p031->pdata->reset) {
+ mt9p031->pdata->reset(&mt9p031->subdev, 1);
+ usleep_range(1000, 2000);
+ }
+
+ if (mt9p031->pdata->set_xclk)
+ mt9p031->pdata->set_xclk(&mt9p031->subdev, 0);
+}
+
+static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ if (!on) {
+ mt9p031_power_off(mt9p031);
+ return 0;
+ }
+
+ ret = mt9p031_power_on(mt9p031);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_reset(mt9p031);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to reset the camera\n");
+ return ret;
+ }
+
+ return v4l2_ctrl_handler_setup(&mt9p031->ctrls);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static int mt9p031_set_params(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ struct v4l2_mbus_framefmt *format = &mt9p031->format;
+ const struct v4l2_rect *crop = &mt9p031->crop;
+ unsigned int hblank;
+ unsigned int vblank;
+ unsigned int xskip;
+ unsigned int yskip;
+ unsigned int xbin;
+ unsigned int ybin;
+ int ret;
+
+ /* Windows position and size.
+ *
+ * TODO: Make sure the start coordinates and window size match the
+ * skipping, binning and mirroring (see description of registers 2 and 4
+ * in table 13, and Binning section on page 41).
+ */
+ ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_START, crop->top);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1);
+ if (ret < 0)
+ return ret;
+
+ /* Row and column binning and skipping. Use the maximum binning value
+ * compatible with the skipping settings.
+ */
+ xskip = DIV_ROUND_CLOSEST(crop->width, format->width);
+ yskip = DIV_ROUND_CLOSEST(crop->height, format->height);
+ xbin = 1 << (ffs(xskip) - 1);
+ ybin = 1 << (ffs(yskip) - 1);
+
+ ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE,
+ ((xbin - 1) << 4) | (xskip - 1));
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (yskip - 1));
+ if (ret < 0)
+ return ret;
+
+ /* Blanking - use minimum value for horizontal blanking and default
+ * value for vertical blanking.
+ */
+ hblank = 346 * ybin + 64 + (80 >> max_t(unsigned int, xbin, 3));
+ vblank = MT9P031_VERTICAL_BLANK_DEF;
+
+ ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ int ret;
+
+ if (!enable) {
+ /* Stop sensor readout */
+ ret = mt9p031_set_output_control(mt9p031,
+ MT9P031_OUTPUT_CONTROL_CEN, 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_pll_disable(mt9p031);
+ }
+
+ ret = mt9p031_set_params(mt9p031);
+ if (ret < 0)
+ return ret;
+
+ /* Switch to master "normal" mode */
+ ret = mt9p031_set_output_control(mt9p031, 0,
+ MT9P031_OUTPUT_CONTROL_CEN);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_pll_enable(mt9p031);
+}
+
+static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ if (code->pad || code->index)
+ return -EINVAL;
+
+ code->code = mt9p031->format.code;
+ return 0;
+}
+
+static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ if (fse->index >= 8 || fse->code != mt9p031->format.code)
+ return -EINVAL;
+
+ fse->min_width = MT9P031_WINDOW_WIDTH_DEF
+ / min_t(unsigned int, 7, fse->index + 1);
+ fse->max_width = fse->min_width;
+ fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1);
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9p031->format;
+ default:
+ return NULL;
+ }
+}
+
+static struct v4l2_rect *
+__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9p031->crop;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9p031_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad,
+ fmt->which);
+ return 0;
+}
+
+static int mt9p031_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad,
+ format->which);
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
+ max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN),
+ __crop->width);
+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
+ max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN),
+ __crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(__crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(__crop->height, height);
+
+ __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad,
+ format->which);
+ __format->width = __crop->width / hratio;
+ __format->height = __crop->height / vratio;
+
+ format->format = *__format;
+
+ return 0;
+}
+
+static int mt9p031_get_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad,
+ crop->which);
+ return 0;
+}
+
+static int mt9p031_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
+ * pixels to ensure a GRBG Bayer pattern.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN,
+ MT9P031_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN,
+ MT9P031_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2),
+ MT9P031_WINDOW_WIDTH_MIN,
+ MT9P031_WINDOW_WIDTH_MAX);
+ rect.height = clamp(ALIGN(crop->rect.height, 2),
+ MT9P031_WINDOW_HEIGHT_MIN,
+ MT9P031_WINDOW_HEIGHT_MAX);
+
+ rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad,
+ crop->which);
+ __format->width = rect.width;
+ __format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
+
+static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9p031 *mt9p031 =
+ container_of(ctrl->handler, struct mt9p031, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 data;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
+ (ctrl->val >> 16) & 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER,
+ ctrl->val & 0xffff);
+
+ case V4L2_CID_GAIN:
+ /* Gain is controlled by 2 analog stages and a digital stage.
+ * Valid values for the 3 stages are
+ *
+ * Stage Min Max Step
+ * ------------------------------------------
+ * First analog stage x1 x2 1
+ * Second analog stage x1 x4 0.125
+ * Digital stage x1 x16 0.125
+ *
+ * To minimize noise, the gain stages should be used in the
+ * second analog stage, first analog stage, digital stage order.
+ * Gain from a previous stage should be pushed to its maximum
+ * value before the next stage is used.
+ */
+ if (ctrl->val <= 32) {
+ data = ctrl->val;
+ } else if (ctrl->val <= 64) {
+ ctrl->val &= ~1;
+ data = (1 << 6) | (ctrl->val >> 1);
+ } else {
+ ctrl->val &= ~7;
+ data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
+ }
+
+ return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
+
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ return mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_COL_MIR);
+ else
+ return mt9p031_set_mode2(mt9p031,
+ MT9P031_READ_MODE_2_COL_MIR, 0);
+
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ return mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_ROW_MIR);
+ else
+ return mt9p031_set_mode2(mt9p031,
+ MT9P031_READ_MODE_2_ROW_MIR, 0);
+
+ case V4L2_CID_TEST_PATTERN:
+ if (!ctrl->val) {
+ ret = mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_ROW_BLC);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_TEST_PATTERN,
+ MT9P031_TEST_PATTERN_DISABLE);
+ }
+
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
+ 0);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_TEST_PATTERN,
+ ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
+ | MT9P031_TEST_PATTERN_ENABLE);
+ }
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
+ .s_ctrl = mt9p031_s_ctrl,
+};
+
+static const char * const mt9p031_test_pattern_menu[] = {
+ "Disabled",
+ "Color Field",
+ "Horizontal Gradient",
+ "Vertical Gradient",
+ "Diagonal Gradient",
+ "Classic Test Pattern",
+ "Walking 1s",
+ "Monochrome Horizontal Bars",
+ "Monochrome Vertical Bars",
+ "Vertical Color Bars",
+};
+
+static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
+ {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_TEST_PATTERN,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Test Pattern",
+ .min = 0,
+ .max = ARRAY_SIZE(mt9p031_test_pattern_menu) - 1,
+ .step = 0,
+ .def = 0,
+ .flags = 0,
+ .menu_skip_mask = 0,
+ .qmenu = mt9p031_test_pattern_menu,
+ }
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int mt9p031_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ int ret = 0;
+
+ mutex_lock(&mt9p031->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9p031->power_count == !on) {
+ ret = __mt9p031_set_power(mt9p031, !!on);
+ if (ret < 0)
+ goto out;
+ }
+
+ /* Update the power count. */
+ mt9p031->power_count += on ? 1 : -1;
+ WARN_ON(mt9p031->power_count < 0);
+
+out:
+ mutex_unlock(&mt9p031->power_lock);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int mt9p031_registered(struct v4l2_subdev *subdev)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ s32 data;
+ int ret;
+
+ ret = mt9p031_power_on(mt9p031);
+ if (ret < 0) {
+ dev_err(&client->dev, "MT9P031 power up failed\n");
+ return ret;
+ }
+
+ /* Read out the chip version register */
+ data = mt9p031_read(client, MT9P031_CHIP_VERSION);
+ if (data != MT9P031_CHIP_VERSION_VALUE) {
+ dev_err(&client->dev, "MT9P031 not detected, wrong version "
+ "0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ mt9p031_power_off(mt9p031);
+
+ dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
+ client->addr);
+
+ return ret;
+}
+
+static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ crop->left = MT9P031_COLUMN_START_DEF;
+ crop->top = MT9P031_ROW_START_DEF;
+ crop->width = MT9P031_WINDOW_WIDTH_DEF;
+ crop->height = MT9P031_WINDOW_HEIGHT_DEF;
+
+ format = v4l2_subdev_get_try_format(fh, 0);
+
+ if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION)
+ format->code = V4L2_MBUS_FMT_Y12_1X12;
+ else
+ format->code = V4L2_MBUS_FMT_SGRBG12_1X12;
+
+ format->width = MT9P031_WINDOW_WIDTH_DEF;
+ format->height = MT9P031_WINDOW_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ mt9p031->xskip = 1;
+ mt9p031->yskip = 1;
+ return mt9p031_set_power(subdev, 1);
+}
+
+static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ return mt9p031_set_power(subdev, 0);
+}
+
+static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
+ .s_power = mt9p031_set_power,
+};
+
+static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
+ .s_stream = mt9p031_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
+ .enum_mbus_code = mt9p031_enum_mbus_code,
+ .enum_frame_size = mt9p031_enum_frame_size,
+ .get_fmt = mt9p031_get_format,
+ .set_fmt = mt9p031_set_format,
+ .get_crop = mt9p031_get_crop,
+ .set_crop = mt9p031_set_crop,
+};
+
+static struct v4l2_subdev_ops mt9p031_subdev_ops = {
+ .core = &mt9p031_subdev_core_ops,
+ .video = &mt9p031_subdev_video_ops,
+ .pad = &mt9p031_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
+ .registered = mt9p031_registered,
+ .open = mt9p031_open,
+ .close = mt9p031_close,
+};
+
+/* -----------------------------------------------------------------------------
+ * Driver initialization and probing
+ */
+
+static int mt9p031_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9p031_platform_data *pdata = client->dev.platform_data;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct mt9p031 *mt9p031;
+ unsigned int i;
+ int ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL);
+ if (mt9p031 == NULL)
+ return -ENOMEM;
+
+ mt9p031->pdata = pdata;
+ mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF;
+ mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
+
+ v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4);
+
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
+ MT9P031_SHUTTER_WIDTH_MAX, 1,
+ MT9P031_SHUTTER_WIDTH_DEF);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN,
+ MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
+ v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
+
+ mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
+
+ if (mt9p031->ctrls.error)
+ printk(KERN_INFO "%s: control initialization error %d\n",
+ __func__, mt9p031->ctrls.error);
+
+ mutex_init(&mt9p031->power_lock);
+ v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
+ mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
+
+ mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
+ if (ret < 0)
+ goto done;
+
+ mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF;
+ mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF;
+ mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
+ mt9p031->crop.top = MT9P031_ROW_START_DEF;
+
+ if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION)
+ mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12;
+ else
+ mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
+
+ mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
+ mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
+ mt9p031->format.field = V4L2_FIELD_NONE;
+ mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ ret = mt9p031_pll_get_divs(mt9p031);
+
+done:
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&mt9p031->ctrls);
+ media_entity_cleanup(&mt9p031->subdev.entity);
+ kfree(mt9p031);
+ }
+
+ return ret;
+}
+
+static int mt9p031_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ v4l2_ctrl_handler_free(&mt9p031->ctrls);
+ v4l2_device_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+ kfree(mt9p031);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9p031_id[] = {
+ { "mt9p031", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9p031_id);
+
+static struct i2c_driver mt9p031_i2c_driver = {
+ .driver = {
+ .name = "mt9p031",
+ },
+ .probe = mt9p031_probe,
+ .remove = mt9p031_remove,
+ .id_table = mt9p031_id,
+};
+
+static int __init mt9p031_mod_init(void)
+{
+ return i2c_add_driver(&mt9p031_i2c_driver);
+}
+
+static void __exit mt9p031_mod_exit(void)
+{
+ i2c_del_driver(&mt9p031_i2c_driver);
+}
+
+module_init(mt9p031_mod_init);
+module_exit(mt9p031_mod_exit);
+
+MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
+MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c
new file mode 100644
index 000000000000..08074b8a2736
--- /dev/null
+++ b/drivers/media/video/mt9t001.c
@@ -0,0 +1,836 @@
+/*
+ * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron)
+ *
+ * Copyright (C) 2010-2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * Based on the MT9M001 driver,
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/log2.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/mt9t001.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#define MT9T001_PIXEL_ARRAY_HEIGHT 1568
+#define MT9T001_PIXEL_ARRAY_WIDTH 2112
+
+#define MT9T001_CHIP_VERSION 0x00
+#define MT9T001_CHIP_ID 0x1621
+#define MT9T001_ROW_START 0x01
+#define MT9T001_ROW_START_MIN 0
+#define MT9T001_ROW_START_DEF 20
+#define MT9T001_ROW_START_MAX 1534
+#define MT9T001_COLUMN_START 0x02
+#define MT9T001_COLUMN_START_MIN 0
+#define MT9T001_COLUMN_START_DEF 32
+#define MT9T001_COLUMN_START_MAX 2046
+#define MT9T001_WINDOW_HEIGHT 0x03
+#define MT9T001_WINDOW_HEIGHT_MIN 1
+#define MT9T001_WINDOW_HEIGHT_DEF 1535
+#define MT9T001_WINDOW_HEIGHT_MAX 1567
+#define MT9T001_WINDOW_WIDTH 0x04
+#define MT9T001_WINDOW_WIDTH_MIN 1
+#define MT9T001_WINDOW_WIDTH_DEF 2047
+#define MT9T001_WINDOW_WIDTH_MAX 2111
+#define MT9T001_HORIZONTAL_BLANKING 0x05
+#define MT9T001_HORIZONTAL_BLANKING_MIN 21
+#define MT9T001_HORIZONTAL_BLANKING_MAX 1023
+#define MT9T001_VERTICAL_BLANKING 0x06
+#define MT9T001_VERTICAL_BLANKING_MIN 3
+#define MT9T001_VERTICAL_BLANKING_MAX 1023
+#define MT9T001_OUTPUT_CONTROL 0x07
+#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0)
+#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1)
+#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6)
+#define MT9T001_SHUTTER_WIDTH_HIGH 0x08
+#define MT9T001_SHUTTER_WIDTH_LOW 0x09
+#define MT9T001_SHUTTER_WIDTH_MIN 1
+#define MT9T001_SHUTTER_WIDTH_DEF 1561
+#define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024)
+#define MT9T001_PIXEL_CLOCK 0x0a
+#define MT9T001_PIXEL_CLOCK_INVERT (1 << 15)
+#define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8)
+#define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8
+#define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0)
+#define MT9T001_FRAME_RESTART 0x0b
+#define MT9T001_SHUTTER_DELAY 0x0c
+#define MT9T001_SHUTTER_DELAY_MAX 2047
+#define MT9T001_RESET 0x0d
+#define MT9T001_READ_MODE1 0x1e
+#define MT9T001_READ_MODE_SNAPSHOT (1 << 8)
+#define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9)
+#define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10)
+#define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11)
+#define MT9T001_READ_MODE2 0x20
+#define MT9T001_READ_MODE_BAD_FRAMES (1 << 0)
+#define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9)
+#define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10)
+#define MT9T001_READ_MODE3 0x21
+#define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0)
+#define MT9T001_READ_MODE_GHST_CTL (1 << 1)
+#define MT9T001_ROW_ADDRESS_MODE 0x22
+#define MT9T001_ROW_SKIP_MASK (7 << 0)
+#define MT9T001_ROW_BIN_MASK (3 << 3)
+#define MT9T001_ROW_BIN_SHIFT 3
+#define MT9T001_COLUMN_ADDRESS_MODE 0x23
+#define MT9T001_COLUMN_SKIP_MASK (7 << 0)
+#define MT9T001_COLUMN_BIN_MASK (3 << 3)
+#define MT9T001_COLUMN_BIN_SHIFT 3
+#define MT9T001_GREEN1_GAIN 0x2b
+#define MT9T001_BLUE_GAIN 0x2c
+#define MT9T001_RED_GAIN 0x2d
+#define MT9T001_GREEN2_GAIN 0x2e
+#define MT9T001_TEST_DATA 0x32
+#define MT9T001_GLOBAL_GAIN 0x35
+#define MT9T001_GLOBAL_GAIN_MIN 8
+#define MT9T001_GLOBAL_GAIN_MAX 1024
+#define MT9T001_BLACK_LEVEL 0x49
+#define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b
+#define MT9T001_BLC_DELTA_THRESHOLDS 0x5d
+#define MT9T001_CAL_THRESHOLDS 0x5f
+#define MT9T001_GREEN1_OFFSET 0x60
+#define MT9T001_GREEN2_OFFSET 0x61
+#define MT9T001_BLACK_LEVEL_CALIBRATION 0x62
+#define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0)
+#define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1)
+#define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12)
+#define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13)
+#define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14)
+#define MT9T001_RED_OFFSET 0x63
+#define MT9T001_BLUE_OFFSET 0x64
+
+struct mt9t001 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_rect crop;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *gains[4];
+
+ u16 output_control;
+ u16 black_level;
+};
+
+static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9t001, subdev);
+}
+
+static int mt9t001_read(struct i2c_client *client, u8 reg)
+{
+ s32 data = i2c_smbus_read_word_data(client, reg);
+ return data < 0 ? data : be16_to_cpu(data);
+}
+
+static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data)
+{
+ return i2c_smbus_write_word_data(client, reg, cpu_to_be16(data));
+}
+
+static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear,
+ u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev);
+ u16 value = (mt9t001->output_control & ~clear) | set;
+ int ret;
+
+ if (value == mt9t001->output_control)
+ return 0;
+
+ ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value);
+ if (ret < 0)
+ return ret;
+
+ mt9t001->output_control = value;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9t001->format;
+ default:
+ return NULL;
+ }
+}
+
+static struct v4l2_rect *
+__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9t001->crop;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE;
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ struct v4l2_mbus_framefmt *format = &mt9t001->format;
+ struct v4l2_rect *crop = &mt9t001->crop;
+ unsigned int hratio;
+ unsigned int vratio;
+ int ret;
+
+ if (!enable)
+ return mt9t001_set_output_control(mt9t001, mode, 0);
+
+ /* Configure the window size and row/column bin */
+ hratio = DIV_ROUND_CLOSEST(crop->width, format->width);
+ vratio = DIV_ROUND_CLOSEST(crop->height, format->height);
+
+ ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_ROW_START, crop->top);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1);
+ if (ret < 0)
+ return ret;
+
+ /* Switch to master "normal" mode */
+ return mt9t001_set_output_control(mt9t001, 0, mode);
+}
+
+static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ return 0;
+}
+
+static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
+ return -EINVAL;
+
+ fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index;
+ fse->max_width = fse->min_width;
+ fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int mt9t001_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+
+ format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad,
+ format->which);
+ return 0;
+}
+
+static int mt9t001_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ __crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad,
+ format->which);
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
+ max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1),
+ __crop->width);
+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
+ max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1),
+ __crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(__crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(__crop->height, height);
+
+ __format = __mt9t001_get_pad_format(mt9t001, fh, format->pad,
+ format->which);
+ __format->width = __crop->width / hratio;
+ __format->height = __crop->height / vratio;
+
+ format->format = *__format;
+
+ return 0;
+}
+
+static int mt9t001_get_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+
+ crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad,
+ crop->which);
+ return 0;
+}
+
+static int mt9t001_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
+ * pixels.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left, 2),
+ MT9T001_COLUMN_START_MIN,
+ MT9T001_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top, 2),
+ MT9T001_ROW_START_MIN,
+ MT9T001_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2),
+ MT9T001_WINDOW_WIDTH_MIN + 1,
+ MT9T001_WINDOW_WIDTH_MAX + 1);
+ rect.height = clamp(ALIGN(crop->rect.height, 2),
+ MT9T001_WINDOW_HEIGHT_MIN + 1,
+ MT9T001_WINDOW_HEIGHT_MAX + 1);
+
+ rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad,
+ crop->which);
+ __format->width = rect.width;
+ __format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
+#define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002)
+#define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003)
+#define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004)
+
+#define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001)
+#define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002)
+#define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003)
+#define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004)
+
+static u16 mt9t001_gain_value(s32 *gain)
+{
+ /* Gain is controlled by 2 analog stages and a digital stage. Valid
+ * values for the 3 stages are
+ *
+ * Stage Min Max Step
+ * ------------------------------------------
+ * First analog stage x1 x2 1
+ * Second analog stage x1 x4 0.125
+ * Digital stage x1 x16 0.125
+ *
+ * To minimize noise, the gain stages should be used in the second
+ * analog stage, first analog stage, digital stage order. Gain from a
+ * previous stage should be pushed to its maximum value before the next
+ * stage is used.
+ */
+ if (*gain <= 32)
+ return *gain;
+
+ if (*gain <= 64) {
+ *gain &= ~1;
+ return (1 << 6) | (*gain >> 1);
+ }
+
+ *gain &= ~7;
+ return ((*gain - 64) << 5) | (1 << 6) | 32;
+}
+
+static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze)
+{
+ return mt9t001_set_output_control(mt9t001,
+ freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC,
+ freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0);
+}
+
+static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ static const u8 gains[4] = {
+ MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN,
+ MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN
+ };
+
+ struct mt9t001 *mt9t001 =
+ container_of(ctrl->handler, struct mt9t001, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev);
+ unsigned int count;
+ unsigned int i;
+ u16 value;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN_RED:
+ case V4L2_CID_GAIN_GREEN_RED:
+ case V4L2_CID_GAIN_GREEN_BLUE:
+ case V4L2_CID_GAIN_BLUE:
+
+ /* Disable control updates if more than one control has changed
+ * in the cluster.
+ */
+ for (i = 0, count = 0; i < 4; ++i) {
+ struct v4l2_ctrl *gain = mt9t001->gains[i];
+
+ if (gain->val != gain->cur.val)
+ count++;
+ }
+
+ if (count > 1) {
+ ret = mt9t001_ctrl_freeze(mt9t001, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Update the gain controls. */
+ for (i = 0; i < 4; ++i) {
+ struct v4l2_ctrl *gain = mt9t001->gains[i];
+
+ if (gain->val == gain->cur.val)
+ continue;
+
+ value = mt9t001_gain_value(&gain->val);
+ ret = mt9t001_write(client, gains[i], value);
+ if (ret < 0) {
+ mt9t001_ctrl_freeze(mt9t001, false);
+ return ret;
+ }
+ }
+
+ /* Enable control updates. */
+ if (count > 1) {
+ ret = mt9t001_ctrl_freeze(mt9t001, false);
+ if (ret < 0)
+ return ret;
+ }
+
+ break;
+
+ case V4L2_CID_EXPOSURE:
+ ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW,
+ ctrl->val & 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH,
+ ctrl->val >> 16);
+
+ case V4L2_CID_TEST_PATTERN:
+ ret = mt9t001_set_output_control(mt9t001,
+ ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA,
+ ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2);
+
+ case V4L2_CID_BLACK_LEVEL_AUTO:
+ value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE;
+ ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION,
+ value);
+ if (ret < 0)
+ return ret;
+
+ mt9t001->black_level = value;
+ break;
+
+ case V4L2_CID_BLACK_LEVEL_OFFSET:
+ ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val);
+ if (ret < 0)
+ return ret;
+
+ return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val);
+
+ case V4L2_CID_BLACK_LEVEL_CALIBRATE:
+ return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION,
+ MT9T001_BLACK_LEVEL_RECALCULATE |
+ mt9t001->black_level);
+ }
+
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9t001_ctrl_ops = {
+ .s_ctrl = mt9t001_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config mt9t001_ctrls[] = {
+ {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_TEST_PATTERN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Test pattern",
+ .min = 0,
+ .max = 1023,
+ .step = 1,
+ .def = 0,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_BLACK_LEVEL_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Black Level, Auto",
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_BLACK_LEVEL_OFFSET,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Black Level, Offset",
+ .min = -256,
+ .max = 255,
+ .step = 1,
+ .def = 32,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_BLACK_LEVEL_CALIBRATE,
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .name = "Black Level, Calibrate",
+ .min = 0,
+ .max = 0,
+ .step = 0,
+ .def = 0,
+ .flags = V4L2_CTRL_FLAG_WRITE_ONLY,
+ },
+};
+
+static const struct v4l2_ctrl_config mt9t001_gains[] = {
+ {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_RED,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Red",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_GREEN_RED,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Green (R)",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_GREEN_BLUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Green (B)",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_BLUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Blue",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ },
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ crop->left = MT9T001_COLUMN_START_DEF;
+ crop->top = MT9T001_ROW_START_DEF;
+ crop->width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+
+ format = v4l2_subdev_get_try_format(fh, 0);
+ format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format->width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ format->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = {
+ .s_stream = mt9t001_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = {
+ .enum_mbus_code = mt9t001_enum_mbus_code,
+ .enum_frame_size = mt9t001_enum_frame_size,
+ .get_fmt = mt9t001_get_format,
+ .set_fmt = mt9t001_set_format,
+ .get_crop = mt9t001_get_crop,
+ .set_crop = mt9t001_set_crop,
+};
+
+static struct v4l2_subdev_ops mt9t001_subdev_ops = {
+ .video = &mt9t001_subdev_video_ops,
+ .pad = &mt9t001_subdev_pad_ops,
+};
+
+static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = {
+ .open = mt9t001_open,
+};
+
+static int mt9t001_video_probe(struct i2c_client *client)
+{
+ struct mt9t001_platform_data *pdata = client->dev.platform_data;
+ s32 data;
+ int ret;
+
+ dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n",
+ client->addr);
+
+ /* Reset the chip and stop data read out */
+ ret = mt9t001_write(client, MT9T001_RESET, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Configure the pixel clock polarity */
+ if (pdata && pdata->clk_pol) {
+ ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK,
+ MT9T001_PIXEL_CLOCK_INVERT);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Read and check the sensor version */
+ data = mt9t001_read(client, MT9T001_CHIP_VERSION);
+ if (data != MT9T001_CHIP_ID) {
+ dev_err(&client->dev, "MT9T001 not detected, wrong version "
+ "0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n",
+ client->addr);
+
+ return ret;
+}
+
+static int mt9t001_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t001 *mt9t001;
+ unsigned int i;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ ret = mt9t001_video_probe(client);
+ if (ret < 0)
+ return ret;
+
+ mt9t001 = kzalloc(sizeof(*mt9t001), GFP_KERNEL);
+ if (!mt9t001)
+ return -ENOMEM;
+
+ v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) +
+ ARRAY_SIZE(mt9t001_gains) + 2);
+
+ v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN,
+ MT9T001_SHUTTER_WIDTH_MAX, 1,
+ MT9T001_SHUTTER_WIDTH_DEF);
+ v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
+ V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1);
+
+ for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i)
+ v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL);
+
+ for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i)
+ mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls,
+ &mt9t001_gains[i], NULL);
+
+ v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains);
+
+ mt9t001->subdev.ctrl_handler = &mt9t001->ctrls;
+
+ if (mt9t001->ctrls.error) {
+ printk(KERN_INFO "%s: control initialization error %d\n",
+ __func__, mt9t001->ctrls.error);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mt9t001->crop.left = MT9T001_COLUMN_START_DEF;
+ mt9t001->crop.top = MT9T001_ROW_START_DEF;
+ mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+
+ mt9t001->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+ mt9t001->format.field = V4L2_FIELD_NONE;
+ mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops);
+ mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops;
+ mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0);
+
+done:
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&mt9t001->ctrls);
+ media_entity_cleanup(&mt9t001->subdev.entity);
+ kfree(mt9t001);
+ }
+
+ return ret;
+}
+
+static int mt9t001_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+
+ v4l2_ctrl_handler_free(&mt9t001->ctrls);
+ v4l2_device_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+ kfree(mt9t001);
+ return 0;
+}
+
+static const struct i2c_device_id mt9t001_id[] = {
+ { "mt9t001", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t001_id);
+
+static struct i2c_driver mt9t001_driver = {
+ .driver = {
+ .name = "mt9t001",
+ },
+ .probe = mt9t001_probe,
+ .remove = mt9t001_remove,
+ .id_table = mt9t001_id,
+};
+
+static int __init mt9t001_init(void)
+{
+ return i2c_add_driver(&mt9t001_driver);
+}
+
+static void __exit mt9t001_exit(void)
+{
+ i2c_del_driver(&mt9t001_driver);
+}
+
+module_init(mt9t001_init);
+module_exit(mt9t001_exit);
+
+MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
index 7ce279c3751d..0e78477452ff 100644
--- a/drivers/media/video/mt9t031.c
+++ b/drivers/media/video/mt9t031.c
@@ -13,11 +13,21 @@
#include <linux/log2.h>
#include <linux/pm.h>
#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
+#include <linux/module.h>
#include <media/soc_camera.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * ATTENTION: this driver still cannot be used outside of the soc-camera
+ * framework because of its PM implementation, using the video_device node.
+ * If hardware becomes available for testing, alternative PM approaches shall
+ * be considered and tested.
+ */
/*
* mt9t031 i2c address 0x5d
@@ -57,21 +67,20 @@
#define MT9T031_COLUMN_SKIP 32
#define MT9T031_ROW_SKIP 20
-#define MT9T031_BUS_PARAM (SOCAM_PCLK_SAMPLE_RISING | \
- SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | \
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | \
- SOCAM_MASTER | SOCAM_DATAWIDTH_10)
-
struct mt9t031 {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
struct v4l2_rect rect; /* Sensor window */
int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
u16 xskip;
u16 yskip;
- unsigned int gain;
+ unsigned int total_h;
unsigned short y_skip_top; /* Lines to skip at the top */
- unsigned int exposure;
- unsigned char autoexposure;
};
static struct mt9t031 *to_mt9t031(const struct i2c_client *client)
@@ -179,95 +188,6 @@ static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int mt9t031_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-
- /* The caller should have queried our parameters, check anyway */
- if (flags & ~MT9T031_BUS_PARAM)
- return -EINVAL;
-
- if (flags & SOCAM_PCLK_SAMPLE_FALLING)
- reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
- else
- reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
-
- return 0;
-}
-
-static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM);
-}
-
-enum {
- MT9T031_CTRL_VFLIP,
- MT9T031_CTRL_HFLIP,
- MT9T031_CTRL_GAIN,
- MT9T031_CTRL_EXPOSURE,
- MT9T031_CTRL_EXPOSURE_AUTO,
-};
-
-static const struct v4l2_queryctrl mt9t031_controls[] = {
- [MT9T031_CTRL_VFLIP] = {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- [MT9T031_CTRL_HFLIP] = {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- [MT9T031_CTRL_GAIN] = {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- },
- [MT9T031_CTRL_EXPOSURE] = {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 1,
- .maximum = 255,
- .step = 1,
- .default_value = 255,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- },
- [MT9T031_CTRL_EXPOSURE_AUTO] = {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }
-};
-
-static struct soc_camera_ops mt9t031_ops = {
- .set_bus_param = mt9t031_set_bus_param,
- .query_bus_param = mt9t031_query_bus_param,
- .controls = mt9t031_controls,
- .num_controls = ARRAY_SIZE(mt9t031_controls),
-};
-
/* target must be _even_ */
static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
{
@@ -353,7 +273,7 @@ static int mt9t031_set_params(struct i2c_client *client,
/*
* The caller provides a supported format, as guaranteed by
- * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap()
+ * .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap()
*/
if (ret >= 0)
ret = reg_write(client, MT9T031_COLUMN_START, rect->left);
@@ -364,17 +284,10 @@ static int mt9t031_set_params(struct i2c_client *client,
if (ret >= 0)
ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
rect->height + mt9t031->y_skip_top - 1);
- if (ret >= 0 && mt9t031->autoexposure) {
- unsigned int total_h = rect->height + mt9t031->y_skip_top + vblank;
- ret = set_shutter(client, total_h);
- if (ret >= 0) {
- const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
- const struct v4l2_queryctrl *qctrl =
- &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
- mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
- (qctrl->maximum - qctrl->minimum)) /
- shutter_max + qctrl->minimum;
- }
+ if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) {
+ mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank;
+
+ ret = set_shutter(client, mt9t031->total_h);
}
/* Re-enable register update, commit all changes */
@@ -543,71 +456,57 @@ static int mt9t031_s_register(struct v4l2_subdev *sd,
}
#endif
-static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
- int data;
+ struct mt9t031 *mt9t031 = container_of(ctrl->handler,
+ struct mt9t031, hdl);
+ const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK;
+ s32 min, max;
switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- data = reg_read(client, MT9T031_READ_MODE_2);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x8000);
- break;
- case V4L2_CID_HFLIP:
- data = reg_read(client, MT9T031_READ_MODE_2);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x4000);
- break;
case V4L2_CID_EXPOSURE_AUTO:
- ctrl->value = mt9t031->autoexposure;
- break;
- case V4L2_CID_GAIN:
- ctrl->value = mt9t031->gain;
- break;
- case V4L2_CID_EXPOSURE:
- ctrl->value = mt9t031->exposure;
+ min = mt9t031->exposure->minimum;
+ max = mt9t031->exposure->maximum;
+ mt9t031->exposure->val =
+ (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min))
+ / shutter_max + min;
break;
}
return 0;
}
-static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct mt9t031 *mt9t031 = container_of(ctrl->handler,
+ struct mt9t031, hdl);
+ struct v4l2_subdev *sd = &mt9t031->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
- const struct v4l2_queryctrl *qctrl;
+ struct v4l2_ctrl *exp = mt9t031->exposure;
int data;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, MT9T031_READ_MODE_2, 0x8000);
else
data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000);
if (data < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_HFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, MT9T031_READ_MODE_2, 0x4000);
else
data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000);
if (data < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_GAIN:
- qctrl = &mt9t031_controls[MT9T031_CTRL_GAIN];
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
/* See Datasheet Table 7, Gain settings. */
- if (ctrl->value <= qctrl->default_value) {
+ if (ctrl->val <= ctrl->default_value) {
/* Pack it into 0..1 step 0.125, register values 0..8 */
- unsigned long range = qctrl->default_value - qctrl->minimum;
- data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
+ unsigned long range = ctrl->default_value - ctrl->minimum;
+ data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
dev_dbg(&client->dev, "Setting gain %d\n", data);
data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
@@ -616,9 +515,9 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
} else {
/* Pack it into 1.125..128 variable step, register values 9..0x7860 */
/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
- unsigned long range = qctrl->maximum - qctrl->default_value - 1;
+ unsigned long range = ctrl->maximum - ctrl->default_value - 1;
/* calculated gain: map 65..127 to 9..1024 step 0.125 */
- unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
+ unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
1015 + range / 2) / range + 9;
if (gain <= 32) /* calculated gain 9..32 -> 9..32 */
@@ -635,19 +534,13 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
if (data < 0)
return -EIO;
}
+ return 0;
- /* Success */
- mt9t031->gain = ctrl->value;
- break;
- case V4L2_CID_EXPOSURE:
- qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
- /* mt9t031 has maximum == default */
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
- else {
- const unsigned long range = qctrl->maximum - qctrl->minimum;
- const u32 shutter = ((ctrl->value - qctrl->minimum) * 1048 +
- range / 2) / range + 1;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ unsigned int range = exp->maximum - exp->minimum;
+ unsigned int shutter = ((exp->val - exp->minimum) * 1048 +
+ range / 2) / range + 1;
u32 old;
get_shutter(client, &old);
@@ -655,27 +548,15 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
old, shutter);
if (set_shutter(client, shutter) < 0)
return -EIO;
- mt9t031->exposure = ctrl->value;
- mt9t031->autoexposure = 0;
- }
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- if (ctrl->value) {
+ } else {
const u16 vblank = MT9T031_VERTICAL_BLANK;
- const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
- unsigned int total_h = mt9t031->rect.height +
+ mt9t031->total_h = mt9t031->rect.height +
mt9t031->y_skip_top + vblank;
- if (set_shutter(client, total_h) < 0)
+ if (set_shutter(client, mt9t031->total_h) < 0)
return -EIO;
- qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
- mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
- (qctrl->maximum - qctrl->minimum)) /
- shutter_max + qctrl->minimum;
- mt9t031->autoexposure = 1;
- } else
- mt9t031->autoexposure = 0;
- break;
+ }
+ return 0;
default:
return -EINVAL;
}
@@ -700,9 +581,7 @@ static int mt9t031_runtime_suspend(struct device *dev)
static int mt9t031_runtime_resume(struct device *dev)
{
struct video_device *vdev = to_video_device(dev);
- struct soc_camera_device *icd = container_of(vdev->parent,
- struct soc_camera_device, dev);
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev);
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t031 *mt9t031 = to_mt9t031(client);
@@ -735,6 +614,19 @@ static struct device_type mt9t031_dev_type = {
.pm = &mt9t031_dev_pm_ops,
};
+static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct video_device *vdev = soc_camera_i2c_to_vdev(client);
+
+ if (on)
+ vdev->dev.type = &mt9t031_dev_type;
+ else
+ vdev->dev.type = NULL;
+
+ return 0;
+}
+
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
@@ -742,7 +634,6 @@ static struct device_type mt9t031_dev_type = {
static int mt9t031_video_probe(struct i2c_client *client)
{
struct mt9t031 *mt9t031 = to_mt9t031(client);
- struct video_device *vdev = soc_camera_i2c_to_vdev(client);
s32 data;
int ret;
@@ -769,11 +660,7 @@ static int mt9t031_video_probe(struct i2c_client *client)
if (ret < 0)
dev_err(&client->dev, "Failed to initialise the camera\n");
else
- vdev->dev.type = &mt9t031_dev_type;
-
- /* mt9t031_idle() has reset the chip to default. */
- mt9t031->exposure = 255;
- mt9t031->gain = 64;
+ v4l2_ctrl_handler_setup(&mt9t031->hdl);
return ret;
}
@@ -788,10 +675,14 @@ static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
return 0;
}
+static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = {
+ .g_volatile_ctrl = mt9t031_g_volatile_ctrl,
+ .s_ctrl = mt9t031_s_ctrl,
+};
+
static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
- .g_ctrl = mt9t031_g_ctrl,
- .s_ctrl = mt9t031_s_ctrl,
.g_chip_ident = mt9t031_g_chip_ident,
+ .s_power = mt9t031_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9t031_g_register,
.s_register = mt9t031_s_register,
@@ -808,6 +699,34 @@ static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int mt9t031_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9t031_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ if (soc_camera_apply_board_flags(icl, cfg) &
+ V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+ else
+ return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+}
+
static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
.s_stream = mt9t031_s_stream,
.s_mbus_fmt = mt9t031_s_fmt,
@@ -817,6 +736,8 @@ static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
.g_crop = mt9t031_g_crop,
.cropcap = mt9t031_cropcap,
.enum_mbus_fmt = mt9t031_enum_fmt,
+ .g_mbus_config = mt9t031_g_mbus_config,
+ .s_mbus_config = mt9t031_s_mbus_config,
};
static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
@@ -833,18 +754,13 @@ static int mt9t031_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9t031 *mt9t031;
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
int ret;
- if (icd) {
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- if (!icl) {
- dev_err(&client->dev, "MT9T031 driver needs platform data\n");
- return -EINVAL;
- }
-
- icd->ops = &mt9t031_ops;
+ if (!icl) {
+ dev_err(&client->dev, "MT9T031 driver needs platform data\n");
+ return -EINVAL;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
@@ -858,6 +774,33 @@ static int mt9t031_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9t031->hdl, 5);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl,
+ &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+
+ mt9t031->subdev.ctrl_handler = &mt9t031->hdl;
+ if (mt9t031->hdl.error) {
+ int err = mt9t031->hdl.error;
+
+ kfree(mt9t031);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
mt9t031->y_skip_top = 0;
mt9t031->rect.left = MT9T031_COLUMN_SKIP;
@@ -865,12 +808,6 @@ static int mt9t031_probe(struct i2c_client *client,
mt9t031->rect.width = MT9T031_MAX_WIDTH;
mt9t031->rect.height = MT9T031_MAX_HEIGHT;
- /*
- * Simulated autoexposure. If enabled, we calculate shutter width
- * ourselves in the driver based on vertical blanking and frame width
- */
- mt9t031->autoexposure = 1;
-
mt9t031->xskip = 1;
mt9t031->yskip = 1;
@@ -881,8 +818,7 @@ static int mt9t031_probe(struct i2c_client *client,
mt9t031_disable(client);
if (ret) {
- if (icd)
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&mt9t031->hdl);
kfree(mt9t031);
}
@@ -892,10 +828,9 @@ static int mt9t031_probe(struct i2c_client *client,
static int mt9t031_remove(struct i2c_client *client)
{
struct mt9t031 *mt9t031 = to_mt9t031(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- if (icd)
- icd->ops = NULL;
+ v4l2_device_unregister_subdev(&mt9t031->subdev);
+ v4l2_ctrl_handler_free(&mt9t031->hdl);
kfree(mt9t031);
return 0;
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
index bffa9ee10968..32114a3c0ca7 100644
--- a/drivers/media/video/mt9t112.c
+++ b/drivers/media/video/mt9t112.c
@@ -22,11 +22,11 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
#include <media/mt9t112.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
@@ -34,11 +34,7 @@
/* #define EXT_CLOCK 24000000 */
/************************************************************************
-
-
macro
-
-
************************************************************************/
/*
* frame size
@@ -80,17 +76,8 @@
#define VAR8(id, offset) _VAR(id, offset, 0x8000)
/************************************************************************
-
-
struct
-
-
************************************************************************/
-struct mt9t112_frame_size {
- u16 width;
- u16 height;
-};
-
struct mt9t112_format {
enum v4l2_mbus_pixelcode code;
enum v4l2_colorspace colorspace;
@@ -102,21 +89,17 @@ struct mt9t112_priv {
struct v4l2_subdev subdev;
struct mt9t112_camera_info *info;
struct i2c_client *client;
- struct soc_camera_device icd;
- struct mt9t112_frame_size frame;
+ struct v4l2_rect frame;
const struct mt9t112_format *format;
int model;
u32 flags;
/* for flags */
-#define INIT_DONE (1<<0)
+#define INIT_DONE (1 << 0)
+#define PCLK_RISING (1 << 1)
};
/************************************************************************
-
-
supported format
-
-
************************************************************************/
static const struct mt9t112_format mt9t112_cfmts[] = {
@@ -154,11 +137,7 @@ static const struct mt9t112_format mt9t112_cfmts[] = {
};
/************************************************************************
-
-
general function
-
-
************************************************************************/
static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
{
@@ -326,50 +305,47 @@ static int mt9t112_clock_info(const struct i2c_client *client, u32 ext)
n = (n >> 8) & 0x003f;
enable = ((6000 > ext) || (54000 < ext)) ? "X" : "";
- dev_info(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
+ dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
vco = 2 * m * ext / (n+1);
enable = ((384000 > vco) || (768000 < vco)) ? "X" : "";
- dev_info(&client->dev, "VCO : %10u K %s\n", vco, enable);
+ dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable);
clk = vco / (p1+1) / (p2+1);
enable = (96000 < clk) ? "X" : "";
- dev_info(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
clk = vco / (p3+1);
enable = (768000 < clk) ? "X" : "";
- dev_info(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
clk = vco / (p6+1);
enable = (96000 < clk) ? "X" : "";
- dev_info(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
clk = vco / (p5+1);
enable = (54000 < clk) ? "X" : "";
- dev_info(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
clk = vco / (p4+1);
enable = (70000 < clk) ? "X" : "";
- dev_info(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
clk = vco / (p7+1);
- dev_info(&client->dev, "External sensor : %10u K\n", clk);
+ dev_dbg(&client->dev, "External sensor : %10u K\n", clk);
clk = ext / (n+1);
enable = ((2000 > clk) || (24000 < clk)) ? "X" : "";
- dev_info(&client->dev, "PFD : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable);
return 0;
}
#endif
-static void mt9t112_frame_check(u32 *width, u32 *height)
+static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top)
{
- if (*width > MAX_WIDTH)
- *width = MAX_WIDTH;
-
- if (*height > MAX_HEIGHT)
- *height = MAX_HEIGHT;
+ soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH);
+ soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT);
}
static int mt9t112_set_a_frame_size(const struct i2c_client *client,
@@ -758,48 +734,7 @@ static int mt9t112_init_camera(const struct i2c_client *client)
}
/************************************************************************
-
-
- soc_camera_ops
-
-
-************************************************************************/
-static int mt9t112_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-static unsigned long mt9t112_query_bus_param(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
- struct mt9t112_priv *priv = to_mt9t112(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long flags = SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH;
-
- flags |= (priv->info->flags & MT9T112_FLAG_PCLK_RISING_EDGE) ?
- SOCAM_PCLK_SAMPLE_RISING : SOCAM_PCLK_SAMPLE_FALLING;
-
- if (priv->info->flags & MT9T112_FLAG_DATAWIDTH_8)
- flags |= SOCAM_DATAWIDTH_8;
- else
- flags |= SOCAM_DATAWIDTH_10;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static struct soc_camera_ops mt9t112_ops = {
- .set_bus_param = mt9t112_set_bus_param,
- .query_bus_param = mt9t112_query_bus_param,
-};
-
-/************************************************************************
-
-
v4l2_subdev_core_ops
-
-
************************************************************************/
static int mt9t112_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *id)
@@ -850,11 +785,7 @@ static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
/************************************************************************
-
-
v4l2_subdev_video_ops
-
-
************************************************************************/
static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
{
@@ -877,8 +808,7 @@ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
}
if (!(priv->flags & INIT_DONE)) {
- u16 param = (MT9T112_FLAG_PCLK_RISING_EDGE &
- priv->info->flags) ? 0x0001 : 0x0000;
+ u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000;
ECHECKER(ret, mt9t112_init_camera(client));
@@ -910,19 +840,12 @@ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
return ret;
}
-static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height,
+static int mt9t112_set_params(struct mt9t112_priv *priv,
+ const struct v4l2_rect *rect,
enum v4l2_mbus_pixelcode code)
{
- struct mt9t112_priv *priv = to_mt9t112(client);
int i;
- priv->format = NULL;
-
- /*
- * frame size check
- */
- mt9t112_frame_check(&width, &height);
-
/*
* get color format
*/
@@ -933,8 +856,13 @@ static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height,
if (i == ARRAY_SIZE(mt9t112_cfmts))
return -EINVAL;
- priv->frame.width = (u16)width;
- priv->frame.height = (u16)height;
+ priv->frame = *rect;
+
+ /*
+ * frame size check
+ */
+ mt9t112_frame_check(&priv->frame.width, &priv->frame.height,
+ &priv->frame.left, &priv->frame.top);
priv->format = mt9t112_cfmts + i;
@@ -945,9 +873,12 @@ static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = 0;
a->bounds.top = 0;
- a->bounds.width = VGA_WIDTH;
- a->bounds.height = VGA_HEIGHT;
- a->defrect = a->bounds;
+ a->bounds.width = MAX_WIDTH;
+ a->bounds.height = MAX_HEIGHT;
+ a->defrect.left = 0;
+ a->defrect.top = 0;
+ a->defrect.width = VGA_WIDTH;
+ a->defrect.height = VGA_HEIGHT;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
@@ -957,11 +888,11 @@ static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- a->c.left = 0;
- a->c.top = 0;
- a->c.width = VGA_WIDTH;
- a->c.height = VGA_HEIGHT;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ a->c = priv->frame;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
@@ -969,10 +900,10 @@ static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
struct v4l2_rect *rect = &a->c;
- return mt9t112_set_params(client, rect->width, rect->height,
- V4L2_MBUS_FMT_UYVY8_2X8);
+ return mt9t112_set_params(priv, rect, priv->format->code);
}
static int mt9t112_g_fmt(struct v4l2_subdev *sd,
@@ -981,16 +912,9 @@ static int mt9t112_g_fmt(struct v4l2_subdev *sd,
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t112_priv *priv = to_mt9t112(client);
- if (!priv->format) {
- int ret = mt9t112_set_params(client, VGA_WIDTH, VGA_HEIGHT,
- V4L2_MBUS_FMT_UYVY8_2X8);
- if (ret < 0)
- return ret;
- }
-
mf->width = priv->frame.width;
mf->height = priv->frame.height;
- /* TODO: set colorspace */
+ mf->colorspace = priv->format->colorspace;
mf->code = priv->format->code;
mf->field = V4L2_FIELD_NONE;
@@ -1001,17 +925,42 @@ static int mt9t112_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ struct v4l2_rect rect = {
+ .width = mf->width,
+ .height = mf->height,
+ .left = priv->frame.left,
+ .top = priv->frame.top,
+ };
+ int ret;
+
+ ret = mt9t112_set_params(priv, &rect, mf->code);
- /* TODO: set colorspace */
- return mt9t112_set_params(client, mf->width, mf->height, mf->code);
+ if (!ret)
+ mf->colorspace = priv->format->colorspace;
+
+ return ret;
}
static int mt9t112_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
- mt9t112_frame_check(&mf->width, &mf->height);
+ unsigned int top, left;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
+ if (mt9t112_cfmts[i].code == mf->code)
+ break;
+
+ if (i == ARRAY_SIZE(mt9t112_cfmts)) {
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ mf->colorspace = mt9t112_cfmts[i].colorspace;
+ }
+
+ mt9t112_frame_check(&mf->width, &mf->height, &left, &top);
- /* TODO: set colorspace */
mf->field = V4L2_FIELD_NONE;
return 0;
@@ -1024,6 +973,35 @@ static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return -EINVAL;
*code = mt9t112_cfmts[index].code;
+
+ return 0;
+}
+
+static int mt9t112_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9t112_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ priv->flags |= PCLK_RISING;
+
return 0;
}
@@ -1036,36 +1014,25 @@ static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = {
.g_crop = mt9t112_g_crop,
.s_crop = mt9t112_s_crop,
.enum_mbus_fmt = mt9t112_enum_fmt,
+ .g_mbus_config = mt9t112_g_mbus_config,
+ .s_mbus_config = mt9t112_s_mbus_config,
};
/************************************************************************
-
-
i2c driver
-
-
************************************************************************/
static struct v4l2_subdev_ops mt9t112_subdev_ops = {
.core = &mt9t112_subdev_core_ops,
.video = &mt9t112_subdev_video_ops,
};
-static int mt9t112_camera_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int mt9t112_camera_probe(struct i2c_client *client)
{
struct mt9t112_priv *priv = to_mt9t112(client);
const char *devname;
int chipid;
/*
- * We must have a parent by now. And it cannot be a wrong one.
- * So this entire test is completely redundant.
- */
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
- return -ENODEV;
-
- /*
* check and show chip ID
*/
mt9t112_reg_read(chipid, client, 0x0000);
@@ -1092,20 +1059,21 @@ static int mt9t112_camera_probe(struct soc_camera_device *icd,
static int mt9t112_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
- struct mt9t112_priv *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl;
- int ret;
+ struct mt9t112_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct v4l2_rect rect = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .left = (MAX_WIDTH - VGA_WIDTH) / 2,
+ .top = (MAX_HEIGHT - VGA_HEIGHT) / 2,
+ };
+ int ret;
- if (!icd) {
- dev_err(&client->dev, "mt9t112: missing soc-camera data!\n");
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "mt9t112: missing platform data!\n");
return -EINVAL;
}
- icl = to_soc_camera_link(icd);
- if (!icl || !icl->priv)
- return -EINVAL;
-
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -1114,13 +1082,12 @@ static int mt9t112_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
- icd->ops = &mt9t112_ops;
-
- ret = mt9t112_camera_probe(icd, client);
- if (ret) {
- icd->ops = NULL;
+ ret = mt9t112_camera_probe(client);
+ if (ret)
kfree(priv);
- }
+
+ /* Cannot fail: using the default supported pixel code */
+ mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
return ret;
}
@@ -1128,9 +1095,7 @@ static int mt9t112_probe(struct i2c_client *client,
static int mt9t112_remove(struct i2c_client *client)
{
struct mt9t112_priv *priv = to_mt9t112(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
kfree(priv);
return 0;
}
@@ -1151,11 +1116,7 @@ static struct i2c_driver mt9t112_i2c_driver = {
};
/************************************************************************
-
-
module function
-
-
************************************************************************/
static int __init mt9t112_module_init(void)
{
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c
index 4904d25f689f..db74dd27c722 100644
--- a/drivers/media/video/mt9v011.c
+++ b/drivers/media/video/mt9v011.c
@@ -9,6 +9,7 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <asm/div64.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
@@ -54,11 +55,20 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Gain",
.minimum = 0,
- .maximum = (1 << 10) - 1,
+ .maximum = (1 << 12) - 1 - 0x0020,
.step = 1,
.default_value = 0x0020,
.flags = 0,
}, {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 2047,
+ .step = 1,
+ .default_value = 0x01fc,
+ .flags = 0,
+ }, {
.id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Red Balance",
@@ -105,7 +115,8 @@ struct mt9v011 {
unsigned hflip:1;
unsigned vflip:1;
- u16 global_gain, red_bal, blue_bal;
+ u16 global_gain, exposure;
+ s16 red_bal, blue_bal;
};
static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd)
@@ -180,24 +191,68 @@ static const struct i2c_reg_value mt9v011_init_default[] = {
{ R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */
};
+
+static u16 calc_mt9v011_gain(s16 lineargain)
+{
+
+ u16 digitalgain = 0;
+ u16 analogmult = 0;
+ u16 analoginit = 0;
+
+ if (lineargain < 0)
+ lineargain = 0;
+
+ /* recommended minimum */
+ lineargain += 0x0020;
+
+ if (lineargain > 2047)
+ lineargain = 2047;
+
+ if (lineargain > 1023) {
+ digitalgain = 3;
+ analogmult = 3;
+ analoginit = lineargain / 16;
+ } else if (lineargain > 511) {
+ digitalgain = 1;
+ analogmult = 3;
+ analoginit = lineargain / 8;
+ } else if (lineargain > 255) {
+ analogmult = 3;
+ analoginit = lineargain / 4;
+ } else if (lineargain > 127) {
+ analogmult = 1;
+ analoginit = lineargain / 2;
+ } else
+ analoginit = lineargain;
+
+ return analoginit + (analogmult << 7) + (digitalgain << 9);
+
+}
+
static void set_balance(struct v4l2_subdev *sd)
{
struct mt9v011 *core = to_mt9v011(sd);
- u16 green1_gain, green2_gain, blue_gain, red_gain;
+ u16 green_gain, blue_gain, red_gain;
+ u16 exposure;
+ s16 bal;
- green1_gain = core->global_gain;
- green2_gain = core->global_gain;
+ exposure = core->exposure;
- blue_gain = core->global_gain +
- core->global_gain * core->blue_bal / (1 << 9);
+ green_gain = calc_mt9v011_gain(core->global_gain);
- red_gain = core->global_gain +
- core->global_gain * core->blue_bal / (1 << 9);
+ bal = core->global_gain;
+ bal += (core->blue_bal * core->global_gain / (1 << 7));
+ blue_gain = calc_mt9v011_gain(bal);
- mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green1_gain);
- mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green1_gain);
+ bal = core->global_gain;
+ bal += (core->red_bal * core->global_gain / (1 << 7));
+ red_gain = calc_mt9v011_gain(bal);
+
+ mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain);
+ mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain);
mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain);
mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain);
+ mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure);
}
static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator)
@@ -286,7 +341,7 @@ static void set_res(struct v4l2_subdev *sd)
* be missing.
*/
- hstart = 14 + (640 - core->width) / 2;
+ hstart = 20 + (640 - core->width) / 2;
mt9v011_write(sd, R02_MT9V011_COLSTART, hstart);
mt9v011_write(sd, R04_MT9V011_WIDTH, core->width);
mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width);
@@ -338,6 +393,9 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
case V4L2_CID_GAIN:
ctrl->value = core->global_gain;
return 0;
+ case V4L2_CID_EXPOSURE:
+ ctrl->value = core->exposure;
+ return 0;
case V4L2_CID_RED_BALANCE:
ctrl->value = core->red_bal;
return 0;
@@ -392,6 +450,9 @@ static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
case V4L2_CID_GAIN:
core->global_gain = ctrl->value;
break;
+ case V4L2_CID_EXPOSURE:
+ core->exposure = ctrl->value;
+ break;
case V4L2_CID_RED_BALANCE:
core->red_bal = ctrl->value;
break;
@@ -598,6 +659,7 @@ static int mt9v011_probe(struct i2c_client *c,
}
core->global_gain = 0x0024;
+ core->exposure = 0x01fc;
core->width = 640;
core->height = 480;
core->xtal = 27000000; /* Hz */
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index fc76ed1c08e5..690ee0d42eeb 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -13,10 +13,13 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/log2.h>
+#include <linux/module.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
/*
* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
@@ -100,6 +103,17 @@ static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = {
struct mt9v022 {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct {
+ /* gain/auto-gain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
struct v4l2_rect rect; /* Sensor window */
const struct mt9v022_datafmt *fmt;
const struct mt9v022_datafmt *fmts;
@@ -178,6 +192,8 @@ static int mt9v022_init(struct i2c_client *client)
ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1);
if (!ret)
ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0);
+ if (!ret)
+ return v4l2_ctrl_handler_setup(&mt9v022->hdl);
return ret;
}
@@ -199,78 +215,6 @@ static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int mt9v022_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
- struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
- int ret;
- u16 pixclk = 0;
-
- /* Only one width bit may be set */
- if (!is_power_of_2(width_flag))
- return -EINVAL;
-
- if (icl->set_bus_param) {
- ret = icl->set_bus_param(icl, width_flag);
- if (ret)
- return ret;
- } else {
- /*
- * Without board specific bus width settings we only support the
- * sensors native bus width
- */
- if (width_flag != SOCAM_DATAWIDTH_10)
- return -EINVAL;
- }
-
- flags = soc_camera_apply_sensor_flags(icl, flags);
-
- if (flags & SOCAM_PCLK_SAMPLE_FALLING)
- pixclk |= 0x10;
-
- if (!(flags & SOCAM_HSYNC_ACTIVE_HIGH))
- pixclk |= 0x1;
-
- if (!(flags & SOCAM_VSYNC_ACTIVE_HIGH))
- pixclk |= 0x2;
-
- ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk);
- if (ret < 0)
- return ret;
-
- if (!(flags & SOCAM_MASTER))
- mt9v022->chip_control &= ~0x8;
-
- ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
- if (ret < 0)
- return ret;
-
- dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
- pixclk, mt9v022->chip_control);
-
- return 0;
-}
-
-static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned int flags = SOCAM_MASTER | SOCAM_SLAVE |
- SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH;
-
- if (icl->query_bus_param)
- flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
- else
- flags |= SOCAM_DATAWIDTH_10;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -389,7 +333,7 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd,
/*
* The caller provides a supported format, as verified per call to
- * icd->try_fmt(), datawidth is from our supported format list
+ * .try_mbus_fmt(), datawidth is from our supported format list
*/
switch (mf->code) {
case V4L2_MBUS_FMT_Y8_1X8:
@@ -502,236 +446,131 @@ static int mt9v022_s_register(struct v4l2_subdev *sd,
}
#endif
-static const struct v4l2_queryctrl mt9v022_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Analog Gain",
- .minimum = 64,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 1,
- .maximum = 255,
- .step = 1,
- .default_value = 255,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }, {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }
-};
-
-static struct soc_camera_ops mt9v022_ops = {
- .set_bus_param = mt9v022_set_bus_param,
- .query_bus_param = mt9v022_query_bus_param,
- .controls = mt9v022_controls,
- .num_controls = ARRAY_SIZE(mt9v022_controls),
-};
-
-static int mt9v022_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct mt9v022 *mt9v022 = container_of(ctrl->handler,
+ struct mt9v022, hdl);
+ struct v4l2_subdev *sd = &mt9v022->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- const struct v4l2_queryctrl *qctrl;
+ struct v4l2_ctrl *gain = mt9v022->gain;
+ struct v4l2_ctrl *exp = mt9v022->exposure;
unsigned long range;
int data;
- qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
-
switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- data = reg_read(client, MT9V022_READ_MODE);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x10);
- break;
- case V4L2_CID_HFLIP:
- data = reg_read(client, MT9V022_READ_MODE);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x20);
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- data = reg_read(client, MT9V022_AEC_AGC_ENABLE);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x1);
- break;
case V4L2_CID_AUTOGAIN:
- data = reg_read(client, MT9V022_AEC_AGC_ENABLE);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x2);
- break;
- case V4L2_CID_GAIN:
data = reg_read(client, MT9V022_ANALOG_GAIN);
if (data < 0)
return -EIO;
- range = qctrl->maximum - qctrl->minimum;
- ctrl->value = ((data - 16) * range + 24) / 48 + qctrl->minimum;
-
- break;
- case V4L2_CID_EXPOSURE:
+ range = gain->maximum - gain->minimum;
+ gain->val = ((data - 16) * range + 24) / 48 + gain->minimum;
+ return 0;
+ case V4L2_CID_EXPOSURE_AUTO:
data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH);
if (data < 0)
return -EIO;
- range = qctrl->maximum - qctrl->minimum;
- ctrl->value = ((data - 1) * range + 239) / 479 + qctrl->minimum;
-
- break;
+ range = exp->maximum - exp->minimum;
+ exp->val = ((data - 1) * range + 239) / 479 + exp->minimum;
+ return 0;
}
- return 0;
+ return -EINVAL;
}
-static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl)
{
- int data;
+ struct mt9v022 *mt9v022 = container_of(ctrl->handler,
+ struct mt9v022, hdl);
+ struct v4l2_subdev *sd = &mt9v022->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- const struct v4l2_queryctrl *qctrl;
-
- qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
- if (!qctrl)
- return -EINVAL;
+ int data;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, MT9V022_READ_MODE, 0x10);
else
data = reg_clear(client, MT9V022_READ_MODE, 0x10);
if (data < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_HFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, MT9V022_READ_MODE, 0x20);
else
data = reg_clear(client, MT9V022_READ_MODE, 0x20);
if (data < 0)
return -EIO;
- break;
- case V4L2_CID_GAIN:
- /* mt9v022 has minimum == default */
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
- else {
- unsigned long range = qctrl->maximum - qctrl->minimum;
+ return 0;
+ case V4L2_CID_AUTOGAIN:
+ if (ctrl->val) {
+ if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
+ return -EIO;
+ } else {
+ struct v4l2_ctrl *gain = mt9v022->gain;
+ /* mt9v022 has minimum == default */
+ unsigned long range = gain->maximum - gain->minimum;
/* Valid values 16 to 64, 32 to 64 must be even. */
- unsigned long gain = ((ctrl->value - qctrl->minimum) *
+ unsigned long gain_val = ((gain->val - gain->minimum) *
48 + range / 2) / range + 16;
- if (gain >= 32)
- gain &= ~1;
+
+ if (gain_val >= 32)
+ gain_val &= ~1;
+
/*
* The user wants to set gain manually, hope, she
* knows, what she's doing... Switch AGC off.
*/
-
if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
return -EIO;
dev_dbg(&client->dev, "Setting gain from %d to %lu\n",
- reg_read(client, MT9V022_ANALOG_GAIN), gain);
- if (reg_write(client, MT9V022_ANALOG_GAIN, gain) < 0)
+ reg_read(client, MT9V022_ANALOG_GAIN), gain_val);
+ if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0)
return -EIO;
}
- break;
- case V4L2_CID_EXPOSURE:
- /* mt9v022 has maximum == default */
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
- else {
- unsigned long range = qctrl->maximum - qctrl->minimum;
- unsigned long shutter = ((ctrl->value - qctrl->minimum) *
- 479 + range / 2) / range + 1;
+ return 0;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_AUTO) {
+ data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
+ } else {
+ struct v4l2_ctrl *exp = mt9v022->exposure;
+ unsigned long range = exp->maximum - exp->minimum;
+ unsigned long shutter = ((exp->val - exp->minimum) *
+ 479 + range / 2) / range + 1;
+
/*
* The user wants to set shutter width manually, hope,
* she knows, what she's doing... Switch AEC off.
*/
-
- if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0)
+ data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
+ if (data < 0)
return -EIO;
-
dev_dbg(&client->dev, "Shutter width from %d to %lu\n",
- reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
- shutter);
+ reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
+ shutter);
if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
- shutter) < 0)
+ shutter) < 0)
return -EIO;
}
- break;
- case V4L2_CID_AUTOGAIN:
- if (ctrl->value)
- data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2);
- else
- data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2);
- if (data < 0)
- return -EIO;
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- if (ctrl->value)
- data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
- else
- data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
- if (data < 0)
- return -EIO;
- break;
+ return 0;
}
- return 0;
+ return -EINVAL;
}
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
-static int mt9v022_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int mt9v022_video_probe(struct i2c_client *client)
{
struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
s32 data;
int ret;
unsigned long flags;
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
- return -ENODEV;
-
/* Read out the chip version register */
data = reg_read(client, MT9V022_CHIP_VERSION);
@@ -805,16 +644,6 @@ ei2c:
return ret;
}
-static void mt9v022_video_remove(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- dev_dbg(&icd->dev, "Video removed: %p, %p\n",
- icd->dev.parent, icd->vdev);
- if (icl->free_bus)
- icl->free_bus(icl);
-}
-
static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -825,9 +654,12 @@ static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
return 0;
}
+static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = {
+ .g_volatile_ctrl = mt9v022_g_volatile_ctrl,
+ .s_ctrl = mt9v022_s_ctrl,
+};
+
static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
- .g_ctrl = mt9v022_g_ctrl,
- .s_ctrl = mt9v022_s_ctrl,
.g_chip_ident = mt9v022_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9v022_g_register,
@@ -848,6 +680,72 @@ static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int mt9v022_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9v022_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+ unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample;
+ int ret;
+ u16 pixclk = 0;
+
+ if (icl->set_bus_param) {
+ ret = icl->set_bus_param(icl, 1 << (bps - 1));
+ if (ret)
+ return ret;
+ } else if (bps != 10) {
+ /*
+ * Without board specific bus width settings we only support the
+ * sensors native bus width
+ */
+ return -EINVAL;
+ }
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ pixclk |= 0x10;
+
+ if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH))
+ pixclk |= 0x1;
+
+ if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH))
+ pixclk |= 0x2;
+
+ ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk);
+ if (ret < 0)
+ return ret;
+
+ if (!(flags & V4L2_MBUS_MASTER))
+ mt9v022->chip_control &= ~0x8;
+
+ ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
+ pixclk, mt9v022->chip_control);
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
.s_stream = mt9v022_s_stream,
.s_mbus_fmt = mt9v022_s_fmt,
@@ -857,6 +755,8 @@ static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
.g_crop = mt9v022_g_crop,
.cropcap = mt9v022_cropcap,
.enum_mbus_fmt = mt9v022_enum_fmt,
+ .g_mbus_config = mt9v022_g_mbus_config,
+ .s_mbus_config = mt9v022_s_mbus_config,
};
static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
@@ -873,17 +773,10 @@ static int mt9v022_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9v022 *mt9v022;
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
int ret;
- if (!icd) {
- dev_err(&client->dev, "MT9V022: missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "MT9V022 driver needs platform data\n");
return -EINVAL;
@@ -900,10 +793,39 @@ static int mt9v022_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9v022->hdl, 6);
+ v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl,
+ &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+
+ mt9v022->subdev.ctrl_handler = &mt9v022->hdl;
+ if (mt9v022->hdl.error) {
+ int err = mt9v022->hdl.error;
+
+ kfree(mt9v022);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+ v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true);
mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
- icd->ops = &mt9v022_ops;
/*
* MT9V022 _really_ corrupts the first read out line.
* TODO: verify on i.MX31
@@ -914,9 +836,9 @@ static int mt9v022_probe(struct i2c_client *client,
mt9v022->rect.width = MT9V022_MAX_WIDTH;
mt9v022->rect.height = MT9V022_MAX_HEIGHT;
- ret = mt9v022_video_probe(icd, client);
+ ret = mt9v022_video_probe(client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&mt9v022->hdl);
kfree(mt9v022);
}
@@ -926,10 +848,12 @@ static int mt9v022_probe(struct i2c_client *client,
static int mt9v022_remove(struct i2c_client *client)
{
struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- icd->ops = NULL;
- mt9v022_video_remove(icd);
+ v4l2_device_unregister_subdev(&mt9v022->subdev);
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ v4l2_ctrl_handler_free(&mt9v022->hdl);
kfree(mt9v022);
return 0;
diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c
index 1319c2c48aff..f080c162123f 100644
--- a/drivers/media/video/mt9v032.c
+++ b/drivers/media/video/mt9v032.c
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/v4l2-mediabus.h>
+#include <linux/module.h>
#include <media/mt9v032.h>
#include <media/v4l2-ctrls.h>
@@ -31,14 +32,14 @@
#define MT9V032_CHIP_VERSION 0x00
#define MT9V032_CHIP_ID_REV1 0x1311
#define MT9V032_CHIP_ID_REV3 0x1313
-#define MT9V032_ROW_START 0x01
-#define MT9V032_ROW_START_MIN 4
-#define MT9V032_ROW_START_DEF 10
-#define MT9V032_ROW_START_MAX 482
-#define MT9V032_COLUMN_START 0x02
+#define MT9V032_COLUMN_START 0x01
#define MT9V032_COLUMN_START_MIN 1
-#define MT9V032_COLUMN_START_DEF 2
+#define MT9V032_COLUMN_START_DEF 1
#define MT9V032_COLUMN_START_MAX 752
+#define MT9V032_ROW_START 0x02
+#define MT9V032_ROW_START_MIN 4
+#define MT9V032_ROW_START_DEF 5
+#define MT9V032_ROW_START_MAX 482
#define MT9V032_WINDOW_HEIGHT 0x03
#define MT9V032_WINDOW_HEIGHT_MIN 1
#define MT9V032_WINDOW_HEIGHT_DEF 480
@@ -420,13 +421,13 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev,
struct v4l2_rect *__crop;
struct v4l2_rect rect;
- /* Clamp the crop rectangle boundaries and align them to a multiple of 2
- * pixels.
+ /* Clamp the crop rectangle boundaries and align them to a non multiple
+ * of 2 pixels to ensure a GRBG Bayer pattern.
*/
- rect.left = clamp(ALIGN(crop->rect.left, 2),
+ rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1,
MT9V032_COLUMN_START_MIN,
MT9V032_COLUMN_START_MAX);
- rect.top = clamp(ALIGN(crop->rect.top, 2),
+ rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1,
MT9V032_ROW_START_MIN,
MT9V032_ROW_START_MAX);
rect.width = clamp(ALIGN(crop->rect.width, 2),
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c
index 63f8a0cc33d8..18e94c7d2be8 100644
--- a/drivers/media/video/mx1_camera.c
+++ b/drivers/media/video/mx1_camera.c
@@ -31,7 +31,6 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/time.h>
-#include <linux/version.h>
#include <linux/videodev2.h>
#include <media/soc_camera.h>
@@ -73,17 +72,16 @@
#define CSISR_SOF_INT (1 << 16)
#define CSISR_DRDY (1 << 0)
-#define VERSION_CODE KERNEL_VERSION(0, 0, 1)
+#define DRIVER_VERSION "0.0.2"
#define DRIVER_NAME "mx1-camera"
#define CSI_IRQ_MASK (CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \
CSISR_STATFF_INT | CSISR_RXFF_INT | CSISR_SOF_INT)
-#define CSI_BUS_FLAGS (SOCAM_MASTER | SOCAM_HSYNC_ACTIVE_HIGH | \
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | \
- SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW | \
- SOCAM_DATAWIDTH_8)
+#define CSI_BUS_FLAGS (V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_LOW)
#define MAX_VIDEO_MEM 16 /* Video memory limit in megabytes */
@@ -142,7 +140,7 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
*count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
- dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
return 0;
}
@@ -154,7 +152,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf)
BUG_ON(in_interrupt());
- dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
/*
@@ -179,7 +177,7 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq,
if (bytes_per_line < 0)
return bytes_per_line;
- dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
/* Added list head initialization on alloc */
@@ -232,7 +230,7 @@ out:
static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
{
struct videobuf_buffer *vbuf = &pcdev->active->vb;
- struct device *dev = pcdev->icd->dev.parent;
+ struct device *dev = pcdev->icd->parent;
int ret;
if (unlikely(!pcdev->active)) {
@@ -256,11 +254,11 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
- dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
list_add_tail(&vb->queue, &pcdev->capture);
@@ -287,7 +285,7 @@ static void mx1_videobuf_release(struct videobuf_queue *vq,
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
#ifdef DEBUG
struct soc_camera_device *icd = vq->priv_data;
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
@@ -343,7 +341,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev,
static void mx1_camera_dma_irq(int channel, void *data)
{
struct mx1_camera_dev *pcdev = data;
- struct device *dev = pcdev->icd->dev.parent;
+ struct device *dev = pcdev->icd->parent;
struct mx1_buffer *buf;
struct videobuf_buffer *vb;
unsigned long flags;
@@ -378,10 +376,10 @@ static struct videobuf_queue_ops mx1_videobuf_ops = {
static void mx1_camera_init_videobuf(struct videobuf_queue *q,
struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
- videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->dev.parent,
+ videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent,
&pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_NONE,
sizeof(struct mx1_buffer), icd, &icd->video_lock);
@@ -401,7 +399,7 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
*/
div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
- dev_dbg(pcdev->icd->dev.parent,
+ dev_dbg(pcdev->icd->parent,
"System clock %lukHz, target freq %dkHz, divisor %lu\n",
lcdclk / 1000, mclk / 1000, div);
@@ -412,7 +410,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
{
unsigned int csicr1 = CSICR1_EN;
- dev_dbg(pcdev->icd->dev.parent, "Activate device\n");
+ dev_dbg(pcdev->icd->parent, "Activate device\n");
clk_enable(pcdev->clk);
@@ -428,7 +426,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
{
- dev_dbg(pcdev->icd->dev.parent, "Deactivate device\n");
+ dev_dbg(pcdev->icd->parent, "Deactivate device\n");
/* Disable all CSI interface */
__raw_writel(0x00, pcdev->base + CSICR1);
@@ -442,13 +440,13 @@ static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
*/
static int mx1_camera_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
if (pcdev->icd)
return -EBUSY;
- dev_info(icd->dev.parent, "MX1 Camera driver attached to camera %d\n",
+ dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
icd->devnum);
mx1_camera_activate(pcdev);
@@ -460,7 +458,7 @@ static int mx1_camera_add_device(struct soc_camera_device *icd)
static void mx1_camera_remove_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
unsigned int csicr1;
@@ -473,7 +471,7 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd)
/* Stop DMA engine */
imx_dma_disable(pcdev->dma_chan);
- dev_info(icd->dev.parent, "MX1 Camera driver detached from camera %d\n",
+ dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
icd->devnum);
mx1_camera_deactivate(pcdev);
@@ -491,59 +489,73 @@ static int mx1_camera_set_crop(struct soc_camera_device *icd,
static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
- unsigned long camera_flags, common_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
unsigned int csicr1;
int ret;
- camera_flags = icd->ops->query_bus_param(icd);
-
/* MX1 supports only 8bit buswidth */
- common_flags = soc_camera_bus_param_compatible(camera_flags,
- CSI_BUS_FLAGS);
- if (!common_flags)
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, CSI_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, CSI_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = CSI_BUS_FLAGS;
+ }
/* Make choises, based on platform choice */
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & MX1_CAMERA_VSYNC_HIGH)
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
else
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
}
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & MX1_CAMERA_PCLK_RISING)
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
}
- if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) &&
- (common_flags & SOCAM_DATA_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & MX1_CAMERA_DATA_HIGH)
- common_flags &= ~SOCAM_DATA_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
else
- common_flags &= ~SOCAM_DATA_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
}
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
return ret;
+ }
csicr1 = __raw_readl(pcdev->base + CSICR1);
- if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
csicr1 |= CSICR1_REDGE;
- if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
csicr1 |= CSICR1_SOF_POL;
- if (common_flags & SOCAM_DATA_ACTIVE_LOW)
+ if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
csicr1 |= CSICR1_DATA_POL;
__raw_writel(csicr1, pcdev->base + CSICR1);
@@ -562,14 +574,14 @@ static int mx1_camera_set_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
- dev_warn(icd->dev.parent, "Format %x not found\n",
+ dev_warn(icd->parent, "Format %x not found\n",
pix->pixelformat);
return -EINVAL;
}
buswidth = xlate->host_fmt->bits_per_sample;
if (buswidth > 8) {
- dev_warn(icd->dev.parent,
+ dev_warn(icd->parent,
"bits-per-sample %d for format %x unsupported\n",
buswidth, pix->pixelformat);
return -EINVAL;
@@ -609,7 +621,7 @@ static int mx1_camera_try_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
- dev_warn(icd->dev.parent, "Format %x not found\n",
+ dev_warn(icd->parent, "Format %x not found\n",
pix->pixelformat);
return -EINVAL;
}
@@ -676,7 +688,6 @@ static int mx1_camera_querycap(struct soc_camera_host *ici,
{
/* cap->name is set by the friendly caller:-> */
strlcpy(cap->card, "i.MX1/i.MXL Camera", sizeof(cap->card));
- cap->version = VERSION_CODE;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
@@ -883,4 +894,5 @@ module_exit(mx1_camera_exit);
MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver");
MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>");
MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
index 4eab1c620318..a803d9ea8fd6 100644
--- a/drivers/media/video/mx2_camera.c
+++ b/drivers/media/video/mx2_camera.c
@@ -23,7 +23,6 @@
#include <linux/mm.h>
#include <linux/moduleparam.h>
#include <linux/time.h>
-#include <linux/version.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
@@ -47,7 +46,7 @@
#include <asm/dma.h>
#define MX2_CAM_DRV_NAME "mx2-camera"
-#define MX2_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)
+#define MX2_CAM_VERSION "0.0.6"
#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
/* reset values */
@@ -278,7 +277,7 @@ static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
*/
static int mx2_camera_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
int ret;
u32 csicr1;
@@ -303,7 +302,7 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
pcdev->icd = icd;
- dev_info(icd->dev.parent, "Camera driver attached to camera %d\n",
+ dev_info(icd->parent, "Camera driver attached to camera %d\n",
icd->devnum);
return 0;
@@ -311,12 +310,12 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
static void mx2_camera_remove_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
BUG_ON(icd != pcdev->icd);
- dev_info(icd->dev.parent, "Camera driver detached from camera %d\n",
+ dev_info(icd->parent, "Camera driver detached from camera %d\n",
icd->devnum);
mx2_camera_deactivate(pcdev);
@@ -437,7 +436,7 @@ static int mx2_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
- dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
if (bytes_per_line < 0)
return bytes_per_line;
@@ -457,7 +456,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx2_buffer *buf)
struct soc_camera_device *icd = vq->priv_data;
struct videobuf_buffer *vb = &buf->vb;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
/*
@@ -467,7 +466,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx2_buffer *buf)
videobuf_waiton(vq, vb, 0, 0);
videobuf_dma_contig_free(vq, vb);
- dev_dbg(&icd->dev, "%s freed\n", __func__);
+ dev_dbg(icd->parent, "%s freed\n", __func__);
vb->state = VIDEOBUF_NEEDS_INIT;
}
@@ -481,7 +480,7 @@ static int mx2_videobuf_prepare(struct videobuf_queue *vq,
icd->current_fmt->host_fmt);
int ret = 0;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
if (bytes_per_line < 0)
@@ -533,12 +532,12 @@ static void mx2_videobuf_queue(struct videobuf_queue *vq,
{
struct soc_camera_device *icd = vq->priv_data;
struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
unsigned long flags;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
spin_lock_irqsave(&pcdev->lock, flags);
@@ -611,27 +610,27 @@ static void mx2_videobuf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
unsigned long flags;
#ifdef DEBUG
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
switch (vb->state) {
case VIDEOBUF_ACTIVE:
- dev_info(&icd->dev, "%s (active)\n", __func__);
+ dev_info(icd->parent, "%s (active)\n", __func__);
break;
case VIDEOBUF_QUEUED:
- dev_info(&icd->dev, "%s (queued)\n", __func__);
+ dev_info(icd->parent, "%s (queued)\n", __func__);
break;
case VIDEOBUF_PREPARED:
- dev_info(&icd->dev, "%s (prepared)\n", __func__);
+ dev_info(icd->parent, "%s (prepared)\n", __func__);
break;
default:
- dev_info(&icd->dev, "%s (unknown) %d\n", __func__,
+ dev_info(icd->parent, "%s (unknown) %d\n", __func__,
vb->state);
break;
}
@@ -678,7 +677,7 @@ static struct videobuf_queue_ops mx2_videobuf_ops = {
static void mx2_camera_init_videobuf(struct videobuf_queue *q,
struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
videobuf_queue_dma_contig_init(q, &mx2_videobuf_ops, pcdev->dev,
@@ -687,16 +686,15 @@ static void mx2_camera_init_videobuf(struct videobuf_queue *q,
icd, &icd->video_lock);
}
-#define MX2_BUS_FLAGS (SOCAM_DATAWIDTH_8 | \
- SOCAM_MASTER | \
- SOCAM_VSYNC_ACTIVE_HIGH | \
- SOCAM_VSYNC_ACTIVE_LOW | \
- SOCAM_HSYNC_ACTIVE_HIGH | \
- SOCAM_HSYNC_ACTIVE_LOW | \
- SOCAM_PCLK_SAMPLE_RISING | \
- SOCAM_PCLK_SAMPLE_FALLING | \
- SOCAM_DATA_ACTIVE_HIGH | \
- SOCAM_DATA_ACTIVE_LOW)
+#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH | \
+ V4L2_MBUS_DATA_ACTIVE_LOW)
static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
{
@@ -719,7 +717,7 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
int bytesperline)
{
struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
writel(pcdev->discard_buffer_dma,
@@ -771,46 +769,59 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
__u32 pixfmt)
{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
- unsigned long camera_flags, common_flags;
- int ret = 0;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ int ret;
int bytesperline;
u32 csicr1 = pcdev->csicr1;
- camera_flags = icd->ops->query_bus_param(icd);
-
- common_flags = soc_camera_bus_param_compatible(camera_flags,
- MX2_BUS_FLAGS);
- if (!common_flags)
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, MX2_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = MX2_BUS_FLAGS;
+ }
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
else
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
}
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
}
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
return ret;
+ }
- if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
csicr1 |= CSICR1_REDGE;
- if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
csicr1 |= CSICR1_SOF_POL;
- if (common_flags & SOCAM_HSYNC_ACTIVE_HIGH)
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
csicr1 |= CSICR1_HSYNC_POL;
if (pcdev->platform_flags & MX2_CAMERA_SWAP16)
csicr1 |= CSICR1_SWAP16_EN;
@@ -891,7 +902,7 @@ static int mx2_camera_set_crop(struct soc_camera_device *icd,
if (ret < 0)
return ret;
- dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n",
+ dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
mf.width, mf.height);
icd->user_width = mf.width;
@@ -911,7 +922,7 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
- dev_warn(icd->dev.parent, "Format %x not found\n",
+ dev_warn(icd->parent, "Format %x not found\n",
pix->pixelformat);
return -EINVAL;
}
@@ -951,7 +962,7 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (pixfmt && !xlate) {
- dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
return -EINVAL;
}
@@ -974,11 +985,16 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
if (pix->bytesperline < 0)
return pix->bytesperline;
pix->sizeimage = pix->height * pix->bytesperline;
- if (pix->sizeimage > (4 * 0x3ffff)) { /* CSIRXCNT limit */
- dev_warn(icd->dev.parent,
- "Image size (%u) above limit\n",
- pix->sizeimage);
- return -EINVAL;
+ /* Check against the CSIRXCNT limit */
+ if (pix->sizeimage > 4 * 0x3ffff) {
+ /* Adjust geometry, preserve aspect ratio */
+ unsigned int new_height = int_sqrt(4 * 0x3ffff *
+ pix->height / pix->bytesperline);
+ pix->width = new_height * pix->width / pix->height;
+ pix->height = new_height;
+ pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+ xlate->host_fmt);
+ BUG_ON(pix->bytesperline < 0);
}
}
@@ -996,7 +1012,7 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
if (mf.field == V4L2_FIELD_ANY)
mf.field = V4L2_FIELD_NONE;
if (mf.field != V4L2_FIELD_NONE) {
- dev_err(icd->dev.parent, "Field type %d unsupported.\n",
+ dev_err(icd->parent, "Field type %d unsupported.\n",
mf.field);
return -EINVAL;
}
@@ -1014,7 +1030,6 @@ static int mx2_camera_querycap(struct soc_camera_host *ici,
{
/* cap->name is set by the friendly caller:-> */
strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
- cap->version = MX2_CAM_VERSION_CODE;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
@@ -1523,3 +1538,4 @@ module_exit(mx2_camera_exit);
MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver");
MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
MODULE_LICENSE("GPL");
+MODULE_VERSION(MX2_CAM_VERSION);
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index c7680eb83664..f96f92f00f92 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -11,7 +11,6 @@
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/videodev2.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
@@ -110,10 +109,12 @@ struct mx3_camera_dev {
unsigned long platform_flags;
unsigned long mclk;
+ u16 width_flags; /* max 15 bits */
struct list_head capture;
spinlock_t lock; /* Protects video buffer lists */
struct mx3_camera_buffer *active;
+ size_t buf_total;
struct vb2_alloc_ctx *alloc_ctx;
enum v4l2_field field;
int sequence;
@@ -191,79 +192,53 @@ static void mx3_cam_dma_done(void *arg)
* Calculate the __buffer__ (not data) size and number of buffers.
*/
static int mx3_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
unsigned int *count, unsigned int *num_planes,
- unsigned long sizes[], void *alloc_ctxs[])
+ unsigned int sizes[], void *alloc_ctxs[])
{
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
-
- if (bytes_per_line < 0)
- return bytes_per_line;
+ int bytes_per_line;
+ unsigned int height;
if (!mx3_cam->idmac_channel[0])
return -EINVAL;
- *num_planes = 1;
-
- mx3_cam->sequence = 0;
- sizes[0] = bytes_per_line * icd->user_height;
- alloc_ctxs[0] = mx3_cam->alloc_ctx;
-
- if (!*count)
- *count = 32;
-
- if (sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
- *count = MAX_VIDEO_MEM * 1024 * 1024 / sizes[0];
-
- return 0;
-}
-
-static int mx3_videobuf_prepare(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
- struct scatterlist *sg;
- struct mx3_camera_buffer *buf;
- size_t new_size;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ if (fmt) {
+ const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+ fmt->fmt.pix.pixelformat);
+ if (!xlate)
+ return -EINVAL;
+ bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ height = fmt->fmt.pix.height;
+ } else {
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
-
+ height = icd->user_height;
+ }
if (bytes_per_line < 0)
return bytes_per_line;
- buf = to_mx3_vb(vb);
- sg = &buf->sg;
-
- new_size = bytes_per_line * icd->user_height;
-
- if (vb2_plane_size(vb, 0) < new_size) {
- dev_err(icd->dev.parent, "Buffer too small (%lu < %zu)\n",
- vb2_plane_size(vb, 0), new_size);
- return -ENOBUFS;
- }
+ sizes[0] = bytes_per_line * height;
- if (buf->state == CSI_BUF_NEEDS_INIT) {
- sg_dma_address(sg) = vb2_dma_contig_plane_paddr(vb, 0);
- sg_dma_len(sg) = new_size;
+ alloc_ctxs[0] = mx3_cam->alloc_ctx;
- buf->txd = ichan->dma_chan.device->device_prep_slave_sg(
- &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE,
- DMA_PREP_INTERRUPT);
- if (!buf->txd)
- return -EIO;
+ if (!vq->num_buffers)
+ mx3_cam->sequence = 0;
- buf->txd->callback_param = buf->txd;
- buf->txd->callback = mx3_cam_dma_done;
+ if (!*count)
+ *count = 2;
- buf->state = CSI_BUF_PREPARED;
- }
+ /* If *num_planes != 0, we have already verified *count. */
+ if (!*num_planes &&
+ sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
+ sizes[0];
- vb2_set_plane_payload(vb, 0, new_size);
+ *num_planes = 1;
return 0;
}
@@ -284,31 +259,61 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
static void mx3_videobuf_queue(struct vb2_buffer *vb)
{
struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct mx3_camera_buffer *buf = to_mx3_vb(vb);
- struct dma_async_tx_descriptor *txd = buf->txd;
- struct idmac_channel *ichan = to_idmac_chan(txd->chan);
+ struct scatterlist *sg = &buf->sg;
+ struct dma_async_tx_descriptor *txd;
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
struct idmac_video_param *video = &ichan->params.video;
- dma_cookie_t cookie;
- u32 fourcc = icd->current_fmt->host_fmt->fourcc;
+ const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, host_fmt);
unsigned long flags;
+ dma_cookie_t cookie;
+ size_t new_size;
+
+ BUG_ON(bytes_per_line <= 0);
+
+ new_size = bytes_per_line * icd->user_height;
+
+ if (vb2_plane_size(vb, 0) < new_size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
+ vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size);
+ goto error;
+ }
+
+ if (buf->state == CSI_BUF_NEEDS_INIT) {
+ sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
+ sg_dma_len(sg) = new_size;
+
+ txd = ichan->dma_chan.device->device_prep_slave_sg(
+ &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT);
+ if (!txd)
+ goto error;
+
+ txd->callback_param = txd;
+ txd->callback = mx3_cam_dma_done;
+
+ buf->state = CSI_BUF_PREPARED;
+ buf->txd = txd;
+ } else {
+ txd = buf->txd;
+ }
+
+ vb2_set_plane_payload(vb, 0, new_size);
/* This is the configuration of one sg-element */
- video->out_pixel_fmt = fourcc_to_ipu_pix(fourcc);
+ video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
/*
- * If the IPU DMA channel is configured to transport
- * generic 8-bit data, we have to set up correctly the
- * geometry parameters upon the current pixel format.
- * So, since the DMA horizontal parameters are expressed
- * in bytes not pixels, convert these in the right unit.
+ * If the IPU DMA channel is configured to transfer generic
+ * 8-bit data, we have to set up the geometry parameters
+ * correctly, according to the current pixel format. The DMA
+ * horizontal parameters in this case are expressed in bytes,
+ * not in pixels.
*/
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- BUG_ON(bytes_per_line <= 0);
-
video->out_width = bytes_per_line;
video->out_height = icd->user_height;
video->out_stride = bytes_per_line;
@@ -337,7 +342,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
spin_unlock_irq(&mx3_cam->lock);
cookie = txd->tx_submit(txd);
- dev_dbg(icd->dev.parent, "Submitted cookie %d DMA 0x%08x\n",
+ dev_dbg(icd->parent, "Submitted cookie %d DMA 0x%08x\n",
cookie, sg_dma_address(&buf->sg));
if (cookie >= 0)
@@ -352,19 +357,20 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
mx3_cam->active = NULL;
spin_unlock_irqrestore(&mx3_cam->lock, flags);
+error:
vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
}
static void mx3_videobuf_release(struct vb2_buffer *vb)
{
struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct mx3_camera_buffer *buf = to_mx3_vb(vb);
struct dma_async_tx_descriptor *txd = buf->txd;
unsigned long flags;
- dev_dbg(icd->dev.parent,
+ dev_dbg(icd->parent,
"Release%s DMA 0x%08x, queue %sempty\n",
mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
list_empty(&buf->queue) ? "" : "not ");
@@ -385,17 +391,24 @@ static void mx3_videobuf_release(struct vb2_buffer *vb)
}
spin_unlock_irqrestore(&mx3_cam->lock, flags);
+
+ mx3_cam->buf_total -= vb2_plane_size(vb, 0);
}
static int mx3_videobuf_init(struct vb2_buffer *vb)
{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+
/* This is for locking debugging only */
INIT_LIST_HEAD(&buf->queue);
sg_init_table(&buf->sg, 1);
buf->state = CSI_BUF_NEEDS_INIT;
- buf->txd = NULL;
+
+ mx3_cam->buf_total += vb2_plane_size(vb, 0);
return 0;
}
@@ -403,16 +416,15 @@ static int mx3_videobuf_init(struct vb2_buffer *vb)
static int mx3_stop_streaming(struct vb2_queue *q)
{
struct soc_camera_device *icd = soc_camera_from_vb2q(q);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
- struct dma_chan *chan;
struct mx3_camera_buffer *buf, *tmp;
unsigned long flags;
if (ichan) {
- chan = &ichan->dma_chan;
- chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+ struct dma_chan *chan = &ichan->dma_chan;
+ chan->device->device_control(chan, DMA_PAUSE, 0);
}
spin_lock_irqsave(&mx3_cam->lock, flags);
@@ -420,8 +432,8 @@ static int mx3_stop_streaming(struct vb2_queue *q)
mx3_cam->active = NULL;
list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
- buf->state = CSI_BUF_NEEDS_INIT;
list_del_init(&buf->queue);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
spin_unlock_irqrestore(&mx3_cam->lock, flags);
@@ -431,7 +443,6 @@ static int mx3_stop_streaming(struct vb2_queue *q)
static struct vb2_ops mx3_videobuf_ops = {
.queue_setup = mx3_videobuf_setup,
- .buf_prepare = mx3_videobuf_prepare,
.buf_queue = mx3_videobuf_queue,
.buf_cleanup = mx3_videobuf_release,
.buf_init = mx3_videobuf_init,
@@ -499,7 +510,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
clk_enable(mx3_cam->clk);
rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
- dev_dbg(icd->dev.parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+ dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
if (rate)
clk_set_rate(mx3_cam->clk, rate);
}
@@ -507,7 +518,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
/* Called with .video_lock held */
static int mx3_camera_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
if (mx3_cam->icd)
@@ -515,9 +526,10 @@ static int mx3_camera_add_device(struct soc_camera_device *icd)
mx3_camera_activate(mx3_cam, icd);
+ mx3_cam->buf_total = 0;
mx3_cam->icd = icd;
- dev_info(icd->dev.parent, "MX3 Camera driver attached to camera %d\n",
+ dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
icd->devnum);
return 0;
@@ -526,7 +538,7 @@ static int mx3_camera_add_device(struct soc_camera_device *icd)
/* Called with .video_lock held */
static void mx3_camera_remove_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
@@ -541,7 +553,7 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
mx3_cam->icd = NULL;
- dev_info(icd->dev.parent, "MX3 Camera driver detached from camera %d\n",
+ dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
icd->devnum);
}
@@ -549,58 +561,27 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
unsigned char buswidth, unsigned long *flags)
{
/*
+ * If requested data width is supported by the platform, use it or any
+ * possible lower value - i.MX31 is smart enough to shift bits
+ */
+ if (buswidth > fls(mx3_cam->width_flags))
+ return -EINVAL;
+
+ /*
* Platform specified synchronization and pixel clock polarities are
* only a recommendation and are only used during probing. MX3x
* camera interface only works in master mode, i.e., uses HSYNC and
* VSYNC signals from the sensor
*/
- *flags = SOCAM_MASTER |
- SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_DATA_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_LOW;
-
- /*
- * If requested data width is supported by the platform, use it or any
- * possible lower value - i.MX31 is smart enough to schift bits
- */
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
- *flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 |
- SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
- else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
- *flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 |
- SOCAM_DATAWIDTH_4;
- else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
- *flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
- else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
- *flags |= SOCAM_DATAWIDTH_4;
-
- switch (buswidth) {
- case 15:
- if (!(*flags & SOCAM_DATAWIDTH_15))
- return -EINVAL;
- break;
- case 10:
- if (!(*flags & SOCAM_DATAWIDTH_10))
- return -EINVAL;
- break;
- case 8:
- if (!(*flags & SOCAM_DATAWIDTH_8))
- return -EINVAL;
- break;
- case 4:
- if (!(*flags & SOCAM_DATAWIDTH_4))
- return -EINVAL;
- break;
- default:
- dev_warn(mx3_cam->soc_host.v4l2_dev.dev,
- "Unsupported bus width %d\n", buswidth);
- return -EINVAL;
- }
+ *flags = V4L2_MBUS_MASTER |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_LOW;
return 0;
}
@@ -608,25 +589,33 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
const unsigned int depth)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- unsigned long bus_flags, camera_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
int ret = test_platform_param(mx3_cam, depth, &bus_flags);
- dev_dbg(icd->dev.parent, "request bus width %d bit: %d\n", depth, ret);
+ dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
if (ret < 0)
return ret;
- camera_flags = icd->ops->query_bus_param(icd);
-
- ret = soc_camera_bus_param_compatible(camera_flags, bus_flags);
- if (ret < 0)
- dev_warn(icd->dev.parent,
- "Flags incompatible: camera %lx, host %lx\n",
- camera_flags, bus_flags);
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
- return ret;
+ return 0;
}
static bool chan_filter(struct dma_chan *chan, void *arg)
@@ -676,7 +665,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
struct soc_camera_format_xlate *xlate)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
int formats = 0, ret;
enum v4l2_mbus_pixelcode code;
const struct soc_mbus_pixelfmt *fmt;
@@ -688,7 +677,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
fmt = soc_mbus_get_fmtdesc(code);
if (!fmt) {
- dev_warn(icd->dev.parent,
+ dev_warn(icd->parent,
"Unsupported format code #%u: %d\n", idx, code);
return 0;
}
@@ -816,7 +805,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd,
struct v4l2_crop *a)
{
struct v4l2_rect *rect = &a->c;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct v4l2_mbus_framefmt mf;
@@ -849,7 +838,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd,
configure_geometry(mx3_cam, mf.width, mf.height,
icd->current_fmt->host_fmt);
- dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n",
+ dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
mf.width, mf.height);
icd->user_width = mf.width;
@@ -861,7 +850,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd,
static int mx3_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
@@ -871,13 +860,13 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
- dev_warn(icd->dev.parent, "Format %x not found\n",
+ dev_warn(icd->parent, "Format %x not found\n",
pix->pixelformat);
return -EINVAL;
}
stride_align(&pix->width);
- dev_dbg(icd->dev.parent, "Set format %dx%d\n", pix->width, pix->height);
+ dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height);
/*
* Might have to perform a complete interface initialisation like in
@@ -913,13 +902,7 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
pix->colorspace = mf.colorspace;
icd->current_fmt = xlate;
- pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
- xlate->host_fmt);
- if (pix->bytesperline < 0)
- return pix->bytesperline;
- pix->sizeimage = pix->height * pix->bytesperline;
-
- dev_dbg(icd->dev.parent, "Sensor set %dx%d\n", pix->width, pix->height);
+ dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height);
return ret;
}
@@ -936,7 +919,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (pixfmt && !xlate) {
- dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
return -EINVAL;
}
@@ -946,12 +929,6 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd,
if (pix->width > 4096)
pix->width = 4096;
- pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
- xlate->host_fmt);
- if (pix->bytesperline < 0)
- return pix->bytesperline;
- pix->sizeimage = pix->height * pix->bytesperline;
-
/* limit to sensor capabilities */
mf.width = pix->width;
mf.height = pix->height;
@@ -974,7 +951,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd,
case V4L2_FIELD_NONE:
break;
default:
- dev_err(icd->dev.parent, "Field type %d unsupported.\n",
+ dev_err(icd->parent, "Field type %d unsupported.\n",
mf.field);
ret = -EINVAL;
}
@@ -1000,7 +977,6 @@ static int mx3_camera_querycap(struct soc_camera_host *ici,
{
/* cap->name is set by the firendly caller:-> */
strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
- cap->version = KERNEL_VERSION(0, 2, 2);
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
@@ -1008,97 +984,92 @@ static int mx3_camera_querycap(struct soc_camera_host *ici,
static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- unsigned long bus_flags, camera_flags, common_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
u32 dw, sens_conf;
const struct soc_mbus_pixelfmt *fmt;
int buswidth;
int ret;
const struct soc_camera_format_xlate *xlate;
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
if (!fmt)
return -EINVAL;
- buswidth = fmt->bits_per_sample;
- ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
-
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
dev_warn(dev, "Format %x not found\n", pixfmt);
return -EINVAL;
}
+ buswidth = fmt->bits_per_sample;
+ ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
+
dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
if (ret < 0)
return ret;
- camera_flags = icd->ops->query_bus_param(icd);
-
- common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
- dev_dbg(dev, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n",
- camera_flags, bus_flags, common_flags);
- if (!common_flags) {
- dev_dbg(dev, "no common flags");
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = bus_flags;
}
+ dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
+ cfg.flags, bus_flags, common_flags);
+
/* Make choices, based on platform preferences */
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) &&
- (common_flags & SOCAM_DATA_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
if (mx3_cam->platform_flags & MX3_CAMERA_DP)
- common_flags &= ~SOCAM_DATA_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_DATA_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
}
- /*
- * Make the camera work in widest common mode, we'll take care of
- * the rest
- */
- if (common_flags & SOCAM_DATAWIDTH_15)
- common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
- SOCAM_DATAWIDTH_15;
- else if (common_flags & SOCAM_DATAWIDTH_10)
- common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
- SOCAM_DATAWIDTH_10;
- else if (common_flags & SOCAM_DATAWIDTH_8)
- common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
- SOCAM_DATAWIDTH_8;
- else
- common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
- SOCAM_DATAWIDTH_4;
-
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0) {
- dev_dbg(dev, "camera set_bus_param(%lx) returned %d\n",
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
common_flags, ret);
return ret;
}
@@ -1122,13 +1093,13 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
/* This has been set in mx3_camera_activate(), but we clear it above */
sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
- if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
- if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
- if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
- if (common_flags & SOCAM_DATA_ACTIVE_LOW)
+ if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
/* Just do what we're asked to do */
@@ -1213,6 +1184,14 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev)
"data widths, using default 8 bit\n");
mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
}
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
+ mx3_cam->width_flags = 1 << 3;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ mx3_cam->width_flags |= 1 << 7;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ mx3_cam->width_flags |= 1 << 9;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ mx3_cam->width_flags |= 1 << 14;
mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
if (!mx3_cam->mclk) {
@@ -1295,8 +1274,6 @@ static int __devexit mx3_camera_remove(struct platform_device *pdev)
dmaengine_put();
- dev_info(&pdev->dev, "i.MX3x Camera driver unloaded\n");
-
return 0;
}
@@ -1325,4 +1302,5 @@ module_exit(mx3_camera_exit);
MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.2.3");
MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index 0b3850023505..2e4131748438 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -21,12 +21,15 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define DEBUG_VARIABLE debug
#include <media/saa7146_vv.h>
#include <media/tuner.h>
#include <media/v4l2-common.h>
#include <media/saa7115.h>
+#include <linux/module.h>
#include "mxb.h"
#include "tea6415c.h"
@@ -171,7 +174,7 @@ static int mxb_probe(struct saa7146_dev *dev)
mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
if (mxb == NULL) {
- DEB_D(("not enough kernel memory.\n"));
+ DEB_D("not enough kernel memory\n");
return -ENOMEM;
}
@@ -179,7 +182,7 @@ static int mxb_probe(struct saa7146_dev *dev)
saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if (i2c_add_adapter(&mxb->i2c_adapter) < 0) {
- DEB_S(("cannot register i2c-device. skipping.\n"));
+ DEB_S("cannot register i2c-device. skipping.\n");
kfree(mxb);
return -EFAULT;
}
@@ -200,7 +203,7 @@ static int mxb_probe(struct saa7146_dev *dev)
/* check if all devices are present */
if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c ||
!mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) {
- printk("mxb: did not find all i2c devices. aborting\n");
+ pr_err("did not find all i2c devices. aborting\n");
i2c_del_adapter(&mxb->i2c_adapter);
kfree(mxb);
return -ENODEV;
@@ -346,11 +349,11 @@ static int mxb_init_done(struct saa7146_dev* dev)
msg.buf = &mxb_saa7740_init[i].data[0];
err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
if (err != 1) {
- DEB_D(("failed to initialize 'sound arena module'.\n"));
+ DEB_D("failed to initialize 'sound arena module'\n");
goto err;
}
}
- INFO(("'sound arena module' detected.\n"));
+ pr_info("'sound arena module' detected\n");
}
err:
/* the rest for saa7146: you should definitely set some basic values
@@ -390,7 +393,7 @@ static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *
for (i = MAXCONTROLS - 1; i >= 0; i--) {
if (mxb_controls[i].id == qc->id) {
*qc = mxb_controls[i];
- DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
+ DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id);
return 0;
}
}
@@ -413,11 +416,11 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
if (vc->id == V4L2_CID_AUDIO_MUTE) {
vc->value = mxb->cur_mute;
- DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
+ DEB_D("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value);
return 0;
}
- DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
+ DEB_EE("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value);
return 0;
}
@@ -440,14 +443,14 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
/* switch the audio-source */
tea6420_route_line(mxb, vc->value ? 6 :
video_audio_connect[mxb->cur_input]);
- DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value));
+ DEB_EE("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d\n", vc->value);
}
return 0;
}
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
{
- DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
if (i->index >= MXB_INPUTS)
return -EINVAL;
memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
@@ -460,7 +463,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
struct mxb *mxb = (struct mxb *)dev->ext_priv;
*i = mxb->cur_input;
- DEB_EE(("VIDIOC_G_INPUT %d.\n", *i));
+ DEB_EE("VIDIOC_G_INPUT %d\n", *i);
return 0;
}
@@ -471,7 +474,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
int err = 0;
int i = 0;
- DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
+ DEB_EE("VIDIOC_S_INPUT %d\n", input);
if (input >= MXB_INPUTS)
return -EINVAL;
@@ -514,7 +517,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
/* switch video in saa7111a */
if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0))
- printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a.\n");
+ pr_err("VIDIOC_S_INPUT: could not address saa7111a\n");
/* switch the audio-source only if necessary */
if (0 == mxb->cur_mute)
@@ -529,11 +532,12 @@ static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
struct mxb *mxb = (struct mxb *)dev->ext_priv;
if (t->index) {
- DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index));
+ DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n",
+ t->index);
return -EINVAL;
}
- DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index));
+ DEB_EE("VIDIOC_G_TUNER: %d\n", t->index);
memset(t, 0, sizeof(*t));
strlcpy(t->name, "TV Tuner", sizeof(t->name));
@@ -550,7 +554,8 @@ static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
struct mxb *mxb = (struct mxb *)dev->ext_priv;
if (t->index) {
- DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n", t->index));
+ DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n",
+ t->index);
return -EINVAL;
}
@@ -564,14 +569,14 @@ static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency
struct mxb *mxb = (struct mxb *)dev->ext_priv;
if (mxb->cur_input) {
- DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
- mxb->cur_input));
+ DEB_D("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
+ mxb->cur_input);
return -EINVAL;
}
*f = mxb->cur_freq;
- DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency));
+ DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency);
return 0;
}
@@ -588,12 +593,13 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency
return -EINVAL;
if (mxb->cur_input) {
- DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input));
+ DEB_D("VIDIOC_S_FREQ: channel %d does not have a tuner!\n",
+ mxb->cur_input);
return -EINVAL;
}
mxb->cur_freq = *f;
- DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency));
+ DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency);
/* tune in desired frequency */
tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
@@ -612,18 +618,18 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
struct mxb *mxb = (struct mxb *)dev->ext_priv;
if (a->index > MXB_INPUTS) {
- DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index));
+ DEB_D("VIDIOC_G_AUDIO %d out of range\n", a->index);
return -EINVAL;
}
- DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index));
+ DEB_EE("VIDIOC_G_AUDIO %d\n", a->index);
memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
return 0;
}
static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
{
- DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index));
+ DEB_D("VIDIOC_S_AUDIO %d\n", a->index);
return 0;
}
@@ -655,11 +661,11 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio,
int i = *(int *)arg;
if (i < 0 || i >= MXB_AUDIOS) {
- DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n", i));
+ DEB_D("invalid argument to MXB_S_AUDIO_CD: i:%d\n", i);
return -EINVAL;
}
- DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n", i));
+ DEB_EE("MXB_S_AUDIO_CD: i:%d\n", i);
tea6420_route_cd(mxb, i);
return 0;
@@ -669,17 +675,18 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio,
int i = *(int *)arg;
if (i < 0 || i >= MXB_AUDIOS) {
- DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n", i));
+ DEB_D("invalid argument to MXB_S_AUDIO_LINE: i:%d\n",
+ i);
return -EINVAL;
}
- DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n", i));
+ DEB_EE("MXB_S_AUDIO_LINE: i:%d\n", i);
tea6420_route_line(mxb, i);
return 0;
}
default:
/*
- DEB2(printk("does not handle this ioctl.\n"));
+ DEB2(pr_err("does not handle this ioctl\n"));
*/
return -ENOIOCTLCMD;
}
@@ -693,7 +700,7 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data
{
struct mxb *mxb;
- DEB_EE(("dev:%p\n", dev));
+ DEB_EE("dev:%p\n", dev);
saa7146_vv_init(dev, &vv_data);
if (mxb_probe(dev)) {
@@ -720,7 +727,7 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data
#endif
vv_data.ops.vidioc_default = vidioc_default;
if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
- ERR(("cannot register capture v4l2 device. skipping.\n"));
+ ERR("cannot register capture v4l2 device. skipping.\n");
saa7146_vv_release(dev);
return -1;
}
@@ -728,11 +735,11 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data
/* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
if (MXB_BOARD_CAN_DO_VBI(dev)) {
if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
- ERR(("cannot register vbi v4l2 device. skipping.\n"));
+ ERR("cannot register vbi v4l2 device. skipping.\n");
}
}
- printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num);
+ pr_info("found Multimedia eXtension Board #%d\n", mxb_num);
mxb_num++;
mxb_init_done(dev);
@@ -743,7 +750,7 @@ static int mxb_detach(struct saa7146_dev *dev)
{
struct mxb *mxb = (struct mxb *)dev->ext_priv;
- DEB_EE(("dev:%p\n", dev));
+ DEB_EE("dev:%p\n", dev);
saa7146_unregister_device(&mxb->video_dev,dev);
if (MXB_BOARD_CAN_DO_VBI(dev))
@@ -765,7 +772,7 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
if (V4L2_STD_PAL_I == standard->id) {
v4l2_std_id std = V4L2_STD_PAL_I;
- DEB_D(("VIDIOC_S_STD: setting mxb for PAL_I.\n"));
+ DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n");
/* set the 7146 gpio register -- I don't know what this does exactly */
saa7146_write(dev, GPIO_CTRL, 0x00404050);
/* unset the 7111 gpio register -- I don't know what this does exactly */
@@ -774,7 +781,7 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
} else {
v4l2_std_id std = V4L2_STD_PAL_BG;
- DEB_D(("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM.\n"));
+ DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n");
/* set the 7146 gpio register -- I don't know what this does exactly */
saa7146_write(dev, GPIO_CTRL, 0x00404050);
/* set the 7111 gpio register -- I don't know what this does exactly */
@@ -852,7 +859,7 @@ static struct saa7146_extension extension = {
static int __init mxb_init_module(void)
{
if (saa7146_register_extension(&extension)) {
- DEB_S(("failed to register extension.\n"));
+ DEB_S("failed to register extension\n");
return -ENODEV;
}
diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c
index 35f722a88f76..50838bf84204 100644
--- a/drivers/media/video/noon010pc30.c
+++ b/drivers/media/video/noon010pc30.c
@@ -1,7 +1,7 @@
/*
* Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP
*
- * Copyright (C) 2010 Samsung Electronics
+ * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd.
* Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
*
* Initial register configuration based on a driver authored by
@@ -10,7 +10,7 @@
* 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 vergsion.
+ * (at your option) any later version.
*/
#include <linux/delay.h>
@@ -21,6 +21,7 @@
#include <media/noon010pc30.h>
#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
+#include <linux/module.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
@@ -131,17 +132,23 @@ static const char * const noon010_supply_name[] = {
struct noon010_info {
struct v4l2_subdev sd;
+ struct media_pad pad;
struct v4l2_ctrl_handler hdl;
- const struct noon010pc30_platform_data *pdata;
+ struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES];
+ u32 gpio_nreset;
+ u32 gpio_nstby;
+
+ /* Protects the struct members below */
+ struct mutex lock;
+
const struct noon010_format *curr_fmt;
const struct noon010_frmsize *curr_win;
+ unsigned int apply_new_cfg:1;
+ unsigned int streaming:1;
unsigned int hflip:1;
unsigned int vflip:1;
unsigned int power:1;
u8 i2c_reg_page;
- struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES];
- u32 gpio_nreset;
- u32 gpio_nstby;
};
struct i2c_regval {
@@ -292,8 +299,10 @@ static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep)
u8 reg = sleep ? 0xF1 : 0xF0;
int ret = 0;
- if (reset)
+ if (reset) {
ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02);
+ udelay(20);
+ }
if (!ret) {
ret = cam_i2c_write(sd, POWER_CTRL_REG, reg);
if (reset && !ret)
@@ -313,6 +322,7 @@ static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on)
return ret;
}
+/* Called with struct noon010_info.lock mutex held */
static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip)
{
struct noon010_info *info = to_noon010(sd);
@@ -340,21 +350,18 @@ static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip)
static int noon010_set_params(struct v4l2_subdev *sd)
{
struct noon010_info *info = to_noon010(sd);
- int ret;
- if (!info->curr_win)
- return -EINVAL;
-
- ret = cam_i2c_write(sd, VDO_CTL_REG(0), info->curr_win->vid_ctl1);
-
- if (!ret && info->curr_fmt)
- ret = cam_i2c_write(sd, ISP_CTL_REG(0),
- info->curr_fmt->ispctl1_reg);
- return ret;
+ int ret = cam_i2c_write(sd, VDO_CTL_REG(0),
+ info->curr_win->vid_ctl1);
+ if (ret)
+ return ret;
+ return cam_i2c_write(sd, ISP_CTL_REG(0),
+ info->curr_fmt->ispctl1_reg);
}
/* Find nearest matching image pixel size. */
-static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf)
+static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf,
+ const struct noon010_frmsize **size)
{
unsigned int min_err = ~0;
int i = ARRAY_SIZE(noon010_sizes);
@@ -374,11 +381,14 @@ static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf)
if (match) {
mf->width = match->width;
mf->height = match->height;
+ if (size)
+ *size = match;
return 0;
}
return -EINVAL;
}
+/* Called with info.lock mutex held */
static int power_enable(struct noon010_info *info)
{
int ret;
@@ -419,6 +429,7 @@ static int power_enable(struct noon010_info *info)
return 0;
}
+/* Called with info.lock mutex held */
static int power_disable(struct noon010_info *info)
{
int ret;
@@ -448,147 +459,175 @@ static int power_disable(struct noon010_info *info)
static int noon010_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
+ struct noon010_info *info = to_noon010(sd);
+ int ret = 0;
v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n",
__func__, ctrl->id, ctrl->val);
+ mutex_lock(&info->lock);
+ /*
+ * If the device is not powered up by the host driver do
+ * not apply any controls to H/W at this time. Instead
+ * the controls will be restored right after power-up.
+ */
+ if (!info->power)
+ goto unlock;
+
switch (ctrl->id) {
case V4L2_CID_AUTO_WHITE_BALANCE:
- return noon010_enable_autowhitebalance(sd, ctrl->val);
+ ret = noon010_enable_autowhitebalance(sd, ctrl->val);
+ break;
case V4L2_CID_BLUE_BALANCE:
- return cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val);
+ ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val);
+ break;
case V4L2_CID_RED_BALANCE:
- return cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val);
+ ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val);
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
+unlock:
+ mutex_unlock(&info->lock);
+ return ret;
}
-static int noon010_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+static int noon010_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
{
- if (!code || index >= ARRAY_SIZE(noon010_formats))
+ if (code->index >= ARRAY_SIZE(noon010_formats))
return -EINVAL;
- *code = noon010_formats[index].code;
+ code->code = noon010_formats[code->index].code;
return 0;
}
-static int noon010_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
{
struct noon010_info *info = to_noon010(sd);
- int ret;
-
- if (!mf)
- return -EINVAL;
+ struct v4l2_mbus_framefmt *mf;
- if (!info->curr_win || !info->curr_fmt) {
- ret = noon010_set_params(sd);
- if (ret)
- return ret;
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ if (fh) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ fmt->format = *mf;
+ }
+ return 0;
}
+ mf = &fmt->format;
- mf->width = info->curr_win->width;
- mf->height = info->curr_win->height;
- mf->code = info->curr_fmt->code;
- mf->colorspace = info->curr_fmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
+ mutex_lock(&info->lock);
+ mf->width = info->curr_win->width;
+ mf->height = info->curr_win->height;
+ mf->code = info->curr_fmt->code;
+ mf->colorspace = info->curr_fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+ mutex_unlock(&info->lock);
return 0;
}
/* Return nearest media bus frame format. */
-static const struct noon010_format *try_fmt(struct v4l2_subdev *sd,
+static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
int i = ARRAY_SIZE(noon010_formats);
- noon010_try_frame_size(mf);
-
- while (i--)
+ while (--i)
if (mf->code == noon010_formats[i].code)
break;
-
mf->code = noon010_formats[i].code;
return &noon010_formats[i];
}
-static int noon010_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- if (!sd || !mf)
- return -EINVAL;
-
- try_fmt(sd, mf);
- return 0;
-}
-
-static int noon010_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
+static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
{
struct noon010_info *info = to_noon010(sd);
+ const struct noon010_frmsize *size = NULL;
+ const struct noon010_format *nf;
+ struct v4l2_mbus_framefmt *mf;
+ int ret = 0;
- if (!sd || !mf)
- return -EINVAL;
-
- info->curr_fmt = try_fmt(sd, mf);
+ nf = noon010_try_fmt(sd, &fmt->format);
+ noon010_try_frame_size(&fmt->format, &size);
+ fmt->format.colorspace = V4L2_COLORSPACE_JPEG;
- return noon010_set_params(sd);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ if (fh) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ *mf = fmt->format;
+ }
+ return 0;
+ }
+ mutex_lock(&info->lock);
+ if (!info->streaming) {
+ info->apply_new_cfg = 1;
+ info->curr_fmt = nf;
+ info->curr_win = size;
+ } else {
+ ret = -EBUSY;
+ }
+ mutex_unlock(&info->lock);
+ return ret;
}
+/* Called with struct noon010_info.lock mutex held */
static int noon010_base_config(struct v4l2_subdev *sd)
{
- struct noon010_info *info = to_noon010(sd);
- int ret;
-
- ret = noon010_bulk_write_reg(sd, noon010_base_regs);
- if (!ret) {
- info->curr_fmt = &noon010_formats[0];
- info->curr_win = &noon010_sizes[0];
+ int ret = noon010_bulk_write_reg(sd, noon010_base_regs);
+ if (!ret)
ret = noon010_set_params(sd);
- }
if (!ret)
ret = noon010_set_flip(sd, 1, 0);
- if (!ret)
- ret = noon010_power_ctrl(sd, false, false);
- /* sync the handler and the registers state */
- v4l2_ctrl_handler_setup(&to_noon010(sd)->hdl);
return ret;
}
static int noon010_s_power(struct v4l2_subdev *sd, int on)
{
struct noon010_info *info = to_noon010(sd);
- const struct noon010pc30_platform_data *pdata = info->pdata;
- int ret = 0;
-
- if (WARN(pdata == NULL, "No platform data!\n"))
- return -ENOMEM;
+ int ret;
+ mutex_lock(&info->lock);
if (on) {
ret = power_enable(info);
- if (ret)
- return ret;
- ret = noon010_base_config(sd);
+ if (!ret)
+ ret = noon010_base_config(sd);
} else {
noon010_power_ctrl(sd, false, true);
ret = power_disable(info);
- info->curr_win = NULL;
- info->curr_fmt = NULL;
}
+ mutex_unlock(&info->lock);
+
+ /* Restore the controls state */
+ if (!ret && on)
+ ret = v4l2_ctrl_handler_setup(&info->hdl);
return ret;
}
-static int noon010_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
+static int noon010_s_stream(struct v4l2_subdev *sd, int on)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct noon010_info *info = to_noon010(sd);
+ int ret = 0;
- return v4l2_chip_ident_i2c_client(client, chip,
- V4L2_IDENT_NOON010PC30, 0);
+ mutex_lock(&info->lock);
+ if (!info->streaming != !on) {
+ ret = noon010_power_ctrl(sd, false, !on);
+ if (!ret)
+ info->streaming = on;
+ }
+ if (!ret && on && info->apply_new_cfg) {
+ ret = noon010_set_params(sd);
+ if (!ret)
+ info->apply_new_cfg = 0;
+ }
+ mutex_unlock(&info->lock);
+ return ret;
}
static int noon010_log_status(struct v4l2_subdev *sd)
@@ -599,12 +638,27 @@ static int noon010_log_status(struct v4l2_subdev *sd)
return 0;
}
+static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0);
+
+ mf->width = noon010_sizes[0].width;
+ mf->height = noon010_sizes[0].height;
+ mf->code = noon010_formats[0].code;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mf->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = {
+ .open = noon010_open,
+};
+
static const struct v4l2_ctrl_ops noon010_ctrl_ops = {
.s_ctrl = noon010_s_ctrl,
};
static const struct v4l2_subdev_core_ops noon010_core_ops = {
- .g_chip_ident = noon010_g_chip_ident,
.s_power = noon010_s_power,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
@@ -616,15 +670,19 @@ static const struct v4l2_subdev_core_ops noon010_core_ops = {
.log_status = noon010_log_status,
};
-static const struct v4l2_subdev_video_ops noon010_video_ops = {
- .g_mbus_fmt = noon010_g_fmt,
- .s_mbus_fmt = noon010_s_fmt,
- .try_mbus_fmt = noon010_try_fmt,
- .enum_mbus_fmt = noon010_enum_fmt,
+static struct v4l2_subdev_pad_ops noon010_pad_ops = {
+ .enum_mbus_code = noon010_enum_mbus_code,
+ .get_fmt = noon010_get_fmt,
+ .set_fmt = noon010_set_fmt,
+};
+
+static struct v4l2_subdev_video_ops noon010_video_ops = {
+ .s_stream = noon010_s_stream,
};
static const struct v4l2_subdev_ops noon010_ops = {
.core = &noon010_core_ops,
+ .pad = &noon010_pad_ops,
.video = &noon010_video_ops,
};
@@ -665,10 +723,14 @@ static int noon010_probe(struct i2c_client *client,
if (!info)
return -ENOMEM;
+ mutex_init(&info->lock);
sd = &info->sd;
strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
v4l2_i2c_subdev_init(sd, client, &noon010_ops);
+ sd->internal_ops = &noon010_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
v4l2_ctrl_handler_init(&info->hdl, 3);
v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops,
@@ -684,10 +746,11 @@ static int noon010_probe(struct i2c_client *client,
if (ret)
goto np_err;
- info->pdata = client->dev.platform_data;
info->i2c_reg_page = -1;
info->gpio_nreset = -EINVAL;
info->gpio_nstby = -EINVAL;
+ info->curr_fmt = &noon010_formats[0];
+ info->curr_win = &noon010_sizes[0];
if (gpio_is_valid(pdata->gpio_nreset)) {
ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST");
@@ -719,11 +782,17 @@ static int noon010_probe(struct i2c_client *client,
if (ret)
goto np_reg_err;
+ info->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+ if (ret < 0)
+ goto np_me_err;
+
ret = noon010_detect(client, info);
if (!ret)
return 0;
- /* the sensor detection failed */
+np_me_err:
regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply);
np_reg_err:
if (gpio_is_valid(info->gpio_nstby))
@@ -754,6 +823,7 @@ static int noon010_remove(struct i2c_client *client)
if (gpio_is_valid(info->gpio_nstby))
gpio_free(info->gpio_nstby);
+ media_entity_cleanup(&sd->entity);
kfree(info);
return 0;
}
diff --git a/drivers/media/video/omap/Kconfig b/drivers/media/video/omap/Kconfig
index e63233fd2aaa..390ab094f9f2 100644
--- a/drivers/media/video/omap/Kconfig
+++ b/drivers/media/video/omap/Kconfig
@@ -1,11 +1,14 @@
+config VIDEO_OMAP2_VOUT_VRFB
+ bool
+
config VIDEO_OMAP2_VOUT
tristate "OMAP2/OMAP3 V4L2-Display driver"
depends on ARCH_OMAP2 || ARCH_OMAP3
select VIDEOBUF_GEN
select VIDEOBUF_DMA_CONTIG
select OMAP2_DSS
- select OMAP2_VRAM
- select OMAP2_VRFB
+ select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
+ select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB
default n
---help---
V4L2 Display driver support for OMAP2/3 based boards.
diff --git a/drivers/media/video/omap/Makefile b/drivers/media/video/omap/Makefile
index b28788070ae1..fc410b438f7d 100644
--- a/drivers/media/video/omap/Makefile
+++ b/drivers/media/video/omap/Makefile
@@ -4,4 +4,5 @@
# OMAP2/3 Display driver
omap-vout-y := omap_vout.o omap_voutlib.o
+omap-vout-$(CONFIG_VIDEO_OMAP2_VOUT_VRFB) += omap_vout_vrfb.o
obj-$(CONFIG_VIDEO_OMAP2_VOUT) += omap-vout.o
diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c
index 4d07c5844402..9c5c19f142de 100644
--- a/drivers/media/video/omap/omap_vout.c
+++ b/drivers/media/video/omap/omap_vout.c
@@ -35,28 +35,26 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
#include <linux/irq.h>
#include <linux/videodev2.h>
-#include <linux/slab.h>
+#include <linux/dma-mapping.h>
#include <media/videobuf-dma-contig.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <plat/dma.h>
-#include <plat/vram.h>
#include <plat/vrfb.h>
#include <video/omapdss.h>
#include "omap_voutlib.h"
#include "omap_voutdef.h"
+#include "omap_vout_vrfb.h"
MODULE_AUTHOR("Texas Instruments");
MODULE_DESCRIPTION("OMAP Video for Linux Video out driver");
MODULE_LICENSE("GPL");
-
/* Driver Configuration macros */
#define VOUT_NAME "omap_vout"
@@ -65,31 +63,6 @@ enum omap_vout_channels {
OMAP_VIDEO2,
};
-enum dma_channel_state {
- DMA_CHAN_NOT_ALLOTED,
- DMA_CHAN_ALLOTED,
-};
-
-#define QQVGA_WIDTH 160
-#define QQVGA_HEIGHT 120
-
-/* Max Resolution supported by the driver */
-#define VID_MAX_WIDTH 1280 /* Largest width */
-#define VID_MAX_HEIGHT 720 /* Largest height */
-
-/* Mimimum requirement is 2x2 for DSS */
-#define VID_MIN_WIDTH 2
-#define VID_MIN_HEIGHT 2
-
-/* 2048 x 2048 is max res supported by OMAP display controller */
-#define MAX_PIXELS_PER_LINE 2048
-
-#define VRFB_TX_TIMEOUT 1000
-#define VRFB_NUM_BUFS 4
-
-/* Max buffer size tobe allocated during init */
-#define OMAP_VOUT_MAX_BUF_SIZE (VID_MAX_WIDTH*VID_MAX_HEIGHT*4)
-
static struct videobuf_queue_ops video_vbq_ops;
/* Variables configurable through module params*/
static u32 video1_numbuffers = 3;
@@ -129,7 +102,7 @@ module_param(debug, bool, S_IRUGO);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* list of image formats supported by OMAP2 video pipelines */
-const static struct v4l2_fmtdesc omap_formats[] = {
+static const struct v4l2_fmtdesc omap_formats[] = {
{
/* Note: V4L2 defines RGB565 as:
*
@@ -172,84 +145,6 @@ const static struct v4l2_fmtdesc omap_formats[] = {
#define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats))
/*
- * Allocate buffers
- */
-static unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr)
-{
- u32 order, size;
- unsigned long virt_addr, addr;
-
- size = PAGE_ALIGN(buf_size);
- order = get_order(size);
- virt_addr = __get_free_pages(GFP_KERNEL | GFP_DMA, order);
- addr = virt_addr;
-
- if (virt_addr) {
- while (size > 0) {
- SetPageReserved(virt_to_page(addr));
- addr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- }
- *phys_addr = (u32) virt_to_phys((void *) virt_addr);
- return virt_addr;
-}
-
-/*
- * Free buffers
- */
-static void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size)
-{
- u32 order, size;
- unsigned long addr = virtaddr;
-
- size = PAGE_ALIGN(buf_size);
- order = get_order(size);
-
- while (size > 0) {
- ClearPageReserved(virt_to_page(addr));
- addr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- free_pages((unsigned long) virtaddr, order);
-}
-
-/*
- * Function for allocating video buffers
- */
-static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout,
- unsigned int *count, int startindex)
-{
- int i, j;
-
- for (i = 0; i < *count; i++) {
- if (!vout->smsshado_virt_addr[i]) {
- vout->smsshado_virt_addr[i] =
- omap_vout_alloc_buffer(vout->smsshado_size,
- &vout->smsshado_phy_addr[i]);
- }
- if (!vout->smsshado_virt_addr[i] && startindex != -1) {
- if (V4L2_MEMORY_MMAP == vout->memory && i >= startindex)
- break;
- }
- if (!vout->smsshado_virt_addr[i]) {
- for (j = 0; j < i; j++) {
- omap_vout_free_buffer(
- vout->smsshado_virt_addr[j],
- vout->smsshado_size);
- vout->smsshado_virt_addr[j] = 0;
- vout->smsshado_phy_addr[j] = 0;
- }
- *count = 0;
- return -ENOMEM;
- }
- memset((void *) vout->smsshado_virt_addr[i], 0,
- vout->smsshado_size);
- }
- return 0;
-}
-
-/*
* Try format
*/
static int omap_vout_try_format(struct v4l2_pix_format *pix)
@@ -342,73 +237,9 @@ static u32 omap_vout_uservirt_to_phys(u32 virtp)
}
/*
- * Wakes up the application once the DMA transfer to VRFB space is completed.
- */
-static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data)
-{
- struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data;
-
- t->tx_status = 1;
- wake_up_interruptible(&t->wait);
-}
-
-/*
- * Release the VRFB context once the module exits
- */
-static void omap_vout_release_vrfb(struct omap_vout_device *vout)
-{
- int i;
-
- for (i = 0; i < VRFB_NUM_BUFS; i++)
- omap_vrfb_release_ctx(&vout->vrfb_context[i]);
-
- if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) {
- vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
- omap_free_dma(vout->vrfb_dma_tx.dma_ch);
- }
-}
-
-/*
- * Return true if rotation is 90 or 270
- */
-static inline int rotate_90_or_270(const struct omap_vout_device *vout)
-{
- return (vout->rotation == dss_rotation_90_degree ||
- vout->rotation == dss_rotation_270_degree);
-}
-
-/*
- * Return true if rotation is enabled
- */
-static inline int rotation_enabled(const struct omap_vout_device *vout)
-{
- return vout->rotation || vout->mirror;
-}
-
-/*
- * Reverse the rotation degree if mirroring is enabled
- */
-static inline int calc_rotation(const struct omap_vout_device *vout)
-{
- if (!vout->mirror)
- return vout->rotation;
-
- switch (vout->rotation) {
- case dss_rotation_90_degree:
- return dss_rotation_270_degree;
- case dss_rotation_270_degree:
- return dss_rotation_90_degree;
- case dss_rotation_180_degree:
- return dss_rotation_0_degree;
- default:
- return dss_rotation_180_degree;
- }
-}
-
-/*
* Free the V4L2 buffers
*/
-static void omap_vout_free_buffers(struct omap_vout_device *vout)
+void omap_vout_free_buffers(struct omap_vout_device *vout)
{
int i, numbuffers;
@@ -425,52 +256,6 @@ static void omap_vout_free_buffers(struct omap_vout_device *vout)
}
/*
- * Free VRFB buffers
- */
-static void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout)
-{
- int j;
-
- for (j = 0; j < VRFB_NUM_BUFS; j++) {
- omap_vout_free_buffer(vout->smsshado_virt_addr[j],
- vout->smsshado_size);
- vout->smsshado_virt_addr[j] = 0;
- vout->smsshado_phy_addr[j] = 0;
- }
-}
-
-/*
- * Allocate the buffers for the VRFB space. Data is copied from V4L2
- * buffers to the VRFB buffers using the DMA engine.
- */
-static int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
- unsigned int *count, unsigned int startindex)
-{
- int i;
- bool yuv_mode;
-
- /* Allocate the VRFB buffers only if the buffers are not
- * allocated during init time.
- */
- if ((rotation_enabled(vout)) && !vout->vrfb_static_allocation)
- if (omap_vout_allocate_vrfb_buffers(vout, count, startindex))
- return -ENOMEM;
-
- if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 ||
- vout->dss_mode == OMAP_DSS_COLOR_UYVY)
- yuv_mode = true;
- else
- yuv_mode = false;
-
- for (i = 0; i < *count; i++)
- omap_vrfb_setup(&vout->vrfb_context[i],
- vout->smsshado_phy_addr[i], vout->pix.width,
- vout->pix.height, vout->bpp, yuv_mode);
-
- return 0;
-}
-
-/*
* Convert V4L2 rotation to DSS rotation
* V4L2 understand 0, 90, 180, 270.
* Convert to 0, 1, 2 and 3 respectively for DSS
@@ -499,124 +284,38 @@ static int v4l2_rot_to_dss_rot(int v4l2_rotation,
return ret;
}
-/*
- * Calculate the buffer offsets from which the streaming should
- * start. This offset calculation is mainly required because of
- * the VRFB 32 pixels alignment with rotation.
- */
static int omap_vout_calculate_offset(struct omap_vout_device *vout)
{
- struct omap_overlay *ovl;
- enum dss_rotation rotation;
struct omapvideo_info *ovid;
- bool mirroring = vout->mirror;
- struct omap_dss_device *cur_display;
struct v4l2_rect *crop = &vout->crop;
struct v4l2_pix_format *pix = &vout->pix;
int *cropped_offset = &vout->cropped_offset;
- int vr_ps = 1, ps = 2, temp_ps = 2;
- int offset = 0, ctop = 0, cleft = 0, line_length = 0;
+ int ps = 2, line_length = 0;
ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
- /* get the display device attached to the overlay */
- if (!ovl->manager || !ovl->manager->device)
- return -1;
- cur_display = ovl->manager->device;
- rotation = calc_rotation(vout);
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ omap_vout_calculate_vrfb_offset(vout);
+ } else {
+ vout->line_length = line_length = pix->width;
- if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
- V4L2_PIX_FMT_UYVY == pix->pixelformat) {
- if (rotation_enabled(vout)) {
- /*
- * ps - Actual pixel size for YUYV/UYVY for
- * VRFB/Mirroring is 4 bytes
- * vr_ps - Virtually pixel size for YUYV/UYVY is
- * 2 bytes
- */
+ if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
+ V4L2_PIX_FMT_UYVY == pix->pixelformat)
+ ps = 2;
+ else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat)
ps = 4;
- vr_ps = 2;
- } else {
- ps = 2; /* otherwise the pixel size is 2 byte */
- }
- } else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) {
- ps = 4;
- } else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) {
- ps = 3;
- }
- vout->ps = ps;
- vout->vr_ps = vr_ps;
-
- if (rotation_enabled(vout)) {
- line_length = MAX_PIXELS_PER_LINE;
- ctop = (pix->height - crop->height) - crop->top;
- cleft = (pix->width - crop->width) - crop->left;
- } else {
- line_length = pix->width;
- }
- vout->line_length = line_length;
- switch (rotation) {
- case dss_rotation_90_degree:
- offset = vout->vrfb_context[0].yoffset *
- vout->vrfb_context[0].bytespp;
- temp_ps = ps / vr_ps;
- if (mirroring == 0) {
- *cropped_offset = offset + line_length *
- temp_ps * cleft + crop->top * temp_ps;
- } else {
- *cropped_offset = offset + line_length * temp_ps *
- cleft + crop->top * temp_ps + (line_length *
- ((crop->width / (vr_ps)) - 1) * ps);
- }
- break;
- case dss_rotation_180_degree:
- offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset *
- vout->vrfb_context[0].bytespp) +
- (vout->vrfb_context[0].xoffset *
- vout->vrfb_context[0].bytespp));
- if (mirroring == 0) {
- *cropped_offset = offset + (line_length * ps * ctop) +
- (cleft / vr_ps) * ps;
+ else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat)
+ ps = 3;
- } else {
- *cropped_offset = offset + (line_length * ps * ctop) +
- (cleft / vr_ps) * ps + (line_length *
- (crop->height - 1) * ps);
- }
- break;
- case dss_rotation_270_degree:
- offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset *
- vout->vrfb_context[0].bytespp;
- temp_ps = ps / vr_ps;
- if (mirroring == 0) {
- *cropped_offset = offset + line_length *
- temp_ps * crop->left + ctop * ps;
- } else {
- *cropped_offset = offset + line_length *
- temp_ps * crop->left + ctop * ps +
- (line_length * ((crop->width / vr_ps) - 1) *
- ps);
- }
- break;
- case dss_rotation_0_degree:
- if (mirroring == 0) {
- *cropped_offset = (line_length * ps) *
- crop->top + (crop->left / vr_ps) * ps;
- } else {
- *cropped_offset = (line_length * ps) *
- crop->top + (crop->left / vr_ps) * ps +
- (line_length * (crop->height - 1) * ps);
- }
- break;
- default:
- *cropped_offset = (line_length * ps * crop->top) /
- vr_ps + (crop->left * ps) / vr_ps +
- ((crop->width / vr_ps) - 1) * ps;
- break;
+ vout->ps = ps;
+
+ *cropped_offset = (line_length * ps) *
+ crop->top + crop->left * ps;
}
+
v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "%s Offset:%x\n",
- __func__, *cropped_offset);
+ __func__, vout->cropped_offset);
+
return 0;
}
@@ -664,7 +363,7 @@ static int video_mode_to_dss_mode(struct omap_vout_device *vout)
/*
* Setup the overlay
*/
-int omapvid_setup_overlay(struct omap_vout_device *vout,
+static int omapvid_setup_overlay(struct omap_vout_device *vout,
struct omap_overlay *ovl, int posx, int posy, int outw,
int outh, u32 addr)
{
@@ -687,7 +386,7 @@ int omapvid_setup_overlay(struct omap_vout_device *vout,
/* Setup the input plane parameters according to
* rotation value selected.
*/
- if (rotate_90_or_270(vout)) {
+ if (is_rotation_90_or_270(vout)) {
cropheight = vout->crop.width;
cropwidth = vout->crop.height;
pixheight = vout->pix.width;
@@ -701,7 +400,6 @@ int omapvid_setup_overlay(struct omap_vout_device *vout,
ovl->get_overlay_info(ovl, &info);
info.paddr = addr;
- info.vaddr = NULL;
info.width = cropwidth;
info.height = cropheight;
info.color_mode = vout->dss_mode;
@@ -711,7 +409,7 @@ int omapvid_setup_overlay(struct omap_vout_device *vout,
info.out_width = outw;
info.out_height = outh;
info.global_alpha = vout->win.global_alpha;
- if (!rotation_enabled(vout)) {
+ if (!is_rotation_enabled(vout)) {
info.rotation = 0;
info.rotation_type = OMAP_DSS_ROT_DMA;
info.screen_width = pixwidth;
@@ -744,7 +442,7 @@ setup_ovl_err:
/*
* Initialize the overlay structure
*/
-int omapvid_init(struct omap_vout_device *vout, u32 addr)
+static int omapvid_init(struct omap_vout_device *vout, u32 addr)
{
int ret = 0, i;
struct v4l2_window *win;
@@ -809,7 +507,7 @@ omapvid_init_err:
/*
* Apply the changes set the go bit of DSS
*/
-int omapvid_apply_changes(struct omap_vout_device *vout)
+static int omapvid_apply_changes(struct omap_vout_device *vout)
{
int i;
struct omap_overlay *ovl;
@@ -825,7 +523,7 @@ int omapvid_apply_changes(struct omap_vout_device *vout)
return 0;
}
-void omap_vout_isr(void *arg, unsigned int irqstatus)
+static void omap_vout_isr(void *arg, unsigned int irqstatus)
{
int ret;
u32 addr, fid;
@@ -848,10 +546,20 @@ void omap_vout_isr(void *arg, unsigned int irqstatus)
spin_lock(&vout->vbq_lock);
do_gettimeofday(&timevalue);
- if (cur_display->type == OMAP_DISPLAY_TYPE_DPI) {
- if (!(irqstatus & DISPC_IRQ_VSYNC))
- goto vout_isr_err;
+ if (cur_display->type != OMAP_DISPLAY_TYPE_VENC) {
+ switch (cur_display->type) {
+ case OMAP_DISPLAY_TYPE_DPI:
+ if (!(irqstatus & (DISPC_IRQ_VSYNC | DISPC_IRQ_VSYNC2)))
+ goto vout_isr_err;
+ break;
+ case OMAP_DISPLAY_TYPE_HDMI:
+ if (!(irqstatus & DISPC_IRQ_EVSYNC_EVEN))
+ goto vout_isr_err;
+ break;
+ default:
+ goto vout_isr_err;
+ }
if (!vout->first_int && (vout->cur_frm != vout->next_frm)) {
vout->cur_frm->ts = timevalue;
vout->cur_frm->state = VIDEOBUF_DONE;
@@ -875,7 +583,7 @@ void omap_vout_isr(void *arg, unsigned int irqstatus)
ret = omapvid_init(vout, addr);
if (ret)
printk(KERN_ERR VOUT_NAME
- "failed to set overlay info\n");
+ "failed to set overlay info\n");
/* Enable the pipeline and set the Go bit */
ret = omapvid_apply_changes(vout);
if (ret)
@@ -954,6 +662,7 @@ static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
int startindex = 0, i, j;
u32 phy_addr = 0, virt_addr = 0;
struct omap_vout_device *vout = q->priv_data;
+ struct omapvideo_info *ovid = &vout->vid_info;
if (!vout)
return -EINVAL;
@@ -966,13 +675,10 @@ static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex)
*count = startindex;
- if ((rotation_enabled(vout)) && *count > VRFB_NUM_BUFS)
- *count = VRFB_NUM_BUFS;
-
- /* If rotation is enabled, allocate memory for VRFB space also */
- if (rotation_enabled(vout))
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
if (omap_vout_vrfb_buffer_setup(vout, count, startindex))
return -ENOMEM;
+ }
if (V4L2_MEMORY_MMAP != vout->memory)
return 0;
@@ -996,8 +702,11 @@ static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
virt_addr = omap_vout_alloc_buffer(vout->buffer_size,
&phy_addr);
if (!virt_addr) {
- if (!rotation_enabled(vout))
+ if (ovid->rotation_type == VOUT_ROT_NONE) {
break;
+ } else {
+ if (!is_rotation_enabled(vout))
+ break;
/* Free the VRFB buffers if no space for V4L2 buffers */
for (j = i; j < *count; j++) {
omap_vout_free_buffer(
@@ -1005,6 +714,7 @@ static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
vout->smsshado_size);
vout->smsshado_virt_addr[j] = 0;
vout->smsshado_phy_addr[j] = 0;
+ }
}
}
vout->buf_virt_addr[i] = virt_addr;
@@ -1017,9 +727,9 @@ static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
/*
* Free the V4L2 buffers additionally allocated than default
- * number of buffers and free all the VRFB buffers
+ * number of buffers
*/
-static void omap_vout_free_allbuffers(struct omap_vout_device *vout)
+static void omap_vout_free_extra_buffers(struct omap_vout_device *vout)
{
int num_buffers = 0, i;
@@ -1034,20 +744,6 @@ static void omap_vout_free_allbuffers(struct omap_vout_device *vout)
vout->buf_virt_addr[i] = 0;
vout->buf_phy_addr[i] = 0;
}
- /* Free the VRFB buffers only if they are allocated
- * during reqbufs. Don't free if init time allocated
- */
- if (!vout->vrfb_static_allocation) {
- for (i = 0; i < VRFB_NUM_BUFS; i++) {
- if (vout->smsshado_virt_addr[i]) {
- omap_vout_free_buffer(
- vout->smsshado_virt_addr[i],
- vout->smsshado_size);
- vout->smsshado_virt_addr[i] = 0;
- vout->smsshado_phy_addr[i] = 0;
- }
- }
- }
vout->buffer_allocated = num_buffers;
}
@@ -1059,16 +755,11 @@ static void omap_vout_free_allbuffers(struct omap_vout_device *vout)
* buffer into VRFB memory space before giving it to the DSS.
*/
static int omap_vout_buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
{
- dma_addr_t dmabuf;
- struct vid_vrfb_dma *tx;
- enum dss_rotation rotation;
struct omap_vout_device *vout = q->priv_data;
- u32 dest_frame_index = 0, src_element_index = 0;
- u32 dest_element_index = 0, src_frame_index = 0;
- u32 elem_count = 0, frame_count = 0, pixsize = 2;
+ struct omapvideo_info *ovid = &vout->vid_info;
if (VIDEOBUF_NEEDS_INIT == vb->state) {
vb->width = vout->pix.width;
@@ -1087,66 +778,24 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q,
vout->queued_buf_addr[vb->i] = (u8 *)
omap_vout_uservirt_to_phys(vb->baddr);
} else {
- vout->queued_buf_addr[vb->i] = (u8 *)vout->buf_phy_addr[vb->i];
- }
+ u32 addr, dma_addr;
+ unsigned long size;
- if (!rotation_enabled(vout))
- return 0;
+ addr = (unsigned long) vout->buf_virt_addr[vb->i];
+ size = (unsigned long) vb->size;
- dmabuf = vout->buf_phy_addr[vb->i];
- /* If rotation is enabled, copy input buffer into VRFB
- * memory space using DMA. We are copying input buffer
- * into VRFB memory space of desired angle and DSS will
- * read image VRFB memory for 0 degree angle
- */
- pixsize = vout->bpp * vout->vrfb_bpp;
- /*
- * DMA transfer in double index mode
- */
+ dma_addr = dma_map_single(vout->vid_dev->v4l2_dev.dev, (void *) addr,
+ size, DMA_TO_DEVICE);
+ if (dma_mapping_error(vout->vid_dev->v4l2_dev.dev, dma_addr))
+ v4l2_err(&vout->vid_dev->v4l2_dev, "dma_map_single failed\n");
- /* Frame index */
- dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) -
- (vout->pix.width * vout->bpp)) + 1;
-
- /* Source and destination parameters */
- src_element_index = 0;
- src_frame_index = 0;
- dest_element_index = 1;
- /* Number of elements per frame */
- elem_count = vout->pix.width * vout->bpp;
- frame_count = vout->pix.height;
- tx = &vout->vrfb_dma_tx;
- tx->tx_status = 0;
- omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32,
- (elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT,
- tx->dev_id, 0x0);
- /* src_port required only for OMAP1 */
- omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- dmabuf, src_element_index, src_frame_index);
- /*set dma source burst mode for VRFB */
- omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
- rotation = calc_rotation(vout);
-
- /* dest_port required only for OMAP1 */
- omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX,
- vout->vrfb_context[vb->i].paddr[0], dest_element_index,
- dest_frame_index);
- /*set dma dest burst mode for VRFB */
- omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
- omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0);
-
- omap_start_dma(tx->dma_ch);
- interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT);
-
- if (tx->tx_status == 0) {
- omap_stop_dma(tx->dma_ch);
- return -EINVAL;
+ vout->queued_buf_addr[vb->i] = (u8 *)vout->buf_phy_addr[vb->i];
}
- /* Store buffers physical address into an array. Addresses
- * from this array will be used to configure DSS */
- vout->queued_buf_addr[vb->i] = (u8 *)
- vout->vrfb_context[vb->i].paddr[rotation];
- return 0;
+
+ if (ovid->rotation_type == VOUT_ROT_VRFB)
+ return omap_vout_prepare_vrfb(vout, vb);
+ else
+ return 0;
}
/*
@@ -1184,6 +833,15 @@ static void omap_vout_buffer_release(struct videobuf_queue *q,
/*
* File operations
*/
+static unsigned int omap_vout_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct omap_vout_device *vout = file->private_data;
+ struct videobuf_queue *q = &vout->vbq;
+
+ return videobuf_poll_stream(file, q, wait);
+}
+
static void omap_vout_vm_open(struct vm_area_struct *vma)
{
struct omap_vout_device *vout = vma->vm_private_data;
@@ -1298,7 +956,15 @@ static int omap_vout_release(struct file *file)
"Unable to apply changes\n");
/* Free all buffers */
- omap_vout_free_allbuffers(vout);
+ omap_vout_free_extra_buffers(vout);
+
+ /* Free the VRFB buffers only if they are allocated
+ * during reqbufs. Don't free if init time allocated
+ */
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ if (!vout->vrfb_static_allocation)
+ omap_vout_free_vrfb_buffers(vout);
+ }
videobuf_mmap_free(q);
/* Even if apply changes fails we should continue
@@ -1307,7 +973,7 @@ static int omap_vout_release(struct file *file)
u32 mask = 0;
mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN |
- DISPC_IRQ_EVSYNC_ODD;
+ DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2;
omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
vout->streaming = 0;
@@ -1383,10 +1049,7 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *fh,
struct v4l2_fmtdesc *fmt)
{
int index = fmt->index;
- enum v4l2_buf_type type = fmt->type;
- fmt->index = index;
- fmt->type = type;
if (index >= NUM_OUTPUT_FORMATS)
return -EINVAL;
@@ -1457,7 +1120,7 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
/* We dont support RGB24-packed mode if vrfb rotation
* is enabled*/
- if ((rotation_enabled(vout)) &&
+ if ((is_rotation_enabled(vout)) &&
f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
ret = -EINVAL;
goto s_fmt_vid_out_exit;
@@ -1465,7 +1128,7 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
/* get the framebuffer parameters */
- if (rotate_90_or_270(vout)) {
+ if (is_rotation_90_or_270(vout)) {
vout->fbuf.fmt.height = timing->x_res;
vout->fbuf.fmt.width = timing->y_res;
} else {
@@ -1510,12 +1173,17 @@ static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh,
{
int ret = 0;
struct omap_vout_device *vout = fh;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
struct v4l2_window *win = &f->fmt.win;
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
ret = omap_vout_try_window(&vout->fbuf, win);
if (!ret) {
- if (vout->vid == OMAP_VIDEO1)
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
win->global_alpha = 255;
else
win->global_alpha = f->fmt.win.global_alpha;
@@ -1539,8 +1207,8 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh,
ret = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win);
if (!ret) {
- /* Video1 plane does not support global alpha */
- if (ovl->id == OMAP_DSS_VIDEO1)
+ /* Video1 plane does not support global alpha on OMAP3 */
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
vout->win.global_alpha = 255;
else
vout->win.global_alpha = f->fmt.win.global_alpha;
@@ -1555,10 +1223,7 @@ static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh,
struct v4l2_fmtdesc *fmt)
{
int index = fmt->index;
- enum v4l2_buf_type type = fmt->type;
- fmt->index = index;
- fmt->type = type;
if (index >= NUM_OUTPUT_FORMATS)
return -EINVAL;
@@ -1645,7 +1310,7 @@ static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
/* get the display device attached to the overlay */
timing = &ovl->manager->device->panel.timings;
- if (rotate_90_or_270(vout)) {
+ if (is_rotation_90_or_270(vout)) {
vout->fbuf.fmt.height = timing->x_res;
vout->fbuf.fmt.width = timing->y_res;
} else {
@@ -1725,9 +1390,17 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
switch (a->id) {
case V4L2_CID_ROTATE:
{
+ struct omapvideo_info *ovid;
int rotation = a->value;
+ ovid = &vout->vid_info;
+
mutex_lock(&vout->lock);
+ if (rotation && ovid->rotation_type == VOUT_ROT_NONE) {
+ mutex_unlock(&vout->lock);
+ ret = -ERANGE;
+ break;
+ }
if (rotation && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
mutex_unlock(&vout->lock);
@@ -1783,6 +1456,11 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
ovl = ovid->overlays[0];
mutex_lock(&vout->lock);
+ if (mirror && ovid->rotation_type == VOUT_ROT_NONE) {
+ mutex_unlock(&vout->lock);
+ ret = -ERANGE;
+ break;
+ }
if (mirror && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
mutex_unlock(&vout->lock);
@@ -1893,7 +1571,7 @@ static int vidioc_qbuf(struct file *file, void *fh,
}
}
- if ((rotation_enabled(vout)) &&
+ if ((is_rotation_enabled(vout)) &&
vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) {
v4l2_warn(&vout->vid_dev->v4l2_dev,
"DMA Channel not allocated for Rotation\n");
@@ -1908,15 +1586,28 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
struct omap_vout_device *vout = fh;
struct videobuf_queue *q = &vout->vbq;
+ int ret;
+ u32 addr;
+ unsigned long size;
+ struct videobuf_buffer *vb;
+
+ vb = q->bufs[b->index];
+
if (!vout->streaming)
return -EINVAL;
if (file->f_flags & O_NONBLOCK)
/* Call videobuf_dqbuf for non blocking mode */
- return videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1);
+ ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1);
else
/* Call videobuf_dqbuf for blocking mode */
- return videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0);
+ ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0);
+
+ addr = (unsigned long) vout->buf_phy_addr[vb->i];
+ size = (unsigned long) vb->size;
+ dma_unmap_single(vout->vid_dev->v4l2_dev.dev, addr,
+ size, DMA_TO_DEVICE);
+ return ret;
}
static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
@@ -1965,7 +1656,8 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i]
+ vout->cropped_offset;
- mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD;
+ mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+ | DISPC_IRQ_VSYNC2;
omap_dispc_register_isr(omap_vout_isr, vout, mask);
@@ -2015,7 +1707,8 @@ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
return -EINVAL;
vout->streaming = 0;
- mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD;
+ mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+ | DISPC_IRQ_VSYNC2;
omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
@@ -2108,7 +1801,9 @@ static int vidioc_s_fbuf(struct file *file, void *fh,
if (ovl->manager && ovl->manager->get_manager_info &&
ovl->manager->set_manager_info) {
ovl->manager->get_manager_info(ovl->manager, &info);
- info.alpha_enabled = enable;
+ /* enable this only if there is no zorder cap */
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
+ info.partial_alpha_enabled = enable;
if (ovl->manager->set_manager_info(ovl->manager, &info))
return -EINVAL;
}
@@ -2140,7 +1835,7 @@ static int vidioc_g_fbuf(struct file *file, void *fh,
}
if (ovl->manager && ovl->manager->get_manager_info) {
ovl->manager->get_manager_info(ovl->manager, &info);
- if (info.alpha_enabled)
+ if (info.partial_alpha_enabled)
a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
}
@@ -2175,6 +1870,7 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = {
static const struct v4l2_file_operations omap_vout_fops = {
.owner = THIS_MODULE,
+ .poll = omap_vout_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = omap_vout_mmap,
.open = omap_vout_open,
@@ -2228,7 +1924,8 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout)
vout->mirror = 0;
vout->control[2].id = V4L2_CID_HFLIP;
vout->control[2].value = 0;
- vout->vrfb_bpp = 2;
+ if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+ vout->vrfb_bpp = 2;
control[1].id = V4L2_CID_BG_COLOR;
control[1].value = 0;
@@ -2260,17 +1957,15 @@ static int __init omap_vout_setup_video_bufs(struct platform_device *pdev,
int vid_num)
{
u32 numbuffers;
- int ret = 0, i, j;
- int image_width, image_height;
- struct video_device *vfd;
+ int ret = 0, i;
+ struct omapvideo_info *ovid;
struct omap_vout_device *vout;
- int static_vrfb_allocation = 0, vrfb_num_bufs = VRFB_NUM_BUFS;
struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
struct omap2video_device *vid_dev =
container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
vout = vid_dev->vouts[vid_num];
- vfd = vout->vfd;
+ ovid = &vout->vid_info;
numbuffers = (vid_num == 0) ? video1_numbuffers : video2_numbuffers;
vout->buffer_size = (vid_num == 0) ? video1_bufsize : video2_bufsize;
@@ -2287,66 +1982,16 @@ static int __init omap_vout_setup_video_bufs(struct platform_device *pdev,
}
}
- for (i = 0; i < VRFB_NUM_BUFS; i++) {
- if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) {
- dev_info(&pdev->dev, ": VRFB allocation failed\n");
- for (j = 0; j < i; j++)
- omap_vrfb_release_ctx(&vout->vrfb_context[j]);
- ret = -ENOMEM;
- goto free_buffers;
- }
- }
vout->cropped_offset = 0;
- /* Calculate VRFB memory size */
- /* allocate for worst case size */
- image_width = VID_MAX_WIDTH / TILE_SIZE;
- if (VID_MAX_WIDTH % TILE_SIZE)
- image_width++;
-
- image_width = image_width * TILE_SIZE;
- image_height = VID_MAX_HEIGHT / TILE_SIZE;
-
- if (VID_MAX_HEIGHT % TILE_SIZE)
- image_height++;
-
- image_height = image_height * TILE_SIZE;
- vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2);
-
- /*
- * Request and Initialize DMA, for DMA based VRFB transfer
- */
- vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE;
- vout->vrfb_dma_tx.dma_ch = -1;
- vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED;
- ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX",
- omap_vout_vrfb_dma_tx_callback,
- (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch);
- if (ret < 0) {
- vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
- dev_info(&pdev->dev, ": failed to allocate DMA Channel for"
- " video%d\n", vfd->minor);
- }
- init_waitqueue_head(&vout->vrfb_dma_tx.wait);
-
- /* Allocate VRFB buffers if selected through bootargs */
- static_vrfb_allocation = (vid_num == 0) ?
- vid1_static_vrfb_alloc : vid2_static_vrfb_alloc;
-
- /* statically allocated the VRFB buffer is done through
- commands line aruments */
- if (static_vrfb_allocation) {
- if (omap_vout_allocate_vrfb_buffers(vout, &vrfb_num_bufs, -1)) {
- ret = -ENOMEM;
- goto release_vrfb_ctx;
- }
- vout->vrfb_static_allocation = 1;
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ int static_vrfb_allocation = (vid_num == 0) ?
+ vid1_static_vrfb_alloc : vid2_static_vrfb_alloc;
+ ret = omap_vout_setup_vrfb_bufs(pdev, vid_num,
+ static_vrfb_allocation);
}
- return 0;
-release_vrfb_ctx:
- for (j = 0; j < VRFB_NUM_BUFS; j++)
- omap_vrfb_release_ctx(&vout->vrfb_context[j]);
+ return ret;
free_buffers:
for (i = 0; i < numbuffers; i++) {
@@ -2389,6 +2034,10 @@ static int __init omap_vout_create_video_devices(struct platform_device *pdev)
vout->vid_info.num_overlays = 1;
vout->vid_info.id = k + 1;
+ /* Set VRFB as rotation_type for omap2 and omap3 */
+ if (cpu_is_omap24xx() || cpu_is_omap34xx())
+ vout->vid_info.rotation_type = VOUT_ROT_VRFB;
+
/* Setup the default configuration for the video devices
*/
if (omap_vout_setup_video_data(vout) != 0) {
@@ -2422,7 +2071,8 @@ static int __init omap_vout_create_video_devices(struct platform_device *pdev)
goto success;
error2:
- omap_vout_release_vrfb(vout);
+ if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+ omap_vout_release_vrfb(vout);
omap_vout_free_buffers(vout);
error1:
video_device_release(vfd);
@@ -2443,11 +2093,13 @@ success:
static void omap_vout_cleanup_device(struct omap_vout_device *vout)
{
struct video_device *vfd;
+ struct omapvideo_info *ovid;
if (!vout)
return;
vfd = vout->vfd;
+ ovid = &vout->vid_info;
if (vfd) {
if (!video_is_registered(vfd)) {
/*
@@ -2463,14 +2115,15 @@ static void omap_vout_cleanup_device(struct omap_vout_device *vout)
video_unregister_device(vfd);
}
}
-
- omap_vout_release_vrfb(vout);
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ omap_vout_release_vrfb(vout);
+ /* Free the VRFB buffer if allocated
+ * init time
+ */
+ if (vout->vrfb_static_allocation)
+ omap_vout_free_vrfb_buffers(vout);
+ }
omap_vout_free_buffers(vout);
- /* Free the VRFB buffer if allocated
- * init time
- */
- if (vout->vrfb_static_allocation)
- omap_vout_free_vrfb_buffers(vout);
kfree(vout);
}
@@ -2557,19 +2210,6 @@ static int __init omap_vout_probe(struct platform_device *pdev)
"'%s' Display already enabled\n",
def_display->name);
}
- /* set the update mode */
- if (def_display->caps &
- OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
- if (dssdrv->enable_te)
- dssdrv->enable_te(def_display, 0);
- if (dssdrv->set_update_mode)
- dssdrv->set_update_mode(def_display,
- OMAP_DSS_UPDATE_MANUAL);
- } else {
- if (dssdrv->set_update_mode)
- dssdrv->set_update_mode(def_display,
- OMAP_DSS_UPDATE_AUTO);
- }
}
}
diff --git a/drivers/media/video/omap/omap_vout_vrfb.c b/drivers/media/video/omap/omap_vout_vrfb.c
new file mode 100644
index 000000000000..ebebcac49225
--- /dev/null
+++ b/drivers/media/video/omap/omap_vout_vrfb.c
@@ -0,0 +1,390 @@
+/*
+ * omap_vout_vrfb.c
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+
+#include <media/videobuf-dma-contig.h>
+#include <media/v4l2-device.h>
+
+#include <plat/dma.h>
+#include <plat/vrfb.h>
+
+#include "omap_voutdef.h"
+#include "omap_voutlib.h"
+
+/*
+ * Function for allocating video buffers
+ */
+static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout,
+ unsigned int *count, int startindex)
+{
+ int i, j;
+
+ for (i = 0; i < *count; i++) {
+ if (!vout->smsshado_virt_addr[i]) {
+ vout->smsshado_virt_addr[i] =
+ omap_vout_alloc_buffer(vout->smsshado_size,
+ &vout->smsshado_phy_addr[i]);
+ }
+ if (!vout->smsshado_virt_addr[i] && startindex != -1) {
+ if (V4L2_MEMORY_MMAP == vout->memory && i >= startindex)
+ break;
+ }
+ if (!vout->smsshado_virt_addr[i]) {
+ for (j = 0; j < i; j++) {
+ omap_vout_free_buffer(
+ vout->smsshado_virt_addr[j],
+ vout->smsshado_size);
+ vout->smsshado_virt_addr[j] = 0;
+ vout->smsshado_phy_addr[j] = 0;
+ }
+ *count = 0;
+ return -ENOMEM;
+ }
+ memset((void *) vout->smsshado_virt_addr[i], 0,
+ vout->smsshado_size);
+ }
+ return 0;
+}
+
+/*
+ * Wakes up the application once the DMA transfer to VRFB space is completed.
+ */
+static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data)
+{
+ struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data;
+
+ t->tx_status = 1;
+ wake_up_interruptible(&t->wait);
+}
+
+/*
+ * Free VRFB buffers
+ */
+void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout)
+{
+ int j;
+
+ for (j = 0; j < VRFB_NUM_BUFS; j++) {
+ omap_vout_free_buffer(vout->smsshado_virt_addr[j],
+ vout->smsshado_size);
+ vout->smsshado_virt_addr[j] = 0;
+ vout->smsshado_phy_addr[j] = 0;
+ }
+}
+
+int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+ u32 static_vrfb_allocation)
+{
+ int ret = 0, i, j;
+ struct omap_vout_device *vout;
+ struct video_device *vfd;
+ int image_width, image_height;
+ int vrfb_num_bufs = VRFB_NUM_BUFS;
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct omap2video_device *vid_dev =
+ container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
+
+ vout = vid_dev->vouts[vid_num];
+ vfd = vout->vfd;
+
+ for (i = 0; i < VRFB_NUM_BUFS; i++) {
+ if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) {
+ dev_info(&pdev->dev, ": VRFB allocation failed\n");
+ for (j = 0; j < i; j++)
+ omap_vrfb_release_ctx(&vout->vrfb_context[j]);
+ ret = -ENOMEM;
+ goto free_buffers;
+ }
+ }
+
+ /* Calculate VRFB memory size */
+ /* allocate for worst case size */
+ image_width = VID_MAX_WIDTH / TILE_SIZE;
+ if (VID_MAX_WIDTH % TILE_SIZE)
+ image_width++;
+
+ image_width = image_width * TILE_SIZE;
+ image_height = VID_MAX_HEIGHT / TILE_SIZE;
+
+ if (VID_MAX_HEIGHT % TILE_SIZE)
+ image_height++;
+
+ image_height = image_height * TILE_SIZE;
+ vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2);
+
+ /*
+ * Request and Initialize DMA, for DMA based VRFB transfer
+ */
+ vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE;
+ vout->vrfb_dma_tx.dma_ch = -1;
+ vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED;
+ ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX",
+ omap_vout_vrfb_dma_tx_callback,
+ (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch);
+ if (ret < 0) {
+ vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
+ dev_info(&pdev->dev, ": failed to allocate DMA Channel for"
+ " video%d\n", vfd->minor);
+ }
+ init_waitqueue_head(&vout->vrfb_dma_tx.wait);
+
+ /* statically allocated the VRFB buffer is done through
+ commands line aruments */
+ if (static_vrfb_allocation) {
+ if (omap_vout_allocate_vrfb_buffers(vout, &vrfb_num_bufs, -1)) {
+ ret = -ENOMEM;
+ goto release_vrfb_ctx;
+ }
+ vout->vrfb_static_allocation = 1;
+ }
+ return 0;
+
+release_vrfb_ctx:
+ for (j = 0; j < VRFB_NUM_BUFS; j++)
+ omap_vrfb_release_ctx(&vout->vrfb_context[j]);
+free_buffers:
+ omap_vout_free_buffers(vout);
+
+ return ret;
+}
+
+/*
+ * Release the VRFB context once the module exits
+ */
+void omap_vout_release_vrfb(struct omap_vout_device *vout)
+{
+ int i;
+
+ for (i = 0; i < VRFB_NUM_BUFS; i++)
+ omap_vrfb_release_ctx(&vout->vrfb_context[i]);
+
+ if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) {
+ vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
+ omap_free_dma(vout->vrfb_dma_tx.dma_ch);
+ }
+}
+
+/*
+ * Allocate the buffers for the VRFB space. Data is copied from V4L2
+ * buffers to the VRFB buffers using the DMA engine.
+ */
+int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+ unsigned int *count, unsigned int startindex)
+{
+ int i;
+ bool yuv_mode;
+
+ if (!is_rotation_enabled(vout))
+ return 0;
+
+ /* If rotation is enabled, allocate memory for VRFB space also */
+ *count = *count > VRFB_NUM_BUFS ? VRFB_NUM_BUFS : *count;
+
+ /* Allocate the VRFB buffers only if the buffers are not
+ * allocated during init time.
+ */
+ if (!vout->vrfb_static_allocation)
+ if (omap_vout_allocate_vrfb_buffers(vout, count, startindex))
+ return -ENOMEM;
+
+ if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 ||
+ vout->dss_mode == OMAP_DSS_COLOR_UYVY)
+ yuv_mode = true;
+ else
+ yuv_mode = false;
+
+ for (i = 0; i < *count; i++)
+ omap_vrfb_setup(&vout->vrfb_context[i],
+ vout->smsshado_phy_addr[i], vout->pix.width,
+ vout->pix.height, vout->bpp, yuv_mode);
+
+ return 0;
+}
+
+int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+ struct videobuf_buffer *vb)
+{
+ dma_addr_t dmabuf;
+ struct vid_vrfb_dma *tx;
+ enum dss_rotation rotation;
+ u32 dest_frame_index = 0, src_element_index = 0;
+ u32 dest_element_index = 0, src_frame_index = 0;
+ u32 elem_count = 0, frame_count = 0, pixsize = 2;
+
+ if (!is_rotation_enabled(vout))
+ return 0;
+
+ dmabuf = vout->buf_phy_addr[vb->i];
+ /* If rotation is enabled, copy input buffer into VRFB
+ * memory space using DMA. We are copying input buffer
+ * into VRFB memory space of desired angle and DSS will
+ * read image VRFB memory for 0 degree angle
+ */
+ pixsize = vout->bpp * vout->vrfb_bpp;
+ /*
+ * DMA transfer in double index mode
+ */
+
+ /* Frame index */
+ dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) -
+ (vout->pix.width * vout->bpp)) + 1;
+
+ /* Source and destination parameters */
+ src_element_index = 0;
+ src_frame_index = 0;
+ dest_element_index = 1;
+ /* Number of elements per frame */
+ elem_count = vout->pix.width * vout->bpp;
+ frame_count = vout->pix.height;
+ tx = &vout->vrfb_dma_tx;
+ tx->tx_status = 0;
+ omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32,
+ (elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT,
+ tx->dev_id, 0x0);
+ /* src_port required only for OMAP1 */
+ omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+ dmabuf, src_element_index, src_frame_index);
+ /*set dma source burst mode for VRFB */
+ omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
+ rotation = calc_rotation(vout);
+
+ /* dest_port required only for OMAP1 */
+ omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX,
+ vout->vrfb_context[vb->i].paddr[0], dest_element_index,
+ dest_frame_index);
+ /*set dma dest burst mode for VRFB */
+ omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
+ omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0);
+
+ omap_start_dma(tx->dma_ch);
+ interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT);
+
+ if (tx->tx_status == 0) {
+ omap_stop_dma(tx->dma_ch);
+ return -EINVAL;
+ }
+ /* Store buffers physical address into an array. Addresses
+ * from this array will be used to configure DSS */
+ vout->queued_buf_addr[vb->i] = (u8 *)
+ vout->vrfb_context[vb->i].paddr[rotation];
+ return 0;
+}
+
+/*
+ * Calculate the buffer offsets from which the streaming should
+ * start. This offset calculation is mainly required because of
+ * the VRFB 32 pixels alignment with rotation.
+ */
+void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
+{
+ enum dss_rotation rotation;
+ bool mirroring = vout->mirror;
+ struct v4l2_rect *crop = &vout->crop;
+ struct v4l2_pix_format *pix = &vout->pix;
+ int *cropped_offset = &vout->cropped_offset;
+ int vr_ps = 1, ps = 2, temp_ps = 2;
+ int offset = 0, ctop = 0, cleft = 0, line_length = 0;
+
+ rotation = calc_rotation(vout);
+
+ if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
+ V4L2_PIX_FMT_UYVY == pix->pixelformat) {
+ if (is_rotation_enabled(vout)) {
+ /*
+ * ps - Actual pixel size for YUYV/UYVY for
+ * VRFB/Mirroring is 4 bytes
+ * vr_ps - Virtually pixel size for YUYV/UYVY is
+ * 2 bytes
+ */
+ ps = 4;
+ vr_ps = 2;
+ } else {
+ ps = 2; /* otherwise the pixel size is 2 byte */
+ }
+ } else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) {
+ ps = 4;
+ } else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) {
+ ps = 3;
+ }
+ vout->ps = ps;
+ vout->vr_ps = vr_ps;
+
+ if (is_rotation_enabled(vout)) {
+ line_length = MAX_PIXELS_PER_LINE;
+ ctop = (pix->height - crop->height) - crop->top;
+ cleft = (pix->width - crop->width) - crop->left;
+ } else {
+ line_length = pix->width;
+ }
+ vout->line_length = line_length;
+ switch (rotation) {
+ case dss_rotation_90_degree:
+ offset = vout->vrfb_context[0].yoffset *
+ vout->vrfb_context[0].bytespp;
+ temp_ps = ps / vr_ps;
+ if (mirroring == 0) {
+ *cropped_offset = offset + line_length *
+ temp_ps * cleft + crop->top * temp_ps;
+ } else {
+ *cropped_offset = offset + line_length * temp_ps *
+ cleft + crop->top * temp_ps + (line_length *
+ ((crop->width / (vr_ps)) - 1) * ps);
+ }
+ break;
+ case dss_rotation_180_degree:
+ offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset *
+ vout->vrfb_context[0].bytespp) +
+ (vout->vrfb_context[0].xoffset *
+ vout->vrfb_context[0].bytespp));
+ if (mirroring == 0) {
+ *cropped_offset = offset + (line_length * ps * ctop) +
+ (cleft / vr_ps) * ps;
+
+ } else {
+ *cropped_offset = offset + (line_length * ps * ctop) +
+ (cleft / vr_ps) * ps + (line_length *
+ (crop->height - 1) * ps);
+ }
+ break;
+ case dss_rotation_270_degree:
+ offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset *
+ vout->vrfb_context[0].bytespp;
+ temp_ps = ps / vr_ps;
+ if (mirroring == 0) {
+ *cropped_offset = offset + line_length *
+ temp_ps * crop->left + ctop * ps;
+ } else {
+ *cropped_offset = offset + line_length *
+ temp_ps * crop->left + ctop * ps +
+ (line_length * ((crop->width / vr_ps) - 1) *
+ ps);
+ }
+ break;
+ case dss_rotation_0_degree:
+ if (mirroring == 0) {
+ *cropped_offset = (line_length * ps) *
+ crop->top + (crop->left / vr_ps) * ps;
+ } else {
+ *cropped_offset = (line_length * ps) *
+ crop->top + (crop->left / vr_ps) * ps +
+ (line_length * (crop->height - 1) * ps);
+ }
+ break;
+ default:
+ *cropped_offset = (line_length * ps * crop->top) /
+ vr_ps + (crop->left * ps) / vr_ps +
+ ((crop->width / vr_ps) - 1) * ps;
+ break;
+ }
+}
diff --git a/drivers/media/video/omap/omap_vout_vrfb.h b/drivers/media/video/omap/omap_vout_vrfb.h
new file mode 100644
index 000000000000..ffde741e0590
--- /dev/null
+++ b/drivers/media/video/omap/omap_vout_vrfb.h
@@ -0,0 +1,40 @@
+/*
+ * omap_vout_vrfb.h
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#ifndef OMAP_VOUT_VRFB_H
+#define OMAP_VOUT_VRFB_H
+
+#ifdef CONFIG_VIDEO_OMAP2_VOUT_VRFB
+void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout);
+int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+ u32 static_vrfb_allocation);
+void omap_vout_release_vrfb(struct omap_vout_device *vout);
+int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+ unsigned int *count, unsigned int startindex);
+int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+ struct videobuf_buffer *vb);
+void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout);
+#else
+void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { }
+int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+ u32 static_vrfb_allocation)
+ { return 0; }
+void omap_vout_release_vrfb(struct omap_vout_device *vout) { }
+int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+ unsigned int *count, unsigned int startindex)
+ { return 0; }
+int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+ struct videobuf_buffer *vb)
+ { return 0; }
+void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) { }
+#endif
+
+#endif
diff --git a/drivers/media/video/omap/omap_voutdef.h b/drivers/media/video/omap/omap_voutdef.h
index 659497b84996..d793501cafcc 100644
--- a/drivers/media/video/omap/omap_voutdef.h
+++ b/drivers/media/video/omap/omap_voutdef.h
@@ -12,6 +12,7 @@
#define OMAP_VOUTDEF_H
#include <video/omapdss.h>
+#include <plat/vrfb.h>
#define YUYV_BPP 2
#define RGB565_BPP 2
@@ -27,6 +28,31 @@
#define MAX_DISPLAYS 3
#define MAX_MANAGERS 3
+#define QQVGA_WIDTH 160
+#define QQVGA_HEIGHT 120
+
+/* Max Resolution supported by the driver */
+#define VID_MAX_WIDTH 1280 /* Largest width */
+#define VID_MAX_HEIGHT 720 /* Largest height */
+
+/* Mimimum requirement is 2x2 for DSS */
+#define VID_MIN_WIDTH 2
+#define VID_MIN_HEIGHT 2
+
+/* 2048 x 2048 is max res supported by OMAP display controller */
+#define MAX_PIXELS_PER_LINE 2048
+
+#define VRFB_TX_TIMEOUT 1000
+#define VRFB_NUM_BUFS 4
+
+/* Max buffer size tobe allocated during init */
+#define OMAP_VOUT_MAX_BUF_SIZE (VID_MAX_WIDTH*VID_MAX_HEIGHT*4)
+
+enum dma_channel_state {
+ DMA_CHAN_NOT_ALLOTED,
+ DMA_CHAN_ALLOTED,
+};
+
/* Enum for Rotation
* DSS understands rotation in 0, 1, 2, 3 context
* while V4L2 driver understands it as 0, 90, 180, 270
@@ -37,6 +63,18 @@ enum dss_rotation {
dss_rotation_180_degree = 2,
dss_rotation_270_degree = 3,
};
+
+/* Enum for choosing rotation type for vout
+ * DSS2 doesn't understand no rotation as an
+ * option while V4L2 driver doesn't support
+ * rotation in the case where VRFB is not built in
+ * the kernel
+ */
+enum vout_rotaion_type {
+ VOUT_ROT_NONE = 0,
+ VOUT_ROT_VRFB = 1,
+};
+
/*
* This structure is used to store the DMA transfer parameters
* for VRFB hidden buffer
@@ -53,6 +91,7 @@ struct omapvideo_info {
int id;
int num_overlays;
struct omap_overlay *overlays[MAX_OVLS];
+ enum vout_rotaion_type rotation_type;
};
struct omap2video_device {
@@ -144,4 +183,43 @@ struct omap_vout_device {
int io_allowed;
};
+
+/*
+ * Return true if rotation is 90 or 270
+ */
+static inline int is_rotation_90_or_270(const struct omap_vout_device *vout)
+{
+ return (vout->rotation == dss_rotation_90_degree ||
+ vout->rotation == dss_rotation_270_degree);
+}
+
+/*
+ * Return true if rotation is enabled
+ */
+static inline int is_rotation_enabled(const struct omap_vout_device *vout)
+{
+ return vout->rotation || vout->mirror;
+}
+
+/*
+ * Reverse the rotation degree if mirroring is enabled
+ */
+static inline int calc_rotation(const struct omap_vout_device *vout)
+{
+ if (!vout->mirror)
+ return vout->rotation;
+
+ switch (vout->rotation) {
+ case dss_rotation_90_degree:
+ return dss_rotation_270_degree;
+ case dss_rotation_270_degree:
+ return dss_rotation_90_degree;
+ case dss_rotation_180_degree:
+ return dss_rotation_0_degree;
+ default:
+ return dss_rotation_180_degree;
+ }
+}
+
+void omap_vout_free_buffers(struct omap_vout_device *vout);
#endif /* ifndef OMAP_VOUTDEF_H */
diff --git a/drivers/media/video/omap/omap_voutlib.c b/drivers/media/video/omap/omap_voutlib.c
index 8ae74817a110..115408b9274f 100644
--- a/drivers/media/video/omap/omap_voutlib.c
+++ b/drivers/media/video/omap/omap_voutlib.c
@@ -24,8 +24,12 @@
#include <linux/types.h>
#include <linux/videodev2.h>
+#include <linux/dma-mapping.h>
+
#include <plat/cpu.h>
+#include "omap_voutlib.h"
+
MODULE_AUTHOR("Texas Instruments");
MODULE_DESCRIPTION("OMAP Video library");
MODULE_LICENSE("GPL");
@@ -291,3 +295,45 @@ void omap_vout_new_format(struct v4l2_pix_format *pix,
}
EXPORT_SYMBOL_GPL(omap_vout_new_format);
+/*
+ * Allocate buffers
+ */
+unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr)
+{
+ u32 order, size;
+ unsigned long virt_addr, addr;
+
+ size = PAGE_ALIGN(buf_size);
+ order = get_order(size);
+ virt_addr = __get_free_pages(GFP_KERNEL, order);
+ addr = virt_addr;
+
+ if (virt_addr) {
+ while (size > 0) {
+ SetPageReserved(virt_to_page(addr));
+ addr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ }
+ *phys_addr = (u32) virt_to_phys((void *) virt_addr);
+ return virt_addr;
+}
+
+/*
+ * Free buffers
+ */
+void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size)
+{
+ u32 order, size;
+ unsigned long addr = virtaddr;
+
+ size = PAGE_ALIGN(buf_size);
+ order = get_order(size);
+
+ while (size > 0) {
+ ClearPageReserved(virt_to_page(addr));
+ addr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ free_pages((unsigned long) virtaddr, order);
+}
diff --git a/drivers/media/video/omap/omap_voutlib.h b/drivers/media/video/omap/omap_voutlib.h
index a60b16e8bfc3..e51750a597e3 100644
--- a/drivers/media/video/omap/omap_voutlib.h
+++ b/drivers/media/video/omap/omap_voutlib.h
@@ -12,23 +12,25 @@
#ifndef OMAP_VOUTLIB_H
#define OMAP_VOUTLIB_H
-extern void omap_vout_default_crop(struct v4l2_pix_format *pix,
+void omap_vout_default_crop(struct v4l2_pix_format *pix,
struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop);
-extern int omap_vout_new_crop(struct v4l2_pix_format *pix,
+int omap_vout_new_crop(struct v4l2_pix_format *pix,
struct v4l2_rect *crop, struct v4l2_window *win,
struct v4l2_framebuffer *fbuf,
const struct v4l2_rect *new_crop);
-extern int omap_vout_try_window(struct v4l2_framebuffer *fbuf,
+int omap_vout_try_window(struct v4l2_framebuffer *fbuf,
struct v4l2_window *new_win);
-extern int omap_vout_new_window(struct v4l2_rect *crop,
+int omap_vout_new_window(struct v4l2_rect *crop,
struct v4l2_window *win, struct v4l2_framebuffer *fbuf,
struct v4l2_window *new_win);
-extern void omap_vout_new_format(struct v4l2_pix_format *pix,
+void omap_vout_new_format(struct v4l2_pix_format *pix,
struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop,
struct v4l2_window *win);
+unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr);
+void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size);
#endif /* #ifndef OMAP_VOUTLIB_H */
diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c
index e7cfc85b0a1c..e87ae2f634b2 100644
--- a/drivers/media/video/omap1_camera.c
+++ b/drivers/media/video/omap1_camera.c
@@ -26,7 +26,6 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/version.h>
#include <media/omap1_camera.h>
#include <media/soc_camera.h>
@@ -38,7 +37,7 @@
#define DRIVER_NAME "omap1-camera"
-#define VERSION_CODE KERNEL_VERSION(0, 0, 1)
+#define DRIVER_VERSION "0.0.2"
/*
@@ -103,10 +102,10 @@
/* end of OMAP1 Camera Interface registers */
-#define SOCAM_BUS_FLAGS (SOCAM_MASTER | \
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | \
- SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8)
+#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
@@ -208,7 +207,7 @@ static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
struct soc_camera_device *icd = vq->priv_data;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
if (bytes_per_line < 0)
@@ -222,7 +221,7 @@ static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
*count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
- dev_dbg(icd->dev.parent,
+ dev_dbg(icd->parent,
"%s: count=%d, size=%d\n", __func__, *count, *size);
return 0;
@@ -241,7 +240,7 @@ static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf,
videobuf_dma_contig_free(vq, vb);
} else {
struct soc_camera_device *icd = vq->priv_data;
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
videobuf_dma_unmap(dev, dma);
@@ -258,7 +257,7 @@ static int omap1_videobuf_prepare(struct videobuf_queue *vq,
struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
int ret;
@@ -490,7 +489,7 @@ static void omap1_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
struct omap1_cam_buf *buf;
u32 mode;
@@ -519,7 +518,7 @@ static void omap1_videobuf_queue(struct videobuf_queue *vq,
pcdev->active = buf;
pcdev->ready = NULL;
- dev_dbg(icd->dev.parent,
+ dev_dbg(icd->parent,
"%s: capture not active, setup FIFO, start DMA\n", __func__);
mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
@@ -543,8 +542,8 @@ static void omap1_videobuf_release(struct videobuf_queue *vq,
struct omap1_cam_buf *buf =
container_of(vb, struct omap1_cam_buf, vb);
struct soc_camera_device *icd = vq->priv_data;
- struct device *dev = icd->dev.parent;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
struct omap1_cam_dev *pcdev = ici->priv;
switch (vb->state) {
@@ -573,7 +572,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev,
{
struct omap1_cam_buf *buf = pcdev->active;
struct videobuf_buffer *vb;
- struct device *dev = pcdev->icd->dev.parent;
+ struct device *dev = pcdev->icd->parent;
if (WARN_ON(!buf)) {
suspend_capture(pcdev);
@@ -799,7 +798,7 @@ out:
static irqreturn_t cam_isr(int irq, void *data)
{
struct omap1_cam_dev *pcdev = data;
- struct device *dev = pcdev->icd->dev.parent;
+ struct device *dev = pcdev->icd->parent;
struct omap1_cam_buf *buf = pcdev->active;
u32 it_status;
unsigned long flags;
@@ -909,7 +908,7 @@ static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
*/
static int omap1_cam_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
u32 ctrlclock;
@@ -952,14 +951,14 @@ static int omap1_cam_add_device(struct soc_camera_device *icd)
pcdev->icd = icd;
- dev_dbg(icd->dev.parent, "OMAP1 Camera driver attached to camera %d\n",
+ dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
icd->devnum);
return 0;
}
static void omap1_cam_remove_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
u32 ctrlclock;
@@ -985,7 +984,7 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd)
pcdev->icd = NULL;
- dev_dbg(icd->dev.parent,
+ dev_dbg(icd->parent,
"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
}
@@ -1070,7 +1069,7 @@ static int omap1_cam_get_formats(struct soc_camera_device *icd,
unsigned int idx, struct soc_camera_format_xlate *xlate)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
int formats = 0, ret;
enum v4l2_mbus_pixelcode code;
const struct soc_mbus_pixelfmt *fmt;
@@ -1222,9 +1221,9 @@ static int omap1_cam_set_crop(struct soc_camera_device *icd,
struct v4l2_rect *rect = &crop->c;
const struct soc_camera_format_xlate *xlate = icd->current_fmt;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
struct omap1_cam_dev *pcdev = ici->priv;
- struct device *dev = icd->dev.parent;
struct v4l2_mbus_framefmt mf;
int ret;
@@ -1270,8 +1269,8 @@ static int omap1_cam_set_fmt(struct soc_camera_device *icd,
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
- struct device *dev = icd->dev.parent;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
struct omap1_cam_dev *pcdev = ici->priv;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_mbus_framefmt mf;
@@ -1326,7 +1325,7 @@ static int omap1_cam_try_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
- dev_warn(icd->dev.parent, "Format %#x not found\n",
+ dev_warn(icd->parent, "Format %#x not found\n",
pix->pixelformat);
return -EINVAL;
}
@@ -1362,7 +1361,7 @@ static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
struct vm_area_struct *vma)
{
struct soc_camera_device *icd = q->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
int ret;
@@ -1377,17 +1376,17 @@ static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
static void omap1_cam_init_videobuf(struct videobuf_queue *q,
struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
if (!sg_mode)
videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
- icd->dev.parent, &pcdev->lock,
+ icd->parent, &pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
else
videobuf_queue_sg_init(q, &omap1_videobuf_ops,
- icd->dev.parent, &pcdev->lock,
+ icd->parent, &pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
@@ -1431,7 +1430,6 @@ static int omap1_cam_querycap(struct soc_camera_host *ici,
{
/* cap->name is set by the friendly caller:-> */
strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
- cap->version = VERSION_CODE;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
@@ -1440,41 +1438,55 @@ static int omap1_cam_querycap(struct soc_camera_host *ici,
static int omap1_cam_set_bus_param(struct soc_camera_device *icd,
__u32 pixfmt)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
struct omap1_cam_dev *pcdev = ici->priv;
- struct device *dev = icd->dev.parent;
const struct soc_camera_format_xlate *xlate;
const struct soc_mbus_pixelfmt *fmt;
- unsigned long camera_flags, common_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
u32 ctrlclock, mode;
int ret;
- camera_flags = icd->ops->query_bus_param(icd);
-
- common_flags = soc_camera_bus_param_compatible(camera_flags,
- SOCAM_BUS_FLAGS);
- if (!common_flags)
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(dev,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, SOCAM_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = SOCAM_BUS_FLAGS;
+ }
/* Make choices, possibly based on platform configuration */
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
}
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
return ret;
+ }
ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
if (ctrlclock & LCLK_EN)
CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
- if (common_flags & SOCAM_PCLK_SAMPLE_RISING) {
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) {
dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
ctrlclock |= POLCLK;
} else {
@@ -1567,10 +1579,10 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
pcdev->clk = clk;
pcdev->pdata = pdev->dev.platform_data;
- pcdev->pflags = pcdev->pdata->flags;
-
- if (pcdev->pdata)
+ if (pcdev->pdata) {
+ pcdev->pflags = pcdev->pdata->flags;
pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
+ }
switch (pcdev->camexclk) {
case 6000000:
@@ -1580,6 +1592,7 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
case 24000000:
break;
default:
+ /* pcdev->camexclk != 0 => pcdev->pdata != NULL */
dev_warn(&pdev->dev,
"Incorrect sensor clock frequency %ld kHz, "
"should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
@@ -1587,8 +1600,7 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
pcdev->pdata->camexclk_khz);
pcdev->camexclk = 0;
case 0:
- dev_info(&pdev->dev,
- "Not providing sensor clock\n");
+ dev_info(&pdev->dev, "Not providing sensor clock\n");
}
INIT_LIST_HEAD(&pcdev->capture);
@@ -1718,4 +1730,5 @@ MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
index f6626e87dbc5..45522e603185 100644
--- a/drivers/media/video/omap24xxcam.c
+++ b/drivers/media/video/omap24xxcam.c
@@ -31,19 +31,19 @@
#include <linux/interrupt.h>
#include <linux/videodev2.h>
#include <linux/pci.h> /* needed for videobufs */
-#include <linux/version.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/module.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include "omap24xxcam.h"
-#define OMAP24XXCAM_VERSION KERNEL_VERSION(0, 0, 0)
+#define OMAP24XXCAM_VERSION "0.0.1"
#define RESET_TIMEOUT_NS 10000
@@ -309,11 +309,11 @@ static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
order--;
/* try to allocate as many contiguous pages as possible */
- page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+ page = alloc_pages(GFP_KERNEL, order);
/* if allocation fails, try to allocate smaller amount */
while (page == NULL) {
order--;
- page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+ page = alloc_pages(GFP_KERNEL, order);
if (page == NULL && !order) {
err = -ENOMEM;
goto out;
@@ -993,7 +993,6 @@ static int vidioc_querycap(struct file *file, void *fh,
strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
- cap->version = OMAP24XXCAM_VERSION;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
@@ -1768,14 +1767,13 @@ static int __devinit omap24xxcam_probe(struct platform_device *pdev)
dev_err(cam->dev, "no mem resource?\n");
goto err;
}
- if (!request_mem_region(mem->start, (mem->end - mem->start) + 1,
- pdev->name)) {
+ if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
dev_err(cam->dev,
"cannot reserve camera register I/O region\n");
goto err;
}
cam->mmio_base_phys = mem->start;
- cam->mmio_size = (mem->end - mem->start) + 1;
+ cam->mmio_size = resource_size(mem);
/* map the region */
cam->mmio_base = (unsigned long)
@@ -1889,6 +1887,7 @@ static void __exit omap24xxcam_cleanup(void)
MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
MODULE_LICENSE("GPL");
+MODULE_VERSION(OMAP24XXCAM_VERSION);
module_param(video_nr, int, 0);
MODULE_PARM_DESC(video_nr,
"Minor number for video device (-1 ==> auto assign)");
diff --git a/drivers/media/video/omap3isp/Makefile b/drivers/media/video/omap3isp/Makefile
index b1b344774ae7..e8847e79e31a 100644
--- a/drivers/media/video/omap3isp/Makefile
+++ b/drivers/media/video/omap3isp/Makefile
@@ -1,8 +1,6 @@
# Makefile for OMAP3 ISP driver
-ifdef CONFIG_VIDEO_OMAP3_DEBUG
-EXTRA_CFLAGS += -DDEBUG
-endif
+ccflags-$(CONFIG_VIDEO_OMAP3_DEBUG) += -DDEBUG
omap3-isp-objs += \
isp.o ispqueue.o ispvideo.o \
diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c
index 94b6ed89e195..b818cacf420f 100644
--- a/drivers/media/video/omap3isp/isp.c
+++ b/drivers/media/video/omap3isp/isp.c
@@ -80,6 +80,13 @@
#include "isph3a.h"
#include "isphist.h"
+/*
+ * this is provided as an interim solution until omap3isp doesn't need
+ * any omap-specific iommu API
+ */
+#define to_iommu(dev) \
+ (struct omap_iommu *)platform_get_drvdata(to_platform_device(dev))
+
static unsigned int autoidle;
module_param(autoidle, int, 0444);
MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support");
@@ -732,7 +739,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
struct media_pad *pad;
struct v4l2_subdev *subdev;
unsigned long flags;
- int ret = 0;
+ int ret;
spin_lock_irqsave(&pipe->lock, flags);
pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT);
@@ -756,7 +763,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
ret = v4l2_subdev_call(subdev, video, s_stream, mode);
if (ret < 0 && ret != -ENOIOCTLCMD)
- break;
+ return ret;
if (subdev == &isp->isp_ccdc.subdev) {
v4l2_subdev_call(&isp->isp_aewb.subdev, video,
@@ -777,7 +784,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT)
atomic_inc(&pipe->frame_number);
- return ret;
+ return 0;
}
static int isp_pipeline_wait_resizer(struct isp_device *isp)
@@ -1108,7 +1115,7 @@ static void isp_save_ctx(struct isp_device *isp)
{
isp_save_context(isp, isp_reg_list);
if (isp->iommu)
- iommu_save_ctx(isp->iommu);
+ omap_iommu_save_ctx(isp->iommu);
}
/*
@@ -1122,7 +1129,7 @@ static void isp_restore_ctx(struct isp_device *isp)
{
isp_restore_context(isp, isp_reg_list);
if (isp->iommu)
- iommu_restore_ctx(isp->iommu);
+ omap_iommu_restore_ctx(isp->iommu);
omap3isp_ccdc_restore_context(isp);
omap3isp_preview_restore_context(isp);
}
@@ -1697,6 +1704,7 @@ static int isp_register_entities(struct isp_device *isp)
isp->media_dev.dev = isp->dev;
strlcpy(isp->media_dev.model, "TI OMAP3 ISP",
sizeof(isp->media_dev.model));
+ isp->media_dev.hw_revision = isp->revision;
isp->media_dev.link_notify = isp_pipeline_link_notify;
ret = media_device_register(&isp->media_dev);
if (ret < 0) {
@@ -1975,7 +1983,8 @@ static int isp_remove(struct platform_device *pdev)
isp_cleanup_modules(isp);
omap3isp_get(isp);
- iommu_put(isp->iommu);
+ iommu_detach_device(isp->domain, isp->iommu_dev);
+ iommu_domain_free(isp->domain);
omap3isp_put(isp);
free_irq(isp->irq_num, isp);
@@ -2123,25 +2132,41 @@ static int isp_probe(struct platform_device *pdev)
}
/* IOMMU */
- isp->iommu = iommu_get("isp");
- if (IS_ERR_OR_NULL(isp->iommu)) {
- isp->iommu = NULL;
+ isp->iommu_dev = omap_find_iommu_device("isp");
+ if (!isp->iommu_dev) {
+ dev_err(isp->dev, "omap_find_iommu_device failed\n");
ret = -ENODEV;
goto error_isp;
}
+ /* to be removed once iommu migration is complete */
+ isp->iommu = to_iommu(isp->iommu_dev);
+
+ isp->domain = iommu_domain_alloc(pdev->dev.bus);
+ if (!isp->domain) {
+ dev_err(isp->dev, "can't alloc iommu domain\n");
+ ret = -ENOMEM;
+ goto error_isp;
+ }
+
+ ret = iommu_attach_device(isp->domain, isp->iommu_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
+ goto free_domain;
+ }
+
/* Interrupt */
isp->irq_num = platform_get_irq(pdev, 0);
if (isp->irq_num <= 0) {
dev_err(isp->dev, "No IRQ resource\n");
ret = -ENODEV;
- goto error_isp;
+ goto detach_dev;
}
if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) {
dev_err(isp->dev, "Unable to request IRQ\n");
ret = -EINVAL;
- goto error_isp;
+ goto detach_dev;
}
/* Entities */
@@ -2162,8 +2187,11 @@ error_modules:
isp_cleanup_modules(isp);
error_irq:
free_irq(isp->irq_num, isp);
+detach_dev:
+ iommu_detach_device(isp->domain, isp->iommu_dev);
+free_domain:
+ iommu_domain_free(isp->domain);
error_isp:
- iommu_put(isp->iommu);
omap3isp_put(isp);
error:
isp_put_clocks(isp);
@@ -2183,6 +2211,8 @@ error:
regulator_put(isp->isp_csiphy2.vdd);
regulator_put(isp->isp_csiphy1.vdd);
platform_set_drvdata(pdev, NULL);
+
+ mutex_destroy(&isp->isp_mutex);
kfree(isp);
return ret;
@@ -2234,3 +2264,4 @@ module_exit(isp_cleanup);
MODULE_AUTHOR("Nokia Corporation");
MODULE_DESCRIPTION("TI OMAP3 ISP driver");
MODULE_LICENSE("GPL");
+MODULE_VERSION(ISP_VIDEO_DRIVER_VERSION);
diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h
index 2620c405f5e4..705946ef4d60 100644
--- a/drivers/media/video/omap3isp/isp.h
+++ b/drivers/media/video/omap3isp/isp.h
@@ -27,11 +27,13 @@
#ifndef OMAP3_ISP_CORE_H
#define OMAP3_ISP_CORE_H
+#include <media/omap3isp.h>
#include <media/v4l2-device.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
+#include <linux/iommu.h>
#include <plat/iommu.h>
#include <plat/iovmm.h>
@@ -94,14 +96,6 @@ enum isp_subclk_resource {
OMAP3_ISP_SUBCLK_RESIZER = (1 << 4),
};
-enum isp_interface_type {
- ISP_INTERFACE_PARALLEL,
- ISP_INTERFACE_CSI2A_PHY2,
- ISP_INTERFACE_CCP2B_PHY1,
- ISP_INTERFACE_CCP2B_PHY2,
- ISP_INTERFACE_CSI2C_PHY1,
-};
-
/* ISP: OMAP 34xx ES 1.0 */
#define ISP_REVISION_1_0 0x10
/* ISP2: OMAP 34xx ES 2.0, 2.1 and 3.0 */
@@ -130,76 +124,6 @@ struct isp_reg {
u32 val;
};
-/**
- * struct isp_parallel_platform_data - Parallel interface platform data
- * @data_lane_shift: Data lane shifter
- * 0 - CAMEXT[13:0] -> CAM[13:0]
- * 1 - CAMEXT[13:2] -> CAM[11:0]
- * 2 - CAMEXT[13:4] -> CAM[9:0]
- * 3 - CAMEXT[13:6] -> CAM[7:0]
- * @clk_pol: Pixel clock polarity
- * 0 - Non Inverted, 1 - Inverted
- * @bridge: CCDC Bridge input control
- * ISPCTRL_PAR_BRIDGE_DISABLE - Disable
- * ISPCTRL_PAR_BRIDGE_LENDIAN - Little endian
- * ISPCTRL_PAR_BRIDGE_BENDIAN - Big endian
- */
-struct isp_parallel_platform_data {
- unsigned int data_lane_shift:2;
- unsigned int clk_pol:1;
- unsigned int bridge:4;
-};
-
-/**
- * struct isp_ccp2_platform_data - CCP2 interface platform data
- * @strobe_clk_pol: Strobe/clock polarity
- * 0 - Non Inverted, 1 - Inverted
- * @crc: Enable the cyclic redundancy check
- * @ccp2_mode: Enable CCP2 compatibility mode
- * 0 - MIPI-CSI1 mode, 1 - CCP2 mode
- * @phy_layer: Physical layer selection
- * ISPCCP2_CTRL_PHY_SEL_CLOCK - Data/clock physical layer
- * ISPCCP2_CTRL_PHY_SEL_STROBE - Data/strobe physical layer
- * @vpclk_div: Video port output clock control
- */
-struct isp_ccp2_platform_data {
- unsigned int strobe_clk_pol:1;
- unsigned int crc:1;
- unsigned int ccp2_mode:1;
- unsigned int phy_layer:1;
- unsigned int vpclk_div:2;
-};
-
-/**
- * struct isp_csi2_platform_data - CSI2 interface platform data
- * @crc: Enable the cyclic redundancy check
- * @vpclk_div: Video port output clock control
- */
-struct isp_csi2_platform_data {
- unsigned crc:1;
- unsigned vpclk_div:2;
-};
-
-struct isp_subdev_i2c_board_info {
- struct i2c_board_info *board_info;
- int i2c_adapter_id;
-};
-
-struct isp_v4l2_subdevs_group {
- struct isp_subdev_i2c_board_info *subdevs;
- enum isp_interface_type interface;
- union {
- struct isp_parallel_platform_data parallel;
- struct isp_ccp2_platform_data ccp2;
- struct isp_csi2_platform_data csi2;
- } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
-};
-
-struct isp_platform_data {
- struct isp_v4l2_subdevs_group *subdevs;
- void (*set_constraints)(struct isp_device *isp, bool enable);
-};
-
struct isp_platform_callback {
u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel);
int (*csiphy_config)(struct isp_csiphy *phy,
@@ -288,7 +212,9 @@ struct isp_device {
unsigned int sbl_resources;
unsigned int subclk_resources;
- struct iommu *iommu;
+ struct omap_iommu *iommu;
+ struct iommu_domain *domain;
+ struct device *iommu_dev;
struct isp_platform_callback platform_cb;
};
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c
index 39d501bda636..b0b0fa5a3572 100644
--- a/drivers/media/video/omap3isp/ispccdc.c
+++ b/drivers/media/video/omap3isp/ispccdc.c
@@ -31,6 +31,7 @@
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <media/v4l2-event.h>
#include "isp.h"
@@ -365,7 +366,7 @@ static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
dma_unmap_sg(isp->dev, req->iovm->sgt->sgl,
req->iovm->sgt->nents, DMA_TO_DEVICE);
if (req->table)
- iommu_vfree(isp->iommu, req->table);
+ omap_iommu_vfree(isp->domain, isp->iommu, req->table);
kfree(req);
}
@@ -437,15 +438,15 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
req->enable = 1;
- req->table = iommu_vmalloc(isp->iommu, 0, req->config.size,
- IOMMU_FLAG);
+ req->table = omap_iommu_vmalloc(isp->domain, isp->iommu, 0,
+ req->config.size, IOMMU_FLAG);
if (IS_ERR_VALUE(req->table)) {
req->table = 0;
ret = -ENOMEM;
goto done;
}
- req->iovm = find_iovm_area(isp->iommu, req->table);
+ req->iovm = omap_find_iovm_area(isp->iommu, req->table);
if (req->iovm == NULL) {
ret = -ENOMEM;
goto done;
@@ -461,7 +462,7 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl,
req->iovm->sgt->nents, DMA_TO_DEVICE);
- table = da_to_va(isp->iommu, req->table);
+ table = omap_da_to_va(isp->iommu, req->table);
if (copy_from_user(table, config->lsc, req->config.size)) {
ret = -EFAULT;
goto done;
@@ -730,18 +731,19 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
/*
* table_new must be 64-bytes aligned, but it's
- * already done by iommu_vmalloc().
+ * already done by omap_iommu_vmalloc().
*/
size = ccdc->fpc.fpnum * 4;
- table_new = iommu_vmalloc(isp->iommu, 0, size,
- IOMMU_FLAG);
+ table_new = omap_iommu_vmalloc(isp->domain, isp->iommu,
+ 0, size, IOMMU_FLAG);
if (IS_ERR_VALUE(table_new))
return -ENOMEM;
- if (copy_from_user(da_to_va(isp->iommu, table_new),
+ if (copy_from_user(omap_da_to_va(isp->iommu, table_new),
(__force void __user *)
ccdc->fpc.fpcaddr, size)) {
- iommu_vfree(isp->iommu, table_new);
+ omap_iommu_vfree(isp->domain, isp->iommu,
+ table_new);
return -EFAULT;
}
@@ -751,7 +753,7 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
ccdc_configure_fpc(ccdc);
if (table_old != 0)
- iommu_vfree(isp->iommu, table_old);
+ omap_iommu_vfree(isp->domain, isp->iommu, table_old);
}
return ccdc_lsc_config(ccdc, ccdc_struct);
@@ -1148,6 +1150,8 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
omap3isp_configure_bridge(isp, ccdc->input, pdata, shift);
ccdc->syncif.datsz = depth_out;
+ ccdc->syncif.hdpol = pdata ? pdata->hs_pol : 0;
+ ccdc->syncif.vdpol = pdata ? pdata->vs_pol : 0;
ccdc_config_sync_if(ccdc, &ccdc->syncif);
/* CCDC_PAD_SINK */
@@ -1402,11 +1406,14 @@ static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc)
{
+ struct isp_pipeline *pipe =
+ to_isp_pipeline(&ccdc->video_out.video.entity);
struct video_device *vdev = &ccdc->subdev.devnode;
struct v4l2_event event;
memset(&event, 0, sizeof(event));
- event.type = V4L2_EVENT_OMAP3ISP_HS_VS;
+ event.type = V4L2_EVENT_FRAME_SYNC;
+ event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number);
v4l2_event_queue(vdev, &event);
}
@@ -1688,10 +1695,14 @@ static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
- if (sub->type != V4L2_EVENT_OMAP3ISP_HS_VS)
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
return -EINVAL;
- return v4l2_event_subscribe(fh, sub);
+ /* line number is zero at frame start */
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS);
}
static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
@@ -1825,7 +1836,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
* callers to request an output size bigger than the input size
* up to the nearest multiple of 16.
*/
- fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15);
+ fmt->width = clamp_t(u32, width, 32, fmt->width + 15);
fmt->width &= ~15;
fmt->height = clamp_t(u32, height, 32, fmt->height);
break;
@@ -2141,6 +2152,37 @@ static const struct media_entity_operations ccdc_media_ops = {
.link_setup = ccdc_link_setup,
};
+void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
+{
+ v4l2_device_unregister_subdev(&ccdc->subdev);
+ omap3isp_video_unregister(&ccdc->video_out);
+}
+
+int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video node. */
+ ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&ccdc->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_ccdc_unregister_entities(ccdc);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP CCDC initialisation and cleanup
+ */
+
/*
* ccdc_init_entities - Initialize V4L2 subdev and media entity
* @ccdc: ISP CCDC module
@@ -2162,7 +2204,6 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
sd->grp_id = 1 << 16; /* group ID for isp subdevs */
v4l2_set_subdevdata(sd, ccdc);
sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
- sd->nevents = OMAP3ISP_CCDC_NEVENTS;
pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
@@ -2183,50 +2224,23 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
ret = omap3isp_video_init(&ccdc->video_out, "CCDC");
if (ret < 0)
- return ret;
+ goto error_video;
/* Connect the CCDC subdev to the video node. */
ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF,
&ccdc->video_out.video.entity, 0, 0);
if (ret < 0)
- return ret;
-
- return 0;
-}
-
-void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
-{
- media_entity_cleanup(&ccdc->subdev.entity);
-
- v4l2_device_unregister_subdev(&ccdc->subdev);
- omap3isp_video_unregister(&ccdc->video_out);
-}
-
-int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video node. */
- ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&ccdc->video_out, vdev);
- if (ret < 0)
- goto error;
+ goto error_link;
return 0;
-error:
- omap3isp_ccdc_unregister_entities(ccdc);
+error_link:
+ omap3isp_video_cleanup(&ccdc->video_out);
+error_video:
+ media_entity_cleanup(me);
return ret;
}
-/* -----------------------------------------------------------------------------
- * ISP CCDC initialisation and cleanup
- */
-
/*
* omap3isp_ccdc_init - CCDC module initialization.
* @dev: Device pointer specific to the OMAP3 ISP.
@@ -2238,6 +2252,7 @@ error:
int omap3isp_ccdc_init(struct isp_device *isp)
{
struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+ int ret;
spin_lock_init(&ccdc->lock);
init_waitqueue_head(&ccdc->wait);
@@ -2257,8 +2272,6 @@ int omap3isp_ccdc_init(struct isp_device *isp)
ccdc->syncif.fldout = 0;
ccdc->syncif.fldpol = 0;
ccdc->syncif.fldstat = 0;
- ccdc->syncif.hdpol = 0;
- ccdc->syncif.vdpol = 0;
ccdc->clamp.oblen = 0;
ccdc->clamp.dcsubval = 0;
@@ -2268,7 +2281,13 @@ int omap3isp_ccdc_init(struct isp_device *isp)
ccdc->update = OMAP3ISP_CCDC_BLCLAMP;
ccdc_apply_controls(ccdc);
- return ccdc_init_entities(ccdc);
+ ret = ccdc_init_entities(ccdc);
+ if (ret < 0) {
+ mutex_destroy(&ccdc->ioctl_lock);
+ return ret;
+ }
+
+ return 0;
}
/*
@@ -2279,6 +2298,9 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
{
struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+ omap3isp_video_cleanup(&ccdc->video_out);
+ media_entity_cleanup(&ccdc->subdev.entity);
+
/* Free LSC requests. As the CCDC is stopped there's no active request,
* so only the pending request and the free queue need to be handled.
*/
@@ -2287,5 +2309,7 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
if (ccdc->fpc.fpcaddr != 0)
- iommu_vfree(isp->iommu, ccdc->fpc.fpcaddr);
+ omap_iommu_vfree(isp->domain, isp->iommu, ccdc->fpc.fpcaddr);
+
+ mutex_destroy(&ccdc->ioctl_lock);
}
diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c
index 0e16cab8e089..904ca8c8b17f 100644
--- a/drivers/media/video/omap3isp/ispccp2.c
+++ b/drivers/media/video/omap3isp/ispccp2.c
@@ -30,6 +30,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
#include "isp.h"
#include "ispreg.h"
@@ -163,6 +164,9 @@ static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
int i;
+ if (enable && ccp2->vdds_csib)
+ regulator_enable(ccp2->vdds_csib);
+
/* Enable/Disable all the LCx channels */
for (i = 0; i < CCP2_LCx_CHANS_NUM; i++)
isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(i),
@@ -186,6 +190,9 @@ static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
ISPCCP2_LC01_IRQENABLE,
ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ);
}
+
+ if (!enable && ccp2->vdds_csib)
+ regulator_disable(ccp2->vdds_csib);
}
/*
@@ -236,9 +243,9 @@ static int ccp2_phyif_config(struct isp_ccp2_device *ccp2,
val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
if (!(val & ISPCCP2_CTRL_MODE)) {
- if (pdata->ccp2_mode)
+ if (pdata->ccp2_mode == ISP_CCP2_MODE_CCP2)
dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n");
- if (pdata->phy_layer == ISPCCP2_CTRL_PHY_SEL_STROBE)
+ if (pdata->phy_layer == ISP_CCP2_PHY_DATA_STROBE)
/* Strobe mode requires CCP2 */
return -EIO;
}
@@ -1025,6 +1032,48 @@ static const struct media_entity_operations ccp2_media_ops = {
};
/*
+ * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev
+ * @ccp2: Pointer to ISP CCP2 device
+ */
+void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2)
+{
+ v4l2_device_unregister_subdev(&ccp2->subdev);
+ omap3isp_video_unregister(&ccp2->video_in);
+}
+
+/*
+ * omap3isp_ccp2_register_entities - Register the subdev media entity
+ * @ccp2: Pointer to ISP CCP2 device
+ * @vdev: Pointer to v4l device
+ * return negative error code or zero on success
+ */
+
+int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &ccp2->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&ccp2->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_ccp2_unregister_entities(ccp2);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP ccp2 initialisation and cleanup
+ */
+
+/*
* ccp2_init_entities - Initialize ccp2 subdev and media entity.
* @ccp2: Pointer to ISP CCP2 device
* return negative error code or zero on success
@@ -1076,69 +1125,23 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
ret = omap3isp_video_init(&ccp2->video_in, "CCP2");
if (ret < 0)
- return ret;
+ goto error_video;
/* Connect the video node to the ccp2 subdev. */
ret = media_entity_create_link(&ccp2->video_in.video.entity, 0,
&ccp2->subdev.entity, CCP2_PAD_SINK, 0);
if (ret < 0)
- return ret;
+ goto error_link;
return 0;
-}
-/*
- * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev
- * @ccp2: Pointer to ISP CCP2 device
- */
-void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2)
-{
+error_link:
+ omap3isp_video_cleanup(&ccp2->video_in);
+error_video:
media_entity_cleanup(&ccp2->subdev.entity);
-
- v4l2_device_unregister_subdev(&ccp2->subdev);
- omap3isp_video_unregister(&ccp2->video_in);
-}
-
-/*
- * omap3isp_ccp2_register_entities - Register the subdev media entity
- * @ccp2: Pointer to ISP CCP2 device
- * @vdev: Pointer to v4l device
- * return negative error code or zero on success
- */
-
-int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &ccp2->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&ccp2->video_in, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_ccp2_unregister_entities(ccp2);
return ret;
}
-/* -----------------------------------------------------------------------------
- * ISP ccp2 initialisation and cleanup
- */
-
-/*
- * omap3isp_ccp2_cleanup - CCP2 un-initialization
- * @isp : Pointer to ISP device
- */
-void omap3isp_ccp2_cleanup(struct isp_device *isp)
-{
-}
-
/*
* omap3isp_ccp2_init - CCP2 initialization.
* @isp : Pointer to ISP device
@@ -1151,23 +1154,48 @@ int omap3isp_ccp2_init(struct isp_device *isp)
init_waitqueue_head(&ccp2->wait);
- /* On the OMAP36xx, the CCP2 uses the CSI PHY1 or PHY2, shared with
+ /*
+ * On the OMAP34xx the CSI1 receiver is operated in the CSIb IO
+ * complex, which is powered by vdds_csib power rail. Hence the
+ * request for the regulator.
+ *
+ * On the OMAP36xx, the CCP2 uses the CSI PHY1 or PHY2, shared with
* the CSI2c or CSI2a receivers. The PHY then needs to be explicitly
* configured.
*
* TODO: Don't hardcode the usage of PHY1 (shared with CSI2c).
*/
- if (isp->revision == ISP_REVISION_15_0)
+ if (isp->revision == ISP_REVISION_2_0) {
+ ccp2->vdds_csib = regulator_get(isp->dev, "vdds_csib");
+ if (IS_ERR(ccp2->vdds_csib)) {
+ dev_dbg(isp->dev,
+ "Could not get regulator vdds_csib\n");
+ ccp2->vdds_csib = NULL;
+ }
+ } else if (isp->revision == ISP_REVISION_15_0) {
ccp2->phy = &isp->isp_csiphy1;
+ }
ret = ccp2_init_entities(ccp2);
- if (ret < 0)
- goto out;
+ if (ret < 0) {
+ regulator_put(ccp2->vdds_csib);
+ return ret;
+ }
ccp2_reset(ccp2);
-out:
- if (ret)
- omap3isp_ccp2_cleanup(isp);
+ return 0;
+}
- return ret;
+/*
+ * omap3isp_ccp2_cleanup - CCP2 un-initialization
+ * @isp : Pointer to ISP device
+ */
+void omap3isp_ccp2_cleanup(struct isp_device *isp)
+{
+ struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
+
+ omap3isp_video_cleanup(&ccp2->video_in);
+ media_entity_cleanup(&ccp2->subdev.entity);
+
+ regulator_put(ccp2->vdds_csib);
}
diff --git a/drivers/media/video/omap3isp/ispccp2.h b/drivers/media/video/omap3isp/ispccp2.h
index 5505a86a9a74..6674e9de2cd7 100644
--- a/drivers/media/video/omap3isp/ispccp2.h
+++ b/drivers/media/video/omap3isp/ispccp2.h
@@ -81,6 +81,7 @@ struct isp_ccp2_device {
struct isp_interface_mem_config mem_cfg;
struct isp_video video_in;
struct isp_csiphy *phy;
+ struct regulator *vdds_csib;
unsigned int error;
enum isp_pipeline_stream_state state;
wait_queue_head_t wait;
diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c
index 69161a682b3d..0c5f1cb9d99d 100644
--- a/drivers/media/video/omap3isp/ispcsi2.c
+++ b/drivers/media/video/omap3isp/ispcsi2.c
@@ -1187,6 +1187,37 @@ static const struct media_entity_operations csi2_media_ops = {
.link_setup = csi2_link_setup,
};
+void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
+{
+ v4l2_device_unregister_subdev(&csi2->subdev);
+ omap3isp_video_unregister(&csi2->video_out);
+}
+
+int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&csi2->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_csi2_unregister_entities(csi2);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP CSI2 initialisation and cleanup
+ */
+
/*
* csi2_init_entities - Initialize subdev and media entity.
* @csi2: Pointer to csi2 structure.
@@ -1228,57 +1259,23 @@ static int csi2_init_entities(struct isp_csi2_device *csi2)
ret = omap3isp_video_init(&csi2->video_out, "CSI2a");
if (ret < 0)
- return ret;
+ goto error_video;
/* Connect the CSI2 subdev to the video node. */
ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
&csi2->video_out.video.entity, 0, 0);
if (ret < 0)
- return ret;
+ goto error_link;
return 0;
-}
-void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
-{
+error_link:
+ omap3isp_video_cleanup(&csi2->video_out);
+error_video:
media_entity_cleanup(&csi2->subdev.entity);
-
- v4l2_device_unregister_subdev(&csi2->subdev);
- omap3isp_video_unregister(&csi2->video_out);
-}
-
-int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&csi2->video_out, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_csi2_unregister_entities(csi2);
return ret;
}
-/* -----------------------------------------------------------------------------
- * ISP CSI2 initialisation and cleanup
- */
-
-/*
- * omap3isp_csi2_cleanup - Routine for module driver cleanup
- */
-void omap3isp_csi2_cleanup(struct isp_device *isp)
-{
-}
-
/*
* omap3isp_csi2_init - Routine for module driver init
*/
@@ -1298,7 +1295,7 @@ int omap3isp_csi2_init(struct isp_device *isp)
ret = csi2_init_entities(csi2a);
if (ret < 0)
- goto fail;
+ return ret;
if (isp->revision == ISP_REVISION_15_0) {
csi2c->isp = isp;
@@ -1311,7 +1308,15 @@ int omap3isp_csi2_init(struct isp_device *isp)
}
return 0;
-fail:
- omap3isp_csi2_cleanup(isp);
- return ret;
+}
+
+/*
+ * omap3isp_csi2_cleanup - Routine for module driver cleanup
+ */
+void omap3isp_csi2_cleanup(struct isp_device *isp)
+{
+ struct isp_csi2_device *csi2a = &isp->isp_csi2a;
+
+ omap3isp_video_cleanup(&csi2a->video_out);
+ media_entity_cleanup(&csi2a->subdev.entity);
}
diff --git a/drivers/media/video/omap3isp/isph3a_aewb.c b/drivers/media/video/omap3isp/isph3a_aewb.c
index 8068cefd8d89..a3c76bf18175 100644
--- a/drivers/media/video/omap3isp/isph3a_aewb.c
+++ b/drivers/media/video/omap3isp/isph3a_aewb.c
@@ -370,5 +370,5 @@ void omap3isp_h3a_aewb_cleanup(struct isp_device *isp)
{
kfree(isp->isp_aewb.priv);
kfree(isp->isp_aewb.recover_priv);
- omap3isp_stat_free(&isp->isp_aewb);
+ omap3isp_stat_cleanup(&isp->isp_aewb);
}
diff --git a/drivers/media/video/omap3isp/isph3a_af.c b/drivers/media/video/omap3isp/isph3a_af.c
index ba54d0acdecf..58e0bc414899 100644
--- a/drivers/media/video/omap3isp/isph3a_af.c
+++ b/drivers/media/video/omap3isp/isph3a_af.c
@@ -425,5 +425,5 @@ void omap3isp_h3a_af_cleanup(struct isp_device *isp)
{
kfree(isp->isp_af.priv);
kfree(isp->isp_af.recover_priv);
- omap3isp_stat_free(&isp->isp_af);
+ omap3isp_stat_cleanup(&isp->isp_af);
}
diff --git a/drivers/media/video/omap3isp/isphist.c b/drivers/media/video/omap3isp/isphist.c
index 1743856b30d1..1163907bcddc 100644
--- a/drivers/media/video/omap3isp/isphist.c
+++ b/drivers/media/video/omap3isp/isphist.c
@@ -516,5 +516,5 @@ void omap3isp_hist_cleanup(struct isp_device *isp)
if (HIST_USING_DMA(&isp->isp_hist))
omap_free_dma(isp->isp_hist.dma_ch);
kfree(isp->isp_hist.priv);
- omap3isp_stat_free(&isp->isp_hist);
+ omap3isp_stat_cleanup(&isp->isp_hist);
}
diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c
index aba537af87e4..ccb876fe023f 100644
--- a/drivers/media/video/omap3isp/isppreview.c
+++ b/drivers/media/video/omap3isp/isppreview.c
@@ -76,9 +76,51 @@ static struct omap3isp_prev_csc flr_prev_csc = {
#define DEF_DETECT_CORRECT_VAL 0xe
-#define PREV_MIN_WIDTH 64
-#define PREV_MIN_HEIGHT 8
-#define PREV_MAX_HEIGHT 16384
+/*
+ * Margins and image size limits.
+ *
+ * The preview engine crops several rows and columns internally depending on
+ * which filters are enabled. To avoid format changes when the filters are
+ * enabled or disabled (which would prevent them from being turned on or off
+ * during streaming), the driver assumes all the filters are enabled when
+ * computing sink crop and source format limits.
+ *
+ * If a filter is disabled, additional cropping is automatically added at the
+ * preview engine input by the driver to avoid overflow at line and frame end.
+ * This is completely transparent for applications.
+ *
+ * Median filter 4 pixels
+ * Noise filter,
+ * Faulty pixels correction 4 pixels, 4 lines
+ * CFA filter 4 pixels, 4 lines in Bayer mode
+ * 2 lines in other modes
+ * Color suppression 2 pixels
+ * or luma enhancement
+ * -------------------------------------------------------------
+ * Maximum total 14 pixels, 8 lines
+ *
+ * The color suppression and luma enhancement filters are applied after bayer to
+ * YUV conversion. They thus can crop one pixel on the left and one pixel on the
+ * right side of the image without changing the color pattern. When both those
+ * filters are disabled, the driver must crop the two pixels on the same side of
+ * the image to avoid changing the bayer pattern. The left margin is thus set to
+ * 8 pixels and the right margin to 6 pixels.
+ */
+
+#define PREV_MARGIN_LEFT 8
+#define PREV_MARGIN_RIGHT 6
+#define PREV_MARGIN_TOP 4
+#define PREV_MARGIN_BOTTOM 4
+
+#define PREV_MIN_IN_WIDTH 64
+#define PREV_MIN_IN_HEIGHT 8
+#define PREV_MAX_IN_HEIGHT 16384
+
+#define PREV_MIN_OUT_WIDTH 0
+#define PREV_MIN_OUT_HEIGHT 0
+#define PREV_MAX_OUT_WIDTH 1280
+#define PREV_MAX_OUT_WIDTH_ES2 3300
+#define PREV_MAX_OUT_WIDTH_3630 4096
/*
* Coeficient Tables for the submodules in Preview.
@@ -979,52 +1021,36 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average)
* enabled when reporting source pad formats to userspace. If this assumption is
* not true, rows and columns must be manually cropped at the preview engine
* input to avoid overflows at the end of lines and frames.
+ *
+ * See the explanation at the PREV_MARGIN_* definitions for more details.
*/
static void preview_config_input_size(struct isp_prev_device *prev)
{
struct isp_device *isp = to_isp_device(prev);
struct prev_params *params = &prev->params;
- struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK];
- unsigned int sph = 0;
- unsigned int eph = format->width - 1;
- unsigned int slv = 0;
- unsigned int elv = format->height - 1;
-
- if (prev->input == PREVIEW_INPUT_CCDC) {
- sph += 2;
- eph -= 2;
+ unsigned int sph = prev->crop.left;
+ unsigned int eph = prev->crop.left + prev->crop.width - 1;
+ unsigned int slv = prev->crop.top;
+ unsigned int elv = prev->crop.top + prev->crop.height - 1;
+
+ if (params->features & PREV_CFA) {
+ sph -= 2;
+ eph += 2;
+ slv -= 2;
+ elv += 2;
}
-
- /*
- * Median filter 4 pixels
- * Noise filter 4 pixels, 4 lines
- * or faulty pixels correction
- * CFA filter 4 pixels, 4 lines in Bayer mode
- * 2 lines in other modes
- * Color suppression 2 pixels
- * or luma enhancement
- * -------------------------------------------------------------
- * Maximum total 14 pixels, 8 lines
- */
-
- if (!(params->features & PREV_CFA)) {
- sph += 2;
- eph -= 2;
- slv += 2;
- elv -= 2;
+ if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) {
+ sph -= 2;
+ eph += 2;
+ slv -= 2;
+ elv += 2;
}
- if (!(params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER))) {
- sph += 2;
- eph -= 2;
- slv += 2;
- elv -= 2;
+ if (params->features & PREV_HORZ_MEDIAN_FILTER) {
+ sph -= 2;
+ eph += 2;
}
- if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) {
- sph += 2;
- eph -= 2;
- }
- if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE)))
- sph += 2;
+ if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))
+ sph -= 2;
isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO);
@@ -1228,7 +1254,6 @@ static void preview_init_params(struct isp_prev_device *prev)
/* Init values */
params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS;
params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS;
- params->average = NO_AVE;
params->cfa.format = OMAP3ISP_CFAFMT_BAYER;
memcpy(params->cfa.table, cfa_coef_table,
sizeof(params->cfa.table));
@@ -1281,14 +1306,14 @@ static unsigned int preview_max_out_width(struct isp_prev_device *prev)
switch (isp->revision) {
case ISP_REVISION_1_0:
- return ISPPRV_MAXOUTPUT_WIDTH;
+ return PREV_MAX_OUT_WIDTH;
case ISP_REVISION_2_0:
default:
- return ISPPRV_MAXOUTPUT_WIDTH_ES2;
+ return PREV_MAX_OUT_WIDTH_ES2;
case ISP_REVISION_15_0:
- return ISPPRV_MAXOUTPUT_WIDTH_3630;
+ return PREV_MAX_OUT_WIDTH_3630;
}
}
@@ -1296,8 +1321,6 @@ static void preview_configure(struct isp_prev_device *prev)
{
struct isp_device *isp = to_isp_device(prev);
struct v4l2_mbus_framefmt *format;
- unsigned int max_out_width;
- unsigned int format_avg;
preview_setup_hw(prev);
@@ -1335,10 +1358,7 @@ static void preview_configure(struct isp_prev_device *prev)
preview_config_outlineoffset(prev,
ALIGN(format->width, 0x10) * 2);
- max_out_width = preview_max_out_width(prev);
-
- format_avg = fls(DIV_ROUND_UP(format->width, max_out_width) - 1);
- preview_config_averager(prev, format_avg);
+ preview_config_averager(prev, 0);
preview_config_ycpos(prev, format->code);
}
@@ -1597,6 +1617,16 @@ __preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
return &prev->formats[pad];
}
+static struct v4l2_rect *
+__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK);
+ else
+ return &prev->crop;
+}
+
/* previewer format descriptions */
static const unsigned int preview_input_fmts[] = {
V4L2_MBUS_FMT_SGRBG10_1X10,
@@ -1611,24 +1641,25 @@ static const unsigned int preview_output_fmts[] = {
};
/*
- * preview_try_format - Handle try format by pad subdev method
- * @prev: ISP preview device
- * @fh : V4L2 subdev file handle
- * @pad: pad num
- * @fmt: pointer to v4l2 format structure
+ * preview_try_format - Validate a format
+ * @prev: ISP preview engine
+ * @fh: V4L2 subdev file handle
+ * @pad: pad number
+ * @fmt: format to be validated
+ * @which: try/active format selector
+ *
+ * Validate and adjust the given format for the given pad based on the preview
+ * engine limits and the format and crop rectangles on other pads.
*/
static void preview_try_format(struct isp_prev_device *prev,
struct v4l2_subdev_fh *fh, unsigned int pad,
struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
- struct v4l2_mbus_framefmt *format;
- unsigned int max_out_width;
enum v4l2_mbus_pixelcode pixelcode;
+ struct v4l2_rect *crop;
unsigned int i;
- max_out_width = preview_max_out_width(prev);
-
switch (pad) {
case PREV_PAD_SINK:
/* When reading data from the CCDC, the input size has already
@@ -1641,10 +1672,11 @@ static void preview_try_format(struct isp_prev_device *prev,
* filter array interpolation.
*/
if (prev->input == PREVIEW_INPUT_MEMORY) {
- fmt->width = clamp_t(u32, fmt->width, PREV_MIN_WIDTH,
- max_out_width * 8);
- fmt->height = clamp_t(u32, fmt->height, PREV_MIN_HEIGHT,
- PREV_MAX_HEIGHT);
+ fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH,
+ preview_max_out_width(prev));
+ fmt->height = clamp_t(u32, fmt->height,
+ PREV_MIN_IN_HEIGHT,
+ PREV_MAX_IN_HEIGHT);
}
fmt->colorspace = V4L2_COLORSPACE_SRGB;
@@ -1661,15 +1693,8 @@ static void preview_try_format(struct isp_prev_device *prev,
case PREV_PAD_SOURCE:
pixelcode = fmt->code;
- format = __preview_get_format(prev, fh, PREV_PAD_SINK, which);
- memcpy(fmt, format, sizeof(*fmt));
+ *fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);
- /* The preview module output size is configurable through the
- * input interface (horizontal and vertical cropping) and the
- * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). In
- * spite of this, hardcode the output size to the biggest
- * possible value for simplicity reasons.
- */
switch (pixelcode) {
case V4L2_MBUS_FMT_YUYV8_1X16:
case V4L2_MBUS_FMT_UYVY8_1X16:
@@ -1681,31 +1706,14 @@ static void preview_try_format(struct isp_prev_device *prev,
break;
}
- /* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped
- * from the left and right sides when the input source is the
- * CCDC. This seems not to be needed in practice, investigation
- * is required.
- */
- if (prev->input == PREVIEW_INPUT_CCDC)
- fmt->width -= 4;
-
- /* The preview module can output a maximum of 3312 pixels
- * horizontally due to fixed memory-line sizes. Compute the
- * horizontal averaging factor accordingly. Note that the limit
- * applies to the noise filter and CFA interpolation blocks, so
- * it doesn't take cropping by further blocks into account.
- *
- * ES 1.0 hardware revision is limited to 1280 pixels
- * horizontally.
- */
- fmt->width >>= fls(DIV_ROUND_UP(fmt->width, max_out_width) - 1);
-
- /* Assume that all blocks are enabled and crop pixels and lines
- * accordingly. See preview_config_input_size() for more
- * information.
+ /* The preview module output size is configurable through the
+ * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). This
+ * is not supported yet, hardcode the output size to the crop
+ * rectangle size.
*/
- fmt->width -= 14;
- fmt->height -= 8;
+ crop = __preview_get_crop(prev, fh, which);
+ fmt->width = crop->width;
+ fmt->height = crop->height;
fmt->colorspace = V4L2_COLORSPACE_JPEG;
break;
@@ -1715,6 +1723,49 @@ static void preview_try_format(struct isp_prev_device *prev,
}
/*
+ * preview_try_crop - Validate a crop rectangle
+ * @prev: ISP preview engine
+ * @sink: format on the sink pad
+ * @crop: crop rectangle to be validated
+ *
+ * The preview engine crops lines and columns for its internal operation,
+ * depending on which filters are enabled. Enforce minimum crop margins to
+ * handle that transparently for userspace.
+ *
+ * See the explanation at the PREV_MARGIN_* definitions for more details.
+ */
+static void preview_try_crop(struct isp_prev_device *prev,
+ const struct v4l2_mbus_framefmt *sink,
+ struct v4l2_rect *crop)
+{
+ unsigned int left = PREV_MARGIN_LEFT;
+ unsigned int right = sink->width - PREV_MARGIN_RIGHT;
+ unsigned int top = PREV_MARGIN_TOP;
+ unsigned int bottom = sink->height - PREV_MARGIN_BOTTOM;
+
+ /* When processing data on-the-fly from the CCDC, at least 2 pixels must
+ * be cropped from the left and right sides of the image. As we don't
+ * know which filters will be enabled, increase the left and right
+ * margins by two.
+ */
+ if (prev->input == PREVIEW_INPUT_CCDC) {
+ left += 2;
+ right -= 2;
+ }
+
+ /* Restrict left/top to even values to keep the Bayer pattern. */
+ crop->left &= ~1;
+ crop->top &= ~1;
+
+ crop->left = clamp_t(u32, crop->left, left, right - PREV_MIN_OUT_WIDTH);
+ crop->top = clamp_t(u32, crop->top, top, bottom - PREV_MIN_OUT_HEIGHT);
+ crop->width = clamp_t(u32, crop->width, PREV_MIN_OUT_WIDTH,
+ right - crop->left);
+ crop->height = clamp_t(u32, crop->height, PREV_MIN_OUT_HEIGHT,
+ bottom - crop->top);
+}
+
+/*
* preview_enum_mbus_code - Handle pixel format enumeration
* @sd : pointer to v4l2 subdev structure
* @fh : V4L2 subdev file handle
@@ -1776,6 +1827,60 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd,
}
/*
+ * preview_get_crop - Retrieve the crop rectangle on a pad
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @crop: crop rectangle
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+
+ /* Cropping is only supported on the sink pad. */
+ if (crop->pad != PREV_PAD_SINK)
+ return -EINVAL;
+
+ crop->rect = *__preview_get_crop(prev, fh, crop->which);
+ return 0;
+}
+
+/*
+ * preview_set_crop - Retrieve the crop rectangle on a pad
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @crop: crop rectangle
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ /* Cropping is only supported on the sink pad. */
+ if (crop->pad != PREV_PAD_SINK)
+ return -EINVAL;
+
+ /* The crop rectangle can't be changed while streaming. */
+ if (prev->state != ISP_PIPELINE_STREAM_STOPPED)
+ return -EBUSY;
+
+ format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which);
+ preview_try_crop(prev, format, &crop->rect);
+ *__preview_get_crop(prev, fh, crop->which) = crop->rect;
+
+ /* Update the source format. */
+ format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which);
+ preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which);
+
+ return 0;
+}
+
+/*
* preview_get_format - Handle get format by pads subdev method
* @sd : pointer to v4l2 subdev structure
* @fh : V4L2 subdev file handle
@@ -1808,6 +1913,7 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
{
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
if (format == NULL)
@@ -1818,9 +1924,18 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
/* Propagate the format from sink to source */
if (fmt->pad == PREV_PAD_SINK) {
+ /* Reset the crop rectangle. */
+ crop = __preview_get_crop(prev, fh, fmt->which);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+
+ preview_try_crop(prev, &fmt->format, crop);
+
+ /* Update the source format. */
format = __preview_get_format(prev, fh, PREV_PAD_SOURCE,
fmt->which);
- *format = fmt->format;
preview_try_format(prev, fh, PREV_PAD_SOURCE, format,
fmt->which);
}
@@ -1869,6 +1984,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = {
.enum_frame_size = preview_enum_frame_size,
.get_fmt = preview_get_format,
.set_fmt = preview_set_format,
+ .get_crop = preview_get_crop,
+ .set_crop = preview_set_crop,
};
/* subdev operations */
@@ -1966,8 +2083,44 @@ static const struct media_entity_operations preview_media_ops = {
.link_setup = preview_link_setup,
};
+void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
+{
+ v4l2_device_unregister_subdev(&prev->subdev);
+ omap3isp_video_unregister(&prev->video_in);
+ omap3isp_video_unregister(&prev->video_out);
+}
+
+int omap3isp_preview_register_entities(struct isp_prev_device *prev,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &prev->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&prev->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&prev->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_preview_unregister_entities(prev);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP previewer initialisation and cleanup
+ */
+
/*
- * review_init_entities - Initialize subdev and media entity.
+ * preview_init_entities - Initialize subdev and media entity.
* @prev : Pointer to preview structure
* return -ENOMEM or zero on success
*/
@@ -2024,69 +2177,34 @@ static int preview_init_entities(struct isp_prev_device *prev)
ret = omap3isp_video_init(&prev->video_in, "preview");
if (ret < 0)
- return ret;
+ goto error_video_in;
ret = omap3isp_video_init(&prev->video_out, "preview");
if (ret < 0)
- return ret;
+ goto error_video_out;
/* Connect the video nodes to the previewer subdev. */
ret = media_entity_create_link(&prev->video_in.video.entity, 0,
&prev->subdev.entity, PREV_PAD_SINK, 0);
if (ret < 0)
- return ret;
+ goto error_link;
ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE,
&prev->video_out.video.entity, 0, 0);
if (ret < 0)
- return ret;
+ goto error_link;
return 0;
-}
-void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
-{
+error_link:
+ omap3isp_video_cleanup(&prev->video_out);
+error_video_out:
+ omap3isp_video_cleanup(&prev->video_in);
+error_video_in:
media_entity_cleanup(&prev->subdev.entity);
-
- v4l2_device_unregister_subdev(&prev->subdev);
- v4l2_ctrl_handler_free(&prev->ctrls);
- omap3isp_video_unregister(&prev->video_in);
- omap3isp_video_unregister(&prev->video_out);
-}
-
-int omap3isp_preview_register_entities(struct isp_prev_device *prev,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &prev->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&prev->video_in, vdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&prev->video_out, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_preview_unregister_entities(prev);
return ret;
}
-/* -----------------------------------------------------------------------------
- * ISP previewer initialisation and cleanup
- */
-
-void omap3isp_preview_cleanup(struct isp_device *isp)
-{
-}
-
/*
* isp_preview_init - Previewer initialization.
* @dev : Pointer to ISP device
@@ -2095,19 +2213,20 @@ void omap3isp_preview_cleanup(struct isp_device *isp)
int omap3isp_preview_init(struct isp_device *isp)
{
struct isp_prev_device *prev = &isp->isp_prev;
- int ret;
spin_lock_init(&prev->lock);
init_waitqueue_head(&prev->wait);
preview_init_params(prev);
- ret = preview_init_entities(prev);
- if (ret < 0)
- goto out;
+ return preview_init_entities(prev);
+}
-out:
- if (ret)
- omap3isp_preview_cleanup(isp);
+void omap3isp_preview_cleanup(struct isp_device *isp)
+{
+ struct isp_prev_device *prev = &isp->isp_prev;
- return ret;
+ v4l2_ctrl_handler_free(&prev->ctrls);
+ omap3isp_video_cleanup(&prev->video_in);
+ omap3isp_video_cleanup(&prev->video_out);
+ media_entity_cleanup(&prev->subdev.entity);
}
diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h
index fa943bd05c7f..f54e775c2df4 100644
--- a/drivers/media/video/omap3isp/isppreview.h
+++ b/drivers/media/video/omap3isp/isppreview.h
@@ -45,11 +45,6 @@
#define ISPPRV_CONTRAST_HIGH 0xFF
#define ISPPRV_CONTRAST_UNITS 0x1
-#define NO_AVE 0x0
-#define AVE_2_PIX 0x1
-#define AVE_4_PIX 0x2
-#define AVE_8_PIX 0x3
-
/* Features list */
#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH
#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW
@@ -106,7 +101,6 @@ enum preview_ycpos_mode {
* @rgb2ycbcr: RGB to ycbcr parameters.
* @hmed: Horizontal median filter.
* @yclimit: YC limits parameters.
- * @average: Downsampling rate for averager.
* @contrast: Contrast.
* @brightness: Brightness.
*/
@@ -124,7 +118,6 @@ struct prev_params {
struct omap3isp_prev_csc rgb2ycbcr;
struct omap3isp_prev_hmed hmed;
struct omap3isp_prev_yclimit yclimit;
- u8 average;
u8 contrast;
u8 brightness;
};
@@ -159,6 +152,7 @@ struct isptables_update {
* @subdev: V4L2 subdevice
* @pads: Media entity pads
* @formats: Active formats at the subdev pad
+ * @crop: Active crop rectangle
* @input: Module currently connected to the input pad
* @output: Bitmask of the active output
* @video_in: Input video entity
@@ -177,6 +171,7 @@ struct isp_prev_device {
struct v4l2_subdev subdev;
struct media_pad pads[PREV_PADS_NUM];
struct v4l2_mbus_framefmt formats[PREV_PADS_NUM];
+ struct v4l2_rect crop;
struct v4l2_ctrl_handler ctrls;
diff --git a/drivers/media/video/omap3isp/ispqueue.c b/drivers/media/video/omap3isp/ispqueue.c
index 9c317148205f..9bebb1e57aab 100644
--- a/drivers/media/video/omap3isp/ispqueue.c
+++ b/drivers/media/video/omap3isp/ispqueue.c
@@ -868,6 +868,10 @@ int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
goto done;
if (vbuf->memory == V4L2_MEMORY_USERPTR &&
+ vbuf->length < buf->vbuf.length)
+ goto done;
+
+ if (vbuf->memory == V4L2_MEMORY_USERPTR &&
vbuf->m.userptr != buf->vbuf.m.userptr) {
isp_video_buffer_cleanup(buf);
buf->vbuf.m.userptr = vbuf->m.userptr;
diff --git a/drivers/media/video/omap3isp/ispreg.h b/drivers/media/video/omap3isp/ispreg.h
index 69f6af6f6b9c..084ea77d65a7 100644
--- a/drivers/media/video/omap3isp/ispreg.h
+++ b/drivers/media/video/omap3isp/ispreg.h
@@ -402,9 +402,6 @@
#define ISPPRV_YENH_TABLE_ADDR 0x1000
#define ISPPRV_CFA_TABLE_ADDR 0x1400
-#define ISPPRV_MAXOUTPUT_WIDTH 1280
-#define ISPPRV_MAXOUTPUT_WIDTH_ES2 3300
-#define ISPPRV_MAXOUTPUT_WIDTH_3630 4096
#define ISPRSZ_MIN_OUTPUT 64
#define ISPRSZ_MAX_OUTPUT 3312
diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c
index 0bb0f8cd36f5..50e593bfcfaf 100644
--- a/drivers/media/video/omap3isp/ispresizer.c
+++ b/drivers/media/video/omap3isp/ispresizer.c
@@ -1608,6 +1608,42 @@ static const struct media_entity_operations resizer_media_ops = {
.link_setup = resizer_link_setup,
};
+void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
+{
+ v4l2_device_unregister_subdev(&res->subdev);
+ omap3isp_video_unregister(&res->video_in);
+ omap3isp_video_unregister(&res->video_out);
+}
+
+int omap3isp_resizer_register_entities(struct isp_res_device *res,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &res->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&res->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&res->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_resizer_unregister_entities(res);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP resizer initialization and cleanup
+ */
+
/*
* resizer_init_entities - Initialize resizer subdev and media entity.
* @res : Pointer to resizer device structure
@@ -1652,68 +1688,34 @@ static int resizer_init_entities(struct isp_res_device *res)
ret = omap3isp_video_init(&res->video_in, "resizer");
if (ret < 0)
- return ret;
+ goto error_video_in;
ret = omap3isp_video_init(&res->video_out, "resizer");
if (ret < 0)
- return ret;
+ goto error_video_out;
/* Connect the video nodes to the resizer subdev. */
ret = media_entity_create_link(&res->video_in.video.entity, 0,
&res->subdev.entity, RESZ_PAD_SINK, 0);
if (ret < 0)
- return ret;
+ goto error_link;
ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE,
&res->video_out.video.entity, 0, 0);
if (ret < 0)
- return ret;
+ goto error_link;
return 0;
-}
-void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
-{
+error_link:
+ omap3isp_video_cleanup(&res->video_out);
+error_video_out:
+ omap3isp_video_cleanup(&res->video_in);
+error_video_in:
media_entity_cleanup(&res->subdev.entity);
-
- v4l2_device_unregister_subdev(&res->subdev);
- omap3isp_video_unregister(&res->video_in);
- omap3isp_video_unregister(&res->video_out);
-}
-
-int omap3isp_resizer_register_entities(struct isp_res_device *res,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &res->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&res->video_in, vdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&res->video_out, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_resizer_unregister_entities(res);
return ret;
}
-/* -----------------------------------------------------------------------------
- * ISP resizer initialization and cleanup
- */
-
-void omap3isp_resizer_cleanup(struct isp_device *isp)
-{
-}
-
/*
* isp_resizer_init - Resizer initialization.
* @isp : Pointer to ISP device
@@ -1722,17 +1724,17 @@ void omap3isp_resizer_cleanup(struct isp_device *isp)
int omap3isp_resizer_init(struct isp_device *isp)
{
struct isp_res_device *res = &isp->isp_res;
- int ret;
init_waitqueue_head(&res->wait);
atomic_set(&res->stopping, 0);
- ret = resizer_init_entities(res);
- if (ret < 0)
- goto out;
+ return resizer_init_entities(res);
+}
-out:
- if (ret)
- omap3isp_resizer_cleanup(isp);
+void omap3isp_resizer_cleanup(struct isp_device *isp)
+{
+ struct isp_res_device *res = &isp->isp_res;
- return ret;
+ omap3isp_video_cleanup(&res->video_in);
+ omap3isp_video_cleanup(&res->video_out);
+ media_entity_cleanup(&res->subdev.entity);
}
diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c
index b44cb685236a..68d539456c55 100644
--- a/drivers/media/video/omap3isp/ispstat.c
+++ b/drivers/media/video/omap3isp/ispstat.c
@@ -366,7 +366,8 @@ static void isp_stat_bufs_free(struct ispstat *stat)
dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl,
buf->iovm->sgt->nents,
DMA_FROM_DEVICE);
- iommu_vfree(isp->iommu, buf->iommu_addr);
+ omap_iommu_vfree(isp->domain, isp->iommu,
+ buf->iommu_addr);
} else {
if (!buf->virt_addr)
continue;
@@ -399,8 +400,8 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
struct iovm_struct *iovm;
WARN_ON(buf->dma_addr);
- buf->iommu_addr = iommu_vmalloc(isp->iommu, 0, size,
- IOMMU_FLAG);
+ buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->iommu, 0,
+ size, IOMMU_FLAG);
if (IS_ERR((void *)buf->iommu_addr)) {
dev_err(stat->isp->dev,
"%s: Can't acquire memory for "
@@ -409,7 +410,7 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
return -ENOMEM;
}
- iovm = find_iovm_area(isp->iommu, buf->iommu_addr);
+ iovm = omap_find_iovm_area(isp->iommu, buf->iommu_addr);
if (!iovm ||
!dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents,
DMA_FROM_DEVICE)) {
@@ -418,7 +419,7 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
}
buf->iovm = iovm;
- buf->virt_addr = da_to_va(stat->isp->iommu,
+ buf->virt_addr = omap_da_to_va(stat->isp->iommu,
(u32)buf->iommu_addr);
buf->empty = 1;
dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
@@ -1022,25 +1023,6 @@ void omap3isp_stat_dma_isr(struct ispstat *stat)
__stat_isr(stat, 1);
}
-static int isp_stat_init_entities(struct ispstat *stat, const char *name,
- const struct v4l2_subdev_ops *sd_ops)
-{
- struct v4l2_subdev *subdev = &stat->subdev;
- struct media_entity *me = &subdev->entity;
-
- v4l2_subdev_init(subdev, sd_ops);
- snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
- subdev->grp_id = 1 << 16; /* group ID for isp subdevs */
- subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
- subdev->nevents = STAT_NEVENTS;
- v4l2_set_subdevdata(subdev, stat);
-
- stat->pad.flags = MEDIA_PAD_FL_SINK;
- me->ops = NULL;
-
- return media_entity_init(me, 1, &stat->pad, 0);
-}
-
int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
@@ -1050,7 +1032,7 @@ int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
if (sub->type != stat->event_type)
return -EINVAL;
- return v4l2_event_subscribe(fh, sub);
+ return v4l2_event_subscribe(fh, sub, STAT_NEVENTS);
}
int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
@@ -1062,7 +1044,6 @@ int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
void omap3isp_stat_unregister_entities(struct ispstat *stat)
{
- media_entity_cleanup(&stat->subdev.entity);
v4l2_device_unregister_subdev(&stat->subdev);
}
@@ -1072,21 +1053,50 @@ int omap3isp_stat_register_entities(struct ispstat *stat,
return v4l2_device_register_subdev(vdev, &stat->subdev);
}
+static int isp_stat_init_entities(struct ispstat *stat, const char *name,
+ const struct v4l2_subdev_ops *sd_ops)
+{
+ struct v4l2_subdev *subdev = &stat->subdev;
+ struct media_entity *me = &subdev->entity;
+
+ v4l2_subdev_init(subdev, sd_ops);
+ snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
+ subdev->grp_id = 1 << 16; /* group ID for isp subdevs */
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+ v4l2_set_subdevdata(subdev, stat);
+
+ stat->pad.flags = MEDIA_PAD_FL_SINK;
+ me->ops = NULL;
+
+ return media_entity_init(me, 1, &stat->pad, 0);
+}
+
int omap3isp_stat_init(struct ispstat *stat, const char *name,
const struct v4l2_subdev_ops *sd_ops)
{
+ int ret;
+
stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL);
if (!stat->buf)
return -ENOMEM;
+
isp_stat_buf_clear(stat);
mutex_init(&stat->ioctl_lock);
atomic_set(&stat->buf_err, 0);
- return isp_stat_init_entities(stat, name, sd_ops);
+ ret = isp_stat_init_entities(stat, name, sd_ops);
+ if (ret < 0) {
+ mutex_destroy(&stat->ioctl_lock);
+ kfree(stat->buf);
+ }
+
+ return ret;
}
-void omap3isp_stat_free(struct ispstat *stat)
+void omap3isp_stat_cleanup(struct ispstat *stat)
{
+ media_entity_cleanup(&stat->subdev.entity);
+ mutex_destroy(&stat->ioctl_lock);
isp_stat_bufs_free(stat);
kfree(stat->buf);
}
diff --git a/drivers/media/video/omap3isp/ispstat.h b/drivers/media/video/omap3isp/ispstat.h
index d86da94fa50d..9b7c8654dc8a 100644
--- a/drivers/media/video/omap3isp/ispstat.h
+++ b/drivers/media/video/omap3isp/ispstat.h
@@ -144,7 +144,7 @@ int omap3isp_stat_request_statistics(struct ispstat *stat,
struct omap3isp_stat_data *data);
int omap3isp_stat_init(struct ispstat *stat, const char *name,
const struct v4l2_subdev_ops *sd_ops);
-void omap3isp_stat_free(struct ispstat *stat);
+void omap3isp_stat_cleanup(struct ispstat *stat);
int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub);
diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c
index 9cd8f1aa567b..d1000723c5ae 100644
--- a/drivers/media/video/omap3isp/ispvideo.c
+++ b/drivers/media/video/omap3isp/ispvideo.c
@@ -278,7 +278,8 @@ isp_video_far_end(struct isp_video *video)
* limits reported by every block in the pipeline.
*
* Return 0 if all formats match, or -EPIPE if at least one link is found with
- * different formats on its two ends.
+ * different formats on its two ends or if the pipeline doesn't start with a
+ * video source (either a subdev with no input pad, or a non-subdev entity).
*/
static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
{
@@ -329,10 +330,15 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
* in the middle of it. */
shifter_link = subdev == &isp->isp_ccdc.subdev;
- /* Retrieve the source format */
+ /* Retrieve the source format. Return an error if no source
+ * entity can be found, and stop checking the pipeline if the
+ * source entity isn't a subdev.
+ */
pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (pad == NULL)
+ return -EPIPE;
+
+ if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
subdev = media_entity_to_v4l2_subdev(pad->entity);
@@ -446,7 +452,7 @@ ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen)
sgt->nents = sglen;
sgt->orig_nents = sglen;
- da = iommu_vmap(isp->iommu, 0, sgt, IOMMU_FLAG);
+ da = omap_iommu_vmap(isp->domain, isp->iommu, 0, sgt, IOMMU_FLAG);
if (IS_ERR_VALUE(da))
kfree(sgt);
@@ -462,7 +468,7 @@ static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da)
{
struct sg_table *sgt;
- sgt = iommu_vunmap(isp->iommu, (u32)da);
+ sgt = omap_iommu_vunmap(isp->domain, isp->iommu, (u32)da);
kfree(sgt);
}
@@ -695,7 +701,6 @@ isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
strlcpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver));
strlcpy(cap->card, video->video.name, sizeof(cap->card));
strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
- cap->version = ISP_VIDEO_DRIVER_VERSION;
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
@@ -1051,6 +1056,14 @@ error:
if (video->isp->pdata->set_constraints)
video->isp->pdata->set_constraints(video->isp, false);
media_entity_pipeline_stop(&video->video.entity);
+ /* The DMA queue must be emptied here, otherwise CCDC interrupts
+ * that will get triggered the next time the CCDC is powered up
+ * will try to access buffers that might have been freed but
+ * still present in the DMA queue. This can easily get triggered
+ * if the above omap3isp_pipeline_set_stream() call fails on a
+ * system with a free-running sensor.
+ */
+ INIT_LIST_HEAD(&video->dmaqueue);
video->queue = NULL;
}
@@ -1312,6 +1325,13 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
return 0;
}
+void omap3isp_video_cleanup(struct isp_video *video)
+{
+ media_entity_cleanup(&video->video.entity);
+ mutex_destroy(&video->stream_lock);
+ mutex_destroy(&video->mutex);
+}
+
int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
{
int ret;
@@ -1328,8 +1348,6 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
void omap3isp_video_unregister(struct isp_video *video)
{
- if (video_is_registered(&video->video)) {
- media_entity_cleanup(&video->video.entity);
+ if (video_is_registered(&video->video))
video_unregister_device(&video->video);
- }
}
diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h
index 911bea64e78a..08cbfa144e6e 100644
--- a/drivers/media/video/omap3isp/ispvideo.h
+++ b/drivers/media/video/omap3isp/ispvideo.h
@@ -27,7 +27,6 @@
#define OMAP3_ISP_VIDEO_H
#include <linux/v4l2-mediabus.h>
-#include <linux/version.h>
#include <media/media-entity.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-fh.h>
@@ -35,7 +34,7 @@
#include "ispqueue.h"
#define ISP_VIDEO_DRIVER_NAME "ispvideo"
-#define ISP_VIDEO_DRIVER_VERSION KERNEL_VERSION(0, 0, 1)
+#define ISP_VIDEO_DRIVER_VERSION "0.0.2"
struct isp_device;
struct isp_video;
@@ -191,6 +190,7 @@ struct isp_video_fh {
container_of(q, struct isp_video_fh, queue)
int omap3isp_video_init(struct isp_video *video, const char *name);
+void omap3isp_video_cleanup(struct isp_video *video);
int omap3isp_video_register(struct isp_video *video,
struct v4l2_device *vdev);
void omap3isp_video_unregister(struct isp_video *video);
diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c
index 0cea0cf36679..b5247cb64fde 100644
--- a/drivers/media/video/ov2640.c
+++ b/drivers/media/video/ov2640.c
@@ -18,11 +18,13 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/v4l2-ctrls.h>
#define VAL_SET(x, mask, rshift, lshift) \
((((x) >> rshift) & mask) << lshift)
@@ -299,12 +301,10 @@ struct ov2640_win_size {
struct ov2640_priv {
struct v4l2_subdev subdev;
- struct ov2640_camera_info *info;
+ struct v4l2_ctrl_handler hdl;
enum v4l2_mbus_pixelcode cfmt_code;
const struct ov2640_win_size *win;
int model;
- u16 flag_vflip:1;
- u16 flag_hflip:1;
};
/*
@@ -610,29 +610,6 @@ static enum v4l2_mbus_pixelcode ov2640_codes[] = {
};
/*
- * Supported controls
- */
-static const struct v4l2_queryctrl ov2640_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
-/*
* General functions
*/
static struct ov2640_priv *to_ov2640(const struct i2c_client *client)
@@ -701,81 +678,23 @@ static int ov2640_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int ov2640_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
-
- /* Only one width bit may be set */
- if (!is_power_of_2(width_flag))
- return -EINVAL;
-
- if (icl->set_bus_param)
- return icl->set_bus_param(icl, width_flag);
-
- /*
- * Without board specific bus width settings we support only the
- * sensors native bus width witch are tested working
- */
- if (width_flag & (SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8))
- return 0;
-
- return 0;
-}
-
-static unsigned long ov2640_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH;
-
- if (icl->query_bus_param)
- flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
- else
- flags |= SOCAM_DATAWIDTH_10;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int ov2640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov2640_priv *priv = to_ov2640(client);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- ctrl->value = priv->flag_vflip;
- break;
- case V4L2_CID_HFLIP:
- ctrl->value = priv->flag_hflip;
- break;
- }
- return 0;
-}
-
-static int ov2640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd =
+ &container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov2640_priv *priv = to_ov2640(client);
- int ret = 0;
u8 val;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- val = ctrl->value ? REG04_VFLIP_IMG : 0x00;
- priv->flag_vflip = ctrl->value ? 1 : 0;
- ret = ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
- break;
+ val = ctrl->val ? REG04_VFLIP_IMG : 0x00;
+ return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
case V4L2_CID_HFLIP:
- val = ctrl->value ? REG04_HFLIP_IMG : 0x00;
- priv->flag_hflip = ctrl->value ? 1 : 0;
- ret = ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
- break;
+ val = ctrl->val ? REG04_HFLIP_IMG : 0x00;
+ return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
}
- return ret;
+ return -EINVAL;
}
static int ov2640_g_chip_ident(struct v4l2_subdev *sd,
@@ -1023,8 +942,7 @@ static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-static int ov2640_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int ov2640_video_probe(struct i2c_client *client)
{
struct ov2640_priv *priv = to_ov2640(client);
u8 pid, ver, midh, midl;
@@ -1032,17 +950,6 @@ static int ov2640_video_probe(struct soc_camera_device *icd,
int ret;
/*
- * we must have a parent by now. And it cannot be a wrong one.
- * So this entire test is completely redundant.
- */
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface) {
- dev_err(&client->dev, "Parent missing or invalid!\n");
- ret = -ENODEV;
- goto err;
- }
-
- /*
* check and show product ID and manufacturer ID
*/
i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS);
@@ -1067,22 +974,17 @@ static int ov2640_video_probe(struct soc_camera_device *icd,
"%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
devname, pid, ver, midh, midl);
- return 0;
+ return v4l2_ctrl_handler_setup(&priv->hdl);
err:
return ret;
}
-static struct soc_camera_ops ov2640_ops = {
- .set_bus_param = ov2640_set_bus_param,
- .query_bus_param = ov2640_query_bus_param,
- .controls = ov2640_controls,
- .num_controls = ARRAY_SIZE(ov2640_controls),
+static const struct v4l2_ctrl_ops ov2640_ctrl_ops = {
+ .s_ctrl = ov2640_s_ctrl,
};
static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
- .g_ctrl = ov2640_g_ctrl,
- .s_ctrl = ov2640_s_ctrl,
.g_chip_ident = ov2640_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov2640_g_register,
@@ -1090,6 +992,21 @@ static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
#endif
};
+static int ov2640_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
.s_stream = ov2640_s_stream,
.g_mbus_fmt = ov2640_g_fmt,
@@ -1098,6 +1015,7 @@ static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
.cropcap = ov2640_cropcap,
.g_crop = ov2640_g_crop,
.enum_mbus_fmt = ov2640_enum_fmt,
+ .g_mbus_config = ov2640_g_mbus_config,
};
static struct v4l2_subdev_ops ov2640_subdev_ops = {
@@ -1111,18 +1029,11 @@ static struct v4l2_subdev_ops ov2640_subdev_ops = {
static int ov2640_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
- struct ov2640_priv *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
- int ret;
-
- if (!icd) {
- dev_err(&adapter->dev, "OV2640: missing soc-camera data!\n");
- return -EINVAL;
- }
+ struct ov2640_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&adapter->dev,
"OV2640: Missing platform_data for driver\n");
@@ -1142,15 +1053,23 @@ static int ov2640_probe(struct i2c_client *client,
return -ENOMEM;
}
- priv->info = icl->priv;
-
v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 2);
+ v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
- icd->ops = &ov2640_ops;
+ kfree(priv);
+ return err;
+ }
- ret = ov2640_video_probe(icd, client);
+ ret = ov2640_video_probe(client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
} else {
dev_info(&adapter->dev, "OV2640 Probed\n");
@@ -1162,9 +1081,9 @@ static int ov2640_probe(struct i2c_client *client,
static int ov2640_remove(struct i2c_client *client)
{
struct ov2640_priv *priv = to_ov2640(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
return 0;
}
diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c
new file mode 100644
index 000000000000..bb37ec80f274
--- /dev/null
+++ b/drivers/media/video/ov5642.c
@@ -0,0 +1,1086 @@
+/*
+ * Driver for OV5642 CMOS Image Sensor from Omnivision
+ *
+ * Copyright (C) 2011, Bastian Hecht <hechtb@gmail.com>
+ *
+ * Based on Sony IMX074 Camera Driver
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Based on Omnivision OV7670 Camera Driver
+ * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+
+/* OV5642 registers */
+#define REG_CHIP_ID_HIGH 0x300a
+#define REG_CHIP_ID_LOW 0x300b
+
+#define REG_WINDOW_START_X_HIGH 0x3800
+#define REG_WINDOW_START_X_LOW 0x3801
+#define REG_WINDOW_START_Y_HIGH 0x3802
+#define REG_WINDOW_START_Y_LOW 0x3803
+#define REG_WINDOW_WIDTH_HIGH 0x3804
+#define REG_WINDOW_WIDTH_LOW 0x3805
+#define REG_WINDOW_HEIGHT_HIGH 0x3806
+#define REG_WINDOW_HEIGHT_LOW 0x3807
+#define REG_OUT_WIDTH_HIGH 0x3808
+#define REG_OUT_WIDTH_LOW 0x3809
+#define REG_OUT_HEIGHT_HIGH 0x380a
+#define REG_OUT_HEIGHT_LOW 0x380b
+#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c
+#define REG_OUT_TOTAL_WIDTH_LOW 0x380d
+#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e
+#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f
+#define REG_OUTPUT_FORMAT 0x4300
+#define REG_ISP_CTRL_01 0x5001
+#define REG_AVG_WINDOW_END_X_HIGH 0x5682
+#define REG_AVG_WINDOW_END_X_LOW 0x5683
+#define REG_AVG_WINDOW_END_Y_HIGH 0x5686
+#define REG_AVG_WINDOW_END_Y_LOW 0x5687
+
+/* active pixel array size */
+#define OV5642_SENSOR_SIZE_X 2592
+#define OV5642_SENSOR_SIZE_Y 1944
+
+/*
+ * About OV5642 resolution, cropping and binning:
+ * This sensor supports it all, at least in the feature description.
+ * Unfortunately, no combination of appropriate registers settings could make
+ * the chip work the intended way. As it works with predefined register lists,
+ * some undocumented registers are presumably changed there to achieve their
+ * goals.
+ * This driver currently only works for resolutions up to 720 lines with a
+ * 1:1 scale. Hopefully these restrictions will be removed in the future.
+ */
+#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X
+#define OV5642_MAX_HEIGHT 720
+
+/* default sizes */
+#define OV5642_DEFAULT_WIDTH 1280
+#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT
+
+/* minimum extra blanking */
+#define BLANKING_EXTRA_WIDTH 500
+#define BLANKING_EXTRA_HEIGHT 20
+
+/*
+ * the sensor's autoexposure is buggy when setting total_height low.
+ * It tries to expose longer than 1 frame period without taking care of it
+ * and this leads to weird output. So we set 1000 lines as minimum.
+ */
+#define BLANKING_MIN_HEIGHT 1000
+
+struct regval_list {
+ u16 reg_num;
+ u8 value;
+};
+
+static struct regval_list ov5642_default_regs_init[] = {
+ { 0x3103, 0x93 },
+ { 0x3008, 0x82 },
+ { 0x3017, 0x7f },
+ { 0x3018, 0xfc },
+ { 0x3810, 0xc2 },
+ { 0x3615, 0xf0 },
+ { 0x3000, 0x0 },
+ { 0x3001, 0x0 },
+ { 0x3002, 0x0 },
+ { 0x3003, 0x0 },
+ { 0x3004, 0xff },
+ { 0x3030, 0x2b },
+ { 0x3011, 0x8 },
+ { 0x3010, 0x10 },
+ { 0x3604, 0x60 },
+ { 0x3622, 0x60 },
+ { 0x3621, 0x9 },
+ { 0x3709, 0x0 },
+ { 0x4000, 0x21 },
+ { 0x401d, 0x22 },
+ { 0x3600, 0x54 },
+ { 0x3605, 0x4 },
+ { 0x3606, 0x3f },
+ { 0x3c01, 0x80 },
+ { 0x300d, 0x22 },
+ { 0x3623, 0x22 },
+ { 0x5000, 0x4f },
+ { 0x5020, 0x4 },
+ { 0x5181, 0x79 },
+ { 0x5182, 0x0 },
+ { 0x5185, 0x22 },
+ { 0x5197, 0x1 },
+ { 0x5500, 0xa },
+ { 0x5504, 0x0 },
+ { 0x5505, 0x7f },
+ { 0x5080, 0x8 },
+ { 0x300e, 0x18 },
+ { 0x4610, 0x0 },
+ { 0x471d, 0x5 },
+ { 0x4708, 0x6 },
+ { 0x370c, 0xa0 },
+ { 0x5687, 0x94 },
+ { 0x501f, 0x0 },
+ { 0x5000, 0x4f },
+ { 0x5001, 0xcf },
+ { 0x4300, 0x30 },
+ { 0x4300, 0x30 },
+ { 0x460b, 0x35 },
+ { 0x471d, 0x0 },
+ { 0x3002, 0xc },
+ { 0x3002, 0x0 },
+ { 0x4713, 0x3 },
+ { 0x471c, 0x50 },
+ { 0x4721, 0x2 },
+ { 0x4402, 0x90 },
+ { 0x460c, 0x22 },
+ { 0x3815, 0x44 },
+ { 0x3503, 0x7 },
+ { 0x3501, 0x73 },
+ { 0x3502, 0x80 },
+ { 0x350b, 0x0 },
+ { 0x3818, 0xc8 },
+ { 0x3824, 0x11 },
+ { 0x3a00, 0x78 },
+ { 0x3a1a, 0x4 },
+ { 0x3a13, 0x30 },
+ { 0x3a18, 0x0 },
+ { 0x3a19, 0x7c },
+ { 0x3a08, 0x12 },
+ { 0x3a09, 0xc0 },
+ { 0x3a0a, 0xf },
+ { 0x3a0b, 0xa0 },
+ { 0x350c, 0x7 },
+ { 0x350d, 0xd0 },
+ { 0x3a0d, 0x8 },
+ { 0x3a0e, 0x6 },
+ { 0x3500, 0x0 },
+ { 0x3501, 0x0 },
+ { 0x3502, 0x0 },
+ { 0x350a, 0x0 },
+ { 0x350b, 0x0 },
+ { 0x3503, 0x0 },
+ { 0x3a0f, 0x3c },
+ { 0x3a10, 0x32 },
+ { 0x3a1b, 0x3c },
+ { 0x3a1e, 0x32 },
+ { 0x3a11, 0x80 },
+ { 0x3a1f, 0x20 },
+ { 0x3030, 0x2b },
+ { 0x3a02, 0x0 },
+ { 0x3a03, 0x7d },
+ { 0x3a04, 0x0 },
+ { 0x3a14, 0x0 },
+ { 0x3a15, 0x7d },
+ { 0x3a16, 0x0 },
+ { 0x3a00, 0x78 },
+ { 0x3a08, 0x9 },
+ { 0x3a09, 0x60 },
+ { 0x3a0a, 0x7 },
+ { 0x3a0b, 0xd0 },
+ { 0x3a0d, 0x10 },
+ { 0x3a0e, 0xd },
+ { 0x4407, 0x4 },
+ { 0x5193, 0x70 },
+ { 0x589b, 0x0 },
+ { 0x589a, 0xc0 },
+ { 0x401e, 0x20 },
+ { 0x4001, 0x42 },
+ { 0x401c, 0x6 },
+ { 0x3825, 0xac },
+ { 0x3827, 0xc },
+ { 0x528a, 0x1 },
+ { 0x528b, 0x4 },
+ { 0x528c, 0x8 },
+ { 0x528d, 0x10 },
+ { 0x528e, 0x20 },
+ { 0x528f, 0x28 },
+ { 0x5290, 0x30 },
+ { 0x5292, 0x0 },
+ { 0x5293, 0x1 },
+ { 0x5294, 0x0 },
+ { 0x5295, 0x4 },
+ { 0x5296, 0x0 },
+ { 0x5297, 0x8 },
+ { 0x5298, 0x0 },
+ { 0x5299, 0x10 },
+ { 0x529a, 0x0 },
+ { 0x529b, 0x20 },
+ { 0x529c, 0x0 },
+ { 0x529d, 0x28 },
+ { 0x529e, 0x0 },
+ { 0x529f, 0x30 },
+ { 0x5282, 0x0 },
+ { 0x5300, 0x0 },
+ { 0x5301, 0x20 },
+ { 0x5302, 0x0 },
+ { 0x5303, 0x7c },
+ { 0x530c, 0x0 },
+ { 0x530d, 0xc },
+ { 0x530e, 0x20 },
+ { 0x530f, 0x80 },
+ { 0x5310, 0x20 },
+ { 0x5311, 0x80 },
+ { 0x5308, 0x20 },
+ { 0x5309, 0x40 },
+ { 0x5304, 0x0 },
+ { 0x5305, 0x30 },
+ { 0x5306, 0x0 },
+ { 0x5307, 0x80 },
+ { 0x5314, 0x8 },
+ { 0x5315, 0x20 },
+ { 0x5319, 0x30 },
+ { 0x5316, 0x10 },
+ { 0x5317, 0x0 },
+ { 0x5318, 0x2 },
+ { 0x5380, 0x1 },
+ { 0x5381, 0x0 },
+ { 0x5382, 0x0 },
+ { 0x5383, 0x4e },
+ { 0x5384, 0x0 },
+ { 0x5385, 0xf },
+ { 0x5386, 0x0 },
+ { 0x5387, 0x0 },
+ { 0x5388, 0x1 },
+ { 0x5389, 0x15 },
+ { 0x538a, 0x0 },
+ { 0x538b, 0x31 },
+ { 0x538c, 0x0 },
+ { 0x538d, 0x0 },
+ { 0x538e, 0x0 },
+ { 0x538f, 0xf },
+ { 0x5390, 0x0 },
+ { 0x5391, 0xab },
+ { 0x5392, 0x0 },
+ { 0x5393, 0xa2 },
+ { 0x5394, 0x8 },
+ { 0x5480, 0x14 },
+ { 0x5481, 0x21 },
+ { 0x5482, 0x36 },
+ { 0x5483, 0x57 },
+ { 0x5484, 0x65 },
+ { 0x5485, 0x71 },
+ { 0x5486, 0x7d },
+ { 0x5487, 0x87 },
+ { 0x5488, 0x91 },
+ { 0x5489, 0x9a },
+ { 0x548a, 0xaa },
+ { 0x548b, 0xb8 },
+ { 0x548c, 0xcd },
+ { 0x548d, 0xdd },
+ { 0x548e, 0xea },
+ { 0x548f, 0x1d },
+ { 0x5490, 0x5 },
+ { 0x5491, 0x0 },
+ { 0x5492, 0x4 },
+ { 0x5493, 0x20 },
+ { 0x5494, 0x3 },
+ { 0x5495, 0x60 },
+ { 0x5496, 0x2 },
+ { 0x5497, 0xb8 },
+ { 0x5498, 0x2 },
+ { 0x5499, 0x86 },
+ { 0x549a, 0x2 },
+ { 0x549b, 0x5b },
+ { 0x549c, 0x2 },
+ { 0x549d, 0x3b },
+ { 0x549e, 0x2 },
+ { 0x549f, 0x1c },
+ { 0x54a0, 0x2 },
+ { 0x54a1, 0x4 },
+ { 0x54a2, 0x1 },
+ { 0x54a3, 0xed },
+ { 0x54a4, 0x1 },
+ { 0x54a5, 0xc5 },
+ { 0x54a6, 0x1 },
+ { 0x54a7, 0xa5 },
+ { 0x54a8, 0x1 },
+ { 0x54a9, 0x6c },
+ { 0x54aa, 0x1 },
+ { 0x54ab, 0x41 },
+ { 0x54ac, 0x1 },
+ { 0x54ad, 0x20 },
+ { 0x54ae, 0x0 },
+ { 0x54af, 0x16 },
+ { 0x54b0, 0x1 },
+ { 0x54b1, 0x20 },
+ { 0x54b2, 0x0 },
+ { 0x54b3, 0x10 },
+ { 0x54b4, 0x0 },
+ { 0x54b5, 0xf0 },
+ { 0x54b6, 0x0 },
+ { 0x54b7, 0xdf },
+ { 0x5402, 0x3f },
+ { 0x5403, 0x0 },
+ { 0x3406, 0x0 },
+ { 0x5180, 0xff },
+ { 0x5181, 0x52 },
+ { 0x5182, 0x11 },
+ { 0x5183, 0x14 },
+ { 0x5184, 0x25 },
+ { 0x5185, 0x24 },
+ { 0x5186, 0x6 },
+ { 0x5187, 0x8 },
+ { 0x5188, 0x8 },
+ { 0x5189, 0x7c },
+ { 0x518a, 0x60 },
+ { 0x518b, 0xb2 },
+ { 0x518c, 0xb2 },
+ { 0x518d, 0x44 },
+ { 0x518e, 0x3d },
+ { 0x518f, 0x58 },
+ { 0x5190, 0x46 },
+ { 0x5191, 0xf8 },
+ { 0x5192, 0x4 },
+ { 0x5193, 0x70 },
+ { 0x5194, 0xf0 },
+ { 0x5195, 0xf0 },
+ { 0x5196, 0x3 },
+ { 0x5197, 0x1 },
+ { 0x5198, 0x4 },
+ { 0x5199, 0x12 },
+ { 0x519a, 0x4 },
+ { 0x519b, 0x0 },
+ { 0x519c, 0x6 },
+ { 0x519d, 0x82 },
+ { 0x519e, 0x0 },
+ { 0x5025, 0x80 },
+ { 0x3a0f, 0x38 },
+ { 0x3a10, 0x30 },
+ { 0x3a1b, 0x3a },
+ { 0x3a1e, 0x2e },
+ { 0x3a11, 0x60 },
+ { 0x3a1f, 0x10 },
+ { 0x5688, 0xa6 },
+ { 0x5689, 0x6a },
+ { 0x568a, 0xea },
+ { 0x568b, 0xae },
+ { 0x568c, 0xa6 },
+ { 0x568d, 0x6a },
+ { 0x568e, 0x62 },
+ { 0x568f, 0x26 },
+ { 0x5583, 0x40 },
+ { 0x5584, 0x40 },
+ { 0x5580, 0x2 },
+ { 0x5000, 0xcf },
+ { 0x5800, 0x27 },
+ { 0x5801, 0x19 },
+ { 0x5802, 0x12 },
+ { 0x5803, 0xf },
+ { 0x5804, 0x10 },
+ { 0x5805, 0x15 },
+ { 0x5806, 0x1e },
+ { 0x5807, 0x2f },
+ { 0x5808, 0x15 },
+ { 0x5809, 0xd },
+ { 0x580a, 0xa },
+ { 0x580b, 0x9 },
+ { 0x580c, 0xa },
+ { 0x580d, 0xc },
+ { 0x580e, 0x12 },
+ { 0x580f, 0x19 },
+ { 0x5810, 0xb },
+ { 0x5811, 0x7 },
+ { 0x5812, 0x4 },
+ { 0x5813, 0x3 },
+ { 0x5814, 0x3 },
+ { 0x5815, 0x6 },
+ { 0x5816, 0xa },
+ { 0x5817, 0xf },
+ { 0x5818, 0xa },
+ { 0x5819, 0x5 },
+ { 0x581a, 0x1 },
+ { 0x581b, 0x0 },
+ { 0x581c, 0x0 },
+ { 0x581d, 0x3 },
+ { 0x581e, 0x8 },
+ { 0x581f, 0xc },
+ { 0x5820, 0xa },
+ { 0x5821, 0x5 },
+ { 0x5822, 0x1 },
+ { 0x5823, 0x0 },
+ { 0x5824, 0x0 },
+ { 0x5825, 0x3 },
+ { 0x5826, 0x8 },
+ { 0x5827, 0xc },
+ { 0x5828, 0xe },
+ { 0x5829, 0x8 },
+ { 0x582a, 0x6 },
+ { 0x582b, 0x4 },
+ { 0x582c, 0x5 },
+ { 0x582d, 0x7 },
+ { 0x582e, 0xb },
+ { 0x582f, 0x12 },
+ { 0x5830, 0x18 },
+ { 0x5831, 0x10 },
+ { 0x5832, 0xc },
+ { 0x5833, 0xa },
+ { 0x5834, 0xb },
+ { 0x5835, 0xe },
+ { 0x5836, 0x15 },
+ { 0x5837, 0x19 },
+ { 0x5838, 0x32 },
+ { 0x5839, 0x1f },
+ { 0x583a, 0x18 },
+ { 0x583b, 0x16 },
+ { 0x583c, 0x17 },
+ { 0x583d, 0x1e },
+ { 0x583e, 0x26 },
+ { 0x583f, 0x53 },
+ { 0x5840, 0x10 },
+ { 0x5841, 0xf },
+ { 0x5842, 0xd },
+ { 0x5843, 0xc },
+ { 0x5844, 0xe },
+ { 0x5845, 0x9 },
+ { 0x5846, 0x11 },
+ { 0x5847, 0x10 },
+ { 0x5848, 0x10 },
+ { 0x5849, 0x10 },
+ { 0x584a, 0x10 },
+ { 0x584b, 0xe },
+ { 0x584c, 0x10 },
+ { 0x584d, 0x10 },
+ { 0x584e, 0x11 },
+ { 0x584f, 0x10 },
+ { 0x5850, 0xf },
+ { 0x5851, 0xc },
+ { 0x5852, 0xf },
+ { 0x5853, 0x10 },
+ { 0x5854, 0x10 },
+ { 0x5855, 0xf },
+ { 0x5856, 0xe },
+ { 0x5857, 0xb },
+ { 0x5858, 0x10 },
+ { 0x5859, 0xd },
+ { 0x585a, 0xd },
+ { 0x585b, 0xc },
+ { 0x585c, 0xc },
+ { 0x585d, 0xc },
+ { 0x585e, 0xb },
+ { 0x585f, 0xc },
+ { 0x5860, 0xc },
+ { 0x5861, 0xc },
+ { 0x5862, 0xd },
+ { 0x5863, 0x8 },
+ { 0x5864, 0x11 },
+ { 0x5865, 0x18 },
+ { 0x5866, 0x18 },
+ { 0x5867, 0x19 },
+ { 0x5868, 0x17 },
+ { 0x5869, 0x19 },
+ { 0x586a, 0x16 },
+ { 0x586b, 0x13 },
+ { 0x586c, 0x13 },
+ { 0x586d, 0x12 },
+ { 0x586e, 0x13 },
+ { 0x586f, 0x16 },
+ { 0x5870, 0x14 },
+ { 0x5871, 0x12 },
+ { 0x5872, 0x10 },
+ { 0x5873, 0x11 },
+ { 0x5874, 0x11 },
+ { 0x5875, 0x16 },
+ { 0x5876, 0x14 },
+ { 0x5877, 0x11 },
+ { 0x5878, 0x10 },
+ { 0x5879, 0xf },
+ { 0x587a, 0x10 },
+ { 0x587b, 0x14 },
+ { 0x587c, 0x13 },
+ { 0x587d, 0x12 },
+ { 0x587e, 0x11 },
+ { 0x587f, 0x11 },
+ { 0x5880, 0x12 },
+ { 0x5881, 0x15 },
+ { 0x5882, 0x14 },
+ { 0x5883, 0x15 },
+ { 0x5884, 0x15 },
+ { 0x5885, 0x15 },
+ { 0x5886, 0x13 },
+ { 0x5887, 0x17 },
+ { 0x3710, 0x10 },
+ { 0x3632, 0x51 },
+ { 0x3702, 0x10 },
+ { 0x3703, 0xb2 },
+ { 0x3704, 0x18 },
+ { 0x370b, 0x40 },
+ { 0x370d, 0x3 },
+ { 0x3631, 0x1 },
+ { 0x3632, 0x52 },
+ { 0x3606, 0x24 },
+ { 0x3620, 0x96 },
+ { 0x5785, 0x7 },
+ { 0x3a13, 0x30 },
+ { 0x3600, 0x52 },
+ { 0x3604, 0x48 },
+ { 0x3606, 0x1b },
+ { 0x370d, 0xb },
+ { 0x370f, 0xc0 },
+ { 0x3709, 0x1 },
+ { 0x3823, 0x0 },
+ { 0x5007, 0x0 },
+ { 0x5009, 0x0 },
+ { 0x5011, 0x0 },
+ { 0x5013, 0x0 },
+ { 0x519e, 0x0 },
+ { 0x5086, 0x0 },
+ { 0x5087, 0x0 },
+ { 0x5088, 0x0 },
+ { 0x5089, 0x0 },
+ { 0x302b, 0x0 },
+ { 0x3503, 0x7 },
+ { 0x3011, 0x8 },
+ { 0x350c, 0x2 },
+ { 0x350d, 0xe4 },
+ { 0x3621, 0xc9 },
+ { 0x370a, 0x81 },
+ { 0xffff, 0xff },
+};
+
+static struct regval_list ov5642_default_regs_finalise[] = {
+ { 0x3810, 0xc2 },
+ { 0x3818, 0xc9 },
+ { 0x381c, 0x10 },
+ { 0x381d, 0xa0 },
+ { 0x381e, 0x5 },
+ { 0x381f, 0xb0 },
+ { 0x3820, 0x0 },
+ { 0x3821, 0x0 },
+ { 0x3824, 0x11 },
+ { 0x3a08, 0x1b },
+ { 0x3a09, 0xc0 },
+ { 0x3a0a, 0x17 },
+ { 0x3a0b, 0x20 },
+ { 0x3a0d, 0x2 },
+ { 0x3a0e, 0x1 },
+ { 0x401c, 0x4 },
+ { 0x5682, 0x5 },
+ { 0x5683, 0x0 },
+ { 0x5686, 0x2 },
+ { 0x5687, 0xcc },
+ { 0x5001, 0x4f },
+ { 0x589b, 0x6 },
+ { 0x589a, 0xc5 },
+ { 0x3503, 0x0 },
+ { 0x460c, 0x20 },
+ { 0x460b, 0x37 },
+ { 0x471c, 0xd0 },
+ { 0x471d, 0x5 },
+ { 0x3815, 0x1 },
+ { 0x3818, 0xc1 },
+ { 0x501f, 0x0 },
+ { 0x5002, 0xe0 },
+ { 0x4300, 0x32 }, /* UYVY */
+ { 0x3002, 0x1c },
+ { 0x4800, 0x14 },
+ { 0x4801, 0xf },
+ { 0x3007, 0x3b },
+ { 0x300e, 0x4 },
+ { 0x4803, 0x50 },
+ { 0x3815, 0x1 },
+ { 0x4713, 0x2 },
+ { 0x4842, 0x1 },
+ { 0x300f, 0xe },
+ { 0x3003, 0x3 },
+ { 0x3003, 0x1 },
+ { 0xffff, 0xff },
+};
+
+struct ov5642_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+struct ov5642 {
+ struct v4l2_subdev subdev;
+ const struct ov5642_datafmt *fmt;
+ struct v4l2_rect crop_rect;
+
+ /* blanking information */
+ int total_width;
+ int total_height;
+};
+
+static const struct ov5642_datafmt ov5642_colour_fmts[] = {
+ {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
+};
+
+static struct ov5642 *to_ov5642(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov5642, subdev);
+}
+
+/* Find a data format by a pixel code in an array */
+static const struct ov5642_datafmt
+ *ov5642_find_datafmt(enum v4l2_mbus_pixelcode code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++)
+ if (ov5642_colour_fmts[i].code == code)
+ return ov5642_colour_fmts + i;
+
+ return NULL;
+}
+
+static int reg_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+ int ret;
+ /* We have 16-bit i2c addresses - care for endianess */
+ unsigned char data[2] = { reg >> 8, reg & 0xff };
+
+ ret = i2c_master_send(client, data, 2);
+ if (ret < 2) {
+ dev_err(&client->dev, "%s: i2c read error, reg: %x\n",
+ __func__, reg);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ ret = i2c_master_recv(client, val, 1);
+ if (ret < 1) {
+ dev_err(&client->dev, "%s: i2c read error, reg: %x\n",
+ __func__, reg);
+ return ret < 0 ? ret : -EIO;
+ }
+ return 0;
+}
+
+static int reg_write(struct i2c_client *client, u16 reg, u8 val)
+{
+ int ret;
+ unsigned char data[3] = { reg >> 8, reg & 0xff, val };
+
+ ret = i2c_master_send(client, data, 3);
+ if (ret < 3) {
+ dev_err(&client->dev, "%s: i2c write error, reg: %x\n",
+ __func__, reg);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * convenience function to write 16 bit register values that are split up
+ * into two consecutive high and low parts
+ */
+static int reg_write16(struct i2c_client *client, u16 reg, u16 val16)
+{
+ int ret;
+
+ ret = reg_write(client, reg, val16 >> 8);
+ if (ret)
+ return ret;
+ return reg_write(client, reg + 1, val16 & 0x00ff);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ u8 val;
+
+ if (reg->reg & ~0xffff)
+ return -EINVAL;
+
+ reg->size = 1;
+
+ ret = reg_read(client, reg->reg, &val);
+ if (!ret)
+ reg->val = (__u64)val;
+
+ return ret;
+}
+
+static int ov5642_set_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg & ~0xffff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov5642_write_array(struct i2c_client *client,
+ struct regval_list *vals)
+{
+ while (vals->reg_num != 0xffff || vals->value != 0xff) {
+ int ret = reg_write(client, vals->reg_num, vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ dev_dbg(&client->dev, "Register list loaded\n");
+ return 0;
+}
+
+static int ov5642_set_resolution(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ int width = priv->crop_rect.width;
+ int height = priv->crop_rect.height;
+ int total_width = priv->total_width;
+ int total_height = priv->total_height;
+ int start_x = (OV5642_SENSOR_SIZE_X - width) / 2;
+ int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2;
+ int ret;
+
+ /*
+ * This should set the starting point for cropping.
+ * Doesn't work so far.
+ */
+ ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x);
+ if (!ret)
+ ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y);
+ if (!ret) {
+ priv->crop_rect.left = start_x;
+ priv->crop_rect.top = start_y;
+ }
+
+ if (!ret)
+ ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width);
+ if (!ret)
+ ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height);
+ if (ret)
+ return ret;
+ priv->crop_rect.width = width;
+ priv->crop_rect.height = height;
+
+ /* Set the output window size. Only 1:1 scale is supported so far. */
+ ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width);
+ if (!ret)
+ ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height);
+
+ /* Total width = output size + blanking */
+ if (!ret)
+ ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width);
+ if (!ret)
+ ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height);
+
+ /* Sets the window for AWB calculations */
+ if (!ret)
+ ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width);
+ if (!ret)
+ ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height);
+
+ return ret;
+}
+
+static int ov5642_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code);
+
+ mf->width = priv->crop_rect.width;
+ mf->height = priv->crop_rect.height;
+
+ if (!fmt) {
+ mf->code = ov5642_colour_fmts[0].code;
+ mf->colorspace = ov5642_colour_fmts[0].colorspace;
+ }
+
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov5642_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+
+ /* MIPI CSI could have changed the format, double-check */
+ if (!ov5642_find_datafmt(mf->code))
+ return -EINVAL;
+
+ ov5642_try_fmt(sd, mf);
+ priv->fmt = ov5642_find_datafmt(mf->code);
+
+ return 0;
+}
+
+static int ov5642_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+
+ const struct ov5642_datafmt *fmt = priv->fmt;
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+ mf->width = priv->crop_rect.width;
+ mf->height = priv->crop_rect.height;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov5642_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov5642_colour_fmts))
+ return -EINVAL;
+
+ *code = ov5642_colour_fmts[index].code;
+ return 0;
+}
+
+static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = V4L2_IDENT_OV5642;
+ id->revision = 0;
+
+ return 0;
+}
+
+static int ov5642_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ struct v4l2_rect *rect = &a->c;
+ int ret;
+
+ v4l_bound_align_image(&rect->width, 48, OV5642_MAX_WIDTH, 1,
+ &rect->height, 32, OV5642_MAX_HEIGHT, 1, 0);
+
+ priv->crop_rect.width = rect->width;
+ priv->crop_rect.height = rect->height;
+ priv->total_width = rect->width + BLANKING_EXTRA_WIDTH;
+ priv->total_height = max_t(int, rect->height +
+ BLANKING_EXTRA_HEIGHT,
+ BLANKING_MIN_HEIGHT);
+ priv->crop_rect.width = rect->width;
+ priv->crop_rect.height = rect->height;
+
+ ret = ov5642_write_array(client, ov5642_default_regs_init);
+ if (!ret)
+ ret = ov5642_set_resolution(sd);
+ if (!ret)
+ ret = ov5642_write_array(client, ov5642_default_regs_finalise);
+
+ return ret;
+}
+
+static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ struct v4l2_rect *rect = &a->c;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ *rect = priv->crop_rect;
+
+ return 0;
+}
+
+static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = OV5642_MAX_WIDTH;
+ a->bounds.height = OV5642_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov5642_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->type = V4L2_MBUS_CSI2;
+ cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ return 0;
+}
+
+static int ov5642_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client;
+ int ret;
+
+ if (!on)
+ return 0;
+
+ client = v4l2_get_subdevdata(sd);
+ ret = ov5642_write_array(client, ov5642_default_regs_init);
+ if (!ret)
+ ret = ov5642_set_resolution(sd);
+ if (!ret)
+ ret = ov5642_write_array(client, ov5642_default_regs_finalise);
+
+ return ret;
+}
+
+static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = {
+ .s_mbus_fmt = ov5642_s_fmt,
+ .g_mbus_fmt = ov5642_g_fmt,
+ .try_mbus_fmt = ov5642_try_fmt,
+ .enum_mbus_fmt = ov5642_enum_fmt,
+ .s_crop = ov5642_s_crop,
+ .g_crop = ov5642_g_crop,
+ .cropcap = ov5642_cropcap,
+ .g_mbus_config = ov5642_g_mbus_config,
+};
+
+static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
+ .s_power = ov5642_s_power,
+ .g_chip_ident = ov5642_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov5642_get_register,
+ .s_register = ov5642_set_register,
+#endif
+};
+
+static struct v4l2_subdev_ops ov5642_subdev_ops = {
+ .core = &ov5642_subdev_core_ops,
+ .video = &ov5642_subdev_video_ops,
+};
+
+static int ov5642_video_probe(struct i2c_client *client)
+{
+ int ret;
+ u8 id_high, id_low;
+ u16 id;
+
+ /* Read sensor Model ID */
+ ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high);
+ if (ret < 0)
+ return ret;
+
+ id = id_high << 8;
+
+ ret = reg_read(client, REG_CHIP_ID_LOW, &id_low);
+ if (ret < 0)
+ return ret;
+
+ id |= id_low;
+
+ dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
+
+ if (id != 0x5642)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int ov5642_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov5642 *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "OV5642: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct ov5642), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops);
+
+ priv->fmt = &ov5642_colour_fmts[0];
+
+ priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
+ priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
+ priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2;
+ priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2;
+ priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
+ priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
+ priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
+ priv->total_height = BLANKING_MIN_HEIGHT;
+
+ ret = ov5642_video_probe(client);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ kfree(priv);
+ return ret;
+}
+
+static int ov5642_remove(struct i2c_client *client)
+{
+ struct ov5642 *priv = to_ov5642(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ kfree(priv);
+
+ return 0;
+}
+
+static const struct i2c_device_id ov5642_id[] = {
+ { "ov5642", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov5642_id);
+
+static struct i2c_driver ov5642_i2c_driver = {
+ .driver = {
+ .name = "ov5642",
+ },
+ .probe = ov5642_probe,
+ .remove = ov5642_remove,
+ .id_table = ov5642_id,
+};
+
+static int __init ov5642_mod_init(void)
+{
+ return i2c_add_driver(&ov5642_i2c_driver);
+}
+
+static void __exit ov5642_mod_exit(void)
+{
+ i2c_del_driver(&ov5642_i2c_driver);
+}
+
+module_init(ov5642_mod_init);
+module_exit(ov5642_mod_exit);
+
+MODULE_DESCRIPTION("Omnivision OV5642 Camera driver");
+MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c
index 456d9ad9ae5a..9f2d26b1d4cb 100644
--- a/drivers/media/video/ov6650.c
+++ b/drivers/media/video/ov6650.c
@@ -28,10 +28,12 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/module.h>
#include <media/soc_camera.h>
#include <media/v4l2-chip-ident.h>
-
+#include <media/v4l2-ctrls.h>
/* Register definitions */
#define REG_GAIN 0x00 /* range 00 - 3F */
@@ -177,20 +179,23 @@ struct ov6650_reg {
struct ov6650 {
struct v4l2_subdev subdev;
-
- int gain;
- int blue;
- int red;
- int saturation;
- int hue;
- int brightness;
- int exposure;
- int gamma;
- int aec;
- bool vflip;
- bool hflip;
- bool awb;
- bool agc;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/autoexposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct {
+ /* gain/autogain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
+ struct {
+ /* blue/red/autowhitebalance cluster */
+ struct v4l2_ctrl *autowb;
+ struct v4l2_ctrl *blue;
+ struct v4l2_ctrl *red;
+ };
bool half_scale; /* scale down output by 2 */
struct v4l2_rect rect; /* sensor cropping window */
unsigned long pclk_limit; /* from host */
@@ -210,126 +215,6 @@ static enum v4l2_mbus_pixelcode ov6650_codes[] = {
V4L2_MBUS_FMT_Y8_1X8,
};
-static const struct v4l2_queryctrl ov6650_controls[] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "AGC",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 0x3f,
- .step = 1,
- .default_value = DEF_GAIN,
- },
- {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "AWB",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = DEF_BLUE,
- },
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = DEF_RED,
- },
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 0xf,
- .step = 1,
- .default_value = 0x8,
- },
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = 0,
- .maximum = HUE_MASK,
- .step = 1,
- .default_value = DEF_HUE,
- },
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x80,
- },
- {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "AEC",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = DEF_AECH,
- },
- {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x12,
- },
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
/* read a register */
static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
{
@@ -419,213 +304,90 @@ static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-/* Alter bus settings on camera side */
-static int ov6650_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
- int ret;
-
- flags = soc_camera_apply_sensor_flags(icl, flags);
-
- if (flags & SOCAM_PCLK_SAMPLE_RISING)
- ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
- else
- ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
- if (ret)
- return ret;
-
- if (flags & SOCAM_HSYNC_ACTIVE_LOW)
- ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
- else
- ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
- if (ret)
- return ret;
-
- if (flags & SOCAM_VSYNC_ACTIVE_HIGH)
- ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
- else
- ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
-
- return ret;
-}
-
-/* Request bus settings on camera side */
-static unsigned long ov6650_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- unsigned long flags = SOCAM_MASTER |
- SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
/* Get status of additional camera capabilities */
-static int ov6650_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov6650 *priv = to_ov6650(client);
- uint8_t reg;
- int ret = 0;
+ uint8_t reg, reg2;
+ int ret;
switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
- ctrl->value = priv->agc;
- break;
- case V4L2_CID_GAIN:
- if (priv->agc) {
- ret = ov6650_reg_read(client, REG_GAIN, &reg);
- ctrl->value = reg;
- } else {
- ctrl->value = priv->gain;
- }
- break;
+ ret = ov6650_reg_read(client, REG_GAIN, &reg);
+ if (!ret)
+ priv->gain->val = reg;
+ return ret;
case V4L2_CID_AUTO_WHITE_BALANCE:
- ctrl->value = priv->awb;
- break;
- case V4L2_CID_BLUE_BALANCE:
- if (priv->awb) {
- ret = ov6650_reg_read(client, REG_BLUE, &reg);
- ctrl->value = reg;
- } else {
- ctrl->value = priv->blue;
- }
- break;
- case V4L2_CID_RED_BALANCE:
- if (priv->awb) {
- ret = ov6650_reg_read(client, REG_RED, &reg);
- ctrl->value = reg;
- } else {
- ctrl->value = priv->red;
+ ret = ov6650_reg_read(client, REG_BLUE, &reg);
+ if (!ret)
+ ret = ov6650_reg_read(client, REG_RED, &reg2);
+ if (!ret) {
+ priv->blue->val = reg;
+ priv->red->val = reg2;
}
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = priv->saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = priv->hue;
- break;
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = priv->brightness;
- break;
+ return ret;
case V4L2_CID_EXPOSURE_AUTO:
- ctrl->value = priv->aec;
- break;
- case V4L2_CID_EXPOSURE:
- if (priv->aec) {
- ret = ov6650_reg_read(client, REG_AECH, &reg);
- ctrl->value = reg;
- } else {
- ctrl->value = priv->exposure;
- }
- break;
- case V4L2_CID_GAMMA:
- ctrl->value = priv->gamma;
- break;
- case V4L2_CID_VFLIP:
- ctrl->value = priv->vflip;
- break;
- case V4L2_CID_HFLIP:
- ctrl->value = priv->hflip;
- break;
+ ret = ov6650_reg_read(client, REG_AECH, &reg);
+ if (!ret)
+ priv->exposure->val = reg;
+ return ret;
}
- return ret;
+ return -EINVAL;
}
/* Set status of additional camera capabilities */
-static int ov6650_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov6650 *priv = to_ov6650(client);
- int ret = 0;
+ int ret;
switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
ret = ov6650_reg_rmw(client, REG_COMB,
- ctrl->value ? COMB_AGC : 0, COMB_AGC);
- if (!ret)
- priv->agc = ctrl->value;
- break;
- case V4L2_CID_GAIN:
- ret = ov6650_reg_write(client, REG_GAIN, ctrl->value);
- if (!ret)
- priv->gain = ctrl->value;
- break;
+ ctrl->val ? COMB_AGC : 0, COMB_AGC);
+ if (!ret && !ctrl->val)
+ ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val);
+ return ret;
case V4L2_CID_AUTO_WHITE_BALANCE:
ret = ov6650_reg_rmw(client, REG_COMB,
- ctrl->value ? COMB_AWB : 0, COMB_AWB);
- if (!ret)
- priv->awb = ctrl->value;
- break;
- case V4L2_CID_BLUE_BALANCE:
- ret = ov6650_reg_write(client, REG_BLUE, ctrl->value);
- if (!ret)
- priv->blue = ctrl->value;
- break;
- case V4L2_CID_RED_BALANCE:
- ret = ov6650_reg_write(client, REG_RED, ctrl->value);
- if (!ret)
- priv->red = ctrl->value;
- break;
+ ctrl->val ? COMB_AWB : 0, COMB_AWB);
+ if (!ret && !ctrl->val) {
+ ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val);
+ if (!ret)
+ ret = ov6650_reg_write(client, REG_RED,
+ priv->red->val);
+ }
+ return ret;
case V4L2_CID_SATURATION:
- ret = ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->value),
+ return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val),
SAT_MASK);
- if (!ret)
- priv->saturation = ctrl->value;
- break;
case V4L2_CID_HUE:
- ret = ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->value),
+ return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val),
HUE_MASK);
- if (!ret)
- priv->hue = ctrl->value;
- break;
case V4L2_CID_BRIGHTNESS:
- ret = ov6650_reg_write(client, REG_BRT, ctrl->value);
- if (!ret)
- priv->brightness = ctrl->value;
- break;
+ return ov6650_reg_write(client, REG_BRT, ctrl->val);
case V4L2_CID_EXPOSURE_AUTO:
- switch (ctrl->value) {
- case V4L2_EXPOSURE_AUTO:
- ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0);
- break;
- default:
- ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC);
- break;
- }
- if (!ret)
- priv->aec = ctrl->value;
- break;
- case V4L2_CID_EXPOSURE:
- ret = ov6650_reg_write(client, REG_AECH, ctrl->value);
- if (!ret)
- priv->exposure = ctrl->value;
- break;
+ ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val ==
+ V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC);
+ if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+ ret = ov6650_reg_write(client, REG_AECH,
+ priv->exposure->val);
+ return ret;
case V4L2_CID_GAMMA:
- ret = ov6650_reg_write(client, REG_GAM1, ctrl->value);
- if (!ret)
- priv->gamma = ctrl->value;
- break;
+ return ov6650_reg_write(client, REG_GAM1, ctrl->val);
case V4L2_CID_VFLIP:
- ret = ov6650_reg_rmw(client, REG_COMB,
- ctrl->value ? COMB_FLIP_V : 0, COMB_FLIP_V);
- if (!ret)
- priv->vflip = ctrl->value;
- break;
+ return ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V);
case V4L2_CID_HFLIP:
- ret = ov6650_reg_rmw(client, REG_COMB,
- ctrl->value ? COMB_FLIP_H : 0, COMB_FLIP_H);
- if (!ret)
- priv->hflip = ctrl->value;
- break;
+ return ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H);
}
- return ret;
+ return -EINVAL;
}
/* Get chip identification */
@@ -778,7 +540,7 @@ static u8 to_clkrc(struct v4l2_fract *timeperframe,
static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id;
struct soc_camera_sense *sense = icd->sense;
struct ov6650 *priv = to_ov6650(client);
bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
@@ -1057,8 +819,7 @@ static int ov6650_prog_dflt(struct i2c_client *client)
return ret;
}
-static int ov6650_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int ov6650_video_probe(struct i2c_client *client)
{
u8 pidh, pidl, midh, midl;
int ret = 0;
@@ -1094,16 +855,12 @@ static int ov6650_video_probe(struct soc_camera_device *icd,
return ret;
}
-static struct soc_camera_ops ov6650_ops = {
- .set_bus_param = ov6650_set_bus_param,
- .query_bus_param = ov6650_query_bus_param,
- .controls = ov6650_controls,
- .num_controls = ARRAY_SIZE(ov6650_controls),
+static const struct v4l2_ctrl_ops ov6550_ctrl_ops = {
+ .g_volatile_ctrl = ov6550_g_volatile_ctrl,
+ .s_ctrl = ov6550_s_ctrl,
};
static struct v4l2_subdev_core_ops ov6650_core_ops = {
- .g_ctrl = ov6650_g_ctrl,
- .s_ctrl = ov6650_s_ctrl,
.g_chip_ident = ov6650_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov6650_get_register,
@@ -1111,6 +868,55 @@ static struct v4l2_subdev_core_ops ov6650_core_ops = {
#endif
};
+/* Request bus settings on camera side */
+static int ov6650_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+/* Alter bus settings on camera side */
+static int ov6650_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+ int ret;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
+ if (ret)
+ return ret;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
+ if (ret)
+ return ret;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
+
+ return ret;
+}
+
static struct v4l2_subdev_video_ops ov6650_video_ops = {
.s_stream = ov6650_s_stream,
.g_mbus_fmt = ov6650_g_fmt,
@@ -1122,6 +928,8 @@ static struct v4l2_subdev_video_ops ov6650_video_ops = {
.s_crop = ov6650_s_crop,
.g_parm = ov6650_g_parm,
.s_parm = ov6650_s_parm,
+ .g_mbus_config = ov6650_g_mbus_config,
+ .s_mbus_config = ov6650_s_mbus_config,
};
static struct v4l2_subdev_ops ov6650_subdev_ops = {
@@ -1136,16 +944,9 @@ static int ov6650_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov6650 *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "Missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "Missing platform_data for driver\n");
return -EINVAL;
@@ -1159,8 +960,46 @@ static int ov6650_probe(struct i2c_client *client,
}
v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 13);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN);
+ priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE);
+ priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 0xf, 1, 0x8);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80);
+ priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl,
+ &ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+ priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 0xff, 1, 0x12);
+
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
- icd->ops = &ov6650_ops;
+ kfree(priv);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true);
+ v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true);
+ v4l2_ctrl_auto_cluster(2, &priv->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
priv->rect.left = DEF_HSTRT << 1;
priv->rect.top = DEF_VSTRT << 1;
@@ -1170,10 +1009,12 @@ static int ov6650_probe(struct i2c_client *client,
priv->code = V4L2_MBUS_FMT_YUYV8_2X8;
priv->colorspace = V4L2_COLORSPACE_JPEG;
- ret = ov6650_video_probe(icd, client);
+ ret = ov6650_video_probe(client);
+ if (!ret)
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
}
@@ -1184,6 +1025,8 @@ static int ov6650_remove(struct i2c_client *client)
{
struct ov6650 *priv = to_ov6650(client);
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
return 0;
}
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index d4e7c11553c3..8aa058531280 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -19,8 +19,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-mediabus.h>
-
-#include "ov7670.h"
+#include <media/ov7670.h>
MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
diff --git a/drivers/media/video/ov7670.h b/drivers/media/video/ov7670.h
deleted file mode 100644
index b133bc123031..000000000000
--- a/drivers/media/video/ov7670.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * A V4L2 driver for OmniVision OV7670 cameras.
- *
- * Copyright 2010 One Laptop Per Child
- *
- * This file may be distributed under the terms of the GNU General
- * Public License, version 2.
- */
-
-#ifndef __OV7670_H
-#define __OV7670_H
-
-struct ov7670_config {
- int min_width; /* Filter out smaller sizes */
- int min_height; /* Filter out smaller sizes */
- int clock_speed; /* External clock speed (MHz) */
- bool use_smbus; /* Use smbus I/O instead of I2C */
-};
-
-#endif
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
index 48895ef863ff..9f6ce3d8a29e 100644
--- a/drivers/media/video/ov772x.c
+++ b/drivers/media/video/ov772x.c
@@ -20,12 +20,14 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
+
+#include <media/ov772x.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
-#include <media/ov772x.h>
/*
* register offset
@@ -400,6 +402,7 @@ struct ov772x_win_size {
struct ov772x_priv {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
struct ov772x_camera_info *info;
const struct ov772x_color_format *cfmt;
const struct ov772x_win_size *win;
@@ -517,36 +520,6 @@ static const struct ov772x_win_size ov772x_win_qvga = {
.regs = ov772x_qvga_regs,
};
-static const struct v4l2_queryctrl ov772x_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_BAND_STOP_FILTER,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Band-stop filter",
- .minimum = 0,
- .maximum = 256,
- .step = 1,
- .default_value = 0,
- },
-};
-
/*
* general function
*/
@@ -620,75 +593,30 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int ov772x_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
- struct ov772x_priv *priv = i2c_get_clientdata(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH;
-
- if (priv->info->flags & OV772X_FLAG_8BIT)
- flags |= SOCAM_DATAWIDTH_8;
- else
- flags |= SOCAM_DATAWIDTH_10;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- ctrl->value = priv->flag_vflip;
- break;
- case V4L2_CID_HFLIP:
- ctrl->value = priv->flag_hflip;
- break;
- case V4L2_CID_BAND_STOP_FILTER:
- ctrl->value = priv->band_filter;
- break;
- }
- return 0;
-}
-
-static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct ov772x_priv *priv = container_of(ctrl->handler,
+ struct ov772x_priv, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
int ret = 0;
u8 val;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- val = ctrl->value ? VFLIP_IMG : 0x00;
- priv->flag_vflip = ctrl->value;
+ val = ctrl->val ? VFLIP_IMG : 0x00;
+ priv->flag_vflip = ctrl->val;
if (priv->info->flags & OV772X_FLAG_VFLIP)
val ^= VFLIP_IMG;
- ret = ov772x_mask_set(client, COM3, VFLIP_IMG, val);
- break;
+ return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
case V4L2_CID_HFLIP:
- val = ctrl->value ? HFLIP_IMG : 0x00;
- priv->flag_hflip = ctrl->value;
+ val = ctrl->val ? HFLIP_IMG : 0x00;
+ priv->flag_hflip = ctrl->val;
if (priv->info->flags & OV772X_FLAG_HFLIP)
val ^= HFLIP_IMG;
- ret = ov772x_mask_set(client, COM3, HFLIP_IMG, val);
- break;
+ return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
case V4L2_CID_BAND_STOP_FILTER:
- if ((unsigned)ctrl->value > 256)
- ctrl->value = 256;
- if (ctrl->value == priv->band_filter)
- break;
- if (!ctrl->value) {
+ if (!ctrl->val) {
/* Switch the filter off, it is on now */
ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
if (!ret)
@@ -696,7 +624,7 @@ static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
BNDF_ON_OFF, 0);
} else {
/* Switch the filter on, set AEC low limit */
- val = 256 - ctrl->value;
+ val = 256 - ctrl->val;
ret = ov772x_mask_set(client, COM8,
BNDF_ON_OFF, BNDF_ON_OFF);
if (!ret)
@@ -704,11 +632,11 @@ static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
0xff, val);
}
if (!ret)
- priv->band_filter = ctrl->value;
- break;
+ priv->band_filter = ctrl->val;
+ return ret;
}
- return ret;
+ return -EINVAL;
}
static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
@@ -822,13 +750,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
goto ov772x_set_fmt_error;
ret = ov772x_mask_set(client,
- EDGE_TRSHLD, EDGE_THRESHOLD_MASK,
+ EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
priv->info->edgectrl.threshold);
if (ret < 0)
goto ov772x_set_fmt_error;
ret = ov772x_mask_set(client,
- EDGE_STRNGT, EDGE_STRENGTH_MASK,
+ EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
priv->info->edgectrl.strength);
if (ret < 0)
goto ov772x_set_fmt_error;
@@ -840,13 +768,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
* set upper and lower limit
*/
ret = ov772x_mask_set(client,
- EDGE_UPPER, EDGE_UPPER_MASK,
+ EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
priv->info->edgectrl.upper);
if (ret < 0)
goto ov772x_set_fmt_error;
ret = ov772x_mask_set(client,
- EDGE_LOWER, EDGE_LOWER_MASK,
+ EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
priv->info->edgectrl.lower);
if (ret < 0)
goto ov772x_set_fmt_error;
@@ -1025,22 +953,13 @@ static int ov772x_try_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int ov772x_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int ov772x_video_probe(struct i2c_client *client)
{
struct ov772x_priv *priv = to_ov772x(client);
u8 pid, ver;
const char *devname;
/*
- * We must have a parent by now. And it cannot be a wrong one.
- * So this entire test is completely redundant.
- */
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
- return -ENODEV;
-
- /*
* check and show product ID and manufacturer ID
*/
pid = i2c_smbus_read_byte_data(client, PID);
@@ -1068,20 +987,14 @@ static int ov772x_video_probe(struct soc_camera_device *icd,
ver,
i2c_smbus_read_byte_data(client, MIDH),
i2c_smbus_read_byte_data(client, MIDL));
-
- return 0;
+ return v4l2_ctrl_handler_setup(&priv->hdl);
}
-static struct soc_camera_ops ov772x_ops = {
- .set_bus_param = ov772x_set_bus_param,
- .query_bus_param = ov772x_query_bus_param,
- .controls = ov772x_controls,
- .num_controls = ARRAY_SIZE(ov772x_controls),
+static const struct v4l2_ctrl_ops ov772x_ctrl_ops = {
+ .s_ctrl = ov772x_s_ctrl,
};
static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
- .g_ctrl = ov772x_g_ctrl,
- .s_ctrl = ov772x_s_ctrl,
.g_chip_ident = ov772x_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov772x_g_register,
@@ -1099,6 +1012,21 @@ static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int ov772x_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
.s_stream = ov772x_s_stream,
.g_mbus_fmt = ov772x_g_fmt,
@@ -1107,6 +1035,7 @@ static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
.cropcap = ov772x_cropcap,
.g_crop = ov772x_g_crop,
.enum_mbus_fmt = ov772x_enum_fmt,
+ .g_mbus_config = ov772x_g_mbus_config,
};
static struct v4l2_subdev_ops ov772x_subdev_ops = {
@@ -1121,20 +1050,15 @@ static struct v4l2_subdev_ops ov772x_subdev_ops = {
static int ov772x_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
- struct ov772x_priv *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
- int ret;
-
- if (!icd) {
- dev_err(&client->dev, "OV772X: missing soc-camera data!\n");
- return -EINVAL;
- }
+ struct ov772x_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
- icl = to_soc_camera_link(icd);
- if (!icl || !icl->priv)
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "OV772X: missing platform data!\n");
return -EINVAL;
+ }
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&adapter->dev,
@@ -1150,12 +1074,24 @@ static int ov772x_probe(struct i2c_client *client,
priv->info = icl->priv;
v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 3);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
- icd->ops = &ov772x_ops;
+ kfree(priv);
+ return err;
+ }
- ret = ov772x_video_probe(icd, client);
+ ret = ov772x_video_probe(client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
}
@@ -1165,9 +1101,9 @@ static int ov772x_probe(struct i2c_client *client,
static int ov772x_remove(struct i2c_client *client)
{
struct ov772x_priv *priv = to_ov772x(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
return 0;
}
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
index 5173ac449dd8..a4f99797eb56 100644
--- a/drivers/media/video/ov9640.c
+++ b/drivers/media/video/ov9640.c
@@ -24,10 +24,13 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
-#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
#include "ov9640.h"
@@ -162,27 +165,6 @@ static enum v4l2_mbus_pixelcode ov9640_codes[] = {
V4L2_MBUS_FMT_RGB565_2X8_LE,
};
-static const struct v4l2_queryctrl ov9640_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
/* read a register */
static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val)
{
@@ -284,75 +266,25 @@ static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-/* Alter bus settings on camera side */
-static int ov9640_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-/* Request bus settings on camera side */
-static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- /*
- * REVISIT: the camera probably can do 10 bit transfers, but I don't
- * have those pins connected on my hardware.
- */
- unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-/* Get status of additional camera capabilities */
-static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct ov9640_priv *priv = to_ov9640_sensor(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- ctrl->value = priv->flag_vflip;
- break;
- case V4L2_CID_HFLIP:
- ctrl->value = priv->flag_hflip;
- break;
- }
- return 0;
-}
-
/* Set status of additional camera capabilities */
-static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov9640_priv *priv = to_ov9640_sensor(sd);
-
- int ret = 0;
+ struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl);
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- priv->flag_vflip = ctrl->value;
- if (ctrl->value)
- ret = ov9640_reg_rmw(client, OV9640_MVFP,
+ if (ctrl->val)
+ return ov9640_reg_rmw(client, OV9640_MVFP,
OV9640_MVFP_V, 0);
- else
- ret = ov9640_reg_rmw(client, OV9640_MVFP,
- 0, OV9640_MVFP_V);
- break;
+ return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V);
case V4L2_CID_HFLIP:
- priv->flag_hflip = ctrl->value;
- if (ctrl->value)
- ret = ov9640_reg_rmw(client, OV9640_MVFP,
+ if (ctrl->val)
+ return ov9640_reg_rmw(client, OV9640_MVFP,
OV9640_MVFP_H, 0);
- else
- ret = ov9640_reg_rmw(client, OV9640_MVFP,
- 0, OV9640_MVFP_H);
- break;
+ return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H);
}
-
- return ret;
+ return -EINVAL;
}
/* Get chip identification */
@@ -646,10 +578,7 @@ static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-
-
-static int ov9640_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int ov9640_video_probe(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov9640_priv *priv = to_ov9640_sensor(sd);
@@ -658,35 +587,18 @@ static int ov9640_video_probe(struct soc_camera_device *icd,
int ret = 0;
/*
- * We must have a parent by now. And it cannot be a wrong one.
- * So this entire test is completely redundant.
- */
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface) {
- dev_err(&client->dev, "Parent missing or invalid!\n");
- ret = -ENODEV;
- goto err;
- }
-
- /*
* check and show product ID and manufacturer ID
*/
ret = ov9640_reg_read(client, OV9640_PID, &pid);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_VER, &ver);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
if (ret)
- goto err;
-
- ret = ov9640_reg_read(client, OV9640_VER, &ver);
- if (ret)
- goto err;
-
- ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
- if (ret)
- goto err;
-
- ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
- if (ret)
- goto err;
+ return ret;
switch (VERSION(pid, ver)) {
case OV9640_V2:
@@ -700,27 +612,20 @@ static int ov9640_video_probe(struct soc_camera_device *icd,
break;
default:
dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
- ret = -ENODEV;
- goto err;
+ return -ENODEV;
}
dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
devname, pid, ver, midh, midl);
-err:
- return ret;
+ return v4l2_ctrl_handler_setup(&priv->hdl);
}
-static struct soc_camera_ops ov9640_ops = {
- .set_bus_param = ov9640_set_bus_param,
- .query_bus_param = ov9640_query_bus_param,
- .controls = ov9640_controls,
- .num_controls = ARRAY_SIZE(ov9640_controls),
+static const struct v4l2_ctrl_ops ov9640_ctrl_ops = {
+ .s_ctrl = ov9640_s_ctrl,
};
static struct v4l2_subdev_core_ops ov9640_core_ops = {
- .g_ctrl = ov9640_g_ctrl,
- .s_ctrl = ov9640_s_ctrl,
.g_chip_ident = ov9640_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov9640_get_register,
@@ -729,6 +634,22 @@ static struct v4l2_subdev_core_ops ov9640_core_ops = {
};
+/* Request bus settings on camera side */
+static int ov9640_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops ov9640_video_ops = {
.s_stream = ov9640_s_stream,
.s_mbus_fmt = ov9640_s_fmt,
@@ -736,7 +657,7 @@ static struct v4l2_subdev_video_ops ov9640_video_ops = {
.enum_mbus_fmt = ov9640_enum_fmt,
.cropcap = ov9640_cropcap,
.g_crop = ov9640_g_crop,
-
+ .g_mbus_config = ov9640_g_mbus_config,
};
static struct v4l2_subdev_ops ov9640_subdev_ops = {
@@ -751,16 +672,9 @@ static int ov9640_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov9640_priv *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "Missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "Missing platform_data for driver\n");
return -EINVAL;
@@ -775,12 +689,23 @@ static int ov9640_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
- icd->ops = &ov9640_ops;
+ v4l2_ctrl_handler_init(&priv->hdl, 2);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
- ret = ov9640_video_probe(icd, client);
+ ret = ov9640_video_probe(client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
}
@@ -792,6 +717,8 @@ static int ov9640_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov9640_priv *priv = to_ov9640_sensor(sd);
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
return 0;
}
diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h
index f8a51b70792e..6b33a972c83c 100644
--- a/drivers/media/video/ov9640.h
+++ b/drivers/media/video/ov9640.h
@@ -198,12 +198,10 @@ struct ov9640_reg {
struct ov9640_priv {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
int model;
int revision;
-
- bool flag_vflip;
- bool flag_hflip;
};
#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */
diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c
index 4d4ee4faca69..d9a9f7174f7a 100644
--- a/drivers/media/video/ov9740.c
+++ b/drivers/media/video/ov9740.c
@@ -14,8 +14,11 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <media/v4l2-chip-ident.h>
+#include <linux/v4l2-mediabus.h>
+
#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev)
@@ -44,12 +47,12 @@
#define OV9740_Y_ADDR_START_LO 0x0347
#define OV9740_X_ADDR_END_HI 0x0348
#define OV9740_X_ADDR_END_LO 0x0349
-#define OV9740_Y_ADDR_END_HI 0x034A
-#define OV9740_Y_ADDR_END_LO 0x034B
-#define OV9740_X_OUTPUT_SIZE_HI 0x034C
-#define OV9740_X_OUTPUT_SIZE_LO 0x034D
-#define OV9740_Y_OUTPUT_SIZE_HI 0x034E
-#define OV9740_Y_OUTPUT_SIZE_LO 0x034F
+#define OV9740_Y_ADDR_END_HI 0x034a
+#define OV9740_Y_ADDR_END_LO 0x034b
+#define OV9740_X_OUTPUT_SIZE_HI 0x034c
+#define OV9740_X_OUTPUT_SIZE_LO 0x034d
+#define OV9740_Y_OUTPUT_SIZE_HI 0x034e
+#define OV9740_Y_OUTPUT_SIZE_LO 0x034f
/* IO Control Registers */
#define OV9740_IO_CREL00 0x3002
@@ -68,6 +71,7 @@
#define OV9740_ANALOG_CTRL04 0x3604
#define OV9740_ANALOG_CTRL10 0x3610
#define OV9740_ANALOG_CTRL12 0x3612
+#define OV9740_ANALOG_CTRL15 0x3615
#define OV9740_ANALOG_CTRL20 0x3620
#define OV9740_ANALOG_CTRL21 0x3621
#define OV9740_ANALOG_CTRL22 0x3622
@@ -89,28 +93,28 @@
#define OV9740_TIMING_CTRL35 0x3835
/* Banding Filter */
-#define OV9740_AEC_MAXEXPO_60_H 0x3A02
-#define OV9740_AEC_MAXEXPO_60_L 0x3A03
-#define OV9740_AEC_B50_STEP_HI 0x3A08
-#define OV9740_AEC_B50_STEP_LO 0x3A09
-#define OV9740_AEC_B60_STEP_HI 0x3A0A
-#define OV9740_AEC_B60_STEP_LO 0x3A0B
-#define OV9740_AEC_CTRL0D 0x3A0D
-#define OV9740_AEC_CTRL0E 0x3A0E
-#define OV9740_AEC_MAXEXPO_50_H 0x3A14
-#define OV9740_AEC_MAXEXPO_50_L 0x3A15
+#define OV9740_AEC_MAXEXPO_60_H 0x3a02
+#define OV9740_AEC_MAXEXPO_60_L 0x3a03
+#define OV9740_AEC_B50_STEP_HI 0x3a08
+#define OV9740_AEC_B50_STEP_LO 0x3a09
+#define OV9740_AEC_B60_STEP_HI 0x3a0a
+#define OV9740_AEC_B60_STEP_LO 0x3a0b
+#define OV9740_AEC_CTRL0D 0x3a0d
+#define OV9740_AEC_CTRL0E 0x3a0e
+#define OV9740_AEC_MAXEXPO_50_H 0x3a14
+#define OV9740_AEC_MAXEXPO_50_L 0x3a15
/* AEC/AGC Control */
#define OV9740_AEC_ENABLE 0x3503
-#define OV9740_GAIN_CEILING_01 0x3A18
-#define OV9740_GAIN_CEILING_02 0x3A19
-#define OV9740_AEC_HI_THRESHOLD 0x3A11
-#define OV9740_AEC_3A1A 0x3A1A
-#define OV9740_AEC_CTRL1B_WPT2 0x3A1B
-#define OV9740_AEC_CTRL0F_WPT 0x3A0F
-#define OV9740_AEC_CTRL10_BPT 0x3A10
-#define OV9740_AEC_CTRL1E_BPT2 0x3A1E
-#define OV9740_AEC_LO_THRESHOLD 0x3A1F
+#define OV9740_GAIN_CEILING_01 0x3a18
+#define OV9740_GAIN_CEILING_02 0x3a19
+#define OV9740_AEC_HI_THRESHOLD 0x3a11
+#define OV9740_AEC_3A1A 0x3a1a
+#define OV9740_AEC_CTRL1B_WPT2 0x3a1b
+#define OV9740_AEC_CTRL0F_WPT 0x3a0f
+#define OV9740_AEC_CTRL10_BPT 0x3a10
+#define OV9740_AEC_CTRL1E_BPT2 0x3a1e
+#define OV9740_AEC_LO_THRESHOLD 0x3a1f
/* BLC Control */
#define OV9740_BLC_AUTO_ENABLE 0x4002
@@ -132,7 +136,7 @@
#define OV9740_VT_SYS_CLK_DIV 0x0303
#define OV9740_VT_PIX_CLK_DIV 0x0301
#define OV9740_PLL_CTRL3010 0x3010
-#define OV9740_VFIFO_CTRL00 0x460E
+#define OV9740_VFIFO_CTRL00 0x460e
/* ISP Control */
#define OV9740_ISP_CTRL00 0x5000
@@ -141,9 +145,9 @@
#define OV9740_ISP_CTRL05 0x5005
#define OV9740_ISP_CTRL12 0x5012
#define OV9740_ISP_CTRL19 0x5019
-#define OV9740_ISP_CTRL1A 0x501A
-#define OV9740_ISP_CTRL1E 0x501E
-#define OV9740_ISP_CTRL1F 0x501F
+#define OV9740_ISP_CTRL1A 0x501a
+#define OV9740_ISP_CTRL1E 0x501e
+#define OV9740_ISP_CTRL1F 0x501f
#define OV9740_ISP_CTRL20 0x5020
#define OV9740_ISP_CTRL21 0x5021
@@ -158,12 +162,12 @@
#define OV9740_AWB_ADV_CTRL04 0x5187
#define OV9740_AWB_ADV_CTRL05 0x5188
#define OV9740_AWB_ADV_CTRL06 0x5189
-#define OV9740_AWB_ADV_CTRL07 0x518A
-#define OV9740_AWB_ADV_CTRL08 0x518B
-#define OV9740_AWB_ADV_CTRL09 0x518C
-#define OV9740_AWB_ADV_CTRL10 0x518D
-#define OV9740_AWB_ADV_CTRL11 0x518E
-#define OV9740_AWB_CTRL0F 0x518F
+#define OV9740_AWB_ADV_CTRL07 0x518a
+#define OV9740_AWB_ADV_CTRL08 0x518b
+#define OV9740_AWB_ADV_CTRL09 0x518c
+#define OV9740_AWB_ADV_CTRL10 0x518d
+#define OV9740_AWB_ADV_CTRL11 0x518e
+#define OV9740_AWB_CTRL0F 0x518f
#define OV9740_AWB_CTRL10 0x5190
#define OV9740_AWB_CTRL11 0x5191
#define OV9740_AWB_CTRL12 0x5192
@@ -180,27 +184,8 @@
#define OV9740_MIPI_CTRL_3012 0x3012
#define OV9740_SC_CMMM_MIPI_CTR 0x3014
-/* supported resolutions */
-enum {
- OV9740_VGA,
- OV9740_720P,
-};
-
-struct ov9740_resolution {
- unsigned int width;
- unsigned int height;
-};
-
-static struct ov9740_resolution ov9740_resolutions[] = {
- [OV9740_VGA] = {
- .width = 640,
- .height = 480,
- },
- [OV9740_720P] = {
- .width = 1280,
- .height = 720,
- },
-};
+#define OV9740_MAX_WIDTH 1280
+#define OV9740_MAX_HEIGHT 720
/* Misc. structures */
struct ov9740_reg {
@@ -210,6 +195,7 @@ struct ov9740_reg {
struct ov9740_priv {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
int ident;
u16 model;
@@ -219,9 +205,16 @@ struct ov9740_priv {
bool flag_vflip;
bool flag_hflip;
+
+ /* For suspend/resume. */
+ struct v4l2_mbus_framefmt current_mf;
+ bool current_enable;
};
static const struct ov9740_reg ov9740_defaults[] = {
+ /* Software Reset */
+ { OV9740_SOFTWARE_RESET, 0x01 },
+
/* Banding Filter */
{ OV9740_AEC_B50_STEP_HI, 0x00 },
{ OV9740_AEC_B50_STEP_LO, 0xe8 },
@@ -241,36 +234,36 @@ static const struct ov9740_reg ov9740_defaults[] = {
/* Un-documented OV9740 registers */
{ 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 },
{ 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c },
- { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580A, 0x0e }, { 0x580B, 0x16 },
- { 0x580C, 0x06 }, { 0x580D, 0x02 }, { 0x580E, 0x00 }, { 0x580F, 0x00 },
+ { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580a, 0x0e }, { 0x580b, 0x16 },
+ { 0x580c, 0x06 }, { 0x580d, 0x02 }, { 0x580e, 0x00 }, { 0x580f, 0x00 },
{ 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 },
{ 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 },
- { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581A, 0x07 }, { 0x581B, 0x08 },
- { 0x581C, 0x0b }, { 0x581D, 0x14 }, { 0x581E, 0x28 }, { 0x581F, 0x23 },
+ { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581a, 0x07 }, { 0x581b, 0x08 },
+ { 0x581c, 0x0b }, { 0x581d, 0x14 }, { 0x581e, 0x28 }, { 0x581f, 0x23 },
{ 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a },
{ 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f },
- { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582A, 0x8f }, { 0x582B, 0x9e },
- { 0x582C, 0x8f }, { 0x582D, 0x9f }, { 0x582E, 0x4f }, { 0x582F, 0x87 },
+ { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582a, 0x8f }, { 0x582b, 0x9e },
+ { 0x582c, 0x8f }, { 0x582d, 0x9f }, { 0x582e, 0x4f }, { 0x582f, 0x87 },
{ 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f },
{ 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf },
- { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583A, 0x9f }, { 0x583B, 0x7f },
- { 0x583C, 0x5f },
+ { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583a, 0x9f }, { 0x583b, 0x7f },
+ { 0x583c, 0x5f },
/* Y Gamma */
{ 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e },
{ 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 },
- { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548A, 0xa4 }, { 0x548B, 0xb1 },
- { 0x548C, 0xc6 }, { 0x548D, 0xd8 }, { 0x548E, 0xe9 },
+ { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548a, 0xa4 }, { 0x548b, 0xb1 },
+ { 0x548c, 0xc6 }, { 0x548d, 0xd8 }, { 0x548e, 0xe9 },
/* UV Gamma */
{ 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 },
{ 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 },
- { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549A, 0x02 }, { 0x549B, 0xeb },
- { 0x549C, 0x02 }, { 0x549D, 0xa0 }, { 0x549E, 0x02 }, { 0x549F, 0x67 },
- { 0x54A0, 0x02 }, { 0x54A1, 0x3b }, { 0x54A2, 0x02 }, { 0x54A3, 0x18 },
- { 0x54A4, 0x01 }, { 0x54A5, 0xe7 }, { 0x54A6, 0x01 }, { 0x54A7, 0xc3 },
- { 0x54A8, 0x01 }, { 0x54A9, 0x94 }, { 0x54AA, 0x01 }, { 0x54AB, 0x72 },
- { 0x54AC, 0x01 }, { 0x54AD, 0x57 },
+ { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549a, 0x02 }, { 0x549b, 0xeb },
+ { 0x549c, 0x02 }, { 0x549d, 0xa0 }, { 0x549e, 0x02 }, { 0x549f, 0x67 },
+ { 0x54a0, 0x02 }, { 0x54a1, 0x3b }, { 0x54a2, 0x02 }, { 0x54a3, 0x18 },
+ { 0x54a4, 0x01 }, { 0x54a5, 0xe7 }, { 0x54a6, 0x01 }, { 0x54a7, 0xc3 },
+ { 0x54a8, 0x01 }, { 0x54a9, 0x94 }, { 0x54aa, 0x01 }, { 0x54ab, 0x72 },
+ { 0x54ac, 0x01 }, { 0x54ad, 0x57 },
/* AWB */
{ OV9740_AWB_CTRL00, 0xf0 },
@@ -296,18 +289,18 @@ static const struct ov9740_reg ov9740_defaults[] = {
{ OV9740_AWB_CTRL14, 0x00 },
/* CIP */
- { 0x530D, 0x12 },
+ { 0x530d, 0x12 },
/* CMX */
{ 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 },
{ 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 },
- { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538A, 0x00 }, { 0x538B, 0x20 },
- { 0x538C, 0x00 }, { 0x538D, 0x00 }, { 0x538E, 0x00 }, { 0x538F, 0x16 },
+ { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538a, 0x00 }, { 0x538b, 0x20 },
+ { 0x538c, 0x00 }, { 0x538d, 0x00 }, { 0x538e, 0x00 }, { 0x538f, 0x16 },
{ 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 },
{ 0x5394, 0x18 },
/* 50/60 Detection */
- { 0x3C0A, 0x9c }, { 0x3C0B, 0x3f },
+ { 0x3c0a, 0x9c }, { 0x3c0b, 0x3f },
/* Output Select */
{ OV9740_IO_OUTPUT_SEL01, 0x00 },
@@ -333,6 +326,7 @@ static const struct ov9740_reg ov9740_defaults[] = {
{ OV9740_ANALOG_CTRL10, 0xa1 },
{ OV9740_ANALOG_CTRL12, 0x24 },
{ OV9740_ANALOG_CTRL22, 0x9f },
+ { OV9740_ANALOG_CTRL15, 0xf0 },
/* Sensor Control */
{ OV9740_SENSOR_CTRL03, 0x42 },
@@ -385,7 +379,7 @@ static const struct ov9740_reg ov9740_defaults[] = {
{ OV9740_LN_LENGTH_PCK_LO, 0x62 },
/* MIPI Control */
- { OV9740_MIPI_CTRL00, 0x44 },
+ { OV9740_MIPI_CTRL00, 0x44 }, /* 0x64 for discontinuous clk */
{ OV9740_MIPI_3837, 0x01 },
{ OV9740_MIPI_CTRL01, 0x0f },
{ OV9740_MIPI_CTRL03, 0x05 },
@@ -393,81 +387,15 @@ static const struct ov9740_reg ov9740_defaults[] = {
{ OV9740_VFIFO_RD_CTRL, 0x16 },
{ OV9740_MIPI_CTRL_3012, 0x70 },
{ OV9740_SC_CMMM_MIPI_CTR, 0x01 },
-};
-
-static const struct ov9740_reg ov9740_regs_vga[] = {
- { OV9740_X_ADDR_START_HI, 0x00 },
- { OV9740_X_ADDR_START_LO, 0xa0 },
- { OV9740_Y_ADDR_START_HI, 0x00 },
- { OV9740_Y_ADDR_START_LO, 0x00 },
- { OV9740_X_ADDR_END_HI, 0x04 },
- { OV9740_X_ADDR_END_LO, 0x63 },
- { OV9740_Y_ADDR_END_HI, 0x02 },
- { OV9740_Y_ADDR_END_LO, 0xd3 },
- { OV9740_X_OUTPUT_SIZE_HI, 0x02 },
- { OV9740_X_OUTPUT_SIZE_LO, 0x80 },
- { OV9740_Y_OUTPUT_SIZE_HI, 0x01 },
- { OV9740_Y_OUTPUT_SIZE_LO, 0xe0 },
- { OV9740_ISP_CTRL1E, 0x03 },
- { OV9740_ISP_CTRL1F, 0xc0 },
- { OV9740_ISP_CTRL20, 0x02 },
- { OV9740_ISP_CTRL21, 0xd0 },
- { OV9740_VFIFO_READ_START_HI, 0x01 },
- { OV9740_VFIFO_READ_START_LO, 0x40 },
- { OV9740_ISP_CTRL00, 0xff },
- { OV9740_ISP_CTRL01, 0xff },
- { OV9740_ISP_CTRL03, 0xff },
-};
-static const struct ov9740_reg ov9740_regs_720p[] = {
- { OV9740_X_ADDR_START_HI, 0x00 },
- { OV9740_X_ADDR_START_LO, 0x00 },
- { OV9740_Y_ADDR_START_HI, 0x00 },
- { OV9740_Y_ADDR_START_LO, 0x00 },
- { OV9740_X_ADDR_END_HI, 0x05 },
- { OV9740_X_ADDR_END_LO, 0x03 },
- { OV9740_Y_ADDR_END_HI, 0x02 },
- { OV9740_Y_ADDR_END_LO, 0xd3 },
- { OV9740_X_OUTPUT_SIZE_HI, 0x05 },
- { OV9740_X_OUTPUT_SIZE_LO, 0x00 },
- { OV9740_Y_OUTPUT_SIZE_HI, 0x02 },
- { OV9740_Y_OUTPUT_SIZE_LO, 0xd0 },
- { OV9740_ISP_CTRL1E, 0x05 },
- { OV9740_ISP_CTRL1F, 0x00 },
- { OV9740_ISP_CTRL20, 0x02 },
- { OV9740_ISP_CTRL21, 0xd0 },
- { OV9740_VFIFO_READ_START_HI, 0x02 },
- { OV9740_VFIFO_READ_START_LO, 0x30 },
- { OV9740_ISP_CTRL00, 0xff },
- { OV9740_ISP_CTRL01, 0xef },
- { OV9740_ISP_CTRL03, 0xff },
+ /* YUYV order */
+ { OV9740_ISP_CTRL19, 0x02 },
};
static enum v4l2_mbus_pixelcode ov9740_codes[] = {
V4L2_MBUS_FMT_YUYV8_2X8,
};
-static const struct v4l2_queryctrl ov9740_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
/* read a register */
static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val)
{
@@ -537,7 +465,8 @@ static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset)
ret = ov9740_reg_read(client, reg, &val);
if (ret < 0) {
dev_err(&client->dev,
- "[Read]-Modify-Write of register %02x failed!\n", reg);
+ "[Read]-Modify-Write of register 0x%04x failed!\n",
+ reg);
return ret;
}
@@ -547,7 +476,8 @@ static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset)
ret = ov9740_reg_write(client, reg, val);
if (ret < 0) {
dev_err(&client->dev,
- "Read-Modify-[Write] of register %02x failed!\n", reg);
+ "Read-Modify-[Write] of register 0x%04x failed!\n",
+ reg);
return ret;
}
@@ -608,148 +538,132 @@ static int ov9740_s_stream(struct v4l2_subdev *sd, int enable)
0x00);
}
- return ret;
-}
+ priv->current_enable = enable;
-/* Alter bus settings on camera side */
-static int ov9740_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
+ return ret;
}
-/* Request bus settings on camera side */
-static unsigned long ov9740_query_bus_param(struct soc_camera_device *icd)
+/* select nearest higher resolution for capture */
+static void ov9740_res_roundup(u32 *width, u32 *height)
{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ /* Width must be a multiple of 4 pixels. */
+ *width = ALIGN(*width, 4);
- unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
+ /* Max resolution is 1280x720 (720p). */
+ if (*width > OV9740_MAX_WIDTH)
+ *width = OV9740_MAX_WIDTH;
- return soc_camera_apply_sensor_flags(icl, flags);
+ if (*height > OV9740_MAX_HEIGHT)
+ *height = OV9740_MAX_HEIGHT;
}
-/* Get status of additional camera capabilities */
-static int ov9740_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+/* Setup registers according to resolution and color encoding */
+static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height)
{
- struct ov9740_priv *priv = to_ov9740(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- ctrl->value = priv->flag_vflip;
- break;
- case V4L2_CID_HFLIP:
- ctrl->value = priv->flag_hflip;
- break;
- default:
- return -EINVAL;
- }
+ u32 x_start;
+ u32 y_start;
+ u32 x_end;
+ u32 y_end;
+ bool scaling = 0;
+ u32 scale_input_x;
+ u32 scale_input_y;
+ int ret;
- return 0;
-}
+ if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT))
+ scaling = 1;
-/* Set status of additional camera capabilities */
-static int ov9740_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct ov9740_priv *priv = to_ov9740(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- priv->flag_vflip = ctrl->value;
- break;
- case V4L2_CID_HFLIP:
- priv->flag_hflip = ctrl->value;
- break;
- default:
- return -EINVAL;
+ /*
+ * Try to use as much of the sensor area as possible when supporting
+ * smaller resolutions. Depending on the aspect ratio of the
+ * chosen resolution, we can either use the full width of the sensor,
+ * or the full height of the sensor (or both if the aspect ratio is
+ * the same as 1280x720.
+ */
+ if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) {
+ scale_input_x = (OV9740_MAX_HEIGHT * width) / height;
+ scale_input_y = OV9740_MAX_HEIGHT;
+ } else {
+ scale_input_x = OV9740_MAX_WIDTH;
+ scale_input_y = (OV9740_MAX_WIDTH * height) / width;
}
- return 0;
-}
-
-/* Get chip identification */
-static int ov9740_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct ov9740_priv *priv = to_ov9740(sd);
-
- id->ident = priv->ident;
- id->revision = priv->revision;
+ /* These describe the area of the sensor to use. */
+ x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2;
+ y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2;
+ x_end = x_start + scale_input_x - 1;
+ y_end = y_start + scale_input_y - 1;
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov9740_get_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
- u8 val;
-
- if (reg->reg & ~0xffff)
- return -EINVAL;
-
- reg->size = 2;
-
- ret = ov9740_reg_read(client, reg->reg, &val);
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8);
if (ret)
- return ret;
-
- reg->val = (__u64)val;
-
- return ret;
-}
-
-static int ov9740_set_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg & ~0xffff || reg->val & ~0xff)
- return -EINVAL;
-
- return ov9740_reg_write(client, reg->reg, reg->val);
-}
-#endif
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff);
+ if (ret)
+ goto done;
-/* select nearest higher resolution for capture */
-static void ov9740_res_roundup(u32 *width, u32 *height)
-{
- int i;
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff);
+ if (ret)
+ goto done;
- for (i = 0; i < ARRAY_SIZE(ov9740_resolutions); i++)
- if ((ov9740_resolutions[i].width >= *width) &&
- (ov9740_resolutions[i].height >= *height)) {
- *width = ov9740_resolutions[i].width;
- *height = ov9740_resolutions[i].height;
- return;
- }
+ ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff);
+ if (ret)
+ goto done;
- *width = ov9740_resolutions[OV9740_720P].width;
- *height = ov9740_resolutions[OV9740_720P].height;
-}
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff);
+ if (ret)
+ goto done;
-/* Setup registers according to resolution and color encoding */
-static int ov9740_set_res(struct i2c_client *client, u32 width)
-{
- int ret;
+ ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI,
+ (scale_input_x - width) >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO,
+ (scale_input_x - width) & 0xff);
+ if (ret)
+ goto done;
- /* select register configuration for given resolution */
- if (width == ov9740_resolutions[OV9740_VGA].width) {
- dev_dbg(&client->dev, "Setting image size to 640x480\n");
- ret = ov9740_reg_write_array(client, ov9740_regs_vga,
- ARRAY_SIZE(ov9740_regs_vga));
- } else if (width == ov9740_resolutions[OV9740_720P].width) {
- dev_dbg(&client->dev, "Setting image size to 1280x720\n");
- ret = ov9740_reg_write_array(client, ov9740_regs_720p,
- ARRAY_SIZE(ov9740_regs_720p));
- } else {
- dev_err(&client->dev, "Failed to select resolution!\n");
- return -EINVAL;
- }
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef |
+ (scaling << 4));
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff);
+done:
return ret;
}
@@ -758,6 +672,7 @@ static int ov9740_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov9740_priv *priv = to_ov9740(sd);
enum v4l2_colorspace cspace;
enum v4l2_mbus_pixelcode code = mf->code;
int ret;
@@ -777,13 +692,15 @@ static int ov9740_s_fmt(struct v4l2_subdev *sd,
if (ret < 0)
return ret;
- ret = ov9740_set_res(client, mf->width);
+ ret = ov9740_set_res(client, mf->width, mf->height);
if (ret < 0)
return ret;
mf->code = code;
mf->colorspace = cspace;
+ memcpy(&priv->current_mf, mf, sizeof(struct v4l2_mbus_framefmt));
+
return ret;
}
@@ -814,8 +731,8 @@ static int ov9740_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = 0;
a->bounds.top = 0;
- a->bounds.width = ov9740_resolutions[OV9740_720P].width;
- a->bounds.height = ov9740_resolutions[OV9740_720P].height;
+ a->bounds.width = OV9740_MAX_WIDTH;
+ a->bounds.height = OV9740_MAX_HEIGHT;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
@@ -828,15 +745,98 @@ static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
a->c.left = 0;
a->c.top = 0;
- a->c.width = ov9740_resolutions[OV9740_720P].width;
- a->c.height = ov9740_resolutions[OV9740_720P].height;
+ a->c.width = OV9740_MAX_WIDTH;
+ a->c.height = OV9740_MAX_HEIGHT;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
-static int ov9740_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+/* Set status of additional camera capabilities */
+static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov9740_priv *priv =
+ container_of(ctrl->handler, struct ov9740_priv, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ priv->flag_vflip = ctrl->val;
+ break;
+ case V4L2_CID_HFLIP:
+ priv->flag_hflip = ctrl->val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Get chip identification */
+static int ov9740_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct ov9740_priv *priv = to_ov9740(sd);
+
+ id->ident = priv->ident;
+ id->revision = priv->revision;
+
+ return 0;
+}
+
+static int ov9740_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct ov9740_priv *priv = to_ov9740(sd);
+
+ if (!priv->current_enable)
+ return 0;
+
+ if (on) {
+ ov9740_s_fmt(sd, &priv->current_mf);
+ ov9740_s_stream(sd, priv->current_enable);
+ } else {
+ ov9740_s_stream(sd, 0);
+ priv->current_enable = true;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov9740_get_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ u8 val;
+
+ if (reg->reg & ~0xffff)
+ return -EINVAL;
+
+ reg->size = 2;
+
+ ret = ov9740_reg_read(client, reg->reg, &val);
+ if (ret)
+ return ret;
+
+ reg->val = (__u64)val;
+
+ return ret;
+}
+
+static int ov9740_set_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg & ~0xffff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return ov9740_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov9740_video_probe(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov9740_priv *priv = to_ov9740(sd);
@@ -844,17 +844,6 @@ static int ov9740_video_probe(struct soc_camera_device *icd,
int ret;
/*
- * We must have a parent by now. And it cannot be a wrong one.
- * So this entire test is completely redundant.
- */
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface) {
- dev_err(&client->dev, "Parent missing or invalid!\n");
- ret = -ENODEV;
- goto err;
- }
-
- /*
* check and show product ID and manufacturer ID
*/
ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi);
@@ -894,31 +883,39 @@ err:
return ret;
}
-static struct soc_camera_ops ov9740_ops = {
- .set_bus_param = ov9740_set_bus_param,
- .query_bus_param = ov9740_query_bus_param,
- .controls = ov9740_controls,
- .num_controls = ARRAY_SIZE(ov9740_controls),
+/* Request bus settings on camera side */
+static int ov9740_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov9740_video_ops = {
+ .s_stream = ov9740_s_stream,
+ .s_mbus_fmt = ov9740_s_fmt,
+ .try_mbus_fmt = ov9740_try_fmt,
+ .enum_mbus_fmt = ov9740_enum_fmt,
+ .cropcap = ov9740_cropcap,
+ .g_crop = ov9740_g_crop,
+ .g_mbus_config = ov9740_g_mbus_config,
};
static struct v4l2_subdev_core_ops ov9740_core_ops = {
- .g_ctrl = ov9740_g_ctrl,
- .s_ctrl = ov9740_s_ctrl,
.g_chip_ident = ov9740_g_chip_ident,
+ .s_power = ov9740_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov9740_get_register,
.s_register = ov9740_set_register,
#endif
-
-};
-
-static struct v4l2_subdev_video_ops ov9740_video_ops = {
- .s_stream = ov9740_s_stream,
- .s_mbus_fmt = ov9740_s_fmt,
- .try_mbus_fmt = ov9740_try_fmt,
- .enum_mbus_fmt = ov9740_enum_fmt,
- .cropcap = ov9740_cropcap,
- .g_crop = ov9740_g_crop,
};
static struct v4l2_subdev_ops ov9740_subdev_ops = {
@@ -926,6 +923,10 @@ static struct v4l2_subdev_ops ov9740_subdev_ops = {
.video = &ov9740_video_ops,
};
+static const struct v4l2_ctrl_ops ov9740_ctrl_ops = {
+ .s_ctrl = ov9740_s_ctrl,
+};
+
/*
* i2c_driver function
*/
@@ -933,16 +934,9 @@ static int ov9740_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov9740_priv *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "Missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "Missing platform_data for driver\n");
return -EINVAL;
@@ -955,12 +949,24 @@ static int ov9740_probe(struct i2c_client *client,
}
v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 13);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
- icd->ops = &ov9740_ops;
+ kfree(priv);
+ return err;
+ }
- ret = ov9740_video_probe(icd, client);
+ ret = ov9740_video_probe(client);
+ if (!ret)
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
if (ret < 0) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
}
@@ -971,8 +977,9 @@ static int ov9740_remove(struct i2c_client *client)
{
struct ov9740_priv *priv = i2c_get_clientdata(client);
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
-
return 0;
}
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c
index 7551907f8c28..e753b5e4d2ce 100644
--- a/drivers/media/video/pms.c
+++ b/drivers/media/video/pms.c
@@ -28,7 +28,6 @@
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
-#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <asm/io.h>
@@ -39,7 +38,7 @@
#include <media/v4l2-device.h>
MODULE_LICENSE("GPL");
-
+MODULE_VERSION("0.0.4");
#define MOTOROLA 1
#define PHILIPS2 2 /* SAA7191 */
@@ -678,7 +677,6 @@ static int pms_querycap(struct file *file, void *priv,
strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info));
- vcap->version = KERNEL_VERSION(0, 0, 3);
vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
return 0;
}
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
index de2fc14f043b..c17f37d964ad 100644
--- a/drivers/media/video/pvrusb2/Makefile
+++ b/drivers/media/video/pvrusb2/Makefile
@@ -16,7 +16,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o \
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
index e799331389b1..c6da8f77e1a2 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
@@ -28,6 +28,7 @@ pvr2_device_desc structures.
#include "pvrusb2-devattr.h"
#include <linux/usb.h>
+#include <linux/module.h>
/* This is needed in order to pull in tuner type ids... */
#include <linux/i2c.h>
#include <media/tuner.h>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index e98d38212791..122b45760f0d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -21,6 +21,7 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
@@ -2993,6 +2994,13 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \
}
+int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std)
+{
+ v4l2_device_call_all(&hdw->v4l2_dev, 0,
+ video, querystd, std);
+ return 0;
+}
+
/* Execute whatever commands are required to update the state of all the
sub-devices so that they match our current control values. */
static void pvr2_subdev_update(struct pvr2_hdw *hdw)
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index d7753ae9ff46..66546580b17d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -214,6 +214,9 @@ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *);
int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
unsigned int idx);
+/* Get the detected video standard */
+int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std);
+
/* Enable / disable retrieval of CPU firmware or prom contents. This must
be enabled before pvr2_hdw_cpufw_get() will function. Note that doing
this may prevent the device from running (and leaving this mode may
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index e72d5103e778..885ce11f222d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -19,6 +19,7 @@
*/
#include <linux/i2c.h>
+#include <linux/module.h>
#include <media/ir-kbd-i2c.h>
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-hdw-internal.h"
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c
index 2254194aad57..c1d9bb61cd77 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-main.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-main.c
@@ -168,6 +168,7 @@ module_exit(pvr_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.9.1");
/*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 38761142a4d9..6d666174dbb4 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -29,6 +29,7 @@
#include "pvrusb2-v4l2.h"
#include "pvrusb2-ioread.h"
#include <linux/videodev2.h>
+#include <linux/module.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
@@ -91,7 +92,7 @@ static struct v4l2_capability pvr_capability ={
.driver = "pvrusb2",
.card = "Hauppauge WinTV pvr-usb2",
.bus_info = "usb",
- .version = KERNEL_VERSION(0, 9, 0),
+ .version = LINUX_VERSION_CODE,
.capabilities = (V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
V4L2_CAP_READWRITE),
@@ -227,6 +228,14 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
break;
}
+ case VIDIOC_QUERYSTD:
+ {
+ v4l2_std_id *std = arg;
+ *std = V4L2_STD_ALL;
+ ret = pvr2_hdw_get_detected_std(hdw, std);
+ break;
+ }
+
case VIDIOC_G_STD:
{
int val = 0;
@@ -369,11 +378,6 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
break;
}
- case VIDIOC_S_AUDIO:
- {
- ret = -EINVAL;
- break;
- }
case VIDIOC_G_TUNER:
{
struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
@@ -850,7 +854,7 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
#endif
default :
- ret = -EINVAL;
+ ret = -ENOTTY;
break;
}
diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig
index 8da42e4f1ba0..d63d0a850035 100644
--- a/drivers/media/video/pwc/Kconfig
+++ b/drivers/media/video/pwc/Kconfig
@@ -1,6 +1,7 @@
config USB_PWC
tristate "USB Philips Cameras"
depends on VIDEO_V4L2
+ select VIDEOBUF2_VMALLOC
---help---
Say Y or M here if you want to use one of these Philips & OEM
webcams:
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
index 760b4de13adf..3977addf3ba8 100644
--- a/drivers/media/video/pwc/pwc-ctrl.c
+++ b/drivers/media/video/pwc/pwc-ctrl.c
@@ -3,6 +3,7 @@
video modes.
(C) 1999-2003 Nemosoft Unv.
(C) 2004-2006 Luc Saillard (luc@saillard.org)
+ (C) 2011 Hans de Goede <hdegoede@redhat.com>
NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
driver and thus may have bugs that are not present in the original version.
@@ -43,61 +44,12 @@
#include <asm/errno.h>
#include "pwc.h"
-#include "pwc-uncompress.h"
#include "pwc-kiara.h"
#include "pwc-timon.h"
#include "pwc-dec1.h"
#include "pwc-dec23.h"
-/* Request types: video */
-#define SET_LUM_CTL 0x01
-#define GET_LUM_CTL 0x02
-#define SET_CHROM_CTL 0x03
-#define GET_CHROM_CTL 0x04
-#define SET_STATUS_CTL 0x05
-#define GET_STATUS_CTL 0x06
-#define SET_EP_STREAM_CTL 0x07
-#define GET_EP_STREAM_CTL 0x08
-#define GET_XX_CTL 0x09
-#define SET_XX_CTL 0x0A
-#define GET_XY_CTL 0x0B
-#define SET_XY_CTL 0x0C
-#define SET_MPT_CTL 0x0D
-#define GET_MPT_CTL 0x0E
-
-/* Selectors for the Luminance controls [GS]ET_LUM_CTL */
-#define AGC_MODE_FORMATTER 0x2000
-#define PRESET_AGC_FORMATTER 0x2100
-#define SHUTTER_MODE_FORMATTER 0x2200
-#define PRESET_SHUTTER_FORMATTER 0x2300
-#define PRESET_CONTOUR_FORMATTER 0x2400
-#define AUTO_CONTOUR_FORMATTER 0x2500
-#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600
-#define CONTRAST_FORMATTER 0x2700
-#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800
-#define FLICKERLESS_MODE_FORMATTER 0x2900
-#define AE_CONTROL_SPEED 0x2A00
-#define BRIGHTNESS_FORMATTER 0x2B00
-#define GAMMA_FORMATTER 0x2C00
-
-/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */
-#define WB_MODE_FORMATTER 0x1000
-#define AWB_CONTROL_SPEED_FORMATTER 0x1100
-#define AWB_CONTROL_DELAY_FORMATTER 0x1200
-#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300
-#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400
-#define COLOUR_MODE_FORMATTER 0x1500
-#define SATURATION_MODE_FORMATTER1 0x1600
-#define SATURATION_MODE_FORMATTER2 0x1700
-
-/* Selectors for the Status controls [GS]ET_STATUS_CTL */
-#define SAVE_USER_DEFAULTS_FORMATTER 0x0200
-#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300
-#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400
-#define READ_AGC_FORMATTER 0x0500
-#define READ_SHUTTER_FORMATTER 0x0600
-#define READ_RED_GAIN_FORMATTER 0x0700
-#define READ_BLUE_GAIN_FORMATTER 0x0800
+/* Selectors for status controls used only in this file */
#define GET_STATUS_B00 0x0B00
#define SENSOR_TYPE_FORMATTER1 0x0C00
#define GET_STATUS_3000 0x3000
@@ -116,11 +68,6 @@
/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */
#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100
-/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */
-#define PT_RELATIVE_CONTROL_FORMATTER 0x01
-#define PT_RESET_CONTROL_FORMATTER 0x02
-#define PT_STATUS_FORMATTER 0x03
-
static const char *size2name[PSZ_MAX] =
{
"subQCIF",
@@ -160,7 +107,7 @@ static void pwc_set_image_buffer_size(struct pwc_device *pdev);
/****************************************************************************/
static int _send_control_msg(struct pwc_device *pdev,
- u8 request, u16 value, int index, void *buf, int buflen, int timeout)
+ u8 request, u16 value, int index, void *buf, int buflen)
{
int rc;
void *kbuf = NULL;
@@ -177,7 +124,7 @@ static int _send_control_msg(struct pwc_device *pdev,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value,
index,
- kbuf, buflen, timeout);
+ kbuf, buflen, USB_CTRL_SET_TIMEOUT);
kfree(kbuf);
return rc;
@@ -197,9 +144,13 @@ static int recv_control_msg(struct pwc_device *pdev,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value,
pdev->vcinterface,
- kbuf, buflen, 500);
+ kbuf, buflen, USB_CTRL_GET_TIMEOUT);
memcpy(buf, kbuf, buflen);
kfree(kbuf);
+
+ if (rc < 0)
+ PWC_ERROR("recv_control_msg error %d req %02x val %04x\n",
+ rc, request, value);
return rc;
}
@@ -210,18 +161,16 @@ static inline int send_video_command(struct pwc_device *pdev,
SET_EP_STREAM_CTL,
VIDEO_OUTPUT_CONTROL_FORMATTER,
index,
- buf, buflen, 1000);
+ buf, buflen);
}
-static inline int send_control_msg(struct pwc_device *pdev,
+int send_control_msg(struct pwc_device *pdev,
u8 request, u16 value, void *buf, int buflen)
{
return _send_control_msg(pdev,
- request, value, pdev->vcinterface, buf, buflen, 500);
+ request, value, pdev->vcinterface, buf, buflen);
}
-
-
static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames)
{
unsigned char buf[3];
@@ -261,8 +210,11 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames)
PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret);
return ret;
}
- if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420)
- pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data);
+ if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
+ ret = pwc_dec1_init(pdev, pdev->type, pdev->release, buf);
+ if (ret < 0)
+ return ret;
+ }
pdev->cmd_len = 3;
memcpy(pdev->cmd_buf, buf, 3);
@@ -321,8 +273,11 @@ static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, i
if (ret < 0)
return ret;
- if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420)
- pwc_dec23_init(pdev, pdev->type, buf);
+ if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
+ ret = pwc_dec23_init(pdev, pdev->type, buf);
+ if (ret < 0)
+ return ret;
+ }
pdev->cmd_len = 13;
memcpy(pdev->cmd_buf, buf, 13);
@@ -394,8 +349,11 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, i
if (ret < 0)
return ret;
- if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420)
- pwc_dec23_init(pdev, pdev->type, buf);
+ if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
+ ret = pwc_dec23_init(pdev, pdev->type, buf);
+ if (ret < 0)
+ return ret;
+ }
pdev->cmd_len = 12;
memcpy(pdev->cmd_buf, buf, 12);
@@ -452,6 +410,7 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame
}
pdev->view.x = width;
pdev->view.y = height;
+ pdev->vcompression = compression;
pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size;
pwc_set_image_buffer_size(pdev);
PWC_DEBUG_SIZE("Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y);
@@ -511,13 +470,9 @@ unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned i
return ret;
}
-#define BLACK_Y 0
-#define BLACK_U 128
-#define BLACK_V 128
-
static void pwc_set_image_buffer_size(struct pwc_device *pdev)
{
- int i, factor = 0;
+ int factor = 0;
/* for V4L2_PIX_FMT_YUV420 */
switch (pdev->pixfmt) {
@@ -541,442 +496,108 @@ static void pwc_set_image_buffer_size(struct pwc_device *pdev)
*/
pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC;
pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE;
-
- /* Fill buffers with black colors */
- for (i = 0; i < pwc_mbufs; i++) {
- unsigned char *p = pdev->image_data + pdev->images[i].offset;
- memset(p, BLACK_Y, pdev->view.x * pdev->view.y);
- p += pdev->view.x * pdev->view.y;
- memset(p, BLACK_U, pdev->view.x * pdev->view.y/4);
- p += pdev->view.x * pdev->view.y/4;
- memset(p, BLACK_V, pdev->view.x * pdev->view.y/4);
- }
}
-
-
-/* BRIGHTNESS */
-
-int pwc_get_brightness(struct pwc_device *pdev)
+int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
{
- char buf;
int ret;
+ u8 buf;
- ret = recv_control_msg(pdev,
- GET_LUM_CTL, BRIGHTNESS_FORMATTER, &buf, sizeof(buf));
+ ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf));
if (ret < 0)
return ret;
- return buf;
-}
-int pwc_set_brightness(struct pwc_device *pdev, int value)
-{
- char buf;
-
- if (value < 0)
- value = 0;
- if (value > 0xffff)
- value = 0xffff;
- buf = (value >> 9) & 0x7f;
- return send_control_msg(pdev,
- SET_LUM_CTL, BRIGHTNESS_FORMATTER, &buf, sizeof(buf));
+ *data = buf;
+ return 0;
}
-/* CONTRAST */
-
-int pwc_get_contrast(struct pwc_device *pdev)
+int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data)
{
- char buf;
int ret;
- ret = recv_control_msg(pdev,
- GET_LUM_CTL, CONTRAST_FORMATTER, &buf, sizeof(buf));
+ ret = send_control_msg(pdev, request, value, &data, sizeof(data));
if (ret < 0)
return ret;
- return buf;
-}
-int pwc_set_contrast(struct pwc_device *pdev, int value)
-{
- char buf;
-
- if (value < 0)
- value = 0;
- if (value > 0xffff)
- value = 0xffff;
- buf = (value >> 10) & 0x3f;
- return send_control_msg(pdev,
- SET_LUM_CTL, CONTRAST_FORMATTER, &buf, sizeof(buf));
+ return 0;
}
-/* GAMMA */
-
-int pwc_get_gamma(struct pwc_device *pdev)
+int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
{
- char buf;
int ret;
+ s8 buf;
- ret = recv_control_msg(pdev,
- GET_LUM_CTL, GAMMA_FORMATTER, &buf, sizeof(buf));
+ ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf));
if (ret < 0)
return ret;
- return buf;
-}
-
-int pwc_set_gamma(struct pwc_device *pdev, int value)
-{
- char buf;
- if (value < 0)
- value = 0;
- if (value > 0xffff)
- value = 0xffff;
- buf = (value >> 11) & 0x1f;
- return send_control_msg(pdev,
- SET_LUM_CTL, GAMMA_FORMATTER, &buf, sizeof(buf));
-}
-
-
-/* SATURATION */
-
-/* return a value between [-100 , 100] */
-int pwc_get_saturation(struct pwc_device *pdev, int *value)
-{
- char buf;
- int ret, saturation_register;
-
- if (pdev->type < 675)
- return -EINVAL;
- if (pdev->type < 730)
- saturation_register = SATURATION_MODE_FORMATTER2;
- else
- saturation_register = SATURATION_MODE_FORMATTER1;
- ret = recv_control_msg(pdev,
- GET_CHROM_CTL, saturation_register, &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- *value = (signed)buf;
+ *data = buf;
return 0;
}
-/* @param value saturation color between [-100 , 100] */
-int pwc_set_saturation(struct pwc_device *pdev, int value)
+int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
{
- char buf;
- int saturation_register;
-
- if (pdev->type < 675)
- return -EINVAL;
- if (value < -100)
- value = -100;
- if (value > 100)
- value = 100;
- if (pdev->type < 730)
- saturation_register = SATURATION_MODE_FORMATTER2;
- else
- saturation_register = SATURATION_MODE_FORMATTER1;
- return send_control_msg(pdev,
- SET_CHROM_CTL, saturation_register, &buf, sizeof(buf));
-}
-
-/* AGC */
-
-int pwc_set_agc(struct pwc_device *pdev, int mode, int value)
-{
- char buf;
int ret;
+ u8 buf[2];
- if (mode)
- buf = 0x0; /* auto */
- else
- buf = 0xff; /* fixed */
-
- ret = send_control_msg(pdev,
- SET_LUM_CTL, AGC_MODE_FORMATTER, &buf, sizeof(buf));
-
- if (!mode && ret >= 0) {
- if (value < 0)
- value = 0;
- if (value > 0xffff)
- value = 0xffff;
- buf = (value >> 10) & 0x3F;
- ret = send_control_msg(pdev,
- SET_LUM_CTL, PRESET_AGC_FORMATTER, &buf, sizeof(buf));
- }
+ ret = recv_control_msg(pdev, request, value, buf, sizeof(buf));
if (ret < 0)
return ret;
+
+ *data = (buf[1] << 8) | buf[0];
return 0;
}
-int pwc_get_agc(struct pwc_device *pdev, int *value)
+int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data)
{
- unsigned char buf;
int ret;
+ u8 buf[2];
- ret = recv_control_msg(pdev,
- GET_LUM_CTL, AGC_MODE_FORMATTER, &buf, sizeof(buf));
+ buf[0] = data & 0xff;
+ buf[1] = data >> 8;
+ ret = send_control_msg(pdev, request, value, buf, sizeof(buf));
if (ret < 0)
return ret;
- if (buf != 0) { /* fixed */
- ret = recv_control_msg(pdev,
- GET_LUM_CTL, PRESET_AGC_FORMATTER, &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- if (buf > 0x3F)
- buf = 0x3F;
- *value = (buf << 10);
- }
- else { /* auto */
- ret = recv_control_msg(pdev,
- GET_STATUS_CTL, READ_AGC_FORMATTER, &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- /* Gah... this value ranges from 0x00 ... 0x9F */
- if (buf > 0x9F)
- buf = 0x9F;
- *value = -(48 + buf * 409);
- }
-
return 0;
}
-int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value)
-{
- char buf[2];
- int speed, ret;
-
-
- if (mode)
- buf[0] = 0x0; /* auto */
- else
- buf[0] = 0xff; /* fixed */
-
- ret = send_control_msg(pdev,
- SET_LUM_CTL, SHUTTER_MODE_FORMATTER, &buf, 1);
-
- if (!mode && ret >= 0) {
- if (value < 0)
- value = 0;
- if (value > 0xffff)
- value = 0xffff;
-
- if (DEVICE_USE_CODEC2(pdev->type)) {
- /* speed ranges from 0x0 to 0x290 (656) */
- speed = (value / 100);
- buf[1] = speed >> 8;
- buf[0] = speed & 0xff;
- } else if (DEVICE_USE_CODEC3(pdev->type)) {
- /* speed seems to range from 0x0 to 0xff */
- buf[1] = 0;
- buf[0] = value >> 8;
- }
-
- ret = send_control_msg(pdev,
- SET_LUM_CTL, PRESET_SHUTTER_FORMATTER,
- &buf, sizeof(buf));
- }
- return ret;
-}
-
-/* This function is not exported to v4l1, so output values between 0 -> 256 */
-int pwc_get_shutter_speed(struct pwc_device *pdev, int *value)
+int pwc_button_ctrl(struct pwc_device *pdev, u16 value)
{
- unsigned char buf[2];
int ret;
- ret = recv_control_msg(pdev,
- GET_STATUS_CTL, READ_SHUTTER_FORMATTER, &buf, sizeof(buf));
+ ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0);
if (ret < 0)
return ret;
- *value = buf[0] + (buf[1] << 8);
- if (DEVICE_USE_CODEC2(pdev->type)) {
- /* speed ranges from 0x0 to 0x290 (656) */
- *value *= 256/656;
- } else if (DEVICE_USE_CODEC3(pdev->type)) {
- /* speed seems to range from 0x0 to 0xff */
- }
+
return 0;
}
-
/* POWER */
-
-int pwc_camera_power(struct pwc_device *pdev, int power)
+void pwc_camera_power(struct pwc_device *pdev, int power)
{
char buf;
+ int r;
+
+ if (!pdev->power_save)
+ return;
if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6))
- return 0; /* Not supported by Nala or Timon < release 6 */
+ return; /* Not supported by Nala or Timon < release 6 */
if (power)
buf = 0x00; /* active */
else
buf = 0xFF; /* power save */
- return send_control_msg(pdev,
+ r = send_control_msg(pdev,
SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER,
&buf, sizeof(buf));
-}
-
-
-
-/* private calls */
-
-int pwc_restore_user(struct pwc_device *pdev)
-{
- return send_control_msg(pdev,
- SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, NULL, 0);
-}
-
-int pwc_save_user(struct pwc_device *pdev)
-{
- return send_control_msg(pdev,
- SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, NULL, 0);
-}
-
-int pwc_restore_factory(struct pwc_device *pdev)
-{
- return send_control_msg(pdev,
- SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, NULL, 0);
-}
-
- /* ************************************************* */
- /* Patch by Alvarado: (not in the original version */
-
- /*
- * the camera recognizes modes from 0 to 4:
- *
- * 00: indoor (incandescant lighting)
- * 01: outdoor (sunlight)
- * 02: fluorescent lighting
- * 03: manual
- * 04: auto
- */
-int pwc_set_awb(struct pwc_device *pdev, int mode)
-{
- char buf;
- int ret;
-
- if (mode < 0)
- mode = 0;
-
- if (mode > 4)
- mode = 4;
-
- buf = mode & 0x07; /* just the lowest three bits */
-
- ret = send_control_msg(pdev,
- SET_CHROM_CTL, WB_MODE_FORMATTER, &buf, sizeof(buf));
-
- if (ret < 0)
- return ret;
- return 0;
-}
-
-int pwc_get_awb(struct pwc_device *pdev)
-{
- unsigned char buf;
- int ret;
-
- ret = recv_control_msg(pdev,
- GET_CHROM_CTL, WB_MODE_FORMATTER, &buf, sizeof(buf));
-
- if (ret < 0)
- return ret;
- return buf;
-}
-
-int pwc_set_red_gain(struct pwc_device *pdev, int value)
-{
- unsigned char buf;
-
- if (value < 0)
- value = 0;
- if (value > 0xffff)
- value = 0xffff;
- /* only the msb is considered */
- buf = value >> 8;
- return send_control_msg(pdev,
- SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER,
- &buf, sizeof(buf));
-}
-
-int pwc_get_red_gain(struct pwc_device *pdev, int *value)
-{
- unsigned char buf;
- int ret;
-
- ret = recv_control_msg(pdev,
- GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER,
- &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- *value = buf << 8;
- return 0;
-}
-
-
-int pwc_set_blue_gain(struct pwc_device *pdev, int value)
-{
- unsigned char buf;
-
- if (value < 0)
- value = 0;
- if (value > 0xffff)
- value = 0xffff;
- /* only the msb is considered */
- buf = value >> 8;
- return send_control_msg(pdev,
- SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER,
- &buf, sizeof(buf));
-}
-
-int pwc_get_blue_gain(struct pwc_device *pdev, int *value)
-{
- unsigned char buf;
- int ret;
-
- ret = recv_control_msg(pdev,
- GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER,
- &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- *value = buf << 8;
- return 0;
-}
-
-/* The following two functions are different, since they only read the
- internal red/blue gains, which may be different from the manual
- gains set or read above.
- */
-static int pwc_read_red_gain(struct pwc_device *pdev, int *value)
-{
- unsigned char buf;
- int ret;
-
- ret = recv_control_msg(pdev,
- GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- *value = buf << 8;
- return 0;
+ if (r < 0)
+ PWC_ERROR("Failed to power %s camera (%d)\n",
+ power ? "on" : "off", r);
}
-static int pwc_read_blue_gain(struct pwc_device *pdev, int *value)
-{
- unsigned char buf;
- int ret;
-
- ret = recv_control_msg(pdev,
- GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- *value = buf << 8;
- return 0;
-}
-
-
static int pwc_set_wb_speed(struct pwc_device *pdev, int speed)
{
unsigned char buf;
@@ -1028,6 +649,7 @@ static int pwc_get_wb_delay(struct pwc_device *pdev, int *value)
int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
{
unsigned char buf[2];
+ int r;
if (pdev->type < 730)
return 0;
@@ -1045,8 +667,12 @@ int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
buf[0] = on_value;
buf[1] = off_value;
- return send_control_msg(pdev,
+ r = send_control_msg(pdev,
SET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf));
+ if (r < 0)
+ PWC_ERROR("Failed to set LED on/off time (%d)\n", r);
+
+ return r;
}
static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
@@ -1069,164 +695,6 @@ static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
return 0;
}
-int pwc_set_contour(struct pwc_device *pdev, int contour)
-{
- unsigned char buf;
- int ret;
-
- if (contour < 0)
- buf = 0xff; /* auto contour on */
- else
- buf = 0x0; /* auto contour off */
- ret = send_control_msg(pdev,
- SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &buf, sizeof(buf));
- if (ret < 0)
- return ret;
-
- if (contour < 0)
- return 0;
- if (contour > 0xffff)
- contour = 0xffff;
-
- buf = (contour >> 10); /* contour preset is [0..3f] */
- ret = send_control_msg(pdev,
- SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- return 0;
-}
-
-int pwc_get_contour(struct pwc_device *pdev, int *contour)
-{
- unsigned char buf;
- int ret;
-
- ret = recv_control_msg(pdev,
- GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &buf, sizeof(buf));
- if (ret < 0)
- return ret;
-
- if (buf == 0) {
- /* auto mode off, query current preset value */
- ret = recv_control_msg(pdev,
- GET_LUM_CTL, PRESET_CONTOUR_FORMATTER,
- &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- *contour = buf << 10;
- }
- else
- *contour = -1;
- return 0;
-}
-
-
-int pwc_set_backlight(struct pwc_device *pdev, int backlight)
-{
- unsigned char buf;
-
- if (backlight)
- buf = 0xff;
- else
- buf = 0x0;
- return send_control_msg(pdev,
- SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER,
- &buf, sizeof(buf));
-}
-
-int pwc_get_backlight(struct pwc_device *pdev, int *backlight)
-{
- int ret;
- unsigned char buf;
-
- ret = recv_control_msg(pdev,
- GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER,
- &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- *backlight = !!buf;
- return 0;
-}
-
-int pwc_set_colour_mode(struct pwc_device *pdev, int colour)
-{
- unsigned char buf;
-
- if (colour)
- buf = 0xff;
- else
- buf = 0x0;
- return send_control_msg(pdev,
- SET_CHROM_CTL, COLOUR_MODE_FORMATTER, &buf, sizeof(buf));
-}
-
-int pwc_get_colour_mode(struct pwc_device *pdev, int *colour)
-{
- int ret;
- unsigned char buf;
-
- ret = recv_control_msg(pdev,
- GET_CHROM_CTL, COLOUR_MODE_FORMATTER, &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- *colour = !!buf;
- return 0;
-}
-
-
-int pwc_set_flicker(struct pwc_device *pdev, int flicker)
-{
- unsigned char buf;
-
- if (flicker)
- buf = 0xff;
- else
- buf = 0x0;
- return send_control_msg(pdev,
- SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, &buf, sizeof(buf));
-}
-
-int pwc_get_flicker(struct pwc_device *pdev, int *flicker)
-{
- int ret;
- unsigned char buf;
-
- ret = recv_control_msg(pdev,
- GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- *flicker = !!buf;
- return 0;
-}
-
-int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise)
-{
- unsigned char buf;
-
- if (noise < 0)
- noise = 0;
- if (noise > 3)
- noise = 3;
- buf = noise;
- return send_control_msg(pdev,
- SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER,
- &buf, sizeof(buf));
-}
-
-int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise)
-{
- int ret;
- unsigned char buf;
-
- ret = recv_control_msg(pdev,
- GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER,
- &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- *noise = buf;
- return 0;
-}
-
static int _pwc_mpt_reset(struct pwc_device *pdev, int flags)
{
unsigned char buf;
@@ -1309,7 +777,7 @@ static int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *st
return 0;
}
-
+#ifdef CONFIG_USB_PWC_DEBUG
int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
{
unsigned char buf;
@@ -1332,7 +800,7 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
*sensor = buf;
return 0;
}
-
+#endif
/* End of Add-Ons */
/* ************************************************* */
@@ -1356,37 +824,41 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
/* copy local variable to arg */
#define ARG_OUT(ARG_name) /* nothing */
+/*
+ * Our ctrls use native values, but the old custom pwc ioctl interface expects
+ * values from 0 - 65535, define 2 helper functions to scale things. */
+static int pwc_ioctl_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+ return v4l2_ctrl_g_ctrl(ctrl) * 65535 / ctrl->maximum;
+}
+
+static int pwc_ioctl_s_ctrl(struct v4l2_ctrl *ctrl, int val)
+{
+ return v4l2_ctrl_s_ctrl(ctrl, val * ctrl->maximum / 65535);
+}
+
long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
{
long ret = 0;
switch(cmd) {
case VIDIOCPWCRUSER:
- {
- if (pwc_restore_user(pdev))
- ret = -EINVAL;
+ ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER);
break;
- }
case VIDIOCPWCSUSER:
- {
- if (pwc_save_user(pdev))
- ret = -EINVAL;
+ ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER);
break;
- }
case VIDIOCPWCFACTORY:
- {
- if (pwc_restore_factory(pdev))
- ret = -EINVAL;
+ ret = pwc_button_ctrl(pdev, RESTORE_FACTORY_DEFAULTS_FORMATTER);
break;
- }
case VIDIOCPWCSCQUAL:
{
ARG_DEF(int, qual)
- if (pdev->iso_init) {
+ if (vb2_is_streaming(&pdev->vb_queue)) {
ret = -EBUSY;
break;
}
@@ -1396,8 +868,6 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
ret = -EINVAL;
else
ret = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot);
- if (ret >= 0)
- pdev->vcompression = ARGR(qual);
break;
}
@@ -1432,71 +902,59 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCSAGC:
{
ARG_DEF(int, agc)
-
ARG_IN(agc)
- if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc)))
- ret = -EINVAL;
+ ret = v4l2_ctrl_s_ctrl(pdev->autogain, ARGR(agc) < 0);
+ if (ret == 0 && ARGR(agc) >= 0)
+ ret = pwc_ioctl_s_ctrl(pdev->gain, ARGR(agc));
break;
}
case VIDIOCPWCGAGC:
{
ARG_DEF(int, agc)
-
- if (pwc_get_agc(pdev, ARGA(agc)))
- ret = -EINVAL;
+ if (v4l2_ctrl_g_ctrl(pdev->autogain))
+ ARGR(agc) = -1;
+ else
+ ARGR(agc) = pwc_ioctl_g_ctrl(pdev->gain);
ARG_OUT(agc)
break;
}
case VIDIOCPWCSSHUTTER:
{
- ARG_DEF(int, shutter_speed)
-
- ARG_IN(shutter_speed)
- ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed));
+ ARG_DEF(int, shutter)
+ ARG_IN(shutter)
+ ret = v4l2_ctrl_s_ctrl(pdev->exposure_auto,
+ /* Menu idx 0 = auto, idx 1 = manual */
+ ARGR(shutter) >= 0);
+ if (ret == 0 && ARGR(shutter) >= 0)
+ ret = pwc_ioctl_s_ctrl(pdev->exposure, ARGR(shutter));
break;
}
case VIDIOCPWCSAWB:
{
ARG_DEF(struct pwc_whitebalance, wb)
-
ARG_IN(wb)
- ret = pwc_set_awb(pdev, ARGR(wb).mode);
- if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) {
- pwc_set_red_gain(pdev, ARGR(wb).manual_red);
- pwc_set_blue_gain(pdev, ARGR(wb).manual_blue);
- }
+ ret = v4l2_ctrl_s_ctrl(pdev->auto_white_balance,
+ ARGR(wb).mode);
+ if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL)
+ ret = pwc_ioctl_s_ctrl(pdev->red_balance,
+ ARGR(wb).manual_red);
+ if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL)
+ ret = pwc_ioctl_s_ctrl(pdev->blue_balance,
+ ARGR(wb).manual_blue);
break;
}
case VIDIOCPWCGAWB:
{
ARG_DEF(struct pwc_whitebalance, wb)
-
- memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance));
- ARGR(wb).mode = pwc_get_awb(pdev);
- if (ARGR(wb).mode < 0)
- ret = -EINVAL;
- else {
- if (ARGR(wb).mode == PWC_WB_MANUAL) {
- ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red);
- if (ret < 0)
- break;
- ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue);
- if (ret < 0)
- break;
- }
- if (ARGR(wb).mode == PWC_WB_AUTO) {
- ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red);
- if (ret < 0)
- break;
- ret = pwc_read_blue_gain(pdev, &ARGR(wb).read_blue);
- if (ret < 0)
- break;
- }
- }
+ ARGR(wb).mode = v4l2_ctrl_g_ctrl(pdev->auto_white_balance);
+ ARGR(wb).manual_red = ARGR(wb).read_red =
+ pwc_ioctl_g_ctrl(pdev->red_balance);
+ ARGR(wb).manual_blue = ARGR(wb).read_blue =
+ pwc_ioctl_g_ctrl(pdev->blue_balance);
ARG_OUT(wb)
break;
}
@@ -1550,17 +1008,20 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCSCONTOUR:
{
ARG_DEF(int, contour)
-
ARG_IN(contour)
- ret = pwc_set_contour(pdev, ARGR(contour));
+ ret = v4l2_ctrl_s_ctrl(pdev->autocontour, ARGR(contour) < 0);
+ if (ret == 0 && ARGR(contour) >= 0)
+ ret = pwc_ioctl_s_ctrl(pdev->contour, ARGR(contour));
break;
}
case VIDIOCPWCGCONTOUR:
{
ARG_DEF(int, contour)
-
- ret = pwc_get_contour(pdev, ARGA(contour));
+ if (v4l2_ctrl_g_ctrl(pdev->autocontour))
+ ARGR(contour) = -1;
+ else
+ ARGR(contour) = pwc_ioctl_g_ctrl(pdev->contour);
ARG_OUT(contour)
break;
}
@@ -1568,17 +1029,15 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCSBACKLIGHT:
{
ARG_DEF(int, backlight)
-
ARG_IN(backlight)
- ret = pwc_set_backlight(pdev, ARGR(backlight));
+ ret = v4l2_ctrl_s_ctrl(pdev->backlight, ARGR(backlight));
break;
}
case VIDIOCPWCGBACKLIGHT:
{
ARG_DEF(int, backlight)
-
- ret = pwc_get_backlight(pdev, ARGA(backlight));
+ ARGR(backlight) = v4l2_ctrl_g_ctrl(pdev->backlight);
ARG_OUT(backlight)
break;
}
@@ -1586,17 +1045,15 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCSFLICKER:
{
ARG_DEF(int, flicker)
-
ARG_IN(flicker)
- ret = pwc_set_flicker(pdev, ARGR(flicker));
+ ret = v4l2_ctrl_s_ctrl(pdev->flicker, ARGR(flicker));
break;
}
case VIDIOCPWCGFLICKER:
{
ARG_DEF(int, flicker)
-
- ret = pwc_get_flicker(pdev, ARGA(flicker));
+ ARGR(flicker) = v4l2_ctrl_g_ctrl(pdev->flicker);
ARG_OUT(flicker)
break;
}
@@ -1604,17 +1061,15 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCSDYNNOISE:
{
ARG_DEF(int, dynnoise)
-
ARG_IN(dynnoise)
- ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise));
+ ret = v4l2_ctrl_s_ctrl(pdev->noise_reduction, ARGR(dynnoise));
break;
}
case VIDIOCPWCGDYNNOISE:
{
ARG_DEF(int, dynnoise)
-
- ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise));
+ ARGR(dynnoise) = v4l2_ctrl_g_ctrl(pdev->noise_reduction);
ARG_OUT(dynnoise);
break;
}
diff --git a/drivers/media/video/pwc/pwc-dec1.c b/drivers/media/video/pwc/pwc-dec1.c
index c29593f589eb..be0e02cb487f 100644
--- a/drivers/media/video/pwc/pwc-dec1.c
+++ b/drivers/media/video/pwc/pwc-dec1.c
@@ -22,29 +22,19 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-
-
-
#include "pwc-dec1.h"
-
-void pwc_dec1_init(int type, int release, void *buffer, void *table)
+int pwc_dec1_init(struct pwc_device *pwc, int type, int release, void *buffer)
{
+ struct pwc_dec1_private *pdec;
-}
-
-void pwc_dec1_exit(void)
-{
+ if (pwc->decompress_data == NULL) {
+ pdec = kmalloc(sizeof(struct pwc_dec1_private), GFP_KERNEL);
+ if (pdec == NULL)
+ return -ENOMEM;
+ pwc->decompress_data = pdec;
+ }
+ pdec = pwc->decompress_data;
-
-
-}
-
-int pwc_dec1_alloc(struct pwc_device *pwc)
-{
- pwc->decompress_data = kmalloc(sizeof(struct pwc_dec1_private), GFP_KERNEL);
- if (pwc->decompress_data == NULL)
- return -ENOMEM;
return 0;
}
-
diff --git a/drivers/media/video/pwc/pwc-dec1.h b/drivers/media/video/pwc/pwc-dec1.h
index 8b62ddcc5c7e..a57d8601080b 100644
--- a/drivers/media/video/pwc/pwc-dec1.h
+++ b/drivers/media/video/pwc/pwc-dec1.h
@@ -22,8 +22,6 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-
-
#ifndef PWC_DEC1_H
#define PWC_DEC1_H
@@ -32,12 +30,8 @@
struct pwc_dec1_private
{
int version;
-
};
-int pwc_dec1_alloc(struct pwc_device *pwc);
-void pwc_dec1_init(int type, int release, void *buffer, void *private_data);
-void pwc_dec1_exit(void);
+int pwc_dec1_init(struct pwc_device *pwc, int type, int release, void *buffer);
#endif
-
diff --git a/drivers/media/video/pwc/pwc-dec23.c b/drivers/media/video/pwc/pwc-dec23.c
index 0c801b8f3eca..06a4e877ba40 100644
--- a/drivers/media/video/pwc/pwc-dec23.c
+++ b/drivers/media/video/pwc/pwc-dec23.c
@@ -916,27 +916,5 @@ void pwc_dec23_decompress(const struct pwc_device *pwc,
pout_planar_v += pwc->view.x;
}
-
}
-
}
-
-void pwc_dec23_exit(void)
-{
- /* Do nothing */
-
-}
-
-/**
- * Allocate a private structure used by lookup table.
- * You must call kfree() to free the memory allocated.
- */
-int pwc_dec23_alloc(struct pwc_device *pwc)
-{
- pwc->decompress_data = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL);
- if (pwc->decompress_data == NULL)
- return -ENOMEM;
- return 0;
-}
-
-/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */
diff --git a/drivers/media/video/pwc/pwc-dec23.h b/drivers/media/video/pwc/pwc-dec23.h
index 1c55298ad153..a0ac4f3dff81 100644
--- a/drivers/media/video/pwc/pwc-dec23.h
+++ b/drivers/media/video/pwc/pwc-dec23.h
@@ -49,19 +49,9 @@ struct pwc_dec23_private
};
-
-int pwc_dec23_alloc(struct pwc_device *pwc);
int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd);
-void pwc_dec23_exit(void);
void pwc_dec23_decompress(const struct pwc_device *pwc,
const void *src,
void *dst,
int flags);
-
-
-
#endif
-
-
-/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */
-
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index b0bde5a87c8a..01ff643e682d 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -2,6 +2,7 @@
USB and Video4Linux interface part.
(C) 1999-2004 Nemosoft Unv.
(C) 2004-2006 Luc Saillard (luc@saillard.org)
+ (C) 2011 Hans de Goede <hdegoede@redhat.com>
NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
driver and thus may have bugs that are not present in the original version.
@@ -74,7 +75,6 @@
#include "pwc-timon.h"
#include "pwc-dec23.h"
#include "pwc-dec1.h"
-#include "pwc-uncompress.h"
/* Function prototypes and driver templates */
@@ -116,6 +116,7 @@ MODULE_DEVICE_TABLE(usb, pwc_device_table);
static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
static void usb_pwc_disconnect(struct usb_interface *intf);
+static void pwc_isoc_cleanup(struct pwc_device *pdev);
static struct usb_driver pwc_driver = {
.name = "Philips webcam", /* name */
@@ -127,14 +128,11 @@ static struct usb_driver pwc_driver = {
#define MAX_DEV_HINTS 20
#define MAX_ISOC_ERRORS 20
-static int default_size = PSZ_QCIF;
static int default_fps = 10;
-static int default_fbufs = 3; /* Default number of frame buffers */
- int pwc_mbufs = 2; /* Default number of mmap() buffers */
#ifdef CONFIG_USB_PWC_DEBUG
int pwc_trace = PWC_DEBUG_LEVEL;
#endif
-static int power_save;
+static int power_save = -1;
static int led_on = 100, led_off; /* defaults to LED that is on while in use */
static int pwc_preferred_compression = 1; /* 0..3 = uncompressed..high */
static struct {
@@ -173,389 +171,20 @@ static struct video_device pwc_template = {
/***************************************************************************/
/* Private functions */
-/* Here we want the physical address of the memory.
- * This is used when initializing the contents of the area.
- */
-
-
-
-static void *pwc_rvmalloc(unsigned long size)
-{
- void * mem;
- unsigned long adr;
-
- mem=vmalloc_32(size);
- if (!mem)
- return NULL;
-
- memset(mem, 0, size); /* Clear the ram out, no junk to the user */
- adr=(unsigned long) mem;
- while (size > 0)
- {
- SetPageReserved(vmalloc_to_page((void *)adr));
- adr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- return mem;
-}
-
-static void pwc_rvfree(void * mem, unsigned long size)
-{
- unsigned long adr;
-
- if (!mem)
- return;
-
- adr=(unsigned long) mem;
- while ((long) size > 0)
- {
- ClearPageReserved(vmalloc_to_page((void *)adr));
- adr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- vfree(mem);
-}
-
-
-
-
-static int pwc_allocate_buffers(struct pwc_device *pdev)
-{
- int i, err;
- void *kbuf;
-
- PWC_DEBUG_MEMORY(">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
-
- if (pdev == NULL)
- return -ENXIO;
-
- /* Allocate Isochronuous pipe buffers */
- for (i = 0; i < MAX_ISO_BUFS; i++) {
- if (pdev->sbuf[i].data == NULL) {
- kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
- if (kbuf == NULL) {
- PWC_ERROR("Failed to allocate iso buffer %d.\n", i);
- return -ENOMEM;
- }
- PWC_DEBUG_MEMORY("Allocated iso buffer at %p.\n", kbuf);
- pdev->sbuf[i].data = kbuf;
- }
- }
-
- /* Allocate frame buffer structure */
- if (pdev->fbuf == NULL) {
- kbuf = kzalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL);
- if (kbuf == NULL) {
- PWC_ERROR("Failed to allocate frame buffer structure.\n");
- return -ENOMEM;
- }
- PWC_DEBUG_MEMORY("Allocated frame buffer structure at %p.\n", kbuf);
- pdev->fbuf = kbuf;
- }
-
- /* create frame buffers, and make circular ring */
- for (i = 0; i < default_fbufs; i++) {
- if (pdev->fbuf[i].data == NULL) {
- kbuf = vzalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */
- if (kbuf == NULL) {
- PWC_ERROR("Failed to allocate frame buffer %d.\n", i);
- return -ENOMEM;
- }
- PWC_DEBUG_MEMORY("Allocated frame buffer %d at %p.\n", i, kbuf);
- pdev->fbuf[i].data = kbuf;
- }
- }
-
- /* Allocate decompressor table space */
- if (DEVICE_USE_CODEC1(pdev->type))
- err = pwc_dec1_alloc(pdev);
- else
- err = pwc_dec23_alloc(pdev);
-
- if (err) {
- PWC_ERROR("Failed to allocate decompress table.\n");
- return err;
- }
-
- /* Allocate image buffer; double buffer for mmap() */
- kbuf = pwc_rvmalloc(pwc_mbufs * pdev->len_per_image);
- if (kbuf == NULL) {
- PWC_ERROR("Failed to allocate image buffer(s). needed (%d)\n",
- pwc_mbufs * pdev->len_per_image);
- return -ENOMEM;
- }
- PWC_DEBUG_MEMORY("Allocated image buffer at %p.\n", kbuf);
- pdev->image_data = kbuf;
- for (i = 0; i < pwc_mbufs; i++) {
- pdev->images[i].offset = i * pdev->len_per_image;
- pdev->images[i].vma_use_count = 0;
- }
- for (; i < MAX_IMAGES; i++) {
- pdev->images[i].offset = 0;
- }
-
- kbuf = NULL;
-
- PWC_DEBUG_MEMORY("<< pwc_allocate_buffers()\n");
- return 0;
-}
-
-static void pwc_free_buffers(struct pwc_device *pdev)
-{
- int i;
-
- PWC_DEBUG_MEMORY("Entering free_buffers(%p).\n", pdev);
-
- if (pdev == NULL)
- return;
- /* Release Iso-pipe buffers */
- for (i = 0; i < MAX_ISO_BUFS; i++)
- if (pdev->sbuf[i].data != NULL) {
- PWC_DEBUG_MEMORY("Freeing ISO buffer at %p.\n", pdev->sbuf[i].data);
- kfree(pdev->sbuf[i].data);
- pdev->sbuf[i].data = NULL;
- }
-
- /* The same for frame buffers */
- if (pdev->fbuf != NULL) {
- for (i = 0; i < default_fbufs; i++) {
- if (pdev->fbuf[i].data != NULL) {
- PWC_DEBUG_MEMORY("Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data);
- vfree(pdev->fbuf[i].data);
- pdev->fbuf[i].data = NULL;
- }
- }
- kfree(pdev->fbuf);
- pdev->fbuf = NULL;
- }
-
- /* Intermediate decompression buffer & tables */
- if (pdev->decompress_data != NULL) {
- PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", pdev->decompress_data);
- kfree(pdev->decompress_data);
- pdev->decompress_data = NULL;
- }
-
- /* Release image buffers */
- if (pdev->image_data != NULL) {
- PWC_DEBUG_MEMORY("Freeing image buffer at %p.\n", pdev->image_data);
- pwc_rvfree(pdev->image_data, pwc_mbufs * pdev->len_per_image);
- }
- pdev->image_data = NULL;
-
- PWC_DEBUG_MEMORY("Leaving free_buffers().\n");
-}
-
-/* The frame & image buffer mess.
-
- Yes, this is a mess. Well, it used to be simple, but alas... In this
- module, 3 buffers schemes are used to get the data from the USB bus to
- the user program. The first scheme involves the ISO buffers (called thus
- since they transport ISO data from the USB controller), and not really
- interesting. Suffices to say the data from this buffer is quickly
- gathered in an interrupt handler (pwc_isoc_handler) and placed into the
- frame buffer.
-
- The frame buffer is the second scheme, and is the central element here.
- It collects the data from a single frame from the camera (hence, the
- name). Frames are delimited by the USB camera with a short USB packet,
- so that's easy to detect. The frame buffers form a list that is filled
- by the camera+USB controller and drained by the user process through
- either read() or mmap().
-
- The image buffer is the third scheme, in which frames are decompressed
- and converted into planar format. For mmap() there is more than
- one image buffer available.
-
- The frame buffers provide the image buffering. In case the user process
- is a bit slow, this introduces lag and some undesired side-effects.
- The problem arises when the frame buffer is full. I used to drop the last
- frame, which makes the data in the queue stale very quickly. But dropping
- the frame at the head of the queue proved to be a litte bit more difficult.
- I tried a circular linked scheme, but this introduced more problems than
- it solved.
-
- Because filling and draining are completely asynchronous processes, this
- requires some fiddling with pointers and mutexes.
-
- Eventually, I came up with a system with 2 lists: an 'empty' frame list
- and a 'full' frame list:
- * Initially, all frame buffers but one are on the 'empty' list; the one
- remaining buffer is our initial fill frame.
- * If a frame is needed for filling, we try to take it from the 'empty'
- list, unless that list is empty, in which case we take the buffer at
- the head of the 'full' list.
- * When our fill buffer has been filled, it is appended to the 'full'
- list.
- * If a frame is needed by read() or mmap(), it is taken from the head of
- the 'full' list, handled, and then appended to the 'empty' list. If no
- buffer is present on the 'full' list, we wait.
- The advantage is that the buffer that is currently being decompressed/
- converted, is on neither list, and thus not in our way (any other scheme
- I tried had the problem of old data lingering in the queue).
-
- Whatever strategy you choose, it always remains a tradeoff: with more
- frame buffers the chances of a missed frame are reduced. On the other
- hand, on slower machines it introduces lag because the queue will
- always be full.
- */
-
-/**
- \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first.
- */
-static int pwc_next_fill_frame(struct pwc_device *pdev)
-{
- int ret;
- unsigned long flags;
-
- ret = 0;
- spin_lock_irqsave(&pdev->ptrlock, flags);
- if (pdev->fill_frame != NULL) {
- /* append to 'full' list */
- if (pdev->full_frames == NULL) {
- pdev->full_frames = pdev->fill_frame;
- pdev->full_frames_tail = pdev->full_frames;
- }
- else {
- pdev->full_frames_tail->next = pdev->fill_frame;
- pdev->full_frames_tail = pdev->fill_frame;
- }
- }
- if (pdev->empty_frames != NULL) {
- /* We have empty frames available. That's easy */
- pdev->fill_frame = pdev->empty_frames;
- pdev->empty_frames = pdev->empty_frames->next;
- }
- else {
- /* Hmm. Take it from the full list */
- /* sanity check */
- if (pdev->full_frames == NULL) {
- PWC_ERROR("Neither empty or full frames available!\n");
- spin_unlock_irqrestore(&pdev->ptrlock, flags);
- return -EINVAL;
- }
- pdev->fill_frame = pdev->full_frames;
- pdev->full_frames = pdev->full_frames->next;
- ret = 1;
- }
- pdev->fill_frame->next = NULL;
- spin_unlock_irqrestore(&pdev->ptrlock, flags);
- return ret;
-}
-
-
-/**
- \brief Reset all buffers, pointers and lists, except for the image_used[] buffer.
-
- If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble.
- */
-static void pwc_reset_buffers(struct pwc_device *pdev)
-{
- int i;
- unsigned long flags;
-
- PWC_DEBUG_MEMORY(">> %s __enter__\n", __func__);
-
- spin_lock_irqsave(&pdev->ptrlock, flags);
- pdev->full_frames = NULL;
- pdev->full_frames_tail = NULL;
- for (i = 0; i < default_fbufs; i++) {
- pdev->fbuf[i].filled = 0;
- if (i > 0)
- pdev->fbuf[i].next = &pdev->fbuf[i - 1];
- else
- pdev->fbuf->next = NULL;
- }
- pdev->empty_frames = &pdev->fbuf[default_fbufs - 1];
- pdev->empty_frames_tail = pdev->fbuf;
- pdev->read_frame = NULL;
- pdev->fill_frame = pdev->empty_frames;
- pdev->empty_frames = pdev->empty_frames->next;
-
- pdev->image_read_pos = 0;
- pdev->fill_image = 0;
- spin_unlock_irqrestore(&pdev->ptrlock, flags);
-
- PWC_DEBUG_MEMORY("<< %s __leaving__\n", __func__);
-}
-
-
-/**
- \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers.
- */
-int pwc_handle_frame(struct pwc_device *pdev)
+struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
{
- int ret = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&pdev->ptrlock, flags);
- /* First grab our read_frame; this is removed from all lists, so
- we can release the lock after this without problems */
- if (pdev->read_frame != NULL) {
- /* This can't theoretically happen */
- PWC_ERROR("Huh? Read frame still in use?\n");
- spin_unlock_irqrestore(&pdev->ptrlock, flags);
- return ret;
- }
-
-
- if (pdev->full_frames == NULL) {
- PWC_ERROR("Woops. No frames ready.\n");
- }
- else {
- pdev->read_frame = pdev->full_frames;
- pdev->full_frames = pdev->full_frames->next;
- pdev->read_frame->next = NULL;
- }
-
- if (pdev->read_frame != NULL) {
- /* Decompression is a lengthy process, so it's outside of the lock.
- This gives the isoc_handler the opportunity to fill more frames
- in the mean time.
- */
- spin_unlock_irqrestore(&pdev->ptrlock, flags);
- ret = pwc_decompress(pdev);
- spin_lock_irqsave(&pdev->ptrlock, flags);
-
- /* We're done with read_buffer, tack it to the end of the empty buffer list */
- if (pdev->empty_frames == NULL) {
- pdev->empty_frames = pdev->read_frame;
- pdev->empty_frames_tail = pdev->empty_frames;
- }
- else {
- pdev->empty_frames_tail->next = pdev->read_frame;
- pdev->empty_frames_tail = pdev->read_frame;
- }
- pdev->read_frame = NULL;
- }
- spin_unlock_irqrestore(&pdev->ptrlock, flags);
- return ret;
-}
-
-/**
- \brief Advance pointers of image buffer (after each user request)
-*/
-void pwc_next_image(struct pwc_device *pdev)
-{
- pdev->image_used[pdev->fill_image] = 0;
- pdev->fill_image = (pdev->fill_image + 1) % pwc_mbufs;
-}
-
-/**
- * Print debug information when a frame is discarded because all of our buffer
- * is full
- */
-static void pwc_frame_dumped(struct pwc_device *pdev)
-{
- pdev->vframes_dumped++;
- if (pdev->vframe_count < FRAME_LOWMARK)
- return;
-
- if (pdev->vframes_dumped < 20)
- PWC_DEBUG_FLOW("Dumping frame %d\n", pdev->vframe_count);
- else if (pdev->vframes_dumped == 20)
- PWC_DEBUG_FLOW("Dumping frame %d (last message)\n",
- pdev->vframe_count);
+ unsigned long flags = 0;
+ struct pwc_frame_buf *buf = NULL;
+
+ spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+ if (list_empty(&pdev->queued_bufs))
+ goto leave;
+
+ buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list);
+ list_del(&buf->list);
+leave:
+ spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
+ return buf;
}
static void pwc_snapshot_button(struct pwc_device *pdev, int down)
@@ -575,9 +204,9 @@ static void pwc_snapshot_button(struct pwc_device *pdev, int down)
#endif
}
-static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_buf *fbuf)
+static void pwc_frame_complete(struct pwc_device *pdev)
{
- int awake = 0;
+ struct pwc_frame_buf *fbuf = pdev->fill_buf;
/* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
frames on the USB wire after an exposure change. This conditition is
@@ -589,7 +218,6 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_
if (ptr[1] == 1 && ptr[0] & 0x10) {
PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
pdev->drop_frames += 2;
- pdev->vframes_error++;
}
if ((ptr[0] ^ pdev->vmirror) & 0x01) {
pwc_snapshot_button(pdev, ptr[0] & 0x01);
@@ -612,8 +240,7 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_
*/
if (fbuf->filled == 4)
pdev->drop_frames++;
- }
- else if (pdev->type == 740 || pdev->type == 720) {
+ } else if (pdev->type == 740 || pdev->type == 720) {
unsigned char *ptr = (unsigned char *)fbuf->data;
if ((ptr[0] ^ pdev->vmirror) & 0x01) {
pwc_snapshot_button(pdev, ptr[0] & 0x01);
@@ -621,33 +248,23 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_
pdev->vmirror = ptr[0] & 0x03;
}
- /* In case we were instructed to drop the frame, do so silently.
- The buffer pointers are not updated either (but the counters are reset below).
- */
- if (pdev->drop_frames > 0)
+ /* In case we were instructed to drop the frame, do so silently. */
+ if (pdev->drop_frames > 0) {
pdev->drop_frames--;
- else {
+ } else {
/* Check for underflow first */
if (fbuf->filled < pdev->frame_total_size) {
PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);"
" discarded.\n", fbuf->filled);
- pdev->vframes_error++;
- }
- else {
- /* Send only once per EOF */
- awake = 1; /* delay wake_ups */
-
- /* Find our next frame to fill. This will always succeed, since we
- * nick a frame from either empty or full list, but if we had to
- * take it from the full list, it means a frame got dropped.
- */
- if (pwc_next_fill_frame(pdev))
- pwc_frame_dumped(pdev);
-
+ } else {
+ fbuf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
+ fbuf->vb.v4l2_buf.sequence = pdev->vframe_count;
+ vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
+ pdev->fill_buf = NULL;
+ pdev->vsync = 0;
}
} /* !drop_frames */
pdev->vframe_count++;
- return awake;
}
/* This gets called for the Isochronous pipe (video). This is done in
@@ -655,24 +272,20 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_
*/
static void pwc_isoc_handler(struct urb *urb)
{
- struct pwc_device *pdev;
+ struct pwc_device *pdev = (struct pwc_device *)urb->context;
int i, fst, flen;
- int awake;
- struct pwc_frame_buf *fbuf;
- unsigned char *fillptr = NULL, *iso_buf = NULL;
+ unsigned char *iso_buf = NULL;
- awake = 0;
- pdev = (struct pwc_device *)urb->context;
- if (pdev == NULL) {
- PWC_ERROR("isoc_handler() called with NULL device?!\n");
- return;
- }
-
- if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
+ if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN) {
PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
return;
}
- if (urb->status != -EINPROGRESS && urb->status != 0) {
+
+ if (pdev->fill_buf == NULL)
+ pdev->fill_buf = pwc_get_next_fill_buf(pdev);
+
+ if (urb->status != 0) {
const char *errmsg;
errmsg = "Unknown";
@@ -684,29 +297,21 @@ static void pwc_isoc_handler(struct urb *urb)
case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
case -ETIME: errmsg = "Device does not respond"; break;
}
- PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
- /* Give up after a number of contiguous errors on the USB bus.
- Appearantly something is wrong so we simulate an unplug event.
- */
+ PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n",
+ urb->status, errmsg);
+ /* Give up after a number of contiguous errors */
if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
{
- PWC_INFO("Too many ISOC errors, bailing out.\n");
- pdev->error_status = EIO;
- awake = 1;
- wake_up_interruptible(&pdev->frameq);
+ PWC_ERROR("Too many ISOC errors, bailing out.\n");
+ if (pdev->fill_buf) {
+ vb2_buffer_done(&pdev->fill_buf->vb,
+ VB2_BUF_STATE_ERROR);
+ pdev->fill_buf = NULL;
+ }
}
- goto handler_end; // ugly, but practical
- }
-
- fbuf = pdev->fill_frame;
- if (fbuf == NULL) {
- PWC_ERROR("pwc_isoc_handler without valid fill frame.\n");
- awake = 1;
+ pdev->vsync = 0; /* Drop the current frame */
goto handler_end;
}
- else {
- fillptr = fbuf->data + fbuf->filled;
- }
/* Reset ISOC error counter. We did get here, after all. */
pdev->visoc_errors = 0;
@@ -720,89 +325,73 @@ static void pwc_isoc_handler(struct urb *urb)
fst = urb->iso_frame_desc[i].status;
flen = urb->iso_frame_desc[i].actual_length;
iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
- if (fst == 0) {
- if (flen > 0) { /* if valid data... */
- if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */
- pdev->vsync = 2;
-
- /* ...copy data to frame buffer, if possible */
- if (flen + fbuf->filled > pdev->frame_total_size) {
- PWC_DEBUG_FLOW("Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size);
- pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */
- pdev->vframes_error++;
- }
- else {
- memmove(fillptr, iso_buf, flen);
- fillptr += flen;
- }
- }
+ if (fst != 0) {
+ PWC_ERROR("Iso frame %d has error %d\n", i, fst);
+ continue;
+ }
+ if (flen > 0 && pdev->vsync) {
+ struct pwc_frame_buf *fbuf = pdev->fill_buf;
+
+ if (pdev->vsync == 1) {
+ do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp);
+ pdev->vsync = 2;
+ }
+
+ if (flen + fbuf->filled > pdev->frame_total_size) {
+ PWC_ERROR("Frame overflow (%d > %d)\n",
+ flen + fbuf->filled,
+ pdev->frame_total_size);
+ pdev->vsync = 0; /* Let's wait for an EOF */
+ } else {
+ memcpy(fbuf->data + fbuf->filled, iso_buf,
+ flen);
fbuf->filled += flen;
- } /* ..flen > 0 */
-
- if (flen < pdev->vlast_packet_size) {
- /* Shorter packet... We probably have the end of an image-frame;
- wake up read() process and let select()/poll() do something.
- Decompression is done in user time over there.
- */
- if (pdev->vsync == 2) {
- if (pwc_rcv_short_packet(pdev, fbuf)) {
- awake = 1;
- fbuf = pdev->fill_frame;
- }
- }
- fbuf->filled = 0;
- fillptr = fbuf->data;
+ }
+ }
+ if (flen < pdev->vlast_packet_size) {
+ /* Shorter packet... end of frame */
+ if (pdev->vsync == 2)
+ pwc_frame_complete(pdev);
+ if (pdev->fill_buf == NULL)
+ pdev->fill_buf = pwc_get_next_fill_buf(pdev);
+ if (pdev->fill_buf) {
+ pdev->fill_buf->filled = 0;
pdev->vsync = 1;
}
-
- pdev->vlast_packet_size = flen;
- } /* ..status == 0 */
- else {
- /* This is normally not interesting to the user, unless
- * you are really debugging something, default = 0 */
- static int iso_error;
- iso_error++;
- if (iso_error < 20)
- PWC_DEBUG_FLOW("Iso frame %d of USB has error %d\n", i, fst);
}
+ pdev->vlast_packet_size = flen;
}
handler_end:
- if (awake)
- wake_up_interruptible(&pdev->frameq);
-
- urb->dev = pdev->udev;
i = usb_submit_urb(urb, GFP_ATOMIC);
if (i != 0)
PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
}
-
-int pwc_isoc_init(struct pwc_device *pdev)
+static int pwc_isoc_init(struct pwc_device *pdev)
{
struct usb_device *udev;
struct urb *urb;
int i, j, ret;
-
struct usb_interface *intf;
struct usb_host_interface *idesc = NULL;
- if (pdev == NULL)
- return -EFAULT;
if (pdev->iso_init)
return 0;
+
pdev->vsync = 0;
+ pdev->vlast_packet_size = 0;
+ pdev->fill_buf = NULL;
+ pdev->vframe_count = 0;
+ pdev->visoc_errors = 0;
udev = pdev->udev;
/* Get the current alternate interface, adjust packet size */
- if (!udev->actconfig)
- return -EFAULT;
intf = usb_ifnum_to_if(udev, 0);
if (intf)
idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
-
if (!idesc)
- return -EFAULT;
+ return -EIO;
/* Search video endpoint */
pdev->vmax_packet_size = -1;
@@ -825,34 +414,32 @@ int pwc_isoc_init(struct pwc_device *pdev)
if (ret < 0)
return ret;
+ /* Allocate and init Isochronuous urbs */
for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
if (urb == NULL) {
PWC_ERROR("Failed to allocate urb %d\n", i);
- ret = -ENOMEM;
- break;
+ pdev->iso_init = 1;
+ pwc_isoc_cleanup(pdev);
+ return -ENOMEM;
}
- pdev->sbuf[i].urb = urb;
+ pdev->urbs[i] = urb;
PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
- }
- if (ret) {
- /* De-allocate in reverse order */
- while (i--) {
- usb_free_urb(pdev->sbuf[i].urb);
- pdev->sbuf[i].urb = NULL;
- }
- return ret;
- }
-
- /* init URB structure */
- for (i = 0; i < MAX_ISO_BUFS; i++) {
- urb = pdev->sbuf[i].urb;
urb->interval = 1; // devik
urb->dev = udev;
urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
- urb->transfer_flags = URB_ISO_ASAP;
- urb->transfer_buffer = pdev->sbuf[i].data;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_buffer = usb_alloc_coherent(udev,
+ ISO_BUFFER_SIZE,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+ if (urb->transfer_buffer == NULL) {
+ PWC_ERROR("Failed to allocate urb buffer %d\n", i);
+ pdev->iso_init = 1;
+ pwc_isoc_cleanup(pdev);
+ return -ENOMEM;
+ }
urb->transfer_buffer_length = ISO_BUFFER_SIZE;
urb->complete = pwc_isoc_handler;
urb->context = pdev;
@@ -866,14 +453,14 @@ int pwc_isoc_init(struct pwc_device *pdev)
/* link */
for (i = 0; i < MAX_ISO_BUFS; i++) {
- ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL);
+ ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL);
if (ret) {
PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
pdev->iso_init = 1;
pwc_isoc_cleanup(pdev);
return ret;
}
- PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->sbuf[i].urb);
+ PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]);
}
/* All is done... */
@@ -888,12 +475,9 @@ static void pwc_iso_stop(struct pwc_device *pdev)
/* Unlinking ISOC buffers one by one */
for (i = 0; i < MAX_ISO_BUFS; i++) {
- struct urb *urb;
-
- urb = pdev->sbuf[i].urb;
- if (urb) {
- PWC_DEBUG_MEMORY("Unlinking URB %p\n", urb);
- usb_kill_urb(urb);
+ if (pdev->urbs[i]) {
+ PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]);
+ usb_kill_urb(pdev->urbs[i]);
}
}
}
@@ -904,40 +488,51 @@ static void pwc_iso_free(struct pwc_device *pdev)
/* Freeing ISOC buffers one by one */
for (i = 0; i < MAX_ISO_BUFS; i++) {
- struct urb *urb;
-
- urb = pdev->sbuf[i].urb;
- if (urb) {
+ if (pdev->urbs[i]) {
PWC_DEBUG_MEMORY("Freeing URB\n");
- usb_free_urb(urb);
- pdev->sbuf[i].urb = NULL;
+ if (pdev->urbs[i]->transfer_buffer) {
+ usb_free_coherent(pdev->udev,
+ pdev->urbs[i]->transfer_buffer_length,
+ pdev->urbs[i]->transfer_buffer,
+ pdev->urbs[i]->transfer_dma);
+ }
+ usb_free_urb(pdev->urbs[i]);
+ pdev->urbs[i] = NULL;
}
}
}
-void pwc_isoc_cleanup(struct pwc_device *pdev)
+static void pwc_isoc_cleanup(struct pwc_device *pdev)
{
PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
- if (pdev == NULL)
- return;
+
if (pdev->iso_init == 0)
return;
pwc_iso_stop(pdev);
pwc_iso_free(pdev);
-
- /* Stop camera, but only if we are sure the camera is still there (unplug
- is signalled by EPIPE)
- */
- if (pdev->error_status != EPIPE) {
- PWC_DEBUG_OPEN("Setting alternate interface 0.\n");
- usb_set_interface(pdev->udev, 0, 0);
- }
+ usb_set_interface(pdev->udev, 0, 0);
pdev->iso_init = 0;
PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
}
+/*
+ * Release all queued buffers, no need to take queued_bufs_lock, since all
+ * iso urbs have been killed when we're called so pwc_isoc_handler won't run.
+ */
+static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
+{
+ while (!list_empty(&pdev->queued_bufs)) {
+ struct pwc_frame_buf *buf;
+
+ buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf,
+ list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+}
+
/*********
* sysfs
*********/
@@ -1051,98 +646,15 @@ static const char *pwc_sensor_type_to_string(unsigned int sensor_type)
static int pwc_video_open(struct file *file)
{
- int i, ret;
struct video_device *vdev = video_devdata(file);
struct pwc_device *pdev;
PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev);
pdev = video_get_drvdata(vdev);
- BUG_ON(!pdev);
- if (pdev->vopen) {
- PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n");
- return -EBUSY;
- }
-
- pwc_construct(pdev); /* set min/max sizes correct */
- if (!pdev->usb_init) {
- PWC_DEBUG_OPEN("Doing first time initialization.\n");
- pdev->usb_init = 1;
-
- /* Query sensor type */
- ret = pwc_get_cmos_sensor(pdev, &i);
- if (ret >= 0)
- {
- PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
- pdev->vdev.name,
- pwc_sensor_type_to_string(i), i);
- }
- }
-
- /* Turn on camera */
- if (power_save) {
- i = pwc_camera_power(pdev, 1);
- if (i < 0)
- PWC_DEBUG_OPEN("Failed to restore power to the camera! (%d)\n", i);
- }
- /* Set LED on/off time */
- if (pwc_set_leds(pdev, led_on, led_off) < 0)
- PWC_DEBUG_OPEN("Failed to set LED on/off time.\n");
-
-
- /* So far, so good. Allocate memory. */
- i = pwc_allocate_buffers(pdev);
- if (i < 0) {
- PWC_DEBUG_OPEN("Failed to allocate buffers memory.\n");
- pwc_free_buffers(pdev);
- return i;
- }
-
- /* Reset buffers & parameters */
- pwc_reset_buffers(pdev);
- for (i = 0; i < pwc_mbufs; i++)
- pdev->image_used[i] = 0;
- pdev->vframe_count = 0;
- pdev->vframes_dumped = 0;
- pdev->vframes_error = 0;
- pdev->visoc_errors = 0;
- pdev->error_status = 0;
- pwc_construct(pdev); /* set min/max sizes correct */
-
- /* Set some defaults */
- pdev->vsnapshot = 0;
-
- /* Set video size, first try the last used video size
- (or the default one); if that fails try QCIF/10 or QSIF/10;
- it that fails too, give up.
- */
- i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0);
- if (i) {
- unsigned int default_resolution;
- PWC_DEBUG_OPEN("First attempt at set_video_mode failed.\n");
- if (pdev->type>= 730)
- default_resolution = PSZ_QSIF;
- else
- default_resolution = PSZ_QCIF;
-
- i = pwc_set_video_mode(pdev,
- pwc_image_sizes[default_resolution].x,
- pwc_image_sizes[default_resolution].y,
- 10,
- pdev->vcompression,
- 0);
- }
- if (i) {
- PWC_DEBUG_OPEN("Second attempt at set_video_mode failed.\n");
- pwc_free_buffers(pdev);
- return i;
- }
-
- /* Initialize the webcam to sane value */
- pwc_set_brightness(pdev, 0x7fff);
- pwc_set_agc(pdev, 1, 0);
+ if (!pdev->udev)
+ return -ENODEV;
- pdev->vopen++;
file->private_data = vdev;
PWC_DEBUG_OPEN("<< video_open() returns 0.\n");
return 0;
@@ -1158,239 +670,211 @@ static void pwc_video_release(struct video_device *vfd)
if (device_hint[hint].pdev == pdev)
device_hint[hint].pdev = NULL;
+ /* Free intermediate decompression buffer & tables */
+ if (pdev->decompress_data != NULL) {
+ PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n",
+ pdev->decompress_data);
+ kfree(pdev->decompress_data);
+ pdev->decompress_data = NULL;
+ }
+
+ v4l2_ctrl_handler_free(&pdev->ctrl_handler);
+
kfree(pdev);
}
-/* Note that all cleanup is done in the reverse order as in _open */
static int pwc_video_close(struct file *file)
{
struct video_device *vdev = file->private_data;
struct pwc_device *pdev;
- int i;
PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);
pdev = video_get_drvdata(vdev);
- if (pdev->vopen == 0)
- PWC_DEBUG_MODULE("video_close() called on closed device?\n");
-
- /* Dump statistics, but only if a reasonable amount of frames were
- processed (to prevent endless log-entries in case of snap-shot
- programs)
- */
- if (pdev->vframe_count > 20)
- PWC_DEBUG_MODULE("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
-
- if (DEVICE_USE_CODEC1(pdev->type))
- pwc_dec1_exit();
- else
- pwc_dec23_exit();
-
- pwc_isoc_cleanup(pdev);
- pwc_free_buffers(pdev);
-
- /* Turn off LEDS and power down camera, but only when not unplugged */
- if (!pdev->unplugged) {
- /* Turn LEDs off */
- if (pwc_set_leds(pdev, 0, 0) < 0)
- PWC_DEBUG_MODULE("Failed to set LED on/off time.\n");
- if (power_save) {
- i = pwc_camera_power(pdev, 0);
- if (i < 0)
- PWC_ERROR("Failed to power down camera (%d)\n", i);
- }
- pdev->vopen--;
- PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen);
+ if (pdev->capt_file == file) {
+ vb2_queue_release(&pdev->vb_queue);
+ pdev->capt_file = NULL;
}
+ PWC_DEBUG_OPEN("<< video_close()\n");
return 0;
}
-/*
- * FIXME: what about two parallel reads ????
- * ANSWER: Not supported. You can't open the device more than once,
- despite what the V4L1 interface says. First, I don't see
- the need, second there's no mechanism of alerting the
- 2nd/3rd/... process of events like changing image size.
- And I don't see the point of blocking that for the
- 2nd/3rd/... process.
- In multi-threaded environments reading parallel from any
- device is tricky anyhow.
- */
-
static ssize_t pwc_video_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct video_device *vdev = file->private_data;
- struct pwc_device *pdev;
- int noblock = file->f_flags & O_NONBLOCK;
- DECLARE_WAITQUEUE(wait, current);
- int bytes_to_read, rv = 0;
- void *image_buffer_addr;
-
- PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n",
- vdev, buf, count);
- if (vdev == NULL)
- return -EFAULT;
- pdev = video_get_drvdata(vdev);
- if (pdev == NULL)
- return -EFAULT;
+ struct pwc_device *pdev = video_get_drvdata(vdev);
- if (pdev->error_status) {
- rv = -pdev->error_status; /* Something happened, report what. */
- goto err_out;
- }
+ if (!pdev->udev)
+ return -ENODEV;
- /* Start the stream (if not already started) */
- rv = pwc_isoc_init(pdev);
- if (rv)
- goto err_out;
-
- /* In case we're doing partial reads, we don't have to wait for a frame */
- if (pdev->image_read_pos == 0) {
- /* Do wait queueing according to the (doc)book */
- add_wait_queue(&pdev->frameq, &wait);
- while (pdev->full_frames == NULL) {
- /* Check for unplugged/etc. here */
- if (pdev->error_status) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- rv = -pdev->error_status ;
- goto err_out;
- }
- if (noblock) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- rv = -EWOULDBLOCK;
- goto err_out;
- }
- if (signal_pending(current)) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- rv = -ERESTARTSYS;
- goto err_out;
- }
- mutex_unlock(&pdev->modlock);
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- mutex_lock(&pdev->modlock);
- }
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
+ if (pdev->capt_file != NULL &&
+ pdev->capt_file != file)
+ return -EBUSY;
- /* Decompress and release frame */
- if (pwc_handle_frame(pdev)) {
- rv = -EFAULT;
- goto err_out;
- }
- }
+ pdev->capt_file = file;
- PWC_DEBUG_READ("Copying data to user space.\n");
- if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
- bytes_to_read = pdev->frame_size + sizeof(struct pwc_raw_frame);
- else
- bytes_to_read = pdev->view.size;
-
- /* copy bytes to user space; we allow for partial reads */
- if (count + pdev->image_read_pos > bytes_to_read)
- count = bytes_to_read - pdev->image_read_pos;
- image_buffer_addr = pdev->image_data;
- image_buffer_addr += pdev->images[pdev->fill_image].offset;
- image_buffer_addr += pdev->image_read_pos;
- if (copy_to_user(buf, image_buffer_addr, count)) {
- rv = -EFAULT;
- goto err_out;
- }
- pdev->image_read_pos += count;
- if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */
- pdev->image_read_pos = 0;
- pwc_next_image(pdev);
- }
- return count;
-err_out:
- return rv;
+ return vb2_read(&pdev->vb_queue, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
}
static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
{
struct video_device *vdev = file->private_data;
- struct pwc_device *pdev;
- int ret;
+ struct pwc_device *pdev = video_get_drvdata(vdev);
- if (vdev == NULL)
- return -EFAULT;
- pdev = video_get_drvdata(vdev);
- if (pdev == NULL)
- return -EFAULT;
+ if (!pdev->udev)
+ return POLL_ERR;
- /* Start the stream (if not already started) */
- ret = pwc_isoc_init(pdev);
- if (ret)
- return ret;
+ return vb2_poll(&pdev->vb_queue, file, wait);
+}
+
+static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = file->private_data;
+ struct pwc_device *pdev = video_get_drvdata(vdev);
+
+ if (pdev->capt_file != file)
+ return -EBUSY;
+
+ return vb2_mmap(&pdev->vb_queue, vma);
+}
+
+/***************************************************************************/
+/* Videobuf2 operations */
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+
+ if (*nbuffers < MIN_FRAMES)
+ *nbuffers = MIN_FRAMES;
+ else if (*nbuffers > MAX_FRAMES)
+ *nbuffers = MAX_FRAMES;
+
+ *nplanes = 1;
- poll_wait(file, &pdev->frameq, wait);
- if (pdev->error_status)
- return POLLERR;
- if (pdev->full_frames != NULL) /* we have frames waiting */
- return (POLLIN | POLLRDNORM);
+ sizes[0] = PAGE_ALIGN((pdev->abs_max.x * pdev->abs_max.y * 3) / 2);
return 0;
}
-static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
+static int buffer_init(struct vb2_buffer *vb)
{
- struct video_device *vdev = file->private_data;
- struct pwc_device *pdev;
- unsigned long start;
- unsigned long size;
- unsigned long page, pos = 0;
- int index;
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
- PWC_DEBUG_MEMORY(">> %s\n", __func__);
- pdev = video_get_drvdata(vdev);
- size = vma->vm_end - vma->vm_start;
- start = vma->vm_start;
+ /* need vmalloc since frame buffer > 128K */
+ buf->data = vzalloc(PWC_FRAME_SIZE);
+ if (buf->data == NULL)
+ return -ENOMEM;
- /* Find the idx buffer for this mapping */
- for (index = 0; index < pwc_mbufs; index++) {
- pos = pdev->images[index].offset;
- if ((pos>>PAGE_SHIFT) == vma->vm_pgoff)
- break;
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+
+ /* Don't allow queing new buffers after device disconnection */
+ if (!pdev->udev)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int buffer_finish(struct vb2_buffer *vb)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+ /*
+ * Application has called dqbuf and is getting back a buffer we've
+ * filled, take the pwc data we've stored in buf->data and decompress
+ * it into a usable format, storing the result in the vb2_buffer
+ */
+ return pwc_decompress(pdev, buf);
+}
+
+static void buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+ vfree(buf->data);
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+ list_add_tail(&buf->list, &pdev->queued_bufs);
+ spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+
+ if (!pdev->udev)
+ return -ENODEV;
+
+ /* Turn on camera and set LEDS on */
+ pwc_camera_power(pdev, 1);
+ if (pdev->power_save) {
+ /* Restore video mode */
+ pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y,
+ pdev->vframes, pdev->vcompression,
+ pdev->vsnapshot);
}
- if (index == MAX_IMAGES)
- return -EINVAL;
- if (index == 0) {
- /*
- * Special case for v4l1. In v4l1, we map only one big buffer,
- * but in v4l2 each buffer is mapped
- */
- unsigned long total_size;
- total_size = pwc_mbufs * pdev->len_per_image;
- if (size != pdev->len_per_image && size != total_size) {
- PWC_ERROR("Wrong size (%lu) needed to be len_per_image=%d or total_size=%lu\n",
- size, pdev->len_per_image, total_size);
- return -EINVAL;
- }
- } else if (size > pdev->len_per_image)
- return -EINVAL;
-
- vma->vm_flags |= VM_IO; /* from 2.6.9-acX */
-
- pos += (unsigned long)pdev->image_data;
- while (size > 0) {
- page = vmalloc_to_pfn((void *)pos);
- if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
- return -EAGAIN;
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
+ pwc_set_leds(pdev, led_on, led_off);
+
+ return pwc_isoc_init(pdev);
+}
+
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+
+ if (pdev->udev) {
+ pwc_set_leds(pdev, 0, 0);
+ pwc_camera_power(pdev, 0);
+ pwc_isoc_cleanup(pdev);
}
+ pwc_cleanup_queued_bufs(pdev);
+
return 0;
}
+static void pwc_lock(struct vb2_queue *vq)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+ mutex_lock(&pdev->modlock);
+}
+
+static void pwc_unlock(struct vb2_queue *vq)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+ mutex_unlock(&pdev->modlock);
+}
+
+static struct vb2_ops pwc_vb_queue_ops = {
+ .queue_setup = queue_setup,
+ .buf_init = buffer_init,
+ .buf_prepare = buffer_prepare,
+ .buf_finish = buffer_finish,
+ .buf_cleanup = buffer_cleanup,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = pwc_unlock,
+ .wait_finish = pwc_lock,
+};
+
/***************************************************************************/
/* USB functions */
@@ -1406,6 +890,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
int hint, rc;
int features = 0;
int video_nr = -1; /* default: use next available device */
+ int my_power_save = power_save;
char serial_number[30], *name;
vendor_id = le16_to_cpu(udev->descriptor.idVendor);
@@ -1513,6 +998,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
name = "Logitech QuickCam Pro 4000";
type_id = 740; /* CCD sensor */
+ if (my_power_save == -1)
+ my_power_save = 1;
break;
case 0x08b3:
PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
@@ -1523,12 +1010,15 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
name = "Logitech QuickCam Zoom";
type_id = 740; /* CCD sensor */
- power_save = 1;
+ if (my_power_save == -1)
+ my_power_save = 1;
break;
case 0x08b5:
PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
name = "Logitech QuickCam Orbit";
type_id = 740; /* CCD sensor */
+ if (my_power_save == -1)
+ my_power_save = 1;
features |= FEATURE_MOTOR_PANTILT;
break;
case 0x08b6:
@@ -1583,6 +1073,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
PWC_INFO("Creative Labs Webcam 5 detected.\n");
name = "Creative Labs Webcam 5";
type_id = 730;
+ if (my_power_save == -1)
+ my_power_save = 1;
break;
case 0x4011:
PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
@@ -1640,6 +1132,9 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
else
return -ENODEV; /* Not any of the know types; but the list keeps growing. */
+ if (my_power_save == -1)
+ my_power_save = 0;
+
memset(serial_number, 0, 30);
usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
@@ -1654,7 +1149,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
return -ENOMEM;
}
pdev->type = type_id;
- pdev->vsize = default_size;
pdev->vframes = default_fps;
strcpy(pdev->serial, serial_number);
pdev->features = features;
@@ -1668,13 +1162,26 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->angle_range.tilt_min = -3000;
pdev->angle_range.tilt_max = 2500;
}
+ pwc_construct(pdev); /* set min/max sizes correct */
mutex_init(&pdev->modlock);
- spin_lock_init(&pdev->ptrlock);
+ mutex_init(&pdev->udevlock);
+ spin_lock_init(&pdev->queued_bufs_lock);
+ INIT_LIST_HEAD(&pdev->queued_bufs);
pdev->udev = udev;
- init_waitqueue_head(&pdev->frameq);
pdev->vcompression = pwc_preferred_compression;
+ pdev->power_save = my_power_save;
+
+ /* Init videobuf2 queue structure */
+ memset(&pdev->vb_queue, 0, sizeof(pdev->vb_queue));
+ pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ pdev->vb_queue.drv_priv = pdev;
+ pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf);
+ pdev->vb_queue.ops = &pwc_vb_queue_ops;
+ pdev->vb_queue.mem_ops = &vb2_vmalloc_memops;
+ vb2_queue_init(&pdev->vb_queue);
/* Init video_device structure */
memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
@@ -1707,14 +1214,40 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev);
usb_set_intfdata(intf, pdev);
+#ifdef CONFIG_USB_PWC_DEBUG
+ /* Query sensor type */
+ if (pwc_get_cmos_sensor(pdev, &rc) >= 0) {
+ PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
+ pdev->vdev.name,
+ pwc_sensor_type_to_string(rc), rc);
+ }
+#endif
+
/* Set the leds off */
pwc_set_leds(pdev, 0, 0);
+
+ /* Setup intial videomode */
+ rc = pwc_set_video_mode(pdev, pdev->view_max.x, pdev->view_max.y,
+ pdev->vframes, pdev->vcompression, 0);
+ if (rc)
+ goto err_free_mem;
+
+ /* Register controls (and read default values from camera */
+ rc = pwc_init_controls(pdev);
+ if (rc) {
+ PWC_ERROR("Failed to register v4l2 controls (%d).\n", rc);
+ goto err_free_mem;
+ }
+
+ pdev->vdev.ctrl_handler = &pdev->ctrl_handler;
+
+ /* And powerdown the camera until streaming starts */
pwc_camera_power(pdev, 0);
rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr);
if (rc < 0) {
PWC_ERROR("Failed to register as video device (%d).\n", rc);
- goto err_free_mem;
+ goto err_free_controls;
}
rc = pwc_create_sysfs_files(pdev);
if (rc)
@@ -1757,7 +1290,10 @@ err_video_unreg:
if (hint < MAX_DEV_HINTS)
device_hint[hint].pdev = NULL;
video_unregister_device(&pdev->vdev);
+err_free_controls:
+ v4l2_ctrl_handler_free(&pdev->ctrl_handler);
err_free_mem:
+ usb_set_intfdata(intf, NULL);
kfree(pdev);
return rc;
}
@@ -1767,33 +1303,17 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
{
struct pwc_device *pdev = usb_get_intfdata(intf);
+ mutex_lock(&pdev->udevlock);
mutex_lock(&pdev->modlock);
- usb_set_intfdata (intf, NULL);
- if (pdev == NULL) {
- PWC_ERROR("pwc_disconnect() Called without private pointer.\n");
- goto disconnect_out;
- }
- if (pdev->udev == NULL) {
- PWC_ERROR("pwc_disconnect() already called for %p\n", pdev);
- goto disconnect_out;
- }
- if (pdev->udev != interface_to_usbdev(intf)) {
- PWC_ERROR("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
- goto disconnect_out;
- }
-
- /* We got unplugged; this is signalled by an EPIPE error code */
- pdev->error_status = EPIPE;
- pdev->unplugged = 1;
-
- /* Alert waiting processes */
- wake_up_interruptible(&pdev->frameq);
+ usb_set_intfdata(intf, NULL);
/* No need to keep the urbs around after disconnection */
pwc_isoc_cleanup(pdev);
+ pwc_cleanup_queued_bufs(pdev);
+ pdev->udev = NULL;
-disconnect_out:
mutex_unlock(&pdev->modlock);
+ mutex_unlock(&pdev->udevlock);
pwc_remove_sysfs_files(pdev);
video_unregister_device(&pdev->vdev);
@@ -1809,36 +1329,27 @@ disconnect_out:
* Initialization code & module stuff
*/
-static char *size;
static int fps;
-static int fbufs;
-static int mbufs;
static int compression = -1;
static int leds[2] = { -1, -1 };
static unsigned int leds_nargs;
static char *dev_hint[MAX_DEV_HINTS];
static unsigned int dev_hint_nargs;
-module_param(size, charp, 0444);
module_param(fps, int, 0444);
-module_param(fbufs, int, 0444);
-module_param(mbufs, int, 0444);
#ifdef CONFIG_USB_PWC_DEBUG
module_param_named(trace, pwc_trace, int, 0644);
#endif
-module_param(power_save, int, 0444);
+module_param(power_save, int, 0644);
module_param(compression, int, 0444);
module_param_array(leds, int, &leds_nargs, 0444);
module_param_array(dev_hint, charp, &dev_hint_nargs, 0444);
-MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga");
MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30");
-MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve");
-MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers");
#ifdef CONFIG_USB_PWC_DEBUG
MODULE_PARM_DESC(trace, "For debugging purposes");
#endif
-MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off");
+MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off");
MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)");
MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
MODULE_PARM_DESC(dev_hint, "Device node hints");
@@ -1851,14 +1362,19 @@ MODULE_VERSION( PWC_VERSION );
static int __init usb_pwc_init(void)
{
- int i, sz;
- char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
+ int i;
+#ifdef CONFIG_USB_PWC_DEBUG
PWC_INFO("Philips webcam module version " PWC_VERSION " loaded.\n");
PWC_INFO("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n");
PWC_INFO("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n");
PWC_INFO("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
+ if (pwc_trace >= 0) {
+ PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace);
+ }
+#endif
+
if (fps) {
if (fps < 4 || fps > 30) {
PWC_ERROR("Framerate out of bounds (4-30).\n");
@@ -1868,41 +1384,6 @@ static int __init usb_pwc_init(void)
PWC_DEBUG_MODULE("Default framerate set to %d.\n", default_fps);
}
- if (size) {
- /* string; try matching with array */
- for (sz = 0; sz < PSZ_MAX; sz++) {
- if (!strcmp(sizenames[sz], size)) { /* Found! */
- default_size = sz;
- break;
- }
- }
- if (sz == PSZ_MAX) {
- PWC_ERROR("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n");
- return -EINVAL;
- }
- PWC_DEBUG_MODULE("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y);
- }
- if (mbufs) {
- if (mbufs < 1 || mbufs > MAX_IMAGES) {
- PWC_ERROR("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES);
- return -EINVAL;
- }
- pwc_mbufs = mbufs;
- PWC_DEBUG_MODULE("Number of image buffers set to %d.\n", pwc_mbufs);
- }
- if (fbufs) {
- if (fbufs < 2 || fbufs > MAX_FRAMES) {
- PWC_ERROR("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES);
- return -EINVAL;
- }
- default_fbufs = fbufs;
- PWC_DEBUG_MODULE("Number of frame buffers set to %d.\n", default_fbufs);
- }
-#ifdef CONFIG_USB_PWC_DEBUG
- if (pwc_trace >= 0) {
- PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace);
- }
-#endif
if (compression >= 0) {
if (compression > 3) {
PWC_ERROR("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n");
@@ -1911,8 +1392,6 @@ static int __init usb_pwc_init(void)
pwc_preferred_compression = compression;
PWC_DEBUG_MODULE("Preferred compression set to %d.\n", pwc_preferred_compression);
}
- if (power_save)
- PWC_DEBUG_MODULE("Enabling power save on open/close.\n");
if (leds[0] >= 0)
led_on = leds[0];
if (leds[1] >= 0)
diff --git a/drivers/media/video/pwc/pwc-ioctl.h b/drivers/media/video/pwc/pwc-ioctl.h
deleted file mode 100644
index 8c0cae7b3daf..000000000000
--- a/drivers/media/video/pwc/pwc-ioctl.h
+++ /dev/null
@@ -1,323 +0,0 @@
-#ifndef PWC_IOCTL_H
-#define PWC_IOCTL_H
-
-/* (C) 2001-2004 Nemosoft Unv.
- (C) 2004-2006 Luc Saillard (luc@saillard.org)
-
- NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
- driver and thus may have bugs that are not present in the original version.
- Please send bug reports and support requests to <luc@saillard.org>.
- The decompression routines have been implemented by reverse-engineering the
- Nemosoft binary pwcx module. Caveat emptor.
-
- 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
-*/
-
-/* This is pwc-ioctl.h belonging to PWC 10.0.10
- It contains structures and defines to communicate from user space
- directly to the driver.
- */
-
-/*
- Changes
- 2001/08/03 Alvarado Added ioctl constants to access methods for
- changing white balance and red/blue gains
- 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE
- 2003/12/13 Nemosft Unv. Some modifications to make interfacing to
- PWCX easier
- */
-
-/* These are private ioctl() commands, specific for the Philips webcams.
- They contain functions not found in other webcams, and settings not
- specified in the Video4Linux API.
-
- The #define names are built up like follows:
- VIDIOC VIDeo IOCtl prefix
- PWC Philps WebCam
- G optional: Get
- S optional: Set
- ... the function
- */
-
-#include <linux/types.h>
-#include <linux/version.h>
-
- /* Enumeration of image sizes */
-#define PSZ_SQCIF 0x00
-#define PSZ_QSIF 0x01
-#define PSZ_QCIF 0x02
-#define PSZ_SIF 0x03
-#define PSZ_CIF 0x04
-#define PSZ_VGA 0x05
-#define PSZ_MAX 6
-
-
-/* The frame rate is encoded in the video_window.flags parameter using
- the upper 16 bits, since some flags are defined nowadays. The following
- defines provide a mask and shift to filter out this value.
- This value can also be passing using the private flag when using v4l2 and
- VIDIOC_S_FMT ioctl.
-
- In 'Snapshot' mode the camera freezes its automatic exposure and colour
- balance controls.
- */
-#define PWC_FPS_SHIFT 16
-#define PWC_FPS_MASK 0x00FF0000
-#define PWC_FPS_FRMASK 0x003F0000
-#define PWC_FPS_SNAPSHOT 0x00400000
-#define PWC_QLT_MASK 0x03000000
-#define PWC_QLT_SHIFT 24
-
-
-/* structure for transferring x & y coordinates */
-struct pwc_coord
-{
- int x, y; /* guess what */
- int size; /* size, or offset */
-};
-
-
-/* Used with VIDIOCPWCPROBE */
-struct pwc_probe
-{
- char name[32];
- int type;
-};
-
-struct pwc_serial
-{
- char serial[30]; /* String with serial number. Contains terminating 0 */
-};
-
-/* pwc_whitebalance.mode values */
-#define PWC_WB_INDOOR 0
-#define PWC_WB_OUTDOOR 1
-#define PWC_WB_FL 2
-#define PWC_WB_MANUAL 3
-#define PWC_WB_AUTO 4
-
-/* Used with VIDIOCPWC[SG]AWB (Auto White Balance).
- Set mode to one of the PWC_WB_* values above.
- *red and *blue are the respective gains of these colour components inside
- the camera; range 0..65535
- When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read;
- otherwise undefined.
- 'read_red' and 'read_blue' are read-only.
-*/
-struct pwc_whitebalance
-{
- int mode;
- int manual_red, manual_blue; /* R/W */
- int read_red, read_blue; /* R/O */
-};
-
-/*
- 'control_speed' and 'control_delay' are used in automatic whitebalance mode,
- and tell the camera how fast it should react to changes in lighting, and
- with how much delay. Valid values are 0..65535.
-*/
-struct pwc_wb_speed
-{
- int control_speed;
- int control_delay;
-
-};
-
-/* Used with VIDIOCPWC[SG]LED */
-struct pwc_leds
-{
- int led_on; /* Led on-time; range = 0..25000 */
- int led_off; /* Led off-time; range = 0..25000 */
-};
-
-/* Image size (used with GREALSIZE) */
-struct pwc_imagesize
-{
- int width;
- int height;
-};
-
-/* Defines and structures for Motorized Pan & Tilt */
-#define PWC_MPT_PAN 0x01
-#define PWC_MPT_TILT 0x02
-#define PWC_MPT_TIMEOUT 0x04 /* for status */
-
-/* Set angles; when absolute != 0, the angle is absolute and the
- driver calculates the relative offset for you. This can only
- be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns
- absolute angles.
- */
-struct pwc_mpt_angles
-{
- int absolute; /* write-only */
- int pan; /* degrees * 100 */
- int tilt; /* degress * 100 */
-};
-
-/* Range of angles of the camera, both horizontally and vertically.
- */
-struct pwc_mpt_range
-{
- int pan_min, pan_max; /* degrees * 100 */
- int tilt_min, tilt_max;
-};
-
-struct pwc_mpt_status
-{
- int status;
- int time_pan;
- int time_tilt;
-};
-
-
-/* This is used for out-of-kernel decompression. With it, you can get
- all the necessary information to initialize and use the decompressor
- routines in standalone applications.
- */
-struct pwc_video_command
-{
- int type; /* camera type (645, 675, 730, etc.) */
- int release; /* release number */
-
- int size; /* one of PSZ_* */
- int alternate;
- int command_len; /* length of USB video command */
- unsigned char command_buf[13]; /* Actual USB video command */
- int bandlength; /* >0 = compressed */
- int frame_size; /* Size of one (un)compressed frame */
-};
-
-/* Flags for PWCX subroutines. Not all modules honour all flags. */
-#define PWCX_FLAG_PLANAR 0x0001
-#define PWCX_FLAG_BAYER 0x0008
-
-
-/* IOCTL definitions */
-
- /* Restore user settings */
-#define VIDIOCPWCRUSER _IO('v', 192)
- /* Save user settings */
-#define VIDIOCPWCSUSER _IO('v', 193)
- /* Restore factory settings */
-#define VIDIOCPWCFACTORY _IO('v', 194)
-
- /* You can manipulate the compression factor. A compression preference of 0
- means use uncompressed modes when available; 1 is low compression, 2 is
- medium and 3 is high compression preferred. Of course, the higher the
- compression, the lower the bandwidth used but more chance of artefacts
- in the image. The driver automatically chooses a higher compression when
- the preferred mode is not available.
- */
- /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */
-#define VIDIOCPWCSCQUAL _IOW('v', 195, int)
- /* Get preferred compression quality */
-#define VIDIOCPWCGCQUAL _IOR('v', 195, int)
-
-
-/* Retrieve serial number of camera */
-#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial)
-
- /* This is a probe function; since so many devices are supported, it
- becomes difficult to include all the names in programs that want to
- check for the enhanced Philips stuff. So in stead, try this PROBE;
- it returns a structure with the original name, and the corresponding
- Philips type.
- To use, fill the structure with zeroes, call PROBE and if that succeeds,
- compare the name with that returned from VIDIOCGCAP; they should be the
- same. If so, you can be assured it is a Philips (OEM) cam and the type
- is valid.
- */
-#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe)
-
- /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */
-#define VIDIOCPWCSAGC _IOW('v', 200, int)
- /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */
-#define VIDIOCPWCGAGC _IOR('v', 200, int)
- /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */
-#define VIDIOCPWCSSHUTTER _IOW('v', 201, int)
-
- /* Color compensation (Auto White Balance) */
-#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance)
-#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance)
-
- /* Auto WB speed */
-#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed)
-#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed)
-
- /* LEDs on/off/blink; int range 0..65535 */
-#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds)
-#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds)
-
- /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */
-#define VIDIOCPWCSCONTOUR _IOW('v', 206, int)
-#define VIDIOCPWCGCONTOUR _IOR('v', 206, int)
-
- /* Backlight compensation; 0 = off, otherwise on */
-#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int)
-#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int)
-
- /* Flickerless mode; = 0 off, otherwise on */
-#define VIDIOCPWCSFLICKER _IOW('v', 208, int)
-#define VIDIOCPWCGFLICKER _IOR('v', 208, int)
-
- /* Dynamic noise reduction; 0 off, 3 = high noise reduction */
-#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int)
-#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int)
-
- /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */
-#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize)
-
- /* Motorized pan & tilt functions */
-#define VIDIOCPWCMPTRESET _IOW('v', 211, int)
-#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range)
-#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles)
-#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles)
-#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status)
-
- /* Get the USB set-video command; needed for initializing libpwcx */
-#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command)
-struct pwc_table_init_buffer {
- int len;
- char *buffer;
-
-};
-#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer)
-
-/*
- * This is private command used when communicating with v4l2.
- * In the future all private ioctl will be remove/replace to
- * use interface offer by v4l2.
- */
-
-#define V4L2_CID_PRIVATE_SAVE_USER (V4L2_CID_PRIVATE_BASE + 0)
-#define V4L2_CID_PRIVATE_RESTORE_USER (V4L2_CID_PRIVATE_BASE + 1)
-#define V4L2_CID_PRIVATE_RESTORE_FACTORY (V4L2_CID_PRIVATE_BASE + 2)
-#define V4L2_CID_PRIVATE_COLOUR_MODE (V4L2_CID_PRIVATE_BASE + 3)
-#define V4L2_CID_PRIVATE_AUTOCONTOUR (V4L2_CID_PRIVATE_BASE + 4)
-#define V4L2_CID_PRIVATE_CONTOUR (V4L2_CID_PRIVATE_BASE + 5)
-#define V4L2_CID_PRIVATE_BACKLIGHT (V4L2_CID_PRIVATE_BASE + 6)
-#define V4L2_CID_PRIVATE_FLICKERLESS (V4L2_CID_PRIVATE_BASE + 7)
-#define V4L2_CID_PRIVATE_NOISE_REDUCTION (V4L2_CID_PRIVATE_BASE + 8)
-
-struct pwc_raw_frame {
- __le16 type; /* type of the webcam */
- __le16 vbandlength; /* Size of 4lines compressed (used by the decompressor) */
- __u8 cmd[4]; /* the four byte of the command (in case of nala,
- only the first 3 bytes is filled) */
- __u8 rawframe[0]; /* frame_size = H/4*vbandlength */
-} __attribute__ ((packed));
-
-
-#endif
diff --git a/drivers/media/video/pwc/pwc-kiara.c b/drivers/media/video/pwc/pwc-kiara.c
index f4ae83c0cf2b..e5f4fd817125 100644
--- a/drivers/media/video/pwc/pwc-kiara.c
+++ b/drivers/media/video/pwc/pwc-kiara.c
@@ -40,7 +40,6 @@
#include "pwc-kiara.h"
-#include "pwc-uncompress.h"
const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA] = { 5, 10, 15, 20, 25, 30 };
diff --git a/drivers/media/video/pwc/pwc-misc.c b/drivers/media/video/pwc/pwc-misc.c
index 6af5bb538358..0b031336eab8 100644
--- a/drivers/media/video/pwc/pwc-misc.c
+++ b/drivers/media/video/pwc/pwc-misc.c
@@ -126,8 +126,4 @@ void pwc_construct(struct pwc_device *pdev)
pdev->pixfmt = V4L2_PIX_FMT_YUV420; /* default */
pdev->view_min.size = pdev->view_min.x * pdev->view_min.y;
pdev->view_max.size = pdev->view_max.x * pdev->view_max.y;
- /* length of image, in YUV format; always allocate enough memory. */
- pdev->len_per_image = PAGE_ALIGN((pdev->abs_max.x * pdev->abs_max.y * 3) / 2);
}
-
-
diff --git a/drivers/media/video/pwc/pwc-uncompress.c b/drivers/media/video/pwc/pwc-uncompress.c
index 3b73f295f032..51265092bd31 100644
--- a/drivers/media/video/pwc/pwc-uncompress.c
+++ b/drivers/media/video/pwc/pwc-uncompress.c
@@ -30,26 +30,17 @@
#include <asm/types.h>
#include "pwc.h"
-#include "pwc-uncompress.h"
#include "pwc-dec1.h"
#include "pwc-dec23.h"
-int pwc_decompress(struct pwc_device *pdev)
+int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf)
{
- struct pwc_frame_buf *fbuf;
int n, line, col, stride;
void *yuv, *image;
u16 *src;
u16 *dsty, *dstu, *dstv;
- if (pdev == NULL)
- return -EFAULT;
-
- fbuf = pdev->read_frame;
- if (fbuf == NULL)
- return -EFAULT;
- image = pdev->image_data;
- image += pdev->images[pdev->fill_image].offset;
+ image = vb2_plane_vaddr(&fbuf->vb, 0);
yuv = fbuf->data + pdev->frame_header_size; /* Skip header */
@@ -64,9 +55,13 @@ int pwc_decompress(struct pwc_device *pdev)
* determine this using the type of the webcam */
memcpy(raw_frame->cmd, pdev->cmd_buf, 4);
memcpy(raw_frame+1, yuv, pdev->frame_size);
+ vb2_set_plane_payload(&fbuf->vb, 0,
+ pdev->frame_size + sizeof(struct pwc_raw_frame));
return 0;
}
+ vb2_set_plane_payload(&fbuf->vb, 0, pdev->view.size);
+
if (pdev->vbandlength == 0) {
/* Uncompressed mode.
* We copy the data into the output buffer, using the viewport
diff --git a/drivers/media/video/pwc/pwc-uncompress.h b/drivers/media/video/pwc/pwc-uncompress.h
deleted file mode 100644
index 43028e74e9e0..000000000000
--- a/drivers/media/video/pwc/pwc-uncompress.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* (C) 1999-2003 Nemosoft Unv.
- (C) 2004-2006 Luc Saillard (luc@saillard.org)
-
- NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
- driver and thus may have bugs that are not present in the original version.
- Please send bug reports and support requests to <luc@saillard.org>.
- The decompression routines have been implemented by reverse-engineering the
- Nemosoft binary pwcx module. Caveat emptor.
-
- 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
-*/
-
-/* This file is the bridge between the kernel module and the plugin; it
- describes the structures and datatypes used in both modules. Any
- significant change should be reflected by increasing the
- pwc_decompressor_version major number.
- */
-#ifndef PWC_UNCOMPRESS_H
-#define PWC_UNCOMPRESS_H
-
-
-#include <media/pwc-ioctl.h>
-
-/* from pwc-dec.h */
-#define PWCX_FLAG_PLANAR 0x0001
-/* */
-
-#endif
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index f85c51249c7b..a10ff6b64acf 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -2,6 +2,7 @@
USB and Video4Linux interface part.
(C) 1999-2004 Nemosoft Unv.
(C) 2004-2006 Luc Saillard (luc@saillard.org)
+ (C) 2011 Hans de Goede <hdegoede@redhat.com>
NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
driver and thus may have bugs that are not present in the original version.
@@ -31,184 +32,330 @@
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/vmalloc.h>
+#include <linux/jiffies.h>
#include <asm/io.h>
#include "pwc.h"
-static struct v4l2_queryctrl pwc_controls[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 128,
- .step = 1,
- .default_value = 64,
- },
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 64,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = -100,
- .maximum = 100,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = 0,
- .maximum = 32,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Gain",
- .minimum = 0,
- .maximum = 256,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Gain",
- .minimum = 0,
- .maximum = 256,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto White Balance",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Shutter Speed (Exposure)",
- .minimum = 0,
- .maximum = 256,
- .step = 1,
- .default_value = 200,
- },
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain Enabled",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain Level",
- .minimum = 0,
- .maximum = 256,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_SAVE_USER,
- .type = V4L2_CTRL_TYPE_BUTTON,
- .name = "Save User Settings",
- .minimum = 0,
- .maximum = 0,
- .step = 0,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_RESTORE_USER,
- .type = V4L2_CTRL_TYPE_BUTTON,
- .name = "Restore User Settings",
- .minimum = 0,
- .maximum = 0,
- .step = 0,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_RESTORE_FACTORY,
- .type = V4L2_CTRL_TYPE_BUTTON,
- .name = "Restore Factory Settings",
- .minimum = 0,
- .maximum = 0,
- .step = 0,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_COLOUR_MODE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Colour mode",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_AUTOCONTOUR,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto contour",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_CONTOUR,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contour",
- .minimum = 0,
- .maximum = 63,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_BACKLIGHT,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Backlight compensation",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_FLICKERLESS,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flickerless",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_NOISE_REDUCTION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Noise reduction",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 0,
- },
+#define PWC_CID_CUSTOM(ctrl) ((V4L2_CID_USER_BASE | 0xf000) + custom_ ## ctrl)
+
+static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
+static int pwc_s_ctrl(struct v4l2_ctrl *ctrl);
+
+static const struct v4l2_ctrl_ops pwc_ctrl_ops = {
+ .g_volatile_ctrl = pwc_g_volatile_ctrl,
+ .s_ctrl = pwc_s_ctrl,
+};
+
+enum { awb_indoor, awb_outdoor, awb_fl, awb_manual, awb_auto };
+enum { custom_autocontour, custom_contour, custom_noise_reduction,
+ custom_save_user, custom_restore_user, custom_restore_factory };
+
+const char * const pwc_auto_whitebal_qmenu[] = {
+ "Indoor (Incandescant Lighting) Mode",
+ "Outdoor (Sunlight) Mode",
+ "Indoor (Fluorescent Lighting) Mode",
+ "Manual Mode",
+ "Auto Mode",
+ NULL
+};
+
+static const struct v4l2_ctrl_config pwc_auto_white_balance_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .max = awb_auto,
+ .qmenu = pwc_auto_whitebal_qmenu,
+};
+
+static const struct v4l2_ctrl_config pwc_autocontour_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(autocontour),
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto contour",
+ .min = 0,
+ .max = 1,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config pwc_contour_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(contour),
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contour",
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = 0,
+ .max = 63,
+ .step = 1,
};
+static const struct v4l2_ctrl_config pwc_backlight_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = V4L2_CID_BACKLIGHT_COMPENSATION,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config pwc_flicker_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = V4L2_CID_BAND_STOP_FILTER,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config pwc_noise_reduction_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(noise_reduction),
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Dynamic Noise Reduction",
+ .min = 0,
+ .max = 3,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config pwc_save_user_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(save_user),
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .name = "Save User Settings",
+};
+
+static const struct v4l2_ctrl_config pwc_restore_user_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(restore_user),
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .name = "Restore User Settings",
+};
+
+static const struct v4l2_ctrl_config pwc_restore_factory_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(restore_factory),
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .name = "Restore Factory Settings",
+};
+
+int pwc_init_controls(struct pwc_device *pdev)
+{
+ struct v4l2_ctrl_handler *hdl;
+ struct v4l2_ctrl_config cfg;
+ int r, def;
+
+ hdl = &pdev->ctrl_handler;
+ r = v4l2_ctrl_handler_init(hdl, 20);
+ if (r)
+ return r;
+
+ /* Brightness, contrast, saturation, gamma */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BRIGHTNESS_FORMATTER, &def);
+ if (r || def > 127)
+ def = 63;
+ pdev->brightness = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 127, 1, def);
+
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, CONTRAST_FORMATTER, &def);
+ if (r || def > 63)
+ def = 31;
+ pdev->contrast = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 63, 1, def);
+
+ if (pdev->type >= 675) {
+ if (pdev->type < 730)
+ pdev->saturation_fmt = SATURATION_MODE_FORMATTER2;
+ else
+ pdev->saturation_fmt = SATURATION_MODE_FORMATTER1;
+ r = pwc_get_s8_ctrl(pdev, GET_CHROM_CTL, pdev->saturation_fmt,
+ &def);
+ if (r || def < -100 || def > 100)
+ def = 0;
+ pdev->saturation = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_SATURATION, -100, 100, 1, def);
+ }
+
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, GAMMA_FORMATTER, &def);
+ if (r || def > 31)
+ def = 15;
+ pdev->gamma = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 31, 1, def);
+
+ /* auto white balance, red gain, blue gain */
+ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, WB_MODE_FORMATTER, &def);
+ if (r || def > awb_auto)
+ def = awb_auto;
+ cfg = pwc_auto_white_balance_cfg;
+ cfg.name = v4l2_ctrl_get_name(cfg.id);
+ cfg.def = def;
+ pdev->auto_white_balance = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+ /* check auto controls to avoid NULL deref in v4l2_ctrl_auto_cluster */
+ if (!pdev->auto_white_balance)
+ return hdl->error;
+
+ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
+ PRESET_MANUAL_RED_GAIN_FORMATTER, &def);
+ if (r)
+ def = 127;
+ pdev->red_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 255, 1, def);
+
+ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
+ PRESET_MANUAL_BLUE_GAIN_FORMATTER, &def);
+ if (r)
+ def = 127;
+ pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 255, 1, def);
+
+ v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, true);
+
+ /* autogain, gain */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0;
+ /* Note a register value if 0 means auto gain is on */
+ pdev->autogain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, def == 0);
+ if (!pdev->autogain)
+ return hdl->error;
+
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_AGC_FORMATTER, &def);
+ if (r || def > 63)
+ def = 31;
+ pdev->gain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_GAIN, 0, 63, 1, def);
+
+ /* auto exposure, exposure */
+ if (DEVICE_USE_CODEC2(pdev->type)) {
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, SHUTTER_MODE_FORMATTER,
+ &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0;
+ /*
+ * def = 0 auto, def = ff manual
+ * menu idx 0 = auto, idx 1 = manual
+ */
+ pdev->exposure_auto = v4l2_ctrl_new_std_menu(hdl,
+ &pwc_ctrl_ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ 1, 0, def != 0);
+ if (!pdev->exposure_auto)
+ return hdl->error;
+
+ /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */
+ r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+ READ_SHUTTER_FORMATTER, &def);
+ if (r || def > 655)
+ def = 655;
+ pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 655, 1, def);
+ /* CODEC2: separate auto gain & auto exposure */
+ v4l2_ctrl_auto_cluster(2, &pdev->autogain, 0, true);
+ v4l2_ctrl_auto_cluster(2, &pdev->exposure_auto,
+ V4L2_EXPOSURE_MANUAL, true);
+ } else if (DEVICE_USE_CODEC3(pdev->type)) {
+ /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */
+ r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+ READ_SHUTTER_FORMATTER, &def);
+ if (r || def > 255)
+ def = 255;
+ pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 255, 1, def);
+ /* CODEC3: both gain and exposure controlled by autogain */
+ pdev->autogain_expo_cluster[0] = pdev->autogain;
+ pdev->autogain_expo_cluster[1] = pdev->gain;
+ pdev->autogain_expo_cluster[2] = pdev->exposure;
+ v4l2_ctrl_auto_cluster(3, pdev->autogain_expo_cluster,
+ 0, true);
+ }
+
+ /* color / bw setting */
+ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, COLOUR_MODE_FORMATTER,
+ &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0xff;
+ /* def = 0 bw, def = ff color, menu idx 0 = color, idx 1 = bw */
+ pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops,
+ V4L2_CID_COLORFX, 1, 0, def == 0);
+
+ /* autocontour, contour */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0;
+ cfg = pwc_autocontour_cfg;
+ cfg.def = def == 0;
+ pdev->autocontour = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+ if (!pdev->autocontour)
+ return hdl->error;
+
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &def);
+ if (r || def > 63)
+ def = 31;
+ cfg = pwc_contour_cfg;
+ cfg.def = def;
+ pdev->contour = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+ v4l2_ctrl_auto_cluster(2, &pdev->autocontour, 0, false);
+
+ /* backlight */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
+ BACK_LIGHT_COMPENSATION_FORMATTER, &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0;
+ cfg = pwc_backlight_cfg;
+ cfg.name = v4l2_ctrl_get_name(cfg.id);
+ cfg.def = def == 0;
+ pdev->backlight = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+ /* flikker rediction */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
+ FLICKERLESS_MODE_FORMATTER, &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0;
+ cfg = pwc_flicker_cfg;
+ cfg.name = v4l2_ctrl_get_name(cfg.id);
+ cfg.def = def == 0;
+ pdev->flicker = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+ /* Dynamic noise reduction */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
+ DYNAMIC_NOISE_CONTROL_FORMATTER, &def);
+ if (r || def > 3)
+ def = 2;
+ cfg = pwc_noise_reduction_cfg;
+ cfg.def = def;
+ pdev->noise_reduction = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+ /* Save / Restore User / Factory Settings */
+ pdev->save_user = v4l2_ctrl_new_custom(hdl, &pwc_save_user_cfg, NULL);
+ pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg,
+ NULL);
+ if (pdev->restore_user)
+ pdev->restore_user->flags |= V4L2_CTRL_FLAG_UPDATE;
+ pdev->restore_factory = v4l2_ctrl_new_custom(hdl,
+ &pwc_restore_factory_cfg,
+ NULL);
+ if (pdev->restore_factory)
+ pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE;
+
+ if (!(pdev->features & FEATURE_MOTOR_PANTILT))
+ return hdl->error;
+
+ /* Motor pan / tilt / reset */
+ pdev->motor_pan = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_PAN_RELATIVE, -4480, 4480, 64, 0);
+ if (!pdev->motor_pan)
+ return hdl->error;
+ pdev->motor_tilt = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_TILT_RELATIVE, -1920, 1920, 64, 0);
+ pdev->motor_pan_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_PAN_RESET, 0, 0, 0, 0);
+ pdev->motor_tilt_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_TILT_RESET, 0, 0, 0, 0);
+ v4l2_ctrl_cluster(4, &pdev->motor_pan);
+
+ return hdl->error;
+}
static void pwc_vidioc_fill_fmt(const struct pwc_device *pdev, struct v4l2_format *f)
{
@@ -284,10 +431,21 @@ static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f)
}
/* ioctl(VIDIOC_SET_FMT) */
-static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f)
+
+static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
{
+ struct pwc_device *pdev = video_drvdata(file);
int ret, fps, snapshot, compression, pixelformat;
+ if (!pdev->udev)
+ return -ENODEV;
+
+ if (pdev->capt_file != NULL &&
+ pdev->capt_file != file)
+ return -EBUSY;
+
+ pdev->capt_file = file;
+
ret = pwc_vidioc_try_fmt(pdev, f);
if (ret<0)
return ret;
@@ -309,7 +467,7 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f)
pixelformat != V4L2_PIX_FMT_PWC2)
return -EINVAL;
- if (pdev->iso_init)
+ if (vb2_is_streaming(&pdev->vb_queue))
return -EBUSY;
PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d "
@@ -343,13 +501,14 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f)
static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
- struct video_device *vdev = video_devdata(file);
struct pwc_device *pdev = video_drvdata(file);
+ if (!pdev->udev)
+ return -ENODEV;
+
strcpy(cap->driver, PWC_NAME);
- strlcpy(cap->card, vdev->name, sizeof(cap->card));
+ strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card));
usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->version = PWC_VERSION_CODE;
cap->capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING |
@@ -377,255 +536,375 @@ static int pwc_s_input(struct file *file, void *fh, unsigned int i)
return i ? -EINVAL : 0;
}
-static int pwc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
+static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
- int i, idx;
- u32 id;
-
- id = c->id;
- if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
- id &= V4L2_CTRL_ID_MASK;
- id++;
- idx = -1;
- for (i = 0; i < ARRAY_SIZE(pwc_controls); i++) {
- if (pwc_controls[i].id < id)
- continue;
- if (idx >= 0
- && pwc_controls[i].id > pwc_controls[idx].id)
- continue;
- idx = i;
- }
- if (idx < 0)
- return -EINVAL;
- memcpy(c, &pwc_controls[idx], sizeof pwc_controls[0]);
- return 0;
+ struct pwc_device *pdev =
+ container_of(ctrl->handler, struct pwc_device, ctrl_handler);
+ int ret = 0;
+
+ /*
+ * Sometimes it can take quite long for the pwc to complete usb control
+ * transfers, so release the modlock to give streaming by another
+ * process / thread the chance to continue with a dqbuf.
+ */
+ mutex_unlock(&pdev->modlock);
+
+ /*
+ * Take the udev-lock to protect against the disconnect handler
+ * completing and setting dev->udev to NULL underneath us. Other code
+ * does not need to do this since it is protected by the modlock.
+ */
+ mutex_lock(&pdev->udevlock);
+
+ if (!pdev->udev) {
+ ret = -ENODEV;
+ goto leave;
}
- for (i = 0; i < sizeof(pwc_controls) / sizeof(struct v4l2_queryctrl); i++) {
- if (pwc_controls[i].id == c->id) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n");
- memcpy(c, &pwc_controls[i], sizeof(struct v4l2_queryctrl));
- return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ if (pdev->color_bal_valid &&
+ (pdev->auto_white_balance->val != awb_auto ||
+ time_before(jiffies,
+ pdev->last_color_bal_update + HZ / 4))) {
+ pdev->red_balance->val = pdev->last_red_balance;
+ pdev->blue_balance->val = pdev->last_blue_balance;
+ break;
}
+ ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+ READ_RED_GAIN_FORMATTER,
+ &pdev->red_balance->val);
+ if (ret)
+ break;
+ ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+ READ_BLUE_GAIN_FORMATTER,
+ &pdev->blue_balance->val);
+ if (ret)
+ break;
+ pdev->last_red_balance = pdev->red_balance->val;
+ pdev->last_blue_balance = pdev->blue_balance->val;
+ pdev->last_color_bal_update = jiffies;
+ pdev->color_bal_valid = true;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (pdev->gain_valid && time_before(jiffies,
+ pdev->last_gain_update + HZ / 4)) {
+ pdev->gain->val = pdev->last_gain;
+ break;
+ }
+ ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+ READ_AGC_FORMATTER, &pdev->gain->val);
+ if (ret)
+ break;
+ pdev->last_gain = pdev->gain->val;
+ pdev->last_gain_update = jiffies;
+ pdev->gain_valid = true;
+ if (!DEVICE_USE_CODEC3(pdev->type))
+ break;
+ /* Fall through for CODEC3 where autogain also controls expo */
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (pdev->exposure_valid && time_before(jiffies,
+ pdev->last_exposure_update + HZ / 4)) {
+ pdev->exposure->val = pdev->last_exposure;
+ break;
+ }
+ ret = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+ READ_SHUTTER_FORMATTER,
+ &pdev->exposure->val);
+ if (ret)
+ break;
+ pdev->last_exposure = pdev->exposure->val;
+ pdev->last_exposure_update = jiffies;
+ pdev->exposure_valid = true;
+ break;
+ default:
+ ret = -EINVAL;
}
- return -EINVAL;
+
+ if (ret)
+ PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret);
+
+leave:
+ mutex_unlock(&pdev->udevlock);
+ mutex_lock(&pdev->modlock);
+ return ret;
}
-static int pwc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+static int pwc_set_awb(struct pwc_device *pdev)
{
- struct pwc_device *pdev = video_drvdata(file);
int ret;
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS:
- c->value = pwc_get_brightness(pdev);
- if (c->value < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_CONTRAST:
- c->value = pwc_get_contrast(pdev);
- if (c->value < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_SATURATION:
- ret = pwc_get_saturation(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_GAMMA:
- c->value = pwc_get_gamma(pdev);
- if (c->value < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_RED_BALANCE:
- ret = pwc_get_red_gain(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value >>= 8;
- return 0;
- case V4L2_CID_BLUE_BALANCE:
- ret = pwc_get_blue_gain(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value >>= 8;
- return 0;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = pwc_get_awb(pdev);
- if (ret < 0)
- return -EINVAL;
- c->value = (ret == PWC_WB_MANUAL) ? 0 : 1;
- return 0;
- case V4L2_CID_GAIN:
- ret = pwc_get_agc(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value >>= 8;
- return 0;
- case V4L2_CID_AUTOGAIN:
- ret = pwc_get_agc(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value = (c->value < 0) ? 1 : 0;
- return 0;
- case V4L2_CID_EXPOSURE:
- ret = pwc_get_shutter_speed(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_COLOUR_MODE:
- ret = pwc_get_colour_mode(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_AUTOCONTOUR:
- ret = pwc_get_contour(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value = (c->value == -1 ? 1 : 0);
- return 0;
- case V4L2_CID_PRIVATE_CONTOUR:
- ret = pwc_get_contour(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value >>= 10;
+ if (pdev->auto_white_balance->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+ WB_MODE_FORMATTER,
+ pdev->auto_white_balance->val);
+ if (ret)
+ return ret;
+
+ if (pdev->auto_white_balance->val != awb_manual)
+ pdev->color_bal_valid = false; /* Force cache update */
+ }
+ if (pdev->auto_white_balance->val != awb_manual)
return 0;
- case V4L2_CID_PRIVATE_BACKLIGHT:
- ret = pwc_get_backlight(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
+
+ if (pdev->red_balance->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+ PRESET_MANUAL_RED_GAIN_FORMATTER,
+ pdev->red_balance->val);
+ if (ret)
+ return ret;
+ }
+
+ if (pdev->blue_balance->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+ PRESET_MANUAL_BLUE_GAIN_FORMATTER,
+ pdev->blue_balance->val);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* For CODEC2 models which have separate autogain and auto exposure */
+static int pwc_set_autogain(struct pwc_device *pdev)
+{
+ int ret;
+
+ if (pdev->autogain->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ AGC_MODE_FORMATTER,
+ pdev->autogain->val ? 0 : 0xff);
+ if (ret)
+ return ret;
+
+ if (pdev->autogain->val)
+ pdev->gain_valid = false; /* Force cache update */
+ }
+
+ if (pdev->autogain->val)
return 0;
- case V4L2_CID_PRIVATE_FLICKERLESS:
- ret = pwc_get_flicker(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value = (c->value ? 1 : 0);
+
+ if (pdev->gain->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ PRESET_AGC_FORMATTER,
+ pdev->gain->val);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* For CODEC2 models which have separate autogain and auto exposure */
+static int pwc_set_exposure_auto(struct pwc_device *pdev)
+{
+ int ret;
+ int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO;
+
+ if (pdev->exposure_auto->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ SHUTTER_MODE_FORMATTER,
+ is_auto ? 0 : 0xff);
+ if (ret)
+ return ret;
+
+ if (is_auto)
+ pdev->exposure_valid = false; /* Force cache update */
+ }
+
+ if (is_auto)
return 0;
- case V4L2_CID_PRIVATE_NOISE_REDUCTION:
- ret = pwc_get_dynamic_noise(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
+
+ if (pdev->exposure->is_new) {
+ ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
+ PRESET_SHUTTER_FORMATTER,
+ pdev->exposure->val);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* For CODEC3 models which have autogain controlling both gain and exposure */
+static int pwc_set_autogain_expo(struct pwc_device *pdev)
+{
+ int ret;
+
+ if (pdev->autogain->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ AGC_MODE_FORMATTER,
+ pdev->autogain->val ? 0 : 0xff);
+ if (ret)
+ return ret;
+
+ if (pdev->autogain->val) {
+ pdev->gain_valid = false; /* Force cache update */
+ pdev->exposure_valid = false; /* Force cache update */
+ }
+ }
+
+ if (pdev->autogain->val)
return 0;
- case V4L2_CID_PRIVATE_SAVE_USER:
- case V4L2_CID_PRIVATE_RESTORE_USER:
- case V4L2_CID_PRIVATE_RESTORE_FACTORY:
- return -EINVAL;
+ if (pdev->gain->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ PRESET_AGC_FORMATTER,
+ pdev->gain->val);
+ if (ret)
+ return ret;
}
- return -EINVAL;
+
+ if (pdev->exposure->is_new) {
+ ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
+ PRESET_SHUTTER_FORMATTER,
+ pdev->exposure->val);
+ if (ret)
+ return ret;
+ }
+ return 0;
}
-static int pwc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+static int pwc_set_motor(struct pwc_device *pdev)
{
- struct pwc_device *pdev = video_drvdata(file);
int ret;
+ u8 buf[4];
+
+ buf[0] = 0;
+ if (pdev->motor_pan_reset->is_new)
+ buf[0] |= 0x01;
+ if (pdev->motor_tilt_reset->is_new)
+ buf[0] |= 0x02;
+ if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) {
+ ret = send_control_msg(pdev, SET_MPT_CTL,
+ PT_RESET_CONTROL_FORMATTER, buf, 1);
+ if (ret < 0)
+ return ret;
+ }
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS:
- c->value <<= 9;
- ret = pwc_set_brightness(pdev, c->value);
+ memset(buf, 0, sizeof(buf));
+ if (pdev->motor_pan->is_new) {
+ buf[0] = pdev->motor_pan->val & 0xFF;
+ buf[1] = (pdev->motor_pan->val >> 8);
+ }
+ if (pdev->motor_tilt->is_new) {
+ buf[2] = pdev->motor_tilt->val & 0xFF;
+ buf[3] = (pdev->motor_tilt->val >> 8);
+ }
+ if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) {
+ ret = send_control_msg(pdev, SET_MPT_CTL,
+ PT_RELATIVE_CONTROL_FORMATTER,
+ buf, sizeof(buf));
if (ret < 0)
- return -EINVAL;
- return 0;
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct pwc_device *pdev =
+ container_of(ctrl->handler, struct pwc_device, ctrl_handler);
+ int ret = 0;
+
+ /* See the comments on locking in pwc_g_volatile_ctrl */
+ mutex_unlock(&pdev->modlock);
+ mutex_lock(&pdev->udevlock);
+
+ if (!pdev->udev) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ BRIGHTNESS_FORMATTER, ctrl->val);
+ break;
case V4L2_CID_CONTRAST:
- c->value <<= 10;
- ret = pwc_set_contrast(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ CONTRAST_FORMATTER, ctrl->val);
+ break;
case V4L2_CID_SATURATION:
- ret = pwc_set_saturation(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
+ ret = pwc_set_s8_ctrl(pdev, SET_CHROM_CTL,
+ pdev->saturation_fmt, ctrl->val);
+ break;
case V4L2_CID_GAMMA:
- c->value <<= 11;
- ret = pwc_set_gamma(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_RED_BALANCE:
- c->value <<= 8;
- ret = pwc_set_red_gain(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_BLUE_BALANCE:
- c->value <<= 8;
- ret = pwc_set_blue_gain(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ GAMMA_FORMATTER, ctrl->val);
+ break;
case V4L2_CID_AUTO_WHITE_BALANCE:
- c->value = (c->value == 0) ? PWC_WB_MANUAL : PWC_WB_AUTO;
- ret = pwc_set_awb(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_EXPOSURE:
- c->value <<= 8;
- ret = pwc_set_shutter_speed(pdev, c->value ? 0 : 1, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
+ ret = pwc_set_awb(pdev);
+ break;
case V4L2_CID_AUTOGAIN:
- /* autogain off means nothing without a gain */
- if (c->value == 0)
- return 0;
- ret = pwc_set_agc(pdev, c->value, 0);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_GAIN:
- c->value <<= 8;
- ret = pwc_set_agc(pdev, 0, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_SAVE_USER:
- if (pwc_save_user(pdev))
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_RESTORE_USER:
- if (pwc_restore_user(pdev))
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_RESTORE_FACTORY:
- if (pwc_restore_factory(pdev))
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_COLOUR_MODE:
- ret = pwc_set_colour_mode(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_AUTOCONTOUR:
- c->value = (c->value == 1) ? -1 : 0;
- ret = pwc_set_contour(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_CONTOUR:
- c->value <<= 10;
- ret = pwc_set_contour(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_BACKLIGHT:
- ret = pwc_set_backlight(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_FLICKERLESS:
- ret = pwc_set_flicker(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- case V4L2_CID_PRIVATE_NOISE_REDUCTION:
- ret = pwc_set_dynamic_noise(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
-
+ if (DEVICE_USE_CODEC2(pdev->type))
+ ret = pwc_set_autogain(pdev);
+ else if (DEVICE_USE_CODEC3(pdev->type))
+ ret = pwc_set_autogain_expo(pdev);
+ else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (DEVICE_USE_CODEC2(pdev->type))
+ ret = pwc_set_exposure_auto(pdev);
+ else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_COLORFX:
+ ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+ COLOUR_MODE_FORMATTER,
+ ctrl->val ? 0 : 0xff);
+ break;
+ case PWC_CID_CUSTOM(autocontour):
+ if (pdev->autocontour->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ AUTO_CONTOUR_FORMATTER,
+ pdev->autocontour->val ? 0 : 0xff);
+ }
+ if (ret == 0 && pdev->contour->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ PRESET_CONTOUR_FORMATTER,
+ pdev->contour->val);
+ }
+ break;
+ case V4L2_CID_BACKLIGHT_COMPENSATION:
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ BACK_LIGHT_COMPENSATION_FORMATTER,
+ ctrl->val ? 0 : 0xff);
+ break;
+ case V4L2_CID_BAND_STOP_FILTER:
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ FLICKERLESS_MODE_FORMATTER,
+ ctrl->val ? 0 : 0xff);
+ break;
+ case PWC_CID_CUSTOM(noise_reduction):
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ DYNAMIC_NOISE_CONTROL_FORMATTER,
+ ctrl->val);
+ break;
+ case PWC_CID_CUSTOM(save_user):
+ ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER);
+ break;
+ case PWC_CID_CUSTOM(restore_user):
+ ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER);
+ break;
+ case PWC_CID_CUSTOM(restore_factory):
+ ret = pwc_button_ctrl(pdev,
+ RESTORE_FACTORY_DEFAULTS_FORMATTER);
+ break;
+ case V4L2_CID_PAN_RELATIVE:
+ ret = pwc_set_motor(pdev);
+ break;
+ default:
+ ret = -EINVAL;
}
- return -EINVAL;
+
+ if (ret)
+ PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret);
+
+leave:
+ mutex_unlock(&pdev->udevlock);
+ mutex_lock(&pdev->modlock);
+ return ret;
}
static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
@@ -667,157 +946,77 @@ static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *
return pwc_vidioc_try_fmt(pdev, f);
}
-static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+static int pwc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *rb)
{
struct pwc_device *pdev = video_drvdata(file);
- return pwc_vidioc_set_fmt(pdev, f);
-}
-
-static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
-{
- int nbuffers;
+ if (pdev->capt_file != NULL &&
+ pdev->capt_file != file)
+ return -EBUSY;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n", rb->count);
- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (rb->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
+ pdev->capt_file = file;
- nbuffers = rb->count;
- if (nbuffers < 2)
- nbuffers = 2;
- else if (nbuffers > pwc_mbufs)
- nbuffers = pwc_mbufs;
- /* Force to use our # of buffers */
- rb->count = pwc_mbufs;
- return 0;
+ return vb2_reqbufs(&pdev->vb_queue, rb);
}
static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{
struct pwc_device *pdev = video_drvdata(file);
- int index;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n", buf->index);
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n");
- return -EINVAL;
- }
- index = buf->index;
- if (index < 0 || index >= pwc_mbufs) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index);
- return -EINVAL;
- }
-
- buf->m.offset = index * pdev->len_per_image;
- if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
- buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
- else
- buf->bytesused = pdev->view.size;
- buf->field = V4L2_FIELD_NONE;
- buf->memory = V4L2_MEMORY_MMAP;
- /*buf->flags = V4L2_BUF_FLAG_MAPPED;*/
- buf->length = pdev->len_per_image;
-
- PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n", buf->index);
- PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n", buf->m.offset);
- PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n", buf->bytesused);
-
- return 0;
+ return vb2_querybuf(&pdev->vb_queue, buf);
}
static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n", buf->index);
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (buf->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
- if (buf->index >= pwc_mbufs)
- return -EINVAL;
+ struct pwc_device *pdev = video_drvdata(file);
- buf->flags |= V4L2_BUF_FLAG_QUEUED;
- buf->flags &= ~V4L2_BUF_FLAG_DONE;
+ if (!pdev->udev)
+ return -ENODEV;
- return 0;
+ if (pdev->capt_file != file)
+ return -EBUSY;
+
+ return vb2_qbuf(&pdev->vb_queue, buf);
}
static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{
- DECLARE_WAITQUEUE(wait, current);
struct pwc_device *pdev = video_drvdata(file);
- int ret;
-
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n");
-
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- add_wait_queue(&pdev->frameq, &wait);
- while (pdev->full_frames == NULL) {
- if (pdev->error_status) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- return -pdev->error_status;
- }
- if (signal_pending(current)) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- return -ERESTARTSYS;
- }
- mutex_unlock(&pdev->modlock);
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- mutex_lock(&pdev->modlock);
- }
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
+ if (!pdev->udev)
+ return -ENODEV;
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n");
- /* Decompress data in pdev->images[pdev->fill_image] */
- ret = pwc_handle_frame(pdev);
- if (ret)
- return -EFAULT;
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
-
- buf->index = pdev->fill_image;
- if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
- buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
- else
- buf->bytesused = pdev->view.size;
- buf->flags = V4L2_BUF_FLAG_MAPPED;
- buf->field = V4L2_FIELD_NONE;
- do_gettimeofday(&buf->timestamp);
- buf->sequence = 0;
- buf->memory = V4L2_MEMORY_MMAP;
- buf->m.offset = pdev->fill_image * pdev->len_per_image;
- buf->length = pdev->len_per_image;
- pwc_next_image(pdev);
-
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n", buf->index);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n", buf->length);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n", buf->m.offset);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n", buf->bytesused);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n");
- return 0;
+ if (pdev->capt_file != file)
+ return -EBUSY;
+ return vb2_dqbuf(&pdev->vb_queue, buf, file->f_flags & O_NONBLOCK);
}
static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
{
struct pwc_device *pdev = video_drvdata(file);
- return pwc_isoc_init(pdev);
+ if (!pdev->udev)
+ return -ENODEV;
+
+ if (pdev->capt_file != file)
+ return -EBUSY;
+
+ return vb2_streamon(&pdev->vb_queue, i);
}
static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
{
struct pwc_device *pdev = video_drvdata(file);
- pwc_isoc_cleanup(pdev);
- return 0;
+ if (!pdev->udev)
+ return -ENODEV;
+
+ if (pdev->capt_file != file)
+ return -EBUSY;
+
+ return vb2_streamoff(&pdev->vb_queue, i);
}
static int pwc_enum_framesizes(struct file *file, void *fh,
@@ -879,6 +1078,14 @@ static int pwc_enum_frameintervals(struct file *file, void *fh,
return 0;
}
+static int pwc_log_status(struct file *file, void *priv)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ v4l2_ctrl_handler_log_status(&pdev->ctrl_handler, PWC_NAME);
+ return 0;
+}
+
static long pwc_default(struct file *file, void *fh, bool valid_prio,
int cmd, void *arg)
{
@@ -896,15 +1103,13 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = {
.vidioc_g_fmt_vid_cap = pwc_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = pwc_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = pwc_try_fmt_vid_cap,
- .vidioc_queryctrl = pwc_queryctrl,
- .vidioc_g_ctrl = pwc_g_ctrl,
- .vidioc_s_ctrl = pwc_s_ctrl,
.vidioc_reqbufs = pwc_reqbufs,
.vidioc_querybuf = pwc_querybuf,
.vidioc_qbuf = pwc_qbuf,
.vidioc_dqbuf = pwc_dqbuf,
.vidioc_streamon = pwc_streamon,
.vidioc_streamoff = pwc_streamoff,
+ .vidioc_log_status = pwc_log_status,
.vidioc_enum_framesizes = pwc_enum_framesizes,
.vidioc_enum_frameintervals = pwc_enum_frameintervals,
.vidioc_default = pwc_default,
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index 083f8b15df73..0e4e2d7b7872 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -29,7 +29,6 @@
#include <linux/usb.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
-#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/slab.h>
@@ -37,19 +36,16 @@
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-vmalloc.h>
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
#include <linux/input.h>
#endif
-#include "pwc-uncompress.h"
#include <media/pwc-ioctl.h>
/* Version block */
-#define PWC_MAJOR 10
-#define PWC_MINOR 0
-#define PWC_EXTRAMINOR 12
-#define PWC_VERSION_CODE KERNEL_VERSION(PWC_MAJOR,PWC_MINOR,PWC_EXTRAMINOR)
-#define PWC_VERSION "10.0.14"
+#define PWC_VERSION "10.0.15"
#define PWC_NAME "pwc"
#define PFX PWC_NAME ": "
@@ -81,9 +77,9 @@
#define PWC_DEBUG_LEVEL (PWC_DEBUG_LEVEL_MODULE)
#define PWC_DEBUG(level, fmt, args...) do {\
- if ((PWC_DEBUG_LEVEL_ ##level) & pwc_trace) \
- printk(KERN_DEBUG PFX fmt, ##args); \
- } while(0)
+ if ((PWC_DEBUG_LEVEL_ ##level) & pwc_trace) \
+ printk(KERN_DEBUG PFX fmt, ##args); \
+ } while (0)
#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args)
#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args)
@@ -110,25 +106,21 @@
#define FEATURE_CODEC1 0x0002
#define FEATURE_CODEC2 0x0004
-/* Turn certain features on/off */
-#define PWC_INT_PIPE 0
-
/* Ignore errors in the first N frames, to allow for startup delays */
#define FRAME_LOWMARK 5
/* Size and number of buffers for the ISO pipe. */
-#define MAX_ISO_BUFS 2
+#define MAX_ISO_BUFS 3
#define ISO_FRAMES_PER_DESC 10
#define ISO_MAX_FRAME_SIZE 960
#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
-/* Frame buffers: contains compressed or uncompressed video data. */
-#define MAX_FRAMES 5
/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */
#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE)
-/* Absolute maximum number of buffers available for mmap() */
-#define MAX_IMAGES 10
+/* Absolute minimum and maximum number of buffers available for mmap() */
+#define MIN_FRAMES 2
+#define MAX_FRAMES 16
/* Some macros to quickly find the type of a webcam */
#define DEVICE_USE_CODEC1(x) ((x)<675)
@@ -136,149 +128,221 @@
#define DEVICE_USE_CODEC3(x) ((x)>=700)
#define DEVICE_USE_CODEC23(x) ((x)>=675)
-/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */
-struct pwc_iso_buf
-{
- void *data;
- int length;
- int read;
- struct urb *urb;
-};
+/* from pwc-dec.h */
+#define PWCX_FLAG_PLANAR 0x0001
+
+/* Request types: video */
+#define SET_LUM_CTL 0x01
+#define GET_LUM_CTL 0x02
+#define SET_CHROM_CTL 0x03
+#define GET_CHROM_CTL 0x04
+#define SET_STATUS_CTL 0x05
+#define GET_STATUS_CTL 0x06
+#define SET_EP_STREAM_CTL 0x07
+#define GET_EP_STREAM_CTL 0x08
+#define GET_XX_CTL 0x09
+#define SET_XX_CTL 0x0A
+#define GET_XY_CTL 0x0B
+#define SET_XY_CTL 0x0C
+#define SET_MPT_CTL 0x0D
+#define GET_MPT_CTL 0x0E
+
+/* Selectors for the Luminance controls [GS]ET_LUM_CTL */
+#define AGC_MODE_FORMATTER 0x2000
+#define PRESET_AGC_FORMATTER 0x2100
+#define SHUTTER_MODE_FORMATTER 0x2200
+#define PRESET_SHUTTER_FORMATTER 0x2300
+#define PRESET_CONTOUR_FORMATTER 0x2400
+#define AUTO_CONTOUR_FORMATTER 0x2500
+#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600
+#define CONTRAST_FORMATTER 0x2700
+#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800
+#define FLICKERLESS_MODE_FORMATTER 0x2900
+#define AE_CONTROL_SPEED 0x2A00
+#define BRIGHTNESS_FORMATTER 0x2B00
+#define GAMMA_FORMATTER 0x2C00
+
+/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */
+#define WB_MODE_FORMATTER 0x1000
+#define AWB_CONTROL_SPEED_FORMATTER 0x1100
+#define AWB_CONTROL_DELAY_FORMATTER 0x1200
+#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300
+#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400
+#define COLOUR_MODE_FORMATTER 0x1500
+#define SATURATION_MODE_FORMATTER1 0x1600
+#define SATURATION_MODE_FORMATTER2 0x1700
+
+/* Selectors for the Status controls [GS]ET_STATUS_CTL */
+#define SAVE_USER_DEFAULTS_FORMATTER 0x0200
+#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300
+#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400
+#define READ_AGC_FORMATTER 0x0500
+#define READ_SHUTTER_FORMATTER 0x0600
+#define READ_RED_GAIN_FORMATTER 0x0700
+#define READ_BLUE_GAIN_FORMATTER 0x0800
+
+/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */
+#define PT_RELATIVE_CONTROL_FORMATTER 0x01
+#define PT_RESET_CONTROL_FORMATTER 0x02
+#define PT_STATUS_FORMATTER 0x03
/* intermediate buffers with raw data from the USB cam */
struct pwc_frame_buf
{
- void *data;
- volatile int filled; /* number of bytes filled */
- struct pwc_frame_buf *next; /* list */
-};
-
-/* additionnal informations used when dealing image between kernel and userland */
-struct pwc_imgbuf
-{
- unsigned long offset; /* offset of this buffer in the big array of image_data */
- int vma_use_count; /* count the number of time this memory is mapped */
+ struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */
+ struct list_head list;
+ void *data;
+ int filled; /* number of bytes filled */
};
struct pwc_device
{
struct video_device vdev;
-
- /* Pointer to our usb_device, may be NULL after unplug */
- struct usb_device *udev;
-
- int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
- int release; /* release number */
- int features; /* feature bits */
- char serial[30]; /* serial number (string) */
- int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */
- int usb_init; /* set when the cam has been initialized over USB */
-
- /*** Video data ***/
- int vopen; /* flag */
- int vendpoint; /* video isoc endpoint */
- int vcinterface; /* video control interface */
- int valternate; /* alternate interface needed */
- int vframes, vsize; /* frames-per-second & size (see PSZ_*) */
- int pixfmt; /* pixelformat: V4L2_PIX_FMT_YUV420 or raw: _PWC1, _PWC2 */
- int vframe_count; /* received frames */
- int vframes_dumped; /* counter for dumped frames */
- int vframes_error; /* frames received in error */
- int vmax_packet_size; /* USB maxpacket size */
- int vlast_packet_size; /* for frame synchronisation */
- int visoc_errors; /* number of contiguous ISOC errors */
- int vcompression; /* desired compression factor */
- int vbandlength; /* compressed band length; 0 is uncompressed */
- char vsnapshot; /* snapshot mode */
- char vsync; /* used by isoc handler */
- char vmirror; /* for ToUCaM series */
- char unplugged;
-
- int cmd_len;
- unsigned char cmd_buf[13];
-
- /* The image acquisition requires 3 to 4 steps:
- 1. data is gathered in short packets from the USB controller
- 2. data is synchronized and packed into a frame buffer
- 3a. in case data is compressed, decompress it directly into image buffer
- 3b. in case data is uncompressed, copy into image buffer with viewport
- 4. data is transferred to the user process
-
- Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES....
- We have in effect a back-to-back-double-buffer system.
- */
- /* 1: isoc */
- struct pwc_iso_buf sbuf[MAX_ISO_BUFS];
- char iso_init;
-
- /* 2: frame */
- struct pwc_frame_buf *fbuf; /* all frames */
- struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */
- struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */
- struct pwc_frame_buf *fill_frame; /* frame currently being filled */
- struct pwc_frame_buf *read_frame; /* frame currently read by user process */
- int frame_header_size, frame_trailer_size;
- int frame_size;
- int frame_total_size; /* including header & trailer */
- int drop_frames;
-
- /* 3: decompression */
- void *decompress_data; /* private data for decompression engine */
-
- /* 4: image */
- /* We have an 'image' and a 'view', where 'image' is the fixed-size image
- as delivered by the camera, and 'view' is the size requested by the
- program. The camera image is centered in this viewport, laced with
- a gray or black border. view_min <= image <= view <= view_max;
- */
- int image_mask; /* bitmask of supported sizes */
- struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */
- struct pwc_coord abs_max; /* maximum supported size with compression */
- struct pwc_coord image, view; /* image and viewport size */
- struct pwc_coord offset; /* offset within the viewport */
-
- void *image_data; /* total buffer, which is subdivided into ... */
- struct pwc_imgbuf images[MAX_IMAGES];/* ...several images... */
- int fill_image; /* ...which are rotated. */
- int len_per_image; /* length per image */
- int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */
- int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */
-
- struct mutex modlock; /* to prevent races in video_open(), etc */
- spinlock_t ptrlock; /* for manipulating the buffer pointers */
-
- /*** motorized pan/tilt feature */
- struct pwc_mpt_range angle_range;
- int pan_angle; /* in degrees * 100 */
- int tilt_angle; /* absolute angle; 0,0 is home position */
- int snapshot_button_status; /* set to 1 when the user push the button, reset to 0 when this value is read */
+ struct mutex modlock;
+
+ /* Pointer to our usb_device, may be NULL after unplug */
+ struct usb_device *udev;
+ /* Protects the setting of udev to NULL by our disconnect handler */
+ struct mutex udevlock;
+
+ /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
+ int type;
+ int release; /* release number */
+ int features; /* feature bits */
+ char serial[30]; /* serial number (string) */
+
+ /*** Video data ***/
+ struct file *capt_file; /* file doing video capture */
+ int vendpoint; /* video isoc endpoint */
+ int vcinterface; /* video control interface */
+ int valternate; /* alternate interface needed */
+ int vframes, vsize; /* frames-per-second & size (see PSZ_*) */
+ int pixfmt; /* pixelformat: V4L2_PIX_FMT_YUV420 or _PWCX */
+ int vframe_count; /* received frames */
+ int vmax_packet_size; /* USB maxpacket size */
+ int vlast_packet_size; /* for frame synchronisation */
+ int visoc_errors; /* number of contiguous ISOC errors */
+ int vcompression; /* desired compression factor */
+ int vbandlength; /* compressed band length; 0 is uncompressed */
+ char vsnapshot; /* snapshot mode */
+ char vsync; /* used by isoc handler */
+ char vmirror; /* for ToUCaM series */
+ char power_save; /* Do powersaving for this cam */
+
+ int cmd_len;
+ unsigned char cmd_buf[13];
+
+ struct urb *urbs[MAX_ISO_BUFS];
+ char iso_init;
+
+ /* videobuf2 queue and queued buffers list */
+ struct vb2_queue vb_queue;
+ struct list_head queued_bufs;
+ spinlock_t queued_bufs_lock;
+
+ /*
+ * Frame currently being filled, this only gets touched by the
+ * isoc urb complete handler, and by stream start / stop since
+ * start / stop touch it before / after starting / killing the urbs
+ * no locking is needed around this
+ */
+ struct pwc_frame_buf *fill_buf;
+
+ int frame_header_size, frame_trailer_size;
+ int frame_size;
+ int frame_total_size; /* including header & trailer */
+ int drop_frames;
+
+ void *decompress_data; /* private data for decompression engine */
+
+ /*
+ * We have an 'image' and a 'view', where 'image' is the fixed-size img
+ * as delivered by the camera, and 'view' is the size requested by the
+ * program. The camera image is centered in this viewport, laced with
+ * a gray or black border. view_min <= image <= view <= view_max;
+ */
+ int image_mask; /* supported sizes */
+ struct pwc_coord view_min, view_max; /* minimum and maximum view */
+ struct pwc_coord abs_max; /* maximum supported size */
+ struct pwc_coord image, view; /* image and viewport size */
+ struct pwc_coord offset; /* offset of the viewport */
+
+ /*** motorized pan/tilt feature */
+ struct pwc_mpt_range angle_range;
+ int pan_angle; /* in degrees * 100 */
+ int tilt_angle; /* absolute angle; 0,0 is home */
+
+ /*
+ * Set to 1 when the user push the button, reset to 0
+ * when this value is read from sysfs.
+ */
+ int snapshot_button_status;
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
- struct input_dev *button_dev; /* webcam snapshot button input */
- char button_phys[64];
+ struct input_dev *button_dev; /* webcam snapshot button input */
+ char button_phys[64];
#endif
- /*** Misc. data ***/
- wait_queue_head_t frameq; /* When waiting for a frame to finish... */
-#if PWC_INT_PIPE
- void *usb_int_handler; /* for the interrupt endpoint */
-#endif
+ /* controls */
+ struct v4l2_ctrl_handler ctrl_handler;
+ u16 saturation_fmt;
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *gamma;
+ struct {
+ /* awb / red-blue balance cluster */
+ struct v4l2_ctrl *auto_white_balance;
+ struct v4l2_ctrl *red_balance;
+ struct v4l2_ctrl *blue_balance;
+ /* usb ctrl transfers are slow, so we cache things */
+ int color_bal_valid;
+ unsigned long last_color_bal_update; /* In jiffies */
+ s32 last_red_balance;
+ s32 last_blue_balance;
+ };
+ struct {
+ /* autogain / gain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ int gain_valid;
+ unsigned long last_gain_update; /* In jiffies */
+ s32 last_gain;
+ };
+ struct {
+ /* exposure_auto / exposure cluster */
+ struct v4l2_ctrl *exposure_auto;
+ struct v4l2_ctrl *exposure;
+ int exposure_valid;
+ unsigned long last_exposure_update; /* In jiffies */
+ s32 last_exposure;
+ };
+ struct v4l2_ctrl *colorfx;
+ struct {
+ /* autocontour/contour cluster */
+ struct v4l2_ctrl *autocontour;
+ struct v4l2_ctrl *contour;
+ };
+ struct v4l2_ctrl *backlight;
+ struct v4l2_ctrl *flicker;
+ struct v4l2_ctrl *noise_reduction;
+ struct v4l2_ctrl *save_user;
+ struct v4l2_ctrl *restore_user;
+ struct v4l2_ctrl *restore_factory;
+ struct {
+ /* motor control cluster */
+ struct v4l2_ctrl *motor_pan;
+ struct v4l2_ctrl *motor_tilt;
+ struct v4l2_ctrl *motor_pan_reset;
+ struct v4l2_ctrl *motor_tilt_reset;
+ };
+ /* CODEC3 models have both gain and exposure controlled by autogain */
+ struct v4l2_ctrl *autogain_expo_cluster[3];
};
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/* Global variables */
#ifdef CONFIG_USB_PWC_DEBUG
extern int pwc_trace;
#endif
-extern int pwc_mbufs;
-
-/** functions in pwc-if.c */
-int pwc_handle_frame(struct pwc_device *pdev);
-void pwc_next_image(struct pwc_device *pdev);
-int pwc_isoc_init(struct pwc_device *pdev);
-void pwc_isoc_cleanup(struct pwc_device *pdev);
/** Functions in pwc-misc.c */
/* sizes in pixels */
@@ -291,50 +355,25 @@ void pwc_construct(struct pwc_device *pdev);
/* Request a certain video mode. Returns < 0 if not possible */
extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot);
extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size);
-/* Calculate the number of bytes per image (not frame) */
extern int pwc_mpt_reset(struct pwc_device *pdev, int flags);
extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt);
-
-/* Various controls; should be obvious. Value 0..65535, or < 0 on error */
-extern int pwc_get_brightness(struct pwc_device *pdev);
-extern int pwc_set_brightness(struct pwc_device *pdev, int value);
-extern int pwc_get_contrast(struct pwc_device *pdev);
-extern int pwc_set_contrast(struct pwc_device *pdev, int value);
-extern int pwc_get_gamma(struct pwc_device *pdev);
-extern int pwc_set_gamma(struct pwc_device *pdev, int value);
-extern int pwc_get_saturation(struct pwc_device *pdev, int *value);
-extern int pwc_set_saturation(struct pwc_device *pdev, int value);
extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value);
extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor);
-extern int pwc_restore_user(struct pwc_device *pdev);
-extern int pwc_save_user(struct pwc_device *pdev);
-extern int pwc_restore_factory(struct pwc_device *pdev);
-
-/* exported for use by v4l2 controls */
-extern int pwc_get_red_gain(struct pwc_device *pdev, int *value);
-extern int pwc_set_red_gain(struct pwc_device *pdev, int value);
-extern int pwc_get_blue_gain(struct pwc_device *pdev, int *value);
-extern int pwc_set_blue_gain(struct pwc_device *pdev, int value);
-extern int pwc_get_awb(struct pwc_device *pdev);
-extern int pwc_set_awb(struct pwc_device *pdev, int mode);
-extern int pwc_set_agc(struct pwc_device *pdev, int mode, int value);
-extern int pwc_get_agc(struct pwc_device *pdev, int *value);
-extern int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value);
-extern int pwc_get_shutter_speed(struct pwc_device *pdev, int *value);
-
-extern int pwc_set_colour_mode(struct pwc_device *pdev, int colour);
-extern int pwc_get_colour_mode(struct pwc_device *pdev, int *colour);
-extern int pwc_set_contour(struct pwc_device *pdev, int contour);
-extern int pwc_get_contour(struct pwc_device *pdev, int *contour);
-extern int pwc_set_backlight(struct pwc_device *pdev, int backlight);
-extern int pwc_get_backlight(struct pwc_device *pdev, int *backlight);
-extern int pwc_set_flicker(struct pwc_device *pdev, int flicker);
-extern int pwc_get_flicker(struct pwc_device *pdev, int *flicker);
-extern int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise);
-extern int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise);
+extern int send_control_msg(struct pwc_device *pdev,
+ u8 request, u16 value, void *buf, int buflen);
+
+/* Control get / set helpers */
+int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data);
+int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data);
+int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data);
+#define pwc_set_s8_ctrl pwc_set_u8_ctrl
+int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *dat);
+int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data);
+int pwc_button_ctrl(struct pwc_device *pdev, u16 value);
+int pwc_init_controls(struct pwc_device *pdev);
/* Power down or up the camera; not supported by all models */
-extern int pwc_camera_power(struct pwc_device *pdev, int power);
+extern void pwc_camera_power(struct pwc_device *pdev, int power);
/* Private ioctl()s; see pwc-ioctl.h */
extern long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg);
@@ -343,12 +382,6 @@ extern const struct v4l2_ioctl_ops pwc_ioctl_ops;
/** pwc-uncompress.c */
/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */
-extern int pwc_decompress(struct pwc_device *pdev);
-
-#ifdef __cplusplus
-}
-#endif
-
+int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf);
#endif
-/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index b42bfa5ccdf2..79fb22c89ae9 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -22,7 +22,6 @@
#include <linux/mm.h>
#include <linux/moduleparam.h>
#include <linux/time.h>
-#include <linux/version.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
@@ -40,7 +39,7 @@
#include <mach/dma.h>
#include <mach/camera.h>
-#define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)
+#define PXA_CAM_VERSION "0.0.6"
#define PXA_CAM_DRV_NAME "pxa27x-camera"
/* Camera Interface */
@@ -215,6 +214,7 @@ struct pxa_camera_dev {
unsigned long ciclk;
unsigned long mclk;
u32 mclk_divisor;
+ u16 width_flags; /* max 10 bits */
struct list_head capture;
@@ -247,7 +247,7 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
if (bytes_per_line < 0)
return bytes_per_line;
- dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
*size = bytes_per_line * icd->user_height;
@@ -262,13 +262,13 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
int i;
BUG_ON(in_interrupt());
- dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
&buf->vb, buf->vb.baddr, buf->vb.bsize);
/*
@@ -429,7 +429,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
struct videobuf_buffer *vb, enum v4l2_field field)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct device *dev = pcdev->soc_host.v4l2_dev.dev;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
@@ -636,11 +636,11 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
- dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n",
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n",
__func__, vb, vb->baddr, vb->bsize, pcdev->active);
list_add_tail(&vb->queue, &pcdev->capture);
@@ -658,7 +658,7 @@ static void pxa_videobuf_release(struct videobuf_queue *vq,
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
#ifdef DEBUG
struct soc_camera_device *icd = vq->priv_data;
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
@@ -843,7 +843,7 @@ static struct videobuf_queue_ops pxa_videobuf_ops = {
static void pxa_camera_init_videobuf(struct videobuf_queue *q,
struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
/*
@@ -972,7 +972,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
*/
static int pxa_camera_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
if (pcdev->icd)
@@ -982,7 +982,7 @@ static int pxa_camera_add_device(struct soc_camera_device *icd)
pcdev->icd = icd;
- dev_info(icd->dev.parent, "PXA Camera driver attached to camera %d\n",
+ dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
icd->devnum);
return 0;
@@ -991,12 +991,12 @@ static int pxa_camera_add_device(struct soc_camera_device *icd)
/* Called with .video_lock held */
static void pxa_camera_remove_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
BUG_ON(icd != pcdev->icd);
- dev_info(icd->dev.parent, "PXA Camera driver detached from camera %d\n",
+ dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
icd->devnum);
/* disable capture, disable interrupts */
@@ -1021,43 +1021,26 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
* quick capture interface supports both.
*/
*flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ?
- SOCAM_MASTER : SOCAM_SLAVE) |
- SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH |
- SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_PCLK_SAMPLE_FALLING;
+ V4L2_MBUS_MASTER : V4L2_MBUS_SLAVE) |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING;
/* If requested data width is supported by the platform, use it */
- switch (buswidth) {
- case 10:
- if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10))
- return -EINVAL;
- *flags |= SOCAM_DATAWIDTH_10;
- break;
- case 9:
- if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9))
- return -EINVAL;
- *flags |= SOCAM_DATAWIDTH_9;
- break;
- case 8:
- if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8))
- return -EINVAL;
- *flags |= SOCAM_DATAWIDTH_8;
- break;
- default:
- return -EINVAL;
- }
+ if ((1 << (buswidth - 1)) & pcdev->width_flags)
+ return 0;
- return 0;
+ return -EINVAL;
}
static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
unsigned long flags, __u32 pixfmt)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
unsigned long dw, bpp;
@@ -1071,12 +1054,12 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
* Datawidth is now guaranteed to be equal to one of the three values.
* We fix bit-per-pixel equal to data-width...
*/
- switch (flags & SOCAM_DATAWIDTH_MASK) {
- case SOCAM_DATAWIDTH_10:
+ switch (icd->current_fmt->host_fmt->bits_per_sample) {
+ case 10:
dw = 4;
bpp = 0x40;
break;
- case SOCAM_DATAWIDTH_9:
+ case 9:
dw = 3;
bpp = 0x20;
break;
@@ -1085,7 +1068,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
* Actually it can only be 8 now,
* default is just to silence compiler warnings
*/
- case SOCAM_DATAWIDTH_8:
+ case 8:
dw = 2;
bpp = 0;
}
@@ -1094,11 +1077,11 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
cicr4 |= CICR4_PCLK_EN;
if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
cicr4 |= CICR4_MCLK_EN;
- if (flags & SOCAM_PCLK_SAMPLE_FALLING)
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
cicr4 |= CICR4_PCP;
- if (flags & SOCAM_HSYNC_ACTIVE_LOW)
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
cicr4 |= CICR4_HSP;
- if (flags & SOCAM_VSYNC_ACTIVE_LOW)
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
cicr4 |= CICR4_VSP;
cicr0 = __raw_readl(pcdev->base + CICR0);
@@ -1152,9 +1135,11 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
- unsigned long bus_flags, camera_flags, common_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
int ret;
struct pxa_cam *cam = icd->host_priv;
@@ -1163,44 +1148,58 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
if (ret < 0)
return ret;
- camera_flags = icd->ops->query_bus_param(icd);
-
- common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
- if (!common_flags)
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = bus_flags;
+ }
pcdev->channels = 1;
/* Make choises, based on platform preferences */
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
if (pcdev->platform_flags & PXA_CAMERA_HSP)
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
if (pcdev->platform_flags & PXA_CAMERA_VSP)
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (pcdev->platform_flags & PXA_CAMERA_PCP)
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
}
- cam->flags = common_flags;
-
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
return ret;
+ }
+
+ cam->flags = common_flags;
pxa_camera_setup_cicr(icd, common_flags, pixfmt);
@@ -1210,17 +1209,31 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
unsigned char buswidth)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
- unsigned long bus_flags, camera_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
int ret = test_platform_param(pcdev, buswidth, &bus_flags);
if (ret < 0)
return ret;
- camera_flags = icd->ops->query_bus_param(icd);
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret == -ENOIOCTLCMD) {
+ ret = 0;
+ }
- return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL;
+ return ret;
}
static const struct soc_mbus_pixelfmt pxa_camera_formats[] = {
@@ -1247,7 +1260,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id
struct soc_camera_format_xlate *xlate)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
int formats = 0, ret;
struct pxa_cam *cam;
enum v4l2_mbus_pixelcode code;
@@ -1335,9 +1348,9 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd,
struct v4l2_crop *a)
{
struct v4l2_rect *rect = &a->c;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
struct pxa_camera_dev *pcdev = ici->priv;
- struct device *dev = icd->dev.parent;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_sense sense = {
.master_clock = pcdev->mclk,
@@ -1379,7 +1392,7 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd,
return ret;
if (pxa_camera_check_frame(mf.width, mf.height)) {
- dev_warn(icd->dev.parent,
+ dev_warn(icd->parent,
"Inconsistent state. Use S_FMT to repair\n");
return -EINVAL;
}
@@ -1406,9 +1419,9 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd,
static int pxa_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
struct pxa_camera_dev *pcdev = ici->priv;
- struct device *dev = icd->dev.parent;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate = NULL;
struct soc_camera_sense sense = {
@@ -1485,7 +1498,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
- dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
return -EINVAL;
}
@@ -1499,16 +1512,11 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
&pix->height, 32, 2048, 0,
pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0);
- pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
- xlate->host_fmt);
- if (pix->bytesperline < 0)
- return pix->bytesperline;
- pix->sizeimage = pix->height * pix->bytesperline;
-
/* limit to sensor capabilities */
mf.width = pix->width;
mf.height = pix->height;
- mf.field = pix->field;
+ /* Only progressive video supported so far */
+ mf.field = V4L2_FIELD_NONE;
mf.colorspace = pix->colorspace;
mf.code = xlate->code;
@@ -1527,7 +1535,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
break;
default:
/* TODO: support interlaced at least in pass-through mode */
- dev_err(icd->dev.parent, "Field type %d unsupported.\n",
+ dev_err(icd->parent, "Field type %d unsupported.\n",
mf.field);
return -EINVAL;
}
@@ -1578,15 +1586,14 @@ static int pxa_camera_querycap(struct soc_camera_host *ici,
{
/* cap->name is set by the firendly caller:-> */
strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card));
- cap->version = PXA_CAM_VERSION_CODE;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
}
-static int pxa_camera_suspend(struct soc_camera_device *icd, pm_message_t state)
+static int pxa_camera_suspend(struct device *dev)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
struct pxa_camera_dev *pcdev = ici->priv;
int i = 0, ret = 0;
@@ -1596,15 +1603,19 @@ static int pxa_camera_suspend(struct soc_camera_device *icd, pm_message_t state)
pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
- if ((pcdev->icd) && (pcdev->icd->ops->suspend))
- ret = pcdev->icd->ops->suspend(pcdev->icd, state);
+ if (pcdev->icd) {
+ struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+ ret = v4l2_subdev_call(sd, core, s_power, 0);
+ if (ret == -ENOIOCTLCMD)
+ ret = 0;
+ }
return ret;
}
-static int pxa_camera_resume(struct soc_camera_device *icd)
+static int pxa_camera_resume(struct device *dev)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
struct pxa_camera_dev *pcdev = ici->priv;
int i = 0, ret = 0;
@@ -1618,8 +1629,12 @@ static int pxa_camera_resume(struct soc_camera_device *icd)
__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3);
__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
- if ((pcdev->icd) && (pcdev->icd->ops->resume))
- ret = pcdev->icd->ops->resume(pcdev->icd);
+ if (pcdev->icd) {
+ struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret == -ENOIOCTLCMD)
+ ret = 0;
+ }
/* Restart frame capture if active buffer exists */
if (!ret && pcdev->active)
@@ -1632,8 +1647,6 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
.owner = THIS_MODULE,
.add = pxa_camera_add_device,
.remove = pxa_camera_remove_device,
- .suspend = pxa_camera_suspend,
- .resume = pxa_camera_resume,
.set_crop = pxa_camera_set_crop,
.get_formats = pxa_camera_get_formats,
.put_formats = pxa_camera_put_formats,
@@ -1688,6 +1701,12 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev)
"data widths, using default 10 bit\n");
pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
}
+ if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)
+ pcdev->width_flags = 1 << 7;
+ if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9)
+ pcdev->width_flags |= 1 << 8;
+ if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10)
+ pcdev->width_flags |= 1 << 9;
pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
if (!pcdev->mclk) {
dev_warn(&pdev->dev,
@@ -1818,9 +1837,15 @@ static int __devexit pxa_camera_remove(struct platform_device *pdev)
return 0;
}
+static struct dev_pm_ops pxa_camera_pm = {
+ .suspend = pxa_camera_suspend,
+ .resume = pxa_camera_resume,
+};
+
static struct platform_driver pxa_camera_driver = {
.driver = {
.name = PXA_CAM_DRV_NAME,
+ .pm = &pxa_camera_pm,
},
.probe = pxa_camera_probe,
.remove = __devexit_p(pxa_camera_remove),
@@ -1843,4 +1868,5 @@ module_exit(pxa_camera_exit);
MODULE_DESCRIPTION("PXA27x SoC Camera Host driver");
MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
MODULE_LICENSE("GPL");
+MODULE_VERSION(PXA_CAM_VERSION);
MODULE_ALIAS("platform:" PXA_CAM_DRV_NAME);
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
index 57e11b6f19fb..9937386a3bae 100644
--- a/drivers/media/video/rj54n1cb0c.c
+++ b/drivers/media/video/rj54n1cb0c.c
@@ -11,13 +11,15 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
+#include <linux/module.h>
#include <media/rj54n1cb0c.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#define RJ54N1_DEV_CODE 0x0400
#define RJ54N1_DEV_CODE2 0x0401
@@ -148,6 +150,7 @@ struct rj54n1_clock_div {
struct rj54n1 {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
struct rj54n1_clock_div clk_div;
const struct rj54n1_datafmt *fmt;
struct v4l2_rect rect; /* Sensor window */
@@ -499,31 +502,6 @@ static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80);
}
-static int rj54n1_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
-
- if (flags & SOCAM_PCLK_SAMPLE_RISING)
- return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
- else
- return reg_write(client, RJ54N1_OUT_SIGPO, 0);
-}
-
-static unsigned long rj54n1_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- const unsigned long flags =
- SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_MASTER | SOCAM_DATAWIDTH_8 |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
static int rj54n1_set_rect(struct i2c_client *client,
u16 reg_x, u16 reg_y, u16 reg_xy,
u32 width, u32 height)
@@ -1202,134 +1180,51 @@ static int rj54n1_s_register(struct v4l2_subdev *sd,
}
#endif
-static const struct v4l2_queryctrl rj54n1_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 66,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto white balance",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
-};
-
-static struct soc_camera_ops rj54n1_ops = {
- .set_bus_param = rj54n1_set_bus_param,
- .query_bus_param = rj54n1_query_bus_param,
- .controls = rj54n1_controls,
- .num_controls = ARRAY_SIZE(rj54n1_controls),
-};
-
-static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl);
+ struct v4l2_subdev *sd = &rj54n1->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- int data;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
- if (data < 0)
- return -EIO;
- ctrl->value = !(data & 1);
- break;
- case V4L2_CID_HFLIP:
- data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
- if (data < 0)
- return -EIO;
- ctrl->value = !(data & 2);
- break;
- case V4L2_CID_GAIN:
- data = reg_read(client, RJ54N1_Y_GAIN);
- if (data < 0)
- return -EIO;
-
- ctrl->value = data / 2;
- break;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ctrl->value = rj54n1->auto_wb;
- break;
- }
-
- return 0;
-}
-
-static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
int data;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- const struct v4l2_queryctrl *qctrl;
-
- qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id);
- if (!qctrl)
- return -EINVAL;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1);
else
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1);
if (data < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_HFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2);
else
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2);
if (data < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_GAIN:
- if (ctrl->value > qctrl->maximum ||
- ctrl->value < qctrl->minimum)
- return -EINVAL;
- else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0)
+ if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_AUTO_WHITE_BALANCE:
/* Auto WB area - whole image */
- if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->value << 7,
+ if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7,
0x80) < 0)
return -EIO;
- rj54n1->auto_wb = ctrl->value;
- break;
+ rj54n1->auto_wb = ctrl->val;
+ return 0;
}
- return 0;
+ return -EINVAL;
}
+static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = {
+ .s_ctrl = rj54n1_s_ctrl,
+};
+
static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
- .g_ctrl = rj54n1_g_ctrl,
- .s_ctrl = rj54n1_s_ctrl,
.g_chip_ident = rj54n1_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = rj54n1_g_register,
@@ -1337,6 +1232,36 @@ static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
#endif
};
+static int rj54n1_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags =
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int rj54n1_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
+ if (soc_camera_apply_board_flags(icl, cfg) &
+ V4L2_MBUS_PCLK_SAMPLE_RISING)
+ return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
+ else
+ return reg_write(client, RJ54N1_OUT_SIGPO, 0);
+}
+
static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
.s_stream = rj54n1_s_stream,
.s_mbus_fmt = rj54n1_s_fmt,
@@ -1346,6 +1271,8 @@ static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
.g_crop = rj54n1_g_crop,
.s_crop = rj54n1_s_crop,
.cropcap = rj54n1_cropcap,
+ .g_mbus_config = rj54n1_g_mbus_config,
+ .s_mbus_config = rj54n1_s_mbus_config,
};
static struct v4l2_subdev_ops rj54n1_subdev_ops = {
@@ -1357,18 +1284,12 @@ static struct v4l2_subdev_ops rj54n1_subdev_ops = {
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
-static int rj54n1_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client,
+static int rj54n1_video_probe(struct i2c_client *client,
struct rj54n1_pdata *priv)
{
int data1, data2;
int ret;
- /* This could be a BUG_ON() or a WARN_ON(), or remove it completely */
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
- return -ENODEV;
-
/* Read out the chip version register */
data1 = reg_read(client, RJ54N1_DEV_CODE);
data2 = reg_read(client, RJ54N1_DEV_CODE2);
@@ -1396,18 +1317,11 @@ static int rj54n1_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct rj54n1 *rj54n1;
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
struct rj54n1_pdata *rj54n1_priv;
int ret;
- if (!icd) {
- dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl || !icl->priv) {
dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
return -EINVAL;
@@ -1426,8 +1340,22 @@ static int rj54n1_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops);
+ v4l2_ctrl_handler_init(&rj54n1->hdl, 4);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 66);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ rj54n1->subdev.ctrl_handler = &rj54n1->hdl;
+ if (rj54n1->hdl.error) {
+ int err = rj54n1->hdl.error;
- icd->ops = &rj54n1_ops;
+ kfree(rj54n1);
+ return err;
+ }
rj54n1->clk_div = clk_div;
rj54n1->rect.left = RJ54N1_COLUMN_SKIP;
@@ -1441,25 +1369,24 @@ static int rj54n1_probe(struct i2c_client *client,
rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
(clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
- ret = rj54n1_video_probe(icd, client, rj54n1_priv);
+ ret = rj54n1_video_probe(client, rj54n1_priv);
if (ret < 0) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&rj54n1->hdl);
kfree(rj54n1);
return ret;
}
-
- return ret;
+ return v4l2_ctrl_handler_setup(&rj54n1->hdl);
}
static int rj54n1_remove(struct i2c_client *client)
{
struct rj54n1 *rj54n1 = to_rj54n1(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- icd->ops = NULL;
+ v4l2_device_unregister_subdev(&rj54n1->subdev);
if (icl->free_bus)
icl->free_bus(icl);
+ v4l2_ctrl_handler_free(&rj54n1->hdl);
kfree(rj54n1);
return 0;
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
index 5b9dce85645c..803c9c82e496 100644
--- a/drivers/media/video/s2255drv.c
+++ b/drivers/media/video/s2255drv.c
@@ -16,15 +16,10 @@
* Example maximum bandwidth utilization:
*
* -full size, color mode YUYV or YUV422P: 2 channels at once
- *
* -full or half size Grey scale: all 4 channels at once
- *
* -half size, color mode YUYV or YUV422P: all 4 channels at once
- *
* -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
* at once.
- * (TODO: Incorporate videodev2 frame rate(FR) enumeration,
- * which is currently experimental.)
*
* 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
@@ -47,7 +42,6 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
-#include <linux/version.h>
#include <linux/mm.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-common.h>
@@ -56,12 +50,7 @@
#include <linux/vmalloc.h>
#include <linux/usb.h>
-#define S2255_MAJOR_VERSION 1
-#define S2255_MINOR_VERSION 21
-#define S2255_RELEASE 0
-#define S2255_VERSION KERNEL_VERSION(S2255_MAJOR_VERSION, \
- S2255_MINOR_VERSION, \
- S2255_RELEASE)
+#define S2255_VERSION "1.22.1"
#define FIRMWARE_FILE_NAME "f2255usb.bin"
/* default JPEG quality */
@@ -126,7 +115,7 @@
#define MASK_COLOR 0x000000ff
#define MASK_JPG_QUALITY 0x0000ff00
#define MASK_INPUT_TYPE 0x000f0000
-/* frame decimation. Not implemented by V4L yet(experimental in V4L) */
+/* frame decimation. */
#define FDEC_1 1 /* capture every frame. default */
#define FDEC_2 2 /* capture every 2nd frame */
#define FDEC_3 3 /* capture every 3rd frame */
@@ -312,9 +301,9 @@ struct s2255_fh {
};
/* current cypress EEPROM firmware version */
-#define S2255_CUR_USB_FWVER ((3 << 8) | 11)
+#define S2255_CUR_USB_FWVER ((3 << 8) | 12)
/* current DSP FW version */
-#define S2255_CUR_DSP_FWVER 10102
+#define S2255_CUR_DSP_FWVER 10104
/* Need DSP version 5+ for video status feature */
#define S2255_MIN_DSP_STATUS 5
#define S2255_MIN_DSP_COLORFILTER 8
@@ -502,7 +491,7 @@ static void planar422p_to_yuv_packed(const unsigned char *in,
static void s2255_reset_dsppower(struct s2255_dev *dev)
{
- s2255_vendor_req(dev, 0x40, 0x0b0b, 0x0b01, NULL, 0, 1);
+ s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
msleep(10);
s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
msleep(600);
@@ -856,7 +845,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->driver, "s2255", sizeof(cap->driver));
strlcpy(cap->card, "s2255", sizeof(cap->card));
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->version = S2255_VERSION;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
}
@@ -1984,9 +1972,8 @@ static int s2255_probe_v4l(struct s2255_dev *dev)
video_device_node_name(&channel->vdev));
}
- printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %d.%d\n",
- S2255_MAJOR_VERSION,
- S2255_MINOR_VERSION);
+ printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n",
+ S2255_VERSION);
/* if no channels registered, return error and probe will fail*/
if (atomic_read(&dev->num_channels) == 0) {
v4l2_device_unregister(&dev->v4l2_dev);
@@ -2302,15 +2289,12 @@ static int s2255_board_init(struct s2255_dev *dev)
/* query the firmware */
fw_ver = s2255_get_fx2fw(dev);
- printk(KERN_INFO "2255 usb firmware version %d.%d\n",
+ printk(KERN_INFO "s2255: usb firmware version %d.%d\n",
(fw_ver >> 8) & 0xff,
fw_ver & 0xff);
if (fw_ver < S2255_CUR_USB_FWVER)
- dev_err(&dev->udev->dev,
- "usb firmware not up to date %d.%d\n",
- (fw_ver >> 8) & 0xff,
- fw_ver & 0xff);
+ printk(KERN_INFO "s2255: newer USB firmware available\n");
for (j = 0; j < MAX_CHANNELS; j++) {
struct s2255_channel *channel = &dev->channel[j];
@@ -2721,3 +2705,4 @@ module_exit(usb_s2255_exit);
MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
MODULE_LICENSE("GPL");
+MODULE_VERSION(S2255_VERSION);
diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c
new file mode 100644
index 000000000000..0df7f2a41814
--- /dev/null
+++ b/drivers/media/video/s5k6aa.c
@@ -0,0 +1,1681 @@
+/*
+ * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor
+ * with embedded SoC ISP.
+ *
+ * Copyright (C) 2011, Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * Based on a driver authored by Dongsoo Nathaniel Kim.
+ * Copyright (C) 2009, Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.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/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/media.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s5k6aa.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define DRIVER_NAME "S5K6AA"
+
+/* The token to indicate array termination */
+#define S5K6AA_TERM 0xffff
+#define S5K6AA_OUT_WIDTH_DEF 640
+#define S5K6AA_OUT_HEIGHT_DEF 480
+#define S5K6AA_WIN_WIDTH_MAX 1280
+#define S5K6AA_WIN_HEIGHT_MAX 1024
+#define S5K6AA_WIN_WIDTH_MIN 8
+#define S5K6AA_WIN_HEIGHT_MIN 8
+
+/*
+ * H/W register Interface (0xD0000000 - 0xD0000FFF)
+ */
+#define AHB_MSB_ADDR_PTR 0xfcfc
+#define GEN_REG_OFFSH 0xd000
+#define REG_CMDWR_ADDRH 0x0028
+#define REG_CMDWR_ADDRL 0x002a
+#define REG_CMDRD_ADDRH 0x002c
+#define REG_CMDRD_ADDRL 0x002e
+#define REG_CMDBUF0_ADDR 0x0f12
+#define REG_CMDBUF1_ADDR 0x0f10
+
+/*
+ * Host S/W Register interface (0x70000000 - 0x70002000)
+ * The value of the two most significant address bytes is 0x7000,
+ * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs.
+ */
+#define HOST_SWIF_OFFSH 0x7000
+
+/* Initialization parameters */
+/* Master clock frequency in KHz */
+#define REG_I_INCLK_FREQ_L 0x01b8
+#define REG_I_INCLK_FREQ_H 0x01ba
+#define MIN_MCLK_FREQ_KHZ 6000U
+#define MAX_MCLK_FREQ_KHZ 27000U
+#define REG_I_USE_NPVI_CLOCKS 0x01c6
+#define REG_I_USE_NMIPI_CLOCKS 0x01c8
+
+/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */
+#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc)
+#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce)
+#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0)
+#define SYS_PLL_OUT_FREQ (48000000 / 4000)
+#define PCLK_FREQ_MIN (24000000 / 4000)
+#define PCLK_FREQ_MAX (48000000 / 4000)
+#define REG_I_INIT_PARAMS_UPDATED 0x01e0
+#define REG_I_ERROR_INFO 0x01e2
+
+/* General purpose parameters */
+#define REG_USER_BRIGHTNESS 0x01e4
+#define REG_USER_CONTRAST 0x01e6
+#define REG_USER_SATURATION 0x01e8
+#define REG_USER_SHARPBLUR 0x01ea
+
+#define REG_G_SPEC_EFFECTS 0x01ee
+#define REG_G_ENABLE_PREV 0x01f0
+#define REG_G_ENABLE_PREV_CHG 0x01f2
+#define REG_G_NEW_CFG_SYNC 0x01f8
+#define REG_G_PREVZOOM_IN_WIDTH 0x020a
+#define REG_G_PREVZOOM_IN_HEIGHT 0x020c
+#define REG_G_PREVZOOM_IN_XOFFS 0x020e
+#define REG_G_PREVZOOM_IN_YOFFS 0x0210
+#define REG_G_INPUTS_CHANGE_REQ 0x021a
+#define REG_G_ACTIVE_PREV_CFG 0x021c
+#define REG_G_PREV_CFG_CHG 0x021e
+#define REG_G_PREV_OPEN_AFTER_CH 0x0220
+#define REG_G_PREV_CFG_ERROR 0x0222
+
+/* Preview control section. n = 0...4. */
+#define PREG(n, x) ((n) * 0x26 + x)
+#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242)
+#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244)
+#define REG_P_FMT(n) PREG(n, 0x0246)
+#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248)
+#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a)
+#define REG_P_PVI_MASK(n) PREG(n, 0x024c)
+#define REG_P_CLK_INDEX(n) PREG(n, 0x024e)
+#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250)
+#define FR_RATE_DYNAMIC 0
+#define FR_RATE_FIXED 1
+#define FR_RATE_FIXED_ACCURATE 2
+#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252)
+#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */
+#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */
+/* Frame period in 0.1 ms units */
+#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254)
+#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256)
+/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */
+#define US_TO_FR_TIME(__t) ((__t) / 100)
+#define S5K6AA_MIN_FR_TIME 33300 /* us */
+#define S5K6AA_MAX_FR_TIME 650000 /* us */
+#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */
+/* The below 5 registers are for "device correction" values */
+#define REG_P_COLORTEMP(n) PREG(n, 0x025e)
+#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262)
+
+/* Extended image property controls */
+/* Exposure time in 10 us units */
+#define REG_SF_USR_EXPOSURE_L 0x03c6
+#define REG_SF_USR_EXPOSURE_H 0x03c8
+#define REG_SF_USR_EXPOSURE_CHG 0x03ca
+#define REG_SF_USR_TOT_GAIN 0x03cc
+#define REG_SF_USR_TOT_GAIN_CHG 0x03ce
+#define REG_SF_RGAIN 0x03d0
+#define REG_SF_RGAIN_CHG 0x03d2
+#define REG_SF_GGAIN 0x03d4
+#define REG_SF_GGAIN_CHG 0x03d6
+#define REG_SF_BGAIN 0x03d8
+#define REG_SF_BGAIN_CHG 0x03da
+#define REG_SF_FLICKER_QUANT 0x03dc
+#define REG_SF_FLICKER_QUANT_CHG 0x03de
+
+/* Output interface (parallel/MIPI) setup */
+#define REG_OIF_EN_MIPI_LANES 0x03fa
+#define REG_OIF_EN_PACKETS 0x03fc
+#define REG_OIF_CFG_CHG 0x03fe
+
+/* Auto-algorithms enable mask */
+#define REG_DBG_AUTOALG_EN 0x0400
+#define AALG_ALL_EN_MASK (1 << 0)
+#define AALG_AE_EN_MASK (1 << 1)
+#define AALG_DIVLEI_EN_MASK (1 << 2)
+#define AALG_WB_EN_MASK (1 << 3)
+#define AALG_FLICKER_EN_MASK (1 << 5)
+#define AALG_FIT_EN_MASK (1 << 6)
+#define AALG_WRHW_EN_MASK (1 << 7)
+
+/* Firmware revision information */
+#define REG_FW_APIVER 0x012e
+#define S5K6AAFX_FW_APIVER 0x0001
+#define REG_FW_REVISION 0x0130
+
+/* For now we use only one user configuration register set */
+#define S5K6AA_MAX_PRESETS 1
+
+static const char * const s5k6aa_supply_names[] = {
+ "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */
+ "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */
+ "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V)
+ or 2.8V (2.6V to 3.0) */
+ "vddio", /* I/O supply 1.8V (1.65V to 1.95V)
+ or 2.8V (2.5V to 3.1V) */
+};
+#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names)
+
+enum s5k6aa_gpio_id {
+ STBY,
+ RST,
+ GPIO_NUM,
+};
+
+struct s5k6aa_regval {
+ u16 addr;
+ u16 val;
+};
+
+struct s5k6aa_pixfmt {
+ enum v4l2_mbus_pixelcode code;
+ u32 colorspace;
+ /* REG_P_FMT(x) register value */
+ u16 reg_p_fmt;
+};
+
+struct s5k6aa_preset {
+ /* output pixel format and resolution */
+ struct v4l2_mbus_framefmt mbus_fmt;
+ u8 clk_id;
+ u8 index;
+};
+
+struct s5k6aa_ctrls {
+ struct v4l2_ctrl_handler handler;
+ /* Auto / manual white balance cluster */
+ struct v4l2_ctrl *awb;
+ struct v4l2_ctrl *gain_red;
+ struct v4l2_ctrl *gain_blue;
+ struct v4l2_ctrl *gain_green;
+ /* Mirror cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ /* Auto exposure / manual exposure and gain cluster */
+ struct v4l2_ctrl *auto_exp;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+};
+
+struct s5k6aa_interval {
+ u16 reg_fr_time;
+ struct v4l2_fract interval;
+ /* Maximum rectangle for the interval */
+ struct v4l2_frmsize_discrete size;
+};
+
+struct s5k6aa {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ enum v4l2_mbus_type bus_type;
+ u8 mipi_lanes;
+
+ int (*s_power)(int enable);
+ struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES];
+ struct s5k6aa_gpio gpio[GPIO_NUM];
+
+ /* external master clock frequency */
+ unsigned long mclk_frequency;
+ /* ISP internal master clock frequency */
+ u16 clk_fop;
+ /* output pixel clock frequency range */
+ u16 pclk_fmin;
+ u16 pclk_fmax;
+
+ unsigned int inv_hflip:1;
+ unsigned int inv_vflip:1;
+
+ /* protects the struct members below */
+ struct mutex lock;
+
+ /* sensor matrix scan window */
+ struct v4l2_rect ccd_rect;
+
+ struct s5k6aa_ctrls ctrls;
+ struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS];
+ struct s5k6aa_preset *preset;
+ const struct s5k6aa_interval *fiv;
+
+ unsigned int streaming:1;
+ unsigned int apply_cfg:1;
+ unsigned int apply_crop:1;
+ unsigned int power;
+};
+
+static struct s5k6aa_regval s5k6aa_analog_config[] = {
+ /* Analog settings */
+ { 0x112a, 0x0000 }, { 0x1132, 0x0000 },
+ { 0x113e, 0x0000 }, { 0x115c, 0x0000 },
+ { 0x1164, 0x0000 }, { 0x1174, 0x0000 },
+ { 0x1178, 0x0000 }, { 0x077a, 0x0000 },
+ { 0x077c, 0x0000 }, { 0x077e, 0x0000 },
+ { 0x0780, 0x0000 }, { 0x0782, 0x0000 },
+ { 0x0784, 0x0000 }, { 0x0786, 0x0000 },
+ { 0x0788, 0x0000 }, { 0x07a2, 0x0000 },
+ { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 },
+ { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 },
+ { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 },
+ { 0x07bc, 0x0004 }, { 0x07be, 0x0005 },
+ { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 },
+};
+
+/* TODO: Add RGB888 and Bayer format */
+static const struct s5k6aa_pixfmt s5k6aa_formats[] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
+ /* range 16-240 */
+ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 },
+ { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 },
+};
+
+static const struct s5k6aa_interval s5k6aa_intervals[] = {
+ { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */
+ { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */
+ { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */
+ { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */
+ { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */
+};
+
+#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd;
+}
+
+static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s5k6aa, sd);
+}
+
+/* Set initial values for all preview presets */
+static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa)
+{
+ struct s5k6aa_preset *preset = &s5k6aa->presets[0];
+ int i;
+
+ for (i = 0; i < S5K6AA_MAX_PRESETS; i++) {
+ preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF;
+ preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF;
+ preset->mbus_fmt.code = s5k6aa_formats[0].code;
+ preset->index = i;
+ preset->clk_id = 0;
+ preset++;
+ }
+
+ s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX];
+ s5k6aa->preset = &s5k6aa->presets[0];
+}
+
+static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+ u8 wbuf[2] = {addr >> 8, addr & 0xFF};
+ struct i2c_msg msg[2];
+ u8 rbuf[2];
+ int ret;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = wbuf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = rbuf;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ *val = be16_to_cpu(*((u16 *)rbuf));
+
+ v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
+
+ return ret == 2 ? 0 : ret;
+}
+
+static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val)
+{
+ u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF};
+
+ int ret = i2c_master_send(client, buf, 4);
+ v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val);
+
+ return ret == 4 ? 0 : ret;
+}
+
+/* The command register write, assumes Command_Wr_addH = 0x7000. */
+static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val)
+{
+ int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val);
+}
+
+/* The command register read, assumes Command_Rd_addH = 0x7000. */
+static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+ int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val);
+}
+
+static int s5k6aa_write_array(struct v4l2_subdev *sd,
+ const struct s5k6aa_regval *msg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 addr_incr = 0;
+ int ret = 0;
+
+ while (msg->addr != S5K6AA_TERM) {
+ if (addr_incr != 2)
+ ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL,
+ msg->addr);
+ if (ret)
+ break;
+ ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val);
+ if (ret)
+ break;
+ /* Assume that msg->addr is always less than 0xfffc */
+ addr_incr = (msg + 1)->addr - msg->addr;
+ msg++;
+ }
+
+ return ret;
+}
+
+/* Configure the AHB high address bytes for GTG registers access */
+static int s5k6aa_set_ahb_address(struct i2c_client *client)
+{
+ int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH);
+ if (ret)
+ return ret;
+ ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH);
+}
+
+/**
+ * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration
+ *
+ * Configure the internal ISP PLL for the required output frequency.
+ * Locking: called with s5k6aa.lock mutex held.
+ */
+static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ unsigned long fmclk = s5k6aa->mclk_frequency / 1000;
+ u16 status;
+ int ret;
+
+ if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ,
+ "Invalid clock frequency: %ld\n", fmclk))
+ return -EINVAL;
+
+ s5k6aa->pclk_fmin = PCLK_FREQ_MIN;
+ s5k6aa->pclk_fmax = PCLK_FREQ_MAX;
+ s5k6aa->clk_fop = SYS_PLL_OUT_FREQ;
+
+ /* External input clock frequency in kHz */
+ ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1);
+ /* Internal PLL frequency */
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0),
+ s5k6aa->pclk_fmin);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0),
+ s5k6aa->pclk_fmax);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1);
+ if (!ret)
+ ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status);
+
+ return ret ? ret : (status ? -EINVAL : 0);
+}
+
+/* Set horizontal and vertical image flipping */
+static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int index = s5k6aa->preset->index;
+
+ unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip;
+ unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1);
+
+ return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip);
+}
+
+/* Configure auto/manual white balance and R/G/B gains */
+static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
+ u16 reg;
+
+ int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &reg);
+
+ if (!ret && !awb) {
+ ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1);
+ if (ret)
+ return ret;
+
+ ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1);
+ if (ret)
+ return ret;
+
+ ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1);
+ }
+ if (!ret) {
+ reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK;
+ ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg);
+ }
+
+ return ret;
+}
+
+/* Program FW with exposure time, 'exposure' in us units */
+static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure)
+{
+ unsigned int time = exposure / 10;
+
+ int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1);
+}
+
+static int s5k6aa_set_user_gain(struct i2c_client *client, int gain)
+{
+ int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1);
+}
+
+/* Set auto/manual exposure and total gain */
+static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ unsigned int exp_time = s5k6aa->ctrls.exposure->val;
+ u16 auto_alg;
+
+ int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg);
+ if (ret)
+ return ret;
+
+ v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n",
+ exp_time, value, auto_alg);
+
+ if (value == V4L2_EXPOSURE_AUTO) {
+ auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK;
+ } else {
+ ret = s5k6aa_set_user_exposure(c, exp_time);
+ if (ret)
+ return ret;
+ ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val);
+ if (ret)
+ return ret;
+ auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK);
+ }
+
+ return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg);
+}
+
+static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 auto_alg;
+ int ret;
+
+ ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg);
+ if (ret)
+ return ret;
+
+ if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) {
+ auto_alg |= AALG_FLICKER_EN_MASK;
+ } else {
+ auto_alg &= ~AALG_FLICKER_EN_MASK;
+ /* The V4L2_CID_LINE_FREQUENCY control values match
+ * the register values */
+ ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value);
+ if (ret)
+ return ret;
+ ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1);
+ if (ret)
+ return ret;
+ }
+
+ return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg);
+}
+
+static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ static const struct v4l2_control colorfx[] = {
+ { V4L2_COLORFX_NONE, 0 },
+ { V4L2_COLORFX_BW, 1 },
+ { V4L2_COLORFX_NEGATIVE, 2 },
+ { V4L2_COLORFX_SEPIA, 3 },
+ { V4L2_COLORFX_SKY_BLUE, 4 },
+ { V4L2_COLORFX_SKETCH, 5 },
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(colorfx); i++) {
+ if (colorfx[i].id == val)
+ return s5k6aa_write(client, REG_G_SPEC_EFFECTS,
+ colorfx[i].value);
+ }
+ return -EINVAL;
+}
+
+static int s5k6aa_preview_config_status(struct i2c_client *client)
+{
+ u16 error = 0;
+ int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error);
+
+ v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret);
+ return ret ? ret : (error ? -EINVAL : 0);
+}
+
+static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa,
+ struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++)
+ if (mf->colorspace == s5k6aa_formats[i].colorspace &&
+ mf->code == s5k6aa_formats[i].code)
+ return i;
+ return 0;
+}
+
+static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa,
+ struct s5k6aa_preset *preset)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt);
+ int ret;
+
+ ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index),
+ preset->mbus_fmt.width);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index),
+ preset->mbus_fmt.height);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FMT(preset->index),
+ s5k6aa_formats[fmt_index].reg_p_fmt);
+ return ret;
+}
+
+static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ struct v4l2_rect *r = &s5k6aa->ccd_rect;
+ int ret;
+
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1);
+ if (!ret)
+ s5k6aa->apply_crop = 0;
+
+ return ret;
+}
+
+/**
+ * s5k6aa_configure_video_bus - configure the video output interface
+ * @bus_type: video bus type: parallel or MIPI-CSI
+ * @nlanes: number of MIPI lanes to be used (MIPI-CSI only)
+ *
+ * Note: Only parallel bus operation has been tested.
+ */
+static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa,
+ enum v4l2_mbus_type bus_type, int nlanes)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 cfg = 0;
+ int ret;
+
+ /*
+ * TODO: The sensor is supposed to support BT.601 and BT.656
+ * but there is nothing indicating how to switch between both
+ * in the datasheet. For now default BT.601 interface is assumed.
+ */
+ if (bus_type == V4L2_MBUS_CSI2)
+ cfg = nlanes;
+ else if (bus_type != V4L2_MBUS_PARALLEL)
+ return -EINVAL;
+
+ ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_OIF_CFG_CHG, 1);
+}
+
+/* This function should be called when switching to new user configuration set*/
+static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout,
+ int cid)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(timeout);
+ u16 reg = 1;
+ int ret;
+
+ ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1);
+ if (timeout == 0)
+ return ret;
+
+ while (ret >= 0 && time_is_after_jiffies(end)) {
+ ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, &reg);
+ if (!reg)
+ return 0;
+ usleep_range(1000, 5000);
+ }
+ return ret ? ret : -ETIMEDOUT;
+}
+
+/**
+ * s5k6aa_set_prev_config - write user preview register set
+ *
+ * Configure output resolution and color fromat, pixel clock
+ * frequency range, device frame rate type and frame period range.
+ */
+static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa,
+ struct s5k6aa_preset *preset)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int idx = preset->index;
+ u16 frame_rate_q;
+ int ret;
+
+ if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME)
+ frame_rate_q = FR_RATE_Q_BEST_FRRATE;
+ else
+ frame_rate_q = FR_RATE_Q_BEST_QUALITY;
+
+ ret = s5k6aa_set_output_framefmt(s5k6aa, preset);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx),
+ s5k6aa->pclk_fmax);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx),
+ s5k6aa->pclk_fmin);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx),
+ preset->clk_id);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx),
+ FR_RATE_DYNAMIC);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx),
+ frame_rate_q);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx),
+ s5k6aa->fiv->reg_fr_time + 33);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx),
+ s5k6aa->fiv->reg_fr_time - 33);
+ if (!ret)
+ ret = s5k6aa_new_config_sync(client, 250, idx);
+ if (!ret)
+ ret = s5k6aa_preview_config_status(client);
+ if (!ret)
+ s5k6aa->apply_cfg = 0;
+
+ v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n",
+ s5k6aa->fiv->reg_fr_time, ret);
+ return ret;
+}
+
+/**
+ * s5k6aa_initialize_isp - basic ISP MCU initialization
+ *
+ * Configure AHB addresses for registers read/write; configure PLLs for
+ * required output pixel clock. The ISP power supply needs to be already
+ * enabled, with an optional H/W reset.
+ * Locking: called with s5k6aa.lock mutex held.
+ */
+static int s5k6aa_initialize_isp(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ s5k6aa->apply_crop = 1;
+ s5k6aa->apply_cfg = 1;
+ msleep(100);
+
+ ret = s5k6aa_set_ahb_address(client);
+ if (ret)
+ return ret;
+ ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type,
+ s5k6aa->mipi_lanes);
+ if (ret)
+ return ret;
+ ret = s5k6aa_write_array(sd, s5k6aa_analog_config);
+ if (ret)
+ return ret;
+ msleep(20);
+
+ return s5k6aa_configure_pixel_clocks(s5k6aa);
+}
+
+static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val)
+{
+ if (!gpio_is_valid(priv->gpio[id].gpio))
+ return 0;
+ gpio_set_value(priv->gpio[id].gpio, !!val);
+ return 1;
+}
+
+static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id)
+{
+ return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level);
+}
+
+static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id)
+{
+ return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level);
+}
+
+static int __s5k6aa_power_on(struct s5k6aa *s5k6aa)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+ if (ret)
+ return ret;
+ if (s5k6aa_gpio_deassert(s5k6aa, STBY))
+ usleep_range(150, 200);
+
+ if (s5k6aa->s_power)
+ ret = s5k6aa->s_power(1);
+ usleep_range(4000, 4000);
+
+ if (s5k6aa_gpio_deassert(s5k6aa, RST))
+ msleep(20);
+
+ return ret;
+}
+
+static int __s5k6aa_power_off(struct s5k6aa *s5k6aa)
+{
+ int ret;
+
+ if (s5k6aa_gpio_assert(s5k6aa, RST))
+ usleep_range(100, 150);
+
+ if (s5k6aa->s_power) {
+ ret = s5k6aa->s_power(0);
+ if (ret)
+ return ret;
+ }
+ if (s5k6aa_gpio_assert(s5k6aa, STBY))
+ usleep_range(50, 100);
+ s5k6aa->streaming = 0;
+
+ return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+}
+
+/*
+ * V4L2 subdev core and video operations
+ */
+static int s5k6aa_set_power(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+
+ if (!on == s5k6aa->power) {
+ if (on) {
+ ret = __s5k6aa_power_on(s5k6aa);
+ if (!ret)
+ ret = s5k6aa_initialize_isp(sd);
+ } else {
+ ret = __s5k6aa_power_off(s5k6aa);
+ }
+
+ if (!ret)
+ s5k6aa->power += on ? 1 : -1;
+ }
+
+ mutex_unlock(&s5k6aa->lock);
+
+ if (!on || ret || s5k6aa->power != 1)
+ return ret;
+
+ return v4l2_ctrl_handler_setup(sd->ctrl_handler);
+}
+
+static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int ret = 0;
+
+ ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1);
+ if (!ret)
+ s5k6aa->streaming = enable;
+
+ return ret;
+}
+
+static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+
+ if (s5k6aa->streaming == !on) {
+ if (!ret && s5k6aa->apply_cfg)
+ ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset);
+ if (s5k6aa->apply_crop)
+ ret = s5k6aa_set_input_params(s5k6aa);
+ if (!ret)
+ ret = __s5k6aa_stream(s5k6aa, !!on);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+
+ mutex_lock(&s5k6aa->lock);
+ fi->interval = s5k6aa->fiv->interval;
+ mutex_unlock(&s5k6aa->lock);
+
+ return 0;
+}
+
+static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt;
+ const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0];
+ unsigned int err, min_err = UINT_MAX;
+ unsigned int i, fr_time;
+
+ if (fi->interval.denominator == 0)
+ return -EINVAL;
+
+ fr_time = fi->interval.numerator * 10000 / fi->interval.denominator;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) {
+ const struct s5k6aa_interval *iv = &s5k6aa_intervals[i];
+
+ if (mbus_fmt->width > iv->size.width ||
+ mbus_fmt->height > iv->size.height)
+ continue;
+
+ err = abs(iv->reg_fr_time - fr_time);
+ if (err < min_err) {
+ fiv = iv;
+ min_err = err;
+ }
+ }
+ s5k6aa->fiv = fiv;
+
+ v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n",
+ fiv->reg_fr_time * 100);
+ return 0;
+}
+
+static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n",
+ fi->interval.numerator, fi->interval.denominator);
+
+ mutex_lock(&s5k6aa->lock);
+ ret = __s5k6aa_set_frame_interval(s5k6aa, fi);
+ s5k6aa->apply_cfg = 1;
+
+ mutex_unlock(&s5k6aa->lock);
+ return ret;
+}
+
+/*
+ * V4L2 subdev pad level and video operations
+ */
+static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ const struct s5k6aa_interval *fi;
+ int ret = 0;
+
+ if (fie->index > ARRAY_SIZE(s5k6aa_intervals))
+ return -EINVAL;
+
+ v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &fie->height, S5K6AA_WIN_HEIGHT_MIN,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ mutex_lock(&s5k6aa->lock);
+ fi = &s5k6aa_intervals[fie->index];
+ if (fie->width > fi->size.width || fie->height > fi->size.height)
+ ret = -EINVAL;
+ else
+ fie->interval = fi->interval;
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5k6aa_formats))
+ return -EINVAL;
+
+ code->code = s5k6aa_formats[code->index].code;
+ return 0;
+}
+
+static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ int i = ARRAY_SIZE(s5k6aa_formats);
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ while (--i)
+ if (fse->code == s5k6aa_formats[i].code)
+ break;
+
+ fse->code = s5k6aa_formats[i].code;
+ fse->min_width = S5K6AA_WIN_WIDTH_MIN;
+ fse->max_width = S5K6AA_WIN_WIDTH_MAX;
+ fse->max_height = S5K6AA_WIN_HEIGHT_MIN;
+ fse->min_height = S5K6AA_WIN_HEIGHT_MAX;
+
+ return 0;
+}
+
+static struct v4l2_rect *
+__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ return &s5k6aa->ccd_rect;
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, 0);
+
+ return NULL;
+}
+
+static void s5k6aa_try_format(struct s5k6aa *s5k6aa,
+ struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int index;
+
+ v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &mf->height, S5K6AA_WIN_HEIGHT_MIN,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ if (mf->colorspace != V4L2_COLORSPACE_JPEG &&
+ mf->colorspace != V4L2_COLORSPACE_REC709)
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ index = s5k6aa_get_pixfmt_index(s5k6aa, mf);
+
+ mf->colorspace = s5k6aa_formats[index].colorspace;
+ mf->code = s5k6aa_formats[index].code;
+ mf->field = V4L2_FIELD_NONE;
+}
+
+static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ memset(fmt->reserved, 0, sizeof(fmt->reserved));
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ fmt->format = *mf;
+ return 0;
+ }
+
+ mutex_lock(&s5k6aa->lock);
+ fmt->format = s5k6aa->preset->mbus_fmt;
+ mutex_unlock(&s5k6aa->lock);
+
+ return 0;
+}
+
+static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct s5k6aa_preset *preset = s5k6aa->preset;
+ struct v4l2_mbus_framefmt *mf;
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+ s5k6aa_try_format(s5k6aa, &fmt->format);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ } else {
+ if (s5k6aa->streaming) {
+ ret = -EBUSY;
+ } else {
+ mf = &preset->mbus_fmt;
+ crop = &s5k6aa->ccd_rect;
+ s5k6aa->apply_cfg = 1;
+ }
+ }
+
+ if (ret == 0) {
+ struct v4l2_subdev_frame_interval fiv = {
+ .interval = {0, 1}
+ };
+
+ *mf = fmt->format;
+ /*
+ * Make sure the crop window is valid, i.e. its size is
+ * greater than the output window, as the ISP supports
+ * only down-scaling.
+ */
+ crop->width = clamp_t(unsigned int, crop->width, mf->width,
+ S5K6AA_WIN_WIDTH_MAX);
+ crop->height = clamp_t(unsigned int, crop->height, mf->height,
+ S5K6AA_WIN_HEIGHT_MAX);
+ crop->left = clamp_t(unsigned int, crop->left, 0,
+ S5K6AA_WIN_WIDTH_MAX - crop->width);
+ crop->top = clamp_t(unsigned int, crop->top, 0,
+ S5K6AA_WIN_HEIGHT_MAX - crop->height);
+
+ /* Reset to minimum possible frame interval */
+ ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_rect *rect;
+
+ memset(crop->reserved, 0, sizeof(crop->reserved));
+ mutex_lock(&s5k6aa->lock);
+
+ rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+ if (rect)
+ crop->rect = *rect;
+
+ mutex_unlock(&s5k6aa->lock);
+
+ v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n",
+ rect->left, rect->top, rect->width, rect->height);
+
+ return 0;
+}
+
+static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_mbus_framefmt *mf;
+ unsigned int max_x, max_y;
+ struct v4l2_rect *crop_r;
+
+ mutex_lock(&s5k6aa->lock);
+ crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ mf = &s5k6aa->preset->mbus_fmt;
+ s5k6aa->apply_crop = 1;
+ } else {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ }
+ v4l_bound_align_image(&crop->rect.width, mf->width,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &crop->rect.height, mf->height,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1;
+ max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1;
+
+ crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x);
+ crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y);
+
+ *crop_r = crop->rect;
+
+ mutex_unlock(&s5k6aa->lock);
+
+ v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n",
+ crop_r->left, crop_r->top, crop_r->width, crop_r->height);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = {
+ .enum_mbus_code = s5k6aa_enum_mbus_code,
+ .enum_frame_size = s5k6aa_enum_frame_size,
+ .enum_frame_interval = s5k6aa_enum_frame_interval,
+ .get_fmt = s5k6aa_get_fmt,
+ .set_fmt = s5k6aa_set_fmt,
+ .get_crop = s5k6aa_get_crop,
+ .set_crop = s5k6aa_set_crop,
+};
+
+static const struct v4l2_subdev_video_ops s5k6aa_video_ops = {
+ .g_frame_interval = s5k6aa_g_frame_interval,
+ .s_frame_interval = s5k6aa_s_frame_interval,
+ .s_stream = s5k6aa_s_stream,
+};
+
+/*
+ * V4L2 subdev controls
+ */
+
+static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int idx, err = 0;
+
+ v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val);
+
+ mutex_lock(&s5k6aa->lock);
+ /*
+ * If the device is not powered up by the host driver do
+ * not apply any controls to H/W at this time. Instead
+ * the controls will be restored right after power-up.
+ */
+ if (s5k6aa->power == 0)
+ goto unlock;
+ idx = s5k6aa->preset->index;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ err = s5k6aa_set_awb(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_BRIGHTNESS:
+ err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val);
+ break;
+
+ case V4L2_CID_COLORFX:
+ err = s5k6aa_set_colorfx(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val);
+ break;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_HFLIP:
+ err = s5k6aa_set_mirror(s5k6aa, ctrl->val);
+ if (err)
+ break;
+ err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ break;
+
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_SATURATION:
+ err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val);
+ break;
+
+ case V4L2_CID_SHARPNESS:
+ err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val);
+ break;
+
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+ err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val);
+ if (err)
+ break;
+ err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ break;
+ }
+unlock:
+ mutex_unlock(&s5k6aa->lock);
+ return err;
+}
+
+static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = {
+ .s_ctrl = s5k6aa_s_ctrl,
+};
+
+static int s5k6aa_log_status(struct v4l2_subdev *sd)
+{
+ v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name);
+ return 0;
+}
+
+#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001)
+#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002)
+#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003)
+
+static const struct v4l2_ctrl_config s5k6aa_ctrls[] = {
+ {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_RED_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Red",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ }, {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_GREEN_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Green",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ }, {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_BLUE_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Blue",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ },
+};
+
+static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa)
+{
+ const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops;
+ struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+
+ int ret = v4l2_ctrl_handler_init(hdl, 16);
+ if (ret)
+ return ret;
+ /* Auto white balance cluster */
+ ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 1);
+ ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL);
+ ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL);
+ ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL);
+ v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false);
+
+ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &ctrls->hflip);
+
+ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+ /* Exposure time: x 1 us */
+ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+ 0, 6000000U, 1, 100000U);
+ /* Total gain: 256 <=> 1x */
+ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
+ 0, 256, 1, 256);
+ v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false);
+
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX,
+ V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ 0, 256, 1, 0);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0);
+
+ if (hdl->error) {
+ ret = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+ }
+
+ s5k6aa->sd.ctrl_handler = hdl;
+ return 0;
+}
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+ struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0);
+
+ format->colorspace = s5k6aa_formats[0].colorspace;
+ format->code = s5k6aa_formats[0].code;
+ format->width = S5K6AA_OUT_WIDTH_DEF;
+ format->height = S5K6AA_OUT_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+
+ crop->width = S5K6AA_WIN_WIDTH_MAX;
+ crop->height = S5K6AA_WIN_HEIGHT_MAX;
+ crop->left = 0;
+ crop->top = 0;
+
+ return 0;
+}
+
+int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 api_ver = 0, fw_rev = 0;
+
+ int ret = s5k6aa_set_ahb_address(client);
+
+ if (!ret)
+ ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver);
+ if (!ret)
+ ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev);
+ if (ret) {
+ v4l2_err(&s5k6aa->sd, "FW revision check failed!\n");
+ return ret;
+ }
+
+ v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n",
+ api_ver, fw_rev);
+
+ return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV;
+}
+
+static int s5k6aa_registered(struct v4l2_subdev *sd)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ mutex_lock(&s5k6aa->lock);
+ ret = __s5k6aa_power_on(s5k6aa);
+ if (!ret) {
+ msleep(100);
+ ret = s5k6aa_check_fw_revision(s5k6aa);
+ __s5k6aa_power_off(s5k6aa);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = {
+ .registered = s5k6aa_registered,
+ .open = s5k6aa_open,
+};
+
+static const struct v4l2_subdev_core_ops s5k6aa_core_ops = {
+ .s_power = s5k6aa_set_power,
+ .log_status = s5k6aa_log_status,
+};
+
+static const struct v4l2_subdev_ops s5k6aa_subdev_ops = {
+ .core = &s5k6aa_core_ops,
+ .pad = &s5k6aa_pad_ops,
+ .video = &s5k6aa_video_ops,
+};
+
+/*
+ * GPIO setup
+ */
+static int s5k6aa_configure_gpio(int nr, int val, const char *name)
+{
+ unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ int ret;
+
+ if (!gpio_is_valid(nr))
+ return 0;
+ ret = gpio_request_one(nr, flags, name);
+ if (!ret)
+ gpio_export(nr, 0);
+ return ret;
+}
+
+static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) {
+ if (!gpio_is_valid(s5k6aa->gpio[i].gpio))
+ continue;
+ gpio_free(s5k6aa->gpio[i].gpio);
+ s5k6aa->gpio[i].gpio = -EINVAL;
+ }
+}
+
+static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa,
+ const struct s5k6aa_platform_data *pdata)
+{
+ const struct s5k6aa_gpio *gpio = &pdata->gpio_stby;
+ int ret;
+
+ s5k6aa->gpio[STBY].gpio = -EINVAL;
+ s5k6aa->gpio[RST].gpio = -EINVAL;
+
+ ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY");
+ if (ret) {
+ s5k6aa_free_gpios(s5k6aa);
+ return ret;
+ }
+ s5k6aa->gpio[STBY] = *gpio;
+ if (gpio_is_valid(gpio->gpio))
+ gpio_set_value(gpio->gpio, 0);
+
+ gpio = &pdata->gpio_reset;
+ ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST");
+ if (ret) {
+ s5k6aa_free_gpios(s5k6aa);
+ return ret;
+ }
+ s5k6aa->gpio[RST] = *gpio;
+ if (gpio_is_valid(gpio->gpio))
+ gpio_set_value(gpio->gpio, 0);
+
+ return 0;
+}
+
+static int s5k6aa_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct s5k6aa_platform_data *pdata = client->dev.platform_data;
+ struct v4l2_subdev *sd;
+ struct s5k6aa *s5k6aa;
+ int i, ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
+
+ if (pdata->mclk_frequency == 0) {
+ dev_err(&client->dev, "MCLK frequency not specified\n");
+ return -EINVAL;
+ }
+
+ s5k6aa = kzalloc(sizeof(*s5k6aa), GFP_KERNEL);
+ if (!s5k6aa)
+ return -ENOMEM;
+
+ mutex_init(&s5k6aa->lock);
+
+ s5k6aa->mclk_frequency = pdata->mclk_frequency;
+ s5k6aa->bus_type = pdata->bus_type;
+ s5k6aa->mipi_lanes = pdata->nlanes;
+ s5k6aa->s_power = pdata->set_power;
+ s5k6aa->inv_hflip = pdata->horiz_flip;
+ s5k6aa->inv_vflip = pdata->vert_flip;
+
+ sd = &s5k6aa->sd;
+ strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
+ v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops);
+
+ sd->internal_ops = &s5k6aa_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0);
+ if (ret)
+ goto out_err1;
+
+ ret = s5k6aa_configure_gpios(s5k6aa, pdata);
+ if (ret)
+ goto out_err2;
+
+ for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++)
+ s5k6aa->supplies[i].supply = s5k6aa_supply_names[i];
+
+ ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES,
+ s5k6aa->supplies);
+ if (ret) {
+ dev_err(&client->dev, "Failed to get regulators\n");
+ goto out_err3;
+ }
+
+ ret = s5k6aa_initialize_ctrls(s5k6aa);
+ if (ret)
+ goto out_err4;
+
+ s5k6aa_presets_data_init(s5k6aa);
+
+ s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX;
+ s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX;
+ s5k6aa->ccd_rect.left = 0;
+ s5k6aa->ccd_rect.top = 0;
+
+ return 0;
+
+out_err4:
+ regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+out_err3:
+ s5k6aa_free_gpios(s5k6aa);
+out_err2:
+ media_entity_cleanup(&s5k6aa->sd.entity);
+out_err1:
+ kfree(s5k6aa);
+ return ret;
+}
+
+static int s5k6aa_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ media_entity_cleanup(&sd->entity);
+ regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+ s5k6aa_free_gpios(s5k6aa);
+ kfree(s5k6aa);
+
+ return 0;
+}
+
+static const struct i2c_device_id s5k6aa_id[] = {
+ { DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, s5k6aa_id);
+
+
+static struct i2c_driver s5k6aa_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME
+ },
+ .probe = s5k6aa_probe,
+ .remove = s5k6aa_remove,
+ .id_table = s5k6aa_id,
+};
+
+static int __init s5k6aa_init(void)
+{
+ return i2c_add_driver(&s5k6aa_i2c_driver);
+}
+
+static void __exit s5k6aa_exit(void)
+{
+ i2c_del_driver(&s5k6aa_i2c_driver);
+}
+
+module_init(s5k6aa_init);
+module_exit(s5k6aa_exit);
+
+MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile
index df6954ab1d99..33dec7f890e7 100644
--- a/drivers/media/video/s5p-fimc/Makefile
+++ b/drivers/media/video/s5p-fimc/Makefile
@@ -1,4 +1,4 @@
-s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o
+s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o fimc-mdevice.o
s5p-csis-objs := mipi-csis.o
obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index 81b4a826ee5e..c8d91b0cd9bd 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -11,17 +11,14 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/bug.h>
#include <linux/interrupt.h>
#include <linux/device.h>
-#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/list.h>
#include <linux/slab.h>
-#include <linux/clk.h>
-#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
@@ -30,135 +27,88 @@
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
+#include "fimc-mdevice.h"
#include "fimc-core.h"
-static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *isp_info)
+static int fimc_init_capture(struct fimc_dev *fimc)
{
- struct i2c_adapter *i2c_adap;
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct v4l2_subdev *sd = NULL;
-
- i2c_adap = i2c_get_adapter(isp_info->i2c_bus_num);
- if (!i2c_adap)
- return ERR_PTR(-ENOMEM);
-
- sd = v4l2_i2c_new_subdev_board(&vid_cap->v4l2_dev, i2c_adap,
- isp_info->board_info, NULL);
- if (!sd) {
- v4l2_err(&vid_cap->v4l2_dev, "failed to acquire subdev\n");
- return NULL;
- }
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_sensor_info *sensor;
+ unsigned long flags;
+ int ret = 0;
- v4l2_info(&vid_cap->v4l2_dev, "subdevice %s registered successfuly\n",
- isp_info->board_info->type);
+ if (fimc->pipeline.sensor == NULL || ctx == NULL)
+ return -ENXIO;
+ if (ctx->s_frame.fmt == NULL)
+ return -EINVAL;
- return sd;
-}
+ sensor = v4l2_get_subdev_hostdata(fimc->pipeline.sensor);
-static void fimc_subdev_unregister(struct fimc_dev *fimc)
-{
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct i2c_client *client;
+ spin_lock_irqsave(&fimc->slock, flags);
+ fimc_prepare_dma_offset(ctx, &ctx->d_frame);
+ fimc_set_yuv_order(ctx);
- if (vid_cap->input_index < 0)
- return; /* Subdevice already released or not registered. */
+ fimc_hw_set_camera_polarity(fimc, sensor->pdata);
+ fimc_hw_set_camera_type(fimc, sensor->pdata);
+ fimc_hw_set_camera_source(fimc, sensor->pdata);
+ fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
- if (vid_cap->sd) {
- v4l2_device_unregister_subdev(vid_cap->sd);
- client = v4l2_get_subdevdata(vid_cap->sd);
- i2c_unregister_device(client);
- i2c_put_adapter(client->adapter);
- vid_cap->sd = NULL;
+ ret = fimc_set_scaler_info(ctx);
+ if (!ret) {
+ fimc_hw_set_input_path(ctx);
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_hw_set_effect(ctx, false);
+ fimc_hw_set_output_path(ctx);
+ fimc_hw_set_out_dma(ctx);
+ clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
}
-
- vid_cap->input_index = -1;
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return ret;
}
-/**
- * fimc_subdev_attach - attach v4l2_subdev to camera host interface
- *
- * @fimc: FIMC device information
- * @index: index to the array of available subdevices,
- * -1 for full array search or non negative value
- * to select specific subdevice
- */
-static int fimc_subdev_attach(struct fimc_dev *fimc, int index)
+static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
{
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct s5p_platform_fimc *pdata = fimc->pdata;
- struct s5p_fimc_isp_info *isp_info;
- struct v4l2_subdev *sd;
- int i;
-
- for (i = 0; i < pdata->num_clients; ++i) {
- isp_info = &pdata->isp_info[i];
+ struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *buf;
+ unsigned long flags;
+ bool streaming;
- if (index >= 0 && i != index)
- continue;
+ spin_lock_irqsave(&fimc->slock, flags);
+ streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM);
- sd = fimc_subdev_register(fimc, isp_info);
- if (!IS_ERR_OR_NULL(sd)) {
- vid_cap->sd = sd;
- vid_cap->input_index = i;
+ fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT |
+ 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM);
+ if (!suspend)
+ fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED);
- return 0;
- }
+ /* Release unused buffers */
+ while (!suspend && !list_empty(&cap->pending_buf_q)) {
+ buf = fimc_pending_queue_pop(cap);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
+ /* If suspending put unused buffers onto pending queue */
+ while (!list_empty(&cap->active_buf_q)) {
+ buf = fimc_active_queue_pop(cap);
+ if (suspend)
+ fimc_pending_queue_add(cap, buf);
+ else
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ set_bit(ST_CAPT_SUSPENDED, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
- vid_cap->input_index = -1;
- vid_cap->sd = NULL;
- v4l2_err(&vid_cap->v4l2_dev, "fimc%d: sensor attach failed\n",
- fimc->id);
- return -ENODEV;
-}
-
-static int fimc_isp_subdev_init(struct fimc_dev *fimc, unsigned int index)
-{
- struct s5p_fimc_isp_info *isp_info;
- struct s5p_platform_fimc *pdata = fimc->pdata;
- int ret;
-
- if (index >= pdata->num_clients)
- return -EINVAL;
-
- isp_info = &pdata->isp_info[index];
-
- if (isp_info->clk_frequency)
- clk_set_rate(fimc->clock[CLK_CAM], isp_info->clk_frequency);
-
- ret = clk_enable(fimc->clock[CLK_CAM]);
- if (ret)
- return ret;
-
- ret = fimc_subdev_attach(fimc, index);
- if (ret)
- return ret;
-
- ret = fimc_hw_set_camera_polarity(fimc, isp_info);
- if (ret)
- return ret;
-
- ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 1);
- if (!ret)
- return ret;
-
- /* enabling power failed so unregister subdev */
- fimc_subdev_unregister(fimc);
-
- v4l2_err(&fimc->vid_cap.v4l2_dev, "ISP initialization failed: %d\n",
- ret);
-
- return ret;
+ if (streaming)
+ return fimc_pipeline_s_stream(fimc, 0);
+ else
+ return 0;
}
-static int fimc_stop_capture(struct fimc_dev *fimc)
+static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend)
{
unsigned long flags;
- struct fimc_vid_cap *cap;
- struct fimc_vid_buffer *buf;
-
- cap = &fimc->vid_cap;
if (!fimc_capture_active(fimc))
return 0;
@@ -170,81 +120,73 @@ static int fimc_stop_capture(struct fimc_dev *fimc)
wait_event_timeout(fimc->irq_queue,
!test_bit(ST_CAPT_SHUT, &fimc->state),
- FIMC_SHUTDOWN_TIMEOUT);
-
- v4l2_subdev_call(cap->sd, video, s_stream, 0);
+ (2*HZ/10)); /* 200 ms */
- spin_lock_irqsave(&fimc->slock, flags);
- fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND |
- 1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM);
+ return fimc_capture_state_cleanup(fimc, suspend);
+}
- fimc->vid_cap.active_buf_cnt = 0;
+/**
+ * fimc_capture_config_update - apply the camera interface configuration
+ *
+ * To be called from within the interrupt handler with fimc.slock
+ * spinlock held. It updates the camera pixel crop, rotation and
+ * image flip in H/W.
+ */
+int fimc_capture_config_update(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
- /* Release buffers that were enqueued in the driver by videobuf2. */
- while (!list_empty(&cap->pending_buf_q)) {
- buf = pending_queue_pop(cap);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- }
+ if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state))
+ return 0;
- while (!list_empty(&cap->active_buf_q)) {
- buf = active_queue_pop(cap);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ spin_lock(&ctx->slock);
+ fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+ ret = fimc_set_scaler_info(ctx);
+ if (ret == 0) {
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_prepare_dma_offset(ctx, &ctx->d_frame);
+ fimc_hw_set_out_dma(ctx);
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
}
-
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- dbg("state: 0x%lx", fimc->state);
- return 0;
+ spin_unlock(&ctx->slock);
+ return ret;
}
-static int start_streaming(struct vb2_queue *q)
+static int start_streaming(struct vb2_queue *q, unsigned int count)
{
struct fimc_ctx *ctx = q->drv_priv;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct s5p_fimc_isp_info *isp_info;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ int min_bufs;
int ret;
fimc_hw_reset(fimc);
+ vid_cap->frame_count = 0;
- ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1);
- if (ret && ret != -ENOIOCTLCMD)
- return ret;
-
- ret = fimc_prepare_config(ctx, ctx->state);
+ ret = fimc_init_capture(fimc);
if (ret)
- return ret;
-
- isp_info = &fimc->pdata->isp_info[fimc->vid_cap.input_index];
- fimc_hw_set_camera_type(fimc, isp_info);
- fimc_hw_set_camera_source(fimc, isp_info);
- fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+ goto error;
- if (ctx->state & FIMC_PARAMS) {
- ret = fimc_set_scaler_info(ctx);
- if (ret) {
- err("Scaler setup error");
- return ret;
- }
- fimc_hw_set_input_path(ctx);
- fimc_hw_set_prescaler(ctx);
- fimc_hw_set_mainscaler(ctx);
- fimc_hw_set_target_format(ctx);
- fimc_hw_set_rotation(ctx);
- fimc_hw_set_effect(ctx);
- }
+ set_bit(ST_CAPT_PEND, &fimc->state);
- fimc_hw_set_output_path(ctx);
- fimc_hw_set_out_dma(ctx);
+ min_bufs = fimc->vid_cap.reqbufs_count > 1 ? 2 : 1;
- INIT_LIST_HEAD(&fimc->vid_cap.pending_buf_q);
- INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
- fimc->vid_cap.active_buf_cnt = 0;
- fimc->vid_cap.frame_count = 0;
- fimc->vid_cap.buf_index = 0;
+ if (vid_cap->active_buf_cnt >= min_bufs &&
+ !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
+ fimc_activate_capture(ctx);
- set_bit(ST_CAPT_PEND, &fimc->state);
+ if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ fimc_pipeline_s_stream(fimc, 1);
+ }
return 0;
+error:
+ fimc_capture_state_cleanup(fimc, false);
+ return ret;
}
static int stop_streaming(struct vb2_queue *q)
@@ -255,7 +197,46 @@ static int stop_streaming(struct vb2_queue *q)
if (!fimc_capture_active(fimc))
return -EINVAL;
- return fimc_stop_capture(fimc);
+ return fimc_stop_capture(fimc, false);
+}
+
+int fimc_capture_suspend(struct fimc_dev *fimc)
+{
+ bool suspend = fimc_capture_busy(fimc);
+
+ int ret = fimc_stop_capture(fimc, suspend);
+ if (ret)
+ return ret;
+ return fimc_pipeline_shutdown(fimc);
+}
+
+static void buffer_queue(struct vb2_buffer *vb);
+
+int fimc_capture_resume(struct fimc_dev *fimc)
+{
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *buf;
+ int i;
+
+ if (!test_and_clear_bit(ST_CAPT_SUSPENDED, &fimc->state))
+ return 0;
+
+ INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
+ vid_cap->buf_index = 0;
+ fimc_pipeline_initialize(fimc, &fimc->vid_cap.vfd->entity,
+ false);
+ fimc_init_capture(fimc);
+
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+
+ for (i = 0; i < vid_cap->reqbufs_count; i++) {
+ if (list_empty(&vid_cap->pending_buf_q))
+ break;
+ buf = fimc_pending_queue_pop(vid_cap);
+ buffer_queue(&buf->vb);
+ }
+ return 0;
+
}
static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane)
@@ -265,9 +246,9 @@ static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane)
return fr->f_width * fr->f_height * fr->fmt->depth[plane] / 8;
}
-static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
- unsigned int *num_planes, unsigned long sizes[],
- void *allocators[])
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
{
struct fimc_ctx *ctx = vq->drv_priv;
struct fimc_fmt *fmt = ctx->d_frame.fmt;
@@ -290,21 +271,20 @@ static int buffer_prepare(struct vb2_buffer *vb)
{
struct vb2_queue *vq = vb->vb2_queue;
struct fimc_ctx *ctx = vq->drv_priv;
- struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
int i;
- if (!ctx->d_frame.fmt || vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ if (ctx->d_frame.fmt == NULL)
return -EINVAL;
for (i = 0; i < ctx->d_frame.fmt->memplanes; i++) {
- unsigned long size = get_plane_size(&ctx->d_frame, i);
+ unsigned long size = ctx->d_frame.payload[i];
if (vb2_plane_size(vb, i) < size) {
- v4l2_err(v4l2_dev, "User buffer too small (%ld < %ld)\n",
+ v4l2_err(ctx->fimc_dev->vid_cap.vfd,
+ "User buffer too small (%ld < %ld)\n",
vb2_plane_size(vb, i), size);
return -EINVAL;
}
-
vb2_set_plane_payload(vb, i, size);
}
@@ -313,10 +293,10 @@ static int buffer_prepare(struct vb2_buffer *vb)
static void buffer_queue(struct vb2_buffer *vb)
{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_vid_buffer *buf
= container_of(vb, struct fimc_vid_buffer, vb);
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
unsigned long flags;
int min_bufs;
@@ -324,15 +304,16 @@ static void buffer_queue(struct vb2_buffer *vb)
spin_lock_irqsave(&fimc->slock, flags);
fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr);
- if (!test_bit(ST_CAPT_STREAM, &fimc->state)
- && vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
+ if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) &&
+ !test_bit(ST_CAPT_STREAM, &fimc->state) &&
+ vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
/* Setup the buffer directly for processing. */
int buf_id = (vid_cap->reqbufs_count == 1) ? -1 :
vid_cap->buf_index;
fimc_hw_set_output_addr(fimc, &buf->paddr, buf_id);
buf->index = vid_cap->buf_index;
- active_queue_add(vid_cap, buf);
+ fimc_active_queue_add(vid_cap, buf);
if (++vid_cap->buf_index >= FIMC_MAX_OUT_BUFS)
vid_cap->buf_index = 0;
@@ -342,10 +323,17 @@ static void buffer_queue(struct vb2_buffer *vb)
min_bufs = vid_cap->reqbufs_count > 1 ? 2 : 1;
- if (vid_cap->active_buf_cnt >= min_bufs &&
- !test_and_set_bit(ST_CAPT_STREAM, &fimc->state))
+
+ if (vb2_is_streaming(&vid_cap->vbq) &&
+ vid_cap->active_buf_cnt >= min_bufs &&
+ !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
fimc_activate_capture(ctx);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ fimc_pipeline_s_stream(fimc, 1);
+ return;
+ }
spin_unlock_irqrestore(&fimc->slock, flags);
}
@@ -371,10 +359,40 @@ static struct vb2_ops fimc_capture_qops = {
.stop_streaming = stop_streaming,
};
+/**
+ * fimc_capture_ctrls_create - initialize the control handler
+ * Initialize the capture video node control handler and fill it
+ * with the FIMC controls. Inherit any sensor's controls if the
+ * 'user_subdev_api' flag is false (default behaviour).
+ * This function need to be called with the graph mutex held.
+ */
+int fimc_capture_ctrls_create(struct fimc_dev *fimc)
+{
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ int ret;
+
+ if (WARN_ON(vid_cap->ctx == NULL))
+ return -ENXIO;
+ if (vid_cap->ctx->ctrls_rdy)
+ return 0;
+
+ ret = fimc_ctrls_create(vid_cap->ctx);
+ if (ret || vid_cap->user_subdev_api)
+ return ret;
+
+ return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrl_handler,
+ fimc->pipeline.sensor->ctrl_handler);
+}
+
+static int fimc_capture_set_default_format(struct fimc_dev *fimc);
+
static int fimc_capture_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
- int ret = 0;
+ int ret = v4l2_fh_open(file);
+
+ if (ret)
+ return ret;
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
@@ -382,17 +400,27 @@ static int fimc_capture_open(struct file *file)
if (fimc_m2m_active(fimc))
return -EBUSY;
+ set_bit(ST_CAPT_BUSY, &fimc->state);
+ pm_runtime_get_sync(&fimc->pdev->dev);
+
if (++fimc->vid_cap.refcnt == 1) {
- ret = fimc_isp_subdev_init(fimc, 0);
- if (ret) {
+ ret = fimc_pipeline_initialize(fimc,
+ &fimc->vid_cap.vfd->entity, true);
+ if (ret < 0) {
+ dev_err(&fimc->pdev->dev,
+ "Video pipeline initialization failed\n");
+ pm_runtime_put_sync(&fimc->pdev->dev);
fimc->vid_cap.refcnt--;
- return -EIO;
+ v4l2_fh_release(file);
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ return ret;
}
- }
-
- file->private_data = fimc->vid_cap.ctx;
+ ret = fimc_capture_ctrls_create(fimc);
- return 0;
+ if (!ret && !fimc->vid_cap.user_subdev_api)
+ ret = fimc_capture_set_default_format(fimc);
+ }
+ return ret;
}
static int fimc_capture_close(struct file *file)
@@ -402,37 +430,36 @@ static int fimc_capture_close(struct file *file)
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
if (--fimc->vid_cap.refcnt == 0) {
- fimc_stop_capture(fimc);
- vb2_queue_release(&fimc->vid_cap.vbq);
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ fimc_stop_capture(fimc, false);
+ fimc_pipeline_shutdown(fimc);
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+ }
- v4l2_err(&fimc->vid_cap.v4l2_dev, "releasing ISP\n");
+ pm_runtime_put(&fimc->pdev->dev);
- v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0);
- clk_disable(fimc->clock[CLK_CAM]);
- fimc_subdev_unregister(fimc);
+ if (fimc->vid_cap.refcnt == 0) {
+ vb2_queue_release(&fimc->vid_cap.vbq);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
}
-
- return 0;
+ return v4l2_fh_release(file);
}
static unsigned int fimc_capture_poll(struct file *file,
struct poll_table_struct *wait)
{
- struct fimc_ctx *ctx = file->private_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
return vb2_poll(&fimc->vid_cap.vbq, file, wait);
}
static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct fimc_ctx *ctx = file->private_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
return vb2_mmap(&fimc->vid_cap.vbq, vma);
}
-/* video device file operations */
static const struct v4l2_file_operations fimc_capture_fops = {
.owner = THIS_MODULE,
.open = fimc_capture_open,
@@ -442,264 +469,553 @@ static const struct v4l2_file_operations fimc_capture_fops = {
.mmap = fimc_capture_mmap,
};
+/*
+ * Format and crop negotiation helpers
+ */
+
+static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
+ u32 *width, u32 *height,
+ u32 *code, u32 *fourcc, int pad)
+{
+ bool rotation = ctx->rotation == 90 || ctx->rotation == 270;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct samsung_fimc_variant *var = fimc->variant;
+ struct fimc_pix_limit *pl = var->pix_limit;
+ struct fimc_frame *dst = &ctx->d_frame;
+ u32 depth, min_w, max_w, min_h, align_h = 3;
+ u32 mask = FMT_FLAGS_CAM;
+ struct fimc_fmt *ffmt;
+
+ /* Color conversion from/to JPEG is not supported */
+ if (code && ctx->s_frame.fmt && pad == FIMC_SD_PAD_SOURCE &&
+ fimc_fmt_is_jpeg(ctx->s_frame.fmt->color))
+ *code = V4L2_MBUS_FMT_JPEG_1X8;
+
+ if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK)
+ mask |= FMT_FLAGS_M2M;
+
+ ffmt = fimc_find_format(fourcc, code, mask, 0);
+ if (WARN_ON(!ffmt))
+ return NULL;
+ if (code)
+ *code = ffmt->mbus_code;
+ if (fourcc)
+ *fourcc = ffmt->fourcc;
+
+ if (pad == FIMC_SD_PAD_SINK) {
+ max_w = fimc_fmt_is_jpeg(ffmt->color) ?
+ pl->scaler_dis_w : pl->scaler_en_w;
+ /* Apply the camera input interface pixel constraints */
+ v4l_bound_align_image(width, max_t(u32, *width, 32), max_w, 4,
+ height, max_t(u32, *height, 32),
+ FIMC_CAMIF_MAX_HEIGHT,
+ fimc_fmt_is_jpeg(ffmt->color) ? 3 : 1,
+ 0);
+ return ffmt;
+ }
+ /* Can't scale or crop in transparent (JPEG) transfer mode */
+ if (fimc_fmt_is_jpeg(ffmt->color)) {
+ *width = ctx->s_frame.f_width;
+ *height = ctx->s_frame.f_height;
+ return ffmt;
+ }
+ /* Apply the scaler and the output DMA constraints */
+ max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w;
+ min_w = ctx->state & FIMC_DST_CROP ? dst->width : var->min_out_pixsize;
+ min_h = ctx->state & FIMC_DST_CROP ? dst->height : var->min_out_pixsize;
+ if (fimc->id == 1 && var->pix_hoff)
+ align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1;
+
+ depth = fimc_get_format_depth(ffmt);
+ v4l_bound_align_image(width, min_w, max_w,
+ ffs(var->min_out_pixsize) - 1,
+ height, min_h, FIMC_CAMIF_MAX_HEIGHT,
+ align_h,
+ 64/(ALIGN(depth, 8)));
+
+ dbg("pad%d: code: 0x%x, %dx%d. dst fmt: %dx%d",
+ pad, code ? *code : 0, *width, *height,
+ dst->f_width, dst->f_height);
+
+ return ffmt;
+}
+
+static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r,
+ int pad)
+{
+ bool rotate = ctx->rotation == 90 || ctx->rotation == 270;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct samsung_fimc_variant *var = fimc->variant;
+ struct fimc_pix_limit *pl = var->pix_limit;
+ struct fimc_frame *sink = &ctx->s_frame;
+ u32 max_w, max_h, min_w = 0, min_h = 0, min_sz;
+ u32 align_sz = 0, align_h = 4;
+ u32 max_sc_h, max_sc_v;
+
+ /* In JPEG transparent transfer mode cropping is not supported */
+ if (fimc_fmt_is_jpeg(ctx->d_frame.fmt->color)) {
+ r->width = sink->f_width;
+ r->height = sink->f_height;
+ r->left = r->top = 0;
+ return;
+ }
+ if (pad == FIMC_SD_PAD_SOURCE) {
+ if (ctx->rotation != 90 && ctx->rotation != 270)
+ align_h = 1;
+ max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3));
+ max_sc_v = min(SCALER_MAX_VRATIO, 1 << (ffs(sink->height) - 1));
+ min_sz = var->min_out_pixsize;
+ } else {
+ u32 depth = fimc_get_format_depth(sink->fmt);
+ align_sz = 64/ALIGN(depth, 8);
+ min_sz = var->min_inp_pixsize;
+ min_w = min_h = min_sz;
+ max_sc_h = max_sc_v = 1;
+ }
+ /*
+ * For the crop rectangle at source pad the following constraints
+ * must be met:
+ * - it must fit in the sink pad format rectangle (f_width/f_height);
+ * - maximum downscaling ratio is 64;
+ * - maximum crop size depends if the rotator is used or not;
+ * - the sink pad format width/height must be 4 multiple of the
+ * prescaler ratios determined by sink pad size and source pad crop,
+ * the prescaler ratio is returned by fimc_get_scaler_factor().
+ */
+ max_w = min_t(u32,
+ rotate ? pl->out_rot_en_w : pl->out_rot_dis_w,
+ rotate ? sink->f_height : sink->f_width);
+ max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height);
+ if (pad == FIMC_SD_PAD_SOURCE) {
+ min_w = min_t(u32, max_w, sink->f_width / max_sc_h);
+ min_h = min_t(u32, max_h, sink->f_height / max_sc_v);
+ if (rotate) {
+ swap(max_sc_h, max_sc_v);
+ swap(min_w, min_h);
+ }
+ }
+ v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1,
+ &r->height, min_h, max_h, align_h,
+ align_sz);
+ /* Adjust left/top if cropping rectangle is out of bounds */
+ r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width);
+ r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height);
+ r->left = round_down(r->left, var->hor_offs_align);
+
+ dbg("pad%d: (%d,%d)/%dx%d, sink fmt: %dx%d",
+ pad, r->left, r->top, r->width, r->height,
+ sink->f_width, sink->f_height);
+}
+
+/*
+ * The video node ioctl operations
+ */
static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct fimc_ctx *ctx = file->private_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
- cap->version = KERNEL_VERSION(1, 0, 0);
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
return 0;
}
-/* Synchronize formats of the camera interface input and attached sensor. */
-static int sync_capture_fmt(struct fimc_ctx *ctx)
+static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM | FMT_FLAGS_M2M,
+ f->index);
+ if (!fmt)
+ return -EINVAL;
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ if (fmt->fourcc == V4L2_MBUS_FMT_JPEG_1X8)
+ f->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ return 0;
+}
+
+/**
+ * fimc_pipeline_try_format - negotiate and/or set formats at pipeline
+ * elements
+ * @ctx: FIMC capture context
+ * @tfmt: media bus format to try/set on subdevs
+ * @fmt_id: fimc pixel format id corresponding to returned @tfmt (output)
+ * @set: true to set format on subdevs, false to try only
+ */
+static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
+ struct v4l2_mbus_framefmt *tfmt,
+ struct fimc_fmt **fmt_id,
+ bool set)
{
- struct fimc_frame *frame = &ctx->s_frame;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct v4l2_mbus_framefmt *fmt = &fimc->vid_cap.fmt;
- int ret;
+ struct v4l2_subdev *sd = fimc->pipeline.sensor;
+ struct v4l2_subdev *csis = fimc->pipeline.csis;
+ struct v4l2_subdev_format sfmt;
+ struct v4l2_mbus_framefmt *mf = &sfmt.format;
+ struct fimc_fmt *ffmt = NULL;
+ int ret, i = 0;
+
+ if (WARN_ON(!sd || !tfmt))
+ return -EINVAL;
- fmt->width = ctx->d_frame.o_width;
- fmt->height = ctx->d_frame.o_height;
+ memset(&sfmt, 0, sizeof(sfmt));
+ sfmt.format = *tfmt;
+
+ sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY;
+ while (1) {
+ ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL,
+ FMT_FLAGS_CAM, i++);
+ if (ffmt == NULL) {
+ /*
+ * Notify user-space if common pixel code for
+ * host and sensor does not exist.
+ */
+ return -EINVAL;
+ }
+ mf->code = tfmt->code = ffmt->mbus_code;
- ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_mbus_fmt, fmt);
- if (ret == -ENOIOCTLCMD) {
- err("s_mbus_fmt failed");
- return ret;
- }
- dbg("w: %d, h: %d, code= %d", fmt->width, fmt->height, fmt->code);
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
+ if (ret)
+ return ret;
+ if (mf->code != tfmt->code) {
+ mf->code = 0;
+ continue;
+ }
+ if (mf->width != tfmt->width || mf->width != tfmt->width) {
+ u32 fcc = ffmt->fourcc;
+ tfmt->width = mf->width;
+ tfmt->height = mf->height;
+ ffmt = fimc_capture_try_format(ctx,
+ &tfmt->width, &tfmt->height,
+ NULL, &fcc, FIMC_SD_PAD_SOURCE);
+ if (ffmt && ffmt->mbus_code)
+ mf->code = ffmt->mbus_code;
+ if (mf->width != tfmt->width || mf->width != tfmt->width)
+ continue;
+ tfmt->code = mf->code;
+ }
+ if (csis)
+ ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt);
- frame->fmt = find_mbus_format(fmt, FMT_FLAGS_CAM);
- if (!frame->fmt) {
- err("fimc source format not found\n");
- return -EINVAL;
+ if (mf->code == tfmt->code &&
+ mf->width == tfmt->width && mf->width == tfmt->width)
+ break;
}
- frame->f_width = fmt->width;
- frame->f_height = fmt->height;
- frame->width = fmt->width;
- frame->height = fmt->height;
- frame->o_width = fmt->width;
- frame->o_height = fmt->height;
- frame->offs_h = 0;
- frame->offs_v = 0;
+ if (fmt_id && ffmt)
+ *fmt_id = ffmt;
+ *tfmt = *mf;
+ dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt);
return 0;
}
-static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
+static int fimc_cap_g_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_frame *frame;
- struct v4l2_pix_format_mplane *pix;
- int ret;
- int i;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
- ret = fimc_vidioc_try_fmt_mplane(file, priv, f);
- if (ret)
- return ret;
-
- if (vb2_is_busy(&fimc->vid_cap.vbq) || fimc_capture_active(fimc))
- return -EBUSY;
+ return fimc_fill_format(&ctx->d_frame, f);
+}
- frame = &ctx->d_frame;
+static int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_mbus_framefmt mf;
+ struct fimc_fmt *ffmt = NULL;
- pix = &f->fmt.pix_mp;
- frame->fmt = find_format(f, FMT_FLAGS_M2M | FMT_FLAGS_CAM);
- if (!frame->fmt) {
- err("fimc target format not found\n");
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SINK);
+ ctx->s_frame.f_width = pix->width;
+ ctx->s_frame.f_height = pix->height;
}
+ ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SOURCE);
+ if (!ffmt)
+ return -EINVAL;
- for (i = 0; i < frame->fmt->colplanes; i++) {
- frame->payload[i] =
- (pix->width * pix->height * frame->fmt->depth[i]) >> 3;
+ if (!fimc->vid_cap.user_subdev_api) {
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.code = ffmt->mbus_code;
+ fimc_md_graph_lock(fimc);
+ fimc_pipeline_try_format(ctx, &mf, &ffmt, false);
+ fimc_md_graph_unlock(fimc);
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ if (ffmt)
+ pix->pixelformat = ffmt->fourcc;
}
- /* Output DMA frame pixel size and offsets. */
- frame->f_width = pix->plane_fmt[0].bytesperline * 8
- / frame->fmt->depth[0];
- frame->f_height = pix->height;
- frame->width = pix->width;
- frame->height = pix->height;
- frame->o_width = pix->width;
- frame->o_height = pix->height;
- frame->offs_h = 0;
- frame->offs_v = 0;
+ fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix);
+ return 0;
+}
- ctx->state |= (FIMC_PARAMS | FIMC_DST_FMT);
+static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, bool jpeg)
+{
+ ctx->scaler.enabled = !jpeg;
+ fimc_ctrls_activate(ctx, !jpeg);
- ret = sync_capture_fmt(ctx);
- return ret;
+ if (jpeg)
+ set_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
+ else
+ clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
}
-static int fimc_cap_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
+static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
- struct s5p_platform_fimc *pldata = ctx->fimc_dev->pdata;
- struct s5p_fimc_isp_info *isp_info;
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf;
+ struct fimc_frame *ff = &ctx->d_frame;
+ struct fimc_fmt *s_fmt = NULL;
+ int ret, i;
- if (i->index >= pldata->num_clients)
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
+ if (vb2_is_busy(&fimc->vid_cap.vbq))
+ return -EBUSY;
- isp_info = &pldata->isp_info[i->index];
+ /* Pre-configure format at camera interface input, for JPEG only */
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SINK);
+ ctx->s_frame.f_width = pix->width;
+ ctx->s_frame.f_height = pix->height;
+ }
+ /* Try the format at the scaler and the DMA output */
+ ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SOURCE);
+ if (!ff->fmt)
+ return -EINVAL;
+ /* Try to match format at the host and the sensor */
+ if (!fimc->vid_cap.user_subdev_api) {
+ mf->code = ff->fmt->mbus_code;
+ mf->width = pix->width;
+ mf->height = pix->height;
+
+ fimc_md_graph_lock(fimc);
+ ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true);
+ fimc_md_graph_unlock(fimc);
+ if (ret)
+ return ret;
+ pix->width = mf->width;
+ pix->height = mf->height;
+ }
+ fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix);
+ for (i = 0; i < ff->fmt->colplanes; i++)
+ ff->payload[i] =
+ (pix->width * pix->height * ff->fmt->depth[i]) / 8;
+
+ set_frame_bounds(ff, pix->width, pix->height);
+ /* Reset the composition rectangle if not yet configured */
+ if (!(ctx->state & FIMC_DST_CROP))
+ set_frame_crop(ff, 0, 0, pix->width, pix->height);
+
+ fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ff->fmt->color));
+
+ /* Reset cropping and set format at the camera interface input */
+ if (!fimc->vid_cap.user_subdev_api) {
+ ctx->s_frame.fmt = s_fmt;
+ set_frame_bounds(&ctx->s_frame, pix->width, pix->height);
+ set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height);
+ }
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strncpy(i->name, isp_info->board_info->type, 32);
- return 0;
+ return ret;
}
-static int fimc_cap_s_input(struct file *file, void *priv,
- unsigned int i)
+static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct s5p_platform_fimc *pdata = fimc->pdata;
-
- if (fimc_capture_active(ctx->fimc_dev))
- return -EBUSY;
+ struct fimc_dev *fimc = video_drvdata(file);
- if (i >= pdata->num_clients)
- return -EINVAL;
+ return fimc_capture_set_format(fimc, f);
+}
+static int fimc_cap_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct v4l2_subdev *sd = fimc->pipeline.sensor;
- if (fimc->vid_cap.sd) {
- int ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0);
- if (ret)
- err("s_power failed: %d", ret);
+ if (i->index != 0)
+ return -EINVAL;
- clk_disable(fimc->clock[CLK_CAM]);
- }
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ if (sd)
+ strlcpy(i->name, sd->name, sizeof(i->name));
+ return 0;
+}
- /* Release the attached sensor subdevice. */
- fimc_subdev_unregister(fimc);
+static int fimc_cap_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return i == 0 ? i : -EINVAL;
+}
- return fimc_isp_subdev_init(fimc, i);
+static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
}
-static int fimc_cap_g_input(struct file *file, void *priv,
- unsigned int *i)
+/**
+ * fimc_pipeline_validate - check for formats inconsistencies
+ * between source and sink pad of each link
+ *
+ * Return 0 if all formats match or -EPIPE otherwise.
+ */
+static int fimc_pipeline_validate(struct fimc_dev *fimc)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
+ struct v4l2_subdev_format sink_fmt, src_fmt;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct v4l2_subdev *sd;
+ struct media_pad *pad;
+ int ret;
- *i = cap->input_index;
+ /* Start with the video capture node pad */
+ pad = media_entity_remote_source(&vid_cap->vd_pad);
+ if (pad == NULL)
+ return -EPIPE;
+ /* FIMC.{N} subdevice */
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ while (1) {
+ /* Retrieve format at the sink pad */
+ pad = &sd->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+ /* Don't call FIMC subdev operation to avoid nested locking */
+ if (sd == fimc->vid_cap.subdev) {
+ struct fimc_frame *ff = &vid_cap->ctx->s_frame;
+ sink_fmt.format.width = ff->f_width;
+ sink_fmt.format.height = ff->f_height;
+ sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0;
+ } else {
+ sink_fmt.pad = pad->index;
+ sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+ }
+ /* Retrieve format at the source pad */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ src_fmt.pad = pad->index;
+ src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ if (src_fmt.format.width != sink_fmt.format.width ||
+ src_fmt.format.height != sink_fmt.format.height ||
+ src_fmt.format.code != sink_fmt.format.code)
+ return -EPIPE;
+ }
return 0;
}
static int fimc_cap_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_pipeline *p = &fimc->pipeline;
+ int ret;
- if (fimc_capture_active(fimc) || !fimc->vid_cap.sd)
+ if (fimc_capture_active(fimc))
return -EBUSY;
- if (!(ctx->state & FIMC_DST_FMT)) {
- v4l2_err(&fimc->vid_cap.v4l2_dev, "Format is not set\n");
- return -EINVAL;
- }
+ media_entity_pipeline_start(&p->sensor->entity, p->pipe);
+ if (fimc->vid_cap.user_subdev_api) {
+ ret = fimc_pipeline_validate(fimc);
+ if (ret)
+ return ret;
+ }
return vb2_streamon(&fimc->vid_cap.vbq, type);
}
static int fimc_cap_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct v4l2_subdev *sd = fimc->pipeline.sensor;
+ int ret;
- return vb2_streamoff(&fimc->vid_cap.vbq, type);
+ ret = vb2_streamoff(&fimc->vid_cap.vbq, type);
+ if (ret == 0)
+ media_entity_pipeline_stop(&sd->entity);
+ return ret;
}
static int fimc_cap_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *reqbufs)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
- int ret;
-
+ struct fimc_dev *fimc = video_drvdata(file);
+ int ret = vb2_reqbufs(&fimc->vid_cap.vbq, reqbufs);
- ret = vb2_reqbufs(&cap->vbq, reqbufs);
if (!ret)
- cap->reqbufs_count = reqbufs->count;
-
+ fimc->vid_cap.reqbufs_count = reqbufs->count;
return ret;
}
static int fimc_cap_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
+ struct fimc_dev *fimc = video_drvdata(file);
- return vb2_querybuf(&cap->vbq, buf);
+ return vb2_querybuf(&fimc->vid_cap.vbq, buf);
}
static int fimc_cap_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
- return vb2_qbuf(&cap->vbq, buf);
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_qbuf(&fimc->vid_cap.vbq, buf);
}
static int fimc_cap_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
- return vb2_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf,
- file->f_flags & O_NONBLOCK);
-}
-
-static int fimc_cap_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct fimc_ctx *ctx = priv;
- int ret = -EINVAL;
+ struct fimc_dev *fimc = video_drvdata(file);
- /* Allow any controls but 90/270 rotation while streaming */
- if (!fimc_capture_active(ctx->fimc_dev) ||
- ctrl->id != V4L2_CID_ROTATE ||
- (ctrl->value != 90 && ctrl->value != 270)) {
- ret = check_ctrl_val(ctx, ctrl);
- if (!ret) {
- ret = fimc_s_ctrl(ctx, ctrl);
- if (!ret)
- ctx->state |= FIMC_PARAMS;
- }
- }
- if (ret == -EINVAL)
- ret = v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd,
- core, s_ctrl, ctrl);
- return ret;
+ return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK);
}
static int fimc_cap_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cr)
{
- struct fimc_frame *f;
- struct fimc_ctx *ctx = fh;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
- f = &ctx->s_frame;
-
cr->bounds.left = 0;
cr->bounds.top = 0;
cr->bounds.width = f->o_width;
@@ -711,10 +1027,8 @@ static int fimc_cap_cropcap(struct file *file, void *fh,
static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
- struct fimc_frame *f;
- struct fimc_ctx *ctx = file->private_data;
-
- f = &ctx->s_frame;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
cr->c.left = f->offs_h;
cr->c.top = f->offs_v;
@@ -724,53 +1038,31 @@ static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
return 0;
}
-static int fimc_cap_s_crop(struct file *file, void *fh,
- struct v4l2_crop *cr)
+static int fimc_cap_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
- struct fimc_frame *f;
- struct fimc_ctx *ctx = file->private_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret = -EINVAL;
-
- if (fimc_capture_active(fimc))
- return -EBUSY;
-
- ret = fimc_try_crop(ctx, cr);
- if (ret)
- return ret;
-
- if (!(ctx->state & FIMC_DST_FMT)) {
- v4l2_err(&fimc->vid_cap.v4l2_dev,
- "Capture color format not set\n");
- return -EINVAL; /* TODO: make sure this is the right value */
- }
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *ff;
+ unsigned long flags;
- f = &ctx->s_frame;
- /* Check for the pixel scaling ratio when cropping input image. */
- ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height,
- ctx->d_frame.width, ctx->d_frame.height,
- ctx->rotation);
- if (ret) {
- v4l2_err(&fimc->vid_cap.v4l2_dev, "Out of the scaler range\n");
- return ret;
- }
+ fimc_capture_try_crop(ctx, &cr->c, FIMC_SD_PAD_SINK);
+ ff = &ctx->s_frame;
- f->offs_h = cr->c.left;
- f->offs_v = cr->c.top;
- f->width = cr->c.width;
- f->height = cr->c.height;
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_frame_crop(ff, cr->c.left, cr->c.top, cr->c.width, cr->c.height);
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
return 0;
}
-
static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_querycap = fimc_vidioc_querycap_capture,
- .vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane,
- .vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane,
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane,
- .vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane,
.vidioc_reqbufs = fimc_cap_reqbufs,
.vidioc_querybuf = fimc_cap_querybuf,
@@ -781,10 +1073,6 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_streamon = fimc_cap_streamon,
.vidioc_streamoff = fimc_cap_streamoff,
- .vidioc_queryctrl = fimc_vidioc_queryctrl,
- .vidioc_g_ctrl = fimc_vidioc_g_ctrl,
- .vidioc_s_ctrl = fimc_cap_s_ctrl,
-
.vidioc_g_crop = fimc_cap_g_crop,
.vidioc_s_crop = fimc_cap_s_crop,
.vidioc_cropcap = fimc_cap_cropcap,
@@ -794,17 +1082,328 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_g_input = fimc_cap_g_input,
};
+/* Capture subdev media entity operations */
+static int fimc_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+
+ if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return -EINVAL;
+
+ if (WARN_ON(fimc == NULL))
+ return 0;
+
+ dbg("%s --> %s, flags: 0x%x. input: 0x%x",
+ local->entity->name, remote->entity->name, flags,
+ fimc->vid_cap.input);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (fimc->vid_cap.input != 0)
+ return -EBUSY;
+ fimc->vid_cap.input = sd->grp_id;
+ return 0;
+ }
+
+ fimc->vid_cap.input = 0;
+ return 0;
+}
+
+static const struct media_entity_operations fimc_sd_media_ops = {
+ .link_setup = fimc_link_setup,
+};
+
+/**
+ * fimc_sensor_notify - v4l2_device notification from a sensor subdev
+ * @sd: pointer to a subdev generating the notification
+ * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY
+ * @arg: pointer to an u32 type integer that stores the frame payload value
+ *
+ * The End Of Frame notification sent by sensor subdev in its still capture
+ * mode. If there is only a single VSYNC generated by the sensor at the
+ * beginning of a frame transmission, FIMC does not issue the LastIrq
+ * (end of frame) interrupt. And this notification is used to complete the
+ * frame capture and returning a buffer to user-space. Subdev drivers should
+ * call this notification from their last 'End of frame capture' interrupt.
+ */
+void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg)
+{
+ struct fimc_sensor_info *sensor;
+ struct fimc_vid_buffer *buf;
+ struct fimc_md *fmd;
+ struct fimc_dev *fimc;
+ unsigned long flags;
+
+ if (sd == NULL)
+ return;
+
+ sensor = v4l2_get_subdev_hostdata(sd);
+ fmd = entity_to_fimc_mdev(&sd->entity);
+
+ spin_lock_irqsave(&fmd->slock, flags);
+ fimc = sensor ? sensor->host : NULL;
+
+ if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY &&
+ test_bit(ST_CAPT_PEND, &fimc->state)) {
+ unsigned long irq_flags;
+ spin_lock_irqsave(&fimc->slock, irq_flags);
+ if (!list_empty(&fimc->vid_cap.active_buf_q)) {
+ buf = list_entry(fimc->vid_cap.active_buf_q.next,
+ struct fimc_vid_buffer, list);
+ vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg));
+ }
+ fimc_capture_irq_handler(fimc, true);
+ fimc_deactivate_capture(fimc);
+ spin_unlock_irqrestore(&fimc->slock, irq_flags);
+ }
+ spin_unlock_irqrestore(&fmd->slock, flags);
+}
+
+static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, code->index);
+ if (!fmt)
+ return -EINVAL;
+ code->code = fmt->mbus_code;
+ return 0;
+}
+
+static int fimc_subdev_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_mbus_framefmt *mf;
+ struct fimc_frame *ff;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ fmt->format = *mf;
+ return 0;
+ }
+ mf = &fmt->format;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ /* The pixel code is same on both input and output pad */
+ if (!WARN_ON(ctx->s_frame.fmt == NULL))
+ mf->code = ctx->s_frame.fmt->mbus_code;
+ mf->width = ff->f_width;
+ mf->height = ff->f_height;
+ mutex_unlock(&fimc->lock);
+
+ return 0;
+}
+
+static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *ff;
+ struct fimc_fmt *ffmt;
+
+ dbg("pad%d: code: 0x%x, %dx%d",
+ fmt->pad, mf->code, mf->width, mf->height);
+
+ if (fmt->pad == FIMC_SD_PAD_SOURCE &&
+ vb2_is_busy(&fimc->vid_cap.vbq))
+ return -EBUSY;
+
+ mutex_lock(&fimc->lock);
+ ffmt = fimc_capture_try_format(ctx, &mf->width, &mf->height,
+ &mf->code, NULL, fmt->pad);
+ mutex_unlock(&fimc->lock);
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ *mf = fmt->format;
+ return 0;
+ }
+ fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ffmt->color));
+
+ ff = fmt->pad == FIMC_SD_PAD_SINK ?
+ &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ set_frame_bounds(ff, mf->width, mf->height);
+ ff->fmt = ffmt;
+
+ /* Reset the crop rectangle if required. */
+ if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_DST_CROP)))
+ set_frame_crop(ff, 0, 0, mf->width, mf->height);
+
+ if (fmt->pad == FIMC_SD_PAD_SINK)
+ ctx->state &= ~FIMC_DST_CROP;
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static int fimc_subdev_get_crop(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_rect *r = &crop->rect;
+ struct fimc_frame *ff;
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
+ crop->rect = *v4l2_subdev_get_try_crop(fh, crop->pad);
+ return 0;
+ }
+ ff = crop->pad == FIMC_SD_PAD_SINK ?
+ &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ r->left = ff->offs_h;
+ r->top = ff->offs_v;
+ r->width = ff->width;
+ r->height = ff->height;
+ mutex_unlock(&fimc->lock);
+
+ dbg("ff:%p, pad%d: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
+ ff, crop->pad, r->left, r->top, r->width, r->height,
+ ff->f_width, ff->f_height);
+
+ return 0;
+}
+
+static int fimc_subdev_set_crop(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_rect *r = &crop->rect;
+ struct fimc_frame *ff;
+ unsigned long flags;
+
+ dbg("(%d,%d)/%dx%d", r->left, r->top, r->width, r->height);
+
+ ff = crop->pad == FIMC_SD_PAD_SOURCE ?
+ &ctx->d_frame : &ctx->s_frame;
+
+ mutex_lock(&fimc->lock);
+ fimc_capture_try_crop(ctx, r, crop->pad);
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mutex_lock(&fimc->lock);
+ *v4l2_subdev_get_try_crop(fh, crop->pad) = *r;
+ return 0;
+ }
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_frame_crop(ff, r->left, r->top, r->width, r->height);
+ if (crop->pad == FIMC_SD_PAD_SOURCE)
+ ctx->state |= FIMC_DST_CROP;
+
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ dbg("pad%d: (%d,%d)/%dx%d", crop->pad, r->left, r->top,
+ r->width, r->height);
+
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = {
+ .enum_mbus_code = fimc_subdev_enum_mbus_code,
+ .get_fmt = fimc_subdev_get_fmt,
+ .set_fmt = fimc_subdev_set_fmt,
+ .get_crop = fimc_subdev_get_crop,
+ .set_crop = fimc_subdev_set_crop,
+};
+
+static struct v4l2_subdev_ops fimc_subdev_ops = {
+ .pad = &fimc_subdev_pad_ops,
+};
+
+static int fimc_create_capture_subdev(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd;
+ int ret;
+
+ sd = kzalloc(sizeof(*sd), GFP_KERNEL);
+ if (!sd)
+ return -ENOMEM;
+
+ v4l2_subdev_init(sd, &fimc_subdev_ops);
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id);
+
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
+ fimc->vid_cap.sd_pads, 0);
+ if (ret)
+ goto me_err;
+ ret = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (ret)
+ goto sd_err;
+
+ fimc->vid_cap.subdev = sd;
+ v4l2_set_subdevdata(sd, fimc);
+ sd->entity.ops = &fimc_sd_media_ops;
+ return 0;
+sd_err:
+ media_entity_cleanup(&sd->entity);
+me_err:
+ kfree(sd);
+ return ret;
+}
+
+static void fimc_destroy_capture_subdev(struct fimc_dev *fimc)
+{
+ struct v4l2_subdev *sd = fimc->vid_cap.subdev;
+
+ if (!sd)
+ return;
+ media_entity_cleanup(&sd->entity);
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ sd = NULL;
+}
+
+/* Set default format at the sensor and host interface */
+static int fimc_capture_set_default_format(struct fimc_dev *fimc)
+{
+ struct v4l2_format fmt = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .fmt.pix_mp = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ };
+
+ return fimc_capture_set_format(fimc, &fmt);
+}
+
/* fimc->lock must be already initialized */
-int fimc_register_capture_device(struct fimc_dev *fimc)
+int fimc_register_capture_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
{
- struct v4l2_device *v4l2_dev = &fimc->vid_cap.v4l2_dev;
struct video_device *vfd;
struct fimc_vid_cap *vid_cap;
struct fimc_ctx *ctx;
- struct v4l2_format f;
- struct fimc_frame *fr;
struct vb2_queue *q;
- int ret;
+ int ret = -ENOMEM;
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx)
@@ -814,33 +1413,21 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
ctx->in_path = FIMC_CAMERA;
ctx->out_path = FIMC_DMA;
ctx->state = FIMC_CTX_CAP;
-
- /* Default format of the output frames */
- f.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
- fr = &ctx->d_frame;
- fr->fmt = find_format(&f, FMT_FLAGS_M2M);
- fr->width = fr->f_width = fr->o_width = 640;
- fr->height = fr->f_height = fr->o_height = 480;
-
- if (!v4l2_dev->name[0])
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
- "%s.capture", dev_name(&fimc->pdev->dev));
-
- ret = v4l2_device_register(NULL, v4l2_dev);
- if (ret)
- goto err_info;
+ ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
+ ctx->d_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
vfd = video_device_alloc();
if (!vfd) {
v4l2_err(v4l2_dev, "Failed to allocate video device\n");
- goto err_v4l2_reg;
+ goto err_vd_alloc;
}
- snprintf(vfd->name, sizeof(vfd->name), "%s:cap",
+ snprintf(vfd->name, sizeof(vfd->name), "%s.capture",
dev_name(&fimc->pdev->dev));
vfd->fops = &fimc_capture_fops;
vfd->ioctl_ops = &fimc_capture_ioctl_ops;
+ vfd->v4l2_dev = v4l2_dev;
vfd->minor = -1;
vfd->release = video_device_release;
vfd->lock = &fimc->lock;
@@ -851,8 +1438,6 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
vid_cap->active_buf_cnt = 0;
vid_cap->reqbufs_count = 0;
vid_cap->refcnt = 0;
- /* Default color format for image sensor */
- vid_cap->fmt.code = V4L2_MBUS_FMT_YUYV8_2X8;
INIT_LIST_HEAD(&vid_cap->pending_buf_q);
INIT_LIST_HEAD(&vid_cap->active_buf_q);
@@ -870,34 +1455,37 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
vb2_queue_init(q);
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
- if (ret) {
- v4l2_err(v4l2_dev, "Failed to register video device\n");
- goto err_vd_reg;
- }
-
- v4l2_info(v4l2_dev,
- "FIMC capture driver registered as /dev/video%d\n",
- vfd->num);
+ fimc->vid_cap.vd_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vfd->entity, 1, &fimc->vid_cap.vd_pad, 0);
+ if (ret)
+ goto err_ent;
+ ret = fimc_create_capture_subdev(fimc, v4l2_dev);
+ if (ret)
+ goto err_sd_reg;
+ vfd->ctrl_handler = &ctx->ctrl_handler;
return 0;
-err_vd_reg:
+err_sd_reg:
+ media_entity_cleanup(&vfd->entity);
+err_ent:
video_device_release(vfd);
-err_v4l2_reg:
- v4l2_device_unregister(v4l2_dev);
-err_info:
+err_vd_alloc:
kfree(ctx);
- dev_err(&fimc->pdev->dev, "failed to install\n");
return ret;
}
void fimc_unregister_capture_device(struct fimc_dev *fimc)
{
- struct fimc_vid_cap *capture = &fimc->vid_cap;
-
- if (capture->vfd)
- video_unregister_device(capture->vfd);
+ struct video_device *vfd = fimc->vid_cap.vfd;
- kfree(capture->ctx);
+ if (vfd) {
+ media_entity_cleanup(&vfd->entity);
+ /* Can also be called if video device was
+ not registered */
+ video_unregister_device(vfd);
+ }
+ fimc_destroy_capture_subdev(fimc);
+ kfree(fimc->vid_cap.ctx);
+ fimc->vid_cap.ctx = NULL;
}
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index bdf19ada9172..19ca6db38b2f 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -12,13 +12,13 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/bug.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -28,9 +28,10 @@
#include <media/videobuf2-dma-contig.h>
#include "fimc-core.h"
+#include "fimc-mdevice.h"
static char *fimc_clocks[MAX_FIMC_CLOCKS] = {
- "sclk_fimc", "fimc", "sclk_cam"
+ "sclk_fimc", "fimc"
};
static struct fimc_fmt fimc_formats[] = {
@@ -158,59 +159,28 @@ static struct fimc_fmt fimc_formats[] = {
.memplanes = 2,
.colplanes = 2,
.flags = FMT_FLAGS_M2M,
- },
-};
-
-static struct v4l2_queryctrl fimc_ctrls[] = {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Horizontal flip",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
}, {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Vertical flip",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_ROTATE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Rotation (CCW)",
- .minimum = 0,
- .maximum = 270,
- .step = 90,
- .default_value = 0,
+ .name = "JPEG encoded data",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .color = S5P_FIMC_JPEG,
+ .depth = { 8 },
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_JPEG_1X8,
+ .flags = FMT_FLAGS_CAM,
},
};
-
-static struct v4l2_queryctrl *get_ctrl(int id)
+int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
+ int dw, int dh, int rotation)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fimc_ctrls); ++i)
- if (id == fimc_ctrls[i].id)
- return &fimc_ctrls[i];
- return NULL;
-}
+ if (rotation == 90 || rotation == 270)
+ swap(dw, dh);
-int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot)
-{
- int tx, ty;
-
- if (rot == 90 || rot == 270) {
- ty = dw;
- tx = dh;
- } else {
- tx = dw;
- ty = dh;
- }
+ if (!ctx->scaler.enabled)
+ return (sw == dw && sh == dh) ? 0 : -EINVAL;
- if ((sw >= SCALER_MAX_HRATIO * tx) || (sh >= SCALER_MAX_VRATIO * ty))
+ if ((sw >= SCALER_MAX_HRATIO * dw) || (sh >= SCALER_MAX_VRATIO * dh))
return -EINVAL;
return 0;
@@ -236,10 +206,11 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
int fimc_set_scaler_info(struct fimc_ctx *ctx)
{
+ struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ struct device *dev = &ctx->fimc_dev->pdev->dev;
struct fimc_scaler *sc = &ctx->scaler;
struct fimc_frame *s_frame = &ctx->s_frame;
struct fimc_frame *d_frame = &ctx->d_frame;
- struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
int tx, ty, sx, sy;
int ret;
@@ -251,15 +222,14 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx)
ty = d_frame->height;
}
if (tx <= 0 || ty <= 0) {
- v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
- "invalid target size: %d x %d", tx, ty);
+ dev_err(dev, "Invalid target size: %dx%d", tx, ty);
return -EINVAL;
}
sx = s_frame->width;
sy = s_frame->height;
if (sx <= 0 || sy <= 0) {
- err("invalid source size: %d x %d", sx, sy);
+ dev_err(dev, "Invalid source size: %dx%d", sx, sy);
return -EINVAL;
}
sc->real_width = sx;
@@ -302,7 +272,6 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx)
static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
{
struct vb2_buffer *src_vb, *dst_vb;
- struct fimc_dev *fimc = ctx->fimc_dev;
if (!ctx || !ctx->m2m_ctx)
return;
@@ -313,54 +282,68 @@ static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
if (src_vb && dst_vb) {
v4l2_m2m_buf_done(src_vb, vb_state);
v4l2_m2m_buf_done(dst_vb, vb_state);
- v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx);
+ v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
+ ctx->m2m_ctx);
}
}
/* Complete the transaction which has been scheduled for execution. */
-static void fimc_m2m_shutdown(struct fimc_ctx *ctx)
+static int fimc_m2m_shutdown(struct fimc_ctx *ctx)
{
struct fimc_dev *fimc = ctx->fimc_dev;
int ret;
if (!fimc_m2m_pending(fimc))
- return;
+ return 0;
fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx);
ret = wait_event_timeout(fimc->irq_queue,
!fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
FIMC_SHUTDOWN_TIMEOUT);
- /*
- * In case of a timeout the buffers are not released in the interrupt
- * handler so return them here with the error flag set, if there are
- * any on the queue.
- */
- if (ret == 0)
- fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+
+ return ret == 0 ? -ETIMEDOUT : ret;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ int ret;
+
+ ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev);
+ return ret > 0 ? 0 : ret;
}
static int stop_streaming(struct vb2_queue *q)
{
struct fimc_ctx *ctx = q->drv_priv;
+ int ret;
- fimc_m2m_shutdown(ctx);
+ ret = fimc_m2m_shutdown(ctx);
+ if (ret == -ETIMEDOUT)
+ fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+ pm_runtime_put(&ctx->fimc_dev->pdev->dev);
return 0;
}
-static void fimc_capture_irq_handler(struct fimc_dev *fimc)
+void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final)
{
struct fimc_vid_cap *cap = &fimc->vid_cap;
struct fimc_vid_buffer *v_buf;
struct timeval *tv;
struct timespec ts;
+ if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
+ wake_up(&fimc->irq_queue);
+ return;
+ }
+
if (!list_empty(&cap->active_buf_q) &&
- test_bit(ST_CAPT_RUN, &fimc->state)) {
+ test_bit(ST_CAPT_RUN, &fimc->state) && final) {
ktime_get_real_ts(&ts);
- v_buf = active_queue_pop(cap);
+ v_buf = fimc_active_queue_pop(cap);
tv = &v_buf->vb.v4l2_buf.timestamp;
tv->tv_sec = ts.tv_sec;
@@ -370,19 +353,14 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc)
vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
}
- if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
- wake_up(&fimc->irq_queue);
- return;
- }
-
if (!list_empty(&cap->pending_buf_q)) {
- v_buf = pending_queue_pop(cap);
+ v_buf = fimc_pending_queue_pop(cap);
fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index);
v_buf->index = cap->buf_index;
/* Move the buffer to the capture active queue */
- active_queue_add(cap, v_buf);
+ fimc_active_queue_add(cap, v_buf);
dbg("next frame: %d, done frame: %d",
fimc_hw_get_frame_index(fimc), v_buf->index);
@@ -392,7 +370,8 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc)
}
if (cap->active_buf_cnt == 0) {
- clear_bit(ST_CAPT_RUN, &fimc->state);
+ if (final)
+ clear_bit(ST_CAPT_RUN, &fimc->state);
if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
cap->buf_index = 0;
@@ -400,11 +379,13 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc)
set_bit(ST_CAPT_RUN, &fimc->state);
}
+ fimc_capture_config_update(cap->ctx);
+
dbg("frame: %d, active_buf_cnt: %d",
fimc_hw_get_frame_index(fimc), cap->active_buf_cnt);
}
-static irqreturn_t fimc_isr(int irq, void *priv)
+static irqreturn_t fimc_irq_handler(int irq, void *priv)
{
struct fimc_dev *fimc = priv;
struct fimc_vid_cap *cap = &fimc->vid_cap;
@@ -412,9 +393,17 @@ static irqreturn_t fimc_isr(int irq, void *priv)
fimc_hw_clear_irq(fimc);
+ spin_lock(&fimc->slock);
+
if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
+ if (test_and_clear_bit(ST_M2M_SUSPENDING, &fimc->state)) {
+ set_bit(ST_M2M_SUSPENDED, &fimc->state);
+ wake_up(&fimc->irq_queue);
+ goto out;
+ }
ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
if (ctx != NULL) {
+ spin_unlock(&fimc->slock);
fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE);
spin_lock(&ctx->slock);
@@ -424,21 +413,16 @@ static irqreturn_t fimc_isr(int irq, void *priv)
}
spin_unlock(&ctx->slock);
}
-
return IRQ_HANDLED;
- }
-
- spin_lock(&fimc->slock);
-
- if (test_bit(ST_CAPT_PEND, &fimc->state)) {
- fimc_capture_irq_handler(fimc);
-
+ } else if (test_bit(ST_CAPT_PEND, &fimc->state)) {
+ fimc_capture_irq_handler(fimc,
+ !test_bit(ST_CAPT_JPEG, &fimc->state));
if (cap->active_buf_cnt == 1) {
fimc_deactivate_capture(fimc);
clear_bit(ST_CAPT_STREAM, &fimc->state);
}
}
-
+out:
spin_unlock(&fimc->slock);
return IRQ_HANDLED;
}
@@ -458,7 +442,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
dbg("memplanes= %d, colplanes= %d, pix_size= %d",
frame->fmt->memplanes, frame->fmt->colplanes, pix_size);
- paddr->y = vb2_dma_contig_plane_paddr(vb, 0);
+ paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
if (frame->fmt->memplanes == 1) {
switch (frame->fmt->colplanes) {
@@ -486,10 +470,10 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
}
} else {
if (frame->fmt->memplanes >= 2)
- paddr->cb = vb2_dma_contig_plane_paddr(vb, 1);
+ paddr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
if (frame->fmt->memplanes == 3)
- paddr->cr = vb2_dma_contig_plane_paddr(vb, 2);
+ paddr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
}
dbg("PHYS_ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d",
@@ -499,7 +483,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
}
/* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */
-static void fimc_set_yuv_order(struct fimc_ctx *ctx)
+void fimc_set_yuv_order(struct fimc_ctx *ctx)
{
/* The one only mode supported in SoC. */
ctx->in_order_2p = S5P_FIMC_LSB_CRCB;
@@ -541,7 +525,7 @@ static void fimc_set_yuv_order(struct fimc_ctx *ctx)
dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
}
-static void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
+void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
{
struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
u32 i, depth = 0;
@@ -607,9 +591,6 @@ int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
fimc_set_yuv_order(ctx);
}
- /* Input DMA mode is not allowed when the scaler is disabled. */
- ctx->scaler.enabled = 1;
-
if (flags & FIMC_SRC_ADDR) {
vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr);
@@ -636,10 +617,10 @@ static void fimc_dma_run(void *priv)
return;
fimc = ctx->fimc_dev;
-
- spin_lock_irqsave(&ctx->slock, flags);
+ spin_lock_irqsave(&fimc->slock, flags);
set_bit(ST_M2M_PEND, &fimc->state);
+ spin_lock(&ctx->slock);
ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR);
ret = fimc_prepare_config(ctx, ctx->state);
if (ret)
@@ -650,8 +631,6 @@ static void fimc_dma_run(void *priv)
ctx->state |= FIMC_PARAMS;
fimc->m2m.ctx = ctx;
}
-
- spin_lock(&fimc->slock);
fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr);
if (ctx->state & FIMC_PARAMS) {
@@ -666,7 +645,7 @@ static void fimc_dma_run(void *priv)
fimc_hw_set_mainscaler(ctx);
fimc_hw_set_target_format(ctx);
fimc_hw_set_rotation(ctx);
- fimc_hw_set_effect(ctx);
+ fimc_hw_set_effect(ctx, false);
}
fimc_hw_set_output_path(ctx);
@@ -681,10 +660,9 @@ static void fimc_dma_run(void *priv)
ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
FIMC_SRC_FMT | FIMC_DST_FMT);
fimc_hw_activate_input_dma(fimc, true);
- spin_unlock(&fimc->slock);
-
dma_unlock:
- spin_unlock_irqrestore(&ctx->slock, flags);
+ spin_unlock(&ctx->slock);
+ spin_unlock_irqrestore(&fimc->slock, flags);
}
static void fimc_job_abort(void *priv)
@@ -692,9 +670,9 @@ static void fimc_job_abort(void *priv)
fimc_m2m_shutdown(priv);
}
-static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
- unsigned int *num_planes, unsigned long sizes[],
- void *allocators[])
+static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
{
struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
struct fimc_frame *f;
@@ -763,147 +741,296 @@ static struct vb2_ops fimc_qops = {
.wait_prepare = fimc_unlock,
.wait_finish = fimc_lock,
.stop_streaming = stop_streaming,
+ .start_streaming = start_streaming,
};
-static int fimc_m2m_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
+/*
+ * V4L2 controls handling
+ */
+#define ctrl_to_ctx(__ctrl) \
+ container_of((__ctrl)->handler, struct fimc_ctx, ctrl_handler)
+
+static int fimc_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct fimc_ctx *ctx = file->private_data;
+ struct fimc_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct samsung_fimc_variant *variant = fimc->variant;
+ unsigned long flags;
+ int ret = 0;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->hflip = ctrl->val;
+ break;
+
+ case V4L2_CID_VFLIP:
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->vflip = ctrl->val;
+ break;
+
+ case V4L2_CID_ROTATE:
+ if (fimc_capture_pending(fimc) ||
+ fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
+ ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
+ ctx->s_frame.height, ctx->d_frame.width,
+ ctx->d_frame.height, ctrl->val);
+ }
+ if (ret) {
+ v4l2_err(fimc->m2m.vfd, "Out of scaler range\n");
+ return -EINVAL;
+ }
+ if ((ctrl->val == 90 || ctrl->val == 270) &&
+ !variant->has_out_rot)
+ return -EINVAL;
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->rotation = ctrl->val;
+ break;
+
+ default:
+ v4l2_err(fimc->v4l2_dev, "Invalid control: 0x%X\n", ctrl->id);
+ return -EINVAL;
+ }
+ ctx->state |= FIMC_PARAMS;
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops fimc_ctrl_ops = {
+ .s_ctrl = fimc_s_ctrl,
+};
+
+int fimc_ctrls_create(struct fimc_ctx *ctx)
+{
+ if (ctx->ctrls_rdy)
+ return 0;
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+
+ ctx->ctrl_rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ V4L2_CID_ROTATE, 0, 270, 90, 0);
+ ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
+
+ return ctx->ctrl_handler.error;
+}
+
+void fimc_ctrls_delete(struct fimc_ctx *ctx)
+{
+ if (ctx->ctrls_rdy) {
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ ctx->ctrls_rdy = false;
+ }
+}
+
+void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active)
+{
+ if (!ctx->ctrls_rdy)
+ return;
+
+ mutex_lock(&ctx->ctrl_handler.lock);
+ v4l2_ctrl_activate(ctx->ctrl_rotate, active);
+ v4l2_ctrl_activate(ctx->ctrl_hflip, active);
+ v4l2_ctrl_activate(ctx->ctrl_vflip, active);
+
+ if (active) {
+ ctx->rotation = ctx->ctrl_rotate->val;
+ ctx->hflip = ctx->ctrl_hflip->val;
+ ctx->vflip = ctx->ctrl_vflip->val;
+ } else {
+ ctx->rotation = 0;
+ ctx->hflip = 0;
+ ctx->vflip = 0;
+ }
+ mutex_unlock(&ctx->ctrl_handler.lock);
+}
+
+/*
+ * V4L2 ioctl handlers
+ */
+static int fimc_m2m_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_dev *fimc = ctx->fimc_dev;
strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
- cap->version = KERNEL_VERSION(1, 0, 0);
cap->capabilities = V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
return 0;
}
-int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
+static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
{
struct fimc_fmt *fmt;
- if (f->index >= ARRAY_SIZE(fimc_formats))
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_M2M, f->index);
+ if (!fmt)
return -EINVAL;
- fmt = &fimc_formats[f->index];
strncpy(f->description, fmt->name, sizeof(f->description) - 1);
f->pixelformat = fmt->fourcc;
-
return 0;
}
-int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
+int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_frame *frame;
- struct v4l2_pix_format_mplane *pixm;
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
int i;
- frame = ctx_get_frame(ctx, f->type);
- if (IS_ERR(frame))
- return PTR_ERR(frame);
-
- pixm = &f->fmt.pix_mp;
-
- pixm->width = frame->width;
- pixm->height = frame->height;
- pixm->field = V4L2_FIELD_NONE;
- pixm->pixelformat = frame->fmt->fourcc;
- pixm->colorspace = V4L2_COLORSPACE_JPEG;
- pixm->num_planes = frame->fmt->memplanes;
+ pixm->width = frame->o_width;
+ pixm->height = frame->o_height;
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->pixelformat = frame->fmt->fourcc;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->num_planes = frame->fmt->memplanes;
for (i = 0; i < pixm->num_planes; ++i) {
- int bpl = frame->o_width;
-
+ int bpl = frame->f_width;
if (frame->fmt->colplanes == 1) /* packed formats */
bpl = (bpl * frame->fmt->depth[0]) / 8;
-
pixm->plane_fmt[i].bytesperline = bpl;
-
pixm->plane_fmt[i].sizeimage = (frame->o_width *
frame->o_height * frame->fmt->depth[i]) / 8;
}
-
return 0;
}
-struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask)
+void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f)
{
- struct fimc_fmt *fmt;
- unsigned int i;
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+
+ frame->f_width = pixm->plane_fmt[0].bytesperline;
+ if (frame->fmt->colplanes == 1)
+ frame->f_width = (frame->f_width * 8) / frame->fmt->depth[0];
+ frame->f_height = pixm->height;
+ frame->width = pixm->width;
+ frame->height = pixm->height;
+ frame->o_width = pixm->width;
+ frame->o_height = pixm->height;
+ frame->offs_h = 0;
+ frame->offs_v = 0;
+}
- for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {
- fmt = &fimc_formats[i];
- if (fmt->fourcc == f->fmt.pix_mp.pixelformat &&
- (fmt->flags & mask))
- break;
+/**
+ * fimc_adjust_mplane_format - adjust bytesperline/sizeimage for each plane
+ * @fmt: fimc pixel format description (input)
+ * @width: requested pixel width
+ * @height: requested pixel height
+ * @pix: multi-plane format to adjust
+ */
+void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
+ struct v4l2_pix_format_mplane *pix)
+{
+ u32 bytesperline = 0;
+ int i;
+
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+ pix->field = V4L2_FIELD_NONE;
+ pix->num_planes = fmt->memplanes;
+ pix->height = height;
+ pix->width = width;
+
+ for (i = 0; i < pix->num_planes; ++i) {
+ u32 bpl = pix->plane_fmt[i].bytesperline;
+ u32 *sizeimage = &pix->plane_fmt[i].sizeimage;
+
+ if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width))
+ bpl = pix->width; /* Planar */
+
+ if (fmt->colplanes == 1 && /* Packed */
+ (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width))
+ bpl = (pix->width * fmt->depth[0]) / 8;
+
+ if (i == 0) /* Same bytesperline for each plane. */
+ bytesperline = bpl;
+
+ pix->plane_fmt[i].bytesperline = bytesperline;
+ *sizeimage = (pix->width * pix->height * fmt->depth[i]) / 8;
}
+}
- return (i == ARRAY_SIZE(fimc_formats)) ? NULL : fmt;
+static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame = ctx_get_frame(ctx, f->type);
+
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ return fimc_fill_format(frame, f);
}
-struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f,
- unsigned int mask)
+/**
+ * fimc_find_format - lookup fimc color format by fourcc or media bus format
+ * @pixelformat: fourcc to match, ignored if null
+ * @mbus_code: media bus code to match, ignored if null
+ * @mask: the color flags to match
+ * @index: offset in the fimc_formats array, ignored if negative
+ */
+struct fimc_fmt *fimc_find_format(u32 *pixelformat, u32 *mbus_code,
+ unsigned int mask, int index)
{
- struct fimc_fmt *fmt;
+ struct fimc_fmt *fmt, *def_fmt = NULL;
unsigned int i;
+ int id = 0;
+
+ if (index >= ARRAY_SIZE(fimc_formats))
+ return NULL;
for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {
fmt = &fimc_formats[i];
- if (fmt->mbus_code == f->code && (fmt->flags & mask))
- break;
+ if (!(fmt->flags & mask))
+ continue;
+ if (pixelformat && fmt->fourcc == *pixelformat)
+ return fmt;
+ if (mbus_code && fmt->mbus_code == *mbus_code)
+ return fmt;
+ if (index == id)
+ def_fmt = fmt;
+ id++;
}
-
- return (i == ARRAY_SIZE(fimc_formats)) ? NULL : fmt;
+ return def_fmt;
}
-
-int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
+static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
struct fimc_dev *fimc = ctx->fimc_dev;
struct samsung_fimc_variant *variant = fimc->variant;
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
struct fimc_fmt *fmt;
- u32 max_width, mod_x, mod_y, mask;
- int i, is_output = 0;
-
+ u32 max_w, mod_x, mod_y;
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx))
- return -EINVAL;
- is_output = 1;
- } else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (!IS_M2M(f->type))
return -EINVAL;
- }
dbg("w: %d, h: %d", pix->width, pix->height);
- mask = is_output ? FMT_FLAGS_M2M : FMT_FLAGS_M2M | FMT_FLAGS_CAM;
- fmt = find_format(f, mask);
- if (!fmt) {
- v4l2_err(&fimc->m2m.v4l2_dev, "Fourcc format (0x%X) invalid.\n",
- pix->pixelformat);
+ fmt = fimc_find_format(&pix->pixelformat, NULL, FMT_FLAGS_M2M, 0);
+ if (WARN(fmt == NULL, "Pixel format lookup failed"))
return -EINVAL;
- }
if (pix->field == V4L2_FIELD_ANY)
pix->field = V4L2_FIELD_NONE;
- else if (V4L2_FIELD_NONE != pix->field)
+ else if (pix->field != V4L2_FIELD_NONE)
return -EINVAL;
- if (is_output) {
- max_width = variant->pix_limit->scaler_dis_w;
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ max_w = variant->pix_limit->scaler_dis_w;
mod_x = ffs(variant->min_inp_pixsize) - 1;
} else {
- max_width = variant->pix_limit->out_rot_dis_w;
+ max_w = variant->pix_limit->out_rot_dis_w;
mod_x = ffs(variant->min_out_pixsize) - 1;
}
@@ -916,70 +1043,52 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv,
else
mod_y = mod_x;
}
+ dbg("mod_x: %d, mod_y: %d, max_w: %d", mod_x, mod_y, max_w);
- dbg("mod_x: %d, mod_y: %d, max_w: %d", mod_x, mod_y, max_width);
-
- v4l_bound_align_image(&pix->width, 16, max_width, mod_x,
+ v4l_bound_align_image(&pix->width, 16, max_w, mod_x,
&pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
- pix->num_planes = fmt->memplanes;
- pix->colorspace = V4L2_COLORSPACE_JPEG;
-
-
- for (i = 0; i < pix->num_planes; ++i) {
- u32 bpl = pix->plane_fmt[i].bytesperline;
- u32 *sizeimage = &pix->plane_fmt[i].sizeimage;
-
- if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width))
- bpl = pix->width; /* Planar */
-
- if (fmt->colplanes == 1 && /* Packed */
- (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width))
- bpl = (pix->width * fmt->depth[0]) / 8;
-
- if (i == 0) /* Same bytesperline for each plane. */
- mod_x = bpl;
+ fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp);
+ return 0;
+}
- pix->plane_fmt[i].bytesperline = mod_x;
- *sizeimage = (pix->width * pix->height * fmt->depth[i]) / 8;
- }
+static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
- return 0;
+ return fimc_try_fmt_mplane(ctx, f);
}
-static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv,
+static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_dev *fimc = ctx->fimc_dev;
struct vb2_queue *vq;
struct fimc_frame *frame;
struct v4l2_pix_format_mplane *pix;
int i, ret = 0;
- ret = fimc_vidioc_try_fmt_mplane(file, priv, f);
+ ret = fimc_try_fmt_mplane(ctx, f);
if (ret)
return ret;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
if (vb2_is_busy(vq)) {
- v4l2_err(&fimc->m2m.v4l2_dev, "queue (%d) busy\n", f->type);
+ v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type);
return -EBUSY;
}
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
frame = &ctx->s_frame;
- } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ else
frame = &ctx->d_frame;
- } else {
- v4l2_err(&fimc->m2m.v4l2_dev,
- "Wrong buffer/video queue type (%d)\n", f->type);
- return -EINVAL;
- }
pix = &f->fmt.pix_mp;
- frame->fmt = find_format(f, FMT_FLAGS_M2M);
+ frame->fmt = fimc_find_format(&pix->pixelformat, NULL,
+ FMT_FLAGS_M2M, 0);
if (!frame->fmt)
return -EINVAL;
@@ -988,15 +1097,9 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv,
(pix->width * pix->height * frame->fmt->depth[i]) / 8;
}
- frame->f_width = pix->plane_fmt[0].bytesperline * 8 /
- frame->fmt->depth[0];
- frame->f_height = pix->height;
- frame->width = pix->width;
- frame->height = pix->height;
- frame->o_width = pix->width;
- frame->o_height = pix->height;
- frame->offs_h = 0;
- frame->offs_v = 0;
+ fimc_fill_frame(frame, f);
+
+ ctx->scaler.enabled = 1;
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx);
@@ -1008,39 +1111,42 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv,
return 0;
}
-static int fimc_m2m_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
+static int fimc_m2m_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *reqbufs)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
}
-static int fimc_m2m_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
+static int fimc_m2m_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
}
-static int fimc_m2m_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
+static int fimc_m2m_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
}
-static int fimc_m2m_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
+static int fimc_m2m_dqbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
}
-static int fimc_m2m_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
+static int fimc_m2m_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type type)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
/* The source and target color format need to be set */
if (V4L2_TYPE_IS_OUTPUT(type)) {
@@ -1053,149 +1159,19 @@ static int fimc_m2m_streamon(struct file *file, void *priv,
return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
}
-static int fimc_m2m_streamoff(struct file *file, void *priv,
+static int fimc_m2m_streamoff(struct file *file, void *fh,
enum v4l2_buf_type type)
{
- struct fimc_ctx *ctx = priv;
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
-int fimc_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct fimc_ctx *ctx = priv;
- struct v4l2_queryctrl *c;
- int ret = -EINVAL;
-
- c = get_ctrl(qc->id);
- if (c) {
- *qc = *c;
- return 0;
- }
-
- if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) {
- return v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd,
- core, queryctrl, qc);
- }
- return ret;
-}
-
-int fimc_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
- switch (ctrl->id) {
- case V4L2_CID_HFLIP:
- ctrl->value = (FLIP_X_AXIS & ctx->flip) ? 1 : 0;
- break;
- case V4L2_CID_VFLIP:
- ctrl->value = (FLIP_Y_AXIS & ctx->flip) ? 1 : 0;
- break;
- case V4L2_CID_ROTATE:
- ctrl->value = ctx->rotation;
- break;
- default:
- if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) {
- return v4l2_subdev_call(fimc->vid_cap.sd, core,
- g_ctrl, ctrl);
- } else {
- v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n");
- return -EINVAL;
- }
- }
- dbg("ctrl->value= %d", ctrl->value);
-
- return 0;
-}
-
-int check_ctrl_val(struct fimc_ctx *ctx, struct v4l2_control *ctrl)
-{
- struct v4l2_queryctrl *c;
- c = get_ctrl(ctrl->id);
- if (!c)
- return -EINVAL;
-
- if (ctrl->value < c->minimum || ctrl->value > c->maximum
- || (c->step != 0 && ctrl->value % c->step != 0)) {
- v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
- "Invalid control value\n");
- return -ERANGE;
- }
-
- return 0;
-}
-
-int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl)
-{
- struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_HFLIP:
- if (ctrl->value)
- ctx->flip |= FLIP_X_AXIS;
- else
- ctx->flip &= ~FLIP_X_AXIS;
- break;
-
- case V4L2_CID_VFLIP:
- if (ctrl->value)
- ctx->flip |= FLIP_Y_AXIS;
- else
- ctx->flip &= ~FLIP_Y_AXIS;
- break;
-
- case V4L2_CID_ROTATE:
- if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
- ret = fimc_check_scaler_ratio(ctx->s_frame.width,
- ctx->s_frame.height, ctx->d_frame.width,
- ctx->d_frame.height, ctrl->value);
- }
-
- if (ret) {
- v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range\n");
- return -EINVAL;
- }
-
- /* Check for the output rotator availability */
- if ((ctrl->value == 90 || ctrl->value == 270) &&
- (ctx->in_path == FIMC_DMA && !variant->has_out_rot))
- return -EINVAL;
- ctx->rotation = ctrl->value;
- break;
-
- default:
- v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n");
- return -EINVAL;
- }
-
- fimc_ctx_state_lock_set(FIMC_PARAMS, ctx);
-
- return 0;
-}
-
-static int fimc_m2m_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct fimc_ctx *ctx = priv;
- int ret = 0;
-
- ret = check_ctrl_val(ctx, ctrl);
- if (ret)
- return ret;
-
- ret = fimc_s_ctrl(ctx, ctrl);
- return 0;
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
}
static int fimc_m2m_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *cr)
+ struct v4l2_cropcap *cr)
{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_frame *frame;
- struct fimc_ctx *ctx = fh;
frame = ctx_get_frame(ctx, cr->type);
if (IS_ERR(frame))
@@ -1203,8 +1179,8 @@ static int fimc_m2m_cropcap(struct file *file, void *fh,
cr->bounds.left = 0;
cr->bounds.top = 0;
- cr->bounds.width = frame->f_width;
- cr->bounds.height = frame->f_height;
+ cr->bounds.width = frame->o_width;
+ cr->bounds.height = frame->o_height;
cr->defrect = cr->bounds;
return 0;
@@ -1212,8 +1188,8 @@ static int fimc_m2m_cropcap(struct file *file, void *fh,
static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_frame *frame;
- struct fimc_ctx *ctx = file->private_data;
frame = ctx_get_frame(ctx, cr->type);
if (IS_ERR(frame))
@@ -1227,26 +1203,21 @@ static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
return 0;
}
-int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
+static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
{
struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_frame *f;
u32 min_size, halign, depth = 0;
- bool is_capture_ctx;
int i;
if (cr->c.top < 0 || cr->c.left < 0) {
- v4l2_err(&fimc->m2m.v4l2_dev,
+ v4l2_err(fimc->m2m.vfd,
"doesn't support negative values for top & left\n");
return -EINVAL;
}
-
- is_capture_ctx = fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx);
-
if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- f = is_capture_ctx ? &ctx->s_frame : &ctx->d_frame;
- else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
- !is_capture_ctx)
+ f = &ctx->d_frame;
+ else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
f = &ctx->s_frame;
else
return -EINVAL;
@@ -1255,15 +1226,10 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
/* Get pixel alignment constraints. */
- if (is_capture_ctx) {
- min_size = 16;
- halign = 4;
- } else {
- if (fimc->id == 1 && fimc->variant->pix_hoff)
- halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
- else
- halign = ffs(min_size) - 1;
- }
+ if (fimc->id == 1 && fimc->variant->pix_hoff)
+ halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
+ else
+ halign = ffs(min_size) - 1;
for (i = 0; i < f->fmt->colplanes; i++)
depth += f->fmt->depth[i];
@@ -1280,7 +1246,7 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
cr->c.top = f->o_height - cr->c.height;
cr->c.left = round_down(cr->c.left, min_size);
- cr->c.top = round_down(cr->c.top, is_capture_ctx ? 16 : 8);
+ cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align);
dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
cr->c.left, cr->c.top, cr->c.width, cr->c.height,
@@ -1291,12 +1257,12 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
- struct fimc_ctx *ctx = file->private_data;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_frame *f;
int ret;
- ret = fimc_try_crop(ctx, cr);
+ ret = fimc_m2m_try_crop(ctx, cr);
if (ret)
return ret;
@@ -1306,18 +1272,16 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
/* Check to see if scaling ratio is within supported range */
if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height,
- ctx->d_frame.width,
- ctx->d_frame.height,
- ctx->rotation);
+ ret = fimc_check_scaler_ratio(ctx, cr->c.width,
+ cr->c.height, ctx->d_frame.width,
+ ctx->d_frame.height, ctx->rotation);
} else {
- ret = fimc_check_scaler_ratio(ctx->s_frame.width,
- ctx->s_frame.height,
- cr->c.width, cr->c.height,
- ctx->rotation);
+ ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
+ ctx->s_frame.height, cr->c.width,
+ cr->c.height, ctx->rotation);
}
if (ret) {
- v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range\n");
+ v4l2_err(fimc->m2m.vfd, "Out of scaler range\n");
return -EINVAL;
}
}
@@ -1335,14 +1299,14 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
.vidioc_querycap = fimc_m2m_querycap,
- .vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane,
- .vidioc_enum_fmt_vid_out_mplane = fimc_vidioc_enum_fmt_mplane,
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane,
+ .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane,
- .vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane,
- .vidioc_g_fmt_vid_out_mplane = fimc_vidioc_g_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane,
+ .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane,
- .vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane,
- .vidioc_try_fmt_vid_out_mplane = fimc_vidioc_try_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane,
+ .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane,
.vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane,
@@ -1356,10 +1320,6 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
.vidioc_streamon = fimc_m2m_streamon,
.vidioc_streamoff = fimc_m2m_streamoff,
- .vidioc_queryctrl = fimc_vidioc_queryctrl,
- .vidioc_g_ctrl = fimc_vidioc_g_ctrl,
- .vidioc_s_ctrl = fimc_m2m_s_ctrl,
-
.vidioc_g_crop = fimc_m2m_g_crop,
.vidioc_s_crop = fimc_m2m_s_crop,
.vidioc_cropcap = fimc_m2m_cropcap
@@ -1398,7 +1358,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
static int fimc_m2m_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_ctx *ctx = NULL;
+ struct fimc_ctx *ctx;
+ int ret;
dbg("pid: %d, state: 0x%lx, refcnt: %d",
task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
@@ -1410,19 +1371,24 @@ static int fimc_m2m_open(struct file *file)
if (fimc->vid_cap.refcnt > 0)
return -EBUSY;
- fimc->m2m.refcnt++;
- set_bit(ST_OUTDMA_RUN, &fimc->state);
-
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx)
return -ENOMEM;
+ v4l2_fh_init(&ctx->fh, fimc->m2m.vfd);
+ ret = fimc_ctrls_create(ctx);
+ if (ret)
+ goto error_fh;
+
+ /* Use separate control handler per file handle */
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
- file->private_data = ctx;
ctx->fimc_dev = fimc;
/* Default color format */
ctx->s_frame.fmt = &fimc_formats[0];
ctx->d_frame.fmt = &fimc_formats[0];
- /* Setup the device context for mem2mem mode. */
+ /* Setup the device context for memory-to-memory mode */
ctx->state = FIMC_CTX_M2M;
ctx->flags = 0;
ctx->in_path = FIMC_DMA;
@@ -1431,34 +1397,46 @@ static int fimc_m2m_open(struct file *file)
ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
if (IS_ERR(ctx->m2m_ctx)) {
- int err = PTR_ERR(ctx->m2m_ctx);
- kfree(ctx);
- return err;
+ ret = PTR_ERR(ctx->m2m_ctx);
+ goto error_c;
}
+ if (fimc->m2m.refcnt++ == 0)
+ set_bit(ST_M2M_RUN, &fimc->state);
return 0;
+
+error_c:
+ fimc_ctrls_delete(ctx);
+error_fh:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ return ret;
}
static int fimc_m2m_release(struct file *file)
{
- struct fimc_ctx *ctx = file->private_data;
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
struct fimc_dev *fimc = ctx->fimc_dev;
dbg("pid: %d, state: 0x%lx, refcnt= %d",
task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
v4l2_m2m_ctx_release(ctx->m2m_ctx);
- kfree(ctx);
- if (--fimc->m2m.refcnt <= 0)
- clear_bit(ST_OUTDMA_RUN, &fimc->state);
+ fimc_ctrls_delete(ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ if (--fimc->m2m.refcnt <= 0)
+ clear_bit(ST_M2M_RUN, &fimc->state);
+ kfree(ctx);
return 0;
}
static unsigned int fimc_m2m_poll(struct file *file,
- struct poll_table_struct *wait)
+ struct poll_table_struct *wait)
{
- struct fimc_ctx *ctx = file->private_data;
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
}
@@ -1466,7 +1444,7 @@ static unsigned int fimc_m2m_poll(struct file *file,
static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct fimc_ctx *ctx = file->private_data;
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
}
@@ -1485,92 +1463,73 @@ static struct v4l2_m2m_ops m2m_ops = {
.job_abort = fimc_job_abort,
};
-static int fimc_register_m2m_device(struct fimc_dev *fimc)
+int fimc_register_m2m_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
{
struct video_device *vfd;
struct platform_device *pdev;
- struct v4l2_device *v4l2_dev;
int ret = 0;
if (!fimc)
return -ENODEV;
pdev = fimc->pdev;
- v4l2_dev = &fimc->m2m.v4l2_dev;
-
- /* set name if it is empty */
- if (!v4l2_dev->name[0])
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
- "%s.m2m", dev_name(&pdev->dev));
-
- ret = v4l2_device_register(&pdev->dev, v4l2_dev);
- if (ret)
- goto err_m2m_r1;
+ fimc->v4l2_dev = v4l2_dev;
vfd = video_device_alloc();
if (!vfd) {
v4l2_err(v4l2_dev, "Failed to allocate video device\n");
- goto err_m2m_r1;
+ return -ENOMEM;
}
vfd->fops = &fimc_m2m_fops;
vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
+ vfd->v4l2_dev = v4l2_dev;
vfd->minor = -1;
vfd->release = video_device_release;
vfd->lock = &fimc->lock;
- snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", dev_name(&pdev->dev));
-
+ snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev));
video_set_drvdata(vfd, fimc);
- platform_set_drvdata(pdev, fimc);
fimc->m2m.vfd = vfd;
fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
if (IS_ERR(fimc->m2m.m2m_dev)) {
v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
ret = PTR_ERR(fimc->m2m.m2m_dev);
- goto err_m2m_r2;
- }
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
- if (ret) {
- v4l2_err(v4l2_dev,
- "%s(): failed to register video device\n", __func__);
- goto err_m2m_r3;
+ goto err_init;
}
- v4l2_info(v4l2_dev,
- "FIMC m2m driver registered as /dev/video%d\n", vfd->num);
- return 0;
+ ret = media_entity_init(&vfd->entity, 0, NULL, 0);
+ if (!ret)
+ return 0;
-err_m2m_r3:
v4l2_m2m_release(fimc->m2m.m2m_dev);
-err_m2m_r2:
+err_init:
video_device_release(fimc->m2m.vfd);
-err_m2m_r1:
- v4l2_device_unregister(v4l2_dev);
-
return ret;
}
-static void fimc_unregister_m2m_device(struct fimc_dev *fimc)
+void fimc_unregister_m2m_device(struct fimc_dev *fimc)
{
- if (fimc) {
+ if (!fimc)
+ return;
+
+ if (fimc->m2m.m2m_dev)
v4l2_m2m_release(fimc->m2m.m2m_dev);
+ if (fimc->m2m.vfd) {
+ media_entity_cleanup(&fimc->m2m.vfd->entity);
+ /* Can also be called if video device wasn't registered */
video_unregister_device(fimc->m2m.vfd);
-
- v4l2_device_unregister(&fimc->m2m.v4l2_dev);
}
}
-static void fimc_clk_release(struct fimc_dev *fimc)
+static void fimc_clk_put(struct fimc_dev *fimc)
{
int i;
for (i = 0; i < fimc->num_clocks; i++) {
- if (fimc->clock[i]) {
- clk_disable(fimc->clock[i]);
+ if (fimc->clock[i])
clk_put(fimc->clock[i]);
- }
}
}
@@ -1579,15 +1538,50 @@ static int fimc_clk_get(struct fimc_dev *fimc)
int i;
for (i = 0; i < fimc->num_clocks; i++) {
fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]);
-
- if (!IS_ERR_OR_NULL(fimc->clock[i])) {
- clk_enable(fimc->clock[i]);
+ if (!IS_ERR_OR_NULL(fimc->clock[i]))
continue;
- }
dev_err(&fimc->pdev->dev, "failed to get fimc clock: %s\n",
fimc_clocks[i]);
return -ENXIO;
}
+
+ return 0;
+}
+
+static int fimc_m2m_suspend(struct fimc_dev *fimc)
+{
+ unsigned long flags;
+ int timeout;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ if (!fimc_m2m_pending(fimc)) {
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return 0;
+ }
+ clear_bit(ST_M2M_SUSPENDED, &fimc->state);
+ set_bit(ST_M2M_SUSPENDING, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ timeout = wait_event_timeout(fimc->irq_queue,
+ test_bit(ST_M2M_SUSPENDED, &fimc->state),
+ FIMC_SHUTDOWN_TIMEOUT);
+
+ clear_bit(ST_M2M_SUSPENDING, &fimc->state);
+ return timeout == 0 ? -EAGAIN : 0;
+}
+
+static int fimc_m2m_resume(struct fimc_dev *fimc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ /* Clear for full H/W setup in first run after resume */
+ fimc->m2m.ctx = NULL;
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (test_and_clear_bit(ST_M2M_SUSPENDED, &fimc->state))
+ fimc_m2m_job_finish(fimc->m2m.ctx,
+ VB2_BUF_STATE_ERROR);
return 0;
}
@@ -1598,7 +1592,6 @@ static int fimc_probe(struct platform_device *pdev)
struct samsung_fimc_driverdata *drv_data;
struct s5p_platform_fimc *pdata;
int ret = 0;
- int cap_input_index = -1;
dev_dbg(&pdev->dev, "%s():\n", __func__);
@@ -1616,15 +1609,16 @@ static int fimc_probe(struct platform_device *pdev)
return -ENOMEM;
fimc->id = pdev->id;
+
fimc->variant = drv_data->variant[fimc->id];
fimc->pdev = pdev;
pdata = pdev->dev.platform_data;
fimc->pdata = pdata;
- fimc->state = ST_IDLE;
+
+ set_bit(ST_LPM, &fimc->state);
init_waitqueue_head(&fimc->irq_queue);
spin_lock_init(&fimc->slock);
-
mutex_init(&fimc->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1649,71 +1643,51 @@ static int fimc_probe(struct platform_device *pdev)
goto err_req_region;
}
- fimc->num_clocks = MAX_FIMC_CLOCKS - 1;
-
- /* Check if a video capture node needs to be registered. */
- if (pdata && pdata->num_clients > 0) {
- cap_input_index = 0;
- fimc->num_clocks++;
- }
-
- ret = fimc_clk_get(fimc);
- if (ret)
- goto err_regs_unmap;
- clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
-
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get IRQ resource\n");
ret = -ENXIO;
- goto err_clk;
+ goto err_regs_unmap;
}
fimc->irq = res->start;
- fimc_hw_reset(fimc);
+ fimc->num_clocks = MAX_FIMC_CLOCKS;
+ ret = fimc_clk_get(fimc);
+ if (ret)
+ goto err_regs_unmap;
+ clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
+ clk_enable(fimc->clock[CLK_BUS]);
- ret = request_irq(fimc->irq, fimc_isr, 0, pdev->name, fimc);
+ platform_set_drvdata(pdev, fimc);
+
+ ret = request_irq(fimc->irq, fimc_irq_handler, 0, pdev->name, fimc);
if (ret) {
dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
goto err_clk;
}
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ goto err_irq;
/* Initialize contiguous memory allocator */
- fimc->alloc_ctx = vb2_dma_contig_init_ctx(&fimc->pdev->dev);
+ fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(fimc->alloc_ctx)) {
ret = PTR_ERR(fimc->alloc_ctx);
- goto err_irq;
+ goto err_pm;
}
- ret = fimc_register_m2m_device(fimc);
- if (ret)
- goto err_irq;
-
- /* At least one camera sensor is required to register capture node */
- if (cap_input_index >= 0) {
- ret = fimc_register_capture_device(fimc);
- if (ret)
- goto err_m2m;
- clk_disable(fimc->clock[CLK_CAM]);
- }
- /*
- * Exclude the additional output DMA address registers by masking
- * them out on HW revisions that provide extended capabilites.
- */
- if (fimc->variant->out_buf_count > 4)
- fimc_hw_set_dma_seq(fimc, 0xF);
-
- dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n",
- __func__, fimc->id);
+ dev_dbg(&pdev->dev, "FIMC.%d registered successfully\n", fimc->id);
+ pm_runtime_put(&pdev->dev);
return 0;
-err_m2m:
- fimc_unregister_m2m_device(fimc);
+err_pm:
+ pm_runtime_put(&pdev->dev);
err_irq:
free_irq(fimc->irq, fimc);
err_clk:
- fimc_clk_release(fimc);
+ fimc_clk_put(fimc);
err_regs_unmap:
iounmap(fimc->regs);
err_req_region:
@@ -1721,31 +1695,105 @@ err_req_region:
kfree(fimc->regs_res);
err_info:
kfree(fimc);
+ return ret;
+}
+static int fimc_runtime_resume(struct device *dev)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
+
+ /* Enable clocks and perform basic initalization */
+ clk_enable(fimc->clock[CLK_GATE]);
+ fimc_hw_reset(fimc);
+ if (fimc->variant->out_buf_count > 4)
+ fimc_hw_set_dma_seq(fimc, 0xF);
+
+ /* Resume the capture or mem-to-mem device */
+ if (fimc_capture_busy(fimc))
+ return fimc_capture_resume(fimc);
+ else if (fimc_m2m_pending(fimc))
+ return fimc_m2m_resume(fimc);
+ return 0;
+}
+
+static int fimc_runtime_suspend(struct device *dev)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (fimc_capture_busy(fimc))
+ ret = fimc_capture_suspend(fimc);
+ else
+ ret = fimc_m2m_suspend(fimc);
+ if (!ret)
+ clk_disable(fimc->clock[CLK_GATE]);
+
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
return ret;
}
-static int __devexit fimc_remove(struct platform_device *pdev)
+#ifdef CONFIG_PM_SLEEP
+static int fimc_resume(struct device *dev)
{
- struct fimc_dev *fimc =
- (struct fimc_dev *)platform_get_drvdata(pdev);
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+ unsigned long flags;
- free_irq(fimc->irq, fimc);
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
+
+ /* Do not resume if the device was idle before system suspend */
+ spin_lock_irqsave(&fimc->slock, flags);
+ if (!test_and_clear_bit(ST_LPM, &fimc->state) ||
+ (!fimc_m2m_active(fimc) && !fimc_capture_busy(fimc))) {
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return 0;
+ }
fimc_hw_reset(fimc);
+ if (fimc->variant->out_buf_count > 4)
+ fimc_hw_set_dma_seq(fimc, 0xF);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (fimc_capture_busy(fimc))
+ return fimc_capture_resume(fimc);
- fimc_unregister_m2m_device(fimc);
- fimc_unregister_capture_device(fimc);
+ return fimc_m2m_resume(fimc);
+}
+
+static int fimc_suspend(struct device *dev)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
- fimc_clk_release(fimc);
+ if (test_and_set_bit(ST_LPM, &fimc->state))
+ return 0;
+ if (fimc_capture_busy(fimc))
+ return fimc_capture_suspend(fimc);
+
+ return fimc_m2m_suspend(fimc);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int __devexit fimc_remove(struct platform_device *pdev)
+{
+ struct fimc_dev *fimc = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ fimc_runtime_suspend(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
+ clk_disable(fimc->clock[CLK_BUS]);
+ fimc_clk_put(fimc);
+ free_irq(fimc->irq, fimc);
iounmap(fimc->regs);
release_resource(fimc->regs_res);
kfree(fimc->regs_res);
kfree(fimc);
- dev_info(&pdev->dev, "%s driver unloaded\n", pdev->name);
+ dev_info(&pdev->dev, "driver unloaded\n");
return 0;
}
@@ -1788,6 +1836,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = {
static struct samsung_fimc_variant fimc0_variant_s5p = {
.has_inp_rot = 1,
.has_out_rot = 1,
+ .has_cam_if = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
@@ -1796,6 +1845,7 @@ static struct samsung_fimc_variant fimc0_variant_s5p = {
};
static struct samsung_fimc_variant fimc2_variant_s5p = {
+ .has_cam_if = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
@@ -1807,6 +1857,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv210 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
+ .has_cam_if = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
@@ -1818,6 +1869,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
+ .has_cam_if = 1,
.has_mainscaler_ext = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
@@ -1827,6 +1879,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
};
static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
+ .has_cam_if = 1,
.pix_hoff = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
@@ -1839,22 +1892,24 @@ static struct samsung_fimc_variant fimc0_variant_exynos4 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
+ .has_cam_if = 1,
.has_cistatus2 = 1,
.has_mainscaler_ext = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
- .hor_offs_align = 1,
+ .hor_offs_align = 2,
.out_buf_count = 32,
.pix_limit = &s5p_pix_limit[1],
};
-static struct samsung_fimc_variant fimc2_variant_exynos4 = {
+static struct samsung_fimc_variant fimc3_variant_exynos4 = {
.pix_hoff = 1,
+ .has_cam_if = 1,
.has_cistatus2 = 1,
.has_mainscaler_ext = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
- .hor_offs_align = 1,
+ .hor_offs_align = 2,
.out_buf_count = 32,
.pix_limit = &s5p_pix_limit[3],
};
@@ -1887,7 +1942,7 @@ static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = {
[0] = &fimc0_variant_exynos4,
[1] = &fimc0_variant_exynos4,
[2] = &fimc0_variant_exynos4,
- [3] = &fimc2_variant_exynos4,
+ [3] = &fimc3_variant_exynos4,
},
.num_entities = 4,
.lclk_frequency = 166000000UL,
@@ -1908,32 +1963,28 @@ static struct platform_device_id fimc_driver_ids[] = {
};
MODULE_DEVICE_TABLE(platform, fimc_driver_ids);
+static const struct dev_pm_ops fimc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume)
+ SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL)
+};
+
static struct platform_driver fimc_driver = {
.probe = fimc_probe,
- .remove = __devexit_p(fimc_remove),
+ .remove = __devexit_p(fimc_remove),
.id_table = fimc_driver_ids,
.driver = {
- .name = MODULE_NAME,
+ .name = FIMC_MODULE_NAME,
.owner = THIS_MODULE,
+ .pm = &fimc_pm_ops,
}
};
-static int __init fimc_init(void)
+int __init fimc_register_driver(void)
{
- int ret = platform_driver_register(&fimc_driver);
- if (ret)
- err("platform_driver_register failed: %d\n", ret);
- return ret;
+ return platform_driver_probe(&fimc_driver, fimc_probe);
}
-static void __exit fimc_exit(void)
+void __exit fimc_unregister_driver(void)
{
platform_driver_unregister(&fimc_driver);
}
-
-module_init(fimc_init);
-module_exit(fimc_exit);
-
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
index 1f70772daaf0..a6936dad5b10 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -11,12 +11,16 @@
/*#define DEBUG*/
+#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <linux/io.h>
+
+#include <media/media-entity.h>
#include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-mediabus.h>
@@ -32,38 +36,46 @@
/* Time to wait for next frame VSYNC interrupt while stopping operation. */
#define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
-#define MAX_FIMC_CLOCKS 3
-#define MODULE_NAME "s5p-fimc"
+#define MAX_FIMC_CLOCKS 2
+#define FIMC_MODULE_NAME "s5p-fimc"
#define FIMC_MAX_DEVS 4
#define FIMC_MAX_OUT_BUFS 4
#define SCALER_MAX_HRATIO 64
#define SCALER_MAX_VRATIO 64
#define DMA_MIN_SIZE 8
+#define FIMC_CAMIF_MAX_HEIGHT 0x2000
/* indices to the clocks array */
enum {
CLK_BUS,
CLK_GATE,
- CLK_CAM,
};
enum fimc_dev_flags {
- /* for m2m node */
- ST_IDLE,
- ST_OUTDMA_RUN,
+ ST_LPM,
+ /* m2m node */
+ ST_M2M_RUN,
ST_M2M_PEND,
- /* for capture node */
+ ST_M2M_SUSPENDING,
+ ST_M2M_SUSPENDED,
+ /* capture node */
ST_CAPT_PEND,
ST_CAPT_RUN,
ST_CAPT_STREAM,
+ ST_CAPT_ISP_STREAM,
+ ST_CAPT_SUSPENDED,
ST_CAPT_SHUT,
+ ST_CAPT_BUSY,
+ ST_CAPT_APPLY_CFG,
+ ST_CAPT_JPEG,
};
-#define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state)
+#define fimc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state)
#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
#define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state)
#define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state)
+#define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state)
enum fimc_datapath {
FIMC_CAMERA,
@@ -83,9 +95,14 @@ enum fimc_color_fmt {
S5P_FIMC_CBYCRY422,
S5P_FIMC_CRYCBY422,
S5P_FIMC_YCBCR444_LOCAL,
+ S5P_FIMC_JPEG = 0x40,
};
-#define fimc_fmt_is_rgb(x) ((x) & 0x10)
+#define fimc_fmt_is_rgb(x) (!!((x) & 0x10))
+#define fimc_fmt_is_jpeg(x) (!!((x) & 0x40))
+
+#define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \
+ __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */
#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
@@ -104,9 +121,10 @@ enum fimc_color_fmt {
#define FIMC_DST_ADDR (1 << 2)
#define FIMC_SRC_FMT (1 << 3)
#define FIMC_DST_FMT (1 << 4)
-#define FIMC_CTX_M2M (1 << 5)
-#define FIMC_CTX_CAP (1 << 6)
-#define FIMC_CTX_SHUT (1 << 7)
+#define FIMC_DST_CROP (1 << 5)
+#define FIMC_CTX_M2M (1 << 16)
+#define FIMC_CTX_CAP (1 << 17)
+#define FIMC_CTX_SHUT (1 << 18)
/* Image conversion flags */
#define FIMC_IN_DMA_ACCESS_TILED (1 << 0)
@@ -122,11 +140,6 @@ enum fimc_color_fmt {
/* Y (16 ~ 235), Cb/Cr (16 ~ 240) */
#define FIMC_COLOR_RANGE_NARROW (1 << 3)
-#define FLIP_NONE 0
-#define FLIP_X_AXIS 1
-#define FLIP_Y_AXIS 2
-#define FLIP_XY_AXIS (FLIP_X_AXIS | FLIP_Y_AXIS)
-
/**
* struct fimc_fmt - the driver's internal color format data
* @mbus_code: Media Bus pixel code, -1 if not applicable
@@ -275,26 +288,29 @@ struct fimc_frame {
/**
* struct fimc_m2m_device - v4l2 memory-to-memory device data
* @vfd: the video device node for v4l2 m2m mode
- * @v4l2_dev: v4l2 device for m2m mode
* @m2m_dev: v4l2 memory-to-memory device data
* @ctx: hardware context data
* @refcnt: the reference counter
*/
struct fimc_m2m_device {
struct video_device *vfd;
- struct v4l2_device v4l2_dev;
struct v4l2_m2m_dev *m2m_dev;
struct fimc_ctx *ctx;
int refcnt;
};
+#define FIMC_SD_PAD_SINK 0
+#define FIMC_SD_PAD_SOURCE 1
+#define FIMC_SD_PADS_NUM 2
+
/**
* struct fimc_vid_cap - camera capture device information
* @ctx: hardware context data
* @vfd: video device node for camera capture mode
- * @v4l2_dev: v4l2_device struct to manage subdevs
- * @sd: pointer to camera sensor subdevice currently in use
- * @fmt: Media Bus format configured at selected image sensor
+ * @subdev: subdev exposing the FIMC processing block
+ * @vd_pad: fimc video capture node pad
+ * @sd_pads: fimc video processing block pads
+ * @mf: media bus format at the FIMC camera input (and the scaler output) pad
* @pending_buf_q: the pending buffer queue head
* @active_buf_q: the queue head of buffers scheduled in hardware
* @vbq: the capture am video buffer queue
@@ -304,14 +320,17 @@ struct fimc_m2m_device {
* @reqbufs_count: the number of buffers requested in REQBUFS ioctl
* @input_index: input (camera sensor) index
* @refcnt: driver's private reference counter
+ * @input: capture input type, grp_id of the attached subdev
+ * @user_subdev_api: true if subdevs are not configured by the host driver
*/
struct fimc_vid_cap {
struct fimc_ctx *ctx;
struct vb2_alloc_ctx *alloc_ctx;
struct video_device *vfd;
- struct v4l2_device v4l2_dev;
- struct v4l2_subdev *sd;;
- struct v4l2_mbus_framefmt fmt;
+ struct v4l2_subdev *subdev;
+ struct media_pad vd_pad;
+ struct v4l2_mbus_framefmt mf;
+ struct media_pad sd_pads[FIMC_SD_PADS_NUM];
struct list_head pending_buf_q;
struct list_head active_buf_q;
struct vb2_queue vbq;
@@ -321,6 +340,8 @@ struct fimc_vid_cap {
unsigned int reqbufs_count;
int input_index;
int refcnt;
+ u32 input;
+ bool user_subdev_api;
};
/**
@@ -351,6 +372,7 @@ struct fimc_pix_limit {
* @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision
* @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register
* are present in this IP revision
+ * @has_cam_if: set if this instance has a camera input interface
* @pix_limit: pixel size constraints for the scaler
* @min_inp_pixsize: minimum input pixel size
* @min_out_pixsize: minimum output pixel size
@@ -363,6 +385,7 @@ struct samsung_fimc_variant {
unsigned int has_out_rot:1;
unsigned int has_cistatus2:1;
unsigned int has_mainscaler_ext:1;
+ unsigned int has_cam_if:1;
struct fimc_pix_limit *pix_limit;
u16 min_inp_pixsize;
u16 min_out_pixsize;
@@ -383,6 +406,12 @@ struct samsung_fimc_driverdata {
int num_entities;
};
+struct fimc_pipeline {
+ struct media_pipeline *pipe;
+ struct v4l2_subdev *sensor;
+ struct v4l2_subdev *csis;
+};
+
struct fimc_ctx;
/**
@@ -399,10 +428,12 @@ struct fimc_ctx;
* @regs_res: the resource claimed for IO registers
* @irq: FIMC interrupt number
* @irq_queue: interrupt handler waitqueue
+ * @v4l2_dev: root v4l2_device
* @m2m: memory-to-memory V4L2 device information
* @vid_cap: camera capture device information
* @state: flags used to synchronize m2m and capture mode operation
* @alloc_ctx: videobuf2 memory allocator context
+ * @pipeline: fimc video capture pipeline data structure
*/
struct fimc_dev {
spinlock_t slock;
@@ -417,10 +448,12 @@ struct fimc_dev {
struct resource *regs_res;
int irq;
wait_queue_head_t irq_queue;
+ struct v4l2_device *v4l2_dev;
struct fimc_m2m_device m2m;
struct fimc_vid_cap vid_cap;
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
+ struct fimc_pipeline pipeline;
};
/**
@@ -437,11 +470,18 @@ struct fimc_dev {
* @scaler: image scaler properties
* @effect: image effect
* @rotation: image clockwise rotation in degrees
- * @flip: image flip mode
+ * @hflip: indicates image horizontal flip if set
+ * @vflip: indicates image vertical flip if set
* @flags: additional flags for image conversion
* @state: flags to keep track of user configuration
* @fimc_dev: the FIMC device this context applies to
* @m2m_ctx: memory-to-memory device context
+ * @fh: v4l2 file handle
+ * @ctrl_handler: v4l2 controls handler
+ * @ctrl_rotate image rotation control
+ * @ctrl_hflip horizontal flip control
+ * @ctrl_vflip vartical flip control
+ * @ctrls_rdy: true if the control handler is initialized
*/
struct fimc_ctx {
spinlock_t slock;
@@ -456,13 +496,49 @@ struct fimc_ctx {
struct fimc_scaler scaler;
struct fimc_effect effect;
int rotation;
- u32 flip;
+ unsigned int hflip:1;
+ unsigned int vflip:1;
u32 flags;
u32 state;
struct fimc_dev *fimc_dev;
struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_fh fh;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *ctrl_rotate;
+ struct v4l2_ctrl *ctrl_hflip;
+ struct v4l2_ctrl *ctrl_vflip;
+ bool ctrls_rdy;
};
+#define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh)
+
+static inline void set_frame_bounds(struct fimc_frame *f, u32 width, u32 height)
+{
+ f->o_width = width;
+ f->o_height = height;
+ f->f_width = width;
+ f->f_height = height;
+}
+
+static inline void set_frame_crop(struct fimc_frame *f,
+ u32 left, u32 top, u32 width, u32 height)
+{
+ f->offs_h = left;
+ f->offs_v = top;
+ f->width = width;
+ f->height = height;
+}
+
+static inline u32 fimc_get_format_depth(struct fimc_fmt *ff)
+{
+ u32 i, depth = 0;
+
+ if (ff != NULL)
+ for (i = 0; i < ff->colplanes; i++)
+ depth += ff->depth[i];
+ return depth;
+}
+
static inline bool fimc_capture_active(struct fimc_dev *fimc)
{
unsigned long flags;
@@ -561,7 +637,7 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
} else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
frame = &ctx->d_frame;
} else {
- v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ v4l2_err(ctx->fimc_dev->v4l2_dev,
"Wrong buffer/video queue type (%d)\n", type);
return ERR_PTR(-EINVAL);
}
@@ -595,7 +671,7 @@ void fimc_hw_en_irq(struct fimc_dev *fimc, int enable);
void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
void fimc_hw_set_mainscaler(struct fimc_ctx *ctx);
void fimc_hw_en_capture(struct fimc_ctx *ctx);
-void fimc_hw_set_effect(struct fimc_ctx *ctx);
+void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active);
void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
void fimc_hw_set_input_path(struct fimc_ctx *ctx);
void fimc_hw_set_output_path(struct fimc_ctx *ctx);
@@ -614,36 +690,45 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
/* fimc-core.c */
int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f);
-int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv,
- struct v4l2_format *f);
-int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv,
- struct v4l2_format *f);
-int fimc_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc);
-int fimc_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl);
-
-int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr);
-int check_ctrl_val(struct fimc_ctx *ctx, struct v4l2_control *ctrl);
-int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl);
-
-struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask);
-struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f,
- unsigned int mask);
-
-int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot);
+int fimc_ctrls_create(struct fimc_ctx *ctx);
+void fimc_ctrls_delete(struct fimc_ctx *ctx);
+void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active);
+int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f);
+void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
+ struct v4l2_pix_format_mplane *pix);
+struct fimc_fmt *fimc_find_format(u32 *pixelformat, u32 *mbus_code,
+ unsigned int mask, int index);
+
+int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
+ int dw, int dh, int rotation);
int fimc_set_scaler_info(struct fimc_ctx *ctx);
int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags);
int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
struct fimc_frame *frame, struct fimc_addr *paddr);
+void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f);
+void fimc_set_yuv_order(struct fimc_ctx *ctx);
+void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f);
+void fimc_capture_irq_handler(struct fimc_dev *fimc, bool done);
+
+int fimc_register_m2m_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev);
+void fimc_unregister_m2m_device(struct fimc_dev *fimc);
+int fimc_register_driver(void);
+void fimc_unregister_driver(void);
/* -----------------------------------------------------*/
/* fimc-capture.c */
-int fimc_register_capture_device(struct fimc_dev *fimc);
+int fimc_register_capture_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev);
void fimc_unregister_capture_device(struct fimc_dev *fimc);
-int fimc_sensor_sd_init(struct fimc_dev *fimc, int index);
+int fimc_capture_ctrls_create(struct fimc_dev *fimc);
int fimc_vid_cap_buf_queue(struct fimc_dev *fimc,
struct fimc_vid_buffer *fimc_vb);
+void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg);
+int fimc_capture_suspend(struct fimc_dev *fimc);
+int fimc_capture_resume(struct fimc_dev *fimc);
+int fimc_capture_config_update(struct fimc_ctx *ctx);
/* Locking: the caller holds fimc->slock */
static inline void fimc_activate_capture(struct fimc_ctx *ctx)
@@ -661,22 +746,27 @@ static inline void fimc_deactivate_capture(struct fimc_dev *fimc)
}
/*
- * Add buf to the capture active buffers queue.
- * Locking: Need to be called with fimc_dev::slock held.
+ * Buffer list manipulation functions. Must be called with fimc.slock held.
*/
-static inline void active_queue_add(struct fimc_vid_cap *vid_cap,
- struct fimc_vid_buffer *buf)
+
+/**
+ * fimc_active_queue_add - add buffer to the capture active buffers queue
+ * @buf: buffer to add to the active buffers list
+ */
+static inline void fimc_active_queue_add(struct fimc_vid_cap *vid_cap,
+ struct fimc_vid_buffer *buf)
{
list_add_tail(&buf->list, &vid_cap->active_buf_q);
vid_cap->active_buf_cnt++;
}
-/*
- * Pop a video buffer from the capture active buffers queue
- * Locking: Need to be called with fimc_dev::slock held.
+/**
+ * fimc_active_queue_pop - pop buffer from the capture active buffers queue
+ *
+ * The caller must assure the active_buf_q list is not empty.
*/
-static inline struct fimc_vid_buffer *
-active_queue_pop(struct fimc_vid_cap *vid_cap)
+static inline struct fimc_vid_buffer *fimc_active_queue_pop(
+ struct fimc_vid_cap *vid_cap)
{
struct fimc_vid_buffer *buf;
buf = list_entry(vid_cap->active_buf_q.next,
@@ -686,16 +776,23 @@ active_queue_pop(struct fimc_vid_cap *vid_cap)
return buf;
}
-/* Add video buffer to the capture pending buffers queue */
+/**
+ * fimc_pending_queue_add - add buffer to the capture pending buffers queue
+ * @buf: buffer to add to the pending buffers list
+ */
static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap,
struct fimc_vid_buffer *buf)
{
list_add_tail(&buf->list, &vid_cap->pending_buf_q);
}
-/* Add video buffer to the capture pending buffers queue */
-static inline struct fimc_vid_buffer *
-pending_queue_pop(struct fimc_vid_cap *vid_cap)
+/**
+ * fimc_pending_queue_pop - pop buffer from the capture pending buffers queue
+ *
+ * The caller must assure the pending_buf_q list is not empty.
+ */
+static inline struct fimc_vid_buffer *fimc_pending_queue_pop(
+ struct fimc_vid_cap *vid_cap)
{
struct fimc_vid_buffer *buf;
buf = list_entry(vid_cap->pending_buf_q.next,
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c
new file mode 100644
index 000000000000..cc337b1de913
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c
@@ -0,0 +1,858 @@
+/*
+ * S5P/EXYNOS4 SoC series camera host interface media device driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.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/bug.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <media/v4l2-ctrls.h>
+#include <media/media-device.h>
+
+#include "fimc-core.h"
+#include "fimc-mdevice.h"
+#include "mipi-csis.h"
+
+static int __fimc_md_set_camclk(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info,
+ bool on);
+/**
+ * fimc_pipeline_prepare - update pipeline information with subdevice pointers
+ * @fimc: fimc device terminating the pipeline
+ *
+ * Caller holds the graph mutex.
+ */
+void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me)
+{
+ struct media_entity_graph graph;
+ struct v4l2_subdev *sd;
+
+ media_entity_graph_walk_start(&graph, me);
+
+ while ((me = media_entity_graph_walk_next(&graph))) {
+ if (media_entity_type(me) != MEDIA_ENT_T_V4L2_SUBDEV)
+ continue;
+ sd = media_entity_to_v4l2_subdev(me);
+
+ if (sd->grp_id == SENSOR_GROUP_ID)
+ fimc->pipeline.sensor = sd;
+ else if (sd->grp_id == CSIS_GROUP_ID)
+ fimc->pipeline.csis = sd;
+ }
+}
+
+/**
+ * __subdev_set_power - change power state of a single subdev
+ * @sd: subdevice to change power state for
+ * @on: 1 to enable power or 0 to disable
+ *
+ * Return result of s_power subdev operation or -ENXIO if sd argument
+ * is NULL. Return 0 if the subdevice does not implement s_power.
+ */
+static int __subdev_set_power(struct v4l2_subdev *sd, int on)
+{
+ int *use_count;
+ int ret;
+
+ if (sd == NULL)
+ return -ENXIO;
+
+ use_count = &sd->entity.use_count;
+ if (on && (*use_count)++ > 0)
+ return 0;
+ else if (!on && (*use_count == 0 || --(*use_count) > 0))
+ return 0;
+ ret = v4l2_subdev_call(sd, core, s_power, on);
+
+ return ret != -ENOIOCTLCMD ? ret : 0;
+}
+
+/**
+ * fimc_pipeline_s_power - change power state of all pipeline subdevs
+ * @fimc: fimc device terminating the pipeline
+ * @state: 1 to enable power or 0 for power down
+ *
+ * Need to be called with the graph mutex held.
+ */
+int fimc_pipeline_s_power(struct fimc_dev *fimc, int state)
+{
+ int ret = 0;
+
+ if (fimc->pipeline.sensor == NULL)
+ return -ENXIO;
+
+ if (state) {
+ ret = __subdev_set_power(fimc->pipeline.csis, 1);
+ if (ret && ret != -ENXIO)
+ return ret;
+ return __subdev_set_power(fimc->pipeline.sensor, 1);
+ }
+
+ ret = __subdev_set_power(fimc->pipeline.sensor, 0);
+ if (ret)
+ return ret;
+ ret = __subdev_set_power(fimc->pipeline.csis, 0);
+
+ return ret == -ENXIO ? 0 : ret;
+}
+
+/**
+ * __fimc_pipeline_initialize - update the pipeline information, enable power
+ * of all pipeline subdevs and the sensor clock
+ * @me: media entity to start graph walk with
+ * @prep: true to acquire sensor (and csis) subdevs
+ *
+ * This function must be called with the graph mutex held.
+ */
+static int __fimc_pipeline_initialize(struct fimc_dev *fimc,
+ struct media_entity *me, bool prep)
+{
+ int ret;
+
+ if (prep)
+ fimc_pipeline_prepare(fimc, me);
+ if (fimc->pipeline.sensor == NULL)
+ return -EINVAL;
+ ret = fimc_md_set_camclk(fimc->pipeline.sensor, true);
+ if (ret)
+ return ret;
+ return fimc_pipeline_s_power(fimc, 1);
+}
+
+int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
+ bool prep)
+{
+ int ret;
+
+ mutex_lock(&me->parent->graph_mutex);
+ ret = __fimc_pipeline_initialize(fimc, me, prep);
+ mutex_unlock(&me->parent->graph_mutex);
+
+ return ret;
+}
+
+/**
+ * __fimc_pipeline_shutdown - disable the sensor clock and pipeline power
+ * @fimc: fimc device terminating the pipeline
+ *
+ * Disable power of all subdevs in the pipeline and turn off the external
+ * sensor clock.
+ * Called with the graph mutex held.
+ */
+int __fimc_pipeline_shutdown(struct fimc_dev *fimc)
+{
+ int ret = 0;
+
+ if (fimc->pipeline.sensor) {
+ ret = fimc_pipeline_s_power(fimc, 0);
+ fimc_md_set_camclk(fimc->pipeline.sensor, false);
+ }
+ return ret == -ENXIO ? 0 : ret;
+}
+
+int fimc_pipeline_shutdown(struct fimc_dev *fimc)
+{
+ struct media_entity *me = &fimc->vid_cap.vfd->entity;
+ int ret;
+
+ mutex_lock(&me->parent->graph_mutex);
+ ret = __fimc_pipeline_shutdown(fimc);
+ mutex_unlock(&me->parent->graph_mutex);
+
+ return ret;
+}
+
+/**
+ * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs
+ * @fimc: fimc device terminating the pipeline
+ * @on: passed as the s_stream call argument
+ */
+int fimc_pipeline_s_stream(struct fimc_dev *fimc, int on)
+{
+ struct fimc_pipeline *p = &fimc->pipeline;
+ int ret = 0;
+
+ if (p->sensor == NULL)
+ return -ENODEV;
+
+ if ((on && p->csis) || !on)
+ ret = v4l2_subdev_call(on ? p->csis : p->sensor,
+ video, s_stream, on);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+ if ((!on && p->csis) || on)
+ ret = v4l2_subdev_call(on ? p->sensor : p->csis,
+ video, s_stream, on);
+ return ret == -ENOIOCTLCMD ? 0 : ret;
+}
+
+/*
+ * Sensor subdevice helper functions
+ */
+static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info)
+{
+ struct i2c_adapter *adapter;
+ struct v4l2_subdev *sd = NULL;
+
+ if (!s_info || !fmd)
+ return NULL;
+
+ adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num);
+ if (!adapter)
+ return NULL;
+ sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
+ s_info->pdata->board_info, NULL);
+ if (IS_ERR_OR_NULL(sd)) {
+ v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n");
+ return NULL;
+ }
+ v4l2_set_subdev_hostdata(sd, s_info);
+ sd->grp_id = SENSOR_GROUP_ID;
+
+ v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
+ s_info->pdata->board_info->type);
+ return sd;
+}
+
+static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!client)
+ return;
+ v4l2_device_unregister_subdev(sd);
+ i2c_unregister_device(client);
+ i2c_put_adapter(client->adapter);
+}
+
+static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
+{
+ struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
+ struct fimc_dev *fd = NULL;
+ int num_clients, ret, i;
+
+ /*
+ * Runtime resume one of the FIMC entities to make sure
+ * the sclk_cam clocks are not globally disabled.
+ */
+ for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++)
+ if (fmd->fimc[i])
+ fd = fmd->fimc[i];
+ if (!fd)
+ return -ENXIO;
+ ret = pm_runtime_get_sync(&fd->pdev->dev);
+ if (ret < 0)
+ return ret;
+
+ WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
+ num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor));
+
+ fmd->num_sensors = num_clients;
+ for (i = 0; i < num_clients; i++) {
+ fmd->sensor[i].pdata = &pdata->isp_info[i];
+ ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
+ if (ret)
+ break;
+ fmd->sensor[i].subdev =
+ fimc_md_register_sensor(fmd, &fmd->sensor[i]);
+ ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
+ if (ret)
+ break;
+ }
+ pm_runtime_put(&fd->pdev->dev);
+ return ret;
+}
+
+/*
+ * MIPI CSIS and FIMC platform devices registration.
+ */
+static int fimc_register_callback(struct device *dev, void *p)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+ struct fimc_md *fmd = p;
+ int ret;
+
+ if (!fimc || !fimc->pdev)
+ return 0;
+ if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS)
+ return 0;
+
+ fmd->fimc[fimc->pdev->id] = fimc;
+ ret = fimc_register_m2m_device(fimc, &fmd->v4l2_dev);
+ if (ret)
+ return ret;
+ ret = fimc_register_capture_device(fimc, &fmd->v4l2_dev);
+ if (!ret)
+ fimc->vid_cap.user_subdev_api = fmd->user_subdev_api;
+ return ret;
+}
+
+static int csis_register_callback(struct device *dev, void *p)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct platform_device *pdev;
+ struct fimc_md *fmd = p;
+ int id, ret;
+
+ if (!sd)
+ return 0;
+ pdev = v4l2_get_subdevdata(sd);
+ if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES)
+ return 0;
+ v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name);
+
+ id = pdev->id < 0 ? 0 : pdev->id;
+ fmd->csis[id].sd = sd;
+ sd->grp_id = CSIS_GROUP_ID;
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (ret)
+ v4l2_err(&fmd->v4l2_dev,
+ "Failed to register CSIS subdevice: %d\n", ret);
+ return ret;
+}
+
+/**
+ * fimc_md_register_platform_entities - register FIMC and CSIS media entities
+ */
+static int fimc_md_register_platform_entities(struct fimc_md *fmd)
+{
+ struct device_driver *driver;
+ int ret;
+
+ driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
+ if (!driver)
+ return -ENODEV;
+ ret = driver_for_each_device(driver, NULL, fmd,
+ fimc_register_callback);
+ put_driver(driver);
+ if (ret)
+ return ret;
+
+ driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
+ if (driver) {
+ ret = driver_for_each_device(driver, NULL, fmd,
+ csis_register_callback);
+ put_driver(driver);
+ }
+ return ret;
+}
+
+static void fimc_md_unregister_entities(struct fimc_md *fmd)
+{
+ int i;
+
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (fmd->fimc[i] == NULL)
+ continue;
+ fimc_unregister_m2m_device(fmd->fimc[i]);
+ fimc_unregister_capture_device(fmd->fimc[i]);
+ fmd->fimc[i] = NULL;
+ }
+ for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
+ if (fmd->csis[i].sd == NULL)
+ continue;
+ v4l2_device_unregister_subdev(fmd->csis[i].sd);
+ fmd->csis[i].sd = NULL;
+ }
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+ fimc_md_unregister_sensor(fmd->sensor[i].subdev);
+ fmd->sensor[i].subdev = NULL;
+ }
+}
+
+static int fimc_md_register_video_nodes(struct fimc_md *fmd)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < FIMC_MAX_DEVS && !ret; i++) {
+ if (!fmd->fimc[i])
+ continue;
+
+ if (fmd->fimc[i]->m2m.vfd)
+ ret = video_register_device(fmd->fimc[i]->m2m.vfd,
+ VFL_TYPE_GRABBER, -1);
+ if (ret)
+ break;
+ if (fmd->fimc[i]->vid_cap.vfd)
+ ret = video_register_device(fmd->fimc[i]->vid_cap.vfd,
+ VFL_TYPE_GRABBER, -1);
+ }
+
+ return ret;
+}
+
+/**
+ * __fimc_md_create_fimc_links - create links to all FIMC entities
+ * @fmd: fimc media device
+ * @source: the source entity to create links to all fimc entities from
+ * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null
+ * @pad: the source entity pad index
+ * @fimc_id: index of the fimc device for which link should be enabled
+ */
+static int __fimc_md_create_fimc_links(struct fimc_md *fmd,
+ struct media_entity *source,
+ struct v4l2_subdev *sensor,
+ int pad, int fimc_id)
+{
+ struct fimc_sensor_info *s_info;
+ struct media_entity *sink;
+ unsigned int flags;
+ int ret, i;
+
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (!fmd->fimc[i])
+ break;
+ /*
+ * Some FIMC variants are not fitted with camera capture
+ * interface. Skip creating a link from sensor for those.
+ */
+ if (sensor->grp_id == SENSOR_GROUP_ID &&
+ !fmd->fimc[i]->variant->has_cam_if)
+ continue;
+
+ flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0;
+ sink = &fmd->fimc[i]->vid_cap.subdev->entity;
+ ret = media_entity_create_link(source, pad, sink,
+ FIMC_SD_PAD_SINK, flags);
+ if (ret)
+ return ret;
+
+ /* Notify FIMC capture subdev entity */
+ ret = media_entity_call(sink, link_setup, &sink->pads[0],
+ &source->pads[pad], flags);
+ if (ret)
+ break;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
+ source->name, flags ? '=' : '-', sink->name);
+
+ if (flags == 0)
+ continue;
+ s_info = v4l2_get_subdev_hostdata(sensor);
+ if (!WARN_ON(s_info == NULL)) {
+ unsigned long irq_flags;
+ spin_lock_irqsave(&fmd->slock, irq_flags);
+ s_info->host = fmd->fimc[i];
+ spin_unlock_irqrestore(&fmd->slock, irq_flags);
+ }
+ }
+ return 0;
+}
+
+/**
+ * fimc_md_create_links - create default links between registered entities
+ *
+ * Parallel interface sensor entities are connected directly to FIMC capture
+ * entities. The sensors using MIPI CSIS bus are connected through immutable
+ * link with CSI receiver entity specified by mux_id. Any registered CSIS
+ * entity has a link to each registered FIMC capture entity. Enabled links
+ * are created by default between each subsequent registered sensor and
+ * subsequent FIMC capture entity. The number of default active links is
+ * determined by the number of available sensors or FIMC entities,
+ * whichever is less.
+ */
+static int fimc_md_create_links(struct fimc_md *fmd)
+{
+ struct v4l2_subdev *sensor, *csis;
+ struct s5p_fimc_isp_info *pdata;
+ struct fimc_sensor_info *s_info;
+ struct media_entity *source, *sink;
+ int i, pad, fimc_id = 0;
+ int ret = 0;
+ u32 flags;
+
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+
+ sensor = fmd->sensor[i].subdev;
+ s_info = v4l2_get_subdev_hostdata(sensor);
+ if (!s_info || !s_info->pdata)
+ continue;
+
+ source = NULL;
+ pdata = s_info->pdata;
+
+ switch (pdata->bus_type) {
+ case FIMC_MIPI_CSI2:
+ if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES,
+ "Wrong CSI channel id: %d\n", pdata->mux_id))
+ return -EINVAL;
+
+ csis = fmd->csis[pdata->mux_id].sd;
+ if (WARN(csis == NULL,
+ "MIPI-CSI interface specified "
+ "but s5p-csis module is not loaded!\n"))
+ continue;
+
+ ret = media_entity_create_link(&sensor->entity, 0,
+ &csis->entity, CSIS_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]",
+ sensor->entity.name, csis->entity.name);
+
+ source = &csis->entity;
+ pad = CSIS_PAD_SOURCE;
+ break;
+
+ case FIMC_ITU_601...FIMC_ITU_656:
+ source = &sensor->entity;
+ pad = 0;
+ break;
+
+ default:
+ v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n",
+ pdata->bus_type);
+ return -EINVAL;
+ }
+ if (source == NULL)
+ continue;
+
+ ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad,
+ fimc_id++);
+ }
+ /* Create immutable links between each FIMC's subdev and video node */
+ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (!fmd->fimc[i])
+ continue;
+ source = &fmd->fimc[i]->vid_cap.subdev->entity;
+ sink = &fmd->fimc[i]->vid_cap.vfd->entity;
+ ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
+ sink, 0, flags);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * The peripheral sensor clock management.
+ */
+static int fimc_md_get_clocks(struct fimc_md *fmd)
+{
+ char clk_name[32];
+ struct clk *clock;
+ int i;
+
+ for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
+ snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);
+ clock = clk_get(NULL, clk_name);
+ if (IS_ERR_OR_NULL(clock)) {
+ v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s",
+ clk_name);
+ return -ENXIO;
+ }
+ fmd->camclk[i].clock = clock;
+ }
+ return 0;
+}
+
+static void fimc_md_put_clocks(struct fimc_md *fmd)
+{
+ int i = FIMC_MAX_CAMCLKS;
+
+ while (--i >= 0) {
+ if (IS_ERR_OR_NULL(fmd->camclk[i].clock))
+ continue;
+ clk_put(fmd->camclk[i].clock);
+ fmd->camclk[i].clock = NULL;
+ }
+}
+
+static int __fimc_md_set_camclk(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info,
+ bool on)
+{
+ struct s5p_fimc_isp_info *pdata = s_info->pdata;
+ struct fimc_camclk_info *camclk;
+ int ret = 0;
+
+ if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL)
+ return -EINVAL;
+
+ if (s_info->clk_on == on)
+ return 0;
+ camclk = &fmd->camclk[pdata->clk_id];
+
+ dbg("camclk %d, f: %lu, clk: %p, on: %d",
+ pdata->clk_id, pdata->clk_frequency, camclk, on);
+
+ if (on) {
+ if (camclk->use_count > 0 &&
+ camclk->frequency != pdata->clk_frequency)
+ return -EINVAL;
+
+ if (camclk->use_count++ == 0) {
+ clk_set_rate(camclk->clock, pdata->clk_frequency);
+ camclk->frequency = pdata->clk_frequency;
+ ret = clk_enable(camclk->clock);
+ }
+ s_info->clk_on = 1;
+ dbg("Enabled camclk %d: f: %lu", pdata->clk_id,
+ clk_get_rate(camclk->clock));
+
+ return ret;
+ }
+
+ if (WARN_ON(camclk->use_count == 0))
+ return 0;
+
+ if (--camclk->use_count == 0) {
+ clk_disable(camclk->clock);
+ s_info->clk_on = 0;
+ dbg("Disabled camclk %d", pdata->clk_id);
+ }
+ return ret;
+}
+
+/**
+ * fimc_md_set_camclk - peripheral sensor clock setup
+ * @sd: sensor subdev to configure sclk_cam clock for
+ * @on: 1 to enable or 0 to disable the clock
+ *
+ * There are 2 separate clock outputs available in the SoC for external
+ * image processors. These clocks are shared between all registered FIMC
+ * devices to which sensors can be attached, either directly or through
+ * the MIPI CSI receiver. The clock is allowed here to be used by
+ * multiple sensors concurrently if they use same frequency.
+ * The per sensor subdev clk_on attribute helps to synchronize accesses
+ * to the sclk_cam clocks from the video and media device nodes.
+ * This function should only be called when the graph mutex is held.
+ */
+int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
+{
+ struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd);
+ struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
+
+ return __fimc_md_set_camclk(fmd, s_info, on);
+}
+
+static int fimc_md_link_notify(struct media_pad *source,
+ struct media_pad *sink, u32 flags)
+{
+ struct v4l2_subdev *sd;
+ struct fimc_dev *fimc;
+ int ret = 0;
+
+ if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return 0;
+
+ sd = media_entity_to_v4l2_subdev(sink->entity);
+ fimc = v4l2_get_subdevdata(sd);
+
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ret = __fimc_pipeline_shutdown(fimc);
+ fimc->pipeline.sensor = NULL;
+ fimc->pipeline.csis = NULL;
+
+ mutex_lock(&fimc->lock);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ mutex_unlock(&fimc->lock);
+ return ret;
+ }
+ /*
+ * Link activation. Enable power of pipeline elements only if the
+ * pipeline is already in use, i.e. its video node is opened.
+ * Recreate the controls destroyed during the link deactivation.
+ */
+ mutex_lock(&fimc->lock);
+ if (fimc->vid_cap.refcnt > 0) {
+ ret = __fimc_pipeline_initialize(fimc, source->entity, true);
+ if (!ret)
+ ret = fimc_capture_ctrls_create(fimc);
+ }
+ mutex_unlock(&fimc->lock);
+
+ return ret ? -EPIPE : ret;
+}
+
+static ssize_t fimc_md_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+
+ if (fmd->user_subdev_api)
+ return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE);
+
+ return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE);
+}
+
+static ssize_t fimc_md_sysfs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+ bool subdev_api;
+ int i;
+
+ if (!strcmp(buf, "vid-dev\n"))
+ subdev_api = false;
+ else if (!strcmp(buf, "sub-dev\n"))
+ subdev_api = true;
+ else
+ return count;
+
+ fmd->user_subdev_api = subdev_api;
+ for (i = 0; i < FIMC_MAX_DEVS; i++)
+ if (fmd->fimc[i])
+ fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api;
+ return count;
+}
+/*
+ * This device attribute is to select video pipeline configuration method.
+ * There are following valid values:
+ * vid-dev - for V4L2 video node API only, subdevice will be configured
+ * by the host driver.
+ * sub-dev - for media controller API, subdevs must be configured in user
+ * space before starting streaming.
+ */
+static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
+ fimc_md_sysfs_show, fimc_md_sysfs_store);
+
+static int __devinit fimc_md_probe(struct platform_device *pdev)
+{
+ struct v4l2_device *v4l2_dev;
+ struct fimc_md *fmd;
+ int ret;
+
+ if (WARN(!pdev->dev.platform_data, "Platform data not specified!\n"))
+ return -EINVAL;
+
+ fmd = kzalloc(sizeof(struct fimc_md), GFP_KERNEL);
+ if (!fmd)
+ return -ENOMEM;
+
+ spin_lock_init(&fmd->slock);
+ fmd->pdev = pdev;
+
+ strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
+ sizeof(fmd->media_dev.model));
+ fmd->media_dev.link_notify = fimc_md_link_notify;
+ fmd->media_dev.dev = &pdev->dev;
+
+ v4l2_dev = &fmd->v4l2_dev;
+ v4l2_dev->mdev = &fmd->media_dev;
+ v4l2_dev->notify = fimc_sensor_notify;
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s",
+ dev_name(&pdev->dev));
+
+ ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
+ goto err1;
+ }
+ ret = media_device_register(&fmd->media_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
+ goto err2;
+ }
+ ret = fimc_md_get_clocks(fmd);
+ if (ret)
+ goto err3;
+
+ fmd->user_subdev_api = false;
+ ret = fimc_md_register_platform_entities(fmd);
+ if (ret)
+ goto err3;
+
+ ret = fimc_md_register_sensor_entities(fmd);
+ if (ret)
+ goto err3;
+ ret = fimc_md_create_links(fmd);
+ if (ret)
+ goto err3;
+ ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
+ if (ret)
+ goto err3;
+ ret = fimc_md_register_video_nodes(fmd);
+ if (ret)
+ goto err3;
+
+ ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ if (!ret) {
+ platform_set_drvdata(pdev, fmd);
+ return 0;
+ }
+err3:
+ media_device_unregister(&fmd->media_dev);
+ fimc_md_put_clocks(fmd);
+ fimc_md_unregister_entities(fmd);
+err2:
+ v4l2_device_unregister(&fmd->v4l2_dev);
+err1:
+ kfree(fmd);
+ return ret;
+}
+
+static int __devexit fimc_md_remove(struct platform_device *pdev)
+{
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+
+ if (!fmd)
+ return 0;
+ device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ fimc_md_unregister_entities(fmd);
+ media_device_unregister(&fmd->media_dev);
+ fimc_md_put_clocks(fmd);
+ kfree(fmd);
+ return 0;
+}
+
+static struct platform_driver fimc_md_driver = {
+ .probe = fimc_md_probe,
+ .remove = __devexit_p(fimc_md_remove),
+ .driver = {
+ .name = "s5p-fimc-md",
+ .owner = THIS_MODULE,
+ }
+};
+
+int __init fimc_md_init(void)
+{
+ int ret;
+ request_module("s5p-csis");
+ ret = fimc_register_driver();
+ if (ret)
+ return ret;
+ return platform_driver_register(&fimc_md_driver);
+}
+void __exit fimc_md_exit(void)
+{
+ platform_driver_unregister(&fimc_md_driver);
+ fimc_unregister_driver();
+}
+
+module_init(fimc_md_init);
+module_exit(fimc_md_exit);
+
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("2.0.1");
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h
new file mode 100644
index 000000000000..da3780823e7d
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_MDEVICE_H_
+#define FIMC_MDEVICE_H_
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "fimc-core.h"
+#include "mipi-csis.h"
+
+/* Group IDs of sensor, MIPI CSIS and the writeback subdevs. */
+#define SENSOR_GROUP_ID (1 << 8)
+#define CSIS_GROUP_ID (1 << 9)
+#define WRITEBACK_GROUP_ID (1 << 10)
+
+#define FIMC_MAX_SENSORS 8
+#define FIMC_MAX_CAMCLKS 2
+
+struct fimc_csis_info {
+ struct v4l2_subdev *sd;
+ int id;
+};
+
+struct fimc_camclk_info {
+ struct clk *clock;
+ int use_count;
+ unsigned long frequency;
+};
+
+/**
+ * struct fimc_sensor_info - image data source subdev information
+ * @pdata: sensor's atrributes passed as media device's platform data
+ * @subdev: image sensor v4l2 subdev
+ * @host: fimc device the sensor is currently linked to
+ * @clk_on: sclk_cam clock's state associated with this subdev
+ *
+ * This data structure applies to image sensor and the writeback subdevs.
+ */
+struct fimc_sensor_info {
+ struct s5p_fimc_isp_info *pdata;
+ struct v4l2_subdev *subdev;
+ struct fimc_dev *host;
+ bool clk_on;
+};
+
+/**
+ * struct fimc_md - fimc media device information
+ * @csis: MIPI CSIS subdevs data
+ * @sensor: array of registered sensor subdevs
+ * @num_sensors: actual number of registered sensors
+ * @camclk: external sensor clock information
+ * @fimc: array of registered fimc devices
+ * @media_dev: top level media device
+ * @v4l2_dev: top level v4l2_device holding up the subdevs
+ * @pdev: platform device this media device is hooked up into
+ * @user_subdev_api: true if subdevs are not configured by the host driver
+ * @slock: spinlock protecting @sensor array
+ */
+struct fimc_md {
+ struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
+ struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
+ int num_sensors;
+ struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
+ struct fimc_dev *fimc[FIMC_MAX_DEVS];
+ struct media_device media_dev;
+ struct v4l2_device v4l2_dev;
+ struct platform_device *pdev;
+ bool user_subdev_api;
+ spinlock_t slock;
+};
+
+#define is_subdev_pad(pad) (pad == NULL || \
+ media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
+
+#define me_subtype(me) \
+ ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK))
+
+#define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)
+
+static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
+{
+ return me->parent == NULL ? NULL :
+ container_of(me->parent, struct fimc_md, media_dev);
+}
+
+static inline void fimc_md_graph_lock(struct fimc_dev *fimc)
+{
+ BUG_ON(fimc->vid_cap.vfd == NULL);
+ mutex_lock(&fimc->vid_cap.vfd->entity.parent->graph_mutex);
+}
+
+static inline void fimc_md_graph_unlock(struct fimc_dev *fimc)
+{
+ BUG_ON(fimc->vid_cap.vfd == NULL);
+ mutex_unlock(&fimc->vid_cap.vfd->entity.parent->graph_mutex);
+}
+
+int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
+void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me);
+int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
+ bool resume);
+int fimc_pipeline_shutdown(struct fimc_dev *fimc);
+int fimc_pipeline_s_power(struct fimc_dev *fimc, int state);
+int fimc_pipeline_s_stream(struct fimc_dev *fimc, int state);
+
+#endif
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c
index 4893b2d91d84..20e664e34163 100644
--- a/drivers/media/video/s5p-fimc/fimc-reg.c
+++ b/drivers/media/video/s5p-fimc/fimc-reg.c
@@ -30,7 +30,7 @@ void fimc_hw_reset(struct fimc_dev *dev)
cfg = readl(dev->regs + S5P_CIGCTRL);
cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
writel(cfg, dev->regs + S5P_CIGCTRL);
- udelay(1000);
+ udelay(10);
cfg = readl(dev->regs + S5P_CIGCTRL);
cfg &= ~S5P_CIGCTRL_SWRST;
@@ -41,19 +41,11 @@ static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx)
{
u32 flip = S5P_MSCTRL_FLIP_NORMAL;
- switch (ctx->flip) {
- case FLIP_X_AXIS:
+ if (ctx->hflip)
flip = S5P_MSCTRL_FLIP_X_MIRROR;
- break;
- case FLIP_Y_AXIS:
+ if (ctx->vflip)
flip = S5P_MSCTRL_FLIP_Y_MIRROR;
- break;
- case FLIP_XY_AXIS:
- flip = S5P_MSCTRL_FLIP_180;
- break;
- default:
- break;
- }
+
if (ctx->rotation <= 90)
return flip;
@@ -64,19 +56,11 @@ static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx)
{
u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
- switch (ctx->flip) {
- case FLIP_X_AXIS:
- flip = S5P_CITRGFMT_FLIP_X_MIRROR;
- break;
- case FLIP_Y_AXIS:
- flip = S5P_CITRGFMT_FLIP_Y_MIRROR;
- break;
- case FLIP_XY_AXIS:
- flip = S5P_CITRGFMT_FLIP_180;
- break;
- default:
- break;
- }
+ if (ctx->hflip)
+ flip |= S5P_CITRGFMT_FLIP_X_MIRROR;
+ if (ctx->vflip)
+ flip |= S5P_CITRGFMT_FLIP_Y_MIRROR;
+
if (ctx->rotation <= 90)
return flip;
@@ -368,17 +352,19 @@ void fimc_hw_en_capture(struct fimc_ctx *ctx)
writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT);
}
-void fimc_hw_set_effect(struct fimc_ctx *ctx)
+void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_effect *effect = &ctx->effect;
- u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER);
-
- cfg |= effect->type;
+ u32 cfg = 0;
- if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
- cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
- cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
+ if (active) {
+ cfg |= S5P_CIIMGEFF_IE_SC_AFTER | S5P_CIIMGEFF_IE_ENABLE;
+ cfg |= effect->type;
+ if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
+ cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
+ cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
+ }
}
writel(cfg, dev->regs + S5P_CIIMGEFF);
@@ -547,20 +533,24 @@ int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
u32 cfg = readl(fimc->regs + S5P_CIGCTRL);
cfg &= ~(S5P_CIGCTRL_INVPOLPCLK | S5P_CIGCTRL_INVPOLVSYNC |
- S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC);
+ S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC |
+ S5P_CIGCTRL_INVPOLFIELD);
- if (cam->flags & FIMC_CLK_INV_PCLK)
+ if (cam->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
cfg |= S5P_CIGCTRL_INVPOLPCLK;
- if (cam->flags & FIMC_CLK_INV_VSYNC)
+ if (cam->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
cfg |= S5P_CIGCTRL_INVPOLVSYNC;
- if (cam->flags & FIMC_CLK_INV_HREF)
+ if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
cfg |= S5P_CIGCTRL_INVPOLHREF;
- if (cam->flags & FIMC_CLK_INV_HSYNC)
+ if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
cfg |= S5P_CIGCTRL_INVPOLHSYNC;
+ if (cam->flags & V4L2_MBUS_FIELD_EVEN_LOW)
+ cfg |= S5P_CIGCTRL_INVPOLFIELD;
+
writel(cfg, fimc->regs + S5P_CIGCTRL);
return 0;
@@ -588,7 +578,7 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc,
if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) {
for (i = 0; i < ARRAY_SIZE(pix_desc); i++) {
- if (fimc->vid_cap.fmt.code == pix_desc[i].pixelcode) {
+ if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) {
cfg = pix_desc[i].cisrcfmt;
bus_width = pix_desc[i].bus_width;
break;
@@ -596,9 +586,9 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc,
}
if (i == ARRAY_SIZE(pix_desc)) {
- v4l2_err(&fimc->vid_cap.v4l2_dev,
+ v4l2_err(fimc->vid_cap.vfd,
"Camera color format not supported: %d\n",
- fimc->vid_cap.fmt.code);
+ fimc->vid_cap.mf.code);
return -EINVAL;
}
@@ -608,6 +598,9 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc,
else if (bus_width == 16)
cfg |= S5P_CISRCFMT_ITU601_16BIT;
} /* else defaults to ITU-R BT.656 8-bit */
+ } else if (cam->bus_type == FIMC_MIPI_CSI2) {
+ if (fimc_fmt_is_jpeg(f->fmt->color))
+ cfg |= S5P_CISRCFMT_ITU601_8BIT;
}
cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height);
@@ -649,7 +642,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
/* Select ITU B interface, disable Writeback path and test pattern. */
cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A |
S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB |
- S5P_CIGCTRL_SELCAM_MIPI_A);
+ S5P_CIGCTRL_SELCAM_MIPI_A | S5P_CIGCTRL_CAM_JPEG);
if (cam->bus_type == FIMC_MIPI_CSI2) {
cfg |= S5P_CIGCTRL_SELCAM_MIPI;
@@ -658,11 +651,18 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
cfg |= S5P_CIGCTRL_SELCAM_MIPI_A;
/* TODO: add remaining supported formats. */
- if (vid_cap->fmt.code == V4L2_MBUS_FMT_VYUY8_2X8) {
+ switch (vid_cap->mf.code) {
+ case V4L2_MBUS_FMT_VYUY8_2X8:
tmp = S5P_CSIIMGFMT_YCBCR422_8BIT;
- } else {
- err("camera image format not supported: %d",
- vid_cap->fmt.code);
+ break;
+ case V4L2_MBUS_FMT_JPEG_1X8:
+ tmp = S5P_CSIIMGFMT_USER(1);
+ cfg |= S5P_CIGCTRL_CAM_JPEG;
+ break;
+ default:
+ v4l2_err(fimc->vid_cap.vfd,
+ "Not supported camera pixel format: %d",
+ vid_cap->mf.code);
return -EINVAL;
}
tmp |= (cam->csi_data_align == 32) << 8;
diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c
index ef056d6605ca..59d79bc2f58a 100644
--- a/drivers/media/video/s5p-fimc/mipi-csis.c
+++ b/drivers/media/video/s5p-fimc/mipi-csis.c
@@ -81,6 +81,12 @@ static char *csi_clock_name[] = {
};
#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name)
+static const char * const csis_supply_name[] = {
+ "vdd11", /* 1.1V or 1.2V (s5pc100) MIPI CSI suppply */
+ "vdd18", /* VDD 1.8V and MIPI CSI PLL supply */
+};
+#define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name)
+
enum {
ST_POWERED = 1,
ST_STREAMING = 2,
@@ -109,9 +115,9 @@ struct csis_state {
struct platform_device *pdev;
struct resource *regs_res;
void __iomem *regs;
+ struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
struct clk *clock[NUM_CSIS_CLOCKS];
int irq;
- struct regulator *supply;
u32 flags;
const struct csis_pix_format *csis_fmt;
struct v4l2_mbus_framefmt format;
@@ -460,6 +466,7 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
struct resource *regs_res;
struct csis_state *state;
int ret = -ENOMEM;
+ int i;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
@@ -519,14 +526,13 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
goto e_clkput;
}
- if (!pdata->fixed_phy_vdd) {
- state->supply = regulator_get(&pdev->dev, "vdd");
- if (IS_ERR(state->supply)) {
- ret = PTR_ERR(state->supply);
- state->supply = NULL;
- goto e_clkput;
- }
- }
+ for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
+ state->supplies[i].supply = csis_supply_name[i];
+
+ ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
+ state->supplies);
+ if (ret)
+ goto e_clkput;
ret = request_irq(state->irq, s5pcsis_irq_handler, 0,
dev_name(&pdev->dev), state);
@@ -553,7 +559,6 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
/* .. and a pointer to the subdev. */
platform_set_drvdata(pdev, &state->sd);
- state->flags = ST_SUSPENDED;
pm_runtime_enable(&pdev->dev);
return 0;
@@ -561,8 +566,7 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
e_irqfree:
free_irq(state->irq, state);
e_regput:
- if (state->supply)
- regulator_put(state->supply);
+ regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
e_clkput:
clk_disable(state->clock[CSIS_CLK_MUX]);
s5pcsis_clk_put(state);
@@ -575,7 +579,7 @@ e_free:
return ret;
}
-static int s5pcsis_suspend(struct device *dev)
+static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
{
struct s5p_platform_mipi_csis *pdata = dev->platform_data;
struct platform_device *pdev = to_platform_device(dev);
@@ -592,21 +596,21 @@ static int s5pcsis_suspend(struct device *dev)
ret = pdata->phy_enable(state->pdev, false);
if (ret)
goto unlock;
- if (state->supply) {
- ret = regulator_disable(state->supply);
- if (ret)
- goto unlock;
- }
+ ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+ state->supplies);
+ if (ret)
+ goto unlock;
clk_disable(state->clock[CSIS_CLK_GATE]);
state->flags &= ~ST_POWERED;
+ if (!runtime)
+ state->flags |= ST_SUSPENDED;
}
- state->flags |= ST_SUSPENDED;
unlock:
mutex_unlock(&state->lock);
return ret ? -EAGAIN : 0;
}
-static int s5pcsis_resume(struct device *dev)
+static int s5pcsis_pm_resume(struct device *dev, bool runtime)
{
struct s5p_platform_mipi_csis *pdata = dev->platform_data;
struct platform_device *pdev = to_platform_device(dev);
@@ -618,20 +622,20 @@ static int s5pcsis_resume(struct device *dev)
__func__, state->flags);
mutex_lock(&state->lock);
- if (!(state->flags & ST_SUSPENDED))
+ if (!runtime && !(state->flags & ST_SUSPENDED))
goto unlock;
if (!(state->flags & ST_POWERED)) {
- if (state->supply)
- ret = regulator_enable(state->supply);
+ ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES,
+ state->supplies);
if (ret)
goto unlock;
-
ret = pdata->phy_enable(state->pdev, true);
if (!ret) {
state->flags |= ST_POWERED;
- } else if (state->supply) {
- regulator_disable(state->supply);
+ } else {
+ regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+ state->supplies);
goto unlock;
}
clk_enable(state->clock[CSIS_CLK_GATE]);
@@ -646,24 +650,26 @@ static int s5pcsis_resume(struct device *dev)
}
#ifdef CONFIG_PM_SLEEP
-static int s5pcsis_pm_suspend(struct device *dev)
+static int s5pcsis_suspend(struct device *dev)
{
- return s5pcsis_suspend(dev);
+ return s5pcsis_pm_suspend(dev, false);
}
-static int s5pcsis_pm_resume(struct device *dev)
+static int s5pcsis_resume(struct device *dev)
{
- int ret;
-
- ret = s5pcsis_resume(dev);
+ return s5pcsis_pm_resume(dev, false);
+}
+#endif
- if (!ret) {
- pm_runtime_disable(dev);
- ret = pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- }
+#ifdef CONFIG_PM_RUNTIME
+static int s5pcsis_runtime_suspend(struct device *dev)
+{
+ return s5pcsis_pm_suspend(dev, true);
+}
- return ret;
+static int s5pcsis_runtime_resume(struct device *dev)
+{
+ return s5pcsis_pm_resume(dev, true);
}
#endif
@@ -679,8 +685,7 @@ static int __devexit s5pcsis_remove(struct platform_device *pdev)
pm_runtime_set_suspended(&pdev->dev);
s5pcsis_clk_put(state);
- if (state->supply)
- regulator_put(state->supply);
+ regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
media_entity_cleanup(&state->sd.entity);
free_irq(state->irq, state);
@@ -692,8 +697,9 @@ static int __devexit s5pcsis_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops s5pcsis_pm_ops = {
- SET_RUNTIME_PM_OPS(s5pcsis_suspend, s5pcsis_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_pm_suspend, s5pcsis_pm_resume)
+ SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
};
static struct platform_driver s5pcsis_driver = {
diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h
index 0fea3e635d76..c8e3b94bd91d 100644
--- a/drivers/media/video/s5p-fimc/regs-fimc.h
+++ b/drivers/media/video/s5p-fimc/regs-fimc.h
@@ -54,12 +54,14 @@
#define S5P_CIGCTRL_IRQ_CLR (1 << 19)
#define S5P_CIGCTRL_IRQ_ENABLE (1 << 16)
#define S5P_CIGCTRL_SHDW_DISABLE (1 << 12)
+#define S5P_CIGCTRL_CAM_JPEG (1 << 8)
#define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7)
#define S5P_CIGCTRL_CAMIF_SELWB (1 << 6)
/* 0 - ITU601; 1 - ITU709 */
#define S5P_CIGCTRL_CSC_ITU601_709 (1 << 5)
#define S5P_CIGCTRL_INVPOLHSYNC (1 << 4)
#define S5P_CIGCTRL_SELCAM_MIPI (1 << 3)
+#define S5P_CIGCTRL_INVPOLFIELD (1 << 1)
#define S5P_CIGCTRL_INTERLACE (1 << 0)
/* Window offset 2 */
@@ -184,7 +186,6 @@
/* Image effect */
#define S5P_CIIMGEFF 0xd0
-#define S5P_CIIMGEFF_IE_DISABLE (0 << 30)
#define S5P_CIIMGEFF_IE_ENABLE (1 << 30)
#define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29)
#define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29)
@@ -286,10 +287,8 @@
#define S5P_CSIIMGFMT_RAW8 0x2a
#define S5P_CSIIMGFMT_RAW10 0x2b
#define S5P_CSIIMGFMT_RAW12 0x2c
-#define S5P_CSIIMGFMT_USER1 0x30
-#define S5P_CSIIMGFMT_USER2 0x31
-#define S5P_CSIIMGFMT_USER3 0x32
-#define S5P_CSIIMGFMT_USER4 0x33
+/* User defined formats. x = 0...16. */
+#define S5P_CSIIMGFMT_USER(x) (0x30 + x - 1)
/* Output frame buffer sequence mask */
#define S5P_CIFCNTSEQ 0x1FC
diff --git a/drivers/media/video/s5p-mfc/Makefile b/drivers/media/video/s5p-mfc/Makefile
new file mode 100644
index 000000000000..d0663409af00
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) := s5p-mfc.o
+s5p-mfc-y += s5p_mfc.o s5p_mfc_intr.o s5p_mfc_opr.o
+s5p-mfc-y += s5p_mfc_dec.o s5p_mfc_enc.o
+s5p-mfc-y += s5p_mfc_ctrl.o s5p_mfc_cmd.o
+s5p-mfc-y += s5p_mfc_pm.o s5p_mfc_shm.o
diff --git a/drivers/media/video/s5p-mfc/regs-mfc.h b/drivers/media/video/s5p-mfc/regs-mfc.h
new file mode 100644
index 000000000000..053a8a872fd7
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/regs-mfc.h
@@ -0,0 +1,413 @@
+/*
+ * Register definition file for Samsung MFC V5.1 Interface (FIMV) driver
+ *
+ * Kamil Debski, Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _REGS_FIMV_H
+#define _REGS_FIMV_H
+
+#define S5P_FIMV_REG_SIZE (S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR)
+#define S5P_FIMV_REG_COUNT ((S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR) / 4)
+
+/* Number of bits that the buffer address should be shifted for particular
+ * MFC buffers. */
+#define S5P_FIMV_START_ADDR 0x0000
+#define S5P_FIMV_END_ADDR 0xe008
+
+#define S5P_FIMV_SW_RESET 0x0000
+#define S5P_FIMV_RISC_HOST_INT 0x0008
+
+/* Command from HOST to RISC */
+#define S5P_FIMV_HOST2RISC_CMD 0x0030
+#define S5P_FIMV_HOST2RISC_ARG1 0x0034
+#define S5P_FIMV_HOST2RISC_ARG2 0x0038
+#define S5P_FIMV_HOST2RISC_ARG3 0x003c
+#define S5P_FIMV_HOST2RISC_ARG4 0x0040
+
+/* Command from RISC to HOST */
+#define S5P_FIMV_RISC2HOST_CMD 0x0044
+#define S5P_FIMV_RISC2HOST_CMD_MASK 0x1FFFF
+#define S5P_FIMV_RISC2HOST_ARG1 0x0048
+#define S5P_FIMV_RISC2HOST_ARG2 0x004c
+#define S5P_FIMV_RISC2HOST_ARG3 0x0050
+#define S5P_FIMV_RISC2HOST_ARG4 0x0054
+
+#define S5P_FIMV_FW_VERSION 0x0058
+#define S5P_FIMV_SYS_MEM_SZ 0x005c
+#define S5P_FIMV_FW_STATUS 0x0080
+
+/* Memory controller register */
+#define S5P_FIMV_MC_DRAMBASE_ADR_A 0x0508
+#define S5P_FIMV_MC_DRAMBASE_ADR_B 0x050c
+#define S5P_FIMV_MC_STATUS 0x0510
+
+/* Common register */
+#define S5P_FIMV_COMMON_BASE_A 0x0600
+#define S5P_FIMV_COMMON_BASE_B 0x0700
+
+/* Decoder */
+#define S5P_FIMV_DEC_CHROMA_ADR (S5P_FIMV_COMMON_BASE_A)
+#define S5P_FIMV_DEC_LUMA_ADR (S5P_FIMV_COMMON_BASE_B)
+
+/* H.264 decoding */
+#define S5P_FIMV_H264_VERT_NB_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x8c)
+ /* vertical neighbor motion vector */
+#define S5P_FIMV_H264_NB_IP_ADR (S5P_FIMV_COMMON_BASE_A + 0x90)
+ /* neighbor pixels for intra pred */
+#define S5P_FIMV_H264_MV_ADR (S5P_FIMV_COMMON_BASE_B + 0x80)
+ /* H264 motion vector */
+
+/* MPEG4 decoding */
+#define S5P_FIMV_MPEG4_NB_DCAC_ADR (S5P_FIMV_COMMON_BASE_A + 0x8c)
+ /* neighbor AC/DC coeff. */
+#define S5P_FIMV_MPEG4_UP_NB_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x90)
+ /* upper neighbor motion vector */
+#define S5P_FIMV_MPEG4_SA_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x94)
+ /* subseq. anchor motion vector */
+#define S5P_FIMV_MPEG4_OT_LINE_ADR (S5P_FIMV_COMMON_BASE_A + 0x98)
+ /* overlap transform line */
+#define S5P_FIMV_MPEG4_SP_ADR (S5P_FIMV_COMMON_BASE_A + 0xa8)
+ /* syntax parser */
+
+/* H.263 decoding */
+#define S5P_FIMV_H263_NB_DCAC_ADR (S5P_FIMV_COMMON_BASE_A + 0x8c)
+#define S5P_FIMV_H263_UP_NB_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x90)
+#define S5P_FIMV_H263_SA_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x94)
+#define S5P_FIMV_H263_OT_LINE_ADR (S5P_FIMV_COMMON_BASE_A + 0x98)
+
+/* VC-1 decoding */
+#define S5P_FIMV_VC1_NB_DCAC_ADR (S5P_FIMV_COMMON_BASE_A + 0x8c)
+#define S5P_FIMV_VC1_UP_NB_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x90)
+#define S5P_FIMV_VC1_SA_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x94)
+#define S5P_FIMV_VC1_OT_LINE_ADR (S5P_FIMV_COMMON_BASE_A + 0x98)
+#define S5P_FIMV_VC1_BITPLANE3_ADR (S5P_FIMV_COMMON_BASE_A + 0x9c)
+ /* bitplane3 */
+#define S5P_FIMV_VC1_BITPLANE2_ADR (S5P_FIMV_COMMON_BASE_A + 0xa0)
+ /* bitplane2 */
+#define S5P_FIMV_VC1_BITPLANE1_ADR (S5P_FIMV_COMMON_BASE_A + 0xa4)
+ /* bitplane1 */
+
+/* Encoder */
+#define S5P_FIMV_ENC_REF0_LUMA_ADR (S5P_FIMV_COMMON_BASE_A + 0x1c)
+#define S5P_FIMV_ENC_REF1_LUMA_ADR (S5P_FIMV_COMMON_BASE_A + 0x20)
+ /* reconstructed luma */
+#define S5P_FIMV_ENC_REF0_CHROMA_ADR (S5P_FIMV_COMMON_BASE_B)
+#define S5P_FIMV_ENC_REF1_CHROMA_ADR (S5P_FIMV_COMMON_BASE_B + 0x04)
+ /* reconstructed chroma */
+#define S5P_FIMV_ENC_REF2_LUMA_ADR (S5P_FIMV_COMMON_BASE_B + 0x10)
+#define S5P_FIMV_ENC_REF2_CHROMA_ADR (S5P_FIMV_COMMON_BASE_B + 0x08)
+#define S5P_FIMV_ENC_REF3_LUMA_ADR (S5P_FIMV_COMMON_BASE_B + 0x14)
+#define S5P_FIMV_ENC_REF3_CHROMA_ADR (S5P_FIMV_COMMON_BASE_B + 0x0c)
+
+/* H.264 encoding */
+#define S5P_FIMV_H264_UP_MV_ADR (S5P_FIMV_COMMON_BASE_A)
+ /* upper motion vector */
+#define S5P_FIMV_H264_NBOR_INFO_ADR (S5P_FIMV_COMMON_BASE_A + 0x04)
+ /* entropy engine's neighbor info. */
+#define S5P_FIMV_H264_UP_INTRA_MD_ADR (S5P_FIMV_COMMON_BASE_A + 0x08)
+ /* upper intra MD */
+#define S5P_FIMV_H264_COZERO_FLAG_ADR (S5P_FIMV_COMMON_BASE_A + 0x10)
+ /* direct cozero flag */
+#define S5P_FIMV_H264_UP_INTRA_PRED_ADR (S5P_FIMV_COMMON_BASE_B + 0x40)
+ /* upper intra PRED */
+
+/* H.263 encoding */
+#define S5P_FIMV_H263_UP_MV_ADR (S5P_FIMV_COMMON_BASE_A)
+ /* upper motion vector */
+#define S5P_FIMV_H263_ACDC_COEF_ADR (S5P_FIMV_COMMON_BASE_A + 0x04)
+ /* upper Q coeff. */
+
+/* MPEG4 encoding */
+#define S5P_FIMV_MPEG4_UP_MV_ADR (S5P_FIMV_COMMON_BASE_A)
+ /* upper motion vector */
+#define S5P_FIMV_MPEG4_ACDC_COEF_ADR (S5P_FIMV_COMMON_BASE_A + 0x04)
+ /* upper Q coeff. */
+#define S5P_FIMV_MPEG4_COZERO_FLAG_ADR (S5P_FIMV_COMMON_BASE_A + 0x10)
+ /* direct cozero flag */
+
+#define S5P_FIMV_ENC_REF_B_LUMA_ADR 0x062c /* ref B Luma addr */
+#define S5P_FIMV_ENC_REF_B_CHROMA_ADR 0x0630 /* ref B Chroma addr */
+
+#define S5P_FIMV_ENC_CUR_LUMA_ADR 0x0718 /* current Luma addr */
+#define S5P_FIMV_ENC_CUR_CHROMA_ADR 0x071C /* current Chroma addr */
+
+/* Codec common register */
+#define S5P_FIMV_ENC_HSIZE_PX 0x0818 /* frame width at encoder */
+#define S5P_FIMV_ENC_VSIZE_PX 0x081c /* frame height at encoder */
+#define S5P_FIMV_ENC_PROFILE 0x0830 /* profile register */
+#define S5P_FIMV_ENC_PROFILE_H264_MAIN 0
+#define S5P_FIMV_ENC_PROFILE_H264_HIGH 1
+#define S5P_FIMV_ENC_PROFILE_H264_BASELINE 2
+#define S5P_FIMV_ENC_PROFILE_MPEG4_SIMPLE 0
+#define S5P_FIMV_ENC_PROFILE_MPEG4_ADVANCED_SIMPLE 1
+#define S5P_FIMV_ENC_PIC_STRUCT 0x083c /* picture field/frame flag */
+#define S5P_FIMV_ENC_LF_CTRL 0x0848 /* loop filter control */
+#define S5P_FIMV_ENC_ALPHA_OFF 0x084c /* loop filter alpha offset */
+#define S5P_FIMV_ENC_BETA_OFF 0x0850 /* loop filter beta offset */
+#define S5P_FIMV_MR_BUSIF_CTRL 0x0854 /* hidden, bus interface ctrl */
+#define S5P_FIMV_ENC_PXL_CACHE_CTRL 0x0a00 /* pixel cache control */
+
+/* Channel & stream interface register */
+#define S5P_FIMV_SI_RTN_CHID 0x2000 /* Return CH inst ID register */
+#define S5P_FIMV_SI_CH0_INST_ID 0x2040 /* codec instance ID */
+#define S5P_FIMV_SI_CH1_INST_ID 0x2080 /* codec instance ID */
+/* Decoder */
+#define S5P_FIMV_SI_VRESOL 0x2004 /* vertical res of decoder */
+#define S5P_FIMV_SI_HRESOL 0x2008 /* horizontal res of decoder */
+#define S5P_FIMV_SI_BUF_NUMBER 0x200c /* number of frames in the
+ decoded pic */
+#define S5P_FIMV_SI_DISPLAY_Y_ADR 0x2010 /* luma addr of displayed pic */
+#define S5P_FIMV_SI_DISPLAY_C_ADR 0x2014 /* chroma addrof displayed pic */
+#define S5P_FIMV_SI_CONSUMED_BYTES 0x2018 /* Consumed number of bytes to
+ decode a frame */
+#define S5P_FIMV_SI_DISPLAY_STATUS 0x201c /* status of decoded picture */
+
+#define S5P_FIMV_SI_CH0_SB_ST_ADR 0x2044 /* start addr of stream buf */
+#define S5P_FIMV_SI_CH0_SB_FRM_SIZE 0x2048 /* size of stream buf */
+#define S5P_FIMV_SI_CH0_DESC_ADR 0x204c /* addr of descriptor buf */
+#define S5P_FIMV_SI_CH0_CPB_SIZE 0x2058 /* max size of coded pic. buf */
+#define S5P_FIMV_SI_CH0_DESC_SIZE 0x205c /* max size of descriptor buf */
+
+#define S5P_FIMV_SI_CH1_SB_ST_ADR 0x2084 /* start addr of stream buf */
+#define S5P_FIMV_SI_CH1_SB_FRM_SIZE 0x2088 /* size of stream buf */
+#define S5P_FIMV_SI_CH1_DESC_ADR 0x208c /* addr of descriptor buf */
+#define S5P_FIMV_SI_CH1_CPB_SIZE 0x2098 /* max size of coded pic. buf */
+#define S5P_FIMV_SI_CH1_DESC_SIZE 0x209c /* max size of descriptor buf */
+
+#define S5P_FIMV_CRC_LUMA0 0x2030 /* luma crc data per frame
+ (top field) */
+#define S5P_FIMV_CRC_CHROMA0 0x2034 /* chroma crc data per frame
+ (top field) */
+#define S5P_FIMV_CRC_LUMA1 0x2038 /* luma crc data per bottom
+ field */
+#define S5P_FIMV_CRC_CHROMA1 0x203c /* chroma crc data per bottom
+ field */
+
+/* Display status */
+#define S5P_FIMV_DEC_STATUS_DECODING_ONLY 0
+#define S5P_FIMV_DEC_STATUS_DECODING_DISPLAY 1
+#define S5P_FIMV_DEC_STATUS_DISPLAY_ONLY 2
+#define S5P_FIMV_DEC_STATUS_DECODING_EMPTY 3
+#define S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK 7
+#define S5P_FIMV_DEC_STATUS_PROGRESSIVE (0<<3)
+#define S5P_FIMV_DEC_STATUS_INTERLACE (1<<3)
+#define S5P_FIMV_DEC_STATUS_INTERLACE_MASK (1<<3)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_TWO (0<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_FOUR (1<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_MASK (1<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_GENERATED (1<<5)
+#define S5P_FIMV_DEC_STATUS_CRC_NOT_GENERATED (0<<5)
+#define S5P_FIMV_DEC_STATUS_CRC_MASK (1<<5)
+
+#define S5P_FIMV_DEC_STATUS_RESOLUTION_MASK (3<<4)
+#define S5P_FIMV_DEC_STATUS_RESOLUTION_INC (1<<4)
+#define S5P_FIMV_DEC_STATUS_RESOLUTION_DEC (2<<4)
+
+/* Decode frame address */
+#define S5P_FIMV_DECODE_Y_ADR 0x2024
+#define S5P_FIMV_DECODE_C_ADR 0x2028
+
+/* Decoded frame tpe */
+#define S5P_FIMV_DECODE_FRAME_TYPE 0x2020
+#define S5P_FIMV_DECODE_FRAME_MASK 7
+
+#define S5P_FIMV_DECODE_FRAME_SKIPPED 0
+#define S5P_FIMV_DECODE_FRAME_I_FRAME 1
+#define S5P_FIMV_DECODE_FRAME_P_FRAME 2
+#define S5P_FIMV_DECODE_FRAME_B_FRAME 3
+#define S5P_FIMV_DECODE_FRAME_OTHER_FRAME 4
+
+/* Sizes of buffers required for decoding */
+#define S5P_FIMV_DEC_NB_IP_SIZE (32 * 1024)
+#define S5P_FIMV_DEC_VERT_NB_MV_SIZE (16 * 1024)
+#define S5P_FIMV_DEC_NB_DCAC_SIZE (16 * 1024)
+#define S5P_FIMV_DEC_UPNB_MV_SIZE (68 * 1024)
+#define S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE (136 * 1024)
+#define S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE (32 * 1024)
+#define S5P_FIMV_DEC_VC1_BITPLANE_SIZE (2 * 1024)
+#define S5P_FIMV_DEC_STX_PARSER_SIZE (68 * 1024)
+
+#define S5P_FIMV_DEC_BUF_ALIGN (8 * 1024)
+#define S5P_FIMV_ENC_BUF_ALIGN (8 * 1024)
+#define S5P_FIMV_NV12M_HALIGN 16
+#define S5P_FIMV_NV12M_LVALIGN 16
+#define S5P_FIMV_NV12M_CVALIGN 8
+#define S5P_FIMV_NV12MT_HALIGN 128
+#define S5P_FIMV_NV12MT_VALIGN 32
+#define S5P_FIMV_NV12M_SALIGN 2048
+#define S5P_FIMV_NV12MT_SALIGN 8192
+
+/* Sizes of buffers required for encoding */
+#define S5P_FIMV_ENC_UPMV_SIZE 0x10000
+#define S5P_FIMV_ENC_COLFLG_SIZE 0x10000
+#define S5P_FIMV_ENC_INTRAMD_SIZE 0x10000
+#define S5P_FIMV_ENC_INTRAPRED_SIZE 0x4000
+#define S5P_FIMV_ENC_NBORINFO_SIZE 0x10000
+#define S5P_FIMV_ENC_ACDCCOEF_SIZE 0x10000
+
+/* Encoder */
+#define S5P_FIMV_ENC_SI_STRM_SIZE 0x2004 /* stream size */
+#define S5P_FIMV_ENC_SI_PIC_CNT 0x2008 /* picture count */
+#define S5P_FIMV_ENC_SI_WRITE_PTR 0x200c /* write pointer */
+#define S5P_FIMV_ENC_SI_SLICE_TYPE 0x2010 /* slice type(I/P/B/IDR) */
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_NON_CODED 0
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_I 1
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_P 2
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_B 3
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_SKIPPED 4
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_OTHERS 5
+#define S5P_FIMV_ENCODED_Y_ADDR 0x2014 /* the addr of the encoded
+ luma pic */
+#define S5P_FIMV_ENCODED_C_ADDR 0x2018 /* the addr of the encoded
+ chroma pic */
+
+#define S5P_FIMV_ENC_SI_CH0_SB_ADR 0x2044 /* addr of stream buf */
+#define S5P_FIMV_ENC_SI_CH0_SB_SIZE 0x204c /* size of stream buf */
+#define S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR 0x2050 /* current Luma addr */
+#define S5P_FIMV_ENC_SI_CH0_CUR_C_ADR 0x2054 /* current Chroma addr */
+#define S5P_FIMV_ENC_SI_CH0_FRAME_INS 0x2058 /* frame insertion */
+
+#define S5P_FIMV_ENC_SI_CH1_SB_ADR 0x2084 /* addr of stream buf */
+#define S5P_FIMV_ENC_SI_CH1_SB_SIZE 0x208c /* size of stream buf */
+#define S5P_FIMV_ENC_SI_CH1_CUR_Y_ADR 0x2090 /* current Luma addr */
+#define S5P_FIMV_ENC_SI_CH1_CUR_C_ADR 0x2094 /* current Chroma addr */
+#define S5P_FIMV_ENC_SI_CH1_FRAME_INS 0x2098 /* frame insertion */
+
+#define S5P_FIMV_ENC_PIC_TYPE_CTRL 0xc504 /* pic type level control */
+#define S5P_FIMV_ENC_B_RECON_WRITE_ON 0xc508 /* B frame recon write ctrl */
+#define S5P_FIMV_ENC_MSLICE_CTRL 0xc50c /* multi slice control */
+#define S5P_FIMV_ENC_MSLICE_MB 0xc510 /* MB number in the one slice */
+#define S5P_FIMV_ENC_MSLICE_BIT 0xc514 /* bit count for one slice */
+#define S5P_FIMV_ENC_CIR_CTRL 0xc518 /* number of intra refresh MB */
+#define S5P_FIMV_ENC_MAP_FOR_CUR 0xc51c /* linear or tiled mode */
+#define S5P_FIMV_ENC_PADDING_CTRL 0xc520 /* padding control */
+
+#define S5P_FIMV_ENC_RC_CONFIG 0xc5a0 /* RC config */
+#define S5P_FIMV_ENC_RC_BIT_RATE 0xc5a8 /* bit rate */
+#define S5P_FIMV_ENC_RC_QBOUND 0xc5ac /* max/min QP */
+#define S5P_FIMV_ENC_RC_RPARA 0xc5b0 /* rate control reaction coeff */
+#define S5P_FIMV_ENC_RC_MB_CTRL 0xc5b4 /* MB adaptive scaling */
+
+/* Encoder for H264 only */
+#define S5P_FIMV_ENC_H264_ENTROPY_MODE 0xd004 /* CAVLC or CABAC */
+#define S5P_FIMV_ENC_H264_ALPHA_OFF 0xd008 /* loop filter alpha offset */
+#define S5P_FIMV_ENC_H264_BETA_OFF 0xd00c /* loop filter beta offset */
+#define S5P_FIMV_ENC_H264_NUM_OF_REF 0xd010 /* number of reference for P/B */
+#define S5P_FIMV_ENC_H264_TRANS_FLAG 0xd034 /* 8x8 transform flag in PPS &
+ high profile */
+
+#define S5P_FIMV_ENC_RC_FRAME_RATE 0xd0d0 /* frame rate */
+
+/* Encoder for MPEG4 only */
+#define S5P_FIMV_ENC_MPEG4_QUART_PXL 0xe008 /* qpel interpolation ctrl */
+
+/* Additional */
+#define S5P_FIMV_SI_CH0_DPB_CONF_CTRL 0x2068 /* DPB Config Control Register */
+#define S5P_FIMV_SLICE_INT_MASK 1
+#define S5P_FIMV_SLICE_INT_SHIFT 31
+#define S5P_FIMV_DDELAY_ENA_SHIFT 30
+#define S5P_FIMV_DDELAY_VAL_MASK 0xff
+#define S5P_FIMV_DDELAY_VAL_SHIFT 16
+#define S5P_FIMV_DPB_COUNT_MASK 0xffff
+#define S5P_FIMV_DPB_FLUSH_MASK 1
+#define S5P_FIMV_DPB_FLUSH_SHIFT 14
+
+
+#define S5P_FIMV_SI_CH0_RELEASE_BUF 0x2060 /* DPB release buffer register */
+#define S5P_FIMV_SI_CH0_HOST_WR_ADR 0x2064 /* address of shared memory */
+
+/* Codec numbers */
+#define S5P_FIMV_CODEC_NONE -1
+
+#define S5P_FIMV_CODEC_H264_DEC 0
+#define S5P_FIMV_CODEC_VC1_DEC 1
+#define S5P_FIMV_CODEC_MPEG4_DEC 2
+#define S5P_FIMV_CODEC_MPEG2_DEC 3
+#define S5P_FIMV_CODEC_H263_DEC 4
+#define S5P_FIMV_CODEC_VC1RCV_DEC 5
+
+#define S5P_FIMV_CODEC_H264_ENC 16
+#define S5P_FIMV_CODEC_MPEG4_ENC 17
+#define S5P_FIMV_CODEC_H263_ENC 18
+
+/* Channel Control Register */
+#define S5P_FIMV_CH_SEQ_HEADER 1
+#define S5P_FIMV_CH_FRAME_START 2
+#define S5P_FIMV_CH_LAST_FRAME 3
+#define S5P_FIMV_CH_INIT_BUFS 4
+#define S5P_FIMV_CH_FRAME_START_REALLOC 5
+#define S5P_FIMV_CH_MASK 7
+#define S5P_FIMV_CH_SHIFT 16
+
+
+/* Host to RISC command */
+#define S5P_FIMV_H2R_CMD_EMPTY 0
+#define S5P_FIMV_H2R_CMD_OPEN_INSTANCE 1
+#define S5P_FIMV_H2R_CMD_CLOSE_INSTANCE 2
+#define S5P_FIMV_H2R_CMD_SYS_INIT 3
+#define S5P_FIMV_H2R_CMD_FLUSH 4
+#define S5P_FIMV_H2R_CMD_SLEEP 5
+#define S5P_FIMV_H2R_CMD_WAKEUP 6
+
+#define S5P_FIMV_R2H_CMD_EMPTY 0
+#define S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET 1
+#define S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET 2
+#define S5P_FIMV_R2H_CMD_RSV_RET 3
+#define S5P_FIMV_R2H_CMD_SEQ_DONE_RET 4
+#define S5P_FIMV_R2H_CMD_FRAME_DONE_RET 5
+#define S5P_FIMV_R2H_CMD_SLICE_DONE_RET 6
+#define S5P_FIMV_R2H_CMD_ENC_COMPLETE_RET 7
+#define S5P_FIMV_R2H_CMD_SYS_INIT_RET 8
+#define S5P_FIMV_R2H_CMD_FW_STATUS_RET 9
+#define S5P_FIMV_R2H_CMD_SLEEP_RET 10
+#define S5P_FIMV_R2H_CMD_WAKEUP_RET 11
+#define S5P_FIMV_R2H_CMD_FLUSH_RET 12
+#define S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET 15
+#define S5P_FIMV_R2H_CMD_EDFU_INIT_RET 16
+#define S5P_FIMV_R2H_CMD_ERR_RET 32
+
+/* Error handling defines */
+#define S5P_FIMV_ERR_WARNINGS_START 145
+#define S5P_FIMV_ERR_DEC_MASK 0xFFFF
+#define S5P_FIMV_ERR_DEC_SHIFT 0
+#define S5P_FIMV_ERR_DSPL_MASK 0xFFFF0000
+#define S5P_FIMV_ERR_DSPL_SHIFT 16
+
+/* Shared memory registers' offsets */
+
+/* An offset of the start position in the stream when
+ * the start position is not aligned */
+#define S5P_FIMV_SHARED_CROP_INFO_H 0x0020
+#define S5P_FIMV_SHARED_CROP_LEFT_MASK 0xFFFF
+#define S5P_FIMV_SHARED_CROP_LEFT_SHIFT 0
+#define S5P_FIMV_SHARED_CROP_RIGHT_MASK 0xFFFF0000
+#define S5P_FIMV_SHARED_CROP_RIGHT_SHIFT 16
+#define S5P_FIMV_SHARED_CROP_INFO_V 0x0024
+#define S5P_FIMV_SHARED_CROP_TOP_MASK 0xFFFF
+#define S5P_FIMV_SHARED_CROP_TOP_SHIFT 0
+#define S5P_FIMV_SHARED_CROP_BOTTOM_MASK 0xFFFF0000
+#define S5P_FIMV_SHARED_CROP_BOTTOM_SHIFT 16
+#define S5P_FIMV_SHARED_SET_FRAME_TAG 0x0004
+#define S5P_FIMV_SHARED_GET_FRAME_TAG_TOP 0x0008
+#define S5P_FIMV_SHARED_GET_FRAME_TAG_BOT 0x000C
+#define S5P_FIMV_SHARED_START_BYTE_NUM 0x0018
+#define S5P_FIMV_SHARED_RC_VOP_TIMING 0x0030
+#define S5P_FIMV_SHARED_LUMA_DPB_SIZE 0x0064
+#define S5P_FIMV_SHARED_CHROMA_DPB_SIZE 0x0068
+#define S5P_FIMV_SHARED_MV_SIZE 0x006C
+#define S5P_FIMV_SHARED_PIC_TIME_TOP 0x0010
+#define S5P_FIMV_SHARED_PIC_TIME_BOTTOM 0x0014
+#define S5P_FIMV_SHARED_EXT_ENC_CONTROL 0x0028
+#define S5P_FIMV_SHARED_P_B_FRAME_QP 0x0070
+#define S5P_FIMV_SHARED_ASPECT_RATIO_IDC 0x0074
+#define S5P_FIMV_SHARED_EXTENDED_SAR 0x0078
+#define S5P_FIMV_SHARED_H264_I_PERIOD 0x009C
+#define S5P_FIMV_SHARED_RC_CONTROL_CONFIG 0x00A0
+
+#endif /* _REGS_FIMV_H */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c
new file mode 100644
index 000000000000..8be8b54eb749
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc.c
@@ -0,0 +1,1273 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.1
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.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/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_ctrl.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_dec.h"
+#include "s5p_mfc_enc.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+
+#define S5P_MFC_NAME "s5p-mfc"
+#define S5P_MFC_DEC_NAME "s5p-mfc-dec"
+#define S5P_MFC_ENC_NAME "s5p-mfc-enc"
+
+int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
+
+/* Helper functions for interrupt processing */
+/* Remove from hw execution round robin */
+static void clear_work_bit(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ spin_lock(&dev->condlock);
+ clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock(&dev->condlock);
+}
+
+/* Wake up context wait_queue */
+static void wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason,
+ unsigned int err)
+{
+ ctx->int_cond = 1;
+ ctx->int_type = reason;
+ ctx->int_err = err;
+ wake_up(&ctx->queue);
+}
+
+/* Wake up device wait_queue */
+static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
+ unsigned int err)
+{
+ dev->int_cond = 1;
+ dev->int_type = reason;
+ dev->int_err = err;
+ wake_up(&dev->queue);
+}
+
+void s5p_mfc_watchdog(unsigned long arg)
+{
+ struct s5p_mfc_dev *dev = (struct s5p_mfc_dev *)arg;
+
+ if (test_bit(0, &dev->hw_lock))
+ atomic_inc(&dev->watchdog_cnt);
+ if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
+ /* This means that hw is busy and no interrupts were
+ * generated by hw for the Nth time of running this
+ * watchdog timer. This usually means a serious hw
+ * error. Now it is time to kill all instances and
+ * reset the MFC. */
+ mfc_err("Time out during waiting for HW\n");
+ queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
+ }
+ dev->watchdog_timer.expires = jiffies +
+ msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
+ add_timer(&dev->watchdog_timer);
+}
+
+static void s5p_mfc_watchdog_worker(struct work_struct *work)
+{
+ struct s5p_mfc_dev *dev;
+ struct s5p_mfc_ctx *ctx;
+ unsigned long flags;
+ int mutex_locked;
+ int i, ret;
+
+ dev = container_of(work, struct s5p_mfc_dev, watchdog_work);
+
+ mfc_err("Driver timeout error handling\n");
+ /* Lock the mutex that protects open and release.
+ * This is necessary as they may load and unload firmware. */
+ mutex_locked = mutex_trylock(&dev->mfc_mutex);
+ if (!mutex_locked)
+ mfc_err("Error: some instance may be closing/opening\n");
+ spin_lock_irqsave(&dev->irqlock, flags);
+
+ s5p_mfc_clock_off();
+
+ for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+ ctx = dev->ctx[i];
+ if (!ctx)
+ continue;
+ ctx->state = MFCINST_ERROR;
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ clear_work_bit(ctx);
+ wake_up_ctx(ctx, S5P_FIMV_R2H_CMD_ERR_RET, 0);
+ }
+ clear_bit(0, &dev->hw_lock);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ /* Double check if there is at least one instance running.
+ * If no instance is in memory than no firmware should be present */
+ if (dev->num_inst > 0) {
+ ret = s5p_mfc_reload_firmware(dev);
+ if (ret) {
+ mfc_err("Failed to reload FW\n");
+ goto unlock;
+ }
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_init_hw(dev);
+ if (ret)
+ mfc_err("Failed to reinit FW\n");
+ }
+unlock:
+ if (mutex_locked)
+ mutex_unlock(&dev->mfc_mutex);
+}
+
+static enum s5p_mfc_node_type s5p_mfc_get_node_type(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (!vdev) {
+ mfc_err("failed to get video_device");
+ return MFCNODE_INVALID;
+ }
+ if (vdev->index == 0)
+ return MFCNODE_DECODER;
+ else if (vdev->index == 1)
+ return MFCNODE_ENCODER;
+ return MFCNODE_INVALID;
+}
+
+static void s5p_mfc_clear_int_flags(struct s5p_mfc_dev *dev)
+{
+ mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT);
+ mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
+ mfc_write(dev, 0xffff, S5P_FIMV_SI_RTN_CHID);
+}
+
+static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_buf *dst_buf;
+
+ ctx->state = MFCINST_FINISHED;
+ ctx->sequence++;
+ while (!list_empty(&ctx->dst_queue)) {
+ dst_buf = list_entry(ctx->dst_queue.next,
+ struct s5p_mfc_buf, list);
+ mfc_debug(2, "Cleaning up buffer: %d\n",
+ dst_buf->b->v4l2_buf.index);
+ vb2_set_plane_payload(dst_buf->b, 0, 0);
+ vb2_set_plane_payload(dst_buf->b, 1, 0);
+ list_del(&dst_buf->list);
+ ctx->dst_queue_cnt--;
+ dst_buf->b->v4l2_buf.sequence = (ctx->sequence++);
+
+ if (s5p_mfc_read_shm(ctx, PIC_TIME_TOP) ==
+ s5p_mfc_read_shm(ctx, PIC_TIME_BOT))
+ dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
+ else
+ dst_buf->b->v4l2_buf.field = V4L2_FIELD_INTERLACED;
+
+ ctx->dec_dst_flag &= ~(1 << dst_buf->b->v4l2_buf.index);
+ vb2_buffer_done(dst_buf->b, VB2_BUF_STATE_DONE);
+ }
+}
+
+static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_buf, *src_buf;
+ size_t dec_y_addr = s5p_mfc_get_dec_y_adr();
+ unsigned int frame_type = s5p_mfc_get_frame_type();
+
+ /* Copy timestamp / timecode from decoded src to dst and set
+ appropraite flags */
+ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
+ if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) {
+ memcpy(&dst_buf->b->v4l2_buf.timecode,
+ &src_buf->b->v4l2_buf.timecode,
+ sizeof(struct v4l2_timecode));
+ memcpy(&dst_buf->b->v4l2_buf.timestamp,
+ &src_buf->b->v4l2_buf.timestamp,
+ sizeof(struct timeval));
+ switch (frame_type) {
+ case S5P_FIMV_DECODE_FRAME_I_FRAME:
+ dst_buf->b->v4l2_buf.flags |=
+ V4L2_BUF_FLAG_KEYFRAME;
+ break;
+ case S5P_FIMV_DECODE_FRAME_P_FRAME:
+ dst_buf->b->v4l2_buf.flags |=
+ V4L2_BUF_FLAG_PFRAME;
+ break;
+ case S5P_FIMV_DECODE_FRAME_B_FRAME:
+ dst_buf->b->v4l2_buf.flags |=
+ V4L2_BUF_FLAG_BFRAME;
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_buf;
+ size_t dspl_y_addr = s5p_mfc_get_dspl_y_adr();
+ unsigned int frame_type = s5p_mfc_get_frame_type();
+ unsigned int index;
+
+ /* If frame is same as previous then skip and do not dequeue */
+ if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
+ if (!ctx->after_packed_pb)
+ ctx->sequence++;
+ ctx->after_packed_pb = 0;
+ return;
+ }
+ ctx->sequence++;
+ /* The MFC returns address of the buffer, now we have to
+ * check which videobuf does it correspond to */
+ list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
+ /* Check if this is the buffer we're looking for */
+ if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dspl_y_addr) {
+ list_del(&dst_buf->list);
+ ctx->dst_queue_cnt--;
+ dst_buf->b->v4l2_buf.sequence = ctx->sequence;
+ if (s5p_mfc_read_shm(ctx, PIC_TIME_TOP) ==
+ s5p_mfc_read_shm(ctx, PIC_TIME_BOT))
+ dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
+ else
+ dst_buf->b->v4l2_buf.field =
+ V4L2_FIELD_INTERLACED;
+ vb2_set_plane_payload(dst_buf->b, 0, ctx->luma_size);
+ vb2_set_plane_payload(dst_buf->b, 1, ctx->chroma_size);
+ clear_bit(dst_buf->b->v4l2_buf.index,
+ &ctx->dec_dst_flag);
+
+ vb2_buffer_done(dst_buf->b,
+ err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+
+ index = dst_buf->b->v4l2_buf.index;
+ break;
+ }
+ }
+}
+
+/* Handle frame decoding interrupt */
+static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int dst_frame_status;
+ struct s5p_mfc_buf *src_buf;
+ unsigned long flags;
+ unsigned int res_change;
+
+ unsigned int index;
+
+ dst_frame_status = s5p_mfc_get_dspl_status()
+ & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
+ res_change = s5p_mfc_get_dspl_status()
+ & S5P_FIMV_DEC_STATUS_RESOLUTION_MASK;
+ mfc_debug(2, "Frame Status: %x\n", dst_frame_status);
+ if (ctx->state == MFCINST_RES_CHANGE_INIT)
+ ctx->state = MFCINST_RES_CHANGE_FLUSH;
+ if (res_change) {
+ ctx->state = MFCINST_RES_CHANGE_INIT;
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+ return;
+ }
+ if (ctx->dpb_flush_flag)
+ ctx->dpb_flush_flag = 0;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ /* All frames remaining in the buffer have been extracted */
+ if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
+ if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
+ s5p_mfc_handle_frame_all_extracted(ctx);
+ ctx->state = MFCINST_RES_CHANGE_END;
+ goto leave_handle_frame;
+ } else {
+ s5p_mfc_handle_frame_all_extracted(ctx);
+ }
+ }
+
+ if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY ||
+ dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY)
+ s5p_mfc_handle_frame_copy_time(ctx);
+
+ /* A frame has been decoded and is in the buffer */
+ if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
+ dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
+ s5p_mfc_handle_frame_new(ctx, err);
+ } else {
+ mfc_debug(2, "No frame decode\n");
+ }
+ /* Mark source buffer as complete */
+ if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
+ && !list_empty(&ctx->src_queue)) {
+ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
+ list);
+ ctx->consumed_stream += s5p_mfc_get_consumed_stream();
+ if (ctx->codec_mode != S5P_FIMV_CODEC_H264_DEC &&
+ s5p_mfc_get_frame_type() == S5P_FIMV_DECODE_FRAME_P_FRAME
+ && ctx->consumed_stream + STUFF_BYTE <
+ src_buf->b->v4l2_planes[0].bytesused) {
+ /* Run MFC again on the same buffer */
+ mfc_debug(2, "Running again the same buffer\n");
+ ctx->after_packed_pb = 1;
+ } else {
+ index = src_buf->b->v4l2_buf.index;
+ mfc_debug(2, "MFC needs next buffer\n");
+ ctx->consumed_stream = 0;
+ list_del(&src_buf->list);
+ ctx->src_queue_cnt--;
+ if (s5p_mfc_err_dec(err) > 0)
+ vb2_buffer_done(src_buf->b, VB2_BUF_STATE_ERROR);
+ else
+ vb2_buffer_done(src_buf->b, VB2_BUF_STATE_DONE);
+ }
+ }
+leave_handle_frame:
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
+ || ctx->dst_queue_cnt < ctx->dpb_count)
+ clear_work_bit(ctx);
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+}
+
+/* Error handling for interrupt */
+static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_dev *dev;
+ unsigned long flags;
+
+ /* If no context is available then all necessary
+ * processing has been done. */
+ if (ctx == 0)
+ return;
+
+ dev = ctx->dev;
+ mfc_err("Interrupt Error: %08x\n", err);
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_dev(dev, reason, err);
+
+ /* Error recovery is dependent on the state of context */
+ switch (ctx->state) {
+ case MFCINST_INIT:
+ /* This error had to happen while acquireing instance */
+ case MFCINST_GOT_INST:
+ /* This error had to happen while parsing the header */
+ case MFCINST_HEAD_PARSED:
+ /* This error had to happen while setting dst buffers */
+ case MFCINST_RETURN_INST:
+ /* This error had to happen while releasing instance */
+ clear_work_bit(ctx);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ ctx->state = MFCINST_ERROR;
+ break;
+ case MFCINST_FINISHING:
+ case MFCINST_FINISHED:
+ case MFCINST_RUNNING:
+ /* It is higly probable that an error occured
+ * while decoding a frame */
+ clear_work_bit(ctx);
+ ctx->state = MFCINST_ERROR;
+ /* Mark all dst buffers as having an error */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ /* Mark all src buffers as having an error */
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ break;
+ default:
+ mfc_err("Encountered an error interrupt which had not been handled\n");
+ break;
+ }
+ return;
+}
+
+/* Header parsing interrupt handling */
+static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_dev *dev;
+ unsigned int guard_width, guard_height;
+
+ if (ctx == 0)
+ return;
+ dev = ctx->dev;
+ if (ctx->c_ops->post_seq_start) {
+ if (ctx->c_ops->post_seq_start(ctx))
+ mfc_err("post_seq_start() failed\n");
+ } else {
+ ctx->img_width = s5p_mfc_get_img_width();
+ ctx->img_height = s5p_mfc_get_img_height();
+
+ ctx->buf_width = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN);
+ ctx->buf_height = ALIGN(ctx->img_height,
+ S5P_FIMV_NV12MT_VALIGN);
+ mfc_debug(2, "SEQ Done: Movie dimensions %dx%d, "
+ "buffer dimensions: %dx%d\n", ctx->img_width,
+ ctx->img_height, ctx->buf_width,
+ ctx->buf_height);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
+ ctx->luma_size = ALIGN(ctx->buf_width *
+ ctx->buf_height, S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->chroma_size = ALIGN(ctx->buf_width *
+ ALIGN((ctx->img_height >> 1),
+ S5P_FIMV_NV12MT_VALIGN),
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->mv_size = ALIGN(ctx->buf_width *
+ ALIGN((ctx->buf_height >> 2),
+ S5P_FIMV_NV12MT_VALIGN),
+ S5P_FIMV_DEC_BUF_ALIGN);
+ } else {
+ guard_width = ALIGN(ctx->img_width + 24,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN(ctx->img_height + 16,
+ S5P_FIMV_NV12MT_VALIGN);
+ ctx->luma_size = ALIGN(guard_width *
+ guard_height, S5P_FIMV_DEC_BUF_ALIGN);
+ guard_width = ALIGN(ctx->img_width + 16,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN((ctx->img_height >> 1) + 4,
+ S5P_FIMV_NV12MT_VALIGN);
+ ctx->chroma_size = ALIGN(guard_width *
+ guard_height, S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->mv_size = 0;
+ }
+ ctx->dpb_count = s5p_mfc_get_dpb_count();
+ if (ctx->img_width == 0 || ctx->img_width == 0)
+ ctx->state = MFCINST_ERROR;
+ else
+ ctx->state = MFCINST_HEAD_PARSED;
+ }
+ s5p_mfc_clear_int_flags(dev);
+ clear_work_bit(ctx);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+ wake_up_ctx(ctx, reason, err);
+}
+
+/* Header parsing interrupt handling */
+static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_buf *src_buf;
+ struct s5p_mfc_dev *dev;
+ unsigned long flags;
+
+ if (ctx == 0)
+ return;
+ dev = ctx->dev;
+ s5p_mfc_clear_int_flags(dev);
+ ctx->int_type = reason;
+ ctx->int_err = err;
+ ctx->int_cond = 1;
+ spin_lock(&dev->condlock);
+ clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock(&dev->condlock);
+ if (err == 0) {
+ ctx->state = MFCINST_RUNNING;
+ if (!ctx->dpb_flush_flag) {
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (!list_empty(&ctx->src_queue)) {
+ src_buf = list_entry(ctx->src_queue.next,
+ struct s5p_mfc_buf, list);
+ list_del(&src_buf->list);
+ ctx->src_queue_cnt--;
+ vb2_buffer_done(src_buf->b,
+ VB2_BUF_STATE_DONE);
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else {
+ ctx->dpb_flush_flag = 0;
+ }
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+
+ s5p_mfc_clock_off();
+
+ wake_up(&ctx->queue);
+ s5p_mfc_try_run(dev);
+ } else {
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+
+ s5p_mfc_clock_off();
+
+ wake_up(&ctx->queue);
+ }
+}
+
+/* Interrupt processing */
+static irqreturn_t s5p_mfc_irq(int irq, void *priv)
+{
+ struct s5p_mfc_dev *dev = priv;
+ struct s5p_mfc_ctx *ctx;
+ unsigned int reason;
+ unsigned int err;
+
+ mfc_debug_enter();
+ /* Reset the timeout watchdog */
+ atomic_set(&dev->watchdog_cnt, 0);
+ ctx = dev->ctx[dev->curr_ctx];
+ /* Get the reason of interrupt and the error code */
+ reason = s5p_mfc_get_int_reason();
+ err = s5p_mfc_get_int_err();
+ mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err);
+ switch (reason) {
+ case S5P_FIMV_R2H_CMD_ERR_RET:
+ /* An error has occured */
+ if (ctx->state == MFCINST_RUNNING &&
+ s5p_mfc_err_dec(err) >= S5P_FIMV_ERR_WARNINGS_START)
+ s5p_mfc_handle_frame(ctx, reason, err);
+ else
+ s5p_mfc_handle_error(ctx, reason, err);
+ clear_bit(0, &dev->enter_suspend);
+ break;
+
+ case S5P_FIMV_R2H_CMD_SLICE_DONE_RET:
+ case S5P_FIMV_R2H_CMD_FRAME_DONE_RET:
+ if (ctx->c_ops->post_frame_start) {
+ if (ctx->c_ops->post_frame_start(ctx))
+ mfc_err("post_frame_start() failed\n");
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+ } else {
+ s5p_mfc_handle_frame(ctx, reason, err);
+ }
+ break;
+
+ case S5P_FIMV_R2H_CMD_SEQ_DONE_RET:
+ s5p_mfc_handle_seq_done(ctx, reason, err);
+ break;
+
+ case S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET:
+ ctx->inst_no = s5p_mfc_get_inst_no();
+ ctx->state = MFCINST_GOT_INST;
+ clear_work_bit(ctx);
+ wake_up(&ctx->queue);
+ goto irq_cleanup_hw;
+
+ case S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET:
+ clear_work_bit(ctx);
+ ctx->state = MFCINST_FREE;
+ wake_up(&ctx->queue);
+ goto irq_cleanup_hw;
+
+ case S5P_FIMV_R2H_CMD_SYS_INIT_RET:
+ case S5P_FIMV_R2H_CMD_FW_STATUS_RET:
+ case S5P_FIMV_R2H_CMD_SLEEP_RET:
+ case S5P_FIMV_R2H_CMD_WAKEUP_RET:
+ if (ctx)
+ clear_work_bit(ctx);
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_dev(dev, reason, err);
+ clear_bit(0, &dev->hw_lock);
+ clear_bit(0, &dev->enter_suspend);
+ break;
+
+ case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET:
+ s5p_mfc_handle_init_buffers(ctx, reason, err);
+ break;
+ default:
+ mfc_debug(2, "Unknown int reason\n");
+ s5p_mfc_clear_int_flags(dev);
+ }
+ mfc_debug_leave();
+ return IRQ_HANDLED;
+irq_cleanup_hw:
+ s5p_mfc_clear_int_flags(dev);
+ ctx->int_type = reason;
+ ctx->int_err = err;
+ ctx->int_cond = 1;
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ mfc_err("Failed to unlock hw\n");
+
+ s5p_mfc_clock_off();
+
+ s5p_mfc_try_run(dev);
+ mfc_debug(2, "Exit via irq_cleanup_hw\n");
+ return IRQ_HANDLED;
+}
+
+/* Open an MFC node */
+static int s5p_mfc_open(struct file *file)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = NULL;
+ struct vb2_queue *q;
+ unsigned long flags;
+ int ret = 0;
+
+ mfc_debug_enter();
+ dev->num_inst++; /* It is guarded by mfc_mutex in vfd */
+ /* Allocate memory for context */
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx) {
+ mfc_err("Not enough memory\n");
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+ ctx->dev = dev;
+ INIT_LIST_HEAD(&ctx->src_queue);
+ INIT_LIST_HEAD(&ctx->dst_queue);
+ ctx->src_queue_cnt = 0;
+ ctx->dst_queue_cnt = 0;
+ /* Get context number */
+ ctx->num = 0;
+ while (dev->ctx[ctx->num]) {
+ ctx->num++;
+ if (ctx->num >= MFC_NUM_CONTEXTS) {
+ mfc_err("Too many open contexts\n");
+ ret = -EBUSY;
+ goto err_no_ctx;
+ }
+ }
+ /* Mark context as idle */
+ spin_lock_irqsave(&dev->condlock, flags);
+ clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ dev->ctx[ctx->num] = ctx;
+ if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ ctx->type = MFCINST_DECODER;
+ ctx->c_ops = get_dec_codec_ops();
+ /* Setup ctrl handler */
+ ret = s5p_mfc_dec_ctrls_setup(ctx);
+ if (ret) {
+ mfc_err("Failed to setup mfc controls\n");
+ goto err_ctrls_setup;
+ }
+ } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ ctx->type = MFCINST_ENCODER;
+ ctx->c_ops = get_enc_codec_ops();
+ /* only for encoder */
+ INIT_LIST_HEAD(&ctx->ref_queue);
+ ctx->ref_queue_cnt = 0;
+ /* Setup ctrl handler */
+ ret = s5p_mfc_enc_ctrls_setup(ctx);
+ if (ret) {
+ mfc_err("Failed to setup mfc controls\n");
+ goto err_ctrls_setup;
+ }
+ } else {
+ ret = -ENOENT;
+ goto err_bad_node;
+ }
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ ctx->inst_no = -1;
+ /* Load firmware if this is the first instance */
+ if (dev->num_inst == 1) {
+ dev->watchdog_timer.expires = jiffies +
+ msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
+ add_timer(&dev->watchdog_timer);
+ ret = s5p_mfc_power_on();
+ if (ret < 0) {
+ mfc_err("power on failed\n");
+ goto err_pwr_enable;
+ }
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_alloc_and_load_firmware(dev);
+ if (ret)
+ goto err_alloc_fw;
+ /* Init the FW */
+ ret = s5p_mfc_init_hw(dev);
+ if (ret)
+ goto err_init_hw;
+ s5p_mfc_clock_off();
+ }
+ /* Init videobuf2 queue for CAPTURE */
+ q = &ctx->vq_dst;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->drv_priv = &ctx->fh;
+ if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ q->io_modes = VB2_MMAP;
+ q->ops = get_dec_queue_ops();
+ } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = get_enc_queue_ops();
+ } else {
+ ret = -ENOENT;
+ goto err_queue_init;
+ }
+ q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ ret = vb2_queue_init(q);
+ if (ret) {
+ mfc_err("Failed to initialize videobuf2 queue(capture)\n");
+ goto err_queue_init;
+ }
+ /* Init videobuf2 queue for OUTPUT */
+ q = &ctx->vq_src;
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ q->io_modes = VB2_MMAP;
+ q->drv_priv = &ctx->fh;
+ if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ q->io_modes = VB2_MMAP;
+ q->ops = get_dec_queue_ops();
+ } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = get_enc_queue_ops();
+ } else {
+ ret = -ENOENT;
+ goto err_queue_init;
+ }
+ q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ ret = vb2_queue_init(q);
+ if (ret) {
+ mfc_err("Failed to initialize videobuf2 queue(output)\n");
+ goto err_queue_init;
+ }
+ init_waitqueue_head(&ctx->queue);
+ mfc_debug_leave();
+ return ret;
+ /* Deinit when failure occured */
+err_queue_init:
+err_init_hw:
+ s5p_mfc_release_firmware(dev);
+err_alloc_fw:
+ dev->ctx[ctx->num] = 0;
+ del_timer_sync(&dev->watchdog_timer);
+ s5p_mfc_clock_off();
+err_pwr_enable:
+ if (dev->num_inst == 1) {
+ if (s5p_mfc_power_off() < 0)
+ mfc_err("power off failed\n");
+ s5p_mfc_release_firmware(dev);
+ }
+err_ctrls_setup:
+ s5p_mfc_dec_ctrls_delete(ctx);
+err_bad_node:
+err_no_ctx:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+err_alloc:
+ dev->num_inst--;
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Release MFC context */
+static int s5p_mfc_release(struct file *file)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+
+ mfc_debug_enter();
+ s5p_mfc_clock_on();
+ vb2_queue_release(&ctx->vq_src);
+ vb2_queue_release(&ctx->vq_dst);
+ /* Mark context as idle */
+ spin_lock_irqsave(&dev->condlock, flags);
+ clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ /* If instance was initialised then
+ * return instance and free reosurces */
+ if (ctx->inst_no != MFC_NO_INSTANCE_SET) {
+ mfc_debug(2, "Has to free instance\n");
+ ctx->state = MFCINST_RETURN_INST;
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_try_run(dev);
+ /* Wait until instance is returned or timeout occured */
+ if (s5p_mfc_wait_for_done_ctx
+ (ctx, S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
+ s5p_mfc_clock_off();
+ mfc_err("Err returning instance\n");
+ }
+ mfc_debug(2, "After free instance\n");
+ /* Free resources */
+ s5p_mfc_release_codec_buffers(ctx);
+ s5p_mfc_release_instance_buffer(ctx);
+ if (ctx->type == MFCINST_DECODER)
+ s5p_mfc_release_dec_desc_buffer(ctx);
+
+ ctx->inst_no = MFC_NO_INSTANCE_SET;
+ }
+ /* hardware locking scheme */
+ if (dev->curr_ctx == ctx->num)
+ clear_bit(0, &dev->hw_lock);
+ dev->num_inst--;
+ if (dev->num_inst == 0) {
+ mfc_debug(2, "Last instance - release firmware\n");
+ /* reset <-> F/W release */
+ s5p_mfc_reset(dev);
+ s5p_mfc_release_firmware(dev);
+ del_timer_sync(&dev->watchdog_timer);
+ if (s5p_mfc_power_off() < 0)
+ mfc_err("Power off failed\n");
+ }
+ mfc_debug(2, "Shutting down clock\n");
+ s5p_mfc_clock_off();
+ dev->ctx[ctx->num] = 0;
+ s5p_mfc_dec_ctrls_delete(ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Poll */
+static unsigned int s5p_mfc_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct vb2_queue *src_q, *dst_q;
+ struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
+ unsigned int rc = 0;
+ unsigned long flags;
+
+ src_q = &ctx->vq_src;
+ dst_q = &ctx->vq_dst;
+ /*
+ * There has to be at least one buffer queued on each queued_list, which
+ * means either in driver already or waiting for driver to claim it
+ * and start processing.
+ */
+ if ((!src_q->streaming || list_empty(&src_q->queued_list))
+ && (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
+ rc = POLLERR;
+ goto end;
+ }
+ mutex_unlock(&dev->mfc_mutex);
+ poll_wait(file, &src_q->done_wq, wait);
+ poll_wait(file, &dst_q->done_wq, wait);
+ mutex_lock(&dev->mfc_mutex);
+ spin_lock_irqsave(&src_q->done_lock, flags);
+ if (!list_empty(&src_q->done_list))
+ src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
+ done_entry);
+ if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE
+ || src_vb->state == VB2_BUF_STATE_ERROR))
+ rc |= POLLOUT | POLLWRNORM;
+ spin_unlock_irqrestore(&src_q->done_lock, flags);
+ spin_lock_irqsave(&dst_q->done_lock, flags);
+ if (!list_empty(&dst_q->done_list))
+ dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
+ done_entry);
+ if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE
+ || dst_vb->state == VB2_BUF_STATE_ERROR))
+ rc |= POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&dst_q->done_lock, flags);
+end:
+ return rc;
+}
+
+/* Mmap */
+static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ int ret;
+ if (offset < DST_QUEUE_OFF_BASE) {
+ mfc_debug(2, "mmaping source\n");
+ ret = vb2_mmap(&ctx->vq_src, vma);
+ } else { /* capture */
+ mfc_debug(2, "mmaping destination\n");
+ vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+ ret = vb2_mmap(&ctx->vq_dst, vma);
+ }
+ return ret;
+}
+
+/* v4l2 ops */
+static const struct v4l2_file_operations s5p_mfc_fops = {
+ .owner = THIS_MODULE,
+ .open = s5p_mfc_open,
+ .release = s5p_mfc_release,
+ .poll = s5p_mfc_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = s5p_mfc_mmap,
+};
+
+static int match_child(struct device *dev, void *data)
+{
+ if (!dev_name(dev))
+ return 0;
+ return !strcmp(dev_name(dev), (char *)data);
+}
+
+/* MFC probe function */
+static int s5p_mfc_probe(struct platform_device *pdev)
+{
+ struct s5p_mfc_dev *dev;
+ struct video_device *vfd;
+ struct resource *res;
+ int ret;
+
+ pr_debug("%s++\n", __func__);
+ dev = kzalloc(sizeof *dev, GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev, "Not enough memory for MFC device\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&dev->irqlock);
+ spin_lock_init(&dev->condlock);
+ dev->plat_dev = pdev;
+ if (!dev->plat_dev) {
+ dev_err(&pdev->dev, "No platform data specified\n");
+ ret = -ENODEV;
+ goto err_dev;
+ }
+
+ ret = s5p_mfc_init_pm(dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get mfc clock source\n");
+ goto err_clk;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region resource\n");
+ ret = -ENOENT;
+ goto err_res;
+ }
+
+ dev->mfc_mem = request_mem_region(res->start, resource_size(res),
+ pdev->name);
+ if (dev->mfc_mem == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region\n");
+ ret = -ENOENT;
+ goto err_mem_reg;
+ }
+ dev->regs_base = ioremap(dev->mfc_mem->start, resource_size(dev->mfc_mem));
+ if (dev->regs_base == NULL) {
+ dev_err(&pdev->dev, "failed to ioremap address region\n");
+ ret = -ENOENT;
+ goto err_ioremap;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get irq resource\n");
+ ret = -ENOENT;
+ goto err_get_res;
+ }
+ dev->irq = res->start;
+ ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name,
+ dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
+ goto err_req_irq;
+ }
+
+ dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l",
+ match_child);
+ if (!dev->mem_dev_l) {
+ mfc_err("Mem child (L) device get failed\n");
+ ret = -ENODEV;
+ goto err_find_child;
+ }
+ dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r",
+ match_child);
+ if (!dev->mem_dev_r) {
+ mfc_err("Mem child (R) device get failed\n");
+ ret = -ENODEV;
+ goto err_find_child;
+ }
+
+ dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l);
+ if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) {
+ ret = PTR_ERR(dev->alloc_ctx[0]);
+ goto err_mem_init_ctx_0;
+ }
+ dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r);
+ if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) {
+ ret = PTR_ERR(dev->alloc_ctx[1]);
+ goto err_mem_init_ctx_1;
+ }
+
+ mutex_init(&dev->mfc_mutex);
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ goto err_v4l2_dev_reg;
+ init_waitqueue_head(&dev->queue);
+
+ /* decoder */
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto err_dec_alloc;
+ }
+ vfd->fops = &s5p_mfc_fops,
+ vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
+ vfd->release = video_device_release,
+ vfd->lock = &dev->mfc_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME);
+ dev->vfd_dec = vfd;
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ video_device_release(vfd);
+ goto err_dec_reg;
+ }
+ v4l2_info(&dev->v4l2_dev,
+ "decoder registered as /dev/video%d\n", vfd->num);
+ video_set_drvdata(vfd, dev);
+
+ /* encoder */
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto err_enc_alloc;
+ }
+ vfd->fops = &s5p_mfc_fops,
+ vfd->ioctl_ops = get_enc_v4l2_ioctl_ops();
+ vfd->release = video_device_release,
+ vfd->lock = &dev->mfc_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME);
+ dev->vfd_enc = vfd;
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ video_device_release(vfd);
+ goto err_enc_reg;
+ }
+ v4l2_info(&dev->v4l2_dev,
+ "encoder registered as /dev/video%d\n", vfd->num);
+ video_set_drvdata(vfd, dev);
+ platform_set_drvdata(pdev, dev);
+
+ dev->hw_lock = 0;
+ dev->watchdog_workqueue = create_singlethread_workqueue(S5P_MFC_NAME);
+ INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
+ atomic_set(&dev->watchdog_cnt, 0);
+ init_timer(&dev->watchdog_timer);
+ dev->watchdog_timer.data = (unsigned long)dev;
+ dev->watchdog_timer.function = s5p_mfc_watchdog;
+
+ pr_debug("%s--\n", __func__);
+ return 0;
+
+/* Deinit MFC if probe had failed */
+err_enc_reg:
+ video_device_release(dev->vfd_enc);
+err_enc_alloc:
+ video_unregister_device(dev->vfd_dec);
+err_dec_reg:
+ video_device_release(dev->vfd_dec);
+err_dec_alloc:
+ v4l2_device_unregister(&dev->v4l2_dev);
+err_v4l2_dev_reg:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
+err_mem_init_ctx_1:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
+err_mem_init_ctx_0:
+err_find_child:
+ free_irq(dev->irq, dev);
+err_req_irq:
+err_get_res:
+ iounmap(dev->regs_base);
+ dev->regs_base = NULL;
+err_ioremap:
+ release_resource(dev->mfc_mem);
+ kfree(dev->mfc_mem);
+err_mem_reg:
+err_res:
+ s5p_mfc_final_pm(dev);
+err_clk:
+err_dev:
+ kfree(dev);
+ pr_debug("%s-- with error\n", __func__);
+ return ret;
+
+}
+
+/* Remove the driver */
+static int __devexit s5p_mfc_remove(struct platform_device *pdev)
+{
+ struct s5p_mfc_dev *dev = platform_get_drvdata(pdev);
+
+ v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
+
+ del_timer_sync(&dev->watchdog_timer);
+ flush_workqueue(dev->watchdog_workqueue);
+ destroy_workqueue(dev->watchdog_workqueue);
+
+ video_unregister_device(dev->vfd_enc);
+ video_unregister_device(dev->vfd_dec);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
+
+ free_irq(dev->irq, dev);
+ iounmap(dev->regs_base);
+ if (dev->mfc_mem) {
+ release_resource(dev->mfc_mem);
+ kfree(dev->mfc_mem);
+ dev->mfc_mem = NULL;
+ }
+ s5p_mfc_final_pm(dev);
+ kfree(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int s5p_mfc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+ int ret;
+
+ if (m_dev->num_inst == 0)
+ return 0;
+ return s5p_mfc_sleep(m_dev);
+ if (test_and_set_bit(0, &m_dev->enter_suspend) != 0) {
+ mfc_err("Error: going to suspend for a second time\n");
+ return -EIO;
+ }
+
+ /* Check if we're processing then wait if it necessary. */
+ while (test_and_set_bit(0, &m_dev->hw_lock) != 0) {
+ /* Try and lock the HW */
+ /* Wait on the interrupt waitqueue */
+ ret = wait_event_interruptible_timeout(m_dev->queue,
+ m_dev->int_cond || m_dev->ctx[m_dev->curr_ctx]->int_cond,
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+
+ if (ret == 0) {
+ mfc_err("Waiting for hardware to finish timed out\n");
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+static int s5p_mfc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+
+ if (m_dev->num_inst == 0)
+ return 0;
+ return s5p_mfc_wakeup(m_dev);
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int s5p_mfc_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+
+ atomic_set(&m_dev->pm.power, 0);
+ return 0;
+}
+
+static int s5p_mfc_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+ int pre_power;
+
+ if (!m_dev->alloc_ctx)
+ return 0;
+ pre_power = atomic_read(&m_dev->pm.power);
+ atomic_set(&m_dev->pm.power, 1);
+ return 0;
+}
+#endif
+
+/* Power management */
+static const struct dev_pm_ops s5p_mfc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(s5p_mfc_suspend, s5p_mfc_resume)
+ SET_RUNTIME_PM_OPS(s5p_mfc_runtime_suspend, s5p_mfc_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver s5p_mfc_driver = {
+ .probe = s5p_mfc_probe,
+ .remove = __devexit_p(s5p_mfc_remove),
+ .driver = {
+ .name = S5P_MFC_NAME,
+ .owner = THIS_MODULE,
+ .pm = &s5p_mfc_pm_ops
+ },
+};
+
+static char banner[] __initdata =
+ "S5P MFC V4L2 Driver, (C) 2011 Samsung Electronics\n";
+
+static int __init s5p_mfc_init(void)
+{
+ int ret;
+
+ pr_info("%s", banner);
+ ret = platform_driver_register(&s5p_mfc_driver);
+ if (ret)
+ pr_err("Platform device registration failed.\n");
+ return ret;
+}
+
+static void __exit s5p_mfc_exit(void)
+{
+ platform_driver_unregister(&s5p_mfc_driver);
+}
+
+module_init(s5p_mfc_init);
+module_exit(s5p_mfc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
+MODULE_DESCRIPTION("Samsung S5P Multi Format Codec V4L2 driver");
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
new file mode 100644
index 000000000000..f0665ed1a529
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
@@ -0,0 +1,120 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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 "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+
+/* This function is used to send a command to the MFC */
+static int s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev, int cmd,
+ struct s5p_mfc_cmd_args *args)
+{
+ int cur_cmd;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+ /* wait until host to risc command register becomes 'H2R_CMD_EMPTY' */
+ do {
+ if (time_after(jiffies, timeout)) {
+ mfc_err("Timeout while waiting for hardware\n");
+ return -EIO;
+ }
+ cur_cmd = mfc_read(dev, S5P_FIMV_HOST2RISC_CMD);
+ } while (cur_cmd != S5P_FIMV_H2R_CMD_EMPTY);
+ mfc_write(dev, args->arg[0], S5P_FIMV_HOST2RISC_ARG1);
+ mfc_write(dev, args->arg[1], S5P_FIMV_HOST2RISC_ARG2);
+ mfc_write(dev, args->arg[2], S5P_FIMV_HOST2RISC_ARG3);
+ mfc_write(dev, args->arg[3], S5P_FIMV_HOST2RISC_ARG4);
+ /* Issue the command */
+ mfc_write(dev, cmd, S5P_FIMV_HOST2RISC_CMD);
+ return 0;
+}
+
+/* Initialize the MFC */
+int s5p_mfc_sys_init_cmd(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_cmd_args h2r_args;
+
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ h2r_args.arg[0] = dev->fw_size;
+ return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SYS_INIT, &h2r_args);
+}
+
+/* Suspend the MFC hardware */
+int s5p_mfc_sleep_cmd(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_cmd_args h2r_args;
+
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SLEEP, &h2r_args);
+}
+
+/* Wake up the MFC hardware */
+int s5p_mfc_wakeup_cmd(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_cmd_args h2r_args;
+
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_WAKEUP, &h2r_args);
+}
+
+
+int s5p_mfc_open_inst_cmd(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_cmd_args h2r_args;
+ int ret;
+
+ /* Preparing decoding - getting instance number */
+ mfc_debug(2, "Getting instance number (codec: %d)\n", ctx->codec_mode);
+ dev->curr_ctx = ctx->num;
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ h2r_args.arg[0] = ctx->codec_mode;
+ h2r_args.arg[1] = 0; /* no crc & no pixelcache */
+ h2r_args.arg[2] = ctx->ctx_ofs;
+ h2r_args.arg[3] = ctx->ctx_size;
+ ret = s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_OPEN_INSTANCE,
+ &h2r_args);
+ if (ret) {
+ mfc_err("Failed to create a new instance\n");
+ ctx->state = MFCINST_ERROR;
+ }
+ return ret;
+}
+
+int s5p_mfc_close_inst_cmd(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_cmd_args h2r_args;
+ int ret;
+
+ if (ctx->state == MFCINST_FREE) {
+ mfc_err("Instance already returned\n");
+ ctx->state = MFCINST_ERROR;
+ return -EINVAL;
+ }
+ /* Closing decoding instance */
+ mfc_debug(2, "Returning instance number %d\n", ctx->inst_no);
+ dev->curr_ctx = ctx->num;
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ h2r_args.arg[0] = ctx->inst_no;
+ ret = s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_CLOSE_INSTANCE,
+ &h2r_args);
+ if (ret) {
+ mfc_err("Failed to return an instance\n");
+ ctx->state = MFCINST_ERROR;
+ return -EINVAL;
+ }
+ return 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
new file mode 100644
index 000000000000..5ceebfe6131a
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
@@ -0,0 +1,30 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_CMD_H_
+#define S5P_MFC_CMD_H_
+
+#include "s5p_mfc_common.h"
+
+#define MAX_H2R_ARG 4
+
+struct s5p_mfc_cmd_args {
+ unsigned int arg[MAX_H2R_ARG];
+};
+
+int s5p_mfc_sys_init_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_sleep_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_wakeup_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_open_inst_cmd(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_close_inst_cmd(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_CMD_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
new file mode 100644
index 000000000000..91146fa622e4
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
@@ -0,0 +1,572 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.0
+ *
+ * This file contains definitions of enums and structs used by the codec
+ * driver.
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.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
+ */
+
+#ifndef S5P_MFC_COMMON_H_
+#define S5P_MFC_COMMON_H_
+
+#include "regs-mfc.h"
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+
+/* Definitions related to MFC memory */
+
+/* Offset base used to differentiate between CAPTURE and OUTPUT
+* while mmaping */
+#define DST_QUEUE_OFF_BASE (TASK_SIZE / 2)
+
+/* Offset used by the hardware to store addresses */
+#define MFC_OFFSET_SHIFT 11
+
+#define FIRMWARE_ALIGN 0x20000 /* 128KB */
+#define MFC_H264_CTX_BUF_SIZE 0x96000 /* 600KB per H264 instance */
+#define MFC_CTX_BUF_SIZE 0x2800 /* 10KB per instance */
+#define DESC_BUF_SIZE 0x20000 /* 128KB for DESC buffer */
+#define SHARED_BUF_SIZE 0x2000 /* 8KB for shared buffer */
+
+#define DEF_CPB_SIZE 0x40000 /* 512KB */
+
+#define MFC_BANK1_ALLOC_CTX 0
+#define MFC_BANK2_ALLOC_CTX 1
+
+#define MFC_BANK1_ALIGN_ORDER 13
+#define MFC_BANK2_ALIGN_ORDER 13
+#define MFC_BASE_ALIGN_ORDER 17
+
+#include <media/videobuf2-dma-contig.h>
+
+static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
+{
+ /* Same functionality as the vb2_dma_contig_plane_paddr */
+ dma_addr_t *paddr = vb2_dma_contig_memops.cookie(b);
+
+ return *paddr;
+}
+
+/* MFC definitions */
+#define MFC_MAX_EXTRA_DPB 5
+#define MFC_MAX_BUFFERS 32
+#define MFC_NUM_CONTEXTS 4
+/* Interrupt timeout */
+#define MFC_INT_TIMEOUT 2000
+/* Busy wait timeout */
+#define MFC_BW_TIMEOUT 500
+/* Watchdog interval */
+#define MFC_WATCHDOG_INTERVAL 1000
+/* After how many executions watchdog should assume lock up */
+#define MFC_WATCHDOG_CNT 10
+#define MFC_NO_INSTANCE_SET -1
+#define MFC_ENC_CAP_PLANE_COUNT 1
+#define MFC_ENC_OUT_PLANE_COUNT 2
+#define STUFF_BYTE 4
+#define MFC_MAX_CTRLS 64
+
+#define mfc_read(dev, offset) readl(dev->regs_base + (offset))
+#define mfc_write(dev, data, offset) writel((data), dev->regs_base + \
+ (offset))
+
+/**
+ * enum s5p_mfc_fmt_type - type of the pixelformat
+ */
+enum s5p_mfc_fmt_type {
+ MFC_FMT_DEC,
+ MFC_FMT_ENC,
+ MFC_FMT_RAW,
+};
+
+/**
+ * enum s5p_mfc_node_type - The type of an MFC device node.
+ */
+enum s5p_mfc_node_type {
+ MFCNODE_INVALID = -1,
+ MFCNODE_DECODER = 0,
+ MFCNODE_ENCODER = 1,
+};
+
+/**
+ * enum s5p_mfc_inst_type - The type of an MFC instance.
+ */
+enum s5p_mfc_inst_type {
+ MFCINST_INVALID,
+ MFCINST_DECODER,
+ MFCINST_ENCODER,
+};
+
+/**
+ * enum s5p_mfc_inst_state - The state of an MFC instance.
+ */
+enum s5p_mfc_inst_state {
+ MFCINST_FREE = 0,
+ MFCINST_INIT = 100,
+ MFCINST_GOT_INST,
+ MFCINST_HEAD_PARSED,
+ MFCINST_BUFS_SET,
+ MFCINST_RUNNING,
+ MFCINST_FINISHING,
+ MFCINST_FINISHED,
+ MFCINST_RETURN_INST,
+ MFCINST_ERROR,
+ MFCINST_ABORT,
+ MFCINST_RES_CHANGE_INIT,
+ MFCINST_RES_CHANGE_FLUSH,
+ MFCINST_RES_CHANGE_END,
+};
+
+/**
+ * enum s5p_mfc_queue_state - The state of buffer queue.
+ */
+enum s5p_mfc_queue_state {
+ QUEUE_FREE,
+ QUEUE_BUFS_REQUESTED,
+ QUEUE_BUFS_QUERIED,
+ QUEUE_BUFS_MMAPED,
+};
+
+/**
+ * enum s5p_mfc_decode_arg - type of frame decoding
+ */
+enum s5p_mfc_decode_arg {
+ MFC_DEC_FRAME,
+ MFC_DEC_LAST_FRAME,
+ MFC_DEC_RES_CHANGE,
+};
+
+struct s5p_mfc_ctx;
+
+/**
+ * struct s5p_mfc_buf - MFC buffer
+ */
+struct s5p_mfc_buf {
+ struct list_head list;
+ struct vb2_buffer *b;
+ union {
+ struct {
+ size_t luma;
+ size_t chroma;
+ } raw;
+ size_t stream;
+ } cookie;
+ int used;
+};
+
+/**
+ * struct s5p_mfc_pm - power management data structure
+ */
+struct s5p_mfc_pm {
+ struct clk *clock;
+ struct clk *clock_gate;
+ atomic_t power;
+ struct device *device;
+};
+
+/**
+ * struct s5p_mfc_dev - The struct containing driver internal parameters.
+ *
+ * @v4l2_dev: v4l2_device
+ * @vfd_dec: video device for decoding
+ * @vfd_enc: video device for encoding
+ * @plat_dev: platform device
+ * @mem_dev_l: child device of the left memory bank (0)
+ * @mem_dev_r: child device of the right memory bank (1)
+ * @regs_base: base address of the MFC hw registers
+ * @irq: irq resource
+ * @mfc_mem: MFC registers memory resource
+ * @dec_ctrl_handler: control framework handler for decoding
+ * @enc_ctrl_handler: control framework handler for encoding
+ * @pm: power management control
+ * @num_inst: couter of active MFC instances
+ * @irqlock: lock for operations on videobuf2 queues
+ * @condlock: lock for changing/checking if a context is ready to be
+ * processed
+ * @mfc_mutex: lock for video_device
+ * @int_cond: variable used by the waitqueue
+ * @int_type: type of last interrupt
+ * @int_err: error number for last interrupt
+ * @queue: waitqueue for waiting for completion of device commands
+ * @fw_size: size of firmware
+ * @bank1: address of the beggining of bank 1 memory
+ * @bank2: address of the beggining of bank 2 memory
+ * @hw_lock: used for hardware locking
+ * @ctx: array of driver contexts
+ * @curr_ctx: number of the currently running context
+ * @ctx_work_bits: used to mark which contexts are waiting for hardware
+ * @watchdog_cnt: counter for the watchdog
+ * @watchdog_workqueue: workqueue for the watchdog
+ * @watchdog_work: worker for the watchdog
+ * @alloc_ctx: videobuf2 allocator contexts for two memory banks
+ * @enter_suspend: flag set when entering suspend
+ *
+ */
+struct s5p_mfc_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd_dec;
+ struct video_device *vfd_enc;
+ struct platform_device *plat_dev;
+ struct device *mem_dev_l;
+ struct device *mem_dev_r;
+ void __iomem *regs_base;
+ int irq;
+ struct resource *mfc_mem;
+ struct v4l2_ctrl_handler dec_ctrl_handler;
+ struct v4l2_ctrl_handler enc_ctrl_handler;
+ struct s5p_mfc_pm pm;
+ int num_inst;
+ spinlock_t irqlock; /* lock when operating on videobuf2 queues */
+ spinlock_t condlock; /* lock when changing/checking if a context is
+ ready to be processed */
+ struct mutex mfc_mutex; /* video_device lock */
+ int int_cond;
+ int int_type;
+ unsigned int int_err;
+ wait_queue_head_t queue;
+ size_t fw_size;
+ size_t bank1;
+ size_t bank2;
+ unsigned long hw_lock;
+ struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
+ int curr_ctx;
+ unsigned long ctx_work_bits;
+ atomic_t watchdog_cnt;
+ struct timer_list watchdog_timer;
+ struct workqueue_struct *watchdog_workqueue;
+ struct work_struct watchdog_work;
+ void *alloc_ctx[2];
+ unsigned long enter_suspend;
+};
+
+/**
+ * struct s5p_mfc_h264_enc_params - encoding parameters for h264
+ */
+struct s5p_mfc_h264_enc_params {
+ enum v4l2_mpeg_video_h264_profile profile;
+ enum v4l2_mpeg_video_h264_loop_filter_mode loop_filter_mode;
+ s8 loop_filter_alpha;
+ s8 loop_filter_beta;
+ enum v4l2_mpeg_video_h264_entropy_mode entropy_mode;
+ u8 max_ref_pic;
+ u8 num_ref_pic_4p;
+ int _8x8_transform;
+ int rc_mb;
+ int rc_mb_dark;
+ int rc_mb_smooth;
+ int rc_mb_static;
+ int rc_mb_activity;
+ int vui_sar;
+ u8 vui_sar_idc;
+ u16 vui_ext_sar_width;
+ u16 vui_ext_sar_height;
+ int open_gop;
+ u16 open_gop_size;
+ u8 rc_frame_qp;
+ u8 rc_min_qp;
+ u8 rc_max_qp;
+ u8 rc_p_frame_qp;
+ u8 rc_b_frame_qp;
+ enum v4l2_mpeg_video_h264_level level_v4l2;
+ int level;
+ u16 cpb_size;
+};
+
+/**
+ * struct s5p_mfc_mpeg4_enc_params - encoding parameters for h263 and mpeg4
+ */
+struct s5p_mfc_mpeg4_enc_params {
+ /* MPEG4 Only */
+ enum v4l2_mpeg_video_mpeg4_profile profile;
+ int quarter_pixel;
+ /* Common for MPEG4, H263 */
+ u16 vop_time_res;
+ u16 vop_frm_delta;
+ u8 rc_frame_qp;
+ u8 rc_min_qp;
+ u8 rc_max_qp;
+ u8 rc_p_frame_qp;
+ u8 rc_b_frame_qp;
+ enum v4l2_mpeg_video_mpeg4_level level_v4l2;
+ int level;
+};
+
+/**
+ * struct s5p_mfc_enc_params - general encoding parameters
+ */
+struct s5p_mfc_enc_params {
+ u16 width;
+ u16 height;
+
+ u16 gop_size;
+ enum v4l2_mpeg_video_multi_slice_mode slice_mode;
+ u16 slice_mb;
+ u32 slice_bit;
+ u16 intra_refresh_mb;
+ int pad;
+ u8 pad_luma;
+ u8 pad_cb;
+ u8 pad_cr;
+ int rc_frame;
+ u32 rc_bitrate;
+ u16 rc_reaction_coeff;
+ u16 vbv_size;
+
+ enum v4l2_mpeg_video_header_mode seq_hdr_mode;
+ enum v4l2_mpeg_mfc51_video_frame_skip_mode frame_skip_mode;
+ int fixed_target_bit;
+
+ u8 num_b_frame;
+ u32 rc_framerate_num;
+ u32 rc_framerate_denom;
+ int interlace;
+
+ union {
+ struct s5p_mfc_h264_enc_params h264;
+ struct s5p_mfc_mpeg4_enc_params mpeg4;
+ } codec;
+
+};
+
+/**
+ * struct s5p_mfc_codec_ops - codec ops, used by encoding
+ */
+struct s5p_mfc_codec_ops {
+ /* initialization routines */
+ int (*pre_seq_start) (struct s5p_mfc_ctx *ctx);
+ int (*post_seq_start) (struct s5p_mfc_ctx *ctx);
+ /* execution routines */
+ int (*pre_frame_start) (struct s5p_mfc_ctx *ctx);
+ int (*post_frame_start) (struct s5p_mfc_ctx *ctx);
+};
+
+#define call_cop(c, op, args...) \
+ (((c)->c_ops->op) ? \
+ ((c)->c_ops->op(args)) : 0)
+
+/**
+ * struct s5p_mfc_ctx - This struct contains the instance context
+ *
+ * @dev: pointer to the s5p_mfc_dev of the device
+ * @fh: struct v4l2_fh
+ * @num: number of the context that this structure describes
+ * @int_cond: variable used by the waitqueue
+ * @int_type: type of the last interrupt
+ * @int_err: error number received from MFC hw in the interrupt
+ * @queue: waitqueue that can be used to wait for this context to
+ * finish
+ * @src_fmt: source pixelformat information
+ * @dst_fmt: destination pixelformat information
+ * @vq_src: vb2 queue for source buffers
+ * @vq_dst: vb2 queue for destination buffers
+ * @src_queue: driver internal queue for source buffers
+ * @dst_queue: driver internal queue for destination buffers
+ * @src_queue_cnt: number of buffers queued on the source internal queue
+ * @dst_queue_cnt: number of buffers queued on the dest internal queue
+ * @type: type of the instance - decoder or encoder
+ * @state: state of the context
+ * @inst_no: number of hw instance associated with the context
+ * @img_width: width of the image that is decoded or encoded
+ * @img_height: height of the image that is decoded or encoded
+ * @buf_width: width of the buffer for processed image
+ * @buf_height: height of the buffer for processed image
+ * @luma_size: size of a luma plane
+ * @chroma_size: size of a chroma plane
+ * @mv_size: size of a motion vectors buffer
+ * @consumed_stream: number of bytes that have been used so far from the
+ * decoding buffer
+ * @dpb_flush_flag: flag used to indicate that a DPB buffers are being
+ * flushed
+ * @bank1_buf: handle to memory allocated for temporary buffers from
+ * memory bank 1
+ * @bank1_phys: address of the temporary buffers from memory bank 1
+ * @bank1_size: size of the memory allocated for temporary buffers from
+ * memory bank 1
+ * @bank2_buf: handle to memory allocated for temporary buffers from
+ * memory bank 2
+ * @bank2_phys: address of the temporary buffers from memory bank 2
+ * @bank2_size: size of the memory allocated for temporary buffers from
+ * memory bank 2
+ * @capture_state: state of the capture buffers queue
+ * @output_state: state of the output buffers queue
+ * @src_bufs: information on allocated source buffers
+ * @dst_bufs: information on allocated destination buffers
+ * @sequence: counter for the sequence number for v4l2
+ * @dec_dst_flag: flags for buffers queued in the hardware
+ * @dec_src_buf_size: size of the buffer for source buffers in decoding
+ * @codec_mode: number of codec mode used by MFC hw
+ * @slice_interface: slice interface flag
+ * @loop_filter_mpeg4: loop filter for MPEG4 flag
+ * @display_delay: value of the display delay for H264
+ * @display_delay_enable: display delay for H264 enable flag
+ * @after_packed_pb: flag used to track buffer when stream is in
+ * Packed PB format
+ * @dpb_count: count of the DPB buffers required by MFC hw
+ * @total_dpb_count: count of DPB buffers with additional buffers
+ * requested by the application
+ * @ctx_buf: handle to the memory associated with this context
+ * @ctx_phys: address of the memory associated with this context
+ * @ctx_size: size of the memory associated with this context
+ * @desc_buf: description buffer for decoding handle
+ * @desc_phys: description buffer for decoding address
+ * @shm_alloc: handle for the shared memory buffer
+ * @shm: virtual address for the shared memory buffer
+ * @shm_ofs: address offset for shared memory
+ * @enc_params: encoding parameters for MFC
+ * @enc_dst_buf_size: size of the buffers for encoder output
+ * @frame_type: used to force the type of the next encoded frame
+ * @ref_queue: list of the reference buffers for encoding
+ * @ref_queue_cnt: number of the buffers in the reference list
+ * @c_ops: ops for encoding
+ * @ctrls: array of controls, used when adding controls to the
+ * v4l2 control framework
+ * @ctrl_handler: handler for v4l2 framework
+ */
+struct s5p_mfc_ctx {
+ struct s5p_mfc_dev *dev;
+ struct v4l2_fh fh;
+
+ int num;
+
+ int int_cond;
+ int int_type;
+ unsigned int int_err;
+ wait_queue_head_t queue;
+
+ struct s5p_mfc_fmt *src_fmt;
+ struct s5p_mfc_fmt *dst_fmt;
+
+ struct vb2_queue vq_src;
+ struct vb2_queue vq_dst;
+
+ struct list_head src_queue;
+ struct list_head dst_queue;
+
+ unsigned int src_queue_cnt;
+ unsigned int dst_queue_cnt;
+
+ enum s5p_mfc_inst_type type;
+ enum s5p_mfc_inst_state state;
+ int inst_no;
+
+ /* Image parameters */
+ int img_width;
+ int img_height;
+ int buf_width;
+ int buf_height;
+
+ int luma_size;
+ int chroma_size;
+ int mv_size;
+
+ unsigned long consumed_stream;
+
+ unsigned int dpb_flush_flag;
+
+ /* Buffers */
+ void *bank1_buf;
+ size_t bank1_phys;
+ size_t bank1_size;
+
+ void *bank2_buf;
+ size_t bank2_phys;
+ size_t bank2_size;
+
+ enum s5p_mfc_queue_state capture_state;
+ enum s5p_mfc_queue_state output_state;
+
+ struct s5p_mfc_buf src_bufs[MFC_MAX_BUFFERS];
+ int src_bufs_cnt;
+ struct s5p_mfc_buf dst_bufs[MFC_MAX_BUFFERS];
+ int dst_bufs_cnt;
+
+ unsigned int sequence;
+ unsigned long dec_dst_flag;
+ size_t dec_src_buf_size;
+
+ /* Control values */
+ int codec_mode;
+ int slice_interface;
+ int loop_filter_mpeg4;
+ int display_delay;
+ int display_delay_enable;
+ int after_packed_pb;
+
+ int dpb_count;
+ int total_dpb_count;
+
+ /* Buffers */
+ void *ctx_buf;
+ size_t ctx_phys;
+ size_t ctx_ofs;
+ size_t ctx_size;
+
+ void *desc_buf;
+ size_t desc_phys;
+
+
+ void *shm_alloc;
+ void *shm;
+ size_t shm_ofs;
+
+ struct s5p_mfc_enc_params enc_params;
+
+ size_t enc_dst_buf_size;
+
+ enum v4l2_mpeg_mfc51_video_force_frame_type force_frame_type;
+
+ struct list_head ref_queue;
+ unsigned int ref_queue_cnt;
+
+ struct s5p_mfc_codec_ops *c_ops;
+
+ struct v4l2_ctrl *ctrls[MFC_MAX_CTRLS];
+ struct v4l2_ctrl_handler ctrl_handler;
+};
+
+/*
+ * struct s5p_mfc_fmt - structure used to store information about pixelformats
+ * used by the MFC
+ */
+struct s5p_mfc_fmt {
+ char *name;
+ u32 fourcc;
+ u32 codec_mode;
+ enum s5p_mfc_fmt_type type;
+ u32 num_planes;
+};
+
+/**
+ * struct mfc_control - structure used to store information about MFC controls
+ * it is used to initialize the control framework.
+ */
+struct mfc_control {
+ __u32 id;
+ enum v4l2_ctrl_type type;
+ __u8 name[32]; /* Whatever */
+ __s32 minimum; /* Note signedness */
+ __s32 maximum;
+ __s32 step;
+ __u32 menu_skip_mask;
+ __s32 default_value;
+ __u32 flags;
+ __u32 reserved[2];
+ __u8 is_volatile;
+};
+
+
+#define fh_to_ctx(__fh) container_of(__fh, struct s5p_mfc_ctx, fh)
+#define ctrl_to_ctx(__ctrl) \
+ container_of((__ctrl)->handler, struct s5p_mfc_ctx, ctrl_handler)
+
+#endif /* S5P_MFC_COMMON_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
new file mode 100644
index 000000000000..f2481a85e0a2
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
@@ -0,0 +1,343 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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/delay.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_pm.h"
+
+static void *s5p_mfc_bitproc_buf;
+static size_t s5p_mfc_bitproc_phys;
+static unsigned char *s5p_mfc_bitproc_virt;
+
+/* Allocate and load firmware */
+int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
+{
+ struct firmware *fw_blob;
+ size_t bank2_base_phys;
+ void *b_base;
+ int err;
+
+ /* Firmare has to be present as a separate file or compiled
+ * into kernel. */
+ mfc_debug_enter();
+ err = request_firmware((const struct firmware **)&fw_blob,
+ "s5p-mfc.fw", dev->v4l2_dev.dev);
+ if (err != 0) {
+ mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
+ return -EINVAL;
+ }
+ dev->fw_size = ALIGN(fw_blob->size, FIRMWARE_ALIGN);
+ if (s5p_mfc_bitproc_buf) {
+ mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size);
+ if (IS_ERR(s5p_mfc_bitproc_buf)) {
+ s5p_mfc_bitproc_buf = 0;
+ mfc_err("Allocating bitprocessor buffer failed\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf);
+ if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
+ mfc_err("The base memory for bank 1 is not aligned to 128KB\n");
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = 0;
+ release_firmware(fw_blob);
+ return -EIO;
+ }
+ s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf);
+ if (!s5p_mfc_bitproc_virt) {
+ mfc_err("Bitprocessor memory remap failed\n");
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = 0;
+ release_firmware(fw_blob);
+ return -EIO;
+ }
+ dev->bank1 = s5p_mfc_bitproc_phys;
+ b_base = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], 1 << MFC_BANK2_ALIGN_ORDER);
+ if (IS_ERR(b_base)) {
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = 0;
+ mfc_err("Allocating bank2 base failed\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ bank2_base_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base);
+ vb2_dma_contig_memops.put(b_base);
+ if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
+ mfc_err("The base memory for bank 2 is not aligned to 128KB\n");
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = 0;
+ release_firmware(fw_blob);
+ return -EIO;
+ }
+ dev->bank2 = bank2_base_phys;
+ memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
+ wmb();
+ release_firmware(fw_blob);
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Reload firmware to MFC */
+int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
+{
+ struct firmware *fw_blob;
+ int err;
+
+ /* Firmare has to be present as a separate file or compiled
+ * into kernel. */
+ mfc_debug_enter();
+ err = request_firmware((const struct firmware **)&fw_blob,
+ "s5p-mfc.fw", dev->v4l2_dev.dev);
+ if (err != 0) {
+ mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
+ return -EINVAL;
+ }
+ if (fw_blob->size > dev->fw_size) {
+ mfc_err("MFC firmware is too big to be loaded\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ if (s5p_mfc_bitproc_buf == 0 || s5p_mfc_bitproc_phys == 0) {
+ mfc_err("MFC firmware is not allocated or was not mapped correctly\n");
+ release_firmware(fw_blob);
+ return -EINVAL;
+ }
+ memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
+ wmb();
+ release_firmware(fw_blob);
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Release firmware memory */
+int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)
+{
+ /* Before calling this function one has to make sure
+ * that MFC is no longer processing */
+ if (!s5p_mfc_bitproc_buf)
+ return -EINVAL;
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_virt = 0;
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = 0;
+ return 0;
+}
+
+/* Reset the device */
+int s5p_mfc_reset(struct s5p_mfc_dev *dev)
+{
+ unsigned int mc_status;
+ unsigned long timeout;
+
+ mfc_debug_enter();
+ /* Stop procedure */
+ /* reset RISC */
+ mfc_write(dev, 0x3f6, S5P_FIMV_SW_RESET);
+ /* All reset except for MC */
+ mfc_write(dev, 0x3e2, S5P_FIMV_SW_RESET);
+ mdelay(10);
+
+ timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+ /* Check MC status */
+ do {
+ if (time_after(jiffies, timeout)) {
+ mfc_err("Timeout while resetting MFC\n");
+ return -EIO;
+ }
+
+ mc_status = mfc_read(dev, S5P_FIMV_MC_STATUS);
+
+ } while (mc_status & 0x3);
+
+ mfc_write(dev, 0x0, S5P_FIMV_SW_RESET);
+ mfc_write(dev, 0x3fe, S5P_FIMV_SW_RESET);
+ mfc_debug_leave();
+ return 0;
+}
+
+static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
+{
+ mfc_write(dev, dev->bank1, S5P_FIMV_MC_DRAMBASE_ADR_A);
+ mfc_write(dev, dev->bank2, S5P_FIMV_MC_DRAMBASE_ADR_B);
+ mfc_debug(2, "Bank1: %08x, Bank2: %08x\n", dev->bank1, dev->bank2);
+}
+
+static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)
+{
+ mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
+ mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
+ mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
+ mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD);
+}
+
+/* Initialize hardware */
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
+{
+ unsigned int ver;
+ int ret;
+
+ mfc_debug_enter();
+ if (!s5p_mfc_bitproc_buf)
+ return -EINVAL;
+
+ /* 0. MFC reset */
+ mfc_debug(2, "MFC reset..\n");
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_reset(dev);
+ if (ret) {
+ mfc_err("Failed to reset MFC - timeout\n");
+ return ret;
+ }
+ mfc_debug(2, "Done MFC reset..\n");
+ /* 1. Set DRAM base Addr */
+ s5p_mfc_init_memctrl(dev);
+ /* 2. Initialize registers of channel I/F */
+ s5p_mfc_clear_cmds(dev);
+ /* 3. Release reset signal to the RISC */
+ s5p_mfc_clean_dev_int_flags(dev);
+ mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
+ mfc_debug(2, "Will now wait for completion of firmware transfer\n");
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET)) {
+ mfc_err("Failed to load firmware\n");
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return -EIO;
+ }
+ s5p_mfc_clean_dev_int_flags(dev);
+ /* 4. Initialize firmware */
+ ret = s5p_mfc_sys_init_cmd(dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFC - timeout\n");
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return ret;
+ }
+ mfc_debug(2, "Ok, now will write a command to init the system\n");
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET)) {
+ mfc_err("Failed to load firmware\n");
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return -EIO;
+ }
+ dev->int_cond = 0;
+ if (dev->int_err != 0 || dev->int_type !=
+ S5P_FIMV_R2H_CMD_SYS_INIT_RET) {
+ /* Failure. */
+ mfc_err("Failed to init firmware - error: %d int: %d\n",
+ dev->int_err, dev->int_type);
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return -EIO;
+ }
+ ver = mfc_read(dev, S5P_FIMV_FW_VERSION);
+ mfc_debug(2, "MFC F/W version : %02xyy, %02xmm, %02xdd\n",
+ (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);
+ s5p_mfc_clock_off();
+ mfc_debug_leave();
+ return 0;
+}
+
+
+int s5p_mfc_sleep(struct s5p_mfc_dev *dev)
+{
+ int ret;
+
+ mfc_debug_enter();
+ s5p_mfc_clock_on();
+ s5p_mfc_clean_dev_int_flags(dev);
+ ret = s5p_mfc_sleep_cmd(dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFC - timeout\n");
+ return ret;
+ }
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SLEEP_RET)) {
+ mfc_err("Failed to sleep\n");
+ return -EIO;
+ }
+ s5p_mfc_clock_off();
+ dev->int_cond = 0;
+ if (dev->int_err != 0 || dev->int_type !=
+ S5P_FIMV_R2H_CMD_SLEEP_RET) {
+ /* Failure. */
+ mfc_err("Failed to sleep - error: %d int: %d\n", dev->int_err,
+ dev->int_type);
+ return -EIO;
+ }
+ mfc_debug_leave();
+ return ret;
+}
+
+int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
+{
+ int ret;
+
+ mfc_debug_enter();
+ /* 0. MFC reset */
+ mfc_debug(2, "MFC reset..\n");
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_reset(dev);
+ if (ret) {
+ mfc_err("Failed to reset MFC - timeout\n");
+ return ret;
+ }
+ mfc_debug(2, "Done MFC reset..\n");
+ /* 1. Set DRAM base Addr */
+ s5p_mfc_init_memctrl(dev);
+ /* 2. Initialize registers of channel I/F */
+ s5p_mfc_clear_cmds(dev);
+ s5p_mfc_clean_dev_int_flags(dev);
+ /* 3. Initialize firmware */
+ ret = s5p_mfc_wakeup_cmd(dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFC - timeout\n");
+ return ret;
+ }
+ /* 4. Release reset signal to the RISC */
+ mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
+ mfc_debug(2, "Ok, now will write a command to wakeup the system\n");
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_WAKEUP_RET)) {
+ mfc_err("Failed to load firmware\n");
+ return -EIO;
+ }
+ s5p_mfc_clock_off();
+ dev->int_cond = 0;
+ if (dev->int_err != 0 || dev->int_type !=
+ S5P_FIMV_R2H_CMD_WAKEUP_RET) {
+ /* Failure. */
+ mfc_err("Failed to wakeup - error: %d int: %d\n", dev->int_err,
+ dev->int_type);
+ return -EIO;
+ }
+ mfc_debug_leave();
+ return 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
new file mode 100644
index 000000000000..61dc23b7ee5a
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
@@ -0,0 +1,29 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_CTRL_H
+#define S5P_MFC_CTRL_H
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_sleep(struct s5p_mfc_dev *dev);
+int s5p_mfc_wakeup(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_reset(struct s5p_mfc_dev *dev);
+
+#endif /* S5P_MFC_CTRL_H */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_debug.h b/drivers/media/video/s5p-mfc/s5p_mfc_debug.h
new file mode 100644
index 000000000000..ecb8616a492a
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_debug.h
@@ -0,0 +1,48 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_debug.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains debug macros
+ *
+ * Kamil Debski, Copyright (c) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef S5P_MFC_DEBUG_H_
+#define S5P_MFC_DEBUG_H_
+
+#define DEBUG
+
+#ifdef DEBUG
+extern int debug;
+
+#define mfc_debug(level, fmt, args...) \
+ do { \
+ if (debug >= level) \
+ printk(KERN_DEBUG "%s:%d: " fmt, \
+ __func__, __LINE__, ##args); \
+ } while (0)
+#else
+#define mfc_debug(level, fmt, args...)
+#endif
+
+#define mfc_debug_enter() mfc_debug(5, "enter")
+#define mfc_debug_leave() mfc_debug(5, "leave")
+
+#define mfc_err(fmt, args...) \
+ do { \
+ printk(KERN_ERR "%s:%d: " fmt, \
+ __func__, __LINE__, ##args); \
+ } while (0)
+
+#define mfc_info(fmt, args...) \
+ do { \
+ printk(KERN_INFO "%s:%d: " fmt, \
+ __func__, __LINE__, ##args); \
+ } while (0)
+
+#endif /* S5P_MFC_DEBUG_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
new file mode 100644
index 000000000000..844a4d7797bc
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
@@ -0,0 +1,1037 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ * Kamil Debski, <k.debski@samsung.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/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_dec.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+
+static struct s5p_mfc_fmt formats[] = {
+ {
+ .name = "4:2:0 2 Planes 64x32 Tiles",
+ .fourcc = V4L2_PIX_FMT_NV12MT,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "4:2:0 2 Planes",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "H264 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H264,
+ .codec_mode = S5P_FIMV_CODEC_H264_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "H263 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H263,
+ .codec_mode = S5P_FIMV_CODEC_H263_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG1 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG1,
+ .codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG2 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG2,
+ .codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG4 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "XviD Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_XVID,
+ .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "VC1 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
+ .codec_mode = S5P_FIMV_CODEC_VC1_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "VC1 RCV Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
+ .codec_mode = S5P_FIMV_CODEC_VC1RCV_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+/* Find selected format description */
+static struct s5p_mfc_fmt *find_format(struct v4l2_format *f, unsigned int t)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+ formats[i].type == t)
+ return &formats[i];
+ }
+ return NULL;
+}
+
+static struct mfc_control controls[] = {
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H264 Display Delay",
+ .minimum = 0,
+ .maximum = 16383,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Display Delay Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mpeg4 Loop Filter Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Slice Interface Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Minimum number of cap bufs",
+ .minimum = 1,
+ .maximum = 32,
+ .step = 1,
+ .default_value = 1,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ },
+};
+
+#define NUM_CTRLS ARRAY_SIZE(controls)
+
+/* Check whether a context should be run on hardware */
+static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+ /* Context is to parse header */
+ if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_GOT_INST)
+ return 1;
+ /* Context is to decode a frame */
+ if (ctx->src_queue_cnt >= 1 &&
+ ctx->state == MFCINST_RUNNING &&
+ ctx->dst_queue_cnt >= ctx->dpb_count)
+ return 1;
+ /* Context is to return last frame */
+ if (ctx->state == MFCINST_FINISHING &&
+ ctx->dst_queue_cnt >= ctx->dpb_count)
+ return 1;
+ /* Context is to set buffers */
+ if (ctx->src_queue_cnt >= 1 &&
+ ctx->state == MFCINST_HEAD_PARSED &&
+ ctx->capture_state == QUEUE_BUFS_MMAPED)
+ return 1;
+ /* Resolution change */
+ if ((ctx->state == MFCINST_RES_CHANGE_INIT ||
+ ctx->state == MFCINST_RES_CHANGE_FLUSH) &&
+ ctx->dst_queue_cnt >= ctx->dpb_count)
+ return 1;
+ if (ctx->state == MFCINST_RES_CHANGE_END &&
+ ctx->src_queue_cnt >= 1)
+ return 1;
+ mfc_debug(2, "ctx is not ready\n");
+ return 0;
+}
+
+static struct s5p_mfc_codec_ops decoder_codec_ops = {
+ .pre_seq_start = NULL,
+ .post_seq_start = NULL,
+ .pre_frame_start = NULL,
+ .post_frame_start = NULL,
+};
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+
+ strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+/* Enumerate format */
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+ struct s5p_mfc_fmt *fmt;
+ int i, j = 0;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (mplane && formats[i].num_planes == 1)
+ continue;
+ else if (!mplane && formats[i].num_planes > 1)
+ continue;
+ if (out && formats[i].type != MFC_FMT_DEC)
+ continue;
+ else if (!out && formats[i].type != MFC_FMT_RAW)
+ continue;
+
+ if (j == f->index)
+ break;
+ ++j;
+ }
+ if (i == ARRAY_SIZE(formats))
+ return -EINVAL;
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, true);
+}
+
+/* Get format */
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_pix_format_mplane *pix_mp;
+
+ mfc_debug_enter();
+ pix_mp = &f->fmt.pix_mp;
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ (ctx->state == MFCINST_GOT_INST || ctx->state ==
+ MFCINST_RES_CHANGE_END)) {
+ /* If the MFC is parsing the header,
+ * so wait until it is finished */
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_SEQ_DONE_RET,
+ 0);
+ }
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ /* This is run on CAPTURE (decode output) */
+ /* Width and height are set to the dimensions
+ of the movie, the buffer is bigger and
+ further processing stages should crop to this
+ rectangle. */
+ pix_mp->width = ctx->buf_width;
+ pix_mp->height = ctx->buf_height;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->num_planes = 2;
+ /* Set pixelformat to the format in which MFC
+ outputs the decoded frame */
+ pix_mp->pixelformat = V4L2_PIX_FMT_NV12MT;
+ pix_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+ pix_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* This is run on OUTPUT
+ The buffer contains compressed image
+ so width and height have no meaning */
+ pix_mp->width = 0;
+ pix_mp->height = 0;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->plane_fmt[0].bytesperline = ctx->dec_src_buf_size;
+ pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size;
+ pix_mp->pixelformat = ctx->src_fmt->fourcc;
+ pix_mp->num_planes = ctx->src_fmt->num_planes;
+ } else {
+ mfc_err("Format could not be read\n");
+ mfc_debug(2, "%s-- with error\n", __func__);
+ return -EINVAL;
+ }
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Try format */
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_fmt *fmt;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_err("This node supports decoding only\n");
+ return -EINVAL;
+ }
+ fmt = find_format(f, MFC_FMT_DEC);
+ if (!fmt) {
+ mfc_err("Unsupported format\n");
+ return -EINVAL;
+ }
+ if (fmt->type != MFC_FMT_DEC) {
+ mfc_err("\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Set format */
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+ struct s5p_mfc_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_mp;
+
+ mfc_debug_enter();
+ ret = vidioc_try_fmt(file, priv, f);
+ pix_mp = &f->fmt.pix_mp;
+ if (ret)
+ return ret;
+ if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
+ v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto out;
+ }
+ fmt = find_format(f, MFC_FMT_DEC);
+ if (!fmt || fmt->codec_mode == S5P_FIMV_CODEC_NONE) {
+ mfc_err("Unknown codec\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (fmt->type != MFC_FMT_DEC) {
+ mfc_err("Wrong format selected, you should choose "
+ "format for decoding\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ ctx->src_fmt = fmt;
+ ctx->codec_mode = fmt->codec_mode;
+ mfc_debug(2, "The codec number is: %d\n", ctx->codec_mode);
+ pix_mp->height = 0;
+ pix_mp->width = 0;
+ if (pix_mp->plane_fmt[0].sizeimage)
+ ctx->dec_src_buf_size = pix_mp->plane_fmt[0].sizeimage;
+ else
+ pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size =
+ DEF_CPB_SIZE;
+ pix_mp->plane_fmt[0].bytesperline = 0;
+ ctx->state = MFCINST_INIT;
+out:
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Reqeust buffers */
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+ unsigned long flags;
+
+ if (reqbufs->memory != V4L2_MEMORY_MMAP) {
+ mfc_err("Only V4L2_MEMORY_MAP is supported\n");
+ return -EINVAL;
+ }
+ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* Can only request buffers after an instance has been opened.*/
+ if (ctx->state == MFCINST_INIT) {
+ ctx->src_bufs_cnt = 0;
+ if (reqbufs->count == 0) {
+ mfc_debug(2, "Freeing buffers\n");
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ s5p_mfc_clock_off();
+ return ret;
+ }
+ /* Decoding */
+ if (ctx->output_state != QUEUE_FREE) {
+ mfc_err("Bufs have already been requested\n");
+ return -EINVAL;
+ }
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ s5p_mfc_clock_off();
+ if (ret) {
+ mfc_err("vb2_reqbufs on output failed\n");
+ return ret;
+ }
+ mfc_debug(2, "vb2_reqbufs: %d\n", ret);
+ ctx->output_state = QUEUE_BUFS_REQUESTED;
+ }
+ } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ctx->dst_bufs_cnt = 0;
+ if (reqbufs->count == 0) {
+ mfc_debug(2, "Freeing buffers\n");
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ return ret;
+ }
+ if (ctx->capture_state != QUEUE_FREE) {
+ mfc_err("Bufs have already been requested\n");
+ return -EINVAL;
+ }
+ ctx->capture_state = QUEUE_BUFS_REQUESTED;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ if (ret) {
+ mfc_err("vb2_reqbufs on capture failed\n");
+ return ret;
+ }
+ if (reqbufs->count < ctx->dpb_count) {
+ mfc_err("Not enough buffers allocated\n");
+ reqbufs->count = 0;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ return -ENOMEM;
+ }
+ ctx->total_dpb_count = reqbufs->count;
+ ret = s5p_mfc_alloc_codec_buffers(ctx);
+ if (ret) {
+ mfc_err("Failed to allocate decoding buffers\n");
+ reqbufs->count = 0;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ return -ENOMEM;
+ }
+ if (ctx->dst_bufs_cnt == ctx->total_dpb_count) {
+ ctx->capture_state = QUEUE_BUFS_MMAPED;
+ } else {
+ mfc_err("Not all buffers passed to buf_init\n");
+ reqbufs->count = 0;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_release_codec_buffers(ctx);
+ s5p_mfc_clock_off();
+ return -ENOMEM;
+ }
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET, 0);
+ }
+ return ret;
+}
+
+/* Query buffer */
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+ int i;
+
+ if (buf->memory != V4L2_MEMORY_MMAP) {
+ mfc_err("Only mmaped buffers can be used\n");
+ return -EINVAL;
+ }
+ mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type);
+ if (ctx->state == MFCINST_INIT &&
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_querybuf(&ctx->vq_src, buf);
+ } else if (ctx->state == MFCINST_RUNNING &&
+ buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = vb2_querybuf(&ctx->vq_dst, buf);
+ for (i = 0; i < buf->length; i++)
+ buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE;
+ } else {
+ mfc_err("vidioc_querybuf called in an inappropriate state\n");
+ ret = -EINVAL;
+ }
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on QBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_qbuf(&ctx->vq_src, buf);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_qbuf(&ctx->vq_dst, buf);
+ return -EINVAL;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on DQBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+ return -EINVAL;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ mfc_debug_enter();
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+
+ if (ctx->state == MFCINST_INIT) {
+ ctx->dst_bufs_cnt = 0;
+ ctx->src_bufs_cnt = 0;
+ ctx->capture_state = QUEUE_FREE;
+ ctx->output_state = QUEUE_FREE;
+ s5p_mfc_alloc_instance_buffer(ctx);
+ s5p_mfc_alloc_dec_temp_buffers(ctx);
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_try_run(dev);
+
+ if (s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
+ /* Error or timeout */
+ mfc_err("Error getting instance from hardware\n");
+ s5p_mfc_release_instance_buffer(ctx);
+ s5p_mfc_release_dec_desc_buffer(ctx);
+ return -EIO;
+ }
+ mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+ }
+ ret = vb2_streamon(&ctx->vq_src, type);
+ }
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ ret = vb2_streamon(&ctx->vq_dst, type);
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_streamoff(&ctx->vq_src, type);
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_streamoff(&ctx->vq_dst, type);
+ return -EINVAL;
+}
+
+/* Set controls - v4l2 control framework */
+static int s5p_mfc_dec_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY:
+ ctx->loop_filter_mpeg4 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE:
+ ctx->display_delay_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ ctx->display_delay = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+ ctx->slice_interface = ctrl->val;
+ break;
+ default:
+ mfc_err("Invalid control 0x%08x\n", ctrl->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ if (ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ ctrl->val = ctx->dpb_count;
+ break;
+ } else if (ctx->state != MFCINST_INIT) {
+ v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
+ return -EINVAL;
+ }
+ /* Should wait for the header to be parsed */
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_SEQ_DONE_RET, 0);
+ if (ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ ctrl->val = ctx->dpb_count;
+ } else {
+ v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+static const struct v4l2_ctrl_ops s5p_mfc_dec_ctrl_ops = {
+ .s_ctrl = s5p_mfc_dec_s_ctrl,
+ .g_volatile_ctrl = s5p_mfc_dec_g_v_ctrl,
+};
+
+/* Get cropping information */
+static int vidioc_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *cr)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ u32 left, right, top, bottom;
+
+ if (ctx->state != MFCINST_HEAD_PARSED &&
+ ctx->state != MFCINST_RUNNING && ctx->state != MFCINST_FINISHING
+ && ctx->state != MFCINST_FINISHED) {
+ mfc_err("Cannont set crop\n");
+ return -EINVAL;
+ }
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_H264) {
+ left = s5p_mfc_read_shm(ctx, CROP_INFO_H);
+ right = left >> S5P_FIMV_SHARED_CROP_RIGHT_SHIFT;
+ left = left & S5P_FIMV_SHARED_CROP_LEFT_MASK;
+ top = s5p_mfc_read_shm(ctx, CROP_INFO_V);
+ bottom = top >> S5P_FIMV_SHARED_CROP_BOTTOM_SHIFT;
+ top = top & S5P_FIMV_SHARED_CROP_TOP_MASK;
+ cr->c.left = left;
+ cr->c.top = top;
+ cr->c.width = ctx->img_width - left - right;
+ cr->c.height = ctx->img_height - top - bottom;
+ mfc_debug(2, "Cropping info [h264]: l=%d t=%d "
+ "w=%d h=%d (r=%d b=%d fw=%d fh=%d\n", left, top,
+ cr->c.width, cr->c.height, right, bottom,
+ ctx->buf_width, ctx->buf_height);
+ } else {
+ cr->c.left = 0;
+ cr->c.top = 0;
+ cr->c.width = ctx->img_width;
+ cr->c.height = ctx->img_height;
+ mfc_debug(2, "Cropping info: w=%d h=%d fw=%d "
+ "fh=%d\n", cr->c.width, cr->c.height, ctx->buf_width,
+ ctx->buf_height);
+ }
+ return 0;
+}
+
+/* v4l2_ioctl_ops */
+static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_crop = vidioc_g_crop,
+};
+
+static int s5p_mfc_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt, unsigned int *buf_count,
+ unsigned int *plane_count, unsigned int psize[],
+ void *allocators[])
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+ /* Video output for decoding (source)
+ * this can be set after getting an instance */
+ if (ctx->state == MFCINST_INIT &&
+ vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* A single plane is required for input */
+ *plane_count = 1;
+ if (*buf_count < 1)
+ *buf_count = 1;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ /* Video capture for decoding (destination)
+ * this can be set after the header was parsed */
+ } else if (ctx->state == MFCINST_HEAD_PARSED &&
+ vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ /* Output plane count is 2 - one for Y and one for CbCr */
+ *plane_count = 2;
+ /* Setup buffer count */
+ if (*buf_count < ctx->dpb_count)
+ *buf_count = ctx->dpb_count;
+ if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
+ *buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ } else {
+ mfc_err("State seems invalid. State = %d, vq->type = %d\n",
+ ctx->state, vq->type);
+ return -EINVAL;
+ }
+ mfc_debug(2, "Buffer count=%d, plane count=%d\n",
+ *buf_count, *plane_count);
+ if (ctx->state == MFCINST_HEAD_PARSED &&
+ vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ psize[0] = ctx->luma_size;
+ psize[1] = ctx->chroma_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+ allocators[1] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ ctx->state == MFCINST_INIT) {
+ psize[0] = ctx->dec_src_buf_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+ } else {
+ mfc_err("This video node is dedicated to decoding. Decoding not initalised\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void s5p_mfc_unlock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_unlock(&dev->mfc_mutex);
+}
+
+static void s5p_mfc_lock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_lock(&dev->mfc_mutex);
+}
+
+static int s5p_mfc_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ unsigned int i;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->capture_state == QUEUE_BUFS_MMAPED)
+ return 0;
+ for (i = 0; i <= ctx->src_fmt->num_planes ; i++) {
+ if (IS_ERR_OR_NULL(ERR_PTR(
+ vb2_dma_contig_plane_dma_addr(vb, i)))) {
+ mfc_err("Plane mem not allocated\n");
+ return -EINVAL;
+ }
+ }
+ if (vb2_plane_size(vb, 0) < ctx->luma_size ||
+ vb2_plane_size(vb, 1) < ctx->chroma_size) {
+ mfc_err("Plane buffer (CAPTURE) is too small\n");
+ return -EINVAL;
+ }
+ i = vb->v4l2_buf.index;
+ ctx->dst_bufs[i].b = vb;
+ ctx->dst_bufs[i].cookie.raw.luma =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->dst_bufs[i].cookie.raw.chroma =
+ vb2_dma_contig_plane_dma_addr(vb, 1);
+ ctx->dst_bufs_cnt++;
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (IS_ERR_OR_NULL(ERR_PTR(
+ vb2_dma_contig_plane_dma_addr(vb, 0)))) {
+ mfc_err("Plane memory not allocated\n");
+ return -EINVAL;
+ }
+ if (vb2_plane_size(vb, 0) < ctx->dec_src_buf_size) {
+ mfc_err("Plane buffer (OUTPUT) is too small\n");
+ return -EINVAL;
+ }
+
+ i = vb->v4l2_buf.index;
+ ctx->src_bufs[i].b = vb;
+ ctx->src_bufs[i].cookie.stream =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->src_bufs_cnt++;
+ } else {
+ mfc_err("s5p_mfc_buf_init: unknown queue type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+ if (ctx->state == MFCINST_FINISHING ||
+ ctx->state == MFCINST_FINISHED)
+ ctx->state = MFCINST_RUNNING;
+ /* If context is ready then dev = work->data;schedule it to run */
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+ return 0;
+}
+
+static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+{
+ unsigned long flags;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ int aborted = 0;
+
+ if ((ctx->state == MFCINST_FINISHING ||
+ ctx->state == MFCINST_RUNNING) &&
+ dev->curr_ctx == ctx->num && dev->hw_lock) {
+ ctx->state = MFCINST_ABORT;
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_FRAME_DONE_RET, 0);
+ aborted = 1;
+ }
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ INIT_LIST_HEAD(&ctx->dst_queue);
+ ctx->dst_queue_cnt = 0;
+ ctx->dpb_flush_flag = 1;
+ ctx->dec_dst_flag = 0;
+ }
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ INIT_LIST_HEAD(&ctx->src_queue);
+ ctx->src_queue_cnt = 0;
+ }
+ if (aborted)
+ ctx->state = MFCINST_RUNNING;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return 0;
+}
+
+
+static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *mfc_buf;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
+ mfc_buf->used = 0;
+ spin_lock_irqsave(&dev->irqlock, flags);
+ list_add_tail(&mfc_buf->list, &ctx->src_queue);
+ ctx->src_queue_cnt++;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
+ mfc_buf->used = 0;
+ /* Mark destination as available for use by MFC */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag);
+ list_add_tail(&mfc_buf->list, &ctx->dst_queue);
+ ctx->dst_queue_cnt++;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else {
+ mfc_err("Unsupported buffer type (%d)\n", vq->type);
+ }
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+}
+
+static struct vb2_ops s5p_mfc_dec_qops = {
+ .queue_setup = s5p_mfc_queue_setup,
+ .wait_prepare = s5p_mfc_unlock,
+ .wait_finish = s5p_mfc_lock,
+ .buf_init = s5p_mfc_buf_init,
+ .start_streaming = s5p_mfc_start_streaming,
+ .stop_streaming = s5p_mfc_stop_streaming,
+ .buf_queue = s5p_mfc_buf_queue,
+};
+
+struct s5p_mfc_codec_ops *get_dec_codec_ops(void)
+{
+ return &decoder_codec_ops;
+}
+
+struct vb2_ops *get_dec_queue_ops(void)
+{
+ return &s5p_mfc_dec_qops;
+}
+
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void)
+{
+ return &s5p_mfc_dec_ioctl_ops;
+}
+
+#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
+ && V4L2_CTRL_DRIVER_PRIV(x))
+
+int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx)
+{
+ struct v4l2_ctrl_config cfg;
+ int i;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
+ if (ctx->ctrl_handler.error) {
+ mfc_err("v4l2_ctrl_handler_init failed\n");
+ return ctx->ctrl_handler.error;
+ }
+
+ for (i = 0; i < NUM_CTRLS; i++) {
+ if (IS_MFC51_PRIV(controls[i].id)) {
+ cfg.ops = &s5p_mfc_dec_ctrl_ops;
+ cfg.id = controls[i].id;
+ cfg.min = controls[i].minimum;
+ cfg.max = controls[i].maximum;
+ cfg.def = controls[i].default_value;
+ cfg.name = controls[i].name;
+ cfg.type = controls[i].type;
+
+ cfg.step = controls[i].step;
+ cfg.menu_skip_mask = 0;
+
+ ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+ &cfg, NULL);
+ } else {
+ ctx->ctrls[i] = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &s5p_mfc_dec_ctrl_ops,
+ controls[i].id, controls[i].minimum,
+ controls[i].maximum, controls[i].step,
+ controls[i].default_value);
+ }
+ if (ctx->ctrl_handler.error) {
+ mfc_err("Adding control (%d) failed\n", i);
+ return ctx->ctrl_handler.error;
+ }
+ if (controls[i].is_volatile && ctx->ctrls[i])
+ ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+ return 0;
+}
+
+void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx)
+{
+ int i;
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ for (i = 0; i < NUM_CTRLS; i++)
+ ctx->ctrls[i] = NULL;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.h b/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
new file mode 100644
index 000000000000..fb8b215db0e7
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
@@ -0,0 +1,23 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_DEC_H_
+#define S5P_MFC_DEC_H_
+
+struct s5p_mfc_codec_ops *get_dec_codec_ops(void);
+struct vb2_ops *get_dec_queue_ops(void);
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void);
+struct s5p_mfc_fmt *get_dec_def_fmt(bool src);
+int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_DEC_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
new file mode 100644
index 000000000000..1e8cdb77d4b8
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
@@ -0,0 +1,1830 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Jeongtae Park <jtp.park@samsung.com>
+ * Kamil Debski <k.debski@samsung.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/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_enc.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+
+static struct s5p_mfc_fmt formats[] = {
+ {
+ .name = "4:2:0 2 Planes 64x32 Tiles",
+ .fourcc = V4L2_PIX_FMT_NV12MT,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "4:2:0 2 Planes",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "H264 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H264,
+ .codec_mode = S5P_FIMV_CODEC_H264_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG4 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .codec_mode = S5P_FIMV_CODEC_MPEG4_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ },
+ {
+ .name = "H264 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H263,
+ .codec_mode = S5P_FIMV_CODEC_H263_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+static struct s5p_mfc_fmt *find_format(struct v4l2_format *f, unsigned int t)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+ formats[i].type == t)
+ return &formats[i];
+ }
+ return NULL;
+}
+
+static struct mfc_control controls[] = {
+ {
+ .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+ .maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES,
+ .default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1900,
+ .maximum = (1 << 30) - 1,
+ .step = 1,
+ .default_value = 1900,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Padding Control Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Padding Color YUV Value",
+ .minimum = 0,
+ .maximum = (1 << 25) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = (1 << 30) - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Rate Control Reaction Coeff.",
+ .minimum = 1,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Force frame type",
+ .minimum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
+ .maximum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED,
+ .default_value = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VBV_SIZE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+ .maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ .default_value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Frame Skip Enable",
+ .minimum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
+ .maximum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
+ .menu_skip_mask = 0,
+ .default_value = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Fixed Target Bit Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
+ .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .menu_skip_mask = ~(
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)
+ ),
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
+ .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .menu_skip_mask = ~(
+ (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+ (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
+ (1 << V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
+ (1 << V4L2_MPEG_VIDEO_H264_LEVEL_5_1)
+ ),
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
+ .maximum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_5,
+ .default_value = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+ .maximum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY,
+ .default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = -6,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = -6,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+ .maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+ .default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "The Number of Ref. Pic for P",
+ .minimum = 1,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 I-Frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 Minimum QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 Maximum QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 P frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 B frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 I-Frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 Minimum QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 Maximum QP value",
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 P frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 B frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Dark Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Smooth Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Static Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Activity Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED,
+ .maximum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED,
+ .default_value = 0,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
+ .maximum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE,
+ .default_value = 0,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_QPEL,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+};
+
+#define NUM_CTRLS ARRAY_SIZE(controls)
+static const char * const *mfc51_get_menu(u32 id)
+{
+ static const char * const mfc51_video_frame_skip[] = {
+ "Disabled",
+ "Level Limit",
+ "VBV/CPB Limit",
+ NULL,
+ };
+ static const char * const mfc51_video_force_frame[] = {
+ "Disabled",
+ "I Frame",
+ "Not Coded",
+ NULL,
+ };
+ switch (id) {
+ case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
+ return mfc51_video_frame_skip;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+ return mfc51_video_force_frame;
+ }
+ return NULL;
+}
+
+static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+ mfc_debug(2, "src=%d, dst=%d, state=%d\n",
+ ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
+ /* context is ready to make header */
+ if (ctx->state == MFCINST_GOT_INST && ctx->dst_queue_cnt >= 1)
+ return 1;
+ /* context is ready to encode a frame */
+ if (ctx->state == MFCINST_RUNNING &&
+ ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1)
+ return 1;
+ /* context is ready to encode remain frames */
+ if (ctx->state == MFCINST_FINISHING &&
+ ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1)
+ return 1;
+ mfc_debug(2, "ctx is not ready\n");
+ return 0;
+}
+
+static void cleanup_ref_queue(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_buf *mb_entry;
+ unsigned long mb_y_addr, mb_c_addr;
+
+ /* move buffers in ref queue to src queue */
+ while (!list_empty(&ctx->ref_queue)) {
+ mb_entry = list_entry((&ctx->ref_queue)->next,
+ struct s5p_mfc_buf, list);
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
+ list_del(&mb_entry->list);
+ ctx->ref_queue_cnt--;
+ list_add_tail(&mb_entry->list, &ctx->src_queue);
+ ctx->src_queue_cnt++;
+ }
+ mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
+ ctx->src_queue_cnt, ctx->ref_queue_cnt);
+ INIT_LIST_HEAD(&ctx->ref_queue);
+ ctx->ref_queue_cnt = 0;
+}
+
+static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_mb;
+ unsigned long dst_addr;
+ unsigned int dst_size;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return 0;
+}
+
+static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_buf *dst_mb;
+ unsigned long flags;
+
+ if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) {
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next,
+ struct s5p_mfc_buf, list);
+ list_del(&dst_mb->list);
+ ctx->dst_queue_cnt--;
+ vb2_set_plane_payload(dst_mb->b, 0,
+ s5p_mfc_get_enc_strm_size());
+ vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ }
+ ctx->state = MFCINST_RUNNING;
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+ return 0;
+}
+
+static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_mb;
+ struct s5p_mfc_buf *src_mb;
+ unsigned long flags;
+ unsigned long src_y_addr, src_c_addr, dst_addr;
+ unsigned int dst_size;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
+ src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
+ s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+
+ return 0;
+}
+
+static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *mb_entry;
+ unsigned long enc_y_addr, enc_c_addr;
+ unsigned long mb_y_addr, mb_c_addr;
+ int slice_type;
+ unsigned int strm_size;
+ unsigned long flags;
+
+ slice_type = s5p_mfc_get_enc_slice_type();
+ strm_size = s5p_mfc_get_enc_strm_size();
+ mfc_debug(2, "Encoded slice type: %d", slice_type);
+ mfc_debug(2, "Encoded stream size: %d", strm_size);
+ mfc_debug(2, "Display order: %d",
+ mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT));
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (slice_type >= 0) {
+ s5p_mfc_get_enc_frame_buffer(ctx, &enc_y_addr, &enc_c_addr);
+ list_for_each_entry(mb_entry, &ctx->src_queue, list) {
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
+ if ((enc_y_addr == mb_y_addr) &&
+ (enc_c_addr == mb_c_addr)) {
+ list_del(&mb_entry->list);
+ ctx->src_queue_cnt--;
+ vb2_buffer_done(mb_entry->b,
+ VB2_BUF_STATE_DONE);
+ break;
+ }
+ }
+ list_for_each_entry(mb_entry, &ctx->ref_queue, list) {
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
+ if ((enc_y_addr == mb_y_addr) &&
+ (enc_c_addr == mb_c_addr)) {
+ list_del(&mb_entry->list);
+ ctx->ref_queue_cnt--;
+ vb2_buffer_done(mb_entry->b,
+ VB2_BUF_STATE_DONE);
+ break;
+ }
+ }
+ }
+ if ((ctx->src_queue_cnt > 0) && (ctx->state == MFCINST_RUNNING)) {
+ mb_entry = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
+ list);
+ if (mb_entry->used) {
+ list_del(&mb_entry->list);
+ ctx->src_queue_cnt--;
+ list_add_tail(&mb_entry->list, &ctx->ref_queue);
+ ctx->ref_queue_cnt++;
+ }
+ mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
+ ctx->src_queue_cnt, ctx->ref_queue_cnt);
+ }
+ if (strm_size > 0) {
+ /* at least one more dest. buffers exist always */
+ mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
+ list);
+ list_del(&mb_entry->list);
+ ctx->dst_queue_cnt--;
+ switch (slice_type) {
+ case S5P_FIMV_ENC_SI_SLICE_TYPE_I:
+ mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ break;
+ case S5P_FIMV_ENC_SI_SLICE_TYPE_P:
+ mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+ break;
+ case S5P_FIMV_ENC_SI_SLICE_TYPE_B:
+ mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_BFRAME;
+ break;
+ }
+ vb2_set_plane_payload(mb_entry->b, 0, strm_size);
+ vb2_buffer_done(mb_entry->b, VB2_BUF_STATE_DONE);
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ if ((ctx->src_queue_cnt == 0) || (ctx->dst_queue_cnt == 0)) {
+ spin_lock(&dev->condlock);
+ clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock(&dev->condlock);
+ }
+ return 0;
+}
+
+static struct s5p_mfc_codec_ops encoder_codec_ops = {
+ .pre_seq_start = enc_pre_seq_start,
+ .post_seq_start = enc_post_seq_start,
+ .pre_frame_start = enc_pre_frame_start,
+ .post_frame_start = enc_post_frame_start,
+};
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+
+ strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE
+ | V4L2_CAP_VIDEO_OUTPUT_MPLANE
+ | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+ struct s5p_mfc_fmt *fmt;
+ int i, j = 0;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (mplane && formats[i].num_planes == 1)
+ continue;
+ else if (!mplane && formats[i].num_planes > 1)
+ continue;
+ if (out && formats[i].type != MFC_FMT_RAW)
+ continue;
+ else if (!out && formats[i].type != MFC_FMT_ENC)
+ continue;
+ if (j == f->index) {
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name,
+ sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+ ++j;
+ }
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, true);
+}
+
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+ mfc_debug(2, "f->type = %d ctx->state = %d\n", f->type, ctx->state);
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ /* This is run on output (encoder dest) */
+ pix_fmt_mp->width = 0;
+ pix_fmt_mp->height = 0;
+ pix_fmt_mp->field = V4L2_FIELD_NONE;
+ pix_fmt_mp->pixelformat = ctx->dst_fmt->fourcc;
+ pix_fmt_mp->num_planes = ctx->dst_fmt->num_planes;
+
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->enc_dst_buf_size;
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->enc_dst_buf_size;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* This is run on capture (encoder src) */
+ pix_fmt_mp->width = ctx->img_width;
+ pix_fmt_mp->height = ctx->img_height;
+
+ pix_fmt_mp->field = V4L2_FIELD_NONE;
+ pix_fmt_mp->pixelformat = ctx->src_fmt->fourcc;
+ pix_fmt_mp->num_planes = ctx->src_fmt->num_planes;
+
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ fmt = find_format(f, MFC_FMT_ENC);
+ if (!fmt) {
+ mfc_err("failed to try output format\n");
+ return -EINVAL;
+ }
+
+ if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
+ mfc_err("must be set encoding output size\n");
+ return -EINVAL;
+ }
+
+ pix_fmt_mp->plane_fmt[0].bytesperline =
+ pix_fmt_mp->plane_fmt[0].sizeimage;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ fmt = find_format(f, MFC_FMT_RAW);
+ if (!fmt) {
+ mfc_err("failed to try output format\n");
+ return -EINVAL;
+ }
+
+ if (fmt->num_planes != pix_fmt_mp->num_planes) {
+ mfc_err("failed to try output format\n");
+ return -EINVAL;
+ }
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_mfc_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+ unsigned long flags;
+ int ret = 0;
+
+ ret = vidioc_try_fmt(file, priv, f);
+ if (ret)
+ return ret;
+ if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
+ v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto out;
+ }
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ fmt = find_format(f, MFC_FMT_ENC);
+ if (!fmt) {
+ mfc_err("failed to set capture format\n");
+ return -EINVAL;
+ }
+ ctx->state = MFCINST_INIT;
+ ctx->dst_fmt = fmt;
+ ctx->codec_mode = ctx->dst_fmt->codec_mode;
+ ctx->enc_dst_buf_size = pix_fmt_mp->plane_fmt[0].sizeimage;
+ pix_fmt_mp->plane_fmt[0].bytesperline = 0;
+ ctx->dst_bufs_cnt = 0;
+ ctx->capture_state = QUEUE_FREE;
+ s5p_mfc_alloc_instance_buffer(ctx);
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_try_run(dev);
+ if (s5p_mfc_wait_for_done_ctx(ctx, \
+ S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
+ /* Error or timeout */
+ mfc_err("Error getting instance from hardware\n");
+ s5p_mfc_release_instance_buffer(ctx);
+ ret = -EIO;
+ goto out;
+ }
+ mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ fmt = find_format(f, MFC_FMT_RAW);
+ if (!fmt) {
+ mfc_err("failed to set output format\n");
+ return -EINVAL;
+ }
+ if (fmt->num_planes != pix_fmt_mp->num_planes) {
+ mfc_err("failed to set output format\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ ctx->src_fmt = fmt;
+ ctx->img_width = pix_fmt_mp->width;
+ ctx->img_height = pix_fmt_mp->height;
+ mfc_debug(2, "codec number: %d\n", ctx->src_fmt->codec_mode);
+ mfc_debug(2, "fmt - w: %d, h: %d, ctx - w: %d, h: %d\n",
+ pix_fmt_mp->width, pix_fmt_mp->height,
+ ctx->img_width, ctx->img_height);
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {
+ ctx->buf_width = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12M_HALIGN);
+ ctx->luma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12M_HALIGN) * ALIGN(ctx->img_height,
+ S5P_FIMV_NV12M_LVALIGN);
+ ctx->chroma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12M_HALIGN) * ALIGN((ctx->img_height
+ >> 1), S5P_FIMV_NV12M_CVALIGN);
+
+ ctx->luma_size = ALIGN(ctx->luma_size,
+ S5P_FIMV_NV12M_SALIGN);
+ ctx->chroma_size = ALIGN(ctx->chroma_size,
+ S5P_FIMV_NV12M_SALIGN);
+
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+
+ } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT) {
+ ctx->buf_width = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN);
+ ctx->luma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN) * ALIGN(ctx->img_height,
+ S5P_FIMV_NV12MT_VALIGN);
+ ctx->chroma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN) * ALIGN((ctx->img_height
+ >> 1), S5P_FIMV_NV12MT_VALIGN);
+ ctx->luma_size = ALIGN(ctx->luma_size,
+ S5P_FIMV_NV12MT_SALIGN);
+ ctx->chroma_size = ALIGN(ctx->chroma_size,
+ S5P_FIMV_NV12MT_SALIGN);
+
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+ }
+ ctx->src_bufs_cnt = 0;
+ ctx->output_state = QUEUE_FREE;
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+out:
+ mfc_debug_leave();
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+
+ /* if memory is not mmp or userptr return error */
+ if ((reqbufs->memory != V4L2_MEMORY_MMAP) &&
+ (reqbufs->memory != V4L2_MEMORY_USERPTR))
+ return -EINVAL;
+ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->capture_state != QUEUE_FREE) {
+ mfc_err("invalid capture state: %d\n",
+ ctx->capture_state);
+ return -EINVAL;
+ }
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ if (ret != 0) {
+ mfc_err("error in vb2_reqbufs() for E(D)\n");
+ return ret;
+ }
+ ctx->capture_state = QUEUE_BUFS_REQUESTED;
+ ret = s5p_mfc_alloc_codec_buffers(ctx);
+ if (ret) {
+ mfc_err("Failed to allocate encoding buffers\n");
+ reqbufs->count = 0;
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ return -ENOMEM;
+ }
+ } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (ctx->output_state != QUEUE_FREE) {
+ mfc_err("invalid output state: %d\n",
+ ctx->output_state);
+ return -EINVAL;
+ }
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ if (ret != 0) {
+ mfc_err("error in vb2_reqbufs() for E(S)\n");
+ return ret;
+ }
+ ctx->output_state = QUEUE_BUFS_REQUESTED;
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+
+ /* if memory is not mmp or userptr return error */
+ if ((buf->memory != V4L2_MEMORY_MMAP) &&
+ (buf->memory != V4L2_MEMORY_USERPTR))
+ return -EINVAL;
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->state != MFCINST_GOT_INST) {
+ mfc_err("invalid context state: %d\n", ctx->state);
+ return -EINVAL;
+ }
+ ret = vb2_querybuf(&ctx->vq_dst, buf);
+ if (ret != 0) {
+ mfc_err("error in vb2_querybuf() for E(D)\n");
+ return ret;
+ }
+ buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE;
+ } else if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_querybuf(&ctx->vq_src, buf);
+ if (ret != 0) {
+ mfc_err("error in vb2_querybuf() for E(S)\n");
+ return ret;
+ }
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on QBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_qbuf(&ctx->vq_src, buf);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_qbuf(&ctx->vq_dst, buf);
+ return -EINVAL;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on DQBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+ return -EINVAL;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_streamon(&ctx->vq_src, type);
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_streamon(&ctx->vq_dst, type);
+ return -EINVAL;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_streamoff(&ctx->vq_src, type);
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_streamoff(&ctx->vq_dst, type);
+ return -EINVAL;
+}
+
+static inline int h264_level(enum v4l2_mpeg_video_h264_level lvl)
+{
+ static unsigned int t[V4L2_MPEG_VIDEO_H264_LEVEL_4_0 + 1] = {
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_0 */ 10,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1B */ 9,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_1 */ 11,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_2 */ 12,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_3 */ 13,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_2_0 */ 20,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_2_1 */ 21,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_2_2 */ 22,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_3_0 */ 30,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_3_1 */ 31,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_3_2 */ 32,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_4_0 */ 40,
+ };
+ return t[lvl];
+}
+
+static inline int mpeg4_level(enum v4l2_mpeg_video_mpeg4_level lvl)
+{
+ static unsigned int t[V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 + 1] = {
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 */ 0,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B */ 9,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_1 */ 1,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_2 */ 2,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3 */ 3,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B */ 7,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 */ 4,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 */ 5,
+ };
+ return t[lvl];
+}
+
+static inline int vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_idc sar)
+{
+ static unsigned int t[V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED + 1] = {
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED */ 0,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1 */ 1,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11 */ 2,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11 */ 3,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11 */ 4,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33 */ 5,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11 */ 6,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11 */ 7,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11 */ 8,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33 */ 9,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11 */ 10,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11 */ 11,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33 */ 12,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99 */ 13,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3 */ 14,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2 */ 15,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1 */ 16,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED */ 255,
+ };
+ return t[sar];
+}
+
+static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ p->gop_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ p->slice_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+ p->slice_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+ p->slice_bit = ctrl->val * 8;
+ break;
+ case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+ p->intra_refresh_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_PADDING:
+ p->pad = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV:
+ p->pad_luma = (ctrl->val >> 16) & 0xff;
+ p->pad_cb = (ctrl->val >> 8) & 0xff;
+ p->pad_cr = (ctrl->val >> 0) & 0xff;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ p->rc_frame = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ p->rc_bitrate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF:
+ p->rc_reaction_coeff = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+ ctx->force_frame_type = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
+ p->vbv_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
+ p->codec.h264.cpb_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ p->seq_hdr_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
+ p->frame_skip_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT:
+ p->fixed_target_bit = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ p->num_b_frame = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+ p->codec.h264.profile =
+ S5P_FIMV_ENC_PROFILE_H264_MAIN;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+ p->codec.h264.profile =
+ S5P_FIMV_ENC_PROFILE_H264_HIGH;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+ p->codec.h264.profile =
+ S5P_FIMV_ENC_PROFILE_H264_BASELINE;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ p->codec.h264.level_v4l2 = ctrl->val;
+ p->codec.h264.level = h264_level(ctrl->val);
+ if (p->codec.h264.level < 0) {
+ mfc_err("Level number is wrong\n");
+ ret = p->codec.h264.level;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ p->codec.mpeg4.level_v4l2 = ctrl->val;
+ p->codec.mpeg4.level = mpeg4_level(ctrl->val);
+ if (p->codec.mpeg4.level < 0) {
+ mfc_err("Level number is wrong\n");
+ ret = p->codec.mpeg4.level;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ p->codec.h264.loop_filter_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+ p->codec.h264.loop_filter_alpha = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+ p->codec.h264.loop_filter_beta = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ p->codec.h264.entropy_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P:
+ p->codec.h264.num_ref_pic_4p = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+ p->codec.h264._8x8_transform = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+ p->codec.h264.rc_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ p->codec.h264.rc_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+ p->codec.h264.rc_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+ p->codec.h264.rc_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+ p->codec.h264.rc_p_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+ p->codec.h264.rc_b_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:
+ p->codec.mpeg4.rc_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+ p->codec.mpeg4.rc_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+ p->codec.mpeg4.rc_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:
+ p->codec.mpeg4.rc_p_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:
+ p->codec.mpeg4.rc_b_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK:
+ p->codec.h264.rc_mb_dark = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH:
+ p->codec.h264.rc_mb_smooth = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC:
+ p->codec.h264.rc_mb_static = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY:
+ p->codec.h264.rc_mb_activity = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+ p->codec.h264.vui_sar = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ p->codec.h264.vui_sar_idc = vui_sar_idc(ctrl->val);
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:
+ p->codec.h264.vui_ext_sar_width = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:
+ p->codec.h264.vui_ext_sar_height = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ p->codec.h264.open_gop = !ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+ p->codec.h264.open_gop_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE:
+ p->codec.mpeg4.profile =
+ S5P_FIMV_ENC_PROFILE_MPEG4_SIMPLE;
+ break;
+ case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE:
+ p->codec.mpeg4.profile =
+ S5P_FIMV_ENC_PROFILE_MPEG4_ADVANCED_SIMPLE;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
+ p->codec.mpeg4.quarter_pixel = ctrl->val;
+ break;
+ default:
+ v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
+ ctrl->id, ctrl->val);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops s5p_mfc_enc_ctrl_ops = {
+ .s_ctrl = s5p_mfc_enc_s_ctrl,
+};
+
+int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ctx->enc_params.rc_framerate_num =
+ a->parm.output.timeperframe.denominator;
+ ctx->enc_params.rc_framerate_denom =
+ a->parm.output.timeperframe.numerator;
+ } else {
+ mfc_err("Setting FPS is only possible for the output queue\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *a)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ a->parm.output.timeperframe.denominator =
+ ctx->enc_params.rc_framerate_num;
+ a->parm.output.timeperframe.numerator =
+ ctx->enc_params.rc_framerate_denom;
+ } else {
+ mfc_err("Setting FPS is only possible for the output queue\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_g_parm = vidioc_g_parm,
+};
+
+static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
+{
+ int i;
+
+ if (!fmt)
+ return -EINVAL;
+ if (fmt->num_planes != vb->num_planes) {
+ mfc_err("invalid plane number for the format\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < fmt->num_planes; i++) {
+ if (!vb2_dma_contig_plane_dma_addr(vb, i)) {
+ mfc_err("failed to get plane cookie\n");
+ return -EINVAL;
+ }
+ mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx",
+ vb->v4l2_buf.index, i,
+ vb2_dma_contig_plane_dma_addr(vb, i));
+ }
+ return 0;
+}
+
+static int s5p_mfc_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *buf_count, unsigned int *plane_count,
+ unsigned int psize[], void *allocators[])
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+ if (ctx->state != MFCINST_GOT_INST) {
+ mfc_err("inavlid state: %d\n", ctx->state);
+ return -EINVAL;
+ }
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->dst_fmt)
+ *plane_count = ctx->dst_fmt->num_planes;
+ else
+ *plane_count = MFC_ENC_CAP_PLANE_COUNT;
+ if (*buf_count < 1)
+ *buf_count = 1;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ psize[0] = ctx->enc_dst_buf_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (ctx->src_fmt)
+ *plane_count = ctx->src_fmt->num_planes;
+ else
+ *plane_count = MFC_ENC_OUT_PLANE_COUNT;
+
+ if (*buf_count < 1)
+ *buf_count = 1;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ psize[0] = ctx->luma_size;
+ psize[1] = ctx->chroma_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+ allocators[1] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+ } else {
+ mfc_err("inavlid queue type: %d\n", vq->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void s5p_mfc_unlock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_unlock(&dev->mfc_mutex);
+}
+
+static void s5p_mfc_lock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_lock(&dev->mfc_mutex);
+}
+
+static int s5p_mfc_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ unsigned int i;
+ int ret;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = check_vb_with_fmt(ctx->dst_fmt, vb);
+ if (ret < 0)
+ return ret;
+ i = vb->v4l2_buf.index;
+ ctx->dst_bufs[i].b = vb;
+ ctx->dst_bufs[i].cookie.stream =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->dst_bufs_cnt++;
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = check_vb_with_fmt(ctx->src_fmt, vb);
+ if (ret < 0)
+ return ret;
+ i = vb->v4l2_buf.index;
+ ctx->src_bufs[i].b = vb;
+ ctx->src_bufs[i].cookie.raw.luma =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->src_bufs[i].cookie.raw.chroma =
+ vb2_dma_contig_plane_dma_addr(vb, 1);
+ ctx->src_bufs_cnt++;
+ } else {
+ mfc_err("inavlid queue type: %d\n", vq->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ int ret;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = check_vb_with_fmt(ctx->dst_fmt, vb);
+ if (ret < 0)
+ return ret;
+ mfc_debug(2, "plane size: %ld, dst size: %d\n",
+ vb2_plane_size(vb, 0), ctx->enc_dst_buf_size);
+ if (vb2_plane_size(vb, 0) < ctx->enc_dst_buf_size) {
+ mfc_err("plane size is too small for capture\n");
+ return -EINVAL;
+ }
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = check_vb_with_fmt(ctx->src_fmt, vb);
+ if (ret < 0)
+ return ret;
+ mfc_debug(2, "plane size: %ld, luma size: %d\n",
+ vb2_plane_size(vb, 0), ctx->luma_size);
+ mfc_debug(2, "plane size: %ld, chroma size: %d\n",
+ vb2_plane_size(vb, 1), ctx->chroma_size);
+ if (vb2_plane_size(vb, 0) < ctx->luma_size ||
+ vb2_plane_size(vb, 1) < ctx->chroma_size) {
+ mfc_err("plane size is too small for output\n");
+ return -EINVAL;
+ }
+ } else {
+ mfc_err("inavlid queue type: %d\n", vq->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+ /* If context is ready then dev = work->data;schedule it to run */
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+ return 0;
+}
+
+static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+{
+ unsigned long flags;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ if ((ctx->state == MFCINST_FINISHING ||
+ ctx->state == MFCINST_RUNNING) &&
+ dev->curr_ctx == ctx->num && dev->hw_lock) {
+ ctx->state = MFCINST_ABORT;
+ s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_FRAME_DONE_RET,
+ 0);
+ }
+ ctx->state = MFCINST_FINISHED;
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ INIT_LIST_HEAD(&ctx->dst_queue);
+ ctx->dst_queue_cnt = 0;
+ }
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ cleanup_ref_queue(ctx);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ INIT_LIST_HEAD(&ctx->src_queue);
+ ctx->src_queue_cnt = 0;
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return 0;
+}
+
+static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *mfc_buf;
+
+ if (ctx->state == MFCINST_ERROR) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ cleanup_ref_queue(ctx);
+ return;
+ }
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
+ mfc_buf->used = 0;
+ /* Mark destination as available for use by MFC */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ list_add_tail(&mfc_buf->list, &ctx->dst_queue);
+ ctx->dst_queue_cnt++;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
+ mfc_buf->used = 0;
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (vb->v4l2_planes[0].bytesused == 0) {
+ mfc_debug(1, "change state to FINISHING\n");
+ ctx->state = MFCINST_FINISHING;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ cleanup_ref_queue(ctx);
+ } else {
+ list_add_tail(&mfc_buf->list, &ctx->src_queue);
+ ctx->src_queue_cnt++;
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else {
+ mfc_err("unsupported buffer type (%d)\n", vq->type);
+ }
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+}
+
+static struct vb2_ops s5p_mfc_enc_qops = {
+ .queue_setup = s5p_mfc_queue_setup,
+ .wait_prepare = s5p_mfc_unlock,
+ .wait_finish = s5p_mfc_lock,
+ .buf_init = s5p_mfc_buf_init,
+ .buf_prepare = s5p_mfc_buf_prepare,
+ .start_streaming = s5p_mfc_start_streaming,
+ .stop_streaming = s5p_mfc_stop_streaming,
+ .buf_queue = s5p_mfc_buf_queue,
+};
+
+struct s5p_mfc_codec_ops *get_enc_codec_ops(void)
+{
+ return &encoder_codec_ops;
+}
+
+struct vb2_ops *get_enc_queue_ops(void)
+{
+ return &s5p_mfc_enc_qops;
+}
+
+const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void)
+{
+ return &s5p_mfc_enc_ioctl_ops;
+}
+
+#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
+ && V4L2_CTRL_DRIVER_PRIV(x))
+
+int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
+{
+ struct v4l2_ctrl_config cfg;
+ int i;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
+ if (ctx->ctrl_handler.error) {
+ mfc_err("v4l2_ctrl_handler_init failed\n");
+ return ctx->ctrl_handler.error;
+ }
+ for (i = 0; i < NUM_CTRLS; i++) {
+ if (IS_MFC51_PRIV(controls[i].id)) {
+ cfg.ops = &s5p_mfc_enc_ctrl_ops;
+ cfg.id = controls[i].id;
+ cfg.min = controls[i].minimum;
+ cfg.max = controls[i].maximum;
+ cfg.def = controls[i].default_value;
+ cfg.name = controls[i].name;
+ cfg.type = controls[i].type;
+ cfg.flags = 0;
+
+ if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+ cfg.step = 0;
+ cfg.menu_skip_mask = cfg.menu_skip_mask;
+ cfg.qmenu = mfc51_get_menu(cfg.id);
+ } else {
+ cfg.step = controls[i].step;
+ cfg.menu_skip_mask = 0;
+ }
+ ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+ &cfg, NULL);
+ } else {
+ if (controls[i].type == V4L2_CTRL_TYPE_MENU) {
+ ctx->ctrls[i] = v4l2_ctrl_new_std_menu(
+ &ctx->ctrl_handler,
+ &s5p_mfc_enc_ctrl_ops, controls[i].id,
+ controls[i].maximum, 0,
+ controls[i].default_value);
+ } else {
+ ctx->ctrls[i] = v4l2_ctrl_new_std(
+ &ctx->ctrl_handler,
+ &s5p_mfc_enc_ctrl_ops, controls[i].id,
+ controls[i].minimum,
+ controls[i].maximum, controls[i].step,
+ controls[i].default_value);
+ }
+ }
+ if (ctx->ctrl_handler.error) {
+ mfc_err("Adding control (%d) failed\n", i);
+ return ctx->ctrl_handler.error;
+ }
+ if (controls[i].is_volatile && ctx->ctrls[i])
+ ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+ return 0;
+}
+
+void s5p_mfc_enc_ctrls_delete(struct s5p_mfc_ctx *ctx)
+{
+ int i;
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ for (i = 0; i < NUM_CTRLS; i++)
+ ctx->ctrls[i] = NULL;
+}
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.h b/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
new file mode 100644
index 000000000000..405bdd3ee083
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
@@ -0,0 +1,23 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_ENC_H_
+#define S5P_MFC_ENC_H_
+
+struct s5p_mfc_codec_ops *get_enc_codec_ops(void);
+struct vb2_ops *get_enc_queue_ops(void);
+const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void);
+struct s5p_mfc_fmt *get_enc_def_fmt(bool src);
+int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_enc_ctrls_delete(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_ENC_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
new file mode 100644
index 000000000000..8f2f8bf4da7f
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
@@ -0,0 +1,92 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_intr.c
+ *
+ * C file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains functions used to wait for command completion.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
+{
+ int ret;
+
+ ret = wait_event_interruptible_timeout(dev->queue,
+ (dev->int_cond && (dev->int_type == command
+ || dev->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+ if (ret == 0) {
+ mfc_err("Interrupt (dev->int_type:%d, command:%d) timed out\n",
+ dev->int_type, command);
+ return 1;
+ } else if (ret == -ERESTARTSYS) {
+ mfc_err("Interrupted by a signal\n");
+ return 1;
+ }
+ mfc_debug(1, "Finished waiting (dev->int_type:%d, command: %d)\n",
+ dev->int_type, command);
+ if (dev->int_type == S5P_FIMV_R2H_CMD_ERR_RET)
+ return 1;
+ return 0;
+}
+
+void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
+{
+ dev->int_cond = 0;
+ dev->int_type = 0;
+ dev->int_err = 0;
+}
+
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
+ int command, int interrupt)
+{
+ int ret;
+
+ if (interrupt) {
+ ret = wait_event_interruptible_timeout(ctx->queue,
+ (ctx->int_cond && (ctx->int_type == command
+ || ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+ } else {
+ ret = wait_event_timeout(ctx->queue,
+ (ctx->int_cond && (ctx->int_type == command
+ || ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+ }
+ if (ret == 0) {
+ mfc_err("Interrupt (ctx->int_type:%d, command:%d) timed out\n",
+ ctx->int_type, command);
+ return 1;
+ } else if (ret == -ERESTARTSYS) {
+ mfc_err("Interrupted by a signal\n");
+ return 1;
+ }
+ mfc_debug(1, "Finished waiting (ctx->int_type:%d, command: %d)\n",
+ ctx->int_type, command);
+ if (ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)
+ return 1;
+ return 0;
+}
+
+void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
+{
+ ctx->int_cond = 0;
+ ctx->int_type = 0;
+ ctx->int_err = 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.h b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
new file mode 100644
index 000000000000..122d7732f745
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
@@ -0,0 +1,26 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_intr.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * It contains waiting functions declarations.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef S5P_MFC_INTR_H_
+#define S5P_MFC_INTR_H_
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
+ int command, int interrupt);
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command);
+void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev);
+
+#endif /* S5P_MFC_INTR_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
new file mode 100644
index 000000000000..e08b21c50ebf
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
@@ -0,0 +1,1397 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_opr.c
+ *
+ * Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains hw related functions.
+ *
+ * Kamil Debski, Copyright (c) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_ctrl.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+#include <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#define OFFSETA(x) (((x) - dev->bank1) >> MFC_OFFSET_SHIFT)
+#define OFFSETB(x) (((x) - dev->bank2) >> MFC_OFFSET_SHIFT)
+
+/* Allocate temporary buffers for decoding */
+int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx)
+{
+ void *desc_virt;
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ ctx->desc_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], DESC_BUF_SIZE);
+ if (IS_ERR_VALUE((int)ctx->desc_buf)) {
+ ctx->desc_buf = 0;
+ mfc_err("Allocating DESC buffer failed\n");
+ return -ENOMEM;
+ }
+ ctx->desc_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->desc_buf);
+ BUG_ON(ctx->desc_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ desc_virt = vb2_dma_contig_memops.vaddr(ctx->desc_buf);
+ if (desc_virt == NULL) {
+ vb2_dma_contig_memops.put(ctx->desc_buf);
+ ctx->desc_phys = 0;
+ ctx->desc_buf = 0;
+ mfc_err("Remapping DESC buffer failed\n");
+ return -ENOMEM;
+ }
+ memset(desc_virt, 0, DESC_BUF_SIZE);
+ wmb();
+ return 0;
+}
+
+/* Release temporary buffers for decoding */
+void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
+{
+ if (ctx->desc_phys) {
+ vb2_dma_contig_memops.put(ctx->desc_buf);
+ ctx->desc_phys = 0;
+ ctx->desc_buf = 0;
+ }
+}
+
+/* Allocate codec buffers */
+int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int enc_ref_y_size = 0;
+ unsigned int enc_ref_c_size = 0;
+ unsigned int guard_width, guard_height;
+
+ if (ctx->type == MFCINST_DECODER) {
+ mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n",
+ ctx->luma_size, ctx->chroma_size, ctx->mv_size);
+ mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count);
+ } else if (ctx->type == MFCINST_ENCODER) {
+ enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
+
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC) {
+ enc_ref_c_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN(ctx->img_height >> 1,
+ S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(enc_ref_c_size,
+ S5P_FIMV_NV12MT_SALIGN);
+ } else {
+ guard_width = ALIGN(ctx->img_width + 16,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN((ctx->img_height >> 1) + 4,
+ S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(guard_width * guard_height,
+ S5P_FIMV_NV12MT_SALIGN);
+ }
+ mfc_debug(2, "recon luma size: %d chroma size: %d\n",
+ enc_ref_y_size, enc_ref_c_size);
+ } else {
+ return -EINVAL;
+ }
+ /* Codecs have different memory requirements */
+ switch (ctx->codec_mode) {
+ case S5P_FIMV_CODEC_H264_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_NB_IP_SIZE +
+ S5P_FIMV_DEC_VERT_NB_MV_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = ctx->total_dpb_count * ctx->mv_size;
+ break;
+ case S5P_FIMV_CODEC_MPEG4_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE +
+ S5P_FIMV_DEC_UPNB_MV_SIZE +
+ S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+ S5P_FIMV_DEC_STX_PARSER_SIZE +
+ S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_VC1RCV_DEC:
+ case S5P_FIMV_CODEC_VC1_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
+ S5P_FIMV_DEC_UPNB_MV_SIZE +
+ S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+ S5P_FIMV_DEC_NB_DCAC_SIZE +
+ 3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_MPEG2_DEC:
+ ctx->bank1_size = 0;
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_H263_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
+ S5P_FIMV_DEC_UPNB_MV_SIZE +
+ S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+ S5P_FIMV_DEC_NB_DCAC_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_H264_ENC:
+ ctx->bank1_size = (enc_ref_y_size * 2) +
+ S5P_FIMV_ENC_UPMV_SIZE +
+ S5P_FIMV_ENC_COLFLG_SIZE +
+ S5P_FIMV_ENC_INTRAMD_SIZE +
+ S5P_FIMV_ENC_NBORINFO_SIZE;
+ ctx->bank2_size = (enc_ref_y_size * 2) +
+ (enc_ref_c_size * 4) +
+ S5P_FIMV_ENC_INTRAPRED_SIZE;
+ break;
+ case S5P_FIMV_CODEC_MPEG4_ENC:
+ ctx->bank1_size = (enc_ref_y_size * 2) +
+ S5P_FIMV_ENC_UPMV_SIZE +
+ S5P_FIMV_ENC_COLFLG_SIZE +
+ S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ ctx->bank2_size = (enc_ref_y_size * 2) +
+ (enc_ref_c_size * 4);
+ break;
+ case S5P_FIMV_CODEC_H263_ENC:
+ ctx->bank1_size = (enc_ref_y_size * 2) +
+ S5P_FIMV_ENC_UPMV_SIZE +
+ S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ ctx->bank2_size = (enc_ref_y_size * 2) +
+ (enc_ref_c_size * 4);
+ break;
+ default:
+ break;
+ }
+ /* Allocate only if memory from bank 1 is necessary */
+ if (ctx->bank1_size > 0) {
+ ctx->bank1_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size);
+ if (IS_ERR(ctx->bank1_buf)) {
+ ctx->bank1_buf = 0;
+ printk(KERN_ERR
+ "Buf alloc for decoding failed (port A)\n");
+ return -ENOMEM;
+ }
+ ctx->bank1_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf);
+ BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ }
+ /* Allocate only if memory from bank 2 is necessary */
+ if (ctx->bank2_size > 0) {
+ ctx->bank2_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size);
+ if (IS_ERR(ctx->bank2_buf)) {
+ ctx->bank2_buf = 0;
+ mfc_err("Buf alloc for decoding failed (port B)\n");
+ return -ENOMEM;
+ }
+ ctx->bank2_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_buf);
+ BUG_ON(ctx->bank2_phys & ((1 << MFC_BANK2_ALIGN_ORDER) - 1));
+ }
+ return 0;
+}
+
+/* Release buffers allocated for codec */
+void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+ if (ctx->bank1_buf) {
+ vb2_dma_contig_memops.put(ctx->bank1_buf);
+ ctx->bank1_buf = 0;
+ ctx->bank1_phys = 0;
+ ctx->bank1_size = 0;
+ }
+ if (ctx->bank2_buf) {
+ vb2_dma_contig_memops.put(ctx->bank2_buf);
+ ctx->bank2_buf = 0;
+ ctx->bank2_phys = 0;
+ ctx->bank2_size = 0;
+ }
+}
+
+/* Allocate memory for instance data buffer */
+int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx)
+{
+ void *context_virt;
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
+ ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
+ ctx->ctx_size = MFC_H264_CTX_BUF_SIZE;
+ else
+ ctx->ctx_size = MFC_CTX_BUF_SIZE;
+ ctx->ctx_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx_size);
+ if (IS_ERR(ctx->ctx_buf)) {
+ mfc_err("Allocating context buffer failed\n");
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = 0;
+ return -ENOMEM;
+ }
+ ctx->ctx_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx_buf);
+ BUG_ON(ctx->ctx_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ ctx->ctx_ofs = OFFSETA(ctx->ctx_phys);
+ context_virt = vb2_dma_contig_memops.vaddr(ctx->ctx_buf);
+ if (context_virt == NULL) {
+ mfc_err("Remapping instance buffer failed\n");
+ vb2_dma_contig_memops.put(ctx->ctx_buf);
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = 0;
+ return -ENOMEM;
+ }
+ /* Zero content of the allocated memory */
+ memset(context_virt, 0, ctx->ctx_size);
+ wmb();
+ if (s5p_mfc_init_shm(ctx) < 0) {
+ vb2_dma_contig_memops.put(ctx->ctx_buf);
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = 0;
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/* Release instance buffer */
+void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx)
+{
+ if (ctx->ctx_buf) {
+ vb2_dma_contig_memops.put(ctx->ctx_buf);
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = 0;
+ }
+ if (ctx->shm_alloc) {
+ vb2_dma_contig_memops.put(ctx->shm_alloc);
+ ctx->shm_alloc = 0;
+ ctx->shm = 0;
+ }
+}
+
+/* Set registers for decoding temporary buffers */
+void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETA(ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
+ mfc_write(dev, DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
+}
+
+/* Set registers for shared buffer */
+void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ mfc_write(dev, ctx->shm_ofs, S5P_FIMV_SI_CH0_HOST_WR_ADR);
+}
+
+/* Set registers for decoding stream buffer */
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, int buf_addr,
+ unsigned int start_num_byte, unsigned int buf_size)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETA(buf_addr), S5P_FIMV_SI_CH0_SB_ST_ADR);
+ mfc_write(dev, ctx->dec_src_buf_size, S5P_FIMV_SI_CH0_CPB_SIZE);
+ mfc_write(dev, buf_size, S5P_FIMV_SI_CH0_SB_FRM_SIZE);
+ s5p_mfc_write_shm(ctx, start_num_byte, START_BYTE_NUM);
+ return 0;
+}
+
+/* Set decoding frame buffer */
+int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *ctx)
+{
+ unsigned int frame_size, i;
+ unsigned int frame_size_ch, frame_size_mv;
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int dpb;
+ size_t buf_addr1, buf_addr2;
+ int buf_size1, buf_size2;
+
+ buf_addr1 = ctx->bank1_phys;
+ buf_size1 = ctx->bank1_size;
+ buf_addr2 = ctx->bank2_phys;
+ buf_size2 = ctx->bank2_size;
+ dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
+ ~S5P_FIMV_DPB_COUNT_MASK;
+ mfc_write(dev, ctx->total_dpb_count | dpb,
+ S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+ s5p_mfc_set_shared_buffer(ctx);
+ switch (ctx->codec_mode) {
+ case S5P_FIMV_CODEC_H264_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_VERT_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H264_NB_IP_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
+ break;
+ case S5P_FIMV_CODEC_MPEG4_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_NB_DCAC_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_UP_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_SA_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_SP_ADR);
+ buf_addr1 += S5P_FIMV_DEC_STX_PARSER_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_STX_PARSER_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_OT_LINE_ADR);
+ buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ break;
+ case S5P_FIMV_CODEC_H263_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_OT_LINE_ADR);
+ buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_UP_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_SA_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_NB_DCAC_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+ break;
+ case S5P_FIMV_CODEC_VC1_DEC:
+ case S5P_FIMV_CODEC_VC1RCV_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_NB_DCAC_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_OT_LINE_ADR);
+ buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_UP_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_SA_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE3_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE2_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE1_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ break;
+ case S5P_FIMV_CODEC_MPEG2_DEC:
+ break;
+ default:
+ mfc_err("Unknown codec for decoding (%x)\n",
+ ctx->codec_mode);
+ return -EINVAL;
+ break;
+ }
+ frame_size = ctx->luma_size;
+ frame_size_ch = ctx->chroma_size;
+ frame_size_mv = ctx->mv_size;
+ mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size, frame_size_ch,
+ frame_size_mv);
+ for (i = 0; i < ctx->total_dpb_count; i++) {
+ /* Bank2 */
+ mfc_debug(2, "Luma %d: %x\n", i,
+ ctx->dst_bufs[i].cookie.raw.luma);
+ mfc_write(dev, OFFSETB(ctx->dst_bufs[i].cookie.raw.luma),
+ S5P_FIMV_DEC_LUMA_ADR + i * 4);
+ mfc_debug(2, "\tChroma %d: %x\n", i,
+ ctx->dst_bufs[i].cookie.raw.chroma);
+ mfc_write(dev, OFFSETA(ctx->dst_bufs[i].cookie.raw.chroma),
+ S5P_FIMV_DEC_CHROMA_ADR + i * 4);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
+ mfc_debug(2, "\tBuf2: %x, size: %d\n",
+ buf_addr2, buf_size2);
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_H264_MV_ADR + i * 4);
+ buf_addr2 += frame_size_mv;
+ buf_size2 -= frame_size_mv;
+ }
+ }
+ mfc_debug(2, "Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
+ mfc_debug(2, "Buf 1/2 size after: %d/%d (frames %d)\n",
+ buf_size1, buf_size2, ctx->total_dpb_count);
+ if (buf_size1 < 0 || buf_size2 < 0) {
+ mfc_debug(2, "Not enough memory has been allocated\n");
+ return -ENOMEM;
+ }
+ s5p_mfc_write_shm(ctx, frame_size, ALLOC_LUMA_DPB_SIZE);
+ s5p_mfc_write_shm(ctx, frame_size_ch, ALLOC_CHROMA_DPB_SIZE);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC)
+ s5p_mfc_write_shm(ctx, frame_size_mv, ALLOC_MV_SIZE);
+ mfc_write(dev, ((S5P_FIMV_CH_INIT_BUFS & S5P_FIMV_CH_MASK)
+ << S5P_FIMV_CH_SHIFT) | (ctx->inst_no),
+ S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+/* Set registers for encoding stream buffer */
+int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long addr, unsigned int size)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETA(addr), S5P_FIMV_ENC_SI_CH0_SB_ADR);
+ mfc_write(dev, size, S5P_FIMV_ENC_SI_CH0_SB_SIZE);
+ return 0;
+}
+
+void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long y_addr, unsigned long c_addr)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETB(y_addr), S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR);
+ mfc_write(dev, OFFSETB(c_addr), S5P_FIMV_ENC_SI_CH0_CUR_C_ADR);
+}
+
+void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long *y_addr, unsigned long *c_addr)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ *y_addr = dev->bank2 + (mfc_read(dev, S5P_FIMV_ENCODED_Y_ADDR)
+ << MFC_OFFSET_SHIFT);
+ *c_addr = dev->bank2 + (mfc_read(dev, S5P_FIMV_ENCODED_C_ADDR)
+ << MFC_OFFSET_SHIFT);
+}
+
+/* Set encoding ref & codec buffer */
+int s5p_mfc_set_enc_ref_buffer(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ size_t buf_addr1, buf_addr2;
+ size_t buf_size1, buf_size2;
+ unsigned int enc_ref_y_size, enc_ref_c_size;
+ unsigned int guard_width, guard_height;
+ int i;
+
+ buf_addr1 = ctx->bank1_phys;
+ buf_size1 = ctx->bank1_size;
+ buf_addr2 = ctx->bank2_phys;
+ buf_size2 = ctx->bank2_size;
+ enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC) {
+ enc_ref_c_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN((ctx->img_height >> 1), S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(enc_ref_c_size, S5P_FIMV_NV12MT_SALIGN);
+ } else {
+ guard_width = ALIGN(ctx->img_width + 16,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN((ctx->img_height >> 1) + 4,
+ S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(guard_width * guard_height,
+ S5P_FIMV_NV12MT_SALIGN);
+ }
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", buf_size1, buf_size2);
+ switch (ctx->codec_mode) {
+ case S5P_FIMV_CODEC_H264_ENC:
+ for (i = 0; i < 2; i++) {
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+ buf_addr1 += enc_ref_y_size;
+ buf_size1 -= enc_ref_y_size;
+
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_y_size;
+ buf_size2 -= enc_ref_y_size;
+ }
+ for (i = 0; i < 4; i++) {
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_c_size;
+ buf_size2 -= enc_ref_c_size;
+ }
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H264_UP_MV_ADR);
+ buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_COZERO_FLAG_ADR);
+ buf_addr1 += S5P_FIMV_ENC_COLFLG_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_COLFLG_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_UP_INTRA_MD_ADR);
+ buf_addr1 += S5P_FIMV_ENC_INTRAMD_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_INTRAMD_SIZE;
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_H264_UP_INTRA_PRED_ADR);
+ buf_addr2 += S5P_FIMV_ENC_INTRAPRED_SIZE;
+ buf_size2 -= S5P_FIMV_ENC_INTRAPRED_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_NBOR_INFO_ADR);
+ buf_addr1 += S5P_FIMV_ENC_NBORINFO_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_NBORINFO_SIZE;
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ buf_size1, buf_size2);
+ break;
+ case S5P_FIMV_CODEC_MPEG4_ENC:
+ for (i = 0; i < 2; i++) {
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+ buf_addr1 += enc_ref_y_size;
+ buf_size1 -= enc_ref_y_size;
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_y_size;
+ buf_size2 -= enc_ref_y_size;
+ }
+ for (i = 0; i < 4; i++) {
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_c_size;
+ buf_size2 -= enc_ref_c_size;
+ }
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_UP_MV_ADR);
+ buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_MPEG4_COZERO_FLAG_ADR);
+ buf_addr1 += S5P_FIMV_ENC_COLFLG_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_COLFLG_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_MPEG4_ACDC_COEF_ADR);
+ buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ buf_size1, buf_size2);
+ break;
+ case S5P_FIMV_CODEC_H263_ENC:
+ for (i = 0; i < 2; i++) {
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+ buf_addr1 += enc_ref_y_size;
+ buf_size1 -= enc_ref_y_size;
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_y_size;
+ buf_size2 -= enc_ref_y_size;
+ }
+ for (i = 0; i < 4; i++) {
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_c_size;
+ buf_size2 -= enc_ref_c_size;
+ }
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_UP_MV_ADR);
+ buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_ACDC_COEF_ADR);
+ buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ buf_size1, buf_size2);
+ break;
+ default:
+ mfc_err("Unknown codec set for encoding: %d\n",
+ ctx->codec_mode);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ unsigned int reg;
+ unsigned int shm;
+
+ /* width */
+ mfc_write(dev, ctx->img_width, S5P_FIMV_ENC_HSIZE_PX);
+ /* height */
+ mfc_write(dev, ctx->img_height, S5P_FIMV_ENC_VSIZE_PX);
+ /* pictype : enable, IDR period */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ reg |= (1 << 18);
+ reg &= ~(0xFFFF);
+ reg |= p->gop_size;
+ mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ mfc_write(dev, 0, S5P_FIMV_ENC_B_RECON_WRITE_ON);
+ /* multi-slice control */
+ /* multi-slice MB number or bit size */
+ mfc_write(dev, p->slice_mode, S5P_FIMV_ENC_MSLICE_CTRL);
+ if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
+ mfc_write(dev, p->slice_mb, S5P_FIMV_ENC_MSLICE_MB);
+ } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
+ mfc_write(dev, p->slice_bit, S5P_FIMV_ENC_MSLICE_BIT);
+ } else {
+ mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_MB);
+ mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_BIT);
+ }
+ /* cyclic intra refresh */
+ mfc_write(dev, p->intra_refresh_mb, S5P_FIMV_ENC_CIR_CTRL);
+ /* memory structure cur. frame */
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M)
+ mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR);
+ else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT)
+ mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR);
+ /* padding control & value */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PADDING_CTRL);
+ if (p->pad) {
+ /** enable */
+ reg |= (1 << 31);
+ /** cr value */
+ reg &= ~(0xFF << 16);
+ reg |= (p->pad_cr << 16);
+ /** cb value */
+ reg &= ~(0xFF << 8);
+ reg |= (p->pad_cb << 8);
+ /** y value */
+ reg &= ~(0xFF);
+ reg |= (p->pad_luma);
+ } else {
+ /** disable & all value clear */
+ reg = 0;
+ }
+ mfc_write(dev, reg, S5P_FIMV_ENC_PADDING_CTRL);
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /** frame-level rate control */
+ reg &= ~(0x1 << 9);
+ reg |= (p->rc_frame << 9);
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* bit rate */
+ if (p->rc_frame)
+ mfc_write(dev, p->rc_bitrate,
+ S5P_FIMV_ENC_RC_BIT_RATE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_BIT_RATE);
+ /* reaction coefficient */
+ if (p->rc_frame)
+ mfc_write(dev, p->rc_reaction_coeff, S5P_FIMV_ENC_RC_RPARA);
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* seq header ctrl */
+ shm &= ~(0x1 << 3);
+ shm |= (p->seq_hdr_mode << 3);
+ /* frame skip mode */
+ shm &= ~(0x3 << 1);
+ shm |= (p->frame_skip_mode << 1);
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ /* fixed target bit */
+ s5p_mfc_write_shm(ctx, p->fixed_target_bit, RC_CONTROL_CONFIG);
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_h264_enc_params *p_264 = &p->codec.h264;
+ unsigned int reg;
+ unsigned int shm;
+
+ s5p_mfc_set_enc_params(ctx);
+ /* pictype : number of B */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* num_b_frame - 0 ~ 2 */
+ reg &= ~(0x3 << 16);
+ reg |= (p->num_b_frame << 16);
+ mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* profile & level */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PROFILE);
+ /* level */
+ reg &= ~(0xFF << 8);
+ reg |= (p_264->level << 8);
+ /* profile - 0 ~ 2 */
+ reg &= ~(0x3F);
+ reg |= p_264->profile;
+ mfc_write(dev, reg, S5P_FIMV_ENC_PROFILE);
+ /* interlace */
+ mfc_write(dev, p->interlace, S5P_FIMV_ENC_PIC_STRUCT);
+ /* height */
+ if (p->interlace)
+ mfc_write(dev, ctx->img_height >> 1, S5P_FIMV_ENC_VSIZE_PX);
+ /* loopfilter ctrl */
+ mfc_write(dev, p_264->loop_filter_mode, S5P_FIMV_ENC_LF_CTRL);
+ /* loopfilter alpha offset */
+ if (p_264->loop_filter_alpha < 0) {
+ reg = 0x10;
+ reg |= (0xFF - p_264->loop_filter_alpha) + 1;
+ } else {
+ reg = 0x00;
+ reg |= (p_264->loop_filter_alpha & 0xF);
+ }
+ mfc_write(dev, reg, S5P_FIMV_ENC_ALPHA_OFF);
+ /* loopfilter beta offset */
+ if (p_264->loop_filter_beta < 0) {
+ reg = 0x10;
+ reg |= (0xFF - p_264->loop_filter_beta) + 1;
+ } else {
+ reg = 0x00;
+ reg |= (p_264->loop_filter_beta & 0xF);
+ }
+ mfc_write(dev, reg, S5P_FIMV_ENC_BETA_OFF);
+ /* entropy coding mode */
+ if (p_264->entropy_mode == V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
+ mfc_write(dev, 1, S5P_FIMV_ENC_H264_ENTROPY_MODE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_H264_ENTROPY_MODE);
+ /* number of ref. picture */
+ reg = mfc_read(dev, S5P_FIMV_ENC_H264_NUM_OF_REF);
+ /* num of ref. pictures of P */
+ reg &= ~(0x3 << 5);
+ reg |= (p_264->num_ref_pic_4p << 5);
+ /* max number of ref. pictures */
+ reg &= ~(0x1F);
+ reg |= p_264->max_ref_pic;
+ mfc_write(dev, reg, S5P_FIMV_ENC_H264_NUM_OF_REF);
+ /* 8x8 transform enable */
+ mfc_write(dev, p_264->_8x8_transform, S5P_FIMV_ENC_H264_TRANS_FLAG);
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /* macroblock level rate control */
+ reg &= ~(0x1 << 8);
+ reg |= (p_264->rc_mb << 8);
+ /* frame QP */
+ reg &= ~(0x3F);
+ reg |= p_264->rc_frame_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* frame rate */
+ if (p->rc_frame && p->rc_framerate_denom)
+ mfc_write(dev, p->rc_framerate_num * 1000
+ / p->rc_framerate_denom, S5P_FIMV_ENC_RC_FRAME_RATE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+ /* max & min value of QP */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+ /* max QP */
+ reg &= ~(0x3F << 8);
+ reg |= (p_264->rc_max_qp << 8);
+ /* min QP */
+ reg &= ~(0x3F);
+ reg |= p_264->rc_min_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+ /* macroblock adaptive scaling features */
+ if (p_264->rc_mb) {
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_MB_CTRL);
+ /* dark region */
+ reg &= ~(0x1 << 3);
+ reg |= (p_264->rc_mb_dark << 3);
+ /* smooth region */
+ reg &= ~(0x1 << 2);
+ reg |= (p_264->rc_mb_smooth << 2);
+ /* static region */
+ reg &= ~(0x1 << 1);
+ reg |= (p_264->rc_mb_static << 1);
+ /* high activity region */
+ reg &= ~(0x1);
+ reg |= p_264->rc_mb_activity;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_MB_CTRL);
+ }
+ if (!p->rc_frame &&
+ !p_264->rc_mb) {
+ shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+ shm &= ~(0xFFF);
+ shm |= ((p_264->rc_b_frame_qp & 0x3F) << 6);
+ shm |= (p_264->rc_p_frame_qp & 0x3F);
+ s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+ }
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* AR VUI control */
+ shm &= ~(0x1 << 15);
+ shm |= (p_264->vui_sar << 1);
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ if (p_264->vui_sar) {
+ /* aspect ration IDC */
+ shm = s5p_mfc_read_shm(ctx, SAMPLE_ASPECT_RATIO_IDC);
+ shm &= ~(0xFF);
+ shm |= p_264->vui_sar_idc;
+ s5p_mfc_write_shm(ctx, shm, SAMPLE_ASPECT_RATIO_IDC);
+ if (p_264->vui_sar_idc == 0xFF) {
+ /* sample AR info */
+ shm = s5p_mfc_read_shm(ctx, EXTENDED_SAR);
+ shm &= ~(0xFFFFFFFF);
+ shm |= p_264->vui_ext_sar_width << 16;
+ shm |= p_264->vui_ext_sar_height;
+ s5p_mfc_write_shm(ctx, shm, EXTENDED_SAR);
+ }
+ }
+ /* intra picture period for H.264 */
+ shm = s5p_mfc_read_shm(ctx, H264_I_PERIOD);
+ /* control */
+ shm &= ~(0x1 << 16);
+ shm |= (p_264->open_gop << 16);
+ /* value */
+ if (p_264->open_gop) {
+ shm &= ~(0xFFFF);
+ shm |= p_264->open_gop_size;
+ }
+ s5p_mfc_write_shm(ctx, shm, H264_I_PERIOD);
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ shm &= ~(0xFFFF << 16);
+ shm |= (p_264->cpb_size << 16);
+ }
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4;
+ unsigned int reg;
+ unsigned int shm;
+ unsigned int framerate;
+
+ s5p_mfc_set_enc_params(ctx);
+ /* pictype : number of B */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* num_b_frame - 0 ~ 2 */
+ reg &= ~(0x3 << 16);
+ reg |= (p->num_b_frame << 16);
+ mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* profile & level */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PROFILE);
+ /* level */
+ reg &= ~(0xFF << 8);
+ reg |= (p_mpeg4->level << 8);
+ /* profile - 0 ~ 2 */
+ reg &= ~(0x3F);
+ reg |= p_mpeg4->profile;
+ mfc_write(dev, reg, S5P_FIMV_ENC_PROFILE);
+ /* quarter_pixel */
+ mfc_write(dev, p_mpeg4->quarter_pixel, S5P_FIMV_ENC_MPEG4_QUART_PXL);
+ /* qp */
+ if (!p->rc_frame) {
+ shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+ shm &= ~(0xFFF);
+ shm |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 6);
+ shm |= (p_mpeg4->rc_p_frame_qp & 0x3F);
+ s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+ }
+ /* frame rate */
+ if (p->rc_frame) {
+ if (p->rc_framerate_denom > 0) {
+ framerate = p->rc_framerate_num * 1000 /
+ p->rc_framerate_denom;
+ mfc_write(dev, framerate,
+ S5P_FIMV_ENC_RC_FRAME_RATE);
+ shm = s5p_mfc_read_shm(ctx, RC_VOP_TIMING);
+ shm &= ~(0xFFFFFFFF);
+ shm |= (1 << 31);
+ shm |= ((p->rc_framerate_num & 0x7FFF) << 16);
+ shm |= (p->rc_framerate_denom & 0xFFFF);
+ s5p_mfc_write_shm(ctx, shm, RC_VOP_TIMING);
+ }
+ } else {
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+ }
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /* frame QP */
+ reg &= ~(0x3F);
+ reg |= p_mpeg4->rc_frame_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* max & min value of QP */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+ /* max QP */
+ reg &= ~(0x3F << 8);
+ reg |= (p_mpeg4->rc_max_qp << 8);
+ /* min QP */
+ reg &= ~(0x3F);
+ reg |= p_mpeg4->rc_min_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ shm &= ~(0xFFFF << 16);
+ shm |= (p->vbv_size << 16);
+ }
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_mpeg4_enc_params *p_h263 = &p->codec.mpeg4;
+ unsigned int reg;
+ unsigned int shm;
+
+ s5p_mfc_set_enc_params(ctx);
+ /* qp */
+ if (!p->rc_frame) {
+ shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+ shm &= ~(0xFFF);
+ shm |= (p_h263->rc_p_frame_qp & 0x3F);
+ s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+ }
+ /* frame rate */
+ if (p->rc_frame && p->rc_framerate_denom)
+ mfc_write(dev, p->rc_framerate_num * 1000
+ / p->rc_framerate_denom, S5P_FIMV_ENC_RC_FRAME_RATE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /* frame QP */
+ reg &= ~(0x3F);
+ reg |= p_h263->rc_frame_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* max & min value of QP */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+ /* max QP */
+ reg &= ~(0x3F << 8);
+ reg |= (p_h263->rc_max_qp << 8);
+ /* min QP */
+ reg &= ~(0x3F);
+ reg |= p_h263->rc_min_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ shm &= ~(0xFFFF << 16);
+ shm |= (p->vbv_size << 16);
+ }
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ return 0;
+}
+
+/* Initialize decoding */
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ s5p_mfc_set_shared_buffer(ctx);
+ /* Setup loop filter, for decoding this is only valid for MPEG4 */
+ if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_DEC)
+ mfc_write(dev, ctx->loop_filter_mpeg4, S5P_FIMV_ENC_LF_CTRL);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_LF_CTRL);
+ mfc_write(dev, ((ctx->slice_interface & S5P_FIMV_SLICE_INT_MASK) <<
+ S5P_FIMV_SLICE_INT_SHIFT) | (ctx->display_delay_enable <<
+ S5P_FIMV_DDELAY_ENA_SHIFT) | ((ctx->display_delay &
+ S5P_FIMV_DDELAY_VAL_MASK) << S5P_FIMV_DDELAY_VAL_SHIFT),
+ S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+ mfc_write(dev,
+ ((S5P_FIMV_CH_SEQ_HEADER & S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT)
+ | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int dpb;
+
+ if (flush)
+ dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) | (
+ S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT);
+ else
+ dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
+ ~(S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT);
+ mfc_write(dev, dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+}
+
+/* Decode a single frame */
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx,
+ enum s5p_mfc_decode_arg last_frame)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, ctx->dec_dst_flag, S5P_FIMV_SI_CH0_RELEASE_BUF);
+ s5p_mfc_set_shared_buffer(ctx);
+ s5p_mfc_set_flush(ctx, ctx->dpb_flush_flag);
+ /* Issue different commands to instance basing on whether it
+ * is the last frame or not. */
+ switch (last_frame) {
+ case MFC_DEC_FRAME:
+ mfc_write(dev, ((S5P_FIMV_CH_FRAME_START & S5P_FIMV_CH_MASK) <<
+ S5P_FIMV_CH_SHIFT) | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ break;
+ case MFC_DEC_LAST_FRAME:
+ mfc_write(dev, ((S5P_FIMV_CH_LAST_FRAME & S5P_FIMV_CH_MASK) <<
+ S5P_FIMV_CH_SHIFT) | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ break;
+ case MFC_DEC_RES_CHANGE:
+ mfc_write(dev, ((S5P_FIMV_CH_FRAME_START_REALLOC &
+ S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT) | (ctx->inst_no),
+ S5P_FIMV_SI_CH0_INST_ID);
+ break;
+ }
+ mfc_debug(2, "Decoding a usual frame\n");
+ return 0;
+}
+
+int s5p_mfc_init_encode(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
+ s5p_mfc_set_enc_params_h264(ctx);
+ else if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_ENC)
+ s5p_mfc_set_enc_params_mpeg4(ctx);
+ else if (ctx->codec_mode == S5P_FIMV_CODEC_H263_ENC)
+ s5p_mfc_set_enc_params_h263(ctx);
+ else {
+ mfc_err("Unknown codec for encoding (%x)\n",
+ ctx->codec_mode);
+ return -EINVAL;
+ }
+ s5p_mfc_set_shared_buffer(ctx);
+ mfc_write(dev, ((S5P_FIMV_CH_SEQ_HEADER << 16) & 0x70000) |
+ (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+/* Encode a single frame */
+int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ /* memory structure cur. frame */
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M)
+ mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR);
+ else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT)
+ mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR);
+ s5p_mfc_set_shared_buffer(ctx);
+ mfc_write(dev, (S5P_FIMV_CH_FRAME_START << 16 & 0x70000) |
+ (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+static int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
+{
+ unsigned long flags;
+ int new_ctx;
+ int cnt;
+
+ spin_lock_irqsave(&dev->condlock, flags);
+ new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
+ cnt = 0;
+ while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
+ new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
+ if (++cnt > MFC_NUM_CONTEXTS) {
+ /* No contexts to run */
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ return -EAGAIN;
+ }
+ }
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ return new_ctx;
+}
+
+static void s5p_mfc_run_res_change(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_decode_one_frame(ctx, MFC_DEC_RES_CHANGE);
+}
+
+static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *temp_vb;
+ unsigned long flags;
+ unsigned int index;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ /* Frames are being decoded */
+ if (list_empty(&ctx->src_queue)) {
+ mfc_debug(2, "No src buffers\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EAGAIN;
+ }
+ /* Get the next source buffer */
+ temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ temp_vb->used = 1;
+ s5p_mfc_set_dec_stream_buffer(ctx,
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), ctx->consumed_stream,
+ temp_vb->b->v4l2_planes[0].bytesused);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ index = temp_vb->b->v4l2_buf.index;
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ if (temp_vb->b->v4l2_planes[0].bytesused == 0) {
+ last_frame = MFC_DEC_LAST_FRAME;
+ mfc_debug(2, "Setting ctx->state to FINISHING\n");
+ ctx->state = MFCINST_FINISHING;
+ }
+ s5p_mfc_decode_one_frame(ctx, last_frame);
+ return 0;
+}
+
+static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *dst_mb;
+ struct s5p_mfc_buf *src_mb;
+ unsigned long src_y_addr, src_c_addr, dst_addr;
+ unsigned int dst_size;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (list_empty(&ctx->src_queue)) {
+ mfc_debug(2, "no src buffers\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EAGAIN;
+ }
+ if (list_empty(&ctx->dst_queue)) {
+ mfc_debug(2, "no dst buffers\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EAGAIN;
+ }
+ src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ src_mb->used = 1;
+ src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
+ src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
+ s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_mb->used = 1;
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_encode_one_frame(ctx);
+ return 0;
+}
+
+static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *temp_vb;
+
+ /* Initializing decoding - parsing header */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ mfc_debug(2, "Preparing to init decoding\n");
+ temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ s5p_mfc_set_dec_desc_buffer(ctx);
+ mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
+ s5p_mfc_set_dec_stream_buffer(ctx,
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
+ 0, temp_vb->b->v4l2_planes[0].bytesused);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_init_decode(ctx);
+}
+
+static void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *dst_mb;
+ unsigned long dst_addr;
+ unsigned int dst_size;
+
+ s5p_mfc_set_enc_ref_buffer(ctx);
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_init_encode(ctx);
+}
+
+static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *temp_vb;
+ int ret;
+
+ /*
+ * Header was parsed now starting processing
+ * First set the output frame buffers
+ */
+ if (ctx->capture_state != QUEUE_BUFS_MMAPED) {
+ mfc_err("It seems that not all destionation buffers were "
+ "mmaped\nMFC requires that all destination are mmaped "
+ "before starting processing\n");
+ return -EAGAIN;
+ }
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (list_empty(&ctx->src_queue)) {
+ mfc_err("Header has been deallocated in the middle of"
+ " initialization\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EIO;
+ }
+ temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
+ s5p_mfc_set_dec_stream_buffer(ctx,
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
+ 0, temp_vb->b->v4l2_planes[0].bytesused);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_set_dec_frame_buffer(ctx);
+ if (ret) {
+ mfc_err("Failed to alloc frame mem\n");
+ ctx->state = MFCINST_ERROR;
+ }
+ return ret;
+}
+
+/* Try running an operation on hardware */
+void s5p_mfc_try_run(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_ctx *ctx;
+ int new_ctx;
+ unsigned int ret = 0;
+
+ if (test_bit(0, &dev->enter_suspend)) {
+ mfc_debug(1, "Entering suspend so do not schedule any jobs\n");
+ return;
+ }
+ /* Check whether hardware is not running */
+ if (test_and_set_bit(0, &dev->hw_lock) != 0) {
+ /* This is perfectly ok, the scheduled ctx should wait */
+ mfc_debug(1, "Couldn't lock HW\n");
+ return;
+ }
+ /* Choose the context to run */
+ new_ctx = s5p_mfc_get_new_ctx(dev);
+ if (new_ctx < 0) {
+ /* No contexts to run */
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
+ mfc_err("Failed to unlock hardware\n");
+ return;
+ }
+ mfc_debug(1, "No ctx is scheduled to be run\n");
+ return;
+ }
+ ctx = dev->ctx[new_ctx];
+ /* Got context to run in ctx */
+ /*
+ * Last frame has already been sent to MFC.
+ * Now obtaining frames from MFC buffer
+ */
+ s5p_mfc_clock_on();
+ if (ctx->type == MFCINST_DECODER) {
+ s5p_mfc_set_dec_desc_buffer(ctx);
+ switch (ctx->state) {
+ case MFCINST_FINISHING:
+ s5p_mfc_run_dec_frame(ctx, MFC_DEC_LAST_FRAME);
+ break;
+ case MFCINST_RUNNING:
+ ret = s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
+ break;
+ case MFCINST_INIT:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_open_inst_cmd(ctx);
+ break;
+ case MFCINST_RETURN_INST:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_close_inst_cmd(ctx);
+ break;
+ case MFCINST_GOT_INST:
+ s5p_mfc_run_init_dec(ctx);
+ break;
+ case MFCINST_HEAD_PARSED:
+ ret = s5p_mfc_run_init_dec_buffers(ctx);
+ mfc_debug(1, "head parsed\n");
+ break;
+ case MFCINST_RES_CHANGE_INIT:
+ s5p_mfc_run_res_change(ctx);
+ break;
+ case MFCINST_RES_CHANGE_FLUSH:
+ s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
+ break;
+ case MFCINST_RES_CHANGE_END:
+ mfc_debug(2, "Finished remaining frames after resolution change\n");
+ ctx->capture_state = QUEUE_FREE;
+ mfc_debug(2, "Will re-init the codec\n");
+ s5p_mfc_run_init_dec(ctx);
+ break;
+ default:
+ ret = -EAGAIN;
+ }
+ } else if (ctx->type == MFCINST_ENCODER) {
+ switch (ctx->state) {
+ case MFCINST_FINISHING:
+ case MFCINST_RUNNING:
+ ret = s5p_mfc_run_enc_frame(ctx);
+ break;
+ case MFCINST_INIT:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_open_inst_cmd(ctx);
+ break;
+ case MFCINST_RETURN_INST:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_close_inst_cmd(ctx);
+ break;
+ case MFCINST_GOT_INST:
+ s5p_mfc_run_init_enc(ctx);
+ break;
+ default:
+ ret = -EAGAIN;
+ }
+ } else {
+ mfc_err("Invalid context type: %d\n", ctx->type);
+ ret = -EAGAIN;
+ }
+
+ if (ret) {
+ /* Free hardware lock */
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ mfc_err("Failed to unlock hardware\n");
+
+ /* This is in deed imporant, as no operation has been
+ * scheduled, reduce the clock count as no one will
+ * ever do this, because no interrupt related to this try_run
+ * will ever come from hardware. */
+ s5p_mfc_clock_off();
+ }
+}
+
+
+void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq)
+{
+ struct s5p_mfc_buf *b;
+ int i;
+
+ while (!list_empty(lh)) {
+ b = list_entry(lh->next, struct s5p_mfc_buf, list);
+ for (i = 0; i < b->b->num_planes; i++)
+ vb2_set_plane_payload(b->b, i, 0);
+ vb2_buffer_done(b->b, VB2_BUF_STATE_ERROR);
+ list_del(&b->list);
+ }
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.h b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
new file mode 100644
index 000000000000..db83836e6a9f
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
@@ -0,0 +1,91 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_opr.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * Contains declarations of hw related functions.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef S5P_MFC_OPR_H_
+#define S5P_MFC_OPR_H_
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_init_encode(struct s5p_mfc_ctx *mfc_ctx);
+
+/* Decoding functions */
+int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, int buf_addr,
+ unsigned int start_num_byte,
+ unsigned int buf_size);
+
+/* Encoding functions */
+void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long y_addr, unsigned long c_addr);
+int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long addr, unsigned int size);
+void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long *y_addr, unsigned long *c_addr);
+int s5p_mfc_set_enc_ref_buffer(struct s5p_mfc_ctx *mfc_ctx);
+
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx,
+ enum s5p_mfc_decode_arg last_frame);
+int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *mfc_ctx);
+
+/* Memory allocation */
+int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx);
+
+void s5p_mfc_try_run(struct s5p_mfc_dev *dev);
+void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq);
+
+#define s5p_mfc_get_dspl_y_adr() (readl(dev->regs_base + \
+ S5P_FIMV_SI_DISPLAY_Y_ADR) << \
+ MFC_OFFSET_SHIFT)
+#define s5p_mfc_get_dec_y_adr() (readl(dev->regs_base + \
+ S5P_FIMV_SI_DISPLAY_Y_ADR) << \
+ MFC_OFFSET_SHIFT)
+#define s5p_mfc_get_dspl_status() readl(dev->regs_base + \
+ S5P_FIMV_SI_DISPLAY_STATUS)
+#define s5p_mfc_get_frame_type() (readl(dev->regs_base + \
+ S5P_FIMV_DECODE_FRAME_TYPE) \
+ & S5P_FIMV_DECODE_FRAME_MASK)
+#define s5p_mfc_get_consumed_stream() readl(dev->regs_base + \
+ S5P_FIMV_SI_CONSUMED_BYTES)
+#define s5p_mfc_get_int_reason() (readl(dev->regs_base + \
+ S5P_FIMV_RISC2HOST_CMD) & \
+ S5P_FIMV_RISC2HOST_CMD_MASK)
+#define s5p_mfc_get_int_err() readl(dev->regs_base + \
+ S5P_FIMV_RISC2HOST_ARG2)
+#define s5p_mfc_err_dec(x) (((x) & S5P_FIMV_ERR_DEC_MASK) >> \
+ S5P_FIMV_ERR_DEC_SHIFT)
+#define s5p_mfc_err_dspl(x) (((x) & S5P_FIMV_ERR_DSPL_MASK) >> \
+ S5P_FIMV_ERR_DSPL_SHIFT)
+#define s5p_mfc_get_img_width() readl(dev->regs_base + \
+ S5P_FIMV_SI_HRESOL)
+#define s5p_mfc_get_img_height() readl(dev->regs_base + \
+ S5P_FIMV_SI_VRESOL)
+#define s5p_mfc_get_dpb_count() readl(dev->regs_base + \
+ S5P_FIMV_SI_BUF_NUMBER)
+#define s5p_mfc_get_inst_no() readl(dev->regs_base + \
+ S5P_FIMV_RISC2HOST_ARG1)
+#define s5p_mfc_get_enc_strm_size() readl(dev->regs_base + \
+ S5P_FIMV_ENC_SI_STRM_SIZE)
+#define s5p_mfc_get_enc_slice_type() readl(dev->regs_base + \
+ S5P_FIMV_ENC_SI_SLICE_TYPE)
+
+#endif /* S5P_MFC_OPR_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
new file mode 100644
index 000000000000..f6a3035c4fb7
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
@@ -0,0 +1,117 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#ifdef CONFIG_PM_RUNTIME
+#include <linux/pm_runtime.h>
+#endif
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_pm.h"
+
+#define MFC_CLKNAME "sclk_mfc"
+#define MFC_GATE_CLK_NAME "mfc"
+
+#define CLK_DEBUG
+
+static struct s5p_mfc_pm *pm;
+static struct s5p_mfc_dev *p_dev;
+
+#ifdef CLK_DEBUG
+atomic_t clk_ref;
+#endif
+
+int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
+{
+ int ret = 0;
+
+ pm = &dev->pm;
+ p_dev = dev;
+ pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME);
+ if (IS_ERR(pm->clock_gate)) {
+ mfc_err("Failed to get clock-gating control\n");
+ ret = -ENOENT;
+ goto err_g_ip_clk;
+ }
+ pm->clock = clk_get(&dev->plat_dev->dev, MFC_CLKNAME);
+ if (IS_ERR(pm->clock)) {
+ mfc_err("Failed to get MFC clock\n");
+ ret = -ENOENT;
+ goto err_g_ip_clk_2;
+ }
+ atomic_set(&pm->power, 0);
+#ifdef CONFIG_PM_RUNTIME
+ pm->device = &dev->plat_dev->dev;
+ pm_runtime_enable(pm->device);
+#endif
+#ifdef CLK_DEBUG
+ atomic_set(&clk_ref, 0);
+#endif
+ return 0;
+err_g_ip_clk_2:
+ clk_put(pm->clock_gate);
+err_g_ip_clk:
+ return ret;
+}
+
+void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
+{
+ clk_put(pm->clock_gate);
+ clk_put(pm->clock);
+#ifdef CONFIG_PM_RUNTIME
+ pm_runtime_disable(pm->device);
+#endif
+}
+
+int s5p_mfc_clock_on(void)
+{
+ int ret;
+#ifdef CLK_DEBUG
+ atomic_inc(&clk_ref);
+ mfc_debug(3, "+ %d", atomic_read(&clk_ref));
+#endif
+ ret = clk_enable(pm->clock_gate);
+ return ret;
+}
+
+void s5p_mfc_clock_off(void)
+{
+#ifdef CLK_DEBUG
+ atomic_dec(&clk_ref);
+ mfc_debug(3, "- %d", atomic_read(&clk_ref));
+#endif
+ clk_disable(pm->clock_gate);
+}
+
+int s5p_mfc_power_on(void)
+{
+#ifdef CONFIG_PM_RUNTIME
+ return pm_runtime_get_sync(pm->device);
+#else
+ atomic_set(&pm->power, 1);
+ return 0;
+#endif
+}
+
+int s5p_mfc_power_off(void)
+{
+#ifdef CONFIG_PM_RUNTIME
+ return pm_runtime_put_sync(pm->device);
+#else
+ atomic_set(&pm->power, 0);
+ return 0;
+#endif
+}
+
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.h b/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
new file mode 100644
index 000000000000..5107914f27e4
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
@@ -0,0 +1,24 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_PM_H_
+#define S5P_MFC_PM_H_
+
+int s5p_mfc_init_pm(struct s5p_mfc_dev *dev);
+void s5p_mfc_final_pm(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_clock_on(void);
+void s5p_mfc_clock_off(void);
+int s5p_mfc_power_on(void);
+int s5p_mfc_power_off(void);
+
+#endif /* S5P_MFC_PM_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_shm.c b/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
new file mode 100644
index 000000000000..91fdbac8c37a
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
@@ -0,0 +1,47 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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.
+ */
+
+#ifdef CONFIG_ARCH_EXYNOS4
+#include <linux/dma-mapping.h>
+#endif
+#include <linux/io.h>
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+
+int s5p_mfc_init_shm(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ void *shm_alloc_ctx = dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+
+ ctx->shm_alloc = vb2_dma_contig_memops.alloc(shm_alloc_ctx,
+ SHARED_BUF_SIZE);
+ if (IS_ERR(ctx->shm_alloc)) {
+ mfc_err("failed to allocate shared memory\n");
+ return PTR_ERR(ctx->shm_alloc);
+ }
+ /* shm_ofs only keeps the offset from base (port a) */
+ ctx->shm_ofs = s5p_mfc_mem_cookie(shm_alloc_ctx, ctx->shm_alloc)
+ - dev->bank1;
+ BUG_ON(ctx->shm_ofs & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ ctx->shm = vb2_dma_contig_memops.vaddr(ctx->shm_alloc);
+ if (!ctx->shm) {
+ vb2_dma_contig_memops.put(ctx->shm_alloc);
+ ctx->shm_ofs = 0;
+ ctx->shm_alloc = NULL;
+ mfc_err("failed to virt addr of shared memory\n");
+ return -ENOMEM;
+ }
+ memset((void *)ctx->shm, 0, SHARED_BUF_SIZE);
+ wmb();
+ return 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_shm.h b/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
new file mode 100644
index 000000000000..764eac6bcc4c
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
@@ -0,0 +1,91 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_SHM_H_
+#define S5P_MFC_SHM_H_
+
+enum MFC_SHM_OFS
+{
+ EXTENEDED_DECODE_STATUS = 0x00, /* D */
+ SET_FRAME_TAG = 0x04, /* D */
+ GET_FRAME_TAG_TOP = 0x08, /* D */
+ GET_FRAME_TAG_BOT = 0x0C, /* D */
+ PIC_TIME_TOP = 0x10, /* D */
+ PIC_TIME_BOT = 0x14, /* D */
+ START_BYTE_NUM = 0x18, /* D */
+
+ CROP_INFO_H = 0x20, /* D */
+ CROP_INFO_V = 0x24, /* D */
+ EXT_ENC_CONTROL = 0x28, /* E */
+ ENC_PARAM_CHANGE = 0x2C, /* E */
+ RC_VOP_TIMING = 0x30, /* E, MPEG4 */
+ HEC_PERIOD = 0x34, /* E, MPEG4 */
+ METADATA_ENABLE = 0x38, /* C */
+ METADATA_STATUS = 0x3C, /* C */
+ METADATA_DISPLAY_INDEX = 0x40, /* C */
+ EXT_METADATA_START_ADDR = 0x44, /* C */
+ PUT_EXTRADATA = 0x48, /* C */
+ EXTRADATA_ADDR = 0x4C, /* C */
+
+ ALLOC_LUMA_DPB_SIZE = 0x64, /* D */
+ ALLOC_CHROMA_DPB_SIZE = 0x68, /* D */
+ ALLOC_MV_SIZE = 0x6C, /* D */
+ P_B_FRAME_QP = 0x70, /* E */
+ SAMPLE_ASPECT_RATIO_IDC = 0x74, /* E, H.264, depend on
+ ASPECT_RATIO_VUI_ENABLE in EXT_ENC_CONTROL */
+ EXTENDED_SAR = 0x78, /* E, H.264, depned on
+ ASPECT_RATIO_VUI_ENABLE in EXT_ENC_CONTROL */
+ DISP_PIC_PROFILE = 0x7C, /* D */
+ FLUSH_CMD_TYPE = 0x80, /* C */
+ FLUSH_CMD_INBUF1 = 0x84, /* C */
+ FLUSH_CMD_INBUF2 = 0x88, /* C */
+ FLUSH_CMD_OUTBUF = 0x8C, /* E */
+ NEW_RC_BIT_RATE = 0x90, /* E, format as RC_BIT_RATE(0xC5A8)
+ depend on RC_BIT_RATE_CHANGE in ENC_PARAM_CHANGE */
+ NEW_RC_FRAME_RATE = 0x94, /* E, format as RC_FRAME_RATE(0xD0D0)
+ depend on RC_FRAME_RATE_CHANGE in ENC_PARAM_CHANGE */
+ NEW_I_PERIOD = 0x98, /* E, format as I_FRM_CTRL(0xC504)
+ depend on I_PERIOD_CHANGE in ENC_PARAM_CHANGE */
+ H264_I_PERIOD = 0x9C, /* E, H.264, open GOP */
+ RC_CONTROL_CONFIG = 0xA0, /* E */
+ BATCH_INPUT_ADDR = 0xA4, /* E */
+ BATCH_OUTPUT_ADDR = 0xA8, /* E */
+ BATCH_OUTPUT_SIZE = 0xAC, /* E */
+ MIN_LUMA_DPB_SIZE = 0xB0, /* D */
+ DEVICE_FORMAT_ID = 0xB4, /* C */
+ H264_POC_TYPE = 0xB8, /* D */
+ MIN_CHROMA_DPB_SIZE = 0xBC, /* D */
+ DISP_PIC_FRAME_TYPE = 0xC0, /* D */
+ FREE_LUMA_DPB = 0xC4, /* D, VC1 MPEG4 */
+ ASPECT_RATIO_INFO = 0xC8, /* D, MPEG4 */
+ EXTENDED_PAR = 0xCC, /* D, MPEG4 */
+ DBG_HISTORY_INPUT0 = 0xD0, /* C */
+ DBG_HISTORY_INPUT1 = 0xD4, /* C */
+ DBG_HISTORY_OUTPUT = 0xD8, /* C */
+ HIERARCHICAL_P_QP = 0xE0, /* E, H.264 */
+};
+
+int s5p_mfc_init_shm(struct s5p_mfc_ctx *ctx);
+
+#define s5p_mfc_write_shm(ctx, x, ofs) \
+ do { \
+ writel(x, (ctx->shm + ofs)); \
+ wmb(); \
+ } while (0)
+
+static inline u32 s5p_mfc_read_shm(struct s5p_mfc_ctx *ctx, unsigned int ofs)
+{
+ rmb();
+ return readl(ctx->shm + ofs);
+}
+
+#endif /* S5P_MFC_SHM_H_ */
diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig
new file mode 100644
index 000000000000..f2a09779ec8f
--- /dev/null
+++ b/drivers/media/video/s5p-tv/Kconfig
@@ -0,0 +1,76 @@
+# drivers/media/video/s5p-tv/Kconfig
+#
+# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+# http://www.samsung.com/
+# Tomasz Stanislawski <t.stanislaws@samsung.com>
+#
+# Licensed under GPL
+
+config VIDEO_SAMSUNG_S5P_TV
+ bool "Samsung TV driver for S5P platform (experimental)"
+ depends on PLAT_S5P && PM_RUNTIME
+ depends on EXPERIMENTAL
+ default n
+ ---help---
+ Say Y here to enable selecting the TV output devices for
+ Samsung S5P platform.
+
+if VIDEO_SAMSUNG_S5P_TV
+
+config VIDEO_SAMSUNG_S5P_HDMI
+ tristate "Samsung HDMI Driver"
+ depends on VIDEO_V4L2
+ depends on VIDEO_SAMSUNG_S5P_TV
+ select VIDEO_SAMSUNG_S5P_HDMIPHY
+ help
+ Say Y here if you want support for the HDMI output
+ interface in S5P Samsung SoC. The driver can be compiled
+ as module. It is an auxiliary driver, that exposes a V4L2
+ subdev for use by other drivers. This driver requires
+ hdmiphy driver to work correctly.
+
+config VIDEO_SAMSUNG_S5P_HDMI_DEBUG
+ bool "Enable debug for HDMI Driver"
+ depends on VIDEO_SAMSUNG_S5P_HDMI
+ default n
+ help
+ Enables debugging for HDMI driver.
+
+config VIDEO_SAMSUNG_S5P_HDMIPHY
+ tristate "Samsung HDMIPHY Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && I2C
+ depends on VIDEO_SAMSUNG_S5P_TV
+ help
+ Say Y here if you want support for the physical HDMI
+ interface in S5P Samsung SoC. The driver can be compiled
+ as module. It is an I2C driver, that exposes a V4L2
+ subdev for use by other drivers.
+
+config VIDEO_SAMSUNG_S5P_SDO
+ tristate "Samsung Analog TV Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on VIDEO_SAMSUNG_S5P_TV
+ help
+ Say Y here if you want support for the analog TV output
+ interface in S5P Samsung SoC. The driver can be compiled
+ as module. It is an auxiliary driver, that exposes a V4L2
+ subdev for use by other drivers. This driver requires
+ hdmiphy driver to work correctly.
+
+config VIDEO_SAMSUNG_S5P_MIXER
+ tristate "Samsung Mixer and Video Processor Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on VIDEO_SAMSUNG_S5P_TV
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Say Y here if you want support for the Mixer in Samsung S5P SoCs.
+ This device produce image data to one of output interfaces.
+
+config VIDEO_SAMSUNG_S5P_MIXER_DEBUG
+ bool "Enable debug for Mixer Driver"
+ depends on VIDEO_SAMSUNG_S5P_MIXER
+ default n
+ help
+ Enables debugging for Mixer driver.
+
+endif # VIDEO_SAMSUNG_S5P_TV
diff --git a/drivers/media/video/s5p-tv/Makefile b/drivers/media/video/s5p-tv/Makefile
new file mode 100644
index 000000000000..37e4c17663b4
--- /dev/null
+++ b/drivers/media/video/s5p-tv/Makefile
@@ -0,0 +1,17 @@
+# drivers/media/video/samsung/tvout/Makefile
+#
+# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+# http://www.samsung.com/
+# Tomasz Stanislawski <t.stanislaws@samsung.com>
+#
+# Licensed under GPL
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o
+s5p-hdmiphy-y += hdmiphy_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o
+s5p-hdmi-y += hdmi_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o
+s5p-sdo-y += sdo_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MIXER) += s5p-mixer.o
+s5p-mixer-y += mixer_drv.o mixer_video.o mixer_reg.o mixer_grp_layer.o mixer_vp_layer.o
+
diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c
new file mode 100644
index 000000000000..0279e6e89feb
--- /dev/null
+++ b/drivers/media/video/s5p-tv/hdmi_drv.c
@@ -0,0 +1,1043 @@
+/*
+ * Samsung HDMI interface driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#ifdef CONFIG_VIDEO_SAMSUNG_S5P_HDMI_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/bug.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+
+#include "regs-hdmi.h"
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung HDMI");
+MODULE_LICENSE("GPL");
+
+/* default preset configured on probe */
+#define HDMI_DEFAULT_PRESET V4L2_DV_1080P60
+
+struct hdmi_resources {
+ struct clk *hdmi;
+ struct clk *sclk_hdmi;
+ struct clk *sclk_pixel;
+ struct clk *sclk_hdmiphy;
+ struct clk *hdmiphy;
+ struct regulator_bulk_data *regul_bulk;
+ int regul_count;
+};
+
+struct hdmi_device {
+ /** base address of HDMI registers */
+ void __iomem *regs;
+ /** HDMI interrupt */
+ unsigned int irq;
+ /** pointer to device parent */
+ struct device *dev;
+ /** subdev generated by HDMI device */
+ struct v4l2_subdev sd;
+ /** V4L2 device structure */
+ struct v4l2_device v4l2_dev;
+ /** subdev of HDMIPHY interface */
+ struct v4l2_subdev *phy_sd;
+ /** configuration of current graphic mode */
+ const struct hdmi_preset_conf *cur_conf;
+ /** current preset */
+ u32 cur_preset;
+ /** other resources */
+ struct hdmi_resources res;
+};
+
+struct hdmi_driver_data {
+ int hdmiphy_bus;
+};
+
+struct hdmi_tg_regs {
+ u8 cmd;
+ u8 h_fsz_l;
+ u8 h_fsz_h;
+ u8 hact_st_l;
+ u8 hact_st_h;
+ u8 hact_sz_l;
+ u8 hact_sz_h;
+ u8 v_fsz_l;
+ u8 v_fsz_h;
+ u8 vsync_l;
+ u8 vsync_h;
+ u8 vsync2_l;
+ u8 vsync2_h;
+ u8 vact_st_l;
+ u8 vact_st_h;
+ u8 vact_sz_l;
+ u8 vact_sz_h;
+ u8 field_chg_l;
+ u8 field_chg_h;
+ u8 vact_st2_l;
+ u8 vact_st2_h;
+ u8 vsync_top_hdmi_l;
+ u8 vsync_top_hdmi_h;
+ u8 vsync_bot_hdmi_l;
+ u8 vsync_bot_hdmi_h;
+ u8 field_top_hdmi_l;
+ u8 field_top_hdmi_h;
+ u8 field_bot_hdmi_l;
+ u8 field_bot_hdmi_h;
+};
+
+struct hdmi_core_regs {
+ u8 h_blank[2];
+ u8 v_blank[3];
+ u8 h_v_line[3];
+ u8 vsync_pol[1];
+ u8 int_pro_mode[1];
+ u8 v_blank_f[3];
+ u8 h_sync_gen[3];
+ u8 v_sync_gen1[3];
+ u8 v_sync_gen2[3];
+ u8 v_sync_gen3[3];
+};
+
+struct hdmi_preset_conf {
+ struct hdmi_core_regs core;
+ struct hdmi_tg_regs tg;
+ struct v4l2_mbus_framefmt mbus_fmt;
+};
+
+/* I2C module and id for HDMIPHY */
+static struct i2c_board_info hdmiphy_info = {
+ I2C_BOARD_INFO("hdmiphy", 0x38),
+};
+
+static struct hdmi_driver_data hdmi_driver_data[] = {
+ { .hdmiphy_bus = 3 },
+ { .hdmiphy_bus = 8 },
+};
+
+static struct platform_device_id hdmi_driver_types[] = {
+ {
+ .name = "s5pv210-hdmi",
+ .driver_data = (unsigned long)&hdmi_driver_data[0],
+ }, {
+ .name = "exynos4-hdmi",
+ .driver_data = (unsigned long)&hdmi_driver_data[1],
+ }, {
+ /* end node */
+ }
+};
+
+static const struct v4l2_subdev_ops hdmi_sd_ops;
+
+static struct hdmi_device *sd_to_hdmi_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct hdmi_device, sd);
+}
+
+static inline
+void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value)
+{
+ writel(value, hdev->regs + reg_id);
+}
+
+static inline
+void hdmi_write_mask(struct hdmi_device *hdev, u32 reg_id, u32 value, u32 mask)
+{
+ u32 old = readl(hdev->regs + reg_id);
+ value = (value & mask) | (old & ~mask);
+ writel(value, hdev->regs + reg_id);
+}
+
+static inline
+void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value)
+{
+ writeb(value, hdev->regs + reg_id);
+}
+
+static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id)
+{
+ return readl(hdev->regs + reg_id);
+}
+
+static irqreturn_t hdmi_irq_handler(int irq, void *dev_data)
+{
+ struct hdmi_device *hdev = dev_data;
+ u32 intc_flag;
+
+ (void)irq;
+ intc_flag = hdmi_read(hdev, HDMI_INTC_FLAG);
+ /* clearing flags for HPD plug/unplug */
+ if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
+ printk(KERN_INFO "unplugged\n");
+ hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0,
+ HDMI_INTC_FLAG_HPD_UNPLUG);
+ }
+ if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
+ printk(KERN_INFO "plugged\n");
+ hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0,
+ HDMI_INTC_FLAG_HPD_PLUG);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hdmi_reg_init(struct hdmi_device *hdev)
+{
+ /* enable HPD interrupts */
+ hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL |
+ HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
+ /* choose DVI mode */
+ hdmi_write_mask(hdev, HDMI_MODE_SEL,
+ HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
+ hdmi_write_mask(hdev, HDMI_CON_2, ~0,
+ HDMI_DVI_PERAMBLE_EN | HDMI_DVI_BAND_EN);
+ /* disable bluescreen */
+ hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
+ /* choose bluescreen (fecal) color */
+ hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12);
+ hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34);
+ hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56);
+}
+
+static void hdmi_timing_apply(struct hdmi_device *hdev,
+ const struct hdmi_preset_conf *conf)
+{
+ const struct hdmi_core_regs *core = &conf->core;
+ const struct hdmi_tg_regs *tg = &conf->tg;
+
+ /* setting core registers */
+ hdmi_writeb(hdev, HDMI_H_BLANK_0, core->h_blank[0]);
+ hdmi_writeb(hdev, HDMI_H_BLANK_1, core->h_blank[1]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_0, core->v_blank[0]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_1, core->v_blank[1]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_2, core->v_blank[2]);
+ hdmi_writeb(hdev, HDMI_H_V_LINE_0, core->h_v_line[0]);
+ hdmi_writeb(hdev, HDMI_H_V_LINE_1, core->h_v_line[1]);
+ hdmi_writeb(hdev, HDMI_H_V_LINE_2, core->h_v_line[2]);
+ hdmi_writeb(hdev, HDMI_VSYNC_POL, core->vsync_pol[0]);
+ hdmi_writeb(hdev, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_F_0, core->v_blank_f[0]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_F_1, core->v_blank_f[1]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_F_2, core->v_blank_f[2]);
+ hdmi_writeb(hdev, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]);
+ hdmi_writeb(hdev, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]);
+ hdmi_writeb(hdev, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
+ /* Timing generator registers */
+ hdmi_writeb(hdev, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
+ hdmi_writeb(hdev, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
+ hdmi_writeb(hdev, HDMI_TG_HACT_ST_L, tg->hact_st_l);
+ hdmi_writeb(hdev, HDMI_TG_HACT_ST_H, tg->hact_st_h);
+ hdmi_writeb(hdev, HDMI_TG_HACT_SZ_L, tg->hact_sz_l);
+ hdmi_writeb(hdev, HDMI_TG_HACT_SZ_H, tg->hact_sz_h);
+ hdmi_writeb(hdev, HDMI_TG_V_FSZ_L, tg->v_fsz_l);
+ hdmi_writeb(hdev, HDMI_TG_V_FSZ_H, tg->v_fsz_h);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_L, tg->vsync_l);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_H, tg->vsync_h);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC2_L, tg->vsync2_l);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC2_H, tg->vsync2_h);
+ hdmi_writeb(hdev, HDMI_TG_VACT_ST_L, tg->vact_st_l);
+ hdmi_writeb(hdev, HDMI_TG_VACT_ST_H, tg->vact_st_h);
+ hdmi_writeb(hdev, HDMI_TG_VACT_SZ_L, tg->vact_sz_l);
+ hdmi_writeb(hdev, HDMI_TG_VACT_SZ_H, tg->vact_sz_h);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_L, tg->field_chg_l);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
+ hdmi_writeb(hdev, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
+ hdmi_writeb(hdev, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
+}
+
+static int hdmi_conf_apply(struct hdmi_device *hdmi_dev)
+{
+ struct device *dev = hdmi_dev->dev;
+ const struct hdmi_preset_conf *conf = hdmi_dev->cur_conf;
+ struct v4l2_dv_preset preset;
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* reset hdmiphy */
+ hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
+ mdelay(10);
+ hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
+ mdelay(10);
+
+ /* configure presets */
+ preset.preset = hdmi_dev->cur_preset;
+ ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset);
+ if (ret) {
+ dev_err(dev, "failed to set preset (%u)\n", preset.preset);
+ return ret;
+ }
+
+ /* resetting HDMI core */
+ hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT);
+ mdelay(10);
+ hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT);
+ mdelay(10);
+
+ hdmi_reg_init(hdmi_dev);
+
+ /* setting core registers */
+ hdmi_timing_apply(hdmi_dev, conf);
+
+ return 0;
+}
+
+static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix)
+{
+#define DUMPREG(reg_id) \
+ dev_dbg(hdev->dev, "%s:" #reg_id " = %08x\n", prefix, \
+ readl(hdev->regs + reg_id))
+
+ dev_dbg(hdev->dev, "%s: ---- CONTROL REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_INTC_FLAG);
+ DUMPREG(HDMI_INTC_CON);
+ DUMPREG(HDMI_HPD_STATUS);
+ DUMPREG(HDMI_PHY_RSTOUT);
+ DUMPREG(HDMI_PHY_VPLL);
+ DUMPREG(HDMI_PHY_CMU);
+ DUMPREG(HDMI_CORE_RSTOUT);
+
+ dev_dbg(hdev->dev, "%s: ---- CORE REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_CON_0);
+ DUMPREG(HDMI_CON_1);
+ DUMPREG(HDMI_CON_2);
+ DUMPREG(HDMI_SYS_STATUS);
+ DUMPREG(HDMI_PHY_STATUS);
+ DUMPREG(HDMI_STATUS_EN);
+ DUMPREG(HDMI_HPD);
+ DUMPREG(HDMI_MODE_SEL);
+ DUMPREG(HDMI_HPD_GEN);
+ DUMPREG(HDMI_DC_CONTROL);
+ DUMPREG(HDMI_VIDEO_PATTERN_GEN);
+
+ dev_dbg(hdev->dev, "%s: ---- CORE SYNC REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_H_BLANK_0);
+ DUMPREG(HDMI_H_BLANK_1);
+ DUMPREG(HDMI_V_BLANK_0);
+ DUMPREG(HDMI_V_BLANK_1);
+ DUMPREG(HDMI_V_BLANK_2);
+ DUMPREG(HDMI_H_V_LINE_0);
+ DUMPREG(HDMI_H_V_LINE_1);
+ DUMPREG(HDMI_H_V_LINE_2);
+ DUMPREG(HDMI_VSYNC_POL);
+ DUMPREG(HDMI_INT_PRO_MODE);
+ DUMPREG(HDMI_V_BLANK_F_0);
+ DUMPREG(HDMI_V_BLANK_F_1);
+ DUMPREG(HDMI_V_BLANK_F_2);
+ DUMPREG(HDMI_H_SYNC_GEN_0);
+ DUMPREG(HDMI_H_SYNC_GEN_1);
+ DUMPREG(HDMI_H_SYNC_GEN_2);
+ DUMPREG(HDMI_V_SYNC_GEN_1_0);
+ DUMPREG(HDMI_V_SYNC_GEN_1_1);
+ DUMPREG(HDMI_V_SYNC_GEN_1_2);
+ DUMPREG(HDMI_V_SYNC_GEN_2_0);
+ DUMPREG(HDMI_V_SYNC_GEN_2_1);
+ DUMPREG(HDMI_V_SYNC_GEN_2_2);
+ DUMPREG(HDMI_V_SYNC_GEN_3_0);
+ DUMPREG(HDMI_V_SYNC_GEN_3_1);
+ DUMPREG(HDMI_V_SYNC_GEN_3_2);
+
+ dev_dbg(hdev->dev, "%s: ---- TG REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_TG_CMD);
+ DUMPREG(HDMI_TG_H_FSZ_L);
+ DUMPREG(HDMI_TG_H_FSZ_H);
+ DUMPREG(HDMI_TG_HACT_ST_L);
+ DUMPREG(HDMI_TG_HACT_ST_H);
+ DUMPREG(HDMI_TG_HACT_SZ_L);
+ DUMPREG(HDMI_TG_HACT_SZ_H);
+ DUMPREG(HDMI_TG_V_FSZ_L);
+ DUMPREG(HDMI_TG_V_FSZ_H);
+ DUMPREG(HDMI_TG_VSYNC_L);
+ DUMPREG(HDMI_TG_VSYNC_H);
+ DUMPREG(HDMI_TG_VSYNC2_L);
+ DUMPREG(HDMI_TG_VSYNC2_H);
+ DUMPREG(HDMI_TG_VACT_ST_L);
+ DUMPREG(HDMI_TG_VACT_ST_H);
+ DUMPREG(HDMI_TG_VACT_SZ_L);
+ DUMPREG(HDMI_TG_VACT_SZ_H);
+ DUMPREG(HDMI_TG_FIELD_CHG_L);
+ DUMPREG(HDMI_TG_FIELD_CHG_H);
+ DUMPREG(HDMI_TG_VACT_ST2_L);
+ DUMPREG(HDMI_TG_VACT_ST2_H);
+ DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
+ DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
+ DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
+ DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
+ DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
+ DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
+ DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
+ DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
+#undef DUMPREG
+}
+
+static const struct hdmi_preset_conf hdmi_conf_480p = {
+ .core = {
+ .h_blank = {0x8a, 0x00},
+ .v_blank = {0x0d, 0x6a, 0x01},
+ .h_v_line = {0x0d, 0xa2, 0x35},
+ .vsync_pol = {0x01},
+ .int_pro_mode = {0x00},
+ .v_blank_f = {0x00, 0x00, 0x00},
+ .h_sync_gen = {0x0e, 0x30, 0x11},
+ .v_sync_gen1 = {0x0f, 0x90, 0x00},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x5a, 0x03, /* h_fsz */
+ 0x8a, 0x00, 0xd0, 0x02, /* hact */
+ 0x0d, 0x02, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0xe0, 0x01, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x49, 0x02, /* vact_st2 */
+ 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ },
+ .mbus_fmt = {
+ .width = 720,
+ .height = 480,
+ .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_720p60 = {
+ .core = {
+ .h_blank = {0x72, 0x01},
+ .v_blank = {0xee, 0xf2, 0x00},
+ .h_v_line = {0xee, 0x22, 0x67},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
+ .h_sync_gen = {0x6c, 0x50, 0x02},
+ .v_sync_gen1 = {0x0a, 0x50, 0x00},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x72, 0x06, /* h_fsz */
+ 0x72, 0x01, 0x00, 0x05, /* hact */
+ 0xee, 0x02, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x1e, 0x00, 0xd0, 0x02, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x49, 0x02, /* vact_st2 */
+ 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ },
+ .mbus_fmt = {
+ .width = 1280,
+ .height = 720,
+ .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
+ .core = {
+ .h_blank = {0xd0, 0x02},
+ .v_blank = {0x65, 0x6c, 0x01},
+ .h_v_line = {0x65, 0x04, 0xa5},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
+ .h_sync_gen = {0x0e, 0xea, 0x08},
+ .v_sync_gen1 = {0x09, 0x40, 0x00},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x98, 0x08, /* h_fsz */
+ 0x18, 0x01, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0x38, 0x04, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x49, 0x02, /* vact_st2 */
+ 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ },
+ .mbus_fmt = {
+ .width = 1920,
+ .height = 1080,
+ .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
+ .core = {
+ .h_blank = {0x18, 0x01},
+ .v_blank = {0x65, 0x6c, 0x01},
+ .h_v_line = {0x65, 0x84, 0x89},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
+ .h_sync_gen = {0x56, 0x08, 0x02},
+ .v_sync_gen1 = {0x09, 0x40, 0x00},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x98, 0x08, /* h_fsz */
+ 0x18, 0x01, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0x38, 0x04, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ },
+ .mbus_fmt = {
+ .width = 1920,
+ .height = 1080,
+ .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+static const struct {
+ u32 preset;
+ const struct hdmi_preset_conf *conf;
+} hdmi_conf[] = {
+ { V4L2_DV_480P59_94, &hdmi_conf_480p },
+ { V4L2_DV_720P59_94, &hdmi_conf_720p60 },
+ { V4L2_DV_1080P50, &hdmi_conf_1080p50 },
+ { V4L2_DV_1080P30, &hdmi_conf_1080p60 },
+ { V4L2_DV_1080P60, &hdmi_conf_1080p60 },
+};
+
+static const struct hdmi_preset_conf *hdmi_preset2conf(u32 preset)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_conf); ++i)
+ if (hdmi_conf[i].preset == preset)
+ return hdmi_conf[i].conf;
+ return NULL;
+}
+
+static int hdmi_streamon(struct hdmi_device *hdev)
+{
+ struct device *dev = hdev->dev;
+ struct hdmi_resources *res = &hdev->res;
+ int ret, tries;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1);
+ if (ret)
+ return ret;
+
+ /* waiting for HDMIPHY's PLL to get to steady state */
+ for (tries = 100; tries; --tries) {
+ u32 val = hdmi_read(hdev, HDMI_PHY_STATUS);
+ if (val & HDMI_PHY_STATUS_READY)
+ break;
+ mdelay(1);
+ }
+ /* steady state not achieved */
+ if (tries == 0) {
+ dev_err(dev, "hdmiphy's pll could not reach steady state.\n");
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+ hdmi_dumpregs(hdev, "s_stream");
+ return -EIO;
+ }
+
+ /* hdmiphy clock is used for HDMI in streaming mode */
+ clk_disable(res->sclk_hdmi);
+ clk_set_parent(res->sclk_hdmi, res->sclk_hdmiphy);
+ clk_enable(res->sclk_hdmi);
+
+ /* enable HDMI and timing generator */
+ hdmi_write_mask(hdev, HDMI_CON_0, ~0, HDMI_EN);
+ hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_EN);
+ hdmi_dumpregs(hdev, "streamon");
+ return 0;
+}
+
+static int hdmi_streamoff(struct hdmi_device *hdev)
+{
+ struct device *dev = hdev->dev;
+ struct hdmi_resources *res = &hdev->res;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_EN);
+ hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_EN);
+
+ /* pixel(vpll) clock is used for HDMI in config mode */
+ clk_disable(res->sclk_hdmi);
+ clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
+ clk_enable(res->sclk_hdmi);
+
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+
+ hdmi_dumpregs(hdev, "streamoff");
+ return 0;
+}
+
+static int hdmi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ struct device *dev = hdev->dev;
+
+ dev_dbg(dev, "%s(%d)\n", __func__, enable);
+ if (enable)
+ return hdmi_streamon(hdev);
+ return hdmi_streamoff(hdev);
+}
+
+static void hdmi_resource_poweron(struct hdmi_resources *res)
+{
+ /* turn HDMI power on */
+ regulator_bulk_enable(res->regul_count, res->regul_bulk);
+ /* power-on hdmi physical interface */
+ clk_enable(res->hdmiphy);
+ /* use VPP as parent clock; HDMIPHY is not working yet */
+ clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
+ /* turn clocks on */
+ clk_enable(res->sclk_hdmi);
+}
+
+static void hdmi_resource_poweroff(struct hdmi_resources *res)
+{
+ /* turn clocks off */
+ clk_disable(res->sclk_hdmi);
+ /* power-off hdmiphy */
+ clk_disable(res->hdmiphy);
+ /* turn HDMI power off */
+ regulator_bulk_disable(res->regul_count, res->regul_bulk);
+}
+
+static int hdmi_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ int ret;
+
+ if (on)
+ ret = pm_runtime_get_sync(hdev->dev);
+ else
+ ret = pm_runtime_put_sync(hdev->dev);
+ /* only values < 0 indicate errors */
+ return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int hdmi_s_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *preset)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ struct device *dev = hdev->dev;
+ const struct hdmi_preset_conf *conf;
+
+ conf = hdmi_preset2conf(preset->preset);
+ if (conf == NULL) {
+ dev_err(dev, "preset (%u) not supported\n", preset->preset);
+ return -EINVAL;
+ }
+ hdev->cur_conf = conf;
+ hdev->cur_preset = preset->preset;
+ return 0;
+}
+
+static int hdmi_g_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *preset)
+{
+ memset(preset, 0, sizeof(*preset));
+ preset->preset = sd_to_hdmi_dev(sd)->cur_preset;
+ return 0;
+}
+
+static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ struct device *dev = hdev->dev;
+
+ dev_dbg(dev, "%s\n", __func__);
+ if (!hdev->cur_conf)
+ return -EINVAL;
+ *fmt = hdev->cur_conf->mbus_fmt;
+ return 0;
+}
+
+static int hdmi_enum_dv_presets(struct v4l2_subdev *sd,
+ struct v4l2_dv_enum_preset *preset)
+{
+ if (preset->index >= ARRAY_SIZE(hdmi_conf))
+ return -EINVAL;
+ return v4l_fill_dv_preset_info(hdmi_conf[preset->index].preset, preset);
+}
+
+static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {
+ .s_power = hdmi_s_power,
+};
+
+static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = {
+ .s_dv_preset = hdmi_s_dv_preset,
+ .g_dv_preset = hdmi_g_dv_preset,
+ .enum_dv_presets = hdmi_enum_dv_presets,
+ .g_mbus_fmt = hdmi_g_mbus_fmt,
+ .s_stream = hdmi_s_stream,
+};
+
+static const struct v4l2_subdev_ops hdmi_sd_ops = {
+ .core = &hdmi_sd_core_ops,
+ .video = &hdmi_sd_video_ops,
+};
+
+static int hdmi_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+
+ dev_dbg(dev, "%s\n", __func__);
+ hdmi_resource_poweroff(&hdev->res);
+ return 0;
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ int ret = 0;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ hdmi_resource_poweron(&hdev->res);
+
+ ret = hdmi_conf_apply(hdev);
+ if (ret)
+ goto fail;
+
+ dev_dbg(dev, "poweron succeed\n");
+
+ return 0;
+
+fail:
+ hdmi_resource_poweroff(&hdev->res);
+ dev_err(dev, "poweron failed\n");
+
+ return ret;
+}
+
+static const struct dev_pm_ops hdmi_pm_ops = {
+ .runtime_suspend = hdmi_runtime_suspend,
+ .runtime_resume = hdmi_runtime_resume,
+};
+
+static void hdmi_resources_cleanup(struct hdmi_device *hdev)
+{
+ struct hdmi_resources *res = &hdev->res;
+
+ dev_dbg(hdev->dev, "HDMI resource cleanup\n");
+ /* put clocks, power */
+ if (res->regul_count)
+ regulator_bulk_free(res->regul_count, res->regul_bulk);
+ /* kfree is NULL-safe */
+ kfree(res->regul_bulk);
+ if (!IS_ERR_OR_NULL(res->hdmiphy))
+ clk_put(res->hdmiphy);
+ if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
+ clk_put(res->sclk_hdmiphy);
+ if (!IS_ERR_OR_NULL(res->sclk_pixel))
+ clk_put(res->sclk_pixel);
+ if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+ clk_put(res->sclk_hdmi);
+ if (!IS_ERR_OR_NULL(res->hdmi))
+ clk_put(res->hdmi);
+ memset(res, 0, sizeof *res);
+}
+
+static int hdmi_resources_init(struct hdmi_device *hdev)
+{
+ struct device *dev = hdev->dev;
+ struct hdmi_resources *res = &hdev->res;
+ static char *supply[] = {
+ "hdmi-en",
+ "vdd",
+ "vdd_osc",
+ "vdd_pll",
+ };
+ int i, ret;
+
+ dev_dbg(dev, "HDMI resource init\n");
+
+ memset(res, 0, sizeof *res);
+ /* get clocks, power */
+
+ res->hdmi = clk_get(dev, "hdmi");
+ if (IS_ERR_OR_NULL(res->hdmi)) {
+ dev_err(dev, "failed to get clock 'hdmi'\n");
+ goto fail;
+ }
+ res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
+ if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
+ dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
+ goto fail;
+ }
+ res->sclk_pixel = clk_get(dev, "sclk_pixel");
+ if (IS_ERR_OR_NULL(res->sclk_pixel)) {
+ dev_err(dev, "failed to get clock 'sclk_pixel'\n");
+ goto fail;
+ }
+ res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy");
+ if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) {
+ dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n");
+ goto fail;
+ }
+ res->hdmiphy = clk_get(dev, "hdmiphy");
+ if (IS_ERR_OR_NULL(res->hdmiphy)) {
+ dev_err(dev, "failed to get clock 'hdmiphy'\n");
+ goto fail;
+ }
+ res->regul_bulk = kzalloc(ARRAY_SIZE(supply) *
+ sizeof res->regul_bulk[0], GFP_KERNEL);
+ if (!res->regul_bulk) {
+ dev_err(dev, "failed to get memory for regulators\n");
+ goto fail;
+ }
+ for (i = 0; i < ARRAY_SIZE(supply); ++i) {
+ res->regul_bulk[i].supply = supply[i];
+ res->regul_bulk[i].consumer = NULL;
+ }
+
+ ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
+ if (ret) {
+ dev_err(dev, "failed to get regulators\n");
+ goto fail;
+ }
+ res->regul_count = ARRAY_SIZE(supply);
+
+ return 0;
+fail:
+ dev_err(dev, "HDMI resource init - failed\n");
+ hdmi_resources_cleanup(hdev);
+ return -ENODEV;
+}
+
+static int __devinit hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct i2c_adapter *phy_adapter;
+ struct v4l2_subdev *sd;
+ struct hdmi_device *hdmi_dev = NULL;
+ struct hdmi_driver_data *drv_data;
+ int ret;
+
+ dev_dbg(dev, "probe start\n");
+
+ hdmi_dev = kzalloc(sizeof(*hdmi_dev), GFP_KERNEL);
+ if (!hdmi_dev) {
+ dev_err(dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ hdmi_dev->dev = dev;
+
+ ret = hdmi_resources_init(hdmi_dev);
+ if (ret)
+ goto fail_hdev;
+
+ /* mapping HDMI registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail_init;
+ }
+
+ hdmi_dev->regs = ioremap(res->start, resource_size(res));
+ if (hdmi_dev->regs == NULL) {
+ dev_err(dev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail_hdev;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(dev, "get interrupt resource failed.\n");
+ ret = -ENXIO;
+ goto fail_regs;
+ }
+
+ ret = request_irq(res->start, hdmi_irq_handler, 0, "hdmi", hdmi_dev);
+ if (ret) {
+ dev_err(dev, "request interrupt failed.\n");
+ goto fail_regs;
+ }
+ hdmi_dev->irq = res->start;
+
+ /* setting v4l2 name to prevent WARN_ON in v4l2_device_register */
+ strlcpy(hdmi_dev->v4l2_dev.name, dev_name(dev),
+ sizeof(hdmi_dev->v4l2_dev.name));
+ /* passing NULL owner prevents driver from erasing drvdata */
+ ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev);
+ if (ret) {
+ dev_err(dev, "could not register v4l2 device.\n");
+ goto fail_irq;
+ }
+
+ drv_data = (struct hdmi_driver_data *)
+ platform_get_device_id(pdev)->driver_data;
+ phy_adapter = i2c_get_adapter(drv_data->hdmiphy_bus);
+ if (phy_adapter == NULL) {
+ dev_err(dev, "adapter request failed\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev,
+ phy_adapter, &hdmiphy_info, NULL);
+ /* on failure or not adapter is no longer useful */
+ i2c_put_adapter(phy_adapter);
+ if (hdmi_dev->phy_sd == NULL) {
+ dev_err(dev, "missing subdev for hdmiphy\n");
+ ret = -ENODEV;
+ goto fail_vdev;
+ }
+
+ clk_enable(hdmi_dev->res.hdmi);
+
+ pm_runtime_enable(dev);
+
+ sd = &hdmi_dev->sd;
+ v4l2_subdev_init(sd, &hdmi_sd_ops);
+ sd->owner = THIS_MODULE;
+
+ strlcpy(sd->name, "s5p-hdmi", sizeof sd->name);
+ hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET;
+ /* FIXME: missing fail preset is not supported */
+ hdmi_dev->cur_conf = hdmi_preset2conf(hdmi_dev->cur_preset);
+
+ /* storing subdev for call that have only access to struct device */
+ dev_set_drvdata(dev, sd);
+
+ dev_info(dev, "probe sucessful\n");
+
+ return 0;
+
+fail_vdev:
+ v4l2_device_unregister(&hdmi_dev->v4l2_dev);
+
+fail_irq:
+ free_irq(hdmi_dev->irq, hdmi_dev);
+
+fail_regs:
+ iounmap(hdmi_dev->regs);
+
+fail_init:
+ hdmi_resources_cleanup(hdmi_dev);
+
+fail_hdev:
+ kfree(hdmi_dev);
+
+fail:
+ dev_err(dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit hdmi_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hdmi_device *hdmi_dev = sd_to_hdmi_dev(sd);
+
+ pm_runtime_disable(dev);
+ clk_disable(hdmi_dev->res.hdmi);
+ v4l2_device_unregister(&hdmi_dev->v4l2_dev);
+ disable_irq(hdmi_dev->irq);
+ free_irq(hdmi_dev->irq, hdmi_dev);
+ iounmap(hdmi_dev->regs);
+ hdmi_resources_cleanup(hdmi_dev);
+ kfree(hdmi_dev);
+ dev_info(dev, "remove sucessful\n");
+
+ return 0;
+}
+
+static struct platform_driver hdmi_driver __refdata = {
+ .probe = hdmi_probe,
+ .remove = __devexit_p(hdmi_remove),
+ .id_table = hdmi_driver_types,
+ .driver = {
+ .name = "s5p-hdmi",
+ .owner = THIS_MODULE,
+ .pm = &hdmi_pm_ops,
+ }
+};
+
+/* D R I V E R I N I T I A L I Z A T I O N */
+
+static int __init hdmi_init(void)
+{
+ int ret;
+ static const char banner[] __initdata = KERN_INFO \
+ "Samsung HDMI output driver, "
+ "(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
+ printk(banner);
+
+ ret = platform_driver_register(&hdmi_driver);
+ if (ret)
+ printk(KERN_ERR "HDMI platform driver register failed\n");
+
+ return ret;
+}
+module_init(hdmi_init);
+
+static void __exit hdmi_exit(void)
+{
+ platform_driver_unregister(&hdmi_driver);
+}
+module_exit(hdmi_exit);
+
+
diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/video/s5p-tv/hdmiphy_drv.c
new file mode 100644
index 000000000000..6693f4aff108
--- /dev/null
+++ b/drivers/media/video/s5p-tv/hdmiphy_drv.c
@@ -0,0 +1,188 @@
+/*
+ * Samsung HDMI Physical interface driver
+ *
+ * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd
+ * Author: Tomasz Stanislawski <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+
+#include <media/v4l2-subdev.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung HDMI Physical interface driver");
+MODULE_LICENSE("GPL");
+
+struct hdmiphy_conf {
+ u32 preset;
+ const u8 *data;
+};
+
+static const u8 hdmiphy_conf27[32] = {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
+ 0x6B, 0x10, 0x02, 0x51, 0xDf, 0xF2, 0x54, 0x87,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xe3, 0x26, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 hdmiphy_conf74_175[32] = {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
+ 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
+};
+
+static const u8 hdmiphy_conf74_25[32] = {
+ 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
+ 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xe0,
+ 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
+};
+
+static const u8 hdmiphy_conf148_5[32] = {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
+ 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
+ 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
+};
+
+static const struct hdmiphy_conf hdmiphy_conf[] = {
+ { V4L2_DV_480P59_94, hdmiphy_conf27 },
+ { V4L2_DV_1080P30, hdmiphy_conf74_175 },
+ { V4L2_DV_720P59_94, hdmiphy_conf74_175 },
+ { V4L2_DV_720P60, hdmiphy_conf74_25 },
+ { V4L2_DV_1080P50, hdmiphy_conf148_5 },
+ { V4L2_DV_1080P60, hdmiphy_conf148_5 },
+};
+
+const u8 *hdmiphy_preset2conf(u32 preset)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(hdmiphy_conf); ++i)
+ if (hdmiphy_conf[i].preset == preset)
+ return hdmiphy_conf[i].data;
+ return NULL;
+}
+
+static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
+{
+ /* to be implemented */
+ return 0;
+}
+
+static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *preset)
+{
+ const u8 *data;
+ u8 buffer[32];
+ int ret;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct device *dev = &client->dev;
+
+ dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset);
+ data = hdmiphy_preset2conf(preset->preset);
+ if (!data) {
+ dev_err(dev, "format not supported\n");
+ return -EINVAL;
+ }
+
+ /* storing configuration to the device */
+ memcpy(buffer, data, 32);
+ ret = i2c_master_send(client, buffer, 32);
+ if (ret != 32) {
+ dev_err(dev, "failed to configure HDMIPHY via I2C\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct device *dev = &client->dev;
+ u8 buffer[2];
+ int ret;
+
+ dev_info(dev, "s_stream(%d)\n", enable);
+ /* going to/from configuration from/to operation mode */
+ buffer[0] = 0x1f;
+ buffer[1] = enable ? 0x80 : 0x00;
+
+ ret = i2c_master_send(client, buffer, 2);
+ if (ret != 2) {
+ dev_err(dev, "stream (%d) failed\n", enable);
+ return -EIO;
+ }
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
+ .s_power = hdmiphy_s_power,
+};
+
+static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
+ .s_dv_preset = hdmiphy_s_dv_preset,
+ .s_stream = hdmiphy_s_stream,
+};
+
+static const struct v4l2_subdev_ops hdmiphy_ops = {
+ .core = &hdmiphy_core_ops,
+ .video = &hdmiphy_video_ops,
+};
+
+static int __devinit hdmiphy_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ static struct v4l2_subdev sd;
+
+ v4l2_i2c_subdev_init(&sd, client, &hdmiphy_ops);
+ dev_info(&client->dev, "probe successful\n");
+ return 0;
+}
+
+static int __devexit hdmiphy_remove(struct i2c_client *client)
+{
+ dev_info(&client->dev, "remove successful\n");
+ return 0;
+}
+
+static const struct i2c_device_id hdmiphy_id[] = {
+ { "hdmiphy", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
+
+static struct i2c_driver hdmiphy_driver = {
+ .driver = {
+ .name = "s5p-hdmiphy",
+ .owner = THIS_MODULE,
+ },
+ .probe = hdmiphy_probe,
+ .remove = __devexit_p(hdmiphy_remove),
+ .id_table = hdmiphy_id,
+};
+
+static int __init hdmiphy_init(void)
+{
+ return i2c_add_driver(&hdmiphy_driver);
+}
+module_init(hdmiphy_init);
+
+static void __exit hdmiphy_exit(void)
+{
+ i2c_del_driver(&hdmiphy_driver);
+}
+module_exit(hdmiphy_exit);
diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h
new file mode 100644
index 000000000000..51ad59b30358
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer.h
@@ -0,0 +1,352 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#ifndef SAMSUNG_MIXER_H
+#define SAMSUNG_MIXER_H
+
+#ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG
+ #define DEBUG
+#endif
+
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+
+#include "regs-mixer.h"
+
+/** maximum number of output interfaces */
+#define MXR_MAX_OUTPUTS 2
+/** maximum number of input interfaces (layers) */
+#define MXR_MAX_LAYERS 3
+#define MXR_DRIVER_NAME "s5p-mixer"
+/** maximal number of planes for every layer */
+#define MXR_MAX_PLANES 2
+
+#define MXR_ENABLE 1
+#define MXR_DISABLE 0
+
+/** description of a macroblock for packed formats */
+struct mxr_block {
+ /** vertical number of pixels in macroblock */
+ unsigned int width;
+ /** horizontal number of pixels in macroblock */
+ unsigned int height;
+ /** size of block in bytes */
+ unsigned int size;
+};
+
+/** description of supported format */
+struct mxr_format {
+ /** format name/mnemonic */
+ const char *name;
+ /** fourcc identifier */
+ u32 fourcc;
+ /** colorspace identifier */
+ enum v4l2_colorspace colorspace;
+ /** number of planes in image data */
+ int num_planes;
+ /** description of block for each plane */
+ struct mxr_block plane[MXR_MAX_PLANES];
+ /** number of subframes in image data */
+ int num_subframes;
+ /** specifies to which subframe belong given plane */
+ int plane2subframe[MXR_MAX_PLANES];
+ /** internal code, driver dependant */
+ unsigned long cookie;
+};
+
+/** description of crop configuration for image */
+struct mxr_crop {
+ /** width of layer in pixels */
+ unsigned int full_width;
+ /** height of layer in pixels */
+ unsigned int full_height;
+ /** horizontal offset of first pixel to be displayed */
+ unsigned int x_offset;
+ /** vertical offset of first pixel to be displayed */
+ unsigned int y_offset;
+ /** width of displayed data in pixels */
+ unsigned int width;
+ /** height of displayed data in pixels */
+ unsigned int height;
+ /** indicate which fields are present in buffer */
+ unsigned int field;
+};
+
+/** description of transformation from source to destination image */
+struct mxr_geometry {
+ /** cropping for source image */
+ struct mxr_crop src;
+ /** cropping for destination image */
+ struct mxr_crop dst;
+ /** layer-dependant description of horizontal scaling */
+ unsigned int x_ratio;
+ /** layer-dependant description of vertical scaling */
+ unsigned int y_ratio;
+};
+
+/** instance of a buffer */
+struct mxr_buffer {
+ /** common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ /** node for layer's lists */
+ struct list_head list;
+};
+
+
+/** internal states of layer */
+enum mxr_layer_state {
+ /** layers is not shown */
+ MXR_LAYER_IDLE = 0,
+ /** layer is shown */
+ MXR_LAYER_STREAMING,
+ /** state before STREAMOFF is finished */
+ MXR_LAYER_STREAMING_FINISH,
+};
+
+/** forward declarations */
+struct mxr_device;
+struct mxr_layer;
+
+/** callback for layers operation */
+struct mxr_layer_ops {
+ /* TODO: try to port it to subdev API */
+ /** handler for resource release function */
+ void (*release)(struct mxr_layer *);
+ /** setting buffer to HW */
+ void (*buffer_set)(struct mxr_layer *, struct mxr_buffer *);
+ /** setting format and geometry in HW */
+ void (*format_set)(struct mxr_layer *);
+ /** streaming stop/start */
+ void (*stream_set)(struct mxr_layer *, int);
+ /** adjusting geometry */
+ void (*fix_geometry)(struct mxr_layer *);
+};
+
+/** layer instance, a single window and content displayed on output */
+struct mxr_layer {
+ /** parent mixer device */
+ struct mxr_device *mdev;
+ /** layer index (unique identifier) */
+ int idx;
+ /** callbacks for layer methods */
+ struct mxr_layer_ops ops;
+ /** format array */
+ const struct mxr_format **fmt_array;
+ /** size of format array */
+ unsigned long fmt_array_size;
+
+ /** lock for protection of list and state fields */
+ spinlock_t enq_slock;
+ /** list for enqueued buffers */
+ struct list_head enq_list;
+ /** buffer currently owned by hardware in temporary registers */
+ struct mxr_buffer *update_buf;
+ /** buffer currently owned by hardware in shadow registers */
+ struct mxr_buffer *shadow_buf;
+ /** state of layer IDLE/STREAMING */
+ enum mxr_layer_state state;
+
+ /** mutex for protection of fields below */
+ struct mutex mutex;
+ /** handler for video node */
+ struct video_device vfd;
+ /** queue for output buffers */
+ struct vb2_queue vb_queue;
+ /** current image format */
+ const struct mxr_format *fmt;
+ /** current geometry of image */
+ struct mxr_geometry geo;
+};
+
+/** description of mixers output interface */
+struct mxr_output {
+ /** name of output */
+ char name[32];
+ /** output subdev */
+ struct v4l2_subdev *sd;
+ /** cookie used for configuration of registers */
+ int cookie;
+};
+
+/** specify source of output subdevs */
+struct mxr_output_conf {
+ /** name of output (connector) */
+ char *output_name;
+ /** name of module that generates output subdev */
+ char *module_name;
+ /** cookie need for mixer HW */
+ int cookie;
+};
+
+struct clk;
+struct regulator;
+
+/** auxiliary resources used my mixer */
+struct mxr_resources {
+ /** interrupt index */
+ int irq;
+ /** pointer to Mixer registers */
+ void __iomem *mxr_regs;
+ /** pointer to Video Processor registers */
+ void __iomem *vp_regs;
+ /** other resources, should used under mxr_device.mutex */
+ struct clk *mixer;
+ struct clk *vp;
+ struct clk *sclk_mixer;
+ struct clk *sclk_hdmi;
+ struct clk *sclk_dac;
+};
+
+/* event flags used */
+enum mxr_devide_flags {
+ MXR_EVENT_VSYNC = 0,
+};
+
+/** drivers instance */
+struct mxr_device {
+ /** master device */
+ struct device *dev;
+ /** state of each layer */
+ struct mxr_layer *layer[MXR_MAX_LAYERS];
+ /** state of each output */
+ struct mxr_output *output[MXR_MAX_OUTPUTS];
+ /** number of registered outputs */
+ int output_cnt;
+
+ /* video resources */
+
+ /** V4L2 device */
+ struct v4l2_device v4l2_dev;
+ /** context of allocator */
+ void *alloc_ctx;
+ /** event wait queue */
+ wait_queue_head_t event_queue;
+ /** state flags */
+ unsigned long event_flags;
+
+ /** spinlock for protection of registers */
+ spinlock_t reg_slock;
+
+ /** mutex for protection of fields below */
+ struct mutex mutex;
+ /** number of entities depndant on output configuration */
+ int n_output;
+ /** number of users that do streaming */
+ int n_streamer;
+ /** index of current output */
+ int current_output;
+ /** auxiliary resources used my mixer */
+ struct mxr_resources res;
+};
+
+/** transform device structure into mixer device */
+static inline struct mxr_device *to_mdev(struct device *dev)
+{
+ struct v4l2_device *vdev = dev_get_drvdata(dev);
+ return container_of(vdev, struct mxr_device, v4l2_dev);
+}
+
+/** get current output data, should be called under mdev's mutex */
+static inline struct mxr_output *to_output(struct mxr_device *mdev)
+{
+ return mdev->output[mdev->current_output];
+}
+
+/** get current output subdev, should be called under mdev's mutex */
+static inline struct v4l2_subdev *to_outsd(struct mxr_device *mdev)
+{
+ struct mxr_output *out = to_output(mdev);
+ return out ? out->sd : NULL;
+}
+
+/** forward declaration for mixer platform data */
+struct mxr_platform_data;
+
+/** acquiring common video resources */
+int __devinit mxr_acquire_video(struct mxr_device *mdev,
+ struct mxr_output_conf *output_cont, int output_count);
+
+/** releasing common video resources */
+void __devexit mxr_release_video(struct mxr_device *mdev);
+
+struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx);
+struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx);
+struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
+ int idx, char *name, struct mxr_layer_ops *ops);
+
+void mxr_base_layer_release(struct mxr_layer *layer);
+void mxr_layer_release(struct mxr_layer *layer);
+
+int mxr_base_layer_register(struct mxr_layer *layer);
+void mxr_base_layer_unregister(struct mxr_layer *layer);
+
+unsigned long mxr_get_plane_size(const struct mxr_block *blk,
+ unsigned int width, unsigned int height);
+
+/** adds new consumer for mixer's power */
+int __must_check mxr_power_get(struct mxr_device *mdev);
+/** removes consumer for mixer's power */
+void mxr_power_put(struct mxr_device *mdev);
+/** add new client for output configuration */
+void mxr_output_get(struct mxr_device *mdev);
+/** removes new client for output configuration */
+void mxr_output_put(struct mxr_device *mdev);
+/** add new client for streaming */
+void mxr_streamer_get(struct mxr_device *mdev);
+/** removes new client for streaming */
+void mxr_streamer_put(struct mxr_device *mdev);
+/** returns format of data delivared to current output */
+void mxr_get_mbus_fmt(struct mxr_device *mdev,
+ struct v4l2_mbus_framefmt *mbus_fmt);
+
+/* Debug */
+
+#define mxr_err(mdev, fmt, ...) dev_err(mdev->dev, fmt, ##__VA_ARGS__)
+#define mxr_warn(mdev, fmt, ...) dev_warn(mdev->dev, fmt, ##__VA_ARGS__)
+#define mxr_info(mdev, fmt, ...) dev_info(mdev->dev, fmt, ##__VA_ARGS__)
+
+#ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG
+ #define mxr_dbg(mdev, fmt, ...) dev_dbg(mdev->dev, fmt, ##__VA_ARGS__)
+#else
+ #define mxr_dbg(mdev, fmt, ...) do { (void) mdev; } while (0)
+#endif
+
+/* accessing Mixer's and Video Processor's registers */
+
+void mxr_vsync_set_update(struct mxr_device *mdev, int en);
+void mxr_reg_reset(struct mxr_device *mdev);
+irqreturn_t mxr_irq_handler(int irq, void *dev_data);
+void mxr_reg_s_output(struct mxr_device *mdev, int cookie);
+void mxr_reg_streamon(struct mxr_device *mdev);
+void mxr_reg_streamoff(struct mxr_device *mdev);
+int mxr_reg_wait4vsync(struct mxr_device *mdev);
+void mxr_reg_set_mbus_fmt(struct mxr_device *mdev,
+ struct v4l2_mbus_framefmt *fmt);
+void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en);
+void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr);
+void mxr_reg_graph_format(struct mxr_device *mdev, int idx,
+ const struct mxr_format *fmt, const struct mxr_geometry *geo);
+
+void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en);
+void mxr_reg_vp_buffer(struct mxr_device *mdev,
+ dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2]);
+void mxr_reg_vp_format(struct mxr_device *mdev,
+ const struct mxr_format *fmt, const struct mxr_geometry *geo);
+void mxr_reg_dump(struct mxr_device *mdev);
+
+#endif /* SAMSUNG_MIXER_H */
+
diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c
new file mode 100644
index 000000000000..00643094b221
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_drv.c
@@ -0,0 +1,487 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung MIXER");
+MODULE_LICENSE("GPL");
+
+/* --------- DRIVER PARAMETERS ---------- */
+
+static struct mxr_output_conf mxr_output_conf[] = {
+ {
+ .output_name = "S5P HDMI connector",
+ .module_name = "s5p-hdmi",
+ .cookie = 1,
+ },
+ {
+ .output_name = "S5P SDO connector",
+ .module_name = "s5p-sdo",
+ .cookie = 0,
+ },
+};
+
+void mxr_get_mbus_fmt(struct mxr_device *mdev,
+ struct v4l2_mbus_framefmt *mbus_fmt)
+{
+ struct v4l2_subdev *sd;
+ int ret;
+
+ mutex_lock(&mdev->mutex);
+ sd = to_outsd(mdev);
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, mbus_fmt);
+ WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
+ mutex_unlock(&mdev->mutex);
+}
+
+void mxr_streamer_get(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ ++mdev->n_streamer;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
+ if (mdev->n_streamer == 1) {
+ struct v4l2_subdev *sd = to_outsd(mdev);
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct mxr_resources *res = &mdev->res;
+ int ret;
+
+ if (to_output(mdev)->cookie == 0)
+ clk_set_parent(res->sclk_mixer, res->sclk_dac);
+ else
+ clk_set_parent(res->sclk_mixer, res->sclk_hdmi);
+ mxr_reg_s_output(mdev, to_output(mdev)->cookie);
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mbus_fmt);
+ WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ WARN(ret, "starting stream failed for output %s\n", sd->name);
+
+ mxr_reg_set_mbus_fmt(mdev, &mbus_fmt);
+ mxr_reg_streamon(mdev);
+ ret = mxr_reg_wait4vsync(mdev);
+ WARN(ret, "failed to get vsync (%d) from output\n", ret);
+ }
+ mutex_unlock(&mdev->mutex);
+ mxr_reg_dump(mdev);
+ /* FIXME: what to do when streaming fails? */
+}
+
+void mxr_streamer_put(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ --mdev->n_streamer;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
+ if (mdev->n_streamer == 0) {
+ int ret;
+ struct v4l2_subdev *sd = to_outsd(mdev);
+
+ mxr_reg_streamoff(mdev);
+ /* vsync applies Mixer setup */
+ ret = mxr_reg_wait4vsync(mdev);
+ WARN(ret, "failed to get vsync (%d) from output\n", ret);
+ ret = v4l2_subdev_call(sd, video, s_stream, 0);
+ WARN(ret, "stopping stream failed for output %s\n", sd->name);
+ }
+ WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n",
+ mdev->n_streamer);
+ mutex_unlock(&mdev->mutex);
+ mxr_reg_dump(mdev);
+}
+
+void mxr_output_get(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ ++mdev->n_output;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
+ /* turn on auxiliary driver */
+ if (mdev->n_output == 1)
+ v4l2_subdev_call(to_outsd(mdev), core, s_power, 1);
+ mutex_unlock(&mdev->mutex);
+}
+
+void mxr_output_put(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ --mdev->n_output;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
+ /* turn on auxiliary driver */
+ if (mdev->n_output == 0)
+ v4l2_subdev_call(to_outsd(mdev), core, s_power, 0);
+ WARN(mdev->n_output < 0, "negative number of output users (%d)\n",
+ mdev->n_output);
+ mutex_unlock(&mdev->mutex);
+}
+
+int mxr_power_get(struct mxr_device *mdev)
+{
+ int ret = pm_runtime_get_sync(mdev->dev);
+
+ /* returning 1 means that power is already enabled,
+ * so zero success be returned */
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ return 0;
+}
+
+void mxr_power_put(struct mxr_device *mdev)
+{
+ pm_runtime_put_sync(mdev->dev);
+}
+
+/* --------- RESOURCE MANAGEMENT -------------*/
+
+static int __devinit mxr_acquire_plat_resources(struct mxr_device *mdev,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr");
+ if (res == NULL) {
+ mxr_err(mdev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ mdev->res.mxr_regs = ioremap(res->start, resource_size(res));
+ if (mdev->res.mxr_regs == NULL) {
+ mxr_err(mdev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp");
+ if (res == NULL) {
+ mxr_err(mdev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail_mxr_regs;
+ }
+
+ mdev->res.vp_regs = ioremap(res->start, resource_size(res));
+ if (mdev->res.vp_regs == NULL) {
+ mxr_err(mdev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail_mxr_regs;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
+ if (res == NULL) {
+ mxr_err(mdev, "get interrupt resource failed.\n");
+ ret = -ENXIO;
+ goto fail_vp_regs;
+ }
+
+ ret = request_irq(res->start, mxr_irq_handler, 0, "s5p-mixer", mdev);
+ if (ret) {
+ mxr_err(mdev, "request interrupt failed.\n");
+ goto fail_vp_regs;
+ }
+ mdev->res.irq = res->start;
+
+ return 0;
+
+fail_vp_regs:
+ iounmap(mdev->res.vp_regs);
+
+fail_mxr_regs:
+ iounmap(mdev->res.mxr_regs);
+
+fail:
+ return ret;
+}
+
+static void mxr_release_plat_resources(struct mxr_device *mdev)
+{
+ free_irq(mdev->res.irq, mdev);
+ iounmap(mdev->res.vp_regs);
+ iounmap(mdev->res.mxr_regs);
+}
+
+static void mxr_release_clocks(struct mxr_device *mdev)
+{
+ struct mxr_resources *res = &mdev->res;
+
+ if (!IS_ERR_OR_NULL(res->sclk_dac))
+ clk_put(res->sclk_dac);
+ if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+ clk_put(res->sclk_hdmi);
+ if (!IS_ERR_OR_NULL(res->sclk_mixer))
+ clk_put(res->sclk_mixer);
+ if (!IS_ERR_OR_NULL(res->vp))
+ clk_put(res->vp);
+ if (!IS_ERR_OR_NULL(res->mixer))
+ clk_put(res->mixer);
+}
+
+static int mxr_acquire_clocks(struct mxr_device *mdev)
+{
+ struct mxr_resources *res = &mdev->res;
+ struct device *dev = mdev->dev;
+
+ res->mixer = clk_get(dev, "mixer");
+ if (IS_ERR_OR_NULL(res->mixer)) {
+ mxr_err(mdev, "failed to get clock 'mixer'\n");
+ goto fail;
+ }
+ res->vp = clk_get(dev, "vp");
+ if (IS_ERR_OR_NULL(res->vp)) {
+ mxr_err(mdev, "failed to get clock 'vp'\n");
+ goto fail;
+ }
+ res->sclk_mixer = clk_get(dev, "sclk_mixer");
+ if (IS_ERR_OR_NULL(res->sclk_mixer)) {
+ mxr_err(mdev, "failed to get clock 'sclk_mixer'\n");
+ goto fail;
+ }
+ res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
+ if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
+ mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n");
+ goto fail;
+ }
+ res->sclk_dac = clk_get(dev, "sclk_dac");
+ if (IS_ERR_OR_NULL(res->sclk_dac)) {
+ mxr_err(mdev, "failed to get clock 'sclk_dac'\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ mxr_release_clocks(mdev);
+ return -ENODEV;
+}
+
+static int __devinit mxr_acquire_resources(struct mxr_device *mdev,
+ struct platform_device *pdev)
+{
+ int ret;
+ ret = mxr_acquire_plat_resources(mdev, pdev);
+
+ if (ret)
+ goto fail;
+
+ ret = mxr_acquire_clocks(mdev);
+ if (ret)
+ goto fail_plat;
+
+ mxr_info(mdev, "resources acquired\n");
+ return 0;
+
+fail_plat:
+ mxr_release_plat_resources(mdev);
+fail:
+ mxr_err(mdev, "resources acquire failed\n");
+ return ret;
+}
+
+static void mxr_release_resources(struct mxr_device *mdev)
+{
+ mxr_release_clocks(mdev);
+ mxr_release_plat_resources(mdev);
+ memset(&mdev->res, 0, sizeof mdev->res);
+}
+
+static void mxr_release_layers(struct mxr_device *mdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i)
+ if (mdev->layer[i])
+ mxr_layer_release(mdev->layer[i]);
+}
+
+static int __devinit mxr_acquire_layers(struct mxr_device *mdev,
+ struct mxr_platform_data *pdata)
+{
+ mdev->layer[0] = mxr_graph_layer_create(mdev, 0);
+ mdev->layer[1] = mxr_graph_layer_create(mdev, 1);
+ mdev->layer[2] = mxr_vp_layer_create(mdev, 0);
+
+ if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) {
+ mxr_err(mdev, "failed to acquire layers\n");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ mxr_release_layers(mdev);
+ return -ENODEV;
+}
+
+/* ---------- POWER MANAGEMENT ----------- */
+
+static int mxr_runtime_resume(struct device *dev)
+{
+ struct mxr_device *mdev = to_mdev(dev);
+ struct mxr_resources *res = &mdev->res;
+
+ mxr_dbg(mdev, "resume - start\n");
+ mutex_lock(&mdev->mutex);
+ /* turn clocks on */
+ clk_enable(res->mixer);
+ clk_enable(res->vp);
+ clk_enable(res->sclk_mixer);
+ /* apply default configuration */
+ mxr_reg_reset(mdev);
+ mxr_dbg(mdev, "resume - finished\n");
+
+ mutex_unlock(&mdev->mutex);
+ return 0;
+}
+
+static int mxr_runtime_suspend(struct device *dev)
+{
+ struct mxr_device *mdev = to_mdev(dev);
+ struct mxr_resources *res = &mdev->res;
+ mxr_dbg(mdev, "suspend - start\n");
+ mutex_lock(&mdev->mutex);
+ /* turn clocks off */
+ clk_disable(res->sclk_mixer);
+ clk_disable(res->vp);
+ clk_disable(res->mixer);
+ mutex_unlock(&mdev->mutex);
+ mxr_dbg(mdev, "suspend - finished\n");
+ return 0;
+}
+
+static const struct dev_pm_ops mxr_pm_ops = {
+ .runtime_suspend = mxr_runtime_suspend,
+ .runtime_resume = mxr_runtime_resume,
+};
+
+/* --------- DRIVER INITIALIZATION ---------- */
+
+static int __devinit mxr_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxr_platform_data *pdata = dev->platform_data;
+ struct mxr_device *mdev;
+ int ret;
+
+ /* mdev does not exist yet so no mxr_dbg is used */
+ dev_info(dev, "probe start\n");
+
+ mdev = kzalloc(sizeof *mdev, GFP_KERNEL);
+ if (!mdev) {
+ mxr_err(mdev, "not enough memory.\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ /* setup pointer to master device */
+ mdev->dev = dev;
+
+ mutex_init(&mdev->mutex);
+ spin_lock_init(&mdev->reg_slock);
+ init_waitqueue_head(&mdev->event_queue);
+
+ /* acquire resources: regs, irqs, clocks, regulators */
+ ret = mxr_acquire_resources(mdev, pdev);
+ if (ret)
+ goto fail_mem;
+
+ /* configure resources for video output */
+ ret = mxr_acquire_video(mdev, mxr_output_conf,
+ ARRAY_SIZE(mxr_output_conf));
+ if (ret)
+ goto fail_resources;
+
+ /* configure layers */
+ ret = mxr_acquire_layers(mdev, pdata);
+ if (ret)
+ goto fail_video;
+
+ pm_runtime_enable(dev);
+
+ mxr_info(mdev, "probe successful\n");
+ return 0;
+
+fail_video:
+ mxr_release_video(mdev);
+
+fail_resources:
+ mxr_release_resources(mdev);
+
+fail_mem:
+ kfree(mdev);
+
+fail:
+ dev_info(dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit mxr_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxr_device *mdev = to_mdev(dev);
+
+ pm_runtime_disable(dev);
+
+ mxr_release_layers(mdev);
+ mxr_release_video(mdev);
+ mxr_release_resources(mdev);
+
+ kfree(mdev);
+
+ dev_info(dev, "remove sucessful\n");
+ return 0;
+}
+
+static struct platform_driver mxr_driver __refdata = {
+ .probe = mxr_probe,
+ .remove = __devexit_p(mxr_remove),
+ .driver = {
+ .name = MXR_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &mxr_pm_ops,
+ }
+};
+
+static int __init mxr_init(void)
+{
+ int i, ret;
+ static const char banner[] __initdata = KERN_INFO
+ "Samsung TV Mixer driver, "
+ "(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
+ printk(banner);
+
+ /* Loading auxiliary modules */
+ for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i)
+ request_module(mxr_output_conf[i].module_name);
+
+ ret = platform_driver_register(&mxr_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "registration of MIXER driver failed\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+module_init(mxr_init);
+
+static void __exit mxr_exit(void)
+{
+ platform_driver_unregister(&mxr_driver);
+}
+module_exit(mxr_exit);
diff --git a/drivers/media/video/s5p-tv/mixer_grp_layer.c b/drivers/media/video/s5p-tv/mixer_grp_layer.c
new file mode 100644
index 000000000000..de8270c2b6e7
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_grp_layer.c
@@ -0,0 +1,185 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include <media/videobuf2-dma-contig.h>
+
+/* FORMAT DEFINITIONS */
+
+static const struct mxr_format mxr_fb_fmt_rgb565 = {
+ .name = "RGB565",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .num_planes = 1,
+ .plane = {
+ { .width = 1, .height = 1, .size = 2 },
+ },
+ .num_subframes = 1,
+ .cookie = 4,
+};
+
+static const struct mxr_format mxr_fb_fmt_argb1555 = {
+ .name = "ARGB1555",
+ .num_planes = 1,
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .plane = {
+ { .width = 1, .height = 1, .size = 2 },
+ },
+ .num_subframes = 1,
+ .cookie = 5,
+};
+
+static const struct mxr_format mxr_fb_fmt_argb4444 = {
+ .name = "ARGB4444",
+ .num_planes = 1,
+ .fourcc = V4L2_PIX_FMT_RGB444,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .plane = {
+ { .width = 1, .height = 1, .size = 2 },
+ },
+ .num_subframes = 1,
+ .cookie = 6,
+};
+
+static const struct mxr_format mxr_fb_fmt_argb8888 = {
+ .name = "ARGB8888",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .num_planes = 1,
+ .plane = {
+ { .width = 1, .height = 1, .size = 4 },
+ },
+ .num_subframes = 1,
+ .cookie = 7,
+};
+
+static const struct mxr_format *mxr_graph_format[] = {
+ &mxr_fb_fmt_rgb565,
+ &mxr_fb_fmt_argb1555,
+ &mxr_fb_fmt_argb4444,
+ &mxr_fb_fmt_argb8888,
+};
+
+/* AUXILIARY CALLBACKS */
+
+static void mxr_graph_layer_release(struct mxr_layer *layer)
+{
+ mxr_base_layer_unregister(layer);
+ mxr_base_layer_release(layer);
+}
+
+static void mxr_graph_buffer_set(struct mxr_layer *layer,
+ struct mxr_buffer *buf)
+{
+ dma_addr_t addr = 0;
+
+ if (buf)
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb, 0);
+ mxr_reg_graph_buffer(layer->mdev, layer->idx, addr);
+}
+
+static void mxr_graph_stream_set(struct mxr_layer *layer, int en)
+{
+ mxr_reg_graph_layer_stream(layer->mdev, layer->idx, en);
+}
+
+static void mxr_graph_format_set(struct mxr_layer *layer)
+{
+ mxr_reg_graph_format(layer->mdev, layer->idx,
+ layer->fmt, &layer->geo);
+}
+
+static void mxr_graph_fix_geometry(struct mxr_layer *layer)
+{
+ struct mxr_geometry *geo = &layer->geo;
+
+ /* limit to boundary size */
+ geo->src.full_width = clamp_val(geo->src.full_width, 1, 32767);
+ geo->src.full_height = clamp_val(geo->src.full_height, 1, 2047);
+ geo->src.width = clamp_val(geo->src.width, 1, geo->src.full_width);
+ geo->src.width = min(geo->src.width, 2047U);
+ /* not possible to crop of Y axis */
+ geo->src.y_offset = min(geo->src.y_offset, geo->src.full_height - 1);
+ geo->src.height = geo->src.full_height - geo->src.y_offset;
+ /* limitting offset */
+ geo->src.x_offset = min(geo->src.x_offset,
+ geo->src.full_width - geo->src.width);
+
+ /* setting position in output */
+ geo->dst.width = min(geo->dst.width, geo->dst.full_width);
+ geo->dst.height = min(geo->dst.height, geo->dst.full_height);
+
+ /* Mixer supports only 1x and 2x scaling */
+ if (geo->dst.width >= 2 * geo->src.width) {
+ geo->x_ratio = 1;
+ geo->dst.width = 2 * geo->src.width;
+ } else {
+ geo->x_ratio = 0;
+ geo->dst.width = geo->src.width;
+ }
+
+ if (geo->dst.height >= 2 * geo->src.height) {
+ geo->y_ratio = 1;
+ geo->dst.height = 2 * geo->src.height;
+ } else {
+ geo->y_ratio = 0;
+ geo->dst.height = geo->src.height;
+ }
+
+ geo->dst.x_offset = min(geo->dst.x_offset,
+ geo->dst.full_width - geo->dst.width);
+ geo->dst.y_offset = min(geo->dst.y_offset,
+ geo->dst.full_height - geo->dst.height);
+}
+
+/* PUBLIC API */
+
+struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx)
+{
+ struct mxr_layer *layer;
+ int ret;
+ struct mxr_layer_ops ops = {
+ .release = mxr_graph_layer_release,
+ .buffer_set = mxr_graph_buffer_set,
+ .stream_set = mxr_graph_stream_set,
+ .format_set = mxr_graph_format_set,
+ .fix_geometry = mxr_graph_fix_geometry,
+ };
+ char name[32];
+
+ sprintf(name, "graph%d", idx);
+
+ layer = mxr_base_layer_create(mdev, idx, name, &ops);
+ if (layer == NULL) {
+ mxr_err(mdev, "failed to initialize layer(%d) base\n", idx);
+ goto fail;
+ }
+
+ layer->fmt_array = mxr_graph_format;
+ layer->fmt_array_size = ARRAY_SIZE(mxr_graph_format);
+
+ ret = mxr_base_layer_register(layer);
+ if (ret)
+ goto fail_layer;
+
+ return layer;
+
+fail_layer:
+ mxr_base_layer_release(layer);
+
+fail:
+ return NULL;
+}
+
diff --git a/drivers/media/video/s5p-tv/mixer_reg.c b/drivers/media/video/s5p-tv/mixer_reg.c
new file mode 100644
index 000000000000..4800a3cbb297
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_reg.c
@@ -0,0 +1,548 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+#include "regs-mixer.h"
+#include "regs-vp.h"
+
+#include <linux/delay.h>
+
+/* Register access subroutines */
+
+static inline u32 vp_read(struct mxr_device *mdev, u32 reg_id)
+{
+ return readl(mdev->res.vp_regs + reg_id);
+}
+
+static inline void vp_write(struct mxr_device *mdev, u32 reg_id, u32 val)
+{
+ writel(val, mdev->res.vp_regs + reg_id);
+}
+
+static inline void vp_write_mask(struct mxr_device *mdev, u32 reg_id,
+ u32 val, u32 mask)
+{
+ u32 old = vp_read(mdev, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, mdev->res.vp_regs + reg_id);
+}
+
+static inline u32 mxr_read(struct mxr_device *mdev, u32 reg_id)
+{
+ return readl(mdev->res.mxr_regs + reg_id);
+}
+
+static inline void mxr_write(struct mxr_device *mdev, u32 reg_id, u32 val)
+{
+ writel(val, mdev->res.mxr_regs + reg_id);
+}
+
+static inline void mxr_write_mask(struct mxr_device *mdev, u32 reg_id,
+ u32 val, u32 mask)
+{
+ u32 old = mxr_read(mdev, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, mdev->res.mxr_regs + reg_id);
+}
+
+void mxr_vsync_set_update(struct mxr_device *mdev, int en)
+{
+ /* block update on vsync */
+ mxr_write_mask(mdev, MXR_STATUS, en ? MXR_STATUS_SYNC_ENABLE : 0,
+ MXR_STATUS_SYNC_ENABLE);
+ vp_write(mdev, VP_SHADOW_UPDATE, en ? VP_SHADOW_UPDATE_ENABLE : 0);
+}
+
+static void __mxr_reg_vp_reset(struct mxr_device *mdev)
+{
+ int tries = 100;
+
+ vp_write(mdev, VP_SRESET, VP_SRESET_PROCESSING);
+ for (tries = 100; tries; --tries) {
+ /* waiting until VP_SRESET_PROCESSING is 0 */
+ if (~vp_read(mdev, VP_SRESET) & VP_SRESET_PROCESSING)
+ break;
+ mdelay(10);
+ }
+ WARN(tries == 0, "failed to reset Video Processor\n");
+}
+
+static void mxr_reg_vp_default_filter(struct mxr_device *mdev);
+
+void mxr_reg_reset(struct mxr_device *mdev)
+{
+ unsigned long flags;
+ u32 val; /* value stored to register */
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ /* set output in RGB888 mode */
+ mxr_write(mdev, MXR_CFG, MXR_CFG_OUT_RGB888);
+
+ /* 16 beat burst in DMA */
+ mxr_write_mask(mdev, MXR_STATUS, MXR_STATUS_16_BURST,
+ MXR_STATUS_BURST_MASK);
+
+ /* setting default layer priority: layer1 > video > layer0
+ * because typical usage scenario would be
+ * layer0 - framebuffer
+ * video - video overlay
+ * layer1 - OSD
+ */
+ val = MXR_LAYER_CFG_GRP0_VAL(1);
+ val |= MXR_LAYER_CFG_VP_VAL(2);
+ val |= MXR_LAYER_CFG_GRP1_VAL(3);
+ mxr_write(mdev, MXR_LAYER_CFG, val);
+
+ /* use dark gray background color */
+ mxr_write(mdev, MXR_BG_COLOR0, 0x808080);
+ mxr_write(mdev, MXR_BG_COLOR1, 0x808080);
+ mxr_write(mdev, MXR_BG_COLOR2, 0x808080);
+
+ /* setting graphical layers */
+
+ val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
+ val |= MXR_GRP_CFG_BLEND_PRE_MUL; /* premul mode */
+ val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
+
+ /* the same configuration for both layers */
+ mxr_write(mdev, MXR_GRAPHIC_CFG(0), val);
+ mxr_write(mdev, MXR_GRAPHIC_CFG(1), val);
+
+ /* configuration of Video Processor Registers */
+ __mxr_reg_vp_reset(mdev);
+ mxr_reg_vp_default_filter(mdev);
+
+ /* enable all interrupts */
+ mxr_write_mask(mdev, MXR_INT_EN, ~0, MXR_INT_EN_ALL);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_graph_format(struct mxr_device *mdev, int idx,
+ const struct mxr_format *fmt, const struct mxr_geometry *geo)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ /* setup format */
+ mxr_write_mask(mdev, MXR_GRAPHIC_CFG(idx),
+ MXR_GRP_CFG_FORMAT_VAL(fmt->cookie), MXR_GRP_CFG_FORMAT_MASK);
+
+ /* setup geometry */
+ mxr_write(mdev, MXR_GRAPHIC_SPAN(idx), geo->src.full_width);
+ val = MXR_GRP_WH_WIDTH(geo->src.width);
+ val |= MXR_GRP_WH_HEIGHT(geo->src.height);
+ val |= MXR_GRP_WH_H_SCALE(geo->x_ratio);
+ val |= MXR_GRP_WH_V_SCALE(geo->y_ratio);
+ mxr_write(mdev, MXR_GRAPHIC_WH(idx), val);
+
+ /* setup offsets in source image */
+ val = MXR_GRP_SXY_SX(geo->src.x_offset);
+ val |= MXR_GRP_SXY_SY(geo->src.y_offset);
+ mxr_write(mdev, MXR_GRAPHIC_SXY(idx), val);
+
+ /* setup offsets in display image */
+ val = MXR_GRP_DXY_DX(geo->dst.x_offset);
+ val |= MXR_GRP_DXY_DY(geo->dst.y_offset);
+ mxr_write(mdev, MXR_GRAPHIC_DXY(idx), val);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_vp_format(struct mxr_device *mdev,
+ const struct mxr_format *fmt, const struct mxr_geometry *geo)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ vp_write_mask(mdev, VP_MODE, fmt->cookie, VP_MODE_FMT_MASK);
+
+ /* setting size of input image */
+ vp_write(mdev, VP_IMG_SIZE_Y, VP_IMG_HSIZE(geo->src.full_width) |
+ VP_IMG_VSIZE(geo->src.full_height));
+ /* chroma height has to reduced by 2 to avoid chroma distorions */
+ vp_write(mdev, VP_IMG_SIZE_C, VP_IMG_HSIZE(geo->src.full_width) |
+ VP_IMG_VSIZE(geo->src.full_height / 2));
+
+ vp_write(mdev, VP_SRC_WIDTH, geo->src.width);
+ vp_write(mdev, VP_SRC_HEIGHT, geo->src.height);
+ vp_write(mdev, VP_SRC_H_POSITION,
+ VP_SRC_H_POSITION_VAL(geo->src.x_offset));
+ vp_write(mdev, VP_SRC_V_POSITION, geo->src.y_offset);
+
+ vp_write(mdev, VP_DST_WIDTH, geo->dst.width);
+ vp_write(mdev, VP_DST_H_POSITION, geo->dst.x_offset);
+ if (geo->dst.field == V4L2_FIELD_INTERLACED) {
+ vp_write(mdev, VP_DST_HEIGHT, geo->dst.height / 2);
+ vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset / 2);
+ } else {
+ vp_write(mdev, VP_DST_HEIGHT, geo->dst.height);
+ vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset);
+ }
+
+ vp_write(mdev, VP_H_RATIO, geo->x_ratio);
+ vp_write(mdev, VP_V_RATIO, geo->y_ratio);
+
+ vp_write(mdev, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+
+}
+
+void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr)
+{
+ u32 val = addr ? ~0 : 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ if (idx == 0)
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
+ else
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
+ mxr_write(mdev, MXR_GRAPHIC_BASE(idx), addr);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_vp_buffer(struct mxr_device *mdev,
+ dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2])
+{
+ u32 val = luma_addr[0] ? ~0 : 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_VP_ENABLE);
+ vp_write_mask(mdev, VP_ENABLE, val, VP_ENABLE_ON);
+ /* TODO: fix tiled mode */
+ vp_write(mdev, VP_TOP_Y_PTR, luma_addr[0]);
+ vp_write(mdev, VP_TOP_C_PTR, chroma_addr[0]);
+ vp_write(mdev, VP_BOT_Y_PTR, luma_addr[1]);
+ vp_write(mdev, VP_BOT_C_PTR, chroma_addr[1]);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+static void mxr_irq_layer_handle(struct mxr_layer *layer)
+{
+ struct list_head *head = &layer->enq_list;
+ struct mxr_buffer *done;
+
+ /* skip non-existing layer */
+ if (layer == NULL)
+ return;
+
+ spin_lock(&layer->enq_slock);
+ if (layer->state == MXR_LAYER_IDLE)
+ goto done;
+
+ done = layer->shadow_buf;
+ layer->shadow_buf = layer->update_buf;
+
+ if (list_empty(head)) {
+ if (layer->state != MXR_LAYER_STREAMING)
+ layer->update_buf = NULL;
+ } else {
+ struct mxr_buffer *next;
+ next = list_first_entry(head, struct mxr_buffer, list);
+ list_del(&next->list);
+ layer->update_buf = next;
+ }
+
+ layer->ops.buffer_set(layer, layer->update_buf);
+
+ if (done && done != layer->shadow_buf)
+ vb2_buffer_done(&done->vb, VB2_BUF_STATE_DONE);
+
+done:
+ spin_unlock(&layer->enq_slock);
+}
+
+irqreturn_t mxr_irq_handler(int irq, void *dev_data)
+{
+ struct mxr_device *mdev = dev_data;
+ u32 i, val;
+
+ spin_lock(&mdev->reg_slock);
+ val = mxr_read(mdev, MXR_INT_STATUS);
+
+ /* wake up process waiting for VSYNC */
+ if (val & MXR_INT_STATUS_VSYNC) {
+ set_bit(MXR_EVENT_VSYNC, &mdev->event_flags);
+ wake_up(&mdev->event_queue);
+ }
+
+ /* clear interrupts */
+ if (~val & MXR_INT_EN_VSYNC) {
+ /* vsync interrupt use different bit for read and clear */
+ val &= ~MXR_INT_EN_VSYNC;
+ val |= MXR_INT_CLEAR_VSYNC;
+ }
+ mxr_write(mdev, MXR_INT_STATUS, val);
+
+ spin_unlock(&mdev->reg_slock);
+ /* leave on non-vsync event */
+ if (~val & MXR_INT_CLEAR_VSYNC)
+ return IRQ_HANDLED;
+ for (i = 0; i < MXR_MAX_LAYERS; ++i)
+ mxr_irq_layer_handle(mdev->layer[i]);
+ return IRQ_HANDLED;
+}
+
+void mxr_reg_s_output(struct mxr_device *mdev, int cookie)
+{
+ u32 val;
+
+ val = cookie == 0 ? MXR_CFG_DST_SDO : MXR_CFG_DST_HDMI;
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_DST_MASK);
+}
+
+void mxr_reg_streamon(struct mxr_device *mdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ /* single write -> no need to block vsync update */
+
+ /* start MIXER */
+ mxr_write_mask(mdev, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
+
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_streamoff(struct mxr_device *mdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ /* single write -> no need to block vsync update */
+
+ /* stop MIXER */
+ mxr_write_mask(mdev, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
+
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+int mxr_reg_wait4vsync(struct mxr_device *mdev)
+{
+ int ret;
+
+ clear_bit(MXR_EVENT_VSYNC, &mdev->event_flags);
+ /* TODO: consider adding interruptible */
+ ret = wait_event_timeout(mdev->event_queue,
+ test_bit(MXR_EVENT_VSYNC, &mdev->event_flags),
+ msecs_to_jiffies(1000));
+ if (ret > 0)
+ return 0;
+ if (ret < 0)
+ return ret;
+ mxr_warn(mdev, "no vsync detected - timeout\n");
+ return -ETIME;
+}
+
+void mxr_reg_set_mbus_fmt(struct mxr_device *mdev,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ u32 val = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ /* selecting colorspace accepted by output */
+ if (fmt->colorspace == V4L2_COLORSPACE_JPEG)
+ val |= MXR_CFG_OUT_YUV444;
+ else
+ val |= MXR_CFG_OUT_RGB888;
+
+ /* choosing between interlace and progressive mode */
+ if (fmt->field == V4L2_FIELD_INTERLACED)
+ val |= MXR_CFG_SCAN_INTERLACE;
+ else
+ val |= MXR_CFG_SCAN_PROGRASSIVE;
+
+ /* choosing between porper HD and SD mode */
+ if (fmt->height == 480)
+ val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
+ else if (fmt->height == 576)
+ val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
+ else if (fmt->height == 720)
+ val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
+ else if (fmt->height == 1080)
+ val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
+ else
+ WARN(1, "unrecognized mbus height %u!\n", fmt->height);
+
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_SCAN_MASK |
+ MXR_CFG_OUT_MASK);
+
+ val = (fmt->field == V4L2_FIELD_INTERLACED) ? ~0 : 0;
+ vp_write_mask(mdev, VP_MODE, val,
+ VP_MODE_LINE_SKIP | VP_MODE_FIELD_ID_AUTO_TOGGLING);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en)
+{
+ /* no extra actions need to be done */
+}
+
+void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en)
+{
+ /* no extra actions need to be done */
+}
+
+static const u8 filter_y_horiz_tap8[] = {
+ 0, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, 0, 0,
+ 0, 2, 4, 5, 6, 6, 6, 6,
+ 6, 5, 5, 4, 3, 2, 1, 1,
+ 0, -6, -12, -16, -18, -20, -21, -20,
+ -20, -18, -16, -13, -10, -8, -5, -2,
+ 127, 126, 125, 121, 114, 107, 99, 89,
+ 79, 68, 57, 46, 35, 25, 16, 8,
+};
+
+static const u8 filter_y_vert_tap4[] = {
+ 0, -3, -6, -8, -8, -8, -8, -7,
+ -6, -5, -4, -3, -2, -1, -1, 0,
+ 127, 126, 124, 118, 111, 102, 92, 81,
+ 70, 59, 48, 37, 27, 19, 11, 5,
+ 0, 5, 11, 19, 27, 37, 48, 59,
+ 70, 81, 92, 102, 111, 118, 124, 126,
+ 0, 0, -1, -1, -2, -3, -4, -5,
+ -6, -7, -8, -8, -8, -8, -6, -3,
+};
+
+static const u8 filter_cr_horiz_tap4[] = {
+ 0, -3, -6, -8, -8, -8, -8, -7,
+ -6, -5, -4, -3, -2, -1, -1, 0,
+ 127, 126, 124, 118, 111, 102, 92, 81,
+ 70, 59, 48, 37, 27, 19, 11, 5,
+};
+
+static inline void mxr_reg_vp_filter_set(struct mxr_device *mdev,
+ int reg_id, const u8 *data, unsigned int size)
+{
+ /* assure 4-byte align */
+ BUG_ON(size & 3);
+ for (; size; size -= 4, reg_id += 4, data += 4) {
+ u32 val = (data[0] << 24) | (data[1] << 16) |
+ (data[2] << 8) | data[3];
+ vp_write(mdev, reg_id, val);
+ }
+}
+
+static void mxr_reg_vp_default_filter(struct mxr_device *mdev)
+{
+ mxr_reg_vp_filter_set(mdev, VP_POLY8_Y0_LL,
+ filter_y_horiz_tap8, sizeof filter_y_horiz_tap8);
+ mxr_reg_vp_filter_set(mdev, VP_POLY4_Y0_LL,
+ filter_y_vert_tap4, sizeof filter_y_vert_tap4);
+ mxr_reg_vp_filter_set(mdev, VP_POLY4_C0_LL,
+ filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4);
+}
+
+static void mxr_reg_mxr_dump(struct mxr_device *mdev)
+{
+#define DUMPREG(reg_id) \
+do { \
+ mxr_dbg(mdev, #reg_id " = %08x\n", \
+ (u32)readl(mdev->res.mxr_regs + reg_id)); \
+} while (0)
+
+ DUMPREG(MXR_STATUS);
+ DUMPREG(MXR_CFG);
+ DUMPREG(MXR_INT_EN);
+ DUMPREG(MXR_INT_STATUS);
+
+ DUMPREG(MXR_LAYER_CFG);
+ DUMPREG(MXR_VIDEO_CFG);
+
+ DUMPREG(MXR_GRAPHIC0_CFG);
+ DUMPREG(MXR_GRAPHIC0_BASE);
+ DUMPREG(MXR_GRAPHIC0_SPAN);
+ DUMPREG(MXR_GRAPHIC0_WH);
+ DUMPREG(MXR_GRAPHIC0_SXY);
+ DUMPREG(MXR_GRAPHIC0_DXY);
+
+ DUMPREG(MXR_GRAPHIC1_CFG);
+ DUMPREG(MXR_GRAPHIC1_BASE);
+ DUMPREG(MXR_GRAPHIC1_SPAN);
+ DUMPREG(MXR_GRAPHIC1_WH);
+ DUMPREG(MXR_GRAPHIC1_SXY);
+ DUMPREG(MXR_GRAPHIC1_DXY);
+#undef DUMPREG
+}
+
+static void mxr_reg_vp_dump(struct mxr_device *mdev)
+{
+#define DUMPREG(reg_id) \
+do { \
+ mxr_dbg(mdev, #reg_id " = %08x\n", \
+ (u32) readl(mdev->res.vp_regs + reg_id)); \
+} while (0)
+
+
+ DUMPREG(VP_ENABLE);
+ DUMPREG(VP_SRESET);
+ DUMPREG(VP_SHADOW_UPDATE);
+ DUMPREG(VP_FIELD_ID);
+ DUMPREG(VP_MODE);
+ DUMPREG(VP_IMG_SIZE_Y);
+ DUMPREG(VP_IMG_SIZE_C);
+ DUMPREG(VP_PER_RATE_CTRL);
+ DUMPREG(VP_TOP_Y_PTR);
+ DUMPREG(VP_BOT_Y_PTR);
+ DUMPREG(VP_TOP_C_PTR);
+ DUMPREG(VP_BOT_C_PTR);
+ DUMPREG(VP_ENDIAN_MODE);
+ DUMPREG(VP_SRC_H_POSITION);
+ DUMPREG(VP_SRC_V_POSITION);
+ DUMPREG(VP_SRC_WIDTH);
+ DUMPREG(VP_SRC_HEIGHT);
+ DUMPREG(VP_DST_H_POSITION);
+ DUMPREG(VP_DST_V_POSITION);
+ DUMPREG(VP_DST_WIDTH);
+ DUMPREG(VP_DST_HEIGHT);
+ DUMPREG(VP_H_RATIO);
+ DUMPREG(VP_V_RATIO);
+
+#undef DUMPREG
+}
+
+void mxr_reg_dump(struct mxr_device *mdev)
+{
+ mxr_reg_mxr_dump(mdev);
+ mxr_reg_vp_dump(mdev);
+}
+
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c
new file mode 100644
index 000000000000..e16d3a4bc1dc
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_video.c
@@ -0,0 +1,1006 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 "mixer.h"
+
+#include <media/v4l2-ioctl.h>
+#include <linux/videodev2.h>
+#include <linux/mm.h>
+#include <linux/version.h>
+#include <linux/timer.h>
+#include <media/videobuf2-dma-contig.h>
+
+static int find_reg_callback(struct device *dev, void *p)
+{
+ struct v4l2_subdev **sd = p;
+
+ *sd = dev_get_drvdata(dev);
+ /* non-zero value stops iteration */
+ return 1;
+}
+
+static struct v4l2_subdev *find_and_register_subdev(
+ struct mxr_device *mdev, char *module_name)
+{
+ struct device_driver *drv;
+ struct v4l2_subdev *sd = NULL;
+ int ret;
+
+ /* TODO: add waiting until probe is finished */
+ drv = driver_find(module_name, &platform_bus_type);
+ if (!drv) {
+ mxr_warn(mdev, "module %s is missing\n", module_name);
+ return NULL;
+ }
+ /* driver refcnt is increased, it is safe to iterate over devices */
+ ret = driver_for_each_device(drv, NULL, &sd, find_reg_callback);
+ /* ret == 0 means that find_reg_callback was never executed */
+ if (sd == NULL) {
+ mxr_warn(mdev, "module %s provides no subdev!\n", module_name);
+ goto done;
+ }
+ /* v4l2_device_register_subdev detects if sd is NULL */
+ ret = v4l2_device_register_subdev(&mdev->v4l2_dev, sd);
+ if (ret) {
+ mxr_warn(mdev, "failed to register subdev %s\n", sd->name);
+ sd = NULL;
+ }
+
+done:
+ put_driver(drv);
+ return sd;
+}
+
+int __devinit mxr_acquire_video(struct mxr_device *mdev,
+ struct mxr_output_conf *output_conf, int output_count)
+{
+ struct device *dev = mdev->dev;
+ struct v4l2_device *v4l2_dev = &mdev->v4l2_dev;
+ int i;
+ int ret = 0;
+ struct v4l2_subdev *sd;
+
+ strlcpy(v4l2_dev->name, dev_name(mdev->dev), sizeof(v4l2_dev->name));
+ /* prepare context for V4L2 device */
+ ret = v4l2_device_register(dev, v4l2_dev);
+ if (ret) {
+ mxr_err(mdev, "could not register v4l2 device.\n");
+ goto fail;
+ }
+
+ mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev);
+ if (IS_ERR_OR_NULL(mdev->alloc_ctx)) {
+ mxr_err(mdev, "could not acquire vb2 allocator\n");
+ goto fail_v4l2_dev;
+ }
+
+ /* registering outputs */
+ mdev->output_cnt = 0;
+ for (i = 0; i < output_count; ++i) {
+ struct mxr_output_conf *conf = &output_conf[i];
+ struct mxr_output *out;
+
+ sd = find_and_register_subdev(mdev, conf->module_name);
+ /* trying to register next output */
+ if (sd == NULL)
+ continue;
+ out = kzalloc(sizeof *out, GFP_KERNEL);
+ if (out == NULL) {
+ mxr_err(mdev, "no memory for '%s'\n",
+ conf->output_name);
+ ret = -ENOMEM;
+ /* registered subdevs are removed in fail_v4l2_dev */
+ goto fail_output;
+ }
+ strlcpy(out->name, conf->output_name, sizeof(out->name));
+ out->sd = sd;
+ out->cookie = conf->cookie;
+ mdev->output[mdev->output_cnt++] = out;
+ mxr_info(mdev, "added output '%s' from module '%s'\n",
+ conf->output_name, conf->module_name);
+ /* checking if maximal number of outputs is reached */
+ if (mdev->output_cnt >= MXR_MAX_OUTPUTS)
+ break;
+ }
+
+ if (mdev->output_cnt == 0) {
+ mxr_err(mdev, "failed to register any output\n");
+ ret = -ENODEV;
+ /* skipping fail_output because there is nothing to free */
+ goto fail_vb2_allocator;
+ }
+
+ return 0;
+
+fail_output:
+ /* kfree is NULL-safe */
+ for (i = 0; i < mdev->output_cnt; ++i)
+ kfree(mdev->output[i]);
+ memset(mdev->output, 0, sizeof mdev->output);
+
+fail_vb2_allocator:
+ /* freeing allocator context */
+ vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
+
+fail_v4l2_dev:
+ /* NOTE: automatically unregister all subdevs */
+ v4l2_device_unregister(v4l2_dev);
+
+fail:
+ return ret;
+}
+
+void __devexit mxr_release_video(struct mxr_device *mdev)
+{
+ int i;
+
+ /* kfree is NULL-safe */
+ for (i = 0; i < mdev->output_cnt; ++i)
+ kfree(mdev->output[i]);
+
+ vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
+ v4l2_device_unregister(&mdev->v4l2_dev);
+}
+
+static int mxr_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver);
+ strlcpy(cap->card, layer->vfd.name, sizeof cap->card);
+ sprintf(cap->bus_info, "%d", layer->idx);
+ cap->version = KERNEL_VERSION(0, 1, 0);
+ cap->capabilities = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+ return 0;
+}
+
+/* Geometry handling */
+static void mxr_layer_geo_fix(struct mxr_layer *layer)
+{
+ struct mxr_device *mdev = layer->mdev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ /* TODO: add some dirty flag to avoid unnecessary adjustments */
+ mxr_get_mbus_fmt(mdev, &mbus_fmt);
+ layer->geo.dst.full_width = mbus_fmt.width;
+ layer->geo.dst.full_height = mbus_fmt.height;
+ layer->geo.dst.field = mbus_fmt.field;
+ layer->ops.fix_geometry(layer);
+}
+
+static void mxr_layer_default_geo(struct mxr_layer *layer)
+{
+ struct mxr_device *mdev = layer->mdev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ memset(&layer->geo, 0, sizeof layer->geo);
+
+ mxr_get_mbus_fmt(mdev, &mbus_fmt);
+
+ layer->geo.dst.full_width = mbus_fmt.width;
+ layer->geo.dst.full_height = mbus_fmt.height;
+ layer->geo.dst.width = layer->geo.dst.full_width;
+ layer->geo.dst.height = layer->geo.dst.full_height;
+ layer->geo.dst.field = mbus_fmt.field;
+
+ layer->geo.src.full_width = mbus_fmt.width;
+ layer->geo.src.full_height = mbus_fmt.height;
+ layer->geo.src.width = layer->geo.src.full_width;
+ layer->geo.src.height = layer->geo.src.full_height;
+
+ layer->ops.fix_geometry(layer);
+}
+
+static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo)
+{
+ mxr_dbg(mdev, "src.full_size = (%u, %u)\n",
+ geo->src.full_width, geo->src.full_height);
+ mxr_dbg(mdev, "src.size = (%u, %u)\n",
+ geo->src.width, geo->src.height);
+ mxr_dbg(mdev, "src.offset = (%u, %u)\n",
+ geo->src.x_offset, geo->src.y_offset);
+ mxr_dbg(mdev, "dst.full_size = (%u, %u)\n",
+ geo->dst.full_width, geo->dst.full_height);
+ mxr_dbg(mdev, "dst.size = (%u, %u)\n",
+ geo->dst.width, geo->dst.height);
+ mxr_dbg(mdev, "dst.offset = (%u, %u)\n",
+ geo->dst.x_offset, geo->dst.y_offset);
+ mxr_dbg(mdev, "ratio = (%u, %u)\n",
+ geo->x_ratio, geo->y_ratio);
+}
+
+
+static const struct mxr_format *find_format_by_fourcc(
+ struct mxr_layer *layer, unsigned long fourcc);
+static const struct mxr_format *find_format_by_index(
+ struct mxr_layer *layer, unsigned long index);
+
+static int mxr_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ const struct mxr_format *fmt;
+
+ mxr_dbg(mdev, "%s\n", __func__);
+ fmt = find_format_by_index(layer, f->index);
+ if (fmt == NULL)
+ return -EINVAL;
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int mxr_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ const struct mxr_format *fmt;
+ struct v4l2_pix_format_mplane *pix;
+ struct mxr_device *mdev = layer->mdev;
+ struct mxr_geometry *geo = &layer->geo;
+
+ mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__);
+
+ pix = &f->fmt.pix_mp;
+ fmt = find_format_by_fourcc(layer, pix->pixelformat);
+ if (fmt == NULL) {
+ mxr_warn(mdev, "not recognized fourcc: %08x\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+ layer->fmt = fmt;
+ geo->src.full_width = pix->width;
+ geo->src.width = pix->width;
+ geo->src.full_height = pix->height;
+ geo->src.height = pix->height;
+ /* assure consistency of geometry */
+ mxr_layer_geo_fix(layer);
+ mxr_dbg(mdev, "width=%u height=%u span=%u\n",
+ geo->src.width, geo->src.height, geo->src.full_width);
+
+ return 0;
+}
+
+static unsigned int divup(unsigned int divident, unsigned int divisor)
+{
+ return (divident + divisor - 1) / divisor;
+}
+
+unsigned long mxr_get_plane_size(const struct mxr_block *blk,
+ unsigned int width, unsigned int height)
+{
+ unsigned int bl_width = divup(width, blk->width);
+ unsigned int bl_height = divup(height, blk->height);
+
+ return bl_width * bl_height * blk->size;
+}
+
+static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes,
+ const struct mxr_format *fmt, u32 width, u32 height)
+{
+ int i;
+
+ memset(planes, 0, sizeof(*planes) * fmt->num_subframes);
+ for (i = 0; i < fmt->num_planes; ++i) {
+ struct v4l2_plane_pix_format *plane = planes
+ + fmt->plane2subframe[i];
+ const struct mxr_block *blk = &fmt->plane[i];
+ u32 bl_width = divup(width, blk->width);
+ u32 bl_height = divup(height, blk->height);
+ u32 sizeimage = bl_width * bl_height * blk->size;
+ u16 bytesperline = bl_width * blk->size / blk->height;
+
+ plane->sizeimage += sizeimage;
+ plane->bytesperline = max(plane->bytesperline, bytesperline);
+ }
+}
+
+static int mxr_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ pix->width = layer->geo.src.full_width;
+ pix->height = layer->geo.src.full_height;
+ pix->field = V4L2_FIELD_NONE;
+ pix->pixelformat = layer->fmt->fourcc;
+ pix->colorspace = layer->fmt->colorspace;
+ mxr_mplane_fill(pix->plane_fmt, layer->fmt, pix->width, pix->height);
+
+ return 0;
+}
+
+static inline struct mxr_crop *choose_crop_by_type(struct mxr_geometry *geo,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ return &geo->dst;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ return &geo->src;
+ default:
+ return NULL;
+ }
+}
+
+static int mxr_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_crop *crop;
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ crop = choose_crop_by_type(&layer->geo, a->type);
+ if (crop == NULL)
+ return -EINVAL;
+ mxr_layer_geo_fix(layer);
+ a->c.left = crop->x_offset;
+ a->c.top = crop->y_offset;
+ a->c.width = crop->width;
+ a->c.height = crop->height;
+ return 0;
+}
+
+static int mxr_s_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_crop *crop;
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ crop = choose_crop_by_type(&layer->geo, a->type);
+ if (crop == NULL)
+ return -EINVAL;
+ crop->x_offset = a->c.left;
+ crop->y_offset = a->c.top;
+ crop->width = a->c.width;
+ crop->height = a->c.height;
+ mxr_layer_geo_fix(layer);
+ return 0;
+}
+
+static int mxr_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_crop *crop;
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ crop = choose_crop_by_type(&layer->geo, a->type);
+ if (crop == NULL)
+ return -EINVAL;
+ mxr_layer_geo_fix(layer);
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = crop->full_width;
+ a->bounds.top = crop->full_height;
+ a->defrect = a->bounds;
+ /* setting pixel aspect to 1/1 */
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+ return 0;
+}
+
+static int mxr_enum_dv_presets(struct file *file, void *fh,
+ struct v4l2_dv_enum_preset *preset)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_presets, preset);
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_s_dv_preset(struct file *file, void *fh,
+ struct v4l2_dv_preset *preset)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+
+ /* preset change cannot be done while there is an entity
+ * dependant on output configuration
+ */
+ if (mdev->n_output > 0) {
+ mutex_unlock(&mdev->mutex);
+ return -EBUSY;
+ }
+
+ ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset);
+
+ mutex_unlock(&mdev->mutex);
+
+ /* any failure should return EINVAL according to V4L2 doc */
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_g_dv_preset(struct file *file, void *fh,
+ struct v4l2_dv_preset *preset)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_preset, preset);
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+
+ /* standard change cannot be done while there is an entity
+ * dependant on output configuration
+ */
+ if (mdev->n_output > 0) {
+ mutex_unlock(&mdev->mutex);
+ return -EBUSY;
+ }
+
+ ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, *norm);
+
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_g_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, g_std_output, norm);
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ struct mxr_output *out;
+ struct v4l2_subdev *sd;
+
+ if (a->index >= mdev->output_cnt)
+ return -EINVAL;
+ out = mdev->output[a->index];
+ BUG_ON(out == NULL);
+ sd = out->sd;
+ strlcpy(a->name, out->name, sizeof(a->name));
+
+ /* try to obtain supported tv norms */
+ v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std);
+ a->capabilities = 0;
+ if (sd->ops->video && sd->ops->video->s_dv_preset)
+ a->capabilities |= V4L2_OUT_CAP_PRESETS;
+ if (sd->ops->video && sd->ops->video->s_std_output)
+ a->capabilities |= V4L2_OUT_CAP_STD;
+ a->type = V4L2_OUTPUT_TYPE_ANALOG;
+
+ return 0;
+}
+
+static int mxr_s_output(struct file *file, void *fh, unsigned int i)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret = 0;
+
+ if (i >= mdev->output_cnt || mdev->output[i] == NULL)
+ return -EINVAL;
+
+ mutex_lock(&mdev->mutex);
+ if (mdev->n_output > 0) {
+ ret = -EBUSY;
+ goto done;
+ }
+ mdev->current_output = i;
+ vfd->tvnorms = 0;
+ v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output,
+ &vfd->tvnorms);
+ mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms);
+
+done:
+ mutex_unlock(&mdev->mutex);
+ return ret;
+}
+
+static int mxr_g_output(struct file *file, void *fh, unsigned int *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+
+ mutex_lock(&mdev->mutex);
+ *p = mdev->current_output;
+ mutex_unlock(&mdev->mutex);
+
+ return 0;
+}
+
+static int mxr_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_reqbufs(&layer->vb_queue, p);
+}
+
+static int mxr_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_querybuf(&layer->vb_queue, p);
+}
+
+static int mxr_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d(%d)\n", __func__, __LINE__, p->index);
+ return vb2_qbuf(&layer->vb_queue, p);
+}
+
+static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK);
+}
+
+static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_streamon(&layer->vb_queue, i);
+}
+
+static int mxr_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_streamoff(&layer->vb_queue, i);
+}
+
+static const struct v4l2_ioctl_ops mxr_ioctl_ops = {
+ .vidioc_querycap = mxr_querycap,
+ /* format handling */
+ .vidioc_enum_fmt_vid_out = mxr_enum_fmt,
+ .vidioc_s_fmt_vid_out_mplane = mxr_s_fmt,
+ .vidioc_g_fmt_vid_out_mplane = mxr_g_fmt,
+ /* buffer control */
+ .vidioc_reqbufs = mxr_reqbufs,
+ .vidioc_querybuf = mxr_querybuf,
+ .vidioc_qbuf = mxr_qbuf,
+ .vidioc_dqbuf = mxr_dqbuf,
+ /* Streaming control */
+ .vidioc_streamon = mxr_streamon,
+ .vidioc_streamoff = mxr_streamoff,
+ /* Preset functions */
+ .vidioc_enum_dv_presets = mxr_enum_dv_presets,
+ .vidioc_s_dv_preset = mxr_s_dv_preset,
+ .vidioc_g_dv_preset = mxr_g_dv_preset,
+ /* analog TV standard functions */
+ .vidioc_s_std = mxr_s_std,
+ .vidioc_g_std = mxr_g_std,
+ /* Output handling */
+ .vidioc_enum_output = mxr_enum_output,
+ .vidioc_s_output = mxr_s_output,
+ .vidioc_g_output = mxr_g_output,
+ /* Crop ioctls */
+ .vidioc_g_crop = mxr_g_crop,
+ .vidioc_s_crop = mxr_s_crop,
+ .vidioc_cropcap = mxr_cropcap,
+};
+
+static int mxr_video_open(struct file *file)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret = 0;
+
+ mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__);
+ /* assure device probe is finished */
+ wait_for_device_probe();
+ /* creating context for file descriptor */
+ ret = v4l2_fh_open(file);
+ if (ret) {
+ mxr_err(mdev, "v4l2_fh_open failed\n");
+ return ret;
+ }
+
+ /* leaving if layer is already initialized */
+ if (!v4l2_fh_is_singular_file(file))
+ return 0;
+
+ /* FIXME: should power be enabled on open? */
+ ret = mxr_power_get(mdev);
+ if (ret) {
+ mxr_err(mdev, "power on failed\n");
+ goto fail_fh_open;
+ }
+
+ ret = vb2_queue_init(&layer->vb_queue);
+ if (ret != 0) {
+ mxr_err(mdev, "failed to initialize vb2 queue\n");
+ goto fail_power;
+ }
+ /* set default format, first on the list */
+ layer->fmt = layer->fmt_array[0];
+ /* setup default geometry */
+ mxr_layer_default_geo(layer);
+
+ return 0;
+
+fail_power:
+ mxr_power_put(mdev);
+
+fail_fh_open:
+ v4l2_fh_release(file);
+
+ return ret;
+}
+
+static unsigned int
+mxr_video_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ return vb2_poll(&layer->vb_queue, file, wait);
+}
+
+static int mxr_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ return vb2_mmap(&layer->vb_queue, vma);
+}
+
+static int mxr_video_release(struct file *file)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ if (v4l2_fh_is_singular_file(file)) {
+ vb2_queue_release(&layer->vb_queue);
+ mxr_power_put(layer->mdev);
+ }
+ v4l2_fh_release(file);
+ return 0;
+}
+
+static const struct v4l2_file_operations mxr_fops = {
+ .owner = THIS_MODULE,
+ .open = mxr_video_open,
+ .poll = mxr_video_poll,
+ .mmap = mxr_video_mmap,
+ .release = mxr_video_release,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[],
+ void *alloc_ctxs[])
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+ const struct mxr_format *fmt = layer->fmt;
+ int i;
+ struct mxr_device *mdev = layer->mdev;
+ struct v4l2_plane_pix_format planes[3];
+
+ mxr_dbg(mdev, "%s\n", __func__);
+ /* checking if format was configured */
+ if (fmt == NULL)
+ return -EINVAL;
+ mxr_dbg(mdev, "fmt = %s\n", fmt->name);
+ mxr_mplane_fill(planes, fmt, layer->geo.src.full_width,
+ layer->geo.src.full_height);
+
+ *nplanes = fmt->num_subframes;
+ for (i = 0; i < fmt->num_subframes; ++i) {
+ alloc_ctxs[i] = layer->mdev->alloc_ctx;
+ sizes[i] = PAGE_ALIGN(planes[i].sizeimage);
+ mxr_dbg(mdev, "size[%d] = %08lx\n", i, sizes[i]);
+ }
+
+ if (*nbuffers == 0)
+ *nbuffers = 1;
+
+ return 0;
+}
+
+static void buf_queue(struct vb2_buffer *vb)
+{
+ struct mxr_buffer *buffer = container_of(vb, struct mxr_buffer, vb);
+ struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue);
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&layer->enq_slock, flags);
+ list_add_tail(&buffer->list, &layer->enq_list);
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ mxr_dbg(mdev, "queuing buffer\n");
+}
+
+static void wait_lock(struct vb2_queue *vq)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+
+ mxr_dbg(layer->mdev, "%s\n", __func__);
+ mutex_lock(&layer->mutex);
+}
+
+static void wait_unlock(struct vb2_queue *vq)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+
+ mxr_dbg(layer->mdev, "%s\n", __func__);
+ mutex_unlock(&layer->mutex);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+
+ mxr_dbg(mdev, "%s\n", __func__);
+
+ if (count == 0) {
+ mxr_dbg(mdev, "no output buffers queued\n");
+ return -EINVAL;
+ }
+
+ /* block any changes in output configuration */
+ mxr_output_get(mdev);
+
+ /* update layers geometry */
+ mxr_layer_geo_fix(layer);
+ mxr_geometry_dump(mdev, &layer->geo);
+
+ layer->ops.format_set(layer);
+ /* enabling layer in hardware */
+ spin_lock_irqsave(&layer->enq_slock, flags);
+ layer->state = MXR_LAYER_STREAMING;
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ layer->ops.stream_set(layer, MXR_ENABLE);
+ mxr_streamer_get(mdev);
+
+ return 0;
+}
+
+static void mxr_watchdog(unsigned long arg)
+{
+ struct mxr_layer *layer = (struct mxr_layer *) arg;
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+
+ mxr_err(mdev, "watchdog fired for layer %s\n", layer->vfd.name);
+
+ spin_lock_irqsave(&layer->enq_slock, flags);
+
+ if (layer->update_buf == layer->shadow_buf)
+ layer->update_buf = NULL;
+ if (layer->update_buf) {
+ vb2_buffer_done(&layer->update_buf->vb, VB2_BUF_STATE_ERROR);
+ layer->update_buf = NULL;
+ }
+ if (layer->shadow_buf) {
+ vb2_buffer_done(&layer->shadow_buf->vb, VB2_BUF_STATE_ERROR);
+ layer->shadow_buf = NULL;
+ }
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+}
+
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+ struct timer_list watchdog;
+ struct mxr_buffer *buf, *buf_tmp;
+
+ mxr_dbg(mdev, "%s\n", __func__);
+
+ spin_lock_irqsave(&layer->enq_slock, flags);
+
+ /* reset list */
+ layer->state = MXR_LAYER_STREAMING_FINISH;
+
+ /* set all buffer to be done */
+ list_for_each_entry_safe(buf, buf_tmp, &layer->enq_list, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ /* give 1 seconds to complete to complete last buffers */
+ setup_timer_on_stack(&watchdog, mxr_watchdog,
+ (unsigned long)layer);
+ mod_timer(&watchdog, jiffies + msecs_to_jiffies(1000));
+
+ /* wait until all buffers are goes to done state */
+ vb2_wait_for_all_buffers(vq);
+
+ /* stop timer if all synchronization is done */
+ del_timer_sync(&watchdog);
+ destroy_timer_on_stack(&watchdog);
+
+ /* stopping hardware */
+ spin_lock_irqsave(&layer->enq_slock, flags);
+ layer->state = MXR_LAYER_IDLE;
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ /* disabling layer in hardware */
+ layer->ops.stream_set(layer, MXR_DISABLE);
+ /* remove one streamer */
+ mxr_streamer_put(mdev);
+ /* allow changes in output configuration */
+ mxr_output_put(mdev);
+ return 0;
+}
+
+static struct vb2_ops mxr_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_queue = buf_queue,
+ .wait_prepare = wait_unlock,
+ .wait_finish = wait_lock,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+/* FIXME: try to put this functions to mxr_base_layer_create */
+int mxr_base_layer_register(struct mxr_layer *layer)
+{
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ ret = video_register_device(&layer->vfd, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ mxr_err(mdev, "failed to register video device\n");
+ else
+ mxr_info(mdev, "registered layer %s as /dev/video%d\n",
+ layer->vfd.name, layer->vfd.num);
+ return ret;
+}
+
+void mxr_base_layer_unregister(struct mxr_layer *layer)
+{
+ video_unregister_device(&layer->vfd);
+}
+
+void mxr_layer_release(struct mxr_layer *layer)
+{
+ if (layer->ops.release)
+ layer->ops.release(layer);
+}
+
+void mxr_base_layer_release(struct mxr_layer *layer)
+{
+ kfree(layer);
+}
+
+static void mxr_vfd_release(struct video_device *vdev)
+{
+ printk(KERN_INFO "video device release\n");
+}
+
+struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
+ int idx, char *name, struct mxr_layer_ops *ops)
+{
+ struct mxr_layer *layer;
+
+ layer = kzalloc(sizeof *layer, GFP_KERNEL);
+ if (layer == NULL) {
+ mxr_err(mdev, "not enough memory for layer.\n");
+ goto fail;
+ }
+
+ layer->mdev = mdev;
+ layer->idx = idx;
+ layer->ops = *ops;
+
+ spin_lock_init(&layer->enq_slock);
+ INIT_LIST_HEAD(&layer->enq_list);
+ mutex_init(&layer->mutex);
+
+ layer->vfd = (struct video_device) {
+ .minor = -1,
+ .release = mxr_vfd_release,
+ .fops = &mxr_fops,
+ .ioctl_ops = &mxr_ioctl_ops,
+ };
+ strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name));
+ /* let framework control PRIORITY */
+ set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags);
+
+ video_set_drvdata(&layer->vfd, layer);
+ layer->vfd.lock = &layer->mutex;
+ layer->vfd.v4l2_dev = &mdev->v4l2_dev;
+
+ layer->vb_queue = (struct vb2_queue) {
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .io_modes = VB2_MMAP | VB2_USERPTR,
+ .drv_priv = layer,
+ .buf_struct_size = sizeof(struct mxr_buffer),
+ .ops = &mxr_video_qops,
+ .mem_ops = &vb2_dma_contig_memops,
+ };
+
+ return layer;
+
+fail:
+ return NULL;
+}
+
+static const struct mxr_format *find_format_by_fourcc(
+ struct mxr_layer *layer, unsigned long fourcc)
+{
+ int i;
+
+ for (i = 0; i < layer->fmt_array_size; ++i)
+ if (layer->fmt_array[i]->fourcc == fourcc)
+ return layer->fmt_array[i];
+ return NULL;
+}
+
+static const struct mxr_format *find_format_by_index(
+ struct mxr_layer *layer, unsigned long index)
+{
+ if (index >= layer->fmt_array_size)
+ return NULL;
+ return layer->fmt_array[index];
+}
+
diff --git a/drivers/media/video/s5p-tv/mixer_vp_layer.c b/drivers/media/video/s5p-tv/mixer_vp_layer.c
new file mode 100644
index 000000000000..f3bb2e34cb51
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_vp_layer.c
@@ -0,0 +1,211 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include "regs-vp.h"
+
+#include <media/videobuf2-dma-contig.h>
+
+/* FORMAT DEFINITIONS */
+static const struct mxr_format mxr_fmt_nv12 = {
+ .name = "NV12",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .num_planes = 2,
+ .plane = {
+ { .width = 1, .height = 1, .size = 1 },
+ { .width = 2, .height = 2, .size = 2 },
+ },
+ .num_subframes = 1,
+ .cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR,
+};
+
+static const struct mxr_format mxr_fmt_nv21 = {
+ .name = "NV21",
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .num_planes = 2,
+ .plane = {
+ { .width = 1, .height = 1, .size = 1 },
+ { .width = 2, .height = 2, .size = 2 },
+ },
+ .num_subframes = 1,
+ .cookie = VP_MODE_NV21 | VP_MODE_MEM_LINEAR,
+};
+
+static const struct mxr_format mxr_fmt_nv12m = {
+ .name = "NV12 (mplane)",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .num_planes = 2,
+ .plane = {
+ { .width = 1, .height = 1, .size = 1 },
+ { .width = 2, .height = 2, .size = 2 },
+ },
+ .num_subframes = 2,
+ .plane2subframe = {0, 1},
+ .cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR,
+};
+
+static const struct mxr_format mxr_fmt_nv12mt = {
+ .name = "NV12 tiled (mplane)",
+ .fourcc = V4L2_PIX_FMT_NV12MT,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .num_planes = 2,
+ .plane = {
+ { .width = 128, .height = 32, .size = 4096 },
+ { .width = 128, .height = 32, .size = 2048 },
+ },
+ .num_subframes = 2,
+ .plane2subframe = {0, 1},
+ .cookie = VP_MODE_NV12 | VP_MODE_MEM_TILED,
+};
+
+static const struct mxr_format *mxr_video_format[] = {
+ &mxr_fmt_nv12,
+ &mxr_fmt_nv21,
+ &mxr_fmt_nv12m,
+ &mxr_fmt_nv12mt,
+};
+
+/* AUXILIARY CALLBACKS */
+
+static void mxr_vp_layer_release(struct mxr_layer *layer)
+{
+ mxr_base_layer_unregister(layer);
+ mxr_base_layer_release(layer);
+}
+
+static void mxr_vp_buffer_set(struct mxr_layer *layer,
+ struct mxr_buffer *buf)
+{
+ dma_addr_t luma_addr[2] = {0, 0};
+ dma_addr_t chroma_addr[2] = {0, 0};
+
+ if (buf == NULL) {
+ mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr);
+ return;
+ }
+ luma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb, 0);
+ if (layer->fmt->num_subframes == 2) {
+ chroma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb, 1);
+ } else {
+ /* FIXME: mxr_get_plane_size compute integer division,
+ * which is slow and should not be performed in interrupt */
+ chroma_addr[0] = luma_addr[0] + mxr_get_plane_size(
+ &layer->fmt->plane[0], layer->geo.src.full_width,
+ layer->geo.src.full_height);
+ }
+ if (layer->fmt->cookie & VP_MODE_MEM_TILED) {
+ luma_addr[1] = luma_addr[0] + 0x40;
+ chroma_addr[1] = chroma_addr[0] + 0x40;
+ } else {
+ luma_addr[1] = luma_addr[0] + layer->geo.src.full_width;
+ chroma_addr[1] = chroma_addr[0];
+ }
+ mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr);
+}
+
+static void mxr_vp_stream_set(struct mxr_layer *layer, int en)
+{
+ mxr_reg_vp_layer_stream(layer->mdev, en);
+}
+
+static void mxr_vp_format_set(struct mxr_layer *layer)
+{
+ mxr_reg_vp_format(layer->mdev, layer->fmt, &layer->geo);
+}
+
+static void mxr_vp_fix_geometry(struct mxr_layer *layer)
+{
+ struct mxr_geometry *geo = &layer->geo;
+
+ /* align horizontal size to 8 pixels */
+ geo->src.full_width = ALIGN(geo->src.full_width, 8);
+ /* limit to boundary size */
+ geo->src.full_width = clamp_val(geo->src.full_width, 8, 8192);
+ geo->src.full_height = clamp_val(geo->src.full_height, 1, 8192);
+ geo->src.width = clamp_val(geo->src.width, 32, geo->src.full_width);
+ geo->src.width = min(geo->src.width, 2047U);
+ geo->src.height = clamp_val(geo->src.height, 4, geo->src.full_height);
+ geo->src.height = min(geo->src.height, 2047U);
+
+ /* setting size of output window */
+ geo->dst.width = clamp_val(geo->dst.width, 8, geo->dst.full_width);
+ geo->dst.height = clamp_val(geo->dst.height, 1, geo->dst.full_height);
+
+ /* ensure that scaling is in range 1/4x to 16x */
+ if (geo->src.width >= 4 * geo->dst.width)
+ geo->src.width = 4 * geo->dst.width;
+ if (geo->dst.width >= 16 * geo->src.width)
+ geo->dst.width = 16 * geo->src.width;
+ if (geo->src.height >= 4 * geo->dst.height)
+ geo->src.height = 4 * geo->dst.height;
+ if (geo->dst.height >= 16 * geo->src.height)
+ geo->dst.height = 16 * geo->src.height;
+
+ /* setting scaling ratio */
+ geo->x_ratio = (geo->src.width << 16) / geo->dst.width;
+ geo->y_ratio = (geo->src.height << 16) / geo->dst.height;
+
+ /* adjust offsets */
+ geo->src.x_offset = min(geo->src.x_offset,
+ geo->src.full_width - geo->src.width);
+ geo->src.y_offset = min(geo->src.y_offset,
+ geo->src.full_height - geo->src.height);
+ geo->dst.x_offset = min(geo->dst.x_offset,
+ geo->dst.full_width - geo->dst.width);
+ geo->dst.y_offset = min(geo->dst.y_offset,
+ geo->dst.full_height - geo->dst.height);
+}
+
+/* PUBLIC API */
+
+struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx)
+{
+ struct mxr_layer *layer;
+ int ret;
+ struct mxr_layer_ops ops = {
+ .release = mxr_vp_layer_release,
+ .buffer_set = mxr_vp_buffer_set,
+ .stream_set = mxr_vp_stream_set,
+ .format_set = mxr_vp_format_set,
+ .fix_geometry = mxr_vp_fix_geometry,
+ };
+ char name[32];
+
+ sprintf(name, "video%d", idx);
+
+ layer = mxr_base_layer_create(mdev, idx, name, &ops);
+ if (layer == NULL) {
+ mxr_err(mdev, "failed to initialize layer(%d) base\n", idx);
+ goto fail;
+ }
+
+ layer->fmt_array = mxr_video_format;
+ layer->fmt_array_size = ARRAY_SIZE(mxr_video_format);
+
+ ret = mxr_base_layer_register(layer);
+ if (ret)
+ goto fail_layer;
+
+ return layer;
+
+fail_layer:
+ mxr_base_layer_release(layer);
+
+fail:
+ return NULL;
+}
+
diff --git a/drivers/media/video/s5p-tv/regs-hdmi.h b/drivers/media/video/s5p-tv/regs-hdmi.h
new file mode 100644
index 000000000000..33247d13e4c0
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-hdmi.h
@@ -0,0 +1,145 @@
+/* linux/arch/arm/mach-exynos4/include/mach/regs-hdmi.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * HDMI register header file for Samsung TVOUT driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef SAMSUNG_REGS_HDMI_H
+#define SAMSUNG_REGS_HDMI_H
+
+/*
+ * Register part
+*/
+
+#define HDMI_CTRL_BASE(x) ((x) + 0x00000000)
+#define HDMI_CORE_BASE(x) ((x) + 0x00010000)
+#define HDMI_TG_BASE(x) ((x) + 0x00050000)
+
+/* Control registers */
+#define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000)
+#define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004)
+#define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C)
+#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014)
+#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018)
+#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C)
+#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020)
+
+/* Core registers */
+#define HDMI_CON_0 HDMI_CORE_BASE(0x0000)
+#define HDMI_CON_1 HDMI_CORE_BASE(0x0004)
+#define HDMI_CON_2 HDMI_CORE_BASE(0x0008)
+#define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010)
+#define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014)
+#define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020)
+#define HDMI_HPD HDMI_CORE_BASE(0x0030)
+#define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040)
+#define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050)
+#define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054)
+#define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058)
+#define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0)
+#define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4)
+#define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0)
+#define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4)
+#define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8)
+#define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0)
+#define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4)
+#define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8)
+#define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4)
+#define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8)
+#define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110)
+#define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114)
+#define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118)
+#define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120)
+#define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124)
+#define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128)
+#define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130)
+#define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134)
+#define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138)
+#define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140)
+#define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144)
+#define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148)
+#define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150)
+#define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154)
+#define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158)
+#define HDMI_AVI_CON HDMI_CORE_BASE(0x0300)
+#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n))
+#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0)
+#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4)
+#define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8)
+
+/* Timing generator registers */
+#define HDMI_TG_CMD HDMI_TG_BASE(0x0000)
+#define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018)
+#define HDMI_TG_H_FSZ_H HDMI_TG_BASE(0x001C)
+#define HDMI_TG_HACT_ST_L HDMI_TG_BASE(0x0020)
+#define HDMI_TG_HACT_ST_H HDMI_TG_BASE(0x0024)
+#define HDMI_TG_HACT_SZ_L HDMI_TG_BASE(0x0028)
+#define HDMI_TG_HACT_SZ_H HDMI_TG_BASE(0x002C)
+#define HDMI_TG_V_FSZ_L HDMI_TG_BASE(0x0030)
+#define HDMI_TG_V_FSZ_H HDMI_TG_BASE(0x0034)
+#define HDMI_TG_VSYNC_L HDMI_TG_BASE(0x0038)
+#define HDMI_TG_VSYNC_H HDMI_TG_BASE(0x003C)
+#define HDMI_TG_VSYNC2_L HDMI_TG_BASE(0x0040)
+#define HDMI_TG_VSYNC2_H HDMI_TG_BASE(0x0044)
+#define HDMI_TG_VACT_ST_L HDMI_TG_BASE(0x0048)
+#define HDMI_TG_VACT_ST_H HDMI_TG_BASE(0x004C)
+#define HDMI_TG_VACT_SZ_L HDMI_TG_BASE(0x0050)
+#define HDMI_TG_VACT_SZ_H HDMI_TG_BASE(0x0054)
+#define HDMI_TG_FIELD_CHG_L HDMI_TG_BASE(0x0058)
+#define HDMI_TG_FIELD_CHG_H HDMI_TG_BASE(0x005C)
+#define HDMI_TG_VACT_ST2_L HDMI_TG_BASE(0x0060)
+#define HDMI_TG_VACT_ST2_H HDMI_TG_BASE(0x0064)
+#define HDMI_TG_VSYNC_TOP_HDMI_L HDMI_TG_BASE(0x0078)
+#define HDMI_TG_VSYNC_TOP_HDMI_H HDMI_TG_BASE(0x007C)
+#define HDMI_TG_VSYNC_BOT_HDMI_L HDMI_TG_BASE(0x0080)
+#define HDMI_TG_VSYNC_BOT_HDMI_H HDMI_TG_BASE(0x0084)
+#define HDMI_TG_FIELD_TOP_HDMI_L HDMI_TG_BASE(0x0088)
+#define HDMI_TG_FIELD_TOP_HDMI_H HDMI_TG_BASE(0x008C)
+#define HDMI_TG_FIELD_BOT_HDMI_L HDMI_TG_BASE(0x0090)
+#define HDMI_TG_FIELD_BOT_HDMI_H HDMI_TG_BASE(0x0094)
+
+/*
+ * Bit definition part
+ */
+
+/* HDMI_INTC_CON */
+#define HDMI_INTC_EN_GLOBAL (1 << 6)
+#define HDMI_INTC_EN_HPD_PLUG (1 << 3)
+#define HDMI_INTC_EN_HPD_UNPLUG (1 << 2)
+
+/* HDMI_INTC_FLAG */
+#define HDMI_INTC_FLAG_HPD_PLUG (1 << 3)
+#define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2)
+
+/* HDMI_PHY_RSTOUT */
+#define HDMI_PHY_SW_RSTOUT (1 << 0)
+
+/* HDMI_CORE_RSTOUT */
+#define HDMI_CORE_SW_RSTOUT (1 << 0)
+
+/* HDMI_CON_0 */
+#define HDMI_BLUE_SCR_EN (1 << 5)
+#define HDMI_EN (1 << 0)
+
+/* HDMI_CON_2 */
+#define HDMI_DVI_PERAMBLE_EN (1 << 5)
+#define HDMI_DVI_BAND_EN (1 << 1)
+
+/* HDMI_PHY_STATUS */
+#define HDMI_PHY_STATUS_READY (1 << 0)
+
+/* HDMI_MODE_SEL */
+#define HDMI_MODE_HDMI_EN (1 << 1)
+#define HDMI_MODE_DVI_EN (1 << 0)
+#define HDMI_MODE_MASK (3 << 0)
+
+/* HDMI_TG_CMD */
+#define HDMI_TG_EN (1 << 0)
+
+#endif /* SAMSUNG_REGS_HDMI_H */
diff --git a/drivers/media/video/s5p-tv/regs-mixer.h b/drivers/media/video/s5p-tv/regs-mixer.h
new file mode 100644
index 000000000000..158abb43d0a4
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-mixer.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Mixer register header file for Samsung Mixer driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef SAMSUNG_REGS_MIXER_H
+#define SAMSUNG_REGS_MIXER_H
+
+/*
+ * Register part
+ */
+#define MXR_STATUS 0x0000
+#define MXR_CFG 0x0004
+#define MXR_INT_EN 0x0008
+#define MXR_INT_STATUS 0x000C
+#define MXR_LAYER_CFG 0x0010
+#define MXR_VIDEO_CFG 0x0014
+#define MXR_GRAPHIC0_CFG 0x0020
+#define MXR_GRAPHIC0_BASE 0x0024
+#define MXR_GRAPHIC0_SPAN 0x0028
+#define MXR_GRAPHIC0_SXY 0x002C
+#define MXR_GRAPHIC0_WH 0x0030
+#define MXR_GRAPHIC0_DXY 0x0034
+#define MXR_GRAPHIC0_BLANK 0x0038
+#define MXR_GRAPHIC1_CFG 0x0040
+#define MXR_GRAPHIC1_BASE 0x0044
+#define MXR_GRAPHIC1_SPAN 0x0048
+#define MXR_GRAPHIC1_SXY 0x004C
+#define MXR_GRAPHIC1_WH 0x0050
+#define MXR_GRAPHIC1_DXY 0x0054
+#define MXR_GRAPHIC1_BLANK 0x0058
+#define MXR_BG_CFG 0x0060
+#define MXR_BG_COLOR0 0x0064
+#define MXR_BG_COLOR1 0x0068
+#define MXR_BG_COLOR2 0x006C
+
+/* for parametrized access to layer registers */
+#define MXR_GRAPHIC_CFG(i) (0x0020 + (i) * 0x20)
+#define MXR_GRAPHIC_BASE(i) (0x0024 + (i) * 0x20)
+#define MXR_GRAPHIC_SPAN(i) (0x0028 + (i) * 0x20)
+#define MXR_GRAPHIC_SXY(i) (0x002C + (i) * 0x20)
+#define MXR_GRAPHIC_WH(i) (0x0030 + (i) * 0x20)
+#define MXR_GRAPHIC_DXY(i) (0x0034 + (i) * 0x20)
+
+/*
+ * Bit definition part
+ */
+
+/* generates mask for range of bits */
+#define MXR_MASK(high_bit, low_bit) \
+ (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
+
+#define MXR_MASK_VAL(val, high_bit, low_bit) \
+ (((val) << (low_bit)) & MXR_MASK(high_bit, low_bit))
+
+/* bits for MXR_STATUS */
+#define MXR_STATUS_16_BURST (1 << 7)
+#define MXR_STATUS_BURST_MASK (1 << 7)
+#define MXR_STATUS_SYNC_ENABLE (1 << 2)
+#define MXR_STATUS_REG_RUN (1 << 0)
+
+/* bits for MXR_CFG */
+#define MXR_CFG_OUT_YUV444 (0 << 8)
+#define MXR_CFG_OUT_RGB888 (1 << 8)
+#define MXR_CFG_OUT_MASK (1 << 8)
+#define MXR_CFG_DST_SDO (0 << 7)
+#define MXR_CFG_DST_HDMI (1 << 7)
+#define MXR_CFG_DST_MASK (1 << 7)
+#define MXR_CFG_SCAN_HD_720 (0 << 6)
+#define MXR_CFG_SCAN_HD_1080 (1 << 6)
+#define MXR_CFG_GRP1_ENABLE (1 << 5)
+#define MXR_CFG_GRP0_ENABLE (1 << 4)
+#define MXR_CFG_VP_ENABLE (1 << 3)
+#define MXR_CFG_SCAN_INTERLACE (0 << 2)
+#define MXR_CFG_SCAN_PROGRASSIVE (1 << 2)
+#define MXR_CFG_SCAN_NTSC (0 << 1)
+#define MXR_CFG_SCAN_PAL (1 << 1)
+#define MXR_CFG_SCAN_SD (0 << 0)
+#define MXR_CFG_SCAN_HD (1 << 0)
+#define MXR_CFG_SCAN_MASK 0x47
+
+/* bits for MXR_GRAPHICn_CFG */
+#define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21)
+#define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20)
+#define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8)
+#define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0)
+#define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0)
+
+/* bits for MXR_GRAPHICn_WH */
+#define MXR_GRP_WH_H_SCALE(x) MXR_MASK_VAL(x, 28, 28)
+#define MXR_GRP_WH_V_SCALE(x) MXR_MASK_VAL(x, 12, 12)
+#define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16)
+#define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0)
+
+/* bits for MXR_GRAPHICn_SXY */
+#define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16)
+#define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0)
+
+/* bits for MXR_GRAPHICn_DXY */
+#define MXR_GRP_DXY_DX(x) MXR_MASK_VAL(x, 26, 16)
+#define MXR_GRP_DXY_DY(x) MXR_MASK_VAL(x, 10, 0)
+
+/* bits for MXR_INT_EN */
+#define MXR_INT_EN_VSYNC (1 << 11)
+#define MXR_INT_EN_ALL (0x0f << 8)
+
+/* bit for MXR_INT_STATUS */
+#define MXR_INT_CLEAR_VSYNC (1 << 11)
+#define MXR_INT_STATUS_VSYNC (1 << 0)
+
+/* bit for MXR_LAYER_CFG */
+#define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8)
+#define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4)
+#define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0)
+
+#endif /* SAMSUNG_REGS_MIXER_H */
+
diff --git a/drivers/media/video/s5p-tv/regs-sdo.h b/drivers/media/video/s5p-tv/regs-sdo.h
new file mode 100644
index 000000000000..7f7c2b8ac140
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-sdo.h
@@ -0,0 +1,63 @@
+/* drivers/media/video/s5p-tv/regs-sdo.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * SDO register description file
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SAMSUNG_REGS_SDO_H
+#define SAMSUNG_REGS_SDO_H
+
+/*
+ * Register part
+ */
+
+#define SDO_CLKCON 0x0000
+#define SDO_CONFIG 0x0008
+#define SDO_VBI 0x0014
+#define SDO_DAC 0x003C
+#define SDO_CCCON 0x0180
+#define SDO_IRQ 0x0280
+#define SDO_IRQMASK 0x0284
+#define SDO_VERSION 0x03D8
+
+/*
+ * Bit definition part
+ */
+
+/* SDO Clock Control Register (SDO_CLKCON) */
+#define SDO_TVOUT_SW_RESET (1 << 4)
+#define SDO_TVOUT_CLOCK_READY (1 << 1)
+#define SDO_TVOUT_CLOCK_ON (1 << 0)
+
+/* SDO Video Standard Configuration Register (SDO_CONFIG) */
+#define SDO_PROGRESSIVE (1 << 4)
+#define SDO_NTSC_M 0
+#define SDO_PAL_M 1
+#define SDO_PAL_BGHID 2
+#define SDO_PAL_N 3
+#define SDO_PAL_NC 4
+#define SDO_NTSC_443 8
+#define SDO_PAL_60 9
+#define SDO_STANDARD_MASK 0xf
+
+/* SDO VBI Configuration Register (SDO_VBI) */
+#define SDO_CVBS_WSS_INS (1 << 14)
+#define SDO_CVBS_CLOSED_CAPTION_MASK (3 << 12)
+
+/* SDO DAC Configuration Register (SDO_DAC) */
+#define SDO_POWER_ON_DAC (1 << 0)
+
+/* SDO Color Compensation On/Off Control (SDO_CCCON) */
+#define SDO_COMPENSATION_BHS_ADJ_OFF (1 << 4)
+#define SDO_COMPENSATION_CVBS_COMP_OFF (1 << 0)
+
+/* SDO Interrupt Request Register (SDO_IRQ) */
+#define SDO_VSYNC_IRQ_PEND (1 << 0)
+
+#endif /* SAMSUNG_REGS_SDO_H */
diff --git a/drivers/media/video/s5p-tv/regs-vp.h b/drivers/media/video/s5p-tv/regs-vp.h
new file mode 100644
index 000000000000..6c63984e11e8
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-vp.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Video processor register header file for Samsung Mixer driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SAMSUNG_REGS_VP_H
+#define SAMSUNG_REGS_VP_H
+
+/*
+ * Register part
+ */
+
+#define VP_ENABLE 0x0000
+#define VP_SRESET 0x0004
+#define VP_SHADOW_UPDATE 0x0008
+#define VP_FIELD_ID 0x000C
+#define VP_MODE 0x0010
+#define VP_IMG_SIZE_Y 0x0014
+#define VP_IMG_SIZE_C 0x0018
+#define VP_PER_RATE_CTRL 0x001C
+#define VP_TOP_Y_PTR 0x0028
+#define VP_BOT_Y_PTR 0x002C
+#define VP_TOP_C_PTR 0x0030
+#define VP_BOT_C_PTR 0x0034
+#define VP_ENDIAN_MODE 0x03CC
+#define VP_SRC_H_POSITION 0x0044
+#define VP_SRC_V_POSITION 0x0048
+#define VP_SRC_WIDTH 0x004C
+#define VP_SRC_HEIGHT 0x0050
+#define VP_DST_H_POSITION 0x0054
+#define VP_DST_V_POSITION 0x0058
+#define VP_DST_WIDTH 0x005C
+#define VP_DST_HEIGHT 0x0060
+#define VP_H_RATIO 0x0064
+#define VP_V_RATIO 0x0068
+#define VP_POLY8_Y0_LL 0x006C
+#define VP_POLY4_Y0_LL 0x00EC
+#define VP_POLY4_C0_LL 0x012C
+
+/*
+ * Bit definition part
+ */
+
+/* generates mask for range of bits */
+
+#define VP_MASK(high_bit, low_bit) \
+ (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
+
+#define VP_MASK_VAL(val, high_bit, low_bit) \
+ (((val) << (low_bit)) & VP_MASK(high_bit, low_bit))
+
+ /* VP_ENABLE */
+#define VP_ENABLE_ON (1 << 0)
+
+/* VP_SRESET */
+#define VP_SRESET_PROCESSING (1 << 0)
+
+/* VP_SHADOW_UPDATE */
+#define VP_SHADOW_UPDATE_ENABLE (1 << 0)
+
+/* VP_MODE */
+#define VP_MODE_NV12 (0 << 6)
+#define VP_MODE_NV21 (1 << 6)
+#define VP_MODE_LINE_SKIP (1 << 5)
+#define VP_MODE_MEM_LINEAR (0 << 4)
+#define VP_MODE_MEM_TILED (1 << 4)
+#define VP_MODE_FMT_MASK (5 << 4)
+#define VP_MODE_FIELD_ID_AUTO_TOGGLING (1 << 2)
+#define VP_MODE_2D_IPC (1 << 1)
+
+/* VP_IMG_SIZE_Y */
+/* VP_IMG_SIZE_C */
+#define VP_IMG_HSIZE(x) VP_MASK_VAL(x, 29, 16)
+#define VP_IMG_VSIZE(x) VP_MASK_VAL(x, 13, 0)
+
+/* VP_SRC_H_POSITION */
+#define VP_SRC_H_POSITION_VAL(x) VP_MASK_VAL(x, 14, 4)
+
+/* VP_ENDIAN_MODE */
+#define VP_ENDIAN_MODE_LITTLE (1 << 0)
+
+#endif /* SAMSUNG_REGS_VP_H */
diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c
new file mode 100644
index 000000000000..8cec67ef48c9
--- /dev/null
+++ b/drivers/media/video/s5p-tv/sdo_drv.c
@@ -0,0 +1,480 @@
+/*
+ * Samsung Standard Definition Output (SDO) driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "regs-sdo.h"
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung Standard Definition Output (SDO)");
+MODULE_LICENSE("GPL");
+
+#define SDO_DEFAULT_STD V4L2_STD_PAL
+
+struct sdo_format {
+ v4l2_std_id id;
+ /* all modes are 720 pixels wide */
+ unsigned int height;
+ unsigned int cookie;
+};
+
+struct sdo_device {
+ /** pointer to device parent */
+ struct device *dev;
+ /** base address of SDO registers */
+ void __iomem *regs;
+ /** SDO interrupt */
+ unsigned int irq;
+ /** DAC source clock */
+ struct clk *sclk_dac;
+ /** DAC clock */
+ struct clk *dac;
+ /** DAC physical interface */
+ struct clk *dacphy;
+ /** clock for control of VPLL */
+ struct clk *fout_vpll;
+ /** regulator for SDO IP power */
+ struct regulator *vdac;
+ /** regulator for SDO plug detection */
+ struct regulator *vdet;
+ /** subdev used as device interface */
+ struct v4l2_subdev sd;
+ /** current format */
+ const struct sdo_format *fmt;
+};
+
+static inline struct sdo_device *sd_to_sdev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct sdo_device, sd);
+}
+
+static inline
+void sdo_write_mask(struct sdo_device *sdev, u32 reg_id, u32 value, u32 mask)
+{
+ u32 old = readl(sdev->regs + reg_id);
+ value = (value & mask) | (old & ~mask);
+ writel(value, sdev->regs + reg_id);
+}
+
+static inline
+void sdo_write(struct sdo_device *sdev, u32 reg_id, u32 value)
+{
+ writel(value, sdev->regs + reg_id);
+}
+
+static inline
+u32 sdo_read(struct sdo_device *sdev, u32 reg_id)
+{
+ return readl(sdev->regs + reg_id);
+}
+
+static irqreturn_t sdo_irq_handler(int irq, void *dev_data)
+{
+ struct sdo_device *sdev = dev_data;
+
+ /* clear interrupt */
+ sdo_write_mask(sdev, SDO_IRQ, ~0, SDO_VSYNC_IRQ_PEND);
+ return IRQ_HANDLED;
+}
+
+static void sdo_reg_debug(struct sdo_device *sdev)
+{
+#define DBGREG(reg_id) \
+ dev_info(sdev->dev, #reg_id " = %08x\n", \
+ sdo_read(sdev, reg_id))
+
+ DBGREG(SDO_CLKCON);
+ DBGREG(SDO_CONFIG);
+ DBGREG(SDO_VBI);
+ DBGREG(SDO_DAC);
+ DBGREG(SDO_IRQ);
+ DBGREG(SDO_IRQMASK);
+ DBGREG(SDO_VERSION);
+}
+
+static const struct sdo_format sdo_format[] = {
+ { V4L2_STD_PAL_N, .height = 576, .cookie = SDO_PAL_N },
+ { V4L2_STD_PAL_Nc, .height = 576, .cookie = SDO_PAL_NC },
+ { V4L2_STD_PAL_M, .height = 480, .cookie = SDO_PAL_M },
+ { V4L2_STD_PAL_60, .height = 480, .cookie = SDO_PAL_60 },
+ { V4L2_STD_NTSC_443, .height = 480, .cookie = SDO_NTSC_443 },
+ { V4L2_STD_PAL, .height = 576, .cookie = SDO_PAL_BGHID },
+ { V4L2_STD_NTSC_M, .height = 480, .cookie = SDO_NTSC_M },
+};
+
+static const struct sdo_format *sdo_find_format(v4l2_std_id id)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(sdo_format); ++i)
+ if (sdo_format[i].id & id)
+ return &sdo_format[i];
+ return NULL;
+}
+
+static int sdo_g_tvnorms_output(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ *std = V4L2_STD_NTSC_M | V4L2_STD_PAL_M | V4L2_STD_PAL |
+ V4L2_STD_PAL_N | V4L2_STD_PAL_Nc |
+ V4L2_STD_NTSC_443 | V4L2_STD_PAL_60;
+ return 0;
+}
+
+static int sdo_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+ const struct sdo_format *fmt;
+ fmt = sdo_find_format(std);
+ if (fmt == NULL)
+ return -EINVAL;
+ sdev->fmt = fmt;
+ return 0;
+}
+
+static int sdo_g_std_output(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ *std = sd_to_sdev(sd)->fmt->id;
+ return 0;
+}
+
+static int sdo_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ if (!sdev->fmt)
+ return -ENXIO;
+ /* all modes are 720 pixels wide */
+ fmt->width = 720;
+ fmt->height = sdev->fmt->height;
+ fmt->code = V4L2_MBUS_FMT_FIXED;
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ return 0;
+}
+
+static int sdo_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+ struct device *dev = sdev->dev;
+ int ret;
+
+ dev_info(dev, "sdo_s_power(%d)\n", on);
+
+ if (on)
+ ret = pm_runtime_get_sync(dev);
+ else
+ ret = pm_runtime_put_sync(dev);
+
+ /* only values < 0 indicate errors */
+ return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int sdo_streamon(struct sdo_device *sdev)
+{
+ /* set proper clock for Timing Generator */
+ clk_set_rate(sdev->fout_vpll, 54000000);
+ dev_info(sdev->dev, "fout_vpll.rate = %lu\n",
+ clk_get_rate(sdev->fout_vpll));
+ /* enable clock in SDO */
+ sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON);
+ clk_enable(sdev->dacphy);
+ /* enable DAC */
+ sdo_write_mask(sdev, SDO_DAC, ~0, SDO_POWER_ON_DAC);
+ sdo_reg_debug(sdev);
+ return 0;
+}
+
+static int sdo_streamoff(struct sdo_device *sdev)
+{
+ int tries;
+
+ sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC);
+ clk_disable(sdev->dacphy);
+ sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON);
+ for (tries = 100; tries; --tries) {
+ if (sdo_read(sdev, SDO_CLKCON) & SDO_TVOUT_CLOCK_READY)
+ break;
+ mdelay(1);
+ }
+ if (tries == 0)
+ dev_err(sdev->dev, "failed to stop streaming\n");
+ return tries ? 0 : -EIO;
+}
+
+static int sdo_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+ return on ? sdo_streamon(sdev) : sdo_streamoff(sdev);
+}
+
+static const struct v4l2_subdev_core_ops sdo_sd_core_ops = {
+ .s_power = sdo_s_power,
+};
+
+static const struct v4l2_subdev_video_ops sdo_sd_video_ops = {
+ .s_std_output = sdo_s_std_output,
+ .g_std_output = sdo_g_std_output,
+ .g_tvnorms_output = sdo_g_tvnorms_output,
+ .g_mbus_fmt = sdo_g_mbus_fmt,
+ .s_stream = sdo_s_stream,
+};
+
+static const struct v4l2_subdev_ops sdo_sd_ops = {
+ .core = &sdo_sd_core_ops,
+ .video = &sdo_sd_video_ops,
+};
+
+static int sdo_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ dev_info(dev, "suspend\n");
+ regulator_disable(sdev->vdet);
+ regulator_disable(sdev->vdac);
+ clk_disable(sdev->sclk_dac);
+ return 0;
+}
+
+static int sdo_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ dev_info(dev, "resume\n");
+ clk_enable(sdev->sclk_dac);
+ regulator_enable(sdev->vdac);
+ regulator_enable(sdev->vdet);
+
+ /* software reset */
+ sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_SW_RESET);
+ mdelay(10);
+ sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_SW_RESET);
+
+ /* setting TV mode */
+ sdo_write_mask(sdev, SDO_CONFIG, sdev->fmt->cookie, SDO_STANDARD_MASK);
+ /* XXX: forcing interlaced mode using undocumented bit */
+ sdo_write_mask(sdev, SDO_CONFIG, 0, SDO_PROGRESSIVE);
+ /* turn all VBI off */
+ sdo_write_mask(sdev, SDO_VBI, 0, SDO_CVBS_WSS_INS |
+ SDO_CVBS_CLOSED_CAPTION_MASK);
+ /* turn all post processing off */
+ sdo_write_mask(sdev, SDO_CCCON, ~0, SDO_COMPENSATION_BHS_ADJ_OFF |
+ SDO_COMPENSATION_CVBS_COMP_OFF);
+ sdo_reg_debug(sdev);
+ return 0;
+}
+
+static const struct dev_pm_ops sdo_pm_ops = {
+ .runtime_suspend = sdo_runtime_suspend,
+ .runtime_resume = sdo_runtime_resume,
+};
+
+static int __devinit sdo_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sdo_device *sdev;
+ struct resource *res;
+ int ret = 0;
+ struct clk *sclk_vpll;
+
+ dev_info(dev, "probe start\n");
+ sdev = kzalloc(sizeof *sdev, GFP_KERNEL);
+ if (!sdev) {
+ dev_err(dev, "not enough memory.\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ sdev->dev = dev;
+
+ /* mapping registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail_sdev;
+ }
+
+ sdev->regs = ioremap(res->start, resource_size(res));
+ if (sdev->regs == NULL) {
+ dev_err(dev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail_sdev;
+ }
+
+ /* acquiring interrupt */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(dev, "get interrupt resource failed.\n");
+ ret = -ENXIO;
+ goto fail_regs;
+ }
+ ret = request_irq(res->start, sdo_irq_handler, 0, "s5p-sdo", sdev);
+ if (ret) {
+ dev_err(dev, "request interrupt failed.\n");
+ goto fail_regs;
+ }
+ sdev->irq = res->start;
+
+ /* acquire clocks */
+ sdev->sclk_dac = clk_get(dev, "sclk_dac");
+ if (IS_ERR_OR_NULL(sdev->sclk_dac)) {
+ dev_err(dev, "failed to get clock 'sclk_dac'\n");
+ ret = -ENXIO;
+ goto fail_irq;
+ }
+ sdev->dac = clk_get(dev, "dac");
+ if (IS_ERR_OR_NULL(sdev->dac)) {
+ dev_err(dev, "failed to get clock 'dac'\n");
+ ret = -ENXIO;
+ goto fail_sclk_dac;
+ }
+ sdev->dacphy = clk_get(dev, "dacphy");
+ if (IS_ERR_OR_NULL(sdev->dacphy)) {
+ dev_err(dev, "failed to get clock 'dacphy'\n");
+ ret = -ENXIO;
+ goto fail_dac;
+ }
+ sclk_vpll = clk_get(dev, "sclk_vpll");
+ if (IS_ERR_OR_NULL(sclk_vpll)) {
+ dev_err(dev, "failed to get clock 'sclk_vpll'\n");
+ ret = -ENXIO;
+ goto fail_dacphy;
+ }
+ clk_set_parent(sdev->sclk_dac, sclk_vpll);
+ clk_put(sclk_vpll);
+ sdev->fout_vpll = clk_get(dev, "fout_vpll");
+ if (IS_ERR_OR_NULL(sdev->fout_vpll)) {
+ dev_err(dev, "failed to get clock 'fout_vpll'\n");
+ goto fail_dacphy;
+ }
+ dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll));
+
+ /* acquire regulator */
+ sdev->vdac = regulator_get(dev, "vdd33a_dac");
+ if (IS_ERR_OR_NULL(sdev->vdac)) {
+ dev_err(dev, "failed to get regulator 'vdac'\n");
+ goto fail_fout_vpll;
+ }
+ sdev->vdet = regulator_get(dev, "vdet");
+ if (IS_ERR_OR_NULL(sdev->vdet)) {
+ dev_err(dev, "failed to get regulator 'vdet'\n");
+ goto fail_vdac;
+ }
+
+ /* enable gate for dac clock, because mixer uses it */
+ clk_enable(sdev->dac);
+
+ /* configure power management */
+ pm_runtime_enable(dev);
+
+ /* configuration of interface subdevice */
+ v4l2_subdev_init(&sdev->sd, &sdo_sd_ops);
+ sdev->sd.owner = THIS_MODULE;
+ strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name);
+
+ /* set default format */
+ sdev->fmt = sdo_find_format(SDO_DEFAULT_STD);
+ BUG_ON(sdev->fmt == NULL);
+
+ /* keeping subdev in device's private for use by other drivers */
+ dev_set_drvdata(dev, &sdev->sd);
+
+ dev_info(dev, "probe succeeded\n");
+ return 0;
+
+fail_vdac:
+ regulator_put(sdev->vdac);
+fail_fout_vpll:
+ clk_put(sdev->fout_vpll);
+fail_dacphy:
+ clk_put(sdev->dacphy);
+fail_dac:
+ clk_put(sdev->dac);
+fail_sclk_dac:
+ clk_put(sdev->sclk_dac);
+fail_irq:
+ free_irq(sdev->irq, sdev);
+fail_regs:
+ iounmap(sdev->regs);
+fail_sdev:
+ kfree(sdev);
+fail:
+ dev_info(dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit sdo_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ pm_runtime_disable(&pdev->dev);
+ clk_disable(sdev->dac);
+ regulator_put(sdev->vdet);
+ regulator_put(sdev->vdac);
+ clk_put(sdev->fout_vpll);
+ clk_put(sdev->dacphy);
+ clk_put(sdev->dac);
+ clk_put(sdev->sclk_dac);
+ free_irq(sdev->irq, sdev);
+ iounmap(sdev->regs);
+ kfree(sdev);
+
+ dev_info(&pdev->dev, "remove successful\n");
+ return 0;
+}
+
+static struct platform_driver sdo_driver __refdata = {
+ .probe = sdo_probe,
+ .remove = __devexit_p(sdo_remove),
+ .driver = {
+ .name = "s5p-sdo",
+ .owner = THIS_MODULE,
+ .pm = &sdo_pm_ops,
+ }
+};
+
+static int __init sdo_init(void)
+{
+ int ret;
+ static const char banner[] __initdata = KERN_INFO \
+ "Samsung Standard Definition Output (SDO) driver, "
+ "(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
+ printk(banner);
+
+ ret = platform_driver_register(&sdo_driver);
+ if (ret)
+ printk(KERN_ERR "SDO platform driver register failed\n");
+
+ return ret;
+}
+module_init(sdo_init);
+
+static void __exit sdo_exit(void)
+{
+ platform_driver_unregister(&sdo_driver);
+}
+module_exit(sdo_exit);
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index 0db90922ee93..5cfdbc78b918 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -757,8 +757,8 @@ static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_CHROMA_AGC:
/* chroma gain cluster */
- if (state->agc->cur.val)
- state->gain->cur.val =
+ if (state->agc->val)
+ state->gain->val =
saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
break;
}
@@ -793,7 +793,6 @@ static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
else
saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
- v4l2_ctrl_activate(state->gain, !state->agc->val);
break;
default:
@@ -1345,35 +1344,57 @@ static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_dat
static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
struct saa711x_state *state = to_state(sd);
- int reg1e;
+ int reg1f, reg1e;
- *std = V4L2_STD_ALL;
- if (state->ident != V4L2_IDENT_SAA7115) {
- int reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ /*
+ * The V4L2 core already initializes std with all supported
+ * Standards. All driver needs to do is to mask it, to remove
+ * standards that don't apply from the mask
+ */
- if (reg1f & 0x20)
- *std = V4L2_STD_525_60;
- else
- *std = V4L2_STD_625_50;
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f);
- return 0;
- }
+ /* horizontal/vertical not locked */
+ if (reg1f & 0x40)
+ goto ret;
+
+ if (reg1f & 0x20)
+ *std &= V4L2_STD_525_60;
+ else
+ *std &= V4L2_STD_625_50;
+
+ if (state->ident != V4L2_IDENT_SAA7115)
+ goto ret;
reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
switch (reg1e & 0x03) {
case 1:
- *std = V4L2_STD_NTSC;
+ *std &= V4L2_STD_NTSC;
break;
case 2:
- *std = V4L2_STD_PAL;
+ /*
+ * V4L2_STD_PAL just cover the european PAL standards.
+ * This is wrong, as the device could also be using an
+ * other PAL standard.
+ */
+ *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc |
+ V4L2_STD_PAL_M | V4L2_STD_PAL_60;
break;
case 3:
- *std = V4L2_STD_SECAM;
+ *std &= V4L2_STD_SECAM;
break;
default:
+ /* Can't detect anything */
break;
}
+
+ v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e);
+
+ret:
+ v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std);
+
return 0;
}
@@ -1601,7 +1622,6 @@ static int saa711x_probe(struct i2c_client *client,
V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
- state->gain->is_volatile = 1;
sd->ctrl_handler = hdl;
if (hdl->error) {
int err = hdl->error;
@@ -1610,8 +1630,7 @@ static int saa711x_probe(struct i2c_client *client,
kfree(state);
return err;
}
- state->agc->flags |= V4L2_CTRL_FLAG_UPDATE;
- v4l2_ctrl_cluster(2, &state->agc);
+ v4l2_ctrl_auto_cluster(2, &state->agc, 0, true);
state->input = -1;
state->output = SAA7115_IPORT_ON;
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
index 8a5ff4d3cf15..a646ccf51696 100644
--- a/drivers/media/video/saa7134/Makefile
+++ b/drivers/media/video/saa7134/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index e2062b240e32..0f9fb99adeb4 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -4951,8 +4951,9 @@ struct saa7134_board saa7134_boards[] = {
.audio_clock = 0x00187de7,
.tuner_type = TUNER_XC2028,
.radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
+ .tuner_addr = 0x61,
.radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
.inputs = {{
.name = name_tv,
.vmux = 3,
@@ -6992,6 +6993,11 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev,
msleep(10);
saa7134_set_gpio(dev, 18, 1);
break;
+ case SAA7134_BOARD_VIDEOMATE_T750:
+ saa7134_set_gpio(dev, 20, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 20, 1);
+ break;
}
return 0;
}
@@ -7451,6 +7457,11 @@ int saa7134_board_init1(struct saa7134_dev *dev)
saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0e050000, 0x0c050000);
saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0e050000, 0x0c050000);
break;
+ case SAA7134_BOARD_VIDEOMATE_T750:
+ /* enable the analog tuner */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00008000, 0x00008000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000);
+ break;
}
return 0;
}
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index f9be737ba6f4..ca65cda3e101 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -39,6 +39,8 @@
MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
+MODULE_VERSION(SAA7134_VERSION);
+
/* ------------------------------------------------------------------ */
@@ -1332,14 +1334,8 @@ static struct pci_driver saa7134_pci_driver = {
static int __init saa7134_init(void)
{
INIT_LIST_HEAD(&saa7134_devlist);
- printk(KERN_INFO "saa7130/34: v4l2 driver version %d.%d.%d loaded\n",
- (SAA7134_VERSION_CODE >> 16) & 0xff,
- (SAA7134_VERSION_CODE >> 8) & 0xff,
- SAA7134_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO "saa7130/34: snapshot date %04d-%02d-%02d\n",
- SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+ printk(KERN_INFO "saa7130/34: v4l2 driver version %s loaded\n",
+ SAA7134_VERSION);
return pci_register_driver(&saa7134_pci_driver);
}
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 996a206c6d79..1e4ef1669887 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -56,6 +56,7 @@
#include "lgs8gxx.h"
#include "zl10353.h"
+#include "qt1010.h"
#include "zl10036.h"
#include "zl10039.h"
@@ -939,6 +940,18 @@ static struct zl10353_config behold_x7_config = {
.disable_i2c_gate_ctrl = 1,
};
+static struct zl10353_config videomate_t750_zl10353_config = {
+ .demod_address = 0x0f,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static struct qt1010_config videomate_t750_qt1010_config = {
+ .i2c_address = 0x62
+};
+
+
/* ==================================================================
* tda10086 based DVB-S cards, helper functions
*/
@@ -1650,6 +1663,18 @@ static int dvb_init(struct saa7134_dev *dev)
__func__);
break;
+ case SAA7134_BOARD_VIDEOMATE_T750:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &videomate_t750_zl10353_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (dvb_attach(qt1010_attach,
+ fe0->dvb.frontend,
+ &dev->i2c_adap,
+ &videomate_t750_qt1010_config) == NULL)
+ wprintk("error attaching QT1010\n");
+ }
+ break;
case SAA7134_BOARD_ZOLID_HYBRID_PCI:
fe0->dvb.frontend = dvb_attach(tda10048_attach,
&zolid_tda10048_config,
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
index 18294db38a01..dde361a9194e 100644
--- a/drivers/media/video/saa7134/saa7134-empress.c
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -172,7 +172,6 @@ static int empress_querycap(struct file *file, void *priv,
strlcpy(cap->card, saa7134_boards[dev->board].name,
sizeof(cap->card));
sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->version = SAA7134_VERSION_CODE;
cap->capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index 776ba2dd7f9f..9cf7914f6f90 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -1810,7 +1810,6 @@ static int saa7134_querycap(struct file *file, void *priv,
strlcpy(cap->card, saa7134_boards[dev->board].name,
sizeof(cap->card));
sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->version = SAA7134_VERSION_CODE;
cap->capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_VBI_CAPTURE |
@@ -2307,7 +2306,6 @@ static int radio_querycap(struct file *file, void *priv,
strcpy(cap->driver, "saa7134");
strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card));
sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->version = SAA7134_VERSION_CODE;
cap->capabilities = V4L2_CAP_TUNER;
return 0;
}
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 28eb10398323..9b550687213a 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -19,8 +19,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/version.h>
-#define SAA7134_VERSION_CODE KERNEL_VERSION(0, 2, 16)
+#define SAA7134_VERSION "0, 2, 17"
#include <linux/pci.h>
#include <linux/i2c.h>
@@ -844,10 +843,10 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev);
int saa7134_ir_start(struct saa7134_dev *dev);
void saa7134_ir_stop(struct saa7134_dev *dev);
#else
-#define saa7134_input_init1(dev) (0)
-#define saa7134_input_fini(dev) (0)
-#define saa7134_input_irq(dev) (0)
-#define saa7134_probe_i2c_ir(dev) (0)
-#define saa7134_ir_start(dev) (0)
-#define saa7134_ir_stop(dev) (0)
+#define saa7134_input_init1(dev) ((void)0)
+#define saa7134_input_fini(dev) ((void)0)
+#define saa7134_input_irq(dev) ((void)0)
+#define saa7134_probe_i2c_ir(dev) ((void)0)
+#define saa7134_ir_start(dev) ((void)0)
+#define saa7134_ir_stop(dev) ((void)0)
#endif
diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile
index 6303a8e60eac..ecd5811dc486 100644
--- a/drivers/media/video/saa7164/Makefile
+++ b/drivers/media/video/saa7164/Makefile
@@ -4,9 +4,9 @@ saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \
obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/saa7164/saa7164-cards.c b/drivers/media/video/saa7164/saa7164-cards.c
index 69822a4e7275..971591d6450f 100644
--- a/drivers/media/video/saa7164/saa7164-cards.c
+++ b/drivers/media/video/saa7164/saa7164-cards.c
@@ -203,6 +203,66 @@ struct saa7164_board saa7164_boards[] = {
.i2c_reg_len = REGLEN_8bit,
} },
},
+ [SAA7164_BOARD_HAUPPAUGE_HVR2200_4] = {
+ .name = "Hauppauge WinTV-HVR2200",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .portc = SAA7164_MPEG_ENCODER,
+ .portd = SAA7164_MPEG_ENCODER,
+ .porte = SAA7164_MPEG_VBI,
+ .portf = SAA7164_MPEG_VBI,
+ .chiprev = SAA7164_CHIP_REV3,
+ .unit = {{
+ .id = 0x1d,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x05,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1b,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1c,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1e,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x10 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1f,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x12 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
[SAA7164_BOARD_HAUPPAUGE_HVR2250] = {
.name = "Hauppauge WinTV-HVR2250",
.porta = SAA7164_MPEG_DVB,
@@ -387,6 +447,62 @@ struct saa7164_board saa7164_boards[] = {
.i2c_reg_len = REGLEN_8bit,
} },
},
+ [SAA7164_BOARD_HAUPPAUGE_HVR2200_5] = {
+ .name = "Hauppauge WinTV-HVR2200",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .chiprev = SAA7164_CHIP_REV3,
+ .unit = {{
+ .id = 0x23,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x05,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x21,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x22,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x24,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x10 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x25,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x12 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
};
const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards);
@@ -426,6 +542,14 @@ struct saa7164_subid saa7164_subids[] = {
.subvendor = 0x0070,
.subdevice = 0x8851,
.card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8940,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_4,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8953,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_5,
},
};
const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids);
@@ -469,6 +593,8 @@ void saa7164_gpio_setup(struct saa7164_dev *dev)
case SAA7164_BOARD_HAUPPAUGE_HVR2200:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
case SAA7164_BOARD_HAUPPAUGE_HVR2250:
case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
@@ -549,6 +675,8 @@ void saa7164_card_setup(struct saa7164_dev *dev)
case SAA7164_BOARD_HAUPPAUGE_HVR2200:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
case SAA7164_BOARD_HAUPPAUGE_HVR2250:
case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c
index f65eab63ca87..5c5cc3ebf9bd 100644
--- a/drivers/media/video/saa7164/saa7164-dvb.c
+++ b/drivers/media/video/saa7164/saa7164-dvb.c
@@ -475,6 +475,8 @@ int saa7164_dvb_register(struct saa7164_port *port)
case SAA7164_BOARD_HAUPPAUGE_HVR2200:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
i2c_bus = &dev->i2c_bus[port->nr + 1];
switch (port->nr) {
case 0:
diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c
index 400364569c8d..2fd38a01887f 100644
--- a/drivers/media/video/saa7164/saa7164-encoder.c
+++ b/drivers/media/video/saa7164/saa7164-encoder.c
@@ -1246,7 +1246,6 @@ static unsigned int fops_poll(struct file *file, poll_table *wait)
struct saa7164_encoder_fh *fh =
(struct saa7164_encoder_fh *)file->private_data;
struct saa7164_port *port = fh->port;
- struct saa7164_user_buffer *ubuf;
unsigned int mask = 0;
port->last_poll_msecs_diff = port->last_poll_msecs;
@@ -1278,10 +1277,7 @@ static unsigned int fops_poll(struct file *file, poll_table *wait)
}
/* Pull the first buffer from the used list */
- ubuf = list_first_entry(&port->list_buf_used.list,
- struct saa7164_user_buffer, list);
-
- if (ubuf)
+ if (!list_empty(&port->list_buf_used.list))
mask |= POLLIN | POLLRDNORM;
return mask;
diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c
index bc1fcedba874..e2e034158718 100644
--- a/drivers/media/video/saa7164/saa7164-vbi.c
+++ b/drivers/media/video/saa7164/saa7164-vbi.c
@@ -1192,7 +1192,6 @@ static unsigned int fops_poll(struct file *file, poll_table *wait)
{
struct saa7164_vbi_fh *fh = (struct saa7164_vbi_fh *)file->private_data;
struct saa7164_port *port = fh->port;
- struct saa7164_user_buffer *ubuf;
unsigned int mask = 0;
port->last_poll_msecs_diff = port->last_poll_msecs;
@@ -1224,10 +1223,7 @@ static unsigned int fops_poll(struct file *file, poll_table *wait)
}
/* Pull the first buffer from the used list */
- ubuf = list_first_entry(&port->list_buf_used.list,
- struct saa7164_user_buffer, list);
-
- if (ubuf)
+ if (!list_empty(&port->list_buf_used.list))
mask |= POLLIN | POLLRDNORM;
return mask;
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
index 16745d2fb349..742b34103b5d 100644
--- a/drivers/media/video/saa7164/saa7164.h
+++ b/drivers/media/video/saa7164/saa7164.h
@@ -48,7 +48,6 @@
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/kdev_t.h>
-#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/crc32.h>
#include <linux/kthread.h>
@@ -83,6 +82,8 @@
#define SAA7164_BOARD_HAUPPAUGE_HVR2200_3 6
#define SAA7164_BOARD_HAUPPAUGE_HVR2250_2 7
#define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_4 9
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_5 10
#define SAA7164_MAX_UNITS 8
#define SAA7164_TS_NUMBER_OF_LINES 312
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 3ae5c9c58cba..f390682629cf 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -27,7 +27,6 @@
#include <linux/mm.h>
#include <linux/moduleparam.h>
#include <linux/time.h>
-#include <linux/version.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
@@ -39,6 +38,7 @@
#include <media/v4l2-dev.h>
#include <media/soc_camera.h>
#include <media/sh_mobile_ceu.h>
+#include <media/sh_mobile_csi2.h>
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-mediabus.h>
#include <media/soc_mediabus.h>
@@ -90,16 +90,17 @@
struct sh_mobile_ceu_buffer {
struct vb2_buffer vb; /* v4l buffer must be first */
struct list_head queue;
- enum v4l2_mbus_pixelcode code;
};
struct sh_mobile_ceu_dev {
struct soc_camera_host ici;
struct soc_camera_device *icd;
+ struct platform_device *csi2_pdev;
unsigned int irq;
void __iomem *base;
- unsigned long video_limit;
+ size_t video_limit;
+ size_t buf_total;
spinlock_t lock; /* Protects video buffer lists */
struct list_head capture;
@@ -120,7 +121,7 @@ struct sh_mobile_ceu_dev {
};
struct sh_mobile_ceu_cam {
- /* CEU offsets within scaled by the CEU camera output */
+ /* CEU offsets within the camera output, before the CEU scaler */
unsigned int ceu_left;
unsigned int ceu_top;
/* Client output, as seen by the CEU */
@@ -143,30 +144,6 @@ static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb)
return container_of(vb, struct sh_mobile_ceu_buffer, vb);
}
-static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev)
-{
- unsigned long flags;
-
- flags = SOCAM_MASTER |
- SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH;
-
- if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS)
- flags |= SOCAM_DATAWIDTH_8;
-
- if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS)
- flags |= SOCAM_DATAWIDTH_16;
-
- if (flags & SOCAM_DATAWIDTH_MASK)
- return flags;
-
- return 0;
-}
-
static void ceu_write(struct sh_mobile_ceu_dev *priv,
unsigned long reg_offs, u32 data)
{
@@ -205,7 +182,7 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
if (2 != success) {
- dev_warn(&icd->dev, "soft reset time out\n");
+ dev_warn(icd->pdev, "soft reset time out\n");
return -EIO;
}
@@ -215,34 +192,62 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
/*
* Videobuf operations
*/
+
+/*
+ * .queue_setup() is called to check, whether the driver can accept the
+ * requested number of buffers and to fill in plane sizes
+ * for the current frame format if required
+ */
static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
unsigned int *count, unsigned int *num_planes,
- unsigned long sizes[], void *alloc_ctxs[])
+ unsigned int sizes[], void *alloc_ctxs[])
{
struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
+ int bytes_per_line;
+ unsigned int height;
+ if (fmt) {
+ const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+ fmt->fmt.pix.pixelformat);
+ if (!xlate)
+ return -EINVAL;
+ bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ height = fmt->fmt.pix.height;
+ } else {
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ height = icd->user_height;
+ }
if (bytes_per_line < 0)
return bytes_per_line;
- *num_planes = 1;
+ sizes[0] = bytes_per_line * height;
- pcdev->sequence = 0;
- sizes[0] = bytes_per_line * icd->user_height;
alloc_ctxs[0] = pcdev->alloc_ctx;
+ if (!vq->num_buffers)
+ pcdev->sequence = 0;
+
if (!*count)
*count = 2;
- if (pcdev->video_limit) {
- if (PAGE_ALIGN(sizes[0]) * *count > pcdev->video_limit)
- *count = pcdev->video_limit / PAGE_ALIGN(sizes[0]);
+ /* If *num_planes != 0, we have already verified *count. */
+ if (pcdev->video_limit && !*num_planes) {
+ size_t size = PAGE_ALIGN(sizes[0]) * *count;
+
+ if (size + pcdev->buf_total > pcdev->video_limit)
+ *count = (pcdev->video_limit - pcdev->buf_total) /
+ PAGE_ALIGN(sizes[0]);
}
- dev_dbg(icd->dev.parent, "count=%d, size=%lu\n", *count, sizes[0]);
+ *num_planes = 1;
+
+ dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
return 0;
}
@@ -266,6 +271,7 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
unsigned long top1, top2;
unsigned long bottom1, bottom2;
u32 status;
+ bool planar;
int ret = 0;
/*
@@ -311,19 +317,31 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
bottom2 = CDBCR;
}
- phys_addr_top = vb2_dma_contig_plane_paddr(pcdev->active, 0);
-
- ceu_write(pcdev, top1, phys_addr_top);
- if (V4L2_FIELD_NONE != pcdev->field) {
- phys_addr_bottom = phys_addr_top + icd->user_width;
- ceu_write(pcdev, bottom1, phys_addr_bottom);
- }
+ phys_addr_top = vb2_dma_contig_plane_dma_addr(pcdev->active, 0);
switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
+ planar = true;
+ break;
+ default:
+ planar = false;
+ }
+
+ ceu_write(pcdev, top1, phys_addr_top);
+ if (V4L2_FIELD_NONE != pcdev->field) {
+ if (planar)
+ phys_addr_bottom = phys_addr_top + icd->user_width;
+ else
+ phys_addr_bottom = phys_addr_top +
+ soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ ceu_write(pcdev, bottom1, phys_addr_bottom);
+ }
+
+ if (planar) {
phys_addr_top += icd->user_width *
icd->user_height;
ceu_write(pcdev, top2, phys_addr_top);
@@ -340,22 +358,39 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
{
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+
+ /* Added list head initialization on alloc */
+ WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
+
+ return 0;
+}
+
+static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
+{
struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
- struct sh_mobile_ceu_buffer *buf;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+ unsigned long size;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
- unsigned long size;
if (bytes_per_line < 0)
- return bytes_per_line;
+ goto error;
- buf = to_ceu_vb(vb);
+ size = icd->user_height * bytes_per_line;
- dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
+ vb->v4l2_buf.index, vb2_plane_size(vb, 0), size);
+ goto error;
+ }
- /* Added list head initialization on alloc */
- WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
+ vb2_set_plane_payload(vb, 0, size);
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
#ifdef DEBUG
/*
@@ -366,31 +401,6 @@ static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
#endif
- BUG_ON(NULL == icd->current_fmt);
-
- size = icd->user_height * bytes_per_line;
-
- if (vb2_plane_size(vb, 0) < size) {
- dev_err(icd->dev.parent, "Buffer too small (%lu < %lu)\n",
- vb2_plane_size(vb, 0), size);
- return -ENOBUFS;
- }
-
- vb2_set_plane_payload(vb, 0, size);
-
- return 0;
-}
-
-static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
-
- dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
spin_lock_irq(&pcdev->lock);
list_add_tail(&buf->queue, &pcdev->capture);
@@ -404,12 +414,17 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
sh_mobile_ceu_capture(pcdev);
}
spin_unlock_irq(&pcdev->lock);
+
+ return;
+
+error:
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
}
static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
{
struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
@@ -421,14 +436,30 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
pcdev->active = NULL;
}
- /* Doesn't hurt also if the list is empty */
- list_del_init(&buf->queue);
+ /*
+ * Doesn't hurt also if the list is empty, but it hurts, if queuing the
+ * buffer failed, and .buf_init() hasn't been called
+ */
+ if (buf->queue.next)
+ list_del_init(&buf->queue);
+
+ pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0));
+ dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
+ pcdev->buf_total);
spin_unlock_irq(&pcdev->lock);
}
static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
{
+ struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0));
+ dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
+ pcdev->buf_total);
+
/* This is for locking debugging only */
INIT_LIST_HEAD(&to_ceu_vb(vb)->queue);
return 0;
@@ -437,7 +468,7 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
{
struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
struct list_head *buf_head, *tmp;
@@ -499,37 +530,74 @@ out:
return IRQ_HANDLED;
}
+static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
+{
+ struct v4l2_subdev *sd;
+
+ if (!pcdev->csi2_pdev)
+ return NULL;
+
+ v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
+ if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd))
+ return sd;
+
+ return NULL;
+}
+
/* Called with .video_lock held */
static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *csi2_sd;
int ret;
if (pcdev->icd)
return -EBUSY;
- dev_info(icd->dev.parent,
+ dev_info(icd->parent,
"SuperH Mobile CEU driver attached to camera %d\n",
icd->devnum);
pm_runtime_get_sync(ici->v4l2_dev.dev);
+ pcdev->buf_total = 0;
+
ret = sh_mobile_ceu_soft_reset(pcdev);
- if (!ret)
- pcdev->icd = icd;
- return ret;
+ csi2_sd = find_csi2(pcdev);
+ if (csi2_sd)
+ csi2_sd->grp_id = (long)icd;
+
+ ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
+ pm_runtime_put_sync(ici->v4l2_dev.dev);
+ return ret;
+ }
+
+ /*
+ * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
+ * has not found this soc-camera device among its clients
+ */
+ if (ret == -ENODEV && csi2_sd)
+ csi2_sd->grp_id = 0;
+ pcdev->icd = icd;
+
+ return 0;
}
/* Called with .video_lock held */
static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
BUG_ON(icd != pcdev->icd);
+ v4l2_subdev_call(csi2_sd, core, s_power, 0);
+ if (csi2_sd)
+ csi2_sd->grp_id = 0;
/* disable capture, disable interrupts */
ceu_write(pcdev, CEIER, 0);
sh_mobile_ceu_soft_reset(pcdev);
@@ -545,7 +613,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
pm_runtime_put_sync(ici->v4l2_dev.dev);
- dev_info(icd->dev.parent,
+ dev_info(icd->parent,
"SuperH Mobile CEU driver detached from camera %d\n",
icd->devnum);
@@ -585,35 +653,35 @@ static u16 calc_scale(unsigned int src, unsigned int *dst)
/* rect is guaranteed to not exceed the scaled camera rectangle */
static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_cam *cam = icd->host_priv;
struct sh_mobile_ceu_dev *pcdev = ici->priv;
unsigned int height, width, cdwdr_width, in_width, in_height;
unsigned int left_offset, top_offset;
u32 camor;
- dev_geo(icd->dev.parent, "Crop %ux%u@%u:%u\n",
+ dev_geo(icd->parent, "Crop %ux%u@%u:%u\n",
icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top);
left_offset = cam->ceu_left;
top_offset = cam->ceu_top;
- /* CEU cropping (CFSZR) is applied _after_ the scaling filter (CFLCR) */
+ WARN_ON(icd->user_width & 3 || icd->user_height & 3);
+
+ width = icd->user_width;
+
if (pcdev->image_mode) {
in_width = cam->width;
if (!pcdev->is_16bit) {
in_width *= 2;
left_offset *= 2;
}
- width = icd->user_width;
- cdwdr_width = icd->user_width;
+ cdwdr_width = width;
} else {
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ int bytes_per_line = soc_mbus_bytes_per_line(width,
icd->current_fmt->host_fmt);
unsigned int w_factor;
- width = icd->user_width;
-
switch (icd->current_fmt->host_fmt->packing) {
case SOC_MBUS_PACKING_2X8_PADHI:
w_factor = 2;
@@ -623,10 +691,10 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
}
in_width = cam->width * w_factor;
- left_offset = left_offset * w_factor;
+ left_offset *= w_factor;
if (bytes_per_line < 0)
- cdwdr_width = icd->user_width;
+ cdwdr_width = width;
else
cdwdr_width = bytes_per_line;
}
@@ -634,14 +702,14 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
height = icd->user_height;
in_height = cam->height;
if (V4L2_FIELD_NONE != pcdev->field) {
- height /= 2;
+ height = (height / 2) & ~3;
in_height /= 2;
top_offset /= 2;
cdwdr_width *= 2;
}
/* CSI2 special configuration */
- if (pcdev->pdata->csi2_dev) {
+ if (pcdev->pdata->csi2) {
in_width = ((in_width - 2) * 2);
left_offset *= 2;
}
@@ -649,13 +717,14 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
/* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */
camor = left_offset | (top_offset << 16);
- dev_geo(icd->dev.parent,
+ dev_geo(icd->parent,
"CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor,
(in_height << 16) | in_width, (height << 16) | width,
cdwdr_width);
ceu_write(pcdev, CAMOR, camor);
ceu_write(pcdev, CAPWR, (in_height << 16) | in_width);
+ /* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */
ceu_write(pcdev, CFSZR, (height << 16) | width);
ceu_write(pcdev, CDWDR, cdwdr_width);
}
@@ -693,66 +762,93 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
ceu_write(pcdev, CAPSR, capsr);
}
+/* Find the bus subdevice driver, e.g., CSI2 */
+static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
+ struct soc_camera_device *icd)
+{
+ if (pcdev->csi2_pdev) {
+ struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
+ if (csi2_sd && csi2_sd->grp_id == (u32)icd)
+ return csi2_sd;
+ }
+
+ return soc_camera_to_subdev(icd);
+}
+
+#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
/* Capture is not running, no interrupts, no locking needed */
static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
__u32 pixfmt)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int ret;
- unsigned long camera_flags, common_flags, value;
- int yuv_lineskip;
+ struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long value, common_flags = CEU_BUS_FLAGS;
u32 capsr = capture_save_reset(pcdev);
+ unsigned int yuv_lineskip;
+ int ret;
- camera_flags = icd->ops->query_bus_param(icd);
- common_flags = soc_camera_bus_param_compatible(camera_flags,
- make_bus_param(pcdev));
- if (!common_flags)
- return -EINVAL;
+ /*
+ * If the client doesn't implement g_mbus_config, we just use our
+ * platform data
+ */
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ common_flags);
+ if (!common_flags)
+ return -EINVAL;
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
/* Make choises, based on platform preferences */
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
- switch (common_flags & SOCAM_DATAWIDTH_MASK) {
- case SOCAM_DATAWIDTH_8:
- pcdev->is_16bit = 0;
- break;
- case SOCAM_DATAWIDTH_16:
+ if (icd->current_fmt->host_fmt->bits_per_sample > 8)
pcdev->is_16bit = 1;
- break;
- default:
- return -EINVAL;
- }
+ else
+ pcdev->is_16bit = 0;
ceu_write(pcdev, CRCNTR, 0);
ceu_write(pcdev, CRCMPR, 0);
value = 0x00000010; /* data fetch by default */
- yuv_lineskip = 0;
+ yuv_lineskip = 0x10;
switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
- yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */
+ /* convert 4:2:2 -> 4:2:0 */
+ yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */
/* fall-through */
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
@@ -778,12 +874,12 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
- value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
- value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
+ value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
+ value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
value |= pcdev->is_16bit ? 1 << 12 : 0;
/* CSI2 mode */
- if (pcdev->pdata->csi2_dev)
+ if (pcdev->pdata->csi2)
value |= 3 << 12;
ceu_write(pcdev, CAMCR, value);
@@ -806,7 +902,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
sh_mobile_ceu_set_rect(icd);
mdelay(1);
- dev_geo(icd->dev.parent, "CFLCR 0x%x\n", pcdev->cflcr);
+ dev_geo(icd->parent, "CFLCR 0x%x\n", pcdev->cflcr);
ceu_write(pcdev, CFLCR, pcdev->cflcr);
/*
@@ -822,14 +918,12 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
* using 7 we swap the data bytes to match the incoming order:
* D0, D1, D2, D3, D4, D5, D6, D7
*/
- value = 0x00000017;
- if (yuv_lineskip)
- value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */
+ value = 0x00000007 | yuv_lineskip;
ceu_write(pcdev, CDOCR, value);
ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
- dev_dbg(icd->dev.parent, "S_FMT successful for %c%c%c%c %ux%u\n",
+ dev_dbg(icd->parent, "S_FMT successful for %c%c%c%c %ux%u\n",
pixfmt & 0xff, (pixfmt >> 8) & 0xff,
(pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff,
icd->user_width, icd->user_height);
@@ -843,15 +937,21 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
unsigned char buswidth)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- unsigned long camera_flags, common_flags;
+ struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
+ unsigned long common_flags = CEU_BUS_FLAGS;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret)
+ common_flags = soc_mbus_config_compatible(&cfg,
+ common_flags);
+ else if (ret != -ENOIOCTLCMD)
+ return ret;
- camera_flags = icd->ops->query_bus_param(icd);
- common_flags = soc_camera_bus_param_compatible(camera_flags,
- make_bus_param(pcdev));
- if (!common_flags || buswidth > 16 ||
- (buswidth > 8 && !(common_flags & SOCAM_DATAWIDTH_16)))
+ if (!common_flags || buswidth > 16)
return -EINVAL;
return 0;
@@ -861,26 +961,26 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
{
.fourcc = V4L2_PIX_FMT_NV12,
.name = "NV12",
- .bits_per_sample = 12,
- .packing = SOC_MBUS_PACKING_NONE,
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
}, {
.fourcc = V4L2_PIX_FMT_NV21,
.name = "NV21",
- .bits_per_sample = 12,
- .packing = SOC_MBUS_PACKING_NONE,
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
}, {
.fourcc = V4L2_PIX_FMT_NV16,
.name = "NV16",
- .bits_per_sample = 16,
- .packing = SOC_MBUS_PACKING_NONE,
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
}, {
.fourcc = V4L2_PIX_FMT_NV61,
.name = "NV61",
- .bits_per_sample = 16,
- .packing = SOC_MBUS_PACKING_NONE,
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
},
};
@@ -890,6 +990,8 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
{
return fmt->packing == SOC_MBUS_PACKING_NONE ||
(fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_1_5X8) ||
+ (fmt->bits_per_sample == 8 &&
fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
(fmt->bits_per_sample > 8 &&
fmt->packing == SOC_MBUS_PACKING_EXTEND16);
@@ -897,11 +999,43 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
+static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct soc_camera_device,
+ ctrl_handler);
+}
+
+static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct soc_camera_device *icd = ctrl_to_icd(ctrl);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_SHARPNESS:
+ switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ ceu_write(pcdev, CLFCR, !ctrl->val);
+ return 0;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = {
+ .s_ctrl = sh_mobile_ceu_s_ctrl,
+};
+
static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx,
struct soc_camera_format_xlate *xlate)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
struct soc_camera_host *ici = to_soc_camera_host(dev);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
int ret, k, n;
@@ -921,7 +1055,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
return 0;
}
- if (!pcdev->pdata->csi2_dev) {
+ if (!pcdev->pdata->csi2) {
+ /* Are there any restrictions in the CSI-2 case? */
ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
if (ret < 0)
return 0;
@@ -932,6 +1067,12 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
struct v4l2_rect rect;
int shift = 0;
+ /* Add our control */
+ v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 1, 1, 0);
+ if (icd->ctrl_handler.error)
+ return icd->ctrl_handler.error;
+
/* FIXME: subwindow is lost between close / open */
/* Cache current client geometry */
@@ -974,9 +1115,6 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
cam->width = mf.width;
cam->height = mf.height;
- cam->width = mf.width;
- cam->height = mf.height;
-
icd->host_priv = cam;
} else {
cam = icd->host_priv;
@@ -1244,10 +1382,11 @@ static int client_s_fmt(struct soc_camera_device *icd,
{
struct sh_mobile_ceu_cam *cam = icd->host_priv;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
unsigned int max_width, max_height;
struct v4l2_cropcap cap;
+ bool ceu_1to1;
int ret;
ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
@@ -1257,7 +1396,14 @@ static int client_s_fmt(struct soc_camera_device *icd,
dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
- if ((width == mf->width && height == mf->height) || !ceu_can_scale)
+ if (width == mf->width && height == mf->height) {
+ /* Perfect! The client has done it all. */
+ ceu_1to1 = true;
+ goto update_cache;
+ }
+
+ ceu_1to1 = false;
+ if (!ceu_can_scale)
goto update_cache;
cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -1297,7 +1443,10 @@ update_cache:
if (ret < 0)
return ret;
- update_subrect(cam);
+ if (ceu_1to1)
+ cam->subrect = cam->rect;
+ else
+ update_subrect(cam);
return 0;
}
@@ -1313,7 +1462,7 @@ static int client_scale(struct soc_camera_device *icd,
bool ceu_can_scale)
{
struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
struct v4l2_mbus_framefmt mf_tmp = *mf;
unsigned int scale_h, scale_v;
int ret;
@@ -1363,13 +1512,13 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
struct v4l2_crop *a)
{
struct v4l2_rect *rect = &a->c;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
struct v4l2_crop cam_crop;
struct sh_mobile_ceu_cam *cam = icd->host_priv;
struct v4l2_rect *cam_rect = &cam_crop.c;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->dev.parent;
struct v4l2_mbus_framefmt mf;
unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v,
out_width, out_height;
@@ -1384,7 +1533,10 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
capsr = capture_save_reset(pcdev);
dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
- /* 1. - 2. Apply iterative camera S_CROP for new input window. */
+ /*
+ * 1. - 2. Apply iterative camera S_CROP for new input window, read back
+ * actual camera rectangle.
+ */
ret = client_s_crop(icd, a, &cam_crop);
if (ret < 0)
return ret;
@@ -1468,8 +1620,9 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
ceu_write(pcdev, CFLCR, cflcr);
}
- icd->user_width = out_width;
- icd->user_height = out_height;
+ icd->user_width = out_width & ~3;
+ icd->user_height = out_height & ~3;
+ /* Offsets are applied at the CEU scaling filter input */
cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1;
cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1;
@@ -1508,10 +1661,10 @@ static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd,
* CEU crop, mapped backed onto the client input (subrect).
*/
static void calculate_client_output(struct soc_camera_device *icd,
- struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
+ const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
{
struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct device *dev = icd->dev.parent;
+ struct device *dev = icd->parent;
struct v4l2_rect *cam_subrect = &cam->subrect;
unsigned int scale_v, scale_h;
@@ -1544,8 +1697,8 @@ static void calculate_client_output(struct soc_camera_device *icd,
dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
/*
- * 4. Calculate client output window by applying combined scales to real
- * input window.
+ * 4. Calculate desired client output window by applying combined scales
+ * to client (real) input window.
*/
mf->width = scale_down(cam->rect.width, scale_h);
mf->height = scale_down(cam->rect.height, scale_v);
@@ -1555,12 +1708,12 @@ static void calculate_client_output(struct soc_camera_device *icd,
static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
struct sh_mobile_ceu_cam *cam = icd->host_priv;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_mbus_framefmt mf;
- struct device *dev = icd->dev.parent;
__u32 pixfmt = pix->pixelformat;
const struct soc_camera_format_xlate *xlate;
/* Keep Compiler Happy */
@@ -1570,8 +1723,6 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
bool image_mode;
enum v4l2_field field;
- dev_geo(dev, "S_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height);
-
switch (pix->field) {
default:
pix->field = V4L2_FIELD_NONE;
@@ -1592,8 +1743,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
return -EINVAL;
}
- /* 1.-4. Calculate client output geometry */
- calculate_client_output(icd, &f->fmt.pix, &mf);
+ /* 1.-4. Calculate desired client output geometry */
+ calculate_client_output(icd, pix, &mf);
mf.field = pix->field;
mf.colorspace = pix->colorspace;
mf.code = xlate->code;
@@ -1609,6 +1760,9 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
image_mode = false;
}
+ dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code,
+ pix->width, pix->height);
+
dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
/* 5. - 9. */
@@ -1670,6 +1824,10 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
pcdev->field = field;
pcdev->image_mode = image_mode;
+ /* CFSZR requirement */
+ pix->width &= ~3;
+ pix->height &= ~3;
+
return 0;
}
@@ -1684,28 +1842,24 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
int width, height;
int ret;
- dev_geo(icd->dev.parent, "TRY_FMT(pix=0x%x, %ux%u)\n",
+ dev_geo(icd->parent, "TRY_FMT(pix=0x%x, %ux%u)\n",
pixfmt, pix->width, pix->height);
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
- dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
return -EINVAL;
}
/* FIXME: calculate using depth and bus width */
- v4l_bound_align_image(&pix->width, 2, 2560, 1,
+ /* CFSZR requires height and width to be 4-pixel aligned */
+ v4l_bound_align_image(&pix->width, 2, 2560, 2,
&pix->height, 4, 1920, 2, 0);
width = pix->width;
height = pix->height;
- pix->bytesperline = soc_mbus_bytes_per_line(width, xlate->host_fmt);
- if ((int)pix->bytesperline < 0)
- return pix->bytesperline;
- pix->sizeimage = height * pix->bytesperline;
-
/* limit to sensor capabilities */
mf.width = pix->width;
mf.height = pix->height;
@@ -1741,7 +1895,7 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
try_mbus_fmt, &mf);
if (ret < 0) {
/* Shouldn't actually happen... */
- dev_err(icd->dev.parent,
+ dev_err(icd->parent,
"FIXME: client try_fmt() = %d\n", ret);
return ret;
}
@@ -1753,7 +1907,10 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
pix->height = height;
}
- dev_geo(icd->dev.parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
+ pix->width &= ~3;
+ pix->height &= ~3;
+
+ dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
__func__, ret, pix->pixelformat, pix->width, pix->height);
return ret;
@@ -1763,7 +1920,7 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd,
struct v4l2_crop *a)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
u32 out_width = icd->user_width, out_height = icd->user_height;
int ret;
@@ -1775,13 +1932,13 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd,
/* Stop the client */
ret = v4l2_subdev_call(sd, video, s_stream, 0);
if (ret < 0)
- dev_warn(icd->dev.parent,
+ dev_warn(icd->parent,
"Client failed to stop the stream: %d\n", ret);
else
/* Do the crop, if it fails, there's nothing more we can do */
sh_mobile_ceu_set_crop(icd, a);
- dev_geo(icd->dev.parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height);
+ dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height);
if (icd->user_width != out_width || icd->user_height != out_height) {
struct v4l2_format f = {
@@ -1799,8 +1956,8 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd,
out_height != f.fmt.pix.height))
ret = -EINVAL;
if (!ret) {
- icd->user_width = out_width;
- icd->user_height = out_height;
+ icd->user_width = out_width & ~3;
+ icd->user_height = out_height & ~3;
ret = sh_mobile_ceu_set_bus_param(icd,
icd->current_fmt->host_fmt->fourcc);
}
@@ -1827,7 +1984,6 @@ static int sh_mobile_ceu_querycap(struct soc_camera_host *ici,
struct v4l2_capability *cap)
{
strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card));
- cap->version = KERNEL_VERSION(0, 0, 5);
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
}
@@ -1845,55 +2001,6 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
return vb2_queue_init(q);
}
-static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd,
- struct v4l2_control *ctrl)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- u32 val;
-
- switch (ctrl->id) {
- case V4L2_CID_SHARPNESS:
- val = ceu_read(pcdev, CLFCR);
- ctrl->value = val ^ 1;
- return 0;
- }
- return -ENOIOCTLCMD;
-}
-
-static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd,
- struct v4l2_control *ctrl)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- switch (ctrl->id) {
- case V4L2_CID_SHARPNESS:
- switch (icd->current_fmt->host_fmt->fourcc) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- ceu_write(pcdev, CLFCR, !ctrl->value);
- return 0;
- }
- return -EINVAL;
- }
- return -ENOIOCTLCMD;
-}
-
-static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Low-pass filter",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
.owner = THIS_MODULE,
.add = sh_mobile_ceu_add_device,
@@ -1905,14 +2012,10 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
.set_livecrop = sh_mobile_ceu_set_livecrop,
.set_fmt = sh_mobile_ceu_set_fmt,
.try_fmt = sh_mobile_ceu_try_fmt,
- .set_ctrl = sh_mobile_ceu_set_ctrl,
- .get_ctrl = sh_mobile_ceu_get_ctrl,
.poll = sh_mobile_ceu_poll,
.querycap = sh_mobile_ceu_querycap,
.set_bus_param = sh_mobile_ceu_set_bus_param,
.init_videobuf2 = sh_mobile_ceu_init_videobuf,
- .controls = sh_mobile_ceu_controls,
- .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls),
};
struct bus_wait {
@@ -1950,7 +2053,7 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
.completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
.notifier.notifier_call = bus_notify,
};
- struct device *csi2;
+ struct sh_mobile_ceu_companion *csi2;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
@@ -2023,26 +2126,61 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
pcdev->ici.drv_name = dev_name(&pdev->dev);
pcdev->ici.ops = &sh_mobile_ceu_host_ops;
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ err = PTR_ERR(pcdev->alloc_ctx);
+ goto exit_free_clk;
+ }
+
+ err = soc_camera_host_register(&pcdev->ici);
+ if (err)
+ goto exit_free_ctx;
+
/* CSI2 interfacing */
- csi2 = pcdev->pdata->csi2_dev;
+ csi2 = pcdev->pdata->csi2;
if (csi2) {
- wait.dev = csi2;
+ struct platform_device *csi2_pdev =
+ platform_device_alloc("sh-mobile-csi2", csi2->id);
+ struct sh_csi2_pdata *csi2_pdata = csi2->platform_data;
+
+ if (!csi2_pdev) {
+ err = -ENOMEM;
+ goto exit_host_unregister;
+ }
+
+ pcdev->csi2_pdev = csi2_pdev;
+
+ err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata));
+ if (err < 0)
+ goto exit_pdev_put;
+
+ csi2_pdata = csi2_pdev->dev.platform_data;
+ csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev;
+
+ csi2_pdev->resource = csi2->resource;
+ csi2_pdev->num_resources = csi2->num_resources;
+
+ err = platform_device_add(csi2_pdev);
+ if (err < 0)
+ goto exit_pdev_put;
+
+ wait.dev = &csi2_pdev->dev;
err = bus_register_notifier(&platform_bus_type, &wait.notifier);
if (err < 0)
- goto exit_free_clk;
+ goto exit_pdev_unregister;
/*
* From this point the driver module will not unload, until
* we complete the completion.
*/
- if (!csi2->driver) {
+ if (!csi2_pdev->dev.driver) {
complete(&wait.completion);
/* Either too late, or probing failed */
bus_unregister_notifier(&platform_bus_type, &wait.notifier);
err = -ENXIO;
- goto exit_free_clk;
+ goto exit_pdev_unregister;
}
/*
@@ -2051,34 +2189,28 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
* the "owner" is safe!
*/
- err = try_module_get(csi2->driver->owner);
+ err = try_module_get(csi2_pdev->dev.driver->owner);
/* Let notifier complete, if it has been locked */
complete(&wait.completion);
bus_unregister_notifier(&platform_bus_type, &wait.notifier);
if (!err) {
err = -ENODEV;
- goto exit_free_clk;
+ goto exit_pdev_unregister;
}
}
- pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(pcdev->alloc_ctx)) {
- err = PTR_ERR(pcdev->alloc_ctx);
- goto exit_module_put;
- }
-
- err = soc_camera_host_register(&pcdev->ici);
- if (err)
- goto exit_free_ctx;
-
return 0;
+exit_pdev_unregister:
+ platform_device_del(pcdev->csi2_pdev);
+exit_pdev_put:
+ pcdev->csi2_pdev->resource = NULL;
+ platform_device_put(pcdev->csi2_pdev);
+exit_host_unregister:
+ soc_camera_host_unregister(&pcdev->ici);
exit_free_ctx:
vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-exit_module_put:
- if (csi2 && csi2->driver)
- module_put(csi2->driver->owner);
exit_free_clk:
pm_runtime_disable(&pdev->dev);
free_irq(pcdev->irq, pcdev);
@@ -2098,7 +2230,7 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev)
struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
struct sh_mobile_ceu_dev *pcdev = container_of(soc_host,
struct sh_mobile_ceu_dev, ici);
- struct device *csi2 = pcdev->pdata->csi2_dev;
+ struct platform_device *csi2_pdev = pcdev->csi2_pdev;
soc_camera_host_unregister(soc_host);
pm_runtime_disable(&pdev->dev);
@@ -2107,8 +2239,13 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev)
dma_release_declared_memory(&pdev->dev);
iounmap(pcdev->base);
vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
- if (csi2 && csi2->driver)
- module_put(csi2->driver->owner);
+ if (csi2_pdev && csi2_pdev->dev.driver) {
+ struct module *csi2_drv = csi2_pdev->dev.driver->owner;
+ platform_device_del(csi2_pdev);
+ csi2_pdev->resource = NULL;
+ platform_device_put(csi2_pdev);
+ module_put(csi2_drv);
+ }
kfree(pcdev);
return 0;
@@ -2158,4 +2295,5 @@ module_exit(sh_mobile_ceu_exit);
MODULE_DESCRIPTION("SuperH Mobile CEU driver");
MODULE_AUTHOR("Magnus Damm");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.6");
MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c
index 98b87481fa94..ea4f0473ed3b 100644
--- a/drivers/media/video/sh_mobile_csi2.c
+++ b/drivers/media/video/sh_mobile_csi2.c
@@ -15,9 +15,12 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <media/sh_mobile_ceu.h>
#include <media/sh_mobile_csi2.h>
#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
@@ -33,13 +36,11 @@
struct sh_csi2 {
struct v4l2_subdev subdev;
struct list_head list;
- struct notifier_block notifier;
unsigned int irq;
+ unsigned long mipi_flags;
void __iomem *base;
struct platform_device *pdev;
struct sh_csi2_client_config *client;
- unsigned long (*query_bus_param)(struct soc_camera_device *);
- int (*set_bus_param)(struct soc_camera_device *, unsigned long);
};
static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
@@ -127,16 +128,34 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
return 0;
}
+static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+
+ return 0;
+}
+
+static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+ struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id;
+ struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
+ .flags = priv->mipi_flags};
+
+ return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
+}
+
static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = {
.s_mbus_fmt = sh_csi2_s_fmt,
.try_mbus_fmt = sh_csi2_try_fmt,
-};
-
-static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops;
-
-static struct v4l2_subdev_ops sh_csi2_subdev_ops = {
- .core = &sh_csi2_subdev_core_ops,
- .video = &sh_csi2_subdev_video_ops,
+ .g_mbus_config = sh_csi2_g_mbus_config,
+ .s_mbus_config = sh_csi2_s_mbus_config,
};
static void sh_csi2_hwinit(struct sh_csi2 *priv)
@@ -151,11 +170,21 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
udelay(5);
iowrite32(0x00000000, priv->base + SH_CSI2_SRST);
- if (priv->client->lanes & 3)
- tmp |= priv->client->lanes & 3;
- else
- /* Default - both lanes */
- tmp |= 3;
+ switch (pdata->type) {
+ case SH_CSI2C:
+ if (priv->client->lanes == 1)
+ tmp |= 1;
+ else
+ /* Default - both lanes */
+ tmp |= 3;
+ break;
+ case SH_CSI2I:
+ if (!priv->client->lanes || priv->client->lanes > 4)
+ /* Default - all 4 lanes */
+ tmp |= 0xf;
+ else
+ tmp |= (1 << priv->client->lanes) - 1;
+ }
if (priv->client->phy == SH_CSI2_PHY_MAIN)
tmp |= 0x8000;
@@ -170,81 +199,101 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
iowrite32(tmp, priv->base + SH_CSI2_CHKSUM);
}
-static int sh_csi2_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd)
+static int sh_csi2_client_connect(struct sh_csi2 *priv)
{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- const unsigned long flags = SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_DATA_ACTIVE_HIGH;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int sh_csi2_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct device *dev = data;
- struct soc_camera_device *icd = to_soc_camera_dev(dev);
- struct v4l2_device *v4l2_dev = dev_get_drvdata(dev->parent);
- struct sh_csi2 *priv =
- container_of(nb, struct sh_csi2, notifier);
struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
- int ret, i;
+ struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id;
+ struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+ struct device *dev = v4l2_get_subdevdata(&priv->subdev);
+ struct v4l2_mbus_config cfg;
+ unsigned long common_flags, csi2_flags;
+ int i, ret;
+
+ if (priv->client)
+ return -EBUSY;
for (i = 0; i < pdata->num_clients; i++)
if (&pdata->clients[i].pdev->dev == icd->pdev)
break;
- dev_dbg(dev, "%s(%p): action = %lu, found #%d\n", __func__, dev, action, i);
+ dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i);
if (i == pdata->num_clients)
- return NOTIFY_DONE;
+ return -ENODEV;
+
+ /* Check if we can support this camera */
+ csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
+
+ switch (pdata->type) {
+ case SH_CSI2C:
+ if (pdata->clients[i].lanes != 1)
+ csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+ break;
+ case SH_CSI2I:
+ switch (pdata->clients[i].lanes) {
+ default:
+ csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
+ case 3:
+ csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
+ case 2:
+ csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+ }
+ }
- switch (action) {
- case BUS_NOTIFY_BOUND_DRIVER:
- snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s%s",
- dev_name(v4l2_dev->dev), ".mipi-csi");
- priv->subdev.grp_id = (long)icd;
- ret = v4l2_device_register_subdev(v4l2_dev, &priv->subdev);
- dev_dbg(dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
- if (ret < 0)
- return NOTIFY_DONE;
+ cfg.type = V4L2_MBUS_CSI2;
+ ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
+ if (ret == -ENOIOCTLCMD)
+ common_flags = csi2_flags;
+ else if (!ret)
+ common_flags = soc_mbus_config_compatible(&cfg,
+ csi2_flags);
+ else
+ common_flags = 0;
- priv->client = pdata->clients + i;
+ if (!common_flags)
+ return -EINVAL;
- priv->set_bus_param = icd->ops->set_bus_param;
- priv->query_bus_param = icd->ops->query_bus_param;
- icd->ops->set_bus_param = sh_csi2_set_bus_param;
- icd->ops->query_bus_param = sh_csi2_query_bus_param;
+ /* All good: camera MIPI configuration supported */
+ priv->mipi_flags = common_flags;
+ priv->client = pdata->clients + i;
- pm_runtime_get_sync(v4l2_get_subdevdata(&priv->subdev));
+ pm_runtime_get_sync(dev);
- sh_csi2_hwinit(priv);
- break;
- case BUS_NOTIFY_UNBIND_DRIVER:
- priv->client = NULL;
+ sh_csi2_hwinit(priv);
+
+ return 0;
+}
- /* Driver is about to be unbound */
- icd->ops->set_bus_param = priv->set_bus_param;
- icd->ops->query_bus_param = priv->query_bus_param;
- priv->set_bus_param = NULL;
- priv->query_bus_param = NULL;
+static void sh_csi2_client_disconnect(struct sh_csi2 *priv)
+{
+ if (!priv->client)
+ return;
- v4l2_device_unregister_subdev(&priv->subdev);
+ priv->client = NULL;
- pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
- break;
- }
+ pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
+}
- return NOTIFY_OK;
+static int sh_csi2_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+
+ if (on)
+ return sh_csi2_client_connect(priv);
+
+ sh_csi2_client_disconnect(priv);
+ return 0;
}
+static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops = {
+ .s_power = sh_csi2_s_power,
+};
+
+static struct v4l2_subdev_ops sh_csi2_subdev_ops = {
+ .core = &sh_csi2_subdev_core_ops,
+ .video = &sh_csi2_subdev_video_ops,
+};
+
static __devinit int sh_csi2_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -274,14 +323,6 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev)
return -ENOMEM;
priv->irq = irq;
- priv->notifier.notifier_call = sh_csi2_notify;
-
- /* We MUST attach after the MIPI sensor */
- ret = bus_register_notifier(&soc_camera_bus_type, &priv->notifier);
- if (ret < 0) {
- dev_err(&pdev->dev, "CSI2 cannot register notifier\n");
- goto ernotify;
- }
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
dev_err(&pdev->dev, "CSI2 register region already claimed\n");
@@ -297,11 +338,17 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev)
}
priv->pdev = pdev;
+ platform_set_drvdata(pdev, priv);
v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
- platform_set_drvdata(pdev, priv);
+ snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi",
+ dev_name(pdata->v4l2_dev->dev));
+ ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev);
+ dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
+ if (ret < 0)
+ goto esdreg;
pm_runtime_enable(&pdev->dev);
@@ -309,11 +356,11 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev)
return 0;
+esdreg:
+ iounmap(priv->base);
eremap:
release_mem_region(res->start, resource_size(res));
ereqreg:
- bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier);
-ernotify:
kfree(priv);
return ret;
@@ -324,7 +371,7 @@ static __devexit int sh_csi2_remove(struct platform_device *pdev)
struct sh_csi2 *priv = platform_get_drvdata(pdev);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier);
+ v4l2_device_unregister_subdev(&priv->subdev);
pm_runtime_disable(&pdev->dev);
iounmap(priv->base);
release_mem_region(res->start, resource_size(res));
@@ -335,8 +382,9 @@ static __devexit int sh_csi2_remove(struct platform_device *pdev)
}
static struct platform_driver __refdata sh_csi2_pdrv = {
- .remove = __devexit_p(sh_csi2_remove),
- .driver = {
+ .remove = __devexit_p(sh_csi2_remove),
+ .probe = sh_csi2_probe,
+ .driver = {
.name = "sh-mobile-csi2",
.owner = THIS_MODULE,
},
@@ -344,7 +392,7 @@ static struct platform_driver __refdata sh_csi2_pdrv = {
static int __init sh_csi2_init(void)
{
- return platform_driver_probe(&sh_csi2_pdrv, sh_csi2_probe);
+ return platform_driver_register(&sh_csi2_pdrv);
}
static void __exit sh_csi2_exit(void)
diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c
index 07cf0c6c7c1f..9644bd861abc 100644
--- a/drivers/media/video/sh_vou.c
+++ b/drivers/media/video/sh_vou.c
@@ -19,8 +19,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
-#include <linux/version.h>
#include <linux/videodev2.h>
+#include <linux/module.h>
#include <media/sh_vou.h>
#include <media/v4l2-common.h>
@@ -393,7 +393,6 @@ static int sh_vou_querycap(struct file *file, void *priv,
dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
strlcpy(cap->card, "SuperH VOU", sizeof(cap->card));
- cap->version = KERNEL_VERSION(0, 1, 0);
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
return 0;
}
@@ -1490,4 +1489,5 @@ module_exit(sh_vou_exit);
MODULE_DESCRIPTION("SuperH VOU driver");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1.0");
MODULE_ALIAS("platform:sh-vou");
diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h
index cbfc44433b99..22ea211ab54f 100644
--- a/drivers/media/video/sn9c102/sn9c102.h
+++ b/drivers/media/video/sn9c102/sn9c102.h
@@ -21,7 +21,6 @@
#ifndef _SN9C102_H_
#define _SN9C102_H_
-#include <linux/version.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index 0e07c493e6f0..16cb07c5c27b 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -33,6 +33,7 @@
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
+#include <linux/version.h>
#include <linux/page-flags.h>
#include <asm/byteorder.h>
#include <asm/page.h>
@@ -47,8 +48,7 @@
#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL"
-#define SN9C102_MODULE_VERSION "1:1.47pre49"
-#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 47)
+#define SN9C102_MODULE_VERSION "1:1.48"
/*****************************************************************************/
@@ -2158,7 +2158,7 @@ sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_capability cap = {
.driver = "sn9c102",
- .version = SN9C102_MODULE_VERSION_CODE,
+ .version = LINUX_VERSION_CODE,
.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING,
};
@@ -3187,16 +3187,8 @@ static long sn9c102_ioctl_v4l2(struct file *filp,
case VIDIOC_S_AUDIO:
return sn9c102_vidioc_s_audio(cam, arg);
- case VIDIOC_G_STD:
- case VIDIOC_S_STD:
- case VIDIOC_QUERYSTD:
- case VIDIOC_ENUMSTD:
- case VIDIOC_QUERYMENU:
- case VIDIOC_ENUM_FRAMEINTERVALS:
- return -EINVAL;
-
default:
- return -EINVAL;
+ return -ENOTTY;
}
}
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 4e4d4122d9a6..b72580c38957 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -50,49 +50,65 @@ static LIST_HEAD(hosts);
static LIST_HEAD(devices);
static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */
-static int soc_camera_power_set(struct soc_camera_device *icd,
- struct soc_camera_link *icl,
- int power_on)
+static int soc_camera_power_on(struct soc_camera_device *icd,
+ struct soc_camera_link *icl)
{
- int ret;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret = regulator_bulk_enable(icl->num_regulators,
+ icl->regulators);
+ if (ret < 0) {
+ dev_err(icd->pdev, "Cannot enable regulators\n");
+ return ret;
+ }
- if (power_on) {
- ret = regulator_bulk_enable(icl->num_regulators,
- icl->regulators);
+ if (icl->power) {
+ ret = icl->power(icd->pdev, 1);
if (ret < 0) {
- dev_err(&icd->dev, "Cannot enable regulators\n");
- return ret;
+ dev_err(icd->pdev,
+ "Platform failed to power-on the camera.\n");
+ goto elinkpwr;
}
+ }
- if (icl->power)
- ret = icl->power(icd->pdev, power_on);
- if (ret < 0) {
- dev_err(&icd->dev,
- "Platform failed to power-on the camera.\n");
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ goto esdpwr;
- regulator_bulk_disable(icl->num_regulators,
- icl->regulators);
- return ret;
- }
- } else {
- ret = 0;
- if (icl->power)
- ret = icl->power(icd->pdev, 0);
- if (ret < 0) {
- dev_err(&icd->dev,
- "Platform failed to power-off the camera.\n");
- return ret;
- }
+ return 0;
- ret = regulator_bulk_disable(icl->num_regulators,
- icl->regulators);
+esdpwr:
+ if (icl->power)
+ icl->power(icd->pdev, 0);
+elinkpwr:
+ regulator_bulk_disable(icl->num_regulators,
+ icl->regulators);
+ return ret;
+}
+
+static int soc_camera_power_off(struct soc_camera_device *icd,
+ struct soc_camera_link *icl)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret = v4l2_subdev_call(sd, core, s_power, 0);
+
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+
+ if (icl->power) {
+ ret = icl->power(icd->pdev, 0);
if (ret < 0) {
- dev_err(&icd->dev, "Cannot disable regulators\n");
+ dev_err(icd->pdev,
+ "Platform failed to power-off the camera.\n");
return ret;
}
}
- return 0;
+ ret = regulator_bulk_disable(icl->num_regulators,
+ icl->regulators);
+ if (ret < 0)
+ dev_err(icd->pdev, "Cannot disable regulators\n");
+
+ return ret;
}
const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
@@ -108,38 +124,38 @@ const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
EXPORT_SYMBOL(soc_camera_xlate_by_fourcc);
/**
- * soc_camera_apply_sensor_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
+ * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
* @icl: camera platform parameters
- * @flags: flags to be inverted according to platform configuration
+ * @cfg: media bus configuration
* @return: resulting flags
*/
-unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
- unsigned long flags)
+unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl,
+ const struct v4l2_mbus_config *cfg)
{
- unsigned long f;
+ unsigned long f, flags = cfg->flags;
/* If only one of the two polarities is supported, switch to the opposite */
if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) {
- f = flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW);
- if (f == SOCAM_HSYNC_ACTIVE_HIGH || f == SOCAM_HSYNC_ACTIVE_LOW)
- flags ^= SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW;
+ f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW);
+ if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW;
}
if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) {
- f = flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW);
- if (f == SOCAM_VSYNC_ACTIVE_HIGH || f == SOCAM_VSYNC_ACTIVE_LOW)
- flags ^= SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW;
+ f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW);
+ if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) {
- f = flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING);
- if (f == SOCAM_PCLK_SAMPLE_RISING || f == SOCAM_PCLK_SAMPLE_FALLING)
- flags ^= SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING;
+ f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING);
+ if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
}
return flags;
}
-EXPORT_SYMBOL(soc_camera_apply_sensor_flags);
+EXPORT_SYMBOL(soc_camera_apply_board_flags);
#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
((x) >> 24) & 0xff
@@ -147,11 +163,11 @@ EXPORT_SYMBOL(soc_camera_apply_sensor_flags);
static int soc_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct v4l2_pix_format *pix = &f->fmt.pix;
int ret;
- dev_dbg(&icd->dev, "TRY_FMT(%c%c%c%c, %ux%u)\n",
+ dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n",
pixfmtstr(pix->pixelformat), pix->width, pix->height);
pix->bytesperline = 0;
@@ -199,22 +215,15 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
static int soc_camera_enum_input(struct file *file, void *priv,
struct v4l2_input *inp)
{
- struct soc_camera_device *icd = file->private_data;
- int ret = 0;
-
if (inp->index != 0)
return -EINVAL;
- if (icd->ops->enum_input)
- ret = icd->ops->enum_input(icd, inp);
- else {
- /* default is camera */
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = V4L2_STD_UNKNOWN;
- strcpy(inp->name, "Camera");
- }
+ /* default is camera */
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_UNKNOWN;
+ strcpy(inp->name, "Camera");
- return ret;
+ return 0;
}
static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i)
@@ -240,11 +249,19 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
return v4l2_subdev_call(sd, core, s_std, *a);
}
+static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, g_std, a);
+}
+
static int soc_camera_enum_fsizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
return ici->ops->enum_fsizes(icd, fsize);
}
@@ -254,7 +271,7 @@ static int soc_camera_reqbufs(struct file *file, void *priv,
{
int ret;
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
WARN_ON(priv != file->private_data);
@@ -281,7 +298,7 @@ static int soc_camera_querybuf(struct file *file, void *priv,
struct v4l2_buffer *p)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
WARN_ON(priv != file->private_data);
@@ -295,7 +312,7 @@ static int soc_camera_qbuf(struct file *file, void *priv,
struct v4l2_buffer *p)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
WARN_ON(priv != file->private_data);
@@ -312,7 +329,7 @@ static int soc_camera_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *p)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
WARN_ON(priv != file->private_data);
@@ -325,11 +342,37 @@ static int soc_camera_dqbuf(struct file *file, void *priv,
return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK);
}
+static int soc_camera_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ /* videobuf2 only */
+ if (ici->ops->init_videobuf)
+ return -EINVAL;
+ else
+ return vb2_create_bufs(&icd->vb2_vidq, create);
+}
+
+static int soc_camera_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ /* videobuf2 only */
+ if (ici->ops->init_videobuf)
+ return -EINVAL;
+ else
+ return vb2_prepare_buf(&icd->vb2_vidq, b);
+}
+
/* Always entered with .video_lock held */
static int soc_camera_init_user_formats(struct soc_camera_device *icd)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
unsigned int i, fmts = 0, raw_fmts = 0;
int ret;
enum v4l2_mbus_pixelcode code;
@@ -363,7 +406,7 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd)
if (!icd->user_formats)
return -ENOMEM;
- dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts);
+ dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts);
/* Second pass - actually fill data formats */
fmts = 0;
@@ -395,7 +438,7 @@ egfmt:
/* Always entered with .video_lock held */
static void soc_camera_free_user_formats(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
if (ici->ops->put_formats)
ici->ops->put_formats(icd);
@@ -409,11 +452,11 @@ static void soc_camera_free_user_formats(struct soc_camera_device *icd)
static int soc_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct v4l2_pix_format *pix = &f->fmt.pix;
int ret;
- dev_dbg(&icd->dev, "S_FMT(%c%c%c%c, %ux%u)\n",
+ dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n",
pixfmtstr(pix->pixelformat), pix->width, pix->height);
/* We always call try_fmt() before set_fmt() or set_crop() */
@@ -426,7 +469,7 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd,
return ret;
} else if (!icd->current_fmt ||
icd->current_fmt->host_fmt->fourcc != pix->pixelformat) {
- dev_err(&icd->dev,
+ dev_err(icd->pdev,
"Host driver hasn't set up current format correctly!\n");
return -EINVAL;
}
@@ -440,7 +483,7 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd,
if (ici->ops->init_videobuf)
icd->vb_vidq.field = pix->field;
- dev_dbg(&icd->dev, "set width: %d height: %d\n",
+ dev_dbg(icd->pdev, "set width: %d height: %d\n",
icd->user_width, icd->user_height);
/* set physical bus parameters */
@@ -450,21 +493,19 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd,
static int soc_camera_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
- struct soc_camera_device *icd = container_of(vdev->parent,
- struct soc_camera_device,
- dev);
+ struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
struct soc_camera_link *icl = to_soc_camera_link(icd);
struct soc_camera_host *ici;
int ret;
- if (!icd->ops)
+ if (!to_soc_camera_control(icd))
/* No device driver attached */
return -ENODEV;
- ici = to_soc_camera_host(icd->dev.parent);
+ ici = to_soc_camera_host(icd->parent);
if (!try_module_get(ici->ops->owner)) {
- dev_err(&icd->dev, "Couldn't lock capture bus driver.\n");
+ dev_err(icd->pdev, "Couldn't lock capture bus driver.\n");
return -EINVAL;
}
@@ -485,7 +526,7 @@ static int soc_camera_open(struct file *file)
},
};
- ret = soc_camera_power_set(icd, icl, 1);
+ ret = soc_camera_power_on(icd, icl);
if (ret < 0)
goto epower;
@@ -495,7 +536,7 @@ static int soc_camera_open(struct file *file)
ret = ici->ops->add(icd);
if (ret < 0) {
- dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);
+ dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
goto eiciadd;
}
@@ -521,10 +562,11 @@ static int soc_camera_open(struct file *file)
if (ret < 0)
goto einitvb;
}
+ v4l2_ctrl_handler_setup(&icd->ctrl_handler);
}
file->private_data = icd;
- dev_dbg(&icd->dev, "camera device open\n");
+ dev_dbg(icd->pdev, "camera device open\n");
return 0;
@@ -538,7 +580,7 @@ esfmt:
eresume:
ici->ops->remove(icd);
eiciadd:
- soc_camera_power_set(icd, icl, 0);
+ soc_camera_power_off(icd, icl);
epower:
icd->use_count--;
module_put(ici->ops->owner);
@@ -549,7 +591,7 @@ epower:
static int soc_camera_close(struct file *file)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
icd->use_count--;
if (!icd->use_count) {
@@ -562,7 +604,7 @@ static int soc_camera_close(struct file *file)
if (ici->ops->init_videobuf2)
vb2_queue_release(&icd->vb2_vidq);
- soc_camera_power_set(icd, icl, 0);
+ soc_camera_power_off(icd, icl);
}
if (icd->streamer == file)
@@ -570,7 +612,7 @@ static int soc_camera_close(struct file *file)
module_put(ici->ops->owner);
- dev_dbg(&icd->dev, "camera device close\n");
+ dev_dbg(icd->pdev, "camera device close\n");
return 0;
}
@@ -581,7 +623,7 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf,
struct soc_camera_device *icd = file->private_data;
int err = -EINVAL;
- dev_err(&icd->dev, "camera device read not implemented\n");
+ dev_err(icd->pdev, "camera device read not implemented\n");
return err;
}
@@ -589,10 +631,10 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf,
static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
int err;
- dev_dbg(&icd->dev, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+ dev_dbg(icd->pdev, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
if (icd->streamer != file)
return -EBUSY;
@@ -602,7 +644,7 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
else
err = vb2_mmap(&icd->vb2_vidq, vma);
- dev_dbg(&icd->dev, "vma start=0x%08lx, size=%ld, ret=%d\n",
+ dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
(unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
err);
@@ -613,13 +655,13 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
if (icd->streamer != file)
return -EBUSY;
if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream)) {
- dev_err(&icd->dev, "Trying to poll with no queued buffers!\n");
+ dev_err(icd->pdev, "Trying to poll with no queued buffers!\n");
return POLLERR;
}
@@ -659,15 +701,15 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
WARN_ON(priv != file->private_data);
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_warn(&icd->dev, "Wrong buf-type %d\n", f->type);
+ dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type);
return -EINVAL;
}
if (icd->streamer && icd->streamer != file)
return -EBUSY;
- if (is_streaming(to_soc_camera_host(icd->dev.parent), icd)) {
- dev_err(&icd->dev, "S_FMT denied: queue initialised\n");
+ if (is_streaming(to_soc_camera_host(icd->parent), icd)) {
+ dev_err(icd->pdev, "S_FMT denied: queue initialised\n");
return -EBUSY;
}
@@ -716,7 +758,7 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
pix->field = icd->field;
pix->pixelformat = icd->current_fmt->host_fmt->fourcc;
pix->colorspace = icd->colorspace;
- dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n",
+ dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n",
icd->current_fmt->host_fmt->fourcc);
return 0;
}
@@ -725,7 +767,7 @@ static int soc_camera_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
WARN_ON(priv != file->private_data);
@@ -737,7 +779,7 @@ static int soc_camera_streamon(struct file *file, void *priv,
enum v4l2_buf_type i)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
int ret;
@@ -766,7 +808,7 @@ static int soc_camera_streamoff(struct file *file, void *priv,
{
struct soc_camera_device *icd = file->private_data;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
WARN_ON(priv != file->private_data);
@@ -790,80 +832,11 @@ static int soc_camera_streamoff(struct file *file, void *priv,
return 0;
}
-static int soc_camera_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- int i;
-
- WARN_ON(priv != file->private_data);
-
- if (!qc->id)
- return -EINVAL;
-
- /* First check host controls */
- for (i = 0; i < ici->ops->num_controls; i++)
- if (qc->id == ici->ops->controls[i].id) {
- memcpy(qc, &(ici->ops->controls[i]),
- sizeof(*qc));
- return 0;
- }
-
- /* Then device controls */
- for (i = 0; i < icd->ops->num_controls; i++)
- if (qc->id == icd->ops->controls[i].id) {
- memcpy(qc, &(icd->ops->controls[i]),
- sizeof(*qc));
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int soc_camera_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- int ret;
-
- WARN_ON(priv != file->private_data);
-
- if (ici->ops->get_ctrl) {
- ret = ici->ops->get_ctrl(icd, ctrl);
- if (ret != -ENOIOCTLCMD)
- return ret;
- }
-
- return v4l2_subdev_call(sd, core, g_ctrl, ctrl);
-}
-
-static int soc_camera_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- int ret;
-
- WARN_ON(priv != file->private_data);
-
- if (ici->ops->set_ctrl) {
- ret = ici->ops->set_ctrl(icd, ctrl);
- if (ret != -ENOIOCTLCMD)
- return ret;
- }
-
- return v4l2_subdev_call(sd, core, s_ctrl, ctrl);
-}
-
static int soc_camera_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *a)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
return ici->ops->cropcap(icd, a);
}
@@ -872,7 +845,7 @@ static int soc_camera_g_crop(struct file *file, void *fh,
struct v4l2_crop *a)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
int ret;
ret = ici->ops->get_crop(icd, a);
@@ -889,7 +862,7 @@ static int soc_camera_s_crop(struct file *file, void *fh,
struct v4l2_crop *a)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct v4l2_rect *rect = &a->c;
struct v4l2_crop current_crop;
int ret;
@@ -897,7 +870,7 @@ static int soc_camera_s_crop(struct file *file, void *fh,
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- dev_dbg(&icd->dev, "S_CROP(%ux%u@%u:%u)\n",
+ dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n",
rect->width, rect->height, rect->left, rect->top);
/* If get_crop fails, we'll let host and / or client drivers decide */
@@ -905,7 +878,7 @@ static int soc_camera_s_crop(struct file *file, void *fh,
/* Prohibit window size change with initialised buffers */
if (ret < 0) {
- dev_err(&icd->dev,
+ dev_err(icd->pdev,
"S_CROP denied: getting current crop failed\n");
} else if ((a->c.width == current_crop.c.width &&
a->c.height == current_crop.c.height) ||
@@ -915,7 +888,7 @@ static int soc_camera_s_crop(struct file *file, void *fh,
} else if (ici->ops->set_livecrop) {
ret = ici->ops->set_livecrop(icd, a);
} else {
- dev_err(&icd->dev,
+ dev_err(icd->pdev,
"S_CROP denied: queue initialised and sizes differ\n");
ret = -EBUSY;
}
@@ -927,7 +900,7 @@ static int soc_camera_g_parm(struct file *file, void *fh,
struct v4l2_streamparm *a)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
if (ici->ops->get_parm)
return ici->ops->get_parm(icd, a);
@@ -939,7 +912,7 @@ static int soc_camera_s_parm(struct file *file, void *fh,
struct v4l2_streamparm *a)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
if (ici->ops->set_parm)
return ici->ops->set_parm(icd, a);
@@ -976,6 +949,8 @@ static int soc_camera_s_register(struct file *file, void *fh,
}
#endif
+static int soc_camera_probe(struct soc_camera_device *icd);
+
/* So far this function cannot fail */
static void scan_add_host(struct soc_camera_host *ici)
{
@@ -986,15 +961,9 @@ static void scan_add_host(struct soc_camera_host *ici)
list_for_each_entry(icd, &devices, list) {
if (icd->iface == ici->nr) {
int ret;
- icd->dev.parent = ici->v4l2_dev.dev;
- dev_set_name(&icd->dev, "%u-%u", icd->iface,
- icd->devnum);
- ret = device_register(&icd->dev);
- if (ret < 0) {
- icd->dev.parent = NULL;
- dev_err(&icd->dev,
- "Cannot register device: %d\n", ret);
- }
+
+ icd->parent = ici->v4l2_dev.dev;
+ ret = soc_camera_probe(icd);
}
}
@@ -1006,17 +975,17 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
struct soc_camera_link *icl)
{
struct i2c_client *client;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id);
struct v4l2_subdev *subdev;
if (!adap) {
- dev_err(&icd->dev, "Cannot get I2C adapter #%d. No driver?\n",
+ dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
icl->i2c_adapter_id);
goto ei2cga;
}
- icl->board_info->platform_data = icd;
+ icl->board_info->platform_data = icl;
subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
icl->board_info, NULL);
@@ -1026,7 +995,7 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
client = v4l2_get_subdevdata(subdev);
/* Use to_i2c_client(dev) to recover the i2c client */
- dev_set_drvdata(&icd->dev, &client->dev);
+ icd->control = &client->dev;
return 0;
ei2cnd:
@@ -1040,7 +1009,8 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd)
struct i2c_client *client =
to_i2c_client(to_soc_camera_control(icd));
struct i2c_adapter *adap = client->adapter;
- dev_set_drvdata(&icd->dev, NULL);
+
+ icd->control = NULL;
v4l2_device_unregister_subdev(i2c_get_clientdata(client));
i2c_unregister_device(client);
i2c_put_adapter(adap);
@@ -1053,24 +1023,40 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd)
static int soc_camera_video_start(struct soc_camera_device *icd);
static int video_dev_create(struct soc_camera_device *icd);
/* Called during host-driver probe */
-static int soc_camera_probe(struct device *dev)
+static int soc_camera_probe(struct soc_camera_device *icd)
{
- struct soc_camera_device *icd = to_soc_camera_dev(dev);
- struct soc_camera_host *ici = to_soc_camera_host(dev->parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct soc_camera_link *icl = to_soc_camera_link(icd);
struct device *control = NULL;
struct v4l2_subdev *sd;
struct v4l2_mbus_framefmt mf;
int ret;
- dev_info(dev, "Probing %s\n", dev_name(dev));
+ dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
+
+ /*
+ * Currently the subdev with the largest number of controls (13) is
+ * ov6550. So let's pick 16 as a hint for the control handler. Note
+ * that this is a hint only: too large and you waste some memory, too
+ * small and there is a (very) small performance hit when looking up
+ * controls in the internal hash.
+ */
+ ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16);
+ if (ret < 0)
+ return ret;
ret = regulator_bulk_get(icd->pdev, icl->num_regulators,
icl->regulators);
if (ret < 0)
goto ereg;
- ret = soc_camera_power_set(icd, icl, 1);
+ /*
+ * This will not yet call v4l2_subdev_core_ops::s_power(1), because the
+ * subdevice has not been initialised yet. We'll have to call it once
+ * again after initialisation, even though it shouldn't be needed, we
+ * don't do any IO here.
+ */
+ ret = soc_camera_power_on(icd, icl);
if (ret < 0)
goto epower;
@@ -1099,7 +1085,7 @@ static int soc_camera_probe(struct device *dev)
if (icl->module_name)
ret = request_module(icl->module_name);
- ret = icl->add_device(icl, &icd->dev);
+ ret = icl->add_device(icd);
if (ret < 0)
goto eadddev;
@@ -1110,7 +1096,8 @@ static int soc_camera_probe(struct device *dev)
control = to_soc_camera_control(icd);
if (!control || !control->driver || !dev_get_drvdata(control) ||
!try_module_get(control->driver->owner)) {
- icl->del_device(icl);
+ icl->del_device(icd);
+ ret = -ENODEV;
goto enodrv;
}
}
@@ -1118,6 +1105,9 @@ static int soc_camera_probe(struct device *dev)
sd = soc_camera_to_subdev(icd);
sd->grp_id = (long)icd;
+ if (v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler))
+ goto ectrl;
+
/* At this point client .probe() should have run already */
ret = soc_camera_init_user_formats(icd);
if (ret < 0)
@@ -1125,8 +1115,6 @@ static int soc_camera_probe(struct device *dev)
icd->field = V4L2_FIELD_ANY;
- icd->vdev->lock = &icd->video_lock;
-
/*
* ..._video_start() will create a device node, video_register_device()
* itself is protected against concurrent open() calls, but we also have
@@ -1138,6 +1126,10 @@ static int soc_camera_probe(struct device *dev)
if (ret < 0)
goto evidstart;
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto esdpwr;
+
/* Try to improve our guess of a reasonable window format */
if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
icd->user_width = mf.width;
@@ -1146,39 +1138,39 @@ static int soc_camera_probe(struct device *dev)
icd->field = mf.field;
}
- /* Do we have to sysfs_remove_link() before device_unregister()? */
- if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,
- "control"))
- dev_warn(&icd->dev, "Failed creating the control symlink\n");
-
ici->ops->remove(icd);
- soc_camera_power_set(icd, icl, 0);
+ soc_camera_power_off(icd, icl);
mutex_unlock(&icd->video_lock);
return 0;
+esdpwr:
+ video_unregister_device(icd->vdev);
evidstart:
mutex_unlock(&icd->video_lock);
soc_camera_free_user_formats(icd);
eiufmt:
+ectrl:
if (icl->board_info) {
soc_camera_free_i2c(icd);
} else {
- icl->del_device(icl);
+ icl->del_device(icd);
module_put(control->driver->owner);
}
enodrv:
eadddev:
video_device_release(icd->vdev);
+ icd->vdev = NULL;
evdc:
ici->ops->remove(icd);
eadd:
- soc_camera_power_set(icd, icl, 0);
+ soc_camera_power_off(icd, icl);
epower:
regulator_bulk_free(icl->num_regulators, icl->regulators);
ereg:
+ v4l2_ctrl_handler_free(&icd->ctrl_handler);
return ret;
}
@@ -1186,14 +1178,14 @@ ereg:
* This is called on device_unregister, which only means we have to disconnect
* from the host, but not remove ourselves from the device list
*/
-static int soc_camera_remove(struct device *dev)
+static int soc_camera_remove(struct soc_camera_device *icd)
{
- struct soc_camera_device *icd = to_soc_camera_dev(dev);
struct soc_camera_link *icl = to_soc_camera_link(icd);
struct video_device *vdev = icd->vdev;
- BUG_ON(!dev->parent);
+ BUG_ON(!icd->parent);
+ v4l2_ctrl_handler_free(&icd->ctrl_handler);
if (vdev) {
video_unregister_device(vdev);
icd->vdev = NULL;
@@ -1202,10 +1194,9 @@ static int soc_camera_remove(struct device *dev)
if (icl->board_info) {
soc_camera_free_i2c(icd);
} else {
- struct device_driver *drv = to_soc_camera_control(icd) ?
- to_soc_camera_control(icd)->driver : NULL;
+ struct device_driver *drv = to_soc_camera_control(icd)->driver;
if (drv) {
- icl->del_device(icl);
+ icl->del_device(icd);
module_put(drv->owner);
}
}
@@ -1216,49 +1207,6 @@ static int soc_camera_remove(struct device *dev)
return 0;
}
-static int soc_camera_suspend(struct device *dev, pm_message_t state)
-{
- struct soc_camera_device *icd = to_soc_camera_dev(dev);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- int ret = 0;
-
- if (ici->ops->suspend)
- ret = ici->ops->suspend(icd, state);
-
- return ret;
-}
-
-static int soc_camera_resume(struct device *dev)
-{
- struct soc_camera_device *icd = to_soc_camera_dev(dev);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- int ret = 0;
-
- if (ici->ops->resume)
- ret = ici->ops->resume(icd);
-
- return ret;
-}
-
-struct bus_type soc_camera_bus_type = {
- .name = "soc-camera",
- .probe = soc_camera_probe,
- .remove = soc_camera_remove,
- .suspend = soc_camera_suspend,
- .resume = soc_camera_resume,
-};
-EXPORT_SYMBOL_GPL(soc_camera_bus_type);
-
-static struct device_driver ic_drv = {
- .name = "camera",
- .bus = &soc_camera_bus_type,
- .owner = THIS_MODULE,
-};
-
-static void dummy_release(struct device *dev)
-{
-}
-
static int default_cropcap(struct soc_camera_device *icd,
struct v4l2_cropcap *a)
{
@@ -1317,13 +1265,6 @@ static int default_enum_fsizes(struct soc_camera_device *icd,
return 0;
}
-static void soc_camera_device_init(struct device *dev, void *pdata)
-{
- dev->platform_data = pdata;
- dev->bus = &soc_camera_bus_type;
- dev->release = dummy_release;
-}
-
int soc_camera_host_register(struct soc_camera_host *ici)
{
struct soc_camera_host *ix;
@@ -1389,24 +1330,9 @@ void soc_camera_host_unregister(struct soc_camera_host *ici)
mutex_lock(&list_lock);
list_del(&ici->list);
-
- list_for_each_entry(icd, &devices, list) {
- if (icd->iface == ici->nr) {
- void *pdata = icd->dev.platform_data;
- /* The bus->remove will be called */
- device_unregister(&icd->dev);
- /*
- * Not before device_unregister(), .remove
- * needs parent to call ici->ops->remove().
- * If the host module is loaded again, device_register()
- * would complain "already initialised," since 2.6.32
- * this is also needed to prevent use-after-free of the
- * device private data.
- */
- memset(&icd->dev, 0, sizeof(icd->dev));
- soc_camera_device_init(&icd->dev, pdata);
- }
- }
+ list_for_each_entry(icd, &devices, list)
+ if (icd->iface == ici->nr && to_soc_camera_control(icd))
+ soc_camera_remove(icd);
mutex_unlock(&list_lock);
@@ -1448,31 +1374,26 @@ static int soc_camera_device_register(struct soc_camera_device *icd)
return 0;
}
-static void soc_camera_device_unregister(struct soc_camera_device *icd)
-{
- list_del(&icd->list);
-}
-
static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
.vidioc_querycap = soc_camera_querycap,
+ .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
.vidioc_enum_input = soc_camera_enum_input,
.vidioc_g_input = soc_camera_g_input,
.vidioc_s_input = soc_camera_s_input,
.vidioc_s_std = soc_camera_s_std,
+ .vidioc_g_std = soc_camera_g_std,
.vidioc_enum_framesizes = soc_camera_enum_fsizes,
.vidioc_reqbufs = soc_camera_reqbufs,
- .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap,
.vidioc_querybuf = soc_camera_querybuf,
.vidioc_qbuf = soc_camera_qbuf,
.vidioc_dqbuf = soc_camera_dqbuf,
+ .vidioc_create_bufs = soc_camera_create_bufs,
+ .vidioc_prepare_buf = soc_camera_prepare_buf,
.vidioc_streamon = soc_camera_streamon,
.vidioc_streamoff = soc_camera_streamoff,
- .vidioc_queryctrl = soc_camera_queryctrl,
- .vidioc_g_ctrl = soc_camera_g_ctrl,
- .vidioc_s_ctrl = soc_camera_s_ctrl,
.vidioc_cropcap = soc_camera_cropcap,
.vidioc_g_crop = soc_camera_g_crop,
.vidioc_s_crop = soc_camera_s_crop,
@@ -1487,7 +1408,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
static int video_dev_create(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct video_device *vdev = video_device_alloc();
if (!vdev)
@@ -1495,12 +1416,14 @@ static int video_dev_create(struct soc_camera_device *icd)
strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
- vdev->parent = &icd->dev;
+ vdev->parent = icd->pdev;
vdev->current_norm = V4L2_STD_UNKNOWN;
vdev->fops = &soc_camera_fops;
vdev->ioctl_ops = &soc_camera_ioctl_ops;
vdev->release = video_device_release;
vdev->tvnorms = V4L2_STD_UNKNOWN;
+ vdev->ctrl_handler = &icd->ctrl_handler;
+ vdev->lock = &icd->video_lock;
icd->vdev = vdev;
@@ -1515,17 +1438,12 @@ static int soc_camera_video_start(struct soc_camera_device *icd)
const struct device_type *type = icd->vdev->dev.type;
int ret;
- if (!icd->dev.parent)
+ if (!icd->parent)
return -ENODEV;
- if (!icd->ops ||
- !icd->ops->query_bus_param ||
- !icd->ops->set_bus_param)
- return -EINVAL;
-
ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
- dev_err(&icd->dev, "video_register_device failed: %d\n", ret);
+ dev_err(icd->pdev, "video_register_device failed: %d\n", ret);
return ret;
}
@@ -1549,6 +1467,7 @@ static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev)
return -ENOMEM;
icd->iface = icl->bus_id;
+ icd->link = icl;
icd->pdev = &pdev->dev;
platform_set_drvdata(pdev, icd);
@@ -1556,8 +1475,6 @@ static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev)
if (ret < 0)
goto escdevreg;
- soc_camera_device_init(&icd->dev, icl);
-
icd->user_width = DEFAULT_WIDTH;
icd->user_height = DEFAULT_HEIGHT;
@@ -1581,7 +1498,7 @@ static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev)
if (!icd)
return -EINVAL;
- soc_camera_device_unregister(icd);
+ list_del(&icd->list);
kfree(icd);
@@ -1598,31 +1515,12 @@ static struct platform_driver __refdata soc_camera_pdrv = {
static int __init soc_camera_init(void)
{
- int ret = bus_register(&soc_camera_bus_type);
- if (ret)
- return ret;
- ret = driver_register(&ic_drv);
- if (ret)
- goto edrvr;
-
- ret = platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
- if (ret)
- goto epdr;
-
- return 0;
-
-epdr:
- driver_unregister(&ic_drv);
-edrvr:
- bus_unregister(&soc_camera_bus_type);
- return ret;
+ return platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
}
static void __exit soc_camera_exit(void)
{
platform_driver_unregister(&soc_camera_pdrv);
- driver_unregister(&ic_drv);
- bus_unregister(&soc_camera_bus_type);
}
module_init(soc_camera_init);
diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
index bf406e89c992..4402a8a74f7a 100644
--- a/drivers/media/video/soc_camera_platform.c
+++ b/drivers/media/video/soc_camera_platform.c
@@ -30,32 +30,12 @@ static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev)
return container_of(subdev, struct soc_camera_platform_priv, subdev);
}
-static struct soc_camera_platform_info *get_info(struct soc_camera_device *icd)
-{
- struct platform_device *pdev =
- to_platform_device(to_soc_camera_control(icd));
- return pdev->dev.platform_data;
-}
-
static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
return p->set_capture(p, enable);
}
-static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-static unsigned long
-soc_camera_platform_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_platform_info *p = get_info(icd);
- return p->bus_param;
-}
-
static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
@@ -115,6 +95,17 @@ static int soc_camera_platform_cropcap(struct v4l2_subdev *sd,
return 0;
}
+static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ cfg->flags = p->mbus_param;
+ cfg->type = p->mbus_type;
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
.s_stream = soc_camera_platform_s_stream,
.enum_mbus_fmt = soc_camera_platform_enum_fmt,
@@ -123,6 +114,7 @@ static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
.try_mbus_fmt = soc_camera_platform_fill_fmt,
.g_mbus_fmt = soc_camera_platform_fill_fmt,
.s_mbus_fmt = soc_camera_platform_fill_fmt,
+ .g_mbus_config = soc_camera_platform_g_mbus_config,
};
static struct v4l2_subdev_ops platform_subdev_ops = {
@@ -130,11 +122,6 @@ static struct v4l2_subdev_ops platform_subdev_ops = {
.video = &platform_subdev_video_ops,
};
-static struct soc_camera_ops soc_camera_platform_ops = {
- .set_bus_param = soc_camera_platform_set_bus_param,
- .query_bus_param = soc_camera_platform_query_bus_param,
-};
-
static int soc_camera_platform_probe(struct platform_device *pdev)
{
struct soc_camera_host *ici;
@@ -146,7 +133,7 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
if (!p)
return -EINVAL;
- if (!p->dev) {
+ if (!p->icd) {
dev_err(&pdev->dev,
"Platform has not set soc_camera_device pointer!\n");
return -EINVAL;
@@ -156,16 +143,14 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- icd = to_soc_camera_dev(p->dev);
+ icd = p->icd;
/* soc-camera convention: control's drvdata points to the subdev */
platform_set_drvdata(pdev, &priv->subdev);
/* Set the control device reference */
- dev_set_drvdata(&icd->dev, &pdev->dev);
+ icd->control = &pdev->dev;
- icd->ops = &soc_camera_platform_ops;
-
- ici = to_soc_camera_host(icd->dev.parent);
+ ici = to_soc_camera_host(icd->parent);
v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
v4l2_set_subdevdata(&priv->subdev, p);
@@ -178,7 +163,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
return ret;
evdrs:
- icd->ops = NULL;
platform_set_drvdata(pdev, NULL);
kfree(priv);
return ret;
@@ -187,11 +171,10 @@ evdrs:
static int soc_camera_platform_remove(struct platform_device *pdev)
{
struct soc_camera_platform_priv *priv = get_priv(pdev);
- struct soc_camera_platform_info *p = pdev->dev.platform_data;
- struct soc_camera_device *icd = to_soc_camera_dev(p->dev);
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev);
+ p->icd->control = NULL;
v4l2_device_unregister_subdev(&priv->subdev);
- icd->ops = NULL;
platform_set_drvdata(pdev, NULL);
kfree(priv);
return 0;
diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c
index bea7c9cf4f88..cf7f2194ded4 100644
--- a/drivers/media/video/soc_mediabus.c
+++ b/drivers/media/video/soc_mediabus.c
@@ -383,6 +383,39 @@ const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
}
EXPORT_SYMBOL(soc_mbus_get_fmtdesc);
+unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg,
+ unsigned int flags)
+{
+ unsigned long common_flags;
+ bool hsync = true, vsync = true, pclk, data, mode;
+ bool mipi_lanes, mipi_clock;
+
+ common_flags = cfg->flags & flags;
+
+ switch (cfg->type) {
+ case V4L2_MBUS_PARALLEL:
+ hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW);
+ vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW);
+ case V4L2_MBUS_BT656:
+ pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING);
+ data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_LOW);
+ mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE);
+ return (!hsync || !vsync || !pclk || !data || !mode) ?
+ 0 : common_flags;
+ case V4L2_MBUS_CSI2:
+ mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES;
+ mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK);
+ return (!mipi_lanes || !mipi_clock) ? 0 : common_flags;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(soc_mbus_config_compatible);
+
static int __init soc_mbus_init(void)
{
return 0;
diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c
index c901721a1db3..d1b07aceaf94 100644
--- a/drivers/media/video/sr030pc30.c
+++ b/drivers/media/video/sr030pc30.c
@@ -19,6 +19,7 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
@@ -714,11 +715,6 @@ static int sr030pc30_base_config(struct v4l2_subdev *sd)
return ret;
}
-static int sr030pc30_s_stream(struct v4l2_subdev *sd, int enable)
-{
- return 0;
-}
-
static int sr030pc30_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -726,8 +722,10 @@ static int sr030pc30_s_power(struct v4l2_subdev *sd, int on)
const struct sr030pc30_platform_data *pdata = info->pdata;
int ret;
- if (WARN(pdata == NULL, "No platform data!\n"))
- return -ENOMEM;
+ if (pdata == NULL) {
+ WARN(1, "No platform data!\n");
+ return -EINVAL;
+ }
/*
* Put sensor into power sleep mode before switching off
@@ -746,6 +744,7 @@ static int sr030pc30_s_power(struct v4l2_subdev *sd, int on)
if (on) {
ret = sr030pc30_base_config(sd);
} else {
+ ret = 0;
info->curr_win = NULL;
info->curr_fmt = NULL;
}
@@ -761,7 +760,6 @@ static const struct v4l2_subdev_core_ops sr030pc30_core_ops = {
};
static const struct v4l2_subdev_video_ops sr030pc30_video_ops = {
- .s_stream = sr030pc30_s_stream,
.g_mbus_fmt = sr030pc30_g_fmt,
.s_mbus_fmt = sr030pc30_s_fmt,
.try_mbus_fmt = sr030pc30_try_fmt,
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
index d1a2cefbf55b..cbc105f975de 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/video/stk-webcam.c
@@ -55,6 +55,8 @@ MODULE_AUTHOR("Jaime Velasco Juan <jsagarribay@gmail.com> and Nicolas VIVIEN");
MODULE_DESCRIPTION("Syntek DC1125 webcam driver");
+/* bool for webcam LED management */
+int first_init = 1;
/* Some cameras have audio interfaces, we aren't interested in those */
static struct usb_device_id stkwebcam_table[] = {
@@ -518,7 +520,7 @@ static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs)
return -ENOMEM;
for (i = 0; i < n_sbufs; i++) {
if (stk_setup_siobuf(dev, i))
- return (dev->n_sbufs > 1)? 0 : -ENOMEM;
+ return (dev->n_sbufs > 1 ? 0 : -ENOMEM);
dev->n_sbufs = i+1;
}
}
@@ -558,9 +560,14 @@ static int v4l_stk_open(struct file *fp)
vdev = video_devdata(fp);
dev = vdev_to_camera(vdev);
- if (dev == NULL || !is_present(dev)) {
+ if (dev == NULL || !is_present(dev))
return -ENXIO;
- }
+
+ if (!first_init)
+ stk_camera_write_reg(dev, 0x0, 0x24);
+ else
+ first_init = 0;
+
fp->private_data = dev;
usb_autopm_get_interface(dev->interface);
@@ -574,10 +581,12 @@ static int v4l_stk_release(struct file *fp)
if (dev->owner == fp) {
stk_stop_stream(dev);
stk_free_buffers(dev);
+ stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */
+ unset_initialised(dev);
dev->owner = NULL;
}
- if(is_present(dev))
+ if (is_present(dev))
usb_autopm_put_interface(dev->interface);
return 0;
@@ -654,7 +663,7 @@ static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait)
return POLLERR;
if (!list_empty(&dev->sio_full))
- return (POLLIN | POLLRDNORM);
+ return POLLIN | POLLRDNORM;
return 0;
}
@@ -891,9 +900,9 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp,
struct stk_camera *dev = priv;
int i;
- for (i = 0; i < ARRAY_SIZE(stk_sizes)
- && stk_sizes[i].m != dev->vsettings.mode;
- i++);
+ for (i = 0; i < ARRAY_SIZE(stk_sizes) &&
+ stk_sizes[i].m != dev->vsettings.mode; i++)
+ ;
if (i == ARRAY_SIZE(stk_sizes)) {
STK_ERROR("ERROR: mode invalid\n");
return -EINVAL;
@@ -1305,9 +1314,8 @@ static int stk_camera_probe(struct usb_interface *interface,
usb_set_intfdata(interface, dev);
err = stk_register_video_device(dev);
- if (err) {
+ if (err)
goto error;
- }
return 0;
@@ -1350,6 +1358,7 @@ static int stk_camera_resume(struct usb_interface *intf)
return 0;
unset_initialised(dev);
stk_initialise(dev);
+ stk_camera_write_reg(dev, 0x0, 0x49);
stk_setup_format(dev);
if (is_streaming(dev))
stk_start_stream(dev);
diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c
index b6ee1bd342dc..462caa44ae00 100644
--- a/drivers/media/video/tcm825x.c
+++ b/drivers/media/video/tcm825x.c
@@ -27,6 +27,7 @@
*/
#include <linux/i2c.h>
+#include <linux/module.h>
#include <media/v4l2-int-device.h>
#include "tcm825x.h"
diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
index 3941f954daf4..bd218545da9c 100644
--- a/drivers/media/video/tda7432.c
+++ b/drivers/media/video/tda7432.c
@@ -49,10 +49,11 @@ static int maxvol;
static int loudness; /* disable loudness by default */
static int debug; /* insmod parameter */
module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Set debugging level from 0 to 3. Default is off(0).");
module_param(loudness, int, S_IRUGO);
-MODULE_PARM_DESC(maxvol,"Set maximium volume to +20db (0), default is 0db(1)");
+MODULE_PARM_DESC(loudness, "Turn loudness on(1) else off(0). Default is off(0).");
module_param(maxvol, int, S_IRUGO | S_IWUSR);
-
+MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default is +20dB(0).");
/* Structure of address and subaddresses for the tda7432 */
diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c
index fc611ebeb82c..a0895bf07487 100644
--- a/drivers/media/video/timblogiw.c
+++ b/drivers/media/video/timblogiw.c
@@ -20,7 +20,6 @@
* Timberdale FPGA LogiWin Video In
*/
-#include <linux/version.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
@@ -28,6 +27,7 @@
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/i2c.h>
+#include <linux/module.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
#include <media/videobuf-dma-contig.h>
diff --git a/drivers/media/video/tlg2300/Makefile b/drivers/media/video/tlg2300/Makefile
index 81bb7fdd1e3d..ea09b9af2d30 100644
--- a/drivers/media/video/tlg2300/Makefile
+++ b/drivers/media/video/tlg2300/Makefile
@@ -2,8 +2,8 @@ poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/tlg2300/pd-common.h b/drivers/media/video/tlg2300/pd-common.h
index 46066bdc73f9..56564e6aaac2 100644
--- a/drivers/media/video/tlg2300/pd-common.h
+++ b/drivers/media/video/tlg2300/pd-common.h
@@ -1,7 +1,6 @@
#ifndef PD_COMMON_H
#define PD_COMMON_H
-#include <linux/version.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/list.h>
diff --git a/drivers/media/video/tlg2300/pd-dvb.c b/drivers/media/video/tlg2300/pd-dvb.c
index edd78f8b1baa..d0da11ae19df 100644
--- a/drivers/media/video/tlg2300/pd-dvb.c
+++ b/drivers/media/video/tlg2300/pd-dvb.c
@@ -7,7 +7,7 @@
#include "vendorcmds.h"
#include <linux/sched.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb);
diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c
index 99c81a9a4f46..129f135d5a5f 100644
--- a/drivers/media/video/tlg2300/pd-main.c
+++ b/drivers/media/video/tlg2300/pd-main.c
@@ -531,3 +531,4 @@ module_exit(poseidon_exit);
MODULE_AUTHOR("Telegent Systems");
MODULE_DESCRIPTION("For tlg2300-based USB device ");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.2");
diff --git a/drivers/media/video/tlg2300/pd-radio.c b/drivers/media/video/tlg2300/pd-radio.c
index fae84c2a0c39..4fad1dfb92cf 100644
--- a/drivers/media/video/tlg2300/pd-radio.c
+++ b/drivers/media/video/tlg2300/pd-radio.c
@@ -6,7 +6,6 @@
#include <linux/usb.h>
#include <linux/i2c.h>
#include <media/v4l2-dev.h>
-#include <linux/version.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <media/v4l2-ioctl.h>
@@ -149,7 +148,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "tele-radio", sizeof(v->driver));
strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
- v->version = KERNEL_VERSION(0, 0, 1);
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
diff --git a/drivers/staging/tm6000/Kconfig b/drivers/media/video/tm6000/Kconfig
index 114eec8a630a..114eec8a630a 100644
--- a/drivers/staging/tm6000/Kconfig
+++ b/drivers/media/video/tm6000/Kconfig
diff --git a/drivers/staging/tm6000/Makefile b/drivers/media/video/tm6000/Makefile
index 395515b4a888..395515b4a888 100644
--- a/drivers/staging/tm6000/Makefile
+++ b/drivers/media/video/tm6000/Makefile
diff --git a/drivers/staging/tm6000/tm6000-alsa.c b/drivers/media/video/tm6000/tm6000-alsa.c
index 2b96047c2983..7d675c72fd47 100644
--- a/drivers/staging/tm6000/tm6000-alsa.c
+++ b/drivers/media/video/tm6000/tm6000-alsa.c
@@ -18,7 +18,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
-#include <asm/delay.h>
+#include <linux/delay.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -80,11 +80,10 @@ static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
dprintk(1, "Starting audio DMA\n");
/* Enables audio */
- tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x40, 0x40);
+ tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40);
tm6000_set_audio_bitrate(core, 48000);
-
return 0;
}
@@ -98,7 +97,7 @@ static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
dprintk(1, "Stopping audio DMA\n");
/* Disables audio */
- tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x00, 0x40);
+ tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40);
return 0;
}
@@ -123,6 +122,7 @@ static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size)
if (substream->runtime->dma_area) {
if (substream->runtime->dma_bytes > size)
return 0;
+
dsp_buffer_free(substream);
}
@@ -152,9 +152,9 @@ static struct snd_pcm_hardware snd_tm6000_digital_hw = {
SNDRV_PCM_INFO_MMAP_VALID,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_CONTINUOUS,
- .rate_min = 48000,
- .rate_max = 48000,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 48000,
+ .rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.period_bytes_min = 64,
@@ -254,9 +254,7 @@ static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size)
memcpy(runtime->dma_area + buf_pos * stride, buf,
length * stride);
-#ifndef NO_PCM_LOCK
- snd_pcm_stream_lock(substream);
-#endif
+ snd_pcm_stream_lock(substream);
chip->buf_pos += length;
if (chip->buf_pos >= runtime->buffer_size)
@@ -268,9 +266,7 @@ static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size)
period_elapsed = 1;
}
-#ifndef NO_PCM_LOCK
- snd_pcm_stream_unlock(substream);
-#endif
+ snd_pcm_stream_unlock(substream);
if (period_elapsed)
snd_pcm_period_elapsed(substream);
@@ -308,6 +304,7 @@ static int snd_tm6000_hw_free(struct snd_pcm_substream *substream)
schedule_work(&core->wq_trigger);
}
+ dsp_buffer_free(substream);
return 0;
}
@@ -401,7 +398,7 @@ static struct snd_pcm_ops snd_tm6000_pcm_ops = {
/*
* Alsa Constructor - Component probe
*/
-int tm6000_audio_init(struct tm6000_core *dev)
+static int tm6000_audio_init(struct tm6000_core *dev)
{
struct snd_card *card;
struct snd_tm6000_card *chip;
@@ -461,7 +458,7 @@ int tm6000_audio_init(struct tm6000_core *dev)
if (rc < 0)
goto error_chip;
- dprintk(1,"Registered audio driver for %s\n", card->longname);
+ dprintk(1, "Registered audio driver for %s\n", card->longname);
return 0;
@@ -494,7 +491,7 @@ static int tm6000_audio_fini(struct tm6000_core *dev)
return 0;
}
-struct tm6000_ops audio_ops = {
+static struct tm6000_ops audio_ops = {
.type = TM6000_AUDIO,
.name = "TM6000 Audio Extension",
.init = tm6000_audio_init,
diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/media/video/tm6000/tm6000-cards.c
index a69c82e11991..ec2578a0fdf7 100644
--- a/drivers/staging/tm6000/tm6000-cards.c
+++ b/drivers/media/video/tm6000/tm6000-cards.c
@@ -23,7 +23,6 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/usb.h>
-#include <linux/version.h>
#include <linux/slab.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
@@ -88,7 +87,7 @@ struct tm6000_board {
char *ir_codes;
};
-struct tm6000_board tm6000_boards[] = {
+static struct tm6000_board tm6000_boards[] = {
[TM6000_BOARD_UNKNOWN] = {
.name = "Unknown tm6000 video grabber",
.caps = {
@@ -395,7 +394,7 @@ struct tm6000_board tm6000_boards[] = {
.has_zl10353 = 1,
.has_eeprom = 1,
.has_remote = 1,
- .has_radio = 1.
+ .has_radio = 1,
},
.gpio = {
.tuner_reset = TM6010_GPIO_0,
@@ -469,6 +468,7 @@ struct tm6000_board tm6000_boards[] = {
.has_zl10353 = 1,
.has_eeprom = 1,
.has_remote = 1,
+ .has_radio = 1,
},
.gpio = {
.tuner_reset = TM6010_GPIO_2,
@@ -494,6 +494,10 @@ struct tm6000_board tm6000_boards[] = {
.amux = TM6000_AMUX_ADC2,
},
},
+ .rinput = {
+ .type = TM6000_INPUT_RADIO,
+ .amux = TM6000_AMUX_SIF1,
+ },
},
[TM5600_BOARD_TERRATEC_GRABSTER] = {
.name = "Terratec Grabster AV 150/250 MX",
@@ -612,7 +616,7 @@ struct tm6000_board tm6000_boards[] = {
};
/* table of devices that work with this driver */
-struct usb_device_id tm6000_id_table[] = {
+static struct usb_device_id tm6000_id_table[] = {
{ USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
{ USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
{ USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
@@ -633,7 +637,7 @@ struct usb_device_id tm6000_id_table[] = {
{ USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
{ USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
{ USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
- { },
+ { }
};
/* Control power led for show some activity */
@@ -781,6 +785,11 @@ int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
rc = tm6000_i2c_reset(dev, 100);
break;
}
+ break;
+ case XC2028_I2C_FLUSH:
+ tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
+ tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
+ break;
}
return rc;
}
@@ -788,8 +797,6 @@ EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
int tm6000_cards_setup(struct tm6000_core *dev)
{
- int i, rc;
-
/*
* Board-specific initialization sequence. Handles all GPIO
* initialization sequences that are board-specific.
@@ -861,6 +868,9 @@ int tm6000_cards_setup(struct tm6000_core *dev)
*/
if (dev->gpio.tuner_reset) {
+ int rc;
+ int i;
+
for (i = 0; i < 2; i++) {
rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
dev->gpio.tuner_reset, 0x00);
@@ -989,16 +999,22 @@ static int fill_board_specific_data(struct tm6000_core *dev)
dev->vinput[2] = tm6000_boards[dev->model].vinput[2];
dev->rinput = tm6000_boards[dev->model].rinput;
+ /* setup per-model quirks */
+ switch (dev->model) {
+ case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+ dev->quirks |= TM6000_QUIRK_NO_USB_DELAY;
+ break;
+
+ default:
+ break;
+ }
+
/* initialize hardware */
rc = tm6000_init(dev);
if (rc < 0)
return rc;
- rc = v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
- if (rc < 0)
- return rc;
-
- return rc;
+ return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
}
@@ -1167,13 +1183,14 @@ static int tm6000_usb_probe(struct usb_interface *interface,
return -ENOMEM;
}
spin_lock_init(&dev->slock);
+ mutex_init(&dev->usb_lock);
/* Increment usage count */
- tm6000_devused |= 1<<nr;
+ set_bit(nr, &tm6000_devused);
snprintf(dev->name, 29, "tm6000 #%d", nr);
dev->model = id->driver_info;
- if ((card[nr] >= 0) && (card[nr] < ARRAY_SIZE(tm6000_boards)))
+ if (card[nr] < ARRAY_SIZE(tm6000_boards))
dev->model = card[nr];
dev->udev = usbdev;
@@ -1194,8 +1211,6 @@ static int tm6000_usb_probe(struct usb_interface *interface,
speed = "unknown";
}
-
-
/* Get endpoints */
for (i = 0; i < interface->num_altsetting; i++) {
int ep;
@@ -1279,7 +1294,6 @@ static int tm6000_usb_probe(struct usb_interface *interface,
printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name);
rc = tm6000_init_dev(dev);
-
if (rc < 0)
goto err;
@@ -1288,7 +1302,7 @@ static int tm6000_usb_probe(struct usb_interface *interface,
err:
printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
- tm6000_devused &= ~(1<<nr);
+ clear_bit(nr, &tm6000_devused);
usb_put_dev(usbdev);
kfree(dev);
@@ -1346,6 +1360,7 @@ static void tm6000_usb_disconnect(struct usb_interface *interface)
tm6000_close_extension(dev);
tm6000_remove_from_devlist(dev);
+ clear_bit(dev->devno, &tm6000_devused);
kfree(dev);
}
diff --git a/drivers/staging/tm6000/tm6000-core.c b/drivers/media/video/tm6000/tm6000-core.c
index d7eb2e23cdbd..9783616a0da2 100644
--- a/drivers/staging/tm6000/tm6000-core.c
+++ b/drivers/media/video/tm6000/tm6000-core.c
@@ -39,10 +39,11 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
unsigned int pipe;
u8 *data = NULL;
+ mutex_lock(&dev->usb_lock);
+
if (len)
data = kzalloc(len, GFP_KERNEL);
-
if (req_type & USB_DIR_IN)
pipe = usb_rcvctrlpipe(dev->udev, 0);
else {
@@ -51,18 +52,18 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
}
if (tm6000_debug & V4L2_DEBUG_I2C) {
- printk("(dev %p, pipe %08x): ", dev->udev, pipe);
+ printk(KERN_DEBUG "(dev %p, pipe %08x): ", dev->udev, pipe);
- printk("%s: %02x %02x %02x %02x %02x %02x %02x %02x ",
+ printk(KERN_CONT "%s: %02x %02x %02x %02x %02x %02x %02x %02x ",
(req_type & USB_DIR_IN) ? " IN" : "OUT",
req_type, req, value&0xff, value>>8, index&0xff,
index>>8, len&0xff, len>>8);
if (!(req_type & USB_DIR_IN)) {
- printk(">>> ");
+ printk(KERN_CONT ">>> ");
for (i = 0; i < len; i++)
- printk(" %02x", buf[i]);
- printk("\n");
+ printk(KERN_CONT " %02x", buf[i]);
+ printk(KERN_CONT "\n");
}
}
@@ -75,21 +76,21 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
if (tm6000_debug & V4L2_DEBUG_I2C) {
if (ret < 0) {
if (req_type & USB_DIR_IN)
- printk("<<< (len=%d)\n", len);
+ printk(KERN_DEBUG "<<< (len=%d)\n", len);
- printk("%s: Error #%d\n", __FUNCTION__, ret);
+ printk(KERN_CONT "%s: Error #%d\n", __func__, ret);
} else if (req_type & USB_DIR_IN) {
- printk("<<< ");
+ printk(KERN_CONT "<<< ");
for (i = 0; i < len; i++)
- printk(" %02x", buf[i]);
- printk("\n");
+ printk(KERN_CONT " %02x", buf[i]);
+ printk(KERN_CONT "\n");
}
}
kfree(data);
-
msleep(5);
+ mutex_unlock(&dev->usb_lock);
return ret;
}
@@ -188,11 +189,11 @@ void tm6000_set_fourcc_format(struct tm6000_core *dev)
if (dev->dev_type == TM6010) {
int val;
- val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0) & 0xfc;
+ val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, 0) & 0xfc;
if (dev->fourcc == V4L2_PIX_FMT_UYVY)
- tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
+ tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val);
else
- tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val | 1);
+ tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val | 1);
} else {
if (dev->fourcc == V4L2_PIX_FMT_UYVY)
tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0);
@@ -268,9 +269,14 @@ int tm6000_init_analog_mode(struct tm6000_core *dev)
struct v4l2_frequency f;
if (dev->dev_type == TM6010) {
+ u8 active = TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE;
+
+ if (!dev->radio)
+ active |= TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE;
+
/* Enable video and audio */
- tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF,
- 0x60, 0x60);
+ tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF,
+ active, 0x60);
/* Disable TS input */
tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE,
0x00, 0x40);
@@ -308,7 +314,7 @@ int tm6000_init_analog_mode(struct tm6000_core *dev)
* FIXME: This is a hack! xc3028 "sleeps" when no channel is detected
* for more than a few seconds. Not sure why, as this behavior does
* not happen on other devices with xc3028. So, I suspect that it
- * is yet another bug at tm6000. After start sleeping, decoding
+ * is yet another bug at tm6000. After start sleeping, decoding
* doesn't start automatically. Instead, it requires some
* I2C commands to wake it up. As we want to have image at the
* beginning, we needed to add this hack. The better would be to
@@ -335,7 +341,7 @@ int tm6000_init_digital_mode(struct tm6000_core *dev)
{
if (dev->dev_type == TM6010) {
/* Disable video and audio */
- tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF,
+ tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF,
0x00, 0x60);
/* Enable TS input */
tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE,
@@ -390,7 +396,7 @@ struct reg_init {
};
/* The meaning of those initializations are unknown */
-struct reg_init tm6000_init_tab[] = {
+static struct reg_init tm6000_init_tab[] = {
/* REG VALUE */
{ TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f },
{ TM6010_REQ07_RFF_SOFT_RESET, 0x08 },
@@ -458,12 +464,12 @@ struct reg_init tm6000_init_tab[] = {
{ TM6010_REQ05_R18_IMASK7, 0x00 },
};
-struct reg_init tm6010_init_tab[] = {
+static struct reg_init tm6010_init_tab[] = {
{ TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 },
{ TM6010_REQ07_RC4_HSTART0, 0xa0 },
{ TM6010_REQ07_RC6_HEND0, 0x40 },
{ TM6010_REQ07_RCA_VEND0, 0x31 },
- { TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0xe1 },
+ { TM6010_REQ07_RCC_ACTIVE_IF, 0xe1 },
{ TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 },
{ TM6010_REQ07_RFE_POWER_DOWN, 0x7f },
@@ -593,6 +599,56 @@ int tm6000_init(struct tm6000_core *dev)
return rc;
}
+int tm6000_reset(struct tm6000_core *dev)
+{
+ int pipe;
+ int err;
+
+ msleep(500);
+
+ err = usb_set_interface(dev->udev, dev->isoc_in.bInterfaceNumber, 0);
+ if (err < 0) {
+ tm6000_err("failed to select interface %d, alt. setting 0\n",
+ dev->isoc_in.bInterfaceNumber);
+ return err;
+ }
+
+ err = usb_reset_configuration(dev->udev);
+ if (err < 0) {
+ tm6000_err("failed to reset configuration\n");
+ return err;
+ }
+
+ if ((dev->quirks & TM6000_QUIRK_NO_USB_DELAY) == 0)
+ msleep(5);
+
+ /*
+ * Not all devices have int_in defined
+ */
+ if (!dev->int_in.endp)
+ return 0;
+
+ err = usb_set_interface(dev->udev, dev->isoc_in.bInterfaceNumber, 2);
+ if (err < 0) {
+ tm6000_err("failed to select interface %d, alt. setting 2\n",
+ dev->isoc_in.bInterfaceNumber);
+ return err;
+ }
+
+ msleep(5);
+
+ pipe = usb_rcvintpipe(dev->udev,
+ dev->int_in.endp->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+ err = usb_clear_halt(dev->udev, pipe);
+ if (err < 0) {
+ tm6000_err("usb_clear_halt failed: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate)
{
int val = 0;
@@ -687,7 +743,7 @@ int tm6000_set_audio_rinput(struct tm6000_core *dev)
return 0;
}
-void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute)
+static void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute)
{
u8 mute_reg = 0;
@@ -697,7 +753,7 @@ void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute)
tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08);
}
-void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute)
+static void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute)
{
u8 mute_reg = 0;
@@ -749,7 +805,7 @@ int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute)
return 0;
}
-void tm6010_set_volume_sif(struct tm6000_core *dev, int vol)
+static void tm6010_set_volume_sif(struct tm6000_core *dev, int vol)
{
u8 vol_reg;
@@ -762,7 +818,7 @@ void tm6010_set_volume_sif(struct tm6000_core *dev, int vol)
tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg);
}
-void tm6010_set_volume_adc(struct tm6000_core *dev, int vol)
+static void tm6010_set_volume_adc(struct tm6000_core *dev, int vol)
{
u8 vol_reg;
diff --git a/drivers/staging/tm6000/tm6000-dvb.c b/drivers/media/video/tm6000/tm6000-dvb.c
index ff04c89e45a3..5e6c129a4beb 100644
--- a/drivers/staging/tm6000/tm6000-dvb.c
+++ b/drivers/media/video/tm6000/tm6000-dvb.c
@@ -98,20 +98,20 @@ static void tm6000_urb_received(struct urb *urb)
if (dev->dvb->streams > 0) {
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0) {
- printk(KERN_ERR "tm6000: error %s\n", __FUNCTION__);
+ printk(KERN_ERR "tm6000: error %s\n", __func__);
kfree(urb->transfer_buffer);
usb_free_urb(urb);
}
}
}
-int tm6000_start_stream(struct tm6000_core *dev)
+static int tm6000_start_stream(struct tm6000_core *dev)
{
int ret;
unsigned int pipe, size;
struct tm6000_dvb *dvb = dev->dvb;
- printk(KERN_INFO "tm6000: got start stream request %s\n", __FUNCTION__);
+ printk(KERN_INFO "tm6000: got start stream request %s\n", __func__);
if (dev->mode != TM6000_MODE_DIGITAL) {
tm6000_init_digital_mode(dev);
@@ -145,7 +145,7 @@ int tm6000_start_stream(struct tm6000_core *dev)
ret = usb_clear_halt(dev->udev, pipe);
if (ret < 0) {
printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n",
- ret, __FUNCTION__);
+ ret, __func__);
return ret;
} else
printk(KERN_ERR "tm6000: pipe resetted\n");
@@ -166,7 +166,7 @@ int tm6000_start_stream(struct tm6000_core *dev)
return 0;
}
-void tm6000_stop_stream(struct tm6000_core *dev)
+static void tm6000_stop_stream(struct tm6000_core *dev)
{
struct tm6000_dvb *dvb = dev->dvb;
@@ -180,12 +180,12 @@ void tm6000_stop_stream(struct tm6000_core *dev)
}
}
-int tm6000_start_feed(struct dvb_demux_feed *feed)
+static int tm6000_start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct tm6000_core *dev = demux->priv;
struct tm6000_dvb *dvb = dev->dvb;
- printk(KERN_INFO "tm6000: got start feed request %s\n", __FUNCTION__);
+ printk(KERN_INFO "tm6000: got start feed request %s\n", __func__);
mutex_lock(&dvb->mutex);
if (dvb->streams == 0) {
@@ -199,13 +199,13 @@ int tm6000_start_feed(struct dvb_demux_feed *feed)
return 0;
}
-int tm6000_stop_feed(struct dvb_demux_feed *feed)
+static int tm6000_stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct tm6000_core *dev = demux->priv;
struct tm6000_dvb *dvb = dev->dvb;
- printk(KERN_INFO "tm6000: got stop feed request %s\n", __FUNCTION__);
+ printk(KERN_INFO "tm6000: got stop feed request %s\n", __func__);
mutex_lock(&dvb->mutex);
@@ -222,7 +222,7 @@ int tm6000_stop_feed(struct dvb_demux_feed *feed)
return 0;
}
-int tm6000_dvb_attach_frontend(struct tm6000_core *dev)
+static int tm6000_dvb_attach_frontend(struct tm6000_core *dev)
{
struct tm6000_dvb *dvb = dev->dvb;
@@ -247,7 +247,7 @@ int tm6000_dvb_attach_frontend(struct tm6000_core *dev)
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-int register_dvb(struct tm6000_core *dev)
+static int register_dvb(struct tm6000_core *dev)
{
int ret = -1;
struct tm6000_dvb *dvb = dev->dvb;
@@ -330,7 +330,7 @@ int register_dvb(struct tm6000_core *dev)
dvb->demux.write_to_decoder = NULL;
ret = dvb_dmx_init(&dvb->demux);
if (ret < 0) {
- printk("tm6000: dvb_dmx_init failed (errno = %d)\n", ret);
+ printk(KERN_ERR "tm6000: dvb_dmx_init failed (errno = %d)\n", ret);
goto frontend_err;
}
@@ -340,7 +340,7 @@ int register_dvb(struct tm6000_core *dev)
ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
if (ret < 0) {
- printk("tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret);
+ printk(KERN_ERR "tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret);
goto dvb_dmx_err;
}
@@ -359,7 +359,7 @@ err:
return ret;
}
-void unregister_dvb(struct tm6000_core *dev)
+static void unregister_dvb(struct tm6000_core *dev)
{
struct tm6000_dvb *dvb = dev->dvb;
diff --git a/drivers/staging/tm6000/tm6000-i2c.c b/drivers/media/video/tm6000/tm6000-i2c.c
index 8828c120b5ca..0290bbf00c3e 100644
--- a/drivers/staging/tm6000/tm6000-i2c.c
+++ b/drivers/media/video/tm6000/tm6000-i2c.c
@@ -40,7 +40,7 @@ MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
#define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \
printk(KERN_DEBUG "%s at %s: " fmt, \
- dev->name, __FUNCTION__ , ##args); } while (0)
+ dev->name, __func__, ##args); } while (0)
static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr,
__u8 reg, char *buf, int len)
@@ -189,7 +189,7 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap,
/* 1 or 2 byte write followed by a read */
if (i2c_debug >= 2)
for (byte = 0; byte < msgs[i].len; byte++)
- printk(" %02x", msgs[i].buf[byte]);
+ printk(KERN_CONT " %02x", msgs[i].buf[byte]);
i2c_dprintk(2, "; joined to read %s len=%d:",
i == num - 2 ? "stop" : "nonstop",
msgs[i + 1].len);
@@ -211,22 +211,17 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap,
}
if (i2c_debug >= 2)
for (byte = 0; byte < msgs[i].len; byte++)
- printk(" %02x", msgs[i].buf[byte]);
+ printk(KERN_CONT " %02x", msgs[i].buf[byte]);
} else {
/* write bytes */
if (i2c_debug >= 2)
for (byte = 0; byte < msgs[i].len; byte++)
- printk(" %02x", msgs[i].buf[byte]);
+ printk(KERN_CONT " %02x", msgs[i].buf[byte]);
rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0],
msgs[i].buf + 1, msgs[i].len - 1);
-
- if (addr == dev->tuner_addr << 1) {
- tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
- tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
- }
}
if (i2c_debug >= 2)
- printk("\n");
+ printk(KERN_CONT "\n");
if (rc < 0)
goto err;
}
@@ -264,7 +259,7 @@ static int tm6000_i2c_eeprom(struct tm6000_core *dev)
p++;
if (0 == (i % 16))
printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i);
- printk(" %02x", dev->eedata[i]);
+ printk(KERN_CONT " %02x", dev->eedata[i]);
if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z'))
bytes[i%16] = dev->eedata[i];
else
@@ -274,14 +269,14 @@ static int tm6000_i2c_eeprom(struct tm6000_core *dev)
if (0 == (i % 16)) {
bytes[16] = '\0';
- printk(" %s\n", bytes);
+ printk(KERN_CONT " %s\n", bytes);
}
}
if (0 != (i%16)) {
bytes[i%16] = '\0';
for (i %= 16; i < 16; i++)
- printk(" ");
- printk(" %s\n", bytes);
+ printk(KERN_CONT " ");
+ printk(KERN_CONT " %s\n", bytes);
}
return 0;
diff --git a/drivers/staging/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c
index dae2f1fdcc5b..405d12729d05 100644
--- a/drivers/staging/tm6000/tm6000-input.c
+++ b/drivers/media/video/tm6000/tm6000-input.c
@@ -284,7 +284,7 @@ static void tm6000_ir_stop(struct rc_dev *rc)
cancel_delayed_work_sync(&ir->work);
}
-int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
+static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
{
struct tm6000_IR *ir = rc->priv;
@@ -449,9 +449,8 @@ int tm6000_ir_fini(struct tm6000_core *dev)
rc_unregister_device(ir->rc);
- if (ir->int_urb) {
+ if (ir->int_urb)
tm6000_ir_int_stop(dev);
- }
kfree(ir);
dev->ir = NULL;
diff --git a/drivers/staging/tm6000/tm6000-regs.h b/drivers/media/video/tm6000/tm6000-regs.h
index 5375a8347374..7f491b6de933 100644
--- a/drivers/staging/tm6000/tm6000-regs.h
+++ b/drivers/media/video/tm6000/tm6000-regs.h
@@ -90,7 +90,7 @@
*/
enum {
- TM6000_URB_MSG_VIDEO=1,
+ TM6000_URB_MSG_VIDEO = 1,
TM6000_URB_MSG_AUDIO,
TM6000_URB_MSG_VBI,
TM6000_URB_MSG_PTS,
@@ -270,7 +270,9 @@ enum {
#define TM6010_REQ07_RCA_VEND0 0x07, 0xca
#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb
/* ONLY for TM6010 */
-#define TM6010_REQ07_RCC_ACTIVE_VIDEO_IF 0x07, 0xcc
+#define TM6010_REQ07_RCC_ACTIVE_IF 0x07, 0xcc
+#define TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE (1 << 5)
+#define TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE (1 << 6)
#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0
#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1
#define TM6010_REQ07_RD2_ADDR_FOR_REQ2 0x07, 0xd2
diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/video/tm6000/tm6000-stds.c
new file mode 100644
index 000000000000..9a4145dc3d87
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-stds.c
@@ -0,0 +1,659 @@
+/*
+ * tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include "tm6000.h"
+#include "tm6000-regs.h"
+
+static unsigned int tm6010_a_mode;
+module_param(tm6010_a_mode, int, 0644);
+MODULE_PARM_DESC(tm6010_a_mode, "set tm6010 sif audio mode");
+
+struct tm6000_reg_settings {
+ unsigned char req;
+ unsigned char reg;
+ unsigned char value;
+};
+
+
+struct tm6000_std_settings {
+ v4l2_std_id id;
+ struct tm6000_reg_settings *common;
+};
+
+static struct tm6000_reg_settings composite_pal_m[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_pal_nc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_pal[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_secam[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_ntsc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_std_settings composite_stds[] = {
+ { .id = V4L2_STD_PAL_M, .common = composite_pal_m, },
+ { .id = V4L2_STD_PAL_Nc, .common = composite_pal_nc, },
+ { .id = V4L2_STD_PAL, .common = composite_pal, },
+ { .id = V4L2_STD_SECAM, .common = composite_secam, },
+ { .id = V4L2_STD_NTSC, .common = composite_ntsc, },
+};
+
+static struct tm6000_reg_settings svideo_pal_m[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_pal_nc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_pal[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_secam[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_ntsc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 },
+ { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_std_settings svideo_stds[] = {
+ { .id = V4L2_STD_PAL_M, .common = svideo_pal_m, },
+ { .id = V4L2_STD_PAL_Nc, .common = svideo_pal_nc, },
+ { .id = V4L2_STD_PAL, .common = svideo_pal, },
+ { .id = V4L2_STD_SECAM, .common = svideo_secam, },
+ { .id = V4L2_STD_NTSC, .common = svideo_ntsc, },
+};
+
+static int tm6000_set_audio_std(struct tm6000_core *dev)
+{
+ uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */
+ uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */
+ uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */
+ uint8_t nicam_flag = 0; /* No NICAM */
+
+ if (dev->radio) {
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
+ tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c);
+ /* set mono or stereo */
+ if (dev->amode == V4L2_TUNER_MODE_MONO)
+ tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
+ else if (dev->amode == V4L2_TUNER_MODE_STEREO)
+ tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x02);
+ tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18);
+ tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a);
+ tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40);
+ tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe);
+ tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0xff);
+ return 0;
+ }
+
+ switch (tm6010_a_mode) {
+ /* auto */
+ case 0:
+ switch (dev->norm) {
+ case V4L2_STD_NTSC_M_KR:
+ areg_05 |= 0x00;
+ break;
+ case V4L2_STD_NTSC_M_JP:
+ areg_05 |= 0x40;
+ break;
+ case V4L2_STD_NTSC_M:
+ case V4L2_STD_PAL_M:
+ case V4L2_STD_PAL_N:
+ areg_05 |= 0x20;
+ break;
+ case V4L2_STD_PAL_Nc:
+ areg_05 |= 0x60;
+ break;
+ case V4L2_STD_SECAM_L:
+ areg_05 |= 0x00;
+ break;
+ case V4L2_STD_DK:
+ areg_05 |= 0x10;
+ break;
+ }
+ break;
+ /* A2 */
+ case 1:
+ switch (dev->norm) {
+ case V4L2_STD_B:
+ case V4L2_STD_GH:
+ areg_05 = 0x05;
+ break;
+ case V4L2_STD_DK:
+ areg_05 = 0x09;
+ break;
+ }
+ break;
+ /* NICAM */
+ case 2:
+ switch (dev->norm) {
+ case V4L2_STD_B:
+ case V4L2_STD_GH:
+ areg_05 = 0x07;
+ break;
+ case V4L2_STD_DK:
+ areg_05 = 0x06;
+ break;
+ case V4L2_STD_PAL_I:
+ areg_05 = 0x08;
+ break;
+ case V4L2_STD_SECAM_L:
+ areg_05 = 0x0a;
+ areg_02 = 0x02;
+ break;
+ }
+ nicam_flag = 1;
+ break;
+ /* other */
+ case 3:
+ switch (dev->norm) {
+ /* DK3_A2 */
+ case V4L2_STD_DK:
+ areg_05 = 0x0b;
+ break;
+ /* Korea */
+ case V4L2_STD_NTSC_M_KR:
+ areg_05 = 0x04;
+ break;
+ /* EIAJ */
+ case V4L2_STD_NTSC_M_JP:
+ areg_05 = 0x03;
+ break;
+ default:
+ areg_05 = 0x02;
+ break;
+ }
+ break;
+ }
+
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02);
+ tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0);
+ tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05);
+ tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06);
+ tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08);
+ tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91);
+ tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20);
+ tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12);
+ tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20);
+ tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0);
+ tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0);
+ tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12);
+ tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe);
+ tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20);
+ tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14);
+ tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe);
+ tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01);
+ tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0);
+ tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32);
+ tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64);
+ tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20);
+ tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00);
+ tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
+ tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+
+ return 0;
+}
+
+void tm6000_get_std_res(struct tm6000_core *dev)
+{
+ /* Currently, those are the only supported resoltions */
+ if (dev->norm & V4L2_STD_525_60)
+ dev->height = 480;
+ else
+ dev->height = 576;
+
+ dev->width = 720;
+}
+
+static int tm6000_load_std(struct tm6000_core *dev, struct tm6000_reg_settings *set)
+{
+ int i, rc;
+
+ /* Load board's initialization table */
+ for (i = 0; set[i].req; i++) {
+ rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value);
+ if (rc < 0) {
+ printk(KERN_ERR "Error %i while setting "
+ "req %d, reg %d to value %d\n",
+ rc, set[i].req, set[i].reg, set[i].value);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+int tm6000_set_standard(struct tm6000_core *dev)
+{
+ struct tm6000_input *input;
+ int i, rc = 0;
+ u8 reg_07_fe = 0x8a;
+ u8 reg_08_f1 = 0xfc;
+ u8 reg_08_e2 = 0xf0;
+ u8 reg_08_e6 = 0x0f;
+
+ tm6000_get_std_res(dev);
+
+ if (!dev->radio)
+ input = &dev->vinput[dev->input];
+ else
+ input = &dev->rinput;
+
+ if (dev->dev_type == TM6010) {
+ switch (input->vmux) {
+ case TM6000_VMUX_VIDEO_A:
+ tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4);
+ tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1);
+ tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0);
+ tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
+ tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8);
+ reg_07_fe |= 0x01;
+ break;
+ case TM6000_VMUX_VIDEO_B:
+ tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8);
+ tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1);
+ tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0);
+ tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
+ tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8);
+ reg_07_fe |= 0x01;
+ break;
+ case TM6000_VMUX_VIDEO_AB:
+ tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc);
+ tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8);
+ reg_08_e6 = 0x00;
+ tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2);
+ tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0);
+ tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
+ tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe0);
+ break;
+ default:
+ break;
+ }
+ switch (input->amux) {
+ case TM6000_AMUX_ADC1:
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x00, 0x0f);
+ break;
+ case TM6000_AMUX_ADC2:
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x08, 0x0f);
+ break;
+ case TM6000_AMUX_SIF1:
+ reg_08_e2 |= 0x02;
+ reg_08_e6 = 0x08;
+ reg_07_fe |= 0x40;
+ reg_08_f1 |= 0x02;
+ tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3);
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x02, 0x0f);
+ break;
+ case TM6000_AMUX_SIF2:
+ reg_08_e2 |= 0x02;
+ reg_08_e6 = 0x08;
+ reg_07_fe |= 0x40;
+ reg_08_f1 |= 0x02;
+ tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7);
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x02, 0x0f);
+ break;
+ default:
+ break;
+ }
+ tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, reg_08_e2);
+ tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, reg_08_e6);
+ tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, reg_08_f1);
+ tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, reg_07_fe);
+ } else {
+ switch (input->vmux) {
+ case TM6000_VMUX_VIDEO_A:
+ tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10);
+ tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f);
+ tm6000_set_reg(dev,
+ REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0);
+ break;
+ case TM6000_VMUX_VIDEO_B:
+ tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00);
+ tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f);
+ tm6000_set_reg(dev,
+ REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0);
+ break;
+ case TM6000_VMUX_VIDEO_AB:
+ tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10);
+ tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x10);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00);
+ tm6000_set_reg(dev,
+ REQ_03_SET_GET_MCU_PIN, input->v_gpio, 1);
+ break;
+ default:
+ break;
+ }
+ switch (input->amux) {
+ case TM6000_AMUX_ADC1:
+ tm6000_set_reg_mask(dev,
+ TM6000_REQ07_REB_VADC_AADC_MODE, 0x00, 0x0f);
+ break;
+ case TM6000_AMUX_ADC2:
+ tm6000_set_reg_mask(dev,
+ TM6000_REQ07_REB_VADC_AADC_MODE, 0x04, 0x0f);
+ break;
+ default:
+ break;
+ }
+ }
+ if (input->type == TM6000_INPUT_SVIDEO) {
+ for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) {
+ if (dev->norm & svideo_stds[i].id) {
+ rc = tm6000_load_std(dev, svideo_stds[i].common);
+ goto ret;
+ }
+ }
+ return -EINVAL;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(composite_stds); i++) {
+ if (dev->norm & composite_stds[i].id) {
+ rc = tm6000_load_std(dev, composite_stds[i].common);
+ goto ret;
+ }
+ }
+ return -EINVAL;
+ }
+
+ret:
+ if (rc < 0)
+ return rc;
+
+ if ((dev->dev_type == TM6010) &&
+ ((input->amux == TM6000_AMUX_SIF1) ||
+ (input->amux == TM6000_AMUX_SIF2)))
+ tm6000_set_audio_std(dev);
+
+ msleep(40);
+
+ return 0;
+}
diff --git a/drivers/staging/tm6000/tm6000-usb-isoc.h b/drivers/media/video/tm6000/tm6000-usb-isoc.h
index 084c2a8904a3..99d15a55aa03 100644
--- a/drivers/staging/tm6000/tm6000-usb-isoc.h
+++ b/drivers/media/video/tm6000/tm6000-usb-isoc.h
@@ -46,5 +46,5 @@ struct usb_isoc_ctl {
int tmp_buf_len;
/* Stores already requested buffers */
- struct tm6000_buffer *buf;
+ struct tm6000_buffer *buf;
};
diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c
index 4264064a727e..1e5ace0b5d10 100644
--- a/drivers/staging/tm6000/tm6000-video.c
+++ b/drivers/media/video/tm6000/tm6000-video.c
@@ -19,6 +19,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
@@ -30,7 +31,6 @@
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/random.h>
-#include <linux/version.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <media/v4l2-ioctl.h>
@@ -180,9 +180,6 @@ static inline void get_next_buf(struct tm6000_dmaqueue *dma_q,
*buf = list_entry(dma_q->active.next,
struct tm6000_buffer, vb.queue);
- if (!buf)
- return;
-
/* Cleans up buffer - Useful for testing for frame/URB loss */
outp = videobuf_to_vmalloc(&(*buf)->vb);
@@ -206,17 +203,6 @@ static inline void buffer_filled(struct tm6000_core *dev,
wake_up(&buf->vb.done);
}
-const char *tm6000_msg_type[] = {
- "unknown(0)", /* 0 */
- "video", /* 1 */
- "audio", /* 2 */
- "vbi", /* 3 */
- "pts", /* 4 */
- "err", /* 5 */
- "unknown(6)", /* 6 */
- "unknown(7)", /* 7 */
-};
-
/*
* Identify the tm5600/6000 buffer header type and properly handles
*/
@@ -290,17 +276,18 @@ static int copy_streams(u8 *data, unsigned long len,
if (size > TM6000_URB_MSG_LEN)
size = TM6000_URB_MSG_LEN;
pktsize = TM6000_URB_MSG_LEN;
- /* calculate position in buffer
- * and change the buffer
+ /*
+ * calculate position in buffer and change the buffer
*/
switch (cmd) {
case TM6000_URB_MSG_VIDEO:
if (!dev->radio) {
if ((dev->isoc_ctl.vfield != field) &&
(field == 1)) {
- /* Announces that a new buffer
- * were filled
- */
+ /*
+ * Announces that a new buffer
+ * were filled
+ */
buffer_filled(dev, dma_q, vbuf);
dprintk(dev, V4L2_DEBUG_ISOC,
"new buffer filled\n");
@@ -325,7 +312,7 @@ static int copy_streams(u8 *data, unsigned long len,
break;
case TM6000_URB_MSG_AUDIO:
case TM6000_URB_MSG_PTS:
- size = pktsize; /* Size is always 180 bytes */
+ size = pktsize; /* Size is always 180 bytes */
break;
}
} else {
@@ -367,7 +354,8 @@ static int copy_streams(u8 *data, unsigned long len,
}
}
if (ptr + pktsize > endp) {
- /* End of URB packet, but cmd processing is not
+ /*
+ * End of URB packet, but cmd processing is not
* complete. Preserve the state for a next packet
*/
dev->isoc_ctl.pos = pos + cpysize;
@@ -525,9 +513,21 @@ static void tm6000_irq_callback(struct urb *urb)
struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
int i;
- if (!dev)
+ switch (urb->status) {
+ case 0:
+ case -ETIMEDOUT:
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
return;
+ default:
+ tm6000_err("urb completion error %d.\n", urb->status);
+ break;
+ }
+
spin_lock(&dev->slock);
tm6000_isoc_copy(urb);
spin_unlock(&dev->slock);
@@ -754,7 +754,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
struct tm6000_fh *fh = vq->priv_data;
struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
struct tm6000_core *dev = fh->dev;
- int rc = 0, urb_init = 0;
+ int rc = 0;
BUG_ON(NULL == fh->fmt);
@@ -777,15 +777,12 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
}
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- if (0 != (rc = videobuf_iolock(vq, &buf->vb, NULL)))
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc != 0)
goto fail;
- urb_init = 1;
}
- if (!dev->isoc_ctl.num_bufs)
- urb_init = 1;
-
- if (urb_init) {
+ if (!dev->isoc_ctl.num_bufs) {
rc = tm6000_prepare_isoc(dev);
if (rc < 0)
goto fail;
@@ -1038,8 +1035,8 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -1048,29 +1045,30 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
if (!res_get(dev, fh, false))
return -EBUSY;
- return (videobuf_streamon(&fh->vb_vidq));
+ return videobuf_streamon(&fh->vb_vidq);
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
- struct tm6000_fh *fh=priv;
- struct tm6000_core *dev = fh->dev;
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
+
if (i != fh->type)
return -EINVAL;
videobuf_streamoff(&fh->vb_vidq);
- res_free(dev,fh);
+ res_free(dev, fh);
- return (0);
+ return 0;
}
-static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *norm)
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
{
- int rc=0;
- struct tm6000_fh *fh=priv;
+ int rc = 0;
+ struct tm6000_fh *fh = priv;
struct tm6000_core *dev = fh->dev;
dev->norm = *norm;
@@ -1079,7 +1077,7 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *norm)
fh->width = dev->width;
fh->height = dev->height;
- if (rc<0)
+ if (rc < 0)
return rc;
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
@@ -1087,7 +1085,7 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *norm)
return 0;
}
-static const char *iname [] = {
+static const char *iname[] = {
[TM6000_INPUT_TV] = "Television",
[TM6000_INPUT_COMPOSITE1] = "Composite 1",
[TM6000_INPUT_COMPOSITE2] = "Composite 2",
@@ -1394,10 +1392,10 @@ static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
struct tm6000_fh *fh = priv;
struct tm6000_core *dev = fh->dev;
- if (dev->input !=5)
+ if (dev->input != 5)
return -EINVAL;
- *i = dev->input -5;
+ *i = dev->input - 5;
return 0;
}
@@ -1467,9 +1465,6 @@ static int tm6000_open(struct file *file)
int i, rc;
int radio = 0;
- printk(KERN_INFO "tm6000: open called (dev=%s)\n",
- video_device_node_name(vdev));
-
dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n",
video_device_node_name(vdev));
@@ -1508,18 +1503,19 @@ static int tm6000_open(struct file *file)
fh->fmt = format_by_fourcc(dev->fourcc);
- tm6000_get_std_res (dev);
+ tm6000_get_std_res(dev);
- fh->width = dev->width;
- fh->height = dev->height;
+ fh->width = dev->width;
+ fh->height = dev->height;
dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, "
"dev->vidq=0x%08lx\n",
- (unsigned long)fh,(unsigned long)dev,(unsigned long)&dev->vidq);
+ (unsigned long)fh, (unsigned long)dev,
+ (unsigned long)&dev->vidq);
dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty "
- "queued=%d\n",list_empty(&dev->vidq.queued));
+ "queued=%d\n", list_empty(&dev->vidq.queued));
dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty "
- "active=%d\n",list_empty(&dev->vidq.active));
+ "active=%d\n", list_empty(&dev->vidq.active));
/* initialize hardware on analog mode */
rc = tm6000_init_analog_mode(dev);
@@ -1534,13 +1530,13 @@ static int tm6000_open(struct file *file)
dev->mode = TM6000_MODE_ANALOG;
}
- videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops,
- NULL, &dev->slock,
- fh->type,
- V4L2_FIELD_INTERLACED,
- sizeof(struct tm6000_buffer), fh, &dev->lock);
-
- if (fh->radio) {
+ if (!fh->radio) {
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops,
+ NULL, &dev->slock,
+ fh->type,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct tm6000_buffer), fh, &dev->lock);
+ } else {
dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n");
dev->input = 5;
tm6000_set_audio_rinput(dev);
@@ -1557,7 +1553,7 @@ tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos)
{
struct tm6000_fh *fh = file->private_data;
- if (fh->type==V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
if (!res_get(fh->dev, fh, true))
return -EBUSY;
@@ -1583,11 +1579,10 @@ tm6000_poll(struct file *file, struct poll_table_struct *wait)
/* streaming capture */
if (list_empty(&fh->vb_vidq.stream))
return POLLERR;
- buf = list_entry(fh->vb_vidq.stream.next,struct tm6000_buffer,vb.stream);
+ buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream);
} else {
/* read() capture */
- return videobuf_poll_stream(file, &fh->vb_vidq,
- wait);
+ return videobuf_poll_stream(file, &fh->vb_vidq, wait);
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE ||
@@ -1608,9 +1603,18 @@ static int tm6000_release(struct file *file)
dev->users--;
res_free(dev, fh);
+
if (!dev->users) {
+ int err;
+
tm6000_uninit_isoc(dev);
- videobuf_mmap_free(&fh->vb_vidq);
+
+ if (!fh->radio)
+ videobuf_mmap_free(&fh->vb_vidq);
+
+ err = tm6000_reset(dev);
+ if (err < 0)
+ dev_err(&vdev->dev, "reset failed: %d\n", err);
}
kfree(fh);
@@ -1620,22 +1624,19 @@ static int tm6000_release(struct file *file)
static int tm6000_mmap(struct file *file, struct vm_area_struct * vma)
{
- struct tm6000_fh *fh = file->private_data;
- int ret;
-
- ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ struct tm6000_fh *fh = file->private_data;
- return ret;
+ return videobuf_mmap_mapper(&fh->vb_vidq, vma);
}
static struct v4l2_file_operations tm6000_fops = {
- .owner = THIS_MODULE,
- .open = tm6000_open,
- .release = tm6000_release,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .read = tm6000_read,
- .poll = tm6000_poll,
- .mmap = tm6000_mmap,
+ .owner = THIS_MODULE,
+ .open = tm6000_open,
+ .release = tm6000_release,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .read = tm6000_read,
+ .poll = tm6000_poll,
+ .mmap = tm6000_mmap,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
@@ -1696,10 +1697,10 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = {
.vidioc_s_frequency = vidioc_s_frequency,
};
-struct video_device tm6000_radio_template = {
+static struct video_device tm6000_radio_template = {
.name = "tm6000",
.fops = &radio_fops,
- .ioctl_ops = &radio_ioctl_ops,
+ .ioctl_ops = &radio_ioctl_ops,
};
/* -----------------------------------------------------------------
diff --git a/drivers/staging/tm6000/tm6000.h b/drivers/media/video/tm6000/tm6000.h
index ae6369b9a90c..2777e514eff2 100644
--- a/drivers/staging/tm6000/tm6000.h
+++ b/drivers/media/video/tm6000/tm6000.h
@@ -20,9 +20,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-/* Use the tm6000-hack, instead of the proper initialization code i*/
-/* #define HACK 1 */
-
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/videobuf-vmalloc.h>
@@ -31,7 +28,6 @@
#include <linux/mutex.h>
#include <media/v4l2-device.h>
-
#include <linux/dvb/frontend.h>
#include "dvb_demux.h"
#include "dvb_frontend.h"
@@ -173,6 +169,8 @@ struct tm6000_endpoint {
unsigned maxsize;
};
+#define TM6000_QUIRK_NO_USB_DELAY (1 << 0)
+
struct tm6000_core {
/* generic device properties */
char name[30]; /* name (including minor) of the device */
@@ -249,6 +247,7 @@ struct tm6000_core {
/* locks */
struct mutex lock;
+ struct mutex usb_lock;
/* usb transfer */
struct usb_device *udev; /* the usb device */
@@ -263,6 +262,8 @@ struct tm6000_core {
struct usb_isoc_ctl isoc_ctl;
spinlock_t slock;
+
+ unsigned long quirks;
};
enum tm6000_ops_type {
@@ -314,6 +315,7 @@ int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value,
u16 index, u16 mask);
int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep);
int tm6000_init(struct tm6000_core *dev);
+int tm6000_reset(struct tm6000_core *dev);
int tm6000_init_analog_mode(struct tm6000_core *dev);
int tm6000_init_digital_mode(struct tm6000_core *dev);
@@ -382,7 +384,7 @@ extern int tm6000_debug;
#define dprintk(dev, level, fmt, arg...) do {\
if (tm6000_debug & level) \
printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \
- dev->name, __FUNCTION__ , ##arg); } while (0)
+ dev->name, __func__ , ##arg); } while (0)
#define V4L2_DEBUG_REG 0x0004
#define V4L2_DEBUG_I2C 0x0008
@@ -393,4 +395,4 @@ extern int tm6000_debug;
#define tm6000_err(fmt, arg...) do {\
printk(KERN_ERR "tm6000 %s :"fmt, \
- __FUNCTION__ , ##arg); } while (0)
+ __func__ , ##arg); } while (0)
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index cfa9f7efe93d..11cc980b0cd5 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -39,6 +39,7 @@
#include "tda9887.h"
#include "xc5000.h"
#include "tda18271.h"
+#include "xc4000.h"
#define UNSET (-1U)
@@ -391,6 +392,23 @@ static void set_type(struct i2c_client *c, unsigned int type,
tune_now = 0;
break;
}
+ case TUNER_XC4000:
+ {
+ struct xc4000_config xc4000_cfg = {
+ .i2c_address = t->i2c->addr,
+ /* FIXME: the correct parameters will be set */
+ /* only when the digital dvb_attach() occurs */
+ .default_pm = 0,
+ .dvb_amplitude = 0,
+ .set_smoothedcvbs = 0,
+ .if_khz = 0
+ };
+ if (!dvb_attach(xc4000_attach,
+ &t->fe, t->i2c->adapter, &xc4000_cfg))
+ goto attach_failed;
+ tune_now = 0;
+ break;
+ }
default:
if (!dvb_attach(simple_tuner_attach, &t->fe,
t->i2c->adapter, t->i2c->addr, t->type))
@@ -714,10 +732,19 @@ static int tuner_remove(struct i2c_client *client)
* returns 0.
* This function is needed for boards that have a separate tuner for
* radio (like devices with tea5767).
+ * NOTE: mt20xx uses V4L2_TUNER_DIGITAL_TV and calls set_tv_freq to
+ * select a TV frequency. So, t_mode = T_ANALOG_TV could actually
+ * be used to represent a Digital TV too.
*/
static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode)
{
- if ((1 << mode & t->mode_mask) == 0)
+ int t_mode;
+ if (mode == V4L2_TUNER_RADIO)
+ t_mode = T_RADIO;
+ else
+ t_mode = T_ANALOG_TV;
+
+ if ((t_mode & t->mode_mask) == 0)
return -EINVAL;
return 0;
@@ -984,7 +1011,7 @@ static void tuner_status(struct dvb_frontend *fe)
case V4L2_TUNER_RADIO:
p = "radio";
break;
- case V4L2_TUNER_DIGITAL_TV:
+ case V4L2_TUNER_DIGITAL_TV: /* Used by mt20xx */
p = "digital TV";
break;
case V4L2_TUNER_ANALOG_TV:
@@ -1135,9 +1162,8 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
return 0;
if (vt->type == t->mode && analog_ops->get_afc)
vt->afc = analog_ops->get_afc(&t->fe);
- if (vt->type == V4L2_TUNER_ANALOG_TV)
+ if (t->mode != V4L2_TUNER_RADIO) {
vt->capability |= V4L2_TUNER_CAP_NORM;
- if (vt->type != V4L2_TUNER_RADIO) {
vt->rangelow = tv_range[0] * 16;
vt->rangehigh = tv_range[1] * 16;
return 0;
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index c46a3bb95852..f22dbef9b95b 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -1695,14 +1695,17 @@ static int tvaudio_s_ctrl(struct v4l2_subdev *sd,
case V4L2_CID_AUDIO_BALANCE:
{
int volume, balance;
+
if (!(desc->flags & CHIP_HAS_VOLUME))
break;
- volume = max(chip->left,chip->right);
+ volume = max(chip->left, chip->right);
balance = ctrl->value;
+ chip->left = (min(65536 - balance, 32768) * volume) / 32768;
+ chip->right = (min(balance, volume * (__u16)32768)) / 32768;
- chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
- chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
+ chip_write(chip, desc->leftreg, desc->volfunc(chip->left));
+ chip_write(chip, desc->rightreg, desc->volfunc(chip->right));
return 0;
}
diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c
index 9b3e828b0775..926f03931156 100644
--- a/drivers/media/video/tvp514x.c
+++ b/drivers/media/video/tvp514x.c
@@ -32,6 +32,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/videodev2.h>
+#include <linux/module.h>
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
index e927d25e0d35..6abaa16ae136 100644
--- a/drivers/media/video/tvp5150.c
+++ b/drivers/media/video/tvp5150.c
@@ -9,6 +9,7 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <media/v4l2-device.h>
#include <media/tvp5150.h>
#include <media/v4l2-chip-ident.h>
diff --git a/drivers/media/video/tvp5150_reg.h b/drivers/media/video/tvp5150_reg.h
index 4240043c0b2a..25a994944918 100644
--- a/drivers/media/video/tvp5150_reg.h
+++ b/drivers/media/video/tvp5150_reg.h
@@ -45,7 +45,22 @@
/* Reserved 1Fh-27h */
-#define TVP5150_VIDEO_STD 0x28 /* Video standard */
+#define VIDEO_STD_MASK (0x07 >> 1)
+#define TVP5150_VIDEO_STD 0x28 /* Video standard */
+#define VIDEO_STD_AUTO_SWITCH_BIT 0x00
+#define VIDEO_STD_NTSC_MJ_BIT 0x02
+#define VIDEO_STD_PAL_BDGHIN_BIT 0x04
+#define VIDEO_STD_PAL_M_BIT 0x06
+#define VIDEO_STD_PAL_COMBINATION_N_BIT 0x08
+#define VIDEO_STD_NTSC_4_43_BIT 0x0a
+#define VIDEO_STD_SECAM_BIT 0x0c
+
+#define VIDEO_STD_NTSC_MJ_BIT_AS 0x01
+#define VIDEO_STD_PAL_BDGHIN_BIT_AS 0x03
+#define VIDEO_STD_PAL_M_BIT_AS 0x05
+#define VIDEO_STD_PAL_COMBINATION_N_BIT_AS 0x07
+#define VIDEO_STD_NTSC_4_43_BIT_AS 0x09
+#define VIDEO_STD_SECAM_BIT_AS 0x0b
/* Reserved 29h-2bh */
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
index b799851bf3d0..7875e80cb2ff 100644
--- a/drivers/media/video/tvp7002.c
+++ b/drivers/media/video/tvp7002.c
@@ -28,6 +28,7 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
+#include <linux/module.h>
#include <media/tvp7002.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
@@ -128,7 +129,7 @@ static const struct i2c_reg_value tvp7002_init_default[] = {
{ TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE },
{ TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE },
{ TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE },
- { TVP7002_RGB_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE },
+ { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE },
{ TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE },
{ TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
{ 0x32, 0x18, TVP7002_RESERVED },
@@ -182,7 +183,6 @@ static const struct i2c_reg_value tvp7002_parms_480P[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE },
@@ -204,7 +204,6 @@ static const struct i2c_reg_value tvp7002_parms_576P[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE },
@@ -226,7 +225,6 @@ static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
@@ -248,7 +246,6 @@ static const struct i2c_reg_value tvp7002_parms_1080P60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
@@ -270,7 +267,6 @@ static const struct i2c_reg_value tvp7002_parms_1080I50[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
@@ -292,7 +288,6 @@ static const struct i2c_reg_value tvp7002_parms_720P60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE },
@@ -314,7 +309,6 @@ static const struct i2c_reg_value tvp7002_parms_720P50[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE },
@@ -687,6 +681,9 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
u8 cpl_msb;
int index;
+ /* Return invalid preset if no active input is detected */
+ qpreset->preset = V4L2_DV_INVALID;
+
device = to_tvp7002(sd);
/* Read standards from device registers */
@@ -720,8 +717,6 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
if (index == NUM_PRESETS) {
v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n",
lpfr, cpln);
- /* Could not detect a signal, so return the 'invalid' preset */
- qpreset->preset = V4L2_DV_INVALID;
return 0;
}
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
index 0347bbe36459..a514fa61116c 100644
--- a/drivers/media/video/tw9910.c
+++ b/drivers/media/video/tw9910.c
@@ -22,11 +22,13 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-subdev.h>
+
#include <media/soc_camera.h>
#include <media/tw9910.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
#define GET_ID(val) ((val & 0xF8) >> 3)
#define GET_REV(val) (val & 0x07)
@@ -203,6 +205,10 @@
#define RTSEL_FIELD 0x06 /* 0110 = FIELD */
#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */
+/* HSYNC start and end are constant for now */
+#define HSYNC_START 0x0260
+#define HSYNC_END 0x0300
+
/*
* structure
*/
@@ -220,22 +226,11 @@ struct tw9910_scale_ctrl {
u16 vscale;
};
-struct tw9910_cropping_ctrl {
- u16 vdelay;
- u16 vactive;
- u16 hdelay;
- u16 hactive;
-};
-
-struct tw9910_hsync_ctrl {
- u16 start;
- u16 end;
-};
-
struct tw9910_priv {
struct v4l2_subdev subdev;
struct tw9910_video_info *info;
const struct tw9910_scale_ctrl *scale;
+ v4l2_std_id norm;
u32 revision;
};
@@ -329,11 +324,6 @@ static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
},
};
-static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = {
- .start = 0x0260,
- .end = 0x0300,
-};
-
/*
* general function
*/
@@ -378,21 +368,20 @@ static int tw9910_set_scale(struct i2c_client *client,
return ret;
}
-static int tw9910_set_hsync(struct i2c_client *client,
- const struct tw9910_hsync_ctrl *hsync)
+static int tw9910_set_hsync(struct i2c_client *client)
{
struct tw9910_priv *priv = to_tw9910(client);
int ret;
/* bit 10 - 3 */
ret = i2c_smbus_write_byte_data(client, HSBEGIN,
- (hsync->start & 0x07F8) >> 3);
+ (HSYNC_START & 0x07F8) >> 3);
if (ret < 0)
return ret;
/* bit 10 - 3 */
ret = i2c_smbus_write_byte_data(client, HSEND,
- (hsync->end & 0x07F8) >> 3);
+ (HSYNC_END & 0x07F8) >> 3);
if (ret < 0)
return ret;
@@ -400,8 +389,8 @@ static int tw9910_set_hsync(struct i2c_client *client,
/* bit 2 - 0 */
if (1 == priv->revision)
ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
- (hsync->start & 0x0007) << 4 |
- (hsync->end & 0x0007));
+ (HSYNC_START & 0x0007) << 4 |
+ (HSYNC_END & 0x0007));
return ret;
}
@@ -433,12 +422,11 @@ static int tw9910_power(struct i2c_client *client, int enable)
return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
}
-static const struct tw9910_scale_ctrl*
-tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height)
+static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
+ u32 width, u32 height)
{
const struct tw9910_scale_ctrl *scale;
const struct tw9910_scale_ctrl *ret = NULL;
- v4l2_std_id norm = icd->vdev->current_norm;
__u32 diff = 0xffffffff, tmp;
int size, i;
@@ -465,7 +453,7 @@ tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height)
}
/*
- * soc_camera_ops function
+ * subdevice operations
*/
static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
{
@@ -507,57 +495,25 @@ static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
return tw9910_power(client, enable);
}
-static int tw9910_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
+static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- u8 val = VSSL_VVALID | HSSL_DVALID;
-
- /*
- * set OUTCTR1
- *
- * We use VVALID and DVALID signals to control VSYNC and HSYNC
- * outputs, in this mode their polarity is inverted.
- */
- if (flags & SOCAM_HSYNC_ACTIVE_LOW)
- val |= HSP_HI;
-
- if (flags & SOCAM_VSYNC_ACTIVE_LOW)
- val |= VSP_HI;
-
- return i2c_smbus_write_byte_data(client, OUTCTR1, val);
-}
-
-static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
struct tw9910_priv *priv = to_tw9910(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_LOW | SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
- return soc_camera_apply_sensor_flags(icl, flags);
+ *norm = priv->norm;
+
+ return 0;
}
static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
{
- int ret = -EINVAL;
-
- if (norm & (V4L2_STD_NTSC | V4L2_STD_PAL))
- ret = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
- return ret;
-}
+ if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
+ return -EINVAL;
-static int tw9910_enum_input(struct soc_camera_device *icd,
- struct v4l2_input *inp)
-{
- inp->type = V4L2_INPUT_TYPE_TUNER;
- inp->std = V4L2_STD_UNKNOWN;
- strcpy(inp->name, "Video");
+ priv->norm = norm;
return 0;
}
@@ -610,19 +566,17 @@ static int tw9910_s_register(struct v4l2_subdev *sd,
}
#endif
-static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
{
- struct v4l2_rect *rect = &a->c;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- int ret = -EINVAL;
- u8 val;
+ int ret = -EINVAL;
+ u8 val;
/*
* select suitable norm
*/
- priv->scale = tw9910_select_norm(icd, rect->width, rect->height);
+ priv->scale = tw9910_select_norm(priv->norm, *width, *height);
if (!priv->scale)
goto tw9910_set_fmt_error;
@@ -680,14 +634,12 @@ static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
/*
* set hsync
*/
- ret = tw9910_set_hsync(client, &tw9910_hsync_ctrl);
+ ret = tw9910_set_hsync(client);
if (ret < 0)
goto tw9910_set_fmt_error;
- rect->width = priv->scale->width;
- rect->height = priv->scale->height;
- rect->left = 0;
- rect->top = 0;
+ *width = priv->scale->width;
+ *height = priv->scale->height;
return ret;
@@ -704,25 +656,15 @@ static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
- if (!priv->scale) {
- int ret;
- struct v4l2_crop crop = {
- .c = {
- .left = 0,
- .top = 0,
- .width = 640,
- .height = 480,
- },
- };
- ret = tw9910_s_crop(sd, &crop);
- if (ret < 0)
- return ret;
- }
-
a->c.left = 0;
a->c.top = 0;
- a->c.width = priv->scale->width;
- a->c.height = priv->scale->height;
+ if (priv->norm & V4L2_STD_NTSC) {
+ a->c.width = 640;
+ a->c.height = 480;
+ } else {
+ a->c.width = 768;
+ a->c.height = 576;
+ }
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
@@ -730,14 +672,19 @@ static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
a->bounds.left = 0;
a->bounds.top = 0;
- a->bounds.width = 768;
- a->bounds.height = 576;
- a->defrect.left = 0;
- a->defrect.top = 0;
- a->defrect.width = 640;
- a->defrect.height = 480;
+ if (priv->norm & V4L2_STD_NTSC) {
+ a->bounds.width = 640;
+ a->bounds.height = 480;
+ } else {
+ a->bounds.width = 768;
+ a->bounds.height = 576;
+ }
+ a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
@@ -753,15 +700,8 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd,
if (!priv->scale) {
int ret;
- struct v4l2_crop crop = {
- .c = {
- .left = 0,
- .top = 0,
- .width = 640,
- .height = 480,
- },
- };
- ret = tw9910_s_crop(sd, &crop);
+ u32 width = 640, height = 480;
+ ret = tw9910_set_frame(sd, &width, &height);
if (ret < 0)
return ret;
}
@@ -778,17 +718,7 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd,
static int tw9910_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
- /* See tw9910_s_crop() - no proper cropping support */
- struct v4l2_crop a = {
- .c = {
- .left = 0,
- .top = 0,
- .width = mf->width,
- .height = mf->height,
- },
- };
+ u32 width = mf->width, height = mf->height;
int ret;
WARN_ON(mf->field != V4L2_FIELD_ANY &&
@@ -802,10 +732,10 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd,
mf->colorspace = V4L2_COLORSPACE_JPEG;
- ret = tw9910_s_crop(sd, &a);
+ ret = tw9910_set_frame(sd, &width, &height);
if (!ret) {
- mf->width = priv->scale->width;
- mf->height = priv->scale->height;
+ mf->width = width;
+ mf->height = height;
}
return ret;
}
@@ -814,7 +744,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct tw9910_priv *priv = to_tw9910(client);
const struct tw9910_scale_ctrl *scale;
if (V4L2_FIELD_ANY == mf->field) {
@@ -830,7 +760,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd,
/*
* select suitable norm
*/
- scale = tw9910_select_norm(icd, mf->width, mf->height);
+ scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
if (!scale)
return -EINVAL;
@@ -840,21 +770,12 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int tw9910_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int tw9910_video_probe(struct i2c_client *client)
{
struct tw9910_priv *priv = to_tw9910(client);
s32 id;
/*
- * We must have a parent by now. And it cannot be a wrong one.
- * So this entire test is completely redundant.
- */
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
- return -ENODEV;
-
- /*
* tw9910 only use 8 or 16 bit bus width
*/
if (SOCAM_DATAWIDTH_16 != priv->info->buswidth &&
@@ -882,21 +803,15 @@ static int tw9910_video_probe(struct soc_camera_device *icd,
dev_info(&client->dev,
"tw9910 Product ID %0x:%0x\n", id, priv->revision);
- icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL;
- icd->vdev->current_norm = V4L2_STD_NTSC;
+ priv->norm = V4L2_STD_NTSC;
return 0;
}
-static struct soc_camera_ops tw9910_ops = {
- .set_bus_param = tw9910_set_bus_param,
- .query_bus_param = tw9910_query_bus_param,
- .enum_input = tw9910_enum_input,
-};
-
static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
.g_chip_ident = tw9910_g_chip_ident,
.s_std = tw9910_s_std,
+ .g_std = tw9910_g_std,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = tw9910_g_register,
.s_register = tw9910_s_register,
@@ -913,6 +828,45 @@ static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int tw9910_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int tw9910_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ u8 val = VSSL_VVALID | HSSL_DVALID;
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+
+ /*
+ * set OUTCTR1
+ *
+ * We use VVALID and DVALID signals to control VSYNC and HSYNC
+ * outputs, in this mode their polarity is inverted.
+ */
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ val |= HSP_HI;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ val |= VSP_HI;
+
+ return i2c_smbus_write_byte_data(client, OUTCTR1, val);
+}
+
static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
.s_stream = tw9910_s_stream,
.g_mbus_fmt = tw9910_g_fmt,
@@ -920,8 +874,9 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
.try_mbus_fmt = tw9910_try_fmt,
.cropcap = tw9910_cropcap,
.g_crop = tw9910_g_crop,
- .s_crop = tw9910_s_crop,
.enum_mbus_fmt = tw9910_enum_fmt,
+ .g_mbus_config = tw9910_g_mbus_config,
+ .s_mbus_config = tw9910_s_mbus_config,
};
static struct v4l2_subdev_ops tw9910_subdev_ops = {
@@ -937,23 +892,18 @@ static int tw9910_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
- struct tw9910_priv *priv;
- struct tw9910_video_info *info;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct i2c_adapter *adapter =
+ struct tw9910_priv *priv;
+ struct tw9910_video_info *info;
+ struct i2c_adapter *adapter =
to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
- int ret;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
- if (!icd) {
- dev_err(&client->dev, "TW9910: missing soc-camera data!\n");
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "TW9910: missing platform data!\n");
return -EINVAL;
}
- icl = to_soc_camera_link(icd);
- if (!icl || !icl->priv)
- return -EINVAL;
-
info = icl->priv;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -971,14 +921,9 @@ static int tw9910_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
- icd->ops = &tw9910_ops;
- icd->iface = icl->bus_id;
-
- ret = tw9910_video_probe(icd, client);
- if (ret) {
- icd->ops = NULL;
+ ret = tw9910_video_probe(client);
+ if (ret)
kfree(priv);
- }
return ret;
}
@@ -986,9 +931,7 @@ static int tw9910_probe(struct i2c_client *client,
static int tw9910_remove(struct i2c_client *client)
{
struct tw9910_priv *priv = to_tw9910(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
kfree(priv);
return 0;
}
diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile
index 338718750945..aea1e3b5f06b 100644
--- a/drivers/media/video/usbvision/Makefile
+++ b/drivers/media/video/usbvision/Makefile
@@ -2,5 +2,5 @@ usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-
obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c
index 8f5266157f15..3103d0d020e8 100644
--- a/drivers/media/video/usbvision/usbvision-cards.c
+++ b/drivers/media/video/usbvision/usbvision-cards.c
@@ -24,6 +24,7 @@
#include <linux/list.h>
+#include <linux/module.h>
#include <media/v4l2-dev.h>
#include <media/tuner.h>
#include "usbvision.h"
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index ea8ea8a48dfe..5a74f5e07d7d 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -45,7 +45,6 @@
*
*/
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/timer.h>
@@ -77,15 +76,7 @@
#define DRIVER_ALIAS "USBVision"
#define DRIVER_DESC "USBVision USB Video Device Driver for Linux"
#define DRIVER_LICENSE "GPL"
-#define USBVISION_DRIVER_VERSION_MAJOR 0
-#define USBVISION_DRIVER_VERSION_MINOR 9
-#define USBVISION_DRIVER_VERSION_PATCHLEVEL 10
-#define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,\
-USBVISION_DRIVER_VERSION_MINOR,\
-USBVISION_DRIVER_VERSION_PATCHLEVEL)
-#define USBVISION_VERSION_STRING __stringify(USBVISION_DRIVER_VERSION_MAJOR) \
-"." __stringify(USBVISION_DRIVER_VERSION_MINOR) \
-"." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL)
+#define USBVISION_VERSION_STRING "0.9.11"
#define ENABLE_HEXDUMP 0 /* Enable if you need it */
@@ -516,7 +507,6 @@ static int vidioc_querycap(struct file *file, void *priv,
usbvision_device_data[usbvision->dev_model].model_string,
sizeof(vc->card));
usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info));
- vc->version = USBVISION_DRIVER_VERSION;
vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_AUDIO |
V4L2_CAP_READWRITE |
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index a4db26fa2f53..254d32688843 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -20,7 +20,7 @@
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "uvcvideo.h"
@@ -1016,7 +1016,8 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
menu_info = &mapping->menu_info[query_menu->index];
- if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
+ (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
s32 bitmap;
if (!ctrl->cached) {
@@ -1225,7 +1226,8 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
/* Valid menu indices are reported by the GET_RES request for
* UVC controls that support it.
*/
- if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
+ (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
if (!ctrl->cached) {
ret = uvc_ctrl_populate_cache(chain, ctrl);
if (ret < 0)
@@ -1664,8 +1666,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
return -EINVAL;
}
- /* Search for the matching (GUID/CS) control in the given device */
- list_for_each_entry(entity, &dev->entities, list) {
+ /* Search for the matching (GUID/CS) control on the current chain */
+ list_for_each_entry(entity, &chain->entities, chain) {
unsigned int i;
if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT ||
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index b6eae48d7fb8..656d4c9e3b9f 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -31,6 +31,7 @@
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
+#include <linux/version.h>
#include <asm/atomic.h>
#include <asm/unaligned.h>
@@ -113,6 +114,11 @@ static struct uvc_format_desc uvc_fmts[] = {
.guid = UVC_GUID_FORMAT_RGBP,
.fcc = V4L2_PIX_FMT_RGB565,
},
+ {
+ .name = "H.264",
+ .guid = UVC_GUID_FORMAT_H264,
+ .fcc = V4L2_PIX_FMT_H264,
+ },
};
/* ------------------------------------------------------------------------
@@ -1857,7 +1863,7 @@ static int uvc_probe(struct usb_interface *intf,
sizeof(dev->mdev.serial));
strcpy(dev->mdev.bus_info, udev->devpath);
dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
- dev->mdev.driver_version = DRIVER_VERSION_NUMBER;
+ dev->mdev.driver_version = LINUX_VERSION_CODE;
if (media_device_register(&dev->mdev) < 0)
goto error;
@@ -1960,7 +1966,7 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
list_for_each_entry(stream, &dev->streams, list) {
if (stream->intf == intf)
- return uvc_video_resume(stream);
+ return uvc_video_resume(stream, reset);
}
uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
@@ -2130,6 +2136,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
| UVC_QUIRK_BUILTIN_ISIGHT },
+ /* Foxlink ("HP Webcam" on HP Mini 5103) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05c8,
+ .idProduct = 0x0403,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
/* Genesys Logic USB 2.0 PC Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2321,6 +2336,14 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_DEF },
+ /* The Imaging Source USB CCD cameras */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x199e,
+ .idProduct = 0x8102,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
/* Bodelin ProScopeHR */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_DEV_HI
diff --git a/drivers/media/video/uvc/uvc_entity.c b/drivers/media/video/uvc/uvc_entity.c
index 48fea373c25a..29e239911d0e 100644
--- a/drivers/media/video/uvc/uvc_entity.c
+++ b/drivers/media/video/uvc/uvc_entity.c
@@ -49,7 +49,7 @@ static int uvc_mc_register_entity(struct uvc_video_chain *chain,
if (remote == NULL)
return -EINVAL;
- source = (UVC_ENTITY_TYPE(remote) != UVC_TT_STREAMING)
+ source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING)
? (remote->vdev ? &remote->vdev->entity : NULL)
: &remote->subdev.entity;
if (source == NULL)
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index f90ce9fce539..677691c44500 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -19,7 +19,7 @@
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "uvcvideo.h"
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 543a80395b7f..dadf11f704dc 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -21,7 +21,7 @@
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
@@ -32,7 +32,7 @@
* UVC ioctls
*/
static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
- struct uvc_xu_control_mapping *xmap, int old)
+ struct uvc_xu_control_mapping *xmap)
{
struct uvc_control_mapping *map;
unsigned int size;
@@ -58,13 +58,6 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
break;
case V4L2_CTRL_TYPE_MENU:
- if (old) {
- uvc_trace(UVC_TRACE_CONTROL, "V4L2_CTRL_TYPE_MENU not "
- "supported for UVCIOC_CTRL_MAP_OLD.\n");
- ret = -EINVAL;
- goto done;
- }
-
size = xmap->menu_count * sizeof(*map->menu_info);
map->menu_info = kmalloc(size, GFP_KERNEL);
if (map->menu_info == NULL) {
@@ -83,7 +76,7 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
default:
uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type "
"%u.\n", xmap->v4l2_type);
- ret = -EINVAL;
+ ret = -ENOTTY;
goto done;
}
@@ -538,20 +531,6 @@ static int uvc_v4l2_release(struct file *file)
return 0;
}
-static void uvc_v4l2_ioctl_warn(void)
-{
- static int warned;
-
- if (warned)
- return;
-
- uvc_printk(KERN_INFO, "Deprecated UVCIOC_CTRL_{ADD,MAP_OLD,GET,SET} "
- "ioctls will be removed in 2.6.42.\n");
- uvc_printk(KERN_INFO, "See http://www.ideasonboard.org/uvc/upgrade/ "
- "for upgrade instructions.\n");
- warned = 1;
-}
-
static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
@@ -571,7 +550,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
strlcpy(cap->card, vdev->name, sizeof cap->card);
usb_make_path(stream->dev->udev,
cap->bus_info, sizeof(cap->bus_info));
- cap->version = DRIVER_VERSION_NUMBER;
+ cap->version = LINUX_VERSION_CODE;
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_STREAMING;
@@ -1032,37 +1011,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd);
return -EINVAL;
- /* Dynamic controls. UVCIOC_CTRL_ADD, UVCIOC_CTRL_MAP_OLD,
- * UVCIOC_CTRL_GET and UVCIOC_CTRL_SET are deprecated and scheduled for
- * removal in 2.6.42.
- */
- case __UVCIOC_CTRL_ADD:
- uvc_v4l2_ioctl_warn();
- return -EEXIST;
-
- case __UVCIOC_CTRL_MAP_OLD:
- uvc_v4l2_ioctl_warn();
- case __UVCIOC_CTRL_MAP:
case UVCIOC_CTRL_MAP:
- return uvc_ioctl_ctrl_map(chain, arg,
- cmd == __UVCIOC_CTRL_MAP_OLD);
-
- case __UVCIOC_CTRL_GET:
- case __UVCIOC_CTRL_SET:
- {
- struct uvc_xu_control *xctrl = arg;
- struct uvc_xu_control_query xqry = {
- .unit = xctrl->unit,
- .selector = xctrl->selector,
- .query = cmd == __UVCIOC_CTRL_GET
- ? UVC_GET_CUR : UVC_SET_CUR,
- .size = xctrl->size,
- .data = xctrl->data,
- };
-
- uvc_v4l2_ioctl_warn();
- return uvc_xu_ctrl_query(chain, &xqry);
- }
+ return uvc_ioctl_ctrl_map(chain, arg);
case UVCIOC_CTRL_QUERY:
return uvc_xu_ctrl_query(chain, arg);
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index 49994793cc77..b015e8e5e8b0 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -19,7 +19,7 @@
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/unaligned.h>
#include <media/v4l2-common.h>
@@ -790,8 +790,12 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
for (i = 0; i < UVC_URBS; ++i) {
if (stream->urb_buffer[i]) {
+#ifndef CONFIG_DMA_NONCOHERENT
usb_free_coherent(stream->dev->udev, stream->urb_size,
stream->urb_buffer[i], stream->urb_dma[i]);
+#else
+ kfree(stream->urb_buffer[i]);
+#endif
stream->urb_buffer[i] = NULL;
}
}
@@ -831,9 +835,14 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
for (; npackets > 1; npackets /= 2) {
for (i = 0; i < UVC_URBS; ++i) {
stream->urb_size = psize * npackets;
+#ifndef CONFIG_DMA_NONCOHERENT
stream->urb_buffer[i] = usb_alloc_coherent(
stream->dev->udev, stream->urb_size,
gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);
+#else
+ stream->urb_buffer[i] =
+ kmalloc(stream->urb_size, gfp_flags | __GFP_NOWARN);
+#endif
if (!stream->urb_buffer[i]) {
uvc_free_urb_buffers(stream);
break;
@@ -908,10 +917,14 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
urb->context = stream;
urb->pipe = usb_rcvisocpipe(stream->dev->udev,
ep->desc.bEndpointAddress);
+#ifndef CONFIG_DMA_NONCOHERENT
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_dma = stream->urb_dma[i];
+#else
+ urb->transfer_flags = URB_ISO_ASAP;
+#endif
urb->interval = ep->desc.bInterval;
urb->transfer_buffer = stream->urb_buffer[i];
- urb->transfer_dma = stream->urb_dma[i];
urb->complete = uvc_video_complete;
urb->number_of_packets = npackets;
urb->transfer_buffer_length = size;
@@ -969,8 +982,10 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
usb_fill_bulk_urb(urb, stream->dev->udev, pipe,
stream->urb_buffer[i], size, uvc_video_complete,
stream);
+#ifndef CONFIG_DMA_NONCOHERENT
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = stream->urb_dma[i];
+#endif
stream->urb[i] = urb;
}
@@ -1104,10 +1119,18 @@ int uvc_video_suspend(struct uvc_streaming *stream)
* buffers, making sure userspace applications are notified of the problem
* instead of waiting forever.
*/
-int uvc_video_resume(struct uvc_streaming *stream)
+int uvc_video_resume(struct uvc_streaming *stream, int reset)
{
int ret;
+ /* If the bus has been reset on resume, set the alternate setting to 0.
+ * This should be the default value, but some devices crash or otherwise
+ * misbehave if they don't receive a SET_INTERFACE request before any
+ * other video control request.
+ */
+ if (reset)
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+
stream->frozen = 0;
ret = uvc_commit_video(stream, &stream->ctrl);
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 20107fd3574d..4c1392ebcd4b 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -1,106 +1,16 @@
#ifndef _USB_VIDEO_H_
#define _USB_VIDEO_H_
-#include <linux/kernel.h>
-#include <linux/videodev2.h>
-
-#ifndef __KERNEL__
-/*
- * This header provides binary compatibility with applications using the private
- * uvcvideo API. This API is deprecated and will be removed in 2.6.42.
- * Applications should be recompiled against the public linux/uvcvideo.h header.
- */
-#warn "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
-
-/*
- * Dynamic controls
- */
-
-/* Data types for UVC control data */
-#define UVC_CTRL_DATA_TYPE_RAW 0
-#define UVC_CTRL_DATA_TYPE_SIGNED 1
-#define UVC_CTRL_DATA_TYPE_UNSIGNED 2
-#define UVC_CTRL_DATA_TYPE_BOOLEAN 3
-#define UVC_CTRL_DATA_TYPE_ENUM 4
-#define UVC_CTRL_DATA_TYPE_BITMASK 5
-
-/* Control flags */
-#define UVC_CONTROL_SET_CUR (1 << 0)
-#define UVC_CONTROL_GET_CUR (1 << 1)
-#define UVC_CONTROL_GET_MIN (1 << 2)
-#define UVC_CONTROL_GET_MAX (1 << 3)
-#define UVC_CONTROL_GET_RES (1 << 4)
-#define UVC_CONTROL_GET_DEF (1 << 5)
-#define UVC_CONTROL_RESTORE (1 << 6)
-#define UVC_CONTROL_AUTO_UPDATE (1 << 7)
-
-#define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \
- UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \
- UVC_CONTROL_GET_DEF)
-
-struct uvc_menu_info {
- __u32 value;
- __u8 name[32];
-};
-
-struct uvc_xu_control_mapping {
- __u32 id;
- __u8 name[32];
- __u8 entity[16];
- __u8 selector;
-
- __u8 size;
- __u8 offset;
- __u32 v4l2_type;
- __u32 data_type;
-
- struct uvc_menu_info __user *menu_info;
- __u32 menu_count;
-
- __u32 reserved[4];
-};
-
-#endif
-
-struct uvc_xu_control_info {
- __u8 entity[16];
- __u8 index;
- __u8 selector;
- __u16 size;
- __u32 flags;
-};
-
-struct uvc_xu_control_mapping_old {
- __u8 reserved[64];
-};
-
-struct uvc_xu_control {
- __u8 unit;
- __u8 selector;
- __u16 size;
- __u8 __user *data;
-};
-
#ifndef __KERNEL__
-#define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info)
-#define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old)
-#define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping)
-#define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control)
-#define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control)
-#else
-#define __UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info)
-#define __UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old)
-#define __UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping)
-#define __UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control)
-#define __UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control)
-#endif
-
-#ifdef __KERNEL__
+#error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
+#endif /* __KERNEL__ */
+#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/usb.h>
#include <linux/usb/video.h>
#include <linux/uvcvideo.h>
+#include <linux/videodev2.h>
#include <media/media-device.h>
#include <media/v4l2-device.h>
@@ -179,12 +89,15 @@ struct uvc_xu_control {
{ 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_H264 \
+ { 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+
/* ------------------------------------------------------------------------
* Driver specific constants.
*/
-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(1, 1, 0)
-#define DRIVER_VERSION "v1.1.0"
+#define DRIVER_VERSION "1.1.1"
/* Number of isochronous URBs. */
#define UVC_URBS 5
@@ -639,7 +552,7 @@ extern void uvc_mc_cleanup_entity(struct uvc_entity *entity);
/* Video */
extern int uvc_video_init(struct uvc_streaming *stream);
extern int uvc_video_suspend(struct uvc_streaming *stream);
-extern int uvc_video_resume(struct uvc_streaming *stream);
+extern int uvc_video_resume(struct uvc_streaming *stream, int reset);
extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
extern int uvc_probe_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe);
@@ -699,6 +612,4 @@ extern struct usb_host_endpoint *uvc_find_endpoint(
void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
struct uvc_buffer *buf);
-#endif /* __KERNEL__ */
-
#endif
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 06b9f9f82013..5c6100fb4072 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -105,6 +105,9 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
menu_items[ctrl->value][0] == '\0')
return -EINVAL;
}
+ if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
+ (ctrl->value & ~qctrl->maximum))
+ return -ERANGE;
return 0;
}
EXPORT_SYMBOL(v4l2_ctrl_check);
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index 7c2694738b31..c68531b88279 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -159,11 +159,25 @@ struct v4l2_format32 {
} fmt;
};
-static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+/**
+ * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument
+ * @index: on return, index of the first created buffer
+ * @count: entry: number of requested buffers,
+ * return: number of created buffers
+ * @memory: buffer memory type
+ * @format: frame format, for which buffers are requested
+ * @reserved: future extensions
+ */
+struct v4l2_create_buffers32 {
+ __u32 index;
+ __u32 count;
+ enum v4l2_memory memory;
+ struct v4l2_format32 format;
+ __u32 reserved[8];
+};
+
+static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
{
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
- get_user(kp->type, &up->type))
- return -EFAULT;
switch (kp->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
@@ -192,11 +206,24 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
}
}
-static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
+ get_user(kp->type, &up->type))
+ return -EFAULT;
+ return __get_v4l2_format32(kp, up);
+}
+
+static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
+ copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt)))
+ return -EFAULT;
+ return __get_v4l2_format32(&kp->format, &up->format);
+}
+
+static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
{
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
- put_user(kp->type, &up->type))
- return -EFAULT;
switch (kp->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
@@ -225,6 +252,22 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
}
}
+static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
+ put_user(kp->type, &up->type))
+ return -EFAULT;
+ return __put_v4l2_format32(kp, up);
+}
+
+static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
+ copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
+ return -EFAULT;
+ return __put_v4l2_format32(&kp->format, &up->format);
+}
+
struct v4l2_standard32 {
__u32 index;
__u32 id[2]; /* __u64 would get the alignment wrong */
@@ -662,6 +705,32 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
return 0;
}
+struct v4l2_event32 {
+ __u32 type;
+ union {
+ __u8 data[64];
+ } u;
+ __u32 pending;
+ __u32 sequence;
+ struct compat_timespec timestamp;
+ __u32 id;
+ __u32 reserved[8];
+};
+
+static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
+ put_user(kp->type, &up->type) ||
+ copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
+ put_user(kp->pending, &up->pending) ||
+ put_user(kp->sequence, &up->sequence) ||
+ put_compat_timespec(&kp->timestamp, &up->timestamp) ||
+ put_user(kp->id, &up->id) ||
+ copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
+ return -EFAULT;
+ return 0;
+}
+
#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32)
#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32)
#define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32)
@@ -675,6 +744,9 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32)
#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32)
#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32)
+#define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32)
+#define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32)
+#define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32)
#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32)
#define VIDIOC_STREAMON32 _IOW ('V', 18, s32)
@@ -693,6 +765,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
struct v4l2_input v2i;
struct v4l2_standard v2s;
struct v4l2_ext_controls v2ecs;
+ struct v4l2_event v2ev;
+ struct v4l2_create_buffers v2crt;
unsigned long vx;
int vi;
} karg;
@@ -715,6 +789,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break;
case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break;
case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break;
+ case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break;
case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
@@ -722,6 +797,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break;
case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
+ case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break;
+ case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break;
}
switch (cmd) {
@@ -746,6 +823,12 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
compatible_arg = 0;
break;
+ case VIDIOC_CREATE_BUFS:
+ err = get_v4l2_create32(&karg.v2crt, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_PREPARE_BUF:
case VIDIOC_QUERYBUF:
case VIDIOC_QBUF:
case VIDIOC_DQBUF:
@@ -778,6 +861,9 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
err = get_v4l2_ext_controls32(&karg.v2ecs, up);
compatible_arg = 0;
break;
+ case VIDIOC_DQEVENT:
+ compatible_arg = 0;
+ break;
}
if (err)
return err;
@@ -818,12 +904,20 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
err = put_v4l2_framebuffer32(&karg.v2fb, up);
break;
+ case VIDIOC_DQEVENT:
+ err = put_v4l2_event32(&karg.v2ev, up);
+ break;
+
case VIDIOC_G_FMT:
case VIDIOC_S_FMT:
case VIDIOC_TRY_FMT:
err = put_v4l2_format32(&karg.v2f, up);
break;
+ case VIDIOC_CREATE_BUFS:
+ err = put_v4l2_create32(&karg.v2crt, up);
+ break;
+
case VIDIOC_QUERYBUF:
case VIDIOC_QBUF:
case VIDIOC_DQBUF:
@@ -920,8 +1014,11 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOC_S_DV_TIMINGS:
case VIDIOC_G_DV_TIMINGS:
case VIDIOC_DQEVENT:
+ case VIDIOC_DQEVENT32:
case VIDIOC_SUBSCRIBE_EVENT:
case VIDIOC_UNSUBSCRIBE_EVENT:
+ case VIDIOC_CREATE_BUFS32:
+ case VIDIOC_PREPARE_BUF32:
ret = do_video_ioctl(file, cmd, arg);
break;
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 2412f08527aa..0f415dade05a 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -20,20 +20,43 @@
#include <linux/ctype.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-dev.h>
+#define has_op(master, op) \
+ (master->ops && master->ops->op)
+#define call_op(master, op) \
+ (has_op(master, op) ? master->ops->op(master) : 0)
+
/* Internal temporary helper struct, one for each v4l2_ext_control */
-struct ctrl_helper {
+struct v4l2_ctrl_helper {
+ /* Pointer to the control reference of the master control */
+ struct v4l2_ctrl_ref *mref;
/* The control corresponding to the v4l2_ext_control ID field. */
struct v4l2_ctrl *ctrl;
- /* Used internally to mark whether this control was already
- processed. */
- bool handled;
+ /* v4l2_ext_control index of the next control belonging to the
+ same cluster, or 0 if there isn't any. */
+ u32 next;
};
+/* Small helper function to determine if the autocluster is set to manual
+ mode. */
+static bool is_cur_manual(const struct v4l2_ctrl *master)
+{
+ return master->is_auto && master->cur.val == master->manual_mode_value;
+}
+
+/* Same as above, but this checks the against the new value instead of the
+ current value. */
+static bool is_new_manual(const struct v4l2_ctrl *master)
+{
+ return master->is_auto && master->val == master->manual_mode_value;
+}
+
/* Returns NULL or a character pointer array containing the menu for
the given control ID. The pointer array ends with a NULL pointer.
An empty string signifies a menu entry that is invalid. This allows
@@ -181,13 +204,14 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
};
static const char * const mpeg_stream_vbi_fmt[] = {
"No VBI",
- "Private packet, IVTV format",
+ "Private Packet, IVTV Format",
NULL
};
static const char * const camera_power_line_frequency[] = {
"Disabled",
"50 Hz",
"60 Hz",
+ "Auto",
NULL
};
static const char * const camera_exposure_auto[] = {
@@ -204,18 +228,130 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
"Negative",
"Emboss",
"Sketch",
- "Sky blue",
- "Grass green",
- "Skin whiten",
+ "Sky Blue",
+ "Grass Green",
+ "Skin Whiten",
"Vivid",
NULL
};
static const char * const tune_preemphasis[] = {
- "No preemphasis",
+ "No Preemphasis",
"50 useconds",
"75 useconds",
NULL,
};
+ static const char * const header_mode[] = {
+ "Separate Buffer",
+ "Joined With 1st Frame",
+ NULL,
+ };
+ static const char * const multi_slice[] = {
+ "Single",
+ "Max Macroblocks",
+ "Max Bytes",
+ NULL,
+ };
+ static const char * const entropy_mode[] = {
+ "CAVLC",
+ "CABAC",
+ NULL,
+ };
+ static const char * const mpeg_h264_level[] = {
+ "1",
+ "1b",
+ "1.1",
+ "1.2",
+ "1.3",
+ "2",
+ "2.1",
+ "2.2",
+ "3",
+ "3.1",
+ "3.2",
+ "4",
+ "4.1",
+ "4.2",
+ "5",
+ "5.1",
+ NULL,
+ };
+ static const char * const h264_loop_filter[] = {
+ "Enabled",
+ "Disabled",
+ "Disabled at Slice Boundary",
+ NULL,
+ };
+ static const char * const h264_profile[] = {
+ "Baseline",
+ "Constrained Baseline",
+ "Main",
+ "Extended",
+ "High",
+ "High 10",
+ "High 422",
+ "High 444 Predictive",
+ "High 10 Intra",
+ "High 422 Intra",
+ "High 444 Intra",
+ "CAVLC 444 Intra",
+ "Scalable Baseline",
+ "Scalable High",
+ "Scalable High Intra",
+ "Multiview High",
+ NULL,
+ };
+ static const char * const vui_sar_idc[] = {
+ "Unspecified",
+ "1:1",
+ "12:11",
+ "10:11",
+ "16:11",
+ "40:33",
+ "24:11",
+ "20:11",
+ "32:11",
+ "80:33",
+ "18:11",
+ "15:11",
+ "64:33",
+ "160:99",
+ "4:3",
+ "3:2",
+ "2:1",
+ "Extended SAR",
+ NULL,
+ };
+ static const char * const mpeg_mpeg4_level[] = {
+ "0",
+ "0b",
+ "1",
+ "2",
+ "3",
+ "3b",
+ "4",
+ "5",
+ NULL,
+ };
+ static const char * const mpeg4_profile[] = {
+ "Simple",
+ "Adcanved Simple",
+ "Core",
+ "Simple Scalable",
+ "Advanced Coding Efficency",
+ NULL,
+ };
+
+ static const char * const flash_led_mode[] = {
+ "Off",
+ "Flash",
+ "Torch",
+ NULL,
+ };
+ static const char * const flash_strobe_source[] = {
+ "Software",
+ "External",
+ NULL,
+ };
switch (id) {
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
@@ -256,6 +392,28 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
return colorfx;
case V4L2_CID_TUNE_PREEMPHASIS:
return tune_preemphasis;
+ case V4L2_CID_FLASH_LED_MODE:
+ return flash_led_mode;
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ return flash_strobe_source;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ return header_mode;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ return multi_slice;
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ return entropy_mode;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ return mpeg_h264_level;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ return h264_loop_filter;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ return h264_profile;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ return vui_sar_idc;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ return mpeg_mpeg4_level;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ return mpeg4_profile;
default:
return NULL;
}
@@ -307,6 +465,8 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_CHROMA_GAIN: return "Chroma Gain";
case V4L2_CID_ILLUMINATORS_1: return "Illuminator 1";
case V4L2_CID_ILLUMINATORS_2: return "Illuminator 2";
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Minimum Number of Capture Buffers";
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Minimum Number of Output Buffers";
/* MPEG controls */
/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -343,6 +503,48 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute";
case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV";
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: return "Decoder Slice Interface";
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: return "MPEG4 Loop Filter Enable";
+ case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: return "The Number of Intra Refresh MBs";
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: return "Frame Level Rate Control Enable";
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: return "H264 MB Level Rate Control";
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE: return "Sequence Header Mode";
+ case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC: return "The Max Number of Reference Picture";
+ case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_MIN_QP: return "H263 Minimum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_MAX_QP: return "H263 Maximum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: return "H264 I-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: return "H264 P frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: return "H264 B frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: return "H264 Maximum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: return "H264 Minimum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: return "H264 8x8 Transform Enable";
+ case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: return "H264 CPB Buffer Size";
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: return "H264 Entorpy Mode";
+ case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: return "H264 I Period";
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL: return "H264 Level";
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: return "H264 Loop Filter Alpha Offset";
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: return "H264 Loop Filter Beta Offset";
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: return "H264 Loop Filter Mode";
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE: return "H264 Profile";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT: return "Vertical Size of SAR";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH: return "Horizontal Size of SAR";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: return "Aspect Ratio VUI Enable";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: return "VUI Aspect Ratio IDC";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: return "MPEG4 Minimum QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: return "MPEG4 Maximum QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: return "MPEG4 Level";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return "MPEG4 Profile";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: return "Quarter Pixel Search Enable";
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: return "The Maximum Bytes Per Slice";
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "The Number of MB in a Slice";
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "The Slice Partitioning Method";
+ case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size";
/* CAMERA controls */
/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -389,6 +591,21 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
+ /* Flash controls */
+ case V4L2_CID_FLASH_CLASS: return "Flash controls";
+ case V4L2_CID_FLASH_LED_MODE: return "LED mode";
+ case V4L2_CID_FLASH_STROBE_SOURCE: return "Strobe source";
+ case V4L2_CID_FLASH_STROBE: return "Strobe";
+ case V4L2_CID_FLASH_STROBE_STOP: return "Stop strobe";
+ case V4L2_CID_FLASH_STROBE_STATUS: return "Strobe status";
+ case V4L2_CID_FLASH_TIMEOUT: return "Strobe timeout";
+ case V4L2_CID_FLASH_INTENSITY: return "Intensity, flash mode";
+ case V4L2_CID_FLASH_TORCH_INTENSITY: return "Intensity, torch mode";
+ case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, indicator";
+ case V4L2_CID_FLASH_FAULT: return "Faults";
+ case V4L2_CID_FLASH_CHARGE: return "Charge";
+ case V4L2_CID_FLASH_READY: return "Ready to strobe";
+
default:
return NULL;
}
@@ -423,12 +640,24 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_PILOT_TONE_ENABLED:
case V4L2_CID_ILLUMINATORS_1:
case V4L2_CID_ILLUMINATORS_2:
+ case V4L2_CID_FLASH_STROBE_STATUS:
+ case V4L2_CID_FLASH_CHARGE:
+ case V4L2_CID_FLASH_READY:
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
*type = V4L2_CTRL_TYPE_BOOLEAN;
*min = 0;
*max = *step = 1;
break;
case V4L2_CID_PAN_RESET:
case V4L2_CID_TILT_RESET:
+ case V4L2_CID_FLASH_STROBE:
+ case V4L2_CID_FLASH_STROBE_STOP:
*type = V4L2_CTRL_TYPE_BUTTON;
*flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
*min = *max = *step = *def = 0;
@@ -452,6 +681,17 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_EXPOSURE_AUTO:
case V4L2_CID_COLORFX:
case V4L2_CID_TUNE_PREEMPHASIS:
+ case V4L2_CID_FLASH_LED_MODE:
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
*type = V4L2_CTRL_TYPE_MENU;
break;
case V4L2_CID_RDS_TX_PS_NAME:
@@ -462,6 +702,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_CAMERA_CLASS:
case V4L2_CID_MPEG_CLASS:
case V4L2_CID_FM_TX_CLASS:
+ case V4L2_CID_FLASH_CLASS:
*type = V4L2_CTRL_TYPE_CTRL_CLASS;
/* You can neither read not write these */
*flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
@@ -474,6 +715,14 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
/* Max is calculated as RGB888 that is 2^24 */
*max = 0xFFFFFF;
break;
+ case V4L2_CID_FLASH_FAULT:
+ *type = V4L2_CTRL_TYPE_BITMASK;
+ break;
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
default:
*type = V4L2_CTRL_TYPE_INTEGER;
break;
@@ -519,6 +768,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_ZOOM_RELATIVE:
*flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
break;
+ case V4L2_CID_FLASH_STROBE_STATUS:
+ case V4L2_CID_FLASH_READY:
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
}
}
EXPORT_SYMBOL(v4l2_ctrl_fill);
@@ -537,6 +790,42 @@ static bool type_is_int(const struct v4l2_ctrl *ctrl)
}
}
+static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes)
+{
+ memset(ev->reserved, 0, sizeof(ev->reserved));
+ ev->type = V4L2_EVENT_CTRL;
+ ev->id = ctrl->id;
+ ev->u.ctrl.changes = changes;
+ ev->u.ctrl.type = ctrl->type;
+ ev->u.ctrl.flags = ctrl->flags;
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING)
+ ev->u.ctrl.value64 = 0;
+ else
+ ev->u.ctrl.value64 = ctrl->cur.val64;
+ ev->u.ctrl.minimum = ctrl->minimum;
+ ev->u.ctrl.maximum = ctrl->maximum;
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU)
+ ev->u.ctrl.step = 1;
+ else
+ ev->u.ctrl.step = ctrl->step;
+ ev->u.ctrl.default_value = ctrl->default_value;
+}
+
+static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
+{
+ struct v4l2_event ev;
+ struct v4l2_subscribed_event *sev;
+
+ if (list_empty(&ctrl->ev_subs))
+ return;
+ fill_event(&ev, ctrl, changes);
+
+ list_for_each_entry(sev, &ctrl->ev_subs, node)
+ if (sev->fh != fh ||
+ (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))
+ v4l2_event_queue_fh(sev->fh, &ev);
+}
+
/* Helper function: copy the current control value back to the caller */
static int cur_to_user(struct v4l2_ext_control *c,
struct v4l2_ctrl *ctrl)
@@ -624,22 +913,51 @@ static int new_to_user(struct v4l2_ext_control *c,
}
/* Copy the new value to the current value. */
-static void new_to_cur(struct v4l2_ctrl *ctrl)
+static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
+ bool update_inactive)
{
+ bool changed = false;
+
if (ctrl == NULL)
return;
switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BUTTON:
+ changed = true;
+ break;
case V4L2_CTRL_TYPE_STRING:
/* strings are always 0-terminated */
+ changed = strcmp(ctrl->string, ctrl->cur.string);
strcpy(ctrl->cur.string, ctrl->string);
break;
case V4L2_CTRL_TYPE_INTEGER64:
+ changed = ctrl->val64 != ctrl->cur.val64;
ctrl->cur.val64 = ctrl->val64;
break;
default:
+ changed = ctrl->val != ctrl->cur.val;
ctrl->cur.val = ctrl->val;
break;
}
+ if (update_inactive) {
+ /* Note: update_inactive can only be true for auto clusters. */
+ ctrl->flags &=
+ ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE);
+ if (!is_cur_manual(ctrl->cluster[0])) {
+ ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (ctrl->cluster[0]->has_volatiles)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+ fh = NULL;
+ }
+ if (changed || update_inactive) {
+ /* If a control was changed that was not one of the controls
+ modified by the application, then send the event to all. */
+ if (!ctrl->is_new)
+ fh = NULL;
+ send_event(fh, ctrl,
+ (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) |
+ (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0));
+ }
}
/* Copy the current value to the new value */
@@ -692,13 +1010,11 @@ static int cluster_changed(struct v4l2_ctrl *master)
return diff;
}
-/* Validate a new control */
-static int validate_new(struct v4l2_ctrl *ctrl)
+/* Validate integer-type control */
+static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval)
{
- s32 val = ctrl->val;
- char *s = ctrl->string;
+ s32 val = *pval;
u32 offset;
- size_t len;
switch (ctrl->type) {
case V4L2_CTRL_TYPE_INTEGER:
@@ -711,11 +1027,11 @@ static int validate_new(struct v4l2_ctrl *ctrl)
offset = val - ctrl->minimum;
offset = ctrl->step * (offset / ctrl->step);
val = ctrl->minimum + offset;
- ctrl->val = val;
+ *pval = val;
return 0;
case V4L2_CTRL_TYPE_BOOLEAN:
- ctrl->val = !!ctrl->val;
+ *pval = !!val;
return 0;
case V4L2_CTRL_TYPE_MENU:
@@ -726,11 +1042,35 @@ static int validate_new(struct v4l2_ctrl *ctrl)
return -EINVAL;
return 0;
+ case V4L2_CTRL_TYPE_BITMASK:
+ *pval &= ctrl->maximum;
+ return 0;
+
case V4L2_CTRL_TYPE_BUTTON:
case V4L2_CTRL_TYPE_CTRL_CLASS:
- ctrl->val64 = 0;
+ *pval = 0;
return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/* Validate a new control */
+static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)
+{
+ char *s = c->string;
+ size_t len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_BITMASK:
+ case V4L2_CTRL_TYPE_BUTTON:
+ case V4L2_CTRL_TYPE_CTRL_CLASS:
+ return validate_new_int(ctrl, &c->value);
+
case V4L2_CTRL_TYPE_INTEGER64:
return 0;
@@ -780,6 +1120,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
{
struct v4l2_ctrl_ref *ref, *next_ref;
struct v4l2_ctrl *ctrl, *next_ctrl;
+ struct v4l2_subscribed_event *sev, *next_sev;
if (hdl == NULL || hdl->buckets == NULL)
return;
@@ -793,6 +1134,8 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
/* Free all controls owned by the handler */
list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
list_del(&ctrl->node);
+ list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
+ list_del(&sev->node);
kfree(ctrl);
}
kfree(hdl->buckets);
@@ -962,13 +1305,17 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
/* Sanity checks */
if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
- max < min ||
(type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
+ (type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
(type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
(type == V4L2_CTRL_TYPE_STRING && max == 0)) {
handler_set_err(hdl, -ERANGE);
return NULL;
}
+ if (type != V4L2_CTRL_TYPE_BITMASK && max < min) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
if ((type == V4L2_CTRL_TYPE_INTEGER ||
type == V4L2_CTRL_TYPE_MENU ||
type == V4L2_CTRL_TYPE_BOOLEAN) &&
@@ -976,6 +1323,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
handler_set_err(hdl, -ERANGE);
return NULL;
}
+ if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
if (type == V4L2_CTRL_TYPE_BUTTON)
flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
@@ -991,6 +1342,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
}
INIT_LIST_HEAD(&ctrl->node);
+ INIT_LIST_HEAD(&ctrl->ev_subs);
ctrl->handler = hdl;
ctrl->ops = ops;
ctrl->id = id;
@@ -1050,10 +1402,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
type, min, max,
is_menu ? cfg->menu_skip_mask : step,
def, flags, qmenu, priv);
- if (ctrl) {
+ if (ctrl)
ctrl->is_private = cfg->is_private;
- ctrl->is_volatile = cfg->is_volatile;
- }
return ctrl;
}
EXPORT_SYMBOL(v4l2_ctrl_new_custom);
@@ -1132,6 +1482,9 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
/* Skip handler-private controls. */
if (ctrl->is_private)
continue;
+ /* And control classes */
+ if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ continue;
ret = handler_new_ref(hdl, ctrl);
if (ret)
break;
@@ -1144,32 +1497,68 @@ EXPORT_SYMBOL(v4l2_ctrl_add_handler);
/* Cluster controls */
void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
{
+ bool has_volatiles = false;
int i;
/* The first control is the master control and it must not be NULL */
- BUG_ON(controls[0] == NULL);
+ BUG_ON(ncontrols == 0 || controls[0] == NULL);
for (i = 0; i < ncontrols; i++) {
if (controls[i]) {
controls[i]->cluster = controls;
controls[i]->ncontrols = ncontrols;
+ if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE)
+ has_volatiles = true;
}
}
+ controls[0]->has_volatiles = has_volatiles;
}
EXPORT_SYMBOL(v4l2_ctrl_cluster);
+void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
+ u8 manual_val, bool set_volatile)
+{
+ struct v4l2_ctrl *master = controls[0];
+ u32 flag = 0;
+ int i;
+
+ v4l2_ctrl_cluster(ncontrols, controls);
+ WARN_ON(ncontrols <= 1);
+ WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
+ WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl));
+ master->is_auto = true;
+ master->has_volatiles = set_volatile;
+ master->manual_mode_value = manual_val;
+ master->flags |= V4L2_CTRL_FLAG_UPDATE;
+
+ if (!is_cur_manual(master))
+ flag = V4L2_CTRL_FLAG_INACTIVE |
+ (set_volatile ? V4L2_CTRL_FLAG_VOLATILE : 0);
+
+ for (i = 1; i < ncontrols; i++)
+ if (controls[i])
+ controls[i]->flags |= flag;
+}
+EXPORT_SYMBOL(v4l2_ctrl_auto_cluster);
+
/* Activate/deactivate a control. */
void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
{
+ /* invert since the actual flag is called 'inactive' */
+ bool inactive = !active;
+ bool old;
+
if (ctrl == NULL)
return;
- if (!active)
+ if (inactive)
/* set V4L2_CTRL_FLAG_INACTIVE */
- set_bit(4, &ctrl->flags);
+ old = test_and_set_bit(4, &ctrl->flags);
else
/* clear V4L2_CTRL_FLAG_INACTIVE */
- clear_bit(4, &ctrl->flags);
+ old = test_and_clear_bit(4, &ctrl->flags);
+ if (old != inactive)
+ send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
}
EXPORT_SYMBOL(v4l2_ctrl_activate);
@@ -1181,15 +1570,21 @@ EXPORT_SYMBOL(v4l2_ctrl_activate);
these controls. */
void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
{
+ bool old;
+
if (ctrl == NULL)
return;
+ v4l2_ctrl_lock(ctrl);
if (grabbed)
/* set V4L2_CTRL_FLAG_GRABBED */
- set_bit(1, &ctrl->flags);
+ old = test_and_set_bit(1, &ctrl->flags);
else
/* clear V4L2_CTRL_FLAG_GRABBED */
- clear_bit(1, &ctrl->flags);
+ old = test_and_clear_bit(1, &ctrl->flags);
+ if (old != grabbed)
+ send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
+ v4l2_ctrl_unlock(ctrl);
}
EXPORT_SYMBOL(v4l2_ctrl_grab);
@@ -1197,9 +1592,6 @@ EXPORT_SYMBOL(v4l2_ctrl_grab);
static void log_ctrl(const struct v4l2_ctrl *ctrl,
const char *prefix, const char *colon)
{
- int fl_inact = ctrl->flags & V4L2_CTRL_FLAG_INACTIVE;
- int fl_grabbed = ctrl->flags & V4L2_CTRL_FLAG_GRABBED;
-
if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
return;
if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
@@ -1217,6 +1609,9 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl,
case V4L2_CTRL_TYPE_MENU:
printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
break;
+ case V4L2_CTRL_TYPE_BITMASK:
+ printk(KERN_CONT "0x%08x", ctrl->cur.val);
+ break;
case V4L2_CTRL_TYPE_INTEGER64:
printk(KERN_CONT "%lld", ctrl->cur.val64);
break;
@@ -1227,14 +1622,17 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl,
printk(KERN_CONT "unknown type %d", ctrl->type);
break;
}
- if (fl_inact && fl_grabbed)
- printk(KERN_CONT " (inactive, grabbed)\n");
- else if (fl_inact)
- printk(KERN_CONT " (inactive)\n");
- else if (fl_grabbed)
- printk(KERN_CONT " (grabbed)\n");
- else
- printk(KERN_CONT "\n");
+ if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE |
+ V4L2_CTRL_FLAG_GRABBED |
+ V4L2_CTRL_FLAG_VOLATILE)) {
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ printk(KERN_CONT " inactive");
+ if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+ printk(KERN_CONT " grabbed");
+ if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE)
+ printk(KERN_CONT " volatile");
+ }
+ printk(KERN_CONT "\n");
}
/* Log all controls owned by the handler */
@@ -1277,26 +1675,21 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
int i;
/* Skip if this control was already handled by a cluster. */
- if (ctrl->done)
+ /* Skip button controls and read-only controls. */
+ if (ctrl->done || ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
continue;
for (i = 0; i < master->ncontrols; i++) {
if (master->cluster[i]) {
cur_to_new(master->cluster[i]);
master->cluster[i]->is_new = 1;
+ master->cluster[i]->done = true;
}
}
-
- /* Skip button controls and read-only controls. */
- if (ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
- (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
- continue;
- ret = master->ops->s_ctrl(master);
+ ret = call_op(master, s_ctrl);
if (ret)
break;
- for (i = 0; i < master->ncontrols; i++)
- if (master->cluster[i])
- master->cluster[i]->done = true;
}
mutex_unlock(&hdl->lock);
return ret;
@@ -1447,18 +1840,19 @@ EXPORT_SYMBOL(v4l2_subdev_querymenu);
Find the controls in the control array and do some basic checks. */
static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
struct v4l2_ext_controls *cs,
- struct ctrl_helper *helpers,
- bool try)
+ struct v4l2_ctrl_helper *helpers)
{
+ struct v4l2_ctrl_helper *h;
+ bool have_clusters = false;
u32 i;
- for (i = 0; i < cs->count; i++) {
+ for (i = 0, h = helpers; i < cs->count; i++, h++) {
struct v4l2_ext_control *c = &cs->controls[i];
+ struct v4l2_ctrl_ref *ref;
struct v4l2_ctrl *ctrl;
u32 id = c->id & V4L2_CTRL_ID_MASK;
- if (try)
- cs->error_idx = i;
+ cs->error_idx = i;
if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
return -EINVAL;
@@ -1467,53 +1861,59 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
extended controls */
if (id >= V4L2_CID_PRIVATE_BASE)
return -EINVAL;
- ctrl = v4l2_ctrl_find(hdl, id);
- if (ctrl == NULL)
+ ref = find_ref_lock(hdl, id);
+ if (ref == NULL)
return -EINVAL;
+ ctrl = ref->ctrl;
if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
return -EINVAL;
- helpers[i].ctrl = ctrl;
- helpers[i].handled = false;
+ if (ctrl->cluster[0]->ncontrols > 1)
+ have_clusters = true;
+ if (ctrl->cluster[0] != ctrl)
+ ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
+ /* Store the ref to the master control of the cluster */
+ h->mref = ref;
+ h->ctrl = ctrl;
+ /* Initially set next to 0, meaning that there is no other
+ control in this helper array belonging to the same
+ cluster */
+ h->next = 0;
}
- return 0;
-}
-typedef int (*cluster_func)(struct v4l2_ext_control *c,
- struct v4l2_ctrl *ctrl);
+ /* We are done if there were no controls that belong to a multi-
+ control cluster. */
+ if (!have_clusters)
+ return 0;
-/* Walk over all controls in v4l2_ext_controls belonging to the same cluster
- and call the provided function. */
-static int cluster_walk(unsigned from,
- struct v4l2_ext_controls *cs,
- struct ctrl_helper *helpers,
- cluster_func f)
-{
- struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
- int ret = 0;
- int i;
+ /* The code below figures out in O(n) time which controls in the list
+ belong to the same cluster. */
- /* Find any controls from the same cluster and call the function */
- for (i = from; !ret && i < cs->count; i++) {
- struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+ /* This has to be done with the handler lock taken. */
+ mutex_lock(&hdl->lock);
- if (!helpers[i].handled && ctrl->cluster == cluster)
- ret = f(&cs->controls[i], ctrl);
+ /* First zero the helper field in the master control references */
+ for (i = 0; i < cs->count; i++)
+ helpers[i].mref->helper = 0;
+ for (i = 0, h = helpers; i < cs->count; i++, h++) {
+ struct v4l2_ctrl_ref *mref = h->mref;
+
+ /* If the mref->helper is set, then it points to an earlier
+ helper that belongs to the same cluster. */
+ if (mref->helper) {
+ /* Set the next field of mref->helper to the current
+ index: this means that that earlier helper now
+ points to the next helper in the same cluster. */
+ mref->helper->next = i;
+ /* mref should be set only for the first helper in the
+ cluster, clear the others. */
+ h->mref = NULL;
+ }
+ /* Point the mref helper to the current helper struct. */
+ mref->helper = h;
}
- return ret;
-}
-
-static void cluster_done(unsigned from,
- struct v4l2_ext_controls *cs,
- struct ctrl_helper *helpers)
-{
- struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
- int i;
-
- /* Find any controls from the same cluster and mark them as handled */
- for (i = from; i < cs->count; i++)
- if (helpers[i].ctrl->cluster == cluster)
- helpers[i].handled = true;
+ mutex_unlock(&hdl->lock);
+ return 0;
}
/* Handles the corner case where cs->count == 0. It checks whether the
@@ -1531,10 +1931,10 @@ static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
/* Get extended controls. Allocates the helpers array if needed. */
int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
{
- struct ctrl_helper helper[4];
- struct ctrl_helper *helpers = helper;
+ struct v4l2_ctrl_helper helper[4];
+ struct v4l2_ctrl_helper *helpers = helper;
int ret;
- int i;
+ int i, j;
cs->error_idx = cs->count;
cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
@@ -1551,30 +1951,47 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
return -ENOMEM;
}
- ret = prepare_ext_ctrls(hdl, cs, helpers, false);
+ ret = prepare_ext_ctrls(hdl, cs, helpers);
+ cs->error_idx = cs->count;
for (i = 0; !ret && i < cs->count; i++)
if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
ret = -EACCES;
for (i = 0; !ret && i < cs->count; i++) {
- struct v4l2_ctrl *ctrl = helpers[i].ctrl;
- struct v4l2_ctrl *master = ctrl->cluster[0];
+ int (*ctrl_to_user)(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl) = cur_to_user;
+ struct v4l2_ctrl *master;
- if (helpers[i].handled)
+ if (helpers[i].mref == NULL)
continue;
+ master = helpers[i].mref->ctrl;
cs->error_idx = i;
v4l2_ctrl_lock(master);
- /* g_volatile_ctrl will update the current control values */
- if (ctrl->is_volatile && master->ops->g_volatile_ctrl)
- ret = master->ops->g_volatile_ctrl(master);
- /* If OK, then copy the current control values to the caller */
- if (!ret)
- ret = cluster_walk(i, cs, helpers, cur_to_user);
+
+ /* g_volatile_ctrl will update the new control values */
+ if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
+ (master->has_volatiles && !is_cur_manual(master))) {
+ for (j = 0; j < master->ncontrols; j++)
+ cur_to_new(master->cluster[j]);
+ ret = call_op(master, g_volatile_ctrl);
+ ctrl_to_user = new_to_user;
+ }
+ /* If OK, then copy the current (for non-volatile controls)
+ or the new (for volatile controls) control values to the
+ caller */
+ if (!ret) {
+ u32 idx = i;
+
+ do {
+ ret = ctrl_to_user(cs->controls + idx,
+ helpers[idx].ctrl);
+ idx = helpers[idx].next;
+ } while (!ret && idx);
+ }
v4l2_ctrl_unlock(master);
- cluster_done(i, cs, helpers);
}
if (cs->count > ARRAY_SIZE(helper))
@@ -1594,15 +2011,21 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
{
struct v4l2_ctrl *master = ctrl->cluster[0];
int ret = 0;
+ int i;
if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
return -EACCES;
v4l2_ctrl_lock(master);
/* g_volatile_ctrl will update the current control values */
- if (ctrl->is_volatile && master->ops->g_volatile_ctrl)
- ret = master->ops->g_volatile_ctrl(master);
- *val = ctrl->cur.val;
+ if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+ ret = call_op(master, g_volatile_ctrl);
+ *val = ctrl->val;
+ } else {
+ *val = ctrl->cur.val;
+ }
v4l2_ctrl_unlock(master);
return ret;
}
@@ -1638,72 +2061,61 @@ EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
/* Core function that calls try/s_ctrl and ensures that the new value is
copied to the current value on a set.
Must be called with ctrl->handler->lock held. */
-static int try_or_set_control_cluster(struct v4l2_ctrl *master, bool set)
+static int try_or_set_cluster(struct v4l2_fh *fh,
+ struct v4l2_ctrl *master, bool set)
{
- bool try = !set;
- int ret = 0;
+ bool update_flag;
+ int ret;
int i;
/* Go through the cluster and either validate the new value or
(if no new value was set), copy the current value to the new
value, ensuring a consistent view for the control ops when
called. */
- for (i = 0; !ret && i < master->ncontrols; i++) {
+ for (i = 0; i < master->ncontrols; i++) {
struct v4l2_ctrl *ctrl = master->cluster[i];
if (ctrl == NULL)
continue;
- if (ctrl->is_new) {
- /* Double check this: it may have changed since the
- last check in try_or_set_ext_ctrls(). */
- if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
- return -EBUSY;
-
- /* Validate if required */
- if (!set)
- ret = validate_new(ctrl);
+ if (!ctrl->is_new) {
+ cur_to_new(ctrl);
continue;
}
- /* No new value was set, so copy the current and force
- a call to try_ctrl later, since the values for the cluster
- may now have changed and the end result might be invalid. */
- try = true;
- cur_to_new(ctrl);
+ /* Check again: it may have changed since the
+ previous check in try_or_set_ext_ctrls(). */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
}
- /* For larger clusters you have to call try_ctrl again to
- verify that the controls are still valid after the
- 'cur_to_new' above. */
- if (!ret && master->ops->try_ctrl && try)
- ret = master->ops->try_ctrl(master);
+ ret = call_op(master, try_ctrl);
/* Don't set if there is no change */
- if (!ret && set && cluster_changed(master)) {
- ret = master->ops->s_ctrl(master);
- /* If OK, then make the new values permanent. */
- if (!ret)
- for (i = 0; i < master->ncontrols; i++)
- new_to_cur(master->cluster[i]);
- }
- return ret;
+ if (ret || !set || !cluster_changed(master))
+ return ret;
+ ret = call_op(master, s_ctrl);
+ if (ret)
+ return ret;
+
+ /* If OK, then make the new values permanent. */
+ update_flag = is_cur_manual(master) != is_new_manual(master);
+ for (i = 0; i < master->ncontrols; i++)
+ new_to_cur(fh, master->cluster[i], update_flag && i > 0);
+ return 0;
}
-/* Try or set controls. */
-static int try_or_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
- struct v4l2_ext_controls *cs,
- struct ctrl_helper *helpers,
- bool set)
+/* Validate controls. */
+static int validate_ctrls(struct v4l2_ext_controls *cs,
+ struct v4l2_ctrl_helper *helpers, bool set)
{
- unsigned i, j;
+ unsigned i;
int ret = 0;
cs->error_idx = cs->count;
for (i = 0; i < cs->count; i++) {
struct v4l2_ctrl *ctrl = helpers[i].ctrl;
- if (!set)
- cs->error_idx = i;
+ cs->error_idx = i;
if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
return -EACCES;
@@ -1715,50 +2127,36 @@ static int try_or_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
best-effort to avoid that. */
if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
return -EBUSY;
+ ret = validate_new(ctrl, &cs->controls[i]);
+ if (ret)
+ return ret;
}
+ return 0;
+}
- for (i = 0; !ret && i < cs->count; i++) {
- struct v4l2_ctrl *ctrl = helpers[i].ctrl;
- struct v4l2_ctrl *master = ctrl->cluster[0];
-
- cs->error_idx = i;
-
- if (helpers[i].handled)
- continue;
-
- v4l2_ctrl_lock(ctrl);
-
- /* Reset the 'is_new' flags of the cluster */
- for (j = 0; j < master->ncontrols; j++)
- if (master->cluster[j])
- master->cluster[j]->is_new = 0;
-
- /* Copy the new caller-supplied control values.
- user_to_new() sets 'is_new' to 1. */
- ret = cluster_walk(i, cs, helpers, user_to_new);
-
- if (!ret)
- ret = try_or_set_control_cluster(master, set);
-
- /* Copy the new values back to userspace. */
- if (!ret)
- ret = cluster_walk(i, cs, helpers, new_to_user);
+/* Obtain the current volatile values of an autocluster and mark them
+ as new. */
+static void update_from_auto_cluster(struct v4l2_ctrl *master)
+{
+ int i;
- v4l2_ctrl_unlock(ctrl);
- cluster_done(i, cs, helpers);
- }
- return ret;
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+ if (!call_op(master, g_volatile_ctrl))
+ for (i = 1; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->is_new = 1;
}
/* Try or try-and-set controls */
-static int try_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
struct v4l2_ext_controls *cs,
bool set)
{
- struct ctrl_helper helper[4];
- struct ctrl_helper *helpers = helper;
+ struct v4l2_ctrl_helper helper[4];
+ struct v4l2_ctrl_helper *helpers = helper;
+ unsigned i, j;
int ret;
- int i;
cs->error_idx = cs->count;
cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
@@ -1774,25 +2172,74 @@ static int try_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
if (!helpers)
return -ENOMEM;
}
- ret = prepare_ext_ctrls(hdl, cs, helpers, !set);
- if (ret)
- goto free;
-
- /* First 'try' all controls and abort on error */
- ret = try_or_set_ext_ctrls(hdl, cs, helpers, false);
- /* If this is a 'set' operation and the initial 'try' failed,
- then set error_idx to count to tell the application that no
- controls changed value yet. */
- if (set)
+ ret = prepare_ext_ctrls(hdl, cs, helpers);
+ if (!ret)
+ ret = validate_ctrls(cs, helpers, set);
+ if (ret && set)
cs->error_idx = cs->count;
- if (!ret && set) {
- /* Reset 'handled' state */
- for (i = 0; i < cs->count; i++)
- helpers[i].handled = false;
- ret = try_or_set_ext_ctrls(hdl, cs, helpers, true);
+ for (i = 0; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *master;
+ u32 idx = i;
+
+ if (helpers[i].mref == NULL)
+ continue;
+
+ cs->error_idx = i;
+ master = helpers[i].mref->ctrl;
+ v4l2_ctrl_lock(master);
+
+ /* Reset the 'is_new' flags of the cluster */
+ for (j = 0; j < master->ncontrols; j++)
+ if (master->cluster[j])
+ master->cluster[j]->is_new = 0;
+
+ /* For volatile autoclusters that are currently in auto mode
+ we need to discover if it will be set to manual mode.
+ If so, then we have to copy the current volatile values
+ first since those will become the new manual values (which
+ may be overwritten by explicit new values from this set
+ of controls). */
+ if (master->is_auto && master->has_volatiles &&
+ !is_cur_manual(master)) {
+ /* Pick an initial non-manual value */
+ s32 new_auto_val = master->manual_mode_value + 1;
+ u32 tmp_idx = idx;
+
+ do {
+ /* Check if the auto control is part of the
+ list, and remember the new value. */
+ if (helpers[tmp_idx].ctrl == master)
+ new_auto_val = cs->controls[tmp_idx].value;
+ tmp_idx = helpers[tmp_idx].next;
+ } while (tmp_idx);
+ /* If the new value == the manual value, then copy
+ the current volatile values. */
+ if (new_auto_val == master->manual_mode_value)
+ update_from_auto_cluster(master);
+ }
+
+ /* Copy the new caller-supplied control values.
+ user_to_new() sets 'is_new' to 1. */
+ do {
+ ret = user_to_new(cs->controls + idx, helpers[idx].ctrl);
+ idx = helpers[idx].next;
+ } while (!ret && idx);
+
+ if (!ret)
+ ret = try_or_set_cluster(fh, master, set);
+
+ /* Copy the new values back to userspace. */
+ if (!ret) {
+ idx = i;
+ do {
+ ret = new_to_user(cs->controls + idx,
+ helpers[idx].ctrl);
+ idx = helpers[idx].next;
+ } while (!ret && idx);
+ }
+ v4l2_ctrl_unlock(master);
}
-free:
if (cs->count > ARRAY_SIZE(helper))
kfree(helpers);
return ret;
@@ -1800,37 +2247,39 @@ free:
int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
{
- return try_set_ext_ctrls(hdl, cs, false);
+ return try_set_ext_ctrls(NULL, hdl, cs, false);
}
EXPORT_SYMBOL(v4l2_try_ext_ctrls);
-int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs)
{
- return try_set_ext_ctrls(hdl, cs, true);
+ return try_set_ext_ctrls(fh, hdl, cs, true);
}
EXPORT_SYMBOL(v4l2_s_ext_ctrls);
int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
{
- return try_set_ext_ctrls(sd->ctrl_handler, cs, false);
+ return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, false);
}
EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
{
- return try_set_ext_ctrls(sd->ctrl_handler, cs, true);
+ return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, true);
}
EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
/* Helper function for VIDIOC_S_CTRL compatibility */
-static int set_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
{
struct v4l2_ctrl *master = ctrl->cluster[0];
int ret;
int i;
- if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
- return -EACCES;
+ ret = validate_new_int(ctrl, val);
+ if (ret)
+ return ret;
v4l2_ctrl_lock(ctrl);
@@ -1839,30 +2288,38 @@ static int set_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
if (master->cluster[i])
master->cluster[i]->is_new = 0;
+ /* For autoclusters with volatiles that are switched from auto to
+ manual mode we have to update the current volatile values since
+ those will become the initial manual values after such a switch. */
+ if (master->is_auto && master->has_volatiles && ctrl == master &&
+ !is_cur_manual(master) && *val == master->manual_mode_value)
+ update_from_auto_cluster(master);
ctrl->val = *val;
ctrl->is_new = 1;
- ret = try_or_set_control_cluster(master, false);
- if (!ret)
- ret = try_or_set_control_cluster(master, true);
+ ret = try_or_set_cluster(fh, master, true);
*val = ctrl->cur.val;
v4l2_ctrl_unlock(ctrl);
return ret;
}
-int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+ struct v4l2_control *control)
{
struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
if (ctrl == NULL || !type_is_int(ctrl))
return -EINVAL;
- return set_ctrl(ctrl, &control->value);
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return -EACCES;
+
+ return set_ctrl(fh, ctrl, &control->value);
}
EXPORT_SYMBOL(v4l2_s_ctrl);
int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
{
- return v4l2_s_ctrl(sd->ctrl_handler, control);
+ return v4l2_s_ctrl(NULL, sd->ctrl_handler, control);
}
EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
@@ -1870,6 +2327,34 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
{
/* It's a driver bug if this happens. */
WARN_ON(!type_is_int(ctrl));
- return set_ctrl(ctrl, &val);
+ return set_ctrl(NULL, ctrl, &val);
}
EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
+
+void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl,
+ struct v4l2_subscribed_event *sev)
+{
+ v4l2_ctrl_lock(ctrl);
+ list_add_tail(&sev->node, &ctrl->ev_subs);
+ if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS &&
+ (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) {
+ struct v4l2_event ev;
+ u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
+
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY))
+ changes |= V4L2_EVENT_CTRL_CH_VALUE;
+ fill_event(&ev, ctrl, changes);
+ v4l2_event_queue_fh(sev->fh, &ev);
+ }
+ v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_event);
+
+void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl,
+ struct v4l2_subscribed_event *sev)
+{
+ v4l2_ctrl_lock(ctrl);
+ list_del(&sev->node);
+ v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_del_event);
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 06f14008b346..a5c9ed128b97 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -173,6 +173,17 @@ static void v4l2_device_release(struct device *cd)
media_device_unregister_entity(&vdev->entity);
#endif
+ /* Do not call v4l2_device_put if there is no release callback set.
+ * Drivers that have no v4l2_device release callback might free the
+ * v4l2_dev instance in the video_device release callback below, so we
+ * must perform this check here.
+ *
+ * TODO: In the long run all drivers that use v4l2_device should use the
+ * v4l2_device release callback. This check will then be unnecessary.
+ */
+ if (v4l2_dev && v4l2_dev->release == NULL)
+ v4l2_dev = NULL;
+
/* Release video_device and perform other
cleanups as needed. */
vdev->release(vdev);
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
index 4aae501f02d0..0edd618b9ddf 100644
--- a/drivers/media/video/v4l2-device.c
+++ b/drivers/media/video/v4l2-device.c
@@ -20,7 +20,9 @@
#include <linux/types.h>
#include <linux/ioctl.h>
+#include <linux/module.h>
#include <linux/i2c.h>
+#include <linux/slab.h>
#if defined(CONFIG_SPI)
#include <linux/spi/spi.h>
#endif
@@ -38,6 +40,7 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
mutex_init(&v4l2_dev->ioctl_lock);
v4l2_prio_init(&v4l2_dev->prio);
kref_init(&v4l2_dev->ref);
+ get_device(dev);
v4l2_dev->dev = dev;
if (dev == NULL) {
/* If dev == NULL, then name must be filled in by the caller */
@@ -93,6 +96,7 @@ void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev)
dev_set_drvdata(v4l2_dev->dev, NULL);
+ put_device(v4l2_dev->dev);
v4l2_dev->dev = NULL;
}
EXPORT_SYMBOL_GPL(v4l2_device_disconnect);
@@ -191,6 +195,13 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
}
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
+static void v4l2_device_release_subdev_node(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
{
struct video_device *vdev;
@@ -204,21 +215,40 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
continue;
- vdev = &sd->devnode;
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ err = -ENOMEM;
+ goto clean_up;
+ }
+
+ video_set_drvdata(vdev, sd);
strlcpy(vdev->name, sd->name, sizeof(vdev->name));
vdev->v4l2_dev = v4l2_dev;
vdev->fops = &v4l2_subdev_fops;
- vdev->release = video_device_release_empty;
+ vdev->release = v4l2_device_release_subdev_node;
+ vdev->ctrl_handler = sd->ctrl_handler;
err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
sd->owner);
- if (err < 0)
- return err;
+ if (err < 0) {
+ kfree(vdev);
+ goto clean_up;
+ }
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.v4l.major = VIDEO_MAJOR;
sd->entity.v4l.minor = vdev->minor;
#endif
+ sd->devnode = vdev;
}
return 0;
+
+clean_up:
+ list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
+ if (!sd->devnode)
+ break;
+ video_unregister_device(sd->devnode);
+ }
+
+ return err;
}
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes);
@@ -244,7 +274,7 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
if (v4l2_dev->mdev)
media_device_unregister_entity(&sd->entity);
#endif
- video_unregister_device(&sd->devnode);
+ video_unregister_device(sd->devnode);
module_put(sd->owner);
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
index 69fd343d4774..c26ad9637143 100644
--- a/drivers/media/video/v4l2-event.c
+++ b/drivers/media/video/v4l2-event.c
@@ -25,100 +25,40 @@
#include <media/v4l2-dev.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/export.h>
-int v4l2_event_init(struct v4l2_fh *fh)
+static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
{
- fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL);
- if (fh->events == NULL)
- return -ENOMEM;
-
- init_waitqueue_head(&fh->events->wait);
-
- INIT_LIST_HEAD(&fh->events->free);
- INIT_LIST_HEAD(&fh->events->available);
- INIT_LIST_HEAD(&fh->events->subscribed);
-
- fh->events->sequence = -1;
-
- return 0;
+ idx += sev->first;
+ return idx >= sev->elems ? idx - sev->elems : idx;
}
-EXPORT_SYMBOL_GPL(v4l2_event_init);
-
-int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n)
-{
- struct v4l2_events *events = fh->events;
- unsigned long flags;
-
- if (!events) {
- WARN_ON(1);
- return -ENOMEM;
- }
-
- while (events->nallocated < n) {
- struct v4l2_kevent *kev;
-
- kev = kzalloc(sizeof(*kev), GFP_KERNEL);
- if (kev == NULL)
- return -ENOMEM;
-
- spin_lock_irqsave(&fh->vdev->fh_lock, flags);
- list_add_tail(&kev->list, &events->free);
- events->nallocated++;
- spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_alloc);
-
-#define list_kfree(list, type, member) \
- while (!list_empty(list)) { \
- type *hi; \
- hi = list_first_entry(list, type, member); \
- list_del(&hi->member); \
- kfree(hi); \
- }
-
-void v4l2_event_free(struct v4l2_fh *fh)
-{
- struct v4l2_events *events = fh->events;
-
- if (!events)
- return;
-
- list_kfree(&events->free, struct v4l2_kevent, list);
- list_kfree(&events->available, struct v4l2_kevent, list);
- list_kfree(&events->subscribed, struct v4l2_subscribed_event, list);
-
- kfree(events);
- fh->events = NULL;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_free);
static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
{
- struct v4l2_events *events = fh->events;
struct v4l2_kevent *kev;
unsigned long flags;
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
- if (list_empty(&events->available)) {
+ if (list_empty(&fh->available)) {
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
return -ENOENT;
}
- WARN_ON(events->navailable == 0);
+ WARN_ON(fh->navailable == 0);
- kev = list_first_entry(&events->available, struct v4l2_kevent, list);
- list_move(&kev->list, &events->free);
- events->navailable--;
+ kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
+ list_del(&kev->list);
+ fh->navailable--;
- kev->event.pending = events->navailable;
+ kev->event.pending = fh->navailable;
*event = kev->event;
+ kev->sev->first = sev_pos(kev->sev, 1);
+ kev->sev->in_use--;
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
@@ -128,7 +68,6 @@ static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
int nonblocking)
{
- struct v4l2_events *events = fh->events;
int ret;
if (nonblocking)
@@ -139,8 +78,8 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
mutex_unlock(fh->vdev->lock);
do {
- ret = wait_event_interruptible(events->wait,
- events->navailable != 0);
+ ret = wait_event_interruptible(fh->wait,
+ fh->navailable != 0);
if (ret < 0)
break;
@@ -154,23 +93,72 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
}
EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
-/* Caller must hold fh->event->lock! */
+/* Caller must hold fh->vdev->fh_lock! */
static struct v4l2_subscribed_event *v4l2_event_subscribed(
- struct v4l2_fh *fh, u32 type)
+ struct v4l2_fh *fh, u32 type, u32 id)
{
- struct v4l2_events *events = fh->events;
struct v4l2_subscribed_event *sev;
assert_spin_locked(&fh->vdev->fh_lock);
- list_for_each_entry(sev, &events->subscribed, list) {
- if (sev->type == type)
+ list_for_each_entry(sev, &fh->subscribed, list)
+ if (sev->type == type && sev->id == id)
return sev;
- }
return NULL;
}
+static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
+ const struct timespec *ts)
+{
+ struct v4l2_subscribed_event *sev;
+ struct v4l2_kevent *kev;
+ bool copy_payload = true;
+
+ /* Are we subscribed? */
+ sev = v4l2_event_subscribed(fh, ev->type, ev->id);
+ if (sev == NULL)
+ return;
+
+ /* Increase event sequence number on fh. */
+ fh->sequence++;
+
+ /* Do we have any free events? */
+ if (sev->in_use == sev->elems) {
+ /* no, remove the oldest one */
+ kev = sev->events + sev_pos(sev, 0);
+ list_del(&kev->list);
+ sev->in_use--;
+ sev->first = sev_pos(sev, 1);
+ fh->navailable--;
+ if (sev->elems == 1) {
+ if (sev->replace) {
+ sev->replace(&kev->event, ev);
+ copy_payload = false;
+ }
+ } else if (sev->merge) {
+ struct v4l2_kevent *second_oldest =
+ sev->events + sev_pos(sev, 0);
+ sev->merge(&kev->event, &second_oldest->event);
+ }
+ }
+
+ /* Take one and fill it. */
+ kev = sev->events + sev_pos(sev, sev->in_use);
+ kev->event.type = ev->type;
+ if (copy_payload)
+ kev->event.u = ev->u;
+ kev->event.id = ev->id;
+ kev->event.timestamp = *ts;
+ kev->event.sequence = fh->sequence;
+ sev->in_use++;
+ list_add_tail(&kev->list, &fh->available);
+
+ fh->navailable++;
+
+ wake_up_all(&fh->wait);
+}
+
void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
{
struct v4l2_fh *fh;
@@ -181,81 +169,98 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
spin_lock_irqsave(&vdev->fh_lock, flags);
- list_for_each_entry(fh, &vdev->fh_list, list) {
- struct v4l2_events *events = fh->events;
- struct v4l2_kevent *kev;
+ list_for_each_entry(fh, &vdev->fh_list, list)
+ __v4l2_event_queue_fh(fh, ev, &timestamp);
- /* Are we subscribed? */
- if (!v4l2_event_subscribed(fh, ev->type))
- continue;
+ spin_unlock_irqrestore(&vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue);
- /* Increase event sequence number on fh. */
- events->sequence++;
+void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
+{
+ unsigned long flags;
+ struct timespec timestamp;
- /* Do we have any free events? */
- if (list_empty(&events->free))
- continue;
+ ktime_get_ts(&timestamp);
- /* Take one and fill it. */
- kev = list_first_entry(&events->free, struct v4l2_kevent, list);
- kev->event.type = ev->type;
- kev->event.u = ev->u;
- kev->event.timestamp = timestamp;
- kev->event.sequence = events->sequence;
- list_move_tail(&kev->list, &events->available);
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ __v4l2_event_queue_fh(fh, ev, &timestamp);
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
- events->navailable++;
+int v4l2_event_pending(struct v4l2_fh *fh)
+{
+ return fh->navailable;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_pending);
- wake_up_all(&events->wait);
- }
+static void ctrls_replace(struct v4l2_event *old, const struct v4l2_event *new)
+{
+ u32 old_changes = old->u.ctrl.changes;
- spin_unlock_irqrestore(&vdev->fh_lock, flags);
+ old->u.ctrl = new->u.ctrl;
+ old->u.ctrl.changes |= old_changes;
}
-EXPORT_SYMBOL_GPL(v4l2_event_queue);
-int v4l2_event_pending(struct v4l2_fh *fh)
+static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new)
{
- return fh->events->navailable;
+ new->u.ctrl.changes |= old->u.ctrl.changes;
}
-EXPORT_SYMBOL_GPL(v4l2_event_pending);
int v4l2_event_subscribe(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
+ struct v4l2_event_subscription *sub, unsigned elems)
{
- struct v4l2_events *events = fh->events;
- struct v4l2_subscribed_event *sev;
+ struct v4l2_subscribed_event *sev, *found_ev;
+ struct v4l2_ctrl *ctrl = NULL;
unsigned long flags;
+ unsigned i;
- if (fh->events == NULL) {
- WARN_ON(1);
- return -ENOMEM;
+ if (sub->type == V4L2_EVENT_ALL)
+ return -EINVAL;
+
+ if (elems < 1)
+ elems = 1;
+ if (sub->type == V4L2_EVENT_CTRL) {
+ ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
+ if (ctrl == NULL)
+ return -EINVAL;
}
- sev = kmalloc(sizeof(*sev), GFP_KERNEL);
+ sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
if (!sev)
return -ENOMEM;
-
- spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-
- if (v4l2_event_subscribed(fh, sub->type) == NULL) {
- INIT_LIST_HEAD(&sev->list);
- sev->type = sub->type;
-
- list_add(&sev->list, &events->subscribed);
- sev = NULL;
+ for (i = 0; i < elems; i++)
+ sev->events[i].sev = sev;
+ sev->type = sub->type;
+ sev->id = sub->id;
+ sev->flags = sub->flags;
+ sev->fh = fh;
+ sev->elems = elems;
+ if (ctrl) {
+ sev->replace = ctrls_replace;
+ sev->merge = ctrls_merge;
}
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
+ if (!found_ev)
+ list_add(&sev->list, &fh->subscribed);
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
- kfree(sev);
+ /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */
+ if (found_ev)
+ kfree(sev);
+ else if (ctrl)
+ v4l2_ctrl_add_event(ctrl, sev);
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
-static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
+void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
{
- struct v4l2_events *events = fh->events;
+ struct v4l2_event_subscription sub;
struct v4l2_subscribed_event *sev;
unsigned long flags;
@@ -263,21 +268,25 @@ static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
sev = NULL;
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
- if (!list_empty(&events->subscribed)) {
- sev = list_first_entry(&events->subscribed,
- struct v4l2_subscribed_event, list);
- list_del(&sev->list);
+ if (!list_empty(&fh->subscribed)) {
+ sev = list_first_entry(&fh->subscribed,
+ struct v4l2_subscribed_event, list);
+ sub.type = sev->type;
+ sub.id = sev->id;
}
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
- kfree(sev);
+ if (sev)
+ v4l2_event_unsubscribe(fh, &sub);
} while (sev);
}
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
int v4l2_event_unsubscribe(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
struct v4l2_subscribed_event *sev;
unsigned long flags;
+ int i;
if (sub->type == V4L2_EVENT_ALL) {
v4l2_event_unsubscribe_all(fh);
@@ -286,11 +295,23 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
- sev = v4l2_event_subscribed(fh, sub->type);
- if (sev != NULL)
+ sev = v4l2_event_subscribed(fh, sub->type, sub->id);
+ if (sev != NULL) {
+ /* Remove any pending events for this subscription */
+ for (i = 0; i < sev->in_use; i++) {
+ list_del(&sev->events[sev_pos(sev, i)].list);
+ fh->navailable--;
+ }
list_del(&sev->list);
+ }
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+ if (sev && sev->type == V4L2_EVENT_CTRL) {
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
+
+ if (ctrl)
+ v4l2_ctrl_del_event(ctrl, sev);
+ }
kfree(sev);
diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c
index 717f71e6370e..9e3fc040ea20 100644
--- a/drivers/media/video/v4l2-fh.c
+++ b/drivers/media/video/v4l2-fh.c
@@ -24,28 +24,24 @@
#include <linux/bitops.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
-int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
+void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
{
fh->vdev = vdev;
+ /* Inherit from video_device. May be overridden by the driver. */
+ fh->ctrl_handler = vdev->ctrl_handler;
INIT_LIST_HEAD(&fh->list);
set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);
fh->prio = V4L2_PRIORITY_UNSET;
-
- /*
- * fh->events only needs to be initialized if the driver
- * supports the VIDIOC_SUBSCRIBE_EVENT ioctl.
- */
- if (vdev->ioctl_ops && vdev->ioctl_ops->vidioc_subscribe_event)
- return v4l2_event_init(fh);
-
- fh->events = NULL;
-
- return 0;
+ init_waitqueue_head(&fh->wait);
+ INIT_LIST_HEAD(&fh->available);
+ INIT_LIST_HEAD(&fh->subscribed);
+ fh->sequence = -1;
}
EXPORT_SYMBOL_GPL(v4l2_fh_init);
@@ -91,10 +87,8 @@ void v4l2_fh_exit(struct v4l2_fh *fh)
{
if (fh->vdev == NULL)
return;
-
+ v4l2_event_unsubscribe_all(fh);
fh->vdev = NULL;
-
- v4l2_event_free(fh);
}
EXPORT_SYMBOL_GPL(v4l2_fh_exit);
diff --git a/drivers/media/video/v4l2-int-device.c b/drivers/media/video/v4l2-int-device.c
index a935bae538ef..f4473494af7a 100644
--- a/drivers/media/video/v4l2-int-device.c
+++ b/drivers/media/video/v4l2-int-device.c
@@ -26,6 +26,7 @@
#include <linux/list.h>
#include <linux/sort.h>
#include <linux/string.h>
+#include <linux/module.h>
#include <media/v4l2-int-device.h>
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 69e8c6ffcc49..e1da8fc9dd2f 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/kernel.h>
+#include <linux/version.h>
#include <linux/videodev2.h>
@@ -54,6 +55,19 @@
memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
+#define have_fmt_ops(foo) ( \
+ ops->vidioc_##foo##_fmt_vid_cap || \
+ ops->vidioc_##foo##_fmt_vid_out || \
+ ops->vidioc_##foo##_fmt_vid_cap_mplane || \
+ ops->vidioc_##foo##_fmt_vid_out_mplane || \
+ ops->vidioc_##foo##_fmt_vid_overlay || \
+ ops->vidioc_##foo##_fmt_vbi_cap || \
+ ops->vidioc_##foo##_fmt_vid_out_overlay || \
+ ops->vidioc_##foo##_fmt_vbi_out || \
+ ops->vidioc_##foo##_fmt_sliced_vbi_cap || \
+ ops->vidioc_##foo##_fmt_sliced_vbi_out || \
+ ops->vidioc_##foo##_fmt_type_private)
+
struct std_descr {
v4l2_std_id std;
const char *descr;
@@ -259,6 +273,8 @@ static const char *v4l2_ioctls[] = {
[_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT",
[_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT",
[_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT",
+ [_IOC_NR(VIDIOC_CREATE_BUFS)] = "VIDIOC_CREATE_BUFS",
+ [_IOC_NR(VIDIOC_PREPARE_BUF)] = "VIDIOC_PREPARE_BUF",
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
@@ -476,63 +492,6 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
return -EINVAL;
}
-/**
- * fmt_sp_to_mp() - Convert a single-plane format to its multi-planar 1-plane
- * equivalent
- */
-static int fmt_sp_to_mp(const struct v4l2_format *f_sp,
- struct v4l2_format *f_mp)
-{
- struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp;
- const struct v4l2_pix_format *pix = &f_sp->fmt.pix;
-
- if (f_sp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- f_mp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- else if (f_sp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- f_mp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
- else
- return -EINVAL;
-
- pix_mp->width = pix->width;
- pix_mp->height = pix->height;
- pix_mp->pixelformat = pix->pixelformat;
- pix_mp->field = pix->field;
- pix_mp->colorspace = pix->colorspace;
- pix_mp->num_planes = 1;
- pix_mp->plane_fmt[0].sizeimage = pix->sizeimage;
- pix_mp->plane_fmt[0].bytesperline = pix->bytesperline;
-
- return 0;
-}
-
-/**
- * fmt_mp_to_sp() - Convert a multi-planar 1-plane format to its single-planar
- * equivalent
- */
-static int fmt_mp_to_sp(const struct v4l2_format *f_mp,
- struct v4l2_format *f_sp)
-{
- const struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp;
- struct v4l2_pix_format *pix = &f_sp->fmt.pix;
-
- if (f_mp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- f_sp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- else if (f_mp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- f_sp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- else
- return -EINVAL;
-
- pix->width = pix_mp->width;
- pix->height = pix_mp->height;
- pix->pixelformat = pix_mp->pixelformat;
- pix->field = pix_mp->field;
- pix->colorspace = pix_mp->colorspace;
- pix->sizeimage = pix_mp->plane_fmt[0].sizeimage;
- pix->bytesperline = pix_mp->plane_fmt[0].bytesperline;
-
- return 0;
-}
-
static long __video_do_ioctl(struct file *file,
unsigned int cmd, void *arg)
{
@@ -540,14 +499,14 @@ static long __video_do_ioctl(struct file *file,
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
void *fh = file->private_data;
struct v4l2_fh *vfh = NULL;
- struct v4l2_format f_copy;
int use_fh_prio = 0;
- long ret = -EINVAL;
+ long ret_prio = 0;
+ long ret = -ENOTTY;
if (ops == NULL) {
printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n",
vfd->name);
- return -EINVAL;
+ return ret;
}
if ((vfd->debug & V4L2_DEBUG_IOCTL) &&
@@ -561,39 +520,8 @@ static long __video_do_ioctl(struct file *file,
use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
}
- if (use_fh_prio) {
- switch (cmd) {
- case VIDIOC_S_CTRL:
- case VIDIOC_S_STD:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_OUTPUT:
- case VIDIOC_S_TUNER:
- case VIDIOC_S_FREQUENCY:
- case VIDIOC_S_FMT:
- case VIDIOC_S_CROP:
- case VIDIOC_S_AUDIO:
- case VIDIOC_S_AUDOUT:
- case VIDIOC_S_EXT_CTRLS:
- case VIDIOC_S_FBUF:
- case VIDIOC_S_PRIORITY:
- case VIDIOC_S_DV_PRESET:
- case VIDIOC_S_DV_TIMINGS:
- case VIDIOC_S_JPEGCOMP:
- case VIDIOC_S_MODULATOR:
- case VIDIOC_S_PARM:
- case VIDIOC_S_HW_FREQ_SEEK:
- case VIDIOC_ENCODER_CMD:
- case VIDIOC_OVERLAY:
- case VIDIOC_REQBUFS:
- case VIDIOC_STREAMON:
- case VIDIOC_STREAMOFF:
- ret = v4l2_prio_check(vfd->prio, vfh->prio);
- if (ret)
- goto exit_prio;
- ret = -EINVAL;
- break;
- }
- }
+ if (use_fh_prio)
+ ret_prio = v4l2_prio_check(vfd->prio, vfh->prio);
switch (cmd) {
@@ -605,6 +533,7 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_querycap)
break;
+ cap->version = LINUX_VERSION_CODE;
ret = ops->vidioc_querycap(file, fh, cap);
if (!ret)
dbgarg(cmd, "driver=%s, card=%s, bus=%s, "
@@ -636,12 +565,14 @@ static long __video_do_ioctl(struct file *file,
enum v4l2_priority *p = arg;
if (!ops->vidioc_s_priority && !use_fh_prio)
- break;
+ break;
dbgarg(cmd, "setting priority to %d\n", *p);
if (ops->vidioc_s_priority)
ret = ops->vidioc_s_priority(file, fh, *p);
else
- ret = v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p);
+ ret = ret_prio ? ret_prio :
+ v4l2_prio_change(&vfd->v4l2_dev->prio,
+ &vfh->prio, *p);
break;
}
@@ -652,37 +583,37 @@ static long __video_do_ioctl(struct file *file,
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (ops->vidioc_enum_fmt_vid_cap)
+ if (likely(ops->vidioc_enum_fmt_vid_cap))
ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (ops->vidioc_enum_fmt_vid_cap_mplane)
+ if (likely(ops->vidioc_enum_fmt_vid_cap_mplane))
ret = ops->vidioc_enum_fmt_vid_cap_mplane(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (ops->vidioc_enum_fmt_vid_overlay)
+ if (likely(ops->vidioc_enum_fmt_vid_overlay))
ret = ops->vidioc_enum_fmt_vid_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (ops->vidioc_enum_fmt_vid_out)
+ if (likely(ops->vidioc_enum_fmt_vid_out))
ret = ops->vidioc_enum_fmt_vid_out(file, fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (ops->vidioc_enum_fmt_vid_out_mplane)
+ if (likely(ops->vidioc_enum_fmt_vid_out_mplane))
ret = ops->vidioc_enum_fmt_vid_out_mplane(file,
fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
- if (ops->vidioc_enum_fmt_type_private)
+ if (likely(ops->vidioc_enum_fmt_type_private))
ret = ops->vidioc_enum_fmt_type_private(file,
fh, f);
break;
default:
break;
}
- if (!ret)
+ if (likely (!ret))
dbgarg(cmd, "index=%d, type=%d, flags=%d, "
"pixelformat=%c%c%c%c, description='%s'\n",
f->index, f->type, f->flags,
@@ -691,6 +622,14 @@ static long __video_do_ioctl(struct file *file,
(f->pixelformat >> 16) & 0xff,
(f->pixelformat >> 24) & 0xff,
f->description);
+ else if (ret == -ENOTTY &&
+ (ops->vidioc_enum_fmt_vid_cap ||
+ ops->vidioc_enum_fmt_vid_out ||
+ ops->vidioc_enum_fmt_vid_cap_mplane ||
+ ops->vidioc_enum_fmt_vid_out_mplane ||
+ ops->vidioc_enum_fmt_vid_overlay ||
+ ops->vidioc_enum_fmt_type_private))
+ ret = -EINVAL;
break;
}
case VIDIOC_G_FMT:
@@ -702,119 +641,67 @@ static long __video_do_ioctl(struct file *file,
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (ops->vidioc_g_fmt_vid_cap) {
+ if (ops->vidioc_g_fmt_vid_cap)
ret = ops->vidioc_g_fmt_vid_cap(file, fh, f);
- } else if (ops->vidioc_g_fmt_vid_cap_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_g_fmt_vid_cap_mplane(file, fh,
- &f_copy);
- if (ret)
- break;
-
- /* Driver is currently in multi-planar format,
- * we can't return it in single-planar API*/
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- ret = -EBUSY;
- break;
- }
-
- ret = fmt_mp_to_sp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (ops->vidioc_g_fmt_vid_cap_mplane) {
+ if (ops->vidioc_g_fmt_vid_cap_mplane)
ret = ops->vidioc_g_fmt_vid_cap_mplane(file,
fh, f);
- } else if (ops->vidioc_g_fmt_vid_cap) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_g_fmt_vid_cap(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_sp_to_mp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (ops->vidioc_g_fmt_vid_overlay)
+ if (likely(ops->vidioc_g_fmt_vid_overlay))
ret = ops->vidioc_g_fmt_vid_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (ops->vidioc_g_fmt_vid_out) {
+ if (ops->vidioc_g_fmt_vid_out)
ret = ops->vidioc_g_fmt_vid_out(file, fh, f);
- } else if (ops->vidioc_g_fmt_vid_out_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_g_fmt_vid_out_mplane(file, fh,
- &f_copy);
- if (ret)
- break;
-
- /* Driver is currently in multi-planar format,
- * we can't return it in single-planar API*/
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- ret = -EBUSY;
- break;
- }
-
- ret = fmt_mp_to_sp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (ops->vidioc_g_fmt_vid_out_mplane) {
+ if (ops->vidioc_g_fmt_vid_out_mplane)
ret = ops->vidioc_g_fmt_vid_out_mplane(file,
fh, f);
- } else if (ops->vidioc_g_fmt_vid_out) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_g_fmt_vid_out(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_sp_to_mp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (ops->vidioc_g_fmt_vid_out_overlay)
+ if (likely(ops->vidioc_g_fmt_vid_out_overlay))
ret = ops->vidioc_g_fmt_vid_out_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (ops->vidioc_g_fmt_vbi_cap)
+ if (likely(ops->vidioc_g_fmt_vbi_cap))
ret = ops->vidioc_g_fmt_vbi_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (ops->vidioc_g_fmt_vbi_out)
+ if (likely(ops->vidioc_g_fmt_vbi_out))
ret = ops->vidioc_g_fmt_vbi_out(file, fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (ops->vidioc_g_fmt_sliced_vbi_cap)
+ if (likely(ops->vidioc_g_fmt_sliced_vbi_cap))
ret = ops->vidioc_g_fmt_sliced_vbi_cap(file,
fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (ops->vidioc_g_fmt_sliced_vbi_out)
+ if (likely(ops->vidioc_g_fmt_sliced_vbi_out))
ret = ops->vidioc_g_fmt_sliced_vbi_out(file,
fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
- if (ops->vidioc_g_fmt_type_private)
+ if (likely(ops->vidioc_g_fmt_type_private))
ret = ops->vidioc_g_fmt_type_private(file,
fh, f);
break;
}
+ if (unlikely(ret == -ENOTTY && have_fmt_ops(g)))
+ ret = -EINVAL;
break;
}
@@ -822,6 +709,14 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_format *f = (struct v4l2_format *)arg;
+ if (!have_fmt_ops(s))
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = -EINVAL;
+
/* FIXME: Should be one dump per type */
dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
@@ -829,44 +724,15 @@ static long __video_do_ioctl(struct file *file,
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.pix);
v4l_print_pix_fmt(vfd, &f->fmt.pix);
- if (ops->vidioc_s_fmt_vid_cap) {
+ if (ops->vidioc_s_fmt_vid_cap)
ret = ops->vidioc_s_fmt_vid_cap(file, fh, f);
- } else if (ops->vidioc_s_fmt_vid_cap_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_s_fmt_vid_cap_mplane(file, fh,
- &f_copy);
- if (ret)
- break;
-
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- /* Drivers shouldn't adjust from 1-plane
- * to more than 1-plane formats */
- ret = -EBUSY;
- WARN_ON(1);
- break;
- }
-
- ret = fmt_mp_to_sp(&f_copy, f);
- }
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
CLEAR_AFTER_FIELD(f, fmt.pix_mp);
v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
- if (ops->vidioc_s_fmt_vid_cap_mplane) {
+ if (ops->vidioc_s_fmt_vid_cap_mplane)
ret = ops->vidioc_s_fmt_vid_cap_mplane(file,
fh, f);
- } else if (ops->vidioc_s_fmt_vid_cap &&
- f->fmt.pix_mp.num_planes == 1) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_s_fmt_vid_cap(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_sp_to_mp(&f_copy, f);
- }
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
@@ -877,44 +743,15 @@ static long __video_do_ioctl(struct file *file,
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.pix);
v4l_print_pix_fmt(vfd, &f->fmt.pix);
- if (ops->vidioc_s_fmt_vid_out) {
+ if (ops->vidioc_s_fmt_vid_out)
ret = ops->vidioc_s_fmt_vid_out(file, fh, f);
- } else if (ops->vidioc_s_fmt_vid_out_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_s_fmt_vid_out_mplane(file, fh,
- &f_copy);
- if (ret)
- break;
-
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- /* Drivers shouldn't adjust from 1-plane
- * to more than 1-plane formats */
- ret = -EBUSY;
- WARN_ON(1);
- break;
- }
-
- ret = fmt_mp_to_sp(&f_copy, f);
- }
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
CLEAR_AFTER_FIELD(f, fmt.pix_mp);
v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
- if (ops->vidioc_s_fmt_vid_out_mplane) {
+ if (ops->vidioc_s_fmt_vid_out_mplane)
ret = ops->vidioc_s_fmt_vid_out_mplane(file,
fh, f);
- } else if (ops->vidioc_s_fmt_vid_out &&
- f->fmt.pix_mp.num_planes == 1) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_s_fmt_vid_out(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_mp_to_sp(&f_copy, f);
- }
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
@@ -924,29 +761,30 @@ static long __video_do_ioctl(struct file *file,
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (ops->vidioc_s_fmt_vbi_cap)
+ if (likely(ops->vidioc_s_fmt_vbi_cap))
ret = ops->vidioc_s_fmt_vbi_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (ops->vidioc_s_fmt_vbi_out)
+ if (likely(ops->vidioc_s_fmt_vbi_out))
ret = ops->vidioc_s_fmt_vbi_out(file, fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (ops->vidioc_s_fmt_sliced_vbi_cap)
+ if (likely(ops->vidioc_s_fmt_sliced_vbi_cap))
ret = ops->vidioc_s_fmt_sliced_vbi_cap(file,
fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (ops->vidioc_s_fmt_sliced_vbi_out)
+ if (likely(ops->vidioc_s_fmt_sliced_vbi_out))
ret = ops->vidioc_s_fmt_sliced_vbi_out(file,
fh, f);
+
break;
case V4L2_BUF_TYPE_PRIVATE:
/* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
- if (ops->vidioc_s_fmt_type_private)
+ if (likely(ops->vidioc_s_fmt_type_private))
ret = ops->vidioc_s_fmt_type_private(file,
fh, f);
break;
@@ -963,132 +801,77 @@ static long __video_do_ioctl(struct file *file,
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.pix);
- if (ops->vidioc_try_fmt_vid_cap) {
+ if (ops->vidioc_try_fmt_vid_cap)
ret = ops->vidioc_try_fmt_vid_cap(file, fh, f);
- } else if (ops->vidioc_try_fmt_vid_cap_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_try_fmt_vid_cap_mplane(file,
- fh, &f_copy);
- if (ret)
- break;
-
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- /* Drivers shouldn't adjust from 1-plane
- * to more than 1-plane formats */
- ret = -EBUSY;
- WARN_ON(1);
- break;
- }
- ret = fmt_mp_to_sp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
CLEAR_AFTER_FIELD(f, fmt.pix_mp);
- if (ops->vidioc_try_fmt_vid_cap_mplane) {
+ if (ops->vidioc_try_fmt_vid_cap_mplane)
ret = ops->vidioc_try_fmt_vid_cap_mplane(file,
fh, f);
- } else if (ops->vidioc_try_fmt_vid_cap &&
- f->fmt.pix_mp.num_planes == 1) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_try_fmt_vid_cap(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_sp_to_mp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
- if (ops->vidioc_try_fmt_vid_overlay)
+ if (likely(ops->vidioc_try_fmt_vid_overlay))
ret = ops->vidioc_try_fmt_vid_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.pix);
- if (ops->vidioc_try_fmt_vid_out) {
+ if (ops->vidioc_try_fmt_vid_out)
ret = ops->vidioc_try_fmt_vid_out(file, fh, f);
- } else if (ops->vidioc_try_fmt_vid_out_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_try_fmt_vid_out_mplane(file,
- fh, &f_copy);
- if (ret)
- break;
-
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- /* Drivers shouldn't adjust from 1-plane
- * to more than 1-plane formats */
- ret = -EBUSY;
- WARN_ON(1);
- break;
- }
- ret = fmt_mp_to_sp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
CLEAR_AFTER_FIELD(f, fmt.pix_mp);
- if (ops->vidioc_try_fmt_vid_out_mplane) {
+ if (ops->vidioc_try_fmt_vid_out_mplane)
ret = ops->vidioc_try_fmt_vid_out_mplane(file,
fh, f);
- } else if (ops->vidioc_try_fmt_vid_out &&
- f->fmt.pix_mp.num_planes == 1) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_try_fmt_vid_out(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_sp_to_mp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
- if (ops->vidioc_try_fmt_vid_out_overlay)
+ if (likely(ops->vidioc_try_fmt_vid_out_overlay))
ret = ops->vidioc_try_fmt_vid_out_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (ops->vidioc_try_fmt_vbi_cap)
+ if (likely(ops->vidioc_try_fmt_vbi_cap))
ret = ops->vidioc_try_fmt_vbi_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (ops->vidioc_try_fmt_vbi_out)
+ if (likely(ops->vidioc_try_fmt_vbi_out))
ret = ops->vidioc_try_fmt_vbi_out(file, fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (ops->vidioc_try_fmt_sliced_vbi_cap)
+ if (likely(ops->vidioc_try_fmt_sliced_vbi_cap))
ret = ops->vidioc_try_fmt_sliced_vbi_cap(file,
fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (ops->vidioc_try_fmt_sliced_vbi_out)
+ if (likely(ops->vidioc_try_fmt_sliced_vbi_out))
ret = ops->vidioc_try_fmt_sliced_vbi_out(file,
fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
/* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
- if (ops->vidioc_try_fmt_type_private)
+ if (likely(ops->vidioc_try_fmt_type_private))
ret = ops->vidioc_try_fmt_type_private(file,
fh, f);
break;
}
-
+ if (unlikely(ret == -ENOTTY && have_fmt_ops(try)))
+ ret = -EINVAL;
break;
}
/* FIXME: Those buf reqs could be handled here,
@@ -1101,6 +884,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_reqbufs)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
ret = check_fmt(ops, p->type);
if (ret)
break;
@@ -1166,6 +953,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_overlay)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "value=%d\n", *i);
ret = ops->vidioc_overlay(file, fh, *i);
break;
@@ -1191,6 +982,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_fbuf)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
p->capability, p->flags, (unsigned long)p->base);
v4l_print_pix_fmt(vfd, &p->fmt);
@@ -1203,6 +998,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_streamon)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
ret = ops->vidioc_streamon(file, fh, i);
break;
@@ -1213,6 +1012,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_streamoff)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
ret = ops->vidioc_streamoff(file, fh, i);
break;
@@ -1225,6 +1028,10 @@ static long __video_do_ioctl(struct file *file,
unsigned int index = p->index, i, j = 0;
const char *descr = "";
+ if (id == 0)
+ break;
+ ret = -EINVAL;
+
/* Return norm array in a canonical way */
for (i = 0; i <= index && id; i++) {
/* last std value in the standards array is 0, so this
@@ -1260,16 +1067,15 @@ static long __video_do_ioctl(struct file *file,
{
v4l2_std_id *id = arg;
- ret = 0;
/* Calls the specific handler */
if (ops->vidioc_g_std)
ret = ops->vidioc_g_std(file, fh, id);
- else if (vfd->current_norm)
+ else if (vfd->current_norm) {
+ ret = 0;
*id = vfd->current_norm;
- else
- ret = -EINVAL;
+ }
- if (!ret)
+ if (likely(!ret))
dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id);
break;
}
@@ -1279,15 +1085,20 @@ static long __video_do_ioctl(struct file *file,
dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id);
+ if (!ops->vidioc_s_std)
+ break;
+
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = -EINVAL;
norm = (*id) & vfd->tvnorms;
if (vfd->tvnorms && !norm) /* Check if std is supported */
break;
/* Calls the specific handler */
- if (ops->vidioc_s_std)
- ret = ops->vidioc_s_std(file, fh, &norm);
- else
- ret = -EINVAL;
+ ret = ops->vidioc_s_std(file, fh, &norm);
/* Updates standard information */
if (ret >= 0)
@@ -1300,6 +1111,14 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_querystd)
break;
+ /*
+ * If nothing detected, it should return all supported
+ * Drivers just need to mask the std argument, in order
+ * to remove the standards that don't apply from the mask.
+ * This means that tuners, audio and video decoders can join
+ * their efforts to improve the standards detection
+ */
+ *p = vfd->tvnorms;
ret = ops->vidioc_querystd(file, fh, arg);
if (!ret)
dbgarg(cmd, "detected std=%08Lx\n",
@@ -1356,6 +1175,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_input)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "value=%d\n", *i);
ret = ops->vidioc_s_input(file, fh, *i);
break;
@@ -1408,6 +1231,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_output)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "value=%d\n", *i);
ret = ops->vidioc_s_output(file, fh, *i);
break;
@@ -1418,7 +1245,9 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_queryctrl *p = arg;
- if (vfd->ctrl_handler)
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_queryctrl(vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
ret = v4l2_queryctrl(vfd->ctrl_handler, p);
else if (ops->vidioc_queryctrl)
ret = ops->vidioc_queryctrl(file, fh, p);
@@ -1438,7 +1267,9 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_control *p = arg;
- if (vfd->ctrl_handler)
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_g_ctrl(vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
else if (ops->vidioc_g_ctrl)
ret = ops->vidioc_g_ctrl(file, fh, p);
@@ -1470,14 +1301,22 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls ctrls;
struct v4l2_ext_control ctrl;
- if (!vfd->ctrl_handler &&
+ if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
+ if (vfh && vfh->ctrl_handler) {
+ ret = v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
+ break;
+ }
if (vfd->ctrl_handler) {
- ret = v4l2_s_ctrl(vfd->ctrl_handler, p);
+ ret = v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
break;
}
if (ops->vidioc_s_ctrl) {
@@ -1494,6 +1333,8 @@ static long __video_do_ioctl(struct file *file,
ctrl.value = p->value;
if (check_ext_ctrls(&ctrls, 1))
ret = ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
+ else
+ ret = -EINVAL;
break;
}
case VIDIOC_G_EXT_CTRLS:
@@ -1501,10 +1342,14 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (vfd->ctrl_handler)
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
- else if (ops->vidioc_g_ext_ctrls && check_ext_ctrls(p, 0))
- ret = ops->vidioc_g_ext_ctrls(file, fh, p);
+ else if (ops->vidioc_g_ext_ctrls)
+ ret = check_ext_ctrls(p, 0) ?
+ ops->vidioc_g_ext_ctrls(file, fh, p) :
+ -EINVAL;
else
break;
v4l_print_ext_ctrls(cmd, vfd, p, !ret);
@@ -1515,13 +1360,22 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls)
+ if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
+ !ops->vidioc_s_ext_ctrls)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
v4l_print_ext_ctrls(cmd, vfd, p, 1);
- if (vfd->ctrl_handler)
- ret = v4l2_s_ext_ctrls(vfd->ctrl_handler, p);
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
+ ret = v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
else if (check_ext_ctrls(p, 0))
ret = ops->vidioc_s_ext_ctrls(file, fh, p);
+ else
+ ret = -EINVAL;
break;
}
case VIDIOC_TRY_EXT_CTRLS:
@@ -1529,20 +1383,27 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!vfd->ctrl_handler && !ops->vidioc_try_ext_ctrls)
+ if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
+ !ops->vidioc_try_ext_ctrls)
break;
v4l_print_ext_ctrls(cmd, vfd, p, 1);
- if (vfd->ctrl_handler)
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
else if (check_ext_ctrls(p, 0))
ret = ops->vidioc_try_ext_ctrls(file, fh, p);
+ else
+ ret = -EINVAL;
break;
}
case VIDIOC_QUERYMENU:
{
struct v4l2_querymenu *p = arg;
- if (vfd->ctrl_handler)
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_querymenu(vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
ret = v4l2_querymenu(vfd->ctrl_handler, p);
else if (ops->vidioc_querymenu)
ret = ops->vidioc_querymenu(file, fh, p);
@@ -1594,6 +1455,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_audio)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
"mode=0x%x\n", p->index, p->name,
p->capability, p->mode);
@@ -1634,6 +1499,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_audout)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "index=%d, name=%s, capability=%d, "
"mode=%d\n", p->index, p->name,
p->capability, p->mode);
@@ -1663,6 +1532,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_modulator)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "index=%d, name=%s, capability=%d, "
"rangelow=%d, rangehigh=%d, txsubchans=%d\n",
p->index, p->name, p->capability, p->rangelow,
@@ -1689,6 +1562,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_crop)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
dbgrect(vfd, "", &p->c);
ret = ops->vidioc_s_crop(file, fh, p);
@@ -1732,11 +1609,15 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_g_jpegcomp)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, "
"COM_len=%d, jpeg_markers=%d\n",
p->quality, p->APPn, p->APP_len,
p->COM_len, p->jpeg_markers);
- ret = ops->vidioc_s_jpegcomp(file, fh, p);
+ ret = ops->vidioc_s_jpegcomp(file, fh, p);
break;
}
case VIDIOC_G_ENC_INDEX:
@@ -1757,6 +1638,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_encoder_cmd)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
ret = ops->vidioc_encoder_cmd(file, fh, p);
if (!ret)
dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
@@ -1777,6 +1662,8 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_streamparm *p = arg;
+ if (!ops->vidioc_g_parm && !vfd->current_norm)
+ break;
if (ops->vidioc_g_parm) {
ret = check_fmt(ops, p->type);
if (ret)
@@ -1785,14 +1672,13 @@ static long __video_do_ioctl(struct file *file,
} else {
v4l2_std_id std = vfd->current_norm;
+ ret = -EINVAL;
if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
break;
ret = 0;
if (ops->vidioc_g_std)
ret = ops->vidioc_g_std(file, fh, &std);
- else if (std == 0)
- ret = -EINVAL;
if (ret == 0)
v4l2_video_std_frame_period(std,
&p->parm.capture.timeperframe);
@@ -1807,6 +1693,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_parm)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
ret = check_fmt(ops, p->type);
if (ret)
break;
@@ -1842,6 +1732,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_tuner)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
dbgarg(cmd, "index=%d, name=%s, type=%d, "
@@ -1876,6 +1770,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_frequency)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
p->tuner, p->type, p->frequency);
ret = ops->vidioc_s_frequency(file, fh, p);
@@ -1950,6 +1848,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_hw_freq_seek)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
dbgarg(cmd,
@@ -2054,6 +1956,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_dv_preset)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "preset=%d\n", p->preset);
ret = ops->vidioc_s_dv_preset(file, fh, p);
@@ -2089,6 +1995,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_dv_timings)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
switch (p->type) {
case V4L2_DV_BT_656_1120:
@@ -2196,20 +2106,47 @@ static long __video_do_ioctl(struct file *file,
dbgarg(cmd, "type=0x%8.8x", sub->type);
break;
}
- default:
+ case VIDIOC_CREATE_BUFS:
{
- bool valid_prio = true;
+ struct v4l2_create_buffers *create = arg;
- if (!ops->vidioc_default)
+ if (!ops->vidioc_create_bufs)
break;
- if (use_fh_prio)
- valid_prio = v4l2_prio_check(vfd->prio, vfh->prio) >= 0;
- ret = ops->vidioc_default(file, fh, valid_prio, cmd, arg);
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = check_fmt(ops, create->format.type);
+ if (ret)
+ break;
+
+ ret = ops->vidioc_create_bufs(file, fh, create);
+
+ dbgarg(cmd, "count=%d @ %d\n", create->count, create->index);
break;
}
+ case VIDIOC_PREPARE_BUF:
+ {
+ struct v4l2_buffer *b = arg;
+
+ if (!ops->vidioc_prepare_buf)
+ break;
+ ret = check_fmt(ops, b->type);
+ if (ret)
+ break;
+
+ ret = ops->vidioc_prepare_buf(file, fh, b);
+
+ dbgarg(cmd, "index=%d", b->index);
+ break;
+ }
+ default:
+ if (!ops->vidioc_default)
+ break;
+ ret = ops->vidioc_default(file, fh, ret_prio >= 0, cmd, arg);
+ break;
} /* switch */
-exit_prio:
if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
if (ret < 0) {
v4l_print_ioctl(vfd->name, cmd);
@@ -2276,7 +2213,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
break;
}
*user_ptr = (void __user *)buf->m.planes;
- *kernel_ptr = (void **)&buf->m.planes;
+ *kernel_ptr = (void *)&buf->m.planes;
*array_size = sizeof(struct v4l2_plane) * buf->length;
ret = 1;
}
@@ -2290,7 +2227,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
if (ctrls->count != 0) {
*user_ptr = (void __user *)ctrls->controls;
- *kernel_ptr = (void **)&ctrls->controls;
+ *kernel_ptr = (void *)&ctrls->controls;
*array_size = sizeof(struct v4l2_ext_control)
* ctrls->count;
ret = 1;
diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c
index 3b15bf5892a8..975d0fa938c6 100644
--- a/drivers/media/video/v4l2-mem2mem.c
+++ b/drivers/media/video/v4l2-mem2mem.c
@@ -97,11 +97,12 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
- if (list_empty(&q_ctx->rdy_queue))
- goto end;
+ if (list_empty(&q_ctx->rdy_queue)) {
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return NULL;
+ }
b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
-end:
spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
return &b->vb;
}
@@ -117,12 +118,13 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
unsigned long flags;
spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
- if (!list_empty(&q_ctx->rdy_queue)) {
- b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer,
- list);
- list_del(&b->list);
- q_ctx->num_rdy--;
+ if (list_empty(&q_ctx->rdy_queue)) {
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return NULL;
}
+ b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
+ list_del(&b->list);
+ q_ctx->num_rdy--;
spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
return &b->vb;
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
index 812729ebf09e..65ade5f03c2e 100644
--- a/drivers/media/video/v4l2-subdev.c
+++ b/drivers/media/video/v4l2-subdev.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/videodev2.h>
+#include <linux/export.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -75,20 +76,7 @@ static int subdev_open(struct file *file)
return ret;
}
- ret = v4l2_fh_init(&subdev_fh->vfh, vdev);
- if (ret)
- goto err;
-
- if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) {
- ret = v4l2_event_init(&subdev_fh->vfh);
- if (ret)
- goto err;
-
- ret = v4l2_event_alloc(&subdev_fh->vfh, sd->nevents);
- if (ret)
- goto err;
- }
-
+ v4l2_fh_init(&subdev_fh->vfh, vdev);
v4l2_fh_add(&subdev_fh->vfh);
file->private_data = &subdev_fh->vfh;
#if defined(CONFIG_MEDIA_CONTROLLER)
@@ -155,25 +143,25 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
switch (cmd) {
case VIDIOC_QUERYCTRL:
- return v4l2_queryctrl(sd->ctrl_handler, arg);
+ return v4l2_queryctrl(vfh->ctrl_handler, arg);
case VIDIOC_QUERYMENU:
- return v4l2_querymenu(sd->ctrl_handler, arg);
+ return v4l2_querymenu(vfh->ctrl_handler, arg);
case VIDIOC_G_CTRL:
- return v4l2_g_ctrl(sd->ctrl_handler, arg);
+ return v4l2_g_ctrl(vfh->ctrl_handler, arg);
case VIDIOC_S_CTRL:
- return v4l2_s_ctrl(sd->ctrl_handler, arg);
+ return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg);
case VIDIOC_G_EXT_CTRLS:
- return v4l2_g_ext_ctrls(sd->ctrl_handler, arg);
+ return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
case VIDIOC_S_EXT_CTRLS:
- return v4l2_s_ext_ctrls(sd->ctrl_handler, arg);
+ return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
case VIDIOC_TRY_EXT_CTRLS:
- return v4l2_try_ext_ctrls(sd->ctrl_handler, arg);
+ return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
case VIDIOC_DQEVENT:
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
@@ -186,6 +174,25 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_UNSUBSCRIBE_EVENT:
return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ {
+ struct v4l2_dbg_register *p = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return v4l2_subdev_call(sd, core, g_register, p);
+ }
+ case VIDIOC_DBG_S_REGISTER:
+ {
+ struct v4l2_dbg_register *p = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return v4l2_subdev_call(sd, core, s_register, p);
+ }
+#endif
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
case VIDIOC_SUBDEV_G_FMT: {
struct v4l2_subdev_format *format = arg;
@@ -297,7 +304,7 @@ static unsigned int subdev_poll(struct file *file, poll_table *wait)
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
return POLLERR;
- poll_wait(file, &fh->events->wait, wait);
+ poll_wait(file, &fh->wait, wait);
if (v4l2_event_pending(fh))
return POLLPRI;
diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
index 85d3048c1d67..cbf13d09b4ac 100644
--- a/drivers/media/video/via-camera.c
+++ b/drivers/media/video/via-camera.c
@@ -21,7 +21,7 @@
#include <media/videobuf-dma-sg.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/via-core.h>
#include <linux/via-gpio.h>
#include <linux/via_i2c.h>
@@ -69,7 +69,7 @@ struct via_camera {
struct mutex lock;
enum viacam_opstate opstate;
unsigned long flags;
- struct pm_qos_request_list qos_request;
+ struct pm_qos_request qos_request;
/*
* GPIO info for power/reset management
*/
@@ -1332,6 +1332,8 @@ static __devinit bool viacam_serial_is_enabled(void)
struct pci_bus *pbus = pci_find_bus(0, 0);
u8 cbyte;
+ if (!pbus)
+ return false;
pci_bus_read_config_byte(pbus, VIACAM_SERIAL_DEVFN,
VIACAM_SERIAL_CREG, &cbyte);
if ((cbyte & VIACAM_SERIAL_BIT) == 0)
diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c
index ddb8f4b46c03..f300deafd268 100644
--- a/drivers/media/video/videobuf-dma-sg.c
+++ b/drivers/media/video/videobuf-dma-sg.c
@@ -108,8 +108,9 @@ static struct scatterlist *videobuf_pages_to_sg(struct page **pages,
if (PageHighMem(pages[0]))
/* DMA to highmem pages might not work */
goto highmem;
- sg_set_page(&sglist[0], pages[0], PAGE_SIZE - offset, offset);
- size -= PAGE_SIZE - offset;
+ sg_set_page(&sglist[0], pages[0],
+ min_t(size_t, PAGE_SIZE - offset, size), offset);
+ size -= min_t(size_t, PAGE_SIZE - offset, size);
for (i = 1; i < nr_pages; i++) {
if (NULL == pages[i])
goto nopage;
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 3015e6000946..95a3f5e82aef 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -38,13 +38,13 @@ module_param(debug, int, 0644);
(((q)->ops->op) ? ((q)->ops->op(args)) : 0)
#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
- V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR)
+ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
+ V4L2_BUF_FLAG_PREPARED)
/**
* __vb2_buf_mem_alloc() - allocate video memory for the given buffer
*/
-static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
- unsigned long *plane_sizes)
+static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
void *mem_priv;
@@ -53,13 +53,13 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
/* Allocate memory for all planes in this buffer */
for (plane = 0; plane < vb->num_planes; ++plane) {
mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane],
- plane_sizes[plane]);
+ q->plane_sizes[plane]);
if (IS_ERR_OR_NULL(mem_priv))
goto free;
/* Associate allocator private data with this plane */
vb->planes[plane].mem_priv = mem_priv;
- vb->v4l2_planes[plane].length = plane_sizes[plane];
+ vb->v4l2_planes[plane].length = q->plane_sizes[plane];
}
return 0;
@@ -110,18 +110,28 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
* __setup_offsets() - setup unique offsets ("cookies") for every plane in
* every buffer on the queue
*/
-static void __setup_offsets(struct vb2_queue *q)
+static void __setup_offsets(struct vb2_queue *q, unsigned int n)
{
unsigned int buffer, plane;
struct vb2_buffer *vb;
- unsigned long off = 0;
+ unsigned long off;
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ if (q->num_buffers) {
+ struct v4l2_plane *p;
+ vb = q->bufs[q->num_buffers - 1];
+ p = &vb->v4l2_planes[vb->num_planes - 1];
+ off = PAGE_ALIGN(p->m.mem_offset + p->length);
+ } else {
+ off = 0;
+ }
+
+ for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
vb = q->bufs[buffer];
if (!vb)
continue;
for (plane = 0; plane < vb->num_planes; ++plane) {
+ vb->v4l2_planes[plane].length = q->plane_sizes[plane];
vb->v4l2_planes[plane].m.mem_offset = off;
dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n",
@@ -141,8 +151,7 @@ static void __setup_offsets(struct vb2_queue *q)
* Returns the number of buffers successfully allocated.
*/
static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
- unsigned int num_buffers, unsigned int num_planes,
- unsigned long plane_sizes[])
+ unsigned int num_buffers, unsigned int num_planes)
{
unsigned int buffer;
struct vb2_buffer *vb;
@@ -163,13 +172,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
vb->state = VB2_BUF_STATE_DEQUEUED;
vb->vb2_queue = q;
vb->num_planes = num_planes;
- vb->v4l2_buf.index = buffer;
+ vb->v4l2_buf.index = q->num_buffers + buffer;
vb->v4l2_buf.type = q->type;
vb->v4l2_buf.memory = memory;
/* Allocate video buffer memory for the MMAP type */
if (memory == V4L2_MEMORY_MMAP) {
- ret = __vb2_buf_mem_alloc(vb, plane_sizes);
+ ret = __vb2_buf_mem_alloc(vb);
if (ret) {
dprintk(1, "Failed allocating memory for "
"buffer %d\n", buffer);
@@ -191,15 +200,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
}
}
- q->bufs[buffer] = vb;
+ q->bufs[q->num_buffers + buffer] = vb;
}
- q->num_buffers = buffer;
-
- __setup_offsets(q);
+ __setup_offsets(q, buffer);
dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
- q->num_buffers, num_planes);
+ buffer, num_planes);
return buffer;
}
@@ -207,12 +214,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
/**
* __vb2_free_mem() - release all video buffer memory for a given queue
*/
-static void __vb2_free_mem(struct vb2_queue *q)
+static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
{
unsigned int buffer;
struct vb2_buffer *vb;
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
vb = q->bufs[buffer];
if (!vb)
continue;
@@ -226,17 +234,18 @@ static void __vb2_free_mem(struct vb2_queue *q)
}
/**
- * __vb2_queue_free() - free the queue - video memory and related information
- * and return the queue to an uninitialized state. Might be called even if the
- * queue has already been freed.
+ * __vb2_queue_free() - free buffers at the end of the queue - video memory and
+ * related information, if no buffers are left return the queue to an
+ * uninitialized state. Might be called even if the queue has already been freed.
*/
-static void __vb2_queue_free(struct vb2_queue *q)
+static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
{
unsigned int buffer;
/* Call driver-provided cleanup function for each buffer, if provided */
if (q->ops->buf_cleanup) {
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
if (NULL == q->bufs[buffer])
continue;
q->ops->buf_cleanup(q->bufs[buffer]);
@@ -244,23 +253,26 @@ static void __vb2_queue_free(struct vb2_queue *q)
}
/* Release video buffer memory */
- __vb2_free_mem(q);
+ __vb2_free_mem(q, buffers);
/* Free videobuf buffers */
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
kfree(q->bufs[buffer]);
q->bufs[buffer] = NULL;
}
- q->num_buffers = 0;
- q->memory = 0;
+ q->num_buffers -= buffers;
+ if (!q->num_buffers)
+ q->memory = 0;
+ INIT_LIST_HEAD(&q->queued_list);
}
/**
* __verify_planes_array() - verify that the planes array passed in struct
* v4l2_buffer from userspace can be safely used
*/
-static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b)
+static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b)
{
/* Is memory for copying plane information present? */
if (NULL == b->m.planes) {
@@ -279,13 +291,48 @@ static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b)
}
/**
+ * __buffer_in_use() - return true if the buffer is in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
+{
+ unsigned int plane;
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ void *mem_priv = vb->planes[plane].mem_priv;
+ /*
+ * If num_users() has not been provided, call_memop
+ * will return 0, apparently nobody cares about this
+ * case anyway. If num_users() returns more than 1,
+ * we are not the only user of the plane's memory.
+ */
+ if (mem_priv && call_memop(q, plane, num_users, mem_priv) > 1)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * __buffers_in_use() - return true if any buffers on the queue are in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffers_in_use(struct vb2_queue *q)
+{
+ unsigned int buffer;
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ if (__buffer_in_use(q, q->bufs[buffer]))
+ return true;
+ }
+ return false;
+}
+
+/**
* __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
* returned to userspace
*/
static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
{
struct vb2_queue *q = vb->vb2_queue;
- int ret = 0;
+ int ret;
/* Copy back data such as timestamp, flags, input, etc. */
memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
@@ -332,15 +379,18 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
case VB2_BUF_STATE_DONE:
b->flags |= V4L2_BUF_FLAG_DONE;
break;
+ case VB2_BUF_STATE_PREPARED:
+ b->flags |= V4L2_BUF_FLAG_PREPARED;
+ break;
case VB2_BUF_STATE_DEQUEUED:
/* nothing */
break;
}
- if (vb->num_planes_mapped == vb->num_planes)
+ if (__buffer_in_use(q, vb))
b->flags |= V4L2_BUF_FLAG_MAPPED;
- return ret;
+ return 0;
}
/**
@@ -402,33 +452,6 @@ static int __verify_mmap_ops(struct vb2_queue *q)
}
/**
- * __buffers_in_use() - return true if any buffers on the queue are in use and
- * the queue cannot be freed (by the means of REQBUFS(0)) call
- */
-static bool __buffers_in_use(struct vb2_queue *q)
-{
- unsigned int buffer, plane;
- struct vb2_buffer *vb;
-
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
- vb = q->bufs[buffer];
- for (plane = 0; plane < vb->num_planes; ++plane) {
- /*
- * If num_users() has not been provided, call_memop
- * will return 0, apparently nobody cares about this
- * case anyway. If num_users() returns more than 1,
- * we are not the only user of the plane's memory.
- */
- if (call_memop(q, plane, num_users,
- vb->planes[plane].mem_priv) > 1)
- return true;
- }
- }
-
- return false;
-}
-
-/**
* vb2_reqbufs() - Initiate streaming
* @q: videobuf2 queue
* @req: struct passed from userspace to vidioc_reqbufs handler in driver
@@ -453,8 +476,7 @@ static bool __buffers_in_use(struct vb2_queue *q)
*/
int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
- unsigned int num_buffers, num_planes;
- unsigned long plane_sizes[VIDEO_MAX_PLANES];
+ unsigned int num_buffers, allocated_buffers, num_planes = 0;
int ret = 0;
if (q->fileio) {
@@ -502,7 +524,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
return -EBUSY;
}
- __vb2_queue_free(q);
+ __vb2_queue_free(q, q->num_buffers);
/*
* In case of REQBUFS(0) return immediately without calling
@@ -516,7 +538,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
* Make sure the requested values and current defaults are sane.
*/
num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
- memset(plane_sizes, 0, sizeof(plane_sizes));
+ memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
q->memory = req->memory;
@@ -524,57 +546,180 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
* Ask the driver how many buffers and planes per buffer it requires.
* Driver also sets the size and allocator context for each plane.
*/
- ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
- plane_sizes, q->alloc_ctx);
+ ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
+ q->plane_sizes, q->alloc_ctx);
if (ret)
return ret;
/* Finally, allocate buffers and video memory */
- ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes,
- plane_sizes);
+ ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
if (ret == 0) {
dprintk(1, "Memory allocation failed\n");
return -ENOMEM;
}
+ allocated_buffers = ret;
+
/*
* Check if driver can handle the allocated number of buffers.
*/
- if (ret < num_buffers) {
- unsigned int orig_num_buffers;
+ if (allocated_buffers < num_buffers) {
+ num_buffers = allocated_buffers;
- orig_num_buffers = num_buffers = ret;
- ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
- plane_sizes, q->alloc_ctx);
- if (ret)
- goto free_mem;
+ ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
- if (orig_num_buffers < num_buffers) {
+ if (!ret && allocated_buffers < num_buffers)
ret = -ENOMEM;
- goto free_mem;
- }
/*
- * Ok, driver accepted smaller number of buffers.
+ * Either the driver has accepted a smaller number of buffers,
+ * or .queue_setup() returned an error
*/
- ret = num_buffers;
+ }
+
+ q->num_buffers = allocated_buffers;
+
+ if (ret < 0) {
+ __vb2_queue_free(q, allocated_buffers);
+ return ret;
}
/*
* Return the number of successfully allocated buffers
* to the userspace.
*/
- req->count = ret;
+ req->count = allocated_buffers;
return 0;
-
-free_mem:
- __vb2_queue_free(q);
- return ret;
}
EXPORT_SYMBOL_GPL(vb2_reqbufs);
/**
+ * vb2_create_bufs() - Allocate buffers and any required auxiliary structs
+ * @q: videobuf2 queue
+ * @create: creation parameters, passed from userspace to vidioc_create_bufs
+ * handler in driver
+ *
+ * Should be called from vidioc_create_bufs ioctl handler of a driver.
+ * This function:
+ * 1) verifies parameter sanity
+ * 2) calls the .queue_setup() queue operation
+ * 3) performs any necessary memory allocations
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_create_bufs handler in driver.
+ */
+int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+{
+ unsigned int num_planes = 0, num_buffers, allocated_buffers;
+ int ret = 0;
+
+ if (q->fileio) {
+ dprintk(1, "%s(): file io in progress\n", __func__);
+ return -EBUSY;
+ }
+
+ if (create->memory != V4L2_MEMORY_MMAP
+ && create->memory != V4L2_MEMORY_USERPTR) {
+ dprintk(1, "%s(): unsupported memory type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (create->format.type != q->type) {
+ dprintk(1, "%s(): requested type is incorrect\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure all the required memory ops for given memory type
+ * are available.
+ */
+ if (create->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
+ dprintk(1, "%s(): MMAP for current setup unsupported\n", __func__);
+ return -EINVAL;
+ }
+
+ if (create->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
+ dprintk(1, "%s(): USERPTR for current setup unsupported\n", __func__);
+ return -EINVAL;
+ }
+
+ if (q->num_buffers == VIDEO_MAX_FRAME) {
+ dprintk(1, "%s(): maximum number of buffers already allocated\n",
+ __func__);
+ return -ENOBUFS;
+ }
+
+ create->index = q->num_buffers;
+
+ if (!q->num_buffers) {
+ memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
+ memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+ q->memory = create->memory;
+ }
+
+ num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers);
+
+ /*
+ * Ask the driver, whether the requested number of buffers, planes per
+ * buffer and their sizes are acceptable
+ */
+ ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
+ if (ret)
+ return ret;
+
+ /* Finally, allocate buffers and video memory */
+ ret = __vb2_queue_alloc(q, create->memory, num_buffers,
+ num_planes);
+ if (ret < 0) {
+ dprintk(1, "Memory allocation failed with error: %d\n", ret);
+ return ret;
+ }
+
+ allocated_buffers = ret;
+
+ /*
+ * Check if driver can handle the so far allocated number of buffers.
+ */
+ if (ret < num_buffers) {
+ num_buffers = ret;
+
+ /*
+ * q->num_buffers contains the total number of buffers, that the
+ * queue driver has set up
+ */
+ ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
+
+ if (!ret && allocated_buffers < num_buffers)
+ ret = -ENOMEM;
+
+ /*
+ * Either the driver has accepted a smaller number of buffers,
+ * or .queue_setup() returned an error
+ */
+ }
+
+ q->num_buffers += allocated_buffers;
+
+ if (ret < 0) {
+ __vb2_queue_free(q, allocated_buffers);
+ return ret;
+ }
+
+ /*
+ * Return the number of successfully allocated buffers
+ * to the userspace.
+ */
+ create->count = allocated_buffers;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_create_bufs);
+
+/**
* vb2_plane_vaddr() - Return a kernel virtual address of a given plane
* @vb: vb2_buffer to which the plane in question belongs to
* @plane_no: plane number for which the address is to be returned
@@ -658,7 +803,7 @@ EXPORT_SYMBOL_GPL(vb2_buffer_done);
* __fill_vb2_buffer() - fill a vb2_buffer with information provided in
* a v4l2_buffer by the userspace
*/
-static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
+static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b,
struct v4l2_plane *v4l2_planes)
{
unsigned int plane;
@@ -722,7 +867,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
/**
* __qbuf_userptr() - handle qbuf of a USERPTR buffer
*/
-static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
+static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
{
struct v4l2_plane planes[VIDEO_MAX_PLANES];
struct vb2_queue *q = vb->vb2_queue;
@@ -745,12 +890,20 @@ static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
dprintk(3, "qbuf: userspace address for plane %d changed, "
"reacquiring memory\n", plane);
+ /* Check if the provided plane buffer is large enough */
+ if (planes[plane].length < q->plane_sizes[plane]) {
+ ret = -EINVAL;
+ goto err;
+ }
+
/* Release previously acquired memory if present */
if (vb->planes[plane].mem_priv)
call_memop(q, plane, put_userptr,
vb->planes[plane].mem_priv);
vb->planes[plane].mem_priv = NULL;
+ vb->v4l2_planes[plane].m.userptr = 0;
+ vb->v4l2_planes[plane].length = 0;
/* Acquire each plane's memory */
if (q->mem_ops->get_userptr) {
@@ -788,10 +941,13 @@ static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
return 0;
err:
/* In case of errors, release planes that were already acquired */
- for (; plane > 0; --plane) {
- call_memop(q, plane, put_userptr,
- vb->planes[plane - 1].mem_priv);
- vb->planes[plane - 1].mem_priv = NULL;
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (vb->planes[plane].mem_priv)
+ call_memop(q, plane, put_userptr,
+ vb->planes[plane].mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ vb->v4l2_planes[plane].m.userptr = 0;
+ vb->v4l2_planes[plane].length = 0;
}
return ret;
@@ -800,7 +956,7 @@ err:
/**
* __qbuf_mmap() - handle qbuf of an MMAP buffer
*/
-static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b)
+static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
{
return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
}
@@ -817,6 +973,95 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
q->ops->buf_queue(vb);
}
+static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ int ret;
+
+ switch (q->memory) {
+ case V4L2_MEMORY_MMAP:
+ ret = __qbuf_mmap(vb, b);
+ break;
+ case V4L2_MEMORY_USERPTR:
+ ret = __qbuf_userptr(vb, b);
+ break;
+ default:
+ WARN(1, "Invalid queue type\n");
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ ret = call_qop(q, buf_prepare, vb);
+ if (ret)
+ dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
+ else
+ vb->state = VB2_BUF_STATE_PREPARED;
+
+ return ret;
+}
+
+/**
+ * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
+ * @q: videobuf2 queue
+ * @b: buffer structure passed from userspace to vidioc_prepare_buf
+ * handler in driver
+ *
+ * Should be called from vidioc_prepare_buf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) calls buf_prepare callback in the driver (if provided), in which
+ * driver-specific buffer initialization can be performed,
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_prepare_buf handler in driver.
+ */
+int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+ struct vb2_buffer *vb;
+ int ret;
+
+ if (q->fileio) {
+ dprintk(1, "%s(): file io in progress\n", __func__);
+ return -EBUSY;
+ }
+
+ if (b->type != q->type) {
+ dprintk(1, "%s(): invalid buffer type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (b->index >= q->num_buffers) {
+ dprintk(1, "%s(): buffer index out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ vb = q->bufs[b->index];
+ if (NULL == vb) {
+ /* Should never happen */
+ dprintk(1, "%s(): buffer is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (b->memory != q->memory) {
+ dprintk(1, "%s(): invalid memory type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state);
+ return -EINVAL;
+ }
+
+ ret = __buf_prepare(vb, b);
+ if (ret < 0)
+ return ret;
+
+ __fill_v4l2_buffer(vb, b);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_prepare_buf);
+
/**
* vb2_qbuf() - Queue a buffer from userspace
* @q: videobuf2 queue
@@ -826,8 +1071,8 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
* Should be called from vidioc_qbuf ioctl handler of a driver.
* This function:
* 1) verifies the passed buffer,
- * 2) calls buf_prepare callback in the driver (if provided), in which
- * driver-specific buffer initialization can be performed,
+ * 2) if necessary, calls buf_prepare callback in the driver (if provided), in
+ * which driver-specific buffer initialization can be performed,
* 3) if streaming is on, queues the buffer in driver by the means of buf_queue
* callback for processing.
*
@@ -837,7 +1082,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
struct vb2_buffer *vb;
- int ret = 0;
+ int ret;
if (q->fileio) {
dprintk(1, "qbuf: file io in progress\n");
@@ -866,29 +1111,18 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
return -EINVAL;
}
- if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ switch (vb->state) {
+ case VB2_BUF_STATE_DEQUEUED:
+ ret = __buf_prepare(vb, b);
+ if (ret)
+ return ret;
+ case VB2_BUF_STATE_PREPARED:
+ break;
+ default:
dprintk(1, "qbuf: buffer already in use\n");
return -EINVAL;
}
- if (q->memory == V4L2_MEMORY_MMAP)
- ret = __qbuf_mmap(vb, b);
- else if (q->memory == V4L2_MEMORY_USERPTR)
- ret = __qbuf_userptr(vb, b);
- else {
- WARN(1, "Invalid queue type\n");
- return -EINVAL;
- }
-
- if (ret)
- return ret;
-
- ret = call_qop(q, buf_prepare, vb);
- if (ret) {
- dprintk(1, "qbuf: buffer preparation failed\n");
- return ret;
- }
-
/*
* Add to the queued buffers list, a buffer will stay on it until
* dequeued in dqbuf.
@@ -903,6 +1137,9 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
if (q->streaming)
__enqueue_in_driver(vb);
+ /* Fill buffer information for the userspace */
+ __fill_v4l2_buffer(vb, b);
+
dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
return 0;
}
@@ -1096,6 +1333,43 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
EXPORT_SYMBOL_GPL(vb2_dqbuf);
/**
+ * __vb2_queue_cancel() - cancel and stop (pause) streaming
+ *
+ * Removes all queued buffers from driver's queue and all buffers queued by
+ * userspace from videobuf's queue. Returns to state after reqbufs.
+ */
+static void __vb2_queue_cancel(struct vb2_queue *q)
+{
+ unsigned int i;
+
+ /*
+ * Tell driver to stop all transactions and release all queued
+ * buffers.
+ */
+ if (q->streaming)
+ call_qop(q, stop_streaming, q);
+ q->streaming = 0;
+
+ /*
+ * Remove all buffers from videobuf's list...
+ */
+ INIT_LIST_HEAD(&q->queued_list);
+ /*
+ * ...and done list; userspace will not receive any buffers it
+ * has not already dequeued before initiating cancel.
+ */
+ INIT_LIST_HEAD(&q->done_list);
+ atomic_set(&q->queued_count, 0);
+ wake_up_all(&q->done_wq);
+
+ /*
+ * Reinitialize all buffers for next use.
+ */
+ for (i = 0; i < q->num_buffers; ++i)
+ q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
+}
+
+/**
* vb2_streamon - start streaming
* @q: videobuf2 queue
* @type: type argument passed from userspace to vidioc_streamon handler
@@ -1103,7 +1377,7 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf);
* Should be called from vidioc_streamon handler of a driver.
* This function:
* 1) verifies current state
- * 2) starts streaming and passes any previously queued buffers to the driver
+ * 2) passes any previously queued buffers to the driver and starts streaming
*
* The return values from this function are intended to be directly returned
* from vidioc_streamon handler in the driver.
@@ -1129,75 +1403,29 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
}
/*
- * Cannot start streaming on an OUTPUT device if no buffers have
- * been queued yet.
+ * If any buffers were queued before streamon,
+ * we can now pass them to driver for processing.
*/
- if (V4L2_TYPE_IS_OUTPUT(q->type)) {
- if (list_empty(&q->queued_list)) {
- dprintk(1, "streamon: no output buffers queued\n");
- return -EINVAL;
- }
- }
+ list_for_each_entry(vb, &q->queued_list, queued_entry)
+ __enqueue_in_driver(vb);
/*
* Let driver notice that streaming state has been enabled.
*/
- ret = call_qop(q, start_streaming, q);
+ ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
if (ret) {
dprintk(1, "streamon: driver refused to start streaming\n");
+ __vb2_queue_cancel(q);
return ret;
}
q->streaming = 1;
- /*
- * If any buffers were queued before streamon,
- * we can now pass them to driver for processing.
- */
- list_for_each_entry(vb, &q->queued_list, queued_entry)
- __enqueue_in_driver(vb);
-
dprintk(3, "Streamon successful\n");
return 0;
}
EXPORT_SYMBOL_GPL(vb2_streamon);
-/**
- * __vb2_queue_cancel() - cancel and stop (pause) streaming
- *
- * Removes all queued buffers from driver's queue and all buffers queued by
- * userspace from videobuf's queue. Returns to state after reqbufs.
- */
-static void __vb2_queue_cancel(struct vb2_queue *q)
-{
- unsigned int i;
-
- /*
- * Tell driver to stop all transactions and release all queued
- * buffers.
- */
- if (q->streaming)
- call_qop(q, stop_streaming, q);
- q->streaming = 0;
-
- /*
- * Remove all buffers from videobuf's list...
- */
- INIT_LIST_HEAD(&q->queued_list);
- /*
- * ...and done list; userspace will not receive any buffers it
- * has not already dequeued before initiating cancel.
- */
- INIT_LIST_HEAD(&q->done_list);
- atomic_set(&q->queued_count, 0);
- wake_up_all(&q->done_wq);
-
- /*
- * Reinitialize all buffers for next use.
- */
- for (i = 0; i < q->num_buffers; ++i)
- q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
-}
/**
* vb2_streamoff - stop streaming
@@ -1336,14 +1564,42 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
if (ret)
return ret;
- vb_plane->mapped = 1;
- vb->num_planes_mapped++;
-
dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
return 0;
}
EXPORT_SYMBOL_GPL(vb2_mmap);
+#ifndef CONFIG_MMU
+unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ unsigned long off = pgoff << PAGE_SHIFT;
+ struct vb2_buffer *vb;
+ unsigned int buffer, plane;
+ int ret;
+
+ if (q->memory != V4L2_MEMORY_MMAP) {
+ dprintk(1, "Queue is not currently set up for mmap\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Find the plane corresponding to the offset passed by userspace.
+ */
+ ret = __find_plane_by_offset(q, off, &buffer, &plane);
+ if (ret)
+ return ret;
+
+ vb = q->bufs[buffer];
+
+ return (unsigned long)vb2_plane_vaddr(vb, plane);
+}
+EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
+#endif
+
static int __vb2_init_fileio(struct vb2_queue *q, int read);
static int __vb2_cleanup_fileio(struct vb2_queue *q);
@@ -1461,7 +1717,7 @@ void vb2_queue_release(struct vb2_queue *q)
{
__vb2_cleanup_fileio(q);
__vb2_queue_cancel(q);
- __vb2_queue_free(q);
+ __vb2_queue_free(q, q->num_buffers);
}
EXPORT_SYMBOL_GPL(vb2_queue_release);
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c
index a790a5f8c06f..f17ad98fcc5f 100644
--- a/drivers/media/video/videobuf2-dma-contig.c
+++ b/drivers/media/video/videobuf2-dma-contig.c
@@ -24,7 +24,7 @@ struct vb2_dc_conf {
struct vb2_dc_buf {
struct vb2_dc_conf *conf;
void *vaddr;
- dma_addr_t paddr;
+ dma_addr_t dma_addr;
unsigned long size;
struct vm_area_struct *vma;
atomic_t refcount;
@@ -42,7 +42,7 @@ static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
if (!buf)
return ERR_PTR(-ENOMEM);
- buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->paddr,
+ buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr,
GFP_KERNEL);
if (!buf->vaddr) {
dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
@@ -69,7 +69,7 @@ static void vb2_dma_contig_put(void *buf_priv)
if (atomic_dec_and_test(&buf->refcount)) {
dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr,
- buf->paddr);
+ buf->dma_addr);
kfree(buf);
}
}
@@ -78,7 +78,7 @@ static void *vb2_dma_contig_cookie(void *buf_priv)
{
struct vb2_dc_buf *buf = buf_priv;
- return &buf->paddr;
+ return &buf->dma_addr;
}
static void *vb2_dma_contig_vaddr(void *buf_priv)
@@ -106,7 +106,7 @@ static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma)
return -EINVAL;
}
- return vb2_mmap_pfn_range(vma, buf->paddr, buf->size,
+ return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size,
&vb2_common_vm_ops, &buf->handler);
}
@@ -115,14 +115,14 @@ static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
{
struct vb2_dc_buf *buf;
struct vm_area_struct *vma;
- dma_addr_t paddr = 0;
+ dma_addr_t dma_addr = 0;
int ret;
buf = kzalloc(sizeof *buf, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
- ret = vb2_get_contig_userptr(vaddr, size, &vma, &paddr);
+ ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr);
if (ret) {
printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
vaddr);
@@ -131,7 +131,7 @@ static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
}
buf->size = size;
- buf->paddr = paddr;
+ buf->dma_addr = dma_addr;
buf->vma = vma;
return buf;
diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c
index 10a20d9509d9..3bad8b105fea 100644
--- a/drivers/media/video/videobuf2-dma-sg.c
+++ b/drivers/media/video/videobuf2-dma-sg.c
@@ -48,12 +48,10 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size)
buf->sg_desc.size = size;
buf->sg_desc.num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
- buf->sg_desc.sglist = vmalloc(buf->sg_desc.num_pages *
+ buf->sg_desc.sglist = vzalloc(buf->sg_desc.num_pages *
sizeof(*buf->sg_desc.sglist));
if (!buf->sg_desc.sglist)
goto fail_sglist_alloc;
- memset(buf->sg_desc.sglist, 0, buf->sg_desc.num_pages *
- sizeof(*buf->sg_desc.sglist));
sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
@@ -77,12 +75,6 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size)
printk(KERN_DEBUG "%s: Allocated buffer of %d pages\n",
__func__, buf->sg_desc.num_pages);
-
- if (!buf->vaddr)
- buf->vaddr = vm_map_ram(buf->pages,
- buf->sg_desc.num_pages,
- -1,
- PAGE_KERNEL);
return buf;
fail_pages_alloc:
@@ -136,13 +128,11 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
buf->sg_desc.num_pages = last - first + 1;
- buf->sg_desc.sglist = vmalloc(
+ buf->sg_desc.sglist = vzalloc(
buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist));
if (!buf->sg_desc.sglist)
goto userptr_fail_sglist_alloc;
- memset(buf->sg_desc.sglist, 0,
- buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist));
sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c
index 5370a3a7ee25..71a7a78c3fc0 100644
--- a/drivers/media/video/videobuf2-memops.c
+++ b/drivers/media/video/videobuf2-memops.c
@@ -18,7 +18,6 @@
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/file.h>
-#include <linux/slab.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-memops.h>
@@ -69,12 +68,12 @@ void vb2_put_vma(struct vm_area_struct *vma)
if (!vma)
return;
- if (vma->vm_file)
- fput(vma->vm_file);
-
if (vma->vm_ops && vma->vm_ops->close)
vma->vm_ops->close(vma);
+ if (vma->vm_file)
+ fput(vma->vm_file);
+
kfree(vma);
}
EXPORT_SYMBOL_GPL(vb2_put_vma);
@@ -177,7 +176,7 @@ int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
vma->vm_ops->open(vma);
- printk(KERN_DEBUG "%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
+ pr_debug("%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
__func__, paddr, vma->vm_start, size);
return 0;
@@ -195,7 +194,7 @@ static void vb2_common_vm_open(struct vm_area_struct *vma)
{
struct vb2_vmarea_handler *h = vma->vm_private_data;
- printk(KERN_DEBUG "%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+ pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
__func__, h, atomic_read(h->refcount), vma->vm_start,
vma->vm_end);
@@ -213,7 +212,7 @@ static void vb2_common_vm_close(struct vm_area_struct *vma)
{
struct vb2_vmarea_handler *h = vma->vm_private_data;
- printk(KERN_DEBUG "%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+ pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
__func__, h, atomic_read(h->refcount), vma->vm_start,
vma->vm_end);
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
index d63e9d978493..52a0a3736c82 100644
--- a/drivers/media/video/vino.c
+++ b/drivers/media/video/vino.c
@@ -36,7 +36,6 @@
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/time.h>
-#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/i2c.h>
@@ -61,8 +60,7 @@
// #define VINO_DEBUG
// #define VINO_DEBUG_INT
-#define VINO_MODULE_VERSION "0.0.6"
-#define VINO_VERSION_CODE KERNEL_VERSION(0, 0, 6)
+#define VINO_MODULE_VERSION "0.0.7"
MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver");
MODULE_VERSION(VINO_MODULE_VERSION);
@@ -2934,7 +2932,6 @@ static int vino_querycap(struct file *file, void *__fh,
strcpy(cap->driver, vino_driver_name);
strcpy(cap->card, vino_driver_description);
strcpy(cap->bus_info, vino_bus_name);
- cap->version = VINO_VERSION_CODE;
cap->capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING;
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 2238a613d664..7d754fbcccbf 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -22,7 +22,6 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/font.h>
-#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/kthread.h>
@@ -32,6 +31,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-common.h>
#define VIVI_MODULE_NAME "vivi"
@@ -44,15 +44,12 @@
#define MAX_WIDTH 1920
#define MAX_HEIGHT 1200
-#define VIVI_MAJOR_VERSION 0
-#define VIVI_MINOR_VERSION 8
-#define VIVI_RELEASE 0
-#define VIVI_VERSION \
- KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
+#define VIVI_VERSION "0.8.1"
MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board");
MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(VIVI_VERSION);
static unsigned video_nr = -1;
module_param(video_nr, uint, 0644);
@@ -167,6 +164,11 @@ struct vivi_dev {
struct v4l2_ctrl *contrast;
struct v4l2_ctrl *saturation;
struct v4l2_ctrl *hue;
+ struct {
+ /* autogain/gain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
struct v4l2_ctrl *volume;
struct v4l2_ctrl *button;
struct v4l2_ctrl *boolean;
@@ -174,6 +176,7 @@ struct vivi_dev {
struct v4l2_ctrl *int64;
struct v4l2_ctrl *menu;
struct v4l2_ctrl *string;
+ struct v4l2_ctrl *bitmask;
spinlock_t slock;
struct mutex mutex;
@@ -457,6 +460,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
unsigned ms;
char str[100];
int h, line = 1;
+ s32 gain;
if (!vbuf)
return;
@@ -479,6 +483,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
dev->width, dev->height, dev->input);
gen_text(dev, vbuf, line++ * 16, 16, str);
+ gain = v4l2_ctrl_g_ctrl(dev->gain);
mutex_lock(&dev->ctrl_handler.lock);
snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
dev->brightness->cur.val,
@@ -486,11 +491,13 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
dev->saturation->cur.val,
dev->hue->cur.val);
gen_text(dev, vbuf, line++ * 16, 16, str);
- snprintf(str, sizeof(str), " volume %3d ", dev->volume->cur.val);
+ snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d ",
+ dev->autogain->cur.val, gain, dev->volume->cur.val);
gen_text(dev, vbuf, line++ * 16, 16, str);
- snprintf(str, sizeof(str), " int32 %d, int64 %lld ",
+ snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
dev->int32->cur.val,
- dev->int64->cur.val64);
+ dev->int64->cur.val64,
+ dev->bitmask->cur.val);
gen_text(dev, vbuf, line++ * 16, 16, str);
snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
dev->boolean->cur.val,
@@ -524,11 +531,13 @@ static void vivi_thread_tick(struct vivi_dev *dev)
spin_lock_irqsave(&dev->slock, flags);
if (list_empty(&dma_q->active)) {
dprintk(dev, 1, "No active queue to serve\n");
- goto unlock;
+ spin_unlock_irqrestore(&dev->slock, flags);
+ return;
}
buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
list_del(&buf->list);
+ spin_unlock_irqrestore(&dev->slock, flags);
do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
@@ -538,8 +547,6 @@ static void vivi_thread_tick(struct vivi_dev *dev)
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
-unlock:
- spin_unlock_irqrestore(&dev->slock, flags);
}
#define frames_to_ms(frames) \
@@ -643,9 +650,9 @@ static void vivi_stop_generating(struct vivi_dev *dev)
/* ------------------------------------------------------------------
Videobuf operations
------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
- unsigned int *nplanes, unsigned long sizes[],
- void *alloc_ctxs[])
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
struct vivi_dev *dev = vb2_get_drv_priv(vq);
unsigned long size;
@@ -759,7 +766,7 @@ static void buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&dev->slock, flags);
}
-static int start_streaming(struct vb2_queue *vq)
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vivi_dev *dev = vb2_get_drv_priv(vq);
dprintk(dev, 1, "%s\n", __func__);
@@ -812,7 +819,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strcpy(cap->driver, "vivi");
strcpy(cap->card, "vivi");
strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
- cap->version = VIVI_VERSION;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \
V4L2_CAP_READWRITE;
return 0;
@@ -846,6 +852,11 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
(f->fmt.pix.width * dev->fmt->depth) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
+ if (dev->fmt->fourcc == V4L2_PIX_FMT_YUYV ||
+ dev->fmt->fourcc == V4L2_PIX_FMT_UYVY)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
@@ -879,6 +890,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
(f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
+ if (fmt->fourcc == V4L2_PIX_FMT_YUYV ||
+ fmt->fourcc == V4L2_PIX_FMT_UYVY)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
@@ -942,6 +958,14 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
return vb2_streamoff(&dev->vb_vidq, i);
}
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ v4l2_ctrl_handler_log_status(&dev->ctrl_handler, dev->v4l2_dev.name);
+ return 0;
+}
+
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
{
return 0;
@@ -975,14 +999,37 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
if (i >= NUM_INPUTS)
return -EINVAL;
+ if (i == dev->input)
+ return 0;
+
dev->input = i;
precalculate_bars(dev);
precalculate_line(dev);
return 0;
}
+static int vidioc_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_CTRL:
+ return v4l2_event_subscribe(fh, sub, 0);
+ default:
+ return -EINVAL;
+ }
+}
+
/* --- controls ---------------------------------------------- */
+static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
+
+ if (ctrl == dev->autogain)
+ dev->gain->val = jiffies & 0xff;
+ return 0;
+}
+
static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
@@ -1010,10 +1057,17 @@ static unsigned int
vivi_poll(struct file *file, struct poll_table_struct *wait)
{
struct vivi_dev *dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
struct vb2_queue *q = &dev->vb_vidq;
+ unsigned int res;
dprintk(dev, 1, "%s\n", __func__);
- return vb2_poll(q, file, wait);
+ res = vb2_poll(q, file, wait);
+ if (v4l2_event_pending(fh))
+ res |= POLLPRI;
+ else
+ poll_wait(file, &fh->wait, wait);
+ return res;
}
static int vivi_close(struct file *file)
@@ -1045,6 +1099,7 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
}
static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
+ .g_volatile_ctrl = vivi_g_volatile_ctrl,
.s_ctrl = vivi_s_ctrl,
};
@@ -1117,9 +1172,20 @@ static const struct v4l2_ctrl_config vivi_ctrl_string = {
.step = 1,
};
+static const struct v4l2_ctrl_config vivi_ctrl_bitmask = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 6,
+ .name = "Bitmask",
+ .type = V4L2_CTRL_TYPE_BITMASK,
+ .def = 0x80002000,
+ .min = 0,
+ .max = 0x80402010,
+ .step = 0,
+};
+
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
- .open = v4l2_fh_open,
+ .open = v4l2_fh_open,
.release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
@@ -1143,6 +1209,9 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_s_input = vidioc_s_input,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_subscribe_event = vidioc_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static struct video_device vivi_template = {
@@ -1213,16 +1282,22 @@ static int __init vivi_create_instance(int inst)
V4L2_CID_SATURATION, 0, 255, 1, 127);
dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
+ dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_GAIN, 0, 255, 1, 100);
dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
+ dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
if (hdl->error) {
ret = hdl->error;
goto unreg_dev;
}
+ v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
dev->v4l2_dev.ctrl_handler = hdl;
/* initialize locks */
@@ -1325,9 +1400,8 @@ static int __init vivi_init(void)
}
printk(KERN_INFO "Video Technology Magazine Virtual Video "
- "Capture Board ver %u.%u.%u successfully loaded.\n",
- (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF,
- VIVI_VERSION & 0xFF);
+ "Capture Board ver %s successfully loaded.\n",
+ VIVI_VERSION);
/* n_devs will reflect the actual number of allocated devices */
n_devs = i;
diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c
index ca372eb911d0..e5cad6ff64a1 100644
--- a/drivers/media/video/vpx3220.c
+++ b/drivers/media/video/vpx3220.c
@@ -331,7 +331,7 @@ static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pst
if (pstd)
*pstd = std;
if (pstatus)
- *pstatus = status;
+ *pstatus = res;
return 0;
}
diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c
index fa35639d0c15..453dbbd1e6e8 100644
--- a/drivers/media/video/w9966.c
+++ b/drivers/media/video/w9966.c
@@ -57,7 +57,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/version.h>
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <media/v4l2-common.h>
@@ -127,7 +126,7 @@ struct w9966 {
MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>");
MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)");
MODULE_LICENSE("GPL");
-
+MODULE_VERSION("0.33.1");
#ifdef MODULE
static const char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
@@ -568,7 +567,6 @@ static int cam_querycap(struct file *file, void *priv,
strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card));
strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
- vcap->version = KERNEL_VERSION(0, 33, 0);
vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
return 0;
}
diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h
index f3f640014928..d7166afc255e 100644
--- a/drivers/media/video/zoran/zoran.h
+++ b/drivers/media/video/zoran/zoran.h
@@ -41,10 +41,6 @@ struct zoran_sync {
};
-#define MAJOR_VERSION 0 /* driver major version */
-#define MINOR_VERSION 10 /* driver minor version */
-#define RELEASE_VERSION 0 /* release version */
-
#define ZORAN_NAME "ZORAN" /* name of the device */
#define ZR_DEVNAME(zr) ((zr)->name)
diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c
index 79b04ac0f1ad..c3602d6cd48e 100644
--- a/drivers/media/video/zoran/zoran_card.c
+++ b/drivers/media/video/zoran/zoran_card.c
@@ -123,9 +123,12 @@ int zr36067_debug = 1;
module_param_named(debug, zr36067_debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-5)");
+#define ZORAN_VERSION "0.10.1"
+
MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
MODULE_AUTHOR("Serguei Miridonov");
MODULE_LICENSE("GPL");
+MODULE_VERSION(ZORAN_VERSION);
#define ZR_DEVICE(subven, subdev, data) { \
.vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \
@@ -1459,8 +1462,8 @@ static int __init zoran_init(void)
{
int res;
- printk(KERN_INFO "Zoran MJPEG board driver version %d.%d.%d\n",
- MAJOR_VERSION, MINOR_VERSION, RELEASE_VERSION);
+ printk(KERN_INFO "Zoran MJPEG board driver version %s\n",
+ ZORAN_VERSION);
/* check the parameters we have been given, adjust if necessary */
if (v4l_nbufs < 2)
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
index 2771d818406e..d4d05d2ace65 100644
--- a/drivers/media/video/zoran/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -44,7 +44,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
@@ -1538,8 +1537,6 @@ static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability
strncpy(cap->driver, "zoran", sizeof(cap->driver)-1);
snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
pci_name(zr->pci_dev));
- cap->version = KERNEL_VERSION(MAJOR_VERSION, MINOR_VERSION,
- RELEASE_VERSION);
cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY;
return 0;
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index 7dfb01e9930e..e78cf94f491e 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -29,7 +29,6 @@
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/vmalloc.h>
@@ -42,8 +41,7 @@
/* Version Information */
-#define DRIVER_VERSION "v0.73"
-#define ZR364XX_VERSION_CODE KERNEL_VERSION(0, 7, 3)
+#define DRIVER_VERSION "0.7.4"
#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/"
#define DRIVER_DESC "Zoran 364xx"
@@ -744,7 +742,6 @@ static int zr364xx_vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->card, cam->udev->product, sizeof(cap->card));
strlcpy(cap->bus_info, dev_name(&cam->udev->dev),
sizeof(cap->bus_info));
- cap->version = ZR364XX_VERSION_CODE;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING;
@@ -1641,6 +1638,9 @@ static int zr364xx_probe(struct usb_interface *intf,
if (!cam->read_endpoint) {
dev_err(&intf->dev, "Could not find bulk-in endpoint\n");
+ video_device_release(cam->vdev);
+ kfree(cam);
+ cam = NULL;
return -ENOMEM;
}
@@ -1721,3 +1721,4 @@ module_exit(zr364xx_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c
index 8c1d85e27be4..56ff19cdc2ad 100644
--- a/drivers/memstick/core/memstick.c
+++ b/drivers/memstick/core/memstick.c
@@ -17,6 +17,7 @@
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#define DRIVER_NAME "memstick"
diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
index 4a1909a32b60..9729b92fbfdd 100644
--- a/drivers/memstick/core/mspro_block.c
+++ b/drivers/memstick/core/mspro_block.c
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/memstick.h>
+#include <linux/module.h>
#define DRIVER_NAME "mspro_block"
diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c
index d89d925caecf..6ce70e9615d3 100644
--- a/drivers/memstick/host/jmb38x_ms.c
+++ b/drivers/memstick/host/jmb38x_ms.c
@@ -17,6 +17,7 @@
#include <linux/highmem.h>
#include <linux/memstick.h>
#include <linux/slab.h>
+#include <linux/module.h>
#define DRIVER_NAME "jmb38x_ms"
diff --git a/drivers/memstick/host/tifm_ms.c b/drivers/memstick/host/tifm_ms.c
index 03f71a431c82..b7aacf47703a 100644
--- a/drivers/memstick/host/tifm_ms.c
+++ b/drivers/memstick/host/tifm_ms.c
@@ -17,6 +17,7 @@
#include <linux/highmem.h>
#include <linux/scatterlist.h>
#include <linux/log2.h>
+#include <linux/module.h>
#include <asm/io.h>
#define DRIVER_NAME "tifm_ms"
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index 7956a10f9488..e9c6a6047a00 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -63,6 +63,8 @@
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
+#include <linux/kthread.h>
+#include <scsi/scsi_host.h>
#include "mptbase.h"
#include "lsi/mpi_log_fc.h"
@@ -323,6 +325,32 @@ mpt_is_discovery_complete(MPT_ADAPTER *ioc)
return rc;
}
+
+/**
+ * mpt_remove_dead_ioc_func - kthread context to remove dead ioc
+ * @arg: input argument, used to derive ioc
+ *
+ * Return 0 if controller is removed from pci subsystem.
+ * Return -1 for other case.
+ */
+static int mpt_remove_dead_ioc_func(void *arg)
+{
+ MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg;
+ struct pci_dev *pdev;
+
+ if ((ioc == NULL))
+ return -1;
+
+ pdev = ioc->pcidev;
+ if ((pdev == NULL))
+ return -1;
+
+ pci_remove_bus_device(pdev);
+ return 0;
+}
+
+
+
/**
* mpt_fault_reset_work - work performed on workq after ioc fault
* @work: input argument, used to derive ioc
@@ -336,12 +364,45 @@ mpt_fault_reset_work(struct work_struct *work)
u32 ioc_raw_state;
int rc;
unsigned long flags;
+ MPT_SCSI_HOST *hd;
+ struct task_struct *p;
if (ioc->ioc_reset_in_progress || !ioc->active)
goto out;
+
ioc_raw_state = mpt_GetIocState(ioc, 0);
- if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) {
+ if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_MASK) {
+ printk(MYIOC_s_INFO_FMT "%s: IOC is non-operational !!!!\n",
+ ioc->name, __func__);
+
+ /*
+ * Call mptscsih_flush_pending_cmds callback so that we
+ * flush all pending commands back to OS.
+ * This call is required to aovid deadlock at block layer.
+ * Dead IOC will fail to do diag reset,and this call is safe
+ * since dead ioc will never return any command back from HW.
+ */
+ hd = shost_priv(ioc->sh);
+ ioc->schedule_dead_ioc_flush_running_cmds(hd);
+
+ /*Remove the Dead Host */
+ p = kthread_run(mpt_remove_dead_ioc_func, ioc,
+ "mpt_dead_ioc_%d", ioc->id);
+ if (IS_ERR(p)) {
+ printk(MYIOC_s_ERR_FMT
+ "%s: Running mpt_dead_ioc thread failed !\n",
+ ioc->name, __func__);
+ } else {
+ printk(MYIOC_s_WARN_FMT
+ "%s: Running mpt_dead_ioc thread success !\n",
+ ioc->name, __func__);
+ }
+ return; /* don't rearm timer */
+ }
+
+ if ((ioc_raw_state & MPI_IOC_STATE_MASK)
+ == MPI_IOC_STATE_FAULT) {
printk(MYIOC_s_WARN_FMT "IOC is in FAULT state (%04xh)!!!\n",
ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK);
printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",
@@ -6413,8 +6474,19 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
pReq->Action, ioc->mptbase_cmds.status, timeleft));
if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
goto out;
- if (!timeleft)
+ if (!timeleft) {
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ if (ioc->ioc_reset_in_progress) {
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock,
+ flags);
+ printk(MYIOC_s_INFO_FMT "%s: host reset in"
+ " progress mpt_config timed out.!!\n",
+ __func__, ioc->name);
+ return -EFAULT;
+ }
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
issue_hard_reset = 1;
+ }
goto out;
}
@@ -7128,7 +7200,18 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
if (ioc->ioc_reset_in_progress) {
spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
- return 0;
+ ioc->wait_on_reset_completion = 1;
+ do {
+ ssleep(1);
+ } while (ioc->ioc_reset_in_progress == 1);
+ ioc->wait_on_reset_completion = 0;
+ return ioc->reset_status;
+ }
+ if (ioc->wait_on_reset_completion) {
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+ rc = 0;
+ time_count = jiffies;
+ goto exit;
}
ioc->ioc_reset_in_progress = 1;
if (ioc->alt_ioc)
@@ -7165,6 +7248,7 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
ioc->ioc_reset_in_progress = 0;
ioc->taskmgmt_quiesce_io = 0;
ioc->taskmgmt_in_progress = 0;
+ ioc->reset_status = rc;
if (ioc->alt_ioc) {
ioc->alt_ioc->ioc_reset_in_progress = 0;
ioc->alt_ioc->taskmgmt_quiesce_io = 0;
@@ -7180,7 +7264,7 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
ioc->alt_ioc, MPT_IOC_POST_RESET);
}
}
-
+exit:
dtmprintk(ioc,
printk(MYIOC_s_DEBUG_FMT
"HardResetHandler: completed (%d seconds): %s\n", ioc->name,
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index fe902338539b..b4d24dc081ae 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -76,8 +76,8 @@
#define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR
#endif
-#define MPT_LINUX_VERSION_COMMON "3.04.19"
-#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.19"
+#define MPT_LINUX_VERSION_COMMON "3.04.20"
+#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.20"
#define WHAT_MAGIC_STRING "@" "(" "#" ")"
#define show_mptmod_ver(s,ver) \
@@ -554,10 +554,47 @@ struct mptfc_rport_info
u8 flags;
};
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+/*
+ * MPT_SCSI_HOST defines - Used by the IOCTL and the SCSI drivers
+ * Private to the driver.
+ */
+
+#define MPT_HOST_BUS_UNKNOWN (0xFF)
+#define MPT_HOST_TOO_MANY_TM (0x05)
+#define MPT_HOST_NVRAM_INVALID (0xFFFFFFFF)
+#define MPT_HOST_NO_CHAIN (0xFFFFFFFF)
+#define MPT_NVRAM_MASK_TIMEOUT (0x000000FF)
+#define MPT_NVRAM_SYNC_MASK (0x0000FF00)
+#define MPT_NVRAM_SYNC_SHIFT (8)
+#define MPT_NVRAM_DISCONNECT_ENABLE (0x00010000)
+#define MPT_NVRAM_ID_SCAN_ENABLE (0x00020000)
+#define MPT_NVRAM_LUN_SCAN_ENABLE (0x00040000)
+#define MPT_NVRAM_TAG_QUEUE_ENABLE (0x00080000)
+#define MPT_NVRAM_WIDE_DISABLE (0x00100000)
+#define MPT_NVRAM_BOOT_CHOICE (0x00200000)
+
+typedef enum {
+ FC,
+ SPI,
+ SAS
+} BUS_TYPE;
+
+typedef struct _MPT_SCSI_HOST {
+ struct _MPT_ADAPTER *ioc;
+ ushort sel_timeout[MPT_MAX_FC_DEVICES];
+ char *info_kbuf;
+ long last_queue_full;
+ u16 spi_pending;
+ struct list_head target_reset_list;
+} MPT_SCSI_HOST;
+
typedef void (*MPT_ADD_SGE)(void *pAddr, u32 flagslength, dma_addr_t dma_addr);
typedef void (*MPT_ADD_CHAIN)(void *pAddr, u8 next, u16 length,
dma_addr_t dma_addr);
typedef void (*MPT_SCHEDULE_TARGET_RESET)(void *ioc);
+typedef void (*MPT_FLUSH_RUNNING_CMDS)(MPT_SCSI_HOST *hd);
/*
* Adapter Structure - pci_dev specific. Maximum: MPT_MAX_ADAPTERS
@@ -716,7 +753,10 @@ typedef struct _MPT_ADAPTER
int taskmgmt_in_progress;
u8 taskmgmt_quiesce_io;
u8 ioc_reset_in_progress;
+ u8 reset_status;
+ u8 wait_on_reset_completion;
MPT_SCHEDULE_TARGET_RESET schedule_target_reset;
+ MPT_FLUSH_RUNNING_CMDS schedule_dead_ioc_flush_running_cmds;
struct work_struct sas_persist_task;
struct work_struct fc_setup_reset_work;
@@ -830,19 +870,6 @@ typedef struct _MPT_LOCAL_REPLY {
u32 pad;
} MPT_LOCAL_REPLY;
-#define MPT_HOST_BUS_UNKNOWN (0xFF)
-#define MPT_HOST_TOO_MANY_TM (0x05)
-#define MPT_HOST_NVRAM_INVALID (0xFFFFFFFF)
-#define MPT_HOST_NO_CHAIN (0xFFFFFFFF)
-#define MPT_NVRAM_MASK_TIMEOUT (0x000000FF)
-#define MPT_NVRAM_SYNC_MASK (0x0000FF00)
-#define MPT_NVRAM_SYNC_SHIFT (8)
-#define MPT_NVRAM_DISCONNECT_ENABLE (0x00010000)
-#define MPT_NVRAM_ID_SCAN_ENABLE (0x00020000)
-#define MPT_NVRAM_LUN_SCAN_ENABLE (0x00040000)
-#define MPT_NVRAM_TAG_QUEUE_ENABLE (0x00080000)
-#define MPT_NVRAM_WIDE_DISABLE (0x00100000)
-#define MPT_NVRAM_BOOT_CHOICE (0x00200000)
/* The TM_STATE variable is used to provide strict single threading of TM
* requests as well as communicate TM error conditions.
@@ -851,21 +878,6 @@ typedef struct _MPT_LOCAL_REPLY {
#define TM_STATE_IN_PROGRESS (1)
#define TM_STATE_ERROR (2)
-typedef enum {
- FC,
- SPI,
- SAS
-} BUS_TYPE;
-
-typedef struct _MPT_SCSI_HOST {
- MPT_ADAPTER *ioc;
- ushort sel_timeout[MPT_MAX_FC_DEVICES];
- char *info_kbuf;
- long last_queue_full;
- u16 spi_pending;
- struct list_head target_reset_list;
-} MPT_SCSI_HOST;
-
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* More Dynamic Multi-Pathing stuff...
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 7596aecd5072..9d9504298549 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -92,6 +92,11 @@ static int max_lun = MPTSAS_MAX_LUN;
module_param(max_lun, int, 0);
MODULE_PARM_DESC(max_lun, " max lun, default=16895 ");
+static int mpt_loadtime_max_sectors = 8192;
+module_param(mpt_loadtime_max_sectors, int, 0);
+MODULE_PARM_DESC(mpt_loadtime_max_sectors,
+ " Maximum sector define for Host Bus Adaptor.Range 64 to 8192 default=8192");
+
static u8 mptsasDoneCtx = MPT_MAX_PROTOCOL_DRIVERS;
static u8 mptsasTaskCtx = MPT_MAX_PROTOCOL_DRIVERS;
static u8 mptsasInternalCtx = MPT_MAX_PROTOCOL_DRIVERS; /* Used only for internal commands */
@@ -285,10 +290,11 @@ mptsas_add_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
spin_lock_irqsave(&ioc->fw_event_lock, flags);
list_add_tail(&fw_event->list, &ioc->fw_event_list);
INIT_DELAYED_WORK(&fw_event->work, mptsas_firmware_event_work);
- devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: add (fw_event=0x%p)\n",
- ioc->name, __func__, fw_event));
- queue_delayed_work(ioc->fw_event_q, &fw_event->work,
- delay);
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: add (fw_event=0x%p)"
+ "on cpuid %d\n", ioc->name, __func__,
+ fw_event, smp_processor_id()));
+ queue_delayed_work_on(smp_processor_id(), ioc->fw_event_q,
+ &fw_event->work, delay);
spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
}
@@ -300,10 +306,11 @@ mptsas_requeue_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
unsigned long flags;
spin_lock_irqsave(&ioc->fw_event_lock, flags);
devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: reschedule task "
- "(fw_event=0x%p)\n", ioc->name, __func__, fw_event));
+ "(fw_event=0x%p)on cpuid %d\n", ioc->name, __func__,
+ fw_event, smp_processor_id()));
fw_event->retries++;
- queue_delayed_work(ioc->fw_event_q, &fw_event->work,
- msecs_to_jiffies(delay));
+ queue_delayed_work_on(smp_processor_id(), ioc->fw_event_q,
+ &fw_event->work, msecs_to_jiffies(delay));
spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
}
@@ -1943,6 +1950,15 @@ static enum blk_eh_timer_return mptsas_eh_timed_out(struct scsi_cmnd *sc)
goto done;
}
+ /* In case if IOC is in reset from internal context.
+ * Do not execute EEH for the same IOC. SML should to reset timer.
+ */
+ if (ioc->ioc_reset_in_progress) {
+ dtmprintk(ioc, printk(MYIOC_s_WARN_FMT ": %s: ioc is in reset,"
+ "SML need to reset the timer (sc=%p)\n",
+ ioc->name, __func__, sc));
+ rc = BLK_EH_RESET_TIMER;
+ }
vdevice = sc->device->hostdata;
if (vdevice && vdevice->vtarget && (vdevice->vtarget->inDMD
|| vdevice->vtarget->deleted)) {
@@ -5142,6 +5158,8 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->TaskCtx = mptsasTaskCtx;
ioc->InternalCtx = mptsasInternalCtx;
ioc->schedule_target_reset = &mptsas_schedule_target_reset;
+ ioc->schedule_dead_ioc_flush_running_cmds =
+ &mptscsih_flush_running_cmds;
/* Added sanity check on readiness of the MPT adapter.
*/
if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) {
@@ -5239,6 +5257,21 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sh->sg_tablesize = numSGE;
}
+ if (mpt_loadtime_max_sectors) {
+ if (mpt_loadtime_max_sectors < 64 ||
+ mpt_loadtime_max_sectors > 8192) {
+ printk(MYIOC_s_INFO_FMT "Invalid value passed for"
+ "mpt_loadtime_max_sectors %d."
+ "Range from 64 to 8192\n", ioc->name,
+ mpt_loadtime_max_sectors);
+ }
+ mpt_loadtime_max_sectors &= 0xFFFFFFFE;
+ dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "Resetting max sector to %d from %d\n",
+ ioc->name, mpt_loadtime_max_sectors, sh->max_sectors));
+ sh->max_sectors = mpt_loadtime_max_sectors;
+ }
+
hd = shost_priv(sh);
hd->ioc = ioc;
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index a1d4ee6671be..0c3ced70707b 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -827,10 +827,11 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
* DID_SOFT_ERROR is set.
*/
if (ioc->bus_type == SPI) {
- if (pScsiReq->CDB[0] == READ_6 ||
+ if ((pScsiReq->CDB[0] == READ_6 && ((pScsiReq->CDB[1] & 0x02) == 0)) ||
pScsiReq->CDB[0] == READ_10 ||
pScsiReq->CDB[0] == READ_12 ||
- pScsiReq->CDB[0] == READ_16 ||
+ (pScsiReq->CDB[0] == READ_16 &&
+ ((pScsiReq->CDB[1] & 0x02) == 0)) ||
pScsiReq->CDB[0] == VERIFY ||
pScsiReq->CDB[0] == VERIFY_16) {
if (scsi_bufflen(sc) !=
@@ -1024,7 +1025,7 @@ out:
*
* Must be called while new I/Os are being queued.
*/
-static void
+void
mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
{
MPT_ADAPTER *ioc = hd->ioc;
@@ -1055,6 +1056,7 @@ mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
sc->scsi_done(sc);
}
}
+EXPORT_SYMBOL(mptscsih_flush_running_cmds);
/*
* mptscsih_search_running_cmds - Delete any commands associated
@@ -1629,7 +1631,13 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun,
return 0;
}
- if (ioc_raw_state & MPI_DOORBELL_ACTIVE) {
+ /* DOORBELL ACTIVE check is not required if
+ * MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q is supported.
+ */
+
+ if (!((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q)
+ && (ioc->facts.MsgVersion >= MPI_VERSION_01_05)) &&
+ (ioc_raw_state & MPI_DOORBELL_ACTIVE)) {
printk(MYIOC_s_WARN_FMT
"TaskMgmt type=%x: ioc_state: "
"DOORBELL_ACTIVE (0x%x)!\n",
@@ -1728,7 +1736,9 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun,
printk(MYIOC_s_WARN_FMT
"Issuing Reset from %s!! doorbell=0x%08x\n",
ioc->name, __func__, mpt_GetIocState(ioc, 0));
- retval = mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
+ retval = (ioc->bus_type == SAS) ?
+ mpt_HardResetHandler(ioc, CAN_SLEEP) :
+ mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
mpt_free_msg_frame(ioc, mf);
}
diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h
index 45a5ff3eff61..43e75ff39921 100644
--- a/drivers/message/fusion/mptscsih.h
+++ b/drivers/message/fusion/mptscsih.h
@@ -135,3 +135,4 @@ extern int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id);
extern struct device_attribute *mptscsih_host_attrs[];
extern struct scsi_cmnd *mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i);
extern void mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code);
+extern void mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd);
diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c
index 74fbe56321ff..c8ed7b63fdf5 100644
--- a/drivers/message/i2o/i2o_scsi.c
+++ b/drivers/message/i2o/i2o_scsi.c
@@ -59,7 +59,7 @@
#include <asm/dma.h>
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c
index 090d2a3a6548..a8c08f332da0 100644
--- a/drivers/message/i2o/iop.c
+++ b/drivers/message/i2o/iop.c
@@ -681,11 +681,11 @@ static int i2o_iop_systab_set(struct i2o_controller *c)
if (root && allocate_resource(root, res, sb->desired_mem_size, sb->desired_mem_size, sb->desired_mem_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */
NULL, NULL) >= 0) {
c->mem_alloc = 1;
- sb->current_mem_size = 1 + res->end - res->start;
+ sb->current_mem_size = resource_size(res);
sb->current_mem_base = res->start;
osm_info("%s: allocated %llu bytes of PCI memory at "
"0x%016llX.\n", c->name,
- (unsigned long long)(1 + res->end - res->start),
+ (unsigned long long)resource_size(res),
(unsigned long long)res->start);
}
}
@@ -703,11 +703,11 @@ static int i2o_iop_systab_set(struct i2o_controller *c)
if (root && allocate_resource(root, res, sb->desired_io_size, sb->desired_io_size, sb->desired_io_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */
NULL, NULL) >= 0) {
c->io_alloc = 1;
- sb->current_io_size = 1 + res->end - res->start;
+ sb->current_io_size = resource_size(res);
sb->current_mem_base = res->start;
osm_info("%s: allocated %llu bytes of PCI I/O at "
"0x%016llX.\n", c->name,
- (unsigned long long)(1 + res->end - res->start),
+ (unsigned long long)resource_size(res),
(unsigned long long)res->start);
}
}
diff --git a/drivers/message/i2o/pci.c b/drivers/message/i2o/pci.c
index 73e4658af53c..7190d5239b4f 100644
--- a/drivers/message/i2o/pci.c
+++ b/drivers/message/i2o/pci.c
@@ -31,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/i2o.h>
+#include <linux/module.h>
#include "core.h"
#define OSM_DESCRIPTION "I2O-subsystem"
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6ca938a6bf94..f1391c21ef26 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2,23 +2,8 @@
# Multifunction miscellaneous devices
#
-menuconfig MFD_SUPPORT
- bool "Multifunction device drivers"
- depends on HAS_IOMEM
- default y
- help
- Multifunction devices embed several functions (e.g. GPIOs,
- touchscreens, keyboards, current regulators, power management chips,
- etc...) in one single integrated circuit. They usually talk to the
- main CPU through one or more IRQ lines and low speed data busses (SPI,
- I2C, etc..). They appear as one single device to the main system
- through the data bus and the MFD framework allows for sub devices
- (a.k.a. functions) to appear as discrete platform devices.
- MFDs are typically found on embedded platforms.
-
- This option alone does not add any kernel code.
-
-if MFD_SUPPORT
+if HAS_IOMEM
+menu "Multifunction device drivers"
config MFD_CORE
tristate
@@ -171,6 +156,37 @@ config MFD_TPS6586X
This driver can also be built as a module. If so, the module
will be called tps6586x.
+config MFD_TPS65910
+ bool "TPS65910 Power Management chip"
+ depends on I2C=y && GPIOLIB
+ select MFD_CORE
+ select GPIO_TPS65910
+ help
+ if you say yes here you get support for the TPS65910 series of
+ Power Management chips.
+
+config MFD_TPS65912
+ bool
+ depends on GPIOLIB
+
+config MFD_TPS65912_I2C
+ bool "TPS95612 Power Management chip with I2C"
+ select MFD_CORE
+ select MFD_TPS65912
+ depends on I2C=y && GPIOLIB
+ help
+ If you say yes here you get support for the TPS65912 series of
+ PM chips with I2C interface.
+
+config MFD_TPS65912_SPI
+ bool "TPS65912 Power Management chip with SPI"
+ select MFD_CORE
+ select MFD_TPS65912
+ depends on SPI_MASTER && GPIOLIB
+ help
+ If you say yes here you get support for the TPS65912 series of
+ PM chips with SPI interface.
+
config MENELAUS
bool "Texas Instruments TWL92330/Menelaus PM chip"
depends on I2C=y && ARCH_OMAP2
@@ -218,7 +234,7 @@ config TWL4030_POWER
and load scripts controlling which resources are switched off/on
or reset when a sleep, wakeup or warm reset event occurs.
-config TWL4030_CODEC
+config MFD_TWL4030_AUDIO
bool
depends on TWL4030_CORE
select MFD_CORE
@@ -233,6 +249,12 @@ config TWL6030_PWM
Say yes here if you want support for TWL6030 PWM.
This is used to control charging LED brightness.
+config TWL6040_CORE
+ bool
+ depends on TWL4030_CORE && GENERIC_HARDIRQS
+ select MFD_CORE
+ default n
+
config MFD_STMPE
bool "Support STMicroelectronics STMPE"
depends on I2C=y && GENERIC_HARDIRQS
@@ -353,6 +375,7 @@ config MFD_WM8400
tristate "Support Wolfson Microelectronics WM8400"
select MFD_CORE
depends on I2C
+ select REGMAP_I2C
help
Support for the Wolfson Microelecronics WM8400 PMIC and audio
CODEC. This driver provides common support for accessing
@@ -367,6 +390,7 @@ config MFD_WM831X_I2C
bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C"
select MFD_CORE
select MFD_WM831X
+ select REGMAP_I2C
depends on I2C=y && GENERIC_HARDIRQS
help
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
@@ -378,6 +402,7 @@ config MFD_WM831X_SPI
bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI"
select MFD_CORE
select MFD_WM831X
+ select REGMAP_SPI
depends on SPI_MASTER && GENERIC_HARDIRQS
help
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
@@ -451,6 +476,7 @@ config MFD_WM8350_I2C
config MFD_WM8994
bool "Support Wolfson Microelectronics WM8994"
select MFD_CORE
+ select REGMAP_I2C
depends on I2C=y && GENERIC_HARDIRQS
help
The WM8994 is a highly integrated hi-fi CODEC designed for
@@ -463,6 +489,7 @@ config MFD_WM8994
config MFD_PCF50633
tristate "Support for NXP PCF50633"
depends on I2C
+ select REGMAP_I2C
help
Say yes here if you have NXP PCF50633 chip on your board.
This core driver provides register access and IRQ handling
@@ -539,6 +566,23 @@ config EZX_PCAP
This enables the PCAP ASIC present on EZX Phones. This is
needed for MMC, TouchScreen, Sound, USB, etc..
+config AB5500_CORE
+ bool "ST-Ericsson AB5500 Mixed Signal Power Management chip"
+ depends on ABX500_CORE && MFD_DB5500_PRCMU
+ select MFD_CORE
+ help
+ Select this option to enable access to AB5500 power management
+ chip. This connects to the db5500 chip via the I2C bus via PRCMU.
+ This chip embeds various other multimedia funtionalities as well.
+
+config AB5500_DEBUG
+ bool "Enable debug info via debugfs"
+ depends on AB5500_CORE && DEBUG_FS
+ default y if DEBUG_FS
+ help
+ Select this option if you want debug information from the AB5500
+ using the debug filesystem, debugfs.
+
config AB8500_CORE
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
depends on GENERIC_HARDIRQS && ABX500_CORE
@@ -575,20 +619,6 @@ config AB8500_GPADC
help
AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage
-config AB3550_CORE
- bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions"
- select MFD_CORE
- depends on I2C=y && GENERIC_HARDIRQS && ABX500_CORE
- help
- Select this to enable the AB3550 Mixed Signal IC core
- functionality. This connects to a AB3550 on the I2C bus
- and expose a number of symbols needed for dependent devices
- to read and write registers and subscribe to events from
- this multi-functional IC. This is needed to use other features
- of the AB3550 such as battery-backed RTC, charging control,
- LEDs, vibrator, system power and temperature, power management
- and ALSA sound.
-
config MFD_DB8500_PRCMU
bool "ST-Ericsson DB8500 Power Reset Control Management Unit"
depends on UX500_SOC_DB8500
@@ -656,8 +686,9 @@ config MFD_JANZ_CMODIO
CAN and GPIO controllers.
config MFD_JZ4740_ADC
- tristate "Support for the JZ4740 SoC ADC core"
+ bool "Support for the JZ4740 SoC ADC core"
select MFD_CORE
+ select GENERIC_IRQ_CHIP
depends on MACH_JZ4740
help
Say yes here if you want support for the ADC unit in the JZ4740 SoC.
@@ -719,19 +750,30 @@ config MFD_PM8XXX_IRQ
This is required to use certain other PM 8xxx features, such as GPIO
and MPP.
-config MFD_TPS65910
- bool "TPS65910 Power Management chip"
- depends on I2C=y && GPIOLIB
+config TPS65911_COMPARATOR
+ tristate
+
+config MFD_AAT2870_CORE
+ bool "Support for the AnalogicTech AAT2870"
select MFD_CORE
- select GPIO_TPS65910
+ depends on I2C=y && GPIOLIB
help
- if you say yes here you get support for the TPS65910 series of
- Power Management chips.
+ If you say yes here you get support for the AAT2870.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
-config TPS65911_COMPARATOR
- tristate
+config MFD_INTEL_MSIC
+ bool "Support for Intel MSIC"
+ depends on INTEL_SCU_IPC
+ select MFD_CORE
+ help
+ Select this option to enable access to Intel MSIC (Avatele
+ Passage) chip. This chip embeds audio, battery, GPIO, etc.
+ devices used in Intel Medfield platforms.
-endif # MFD_SUPPORT
+endmenu
+endif
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d7d47d2a4c76..b2292eb75242 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
+wm831x-objs += wm831x-auxadc.o
obj-$(CONFIG_MFD_WM831X) += wm831x.o
obj-$(CONFIG_MFD_WM831X_I2C) += wm831x-i2c.o
obj-$(CONFIG_MFD_WM831X_SPI) += wm831x-spi.o
@@ -35,13 +36,19 @@ obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o
obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
+obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
+tps65912-objs := tps65912-core.o tps65912-irq.o
+obj-$(CONFIG_MFD_TPS65912) += tps65912.o
+obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
+obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
-obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
+obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
+obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
@@ -72,7 +79,8 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
obj-$(CONFIG_ABX500_CORE) += abx500-core.o
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
-obj-$(CONFIG_AB3550_CORE) += ab3550-core.o
+obj-$(CONFIG_AB5500_CORE) += ab5500-core.o
+obj-$(CONFIG_AB5500_DEBUG) += ab5500-debugfs.o
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
@@ -93,5 +101,6 @@ obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
-obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
+obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
+obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c
new file mode 100644
index 000000000000..02c42015ba51
--- /dev/null
+++ b/drivers/mfd/aat2870-core.c
@@ -0,0 +1,535 @@
+/*
+ * linux/drivers/mfd/aat2870-core.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/aat2870.h>
+#include <linux/regulator/machine.h>
+
+static struct aat2870_register aat2870_regs[AAT2870_REG_NUM] = {
+ /* readable, writeable, value */
+ { 0, 1, 0x00 }, /* 0x00 AAT2870_BL_CH_EN */
+ { 0, 1, 0x16 }, /* 0x01 AAT2870_BLM */
+ { 0, 1, 0x16 }, /* 0x02 AAT2870_BLS */
+ { 0, 1, 0x56 }, /* 0x03 AAT2870_BL1 */
+ { 0, 1, 0x56 }, /* 0x04 AAT2870_BL2 */
+ { 0, 1, 0x56 }, /* 0x05 AAT2870_BL3 */
+ { 0, 1, 0x56 }, /* 0x06 AAT2870_BL4 */
+ { 0, 1, 0x56 }, /* 0x07 AAT2870_BL5 */
+ { 0, 1, 0x56 }, /* 0x08 AAT2870_BL6 */
+ { 0, 1, 0x56 }, /* 0x09 AAT2870_BL7 */
+ { 0, 1, 0x56 }, /* 0x0A AAT2870_BL8 */
+ { 0, 1, 0x00 }, /* 0x0B AAT2870_FLR */
+ { 0, 1, 0x03 }, /* 0x0C AAT2870_FM */
+ { 0, 1, 0x03 }, /* 0x0D AAT2870_FS */
+ { 0, 1, 0x10 }, /* 0x0E AAT2870_ALS_CFG0 */
+ { 0, 1, 0x06 }, /* 0x0F AAT2870_ALS_CFG1 */
+ { 0, 1, 0x00 }, /* 0x10 AAT2870_ALS_CFG2 */
+ { 1, 0, 0x00 }, /* 0x11 AAT2870_AMB */
+ { 0, 1, 0x00 }, /* 0x12 AAT2870_ALS0 */
+ { 0, 1, 0x00 }, /* 0x13 AAT2870_ALS1 */
+ { 0, 1, 0x00 }, /* 0x14 AAT2870_ALS2 */
+ { 0, 1, 0x00 }, /* 0x15 AAT2870_ALS3 */
+ { 0, 1, 0x00 }, /* 0x16 AAT2870_ALS4 */
+ { 0, 1, 0x00 }, /* 0x17 AAT2870_ALS5 */
+ { 0, 1, 0x00 }, /* 0x18 AAT2870_ALS6 */
+ { 0, 1, 0x00 }, /* 0x19 AAT2870_ALS7 */
+ { 0, 1, 0x00 }, /* 0x1A AAT2870_ALS8 */
+ { 0, 1, 0x00 }, /* 0x1B AAT2870_ALS9 */
+ { 0, 1, 0x00 }, /* 0x1C AAT2870_ALSA */
+ { 0, 1, 0x00 }, /* 0x1D AAT2870_ALSB */
+ { 0, 1, 0x00 }, /* 0x1E AAT2870_ALSC */
+ { 0, 1, 0x00 }, /* 0x1F AAT2870_ALSD */
+ { 0, 1, 0x00 }, /* 0x20 AAT2870_ALSE */
+ { 0, 1, 0x00 }, /* 0x21 AAT2870_ALSF */
+ { 0, 1, 0x00 }, /* 0x22 AAT2870_SUB_SET */
+ { 0, 1, 0x00 }, /* 0x23 AAT2870_SUB_CTRL */
+ { 0, 1, 0x00 }, /* 0x24 AAT2870_LDO_AB */
+ { 0, 1, 0x00 }, /* 0x25 AAT2870_LDO_CD */
+ { 0, 1, 0x00 }, /* 0x26 AAT2870_LDO_EN */
+};
+
+static struct mfd_cell aat2870_devs[] = {
+ {
+ .name = "aat2870-backlight",
+ .id = AAT2870_ID_BL,
+ .pdata_size = sizeof(struct aat2870_bl_platform_data),
+ },
+ {
+ .name = "aat2870-regulator",
+ .id = AAT2870_ID_LDOA,
+ .pdata_size = sizeof(struct regulator_init_data),
+ },
+ {
+ .name = "aat2870-regulator",
+ .id = AAT2870_ID_LDOB,
+ .pdata_size = sizeof(struct regulator_init_data),
+ },
+ {
+ .name = "aat2870-regulator",
+ .id = AAT2870_ID_LDOC,
+ .pdata_size = sizeof(struct regulator_init_data),
+ },
+ {
+ .name = "aat2870-regulator",
+ .id = AAT2870_ID_LDOD,
+ .pdata_size = sizeof(struct regulator_init_data),
+ },
+};
+
+static int __aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
+{
+ int ret;
+
+ if (addr >= AAT2870_REG_NUM) {
+ dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);
+ return -EINVAL;
+ }
+
+ if (!aat2870->reg_cache[addr].readable) {
+ *val = aat2870->reg_cache[addr].value;
+ goto out;
+ }
+
+ ret = i2c_master_send(aat2870->client, &addr, 1);
+ if (ret < 0)
+ return ret;
+ if (ret != 1)
+ return -EIO;
+
+ ret = i2c_master_recv(aat2870->client, val, 1);
+ if (ret < 0)
+ return ret;
+ if (ret != 1)
+ return -EIO;
+
+out:
+ dev_dbg(aat2870->dev, "read: addr=0x%02x, val=0x%02x\n", addr, *val);
+ return 0;
+}
+
+static int __aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
+{
+ u8 msg[2];
+ int ret;
+
+ if (addr >= AAT2870_REG_NUM) {
+ dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);
+ return -EINVAL;
+ }
+
+ if (!aat2870->reg_cache[addr].writeable) {
+ dev_err(aat2870->dev, "Address 0x%02x is not writeable\n",
+ addr);
+ return -EINVAL;
+ }
+
+ msg[0] = addr;
+ msg[1] = val;
+ ret = i2c_master_send(aat2870->client, msg, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ aat2870->reg_cache[addr].value = val;
+
+ dev_dbg(aat2870->dev, "write: addr=0x%02x, val=0x%02x\n", addr, val);
+ return 0;
+}
+
+static int aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
+{
+ int ret;
+
+ mutex_lock(&aat2870->io_lock);
+ ret = __aat2870_read(aat2870, addr, val);
+ mutex_unlock(&aat2870->io_lock);
+
+ return ret;
+}
+
+static int aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
+{
+ int ret;
+
+ mutex_lock(&aat2870->io_lock);
+ ret = __aat2870_write(aat2870, addr, val);
+ mutex_unlock(&aat2870->io_lock);
+
+ return ret;
+}
+
+static int aat2870_update(struct aat2870_data *aat2870, u8 addr, u8 mask,
+ u8 val)
+{
+ int change;
+ u8 old_val, new_val;
+ int ret;
+
+ mutex_lock(&aat2870->io_lock);
+
+ ret = __aat2870_read(aat2870, addr, &old_val);
+ if (ret)
+ goto out_unlock;
+
+ new_val = (old_val & ~mask) | (val & mask);
+ change = old_val != new_val;
+ if (change)
+ ret = __aat2870_write(aat2870, addr, new_val);
+
+out_unlock:
+ mutex_unlock(&aat2870->io_lock);
+
+ return ret;
+}
+
+static inline void aat2870_enable(struct aat2870_data *aat2870)
+{
+ if (aat2870->en_pin >= 0)
+ gpio_set_value(aat2870->en_pin, 1);
+
+ aat2870->is_enable = 1;
+}
+
+static inline void aat2870_disable(struct aat2870_data *aat2870)
+{
+ if (aat2870->en_pin >= 0)
+ gpio_set_value(aat2870->en_pin, 0);
+
+ aat2870->is_enable = 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf)
+{
+ u8 addr, val;
+ ssize_t count = 0;
+ int ret;
+
+ count += sprintf(buf, "aat2870 registers\n");
+ for (addr = 0; addr < AAT2870_REG_NUM; addr++) {
+ count += sprintf(buf + count, "0x%02x: ", addr);
+ if (count >= PAGE_SIZE - 1)
+ break;
+
+ ret = aat2870->read(aat2870, addr, &val);
+ if (ret == 0)
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "0x%02x", val);
+ else
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "<read fail: %d>", ret);
+
+ if (count >= PAGE_SIZE - 1)
+ break;
+
+ count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+ if (count >= PAGE_SIZE - 1)
+ break;
+ }
+
+ /* Truncate count; min() would cause a warning */
+ if (count >= PAGE_SIZE)
+ count = PAGE_SIZE - 1;
+
+ return count;
+}
+
+static int aat2870_reg_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static ssize_t aat2870_reg_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct aat2870_data *aat2870 = file->private_data;
+ char *buf;
+ ssize_t ret;
+
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = aat2870_dump_reg(aat2870, buf);
+ if (ret >= 0)
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t aat2870_reg_write_file(struct file *file,
+ const char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct aat2870_data *aat2870 = file->private_data;
+ char buf[32];
+ ssize_t buf_size;
+ char *start = buf;
+ unsigned long addr, val;
+ int ret;
+
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size)) {
+ dev_err(aat2870->dev, "Failed to copy from user\n");
+ return -EFAULT;
+ }
+ buf[buf_size] = 0;
+
+ while (*start == ' ')
+ start++;
+
+ addr = simple_strtoul(start, &start, 16);
+ if (addr >= AAT2870_REG_NUM) {
+ dev_err(aat2870->dev, "Invalid address, 0x%lx\n", addr);
+ return -EINVAL;
+ }
+
+ while (*start == ' ')
+ start++;
+
+ if (strict_strtoul(start, 16, &val))
+ return -EINVAL;
+
+ ret = aat2870->write(aat2870, (u8)addr, (u8)val);
+ if (ret)
+ return ret;
+
+ return buf_size;
+}
+
+static const struct file_operations aat2870_reg_fops = {
+ .open = aat2870_reg_open_file,
+ .read = aat2870_reg_read_file,
+ .write = aat2870_reg_write_file,
+};
+
+static void aat2870_init_debugfs(struct aat2870_data *aat2870)
+{
+ aat2870->dentry_root = debugfs_create_dir("aat2870", NULL);
+ if (!aat2870->dentry_root) {
+ dev_warn(aat2870->dev,
+ "Failed to create debugfs root directory\n");
+ return;
+ }
+
+ aat2870->dentry_reg = debugfs_create_file("regs", 0644,
+ aat2870->dentry_root,
+ aat2870, &aat2870_reg_fops);
+ if (!aat2870->dentry_reg)
+ dev_warn(aat2870->dev,
+ "Failed to create debugfs register file\n");
+}
+
+static void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
+{
+ debugfs_remove_recursive(aat2870->dentry_root);
+}
+#else
+static inline void aat2870_init_debugfs(struct aat2870_data *aat2870)
+{
+}
+
+static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int aat2870_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct aat2870_platform_data *pdata = client->dev.platform_data;
+ struct aat2870_data *aat2870;
+ int i, j;
+ int ret = 0;
+
+ aat2870 = kzalloc(sizeof(struct aat2870_data), GFP_KERNEL);
+ if (!aat2870) {
+ dev_err(&client->dev,
+ "Failed to allocate memory for aat2870\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ aat2870->dev = &client->dev;
+ dev_set_drvdata(aat2870->dev, aat2870);
+
+ aat2870->client = client;
+ i2c_set_clientdata(client, aat2870);
+
+ aat2870->reg_cache = aat2870_regs;
+
+ if (pdata->en_pin < 0)
+ aat2870->en_pin = -1;
+ else
+ aat2870->en_pin = pdata->en_pin;
+
+ aat2870->init = pdata->init;
+ aat2870->uninit = pdata->uninit;
+ aat2870->read = aat2870_read;
+ aat2870->write = aat2870_write;
+ aat2870->update = aat2870_update;
+
+ mutex_init(&aat2870->io_lock);
+
+ if (aat2870->init)
+ aat2870->init(aat2870);
+
+ if (aat2870->en_pin >= 0) {
+ ret = gpio_request(aat2870->en_pin, "aat2870-en");
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to request GPIO %d\n", aat2870->en_pin);
+ goto out_kfree;
+ }
+ gpio_direction_output(aat2870->en_pin, 1);
+ }
+
+ aat2870_enable(aat2870);
+
+ for (i = 0; i < pdata->num_subdevs; i++) {
+ for (j = 0; j < ARRAY_SIZE(aat2870_devs); j++) {
+ if ((pdata->subdevs[i].id == aat2870_devs[j].id) &&
+ !strcmp(pdata->subdevs[i].name,
+ aat2870_devs[j].name)) {
+ aat2870_devs[j].platform_data =
+ pdata->subdevs[i].platform_data;
+ break;
+ }
+ }
+ }
+
+ ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs,
+ ARRAY_SIZE(aat2870_devs), NULL, 0);
+ if (ret != 0) {
+ dev_err(aat2870->dev, "Failed to add subdev: %d\n", ret);
+ goto out_disable;
+ }
+
+ aat2870_init_debugfs(aat2870);
+
+ return 0;
+
+out_disable:
+ aat2870_disable(aat2870);
+ if (aat2870->en_pin >= 0)
+ gpio_free(aat2870->en_pin);
+out_kfree:
+ kfree(aat2870);
+out:
+ return ret;
+}
+
+static int aat2870_i2c_remove(struct i2c_client *client)
+{
+ struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+
+ aat2870_uninit_debugfs(aat2870);
+
+ mfd_remove_devices(aat2870->dev);
+ aat2870_disable(aat2870);
+ if (aat2870->en_pin >= 0)
+ gpio_free(aat2870->en_pin);
+ if (aat2870->uninit)
+ aat2870->uninit(aat2870);
+ kfree(aat2870);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int aat2870_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+ struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+
+ aat2870_disable(aat2870);
+
+ return 0;
+}
+
+static int aat2870_i2c_resume(struct i2c_client *client)
+{
+ struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+ struct aat2870_register *reg = NULL;
+ int i;
+
+ aat2870_enable(aat2870);
+
+ /* restore registers */
+ for (i = 0; i < AAT2870_REG_NUM; i++) {
+ reg = &aat2870->reg_cache[i];
+ if (reg->writeable)
+ aat2870->write(aat2870, i, reg->value);
+ }
+
+ return 0;
+}
+#else
+#define aat2870_i2c_suspend NULL
+#define aat2870_i2c_resume NULL
+#endif /* CONFIG_PM */
+
+static struct i2c_device_id aat2870_i2c_id_table[] = {
+ { "aat2870", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aat2870_i2c_id_table);
+
+static struct i2c_driver aat2870_i2c_driver = {
+ .driver = {
+ .name = "aat2870",
+ .owner = THIS_MODULE,
+ },
+ .probe = aat2870_i2c_probe,
+ .remove = aat2870_i2c_remove,
+ .suspend = aat2870_i2c_suspend,
+ .resume = aat2870_i2c_resume,
+ .id_table = aat2870_i2c_id_table,
+};
+
+static int __init aat2870_init(void)
+{
+ return i2c_add_driver(&aat2870_i2c_driver);
+}
+subsys_initcall(aat2870_init);
+
+static void __exit aat2870_exit(void)
+{
+ i2c_del_driver(&aat2870_i2c_driver);
+}
+module_exit(aat2870_exit);
+
+MODULE_DESCRIPTION("Core support for the AnalogicTech AAT2870");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index a20e1c41bed2..60107ee166fc 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -12,6 +12,7 @@
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/interrupt.h>
@@ -809,7 +810,7 @@ struct ab_family_id {
char *name;
};
-static const struct ab_family_id ids[] __devinitdata = {
+static const struct ab_family_id ids[] __devinitconst = {
/* AB3100 */
{
.id = 0xc0,
diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c
deleted file mode 100644
index 3d7dce671b93..000000000000
--- a/drivers/mfd/ab3550-core.c
+++ /dev/null
@@ -1,1399 +0,0 @@
-/*
- * Copyright (C) 2007-2010 ST-Ericsson
- * License terms: GNU General Public License (GPL) version 2
- * Low-level core for exclusive access to the AB3550 IC on the I2C bus
- * and some basic chip-configuration.
- * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
- * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
- * Author: Mattias Wallin <mattias.wallin@stericsson.com>
- * Author: Rickard Andersson <rickard.andersson@stericsson.com>
- */
-
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/random.h>
-#include <linux/workqueue.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/uaccess.h>
-#include <linux/mfd/abx500.h>
-#include <linux/list.h>
-#include <linux/bitops.h>
-#include <linux/spinlock.h>
-#include <linux/mfd/core.h>
-
-#define AB3550_NAME_STRING "ab3550"
-#define AB3550_ID_FORMAT_STRING "AB3550 %s"
-#define AB3550_NUM_BANKS 2
-#define AB3550_NUM_EVENT_REG 5
-
-/* These are the only registers inside AB3550 used in this main file */
-
-/* Chip ID register */
-#define AB3550_CID_REG 0x20
-
-/* Interrupt event registers */
-#define AB3550_EVENT_BANK 0
-#define AB3550_EVENT_REG 0x22
-
-/* Read/write operation values. */
-#define AB3550_PERM_RD (0x01)
-#define AB3550_PERM_WR (0x02)
-
-/* Read/write permissions. */
-#define AB3550_PERM_RO (AB3550_PERM_RD)
-#define AB3550_PERM_RW (AB3550_PERM_RD | AB3550_PERM_WR)
-
-/**
- * struct ab3550
- * @access_mutex: lock out concurrent accesses to the AB registers
- * @i2c_client: I2C client for this chip
- * @chip_name: name of this chip variant
- * @chip_id: 8 bit chip ID for this chip variant
- * @mask_work: a worker for writing to mask registers
- * @event_lock: a lock to protect the event_mask
- * @event_mask: a local copy of the mask event registers
- * @startup_events: a copy of the first reading of the event registers
- * @startup_events_read: whether the first events have been read
- */
-struct ab3550 {
- struct mutex access_mutex;
- struct i2c_client *i2c_client[AB3550_NUM_BANKS];
- char chip_name[32];
- u8 chip_id;
- struct work_struct mask_work;
- spinlock_t event_lock;
- u8 event_mask[AB3550_NUM_EVENT_REG];
- u8 startup_events[AB3550_NUM_EVENT_REG];
- bool startup_events_read;
-#ifdef CONFIG_DEBUG_FS
- unsigned int debug_bank;
- unsigned int debug_address;
-#endif
-};
-
-/**
- * struct ab3550_reg_range
- * @first: the first address of the range
- * @last: the last address of the range
- * @perm: access permissions for the range
- */
-struct ab3550_reg_range {
- u8 first;
- u8 last;
- u8 perm;
-};
-
-/**
- * struct ab3550_reg_ranges
- * @count: the number of ranges in the list
- * @range: the list of register ranges
- */
-struct ab3550_reg_ranges {
- u8 count;
- const struct ab3550_reg_range *range;
-};
-
-/*
- * Permissible register ranges for reading and writing per device and bank.
- *
- * The ranges must be listed in increasing address order, and no overlaps are
- * allowed. It is assumed that write permission implies read permission
- * (i.e. only RO and RW permissions should be used). Ranges with write
- * permission must not be split up.
- */
-
-#define NO_RANGE {.count = 0, .range = NULL,}
-
-static struct
-ab3550_reg_ranges ab3550_reg_ranges[AB3550_NUM_DEVICES][AB3550_NUM_BANKS] = {
- [AB3550_DEVID_DAC] = {
- NO_RANGE,
- {
- .count = 2,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0xb0,
- .last = 0xba,
- .perm = AB3550_PERM_RW,
- },
- {
- .first = 0xbc,
- .last = 0xc3,
- .perm = AB3550_PERM_RW,
- },
- },
- },
- },
- [AB3550_DEVID_LEDS] = {
- NO_RANGE,
- {
- .count = 2,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x5a,
- .last = 0x88,
- .perm = AB3550_PERM_RW,
- },
- {
- .first = 0x8a,
- .last = 0xad,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- },
- [AB3550_DEVID_POWER] = {
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x21,
- .last = 0x21,
- .perm = AB3550_PERM_RO,
- },
- }
- },
- NO_RANGE,
- },
- [AB3550_DEVID_REGULATORS] = {
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x69,
- .last = 0xa3,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x14,
- .last = 0x16,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- },
- [AB3550_DEVID_SIM] = {
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x21,
- .last = 0x21,
- .perm = AB3550_PERM_RO,
- },
- }
- },
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x14,
- .last = 0x17,
- .perm = AB3550_PERM_RW,
- },
- }
-
- },
- },
- [AB3550_DEVID_UART] = {
- NO_RANGE,
- NO_RANGE,
- },
- [AB3550_DEVID_RTC] = {
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x00,
- .last = 0x0c,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- NO_RANGE,
- },
- [AB3550_DEVID_CHARGER] = {
- {
- .count = 2,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x10,
- .last = 0x1a,
- .perm = AB3550_PERM_RW,
- },
- {
- .first = 0x21,
- .last = 0x21,
- .perm = AB3550_PERM_RO,
- },
- }
- },
- NO_RANGE,
- },
- [AB3550_DEVID_ADC] = {
- NO_RANGE,
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x20,
- .last = 0x56,
- .perm = AB3550_PERM_RW,
- },
-
- }
- },
- },
- [AB3550_DEVID_FUELGAUGE] = {
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x21,
- .last = 0x21,
- .perm = AB3550_PERM_RO,
- },
- }
- },
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x00,
- .last = 0x0e,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- },
- [AB3550_DEVID_VIBRATOR] = {
- NO_RANGE,
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x10,
- .last = 0x13,
- .perm = AB3550_PERM_RW,
- },
-
- }
- },
- },
- [AB3550_DEVID_CODEC] = {
- {
- .count = 2,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x31,
- .last = 0x63,
- .perm = AB3550_PERM_RW,
- },
- {
- .first = 0x65,
- .last = 0x68,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- NO_RANGE,
- },
-};
-
-static struct mfd_cell ab3550_devs[AB3550_NUM_DEVICES] = {
- [AB3550_DEVID_DAC] = {
- .name = "ab3550-dac",
- .id = AB3550_DEVID_DAC,
- .num_resources = 0,
- },
- [AB3550_DEVID_LEDS] = {
- .name = "ab3550-leds",
- .id = AB3550_DEVID_LEDS,
- },
- [AB3550_DEVID_POWER] = {
- .name = "ab3550-power",
- .id = AB3550_DEVID_POWER,
- },
- [AB3550_DEVID_REGULATORS] = {
- .name = "ab3550-regulators",
- .id = AB3550_DEVID_REGULATORS,
- },
- [AB3550_DEVID_SIM] = {
- .name = "ab3550-sim",
- .id = AB3550_DEVID_SIM,
- },
- [AB3550_DEVID_UART] = {
- .name = "ab3550-uart",
- .id = AB3550_DEVID_UART,
- },
- [AB3550_DEVID_RTC] = {
- .name = "ab3550-rtc",
- .id = AB3550_DEVID_RTC,
- },
- [AB3550_DEVID_CHARGER] = {
- .name = "ab3550-charger",
- .id = AB3550_DEVID_CHARGER,
- },
- [AB3550_DEVID_ADC] = {
- .name = "ab3550-adc",
- .id = AB3550_DEVID_ADC,
- .num_resources = 10,
- .resources = (struct resource[]) {
- {
- .name = "TRIGGER-0",
- .flags = IORESOURCE_IRQ,
- .start = 16,
- .end = 16,
- },
- {
- .name = "TRIGGER-1",
- .flags = IORESOURCE_IRQ,
- .start = 17,
- .end = 17,
- },
- {
- .name = "TRIGGER-2",
- .flags = IORESOURCE_IRQ,
- .start = 18,
- .end = 18,
- },
- {
- .name = "TRIGGER-3",
- .flags = IORESOURCE_IRQ,
- .start = 19,
- .end = 19,
- },
- {
- .name = "TRIGGER-4",
- .flags = IORESOURCE_IRQ,
- .start = 20,
- .end = 20,
- },
- {
- .name = "TRIGGER-5",
- .flags = IORESOURCE_IRQ,
- .start = 21,
- .end = 21,
- },
- {
- .name = "TRIGGER-6",
- .flags = IORESOURCE_IRQ,
- .start = 22,
- .end = 22,
- },
- {
- .name = "TRIGGER-7",
- .flags = IORESOURCE_IRQ,
- .start = 23,
- .end = 23,
- },
- {
- .name = "TRIGGER-VBAT-TXON",
- .flags = IORESOURCE_IRQ,
- .start = 13,
- .end = 13,
- },
- {
- .name = "TRIGGER-VBAT",
- .flags = IORESOURCE_IRQ,
- .start = 12,
- .end = 12,
- },
- },
- },
- [AB3550_DEVID_FUELGAUGE] = {
- .name = "ab3550-fuelgauge",
- .id = AB3550_DEVID_FUELGAUGE,
- },
- [AB3550_DEVID_VIBRATOR] = {
- .name = "ab3550-vibrator",
- .id = AB3550_DEVID_VIBRATOR,
- },
- [AB3550_DEVID_CODEC] = {
- .name = "ab3550-codec",
- .id = AB3550_DEVID_CODEC,
- },
-};
-
-/*
- * I2C transactions with error messages.
- */
-static int ab3550_i2c_master_send(struct ab3550 *ab, u8 bank, u8 *data,
- u8 count)
-{
- int err;
-
- err = i2c_master_send(ab->i2c_client[bank], data, count);
- if (err < 0) {
- dev_err(&ab->i2c_client[0]->dev, "send error: %d\n", err);
- return err;
- }
- return 0;
-}
-
-static int ab3550_i2c_master_recv(struct ab3550 *ab, u8 bank, u8 *data,
- u8 count)
-{
- int err;
-
- err = i2c_master_recv(ab->i2c_client[bank], data, count);
- if (err < 0) {
- dev_err(&ab->i2c_client[0]->dev, "receive error: %d\n", err);
- return err;
- }
- return 0;
-}
-
-/*
- * Functionality for getting/setting register values.
- */
-static int get_register_interruptible(struct ab3550 *ab, u8 bank, u8 reg,
- u8 *value)
-{
- int err;
-
- err = mutex_lock_interruptible(&ab->access_mutex);
- if (err)
- return err;
-
- err = ab3550_i2c_master_send(ab, bank, &reg, 1);
- if (!err)
- err = ab3550_i2c_master_recv(ab, bank, value, 1);
-
- mutex_unlock(&ab->access_mutex);
- return err;
-}
-
-static int get_register_page_interruptible(struct ab3550 *ab, u8 bank,
- u8 first_reg, u8 *regvals, u8 numregs)
-{
- int err;
-
- err = mutex_lock_interruptible(&ab->access_mutex);
- if (err)
- return err;
-
- err = ab3550_i2c_master_send(ab, bank, &first_reg, 1);
- if (!err)
- err = ab3550_i2c_master_recv(ab, bank, regvals, numregs);
-
- mutex_unlock(&ab->access_mutex);
- return err;
-}
-
-static int mask_and_set_register_interruptible(struct ab3550 *ab, u8 bank,
- u8 reg, u8 bitmask, u8 bitvalues)
-{
- int err = 0;
-
- if (likely(bitmask)) {
- u8 reg_bits[2] = {reg, 0};
-
- err = mutex_lock_interruptible(&ab->access_mutex);
- if (err)
- return err;
-
- if (bitmask == 0xFF) /* No need to read in this case. */
- reg_bits[1] = bitvalues;
- else { /* Read and modify the register value. */
- u8 bits;
-
- err = ab3550_i2c_master_send(ab, bank, &reg, 1);
- if (err)
- goto unlock_and_return;
- err = ab3550_i2c_master_recv(ab, bank, &bits, 1);
- if (err)
- goto unlock_and_return;
- reg_bits[1] = ((~bitmask & bits) |
- (bitmask & bitvalues));
- }
- /* Write the new value. */
- err = ab3550_i2c_master_send(ab, bank, reg_bits, 2);
-unlock_and_return:
- mutex_unlock(&ab->access_mutex);
- }
- return err;
-}
-
-/*
- * Read/write permission checking functions.
- */
-static bool page_write_allowed(const struct ab3550_reg_ranges *ranges,
- u8 first_reg, u8 last_reg)
-{
- u8 i;
-
- if (last_reg < first_reg)
- return false;
-
- for (i = 0; i < ranges->count; i++) {
- if (first_reg < ranges->range[i].first)
- break;
- if ((last_reg <= ranges->range[i].last) &&
- (ranges->range[i].perm & AB3550_PERM_WR))
- return true;
- }
- return false;
-}
-
-static bool reg_write_allowed(const struct ab3550_reg_ranges *ranges, u8 reg)
-{
- return page_write_allowed(ranges, reg, reg);
-}
-
-static bool page_read_allowed(const struct ab3550_reg_ranges *ranges,
- u8 first_reg, u8 last_reg)
-{
- u8 i;
-
- if (last_reg < first_reg)
- return false;
- /* Find the range (if it exists in the list) that includes first_reg. */
- for (i = 0; i < ranges->count; i++) {
- if (first_reg < ranges->range[i].first)
- return false;
- if (first_reg <= ranges->range[i].last)
- break;
- }
- /* Make sure that the entire range up to and including last_reg is
- * readable. This may span several of the ranges in the list.
- */
- while ((i < ranges->count) &&
- (ranges->range[i].perm & AB3550_PERM_RD)) {
- if (last_reg <= ranges->range[i].last)
- return true;
- if ((++i >= ranges->count) ||
- (ranges->range[i].first !=
- (ranges->range[i - 1].last + 1))) {
- break;
- }
- }
- return false;
-}
-
-static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg)
-{
- return page_read_allowed(ranges, reg, reg);
-}
-
-/*
- * The register access functionality.
- */
-static int ab3550_get_chip_id(struct device *dev)
-{
- struct ab3550 *ab = dev_get_drvdata(dev->parent);
- return (int)ab->chip_id;
-}
-
-static int ab3550_mask_and_set_register_interruptible(struct device *dev,
- u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
-{
- struct ab3550 *ab;
- struct platform_device *pdev = to_platform_device(dev);
-
- if ((AB3550_NUM_BANKS <= bank) ||
- !reg_write_allowed(&ab3550_reg_ranges[pdev->id][bank], reg))
- return -EINVAL;
-
- ab = dev_get_drvdata(dev->parent);
- return mask_and_set_register_interruptible(ab, bank, reg,
- bitmask, bitvalues);
-}
-
-static int ab3550_set_register_interruptible(struct device *dev, u8 bank,
- u8 reg, u8 value)
-{
- return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
- value);
-}
-
-static int ab3550_get_register_interruptible(struct device *dev, u8 bank,
- u8 reg, u8 *value)
-{
- struct ab3550 *ab;
- struct platform_device *pdev = to_platform_device(dev);
-
- if ((AB3550_NUM_BANKS <= bank) ||
- !reg_read_allowed(&ab3550_reg_ranges[pdev->id][bank], reg))
- return -EINVAL;
-
- ab = dev_get_drvdata(dev->parent);
- return get_register_interruptible(ab, bank, reg, value);
-}
-
-static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
- u8 first_reg, u8 *regvals, u8 numregs)
-{
- struct ab3550 *ab;
- struct platform_device *pdev = to_platform_device(dev);
-
- if ((AB3550_NUM_BANKS <= bank) ||
- !page_read_allowed(&ab3550_reg_ranges[pdev->id][bank],
- first_reg, (first_reg + numregs - 1)))
- return -EINVAL;
-
- ab = dev_get_drvdata(dev->parent);
- return get_register_page_interruptible(ab, bank, first_reg, regvals,
- numregs);
-}
-
-static int ab3550_event_registers_startup_state_get(struct device *dev,
- u8 *event)
-{
- struct ab3550 *ab;
-
- ab = dev_get_drvdata(dev->parent);
- if (!ab->startup_events_read)
- return -EAGAIN; /* Try again later */
-
- memcpy(event, ab->startup_events, AB3550_NUM_EVENT_REG);
- return 0;
-}
-
-static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
-{
- struct ab3550 *ab;
- struct ab3550_platform_data *plf_data;
- bool val;
-
- ab = irq_get_chip_data(irq);
- plf_data = ab->i2c_client[0]->dev.platform_data;
- irq -= plf_data->irq.base;
- val = ((ab->startup_events[irq / 8] & BIT(irq % 8)) != 0);
-
- return val;
-}
-
-static struct abx500_ops ab3550_ops = {
- .get_chip_id = ab3550_get_chip_id,
- .get_register = ab3550_get_register_interruptible,
- .set_register = ab3550_set_register_interruptible,
- .get_register_page = ab3550_get_register_page_interruptible,
- .set_register_page = NULL,
- .mask_and_set_register = ab3550_mask_and_set_register_interruptible,
- .event_registers_startup_state_get =
- ab3550_event_registers_startup_state_get,
- .startup_irq_enabled = ab3550_startup_irq_enabled,
-};
-
-static irqreturn_t ab3550_irq_handler(int irq, void *data)
-{
- struct ab3550 *ab = data;
- int err;
- unsigned int i;
- u8 e[AB3550_NUM_EVENT_REG];
- u8 *events;
- unsigned long flags;
-
- events = (ab->startup_events_read ? e : ab->startup_events);
-
- err = get_register_page_interruptible(ab, AB3550_EVENT_BANK,
- AB3550_EVENT_REG, events, AB3550_NUM_EVENT_REG);
- if (err)
- goto err_event_rd;
-
- if (!ab->startup_events_read) {
- dev_info(&ab->i2c_client[0]->dev,
- "startup events 0x%x,0x%x,0x%x,0x%x,0x%x\n",
- ab->startup_events[0], ab->startup_events[1],
- ab->startup_events[2], ab->startup_events[3],
- ab->startup_events[4]);
- ab->startup_events_read = true;
- goto out;
- }
-
- /* The two highest bits in event[4] are not used. */
- events[4] &= 0x3f;
-
- spin_lock_irqsave(&ab->event_lock, flags);
- for (i = 0; i < AB3550_NUM_EVENT_REG; i++)
- events[i] &= ~ab->event_mask[i];
- spin_unlock_irqrestore(&ab->event_lock, flags);
-
- for (i = 0; i < AB3550_NUM_EVENT_REG; i++) {
- u8 bit;
- u8 event_reg;
-
- dev_dbg(&ab->i2c_client[0]->dev, "IRQ Event[%d]: 0x%2x\n",
- i, events[i]);
-
- event_reg = events[i];
- for (bit = 0; event_reg; bit++, event_reg /= 2) {
- if (event_reg % 2) {
- unsigned int irq;
- struct ab3550_platform_data *plf_data;
-
- plf_data = ab->i2c_client[0]->dev.platform_data;
- irq = plf_data->irq.base + (i * 8) + bit;
- handle_nested_irq(irq);
- }
- }
- }
-out:
- return IRQ_HANDLED;
-
-err_event_rd:
- dev_dbg(&ab->i2c_client[0]->dev, "error reading event registers\n");
- return IRQ_HANDLED;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static struct ab3550_reg_ranges debug_ranges[AB3550_NUM_BANKS] = {
- {
- .count = 6,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x00,
- .last = 0x0e,
- },
- {
- .first = 0x10,
- .last = 0x1a,
- },
- {
- .first = 0x1e,
- .last = 0x4f,
- },
- {
- .first = 0x51,
- .last = 0x63,
- },
- {
- .first = 0x65,
- .last = 0xa3,
- },
- {
- .first = 0xa5,
- .last = 0xa8,
- },
- }
- },
- {
- .count = 8,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x00,
- .last = 0x0e,
- },
- {
- .first = 0x10,
- .last = 0x17,
- },
- {
- .first = 0x1a,
- .last = 0x1c,
- },
- {
- .first = 0x20,
- .last = 0x56,
- },
- {
- .first = 0x5a,
- .last = 0x88,
- },
- {
- .first = 0x8a,
- .last = 0xad,
- },
- {
- .first = 0xb0,
- .last = 0xba,
- },
- {
- .first = 0xbc,
- .last = 0xc3,
- },
- }
- },
-};
-
-static int ab3550_registers_print(struct seq_file *s, void *p)
-{
- struct ab3550 *ab = s->private;
- int bank;
-
- seq_printf(s, AB3550_NAME_STRING " register values:\n");
-
- for (bank = 0; bank < AB3550_NUM_BANKS; bank++) {
- unsigned int i;
-
- seq_printf(s, " bank %d:\n", bank);
- for (i = 0; i < debug_ranges[bank].count; i++) {
- u8 reg;
-
- for (reg = debug_ranges[bank].range[i].first;
- reg <= debug_ranges[bank].range[i].last;
- reg++) {
- u8 value;
-
- get_register_interruptible(ab, bank, reg,
- &value);
- seq_printf(s, " [%d/0x%02X]: 0x%02X\n", bank,
- reg, value);
- }
- }
- }
- return 0;
-}
-
-static int ab3550_registers_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ab3550_registers_print, inode->i_private);
-}
-
-static const struct file_operations ab3550_registers_fops = {
- .open = ab3550_registers_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static int ab3550_bank_print(struct seq_file *s, void *p)
-{
- struct ab3550 *ab = s->private;
-
- seq_printf(s, "%d\n", ab->debug_bank);
- return 0;
-}
-
-static int ab3550_bank_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ab3550_bank_print, inode->i_private);
-}
-
-static ssize_t ab3550_bank_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
- unsigned long user_bank;
- int err;
-
- /* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_bank);
- if (err)
- return -EINVAL;
-
- if (user_bank >= AB3550_NUM_BANKS) {
- dev_err(&ab->i2c_client[0]->dev,
- "debugfs error input > number of banks\n");
- return -EINVAL;
- }
-
- ab->debug_bank = user_bank;
-
- return buf_size;
-}
-
-static int ab3550_address_print(struct seq_file *s, void *p)
-{
- struct ab3550 *ab = s->private;
-
- seq_printf(s, "0x%02X\n", ab->debug_address);
- return 0;
-}
-
-static int ab3550_address_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ab3550_address_print, inode->i_private);
-}
-
-static ssize_t ab3550_address_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
- unsigned long user_address;
- int err;
-
- /* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_address);
- if (err)
- return -EINVAL;
- if (user_address > 0xff) {
- dev_err(&ab->i2c_client[0]->dev,
- "debugfs error input > 0xff\n");
- return -EINVAL;
- }
- ab->debug_address = user_address;
- return buf_size;
-}
-
-static int ab3550_val_print(struct seq_file *s, void *p)
-{
- struct ab3550 *ab = s->private;
- int err;
- u8 regvalue;
-
- err = get_register_interruptible(ab, (u8)ab->debug_bank,
- (u8)ab->debug_address, &regvalue);
- if (err)
- return -EINVAL;
- seq_printf(s, "0x%02X\n", regvalue);
-
- return 0;
-}
-
-static int ab3550_val_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ab3550_val_print, inode->i_private);
-}
-
-static ssize_t ab3550_val_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
- unsigned long user_val;
- int err;
- u8 regvalue;
-
- /* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_val);
- if (err)
- return -EINVAL;
- if (user_val > 0xff) {
- dev_err(&ab->i2c_client[0]->dev,
- "debugfs error input > 0xff\n");
- return -EINVAL;
- }
- err = mask_and_set_register_interruptible(
- ab, (u8)ab->debug_bank,
- (u8)ab->debug_address, 0xFF, (u8)user_val);
- if (err)
- return -EINVAL;
-
- get_register_interruptible(ab, (u8)ab->debug_bank,
- (u8)ab->debug_address, &regvalue);
- if (err)
- return -EINVAL;
-
- return buf_size;
-}
-
-static const struct file_operations ab3550_bank_fops = {
- .open = ab3550_bank_open,
- .write = ab3550_bank_write,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static const struct file_operations ab3550_address_fops = {
- .open = ab3550_address_open,
- .write = ab3550_address_write,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static const struct file_operations ab3550_val_fops = {
- .open = ab3550_val_open,
- .write = ab3550_val_write,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static struct dentry *ab3550_dir;
-static struct dentry *ab3550_reg_file;
-static struct dentry *ab3550_bank_file;
-static struct dentry *ab3550_address_file;
-static struct dentry *ab3550_val_file;
-
-static inline void ab3550_setup_debugfs(struct ab3550 *ab)
-{
- ab->debug_bank = 0;
- ab->debug_address = 0x00;
-
- ab3550_dir = debugfs_create_dir(AB3550_NAME_STRING, NULL);
- if (!ab3550_dir)
- goto exit_no_debugfs;
-
- ab3550_reg_file = debugfs_create_file("all-registers",
- S_IRUGO, ab3550_dir, ab, &ab3550_registers_fops);
- if (!ab3550_reg_file)
- goto exit_destroy_dir;
-
- ab3550_bank_file = debugfs_create_file("register-bank",
- (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_bank_fops);
- if (!ab3550_bank_file)
- goto exit_destroy_reg;
-
- ab3550_address_file = debugfs_create_file("register-address",
- (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_address_fops);
- if (!ab3550_address_file)
- goto exit_destroy_bank;
-
- ab3550_val_file = debugfs_create_file("register-value",
- (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_val_fops);
- if (!ab3550_val_file)
- goto exit_destroy_address;
-
- return;
-
-exit_destroy_address:
- debugfs_remove(ab3550_address_file);
-exit_destroy_bank:
- debugfs_remove(ab3550_bank_file);
-exit_destroy_reg:
- debugfs_remove(ab3550_reg_file);
-exit_destroy_dir:
- debugfs_remove(ab3550_dir);
-exit_no_debugfs:
- dev_err(&ab->i2c_client[0]->dev, "failed to create debugfs entries.\n");
- return;
-}
-
-static inline void ab3550_remove_debugfs(void)
-{
- debugfs_remove(ab3550_val_file);
- debugfs_remove(ab3550_address_file);
- debugfs_remove(ab3550_bank_file);
- debugfs_remove(ab3550_reg_file);
- debugfs_remove(ab3550_dir);
-}
-
-#else /* !CONFIG_DEBUG_FS */
-static inline void ab3550_setup_debugfs(struct ab3550 *ab)
-{
-}
-static inline void ab3550_remove_debugfs(void)
-{
-}
-#endif
-
-/*
- * Basic set-up, datastructure creation/destruction and I2C interface.
- * This sets up a default config in the AB3550 chip so that it
- * will work as expected.
- */
-static int __init ab3550_setup(struct ab3550 *ab)
-{
- int err = 0;
- int i;
- struct ab3550_platform_data *plf_data;
- struct abx500_init_settings *settings;
-
- plf_data = ab->i2c_client[0]->dev.platform_data;
- settings = plf_data->init_settings;
-
- for (i = 0; i < plf_data->init_settings_sz; i++) {
- err = mask_and_set_register_interruptible(ab,
- settings[i].bank,
- settings[i].reg,
- 0xFF, settings[i].setting);
- if (err)
- goto exit_no_setup;
-
- /* If event mask register update the event mask in ab3550 */
- if ((settings[i].bank == 0) &&
- (AB3550_IMR1 <= settings[i].reg) &&
- (settings[i].reg <= AB3550_IMR5)) {
- ab->event_mask[settings[i].reg - AB3550_IMR1] =
- settings[i].setting;
- }
- }
-exit_no_setup:
- return err;
-}
-
-static void ab3550_mask_work(struct work_struct *work)
-{
- struct ab3550 *ab = container_of(work, struct ab3550, mask_work);
- int i;
- unsigned long flags;
- u8 mask[AB3550_NUM_EVENT_REG];
-
- spin_lock_irqsave(&ab->event_lock, flags);
- for (i = 0; i < AB3550_NUM_EVENT_REG; i++)
- mask[i] = ab->event_mask[i];
- spin_unlock_irqrestore(&ab->event_lock, flags);
-
- for (i = 0; i < AB3550_NUM_EVENT_REG; i++) {
- int err;
-
- err = mask_and_set_register_interruptible(ab, 0,
- (AB3550_IMR1 + i), ~0, mask[i]);
- if (err)
- dev_err(&ab->i2c_client[0]->dev,
- "ab3550_mask_work failed 0x%x,0x%x\n",
- (AB3550_IMR1 + i), mask[i]);
- }
-}
-
-static void ab3550_mask(struct irq_data *data)
-{
- unsigned long flags;
- struct ab3550 *ab;
- struct ab3550_platform_data *plf_data;
- int irq;
-
- ab = irq_data_get_irq_chip_data(data);
- plf_data = ab->i2c_client[0]->dev.platform_data;
- irq = data->irq - plf_data->irq.base;
-
- spin_lock_irqsave(&ab->event_lock, flags);
- ab->event_mask[irq / 8] |= BIT(irq % 8);
- spin_unlock_irqrestore(&ab->event_lock, flags);
-
- schedule_work(&ab->mask_work);
-}
-
-static void ab3550_unmask(struct irq_data *data)
-{
- unsigned long flags;
- struct ab3550 *ab;
- struct ab3550_platform_data *plf_data;
- int irq;
-
- ab = irq_data_get_irq_chip_data(data);
- plf_data = ab->i2c_client[0]->dev.platform_data;
- irq = data->irq - plf_data->irq.base;
-
- spin_lock_irqsave(&ab->event_lock, flags);
- ab->event_mask[irq / 8] &= ~BIT(irq % 8);
- spin_unlock_irqrestore(&ab->event_lock, flags);
-
- schedule_work(&ab->mask_work);
-}
-
-static void noop(struct irq_data *data)
-{
-}
-
-static struct irq_chip ab3550_irq_chip = {
- .name = "ab3550-core", /* Keep the same name as the request */
- .irq_disable = ab3550_mask, /* No default to mask in chip.c */
- .irq_ack = noop,
- .irq_mask = ab3550_mask,
- .irq_unmask = ab3550_unmask,
-};
-
-struct ab_family_id {
- u8 id;
- char *name;
-};
-
-static const struct ab_family_id ids[] __initdata = {
- /* AB3550 */
- {
- .id = AB3550_P1A,
- .name = "P1A"
- },
- /* Terminator */
- {
- .id = 0x00,
- }
-};
-
-static int __init ab3550_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct ab3550 *ab;
- struct ab3550_platform_data *ab3550_plf_data =
- client->dev.platform_data;
- int err;
- int i;
- int num_i2c_clients = 0;
-
- ab = kzalloc(sizeof(struct ab3550), GFP_KERNEL);
- if (!ab) {
- dev_err(&client->dev,
- "could not allocate " AB3550_NAME_STRING " device\n");
- return -ENOMEM;
- }
-
- /* Initialize data structure */
- mutex_init(&ab->access_mutex);
- spin_lock_init(&ab->event_lock);
- ab->i2c_client[0] = client;
-
- i2c_set_clientdata(client, ab);
-
- /* Read chip ID register */
- err = get_register_interruptible(ab, 0, AB3550_CID_REG, &ab->chip_id);
- if (err) {
- dev_err(&client->dev, "could not communicate with the analog "
- "baseband chip\n");
- goto exit_no_detect;
- }
-
- for (i = 0; ids[i].id != 0x0; i++) {
- if (ids[i].id == ab->chip_id) {
- snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1,
- AB3550_ID_FORMAT_STRING, ids[i].name);
- break;
- }
- }
-
- if (ids[i].id == 0x0) {
- dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n",
- ab->chip_id);
- dev_err(&client->dev, "driver not started!\n");
- goto exit_no_detect;
- }
-
- dev_info(&client->dev, "detected AB chip: %s\n", &ab->chip_name[0]);
-
- /* Attach other dummy I2C clients. */
- while (++num_i2c_clients < AB3550_NUM_BANKS) {
- ab->i2c_client[num_i2c_clients] =
- i2c_new_dummy(client->adapter,
- (client->addr + num_i2c_clients));
- if (!ab->i2c_client[num_i2c_clients]) {
- err = -ENOMEM;
- goto exit_no_dummy_client;
- }
- strlcpy(ab->i2c_client[num_i2c_clients]->name, id->name,
- sizeof(ab->i2c_client[num_i2c_clients]->name));
- }
-
- err = ab3550_setup(ab);
- if (err)
- goto exit_no_setup;
-
- INIT_WORK(&ab->mask_work, ab3550_mask_work);
-
- for (i = 0; i < ab3550_plf_data->irq.count; i++) {
- unsigned int irq;
-
- irq = ab3550_plf_data->irq.base + i;
- irq_set_chip_data(irq, ab);
- irq_set_chip_and_handler(irq, &ab3550_irq_chip,
- handle_simple_irq);
- irq_set_nested_thread(irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID);
-#else
- irq_set_noprobe(irq);
-#endif
- }
-
- err = request_threaded_irq(client->irq, NULL, ab3550_irq_handler,
- IRQF_ONESHOT, "ab3550-core", ab);
- /* This real unpredictable IRQ is of course sampled for entropy */
- rand_initialize_irq(client->irq);
-
- if (err)
- goto exit_no_irq;
-
- err = abx500_register_ops(&client->dev, &ab3550_ops);
- if (err)
- goto exit_no_ops;
-
- /* Set up and register the platform devices. */
- for (i = 0; i < AB3550_NUM_DEVICES; i++) {
- ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i];
- ab3550_devs[i].pdata_size = ab3550_plf_data->dev_data_sz[i];
- }
-
- err = mfd_add_devices(&client->dev, 0, ab3550_devs,
- ARRAY_SIZE(ab3550_devs), NULL,
- ab3550_plf_data->irq.base);
-
- ab3550_setup_debugfs(ab);
-
- return 0;
-
-exit_no_ops:
-exit_no_irq:
-exit_no_setup:
-exit_no_dummy_client:
- /* Unregister the dummy i2c clients. */
- while (--num_i2c_clients)
- i2c_unregister_device(ab->i2c_client[num_i2c_clients]);
-exit_no_detect:
- kfree(ab);
- return err;
-}
-
-static int __exit ab3550_remove(struct i2c_client *client)
-{
- struct ab3550 *ab = i2c_get_clientdata(client);
- int num_i2c_clients = AB3550_NUM_BANKS;
-
- mfd_remove_devices(&client->dev);
- ab3550_remove_debugfs();
-
- while (--num_i2c_clients)
- i2c_unregister_device(ab->i2c_client[num_i2c_clients]);
-
- /*
- * At this point, all subscribers should have unregistered
- * their notifiers so deactivate IRQ
- */
- free_irq(client->irq, ab);
- kfree(ab);
- return 0;
-}
-
-static const struct i2c_device_id ab3550_id[] = {
- {AB3550_NAME_STRING, 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, ab3550_id);
-
-static struct i2c_driver ab3550_driver = {
- .driver = {
- .name = AB3550_NAME_STRING,
- .owner = THIS_MODULE,
- },
- .id_table = ab3550_id,
- .probe = ab3550_probe,
- .remove = __exit_p(ab3550_remove),
-};
-
-static int __init ab3550_i2c_init(void)
-{
- return i2c_add_driver(&ab3550_driver);
-}
-
-static void __exit ab3550_i2c_exit(void)
-{
- i2c_del_driver(&ab3550_driver);
-}
-
-subsys_initcall(ab3550_i2c_init);
-module_exit(ab3550_i2c_exit);
-
-MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
-MODULE_DESCRIPTION("AB3550 core driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c
new file mode 100644
index 000000000000..ec10629a0b0b
--- /dev/null
+++ b/drivers/mfd/ab5500-core.c
@@ -0,0 +1,1440 @@
+/*
+ * Copyright (C) 2007-2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Low-level core for exclusive access to the AB5500 IC on the I2C bus
+ * and some basic chip-configuration.
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com>
+ * Author: Karl Komierowski <karl.komierowski@stericsson.com>
+ * Author: Bibek Basu <bibek.basu@stericsson.com>
+ *
+ * TODO: Event handling with irq_chip. Waiting for PRCMU fw support.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/mfd/ab5500/ab5500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/core.h>
+#include <linux/version.h>
+#include <linux/mfd/db5500-prcmu.h>
+
+#include "ab5500-core.h"
+#include "ab5500-debugfs.h"
+
+#define AB5500_NUM_EVENT_REG 23
+#define AB5500_IT_LATCH0_REG 0x40
+#define AB5500_IT_MASK0_REG 0x60
+
+/*
+ * Permissible register ranges for reading and writing per device and bank.
+ *
+ * The ranges must be listed in increasing address order, and no overlaps are
+ * allowed. It is assumed that write permission implies read permission
+ * (i.e. only RO and RW permissions should be used). Ranges with write
+ * permission must not be split up.
+ */
+
+#define NO_RANGE {.count = 0, .range = NULL,}
+static struct ab5500_i2c_banks ab5500_bank_ranges[AB5500_NUM_DEVICES] = {
+ [AB5500_DEVID_USB] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_USB,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x83,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x87,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8B,
+ .last = 0x8B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x91,
+ .last = 0x92,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x93,
+ .last = 0x93,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x94,
+ .last = 0x94,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xA8,
+ .last = 0xB0,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB2,
+ .last = 0xB2,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB4,
+ .last = 0xBC,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xBF,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xC5,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_ADC] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_ADC,
+ .nranges = 6,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x1F,
+ .last = 0x22,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x23,
+ .last = 0x24,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x26,
+ .last = 0x2D,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x34,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x37,
+ .last = 0x57,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x58,
+ .last = 0x58,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_LEDS] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_LED,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_VIDEO] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_VDENC,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x08,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x09,
+ .last = 0x09,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0A,
+ .last = 0x12,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x15,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1B,
+ .last = 0x21,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x27,
+ .last = 0x2C,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x41,
+ .last = 0x41,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x45,
+ .last = 0x5B,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x5D,
+ .last = 0x5D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x69,
+ .last = 0x69,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x6C,
+ .last = 0x6D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_REGULATORS] = {
+ .nbanks = 2,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_STARTUP,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1F,
+ .last = 0x1F,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x2E,
+ .last = 0x2E,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x30,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x51,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x61,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x66,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8C,
+ .last = 0x96,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xAA,
+ .last = 0xB4,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xB7,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xCA,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xD3,
+ .last = 0xE0,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ {
+ .bankid = AB5500_BANK_SIM_USBSIM,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x13,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_SIM] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_SIM_USBSIM,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x13,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_RTC] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_RTC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x04,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x06,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_CHARGER] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_CHG,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x11,
+ .last = 0x11,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x12,
+ .last = 0x1B,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_FUELGAUGE] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_FG_BATTCOM_ACC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0C,
+ .last = 0x10,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_VIBRATOR] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_VIBRA,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x10,
+ .last = 0x13,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xFE,
+ .last = 0xFE,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_CODEC] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_AUDIO_HEADSETUSB,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x48,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xEB,
+ .last = 0xFB,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_POWER] = {
+ .nbanks = 2,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_STARTUP,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x30,
+ .last = 0x30,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ {
+ .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+};
+
+#define AB5500_IRQ(bank, bit) ((bank) * 8 + (bit))
+
+/* I appologize for the resource names beeing a mix of upper case
+ * and lower case but I want them to be exact as the documentation */
+static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = {
+ [AB5500_DEVID_LEDS] = {
+ .name = "ab5500-leds",
+ .id = AB5500_DEVID_LEDS,
+ },
+ [AB5500_DEVID_POWER] = {
+ .name = "ab5500-power",
+ .id = AB5500_DEVID_POWER,
+ },
+ [AB5500_DEVID_REGULATORS] = {
+ .name = "ab5500-regulator",
+ .id = AB5500_DEVID_REGULATORS,
+ },
+ [AB5500_DEVID_SIM] = {
+ .name = "ab5500-sim",
+ .id = AB5500_DEVID_SIM,
+ .num_resources = 1,
+ .resources = (struct resource[]) {
+ {
+ .name = "SIMOFF",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(2, 0), /*rising*/
+ .end = AB5500_IRQ(2, 1), /*falling*/
+ },
+ },
+ },
+ [AB5500_DEVID_RTC] = {
+ .name = "ab5500-rtc",
+ .id = AB5500_DEVID_RTC,
+ .num_resources = 1,
+ .resources = (struct resource[]) {
+ {
+ .name = "RTC_Alarm",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(1, 7),
+ .end = AB5500_IRQ(1, 7),
+ }
+ },
+ },
+ [AB5500_DEVID_CHARGER] = {
+ .name = "ab5500-charger",
+ .id = AB5500_DEVID_CHARGER,
+ },
+ [AB5500_DEVID_ADC] = {
+ .name = "ab5500-adc",
+ .id = AB5500_DEVID_ADC,
+ .num_resources = 10,
+ .resources = (struct resource[]) {
+ {
+ .name = "TRIGGER-0",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 0),
+ .end = AB5500_IRQ(0, 0),
+ },
+ {
+ .name = "TRIGGER-1",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 1),
+ .end = AB5500_IRQ(0, 1),
+ },
+ {
+ .name = "TRIGGER-2",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 2),
+ .end = AB5500_IRQ(0, 2),
+ },
+ {
+ .name = "TRIGGER-3",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 3),
+ .end = AB5500_IRQ(0, 3),
+ },
+ {
+ .name = "TRIGGER-4",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 4),
+ .end = AB5500_IRQ(0, 4),
+ },
+ {
+ .name = "TRIGGER-5",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 5),
+ .end = AB5500_IRQ(0, 5),
+ },
+ {
+ .name = "TRIGGER-6",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 6),
+ .end = AB5500_IRQ(0, 6),
+ },
+ {
+ .name = "TRIGGER-7",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 7),
+ .end = AB5500_IRQ(0, 7),
+ },
+ {
+ .name = "TRIGGER-VBAT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 8),
+ .end = AB5500_IRQ(0, 8),
+ },
+ {
+ .name = "TRIGGER-VBAT-TXON",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 9),
+ .end = AB5500_IRQ(0, 9),
+ },
+ },
+ },
+ [AB5500_DEVID_FUELGAUGE] = {
+ .name = "ab5500-fuelgauge",
+ .id = AB5500_DEVID_FUELGAUGE,
+ .num_resources = 6,
+ .resources = (struct resource[]) {
+ {
+ .name = "Batt_attach",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(7, 5),
+ .end = AB5500_IRQ(7, 5),
+ },
+ {
+ .name = "Batt_removal",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(7, 6),
+ .end = AB5500_IRQ(7, 6),
+ },
+ {
+ .name = "UART_framing",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(7, 7),
+ .end = AB5500_IRQ(7, 7),
+ },
+ {
+ .name = "UART_overrun",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 0),
+ .end = AB5500_IRQ(8, 0),
+ },
+ {
+ .name = "UART_Rdy_RX",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 1),
+ .end = AB5500_IRQ(8, 1),
+ },
+ {
+ .name = "UART_Rdy_TX",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 2),
+ .end = AB5500_IRQ(8, 2),
+ },
+ },
+ },
+ [AB5500_DEVID_VIBRATOR] = {
+ .name = "ab5500-vibrator",
+ .id = AB5500_DEVID_VIBRATOR,
+ },
+ [AB5500_DEVID_CODEC] = {
+ .name = "ab5500-codec",
+ .id = AB5500_DEVID_CODEC,
+ .num_resources = 3,
+ .resources = (struct resource[]) {
+ {
+ .name = "audio_spkr1_ovc",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 5),
+ .end = AB5500_IRQ(9, 5),
+ },
+ {
+ .name = "audio_plllocked",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 6),
+ .end = AB5500_IRQ(9, 6),
+ },
+ {
+ .name = "audio_spkr2_ovc",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 4),
+ .end = AB5500_IRQ(17, 4),
+ },
+ },
+ },
+ [AB5500_DEVID_USB] = {
+ .name = "ab5500-usb",
+ .id = AB5500_DEVID_USB,
+ .num_resources = 36,
+ .resources = (struct resource[]) {
+ {
+ .name = "Link_Update",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(22, 1),
+ .end = AB5500_IRQ(22, 1),
+ },
+ {
+ .name = "DCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 3),
+ .end = AB5500_IRQ(8, 4),
+ },
+ {
+ .name = "VBUS_R",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 5),
+ .end = AB5500_IRQ(8, 5),
+ },
+ {
+ .name = "VBUS_F",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 6),
+ .end = AB5500_IRQ(8, 6),
+ },
+ {
+ .name = "CHGstate_10_PCVBUSchg",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 7),
+ .end = AB5500_IRQ(8, 7),
+ },
+ {
+ .name = "DCIOreverse_ovc",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 0),
+ .end = AB5500_IRQ(9, 0),
+ },
+ {
+ .name = "USBCharDetDone",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 1),
+ .end = AB5500_IRQ(9, 1),
+ },
+ {
+ .name = "DCIO_no_limit",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 2),
+ .end = AB5500_IRQ(9, 2),
+ },
+ {
+ .name = "USB_suspend",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 3),
+ .end = AB5500_IRQ(9, 3),
+ },
+ {
+ .name = "DCIOreverse_fwdcurrent",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 4),
+ .end = AB5500_IRQ(9, 4),
+ },
+ {
+ .name = "Vbus_Imeasmax_change",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 5),
+ .end = AB5500_IRQ(9, 6),
+ },
+ {
+ .name = "OVV",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 5),
+ .end = AB5500_IRQ(14, 5),
+ },
+ {
+ .name = "USBcharging_NOTok",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 3),
+ .end = AB5500_IRQ(15, 3),
+ },
+ {
+ .name = "usb_adp_sensoroff",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 6),
+ .end = AB5500_IRQ(15, 6),
+ },
+ {
+ .name = "usb_adp_probeplug",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 7),
+ .end = AB5500_IRQ(15, 7),
+ },
+ {
+ .name = "usb_adp_sinkerror",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 0),
+ .end = AB5500_IRQ(16, 6),
+ },
+ {
+ .name = "usb_adp_sourceerror",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 1),
+ .end = AB5500_IRQ(16, 1),
+ },
+ {
+ .name = "usb_idgnd_r",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 2),
+ .end = AB5500_IRQ(16, 2),
+ },
+ {
+ .name = "usb_idgnd_f",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 3),
+ .end = AB5500_IRQ(16, 3),
+ },
+ {
+ .name = "usb_iddetR1",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 4),
+ .end = AB5500_IRQ(16, 5),
+ },
+ {
+ .name = "usb_iddetR2",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 6),
+ .end = AB5500_IRQ(16, 7),
+ },
+ {
+ .name = "usb_iddetR3",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 0),
+ .end = AB5500_IRQ(17, 1),
+ },
+ {
+ .name = "usb_iddetR4",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 2),
+ .end = AB5500_IRQ(17, 3),
+ },
+ {
+ .name = "CharTempWindowOk",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 7),
+ .end = AB5500_IRQ(18, 0),
+ },
+ {
+ .name = "USB_SprDetect",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 1),
+ .end = AB5500_IRQ(18, 1),
+ },
+ {
+ .name = "usb_adp_probe_unplug",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 2),
+ .end = AB5500_IRQ(18, 2),
+ },
+ {
+ .name = "VBUSChDrop",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 3),
+ .end = AB5500_IRQ(18, 4),
+ },
+ {
+ .name = "dcio_char_rec_done",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 5),
+ .end = AB5500_IRQ(18, 5),
+ },
+ {
+ .name = "Charging_stopped_by_temp",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 6),
+ .end = AB5500_IRQ(18, 6),
+ },
+ {
+ .name = "CHGstate_11_SafeModeVBUS",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 1),
+ .end = AB5500_IRQ(21, 2),
+ },
+ {
+ .name = "CHGstate_12_comletedVBUS",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 2),
+ .end = AB5500_IRQ(21, 2),
+ },
+ {
+ .name = "CHGstate_13_completedVBUS",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 3),
+ .end = AB5500_IRQ(21, 3),
+ },
+ {
+ .name = "CHGstate_14_FullChgDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 4),
+ .end = AB5500_IRQ(21, 4),
+ },
+ {
+ .name = "CHGstate_15_SafeModeDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 5),
+ .end = AB5500_IRQ(21, 5),
+ },
+ {
+ .name = "CHGstate_16_OFFsuspendDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 6),
+ .end = AB5500_IRQ(21, 6),
+ },
+ {
+ .name = "CHGstate_17_completedDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 7),
+ .end = AB5500_IRQ(21, 7),
+ },
+ },
+ },
+ [AB5500_DEVID_OTP] = {
+ .name = "ab5500-otp",
+ .id = AB5500_DEVID_OTP,
+ },
+ [AB5500_DEVID_VIDEO] = {
+ .name = "ab5500-video",
+ .id = AB5500_DEVID_VIDEO,
+ .num_resources = 1,
+ .resources = (struct resource[]) {
+ {
+ .name = "plugTVdet",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(22, 2),
+ .end = AB5500_IRQ(22, 2),
+ },
+ },
+ },
+ [AB5500_DEVID_DBIECI] = {
+ .name = "ab5500-dbieci",
+ .id = AB5500_DEVID_DBIECI,
+ .num_resources = 10,
+ .resources = (struct resource[]) {
+ {
+ .name = "COLL",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 0),
+ .end = AB5500_IRQ(14, 0),
+ },
+ {
+ .name = "RESERR",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 1),
+ .end = AB5500_IRQ(14, 1),
+ },
+ {
+ .name = "FRAERR",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 2),
+ .end = AB5500_IRQ(14, 2),
+ },
+ {
+ .name = "COMERR",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 3),
+ .end = AB5500_IRQ(14, 3),
+ },
+ {
+ .name = "BSI_indicator",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 4),
+ .end = AB5500_IRQ(14, 4),
+ },
+ {
+ .name = "SPDSET",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 6),
+ .end = AB5500_IRQ(14, 6),
+ },
+ {
+ .name = "DSENT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 7),
+ .end = AB5500_IRQ(14, 7),
+ },
+ {
+ .name = "DREC",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 0),
+ .end = AB5500_IRQ(15, 0),
+ },
+ {
+ .name = "ACCINT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 1),
+ .end = AB5500_IRQ(15, 1),
+ },
+ {
+ .name = "NOPINT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 2),
+ .end = AB5500_IRQ(15, 2),
+ },
+ },
+ },
+ [AB5500_DEVID_ONSWA] = {
+ .name = "ab5500-onswa",
+ .id = AB5500_DEVID_ONSWA,
+ .num_resources = 2,
+ .resources = (struct resource[]) {
+ {
+ .name = "ONSWAn_rising",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(1, 3),
+ .end = AB5500_IRQ(1, 3),
+ },
+ {
+ .name = "ONSWAn_falling",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(1, 4),
+ .end = AB5500_IRQ(1, 4),
+ },
+ },
+ },
+};
+
+/*
+ * Functionality for getting/setting register values.
+ */
+int ab5500_get_register_interruptible_raw(struct ab5500 *ab,
+ u8 bank, u8 reg,
+ u8 *value)
+{
+ int err;
+
+ if (bank >= AB5500_NUM_BANKS)
+ return -EINVAL;
+
+ err = mutex_lock_interruptible(&ab->access_mutex);
+ if (err)
+ return err;
+ err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, reg, value, 1);
+
+ mutex_unlock(&ab->access_mutex);
+ return err;
+}
+
+static int get_register_page_interruptible(struct ab5500 *ab, u8 bank,
+ u8 first_reg, u8 *regvals, u8 numregs)
+{
+ int err;
+
+ if (bank >= AB5500_NUM_BANKS)
+ return -EINVAL;
+
+ err = mutex_lock_interruptible(&ab->access_mutex);
+ if (err)
+ return err;
+
+ while (numregs) {
+ /* The hardware limit for get page is 4 */
+ u8 curnum = min_t(u8, numregs, 4u);
+
+ err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr,
+ first_reg, regvals, curnum);
+ if (err)
+ goto out;
+
+ numregs -= curnum;
+ first_reg += curnum;
+ regvals += curnum;
+ }
+
+out:
+ mutex_unlock(&ab->access_mutex);
+ return err;
+}
+
+int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank,
+ u8 reg, u8 bitmask, u8 bitvalues)
+{
+ int err = 0;
+
+ if (bank >= AB5500_NUM_BANKS)
+ return -EINVAL;
+
+ if (bitmask) {
+ u8 buf;
+
+ err = mutex_lock_interruptible(&ab->access_mutex);
+ if (err)
+ return err;
+
+ if (bitmask == 0xFF) /* No need to read in this case. */
+ buf = bitvalues;
+ else { /* Read and modify the register value. */
+ err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr,
+ reg, &buf, 1);
+ if (err)
+ return err;
+
+ buf = ((~bitmask & buf) | (bitmask & bitvalues));
+ }
+ /* Write the new value. */
+ err = db5500_prcmu_abb_write(bankinfo[bank].slave_addr, reg,
+ &buf, 1);
+
+ mutex_unlock(&ab->access_mutex);
+ }
+ return err;
+}
+
+static int
+set_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, u8 value)
+{
+ return ab5500_mask_and_set_register_interruptible_raw(ab, bank, reg,
+ 0xff, value);
+}
+
+/*
+ * Read/write permission checking functions.
+ */
+static const struct ab5500_i2c_ranges *get_bankref(u8 devid, u8 bank)
+{
+ u8 i;
+
+ if (devid < AB5500_NUM_DEVICES) {
+ for (i = 0; i < ab5500_bank_ranges[devid].nbanks; i++) {
+ if (ab5500_bank_ranges[devid].bank[i].bankid == bank)
+ return &ab5500_bank_ranges[devid].bank[i];
+ }
+ }
+ return NULL;
+}
+
+static bool page_write_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg)
+{
+ u8 i; /* range loop index */
+ const struct ab5500_i2c_ranges *bankref;
+
+ bankref = get_bankref(devid, bank);
+ if (bankref == NULL || last_reg < first_reg)
+ return false;
+
+ for (i = 0; i < bankref->nranges; i++) {
+ if (first_reg < bankref->range[i].first)
+ break;
+ if ((last_reg <= bankref->range[i].last) &&
+ (bankref->range[i].perm & AB5500_PERM_WR))
+ return true;
+ }
+ return false;
+}
+
+static bool reg_write_allowed(u8 devid, u8 bank, u8 reg)
+{
+ return page_write_allowed(devid, bank, reg, reg);
+}
+
+static bool page_read_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg)
+{
+ u8 i;
+ const struct ab5500_i2c_ranges *bankref;
+
+ bankref = get_bankref(devid, bank);
+ if (bankref == NULL || last_reg < first_reg)
+ return false;
+
+
+ /* Find the range (if it exists in the list) that includes first_reg. */
+ for (i = 0; i < bankref->nranges; i++) {
+ if (first_reg < bankref->range[i].first)
+ return false;
+ if (first_reg <= bankref->range[i].last)
+ break;
+ }
+ /* Make sure that the entire range up to and including last_reg is
+ * readable. This may span several of the ranges in the list.
+ */
+ while ((i < bankref->nranges) &&
+ (bankref->range[i].perm & AB5500_PERM_RD)) {
+ if (last_reg <= bankref->range[i].last)
+ return true;
+ if ((++i >= bankref->nranges) ||
+ (bankref->range[i].first !=
+ (bankref->range[i - 1].last + 1))) {
+ break;
+ }
+ }
+ return false;
+}
+
+static bool reg_read_allowed(u8 devid, u8 bank, u8 reg)
+{
+ return page_read_allowed(devid, bank, reg, reg);
+}
+
+
+/*
+ * The exported register access functionality.
+ */
+static int ab5500_get_chip_id(struct device *dev)
+{
+ struct ab5500 *ab = dev_get_drvdata(dev->parent);
+
+ return (int)ab->chip_id;
+}
+
+static int ab5500_mask_and_set_register_interruptible(struct device *dev,
+ u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
+{
+ struct ab5500 *ab;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if ((AB5500_NUM_BANKS <= bank) ||
+ !reg_write_allowed(pdev->id, bank, reg))
+ return -EINVAL;
+
+ ab = dev_get_drvdata(dev->parent);
+ return ab5500_mask_and_set_register_interruptible_raw(ab, bank, reg,
+ bitmask, bitvalues);
+}
+
+static int ab5500_set_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 value)
+{
+ return ab5500_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
+ value);
+}
+
+static int ab5500_get_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 *value)
+{
+ struct ab5500 *ab;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if ((AB5500_NUM_BANKS <= bank) ||
+ !reg_read_allowed(pdev->id, bank, reg))
+ return -EINVAL;
+
+ ab = dev_get_drvdata(dev->parent);
+ return ab5500_get_register_interruptible_raw(ab, bank, reg, value);
+}
+
+static int ab5500_get_register_page_interruptible(struct device *dev, u8 bank,
+ u8 first_reg, u8 *regvals, u8 numregs)
+{
+ struct ab5500 *ab;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if ((AB5500_NUM_BANKS <= bank) ||
+ !page_read_allowed(pdev->id, bank,
+ first_reg, (first_reg + numregs - 1)))
+ return -EINVAL;
+
+ ab = dev_get_drvdata(dev->parent);
+ return get_register_page_interruptible(ab, bank, first_reg, regvals,
+ numregs);
+}
+
+static int
+ab5500_event_registers_startup_state_get(struct device *dev, u8 *event)
+{
+ struct ab5500 *ab;
+
+ ab = dev_get_drvdata(dev->parent);
+ if (!ab->startup_events_read)
+ return -EAGAIN; /* Try again later */
+
+ memcpy(event, ab->startup_events, AB5500_NUM_EVENT_REG);
+ return 0;
+}
+
+static struct abx500_ops ab5500_ops = {
+ .get_chip_id = ab5500_get_chip_id,
+ .get_register = ab5500_get_register_interruptible,
+ .set_register = ab5500_set_register_interruptible,
+ .get_register_page = ab5500_get_register_page_interruptible,
+ .set_register_page = NULL,
+ .mask_and_set_register = ab5500_mask_and_set_register_interruptible,
+ .event_registers_startup_state_get =
+ ab5500_event_registers_startup_state_get,
+ .startup_irq_enabled = NULL,
+};
+
+/*
+ * ab5500_setup : Basic set-up, datastructure creation/destruction
+ * and I2C interface.This sets up a default config
+ * in the AB5500 chip so that it will work as expected.
+ * @ab : Pointer to ab5500 structure
+ * @settings : Pointer to struct abx500_init_settings
+ * @size : Size of init data
+ */
+static int __init ab5500_setup(struct ab5500 *ab,
+ struct abx500_init_settings *settings, unsigned int size)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ err = ab5500_mask_and_set_register_interruptible_raw(ab,
+ settings[i].bank,
+ settings[i].reg,
+ 0xFF, settings[i].setting);
+ if (err)
+ goto exit_no_setup;
+
+ /* If event mask register update the event mask in ab5500 */
+ if ((settings[i].bank == AB5500_BANK_IT) &&
+ (AB5500_MASK_BASE <= settings[i].reg) &&
+ (settings[i].reg <= AB5500_MASK_END)) {
+ ab->mask[settings[i].reg - AB5500_MASK_BASE] =
+ settings[i].setting;
+ }
+ }
+exit_no_setup:
+ return err;
+}
+
+struct ab_family_id {
+ u8 id;
+ char *name;
+};
+
+static const struct ab_family_id ids[] __initdata = {
+ /* AB5500 */
+ {
+ .id = AB5500_1_0,
+ .name = "1.0"
+ },
+ {
+ .id = AB5500_1_1,
+ .name = "1.1"
+ },
+ /* Terminator */
+ {
+ .id = 0x00,
+ }
+};
+
+static int __init ab5500_probe(struct platform_device *pdev)
+{
+ struct ab5500 *ab;
+ struct ab5500_platform_data *ab5500_plf_data =
+ pdev->dev.platform_data;
+ int err;
+ int i;
+
+ ab = kzalloc(sizeof(struct ab5500), GFP_KERNEL);
+ if (!ab) {
+ dev_err(&pdev->dev,
+ "could not allocate ab5500 device\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize data structure */
+ mutex_init(&ab->access_mutex);
+ mutex_init(&ab->irq_lock);
+ ab->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, ab);
+
+ /* Read chip ID register */
+ err = ab5500_get_register_interruptible_raw(ab,
+ AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ AB5500_CHIP_ID, &ab->chip_id);
+ if (err) {
+ dev_err(&pdev->dev, "could not communicate with the analog "
+ "baseband chip\n");
+ goto exit_no_detect;
+ }
+
+ for (i = 0; ids[i].id != 0x0; i++) {
+ if (ids[i].id == ab->chip_id) {
+ snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1,
+ "AB5500 %s", ids[i].name);
+ break;
+ }
+ }
+ if (ids[i].id == 0x0) {
+ dev_err(&pdev->dev, "unknown analog baseband chip id: 0x%x\n",
+ ab->chip_id);
+ dev_err(&pdev->dev, "driver not started!\n");
+ goto exit_no_detect;
+ }
+
+ /* Clear and mask all interrupts */
+ for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) {
+ u8 latchreg = AB5500_IT_LATCH0_REG + i;
+ u8 maskreg = AB5500_IT_MASK0_REG + i;
+ u8 val;
+
+ ab5500_get_register_interruptible_raw(ab, AB5500_BANK_IT,
+ latchreg, &val);
+ set_register_interruptible(ab, AB5500_BANK_IT, maskreg, 0xff);
+ ab->mask[i] = ab->oldmask[i] = 0xff;
+ }
+
+ err = abx500_register_ops(&pdev->dev, &ab5500_ops);
+ if (err) {
+ dev_err(&pdev->dev, "ab5500_register ops error\n");
+ goto exit_no_detect;
+ }
+
+ /* Set up and register the platform devices. */
+ for (i = 0; i < AB5500_NUM_DEVICES; i++) {
+ ab5500_devs[i].platform_data = ab5500_plf_data->dev_data[i];
+ ab5500_devs[i].pdata_size =
+ sizeof(ab5500_plf_data->dev_data[i]);
+ }
+
+ err = mfd_add_devices(&pdev->dev, 0, ab5500_devs,
+ ARRAY_SIZE(ab5500_devs), NULL,
+ ab5500_plf_data->irq.base);
+ if (err) {
+ dev_err(&pdev->dev, "ab5500_mfd_add_device error\n");
+ goto exit_no_detect;
+ }
+
+ err = ab5500_setup(ab, ab5500_plf_data->init_settings,
+ ab5500_plf_data->init_settings_sz);
+ if (err) {
+ dev_err(&pdev->dev, "ab5500_setup error\n");
+ goto exit_no_detect;
+ }
+
+ ab5500_setup_debugfs(ab);
+
+ dev_info(&pdev->dev, "detected AB chip: %s\n", &ab->chip_name[0]);
+ return 0;
+
+exit_no_detect:
+ kfree(ab);
+ return err;
+}
+
+static int __exit ab5500_remove(struct platform_device *pdev)
+{
+ struct ab5500 *ab = platform_get_drvdata(pdev);
+
+ ab5500_remove_debugfs();
+ mfd_remove_devices(&pdev->dev);
+ kfree(ab);
+ return 0;
+}
+
+static struct platform_driver ab5500_driver = {
+ .driver = {
+ .name = "ab5500-core",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(ab5500_remove),
+};
+
+static int __init ab5500_core_init(void)
+{
+ return platform_driver_probe(&ab5500_driver, ab5500_probe);
+}
+
+static void __exit ab5500_core_exit(void)
+{
+ platform_driver_unregister(&ab5500_driver);
+}
+
+subsys_initcall(ab5500_core_init);
+module_exit(ab5500_core_exit);
+
+MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
+MODULE_DESCRIPTION("AB5500 core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/ab5500-core.h b/drivers/mfd/ab5500-core.h
new file mode 100644
index 000000000000..63b30b17e4f3
--- /dev/null
+++ b/drivers/mfd/ab5500-core.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Shared definitions and data structures for the AB5500 MFD driver
+ */
+
+/* Read/write operation values. */
+#define AB5500_PERM_RD (0x01)
+#define AB5500_PERM_WR (0x02)
+
+/* Read/write permissions. */
+#define AB5500_PERM_RO (AB5500_PERM_RD)
+#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR)
+
+#define AB5500_MASK_BASE (0x60)
+#define AB5500_MASK_END (0x79)
+#define AB5500_CHIP_ID (0x20)
+
+/**
+ * struct ab5500_reg_range
+ * @first: the first address of the range
+ * @last: the last address of the range
+ * @perm: access permissions for the range
+ */
+struct ab5500_reg_range {
+ u8 first;
+ u8 last;
+ u8 perm;
+};
+
+/**
+ * struct ab5500_i2c_ranges
+ * @count: the number of ranges in the list
+ * @range: the list of register ranges
+ */
+struct ab5500_i2c_ranges {
+ u8 nranges;
+ u8 bankid;
+ const struct ab5500_reg_range *range;
+};
+
+/**
+ * struct ab5500_i2c_banks
+ * @count: the number of ranges in the list
+ * @range: the list of register ranges
+ */
+struct ab5500_i2c_banks {
+ u8 nbanks;
+ const struct ab5500_i2c_ranges *bank;
+};
+
+/**
+ * struct ab5500_bank
+ * @slave_addr: I2C slave_addr found in AB5500 specification
+ * @name: Documentation name of the bank. For reference
+ */
+struct ab5500_bank {
+ u8 slave_addr;
+ const char *name;
+};
+
+static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = {
+ [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
+ AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"},
+ [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
+ AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"},
+ [AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"},
+ [AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"},
+ [AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"},
+ [AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"},
+ [AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"},
+ [AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"},
+ [AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"},
+ [AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"},
+ [AB5500_BANK_FG_BATTCOM_ACC] = {
+ AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"},
+ [AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"},
+ [AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"},
+ [AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"},
+ [AB5500_BANK_AUDIO_HEADSETUSB] = {
+ AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"},
+};
+
+int ab5500_get_register_interruptible_raw(struct ab5500 *ab, u8 bank, u8 reg,
+ u8 *value);
+int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank,
+ u8 reg, u8 bitmask, u8 bitvalues);
diff --git a/drivers/mfd/ab5500-debugfs.c b/drivers/mfd/ab5500-debugfs.c
new file mode 100644
index 000000000000..43c0ebb81956
--- /dev/null
+++ b/drivers/mfd/ab5500-debugfs.c
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Debugfs support for the AB5500 MFD driver
+ */
+
+#include <linux/export.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mfd/ab5500/ab5500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/uaccess.h>
+
+#include "ab5500-core.h"
+#include "ab5500-debugfs.h"
+
+static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = {
+ [AB5500_BANK_LED] = {
+ .bankid = AB5500_BANK_LED,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_ADC] = {
+ .bankid = AB5500_BANK_ADC,
+ .nranges = 6,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x1F,
+ .last = 0x22,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x23,
+ .last = 0x24,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x26,
+ .last = 0x2D,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x34,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x37,
+ .last = 0x57,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x58,
+ .last = 0x58,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ [AB5500_BANK_RTC] = {
+ .bankid = AB5500_BANK_RTC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x04,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x06,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_STARTUP] = {
+ .bankid = AB5500_BANK_STARTUP,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1F,
+ .last = 0x1F,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x2E,
+ .last = 0x2E,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x30,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x51,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x61,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x66,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8C,
+ .last = 0x96,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xAA,
+ .last = 0xB4,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xB7,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xCA,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xD3,
+ .last = 0xE0,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_DBI_ECI] = {
+ .bankid = AB5500_BANK_DBI_ECI,
+ .nranges = 3,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x07,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x10,
+ .last = 0x10,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x13,
+ .last = 0x13,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_CHG] = {
+ .bankid = AB5500_BANK_CHG,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x11,
+ .last = 0x11,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x12,
+ .last = 0x1B,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_FG_BATTCOM_ACC] = {
+ .bankid = AB5500_BANK_FG_BATTCOM_ACC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0C,
+ .last = 0x10,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_USB] = {
+ .bankid = AB5500_BANK_USB,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x83,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x87,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8B,
+ .last = 0x8B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x91,
+ .last = 0x92,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x93,
+ .last = 0x93,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x94,
+ .last = 0x94,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xA8,
+ .last = 0xB0,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB2,
+ .last = 0xB2,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB4,
+ .last = 0xBC,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xBF,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xC5,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ [AB5500_BANK_IT] = {
+ .bankid = AB5500_BANK_IT,
+ .nranges = 4,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x02,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x20,
+ .last = 0x36,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x40,
+ .last = 0x56,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x60,
+ .last = 0x76,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
+ .bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST,
+ .nranges = 7,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x02,
+ .last = 0x02,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x12,
+ .last = 0x12,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x30,
+ .last = 0x34,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x40,
+ .last = 0x44,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x54,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x64,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x70,
+ .last = 0x74,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
+ .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ .nranges = 13,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x02,
+ .last = 0x02,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0D,
+ .last = 0x0F,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1C,
+ .last = 0x1C,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1E,
+ .last = 0x1E,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x20,
+ .last = 0x21,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x25,
+ .last = 0x25,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x28,
+ .last = 0x2A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x30,
+ .last = 0x33,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x40,
+ .last = 0x43,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x53,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x63,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x70,
+ .last = 0x73,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_VIBRA] = {
+ .bankid = AB5500_BANK_VIBRA,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x10,
+ .last = 0x13,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xFE,
+ .last = 0xFE,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_AUDIO_HEADSETUSB] = {
+ .bankid = AB5500_BANK_AUDIO_HEADSETUSB,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x48,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xEB,
+ .last = 0xFB,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_SIM_USBSIM] = {
+ .bankid = AB5500_BANK_SIM_USBSIM,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x13,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_VDENC] = {
+ .bankid = AB5500_BANK_VDENC,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x08,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x09,
+ .last = 0x09,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0A,
+ .last = 0x12,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x15,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1B,
+ .last = 0x21,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x27,
+ .last = 0x2C,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x41,
+ .last = 0x41,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x45,
+ .last = 0x5B,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x5D,
+ .last = 0x5D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x69,
+ .last = 0x69,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x6C,
+ .last = 0x6D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+};
+
+static int ab5500_registers_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+ unsigned int i;
+ u8 bank = (u8)ab->debug_bank;
+
+ seq_printf(s, "ab5500 register values:\n");
+ for (bank = 0; bank < AB5500_NUM_BANKS; bank++) {
+ seq_printf(s, " bank %u, %s (0x%x):\n", bank,
+ bankinfo[bank].name,
+ bankinfo[bank].slave_addr);
+ for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) {
+ u8 reg;
+ int err;
+
+ for (reg = ab5500_reg_ranges[bank].range[i].first;
+ reg <= ab5500_reg_ranges[bank].range[i].last;
+ reg++) {
+ u8 value;
+
+ err = ab5500_get_register_interruptible_raw(ab,
+ bank, reg,
+ &value);
+ if (err < 0) {
+ dev_err(ab->dev, "get_reg failed %d"
+ "bank 0x%x reg 0x%x\n",
+ err, bank, reg);
+ return err;
+ }
+
+ err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n",
+ bank, reg, value);
+ if (err < 0) {
+ dev_err(ab->dev,
+ "seq_printf overflow\n");
+ /*
+ * Error is not returned here since
+ * the output is wanted in any case
+ */
+ return 0;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int ab5500_registers_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_registers_print, inode->i_private);
+}
+
+static const struct file_operations ab5500_registers_fops = {
+ .open = ab5500_registers_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab5500_bank_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+
+ seq_printf(s, "%d\n", ab->debug_bank);
+ return 0;
+}
+
+static int ab5500_bank_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_bank_print, inode->i_private);
+}
+
+static ssize_t ab5500_bank_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_bank;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_bank);
+ if (err)
+ return -EINVAL;
+
+ if (user_bank >= AB5500_NUM_BANKS) {
+ dev_err(ab->dev,
+ "debugfs error input > number of banks\n");
+ return -EINVAL;
+ }
+
+ ab->debug_bank = user_bank;
+
+ return buf_size;
+}
+
+static int ab5500_address_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+
+ seq_printf(s, "0x%02X\n", ab->debug_address);
+ return 0;
+}
+
+static int ab5500_address_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_address_print, inode->i_private);
+}
+
+static ssize_t ab5500_address_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_address;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_address);
+ if (err)
+ return -EINVAL;
+ if (user_address > 0xff) {
+ dev_err(ab->dev,
+ "debugfs error input > 0xff\n");
+ return -EINVAL;
+ }
+ ab->debug_address = user_address;
+ return buf_size;
+}
+
+static int ab5500_val_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+ int err;
+ u8 regvalue;
+
+ err = ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank,
+ (u8)ab->debug_address, &regvalue);
+ if (err) {
+ dev_err(ab->dev, "get_reg failed %d, bank 0x%x"
+ ", reg 0x%x\n", err, ab->debug_bank,
+ ab->debug_address);
+ return -EINVAL;
+ }
+ seq_printf(s, "0x%02X\n", regvalue);
+
+ return 0;
+}
+
+static int ab5500_val_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_val_print, inode->i_private);
+}
+
+static ssize_t ab5500_val_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+ u8 regvalue;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val > 0xff) {
+ dev_err(ab->dev,
+ "debugfs error input > 0xff\n");
+ return -EINVAL;
+ }
+ err = ab5500_mask_and_set_register_interruptible_raw(
+ ab, (u8)ab->debug_bank,
+ (u8)ab->debug_address, 0xFF, (u8)user_val);
+ if (err)
+ return -EINVAL;
+
+ ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank,
+ (u8)ab->debug_address, &regvalue);
+ if (err)
+ return -EINVAL;
+
+ return buf_size;
+}
+
+static const struct file_operations ab5500_bank_fops = {
+ .open = ab5500_bank_open,
+ .write = ab5500_bank_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab5500_address_fops = {
+ .open = ab5500_address_open,
+ .write = ab5500_address_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab5500_val_fops = {
+ .open = ab5500_val_open,
+ .write = ab5500_val_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static struct dentry *ab5500_dir;
+static struct dentry *ab5500_reg_file;
+static struct dentry *ab5500_bank_file;
+static struct dentry *ab5500_address_file;
+static struct dentry *ab5500_val_file;
+
+void __init ab5500_setup_debugfs(struct ab5500 *ab)
+{
+ ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP;
+ ab->debug_address = AB5500_CHIP_ID;
+
+ ab5500_dir = debugfs_create_dir("ab5500", NULL);
+ if (!ab5500_dir)
+ goto exit_no_debugfs;
+
+ ab5500_reg_file = debugfs_create_file("all-bank-registers",
+ S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops);
+ if (!ab5500_reg_file)
+ goto exit_destroy_dir;
+
+ ab5500_bank_file = debugfs_create_file("register-bank",
+ (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops);
+ if (!ab5500_bank_file)
+ goto exit_destroy_reg;
+
+ ab5500_address_file = debugfs_create_file("register-address",
+ (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops);
+ if (!ab5500_address_file)
+ goto exit_destroy_bank;
+
+ ab5500_val_file = debugfs_create_file("register-value",
+ (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops);
+ if (!ab5500_val_file)
+ goto exit_destroy_address;
+
+ return;
+
+exit_destroy_address:
+ debugfs_remove(ab5500_address_file);
+exit_destroy_bank:
+ debugfs_remove(ab5500_bank_file);
+exit_destroy_reg:
+ debugfs_remove(ab5500_reg_file);
+exit_destroy_dir:
+ debugfs_remove(ab5500_dir);
+exit_no_debugfs:
+ dev_err(ab->dev, "failed to create debugfs entries.\n");
+ return;
+}
+
+void __exit ab5500_remove_debugfs(void)
+{
+ debugfs_remove(ab5500_val_file);
+ debugfs_remove(ab5500_address_file);
+ debugfs_remove(ab5500_bank_file);
+ debugfs_remove(ab5500_reg_file);
+ debugfs_remove(ab5500_dir);
+}
diff --git a/drivers/mfd/ab5500-debugfs.h b/drivers/mfd/ab5500-debugfs.h
new file mode 100644
index 000000000000..7330a9b6afa6
--- /dev/null
+++ b/drivers/mfd/ab5500-debugfs.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Debugfs interface to the AB5500 core driver
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+void ab5500_setup_debugfs(struct ab5500 *ab);
+void ab5500_remove_debugfs(void);
+
+#else /* !CONFIG_DEBUG_FS */
+
+static inline void ab5500_setup_debugfs(struct ab5500 *ab)
+{
+}
+
+static inline void ab5500_remove_debugfs(void)
+{
+}
+
+#endif
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index fc0c1af1566e..1e9173804ede 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -92,6 +92,8 @@
#define AB8500_REV_REG 0x80
#define AB8500_SWITCH_OFF_STATUS 0x00
+#define AB8500_TURN_ON_STATUS 0x00
+
/*
* Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
* numbers are indexed into this array with (num / 8).
@@ -293,6 +295,7 @@ static struct irq_chip ab8500_irq_chip = {
.irq_bus_lock = ab8500_irq_lock,
.irq_bus_sync_unlock = ab8500_irq_sync_unlock,
.irq_mask = ab8500_irq_mask,
+ .irq_disable = ab8500_irq_mask,
.irq_unmask = ab8500_irq_unmask,
};
@@ -363,7 +366,7 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
}
}
-static struct resource ab8500_gpio_resources[] = {
+static struct resource __devinitdata ab8500_gpio_resources[] = {
{
.name = "GPIO_INT6",
.start = AB8500_INT_GPIO6R,
@@ -372,7 +375,7 @@ static struct resource ab8500_gpio_resources[] = {
}
};
-static struct resource ab8500_gpadc_resources[] = {
+static struct resource __devinitdata ab8500_gpadc_resources[] = {
{
.name = "HW_CONV_END",
.start = AB8500_INT_GP_HW_ADC_CONV_END,
@@ -387,7 +390,7 @@ static struct resource ab8500_gpadc_resources[] = {
},
};
-static struct resource ab8500_rtc_resources[] = {
+static struct resource __devinitdata ab8500_rtc_resources[] = {
{
.name = "60S",
.start = AB8500_INT_RTC_60S,
@@ -402,7 +405,7 @@ static struct resource ab8500_rtc_resources[] = {
},
};
-static struct resource ab8500_poweronkey_db_resources[] = {
+static struct resource __devinitdata ab8500_poweronkey_db_resources[] = {
{
.name = "ONKEY_DBF",
.start = AB8500_INT_PON_KEY1DB_F,
@@ -417,20 +420,47 @@ static struct resource ab8500_poweronkey_db_resources[] = {
},
};
-static struct resource ab8500_bm_resources[] = {
+static struct resource __devinitdata ab8500_av_acc_detect_resources[] = {
{
- .name = "MAIN_EXT_CH_NOT_OK",
- .start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
- .end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
- .flags = IORESOURCE_IRQ,
+ .name = "ACC_DETECT_1DB_F",
+ .start = AB8500_INT_ACC_DETECT_1DB_F,
+ .end = AB8500_INT_ACC_DETECT_1DB_F,
+ .flags = IORESOURCE_IRQ,
},
{
- .name = "BATT_OVV",
- .start = AB8500_INT_BATT_OVV,
- .end = AB8500_INT_BATT_OVV,
- .flags = IORESOURCE_IRQ,
+ .name = "ACC_DETECT_1DB_R",
+ .start = AB8500_INT_ACC_DETECT_1DB_R,
+ .end = AB8500_INT_ACC_DETECT_1DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_21DB_F",
+ .start = AB8500_INT_ACC_DETECT_21DB_F,
+ .end = AB8500_INT_ACC_DETECT_21DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_21DB_R",
+ .start = AB8500_INT_ACC_DETECT_21DB_R,
+ .end = AB8500_INT_ACC_DETECT_21DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_22DB_F",
+ .start = AB8500_INT_ACC_DETECT_22DB_F,
+ .end = AB8500_INT_ACC_DETECT_22DB_F,
+ .flags = IORESOURCE_IRQ,
},
{
+ .name = "ACC_DETECT_22DB_R",
+ .start = AB8500_INT_ACC_DETECT_22DB_R,
+ .end = AB8500_INT_ACC_DETECT_22DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_charger_resources[] = {
+ {
.name = "MAIN_CH_UNPLUG_DET",
.start = AB8500_INT_MAIN_CH_UNPLUG_DET,
.end = AB8500_INT_MAIN_CH_UNPLUG_DET,
@@ -443,27 +473,27 @@ static struct resource ab8500_bm_resources[] = {
.flags = IORESOURCE_IRQ,
},
{
- .name = "VBUS_DET_F",
- .start = AB8500_INT_VBUS_DET_F,
- .end = AB8500_INT_VBUS_DET_F,
- .flags = IORESOURCE_IRQ,
- },
- {
.name = "VBUS_DET_R",
.start = AB8500_INT_VBUS_DET_R,
.end = AB8500_INT_VBUS_DET_R,
.flags = IORESOURCE_IRQ,
},
{
- .name = "BAT_CTRL_INDB",
- .start = AB8500_INT_BAT_CTRL_INDB,
- .end = AB8500_INT_BAT_CTRL_INDB,
+ .name = "VBUS_DET_F",
+ .start = AB8500_INT_VBUS_DET_F,
+ .end = AB8500_INT_VBUS_DET_F,
.flags = IORESOURCE_IRQ,
},
{
- .name = "CH_WD_EXP",
- .start = AB8500_INT_CH_WD_EXP,
- .end = AB8500_INT_CH_WD_EXP,
+ .name = "USB_LINK_STATUS",
+ .start = AB8500_INT_USB_LINK_STATUS,
+ .end = AB8500_INT_USB_LINK_STATUS,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CHARGE_DET_DONE",
+ .start = AB8500_INT_USB_CHG_DET_DONE,
+ .end = AB8500_INT_USB_CHG_DET_DONE,
.flags = IORESOURCE_IRQ,
},
{
@@ -473,21 +503,60 @@ static struct resource ab8500_bm_resources[] = {
.flags = IORESOURCE_IRQ,
},
{
- .name = "NCONV_ACCU",
- .start = AB8500_INT_CCN_CONV_ACC,
- .end = AB8500_INT_CCN_CONV_ACC,
+ .name = "USB_CH_TH_PROT_R",
+ .start = AB8500_INT_USB_CH_TH_PROT_R,
+ .end = AB8500_INT_USB_CH_TH_PROT_R,
.flags = IORESOURCE_IRQ,
},
{
- .name = "LOW_BAT_F",
- .start = AB8500_INT_LOW_BAT_F,
- .end = AB8500_INT_LOW_BAT_F,
+ .name = "USB_CH_TH_PROT_F",
+ .start = AB8500_INT_USB_CH_TH_PROT_F,
+ .end = AB8500_INT_USB_CH_TH_PROT_F,
.flags = IORESOURCE_IRQ,
},
{
- .name = "LOW_BAT_R",
- .start = AB8500_INT_LOW_BAT_R,
- .end = AB8500_INT_LOW_BAT_R,
+ .name = "MAIN_EXT_CH_NOT_OK",
+ .start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+ .end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "MAIN_CH_TH_PROT_R",
+ .start = AB8500_INT_MAIN_CH_TH_PROT_R,
+ .end = AB8500_INT_MAIN_CH_TH_PROT_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "MAIN_CH_TH_PROT_F",
+ .start = AB8500_INT_MAIN_CH_TH_PROT_F,
+ .end = AB8500_INT_MAIN_CH_TH_PROT_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CHARGER_NOT_OKR",
+ .start = AB8500_INT_USB_CHARGER_NOT_OK,
+ .end = AB8500_INT_USB_CHARGER_NOT_OK,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CHARGER_NOT_OKF",
+ .start = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .end = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "CH_WD_EXP",
+ .start = AB8500_INT_CH_WD_EXP,
+ .end = AB8500_INT_CH_WD_EXP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_btemp_resources[] = {
+ {
+ .name = "BAT_CTRL_INDB",
+ .start = AB8500_INT_BAT_CTRL_INDB,
+ .end = AB8500_INT_BAT_CTRL_INDB,
.flags = IORESOURCE_IRQ,
},
{
@@ -503,38 +572,55 @@ static struct resource ab8500_bm_resources[] = {
.flags = IORESOURCE_IRQ,
},
{
- .name = "USB_CHARGER_NOT_OKR",
- .start = AB8500_INT_USB_CHARGER_NOT_OK,
- .end = AB8500_INT_USB_CHARGER_NOT_OK,
+ .name = "BTEMP_LOW_MEDIUM",
+ .start = AB8500_INT_BTEMP_LOW_MEDIUM,
+ .end = AB8500_INT_BTEMP_LOW_MEDIUM,
.flags = IORESOURCE_IRQ,
},
{
- .name = "USB_CHARGE_DET_DONE",
- .start = AB8500_INT_USB_CHG_DET_DONE,
- .end = AB8500_INT_USB_CHG_DET_DONE,
+ .name = "BTEMP_MEDIUM_HIGH",
+ .start = AB8500_INT_BTEMP_MEDIUM_HIGH,
+ .end = AB8500_INT_BTEMP_MEDIUM_HIGH,
.flags = IORESOURCE_IRQ,
},
+};
+
+static struct resource __devinitdata ab8500_fg_resources[] = {
{
- .name = "USB_CH_TH_PROT_R",
- .start = AB8500_INT_USB_CH_TH_PROT_R,
- .end = AB8500_INT_USB_CH_TH_PROT_R,
+ .name = "NCONV_ACCU",
+ .start = AB8500_INT_CCN_CONV_ACC,
+ .end = AB8500_INT_CCN_CONV_ACC,
.flags = IORESOURCE_IRQ,
},
{
- .name = "MAIN_CH_TH_PROT_R",
- .start = AB8500_INT_MAIN_CH_TH_PROT_R,
- .end = AB8500_INT_MAIN_CH_TH_PROT_R,
+ .name = "BATT_OVV",
+ .start = AB8500_INT_BATT_OVV,
+ .end = AB8500_INT_BATT_OVV,
.flags = IORESOURCE_IRQ,
},
{
- .name = "USB_CHARGER_NOT_OKF",
- .start = AB8500_INT_USB_CHARGER_NOT_OKF,
- .end = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .name = "LOW_BAT_F",
+ .start = AB8500_INT_LOW_BAT_F,
+ .end = AB8500_INT_LOW_BAT_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "LOW_BAT_R",
+ .start = AB8500_INT_LOW_BAT_R,
+ .end = AB8500_INT_LOW_BAT_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "CC_INT_CALIB",
+ .start = AB8500_INT_CC_INT_CALIB,
+ .end = AB8500_INT_CC_INT_CALIB,
.flags = IORESOURCE_IRQ,
},
};
-static struct resource ab8500_debug_resources[] = {
+static struct resource __devinitdata ab8500_chargalg_resources[] = {};
+
+static struct resource __devinitdata ab8500_debug_resources[] = {
{
.name = "IRQ_FIRST",
.start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
@@ -549,7 +635,7 @@ static struct resource ab8500_debug_resources[] = {
},
};
-static struct resource ab8500_usb_resources[] = {
+static struct resource __devinitdata ab8500_usb_resources[] = {
{
.name = "ID_WAKEUP_R",
.start = AB8500_INT_ID_WAKEUP_R,
@@ -580,9 +666,21 @@ static struct resource ab8500_usb_resources[] = {
.end = AB8500_INT_USB_LINK_STATUS,
.flags = IORESOURCE_IRQ,
},
+ {
+ .name = "USB_ADP_PROBE_PLUG",
+ .start = AB8500_INT_ADP_PROBE_PLUG,
+ .end = AB8500_INT_ADP_PROBE_PLUG,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_ADP_PROBE_UNPLUG",
+ .start = AB8500_INT_ADP_PROBE_UNPLUG,
+ .end = AB8500_INT_ADP_PROBE_UNPLUG,
+ .flags = IORESOURCE_IRQ,
+ },
};
-static struct resource ab8500_temp_resources[] = {
+static struct resource __devinitdata ab8500_temp_resources[] = {
{
.name = "AB8500_TEMP_WARM",
.start = AB8500_INT_TEMP_WARM,
@@ -591,7 +689,7 @@ static struct resource ab8500_temp_resources[] = {
},
};
-static struct mfd_cell ab8500_devs[] = {
+static struct mfd_cell __devinitdata ab8500_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
@@ -621,11 +719,33 @@ static struct mfd_cell ab8500_devs[] = {
.resources = ab8500_rtc_resources,
},
{
- .name = "ab8500-bm",
- .num_resources = ARRAY_SIZE(ab8500_bm_resources),
- .resources = ab8500_bm_resources,
+ .name = "ab8500-charger",
+ .num_resources = ARRAY_SIZE(ab8500_charger_resources),
+ .resources = ab8500_charger_resources,
+ },
+ {
+ .name = "ab8500-btemp",
+ .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
+ .resources = ab8500_btemp_resources,
+ },
+ {
+ .name = "ab8500-fg",
+ .num_resources = ARRAY_SIZE(ab8500_fg_resources),
+ .resources = ab8500_fg_resources,
+ },
+ {
+ .name = "ab8500-chargalg",
+ .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
+ .resources = ab8500_chargalg_resources,
+ },
+ {
+ .name = "ab8500-acc-det",
+ .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+ .resources = ab8500_av_acc_detect_resources,
+ },
+ {
+ .name = "ab8500-codec",
},
- { .name = "ab8500-codec", },
{
.name = "ab8500-usb",
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
@@ -694,12 +814,40 @@ static ssize_t show_switch_off_status(struct device *dev,
return sprintf(buf, "%#x\n", value);
}
+/*
+ * ab8500 has turned on due to (TURN_ON_STATUS):
+ * 0x01 PORnVbat
+ * 0x02 PonKey1dbF
+ * 0x04 PonKey2dbF
+ * 0x08 RTCAlarm
+ * 0x10 MainChDet
+ * 0x20 VbusDet
+ * 0x40 UsbIDDetect
+ * 0x80 Reserved
+ */
+static ssize_t show_turn_on_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ u8 value;
+ struct ab8500 *ab8500;
+
+ ab8500 = dev_get_drvdata(dev);
+ ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
+ AB8500_TURN_ON_STATUS, &value);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%#x\n", value);
+}
+
static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
+static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
static struct attribute *ab8500_sysfs_entries[] = {
&dev_attr_chip_id.attr,
&dev_attr_switch_off_status.attr,
+ &dev_attr_turn_on_status.attr,
NULL,
};
@@ -726,11 +874,11 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
return ret;
switch (value) {
- case AB8500_CUTEARLY:
case AB8500_CUT1P0:
case AB8500_CUT1P1:
case AB8500_CUT2P0:
case AB8500_CUT3P0:
+ case AB8500_CUT3P3:
dev_info(ab8500->dev, "detected chip, revision: %#x\n", value);
break;
default:
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 64748e42ac03..dedb7f65cea6 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -8,6 +8,7 @@
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
+#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
@@ -419,20 +420,13 @@ static ssize_t ab8500_bank_write(struct file *file,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
unsigned long user_bank;
int err;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_bank);
+ err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
if (err)
- return -EINVAL;
+ return err;
if (user_bank >= AB8500_NUM_BANKS) {
dev_err(dev, "debugfs error input > number of banks\n");
@@ -441,7 +435,7 @@ static ssize_t ab8500_bank_write(struct file *file,
debug_bank = user_bank;
- return buf_size;
+ return count;
}
static int ab8500_address_print(struct seq_file *s, void *p)
@@ -459,26 +453,20 @@ static ssize_t ab8500_address_write(struct file *file,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
unsigned long user_address;
int err;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_address);
+ err = kstrtoul_from_user(user_buf, count, 0, &user_address);
if (err)
- return -EINVAL;
+ return err;
+
if (user_address > 0xff) {
dev_err(dev, "debugfs error input > 0xff\n");
return -EINVAL;
}
debug_address = user_address;
- return buf_size;
+ return count;
}
static int ab8500_val_print(struct seq_file *s, void *p)
@@ -509,20 +497,14 @@ static ssize_t ab8500_val_write(struct file *file,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
unsigned long user_val;
int err;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_val);
+ err = kstrtoul_from_user(user_buf, count, 0, &user_val);
if (err)
- return -EINVAL;
+ return err;
+
if (user_val > 0xff) {
dev_err(dev, "debugfs error input > 0xff\n");
return -EINVAL;
@@ -534,7 +516,7 @@ static ssize_t ab8500_val_write(struct file *file,
return -EINVAL;
}
- return buf_size;
+ return count;
}
static const struct file_operations ab8500_bank_fops = {
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index f16afb234ff9..e985d1701a83 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -143,12 +143,15 @@ struct ab8500_gpadc *ab8500_gpadc_get(char *name)
}
EXPORT_SYMBOL(ab8500_gpadc_get);
-static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input,
+/**
+ * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage
+ */
+int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
int ad_value)
{
int res;
- switch (input) {
+ switch (channel) {
case MAIN_CHARGER_V:
/* For some reason we don't have calibrated data */
if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) {
@@ -232,18 +235,46 @@ static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input,
}
return res;
}
+EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage);
/**
* ab8500_gpadc_convert() - gpadc conversion
- * @input: analog input to be converted to digital data
+ * @channel: analog channel to be converted to digital data
*
* This function converts the selected analog i/p to digital
* data.
*/
-int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
+int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
+{
+ int ad_value;
+ int voltage;
+
+ ad_value = ab8500_gpadc_read_raw(gpadc, channel);
+ if (ad_value < 0) {
+ dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel);
+ return ad_value;
+ }
+
+ voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
+
+ if (voltage < 0)
+ dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
+ " %d AD: 0x%x\n", channel, ad_value);
+
+ return voltage;
+}
+EXPORT_SYMBOL(ab8500_gpadc_convert);
+
+/**
+ * ab8500_gpadc_read_raw() - gpadc read
+ * @channel: analog channel to be read
+ *
+ * This function obtains the raw ADC value, this then needs
+ * to be converted by calling ab8500_gpadc_ad_to_voltage()
+ */
+int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
{
int ret;
- u16 data = 0;
int looplimit = 0;
u8 val, low_data, high_data;
@@ -278,9 +309,9 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
goto out;
}
- /* Select the input source and set average samples to 16 */
+ /* Select the channel source and set average samples to 16 */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
- AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16));
+ AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16));
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: set avg samples failed\n");
@@ -292,7 +323,7 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
* charging current sense if it needed, ABB 3.0 needs some special
* treatment too.
*/
- switch (input) {
+ switch (channel) {
case MAIN_CHARGER_C:
case USB_CHARGER_C:
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
@@ -359,7 +390,6 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
goto out;
}
- data = (high_data << 8) | low_data;
/* Disable GPADC */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
@@ -370,8 +400,8 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
/* Disable VTVout LDO this is required for GPADC */
regulator_disable(gpadc->regu);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
- ret = ab8500_gpadc_ad_to_voltage(gpadc, input, data);
- return ret;
+
+ return (high_data << 8) | low_data;
out:
/*
@@ -385,10 +415,10 @@ out:
regulator_disable(gpadc->regu);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
dev_err(gpadc->dev,
- "gpadc_conversion: Failed to AD convert channel %d\n", input);
+ "gpadc_conversion: Failed to AD convert channel %d\n", channel);
return ret;
}
-EXPORT_SYMBOL(ab8500_gpadc_convert);
+EXPORT_SYMBOL(ab8500_gpadc_read_raw);
/**
* ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c
index 392185965b39..f20feefac190 100644
--- a/drivers/mfd/ab8500-sysctrl.c
+++ b/drivers/mfd/ab8500-sysctrl.c
@@ -5,6 +5,7 @@
*/
#include <linux/err.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/ab8500.h>
#include <linux/mfd/abx500.h>
diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c
index f12720dbe126..7ce65f49480f 100644
--- a/drivers/mfd/abx500-core.c
+++ b/drivers/mfd/abx500-core.c
@@ -8,6 +8,7 @@
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/module.h>
#include <linux/mfd/abx500.h>
static LIST_HEAD(abx500_list);
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index c71ae09430c5..b85bbd7f0d19 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/gpio.h>
+#include <linux/export.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -584,7 +585,7 @@ static int asic3_gpio_remove(struct platform_device *pdev)
return gpiochip_remove(&asic->gpio);
}
-static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
+static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
{
unsigned long flags;
u32 cdex;
@@ -596,8 +597,6 @@ static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
}
spin_unlock_irqrestore(&asic->lock, flags);
-
- return 0;
}
static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk)
@@ -779,6 +778,8 @@ static struct mfd_cell asic3_cell_mmc = {
.name = "tmio-mmc",
.enable = asic3_mmc_enable,
.disable = asic3_mmc_disable,
+ .suspend = asic3_mmc_disable,
+ .resume = asic3_mmc_enable,
.platform_data = &asic3_mmc_data,
.pdata_size = sizeof(asic3_mmc_data),
.num_resources = ARRAY_SIZE(asic3_mmc_resources),
@@ -811,24 +812,43 @@ static int asic3_leds_disable(struct platform_device *pdev)
return 0;
}
+static int asic3_leds_suspend(struct platform_device *pdev)
+{
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+ struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+ while (asic3_gpio_get(&asic->gpio, ASIC3_GPIO(C, cell->id)) != 0)
+ msleep(1);
+
+ asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]);
+
+ return 0;
+}
+
static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = {
[0] = {
.name = "leds-asic3",
.id = 0,
.enable = asic3_leds_enable,
.disable = asic3_leds_disable,
+ .suspend = asic3_leds_suspend,
+ .resume = asic3_leds_enable,
},
[1] = {
.name = "leds-asic3",
.id = 1,
.enable = asic3_leds_enable,
.disable = asic3_leds_disable,
+ .suspend = asic3_leds_suspend,
+ .resume = asic3_leds_enable,
},
[2] = {
.name = "leds-asic3",
.id = 2,
.enable = asic3_leds_enable,
.disable = asic3_leds_disable,
+ .suspend = asic3_leds_suspend,
+ .resume = asic3_leds_enable,
},
};
@@ -949,6 +969,7 @@ static int __init asic3_probe(struct platform_device *pdev)
goto out_unmap;
}
+ asic->gpio.label = "asic3";
asic->gpio.base = pdata->gpio_base;
asic->gpio.ngpio = ASIC3_NUM_GPIOS;
asic->gpio.get = asic3_gpio_get;
diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c
index 2fadbaeb1cb1..1b79c37fd599 100644
--- a/drivers/mfd/da903x.c
+++ b/drivers/mfd/da903x.c
@@ -523,7 +523,7 @@ static int __devinit da903x_probe(struct i2c_client *client,
chip->ops->read_events(chip, &tmp);
ret = request_irq(client->irq, da903x_irq_handler,
- IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_FALLING,
"da903x", chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c
index 9dbb3cab4a6f..bb115b2f04e9 100644
--- a/drivers/mfd/db5500-prcmu.c
+++ b/drivers/mfd/db5500-prcmu.c
@@ -20,11 +20,11 @@
#include <linux/jiffies.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
-#include <linux/mfd/db5500-prcmu.h>
+#include <linux/mfd/dbx500-prcmu.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db5500-regs.h>
-#include "db5500-prcmu-regs.h"
+#include "dbx500-prcmu-regs.h"
#define _PRCM_MB_HEADER (tcdm_base + 0xFE8)
#define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0)
@@ -109,15 +109,18 @@ enum mb5_header {
#define PRCMU_DSI_CLOCK_SETTING 0x00000128
/* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */
#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000135
-#define PRCMU_PLLDSI_FREQ_SETTING 0x0004013C
+#define PRCMU_PLLDSI_FREQ_SETTING 0x00020121
#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002
-#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000101
+#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000201
#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00000101
#define PRCMU_ENABLE_PLLDSI 0x00000001
#define PRCMU_DISABLE_PLLDSI 0x00000000
#define PRCMU_DSI_RESET_SW 0x00000003
+#define PRCMU_RESOUTN0_PIN 0x00000001
+#define PRCMU_RESOUTN1_PIN 0x00000002
+#define PRCMU_RESOUTN2_PIN 0x00000004
#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
@@ -315,31 +318,31 @@ static bool read_mailbox_0(void)
r = false;
break;
}
- writel(MBOX_BIT(0), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR);
return r;
}
static bool read_mailbox_1(void)
{
- writel(MBOX_BIT(1), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_2(void)
{
- writel(MBOX_BIT(2), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_3(void)
{
- writel(MBOX_BIT(3), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_4(void)
{
- writel(MBOX_BIT(4), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR);
return false;
}
@@ -360,19 +363,19 @@ static bool read_mailbox_5(void)
print_unknown_header_warning(5, header);
break;
}
- writel(MBOX_BIT(5), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_6(void)
{
- writel(MBOX_BIT(6), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_7(void)
{
- writel(MBOX_BIT(7), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR);
return false;
}
@@ -434,7 +437,7 @@ int __init db5500_prcmu_init(void)
return -ENODEV;
/* Clean up the mailbox interrupts after pre-kernel code. */
- writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLEAR);
+ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler,
prcmu_irq_thread_fn, 0, "prcmu", NULL);
diff --git a/drivers/mfd/db8500-prcmu-regs.h b/drivers/mfd/db8500-prcmu-regs.h
deleted file mode 100644
index 3bbf04d58043..000000000000
--- a/drivers/mfd/db8500-prcmu-regs.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) STMicroelectronics 2009
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
- * Author: Sundar Iyer <sundar.iyer@stericsson.com>
- *
- * License Terms: GNU General Public License v2
- *
- * PRCM Unit registers
- */
-#ifndef __DB8500_PRCMU_REGS_H
-#define __DB8500_PRCMU_REGS_H
-
-#include <linux/bitops.h>
-#include <mach/hardware.h>
-
-#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
-
-#define PRCM_ARM_PLLDIVPS 0x118
-#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE BITS(0, 5)
-#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xF
-
-#define PRCM_PLLARM_LOCKP 0x0A8
-#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 BIT(1)
-
-#define PRCM_ARM_CHGCLKREQ 0x114
-#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ BIT(0)
-
-#define PRCM_PLLARM_ENABLE 0x98
-#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE BIT(0)
-#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON BIT(8)
-
-#define PRCM_ARMCLKFIX_MGT 0x0
-#define PRCM_A9_RESETN_CLR 0x1f4
-#define PRCM_A9_RESETN_SET 0x1f0
-#define PRCM_ARM_LS_CLAMP 0x30C
-#define PRCM_SRAM_A9 0x308
-
-/* ARM WFI Standby signal register */
-#define PRCM_ARM_WFI_STANDBY 0x130
-#define PRCM_IOCR 0x310
-#define PRCM_IOCR_IOFORCE BIT(0)
-
-/* CPU mailbox registers */
-#define PRCM_MBOX_CPU_VAL 0x0FC
-#define PRCM_MBOX_CPU_SET 0x100
-
-/* Dual A9 core interrupt management unit registers */
-#define PRCM_A9_MASK_REQ 0x328
-#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ BIT(0)
-
-#define PRCM_A9_MASK_ACK 0x32C
-#define PRCM_ARMITMSK31TO0 0x11C
-#define PRCM_ARMITMSK63TO32 0x120
-#define PRCM_ARMITMSK95TO64 0x124
-#define PRCM_ARMITMSK127TO96 0x128
-#define PRCM_POWER_STATE_VAL 0x25C
-#define PRCM_ARMITVAL31TO0 0x260
-#define PRCM_ARMITVAL63TO32 0x264
-#define PRCM_ARMITVAL95TO64 0x268
-#define PRCM_ARMITVAL127TO96 0x26C
-
-#define PRCM_HOSTACCESS_REQ 0x334
-#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ BIT(0)
-
-#define PRCM_ARM_IT1_CLR 0x48C
-#define PRCM_ARM_IT1_VAL 0x494
-
-#define PRCM_ITSTATUS0 0x148
-#define PRCM_ITSTATUS1 0x150
-#define PRCM_ITSTATUS2 0x158
-#define PRCM_ITSTATUS3 0x160
-#define PRCM_ITSTATUS4 0x168
-#define PRCM_ITSTATUS5 0x484
-#define PRCM_ITCLEAR5 0x488
-#define PRCM_ARMIT_MASKXP70_IT 0x1018
-
-/* System reset register */
-#define PRCM_APE_SOFTRST 0x228
-
-/* Level shifter and clamp control registers */
-#define PRCM_MMIP_LS_CLAMP_SET 0x420
-#define PRCM_MMIP_LS_CLAMP_CLR 0x424
-
-/* PRCMU HW semaphore */
-#define PRCM_SEM 0x400
-#define PRCM_SEM_PRCM_SEM BIT(0)
-
-/* PRCMU clock/PLL/reset registers */
-#define PRCM_PLLDSI_FREQ 0x500
-#define PRCM_PLLDSI_ENABLE 0x504
-#define PRCM_PLLDSI_LOCKP 0x508
-#define PRCM_DSI_PLLOUT_SEL 0x530
-#define PRCM_DSITVCLK_DIV 0x52C
-#define PRCM_APE_RESETN_SET 0x1E4
-#define PRCM_APE_RESETN_CLR 0x1E8
-
-#define PRCM_TCR 0x1C8
-#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
-#define PRCM_TCR_STOP_TIMERS BIT(16)
-#define PRCM_TCR_DOZE_MODE BIT(17)
-
-#define PRCM_CLKOCR 0x1CC
-#define PRCM_CLKOCR_CLKODIV0_SHIFT 0
-#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5)
-#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6
-#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8)
-#define PRCM_CLKOCR_CLKODIV1_SHIFT 16
-#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21)
-#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22
-#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24)
-#define PRCM_CLKOCR_CLK1TYPE BIT(28)
-
-#define PRCM_SGACLK_MGT 0x014
-#define PRCM_UARTCLK_MGT 0x018
-#define PRCM_MSP02CLK_MGT 0x01C
-#define PRCM_MSP1CLK_MGT 0x288
-#define PRCM_I2CCLK_MGT 0x020
-#define PRCM_SDMMCCLK_MGT 0x024
-#define PRCM_SLIMCLK_MGT 0x028
-#define PRCM_PER1CLK_MGT 0x02C
-#define PRCM_PER2CLK_MGT 0x030
-#define PRCM_PER3CLK_MGT 0x034
-#define PRCM_PER5CLK_MGT 0x038
-#define PRCM_PER6CLK_MGT 0x03C
-#define PRCM_PER7CLK_MGT 0x040
-#define PRCM_LCDCLK_MGT 0x044
-#define PRCM_BMLCLK_MGT 0x04C
-#define PRCM_HSITXCLK_MGT 0x050
-#define PRCM_HSIRXCLK_MGT 0x054
-#define PRCM_HDMICLK_MGT 0x058
-#define PRCM_APEATCLK_MGT 0x05C
-#define PRCM_APETRACECLK_MGT 0x060
-#define PRCM_MCDECLK_MGT 0x064
-#define PRCM_IPI2CCLK_MGT 0x068
-#define PRCM_DSIALTCLK_MGT 0x06C
-#define PRCM_DMACLK_MGT 0x074
-#define PRCM_B2R2CLK_MGT 0x078
-#define PRCM_TVCLK_MGT 0x07C
-#define PRCM_UNIPROCLK_MGT 0x278
-#define PRCM_SSPCLK_MGT 0x280
-#define PRCM_RNGCLK_MGT 0x284
-#define PRCM_UICCCLK_MGT 0x27C
-
-#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
-#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
-#define PRCM_CLK_MGT_CLKEN BIT(8)
-
-/* ePOD and memory power signal control registers */
-#define PRCM_EPOD_C_SET 0x410
-#define PRCM_SRAM_LS_SLEEP 0x304
-
-/* Debug power control unit registers */
-#define PRCM_POWER_STATE_SET 0x254
-
-/* Miscellaneous unit registers */
-#define PRCM_DSI_SW_RESET 0x324
-#define PRCM_GPIOCR 0x138
-
-/* GPIOCR register */
-#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
-
-#define PRCM_DDR_SUBSYS_APE_MINBW 0x438
-
-#endif /* __DB8500_PRCMU_REGS_H */
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 02a15d7cb3b0..a25ab9c6b5af 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -27,14 +27,14 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/mfd/core.h>
-#include <linux/mfd/db8500-prcmu.h>
+#include <linux/mfd/dbx500-prcmu.h>
#include <linux/regulator/db8500-prcmu.h>
#include <linux/regulator/machine.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db8500-regs.h>
#include <mach/id.h>
-#include "db8500-prcmu-regs.h"
+#include "dbx500-prcmu-regs.h"
/* Offset for the firmware version within the TCPM */
#define PRCMU_FW_VERSION_OFFSET 0xA4
@@ -131,12 +131,14 @@
#define MB1H_REQUEST_APE_OPP_100_VOLT 0x3
#define MB1H_RELEASE_APE_OPP_100_VOLT 0x4
#define MB1H_RELEASE_USB_WAKEUP 0x5
+#define MB1H_PLL_ON_OFF 0x6
/* Mailbox 1 Requests */
#define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0)
#define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1)
-#define PRCM_REQ_MB1_APE_OPP_100_RESTORE (PRCM_REQ_MB1 + 0x4)
-#define PRCM_REQ_MB1_ARM_OPP_100_RESTORE (PRCM_REQ_MB1 + 0x8)
+#define PRCM_REQ_MB1_PLL_ON_OFF (PRCM_REQ_MB1 + 0x4)
+#define PLL_SOC1_OFF 0x4
+#define PLL_SOC1_ON 0x8
/* Mailbox 1 ACKs */
#define PRCM_ACK_MB1_CURRENT_ARM_OPP (PRCM_ACK_MB1 + 0x0)
@@ -184,6 +186,11 @@
#define MB4H_HOTDOG 0x12
#define MB4H_HOTMON 0x13
#define MB4H_HOT_PERIOD 0x14
+#define MB4H_A9WDOG_CONF 0x16
+#define MB4H_A9WDOG_EN 0x17
+#define MB4H_A9WDOG_DIS 0x18
+#define MB4H_A9WDOG_LOAD 0x19
+#define MB4H_A9WDOG_KICK 0x20
/* Mailbox 4 Requests */
#define PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE (PRCM_REQ_MB4 + 0x0)
@@ -196,6 +203,13 @@
#define PRCM_REQ_MB4_HOT_PERIOD (PRCM_REQ_MB4 + 0x0)
#define HOTMON_CONFIG_LOW BIT(0)
#define HOTMON_CONFIG_HIGH BIT(1)
+#define PRCM_REQ_MB4_A9WDOG_0 (PRCM_REQ_MB4 + 0x0)
+#define PRCM_REQ_MB4_A9WDOG_1 (PRCM_REQ_MB4 + 0x1)
+#define PRCM_REQ_MB4_A9WDOG_2 (PRCM_REQ_MB4 + 0x2)
+#define PRCM_REQ_MB4_A9WDOG_3 (PRCM_REQ_MB4 + 0x3)
+#define A9WDOG_AUTO_OFF_EN BIT(7)
+#define A9WDOG_AUTO_OFF_DIS 0
+#define A9WDOG_ID_MASK 0xf
/* Mailbox 5 Requests */
#define PRCM_REQ_MB5_I2C_SLAVE_OP (PRCM_REQ_MB5 + 0x0)
@@ -412,7 +426,7 @@ struct clk_mgt {
static DEFINE_SPINLOCK(clk_mgt_lock);
-#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { (PRCM_##_name##_MGT), 0 }
+#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { (PRCM_##_name##_MGT_OFF), 0 }
struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
CLK_MGT_ENTRY(SGACLK),
CLK_MGT_ENTRY(UARTCLK),
@@ -445,6 +459,35 @@ struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
CLK_MGT_ENTRY(UICCCLK),
};
+static struct regulator *hwacc_regulator[NUM_HW_ACC];
+static struct regulator *hwacc_ret_regulator[NUM_HW_ACC];
+
+static bool hwacc_enabled[NUM_HW_ACC];
+static bool hwacc_ret_enabled[NUM_HW_ACC];
+
+static const char *hwacc_regulator_name[NUM_HW_ACC] = {
+ [HW_ACC_SVAMMDSP] = "hwacc-sva-mmdsp",
+ [HW_ACC_SVAPIPE] = "hwacc-sva-pipe",
+ [HW_ACC_SIAMMDSP] = "hwacc-sia-mmdsp",
+ [HW_ACC_SIAPIPE] = "hwacc-sia-pipe",
+ [HW_ACC_SGA] = "hwacc-sga",
+ [HW_ACC_B2R2] = "hwacc-b2r2",
+ [HW_ACC_MCDE] = "hwacc-mcde",
+ [HW_ACC_ESRAM1] = "hwacc-esram1",
+ [HW_ACC_ESRAM2] = "hwacc-esram2",
+ [HW_ACC_ESRAM3] = "hwacc-esram3",
+ [HW_ACC_ESRAM4] = "hwacc-esram4",
+};
+
+static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = {
+ [HW_ACC_SVAMMDSP] = "hwacc-sva-mmdsp-ret",
+ [HW_ACC_SIAMMDSP] = "hwacc-sia-mmdsp-ret",
+ [HW_ACC_ESRAM1] = "hwacc-esram1-ret",
+ [HW_ACC_ESRAM2] = "hwacc-esram2-ret",
+ [HW_ACC_ESRAM3] = "hwacc-esram3-ret",
+ [HW_ACC_ESRAM4] = "hwacc-esram4-ret",
+};
+
/*
* Used by MCDE to setup all necessary PRCMU registers
*/
@@ -493,55 +536,51 @@ static struct {
} prcmu_version;
-int prcmu_enable_dsipll(void)
+int db8500_prcmu_enable_dsipll(void)
{
int i;
unsigned int plldsifreq;
/* Clear DSIPLL_RESETN */
- writel(PRCMU_RESET_DSIPLL, (_PRCMU_BASE + PRCM_APE_RESETN_CLR));
+ writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR);
/* Unclamp DSIPLL in/out */
- writel(PRCMU_UNCLAMP_DSIPLL, (_PRCMU_BASE + PRCM_MMIP_LS_CLAMP_CLR));
+ writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR);
if (prcmu_is_u8400())
plldsifreq = PRCMU_PLLDSI_FREQ_SETTING_U8400;
else
plldsifreq = PRCMU_PLLDSI_FREQ_SETTING;
/* Set DSI PLL FREQ */
- writel(plldsifreq, (_PRCMU_BASE + PRCM_PLLDSI_FREQ));
- writel(PRCMU_DSI_PLLOUT_SEL_SETTING,
- (_PRCMU_BASE + PRCM_DSI_PLLOUT_SEL));
+ writel(plldsifreq, PRCM_PLLDSI_FREQ);
+ writel(PRCMU_DSI_PLLOUT_SEL_SETTING, PRCM_DSI_PLLOUT_SEL);
/* Enable Escape clocks */
- writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV,
- (_PRCMU_BASE + PRCM_DSITVCLK_DIV));
+ writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
/* Start DSI PLL */
- writel(PRCMU_ENABLE_PLLDSI, (_PRCMU_BASE + PRCM_PLLDSI_ENABLE));
+ writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
/* Reset DSI PLL */
- writel(PRCMU_DSI_RESET_SW, (_PRCMU_BASE + PRCM_DSI_SW_RESET));
+ writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET);
for (i = 0; i < 10; i++) {
- if ((readl(_PRCMU_BASE + PRCM_PLLDSI_LOCKP) &
- PRCMU_PLLDSI_LOCKP_LOCKED)
+ if ((readl(PRCM_PLLDSI_LOCKP) & PRCMU_PLLDSI_LOCKP_LOCKED)
== PRCMU_PLLDSI_LOCKP_LOCKED)
break;
udelay(100);
}
/* Set DSIPLL_RESETN */
- writel(PRCMU_RESET_DSIPLL, (_PRCMU_BASE + PRCM_APE_RESETN_SET));
+ writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET);
return 0;
}
-int prcmu_disable_dsipll(void)
+int db8500_prcmu_disable_dsipll(void)
{
/* Disable dsi pll */
- writel(PRCMU_DISABLE_PLLDSI, (_PRCMU_BASE + PRCM_PLLDSI_ENABLE));
+ writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
/* Disable escapeclock */
- writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV,
- (_PRCMU_BASE + PRCM_DSITVCLK_DIV));
+ writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
return 0;
}
-int prcmu_set_display_clocks(void)
+int db8500_prcmu_set_display_clocks(void)
{
unsigned long flags;
unsigned int dsiclk;
@@ -554,15 +593,15 @@ int prcmu_set_display_clocks(void)
spin_lock_irqsave(&clk_mgt_lock, flags);
/* Grab the HW semaphore. */
- while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
- writel(dsiclk, (_PRCMU_BASE + PRCM_HDMICLK_MGT));
- writel(PRCMU_DSI_LP_CLOCK_SETTING, (_PRCMU_BASE + PRCM_TVCLK_MGT));
- writel(PRCMU_DPI_CLOCK_SETTING, (_PRCMU_BASE + PRCM_LCDCLK_MGT));
+ writel(dsiclk, PRCM_HDMICLK_MGT);
+ writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT);
+ writel(PRCMU_DPI_CLOCK_SETTING, PRCM_LCDCLK_MGT);
/* Release the HW semaphore. */
- writel(0, (_PRCMU_BASE + PRCM_SEM));
+ writel(0, PRCM_SEM);
spin_unlock_irqrestore(&clk_mgt_lock, flags);
@@ -578,8 +617,8 @@ void prcmu_enable_spi2(void)
unsigned long flags;
spin_lock_irqsave(&gpiocr_lock, flags);
- reg = readl(_PRCMU_BASE + PRCM_GPIOCR);
- writel(reg | PRCM_GPIOCR_SPI2_SELECT, _PRCMU_BASE + PRCM_GPIOCR);
+ reg = readl(PRCM_GPIOCR);
+ writel(reg | PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR);
spin_unlock_irqrestore(&gpiocr_lock, flags);
}
@@ -592,8 +631,8 @@ void prcmu_disable_spi2(void)
unsigned long flags;
spin_lock_irqsave(&gpiocr_lock, flags);
- reg = readl(_PRCMU_BASE + PRCM_GPIOCR);
- writel(reg & ~PRCM_GPIOCR_SPI2_SELECT, _PRCMU_BASE + PRCM_GPIOCR);
+ reg = readl(PRCM_GPIOCR);
+ writel(reg & ~PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR);
spin_unlock_irqrestore(&gpiocr_lock, flags);
}
@@ -701,7 +740,7 @@ int prcmu_config_clkout(u8 clkout, u8 source, u8 div)
spin_lock_irqsave(&clkout_lock, flags);
- val = readl(_PRCMU_BASE + PRCM_CLKOCR);
+ val = readl(PRCM_CLKOCR);
if (val & div_mask) {
if (div) {
if ((val & mask) != bits) {
@@ -715,7 +754,7 @@ int prcmu_config_clkout(u8 clkout, u8 source, u8 div)
}
}
}
- writel((bits | (val & ~mask)), (_PRCMU_BASE + PRCM_CLKOCR));
+ writel((bits | (val & ~mask)), PRCM_CLKOCR);
requests[clkout] += (div ? 1 : -1);
unlock_and_return:
@@ -724,7 +763,7 @@ unlock_and_return:
return r;
}
-int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
+int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
{
unsigned long flags;
@@ -732,7 +771,7 @@ int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
spin_lock_irqsave(&mb0_transfer.lock, flags);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
cpu_relax();
writeb(MB0H_POWER_STATE_TRANS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
@@ -741,7 +780,7 @@ int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
writeb((keep_ulp_clk ? 1 : 0),
(tcdm_base + PRCM_REQ_MB0_ULP_CLOCK_STATE));
writeb(0, (tcdm_base + PRCM_REQ_MB0_DO_NOT_WFI));
- writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
@@ -770,18 +809,18 @@ static void config_wakeups(void)
return;
for (i = 0; i < 2; i++) {
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
cpu_relax();
writel(dbb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_8500));
writel(abb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_4500));
writeb(header[i], (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
- writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
}
last_dbb_events = dbb_events;
last_abb_events = abb_events;
}
-void prcmu_enable_wakeups(u32 wakeups)
+void db8500_prcmu_enable_wakeups(u32 wakeups)
{
unsigned long flags;
u32 bits;
@@ -802,7 +841,7 @@ void prcmu_enable_wakeups(u32 wakeups)
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
}
-void prcmu_config_abb_event_readout(u32 abb_events)
+void db8500_prcmu_config_abb_event_readout(u32 abb_events)
{
unsigned long flags;
@@ -814,7 +853,7 @@ void prcmu_config_abb_event_readout(u32 abb_events)
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
}
-void prcmu_get_abb_event_buffer(void __iomem **buf)
+void db8500_prcmu_get_abb_event_buffer(void __iomem **buf)
{
if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1)
*buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_1_4500);
@@ -823,13 +862,13 @@ void prcmu_get_abb_event_buffer(void __iomem **buf)
}
/**
- * prcmu_set_arm_opp - set the appropriate ARM OPP
+ * db8500_prcmu_set_arm_opp - set the appropriate ARM OPP
* @opp: The new ARM operating point to which transition is to be made
* Returns: 0 on success, non-zero on failure
*
* This function sets the the operating point of the ARM.
*/
-int prcmu_set_arm_opp(u8 opp)
+int db8500_prcmu_set_arm_opp(u8 opp)
{
int r;
@@ -840,14 +879,14 @@ int prcmu_set_arm_opp(u8 opp)
mutex_lock(&mb1_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
writeb(opp, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
writeb(APE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
@@ -860,11 +899,11 @@ int prcmu_set_arm_opp(u8 opp)
}
/**
- * prcmu_get_arm_opp - get the current ARM OPP
+ * db8500_prcmu_get_arm_opp - get the current ARM OPP
*
* Returns: the current ARM OPP
*/
-int prcmu_get_arm_opp(void)
+int db8500_prcmu_get_arm_opp(void)
{
return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_ARM_OPP);
}
@@ -876,7 +915,7 @@ int prcmu_get_arm_opp(void)
*/
int prcmu_get_ddr_opp(void)
{
- return readb(_PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW);
+ return readb(PRCM_DDR_SUBSYS_APE_MINBW);
}
/**
@@ -892,7 +931,7 @@ int prcmu_set_ddr_opp(u8 opp)
return -EINVAL;
/* Changing the DDR OPP can hang the hardware pre-v21 */
if (cpu_is_u8500v20_or_later() && !cpu_is_u8500v20())
- writeb(opp, (_PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW));
+ writeb(opp, PRCM_DDR_SUBSYS_APE_MINBW);
return 0;
}
@@ -909,14 +948,14 @@ int prcmu_set_ape_opp(u8 opp)
mutex_lock(&mb1_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
writeb(opp, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
@@ -966,12 +1005,12 @@ int prcmu_request_ape_opp_100_voltage(bool enable)
header = MB1H_RELEASE_APE_OPP_100_VOLT;
}
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(header, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
if ((mb1_transfer.ack.header != header) ||
@@ -995,13 +1034,13 @@ int prcmu_release_usb_wakeup_state(void)
mutex_lock(&mb1_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_RELEASE_USB_WAKEUP,
(tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
if ((mb1_transfer.ack.header != MB1H_RELEASE_USB_WAKEUP) ||
@@ -1013,15 +1052,169 @@ int prcmu_release_usb_wakeup_state(void)
return r;
}
+static int request_pll(u8 clock, bool enable)
+{
+ int r = 0;
+
+ if (clock == PRCMU_PLLSOC1)
+ clock = (enable ? PLL_SOC1_ON : PLL_SOC1_OFF);
+ else
+ return -EINVAL;
+
+ mutex_lock(&mb1_transfer.lock);
+
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_PLL_ON_OFF, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+ writeb(clock, (tcdm_base + PRCM_REQ_MB1_PLL_ON_OFF));
+
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
+ wait_for_completion(&mb1_transfer.work);
+
+ if (mb1_transfer.ack.header != MB1H_PLL_ON_OFF)
+ r = -EIO;
+
+ mutex_unlock(&mb1_transfer.lock);
+
+ return r;
+}
+
/**
- * prcmu_set_epod - set the state of a EPOD (power domain)
+ * prcmu_set_hwacc - set the power state of a h/w accelerator
+ * @hwacc_dev: The hardware accelerator (enum hw_acc_dev).
+ * @state: The new power state (enum hw_acc_state).
+ *
+ * This function sets the power state of a hardware accelerator.
+ * This function should not be called from interrupt context.
+ *
+ * NOTE! Deprecated, to be removed when all users switched over to use the
+ * regulator framework API.
+ */
+int prcmu_set_hwacc(u16 hwacc_dev, u8 state)
+{
+ int r = 0;
+ bool ram_retention = false;
+ bool enable, enable_ret;
+
+ /* check argument */
+ BUG_ON(hwacc_dev >= NUM_HW_ACC);
+
+ /* get state of switches */
+ enable = hwacc_enabled[hwacc_dev];
+ enable_ret = hwacc_ret_enabled[hwacc_dev];
+
+ /* set flag if retention is possible */
+ switch (hwacc_dev) {
+ case HW_ACC_SVAMMDSP:
+ case HW_ACC_SIAMMDSP:
+ case HW_ACC_ESRAM1:
+ case HW_ACC_ESRAM2:
+ case HW_ACC_ESRAM3:
+ case HW_ACC_ESRAM4:
+ ram_retention = true;
+ break;
+ }
+
+ /* check argument */
+ BUG_ON(state > HW_ON);
+ BUG_ON(state == HW_OFF_RAMRET && !ram_retention);
+
+ /* modify enable flags */
+ switch (state) {
+ case HW_OFF:
+ enable_ret = false;
+ enable = false;
+ break;
+ case HW_ON:
+ enable = true;
+ break;
+ case HW_OFF_RAMRET:
+ enable_ret = true;
+ enable = false;
+ break;
+ }
+
+ /* get regulator (lazy) */
+ if (hwacc_regulator[hwacc_dev] == NULL) {
+ hwacc_regulator[hwacc_dev] = regulator_get(NULL,
+ hwacc_regulator_name[hwacc_dev]);
+ if (IS_ERR(hwacc_regulator[hwacc_dev])) {
+ pr_err("prcmu: failed to get supply %s\n",
+ hwacc_regulator_name[hwacc_dev]);
+ r = PTR_ERR(hwacc_regulator[hwacc_dev]);
+ goto out;
+ }
+ }
+
+ if (ram_retention) {
+ if (hwacc_ret_regulator[hwacc_dev] == NULL) {
+ hwacc_ret_regulator[hwacc_dev] = regulator_get(NULL,
+ hwacc_ret_regulator_name[hwacc_dev]);
+ if (IS_ERR(hwacc_ret_regulator[hwacc_dev])) {
+ pr_err("prcmu: failed to get supply %s\n",
+ hwacc_ret_regulator_name[hwacc_dev]);
+ r = PTR_ERR(hwacc_ret_regulator[hwacc_dev]);
+ goto out;
+ }
+ }
+ }
+
+ /* set regulators */
+ if (ram_retention) {
+ if (enable_ret && !hwacc_ret_enabled[hwacc_dev]) {
+ r = regulator_enable(hwacc_ret_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: ret enable failed\n");
+ goto out;
+ }
+ hwacc_ret_enabled[hwacc_dev] = true;
+ }
+ }
+
+ if (enable && !hwacc_enabled[hwacc_dev]) {
+ r = regulator_enable(hwacc_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: enable failed\n");
+ goto out;
+ }
+ hwacc_enabled[hwacc_dev] = true;
+ }
+
+ if (!enable && hwacc_enabled[hwacc_dev]) {
+ r = regulator_disable(hwacc_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: disable failed\n");
+ goto out;
+ }
+ hwacc_enabled[hwacc_dev] = false;
+ }
+
+ if (ram_retention) {
+ if (!enable_ret && hwacc_ret_enabled[hwacc_dev]) {
+ r = regulator_disable(hwacc_ret_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: ret disable failed\n");
+ goto out;
+ }
+ hwacc_ret_enabled[hwacc_dev] = false;
+ }
+ }
+
+out:
+ return r;
+}
+EXPORT_SYMBOL(prcmu_set_hwacc);
+
+/**
+ * db8500_prcmu_set_epod - set the state of a EPOD (power domain)
* @epod_id: The EPOD to set
* @epod_state: The new EPOD state
*
* This function sets the state of a EPOD (power domain). It may not be called
* from interrupt context.
*/
-int prcmu_set_epod(u16 epod_id, u8 epod_state)
+int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state)
{
int r = 0;
bool ram_retention = false;
@@ -1048,7 +1241,7 @@ int prcmu_set_epod(u16 epod_id, u8 epod_state)
mutex_lock(&mb2_transfer.lock);
/* wait for mailbox */
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
cpu_relax();
/* fill in mailbox */
@@ -1058,7 +1251,7 @@ int prcmu_set_epod(u16 epod_id, u8 epod_state)
writeb(MB2H_DPS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB2));
- writel(MBOX_BIT(2), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(2), PRCM_MBOX_CPU_SET);
/*
* The current firmware version does not handle errors correctly,
@@ -1145,13 +1338,13 @@ static int request_sysclk(bool enable)
spin_lock_irqsave(&mb3_transfer.lock, flags);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(3))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(3))
cpu_relax();
writeb((enable ? ON : OFF), (tcdm_base + PRCM_REQ_MB3_SYSCLK_MGT));
writeb(MB3H_SYSCLK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB3));
- writel(MBOX_BIT(3), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(3), PRCM_MBOX_CPU_SET);
spin_unlock_irqrestore(&mb3_transfer.lock, flags);
@@ -1177,7 +1370,7 @@ static int request_timclk(bool enable)
if (!enable)
val |= PRCM_TCR_STOP_TIMERS;
- writel(val, (_PRCMU_BASE + PRCM_TCR));
+ writel(val, PRCM_TCR);
return 0;
}
@@ -1190,7 +1383,7 @@ static int request_reg_clock(u8 clock, bool enable)
spin_lock_irqsave(&clk_mgt_lock, flags);
/* Grab the HW semaphore. */
- while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
@@ -1203,34 +1396,61 @@ static int request_reg_clock(u8 clock, bool enable)
writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
/* Release the HW semaphore. */
- writel(0, (_PRCMU_BASE + PRCM_SEM));
+ writel(0, PRCM_SEM);
spin_unlock_irqrestore(&clk_mgt_lock, flags);
return 0;
}
+static int request_sga_clock(u8 clock, bool enable)
+{
+ u32 val;
+ int ret;
+
+ if (enable) {
+ val = readl(PRCM_CGATING_BYPASS);
+ writel(val | PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS);
+ }
+
+ ret = request_reg_clock(clock, enable);
+
+ if (!ret && !enable) {
+ val = readl(PRCM_CGATING_BYPASS);
+ writel(val & ~PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS);
+ }
+
+ return ret;
+}
+
/**
- * prcmu_request_clock() - Request for a clock to be enabled or disabled.
+ * db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled.
* @clock: The clock for which the request is made.
* @enable: Whether the clock should be enabled (true) or disabled (false).
*
* This function should only be used by the clock implementation.
* Do not use it from any other place!
*/
-int prcmu_request_clock(u8 clock, bool enable)
+int db8500_prcmu_request_clock(u8 clock, bool enable)
{
- if (clock < PRCMU_NUM_REG_CLOCKS)
- return request_reg_clock(clock, enable);
- else if (clock == PRCMU_TIMCLK)
+ switch(clock) {
+ case PRCMU_SGACLK:
+ return request_sga_clock(clock, enable);
+ case PRCMU_TIMCLK:
return request_timclk(enable);
- else if (clock == PRCMU_SYSCLK)
+ case PRCMU_SYSCLK:
return request_sysclk(enable);
- else
- return -EINVAL;
+ case PRCMU_PLLSOC1:
+ return request_pll(clock, enable);
+ default:
+ break;
+ }
+ if (clock < PRCMU_NUM_REG_CLOCKS)
+ return request_reg_clock(clock, enable);
+ return -EINVAL;
}
-int prcmu_config_esram0_deep_sleep(u8 state)
+int db8500_prcmu_config_esram0_deep_sleep(u8 state)
{
if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) ||
(state < ESRAM0_DEEP_SLEEP_STATE_OFF))
@@ -1238,7 +1458,7 @@ int prcmu_config_esram0_deep_sleep(u8 state)
mutex_lock(&mb4_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
cpu_relax();
writeb(MB4H_MEM_ST, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
@@ -1248,7 +1468,7 @@ int prcmu_config_esram0_deep_sleep(u8 state)
(tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE));
writeb(state, (tcdm_base + PRCM_REQ_MB4_ESRAM0_ST));
- writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb4_transfer.work);
mutex_unlock(&mb4_transfer.lock);
@@ -1260,13 +1480,13 @@ int prcmu_config_hotdog(u8 threshold)
{
mutex_lock(&mb4_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
cpu_relax();
writeb(threshold, (tcdm_base + PRCM_REQ_MB4_HOTDOG_THRESHOLD));
writeb(MB4H_HOTDOG, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
- writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb4_transfer.work);
mutex_unlock(&mb4_transfer.lock);
@@ -1278,7 +1498,7 @@ int prcmu_config_hotmon(u8 low, u8 high)
{
mutex_lock(&mb4_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
cpu_relax();
writeb(low, (tcdm_base + PRCM_REQ_MB4_HOTMON_LOW));
@@ -1287,7 +1507,7 @@ int prcmu_config_hotmon(u8 low, u8 high)
(tcdm_base + PRCM_REQ_MB4_HOTMON_CONFIG));
writeb(MB4H_HOTMON, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
- writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb4_transfer.work);
mutex_unlock(&mb4_transfer.lock);
@@ -1299,13 +1519,13 @@ static int config_hot_period(u16 val)
{
mutex_lock(&mb4_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
cpu_relax();
writew(val, (tcdm_base + PRCM_REQ_MB4_HOT_PERIOD));
writeb(MB4H_HOT_PERIOD, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
- writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb4_transfer.work);
mutex_unlock(&mb4_transfer.lock);
@@ -1326,6 +1546,78 @@ int prcmu_stop_temp_sense(void)
return config_hot_period(0xFFFF);
}
+static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3)
+{
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writeb(d0, (tcdm_base + PRCM_REQ_MB4_A9WDOG_0));
+ writeb(d1, (tcdm_base + PRCM_REQ_MB4_A9WDOG_1));
+ writeb(d2, (tcdm_base + PRCM_REQ_MB4_A9WDOG_2));
+ writeb(d3, (tcdm_base + PRCM_REQ_MB4_A9WDOG_3));
+
+ writeb(cmd, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+ writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
+ wait_for_completion(&mb4_transfer.work);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return 0;
+
+}
+
+int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
+{
+ BUG_ON(num == 0 || num > 0xf);
+ return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0,
+ sleep_auto_off ? A9WDOG_AUTO_OFF_EN :
+ A9WDOG_AUTO_OFF_DIS);
+}
+
+int prcmu_enable_a9wdog(u8 id)
+{
+ return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);
+}
+
+int prcmu_disable_a9wdog(u8 id)
+{
+ return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);
+}
+
+int prcmu_kick_a9wdog(u8 id)
+{
+ return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);
+}
+
+/*
+ * timeout is 28 bit, in ms.
+ */
+#define MAX_WATCHDOG_TIMEOUT 131000
+int prcmu_load_a9wdog(u8 id, u32 timeout)
+{
+ if (timeout > MAX_WATCHDOG_TIMEOUT)
+ /*
+ * Due to calculation bug in prcmu fw, timeouts
+ * can't be bigger than 131 seconds.
+ */
+ return -EINVAL;
+
+ return prcmu_a9wdog(MB4H_A9WDOG_LOAD,
+ (id & A9WDOG_ID_MASK) |
+ /*
+ * Put the lowest 28 bits of timeout at
+ * offset 4. Four first bits are used for id.
+ */
+ (u8)((timeout << 4) & 0xf0),
+ (u8)((timeout >> 4) & 0xff),
+ (u8)((timeout >> 12) & 0xff),
+ (u8)((timeout >> 20) & 0xff));
+}
+
/**
* prcmu_set_clock_divider() - Configure the clock divider.
* @clock: The clock for which the request is made.
@@ -1345,7 +1637,7 @@ int prcmu_set_clock_divider(u8 clock, u8 divider)
spin_lock_irqsave(&clk_mgt_lock, flags);
/* Grab the HW semaphore. */
- while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
@@ -1354,7 +1646,7 @@ int prcmu_set_clock_divider(u8 clock, u8 divider)
writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
/* Release the HW semaphore. */
- writel(0, (_PRCMU_BASE + PRCM_SEM));
+ writel(0, PRCM_SEM);
spin_unlock_irqrestore(&clk_mgt_lock, flags);
@@ -1380,7 +1672,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
mutex_lock(&mb5_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
cpu_relax();
writeb(PRCMU_I2C_READ(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
@@ -1388,7 +1680,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
writeb(0, (tcdm_base + PRCM_REQ_MB5_I2C_VAL));
- writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
if (!wait_for_completion_timeout(&mb5_transfer.work,
msecs_to_jiffies(20000))) {
@@ -1426,7 +1718,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
mutex_lock(&mb5_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
cpu_relax();
writeb(PRCMU_I2C_WRITE(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
@@ -1434,7 +1726,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
writeb(*value, (tcdm_base + PRCM_REQ_MB5_I2C_VAL));
- writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
if (!wait_for_completion_timeout(&mb5_transfer.work,
msecs_to_jiffies(20000))) {
@@ -1456,21 +1748,44 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
void prcmu_ac_wake_req(void)
{
u32 val;
+ u32 status;
mutex_lock(&mb0_transfer.ac_wake_lock);
- val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ);
+ val = readl(PRCM_HOSTACCESS_REQ);
if (val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ)
goto unlock_and_return;
atomic_set(&ac_wake_req_state, 1);
- writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ),
- (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+retry:
+ writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ);
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
- msecs_to_jiffies(20000))) {
- pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ msecs_to_jiffies(5000))) {
+ pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
+ __func__);
+ goto unlock_and_return;
+ }
+
+ /*
+ * The modem can generate an AC_WAKE_ACK, and then still go to sleep.
+ * As a workaround, we wait, and then check that the modem is indeed
+ * awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS
+ * register, which may not be the whole truth).
+ */
+ udelay(400);
+ status = (readl(PRCM_MOD_AWAKE_STATUS) & BITS(0, 2));
+ if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE |
+ PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) {
+ pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n",
+ __func__, status);
+ udelay(1200);
+ writel(val, PRCM_HOSTACCESS_REQ);
+ if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
+ msecs_to_jiffies(5000)))
+ goto retry;
+ pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n",
__func__);
}
@@ -1487,16 +1802,16 @@ void prcmu_ac_sleep_req()
mutex_lock(&mb0_transfer.ac_wake_lock);
- val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ);
+ val = readl(PRCM_HOSTACCESS_REQ);
if (!(val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ))
goto unlock_and_return;
writel((val & ~PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ),
- (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+ PRCM_HOSTACCESS_REQ);
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
- msecs_to_jiffies(20000))) {
- pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ msecs_to_jiffies(5000))) {
+ pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
__func__);
}
@@ -1506,21 +1821,32 @@ unlock_and_return:
mutex_unlock(&mb0_transfer.ac_wake_lock);
}
-bool prcmu_is_ac_wake_requested(void)
+bool db8500_prcmu_is_ac_wake_requested(void)
{
return (atomic_read(&ac_wake_req_state) != 0);
}
/**
- * prcmu_system_reset - System reset
+ * db8500_prcmu_system_reset - System reset
*
- * Saves the reset reason code and then sets the APE_SOFRST register which
+ * Saves the reset reason code and then sets the APE_SOFTRST register which
* fires interrupt to fw
*/
-void prcmu_system_reset(u16 reset_code)
+void db8500_prcmu_system_reset(u16 reset_code)
{
writew(reset_code, (tcdm_base + PRCM_SW_RST_REASON));
- writel(1, (_PRCMU_BASE + PRCM_APE_SOFTRST));
+ writel(1, PRCM_APE_SOFTRST);
+}
+
+/**
+ * db8500_prcmu_get_reset_code - Retrieve SW reset reason code
+ *
+ * Retrieves the reset reason code stored by prcmu_system_reset() before
+ * last restart.
+ */
+u16 db8500_prcmu_get_reset_code(void)
+{
+ return readw(tcdm_base + PRCM_SW_RST_REASON);
}
/**
@@ -1530,11 +1856,11 @@ void prcmu_modem_reset(void)
{
mutex_lock(&mb1_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_RESET_MODEM, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
/*
@@ -1551,11 +1877,11 @@ static void ack_dbb_wakeup(void)
spin_lock_irqsave(&mb0_transfer.lock, flags);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
cpu_relax();
writeb(MB0H_READ_WAKEUP_ACK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
- writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
}
@@ -1600,7 +1926,7 @@ static bool read_mailbox_0(void)
r = false;
break;
}
- writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR);
return r;
}
@@ -1613,7 +1939,7 @@ static bool read_mailbox_1(void)
PRCM_ACK_MB1_CURRENT_APE_OPP);
mb1_transfer.ack.ape_voltage_status = readb(tcdm_base +
PRCM_ACK_MB1_APE_VOLTAGE_STATUS);
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR);
complete(&mb1_transfer.work);
return false;
}
@@ -1621,14 +1947,14 @@ static bool read_mailbox_1(void)
static bool read_mailbox_2(void)
{
mb2_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB2_DPS_STATUS);
- writel(MBOX_BIT(2), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR);
complete(&mb2_transfer.work);
return false;
}
static bool read_mailbox_3(void)
{
- writel(MBOX_BIT(3), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR);
return false;
}
@@ -1643,6 +1969,11 @@ static bool read_mailbox_4(void)
case MB4H_HOTDOG:
case MB4H_HOTMON:
case MB4H_HOT_PERIOD:
+ case MB4H_A9WDOG_CONF:
+ case MB4H_A9WDOG_EN:
+ case MB4H_A9WDOG_DIS:
+ case MB4H_A9WDOG_LOAD:
+ case MB4H_A9WDOG_KICK:
break;
default:
print_unknown_header_warning(4, header);
@@ -1650,7 +1981,7 @@ static bool read_mailbox_4(void)
break;
}
- writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR);
if (do_complete)
complete(&mb4_transfer.work);
@@ -1662,20 +1993,20 @@ static bool read_mailbox_5(void)
{
mb5_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB5_I2C_STATUS);
mb5_transfer.ack.value = readb(tcdm_base + PRCM_ACK_MB5_I2C_VAL);
- writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR);
complete(&mb5_transfer.work);
return false;
}
static bool read_mailbox_6(void)
{
- writel(MBOX_BIT(6), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_7(void)
{
- writel(MBOX_BIT(7), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR);
return false;
}
@@ -1696,7 +2027,7 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
u8 n;
irqreturn_t r;
- bits = (readl(_PRCMU_BASE + PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+ bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
if (unlikely(!bits))
return IRQ_NONE;
@@ -1768,7 +2099,7 @@ static struct irq_chip prcmu_irq_chip = {
.irq_unmask = prcmu_irq_unmask,
};
-void __init prcmu_early_init(void)
+void __init db8500_prcmu_early_init(void)
{
unsigned int i;
@@ -1826,6 +2157,16 @@ void __init prcmu_early_init(void)
}
}
+static void __init db8500_prcmu_init_clkforce(void)
+{
+ u32 val;
+
+ val = readl(PRCM_A9PL_FORCE_CLKEN);
+ val &= ~(PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN |
+ PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN);
+ writel(val, (PRCM_A9PL_FORCE_CLKEN));
+}
+
/*
* Power domain switches (ePODs) modeled as regulators for the DB8500 SoC
*/
@@ -1861,7 +2202,42 @@ static struct regulator_consumer_supply db8500_vsmps2_consumers[] = {
static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = {
REGULATOR_SUPPLY("vsupply", "b2r2.0"),
- REGULATOR_SUPPLY("vsupply", "mcde.0"),
+ REGULATOR_SUPPLY("vsupply", "mcde"),
+};
+
+/* SVA MMDSP regulator switch */
+static struct regulator_consumer_supply db8500_svammdsp_consumers[] = {
+ REGULATOR_SUPPLY("sva-mmdsp", "cm_control"),
+};
+
+/* SVA pipe regulator switch */
+static struct regulator_consumer_supply db8500_svapipe_consumers[] = {
+ REGULATOR_SUPPLY("sva-pipe", "cm_control"),
+};
+
+/* SIA MMDSP regulator switch */
+static struct regulator_consumer_supply db8500_siammdsp_consumers[] = {
+ REGULATOR_SUPPLY("sia-mmdsp", "cm_control"),
+};
+
+/* SIA pipe regulator switch */
+static struct regulator_consumer_supply db8500_siapipe_consumers[] = {
+ REGULATOR_SUPPLY("sia-pipe", "cm_control"),
+};
+
+static struct regulator_consumer_supply db8500_sga_consumers[] = {
+ REGULATOR_SUPPLY("v-mali", NULL),
+};
+
+/* ESRAM1 and 2 regulator switch */
+static struct regulator_consumer_supply db8500_esram12_consumers[] = {
+ REGULATOR_SUPPLY("esram12", "cm_control"),
+};
+
+/* ESRAM3 and 4 regulator switch */
+static struct regulator_consumer_supply db8500_esram34_consumers[] = {
+ REGULATOR_SUPPLY("v-esram34", "mcde"),
+ REGULATOR_SUPPLY("esram34", "cm_control"),
};
static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
@@ -1923,6 +2299,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-sva-mmdsp",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_svammdsp_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_svammdsp_consumers),
},
[DB8500_REGULATOR_SWITCH_SVAMMDSPRET] = {
.constraints = {
@@ -1937,6 +2315,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-sva-pipe",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_svapipe_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_svapipe_consumers),
},
[DB8500_REGULATOR_SWITCH_SIAMMDSP] = {
.supply_regulator = "db8500-vape",
@@ -1944,6 +2324,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-sia-mmdsp",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_siammdsp_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_siammdsp_consumers),
},
[DB8500_REGULATOR_SWITCH_SIAMMDSPRET] = {
.constraints = {
@@ -1957,6 +2339,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-sia-pipe",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_siapipe_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_siapipe_consumers),
},
[DB8500_REGULATOR_SWITCH_SGA] = {
.supply_regulator = "db8500-vape",
@@ -1964,6 +2348,9 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-sga",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_sga_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_sga_consumers),
+
},
[DB8500_REGULATOR_SWITCH_B2R2_MCDE] = {
.supply_regulator = "db8500-vape",
@@ -1980,6 +2367,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-esram12",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_esram12_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_esram12_consumers),
},
[DB8500_REGULATOR_SWITCH_ESRAM12RET] = {
.constraints = {
@@ -1993,6 +2382,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-esram34",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_esram34_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_esram34_consumers),
},
[DB8500_REGULATOR_SWITCH_ESRAM34RET] = {
.constraints = {
@@ -2024,8 +2415,10 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
if (ux500_is_svp())
return -ENODEV;
+ db8500_prcmu_init_clkforce();
+
/* Clean up the mailbox interrupts after pre-kernel code. */
- writel(ALL_MBOX_BITS, (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
err = request_threaded_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler,
prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
diff --git a/drivers/mfd/db5500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h
index 9a8e9e4ddd33..ec22e9f15d32 100644
--- a/drivers/mfd/db5500-prcmu-regs.h
+++ b/drivers/mfd/dbx500-prcmu-regs.h
@@ -10,11 +10,49 @@
* PRCM Unit registers
*/
-#ifndef __MACH_PRCMU_REGS_H
-#define __MACH_PRCMU_REGS_H
+#ifndef __DB8500_PRCMU_REGS_H
+#define __DB8500_PRCMU_REGS_H
#include <mach/hardware.h>
+#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
+
+#define PRCM_SVACLK_MGT_OFF 0x008
+#define PRCM_SIACLK_MGT_OFF 0x00C
+#define PRCM_SGACLK_MGT_OFF 0x014
+#define PRCM_UARTCLK_MGT_OFF 0x018
+#define PRCM_MSP02CLK_MGT_OFF 0x01C
+#define PRCM_I2CCLK_MGT_OFF 0x020
+#define PRCM_SDMMCCLK_MGT_OFF 0x024
+#define PRCM_SLIMCLK_MGT_OFF 0x028
+#define PRCM_PER1CLK_MGT_OFF 0x02C
+#define PRCM_PER2CLK_MGT_OFF 0x030
+#define PRCM_PER3CLK_MGT_OFF 0x034
+#define PRCM_PER5CLK_MGT_OFF 0x038
+#define PRCM_PER6CLK_MGT_OFF 0x03C
+#define PRCM_PER7CLK_MGT_OFF 0x040
+#define PRCM_PWMCLK_MGT_OFF 0x044 /* for DB5500 */
+#define PRCM_IRDACLK_MGT_OFF 0x048 /* for DB5500 */
+#define PRCM_IRRCCLK_MGT_OFF 0x04C /* for DB5500 */
+#define PRCM_LCDCLK_MGT_OFF 0x044
+#define PRCM_BMLCLK_MGT_OFF 0x04C
+#define PRCM_HSITXCLK_MGT_OFF 0x050
+#define PRCM_HSIRXCLK_MGT_OFF 0x054
+#define PRCM_HDMICLK_MGT_OFF 0x058
+#define PRCM_APEATCLK_MGT_OFF 0x05C
+#define PRCM_APETRACECLK_MGT_OFF 0x060
+#define PRCM_MCDECLK_MGT_OFF 0x064
+#define PRCM_IPI2CCLK_MGT_OFF 0x068
+#define PRCM_DSIALTCLK_MGT_OFF 0x06C
+#define PRCM_DMACLK_MGT_OFF 0x074
+#define PRCM_B2R2CLK_MGT_OFF 0x078
+#define PRCM_TVCLK_MGT_OFF 0x07C
+#define PRCM_UNIPROCLK_MGT_OFF 0x278
+#define PRCM_SSPCLK_MGT_OFF 0x280
+#define PRCM_RNGCLK_MGT_OFF 0x284
+#define PRCM_UICCCLK_MGT_OFF 0x27C
+#define PRCM_MSP1CLK_MGT_OFF 0x288
+
#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118)
#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f
#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf
@@ -30,11 +68,15 @@
#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100
#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0)
+#define PRCM_A9PL_FORCE_CLKEN (_PRCMU_BASE + 0x19C)
#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4)
#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0)
#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c)
#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308)
+#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0)
+#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1)
+
/* ARM WFI Standby signal register */
#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130)
#define PRCM_IOCR (_PRCMU_BASE + 0x310)
@@ -61,12 +103,18 @@
#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C)
#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334)
+#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1
#define ARM_WAKEUP_MODEM 0x1
-#define PRCM_ARM_IT1_CLEAR (_PRCMU_BASE + 0x48C)
+#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C)
#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494)
#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174)
+#define PRCM_MOD_AWAKE_STATUS (_PRCMU_BASE + 0x4A0)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2)
+
#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148)
#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150)
#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158)
@@ -87,16 +135,21 @@
#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500)
#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504)
#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
-#define PRCM_LCDCLK_MGT (_PRCMU_BASE + 0x044)
-#define PRCM_MCDECLK_MGT (_PRCMU_BASE + 0x064)
-#define PRCM_HDMICLK_MGT (_PRCMU_BASE + 0x058)
-#define PRCM_TVCLK_MGT (_PRCMU_BASE + 0x07c)
+#define PRCM_LCDCLK_MGT (_PRCMU_BASE + PRCM_LCDCLK_MGT_OFF)
+#define PRCM_MCDECLK_MGT (_PRCMU_BASE + PRCM_MCDECLK_MGT_OFF)
+#define PRCM_HDMICLK_MGT (_PRCMU_BASE + PRCM_HDMICLK_MGT_OFF)
+#define PRCM_TVCLK_MGT (_PRCMU_BASE + PRCM_TVCLK_MGT_OFF)
#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530)
#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C)
#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4)
#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8)
+
#define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC)
+#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0)
+#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13)
+#define PRCM_CLKOCR_CLKOUT1_REF_CLK (1 << 16)
+#define PRCM_CLKOCR_CLKOUT1_MASK BITS(16, 29)
/* ePOD and memory power signal control registers */
#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410)
@@ -111,5 +164,41 @@
#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800
#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1
+/* PRCMU HW semaphore */
+#define PRCM_SEM (_PRCMU_BASE + 0x400)
+#define PRCM_SEM_PRCM_SEM BIT(0)
+
+#define PRCM_TCR (_PRCMU_BASE + 0x1C8)
+#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
+#define PRCM_TCR_STOP_TIMERS BIT(16)
+#define PRCM_TCR_DOZE_MODE BIT(17)
+
+#define PRCM_CLKOCR_CLKODIV0_SHIFT 0
+#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5)
+#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6
+#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8)
+#define PRCM_CLKOCR_CLKODIV1_SHIFT 16
+#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21)
+#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22
+#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24)
+#define PRCM_CLKOCR_CLK1TYPE BIT(28)
+
+#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
+#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
+#define PRCM_CLK_MGT_CLKEN BIT(8)
+
+/* GPIOCR register */
+#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
+
+#define PRCM_DDR_SUBSYS_APE_MINBW (_PRCMU_BASE + 0x438)
+#define PRCM_CGATING_BYPASS (_PRCMU_BASE + 0x134)
+#define PRCM_CGATING_BYPASS_ICN2 BIT(6)
+
+/* Miscellaneous unit registers */
+#define PRCM_RESOUTN_SET (_PRCMU_BASE + 0x214)
+#define PRCM_RESOUTN_CLR (_PRCMU_BASE + 0x218)
+
+/* System reset register */
+#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228)
-#endif /* __MACH_PRCMU__REGS_H */
+#endif /* __DB8500_PRCMU_REGS_H */
diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c
index 3d4a861976ca..8ad88da647b9 100644
--- a/drivers/mfd/dm355evm_msp.c
+++ b/drivers/mfd/dm355evm_msp.c
@@ -13,6 +13,7 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/module.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/leds.h>
diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c
new file mode 100644
index 000000000000..97c27762174f
--- /dev/null
+++ b/drivers/mfd/intel_msic.c
@@ -0,0 +1,502 @@
+/*
+ * Driver for Intel MSIC
+ *
+ * Copyright (C) 2011, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel_msic.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <asm/intel_scu_ipc.h>
+
+#define MSIC_VENDOR(id) ((id >> 6) & 3)
+#define MSIC_VERSION(id) (id & 0x3f)
+#define MSIC_MAJOR(id) ('A' + ((id >> 3) & 7))
+#define MSIC_MINOR(id) (id & 7)
+
+/*
+ * MSIC interrupt tree is readable from SRAM at INTEL_MSIC_IRQ_PHYS_BASE.
+ * Since IRQ block starts from address 0x002 we need to substract that from
+ * the actual IRQ status register address.
+ */
+#define MSIC_IRQ_STATUS(x) (INTEL_MSIC_IRQ_PHYS_BASE + ((x) - 2))
+#define MSIC_IRQ_STATUS_ACCDET MSIC_IRQ_STATUS(INTEL_MSIC_ACCDET)
+
+/*
+ * The SCU hardware has limitation of 16 bytes per read/write buffer on
+ * Medfield.
+ */
+#define SCU_IPC_RWBUF_LIMIT 16
+
+/**
+ * struct intel_msic - an MSIC MFD instance
+ * @pdev: pointer to the platform device
+ * @vendor: vendor ID
+ * @version: chip version
+ * @irq_base: base address of the mapped MSIC SRAM interrupt tree
+ */
+struct intel_msic {
+ struct platform_device *pdev;
+ unsigned vendor;
+ unsigned version;
+ void __iomem *irq_base;
+};
+
+static struct resource msic_touch_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_adc_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_battery_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_gpio_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_audio_resources[] = {
+ {
+ .name = "IRQ",
+ .flags = IORESOURCE_IRQ,
+ },
+ /*
+ * We will pass IRQ_BASE to the driver now but this can be removed
+ * when/if the driver starts to use intel_msic_irq_read().
+ */
+ {
+ .name = "IRQ_BASE",
+ .flags = IORESOURCE_MEM,
+ .start = MSIC_IRQ_STATUS_ACCDET,
+ .end = MSIC_IRQ_STATUS_ACCDET,
+ },
+};
+
+static struct resource msic_hdmi_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_thermal_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_power_btn_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_ocd_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*
+ * Devices that are part of the MSIC and are available via firmware
+ * populated SFI DEVS table.
+ */
+static struct mfd_cell msic_devs[] = {
+ [INTEL_MSIC_BLOCK_TOUCH] = {
+ .name = "msic_touch",
+ .num_resources = ARRAY_SIZE(msic_touch_resources),
+ .resources = msic_touch_resources,
+ },
+ [INTEL_MSIC_BLOCK_ADC] = {
+ .name = "msic_adc",
+ .num_resources = ARRAY_SIZE(msic_adc_resources),
+ .resources = msic_adc_resources,
+ },
+ [INTEL_MSIC_BLOCK_BATTERY] = {
+ .name = "msic_battery",
+ .num_resources = ARRAY_SIZE(msic_battery_resources),
+ .resources = msic_battery_resources,
+ },
+ [INTEL_MSIC_BLOCK_GPIO] = {
+ .name = "msic_gpio",
+ .num_resources = ARRAY_SIZE(msic_gpio_resources),
+ .resources = msic_gpio_resources,
+ },
+ [INTEL_MSIC_BLOCK_AUDIO] = {
+ .name = "msic_audio",
+ .num_resources = ARRAY_SIZE(msic_audio_resources),
+ .resources = msic_audio_resources,
+ },
+ [INTEL_MSIC_BLOCK_HDMI] = {
+ .name = "msic_hdmi",
+ .num_resources = ARRAY_SIZE(msic_hdmi_resources),
+ .resources = msic_hdmi_resources,
+ },
+ [INTEL_MSIC_BLOCK_THERMAL] = {
+ .name = "msic_thermal",
+ .num_resources = ARRAY_SIZE(msic_thermal_resources),
+ .resources = msic_thermal_resources,
+ },
+ [INTEL_MSIC_BLOCK_POWER_BTN] = {
+ .name = "msic_power_btn",
+ .num_resources = ARRAY_SIZE(msic_power_btn_resources),
+ .resources = msic_power_btn_resources,
+ },
+ [INTEL_MSIC_BLOCK_OCD] = {
+ .name = "msic_ocd",
+ .num_resources = ARRAY_SIZE(msic_ocd_resources),
+ .resources = msic_ocd_resources,
+ },
+};
+
+/*
+ * Other MSIC related devices which are not directly available via SFI DEVS
+ * table. These can be pseudo devices, regulators etc. which are needed for
+ * different purposes.
+ *
+ * These devices appear only after the MSIC driver itself is initialized so
+ * we can guarantee that the SCU IPC interface is ready.
+ */
+static struct mfd_cell msic_other_devs[] = {
+ /* Audio codec in the MSIC */
+ {
+ .id = -1,
+ .name = "sn95031",
+ },
+};
+
+/**
+ * intel_msic_reg_read - read a single MSIC register
+ * @reg: register to read
+ * @val: register value is placed here
+ *
+ * Read a single register from MSIC. Returns %0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_read(unsigned short reg, u8 *val)
+{
+ return intel_scu_ipc_ioread8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_read);
+
+/**
+ * intel_msic_reg_write - write a single MSIC register
+ * @reg: register to write
+ * @val: value to write to that register
+ *
+ * Write a single MSIC register. Returns 0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_write(unsigned short reg, u8 val)
+{
+ return intel_scu_ipc_iowrite8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_write);
+
+/**
+ * intel_msic_reg_update - update a single MSIC register
+ * @reg: register to update
+ * @val: value to write to the register
+ * @mask: specifies which of the bits are updated (%0 = don't update,
+ * %1 = update)
+ *
+ * Perform an update to a register @reg. @mask is used to specify which
+ * bits are updated. Returns %0 in case of success and negative errno in
+ * case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask)
+{
+ return intel_scu_ipc_update_register(reg, val, mask);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_update);
+
+/**
+ * intel_msic_bulk_read - read an array of registers
+ * @reg: array of register addresses to read
+ * @buf: array where the read values are placed
+ * @count: number of registers to read
+ *
+ * Function reads @count registers from the MSIC using addresses passed in
+ * @reg. Read values are placed in @buf. Reads are performed atomically
+ * wrt. MSIC.
+ *
+ * Returns %0 in case of success and negative errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count)
+{
+ if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
+ return -EINVAL;
+
+ return intel_scu_ipc_readv(reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(intel_msic_bulk_read);
+
+/**
+ * intel_msic_bulk_write - write an array of values to the MSIC registers
+ * @reg: array of registers to write
+ * @buf: values to write to each register
+ * @count: number of registers to write
+ *
+ * Function writes @count registers in @buf to MSIC. Writes are performed
+ * atomically wrt MSIC. Returns %0 in case of success and negative errno in
+ * case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count)
+{
+ if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
+ return -EINVAL;
+
+ return intel_scu_ipc_writev(reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(intel_msic_bulk_write);
+
+/**
+ * intel_msic_irq_read - read a register from an MSIC interrupt tree
+ * @msic: MSIC instance
+ * @reg: interrupt register (between %INTEL_MSIC_IRQLVL1 and
+ * %INTEL_MSIC_RESETIRQ2)
+ * @val: value of the register is placed here
+ *
+ * This function can be used by an MSIC subdevice interrupt handler to read
+ * a register value from the MSIC interrupt tree. In this way subdevice
+ * drivers don't have to map in the interrupt tree themselves but can just
+ * call this function instead.
+ *
+ * Function doesn't sleep and is callable from interrupt context.
+ *
+ * Returns %-EINVAL if @reg is outside of the allowed register region.
+ */
+int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, u8 *val)
+{
+ if (WARN_ON(reg < INTEL_MSIC_IRQLVL1 || reg > INTEL_MSIC_RESETIRQ2))
+ return -EINVAL;
+
+ *val = readb(msic->irq_base + (reg - INTEL_MSIC_IRQLVL1));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(intel_msic_irq_read);
+
+static int __devinit intel_msic_init_devices(struct intel_msic *msic)
+{
+ struct platform_device *pdev = msic->pdev;
+ struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+ int ret, i;
+
+ if (pdata->gpio) {
+ struct mfd_cell *cell = &msic_devs[INTEL_MSIC_BLOCK_GPIO];
+
+ cell->platform_data = pdata->gpio;
+ cell->pdata_size = sizeof(*pdata->gpio);
+ }
+
+ if (pdata->ocd) {
+ unsigned gpio = pdata->ocd->gpio;
+
+ ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register OCD GPIO\n");
+ return ret;
+ }
+
+ ret = gpio_to_irq(gpio);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n");
+ gpio_free(gpio);
+ return ret;
+ }
+
+ /* Update the IRQ number for the OCD */
+ pdata->irq[INTEL_MSIC_BLOCK_OCD] = ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(msic_devs); i++) {
+ if (!pdata->irq[i])
+ continue;
+
+ ret = mfd_add_devices(&pdev->dev, -1, &msic_devs[i], 1, NULL,
+ pdata->irq[i]);
+ if (ret)
+ goto fail;
+ }
+
+ ret = mfd_add_devices(&pdev->dev, 0, msic_other_devs,
+ ARRAY_SIZE(msic_other_devs), NULL, 0);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ mfd_remove_devices(&pdev->dev);
+ if (pdata->ocd)
+ gpio_free(pdata->ocd->gpio);
+
+ return ret;
+}
+
+static void __devexit intel_msic_remove_devices(struct intel_msic *msic)
+{
+ struct platform_device *pdev = msic->pdev;
+ struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+
+ mfd_remove_devices(&pdev->dev);
+
+ if (pdata->ocd)
+ gpio_free(pdata->ocd->gpio);
+}
+
+static int __devinit intel_msic_probe(struct platform_device *pdev)
+{
+ struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+ struct intel_msic *msic;
+ struct resource *res;
+ u8 id0, id1;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data passed\n");
+ return -EINVAL;
+ }
+
+ /* First validate that we have an MSIC in place */
+ ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID0, &id0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to identify the MSIC chip (ID0)\n");
+ return -ENXIO;
+ }
+
+ ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID1, &id1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to identify the MSIC chip (ID1)\n");
+ return -ENXIO;
+ }
+
+ if (MSIC_VENDOR(id0) != MSIC_VENDOR(id1)) {
+ dev_err(&pdev->dev, "invalid vendor ID: %x, %x\n", id0, id1);
+ return -ENXIO;
+ }
+
+ msic = kzalloc(sizeof(*msic), GFP_KERNEL);
+ if (!msic)
+ return -ENOMEM;
+
+ msic->vendor = MSIC_VENDOR(id0);
+ msic->version = MSIC_VERSION(id0);
+ msic->pdev = pdev;
+
+ /*
+ * Map in the MSIC interrupt tree area in SRAM. This is exposed to
+ * the clients via intel_msic_irq_read().
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get SRAM iomem resource\n");
+ ret = -ENODEV;
+ goto fail_free_msic;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!res) {
+ ret = -EBUSY;
+ goto fail_free_msic;
+ }
+
+ msic->irq_base = ioremap_nocache(res->start, resource_size(res));
+ if (!msic->irq_base) {
+ dev_err(&pdev->dev, "failed to map SRAM memory\n");
+ ret = -ENOMEM;
+ goto fail_release_region;
+ }
+
+ platform_set_drvdata(pdev, msic);
+
+ ret = intel_msic_init_devices(msic);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize MSIC devices\n");
+ goto fail_unmap_mem;
+ }
+
+ dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n",
+ MSIC_MAJOR(msic->version), MSIC_MINOR(msic->version),
+ msic->vendor);
+
+ return 0;
+
+fail_unmap_mem:
+ iounmap(msic->irq_base);
+fail_release_region:
+ release_mem_region(res->start, resource_size(res));
+fail_free_msic:
+ kfree(msic);
+
+ return ret;
+}
+
+static int __devexit intel_msic_remove(struct platform_device *pdev)
+{
+ struct intel_msic *msic = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ intel_msic_remove_devices(msic);
+ platform_set_drvdata(pdev, NULL);
+ iounmap(msic->irq_base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(msic);
+
+ return 0;
+}
+
+static struct platform_driver intel_msic_driver = {
+ .probe = intel_msic_probe,
+ .remove = __devexit_p(intel_msic_remove),
+ .driver = {
+ .name = "intel_msic",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init intel_msic_init(void)
+{
+ return platform_driver_register(&intel_msic_driver);
+}
+module_init(intel_msic_init);
+
+static void __exit intel_msic_exit(void)
+{
+ platform_driver_unregister(&intel_msic_driver);
+}
+module_exit(intel_msic_exit);
+
+MODULE_DESCRIPTION("Driver for Intel MSIC");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c
index a0bd0cf05af3..1e9ee533eacb 100644
--- a/drivers/mfd/jz4740-adc.c
+++ b/drivers/mfd/jz4740-adc.c
@@ -56,7 +56,7 @@ struct jz4740_adc {
void __iomem *base;
int irq;
- int irq_base;
+ struct irq_chip_generic *gc;
struct clk *clk;
atomic_t clk_ref;
@@ -64,63 +64,17 @@ struct jz4740_adc {
spinlock_t lock;
};
-static inline void jz4740_adc_irq_set_masked(struct jz4740_adc *adc, int irq,
- bool masked)
-{
- unsigned long flags;
- uint8_t val;
-
- irq -= adc->irq_base;
-
- spin_lock_irqsave(&adc->lock, flags);
-
- val = readb(adc->base + JZ_REG_ADC_CTRL);
- if (masked)
- val |= BIT(irq);
- else
- val &= ~BIT(irq);
- writeb(val, adc->base + JZ_REG_ADC_CTRL);
-
- spin_unlock_irqrestore(&adc->lock, flags);
-}
-
-static void jz4740_adc_irq_mask(struct irq_data *data)
-{
- struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
- jz4740_adc_irq_set_masked(adc, data->irq, true);
-}
-
-static void jz4740_adc_irq_unmask(struct irq_data *data)
-{
- struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
- jz4740_adc_irq_set_masked(adc, data->irq, false);
-}
-
-static void jz4740_adc_irq_ack(struct irq_data *data)
-{
- struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
- unsigned int irq = data->irq - adc->irq_base;
- writeb(BIT(irq), adc->base + JZ_REG_ADC_STATUS);
-}
-
-static struct irq_chip jz4740_adc_irq_chip = {
- .name = "jz4740-adc",
- .irq_mask = jz4740_adc_irq_mask,
- .irq_unmask = jz4740_adc_irq_unmask,
- .irq_ack = jz4740_adc_irq_ack,
-};
-
static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc)
{
- struct jz4740_adc *adc = irq_desc_get_handler_data(desc);
+ struct irq_chip_generic *gc = irq_desc_get_handler_data(desc);
uint8_t status;
unsigned int i;
- status = readb(adc->base + JZ_REG_ADC_STATUS);
+ status = readb(gc->reg_base + JZ_REG_ADC_STATUS);
for (i = 0; i < 5; ++i) {
if (status & BIT(i))
- generic_handle_irq(adc->irq_base + i);
+ generic_handle_irq(gc->irq_base + i);
}
}
@@ -249,10 +203,12 @@ const struct mfd_cell jz4740_adc_cells[] = {
static int __devinit jz4740_adc_probe(struct platform_device *pdev)
{
- int ret;
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
struct jz4740_adc *adc;
struct resource *mem_base;
- int irq;
+ int ret;
+ int irq_base;
adc = kmalloc(sizeof(*adc), GFP_KERNEL);
if (!adc) {
@@ -267,9 +223,9 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
goto err_free;
}
- adc->irq_base = platform_get_irq(pdev, 1);
- if (adc->irq_base < 0) {
- ret = adc->irq_base;
+ irq_base = platform_get_irq(pdev, 1);
+ if (irq_base < 0) {
+ ret = irq_base;
dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret);
goto err_free;
}
@@ -309,20 +265,28 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, adc);
- for (irq = adc->irq_base; irq < adc->irq_base + 5; ++irq) {
- irq_set_chip_data(irq, adc);
- irq_set_chip_and_handler(irq, &jz4740_adc_irq_chip,
- handle_level_irq);
- }
+ gc = irq_alloc_generic_chip("INTC", 1, irq_base, adc->base,
+ handle_level_irq);
+
+ ct = gc->chip_types;
+ ct->regs.mask = JZ_REG_ADC_CTRL;
+ ct->regs.ack = JZ_REG_ADC_STATUS;
+ ct->chip.irq_mask = irq_gc_mask_set_bit;
+ ct->chip.irq_unmask = irq_gc_mask_clr_bit;
+ ct->chip.irq_ack = irq_gc_ack_set_bit;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(5), 0, 0, IRQ_NOPROBE | IRQ_LEVEL);
+
+ adc->gc = gc;
- irq_set_handler_data(adc->irq, adc);
+ irq_set_handler_data(adc->irq, gc);
irq_set_chained_handler(adc->irq, jz4740_adc_irq_demux);
writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
- ARRAY_SIZE(jz4740_adc_cells), mem_base, adc->irq_base);
+ ARRAY_SIZE(jz4740_adc_cells), mem_base, irq_base);
if (ret < 0)
goto err_clk_put;
@@ -347,6 +311,8 @@ static int __devexit jz4740_adc_remove(struct platform_device *pdev)
mfd_remove_devices(&pdev->dev);
+ irq_remove_generic_chip(adc->gc, IRQ_MSK(5), IRQ_NOPROBE | IRQ_LEVEL, 0);
+ kfree(adc->gc);
irq_set_handler_data(adc->irq, NULL);
irq_set_chained_handler(adc->irq, NULL);
@@ -362,7 +328,7 @@ static int __devexit jz4740_adc_remove(struct platform_device *pdev)
return 0;
}
-struct platform_driver jz4740_adc_driver = {
+static struct platform_driver jz4740_adc_driver = {
.probe = jz4740_adc_probe,
.remove = __devexit_p(jz4740_adc_remove),
.driver = {
diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c
index ea3f52c07ef7..ea1169b04779 100644
--- a/drivers/mfd/lpc_sch.c
+++ b/drivers/mfd/lpc_sch.c
@@ -37,6 +37,9 @@
#define GPIOBASE 0x44
#define GPIO_IO_SIZE 64
+#define WDTBASE 0x84
+#define WDT_IO_SIZE 64
+
static struct resource smbus_sch_resource = {
.flags = IORESOURCE_IO,
};
@@ -59,6 +62,18 @@ static struct mfd_cell lpc_sch_cells[] = {
},
};
+static struct resource wdt_sch_resource = {
+ .flags = IORESOURCE_IO,
+};
+
+static struct mfd_cell tunnelcreek_cells[] = {
+ {
+ .name = "tunnelcreek_wdt",
+ .num_resources = 1,
+ .resources = &wdt_sch_resource,
+ },
+};
+
static struct pci_device_id lpc_sch_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
@@ -72,6 +87,7 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
unsigned int base_addr_cfg;
unsigned short base_addr;
int i;
+ int ret;
pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
if (!(base_addr_cfg & (1 << 31))) {
@@ -104,8 +120,39 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
lpc_sch_cells[i].id = id->device;
- return mfd_add_devices(&dev->dev, 0,
+ ret = mfd_add_devices(&dev->dev, 0,
lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
+ if (ret)
+ goto out_dev;
+
+ if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC) {
+ pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
+ if (!(base_addr_cfg & (1 << 31))) {
+ dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
+ ret = -ENODEV;
+ goto out_dev;
+ }
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0) {
+ dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
+ ret = -ENODEV;
+ goto out_dev;
+ }
+
+ wdt_sch_resource.start = base_addr;
+ wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
+
+ for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
+ tunnelcreek_cells[i].id = id->device;
+
+ ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
+ ARRAY_SIZE(tunnelcreek_cells), NULL, 0);
+ }
+
+ return ret;
+out_dev:
+ mfd_remove_devices(&dev->dev);
+ return ret;
}
static void __devexit lpc_sch_remove(struct pci_dev *dev)
diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c
index 638bf7e4d3b3..09274cf7c33b 100644
--- a/drivers/mfd/max8997-irq.c
+++ b/drivers/mfd/max8997-irq.c
@@ -58,8 +58,6 @@ static struct i2c_client *get_i2c(struct max8997_dev *max8997,
default:
return ERR_PTR(-EINVAL);
}
-
- return ERR_PTR(-EINVAL);
}
struct max8997_irq_data {
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 5d1fca0277ef..5be53ae9b61c 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -23,7 +23,9 @@
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max8997.h>
@@ -135,11 +137,13 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
max8997->dev = &i2c->dev;
max8997->i2c = i2c;
max8997->type = id->driver_data;
+ max8997->irq = i2c->irq;
if (!pdata)
goto err;
- max8997->wakeup = pdata->wakeup;
+ max8997->irq_base = pdata->irq_base;
+ max8997->ono = pdata->ono;
mutex_init(&max8997->iolock);
@@ -152,6 +156,8 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
pm_runtime_set_active(max8997->dev);
+ max8997_irq_init(max8997);
+
mfd_add_devices(max8997->dev, -1, max8997_devs,
ARRAY_SIZE(max8997_devs),
NULL, 0);
@@ -164,6 +170,9 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
if (ret < 0)
goto err_mfd;
+ /* MAX8997 has a power button input. */
+ device_init_wakeup(max8997->dev, pdata->wakeup);
+
return ret;
err_mfd:
@@ -393,7 +402,29 @@ static int max8997_restore(struct device *dev)
return 0;
}
+static int max8997_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ irq_set_irq_wake(max8997->irq, 1);
+ return 0;
+}
+
+static int max8997_resume(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ irq_set_irq_wake(max8997->irq, 0);
+ return max8997_irq_resume(max8997);
+}
+
const struct dev_pm_ops max8997_pm = {
+ .suspend = max8997_suspend,
+ .resume = max8997_resume,
.freeze = max8997_freeze,
.restore = max8997_restore,
};
diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c
index 9ec7570f5b81..de4096aee248 100644
--- a/drivers/mfd/max8998.c
+++ b/drivers/mfd/max8998.c
@@ -39,6 +39,8 @@ static struct mfd_cell max8998_devs[] = {
.name = "max8998-pmic",
}, {
.name = "max8998-rtc",
+ }, {
+ .name = "max8998-battery",
},
};
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 7e4d44bf92ab..e9619acc0237 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -26,20 +26,10 @@ struct mc13xxx {
irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
void *irqdata[MC13XXX_NUM_IRQ];
-};
-
-struct mc13783 {
- struct mc13xxx mc13xxx;
int adcflags;
};
-struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783)
-{
- return &mc13783->mc13xxx;
-}
-EXPORT_SYMBOL(mc13783_to_mc13xxx);
-
#define MC13XXX_IRQSTAT0 0
#define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0)
#define MC13XXX_IRQSTAT0_ADCBISDONEI (1 << 1)
@@ -136,14 +126,14 @@ EXPORT_SYMBOL(mc13783_to_mc13xxx);
#define MC13XXX_REVISION_FAB (0x03 << 11)
#define MC13XXX_REVISION_ICIDCODE (0x3f << 13)
-#define MC13783_ADC1 44
-#define MC13783_ADC1_ADEN (1 << 0)
-#define MC13783_ADC1_RAND (1 << 1)
-#define MC13783_ADC1_ADSEL (1 << 3)
-#define MC13783_ADC1_ASC (1 << 20)
-#define MC13783_ADC1_ADTRIGIGN (1 << 21)
+#define MC13XXX_ADC1 44
+#define MC13XXX_ADC1_ADEN (1 << 0)
+#define MC13XXX_ADC1_RAND (1 << 1)
+#define MC13XXX_ADC1_ADSEL (1 << 3)
+#define MC13XXX_ADC1_ASC (1 << 20)
+#define MC13XXX_ADC1_ADTRIGIGN (1 << 21)
-#define MC13783_ADC2 45
+#define MC13XXX_ADC2 45
#define MC13XXX_NUMREGS 0x3f
@@ -487,7 +477,7 @@ enum mc13xxx_id {
MC13XXX_ID_INVALID,
};
-const char *mc13xxx_chipname[] = {
+static const char *mc13xxx_chipname[] = {
[MC13XXX_ID_MC13783] = "mc13783",
[MC13XXX_ID_MC13892] = "mc13892",
};
@@ -558,8 +548,6 @@ static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
return mc13xxx_chipname[devid->driver_data];
}
-#include <linux/mfd/mc13783.h>
-
int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
{
struct mc13xxx_platform_data *pdata =
@@ -569,15 +557,15 @@ int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
}
EXPORT_SYMBOL(mc13xxx_get_flags);
-#define MC13783_ADC1_CHAN0_SHIFT 5
-#define MC13783_ADC1_CHAN1_SHIFT 8
+#define MC13XXX_ADC1_CHAN0_SHIFT 5
+#define MC13XXX_ADC1_CHAN1_SHIFT 8
struct mc13xxx_adcdone_data {
struct mc13xxx *mc13xxx;
struct completion done;
};
-static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
+static irqreturn_t mc13xxx_handler_adcdone(int irq, void *data)
{
struct mc13xxx_adcdone_data *adcdone_data = data;
@@ -588,12 +576,11 @@ static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
return IRQ_HANDLED;
}
-#define MC13783_ADC_WORKING (1 << 0)
+#define MC13XXX_ADC_WORKING (1 << 0)
-int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
+int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
unsigned int channel, unsigned int *sample)
{
- struct mc13xxx *mc13xxx = &mc13783->mc13xxx;
u32 adc0, adc1, old_adc0;
int i, ret;
struct mc13xxx_adcdone_data adcdone_data = {
@@ -605,51 +592,51 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
mc13xxx_lock(mc13xxx);
- if (mc13783->adcflags & MC13783_ADC_WORKING) {
+ if (mc13xxx->adcflags & MC13XXX_ADC_WORKING) {
ret = -EBUSY;
goto out;
}
- mc13783->adcflags |= MC13783_ADC_WORKING;
+ mc13xxx->adcflags |= MC13XXX_ADC_WORKING;
- mc13xxx_reg_read(mc13xxx, MC13783_ADC0, &old_adc0);
+ mc13xxx_reg_read(mc13xxx, MC13XXX_ADC0, &old_adc0);
- adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
- adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
+ adc0 = MC13XXX_ADC0_ADINC1 | MC13XXX_ADC0_ADINC2;
+ adc1 = MC13XXX_ADC1_ADEN | MC13XXX_ADC1_ADTRIGIGN | MC13XXX_ADC1_ASC;
if (channel > 7)
- adc1 |= MC13783_ADC1_ADSEL;
+ adc1 |= MC13XXX_ADC1_ADSEL;
switch (mode) {
- case MC13783_ADC_MODE_TS:
- adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
- MC13783_ADC0_TSMOD1;
- adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ case MC13XXX_ADC_MODE_TS:
+ adc0 |= MC13XXX_ADC0_ADREFEN | MC13XXX_ADC0_TSMOD0 |
+ MC13XXX_ADC0_TSMOD1;
+ adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT;
break;
- case MC13783_ADC_MODE_SINGLE_CHAN:
- adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
- adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
- adc1 |= MC13783_ADC1_RAND;
+ case MC13XXX_ADC_MODE_SINGLE_CHAN:
+ adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK;
+ adc1 |= (channel & 0x7) << MC13XXX_ADC1_CHAN0_SHIFT;
+ adc1 |= MC13XXX_ADC1_RAND;
break;
- case MC13783_ADC_MODE_MULT_CHAN:
- adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
- adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ case MC13XXX_ADC_MODE_MULT_CHAN:
+ adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK;
+ adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT;
break;
default:
- mc13783_unlock(mc13783);
+ mc13xxx_unlock(mc13xxx);
return -EINVAL;
}
- dev_dbg(&mc13783->mc13xxx.spidev->dev, "%s: request irq\n", __func__);
- mc13xxx_irq_request(mc13xxx, MC13783_IRQ_ADCDONE,
- mc13783_handler_adcdone, __func__, &adcdone_data);
- mc13xxx_irq_ack(mc13xxx, MC13783_IRQ_ADCDONE);
+ dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__);
+ mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE,
+ mc13xxx_handler_adcdone, __func__, &adcdone_data);
+ mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE);
- mc13xxx_reg_write(mc13xxx, MC13783_ADC0, adc0);
- mc13xxx_reg_write(mc13xxx, MC13783_ADC1, adc1);
+ mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, adc0);
+ mc13xxx_reg_write(mc13xxx, MC13XXX_ADC1, adc1);
mc13xxx_unlock(mc13xxx);
@@ -660,27 +647,27 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
mc13xxx_lock(mc13xxx);
- mc13xxx_irq_free(mc13xxx, MC13783_IRQ_ADCDONE, &adcdone_data);
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_ADCDONE, &adcdone_data);
if (ret > 0)
for (i = 0; i < 4; ++i) {
ret = mc13xxx_reg_read(mc13xxx,
- MC13783_ADC2, &sample[i]);
+ MC13XXX_ADC2, &sample[i]);
if (ret)
break;
}
- if (mode == MC13783_ADC_MODE_TS)
+ if (mode == MC13XXX_ADC_MODE_TS)
/* restore TSMOD */
- mc13xxx_reg_write(mc13xxx, MC13783_ADC0, old_adc0);
+ mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, old_adc0);
- mc13783->adcflags &= ~MC13783_ADC_WORKING;
+ mc13xxx->adcflags &= ~MC13XXX_ADC_WORKING;
out:
mc13xxx_unlock(mc13xxx);
return ret;
}
-EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
+EXPORT_SYMBOL_GPL(mc13xxx_adc_do_conversion);
static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
const char *format, void *pdata, size_t pdata_size)
@@ -716,6 +703,11 @@ static int mc13xxx_probe(struct spi_device *spi)
enum mc13xxx_id id;
int ret;
+ if (!pdata) {
+ dev_err(&spi->dev, "invalid platform data\n");
+ return -EINVAL;
+ }
+
mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
if (!mc13xxx)
return -ENOMEM;
@@ -763,10 +755,8 @@ err_revision:
if (pdata->flags & MC13XXX_USE_CODEC)
mc13xxx_add_subdevice(mc13xxx, "%s-codec");
- if (pdata->flags & MC13XXX_USE_REGULATOR) {
- mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
- &pdata->regulators, sizeof(pdata->regulators));
- }
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
+ &pdata->regulators, sizeof(pdata->regulators));
if (pdata->flags & MC13XXX_USE_RTC)
mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
@@ -774,10 +764,14 @@ err_revision:
if (pdata->flags & MC13XXX_USE_TOUCHSCREEN)
mc13xxx_add_subdevice(mc13xxx, "%s-ts");
- if (pdata->flags & MC13XXX_USE_LED)
+ if (pdata->leds)
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
pdata->leds, sizeof(*pdata->leds));
+ if (pdata->buttons)
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",
+ pdata->buttons, sizeof(*pdata->buttons));
+
return 0;
}
diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c
index 9cee8e7f0bcb..cb4910ac4d12 100644
--- a/drivers/mfd/menelaus.c
+++ b/drivers/mfd/menelaus.c
@@ -44,7 +44,7 @@
#include <asm/mach/irq.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#include <plat/menelaus.h>
#define DRIVER_NAME "menelaus"
@@ -1226,7 +1226,7 @@ static int menelaus_probe(struct i2c_client *client,
menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73);
if (client->irq > 0) {
- err = request_irq(client->irq, menelaus_irq, IRQF_DISABLED,
+ err = request_irq(client->irq, menelaus_irq, 0,
DRIVER_NAME, menelaus);
if (err) {
dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 0902523af62d..0f5922812bff 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -17,6 +17,7 @@
#include <linux/mfd/core.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
+#include <linux/module.h>
int mfd_cell_enable(struct platform_device *pdev)
{
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 1717144fe7f4..86e14583a082 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -17,6 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/delay.h>
@@ -676,7 +677,6 @@ static void usbhs_omap_tll_init(struct device *dev, u8 tll_channel_count)
| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
| OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
- reg |= (1 << (i + 1));
} else
continue;
@@ -998,9 +998,9 @@ static void usbhs_disable(struct device *dev)
if (is_omap_usbhs_rev2(omap)) {
if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_enable(omap->usbtll_p1_fck);
+ clk_disable(omap->usbtll_p1_fck);
if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_enable(omap->usbtll_p2_fck);
+ clk_disable(omap->usbtll_p2_fck);
clk_disable(omap->utmi_p2_fck);
clk_disable(omap->utmi_p1_fck);
}
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index 57868416c760..ff1a7e741ecd 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -23,45 +23,22 @@
#include <linux/i2c.h>
#include <linux/pm.h>
#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
#include <linux/mfd/pcf50633/core.h>
-static int __pcf50633_read(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
-{
- int ret;
-
- ret = i2c_smbus_read_i2c_block_data(pcf->i2c_client, reg,
- num, data);
- if (ret < 0)
- dev_err(pcf->dev, "Error reading %d regs at %d\n", num, reg);
-
- return ret;
-}
-
-static int __pcf50633_write(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
-{
- int ret;
-
- ret = i2c_smbus_write_i2c_block_data(pcf->i2c_client, reg,
- num, data);
- if (ret < 0)
- dev_err(pcf->dev, "Error writing %d regs at %d\n", num, reg);
-
- return ret;
-
-}
-
/* Read a block of up to 32 regs */
int pcf50633_read_block(struct pcf50633 *pcf, u8 reg,
int nr_regs, u8 *data)
{
int ret;
- mutex_lock(&pcf->lock);
- ret = __pcf50633_read(pcf, reg, nr_regs, data);
- mutex_unlock(&pcf->lock);
+ ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs);
+ if (ret != 0)
+ return ret;
- return ret;
+ return nr_regs;
}
EXPORT_SYMBOL_GPL(pcf50633_read_block);
@@ -71,21 +48,22 @@ int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,
{
int ret;
- mutex_lock(&pcf->lock);
- ret = __pcf50633_write(pcf, reg, nr_regs, data);
- mutex_unlock(&pcf->lock);
+ ret = regmap_raw_write(pcf->regmap, reg, data, nr_regs);
+ if (ret != 0)
+ return ret;
- return ret;
+ return nr_regs;
}
EXPORT_SYMBOL_GPL(pcf50633_write_block);
u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg)
{
- u8 val;
+ unsigned int val;
+ int ret;
- mutex_lock(&pcf->lock);
- __pcf50633_read(pcf, reg, 1, &val);
- mutex_unlock(&pcf->lock);
+ ret = regmap_read(pcf->regmap, reg, &val);
+ if (ret < 0)
+ return -1;
return val;
}
@@ -93,56 +71,19 @@ EXPORT_SYMBOL_GPL(pcf50633_reg_read);
int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val)
{
- int ret;
-
- mutex_lock(&pcf->lock);
- ret = __pcf50633_write(pcf, reg, 1, &val);
- mutex_unlock(&pcf->lock);
-
- return ret;
+ return regmap_write(pcf->regmap, reg, val);
}
EXPORT_SYMBOL_GPL(pcf50633_reg_write);
int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val)
{
- int ret;
- u8 tmp;
-
- val &= mask;
-
- mutex_lock(&pcf->lock);
- ret = __pcf50633_read(pcf, reg, 1, &tmp);
- if (ret < 0)
- goto out;
-
- tmp &= ~mask;
- tmp |= val;
- ret = __pcf50633_write(pcf, reg, 1, &tmp);
-
-out:
- mutex_unlock(&pcf->lock);
-
- return ret;
+ return regmap_update_bits(pcf->regmap, reg, mask, val);
}
EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask);
int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val)
{
- int ret;
- u8 tmp;
-
- mutex_lock(&pcf->lock);
- ret = __pcf50633_read(pcf, reg, 1, &tmp);
- if (ret < 0)
- goto out;
-
- tmp &= ~val;
- ret = __pcf50633_write(pcf, reg, 1, &tmp);
-
-out:
- mutex_unlock(&pcf->lock);
-
- return ret;
+ return regmap_update_bits(pcf->regmap, reg, val, 0);
}
EXPORT_SYMBOL_GPL(pcf50633_reg_clear_bits);
@@ -251,6 +192,11 @@ static int pcf50633_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume);
+static struct regmap_config pcf50633_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
static int __devinit pcf50633_probe(struct i2c_client *client,
const struct i2c_device_id *ids)
{
@@ -272,16 +218,23 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
mutex_init(&pcf->lock);
+ pcf->regmap = regmap_init_i2c(client, &pcf50633_regmap_config);
+ if (IS_ERR(pcf->regmap)) {
+ ret = PTR_ERR(pcf->regmap);
+ dev_err(pcf->dev, "Failed to allocate register map: %d\n",
+ ret);
+ goto err_free;
+ }
+
i2c_set_clientdata(client, pcf);
pcf->dev = &client->dev;
- pcf->i2c_client = client;
version = pcf50633_reg_read(pcf, 0);
variant = pcf50633_reg_read(pcf, 1);
if (version < 0 || variant < 0) {
dev_err(pcf->dev, "Unable to probe pcf50633\n");
ret = -ENODEV;
- goto err_free;
+ goto err_regmap;
}
dev_info(pcf->dev, "Probed device version %d variant %d\n",
@@ -328,6 +281,8 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
return 0;
+err_regmap:
+ regmap_exit(pcf->regmap);
err_free:
kfree(pcf);
@@ -351,6 +306,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
platform_device_unregister(pcf->regulator_pdev[i]);
+ regmap_exit(pcf->regmap);
kfree(pcf);
return 0;
diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c
index 1b0192f1efff..048a3b903b01 100644
--- a/drivers/mfd/pcf50633-irq.c
+++ b/drivers/mfd/pcf50633-irq.c
@@ -15,6 +15,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/mfd/pcf50633/core.h>
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 7ab7746631d4..2963689cf45c 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -228,7 +228,7 @@ int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
EXPORT_SYMBOL_GPL(stmpe_block_write);
/**
- * stmpe_set_altfunc: set the alternate function for STMPE pins
+ * stmpe_set_altfunc()- set the alternate function for STMPE pins
* @stmpe: Device to configure
* @pins: Bitmask of pins to affect
* @block: block to enable alternate functions for
diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h
index 0dbdc4e8cd77..e4ee38956583 100644
--- a/drivers/mfd/stmpe.h
+++ b/drivers/mfd/stmpe.h
@@ -42,6 +42,7 @@ struct stmpe_variant_block {
* @id_mask: bits valid in CHIPID register for comparison with id_val
* @num_gpios: number of GPIOS
* @af_bits: number of bits used to specify the alternate function
+ * @regs: variant specific registers.
* @blocks: list of blocks present on this device
* @num_blocks: number of blocks present on this device
* @num_irqs: number of internal IRQs available on this device
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index c27e515b0722..de979742c6fc 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -357,6 +357,7 @@ static int __devexit tc3589x_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
static int tc3589x_suspend(struct device *dev)
{
struct tc3589x *tc3589x = dev_get_drvdata(dev);
@@ -387,6 +388,7 @@ static int tc3589x_resume(struct device *dev)
static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend,
tc3589x_resume);
+#endif
static const struct i2c_device_id tc3589x_id[] = {
{ "tc3589x", 24 },
diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c
index ad715bf49cac..71bc835324d8 100644
--- a/drivers/mfd/tc6387xb.c
+++ b/drivers/mfd/tc6387xb.c
@@ -177,7 +177,7 @@ static int __devinit tc6387xb_probe(struct platform_device *dev)
if (ret)
goto err_resource;
- tc6387xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1);
+ tc6387xb->scr = ioremap(rscr->start, resource_size(rscr));
if (!tc6387xb->scr) {
ret = -ENOMEM;
goto err_ioremap;
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index 69272e4e3459..02d65692ceb4 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -287,12 +287,8 @@ static __devinitdata struct i2c_board_info timberdale_saa7706_i2c_board_info = {
static __devinitdata struct timb_radio_platform_data
timberdale_radio_platform_data = {
.i2c_adapter = 0,
- .tuner = {
- .info = &timberdale_tef6868_i2c_board_info
- },
- .dsp = {
- .info = &timberdale_saa7706_i2c_board_info
- }
+ .tuner = &timberdale_tef6868_i2c_board_info,
+ .dsp = &timberdale_saa7706_i2c_board_info
};
static const __devinitconst struct resource timberdale_video_resources[] = {
@@ -701,7 +697,7 @@ static int __devinit timb_probe(struct pci_dev *dev,
dev_err(&dev->dev, "The driver supports an older "
"version of the FPGA, please update the driver to "
"support %d.%d\n", priv->fw.major, priv->fw.minor);
- goto err_ioremap;
+ goto err_config;
}
if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
priv->fw.minor < TIMB_REQUIRED_MINOR) {
@@ -709,13 +705,13 @@ static int __devinit timb_probe(struct pci_dev *dev,
"please upgrade the FPGA to at least: %d.%d\n",
priv->fw.major, priv->fw.minor,
TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR);
- goto err_ioremap;
+ goto err_config;
}
msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries),
GFP_KERNEL);
if (!msix_entries)
- goto err_ioremap;
+ goto err_config;
for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
msix_entries[i].entry = i;
@@ -829,6 +825,8 @@ err_mfd:
err_create_file:
pci_disable_msix(dev);
err_msix:
+ kfree(msix_entries);
+err_config:
iounmap(priv->ctl_membase);
err_ioremap:
release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
@@ -837,7 +835,6 @@ err_request:
err_start:
pci_disable_device(dev);
err_enable:
- kfree(msix_entries);
kfree(priv);
pci_set_drvdata(dev, NULL);
return -ENODEV;
diff --git a/drivers/mfd/tmio_core.c b/drivers/mfd/tmio_core.c
index eddc19ae464b..83af78c1b0eb 100644
--- a/drivers/mfd/tmio_core.c
+++ b/drivers/mfd/tmio_core.c
@@ -6,6 +6,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/export.h>
#include <linux/mfd/tmio.h>
int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base)
diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c
index 2bfad5c86cc7..a56be931551c 100644
--- a/drivers/mfd/tps65910-irq.c
+++ b/drivers/mfd/tps65910-irq.c
@@ -178,8 +178,10 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq,
switch (tps65910_chip_id(tps65910)) {
case TPS65910:
tps65910->irq_num = TPS65910_NUM_IRQ;
+ break;
case TPS65911:
tps65910->irq_num = TPS65911_NUM_IRQ;
+ break;
}
/* Register with genirq */
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index 2229e66d80db..6f5b8cf2f652 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -147,12 +147,11 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
if (init_data == NULL)
return -ENOMEM;
- init_data->irq = pmic_plat_data->irq;
- init_data->irq_base = pmic_plat_data->irq;
-
tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
- if (tps65910 == NULL)
+ if (tps65910 == NULL) {
+ kfree(init_data);
return -ENOMEM;
+ }
i2c_set_clientdata(i2c, tps65910);
tps65910->dev = &i2c->dev;
@@ -168,17 +167,22 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
if (ret < 0)
goto err;
+ init_data->irq = pmic_plat_data->irq;
+ init_data->irq_base = pmic_plat_data->irq;
+
tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
ret = tps65910_irq_init(tps65910, init_data->irq, init_data);
if (ret < 0)
goto err;
+ kfree(init_data);
return ret;
err:
mfd_remove_devices(tps65910->dev);
kfree(tps65910);
+ kfree(init_data);
return ret;
}
@@ -187,6 +191,7 @@ static int tps65910_i2c_remove(struct i2c_client *i2c)
struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
mfd_remove_devices(tps65910->dev);
+ tps65910_irq_exit(tps65910);
kfree(tps65910);
return 0;
diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c
index 283ac6759757..e7ff783aa31e 100644
--- a/drivers/mfd/tps65911-comparator.c
+++ b/drivers/mfd/tps65911-comparator.c
@@ -157,6 +157,8 @@ static __devexit int tps65911_comparator_remove(struct platform_device *pdev)
struct tps65910 *tps65910;
tps65910 = dev_get_drvdata(pdev->dev.parent);
+ device_remove_file(&pdev->dev, &dev_attr_comp2_threshold);
+ device_remove_file(&pdev->dev, &dev_attr_comp1_threshold);
return 0;
}
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
new file mode 100644
index 000000000000..5fec23a9ac03
--- /dev/null
+++ b/drivers/mfd/tps65912-core.c
@@ -0,0 +1,177 @@
+/*
+ * tps65912-core.c -- TI TPS65912x
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static struct mfd_cell tps65912s[] = {
+ {
+ .name = "tps65912-pmic",
+ },
+};
+
+int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
+{
+ u8 data;
+ int err;
+
+ mutex_lock(&tps65912->io_mutex);
+
+ err = tps65912->read(tps65912, reg, 1, &data);
+ if (err) {
+ dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+ goto out;
+ }
+
+ data |= mask;
+ err = tps65912->write(tps65912, reg, 1, &data);
+ if (err)
+ dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+ mutex_unlock(&tps65912->io_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_set_bits);
+
+int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
+{
+ u8 data;
+ int err;
+
+ mutex_lock(&tps65912->io_mutex);
+ err = tps65912->read(tps65912, reg, 1, &data);
+ if (err) {
+ dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+ goto out;
+ }
+
+ data &= ~mask;
+ err = tps65912->write(tps65912, reg, 1, &data);
+ if (err)
+ dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+ mutex_unlock(&tps65912->io_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_clear_bits);
+
+static inline int tps65912_read(struct tps65912 *tps65912, u8 reg)
+{
+ u8 val;
+ int err;
+
+ err = tps65912->read(tps65912, reg, 1, &val);
+ if (err < 0)
+ return err;
+
+ return val;
+}
+
+static inline int tps65912_write(struct tps65912 *tps65912, u8 reg, u8 val)
+{
+ return tps65912->write(tps65912, reg, 1, &val);
+}
+
+int tps65912_reg_read(struct tps65912 *tps65912, u8 reg)
+{
+ int data;
+
+ mutex_lock(&tps65912->io_mutex);
+
+ data = tps65912_read(tps65912, reg);
+ if (data < 0)
+ dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+
+ mutex_unlock(&tps65912->io_mutex);
+ return data;
+}
+EXPORT_SYMBOL_GPL(tps65912_reg_read);
+
+int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val)
+{
+ int err;
+
+ mutex_lock(&tps65912->io_mutex);
+
+ err = tps65912_write(tps65912, reg, val);
+ if (err < 0)
+ dev_err(tps65912->dev, "Write for reg 0x%x failed\n", reg);
+
+ mutex_unlock(&tps65912->io_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_reg_write);
+
+int tps65912_device_init(struct tps65912 *tps65912)
+{
+ struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data;
+ struct tps65912_platform_data *init_data;
+ int ret, dcdc_avs, value;
+
+ init_data = kzalloc(sizeof(struct tps65912_platform_data), GFP_KERNEL);
+ if (init_data == NULL)
+ return -ENOMEM;
+
+ mutex_init(&tps65912->io_mutex);
+ dev_set_drvdata(tps65912->dev, tps65912);
+
+ dcdc_avs = (pmic_plat_data->is_dcdc1_avs << 0 |
+ pmic_plat_data->is_dcdc2_avs << 1 |
+ pmic_plat_data->is_dcdc3_avs << 2 |
+ pmic_plat_data->is_dcdc4_avs << 3);
+ if (dcdc_avs) {
+ tps65912->read(tps65912, TPS65912_I2C_SPI_CFG, 1, &value);
+ dcdc_avs |= value;
+ tps65912->write(tps65912, TPS65912_I2C_SPI_CFG, 1, &dcdc_avs);
+ }
+
+ ret = mfd_add_devices(tps65912->dev, -1,
+ tps65912s, ARRAY_SIZE(tps65912s),
+ NULL, 0);
+ if (ret < 0)
+ goto err;
+
+ init_data->irq = pmic_plat_data->irq;
+ init_data->irq_base = pmic_plat_data->irq;
+ ret = tps65912_irq_init(tps65912, init_data->irq, init_data);
+ if (ret < 0)
+ goto err;
+
+ kfree(init_data);
+ return ret;
+
+err:
+ kfree(init_data);
+ mfd_remove_devices(tps65912->dev);
+ kfree(tps65912);
+ return ret;
+}
+
+void tps65912_device_exit(struct tps65912 *tps65912)
+{
+ mfd_remove_devices(tps65912->dev);
+ kfree(tps65912);
+}
+
+MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS65912x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c
new file mode 100644
index 000000000000..c041f2c3d2bd
--- /dev/null
+++ b/drivers/mfd/tps65912-i2c.c
@@ -0,0 +1,139 @@
+/*
+ * tps65912-i2c.c -- I2C access for TI TPS65912x PMIC
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static int tps65912_i2c_read(struct tps65912 *tps65912, u8 reg,
+ int bytes, void *dest)
+{
+ struct i2c_client *i2c = tps65912->control_data;
+ struct i2c_msg xfer[2];
+ int ret;
+
+ /* Write register */
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 1;
+ xfer[0].buf = &reg;
+
+ /* Read data */
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = bytes;
+ xfer[1].buf = dest;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret == 2)
+ ret = 0;
+ else if (ret >= 0)
+ ret = -EIO;
+ return ret;
+}
+
+static int tps65912_i2c_write(struct tps65912 *tps65912, u8 reg,
+ int bytes, void *src)
+{
+ struct i2c_client *i2c = tps65912->control_data;
+ /* we add 1 byte for device register */
+ u8 msg[TPS6591X_MAX_REGISTER + 1];
+ int ret;
+
+ if (bytes > TPS6591X_MAX_REGISTER)
+ return -EINVAL;
+
+ msg[0] = reg;
+ memcpy(&msg[1], src, bytes);
+
+ ret = i2c_master_send(i2c, msg, bytes + 1);
+ if (ret < 0)
+ return ret;
+ if (ret != bytes + 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int tps65912_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct tps65912 *tps65912;
+
+ tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
+ if (tps65912 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, tps65912);
+ tps65912->dev = &i2c->dev;
+ tps65912->control_data = i2c;
+ tps65912->read = tps65912_i2c_read;
+ tps65912->write = tps65912_i2c_write;
+
+ return tps65912_device_init(tps65912);
+}
+
+static int tps65912_i2c_remove(struct i2c_client *i2c)
+{
+ struct tps65912 *tps65912 = i2c_get_clientdata(i2c);
+
+ tps65912_device_exit(tps65912);
+
+ return 0;
+}
+
+static const struct i2c_device_id tps65912_i2c_id[] = {
+ {"tps65912", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id);
+
+static struct i2c_driver tps65912_i2c_driver = {
+ .driver = {
+ .name = "tps65912",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65912_i2c_probe,
+ .remove = tps65912_i2c_remove,
+ .id_table = tps65912_i2c_id,
+};
+
+static int __init tps65912_i2c_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&tps65912_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register TPS65912 I2C driver: %d\n", ret);
+
+ return ret;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps65912_i2c_init);
+
+static void __exit tps65912_i2c_exit(void)
+{
+ i2c_del_driver(&tps65912_i2c_driver);
+}
+module_exit(tps65912_i2c_exit);
+
+MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c
new file mode 100644
index 000000000000..d360a83a2738
--- /dev/null
+++ b/drivers/mfd/tps65912-irq.c
@@ -0,0 +1,224 @@
+/*
+ * tps65912-irq.c -- TI TPS6591x
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65912.h>
+
+static inline int irq_to_tps65912_irq(struct tps65912 *tps65912,
+ int irq)
+{
+ return irq - tps65912->irq_base;
+}
+
+/*
+ * This is a threaded IRQ handler so can access I2C/SPI. Since the
+ * IRQ handler explicitly clears the IRQ it handles the IRQ line
+ * will be reasserted and the physical IRQ will be handled again if
+ * another interrupt is asserted while we run - in the normal course
+ * of events this is a rare occurrence so we save I2C/SPI reads. We're
+ * also assuming that it's rare to get lots of interrupts firing
+ * simultaneously so try to minimise I/O.
+ */
+static irqreturn_t tps65912_irq(int irq, void *irq_data)
+{
+ struct tps65912 *tps65912 = irq_data;
+ u32 irq_sts;
+ u32 irq_mask;
+ u8 reg;
+ int i;
+
+
+ tps65912->read(tps65912, TPS65912_INT_STS, 1, &reg);
+ irq_sts = reg;
+ tps65912->read(tps65912, TPS65912_INT_STS2, 1, &reg);
+ irq_sts |= reg << 8;
+ tps65912->read(tps65912, TPS65912_INT_STS3, 1, &reg);
+ irq_sts |= reg << 16;
+ tps65912->read(tps65912, TPS65912_INT_STS4, 1, &reg);
+ irq_sts |= reg << 24;
+
+ tps65912->read(tps65912, TPS65912_INT_MSK, 1, &reg);
+ irq_mask = reg;
+ tps65912->read(tps65912, TPS65912_INT_MSK2, 1, &reg);
+ irq_mask |= reg << 8;
+ tps65912->read(tps65912, TPS65912_INT_MSK3, 1, &reg);
+ irq_mask |= reg << 16;
+ tps65912->read(tps65912, TPS65912_INT_MSK4, 1, &reg);
+ irq_mask |= reg << 24;
+
+ irq_sts &= ~irq_mask;
+ if (!irq_sts)
+ return IRQ_NONE;
+
+ for (i = 0; i < tps65912->irq_num; i++) {
+ if (!(irq_sts & (1 << i)))
+ continue;
+
+ handle_nested_irq(tps65912->irq_base + i);
+ }
+
+ /* Write the STS register back to clear IRQs we handled */
+ reg = irq_sts & 0xFF;
+ irq_sts >>= 8;
+ if (reg)
+ tps65912->write(tps65912, TPS65912_INT_STS, 1, &reg);
+ reg = irq_sts & 0xFF;
+ irq_sts >>= 8;
+ if (reg)
+ tps65912->write(tps65912, TPS65912_INT_STS2, 1, &reg);
+ reg = irq_sts & 0xFF;
+ irq_sts >>= 8;
+ if (reg)
+ tps65912->write(tps65912, TPS65912_INT_STS3, 1, &reg);
+ reg = irq_sts & 0xFF;
+ if (reg)
+ tps65912->write(tps65912, TPS65912_INT_STS4, 1, &reg);
+
+ return IRQ_HANDLED;
+}
+
+static void tps65912_irq_lock(struct irq_data *data)
+{
+ struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&tps65912->irq_lock);
+}
+
+static void tps65912_irq_sync_unlock(struct irq_data *data)
+{
+ struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+ u32 reg_mask;
+ u8 reg;
+
+ tps65912->read(tps65912, TPS65912_INT_MSK, 1, &reg);
+ reg_mask = reg;
+ tps65912->read(tps65912, TPS65912_INT_MSK2, 1, &reg);
+ reg_mask |= reg << 8;
+ tps65912->read(tps65912, TPS65912_INT_MSK3, 1, &reg);
+ reg_mask |= reg << 16;
+ tps65912->read(tps65912, TPS65912_INT_MSK4, 1, &reg);
+ reg_mask |= reg << 24;
+
+ if (tps65912->irq_mask != reg_mask) {
+ reg = tps65912->irq_mask & 0xFF;
+ tps65912->write(tps65912, TPS65912_INT_MSK, 1, &reg);
+ reg = tps65912->irq_mask >> 8 & 0xFF;
+ tps65912->write(tps65912, TPS65912_INT_MSK2, 1, &reg);
+ reg = tps65912->irq_mask >> 16 & 0xFF;
+ tps65912->write(tps65912, TPS65912_INT_MSK3, 1, &reg);
+ reg = tps65912->irq_mask >> 24 & 0xFF;
+ tps65912->write(tps65912, TPS65912_INT_MSK4, 1, &reg);
+ }
+
+ mutex_unlock(&tps65912->irq_lock);
+}
+
+static void tps65912_irq_enable(struct irq_data *data)
+{
+ struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+ tps65912->irq_mask &= ~(1 << irq_to_tps65912_irq(tps65912, data->irq));
+}
+
+static void tps65912_irq_disable(struct irq_data *data)
+{
+ struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+ tps65912->irq_mask |= (1 << irq_to_tps65912_irq(tps65912, data->irq));
+}
+
+static struct irq_chip tps65912_irq_chip = {
+ .name = "tps65912",
+ .irq_bus_lock = tps65912_irq_lock,
+ .irq_bus_sync_unlock = tps65912_irq_sync_unlock,
+ .irq_disable = tps65912_irq_disable,
+ .irq_enable = tps65912_irq_enable,
+};
+
+int tps65912_irq_init(struct tps65912 *tps65912, int irq,
+ struct tps65912_platform_data *pdata)
+{
+ int ret, cur_irq;
+ int flags = IRQF_ONESHOT;
+ u8 reg;
+
+ if (!irq) {
+ dev_warn(tps65912->dev, "No interrupt support, no core IRQ\n");
+ return 0;
+ }
+
+ if (!pdata || !pdata->irq_base) {
+ dev_warn(tps65912->dev, "No interrupt support, no IRQ base\n");
+ return 0;
+ }
+
+ /* Clear unattended interrupts */
+ tps65912->read(tps65912, TPS65912_INT_STS, 1, &reg);
+ tps65912->write(tps65912, TPS65912_INT_STS, 1, &reg);
+ tps65912->read(tps65912, TPS65912_INT_STS2, 1, &reg);
+ tps65912->write(tps65912, TPS65912_INT_STS2, 1, &reg);
+ tps65912->read(tps65912, TPS65912_INT_STS3, 1, &reg);
+ tps65912->write(tps65912, TPS65912_INT_STS3, 1, &reg);
+ tps65912->read(tps65912, TPS65912_INT_STS4, 1, &reg);
+ tps65912->write(tps65912, TPS65912_INT_STS4, 1, &reg);
+
+ /* Mask top level interrupts */
+ tps65912->irq_mask = 0xFFFFFFFF;
+
+ mutex_init(&tps65912->irq_lock);
+ tps65912->chip_irq = irq;
+ tps65912->irq_base = pdata->irq_base;
+
+ tps65912->irq_num = TPS65912_NUM_IRQ;
+
+ /* Register with genirq */
+ for (cur_irq = tps65912->irq_base;
+ cur_irq < tps65912->irq_num + tps65912->irq_base;
+ cur_irq++) {
+ irq_set_chip_data(cur_irq, tps65912);
+ irq_set_chip_and_handler(cur_irq, &tps65912_irq_chip,
+ handle_edge_irq);
+ irq_set_nested_thread(cur_irq, 1);
+ /* ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ irq_set_noprobe(cur_irq);
+#endif
+ }
+
+ ret = request_threaded_irq(irq, NULL, tps65912_irq, flags,
+ "tps65912", tps65912);
+
+ irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
+ if (ret != 0)
+ dev_err(tps65912->dev, "Failed to request IRQ: %d\n", ret);
+
+ return ret;
+}
+
+int tps65912_irq_exit(struct tps65912 *tps65912)
+{
+ free_irq(tps65912->chip_irq, tps65912);
+ return 0;
+}
diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c
new file mode 100644
index 000000000000..6d71e0d25744
--- /dev/null
+++ b/drivers/mfd/tps65912-spi.c
@@ -0,0 +1,142 @@
+/*
+ * tps65912-spi.c -- SPI access for TI TPS65912x PMIC
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static int tps65912_spi_write(struct tps65912 *tps65912, u8 addr,
+ int bytes, void *src)
+{
+ struct spi_device *spi = tps65912->control_data;
+ u8 *data = (u8 *) src;
+ int ret;
+ /* bit 23 is the read/write bit */
+ unsigned long spi_data = 1 << 23 | addr << 15 | *data;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ u32 tx_buf, rx_buf;
+
+ tx_buf = spi_data;
+ rx_buf = 0;
+
+ xfer.tx_buf = &tx_buf;
+ xfer.rx_buf = NULL;
+ xfer.len = sizeof(unsigned long);
+ xfer.bits_per_word = 24;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ ret = spi_sync(spi, &msg);
+ return ret;
+}
+
+static int tps65912_spi_read(struct tps65912 *tps65912, u8 addr,
+ int bytes, void *dest)
+{
+ struct spi_device *spi = tps65912->control_data;
+ /* bit 23 is the read/write bit */
+ unsigned long spi_data = 0 << 23 | addr << 15;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ int ret;
+ u8 *data = (u8 *) dest;
+ u32 tx_buf, rx_buf;
+
+ tx_buf = spi_data;
+ rx_buf = 0;
+
+ xfer.tx_buf = &tx_buf;
+ xfer.rx_buf = &rx_buf;
+ xfer.len = sizeof(unsigned long);
+ xfer.bits_per_word = 24;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ if (spi == NULL)
+ return 0;
+
+ ret = spi_sync(spi, &msg);
+ if (ret == 0)
+ *data = (u8) (rx_buf & 0xFF);
+ return ret;
+}
+
+static int __devinit tps65912_spi_probe(struct spi_device *spi)
+{
+ struct tps65912 *tps65912;
+
+ tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
+ if (tps65912 == NULL)
+ return -ENOMEM;
+
+ tps65912->dev = &spi->dev;
+ tps65912->control_data = spi;
+ tps65912->read = tps65912_spi_read;
+ tps65912->write = tps65912_spi_write;
+
+ spi_set_drvdata(spi, tps65912);
+
+ return tps65912_device_init(tps65912);
+}
+
+static int __devexit tps65912_spi_remove(struct spi_device *spi)
+{
+ struct tps65912 *tps65912 = spi_get_drvdata(spi);
+
+ tps65912_device_exit(tps65912);
+
+ return 0;
+}
+
+static struct spi_driver tps65912_spi_driver = {
+ .driver = {
+ .name = "tps65912",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65912_spi_probe,
+ .remove = __devexit_p(tps65912_spi_remove),
+};
+
+static int __init tps65912_spi_init(void)
+{
+ int ret;
+
+ ret = spi_register_driver(&tps65912_spi_driver);
+ if (ret != 0)
+ pr_err("Failed to register TPS65912 SPI driver: %d\n", ret);
+
+ return 0;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps65912_spi_init);
+
+static void __exit tps65912_spi_exit(void)
+{
+ spi_unregister_driver(&tps65912_spi_driver);
+}
+module_exit(tps65912_spi_exit);
+
+MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("SPI support for TPS65912 chip family mfd");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index b8f2a4e7f6e7..bfbd66021afd 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -30,6 +30,7 @@
#include <linux/init.h>
#include <linux/mutex.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -109,8 +110,8 @@
#define twl_has_watchdog() false
#endif
-#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
- defined(CONFIG_SND_SOC_TWL6040) || defined(CONFIG_SND_SOC_TWL6040_MODULE)
+#if defined(CONFIG_MFD_TWL4030_AUDIO) || defined(CONFIG_MFD_TWL4030_AUDIO_MODULE) ||\
+ defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE)
#define twl_has_codec() true
#else
#define twl_has_codec() false
@@ -815,20 +816,19 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
- if (twl_has_codec() && pdata->codec && twl_class_is_4030()) {
+ if (twl_has_codec() && pdata->audio && twl_class_is_4030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
child = add_child(sub_chip_id, "twl4030-audio",
- pdata->codec, sizeof(*pdata->codec),
+ pdata->audio, sizeof(*pdata->audio),
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
- /* Phoenix codec driver is probed directly atm */
- if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
+ if (twl_has_codec() && pdata->audio && twl_class_is_6030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
- child = add_child(sub_chip_id, "twl6040-codec",
- pdata->codec, sizeof(*pdata->codec),
+ child = add_child(sub_chip_id, "twl6040",
+ pdata->audio, sizeof(*pdata->audio),
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
@@ -1284,6 +1284,8 @@ static const struct i2c_device_id twl_ids[] = {
{ "tps65950", 0 }, /* catalog version of twl5030 */
{ "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
{ "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
+ { "tps65921", TPS_SUBSET }, /* fewer LDOs; no codec, no LED
+ and vibrator. Charger in USB module*/
{ "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
{ "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */
{ /* end of list */ },
diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c
new file mode 100644
index 000000000000..ae51ab5d0e5d
--- /dev/null
+++ b/drivers/mfd/twl4030-audio.c
@@ -0,0 +1,277 @@
+/*
+ * MFD driver for twl4030 audio submodule, which contains an audio codec, and
+ * the vibra control.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl4030-audio.h>
+
+#define TWL4030_AUDIO_CELLS 2
+
+static struct platform_device *twl4030_audio_dev;
+
+struct twl4030_audio_resource {
+ int request_count;
+ u8 reg;
+ u8 mask;
+};
+
+struct twl4030_audio {
+ unsigned int audio_mclk;
+ struct mutex mutex;
+ struct twl4030_audio_resource resource[TWL4030_AUDIO_RES_MAX];
+ struct mfd_cell cells[TWL4030_AUDIO_CELLS];
+};
+
+/*
+ * Modify the resource, the function returns the content of the register
+ * after the modification.
+ */
+static int twl4030_audio_set_resource(enum twl4030_audio_res id, int enable)
+{
+ struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+ u8 val;
+
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+ audio->resource[id].reg);
+
+ if (enable)
+ val |= audio->resource[id].mask;
+ else
+ val &= ~audio->resource[id].mask;
+
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ val, audio->resource[id].reg);
+
+ return val;
+}
+
+static inline int twl4030_audio_get_resource(enum twl4030_audio_res id)
+{
+ struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+ u8 val;
+
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+ audio->resource[id].reg);
+
+ return val;
+}
+
+/*
+ * Enable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_audio_enable_resource(enum twl4030_audio_res id)
+{
+ struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+ int val;
+
+ if (id >= TWL4030_AUDIO_RES_MAX) {
+ dev_err(&twl4030_audio_dev->dev,
+ "Invalid resource ID (%u)\n", id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&audio->mutex);
+ if (!audio->resource[id].request_count)
+ /* Resource was disabled, enable it */
+ val = twl4030_audio_set_resource(id, 1);
+ else
+ val = twl4030_audio_get_resource(id);
+
+ audio->resource[id].request_count++;
+ mutex_unlock(&audio->mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource);
+
+/*
+ * Disable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_audio_disable_resource(unsigned id)
+{
+ struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+ int val;
+
+ if (id >= TWL4030_AUDIO_RES_MAX) {
+ dev_err(&twl4030_audio_dev->dev,
+ "Invalid resource ID (%u)\n", id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&audio->mutex);
+ if (!audio->resource[id].request_count) {
+ dev_err(&twl4030_audio_dev->dev,
+ "Resource has been disabled already (%u)\n", id);
+ mutex_unlock(&audio->mutex);
+ return -EPERM;
+ }
+ audio->resource[id].request_count--;
+
+ if (!audio->resource[id].request_count)
+ /* Resource can be disabled now */
+ val = twl4030_audio_set_resource(id, 0);
+ else
+ val = twl4030_audio_get_resource(id);
+
+ mutex_unlock(&audio->mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_audio_disable_resource);
+
+unsigned int twl4030_audio_get_mclk(void)
+{
+ struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+
+ return audio->audio_mclk;
+}
+EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk);
+
+static int __devinit twl4030_audio_probe(struct platform_device *pdev)
+{
+ struct twl4030_audio *audio;
+ struct twl4030_audio_data *pdata = pdev->dev.platform_data;
+ struct mfd_cell *cell = NULL;
+ int ret, childs = 0;
+ u8 val;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Platform data is missing\n");
+ return -EINVAL;
+ }
+
+ /* Configure APLL_INFREQ and disable APLL if enabled */
+ val = 0;
+ switch (pdata->audio_mclk) {
+ case 19200000:
+ val |= TWL4030_APLL_INFREQ_19200KHZ;
+ break;
+ case 26000000:
+ val |= TWL4030_APLL_INFREQ_26000KHZ;
+ break;
+ case 38400000:
+ val |= TWL4030_APLL_INFREQ_38400KHZ;
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid audio_mclk\n");
+ return -EINVAL;
+ }
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ val, TWL4030_REG_APLL_CTL);
+
+ audio = kzalloc(sizeof(struct twl4030_audio), GFP_KERNEL);
+ if (!audio)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, audio);
+
+ twl4030_audio_dev = pdev;
+ mutex_init(&audio->mutex);
+ audio->audio_mclk = pdata->audio_mclk;
+
+ /* Codec power */
+ audio->resource[TWL4030_AUDIO_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
+ audio->resource[TWL4030_AUDIO_RES_POWER].mask = TWL4030_CODECPDZ;
+
+ /* PLL */
+ audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL;
+ audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN;
+
+ if (pdata->codec) {
+ cell = &audio->cells[childs];
+ cell->name = "twl4030-codec";
+ cell->platform_data = pdata->codec;
+ cell->pdata_size = sizeof(*pdata->codec);
+ childs++;
+ }
+ if (pdata->vibra) {
+ cell = &audio->cells[childs];
+ cell->name = "twl4030-vibra";
+ cell->platform_data = pdata->vibra;
+ cell->pdata_size = sizeof(*pdata->vibra);
+ childs++;
+ }
+
+ if (childs)
+ ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells,
+ childs, NULL, 0);
+ else {
+ dev_err(&pdev->dev, "No platform data found for childs\n");
+ ret = -ENODEV;
+ }
+
+ if (!ret)
+ return 0;
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(audio);
+ twl4030_audio_dev = NULL;
+ return ret;
+}
+
+static int __devexit twl4030_audio_remove(struct platform_device *pdev)
+{
+ struct twl4030_audio *audio = platform_get_drvdata(pdev);
+
+ mfd_remove_devices(&pdev->dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(audio);
+ twl4030_audio_dev = NULL;
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:twl4030-audio");
+
+static struct platform_driver twl4030_audio_driver = {
+ .probe = twl4030_audio_probe,
+ .remove = __devexit_p(twl4030_audio_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "twl4030-audio",
+ },
+};
+
+static int __devinit twl4030_audio_init(void)
+{
+ return platform_driver_register(&twl4030_audio_driver);
+}
+module_init(twl4030_audio_init);
+
+static void __devexit twl4030_audio_exit(void)
+{
+ platform_driver_unregister(&twl4030_audio_driver);
+}
+module_exit(twl4030_audio_exit);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
deleted file mode 100644
index 2bf4136464c1..000000000000
--- a/drivers/mfd/twl4030-codec.c
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * MFD driver for twl4030 codec submodule
- *
- * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * Copyright: (C) 2009 Nokia Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/platform_device.h>
-#include <linux/i2c/twl.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/twl4030-codec.h>
-
-#define TWL4030_CODEC_CELLS 2
-
-static struct platform_device *twl4030_codec_dev;
-
-struct twl4030_codec_resource {
- int request_count;
- u8 reg;
- u8 mask;
-};
-
-struct twl4030_codec {
- unsigned int audio_mclk;
- struct mutex mutex;
- struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
- struct mfd_cell cells[TWL4030_CODEC_CELLS];
-};
-
-/*
- * Modify the resource, the function returns the content of the register
- * after the modification.
- */
-static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
-{
- struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
- u8 val;
-
- twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
- codec->resource[id].reg);
-
- if (enable)
- val |= codec->resource[id].mask;
- else
- val &= ~codec->resource[id].mask;
-
- twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
- val, codec->resource[id].reg);
-
- return val;
-}
-
-static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
-{
- struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
- u8 val;
-
- twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
- codec->resource[id].reg);
-
- return val;
-}
-
-/*
- * Enable the resource.
- * The function returns with error or the content of the register
- */
-int twl4030_codec_enable_resource(enum twl4030_codec_res id)
-{
- struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
- int val;
-
- if (id >= TWL4030_CODEC_RES_MAX) {
- dev_err(&twl4030_codec_dev->dev,
- "Invalid resource ID (%u)\n", id);
- return -EINVAL;
- }
-
- mutex_lock(&codec->mutex);
- if (!codec->resource[id].request_count)
- /* Resource was disabled, enable it */
- val = twl4030_codec_set_resource(id, 1);
- else
- val = twl4030_codec_get_resource(id);
-
- codec->resource[id].request_count++;
- mutex_unlock(&codec->mutex);
-
- return val;
-}
-EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
-
-/*
- * Disable the resource.
- * The function returns with error or the content of the register
- */
-int twl4030_codec_disable_resource(unsigned id)
-{
- struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
- int val;
-
- if (id >= TWL4030_CODEC_RES_MAX) {
- dev_err(&twl4030_codec_dev->dev,
- "Invalid resource ID (%u)\n", id);
- return -EINVAL;
- }
-
- mutex_lock(&codec->mutex);
- if (!codec->resource[id].request_count) {
- dev_err(&twl4030_codec_dev->dev,
- "Resource has been disabled already (%u)\n", id);
- mutex_unlock(&codec->mutex);
- return -EPERM;
- }
- codec->resource[id].request_count--;
-
- if (!codec->resource[id].request_count)
- /* Resource can be disabled now */
- val = twl4030_codec_set_resource(id, 0);
- else
- val = twl4030_codec_get_resource(id);
-
- mutex_unlock(&codec->mutex);
-
- return val;
-}
-EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
-
-unsigned int twl4030_codec_get_mclk(void)
-{
- struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
-
- return codec->audio_mclk;
-}
-EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk);
-
-static int __devinit twl4030_codec_probe(struct platform_device *pdev)
-{
- struct twl4030_codec *codec;
- struct twl4030_codec_data *pdata = pdev->dev.platform_data;
- struct mfd_cell *cell = NULL;
- int ret, childs = 0;
- u8 val;
-
- if (!pdata) {
- dev_err(&pdev->dev, "Platform data is missing\n");
- return -EINVAL;
- }
-
- /* Configure APLL_INFREQ and disable APLL if enabled */
- val = 0;
- switch (pdata->audio_mclk) {
- case 19200000:
- val |= TWL4030_APLL_INFREQ_19200KHZ;
- break;
- case 26000000:
- val |= TWL4030_APLL_INFREQ_26000KHZ;
- break;
- case 38400000:
- val |= TWL4030_APLL_INFREQ_38400KHZ;
- break;
- default:
- dev_err(&pdev->dev, "Invalid audio_mclk\n");
- return -EINVAL;
- }
- twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
- val, TWL4030_REG_APLL_CTL);
-
- codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
- if (!codec)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, codec);
-
- twl4030_codec_dev = pdev;
- mutex_init(&codec->mutex);
- codec->audio_mclk = pdata->audio_mclk;
-
- /* Codec power */
- codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
- codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
-
- /* PLL */
- codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
- codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
-
- if (pdata->audio) {
- cell = &codec->cells[childs];
- cell->name = "twl4030-codec";
- cell->platform_data = pdata->audio;
- cell->pdata_size = sizeof(*pdata->audio);
- childs++;
- }
- if (pdata->vibra) {
- cell = &codec->cells[childs];
- cell->name = "twl4030-vibra";
- cell->platform_data = pdata->vibra;
- cell->pdata_size = sizeof(*pdata->vibra);
- childs++;
- }
-
- if (childs)
- ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
- childs, NULL, 0);
- else {
- dev_err(&pdev->dev, "No platform data found for childs\n");
- ret = -ENODEV;
- }
-
- if (!ret)
- return 0;
-
- platform_set_drvdata(pdev, NULL);
- kfree(codec);
- twl4030_codec_dev = NULL;
- return ret;
-}
-
-static int __devexit twl4030_codec_remove(struct platform_device *pdev)
-{
- struct twl4030_codec *codec = platform_get_drvdata(pdev);
-
- mfd_remove_devices(&pdev->dev);
- platform_set_drvdata(pdev, NULL);
- kfree(codec);
- twl4030_codec_dev = NULL;
-
- return 0;
-}
-
-MODULE_ALIAS("platform:twl4030-audio");
-
-static struct platform_driver twl4030_codec_driver = {
- .probe = twl4030_codec_probe,
- .remove = __devexit_p(twl4030_codec_remove),
- .driver = {
- .owner = THIS_MODULE,
- .name = "twl4030-audio",
- },
-};
-
-static int __devinit twl4030_codec_init(void)
-{
- return platform_driver_register(&twl4030_codec_driver);
-}
-module_init(twl4030_codec_init);
-
-static void __devexit twl4030_codec_exit(void)
-{
- platform_driver_unregister(&twl4030_codec_driver);
-}
-module_exit(twl4030_codec_exit);
-
-MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
-MODULE_LICENSE("GPL");
-
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index 8a7ee3139b86..f062c8cc6c38 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -30,7 +30,6 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
-#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/i2c/twl.h>
@@ -278,59 +277,6 @@ static const struct sih sih_modules_twl5031[8] = {
static unsigned twl4030_irq_base;
-static struct completion irq_event;
-
-/*
- * This thread processes interrupts reported by the Primary Interrupt Handler.
- */
-static int twl4030_irq_thread(void *data)
-{
- long irq = (long)data;
- static unsigned i2c_errors;
- static const unsigned max_i2c_errors = 100;
-
-
- current->flags |= PF_NOFREEZE;
-
- while (!kthread_should_stop()) {
- int ret;
- int module_irq;
- u8 pih_isr;
-
- /* Wait for IRQ, then read PIH irq status (also blocking) */
- wait_for_completion_interruptible(&irq_event);
-
- ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
- REG_PIH_ISR_P1);
- if (ret) {
- pr_warning("twl4030: I2C error %d reading PIH ISR\n",
- ret);
- if (++i2c_errors >= max_i2c_errors) {
- printk(KERN_ERR "Maximum I2C error count"
- " exceeded. Terminating %s.\n",
- __func__);
- break;
- }
- complete(&irq_event);
- continue;
- }
-
- /* these handlers deal with the relevant SIH irq status */
- local_irq_disable();
- for (module_irq = twl4030_irq_base;
- pih_isr;
- pih_isr >>= 1, module_irq++) {
- if (pih_isr & 0x1)
- generic_handle_irq(module_irq);
- }
- local_irq_enable();
-
- enable_irq(irq);
- }
-
- return 0;
-}
-
/*
* handle_twl4030_pih() is the desc->handle method for the twl4030 interrupt.
* This is a chained interrupt, so there is no desc->action method for it.
@@ -342,9 +288,25 @@ static int twl4030_irq_thread(void *data)
*/
static irqreturn_t handle_twl4030_pih(int irq, void *devid)
{
- /* Acknowledge, clear *AND* mask the interrupt... */
- disable_irq_nosync(irq);
- complete(devid);
+ int module_irq;
+ irqreturn_t ret;
+ u8 pih_isr;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
+ REG_PIH_ISR_P1);
+ if (ret) {
+ pr_warning("twl4030: I2C error %d reading PIH ISR\n", ret);
+ return IRQ_NONE;
+ }
+
+ /* these handlers deal with the relevant SIH irq status */
+ for (module_irq = twl4030_irq_base;
+ pih_isr;
+ pih_isr >>= 1, module_irq++) {
+ if (pih_isr & 0x1)
+ handle_nested_irq(module_irq);
+ }
+
return IRQ_HANDLED;
}
/*----------------------------------------------------------------------*/
@@ -460,113 +422,17 @@ static inline void activate_irq(int irq)
/*----------------------------------------------------------------------*/
-static DEFINE_SPINLOCK(sih_agent_lock);
-
-static struct workqueue_struct *wq;
-
struct sih_agent {
int irq_base;
const struct sih *sih;
u32 imr;
bool imr_change_pending;
- struct work_struct mask_work;
-
- u32 edge_change;
- struct work_struct edge_work;
-};
-
-static void twl4030_sih_do_mask(struct work_struct *work)
-{
- struct sih_agent *agent;
- const struct sih *sih;
- union {
- u8 bytes[4];
- u32 word;
- } imr;
- int status;
- agent = container_of(work, struct sih_agent, mask_work);
-
- /* see what work we have */
- spin_lock_irq(&sih_agent_lock);
- if (agent->imr_change_pending) {
- sih = agent->sih;
- /* byte[0] gets overwritten as we write ... */
- imr.word = cpu_to_le32(agent->imr << 8);
- agent->imr_change_pending = false;
- } else
- sih = NULL;
- spin_unlock_irq(&sih_agent_lock);
- if (!sih)
- return;
-
- /* write the whole mask ... simpler than subsetting it */
- status = twl_i2c_write(sih->module, imr.bytes,
- sih->mask[irq_line].imr_offset, sih->bytes_ixr);
- if (status)
- pr_err("twl4030: %s, %s --> %d\n", __func__,
- "write", status);
-}
-
-static void twl4030_sih_do_edge(struct work_struct *work)
-{
- struct sih_agent *agent;
- const struct sih *sih;
- u8 bytes[6];
u32 edge_change;
- int status;
-
- agent = container_of(work, struct sih_agent, edge_work);
-
- /* see what work we have */
- spin_lock_irq(&sih_agent_lock);
- edge_change = agent->edge_change;
- agent->edge_change = 0;
- sih = edge_change ? agent->sih : NULL;
- spin_unlock_irq(&sih_agent_lock);
- if (!sih)
- return;
-
- /* Read, reserving first byte for write scratch. Yes, this
- * could be cached for some speedup ... but be careful about
- * any processor on the other IRQ line, EDR registers are
- * shared.
- */
- status = twl_i2c_read(sih->module, bytes + 1,
- sih->edr_offset, sih->bytes_edr);
- if (status) {
- pr_err("twl4030: %s, %s --> %d\n", __func__,
- "read", status);
- return;
- }
-
- /* Modify only the bits we know must change */
- while (edge_change) {
- int i = fls(edge_change) - 1;
- struct irq_data *idata = irq_get_irq_data(i + agent->irq_base);
- int byte = 1 + (i >> 2);
- int off = (i & 0x3) * 2;
- unsigned int type;
-
- bytes[byte] &= ~(0x03 << off);
- type = irqd_get_trigger_type(idata);
- if (type & IRQ_TYPE_EDGE_RISING)
- bytes[byte] |= BIT(off + 1);
- if (type & IRQ_TYPE_EDGE_FALLING)
- bytes[byte] |= BIT(off + 0);
-
- edge_change &= ~BIT(i);
- }
-
- /* Write */
- status = twl_i2c_write(sih->module, bytes,
- sih->edr_offset, sih->bytes_edr);
- if (status)
- pr_err("twl4030: %s, %s --> %d\n", __func__,
- "write", status);
-}
+ struct mutex irq_lock;
+};
/*----------------------------------------------------------------------*/
@@ -579,50 +445,125 @@ static void twl4030_sih_do_edge(struct work_struct *work)
static void twl4030_sih_mask(struct irq_data *data)
{
- struct sih_agent *sih = irq_data_get_irq_chip_data(data);
- unsigned long flags;
-
- spin_lock_irqsave(&sih_agent_lock, flags);
- sih->imr |= BIT(data->irq - sih->irq_base);
- sih->imr_change_pending = true;
- queue_work(wq, &sih->mask_work);
- spin_unlock_irqrestore(&sih_agent_lock, flags);
+ struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+
+ agent->imr |= BIT(data->irq - agent->irq_base);
+ agent->imr_change_pending = true;
}
static void twl4030_sih_unmask(struct irq_data *data)
{
- struct sih_agent *sih = irq_data_get_irq_chip_data(data);
- unsigned long flags;
-
- spin_lock_irqsave(&sih_agent_lock, flags);
- sih->imr &= ~BIT(data->irq - sih->irq_base);
- sih->imr_change_pending = true;
- queue_work(wq, &sih->mask_work);
- spin_unlock_irqrestore(&sih_agent_lock, flags);
+ struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+
+ agent->imr &= ~BIT(data->irq - agent->irq_base);
+ agent->imr_change_pending = true;
}
static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger)
{
- struct sih_agent *sih = irq_data_get_irq_chip_data(data);
- unsigned long flags;
+ struct sih_agent *agent = irq_data_get_irq_chip_data(data);
if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
return -EINVAL;
- spin_lock_irqsave(&sih_agent_lock, flags);
- if (irqd_get_trigger_type(data) != trigger) {
- sih->edge_change |= BIT(data->irq - sih->irq_base);
- queue_work(wq, &sih->edge_work);
- }
- spin_unlock_irqrestore(&sih_agent_lock, flags);
+ if (irqd_get_trigger_type(data) != trigger)
+ agent->edge_change |= BIT(data->irq - agent->irq_base);
+
return 0;
}
+static void twl4030_sih_bus_lock(struct irq_data *data)
+{
+ struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&agent->irq_lock);
+}
+
+static void twl4030_sih_bus_sync_unlock(struct irq_data *data)
+{
+ struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+ const struct sih *sih = agent->sih;
+ int status;
+
+ if (agent->imr_change_pending) {
+ union {
+ u32 word;
+ u8 bytes[4];
+ } imr;
+
+ /* byte[0] gets overwriten as we write ... */
+ imr.word = cpu_to_le32(agent->imr << 8);
+ agent->imr_change_pending = false;
+
+ /* write the whole mask ... simpler than subsetting it */
+ status = twl_i2c_write(sih->module, imr.bytes,
+ sih->mask[irq_line].imr_offset,
+ sih->bytes_ixr);
+ if (status)
+ pr_err("twl4030: %s, %s --> %d\n", __func__,
+ "write", status);
+ }
+
+ if (agent->edge_change) {
+ u32 edge_change;
+ u8 bytes[6];
+
+ edge_change = agent->edge_change;
+ agent->edge_change = 0;
+
+ /*
+ * Read, reserving first byte for write scratch. Yes, this
+ * could be cached for some speedup ... but be careful about
+ * any processor on the other IRQ line, EDR registers are
+ * shared.
+ */
+ status = twl_i2c_read(sih->module, bytes + 1,
+ sih->edr_offset, sih->bytes_edr);
+ if (status) {
+ pr_err("twl4030: %s, %s --> %d\n", __func__,
+ "read", status);
+ return;
+ }
+
+ /* Modify only the bits we know must change */
+ while (edge_change) {
+ int i = fls(edge_change) - 1;
+ struct irq_data *idata;
+ int byte = 1 + (i >> 2);
+ int off = (i & 0x3) * 2;
+ unsigned int type;
+
+ idata = irq_get_irq_data(i + agent->irq_base);
+
+ bytes[byte] &= ~(0x03 << off);
+
+ type = irqd_get_trigger_type(idata);
+ if (type & IRQ_TYPE_EDGE_RISING)
+ bytes[byte] |= BIT(off + 1);
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ bytes[byte] |= BIT(off + 0);
+
+ edge_change &= ~BIT(i);
+ }
+
+ /* Write */
+ status = twl_i2c_write(sih->module, bytes,
+ sih->edr_offset, sih->bytes_edr);
+ if (status)
+ pr_err("twl4030: %s, %s --> %d\n", __func__,
+ "write", status);
+ }
+
+ mutex_unlock(&agent->irq_lock);
+}
+
static struct irq_chip twl4030_sih_irq_chip = {
.name = "twl4030",
- .irq_mask = twl4030_sih_mask,
+ .irq_mask = twl4030_sih_mask,
.irq_unmask = twl4030_sih_unmask,
.irq_set_type = twl4030_sih_set_type,
+ .irq_bus_lock = twl4030_sih_bus_lock,
+ .irq_bus_sync_unlock = twl4030_sih_bus_sync_unlock,
};
/*----------------------------------------------------------------------*/
@@ -655,9 +596,7 @@ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc)
int isr;
/* reading ISR acks the IRQs, using clear-on-read mode */
- local_irq_enable();
isr = sih_read_isr(sih);
- local_irq_disable();
if (isr < 0) {
pr_err("twl4030: %s SIH, read ISR error %d\n",
@@ -672,7 +611,7 @@ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc)
isr &= ~BIT(irq);
if (irq < sih->bits)
- generic_handle_irq(agent->irq_base + irq);
+ handle_nested_irq(agent->irq_base + irq);
else
pr_err("twl4030: %s SIH, invalid ISR bit %d\n",
sih->name, irq);
@@ -718,15 +657,14 @@ int twl4030_sih_setup(int module)
agent->irq_base = irq_base;
agent->sih = sih;
agent->imr = ~0;
- INIT_WORK(&agent->mask_work, twl4030_sih_do_mask);
- INIT_WORK(&agent->edge_work, twl4030_sih_do_edge);
+ mutex_init(&agent->irq_lock);
for (i = 0; i < sih->bits; i++) {
irq = irq_base + i;
+ irq_set_chip_data(irq, agent);
irq_set_chip_and_handler(irq, &twl4030_sih_irq_chip,
handle_edge_irq);
- irq_set_chip_data(irq, agent);
activate_irq(irq);
}
@@ -758,7 +696,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
int status;
int i;
- struct task_struct *task;
/*
* Mask and clear all TWL4030 interrupts since initially we do
@@ -768,12 +705,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
if (status < 0)
return status;
- wq = create_singlethread_workqueue("twl4030-irqchip");
- if (!wq) {
- pr_err("twl4030: workqueue FAIL\n");
- return -ESRCH;
- }
-
twl4030_irq_base = irq_base;
/* install an irq handler for each of the SIH modules;
@@ -787,6 +718,7 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
for (i = irq_base; i < irq_end; i++) {
irq_set_chip_and_handler(i, &twl4030_irq_chip,
handle_simple_irq);
+ irq_set_nested_thread(i, 1);
activate_irq(i);
}
twl4030_irq_next = i;
@@ -801,34 +733,22 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
}
/* install an irq handler to demultiplex the TWL4030 interrupt */
-
-
- init_completion(&irq_event);
-
- status = request_irq(irq_num, handle_twl4030_pih, IRQF_DISABLED,
- "TWL4030-PIH", &irq_event);
+ status = request_threaded_irq(irq_num, NULL, handle_twl4030_pih, 0,
+ "TWL4030-PIH", NULL);
if (status < 0) {
pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status);
goto fail_rqirq;
}
- task = kthread_run(twl4030_irq_thread, (void *)(long)irq_num,
- "twl4030-irq");
- if (IS_ERR(task)) {
- pr_err("twl4030: could not create irq %d thread!\n", irq_num);
- status = PTR_ERR(task);
- goto fail_kthread;
- }
return status;
-fail_kthread:
- free_irq(irq_num, &irq_event);
fail_rqirq:
/* clean up twl4030_sih_setup */
fail:
- for (i = irq_base; i < irq_end; i++)
+ for (i = irq_base; i < irq_end; i++) {
+ irq_set_nested_thread(i, 0);
irq_set_chip_and_handler(i, NULL, NULL);
- destroy_workqueue(wq);
- wq = NULL;
+ }
+
return status;
}
diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
index 3941ddcf15fe..834f824d3c11 100644
--- a/drivers/mfd/twl4030-madc.c
+++ b/drivers/mfd/twl4030-madc.c
@@ -510,8 +510,9 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
u8 ch_msb, ch_lsb;
int ret;
- if (!req)
+ if (!req || !twl4030_madc)
return -EINVAL;
+
mutex_lock(&twl4030_madc->lock);
if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
ret = -EINVAL;
@@ -530,13 +531,13 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
if (ret) {
dev_err(twl4030_madc->dev,
"unable to write sel register 0x%X\n", method->sel + 1);
- return ret;
+ goto out;
}
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
if (ret) {
dev_err(twl4030_madc->dev,
"unable to write sel register 0x%X\n", method->sel + 1);
- return ret;
+ goto out;
}
/* Select averaging for all channels if do_avg is set */
if (req->do_avg) {
@@ -546,7 +547,7 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
dev_err(twl4030_madc->dev,
"unable to write avg register 0x%X\n",
method->avg + 1);
- return ret;
+ goto out;
}
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
ch_lsb, method->avg);
@@ -554,7 +555,7 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
dev_err(twl4030_madc->dev,
"unable to write sel reg 0x%X\n",
method->sel + 1);
- return ret;
+ goto out;
}
}
if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
@@ -706,6 +707,8 @@ static int __devinit twl4030_madc_probe(struct platform_device *pdev)
if (!madc)
return -ENOMEM;
+ madc->dev = &pdev->dev;
+
/*
* Phoenix provides 2 interrupt lines. The first one is connected to
* the OMAP. The other one can be connected to the other processor such
@@ -737,6 +740,28 @@ static int __devinit twl4030_madc_probe(struct platform_device *pdev)
TWL4030_BCI_BCICTL1);
goto err_i2c;
}
+
+ /* Check that MADC clock is on */
+ ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &regval, TWL4030_REG_GPBR1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
+ TWL4030_REG_GPBR1);
+ goto err_i2c;
+ }
+
+ /* If MADC clk is not on, turn it on */
+ if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
+ dev_info(&pdev->dev, "clk disabled, enabling\n");
+ regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
+ TWL4030_REG_GPBR1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
+ TWL4030_REG_GPBR1);
+ goto err_i2c;
+ }
+ }
+
platform_set_drvdata(pdev, madc);
mutex_init(&madc->lock);
ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL,
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index eb3b5f88e566..3eee45ffb096 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -32,11 +32,13 @@
*/
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kthread.h>
#include <linux/i2c/twl.h>
#include <linux/platform_device.h>
+#include <linux/suspend.h>
#include "twl-core.h"
@@ -83,8 +85,48 @@ static int twl6030_interrupt_mapping[24] = {
/*----------------------------------------------------------------------*/
static unsigned twl6030_irq_base;
+static int twl_irq;
+static bool twl_irq_wake_enabled;
static struct completion irq_event;
+static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0);
+
+static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event, void *unused)
+{
+ int chained_wakeups;
+
+ switch (pm_event) {
+ case PM_SUSPEND_PREPARE:
+ chained_wakeups = atomic_read(&twl6030_wakeirqs);
+
+ if (chained_wakeups && !twl_irq_wake_enabled) {
+ if (enable_irq_wake(twl_irq))
+ pr_err("twl6030 IRQ wake enable failed\n");
+ else
+ twl_irq_wake_enabled = true;
+ } else if (!chained_wakeups && twl_irq_wake_enabled) {
+ disable_irq_wake(twl_irq);
+ twl_irq_wake_enabled = false;
+ }
+
+ disable_irq(twl_irq);
+ break;
+
+ case PM_POST_SUSPEND:
+ enable_irq(twl_irq);
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block twl6030_irq_pm_notifier_block = {
+ .notifier_call = twl6030_irq_pm_notifier,
+};
/*
* This thread processes interrupts reported by the Primary Interrupt Handler.
@@ -187,6 +229,16 @@ static inline void activate_irq(int irq)
#endif
}
+int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ if (on)
+ atomic_inc(&twl6030_wakeirqs);
+ else
+ atomic_dec(&twl6030_wakeirqs);
+
+ return 0;
+}
+
/*----------------------------------------------------------------------*/
static unsigned twl6030_irq_next;
@@ -318,10 +370,12 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
twl6030_irq_chip = dummy_irq_chip;
twl6030_irq_chip.name = "twl6030";
twl6030_irq_chip.irq_set_type = NULL;
+ twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake;
for (i = irq_base; i < irq_end; i++) {
irq_set_chip_and_handler(i, &twl6030_irq_chip,
handle_simple_irq);
+ irq_set_chip_data(i, (void *)irq_num);
activate_irq(i);
}
@@ -331,6 +385,14 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
/* install an irq handler to demultiplex the TWL6030 interrupt */
init_completion(&irq_event);
+
+ status = request_irq(irq_num, handle_twl6030_pih, 0,
+ "TWL6030-PIH", &irq_event);
+ if (status < 0) {
+ pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
+ goto fail_irq;
+ }
+
task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
if (IS_ERR(task)) {
pr_err("twl6030: could not create irq %d thread!\n", irq_num);
@@ -338,17 +400,14 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
goto fail_kthread;
}
- status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED,
- "TWL6030-PIH", &irq_event);
- if (status < 0) {
- pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
- goto fail_irq;
- }
+ twl_irq = irq_num;
+ register_pm_notifier(&twl6030_irq_pm_notifier_block);
return status;
-fail_irq:
- free_irq(irq_num, &irq_event);
fail_kthread:
+ free_irq(irq_num, &irq_event);
+
+fail_irq:
for (i = irq_base; i < irq_end; i++)
irq_set_chip_and_handler(i, NULL, NULL);
return status;
@@ -356,6 +415,7 @@ fail_kthread:
int twl6030_exit_irq(void)
{
+ unregister_pm_notifier(&twl6030_irq_pm_notifier_block);
if (twl6030_irq_base) {
pr_err("twl6030: can't yet clean up IRQs?\n");
diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c
index 5d25bdc78424..e8fee147678d 100644
--- a/drivers/mfd/twl6030-pwm.c
+++ b/drivers/mfd/twl6030-pwm.c
@@ -161,3 +161,5 @@ void pwm_free(struct pwm_device *pwm)
kfree(pwm);
}
EXPORT_SYMBOL(pwm_free);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c
new file mode 100644
index 000000000000..268f80fd0439
--- /dev/null
+++ b/drivers/mfd/twl6040-core.c
@@ -0,0 +1,639 @@
+/*
+ * MFD driver for TWL6040 audio device
+ *
+ * Authors: Misael Lopez Cruz <misael.lopez@ti.com>
+ * Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Copyright: (C) 2011 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl6040.h>
+
+#define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1)
+
+int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
+{
+ int ret;
+ u8 val = 0;
+
+ mutex_lock(&twl6040->io_mutex);
+ /* Vibra control registers from cache */
+ if (unlikely(reg == TWL6040_REG_VIBCTLL ||
+ reg == TWL6040_REG_VIBCTLR)) {
+ val = twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)];
+ } else {
+ ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg);
+ if (ret < 0) {
+ mutex_unlock(&twl6040->io_mutex);
+ return ret;
+ }
+ }
+ mutex_unlock(&twl6040->io_mutex);
+
+ return val;
+}
+EXPORT_SYMBOL(twl6040_reg_read);
+
+int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
+{
+ int ret;
+
+ mutex_lock(&twl6040->io_mutex);
+ ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg);
+ /* Cache the vibra control registers */
+ if (reg == TWL6040_REG_VIBCTLL || reg == TWL6040_REG_VIBCTLR)
+ twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)] = val;
+ mutex_unlock(&twl6040->io_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(twl6040_reg_write);
+
+int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
+{
+ int ret;
+ u8 val;
+
+ mutex_lock(&twl6040->io_mutex);
+ ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg);
+ if (ret)
+ goto out;
+
+ val |= mask;
+ ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg);
+out:
+ mutex_unlock(&twl6040->io_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(twl6040_set_bits);
+
+int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
+{
+ int ret;
+ u8 val;
+
+ mutex_lock(&twl6040->io_mutex);
+ ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg);
+ if (ret)
+ goto out;
+
+ val &= ~mask;
+ ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg);
+out:
+ mutex_unlock(&twl6040->io_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(twl6040_clear_bits);
+
+/* twl6040 codec manual power-up sequence */
+static int twl6040_power_up(struct twl6040 *twl6040)
+{
+ u8 ldoctl, ncpctl, lppllctl;
+ int ret;
+
+ /* enable high-side LDO, reference system and internal oscillator */
+ ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA;
+ ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+ if (ret)
+ return ret;
+ usleep_range(10000, 10500);
+
+ /* enable negative charge pump */
+ ncpctl = TWL6040_NCPENA;
+ ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
+ if (ret)
+ goto ncp_err;
+ usleep_range(1000, 1500);
+
+ /* enable low-side LDO */
+ ldoctl |= TWL6040_LSLDOENA;
+ ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+ if (ret)
+ goto lsldo_err;
+ usleep_range(1000, 1500);
+
+ /* enable low-power PLL */
+ lppllctl = TWL6040_LPLLENA;
+ ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+ if (ret)
+ goto lppll_err;
+ usleep_range(5000, 5500);
+
+ /* disable internal oscillator */
+ ldoctl &= ~TWL6040_OSCENA;
+ ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+ if (ret)
+ goto osc_err;
+
+ return 0;
+
+osc_err:
+ lppllctl &= ~TWL6040_LPLLENA;
+ twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+lppll_err:
+ ldoctl &= ~TWL6040_LSLDOENA;
+ twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+lsldo_err:
+ ncpctl &= ~TWL6040_NCPENA;
+ twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
+ncp_err:
+ ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
+ twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+
+ return ret;
+}
+
+/* twl6040 manual power-down sequence */
+static void twl6040_power_down(struct twl6040 *twl6040)
+{
+ u8 ncpctl, ldoctl, lppllctl;
+
+ ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL);
+ ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL);
+ lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
+
+ /* enable internal oscillator */
+ ldoctl |= TWL6040_OSCENA;
+ twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+ usleep_range(1000, 1500);
+
+ /* disable low-power PLL */
+ lppllctl &= ~TWL6040_LPLLENA;
+ twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+
+ /* disable low-side LDO */
+ ldoctl &= ~TWL6040_LSLDOENA;
+ twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+
+ /* disable negative charge pump */
+ ncpctl &= ~TWL6040_NCPENA;
+ twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
+
+ /* disable high-side LDO, reference system and internal oscillator */
+ ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
+ twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+}
+
+static irqreturn_t twl6040_naudint_handler(int irq, void *data)
+{
+ struct twl6040 *twl6040 = data;
+ u8 intid, status;
+
+ intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+
+ if (intid & TWL6040_READYINT)
+ complete(&twl6040->ready);
+
+ if (intid & TWL6040_THINT) {
+ status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
+ if (status & TWL6040_TSHUTDET) {
+ dev_warn(twl6040->dev,
+ "Thermal shutdown, powering-off");
+ twl6040_power(twl6040, 0);
+ } else {
+ dev_warn(twl6040->dev,
+ "Leaving thermal shutdown, powering-on");
+ twl6040_power(twl6040, 1);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int twl6040_power_up_completion(struct twl6040 *twl6040,
+ int naudint)
+{
+ int time_left;
+ u8 intid;
+
+ time_left = wait_for_completion_timeout(&twl6040->ready,
+ msecs_to_jiffies(144));
+ if (!time_left) {
+ intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+ if (!(intid & TWL6040_READYINT)) {
+ dev_err(twl6040->dev,
+ "timeout waiting for READYINT\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+int twl6040_power(struct twl6040 *twl6040, int on)
+{
+ int audpwron = twl6040->audpwron;
+ int naudint = twl6040->irq;
+ int ret = 0;
+
+ mutex_lock(&twl6040->mutex);
+
+ if (on) {
+ /* already powered-up */
+ if (twl6040->power_count++)
+ goto out;
+
+ if (gpio_is_valid(audpwron)) {
+ /* use AUDPWRON line */
+ gpio_set_value(audpwron, 1);
+ /* wait for power-up completion */
+ ret = twl6040_power_up_completion(twl6040, naudint);
+ if (ret) {
+ dev_err(twl6040->dev,
+ "automatic power-down failed\n");
+ twl6040->power_count = 0;
+ goto out;
+ }
+ } else {
+ /* use manual power-up sequence */
+ ret = twl6040_power_up(twl6040);
+ if (ret) {
+ dev_err(twl6040->dev,
+ "manual power-up failed\n");
+ twl6040->power_count = 0;
+ goto out;
+ }
+ }
+ /* Default PLL configuration after power up */
+ twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
+ twl6040->sysclk = 19200000;
+ } else {
+ /* already powered-down */
+ if (!twl6040->power_count) {
+ dev_err(twl6040->dev,
+ "device is already powered-off\n");
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (--twl6040->power_count)
+ goto out;
+
+ if (gpio_is_valid(audpwron)) {
+ /* use AUDPWRON line */
+ gpio_set_value(audpwron, 0);
+
+ /* power-down sequence latency */
+ usleep_range(500, 700);
+ } else {
+ /* use manual power-down sequence */
+ twl6040_power_down(twl6040);
+ }
+ twl6040->sysclk = 0;
+ }
+
+out:
+ mutex_unlock(&twl6040->mutex);
+ return ret;
+}
+EXPORT_SYMBOL(twl6040_power);
+
+int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ u8 hppllctl, lppllctl;
+ int ret = 0;
+
+ mutex_lock(&twl6040->mutex);
+
+ hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL);
+ lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
+
+ switch (pll_id) {
+ case TWL6040_SYSCLK_SEL_LPPLL:
+ /* low-power PLL divider */
+ switch (freq_out) {
+ case 17640000:
+ lppllctl |= TWL6040_LPLLFIN;
+ break;
+ case 19200000:
+ lppllctl &= ~TWL6040_LPLLFIN;
+ break;
+ default:
+ dev_err(twl6040->dev,
+ "freq_out %d not supported\n", freq_out);
+ ret = -EINVAL;
+ goto pll_out;
+ }
+ twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+
+ switch (freq_in) {
+ case 32768:
+ lppllctl |= TWL6040_LPLLENA;
+ twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
+ lppllctl);
+ mdelay(5);
+ lppllctl &= ~TWL6040_HPLLSEL;
+ twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
+ lppllctl);
+ hppllctl &= ~TWL6040_HPLLENA;
+ twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
+ hppllctl);
+ break;
+ default:
+ dev_err(twl6040->dev,
+ "freq_in %d not supported\n", freq_in);
+ ret = -EINVAL;
+ goto pll_out;
+ }
+ break;
+ case TWL6040_SYSCLK_SEL_HPPLL:
+ /* high-performance PLL can provide only 19.2 MHz */
+ if (freq_out != 19200000) {
+ dev_err(twl6040->dev,
+ "freq_out %d not supported\n", freq_out);
+ ret = -EINVAL;
+ goto pll_out;
+ }
+
+ hppllctl &= ~TWL6040_MCLK_MSK;
+
+ switch (freq_in) {
+ case 12000000:
+ /* PLL enabled, active mode */
+ hppllctl |= TWL6040_MCLK_12000KHZ |
+ TWL6040_HPLLENA;
+ break;
+ case 19200000:
+ /*
+ * PLL disabled
+ * (enable PLL if MCLK jitter quality
+ * doesn't meet specification)
+ */
+ hppllctl |= TWL6040_MCLK_19200KHZ;
+ break;
+ case 26000000:
+ /* PLL enabled, active mode */
+ hppllctl |= TWL6040_MCLK_26000KHZ |
+ TWL6040_HPLLENA;
+ break;
+ case 38400000:
+ /* PLL enabled, active mode */
+ hppllctl |= TWL6040_MCLK_38400KHZ |
+ TWL6040_HPLLENA;
+ break;
+ default:
+ dev_err(twl6040->dev,
+ "freq_in %d not supported\n", freq_in);
+ ret = -EINVAL;
+ goto pll_out;
+ }
+
+ /* enable clock slicer to ensure input waveform is square */
+ hppllctl |= TWL6040_HPLLSQRENA;
+
+ twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, hppllctl);
+ usleep_range(500, 700);
+ lppllctl |= TWL6040_HPLLSEL;
+ twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+ lppllctl &= ~TWL6040_LPLLENA;
+ twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+ break;
+ default:
+ dev_err(twl6040->dev, "unknown pll id %d\n", pll_id);
+ ret = -EINVAL;
+ goto pll_out;
+ }
+
+ twl6040->sysclk = freq_out;
+ twl6040->pll = pll_id;
+
+pll_out:
+ mutex_unlock(&twl6040->mutex);
+ return ret;
+}
+EXPORT_SYMBOL(twl6040_set_pll);
+
+int twl6040_get_pll(struct twl6040 *twl6040)
+{
+ if (twl6040->power_count)
+ return twl6040->pll;
+ else
+ return -ENODEV;
+}
+EXPORT_SYMBOL(twl6040_get_pll);
+
+unsigned int twl6040_get_sysclk(struct twl6040 *twl6040)
+{
+ return twl6040->sysclk;
+}
+EXPORT_SYMBOL(twl6040_get_sysclk);
+
+/* Get the combined status of the vibra control register */
+int twl6040_get_vibralr_status(struct twl6040 *twl6040)
+{
+ u8 status;
+
+ status = twl6040->vibra_ctrl_cache[0] | twl6040->vibra_ctrl_cache[1];
+ status &= (TWL6040_VIBENA | TWL6040_VIBSEL);
+
+ return status;
+}
+EXPORT_SYMBOL(twl6040_get_vibralr_status);
+
+static struct resource twl6040_vibra_rsrc[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource twl6040_codec_rsrc[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int __devinit twl6040_probe(struct platform_device *pdev)
+{
+ struct twl4030_audio_data *pdata = pdev->dev.platform_data;
+ struct twl6040 *twl6040;
+ struct mfd_cell *cell = NULL;
+ int ret, children = 0;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Platform data is missing\n");
+ return -EINVAL;
+ }
+
+ /* In order to operate correctly we need valid interrupt config */
+ if (!pdata->naudint_irq || !pdata->irq_base) {
+ dev_err(&pdev->dev, "Invalid IRQ configuration\n");
+ return -EINVAL;
+ }
+
+ twl6040 = kzalloc(sizeof(struct twl6040), GFP_KERNEL);
+ if (!twl6040)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, twl6040);
+
+ twl6040->dev = &pdev->dev;
+ twl6040->irq = pdata->naudint_irq;
+ twl6040->irq_base = pdata->irq_base;
+
+ mutex_init(&twl6040->mutex);
+ mutex_init(&twl6040->io_mutex);
+ init_completion(&twl6040->ready);
+
+ twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
+
+ /* ERRATA: Automatic power-up is not possible in ES1.0 */
+ if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
+ twl6040->audpwron = pdata->audpwron_gpio;
+ else
+ twl6040->audpwron = -EINVAL;
+
+ if (gpio_is_valid(twl6040->audpwron)) {
+ ret = gpio_request(twl6040->audpwron, "audpwron");
+ if (ret)
+ goto gpio1_err;
+
+ ret = gpio_direction_output(twl6040->audpwron, 0);
+ if (ret)
+ goto gpio2_err;
+ }
+
+ /* codec interrupt */
+ ret = twl6040_irq_init(twl6040);
+ if (ret)
+ goto gpio2_err;
+
+ ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY,
+ NULL, twl6040_naudint_handler, 0,
+ "twl6040_irq_ready", twl6040);
+ if (ret) {
+ dev_err(twl6040->dev, "READY IRQ request failed: %d\n",
+ ret);
+ goto irq_err;
+ }
+
+ /* dual-access registers controlled by I2C only */
+ twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
+
+ if (pdata->codec) {
+ int irq = twl6040->irq_base + TWL6040_IRQ_PLUG;
+
+ cell = &twl6040->cells[children];
+ cell->name = "twl6040-codec";
+ twl6040_codec_rsrc[0].start = irq;
+ twl6040_codec_rsrc[0].end = irq;
+ cell->resources = twl6040_codec_rsrc;
+ cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
+ cell->platform_data = pdata->codec;
+ cell->pdata_size = sizeof(*pdata->codec);
+ children++;
+ }
+
+ if (pdata->vibra) {
+ int irq = twl6040->irq_base + TWL6040_IRQ_VIB;
+
+ cell = &twl6040->cells[children];
+ cell->name = "twl6040-vibra";
+ twl6040_vibra_rsrc[0].start = irq;
+ twl6040_vibra_rsrc[0].end = irq;
+ cell->resources = twl6040_vibra_rsrc;
+ cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
+
+ cell->platform_data = pdata->vibra;
+ cell->pdata_size = sizeof(*pdata->vibra);
+ children++;
+ }
+
+ if (children) {
+ ret = mfd_add_devices(&pdev->dev, pdev->id, twl6040->cells,
+ children, NULL, 0);
+ if (ret)
+ goto mfd_err;
+ } else {
+ dev_err(&pdev->dev, "No platform data found for children\n");
+ ret = -ENODEV;
+ goto mfd_err;
+ }
+
+ return 0;
+
+mfd_err:
+ free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
+irq_err:
+ twl6040_irq_exit(twl6040);
+gpio2_err:
+ if (gpio_is_valid(twl6040->audpwron))
+ gpio_free(twl6040->audpwron);
+gpio1_err:
+ platform_set_drvdata(pdev, NULL);
+ kfree(twl6040);
+ return ret;
+}
+
+static int __devexit twl6040_remove(struct platform_device *pdev)
+{
+ struct twl6040 *twl6040 = platform_get_drvdata(pdev);
+
+ if (twl6040->power_count)
+ twl6040_power(twl6040, 0);
+
+ if (gpio_is_valid(twl6040->audpwron))
+ gpio_free(twl6040->audpwron);
+
+ free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
+ twl6040_irq_exit(twl6040);
+
+ mfd_remove_devices(&pdev->dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(twl6040);
+
+ return 0;
+}
+
+static struct platform_driver twl6040_driver = {
+ .probe = twl6040_probe,
+ .remove = __devexit_p(twl6040_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "twl6040",
+ },
+};
+
+static int __devinit twl6040_init(void)
+{
+ return platform_driver_register(&twl6040_driver);
+}
+module_init(twl6040_init);
+
+static void __devexit twl6040_exit(void)
+{
+ platform_driver_unregister(&twl6040_driver);
+}
+
+module_exit(twl6040_exit);
+
+MODULE_DESCRIPTION("TWL6040 MFD");
+MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:twl6040");
diff --git a/drivers/mfd/twl6040-irq.c b/drivers/mfd/twl6040-irq.c
new file mode 100644
index 000000000000..b3f8ddaa28a8
--- /dev/null
+++ b/drivers/mfd/twl6040-irq.c
@@ -0,0 +1,191 @@
+/*
+ * Interrupt controller support for TWL6040
+ *
+ * Author: Misael Lopez Cruz <misael.lopez@ti.com>
+ *
+ * Copyright: (C) 2011 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl6040.h>
+
+struct twl6040_irq_data {
+ int mask;
+ int status;
+};
+
+static struct twl6040_irq_data twl6040_irqs[] = {
+ {
+ .mask = TWL6040_THMSK,
+ .status = TWL6040_THINT,
+ },
+ {
+ .mask = TWL6040_PLUGMSK,
+ .status = TWL6040_PLUGINT | TWL6040_UNPLUGINT,
+ },
+ {
+ .mask = TWL6040_HOOKMSK,
+ .status = TWL6040_HOOKINT,
+ },
+ {
+ .mask = TWL6040_HFMSK,
+ .status = TWL6040_HFINT,
+ },
+ {
+ .mask = TWL6040_VIBMSK,
+ .status = TWL6040_VIBINT,
+ },
+ {
+ .mask = TWL6040_READYMSK,
+ .status = TWL6040_READYINT,
+ },
+};
+
+static inline
+struct twl6040_irq_data *irq_to_twl6040_irq(struct twl6040 *twl6040,
+ int irq)
+{
+ return &twl6040_irqs[irq - twl6040->irq_base];
+}
+
+static void twl6040_irq_lock(struct irq_data *data)
+{
+ struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&twl6040->irq_mutex);
+}
+
+static void twl6040_irq_sync_unlock(struct irq_data *data)
+{
+ struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+
+ /* write back to hardware any change in irq mask */
+ if (twl6040->irq_masks_cur != twl6040->irq_masks_cache) {
+ twl6040->irq_masks_cache = twl6040->irq_masks_cur;
+ twl6040_reg_write(twl6040, TWL6040_REG_INTMR,
+ twl6040->irq_masks_cur);
+ }
+
+ mutex_unlock(&twl6040->irq_mutex);
+}
+
+static void twl6040_irq_enable(struct irq_data *data)
+{
+ struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+ struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040,
+ data->irq);
+
+ twl6040->irq_masks_cur &= ~irq_data->mask;
+}
+
+static void twl6040_irq_disable(struct irq_data *data)
+{
+ struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+ struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040,
+ data->irq);
+
+ twl6040->irq_masks_cur |= irq_data->mask;
+}
+
+static struct irq_chip twl6040_irq_chip = {
+ .name = "twl6040",
+ .irq_bus_lock = twl6040_irq_lock,
+ .irq_bus_sync_unlock = twl6040_irq_sync_unlock,
+ .irq_enable = twl6040_irq_enable,
+ .irq_disable = twl6040_irq_disable,
+};
+
+static irqreturn_t twl6040_irq_thread(int irq, void *data)
+{
+ struct twl6040 *twl6040 = data;
+ u8 intid;
+ int i;
+
+ intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+
+ /* apply masking and report (backwards to handle READYINT first) */
+ for (i = ARRAY_SIZE(twl6040_irqs) - 1; i >= 0; i--) {
+ if (twl6040->irq_masks_cur & twl6040_irqs[i].mask)
+ intid &= ~twl6040_irqs[i].status;
+ if (intid & twl6040_irqs[i].status)
+ handle_nested_irq(twl6040->irq_base + i);
+ }
+
+ /* ack unmasked irqs */
+ twl6040_reg_write(twl6040, TWL6040_REG_INTID, intid);
+
+ return IRQ_HANDLED;
+}
+
+int twl6040_irq_init(struct twl6040 *twl6040)
+{
+ int cur_irq, ret;
+ u8 val;
+
+ mutex_init(&twl6040->irq_mutex);
+
+ /* mask the individual interrupt sources */
+ twl6040->irq_masks_cur = TWL6040_ALLINT_MSK;
+ twl6040->irq_masks_cache = TWL6040_ALLINT_MSK;
+ twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK);
+
+ /* Register them with genirq */
+ for (cur_irq = twl6040->irq_base;
+ cur_irq < twl6040->irq_base + ARRAY_SIZE(twl6040_irqs);
+ cur_irq++) {
+ irq_set_chip_data(cur_irq, twl6040);
+ irq_set_chip_and_handler(cur_irq, &twl6040_irq_chip,
+ handle_level_irq);
+ irq_set_nested_thread(cur_irq, 1);
+
+ /* ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ irq_set_noprobe(cur_irq);
+#endif
+ }
+
+ ret = request_threaded_irq(twl6040->irq, NULL, twl6040_irq_thread,
+ IRQF_ONESHOT, "twl6040", twl6040);
+ if (ret) {
+ dev_err(twl6040->dev, "failed to request IRQ %d: %d\n",
+ twl6040->irq, ret);
+ return ret;
+ }
+
+ /* reset interrupts */
+ val = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+
+ /* interrupts cleared on write */
+ twl6040_clear_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_INTCLRMODE);
+
+ return 0;
+}
+EXPORT_SYMBOL(twl6040_irq_init);
+
+void twl6040_irq_exit(struct twl6040 *twl6040)
+{
+ free_irq(twl6040->irq, twl6040);
+}
+EXPORT_SYMBOL(twl6040_irq_exit);
diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c
index d97a86945174..f39b756df561 100644
--- a/drivers/mfd/wl1273-core.c
+++ b/drivers/mfd/wl1273-core.c
@@ -22,6 +22,7 @@
#include <linux/mfd/wl1273-core.h>
#include <linux/slab.h>
+#include <linux/module.h>
#define DRIVER_DESC "WL1273 FM Radio Core"
diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c
new file mode 100644
index 000000000000..87210954a066
--- /dev/null
+++ b/drivers/mfd/wm831x-auxadc.c
@@ -0,0 +1,299 @@
+/*
+ * wm831x-auxadc.c -- AUXADC for Wolfson WM831x PMICs
+ *
+ * Copyright 2009-2011 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/auxadc.h>
+#include <linux/mfd/wm831x/otp.h>
+#include <linux/mfd/wm831x/regulator.h>
+
+struct wm831x_auxadc_req {
+ struct list_head list;
+ enum wm831x_auxadc input;
+ int val;
+ struct completion done;
+};
+
+static int wm831x_auxadc_read_irq(struct wm831x *wm831x,
+ enum wm831x_auxadc input)
+{
+ struct wm831x_auxadc_req *req;
+ int ret;
+ bool ena = false;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ init_completion(&req->done);
+ req->input = input;
+ req->val = -ETIMEDOUT;
+
+ mutex_lock(&wm831x->auxadc_lock);
+
+ /* Enqueue the request */
+ list_add(&req->list, &wm831x->auxadc_pending);
+
+ ena = !wm831x->auxadc_active;
+
+ if (ena) {
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+ WM831X_AUX_ENA, WM831X_AUX_ENA);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n",
+ ret);
+ goto out;
+ }
+ }
+
+ /* Enable the conversion if not already running */
+ if (!(wm831x->auxadc_active & (1 << input))) {
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
+ 1 << input, 1 << input);
+ if (ret != 0) {
+ dev_err(wm831x->dev,
+ "Failed to set AUXADC source: %d\n", ret);
+ goto out;
+ }
+
+ wm831x->auxadc_active |= 1 << input;
+ }
+
+ /* We convert at the fastest rate possible */
+ if (ena) {
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+ WM831X_AUX_CVT_ENA |
+ WM831X_AUX_RATE_MASK,
+ WM831X_AUX_CVT_ENA |
+ WM831X_AUX_RATE_MASK);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to start AUXADC: %d\n",
+ ret);
+ goto out;
+ }
+ }
+
+ mutex_unlock(&wm831x->auxadc_lock);
+
+ /* Wait for an interrupt */
+ wait_for_completion_timeout(&req->done, msecs_to_jiffies(500));
+
+ mutex_lock(&wm831x->auxadc_lock);
+
+ list_del(&req->list);
+ ret = req->val;
+
+out:
+ mutex_unlock(&wm831x->auxadc_lock);
+
+ kfree(req);
+
+ return ret;
+}
+
+static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
+{
+ struct wm831x *wm831x = irq_data;
+ struct wm831x_auxadc_req *req;
+ int ret, input, val;
+
+ ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
+ if (ret < 0) {
+ dev_err(wm831x->dev,
+ "Failed to read AUXADC data: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ input = ((ret & WM831X_AUX_DATA_SRC_MASK)
+ >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
+
+ if (input == 14)
+ input = WM831X_AUX_CAL;
+
+ val = ret & WM831X_AUX_DATA_MASK;
+
+ mutex_lock(&wm831x->auxadc_lock);
+
+ /* Disable this conversion, we're about to complete all users */
+ wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
+ 1 << input, 0);
+ wm831x->auxadc_active &= ~(1 << input);
+
+ /* Turn off the entire convertor if idle */
+ if (!wm831x->auxadc_active)
+ wm831x_reg_write(wm831x, WM831X_AUXADC_CONTROL, 0);
+
+ /* Wake up any threads waiting for this request */
+ list_for_each_entry(req, &wm831x->auxadc_pending, list) {
+ if (req->input == input) {
+ req->val = val;
+ complete(&req->done);
+ }
+ }
+
+ mutex_unlock(&wm831x->auxadc_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int wm831x_auxadc_read_polled(struct wm831x *wm831x,
+ enum wm831x_auxadc input)
+{
+ int ret, src, timeout;
+
+ mutex_lock(&wm831x->auxadc_lock);
+
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+ WM831X_AUX_ENA, WM831X_AUX_ENA);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
+ goto out;
+ }
+
+ /* We force a single source at present */
+ src = input;
+ ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
+ 1 << src);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
+ goto out;
+ }
+
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+ WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
+ goto disable;
+ }
+
+ /* If we're not using interrupts then poll the
+ * interrupt status register */
+ timeout = 5;
+ while (timeout) {
+ msleep(1);
+
+ ret = wm831x_reg_read(wm831x,
+ WM831X_INTERRUPT_STATUS_1);
+ if (ret < 0) {
+ dev_err(wm831x->dev,
+ "ISR 1 read failed: %d\n", ret);
+ goto disable;
+ }
+
+ /* Did it complete? */
+ if (ret & WM831X_AUXADC_DATA_EINT) {
+ wm831x_reg_write(wm831x,
+ WM831X_INTERRUPT_STATUS_1,
+ WM831X_AUXADC_DATA_EINT);
+ break;
+ } else {
+ dev_err(wm831x->dev,
+ "AUXADC conversion timeout\n");
+ ret = -EBUSY;
+ goto disable;
+ }
+ }
+
+ ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
+ if (ret < 0) {
+ dev_err(wm831x->dev,
+ "Failed to read AUXADC data: %d\n", ret);
+ goto disable;
+ }
+
+ src = ((ret & WM831X_AUX_DATA_SRC_MASK)
+ >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
+
+ if (src == 14)
+ src = WM831X_AUX_CAL;
+
+ if (src != input) {
+ dev_err(wm831x->dev, "Data from source %d not %d\n",
+ src, input);
+ ret = -EINVAL;
+ } else {
+ ret &= WM831X_AUX_DATA_MASK;
+ }
+
+disable:
+ wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
+out:
+ mutex_unlock(&wm831x->auxadc_lock);
+ return ret;
+}
+
+/**
+ * wm831x_auxadc_read: Read a value from the WM831x AUXADC
+ *
+ * @wm831x: Device to read from.
+ * @input: AUXADC input to read.
+ */
+int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
+{
+ return wm831x->auxadc_read(wm831x, input);
+}
+EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
+
+/**
+ * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
+ *
+ * @wm831x: Device to read from.
+ * @input: AUXADC input to read.
+ */
+int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
+{
+ int ret;
+
+ ret = wm831x_auxadc_read(wm831x, input);
+ if (ret < 0)
+ return ret;
+
+ ret *= 1465;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
+
+void wm831x_auxadc_init(struct wm831x *wm831x)
+{
+ int ret;
+
+ mutex_init(&wm831x->auxadc_lock);
+ INIT_LIST_HEAD(&wm831x->auxadc_pending);
+
+ if (wm831x->irq && wm831x->irq_base) {
+ wm831x->auxadc_read = wm831x_auxadc_read_irq;
+
+ ret = request_threaded_irq(wm831x->irq_base +
+ WM831X_IRQ_AUXADC_DATA,
+ NULL, wm831x_auxadc_irq, 0,
+ "auxadc", wm831x);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
+ ret);
+ wm831x->auxadc_read = NULL;
+ }
+ }
+
+ if (!wm831x->auxadc_read)
+ wm831x->auxadc_read = wm831x_auxadc_read_polled;
+}
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 265f75fc6a25..0a2b8d41a702 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -18,12 +18,14 @@
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
+#include <linux/err.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/irq.h>
#include <linux/mfd/wm831x/auxadc.h>
#include <linux/mfd/wm831x/otp.h>
+#include <linux/mfd/wm831x/pmu.h>
#include <linux/mfd/wm831x/regulator.h>
/* Current settings - values are 2*2^(reg_val/4) microamps. These are
@@ -160,27 +162,350 @@ int wm831x_reg_unlock(struct wm831x *wm831x)
}
EXPORT_SYMBOL_GPL(wm831x_reg_unlock);
-static int wm831x_read(struct wm831x *wm831x, unsigned short reg,
- int bytes, void *dest)
+static bool wm831x_reg_readable(struct device *dev, unsigned int reg)
{
- int ret, i;
- u16 *buf = dest;
-
- BUG_ON(bytes % 2);
- BUG_ON(bytes <= 0);
+ switch (reg) {
+ case WM831X_RESET_ID:
+ case WM831X_REVISION:
+ case WM831X_PARENT_ID:
+ case WM831X_SYSVDD_CONTROL:
+ case WM831X_THERMAL_MONITORING:
+ case WM831X_POWER_STATE:
+ case WM831X_WATCHDOG:
+ case WM831X_ON_PIN_CONTROL:
+ case WM831X_RESET_CONTROL:
+ case WM831X_CONTROL_INTERFACE:
+ case WM831X_SECURITY_KEY:
+ case WM831X_SOFTWARE_SCRATCH:
+ case WM831X_OTP_CONTROL:
+ case WM831X_GPIO_LEVEL:
+ case WM831X_SYSTEM_STATUS:
+ case WM831X_ON_SOURCE:
+ case WM831X_OFF_SOURCE:
+ case WM831X_SYSTEM_INTERRUPTS:
+ case WM831X_INTERRUPT_STATUS_1:
+ case WM831X_INTERRUPT_STATUS_2:
+ case WM831X_INTERRUPT_STATUS_3:
+ case WM831X_INTERRUPT_STATUS_4:
+ case WM831X_INTERRUPT_STATUS_5:
+ case WM831X_IRQ_CONFIG:
+ case WM831X_SYSTEM_INTERRUPTS_MASK:
+ case WM831X_INTERRUPT_STATUS_1_MASK:
+ case WM831X_INTERRUPT_STATUS_2_MASK:
+ case WM831X_INTERRUPT_STATUS_3_MASK:
+ case WM831X_INTERRUPT_STATUS_4_MASK:
+ case WM831X_INTERRUPT_STATUS_5_MASK:
+ case WM831X_RTC_WRITE_COUNTER:
+ case WM831X_RTC_TIME_1:
+ case WM831X_RTC_TIME_2:
+ case WM831X_RTC_ALARM_1:
+ case WM831X_RTC_ALARM_2:
+ case WM831X_RTC_CONTROL:
+ case WM831X_RTC_TRIM:
+ case WM831X_TOUCH_CONTROL_1:
+ case WM831X_TOUCH_CONTROL_2:
+ case WM831X_TOUCH_DATA_X:
+ case WM831X_TOUCH_DATA_Y:
+ case WM831X_TOUCH_DATA_Z:
+ case WM831X_AUXADC_DATA:
+ case WM831X_AUXADC_CONTROL:
+ case WM831X_AUXADC_SOURCE:
+ case WM831X_COMPARATOR_CONTROL:
+ case WM831X_COMPARATOR_1:
+ case WM831X_COMPARATOR_2:
+ case WM831X_COMPARATOR_3:
+ case WM831X_COMPARATOR_4:
+ case WM831X_GPIO1_CONTROL:
+ case WM831X_GPIO2_CONTROL:
+ case WM831X_GPIO3_CONTROL:
+ case WM831X_GPIO4_CONTROL:
+ case WM831X_GPIO5_CONTROL:
+ case WM831X_GPIO6_CONTROL:
+ case WM831X_GPIO7_CONTROL:
+ case WM831X_GPIO8_CONTROL:
+ case WM831X_GPIO9_CONTROL:
+ case WM831X_GPIO10_CONTROL:
+ case WM831X_GPIO11_CONTROL:
+ case WM831X_GPIO12_CONTROL:
+ case WM831X_GPIO13_CONTROL:
+ case WM831X_GPIO14_CONTROL:
+ case WM831X_GPIO15_CONTROL:
+ case WM831X_GPIO16_CONTROL:
+ case WM831X_CHARGER_CONTROL_1:
+ case WM831X_CHARGER_CONTROL_2:
+ case WM831X_CHARGER_STATUS:
+ case WM831X_BACKUP_CHARGER_CONTROL:
+ case WM831X_STATUS_LED_1:
+ case WM831X_STATUS_LED_2:
+ case WM831X_CURRENT_SINK_1:
+ case WM831X_CURRENT_SINK_2:
+ case WM831X_DCDC_ENABLE:
+ case WM831X_LDO_ENABLE:
+ case WM831X_DCDC_STATUS:
+ case WM831X_LDO_STATUS:
+ case WM831X_DCDC_UV_STATUS:
+ case WM831X_LDO_UV_STATUS:
+ case WM831X_DC1_CONTROL_1:
+ case WM831X_DC1_CONTROL_2:
+ case WM831X_DC1_ON_CONFIG:
+ case WM831X_DC1_SLEEP_CONTROL:
+ case WM831X_DC1_DVS_CONTROL:
+ case WM831X_DC2_CONTROL_1:
+ case WM831X_DC2_CONTROL_2:
+ case WM831X_DC2_ON_CONFIG:
+ case WM831X_DC2_SLEEP_CONTROL:
+ case WM831X_DC2_DVS_CONTROL:
+ case WM831X_DC3_CONTROL_1:
+ case WM831X_DC3_CONTROL_2:
+ case WM831X_DC3_ON_CONFIG:
+ case WM831X_DC3_SLEEP_CONTROL:
+ case WM831X_DC4_CONTROL:
+ case WM831X_DC4_SLEEP_CONTROL:
+ case WM831X_EPE1_CONTROL:
+ case WM831X_EPE2_CONTROL:
+ case WM831X_LDO1_CONTROL:
+ case WM831X_LDO1_ON_CONTROL:
+ case WM831X_LDO1_SLEEP_CONTROL:
+ case WM831X_LDO2_CONTROL:
+ case WM831X_LDO2_ON_CONTROL:
+ case WM831X_LDO2_SLEEP_CONTROL:
+ case WM831X_LDO3_CONTROL:
+ case WM831X_LDO3_ON_CONTROL:
+ case WM831X_LDO3_SLEEP_CONTROL:
+ case WM831X_LDO4_CONTROL:
+ case WM831X_LDO4_ON_CONTROL:
+ case WM831X_LDO4_SLEEP_CONTROL:
+ case WM831X_LDO5_CONTROL:
+ case WM831X_LDO5_ON_CONTROL:
+ case WM831X_LDO5_SLEEP_CONTROL:
+ case WM831X_LDO6_CONTROL:
+ case WM831X_LDO6_ON_CONTROL:
+ case WM831X_LDO6_SLEEP_CONTROL:
+ case WM831X_LDO7_CONTROL:
+ case WM831X_LDO7_ON_CONTROL:
+ case WM831X_LDO7_SLEEP_CONTROL:
+ case WM831X_LDO8_CONTROL:
+ case WM831X_LDO8_ON_CONTROL:
+ case WM831X_LDO8_SLEEP_CONTROL:
+ case WM831X_LDO9_CONTROL:
+ case WM831X_LDO9_ON_CONTROL:
+ case WM831X_LDO9_SLEEP_CONTROL:
+ case WM831X_LDO10_CONTROL:
+ case WM831X_LDO10_ON_CONTROL:
+ case WM831X_LDO10_SLEEP_CONTROL:
+ case WM831X_LDO11_ON_CONTROL:
+ case WM831X_LDO11_SLEEP_CONTROL:
+ case WM831X_POWER_GOOD_SOURCE_1:
+ case WM831X_POWER_GOOD_SOURCE_2:
+ case WM831X_CLOCK_CONTROL_1:
+ case WM831X_CLOCK_CONTROL_2:
+ case WM831X_FLL_CONTROL_1:
+ case WM831X_FLL_CONTROL_2:
+ case WM831X_FLL_CONTROL_3:
+ case WM831X_FLL_CONTROL_4:
+ case WM831X_FLL_CONTROL_5:
+ case WM831X_UNIQUE_ID_1:
+ case WM831X_UNIQUE_ID_2:
+ case WM831X_UNIQUE_ID_3:
+ case WM831X_UNIQUE_ID_4:
+ case WM831X_UNIQUE_ID_5:
+ case WM831X_UNIQUE_ID_6:
+ case WM831X_UNIQUE_ID_7:
+ case WM831X_UNIQUE_ID_8:
+ case WM831X_FACTORY_OTP_ID:
+ case WM831X_FACTORY_OTP_1:
+ case WM831X_FACTORY_OTP_2:
+ case WM831X_FACTORY_OTP_3:
+ case WM831X_FACTORY_OTP_4:
+ case WM831X_FACTORY_OTP_5:
+ case WM831X_CUSTOMER_OTP_ID:
+ case WM831X_DC1_OTP_CONTROL:
+ case WM831X_DC2_OTP_CONTROL:
+ case WM831X_DC3_OTP_CONTROL:
+ case WM831X_LDO1_2_OTP_CONTROL:
+ case WM831X_LDO3_4_OTP_CONTROL:
+ case WM831X_LDO5_6_OTP_CONTROL:
+ case WM831X_LDO7_8_OTP_CONTROL:
+ case WM831X_LDO9_10_OTP_CONTROL:
+ case WM831X_LDO11_EPE_CONTROL:
+ case WM831X_GPIO1_OTP_CONTROL:
+ case WM831X_GPIO2_OTP_CONTROL:
+ case WM831X_GPIO3_OTP_CONTROL:
+ case WM831X_GPIO4_OTP_CONTROL:
+ case WM831X_GPIO5_OTP_CONTROL:
+ case WM831X_GPIO6_OTP_CONTROL:
+ case WM831X_DBE_CHECK_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
- ret = wm831x->read_dev(wm831x, reg, bytes, dest);
- if (ret < 0)
- return ret;
+static bool wm831x_reg_writeable(struct device *dev, unsigned int reg)
+{
+ struct wm831x *wm831x = dev_get_drvdata(dev);
- for (i = 0; i < bytes / 2; i++) {
- buf[i] = be16_to_cpu(buf[i]);
+ if (wm831x_reg_locked(wm831x, reg))
+ return false;
- dev_vdbg(wm831x->dev, "Read %04x from R%d(0x%x)\n",
- buf[i], reg + i, reg + i);
+ switch (reg) {
+ case WM831X_SYSVDD_CONTROL:
+ case WM831X_THERMAL_MONITORING:
+ case WM831X_POWER_STATE:
+ case WM831X_WATCHDOG:
+ case WM831X_ON_PIN_CONTROL:
+ case WM831X_RESET_CONTROL:
+ case WM831X_CONTROL_INTERFACE:
+ case WM831X_SECURITY_KEY:
+ case WM831X_SOFTWARE_SCRATCH:
+ case WM831X_OTP_CONTROL:
+ case WM831X_GPIO_LEVEL:
+ case WM831X_INTERRUPT_STATUS_1:
+ case WM831X_INTERRUPT_STATUS_2:
+ case WM831X_INTERRUPT_STATUS_3:
+ case WM831X_INTERRUPT_STATUS_4:
+ case WM831X_INTERRUPT_STATUS_5:
+ case WM831X_IRQ_CONFIG:
+ case WM831X_SYSTEM_INTERRUPTS_MASK:
+ case WM831X_INTERRUPT_STATUS_1_MASK:
+ case WM831X_INTERRUPT_STATUS_2_MASK:
+ case WM831X_INTERRUPT_STATUS_3_MASK:
+ case WM831X_INTERRUPT_STATUS_4_MASK:
+ case WM831X_INTERRUPT_STATUS_5_MASK:
+ case WM831X_RTC_TIME_1:
+ case WM831X_RTC_TIME_2:
+ case WM831X_RTC_ALARM_1:
+ case WM831X_RTC_ALARM_2:
+ case WM831X_RTC_CONTROL:
+ case WM831X_RTC_TRIM:
+ case WM831X_TOUCH_CONTROL_1:
+ case WM831X_TOUCH_CONTROL_2:
+ case WM831X_AUXADC_CONTROL:
+ case WM831X_AUXADC_SOURCE:
+ case WM831X_COMPARATOR_CONTROL:
+ case WM831X_COMPARATOR_1:
+ case WM831X_COMPARATOR_2:
+ case WM831X_COMPARATOR_3:
+ case WM831X_COMPARATOR_4:
+ case WM831X_GPIO1_CONTROL:
+ case WM831X_GPIO2_CONTROL:
+ case WM831X_GPIO3_CONTROL:
+ case WM831X_GPIO4_CONTROL:
+ case WM831X_GPIO5_CONTROL:
+ case WM831X_GPIO6_CONTROL:
+ case WM831X_GPIO7_CONTROL:
+ case WM831X_GPIO8_CONTROL:
+ case WM831X_GPIO9_CONTROL:
+ case WM831X_GPIO10_CONTROL:
+ case WM831X_GPIO11_CONTROL:
+ case WM831X_GPIO12_CONTROL:
+ case WM831X_GPIO13_CONTROL:
+ case WM831X_GPIO14_CONTROL:
+ case WM831X_GPIO15_CONTROL:
+ case WM831X_GPIO16_CONTROL:
+ case WM831X_CHARGER_CONTROL_1:
+ case WM831X_CHARGER_CONTROL_2:
+ case WM831X_CHARGER_STATUS:
+ case WM831X_BACKUP_CHARGER_CONTROL:
+ case WM831X_STATUS_LED_1:
+ case WM831X_STATUS_LED_2:
+ case WM831X_CURRENT_SINK_1:
+ case WM831X_CURRENT_SINK_2:
+ case WM831X_DCDC_ENABLE:
+ case WM831X_LDO_ENABLE:
+ case WM831X_DC1_CONTROL_1:
+ case WM831X_DC1_CONTROL_2:
+ case WM831X_DC1_ON_CONFIG:
+ case WM831X_DC1_SLEEP_CONTROL:
+ case WM831X_DC1_DVS_CONTROL:
+ case WM831X_DC2_CONTROL_1:
+ case WM831X_DC2_CONTROL_2:
+ case WM831X_DC2_ON_CONFIG:
+ case WM831X_DC2_SLEEP_CONTROL:
+ case WM831X_DC2_DVS_CONTROL:
+ case WM831X_DC3_CONTROL_1:
+ case WM831X_DC3_CONTROL_2:
+ case WM831X_DC3_ON_CONFIG:
+ case WM831X_DC3_SLEEP_CONTROL:
+ case WM831X_DC4_CONTROL:
+ case WM831X_DC4_SLEEP_CONTROL:
+ case WM831X_EPE1_CONTROL:
+ case WM831X_EPE2_CONTROL:
+ case WM831X_LDO1_CONTROL:
+ case WM831X_LDO1_ON_CONTROL:
+ case WM831X_LDO1_SLEEP_CONTROL:
+ case WM831X_LDO2_CONTROL:
+ case WM831X_LDO2_ON_CONTROL:
+ case WM831X_LDO2_SLEEP_CONTROL:
+ case WM831X_LDO3_CONTROL:
+ case WM831X_LDO3_ON_CONTROL:
+ case WM831X_LDO3_SLEEP_CONTROL:
+ case WM831X_LDO4_CONTROL:
+ case WM831X_LDO4_ON_CONTROL:
+ case WM831X_LDO4_SLEEP_CONTROL:
+ case WM831X_LDO5_CONTROL:
+ case WM831X_LDO5_ON_CONTROL:
+ case WM831X_LDO5_SLEEP_CONTROL:
+ case WM831X_LDO6_CONTROL:
+ case WM831X_LDO6_ON_CONTROL:
+ case WM831X_LDO6_SLEEP_CONTROL:
+ case WM831X_LDO7_CONTROL:
+ case WM831X_LDO7_ON_CONTROL:
+ case WM831X_LDO7_SLEEP_CONTROL:
+ case WM831X_LDO8_CONTROL:
+ case WM831X_LDO8_ON_CONTROL:
+ case WM831X_LDO8_SLEEP_CONTROL:
+ case WM831X_LDO9_CONTROL:
+ case WM831X_LDO9_ON_CONTROL:
+ case WM831X_LDO9_SLEEP_CONTROL:
+ case WM831X_LDO10_CONTROL:
+ case WM831X_LDO10_ON_CONTROL:
+ case WM831X_LDO10_SLEEP_CONTROL:
+ case WM831X_LDO11_ON_CONTROL:
+ case WM831X_LDO11_SLEEP_CONTROL:
+ case WM831X_POWER_GOOD_SOURCE_1:
+ case WM831X_POWER_GOOD_SOURCE_2:
+ case WM831X_CLOCK_CONTROL_1:
+ case WM831X_CLOCK_CONTROL_2:
+ case WM831X_FLL_CONTROL_1:
+ case WM831X_FLL_CONTROL_2:
+ case WM831X_FLL_CONTROL_3:
+ case WM831X_FLL_CONTROL_4:
+ case WM831X_FLL_CONTROL_5:
+ return true;
+ default:
+ return false;
}
+}
- return 0;
+static bool wm831x_reg_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM831X_SYSTEM_STATUS:
+ case WM831X_ON_SOURCE:
+ case WM831X_OFF_SOURCE:
+ case WM831X_GPIO_LEVEL:
+ case WM831X_SYSTEM_INTERRUPTS:
+ case WM831X_INTERRUPT_STATUS_1:
+ case WM831X_INTERRUPT_STATUS_2:
+ case WM831X_INTERRUPT_STATUS_3:
+ case WM831X_INTERRUPT_STATUS_4:
+ case WM831X_INTERRUPT_STATUS_5:
+ case WM831X_RTC_TIME_1:
+ case WM831X_RTC_TIME_2:
+ case WM831X_TOUCH_DATA_X:
+ case WM831X_TOUCH_DATA_Y:
+ case WM831X_TOUCH_DATA_Z:
+ case WM831X_AUXADC_DATA:
+ case WM831X_CHARGER_STATUS:
+ case WM831X_DCDC_STATUS:
+ case WM831X_LDO_STATUS:
+ case WM831X_DCDC_UV_STATUS:
+ case WM831X_LDO_UV_STATUS:
+ return true;
+ default:
+ return false;
+ }
}
/**
@@ -191,14 +516,10 @@ static int wm831x_read(struct wm831x *wm831x, unsigned short reg,
*/
int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg)
{
- unsigned short val;
+ unsigned int val;
int ret;
- mutex_lock(&wm831x->io_lock);
-
- ret = wm831x_read(wm831x, reg, 2, &val);
-
- mutex_unlock(&wm831x->io_lock);
+ ret = regmap_read(wm831x->regmap, reg, &val);
if (ret < 0)
return ret;
@@ -218,15 +539,7 @@ EXPORT_SYMBOL_GPL(wm831x_reg_read);
int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
int count, u16 *buf)
{
- int ret;
-
- mutex_lock(&wm831x->io_lock);
-
- ret = wm831x_read(wm831x, reg, count * 2, buf);
-
- mutex_unlock(&wm831x->io_lock);
-
- return ret;
+ return regmap_bulk_read(wm831x->regmap, reg, buf, count);
}
EXPORT_SYMBOL_GPL(wm831x_bulk_read);
@@ -234,7 +547,7 @@ static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
int bytes, void *src)
{
u16 *buf = src;
- int i;
+ int i, ret;
BUG_ON(bytes % 2);
BUG_ON(bytes <= 0);
@@ -245,11 +558,10 @@ static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n",
buf[i], reg + i, reg + i);
-
- buf[i] = cpu_to_be16(buf[i]);
+ ret = regmap_write(wm831x->regmap, reg + i, buf[i]);
}
- return wm831x->write_dev(wm831x, reg, bytes, src);
+ return 0;
}
/**
@@ -286,166 +598,20 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
unsigned short mask, unsigned short val)
{
int ret;
- u16 r;
mutex_lock(&wm831x->io_lock);
- ret = wm831x_read(wm831x, reg, 2, &r);
- if (ret < 0)
- goto out;
-
- r &= ~mask;
- r |= val;
-
- ret = wm831x_write(wm831x, reg, 2, &r);
+ if (!wm831x_reg_locked(wm831x, reg))
+ ret = regmap_update_bits(wm831x->regmap, reg, mask, val);
+ else
+ ret = -EPERM;
-out:
mutex_unlock(&wm831x->io_lock);
return ret;
}
EXPORT_SYMBOL_GPL(wm831x_set_bits);
-/**
- * wm831x_auxadc_read: Read a value from the WM831x AUXADC
- *
- * @wm831x: Device to read from.
- * @input: AUXADC input to read.
- */
-int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
-{
- int ret, src, irq_masked, timeout;
-
- /* Are we using the interrupt? */
- irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK);
- irq_masked &= WM831X_AUXADC_DATA_EINT;
-
- mutex_lock(&wm831x->auxadc_lock);
-
- ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
- WM831X_AUX_ENA, WM831X_AUX_ENA);
- if (ret < 0) {
- dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
- goto out;
- }
-
- /* We force a single source at present */
- src = input;
- ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
- 1 << src);
- if (ret < 0) {
- dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
- goto out;
- }
-
- /* Clear any notification from a very late arriving interrupt */
- try_wait_for_completion(&wm831x->auxadc_done);
-
- ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
- WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
- if (ret < 0) {
- dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
- goto disable;
- }
-
- if (irq_masked) {
- /* If we're not using interrupts then poll the
- * interrupt status register */
- timeout = 5;
- while (timeout) {
- msleep(1);
-
- ret = wm831x_reg_read(wm831x,
- WM831X_INTERRUPT_STATUS_1);
- if (ret < 0) {
- dev_err(wm831x->dev,
- "ISR 1 read failed: %d\n", ret);
- goto disable;
- }
-
- /* Did it complete? */
- if (ret & WM831X_AUXADC_DATA_EINT) {
- wm831x_reg_write(wm831x,
- WM831X_INTERRUPT_STATUS_1,
- WM831X_AUXADC_DATA_EINT);
- break;
- } else {
- dev_err(wm831x->dev,
- "AUXADC conversion timeout\n");
- ret = -EBUSY;
- goto disable;
- }
- }
- } else {
- /* If we are using interrupts then wait for the
- * interrupt to complete. Use an extremely long
- * timeout to handle situations with heavy load where
- * the notification of the interrupt may be delayed by
- * threaded IRQ handling. */
- if (!wait_for_completion_timeout(&wm831x->auxadc_done,
- msecs_to_jiffies(500))) {
- dev_err(wm831x->dev, "Timed out waiting for AUXADC\n");
- ret = -EBUSY;
- goto disable;
- }
- }
-
- ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
- if (ret < 0) {
- dev_err(wm831x->dev, "Failed to read AUXADC data: %d\n", ret);
- } else {
- src = ((ret & WM831X_AUX_DATA_SRC_MASK)
- >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
-
- if (src == 14)
- src = WM831X_AUX_CAL;
-
- if (src != input) {
- dev_err(wm831x->dev, "Data from source %d not %d\n",
- src, input);
- ret = -EINVAL;
- } else {
- ret &= WM831X_AUX_DATA_MASK;
- }
- }
-
-disable:
- wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
-out:
- mutex_unlock(&wm831x->auxadc_lock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
-
-static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
-{
- struct wm831x *wm831x = irq_data;
-
- complete(&wm831x->auxadc_done);
-
- return IRQ_HANDLED;
-}
-
-/**
- * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
- *
- * @wm831x: Device to read from.
- * @input: AUXADC input to read.
- */
-int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
-{
- int ret;
-
- ret = wm831x_auxadc_read(wm831x, input);
- if (ret < 0)
- return ret;
-
- ret *= 1465;
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
-
static struct resource wm831x_dcdc1_resources[] = {
{
.start = WM831X_DC1_CONTROL_1,
@@ -872,6 +1038,9 @@ static struct mfd_cell wm8310_devs[] = {
.resources = wm831x_dcdc4_resources,
},
{
+ .name = "wm831x-clk",
+ },
+ {
.name = "wm831x-epe",
.id = 1,
},
@@ -976,11 +1145,6 @@ static struct mfd_cell wm8310_devs[] = {
.resources = wm831x_power_resources,
},
{
- .name = "wm831x-rtc",
- .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
- .resources = wm831x_rtc_resources,
- },
- {
.name = "wm831x-status",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_status1_resources),
@@ -1028,6 +1192,9 @@ static struct mfd_cell wm8311_devs[] = {
.resources = wm831x_dcdc4_resources,
},
{
+ .name = "wm831x-clk",
+ },
+ {
.name = "wm831x-epe",
.id = 1,
},
@@ -1108,11 +1275,6 @@ static struct mfd_cell wm8311_devs[] = {
.resources = wm831x_power_resources,
},
{
- .name = "wm831x-rtc",
- .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
- .resources = wm831x_rtc_resources,
- },
- {
.name = "wm831x-status",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_status1_resources),
@@ -1125,11 +1287,6 @@ static struct mfd_cell wm8311_devs[] = {
.resources = wm831x_status2_resources,
},
{
- .name = "wm831x-touch",
- .num_resources = ARRAY_SIZE(wm831x_touch_resources),
- .resources = wm831x_touch_resources,
- },
- {
.name = "wm831x-watchdog",
.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
.resources = wm831x_wdt_resources,
@@ -1165,6 +1322,9 @@ static struct mfd_cell wm8312_devs[] = {
.resources = wm831x_dcdc4_resources,
},
{
+ .name = "wm831x-clk",
+ },
+ {
.name = "wm831x-epe",
.id = 1,
},
@@ -1269,11 +1429,6 @@ static struct mfd_cell wm8312_devs[] = {
.resources = wm831x_power_resources,
},
{
- .name = "wm831x-rtc",
- .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
- .resources = wm831x_rtc_resources,
- },
- {
.name = "wm831x-status",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_status1_resources),
@@ -1286,11 +1441,6 @@ static struct mfd_cell wm8312_devs[] = {
.resources = wm831x_status2_resources,
},
{
- .name = "wm831x-touch",
- .num_resources = ARRAY_SIZE(wm831x_touch_resources),
- .resources = wm831x_touch_resources,
- },
- {
.name = "wm831x-watchdog",
.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
.resources = wm831x_wdt_resources,
@@ -1326,6 +1476,9 @@ static struct mfd_cell wm8320_devs[] = {
.resources = wm8320_dcdc4_buck_resources,
},
{
+ .name = "wm831x-clk",
+ },
+ {
.name = "wm831x-gpio",
.num_resources = ARRAY_SIZE(wm831x_gpio_resources),
.resources = wm831x_gpio_resources,
@@ -1405,11 +1558,6 @@ static struct mfd_cell wm8320_devs[] = {
.resources = wm831x_on_resources,
},
{
- .name = "wm831x-rtc",
- .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
- .resources = wm831x_rtc_resources,
- },
- {
.name = "wm831x-status",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_status1_resources),
@@ -1428,32 +1576,60 @@ static struct mfd_cell wm8320_devs[] = {
},
};
+static struct mfd_cell touch_devs[] = {
+ {
+ .name = "wm831x-touch",
+ .num_resources = ARRAY_SIZE(wm831x_touch_resources),
+ .resources = wm831x_touch_resources,
+ },
+};
+
+static struct mfd_cell rtc_devs[] = {
+ {
+ .name = "wm831x-rtc",
+ .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
+ .resources = wm831x_rtc_resources,
+ },
+};
+
static struct mfd_cell backlight_devs[] = {
{
.name = "wm831x-backlight",
},
};
+struct regmap_config wm831x_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .cache_type = REGCACHE_RBTREE,
+
+ .max_register = WM831X_DBE_CHECK_DATA,
+ .readable_reg = wm831x_reg_readable,
+ .writeable_reg = wm831x_reg_writeable,
+ .volatile_reg = wm831x_reg_volatile,
+};
+EXPORT_SYMBOL_GPL(wm831x_regmap_config);
+
/*
* Instantiate the generic non-control parts of the device.
*/
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
{
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
- int rev;
+ int rev, wm831x_num;
enum wm831x_parent parent;
int ret, i;
mutex_init(&wm831x->io_lock);
mutex_init(&wm831x->key_lock);
- mutex_init(&wm831x->auxadc_lock);
- init_completion(&wm831x->auxadc_done);
dev_set_drvdata(wm831x->dev, wm831x);
+ wm831x->soft_shutdown = pdata->soft_shutdown;
ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
- goto err;
+ goto err_regmap;
}
switch (ret) {
case 0x6204:
@@ -1462,20 +1638,20 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
default:
dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
ret = -EINVAL;
- goto err;
+ goto err_regmap;
}
ret = wm831x_reg_read(wm831x, WM831X_REVISION);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
- goto err;
+ goto err_regmap;
}
rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;
ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
- goto err;
+ goto err_regmap;
}
/* Some engineering samples do not have the ID set, rely on
@@ -1550,7 +1726,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
default:
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
ret = -EINVAL;
- goto err;
+ goto err_regmap;
}
/* This will need revisiting in future but is OK for all
@@ -1564,7 +1740,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
- goto err;
+ goto err_regmap;
}
if (ret != 0) {
dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
@@ -1577,7 +1753,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
ret = pdata->pre_init(wm831x);
if (ret != 0) {
dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
- goto err;
+ goto err_regmap;
}
}
@@ -1592,45 +1768,51 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
}
}
+ /* Multiply by 10 as we have many subdevices of the same type */
+ if (pdata && pdata->wm831x_num)
+ wm831x_num = pdata->wm831x_num * 10;
+ else
+ wm831x_num = -1;
+
ret = wm831x_irq_init(wm831x, irq);
if (ret != 0)
- goto err;
+ goto err_regmap;
- if (wm831x->irq_base) {
- ret = request_threaded_irq(wm831x->irq_base +
- WM831X_IRQ_AUXADC_DATA,
- NULL, wm831x_auxadc_irq, 0,
- "auxadc", wm831x);
- if (ret < 0)
- dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
- ret);
- }
+ wm831x_auxadc_init(wm831x);
/* The core device is up, instantiate the subdevices. */
switch (parent) {
case WM8310:
- ret = mfd_add_devices(wm831x->dev, -1,
+ ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8310_devs, ARRAY_SIZE(wm8310_devs),
NULL, wm831x->irq_base);
break;
case WM8311:
- ret = mfd_add_devices(wm831x->dev, -1,
+ ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8311_devs, ARRAY_SIZE(wm8311_devs),
NULL, wm831x->irq_base);
+ if (!pdata || !pdata->disable_touch)
+ mfd_add_devices(wm831x->dev, wm831x_num,
+ touch_devs, ARRAY_SIZE(touch_devs),
+ NULL, wm831x->irq_base);
break;
case WM8312:
- ret = mfd_add_devices(wm831x->dev, -1,
+ ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8312_devs, ARRAY_SIZE(wm8312_devs),
NULL, wm831x->irq_base);
+ if (!pdata || !pdata->disable_touch)
+ mfd_add_devices(wm831x->dev, wm831x_num,
+ touch_devs, ARRAY_SIZE(touch_devs),
+ NULL, wm831x->irq_base);
break;
case WM8320:
case WM8321:
case WM8325:
case WM8326:
- ret = mfd_add_devices(wm831x->dev, -1,
+ ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8320_devs, ARRAY_SIZE(wm8320_devs),
NULL, wm831x->irq_base);
break;
@@ -1645,9 +1827,30 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
goto err_irq;
}
+ /* The RTC can only be used if the 32.768kHz crystal is
+ * enabled; this can't be controlled by software at runtime.
+ */
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to read clock status: %d\n", ret);
+ goto err_irq;
+ }
+
+ if (ret & WM831X_XTAL_ENA) {
+ ret = mfd_add_devices(wm831x->dev, wm831x_num,
+ rtc_devs, ARRAY_SIZE(rtc_devs),
+ NULL, wm831x->irq_base);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
+ goto err_irq;
+ }
+ } else {
+ dev_info(wm831x->dev, "32.768kHz clock disabled, no RTC\n");
+ }
+
if (pdata && pdata->backlight) {
/* Treat errors as non-critical */
- ret = mfd_add_devices(wm831x->dev, -1, backlight_devs,
+ ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
ARRAY_SIZE(backlight_devs), NULL,
wm831x->irq_base);
if (ret < 0)
@@ -1669,8 +1872,9 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
err_irq:
wm831x_irq_exit(wm831x);
-err:
+err_regmap:
mfd_remove_devices(wm831x->dev);
+ regmap_exit(wm831x->regmap);
kfree(wm831x);
return ret;
}
@@ -1682,6 +1886,7 @@ void wm831x_device_exit(struct wm831x *wm831x)
if (wm831x->irq_base)
free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
wm831x_irq_exit(wm831x);
+ regmap_exit(wm831x->regmap);
kfree(wm831x);
}
@@ -1721,6 +1926,15 @@ int wm831x_device_suspend(struct wm831x *wm831x)
return 0;
}
+void wm831x_device_shutdown(struct wm831x *wm831x)
+{
+ if (wm831x->soft_shutdown) {
+ dev_info(wm831x->dev, "Initiating shutdown...\n");
+ wm831x_set_bits(wm831x, WM831X_POWER_STATE, WM831X_CHIP_ON, 0);
+ }
+}
+EXPORT_SYMBOL_GPL(wm831x_device_shutdown);
+
MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mark Brown");
diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c
index a06cbc739716..ac8da1d439da 100644
--- a/drivers/mfd/wm831x-i2c.c
+++ b/drivers/mfd/wm831x-i2c.c
@@ -18,67 +18,17 @@
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
-static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
- int bytes, void *dest)
-{
- struct i2c_client *i2c = wm831x->control_data;
- int ret;
- u16 r = cpu_to_be16(reg);
-
- ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
- if (ret < 0)
- return ret;
- if (ret != 2)
- return -EIO;
-
- ret = i2c_master_recv(i2c, dest, bytes);
- if (ret < 0)
- return ret;
- if (ret != bytes)
- return -EIO;
- return 0;
-}
-
-/* Currently we allocate the write buffer on the stack; this is OK for
- * small writes - if we need to do large writes this will need to be
- * revised.
- */
-static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
- int bytes, void *src)
-{
- struct i2c_client *i2c = wm831x->control_data;
- struct i2c_msg xfer[2];
- int ret;
-
- reg = cpu_to_be16(reg);
-
- xfer[0].addr = i2c->addr;
- xfer[0].flags = 0;
- xfer[0].len = 2;
- xfer[0].buf = (char *)&reg;
-
- xfer[1].addr = i2c->addr;
- xfer[1].flags = I2C_M_NOSTART;
- xfer[1].len = bytes;
- xfer[1].buf = (char *)src;
-
- ret = i2c_transfer(i2c->adapter, xfer, 2);
- if (ret < 0)
- return ret;
- if (ret != 2)
- return -EIO;
-
- return 0;
-}
-
static int wm831x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm831x *wm831x;
+ int ret;
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
if (wm831x == NULL)
@@ -86,9 +36,15 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm831x);
wm831x->dev = &i2c->dev;
- wm831x->control_data = i2c;
- wm831x->read_dev = wm831x_i2c_read_device;
- wm831x->write_dev = wm831x_i2c_write_device;
+
+ wm831x->regmap = regmap_init_i2c(i2c, &wm831x_regmap_config);
+ if (IS_ERR(wm831x->regmap)) {
+ ret = PTR_ERR(wm831x->regmap);
+ dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
+ ret);
+ kfree(wm831x);
+ return ret;
+ }
return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
}
@@ -109,6 +65,13 @@ static int wm831x_i2c_suspend(struct device *dev)
return wm831x_device_suspend(wm831x);
}
+static void wm831x_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+ wm831x_device_shutdown(wm831x);
+}
+
static const struct i2c_device_id wm831x_i2c_id[] = {
{ "wm8310", WM8310 },
{ "wm8311", WM8311 },
@@ -133,6 +96,7 @@ static struct i2c_driver wm831x_i2c_driver = {
},
.probe = wm831x_i2c_probe,
.remove = wm831x_i2c_remove,
+ .shutdown = wm831x_i2c_shutdown,
.id_table = wm831x_i2c_id,
};
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
index 42b928ec891e..f4747a4a9a93 100644
--- a/drivers/mfd/wm831x-irq.c
+++ b/drivers/mfd/wm831x-irq.c
@@ -348,6 +348,15 @@ static void wm831x_irq_sync_unlock(struct irq_data *data)
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
int i;
+ for (i = 0; i < ARRAY_SIZE(wm831x->gpio_update); i++) {
+ if (wm831x->gpio_update[i]) {
+ wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + i,
+ WM831X_GPN_INT_MODE | WM831X_GPN_POL,
+ wm831x->gpio_update[i]);
+ wm831x->gpio_update[i] = 0;
+ }
+ }
+
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
/* If there's been a change in the mask write it back
* to the hardware. */
@@ -387,7 +396,7 @@ static void wm831x_irq_disable(struct irq_data *data)
static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
{
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
- int val, irq;
+ int irq;
irq = data->irq - wm831x->irq_base;
@@ -399,22 +408,37 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
return -EINVAL;
}
+ /* Rebase the IRQ into the GPIO range so we've got a sensible array
+ * index.
+ */
+ irq -= WM831X_IRQ_GPIO_1;
+
+ /* We set the high bit to flag that we need an update; don't
+ * do the update here as we can be called with the bus lock
+ * held.
+ */
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
- val = WM831X_GPN_INT_MODE;
+ wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE;
+ wm831x->gpio_level[irq] = false;
break;
case IRQ_TYPE_EDGE_RISING:
- val = WM831X_GPN_POL;
+ wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
+ wm831x->gpio_level[irq] = false;
break;
case IRQ_TYPE_EDGE_FALLING:
- val = 0;
+ wm831x->gpio_update[irq] = 0x10000;
+ wm831x->gpio_level[irq] = false;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
+ wm831x->gpio_level[irq] = true;
break;
default:
return -EINVAL;
}
- return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq,
- WM831X_GPN_INT_MODE | WM831X_GPN_POL, val);
+ return 0;
}
static struct irq_chip wm831x_irq_chip = {
@@ -432,7 +456,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
{
struct wm831x *wm831x = data;
unsigned int i;
- int primary;
+ int primary, status_addr, ret;
int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
int read[WM831X_NUM_IRQ_REGS] = { 0 };
int *status;
@@ -467,8 +491,9 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
/* Hopefully there should only be one register to read
* each time otherwise we ought to do a block read. */
if (!read[offset]) {
- *status = wm831x_reg_read(wm831x,
- irq_data_to_status_reg(&wm831x_irqs[i]));
+ status_addr = irq_data_to_status_reg(&wm831x_irqs[i]);
+
+ *status = wm831x_reg_read(wm831x, status_addr);
if (*status < 0) {
dev_err(wm831x->dev,
"Failed to read IRQ status: %d\n",
@@ -477,26 +502,34 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
}
read[offset] = 1;
+
+ /* Ignore any bits that we don't think are masked */
+ *status &= ~wm831x->irq_masks_cur[offset];
+
+ /* Acknowledge now so we don't miss
+ * notifications while we handle.
+ */
+ wm831x_reg_write(wm831x, status_addr, *status);
}
- /* Report it if it isn't masked, or forget the status. */
- if ((*status & ~wm831x->irq_masks_cur[offset])
- & wm831x_irqs[i].mask)
+ if (*status & wm831x_irqs[i].mask)
handle_nested_irq(wm831x->irq_base + i);
- else
- *status &= ~wm831x_irqs[i].mask;
- }
-out:
- /* Touchscreen interrupts are handled specially in the driver */
- status_regs[0] &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT);
-
- for (i = 0; i < ARRAY_SIZE(status_regs); i++) {
- if (status_regs[i])
- wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i,
- status_regs[i]);
+ /* Simulate an edge triggered IRQ by polling the input
+ * status. This is sucky but improves interoperability.
+ */
+ if (primary == WM831X_GP_INT &&
+ wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) {
+ ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
+ while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) {
+ handle_nested_irq(wm831x->irq_base + i);
+ ret = wm831x_reg_read(wm831x,
+ WM831X_GPIO_LEVEL);
+ }
+ }
}
+out:
return IRQ_HANDLED;
}
@@ -515,13 +548,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
0xffff);
}
- if (!pdata || !pdata->irq_base) {
- dev_err(wm831x->dev,
- "No interrupt base specified, no interrupts\n");
+ /* Try to dynamically allocate IRQs if no base is specified */
+ if (!pdata || !pdata->irq_base)
+ wm831x->irq_base = -1;
+ else
+ wm831x->irq_base = pdata->irq_base;
+
+ wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0,
+ WM831X_NUM_IRQS, 0);
+ if (wm831x->irq_base < 0) {
+ dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
+ wm831x->irq_base);
+ wm831x->irq_base = 0;
return 0;
}
- if (pdata->irq_cmos)
+ if (pdata && pdata->irq_cmos)
i = 0;
else
i = WM831X_IRQ_OD;
@@ -541,7 +583,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
}
wm831x->irq = irq;
- wm831x->irq_base = pdata->irq_base;
/* Register them with genirq */
for (cur_irq = wm831x->irq_base;
@@ -575,8 +616,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
"No interrupt specified - functionality limited\n");
}
-
-
/* Enable top level interrupts, we mask at secondary level */
wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c
index eed8e4f7a5a1..8d6a9a969dbc 100644
--- a/drivers/mfd/wm831x-spi.c
+++ b/drivers/mfd/wm831x-spi.c
@@ -16,78 +16,19 @@
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
#include <linux/mfd/wm831x/core.h>
-static int wm831x_spi_read_device(struct wm831x *wm831x, unsigned short reg,
- int bytes, void *dest)
-{
- u16 tx_val;
- u16 *d = dest;
- int r, ret;
-
- /* Go register at a time */
- for (r = reg; r < reg + (bytes / 2); r++) {
- tx_val = r | 0x8000;
-
- ret = spi_write_then_read(wm831x->control_data,
- (u8 *)&tx_val, 2, (u8 *)d, 2);
- if (ret != 0)
- return ret;
-
- *d = be16_to_cpu(*d);
-
- d++;
- }
-
- return 0;
-}
-
-static int wm831x_spi_write_device(struct wm831x *wm831x, unsigned short reg,
- int bytes, void *src)
-{
- struct spi_device *spi = wm831x->control_data;
- u16 *s = src;
- u16 data[2];
- int ret, r;
-
- /* Go register at a time */
- for (r = reg; r < reg + (bytes / 2); r++) {
- data[0] = r;
- data[1] = *s++;
-
- ret = spi_write(spi, (char *)&data, sizeof(data));
- if (ret != 0)
- return ret;
- }
-
- return 0;
-}
-
static int __devinit wm831x_spi_probe(struct spi_device *spi)
{
+ const struct spi_device_id *id = spi_get_device_id(spi);
struct wm831x *wm831x;
enum wm831x_parent type;
+ int ret;
- /* Currently SPI support for ID tables is unmerged, we're faking it */
- if (strcmp(spi->modalias, "wm8310") == 0)
- type = WM8310;
- else if (strcmp(spi->modalias, "wm8311") == 0)
- type = WM8311;
- else if (strcmp(spi->modalias, "wm8312") == 0)
- type = WM8312;
- else if (strcmp(spi->modalias, "wm8320") == 0)
- type = WM8320;
- else if (strcmp(spi->modalias, "wm8321") == 0)
- type = WM8321;
- else if (strcmp(spi->modalias, "wm8325") == 0)
- type = WM8325;
- else if (strcmp(spi->modalias, "wm8326") == 0)
- type = WM8326;
- else {
- dev_err(&spi->dev, "Unknown device type\n");
- return -EINVAL;
- }
+ type = (enum wm831x_parent)id->driver_data;
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
if (wm831x == NULL)
@@ -98,9 +39,15 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi)
dev_set_drvdata(&spi->dev, wm831x);
wm831x->dev = &spi->dev;
- wm831x->control_data = spi;
- wm831x->read_dev = wm831x_spi_read_device;
- wm831x->write_dev = wm831x_spi_write_device;
+
+ wm831x->regmap = regmap_init_spi(spi, &wm831x_regmap_config);
+ if (IS_ERR(wm831x->regmap)) {
+ ret = PTR_ERR(wm831x->regmap);
+ dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
+ ret);
+ kfree(wm831x);
+ return ret;
+ }
return wm831x_device_init(wm831x, type, spi->irq);
}
@@ -121,119 +68,50 @@ static int wm831x_spi_suspend(struct device *dev)
return wm831x_device_suspend(wm831x);
}
+static void wm831x_spi_shutdown(struct spi_device *spi)
+{
+ struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
+
+ wm831x_device_shutdown(wm831x);
+}
+
static const struct dev_pm_ops wm831x_spi_pm = {
.freeze = wm831x_spi_suspend,
.suspend = wm831x_spi_suspend,
};
-static struct spi_driver wm8310_spi_driver = {
- .driver = {
- .name = "wm8310",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- .pm = &wm831x_spi_pm,
- },
- .probe = wm831x_spi_probe,
- .remove = __devexit_p(wm831x_spi_remove),
+static const struct spi_device_id wm831x_spi_ids[] = {
+ { "wm8310", WM8310 },
+ { "wm8311", WM8311 },
+ { "wm8312", WM8312 },
+ { "wm8320", WM8320 },
+ { "wm8321", WM8321 },
+ { "wm8325", WM8325 },
+ { "wm8326", WM8326 },
+ { },
};
+MODULE_DEVICE_TABLE(spi, wm831x_spi_id);
-static struct spi_driver wm8311_spi_driver = {
+static struct spi_driver wm831x_spi_driver = {
.driver = {
- .name = "wm8311",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- .pm = &wm831x_spi_pm,
- },
- .probe = wm831x_spi_probe,
- .remove = __devexit_p(wm831x_spi_remove),
-};
-
-static struct spi_driver wm8312_spi_driver = {
- .driver = {
- .name = "wm8312",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- .pm = &wm831x_spi_pm,
- },
- .probe = wm831x_spi_probe,
- .remove = __devexit_p(wm831x_spi_remove),
-};
-
-static struct spi_driver wm8320_spi_driver = {
- .driver = {
- .name = "wm8320",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- .pm = &wm831x_spi_pm,
- },
- .probe = wm831x_spi_probe,
- .remove = __devexit_p(wm831x_spi_remove),
-};
-
-static struct spi_driver wm8321_spi_driver = {
- .driver = {
- .name = "wm8321",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- .pm = &wm831x_spi_pm,
- },
- .probe = wm831x_spi_probe,
- .remove = __devexit_p(wm831x_spi_remove),
-};
-
-static struct spi_driver wm8325_spi_driver = {
- .driver = {
- .name = "wm8325",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- .pm = &wm831x_spi_pm,
- },
- .probe = wm831x_spi_probe,
- .remove = __devexit_p(wm831x_spi_remove),
-};
-
-static struct spi_driver wm8326_spi_driver = {
- .driver = {
- .name = "wm8326",
+ .name = "wm831x",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.pm = &wm831x_spi_pm,
},
+ .id_table = wm831x_spi_ids,
.probe = wm831x_spi_probe,
.remove = __devexit_p(wm831x_spi_remove),
+ .shutdown = wm831x_spi_shutdown,
};
static int __init wm831x_spi_init(void)
{
int ret;
- ret = spi_register_driver(&wm8310_spi_driver);
- if (ret != 0)
- pr_err("Failed to register WM8310 SPI driver: %d\n", ret);
-
- ret = spi_register_driver(&wm8311_spi_driver);
- if (ret != 0)
- pr_err("Failed to register WM8311 SPI driver: %d\n", ret);
-
- ret = spi_register_driver(&wm8312_spi_driver);
- if (ret != 0)
- pr_err("Failed to register WM8312 SPI driver: %d\n", ret);
-
- ret = spi_register_driver(&wm8320_spi_driver);
- if (ret != 0)
- pr_err("Failed to register WM8320 SPI driver: %d\n", ret);
-
- ret = spi_register_driver(&wm8321_spi_driver);
- if (ret != 0)
- pr_err("Failed to register WM8321 SPI driver: %d\n", ret);
-
- ret = spi_register_driver(&wm8325_spi_driver);
- if (ret != 0)
- pr_err("Failed to register WM8325 SPI driver: %d\n", ret);
-
- ret = spi_register_driver(&wm8326_spi_driver);
+ ret = spi_register_driver(&wm831x_spi_driver);
if (ret != 0)
- pr_err("Failed to register WM8326 SPI driver: %d\n", ret);
+ pr_err("Failed to register WM831x SPI driver: %d\n", ret);
return 0;
}
@@ -241,13 +119,7 @@ subsys_initcall(wm831x_spi_init);
static void __exit wm831x_spi_exit(void)
{
- spi_unregister_driver(&wm8326_spi_driver);
- spi_unregister_driver(&wm8325_spi_driver);
- spi_unregister_driver(&wm8321_spi_driver);
- spi_unregister_driver(&wm8320_spi_driver);
- spi_unregister_driver(&wm8312_spi_driver);
- spi_unregister_driver(&wm8311_spi_driver);
- spi_unregister_driver(&wm8310_spi_driver);
+ spi_unregister_driver(&wm831x_spi_driver);
}
module_exit(wm831x_spi_exit);
diff --git a/drivers/mfd/wm8350-gpio.c b/drivers/mfd/wm8350-gpio.c
index ebf99bef392f..d584f6b4d6e2 100644
--- a/drivers/mfd/wm8350-gpio.c
+++ b/drivers/mfd/wm8350-gpio.c
@@ -37,7 +37,7 @@ static int gpio_set_dir(struct wm8350 *wm8350, int gpio, int dir)
return ret;
}
-static int gpio_set_debounce(struct wm8350 *wm8350, int gpio, int db)
+static int wm8350_gpio_set_debounce(struct wm8350 *wm8350, int gpio, int db)
{
if (db == WM8350_GPIO_DEBOUNCE_ON)
return wm8350_set_bits(wm8350, WM8350_GPIO_DEBOUNCE,
@@ -210,7 +210,7 @@ int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func,
goto err;
if (gpio_set_polarity(wm8350, gpio, pol))
goto err;
- if (gpio_set_debounce(wm8350, gpio, debounce))
+ if (wm8350_gpio_set_debounce(wm8350, gpio, debounce))
goto err;
if (gpio_set_dir(wm8350, gpio, dir))
goto err;
diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c
index ed4b22a167b3..8a1fafd0bf7d 100644
--- a/drivers/mfd/wm8350-irq.c
+++ b/drivers/mfd/wm8350-irq.c
@@ -473,17 +473,13 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq,
{
int ret, cur_irq, i;
int flags = IRQF_ONESHOT;
+ int irq_base = -1;
if (!irq) {
dev_warn(wm8350->dev, "No interrupt support, no core IRQ\n");
return 0;
}
- if (!pdata || !pdata->irq_base) {
- dev_warn(wm8350->dev, "No interrupt support, no IRQ base\n");
- return 0;
- }
-
/* Mask top level interrupts */
wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
@@ -502,7 +498,17 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq,
wm8350->chip_irq = irq;
wm8350->irq_base = pdata->irq_base;
- if (pdata->irq_high) {
+ if (pdata && pdata->irq_base > 0)
+ irq_base = pdata->irq_base;
+
+ wm8350->irq_base = irq_alloc_descs(irq_base, 0, ARRAY_SIZE(wm8350_irqs), 0);
+ if (wm8350->irq_base < 0) {
+ dev_warn(wm8350->dev, "Allocating irqs failed with %d\n",
+ wm8350->irq_base);
+ return 0;
+ }
+
+ if (pdata && pdata->irq_high) {
flags |= IRQF_TRIGGER_HIGH;
wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
index 597f82edacaa..62b4626f4561 100644
--- a/drivers/mfd/wm8400-core.c
+++ b/drivers/mfd/wm8400-core.c
@@ -12,12 +12,15 @@
*
*/
+#include <linux/module.h>
#include <linux/bug.h>
+#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/mfd/core.h>
#include <linux/mfd/wm8400-private.h>
#include <linux/mfd/wm8400-audio.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
static struct {
@@ -123,14 +126,9 @@ static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest)
/* If there are any volatile reads then read back the entire block */
for (i = reg; i < reg + num_regs; i++)
if (reg_data[i].vol) {
- ret = wm8400->read_dev(wm8400->io_data, reg,
- num_regs, dest);
- if (ret != 0)
- return ret;
- for (i = 0; i < num_regs; i++)
- dest[i] = be16_to_cpu(dest[i]);
-
- return 0;
+ ret = regmap_bulk_read(wm8400->regmap, reg, dest,
+ num_regs);
+ return ret;
}
/* Otherwise use the cache */
@@ -149,14 +147,11 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
for (i = 0; i < num_regs; i++) {
BUG_ON(!reg_data[reg + i].writable);
wm8400->reg_cache[reg + i] = src[i];
- src[i] = cpu_to_be16(src[i]);
+ ret = regmap_write(wm8400->regmap, reg, src[i]);
+ if (ret != 0)
+ return ret;
}
- /* Do the actual I/O */
- ret = wm8400->write_dev(wm8400->io_data, reg, num_regs, src);
- if (ret != 0)
- return -EIO;
-
return 0;
}
@@ -270,14 +265,14 @@ static int wm8400_init(struct wm8400 *wm8400,
dev_set_drvdata(wm8400->dev, wm8400);
/* Check that this is actually a WM8400 */
- ret = wm8400->read_dev(wm8400->io_data, WM8400_RESET_ID, 1, &reg);
+ ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &i);
if (ret != 0) {
dev_err(wm8400->dev, "Chip ID register read failed\n");
return -EIO;
}
- if (be16_to_cpu(reg) != reg_data[WM8400_RESET_ID].default_val) {
+ if (i != reg_data[WM8400_RESET_ID].default_val) {
dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n",
- be16_to_cpu(reg));
+ reg);
return -ENODEV;
}
@@ -285,9 +280,8 @@ static int wm8400_init(struct wm8400 *wm8400,
* is a PMIC we can't reset it safely so initialise the register
* cache from the hardware.
*/
- ret = wm8400->read_dev(wm8400->io_data, 0,
- ARRAY_SIZE(wm8400->reg_cache),
- wm8400->reg_cache);
+ ret = regmap_raw_read(wm8400->regmap, 0, wm8400->reg_cache,
+ ARRAY_SIZE(wm8400->reg_cache));
if (ret != 0) {
dev_err(wm8400->dev, "Register cache read failed\n");
return -EIO;
@@ -337,60 +331,13 @@ static void wm8400_release(struct wm8400 *wm8400)
mfd_remove_devices(wm8400->dev);
}
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static int wm8400_i2c_read(void *io_data, char reg, int count, u16 *dest)
-{
- struct i2c_client *i2c = io_data;
- struct i2c_msg xfer[2];
- int ret;
-
- /* Write register */
- xfer[0].addr = i2c->addr;
- xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
-
- /* Read data */
- xfer[1].addr = i2c->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = count * sizeof(u16);
- xfer[1].buf = (u8 *)dest;
-
- ret = i2c_transfer(i2c->adapter, xfer, 2);
- if (ret == 2)
- ret = 0;
- else if (ret >= 0)
- ret = -EIO;
-
- return ret;
-}
-
-static int wm8400_i2c_write(void *io_data, char reg, int count, const u16 *src)
-{
- struct i2c_client *i2c = io_data;
- u8 *msg;
- int ret;
-
- /* We add 1 byte for device register - ideally I2C would gather. */
- msg = kmalloc((count * sizeof(u16)) + 1, GFP_KERNEL);
- if (msg == NULL)
- return -ENOMEM;
-
- msg[0] = reg;
- memcpy(&msg[1], src, count * sizeof(u16));
-
- ret = i2c_master_send(i2c, msg, (count * sizeof(u16)) + 1);
-
- if (ret == (count * 2) + 1)
- ret = 0;
- else if (ret >= 0)
- ret = -EIO;
-
- kfree(msg);
-
- return ret;
-}
+static const struct regmap_config wm8400_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = WM8400_REGISTER_COUNT - 1,
+};
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int wm8400_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -403,18 +350,23 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
goto err;
}
- wm8400->io_data = i2c;
- wm8400->read_dev = wm8400_i2c_read;
- wm8400->write_dev = wm8400_i2c_write;
+ wm8400->regmap = regmap_init_i2c(i2c, &wm8400_regmap_config);
+ if (IS_ERR(wm8400->regmap)) {
+ ret = PTR_ERR(wm8400->regmap);
+ goto struct_err;
+ }
+
wm8400->dev = &i2c->dev;
i2c_set_clientdata(i2c, wm8400);
ret = wm8400_init(wm8400, i2c->dev.platform_data);
if (ret != 0)
- goto struct_err;
+ goto map_err;
return 0;
+map_err:
+ regmap_exit(wm8400->regmap);
struct_err:
kfree(wm8400);
err:
@@ -426,6 +378,7 @@ static int wm8400_i2c_remove(struct i2c_client *i2c)
struct wm8400 *wm8400 = i2c_get_clientdata(i2c);
wm8400_release(wm8400);
+ regmap_exit(wm8400->regmap);
kfree(wm8400);
return 0;
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index e198d40292e7..5d6ba132837e 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -16,9 +16,11 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/err.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/machine.h>
@@ -29,22 +31,7 @@
static int wm8994_read(struct wm8994 *wm8994, unsigned short reg,
int bytes, void *dest)
{
- int ret, i;
- u16 *buf = dest;
-
- BUG_ON(bytes % 2);
- BUG_ON(bytes <= 0);
-
- ret = wm8994->read_dev(wm8994, reg, bytes, dest);
- if (ret < 0)
- return ret;
-
- for (i = 0; i < bytes / 2; i++) {
- dev_vdbg(wm8994->dev, "Read %04x from R%d(0x%x)\n",
- be16_to_cpu(buf[i]), reg + i, reg + i);
- }
-
- return 0;
+ return regmap_raw_read(wm8994->regmap, reg, dest, bytes);
}
/**
@@ -55,19 +42,15 @@ static int wm8994_read(struct wm8994 *wm8994, unsigned short reg,
*/
int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg)
{
- unsigned short val;
+ unsigned int val;
int ret;
- mutex_lock(&wm8994->io_lock);
-
- ret = wm8994_read(wm8994, reg, 2, &val);
-
- mutex_unlock(&wm8994->io_lock);
+ ret = regmap_read(wm8994->regmap, reg, &val);
if (ret < 0)
return ret;
else
- return be16_to_cpu(val);
+ return val;
}
EXPORT_SYMBOL_GPL(wm8994_reg_read);
@@ -82,33 +65,13 @@ EXPORT_SYMBOL_GPL(wm8994_reg_read);
int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
int count, u16 *buf)
{
- int ret;
-
- mutex_lock(&wm8994->io_lock);
-
- ret = wm8994_read(wm8994, reg, count * 2, buf);
-
- mutex_unlock(&wm8994->io_lock);
-
- return ret;
+ return regmap_bulk_read(wm8994->regmap, reg, buf, count);
}
-EXPORT_SYMBOL_GPL(wm8994_bulk_read);
static int wm8994_write(struct wm8994 *wm8994, unsigned short reg,
int bytes, const void *src)
{
- const u16 *buf = src;
- int i;
-
- BUG_ON(bytes % 2);
- BUG_ON(bytes <= 0);
-
- for (i = 0; i < bytes / 2; i++) {
- dev_vdbg(wm8994->dev, "Write %04x to R%d(0x%x)\n",
- be16_to_cpu(buf[i]), reg + i, reg + i);
- }
-
- return wm8994->write_dev(wm8994, reg, bytes, src);
+ return regmap_raw_write(wm8994->regmap, reg, src, bytes);
}
/**
@@ -121,17 +84,7 @@ static int wm8994_write(struct wm8994 *wm8994, unsigned short reg,
int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
unsigned short val)
{
- int ret;
-
- val = cpu_to_be16(val);
-
- mutex_lock(&wm8994->io_lock);
-
- ret = wm8994_write(wm8994, reg, 2, &val);
-
- mutex_unlock(&wm8994->io_lock);
-
- return ret;
+ return regmap_write(wm8994->regmap, reg, val);
}
EXPORT_SYMBOL_GPL(wm8994_reg_write);
@@ -146,15 +99,7 @@ EXPORT_SYMBOL_GPL(wm8994_reg_write);
int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg,
int count, const u16 *buf)
{
- int ret;
-
- mutex_lock(&wm8994->io_lock);
-
- ret = wm8994_write(wm8994, reg, count * 2, buf);
-
- mutex_unlock(&wm8994->io_lock);
-
- return ret;
+ return regmap_raw_write(wm8994->regmap, reg, buf, count * sizeof(u16));
}
EXPORT_SYMBOL_GPL(wm8994_bulk_write);
@@ -169,28 +114,7 @@ EXPORT_SYMBOL_GPL(wm8994_bulk_write);
int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
unsigned short mask, unsigned short val)
{
- int ret;
- u16 r;
-
- mutex_lock(&wm8994->io_lock);
-
- ret = wm8994_read(wm8994, reg, 2, &r);
- if (ret < 0)
- goto out;
-
- r = be16_to_cpu(r);
-
- r &= ~mask;
- r |= val;
-
- r = cpu_to_be16(r);
-
- ret = wm8994_write(wm8994, reg, 2, &r);
-
-out:
- mutex_unlock(&wm8994->io_lock);
-
- return ret;
+ return regmap_update_bits(wm8994->regmap, reg, mask, val);
}
EXPORT_SYMBOL_GPL(wm8994_set_bits);
@@ -243,6 +167,18 @@ static struct mfd_cell wm8994_devs[] = {
* and should be handled via the standard regulator API supply
* management.
*/
+static const char *wm1811_main_supplies[] = {
+ "DBVDD1",
+ "DBVDD2",
+ "DBVDD3",
+ "DCVDD",
+ "AVDD1",
+ "AVDD2",
+ "CPVDD",
+ "SPKVDD1",
+ "SPKVDD2",
+};
+
static const char *wm8994_main_supplies[] = {
"DBVDD",
"DCVDD",
@@ -281,6 +217,47 @@ static int wm8994_suspend(struct device *dev)
return 0;
}
+ ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_4);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read power status: %d\n", ret);
+ } else if (ret & (WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA |
+ WM8994_AIF1ADC2L_ENA | WM8994_AIF1ADC2R_ENA |
+ WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC1R_ENA)) {
+ dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+ return 0;
+ }
+
+ ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_5);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read power status: %d\n", ret);
+ } else if (ret & (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA |
+ WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA |
+ WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA)) {
+ dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+ return 0;
+ }
+
+ switch (wm8994->type) {
+ case WM8958:
+ ret = wm8994_reg_read(wm8994, WM8958_MIC_DETECT_1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read power status: %d\n", ret);
+ } else if (ret & WM8958_MICD_ENA) {
+ dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Disable LDO pulldowns while the device is suspended if we
+ * don't know that something will be driving them. */
+ if (!wm8994->ldo_ena_always_driven)
+ wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD);
+
/* GPIO configuration state is saved here since we may be configuring
* the GPIO alternate functions even if we're not using the gpiolib
* driver for them.
@@ -316,7 +293,7 @@ static int wm8994_suspend(struct device *dev)
static int wm8994_resume(struct device *dev)
{
struct wm8994 *wm8994 = dev_get_drvdata(dev);
- int ret;
+ int ret, i;
/* We may have lied to the PM core about suspending */
if (!wm8994->suspended)
@@ -329,10 +306,16 @@ static int wm8994_resume(struct device *dev)
return ret;
}
- ret = wm8994_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK,
- WM8994_NUM_IRQ_REGS * 2, &wm8994->irq_masks_cur);
- if (ret < 0)
- dev_err(dev, "Failed to restore interrupt masks: %d\n", ret);
+ /* Write register at a time as we use the cache on the CPU so store
+ * it in native endian.
+ */
+ for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) {
+ ret = wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK
+ + i, wm8994->irq_masks_cur[i]);
+ if (ret < 0)
+ dev_err(dev, "Failed to restore interrupt masks: %d\n",
+ ret);
+ }
ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
&wm8994->ldo_regs);
@@ -344,6 +327,11 @@ static int wm8994_resume(struct device *dev)
if (ret < 0)
dev_err(dev, "Failed to restore GPIO registers: %d\n", ret);
+ /* Disable LDO pulldowns while the device is active */
+ wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
+ 0);
+
wm8994->suspended = false;
return 0;
@@ -372,6 +360,11 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
}
#endif
+static struct regmap_config wm8994_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+};
+
/*
* Instantiate the generic non-control parts of the device.
*/
@@ -381,7 +374,6 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
const char *devname;
int ret, i;
- mutex_init(&wm8994->io_lock);
dev_set_drvdata(wm8994->dev, wm8994);
/* Add the on-chip regulators first for bootstrapping */
@@ -391,10 +383,13 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
NULL, 0);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
- goto err;
+ goto err_regmap;
}
switch (wm8994->type) {
+ case WM1811:
+ wm8994->num_supplies = ARRAY_SIZE(wm1811_main_supplies);
+ break;
case WM8994:
wm8994->num_supplies = ARRAY_SIZE(wm8994_main_supplies);
break;
@@ -403,7 +398,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
break;
default:
BUG();
- return -EINVAL;
+ goto err_regmap;
}
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
@@ -411,10 +406,14 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
GFP_KERNEL);
if (!wm8994->supplies) {
ret = -ENOMEM;
- goto err;
+ goto err_regmap;
}
switch (wm8994->type) {
+ case WM1811:
+ for (i = 0; i < ARRAY_SIZE(wm1811_main_supplies); i++)
+ wm8994->supplies[i].supply = wm1811_main_supplies[i];
+ break;
case WM8994:
for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
wm8994->supplies[i].supply = wm8994_main_supplies[i];
@@ -425,7 +424,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
break;
default:
BUG();
- return -EINVAL;
+ goto err_regmap;
}
ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
@@ -448,6 +447,13 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
goto err_enable;
}
switch (ret) {
+ case 0x1811:
+ devname = "WM1811";
+ if (wm8994->type != WM1811)
+ dev_warn(wm8994->dev, "Device registered as type %d\n",
+ wm8994->type);
+ wm8994->type = WM1811;
+ break;
case 0x8994:
devname = "WM8994";
if (wm8994->type != WM8994)
@@ -476,13 +482,18 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
goto err_enable;
}
- switch (ret) {
- case 0:
- case 1:
- if (wm8994->type == WM8994)
+ switch (wm8994->type) {
+ case WM8994:
+ switch (ret) {
+ case 0:
+ case 1:
dev_warn(wm8994->dev,
"revision %c not fully supported\n",
'A' + ret);
+ break;
+ default:
+ break;
+ }
break;
default:
break;
@@ -502,8 +513,15 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
pdata->gpio_defaults[i]);
}
}
+
+ wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven;
}
+ /* Disable LDO pulldowns while the device is active */
+ wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
+ 0);
+
/* In some system designs where the regulators are not in use,
* we can achieve a small reduction in leakage currents by
* floating LDO outputs. This bit makes no difference if the
@@ -543,7 +561,8 @@ err_get:
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
err_supplies:
kfree(wm8994->supplies);
-err:
+err_regmap:
+ regmap_exit(wm8994->regmap);
mfd_remove_devices(wm8994->dev);
kfree(wm8994);
return ret;
@@ -558,62 +577,15 @@ static void wm8994_device_exit(struct wm8994 *wm8994)
wm8994->supplies);
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
kfree(wm8994->supplies);
+ regmap_exit(wm8994->regmap);
kfree(wm8994);
}
-static int wm8994_i2c_read_device(struct wm8994 *wm8994, unsigned short reg,
- int bytes, void *dest)
-{
- struct i2c_client *i2c = wm8994->control_data;
- int ret;
- u16 r = cpu_to_be16(reg);
-
- ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
- if (ret < 0)
- return ret;
- if (ret != 2)
- return -EIO;
-
- ret = i2c_master_recv(i2c, dest, bytes);
- if (ret < 0)
- return ret;
- if (ret != bytes)
- return -EIO;
- return 0;
-}
-
-static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg,
- int bytes, const void *src)
-{
- struct i2c_client *i2c = wm8994->control_data;
- struct i2c_msg xfer[2];
- int ret;
-
- reg = cpu_to_be16(reg);
-
- xfer[0].addr = i2c->addr;
- xfer[0].flags = 0;
- xfer[0].len = 2;
- xfer[0].buf = (char *)&reg;
-
- xfer[1].addr = i2c->addr;
- xfer[1].flags = I2C_M_NOSTART;
- xfer[1].len = bytes;
- xfer[1].buf = (char *)src;
-
- ret = i2c_transfer(i2c->adapter, xfer, 2);
- if (ret < 0)
- return ret;
- if (ret != 2)
- return -EIO;
-
- return 0;
-}
-
static int wm8994_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8994 *wm8994;
+ int ret;
wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL);
if (wm8994 == NULL)
@@ -621,12 +593,18 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8994);
wm8994->dev = &i2c->dev;
- wm8994->control_data = i2c;
- wm8994->read_dev = wm8994_i2c_read_device;
- wm8994->write_dev = wm8994_i2c_write_device;
wm8994->irq = i2c->irq;
wm8994->type = id->driver_data;
+ wm8994->regmap = regmap_init_i2c(i2c, &wm8994_regmap_config);
+ if (IS_ERR(wm8994->regmap)) {
+ ret = PTR_ERR(wm8994->regmap);
+ dev_err(wm8994->dev, "Failed to allocate register map: %d\n",
+ ret);
+ kfree(wm8994);
+ return ret;
+ }
+
return wm8994_device_init(wm8994, i2c->irq);
}
@@ -640,6 +618,7 @@ static int wm8994_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8994_i2c_id[] = {
+ { "wm1811", WM1811 },
{ "wm8994", WM8994 },
{ "wm8958", WM8958 },
{ }
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index 71c6e8f9aedb..d682f7bd112c 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -231,12 +231,6 @@ static irqreturn_t wm8994_irq_thread(int irq, void *data)
status[i] &= ~wm8994->irq_masks_cur[i];
}
- /* Report */
- for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
- if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
- handle_nested_irq(wm8994->irq_base + i);
- }
-
/* Ack any unmasked IRQs */
for (i = 0; i < ARRAY_SIZE(status); i++) {
if (status[i])
@@ -244,6 +238,12 @@ static irqreturn_t wm8994_irq_thread(int irq, void *data)
status[i]);
}
+ /* Report */
+ for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
+ if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
+ handle_nested_irq(wm8994->irq_base + i);
+ }
+
return IRQ_HANDLED;
}
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4e349cd98bcf..5664696f2d3a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -63,7 +63,7 @@ config AD525X_DPOT_SPI
config ATMEL_PWM
tristate "Atmel AT32/AT91 PWM support"
- depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
+ depends on HAVE_CLK
help
This option enables device driver support for the PWM channels
on certain Atmel processors. Pulse Width Modulation is used for
@@ -146,6 +146,7 @@ config PHANTOM
config INTEL_MID_PTI
tristate "Parallel Trace Interface for MIPI P1149.7 cJTAG standard"
+ depends on PCI
default n
help
The PTI (Parallel Trace Interface) driver directs
@@ -245,8 +246,7 @@ config SGI_XP
config CS5535_MFGPT
tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
- depends on PCI
- depends on X86
+ depends on PCI && X86 && MFD_CS5535
default n
help
This driver provides access to MFGPT functionality for other
@@ -472,7 +472,7 @@ config BMP085
module will be called bmp085.
config PCH_PHUB
- tristate "Intel EG20T PCH / OKI SEMICONDUCTOR IOH(ML7213/ML7223) PHUB"
+ tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
depends on PCI
help
This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of
@@ -480,16 +480,26 @@ config PCH_PHUB
processor. The Topcliff has MAC address and Option ROM data in SROM.
This driver can access MAC address and Option ROM data in SROM.
- This driver also can be used for OKI SEMICONDUCTOR IOH(Input/
- Output Hub), ML7213 and ML7223.
- ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is
- for MP(Media Phone) use.
- ML7213/ML7223 is companion chip for Intel Atom E6xx series.
- ML7213/ML7223 is completely compatible for Intel EG20T PCH.
+ This driver also can be used for LAPIS Semiconductor's IOH,
+ ML7213/ML7223/ML7831.
+ ML7213 which is for IVI(In-Vehicle Infotainment) use.
+ ML7223 IOH is for MP(Media Phone) use.
+ ML7831 IOH is for general purpose use.
+ ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
+ ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
To compile this driver as a module, choose M here: the module will
be called pch_phub.
+config USB_SWITCH_FSA9480
+ tristate "FSA9480 USB Switch"
+ depends on I2C
+ help
+ The FSA9480 is a USB port accessory detector and switch.
+ The FSA9480 is fully controlled using I2C and enables USB data,
+ stereo and mono audio, video, microphone and UART data to use
+ a common connector port.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
@@ -497,5 +507,6 @@ source "drivers/misc/iwmc3200top/Kconfig"
source "drivers/misc/ti-st/Kconfig"
source "drivers/misc/lis3lv02d/Kconfig"
source "drivers/misc/carma/Kconfig"
+source "drivers/misc/altera-stapl/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 5f03172cc0b5..b26495a02554 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -6,7 +6,7 @@ obj-$(CONFIG_IBM_ASM) += ibmasm/
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
-0bj-$(CONFIG_INTEL_MID_PTI) += pti.o
+obj-$(CONFIG_INTEL_MID_PTI) += pti.o
obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
@@ -46,3 +46,5 @@ obj-y += ti-st/
obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
obj-y += lis3lv02d/
obj-y += carma/
+obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
+obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c
index 54e3d05b63cc..2208a9d52622 100644
--- a/drivers/misc/ab8500-pwm.c
+++ b/drivers/misc/ab8500-pwm.c
@@ -10,6 +10,7 @@
#include <linux/pwm.h>
#include <linux/mfd/ab8500.h>
#include <linux/mfd/abx500.h>
+#include <linux/module.h>
/*
* PWM Out generators
@@ -164,5 +165,5 @@ subsys_initcall(ab8500_pwm_init);
module_exit(ab8500_pwm_exit);
MODULE_AUTHOR("Arun MURTHY <arun.murthy@stericsson.com>");
MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver");
-MODULE_ALIAS("AB8500 PWM driver");
+MODULE_ALIAS("platform:ab8500-pwm");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c
index 4ff73c215746..a39e0555df63 100644
--- a/drivers/misc/ad525x_dpot-i2c.c
+++ b/drivers/misc/ad525x_dpot-i2c.c
@@ -98,6 +98,7 @@ static const struct i2c_device_id ad_dpot_id[] = {
{"ad5282", AD5282_ID},
{"adn2860", ADN2860_ID},
{"ad5273", AD5273_ID},
+ {"ad5161", AD5161_ID},
{"ad5171", AD5171_ID},
{"ad5170", AD5170_ID},
{"ad5172", AD5172_ID},
diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h
index a662f5987b68..82b2cb77ae19 100644
--- a/drivers/misc/ad525x_dpot.h
+++ b/drivers/misc/ad525x_dpot.h
@@ -100,7 +100,7 @@ enum dpot_devid {
AD5293_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 10, 27),
AD7376_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
BRDAC0, 7, 28),
- AD8400_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+ AD8400_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
BRDAC0, 8, 29),
AD8402_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
BRDAC0 | BRDAC1, 8, 30),
diff --git a/drivers/staging/altera-stapl/Kconfig b/drivers/misc/altera-stapl/Kconfig
index 7f01d8e93992..7f01d8e93992 100644
--- a/drivers/staging/altera-stapl/Kconfig
+++ b/drivers/misc/altera-stapl/Kconfig
diff --git a/drivers/staging/altera-stapl/Makefile b/drivers/misc/altera-stapl/Makefile
index 055f61ee781a..055f61ee781a 100644
--- a/drivers/staging/altera-stapl/Makefile
+++ b/drivers/misc/altera-stapl/Makefile
diff --git a/drivers/staging/altera-stapl/altera-comp.c b/drivers/misc/altera-stapl/altera-comp.c
index 49b103bedaaf..49b103bedaaf 100644
--- a/drivers/staging/altera-stapl/altera-comp.c
+++ b/drivers/misc/altera-stapl/altera-comp.c
diff --git a/drivers/staging/altera-stapl/altera-exprt.h b/drivers/misc/altera-stapl/altera-exprt.h
index 39c38d84a670..39c38d84a670 100644
--- a/drivers/staging/altera-stapl/altera-exprt.h
+++ b/drivers/misc/altera-stapl/altera-exprt.h
diff --git a/drivers/staging/altera-stapl/altera-jtag.c b/drivers/misc/altera-stapl/altera-jtag.c
index 8b1620b1b2d0..f4bf20096972 100644
--- a/drivers/staging/altera-stapl/altera-jtag.c
+++ b/drivers/misc/altera-stapl/altera-jtag.c
@@ -26,7 +26,7 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/slab.h>
-#include "altera.h"
+#include <misc/altera.h>
#include "altera-exprt.h"
#include "altera-jtag.h"
diff --git a/drivers/staging/altera-stapl/altera-jtag.h b/drivers/misc/altera-stapl/altera-jtag.h
index 2f97e36a2fbc..2f97e36a2fbc 100644
--- a/drivers/staging/altera-stapl/altera-jtag.h
+++ b/drivers/misc/altera-stapl/altera-jtag.h
diff --git a/drivers/staging/altera-stapl/altera-lpt.c b/drivers/misc/altera-stapl/altera-lpt.c
index 91456a03612d..91456a03612d 100644
--- a/drivers/staging/altera-stapl/altera-lpt.c
+++ b/drivers/misc/altera-stapl/altera-lpt.c
diff --git a/drivers/staging/altera-stapl/altera.c b/drivers/misc/altera-stapl/altera.c
index 9cd5e76880c0..24272e022bec 100644
--- a/drivers/staging/altera-stapl/altera.c
+++ b/drivers/misc/altera-stapl/altera.c
@@ -28,7 +28,8 @@
#include <linux/string.h>
#include <linux/firmware.h>
#include <linux/slab.h>
-#include "altera.h"
+#include <linux/module.h>
+#include <misc/altera.h>
#include "altera-exprt.h"
#include "altera-jtag.h"
@@ -2384,7 +2385,7 @@ static int altera_get_act_info(u8 *p,
act_proc_attribute =
(p[proc_table + (13 * act_proc_id) + 8] & 0x03);
- procptr = (struct altera_procinfo *)
+ procptr =
kzalloc(sizeof(struct altera_procinfo),
GFP_KERNEL);
@@ -2430,16 +2431,23 @@ int altera_init(struct altera_config *config, const struct firmware *fw)
int index = 0;
s32 offset = 0L;
s32 error_address = 0L;
+ int retval = 0;
- key = kzalloc(33 * sizeof(char), GFP_KERNEL);
- if (!key)
- return -ENOMEM;
- value = kzalloc(257 * sizeof(char), GFP_KERNEL);
- if (!value)
- return -ENOMEM;
+ key = kzalloc(33, GFP_KERNEL);
+ if (!key) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ value = kzalloc(257, GFP_KERNEL);
+ if (!value) {
+ retval = -ENOMEM;
+ goto free_key;
+ }
astate = kzalloc(sizeof(struct altera_state), GFP_KERNEL);
- if (!astate)
- return -ENOMEM;
+ if (!astate) {
+ retval = -ENOMEM;
+ goto free_value;
+ }
astate->config = config;
if (!astate->config->jtag_io) {
@@ -2518,10 +2526,12 @@ int altera_init(struct altera_config *config, const struct firmware *fw)
} else if (exec_result)
printk(KERN_ERR "%s: error %d\n", __func__, exec_result);
- kfree(key);
- kfree(value);
kfree(astate);
-
- return 0;
+free_value:
+ kfree(value);
+free_key:
+ kfree(key);
+out:
+ return retval;
}
EXPORT_SYMBOL(altera_init);
diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c
index 4afffe610f99..5bb187781074 100644
--- a/drivers/misc/atmel-ssc.c
+++ b/drivers/misc/atmel-ssc.c
@@ -16,6 +16,7 @@
#include <linux/spinlock.h>
#include <linux/atmel-ssc.h>
#include <linux/slab.h>
+#include <linux/module.h>
/* Serialize access to ssc_list and user count */
static DEFINE_SPINLOCK(user_lock);
@@ -95,7 +96,7 @@ static int __init ssc_probe(struct platform_device *pdev)
}
ssc->pdev = pdev;
- ssc->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ ssc->regs = ioremap(regs->start, resource_size(regs));
if (!ssc->regs) {
dev_dbg(&pdev->dev, "ioremap failed\n");
retval = -EINVAL;
diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c
index 0f3fb4f03bdf..28f5aaa19d4a 100644
--- a/drivers/misc/atmel_pwm.c
+++ b/drivers/misc/atmel_pwm.c
@@ -329,7 +329,7 @@ static int __init pwm_probe(struct platform_device *pdev)
p->pdev = pdev;
p->mask = *mp;
p->irq = irq;
- p->base = ioremap(r->start, r->end - r->start + 1);
+ p->base = ioremap(r->start, resource_size(r));
if (!p->base)
goto fail;
p->clk = clk_get(&pdev->dev, "pwm_clk");
diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c
index a844810b50f6..4bcfc3759734 100644
--- a/drivers/misc/atmel_tclib.c
+++ b/drivers/misc/atmel_tclib.c
@@ -7,6 +7,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/export.h>
/* Number of bytes to reserve for the iomem resource */
#define ATMEL_TC_IOMEM_SIZE 256
diff --git a/drivers/misc/bh1780gli.c b/drivers/misc/bh1780gli.c
index 82fe2d067827..bfeea9ba702e 100644
--- a/drivers/misc/bh1780gli.c
+++ b/drivers/misc/bh1780gli.c
@@ -22,6 +22,7 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
+#include <linux/module.h>
#define BH1780_REG_CONTROL 0x80
#define BH1780_REG_PARTID 0x8A
diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c
index 7ce6065dc20e..eb5cd28bc6d8 100644
--- a/drivers/misc/carma/carma-fpga-program.c
+++ b/drivers/misc/carma/carma-fpga-program.c
@@ -945,8 +945,7 @@ static int fpga_of_remove(struct platform_device *op)
/* CTL-CPLD Version Register */
#define CTL_CPLD_VERSION 0x2000
-static int fpga_of_probe(struct platform_device *op,
- const struct of_device_id *match)
+static int fpga_of_probe(struct platform_device *op)
{
struct device_node *of_node = op->dev.of_node;
struct device *this_device;
@@ -1107,7 +1106,7 @@ static struct of_device_id fpga_of_match[] = {
{},
};
-static struct of_platform_driver fpga_of_driver = {
+static struct platform_driver fpga_of_driver = {
.probe = fpga_of_probe,
.remove = fpga_of_remove,
.driver = {
@@ -1124,12 +1123,12 @@ static struct of_platform_driver fpga_of_driver = {
static int __init fpga_init(void)
{
led_trigger_register_simple("fpga", &ledtrig_fpga);
- return of_register_platform_driver(&fpga_of_driver);
+ return platform_driver_register(&fpga_of_driver);
}
static void __exit fpga_exit(void)
{
- of_unregister_platform_driver(&fpga_of_driver);
+ platform_driver_unregister(&fpga_of_driver);
led_trigger_unregister_simple(ledtrig_fpga);
}
diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c
index 3965821fef17..14e974b2a781 100644
--- a/drivers/misc/carma/carma-fpga.c
+++ b/drivers/misc/carma/carma-fpga.c
@@ -1249,8 +1249,7 @@ static bool dma_filter(struct dma_chan *chan, void *data)
return true;
}
-static int data_of_probe(struct platform_device *op,
- const struct of_device_id *match)
+static int data_of_probe(struct platform_device *op)
{
struct device_node *of_node = op->dev.of_node;
struct device *this_device;
@@ -1401,7 +1400,7 @@ static struct of_device_id data_of_match[] = {
{},
};
-static struct of_platform_driver data_of_driver = {
+static struct platform_driver data_of_driver = {
.probe = data_of_probe,
.remove = data_of_remove,
.driver = {
@@ -1417,12 +1416,12 @@ static struct of_platform_driver data_of_driver = {
static int __init data_init(void)
{
- return of_register_platform_driver(&data_of_driver);
+ return platform_driver_register(&data_of_driver);
}
static void __exit data_exit(void)
{
- of_unregister_platform_driver(&data_of_driver);
+ platform_driver_unregister(&data_of_driver);
}
MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c
index efec4139c3f6..68cd05b6d829 100644
--- a/drivers/misc/cb710/core.c
+++ b/drivers/misc/cb710/core.c
@@ -33,7 +33,7 @@ EXPORT_SYMBOL_GPL(cb710_pci_update_config_reg);
static int __devinit cb710_pci_configure(struct pci_dev *pdev)
{
unsigned int devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
- struct pci_dev *pdev0 = pci_get_slot(pdev->bus, devfn);
+ struct pci_dev *pdev0;
u32 val;
cb710_pci_update_config_reg(pdev, 0x48,
@@ -43,6 +43,7 @@ static int __devinit cb710_pci_configure(struct pci_dev *pdev)
if (val & 0x80000000)
return 0;
+ pdev0 = pci_get_slot(pdev->bus, devfn);
if (!pdev0)
return -ENODEV;
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 9118613af321..701edf658970 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -70,4 +70,29 @@ config EEPROM_93CX6
If unsure, say N.
+config EEPROM_93XX46
+ tristate "Microwire EEPROM 93XX46 support"
+ depends on SPI && SYSFS
+ help
+ Driver for the microwire EEPROM chipsets 93xx46x. The driver
+ supports both read and write commands and also the command to
+ erase the whole EEPROM.
+
+ This driver can also be built as a module. If so, the module
+ will be called eeprom_93xx46.
+
+ If unsure, say N.
+
+config EEPROM_DIGSY_MTC_CFG
+ bool "DigsyMTC display configuration EEPROMs device"
+ depends on GPIO_MPC5200 && SPI_GPIO
+ help
+ This option enables access to display configuration EEPROMs
+ on digsy_mtc board. You have to additionally select Microwire
+ EEPROM 93XX46 driver. sysfs entries will be created for that
+ EEPROM allowing to read/write the configuration data or to
+ erase the whole EEPROM.
+
+ If unsure, say N.
+
endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index df3d68ffa9d1..fc1e81d29267 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -3,3 +3,5 @@ obj-$(CONFIG_EEPROM_AT25) += at25.o
obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
+obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
+obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
diff --git a/drivers/misc/eeprom/digsy_mtc_eeprom.c b/drivers/misc/eeprom/digsy_mtc_eeprom.c
new file mode 100644
index 000000000000..66d9e1baeae5
--- /dev/null
+++ b/drivers/misc/eeprom/digsy_mtc_eeprom.c
@@ -0,0 +1,85 @@
+/*
+ * EEPROMs access control driver for display configuration EEPROMs
+ * on DigsyMTC board.
+ *
+ * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_gpio.h>
+#include <linux/eeprom_93xx46.h>
+
+#define GPIO_EEPROM_CLK 216
+#define GPIO_EEPROM_CS 210
+#define GPIO_EEPROM_DI 217
+#define GPIO_EEPROM_DO 249
+#define GPIO_EEPROM_OE 255
+#define EE_SPI_BUS_NUM 1
+
+static void digsy_mtc_op_prepare(void *p)
+{
+ /* enable */
+ gpio_set_value(GPIO_EEPROM_OE, 0);
+}
+
+static void digsy_mtc_op_finish(void *p)
+{
+ /* disable */
+ gpio_set_value(GPIO_EEPROM_OE, 1);
+}
+
+struct eeprom_93xx46_platform_data digsy_mtc_eeprom_data = {
+ .flags = EE_ADDR8,
+ .prepare = digsy_mtc_op_prepare,
+ .finish = digsy_mtc_op_finish,
+};
+
+static struct spi_gpio_platform_data eeprom_spi_gpio_data = {
+ .sck = GPIO_EEPROM_CLK,
+ .mosi = GPIO_EEPROM_DI,
+ .miso = GPIO_EEPROM_DO,
+ .num_chipselect = 1,
+};
+
+static struct platform_device digsy_mtc_eeprom = {
+ .name = "spi_gpio",
+ .id = EE_SPI_BUS_NUM,
+ .dev = {
+ .platform_data = &eeprom_spi_gpio_data,
+ },
+};
+
+static struct spi_board_info digsy_mtc_eeprom_info[] __initdata = {
+ {
+ .modalias = "93xx46",
+ .max_speed_hz = 1000000,
+ .bus_num = EE_SPI_BUS_NUM,
+ .chip_select = 0,
+ .mode = SPI_MODE_0,
+ .controller_data = (void *)GPIO_EEPROM_CS,
+ .platform_data = &digsy_mtc_eeprom_data,
+ },
+};
+
+static int __init digsy_mtc_eeprom_devices_init(void)
+{
+ int ret;
+
+ ret = gpio_request_one(GPIO_EEPROM_OE, GPIOF_OUT_INIT_HIGH,
+ "93xx46 EEPROMs OE");
+ if (ret) {
+ pr_err("can't request gpio %d\n", GPIO_EEPROM_OE);
+ return ret;
+ }
+ spi_register_board_info(digsy_mtc_eeprom_info,
+ ARRAY_SIZE(digsy_mtc_eeprom_info));
+ return platform_device_register(&digsy_mtc_eeprom);
+}
+device_initcall(digsy_mtc_eeprom_devices_init);
diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c
new file mode 100644
index 000000000000..0c7ebb1e19e5
--- /dev/null
+++ b/drivers/misc/eeprom/eeprom_93xx46.c
@@ -0,0 +1,410 @@
+/*
+ * Driver for 93xx46 EEPROMs
+ *
+ * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/eeprom_93xx46.h>
+
+#define OP_START 0x4
+#define OP_WRITE (OP_START | 0x1)
+#define OP_READ (OP_START | 0x2)
+#define ADDR_EWDS 0x00
+#define ADDR_ERAL 0x20
+#define ADDR_EWEN 0x30
+
+struct eeprom_93xx46_dev {
+ struct spi_device *spi;
+ struct eeprom_93xx46_platform_data *pdata;
+ struct bin_attribute bin;
+ struct mutex lock;
+ int addrlen;
+};
+
+static ssize_t
+eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct eeprom_93xx46_dev *edev;
+ struct device *dev;
+ struct spi_message m;
+ struct spi_transfer t[2];
+ int bits, ret;
+ u16 cmd_addr;
+
+ dev = container_of(kobj, struct device, kobj);
+ edev = dev_get_drvdata(dev);
+
+ if (unlikely(off >= edev->bin.size))
+ return 0;
+ if ((off + count) > edev->bin.size)
+ count = edev->bin.size - off;
+ if (unlikely(!count))
+ return count;
+
+ cmd_addr = OP_READ << edev->addrlen;
+
+ if (edev->addrlen == 7) {
+ cmd_addr |= off & 0x7f;
+ bits = 10;
+ } else {
+ cmd_addr |= off & 0x3f;
+ bits = 9;
+ }
+
+ dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n",
+ cmd_addr, edev->spi->max_speed_hz);
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+
+ t[0].tx_buf = (char *)&cmd_addr;
+ t[0].len = 2;
+ t[0].bits_per_word = bits;
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].rx_buf = buf;
+ t[1].len = count;
+ t[1].bits_per_word = 8;
+ spi_message_add_tail(&t[1], &m);
+
+ mutex_lock(&edev->lock);
+
+ if (edev->pdata->prepare)
+ edev->pdata->prepare(edev);
+
+ ret = spi_sync(edev->spi, &m);
+ /* have to wait at least Tcsl ns */
+ ndelay(250);
+ if (ret) {
+ dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n",
+ count, (int)off, ret);
+ }
+
+ if (edev->pdata->finish)
+ edev->pdata->finish(edev);
+
+ mutex_unlock(&edev->lock);
+ return ret ? : count;
+}
+
+static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
+{
+ struct spi_message m;
+ struct spi_transfer t;
+ int bits, ret;
+ u16 cmd_addr;
+
+ cmd_addr = OP_START << edev->addrlen;
+ if (edev->addrlen == 7) {
+ cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << 1;
+ bits = 10;
+ } else {
+ cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS);
+ bits = 9;
+ }
+
+ dev_dbg(&edev->spi->dev, "ew cmd 0x%04x\n", cmd_addr);
+
+ spi_message_init(&m);
+ memset(&t, 0, sizeof(t));
+
+ t.tx_buf = &cmd_addr;
+ t.len = 2;
+ t.bits_per_word = bits;
+ spi_message_add_tail(&t, &m);
+
+ mutex_lock(&edev->lock);
+
+ if (edev->pdata->prepare)
+ edev->pdata->prepare(edev);
+
+ ret = spi_sync(edev->spi, &m);
+ /* have to wait at least Tcsl ns */
+ ndelay(250);
+ if (ret)
+ dev_err(&edev->spi->dev, "erase/write %sable error %d\n",
+ is_on ? "en" : "dis", ret);
+
+ if (edev->pdata->finish)
+ edev->pdata->finish(edev);
+
+ mutex_unlock(&edev->lock);
+ return ret;
+}
+
+static ssize_t
+eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev,
+ const char *buf, unsigned off)
+{
+ struct spi_message m;
+ struct spi_transfer t[2];
+ int bits, data_len, ret;
+ u16 cmd_addr;
+
+ cmd_addr = OP_WRITE << edev->addrlen;
+
+ if (edev->addrlen == 7) {
+ cmd_addr |= off & 0x7f;
+ bits = 10;
+ data_len = 1;
+ } else {
+ cmd_addr |= off & 0x3f;
+ bits = 9;
+ data_len = 2;
+ }
+
+ dev_dbg(&edev->spi->dev, "write cmd 0x%x\n", cmd_addr);
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+
+ t[0].tx_buf = (char *)&cmd_addr;
+ t[0].len = 2;
+ t[0].bits_per_word = bits;
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].tx_buf = buf;
+ t[1].len = data_len;
+ t[1].bits_per_word = 8;
+ spi_message_add_tail(&t[1], &m);
+
+ ret = spi_sync(edev->spi, &m);
+ /* have to wait program cycle time Twc ms */
+ mdelay(6);
+ return ret;
+}
+
+static ssize_t
+eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct eeprom_93xx46_dev *edev;
+ struct device *dev;
+ int i, ret, step = 1;
+
+ dev = container_of(kobj, struct device, kobj);
+ edev = dev_get_drvdata(dev);
+
+ if (unlikely(off >= edev->bin.size))
+ return 0;
+ if ((off + count) > edev->bin.size)
+ count = edev->bin.size - off;
+ if (unlikely(!count))
+ return count;
+
+ /* only write even number of bytes on 16-bit devices */
+ if (edev->addrlen == 6) {
+ step = 2;
+ count &= ~1;
+ }
+
+ /* erase/write enable */
+ ret = eeprom_93xx46_ew(edev, 1);
+ if (ret)
+ return ret;
+
+ mutex_lock(&edev->lock);
+
+ if (edev->pdata->prepare)
+ edev->pdata->prepare(edev);
+
+ for (i = 0; i < count; i += step) {
+ ret = eeprom_93xx46_write_word(edev, &buf[i], off + i);
+ if (ret) {
+ dev_err(&edev->spi->dev, "write failed at %d: %d\n",
+ (int)off + i, ret);
+ break;
+ }
+ }
+
+ if (edev->pdata->finish)
+ edev->pdata->finish(edev);
+
+ mutex_unlock(&edev->lock);
+
+ /* erase/write disable */
+ eeprom_93xx46_ew(edev, 0);
+ return ret ? : count;
+}
+
+static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
+{
+ struct eeprom_93xx46_platform_data *pd = edev->pdata;
+ struct spi_message m;
+ struct spi_transfer t;
+ int bits, ret;
+ u16 cmd_addr;
+
+ cmd_addr = OP_START << edev->addrlen;
+ if (edev->addrlen == 7) {
+ cmd_addr |= ADDR_ERAL << 1;
+ bits = 10;
+ } else {
+ cmd_addr |= ADDR_ERAL;
+ bits = 9;
+ }
+
+ spi_message_init(&m);
+ memset(&t, 0, sizeof(t));
+
+ t.tx_buf = &cmd_addr;
+ t.len = 2;
+ t.bits_per_word = bits;
+ spi_message_add_tail(&t, &m);
+
+ mutex_lock(&edev->lock);
+
+ if (edev->pdata->prepare)
+ edev->pdata->prepare(edev);
+
+ ret = spi_sync(edev->spi, &m);
+ if (ret)
+ dev_err(&edev->spi->dev, "erase error %d\n", ret);
+ /* have to wait erase cycle time Tec ms */
+ mdelay(6);
+
+ if (pd->finish)
+ pd->finish(edev);
+
+ mutex_unlock(&edev->lock);
+ return ret;
+}
+
+static ssize_t eeprom_93xx46_store_erase(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct eeprom_93xx46_dev *edev = dev_get_drvdata(dev);
+ int erase = 0, ret;
+
+ sscanf(buf, "%d", &erase);
+ if (erase) {
+ ret = eeprom_93xx46_ew(edev, 1);
+ if (ret)
+ return ret;
+ ret = eeprom_93xx46_eral(edev);
+ if (ret)
+ return ret;
+ ret = eeprom_93xx46_ew(edev, 0);
+ if (ret)
+ return ret;
+ }
+ return count;
+}
+static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase);
+
+static int __devinit eeprom_93xx46_probe(struct spi_device *spi)
+{
+ struct eeprom_93xx46_platform_data *pd;
+ struct eeprom_93xx46_dev *edev;
+ int err;
+
+ pd = spi->dev.platform_data;
+ if (!pd) {
+ dev_err(&spi->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+ if (!edev)
+ return -ENOMEM;
+
+ if (pd->flags & EE_ADDR8)
+ edev->addrlen = 7;
+ else if (pd->flags & EE_ADDR16)
+ edev->addrlen = 6;
+ else {
+ dev_err(&spi->dev, "unspecified address type\n");
+ err = -EINVAL;
+ goto fail;
+ }
+
+ mutex_init(&edev->lock);
+
+ edev->spi = spi_dev_get(spi);
+ edev->pdata = pd;
+
+ sysfs_bin_attr_init(&edev->bin);
+ edev->bin.attr.name = "eeprom";
+ edev->bin.attr.mode = S_IRUSR;
+ edev->bin.read = eeprom_93xx46_bin_read;
+ edev->bin.size = 128;
+ if (!(pd->flags & EE_READONLY)) {
+ edev->bin.write = eeprom_93xx46_bin_write;
+ edev->bin.attr.mode |= S_IWUSR;
+ }
+
+ err = sysfs_create_bin_file(&spi->dev.kobj, &edev->bin);
+ if (err)
+ goto fail;
+
+ dev_info(&spi->dev, "%d-bit eeprom %s\n",
+ (pd->flags & EE_ADDR8) ? 8 : 16,
+ (pd->flags & EE_READONLY) ? "(readonly)" : "");
+
+ if (!(pd->flags & EE_READONLY)) {
+ if (device_create_file(&spi->dev, &dev_attr_erase))
+ dev_err(&spi->dev, "can't create erase interface\n");
+ }
+
+ dev_set_drvdata(&spi->dev, edev);
+ return 0;
+fail:
+ kfree(edev);
+ return err;
+}
+
+static int __devexit eeprom_93xx46_remove(struct spi_device *spi)
+{
+ struct eeprom_93xx46_dev *edev = dev_get_drvdata(&spi->dev);
+
+ if (!(edev->pdata->flags & EE_READONLY))
+ device_remove_file(&spi->dev, &dev_attr_erase);
+
+ sysfs_remove_bin_file(&spi->dev.kobj, &edev->bin);
+ dev_set_drvdata(&spi->dev, NULL);
+ kfree(edev);
+ return 0;
+}
+
+static struct spi_driver eeprom_93xx46_driver = {
+ .driver = {
+ .name = "93xx46",
+ .owner = THIS_MODULE,
+ },
+ .probe = eeprom_93xx46_probe,
+ .remove = __devexit_p(eeprom_93xx46_remove),
+};
+
+static int __init eeprom_93xx46_init(void)
+{
+ return spi_register_driver(&eeprom_93xx46_driver);
+}
+module_init(eeprom_93xx46_init);
+
+static void __exit eeprom_93xx46_exit(void)
+{
+ spi_unregister_driver(&eeprom_93xx46_driver);
+}
+module_exit(eeprom_93xx46_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs");
+MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
+MODULE_ALIAS("spi:93xx46");
diff --git a/drivers/misc/fsa9480.c b/drivers/misc/fsa9480.c
new file mode 100644
index 000000000000..f6586d53e1a3
--- /dev/null
+++ b/drivers/misc/fsa9480.c
@@ -0,0 +1,558 @@
+/*
+ * fsa9480.c - FSA9480 micro USB switch device driver
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ * Wonguk Jeong <wonguk.jeong@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_data/fsa9480.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+/* FSA9480 I2C registers */
+#define FSA9480_REG_DEVID 0x01
+#define FSA9480_REG_CTRL 0x02
+#define FSA9480_REG_INT1 0x03
+#define FSA9480_REG_INT2 0x04
+#define FSA9480_REG_INT1_MASK 0x05
+#define FSA9480_REG_INT2_MASK 0x06
+#define FSA9480_REG_ADC 0x07
+#define FSA9480_REG_TIMING1 0x08
+#define FSA9480_REG_TIMING2 0x09
+#define FSA9480_REG_DEV_T1 0x0a
+#define FSA9480_REG_DEV_T2 0x0b
+#define FSA9480_REG_BTN1 0x0c
+#define FSA9480_REG_BTN2 0x0d
+#define FSA9480_REG_CK 0x0e
+#define FSA9480_REG_CK_INT1 0x0f
+#define FSA9480_REG_CK_INT2 0x10
+#define FSA9480_REG_CK_INTMASK1 0x11
+#define FSA9480_REG_CK_INTMASK2 0x12
+#define FSA9480_REG_MANSW1 0x13
+#define FSA9480_REG_MANSW2 0x14
+
+/* Control */
+#define CON_SWITCH_OPEN (1 << 4)
+#define CON_RAW_DATA (1 << 3)
+#define CON_MANUAL_SW (1 << 2)
+#define CON_WAIT (1 << 1)
+#define CON_INT_MASK (1 << 0)
+#define CON_MASK (CON_SWITCH_OPEN | CON_RAW_DATA | \
+ CON_MANUAL_SW | CON_WAIT)
+
+/* Device Type 1 */
+#define DEV_USB_OTG (1 << 7)
+#define DEV_DEDICATED_CHG (1 << 6)
+#define DEV_USB_CHG (1 << 5)
+#define DEV_CAR_KIT (1 << 4)
+#define DEV_UART (1 << 3)
+#define DEV_USB (1 << 2)
+#define DEV_AUDIO_2 (1 << 1)
+#define DEV_AUDIO_1 (1 << 0)
+
+#define DEV_T1_USB_MASK (DEV_USB_OTG | DEV_USB)
+#define DEV_T1_UART_MASK (DEV_UART)
+#define DEV_T1_CHARGER_MASK (DEV_DEDICATED_CHG | DEV_USB_CHG)
+
+/* Device Type 2 */
+#define DEV_AV (1 << 6)
+#define DEV_TTY (1 << 5)
+#define DEV_PPD (1 << 4)
+#define DEV_JIG_UART_OFF (1 << 3)
+#define DEV_JIG_UART_ON (1 << 2)
+#define DEV_JIG_USB_OFF (1 << 1)
+#define DEV_JIG_USB_ON (1 << 0)
+
+#define DEV_T2_USB_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON)
+#define DEV_T2_UART_MASK (DEV_JIG_UART_OFF | DEV_JIG_UART_ON)
+#define DEV_T2_JIG_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON | \
+ DEV_JIG_UART_OFF | DEV_JIG_UART_ON)
+
+/*
+ * Manual Switch
+ * D- [7:5] / D+ [4:2]
+ * 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO
+ */
+#define SW_VAUDIO ((4 << 5) | (4 << 2))
+#define SW_UART ((3 << 5) | (3 << 2))
+#define SW_AUDIO ((2 << 5) | (2 << 2))
+#define SW_DHOST ((1 << 5) | (1 << 2))
+#define SW_AUTO ((0 << 5) | (0 << 2))
+
+/* Interrupt 1 */
+#define INT_DETACH (1 << 1)
+#define INT_ATTACH (1 << 0)
+
+struct fsa9480_usbsw {
+ struct i2c_client *client;
+ struct fsa9480_platform_data *pdata;
+ int dev1;
+ int dev2;
+ int mansw;
+};
+
+static struct fsa9480_usbsw *chip;
+
+static int fsa9480_write_reg(struct i2c_client *client,
+ int reg, int value)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, reg, value);
+
+ if (ret < 0)
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int fsa9480_read_reg(struct i2c_client *client, int reg)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+
+ if (ret < 0)
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int fsa9480_read_irq(struct i2c_client *client, int *value)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client,
+ FSA9480_REG_INT1, 2, (u8 *)value);
+ *value &= 0xffff;
+
+ if (ret < 0)
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+ return ret;
+}
+
+static void fsa9480_set_switch(const char *buf)
+{
+ struct fsa9480_usbsw *usbsw = chip;
+ struct i2c_client *client = usbsw->client;
+ unsigned int value;
+ unsigned int path = 0;
+
+ value = fsa9480_read_reg(client, FSA9480_REG_CTRL);
+
+ if (!strncmp(buf, "VAUDIO", 6)) {
+ path = SW_VAUDIO;
+ value &= ~CON_MANUAL_SW;
+ } else if (!strncmp(buf, "UART", 4)) {
+ path = SW_UART;
+ value &= ~CON_MANUAL_SW;
+ } else if (!strncmp(buf, "AUDIO", 5)) {
+ path = SW_AUDIO;
+ value &= ~CON_MANUAL_SW;
+ } else if (!strncmp(buf, "DHOST", 5)) {
+ path = SW_DHOST;
+ value &= ~CON_MANUAL_SW;
+ } else if (!strncmp(buf, "AUTO", 4)) {
+ path = SW_AUTO;
+ value |= CON_MANUAL_SW;
+ } else {
+ printk(KERN_ERR "Wrong command\n");
+ return;
+ }
+
+ usbsw->mansw = path;
+ fsa9480_write_reg(client, FSA9480_REG_MANSW1, path);
+ fsa9480_write_reg(client, FSA9480_REG_CTRL, value);
+}
+
+static ssize_t fsa9480_get_switch(char *buf)
+{
+ struct fsa9480_usbsw *usbsw = chip;
+ struct i2c_client *client = usbsw->client;
+ unsigned int value;
+
+ value = fsa9480_read_reg(client, FSA9480_REG_MANSW1);
+
+ if (value == SW_VAUDIO)
+ return sprintf(buf, "VAUDIO\n");
+ else if (value == SW_UART)
+ return sprintf(buf, "UART\n");
+ else if (value == SW_AUDIO)
+ return sprintf(buf, "AUDIO\n");
+ else if (value == SW_DHOST)
+ return sprintf(buf, "DHOST\n");
+ else if (value == SW_AUTO)
+ return sprintf(buf, "AUTO\n");
+ else
+ return sprintf(buf, "%x", value);
+}
+
+static ssize_t fsa9480_show_device(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fsa9480_usbsw *usbsw = dev_get_drvdata(dev);
+ struct i2c_client *client = usbsw->client;
+ int dev1, dev2;
+
+ dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
+ dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
+
+ if (!dev1 && !dev2)
+ return sprintf(buf, "NONE\n");
+
+ /* USB */
+ if (dev1 & DEV_T1_USB_MASK || dev2 & DEV_T2_USB_MASK)
+ return sprintf(buf, "USB\n");
+
+ /* UART */
+ if (dev1 & DEV_T1_UART_MASK || dev2 & DEV_T2_UART_MASK)
+ return sprintf(buf, "UART\n");
+
+ /* CHARGER */
+ if (dev1 & DEV_T1_CHARGER_MASK)
+ return sprintf(buf, "CHARGER\n");
+
+ /* JIG */
+ if (dev2 & DEV_T2_JIG_MASK)
+ return sprintf(buf, "JIG\n");
+
+ return sprintf(buf, "UNKNOWN\n");
+}
+
+static ssize_t fsa9480_show_manualsw(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return fsa9480_get_switch(buf);
+
+}
+
+static ssize_t fsa9480_set_manualsw(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ fsa9480_set_switch(buf);
+
+ return count;
+}
+
+static DEVICE_ATTR(device, S_IRUGO, fsa9480_show_device, NULL);
+static DEVICE_ATTR(switch, S_IRUGO | S_IWUSR,
+ fsa9480_show_manualsw, fsa9480_set_manualsw);
+
+static struct attribute *fsa9480_attributes[] = {
+ &dev_attr_device.attr,
+ &dev_attr_switch.attr,
+ NULL
+};
+
+static const struct attribute_group fsa9480_group = {
+ .attrs = fsa9480_attributes,
+};
+
+static void fsa9480_detect_dev(struct fsa9480_usbsw *usbsw, int intr)
+{
+ int val1, val2, ctrl;
+ struct fsa9480_platform_data *pdata = usbsw->pdata;
+ struct i2c_client *client = usbsw->client;
+
+ val1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
+ val2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
+ ctrl = fsa9480_read_reg(client, FSA9480_REG_CTRL);
+
+ dev_info(&client->dev, "intr: 0x%x, dev1: 0x%x, dev2: 0x%x\n",
+ intr, val1, val2);
+
+ if (!intr)
+ goto out;
+
+ if (intr & INT_ATTACH) { /* Attached */
+ /* USB */
+ if (val1 & DEV_T1_USB_MASK || val2 & DEV_T2_USB_MASK) {
+ if (pdata->usb_cb)
+ pdata->usb_cb(FSA9480_ATTACHED);
+
+ if (usbsw->mansw) {
+ fsa9480_write_reg(client,
+ FSA9480_REG_MANSW1, usbsw->mansw);
+ }
+ }
+
+ /* UART */
+ if (val1 & DEV_T1_UART_MASK || val2 & DEV_T2_UART_MASK) {
+ if (pdata->uart_cb)
+ pdata->uart_cb(FSA9480_ATTACHED);
+
+ if (!(ctrl & CON_MANUAL_SW)) {
+ fsa9480_write_reg(client,
+ FSA9480_REG_MANSW1, SW_UART);
+ }
+ }
+
+ /* CHARGER */
+ if (val1 & DEV_T1_CHARGER_MASK) {
+ if (pdata->charger_cb)
+ pdata->charger_cb(FSA9480_ATTACHED);
+ }
+
+ /* JIG */
+ if (val2 & DEV_T2_JIG_MASK) {
+ if (pdata->jig_cb)
+ pdata->jig_cb(FSA9480_ATTACHED);
+ }
+ } else if (intr & INT_DETACH) { /* Detached */
+ /* USB */
+ if (usbsw->dev1 & DEV_T1_USB_MASK ||
+ usbsw->dev2 & DEV_T2_USB_MASK) {
+ if (pdata->usb_cb)
+ pdata->usb_cb(FSA9480_DETACHED);
+ }
+
+ /* UART */
+ if (usbsw->dev1 & DEV_T1_UART_MASK ||
+ usbsw->dev2 & DEV_T2_UART_MASK) {
+ if (pdata->uart_cb)
+ pdata->uart_cb(FSA9480_DETACHED);
+ }
+
+ /* CHARGER */
+ if (usbsw->dev1 & DEV_T1_CHARGER_MASK) {
+ if (pdata->charger_cb)
+ pdata->charger_cb(FSA9480_DETACHED);
+ }
+
+ /* JIG */
+ if (usbsw->dev2 & DEV_T2_JIG_MASK) {
+ if (pdata->jig_cb)
+ pdata->jig_cb(FSA9480_DETACHED);
+ }
+ }
+
+ usbsw->dev1 = val1;
+ usbsw->dev2 = val2;
+
+out:
+ ctrl &= ~CON_INT_MASK;
+ fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl);
+}
+
+static irqreturn_t fsa9480_irq_handler(int irq, void *data)
+{
+ struct fsa9480_usbsw *usbsw = data;
+ struct i2c_client *client = usbsw->client;
+ int intr;
+
+ /* clear interrupt */
+ fsa9480_read_irq(client, &intr);
+
+ /* device detection */
+ fsa9480_detect_dev(usbsw, intr);
+
+ return IRQ_HANDLED;
+}
+
+static int fsa9480_irq_init(struct fsa9480_usbsw *usbsw)
+{
+ struct fsa9480_platform_data *pdata = usbsw->pdata;
+ struct i2c_client *client = usbsw->client;
+ int ret;
+ int intr;
+ unsigned int ctrl = CON_MASK;
+
+ /* clear interrupt */
+ fsa9480_read_irq(client, &intr);
+
+ /* unmask interrupt (attach/detach only) */
+ fsa9480_write_reg(client, FSA9480_REG_INT1_MASK, 0xfc);
+ fsa9480_write_reg(client, FSA9480_REG_INT2_MASK, 0x1f);
+
+ usbsw->mansw = fsa9480_read_reg(client, FSA9480_REG_MANSW1);
+
+ if (usbsw->mansw)
+ ctrl &= ~CON_MANUAL_SW; /* Manual Switching Mode */
+
+ fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl);
+
+ if (pdata && pdata->cfg_gpio)
+ pdata->cfg_gpio();
+
+ if (client->irq) {
+ ret = request_threaded_irq(client->irq, NULL,
+ fsa9480_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "fsa9480 micro USB", usbsw);
+ if (ret) {
+ dev_err(&client->dev, "failed to reqeust IRQ\n");
+ return ret;
+ }
+
+ if (pdata)
+ device_init_wakeup(&client->dev, pdata->wakeup);
+ }
+
+ return 0;
+}
+
+static int __devinit fsa9480_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct fsa9480_usbsw *usbsw;
+ int ret = 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ usbsw = kzalloc(sizeof(struct fsa9480_usbsw), GFP_KERNEL);
+ if (!usbsw) {
+ dev_err(&client->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ usbsw->client = client;
+ usbsw->pdata = client->dev.platform_data;
+
+ chip = usbsw;
+
+ i2c_set_clientdata(client, usbsw);
+
+ ret = fsa9480_irq_init(usbsw);
+ if (ret)
+ goto fail1;
+
+ ret = sysfs_create_group(&client->dev.kobj, &fsa9480_group);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to create fsa9480 attribute group\n");
+ goto fail2;
+ }
+
+ /* ADC Detect Time: 500ms */
+ fsa9480_write_reg(client, FSA9480_REG_TIMING1, 0x6);
+
+ if (chip->pdata->reset_cb)
+ chip->pdata->reset_cb();
+
+ /* device detection */
+ fsa9480_detect_dev(usbsw, INT_ATTACH);
+
+ pm_runtime_set_active(&client->dev);
+
+ return 0;
+
+fail2:
+ if (client->irq)
+ free_irq(client->irq, usbsw);
+fail1:
+ i2c_set_clientdata(client, NULL);
+ kfree(usbsw);
+ return ret;
+}
+
+static int __devexit fsa9480_remove(struct i2c_client *client)
+{
+ struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
+ if (client->irq)
+ free_irq(client->irq, usbsw);
+ i2c_set_clientdata(client, NULL);
+
+ sysfs_remove_group(&client->dev.kobj, &fsa9480_group);
+ device_init_wakeup(&client->dev, 0);
+ kfree(usbsw);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int fsa9480_suspend(struct i2c_client *client, pm_message_t state)
+{
+ struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
+ struct fsa9480_platform_data *pdata = usbsw->pdata;
+
+ if (device_may_wakeup(&client->dev) && client->irq)
+ enable_irq_wake(client->irq);
+
+ if (pdata->usb_power)
+ pdata->usb_power(0);
+
+ return 0;
+}
+
+static int fsa9480_resume(struct i2c_client *client)
+{
+ struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
+ int dev1, dev2;
+
+ if (device_may_wakeup(&client->dev) && client->irq)
+ disable_irq_wake(client->irq);
+
+ /*
+ * Clear Pending interrupt. Note that detect_dev does what
+ * the interrupt handler does. So, we don't miss pending and
+ * we reenable interrupt if there is one.
+ */
+ fsa9480_read_reg(client, FSA9480_REG_INT1);
+ fsa9480_read_reg(client, FSA9480_REG_INT2);
+
+ dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
+ dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
+
+ /* device detection */
+ fsa9480_detect_dev(usbsw, (dev1 || dev2) ? INT_ATTACH : INT_DETACH);
+
+ return 0;
+}
+
+#else
+
+#define fsa9480_suspend NULL
+#define fsa9480_resume NULL
+
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id fsa9480_id[] = {
+ {"fsa9480", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, fsa9480_id);
+
+static struct i2c_driver fsa9480_i2c_driver = {
+ .driver = {
+ .name = "fsa9480",
+ },
+ .probe = fsa9480_probe,
+ .remove = __devexit_p(fsa9480_remove),
+ .resume = fsa9480_resume,
+ .suspend = fsa9480_suspend,
+ .id_table = fsa9480_id,
+};
+
+static int __init fsa9480_init(void)
+{
+ return i2c_add_driver(&fsa9480_i2c_driver);
+}
+module_init(fsa9480_init);
+
+static void __exit fsa9480_exit(void)
+{
+ i2c_del_driver(&fsa9480_i2c_driver);
+}
+module_exit(fsa9480_exit);
+
+MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
+MODULE_DESCRIPTION("FSA9480 USB Switch driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index 8cebec5e85ee..3f7ad83ed740 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -102,6 +102,7 @@
#include <linux/nmi.h>
#include <linux/delay.h>
#include <linux/kthread.h>
+#include <linux/module.h>
#define v1printk(a...) do { \
if (verbose) \
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c
index b928bc14e97b..29d12a70eb1b 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d.c
@@ -163,7 +163,7 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
int i;
if (lis3->blkread) {
- if (lis3_dev.whoami == WAI_12B) {
+ if (lis3->whoami == WAI_12B) {
u16 data[3];
lis3->blkread(lis3, OUTX_L, 6, (u8 *)data);
for (i = 0; i < 3; i++)
@@ -195,18 +195,30 @@ static int lis3_8_rates[2] = {100, 400};
static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000};
/* ODR is Output Data Rate */
-static int lis3lv02d_get_odr(void)
+static int lis3lv02d_get_odr(struct lis3lv02d *lis3)
{
u8 ctrl;
int shift;
- lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
- ctrl &= lis3_dev.odr_mask;
- shift = ffs(lis3_dev.odr_mask) - 1;
- return lis3_dev.odrs[(ctrl >> shift)];
+ lis3->read(lis3, CTRL_REG1, &ctrl);
+ ctrl &= lis3->odr_mask;
+ shift = ffs(lis3->odr_mask) - 1;
+ return lis3->odrs[(ctrl >> shift)];
}
-static int lis3lv02d_set_odr(int rate)
+static int lis3lv02d_get_pwron_wait(struct lis3lv02d *lis3)
+{
+ int div = lis3lv02d_get_odr(lis3);
+
+ if (WARN_ONCE(div == 0, "device returned spurious data"))
+ return -ENXIO;
+
+ /* LIS3 power on delay is quite long */
+ msleep(lis3->pwron_delay / div);
+ return 0;
+}
+
+static int lis3lv02d_set_odr(struct lis3lv02d *lis3, int rate)
{
u8 ctrl;
int i, len, shift;
@@ -214,14 +226,14 @@ static int lis3lv02d_set_odr(int rate)
if (!rate)
return -EINVAL;
- lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
- ctrl &= ~lis3_dev.odr_mask;
- len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */
- shift = ffs(lis3_dev.odr_mask) - 1;
+ lis3->read(lis3, CTRL_REG1, &ctrl);
+ ctrl &= ~lis3->odr_mask;
+ len = 1 << hweight_long(lis3->odr_mask); /* # of possible values */
+ shift = ffs(lis3->odr_mask) - 1;
for (i = 0; i < len; i++)
- if (lis3_dev.odrs[i] == rate) {
- lis3_dev.write(&lis3_dev, CTRL_REG1,
+ if (lis3->odrs[i] == rate) {
+ lis3->write(lis3, CTRL_REG1,
ctrl | (i << shift));
return 0;
}
@@ -240,12 +252,12 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
mutex_lock(&lis3->mutex);
irq_cfg = lis3->irq_cfg;
- if (lis3_dev.whoami == WAI_8B) {
+ if (lis3->whoami == WAI_8B) {
lis3->data_ready_count[IRQ_LINE0] = 0;
lis3->data_ready_count[IRQ_LINE1] = 0;
/* Change interrupt cfg to data ready for selftest */
- atomic_inc(&lis3_dev.wake_thread);
+ atomic_inc(&lis3->wake_thread);
lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY;
lis3->read(lis3, CTRL_REG3, &ctrl_reg_data);
lis3->write(lis3, CTRL_REG3, (ctrl_reg_data &
@@ -253,12 +265,12 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
(LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY));
}
- if (lis3_dev.whoami == WAI_3DC) {
+ if (lis3->whoami == WAI_3DC) {
ctlreg = CTRL_REG4;
selftest = CTRL4_ST0;
} else {
ctlreg = CTRL_REG1;
- if (lis3_dev.whoami == WAI_12B)
+ if (lis3->whoami == WAI_12B)
selftest = CTRL1_ST;
else
selftest = CTRL1_STP;
@@ -266,7 +278,9 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
lis3->read(lis3, ctlreg, &reg);
lis3->write(lis3, ctlreg, (reg | selftest));
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ ret = lis3lv02d_get_pwron_wait(lis3);
+ if (ret)
+ goto fail;
/* Read directly to avoid axis remap */
x = lis3->read_data(lis3, OUTX);
@@ -275,7 +289,9 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
/* back to normal settings */
lis3->write(lis3, ctlreg, reg);
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ ret = lis3lv02d_get_pwron_wait(lis3);
+ if (ret)
+ goto fail;
results[0] = x - lis3->read_data(lis3, OUTX);
results[1] = y - lis3->read_data(lis3, OUTY);
@@ -283,9 +299,9 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
ret = 0;
- if (lis3_dev.whoami == WAI_8B) {
+ if (lis3->whoami == WAI_8B) {
/* Restore original interrupt configuration */
- atomic_dec(&lis3_dev.wake_thread);
+ atomic_dec(&lis3->wake_thread);
lis3->write(lis3, CTRL_REG3, ctrl_reg_data);
lis3->irq_cfg = irq_cfg;
@@ -363,8 +379,9 @@ void lis3lv02d_poweroff(struct lis3lv02d *lis3)
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweroff);
-void lis3lv02d_poweron(struct lis3lv02d *lis3)
+int lis3lv02d_poweron(struct lis3lv02d *lis3)
{
+ int err;
u8 reg;
lis3->init(lis3);
@@ -375,42 +392,50 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3)
* both have been read. So the value read will always be correct.
* Set BOOT bit to refresh factory tuning values.
*/
- lis3->read(lis3, CTRL_REG2, &reg);
- if (lis3->whoami == WAI_12B)
- reg |= CTRL2_BDU | CTRL2_BOOT;
- else
- reg |= CTRL2_BOOT_8B;
- lis3->write(lis3, CTRL_REG2, reg);
+ if (lis3->pdata) {
+ lis3->read(lis3, CTRL_REG2, &reg);
+ if (lis3->whoami == WAI_12B)
+ reg |= CTRL2_BDU | CTRL2_BOOT;
+ else
+ reg |= CTRL2_BOOT_8B;
+ lis3->write(lis3, CTRL_REG2, reg);
+ }
- /* LIS3 power on delay is quite long */
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ err = lis3lv02d_get_pwron_wait(lis3);
+ if (err)
+ return err;
if (lis3->reg_ctrl)
lis3_context_restore(lis3);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)
{
+ struct lis3lv02d *lis3 = pidev->private;
int x, y, z;
- mutex_lock(&lis3_dev.mutex);
- lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
+ mutex_lock(&lis3->mutex);
+ lis3lv02d_get_xyz(lis3, &x, &y, &z);
input_report_abs(pidev->input, ABS_X, x);
input_report_abs(pidev->input, ABS_Y, y);
input_report_abs(pidev->input, ABS_Z, z);
input_sync(pidev->input);
- mutex_unlock(&lis3_dev.mutex);
+ mutex_unlock(&lis3->mutex);
}
static void lis3lv02d_joystick_open(struct input_polled_dev *pidev)
{
- if (lis3_dev.pm_dev)
- pm_runtime_get_sync(lis3_dev.pm_dev);
+ struct lis3lv02d *lis3 = pidev->private;
+
+ if (lis3->pm_dev)
+ pm_runtime_get_sync(lis3->pm_dev);
- if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev)
- atomic_set(&lis3_dev.wake_thread, 1);
+ if (lis3->pdata && lis3->whoami == WAI_8B && lis3->idev)
+ atomic_set(&lis3->wake_thread, 1);
/*
* Update coordinates for the case where poll interval is 0 and
* the chip in running purely under interrupt control
@@ -420,14 +445,18 @@ static void lis3lv02d_joystick_open(struct input_polled_dev *pidev)
static void lis3lv02d_joystick_close(struct input_polled_dev *pidev)
{
- atomic_set(&lis3_dev.wake_thread, 0);
- if (lis3_dev.pm_dev)
- pm_runtime_put(lis3_dev.pm_dev);
+ struct lis3lv02d *lis3 = pidev->private;
+
+ atomic_set(&lis3->wake_thread, 0);
+ if (lis3->pm_dev)
+ pm_runtime_put(lis3->pm_dev);
}
-static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
+static irqreturn_t lis302dl_interrupt(int irq, void *data)
{
- if (!test_bit(0, &lis3_dev.misc_opened))
+ struct lis3lv02d *lis3 = data;
+
+ if (!test_bit(0, &lis3->misc_opened))
goto out;
/*
@@ -435,12 +464,12 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
* the lid is closed. This leads to interrupts as soon as a little move
* is done.
*/
- atomic_inc(&lis3_dev.count);
+ atomic_inc(&lis3->count);
- wake_up_interruptible(&lis3_dev.misc_wait);
- kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN);
+ wake_up_interruptible(&lis3->misc_wait);
+ kill_fasync(&lis3->async_queue, SIGIO, POLL_IN);
out:
- if (atomic_read(&lis3_dev.wake_thread))
+ if (atomic_read(&lis3->wake_thread))
return IRQ_WAKE_THREAD;
return IRQ_HANDLED;
}
@@ -512,28 +541,37 @@ static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data)
static int lis3lv02d_misc_open(struct inode *inode, struct file *file)
{
- if (test_and_set_bit(0, &lis3_dev.misc_opened))
+ struct lis3lv02d *lis3 = container_of(file->private_data,
+ struct lis3lv02d, miscdev);
+
+ if (test_and_set_bit(0, &lis3->misc_opened))
return -EBUSY; /* already open */
- if (lis3_dev.pm_dev)
- pm_runtime_get_sync(lis3_dev.pm_dev);
+ if (lis3->pm_dev)
+ pm_runtime_get_sync(lis3->pm_dev);
- atomic_set(&lis3_dev.count, 0);
+ atomic_set(&lis3->count, 0);
return 0;
}
static int lis3lv02d_misc_release(struct inode *inode, struct file *file)
{
- fasync_helper(-1, file, 0, &lis3_dev.async_queue);
- clear_bit(0, &lis3_dev.misc_opened); /* release the device */
- if (lis3_dev.pm_dev)
- pm_runtime_put(lis3_dev.pm_dev);
+ struct lis3lv02d *lis3 = container_of(file->private_data,
+ struct lis3lv02d, miscdev);
+
+ fasync_helper(-1, file, 0, &lis3->async_queue);
+ clear_bit(0, &lis3->misc_opened); /* release the device */
+ if (lis3->pm_dev)
+ pm_runtime_put(lis3->pm_dev);
return 0;
}
static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
+ struct lis3lv02d *lis3 = container_of(file->private_data,
+ struct lis3lv02d, miscdev);
+
DECLARE_WAITQUEUE(wait, current);
u32 data;
unsigned char byte_data;
@@ -542,10 +580,10 @@ static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf,
if (count < 1)
return -EINVAL;
- add_wait_queue(&lis3_dev.misc_wait, &wait);
+ add_wait_queue(&lis3->misc_wait, &wait);
while (true) {
set_current_state(TASK_INTERRUPTIBLE);
- data = atomic_xchg(&lis3_dev.count, 0);
+ data = atomic_xchg(&lis3->count, 0);
if (data)
break;
@@ -575,22 +613,28 @@ static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf,
out:
__set_current_state(TASK_RUNNING);
- remove_wait_queue(&lis3_dev.misc_wait, &wait);
+ remove_wait_queue(&lis3->misc_wait, &wait);
return retval;
}
static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait)
{
- poll_wait(file, &lis3_dev.misc_wait, wait);
- if (atomic_read(&lis3_dev.count))
+ struct lis3lv02d *lis3 = container_of(file->private_data,
+ struct lis3lv02d, miscdev);
+
+ poll_wait(file, &lis3->misc_wait, wait);
+ if (atomic_read(&lis3->count))
return POLLIN | POLLRDNORM;
return 0;
}
static int lis3lv02d_misc_fasync(int fd, struct file *file, int on)
{
- return fasync_helper(fd, file, on, &lis3_dev.async_queue);
+ struct lis3lv02d *lis3 = container_of(file->private_data,
+ struct lis3lv02d, miscdev);
+
+ return fasync_helper(fd, file, on, &lis3->async_queue);
}
static const struct file_operations lis3lv02d_misc_fops = {
@@ -603,85 +647,80 @@ static const struct file_operations lis3lv02d_misc_fops = {
.fasync = lis3lv02d_misc_fasync,
};
-static struct miscdevice lis3lv02d_misc_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "freefall",
- .fops = &lis3lv02d_misc_fops,
-};
-
-int lis3lv02d_joystick_enable(void)
+int lis3lv02d_joystick_enable(struct lis3lv02d *lis3)
{
struct input_dev *input_dev;
int err;
int max_val, fuzz, flat;
int btns[] = {BTN_X, BTN_Y, BTN_Z};
- if (lis3_dev.idev)
+ if (lis3->idev)
return -EINVAL;
- lis3_dev.idev = input_allocate_polled_device();
- if (!lis3_dev.idev)
+ lis3->idev = input_allocate_polled_device();
+ if (!lis3->idev)
return -ENOMEM;
- lis3_dev.idev->poll = lis3lv02d_joystick_poll;
- lis3_dev.idev->open = lis3lv02d_joystick_open;
- lis3_dev.idev->close = lis3lv02d_joystick_close;
- lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL;
- lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN;
- lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX;
- input_dev = lis3_dev.idev->input;
+ lis3->idev->poll = lis3lv02d_joystick_poll;
+ lis3->idev->open = lis3lv02d_joystick_open;
+ lis3->idev->close = lis3lv02d_joystick_close;
+ lis3->idev->poll_interval = MDPS_POLL_INTERVAL;
+ lis3->idev->poll_interval_min = MDPS_POLL_MIN;
+ lis3->idev->poll_interval_max = MDPS_POLL_MAX;
+ lis3->idev->private = lis3;
+ input_dev = lis3->idev->input;
input_dev->name = "ST LIS3LV02DL Accelerometer";
input_dev->phys = DRIVER_NAME "/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0;
- input_dev->dev.parent = &lis3_dev.pdev->dev;
+ input_dev->dev.parent = &lis3->pdev->dev;
set_bit(EV_ABS, input_dev->evbit);
- max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY;
- if (lis3_dev.whoami == WAI_12B) {
+ max_val = (lis3->mdps_max_val * lis3->scale) / LIS3_ACCURACY;
+ if (lis3->whoami == WAI_12B) {
fuzz = LIS3_DEFAULT_FUZZ_12B;
flat = LIS3_DEFAULT_FLAT_12B;
} else {
fuzz = LIS3_DEFAULT_FUZZ_8B;
flat = LIS3_DEFAULT_FLAT_8B;
}
- fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY;
- flat = (flat * lis3_dev.scale) / LIS3_ACCURACY;
+ fuzz = (fuzz * lis3->scale) / LIS3_ACCURACY;
+ flat = (flat * lis3->scale) / LIS3_ACCURACY;
input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat);
input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat);
input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat);
- lis3_dev.mapped_btns[0] = lis3lv02d_get_axis(abs(lis3_dev.ac.x), btns);
- lis3_dev.mapped_btns[1] = lis3lv02d_get_axis(abs(lis3_dev.ac.y), btns);
- lis3_dev.mapped_btns[2] = lis3lv02d_get_axis(abs(lis3_dev.ac.z), btns);
+ lis3->mapped_btns[0] = lis3lv02d_get_axis(abs(lis3->ac.x), btns);
+ lis3->mapped_btns[1] = lis3lv02d_get_axis(abs(lis3->ac.y), btns);
+ lis3->mapped_btns[2] = lis3lv02d_get_axis(abs(lis3->ac.z), btns);
- err = input_register_polled_device(lis3_dev.idev);
+ err = input_register_polled_device(lis3->idev);
if (err) {
- input_free_polled_device(lis3_dev.idev);
- lis3_dev.idev = NULL;
+ input_free_polled_device(lis3->idev);
+ lis3->idev = NULL;
}
return err;
}
EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable);
-void lis3lv02d_joystick_disable(void)
+void lis3lv02d_joystick_disable(struct lis3lv02d *lis3)
{
- if (lis3_dev.irq)
- free_irq(lis3_dev.irq, &lis3_dev);
- if (lis3_dev.pdata && lis3_dev.pdata->irq2)
- free_irq(lis3_dev.pdata->irq2, &lis3_dev);
+ if (lis3->irq)
+ free_irq(lis3->irq, lis3);
+ if (lis3->pdata && lis3->pdata->irq2)
+ free_irq(lis3->pdata->irq2, lis3);
- if (!lis3_dev.idev)
+ if (!lis3->idev)
return;
- if (lis3_dev.irq)
- misc_deregister(&lis3lv02d_misc_device);
- input_unregister_polled_device(lis3_dev.idev);
- input_free_polled_device(lis3_dev.idev);
- lis3_dev.idev = NULL;
+ if (lis3->irq)
+ misc_deregister(&lis3->miscdev);
+ input_unregister_polled_device(lis3->idev);
+ input_free_polled_device(lis3->idev);
+ lis3->idev = NULL;
}
EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
@@ -706,6 +745,7 @@ static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3)
static ssize_t lis3lv02d_selftest_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct lis3lv02d *lis3 = dev_get_drvdata(dev);
s16 values[3];
static const char ok[] = "OK";
@@ -713,8 +753,8 @@ static ssize_t lis3lv02d_selftest_show(struct device *dev,
static const char irq[] = "FAIL_IRQ";
const char *res;
- lis3lv02d_sysfs_poweron(&lis3_dev);
- switch (lis3lv02d_selftest(&lis3_dev, values)) {
+ lis3lv02d_sysfs_poweron(lis3);
+ switch (lis3lv02d_selftest(lis3, values)) {
case SELFTEST_FAIL:
res = fail;
break;
@@ -733,33 +773,37 @@ static ssize_t lis3lv02d_selftest_show(struct device *dev,
static ssize_t lis3lv02d_position_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct lis3lv02d *lis3 = dev_get_drvdata(dev);
int x, y, z;
- lis3lv02d_sysfs_poweron(&lis3_dev);
- mutex_lock(&lis3_dev.mutex);
- lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
- mutex_unlock(&lis3_dev.mutex);
+ lis3lv02d_sysfs_poweron(lis3);
+ mutex_lock(&lis3->mutex);
+ lis3lv02d_get_xyz(lis3, &x, &y, &z);
+ mutex_unlock(&lis3->mutex);
return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
}
static ssize_t lis3lv02d_rate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- lis3lv02d_sysfs_poweron(&lis3_dev);
- return sprintf(buf, "%d\n", lis3lv02d_get_odr());
+ struct lis3lv02d *lis3 = dev_get_drvdata(dev);
+
+ lis3lv02d_sysfs_poweron(lis3);
+ return sprintf(buf, "%d\n", lis3lv02d_get_odr(lis3));
}
static ssize_t lis3lv02d_rate_set(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
+ struct lis3lv02d *lis3 = dev_get_drvdata(dev);
unsigned long rate;
if (strict_strtoul(buf, 0, &rate))
return -EINVAL;
- lis3lv02d_sysfs_poweron(&lis3_dev);
- if (lis3lv02d_set_odr(rate))
+ lis3lv02d_sysfs_poweron(lis3);
+ if (lis3lv02d_set_odr(lis3, rate))
return -EINVAL;
return count;
@@ -788,6 +832,7 @@ static int lis3lv02d_add_fs(struct lis3lv02d *lis3)
if (IS_ERR(lis3->pdev))
return PTR_ERR(lis3->pdev);
+ platform_set_drvdata(lis3->pdev, lis3);
return sysfs_create_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group);
}
@@ -801,7 +846,7 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3)
/* SYSFS may have left chip running. Turn off if necessary */
if (!pm_runtime_suspended(lis3->pm_dev))
- lis3lv02d_poweroff(&lis3_dev);
+ lis3lv02d_poweroff(lis3);
pm_runtime_disable(lis3->pm_dev);
pm_runtime_set_suspended(lis3->pm_dev);
@@ -811,24 +856,24 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3)
}
EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
-static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
+static void lis3lv02d_8b_configure(struct lis3lv02d *lis3,
struct lis3lv02d_platform_data *p)
{
int err;
int ctrl2 = p->hipass_ctrl;
if (p->click_flags) {
- dev->write(dev, CLICK_CFG, p->click_flags);
- dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit);
- dev->write(dev, CLICK_LATENCY, p->click_latency);
- dev->write(dev, CLICK_WINDOW, p->click_window);
- dev->write(dev, CLICK_THSZ, p->click_thresh_z & 0xf);
- dev->write(dev, CLICK_THSY_X,
+ lis3->write(lis3, CLICK_CFG, p->click_flags);
+ lis3->write(lis3, CLICK_TIMELIMIT, p->click_time_limit);
+ lis3->write(lis3, CLICK_LATENCY, p->click_latency);
+ lis3->write(lis3, CLICK_WINDOW, p->click_window);
+ lis3->write(lis3, CLICK_THSZ, p->click_thresh_z & 0xf);
+ lis3->write(lis3, CLICK_THSY_X,
(p->click_thresh_x & 0xf) |
(p->click_thresh_y << 4));
- if (dev->idev) {
- struct input_dev *input_dev = lis3_dev.idev->input;
+ if (lis3->idev) {
+ struct input_dev *input_dev = lis3->idev->input;
input_set_capability(input_dev, EV_KEY, BTN_X);
input_set_capability(input_dev, EV_KEY, BTN_Y);
input_set_capability(input_dev, EV_KEY, BTN_Z);
@@ -836,22 +881,22 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
}
if (p->wakeup_flags) {
- dev->write(dev, FF_WU_CFG_1, p->wakeup_flags);
- dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
+ lis3->write(lis3, FF_WU_CFG_1, p->wakeup_flags);
+ lis3->write(lis3, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
/* pdata value + 1 to keep this backward compatible*/
- dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1);
+ lis3->write(lis3, FF_WU_DURATION_1, p->duration1 + 1);
ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/
}
if (p->wakeup_flags2) {
- dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2);
- dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f);
+ lis3->write(lis3, FF_WU_CFG_2, p->wakeup_flags2);
+ lis3->write(lis3, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f);
/* pdata value + 1 to keep this backward compatible*/
- dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1);
+ lis3->write(lis3, FF_WU_DURATION_2, p->duration2 + 1);
ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/
}
/* Configure hipass filters */
- dev->write(dev, CTRL_REG2, ctrl2);
+ lis3->write(lis3, CTRL_REG2, ctrl2);
if (p->irq2) {
err = request_threaded_irq(p->irq2,
@@ -859,7 +904,7 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
lis302dl_interrupt_thread2_8b,
IRQF_TRIGGER_RISING | IRQF_ONESHOT |
(p->irq_flags2 & IRQF_TRIGGER_MASK),
- DRIVER_NAME, &lis3_dev);
+ DRIVER_NAME, lis3);
if (err < 0)
pr_err("No second IRQ. Limited functionality\n");
}
@@ -869,93 +914,97 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
* Initialise the accelerometer and the various subsystems.
* Should be rather independent of the bus system.
*/
-int lis3lv02d_init_device(struct lis3lv02d *dev)
+int lis3lv02d_init_device(struct lis3lv02d *lis3)
{
int err;
irq_handler_t thread_fn;
int irq_flags = 0;
- dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I);
+ lis3->whoami = lis3lv02d_read_8(lis3, WHO_AM_I);
- switch (dev->whoami) {
+ switch (lis3->whoami) {
case WAI_12B:
pr_info("12 bits sensor found\n");
- dev->read_data = lis3lv02d_read_12;
- dev->mdps_max_val = 2048;
- dev->pwron_delay = LIS3_PWRON_DELAY_WAI_12B;
- dev->odrs = lis3_12_rates;
- dev->odr_mask = CTRL1_DF0 | CTRL1_DF1;
- dev->scale = LIS3_SENSITIVITY_12B;
- dev->regs = lis3_wai12_regs;
- dev->regs_size = ARRAY_SIZE(lis3_wai12_regs);
+ lis3->read_data = lis3lv02d_read_12;
+ lis3->mdps_max_val = 2048;
+ lis3->pwron_delay = LIS3_PWRON_DELAY_WAI_12B;
+ lis3->odrs = lis3_12_rates;
+ lis3->odr_mask = CTRL1_DF0 | CTRL1_DF1;
+ lis3->scale = LIS3_SENSITIVITY_12B;
+ lis3->regs = lis3_wai12_regs;
+ lis3->regs_size = ARRAY_SIZE(lis3_wai12_regs);
break;
case WAI_8B:
pr_info("8 bits sensor found\n");
- dev->read_data = lis3lv02d_read_8;
- dev->mdps_max_val = 128;
- dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
- dev->odrs = lis3_8_rates;
- dev->odr_mask = CTRL1_DR;
- dev->scale = LIS3_SENSITIVITY_8B;
- dev->regs = lis3_wai8_regs;
- dev->regs_size = ARRAY_SIZE(lis3_wai8_regs);
+ lis3->read_data = lis3lv02d_read_8;
+ lis3->mdps_max_val = 128;
+ lis3->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
+ lis3->odrs = lis3_8_rates;
+ lis3->odr_mask = CTRL1_DR;
+ lis3->scale = LIS3_SENSITIVITY_8B;
+ lis3->regs = lis3_wai8_regs;
+ lis3->regs_size = ARRAY_SIZE(lis3_wai8_regs);
break;
case WAI_3DC:
pr_info("8 bits 3DC sensor found\n");
- dev->read_data = lis3lv02d_read_8;
- dev->mdps_max_val = 128;
- dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
- dev->odrs = lis3_3dc_rates;
- dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;
- dev->scale = LIS3_SENSITIVITY_8B;
+ lis3->read_data = lis3lv02d_read_8;
+ lis3->mdps_max_val = 128;
+ lis3->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
+ lis3->odrs = lis3_3dc_rates;
+ lis3->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;
+ lis3->scale = LIS3_SENSITIVITY_8B;
break;
default:
- pr_err("unknown sensor type 0x%X\n", dev->whoami);
+ pr_err("unknown sensor type 0x%X\n", lis3->whoami);
return -EINVAL;
}
- dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs),
+ lis3->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs),
sizeof(lis3_wai12_regs)), GFP_KERNEL);
- if (dev->reg_cache == NULL) {
+ if (lis3->reg_cache == NULL) {
printk(KERN_ERR DRIVER_NAME "out of memory\n");
return -ENOMEM;
}
- mutex_init(&dev->mutex);
- atomic_set(&dev->wake_thread, 0);
+ mutex_init(&lis3->mutex);
+ atomic_set(&lis3->wake_thread, 0);
- lis3lv02d_add_fs(dev);
- lis3lv02d_poweron(dev);
+ lis3lv02d_add_fs(lis3);
+ err = lis3lv02d_poweron(lis3);
+ if (err) {
+ lis3lv02d_remove_fs(lis3);
+ return err;
+ }
- if (dev->pm_dev) {
- pm_runtime_set_active(dev->pm_dev);
- pm_runtime_enable(dev->pm_dev);
+ if (lis3->pm_dev) {
+ pm_runtime_set_active(lis3->pm_dev);
+ pm_runtime_enable(lis3->pm_dev);
}
- if (lis3lv02d_joystick_enable())
+ if (lis3lv02d_joystick_enable(lis3))
pr_err("joystick initialization failed\n");
/* passing in platform specific data is purely optional and only
* used by the SPI transport layer at the moment */
- if (dev->pdata) {
- struct lis3lv02d_platform_data *p = dev->pdata;
+ if (lis3->pdata) {
+ struct lis3lv02d_platform_data *p = lis3->pdata;
- if (dev->whoami == WAI_8B)
- lis3lv02d_8b_configure(dev, p);
+ if (lis3->whoami == WAI_8B)
+ lis3lv02d_8b_configure(lis3, p);
irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK;
- dev->irq_cfg = p->irq_cfg;
+ lis3->irq_cfg = p->irq_cfg;
if (p->irq_cfg)
- dev->write(dev, CTRL_REG3, p->irq_cfg);
+ lis3->write(lis3, CTRL_REG3, p->irq_cfg);
if (p->default_rate)
- lis3lv02d_set_odr(p->default_rate);
+ lis3lv02d_set_odr(lis3, p->default_rate);
}
/* bail if we did not get an IRQ from the bus layer */
- if (!dev->irq) {
+ if (!lis3->irq) {
pr_debug("No IRQ. Disabling /dev/freefall\n");
goto out;
}
@@ -971,23 +1020,27 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
* io-apic is not configurable (and generates a warning) but I keep it
* in case of support for other hardware.
*/
- if (dev->pdata && dev->whoami == WAI_8B)
+ if (lis3->pdata && lis3->whoami == WAI_8B)
thread_fn = lis302dl_interrupt_thread1_8b;
else
thread_fn = NULL;
- err = request_threaded_irq(dev->irq, lis302dl_interrupt,
+ err = request_threaded_irq(lis3->irq, lis302dl_interrupt,
thread_fn,
IRQF_TRIGGER_RISING | IRQF_ONESHOT |
irq_flags,
- DRIVER_NAME, &lis3_dev);
+ DRIVER_NAME, lis3);
if (err < 0) {
pr_err("Cannot get IRQ\n");
goto out;
}
- if (misc_register(&lis3lv02d_misc_device))
+ lis3->miscdev.minor = MISC_DYNAMIC_MINOR;
+ lis3->miscdev.name = "freefall";
+ lis3->miscdev.fops = &lis3lv02d_misc_fops;
+
+ if (misc_register(&lis3->miscdev))
pr_err("misc_register failed\n");
out:
return 0;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.h b/drivers/misc/lis3lv02d/lis3lv02d.h
index a1939589eb2c..2b1482ad3f16 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.h
+++ b/drivers/misc/lis3lv02d/lis3lv02d.h
@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/input-polldev.h>
#include <linux/regulator/consumer.h>
+#include <linux/miscdevice.h>
/*
* This driver tries to support the "digital" accelerometer chips from
@@ -273,6 +274,8 @@ struct lis3lv02d {
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 */
+ struct miscdevice miscdev;
+
int data_ready_count[2];
atomic_t wake_thread;
unsigned char irq_cfg;
@@ -282,10 +285,10 @@ struct lis3lv02d {
};
int lis3lv02d_init_device(struct lis3lv02d *lis3);
-int lis3lv02d_joystick_enable(void);
-void lis3lv02d_joystick_disable(void);
+int lis3lv02d_joystick_enable(struct lis3lv02d *lis3);
+void lis3lv02d_joystick_disable(struct lis3lv02d *lis3);
void lis3lv02d_poweroff(struct lis3lv02d *lis3);
-void lis3lv02d_poweron(struct lis3lv02d *lis3);
+int lis3lv02d_poweron(struct lis3lv02d *lis3);
int lis3lv02d_remove_fs(struct lis3lv02d *lis3);
extern struct lis3lv02d lis3_dev;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
index b20dfb4522d2..c02fea029dcf 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
@@ -79,8 +79,7 @@ static int lis3_i2c_init(struct lis3lv02d *lis3)
u8 reg;
int ret;
- if (lis3->reg_ctrl)
- lis3_reg_ctrl(lis3, LIS3_REG_ON);
+ lis3_reg_ctrl(lis3, LIS3_REG_ON);
lis3->read(lis3, WHO_AM_I, &reg);
if (reg != lis3->whoami)
@@ -106,10 +105,6 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
if (pdata) {
- /* Regulator control is optional */
- if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL)
- lis3_dev.reg_ctrl = lis3_reg_ctrl;
-
if ((pdata->driver_features & LIS3_USE_BLOCK_READ) &&
(i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK)))
@@ -131,15 +126,13 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
goto fail;
}
- if (lis3_dev.reg_ctrl) {
- lis3_dev.regulators[0].supply = reg_vdd;
- lis3_dev.regulators[1].supply = reg_vdd_io;
- ret = regulator_bulk_get(&client->dev,
- ARRAY_SIZE(lis3_dev.regulators),
- lis3_dev.regulators);
- if (ret < 0)
- goto fail;
- }
+ lis3_dev.regulators[0].supply = reg_vdd;
+ lis3_dev.regulators[1].supply = reg_vdd_io;
+ ret = regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(lis3_dev.regulators),
+ lis3_dev.regulators);
+ if (ret < 0)
+ goto fail;
lis3_dev.pdata = pdata;
lis3_dev.bus_priv = client;
@@ -153,16 +146,19 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, &lis3_dev);
/* Provide power over the init call */
- if (lis3_dev.reg_ctrl)
- lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON);
+ lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON);
ret = lis3lv02d_init_device(&lis3_dev);
- if (lis3_dev.reg_ctrl)
- lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF);
+ lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF);
- if (ret == 0)
- return 0;
+ if (ret)
+ goto fail2;
+ return 0;
+
+fail2:
+ regulator_bulk_free(ARRAY_SIZE(lis3_dev.regulators),
+ lis3_dev.regulators);
fail:
if (pdata && pdata->release_resources)
pdata->release_resources();
@@ -177,12 +173,11 @@ static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client)
if (pdata && pdata->release_resources)
pdata->release_resources();
- lis3lv02d_joystick_disable();
+ lis3lv02d_joystick_disable(lis3);
lis3lv02d_remove_fs(&lis3_dev);
- if (lis3_dev.reg_ctrl)
- regulator_bulk_free(ARRAY_SIZE(lis3->regulators),
- lis3_dev.regulators);
+ regulator_bulk_free(ARRAY_SIZE(lis3->regulators),
+ lis3_dev.regulators);
return 0;
}
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
index c1f8a8fbf694..b2c1be12d16f 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
@@ -83,7 +83,7 @@ static int __devinit lis302dl_spi_probe(struct spi_device *spi)
static int __devexit lis302dl_spi_remove(struct spi_device *spi)
{
struct lis3lv02d *lis3 = spi_get_drvdata(spi);
- lis3lv02d_joystick_disable();
+ lis3lv02d_joystick_disable(lis3);
lis3lv02d_poweroff(lis3);
return lis3lv02d_remove_fs(&lis3_dev);
diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c
index 5fe79df44838..10fc4785dba7 100644
--- a/drivers/misc/pch_phub.c
+++ b/drivers/misc/pch_phub.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD.
+ * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
*
* 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
@@ -41,10 +41,10 @@
#define PCH_PHUB_ROM_START_ADDR_EG20T 0x80 /* ROM data area start address offset
(Intel EG20T PCH)*/
#define PCH_PHUB_ROM_START_ADDR_ML7213 0x400 /* ROM data area start address
- offset(OKI SEMICONDUCTOR ML7213)
+ offset(LAPIS Semicon ML7213)
*/
#define PCH_PHUB_ROM_START_ADDR_ML7223 0x400 /* ROM data area start address
- offset(OKI SEMICONDUCTOR ML7223)
+ offset(LAPIS Semicon ML7223)
*/
/* MAX number of INT_REDUCE_CONTROL registers */
@@ -73,6 +73,9 @@
#define PCI_DEVICE_ID_ROHM_ML7223_mPHUB 0x8012 /* for Bus-m */
#define PCI_DEVICE_ID_ROHM_ML7223_nPHUB 0x8002 /* for Bus-n */
+/* Macros for ML7831 */
+#define PCI_DEVICE_ID_ROHM_ML7831_PHUB 0x8801
+
/* SROM ACCESS Macro */
#define PCH_WORD_ADDR_MASK (~((1 << 2) - 1))
@@ -90,6 +93,7 @@
#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3 0x002C
#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE 0x0040
#define CLKCFG_REG_OFFSET 0x500
+#define FUNCSEL_REG_OFFSET 0x508
#define PCH_PHUB_OROM_SIZE 15360
@@ -108,11 +112,13 @@
* @intpin_reg_wpermit_reg3: INTPIN_REG_WPERMIT register 3 val
* @int_reduce_control_reg: INT_REDUCE_CONTROL registers val
* @clkcfg_reg: CLK CFG register val
+ * @funcsel_reg: Function select register value
* @pch_phub_base_address: Register base address
* @pch_phub_extrom_base_address: external rom base address
* @pch_mac_start_address: MAC address area start address
* @pch_opt_rom_start_address: Option ROM start address
* @ioh_type: Save IOH type
+ * @pdev: pointer to pci device struct
*/
struct pch_phub_reg {
u32 phub_id_reg;
@@ -128,11 +134,13 @@ struct pch_phub_reg {
u32 intpin_reg_wpermit_reg3;
u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
u32 clkcfg_reg;
+ u32 funcsel_reg;
void __iomem *pch_phub_base_address;
void __iomem *pch_phub_extrom_base_address;
u32 pch_mac_start_address;
u32 pch_opt_rom_start_address;
int ioh_type;
+ struct pci_dev *pdev;
};
/* SROM SPEC for MAC address assignment offset */
@@ -211,6 +219,8 @@ static void pch_phub_save_reg_conf(struct pci_dev *pdev)
__func__, i, chip->int_reduce_control_reg[i]);
}
chip->clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
+ if ((chip->ioh_type == 2) || (chip->ioh_type == 4))
+ chip->funcsel_reg = ioread32(p + FUNCSEL_REG_OFFSET);
}
/* pch_phub_restore_reg_conf - restore register configuration */
@@ -271,6 +281,8 @@ static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
}
iowrite32(chip->clkcfg_reg, p + CLKCFG_REG_OFFSET);
+ if ((chip->ioh_type == 2) || (chip->ioh_type == 4))
+ iowrite32(chip->funcsel_reg, p + FUNCSEL_REG_OFFSET);
}
/**
@@ -464,7 +476,7 @@ static int pch_phub_write_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data)
int retval;
int i;
- if (chip->ioh_type == 1) /* EG20T */
+ if ((chip->ioh_type == 1) || (chip->ioh_type == 5)) /* EG20T or ML7831*/
retval = pch_phub_gbe_serial_rom_conf(chip);
else /* ML7223 */
retval = pch_phub_gbe_serial_rom_conf_mp(chip);
@@ -491,6 +503,7 @@ static ssize_t pch_phub_bin_read(struct file *filp, struct kobject *kobj,
unsigned int orom_size;
int ret;
int err;
+ ssize_t rom_size;
struct pch_phub_reg *chip =
dev_get_drvdata(container_of(kobj, struct device, kobj));
@@ -502,6 +515,10 @@ static ssize_t pch_phub_bin_read(struct file *filp, struct kobject *kobj,
}
/* Get Rom signature */
+ chip->pch_phub_extrom_base_address = pci_map_rom(chip->pdev, &rom_size);
+ if (!chip->pch_phub_extrom_base_address)
+ goto exrom_map_err;
+
pch_phub_read_serial_rom(chip, chip->pch_opt_rom_start_address,
(unsigned char *)&rom_signature);
rom_signature &= 0xff;
@@ -532,10 +549,13 @@ static ssize_t pch_phub_bin_read(struct file *filp, struct kobject *kobj,
goto return_err;
}
return_ok:
+ pci_unmap_rom(chip->pdev, chip->pch_phub_extrom_base_address);
mutex_unlock(&pch_phub_mutex);
return addr_offset;
return_err:
+ pci_unmap_rom(chip->pdev, chip->pch_phub_extrom_base_address);
+exrom_map_err:
mutex_unlock(&pch_phub_mutex);
return_err_nomutex:
return err;
@@ -548,6 +568,7 @@ static ssize_t pch_phub_bin_write(struct file *filp, struct kobject *kobj,
int err;
unsigned int addr_offset;
int ret;
+ ssize_t rom_size;
struct pch_phub_reg *chip =
dev_get_drvdata(container_of(kobj, struct device, kobj));
@@ -564,6 +585,12 @@ static ssize_t pch_phub_bin_write(struct file *filp, struct kobject *kobj,
goto return_ok;
}
+ chip->pch_phub_extrom_base_address = pci_map_rom(chip->pdev, &rom_size);
+ if (!chip->pch_phub_extrom_base_address) {
+ err = -ENOMEM;
+ goto exrom_map_err;
+ }
+
for (addr_offset = 0; addr_offset < count; addr_offset++) {
if (PCH_PHUB_OROM_SIZE < off + addr_offset)
goto return_ok;
@@ -578,10 +605,14 @@ static ssize_t pch_phub_bin_write(struct file *filp, struct kobject *kobj,
}
return_ok:
+ pci_unmap_rom(chip->pdev, chip->pch_phub_extrom_base_address);
mutex_unlock(&pch_phub_mutex);
return addr_offset;
return_err:
+ pci_unmap_rom(chip->pdev, chip->pch_phub_extrom_base_address);
+
+exrom_map_err:
mutex_unlock(&pch_phub_mutex);
return err;
}
@@ -591,17 +622,23 @@ static ssize_t show_pch_mac(struct device *dev, struct device_attribute *attr,
{
u8 mac[8];
struct pch_phub_reg *chip = dev_get_drvdata(dev);
+ ssize_t rom_size;
+
+ chip->pch_phub_extrom_base_address = pci_map_rom(chip->pdev, &rom_size);
+ if (!chip->pch_phub_extrom_base_address)
+ return -ENOMEM;
pch_phub_read_gbe_mac_addr(chip, mac);
+ pci_unmap_rom(chip->pdev, chip->pch_phub_extrom_base_address);
- return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ return sprintf(buf, "%pM\n", mac);
}
static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
u8 mac[6];
+ ssize_t rom_size;
struct pch_phub_reg *chip = dev_get_drvdata(dev);
if (count != 18)
@@ -611,7 +648,12 @@ static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr,
(u32 *)&mac[0], (u32 *)&mac[1], (u32 *)&mac[2], (u32 *)&mac[3],
(u32 *)&mac[4], (u32 *)&mac[5]);
+ chip->pch_phub_extrom_base_address = pci_map_rom(chip->pdev, &rom_size);
+ if (!chip->pch_phub_extrom_base_address)
+ return -ENOMEM;
+
pch_phub_write_gbe_mac_addr(chip, mac);
+ pci_unmap_rom(chip->pdev, chip->pch_phub_extrom_base_address);
return count;
}
@@ -634,7 +676,6 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
int retval;
int ret;
- ssize_t rom_size;
struct pch_phub_reg *chip;
chip = kzalloc(sizeof(struct pch_phub_reg), GFP_KERNEL);
@@ -671,21 +712,11 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
"in pch_phub_base_address variable is %p\n", __func__,
chip->pch_phub_base_address);
- if (id->driver_data != 3) {
- chip->pch_phub_extrom_base_address =\
- pci_map_rom(pdev, &rom_size);
- if (chip->pch_phub_extrom_base_address == 0) {
- dev_err(&pdev->dev, "%s: pci_map_rom FAILED", __func__);
- ret = -ENOMEM;
- goto err_pci_map;
- }
- dev_dbg(&pdev->dev, "%s : "
- "pci_map_rom SUCCESS and value in "
- "pch_phub_extrom_base_address variable is %p\n",
- __func__, chip->pch_phub_extrom_base_address);
- }
+ chip->pdev = pdev; /* Save pci device struct */
if (id->driver_data == 1) { /* EG20T PCH */
+ const char *board_name;
+
retval = sysfs_create_file(&pdev->dev.kobj,
&dev_attr_pch_mac.attr);
if (retval)
@@ -701,7 +732,8 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
CLKCFG_CANCLK_MASK);
/* quirk for CM-iTC board */
- if (strstr(dmi_get_system_info(DMI_BOARD_NAME), "CM-iTC"))
+ board_name = dmi_get_system_info(DMI_BOARD_NAME);
+ if (board_name && strstr(board_name, "CM-iTC"))
pch_phub_read_modify_write_reg(chip,
(unsigned int)CLKCFG_REG_OFFSET,
CLKCFG_UART_48MHZ | CLKCFG_BAUDDIV |
@@ -732,6 +764,8 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
* Device8(GbE)
*/
iowrite32(0x000a0000, chip->pch_phub_base_address + 0x14);
+ /* set the interrupt delay value */
+ iowrite32(0x25, chip->pch_phub_base_address + 0x140);
chip->pch_opt_rom_start_address =\
PCH_PHUB_ROM_START_ADDR_ML7223;
chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_ML7223;
@@ -749,11 +783,25 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
* Device6(SATA 2):f
*/
iowrite32(0x0000ffa0, chip->pch_phub_base_address + 0x14);
- /* set the interrupt delay value */
- iowrite32(0x25, chip->pch_phub_base_address + 0x140);
chip->pch_opt_rom_start_address =\
PCH_PHUB_ROM_START_ADDR_ML7223;
chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_ML7223;
+ } else if (id->driver_data == 5) { /* ML7831 */
+ retval = sysfs_create_file(&pdev->dev.kobj,
+ &dev_attr_pch_mac.attr);
+ if (retval)
+ goto err_sysfs_create;
+
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
+ if (retval)
+ goto exit_bin_attr;
+
+ /* set the prefech value */
+ iowrite32(0x000affaa, chip->pch_phub_base_address + 0x14);
+ /* set the interrupt delay value */
+ iowrite32(0x25, chip->pch_phub_base_address + 0x44);
+ chip->pch_opt_rom_start_address = PCH_PHUB_ROM_START_ADDR_EG20T;
+ chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_EG20T;
}
chip->ioh_type = id->driver_data;
@@ -764,8 +812,6 @@ exit_bin_attr:
sysfs_remove_file(&pdev->dev.kobj, &dev_attr_pch_mac.attr);
err_sysfs_create:
- pci_unmap_rom(pdev, chip->pch_phub_extrom_base_address);
-err_pci_map:
pci_iounmap(pdev, chip->pch_phub_base_address);
err_pci_iomap:
pci_release_regions(pdev);
@@ -783,7 +829,6 @@ static void __devexit pch_phub_remove(struct pci_dev *pdev)
sysfs_remove_file(&pdev->dev.kobj, &dev_attr_pch_mac.attr);
sysfs_remove_bin_file(&pdev->dev.kobj, &pch_bin_attr);
- pci_unmap_rom(pdev, chip->pch_phub_extrom_base_address);
pci_iounmap(pdev, chip->pch_phub_base_address);
pci_release_regions(pdev);
pci_disable_device(pdev);
@@ -838,6 +883,7 @@ static struct pci_device_id pch_phub_pcidev_id[] = {
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7213_PHUB), 2, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7223_mPHUB), 3, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7223_nPHUB), 4, },
+ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7831_PHUB), 5, },
{ }
};
MODULE_DEVICE_TABLE(pci, pch_phub_pcidev_id);
@@ -864,5 +910,5 @@ static void __exit pch_phub_pci_exit(void)
module_init(pch_phub_pci_init);
module_exit(pch_phub_pci_exit);
-MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR IOH(ML7213/ML7223) PHUB");
+MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7223) PHUB");
MODULE_LICENSE("GPL");
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c
index b05db55c8c8e..21b28fc6d912 100644
--- a/drivers/misc/phantom.c
+++ b/drivers/misc/phantom.c
@@ -26,7 +26,7 @@
#include <linux/sched.h>
#include <linux/mutex.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/io.h>
#define PHANTOM_VERSION "n0.9.8"
diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c
index 374dfcfccd07..0b56e3f43573 100644
--- a/drivers/misc/pti.c
+++ b/drivers/misc/pti.c
@@ -33,6 +33,8 @@
#include <linux/mutex.h>
#include <linux/miscdevice.h>
#include <linux/pti.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
#define DRIVERNAME "pti"
#define PCINAME "pciPTI"
@@ -146,45 +148,52 @@ static void pti_write_to_aperture(struct pti_masterchannel *mc,
/**
* pti_control_frame_built_and_sent()- control frame build and send function.
*
- * @mc: The master / channel structure on which the function
- * built a control frame.
+ * @mc: The master / channel structure on which the function
+ * built a control frame.
+ * @thread_name: The thread name associated with the master / channel or
+ * 'NULL' if using the 'current' global variable.
*
* To be able to post process the PTI contents on host side, a control frame
* is added before sending any PTI content. So the host side knows on
* each PTI frame the name of the thread using a dedicated master / channel.
- * The thread name is retrieved from the 'current' global variable.
+ * The thread name is retrieved from 'current' global variable if 'thread_name'
+ * is 'NULL', else it is retrieved from 'thread_name' parameter.
* This function builds this frame and sends it to a master ID CONTROL_ID.
* The overhead is only 32 bytes since the driver only writes to HW
* in 32 byte chunks.
*/
-
-static void pti_control_frame_built_and_sent(struct pti_masterchannel *mc)
+static void pti_control_frame_built_and_sent(struct pti_masterchannel *mc,
+ const char *thread_name)
{
+ /*
+ * Since we access the comm member in current's task_struct, we only
+ * need to be as large as what 'comm' in that structure is.
+ */
+ char comm[TASK_COMM_LEN];
struct pti_masterchannel mccontrol = {.master = CONTROL_ID,
.channel = 0};
+ const char *thread_name_p;
const char *control_format = "%3d %3d %s";
u8 control_frame[CONTROL_FRAME_LEN];
- /*
- * Since we access the comm member in current's task_struct,
- * we only need to be as large as what 'comm' in that
- * structure is.
- */
- char comm[TASK_COMM_LEN];
-
- if (!in_interrupt())
- get_task_comm(comm, current);
- else
- strncpy(comm, "Interrupt", TASK_COMM_LEN);
+ if (!thread_name) {
+ if (!in_interrupt())
+ get_task_comm(comm, current);
+ else
+ strncpy(comm, "Interrupt", TASK_COMM_LEN);
- /* Absolutely ensure our buffer is zero terminated. */
- comm[TASK_COMM_LEN-1] = 0;
+ /* Absolutely ensure our buffer is zero terminated. */
+ comm[TASK_COMM_LEN-1] = 0;
+ thread_name_p = comm;
+ } else {
+ thread_name_p = thread_name;
+ }
mccontrol.channel = pti_control_channel;
pti_control_channel = (pti_control_channel + 1) & 0x7f;
snprintf(control_frame, CONTROL_FRAME_LEN, control_format, mc->master,
- mc->channel, comm);
+ mc->channel, thread_name_p);
pti_write_to_aperture(&mccontrol, control_frame, strlen(control_frame));
}
@@ -206,18 +215,20 @@ static void pti_write_full_frame_to_aperture(struct pti_masterchannel *mc,
const unsigned char *buf,
int len)
{
- pti_control_frame_built_and_sent(mc);
+ pti_control_frame_built_and_sent(mc, NULL);
pti_write_to_aperture(mc, (u8 *)buf, len);
}
/**
* get_id()- Allocate a master and channel ID.
*
- * @id_array: an array of bits representing what channel
- * id's are allocated for writing.
- * @max_ids: The max amount of available write IDs to use.
- * @base_id: The starting SW channel ID, based on the Intel
- * PTI arch.
+ * @id_array: an array of bits representing what channel
+ * id's are allocated for writing.
+ * @max_ids: The max amount of available write IDs to use.
+ * @base_id: The starting SW channel ID, based on the Intel
+ * PTI arch.
+ * @thread_name: The thread name associated with the master / channel or
+ * 'NULL' if using the 'current' global variable.
*
* Returns:
* pti_masterchannel struct with master, channel ID address
@@ -227,7 +238,10 @@ static void pti_write_full_frame_to_aperture(struct pti_masterchannel *mc,
* channel id. The bit is one if the id is taken and 0 if free. For
* every master there are 128 channel id's.
*/
-static struct pti_masterchannel *get_id(u8 *id_array, int max_ids, int base_id)
+static struct pti_masterchannel *get_id(u8 *id_array,
+ int max_ids,
+ int base_id,
+ const char *thread_name)
{
struct pti_masterchannel *mc;
int i, j, mask;
@@ -257,7 +271,7 @@ static struct pti_masterchannel *get_id(u8 *id_array, int max_ids, int base_id)
mc->master = base_id;
mc->channel = ((i & 0xf)<<3) + j;
/* write new master Id / channel Id allocation to channel control */
- pti_control_frame_built_and_sent(mc);
+ pti_control_frame_built_and_sent(mc, thread_name);
return mc;
}
@@ -273,18 +287,22 @@ static struct pti_masterchannel *get_id(u8 *id_array, int max_ids, int base_id)
* a master, channel ID address
* to write to PTI HW.
*
- * @type: 0- request Application master, channel aperture ID write address.
- * 1- request OS master, channel aperture ID write
- * address.
- * 2- request Modem master, channel aperture ID
- * write address.
- * Other values, error.
+ * @type: 0- request Application master, channel aperture ID
+ * write address.
+ * 1- request OS master, channel aperture ID write
+ * address.
+ * 2- request Modem master, channel aperture ID
+ * write address.
+ * Other values, error.
+ * @thread_name: The thread name associated with the master / channel or
+ * 'NULL' if using the 'current' global variable.
*
* Returns:
* pti_masterchannel struct
* 0 for error
*/
-struct pti_masterchannel *pti_request_masterchannel(u8 type)
+struct pti_masterchannel *pti_request_masterchannel(u8 type,
+ const char *thread_name)
{
struct pti_masterchannel *mc;
@@ -293,15 +311,18 @@ struct pti_masterchannel *pti_request_masterchannel(u8 type)
switch (type) {
case 0:
- mc = get_id(drv_data->ia_app, MAX_APP_IDS, APP_BASE_ID);
+ mc = get_id(drv_data->ia_app, MAX_APP_IDS,
+ APP_BASE_ID, thread_name);
break;
case 1:
- mc = get_id(drv_data->ia_os, MAX_OS_IDS, OS_BASE_ID);
+ mc = get_id(drv_data->ia_os, MAX_OS_IDS,
+ OS_BASE_ID, thread_name);
break;
case 2:
- mc = get_id(drv_data->ia_modem, MAX_MODEM_IDS, MODEM_BASE_ID);
+ mc = get_id(drv_data->ia_modem, MAX_MODEM_IDS,
+ MODEM_BASE_ID, thread_name);
break;
default:
mc = NULL;
@@ -445,9 +466,9 @@ static void pti_tty_driver_close(struct tty_struct *tty, struct file *filp)
}
/**
- * pti_tty_intstall()- Used to set up specific master-channels
- * to tty ports for organizational purposes when
- * tracing viewed from debuging tools.
+ * pti_tty_install()- Used to set up specific master-channels
+ * to tty ports for organizational purposes when
+ * tracing viewed from debuging tools.
*
* @driver: tty driver information.
* @tty: tty struct containing pti information.
@@ -472,9 +493,9 @@ static int pti_tty_install(struct tty_driver *driver, struct tty_struct *tty)
return -ENOMEM;
if (idx == PTITTY_MINOR_START)
- pti_tty_data->mc = pti_request_masterchannel(0);
+ pti_tty_data->mc = pti_request_masterchannel(0, NULL);
else
- pti_tty_data->mc = pti_request_masterchannel(2);
+ pti_tty_data->mc = pti_request_masterchannel(2, NULL);
if (pti_tty_data->mc == NULL) {
kfree(pti_tty_data);
@@ -563,7 +584,7 @@ static int pti_char_open(struct inode *inode, struct file *filp)
* before assigning the value to filp->private_data.
* Slightly easier to debug if this driver needs debugging.
*/
- mc = pti_request_masterchannel(0);
+ mc = pti_request_masterchannel(0, NULL);
if (mc == NULL)
return -ENOMEM;
filp->private_data = mc;
diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c
index 9e9bddaa95ae..913de07e577c 100644
--- a/drivers/misc/sgi-gru/grukservices.c
+++ b/drivers/misc/sgi-gru/grukservices.c
@@ -31,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
+#include <linux/export.h>
#include <asm/io_apic.h>
#include "gru.h"
#include "grulib.h"
diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c
index cfbddbef11de..43d073bc1d9c 100644
--- a/drivers/misc/spear13xx_pcie_gadget.c
+++ b/drivers/misc/spear13xx_pcie_gadget.c
@@ -903,6 +903,6 @@ static void __exit spear_pcie_gadget_exit(void)
}
module_exit(spear_pcie_gadget_exit);
-MODULE_ALIAS("pcie-gadget-spear");
+MODULE_ALIAS("platform:pcie-gadget-spear");
MODULE_AUTHOR("Pratyush Anand");
MODULE_LICENSE("GPL");
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
index 54c91ffe4a91..ba168a7d54d4 100644
--- a/drivers/misc/ti-st/st_core.c
+++ b/drivers/misc/ti-st/st_core.c
@@ -338,6 +338,12 @@ void st_int_recv(void *disc_data,
/* Unknow packet? */
default:
type = *ptr;
+ if (st_gdata->list[type] == NULL) {
+ pr_err("chip/interface misbehavior dropping"
+ " frame starting with 0x%02x", type);
+ goto done;
+
+ }
st_gdata->rx_skb = alloc_skb(
st_gdata->list[type]->max_frame_size,
GFP_ATOMIC);
@@ -354,6 +360,7 @@ void st_int_recv(void *disc_data,
ptr++;
count--;
}
+done:
spin_unlock_irqrestore(&st_gdata->lock, flags);
pr_debug("done %s", __func__);
return;
@@ -717,9 +724,10 @@ static void st_tty_close(struct tty_struct *tty)
*/
spin_lock_irqsave(&st_gdata->lock, flags);
for (i = ST_BT; i < ST_MAX_CHANNELS; i++) {
- if (st_gdata->list[i] != NULL)
+ if (st_gdata->is_registered[i] == true)
pr_err("%d not un-registered", i);
st_gdata->list[i] = NULL;
+ st_gdata->is_registered[i] = false;
}
st_gdata->protos_registered = 0;
spin_unlock_irqrestore(&st_gdata->lock, flags);
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index 38fd2f04c07e..43ef8d162f2d 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -35,6 +35,7 @@
#include <linux/skbuff.h>
#include <linux/ti_wilink_st.h>
+#include <linux/module.h>
#define MAX_ST_DEVICES 3 /* Imagine 1 on each UART for now */
@@ -68,6 +69,7 @@ void validate_firmware_response(struct kim_data_s *kim_gdata)
if (unlikely(skb->data[5] != 0)) {
pr_err("no proper response during fw download");
pr_err("data6 %x", skb->data[5]);
+ kfree_skb(skb);
return; /* keep waiting for the proper response */
}
/* becos of all the script being downloaded */
@@ -210,6 +212,7 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
pr_err(" waiting for ver info- timed out ");
return -ETIMEDOUT;
}
+ INIT_COMPLETION(kim_gdata->kim_rcvd);
version =
MAKEWORD(kim_gdata->resp_buffer[13],
@@ -298,6 +301,7 @@ static long download_firmware(struct kim_data_s *kim_gdata)
switch (((struct bts_action *)ptr)->type) {
case ACTION_SEND_COMMAND: /* action send */
+ pr_debug("S");
action_ptr = &(((struct bts_action *)ptr)->data[0]);
if (unlikely
(((struct hci_command *)action_ptr)->opcode ==
@@ -335,6 +339,10 @@ static long download_firmware(struct kim_data_s *kim_gdata)
release_firmware(kim_gdata->fw_entry);
return -ETIMEDOUT;
}
+ /* reinit completion before sending for the
+ * relevant wait
+ */
+ INIT_COMPLETION(kim_gdata->kim_rcvd);
/*
* Free space found in uart buffer, call st_int_write
@@ -361,6 +369,7 @@ static long download_firmware(struct kim_data_s *kim_gdata)
}
break;
case ACTION_WAIT_EVENT: /* wait */
+ pr_debug("W");
if (!wait_for_completion_timeout
(&kim_gdata->kim_rcvd,
msecs_to_jiffies(CMD_RESP_TIME))) {
@@ -434,11 +443,17 @@ long st_kim_start(void *kim_data)
{
long err = 0;
long retry = POR_RETRY_COUNT;
+ struct ti_st_plat_data *pdata;
struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data;
pr_info(" %s", __func__);
+ pdata = kim_gdata->kim_pdev->dev.platform_data;
do {
+ /* platform specific enabling code here */
+ if (pdata->chip_enable)
+ pdata->chip_enable(kim_gdata);
+
/* Configure BT nShutdown to HIGH state */
gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
mdelay(5); /* FIXME: a proper toggle */
@@ -460,6 +475,12 @@ long st_kim_start(void *kim_data)
pr_info("ldisc_install = 0");
sysfs_notify(&kim_gdata->kim_pdev->dev.kobj,
NULL, "install");
+ /* the following wait is never going to be completed,
+ * since the ldisc was never installed, hence serving
+ * as a mdelay of LDISC_TIME msecs */
+ err = wait_for_completion_timeout
+ (&kim_gdata->ldisc_installed,
+ msecs_to_jiffies(LDISC_TIME));
err = -ETIMEDOUT;
continue;
} else {
@@ -472,6 +493,13 @@ long st_kim_start(void *kim_data)
pr_info("ldisc_install = 0");
sysfs_notify(&kim_gdata->kim_pdev->dev.kobj,
NULL, "install");
+ /* this wait might be completed, though in the
+ * tty_close() since the ldisc is already
+ * installed */
+ err = wait_for_completion_timeout
+ (&kim_gdata->ldisc_installed,
+ msecs_to_jiffies(LDISC_TIME));
+ err = -EINVAL;
continue;
} else { /* on success don't retry */
break;
@@ -489,6 +517,8 @@ long st_kim_stop(void *kim_data)
{
long err = 0;
struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data;
+ struct ti_st_plat_data *pdata =
+ kim_gdata->kim_pdev->dev.platform_data;
INIT_COMPLETION(kim_gdata->ldisc_installed);
@@ -515,6 +545,10 @@ long st_kim_stop(void *kim_data)
gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH);
mdelay(1);
gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
+
+ /* platform specific disable */
+ if (pdata->chip_disable)
+ pdata->chip_disable(kim_gdata);
return err;
}
diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c
index 3f2495138855..1ff460a8e9c7 100644
--- a/drivers/misc/ti-st/st_ll.c
+++ b/drivers/misc/ti-st/st_ll.c
@@ -22,6 +22,7 @@
#define pr_fmt(fmt) "(stll) :" fmt
#include <linux/skbuff.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/ti_wilink_st.h>
/**********************************************************************/
@@ -37,6 +38,9 @@ static void send_ll_cmd(struct st_data_s *st_data,
static void ll_device_want_to_sleep(struct st_data_s *st_data)
{
+ struct kim_data_s *kim_data;
+ struct ti_st_plat_data *pdata;
+
pr_debug("%s", __func__);
/* sanity check */
if (st_data->ll_state != ST_LL_AWAKE)
@@ -46,10 +50,19 @@ static void ll_device_want_to_sleep(struct st_data_s *st_data)
send_ll_cmd(st_data, LL_SLEEP_ACK);
/* update state */
st_data->ll_state = ST_LL_ASLEEP;
+
+ /* communicate to platform about chip asleep */
+ kim_data = st_data->kim_data;
+ pdata = kim_data->kim_pdev->dev.platform_data;
+ if (pdata->chip_asleep)
+ pdata->chip_asleep(NULL);
}
static void ll_device_want_to_wakeup(struct st_data_s *st_data)
{
+ struct kim_data_s *kim_data;
+ struct ti_st_plat_data *pdata;
+
/* diff actions in diff states */
switch (st_data->ll_state) {
case ST_LL_ASLEEP:
@@ -70,6 +83,12 @@ static void ll_device_want_to_wakeup(struct st_data_s *st_data)
}
/* update state */
st_data->ll_state = ST_LL_AWAKE;
+
+ /* communicate to platform about chip wakeup */
+ kim_data = st_data->kim_data;
+ pdata = kim_data->kim_pdev->dev.platform_data;
+ if (pdata->chip_asleep)
+ pdata->chip_awake(NULL);
}
/**********************************************************************/
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c
index a6ef18259da0..ba2479022670 100644
--- a/drivers/misc/tifm_7xx1.c
+++ b/drivers/misc/tifm_7xx1.c
@@ -11,6 +11,7 @@
#include <linux/tifm.h>
#include <linux/dma-mapping.h>
+#include <linux/module.h>
#define DRIVER_NAME "tifm_7xx1"
#define DRIVER_VERSION "0.8"
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c
index 44d4475a09dd..0bd5349b0422 100644
--- a/drivers/misc/tifm_core.c
+++ b/drivers/misc/tifm_core.c
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/idr.h>
+#include <linux/module.h>
#define DRIVER_NAME "tifm_core"
#define DRIVER_VERSION "0.8"
diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c
index 6df5a55da110..cd41d403c9df 100644
--- a/drivers/misc/vmw_balloon.c
+++ b/drivers/misc/vmw_balloon.c
@@ -45,7 +45,7 @@
MODULE_AUTHOR("VMware, Inc.");
MODULE_DESCRIPTION("VMware Memory Control (Balloon) Driver");
-MODULE_VERSION("1.2.1.2-k");
+MODULE_VERSION("1.2.1.3-k");
MODULE_ALIAS("dmi:*:svnVMware*:*");
MODULE_ALIAS("vmware_vmmemctl");
MODULE_LICENSE("GPL");
@@ -151,7 +151,7 @@ MODULE_LICENSE("GPL");
struct vmballoon_stats {
unsigned int timer;
- /* allocation statustics */
+ /* allocation statistics */
unsigned int alloc;
unsigned int alloc_fail;
unsigned int sleep_alloc;
@@ -215,7 +215,6 @@ struct vmballoon {
};
static struct vmballoon balloon;
-static struct workqueue_struct *vmballoon_wq;
/*
* Send "start" command to the host, communicating supported version
@@ -413,6 +412,7 @@ static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep)
gfp_t flags;
unsigned int hv_status;
bool locked = false;
+ flags = can_sleep ? VMW_PAGE_ALLOC_CANSLEEP : VMW_PAGE_ALLOC_NOSLEEP;
do {
if (!can_sleep)
@@ -420,7 +420,6 @@ static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep)
else
STATS_INC(b->stats.sleep_alloc);
- flags = can_sleep ? VMW_PAGE_ALLOC_CANSLEEP : VMW_PAGE_ALLOC_NOSLEEP;
page = alloc_page(flags);
if (!page) {
if (!can_sleep)
@@ -674,7 +673,12 @@ static void vmballoon_work(struct work_struct *work)
vmballoon_deflate(b);
}
- queue_delayed_work(vmballoon_wq, dwork, round_jiffies_relative(HZ));
+ /*
+ * We are using a freezable workqueue so that balloon operations are
+ * stopped while the system transitions to/from sleep/hibernation.
+ */
+ queue_delayed_work(system_freezable_wq,
+ dwork, round_jiffies_relative(HZ));
}
/*
@@ -785,12 +789,6 @@ static int __init vmballoon_init(void)
if (x86_hyper != &x86_hyper_vmware)
return -ENODEV;
- vmballoon_wq = create_freezable_workqueue("vmmemctl");
- if (!vmballoon_wq) {
- pr_err("failed to create workqueue\n");
- return -ENOMEM;
- }
-
INIT_LIST_HEAD(&balloon.pages);
INIT_LIST_HEAD(&balloon.refused_pages);
@@ -805,34 +803,27 @@ static int __init vmballoon_init(void)
*/
if (!vmballoon_send_start(&balloon)) {
pr_err("failed to send start command to the host\n");
- error = -EIO;
- goto fail;
+ return -EIO;
}
if (!vmballoon_send_guest_id(&balloon)) {
pr_err("failed to send guest ID to the host\n");
- error = -EIO;
- goto fail;
+ return -EIO;
}
error = vmballoon_debugfs_init(&balloon);
if (error)
- goto fail;
+ return error;
- queue_delayed_work(vmballoon_wq, &balloon.dwork, 0);
+ queue_delayed_work(system_freezable_wq, &balloon.dwork, 0);
return 0;
-
-fail:
- destroy_workqueue(vmballoon_wq);
- return error;
}
module_init(vmballoon_init);
static void __exit vmballoon_exit(void)
{
cancel_delayed_work_sync(&balloon.dwork);
- destroy_workqueue(vmballoon_wq);
vmballoon_debugfs_exit(&balloon);
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index f85e42224559..a1cb21f95302 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -94,6 +94,11 @@ struct mmc_blk_data {
unsigned int read_only;
unsigned int part_type;
unsigned int name_idx;
+ unsigned int reset_done;
+#define MMC_BLK_READ BIT(0)
+#define MMC_BLK_WRITE BIT(1)
+#define MMC_BLK_DISCARD BIT(2)
+#define MMC_BLK_SECDISCARD BIT(3)
/*
* Only set in main mmc_blk_data associated
@@ -106,6 +111,16 @@ struct mmc_blk_data {
static DEFINE_MUTEX(open_lock);
+enum mmc_blk_status {
+ MMC_BLK_SUCCESS = 0,
+ MMC_BLK_PARTIAL,
+ MMC_BLK_CMD_ERR,
+ MMC_BLK_RETRY,
+ MMC_BLK_ABORT,
+ MMC_BLK_DATA_ERR,
+ MMC_BLK_ECC_ERR,
+};
+
module_param(perdev_minors, int, 0444);
MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
@@ -281,7 +296,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
struct mmc_card *card;
struct mmc_command cmd = {0};
struct mmc_data data = {0};
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
struct scatterlist sg;
int err;
@@ -427,32 +442,29 @@ static const struct block_device_operations mmc_bdops = {
#endif
};
-struct mmc_blk_request {
- struct mmc_request mrq;
- struct mmc_command sbc;
- struct mmc_command cmd;
- struct mmc_command stop;
- struct mmc_data data;
-};
-
static inline int mmc_blk_part_switch(struct mmc_card *card,
struct mmc_blk_data *md)
{
int ret;
struct mmc_blk_data *main_md = mmc_get_drvdata(card);
+
if (main_md->part_curr == md->part_type)
return 0;
if (mmc_card_mmc(card)) {
- card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
- card->ext_csd.part_config |= md->part_type;
+ u8 part_config = card->ext_csd.part_config;
+
+ part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
+ part_config |= md->part_type;
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_PART_CONFIG, card->ext_csd.part_config,
+ EXT_CSD_PART_CONFIG, part_config,
card->ext_csd.part_time);
if (ret)
return ret;
-}
+
+ card->ext_csd.part_config = part_config;
+ }
main_md->part_curr = md->part_type;
return 0;
@@ -464,7 +476,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
u32 result;
__be32 *blocks;
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_data data = {0};
unsigned int timeout_us;
@@ -525,7 +537,20 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
return result;
}
-static u32 get_card_status(struct mmc_card *card, struct request *req)
+static int send_stop(struct mmc_card *card, u32 *status)
+{
+ struct mmc_command cmd = {0};
+ int err;
+
+ cmd.opcode = MMC_STOP_TRANSMISSION;
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 5);
+ if (err == 0)
+ *status = cmd.resp[0];
+ return err;
+}
+
+static int get_card_status(struct mmc_card *card, u32 *status, int retries)
{
struct mmc_command cmd = {0};
int err;
@@ -534,11 +559,186 @@ static u32 get_card_status(struct mmc_card *card, struct request *req)
if (!mmc_host_is_spi(card->host))
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
- err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ err = mmc_wait_for_cmd(card->host, &cmd, retries);
+ if (err == 0)
+ *status = cmd.resp[0];
+ return err;
+}
+
+#define ERR_RETRY 2
+#define ERR_ABORT 1
+#define ERR_CONTINUE 0
+
+static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
+ bool status_valid, u32 status)
+{
+ switch (error) {
+ case -EILSEQ:
+ /* response crc error, retry the r/w cmd */
+ pr_err("%s: %s sending %s command, card status %#x\n",
+ req->rq_disk->disk_name, "response CRC error",
+ name, status);
+ return ERR_RETRY;
+
+ case -ETIMEDOUT:
+ pr_err("%s: %s sending %s command, card status %#x\n",
+ req->rq_disk->disk_name, "timed out", name, status);
+
+ /* If the status cmd initially failed, retry the r/w cmd */
+ if (!status_valid)
+ return ERR_RETRY;
+
+ /*
+ * If it was a r/w cmd crc error, or illegal command
+ * (eg, issued in wrong state) then retry - we should
+ * have corrected the state problem above.
+ */
+ if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND))
+ return ERR_RETRY;
+
+ /* Otherwise abort the command */
+ return ERR_ABORT;
+
+ default:
+ /* We don't understand the error code the driver gave us */
+ pr_err("%s: unknown error %d sending read/write command, card status %#x\n",
+ req->rq_disk->disk_name, error, status);
+ return ERR_ABORT;
+ }
+}
+
+/*
+ * Initial r/w and stop cmd error recovery.
+ * We don't know whether the card received the r/w cmd or not, so try to
+ * restore things back to a sane state. Essentially, we do this as follows:
+ * - Obtain card status. If the first attempt to obtain card status fails,
+ * the status word will reflect the failed status cmd, not the failed
+ * r/w cmd. If we fail to obtain card status, it suggests we can no
+ * longer communicate with the card.
+ * - Check the card state. If the card received the cmd but there was a
+ * transient problem with the response, it might still be in a data transfer
+ * mode. Try to send it a stop command. If this fails, we can't recover.
+ * - If the r/w cmd failed due to a response CRC error, it was probably
+ * transient, so retry the cmd.
+ * - If the r/w cmd timed out, but we didn't get the r/w cmd status, retry.
+ * - If the r/w cmd timed out, and the r/w cmd failed due to CRC error or
+ * illegal cmd, retry.
+ * Otherwise we don't understand what happened, so abort.
+ */
+static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
+ struct mmc_blk_request *brq, int *ecc_err)
+{
+ bool prev_cmd_status_valid = true;
+ u32 status, stop_status = 0;
+ int err, retry;
+
+ /*
+ * Try to get card status which indicates both the card state
+ * and why there was no response. If the first attempt fails,
+ * we can't be sure the returned status is for the r/w command.
+ */
+ for (retry = 2; retry >= 0; retry--) {
+ err = get_card_status(card, &status, 0);
+ if (!err)
+ break;
+
+ prev_cmd_status_valid = false;
+ pr_err("%s: error %d sending status command, %sing\n",
+ req->rq_disk->disk_name, err, retry ? "retry" : "abort");
+ }
+
+ /* We couldn't get a response from the card. Give up. */
if (err)
- printk(KERN_ERR "%s: error %d sending status command",
- req->rq_disk->disk_name, err);
- return cmd.resp[0];
+ return ERR_ABORT;
+
+ /* Flag ECC errors */
+ if ((status & R1_CARD_ECC_FAILED) ||
+ (brq->stop.resp[0] & R1_CARD_ECC_FAILED) ||
+ (brq->cmd.resp[0] & R1_CARD_ECC_FAILED))
+ *ecc_err = 1;
+
+ /*
+ * Check the current card state. If it is in some data transfer
+ * mode, tell it to stop (and hopefully transition back to TRAN.)
+ */
+ if (R1_CURRENT_STATE(status) == R1_STATE_DATA ||
+ R1_CURRENT_STATE(status) == R1_STATE_RCV) {
+ err = send_stop(card, &stop_status);
+ if (err)
+ pr_err("%s: error %d sending stop command\n",
+ req->rq_disk->disk_name, err);
+
+ /*
+ * If the stop cmd also timed out, the card is probably
+ * not present, so abort. Other errors are bad news too.
+ */
+ if (err)
+ return ERR_ABORT;
+ if (stop_status & R1_CARD_ECC_FAILED)
+ *ecc_err = 1;
+ }
+
+ /* Check for set block count errors */
+ if (brq->sbc.error)
+ return mmc_blk_cmd_error(req, "SET_BLOCK_COUNT", brq->sbc.error,
+ prev_cmd_status_valid, status);
+
+ /* Check for r/w command errors */
+ if (brq->cmd.error)
+ return mmc_blk_cmd_error(req, "r/w cmd", brq->cmd.error,
+ prev_cmd_status_valid, status);
+
+ /* Data errors */
+ if (!brq->stop.error)
+ return ERR_CONTINUE;
+
+ /* Now for stop errors. These aren't fatal to the transfer. */
+ pr_err("%s: error %d sending stop command, original cmd response %#x, card status %#x\n",
+ req->rq_disk->disk_name, brq->stop.error,
+ brq->cmd.resp[0], status);
+
+ /*
+ * Subsitute in our own stop status as this will give the error
+ * state which happened during the execution of the r/w command.
+ */
+ if (stop_status) {
+ brq->stop.resp[0] = stop_status;
+ brq->stop.error = 0;
+ }
+ return ERR_CONTINUE;
+}
+
+static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
+ int type)
+{
+ int err;
+
+ if (md->reset_done & type)
+ return -EEXIST;
+
+ md->reset_done |= type;
+ err = mmc_hw_reset(host);
+ /* Ensure we switch back to the correct partition */
+ if (err != -EOPNOTSUPP) {
+ struct mmc_blk_data *main_md = mmc_get_drvdata(host->card);
+ int part_err;
+
+ main_md->part_curr = main_md->part_type;
+ part_err = mmc_blk_part_switch(host->card, md);
+ if (part_err) {
+ /*
+ * We have failed to get back into the correct
+ * partition, so we need to abort the whole request.
+ */
+ return -ENODEV;
+ }
+ }
+ return err;
+}
+
+static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type)
+{
+ md->reset_done &= ~type;
}
static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
@@ -546,7 +746,7 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
unsigned int from, nr, arg;
- int err = 0;
+ int err = 0, type = MMC_BLK_DISCARD;
if (!mmc_can_erase(card)) {
err = -EOPNOTSUPP;
@@ -556,11 +756,13 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
from = blk_rq_pos(req);
nr = blk_rq_sectors(req);
- if (mmc_can_trim(card))
+ if (mmc_can_discard(card))
+ arg = MMC_DISCARD_ARG;
+ else if (mmc_can_trim(card))
arg = MMC_TRIM_ARG;
else
arg = MMC_ERASE_ARG;
-
+retry:
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
INAND_CMD38_ARG_EXT_CSD,
@@ -573,6 +775,10 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
}
err = mmc_erase(card, from, nr, arg);
out:
+ if (err == -EIO && !mmc_blk_reset(md, card->host, type))
+ goto retry;
+ if (!err)
+ mmc_blk_reset_success(md, type);
spin_lock_irq(&md->lock);
__blk_end_request(req, err, blk_rq_bytes(req));
spin_unlock_irq(&md->lock);
@@ -586,13 +792,20 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
unsigned int from, nr, arg;
- int err = 0;
+ int err = 0, type = MMC_BLK_SECDISCARD;
- if (!mmc_can_secure_erase_trim(card)) {
+ if (!(mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))) {
err = -EOPNOTSUPP;
goto out;
}
+ /* The sanitize operation is supported at v4.5 only */
+ if (mmc_can_sanitize(card)) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_SANITIZE_START, 1, 0);
+ goto out;
+ }
+
from = blk_rq_pos(req);
nr = blk_rq_sectors(req);
@@ -600,7 +813,7 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
arg = MMC_SECURE_TRIM1_ARG;
else
arg = MMC_SECURE_ERASE_ARG;
-
+retry:
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
INAND_CMD38_ARG_EXT_CSD,
@@ -624,6 +837,10 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
}
out:
+ if (err == -EIO && !mmc_blk_reset(md, card->host, type))
+ goto retry;
+ if (!err)
+ mmc_blk_reset_success(md, type);
spin_lock_irq(&md->lock);
__blk_end_request(req, err, blk_rq_bytes(req));
spin_unlock_irq(&md->lock);
@@ -634,16 +851,18 @@ out:
static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ int ret = 0;
+
+ ret = mmc_flush_cache(card);
+ if (ret)
+ ret = -EIO;
- /*
- * No-op, only service this because we need REQ_FUA for reliable
- * writes.
- */
spin_lock_irq(&md->lock);
- __blk_end_request_all(req, 0);
+ __blk_end_request_all(req, ret);
spin_unlock_irq(&md->lock);
- return 1;
+ return ret ? 0 : 1;
}
/*
@@ -669,249 +888,255 @@ static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq,
}
}
-static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
+#define CMD_ERRORS \
+ (R1_OUT_OF_RANGE | /* Command argument out of range */ \
+ R1_ADDRESS_ERROR | /* Misaligned address */ \
+ R1_BLOCK_LEN_ERROR | /* Transferred block length incorrect */\
+ R1_WP_VIOLATION | /* Tried to write to protected block */ \
+ R1_CC_ERROR | /* Card controller error */ \
+ R1_ERROR) /* General/unknown error */
+
+static int mmc_blk_err_check(struct mmc_card *card,
+ struct mmc_async_req *areq)
{
- struct mmc_blk_data *md = mq->data;
- struct mmc_card *card = md->queue.card;
- struct mmc_blk_request brq;
- int ret = 1, disable_multi = 0;
+ struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req,
+ mmc_active);
+ struct mmc_blk_request *brq = &mq_mrq->brq;
+ struct request *req = mq_mrq->req;
+ int ecc_err = 0;
/*
- * Reliable writes are used to implement Forced Unit Access and
- * REQ_META accesses, and are supported only on MMCs.
+ * sbc.error indicates a problem with the set block count
+ * command. No data will have been transferred.
+ *
+ * cmd.error indicates a problem with the r/w command. No
+ * data will have been transferred.
+ *
+ * stop.error indicates a problem with the stop command. Data
+ * may have been transferred, or may still be transferring.
*/
- bool do_rel_wr = ((req->cmd_flags & REQ_FUA) ||
- (req->cmd_flags & REQ_META)) &&
- (rq_data_dir(req) == WRITE) &&
- (md->flags & MMC_BLK_REL_WR);
+ if (brq->sbc.error || brq->cmd.error || brq->stop.error ||
+ brq->data.error) {
+ switch (mmc_blk_cmd_recovery(card, req, brq, &ecc_err)) {
+ case ERR_RETRY:
+ return MMC_BLK_RETRY;
+ case ERR_ABORT:
+ return MMC_BLK_ABORT;
+ case ERR_CONTINUE:
+ break;
+ }
+ }
- do {
- struct mmc_command cmd = {0};
- u32 readcmd, writecmd, status = 0;
-
- memset(&brq, 0, sizeof(struct mmc_blk_request));
- brq.mrq.cmd = &brq.cmd;
- brq.mrq.data = &brq.data;
-
- brq.cmd.arg = blk_rq_pos(req);
- if (!mmc_card_blockaddr(card))
- brq.cmd.arg <<= 9;
- brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
- brq.data.blksz = 512;
- brq.stop.opcode = MMC_STOP_TRANSMISSION;
- brq.stop.arg = 0;
- brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
- brq.data.blocks = blk_rq_sectors(req);
+ /*
+ * Check for errors relating to the execution of the
+ * initial command - such as address errors. No data
+ * has been transferred.
+ */
+ if (brq->cmd.resp[0] & CMD_ERRORS) {
+ pr_err("%s: r/w command failed, status = %#x\n",
+ req->rq_disk->disk_name, brq->cmd.resp[0]);
+ return MMC_BLK_ABORT;
+ }
- /*
- * The block layer doesn't support all sector count
- * restrictions, so we need to be prepared for too big
- * requests.
- */
- if (brq.data.blocks > card->host->max_blk_count)
- brq.data.blocks = card->host->max_blk_count;
+ /*
+ * Everything else is either success, or a data error of some
+ * kind. If it was a write, we may have transitioned to
+ * program mode, which we have to wait for it to complete.
+ */
+ if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
+ u32 status;
+ do {
+ int err = get_card_status(card, &status, 5);
+ if (err) {
+ pr_err("%s: error %d requesting status\n",
+ req->rq_disk->disk_name, err);
+ return MMC_BLK_CMD_ERR;
+ }
+ /*
+ * Some cards mishandle the status bits,
+ * so make sure to check both the busy
+ * indication and the card state.
+ */
+ } while (!(status & R1_READY_FOR_DATA) ||
+ (R1_CURRENT_STATE(status) == R1_STATE_PRG));
+ }
- /*
- * After a read error, we redo the request one sector at a time
- * in order to accurately determine which sectors can be read
- * successfully.
- */
- if (disable_multi && brq.data.blocks > 1)
- brq.data.blocks = 1;
+ if (brq->data.error) {
+ pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n",
+ req->rq_disk->disk_name, brq->data.error,
+ (unsigned)blk_rq_pos(req),
+ (unsigned)blk_rq_sectors(req),
+ brq->cmd.resp[0], brq->stop.resp[0]);
- if (brq.data.blocks > 1 || do_rel_wr) {
- /* SPI multiblock writes terminate using a special
- * token, not a STOP_TRANSMISSION request.
- */
- if (!mmc_host_is_spi(card->host) ||
- rq_data_dir(req) == READ)
- brq.mrq.stop = &brq.stop;
- readcmd = MMC_READ_MULTIPLE_BLOCK;
- writecmd = MMC_WRITE_MULTIPLE_BLOCK;
- } else {
- brq.mrq.stop = NULL;
- readcmd = MMC_READ_SINGLE_BLOCK;
- writecmd = MMC_WRITE_BLOCK;
- }
if (rq_data_dir(req) == READ) {
- brq.cmd.opcode = readcmd;
- brq.data.flags |= MMC_DATA_READ;
+ if (ecc_err)
+ return MMC_BLK_ECC_ERR;
+ return MMC_BLK_DATA_ERR;
} else {
- brq.cmd.opcode = writecmd;
- brq.data.flags |= MMC_DATA_WRITE;
+ return MMC_BLK_CMD_ERR;
}
+ }
- if (do_rel_wr)
- mmc_apply_rel_rw(&brq, card, req);
-
- /*
- * Pre-defined multi-block transfers are preferable to
- * open ended-ones (and necessary for reliable writes).
- * However, it is not sufficient to just send CMD23,
- * and avoid the final CMD12, as on an error condition
- * CMD12 (stop) needs to be sent anyway. This, coupled
- * with Auto-CMD23 enhancements provided by some
- * hosts, means that the complexity of dealing
- * with this is best left to the host. If CMD23 is
- * supported by card and host, we'll fill sbc in and let
- * the host deal with handling it correctly. This means
- * that for hosts that don't expose MMC_CAP_CMD23, no
- * change of behavior will be observed.
- *
- * N.B: Some MMC cards experience perf degradation.
- * We'll avoid using CMD23-bounded multiblock writes for
- * these, while retaining features like reliable writes.
- */
+ if (!brq->data.bytes_xfered)
+ return MMC_BLK_RETRY;
- if ((md->flags & MMC_BLK_CMD23) &&
- mmc_op_multi(brq.cmd.opcode) &&
- (do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23))) {
- brq.sbc.opcode = MMC_SET_BLOCK_COUNT;
- brq.sbc.arg = brq.data.blocks |
- (do_rel_wr ? (1 << 31) : 0);
- brq.sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
- brq.mrq.sbc = &brq.sbc;
- }
+ if (blk_rq_bytes(req) != brq->data.bytes_xfered)
+ return MMC_BLK_PARTIAL;
- mmc_set_data_timeout(&brq.data, card);
+ return MMC_BLK_SUCCESS;
+}
- brq.data.sg = mq->sg;
- brq.data.sg_len = mmc_queue_map_sg(mq);
+static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
+ struct mmc_card *card,
+ int disable_multi,
+ struct mmc_queue *mq)
+{
+ u32 readcmd, writecmd;
+ struct mmc_blk_request *brq = &mqrq->brq;
+ struct request *req = mqrq->req;
+ struct mmc_blk_data *md = mq->data;
- /*
- * Adjust the sg list so it is the same size as the
- * request.
- */
- if (brq.data.blocks != blk_rq_sectors(req)) {
- int i, data_size = brq.data.blocks << 9;
- struct scatterlist *sg;
-
- for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) {
- data_size -= sg->length;
- if (data_size <= 0) {
- sg->length += data_size;
- i++;
- break;
- }
- }
- brq.data.sg_len = i;
- }
+ /*
+ * Reliable writes are used to implement Forced Unit Access and
+ * REQ_META accesses, and are supported only on MMCs.
+ *
+ * XXX: this really needs a good explanation of why REQ_META
+ * is treated special.
+ */
+ bool do_rel_wr = ((req->cmd_flags & REQ_FUA) ||
+ (req->cmd_flags & REQ_META)) &&
+ (rq_data_dir(req) == WRITE) &&
+ (md->flags & MMC_BLK_REL_WR);
- mmc_queue_bounce_pre(mq);
+ memset(brq, 0, sizeof(struct mmc_blk_request));
+ brq->mrq.cmd = &brq->cmd;
+ brq->mrq.data = &brq->data;
- mmc_wait_for_req(card->host, &brq.mrq);
+ brq->cmd.arg = blk_rq_pos(req);
+ if (!mmc_card_blockaddr(card))
+ brq->cmd.arg <<= 9;
+ brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ brq->data.blksz = 512;
+ brq->stop.opcode = MMC_STOP_TRANSMISSION;
+ brq->stop.arg = 0;
+ brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ brq->data.blocks = blk_rq_sectors(req);
- mmc_queue_bounce_post(mq);
+ /*
+ * The block layer doesn't support all sector count
+ * restrictions, so we need to be prepared for too big
+ * requests.
+ */
+ if (brq->data.blocks > card->host->max_blk_count)
+ brq->data.blocks = card->host->max_blk_count;
+ if (brq->data.blocks > 1) {
/*
- * Check for errors here, but don't jump to cmd_err
- * until later as we need to wait for the card to leave
- * programming mode even when things go wrong.
+ * After a read error, we redo the request one sector
+ * at a time in order to accurately determine which
+ * sectors can be read successfully.
*/
- if (brq.sbc.error || brq.cmd.error ||
- brq.data.error || brq.stop.error) {
- if (brq.data.blocks > 1 && rq_data_dir(req) == READ) {
- /* Redo read one sector at a time */
- printk(KERN_WARNING "%s: retrying using single "
- "block read\n", req->rq_disk->disk_name);
- disable_multi = 1;
- continue;
- }
- status = get_card_status(card, req);
- }
+ if (disable_multi)
+ brq->data.blocks = 1;
- if (brq.sbc.error) {
- printk(KERN_ERR "%s: error %d sending SET_BLOCK_COUNT "
- "command, response %#x, card status %#x\n",
- req->rq_disk->disk_name, brq.sbc.error,
- brq.sbc.resp[0], status);
- }
+ /* Some controllers can't do multiblock reads due to hw bugs */
+ if (card->host->caps2 & MMC_CAP2_NO_MULTI_READ &&
+ rq_data_dir(req) == READ)
+ brq->data.blocks = 1;
+ }
- if (brq.cmd.error) {
- printk(KERN_ERR "%s: error %d sending read/write "
- "command, response %#x, card status %#x\n",
- req->rq_disk->disk_name, brq.cmd.error,
- brq.cmd.resp[0], status);
- }
+ if (brq->data.blocks > 1 || do_rel_wr) {
+ /* SPI multiblock writes terminate using a special
+ * token, not a STOP_TRANSMISSION request.
+ */
+ if (!mmc_host_is_spi(card->host) ||
+ rq_data_dir(req) == READ)
+ brq->mrq.stop = &brq->stop;
+ readcmd = MMC_READ_MULTIPLE_BLOCK;
+ writecmd = MMC_WRITE_MULTIPLE_BLOCK;
+ } else {
+ brq->mrq.stop = NULL;
+ readcmd = MMC_READ_SINGLE_BLOCK;
+ writecmd = MMC_WRITE_BLOCK;
+ }
+ if (rq_data_dir(req) == READ) {
+ brq->cmd.opcode = readcmd;
+ brq->data.flags |= MMC_DATA_READ;
+ } else {
+ brq->cmd.opcode = writecmd;
+ brq->data.flags |= MMC_DATA_WRITE;
+ }
- if (brq.data.error) {
- if (brq.data.error == -ETIMEDOUT && brq.mrq.stop)
- /* 'Stop' response contains card status */
- status = brq.mrq.stop->resp[0];
- printk(KERN_ERR "%s: error %d transferring data,"
- " sector %u, nr %u, card status %#x\n",
- req->rq_disk->disk_name, brq.data.error,
- (unsigned)blk_rq_pos(req),
- (unsigned)blk_rq_sectors(req), status);
- }
+ if (do_rel_wr)
+ mmc_apply_rel_rw(brq, card, req);
- if (brq.stop.error) {
- printk(KERN_ERR "%s: error %d sending stop command, "
- "response %#x, card status %#x\n",
- req->rq_disk->disk_name, brq.stop.error,
- brq.stop.resp[0], status);
- }
+ /*
+ * Pre-defined multi-block transfers are preferable to
+ * open ended-ones (and necessary for reliable writes).
+ * However, it is not sufficient to just send CMD23,
+ * and avoid the final CMD12, as on an error condition
+ * CMD12 (stop) needs to be sent anyway. This, coupled
+ * with Auto-CMD23 enhancements provided by some
+ * hosts, means that the complexity of dealing
+ * with this is best left to the host. If CMD23 is
+ * supported by card and host, we'll fill sbc in and let
+ * the host deal with handling it correctly. This means
+ * that for hosts that don't expose MMC_CAP_CMD23, no
+ * change of behavior will be observed.
+ *
+ * N.B: Some MMC cards experience perf degradation.
+ * We'll avoid using CMD23-bounded multiblock writes for
+ * these, while retaining features like reliable writes.
+ */
- if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
- do {
- int err;
-
- cmd.opcode = MMC_SEND_STATUS;
- cmd.arg = card->rca << 16;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
- err = mmc_wait_for_cmd(card->host, &cmd, 5);
- if (err) {
- printk(KERN_ERR "%s: error %d requesting status\n",
- req->rq_disk->disk_name, err);
- goto cmd_err;
- }
- /*
- * Some cards mishandle the status bits,
- * so make sure to check both the busy
- * indication and the card state.
- */
- } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
- (R1_CURRENT_STATE(cmd.resp[0]) == 7));
-
-#if 0
- if (cmd.resp[0] & ~0x00000900)
- printk(KERN_ERR "%s: status = %08x\n",
- req->rq_disk->disk_name, cmd.resp[0]);
- if (mmc_decode_status(cmd.resp))
- goto cmd_err;
-#endif
- }
+ if ((md->flags & MMC_BLK_CMD23) &&
+ mmc_op_multi(brq->cmd.opcode) &&
+ (do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23))) {
+ brq->sbc.opcode = MMC_SET_BLOCK_COUNT;
+ brq->sbc.arg = brq->data.blocks |
+ (do_rel_wr ? (1 << 31) : 0);
+ brq->sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ brq->mrq.sbc = &brq->sbc;
+ }
- if (brq.cmd.error || brq.stop.error || brq.data.error) {
- if (rq_data_dir(req) == READ) {
- /*
- * After an error, we redo I/O one sector at a
- * time, so we only reach here after trying to
- * read a single sector.
- */
- spin_lock_irq(&md->lock);
- ret = __blk_end_request(req, -EIO, brq.data.blksz);
- spin_unlock_irq(&md->lock);
- continue;
+ mmc_set_data_timeout(&brq->data, card);
+
+ brq->data.sg = mqrq->sg;
+ brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
+
+ /*
+ * Adjust the sg list so it is the same size as the
+ * request.
+ */
+ if (brq->data.blocks != blk_rq_sectors(req)) {
+ int i, data_size = brq->data.blocks << 9;
+ struct scatterlist *sg;
+
+ for_each_sg(brq->data.sg, sg, brq->data.sg_len, i) {
+ data_size -= sg->length;
+ if (data_size <= 0) {
+ sg->length += data_size;
+ i++;
+ break;
}
- goto cmd_err;
}
+ brq->data.sg_len = i;
+ }
- /*
- * A block was successfully transferred.
- */
- spin_lock_irq(&md->lock);
- ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
- spin_unlock_irq(&md->lock);
- } while (ret);
+ mqrq->mmc_active.mrq = &brq->mrq;
+ mqrq->mmc_active.err_check = mmc_blk_err_check;
- return 1;
+ mmc_queue_bounce_pre(mqrq);
+}
- cmd_err:
- /*
- * If this is an SD card and we're writing, we can first
- * mark the known good sectors as ok.
- *
+static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
+ struct mmc_blk_request *brq, struct request *req,
+ int ret)
+{
+ /*
+ * If this is an SD card and we're writing, we can first
+ * mark the known good sectors as ok.
+ *
* If the card is not SD, we can still ok written sectors
* as reported by the controller (which might be less than
* the real number of written sectors, but never more).
@@ -927,15 +1152,135 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
}
} else {
spin_lock_irq(&md->lock);
- ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
+ ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
spin_unlock_irq(&md->lock);
}
+ return ret;
+}
+static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
+ int ret = 1, disable_multi = 0, retry = 0, type;
+ enum mmc_blk_status status;
+ struct mmc_queue_req *mq_rq;
+ struct request *req;
+ struct mmc_async_req *areq;
+
+ if (!rqc && !mq->mqrq_prev->req)
+ return 0;
+
+ do {
+ if (rqc) {
+ mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
+ areq = &mq->mqrq_cur->mmc_active;
+ } else
+ areq = NULL;
+ areq = mmc_start_req(card->host, areq, (int *) &status);
+ if (!areq)
+ return 0;
+
+ mq_rq = container_of(areq, struct mmc_queue_req, mmc_active);
+ brq = &mq_rq->brq;
+ req = mq_rq->req;
+ type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
+ mmc_queue_bounce_post(mq_rq);
+
+ switch (status) {
+ case MMC_BLK_SUCCESS:
+ case MMC_BLK_PARTIAL:
+ /*
+ * A block was successfully transferred.
+ */
+ mmc_blk_reset_success(md, type);
+ spin_lock_irq(&md->lock);
+ ret = __blk_end_request(req, 0,
+ brq->data.bytes_xfered);
+ spin_unlock_irq(&md->lock);
+ /*
+ * If the blk_end_request function returns non-zero even
+ * though all data has been transferred and no errors
+ * were returned by the host controller, it's a bug.
+ */
+ if (status == MMC_BLK_SUCCESS && ret) {
+ pr_err("%s BUG rq_tot %d d_xfer %d\n",
+ __func__, blk_rq_bytes(req),
+ brq->data.bytes_xfered);
+ rqc = NULL;
+ goto cmd_abort;
+ }
+ break;
+ case MMC_BLK_CMD_ERR:
+ ret = mmc_blk_cmd_err(md, card, brq, req, ret);
+ if (!mmc_blk_reset(md, card->host, type))
+ break;
+ goto cmd_abort;
+ case MMC_BLK_RETRY:
+ if (retry++ < 5)
+ break;
+ /* Fall through */
+ case MMC_BLK_ABORT:
+ if (!mmc_blk_reset(md, card->host, type))
+ break;
+ goto cmd_abort;
+ case MMC_BLK_DATA_ERR: {
+ int err;
+
+ err = mmc_blk_reset(md, card->host, type);
+ if (!err)
+ break;
+ if (err == -ENODEV)
+ goto cmd_abort;
+ /* Fall through */
+ }
+ case MMC_BLK_ECC_ERR:
+ if (brq->data.blocks > 1) {
+ /* Redo read one sector at a time */
+ pr_warning("%s: retrying using single block read\n",
+ req->rq_disk->disk_name);
+ disable_multi = 1;
+ break;
+ }
+ /*
+ * After an error, we redo I/O one sector at a
+ * time, so we only reach here after trying to
+ * read a single sector.
+ */
+ spin_lock_irq(&md->lock);
+ ret = __blk_end_request(req, -EIO,
+ brq->data.blksz);
+ spin_unlock_irq(&md->lock);
+ if (!ret)
+ goto start_new_req;
+ break;
+ }
+
+ if (ret) {
+ /*
+ * In case of a incomplete request
+ * prepare it again and resend.
+ */
+ mmc_blk_rw_rq_prep(mq_rq, card, disable_multi, mq);
+ mmc_start_req(card->host, &mq_rq->mmc_active, NULL);
+ }
+ } while (ret);
+
+ return 1;
+
+ cmd_abort:
spin_lock_irq(&md->lock);
while (ret)
ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
spin_unlock_irq(&md->lock);
+ start_new_req:
+ if (rqc) {
+ mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
+ mmc_start_req(card->host, &mq->mqrq_cur->mmc_active, NULL);
+ }
+
return 0;
}
@@ -945,26 +1290,42 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
- mmc_claim_host(card->host);
+ if (req && !mq->mqrq_prev->req)
+ /* claim host only for the first request */
+ mmc_claim_host(card->host);
+
ret = mmc_blk_part_switch(card, md);
if (ret) {
+ if (req) {
+ spin_lock_irq(&md->lock);
+ __blk_end_request_all(req, -EIO);
+ spin_unlock_irq(&md->lock);
+ }
ret = 0;
goto out;
}
- if (req->cmd_flags & REQ_DISCARD) {
+ if (req && req->cmd_flags & REQ_DISCARD) {
+ /* complete ongoing async transfer before issuing discard */
+ if (card->host->areq)
+ mmc_blk_issue_rw_rq(mq, NULL);
if (req->cmd_flags & REQ_SECURE)
ret = mmc_blk_issue_secdiscard_rq(mq, req);
else
ret = mmc_blk_issue_discard_rq(mq, req);
- } else if (req->cmd_flags & REQ_FLUSH) {
+ } else if (req && req->cmd_flags & REQ_FLUSH) {
+ /* complete ongoing async transfer before issuing flush */
+ if (card->host->areq)
+ mmc_blk_issue_rw_rq(mq, NULL);
ret = mmc_blk_issue_flush(mq, req);
} else {
ret = mmc_blk_issue_rw_rq(mq, req);
}
out:
- mmc_release_host(card->host);
+ if (!req)
+ /* release host only when there are no more requests */
+ mmc_release_host(card->host);
return ret;
}
@@ -1124,32 +1485,35 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
string_get_size((u64)get_capacity(part_md->disk) << 9, STRING_UNITS_2,
cap_str, sizeof(cap_str));
- printk(KERN_INFO "%s: %s %s partition %u %s\n",
+ pr_info("%s: %s %s partition %u %s\n",
part_md->disk->disk_name, mmc_card_id(card),
mmc_card_name(card), part_md->part_type, cap_str);
return 0;
}
+/* MMC Physical partitions consist of two boot partitions and
+ * up to four general purpose partitions.
+ * For each partition enabled in EXT_CSD a block device will be allocatedi
+ * to provide access to the partition.
+ */
+
static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
{
- int ret = 0;
+ int idx, ret = 0;
if (!mmc_card_mmc(card))
return 0;
- if (card->ext_csd.boot_size) {
- ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT0,
- card->ext_csd.boot_size >> 9,
- true,
- "boot0");
- if (ret)
- return ret;
- ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT1,
- card->ext_csd.boot_size >> 9,
- true,
- "boot1");
- if (ret)
- return ret;
+ for (idx = 0; idx < card->nr_parts; idx++) {
+ if (card->part[idx].size) {
+ ret = mmc_blk_alloc_part(card, md,
+ card->part[idx].part_cfg,
+ card->part[idx].size >> 9,
+ card->part[idx].force_ro,
+ card->part[idx].name);
+ if (ret)
+ return ret;
+ }
}
return ret;
@@ -1165,7 +1529,7 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
mmc_release_host(card->host);
if (err) {
- printk(KERN_ERR "%s: unable to set block size to 512: %d\n",
+ pr_err("%s: unable to set block size to 512: %d\n",
md->disk->disk_name, err);
return -EINVAL;
}
@@ -1267,7 +1631,7 @@ static int mmc_blk_probe(struct mmc_card *card)
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",
+ pr_info("%s: %s %s %s %s\n",
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
cap_str, md->read_only ? "(ro)" : "");
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 233cdfae92f4..b038c4a9468b 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -22,6 +22,7 @@
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/seq_file.h>
+#include <linux/module.h>
#define RESULT_OK 0
#define RESULT_FAIL 1
@@ -148,6 +149,27 @@ struct mmc_test_card {
struct mmc_test_general_result *gr;
};
+enum mmc_test_prep_media {
+ MMC_TEST_PREP_NONE = 0,
+ MMC_TEST_PREP_WRITE_FULL = 1 << 0,
+ MMC_TEST_PREP_ERASE = 1 << 1,
+};
+
+struct mmc_test_multiple_rw {
+ unsigned int *sg_len;
+ unsigned int *bs;
+ unsigned int len;
+ unsigned int size;
+ bool do_write;
+ bool do_nonblock_req;
+ enum mmc_test_prep_media prepare;
+};
+
+struct mmc_test_async_req {
+ struct mmc_async_req areq;
+ struct mmc_test_card *test;
+};
+
/*******************************************************************/
/* General helper functions */
/*******************************************************************/
@@ -203,7 +225,7 @@ static void mmc_test_prepare_mrq(struct mmc_test_card *test,
static int mmc_test_busy(struct mmc_command *cmd)
{
return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
- (R1_CURRENT_STATE(cmd->resp[0]) == 7);
+ (R1_CURRENT_STATE(cmd->resp[0]) == R1_STATE_PRG);
}
/*
@@ -229,7 +251,7 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
if (!busy && mmc_test_busy(&cmd)) {
busy = 1;
if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
- printk(KERN_INFO "%s: Warning: Host did not "
+ pr_info("%s: Warning: Host did not "
"wait for busy state to end.\n",
mmc_hostname(test->card->host));
}
@@ -367,21 +389,26 @@ out_free:
* Map memory into a scatterlist. Optionally allow the same memory to be
* mapped more than once.
*/
-static int mmc_test_map_sg(struct mmc_test_mem *mem, unsigned long sz,
+static int mmc_test_map_sg(struct mmc_test_mem *mem, unsigned long size,
struct scatterlist *sglist, int repeat,
unsigned int max_segs, unsigned int max_seg_sz,
- unsigned int *sg_len)
+ unsigned int *sg_len, int min_sg_len)
{
struct scatterlist *sg = NULL;
unsigned int i;
+ unsigned long sz = size;
sg_init_table(sglist, max_segs);
+ if (min_sg_len > max_segs)
+ min_sg_len = max_segs;
*sg_len = 0;
do {
for (i = 0; i < mem->cnt; i++) {
unsigned long len = PAGE_SIZE << mem->arr[i].order;
+ if (min_sg_len && (size / min_sg_len < len))
+ len = ALIGN(size / min_sg_len, 512);
if (len > sz)
len = sz;
if (len > max_seg_sz)
@@ -526,7 +553,7 @@ static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
rate = mmc_test_rate(bytes, &ts);
iops = mmc_test_rate(100, &ts); /* I/O ops per sec x 100 */
- printk(KERN_INFO "%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu "
+ pr_info("%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu "
"seconds (%u kB/s, %u KiB/s, %u.%02u IOPS)\n",
mmc_hostname(test->card->host), sectors, sectors >> 1,
(sectors & 1 ? ".5" : ""), (unsigned long)ts.tv_sec,
@@ -552,13 +579,14 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
rate = mmc_test_rate(tot, &ts);
iops = mmc_test_rate(count * 100, &ts); /* I/O ops per sec x 100 */
- printk(KERN_INFO "%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
+ pr_info("%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
"%lu.%09lu seconds (%u kB/s, %u KiB/s, "
- "%u.%02u IOPS)\n",
+ "%u.%02u IOPS, sg_len %d)\n",
mmc_hostname(test->card->host), count, sectors, count,
sectors >> 1, (sectors & 1 ? ".5" : ""),
(unsigned long)ts.tv_sec, (unsigned long)ts.tv_nsec,
- rate / 1000, rate / 1024, iops / 100, iops % 100);
+ rate / 1000, rate / 1024, iops / 100, iops % 100,
+ test->area.sg_len);
mmc_test_save_transfer_result(test, count, sectors, ts, rate, iops);
}
@@ -661,7 +689,7 @@ static void mmc_test_prepare_broken_mrq(struct mmc_test_card *test,
* Checks that a normal transfer didn't have any errors
*/
static int mmc_test_check_result(struct mmc_test_card *test,
- struct mmc_request *mrq)
+ struct mmc_request *mrq)
{
int ret;
@@ -685,6 +713,17 @@ static int mmc_test_check_result(struct mmc_test_card *test,
return ret;
}
+static int mmc_test_check_result_async(struct mmc_card *card,
+ struct mmc_async_req *areq)
+{
+ struct mmc_test_async_req *test_async =
+ container_of(areq, struct mmc_test_async_req, areq);
+
+ mmc_test_wait_busy(test_async->test);
+
+ return mmc_test_check_result(test_async->test, areq->mrq);
+}
+
/*
* Checks that a "short transfer" behaved as expected
*/
@@ -720,6 +759,85 @@ static int mmc_test_check_broken_result(struct mmc_test_card *test,
}
/*
+ * Tests nonblock transfer with certain parameters
+ */
+static void mmc_test_nonblock_reset(struct mmc_request *mrq,
+ struct mmc_command *cmd,
+ struct mmc_command *stop,
+ struct mmc_data *data)
+{
+ memset(mrq, 0, sizeof(struct mmc_request));
+ memset(cmd, 0, sizeof(struct mmc_command));
+ memset(data, 0, sizeof(struct mmc_data));
+ memset(stop, 0, sizeof(struct mmc_command));
+
+ mrq->cmd = cmd;
+ mrq->data = data;
+ mrq->stop = stop;
+}
+static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
+ struct scatterlist *sg, unsigned sg_len,
+ unsigned dev_addr, unsigned blocks,
+ unsigned blksz, int write, int count)
+{
+ struct mmc_request mrq1;
+ struct mmc_command cmd1;
+ struct mmc_command stop1;
+ struct mmc_data data1;
+
+ struct mmc_request mrq2;
+ struct mmc_command cmd2;
+ struct mmc_command stop2;
+ struct mmc_data data2;
+
+ struct mmc_test_async_req test_areq[2];
+ struct mmc_async_req *done_areq;
+ struct mmc_async_req *cur_areq = &test_areq[0].areq;
+ struct mmc_async_req *other_areq = &test_areq[1].areq;
+ int i;
+ int ret;
+
+ test_areq[0].test = test;
+ test_areq[1].test = test;
+
+ mmc_test_nonblock_reset(&mrq1, &cmd1, &stop1, &data1);
+ mmc_test_nonblock_reset(&mrq2, &cmd2, &stop2, &data2);
+
+ cur_areq->mrq = &mrq1;
+ cur_areq->err_check = mmc_test_check_result_async;
+ other_areq->mrq = &mrq2;
+ other_areq->err_check = mmc_test_check_result_async;
+
+ for (i = 0; i < count; i++) {
+ mmc_test_prepare_mrq(test, cur_areq->mrq, sg, sg_len, dev_addr,
+ blocks, blksz, write);
+ done_areq = mmc_start_req(test->card->host, cur_areq, &ret);
+
+ if (ret || (!done_areq && i > 0))
+ goto err;
+
+ if (done_areq) {
+ if (done_areq->mrq == &mrq2)
+ mmc_test_nonblock_reset(&mrq2, &cmd2,
+ &stop2, &data2);
+ else
+ mmc_test_nonblock_reset(&mrq1, &cmd1,
+ &stop1, &data1);
+ }
+ done_areq = cur_areq;
+ cur_areq = other_areq;
+ other_areq = done_areq;
+ dev_addr += blocks;
+ }
+
+ done_areq = mmc_start_req(test->card->host, NULL, &ret);
+
+ return ret;
+err:
+ return ret;
+}
+
+/*
* Tests a basic transfer with certain parameters
*/
static int mmc_test_simple_transfer(struct mmc_test_card *test,
@@ -1291,7 +1409,7 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test)
static int mmc_test_no_highmem(struct mmc_test_card *test)
{
- printk(KERN_INFO "%s: Highmem not configured - test skipped\n",
+ pr_info("%s: Highmem not configured - test skipped\n",
mmc_hostname(test->card->host));
return 0;
}
@@ -1302,7 +1420,7 @@ static int mmc_test_no_highmem(struct mmc_test_card *test)
* Map sz bytes so that it can be transferred.
*/
static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz,
- int max_scatter)
+ int max_scatter, int min_sg_len)
{
struct mmc_test_area *t = &test->area;
int err;
@@ -1315,10 +1433,10 @@ static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz,
&t->sg_len);
} else {
err = mmc_test_map_sg(t->mem, sz, t->sg, 1, t->max_segs,
- t->max_seg_sz, &t->sg_len);
+ t->max_seg_sz, &t->sg_len, min_sg_len);
}
if (err)
- printk(KERN_INFO "%s: Failed to map sg list\n",
+ pr_info("%s: Failed to map sg list\n",
mmc_hostname(test->card->host));
return err;
}
@@ -1336,14 +1454,17 @@ static int mmc_test_area_transfer(struct mmc_test_card *test,
}
/*
- * Map and transfer bytes.
+ * Map and transfer bytes for multiple transfers.
*/
-static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
- unsigned int dev_addr, int write, int max_scatter,
- int timed)
+static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz,
+ unsigned int dev_addr, int write,
+ int max_scatter, int timed, int count,
+ bool nonblock, int min_sg_len)
{
struct timespec ts1, ts2;
- int ret;
+ int ret = 0;
+ int i;
+ struct mmc_test_area *t = &test->area;
/*
* In the case of a maximally scattered transfer, the maximum transfer
@@ -1361,14 +1482,21 @@ static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
sz = max_tfr;
}
- ret = mmc_test_area_map(test, sz, max_scatter);
+ ret = mmc_test_area_map(test, sz, max_scatter, min_sg_len);
if (ret)
return ret;
if (timed)
getnstimeofday(&ts1);
+ if (nonblock)
+ ret = mmc_test_nonblock_transfer(test, t->sg, t->sg_len,
+ dev_addr, t->blocks, 512, write, count);
+ else
+ for (i = 0; i < count && ret == 0; i++) {
+ ret = mmc_test_area_transfer(test, dev_addr, write);
+ dev_addr += sz >> 9;
+ }
- ret = mmc_test_area_transfer(test, dev_addr, write);
if (ret)
return ret;
@@ -1376,11 +1504,19 @@ static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
getnstimeofday(&ts2);
if (timed)
- mmc_test_print_rate(test, sz, &ts1, &ts2);
+ mmc_test_print_avg_rate(test, sz, count, &ts1, &ts2);
return 0;
}
+static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
+ unsigned int dev_addr, int write, int max_scatter,
+ int timed)
+{
+ return mmc_test_area_io_seq(test, sz, dev_addr, write, max_scatter,
+ timed, 1, false, 0);
+}
+
/*
* Write the test area entirely.
*/
@@ -1954,6 +2090,270 @@ static int mmc_test_large_seq_write_perf(struct mmc_test_card *test)
return mmc_test_large_seq_perf(test, 1);
}
+static int mmc_test_rw_multiple(struct mmc_test_card *test,
+ struct mmc_test_multiple_rw *tdata,
+ unsigned int reqsize, unsigned int size,
+ int min_sg_len)
+{
+ unsigned int dev_addr;
+ struct mmc_test_area *t = &test->area;
+ int ret = 0;
+
+ /* Set up test area */
+ if (size > mmc_test_capacity(test->card) / 2 * 512)
+ size = mmc_test_capacity(test->card) / 2 * 512;
+ if (reqsize > t->max_tfr)
+ reqsize = t->max_tfr;
+ dev_addr = mmc_test_capacity(test->card) / 4;
+ if ((dev_addr & 0xffff0000))
+ dev_addr &= 0xffff0000; /* Round to 64MiB boundary */
+ else
+ dev_addr &= 0xfffff800; /* Round to 1MiB boundary */
+ if (!dev_addr)
+ goto err;
+
+ if (reqsize > size)
+ return 0;
+
+ /* prepare test area */
+ if (mmc_can_erase(test->card) &&
+ tdata->prepare & MMC_TEST_PREP_ERASE) {
+ ret = mmc_erase(test->card, dev_addr,
+ size / 512, MMC_SECURE_ERASE_ARG);
+ if (ret)
+ ret = mmc_erase(test->card, dev_addr,
+ size / 512, MMC_ERASE_ARG);
+ if (ret)
+ goto err;
+ }
+
+ /* Run test */
+ ret = mmc_test_area_io_seq(test, reqsize, dev_addr,
+ tdata->do_write, 0, 1, size / reqsize,
+ tdata->do_nonblock_req, min_sg_len);
+ if (ret)
+ goto err;
+
+ return ret;
+ err:
+ pr_info("[%s] error\n", __func__);
+ return ret;
+}
+
+static int mmc_test_rw_multiple_size(struct mmc_test_card *test,
+ struct mmc_test_multiple_rw *rw)
+{
+ int ret = 0;
+ int i;
+ void *pre_req = test->card->host->ops->pre_req;
+ void *post_req = test->card->host->ops->post_req;
+
+ if (rw->do_nonblock_req &&
+ ((!pre_req && post_req) || (pre_req && !post_req))) {
+ pr_info("error: only one of pre/post is defined\n");
+ return -EINVAL;
+ }
+
+ for (i = 0 ; i < rw->len && ret == 0; i++) {
+ ret = mmc_test_rw_multiple(test, rw, rw->bs[i], rw->size, 0);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test,
+ struct mmc_test_multiple_rw *rw)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0 ; i < rw->len && ret == 0; i++) {
+ ret = mmc_test_rw_multiple(test, rw, 512*1024, rw->size,
+ rw->sg_len[i]);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Multiple blocking write 4k to 4 MB chunks
+ */
+static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test)
+{
+ unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
+ 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
+ struct mmc_test_multiple_rw test_data = {
+ .bs = bs,
+ .size = TEST_AREA_MAX_SIZE,
+ .len = ARRAY_SIZE(bs),
+ .do_write = true,
+ .do_nonblock_req = false,
+ .prepare = MMC_TEST_PREP_ERASE,
+ };
+
+ return mmc_test_rw_multiple_size(test, &test_data);
+};
+
+/*
+ * Multiple non-blocking write 4k to 4 MB chunks
+ */
+static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test)
+{
+ unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
+ 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
+ struct mmc_test_multiple_rw test_data = {
+ .bs = bs,
+ .size = TEST_AREA_MAX_SIZE,
+ .len = ARRAY_SIZE(bs),
+ .do_write = true,
+ .do_nonblock_req = true,
+ .prepare = MMC_TEST_PREP_ERASE,
+ };
+
+ return mmc_test_rw_multiple_size(test, &test_data);
+}
+
+/*
+ * Multiple blocking read 4k to 4 MB chunks
+ */
+static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test)
+{
+ unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
+ 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
+ struct mmc_test_multiple_rw test_data = {
+ .bs = bs,
+ .size = TEST_AREA_MAX_SIZE,
+ .len = ARRAY_SIZE(bs),
+ .do_write = false,
+ .do_nonblock_req = false,
+ .prepare = MMC_TEST_PREP_NONE,
+ };
+
+ return mmc_test_rw_multiple_size(test, &test_data);
+}
+
+/*
+ * Multiple non-blocking read 4k to 4 MB chunks
+ */
+static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test)
+{
+ unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
+ 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
+ struct mmc_test_multiple_rw test_data = {
+ .bs = bs,
+ .size = TEST_AREA_MAX_SIZE,
+ .len = ARRAY_SIZE(bs),
+ .do_write = false,
+ .do_nonblock_req = true,
+ .prepare = MMC_TEST_PREP_NONE,
+ };
+
+ return mmc_test_rw_multiple_size(test, &test_data);
+}
+
+/*
+ * Multiple blocking write 1 to 512 sg elements
+ */
+static int mmc_test_profile_sglen_wr_blocking_perf(struct mmc_test_card *test)
+{
+ unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
+ 1 << 7, 1 << 8, 1 << 9};
+ struct mmc_test_multiple_rw test_data = {
+ .sg_len = sg_len,
+ .size = TEST_AREA_MAX_SIZE,
+ .len = ARRAY_SIZE(sg_len),
+ .do_write = true,
+ .do_nonblock_req = false,
+ .prepare = MMC_TEST_PREP_ERASE,
+ };
+
+ return mmc_test_rw_multiple_sg_len(test, &test_data);
+};
+
+/*
+ * Multiple non-blocking write 1 to 512 sg elements
+ */
+static int mmc_test_profile_sglen_wr_nonblock_perf(struct mmc_test_card *test)
+{
+ unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
+ 1 << 7, 1 << 8, 1 << 9};
+ struct mmc_test_multiple_rw test_data = {
+ .sg_len = sg_len,
+ .size = TEST_AREA_MAX_SIZE,
+ .len = ARRAY_SIZE(sg_len),
+ .do_write = true,
+ .do_nonblock_req = true,
+ .prepare = MMC_TEST_PREP_ERASE,
+ };
+
+ return mmc_test_rw_multiple_sg_len(test, &test_data);
+}
+
+/*
+ * Multiple blocking read 1 to 512 sg elements
+ */
+static int mmc_test_profile_sglen_r_blocking_perf(struct mmc_test_card *test)
+{
+ unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
+ 1 << 7, 1 << 8, 1 << 9};
+ struct mmc_test_multiple_rw test_data = {
+ .sg_len = sg_len,
+ .size = TEST_AREA_MAX_SIZE,
+ .len = ARRAY_SIZE(sg_len),
+ .do_write = false,
+ .do_nonblock_req = false,
+ .prepare = MMC_TEST_PREP_NONE,
+ };
+
+ return mmc_test_rw_multiple_sg_len(test, &test_data);
+}
+
+/*
+ * Multiple non-blocking read 1 to 512 sg elements
+ */
+static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test)
+{
+ unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
+ 1 << 7, 1 << 8, 1 << 9};
+ struct mmc_test_multiple_rw test_data = {
+ .sg_len = sg_len,
+ .size = TEST_AREA_MAX_SIZE,
+ .len = ARRAY_SIZE(sg_len),
+ .do_write = false,
+ .do_nonblock_req = true,
+ .prepare = MMC_TEST_PREP_NONE,
+ };
+
+ return mmc_test_rw_multiple_sg_len(test, &test_data);
+}
+
+/*
+ * eMMC hardware reset.
+ */
+static int mmc_test_hw_reset(struct mmc_test_card *test)
+{
+ struct mmc_card *card = test->card;
+ struct mmc_host *host = card->host;
+ int err;
+
+ err = mmc_hw_reset_check(host);
+ if (!err)
+ return RESULT_OK;
+
+ if (err == -ENOSYS)
+ return RESULT_FAIL;
+
+ if (err != -EOPNOTSUPP)
+ return err;
+
+ if (!mmc_can_reset(card))
+ return RESULT_UNSUP_CARD;
+
+ return RESULT_UNSUP_HOST;
+}
+
static const struct mmc_test_case mmc_test_cases[] = {
{
.name = "Basic write (no data verification)",
@@ -2221,6 +2621,66 @@ static const struct mmc_test_case mmc_test_cases[] = {
.cleanup = mmc_test_area_cleanup,
},
+ {
+ .name = "Write performance with blocking req 4k to 4MB",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_mult_write_blocking_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Write performance with non-blocking req 4k to 4MB",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_mult_write_nonblock_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Read performance with blocking req 4k to 4MB",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_mult_read_blocking_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Read performance with non-blocking req 4k to 4MB",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_mult_read_nonblock_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Write performance blocking req 1 to 512 sg elems",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_sglen_wr_blocking_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Write performance non-blocking req 1 to 512 sg elems",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_sglen_wr_nonblock_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Read performance blocking req 1 to 512 sg elems",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_sglen_r_blocking_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Read performance non-blocking req 1 to 512 sg elems",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_sglen_r_nonblock_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "eMMC hardware reset",
+ .run = mmc_test_hw_reset,
+ },
};
static DEFINE_MUTEX(mmc_test_lock);
@@ -2231,7 +2691,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
{
int i, ret;
- printk(KERN_INFO "%s: Starting tests of card %s...\n",
+ pr_info("%s: Starting tests of card %s...\n",
mmc_hostname(test->card->host), mmc_card_id(test->card));
mmc_claim_host(test->card->host);
@@ -2242,14 +2702,14 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
if (testcase && ((i + 1) != testcase))
continue;
- printk(KERN_INFO "%s: Test case %d. %s...\n",
+ pr_info("%s: Test case %d. %s...\n",
mmc_hostname(test->card->host), i + 1,
mmc_test_cases[i].name);
if (mmc_test_cases[i].prepare) {
ret = mmc_test_cases[i].prepare(test);
if (ret) {
- printk(KERN_INFO "%s: Result: Prepare "
+ pr_info("%s: Result: Prepare "
"stage failed! (%d)\n",
mmc_hostname(test->card->host),
ret);
@@ -2279,25 +2739,25 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
ret = mmc_test_cases[i].run(test);
switch (ret) {
case RESULT_OK:
- printk(KERN_INFO "%s: Result: OK\n",
+ pr_info("%s: Result: OK\n",
mmc_hostname(test->card->host));
break;
case RESULT_FAIL:
- printk(KERN_INFO "%s: Result: FAILED\n",
+ pr_info("%s: Result: FAILED\n",
mmc_hostname(test->card->host));
break;
case RESULT_UNSUP_HOST:
- printk(KERN_INFO "%s: Result: UNSUPPORTED "
+ pr_info("%s: Result: UNSUPPORTED "
"(by host)\n",
mmc_hostname(test->card->host));
break;
case RESULT_UNSUP_CARD:
- printk(KERN_INFO "%s: Result: UNSUPPORTED "
+ pr_info("%s: Result: UNSUPPORTED "
"(by card)\n",
mmc_hostname(test->card->host));
break;
default:
- printk(KERN_INFO "%s: Result: ERROR (%d)\n",
+ pr_info("%s: Result: ERROR (%d)\n",
mmc_hostname(test->card->host), ret);
}
@@ -2308,7 +2768,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
if (mmc_test_cases[i].cleanup) {
ret = mmc_test_cases[i].cleanup(test);
if (ret) {
- printk(KERN_INFO "%s: Warning: Cleanup "
+ pr_info("%s: Warning: Cleanup "
"stage failed! (%d)\n",
mmc_hostname(test->card->host),
ret);
@@ -2318,7 +2778,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
mmc_release_host(test->card->host);
- printk(KERN_INFO "%s: Tests completed.\n",
+ pr_info("%s: Tests completed.\n",
mmc_hostname(test->card->host));
}
@@ -2445,7 +2905,33 @@ static const struct file_operations mmc_test_fops_test = {
.release = single_release,
};
-static void mmc_test_free_file_test(struct mmc_card *card)
+static int mtf_testlist_show(struct seq_file *sf, void *data)
+{
+ int i;
+
+ mutex_lock(&mmc_test_lock);
+
+ for (i = 0; i < ARRAY_SIZE(mmc_test_cases); i++)
+ seq_printf(sf, "%d:\t%s\n", i+1, mmc_test_cases[i].name);
+
+ mutex_unlock(&mmc_test_lock);
+
+ return 0;
+}
+
+static int mtf_testlist_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mtf_testlist_show, inode->i_private);
+}
+
+static const struct file_operations mmc_test_fops_testlist = {
+ .open = mtf_testlist_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void mmc_test_free_dbgfs_file(struct mmc_card *card)
{
struct mmc_test_dbgfs_file *df, *dfs;
@@ -2462,23 +2948,21 @@ static void mmc_test_free_file_test(struct mmc_card *card)
mutex_unlock(&mmc_test_lock);
}
-static int mmc_test_register_file_test(struct mmc_card *card)
+static int __mmc_test_register_dbgfs_file(struct mmc_card *card,
+ const char *name, mode_t mode, const struct file_operations *fops)
{
struct dentry *file = NULL;
struct mmc_test_dbgfs_file *df;
- int ret = 0;
-
- mutex_lock(&mmc_test_lock);
if (card->debugfs_root)
- file = debugfs_create_file("test", S_IWUSR | S_IRUGO,
- card->debugfs_root, card, &mmc_test_fops_test);
+ file = debugfs_create_file(name, mode, card->debugfs_root,
+ card, fops);
if (IS_ERR_OR_NULL(file)) {
dev_err(&card->dev,
- "Can't create file. Perhaps debugfs is disabled.\n");
- ret = -ENODEV;
- goto err;
+ "Can't create %s. Perhaps debugfs is disabled.\n",
+ name);
+ return -ENODEV;
}
df = kmalloc(sizeof(struct mmc_test_dbgfs_file), GFP_KERNEL);
@@ -2486,14 +2970,31 @@ static int mmc_test_register_file_test(struct mmc_card *card)
debugfs_remove(file);
dev_err(&card->dev,
"Can't allocate memory for internal usage.\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
df->card = card;
df->file = file;
list_add(&df->link, &mmc_test_file_test);
+ return 0;
+}
+
+static int mmc_test_register_dbgfs_file(struct mmc_card *card)
+{
+ int ret;
+
+ mutex_lock(&mmc_test_lock);
+
+ ret = __mmc_test_register_dbgfs_file(card, "test", S_IWUSR | S_IRUGO,
+ &mmc_test_fops_test);
+ if (ret)
+ goto err;
+
+ ret = __mmc_test_register_dbgfs_file(card, "testlist", S_IRUGO,
+ &mmc_test_fops_testlist);
+ if (ret)
+ goto err;
err:
mutex_unlock(&mmc_test_lock);
@@ -2508,7 +3009,7 @@ static int mmc_test_probe(struct mmc_card *card)
if (!mmc_card_mmc(card) && !mmc_card_sd(card))
return -ENODEV;
- ret = mmc_test_register_file_test(card);
+ ret = mmc_test_register_dbgfs_file(card);
if (ret)
return ret;
@@ -2520,7 +3021,7 @@ static int mmc_test_probe(struct mmc_card *card)
static void mmc_test_remove(struct mmc_card *card)
{
mmc_test_free_result(card);
- mmc_test_free_file_test(card);
+ mmc_test_free_dbgfs_file(card);
}
static struct mmc_driver mmc_driver = {
@@ -2540,7 +3041,7 @@ static void __exit mmc_test_exit(void)
{
/* Clear stalled data if card is still plugged */
mmc_test_free_result(NULL);
- mmc_test_free_file_test(NULL);
+ mmc_test_free_dbgfs_file(NULL);
mmc_unregister_driver(&mmc_driver);
}
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 6413afa318d2..dcad59cbfef1 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -52,14 +52,18 @@ static int mmc_queue_thread(void *d)
down(&mq->thread_sem);
do {
struct request *req = NULL;
+ struct mmc_queue_req *tmp;
spin_lock_irq(q->queue_lock);
set_current_state(TASK_INTERRUPTIBLE);
req = blk_fetch_request(q);
- mq->req = req;
+ mq->mqrq_cur->req = req;
spin_unlock_irq(q->queue_lock);
- if (!req) {
+ if (req || mq->mqrq_prev->req) {
+ set_current_state(TASK_RUNNING);
+ mq->issue_fn(mq, req);
+ } else {
if (kthread_should_stop()) {
set_current_state(TASK_RUNNING);
break;
@@ -67,11 +71,14 @@ static int mmc_queue_thread(void *d)
up(&mq->thread_sem);
schedule();
down(&mq->thread_sem);
- continue;
}
- set_current_state(TASK_RUNNING);
- mq->issue_fn(mq, req);
+ /* Current request becomes previous request and vice versa. */
+ mq->mqrq_prev->brq.mrq.data = NULL;
+ mq->mqrq_prev->req = NULL;
+ tmp = mq->mqrq_prev;
+ mq->mqrq_prev = mq->mqrq_cur;
+ mq->mqrq_cur = tmp;
} while (1);
up(&mq->thread_sem);
@@ -97,10 +104,46 @@ static void mmc_request(struct request_queue *q)
return;
}
- if (!mq->req)
+ if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
wake_up_process(mq->thread);
}
+static struct scatterlist *mmc_alloc_sg(int sg_len, int *err)
+{
+ struct scatterlist *sg;
+
+ sg = kmalloc(sizeof(struct scatterlist)*sg_len, GFP_KERNEL);
+ if (!sg)
+ *err = -ENOMEM;
+ else {
+ *err = 0;
+ sg_init_table(sg, sg_len);
+ }
+
+ return sg;
+}
+
+static void mmc_queue_setup_discard(struct request_queue *q,
+ struct mmc_card *card)
+{
+ unsigned max_discard;
+
+ max_discard = mmc_calc_max_discard(card);
+ if (!max_discard)
+ return;
+
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ q->limits.max_discard_sectors = max_discard;
+ if (card->erased_byte == 0)
+ q->limits.discard_zeroes_data = 1;
+ q->limits.discard_granularity = card->pref_erase << 9;
+ /* granularity must not be greater than max. discard */
+ if (card->pref_erase > max_discard)
+ q->limits.discard_granularity = 0;
+ if (mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))
+ queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q);
+}
+
/**
* mmc_init_queue - initialise a queue structure.
* @mq: mmc queue
@@ -116,6 +159,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
struct mmc_host *host = card->host;
u64 limit = BLK_BOUNCE_HIGH;
int ret;
+ struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
+ struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];
if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
limit = *mmc_dev(host)->dma_mask;
@@ -125,21 +170,16 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
if (!mq->queue)
return -ENOMEM;
+ memset(&mq->mqrq_cur, 0, sizeof(mq->mqrq_cur));
+ memset(&mq->mqrq_prev, 0, sizeof(mq->mqrq_prev));
+ mq->mqrq_cur = mqrq_cur;
+ mq->mqrq_prev = mqrq_prev;
mq->queue->queuedata = mq;
- mq->req = NULL;
blk_queue_prep_rq(mq->queue, mmc_prep_request);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
- if (mmc_can_erase(card)) {
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mq->queue);
- mq->queue->limits.max_discard_sectors = UINT_MAX;
- if (card->erased_byte == 0)
- mq->queue->limits.discard_zeroes_data = 1;
- mq->queue->limits.discard_granularity = card->pref_erase << 9;
- if (mmc_can_secure_erase_trim(card))
- queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD,
- mq->queue);
- }
+ if (mmc_can_erase(card))
+ mmc_queue_setup_discard(mq->queue, card);
#ifdef CONFIG_MMC_BLOCK_BOUNCE
if (host->max_segs == 1) {
@@ -155,53 +195,64 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
bouncesz = host->max_blk_count * 512;
if (bouncesz > 512) {
- mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
- if (!mq->bounce_buf) {
- printk(KERN_WARNING "%s: unable to "
- "allocate bounce buffer\n",
+ mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
+ if (!mqrq_cur->bounce_buf) {
+ pr_warning("%s: unable to "
+ "allocate bounce cur buffer\n",
+ mmc_card_name(card));
+ }
+ mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
+ if (!mqrq_prev->bounce_buf) {
+ pr_warning("%s: unable to "
+ "allocate bounce prev buffer\n",
mmc_card_name(card));
+ kfree(mqrq_cur->bounce_buf);
+ mqrq_cur->bounce_buf = NULL;
}
}
- if (mq->bounce_buf) {
+ if (mqrq_cur->bounce_buf && mqrq_prev->bounce_buf) {
blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
blk_queue_max_hw_sectors(mq->queue, bouncesz / 512);
blk_queue_max_segments(mq->queue, bouncesz / 512);
blk_queue_max_segment_size(mq->queue, bouncesz);
- mq->sg = kmalloc(sizeof(struct scatterlist),
- GFP_KERNEL);
- if (!mq->sg) {
- ret = -ENOMEM;
+ mqrq_cur->sg = mmc_alloc_sg(1, &ret);
+ if (ret)
goto cleanup_queue;
- }
- sg_init_table(mq->sg, 1);
- mq->bounce_sg = kmalloc(sizeof(struct scatterlist) *
- bouncesz / 512, GFP_KERNEL);
- if (!mq->bounce_sg) {
- ret = -ENOMEM;
+ mqrq_cur->bounce_sg =
+ mmc_alloc_sg(bouncesz / 512, &ret);
+ if (ret)
+ goto cleanup_queue;
+
+ mqrq_prev->sg = mmc_alloc_sg(1, &ret);
+ if (ret)
+ goto cleanup_queue;
+
+ mqrq_prev->bounce_sg =
+ mmc_alloc_sg(bouncesz / 512, &ret);
+ if (ret)
goto cleanup_queue;
- }
- sg_init_table(mq->bounce_sg, bouncesz / 512);
}
}
#endif
- if (!mq->bounce_buf) {
+ if (!mqrq_cur->bounce_buf && !mqrq_prev->bounce_buf) {
blk_queue_bounce_limit(mq->queue, limit);
blk_queue_max_hw_sectors(mq->queue,
min(host->max_blk_count, host->max_req_size / 512));
blk_queue_max_segments(mq->queue, host->max_segs);
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
- mq->sg = kmalloc(sizeof(struct scatterlist) *
- host->max_segs, GFP_KERNEL);
- if (!mq->sg) {
- ret = -ENOMEM;
+ mqrq_cur->sg = mmc_alloc_sg(host->max_segs, &ret);
+ if (ret)
+ goto cleanup_queue;
+
+
+ mqrq_prev->sg = mmc_alloc_sg(host->max_segs, &ret);
+ if (ret)
goto cleanup_queue;
- }
- sg_init_table(mq->sg, host->max_segs);
}
sema_init(&mq->thread_sem, 1);
@@ -216,16 +267,22 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
return 0;
free_bounce_sg:
- if (mq->bounce_sg)
- kfree(mq->bounce_sg);
- mq->bounce_sg = NULL;
+ kfree(mqrq_cur->bounce_sg);
+ mqrq_cur->bounce_sg = NULL;
+ kfree(mqrq_prev->bounce_sg);
+ mqrq_prev->bounce_sg = NULL;
+
cleanup_queue:
- if (mq->sg)
- kfree(mq->sg);
- mq->sg = NULL;
- if (mq->bounce_buf)
- kfree(mq->bounce_buf);
- mq->bounce_buf = NULL;
+ kfree(mqrq_cur->sg);
+ mqrq_cur->sg = NULL;
+ kfree(mqrq_cur->bounce_buf);
+ mqrq_cur->bounce_buf = NULL;
+
+ kfree(mqrq_prev->sg);
+ mqrq_prev->sg = NULL;
+ kfree(mqrq_prev->bounce_buf);
+ mqrq_prev->bounce_buf = NULL;
+
blk_cleanup_queue(mq->queue);
return ret;
}
@@ -234,6 +291,8 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
{
struct request_queue *q = mq->queue;
unsigned long flags;
+ struct mmc_queue_req *mqrq_cur = mq->mqrq_cur;
+ struct mmc_queue_req *mqrq_prev = mq->mqrq_prev;
/* Make sure the queue isn't suspended, as that will deadlock */
mmc_queue_resume(mq);
@@ -247,16 +306,23 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
blk_start_queue(q);
spin_unlock_irqrestore(q->queue_lock, flags);
- if (mq->bounce_sg)
- kfree(mq->bounce_sg);
- mq->bounce_sg = NULL;
+ kfree(mqrq_cur->bounce_sg);
+ mqrq_cur->bounce_sg = NULL;
- kfree(mq->sg);
- mq->sg = NULL;
+ kfree(mqrq_cur->sg);
+ mqrq_cur->sg = NULL;
- if (mq->bounce_buf)
- kfree(mq->bounce_buf);
- mq->bounce_buf = NULL;
+ kfree(mqrq_cur->bounce_buf);
+ mqrq_cur->bounce_buf = NULL;
+
+ kfree(mqrq_prev->bounce_sg);
+ mqrq_prev->bounce_sg = NULL;
+
+ kfree(mqrq_prev->sg);
+ mqrq_prev->sg = NULL;
+
+ kfree(mqrq_prev->bounce_buf);
+ mqrq_prev->bounce_buf = NULL;
mq->card = NULL;
}
@@ -309,27 +375,27 @@ void mmc_queue_resume(struct mmc_queue *mq)
/*
* Prepare the sg list(s) to be handed of to the host driver
*/
-unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
+unsigned int mmc_queue_map_sg(struct mmc_queue *mq, struct mmc_queue_req *mqrq)
{
unsigned int sg_len;
size_t buflen;
struct scatterlist *sg;
int i;
- if (!mq->bounce_buf)
- return blk_rq_map_sg(mq->queue, mq->req, mq->sg);
+ if (!mqrq->bounce_buf)
+ return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg);
- BUG_ON(!mq->bounce_sg);
+ BUG_ON(!mqrq->bounce_sg);
- sg_len = blk_rq_map_sg(mq->queue, mq->req, mq->bounce_sg);
+ sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg);
- mq->bounce_sg_len = sg_len;
+ mqrq->bounce_sg_len = sg_len;
buflen = 0;
- for_each_sg(mq->bounce_sg, sg, sg_len, i)
+ for_each_sg(mqrq->bounce_sg, sg, sg_len, i)
buflen += sg->length;
- sg_init_one(mq->sg, mq->bounce_buf, buflen);
+ sg_init_one(mqrq->sg, mqrq->bounce_buf, buflen);
return 1;
}
@@ -338,31 +404,30 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
* If writing, bounce the data to the buffer before the request
* is sent to the host driver
*/
-void mmc_queue_bounce_pre(struct mmc_queue *mq)
+void mmc_queue_bounce_pre(struct mmc_queue_req *mqrq)
{
- if (!mq->bounce_buf)
+ if (!mqrq->bounce_buf)
return;
- if (rq_data_dir(mq->req) != WRITE)
+ if (rq_data_dir(mqrq->req) != WRITE)
return;
- sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len,
- mq->bounce_buf, mq->sg[0].length);
+ sg_copy_to_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len,
+ mqrq->bounce_buf, mqrq->sg[0].length);
}
/*
* If reading, bounce the data from the buffer after the request
* has been handled by the host driver
*/
-void mmc_queue_bounce_post(struct mmc_queue *mq)
+void mmc_queue_bounce_post(struct mmc_queue_req *mqrq)
{
- if (!mq->bounce_buf)
+ if (!mqrq->bounce_buf)
return;
- if (rq_data_dir(mq->req) != READ)
+ if (rq_data_dir(mqrq->req) != READ)
return;
- sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len,
- mq->bounce_buf, mq->sg[0].length);
+ sg_copy_from_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len,
+ mqrq->bounce_buf, mqrq->sg[0].length);
}
-
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 6223ef8dc9cd..d2a1eb4b9f9f 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -4,19 +4,35 @@
struct request;
struct task_struct;
+struct mmc_blk_request {
+ struct mmc_request mrq;
+ struct mmc_command sbc;
+ struct mmc_command cmd;
+ struct mmc_command stop;
+ struct mmc_data data;
+};
+
+struct mmc_queue_req {
+ struct request *req;
+ struct mmc_blk_request brq;
+ struct scatterlist *sg;
+ char *bounce_buf;
+ struct scatterlist *bounce_sg;
+ unsigned int bounce_sg_len;
+ struct mmc_async_req mmc_active;
+};
+
struct mmc_queue {
struct mmc_card *card;
struct task_struct *thread;
struct semaphore thread_sem;
unsigned int flags;
- struct request *req;
int (*issue_fn)(struct mmc_queue *, struct request *);
void *data;
struct request_queue *queue;
- struct scatterlist *sg;
- char *bounce_buf;
- struct scatterlist *bounce_sg;
- unsigned int bounce_sg_len;
+ struct mmc_queue_req mqrq[2];
+ struct mmc_queue_req *mqrq_cur;
+ struct mmc_queue_req *mqrq_prev;
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
@@ -25,8 +41,9 @@ extern void mmc_cleanup_queue(struct mmc_queue *);
extern void mmc_queue_suspend(struct mmc_queue *);
extern void mmc_queue_resume(struct mmc_queue *);
-extern unsigned int mmc_queue_map_sg(struct mmc_queue *);
-extern void mmc_queue_bounce_pre(struct mmc_queue *);
-extern void mmc_queue_bounce_post(struct mmc_queue *);
+extern unsigned int mmc_queue_map_sg(struct mmc_queue *,
+ struct mmc_queue_req *);
+extern void mmc_queue_bounce_pre(struct mmc_queue_req *);
+extern void mmc_queue_bounce_post(struct mmc_queue_req *);
#endif
diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index c8c9edb3d7cb..2c151e18c9e8 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -1082,7 +1082,7 @@ static int sdio_uart_probe(struct sdio_func *func,
return -ENOMEM;
if (func->class == SDIO_CLASS_UART) {
- printk(KERN_WARNING "%s: need info on UART class basic setup\n",
+ pr_warning("%s: need info on UART class basic setup\n",
sdio_func_id(func));
kfree(port);
return -ENOSYS;
@@ -1101,23 +1101,23 @@ static int sdio_uart_probe(struct sdio_func *func,
break;
}
if (!tpl) {
- printk(KERN_WARNING
+ pr_warning(
"%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
sdio_func_id(func));
kfree(port);
return -EINVAL;
}
- printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
+ pr_debug("%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
sdio_func_id(func), tpl->data[2], tpl->data[3]);
port->regs_offset = (tpl->data[4] << 0) |
(tpl->data[5] << 8) |
(tpl->data[6] << 16);
- printk(KERN_DEBUG "%s: regs offset = 0x%x\n",
+ pr_debug("%s: regs offset = 0x%x\n",
sdio_func_id(func), port->regs_offset);
port->uartclk = tpl->data[7] * 115200;
if (port->uartclk == 0)
port->uartclk = 115200;
- printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n",
+ pr_debug("%s: clk %d baudcode %u 4800-div %u\n",
sdio_func_id(func), port->uartclk,
tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));
} else {
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 393d817ed040..6be49249895a 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -11,9 +11,11 @@
* MMC card bus driver model
*/
+#include <linux/export.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/stat.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/card.h>
@@ -295,7 +297,7 @@ int mmc_add_card(struct mmc_card *card)
}
if (mmc_host_is_spi(card->host)) {
- printk(KERN_INFO "%s: new %s%s%s card on SPI\n",
+ pr_info("%s: new %s%s%s card on SPI\n",
mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "",
mmc_card_ddr_mode(card) ? "DDR " : "",
@@ -334,10 +336,10 @@ void mmc_remove_card(struct mmc_card *card)
if (mmc_card_present(card)) {
if (mmc_host_is_spi(card->host)) {
- printk(KERN_INFO "%s: SPI card removed\n",
+ pr_info("%s: SPI card removed\n",
mmc_hostname(card->host));
} else {
- printk(KERN_INFO "%s: card %04x removed\n",
+ pr_info("%s: card %04x removed\n",
mmc_hostname(card->host), card->rca);
}
device_del(&card->dev);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 7843efe22359..5278ffb20e74 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -23,6 +23,9 @@
#include <linux/log2.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+#include <linux/fault-inject.h>
+#include <linux/random.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -82,6 +85,43 @@ static void mmc_flush_scheduled_work(void)
flush_workqueue(workqueue);
}
+#ifdef CONFIG_FAIL_MMC_REQUEST
+
+/*
+ * Internal function. Inject random data errors.
+ * If mmc_data is NULL no errors are injected.
+ */
+static void mmc_should_fail_request(struct mmc_host *host,
+ struct mmc_request *mrq)
+{
+ struct mmc_command *cmd = mrq->cmd;
+ struct mmc_data *data = mrq->data;
+ static const int data_errors[] = {
+ -ETIMEDOUT,
+ -EILSEQ,
+ -EIO,
+ };
+
+ if (!data)
+ return;
+
+ if (cmd->error || data->error ||
+ !should_fail(&host->fail_mmc_request, data->blksz * data->blocks))
+ return;
+
+ data->error = data_errors[random32() % ARRAY_SIZE(data_errors)];
+ data->bytes_xfered = (random32() % (data->bytes_xfered >> 9)) << 9;
+}
+
+#else /* CONFIG_FAIL_MMC_REQUEST */
+
+static inline void mmc_should_fail_request(struct mmc_host *host,
+ struct mmc_request *mrq)
+{
+}
+
+#endif /* CONFIG_FAIL_MMC_REQUEST */
+
/**
* mmc_request_done - finish processing an MMC request
* @host: MMC host which completed request
@@ -101,13 +141,15 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
}
if (err && cmd->retries) {
- pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
- mmc_hostname(host), cmd->opcode, err);
-
- cmd->retries--;
- cmd->error = 0;
- host->ops->request(host, mrq);
+ /*
+ * Request starter must handle retries - see
+ * mmc_wait_for_req_done().
+ */
+ if (mrq->done)
+ mrq->done(mrq);
} else {
+ mmc_should_fail_request(host, mrq);
+
led_trigger_event(host->led, LED_OFF);
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
@@ -132,7 +174,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
if (mrq->done)
mrq->done(mrq);
- mmc_host_clk_gate(host);
+ mmc_host_clk_release(host);
}
}
@@ -191,15 +233,135 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mrq->stop->mrq = mrq;
}
}
- mmc_host_clk_ungate(host);
+ mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);
host->ops->request(host, mrq);
}
static void mmc_wait_done(struct mmc_request *mrq)
{
- complete(mrq->done_data);
+ complete(&mrq->completion);
+}
+
+static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
+{
+ init_completion(&mrq->completion);
+ mrq->done = mmc_wait_done;
+ mmc_start_request(host, mrq);
+}
+
+static void mmc_wait_for_req_done(struct mmc_host *host,
+ struct mmc_request *mrq)
+{
+ struct mmc_command *cmd;
+
+ while (1) {
+ wait_for_completion(&mrq->completion);
+
+ cmd = mrq->cmd;
+ if (!cmd->error || !cmd->retries)
+ break;
+
+ pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
+ mmc_hostname(host), cmd->opcode, cmd->error);
+ cmd->retries--;
+ cmd->error = 0;
+ host->ops->request(host, mrq);
+ }
+}
+
+/**
+ * mmc_pre_req - Prepare for a new request
+ * @host: MMC host to prepare command
+ * @mrq: MMC request to prepare for
+ * @is_first_req: true if there is no previous started request
+ * that may run in parellel to this call, otherwise false
+ *
+ * mmc_pre_req() is called in prior to mmc_start_req() to let
+ * host prepare for the new request. Preparation of a request may be
+ * performed while another request is running on the host.
+ */
+static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq,
+ bool is_first_req)
+{
+ if (host->ops->pre_req)
+ host->ops->pre_req(host, mrq, is_first_req);
+}
+
+/**
+ * mmc_post_req - Post process a completed request
+ * @host: MMC host to post process command
+ * @mrq: MMC request to post process for
+ * @err: Error, if non zero, clean up any resources made in pre_req
+ *
+ * Let the host post process a completed request. Post processing of
+ * a request may be performed while another reuqest is running.
+ */
+static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
+ int err)
+{
+ if (host->ops->post_req)
+ host->ops->post_req(host, mrq, err);
+}
+
+/**
+ * mmc_start_req - start a non-blocking request
+ * @host: MMC host to start command
+ * @areq: async request to start
+ * @error: out parameter returns 0 for success, otherwise non zero
+ *
+ * Start a new MMC custom command request for a host.
+ * If there is on ongoing async request wait for completion
+ * of that request and start the new one and return.
+ * Does not wait for the new request to complete.
+ *
+ * Returns the completed request, NULL in case of none completed.
+ * Wait for the an ongoing request (previoulsy started) to complete and
+ * return the completed request. If there is no ongoing request, NULL
+ * is returned without waiting. NULL is not an error condition.
+ */
+struct mmc_async_req *mmc_start_req(struct mmc_host *host,
+ struct mmc_async_req *areq, int *error)
+{
+ int err = 0;
+ struct mmc_async_req *data = host->areq;
+
+ /* Prepare a new request */
+ if (areq)
+ mmc_pre_req(host, areq->mrq, !host->areq);
+
+ if (host->areq) {
+ mmc_wait_for_req_done(host, host->areq->mrq);
+ err = host->areq->err_check(host->card, host->areq);
+ if (err) {
+ /* post process the completed failed request */
+ mmc_post_req(host, host->areq->mrq, 0);
+ if (areq)
+ /*
+ * Cancel the new prepared request, because
+ * it can't run until the failed
+ * request has been properly handled.
+ */
+ mmc_post_req(host, areq->mrq, -EINVAL);
+
+ host->areq = NULL;
+ goto out;
+ }
+ }
+
+ if (areq)
+ __mmc_start_req(host, areq->mrq);
+
+ if (host->areq)
+ mmc_post_req(host, host->areq->mrq, 0);
+
+ host->areq = areq;
+ out:
+ if (error)
+ *error = err;
+ return data;
}
+EXPORT_SYMBOL(mmc_start_req);
/**
* mmc_wait_for_req - start a request and wait for completion
@@ -212,17 +374,67 @@ static void mmc_wait_done(struct mmc_request *mrq)
*/
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
- DECLARE_COMPLETION_ONSTACK(complete);
+ __mmc_start_req(host, mrq);
+ mmc_wait_for_req_done(host, mrq);
+}
+EXPORT_SYMBOL(mmc_wait_for_req);
- mrq->done_data = &complete;
- mrq->done = mmc_wait_done;
+/**
+ * mmc_interrupt_hpi - Issue for High priority Interrupt
+ * @card: the MMC card associated with the HPI transfer
+ *
+ * Issued High Priority Interrupt, and check for card status
+ * util out-of prg-state.
+ */
+int mmc_interrupt_hpi(struct mmc_card *card)
+{
+ int err;
+ u32 status;
- mmc_start_request(host, mrq);
+ BUG_ON(!card);
- wait_for_completion(&complete);
-}
+ if (!card->ext_csd.hpi_en) {
+ pr_info("%s: HPI enable bit unset\n", mmc_hostname(card->host));
+ return 1;
+ }
-EXPORT_SYMBOL(mmc_wait_for_req);
+ mmc_claim_host(card->host);
+ err = mmc_send_status(card, &status);
+ if (err) {
+ pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
+ goto out;
+ }
+
+ /*
+ * If the card status is in PRG-state, we can send the HPI command.
+ */
+ if (R1_CURRENT_STATE(status) == R1_STATE_PRG) {
+ do {
+ /*
+ * We don't know when the HPI command will finish
+ * processing, so we need to resend HPI until out
+ * of prg-state, and keep checking the card status
+ * with SEND_STATUS. If a timeout error occurs when
+ * sending the HPI command, we are already out of
+ * prg-state.
+ */
+ err = mmc_send_hpi_cmd(card, &status);
+ if (err)
+ pr_debug("%s: abort HPI (%d error)\n",
+ mmc_hostname(card->host), err);
+
+ err = mmc_send_status(card, &status);
+ if (err)
+ break;
+ } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
+ } else
+ pr_debug("%s: Left prg-state\n", mmc_hostname(card->host));
+
+out:
+ mmc_release_host(card->host);
+ return err;
+}
+EXPORT_SYMBOL(mmc_interrupt_hpi);
/**
* mmc_wait_for_cmd - start a command and wait for completion
@@ -236,7 +448,7 @@ EXPORT_SYMBOL(mmc_wait_for_req);
*/
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
WARN_ON(!host->claimed);
@@ -634,15 +846,17 @@ static inline void mmc_set_ios(struct mmc_host *host)
*/
void mmc_set_chip_select(struct mmc_host *host, int mode)
{
+ mmc_host_clk_hold(host);
host->ios.chip_select = mode;
mmc_set_ios(host);
+ mmc_host_clk_release(host);
}
/*
* Sets the host clock to the highest possible frequency that
* is below "hz".
*/
-void mmc_set_clock(struct mmc_host *host, unsigned int hz)
+static void __mmc_set_clock(struct mmc_host *host, unsigned int hz)
{
WARN_ON(hz < host->f_min);
@@ -653,6 +867,13 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz)
mmc_set_ios(host);
}
+void mmc_set_clock(struct mmc_host *host, unsigned int hz)
+{
+ mmc_host_clk_hold(host);
+ __mmc_set_clock(host, hz);
+ mmc_host_clk_release(host);
+}
+
#ifdef CONFIG_MMC_CLKGATE
/*
* This gates the clock by setting it to 0 Hz.
@@ -685,7 +906,7 @@ void mmc_ungate_clock(struct mmc_host *host)
if (host->clk_old) {
BUG_ON(host->ios.clock);
/* This call will also set host->clk_gated to false */
- mmc_set_clock(host, host->clk_old);
+ __mmc_set_clock(host, host->clk_old);
}
}
@@ -713,8 +934,10 @@ void mmc_set_ungated(struct mmc_host *host)
*/
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
{
+ mmc_host_clk_hold(host);
host->ios.bus_mode = mode;
mmc_set_ios(host);
+ mmc_host_clk_release(host);
}
/*
@@ -722,8 +945,10 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
*/
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
+ mmc_host_clk_hold(host);
host->ios.bus_width = width;
mmc_set_ios(host);
+ mmc_host_clk_release(host);
}
/**
@@ -921,8 +1146,10 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
ocr &= 3 << bit;
+ mmc_host_clk_hold(host);
host->ios.vdd = bit;
mmc_set_ios(host);
+ mmc_host_clk_release(host);
} else {
pr_warning("%s: host doesn't support card's voltages\n",
mmc_hostname(host));
@@ -969,8 +1196,10 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11
*/
void mmc_set_timing(struct mmc_host *host, unsigned int timing)
{
+ mmc_host_clk_hold(host);
host->ios.timing = timing;
mmc_set_ios(host);
+ mmc_host_clk_release(host);
}
/*
@@ -978,8 +1207,10 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing)
*/
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
{
+ mmc_host_clk_hold(host);
host->ios.drv_type = drv_type;
mmc_set_ios(host);
+ mmc_host_clk_release(host);
}
/*
@@ -997,6 +1228,8 @@ static void mmc_power_up(struct mmc_host *host)
{
int bit;
+ mmc_host_clk_hold(host);
+
/* If ocr is set, we use it */
if (host->ocr)
bit = ffs(host->ocr) - 1;
@@ -1004,13 +1237,11 @@ static void mmc_power_up(struct mmc_host *host)
bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
- if (mmc_host_is_spi(host)) {
+ if (mmc_host_is_spi(host))
host->ios.chip_select = MMC_CS_HIGH;
- host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
- } else {
+ else
host->ios.chip_select = MMC_CS_DONTCARE;
- host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
- }
+ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
host->ios.power_mode = MMC_POWER_UP;
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
@@ -1032,13 +1263,49 @@ static void mmc_power_up(struct mmc_host *host)
* time required to reach a stable voltage.
*/
mmc_delay(10);
+
+ mmc_host_clk_release(host);
}
-static void mmc_power_off(struct mmc_host *host)
+void mmc_power_off(struct mmc_host *host)
{
+ struct mmc_card *card;
+ unsigned int notify_type;
+ unsigned int timeout;
+ int err;
+
+ mmc_host_clk_hold(host);
+
+ card = host->card;
host->ios.clock = 0;
host->ios.vdd = 0;
+ if (card && mmc_card_mmc(card) &&
+ (card->poweroff_notify_state == MMC_POWERED_ON)) {
+
+ if (host->power_notify_type == MMC_HOST_PW_NOTIFY_SHORT) {
+ notify_type = EXT_CSD_POWER_OFF_SHORT;
+ timeout = card->ext_csd.generic_cmd6_time;
+ card->poweroff_notify_state = MMC_POWEROFF_SHORT;
+ } else {
+ notify_type = EXT_CSD_POWER_OFF_LONG;
+ timeout = card->ext_csd.power_off_longtime;
+ card->poweroff_notify_state = MMC_POWEROFF_LONG;
+ }
+
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_OFF_NOTIFICATION,
+ notify_type, timeout);
+
+ if (err && err != -EBADMSG)
+ pr_err("Device failed to respond within %d poweroff "
+ "time. Forcefully powering down the device\n",
+ timeout);
+
+ /* Set the card state to no notification after the poweroff */
+ card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION;
+ }
+
/*
* Reset ocr mask to be the highest possible voltage supported for
* this mmc host. This value will be used at next power up.
@@ -1053,6 +1320,15 @@ static void mmc_power_off(struct mmc_host *host)
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
mmc_set_ios(host);
+
+ /*
+ * Some configurations, such as the 802.11 SDIO card in the OLPC
+ * XO-1.5, require a short delay after poweroff before the card
+ * can be successfully turned on again.
+ */
+ mmc_delay(1);
+
+ mmc_host_clk_release(host);
}
/*
@@ -1120,8 +1396,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
}
/*
- * Remove the current bus handler from a host. Assumes that there are
- * no interesting cards left, so the bus is powered down.
+ * Remove the current bus handler from a host.
*/
void mmc_detach_bus(struct mmc_host *host)
{
@@ -1138,8 +1413,6 @@ void mmc_detach_bus(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
- mmc_power_off(host);
-
mmc_bus_put(host);
}
@@ -1357,9 +1630,9 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) {
- printk(KERN_ERR "mmc_erase: group start error %d, "
+ pr_err("mmc_erase: group start error %d, "
"status %#x\n", err, cmd.resp[0]);
- err = -EINVAL;
+ err = -EIO;
goto out;
}
@@ -1372,9 +1645,9 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) {
- printk(KERN_ERR "mmc_erase: group end error %d, status %#x\n",
+ pr_err("mmc_erase: group end error %d, status %#x\n",
err, cmd.resp[0]);
- err = -EINVAL;
+ err = -EIO;
goto out;
}
@@ -1385,7 +1658,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty);
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) {
- printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n",
+ pr_err("mmc_erase: erase error %d, status %#x\n",
err, cmd.resp[0]);
err = -EIO;
goto out;
@@ -1402,13 +1675,13 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
/* Do not retry else we can't see errors */
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err || (cmd.resp[0] & 0xFDF92000)) {
- printk(KERN_ERR "error %d requesting status %#x\n",
+ pr_err("error %d requesting status %#x\n",
err, cmd.resp[0]);
err = -EIO;
goto out;
}
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
- R1_CURRENT_STATE(cmd.resp[0]) == 7);
+ R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG);
out:
return err;
}
@@ -1493,10 +1766,32 @@ int mmc_can_trim(struct mmc_card *card)
{
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)
return 1;
+ if (mmc_can_discard(card))
+ return 1;
return 0;
}
EXPORT_SYMBOL(mmc_can_trim);
+int mmc_can_discard(struct mmc_card *card)
+{
+ /*
+ * As there's no way to detect the discard support bit at v4.5
+ * use the s/w feature support filed.
+ */
+ if (card->ext_csd.feature_support & MMC_DISCARD_FEATURE)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_discard);
+
+int mmc_can_sanitize(struct mmc_card *card)
+{
+ if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_SANITIZE)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_sanitize);
+
int mmc_can_secure_erase_trim(struct mmc_card *card)
{
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)
@@ -1516,6 +1811,82 @@ int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
}
EXPORT_SYMBOL(mmc_erase_group_aligned);
+static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
+ unsigned int arg)
+{
+ struct mmc_host *host = card->host;
+ unsigned int max_discard, x, y, qty = 0, max_qty, timeout;
+ unsigned int last_timeout = 0;
+
+ if (card->erase_shift)
+ max_qty = UINT_MAX >> card->erase_shift;
+ else if (mmc_card_sd(card))
+ max_qty = UINT_MAX;
+ else
+ max_qty = UINT_MAX / card->erase_size;
+
+ /* Find the largest qty with an OK timeout */
+ do {
+ y = 0;
+ for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
+ timeout = mmc_erase_timeout(card, arg, qty + x);
+ if (timeout > host->max_discard_to)
+ break;
+ if (timeout < last_timeout)
+ break;
+ last_timeout = timeout;
+ y = x;
+ }
+ qty += y;
+ } while (y);
+
+ if (!qty)
+ return 0;
+
+ if (qty == 1)
+ return 1;
+
+ /* Convert qty to sectors */
+ if (card->erase_shift)
+ max_discard = --qty << card->erase_shift;
+ else if (mmc_card_sd(card))
+ max_discard = qty;
+ else
+ max_discard = --qty * card->erase_size;
+
+ return max_discard;
+}
+
+unsigned int mmc_calc_max_discard(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ unsigned int max_discard, max_trim;
+
+ if (!host->max_discard_to)
+ return UINT_MAX;
+
+ /*
+ * Without erase_group_def set, MMC erase timeout depends on clock
+ * frequence which can change. In that case, the best choice is
+ * just the preferred erase size.
+ */
+ if (mmc_card_mmc(card) && !(card->ext_csd.erase_group_def & 1))
+ return card->pref_erase;
+
+ max_discard = mmc_do_calc_max_discard(card, MMC_ERASE_ARG);
+ if (mmc_can_trim(card)) {
+ max_trim = mmc_do_calc_max_discard(card, MMC_TRIM_ARG);
+ if (max_trim < max_discard)
+ max_discard = max_trim;
+ } else if (max_discard < card->erase_size) {
+ max_discard = 0;
+ }
+ pr_debug("%s: calculated max. discard sectors %u for timeout %u ms\n",
+ mmc_hostname(host), max_discard, host->max_discard_to);
+ return max_discard;
+}
+EXPORT_SYMBOL(mmc_calc_max_discard);
+
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
{
struct mmc_command cmd = {0};
@@ -1530,6 +1901,94 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
}
EXPORT_SYMBOL(mmc_set_blocklen);
+static void mmc_hw_reset_for_init(struct mmc_host *host)
+{
+ if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
+ return;
+ mmc_host_clk_hold(host);
+ host->ops->hw_reset(host);
+ mmc_host_clk_release(host);
+}
+
+int mmc_can_reset(struct mmc_card *card)
+{
+ u8 rst_n_function;
+
+ if (!mmc_card_mmc(card))
+ return 0;
+ rst_n_function = card->ext_csd.rst_n_function;
+ if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
+ return 0;
+ return 1;
+}
+EXPORT_SYMBOL(mmc_can_reset);
+
+static int mmc_do_hw_reset(struct mmc_host *host, int check)
+{
+ struct mmc_card *card = host->card;
+
+ if (!host->bus_ops->power_restore)
+ return -EOPNOTSUPP;
+
+ if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
+ return -EOPNOTSUPP;
+
+ if (!card)
+ return -EINVAL;
+
+ if (!mmc_can_reset(card))
+ return -EOPNOTSUPP;
+
+ mmc_host_clk_hold(host);
+ mmc_set_clock(host, host->f_init);
+
+ host->ops->hw_reset(host);
+
+ /* If the reset has happened, then a status command will fail */
+ if (check) {
+ struct mmc_command cmd = {0};
+ int err;
+
+ cmd.opcode = MMC_SEND_STATUS;
+ if (!mmc_host_is_spi(card->host))
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (!err) {
+ mmc_host_clk_release(host);
+ return -ENOSYS;
+ }
+ }
+
+ host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_DDR);
+ if (mmc_host_is_spi(host)) {
+ host->ios.chip_select = MMC_CS_HIGH;
+ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+ } else {
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+ }
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
+ host->ios.timing = MMC_TIMING_LEGACY;
+ mmc_set_ios(host);
+
+ mmc_host_clk_release(host);
+
+ return host->bus_ops->power_restore(host);
+}
+
+int mmc_hw_reset(struct mmc_host *host)
+{
+ return mmc_do_hw_reset(host, 0);
+}
+EXPORT_SYMBOL(mmc_hw_reset);
+
+int mmc_hw_reset_check(struct mmc_host *host)
+{
+ return mmc_do_hw_reset(host, 1);
+}
+EXPORT_SYMBOL(mmc_hw_reset_check);
+
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
@@ -1541,6 +2000,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
mmc_power_up(host);
/*
+ * Some eMMCs (with VCCQ always on) may not be reset after power up, so
+ * do a hardware reset if possible.
+ */
+ mmc_hw_reset_for_init(host);
+
+ /*
* sdio_reset sends CMD52 to reset card. Since we do not know
* if the card is being re-initialized, just send it. CMD52
* should be ignored by SD/eMMC cards.
@@ -1648,6 +2113,7 @@ void mmc_stop_host(struct mmc_host *host)
mmc_claim_host(host);
mmc_detach_bus(host);
+ mmc_power_off(host);
mmc_release_host(host);
mmc_bus_put(host);
return;
@@ -1663,6 +2129,10 @@ int mmc_power_save_host(struct mmc_host *host)
{
int ret = 0;
+#ifdef CONFIG_MMC_DEBUG
+ pr_info("%s: %s: powering down\n", mmc_hostname(host), __func__);
+#endif
+
mmc_bus_get(host);
if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
@@ -1685,6 +2155,10 @@ int mmc_power_restore_host(struct mmc_host *host)
{
int ret;
+#ifdef CONFIG_MMC_DEBUG
+ pr_info("%s: %s: powering up\n", mmc_hostname(host), __func__);
+#endif
+
mmc_bus_get(host);
if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
@@ -1741,6 +2215,65 @@ int mmc_card_can_sleep(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_card_can_sleep);
+/*
+ * Flush the cache to the non-volatile storage.
+ */
+int mmc_flush_cache(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ int err = 0;
+
+ if (!(host->caps2 & MMC_CAP2_CACHE_CTRL))
+ return err;
+
+ if (mmc_card_mmc(card) &&
+ (card->ext_csd.cache_size > 0) &&
+ (card->ext_csd.cache_ctrl & 1)) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_FLUSH_CACHE, 1, 0);
+ if (err)
+ pr_err("%s: cache flush error %d\n",
+ mmc_hostname(card->host), err);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL(mmc_flush_cache);
+
+/*
+ * Turn the cache ON/OFF.
+ * Turning the cache OFF shall trigger flushing of the data
+ * to the non-volatile storage.
+ */
+int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
+{
+ struct mmc_card *card = host->card;
+ int err = 0;
+
+ if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) ||
+ mmc_card_is_removable(host))
+ return err;
+
+ if (card && mmc_card_mmc(card) &&
+ (card->ext_csd.cache_size > 0)) {
+ enable = !!enable;
+
+ if (card->ext_csd.cache_ctrl ^ enable)
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_CACHE_CTRL, enable, 0);
+ if (err)
+ pr_err("%s: cache %s error %d\n",
+ mmc_hostname(card->host),
+ enable ? "on" : "off",
+ err);
+ else
+ card->ext_csd.cache_ctrl = enable;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL(mmc_cache_ctrl);
+
#ifdef CONFIG_PM
/**
@@ -1755,23 +2288,39 @@ int mmc_suspend_host(struct mmc_host *host)
cancel_delayed_work(&host->disable);
cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work();
+ err = mmc_cache_ctrl(host, 0);
+ if (err)
+ goto out;
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
- if (host->bus_ops->suspend)
- err = host->bus_ops->suspend(host);
- if (err == -ENOSYS || !host->bus_ops->resume) {
- /*
- * We simply "remove" the card in this case.
- * It will be redetected on resume.
- */
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
- mmc_claim_host(host);
- mmc_detach_bus(host);
- mmc_release_host(host);
- host->pm_flags = 0;
- err = 0;
+
+ /*
+ * A long response time is not acceptable for device drivers
+ * when doing suspend. Prevent mmc_claim_host in the suspend
+ * sequence, to potentially wait "forever" by trying to
+ * pre-claim the host.
+ */
+ if (mmc_try_claim_host(host)) {
+ if (host->bus_ops->suspend)
+ err = host->bus_ops->suspend(host);
+ if (err == -ENOSYS || !host->bus_ops->resume) {
+ /*
+ * We simply "remove" the card in this case.
+ * It will be redetected on resume.
+ */
+ if (host->bus_ops->remove)
+ host->bus_ops->remove(host);
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ mmc_power_off(host);
+ mmc_release_host(host);
+ host->pm_flags = 0;
+ err = 0;
+ }
+ mmc_do_release_host(host);
+ } else {
+ err = -EBUSY;
}
}
mmc_bus_put(host);
@@ -1779,6 +2328,7 @@ int mmc_suspend_host(struct mmc_host *host)
if (!err && !mmc_card_keep_power(host))
mmc_power_off(host);
+out:
return err;
}
@@ -1813,7 +2363,7 @@ int mmc_resume_host(struct mmc_host *host)
BUG_ON(!host->bus_ops->resume);
err = host->bus_ops->resume(host);
if (err) {
- printk(KERN_WARNING "%s: error %d during resume "
+ pr_warning("%s: error %d during resume "
"(card was removed?)\n",
mmc_hostname(host), err);
err = 0;
@@ -1844,6 +2394,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 1;
+ host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->detect);
@@ -1856,6 +2407,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
host->bus_ops->remove(host);
mmc_detach_bus(host);
+ mmc_power_off(host);
mmc_release_host(host);
host->pm_flags = 0;
break;
@@ -1866,6 +2418,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 0;
+ host->power_notify_type = MMC_HOST_PW_NOTIFY_LONG;
spin_unlock_irqrestore(&host->lock, flags);
mmc_detect_change(host, 0);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index d9411ed2a39b..14664f1fb16f 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -43,6 +43,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
bool cmd11);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
+void mmc_power_off(struct mmc_host *host);
static inline void mmc_delay(unsigned int ms)
{
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 998797ed67a6..3923880118b6 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -7,11 +7,14 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/moduleparam.h>
+#include <linux/export.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/stat.h>
+#include <linux/fault-inject.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -19,6 +22,14 @@
#include "core.h"
#include "mmc_ops.h"
+#ifdef CONFIG_FAIL_MMC_REQUEST
+
+static DECLARE_FAULT_ATTR(fail_default_attr);
+static char *fail_request;
+module_param(fail_request, charp, 0);
+
+#endif /* CONFIG_FAIL_MMC_REQUEST */
+
/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
static int mmc_ios_show(struct seq_file *s, void *data)
{
@@ -113,6 +124,15 @@ static int mmc_ios_show(struct seq_file *s, void *data)
case MMC_TIMING_SD_HS:
str = "sd high-speed";
break;
+ case MMC_TIMING_UHS_SDR50:
+ str = "sd uhs SDR50";
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ str = "sd uhs SDR104";
+ break;
+ case MMC_TIMING_UHS_DDR50:
+ str = "sd uhs DDR50";
+ break;
default:
str = "invalid";
break;
@@ -188,6 +208,15 @@ void mmc_add_host_debugfs(struct mmc_host *host)
root, &host->clk_delay))
goto err_node;
#endif
+#ifdef CONFIG_FAIL_MMC_REQUEST
+ if (fail_request)
+ setup_fault_attr(&fail_default_attr, fail_request);
+ host->fail_mmc_request = fail_default_attr;
+ if (IS_ERR(fault_create_debugfs_attr("fail_mmc_request",
+ root,
+ &host->fail_mmc_request)))
+ goto err_node;
+#endif
return;
err_node:
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index b29d3e8fd3a2..e8a5eb38748b 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -16,6 +16,7 @@
#include <linux/err.h>
#include <linux/idr.h>
#include <linux/pagemap.h>
+#include <linux/export.h>
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/suspend.h>
@@ -119,14 +120,14 @@ static void mmc_host_clk_gate_work(struct work_struct *work)
}
/**
- * mmc_host_clk_ungate - ungate hardware MCI clocks
+ * mmc_host_clk_hold - ungate hardware MCI clocks
* @host: host to ungate.
*
* Makes sure the host ios.clock is restored to a non-zero value
* past this call. Increase clock reference count and ungate clock
* if we're the first user.
*/
-void mmc_host_clk_ungate(struct mmc_host *host)
+void mmc_host_clk_hold(struct mmc_host *host)
{
unsigned long flags;
@@ -164,14 +165,14 @@ static bool mmc_host_may_gate_card(struct mmc_card *card)
}
/**
- * mmc_host_clk_gate - gate off hardware MCI clocks
+ * mmc_host_clk_release - gate off hardware MCI clocks
* @host: host to gate.
*
* Calls the host driver with ios.clock set to zero as often as possible
* in order to gate off hardware MCI clocks. Decrease clock reference
* count and schedule disabling of clock.
*/
-void mmc_host_clk_gate(struct mmc_host *host)
+void mmc_host_clk_release(struct mmc_host *host)
{
unsigned long flags;
@@ -179,7 +180,7 @@ void mmc_host_clk_gate(struct mmc_host *host)
host->clk_requests--;
if (mmc_host_may_gate_card(host->card) &&
!host->clk_requests)
- schedule_work(&host->clk_gate_work);
+ queue_work(system_nrt_wq, &host->clk_gate_work);
spin_unlock_irqrestore(&host->clk_lock, flags);
}
@@ -231,7 +232,7 @@ static inline void mmc_host_clk_exit(struct mmc_host *host)
if (cancel_work_sync(&host->clk_gate_work))
mmc_host_clk_gate_delayed(host);
if (host->clk_gated)
- mmc_host_clk_ungate(host);
+ mmc_host_clk_hold(host);
/* There should be only one user now */
WARN_ON(host->clk_requests > 1);
}
@@ -301,6 +302,17 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->max_blk_size = 512;
host->max_blk_count = PAGE_CACHE_SIZE / 512;
+ /*
+ * Enable runtime power management by default. This flag was added due
+ * to runtime power management causing disruption for some users, but
+ * the power on/off code has been improved since then.
+ *
+ * We'll enable this flag by default as an experiment, and if no
+ * problems are reported, we will follow up later and remove the flag
+ * altogether.
+ */
+ host->caps = MMC_CAP_POWER_OFF_CARD;
+
return host;
free:
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index de199f911928..fb8a5cd2e4a1 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -16,16 +16,16 @@ int mmc_register_host_class(void);
void mmc_unregister_host_class(void);
#ifdef CONFIG_MMC_CLKGATE
-void mmc_host_clk_ungate(struct mmc_host *host);
-void mmc_host_clk_gate(struct mmc_host *host);
+void mmc_host_clk_hold(struct mmc_host *host);
+void mmc_host_clk_release(struct mmc_host *host);
unsigned int mmc_host_clk_rate(struct mmc_host *host);
#else
-static inline void mmc_host_clk_ungate(struct mmc_host *host)
+static inline void mmc_host_clk_hold(struct mmc_host *host)
{
}
-static inline void mmc_host_clk_gate(struct mmc_host *host)
+static inline void mmc_host_clk_release(struct mmc_host *host)
{
}
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 2a7e43bc796d..dbf421a6279c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -12,6 +12,7 @@
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/stat.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
@@ -101,7 +102,7 @@ static int mmc_decode_cid(struct mmc_card *card)
break;
default:
- printk(KERN_ERR "%s: card has unknown MMCA version %d\n",
+ pr_err("%s: card has unknown MMCA version %d\n",
mmc_hostname(card->host), card->csd.mmca_vsn);
return -EINVAL;
}
@@ -135,7 +136,7 @@ static int mmc_decode_csd(struct mmc_card *card)
*/
csd->structure = UNSTUFF_BITS(resp, 126, 2);
if (csd->structure == 0) {
- printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
+ pr_err("%s: unrecognised CSD structure version %d\n",
mmc_hostname(card->host), csd->structure);
return -EINVAL;
}
@@ -195,7 +196,7 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
*/
ext_csd = kmalloc(512, GFP_KERNEL);
if (!ext_csd) {
- printk(KERN_ERR "%s: could not allocate a buffer to "
+ pr_err("%s: could not allocate a buffer to "
"receive the ext_csd.\n", mmc_hostname(card->host));
return -ENOMEM;
}
@@ -217,12 +218,12 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
* stored in their CSD.
*/
if (card->csd.capacity == (4096 * 512)) {
- printk(KERN_ERR "%s: unable to read EXT_CSD "
+ pr_err("%s: unable to read EXT_CSD "
"on a possible high capacity card. "
"Card will be ignored.\n",
mmc_hostname(card->host));
} else {
- printk(KERN_WARNING "%s: unable to read "
+ pr_warning("%s: unable to read "
"EXT_CSD, performance might "
"suffer.\n",
mmc_hostname(card->host));
@@ -239,7 +240,9 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
*/
static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
- int err = 0;
+ int err = 0, idx;
+ unsigned int part_size;
+ u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0;
BUG_ON(!card);
@@ -247,25 +250,29 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
return 0;
/* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
+ card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE];
if (card->csd.structure == 3) {
- int ext_csd_struct = ext_csd[EXT_CSD_STRUCTURE];
- if (ext_csd_struct > 2) {
- printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
+ if (card->ext_csd.raw_ext_csd_structure > 2) {
+ pr_err("%s: unrecognised EXT_CSD structure "
"version %d\n", mmc_hostname(card->host),
- ext_csd_struct);
+ card->ext_csd.raw_ext_csd_structure);
err = -EINVAL;
goto out;
}
}
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
- if (card->ext_csd.rev > 5) {
- printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n",
+ if (card->ext_csd.rev > 6) {
+ pr_err("%s: unrecognised EXT_CSD revision %d\n",
mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL;
goto out;
}
+ card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0];
+ card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
+ card->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2];
+ card->ext_csd.raw_sectors[3] = ext_csd[EXT_CSD_SEC_CNT + 3];
if (card->ext_csd.rev >= 2) {
card->ext_csd.sectors =
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
@@ -277,7 +284,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512)
mmc_card_set_blockaddr(card);
}
-
+ card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
EXT_CSD_CARD_TYPE_26:
@@ -302,11 +309,16 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
break;
default:
/* MMC v4 spec says this cannot happen */
- printk(KERN_WARNING "%s: card is mmc v4 but doesn't "
+ pr_warning("%s: card is mmc v4 but doesn't "
"support any high-speed modes.\n",
mmc_hostname(card->host));
}
+ card->ext_csd.raw_s_a_timeout = ext_csd[EXT_CSD_S_A_TIMEOUT];
+ card->ext_csd.raw_erase_timeout_mult =
+ ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT];
+ card->ext_csd.raw_hc_erase_grp_size =
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
if (card->ext_csd.rev >= 3) {
u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT];
card->ext_csd.part_config = ext_csd[EXT_CSD_PART_CONFIG];
@@ -331,20 +343,38 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
* There are two boot regions of equal size, defined in
* multiples of 128K.
*/
- card->ext_csd.boot_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
+ if (ext_csd[EXT_CSD_BOOT_MULT] && mmc_boot_partition_access(card->host)) {
+ for (idx = 0; idx < MMC_NUM_BOOT_PARTITION; idx++) {
+ part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
+ mmc_part_add(card, part_size,
+ EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx,
+ "boot%d", idx, true);
+ }
+ }
}
+ card->ext_csd.raw_hc_erase_gap_size =
+ ext_csd[EXT_CSD_PARTITION_ATTRIBUTE];
+ card->ext_csd.raw_sec_trim_mult =
+ ext_csd[EXT_CSD_SEC_TRIM_MULT];
+ card->ext_csd.raw_sec_erase_mult =
+ ext_csd[EXT_CSD_SEC_ERASE_MULT];
+ card->ext_csd.raw_sec_feature_support =
+ ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
+ card->ext_csd.raw_trim_mult =
+ ext_csd[EXT_CSD_TRIM_MULT];
if (card->ext_csd.rev >= 4) {
/*
* Enhanced area feature support -- check whether the eMMC
* card has the Enhanced area enabled. If so, export enhanced
* area offset and size to user by adding sysfs interface.
*/
+ card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) &&
- (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
- u8 hc_erase_grp_sz =
+ (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
+ hc_erase_grp_sz =
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
- u8 hc_wp_grp_sz =
+ hc_wp_grp_sz =
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
card->ext_csd.enhanced_area_en = 1;
@@ -373,6 +403,41 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.enhanced_area_offset = -EINVAL;
card->ext_csd.enhanced_area_size = -EINVAL;
}
+
+ /*
+ * General purpose partition feature support --
+ * If ext_csd has the size of general purpose partitions,
+ * set size, part_cfg, partition name in mmc_part.
+ */
+ if (ext_csd[EXT_CSD_PARTITION_SUPPORT] &
+ EXT_CSD_PART_SUPPORT_PART_EN) {
+ if (card->ext_csd.enhanced_area_en != 1) {
+ hc_erase_grp_sz =
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
+ hc_wp_grp_sz =
+ ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+
+ card->ext_csd.enhanced_area_en = 1;
+ }
+
+ for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) {
+ if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] &&
+ !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] &&
+ !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2])
+ continue;
+ part_size =
+ (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]
+ << 16) +
+ (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1]
+ << 8) +
+ ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3];
+ part_size *= (size_t)(hc_erase_grp_sz *
+ hc_wp_grp_sz);
+ mmc_part_add(card, part_size << 19,
+ EXT_CSD_PART_CONFIG_ACC_GP0 + idx,
+ "gp%d", idx, false);
+ }
+ }
card->ext_csd.sec_trim_mult =
ext_csd[EXT_CSD_SEC_TRIM_MULT];
card->ext_csd.sec_erase_mult =
@@ -383,14 +448,48 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
ext_csd[EXT_CSD_TRIM_MULT];
}
- if (card->ext_csd.rev >= 5)
+ if (card->ext_csd.rev >= 5) {
+ /* check whether the eMMC card supports HPI */
+ if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
+ card->ext_csd.hpi = 1;
+ if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
+ card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
+ else
+ card->ext_csd.hpi_cmd = MMC_SEND_STATUS;
+ /*
+ * Indicate the maximum timeout to close
+ * a command interrupted by HPI
+ */
+ card->ext_csd.out_of_int_time =
+ ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10;
+ }
+
card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
+ card->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION];
+ }
+ card->ext_csd.raw_erased_mem_count = ext_csd[EXT_CSD_ERASED_MEM_CONT];
if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
card->erased_byte = 0xFF;
else
card->erased_byte = 0x0;
+ /* eMMC v4.5 or later */
+ if (card->ext_csd.rev >= 6) {
+ card->ext_csd.feature_support |= MMC_DISCARD_FEATURE;
+
+ card->ext_csd.generic_cmd6_time = 10 *
+ ext_csd[EXT_CSD_GENERIC_CMD6_TIME];
+ card->ext_csd.power_off_longtime = 10 *
+ ext_csd[EXT_CSD_POWER_OFF_LONG_TIME];
+
+ card->ext_csd.cache_size =
+ ext_csd[EXT_CSD_CACHE_SIZE + 0] << 0 |
+ ext_csd[EXT_CSD_CACHE_SIZE + 1] << 8 |
+ ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 |
+ ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24;
+ }
+
out:
return err;
}
@@ -401,17 +500,17 @@ static inline void mmc_free_ext_csd(u8 *ext_csd)
}
-static int mmc_compare_ext_csds(struct mmc_card *card, u8 *ext_csd,
- unsigned bus_width)
+static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
{
u8 *bw_ext_csd;
int err;
+ if (bus_width == MMC_BUS_WIDTH_1)
+ return 0;
+
err = mmc_get_ext_csd(card, &bw_ext_csd);
- if (err)
- return err;
- if ((ext_csd == NULL || bw_ext_csd == NULL)) {
+ if (err || bw_ext_csd == NULL) {
if (bus_width != MMC_BUS_WIDTH_1)
err = -EINVAL;
goto out;
@@ -421,35 +520,40 @@ static int mmc_compare_ext_csds(struct mmc_card *card, u8 *ext_csd,
goto out;
/* only compare read only fields */
- err = (!(ext_csd[EXT_CSD_PARTITION_SUPPORT] ==
+ err = (!(card->ext_csd.raw_partition_support ==
bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
- (ext_csd[EXT_CSD_ERASED_MEM_CONT] ==
+ (card->ext_csd.raw_erased_mem_count ==
bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) &&
- (ext_csd[EXT_CSD_REV] ==
+ (card->ext_csd.rev ==
bw_ext_csd[EXT_CSD_REV]) &&
- (ext_csd[EXT_CSD_STRUCTURE] ==
+ (card->ext_csd.raw_ext_csd_structure ==
bw_ext_csd[EXT_CSD_STRUCTURE]) &&
- (ext_csd[EXT_CSD_CARD_TYPE] ==
+ (card->ext_csd.raw_card_type ==
bw_ext_csd[EXT_CSD_CARD_TYPE]) &&
- (ext_csd[EXT_CSD_S_A_TIMEOUT] ==
+ (card->ext_csd.raw_s_a_timeout ==
bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) &&
- (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] ==
+ (card->ext_csd.raw_hc_erase_gap_size ==
bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
- (ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] ==
+ (card->ext_csd.raw_erase_timeout_mult ==
bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) &&
- (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
+ (card->ext_csd.raw_hc_erase_grp_size ==
bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
- (ext_csd[EXT_CSD_SEC_TRIM_MULT] ==
+ (card->ext_csd.raw_sec_trim_mult ==
bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) &&
- (ext_csd[EXT_CSD_SEC_ERASE_MULT] ==
+ (card->ext_csd.raw_sec_erase_mult ==
bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) &&
- (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] ==
+ (card->ext_csd.raw_sec_feature_support ==
bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) &&
- (ext_csd[EXT_CSD_TRIM_MULT] ==
+ (card->ext_csd.raw_trim_mult ==
bw_ext_csd[EXT_CSD_TRIM_MULT]) &&
- memcmp(&ext_csd[EXT_CSD_SEC_CNT],
- &bw_ext_csd[EXT_CSD_SEC_CNT],
- 4) != 0);
+ (card->ext_csd.raw_sectors[0] ==
+ bw_ext_csd[EXT_CSD_SEC_CNT + 0]) &&
+ (card->ext_csd.raw_sectors[1] ==
+ bw_ext_csd[EXT_CSD_SEC_CNT + 1]) &&
+ (card->ext_csd.raw_sectors[2] ==
+ bw_ext_csd[EXT_CSD_SEC_CNT + 2]) &&
+ (card->ext_csd.raw_sectors[3] ==
+ bw_ext_csd[EXT_CSD_SEC_CNT + 3]));
if (err)
err = -EINVAL;
@@ -506,6 +610,86 @@ static struct device_type mmc_type = {
};
/*
+ * Select the PowerClass for the current bus width
+ * If power class is defined for 4/8 bit bus in the
+ * extended CSD register, select it by executing the
+ * mmc_switch command.
+ */
+static int mmc_select_powerclass(struct mmc_card *card,
+ unsigned int bus_width, u8 *ext_csd)
+{
+ int err = 0;
+ unsigned int pwrclass_val;
+ unsigned int index = 0;
+ struct mmc_host *host;
+
+ BUG_ON(!card);
+
+ host = card->host;
+ BUG_ON(!host);
+
+ if (ext_csd == NULL)
+ return 0;
+
+ /* Power class selection is supported for versions >= 4.0 */
+ if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
+ return 0;
+
+ /* Power class values are defined only for 4/8 bit bus */
+ if (bus_width == EXT_CSD_BUS_WIDTH_1)
+ return 0;
+
+ switch (1 << host->ios.vdd) {
+ case MMC_VDD_165_195:
+ if (host->ios.clock <= 26000000)
+ index = EXT_CSD_PWR_CL_26_195;
+ else if (host->ios.clock <= 52000000)
+ index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
+ EXT_CSD_PWR_CL_52_195 :
+ EXT_CSD_PWR_CL_DDR_52_195;
+ else if (host->ios.clock <= 200000000)
+ index = EXT_CSD_PWR_CL_200_195;
+ break;
+ case MMC_VDD_32_33:
+ case MMC_VDD_33_34:
+ case MMC_VDD_34_35:
+ case MMC_VDD_35_36:
+ if (host->ios.clock <= 26000000)
+ index = EXT_CSD_PWR_CL_26_360;
+ else if (host->ios.clock <= 52000000)
+ index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
+ EXT_CSD_PWR_CL_52_360 :
+ EXT_CSD_PWR_CL_DDR_52_360;
+ else if (host->ios.clock <= 200000000)
+ index = EXT_CSD_PWR_CL_200_360;
+ break;
+ default:
+ pr_warning("%s: Voltage range not supported "
+ "for power class.\n", mmc_hostname(host));
+ return -EINVAL;
+ }
+
+ pwrclass_val = ext_csd[index];
+
+ if (bus_width & (EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_BUS_WIDTH_8))
+ pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_8BIT_MASK) >>
+ EXT_CSD_PWR_CL_8BIT_SHIFT;
+ else
+ pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_4BIT_MASK) >>
+ EXT_CSD_PWR_CL_4BIT_SHIFT;
+
+ /* If the power class is different from the default value */
+ if (pwrclass_val > 0) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_CLASS,
+ pwrclass_val,
+ card->ext_csd.generic_cmd6_time);
+ }
+
+ return err;
+}
+
+/*
* Handle the detection and initialisation of a card.
*
* In the case of a resume, "oldcard" will contain the card
@@ -524,11 +708,16 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
BUG_ON(!host);
WARN_ON(!host->claimed);
+ /* Set correct bus mode for MMC before attempting init */
+ if (!mmc_host_is_spi(host))
+ mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
+
/*
* Since we're changing the OCR value, we seem to
* need to tell some cards to go back to the idle
* state. We wait 1ms to give cards time to
* respond.
+ * mmc_go_idle is needed for eMMC that are asleep
*/
mmc_go_idle(host);
@@ -644,7 +833,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/
if (card->ext_csd.enhanced_area_en) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_ERASE_GROUP_DEF, 1, 0);
+ EXT_CSD_ERASE_GROUP_DEF, 1,
+ card->ext_csd.generic_cmd6_time);
if (err && err != -EBADMSG)
goto free_card;
@@ -682,17 +872,35 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * If the host supports the power_off_notify capability then
+ * set the notification byte in the ext_csd register of device
+ */
+ if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) &&
+ (card->poweroff_notify_state == MMC_NO_POWER_NOTIFICATION)) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_OFF_NOTIFICATION,
+ EXT_CSD_POWER_ON,
+ card->ext_csd.generic_cmd6_time);
+ if (err && err != -EBADMSG)
+ goto free_card;
+ }
+
+ if (!err)
+ card->poweroff_notify_state = MMC_POWERED_ON;
+
+ /*
* Activate high speed (if supported)
*/
if ((card->ext_csd.hs_max_dtr != 0) &&
(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, 1, 0);
+ EXT_CSD_HS_TIMING, 1,
+ card->ext_csd.generic_cmd6_time);
if (err && err != -EBADMSG)
goto free_card;
if (err) {
- printk(KERN_WARNING "%s: switch to highspeed failed\n",
+ pr_warning("%s: switch to highspeed failed\n",
mmc_hostname(card->host));
err = 0;
} else {
@@ -702,6 +910,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * Enable HPI feature (if supported)
+ */
+ if (card->ext_csd.hpi) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HPI_MGMT, 1, 0);
+ if (err && err != -EBADMSG)
+ goto free_card;
+ if (err) {
+ pr_warning("%s: Enabling HPI failed\n",
+ mmc_hostname(card->host));
+ err = 0;
+ } else
+ card->ext_csd.hpi_en = 1;
+ }
+
+ /*
* Compute bus speed.
*/
max_dtr = (unsigned int)-1;
@@ -756,10 +980,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
bus_width = bus_widths[idx];
if (bus_width == MMC_BUS_WIDTH_1)
ddr = 0; /* no DDR for 1-bit width */
+ err = mmc_select_powerclass(card, ext_csd_bits[idx][0],
+ ext_csd);
+ if (err)
+ pr_err("%s: power class selection to "
+ "bus width %d failed\n",
+ mmc_hostname(card->host),
+ 1 << bus_width);
+
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
ext_csd_bits[idx][0],
- 0);
+ card->ext_csd.generic_cmd6_time);
if (!err) {
mmc_set_bus_width(card->host, bus_width);
@@ -770,7 +1002,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/
if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
err = mmc_compare_ext_csds(card,
- ext_csd,
bus_width);
else
err = mmc_bus_test(card, bus_width);
@@ -780,13 +1011,21 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
if (!err && ddr) {
+ err = mmc_select_powerclass(card, ext_csd_bits[idx][1],
+ ext_csd);
+ if (err)
+ pr_err("%s: power class selection to "
+ "bus width %d ddr %d failed\n",
+ mmc_hostname(card->host),
+ 1 << bus_width, ddr);
+
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
ext_csd_bits[idx][1],
- 0);
+ card->ext_csd.generic_cmd6_time);
}
if (err) {
- printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
+ pr_warning("%s: switch to bus width %d ddr %d "
"failed\n", mmc_hostname(card->host),
1 << bus_width, ddr);
goto free_card;
@@ -817,6 +1056,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
}
+ /*
+ * If cache size is higher than 0, this indicates
+ * the existence of cache and it can be turned on.
+ */
+ if ((host->caps2 & MMC_CAP2_CACHE_CTRL) &&
+ card->ext_csd.cache_size > 0) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_CACHE_CTRL, 1, 0);
+ if (err && err != -EBADMSG)
+ goto free_card;
+
+ /*
+ * Only if no error, cache is turned on successfully.
+ */
+ card->ext_csd.cache_ctrl = err ? 0 : 1;
+ }
+
if (!oldcard)
host->card = card;
@@ -868,6 +1124,7 @@ static void mmc_detect(struct mmc_host *host)
mmc_claim_host(host);
mmc_detach_bus(host);
+ mmc_power_off(host);
mmc_release_host(host);
}
}
@@ -877,16 +1134,20 @@ static void mmc_detect(struct mmc_host *host)
*/
static int mmc_suspend(struct mmc_host *host)
{
+ int err = 0;
+
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
- if (!mmc_host_is_spi(host))
+ if (mmc_card_can_sleep(host))
+ err = mmc_card_sleep(host);
+ else if (!mmc_host_is_spi(host))
mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host);
- return 0;
+ return err;
}
/*
@@ -993,6 +1254,10 @@ int mmc_attach_mmc(struct mmc_host *host)
BUG_ON(!host);
WARN_ON(!host->claimed);
+ /* Set correct bus mode for MMC before attempting attach */
+ if (!mmc_host_is_spi(host))
+ mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
+
err = mmc_send_op_cond(host, 0, &ocr);
if (err)
return err;
@@ -1015,7 +1280,7 @@ int mmc_attach_mmc(struct mmc_host *host)
* support.
*/
if (ocr & 0x7F) {
- printk(KERN_WARNING "%s: card claims to support voltages "
+ pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
@@ -1054,7 +1319,7 @@ remove_card:
err:
mmc_detach_bus(host);
- printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
+ pr_err("%s: error %d whilst initialising MMC card\n",
mmc_hostname(host), err);
return err;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 845ce7c533b9..4d41fa984c93 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -10,6 +10,7 @@
*/
#include <linux/slab.h>
+#include <linux/export.h>
#include <linux/types.h>
#include <linux/scatterlist.h>
@@ -233,7 +234,7 @@ static int
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
u32 opcode, void *buf, unsigned len)
{
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct scatterlist sg;
@@ -407,14 +408,14 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
break;
if (mmc_host_is_spi(card->host))
break;
- } while (R1_CURRENT_STATE(status) == 7);
+ } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
if (mmc_host_is_spi(card->host)) {
if (status & R1_SPI_ILLEGAL_COMMAND)
return -EBADMSG;
} else {
if (status & 0xFDFFA000)
- printk(KERN_WARNING "%s: unexpected status %#x after "
+ pr_warning("%s: unexpected status %#x after "
"switch", mmc_hostname(card->host), status);
if (status & R1_SWITCH_ERROR)
return -EBADMSG;
@@ -454,7 +455,7 @@ static int
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
u8 len)
{
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct scatterlist sg;
@@ -476,7 +477,7 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
else if (len == 4)
test_buf = testdata_4bit;
else {
- printk(KERN_ERR "%s: Invalid bus_width %d\n",
+ pr_err("%s: Invalid bus_width %d\n",
mmc_hostname(host), len);
kfree(data_buf);
return -EINVAL;
@@ -547,3 +548,34 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width)
err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width);
return err;
}
+
+int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
+{
+ struct mmc_command cmd = {0};
+ unsigned int opcode;
+ unsigned int flags;
+ int err;
+
+ opcode = card->ext_csd.hpi_cmd;
+ if (opcode == MMC_STOP_TRANSMISSION)
+ flags = MMC_RSP_R1 | MMC_CMD_AC;
+ else if (opcode == MMC_SEND_STATUS)
+ flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ cmd.opcode = opcode;
+ cmd.arg = card->rca << 16 | 1;
+ cmd.flags = flags;
+ cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ pr_warn("%s: error %d interrupting operation. "
+ "HPI command response %#x\n", mmc_hostname(card->host),
+ err, cmd.resp[0]);
+ return err;
+ }
+ if (status)
+ *status = cmd.resp[0];
+
+ return 0;
+}
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 9276946fa5b7..3dd8941c2980 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -26,6 +26,7 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
+int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
#endif
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index 3a596217029e..06ee1aeaacec 100644
--- a/drivers/mmc/core/quirks.c
+++ b/drivers/mmc/core/quirks.c
@@ -11,6 +11,7 @@
#include <linux/types.h>
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/mmc/card.h>
#ifndef SDIO_VENDOR_ID_TI
@@ -21,6 +22,14 @@
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
#endif
+#ifndef SDIO_VENDOR_ID_STE
+#define SDIO_VENDOR_ID_STE 0x0020
+#endif
+
+#ifndef SDIO_DEVICE_ID_STE_CW1200
+#define SDIO_DEVICE_ID_STE_CW1200 0x2280
+#endif
+
/*
* This hook just adds a quirk for all sdio devices
*/
@@ -46,6 +55,9 @@ static const struct mmc_fixup mmc_fixup_methods[] = {
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
add_quirk, MMC_QUIRK_DISABLE_CD),
+ SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,
+ add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512),
+
END_FIXUP
};
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ff2774128aa9..f2a05ea40f2a 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -12,6 +12,7 @@
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/stat.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
@@ -163,7 +164,7 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->erase_size = 1;
break;
default:
- printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
+ pr_err("%s: unrecognised CSD structure version %d\n",
mmc_hostname(card->host), csd_struct);
return -EINVAL;
}
@@ -187,7 +188,7 @@ static int mmc_decode_scr(struct mmc_card *card)
scr_struct = UNSTUFF_BITS(resp, 60, 4);
if (scr_struct != 0) {
- printk(KERN_ERR "%s: unrecognised SCR structure version %d\n",
+ pr_err("%s: unrecognised SCR structure version %d\n",
mmc_hostname(card->host), scr_struct);
return -EINVAL;
}
@@ -218,7 +219,7 @@ static int mmc_read_ssr(struct mmc_card *card)
u32 *ssr;
if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
- printk(KERN_WARNING "%s: card lacks mandatory SD Status "
+ pr_warning("%s: card lacks mandatory SD Status "
"function.\n", mmc_hostname(card->host));
return 0;
}
@@ -229,7 +230,7 @@ static int mmc_read_ssr(struct mmc_card *card)
err = mmc_app_sd_status(card, ssr);
if (err) {
- printk(KERN_WARNING "%s: problem reading SD Status "
+ pr_warning("%s: problem reading SD Status "
"register.\n", mmc_hostname(card->host));
err = 0;
goto out;
@@ -253,7 +254,7 @@ static int mmc_read_ssr(struct mmc_card *card)
card->ssr.erase_offset = eo * 1000;
}
} else {
- printk(KERN_WARNING "%s: SD Status: Invalid Allocation Unit "
+ pr_warning("%s: SD Status: Invalid Allocation Unit "
"size.\n", mmc_hostname(card->host));
}
out:
@@ -273,7 +274,7 @@ static int mmc_read_switch(struct mmc_card *card)
return 0;
if (!(card->csd.cmdclass & CCC_SWITCH)) {
- printk(KERN_WARNING "%s: card lacks mandatory switch "
+ pr_warning("%s: card lacks mandatory switch "
"function, performance might suffer.\n",
mmc_hostname(card->host));
return 0;
@@ -283,7 +284,7 @@ static int mmc_read_switch(struct mmc_card *card)
status = kmalloc(64, GFP_KERNEL);
if (!status) {
- printk(KERN_ERR "%s: could not allocate a buffer for "
+ pr_err("%s: could not allocate a buffer for "
"switch capabilities.\n",
mmc_hostname(card->host));
return -ENOMEM;
@@ -299,13 +300,16 @@ static int mmc_read_switch(struct mmc_card *card)
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
goto out;
- printk(KERN_WARNING "%s: problem reading Bus Speed modes.\n",
+ pr_warning("%s: problem reading Bus Speed modes.\n",
mmc_hostname(card->host));
err = 0;
goto out;
}
+ if (status[13] & UHS_SDR50_BUS_SPEED)
+ card->sw_caps.hs_max_dtr = 50000000;
+
if (card->scr.sda_spec3) {
card->sw_caps.sd3_bus_mode = status[13];
@@ -319,7 +323,7 @@ static int mmc_read_switch(struct mmc_card *card)
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
goto out;
- printk(KERN_WARNING "%s: problem reading "
+ pr_warning("%s: problem reading "
"Driver Strength.\n",
mmc_hostname(card->host));
err = 0;
@@ -339,7 +343,7 @@ static int mmc_read_switch(struct mmc_card *card)
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
goto out;
- printk(KERN_WARNING "%s: problem reading "
+ pr_warning("%s: problem reading "
"Current Limit.\n",
mmc_hostname(card->host));
err = 0;
@@ -348,9 +352,6 @@ static int mmc_read_switch(struct mmc_card *card)
}
card->sw_caps.sd3_curr_limit = status[7];
- } else {
- if (status[13] & 0x02)
- card->sw_caps.hs_max_dtr = 50000000;
}
out:
@@ -383,7 +384,7 @@ int mmc_sd_switch_hs(struct mmc_card *card)
status = kmalloc(64, GFP_KERNEL);
if (!status) {
- printk(KERN_ERR "%s: could not allocate a buffer for "
+ pr_err("%s: could not allocate a buffer for "
"switch capabilities.\n", mmc_hostname(card->host));
return -ENOMEM;
}
@@ -393,7 +394,7 @@ int mmc_sd_switch_hs(struct mmc_card *card)
goto out;
if ((status[16] & 0xF) != 1) {
- printk(KERN_WARNING "%s: Problem switching card "
+ pr_warning("%s: Problem switching card "
"into high-speed mode!\n",
mmc_hostname(card->host));
err = 0;
@@ -409,107 +410,136 @@ out:
static int sd_select_driver_type(struct mmc_card *card, u8 *status)
{
- int host_drv_type = 0, card_drv_type = 0;
+ int host_drv_type = SD_DRIVER_TYPE_B;
+ int card_drv_type = SD_DRIVER_TYPE_B;
+ int drive_strength;
int err;
/*
* If the host doesn't support any of the Driver Types A,C or D,
- * default Driver Type B is used.
+ * or there is no board specific handler then default Driver
+ * Type B is used.
*/
if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C
| MMC_CAP_DRIVER_TYPE_D)))
return 0;
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) {
- host_drv_type = MMC_SET_DRIVER_TYPE_A;
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
- card_drv_type = MMC_SET_DRIVER_TYPE_A;
- else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_B)
- card_drv_type = MMC_SET_DRIVER_TYPE_B;
- else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
- card_drv_type = MMC_SET_DRIVER_TYPE_C;
- } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
- host_drv_type = MMC_SET_DRIVER_TYPE_C;
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
- card_drv_type = MMC_SET_DRIVER_TYPE_C;
- } else if (!(card->host->caps & MMC_CAP_DRIVER_TYPE_D)) {
- /*
- * If we are here, that means only the default driver type
- * B is supported by the host.
- */
- host_drv_type = MMC_SET_DRIVER_TYPE_B;
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_B)
- card_drv_type = MMC_SET_DRIVER_TYPE_B;
- else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
- card_drv_type = MMC_SET_DRIVER_TYPE_C;
- }
+ if (!card->host->ops->select_drive_strength)
+ return 0;
+
+ if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
+ host_drv_type |= SD_DRIVER_TYPE_A;
+
+ if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
+ host_drv_type |= SD_DRIVER_TYPE_C;
+
+ if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
+ host_drv_type |= SD_DRIVER_TYPE_D;
+
+ if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
+ card_drv_type |= SD_DRIVER_TYPE_A;
+
+ if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
+ card_drv_type |= SD_DRIVER_TYPE_C;
+
+ if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
+ card_drv_type |= SD_DRIVER_TYPE_D;
- err = mmc_sd_switch(card, 1, 2, card_drv_type, status);
+ /*
+ * The drive strength that the hardware can support
+ * depends on the board design. Pass the appropriate
+ * information and let the hardware specific code
+ * return what is possible given the options
+ */
+ drive_strength = card->host->ops->select_drive_strength(
+ card->sw_caps.uhs_max_dtr,
+ host_drv_type, card_drv_type);
+
+ err = mmc_sd_switch(card, 1, 2, drive_strength, status);
if (err)
return err;
- if ((status[15] & 0xF) != card_drv_type) {
- printk(KERN_WARNING "%s: Problem setting driver strength!\n",
+ if ((status[15] & 0xF) != drive_strength) {
+ pr_warning("%s: Problem setting drive strength!\n",
mmc_hostname(card->host));
return 0;
}
- mmc_set_driver_type(card->host, host_drv_type);
+ mmc_set_driver_type(card->host, drive_strength);
return 0;
}
-static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
+static void sd_update_bus_speed_mode(struct mmc_card *card)
{
- unsigned int bus_speed = 0, timing = 0;
- int err;
-
/*
* If the host doesn't support any of the UHS-I modes, fallback on
* default speed.
*/
if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
- MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
- return 0;
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))) {
+ card->sd_bus_speed = 0;
+ return;
+ }
if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
- bus_speed = UHS_SDR104_BUS_SPEED;
- timing = MMC_TIMING_UHS_SDR104;
- card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
+ card->sd_bus_speed = UHS_SDR104_BUS_SPEED;
} else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
- bus_speed = UHS_DDR50_BUS_SPEED;
- timing = MMC_TIMING_UHS_DDR50;
- card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+ card->sd_bus_speed = UHS_DDR50_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
SD_MODE_UHS_SDR50)) {
- bus_speed = UHS_SDR50_BUS_SPEED;
- timing = MMC_TIMING_UHS_SDR50;
- card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
+ card->sd_bus_speed = UHS_SDR50_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
- bus_speed = UHS_SDR25_BUS_SPEED;
- timing = MMC_TIMING_UHS_SDR25;
- card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+ card->sd_bus_speed = UHS_SDR25_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
SD_MODE_UHS_SDR12)) {
- bus_speed = UHS_SDR12_BUS_SPEED;
- timing = MMC_TIMING_UHS_SDR12;
- card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
+ card->sd_bus_speed = UHS_SDR12_BUS_SPEED;
+ }
+}
+
+static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
+{
+ int err;
+ unsigned int timing = 0;
+
+ switch (card->sd_bus_speed) {
+ case UHS_SDR104_BUS_SPEED:
+ timing = MMC_TIMING_UHS_SDR104;
+ card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
+ break;
+ case UHS_DDR50_BUS_SPEED:
+ timing = MMC_TIMING_UHS_DDR50;
+ card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+ break;
+ case UHS_SDR50_BUS_SPEED:
+ timing = MMC_TIMING_UHS_SDR50;
+ card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
+ break;
+ case UHS_SDR25_BUS_SPEED:
+ timing = MMC_TIMING_UHS_SDR25;
+ card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+ break;
+ case UHS_SDR12_BUS_SPEED:
+ timing = MMC_TIMING_UHS_SDR12;
+ card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
+ break;
+ default:
+ return 0;
}
- card->sd_bus_speed = bus_speed;
- err = mmc_sd_switch(card, 1, 0, bus_speed, status);
+ err = mmc_sd_switch(card, 1, 0, card->sd_bus_speed, status);
if (err)
return err;
- if ((status[16] & 0xF) != bus_speed)
- printk(KERN_WARNING "%s: Problem setting bus speed mode!\n",
+ if ((status[16] & 0xF) != card->sd_bus_speed)
+ pr_warning("%s: Problem setting bus speed mode!\n",
mmc_hostname(card->host));
else {
mmc_set_timing(card->host, timing);
@@ -571,7 +601,7 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
return err;
if (((status[15] >> 4) & 0x0F) != current_limit)
- printk(KERN_WARNING "%s: Problem setting current limit!\n",
+ pr_warning("%s: Problem setting current limit!\n",
mmc_hostname(card->host));
return 0;
@@ -593,7 +623,7 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
status = kmalloc(64, GFP_KERNEL);
if (!status) {
- printk(KERN_ERR "%s: could not allocate a buffer for "
+ pr_err("%s: could not allocate a buffer for "
"switch capabilities.\n", mmc_hostname(card->host));
return -ENOMEM;
}
@@ -608,18 +638,24 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
}
+ /*
+ * Select the bus speed mode depending on host
+ * and card capability.
+ */
+ sd_update_bus_speed_mode(card);
+
/* Set the driver strength for the card */
err = sd_select_driver_type(card, status);
if (err)
goto out;
- /* Set bus speed mode of the card */
- err = sd_set_bus_speed_mode(card, status);
+ /* Set current limit for the card */
+ err = sd_set_current_limit(card, status);
if (err)
goto out;
- /* Set current limit for the card */
- err = sd_set_current_limit(card, status);
+ /* Set bus speed mode of the card */
+ err = sd_set_bus_speed_mode(card, status);
if (err)
goto out;
@@ -817,7 +853,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
ro = host->ops->get_ro(host);
if (ro < 0) {
- printk(KERN_WARNING "%s: host does not "
+ pr_warning("%s: host does not "
"support reading read-only "
"switch. assuming write-enable.\n",
mmc_hostname(host));
@@ -894,8 +930,6 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
err = mmc_send_relative_addr(host, &card->rca);
if (err)
return err;
-
- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
if (!oldcard) {
@@ -1008,6 +1042,7 @@ static void mmc_sd_detect(struct mmc_host *host)
mmc_claim_host(host);
mmc_detach_bus(host);
+ mmc_power_off(host);
mmc_release_host(host);
}
}
@@ -1132,7 +1167,7 @@ int mmc_attach_sd(struct mmc_host *host)
* support.
*/
if (ocr & 0x7F) {
- printk(KERN_WARNING "%s: card claims to support voltages "
+ pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
@@ -1140,7 +1175,7 @@ int mmc_attach_sd(struct mmc_host *host)
if ((ocr & MMC_VDD_165_195) &&
!(host->ocr_avail_sd & MMC_VDD_165_195)) {
- printk(KERN_WARNING "%s: SD card claims to support the "
+ pr_warning("%s: SD card claims to support the "
"incompletely defined 'low voltage range'. This "
"will be ignored.\n", mmc_hostname(host));
ocr &= ~MMC_VDD_165_195;
@@ -1179,7 +1214,7 @@ remove_card:
err:
mmc_detach_bus(host);
- printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
+ pr_err("%s: error %d whilst initialising SD card\n",
mmc_hostname(host), err);
return err;
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 021fed153804..274ef00b4463 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -11,6 +11,7 @@
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/export.h>
#include <linux/scatterlist.h>
#include <linux/mmc/host.h>
@@ -67,7 +68,7 @@ EXPORT_SYMBOL_GPL(mmc_app_cmd);
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
struct mmc_command *cmd, int retries)
{
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
int i, err;
@@ -244,7 +245,7 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
{
int err;
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct scatterlist sg;
@@ -303,7 +304,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp)
{
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct scatterlist sg;
@@ -348,7 +349,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
int mmc_app_sd_status(struct mmc_card *card, void *ssr)
{
int err;
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct scatterlist sg;
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 262fff019177..3ab565e32a6a 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -111,8 +111,8 @@ static int sdio_read_cccr(struct mmc_card *card)
cccr_vsn = data & 0x0f;
- if (cccr_vsn > SDIO_CCCR_REV_1_20) {
- printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n",
+ if (cccr_vsn > SDIO_CCCR_REV_3_00) {
+ pr_err("%s: unrecognised CCCR structure version %d\n",
mmc_hostname(card->host), cccr_vsn);
return -EINVAL;
}
@@ -408,8 +408,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
*/
if (oldcard)
oldcard->rca = card->rca;
-
- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
/*
@@ -597,6 +595,7 @@ out:
mmc_claim_host(host);
mmc_detach_bus(host);
+ mmc_power_off(host);
mmc_release_host(host);
}
}
@@ -778,7 +777,7 @@ int mmc_attach_sdio(struct mmc_host *host)
* support.
*/
if (ocr & 0x7F) {
- printk(KERN_WARNING "%s: card claims to support voltages "
+ pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
@@ -875,7 +874,7 @@ remove:
err:
mmc_detach_bus(host);
- printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
+ pr_err("%s: error %d whilst initialising SDIO card\n",
mmc_hostname(host), err);
return err;
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index d2565df8a7fb..40989e6bb53a 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
@@ -167,16 +168,13 @@ static int sdio_bus_remove(struct device *dev)
int ret = 0;
/* Make sure card is powered before invoking ->remove() */
- if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) {
- ret = pm_runtime_get_sync(dev);
- if (ret < 0)
- goto out;
- }
+ if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
+ pm_runtime_get_sync(dev);
drv->remove(func);
if (func->irq_handler) {
- printk(KERN_WARNING "WARNING: driver %s did not remove "
+ pr_warning("WARNING: driver %s did not remove "
"its interrupt handler!\n", drv->name);
sdio_claim_host(func);
sdio_release_irq(func);
@@ -191,7 +189,6 @@ static int sdio_bus_remove(struct device *dev)
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_sync(dev);
-out:
return ret;
}
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
index 541bdb89e0c5..f1c7ed8f4d85 100644
--- a/drivers/mmc/core/sdio_cis.c
+++ b/drivers/mmc/core/sdio_cis.c
@@ -132,7 +132,7 @@ static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,
ret = -EINVAL;
}
if (ret && ret != -EILSEQ && ret != -ENOENT) {
- printk(KERN_ERR "%s: bad %s tuple 0x%02x (%u bytes)\n",
+ pr_err("%s: bad %s tuple 0x%02x (%u bytes)\n",
mmc_hostname(card->host), tpl_descr, code, size);
}
} else {
@@ -313,7 +313,7 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
if (ret == -ENOENT) {
/* warn about unknown tuples */
- printk(KERN_WARNING "%s: queuing unknown"
+ pr_warning("%s: queuing unknown"
" CIS tuple 0x%02x (%u bytes)\n",
mmc_hostname(card->host),
tpl_code, tpl_link);
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index 0f687cdeb064..b1f3168f791b 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -9,6 +9,7 @@
* your option) any later version.
*/
+#include <linux/export.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index 03ead028d2ce..68f81b9ee0fb 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/kthread.h>
+#include <linux/export.h>
#include <linux/wait.h>
#include <linux/delay.h>
@@ -45,7 +46,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
if (ret) {
- printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n",
+ pr_debug("%s: error %d reading SDIO_CCCR_INTx\n",
mmc_card_id(card), ret);
return ret;
}
@@ -55,7 +56,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
if (pending & (1 << i)) {
func = card->sdio_func[i - 1];
if (!func) {
- printk(KERN_WARNING "%s: pending IRQ for "
+ pr_warning("%s: pending IRQ for "
"non-existent function\n",
mmc_card_id(card));
ret = -EINVAL;
@@ -63,7 +64,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
func->irq_handler(func);
count++;
} else {
- printk(KERN_WARNING "%s: pending IRQ with no handler\n",
+ pr_warning("%s: pending IRQ with no handler\n",
sdio_func_id(func));
ret = -EINVAL;
}
diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c
index f087d876c573..b0517cc06200 100644
--- a/drivers/mmc/core/sdio_ops.c
+++ b/drivers/mmc/core/sdio_ops.c
@@ -121,7 +121,7 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
{
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct scatterlist sg;
@@ -144,8 +144,11 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
cmd.arg |= fn << 28;
cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
cmd.arg |= addr << 9;
- if (blocks == 1 && blksz <= 512)
- cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */
+ if (blocks == 1 && blksz < 512)
+ cmd.arg |= blksz; /* byte mode */
+ else if (blocks == 1 && blksz == 512 &&
+ !(mmc_card_broken_byte_mode_512(card)))
+ cmd.arg |= 0; /* byte mode, 0==512 */
else
cmd.arg |= 0x08000000 | blocks; /* block mode */
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 56dbf3f6ad08..cf444b0ca2cc 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -81,28 +81,32 @@ config MMC_RICOH_MMC
If unsure, say Y.
-config MMC_SDHCI_OF
- tristate "SDHCI support on OpenFirmware platforms"
- depends on MMC_SDHCI && OF
+config MMC_SDHCI_PLTFM
+ tristate "SDHCI platform and OF driver helper"
+ depends on MMC_SDHCI
help
- This selects the OF support for Secure Digital Host Controller
- Interfaces.
+ This selects the common helper functions support for Secure Digital
+ Host Controller Interface based platform and OF drivers.
+
+ If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_SDHCI_OF_ESDHC
- bool "SDHCI OF support for the Freescale eSDHC controller"
- depends on MMC_SDHCI_OF
+ tristate "SDHCI OF support for the Freescale eSDHC controller"
+ depends on MMC_SDHCI_PLTFM
depends on PPC_OF
select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
help
This selects the Freescale eSDHC controller support.
+ If you have a controller with this interface, say Y or M here.
+
If unsure, say N.
config MMC_SDHCI_OF_HLWD
- bool "SDHCI OF support for the Nintendo Wii SDHCI controllers"
- depends on MMC_SDHCI_OF
+ tristate "SDHCI OF support for the Nintendo Wii SDHCI controllers"
+ depends on MMC_SDHCI_PLTFM
depends on PPC_OF
select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
help
@@ -110,40 +114,36 @@ config MMC_SDHCI_OF_HLWD
found in the "Hollywood" chipset of the Nintendo Wii video game
console.
- If unsure, say N.
-
-config MMC_SDHCI_PLTFM
- tristate "SDHCI support on the platform specific bus"
- depends on MMC_SDHCI
- help
- This selects the platform specific bus support for Secure Digital Host
- Controller Interface.
-
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_SDHCI_CNS3XXX
- bool "SDHCI support on the Cavium Networks CNS3xxx SoC"
+ tristate "SDHCI support on the Cavium Networks CNS3xxx SoC"
depends on ARCH_CNS3XXX
depends on MMC_SDHCI_PLTFM
help
This selects the SDHCI support for CNS3xxx System-on-Chip devices.
+ If you have a controller with this interface, say Y or M here.
+
If unsure, say N.
config MMC_SDHCI_ESDHC_IMX
- bool "SDHCI platform support for the Freescale eSDHC i.MX controller"
- depends on MMC_SDHCI_PLTFM && (ARCH_MX25 || ARCH_MX35 || ARCH_MX5)
+ tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
+ depends on ARCH_MXC
+ depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS
help
- This selects the Freescale eSDHC controller support on the platform
- bus, found on platforms like mx35/51.
+ This selects the Freescale eSDHC/uSDHC controller support
+ found on i.MX25, i.MX35 i.MX5x and i.MX6x.
+
+ If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_SDHCI_DOVE
- bool "SDHCI support on Marvell's Dove SoC"
+ tristate "SDHCI support on Marvell's Dove SoC"
depends on ARCH_DOVE
depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS
@@ -151,11 +151,14 @@ config MMC_SDHCI_DOVE
This selects the Secure Digital Host Controller Interface in
Marvell's Dove SoC.
+ If you have a controller with this interface, say Y or M here.
+
If unsure, say N.
config MMC_SDHCI_TEGRA
- bool "SDHCI platform support for the Tegra SD/MMC Controller"
- depends on MMC_SDHCI_PLTFM && ARCH_TEGRA
+ tristate "SDHCI platform support for the Tegra SD/MMC Controller"
+ depends on ARCH_TEGRA
+ depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS
help
This selects the Tegra SD/MMC controller. If you have a Tegra
@@ -178,14 +181,28 @@ config MMC_SDHCI_S3C
If unsure, say N.
-config MMC_SDHCI_PXA
- tristate "Marvell PXA168/PXA910/MMP2 SD Host Controller support"
- depends on ARCH_PXA || ARCH_MMP
+config MMC_SDHCI_PXAV3
+ tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
+ depends on CLKDEV_LOOKUP
select MMC_SDHCI
- select MMC_SDHCI_IO_ACCESSORS
+ select MMC_SDHCI_PLTFM
+ default CPU_MMP2
help
- This selects the Marvell(R) PXA168/PXA910/MMP2 SD Host Controller.
- If you have a PXA168/PXA910/MMP2 platform with SD Host Controller
+ This selects the Marvell(R) PXAV3 SD Host Controller.
+ If you have a MMP2 platform with SD Host Controller
+ and a card slot, say Y or M here.
+
+ If unsure, say N.
+
+config MMC_SDHCI_PXAV2
+ tristate "Marvell PXA9XX SD Host Controller support (PXAV2)"
+ depends on CLKDEV_LOOKUP
+ select MMC_SDHCI
+ select MMC_SDHCI_PLTFM
+ default CPU_PXA910
+ help
+ This selects the Marvell(R) PXAV2 SD Host Controller.
+ If you have a PXA9XX platform with SD Host Controller
and a card slot, say Y or M here.
If unsure, say N.
@@ -246,7 +263,7 @@ config MMC_WBSD
config MMC_AU1X
tristate "Alchemy AU1XX0 MMC Card Interface support"
- depends on SOC_AU1200
+ depends on MIPS_ALCHEMY
help
This selects the AMD Alchemy(R) Multimedia card interface.
If you have a Alchemy platform with a MMC slot, say Y or M here.
@@ -281,13 +298,12 @@ config MMC_ATMELMCI
endchoice
config MMC_ATMELMCI_DMA
- bool "Atmel MCI DMA support (EXPERIMENTAL)"
- depends on MMC_ATMELMCI && (AVR32 || ARCH_AT91SAM9G45) && DMA_ENGINE && EXPERIMENTAL
+ bool "Atmel MCI DMA support"
+ depends on MMC_ATMELMCI && (AVR32 || ARCH_AT91SAM9G45) && DMA_ENGINE
help
Say Y here to have the Atmel MCI driver use a DMA engine to
do data transfers and thus increase the throughput and
- reduce the CPU utilization. Note that this is highly
- experimental and may cause the driver to lock up.
+ reduce the CPU utilization.
If unsure, say N.
@@ -310,11 +326,11 @@ config MMC_MSM
support for SDIO devices.
config MMC_MXC
- tristate "Freescale i.MX2/3 Multimedia Card Interface support"
- depends on MACH_MX21 || MACH_MX27 || ARCH_MX31
+ tristate "Freescale i.MX21/27/31 Multimedia Card Interface support"
+ depends on ARCH_MXC
help
- This selects the Freescale i.MX2/3 Multimedia card Interface.
- If you have a i.MX platform with a Multimedia Card slot,
+ This selects the Freescale i.MX21, i.MX27 and i.MX31 Multimedia card
+ Interface. If you have a i.MX platform with a Multimedia Card slot,
say Y or M here.
If unsure, say N.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 58a5cf73d6e9..b4b83f302e32 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -9,7 +9,8 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
-obj-$(CONFIG_MMC_SDHCI_PXA) += sdhci-pxa.o
+obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
+obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
@@ -31,9 +32,7 @@ obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
obj-$(CONFIG_MMC_TMIO_CORE) += tmio_mmc_core.o
tmio_mmc_core-y := tmio_mmc_pio.o
-ifneq ($(CONFIG_MMC_SDHI),n)
-tmio_mmc_core-y += tmio_mmc_dma.o
-endif
+tmio_mmc_core-$(subst m,y,$(CONFIG_MMC_SDHI)) += tmio_mmc_dma.o
obj-$(CONFIG_MMC_SDHI) += sh_mobile_sdhi.o
obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
@@ -44,17 +43,13 @@ obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o
obj-$(CONFIG_MMC_USHC) += ushc.o
-obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o
-sdhci-platform-y := sdhci-pltfm.o
-sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
-sdhci-platform-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
-sdhci-platform-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
-sdhci-platform-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
-
-obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
-sdhci-of-y := sdhci-of-core.o
-sdhci-of-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
-sdhci-of-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
+obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
+obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
+obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
+obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
+obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
+obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
+obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index d3e6a962f423..a8b4d2aa18e5 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -77,7 +77,8 @@
#include <mach/board.h>
#include <mach/cpu.h>
-#include <mach/at91_mci.h>
+
+#include "at91_mci.h"
#define DRIVER_NAME "at91_mci"
@@ -868,7 +869,11 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
{
struct at91mci_host *host = _host;
- int present = !gpio_get_value(irq_to_gpio(irq));
+ int present;
+
+ /* entering this ISR means that we have configured det_pin:
+ * we can use its value in board structure */
+ present = !gpio_get_value(host->board->det_pin);
/*
* we expect this irq on both insert and remove,
diff --git a/drivers/mmc/host/at91_mci.h b/drivers/mmc/host/at91_mci.h
new file mode 100644
index 000000000000..eec3a6b1c2bc
--- /dev/null
+++ b/drivers/mmc/host/at91_mci.h
@@ -0,0 +1,115 @@
+/*
+ * drivers/mmc/host/at91_mci.h
+ *
+ * Copyright (C) 2005 Ivan Kokshaysky
+ * Copyright (C) SAN People
+ *
+ * MultiMedia Card Interface (MCI) registers.
+ * Based on AT91RM9200 datasheet revision F.
+ *
+ * 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 AT91_MCI_H
+#define AT91_MCI_H
+
+#define AT91_MCI_CR 0x00 /* Control Register */
+#define AT91_MCI_MCIEN (1 << 0) /* Multi-Media Interface Enable */
+#define AT91_MCI_MCIDIS (1 << 1) /* Multi-Media Interface Disable */
+#define AT91_MCI_PWSEN (1 << 2) /* Power Save Mode Enable */
+#define AT91_MCI_PWSDIS (1 << 3) /* Power Save Mode Disable */
+#define AT91_MCI_SWRST (1 << 7) /* Software Reset */
+
+#define AT91_MCI_MR 0x04 /* Mode Register */
+#define AT91_MCI_CLKDIV (0xff << 0) /* Clock Divider */
+#define AT91_MCI_PWSDIV (7 << 8) /* Power Saving Divider */
+#define AT91_MCI_RDPROOF (1 << 11) /* Read Proof Enable [SAM926[03] only] */
+#define AT91_MCI_WRPROOF (1 << 12) /* Write Proof Enable [SAM926[03] only] */
+#define AT91_MCI_PDCFBYTE (1 << 13) /* PDC Force Byte Transfer [SAM926[03] only] */
+#define AT91_MCI_PDCPADV (1 << 14) /* PDC Padding Value */
+#define AT91_MCI_PDCMODE (1 << 15) /* PDC-orientated Mode */
+#define AT91_MCI_BLKLEN (0xfff << 18) /* Data Block Length */
+
+#define AT91_MCI_DTOR 0x08 /* Data Timeout Register */
+#define AT91_MCI_DTOCYC (0xf << 0) /* Data Timeout Cycle Number */
+#define AT91_MCI_DTOMUL (7 << 4) /* Data Timeout Multiplier */
+#define AT91_MCI_DTOMUL_1 (0 << 4)
+#define AT91_MCI_DTOMUL_16 (1 << 4)
+#define AT91_MCI_DTOMUL_128 (2 << 4)
+#define AT91_MCI_DTOMUL_256 (3 << 4)
+#define AT91_MCI_DTOMUL_1K (4 << 4)
+#define AT91_MCI_DTOMUL_4K (5 << 4)
+#define AT91_MCI_DTOMUL_64K (6 << 4)
+#define AT91_MCI_DTOMUL_1M (7 << 4)
+
+#define AT91_MCI_SDCR 0x0c /* SD Card Register */
+#define AT91_MCI_SDCSEL (3 << 0) /* SD Card Selector */
+#define AT91_MCI_SDCBUS (1 << 7) /* 1-bit or 4-bit bus */
+
+#define AT91_MCI_ARGR 0x10 /* Argument Register */
+
+#define AT91_MCI_CMDR 0x14 /* Command Register */
+#define AT91_MCI_CMDNB (0x3f << 0) /* Command Number */
+#define AT91_MCI_RSPTYP (3 << 6) /* Response Type */
+#define AT91_MCI_RSPTYP_NONE (0 << 6)
+#define AT91_MCI_RSPTYP_48 (1 << 6)
+#define AT91_MCI_RSPTYP_136 (2 << 6)
+#define AT91_MCI_SPCMD (7 << 8) /* Special Command */
+#define AT91_MCI_SPCMD_NONE (0 << 8)
+#define AT91_MCI_SPCMD_INIT (1 << 8)
+#define AT91_MCI_SPCMD_SYNC (2 << 8)
+#define AT91_MCI_SPCMD_ICMD (4 << 8)
+#define AT91_MCI_SPCMD_IRESP (5 << 8)
+#define AT91_MCI_OPDCMD (1 << 11) /* Open Drain Command */
+#define AT91_MCI_MAXLAT (1 << 12) /* Max Latency for Command to Response */
+#define AT91_MCI_TRCMD (3 << 16) /* Transfer Command */
+#define AT91_MCI_TRCMD_NONE (0 << 16)
+#define AT91_MCI_TRCMD_START (1 << 16)
+#define AT91_MCI_TRCMD_STOP (2 << 16)
+#define AT91_MCI_TRDIR (1 << 18) /* Transfer Direction */
+#define AT91_MCI_TRTYP (3 << 19) /* Transfer Type */
+#define AT91_MCI_TRTYP_BLOCK (0 << 19)
+#define AT91_MCI_TRTYP_MULTIPLE (1 << 19)
+#define AT91_MCI_TRTYP_STREAM (2 << 19)
+#define AT91_MCI_TRTYP_SDIO_BYTE (4 << 19)
+#define AT91_MCI_TRTYP_SDIO_BLOCK (5 << 19)
+
+#define AT91_MCI_BLKR 0x18 /* Block Register */
+#define AT91_MCI_BLKR_BCNT(n) ((0xffff & (n)) << 0) /* Block count */
+#define AT91_MCI_BLKR_BLKLEN(n) ((0xffff & (n)) << 16) /* Block length */
+
+#define AT91_MCI_RSPR(n) (0x20 + ((n) * 4)) /* Response Registers 0-3 */
+#define AT91_MCR_RDR 0x30 /* Receive Data Register */
+#define AT91_MCR_TDR 0x34 /* Transmit Data Register */
+
+#define AT91_MCI_SR 0x40 /* Status Register */
+#define AT91_MCI_CMDRDY (1 << 0) /* Command Ready */
+#define AT91_MCI_RXRDY (1 << 1) /* Receiver Ready */
+#define AT91_MCI_TXRDY (1 << 2) /* Transmit Ready */
+#define AT91_MCI_BLKE (1 << 3) /* Data Block Ended */
+#define AT91_MCI_DTIP (1 << 4) /* Data Transfer in Progress */
+#define AT91_MCI_NOTBUSY (1 << 5) /* Data Not Busy */
+#define AT91_MCI_ENDRX (1 << 6) /* End of RX Buffer */
+#define AT91_MCI_ENDTX (1 << 7) /* End fo TX Buffer */
+#define AT91_MCI_SDIOIRQA (1 << 8) /* SDIO Interrupt for Slot A */
+#define AT91_MCI_SDIOIRQB (1 << 9) /* SDIO Interrupt for Slot B */
+#define AT91_MCI_RXBUFF (1 << 14) /* RX Buffer Full */
+#define AT91_MCI_TXBUFE (1 << 15) /* TX Buffer Empty */
+#define AT91_MCI_RINDE (1 << 16) /* Response Index Error */
+#define AT91_MCI_RDIRE (1 << 17) /* Response Direction Error */
+#define AT91_MCI_RCRCE (1 << 18) /* Response CRC Error */
+#define AT91_MCI_RENDE (1 << 19) /* Response End Bit Error */
+#define AT91_MCI_RTOE (1 << 20) /* Response Time-out Error */
+#define AT91_MCI_DCRCE (1 << 21) /* Data CRC Error */
+#define AT91_MCI_DTOE (1 << 22) /* Data Time-out Error */
+#define AT91_MCI_OVRE (1 << 30) /* Overrun */
+#define AT91_MCI_UNRE (1 << 31) /* Underrun */
+
+#define AT91_MCI_IER 0x44 /* Interrupt Enable Register */
+#define AT91_MCI_IDR 0x48 /* Interrupt Disable Register */
+#define AT91_MCI_IMR 0x4c /* Interrupt Mask Register */
+
+#endif
diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h
index fc8a0fe7c5c5..000b3ad0f5ca 100644
--- a/drivers/mmc/host/atmel-mci-regs.h
+++ b/drivers/mmc/host/atmel-mci-regs.h
@@ -17,112 +17,126 @@
#define __DRIVERS_MMC_ATMEL_MCI_H__
/* MCI Register Definitions */
-#define MCI_CR 0x0000 /* Control */
-# define MCI_CR_MCIEN ( 1 << 0) /* MCI Enable */
-# define MCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */
-# define MCI_CR_PWSEN ( 1 << 2) /* Power Save Enable */
-# define MCI_CR_PWSDIS ( 1 << 3) /* Power Save Disable */
-# define MCI_CR_SWRST ( 1 << 7) /* Software Reset */
-#define MCI_MR 0x0004 /* Mode */
-# define MCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */
-# define MCI_MR_PWSDIV(x) ((x) << 8) /* Power Saving Divider */
-# define MCI_MR_RDPROOF ( 1 << 11) /* Read Proof */
-# define MCI_MR_WRPROOF ( 1 << 12) /* Write Proof */
-# define MCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */
-# define MCI_MR_PDCPADV ( 1 << 14) /* Padding Value */
-# define MCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */
-#define MCI_DTOR 0x0008 /* Data Timeout */
-# define MCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
-# define MCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */
-#define MCI_SDCR 0x000c /* SD Card / SDIO */
-# define MCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */
-# define MCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */
-# define MCI_SDCSEL_MASK ( 3 << 0)
-# define MCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */
-# define MCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */
-# define MCI_SDCBUS_8BIT ( 3 << 6) /* 8-bit data bus[2] */
-# define MCI_SDCBUS_MASK ( 3 << 6)
-#define MCI_ARGR 0x0010 /* Command Argument */
-#define MCI_CMDR 0x0014 /* Command */
-# define MCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */
-# define MCI_CMDR_RSPTYP_NONE ( 0 << 6) /* No response */
-# define MCI_CMDR_RSPTYP_48BIT ( 1 << 6) /* 48-bit response */
-# define MCI_CMDR_RSPTYP_136BIT ( 2 << 6) /* 136-bit response */
-# define MCI_CMDR_SPCMD_INIT ( 1 << 8) /* Initialization command */
-# define MCI_CMDR_SPCMD_SYNC ( 2 << 8) /* Synchronized command */
-# define MCI_CMDR_SPCMD_INT ( 4 << 8) /* Interrupt command */
-# define MCI_CMDR_SPCMD_INTRESP ( 5 << 8) /* Interrupt response */
-# define MCI_CMDR_OPDCMD ( 1 << 11) /* Open Drain */
-# define MCI_CMDR_MAXLAT_5CYC ( 0 << 12) /* Max latency 5 cycles */
-# define MCI_CMDR_MAXLAT_64CYC ( 1 << 12) /* Max latency 64 cycles */
-# define MCI_CMDR_START_XFER ( 1 << 16) /* Start data transfer */
-# define MCI_CMDR_STOP_XFER ( 2 << 16) /* Stop data transfer */
-# define MCI_CMDR_TRDIR_WRITE ( 0 << 18) /* Write data */
-# define MCI_CMDR_TRDIR_READ ( 1 << 18) /* Read data */
-# define MCI_CMDR_BLOCK ( 0 << 19) /* Single-block transfer */
-# define MCI_CMDR_MULTI_BLOCK ( 1 << 19) /* Multi-block transfer */
-# define MCI_CMDR_STREAM ( 2 << 19) /* MMC Stream transfer */
-# define MCI_CMDR_SDIO_BYTE ( 4 << 19) /* SDIO Byte transfer */
-# define MCI_CMDR_SDIO_BLOCK ( 5 << 19) /* SDIO Block transfer */
-# define MCI_CMDR_SDIO_SUSPEND ( 1 << 24) /* SDIO Suspend Command */
-# define MCI_CMDR_SDIO_RESUME ( 2 << 24) /* SDIO Resume Command */
-#define MCI_BLKR 0x0018 /* Block */
-# define MCI_BCNT(x) ((x) << 0) /* Data Block Count */
-# define MCI_BLKLEN(x) ((x) << 16) /* Data Block Length */
-#define MCI_CSTOR 0x001c /* Completion Signal Timeout[2] */
-# define MCI_CSTOCYC(x) ((x) << 0) /* CST cycles */
-# define MCI_CSTOMUL(x) ((x) << 4) /* CST multiplier */
-#define MCI_RSPR 0x0020 /* Response 0 */
-#define MCI_RSPR1 0x0024 /* Response 1 */
-#define MCI_RSPR2 0x0028 /* Response 2 */
-#define MCI_RSPR3 0x002c /* Response 3 */
-#define MCI_RDR 0x0030 /* Receive Data */
-#define MCI_TDR 0x0034 /* Transmit Data */
-#define MCI_SR 0x0040 /* Status */
-#define MCI_IER 0x0044 /* Interrupt Enable */
-#define MCI_IDR 0x0048 /* Interrupt Disable */
-#define MCI_IMR 0x004c /* Interrupt Mask */
-# define MCI_CMDRDY ( 1 << 0) /* Command Ready */
-# define MCI_RXRDY ( 1 << 1) /* Receiver Ready */
-# define MCI_TXRDY ( 1 << 2) /* Transmitter Ready */
-# define MCI_BLKE ( 1 << 3) /* Data Block Ended */
-# define MCI_DTIP ( 1 << 4) /* Data Transfer In Progress */
-# define MCI_NOTBUSY ( 1 << 5) /* Data Not Busy */
-# define MCI_SDIOIRQA ( 1 << 8) /* SDIO IRQ in slot A */
-# define MCI_SDIOIRQB ( 1 << 9) /* SDIO IRQ in slot B */
-# define MCI_RINDE ( 1 << 16) /* Response Index Error */
-# define MCI_RDIRE ( 1 << 17) /* Response Direction Error */
-# define MCI_RCRCE ( 1 << 18) /* Response CRC Error */
-# define MCI_RENDE ( 1 << 19) /* Response End Bit Error */
-# define MCI_RTOE ( 1 << 20) /* Response Time-Out Error */
-# define MCI_DCRCE ( 1 << 21) /* Data CRC Error */
-# define MCI_DTOE ( 1 << 22) /* Data Time-Out Error */
-# define MCI_OVRE ( 1 << 30) /* RX Overrun Error */
-# define MCI_UNRE ( 1 << 31) /* TX Underrun Error */
-#define MCI_DMA 0x0050 /* DMA Configuration[2] */
-# define MCI_DMA_OFFSET(x) ((x) << 0) /* DMA Write Buffer Offset */
-# define MCI_DMA_CHKSIZE(x) ((x) << 4) /* DMA Channel Read and Write Chunk Size */
-# define MCI_DMAEN ( 1 << 8) /* DMA Hardware Handshaking Enable */
-#define MCI_CFG 0x0054 /* Configuration[2] */
-# define MCI_CFG_FIFOMODE_1DATA ( 1 << 0) /* MCI Internal FIFO control mode */
-# define MCI_CFG_FERRCTRL_COR ( 1 << 4) /* Flow Error flag reset control mode */
-# define MCI_CFG_HSMODE ( 1 << 8) /* High Speed Mode */
-# define MCI_CFG_LSYNC ( 1 << 12) /* Synchronize on the last block */
-#define MCI_WPMR 0x00e4 /* Write Protection Mode[2] */
-# define MCI_WP_EN ( 1 << 0) /* WP Enable */
-# define MCI_WP_KEY (0x4d4349 << 8) /* WP Key */
-#define MCI_WPSR 0x00e8 /* Write Protection Status[2] */
-# define MCI_GET_WP_VS(x) ((x) & 0x0f)
-# define MCI_GET_WP_VSRC(x) (((x) >> 8) & 0xffff)
-#define MCI_FIFO_APERTURE 0x0200 /* FIFO Aperture[2] */
+#define ATMCI_CR 0x0000 /* Control */
+# define ATMCI_CR_MCIEN ( 1 << 0) /* MCI Enable */
+# define ATMCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */
+# define ATMCI_CR_PWSEN ( 1 << 2) /* Power Save Enable */
+# define ATMCI_CR_PWSDIS ( 1 << 3) /* Power Save Disable */
+# define ATMCI_CR_SWRST ( 1 << 7) /* Software Reset */
+#define ATMCI_MR 0x0004 /* Mode */
+# define ATMCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */
+# define ATMCI_MR_PWSDIV(x) ((x) << 8) /* Power Saving Divider */
+# define ATMCI_MR_RDPROOF ( 1 << 11) /* Read Proof */
+# define ATMCI_MR_WRPROOF ( 1 << 12) /* Write Proof */
+# define ATMCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */
+# define ATMCI_MR_PDCPADV ( 1 << 14) /* Padding Value */
+# define ATMCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */
+#define ATMCI_DTOR 0x0008 /* Data Timeout */
+# define ATMCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
+# define ATMCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */
+#define ATMCI_SDCR 0x000c /* SD Card / SDIO */
+# define ATMCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */
+# define ATMCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */
+# define ATMCI_SDCSEL_MASK ( 3 << 0)
+# define ATMCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */
+# define ATMCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */
+# define ATMCI_SDCBUS_8BIT ( 3 << 6) /* 8-bit data bus[2] */
+# define ATMCI_SDCBUS_MASK ( 3 << 6)
+#define ATMCI_ARGR 0x0010 /* Command Argument */
+#define ATMCI_CMDR 0x0014 /* Command */
+# define ATMCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */
+# define ATMCI_CMDR_RSPTYP_NONE ( 0 << 6) /* No response */
+# define ATMCI_CMDR_RSPTYP_48BIT ( 1 << 6) /* 48-bit response */
+# define ATMCI_CMDR_RSPTYP_136BIT ( 2 << 6) /* 136-bit response */
+# define ATMCI_CMDR_SPCMD_INIT ( 1 << 8) /* Initialization command */
+# define ATMCI_CMDR_SPCMD_SYNC ( 2 << 8) /* Synchronized command */
+# define ATMCI_CMDR_SPCMD_INT ( 4 << 8) /* Interrupt command */
+# define ATMCI_CMDR_SPCMD_INTRESP ( 5 << 8) /* Interrupt response */
+# define ATMCI_CMDR_OPDCMD ( 1 << 11) /* Open Drain */
+# define ATMCI_CMDR_MAXLAT_5CYC ( 0 << 12) /* Max latency 5 cycles */
+# define ATMCI_CMDR_MAXLAT_64CYC ( 1 << 12) /* Max latency 64 cycles */
+# define ATMCI_CMDR_START_XFER ( 1 << 16) /* Start data transfer */
+# define ATMCI_CMDR_STOP_XFER ( 2 << 16) /* Stop data transfer */
+# define ATMCI_CMDR_TRDIR_WRITE ( 0 << 18) /* Write data */
+# define ATMCI_CMDR_TRDIR_READ ( 1 << 18) /* Read data */
+# define ATMCI_CMDR_BLOCK ( 0 << 19) /* Single-block transfer */
+# define ATMCI_CMDR_MULTI_BLOCK ( 1 << 19) /* Multi-block transfer */
+# define ATMCI_CMDR_STREAM ( 2 << 19) /* MMC Stream transfer */
+# define ATMCI_CMDR_SDIO_BYTE ( 4 << 19) /* SDIO Byte transfer */
+# define ATMCI_CMDR_SDIO_BLOCK ( 5 << 19) /* SDIO Block transfer */
+# define ATMCI_CMDR_SDIO_SUSPEND ( 1 << 24) /* SDIO Suspend Command */
+# define ATMCI_CMDR_SDIO_RESUME ( 2 << 24) /* SDIO Resume Command */
+#define ATMCI_BLKR 0x0018 /* Block */
+# define ATMCI_BCNT(x) ((x) << 0) /* Data Block Count */
+# define ATMCI_BLKLEN(x) ((x) << 16) /* Data Block Length */
+#define ATMCI_CSTOR 0x001c /* Completion Signal Timeout[2] */
+# define ATMCI_CSTOCYC(x) ((x) << 0) /* CST cycles */
+# define ATMCI_CSTOMUL(x) ((x) << 4) /* CST multiplier */
+#define ATMCI_RSPR 0x0020 /* Response 0 */
+#define ATMCI_RSPR1 0x0024 /* Response 1 */
+#define ATMCI_RSPR2 0x0028 /* Response 2 */
+#define ATMCI_RSPR3 0x002c /* Response 3 */
+#define ATMCI_RDR 0x0030 /* Receive Data */
+#define ATMCI_TDR 0x0034 /* Transmit Data */
+#define ATMCI_SR 0x0040 /* Status */
+#define ATMCI_IER 0x0044 /* Interrupt Enable */
+#define ATMCI_IDR 0x0048 /* Interrupt Disable */
+#define ATMCI_IMR 0x004c /* Interrupt Mask */
+# define ATMCI_CMDRDY ( 1 << 0) /* Command Ready */
+# define ATMCI_RXRDY ( 1 << 1) /* Receiver Ready */
+# define ATMCI_TXRDY ( 1 << 2) /* Transmitter Ready */
+# define ATMCI_BLKE ( 1 << 3) /* Data Block Ended */
+# define ATMCI_DTIP ( 1 << 4) /* Data Transfer In Progress */
+# define ATMCI_NOTBUSY ( 1 << 5) /* Data Not Busy */
+# define ATMCI_ENDRX ( 1 << 6) /* End of RX Buffer */
+# define ATMCI_ENDTX ( 1 << 7) /* End of TX Buffer */
+# define ATMCI_SDIOIRQA ( 1 << 8) /* SDIO IRQ in slot A */
+# define ATMCI_SDIOIRQB ( 1 << 9) /* SDIO IRQ in slot B */
+# define ATMCI_SDIOWAIT ( 1 << 12) /* SDIO Read Wait Operation Status */
+# define ATMCI_CSRCV ( 1 << 13) /* CE-ATA Completion Signal Received */
+# define ATMCI_RXBUFF ( 1 << 14) /* RX Buffer Full */
+# define ATMCI_TXBUFE ( 1 << 15) /* TX Buffer Empty */
+# define ATMCI_RINDE ( 1 << 16) /* Response Index Error */
+# define ATMCI_RDIRE ( 1 << 17) /* Response Direction Error */
+# define ATMCI_RCRCE ( 1 << 18) /* Response CRC Error */
+# define ATMCI_RENDE ( 1 << 19) /* Response End Bit Error */
+# define ATMCI_RTOE ( 1 << 20) /* Response Time-Out Error */
+# define ATMCI_DCRCE ( 1 << 21) /* Data CRC Error */
+# define ATMCI_DTOE ( 1 << 22) /* Data Time-Out Error */
+# define ATMCI_CSTOE ( 1 << 23) /* Completion Signal Time-out Error */
+# define ATMCI_BLKOVRE ( 1 << 24) /* DMA Block Overrun Error */
+# define ATMCI_DMADONE ( 1 << 25) /* DMA Transfer Done */
+# define ATMCI_FIFOEMPTY ( 1 << 26) /* FIFO Empty Flag */
+# define ATMCI_XFRDONE ( 1 << 27) /* Transfer Done Flag */
+# define ATMCI_ACKRCV ( 1 << 28) /* Boot Operation Acknowledge Received */
+# define ATMCI_ACKRCVE ( 1 << 29) /* Boot Operation Acknowledge Error */
+# define ATMCI_OVRE ( 1 << 30) /* RX Overrun Error */
+# define ATMCI_UNRE ( 1 << 31) /* TX Underrun Error */
+#define ATMCI_DMA 0x0050 /* DMA Configuration[2] */
+# define ATMCI_DMA_OFFSET(x) ((x) << 0) /* DMA Write Buffer Offset */
+# define ATMCI_DMA_CHKSIZE(x) ((x) << 4) /* DMA Channel Read and Write Chunk Size */
+# define ATMCI_DMAEN ( 1 << 8) /* DMA Hardware Handshaking Enable */
+#define ATMCI_CFG 0x0054 /* Configuration[2] */
+# define ATMCI_CFG_FIFOMODE_1DATA ( 1 << 0) /* MCI Internal FIFO control mode */
+# define ATMCI_CFG_FERRCTRL_COR ( 1 << 4) /* Flow Error flag reset control mode */
+# define ATMCI_CFG_HSMODE ( 1 << 8) /* High Speed Mode */
+# define ATMCI_CFG_LSYNC ( 1 << 12) /* Synchronize on the last block */
+#define ATMCI_WPMR 0x00e4 /* Write Protection Mode[2] */
+# define ATMCI_WP_EN ( 1 << 0) /* WP Enable */
+# define ATMCI_WP_KEY (0x4d4349 << 8) /* WP Key */
+#define ATMCI_WPSR 0x00e8 /* Write Protection Status[2] */
+# define ATMCI_GET_WP_VS(x) ((x) & 0x0f)
+# define ATMCI_GET_WP_VSRC(x) (((x) >> 8) & 0xffff)
+#define ATMCI_VERSION 0x00FC /* Version */
+#define ATMCI_FIFO_APERTURE 0x0200 /* FIFO Aperture[2] */
/* This is not including the FIFO Aperture on MCI2 */
-#define MCI_REGS_SIZE 0x100
+#define ATMCI_REGS_SIZE 0x100
/* Register access macros */
-#define mci_readl(port,reg) \
- __raw_readl((port)->regs + MCI_##reg)
-#define mci_writel(port,reg,value) \
- __raw_writel((value), (port)->regs + MCI_##reg)
+#define atmci_readl(port,reg) \
+ __raw_readl((port)->regs + reg)
+#define atmci_writel(port,reg,value) \
+ __raw_writel((value), (port)->regs + reg)
#endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index aa8039f473c4..a7ee50271465 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -30,6 +30,7 @@
#include <mach/atmel-mci.h>
#include <linux/atmel-mci.h>
+#include <linux/atmel_pdc.h>
#include <asm/io.h>
#include <asm/unaligned.h>
@@ -39,7 +40,7 @@
#include "atmel-mci-regs.h"
-#define ATMCI_DATA_ERROR_FLAGS (MCI_DCRCE | MCI_DTOE | MCI_OVRE | MCI_UNRE)
+#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
#define ATMCI_DMA_THRESHOLD 16
enum {
@@ -58,18 +59,35 @@ enum atmel_mci_state {
STATE_DATA_ERROR,
};
+enum atmci_xfer_dir {
+ XFER_RECEIVE = 0,
+ XFER_TRANSMIT,
+};
+
+enum atmci_pdc_buf {
+ PDC_FIRST_BUF = 0,
+ PDC_SECOND_BUF,
+};
+
+struct atmel_mci_caps {
+ bool has_dma;
+ bool has_pdc;
+ bool has_cfg_reg;
+ bool has_cstor_reg;
+ bool has_highspeed;
+ bool has_rwproof;
+};
+
struct atmel_mci_dma {
-#ifdef CONFIG_MMC_ATMELMCI_DMA
struct dma_chan *chan;
struct dma_async_tx_descriptor *data_desc;
-#endif
};
/**
* struct atmel_mci - MMC controller state shared between all slots
* @lock: Spinlock protecting the queue and associated data.
* @regs: Pointer to MMIO registers.
- * @sg: Scatterlist entry currently being processed by PIO code, if any.
+ * @sg: Scatterlist entry currently being processed by PIO or PDC code.
* @pio_offset: Offset into the current scatterlist entry.
* @cur_slot: The slot which is currently using the controller.
* @mrq: The request currently being processed on @cur_slot,
@@ -77,6 +95,7 @@ struct atmel_mci_dma {
* @cmd: The command currently being sent to the card, or NULL.
* @data: The data currently being transferred, or NULL if no data
* transfer is in progress.
+ * @data_size: just data->blocks * data->blksz.
* @dma: DMA client state.
* @data_chan: DMA channel being used for the current data transfer.
* @cmd_status: Snapshot of SR taken upon completion of the current
@@ -103,6 +122,13 @@ struct atmel_mci_dma {
* @mck: The peripheral bus clock hooked up to the MMC controller.
* @pdev: Platform device associated with the MMC controller.
* @slot: Slots sharing this MMC controller.
+ * @caps: MCI capabilities depending on MCI version.
+ * @prepare_data: function to setup MCI before data transfer which
+ * depends on MCI capabilities.
+ * @submit_data: function to start data transfer which depends on MCI
+ * capabilities.
+ * @stop_transfer: function to stop data transfer which depends on MCI
+ * capabilities.
*
* Locking
* =======
@@ -143,6 +169,7 @@ struct atmel_mci {
struct mmc_request *mrq;
struct mmc_command *cmd;
struct mmc_data *data;
+ unsigned int data_size;
struct atmel_mci_dma dma;
struct dma_chan *data_chan;
@@ -166,7 +193,13 @@ struct atmel_mci {
struct clk *mck;
struct platform_device *pdev;
- struct atmel_mci_slot *slot[ATMEL_MCI_MAX_NR_SLOTS];
+ struct atmel_mci_slot *slot[ATMCI_MAX_NR_SLOTS];
+
+ struct atmel_mci_caps caps;
+
+ u32 (*prepare_data)(struct atmel_mci *host, struct mmc_data *data);
+ void (*submit_data)(struct atmel_mci *host, struct mmc_data *data);
+ void (*stop_transfer)(struct atmel_mci *host);
};
/**
@@ -203,6 +236,7 @@ struct atmel_mci_slot {
#define ATMCI_CARD_PRESENT 0
#define ATMCI_CARD_NEED_INIT 1
#define ATMCI_SHUTDOWN 2
+#define ATMCI_SUSPENDED 3
int detect_pin;
int wp_pin;
@@ -219,31 +253,6 @@ struct atmel_mci_slot {
set_bit(event, &host->pending_events)
/*
- * Enable or disable features/registers based on
- * whether the processor supports them
- */
-static bool mci_has_rwproof(void)
-{
- if (cpu_is_at91sam9261() || cpu_is_at91rm9200())
- return false;
- else
- return true;
-}
-
-/*
- * The new MCI2 module isn't 100% compatible with the old MCI module,
- * and it has a few nice features which we want to use...
- */
-static inline bool atmci_is_mci2(void)
-{
- if (cpu_is_at91sam9g45())
- return true;
-
- return false;
-}
-
-
-/*
* The debugfs stuff below is mostly optimized away when
* CONFIG_DEBUG_FS is not set.
*/
@@ -351,7 +360,7 @@ static int atmci_regs_show(struct seq_file *s, void *v)
struct atmel_mci *host = s->private;
u32 *buf;
- buf = kmalloc(MCI_REGS_SIZE, GFP_KERNEL);
+ buf = kmalloc(ATMCI_REGS_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -362,47 +371,50 @@ static int atmci_regs_show(struct seq_file *s, void *v)
*/
spin_lock_bh(&host->lock);
clk_enable(host->mck);
- memcpy_fromio(buf, host->regs, MCI_REGS_SIZE);
+ memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE);
clk_disable(host->mck);
spin_unlock_bh(&host->lock);
seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
- buf[MCI_MR / 4],
- buf[MCI_MR / 4] & MCI_MR_RDPROOF ? " RDPROOF" : "",
- buf[MCI_MR / 4] & MCI_MR_WRPROOF ? " WRPROOF" : "",
- buf[MCI_MR / 4] & 0xff);
- seq_printf(s, "DTOR:\t0x%08x\n", buf[MCI_DTOR / 4]);
- seq_printf(s, "SDCR:\t0x%08x\n", buf[MCI_SDCR / 4]);
- seq_printf(s, "ARGR:\t0x%08x\n", buf[MCI_ARGR / 4]);
+ buf[ATMCI_MR / 4],
+ buf[ATMCI_MR / 4] & ATMCI_MR_RDPROOF ? " RDPROOF" : "",
+ buf[ATMCI_MR / 4] & ATMCI_MR_WRPROOF ? " WRPROOF" : "",
+ buf[ATMCI_MR / 4] & 0xff);
+ seq_printf(s, "DTOR:\t0x%08x\n", buf[ATMCI_DTOR / 4]);
+ seq_printf(s, "SDCR:\t0x%08x\n", buf[ATMCI_SDCR / 4]);
+ seq_printf(s, "ARGR:\t0x%08x\n", buf[ATMCI_ARGR / 4]);
seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n",
- buf[MCI_BLKR / 4],
- buf[MCI_BLKR / 4] & 0xffff,
- (buf[MCI_BLKR / 4] >> 16) & 0xffff);
- if (atmci_is_mci2())
- seq_printf(s, "CSTOR:\t0x%08x\n", buf[MCI_CSTOR / 4]);
+ buf[ATMCI_BLKR / 4],
+ buf[ATMCI_BLKR / 4] & 0xffff,
+ (buf[ATMCI_BLKR / 4] >> 16) & 0xffff);
+ if (host->caps.has_cstor_reg)
+ seq_printf(s, "CSTOR:\t0x%08x\n", buf[ATMCI_CSTOR / 4]);
/* Don't read RSPR and RDR; it will consume the data there */
- atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
- atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
+ atmci_show_status_reg(s, "SR", buf[ATMCI_SR / 4]);
+ atmci_show_status_reg(s, "IMR", buf[ATMCI_IMR / 4]);
- if (atmci_is_mci2()) {
+ if (host->caps.has_dma) {
u32 val;
- val = buf[MCI_DMA / 4];
+ val = buf[ATMCI_DMA / 4];
seq_printf(s, "DMA:\t0x%08x OFFSET=%u CHKSIZE=%u%s\n",
val, val & 3,
((val >> 4) & 3) ?
1 << (((val >> 4) & 3) + 1) : 1,
- val & MCI_DMAEN ? " DMAEN" : "");
+ val & ATMCI_DMAEN ? " DMAEN" : "");
+ }
+ if (host->caps.has_cfg_reg) {
+ u32 val;
- val = buf[MCI_CFG / 4];
+ val = buf[ATMCI_CFG / 4];
seq_printf(s, "CFG:\t0x%08x%s%s%s%s\n",
val,
- val & MCI_CFG_FIFOMODE_1DATA ? " FIFOMODE_ONE_DATA" : "",
- val & MCI_CFG_FERRCTRL_COR ? " FERRCTRL_CLEAR_ON_READ" : "",
- val & MCI_CFG_HSMODE ? " HSMODE" : "",
- val & MCI_CFG_LSYNC ? " LSYNC" : "");
+ val & ATMCI_CFG_FIFOMODE_1DATA ? " FIFOMODE_ONE_DATA" : "",
+ val & ATMCI_CFG_FERRCTRL_COR ? " FERRCTRL_CLEAR_ON_READ" : "",
+ val & ATMCI_CFG_HSMODE ? " HSMODE" : "",
+ val & ATMCI_CFG_LSYNC ? " LSYNC" : "");
}
kfree(buf);
@@ -465,7 +477,7 @@ err:
dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
}
-static inline unsigned int ns_to_clocks(struct atmel_mci *host,
+static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
unsigned int ns)
{
return (ns * (host->bus_hz / 1000000) + 999) / 1000;
@@ -481,7 +493,8 @@ static void atmci_set_timeout(struct atmel_mci *host,
unsigned dtocyc;
unsigned dtomul;
- timeout = ns_to_clocks(host, data->timeout_ns) + data->timeout_clks;
+ timeout = atmci_ns_to_clocks(host, data->timeout_ns)
+ + data->timeout_clks;
for (dtomul = 0; dtomul < 8; dtomul++) {
unsigned shift = dtomul_to_shift[dtomul];
@@ -497,7 +510,7 @@ static void atmci_set_timeout(struct atmel_mci *host,
dev_vdbg(&slot->mmc->class_dev, "setting timeout to %u cycles\n",
dtocyc << dtomul_to_shift[dtomul]);
- mci_writel(host, DTOR, (MCI_DTOMUL(dtomul) | MCI_DTOCYC(dtocyc)));
+ atmci_writel(host, ATMCI_DTOR, (ATMCI_DTOMUL(dtomul) | ATMCI_DTOCYC(dtocyc)));
}
/*
@@ -511,13 +524,13 @@ static u32 atmci_prepare_command(struct mmc_host *mmc,
cmd->error = -EINPROGRESS;
- cmdr = MCI_CMDR_CMDNB(cmd->opcode);
+ cmdr = ATMCI_CMDR_CMDNB(cmd->opcode);
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136)
- cmdr |= MCI_CMDR_RSPTYP_136BIT;
+ cmdr |= ATMCI_CMDR_RSPTYP_136BIT;
else
- cmdr |= MCI_CMDR_RSPTYP_48BIT;
+ cmdr |= ATMCI_CMDR_RSPTYP_48BIT;
}
/*
@@ -525,34 +538,34 @@ static u32 atmci_prepare_command(struct mmc_host *mmc,
* it's too difficult to determine whether this is an ACMD or
* not. Better make it 64.
*/
- cmdr |= MCI_CMDR_MAXLAT_64CYC;
+ cmdr |= ATMCI_CMDR_MAXLAT_64CYC;
if (mmc->ios.bus_mode == MMC_BUSMODE_OPENDRAIN)
- cmdr |= MCI_CMDR_OPDCMD;
+ cmdr |= ATMCI_CMDR_OPDCMD;
data = cmd->data;
if (data) {
- cmdr |= MCI_CMDR_START_XFER;
+ cmdr |= ATMCI_CMDR_START_XFER;
if (cmd->opcode == SD_IO_RW_EXTENDED) {
- cmdr |= MCI_CMDR_SDIO_BLOCK;
+ cmdr |= ATMCI_CMDR_SDIO_BLOCK;
} else {
if (data->flags & MMC_DATA_STREAM)
- cmdr |= MCI_CMDR_STREAM;
+ cmdr |= ATMCI_CMDR_STREAM;
else if (data->blocks > 1)
- cmdr |= MCI_CMDR_MULTI_BLOCK;
+ cmdr |= ATMCI_CMDR_MULTI_BLOCK;
else
- cmdr |= MCI_CMDR_BLOCK;
+ cmdr |= ATMCI_CMDR_BLOCK;
}
if (data->flags & MMC_DATA_READ)
- cmdr |= MCI_CMDR_TRDIR_READ;
+ cmdr |= ATMCI_CMDR_TRDIR_READ;
}
return cmdr;
}
-static void atmci_start_command(struct atmel_mci *host,
+static void atmci_send_command(struct atmel_mci *host,
struct mmc_command *cmd, u32 cmd_flags)
{
WARN_ON(host->cmd);
@@ -562,43 +575,119 @@ static void atmci_start_command(struct atmel_mci *host,
"start command: ARGR=0x%08x CMDR=0x%08x\n",
cmd->arg, cmd_flags);
- mci_writel(host, ARGR, cmd->arg);
- mci_writel(host, CMDR, cmd_flags);
+ atmci_writel(host, ATMCI_ARGR, cmd->arg);
+ atmci_writel(host, ATMCI_CMDR, cmd_flags);
}
-static void send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
+static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
{
- atmci_start_command(host, data->stop, host->stop_cmdr);
- mci_writel(host, IER, MCI_CMDRDY);
+ atmci_send_command(host, data->stop, host->stop_cmdr);
+ atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
}
-#ifdef CONFIG_MMC_ATMELMCI_DMA
-static void atmci_dma_cleanup(struct atmel_mci *host)
+/*
+ * Configure given PDC buffer taking care of alignement issues.
+ * Update host->data_size and host->sg.
+ */
+static void atmci_pdc_set_single_buf(struct atmel_mci *host,
+ enum atmci_xfer_dir dir, enum atmci_pdc_buf buf_nb)
{
- struct mmc_data *data = host->data;
+ u32 pointer_reg, counter_reg;
+
+ if (dir == XFER_RECEIVE) {
+ pointer_reg = ATMEL_PDC_RPR;
+ counter_reg = ATMEL_PDC_RCR;
+ } else {
+ pointer_reg = ATMEL_PDC_TPR;
+ counter_reg = ATMEL_PDC_TCR;
+ }
+
+ if (buf_nb == PDC_SECOND_BUF) {
+ pointer_reg += ATMEL_PDC_SCND_BUF_OFF;
+ counter_reg += ATMEL_PDC_SCND_BUF_OFF;
+ }
+
+ atmci_writel(host, pointer_reg, sg_dma_address(host->sg));
+ if (host->data_size <= sg_dma_len(host->sg)) {
+ if (host->data_size & 0x3) {
+ /* If size is different from modulo 4, transfer bytes */
+ atmci_writel(host, counter_reg, host->data_size);
+ atmci_writel(host, ATMCI_MR, host->mode_reg | ATMCI_MR_PDCFBYTE);
+ } else {
+ /* Else transfer 32-bits words */
+ atmci_writel(host, counter_reg, host->data_size / 4);
+ }
+ host->data_size = 0;
+ } else {
+ /* We assume the size of a page is 32-bits aligned */
+ atmci_writel(host, counter_reg, sg_dma_len(host->sg) / 4);
+ host->data_size -= sg_dma_len(host->sg);
+ if (host->data_size)
+ host->sg = sg_next(host->sg);
+ }
+}
+
+/*
+ * Configure PDC buffer according to the data size ie configuring one or two
+ * buffers. Don't use this function if you want to configure only the second
+ * buffer. In this case, use atmci_pdc_set_single_buf.
+ */
+static void atmci_pdc_set_both_buf(struct atmel_mci *host, int dir)
+{
+ atmci_pdc_set_single_buf(host, dir, PDC_FIRST_BUF);
+ if (host->data_size)
+ atmci_pdc_set_single_buf(host, dir, PDC_SECOND_BUF);
+}
+
+/*
+ * Unmap sg lists, called when transfer is finished.
+ */
+static void atmci_pdc_cleanup(struct atmel_mci *host)
+{
+ struct mmc_data *data = host->data;
if (data)
- dma_unmap_sg(host->dma.chan->device->dev,
- data->sg, data->sg_len,
- ((data->flags & MMC_DATA_WRITE)
- ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+ dma_unmap_sg(&host->pdev->dev,
+ data->sg, data->sg_len,
+ ((data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
}
-static void atmci_stop_dma(struct atmel_mci *host)
+/*
+ * Disable PDC transfers. Update pending flags to EVENT_XFER_COMPLETE after
+ * having received ATMCI_TXBUFE or ATMCI_RXBUFF interrupt. Enable ATMCI_NOTBUSY
+ * interrupt needed for both transfer directions.
+ */
+static void atmci_pdc_complete(struct atmel_mci *host)
{
- struct dma_chan *chan = host->data_chan;
+ atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+ atmci_pdc_cleanup(host);
- if (chan) {
- dmaengine_terminate_all(chan);
- atmci_dma_cleanup(host);
- } else {
- /* Data transfer was stopped by the interrupt handler */
+ /*
+ * If the card was removed, data will be NULL. No point trying
+ * to send the stop command or waiting for NBUSY in this case.
+ */
+ if (host->data) {
atmci_set_pending(host, EVENT_XFER_COMPLETE);
- mci_writel(host, IER, MCI_NOTBUSY);
+ tasklet_schedule(&host->tasklet);
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
}
}
-/* This function is called by the DMA driver from tasklet context. */
+static void atmci_dma_cleanup(struct atmel_mci *host)
+{
+ struct mmc_data *data = host->data;
+
+ if (data)
+ dma_unmap_sg(host->dma.chan->device->dev,
+ data->sg, data->sg_len,
+ ((data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+}
+
+/*
+ * This function is called by the DMA driver from tasklet context.
+ */
static void atmci_dma_complete(void *arg)
{
struct atmel_mci *host = arg;
@@ -606,9 +695,9 @@ static void atmci_dma_complete(void *arg)
dev_vdbg(&host->pdev->dev, "DMA complete\n");
- if (atmci_is_mci2())
+ if (host->caps.has_dma)
/* Disable DMA hardware handshaking on MCI */
- mci_writel(host, DMA, mci_readl(host, DMA) & ~MCI_DMAEN);
+ atmci_writel(host, ATMCI_DMA, atmci_readl(host, ATMCI_DMA) & ~ATMCI_DMAEN);
atmci_dma_cleanup(host);
@@ -640,11 +729,93 @@ static void atmci_dma_complete(void *arg)
* completion callback" rule of the dma engine
* framework.
*/
- mci_writel(host, IER, MCI_NOTBUSY);
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
+ }
+}
+
+/*
+ * Returns a mask of interrupt flags to be enabled after the whole
+ * request has been prepared.
+ */
+static u32 atmci_prepare_data(struct atmel_mci *host, struct mmc_data *data)
+{
+ u32 iflags;
+
+ data->error = -EINPROGRESS;
+
+ host->sg = data->sg;
+ host->data = data;
+ host->data_chan = NULL;
+
+ iflags = ATMCI_DATA_ERROR_FLAGS;
+
+ /*
+ * Errata: MMC data write operation with less than 12
+ * bytes is impossible.
+ *
+ * Errata: MCI Transmit Data Register (TDR) FIFO
+ * corruption when length is not multiple of 4.
+ */
+ if (data->blocks * data->blksz < 12
+ || (data->blocks * data->blksz) & 3)
+ host->need_reset = true;
+
+ host->pio_offset = 0;
+ if (data->flags & MMC_DATA_READ)
+ iflags |= ATMCI_RXRDY;
+ else
+ iflags |= ATMCI_TXRDY;
+
+ return iflags;
+}
+
+/*
+ * Set interrupt flags and set block length into the MCI mode register even
+ * if this value is also accessible in the MCI block register. It seems to be
+ * necessary before the High Speed MCI version. It also map sg and configure
+ * PDC registers.
+ */
+static u32
+atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
+{
+ u32 iflags, tmp;
+ unsigned int sg_len;
+ enum dma_data_direction dir;
+
+ data->error = -EINPROGRESS;
+
+ host->data = data;
+ host->sg = data->sg;
+ iflags = ATMCI_DATA_ERROR_FLAGS;
+
+ /* Enable pdc mode */
+ atmci_writel(host, ATMCI_MR, host->mode_reg | ATMCI_MR_PDCMODE);
+
+ if (data->flags & MMC_DATA_READ) {
+ dir = DMA_FROM_DEVICE;
+ iflags |= ATMCI_ENDRX | ATMCI_RXBUFF;
+ } else {
+ dir = DMA_TO_DEVICE;
+ iflags |= ATMCI_ENDTX | ATMCI_TXBUFE;
}
+
+ /* Set BLKLEN */
+ tmp = atmci_readl(host, ATMCI_MR);
+ tmp &= 0x0000ffff;
+ tmp |= ATMCI_BLKLEN(data->blksz);
+ atmci_writel(host, ATMCI_MR, tmp);
+
+ /* Configure PDC */
+ host->data_size = data->blocks * data->blksz;
+ sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, dir);
+ if (host->data_size)
+ atmci_pdc_set_both_buf(host,
+ ((dir == DMA_FROM_DEVICE) ? XFER_RECEIVE : XFER_TRANSMIT));
+
+ return iflags;
}
-static int
+static u32
atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
{
struct dma_chan *chan;
@@ -653,6 +824,15 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
unsigned int i;
enum dma_data_direction direction;
unsigned int sglen;
+ u32 iflags;
+
+ data->error = -EINPROGRESS;
+
+ WARN_ON(host->data);
+ host->sg = NULL;
+ host->data = data;
+
+ iflags = ATMCI_DATA_ERROR_FLAGS;
/*
* We don't do DMA on "complex" transfers, i.e. with
@@ -660,13 +840,13 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
* with all the DMA setup overhead for short transfers.
*/
if (data->blocks * data->blksz < ATMCI_DMA_THRESHOLD)
- return -EINVAL;
+ return atmci_prepare_data(host, data);
if (data->blksz & 3)
- return -EINVAL;
+ return atmci_prepare_data(host, data);
for_each_sg(data->sg, sg, data->sg_len, i) {
if (sg->offset & 3 || sg->length & 3)
- return -EINVAL;
+ return atmci_prepare_data(host, data);
}
/* If we don't have a channel, we can't do DMA */
@@ -677,8 +857,8 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
if (!chan)
return -ENODEV;
- if (atmci_is_mci2())
- mci_writel(host, DMA, MCI_DMA_CHKSIZE(3) | MCI_DMAEN);
+ if (host->caps.has_dma)
+ atmci_writel(host, ATMCI_DMA, ATMCI_DMA_CHKSIZE(3) | ATMCI_DMAEN);
if (data->flags & MMC_DATA_READ)
direction = DMA_FROM_DEVICE;
@@ -686,7 +866,7 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
direction = DMA_TO_DEVICE;
sglen = dma_map_sg(chan->device->dev, data->sg,
- data->sg_len, direction);
+ data->sg_len, direction);
desc = chan->device->device_prep_slave_sg(chan,
data->sg, sglen, direction,
@@ -698,13 +878,32 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
desc->callback = atmci_dma_complete;
desc->callback_param = host;
- return 0;
+ return iflags;
unmap_exit:
dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, direction);
return -ENOMEM;
}
-static void atmci_submit_data(struct atmel_mci *host)
+static void
+atmci_submit_data(struct atmel_mci *host, struct mmc_data *data)
+{
+ return;
+}
+
+/*
+ * Start PDC according to transfer direction.
+ */
+static void
+atmci_submit_data_pdc(struct atmel_mci *host, struct mmc_data *data)
+{
+ if (data->flags & MMC_DATA_READ)
+ atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN);
+ else
+ atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
+}
+
+static void
+atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
{
struct dma_chan *chan = host->data_chan;
struct dma_async_tx_descriptor *desc = host->dma.data_desc;
@@ -715,64 +914,39 @@ static void atmci_submit_data(struct atmel_mci *host)
}
}
-#else /* CONFIG_MMC_ATMELMCI_DMA */
-
-static int atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
+static void atmci_stop_transfer(struct atmel_mci *host)
{
- return -ENOSYS;
-}
-
-static void atmci_submit_data(struct atmel_mci *host) {}
-
-static void atmci_stop_dma(struct atmel_mci *host)
-{
- /* Data transfer was stopped by the interrupt handler */
atmci_set_pending(host, EVENT_XFER_COMPLETE);
- mci_writel(host, IER, MCI_NOTBUSY);
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
}
-#endif /* CONFIG_MMC_ATMELMCI_DMA */
-
/*
- * Returns a mask of interrupt flags to be enabled after the whole
- * request has been prepared.
+ * Stop data transfer because error(s) occured.
*/
-static u32 atmci_prepare_data(struct atmel_mci *host, struct mmc_data *data)
+static void atmci_stop_transfer_pdc(struct atmel_mci *host)
{
- u32 iflags;
-
- data->error = -EINPROGRESS;
-
- WARN_ON(host->data);
- host->sg = NULL;
- host->data = data;
-
- iflags = ATMCI_DATA_ERROR_FLAGS;
- if (atmci_prepare_data_dma(host, data)) {
- host->data_chan = NULL;
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
+}
- /*
- * Errata: MMC data write operation with less than 12
- * bytes is impossible.
- *
- * Errata: MCI Transmit Data Register (TDR) FIFO
- * corruption when length is not multiple of 4.
- */
- if (data->blocks * data->blksz < 12
- || (data->blocks * data->blksz) & 3)
- host->need_reset = true;
+static void atmci_stop_transfer_dma(struct atmel_mci *host)
+{
+ struct dma_chan *chan = host->data_chan;
- host->sg = data->sg;
- host->pio_offset = 0;
- if (data->flags & MMC_DATA_READ)
- iflags |= MCI_RXRDY;
- else
- iflags |= MCI_TXRDY;
+ if (chan) {
+ dmaengine_terminate_all(chan);
+ atmci_dma_cleanup(host);
+ } else {
+ /* Data transfer was stopped by the interrupt handler */
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
}
-
- return iflags;
}
+/*
+ * Start a request: prepare data if needed, prepare the command and activate
+ * interrupts.
+ */
static void atmci_start_request(struct atmel_mci *host,
struct atmel_mci_slot *slot)
{
@@ -791,24 +965,24 @@ static void atmci_start_request(struct atmel_mci *host,
host->data_status = 0;
if (host->need_reset) {
- mci_writel(host, CR, MCI_CR_SWRST);
- mci_writel(host, CR, MCI_CR_MCIEN);
- mci_writel(host, MR, host->mode_reg);
- if (atmci_is_mci2())
- mci_writel(host, CFG, host->cfg_reg);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
+ atmci_writel(host, ATMCI_MR, host->mode_reg);
+ if (host->caps.has_cfg_reg)
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
host->need_reset = false;
}
- mci_writel(host, SDCR, slot->sdc_reg);
+ atmci_writel(host, ATMCI_SDCR, slot->sdc_reg);
- iflags = mci_readl(host, IMR);
- if (iflags & ~(MCI_SDIOIRQA | MCI_SDIOIRQB))
+ iflags = atmci_readl(host, ATMCI_IMR);
+ if (iflags & ~(ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
iflags);
if (unlikely(test_and_clear_bit(ATMCI_CARD_NEED_INIT, &slot->flags))) {
/* Send init sequence (74 clock cycles) */
- mci_writel(host, CMDR, MCI_CMDR_SPCMD_INIT);
- while (!(mci_readl(host, SR) & MCI_CMDRDY))
+ atmci_writel(host, ATMCI_CMDR, ATMCI_CMDR_SPCMD_INIT);
+ while (!(atmci_readl(host, ATMCI_SR) & ATMCI_CMDRDY))
cpu_relax();
}
iflags = 0;
@@ -817,31 +991,31 @@ static void atmci_start_request(struct atmel_mci *host,
atmci_set_timeout(host, slot, data);
/* Must set block count/size before sending command */
- mci_writel(host, BLKR, MCI_BCNT(data->blocks)
- | MCI_BLKLEN(data->blksz));
+ atmci_writel(host, ATMCI_BLKR, ATMCI_BCNT(data->blocks)
+ | ATMCI_BLKLEN(data->blksz));
dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n",
- MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz));
+ ATMCI_BCNT(data->blocks) | ATMCI_BLKLEN(data->blksz));
- iflags |= atmci_prepare_data(host, data);
+ iflags |= host->prepare_data(host, data);
}
- iflags |= MCI_CMDRDY;
+ iflags |= ATMCI_CMDRDY;
cmd = mrq->cmd;
cmdflags = atmci_prepare_command(slot->mmc, cmd);
- atmci_start_command(host, cmd, cmdflags);
+ atmci_send_command(host, cmd, cmdflags);
if (data)
- atmci_submit_data(host);
+ host->submit_data(host, data);
if (mrq->stop) {
host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
- host->stop_cmdr |= MCI_CMDR_STOP_XFER;
+ host->stop_cmdr |= ATMCI_CMDR_STOP_XFER;
if (!(data->flags & MMC_DATA_WRITE))
- host->stop_cmdr |= MCI_CMDR_TRDIR_READ;
+ host->stop_cmdr |= ATMCI_CMDR_TRDIR_READ;
if (data->flags & MMC_DATA_STREAM)
- host->stop_cmdr |= MCI_CMDR_STREAM;
+ host->stop_cmdr |= ATMCI_CMDR_STREAM;
else
- host->stop_cmdr |= MCI_CMDR_MULTI_BLOCK;
+ host->stop_cmdr |= ATMCI_CMDR_MULTI_BLOCK;
}
/*
@@ -850,7 +1024,7 @@ static void atmci_start_request(struct atmel_mci *host,
* conditions (e.g. command and data complete, but stop not
* prepared yet.)
*/
- mci_writel(host, IER, iflags);
+ atmci_writel(host, ATMCI_IER, iflags);
}
static void atmci_queue_request(struct atmel_mci *host,
@@ -908,13 +1082,13 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
struct atmel_mci *host = slot->host;
unsigned int i;
- slot->sdc_reg &= ~MCI_SDCBUS_MASK;
+ slot->sdc_reg &= ~ATMCI_SDCBUS_MASK;
switch (ios->bus_width) {
case MMC_BUS_WIDTH_1:
- slot->sdc_reg |= MCI_SDCBUS_1BIT;
+ slot->sdc_reg |= ATMCI_SDCBUS_1BIT;
break;
case MMC_BUS_WIDTH_4:
- slot->sdc_reg |= MCI_SDCBUS_4BIT;
+ slot->sdc_reg |= ATMCI_SDCBUS_4BIT;
break;
}
@@ -925,10 +1099,10 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_lock_bh(&host->lock);
if (!host->mode_reg) {
clk_enable(host->mck);
- mci_writel(host, CR, MCI_CR_SWRST);
- mci_writel(host, CR, MCI_CR_MCIEN);
- if (atmci_is_mci2())
- mci_writel(host, CFG, host->cfg_reg);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
+ if (host->caps.has_cfg_reg)
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
}
/*
@@ -936,7 +1110,7 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* core ios update when finding the minimum.
*/
slot->clock = ios->clock;
- for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
if (host->slot[i] && host->slot[i]->clock
&& host->slot[i]->clock < clock_min)
clock_min = host->slot[i]->clock;
@@ -951,28 +1125,28 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clkdiv = 255;
}
- host->mode_reg = MCI_MR_CLKDIV(clkdiv);
+ host->mode_reg = ATMCI_MR_CLKDIV(clkdiv);
/*
* WRPROOF and RDPROOF prevent overruns/underruns by
* stopping the clock when the FIFO is full/empty.
* This state is not expected to last for long.
*/
- if (mci_has_rwproof())
- host->mode_reg |= (MCI_MR_WRPROOF | MCI_MR_RDPROOF);
+ if (host->caps.has_rwproof)
+ host->mode_reg |= (ATMCI_MR_WRPROOF | ATMCI_MR_RDPROOF);
- if (atmci_is_mci2()) {
+ if (host->caps.has_cfg_reg) {
/* setup High Speed mode in relation with card capacity */
if (ios->timing == MMC_TIMING_SD_HS)
- host->cfg_reg |= MCI_CFG_HSMODE;
+ host->cfg_reg |= ATMCI_CFG_HSMODE;
else
- host->cfg_reg &= ~MCI_CFG_HSMODE;
+ host->cfg_reg &= ~ATMCI_CFG_HSMODE;
}
if (list_empty(&host->queue)) {
- mci_writel(host, MR, host->mode_reg);
- if (atmci_is_mci2())
- mci_writel(host, CFG, host->cfg_reg);
+ atmci_writel(host, ATMCI_MR, host->mode_reg);
+ if (host->caps.has_cfg_reg)
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
} else {
host->need_clock_update = true;
}
@@ -983,16 +1157,16 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_lock_bh(&host->lock);
slot->clock = 0;
- for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
if (host->slot[i] && host->slot[i]->clock) {
any_slot_active = true;
break;
}
}
if (!any_slot_active) {
- mci_writel(host, CR, MCI_CR_MCIDIS);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
if (host->mode_reg) {
- mci_readl(host, MR);
+ atmci_readl(host, ATMCI_MR);
clk_disable(host->mck);
}
host->mode_reg = 0;
@@ -1056,9 +1230,9 @@ static void atmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
struct atmel_mci *host = slot->host;
if (enable)
- mci_writel(host, IER, slot->sdio_irq);
+ atmci_writel(host, ATMCI_IER, slot->sdio_irq);
else
- mci_writel(host, IDR, slot->sdio_irq);
+ atmci_writel(host, ATMCI_IDR, slot->sdio_irq);
}
static const struct mmc_host_ops atmci_ops = {
@@ -1085,9 +1259,9 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
* busy transferring data.
*/
if (host->need_clock_update) {
- mci_writel(host, MR, host->mode_reg);
- if (atmci_is_mci2())
- mci_writel(host, CFG, host->cfg_reg);
+ atmci_writel(host, ATMCI_MR, host->mode_reg);
+ if (host->caps.has_cfg_reg)
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
}
host->cur_slot->mrq = NULL;
@@ -1116,16 +1290,16 @@ static void atmci_command_complete(struct atmel_mci *host,
u32 status = host->cmd_status;
/* Read the response from the card (up to 16 bytes) */
- cmd->resp[0] = mci_readl(host, RSPR);
- cmd->resp[1] = mci_readl(host, RSPR);
- cmd->resp[2] = mci_readl(host, RSPR);
- cmd->resp[3] = mci_readl(host, RSPR);
+ cmd->resp[0] = atmci_readl(host, ATMCI_RSPR);
+ cmd->resp[1] = atmci_readl(host, ATMCI_RSPR);
+ cmd->resp[2] = atmci_readl(host, ATMCI_RSPR);
+ cmd->resp[3] = atmci_readl(host, ATMCI_RSPR);
- if (status & MCI_RTOE)
+ if (status & ATMCI_RTOE)
cmd->error = -ETIMEDOUT;
- else if ((cmd->flags & MMC_RSP_CRC) && (status & MCI_RCRCE))
+ else if ((cmd->flags & MMC_RSP_CRC) && (status & ATMCI_RCRCE))
cmd->error = -EILSEQ;
- else if (status & (MCI_RINDE | MCI_RDIRE | MCI_RENDE))
+ else if (status & (ATMCI_RINDE | ATMCI_RDIRE | ATMCI_RENDE))
cmd->error = -EIO;
else
cmd->error = 0;
@@ -1135,10 +1309,10 @@ static void atmci_command_complete(struct atmel_mci *host,
"command error: status=0x%08x\n", status);
if (cmd->data) {
- atmci_stop_dma(host);
+ host->stop_transfer(host);
host->data = NULL;
- mci_writel(host, IDR, MCI_NOTBUSY
- | MCI_TXRDY | MCI_RXRDY
+ atmci_writel(host, ATMCI_IDR, ATMCI_NOTBUSY
+ | ATMCI_TXRDY | ATMCI_RXRDY
| ATMCI_DATA_ERROR_FLAGS);
}
}
@@ -1190,11 +1364,11 @@ static void atmci_detect_change(unsigned long data)
* Reset controller to terminate any ongoing
* commands or data transfers.
*/
- mci_writel(host, CR, MCI_CR_SWRST);
- mci_writel(host, CR, MCI_CR_MCIEN);
- mci_writel(host, MR, host->mode_reg);
- if (atmci_is_mci2())
- mci_writel(host, CFG, host->cfg_reg);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
+ atmci_writel(host, ATMCI_MR, host->mode_reg);
+ if (host->caps.has_cfg_reg)
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
host->data = NULL;
host->cmd = NULL;
@@ -1209,7 +1383,7 @@ static void atmci_detect_change(unsigned long data)
/* fall through */
case STATE_SENDING_DATA:
mrq->data->error = -ENOMEDIUM;
- atmci_stop_dma(host);
+ host->stop_transfer(host);
break;
case STATE_DATA_BUSY:
case STATE_DATA_ERROR:
@@ -1260,7 +1434,7 @@ static void atmci_tasklet_func(unsigned long priv)
dev_vdbg(&host->pdev->dev,
"tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
state, host->pending_events, host->completed_events,
- mci_readl(host, IMR));
+ atmci_readl(host, ATMCI_IMR));
do {
prev_state = state;
@@ -1288,9 +1462,9 @@ static void atmci_tasklet_func(unsigned long priv)
case STATE_SENDING_DATA:
if (atmci_test_and_clear_pending(host,
EVENT_DATA_ERROR)) {
- atmci_stop_dma(host);
+ host->stop_transfer(host);
if (data->stop)
- send_stop_cmd(host, data);
+ atmci_send_stop_cmd(host, data);
state = STATE_DATA_ERROR;
break;
}
@@ -1312,11 +1486,11 @@ static void atmci_tasklet_func(unsigned long priv)
atmci_set_completed(host, EVENT_DATA_COMPLETE);
status = host->data_status;
if (unlikely(status & ATMCI_DATA_ERROR_FLAGS)) {
- if (status & MCI_DTOE) {
+ if (status & ATMCI_DTOE) {
dev_dbg(&host->pdev->dev,
"data timeout error\n");
data->error = -ETIMEDOUT;
- } else if (status & MCI_DCRCE) {
+ } else if (status & ATMCI_DCRCE) {
dev_dbg(&host->pdev->dev,
"data CRC error\n");
data->error = -EILSEQ;
@@ -1329,7 +1503,7 @@ static void atmci_tasklet_func(unsigned long priv)
} else {
data->bytes_xfered = data->blocks * data->blksz;
data->error = 0;
- mci_writel(host, IDR, ATMCI_DATA_ERROR_FLAGS);
+ atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS);
}
if (!data->stop) {
@@ -1339,7 +1513,7 @@ static void atmci_tasklet_func(unsigned long priv)
prev_state = state = STATE_SENDING_STOP;
if (!data->error)
- send_stop_cmd(host, data);
+ atmci_send_stop_cmd(host, data);
/* fall through */
case STATE_SENDING_STOP:
@@ -1379,7 +1553,7 @@ static void atmci_read_data_pio(struct atmel_mci *host)
unsigned int nbytes = 0;
do {
- value = mci_readl(host, RDR);
+ value = atmci_readl(host, ATMCI_RDR);
if (likely(offset + 4 <= sg->length)) {
put_unaligned(value, (u32 *)(buf + offset));
@@ -1411,9 +1585,9 @@ static void atmci_read_data_pio(struct atmel_mci *host)
nbytes += offset;
}
- status = mci_readl(host, SR);
+ status = atmci_readl(host, ATMCI_SR);
if (status & ATMCI_DATA_ERROR_FLAGS) {
- mci_writel(host, IDR, (MCI_NOTBUSY | MCI_RXRDY
+ atmci_writel(host, ATMCI_IDR, (ATMCI_NOTBUSY | ATMCI_RXRDY
| ATMCI_DATA_ERROR_FLAGS));
host->data_status = status;
data->bytes_xfered += nbytes;
@@ -1422,7 +1596,7 @@ static void atmci_read_data_pio(struct atmel_mci *host)
tasklet_schedule(&host->tasklet);
return;
}
- } while (status & MCI_RXRDY);
+ } while (status & ATMCI_RXRDY);
host->pio_offset = offset;
data->bytes_xfered += nbytes;
@@ -1430,8 +1604,8 @@ static void atmci_read_data_pio(struct atmel_mci *host)
return;
done:
- mci_writel(host, IDR, MCI_RXRDY);
- mci_writel(host, IER, MCI_NOTBUSY);
+ atmci_writel(host, ATMCI_IDR, ATMCI_RXRDY);
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
data->bytes_xfered += nbytes;
smp_wmb();
atmci_set_pending(host, EVENT_XFER_COMPLETE);
@@ -1450,7 +1624,7 @@ static void atmci_write_data_pio(struct atmel_mci *host)
do {
if (likely(offset + 4 <= sg->length)) {
value = get_unaligned((u32 *)(buf + offset));
- mci_writel(host, TDR, value);
+ atmci_writel(host, ATMCI_TDR, value);
offset += 4;
nbytes += 4;
@@ -1471,20 +1645,20 @@ static void atmci_write_data_pio(struct atmel_mci *host)
host->sg = sg = sg_next(sg);
if (!sg) {
- mci_writel(host, TDR, value);
+ atmci_writel(host, ATMCI_TDR, value);
goto done;
}
offset = 4 - remaining;
buf = sg_virt(sg);
memcpy((u8 *)&value + remaining, buf, offset);
- mci_writel(host, TDR, value);
+ atmci_writel(host, ATMCI_TDR, value);
nbytes += offset;
}
- status = mci_readl(host, SR);
+ status = atmci_readl(host, ATMCI_SR);
if (status & ATMCI_DATA_ERROR_FLAGS) {
- mci_writel(host, IDR, (MCI_NOTBUSY | MCI_TXRDY
+ atmci_writel(host, ATMCI_IDR, (ATMCI_NOTBUSY | ATMCI_TXRDY
| ATMCI_DATA_ERROR_FLAGS));
host->data_status = status;
data->bytes_xfered += nbytes;
@@ -1493,7 +1667,7 @@ static void atmci_write_data_pio(struct atmel_mci *host)
tasklet_schedule(&host->tasklet);
return;
}
- } while (status & MCI_TXRDY);
+ } while (status & ATMCI_TXRDY);
host->pio_offset = offset;
data->bytes_xfered += nbytes;
@@ -1501,8 +1675,8 @@ static void atmci_write_data_pio(struct atmel_mci *host)
return;
done:
- mci_writel(host, IDR, MCI_TXRDY);
- mci_writel(host, IER, MCI_NOTBUSY);
+ atmci_writel(host, ATMCI_IDR, ATMCI_TXRDY);
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
data->bytes_xfered += nbytes;
smp_wmb();
atmci_set_pending(host, EVENT_XFER_COMPLETE);
@@ -1510,7 +1684,7 @@ done:
static void atmci_cmd_interrupt(struct atmel_mci *host, u32 status)
{
- mci_writel(host, IDR, MCI_CMDRDY);
+ atmci_writel(host, ATMCI_IDR, ATMCI_CMDRDY);
host->cmd_status = status;
smp_wmb();
@@ -1522,7 +1696,7 @@ static void atmci_sdio_interrupt(struct atmel_mci *host, u32 status)
{
int i;
- for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
struct atmel_mci_slot *slot = host->slot[i];
if (slot && (status & slot->sdio_irq)) {
mmc_signal_sdio_irq(slot->mmc);
@@ -1538,40 +1712,92 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
unsigned int pass_count = 0;
do {
- status = mci_readl(host, SR);
- mask = mci_readl(host, IMR);
+ status = atmci_readl(host, ATMCI_SR);
+ mask = atmci_readl(host, ATMCI_IMR);
pending = status & mask;
if (!pending)
break;
if (pending & ATMCI_DATA_ERROR_FLAGS) {
- mci_writel(host, IDR, ATMCI_DATA_ERROR_FLAGS
- | MCI_RXRDY | MCI_TXRDY);
- pending &= mci_readl(host, IMR);
+ atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS
+ | ATMCI_RXRDY | ATMCI_TXRDY);
+ pending &= atmci_readl(host, ATMCI_IMR);
host->data_status = status;
smp_wmb();
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
}
- if (pending & MCI_NOTBUSY) {
- mci_writel(host, IDR,
- ATMCI_DATA_ERROR_FLAGS | MCI_NOTBUSY);
+
+ if (pending & ATMCI_TXBUFE) {
+ atmci_writel(host, ATMCI_IDR, ATMCI_TXBUFE);
+ atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
+ /*
+ * We can receive this interruption before having configured
+ * the second pdc buffer, so we need to reconfigure first and
+ * second buffers again
+ */
+ if (host->data_size) {
+ atmci_pdc_set_both_buf(host, XFER_TRANSMIT);
+ atmci_writel(host, ATMCI_IER, ATMCI_ENDTX);
+ atmci_writel(host, ATMCI_IER, ATMCI_TXBUFE);
+ } else {
+ atmci_pdc_complete(host);
+ }
+ } else if (pending & ATMCI_ENDTX) {
+ atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
+
+ if (host->data_size) {
+ atmci_pdc_set_single_buf(host,
+ XFER_TRANSMIT, PDC_SECOND_BUF);
+ atmci_writel(host, ATMCI_IER, ATMCI_ENDTX);
+ }
+ }
+
+ if (pending & ATMCI_RXBUFF) {
+ atmci_writel(host, ATMCI_IDR, ATMCI_RXBUFF);
+ atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
+ /*
+ * We can receive this interruption before having configured
+ * the second pdc buffer, so we need to reconfigure first and
+ * second buffers again
+ */
+ if (host->data_size) {
+ atmci_pdc_set_both_buf(host, XFER_RECEIVE);
+ atmci_writel(host, ATMCI_IER, ATMCI_ENDRX);
+ atmci_writel(host, ATMCI_IER, ATMCI_RXBUFF);
+ } else {
+ atmci_pdc_complete(host);
+ }
+ } else if (pending & ATMCI_ENDRX) {
+ atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
+
+ if (host->data_size) {
+ atmci_pdc_set_single_buf(host,
+ XFER_RECEIVE, PDC_SECOND_BUF);
+ atmci_writel(host, ATMCI_IER, ATMCI_ENDRX);
+ }
+ }
+
+
+ if (pending & ATMCI_NOTBUSY) {
+ atmci_writel(host, ATMCI_IDR,
+ ATMCI_DATA_ERROR_FLAGS | ATMCI_NOTBUSY);
if (!host->data_status)
host->data_status = status;
smp_wmb();
atmci_set_pending(host, EVENT_DATA_COMPLETE);
tasklet_schedule(&host->tasklet);
}
- if (pending & MCI_RXRDY)
+ if (pending & ATMCI_RXRDY)
atmci_read_data_pio(host);
- if (pending & MCI_TXRDY)
+ if (pending & ATMCI_TXRDY)
atmci_write_data_pio(host);
- if (pending & MCI_CMDRDY)
+ if (pending & ATMCI_CMDRDY)
atmci_cmd_interrupt(host, status);
- if (pending & (MCI_SDIOIRQA | MCI_SDIOIRQB))
+ if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
atmci_sdio_interrupt(host, status);
} while (pass_count++ < 5);
@@ -1620,7 +1846,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
if (sdio_irq)
mmc->caps |= MMC_CAP_SDIO_IRQ;
- if (atmci_is_mci2())
+ if (host->caps.has_highspeed)
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
if (slot_data->bus_width >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;
@@ -1703,8 +1929,7 @@ static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot,
mmc_free_host(slot->mmc);
}
-#ifdef CONFIG_MMC_ATMELMCI_DMA
-static bool filter(struct dma_chan *chan, void *slave)
+static bool atmci_filter(struct dma_chan *chan, void *slave)
{
struct mci_dma_data *sl = slave;
@@ -1729,14 +1954,14 @@ static void atmci_configure_dma(struct atmel_mci *host)
dma_cap_mask_t mask;
setup_dma_addr(pdata->dma_slave,
- host->mapbase + MCI_TDR,
- host->mapbase + MCI_RDR);
+ host->mapbase + ATMCI_TDR,
+ host->mapbase + ATMCI_RDR);
/* Try to grab a DMA channel */
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
host->dma.chan =
- dma_request_channel(mask, filter, pdata->dma_slave);
+ dma_request_channel(mask, atmci_filter, pdata->dma_slave);
}
if (!host->dma.chan)
dev_notice(&host->pdev->dev, "DMA not available, using PIO\n");
@@ -1745,9 +1970,60 @@ static void atmci_configure_dma(struct atmel_mci *host)
"Using %s for DMA transfers\n",
dma_chan_name(host->dma.chan));
}
+
+static inline unsigned int atmci_get_version(struct atmel_mci *host)
+{
+ return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
+}
+
+/*
+ * HSMCI (High Speed MCI) module is not fully compatible with MCI module.
+ * HSMCI provides DMA support and a new config register but no more supports
+ * PDC.
+ */
+static void __init atmci_get_cap(struct atmel_mci *host)
+{
+ unsigned int version;
+
+ version = atmci_get_version(host);
+ dev_info(&host->pdev->dev,
+ "version: 0x%x\n", version);
+
+ host->caps.has_dma = 0;
+ host->caps.has_pdc = 0;
+ host->caps.has_cfg_reg = 0;
+ host->caps.has_cstor_reg = 0;
+ host->caps.has_highspeed = 0;
+ host->caps.has_rwproof = 0;
+
+ /* keep only major version number */
+ switch (version & 0xf00) {
+ case 0x100:
+ case 0x200:
+ host->caps.has_pdc = 1;
+ host->caps.has_rwproof = 1;
+ break;
+ case 0x300:
+ case 0x400:
+ case 0x500:
+#ifdef CONFIG_AT_HDMAC
+ host->caps.has_dma = 1;
#else
-static void atmci_configure_dma(struct atmel_mci *host) {}
+ host->caps.has_dma = 0;
+ dev_info(&host->pdev->dev,
+ "has dma capability but dma engine is not selected, then use pio\n");
#endif
+ host->caps.has_cfg_reg = 1;
+ host->caps.has_cstor_reg = 1;
+ host->caps.has_highspeed = 1;
+ host->caps.has_rwproof = 1;
+ break;
+ default:
+ dev_warn(&host->pdev->dev,
+ "Unmanaged mci version, set minimum capabilities\n");
+ break;
+ }
+}
static int __init atmci_probe(struct platform_device *pdev)
{
@@ -1788,7 +2064,7 @@ static int __init atmci_probe(struct platform_device *pdev)
goto err_ioremap;
clk_enable(host->mck);
- mci_writel(host, CR, MCI_CR_SWRST);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
host->bus_hz = clk_get_rate(host->mck);
clk_disable(host->mck);
@@ -1800,7 +2076,27 @@ static int __init atmci_probe(struct platform_device *pdev)
if (ret)
goto err_request_irq;
- atmci_configure_dma(host);
+ /* Get MCI capabilities and set operations according to it */
+ atmci_get_cap(host);
+ if (host->caps.has_dma) {
+ dev_info(&pdev->dev, "using DMA\n");
+ host->prepare_data = &atmci_prepare_data_dma;
+ host->submit_data = &atmci_submit_data_dma;
+ host->stop_transfer = &atmci_stop_transfer_dma;
+ } else if (host->caps.has_pdc) {
+ dev_info(&pdev->dev, "using PDC\n");
+ host->prepare_data = &atmci_prepare_data_pdc;
+ host->submit_data = &atmci_submit_data_pdc;
+ host->stop_transfer = &atmci_stop_transfer_pdc;
+ } else {
+ dev_info(&pdev->dev, "no DMA, no PDC\n");
+ host->prepare_data = &atmci_prepare_data;
+ host->submit_data = &atmci_submit_data;
+ host->stop_transfer = &atmci_stop_transfer;
+ }
+
+ if (host->caps.has_dma)
+ atmci_configure_dma(host);
platform_set_drvdata(pdev, host);
@@ -1809,13 +2105,13 @@ static int __init atmci_probe(struct platform_device *pdev)
ret = -ENODEV;
if (pdata->slot[0].bus_width) {
ret = atmci_init_slot(host, &pdata->slot[0],
- 0, MCI_SDCSEL_SLOT_A, MCI_SDIOIRQA);
+ 0, ATMCI_SDCSEL_SLOT_A, ATMCI_SDIOIRQA);
if (!ret)
nr_slots++;
}
if (pdata->slot[1].bus_width) {
ret = atmci_init_slot(host, &pdata->slot[1],
- 1, MCI_SDCSEL_SLOT_B, MCI_SDIOIRQB);
+ 1, ATMCI_SDCSEL_SLOT_B, ATMCI_SDIOIRQB);
if (!ret)
nr_slots++;
}
@@ -1832,10 +2128,8 @@ static int __init atmci_probe(struct platform_device *pdev)
return 0;
err_init_slot:
-#ifdef CONFIG_MMC_ATMELMCI_DMA
if (host->dma.chan)
dma_release_channel(host->dma.chan);
-#endif
free_irq(irq, host);
err_request_irq:
iounmap(host->regs);
@@ -1853,15 +2147,15 @@ static int __exit atmci_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
- for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
if (host->slot[i])
atmci_cleanup_slot(host->slot[i], i);
}
clk_enable(host->mck);
- mci_writel(host, IDR, ~0UL);
- mci_writel(host, CR, MCI_CR_MCIDIS);
- mci_readl(host, SR);
+ atmci_writel(host, ATMCI_IDR, ~0UL);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
+ atmci_readl(host, ATMCI_SR);
clk_disable(host->mck);
#ifdef CONFIG_MMC_ATMELMCI_DMA
@@ -1878,10 +2172,72 @@ static int __exit atmci_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int atmci_suspend(struct device *dev)
+{
+ struct atmel_mci *host = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
+ struct atmel_mci_slot *slot = host->slot[i];
+ int ret;
+
+ if (!slot)
+ continue;
+ ret = mmc_suspend_host(slot->mmc);
+ if (ret < 0) {
+ while (--i >= 0) {
+ slot = host->slot[i];
+ if (slot
+ && test_bit(ATMCI_SUSPENDED, &slot->flags)) {
+ mmc_resume_host(host->slot[i]->mmc);
+ clear_bit(ATMCI_SUSPENDED, &slot->flags);
+ }
+ }
+ return ret;
+ } else {
+ set_bit(ATMCI_SUSPENDED, &slot->flags);
+ }
+ }
+
+ return 0;
+}
+
+static int atmci_resume(struct device *dev)
+{
+ struct atmel_mci *host = dev_get_drvdata(dev);
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
+ struct atmel_mci_slot *slot = host->slot[i];
+ int err;
+
+ slot = host->slot[i];
+ if (!slot)
+ continue;
+ if (!test_bit(ATMCI_SUSPENDED, &slot->flags))
+ continue;
+ err = mmc_resume_host(slot->mmc);
+ if (err < 0)
+ ret = err;
+ else
+ clear_bit(ATMCI_SUSPENDED, &slot->flags);
+ }
+
+ return ret;
+}
+static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume);
+#define ATMCI_PM_OPS (&atmci_pm)
+#else
+#define ATMCI_PM_OPS NULL
+#endif
+
static struct platform_driver atmci_driver = {
.remove = __exit_p(atmci_remove),
.driver = {
.name = "atmel_mci",
+ .pm = ATMCI_PM_OPS,
},
};
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index ef72e874ca36..5d3b9ae64523 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -55,7 +55,7 @@
#ifdef DEBUG
#define DBG(fmt, idx, args...) \
- printk(KERN_DEBUG "au1xmmc(%d): DEBUG: " fmt, idx, ##args)
+ pr_debug("au1xmmc(%d): DEBUG: " fmt, idx, ##args)
#else
#define DBG(fmt, idx, args...) do {} while (0)
#endif
@@ -64,11 +64,8 @@
#define AU1XMMC_DESCRIPTOR_COUNT 1
/* max DMA seg size: 64KB on Au1100, 4MB on Au1200 */
-#ifdef CONFIG_SOC_AU1100
-#define AU1XMMC_DESCRIPTOR_SIZE 0x0000ffff
-#else /* Au1200 */
-#define AU1XMMC_DESCRIPTOR_SIZE 0x003fffff
-#endif
+#define AU1100_MMC_DESCRIPTOR_SIZE 0x0000ffff
+#define AU1200_MMC_DESCRIPTOR_SIZE 0x003fffff
#define AU1XMMC_OCR (MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \
MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \
@@ -127,6 +124,7 @@ struct au1xmmc_host {
#define HOST_F_XMIT 0x0001
#define HOST_F_RECV 0x0002
#define HOST_F_DMA 0x0010
+#define HOST_F_DBDMA 0x0020
#define HOST_F_ACTIVE 0x0100
#define HOST_F_STOP 0x1000
@@ -151,6 +149,16 @@ struct au1xmmc_host {
#define DMA_CHANNEL(h) \
(((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan)
+static inline int has_dbdma(void)
+{
+ switch (alchemy_get_cputype()) {
+ case ALCHEMY_CPU_AU1200:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask)
{
u32 val = au_readl(HOST_CONFIG(host));
@@ -268,7 +276,7 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
mmccmd |= SD_CMD_RT_3;
break;
default:
- printk(KERN_INFO "au1xmmc: unhandled response type %02x\n",
+ pr_info("au1xmmc: unhandled response type %02x\n",
mmc_resp_type(cmd));
return -EINVAL;
}
@@ -353,14 +361,12 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
data->bytes_xfered = 0;
if (!data->error) {
- if (host->flags & HOST_F_DMA) {
-#ifdef CONFIG_SOC_AU1200 /* DBDMA */
+ if (host->flags & (HOST_F_DMA | HOST_F_DBDMA)) {
u32 chan = DMA_CHANNEL(host);
chan_tab_t *c = *((chan_tab_t **)chan);
au1x_dma_chan_t *cp = c->chan_ptr;
data->bytes_xfered = cp->ddma_bytecnt;
-#endif
} else
data->bytes_xfered =
(data->blocks * data->blksz) - host->pio.len;
@@ -570,11 +576,10 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
host->status = HOST_S_DATA;
- if (host->flags & HOST_F_DMA) {
-#ifdef CONFIG_SOC_AU1200 /* DBDMA */
+ if ((host->flags & (HOST_F_DMA | HOST_F_DBDMA))) {
u32 channel = DMA_CHANNEL(host);
- /* Start the DMA as soon as the buffer gets something in it */
+ /* Start the DBDMA as soon as the buffer gets something in it */
if (host->flags & HOST_F_RECV) {
u32 mask = SD_STATUS_DB | SD_STATUS_NE;
@@ -584,7 +589,6 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
}
au1xxx_dbdma_start(channel);
-#endif
}
}
@@ -633,8 +637,7 @@ static int au1xmmc_prepare_data(struct au1xmmc_host *host,
au_writel(data->blksz - 1, HOST_BLKSIZE(host));
- if (host->flags & HOST_F_DMA) {
-#ifdef CONFIG_SOC_AU1200 /* DBDMA */
+ if (host->flags & (HOST_F_DMA | HOST_F_DBDMA)) {
int i;
u32 channel = DMA_CHANNEL(host);
@@ -663,7 +666,6 @@ static int au1xmmc_prepare_data(struct au1xmmc_host *host,
datalen -= len;
}
-#endif
} else {
host->pio.index = 0;
host->pio.offset = 0;
@@ -838,7 +840,6 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
-#ifdef CONFIG_SOC_AU1200
/* 8bit memory DMA device */
static dbdev_tab_t au1xmmc_mem_dbdev = {
.dev_id = DSCR_CMD0_ALWAYS,
@@ -905,7 +906,7 @@ static int au1xmmc_dbdma_init(struct au1xmmc_host *host)
au1xxx_dbdma_ring_alloc(host->rx_chan, AU1XMMC_DESCRIPTOR_COUNT);
/* DBDMA is good to go */
- host->flags |= HOST_F_DMA;
+ host->flags |= HOST_F_DMA | HOST_F_DBDMA;
return 0;
}
@@ -918,7 +919,6 @@ static void au1xmmc_dbdma_shutdown(struct au1xmmc_host *host)
au1xxx_dbdma_chan_free(host->rx_chan);
}
}
-#endif
static void au1xmmc_enable_sdio_irq(struct mmc_host *mmc, int en)
{
@@ -997,8 +997,16 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
mmc->f_min = 450000;
mmc->f_max = 24000000;
- mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE;
- mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT;
+ switch (alchemy_get_cputype()) {
+ case ALCHEMY_CPU_AU1100:
+ mmc->max_seg_size = AU1100_MMC_DESCRIPTOR_SIZE;
+ mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT;
+ break;
+ case ALCHEMY_CPU_AU1200:
+ mmc->max_seg_size = AU1200_MMC_DESCRIPTOR_SIZE;
+ mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT;
+ break;
+ }
mmc->max_blk_size = 2048;
mmc->max_blk_count = 512;
@@ -1028,11 +1036,11 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
tasklet_init(&host->finish_task, au1xmmc_tasklet_finish,
(unsigned long)host);
-#ifdef CONFIG_SOC_AU1200
- ret = au1xmmc_dbdma_init(host);
- if (ret)
- printk(KERN_INFO DRIVER_NAME ": DBDMA init failed; using PIO\n");
-#endif
+ if (has_dbdma()) {
+ ret = au1xmmc_dbdma_init(host);
+ if (ret)
+ pr_info(DRIVER_NAME ": DBDMA init failed; using PIO\n");
+ }
#ifdef CONFIG_LEDS_CLASS
if (host->platdata && host->platdata->led) {
@@ -1056,7 +1064,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
- printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X"
+ pr_info(DRIVER_NAME ": MMC Controller %d set up at %8.8X"
" (mode=%s)\n", pdev->id, host->iobase,
host->flags & HOST_F_DMA ? "dma" : "pio");
@@ -1073,9 +1081,8 @@ out5:
au_writel(0, HOST_CONFIG2(host));
au_sync();
-#ifdef CONFIG_SOC_AU1200
- au1xmmc_dbdma_shutdown(host);
-#endif
+ if (host->flags & HOST_F_DBDMA)
+ au1xmmc_dbdma_shutdown(host);
tasklet_kill(&host->data_task);
tasklet_kill(&host->finish_task);
@@ -1120,9 +1127,9 @@ static int __devexit au1xmmc_remove(struct platform_device *pdev)
tasklet_kill(&host->data_task);
tasklet_kill(&host->finish_task);
-#ifdef CONFIG_SOC_AU1200
- au1xmmc_dbdma_shutdown(host);
-#endif
+ if (host->flags & HOST_F_DBDMA)
+ au1xmmc_dbdma_shutdown(host);
+
au1xmmc_set_power(host, 0);
free_irq(host->irq, host);
@@ -1181,24 +1188,23 @@ static struct platform_driver au1xmmc_driver = {
static int __init au1xmmc_init(void)
{
-#ifdef CONFIG_SOC_AU1200
- /* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride
- * of 8 bits. And since devices are shared, we need to create
- * our own to avoid freaking out other devices.
- */
- memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev);
- if (!memid)
- printk(KERN_ERR "au1xmmc: cannot add memory dbdma dev\n");
-#endif
+ if (has_dbdma()) {
+ /* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride
+ * of 8 bits. And since devices are shared, we need to create
+ * our own to avoid freaking out other devices.
+ */
+ memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev);
+ if (!memid)
+ pr_err("au1xmmc: cannot add memory dbdma\n");
+ }
return platform_driver_register(&au1xmmc_driver);
}
static void __exit au1xmmc_exit(void)
{
-#ifdef CONFIG_SOC_AU1200
- if (memid)
+ if (has_dbdma() && memid)
au1xxx_ddma_del_device(memid);
-#endif
+
platform_driver_unregister(&au1xmmc_driver);
}
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 0076c7448fe6..64a8325a4a8a 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -807,12 +807,25 @@ static void calculate_clk_divider(struct mmc_host *mmc, struct mmc_ios *ios)
static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mmc_davinci_host *host = mmc_priv(mmc);
+ struct platform_device *pdev = to_platform_device(mmc->parent);
+ struct davinci_mmc_config *config = pdev->dev.platform_data;
dev_dbg(mmc_dev(host->mmc),
"clock %dHz busmode %d powermode %d Vdd %04x\n",
ios->clock, ios->bus_mode, ios->power_mode,
ios->vdd);
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ if (config && config->set_power)
+ config->set_power(pdev->id, false);
+ break;
+ case MMC_POWER_UP:
+ if (config && config->set_power)
+ config->set_power(pdev->id, true);
+ break;
+ }
+
switch (ios->bus_width) {
case MMC_BUS_WIDTH_8:
dev_dbg(mmc_dev(host->mmc), "Enabling 8 bit mode\n");
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 66dcddb9c205..3aaeb0841914 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -33,6 +33,7 @@
#include <linux/mmc/dw_mmc.h>
#include <linux/bitops.h>
#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
#include "dw_mmc.h"
@@ -61,7 +62,7 @@ struct idmac_desc {
u32 des1; /* Buffer sizes */
#define IDMAC_SET_BUFFER1_SIZE(d, s) \
- ((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
+ ((d)->des1 = ((d)->des1 & 0x03ffe000) | ((s) & 0x1fff))
u32 des2; /* buffer 1 physical address */
@@ -100,6 +101,8 @@ struct dw_mci_slot {
int last_detect_state;
};
+static struct workqueue_struct *dw_mci_card_workqueue;
+
#if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v)
{
@@ -284,7 +287,7 @@ static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
/* DMA interface functions */
static void dw_mci_stop_dma(struct dw_mci *host)
{
- if (host->use_dma) {
+ if (host->using_dma) {
host->dma_ops->stop(host);
host->dma_ops->cleanup(host);
} else {
@@ -432,6 +435,8 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
unsigned int i, direction, sg_len;
u32 temp;
+ host->using_dma = 0;
+
/* If we don't have a channel, we can't do DMA */
if (!host->use_dma)
return -ENODEV;
@@ -451,6 +456,8 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
return -EINVAL;
}
+ host->using_dma = 1;
+
if (data->flags & MMC_DATA_READ)
direction = DMA_FROM_DEVICE;
else
@@ -489,14 +496,18 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
host->sg = NULL;
host->data = data;
+ if (data->flags & MMC_DATA_READ)
+ host->dir_status = DW_MCI_RECV_STATUS;
+ else
+ host->dir_status = DW_MCI_SEND_STATUS;
+
if (dw_mci_submit_data_dma(host, data)) {
host->sg = data->sg;
host->pio_offset = 0;
- if (data->flags & MMC_DATA_READ)
- host->dir_status = DW_MCI_RECV_STATUS;
- else
- host->dir_status = DW_MCI_SEND_STATUS;
+ host->part_buf_start = 0;
+ host->part_buf_count = 0;
+ mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR);
temp = mci_readl(host, INTMASK);
temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
mci_writel(host, INTMASK, temp);
@@ -574,7 +585,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
}
/* Set the current slot bus width */
- mci_writel(host, CTYPE, slot->ctype);
+ mci_writel(host, CTYPE, (slot->ctype << slot->id));
}
static void dw_mci_start_request(struct dw_mci *host,
@@ -624,13 +635,13 @@ static void dw_mci_start_request(struct dw_mci *host,
host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
}
+/* must be called with host->lock held */
static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
struct mmc_request *mrq)
{
dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
host->state);
- spin_lock_bh(&host->lock);
slot->mrq = mrq;
if (host->state == STATE_IDLE) {
@@ -639,8 +650,6 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
} else {
list_add_tail(&slot->queue_node, &host->queue);
}
-
- spin_unlock_bh(&host->lock);
}
static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
@@ -650,14 +659,23 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
WARN_ON(slot->mrq);
+ /*
+ * The check for card presence and queueing of the request must be
+ * atomic, otherwise the card could be removed in between and the
+ * request wouldn't fail until another card was inserted.
+ */
+ spin_lock_bh(&host->lock);
+
if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
+ spin_unlock_bh(&host->lock);
mrq->cmd->error = -ENOMEDIUM;
mmc_request_done(mmc, mrq);
return;
}
- /* We don't support multiple blocks of weird lengths. */
dw_mci_queue_request(host, slot, mrq);
+
+ spin_unlock_bh(&host->lock);
}
static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -681,7 +699,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
/* DDR mode set */
- if (ios->ddr) {
+ if (ios->timing == MMC_TIMING_UHS_DDR50) {
regs = mci_readl(slot->host, UHS_REG);
regs |= (0x1 << slot->id) << 16;
mci_writel(slot->host, UHS_REG, regs);
@@ -746,11 +764,29 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
return present;
}
+static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
+{
+ struct dw_mci_slot *slot = mmc_priv(mmc);
+ struct dw_mci *host = slot->host;
+ u32 int_mask;
+
+ /* Enable/disable Slot Specific SDIO interrupt */
+ int_mask = mci_readl(host, INTMASK);
+ if (enb) {
+ mci_writel(host, INTMASK,
+ (int_mask | (1 << SDMMC_INT_SDIO(slot->id))));
+ } else {
+ mci_writel(host, INTMASK,
+ (int_mask & ~(1 << SDMMC_INT_SDIO(slot->id))));
+ }
+}
+
static const struct mmc_host_ops dw_mci_ops = {
- .request = dw_mci_request,
- .set_ios = dw_mci_set_ios,
- .get_ro = dw_mci_get_ro,
- .get_cd = dw_mci_get_cd,
+ .request = dw_mci_request,
+ .set_ios = dw_mci_set_ios,
+ .get_ro = dw_mci_get_ro,
+ .get_cd = dw_mci_get_cd,
+ .enable_sdio_irq = dw_mci_enable_sdio_irq,
};
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
@@ -831,7 +867,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
struct mmc_command *cmd;
enum dw_mci_state state;
enum dw_mci_state prev_state;
- u32 status;
+ u32 status, ctrl;
spin_lock(&host->lock);
@@ -891,13 +927,19 @@ static void dw_mci_tasklet_func(unsigned long priv)
if (status & DW_MCI_DATA_ERROR_FLAGS) {
if (status & SDMMC_INT_DTO) {
- dev_err(&host->pdev->dev,
- "data timeout error\n");
data->error = -ETIMEDOUT;
} else if (status & SDMMC_INT_DCRC) {
- dev_err(&host->pdev->dev,
- "data CRC error\n");
data->error = -EILSEQ;
+ } else if (status & SDMMC_INT_EBE &&
+ host->dir_status ==
+ DW_MCI_SEND_STATUS) {
+ /*
+ * No data CRC status was returned.
+ * The number of bytes transferred will
+ * be exaggerated in PIO mode.
+ */
+ data->bytes_xfered = 0;
+ data->error = -ETIMEDOUT;
} else {
dev_err(&host->pdev->dev,
"data FIFO error "
@@ -905,6 +947,16 @@ static void dw_mci_tasklet_func(unsigned long priv)
status);
data->error = -EIO;
}
+ /*
+ * After an error, there may be data lingering
+ * in the FIFO, so reset it - doing so
+ * generates a block interrupt, hence setting
+ * the scatter-gather pointer to NULL.
+ */
+ host->sg = NULL;
+ ctrl = mci_readl(host, CTRL);
+ ctrl |= SDMMC_CTRL_FIFO_RESET;
+ mci_writel(host, CTRL, ctrl);
} else {
data->bytes_xfered = data->blocks * data->blksz;
data->error = 0;
@@ -946,84 +998,290 @@ unlock:
}
-static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
+/* push final bytes to part_buf, only use during push */
+static void dw_mci_set_part_bytes(struct dw_mci *host, void *buf, int cnt)
{
- u16 *pdata = (u16 *)buf;
+ memcpy((void *)&host->part_buf, buf, cnt);
+ host->part_buf_count = cnt;
+}
- WARN_ON(cnt % 2 != 0);
+/* append bytes to part_buf, only use during push */
+static int dw_mci_push_part_bytes(struct dw_mci *host, void *buf, int cnt)
+{
+ cnt = min(cnt, (1 << host->data_shift) - host->part_buf_count);
+ memcpy((void *)&host->part_buf + host->part_buf_count, buf, cnt);
+ host->part_buf_count += cnt;
+ return cnt;
+}
- cnt = cnt >> 1;
- while (cnt > 0) {
- mci_writew(host, DATA, *pdata++);
- cnt--;
+/* pull first bytes from part_buf, only use during pull */
+static int dw_mci_pull_part_bytes(struct dw_mci *host, void *buf, int cnt)
+{
+ cnt = min(cnt, (int)host->part_buf_count);
+ if (cnt) {
+ memcpy(buf, (void *)&host->part_buf + host->part_buf_start,
+ cnt);
+ host->part_buf_count -= cnt;
+ host->part_buf_start += cnt;
}
+ return cnt;
}
-static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
+/* pull final bytes from the part_buf, assuming it's just been filled */
+static void dw_mci_pull_final_bytes(struct dw_mci *host, void *buf, int cnt)
{
- u16 *pdata = (u16 *)buf;
+ memcpy(buf, &host->part_buf, cnt);
+ host->part_buf_start = cnt;
+ host->part_buf_count = (1 << host->data_shift) - cnt;
+}
- WARN_ON(cnt % 2 != 0);
+static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
+{
+ /* try and push anything in the part_buf */
+ if (unlikely(host->part_buf_count)) {
+ int len = dw_mci_push_part_bytes(host, buf, cnt);
+ buf += len;
+ cnt -= len;
+ if (!sg_next(host->sg) || host->part_buf_count == 2) {
+ mci_writew(host, DATA(host->data_offset),
+ host->part_buf16);
+ host->part_buf_count = 0;
+ }
+ }
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ if (unlikely((unsigned long)buf & 0x1)) {
+ while (cnt >= 2) {
+ u16 aligned_buf[64];
+ int len = min(cnt & -2, (int)sizeof(aligned_buf));
+ int items = len >> 1;
+ int i;
+ /* memcpy from input buffer into aligned buffer */
+ memcpy(aligned_buf, buf, len);
+ buf += len;
+ cnt -= len;
+ /* push data from aligned buffer into fifo */
+ for (i = 0; i < items; ++i)
+ mci_writew(host, DATA(host->data_offset),
+ aligned_buf[i]);
+ }
+ } else
+#endif
+ {
+ u16 *pdata = buf;
+ for (; cnt >= 2; cnt -= 2)
+ mci_writew(host, DATA(host->data_offset), *pdata++);
+ buf = pdata;
+ }
+ /* put anything remaining in the part_buf */
+ if (cnt) {
+ dw_mci_set_part_bytes(host, buf, cnt);
+ if (!sg_next(host->sg))
+ mci_writew(host, DATA(host->data_offset),
+ host->part_buf16);
+ }
+}
- cnt = cnt >> 1;
- while (cnt > 0) {
- *pdata++ = mci_readw(host, DATA);
- cnt--;
+static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
+{
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ if (unlikely((unsigned long)buf & 0x1)) {
+ while (cnt >= 2) {
+ /* pull data from fifo into aligned buffer */
+ u16 aligned_buf[64];
+ int len = min(cnt & -2, (int)sizeof(aligned_buf));
+ int items = len >> 1;
+ int i;
+ for (i = 0; i < items; ++i)
+ aligned_buf[i] = mci_readw(host,
+ DATA(host->data_offset));
+ /* memcpy from aligned buffer into output buffer */
+ memcpy(buf, aligned_buf, len);
+ buf += len;
+ cnt -= len;
+ }
+ } else
+#endif
+ {
+ u16 *pdata = buf;
+ for (; cnt >= 2; cnt -= 2)
+ *pdata++ = mci_readw(host, DATA(host->data_offset));
+ buf = pdata;
+ }
+ if (cnt) {
+ host->part_buf16 = mci_readw(host, DATA(host->data_offset));
+ dw_mci_pull_final_bytes(host, buf, cnt);
}
}
static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
{
- u32 *pdata = (u32 *)buf;
-
- WARN_ON(cnt % 4 != 0);
- WARN_ON((unsigned long)pdata & 0x3);
-
- cnt = cnt >> 2;
- while (cnt > 0) {
- mci_writel(host, DATA, *pdata++);
- cnt--;
+ /* try and push anything in the part_buf */
+ if (unlikely(host->part_buf_count)) {
+ int len = dw_mci_push_part_bytes(host, buf, cnt);
+ buf += len;
+ cnt -= len;
+ if (!sg_next(host->sg) || host->part_buf_count == 4) {
+ mci_writel(host, DATA(host->data_offset),
+ host->part_buf32);
+ host->part_buf_count = 0;
+ }
+ }
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ if (unlikely((unsigned long)buf & 0x3)) {
+ while (cnt >= 4) {
+ u32 aligned_buf[32];
+ int len = min(cnt & -4, (int)sizeof(aligned_buf));
+ int items = len >> 2;
+ int i;
+ /* memcpy from input buffer into aligned buffer */
+ memcpy(aligned_buf, buf, len);
+ buf += len;
+ cnt -= len;
+ /* push data from aligned buffer into fifo */
+ for (i = 0; i < items; ++i)
+ mci_writel(host, DATA(host->data_offset),
+ aligned_buf[i]);
+ }
+ } else
+#endif
+ {
+ u32 *pdata = buf;
+ for (; cnt >= 4; cnt -= 4)
+ mci_writel(host, DATA(host->data_offset), *pdata++);
+ buf = pdata;
+ }
+ /* put anything remaining in the part_buf */
+ if (cnt) {
+ dw_mci_set_part_bytes(host, buf, cnt);
+ if (!sg_next(host->sg))
+ mci_writel(host, DATA(host->data_offset),
+ host->part_buf32);
}
}
static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
{
- u32 *pdata = (u32 *)buf;
-
- WARN_ON(cnt % 4 != 0);
- WARN_ON((unsigned long)pdata & 0x3);
-
- cnt = cnt >> 2;
- while (cnt > 0) {
- *pdata++ = mci_readl(host, DATA);
- cnt--;
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ if (unlikely((unsigned long)buf & 0x3)) {
+ while (cnt >= 4) {
+ /* pull data from fifo into aligned buffer */
+ u32 aligned_buf[32];
+ int len = min(cnt & -4, (int)sizeof(aligned_buf));
+ int items = len >> 2;
+ int i;
+ for (i = 0; i < items; ++i)
+ aligned_buf[i] = mci_readl(host,
+ DATA(host->data_offset));
+ /* memcpy from aligned buffer into output buffer */
+ memcpy(buf, aligned_buf, len);
+ buf += len;
+ cnt -= len;
+ }
+ } else
+#endif
+ {
+ u32 *pdata = buf;
+ for (; cnt >= 4; cnt -= 4)
+ *pdata++ = mci_readl(host, DATA(host->data_offset));
+ buf = pdata;
+ }
+ if (cnt) {
+ host->part_buf32 = mci_readl(host, DATA(host->data_offset));
+ dw_mci_pull_final_bytes(host, buf, cnt);
}
}
static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
{
- u64 *pdata = (u64 *)buf;
-
- WARN_ON(cnt % 8 != 0);
-
- cnt = cnt >> 3;
- while (cnt > 0) {
- mci_writeq(host, DATA, *pdata++);
- cnt--;
+ /* try and push anything in the part_buf */
+ if (unlikely(host->part_buf_count)) {
+ int len = dw_mci_push_part_bytes(host, buf, cnt);
+ buf += len;
+ cnt -= len;
+ if (!sg_next(host->sg) || host->part_buf_count == 8) {
+ mci_writew(host, DATA(host->data_offset),
+ host->part_buf);
+ host->part_buf_count = 0;
+ }
+ }
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ if (unlikely((unsigned long)buf & 0x7)) {
+ while (cnt >= 8) {
+ u64 aligned_buf[16];
+ int len = min(cnt & -8, (int)sizeof(aligned_buf));
+ int items = len >> 3;
+ int i;
+ /* memcpy from input buffer into aligned buffer */
+ memcpy(aligned_buf, buf, len);
+ buf += len;
+ cnt -= len;
+ /* push data from aligned buffer into fifo */
+ for (i = 0; i < items; ++i)
+ mci_writeq(host, DATA(host->data_offset),
+ aligned_buf[i]);
+ }
+ } else
+#endif
+ {
+ u64 *pdata = buf;
+ for (; cnt >= 8; cnt -= 8)
+ mci_writeq(host, DATA(host->data_offset), *pdata++);
+ buf = pdata;
+ }
+ /* put anything remaining in the part_buf */
+ if (cnt) {
+ dw_mci_set_part_bytes(host, buf, cnt);
+ if (!sg_next(host->sg))
+ mci_writeq(host, DATA(host->data_offset),
+ host->part_buf);
}
}
static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
{
- u64 *pdata = (u64 *)buf;
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ if (unlikely((unsigned long)buf & 0x7)) {
+ while (cnt >= 8) {
+ /* pull data from fifo into aligned buffer */
+ u64 aligned_buf[16];
+ int len = min(cnt & -8, (int)sizeof(aligned_buf));
+ int items = len >> 3;
+ int i;
+ for (i = 0; i < items; ++i)
+ aligned_buf[i] = mci_readq(host,
+ DATA(host->data_offset));
+ /* memcpy from aligned buffer into output buffer */
+ memcpy(buf, aligned_buf, len);
+ buf += len;
+ cnt -= len;
+ }
+ } else
+#endif
+ {
+ u64 *pdata = buf;
+ for (; cnt >= 8; cnt -= 8)
+ *pdata++ = mci_readq(host, DATA(host->data_offset));
+ buf = pdata;
+ }
+ if (cnt) {
+ host->part_buf = mci_readq(host, DATA(host->data_offset));
+ dw_mci_pull_final_bytes(host, buf, cnt);
+ }
+}
- WARN_ON(cnt % 8 != 0);
+static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt)
+{
+ int len;
- cnt = cnt >> 3;
- while (cnt > 0) {
- *pdata++ = mci_readq(host, DATA);
- cnt--;
- }
+ /* get remaining partial bytes */
+ len = dw_mci_pull_part_bytes(host, buf, cnt);
+ if (unlikely(len == cnt))
+ return;
+ buf += len;
+ cnt -= len;
+
+ /* get the rest of the data */
+ host->pull_data(host, buf, cnt);
}
static void dw_mci_read_data_pio(struct dw_mci *host)
@@ -1037,9 +1295,10 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
unsigned int nbytes = 0, len;
do {
- len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
+ len = host->part_buf_count +
+ (SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
if (offset + len <= sg->length) {
- host->pull_data(host, (void *)(buf + offset), len);
+ dw_mci_pull_data(host, (void *)(buf + offset), len);
offset += len;
nbytes += len;
@@ -1055,8 +1314,8 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
}
} else {
unsigned int remaining = sg->length - offset;
- host->pull_data(host, (void *)(buf + offset),
- remaining);
+ dw_mci_pull_data(host, (void *)(buf + offset),
+ remaining);
nbytes += remaining;
flush_dcache_page(sg_page(sg));
@@ -1066,7 +1325,7 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
offset = len - remaining;
buf = sg_virt(sg);
- host->pull_data(host, buf, offset);
+ dw_mci_pull_data(host, buf, offset);
nbytes += offset;
}
@@ -1083,7 +1342,6 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
return;
}
} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/
- len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
host->pio_offset = offset;
data->bytes_xfered += nbytes;
return;
@@ -1105,8 +1363,9 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
unsigned int nbytes = 0, len;
do {
- len = SDMMC_FIFO_SZ -
- (SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
+ len = ((host->fifo_depth -
+ SDMMC_GET_FCNT(mci_readl(host, STATUS))) << shift)
+ - host->part_buf_count;
if (offset + len <= sg->length) {
host->push_data(host, (void *)(buf + offset), len);
@@ -1151,10 +1410,8 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
return;
}
} while (status & SDMMC_INT_TXDR); /* if TXDR write again */
-
host->pio_offset = offset;
data->bytes_xfered += nbytes;
-
return;
done:
@@ -1179,6 +1436,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
struct dw_mci *host = dev_id;
u32 status, pending;
unsigned int pass_count = 0;
+ int i;
do {
status = mci_readl(host, RINTSTS);
@@ -1202,7 +1460,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
host->cmd_status = status;
smp_wmb();
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
- tasklet_schedule(&host->tasklet);
}
if (pending & DW_MCI_DATA_ERROR_FLAGS) {
@@ -1211,7 +1468,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
host->data_status = status;
smp_wmb();
set_bit(EVENT_DATA_ERROR, &host->pending_events);
- tasklet_schedule(&host->tasklet);
+ if (!(pending & (SDMMC_INT_DTO | SDMMC_INT_DCRC |
+ SDMMC_INT_SBE | SDMMC_INT_EBE)))
+ tasklet_schedule(&host->tasklet);
}
if (pending & SDMMC_INT_DATA_OVER) {
@@ -1229,13 +1488,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_RXDR) {
mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
- if (host->sg)
+ if (host->dir_status == DW_MCI_RECV_STATUS && host->sg)
dw_mci_read_data_pio(host);
}
if (pending & SDMMC_INT_TXDR) {
mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
- if (host->sg)
+ if (host->dir_status == DW_MCI_SEND_STATUS && host->sg)
dw_mci_write_data_pio(host);
}
@@ -1246,7 +1505,16 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_CD) {
mci_writel(host, RINTSTS, SDMMC_INT_CD);
- tasklet_schedule(&host->card_tasklet);
+ queue_work(dw_mci_card_workqueue, &host->card_work);
+ }
+
+ /* Handle SDIO Interrupts */
+ for (i = 0; i < host->num_slots; i++) {
+ struct dw_mci_slot *slot = host->slot[i];
+ if (pending & SDMMC_INT_SDIO(i)) {
+ mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i));
+ mmc_signal_sdio_irq(slot->mmc);
+ }
}
} while (pass_count++ < 5);
@@ -1265,9 +1533,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static void dw_mci_tasklet_card(unsigned long data)
+static void dw_mci_work_routine_card(struct work_struct *work)
{
- struct dw_mci *host = (struct dw_mci *)data;
+ struct dw_mci *host = container_of(work, struct dw_mci, card_work);
int i;
for (i = 0; i < host->num_slots; i++) {
@@ -1279,22 +1547,21 @@ static void dw_mci_tasklet_card(unsigned long data)
present = dw_mci_get_cd(mmc);
while (present != slot->last_detect_state) {
- spin_lock(&host->lock);
-
dev_dbg(&slot->mmc->class_dev, "card %s\n",
present ? "inserted" : "removed");
+ /* Power up slot (before spin_lock, may sleep) */
+ if (present != 0 && host->pdata->setpower)
+ host->pdata->setpower(slot->id, mmc->ocr_avail);
+
+ spin_lock_bh(&host->lock);
+
/* Card change detected */
slot->last_detect_state = present;
- /* Power up slot */
- if (present != 0) {
- if (host->pdata->setpower)
- host->pdata->setpower(slot->id,
- mmc->ocr_avail);
-
+ /* Mark card as present if applicable */
+ if (present != 0)
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
- }
/* Clean up queue if present */
mrq = slot->mrq;
@@ -1344,8 +1611,6 @@ static void dw_mci_tasklet_card(unsigned long data)
/* Power down slot */
if (present == 0) {
- if (host->pdata->setpower)
- host->pdata->setpower(slot->id, 0);
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
/*
@@ -1367,7 +1632,12 @@ static void dw_mci_tasklet_card(unsigned long data)
}
- spin_unlock(&host->lock);
+ spin_unlock_bh(&host->lock);
+
+ /* Power down slot (after spin_unlock, may sleep) */
+ if (present == 0 && host->pdata->setpower)
+ host->pdata->setpower(slot->id, 0);
+
present = dw_mci_get_cd(mmc);
}
@@ -1416,7 +1686,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
mmc->caps |= MMC_CAP_4_BIT_DATA;
if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
- mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+ mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
#ifdef CONFIG_MMC_DW_IDMAC
mmc->max_segs = host->ring_size;
@@ -1443,7 +1713,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) {
- printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc));
+ pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
host->vmmc = NULL;
} else
regulator_enable(host->vmmc);
@@ -1467,7 +1737,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
* Card may have been plugged in prior to boot so we
* need to run the detect tasklet
*/
- tasklet_schedule(&host->card_tasklet);
+ queue_work(dw_mci_card_workqueue, &host->card_work);
return 0;
}
@@ -1595,7 +1865,7 @@ static int dw_mci_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&host->queue);
ret = -ENOMEM;
- host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ host->regs = ioremap(regs->start, resource_size(regs));
if (!host->regs)
goto err_freehost;
@@ -1645,8 +1915,19 @@ static int dw_mci_probe(struct platform_device *pdev)
* FIFO threshold settings RxMark = fifo_size / 2 - 1,
* Tx Mark = fifo_size / 2 DMA Size = 8
*/
- fifo_size = mci_readl(host, FIFOTH);
- fifo_size = (fifo_size >> 16) & 0x7ff;
+ if (!host->pdata->fifo_depth) {
+ /*
+ * Power-on value of RX_WMark is FIFO_DEPTH-1, but this may
+ * have been overwritten by the bootloader, just like we're
+ * about to do, so if you know the value for your hardware, you
+ * should put it in the platform data.
+ */
+ fifo_size = mci_readl(host, FIFOTH);
+ fifo_size = 1 + ((fifo_size >> 16) & 0x7ff);
+ } else {
+ fifo_size = host->pdata->fifo_depth;
+ }
+ host->fifo_depth = fifo_size;
host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
((fifo_size/2) << 0));
mci_writel(host, FIFOTH, host->fifoth_val);
@@ -1656,12 +1937,15 @@ static int dw_mci_probe(struct platform_device *pdev)
mci_writel(host, CLKSRC, 0);
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
- tasklet_init(&host->card_tasklet,
- dw_mci_tasklet_card, (unsigned long)host);
+ dw_mci_card_workqueue = alloc_workqueue("dw-mci-card",
+ WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
+ if (!dw_mci_card_workqueue)
+ goto err_dmaunmap;
+ INIT_WORK(&host->card_work, dw_mci_work_routine_card);
ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
if (ret)
- goto err_dmaunmap;
+ goto err_workqueue;
platform_set_drvdata(pdev, host);
@@ -1680,6 +1964,18 @@ static int dw_mci_probe(struct platform_device *pdev)
}
/*
+ * In 2.40a spec, Data offset is changed.
+ * Need to check the version-id and set data-offset for DATA register.
+ */
+ host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
+ dev_info(&pdev->dev, "Version ID is %04x\n", host->verid);
+
+ if (host->verid < DW_MMC_240A)
+ host->data_offset = DATA_OFFSET;
+ else
+ host->data_offset = DATA_240A_OFFSET;
+
+ /*
* Enable interrupts for command done, data over, data empty, card det,
* receive ready and error such as transmit, receive timeout, crc error
*/
@@ -1690,7 +1986,9 @@ static int dw_mci_probe(struct platform_device *pdev)
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
dev_info(&pdev->dev, "DW MMC controller at irq %d, "
- "%d bit host data width\n", irq, width);
+ "%d bit host data width, "
+ "%u deep fifo\n",
+ irq, width, fifo_size);
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
@@ -1705,6 +2003,9 @@ err_init_slot:
}
free_irq(irq, host);
+err_workqueue:
+ destroy_workqueue(dw_mci_card_workqueue);
+
err_dmaunmap:
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
@@ -1744,6 +2045,7 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
mci_writel(host, CLKSRC, 0);
free_irq(platform_get_irq(pdev, 0), host);
+ destroy_workqueue(dw_mci_card_workqueue);
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
if (host->use_dma && host->dma_ops->exit)
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 23c662af5616..72c071f6e001 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -14,6 +14,8 @@
#ifndef _DW_MMC_H_
#define _DW_MMC_H_
+#define DW_MMC_240A 0x240a
+
#define SDMMC_CTRL 0x000
#define SDMMC_PWREN 0x004
#define SDMMC_CLKDIV 0x008
@@ -51,7 +53,14 @@
#define SDMMC_IDINTEN 0x090
#define SDMMC_DSCADDR 0x094
#define SDMMC_BUFADDR 0x098
-#define SDMMC_DATA 0x100
+#define SDMMC_DATA(x) (x)
+
+/*
+ * Data offset is difference according to Version
+ * Lower than 2.40a : data register offest is 0x100
+ */
+#define DATA_OFFSET 0x100
+#define DATA_240A_OFFSET 0x200
/* shift bit field */
#define _SBF(f, v) ((v) << (f))
@@ -82,7 +91,7 @@
#define SDMMC_CTYPE_4BIT BIT(0)
#define SDMMC_CTYPE_1BIT 0
/* Interrupt status & mask register defines */
-#define SDMMC_INT_SDIO BIT(16)
+#define SDMMC_INT_SDIO(n) BIT(16 + (n))
#define SDMMC_INT_EBE BIT(15)
#define SDMMC_INT_ACD BIT(14)
#define SDMMC_INT_SBE BIT(13)
@@ -118,7 +127,6 @@
#define SDMMC_CMD_INDX(n) ((n) & 0x1F)
/* Status register defines */
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FF)
-#define SDMMC_FIFO_SZ 32
/* Internal DMAC interrupt defines */
#define SDMMC_IDMAC_INT_AI BIT(9)
#define SDMMC_IDMAC_INT_NI BIT(8)
@@ -131,25 +139,27 @@
#define SDMMC_IDMAC_ENABLE BIT(7)
#define SDMMC_IDMAC_FB BIT(1)
#define SDMMC_IDMAC_SWRESET BIT(0)
+/* Version ID register define */
+#define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
/* Register access macros */
#define mci_readl(dev, reg) \
- __raw_readl(dev->regs + SDMMC_##reg)
+ __raw_readl((dev)->regs + SDMMC_##reg)
#define mci_writel(dev, reg, value) \
- __raw_writel((value), dev->regs + SDMMC_##reg)
+ __raw_writel((value), (dev)->regs + SDMMC_##reg)
/* 16-bit FIFO access macros */
#define mci_readw(dev, reg) \
- __raw_readw(dev->regs + SDMMC_##reg)
+ __raw_readw((dev)->regs + SDMMC_##reg)
#define mci_writew(dev, reg, value) \
- __raw_writew((value), dev->regs + SDMMC_##reg)
+ __raw_writew((value), (dev)->regs + SDMMC_##reg)
/* 64-bit FIFO access macros */
#ifdef readq
#define mci_readq(dev, reg) \
- __raw_readq(dev->regs + SDMMC_##reg)
+ __raw_readq((dev)->regs + SDMMC_##reg)
#define mci_writeq(dev, reg, value) \
- __raw_writeq((value), dev->regs + SDMMC_##reg)
+ __raw_writeq((value), (dev)->regs + SDMMC_##reg)
#else
/*
* Dummy readq implementation for architectures that don't define it.
@@ -160,9 +170,9 @@
* rest of the code free from ifdefs.
*/
#define mci_readq(dev, reg) \
- (*(volatile u64 __force *)(dev->regs + SDMMC_##reg))
+ (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg))
#define mci_writeq(dev, reg, value) \
- (*(volatile u64 __force *)(dev->regs + SDMMC_##reg) = value)
+ (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value))
#endif
#endif /* _DW_MMC_H_ */
diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c
index 881f7ba545ae..ea0f3cedef21 100644
--- a/drivers/mmc/host/imxmmc.c
+++ b/drivers/mmc/host/imxmmc.c
@@ -942,7 +942,7 @@ static int __init imxmci_probe(struct platform_device *pdev)
int ret = 0, irq;
u16 rev_no;
- printk(KERN_INFO "i.MX mmc driver\n");
+ pr_info("i.MX mmc driver\n");
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 7c1e16aaf17f..92946b84e9fa 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -27,6 +27,7 @@
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/bio.h>
#include <linux/dma-mapping.h>
#include <linux/crc7.h>
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index fe140724a02e..50b5f9926f64 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -29,6 +29,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/amba/mmci.h>
+#include <linux/pm_runtime.h>
#include <asm/div64.h>
#include <asm/io.h>
@@ -170,6 +171,7 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
* back into the driver...
*/
spin_unlock(&host->lock);
+ pm_runtime_put(mmc_dev(host->mmc));
mmc_request_done(host->mmc, mrq);
spin_lock(&host->lock);
}
@@ -226,6 +228,9 @@ static void __devinit mmci_dma_setup(struct mmci_host *host)
return;
}
+ /* initialize pre request cookie */
+ host->next_data.cookie = 1;
+
/* Try to acquire a generic DMA engine slave channel */
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
@@ -335,7 +340,8 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
dir = DMA_FROM_DEVICE;
}
- dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
+ if (!data->host_cookie)
+ dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
/*
* Use of DMA with scatter-gather is impossible.
@@ -353,7 +359,8 @@ static void mmci_dma_data_error(struct mmci_host *host)
dmaengine_terminate_all(host->dma_current);
}
-static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
+static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
+ struct mmci_host_next *next)
{
struct variant_data *variant = host->variant;
struct dma_slave_config conf = {
@@ -364,13 +371,20 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
.src_maxburst = variant->fifohalfsize >> 2, /* # of words */
.dst_maxburst = variant->fifohalfsize >> 2, /* # of words */
};
- struct mmc_data *data = host->data;
struct dma_chan *chan;
struct dma_device *device;
struct dma_async_tx_descriptor *desc;
int nr_sg;
- host->dma_current = NULL;
+ /* Check if next job is already prepared */
+ if (data->host_cookie && !next &&
+ host->dma_current && host->dma_desc_current)
+ return 0;
+
+ if (!next) {
+ host->dma_current = NULL;
+ host->dma_desc_current = NULL;
+ }
if (data->flags & MMC_DATA_READ) {
conf.direction = DMA_FROM_DEVICE;
@@ -385,7 +399,7 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
return -EINVAL;
/* If less than or equal to the fifo size, don't bother with DMA */
- if (host->size <= variant->fifosize)
+ if (data->blksz * data->blocks <= variant->fifosize)
return -EINVAL;
device = chan->device;
@@ -399,14 +413,38 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
if (!desc)
goto unmap_exit;
- /* Okay, go for it. */
- host->dma_current = chan;
+ if (next) {
+ next->dma_chan = chan;
+ next->dma_desc = desc;
+ } else {
+ host->dma_current = chan;
+ host->dma_desc_current = desc;
+ }
+ return 0;
+
+ unmap_exit:
+ if (!next)
+ dmaengine_terminate_all(chan);
+ dma_unmap_sg(device->dev, data->sg, data->sg_len, conf.direction);
+ return -ENOMEM;
+}
+
+static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
+{
+ int ret;
+ struct mmc_data *data = host->data;
+
+ ret = mmci_dma_prep_data(host, host->data, NULL);
+ if (ret)
+ return ret;
+
+ /* Okay, go for it. */
dev_vdbg(mmc_dev(host->mmc),
"Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",
data->sg_len, data->blksz, data->blocks, data->flags);
- dmaengine_submit(desc);
- dma_async_issue_pending(chan);
+ dmaengine_submit(host->dma_desc_current);
+ dma_async_issue_pending(host->dma_current);
datactrl |= MCI_DPSM_DMAENABLE;
@@ -421,14 +459,90 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK,
host->base + MMCIMASK0);
return 0;
+}
-unmap_exit:
- dmaengine_terminate_all(chan);
- dma_unmap_sg(device->dev, data->sg, data->sg_len, conf.direction);
- return -ENOMEM;
+static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
+{
+ struct mmci_host_next *next = &host->next_data;
+
+ if (data->host_cookie && data->host_cookie != next->cookie) {
+ pr_warning("[%s] invalid cookie: data->host_cookie %d"
+ " host->next_data.cookie %d\n",
+ __func__, data->host_cookie, host->next_data.cookie);
+ data->host_cookie = 0;
+ }
+
+ if (!data->host_cookie)
+ return;
+
+ host->dma_desc_current = next->dma_desc;
+ host->dma_current = next->dma_chan;
+
+ next->dma_desc = NULL;
+ next->dma_chan = NULL;
}
+
+static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq,
+ bool is_first_req)
+{
+ struct mmci_host *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+ struct mmci_host_next *nd = &host->next_data;
+
+ if (!data)
+ return;
+
+ if (data->host_cookie) {
+ data->host_cookie = 0;
+ return;
+ }
+
+ /* if config for dma */
+ if (((data->flags & MMC_DATA_WRITE) && host->dma_tx_channel) ||
+ ((data->flags & MMC_DATA_READ) && host->dma_rx_channel)) {
+ if (mmci_dma_prep_data(host, data, nd))
+ data->host_cookie = 0;
+ else
+ data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie;
+ }
+}
+
+static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
+ int err)
+{
+ struct mmci_host *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+ struct dma_chan *chan;
+ enum dma_data_direction dir;
+
+ if (!data)
+ return;
+
+ if (data->flags & MMC_DATA_READ) {
+ dir = DMA_FROM_DEVICE;
+ chan = host->dma_rx_channel;
+ } else {
+ dir = DMA_TO_DEVICE;
+ chan = host->dma_tx_channel;
+ }
+
+
+ /* if config for dma */
+ if (chan) {
+ if (err)
+ dmaengine_terminate_all(chan);
+ if (data->host_cookie)
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, dir);
+ mrq->data->host_cookie = 0;
+ }
+}
+
#else
/* Blank functions if the DMA engine is not available */
+static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
+{
+}
static inline void mmci_dma_setup(struct mmci_host *host)
{
}
@@ -449,6 +563,10 @@ static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datac
{
return -ENOSYS;
}
+
+#define mmci_pre_request NULL
+#define mmci_post_request NULL
+
#endif
static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
@@ -868,10 +986,15 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
return;
}
+ pm_runtime_get_sync(mmc_dev(mmc));
+
spin_lock_irqsave(&host->lock, flags);
host->mrq = mrq;
+ if (mrq->data)
+ mmci_get_next_data(host, mrq->data);
+
if (mrq->data && mrq->data->flags & MMC_DATA_READ)
mmci_start_data(host, mrq->data);
@@ -986,6 +1109,8 @@ static irqreturn_t mmci_cd_irq(int irq, void *dev_id)
static const struct mmc_host_ops mmci_ops = {
.request = mmci_request,
+ .pre_req = mmci_pre_request,
+ .post_req = mmci_post_request,
.set_ios = mmci_set_ios,
.get_ro = mmci_get_ro,
.get_cd = mmci_get_cd,
@@ -1035,10 +1160,14 @@ static int __devinit mmci_probe(struct amba_device *dev,
goto host_free;
}
- ret = clk_enable(host->clk);
+ ret = clk_prepare(host->clk);
if (ret)
goto clk_free;
+ ret = clk_enable(host->clk);
+ if (ret)
+ goto clk_unprep;
+
host->plat = plat;
host->variant = variant;
host->mclk = clk_get_rate(host->clk);
@@ -1063,7 +1192,15 @@ static int __devinit mmci_probe(struct amba_device *dev,
}
mmc->ops = &mmci_ops;
- mmc->f_min = (host->mclk + 511) / 512;
+ /*
+ * The ARM and ST versions of the block have slightly different
+ * clock divider equations which means that the minimum divider
+ * differs too.
+ */
+ if (variant->st_clkdiv)
+ mmc->f_min = DIV_ROUND_UP(host->mclk, 257);
+ else
+ mmc->f_min = DIV_ROUND_UP(host->mclk, 512);
/*
* If the platform data supplies a maximum operating
* frequency, this takes precedence. Else, we fall back
@@ -1198,6 +1335,8 @@ static int __devinit mmci_probe(struct amba_device *dev,
mmci_dma_setup(host);
+ pm_runtime_put(&dev->dev);
+
mmc_add_host(mmc);
return 0;
@@ -1216,6 +1355,8 @@ static int __devinit mmci_probe(struct amba_device *dev,
iounmap(host->base);
clk_disable:
clk_disable(host->clk);
+ clk_unprep:
+ clk_unprepare(host->clk);
clk_free:
clk_put(host->clk);
host_free:
@@ -1235,6 +1376,12 @@ static int __devexit mmci_remove(struct amba_device *dev)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
+ /*
+ * Undo pm_runtime_put() in probe. We use the _sync
+ * version here so that we can access the primecell.
+ */
+ pm_runtime_get_sync(&dev->dev);
+
mmc_remove_host(mmc);
writel(0, host->base + MMCIMASK0);
@@ -1257,6 +1404,7 @@ static int __devexit mmci_remove(struct amba_device *dev)
iounmap(host->base);
clk_disable(host->clk);
+ clk_unprepare(host->clk);
clk_put(host->clk);
if (host->vcc)
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 2164e8c6476c..79e4143ab9df 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -166,6 +166,12 @@ struct clk;
struct variant_data;
struct dma_chan;
+struct mmci_host_next {
+ struct dma_async_tx_descriptor *dma_desc;
+ struct dma_chan *dma_chan;
+ s32 cookie;
+};
+
struct mmci_host {
phys_addr_t phybase;
void __iomem *base;
@@ -203,6 +209,8 @@ struct mmci_host {
struct dma_chan *dma_current;
struct dma_chan *dma_rx_channel;
struct dma_chan *dma_tx_channel;
+ struct dma_async_tx_descriptor *dma_desc_current;
+ struct mmci_host_next next_data;
#define dma_inprogress(host) ((host)->dma_current)
#else
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index a4c865a5286b..80d8eb143b48 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -213,7 +213,8 @@ msmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd)
msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER);
msmsdcc_writel(host, (unsigned int)host->curr.xfer_size,
MMCIDATALENGTH);
- msmsdcc_writel(host, host->cmd_pio_irqmask, MMCIMASK1);
+ msmsdcc_writel(host, (msmsdcc_readl(host, MMCIMASK0) &
+ (~MCI_IRQ_PIO)) | host->cmd_pio_irqmask, MMCIMASK0);
msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL);
if (host->cmd_cmd) {
@@ -388,7 +389,7 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
host->dma.num_ents, host->dma.dir);
if (n == 0) {
- printk(KERN_ERR "%s: Unable to map in all sg elements\n",
+ pr_err("%s: Unable to map in all sg elements\n",
mmc_hostname(host->mmc));
host->dma.sg = NULL;
host->dma.num_ents = 0;
@@ -474,7 +475,7 @@ msmsdcc_start_command_deferred(struct msmsdcc_host *host,
*c |= MCI_CSPM_MCIABORT;
if (host->curr.cmd != NULL) {
- printk(KERN_ERR "%s: Overlapping command requests\n",
+ pr_err("%s: Overlapping command requests\n",
mmc_hostname(host->mmc));
}
host->curr.cmd = cmd;
@@ -543,7 +544,9 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,
msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);
- msmsdcc_writel(host, pio_irqmask, MMCIMASK1);
+ msmsdcc_writel(host, (msmsdcc_readl(host, MMCIMASK0) &
+ (~MCI_IRQ_PIO)) | pio_irqmask, MMCIMASK0);
+
msmsdcc_writel(host, datactrl, MMCIDATACTRL);
if (cmd) {
@@ -659,8 +662,13 @@ msmsdcc_pio_irq(int irq, void *dev_id)
{
struct msmsdcc_host *host = dev_id;
uint32_t status;
+ u32 mci_mask0;
status = msmsdcc_readl(host, MMCISTATUS);
+ mci_mask0 = msmsdcc_readl(host, MMCIMASK0);
+
+ if (((mci_mask0 & status) & MCI_IRQ_PIO) == 0)
+ return IRQ_NONE;
do {
unsigned long flags;
@@ -719,10 +727,12 @@ msmsdcc_pio_irq(int irq, void *dev_id)
} while (1);
if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
- msmsdcc_writel(host, MCI_RXDATAAVLBLMASK, MMCIMASK1);
+ msmsdcc_writel(host, (mci_mask0 & (~MCI_IRQ_PIO)) |
+ MCI_RXDATAAVLBLMASK, MMCIMASK0);
if (!host->curr.xfer_remain)
- msmsdcc_writel(host, 0, MMCIMASK1);
+ msmsdcc_writel(host, (mci_mask0 & (~MCI_IRQ_PIO)) | 0,
+ MMCIMASK0);
return IRQ_HANDLED;
}
@@ -854,6 +864,8 @@ msmsdcc_irq(int irq, void *dev_id)
do {
status = msmsdcc_readl(host, MMCISTATUS);
status &= msmsdcc_readl(host, MMCIMASK0);
+ if ((status & (~MCI_IRQ_PIO)) == 0)
+ break;
msmsdcc_writel(host, status, MMCICLEAR);
if (status & MCI_SDIOINTR)
@@ -939,7 +951,7 @@ static void msmsdcc_setup_gpio(struct msmsdcc_host *host, bool enable)
struct msm_mmc_gpio_data *curr;
int i, rc = 0;
- if (!host->plat->gpio_data && host->gpio_config_status == enable)
+ if (!host->plat->gpio_data || host->gpio_config_status == enable)
return;
curr = host->plat->gpio_data;
@@ -1052,10 +1064,19 @@ static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags);
}
+static void msmsdcc_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+ struct msmsdcc_host *host = mmc_priv(mmc);
+
+ if (host->plat->init_card)
+ host->plat->init_card(card);
+}
+
static const struct mmc_host_ops msmsdcc_ops = {
.request = msmsdcc_request,
.set_ios = msmsdcc_set_ios,
.enable_sdio_irq = msmsdcc_enable_sdio_irq,
+ .init_card = msmsdcc_init_card,
};
static void
@@ -1092,7 +1113,7 @@ msmsdcc_platform_status_irq(int irq, void *dev_id)
{
struct msmsdcc_host *host = dev_id;
- printk(KERN_DEBUG "%s: %d\n", __func__, irq);
+ pr_debug("%s: %d\n", __func__, irq);
msmsdcc_check_status((unsigned long) host);
return IRQ_HANDLED;
}
@@ -1102,7 +1123,7 @@ msmsdcc_status_notify_cb(int card_present, void *dev_id)
{
struct msmsdcc_host *host = dev_id;
- printk(KERN_DEBUG "%s: card_present %d\n", mmc_hostname(host->mmc),
+ pr_debug("%s: card_present %d\n", mmc_hostname(host->mmc),
card_present);
msmsdcc_check_status((unsigned long) host);
}
@@ -1150,7 +1171,6 @@ msmsdcc_probe(struct platform_device *pdev)
struct msmsdcc_host *host;
struct mmc_host *mmc;
struct resource *cmd_irqres = NULL;
- struct resource *pio_irqres = NULL;
struct resource *stat_irqres = NULL;
struct resource *memres = NULL;
struct resource *dmares = NULL;
@@ -1175,12 +1195,10 @@ msmsdcc_probe(struct platform_device *pdev)
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"cmd_irq");
- pio_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "pio_irq");
stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"status_irq");
- if (!cmd_irqres || !pio_irqres || !memres) {
+ if (!cmd_irqres || !memres) {
pr_err("%s: Invalid resource\n", __func__);
return -ENXIO;
}
@@ -1200,17 +1218,20 @@ msmsdcc_probe(struct platform_device *pdev)
host->plat = plat;
host->mmc = mmc;
host->curr.cmd = NULL;
+ init_timer(&host->busclk_timer);
+ host->busclk_timer.data = (unsigned long) host;
+ host->busclk_timer.function = msmsdcc_busclk_expired;
+
host->cmdpoll = 1;
host->base = ioremap(memres->start, PAGE_SIZE);
if (!host->base) {
ret = -ENOMEM;
- goto out;
+ goto host_free;
}
host->cmd_irqres = cmd_irqres;
- host->pio_irqres = pio_irqres;
host->memres = memres;
host->dmares = dmares;
spin_lock_init(&host->lock);
@@ -1221,13 +1242,19 @@ msmsdcc_probe(struct platform_device *pdev)
/*
* Setup DMA
*/
- msmsdcc_init_dma(host);
+ if (host->dmares) {
+ ret = msmsdcc_init_dma(host);
+ if (ret)
+ goto ioremap_free;
+ } else {
+ host->dma.channel = -1;
+ }
/* Get our clocks */
host->pclk = clk_get(&pdev->dev, "sdc_pclk");
if (IS_ERR(host->pclk)) {
ret = PTR_ERR(host->pclk);
- goto host_free;
+ goto dma_free;
}
host->clk = clk_get(&pdev->dev, "sdc_clk");
@@ -1236,17 +1263,17 @@ msmsdcc_probe(struct platform_device *pdev)
goto pclk_put;
}
- /* Enable clocks */
- ret = msmsdcc_enable_clocks(host);
- if (ret)
- goto clk_put;
-
ret = clk_set_rate(host->clk, msmsdcc_fmin);
if (ret) {
pr_err("%s: Clock rate set failed (%d)\n", __func__, ret);
- goto clk_disable;
+ goto clk_put;
}
+ /* Enable clocks */
+ ret = msmsdcc_enable_clocks(host);
+ if (ret)
+ goto clk_put;
+
host->pclk_rate = clk_get_rate(host->pclk);
host->clk_rate = clk_get_rate(host->clk);
@@ -1316,16 +1343,12 @@ msmsdcc_probe(struct platform_device *pdev)
host->eject = !host->oldstat;
}
- init_timer(&host->busclk_timer);
- host->busclk_timer.data = (unsigned long) host;
- host->busclk_timer.function = msmsdcc_busclk_expired;
-
ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
DRIVER_NAME " (cmd)", host);
if (ret)
goto stat_irq_free;
- ret = request_irq(pio_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
+ ret = request_irq(cmd_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
DRIVER_NAME " (pio)", host);
if (ret)
goto cmd_irq_free;
@@ -1368,6 +1391,13 @@ msmsdcc_probe(struct platform_device *pdev)
clk_put(host->clk);
pclk_put:
clk_put(host->pclk);
+dma_free:
+ if (host->dmares)
+ dma_free_coherent(NULL, sizeof(struct msmsdcc_nc_dmadata),
+ host->dma.nc, host->dma.nc_busaddr);
+ioremap_free:
+ tasklet_kill(&host->dma_tlet);
+ iounmap(host->base);
host_free:
mmc_free_host(mmc);
out:
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 42d7bbc977c5..402028d16b86 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -140,6 +140,11 @@
MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK|MCI_PROGDONEMASK)
+#define MCI_IRQ_PIO \
+ (MCI_RXDATAAVLBLMASK | MCI_TXDATAAVLBLMASK | MCI_RXFIFOEMPTYMASK | \
+ MCI_TXFIFOEMPTYMASK | MCI_RXFIFOFULLMASK | MCI_TXFIFOFULLMASK | \
+ MCI_RXFIFOHALFFULLMASK | MCI_TXFIFOHALFEMPTYMASK | \
+ MCI_RXACTIVEMASK | MCI_TXACTIVEMASK)
/*
* The size of the FIFO in bytes.
*/
@@ -202,7 +207,6 @@ struct msmsdcc_stats {
struct msmsdcc_host {
struct resource *cmd_irqres;
- struct resource *pio_irqres;
struct resource *memres;
struct resource *dmares;
void __iomem *base;
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index a5bf60e01af4..211a4959c293 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -117,7 +117,7 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
host->pio_size = data->blocks * data->blksz;
host->pio_ptr = sg_virt(data->sg);
if (!nodma)
- printk(KERN_DEBUG "%s: fallback to PIO for data "
+ pr_debug("%s: fallback to PIO for data "
"at 0x%p size %d\n",
mmc_hostname(host->mmc),
host->pio_ptr, host->pio_size);
@@ -471,7 +471,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
if (mrq->data)
err_status = mvsd_finish_data(host, mrq->data, err_status);
if (err_status) {
- printk(KERN_ERR "%s: unhandled error status %#04x\n",
+ pr_err("%s: unhandled error status %#04x\n",
mmc_hostname(host->mmc), err_status);
cmd->error = -ENOMSG;
}
@@ -489,7 +489,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
if (irq_handled)
return IRQ_HANDLED;
- printk(KERN_ERR "%s: unhandled interrupt status=0x%04x en=0x%04x "
+ pr_err("%s: unhandled interrupt status=0x%04x en=0x%04x "
"pio=%d\n", mmc_hostname(host->mmc), intr_status,
host->intr_en, host->pio_size);
return IRQ_NONE;
@@ -505,9 +505,9 @@ static void mvsd_timeout_timer(unsigned long data)
spin_lock_irqsave(&host->lock, flags);
mrq = host->mrq;
if (mrq) {
- printk(KERN_ERR "%s: Timeout waiting for hardware interrupt.\n",
+ pr_err("%s: Timeout waiting for hardware interrupt.\n",
mmc_hostname(host->mmc));
- printk(KERN_ERR "%s: hw_state=0x%04x, intr_status=0x%04x "
+ pr_err("%s: hw_state=0x%04x, intr_status=0x%04x "
"intr_en=0x%04x\n", mmc_hostname(host->mmc),
mvsd_read(MVSD_HW_STATE),
mvsd_read(MVSD_NOR_INTR_STATUS),
@@ -762,7 +762,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host);
if (ret) {
- printk(KERN_ERR "%s: cannot assign irq %d\n", DRIVER_NAME, irq);
+ pr_err("%s: cannot assign irq %d\n", DRIVER_NAME, irq);
goto out;
} else
host->irq = irq;
@@ -802,7 +802,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
if (ret)
goto out;
- printk(KERN_NOTICE "%s: %s driver initialized, ",
+ pr_notice("%s: %s driver initialized, ",
mmc_hostname(mmc), DRIVER_NAME);
if (host->gpio_card_detect)
printk("using GPIO %d for card detection\n",
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index cc20e0259325..325ea61e12d3 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -40,6 +40,7 @@
#include <mach/mmc.h>
#include <mach/dma.h>
+#include <mach/hardware.h>
#define DRIVER_NAME "mxc-mmc"
@@ -715,13 +716,13 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
int burstlen, ret;
/*
- * use burstlen of 64 in 4 bit mode (--> reg value 0)
- * use burstlen of 16 in 1 bit mode (--> reg value 16)
+ * use burstlen of 64 (16 words) in 4 bit mode (--> reg value 0)
+ * use burstlen of 16 (4 words) in 1 bit mode (--> reg value 16)
*/
if (ios->bus_width == MMC_BUS_WIDTH_4)
- burstlen = 64;
- else
burstlen = 16;
+ else
+ burstlen = 4;
if (mxcmci_use_dma(host) && burstlen != host->burstlen) {
host->burstlen = burstlen;
@@ -842,7 +843,7 @@ static int mxcmci_probe(struct platform_device *pdev)
int ret = 0, irq;
dma_cap_mask_t mask;
- printk(KERN_INFO "i.MX SDHC driver\n");
+ pr_info("i.MX SDHC driver\n");
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 99d39a6a1032..99b449d26a4d 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -37,6 +37,7 @@
#include <linux/mmc/sdio.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/module.h>
#include <mach/mxs.h>
#include <mach/common.h>
@@ -564,40 +565,38 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate)
{
- unsigned int ssp_rate, bit_rate;
- u32 div1, div2;
+ unsigned int ssp_clk, ssp_sck;
+ u32 clock_divide, clock_rate;
u32 val;
- ssp_rate = clk_get_rate(host->clk);
+ ssp_clk = clk_get_rate(host->clk);
- for (div1 = 2; div1 < 254; div1 += 2) {
- div2 = ssp_rate / rate / div1;
- if (div2 < 0x100)
+ for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) {
+ clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide);
+ clock_rate = (clock_rate > 0) ? clock_rate - 1 : 0;
+ if (clock_rate <= 255)
break;
}
- if (div1 >= 254) {
+ if (clock_divide > 254) {
dev_err(mmc_dev(host->mmc),
"%s: cannot set clock to %d\n", __func__, rate);
return;
}
- if (div2 == 0)
- bit_rate = ssp_rate / div1;
- else
- bit_rate = ssp_rate / div1 / div2;
+ ssp_sck = ssp_clk / clock_divide / (1 + clock_rate);
val = readl(host->base + HW_SSP_TIMING);
val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE);
- val |= BF_SSP(div1, TIMING_CLOCK_DIVIDE);
- val |= BF_SSP(div2 - 1, TIMING_CLOCK_RATE);
+ val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE);
+ val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE);
writel(val, host->base + HW_SSP_TIMING);
- host->clk_rate = bit_rate;
+ host->clk_rate = ssp_sck;
dev_dbg(mmc_dev(host->mmc),
- "%s: div1 %d, div2 %d, ssp %d, bit %d, rate %d\n",
- __func__, div1, div2, ssp_rate, bit_rate, rate);
+ "%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n",
+ __func__, clock_divide, clock_rate, ssp_clk, ssp_sck, rate);
}
static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index a6c329040140..2dba999caf2c 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -33,7 +33,7 @@
#include <plat/board.h>
#include <plat/mmc.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#include <plat/dma.h>
#include <plat/mux.h>
#include <plat/fpga.h>
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index dedf3dab8a3b..101cd31c8220 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
@@ -33,6 +34,7 @@
#include <linux/semaphore.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
#include <plat/dma.h>
#include <mach/hardware.h>
#include <plat/board.h>
@@ -116,15 +118,13 @@
#define OMAP_MMC4_DEVID 3
#define OMAP_MMC5_DEVID 4
+#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20
#define OMAP_MMC_MASTER_CLOCK 96000000
+#define OMAP_MMC_MIN_CLOCK 400000
+#define OMAP_MMC_MAX_CLOCK 52000000
#define DRIVER_NAME "omap_hsmmc"
-/* Timeouts for entering power saving states on inactivity, msec */
-#define OMAP_MMC_DISABLED_TIMEOUT 100
-#define OMAP_MMC_SLEEP_TIMEOUT 1000
-#define OMAP_MMC_OFF_TIMEOUT 8000
-
/*
* One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known
@@ -141,6 +141,11 @@
#define OMAP_HSMMC_WRITE(base, reg, val) \
__raw_writel((val), (base) + OMAP_HSMMC_##reg)
+struct omap_hsmmc_next {
+ unsigned int dma_len;
+ s32 cookie;
+};
+
struct omap_hsmmc_host {
struct device *dev;
struct mmc_host *mmc;
@@ -148,7 +153,6 @@ struct omap_hsmmc_host {
struct mmc_command *cmd;
struct mmc_data *data;
struct clk *fclk;
- struct clk *iclk;
struct clk *dbclk;
/*
* vcc == configured supply
@@ -184,6 +188,7 @@ struct omap_hsmmc_host {
int reqs_blocked;
int use_reg;
int req_in_progress;
+ struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata;
};
@@ -445,15 +450,14 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
* framework is fixed, we need a workaround like this
* (which is safe for MMC, but not in general).
*/
- if (regulator_is_enabled(host->vcc) > 0) {
- regulator_enable(host->vcc);
- regulator_disable(host->vcc);
- }
- if (host->vcc_aux) {
- if (regulator_is_enabled(reg) > 0) {
- regulator_enable(reg);
- regulator_disable(reg);
- }
+ if (regulator_is_enabled(host->vcc) > 0 ||
+ (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
+ int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
+
+ mmc_slot(host).set_power(host->dev, host->slot_id,
+ 1, vdd);
+ mmc_slot(host).set_power(host->dev, host->slot_id,
+ 0, 0);
}
}
@@ -548,6 +552,15 @@ static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)
}
/*
+ * Start clock to the card
+ */
+static void omap_hsmmc_start_clock(struct omap_hsmmc_host *host)
+{
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
+ OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
+}
+
+/*
* Stop clock to the card
*/
static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
@@ -584,6 +597,81 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
}
+/* Calculate divisor for the given clock frequency */
+static u16 calc_divisor(struct mmc_ios *ios)
+{
+ u16 dsor = 0;
+
+ if (ios->clock) {
+ dsor = DIV_ROUND_UP(OMAP_MMC_MASTER_CLOCK, ios->clock);
+ if (dsor > 250)
+ dsor = 250;
+ }
+
+ return dsor;
+}
+
+static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
+{
+ struct mmc_ios *ios = &host->mmc->ios;
+ unsigned long regval;
+ unsigned long timeout;
+
+ dev_dbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock);
+
+ omap_hsmmc_stop_clock(host);
+
+ regval = OMAP_HSMMC_READ(host->base, SYSCTL);
+ regval = regval & ~(CLKD_MASK | DTO_MASK);
+ regval = regval | (calc_divisor(ios) << 6) | (DTO << 16);
+ OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
+ OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
+
+ /* Wait till the ICS bit is set */
+ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+ while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
+ && time_before(jiffies, timeout))
+ cpu_relax();
+
+ omap_hsmmc_start_clock(host);
+}
+
+static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)
+{
+ struct mmc_ios *ios = &host->mmc->ios;
+ u32 con;
+
+ con = OMAP_HSMMC_READ(host->base, CON);
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_8:
+ OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
+ break;
+ case MMC_BUS_WIDTH_4:
+ OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
+ break;
+ case MMC_BUS_WIDTH_1:
+ OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
+ break;
+ }
+}
+
+static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)
+{
+ struct mmc_ios *ios = &host->mmc->ios;
+ u32 con;
+
+ con = OMAP_HSMMC_READ(host->base, CON);
+ if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ OMAP_HSMMC_WRITE(host->base, CON, con | OD);
+ else
+ OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
+}
+
#ifdef CONFIG_PM
/*
@@ -595,8 +683,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
struct mmc_ios *ios = &host->mmc->ios;
struct omap_mmc_platform_data *pdata = host->pdata;
int context_loss = 0;
- u32 hctl, capa, con;
- u16 dsor = 0;
+ u32 hctl, capa;
unsigned long timeout;
if (pdata->get_context_loss_count) {
@@ -658,54 +745,12 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
if (host->power_mode == MMC_POWER_OFF)
goto out;
- con = OMAP_HSMMC_READ(host->base, CON);
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_8:
- OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
- break;
- case MMC_BUS_WIDTH_4:
- OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
- break;
- case MMC_BUS_WIDTH_1:
- OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
- break;
- }
-
- if (ios->clock) {
- dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
- if (dsor < 1)
- dsor = 1;
+ omap_hsmmc_set_bus_width(host);
- if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
- dsor++;
+ omap_hsmmc_set_clock(host);
- if (dsor > 250)
- dsor = 250;
- }
-
- OMAP_HSMMC_WRITE(host->base, SYSCTL,
- OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
- OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16));
- OMAP_HSMMC_WRITE(host->base, SYSCTL,
- OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
-
- timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
- while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
- && time_before(jiffies, timeout))
- ;
+ omap_hsmmc_set_bus_mode(host);
- OMAP_HSMMC_WRITE(host->base, SYSCTL,
- OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
-
- con = OMAP_HSMMC_READ(host->base, CON);
- if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
- OMAP_HSMMC_WRITE(host->base, CON, con | OD);
- else
- OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
out:
host->context_loss = context_loss;
@@ -973,14 +1018,14 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
* Readable error output
*/
#ifdef CONFIG_MMC_DEBUG
-static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)
+static void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host, u32 status)
{
/* --- means reserved bit without definition at documentation */
static const char *omap_hsmmc_status_bits[] = {
- "CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ",
- "OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC",
- "CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---",
- "---", "---", "---", "CERR", "CERR", "BADA", "---", "---", "---"
+ "CC" , "TC" , "BGE", "---", "BWR" , "BRR" , "---" , "---" ,
+ "CIRQ", "OBI" , "---", "---", "---" , "---" , "---" , "ERRI",
+ "CTO" , "CCRC", "CEB", "CIE", "DTO" , "DCRC", "DEB" , "---" ,
+ "ACE" , "---" , "---", "---", "CERR", "BADA", "---" , "---"
};
char res[256];
char *buf = res;
@@ -997,6 +1042,11 @@ static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)
dev_dbg(mmc_dev(host->mmc), "%s\n", res);
}
+#else
+static inline void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host,
+ u32 status)
+{
+}
#endif /* CONFIG_MMC_DEBUG */
/*
@@ -1055,9 +1105,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
if (status & ERR) {
-#ifdef CONFIG_MMC_DEBUG
- omap_hsmmc_report_irq(host, status);
-#endif
+ omap_hsmmc_dbg_report_irq(host, status);
if ((status & CMD_TIMEOUT) ||
(status & CMD_CRC)) {
if (host->cmd) {
@@ -1155,8 +1203,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
int ret;
/* Disable the clocks */
- clk_disable(host->fclk);
- clk_disable(host->iclk);
+ pm_runtime_put_sync(host->dev);
if (host->got_dbclk)
clk_disable(host->dbclk);
@@ -1167,8 +1214,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
if (!ret)
ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,
vdd);
- clk_enable(host->iclk);
- clk_enable(host->fclk);
+ pm_runtime_get_sync(host->dev);
if (host->got_dbclk)
clk_enable(host->dbclk);
@@ -1217,14 +1263,14 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
host->reqs_blocked = 0;
if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {
if (host->protect_card) {
- printk(KERN_INFO "%s: cover is closed, "
+ pr_info("%s: cover is closed, "
"card is now accessible\n",
mmc_hostname(host->mmc));
host->protect_card = 0;
}
} else {
if (!host->protect_card) {
- printk(KERN_INFO "%s: cover is open, "
+ pr_info("%s: cover is open, "
"card is now inaccessible\n",
mmc_hostname(host->mmc));
host->protect_card = 1;
@@ -1322,7 +1368,7 @@ static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
{
struct omap_hsmmc_host *host = cb_data;
- struct mmc_data *data = host->mrq->data;
+ struct mmc_data *data;
int dma_ch, req_in_progress;
if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
@@ -1337,6 +1383,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
return;
}
+ data = host->mrq->data;
host->dma_sg_idx++;
if (host->dma_sg_idx < host->dma_len) {
/* Fire up the next transfer. */
@@ -1346,8 +1393,9 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
return;
}
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
- omap_hsmmc_get_dma_dir(host, data));
+ if (!data->host_cookie)
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ omap_hsmmc_get_dma_dir(host, data));
req_in_progress = host->req_in_progress;
dma_ch = host->dma_ch;
@@ -1365,6 +1413,45 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
}
}
+static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
+ struct mmc_data *data,
+ struct omap_hsmmc_next *next)
+{
+ int dma_len;
+
+ if (!next && data->host_cookie &&
+ data->host_cookie != host->next_data.cookie) {
+ pr_warning("[%s] invalid cookie: data->host_cookie %d"
+ " host->next_data.cookie %d\n",
+ __func__, data->host_cookie, host->next_data.cookie);
+ data->host_cookie = 0;
+ }
+
+ /* Check if next job is already prepared */
+ if (next ||
+ (!next && data->host_cookie != host->next_data.cookie)) {
+ dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len,
+ omap_hsmmc_get_dma_dir(host, data));
+
+ } else {
+ dma_len = host->next_data.dma_len;
+ host->next_data.dma_len = 0;
+ }
+
+
+ if (dma_len == 0)
+ return -EINVAL;
+
+ if (next) {
+ next->dma_len = dma_len;
+ data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
+ } else
+ host->dma_len = dma_len;
+
+ return 0;
+}
+
/*
* Routine to configure and start DMA for the MMC card
*/
@@ -1398,9 +1485,10 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
mmc_hostname(host->mmc), ret);
return ret;
}
+ ret = omap_hsmmc_pre_dma_transfer(host, data, NULL);
+ if (ret)
+ return ret;
- host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, omap_hsmmc_get_dma_dir(host, data));
host->dma_ch = dma_ch;
host->dma_sg_idx = 0;
@@ -1480,6 +1568,35 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
return 0;
}
+static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ int err)
+{
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (host->use_dma) {
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ omap_hsmmc_get_dma_dir(host, data));
+ data->host_cookie = 0;
+ }
+}
+
+static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ bool is_first_req)
+{
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
+
+ if (mrq->data->host_cookie) {
+ mrq->data->host_cookie = 0;
+ return ;
+ }
+
+ if (host->use_dma)
+ if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
+ &host->next_data))
+ mrq->data->host_cookie = 0;
+}
+
/*
* Request function. for read/write operation
*/
@@ -1528,13 +1645,9 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
- u16 dsor = 0;
- unsigned long regval;
- unsigned long timeout;
- u32 con;
int do_send_init_stream = 0;
- mmc_host_enable(host->mmc);
+ pm_runtime_get_sync(host->dev);
if (ios->power_mode != host->power_mode) {
switch (ios->power_mode) {
@@ -1557,22 +1670,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
/* FIXME: set registers based only on changes to ios */
- con = OMAP_HSMMC_READ(host->base, CON);
- switch (mmc->ios.bus_width) {
- case MMC_BUS_WIDTH_8:
- OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
- break;
- case MMC_BUS_WIDTH_4:
- OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
- break;
- case MMC_BUS_WIDTH_1:
- OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
- break;
- }
+ omap_hsmmc_set_bus_width(host);
if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
/* Only MMC1 can interface at 3V without some flavor
@@ -1592,47 +1690,14 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
}
- if (ios->clock) {
- dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
- if (dsor < 1)
- dsor = 1;
-
- if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
- dsor++;
-
- if (dsor > 250)
- dsor = 250;
- }
- omap_hsmmc_stop_clock(host);
- regval = OMAP_HSMMC_READ(host->base, SYSCTL);
- regval = regval & ~(CLKD_MASK);
- regval = regval | (dsor << 6) | (DTO << 16);
- OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
- OMAP_HSMMC_WRITE(host->base, SYSCTL,
- OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
-
- /* Wait till the ICS bit is set */
- timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
- while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
- && time_before(jiffies, timeout))
- msleep(1);
-
- OMAP_HSMMC_WRITE(host->base, SYSCTL,
- OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
+ omap_hsmmc_set_clock(host);
if (do_send_init_stream)
send_init_stream(host);
- con = OMAP_HSMMC_READ(host->base, CON);
- if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
- OMAP_HSMMC_WRITE(host->base, CON, con | OD);
- else
- OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
+ omap_hsmmc_set_bus_mode(host);
- if (host->power_mode == MMC_POWER_OFF)
- mmc_host_disable(host->mmc);
- else
- mmc_host_lazy_disable(host->mmc);
+ pm_runtime_put_autosuspend(host->dev);
}
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1688,230 +1753,12 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
set_sd_bus_power(host);
}
-/*
- * Dynamic power saving handling, FSM:
- * ENABLED -> DISABLED -> CARDSLEEP / REGSLEEP -> OFF
- * ^___________| | |
- * |______________________|______________________|
- *
- * ENABLED: mmc host is fully functional
- * DISABLED: fclk is off
- * CARDSLEEP: fclk is off, card is asleep, voltage regulator is asleep
- * REGSLEEP: fclk is off, voltage regulator is asleep
- * OFF: fclk is off, voltage regulator is off
- *
- * Transition handlers return the timeout for the next state transition
- * or negative error.
- */
-
-enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF};
-
-/* Handler for [ENABLED -> DISABLED] transition */
-static int omap_hsmmc_enabled_to_disabled(struct omap_hsmmc_host *host)
-{
- omap_hsmmc_context_save(host);
- clk_disable(host->fclk);
- host->dpm_state = DISABLED;
-
- dev_dbg(mmc_dev(host->mmc), "ENABLED -> DISABLED\n");
-
- if (host->power_mode == MMC_POWER_OFF)
- return 0;
-
- return OMAP_MMC_SLEEP_TIMEOUT;
-}
-
-/* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */
-static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *host)
-{
- int err, new_state;
-
- if (!mmc_try_claim_host(host->mmc))
- return 0;
-
- clk_enable(host->fclk);
- omap_hsmmc_context_restore(host);
- if (mmc_card_can_sleep(host->mmc)) {
- err = mmc_card_sleep(host->mmc);
- if (err < 0) {
- clk_disable(host->fclk);
- mmc_release_host(host->mmc);
- return err;
- }
- new_state = CARDSLEEP;
- } else {
- new_state = REGSLEEP;
- }
- if (mmc_slot(host).set_sleep)
- mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0,
- new_state == CARDSLEEP);
- /* FIXME: turn off bus power and perhaps interrupts too */
- clk_disable(host->fclk);
- host->dpm_state = new_state;
-
- mmc_release_host(host->mmc);
-
- dev_dbg(mmc_dev(host->mmc), "DISABLED -> %s\n",
- host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
-
- if (mmc_slot(host).no_off)
- return 0;
-
- if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
- mmc_slot(host).card_detect ||
- (mmc_slot(host).get_cover_state &&
- mmc_slot(host).get_cover_state(host->dev, host->slot_id)))
- return OMAP_MMC_OFF_TIMEOUT;
-
- return 0;
-}
-
-/* Handler for [REGSLEEP / CARDSLEEP -> OFF] transition */
-static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host)
-{
- if (!mmc_try_claim_host(host->mmc))
- return 0;
-
- if (mmc_slot(host).no_off)
- return 0;
-
- if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
- mmc_slot(host).card_detect ||
- (mmc_slot(host).get_cover_state &&
- mmc_slot(host).get_cover_state(host->dev, host->slot_id)))) {
- mmc_release_host(host->mmc);
- return 0;
- }
-
- mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
- host->vdd = 0;
- host->power_mode = MMC_POWER_OFF;
-
- dev_dbg(mmc_dev(host->mmc), "%s -> OFF\n",
- host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
-
- host->dpm_state = OFF;
-
- mmc_release_host(host->mmc);
-
- return 0;
-}
-
-/* Handler for [DISABLED -> ENABLED] transition */
-static int omap_hsmmc_disabled_to_enabled(struct omap_hsmmc_host *host)
-{
- int err;
-
- err = clk_enable(host->fclk);
- if (err < 0)
- return err;
-
- omap_hsmmc_context_restore(host);
- host->dpm_state = ENABLED;
-
- dev_dbg(mmc_dev(host->mmc), "DISABLED -> ENABLED\n");
-
- return 0;
-}
-
-/* Handler for [SLEEP -> ENABLED] transition */
-static int omap_hsmmc_sleep_to_enabled(struct omap_hsmmc_host *host)
-{
- if (!mmc_try_claim_host(host->mmc))
- return 0;
-
- clk_enable(host->fclk);
- omap_hsmmc_context_restore(host);
- if (mmc_slot(host).set_sleep)
- mmc_slot(host).set_sleep(host->dev, host->slot_id, 0,
- host->vdd, host->dpm_state == CARDSLEEP);
- if (mmc_card_can_sleep(host->mmc))
- mmc_card_awake(host->mmc);
-
- dev_dbg(mmc_dev(host->mmc), "%s -> ENABLED\n",
- host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
-
- host->dpm_state = ENABLED;
-
- mmc_release_host(host->mmc);
-
- return 0;
-}
-
-/* Handler for [OFF -> ENABLED] transition */
-static int omap_hsmmc_off_to_enabled(struct omap_hsmmc_host *host)
-{
- clk_enable(host->fclk);
-
- omap_hsmmc_context_restore(host);
- omap_hsmmc_conf_bus_power(host);
- mmc_power_restore_host(host->mmc);
-
- host->dpm_state = ENABLED;
-
- dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n");
-
- return 0;
-}
-
-/*
- * Bring MMC host to ENABLED from any other PM state.
- */
-static int omap_hsmmc_enable(struct mmc_host *mmc)
-{
- struct omap_hsmmc_host *host = mmc_priv(mmc);
-
- switch (host->dpm_state) {
- case DISABLED:
- return omap_hsmmc_disabled_to_enabled(host);
- case CARDSLEEP:
- case REGSLEEP:
- return omap_hsmmc_sleep_to_enabled(host);
- case OFF:
- return omap_hsmmc_off_to_enabled(host);
- default:
- dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
- return -EINVAL;
- }
-}
-
-/*
- * Bring MMC host in PM state (one level deeper).
- */
-static int omap_hsmmc_disable(struct mmc_host *mmc, int lazy)
-{
- struct omap_hsmmc_host *host = mmc_priv(mmc);
-
- switch (host->dpm_state) {
- case ENABLED: {
- int delay;
-
- delay = omap_hsmmc_enabled_to_disabled(host);
- if (lazy || delay < 0)
- return delay;
- return 0;
- }
- case DISABLED:
- return omap_hsmmc_disabled_to_sleep(host);
- case CARDSLEEP:
- case REGSLEEP:
- return omap_hsmmc_sleep_to_off(host);
- default:
- dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
- return -EINVAL;
- }
-}
-
static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
- int err;
- err = clk_enable(host->fclk);
- if (err)
- return err;
- dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
- omap_hsmmc_context_restore(host);
+ pm_runtime_get_sync(host->dev);
+
return 0;
}
@@ -1919,26 +1766,17 @@ static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
- omap_hsmmc_context_save(host);
- clk_disable(host->fclk);
- dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
+
return 0;
}
static const struct mmc_host_ops omap_hsmmc_ops = {
.enable = omap_hsmmc_enable_fclk,
.disable = omap_hsmmc_disable_fclk,
- .request = omap_hsmmc_request,
- .set_ios = omap_hsmmc_set_ios,
- .get_cd = omap_hsmmc_get_cd,
- .get_ro = omap_hsmmc_get_ro,
- .init_card = omap_hsmmc_init_card,
- /* NYET -- enable_sdio_irq */
-};
-
-static const struct mmc_host_ops omap_hsmmc_ps_ops = {
- .enable = omap_hsmmc_enable,
- .disable = omap_hsmmc_disable,
+ .post_req = omap_hsmmc_post_req,
+ .pre_req = omap_hsmmc_pre_req,
.request = omap_hsmmc_request,
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
@@ -1968,15 +1806,12 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
host->dpm_state, mmc->nesting_cnt,
host->context_loss, context_loss);
- if (host->suspended || host->dpm_state == OFF) {
+ if (host->suspended) {
seq_printf(s, "host suspended, can't read registers\n");
return 0;
}
- if (clk_enable(host->fclk) != 0) {
- seq_printf(s, "can't read the regs\n");
- return 0;
- }
+ pm_runtime_get_sync(host->dev);
seq_printf(s, "SYSCONFIG:\t0x%08x\n",
OMAP_HSMMC_READ(host->base, SYSCONFIG));
@@ -1993,7 +1828,8 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
seq_printf(s, "CAPA:\t\t0x%08x\n",
OMAP_HSMMC_READ(host->base, CAPA));
- clk_disable(host->fclk);
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
return 0;
}
@@ -2077,14 +1913,12 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
host->mapbase = res->start;
host->base = ioremap(host->mapbase, SZ_4K);
host->power_mode = MMC_POWER_OFF;
+ host->next_data.cookie = 1;
platform_set_drvdata(pdev, host);
INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect);
- if (mmc_slot(host).power_saving)
- mmc->ops = &omap_hsmmc_ps_ops;
- else
- mmc->ops = &omap_hsmmc_ops;
+ mmc->ops = &omap_hsmmc_ops;
/*
* If regulator_disable can only put vcc_aux to sleep then there is
@@ -2093,44 +1927,30 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
if (mmc_slot(host).vcc_aux_disable_is_sleep)
mmc_slot(host).no_off = 1;
- mmc->f_min = 400000;
- mmc->f_max = 52000000;
+ mmc->f_min = OMAP_MMC_MIN_CLOCK;
+ mmc->f_max = OMAP_MMC_MAX_CLOCK;
spin_lock_init(&host->irq_lock);
- host->iclk = clk_get(&pdev->dev, "ick");
- if (IS_ERR(host->iclk)) {
- ret = PTR_ERR(host->iclk);
- host->iclk = NULL;
- goto err1;
- }
host->fclk = clk_get(&pdev->dev, "fck");
if (IS_ERR(host->fclk)) {
ret = PTR_ERR(host->fclk);
host->fclk = NULL;
- clk_put(host->iclk);
goto err1;
}
omap_hsmmc_context_save(host);
mmc->caps |= MMC_CAP_DISABLE;
- mmc_set_disable_delay(mmc, OMAP_MMC_DISABLED_TIMEOUT);
- /* we start off in DISABLED state */
- host->dpm_state = DISABLED;
-
- if (clk_enable(host->iclk) != 0) {
- clk_put(host->iclk);
- clk_put(host->fclk);
- goto err1;
+ if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
+ dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
+ mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;
}
- if (mmc_host_enable(host->mmc) != 0) {
- clk_disable(host->iclk);
- clk_put(host->iclk);
- clk_put(host->fclk);
- goto err1;
- }
+ pm_runtime_enable(host->dev);
+ pm_runtime_get_sync(host->dev);
+ pm_runtime_set_autosuspend_delay(host->dev, MMC_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(host->dev);
if (cpu_is_omap2430()) {
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
@@ -2198,7 +2018,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
}
/* Request IRQ for MMC operations */
- ret = request_irq(host->irq, omap_hsmmc_irq, IRQF_DISABLED,
+ ret = request_irq(host->irq, omap_hsmmc_irq, 0,
mmc_hostname(mmc), host);
if (ret) {
dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
@@ -2226,8 +2046,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
if ((mmc_slot(host).card_detect_irq)) {
ret = request_irq(mmc_slot(host).card_detect_irq,
omap_hsmmc_cd_handler,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
- | IRQF_DISABLED,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
mmc_hostname(mmc), host);
if (ret) {
dev_dbg(mmc_dev(host->mmc),
@@ -2240,8 +2059,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_disable_irq(host);
- mmc_host_lazy_disable(host->mmc);
-
omap_hsmmc_protect_card(host);
mmc_add_host(mmc);
@@ -2259,6 +2076,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
}
omap_hsmmc_debugfs(mmc);
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
return 0;
@@ -2274,10 +2093,9 @@ err_reg:
err_irq_cd_init:
free_irq(host->irq, host);
err_irq:
- mmc_host_disable(host->mmc);
- clk_disable(host->iclk);
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
clk_put(host->fclk);
- clk_put(host->iclk);
if (host->got_dbclk) {
clk_disable(host->dbclk);
clk_put(host->dbclk);
@@ -2299,7 +2117,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
struct resource *res;
if (host) {
- mmc_host_enable(host->mmc);
+ pm_runtime_get_sync(host->dev);
mmc_remove_host(host->mmc);
if (host->use_reg)
omap_hsmmc_reg_put(host);
@@ -2310,10 +2128,9 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
free_irq(mmc_slot(host).card_detect_irq, host);
flush_work_sync(&host->mmc_carddetect_work);
- mmc_host_disable(host->mmc);
- clk_disable(host->iclk);
+ pm_runtime_put_sync(host->dev);
+ pm_runtime_disable(host->dev);
clk_put(host->fclk);
- clk_put(host->iclk);
if (host->got_dbclk) {
clk_disable(host->dbclk);
clk_put(host->dbclk);
@@ -2343,6 +2160,7 @@ static int omap_hsmmc_suspend(struct device *dev)
return 0;
if (host) {
+ pm_runtime_get_sync(host->dev);
host->suspended = 1;
if (host->pdata->suspend) {
ret = host->pdata->suspend(&pdev->dev,
@@ -2357,13 +2175,11 @@ static int omap_hsmmc_suspend(struct device *dev)
}
cancel_work_sync(&host->mmc_carddetect_work);
ret = mmc_suspend_host(host->mmc);
- mmc_host_enable(host->mmc);
+
if (ret == 0) {
omap_hsmmc_disable_irq(host);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
- mmc_host_disable(host->mmc);
- clk_disable(host->iclk);
if (host->got_dbclk)
clk_disable(host->dbclk);
} else {
@@ -2375,9 +2191,8 @@ static int omap_hsmmc_suspend(struct device *dev)
dev_dbg(mmc_dev(host->mmc),
"Unmask interrupt failed\n");
}
- mmc_host_disable(host->mmc);
}
-
+ pm_runtime_put_sync(host->dev);
}
return ret;
}
@@ -2393,14 +2208,7 @@ static int omap_hsmmc_resume(struct device *dev)
return 0;
if (host) {
- ret = clk_enable(host->iclk);
- if (ret)
- goto clk_en_err;
-
- if (mmc_host_enable(host->mmc) != 0) {
- clk_disable(host->iclk);
- goto clk_en_err;
- }
+ pm_runtime_get_sync(host->dev);
if (host->got_dbclk)
clk_enable(host->dbclk);
@@ -2421,15 +2229,12 @@ static int omap_hsmmc_resume(struct device *dev)
if (ret == 0)
host->suspended = 0;
- mmc_host_lazy_disable(host->mmc);
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
}
return ret;
-clk_en_err:
- dev_dbg(mmc_dev(host->mmc),
- "Failed to enable MMC clocks during resume\n");
- return ret;
}
#else
@@ -2437,9 +2242,33 @@ clk_en_err:
#define omap_hsmmc_resume NULL
#endif
+static int omap_hsmmc_runtime_suspend(struct device *dev)
+{
+ struct omap_hsmmc_host *host;
+
+ host = platform_get_drvdata(to_platform_device(dev));
+ omap_hsmmc_context_save(host);
+ dev_dbg(mmc_dev(host->mmc), "disabled\n");
+
+ return 0;
+}
+
+static int omap_hsmmc_runtime_resume(struct device *dev)
+{
+ struct omap_hsmmc_host *host;
+
+ host = platform_get_drvdata(to_platform_device(dev));
+ omap_hsmmc_context_restore(host);
+ dev_dbg(mmc_dev(host->mmc), "enabled\n");
+
+ return 0;
+}
+
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
.suspend = omap_hsmmc_suspend,
.resume = omap_hsmmc_resume,
+ .runtime_suspend = omap_hsmmc_runtime_suspend,
+ .runtime_resume = omap_hsmmc_runtime_resume,
};
static struct platform_driver omap_hsmmc_driver = {
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 7257738fd7da..fc4356e00d46 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -558,7 +558,7 @@ static void pxamci_dma_irq(int dma, void *devid)
if (dcsr & DCSR_ENDINTR) {
writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
} else {
- printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
+ pr_err("%s: DMA error on channel %d (DCSR=%#x)\n",
mmc_hostname(host->mmc), dma, dcsr);
host->data->error = -EIO;
pxamci_data_done(host, 0);
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index a04f87d7ee3d..720f99334a7f 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -247,7 +247,7 @@ static void s3cmci_check_sdio_irq(struct s3cmci_host *host)
{
if (host->sdio_irqen) {
if (gpio_get_value(S3C2410_GPE(8)) == 0) {
- printk(KERN_DEBUG "%s: signalling irq\n", __func__);
+ pr_debug("%s: signalling irq\n", __func__);
mmc_signal_sdio_irq(host->mmc);
}
}
@@ -344,7 +344,7 @@ static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer)
local_irq_save(flags);
- //printk(KERN_DEBUG "%s: transfer %d\n", __func__, transfer);
+ /* pr_debug("%s: transfer %d\n", __func__, transfer); */
host->irq_disabled = transfer;
@@ -913,9 +913,9 @@ request_done:
}
static void s3cmci_dma_setup(struct s3cmci_host *host,
- enum s3c2410_dmasrc source)
+ enum dma_data_direction source)
{
- static enum s3c2410_dmasrc last_source = -1;
+ static enum dma_data_direction last_source = -1;
static int setup_ok;
if (last_source == source)
@@ -1087,7 +1087,7 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
- s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);
+ s3cmci_dma_setup(host, rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c
index 9ebd1d7759dc..4b920b7621cf 100644
--- a/drivers/mmc/host/sdhci-cns3xxx.c
+++ b/drivers/mmc/host/sdhci-cns3xxx.c
@@ -15,9 +15,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mmc/host.h>
-#include <linux/mmc/sdhci-pltfm.h>
#include <mach/cns3xxx.h>
-#include "sdhci.h"
#include "sdhci-pltfm.h"
static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host)
@@ -86,7 +84,7 @@ static struct sdhci_ops sdhci_cns3xxx_ops = {
.set_clock = sdhci_cns3xxx_set_clock,
};
-struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
+static struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
.ops = &sdhci_cns3xxx_ops,
.quirks = SDHCI_QUIRK_BROKEN_DMA |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
@@ -95,3 +93,43 @@ struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_NONSTANDARD_CLOCK,
};
+
+static int __devinit sdhci_cns3xxx_probe(struct platform_device *pdev)
+{
+ return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata);
+}
+
+static int __devexit sdhci_cns3xxx_remove(struct platform_device *pdev)
+{
+ return sdhci_pltfm_unregister(pdev);
+}
+
+static struct platform_driver sdhci_cns3xxx_driver = {
+ .driver = {
+ .name = "sdhci-cns3xxx",
+ .owner = THIS_MODULE,
+ },
+ .probe = sdhci_cns3xxx_probe,
+ .remove = __devexit_p(sdhci_cns3xxx_remove),
+#ifdef CONFIG_PM
+ .suspend = sdhci_pltfm_suspend,
+ .resume = sdhci_pltfm_resume,
+#endif
+};
+
+static int __init sdhci_cns3xxx_init(void)
+{
+ return platform_driver_register(&sdhci_cns3xxx_driver);
+}
+module_init(sdhci_cns3xxx_init);
+
+static void __exit sdhci_cns3xxx_exit(void)
+{
+ platform_driver_unregister(&sdhci_cns3xxx_driver);
+}
+module_exit(sdhci_cns3xxx_exit);
+
+MODULE_DESCRIPTION("SDHCI driver for CNS3xxx");
+MODULE_AUTHOR("Scott Shu, "
+ "Anton Vorontsov <avorontsov@mvista.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c
index 2aeef4ffed8c..f2d29dca4420 100644
--- a/drivers/mmc/host/sdhci-dove.c
+++ b/drivers/mmc/host/sdhci-dove.c
@@ -22,7 +22,6 @@
#include <linux/io.h>
#include <linux/mmc/host.h>
-#include "sdhci.h"
#include "sdhci-pltfm.h"
static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
@@ -61,10 +60,50 @@ static struct sdhci_ops sdhci_dove_ops = {
.read_l = sdhci_dove_readl,
};
-struct sdhci_pltfm_data sdhci_dove_pdata = {
+static struct sdhci_pltfm_data sdhci_dove_pdata = {
.ops = &sdhci_dove_ops,
.quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
SDHCI_QUIRK_NO_BUSY_IRQ |
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_FORCE_DMA,
};
+
+static int __devinit sdhci_dove_probe(struct platform_device *pdev)
+{
+ return sdhci_pltfm_register(pdev, &sdhci_dove_pdata);
+}
+
+static int __devexit sdhci_dove_remove(struct platform_device *pdev)
+{
+ return sdhci_pltfm_unregister(pdev);
+}
+
+static struct platform_driver sdhci_dove_driver = {
+ .driver = {
+ .name = "sdhci-dove",
+ .owner = THIS_MODULE,
+ },
+ .probe = sdhci_dove_probe,
+ .remove = __devexit_p(sdhci_dove_remove),
+#ifdef CONFIG_PM
+ .suspend = sdhci_pltfm_suspend,
+ .resume = sdhci_pltfm_resume,
+#endif
+};
+
+static int __init sdhci_dove_init(void)
+{
+ return platform_driver_register(&sdhci_dove_driver);
+}
+module_init(sdhci_dove_init);
+
+static void __exit sdhci_dove_exit(void)
+{
+ platform_driver_unregister(&sdhci_dove_driver);
+}
+module_exit(sdhci_dove_exit);
+
+MODULE_DESCRIPTION("SDHCI driver for Dove");
+MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>, "
+ "Mike Rapoport <mike@compulab.co.il>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index a19967d0bfc4..4b976f00ea85 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -16,22 +16,33 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/gpio.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mmc/host.h>
-#include <linux/mmc/sdhci-pltfm.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
-#include <mach/hardware.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <mach/esdhc.h>
-#include "sdhci.h"
#include "sdhci-pltfm.h"
#include "sdhci-esdhc.h"
+#define SDHCI_CTRL_D3CD 0x08
/* VENDOR SPEC register */
#define SDHCI_VENDOR_SPEC 0xC0
#define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002
+#define SDHCI_WTMK_LVL 0x44
+#define SDHCI_MIX_CTRL 0x48
+
+/*
+ * There is an INT DMA ERR mis-match between eSDHC and STD SDHC SPEC:
+ * Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
+ * but bit28 is used as the INT DMA ERR in fsl eSDHC design.
+ * Define this macro DMA error INT for fsl eSDHC
+ */
+#define SDHCI_INT_VENDOR_SPEC_DMA_ERR 0x10000000
-#define ESDHC_FLAG_GPIO_FOR_CD_WP (1 << 0)
/*
* The CMDTYPE of the CMD register (offset 0xE) should be set to
* "11" when the STOP CMD12 is issued on imx53 to abort one
@@ -45,10 +56,77 @@
*/
#define ESDHC_FLAG_MULTIBLK_NO_INT (1 << 1)
+enum imx_esdhc_type {
+ IMX25_ESDHC,
+ IMX35_ESDHC,
+ IMX51_ESDHC,
+ IMX53_ESDHC,
+ IMX6Q_USDHC,
+};
+
struct pltfm_imx_data {
int flags;
u32 scratchpad;
+ enum imx_esdhc_type devtype;
+ struct esdhc_platform_data boarddata;
+};
+
+static struct platform_device_id imx_esdhc_devtype[] = {
+ {
+ .name = "sdhci-esdhc-imx25",
+ .driver_data = IMX25_ESDHC,
+ }, {
+ .name = "sdhci-esdhc-imx35",
+ .driver_data = IMX35_ESDHC,
+ }, {
+ .name = "sdhci-esdhc-imx51",
+ .driver_data = IMX51_ESDHC,
+ }, {
+ .name = "sdhci-esdhc-imx53",
+ .driver_data = IMX53_ESDHC,
+ }, {
+ .name = "sdhci-usdhc-imx6q",
+ .driver_data = IMX6Q_USDHC,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);
+
+static const struct of_device_id imx_esdhc_dt_ids[] = {
+ { .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], },
+ { .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], },
+ { .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], },
+ { .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], },
+ { .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
+
+static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
+{
+ return data->devtype == IMX25_ESDHC;
+}
+
+static inline int is_imx35_esdhc(struct pltfm_imx_data *data)
+{
+ return data->devtype == IMX35_ESDHC;
+}
+
+static inline int is_imx51_esdhc(struct pltfm_imx_data *data)
+{
+ return data->devtype == IMX51_ESDHC;
+}
+
+static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
+{
+ return data->devtype == IMX53_ESDHC;
+}
+
+static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
+{
+ return data->devtype == IMX6Q_USDHC;
+}
static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
{
@@ -62,24 +140,42 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
+ struct esdhc_platform_data *boarddata = &imx_data->boarddata;
- /* fake CARD_PRESENT flag on mx25/35 */
+ /* fake CARD_PRESENT flag */
u32 val = readl(host->ioaddr + reg);
if (unlikely((reg == SDHCI_PRESENT_STATE)
- && (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP))) {
- struct esdhc_platform_data *boarddata =
- host->mmc->parent->platform_data;
-
- if (boarddata && gpio_is_valid(boarddata->cd_gpio)
- && gpio_get_value(boarddata->cd_gpio))
+ && gpio_is_valid(boarddata->cd_gpio))) {
+ if (gpio_get_value(boarddata->cd_gpio))
/* no card, if a valid gpio says so... */
- val &= SDHCI_CARD_PRESENT;
+ val &= ~SDHCI_CARD_PRESENT;
else
/* ... in all other cases assume card is present */
val |= SDHCI_CARD_PRESENT;
}
+ if (unlikely(reg == SDHCI_CAPABILITIES)) {
+ /* In FSL esdhc IC module, only bit20 is used to indicate the
+ * ADMA2 capability of esdhc, but this bit is messed up on
+ * some SOCs (e.g. on MX25, MX35 this bit is set, but they
+ * don't actually support ADMA2). So set the BROKEN_ADMA
+ * uirk on MX25/35 platforms.
+ */
+
+ if (val & SDHCI_CAN_DO_ADMA1) {
+ val &= ~SDHCI_CAN_DO_ADMA1;
+ val |= SDHCI_CAN_DO_ADMA2;
+ }
+ }
+
+ if (unlikely(reg == SDHCI_INT_STATUS)) {
+ if (val & SDHCI_INT_VENDOR_SPEC_DMA_ERR) {
+ val &= ~SDHCI_INT_VENDOR_SPEC_DMA_ERR;
+ val |= SDHCI_INT_ADMA_ERROR;
+ }
+ }
+
return val;
}
@@ -87,14 +183,33 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
-
- if (unlikely((reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)
- && (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP)))
- /*
- * these interrupts won't work with a custom card_detect gpio
- * (only applied to mx25/35)
- */
- val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
+ struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+ u32 data;
+
+ if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
+ if (boarddata->cd_type == ESDHC_CD_GPIO)
+ /*
+ * These interrupts won't work with a custom
+ * card_detect gpio (only applied to mx25/35)
+ */
+ val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
+
+ if (val & SDHCI_INT_CARD_INT) {
+ /*
+ * Clear and then set D3CD bit to avoid missing the
+ * card interrupt. This is a eSDHC controller problem
+ * so we need to apply the following workaround: clear
+ * and set D3CD bit will make eSDHC re-sample the card
+ * interrupt. In case a card interrupt was lost,
+ * re-sample it by the following steps.
+ */
+ data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
+ data &= ~SDHCI_CTRL_D3CD;
+ writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
+ data |= SDHCI_CTRL_D3CD;
+ writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
+ }
+ }
if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
&& (reg == SDHCI_INT_STATUS)
@@ -105,13 +220,28 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
}
+ if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
+ if (val & SDHCI_INT_ADMA_ERROR) {
+ val &= ~SDHCI_INT_ADMA_ERROR;
+ val |= SDHCI_INT_VENDOR_SPEC_DMA_ERR;
+ }
+ }
+
writel(val, host->ioaddr + reg);
}
static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
{
- if (unlikely(reg == SDHCI_HOST_VERSION))
- reg ^= 2;
+ if (unlikely(reg == SDHCI_HOST_VERSION)) {
+ u16 val = readw(host->ioaddr + (reg ^ 2));
+ /*
+ * uSDHC supports SDHCI v3.0, but it's encoded as value
+ * 0x3 in host controller version register, which violates
+ * SDHCI_SPEC_300 definition. Work it around here.
+ */
+ if ((val & SDHCI_SPEC_VER_MASK) == 3)
+ return --val;
+ }
return readw(host->ioaddr + reg);
}
@@ -142,8 +272,17 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
if ((host->cmd->opcode == MMC_STOP_TRANSMISSION)
&& (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
val |= SDHCI_CMD_ABORTCMD;
- writel(val << 16 | imx_data->scratchpad,
- host->ioaddr + SDHCI_TRANSFER_MODE);
+
+ if (is_imx6q_usdhc(imx_data)) {
+ u32 m = readl(host->ioaddr + SDHCI_MIX_CTRL);
+ m = imx_data->scratchpad | (m & 0xffff0000);
+ writel(m, host->ioaddr + SDHCI_MIX_CTRL);
+ writel(val << 16,
+ host->ioaddr + SDHCI_TRANSFER_MODE);
+ } else {
+ writel(val << 16 | imx_data->scratchpad,
+ host->ioaddr + SDHCI_TRANSFER_MODE);
+ }
return;
case SDHCI_BLOCK_SIZE:
val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
@@ -164,8 +303,10 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
*/
return;
case SDHCI_HOST_CONTROL:
- /* FSL messed up here, so we can just keep those two */
- new_val = val & (SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS);
+ /* FSL messed up here, so we can just keep those three */
+ new_val = val & (SDHCI_CTRL_LED | \
+ SDHCI_CTRL_4BITBUS | \
+ SDHCI_CTRL_D3CD);
/* ensure the endianess */
new_val |= ESDHC_HOST_CONTROL_LE;
/* DMA mode bits are shifted */
@@ -175,6 +316,17 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
return;
}
esdhc_clrset_le(host, 0xff, val, reg);
+
+ /*
+ * The esdhc has a design violation to SDHC spec which tells
+ * that software reset should not affect card detection circuit.
+ * But esdhc clears its SYSCTL register bits [0..2] during the
+ * software reset. This will stop those clocks that card detection
+ * circuit relies on. To work around it, we turn the clocks on back
+ * to keep card detection circuit functional.
+ */
+ if ((reg == SDHCI_SOFTWARE_RESET) && (val & 1))
+ esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
}
static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
@@ -193,12 +345,22 @@ static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
{
- struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = pltfm_host->priv;
+ struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+
+ switch (boarddata->wp_type) {
+ case ESDHC_WP_GPIO:
+ if (gpio_is_valid(boarddata->wp_gpio))
+ return gpio_get_value(boarddata->wp_gpio);
+ case ESDHC_WP_CONTROLLER:
+ return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+ SDHCI_WRITE_PROTECT);
+ case ESDHC_WP_NONE:
+ break;
+ }
- if (boarddata && gpio_is_valid(boarddata->wp_gpio))
- return gpio_get_value(boarddata->wp_gpio);
- else
- return -ENOSYS;
+ return -ENOSYS;
}
static struct sdhci_ops sdhci_esdhc_ops = {
@@ -210,6 +372,15 @@ static struct sdhci_ops sdhci_esdhc_ops = {
.set_clock = esdhc_set_clock,
.get_max_clock = esdhc_pltfm_get_max_clock,
.get_min_clock = esdhc_pltfm_get_min_clock,
+ .get_ro = esdhc_pltfm_get_ro,
+};
+
+static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
+ .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
+ | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
+ | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
+ | SDHCI_QUIRK_BROKEN_CARD_DETECTION,
+ .ops = &sdhci_esdhc_ops,
};
static irqreturn_t cd_irq(int irq, void *data)
@@ -220,112 +391,236 @@ static irqreturn_t cd_irq(int irq, void *data)
return IRQ_HANDLED;
};
-static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata)
+#ifdef CONFIG_OF
+static int __devinit
+sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
+ struct esdhc_platform_data *boarddata)
{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
+ struct device_node *np = pdev->dev.of_node;
+
+ if (!np)
+ return -ENODEV;
+
+ if (of_get_property(np, "fsl,card-wired", NULL))
+ boarddata->cd_type = ESDHC_CD_PERMANENT;
+
+ if (of_get_property(np, "fsl,cd-controller", NULL))
+ boarddata->cd_type = ESDHC_CD_CONTROLLER;
+
+ if (of_get_property(np, "fsl,wp-controller", NULL))
+ boarddata->wp_type = ESDHC_WP_CONTROLLER;
+
+ boarddata->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
+ if (gpio_is_valid(boarddata->cd_gpio))
+ boarddata->cd_type = ESDHC_CD_GPIO;
+
+ boarddata->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
+ if (gpio_is_valid(boarddata->wp_gpio))
+ boarddata->wp_type = ESDHC_WP_GPIO;
+
+ return 0;
+}
+#else
+static inline int
+sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
+ struct esdhc_platform_data *boarddata)
+{
+ return -ENODEV;
+}
+#endif
+
+static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(imx_esdhc_dt_ids, &pdev->dev);
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_host *host;
+ struct esdhc_platform_data *boarddata;
struct clk *clk;
int err;
struct pltfm_imx_data *imx_data;
+ host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+
+ imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);
+ if (!imx_data) {
+ err = -ENOMEM;
+ goto err_imx_data;
+ }
+
+ if (of_id)
+ pdev->id_entry = of_id->data;
+ imx_data->devtype = pdev->id_entry->driver_data;
+ pltfm_host->priv = imx_data;
+
clk = clk_get(mmc_dev(host->mmc), NULL);
if (IS_ERR(clk)) {
dev_err(mmc_dev(host->mmc), "clk err\n");
- return PTR_ERR(clk);
+ err = PTR_ERR(clk);
+ goto err_clk_get;
}
clk_enable(clk);
pltfm_host->clk = clk;
- imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);
- if (!imx_data) {
- clk_disable(pltfm_host->clk);
- clk_put(pltfm_host->clk);
- return -ENOMEM;
- }
- pltfm_host->priv = imx_data;
-
- if (!cpu_is_mx25())
+ if (!is_imx25_esdhc(imx_data))
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
- if (cpu_is_mx25() || cpu_is_mx35()) {
+ if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data))
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
- host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK;
- /* write_protect can't be routed to controller, use gpio */
- sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro;
- }
+ host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
+ | SDHCI_QUIRK_BROKEN_ADMA;
- if (!(cpu_is_mx25() || cpu_is_mx35() || cpu_is_mx51()))
+ if (is_imx53_esdhc(imx_data))
imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
- if (boarddata) {
+ /*
+ * The imx6q ROM code will change the default watermark level setting
+ * to something insane. Change it back here.
+ */
+ if (is_imx6q_usdhc(imx_data))
+ writel(0x08100810, host->ioaddr + SDHCI_WTMK_LVL);
+
+ boarddata = &imx_data->boarddata;
+ if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
+ if (!host->mmc->parent->platform_data) {
+ dev_err(mmc_dev(host->mmc), "no board data!\n");
+ err = -EINVAL;
+ goto no_board_data;
+ }
+ imx_data->boarddata = *((struct esdhc_platform_data *)
+ host->mmc->parent->platform_data);
+ }
+
+ /* write_protect */
+ if (boarddata->wp_type == ESDHC_WP_GPIO) {
err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP");
if (err) {
dev_warn(mmc_dev(host->mmc),
- "no write-protect pin available!\n");
- boarddata->wp_gpio = err;
+ "no write-protect pin available!\n");
+ boarddata->wp_gpio = -EINVAL;
}
+ } else {
+ boarddata->wp_gpio = -EINVAL;
+ }
+ /* card_detect */
+ if (boarddata->cd_type != ESDHC_CD_GPIO)
+ boarddata->cd_gpio = -EINVAL;
+
+ switch (boarddata->cd_type) {
+ case ESDHC_CD_GPIO:
err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD");
if (err) {
- dev_warn(mmc_dev(host->mmc),
+ dev_err(mmc_dev(host->mmc),
"no card-detect pin available!\n");
goto no_card_detect_pin;
}
- /* i.MX5x has issues to be researched */
- if (!cpu_is_mx25() && !cpu_is_mx35())
- goto not_supported;
-
err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
mmc_hostname(host->mmc), host);
if (err) {
- dev_warn(mmc_dev(host->mmc), "request irq error\n");
+ dev_err(mmc_dev(host->mmc), "request irq error\n");
goto no_card_detect_irq;
}
+ /* fall through */
- imx_data->flags |= ESDHC_FLAG_GPIO_FOR_CD_WP;
- /* Now we have a working card_detect again */
+ case ESDHC_CD_CONTROLLER:
+ /* we have a working card_detect back */
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ break;
+
+ case ESDHC_CD_PERMANENT:
+ host->mmc->caps = MMC_CAP_NONREMOVABLE;
+ break;
+
+ case ESDHC_CD_NONE:
+ break;
}
+ err = sdhci_add_host(host);
+ if (err)
+ goto err_add_host;
+
return 0;
- no_card_detect_irq:
- gpio_free(boarddata->cd_gpio);
- no_card_detect_pin:
- boarddata->cd_gpio = err;
- not_supported:
+err_add_host:
+ if (gpio_is_valid(boarddata->cd_gpio))
+ free_irq(gpio_to_irq(boarddata->cd_gpio), host);
+no_card_detect_irq:
+ if (gpio_is_valid(boarddata->cd_gpio))
+ gpio_free(boarddata->cd_gpio);
+ if (gpio_is_valid(boarddata->wp_gpio))
+ gpio_free(boarddata->wp_gpio);
+no_card_detect_pin:
+no_board_data:
+ clk_disable(pltfm_host->clk);
+ clk_put(pltfm_host->clk);
+err_clk_get:
kfree(imx_data);
- return 0;
+err_imx_data:
+ sdhci_pltfm_free(pdev);
+ return err;
}
-static void esdhc_pltfm_exit(struct sdhci_host *host)
+static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev)
{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
struct pltfm_imx_data *imx_data = pltfm_host->priv;
+ struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+ int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
- if (boarddata && gpio_is_valid(boarddata->wp_gpio))
+ sdhci_remove_host(host, dead);
+
+ if (gpio_is_valid(boarddata->wp_gpio))
gpio_free(boarddata->wp_gpio);
- if (boarddata && gpio_is_valid(boarddata->cd_gpio)) {
+ if (gpio_is_valid(boarddata->cd_gpio)) {
+ free_irq(gpio_to_irq(boarddata->cd_gpio), host);
gpio_free(boarddata->cd_gpio);
-
- if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION))
- free_irq(gpio_to_irq(boarddata->cd_gpio), host);
}
clk_disable(pltfm_host->clk);
clk_put(pltfm_host->clk);
kfree(imx_data);
+
+ sdhci_pltfm_free(pdev);
+
+ return 0;
}
-struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
- .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA
- | SDHCI_QUIRK_BROKEN_CARD_DETECTION,
- /* ADMA has issues. Might be fixable */
- .ops = &sdhci_esdhc_ops,
- .init = esdhc_pltfm_init,
- .exit = esdhc_pltfm_exit,
+static struct platform_driver sdhci_esdhc_imx_driver = {
+ .driver = {
+ .name = "sdhci-esdhc-imx",
+ .owner = THIS_MODULE,
+ .of_match_table = imx_esdhc_dt_ids,
+ },
+ .id_table = imx_esdhc_devtype,
+ .probe = sdhci_esdhc_imx_probe,
+ .remove = __devexit_p(sdhci_esdhc_imx_remove),
+#ifdef CONFIG_PM
+ .suspend = sdhci_pltfm_suspend,
+ .resume = sdhci_pltfm_resume,
+#endif
};
+
+static int __init sdhci_esdhc_imx_init(void)
+{
+ return platform_driver_register(&sdhci_esdhc_imx_driver);
+}
+module_init(sdhci_esdhc_imx_init);
+
+static void __exit sdhci_esdhc_imx_exit(void)
+{
+ platform_driver_unregister(&sdhci_esdhc_imx_driver);
+}
+module_exit(sdhci_esdhc_imx_exit);
+
+MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
+MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-of-core.c b/drivers/mmc/host/sdhci-of-core.c
deleted file mode 100644
index 60e4186a4345..000000000000
--- a/drivers/mmc/host/sdhci-of-core.c
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * OpenFirmware bindings for Secure Digital Host Controller Interface.
- *
- * Copyright (c) 2007 Freescale Semiconductor, Inc.
- * Copyright (c) 2009 MontaVista Software, Inc.
- *
- * Authors: Xiaobo Xie <X.Xie@freescale.com>
- * Anton Vorontsov <avorontsov@ru.mvista.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/err.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/mmc/host.h>
-#ifdef CONFIG_PPC
-#include <asm/machdep.h>
-#endif
-#include "sdhci-of.h"
-#include "sdhci.h"
-
-#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
-
-/*
- * These accessors are designed for big endian hosts doing I/O to
- * little endian controllers incorporating a 32-bit hardware byte swapper.
- */
-
-u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg)
-{
- return in_be32(host->ioaddr + reg);
-}
-
-u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg)
-{
- return in_be16(host->ioaddr + (reg ^ 0x2));
-}
-
-u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg)
-{
- return in_8(host->ioaddr + (reg ^ 0x3));
-}
-
-void sdhci_be32bs_writel(struct sdhci_host *host, u32 val, int reg)
-{
- out_be32(host->ioaddr + reg, val);
-}
-
-void sdhci_be32bs_writew(struct sdhci_host *host, u16 val, int reg)
-{
- struct sdhci_of_host *of_host = sdhci_priv(host);
- int base = reg & ~0x3;
- int shift = (reg & 0x2) * 8;
-
- switch (reg) {
- case SDHCI_TRANSFER_MODE:
- /*
- * Postpone this write, we must do it together with a
- * command write that is down below.
- */
- of_host->xfer_mode_shadow = val;
- return;
- case SDHCI_COMMAND:
- sdhci_be32bs_writel(host, val << 16 | of_host->xfer_mode_shadow,
- SDHCI_TRANSFER_MODE);
- return;
- }
- clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
-}
-
-void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
-{
- int base = reg & ~0x3;
- int shift = (reg & 0x3) * 8;
-
- clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
-}
-#endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */
-
-#ifdef CONFIG_PM
-
-static int sdhci_of_suspend(struct platform_device *ofdev, pm_message_t state)
-{
- struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
-
- return mmc_suspend_host(host->mmc);
-}
-
-static int sdhci_of_resume(struct platform_device *ofdev)
-{
- struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
-
- return mmc_resume_host(host->mmc);
-}
-
-#else
-
-#define sdhci_of_suspend NULL
-#define sdhci_of_resume NULL
-
-#endif
-
-static bool __devinit sdhci_of_wp_inverted(struct device_node *np)
-{
- if (of_get_property(np, "sdhci,wp-inverted", NULL))
- return true;
-
- /* Old device trees don't have the wp-inverted property. */
-#ifdef CONFIG_PPC
- return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds);
-#else
- return false;
-#endif
-}
-
-static const struct of_device_id sdhci_of_match[];
-static int __devinit sdhci_of_probe(struct platform_device *ofdev)
-{
- const struct of_device_id *match;
- struct device_node *np = ofdev->dev.of_node;
- struct sdhci_of_data *sdhci_of_data;
- struct sdhci_host *host;
- struct sdhci_of_host *of_host;
- const __be32 *clk;
- int size;
- int ret;
-
- match = of_match_device(sdhci_of_match, &ofdev->dev);
- if (!match)
- return -EINVAL;
- sdhci_of_data = match->data;
-
- if (!of_device_is_available(np))
- return -ENODEV;
-
- host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host));
- if (IS_ERR(host))
- return -ENOMEM;
-
- of_host = sdhci_priv(host);
- dev_set_drvdata(&ofdev->dev, host);
-
- host->ioaddr = of_iomap(np, 0);
- if (!host->ioaddr) {
- ret = -ENOMEM;
- goto err_addr_map;
- }
-
- host->irq = irq_of_parse_and_map(np, 0);
- if (!host->irq) {
- ret = -EINVAL;
- goto err_no_irq;
- }
-
- host->hw_name = dev_name(&ofdev->dev);
- if (sdhci_of_data) {
- host->quirks = sdhci_of_data->quirks;
- host->ops = &sdhci_of_data->ops;
- }
-
- if (of_get_property(np, "sdhci,auto-cmd12", NULL))
- host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
-
-
- if (of_get_property(np, "sdhci,1-bit-only", NULL))
- host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
-
- if (sdhci_of_wp_inverted(np))
- host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
-
- clk = of_get_property(np, "clock-frequency", &size);
- if (clk && size == sizeof(*clk) && *clk)
- of_host->clock = be32_to_cpup(clk);
-
- ret = sdhci_add_host(host);
- if (ret)
- goto err_add_host;
-
- return 0;
-
-err_add_host:
- irq_dispose_mapping(host->irq);
-err_no_irq:
- iounmap(host->ioaddr);
-err_addr_map:
- sdhci_free_host(host);
- return ret;
-}
-
-static int __devexit sdhci_of_remove(struct platform_device *ofdev)
-{
- struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
-
- sdhci_remove_host(host, 0);
- sdhci_free_host(host);
- irq_dispose_mapping(host->irq);
- iounmap(host->ioaddr);
- return 0;
-}
-
-static const struct of_device_id sdhci_of_match[] = {
-#ifdef CONFIG_MMC_SDHCI_OF_ESDHC
- { .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, },
- { .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, },
- { .compatible = "fsl,esdhc", .data = &sdhci_esdhc, },
-#endif
-#ifdef CONFIG_MMC_SDHCI_OF_HLWD
- { .compatible = "nintendo,hollywood-sdhci", .data = &sdhci_hlwd, },
-#endif
- { .compatible = "generic-sdhci", },
- {},
-};
-MODULE_DEVICE_TABLE(of, sdhci_of_match);
-
-static struct platform_driver sdhci_of_driver = {
- .driver = {
- .name = "sdhci-of",
- .owner = THIS_MODULE,
- .of_match_table = sdhci_of_match,
- },
- .probe = sdhci_of_probe,
- .remove = __devexit_p(sdhci_of_remove),
- .suspend = sdhci_of_suspend,
- .resume = sdhci_of_resume,
-};
-
-static int __init sdhci_of_init(void)
-{
- return platform_driver_register(&sdhci_of_driver);
-}
-module_init(sdhci_of_init);
-
-static void __exit sdhci_of_exit(void)
-{
- platform_driver_unregister(&sdhci_of_driver);
-}
-module_exit(sdhci_of_exit);
-
-MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver");
-MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
- "Anton Vorontsov <avorontsov@ru.mvista.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index ba40d6d035c7..59e9d003e589 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -1,7 +1,7 @@
/*
* Freescale eSDHC controller driver.
*
- * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2007, 2010 Freescale Semiconductor, Inc.
* Copyright (c) 2009 MontaVista Software, Inc.
*
* Authors: Xiaobo Xie <X.Xie@freescale.com>
@@ -15,19 +15,29 @@
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/mmc/host.h>
-#include "sdhci-of.h"
-#include "sdhci.h"
+#include "sdhci-pltfm.h"
#include "sdhci-esdhc.h"
static u16 esdhc_readw(struct sdhci_host *host, int reg)
{
u16 ret;
+ int base = reg & ~0x3;
+ int shift = (reg & 0x2) * 8;
if (unlikely(reg == SDHCI_HOST_VERSION))
- ret = in_be16(host->ioaddr + reg);
+ ret = in_be32(host->ioaddr + base) & 0xffff;
else
- ret = sdhci_be32bs_readw(host, reg);
+ ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff;
+ return ret;
+}
+
+static u8 esdhc_readb(struct sdhci_host *host, int reg)
+{
+ int base = reg & ~0x3;
+ int shift = (reg & 0x3) * 8;
+ u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff;
return ret;
}
@@ -60,32 +70,83 @@ static int esdhc_of_enable_dma(struct sdhci_host *host)
static unsigned int esdhc_of_get_max_clock(struct sdhci_host *host)
{
- struct sdhci_of_host *of_host = sdhci_priv(host);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- return of_host->clock;
+ return pltfm_host->clock;
}
static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
{
- struct sdhci_of_host *of_host = sdhci_priv(host);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- return of_host->clock / 256 / 16;
+ return pltfm_host->clock / 256 / 16;
}
-struct sdhci_of_data sdhci_esdhc = {
+static struct sdhci_ops sdhci_esdhc_ops = {
+ .read_l = sdhci_be32bs_readl,
+ .read_w = esdhc_readw,
+ .read_b = esdhc_readb,
+ .write_l = sdhci_be32bs_writel,
+ .write_w = esdhc_writew,
+ .write_b = esdhc_writeb,
+ .set_clock = esdhc_set_clock,
+ .enable_dma = esdhc_of_enable_dma,
+ .get_max_clock = esdhc_of_get_max_clock,
+ .get_min_clock = esdhc_of_get_min_clock,
+};
+
+static struct sdhci_pltfm_data sdhci_esdhc_pdata = {
/* card detection could be handled via GPIO */
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
| SDHCI_QUIRK_NO_CARD_NO_RESET,
- .ops = {
- .read_l = sdhci_be32bs_readl,
- .read_w = esdhc_readw,
- .read_b = sdhci_be32bs_readb,
- .write_l = sdhci_be32bs_writel,
- .write_w = esdhc_writew,
- .write_b = esdhc_writeb,
- .set_clock = esdhc_set_clock,
- .enable_dma = esdhc_of_enable_dma,
- .get_max_clock = esdhc_of_get_max_clock,
- .get_min_clock = esdhc_of_get_min_clock,
+ .ops = &sdhci_esdhc_ops,
+};
+
+static int __devinit sdhci_esdhc_probe(struct platform_device *pdev)
+{
+ return sdhci_pltfm_register(pdev, &sdhci_esdhc_pdata);
+}
+
+static int __devexit sdhci_esdhc_remove(struct platform_device *pdev)
+{
+ return sdhci_pltfm_unregister(pdev);
+}
+
+static const struct of_device_id sdhci_esdhc_of_match[] = {
+ { .compatible = "fsl,mpc8379-esdhc" },
+ { .compatible = "fsl,mpc8536-esdhc" },
+ { .compatible = "fsl,esdhc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match);
+
+static struct platform_driver sdhci_esdhc_driver = {
+ .driver = {
+ .name = "sdhci-esdhc",
+ .owner = THIS_MODULE,
+ .of_match_table = sdhci_esdhc_of_match,
},
+ .probe = sdhci_esdhc_probe,
+ .remove = __devexit_p(sdhci_esdhc_remove),
+#ifdef CONFIG_PM
+ .suspend = sdhci_pltfm_suspend,
+ .resume = sdhci_pltfm_resume,
+#endif
};
+
+static int __init sdhci_esdhc_init(void)
+{
+ return platform_driver_register(&sdhci_esdhc_driver);
+}
+module_init(sdhci_esdhc_init);
+
+static void __exit sdhci_esdhc_exit(void)
+{
+ platform_driver_unregister(&sdhci_esdhc_driver);
+}
+module_exit(sdhci_esdhc_exit);
+
+MODULE_DESCRIPTION("SDHCI OF driver for Freescale MPC eSDHC");
+MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
+ "Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c
index 68ddb7546ae2..9b0d794a4f69 100644
--- a/drivers/mmc/host/sdhci-of-hlwd.c
+++ b/drivers/mmc/host/sdhci-of-hlwd.c
@@ -20,9 +20,9 @@
*/
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/mmc/host.h>
-#include "sdhci-of.h"
-#include "sdhci.h"
+#include "sdhci-pltfm.h"
/*
* Ops and quirks for the Nintendo Wii SDHCI controllers.
@@ -51,15 +51,63 @@ static void sdhci_hlwd_writeb(struct sdhci_host *host, u8 val, int reg)
udelay(SDHCI_HLWD_WRITE_DELAY);
}
-struct sdhci_of_data sdhci_hlwd = {
+static struct sdhci_ops sdhci_hlwd_ops = {
+ .read_l = sdhci_be32bs_readl,
+ .read_w = sdhci_be32bs_readw,
+ .read_b = sdhci_be32bs_readb,
+ .write_l = sdhci_hlwd_writel,
+ .write_w = sdhci_hlwd_writew,
+ .write_b = sdhci_hlwd_writeb,
+};
+
+static struct sdhci_pltfm_data sdhci_hlwd_pdata = {
.quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE,
- .ops = {
- .read_l = sdhci_be32bs_readl,
- .read_w = sdhci_be32bs_readw,
- .read_b = sdhci_be32bs_readb,
- .write_l = sdhci_hlwd_writel,
- .write_w = sdhci_hlwd_writew,
- .write_b = sdhci_hlwd_writeb,
+ .ops = &sdhci_hlwd_ops,
+};
+
+static int __devinit sdhci_hlwd_probe(struct platform_device *pdev)
+{
+ return sdhci_pltfm_register(pdev, &sdhci_hlwd_pdata);
+}
+
+static int __devexit sdhci_hlwd_remove(struct platform_device *pdev)
+{
+ return sdhci_pltfm_unregister(pdev);
+}
+
+static const struct of_device_id sdhci_hlwd_of_match[] = {
+ { .compatible = "nintendo,hollywood-sdhci" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sdhci_hlwd_of_match);
+
+static struct platform_driver sdhci_hlwd_driver = {
+ .driver = {
+ .name = "sdhci-hlwd",
+ .owner = THIS_MODULE,
+ .of_match_table = sdhci_hlwd_of_match,
},
+ .probe = sdhci_hlwd_probe,
+ .remove = __devexit_p(sdhci_hlwd_remove),
+#ifdef CONFIG_PM
+ .suspend = sdhci_pltfm_suspend,
+ .resume = sdhci_pltfm_resume,
+#endif
};
+
+static int __init sdhci_hlwd_init(void)
+{
+ return platform_driver_register(&sdhci_hlwd_driver);
+}
+module_init(sdhci_hlwd_init);
+
+static void __exit sdhci_hlwd_exit(void)
+{
+ platform_driver_unregister(&sdhci_hlwd_driver);
+}
+module_exit(sdhci_hlwd_exit);
+
+MODULE_DESCRIPTION("Nintendo Wii SDHCI OF driver");
+MODULE_AUTHOR("The GameCube Linux Team, Albert Herranz");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-of.h b/drivers/mmc/host/sdhci-of.h
deleted file mode 100644
index ad09ad9915d8..000000000000
--- a/drivers/mmc/host/sdhci-of.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * OpenFirmware bindings for Secure Digital Host Controller Interface.
- *
- * Copyright (c) 2007 Freescale Semiconductor, Inc.
- * Copyright (c) 2009 MontaVista Software, Inc.
- *
- * Authors: Xiaobo Xie <X.Xie@freescale.com>
- * Anton Vorontsov <avorontsov@ru.mvista.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.
- */
-
-#ifndef __SDHCI_OF_H
-#define __SDHCI_OF_H
-
-#include <linux/types.h>
-#include "sdhci.h"
-
-struct sdhci_of_data {
- unsigned int quirks;
- struct sdhci_ops ops;
-};
-
-struct sdhci_of_host {
- unsigned int clock;
- u16 xfer_mode_shadow;
-};
-
-extern u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg);
-extern u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg);
-extern u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg);
-extern void sdhci_be32bs_writel(struct sdhci_host *host, u32 val, int reg);
-extern void sdhci_be32bs_writew(struct sdhci_host *host, u16 val, int reg);
-extern void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg);
-
-extern struct sdhci_of_data sdhci_esdhc;
-extern struct sdhci_of_data sdhci_hlwd;
-
-#endif /* __SDHCI_OF_H */
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 936bbca19c0a..d833d9c2f7e3 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -14,6 +14,7 @@
#include <linux/delay.h>
#include <linux/highmem.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
@@ -21,6 +22,9 @@
#include <linux/mmc/host.h>
#include <linux/scatterlist.h>
#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/sfi.h>
+#include <linux/pm_runtime.h>
#include "sdhci.h"
@@ -43,6 +47,7 @@ struct sdhci_pci_slot;
struct sdhci_pci_fixes {
unsigned int quirks;
+ bool allow_runtime_pm;
int (*probe) (struct sdhci_pci_chip *);
@@ -59,12 +64,16 @@ struct sdhci_pci_slot {
struct sdhci_host *host;
int pci_bar;
+ int rst_n_gpio;
+ int cd_gpio;
+ int cd_irq;
};
struct sdhci_pci_chip {
struct pci_dev *pdev;
unsigned int quirks;
+ bool allow_runtime_pm;
const struct sdhci_pci_fixes *fixes;
int num_slots; /* Slots on controller */
@@ -143,6 +152,12 @@ static const struct sdhci_pci_fixes sdhci_cafe = {
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
};
+static int mrst_hc_probe_slot(struct sdhci_pci_slot *slot)
+{
+ slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+ return 0;
+}
+
/*
* ADMA operation is disabled for Moorestown platform due to
* hardware bugs.
@@ -157,8 +172,132 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip)
return 0;
}
+/* Medfield eMMC hardware reset GPIOs */
+static int mfd_emmc0_rst_gpio = -EINVAL;
+static int mfd_emmc1_rst_gpio = -EINVAL;
+
+static int mfd_emmc_gpio_parse(struct sfi_table_header *table)
+{
+ struct sfi_table_simple *sb = (struct sfi_table_simple *)table;
+ struct sfi_gpio_table_entry *entry;
+ int i, num;
+
+ num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry);
+ entry = (struct sfi_gpio_table_entry *)sb->pentry;
+
+ for (i = 0; i < num; i++, entry++) {
+ if (!strncmp(entry->pin_name, "emmc0_rst", SFI_NAME_LEN))
+ mfd_emmc0_rst_gpio = entry->pin_no;
+ else if (!strncmp(entry->pin_name, "emmc1_rst", SFI_NAME_LEN))
+ mfd_emmc1_rst_gpio = entry->pin_no;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static irqreturn_t mfd_sd_cd(int irq, void *dev_id)
+{
+ struct sdhci_pci_slot *slot = dev_id;
+ struct sdhci_host *host = slot->host;
+
+ mmc_detect_change(host->mmc, msecs_to_jiffies(200));
+ return IRQ_HANDLED;
+}
+
+#define MFLD_SD_CD_PIN 69
+
+static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot)
+{
+ int err, irq, gpio = MFLD_SD_CD_PIN;
+
+ slot->cd_gpio = -EINVAL;
+ slot->cd_irq = -EINVAL;
+
+ err = gpio_request(gpio, "sd_cd");
+ if (err < 0)
+ goto out;
+
+ err = gpio_direction_input(gpio);
+ if (err < 0)
+ goto out_free;
+
+ irq = gpio_to_irq(gpio);
+ if (irq < 0)
+ goto out_free;
+
+ err = request_irq(irq, mfd_sd_cd, IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING, "sd_cd", slot);
+ if (err)
+ goto out_free;
+
+ slot->cd_gpio = gpio;
+ slot->cd_irq = irq;
+ slot->host->quirks2 |= SDHCI_QUIRK2_OWN_CARD_DETECTION;
+
+ return 0;
+
+out_free:
+ gpio_free(gpio);
+out:
+ dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n");
+ return 0;
+}
+
+static void mfd_sd_remove_slot(struct sdhci_pci_slot *slot, int dead)
+{
+ if (slot->cd_irq >= 0)
+ free_irq(slot->cd_irq, slot);
+ gpio_free(slot->cd_gpio);
+}
+
+#else
+
+#define mfd_sd_probe_slot NULL
+#define mfd_sd_remove_slot NULL
+
+#endif
+
+static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
+{
+ const char *name = NULL;
+ int gpio = -EINVAL;
+
+ sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, mfd_emmc_gpio_parse);
+
+ switch (slot->chip->pdev->device) {
+ case PCI_DEVICE_ID_INTEL_MFD_EMMC0:
+ gpio = mfd_emmc0_rst_gpio;
+ name = "eMMC0_reset";
+ break;
+ case PCI_DEVICE_ID_INTEL_MFD_EMMC1:
+ gpio = mfd_emmc1_rst_gpio;
+ name = "eMMC1_reset";
+ break;
+ }
+
+ if (!gpio_request(gpio, name)) {
+ gpio_direction_output(gpio, 1);
+ slot->rst_n_gpio = gpio;
+ slot->host->mmc->caps |= MMC_CAP_HW_RESET;
+ }
+
+ slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
+
+ slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC;
+
+ return 0;
+}
+
+static void mfd_emmc_remove_slot(struct sdhci_pci_slot *slot, int dead)
+{
+ gpio_free(slot->rst_n_gpio);
+}
+
static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
.quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT,
+ .probe_slot = mrst_hc_probe_slot,
};
static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = {
@@ -168,10 +307,21 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = {
static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+ .allow_runtime_pm = true,
+ .probe_slot = mfd_sd_probe_slot,
+ .remove_slot = mfd_sd_remove_slot,
};
-static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc_sdio = {
+static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+ .allow_runtime_pm = true,
+};
+
+static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = {
+ .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+ .allow_runtime_pm = true,
+ .probe_slot = mfd_emmc_probe_slot,
+ .remove_slot = mfd_emmc_remove_slot,
};
/* O2Micro extra registers */
@@ -682,7 +832,7 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.device = PCI_DEVICE_ID_INTEL_MFD_SDIO1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
- .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio,
},
{
@@ -690,7 +840,7 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.device = PCI_DEVICE_ID_INTEL_MFD_SDIO2,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
- .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio,
},
{
@@ -698,7 +848,7 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.device = PCI_DEVICE_ID_INTEL_MFD_EMMC0,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
- .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc,
},
{
@@ -706,7 +856,7 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.device = PCI_DEVICE_ID_INTEL_MFD_EMMC1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
- .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc,
},
{
@@ -789,8 +939,50 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)
return 0;
}
+static int sdhci_pci_8bit_width(struct sdhci_host *host, int width)
+{
+ u8 ctrl;
+
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+
+ switch (width) {
+ case MMC_BUS_WIDTH_8:
+ ctrl |= SDHCI_CTRL_8BITBUS;
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
+ break;
+ case MMC_BUS_WIDTH_4:
+ ctrl |= SDHCI_CTRL_4BITBUS;
+ ctrl &= ~SDHCI_CTRL_8BITBUS;
+ break;
+ default:
+ ctrl &= ~(SDHCI_CTRL_8BITBUS | SDHCI_CTRL_4BITBUS);
+ break;
+ }
+
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+
+ return 0;
+}
+
+static void sdhci_pci_hw_reset(struct sdhci_host *host)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ int rst_n_gpio = slot->rst_n_gpio;
+
+ if (!gpio_is_valid(rst_n_gpio))
+ return;
+ gpio_set_value_cansleep(rst_n_gpio, 0);
+ /* For eMMC, minimum is 1us but give it 10us for good measure */
+ udelay(10);
+ gpio_set_value_cansleep(rst_n_gpio, 1);
+ /* For eMMC, minimum is 200us but give it 300us for good measure */
+ usleep_range(300, 1000);
+}
+
static struct sdhci_ops sdhci_pci_ops = {
.enable_dma = sdhci_pci_enable_dma,
+ .platform_8bit_width = sdhci_pci_8bit_width,
+ .hw_reset = sdhci_pci_hw_reset,
};
/*****************************************************************************\
@@ -900,6 +1092,95 @@ static int sdhci_pci_resume(struct pci_dev *pdev)
#endif /* CONFIG_PM */
+#ifdef CONFIG_PM_RUNTIME
+
+static int sdhci_pci_runtime_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct sdhci_pci_chip *chip;
+ struct sdhci_pci_slot *slot;
+ pm_message_t state = { .event = PM_EVENT_SUSPEND };
+ int i, ret;
+
+ chip = pci_get_drvdata(pdev);
+ if (!chip)
+ return 0;
+
+ for (i = 0; i < chip->num_slots; i++) {
+ slot = chip->slots[i];
+ if (!slot)
+ continue;
+
+ ret = sdhci_runtime_suspend_host(slot->host);
+
+ if (ret) {
+ for (i--; i >= 0; i--)
+ sdhci_runtime_resume_host(chip->slots[i]->host);
+ return ret;
+ }
+ }
+
+ if (chip->fixes && chip->fixes->suspend) {
+ ret = chip->fixes->suspend(chip, state);
+ if (ret) {
+ for (i = chip->num_slots - 1; i >= 0; i--)
+ sdhci_runtime_resume_host(chip->slots[i]->host);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int sdhci_pci_runtime_resume(struct device *dev)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct sdhci_pci_chip *chip;
+ struct sdhci_pci_slot *slot;
+ int i, ret;
+
+ chip = pci_get_drvdata(pdev);
+ if (!chip)
+ return 0;
+
+ if (chip->fixes && chip->fixes->resume) {
+ ret = chip->fixes->resume(chip);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < chip->num_slots; i++) {
+ slot = chip->slots[i];
+ if (!slot)
+ continue;
+
+ ret = sdhci_runtime_resume_host(slot->host);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sdhci_pci_runtime_idle(struct device *dev)
+{
+ return 0;
+}
+
+#else
+
+#define sdhci_pci_runtime_suspend NULL
+#define sdhci_pci_runtime_resume NULL
+#define sdhci_pci_runtime_idle NULL
+
+#endif
+
+static const struct dev_pm_ops sdhci_pci_pm_ops = {
+ .runtime_suspend = sdhci_pci_runtime_suspend,
+ .runtime_resume = sdhci_pci_runtime_resume,
+ .runtime_idle = sdhci_pci_runtime_idle,
+};
+
/*****************************************************************************\
* *
* Device probing/removal *
@@ -944,6 +1225,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
slot->chip = chip;
slot->host = host;
slot->pci_bar = bar;
+ slot->rst_n_gpio = -EINVAL;
host->hw_name = "PCI";
host->ops = &sdhci_pci_ops;
@@ -1014,6 +1296,21 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
sdhci_free_host(slot->host);
}
+static void __devinit sdhci_pci_runtime_pm_allow(struct device *dev)
+{
+ pm_runtime_put_noidle(dev);
+ pm_runtime_allow(dev);
+ pm_runtime_set_autosuspend_delay(dev, 50);
+ pm_runtime_use_autosuspend(dev);
+ pm_suspend_ignore_children(dev, 1);
+}
+
+static void __devexit sdhci_pci_runtime_pm_forbid(struct device *dev)
+{
+ pm_runtime_forbid(dev);
+ pm_runtime_get_noresume(dev);
+}
+
static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -1063,8 +1360,10 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
chip->pdev = pdev;
chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
- if (chip->fixes)
+ if (chip->fixes) {
chip->quirks = chip->fixes->quirks;
+ chip->allow_runtime_pm = chip->fixes->allow_runtime_pm;
+ }
chip->num_slots = slots;
pci_set_drvdata(pdev, chip);
@@ -1089,6 +1388,9 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
chip->slots[i] = slot;
}
+ if (chip->allow_runtime_pm)
+ sdhci_pci_runtime_pm_allow(&pdev->dev);
+
return 0;
free:
@@ -1108,6 +1410,9 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
chip = pci_get_drvdata(pdev);
if (chip) {
+ if (chip->allow_runtime_pm)
+ sdhci_pci_runtime_pm_forbid(&pdev->dev);
+
for (i = 0; i < chip->num_slots; i++)
sdhci_pci_remove_slot(chip->slots[i]);
@@ -1125,6 +1430,9 @@ static struct pci_driver sdhci_driver = {
.remove = __devexit_p(sdhci_pci_remove),
.suspend = sdhci_pci_suspend,
.resume = sdhci_pci_resume,
+ .driver = {
+ .pm = &sdhci_pci_pm_ops
+ },
};
/*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index dbab0407f4b6..a9e12ea05583 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -2,6 +2,12 @@
* sdhci-pltfm.c Support for SDHCI platform devices
* Copyright (c) 2009 Intel Corporation
*
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
@@ -22,48 +28,68 @@
* Inspired by sdhci-pci.c, by Pierre Ossman
*/
-#include <linux/delay.h>
-#include <linux/highmem.h>
-#include <linux/mod_devicetable.h>
-#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#ifdef CONFIG_PPC
+#include <asm/machdep.h>
+#endif
+#include "sdhci-pltfm.h"
-#include <linux/mmc/host.h>
+static struct sdhci_ops sdhci_pltfm_ops = {
+};
-#include <linux/io.h>
-#include <linux/mmc/sdhci-pltfm.h>
+#ifdef CONFIG_OF
+static bool sdhci_of_wp_inverted(struct device_node *np)
+{
+ if (of_get_property(np, "sdhci,wp-inverted", NULL))
+ return true;
-#include "sdhci.h"
-#include "sdhci-pltfm.h"
+ /* Old device trees don't have the wp-inverted property. */
+#ifdef CONFIG_PPC
+ return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds);
+#else
+ return false;
+#endif /* CONFIG_PPC */
+}
+
+void sdhci_get_of_property(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ const __be32 *clk;
+ int size;
-/*****************************************************************************\
- * *
- * SDHCI core callbacks *
- * *
-\*****************************************************************************/
+ if (of_device_is_available(np)) {
+ if (of_get_property(np, "sdhci,auto-cmd12", NULL))
+ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
-static struct sdhci_ops sdhci_pltfm_ops = {
-};
+ if (of_get_property(np, "sdhci,1-bit-only", NULL))
+ host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
-/*****************************************************************************\
- * *
- * Device probing/removal *
- * *
-\*****************************************************************************/
+ if (sdhci_of_wp_inverted(np))
+ host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
-static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
+ clk = of_get_property(np, "clock-frequency", &size);
+ if (clk && size == sizeof(*clk) && *clk)
+ pltfm_host->clock = be32_to_cpup(clk);
+ }
+}
+#else
+void sdhci_get_of_property(struct platform_device *pdev) {}
+#endif /* CONFIG_OF */
+EXPORT_SYMBOL_GPL(sdhci_get_of_property);
+
+struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
+ struct sdhci_pltfm_data *pdata)
{
- const struct platform_device_id *platid = platform_get_device_id(pdev);
- struct sdhci_pltfm_data *pdata;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
+ struct device_node *np = pdev->dev.of_node;
struct resource *iomem;
int ret;
- if (platid && platid->driver_data)
- pdata = (void *)platid->driver_data;
- else
- pdata = pdev->dev.platform_data;
-
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) {
ret = -ENOMEM;
@@ -71,11 +97,10 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
}
if (resource_size(iomem) < 0x100)
- dev_err(&pdev->dev, "Invalid iomem size. You may "
- "experience problems.\n");
+ dev_err(&pdev->dev, "Invalid iomem size!\n");
/* Some PCI-based MFD need the parent here */
- if (pdev->dev.parent != &platform_bus)
+ if (pdev->dev.parent != &platform_bus && !np)
host = sdhci_alloc_host(pdev->dev.parent, sizeof(*pltfm_host));
else
host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host));
@@ -87,7 +112,7 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host);
- host->hw_name = "platform";
+ host->hw_name = dev_name(&pdev->dev);
if (pdata && pdata->ops)
host->ops = pdata->ops;
else
@@ -110,126 +135,95 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
goto err_remap;
}
- if (pdata && pdata->init) {
- ret = pdata->init(host, pdata);
- if (ret)
- goto err_plat_init;
- }
-
- ret = sdhci_add_host(host);
- if (ret)
- goto err_add_host;
-
platform_set_drvdata(pdev, host);
- return 0;
+ return host;
-err_add_host:
- if (pdata && pdata->exit)
- pdata->exit(host);
-err_plat_init:
- iounmap(host->ioaddr);
err_remap:
release_mem_region(iomem->start, resource_size(iomem));
err_request:
sdhci_free_host(host);
err:
- printk(KERN_ERR"Probing of sdhci-pltfm failed: %d\n", ret);
- return ret;
+ dev_err(&pdev->dev, "%s failed %d\n", __func__, ret);
+ return ERR_PTR(ret);
}
+EXPORT_SYMBOL_GPL(sdhci_pltfm_init);
-static int __devexit sdhci_pltfm_remove(struct platform_device *pdev)
+void sdhci_pltfm_free(struct platform_device *pdev)
{
- struct sdhci_pltfm_data *pdata = pdev->dev.platform_data;
struct sdhci_host *host = platform_get_drvdata(pdev);
struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- int dead;
- u32 scratch;
-
- dead = 0;
- scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
- if (scratch == (u32)-1)
- dead = 1;
- sdhci_remove_host(host, dead);
- if (pdata && pdata->exit)
- pdata->exit(host);
iounmap(host->ioaddr);
release_mem_region(iomem->start, resource_size(iomem));
sdhci_free_host(host);
platform_set_drvdata(pdev, NULL);
+}
+EXPORT_SYMBOL_GPL(sdhci_pltfm_free);
- return 0;
+int sdhci_pltfm_register(struct platform_device *pdev,
+ struct sdhci_pltfm_data *pdata)
+{
+ struct sdhci_host *host;
+ int ret = 0;
+
+ host = sdhci_pltfm_init(pdev, pdata);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ sdhci_get_of_property(pdev);
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ sdhci_pltfm_free(pdev);
+
+ return ret;
}
+EXPORT_SYMBOL_GPL(sdhci_pltfm_register);
-static const struct platform_device_id sdhci_pltfm_ids[] = {
- { "sdhci", },
-#ifdef CONFIG_MMC_SDHCI_CNS3XXX
- { "sdhci-cns3xxx", (kernel_ulong_t)&sdhci_cns3xxx_pdata },
-#endif
-#ifdef CONFIG_MMC_SDHCI_ESDHC_IMX
- { "sdhci-esdhc-imx", (kernel_ulong_t)&sdhci_esdhc_imx_pdata },
-#endif
-#ifdef CONFIG_MMC_SDHCI_DOVE
- { "sdhci-dove", (kernel_ulong_t)&sdhci_dove_pdata },
-#endif
-#ifdef CONFIG_MMC_SDHCI_TEGRA
- { "sdhci-tegra", (kernel_ulong_t)&sdhci_tegra_pdata },
-#endif
- { },
-};
-MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids);
+int sdhci_pltfm_unregister(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
+
+ sdhci_remove_host(host, dead);
+ sdhci_pltfm_free(pdev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister);
#ifdef CONFIG_PM
-static int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state)
+int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state)
{
struct sdhci_host *host = platform_get_drvdata(dev);
return sdhci_suspend_host(host, state);
}
+EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend);
-static int sdhci_pltfm_resume(struct platform_device *dev)
+int sdhci_pltfm_resume(struct platform_device *dev)
{
struct sdhci_host *host = platform_get_drvdata(dev);
return sdhci_resume_host(host);
}
-#else
-#define sdhci_pltfm_suspend NULL
-#define sdhci_pltfm_resume NULL
+EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);
#endif /* CONFIG_PM */
-static struct platform_driver sdhci_pltfm_driver = {
- .driver = {
- .name = "sdhci",
- .owner = THIS_MODULE,
- },
- .probe = sdhci_pltfm_probe,
- .remove = __devexit_p(sdhci_pltfm_remove),
- .id_table = sdhci_pltfm_ids,
- .suspend = sdhci_pltfm_suspend,
- .resume = sdhci_pltfm_resume,
-};
-
-/*****************************************************************************\
- * *
- * Driver init/exit *
- * *
-\*****************************************************************************/
-
-static int __init sdhci_drv_init(void)
+static int __init sdhci_pltfm_drv_init(void)
{
- return platform_driver_register(&sdhci_pltfm_driver);
+ pr_info("sdhci-pltfm: SDHCI platform and OF driver helper\n");
+
+ return 0;
}
+module_init(sdhci_pltfm_drv_init);
-static void __exit sdhci_drv_exit(void)
+static void __exit sdhci_pltfm_drv_exit(void)
{
- platform_driver_unregister(&sdhci_pltfm_driver);
}
+module_exit(sdhci_pltfm_drv_exit);
-module_init(sdhci_drv_init);
-module_exit(sdhci_drv_exit);
-
-MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
-MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_DESCRIPTION("SDHCI platform and OF driver helper");
+MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h
index 2b37016ad0ac..3a9fc3f40840 100644
--- a/drivers/mmc/host/sdhci-pltfm.h
+++ b/drivers/mmc/host/sdhci-pltfm.h
@@ -12,17 +12,95 @@
#define _DRIVERS_MMC_SDHCI_PLTFM_H
#include <linux/clk.h>
-#include <linux/types.h>
-#include <linux/mmc/sdhci-pltfm.h>
+#include <linux/platform_device.h>
+#include "sdhci.h"
+
+struct sdhci_pltfm_data {
+ struct sdhci_ops *ops;
+ unsigned int quirks;
+};
struct sdhci_pltfm_host {
struct clk *clk;
void *priv; /* to handle quirks across io-accessor calls */
+
+ /* migrate from sdhci_of_host */
+ unsigned int clock;
+ u16 xfer_mode_shadow;
};
-extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata;
-extern struct sdhci_pltfm_data sdhci_esdhc_imx_pdata;
-extern struct sdhci_pltfm_data sdhci_dove_pdata;
-extern struct sdhci_pltfm_data sdhci_tegra_pdata;
+#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
+/*
+ * These accessors are designed for big endian hosts doing I/O to
+ * little endian controllers incorporating a 32-bit hardware byte swapper.
+ */
+static inline u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg)
+{
+ return in_be32(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg)
+{
+ return in_be16(host->ioaddr + (reg ^ 0x2));
+}
+
+static inline u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg)
+{
+ return in_8(host->ioaddr + (reg ^ 0x3));
+}
+
+static inline void sdhci_be32bs_writel(struct sdhci_host *host,
+ u32 val, int reg)
+{
+ out_be32(host->ioaddr + reg, val);
+}
+
+static inline void sdhci_be32bs_writew(struct sdhci_host *host,
+ u16 val, int reg)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ int base = reg & ~0x3;
+ int shift = (reg & 0x2) * 8;
+
+ switch (reg) {
+ case SDHCI_TRANSFER_MODE:
+ /*
+ * Postpone this write, we must do it together with a
+ * command write that is down below.
+ */
+ pltfm_host->xfer_mode_shadow = val;
+ return;
+ case SDHCI_COMMAND:
+ sdhci_be32bs_writel(host,
+ val << 16 | pltfm_host->xfer_mode_shadow,
+ SDHCI_TRANSFER_MODE);
+ return;
+ }
+ clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
+}
+
+static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ int base = reg & ~0x3;
+ int shift = (reg & 0x3) * 8;
+
+ clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
+}
+#endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */
+
+extern void sdhci_get_of_property(struct platform_device *pdev);
+
+extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
+ struct sdhci_pltfm_data *pdata);
+extern void sdhci_pltfm_free(struct platform_device *pdev);
+
+extern int sdhci_pltfm_register(struct platform_device *pdev,
+ struct sdhci_pltfm_data *pdata);
+extern int sdhci_pltfm_unregister(struct platform_device *pdev);
+
+#ifdef CONFIG_PM
+extern int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state);
+extern int sdhci_pltfm_resume(struct platform_device *dev);
+#endif
#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
deleted file mode 100644
index 089c9a68b7b1..000000000000
--- a/drivers/mmc/host/sdhci-pxa.c
+++ /dev/null
@@ -1,303 +0,0 @@
-/* linux/drivers/mmc/host/sdhci-pxa.c
- *
- * Copyright (C) 2010 Marvell International Ltd.
- * Zhangfei Gao <zhangfei.gao@marvell.com>
- * Kevin Wang <dwang4@marvell.com>
- * Mingwei Wang <mwwang@marvell.com>
- * Philip Rakity <prakity@marvell.com>
- * Mark Brown <markb@marvell.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-/* Supports:
- * SDHCI support for MMP2/PXA910/PXA168
- *
- * Refer to sdhci-s3c.c.
- */
-
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/mmc/host.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/err.h>
-#include <plat/sdhci.h>
-#include "sdhci.h"
-
-#define DRIVER_NAME "sdhci-pxa"
-
-#define SD_FIFO_PARAM 0x104
-#define DIS_PAD_SD_CLK_GATE 0x400
-
-struct sdhci_pxa {
- struct sdhci_host *host;
- struct sdhci_pxa_platdata *pdata;
- struct clk *clk;
- struct resource *res;
-
- u8 clk_enable;
-};
-
-/*****************************************************************************\
- * *
- * SDHCI core callbacks *
- * *
-\*****************************************************************************/
-static void set_clock(struct sdhci_host *host, unsigned int clock)
-{
- struct sdhci_pxa *pxa = sdhci_priv(host);
- u32 tmp = 0;
-
- if (clock == 0) {
- if (pxa->clk_enable) {
- clk_disable(pxa->clk);
- pxa->clk_enable = 0;
- }
- } else {
- if (0 == pxa->clk_enable) {
- if (pxa->pdata->flags & PXA_FLAG_DISABLE_CLOCK_GATING) {
- tmp = readl(host->ioaddr + SD_FIFO_PARAM);
- tmp |= DIS_PAD_SD_CLK_GATE;
- writel(tmp, host->ioaddr + SD_FIFO_PARAM);
- }
- clk_enable(pxa->clk);
- pxa->clk_enable = 1;
- }
- }
-}
-
-static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
-{
- u16 ctrl_2;
-
- /*
- * Set V18_EN -- UHS modes do not work without this.
- * does not change signaling voltage
- */
- ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
-
- /* Select Bus Speed Mode for host */
- ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
- switch (uhs) {
- case MMC_TIMING_UHS_SDR12:
- ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
- break;
- case MMC_TIMING_UHS_SDR25:
- ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
- break;
- case MMC_TIMING_UHS_SDR50:
- ctrl_2 |= SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180;
- break;
- case MMC_TIMING_UHS_SDR104:
- ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180;
- break;
- case MMC_TIMING_UHS_DDR50:
- ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180;
- break;
- }
-
- sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
- pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
- __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
-
- return 0;
-}
-
-static struct sdhci_ops sdhci_pxa_ops = {
- .set_uhs_signaling = set_uhs_signaling,
- .set_clock = set_clock,
-};
-
-/*****************************************************************************\
- * *
- * Device probing/removal *
- * *
-\*****************************************************************************/
-
-static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
-{
- struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
- struct device *dev = &pdev->dev;
- struct sdhci_host *host = NULL;
- struct resource *iomem = NULL;
- struct sdhci_pxa *pxa = NULL;
- int ret, irq;
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "no irq specified\n");
- return irq;
- }
-
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!iomem) {
- dev_err(dev, "no memory specified\n");
- return -ENOENT;
- }
-
- host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pxa));
- if (IS_ERR(host)) {
- dev_err(dev, "failed to alloc host\n");
- return PTR_ERR(host);
- }
-
- pxa = sdhci_priv(host);
- pxa->host = host;
- pxa->pdata = pdata;
- pxa->clk_enable = 0;
-
- pxa->clk = clk_get(dev, "PXA-SDHCLK");
- if (IS_ERR(pxa->clk)) {
- dev_err(dev, "failed to get io clock\n");
- ret = PTR_ERR(pxa->clk);
- goto out;
- }
-
- pxa->res = request_mem_region(iomem->start, resource_size(iomem),
- mmc_hostname(host->mmc));
- if (!pxa->res) {
- dev_err(&pdev->dev, "cannot request region\n");
- ret = -EBUSY;
- goto out;
- }
-
- host->ioaddr = ioremap(iomem->start, resource_size(iomem));
- if (!host->ioaddr) {
- dev_err(&pdev->dev, "failed to remap registers\n");
- ret = -ENOMEM;
- goto out;
- }
-
- host->hw_name = "MMC";
- host->ops = &sdhci_pxa_ops;
- host->irq = irq;
- host->quirks = SDHCI_QUIRK_BROKEN_ADMA
- | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
- | SDHCI_QUIRK_32BIT_DMA_ADDR
- | SDHCI_QUIRK_32BIT_DMA_SIZE
- | SDHCI_QUIRK_32BIT_ADMA_SIZE
- | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
-
- if (pdata->quirks)
- host->quirks |= pdata->quirks;
-
- /* enable 1/8V DDR capable */
- host->mmc->caps |= MMC_CAP_1_8V_DDR;
-
- /* If slot design supports 8 bit data, indicate this to MMC. */
- if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
- host->mmc->caps |= MMC_CAP_8_BIT_DATA;
-
- ret = sdhci_add_host(host);
- if (ret) {
- dev_err(&pdev->dev, "failed to add host\n");
- goto out;
- }
-
- if (pxa->pdata->max_speed)
- host->mmc->f_max = pxa->pdata->max_speed;
-
- platform_set_drvdata(pdev, host);
-
- return 0;
-out:
- if (host) {
- clk_put(pxa->clk);
- if (host->ioaddr)
- iounmap(host->ioaddr);
- if (pxa->res)
- release_mem_region(pxa->res->start,
- resource_size(pxa->res));
- sdhci_free_host(host);
- }
-
- return ret;
-}
-
-static int __devexit sdhci_pxa_remove(struct platform_device *pdev)
-{
- struct sdhci_host *host = platform_get_drvdata(pdev);
- struct sdhci_pxa *pxa = sdhci_priv(host);
- int dead = 0;
- u32 scratch;
-
- if (host) {
- scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
- if (scratch == (u32)-1)
- dead = 1;
-
- sdhci_remove_host(host, dead);
-
- if (host->ioaddr)
- iounmap(host->ioaddr);
- if (pxa->res)
- release_mem_region(pxa->res->start,
- resource_size(pxa->res));
- if (pxa->clk_enable) {
- clk_disable(pxa->clk);
- pxa->clk_enable = 0;
- }
- clk_put(pxa->clk);
-
- sdhci_free_host(host);
- platform_set_drvdata(pdev, NULL);
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int sdhci_pxa_suspend(struct platform_device *dev, pm_message_t state)
-{
- struct sdhci_host *host = platform_get_drvdata(dev);
-
- return sdhci_suspend_host(host, state);
-}
-
-static int sdhci_pxa_resume(struct platform_device *dev)
-{
- struct sdhci_host *host = platform_get_drvdata(dev);
-
- return sdhci_resume_host(host);
-}
-#else
-#define sdhci_pxa_suspend NULL
-#define sdhci_pxa_resume NULL
-#endif
-
-static struct platform_driver sdhci_pxa_driver = {
- .probe = sdhci_pxa_probe,
- .remove = __devexit_p(sdhci_pxa_remove),
- .suspend = sdhci_pxa_suspend,
- .resume = sdhci_pxa_resume,
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-/*****************************************************************************\
- * *
- * Driver init/exit *
- * *
-\*****************************************************************************/
-
-static int __init sdhci_pxa_init(void)
-{
- return platform_driver_register(&sdhci_pxa_driver);
-}
-
-static void __exit sdhci_pxa_exit(void)
-{
- platform_driver_unregister(&sdhci_pxa_driver);
-}
-
-module_init(sdhci_pxa_init);
-module_exit(sdhci_pxa_exit);
-
-MODULE_DESCRIPTION("SDH controller driver for PXA168/PXA910/MMP2");
-MODULE_AUTHOR("Zhangfei Gao <zhangfei.gao@marvell.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c
new file mode 100644
index 000000000000..d4bf6d30c7ba
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pxav2.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2010 Marvell International Ltd.
+ * Zhangfei Gao <zhangfei.gao@marvell.com>
+ * Kevin Wang <dwang4@marvell.com>
+ * Jun Nie <njun@marvell.com>
+ * Qiming Wu <wuqm@marvell.com>
+ * Philip Rakity <prakity@marvell.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/platform_data/pxa_sdhci.h>
+#include <linux/slab.h>
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+
+#define SD_FIFO_PARAM 0xe0
+#define DIS_PAD_SD_CLK_GATE 0x0400 /* Turn on/off Dynamic SD Clock Gating */
+#define CLK_GATE_ON 0x0200 /* Disable/enable Clock Gate */
+#define CLK_GATE_CTL 0x0100 /* Clock Gate Control */
+#define CLK_GATE_SETTING_BITS (DIS_PAD_SD_CLK_GATE | \
+ CLK_GATE_ON | CLK_GATE_CTL)
+
+#define SD_CLOCK_BURST_SIZE_SETUP 0xe6
+#define SDCLK_SEL_SHIFT 8
+#define SDCLK_SEL_MASK 0x3
+#define SDCLK_DELAY_SHIFT 10
+#define SDCLK_DELAY_MASK 0x3c
+
+#define SD_CE_ATA_2 0xea
+#define MMC_CARD 0x1000
+#define MMC_WIDTH 0x0100
+
+static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask)
+{
+ struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
+ struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
+
+ if (mask == SDHCI_RESET_ALL) {
+ u16 tmp = 0;
+
+ /*
+ * tune timing of read data/command when crc error happen
+ * no performance impact
+ */
+ if (pdata && pdata->clk_delay_sel == 1) {
+ tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
+
+ tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
+ tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK)
+ << SDCLK_DELAY_SHIFT;
+ tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT);
+ tmp |= (1 & SDCLK_SEL_MASK) << SDCLK_SEL_SHIFT;
+
+ writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
+ }
+
+ if (pdata && (pdata->flags & PXA_FLAG_ENABLE_CLOCK_GATING)) {
+ tmp = readw(host->ioaddr + SD_FIFO_PARAM);
+ tmp &= ~CLK_GATE_SETTING_BITS;
+ writew(tmp, host->ioaddr + SD_FIFO_PARAM);
+ } else {
+ tmp = readw(host->ioaddr + SD_FIFO_PARAM);
+ tmp &= ~CLK_GATE_SETTING_BITS;
+ tmp |= CLK_GATE_SETTING_BITS;
+ writew(tmp, host->ioaddr + SD_FIFO_PARAM);
+ }
+ }
+}
+
+static int pxav2_mmc_set_width(struct sdhci_host *host, int width)
+{
+ u8 ctrl;
+ u16 tmp;
+
+ ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+ tmp = readw(host->ioaddr + SD_CE_ATA_2);
+ if (width == MMC_BUS_WIDTH_8) {
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
+ tmp |= MMC_CARD | MMC_WIDTH;
+ } else {
+ tmp &= ~(MMC_CARD | MMC_WIDTH);
+ if (width == MMC_BUS_WIDTH_4)
+ ctrl |= SDHCI_CTRL_4BITBUS;
+ else
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
+ }
+ writew(tmp, host->ioaddr + SD_CE_ATA_2);
+ writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+
+ return 0;
+}
+
+static u32 pxav2_get_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+ return clk_get_rate(pltfm_host->clk);
+}
+
+static struct sdhci_ops pxav2_sdhci_ops = {
+ .get_max_clock = pxav2_get_max_clock,
+ .platform_reset_exit = pxav2_set_private_registers,
+ .platform_8bit_width = pxav2_mmc_set_width,
+};
+
+static int __devinit sdhci_pxav2_probe(struct platform_device *pdev)
+{
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct sdhci_host *host = NULL;
+ struct sdhci_pxa *pxa = NULL;
+ int ret;
+ struct clk *clk;
+
+ pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL);
+ if (!pxa)
+ return -ENOMEM;
+
+ host = sdhci_pltfm_init(pdev, NULL);
+ if (IS_ERR(host)) {
+ kfree(pxa);
+ return PTR_ERR(host);
+ }
+ pltfm_host = sdhci_priv(host);
+ pltfm_host->priv = pxa;
+
+ clk = clk_get(dev, "PXA-SDHCLK");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "failed to get io clock\n");
+ ret = PTR_ERR(clk);
+ goto err_clk_get;
+ }
+ pltfm_host->clk = clk;
+ clk_enable(clk);
+
+ host->quirks = SDHCI_QUIRK_BROKEN_ADMA
+ | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
+ | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
+
+ if (pdata) {
+ if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
+ /* on-chip device */
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+ }
+
+ /* If slot design supports 8 bit data, indicate this to MMC. */
+ if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
+ host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+
+ if (pdata->quirks)
+ host->quirks |= pdata->quirks;
+ if (pdata->host_caps)
+ host->mmc->caps |= pdata->host_caps;
+ if (pdata->pm_caps)
+ host->mmc->pm_caps |= pdata->pm_caps;
+ }
+
+ host->ops = &pxav2_sdhci_ops;
+
+ ret = sdhci_add_host(host);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add host\n");
+ goto err_add_host;
+ }
+
+ platform_set_drvdata(pdev, host);
+
+ return 0;
+
+err_add_host:
+ clk_disable(clk);
+ clk_put(clk);
+err_clk_get:
+ sdhci_pltfm_free(pdev);
+ kfree(pxa);
+ return ret;
+}
+
+static int __devexit sdhci_pxav2_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_pxa *pxa = pltfm_host->priv;
+
+ sdhci_remove_host(host, 1);
+
+ clk_disable(pltfm_host->clk);
+ clk_put(pltfm_host->clk);
+ sdhci_pltfm_free(pdev);
+ kfree(pxa);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver sdhci_pxav2_driver = {
+ .driver = {
+ .name = "sdhci-pxav2",
+ .owner = THIS_MODULE,
+ },
+ .probe = sdhci_pxav2_probe,
+ .remove = __devexit_p(sdhci_pxav2_remove),
+#ifdef CONFIG_PM
+ .suspend = sdhci_pltfm_suspend,
+ .resume = sdhci_pltfm_resume,
+#endif
+};
+static int __init sdhci_pxav2_init(void)
+{
+ return platform_driver_register(&sdhci_pxav2_driver);
+}
+
+static void __exit sdhci_pxav2_exit(void)
+{
+ platform_driver_unregister(&sdhci_pxav2_driver);
+}
+
+module_init(sdhci_pxav2_init);
+module_exit(sdhci_pxav2_exit);
+
+MODULE_DESCRIPTION("SDHCI driver for pxav2");
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
new file mode 100644
index 000000000000..cff4ad3e7a59
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2010 Marvell International Ltd.
+ * Zhangfei Gao <zhangfei.gao@marvell.com>
+ * Kevin Wang <dwang4@marvell.com>
+ * Mingwei Wang <mwwang@marvell.com>
+ * Philip Rakity <prakity@marvell.com>
+ * Mark Brown <markb@marvell.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/platform_data/pxa_sdhci.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+
+#define SD_CLOCK_BURST_SIZE_SETUP 0x10A
+#define SDCLK_SEL 0x100
+#define SDCLK_DELAY_SHIFT 9
+#define SDCLK_DELAY_MASK 0x1f
+
+#define SD_CFG_FIFO_PARAM 0x100
+#define SDCFG_GEN_PAD_CLK_ON (1<<6)
+#define SDCFG_GEN_PAD_CLK_CNT_MASK 0xFF
+#define SDCFG_GEN_PAD_CLK_CNT_SHIFT 24
+
+#define SD_SPI_MODE 0x108
+#define SD_CE_ATA_1 0x10C
+
+#define SD_CE_ATA_2 0x10E
+#define SDCE_MISC_INT (1<<2)
+#define SDCE_MISC_INT_EN (1<<1)
+
+static void pxav3_set_private_registers(struct sdhci_host *host, u8 mask)
+{
+ struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
+ struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
+
+ if (mask == SDHCI_RESET_ALL) {
+ /*
+ * tune timing of read data/command when crc error happen
+ * no performance impact
+ */
+ if (pdata && 0 != pdata->clk_delay_cycles) {
+ u16 tmp;
+
+ tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
+ tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK)
+ << SDCLK_DELAY_SHIFT;
+ tmp |= SDCLK_SEL;
+ writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
+ }
+ }
+}
+
+#define MAX_WAIT_COUNT 5
+static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_pxa *pxa = pltfm_host->priv;
+ u16 tmp;
+ int count;
+
+ if (pxa->power_mode == MMC_POWER_UP
+ && power_mode == MMC_POWER_ON) {
+
+ dev_dbg(mmc_dev(host->mmc),
+ "%s: slot->power_mode = %d,"
+ "ios->power_mode = %d\n",
+ __func__,
+ pxa->power_mode,
+ power_mode);
+
+ /* set we want notice of when 74 clocks are sent */
+ tmp = readw(host->ioaddr + SD_CE_ATA_2);
+ tmp |= SDCE_MISC_INT_EN;
+ writew(tmp, host->ioaddr + SD_CE_ATA_2);
+
+ /* start sending the 74 clocks */
+ tmp = readw(host->ioaddr + SD_CFG_FIFO_PARAM);
+ tmp |= SDCFG_GEN_PAD_CLK_ON;
+ writew(tmp, host->ioaddr + SD_CFG_FIFO_PARAM);
+
+ /* slowest speed is about 100KHz or 10usec per clock */
+ udelay(740);
+ count = 0;
+
+ while (count++ < MAX_WAIT_COUNT) {
+ if ((readw(host->ioaddr + SD_CE_ATA_2)
+ & SDCE_MISC_INT) == 0)
+ break;
+ udelay(10);
+ }
+
+ if (count == MAX_WAIT_COUNT)
+ dev_warn(mmc_dev(host->mmc), "74 clock interrupt not cleared\n");
+
+ /* clear the interrupt bit if posted */
+ tmp = readw(host->ioaddr + SD_CE_ATA_2);
+ tmp |= SDCE_MISC_INT;
+ writew(tmp, host->ioaddr + SD_CE_ATA_2);
+ }
+ pxa->power_mode = power_mode;
+}
+
+static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
+{
+ u16 ctrl_2;
+
+ /*
+ * Set V18_EN -- UHS modes do not work without this.
+ * does not change signaling voltage
+ */
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+ /* Select Bus Speed Mode for host */
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+ switch (uhs) {
+ case MMC_TIMING_UHS_SDR12:
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+ break;
+ case MMC_TIMING_UHS_SDR25:
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180;
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180;
+ break;
+ case MMC_TIMING_UHS_DDR50:
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180;
+ break;
+ }
+
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+ dev_dbg(mmc_dev(host->mmc),
+ "%s uhs = %d, ctrl_2 = %04X\n",
+ __func__, uhs, ctrl_2);
+
+ return 0;
+}
+
+static struct sdhci_ops pxav3_sdhci_ops = {
+ .platform_reset_exit = pxav3_set_private_registers,
+ .set_uhs_signaling = pxav3_set_uhs_signaling,
+ .platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
+};
+
+static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
+{
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct sdhci_host *host = NULL;
+ struct sdhci_pxa *pxa = NULL;
+ int ret;
+ struct clk *clk;
+
+ pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL);
+ if (!pxa)
+ return -ENOMEM;
+
+ host = sdhci_pltfm_init(pdev, NULL);
+ if (IS_ERR(host)) {
+ kfree(pxa);
+ return PTR_ERR(host);
+ }
+ pltfm_host = sdhci_priv(host);
+ pltfm_host->priv = pxa;
+
+ clk = clk_get(dev, "PXA-SDHCLK");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "failed to get io clock\n");
+ ret = PTR_ERR(clk);
+ goto err_clk_get;
+ }
+ pltfm_host->clk = clk;
+ clk_enable(clk);
+
+ host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
+ | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
+ | SDHCI_QUIRK_32BIT_ADMA_SIZE;
+
+ /* enable 1/8V DDR capable */
+ host->mmc->caps |= MMC_CAP_1_8V_DDR;
+
+ if (pdata) {
+ if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
+ /* on-chip device */
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+ }
+
+ /* If slot design supports 8 bit data, indicate this to MMC. */
+ if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
+ host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+
+ if (pdata->quirks)
+ host->quirks |= pdata->quirks;
+ if (pdata->host_caps)
+ host->mmc->caps |= pdata->host_caps;
+ if (pdata->pm_caps)
+ host->mmc->pm_caps |= pdata->pm_caps;
+ }
+
+ host->ops = &pxav3_sdhci_ops;
+
+ ret = sdhci_add_host(host);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add host\n");
+ goto err_add_host;
+ }
+
+ platform_set_drvdata(pdev, host);
+
+ return 0;
+
+err_add_host:
+ clk_disable(clk);
+ clk_put(clk);
+err_clk_get:
+ sdhci_pltfm_free(pdev);
+ kfree(pxa);
+ return ret;
+}
+
+static int __devexit sdhci_pxav3_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_pxa *pxa = pltfm_host->priv;
+
+ sdhci_remove_host(host, 1);
+
+ clk_disable(pltfm_host->clk);
+ clk_put(pltfm_host->clk);
+ sdhci_pltfm_free(pdev);
+ kfree(pxa);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver sdhci_pxav3_driver = {
+ .driver = {
+ .name = "sdhci-pxav3",
+ .owner = THIS_MODULE,
+ },
+ .probe = sdhci_pxav3_probe,
+ .remove = __devexit_p(sdhci_pxav3_remove),
+#ifdef CONFIG_PM
+ .suspend = sdhci_pltfm_suspend,
+ .resume = sdhci_pltfm_resume,
+#endif
+};
+static int __init sdhci_pxav3_init(void)
+{
+ return platform_driver_register(&sdhci_pxav3_driver);
+}
+
+static void __exit sdhci_pxav3_exit(void)
+{
+ platform_driver_unregister(&sdhci_pxav3_driver);
+}
+
+module_init(sdhci_pxav3_init);
+module_exit(sdhci_pxav3_exit);
+
+MODULE_DESCRIPTION("SDHCI driver for pxav3");
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 69e3ee321eb5..3d00e722efc9 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -19,6 +19,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/gpio.h>
+#include <linux/module.h>
#include <linux/mmc/host.h>
@@ -202,17 +203,23 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
}
- /* reconfigure the hardware for new clock rate */
-
- {
- struct mmc_ios ios;
-
- ios.clock = clock;
-
- if (ourhost->pdata->cfg_card)
- (ourhost->pdata->cfg_card)(ourhost->pdev, host->ioaddr,
- &ios, NULL);
- }
+ /* reprogram default hardware configuration */
+ writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA,
+ host->ioaddr + S3C64XX_SDHCI_CONTROL4);
+
+ ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
+ ctrl |= (S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR |
+ S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK |
+ S3C_SDHCI_CTRL2_ENFBCLKRX |
+ S3C_SDHCI_CTRL2_DFCNT_NONE |
+ S3C_SDHCI_CTRL2_ENCLKOUTHOLD);
+ writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
+
+ /* reconfigure the controller for new clock rate */
+ ctrl = (S3C_SDHCI_CTRL3_FCSEL1 | S3C_SDHCI_CTRL3_FCSEL0);
+ if (clock < 25 * 1000000)
+ ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2);
+ writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3);
}
/**
@@ -301,6 +308,8 @@ static int sdhci_s3c_platform_8bit_width(struct sdhci_host *host, int width)
ctrl &= ~SDHCI_CTRL_8BITBUS;
break;
default:
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
+ ctrl &= ~SDHCI_CTRL_8BITBUS;
break;
}
@@ -502,6 +511,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
/* This host supports the Auto CMD12 */
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+ /* Samsung SoCs need BROKEN_ADMA_ZEROLEN_DESC */
+ host->quirks |= SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC;
+
if (pdata->cd_type == S3C_SDHCI_CD_NONE ||
pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
@@ -555,8 +567,10 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
err_req_regs:
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
- clk_disable(sc->clk_bus[ptr]);
- clk_put(sc->clk_bus[ptr]);
+ if (sc->clk_bus[ptr]) {
+ clk_disable(sc->clk_bus[ptr]);
+ clk_put(sc->clk_bus[ptr]);
+ }
}
err_no_busclks:
@@ -612,16 +626,14 @@ static int sdhci_s3c_suspend(struct platform_device *dev, pm_message_t pm)
{
struct sdhci_host *host = platform_get_drvdata(dev);
- sdhci_suspend_host(host, pm);
- return 0;
+ return sdhci_suspend_host(host, pm);
}
static int sdhci_s3c_resume(struct platform_device *dev)
{
struct sdhci_host *host = platform_get_drvdata(dev);
- sdhci_resume_host(host);
- return 0;
+ return sdhci_resume_host(host);
}
#else
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index 60a4c97d3d18..63cc8b6a1c9e 100644
--- a/drivers/mmc/host/sdhci-spear.c
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -17,6 +17,7 @@
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/highmem.h>
+#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
@@ -177,8 +178,6 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
sdhci->data->card_power_gpio);
goto err_pgpio_direction;
}
-
- gpio_set_value(sdhci->data->card_power_gpio, 1);
}
if (sdhci->data->card_int_gpio >= 0) {
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 343c97edba32..89699e861fc1 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -13,18 +13,23 @@
*/
#include <linux/err.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
+#include <linux/module.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
+
+#include <mach/gpio-tegra.h>
#include <mach/sdhci.h>
-#include "sdhci.h"
#include "sdhci-pltfm.h"
static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
@@ -74,10 +79,8 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci)
{
- struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc));
- struct tegra_sdhci_platform_data *plat;
-
- plat = pdev->dev.platform_data;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+ struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
if (!gpio_is_valid(plat->wp_gpio))
return -1;
@@ -95,12 +98,10 @@ static irqreturn_t carddetect_irq(int irq, void *data)
static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
{
- struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
- struct tegra_sdhci_platform_data *plat;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
u32 ctrl;
- plat = pdev->dev.platform_data;
-
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
ctrl &= ~SDHCI_CTRL_4BITBUS;
@@ -116,28 +117,85 @@ static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
return 0;
}
+static struct sdhci_ops tegra_sdhci_ops = {
+ .get_ro = tegra_sdhci_get_ro,
+ .read_l = tegra_sdhci_readl,
+ .read_w = tegra_sdhci_readw,
+ .write_l = tegra_sdhci_writel,
+ .platform_8bit_width = tegra_sdhci_8bit,
+};
+
+static struct sdhci_pltfm_data sdhci_tegra_pdata = {
+ .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+ SDHCI_QUIRK_SINGLE_POWER_WRITE |
+ SDHCI_QUIRK_NO_HISPD_BIT |
+ SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
+ .ops = &tegra_sdhci_ops,
+};
+
+static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
+ { .compatible = "nvidia,tegra20-sdhci", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
-static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
- struct sdhci_pltfm_data *pdata)
+static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
+ struct platform_device *pdev)
{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
struct tegra_sdhci_platform_data *plat;
+ struct device_node *np = pdev->dev.of_node;
+
+ if (!np)
+ return NULL;
+
+ plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
+ if (!plat) {
+ dev_err(&pdev->dev, "Can't allocate platform data\n");
+ return NULL;
+ }
+
+ plat->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
+ plat->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
+ plat->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
+ if (of_find_property(np, "support-8bit", NULL))
+ plat->is_8bit = 1;
+
+ return plat;
+}
+
+static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
+{
+ struct sdhci_pltfm_host *pltfm_host;
+ struct tegra_sdhci_platform_data *plat;
+ struct sdhci_host *host;
struct clk *clk;
int rc;
+ host = sdhci_pltfm_init(pdev, &sdhci_tegra_pdata);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+
plat = pdev->dev.platform_data;
+
+ if (plat == NULL)
+ plat = sdhci_tegra_dt_parse_pdata(pdev);
+
if (plat == NULL) {
dev_err(mmc_dev(host->mmc), "missing platform data\n");
- return -ENXIO;
+ rc = -ENXIO;
+ goto err_no_plat;
}
+ pltfm_host->priv = plat;
+
if (gpio_is_valid(plat->power_gpio)) {
rc = gpio_request(plat->power_gpio, "sdhci_power");
if (rc) {
dev_err(mmc_dev(host->mmc),
"failed to allocate power gpio\n");
- goto out;
+ goto err_power_req;
}
tegra_gpio_enable(plat->power_gpio);
gpio_direction_output(plat->power_gpio, 1);
@@ -148,7 +206,7 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
if (rc) {
dev_err(mmc_dev(host->mmc),
"failed to allocate cd gpio\n");
- goto out_power;
+ goto err_cd_req;
}
tegra_gpio_enable(plat->cd_gpio);
gpio_direction_input(plat->cd_gpio);
@@ -159,7 +217,7 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
if (rc) {
dev_err(mmc_dev(host->mmc), "request irq error\n");
- goto out_cd;
+ goto err_cd_irq_req;
}
}
@@ -169,7 +227,7 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
if (rc) {
dev_err(mmc_dev(host->mmc),
"failed to allocate wp gpio\n");
- goto out_irq;
+ goto err_wp_req;
}
tegra_gpio_enable(plat->wp_gpio);
gpio_direction_input(plat->wp_gpio);
@@ -179,7 +237,7 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
if (IS_ERR(clk)) {
dev_err(mmc_dev(host->mmc), "clk err\n");
rc = PTR_ERR(clk);
- goto out_wp;
+ goto err_clk_get;
}
clk_enable(clk);
pltfm_host->clk = clk;
@@ -189,40 +247,47 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
if (plat->is_8bit)
host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+ rc = sdhci_add_host(host);
+ if (rc)
+ goto err_add_host;
+
return 0;
-out_wp:
+err_add_host:
+ clk_disable(pltfm_host->clk);
+ clk_put(pltfm_host->clk);
+err_clk_get:
if (gpio_is_valid(plat->wp_gpio)) {
tegra_gpio_disable(plat->wp_gpio);
gpio_free(plat->wp_gpio);
}
-
-out_irq:
+err_wp_req:
if (gpio_is_valid(plat->cd_gpio))
free_irq(gpio_to_irq(plat->cd_gpio), host);
-out_cd:
+err_cd_irq_req:
if (gpio_is_valid(plat->cd_gpio)) {
tegra_gpio_disable(plat->cd_gpio);
gpio_free(plat->cd_gpio);
}
-
-out_power:
+err_cd_req:
if (gpio_is_valid(plat->power_gpio)) {
tegra_gpio_disable(plat->power_gpio);
gpio_free(plat->power_gpio);
}
-
-out:
+err_power_req:
+err_no_plat:
+ sdhci_pltfm_free(pdev);
return rc;
}
-static void tegra_sdhci_pltfm_exit(struct sdhci_host *host)
+static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
- struct tegra_sdhci_platform_data *plat;
+ struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
+ int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
- plat = pdev->dev.platform_data;
+ sdhci_remove_host(host, dead);
if (gpio_is_valid(plat->wp_gpio)) {
tegra_gpio_disable(plat->wp_gpio);
@@ -242,22 +307,38 @@ static void tegra_sdhci_pltfm_exit(struct sdhci_host *host)
clk_disable(pltfm_host->clk);
clk_put(pltfm_host->clk);
+
+ sdhci_pltfm_free(pdev);
+
+ return 0;
}
-static struct sdhci_ops tegra_sdhci_ops = {
- .get_ro = tegra_sdhci_get_ro,
- .read_l = tegra_sdhci_readl,
- .read_w = tegra_sdhci_readw,
- .write_l = tegra_sdhci_writel,
- .platform_8bit_width = tegra_sdhci_8bit,
+static struct platform_driver sdhci_tegra_driver = {
+ .driver = {
+ .name = "sdhci-tegra",
+ .owner = THIS_MODULE,
+ .of_match_table = sdhci_tegra_dt_match,
+ },
+ .probe = sdhci_tegra_probe,
+ .remove = __devexit_p(sdhci_tegra_remove),
+#ifdef CONFIG_PM
+ .suspend = sdhci_pltfm_suspend,
+ .resume = sdhci_pltfm_resume,
+#endif
};
-struct sdhci_pltfm_data sdhci_tegra_pdata = {
- .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
- SDHCI_QUIRK_SINGLE_POWER_WRITE |
- SDHCI_QUIRK_NO_HISPD_BIT |
- SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
- .ops = &tegra_sdhci_ops,
- .init = tegra_sdhci_pltfm_init,
- .exit = tegra_sdhci_pltfm_exit,
-};
+static int __init sdhci_tegra_init(void)
+{
+ return platform_driver_register(&sdhci_tegra_driver);
+}
+module_init(sdhci_tegra_init);
+
+static void __exit sdhci_tegra_exit(void)
+{
+ platform_driver_unregister(&sdhci_tegra_driver);
+}
+module_exit(sdhci_tegra_exit);
+
+MODULE_DESCRIPTION("SDHCI driver for Tegra");
+MODULE_AUTHOR(" Google, Inc.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 58d5436ff649..6d8eea323541 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -16,10 +16,12 @@
#include <linux/delay.h>
#include <linux/highmem.h>
#include <linux/io.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/scatterlist.h>
#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/leds.h>
@@ -41,6 +43,7 @@
#define MAX_TUNING_LOOP 40
static unsigned int debug_quirks = 0;
+static unsigned int debug_quirks2;
static void sdhci_finish_data(struct sdhci_host *);
@@ -49,53 +52,67 @@ static void sdhci_finish_command(struct sdhci_host *);
static int sdhci_execute_tuning(struct mmc_host *mmc);
static void sdhci_tuning_timer(unsigned long data);
+#ifdef CONFIG_PM_RUNTIME
+static int sdhci_runtime_pm_get(struct sdhci_host *host);
+static int sdhci_runtime_pm_put(struct sdhci_host *host);
+#else
+static inline int sdhci_runtime_pm_get(struct sdhci_host *host)
+{
+ return 0;
+}
+static inline int sdhci_runtime_pm_put(struct sdhci_host *host)
+{
+ return 0;
+}
+#endif
+
static void sdhci_dumpregs(struct sdhci_host *host)
{
- printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
+ pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
mmc_hostname(host->mmc));
- printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
sdhci_readl(host, SDHCI_DMA_ADDRESS),
sdhci_readw(host, SDHCI_HOST_VERSION));
- printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
sdhci_readw(host, SDHCI_BLOCK_SIZE),
sdhci_readw(host, SDHCI_BLOCK_COUNT));
- printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
sdhci_readl(host, SDHCI_ARGUMENT),
sdhci_readw(host, SDHCI_TRANSFER_MODE));
- printk(KERN_DEBUG DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
sdhci_readl(host, SDHCI_PRESENT_STATE),
sdhci_readb(host, SDHCI_HOST_CONTROL));
- printk(KERN_DEBUG DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
sdhci_readb(host, SDHCI_POWER_CONTROL),
sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
- printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
sdhci_readw(host, SDHCI_CLOCK_CONTROL));
- printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
sdhci_readl(host, SDHCI_INT_STATUS));
- printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
sdhci_readl(host, SDHCI_INT_ENABLE),
sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
- printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
sdhci_readw(host, SDHCI_ACMD12_ERR),
sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
- printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
sdhci_readl(host, SDHCI_CAPABILITIES),
sdhci_readl(host, SDHCI_CAPABILITIES_1));
- printk(KERN_DEBUG DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
sdhci_readw(host, SDHCI_COMMAND),
sdhci_readl(host, SDHCI_MAX_CURRENT));
- printk(KERN_DEBUG DRIVER_NAME ": Host ctl2: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
sdhci_readw(host, SDHCI_HOST_CONTROL2));
if (host->flags & SDHCI_USE_ADMA)
- printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
+ pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
readl(host->ioaddr + SDHCI_ADMA_ERROR),
readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
- printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
+ pr_debug(DRIVER_NAME ": ===========================================\n");
}
/*****************************************************************************\
@@ -127,11 +144,18 @@ static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs)
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{
- u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
+ u32 present, irqs;
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
return;
+ if (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION)
+ return;
+
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT;
+ irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
+
if (enable)
sdhci_unmask_irqs(host, irqs);
else
@@ -176,7 +200,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
/* hw clears the bit when it's done */
while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
if (timeout == 0) {
- printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
+ pr_err("%s: Reset 0x%x never completed.\n",
mmc_hostname(host->mmc), (int)mask);
sdhci_dumpregs(host);
return;
@@ -247,11 +271,14 @@ static void sdhci_led_control(struct led_classdev *led,
spin_lock_irqsave(&host->lock, flags);
+ if (host->runtime_suspended)
+ goto out;
+
if (brightness == LED_OFF)
sdhci_deactivate_led(host);
else
sdhci_activate_led(host);
-
+out:
spin_unlock_irqrestore(&host->lock, flags);
}
#endif
@@ -624,12 +651,11 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
/* timeout in us */
if (!data)
target_timeout = cmd->cmd_timeout_ms * 1000;
- else
- target_timeout = data->timeout_ns / 1000 +
- data->timeout_clks / host->clock;
-
- if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
- host->timeout_clk = host->clock / 1000;
+ else {
+ target_timeout = data->timeout_ns / 1000;
+ if (host->clock)
+ target_timeout += data->timeout_clks / host->clock;
+ }
/*
* Figure out needed cycles.
@@ -641,7 +667,6 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
* =>
* (1) / (2) > 2^6
*/
- BUG_ON(!host->timeout_clk);
count = 0;
current_timeout = (1 << 13) * 1000 / host->timeout_clk;
while (current_timeout < target_timeout) {
@@ -652,7 +677,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
}
if (count >= 0xF) {
- printk(KERN_WARNING "%s: Too large timeout requested for CMD%d!\n",
+ pr_warning("%s: Too large timeout requested for CMD%d!\n",
mmc_hostname(host->mmc), cmd->opcode);
count = 0xE;
}
@@ -947,7 +972,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (timeout == 0) {
- printk(KERN_ERR "%s: Controller never released "
+ pr_err("%s: Controller never released "
"inhibit bit(s).\n", mmc_hostname(host->mmc));
sdhci_dumpregs(host);
cmd->error = -EIO;
@@ -969,7 +994,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_set_transfer_mode(host, cmd);
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
- printk(KERN_ERR "%s: Unsupported response type!\n",
+ pr_err("%s: Unsupported response type!\n",
mmc_hostname(host->mmc));
cmd->error = -EINVAL;
tasklet_schedule(&host->finish_tasklet);
@@ -1119,7 +1144,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
& SDHCI_CLOCK_INT_STABLE)) {
if (timeout == 0) {
- printk(KERN_ERR "%s: Internal clock never "
+ pr_err("%s: Internal clock never "
"stabilised.\n", mmc_hostname(host->mmc));
sdhci_dumpregs(host);
return;
@@ -1207,6 +1232,8 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host = mmc_priv(mmc);
+ sdhci_runtime_pm_get(host);
+
spin_lock_irqsave(&host->lock, flags);
WARN_ON(host->mrq != NULL);
@@ -1267,14 +1294,11 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_unlock_irqrestore(&host->lock, flags);
}
-static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
{
- struct sdhci_host *host;
unsigned long flags;
u8 ctrl;
- host = mmc_priv(mmc);
-
spin_lock_irqsave(&host->lock, flags);
if (host->flags & SDHCI_DEVICE_DEAD)
@@ -1424,7 +1448,16 @@ out:
spin_unlock_irqrestore(&host->lock, flags);
}
-static int check_ro(struct sdhci_host *host)
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ sdhci_runtime_pm_get(host);
+ sdhci_do_set_ios(host, ios);
+ sdhci_runtime_pm_put(host);
+}
+
+static int sdhci_check_ro(struct sdhci_host *host)
{
unsigned long flags;
int is_readonly;
@@ -1448,19 +1481,16 @@ static int check_ro(struct sdhci_host *host)
#define SAMPLE_COUNT 5
-static int sdhci_get_ro(struct mmc_host *mmc)
+static int sdhci_do_get_ro(struct sdhci_host *host)
{
- struct sdhci_host *host;
int i, ro_count;
- host = mmc_priv(mmc);
-
if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT))
- return check_ro(host);
+ return sdhci_check_ro(host);
ro_count = 0;
for (i = 0; i < SAMPLE_COUNT; i++) {
- if (check_ro(host)) {
+ if (sdhci_check_ro(host)) {
if (++ro_count > SAMPLE_COUNT / 2)
return 1;
}
@@ -1469,38 +1499,64 @@ static int sdhci_get_ro(struct mmc_host *mmc)
return 0;
}
-static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+static void sdhci_hw_reset(struct mmc_host *mmc)
{
- struct sdhci_host *host;
- unsigned long flags;
+ struct sdhci_host *host = mmc_priv(mmc);
- host = mmc_priv(mmc);
+ if (host->ops && host->ops->hw_reset)
+ host->ops->hw_reset(host);
+}
- spin_lock_irqsave(&host->lock, flags);
+static int sdhci_get_ro(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ int ret;
+
+ sdhci_runtime_pm_get(host);
+ ret = sdhci_do_get_ro(host);
+ sdhci_runtime_pm_put(host);
+ return ret;
+}
+static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
+{
if (host->flags & SDHCI_DEVICE_DEAD)
goto out;
if (enable)
+ host->flags |= SDHCI_SDIO_IRQ_ENABLED;
+ else
+ host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
+
+ /* SDIO IRQ will be enabled as appropriate in runtime resume */
+ if (host->runtime_suspended)
+ goto out;
+
+ if (enable)
sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
else
sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
out:
mmiowb();
+}
+
+static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_enable_sdio_irq_nolock(host, enable);
spin_unlock_irqrestore(&host->lock, flags);
}
-static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
- struct mmc_ios *ios)
+static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
+ struct mmc_ios *ios)
{
- struct sdhci_host *host;
u8 pwr;
u16 clk, ctrl;
u32 present_state;
- host = mmc_priv(mmc);
-
/*
* Signal Voltage Switching is only applicable for Host Controllers
* v3.00 and above.
@@ -1526,7 +1582,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
if (!(ctrl & SDHCI_CTRL_VDD_180))
return 0;
else {
- printk(KERN_INFO DRIVER_NAME ": Switching to 3.3V "
+ pr_info(DRIVER_NAME ": Switching to 3.3V "
"signalling voltage failed\n");
return -EIO;
}
@@ -1585,7 +1641,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
pwr |= SDHCI_POWER_ON;
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
- printk(KERN_INFO DRIVER_NAME ": Switching to 1.8V signalling "
+ pr_info(DRIVER_NAME ": Switching to 1.8V signalling "
"voltage failed, retrying with S18R set to 0\n");
return -EAGAIN;
} else
@@ -1593,6 +1649,20 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return 0;
}
+static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ int err;
+
+ if (host->version < SDHCI_SPEC_300)
+ return 0;
+ sdhci_runtime_pm_get(host);
+ err = sdhci_do_start_signal_voltage_switch(host, ios);
+ sdhci_runtime_pm_put(host);
+ return err;
+}
+
static int sdhci_execute_tuning(struct mmc_host *mmc)
{
struct sdhci_host *host;
@@ -1604,6 +1674,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
host = mmc_priv(mmc);
+ sdhci_runtime_pm_get(host);
disable_irq(host->irq);
spin_lock(&host->lock);
@@ -1621,6 +1692,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
else {
spin_unlock(&host->lock);
enable_irq(host->irq);
+ sdhci_runtime_pm_put(host);
return 0;
}
@@ -1646,7 +1718,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
timeout = 150;
do {
struct mmc_command cmd = {0};
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
if (!tuning_loop_counter && !timeout)
break;
@@ -1692,7 +1764,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
spin_lock(&host->lock);
if (!host->tuning_done) {
- printk(KERN_INFO DRIVER_NAME ": Timeout waiting for "
+ pr_info(DRIVER_NAME ": Timeout waiting for "
"Buffer Read Ready interrupt during tuning "
"procedure, falling back to fixed sampling "
"clock\n");
@@ -1722,7 +1794,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
} else {
if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
- printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
+ pr_info(DRIVER_NAME ": Tuning procedure"
" failed, falling back to fixed sampling"
" clock\n");
err = -EIO;
@@ -1764,18 +1836,16 @@ out:
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
spin_unlock(&host->lock);
enable_irq(host->irq);
+ sdhci_runtime_pm_put(host);
return err;
}
-static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
+static void sdhci_do_enable_preset_value(struct sdhci_host *host, bool enable)
{
- struct sdhci_host *host;
u16 ctrl;
unsigned long flags;
- host = mmc_priv(mmc);
-
/* Host Controller v3.00 defines preset value registers */
if (host->version < SDHCI_SPEC_300)
return;
@@ -1791,18 +1861,30 @@ static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ host->flags |= SDHCI_PV_ENABLED;
} else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ host->flags &= ~SDHCI_PV_ENABLED;
}
spin_unlock_irqrestore(&host->lock, flags);
}
+static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ sdhci_runtime_pm_get(host);
+ sdhci_do_enable_preset_value(host, enable);
+ sdhci_runtime_pm_put(host);
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
.get_ro = sdhci_get_ro,
+ .hw_reset = sdhci_hw_reset,
.enable_sdio_irq = sdhci_enable_sdio_irq,
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.execute_tuning = sdhci_execute_tuning,
@@ -1824,19 +1906,19 @@ static void sdhci_tasklet_card(unsigned long param)
spin_lock_irqsave(&host->lock, flags);
- if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
- if (host->mrq) {
- printk(KERN_ERR "%s: Card removed during transfer!\n",
- mmc_hostname(host->mmc));
- printk(KERN_ERR "%s: Resetting controller.\n",
- mmc_hostname(host->mmc));
+ /* Check host->mrq first in case we are runtime suspended */
+ if (host->mrq &&
+ !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+ pr_err("%s: Card removed during transfer!\n",
+ mmc_hostname(host->mmc));
+ pr_err("%s: Resetting controller.\n",
+ mmc_hostname(host->mmc));
- sdhci_reset(host, SDHCI_RESET_CMD);
- sdhci_reset(host, SDHCI_RESET_DATA);
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
- host->mrq->cmd->error = -ENOMEDIUM;
- tasklet_schedule(&host->finish_tasklet);
- }
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
}
spin_unlock_irqrestore(&host->lock, flags);
@@ -1852,20 +1934,19 @@ static void sdhci_tasklet_finish(unsigned long param)
host = (struct sdhci_host*)param;
+ spin_lock_irqsave(&host->lock, flags);
+
/*
* If this tasklet gets rescheduled while running, it will
* be run again afterwards but without any active request.
*/
- if (!host->mrq)
+ if (!host->mrq) {
+ spin_unlock_irqrestore(&host->lock, flags);
return;
-
- spin_lock_irqsave(&host->lock, flags);
+ }
del_timer(&host->timer);
- if (host->version >= SDHCI_SPEC_300)
- del_timer(&host->tuning_timer);
-
mrq = host->mrq;
/*
@@ -1906,6 +1987,7 @@ static void sdhci_tasklet_finish(unsigned long param)
spin_unlock_irqrestore(&host->lock, flags);
mmc_request_done(host->mmc, mrq);
+ sdhci_runtime_pm_put(host);
}
static void sdhci_timeout_timer(unsigned long data)
@@ -1918,7 +2000,7 @@ static void sdhci_timeout_timer(unsigned long data)
spin_lock_irqsave(&host->lock, flags);
if (host->mrq) {
- printk(KERN_ERR "%s: Timeout waiting for hardware "
+ pr_err("%s: Timeout waiting for hardware "
"interrupt.\n", mmc_hostname(host->mmc));
sdhci_dumpregs(host);
@@ -1964,7 +2046,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
BUG_ON(intmask == 0);
if (!host->cmd) {
- printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
+ pr_err("%s: Got command interrupt 0x%08x even "
"though no command operation was in progress.\n",
mmc_hostname(host->mmc), (unsigned)intmask);
sdhci_dumpregs(host);
@@ -2064,7 +2146,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
}
}
- printk(KERN_ERR "%s: Got data interrupt 0x%08x even "
+ pr_err("%s: Got data interrupt 0x%08x even "
"though no data operation was in progress.\n",
mmc_hostname(host->mmc), (unsigned)intmask);
sdhci_dumpregs(host);
@@ -2081,7 +2163,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
!= MMC_BUS_TEST_R)
host->data->error = -EILSEQ;
else if (intmask & SDHCI_INT_ADMA_ERROR) {
- printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc));
+ pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
sdhci_show_adma_error(host);
host->data->error = -EIO;
}
@@ -2137,12 +2219,19 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
static irqreturn_t sdhci_irq(int irq, void *dev_id)
{
irqreturn_t result;
- struct sdhci_host* host = dev_id;
+ struct sdhci_host *host = dev_id;
u32 intmask;
int cardint = 0;
spin_lock(&host->lock);
+ if (host->runtime_suspended) {
+ spin_unlock(&host->lock);
+ pr_warning("%s: got irq while runtime suspended\n",
+ mmc_hostname(host->mmc));
+ return IRQ_HANDLED;
+ }
+
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) {
@@ -2154,13 +2243,30 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
mmc_hostname(host->mmc), intmask);
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
+ u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT;
+
+ /*
+ * There is a observation on i.mx esdhc. INSERT bit will be
+ * immediately set again when it gets cleared, if a card is
+ * inserted. We have to mask the irq to prevent interrupt
+ * storm which will freeze the system. And the REMOVE gets
+ * the same situation.
+ *
+ * More testing are needed here to ensure it works for other
+ * platforms though.
+ */
+ sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
+ SDHCI_INT_CARD_REMOVE);
+ sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
+ SDHCI_INT_CARD_INSERT);
+
sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
- SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
+ SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
+ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
tasklet_schedule(&host->card_tasklet);
}
- intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
-
if (intmask & SDHCI_INT_CMD_MASK) {
sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
SDHCI_INT_STATUS);
@@ -2178,7 +2284,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
intmask &= ~SDHCI_INT_ERROR;
if (intmask & SDHCI_INT_BUS_POWER) {
- printk(KERN_ERR "%s: Card is consuming too much power!\n",
+ pr_err("%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc));
sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
}
@@ -2191,7 +2297,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
intmask &= ~SDHCI_INT_CARD_INT;
if (intmask) {
- printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
+ pr_err("%s: Unexpected interrupt 0x%08x.\n",
mmc_hostname(host->mmc), intmask);
sdhci_dumpregs(host);
@@ -2259,7 +2365,6 @@ int sdhci_resume_host(struct sdhci_host *host)
return ret;
}
-
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma)
host->ops->enable_dma(host);
@@ -2298,6 +2403,90 @@ EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
#endif /* CONFIG_PM */
+#ifdef CONFIG_PM_RUNTIME
+
+static int sdhci_runtime_pm_get(struct sdhci_host *host)
+{
+ return pm_runtime_get_sync(host->mmc->parent);
+}
+
+static int sdhci_runtime_pm_put(struct sdhci_host *host)
+{
+ pm_runtime_mark_last_busy(host->mmc->parent);
+ return pm_runtime_put_autosuspend(host->mmc->parent);
+}
+
+int sdhci_runtime_suspend_host(struct sdhci_host *host)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ /* Disable tuning since we are suspending */
+ if (host->version >= SDHCI_SPEC_300 &&
+ host->tuning_mode == SDHCI_TUNING_MODE_1) {
+ del_timer_sync(&host->tuning_timer);
+ host->flags &= ~SDHCI_NEEDS_RETUNING;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ synchronize_irq(host->irq);
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->runtime_suspended = true;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
+
+int sdhci_runtime_resume_host(struct sdhci_host *host)
+{
+ unsigned long flags;
+ int ret = 0, host_flags = host->flags;
+
+ if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
+ if (host->ops->enable_dma)
+ host->ops->enable_dma(host);
+ }
+
+ sdhci_init(host, 0);
+
+ /* Force clock and power re-program */
+ host->pwr = 0;
+ host->clock = 0;
+ sdhci_do_set_ios(host, &host->mmc->ios);
+
+ sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
+ if (host_flags & SDHCI_PV_ENABLED)
+ sdhci_do_enable_preset_value(host, true);
+
+ /* Set the re-tuning expiration flag */
+ if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
+ (host->tuning_mode == SDHCI_TUNING_MODE_1))
+ host->flags |= SDHCI_NEEDS_RETUNING;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ host->runtime_suspended = false;
+
+ /* Enable SDIO IRQ */
+ if ((host->flags & SDHCI_SDIO_IRQ_ENABLED))
+ sdhci_enable_sdio_irq_nolock(host, true);
+
+ /* Enable Card Detection */
+ sdhci_enable_card_detection(host);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
+
+#endif
+
/*****************************************************************************\
* *
* Device allocation/registration *
@@ -2340,6 +2529,8 @@ int sdhci_add_host(struct sdhci_host *host)
if (debug_quirks)
host->quirks = debug_quirks;
+ if (debug_quirks2)
+ host->quirks2 = debug_quirks2;
sdhci_reset(host, SDHCI_RESET_ALL);
@@ -2347,7 +2538,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->version = (host->version & SDHCI_SPEC_VER_MASK)
>> SDHCI_SPEC_VER_SHIFT;
if (host->version > SDHCI_SPEC_300) {
- printk(KERN_ERR "%s: Unknown controller version (%d). "
+ pr_err("%s: Unknown controller version (%d). "
"You may experience problems.\n", mmc_hostname(mmc),
host->version);
}
@@ -2384,7 +2575,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma) {
if (host->ops->enable_dma(host)) {
- printk(KERN_WARNING "%s: No suitable DMA "
+ pr_warning("%s: No suitable DMA "
"available. Falling back to PIO.\n",
mmc_hostname(mmc));
host->flags &=
@@ -2404,7 +2595,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (!host->adma_desc || !host->align_buffer) {
kfree(host->adma_desc);
kfree(host->align_buffer);
- printk(KERN_WARNING "%s: Unable to allocate ADMA "
+ pr_warning("%s: Unable to allocate ADMA "
"buffers. Falling back to standard DMA.\n",
mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_ADMA;
@@ -2432,30 +2623,13 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->max_clk == 0 || host->quirks &
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
if (!host->ops->get_max_clock) {
- printk(KERN_ERR
- "%s: Hardware doesn't specify base clock "
+ pr_err("%s: Hardware doesn't specify base clock "
"frequency.\n", mmc_hostname(mmc));
return -ENODEV;
}
host->max_clk = host->ops->get_max_clock(host);
}
- host->timeout_clk =
- (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
- if (host->timeout_clk == 0) {
- if (host->ops->get_timeout_clock) {
- host->timeout_clk = host->ops->get_timeout_clock(host);
- } else if (!(host->quirks &
- SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
- printk(KERN_ERR
- "%s: Hardware doesn't specify timeout clock "
- "frequency.\n", mmc_hostname(mmc));
- return -ENODEV;
- }
- }
- if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
- host->timeout_clk *= 1000;
-
/*
* In case of Host Controller v3.00, find out whether clock
* multiplier is supported.
@@ -2488,6 +2662,26 @@ int sdhci_add_host(struct sdhci_host *host)
} else
mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
+ host->timeout_clk =
+ (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
+ if (host->timeout_clk == 0) {
+ if (host->ops->get_timeout_clock) {
+ host->timeout_clk = host->ops->get_timeout_clock(host);
+ } else if (!(host->quirks &
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
+ pr_err("%s: Hardware doesn't specify timeout clock "
+ "frequency.\n", mmc_hostname(mmc));
+ return -ENODEV;
+ }
+ }
+ if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
+ host->timeout_clk *= 1000;
+
+ if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
+ host->timeout_clk = mmc->f_max / 1000;
+
+ mmc->max_discard_to = (1 << 27) / host->timeout_clk;
+
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
@@ -2545,6 +2739,15 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_DRIVER_TYPE_D)
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
+ /*
+ * If Power Off Notify capability is enabled by the host,
+ * set notify to short power off notify timeout value.
+ */
+ if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
+ mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
+ else
+ mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;
+
/* Initial value for re-tuning timer count */
host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
SDHCI_RETUNING_TIMER_COUNT_SHIFT;
@@ -2634,7 +2837,7 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->ocr_avail_mmc &= host->ocr_avail_mmc;
if (mmc->ocr_avail == 0) {
- printk(KERN_ERR "%s: Hardware doesn't report any "
+ pr_err("%s: Hardware doesn't report any "
"support voltages.\n", mmc_hostname(mmc));
return -ENODEV;
}
@@ -2682,7 +2885,7 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
SDHCI_MAX_BLOCK_SHIFT;
if (mmc->max_blk_size >= 3) {
- printk(KERN_WARNING "%s: Invalid maximum block size, "
+ pr_warning("%s: Invalid maximum block size, "
"assuming 512 bytes\n", mmc_hostname(mmc));
mmc->max_blk_size = 0;
}
@@ -2721,7 +2924,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) {
- printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc));
+ pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
host->vmmc = NULL;
} else {
regulator_enable(host->vmmc);
@@ -2750,7 +2953,7 @@ int sdhci_add_host(struct sdhci_host *host)
mmc_add_host(mmc);
- printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s\n",
+ pr_info("%s: SDHCI controller on %s [%s] using %s\n",
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
(host->flags & SDHCI_USE_ADMA) ? "ADMA" :
(host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
@@ -2783,7 +2986,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
host->flags |= SDHCI_DEVICE_DEAD;
if (host->mrq) {
- printk(KERN_ERR "%s: Controller removed during "
+ pr_err("%s: Controller removed during "
" transfer!\n", mmc_hostname(host->mmc));
host->mrq->cmd->error = -ENOMEDIUM;
@@ -2842,9 +3045,9 @@ EXPORT_SYMBOL_GPL(sdhci_free_host);
static int __init sdhci_drv_init(void)
{
- printk(KERN_INFO DRIVER_NAME
+ pr_info(DRIVER_NAME
": Secure Digital Host Controller Interface driver\n");
- printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
+ pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
return 0;
}
@@ -2857,9 +3060,11 @@ module_init(sdhci_drv_init);
module_exit(sdhci_drv_exit);
module_param(debug_quirks, uint, 0444);
+module_param(debug_quirks2, uint, 0444);
MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>");
MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");
MODULE_LICENSE("GPL");
MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
+MODULE_PARM_DESC(debug_quirks2, "Force certain other quirks.");
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 745c42fa41ed..0a5b65460d8a 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -273,7 +273,7 @@ struct sdhci_ops {
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
-
+ void (*hw_reset)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@@ -379,4 +379,9 @@ extern int sdhci_resume_host(struct sdhci_host *host);
extern void sdhci_enable_irq_wakeups(struct sdhci_host *host);
#endif
+#ifdef CONFIG_PM_RUNTIME
+extern int sdhci_runtime_suspend_host(struct sdhci_host *host);
+extern int sdhci_runtime_resume_host(struct sdhci_host *host);
+#endif
+
#endif /* __SDHCI_HW_H */
diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c
index 496b7efbc6b0..7009f17ad6cd 100644
--- a/drivers/mmc/host/sdricoh_cs.c
+++ b/drivers/mmc/host/sdricoh_cs.c
@@ -26,6 +26,7 @@
*/
#include <linux/delay.h>
#include <linux/highmem.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/scatterlist.h>
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 14f8edbaa195..369366c8e205 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -31,6 +31,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
+#include <linux/module.h>
#define DRIVER_NAME "sh_mmcif"
#define DRIVER_VERSION "2010-04-28"
@@ -165,6 +166,8 @@ struct sh_mmcif_host {
struct mmc_host *mmc;
struct mmc_data *data;
struct platform_device *pd;
+ struct sh_dmae_slave dma_slave_tx;
+ struct sh_dmae_slave dma_slave_rx;
struct clk *hclk;
unsigned int clk;
int bus_width;
@@ -175,6 +178,7 @@ struct sh_mmcif_host {
enum mmcif_state state;
spinlock_t lock;
bool power;
+ bool card_present;
/* DMA support */
struct dma_chan *chan_rx;
@@ -322,25 +326,35 @@ static bool sh_mmcif_filter(struct dma_chan *chan, void *arg)
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
struct sh_mmcif_plat_data *pdata)
{
+ struct sh_dmae_slave *tx, *rx;
host->dma_active = false;
/* We can only either use DMA for both Tx and Rx or not use it at all */
if (pdata->dma) {
+ dev_warn(&host->pd->dev,
+ "Update your platform to use embedded DMA slave IDs\n");
+ tx = &pdata->dma->chan_priv_tx;
+ rx = &pdata->dma->chan_priv_rx;
+ } else {
+ tx = &host->dma_slave_tx;
+ tx->slave_id = pdata->slave_id_tx;
+ rx = &host->dma_slave_rx;
+ rx->slave_id = pdata->slave_id_rx;
+ }
+ if (tx->slave_id > 0 && rx->slave_id > 0) {
dma_cap_mask_t mask;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
- host->chan_tx = dma_request_channel(mask, sh_mmcif_filter,
- &pdata->dma->chan_priv_tx);
+ host->chan_tx = dma_request_channel(mask, sh_mmcif_filter, tx);
dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__,
host->chan_tx);
if (!host->chan_tx)
return;
- host->chan_rx = dma_request_channel(mask, sh_mmcif_filter,
- &pdata->dma->chan_priv_rx);
+ host->chan_rx = dma_request_channel(mask, sh_mmcif_filter, rx);
dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__,
host->chan_rx);
@@ -877,23 +891,23 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_unlock_irqrestore(&host->lock, flags);
if (ios->power_mode == MMC_POWER_UP) {
- if (p->set_pwr)
- p->set_pwr(host->pd, ios->power_mode);
- if (!host->power) {
+ if (!host->card_present) {
/* See if we also get DMA */
sh_mmcif_request_dma(host, host->pd->dev.platform_data);
- pm_runtime_get_sync(&host->pd->dev);
- host->power = true;
+ host->card_present = true;
}
} else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
/* clock stop */
sh_mmcif_clock_control(host, 0);
if (ios->power_mode == MMC_POWER_OFF) {
- if (host->power) {
- pm_runtime_put(&host->pd->dev);
+ if (host->card_present) {
sh_mmcif_release_dma(host);
- host->power = false;
+ host->card_present = false;
}
+ }
+ if (host->power) {
+ pm_runtime_put(&host->pd->dev);
+ host->power = false;
if (p->down_pwr)
p->down_pwr(host->pd);
}
@@ -901,8 +915,16 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
return;
}
- if (ios->clock)
+ if (ios->clock) {
+ if (!host->power) {
+ if (p->set_pwr)
+ p->set_pwr(host->pd, ios->power_mode);
+ pm_runtime_get_sync(&host->pd->dev);
+ host->power = true;
+ sh_mmcif_sync_reset(host);
+ }
sh_mmcif_clock_control(host, ios->clock);
+ }
host->bus_width = ios->bus_width;
host->state = STATE_IDLE;
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index ce500f03df85..41ae6466bd83 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -21,11 +21,13 @@
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sh_mobile_sdhi.h>
#include <linux/mfd/tmio.h>
#include <linux/sh_dma.h>
+#include <linux/delay.h>
#include "tmio_mmc.h"
@@ -55,6 +57,39 @@ static int sh_mobile_sdhi_get_cd(struct platform_device *pdev)
return -ENOSYS;
}
+static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
+{
+ int timeout = 1000;
+
+ while (--timeout && !(sd_ctrl_read16(host, CTL_STATUS2) & (1 << 13)))
+ udelay(1);
+
+ if (!timeout) {
+ dev_warn(host->pdata->dev, "timeout waiting for SD bus idle\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
+{
+ switch (addr)
+ {
+ case CTL_SD_CMD:
+ case CTL_STOP_INTERNAL_ACTION:
+ case CTL_XFER_BLK_COUNT:
+ case CTL_SD_CARD_CLK_CTL:
+ case CTL_SD_XFER_LEN:
+ case CTL_SD_MEM_CARD_OPT:
+ case CTL_TRANSACTION_CTL:
+ case CTL_DMA_ENABLE:
+ return sh_mobile_sdhi_wait_idle(host);
+ }
+
+ return 0;
+}
+
static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
{
struct sh_mobile_sdhi *priv;
@@ -62,7 +97,8 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
struct tmio_mmc_host *host;
char clk_name[8];
- int i, irq, ret;
+ int irq, ret, i = 0;
+ bool multiplexed_isr = true;
priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
if (priv == NULL) {
@@ -89,6 +125,8 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
if (p) {
mmc_data->flags = p->tmio_flags;
+ if (mmc_data->flags & TMIO_MMC_HAS_IDLE_WAIT)
+ mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
mmc_data->ocr_mask = p->tmio_ocr_mask;
mmc_data->capabilities |= p->tmio_caps;
@@ -117,27 +155,60 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
if (ret < 0)
goto eprobe;
- for (i = 0; i < 3; i++) {
- irq = platform_get_irq(pdev, i);
- if (irq < 0) {
- if (i) {
- continue;
- } else {
- ret = irq;
- goto eirq;
- }
- }
- ret = request_irq(irq, tmio_mmc_irq, 0,
+ /*
+ * Allow one or more specific (named) ISRs or
+ * one or more multiplexed (un-named) ISRs.
+ */
+
+ irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
+ if (irq >= 0) {
+ multiplexed_isr = false;
+ ret = request_irq(irq, tmio_mmc_card_detect_irq, 0,
+ dev_name(&pdev->dev), host);
+ if (ret)
+ goto eirq_card_detect;
+ }
+
+ irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
+ if (irq >= 0) {
+ multiplexed_isr = false;
+ ret = request_irq(irq, tmio_mmc_sdio_irq, 0,
+ dev_name(&pdev->dev), host);
+ if (ret)
+ goto eirq_sdio;
+ }
+
+ irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDCARD);
+ if (irq >= 0) {
+ multiplexed_isr = false;
+ ret = request_irq(irq, tmio_mmc_sdcard_irq, 0,
dev_name(&pdev->dev), host);
- if (ret) {
- while (i--) {
- irq = platform_get_irq(pdev, i);
- if (irq >= 0)
- free_irq(irq, host);
- }
- goto eirq;
+ if (ret)
+ goto eirq_sdcard;
+ } else if (!multiplexed_isr) {
+ dev_err(&pdev->dev,
+ "Principal SD-card IRQ is missing among named interrupts\n");
+ ret = irq;
+ goto eirq_sdcard;
+ }
+
+ if (multiplexed_isr) {
+ while (1) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ break;
+ i++;
+ ret = request_irq(irq, tmio_mmc_irq, 0,
+ dev_name(&pdev->dev), host);
+ if (ret)
+ goto eirq_multiplexed;
}
+
+ /* There must be at least one IRQ source */
+ if (!i)
+ goto eirq_multiplexed;
}
+
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
mmc_hostname(host->mmc), (unsigned long)
(platform_get_resource(pdev,IORESOURCE_MEM, 0)->start),
@@ -145,7 +216,20 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
return ret;
-eirq:
+eirq_multiplexed:
+ while (i--) {
+ irq = platform_get_irq(pdev, i);
+ free_irq(irq, host);
+ }
+eirq_sdcard:
+ irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
+ if (irq >= 0)
+ free_irq(irq, host);
+eirq_sdio:
+ irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
+ if (irq >= 0)
+ free_irq(irq, host);
+eirq_card_detect:
tmio_mmc_host_remove(host);
eprobe:
clk_disable(priv->clk);
@@ -161,16 +245,17 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
struct tmio_mmc_host *host = mmc_priv(mmc);
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
- int i, irq;
+ int i = 0, irq;
p->pdata = NULL;
tmio_mmc_host_remove(host);
- for (i = 0; i < 3; i++) {
- irq = platform_get_irq(pdev, i);
- if (irq >= 0)
- free_irq(irq, host);
+ while (1) {
+ irq = platform_get_irq(pdev, i++);
+ if (irq < 0)
+ break;
+ free_irq(irq, host);
}
clk_disable(priv->clk);
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index 457c26ea09de..f70d04664cac 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -16,6 +16,7 @@
#include <linux/mmc/host.h>
#include <linux/highmem.h>
#include <linux/scatterlist.h>
+#include <linux/module.h>
#include <asm/io.h>
#define DRIVER_NAME "tifm_sd"
@@ -631,7 +632,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
}
if (host->req) {
- printk(KERN_ERR "%s : unfinished request detected\n",
+ pr_err("%s : unfinished request detected\n",
dev_name(&sock->dev));
mrq->cmd->error = -ETIMEDOUT;
goto err_out;
@@ -671,7 +672,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
r_data->flags & MMC_DATA_WRITE
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE)) {
- printk(KERN_ERR "%s : scatterlist map failed\n",
+ pr_err("%s : scatterlist map failed\n",
dev_name(&sock->dev));
mrq->cmd->error = -ENOMEM;
goto err_out;
@@ -683,7 +684,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE);
if (host->sg_len < 1) {
- printk(KERN_ERR "%s : scatterlist map failed\n",
+ pr_err("%s : scatterlist map failed\n",
dev_name(&sock->dev));
tifm_unmap_sg(sock, &host->bounce_buf, 1,
r_data->flags & MMC_DATA_WRITE
@@ -747,7 +748,7 @@ static void tifm_sd_end_cmd(unsigned long data)
host->req = NULL;
if (!mrq) {
- printk(KERN_ERR " %s : no request to complete?\n",
+ pr_err(" %s : no request to complete?\n",
dev_name(&sock->dev));
spin_unlock_irqrestore(&sock->lock, flags);
return;
@@ -786,8 +787,7 @@ static void tifm_sd_abort(unsigned long data)
{
struct tifm_sd *host = (struct tifm_sd*)data;
- printk(KERN_ERR
- "%s : card failed to respond for a long period of time "
+ pr_err("%s : card failed to respond for a long period of time "
"(%x, %x)\n",
dev_name(&host->dev->dev), host->req->cmd->opcode, host->cmd_flags);
@@ -905,7 +905,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host)
}
if (rc) {
- printk(KERN_ERR "%s : controller failed to reset\n",
+ pr_err("%s : controller failed to reset\n",
dev_name(&sock->dev));
return -ENODEV;
}
@@ -931,8 +931,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host)
}
if (rc) {
- printk(KERN_ERR
- "%s : card not ready - probe failed on initialization\n",
+ pr_err("%s : card not ready - probe failed on initialization\n",
dev_name(&sock->dev));
return -ENODEV;
}
@@ -953,7 +952,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
if (!(TIFM_SOCK_STATE_OCCUPIED
& readl(sock->addr + SOCK_PRESENT_STATE))) {
- printk(KERN_WARNING "%s : card gone, unexpectedly\n",
+ pr_warning("%s : card gone, unexpectedly\n",
dev_name(&sock->dev));
return rc;
}
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 8d185de90d20..a4ea10242787 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -27,7 +27,6 @@
static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
{
const struct mfd_cell *cell = mfd_get_cell(dev);
- struct mmc_host *mmc = platform_get_drvdata(dev);
int ret;
ret = tmio_mmc_host_suspend(&dev->dev);
@@ -42,7 +41,6 @@ static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
static int tmio_mmc_resume(struct platform_device *dev)
{
const struct mfd_cell *cell = mfd_get_cell(dev);
- struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
/* Tell the MFD core we are ready to be enabled */
@@ -90,8 +88,8 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev)
if (ret)
goto cell_disable;
- ret = request_irq(irq, tmio_mmc_irq, IRQF_DISABLED |
- IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), host);
+ ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING,
+ dev_name(&pdev->dev), host);
if (ret)
goto host_remove;
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 8260bc2c34e3..3020f98218f0 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -18,8 +18,10 @@
#include <linux/highmem.h>
#include <linux/mmc/tmio.h>
+#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/spinlock.h>
+#include <linux/scatterlist.h>
/* Definitions for values the CTRL_SDIO_STATUS register can take. */
#define TMIO_SDIO_STAT_IOIRQ 0x0001
@@ -52,6 +54,8 @@ struct tmio_mmc_host {
void (*set_clk_div)(struct platform_device *host, int state);
int pm_error;
+ /* recognise system-wide suspend in runtime PM methods */
+ bool pm_global;
/* pio related stuff */
struct scatterlist *sg_ptr;
@@ -73,8 +77,15 @@ struct tmio_mmc_host {
/* Track lost interrupts */
struct delayed_work delayed_reset_work;
- spinlock_t lock;
+ struct work_struct done;
+
+ /* Cache IRQ mask */
+ u32 sdcard_irq_mask;
+ u32 sdio_irq_mask;
+
+ spinlock_t lock; /* protect host private data */
unsigned long last_req_ts;
+ struct mutex ios_lock; /* protect set_ios() context */
};
int tmio_mmc_host_probe(struct tmio_mmc_host **host,
@@ -86,6 +97,9 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host);
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
irqreturn_t tmio_mmc_irq(int irq, void *devid);
+irqreturn_t tmio_mmc_sdcard_irq(int irq, void *devid);
+irqreturn_t tmio_mmc_card_detect_irq(int irq, void *devid);
+irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid);
static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg,
unsigned long *flags)
@@ -103,6 +117,7 @@ static inline void tmio_mmc_kunmap_atomic(struct scatterlist *sg,
#if defined(CONFIG_MMC_SDHI) || defined(CONFIG_MMC_SDHI_MODULE)
void tmio_mmc_start_dma(struct tmio_mmc_host *host, struct mmc_data *data);
+void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable);
void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata);
void tmio_mmc_release_dma(struct tmio_mmc_host *host);
#else
@@ -111,6 +126,10 @@ static inline void tmio_mmc_start_dma(struct tmio_mmc_host *host,
{
}
+static inline void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
+{
+}
+
static inline void tmio_mmc_request_dma(struct tmio_mmc_host *host,
struct tmio_mmc_data *pdata)
{
@@ -134,4 +153,44 @@ int tmio_mmc_host_resume(struct device *dev);
int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev);
+static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
+{
+ return readw(host->ctl + (addr << host->bus_shift));
+}
+
+static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
+ u16 *buf, int count)
+{
+ readsw(host->ctl + (addr << host->bus_shift), buf, count);
+}
+
+static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
+{
+ return readw(host->ctl + (addr << host->bus_shift)) |
+ readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
+}
+
+static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
+{
+ /* If there is a hook and it returns non-zero then there
+ * is an error and the write should be skipped
+ */
+ if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr))
+ return;
+ writew(val, host->ctl + (addr << host->bus_shift));
+}
+
+static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
+ u16 *buf, int count)
+{
+ writesw(host->ctl + (addr << host->bus_shift), buf, count);
+}
+
+static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val)
+{
+ writew(val, host->ctl + (addr << host->bus_shift));
+ writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
+}
+
+
#endif
diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c
index 25f1ad6cbe09..86f259cdfcbc 100644
--- a/drivers/mmc/host/tmio_mmc_dma.c
+++ b/drivers/mmc/host/tmio_mmc_dma.c
@@ -11,6 +11,7 @@
*/
#include <linux/device.h>
+#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/mfd/tmio.h>
#include <linux/mmc/host.h>
@@ -22,11 +23,14 @@
#define TMIO_MMC_MIN_DMA_LEN 8
-static void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
+void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
{
+ if (!host->chan_tx || !host->chan_rx)
+ return;
+
#if defined(CONFIG_SUPERH) || defined(CONFIG_ARCH_SHMOBILE)
/* Switch DMA mode on or off - SuperH specific? */
- writew(enable ? 2 : 0, host->ctl + (0xd8 << host->bus_shift));
+ sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
#endif
}
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 0b09e8239aa0..d85a60cda167 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -46,50 +46,16 @@
#include "tmio_mmc.h"
-static u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
-{
- return readw(host->ctl + (addr << host->bus_shift));
-}
-
-static void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
- u16 *buf, int count)
-{
- readsw(host->ctl + (addr << host->bus_shift), buf, count);
-}
-
-static u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
-{
- return readw(host->ctl + (addr << host->bus_shift)) |
- readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
-}
-
-static void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
-{
- writew(val, host->ctl + (addr << host->bus_shift));
-}
-
-static void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
- u16 *buf, int count)
-{
- writesw(host->ctl + (addr << host->bus_shift), buf, count);
-}
-
-static void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val)
-{
- writew(val, host->ctl + (addr << host->bus_shift));
- writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
-}
-
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i)
{
- u32 mask = sd_ctrl_read32(host, CTL_IRQ_MASK) & ~(i & TMIO_MASK_IRQ);
- sd_ctrl_write32(host, CTL_IRQ_MASK, mask);
+ host->sdcard_irq_mask &= ~(i & TMIO_MASK_IRQ);
+ sd_ctrl_write32(host, CTL_IRQ_MASK, host->sdcard_irq_mask);
}
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i)
{
- u32 mask = sd_ctrl_read32(host, CTL_IRQ_MASK) | (i & TMIO_MASK_IRQ);
- sd_ctrl_write32(host, CTL_IRQ_MASK, mask);
+ host->sdcard_irq_mask |= (i & TMIO_MASK_IRQ);
+ sd_ctrl_write32(host, CTL_IRQ_MASK, host->sdcard_irq_mask);
}
static void tmio_mmc_ack_mmc_irqs(struct tmio_mmc_host *host, u32 i)
@@ -126,7 +92,7 @@ static int tmio_mmc_next_sg(struct tmio_mmc_host *host)
static void pr_debug_status(u32 status)
{
int i = 0;
- printk(KERN_DEBUG "status: %08x = ", status);
+ pr_debug("status: %08x = ", status);
STATUS_TO_TEXT(CARD_REMOVE, status, i);
STATUS_TO_TEXT(CARD_INSERT, status, i);
STATUS_TO_TEXT(SIGSTATE, status, i);
@@ -161,11 +127,13 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
if (enable) {
host->sdio_irq_enabled = 1;
+ host->sdio_irq_mask = TMIO_SDIO_MASK_ALL &
+ ~TMIO_SDIO_STAT_IOIRQ;
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
- sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK,
- (TMIO_SDIO_MASK_ALL & ~TMIO_SDIO_STAT_IOIRQ));
+ sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
} else {
- sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, TMIO_SDIO_MASK_ALL);
+ host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
+ sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
host->sdio_irq_enabled = 0;
}
@@ -284,10 +252,16 @@ static void tmio_mmc_reset_work(struct work_struct *work)
/* called with host->lock held, interrupts disabled */
static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
{
- struct mmc_request *mrq = host->mrq;
+ struct mmc_request *mrq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
- if (!mrq)
+ mrq = host->mrq;
+ if (IS_ERR_OR_NULL(mrq)) {
+ spin_unlock_irqrestore(&host->lock, flags);
return;
+ }
host->cmd = NULL;
host->data = NULL;
@@ -296,11 +270,18 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
cancel_delayed_work(&host->delayed_reset_work);
host->mrq = NULL;
+ spin_unlock_irqrestore(&host->lock, flags);
- /* FIXME: mmc_request_done() can schedule! */
mmc_request_done(host->mmc, mrq);
}
+static void tmio_mmc_done_work(struct work_struct *work)
+{
+ struct tmio_mmc_host *host = container_of(work, struct tmio_mmc_host,
+ done);
+ tmio_mmc_finish_request(host);
+}
+
/* These are the bitmasks the tmio chip requires to implement the MMC response
* types. Note that R1 and R6 are the same in this scheme. */
#define APP_CMD 0x0040
@@ -467,7 +448,7 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host)
BUG();
}
- tmio_mmc_finish_request(host);
+ schedule_work(&host->done);
}
static void tmio_mmc_data_irq(struct tmio_mmc_host *host)
@@ -557,106 +538,132 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
tasklet_schedule(&host->dma_issue);
}
} else {
- tmio_mmc_finish_request(host);
+ schedule_work(&host->done);
}
out:
spin_unlock(&host->lock);
}
-irqreturn_t tmio_mmc_irq(int irq, void *devid)
+static void tmio_mmc_card_irq_status(struct tmio_mmc_host *host,
+ int *ireg, int *status)
{
- struct tmio_mmc_host *host = devid;
- struct tmio_mmc_data *pdata = host->pdata;
- unsigned int ireg, irq_mask, status;
- unsigned int sdio_ireg, sdio_irq_mask, sdio_status;
+ *status = sd_ctrl_read32(host, CTL_STATUS);
+ *ireg = *status & TMIO_MASK_IRQ & ~host->sdcard_irq_mask;
- pr_debug("MMC IRQ begin\n");
+ pr_debug_status(*status);
+ pr_debug_status(*ireg);
+}
- status = sd_ctrl_read32(host, CTL_STATUS);
- irq_mask = sd_ctrl_read32(host, CTL_IRQ_MASK);
- ireg = status & TMIO_MASK_IRQ & ~irq_mask;
+static bool __tmio_mmc_card_detect_irq(struct tmio_mmc_host *host,
+ int ireg, int status)
+{
+ struct mmc_host *mmc = host->mmc;
+
+ /* Card insert / remove attempts */
+ if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) {
+ tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_CARD_INSERT |
+ TMIO_STAT_CARD_REMOVE);
+ if ((((ireg & TMIO_STAT_CARD_REMOVE) && mmc->card) ||
+ ((ireg & TMIO_STAT_CARD_INSERT) && !mmc->card)) &&
+ !work_pending(&mmc->detect.work))
+ mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+ return true;
+ }
- sdio_ireg = 0;
- if (!ireg && pdata->flags & TMIO_MMC_SDIO_IRQ) {
- sdio_status = sd_ctrl_read16(host, CTL_SDIO_STATUS);
- sdio_irq_mask = sd_ctrl_read16(host, CTL_SDIO_IRQ_MASK);
- sdio_ireg = sdio_status & TMIO_SDIO_MASK_ALL & ~sdio_irq_mask;
+ return false;
+}
- sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status & ~TMIO_SDIO_MASK_ALL);
+irqreturn_t tmio_mmc_card_detect_irq(int irq, void *devid)
+{
+ unsigned int ireg, status;
+ struct tmio_mmc_host *host = devid;
- if (sdio_ireg && !host->sdio_irq_enabled) {
- pr_warning("tmio_mmc: Spurious SDIO IRQ, disabling! 0x%04x 0x%04x 0x%04x\n",
- sdio_status, sdio_irq_mask, sdio_ireg);
- tmio_mmc_enable_sdio_irq(host->mmc, 0);
- goto out;
- }
+ tmio_mmc_card_irq_status(host, &ireg, &status);
+ __tmio_mmc_card_detect_irq(host, ireg, status);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(tmio_mmc_card_detect_irq);
- if (host->mmc->caps & MMC_CAP_SDIO_IRQ &&
- sdio_ireg & TMIO_SDIO_STAT_IOIRQ)
- mmc_signal_sdio_irq(host->mmc);
+static bool __tmio_mmc_sdcard_irq(struct tmio_mmc_host *host,
+ int ireg, int status)
+{
+ /* Command completion */
+ if (ireg & (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT)) {
+ tmio_mmc_ack_mmc_irqs(host,
+ TMIO_STAT_CMDRESPEND |
+ TMIO_STAT_CMDTIMEOUT);
+ tmio_mmc_cmd_irq(host, status);
+ return true;
+ }
- if (sdio_ireg)
- goto out;
+ /* Data transfer */
+ if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) {
+ tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ);
+ tmio_mmc_pio_irq(host);
+ return true;
}
- pr_debug_status(status);
- pr_debug_status(ireg);
+ /* Data transfer completion */
+ if (ireg & TMIO_STAT_DATAEND) {
+ tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_DATAEND);
+ tmio_mmc_data_irq(host);
+ return true;
+ }
- if (!ireg) {
- tmio_mmc_disable_mmc_irqs(host, status & ~irq_mask);
+ return false;
+}
- pr_warning("tmio_mmc: Spurious irq, disabling! "
- "0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg);
- pr_debug_status(status);
+irqreturn_t tmio_mmc_sdcard_irq(int irq, void *devid)
+{
+ unsigned int ireg, status;
+ struct tmio_mmc_host *host = devid;
- goto out;
- }
+ tmio_mmc_card_irq_status(host, &ireg, &status);
+ __tmio_mmc_sdcard_irq(host, ireg, status);
- while (ireg) {
- /* Card insert / remove attempts */
- if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) {
- tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_CARD_INSERT |
- TMIO_STAT_CARD_REMOVE);
- mmc_detect_change(host->mmc, msecs_to_jiffies(100));
- }
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(tmio_mmc_sdcard_irq);
- /* CRC and other errors */
-/* if (ireg & TMIO_STAT_ERR_IRQ)
- * handled |= tmio_error_irq(host, irq, stat);
- */
+irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid)
+{
+ struct tmio_mmc_host *host = devid;
+ struct mmc_host *mmc = host->mmc;
+ struct tmio_mmc_data *pdata = host->pdata;
+ unsigned int ireg, status;
- /* Command completion */
- if (ireg & (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT)) {
- tmio_mmc_ack_mmc_irqs(host,
- TMIO_STAT_CMDRESPEND |
- TMIO_STAT_CMDTIMEOUT);
- tmio_mmc_cmd_irq(host, status);
- }
+ if (!(pdata->flags & TMIO_MMC_SDIO_IRQ))
+ return IRQ_HANDLED;
- /* Data transfer */
- if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) {
- tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ);
- tmio_mmc_pio_irq(host);
- }
+ status = sd_ctrl_read16(host, CTL_SDIO_STATUS);
+ ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdcard_irq_mask;
- /* Data transfer completion */
- if (ireg & TMIO_STAT_DATAEND) {
- tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_DATAEND);
- tmio_mmc_data_irq(host);
- }
+ sd_ctrl_write16(host, CTL_SDIO_STATUS, status & ~TMIO_SDIO_MASK_ALL);
- /* Check status - keep going until we've handled it all */
- status = sd_ctrl_read32(host, CTL_STATUS);
- irq_mask = sd_ctrl_read32(host, CTL_IRQ_MASK);
- ireg = status & TMIO_MASK_IRQ & ~irq_mask;
+ if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ)
+ mmc_signal_sdio_irq(mmc);
- pr_debug("Status at end of loop: %08x\n", status);
- pr_debug_status(status);
- }
- pr_debug("MMC IRQ end\n");
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(tmio_mmc_sdio_irq);
+
+irqreturn_t tmio_mmc_irq(int irq, void *devid)
+{
+ struct tmio_mmc_host *host = devid;
+ unsigned int ireg, status;
+
+ pr_debug("MMC IRQ begin\n");
+
+ tmio_mmc_card_irq_status(host, &ireg, &status);
+ if (__tmio_mmc_card_detect_irq(host, ireg, status))
+ return IRQ_HANDLED;
+ if (__tmio_mmc_sdcard_irq(host, ireg, status))
+ return IRQ_HANDLED;
+
+ tmio_mmc_sdio_irq(irq, devid);
-out:
return IRQ_HANDLED;
}
EXPORT_SYMBOL(tmio_mmc_irq);
@@ -749,6 +756,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
struct tmio_mmc_data *pdata = host->pdata;
unsigned long flags;
+ mutex_lock(&host->ios_lock);
+
spin_lock_irqsave(&host->lock, flags);
if (host->mrq) {
if (IS_ERR(host->mrq)) {
@@ -764,6 +773,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
host->mrq->cmd->opcode, host->last_req_ts, jiffies);
}
spin_unlock_irqrestore(&host->lock, flags);
+
+ mutex_unlock(&host->ios_lock);
return;
}
@@ -771,33 +782,30 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_unlock_irqrestore(&host->lock, flags);
- if (ios->clock)
- tmio_mmc_set_clock(host, ios->clock);
-
- /* Power sequence - OFF -> UP -> ON */
- if (ios->power_mode == MMC_POWER_UP) {
- if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) && !pdata->power) {
+ /*
+ * pdata->power == false only if COLD_CD is available, otherwise only
+ * in short time intervals during probing or resuming
+ */
+ if (ios->power_mode == MMC_POWER_ON && ios->clock) {
+ if (!pdata->power) {
pm_runtime_get_sync(&host->pdev->dev);
pdata->power = true;
}
+ tmio_mmc_set_clock(host, ios->clock);
/* power up SD bus */
if (host->set_pwr)
host->set_pwr(host->pdev, 1);
- } else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
- /* power down SD bus */
- if (ios->power_mode == MMC_POWER_OFF) {
- if (host->set_pwr)
- host->set_pwr(host->pdev, 0);
- if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) &&
- pdata->power) {
- pdata->power = false;
- pm_runtime_put(&host->pdev->dev);
- }
- }
- tmio_mmc_clk_stop(host);
- } else {
/* start bus clock */
tmio_mmc_clk_start(host);
+ } else if (ios->power_mode != MMC_POWER_UP) {
+ if (host->set_pwr)
+ host->set_pwr(host->pdev, 0);
+ if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) &&
+ pdata->power) {
+ pdata->power = false;
+ pm_runtime_put(&host->pdev->dev);
+ }
+ tmio_mmc_clk_stop(host);
}
switch (ios->bus_width) {
@@ -817,6 +825,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
current->comm, task_pid_nr(current),
ios->clock, ios->power_mode);
host->mrq = NULL;
+
+ mutex_unlock(&host->ios_lock);
}
static int tmio_mmc_get_ro(struct mmc_host *mmc)
@@ -908,21 +918,26 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
tmio_mmc_clk_stop(_host);
tmio_mmc_reset(_host);
+ _host->sdcard_irq_mask = sd_ctrl_read32(_host, CTL_IRQ_MASK);
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
if (pdata->flags & TMIO_MMC_SDIO_IRQ)
tmio_mmc_enable_sdio_irq(mmc, 0);
spin_lock_init(&_host->lock);
+ mutex_init(&_host->ios_lock);
/* Init delayed work for request timeouts */
INIT_DELAYED_WORK(&_host->delayed_reset_work, tmio_mmc_reset_work);
+ INIT_WORK(&_host->done, tmio_mmc_done_work);
/* See if we also get DMA */
tmio_mmc_request_dma(_host, pdata);
/* We have to keep the device powered for its card detection to work */
- if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD))
+ if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD)) {
+ pdata->power = true;
pm_runtime_get_noresume(&pdev->dev);
+ }
mmc_add_host(mmc);
@@ -963,6 +978,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
pm_runtime_get_sync(&pdev->dev);
mmc_remove_host(host->mmc);
+ cancel_work_sync(&host->done);
cancel_delayed_work_sync(&host->delayed_reset_work);
tmio_mmc_release_dma(host);
@@ -998,11 +1014,16 @@ int tmio_mmc_host_resume(struct device *dev)
/* The MMC core will perform the complete set up */
host->pdata->power = false;
+ host->pm_global = true;
if (!host->pm_error)
pm_runtime_get_sync(dev);
- tmio_mmc_reset(mmc_priv(mmc));
- tmio_mmc_request_dma(host, host->pdata);
+ if (host->pm_global) {
+ /* Runtime PM resume callback didn't run */
+ tmio_mmc_reset(host);
+ tmio_mmc_enable_dma(host, true);
+ host->pm_global = false;
+ }
return mmc_resume_host(mmc);
}
@@ -1023,12 +1044,15 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
struct tmio_mmc_data *pdata = host->pdata;
tmio_mmc_reset(host);
+ tmio_mmc_enable_dma(host, true);
if (pdata->power) {
/* Only entered after a card-insert interrupt */
- tmio_mmc_set_ios(mmc, &mmc->ios);
+ if (!mmc->card)
+ tmio_mmc_set_ios(mmc, &mmc->ios);
mmc_detect_change(mmc, msecs_to_jiffies(100));
}
+ host->pm_global = false;
return 0;
}
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index 4dfe2c02ea91..4b83c43f950d 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -9,6 +9,7 @@
*/
#include <linux/pci.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/highmem.h>
#include <linux/delay.h>
@@ -1191,7 +1192,7 @@ static void __devexit via_sd_remove(struct pci_dev *pcidev)
mmiowb();
if (sdhost->mrq) {
- printk(KERN_ERR "%s: Controller removed during "
+ pr_err("%s: Controller removed during "
"transfer\n", mmc_hostname(sdhost->mmc));
/* make sure all DMA is stopped */
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index d4455ffbefd8..e8f6e65183d7 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -1625,8 +1625,8 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300,
cmd->error = respretval;
} else if (cmd->error) {
/*
- * the error occured sending the command
- * or recieving the response
+ * the error occurred sending the command
+ * or receiving the response
*/
} else if (vub300->command_out_urb->status) {
vub300->usb_transport_fail = vub300->command_out_urb->status;
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index 62e5a4d171e1..64acd9ce141c 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -194,7 +194,7 @@ static void wbsd_reset(struct wbsd_host *host)
{
u8 setup;
- printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc));
+ pr_err("%s: Resetting chip\n", mmc_hostname(host->mmc));
/*
* Soft reset of chip (SD/MMC part).
@@ -721,7 +721,7 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
* Any leftover data?
*/
if (count) {
- printk(KERN_ERR "%s: Incomplete DMA transfer. "
+ pr_err("%s: Incomplete DMA transfer. "
"%d bytes left.\n",
mmc_hostname(host->mmc), count);
@@ -803,7 +803,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
default:
#ifdef CONFIG_MMC_DEBUG
- printk(KERN_WARNING "%s: Data command %d is not "
+ pr_warning("%s: Data command %d is not "
"supported by this controller.\n",
mmc_hostname(host->mmc), cmd->opcode);
#endif
@@ -1029,7 +1029,7 @@ static void wbsd_tasklet_card(unsigned long param)
host->flags &= ~WBSD_FCARD_PRESENT;
if (host->mrq) {
- printk(KERN_ERR "%s: Card removed during transfer!\n",
+ pr_err("%s: Card removed during transfer!\n",
mmc_hostname(host->mmc));
wbsd_reset(host);
@@ -1429,7 +1429,7 @@ free:
free_dma(dma);
err:
- printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. "
+ pr_warning(DRIVER_NAME ": Unable to allocate DMA %d. "
"Falling back on FIFO.\n", dma);
}
@@ -1664,7 +1664,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
ret = wbsd_scan(host);
if (ret) {
if (pnp && (ret == -ENODEV)) {
- printk(KERN_WARNING DRIVER_NAME
+ pr_warning(DRIVER_NAME
": Unable to confirm device presence. You may "
"experience lock-ups.\n");
} else {
@@ -1688,7 +1688,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
*/
if (pnp) {
if ((host->config != 0) && !wbsd_chip_validate(host)) {
- printk(KERN_WARNING DRIVER_NAME
+ pr_warning(DRIVER_NAME
": PnP active but chip not configured! "
"You probably have a buggy BIOS. "
"Configuring chip manually.\n");
@@ -1720,7 +1720,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
mmc_add_host(mmc);
- printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
+ pr_info("%s: W83L51xD", mmc_hostname(mmc));
if (host->chip_id != 0)
printk(" id %x", (int)host->chip_id);
printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
@@ -1909,7 +1909,7 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
*/
if (host->config != 0) {
if (!wbsd_chip_validate(host)) {
- printk(KERN_WARNING DRIVER_NAME
+ pr_warning(DRIVER_NAME
": PnP active but chip not configured! "
"You probably have a buggy BIOS. "
"Configuring chip manually.\n");
@@ -1973,9 +1973,9 @@ static int __init wbsd_drv_init(void)
{
int result;
- printk(KERN_INFO DRIVER_NAME
+ pr_info(DRIVER_NAME
": Winbond W83L51xD SD/MMC card interface driver\n");
- printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
+ pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
#ifdef CONFIG_PNP
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 4be8373d43e5..318a869286ab 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -12,27 +12,17 @@ menuconfig MTD
if MTD
-config MTD_DEBUG
- bool "Debugging"
- help
- This turns on low-level debugging for the entire MTD sub-system.
- Normally, you should say 'N'.
-
-config MTD_DEBUG_VERBOSE
- int "Debugging verbosity (0 = quiet, 3 = noisy)"
- depends on MTD_DEBUG
- default "0"
- help
- Determines the verbosity level of the MTD debugging messages.
-
config MTD_TESTS
- tristate "MTD tests support"
+ tristate "MTD tests support (DANGEROUS)"
depends on m
help
This option includes various MTD tests into compilation. The tests
should normally be compiled as kernel modules. The modules perform
various checks and verifications when loaded.
+ WARNING: some of the tests will ERASE entire MTD device which they
+ test. Do not use these tests unless you really know what you do.
+
config MTD_REDBOOT_PARTS
tristate "RedBoot partition table parsing"
---help---
@@ -137,12 +127,13 @@ config MTD_AFS_PARTS
'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example.
config MTD_OF_PARTS
- def_bool y
+ tristate "OpenFirmware partitioning information support"
+ default Y
depends on OF
help
This provides a partition parsing function which derives
the partition map from the children of the flash node,
- as described in Documentation/powerpc/booting-without-of.txt.
+ as described in Documentation/devicetree/booting-without-of.txt.
config MTD_AR7_PARTS
tristate "TI AR7 partitioning support"
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 39664c4229ff..9aaac3ac89f3 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -5,8 +5,8 @@
# Core functionality.
obj-$(CONFIG_MTD) += mtd.o
mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o
-mtd-$(CONFIG_MTD_OF_PARTS) += ofpart.o
+obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c
index 302372c08b56..89a02f6f65dc 100644
--- a/drivers/mtd/afs.c
+++ b/drivers/mtd/afs.c
@@ -162,8 +162,8 @@ afs_read_iis(struct mtd_info *mtd, struct image_info_struct *iis, u_int ptr)
}
static int parse_afs_partitions(struct mtd_info *mtd,
- struct mtd_partition **pparts,
- unsigned long origin)
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
{
struct mtd_partition *parts;
u_int mask, off, idx, sz;
diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c
index 6697a1ec72d0..f40ea4547554 100644
--- a/drivers/mtd/ar7part.c
+++ b/drivers/mtd/ar7part.c
@@ -27,6 +27,7 @@
#include <linux/mtd/partitions.h>
#include <linux/bootmem.h>
#include <linux/magic.h>
+#include <linux/module.h>
#define AR7_PARTS 4
#define ROOT_OFFSET 0xe0000
@@ -46,7 +47,7 @@ struct ar7_bin_rec {
static int create_mtd_partitions(struct mtd_info *master,
struct mtd_partition **pparts,
- unsigned long origin)
+ struct mtd_part_parser_data *data)
{
struct ar7_bin_rec header;
unsigned int offset;
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 23175edd5634..8d70895a58d6 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -145,8 +145,7 @@ static void fixup_amd_bootblock(struct mtd_info *mtd)
if (((major << 8) | minor) < 0x3131) {
/* CFI version 1.0 => don't trust bootloc */
- DEBUG(MTD_DEBUG_LEVEL1,
- "%s: JEDEC Vendor ID is 0x%02X Device ID is 0x%02X\n",
+ pr_debug("%s: JEDEC Vendor ID is 0x%02X Device ID is 0x%02X\n",
map->name, cfi->mfr, cfi->id);
/* AFAICS all 29LV400 with a bottom boot block have a device ID
@@ -166,8 +165,7 @@ static void fixup_amd_bootblock(struct mtd_info *mtd)
* the 8-bit device ID.
*/
(cfi->mfr == CFI_MFR_MACRONIX)) {
- DEBUG(MTD_DEBUG_LEVEL1,
- "%s: Macronix MX29LV400C with bottom boot block"
+ pr_debug("%s: Macronix MX29LV400C with bottom boot block"
" detected\n", map->name);
extp->TopBottom = 2; /* bottom boot */
} else
@@ -178,8 +176,7 @@ static void fixup_amd_bootblock(struct mtd_info *mtd)
extp->TopBottom = 2; /* bottom boot */
}
- DEBUG(MTD_DEBUG_LEVEL1,
- "%s: AMD CFI PRI V%c.%c has no boot block field;"
+ pr_debug("%s: AMD CFI PRI V%c.%c has no boot block field;"
" deduced %s from Device ID\n", map->name, major, minor,
extp->TopBottom == 2 ? "bottom" : "top");
}
@@ -191,7 +188,7 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
if (cfi->cfiq->BufWriteTimeoutTyp) {
- DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" );
+ pr_debug("Using buffer write method\n" );
mtd->write = cfi_amdstd_write_buffers;
}
}
@@ -443,8 +440,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
mtd->writesize = 1;
mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
- DEBUG(MTD_DEBUG_LEVEL3, "MTD %s(): write buffer size %d\n",
- __func__, mtd->writebufsize);
+ pr_debug("MTD %s(): write buffer size %d\n", __func__,
+ mtd->writebufsize);
mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot;
@@ -1163,7 +1160,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
return ret;
}
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
+ pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
__func__, adr, datum.x[0] );
/*
@@ -1174,7 +1171,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
*/
oldd = map_read(map, adr);
if (map_word_equal(map, oldd, datum)) {
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP\n",
+ pr_debug("MTD %s(): NOP\n",
__func__);
goto op_done;
}
@@ -1400,7 +1397,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
datum = map_word_load(map, buf);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
+ pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
__func__, adr, datum.x[0] );
XIP_INVAL_CACHED_RANGE(map, adr, len);
@@ -1587,7 +1584,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
return ret;
}
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
+ pr_debug("MTD %s(): ERASE 0x%.8lx\n",
__func__, chip->start );
XIP_INVAL_CACHED_RANGE(map, adr, map->size);
@@ -1675,7 +1672,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
return ret;
}
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
+ pr_debug("MTD %s(): ERASE 0x%.8lx\n",
__func__, adr );
XIP_INVAL_CACHED_RANGE(map, adr, len);
@@ -1801,8 +1798,7 @@ static int do_atmel_lock(struct map_info *map, struct flchip *chip,
goto out_unlock;
chip->state = FL_LOCKING;
- DEBUG(MTD_DEBUG_LEVEL3, "MTD %s(): LOCK 0x%08lx len %d\n",
- __func__, adr, len);
+ pr_debug("MTD %s(): LOCK 0x%08lx len %d\n", __func__, adr, len);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
@@ -1837,8 +1833,7 @@ static int do_atmel_unlock(struct map_info *map, struct flchip *chip,
goto out_unlock;
chip->state = FL_UNLOCKING;
- DEBUG(MTD_DEBUG_LEVEL3, "MTD %s(): LOCK 0x%08lx len %d\n",
- __func__, adr, len);
+ pr_debug("MTD %s(): LOCK 0x%08lx len %d\n", __func__, adr, len);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h
index 5e3cc80128aa..89c6595454a5 100644
--- a/drivers/mtd/chips/fwh_lock.h
+++ b/drivers/mtd/chips/fwh_lock.h
@@ -34,8 +34,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
/* Refuse the operation if the we cannot look behind the chip */
if (chip->start < 0x400000) {
- DEBUG( MTD_DEBUG_LEVEL3,
- "MTD %s(): chip->start: %lx wanted >= 0x400000\n",
+ pr_debug( "MTD %s(): chip->start: %lx wanted >= 0x400000\n",
__func__, chip->start );
return -EIO;
}
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index ea832ea0e4aa..c443f527a53a 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -1914,11 +1914,10 @@ static void jedec_reset(u32 base, struct map_info *map, struct cfi_private *cfi)
* (oh and incidentaly the jedec spec - 3.5.3.3) the reset
* sequence is *supposed* to be 0xaa at 0x5555, 0x55 at
* 0x2aaa, 0xF0 at 0x5555 this will not affect the AMD chips
- * as they will ignore the writes and dont care what address
+ * as they will ignore the writes and don't care what address
* the F0 is written to */
if (cfi->addr_unlock1) {
- DEBUG( MTD_DEBUG_LEVEL3,
- "reset unlock called %x %x \n",
+ pr_debug( "reset unlock called %x %x \n",
cfi->addr_unlock1,cfi->addr_unlock2);
cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL);
@@ -1941,7 +1940,7 @@ static int cfi_jedec_setup(struct map_info *map, struct cfi_private *cfi, int in
uint8_t uaddr;
if (!(jedec_table[index].devtypes & cfi->device_type)) {
- DEBUG(MTD_DEBUG_LEVEL1, "Rejecting potential %s with incompatible %d-bit device type\n",
+ pr_debug("Rejecting potential %s with incompatible %d-bit device type\n",
jedec_table[index].name, 4 * (1<<cfi->device_type));
return 0;
}
@@ -2021,7 +2020,7 @@ static inline int jedec_match( uint32_t base,
* there aren't.
*/
if (finfo->dev_id > 0xff) {
- DEBUG( MTD_DEBUG_LEVEL3, "%s(): ID is not 8bit\n",
+ pr_debug("%s(): ID is not 8bit\n",
__func__);
goto match_done;
}
@@ -2045,12 +2044,10 @@ static inline int jedec_match( uint32_t base,
}
/* the part size must fit in the memory window */
- DEBUG( MTD_DEBUG_LEVEL3,
- "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n",
+ pr_debug("MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n",
__func__, base, 1 << finfo->dev_size, base + (1 << finfo->dev_size) );
if ( base + cfi_interleave(cfi) * ( 1 << finfo->dev_size ) > map->size ) {
- DEBUG( MTD_DEBUG_LEVEL3,
- "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n",
+ pr_debug("MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n",
__func__, finfo->mfr_id, finfo->dev_id,
1 << finfo->dev_size );
goto match_done;
@@ -2061,13 +2058,12 @@ static inline int jedec_match( uint32_t base,
uaddr = finfo->uaddr;
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n",
+ pr_debug("MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n",
__func__, cfi->addr_unlock1, cfi->addr_unlock2 );
if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr
&& ( unlock_addrs[uaddr].addr1 / cfi->device_type != cfi->addr_unlock1 ||
unlock_addrs[uaddr].addr2 / cfi->device_type != cfi->addr_unlock2 ) ) {
- DEBUG( MTD_DEBUG_LEVEL3,
- "MTD %s(): 0x%.4x 0x%.4x did not match\n",
+ pr_debug("MTD %s(): 0x%.4x 0x%.4x did not match\n",
__func__,
unlock_addrs[uaddr].addr1,
unlock_addrs[uaddr].addr2);
@@ -2083,15 +2079,13 @@ static inline int jedec_match( uint32_t base,
* FIXME - write a driver that takes all of the chip info as
* module parameters, doesn't probe but forces a load.
*/
- DEBUG( MTD_DEBUG_LEVEL3,
- "MTD %s(): check ID's disappear when not in ID mode\n",
+ pr_debug("MTD %s(): check ID's disappear when not in ID mode\n",
__func__ );
jedec_reset( base, map, cfi );
mfr = jedec_read_mfr( map, base, cfi );
id = jedec_read_id( map, base, cfi );
if ( mfr == cfi->mfr && id == cfi->id ) {
- DEBUG( MTD_DEBUG_LEVEL3,
- "MTD %s(): ID 0x%.2x:0x%.2x did not change after reset:\n"
+ pr_debug("MTD %s(): ID 0x%.2x:0x%.2x did not change after reset:\n"
"You might need to manually specify JEDEC parameters.\n",
__func__, cfi->mfr, cfi->id );
goto match_done;
@@ -2104,7 +2098,7 @@ static inline int jedec_match( uint32_t base,
* Put the device back in ID mode - only need to do this if we
* were truly frobbing a real device.
*/
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ );
+ pr_debug("MTD %s(): return to ID mode\n", __func__ );
if (cfi->addr_unlock1) {
cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL);
@@ -2167,13 +2161,11 @@ static int jedec_probe_chip(struct map_info *map, __u32 base,
cfi->mfr = jedec_read_mfr(map, base, cfi);
cfi->id = jedec_read_id(map, base, cfi);
- DEBUG(MTD_DEBUG_LEVEL3,
- "Search for id:(%02x %02x) interleave(%d) type(%d)\n",
+ pr_debug("Search for id:(%02x %02x) interleave(%d) type(%d)\n",
cfi->mfr, cfi->id, cfi_interleave(cfi), cfi->device_type);
for (i = 0; i < ARRAY_SIZE(jedec_table); i++) {
if ( jedec_match( base, map, cfi, &jedec_table[i] ) ) {
- DEBUG( MTD_DEBUG_LEVEL3,
- "MTD %s(): matched device 0x%x,0x%x unlock_addrs: 0x%.4x 0x%.4x\n",
+ pr_debug("MTD %s(): matched device 0x%x,0x%x unlock_addrs: 0x%.4x 0x%.4x\n",
__func__, cfi->mfr, cfi->id,
cfi->addr_unlock1, cfi->addr_unlock2 );
if (!cfi_jedec_setup(map, cfi, i))
diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c
index e790f38893b0..ddf9ec6d9168 100644
--- a/drivers/mtd/cmdlinepart.c
+++ b/drivers/mtd/cmdlinepart.c
@@ -43,6 +43,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/bootmem.h>
+#include <linux/module.h>
/* error message prefix */
#define ERRP "mtd: "
@@ -188,10 +189,7 @@ static struct mtd_partition * newpart(char *s,
extra_mem_size;
parts = kzalloc(alloc_size, GFP_KERNEL);
if (!parts)
- {
- printk(KERN_ERR ERRP "out of memory\n");
return NULL;
- }
extra_mem = (unsigned char *)(parts + *num_parts);
}
/* enter this partition (offset will be calculated later if it is zero at this point) */
@@ -316,8 +314,8 @@ static int mtdpart_setup_real(char *s)
* the first one in the chain if a NULL mtd_id is passed in.
*/
static int parse_cmdline_partitions(struct mtd_info *master,
- struct mtd_partition **pparts,
- unsigned long origin)
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
{
unsigned long offset;
int i;
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 35081ce77fbd..283d887f7825 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -249,6 +249,16 @@ config MTD_DOC2001PLUS
under "NAND Flash Device Drivers" (currently that driver does not
support all Millennium Plus devices).
+config MTD_DOCG3
+ tristate "M-Systems Disk-On-Chip G3"
+ ---help---
+ This provides an MTD device driver for the M-Systems DiskOnChip
+ G3 devices.
+
+ The driver provides access to G3 DiskOnChip, distributed by
+ M-Systems and now Sandisk. The support is very experimental,
+ and doesn't give access to any write operations.
+
config MTD_DOCPROBE
tristate
select MTD_DOCECC
@@ -268,8 +278,7 @@ config MTD_DOCPROBE_ADVANCED
config MTD_DOCPROBE_ADDRESS
hex "Physical address of DiskOnChip" if MTD_DOCPROBE_ADVANCED
depends on MTD_DOCPROBE
- default "0x0000" if MTD_DOCPROBE_ADVANCED
- default "0" if !MTD_DOCPROBE_ADVANCED
+ default "0x0"
---help---
By default, the probe for DiskOnChip devices will look for a
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index f3226b1d38fc..56c7cd462f11 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -5,6 +5,7 @@
obj-$(CONFIG_MTD_DOC2000) += doc2000.o
obj-$(CONFIG_MTD_DOC2001) += doc2001.o
obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o
+obj-$(CONFIG_MTD_DOCG3) += docg3.o
obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o
obj-$(CONFIG_MTD_DOCECC) += docecc.o
obj-$(CONFIG_MTD_SLRAM) += slram.o
@@ -17,3 +18,5 @@ obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
+
+CFLAGS_docg3.o += -I$(src) \ No newline at end of file
diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c
index f7fbf6025ef2..e9fad9151219 100644
--- a/drivers/mtd/devices/doc2000.c
+++ b/drivers/mtd/devices/doc2000.c
@@ -82,8 +82,7 @@ static int _DoC_WaitReady(struct DiskOnChip *doc)
void __iomem *docptr = doc->virtadr;
unsigned long timeo = jiffies + (HZ * 10);
- DEBUG(MTD_DEBUG_LEVEL3,
- "_DoC_WaitReady called for out-of-line wait\n");
+ pr_debug("_DoC_WaitReady called for out-of-line wait\n");
/* Out-of-line routine to wait for chip response */
while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
@@ -92,7 +91,7 @@ static int _DoC_WaitReady(struct DiskOnChip *doc)
DoC_Delay(doc, 2);
if (time_after(jiffies, timeo)) {
- DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+ pr_debug("_DoC_WaitReady timed out.\n");
return -EIO;
}
udelay(1);
@@ -323,8 +322,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
/* Reset the chip */
if (DoC_Command(doc, NAND_CMD_RESET, CDSN_CTRL_WP)) {
- DEBUG(MTD_DEBUG_LEVEL2,
- "DoC_Command (reset) for %d,%d returned true\n",
+ pr_debug("DoC_Command (reset) for %d,%d returned true\n",
floor, chip);
return 0;
}
@@ -332,8 +330,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
/* Read the NAND chip ID: 1. Send ReadID command */
if (DoC_Command(doc, NAND_CMD_READID, CDSN_CTRL_WP)) {
- DEBUG(MTD_DEBUG_LEVEL2,
- "DoC_Command (ReadID) for %d,%d returned true\n",
+ pr_debug("DoC_Command (ReadID) for %d,%d returned true\n",
floor, chip);
return 0;
}
@@ -699,7 +696,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
#ifdef ECC_DEBUG
printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from);
#endif
- /* Read the ECC syndrom through the DiskOnChip ECC
+ /* Read the ECC syndrome through the DiskOnChip ECC
logic. These syndrome will be all ZERO when there
is no error */
for (i = 0; i < 6; i++) {
@@ -930,7 +927,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
uint8_t *buf = ops->oobbuf;
size_t len = ops->len;
- BUG_ON(ops->mode != MTD_OOB_PLACE);
+ BUG_ON(ops->mode != MTD_OPS_PLACE_OOB);
ofs += ops->ooboffs;
@@ -1094,7 +1091,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
struct DiskOnChip *this = mtd->priv;
int ret;
- BUG_ON(ops->mode != MTD_OOB_PLACE);
+ BUG_ON(ops->mode != MTD_OPS_PLACE_OOB);
mutex_lock(&this->lock);
ret = doc_write_oob_nolock(mtd, ofs + ops->ooboffs, ops->len,
diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c
index 241192f05bc8..a3f7a27499be 100644
--- a/drivers/mtd/devices/doc2001.c
+++ b/drivers/mtd/devices/doc2001.c
@@ -55,15 +55,14 @@ static int _DoC_WaitReady(void __iomem * docptr)
{
unsigned short c = 0xffff;
- DEBUG(MTD_DEBUG_LEVEL3,
- "_DoC_WaitReady called for out-of-line wait\n");
+ pr_debug("_DoC_WaitReady called for out-of-line wait\n");
/* Out-of-line routine to wait for chip response */
while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c)
;
if (c == 0)
- DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+ pr_debug("_DoC_WaitReady timed out.\n");
return (c == 0);
}
@@ -464,7 +463,7 @@ static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
#ifdef ECC_DEBUG
printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
#endif
- /* Read the ECC syndrom through the DiskOnChip ECC logic.
+ /* Read the ECC syndrome through the DiskOnChip ECC logic.
These syndrome will be all ZERO when there is no error */
for (i = 0; i < 6; i++) {
syndrome[i] = ReadDOC(docptr, ECCSyndrome0 + i);
@@ -632,7 +631,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
uint8_t *buf = ops->oobbuf;
size_t len = ops->len;
- BUG_ON(ops->mode != MTD_OOB_PLACE);
+ BUG_ON(ops->mode != MTD_OPS_PLACE_OOB);
ofs += ops->ooboffs;
@@ -690,7 +689,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
uint8_t *buf = ops->oobbuf;
size_t len = ops->len;
- BUG_ON(ops->mode != MTD_OOB_PLACE);
+ BUG_ON(ops->mode != MTD_OPS_PLACE_OOB);
ofs += ops->ooboffs;
diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c
index 09ae0adc3ad0..99351bc3e0ed 100644
--- a/drivers/mtd/devices/doc2001plus.c
+++ b/drivers/mtd/devices/doc2001plus.c
@@ -61,15 +61,14 @@ static int _DoC_WaitReady(void __iomem * docptr)
{
unsigned int c = 0xffff;
- DEBUG(MTD_DEBUG_LEVEL3,
- "_DoC_WaitReady called for out-of-line wait\n");
+ pr_debug("_DoC_WaitReady called for out-of-line wait\n");
/* Out-of-line routine to wait for chip response */
while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c)
;
if (c == 0)
- DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+ pr_debug("_DoC_WaitReady timed out.\n");
return (c == 0);
}
@@ -655,7 +654,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
#ifdef ECC_DEBUG
printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
#endif
- /* Read the ECC syndrom through the DiskOnChip ECC logic.
+ /* Read the ECC syndrome through the DiskOnChip ECC logic.
These syndrome will be all ZERO when there is no error */
for (i = 0; i < 6; i++)
syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i);
@@ -835,7 +834,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
uint8_t *buf = ops->oobbuf;
size_t len = ops->len;
- BUG_ON(ops->mode != MTD_OOB_PLACE);
+ BUG_ON(ops->mode != MTD_OPS_PLACE_OOB);
ofs += ops->ooboffs;
@@ -920,7 +919,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
uint8_t *buf = ops->oobbuf;
size_t len = ops->len;
- BUG_ON(ops->mode != MTD_OOB_PLACE);
+ BUG_ON(ops->mode != MTD_OPS_PLACE_OOB);
ofs += ops->ooboffs;
diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c
index 37ef29a73ee4..4a1c39b6f37d 100644
--- a/drivers/mtd/devices/docecc.c
+++ b/drivers/mtd/devices/docecc.c
@@ -2,7 +2,7 @@
* ECC algorithm for M-systems disk on chip. We use the excellent Reed
* Solmon code of Phil Karn (karn@ka9q.ampr.org) available under the
* GNU GPL License. The rest is simply to convert the disk on chip
- * syndrom into a standard syndom.
+ * syndrome into a standard syndome.
*
* Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* Copyright (C) 2000 Netgem S.A.
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
new file mode 100644
index 000000000000..bdcf5df982e8
--- /dev/null
+++ b/drivers/mtd/devices/docg3.c
@@ -0,0 +1,1114 @@
+/*
+ * Handles the M-Systems DiskOnChip G3 chip
+ *
+ * Copyright (C) 2011 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#define CREATE_TRACE_POINTS
+#include "docg3.h"
+
+/*
+ * This driver handles the DiskOnChip G3 flash memory.
+ *
+ * As no specification is available from M-Systems/Sandisk, this drivers lacks
+ * several functions available on the chip, as :
+ * - block erase
+ * - page write
+ * - IPL write
+ * - ECC fixing (lack of BCH algorith understanding)
+ * - powerdown / powerup
+ *
+ * The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and
+ * the driver assumes a 16bits data bus.
+ *
+ * DocG3 relies on 2 ECC algorithms, which are handled in hardware :
+ * - a 1 byte Hamming code stored in the OOB for each page
+ * - a 7 bytes BCH code stored in the OOB for each page
+ * The BCH part is only used for check purpose, no correction is available as
+ * some information is missing. What is known is that :
+ * - BCH is in GF(2^14)
+ * - BCH is over data of 520 bytes (512 page + 7 page_info bytes
+ * + 1 hamming byte)
+ * - BCH can correct up to 4 bits (t = 4)
+ * - BCH syndroms are calculated in hardware, and checked in hardware as well
+ *
+ */
+
+static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
+{
+ u8 val = readb(docg3->base + reg);
+
+ trace_docg3_io(0, 8, reg, (int)val);
+ return val;
+}
+
+static inline u16 doc_readw(struct docg3 *docg3, u16 reg)
+{
+ u16 val = readw(docg3->base + reg);
+
+ trace_docg3_io(0, 16, reg, (int)val);
+ return val;
+}
+
+static inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg)
+{
+ writeb(val, docg3->base + reg);
+ trace_docg3_io(1, 16, reg, val);
+}
+
+static inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg)
+{
+ writew(val, docg3->base + reg);
+ trace_docg3_io(1, 16, reg, val);
+}
+
+static inline void doc_flash_command(struct docg3 *docg3, u8 cmd)
+{
+ doc_writeb(docg3, cmd, DOC_FLASHCOMMAND);
+}
+
+static inline void doc_flash_sequence(struct docg3 *docg3, u8 seq)
+{
+ doc_writeb(docg3, seq, DOC_FLASHSEQUENCE);
+}
+
+static inline void doc_flash_address(struct docg3 *docg3, u8 addr)
+{
+ doc_writeb(docg3, addr, DOC_FLASHADDRESS);
+}
+
+static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL };
+
+static int doc_register_readb(struct docg3 *docg3, int reg)
+{
+ u8 val;
+
+ doc_writew(docg3, reg, DOC_READADDRESS);
+ val = doc_readb(docg3, reg);
+ doc_vdbg("Read register %04x : %02x\n", reg, val);
+ return val;
+}
+
+static int doc_register_readw(struct docg3 *docg3, int reg)
+{
+ u16 val;
+
+ doc_writew(docg3, reg, DOC_READADDRESS);
+ val = doc_readw(docg3, reg);
+ doc_vdbg("Read register %04x : %04x\n", reg, val);
+ return val;
+}
+
+/**
+ * doc_delay - delay docg3 operations
+ * @docg3: the device
+ * @nbNOPs: the number of NOPs to issue
+ *
+ * As no specification is available, the right timings between chip commands are
+ * unknown. The only available piece of information are the observed nops on a
+ * working docg3 chip.
+ * Therefore, doc_delay relies on a busy loop of NOPs, instead of scheduler
+ * friendlier msleep() functions or blocking mdelay().
+ */
+static void doc_delay(struct docg3 *docg3, int nbNOPs)
+{
+ int i;
+
+ doc_dbg("NOP x %d\n", nbNOPs);
+ for (i = 0; i < nbNOPs; i++)
+ doc_writeb(docg3, 0, DOC_NOP);
+}
+
+static int is_prot_seq_error(struct docg3 *docg3)
+{
+ int ctrl;
+
+ ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
+ return ctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR);
+}
+
+static int doc_is_ready(struct docg3 *docg3)
+{
+ int ctrl;
+
+ ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
+ return ctrl & DOC_CTRL_FLASHREADY;
+}
+
+static int doc_wait_ready(struct docg3 *docg3)
+{
+ int maxWaitCycles = 100;
+
+ do {
+ doc_delay(docg3, 4);
+ cpu_relax();
+ } while (!doc_is_ready(docg3) && maxWaitCycles--);
+ doc_delay(docg3, 2);
+ if (maxWaitCycles > 0)
+ return 0;
+ else
+ return -EIO;
+}
+
+static int doc_reset_seq(struct docg3 *docg3)
+{
+ int ret;
+
+ doc_writeb(docg3, 0x10, DOC_FLASHCONTROL);
+ doc_flash_sequence(docg3, DOC_SEQ_RESET);
+ doc_flash_command(docg3, DOC_CMD_RESET);
+ doc_delay(docg3, 2);
+ ret = doc_wait_ready(docg3);
+
+ doc_dbg("doc_reset_seq() -> isReady=%s\n", ret ? "false" : "true");
+ return ret;
+}
+
+/**
+ * doc_read_data_area - Read data from data area
+ * @docg3: the device
+ * @buf: the buffer to fill in
+ * @len: the lenght to read
+ * @first: first time read, DOC_READADDRESS should be set
+ *
+ * Reads bytes from flash data. Handles the single byte / even bytes reads.
+ */
+static void doc_read_data_area(struct docg3 *docg3, void *buf, int len,
+ int first)
+{
+ int i, cdr, len4;
+ u16 data16, *dst16;
+ u8 data8, *dst8;
+
+ doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len);
+ cdr = len & 0x3;
+ len4 = len - cdr;
+
+ if (first)
+ doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS);
+ dst16 = buf;
+ for (i = 0; i < len4; i += 2) {
+ data16 = doc_readw(docg3, DOC_IOSPACE_DATA);
+ *dst16 = data16;
+ dst16++;
+ }
+
+ if (cdr) {
+ doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE,
+ DOC_READADDRESS);
+ doc_delay(docg3, 1);
+ dst8 = (u8 *)dst16;
+ for (i = 0; i < cdr; i++) {
+ data8 = doc_readb(docg3, DOC_IOSPACE_DATA);
+ *dst8 = data8;
+ dst8++;
+ }
+ }
+}
+
+/**
+ * doc_set_data_mode - Sets the flash to reliable data mode
+ * @docg3: the device
+ *
+ * The reliable data mode is a bit slower than the fast mode, but less errors
+ * occur. Entering the reliable mode cannot be done without entering the fast
+ * mode first.
+ */
+static void doc_set_reliable_mode(struct docg3 *docg3)
+{
+ doc_dbg("doc_set_reliable_mode()\n");
+ doc_flash_sequence(docg3, DOC_SEQ_SET_MODE);
+ doc_flash_command(docg3, DOC_CMD_FAST_MODE);
+ doc_flash_command(docg3, DOC_CMD_RELIABLE_MODE);
+ doc_delay(docg3, 2);
+}
+
+/**
+ * doc_set_asic_mode - Set the ASIC mode
+ * @docg3: the device
+ * @mode: the mode
+ *
+ * The ASIC can work in 3 modes :
+ * - RESET: all registers are zeroed
+ * - NORMAL: receives and handles commands
+ * - POWERDOWN: minimal poweruse, flash parts shut off
+ */
+static void doc_set_asic_mode(struct docg3 *docg3, u8 mode)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ doc_readb(docg3, DOC_IOSPACE_IPL);
+
+ mode |= DOC_ASICMODE_MDWREN;
+ doc_dbg("doc_set_asic_mode(%02x)\n", mode);
+ doc_writeb(docg3, mode, DOC_ASICMODE);
+ doc_writeb(docg3, ~mode, DOC_ASICMODECONFIRM);
+ doc_delay(docg3, 1);
+}
+
+/**
+ * doc_set_device_id - Sets the devices id for cascaded G3 chips
+ * @docg3: the device
+ * @id: the chip to select (amongst 0, 1, 2, 3)
+ *
+ * There can be 4 cascaded G3 chips. This function selects the one which will
+ * should be the active one.
+ */
+static void doc_set_device_id(struct docg3 *docg3, int id)
+{
+ u8 ctrl;
+
+ doc_dbg("doc_set_device_id(%d)\n", id);
+ doc_writeb(docg3, id, DOC_DEVICESELECT);
+ ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
+
+ ctrl &= ~DOC_CTRL_VIOLATION;
+ ctrl |= DOC_CTRL_CE;
+ doc_writeb(docg3, ctrl, DOC_FLASHCONTROL);
+}
+
+/**
+ * doc_set_extra_page_mode - Change flash page layout
+ * @docg3: the device
+ *
+ * Normally, the flash page is split into the data (512 bytes) and the out of
+ * band data (16 bytes). For each, 4 more bytes can be accessed, where the wear
+ * leveling counters are stored. To access this last area of 4 bytes, a special
+ * mode must be input to the flash ASIC.
+ *
+ * Returns 0 if no error occured, -EIO else.
+ */
+static int doc_set_extra_page_mode(struct docg3 *docg3)
+{
+ int fctrl;
+
+ doc_dbg("doc_set_extra_page_mode()\n");
+ doc_flash_sequence(docg3, DOC_SEQ_PAGE_SIZE_532);
+ doc_flash_command(docg3, DOC_CMD_PAGE_SIZE_532);
+ doc_delay(docg3, 2);
+
+ fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
+ if (fctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR))
+ return -EIO;
+ else
+ return 0;
+}
+
+/**
+ * doc_seek - Set both flash planes to the specified block, page for reading
+ * @docg3: the device
+ * @block0: the first plane block index
+ * @block1: the second plane block index
+ * @page: the page index within the block
+ * @wear: if true, read will occur on the 4 extra bytes of the wear area
+ * @ofs: offset in page to read
+ *
+ * Programs the flash even and odd planes to the specific block and page.
+ * Alternatively, programs the flash to the wear area of the specified page.
+ */
+static int doc_read_seek(struct docg3 *docg3, int block0, int block1, int page,
+ int wear, int ofs)
+{
+ int sector, ret = 0;
+
+ doc_dbg("doc_seek(blocks=(%d,%d), page=%d, ofs=%d, wear=%d)\n",
+ block0, block1, page, ofs, wear);
+
+ if (!wear && (ofs < 2 * DOC_LAYOUT_PAGE_SIZE)) {
+ doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1);
+ doc_flash_command(docg3, DOC_CMD_READ_PLANE1);
+ doc_delay(docg3, 2);
+ } else {
+ doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2);
+ doc_flash_command(docg3, DOC_CMD_READ_PLANE2);
+ doc_delay(docg3, 2);
+ }
+
+ doc_set_reliable_mode(docg3);
+ if (wear)
+ ret = doc_set_extra_page_mode(docg3);
+ if (ret)
+ goto out;
+
+ sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
+ doc_flash_sequence(docg3, DOC_SEQ_READ);
+ doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR);
+ doc_delay(docg3, 1);
+ doc_flash_address(docg3, sector & 0xff);
+ doc_flash_address(docg3, (sector >> 8) & 0xff);
+ doc_flash_address(docg3, (sector >> 16) & 0xff);
+ doc_delay(docg3, 1);
+
+ sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
+ doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR);
+ doc_delay(docg3, 1);
+ doc_flash_address(docg3, sector & 0xff);
+ doc_flash_address(docg3, (sector >> 8) & 0xff);
+ doc_flash_address(docg3, (sector >> 16) & 0xff);
+ doc_delay(docg3, 2);
+
+out:
+ return ret;
+}
+
+/**
+ * doc_read_page_ecc_init - Initialize hardware ECC engine
+ * @docg3: the device
+ * @len: the number of bytes covered by the ECC (BCH covered)
+ *
+ * The function does initialize the hardware ECC engine to compute the Hamming
+ * ECC (on 1 byte) and the BCH Syndroms (on 7 bytes).
+ *
+ * Return 0 if succeeded, -EIO on error
+ */
+static int doc_read_page_ecc_init(struct docg3 *docg3, int len)
+{
+ doc_writew(docg3, DOC_ECCCONF0_READ_MODE
+ | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE
+ | (len & DOC_ECCCONF0_DATA_BYTES_MASK),
+ DOC_ECCCONF0);
+ doc_delay(docg3, 4);
+ doc_register_readb(docg3, DOC_FLASHCONTROL);
+ return doc_wait_ready(docg3);
+}
+
+/**
+ * doc_read_page_prepare - Prepares reading data from a flash page
+ * @docg3: the device
+ * @block0: the first plane block index on flash memory
+ * @block1: the second plane block index on flash memory
+ * @page: the page index in the block
+ * @offset: the offset in the page (must be a multiple of 4)
+ *
+ * Prepares the page to be read in the flash memory :
+ * - tell ASIC to map the flash pages
+ * - tell ASIC to be in read mode
+ *
+ * After a call to this method, a call to doc_read_page_finish is mandatory,
+ * to end the read cycle of the flash.
+ *
+ * Read data from a flash page. The length to be read must be between 0 and
+ * (page_size + oob_size + wear_size), ie. 532, and a multiple of 4 (because
+ * the extra bytes reading is not implemented).
+ *
+ * As pages are grouped by 2 (in 2 planes), reading from a page must be done
+ * in two steps:
+ * - one read of 512 bytes at offset 0
+ * - one read of 512 bytes at offset 512 + 16
+ *
+ * Returns 0 if successful, -EIO if a read error occured.
+ */
+static int doc_read_page_prepare(struct docg3 *docg3, int block0, int block1,
+ int page, int offset)
+{
+ int wear_area = 0, ret = 0;
+
+ doc_dbg("doc_read_page_prepare(blocks=(%d,%d), page=%d, ofsInPage=%d)\n",
+ block0, block1, page, offset);
+ if (offset >= DOC_LAYOUT_WEAR_OFFSET)
+ wear_area = 1;
+ if (!wear_area && offset > (DOC_LAYOUT_PAGE_OOB_SIZE * 2))
+ return -EINVAL;
+
+ doc_set_device_id(docg3, docg3->device_id);
+ ret = doc_reset_seq(docg3);
+ if (ret)
+ goto err;
+
+ /* Program the flash address block and page */
+ ret = doc_read_seek(docg3, block0, block1, page, wear_area, offset);
+ if (ret)
+ goto err;
+
+ doc_flash_command(docg3, DOC_CMD_READ_ALL_PLANES);
+ doc_delay(docg3, 2);
+ doc_wait_ready(docg3);
+
+ doc_flash_command(docg3, DOC_CMD_SET_ADDR_READ);
+ doc_delay(docg3, 1);
+ if (offset >= DOC_LAYOUT_PAGE_SIZE * 2)
+ offset -= 2 * DOC_LAYOUT_PAGE_SIZE;
+ doc_flash_address(docg3, offset >> 2);
+ doc_delay(docg3, 1);
+ doc_wait_ready(docg3);
+
+ doc_flash_command(docg3, DOC_CMD_READ_FLASH);
+
+ return 0;
+err:
+ doc_writeb(docg3, 0, DOC_DATAEND);
+ doc_delay(docg3, 2);
+ return -EIO;
+}
+
+/**
+ * doc_read_page_getbytes - Reads bytes from a prepared page
+ * @docg3: the device
+ * @len: the number of bytes to be read (must be a multiple of 4)
+ * @buf: the buffer to be filled in
+ * @first: 1 if first time read, DOC_READADDRESS should be set
+ *
+ */
+static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf,
+ int first)
+{
+ doc_read_data_area(docg3, buf, len, first);
+ doc_delay(docg3, 2);
+ return len;
+}
+
+/**
+ * doc_get_hw_bch_syndroms - Get hardware calculated BCH syndroms
+ * @docg3: the device
+ * @syns: the array of 7 integers where the syndroms will be stored
+ */
+static void doc_get_hw_bch_syndroms(struct docg3 *docg3, int *syns)
+{
+ int i;
+
+ for (i = 0; i < DOC_ECC_BCH_SIZE; i++)
+ syns[i] = doc_register_readb(docg3, DOC_BCH_SYNDROM(i));
+}
+
+/**
+ * doc_read_page_finish - Ends reading of a flash page
+ * @docg3: the device
+ *
+ * As a side effect, resets the chip selector to 0. This ensures that after each
+ * read operation, the floor 0 is selected. Therefore, if the systems halts, the
+ * reboot will boot on floor 0, where the IPL is.
+ */
+static void doc_read_page_finish(struct docg3 *docg3)
+{
+ doc_writeb(docg3, 0, DOC_DATAEND);
+ doc_delay(docg3, 2);
+ doc_set_device_id(docg3, 0);
+}
+
+/**
+ * calc_block_sector - Calculate blocks, pages and ofs.
+
+ * @from: offset in flash
+ * @block0: first plane block index calculated
+ * @block1: second plane block index calculated
+ * @page: page calculated
+ * @ofs: offset in page
+ */
+static void calc_block_sector(loff_t from, int *block0, int *block1, int *page,
+ int *ofs)
+{
+ uint sector;
+
+ sector = from / DOC_LAYOUT_PAGE_SIZE;
+ *block0 = sector / (DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES)
+ * DOC_LAYOUT_NBPLANES;
+ *block1 = *block0 + 1;
+ *page = sector % (DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES);
+ *page /= DOC_LAYOUT_NBPLANES;
+ if (sector % 2)
+ *ofs = DOC_LAYOUT_PAGE_OOB_SIZE;
+ else
+ *ofs = 0;
+}
+
+/**
+ * doc_read - Read bytes from flash
+ * @mtd: the device
+ * @from: the offset from first block and first page, in bytes, aligned on page
+ * size
+ * @len: the number of bytes to read (must be a multiple of 4)
+ * @retlen: the number of bytes actually read
+ * @buf: the filled in buffer
+ *
+ * Reads flash memory pages. This function does not read the OOB chunk, but only
+ * the page data.
+ *
+ * Returns 0 if read successfull, of -EIO, -EINVAL if an error occured
+ */
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct docg3 *docg3 = mtd->priv;
+ int block0, block1, page, readlen, ret, ofs = 0;
+ int syn[DOC_ECC_BCH_SIZE], eccconf1;
+ u8 oob[DOC_LAYOUT_OOB_SIZE];
+
+ ret = -EINVAL;
+ doc_dbg("doc_read(from=%lld, len=%zu, buf=%p)\n", from, len, buf);
+ if (from % DOC_LAYOUT_PAGE_SIZE)
+ goto err;
+ if (len % 4)
+ goto err;
+ calc_block_sector(from, &block0, &block1, &page, &ofs);
+ if (block1 > docg3->max_block)
+ goto err;
+
+ *retlen = 0;
+ ret = 0;
+ readlen = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE);
+ while (!ret && len > 0) {
+ readlen = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE);
+ ret = doc_read_page_prepare(docg3, block0, block1, page, ofs);
+ if (ret < 0)
+ goto err;
+ ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES);
+ if (ret < 0)
+ goto err_in_read;
+ ret = doc_read_page_getbytes(docg3, readlen, buf, 1);
+ if (ret < readlen)
+ goto err_in_read;
+ ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE,
+ oob, 0);
+ if (ret < DOC_LAYOUT_OOB_SIZE)
+ goto err_in_read;
+
+ *retlen += readlen;
+ buf += readlen;
+ len -= readlen;
+
+ ofs ^= DOC_LAYOUT_PAGE_OOB_SIZE;
+ if (ofs == 0)
+ page += 2;
+ if (page > DOC_ADDR_PAGE_MASK) {
+ page = 0;
+ block0 += 2;
+ block1 += 2;
+ }
+
+ /*
+ * There should be a BCH bitstream fixing algorithm here ...
+ * By now, a page read failure is triggered by BCH error
+ */
+ doc_get_hw_bch_syndroms(docg3, syn);
+ eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1);
+
+ doc_dbg("OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ oob[0], oob[1], oob[2], oob[3], oob[4],
+ oob[5], oob[6]);
+ doc_dbg("OOB - HAMMING: %02x\n", oob[7]);
+ doc_dbg("OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ oob[8], oob[9], oob[10], oob[11], oob[12],
+ oob[13], oob[14]);
+ doc_dbg("OOB - UNUSED: %02x\n", oob[15]);
+ doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1);
+ doc_dbg("ECC BCH syndrom: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ syn[0], syn[1], syn[2], syn[3], syn[4], syn[5], syn[6]);
+
+ ret = -EBADMSG;
+ if (block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) {
+ if (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR)
+ goto err_in_read;
+ if (is_prot_seq_error(docg3))
+ goto err_in_read;
+ }
+ doc_read_page_finish(docg3);
+ }
+
+ return 0;
+err_in_read:
+ doc_read_page_finish(docg3);
+err:
+ return ret;
+}
+
+/**
+ * doc_read_oob - Read out of band bytes from flash
+ * @mtd: the device
+ * @from: the offset from first block and first page, in bytes, aligned on page
+ * size
+ * @ops: the mtd oob structure
+ *
+ * Reads flash memory OOB area of pages.
+ *
+ * Returns 0 if read successfull, of -EIO, -EINVAL if an error occured
+ */
+static int doc_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct docg3 *docg3 = mtd->priv;
+ int block0, block1, page, ofs, ret;
+ u8 *buf = ops->oobbuf;
+ size_t len = ops->ooblen;
+
+ doc_dbg("doc_read_oob(from=%lld, buf=%p, len=%zu)\n", from, buf, len);
+ if (len != DOC_LAYOUT_OOB_SIZE)
+ return -EINVAL;
+
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ buf += ops->ooboffs;
+ break;
+ default:
+ break;
+ }
+
+ calc_block_sector(from, &block0, &block1, &page, &ofs);
+ if (block1 > docg3->max_block)
+ return -EINVAL;
+
+ ret = doc_read_page_prepare(docg3, block0, block1, page,
+ ofs + DOC_LAYOUT_PAGE_SIZE);
+ if (!ret)
+ ret = doc_read_page_ecc_init(docg3, DOC_LAYOUT_OOB_SIZE);
+ if (!ret)
+ ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE,
+ buf, 1);
+ doc_read_page_finish(docg3);
+
+ if (ret > 0)
+ ops->oobretlen = ret;
+ else
+ ops->oobretlen = 0;
+ return (ret > 0) ? 0 : ret;
+}
+
+static int doc_reload_bbt(struct docg3 *docg3)
+{
+ int block = DOC_LAYOUT_BLOCK_BBT;
+ int ret = 0, nbpages, page;
+ u_char *buf = docg3->bbt;
+
+ nbpages = DIV_ROUND_UP(docg3->max_block + 1, 8 * DOC_LAYOUT_PAGE_SIZE);
+ for (page = 0; !ret && (page < nbpages); page++) {
+ ret = doc_read_page_prepare(docg3, block, block + 1,
+ page + DOC_LAYOUT_PAGE_BBT, 0);
+ if (!ret)
+ ret = doc_read_page_ecc_init(docg3,
+ DOC_LAYOUT_PAGE_SIZE);
+ if (!ret)
+ doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE,
+ buf, 1);
+ buf += DOC_LAYOUT_PAGE_SIZE;
+ }
+ doc_read_page_finish(docg3);
+ return ret;
+}
+
+/**
+ * doc_block_isbad - Checks whether a block is good or not
+ * @mtd: the device
+ * @from: the offset to find the correct block
+ *
+ * Returns 1 if block is bad, 0 if block is good
+ */
+static int doc_block_isbad(struct mtd_info *mtd, loff_t from)
+{
+ struct docg3 *docg3 = mtd->priv;
+ int block0, block1, page, ofs, is_good;
+
+ calc_block_sector(from, &block0, &block1, &page, &ofs);
+ doc_dbg("doc_block_isbad(from=%lld) => block=(%d,%d), page=%d, ofs=%d\n",
+ from, block0, block1, page, ofs);
+
+ if (block0 < DOC_LAYOUT_BLOCK_FIRST_DATA)
+ return 0;
+ if (block1 > docg3->max_block)
+ return -EINVAL;
+
+ is_good = docg3->bbt[block0 >> 3] & (1 << (block0 & 0x7));
+ return !is_good;
+}
+
+/**
+ * doc_get_erase_count - Get block erase count
+ * @docg3: the device
+ * @from: the offset in which the block is.
+ *
+ * Get the number of times a block was erased. The number is the maximum of
+ * erase times between first and second plane (which should be equal normally).
+ *
+ * Returns The number of erases, or -EINVAL or -EIO on error.
+ */
+static int doc_get_erase_count(struct docg3 *docg3, loff_t from)
+{
+ u8 buf[DOC_LAYOUT_WEAR_SIZE];
+ int ret, plane1_erase_count, plane2_erase_count;
+ int block0, block1, page, ofs;
+
+ doc_dbg("doc_get_erase_count(from=%lld, buf=%p)\n", from, buf);
+ if (from % DOC_LAYOUT_PAGE_SIZE)
+ return -EINVAL;
+ calc_block_sector(from, &block0, &block1, &page, &ofs);
+ if (block1 > docg3->max_block)
+ return -EINVAL;
+
+ ret = doc_reset_seq(docg3);
+ if (!ret)
+ ret = doc_read_page_prepare(docg3, block0, block1, page,
+ ofs + DOC_LAYOUT_WEAR_OFFSET);
+ if (!ret)
+ ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE,
+ buf, 1);
+ doc_read_page_finish(docg3);
+
+ if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK))
+ return -EIO;
+ plane1_erase_count = (u8)(~buf[1]) | ((u8)(~buf[4]) << 8)
+ | ((u8)(~buf[5]) << 16);
+ plane2_erase_count = (u8)(~buf[3]) | ((u8)(~buf[6]) << 8)
+ | ((u8)(~buf[7]) << 16);
+
+ return max(plane1_erase_count, plane2_erase_count);
+}
+
+/*
+ * Debug sysfs entries
+ */
+static int dbg_flashctrl_show(struct seq_file *s, void *p)
+{
+ struct docg3 *docg3 = (struct docg3 *)s->private;
+
+ int pos = 0;
+ u8 fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
+
+ pos += seq_printf(s,
+ "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n",
+ fctrl,
+ fctrl & DOC_CTRL_VIOLATION ? "protocol violation" : "-",
+ fctrl & DOC_CTRL_CE ? "active" : "inactive",
+ fctrl & DOC_CTRL_PROTECTION_ERROR ? "protection error" : "-",
+ fctrl & DOC_CTRL_SEQUENCE_ERROR ? "sequence error" : "-",
+ fctrl & DOC_CTRL_FLASHREADY ? "ready" : "not ready");
+ return pos;
+}
+DEBUGFS_RO_ATTR(flashcontrol, dbg_flashctrl_show);
+
+static int dbg_asicmode_show(struct seq_file *s, void *p)
+{
+ struct docg3 *docg3 = (struct docg3 *)s->private;
+
+ int pos = 0;
+ int pctrl = doc_register_readb(docg3, DOC_ASICMODE);
+ int mode = pctrl & 0x03;
+
+ pos += seq_printf(s,
+ "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (",
+ pctrl,
+ pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0,
+ pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0,
+ pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0,
+ pctrl & DOC_ASICMODE_MDWREN ? 1 : 0,
+ pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0,
+ mode >> 1, mode & 0x1);
+
+ switch (mode) {
+ case DOC_ASICMODE_RESET:
+ pos += seq_printf(s, "reset");
+ break;
+ case DOC_ASICMODE_NORMAL:
+ pos += seq_printf(s, "normal");
+ break;
+ case DOC_ASICMODE_POWERDOWN:
+ pos += seq_printf(s, "powerdown");
+ break;
+ }
+ pos += seq_printf(s, ")\n");
+ return pos;
+}
+DEBUGFS_RO_ATTR(asic_mode, dbg_asicmode_show);
+
+static int dbg_device_id_show(struct seq_file *s, void *p)
+{
+ struct docg3 *docg3 = (struct docg3 *)s->private;
+ int pos = 0;
+ int id = doc_register_readb(docg3, DOC_DEVICESELECT);
+
+ pos += seq_printf(s, "DeviceId = %d\n", id);
+ return pos;
+}
+DEBUGFS_RO_ATTR(device_id, dbg_device_id_show);
+
+static int dbg_protection_show(struct seq_file *s, void *p)
+{
+ struct docg3 *docg3 = (struct docg3 *)s->private;
+ int pos = 0;
+ int protect = doc_register_readb(docg3, DOC_PROTECTION);
+ int dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
+ int dps0_low = doc_register_readb(docg3, DOC_DPS0_ADDRLOW);
+ int dps0_high = doc_register_readb(docg3, DOC_DPS0_ADDRHIGH);
+ int dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
+ int dps1_low = doc_register_readb(docg3, DOC_DPS1_ADDRLOW);
+ int dps1_high = doc_register_readb(docg3, DOC_DPS1_ADDRHIGH);
+
+ pos += seq_printf(s, "Protection = 0x%02x (",
+ protect);
+ if (protect & DOC_PROTECT_FOUNDRY_OTP_LOCK)
+ pos += seq_printf(s, "FOUNDRY_OTP_LOCK,");
+ if (protect & DOC_PROTECT_CUSTOMER_OTP_LOCK)
+ pos += seq_printf(s, "CUSTOMER_OTP_LOCK,");
+ if (protect & DOC_PROTECT_LOCK_INPUT)
+ pos += seq_printf(s, "LOCK_INPUT,");
+ if (protect & DOC_PROTECT_STICKY_LOCK)
+ pos += seq_printf(s, "STICKY_LOCK,");
+ if (protect & DOC_PROTECT_PROTECTION_ENABLED)
+ pos += seq_printf(s, "PROTECTION ON,");
+ if (protect & DOC_PROTECT_IPL_DOWNLOAD_LOCK)
+ pos += seq_printf(s, "IPL_DOWNLOAD_LOCK,");
+ if (protect & DOC_PROTECT_PROTECTION_ERROR)
+ pos += seq_printf(s, "PROTECT_ERR,");
+ else
+ pos += seq_printf(s, "NO_PROTECT_ERR");
+ pos += seq_printf(s, ")\n");
+
+ pos += seq_printf(s, "DPS0 = 0x%02x : "
+ "Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, "
+ "WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
+ dps0, dps0_low, dps0_high,
+ !!(dps0 & DOC_DPS_OTP_PROTECTED),
+ !!(dps0 & DOC_DPS_READ_PROTECTED),
+ !!(dps0 & DOC_DPS_WRITE_PROTECTED),
+ !!(dps0 & DOC_DPS_HW_LOCK_ENABLED),
+ !!(dps0 & DOC_DPS_KEY_OK));
+ pos += seq_printf(s, "DPS1 = 0x%02x : "
+ "Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, "
+ "WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
+ dps1, dps1_low, dps1_high,
+ !!(dps1 & DOC_DPS_OTP_PROTECTED),
+ !!(dps1 & DOC_DPS_READ_PROTECTED),
+ !!(dps1 & DOC_DPS_WRITE_PROTECTED),
+ !!(dps1 & DOC_DPS_HW_LOCK_ENABLED),
+ !!(dps1 & DOC_DPS_KEY_OK));
+ return pos;
+}
+DEBUGFS_RO_ATTR(protection, dbg_protection_show);
+
+static int __init doc_dbg_register(struct docg3 *docg3)
+{
+ struct dentry *root, *entry;
+
+ root = debugfs_create_dir("docg3", NULL);
+ if (!root)
+ return -ENOMEM;
+
+ entry = debugfs_create_file("flashcontrol", S_IRUSR, root, docg3,
+ &flashcontrol_fops);
+ if (entry)
+ entry = debugfs_create_file("asic_mode", S_IRUSR, root,
+ docg3, &asic_mode_fops);
+ if (entry)
+ entry = debugfs_create_file("device_id", S_IRUSR, root,
+ docg3, &device_id_fops);
+ if (entry)
+ entry = debugfs_create_file("protection", S_IRUSR, root,
+ docg3, &protection_fops);
+ if (entry) {
+ docg3->debugfs_root = root;
+ return 0;
+ } else {
+ debugfs_remove_recursive(root);
+ return -ENOMEM;
+ }
+}
+
+static void __exit doc_dbg_unregister(struct docg3 *docg3)
+{
+ debugfs_remove_recursive(docg3->debugfs_root);
+}
+
+/**
+ * doc_set_driver_info - Fill the mtd_info structure and docg3 structure
+ * @chip_id: The chip ID of the supported chip
+ * @mtd: The structure to fill
+ */
+static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
+{
+ struct docg3 *docg3 = mtd->priv;
+ int cfg;
+
+ cfg = doc_register_readb(docg3, DOC_CONFIGURATION);
+ docg3->if_cfg = (cfg & DOC_CONF_IF_CFG ? 1 : 0);
+
+ switch (chip_id) {
+ case DOC_CHIPID_G3:
+ mtd->name = "DiskOnChip G3";
+ docg3->max_block = 2047;
+ break;
+ }
+ mtd->type = MTD_NANDFLASH;
+ /*
+ * Once write methods are added, the correct flags will be set.
+ * mtd->flags = MTD_CAP_NANDFLASH;
+ */
+ mtd->flags = MTD_CAP_ROM;
+ mtd->size = (docg3->max_block + 1) * DOC_LAYOUT_BLOCK_SIZE;
+ mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES;
+ mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
+ mtd->oobsize = DOC_LAYOUT_OOB_SIZE;
+ mtd->owner = THIS_MODULE;
+ mtd->erase = NULL;
+ mtd->point = NULL;
+ mtd->unpoint = NULL;
+ mtd->read = doc_read;
+ mtd->write = NULL;
+ mtd->read_oob = doc_read_oob;
+ mtd->write_oob = NULL;
+ mtd->sync = NULL;
+ mtd->block_isbad = doc_block_isbad;
+}
+
+/**
+ * doc_probe - Probe the IO space for a DiskOnChip G3 chip
+ * @pdev: platform device
+ *
+ * Probes for a G3 chip at the specified IO space in the platform data
+ * ressources.
+ *
+ * Returns 0 on success, -ENOMEM, -ENXIO on error
+ */
+static int __init docg3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct docg3 *docg3;
+ struct mtd_info *mtd;
+ struct resource *ress;
+ int ret, bbt_nbpages;
+ u16 chip_id, chip_id_inv;
+
+ ret = -ENOMEM;
+ docg3 = kzalloc(sizeof(struct docg3), GFP_KERNEL);
+ if (!docg3)
+ goto nomem1;
+ mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
+ if (!mtd)
+ goto nomem2;
+ mtd->priv = docg3;
+
+ ret = -ENXIO;
+ ress = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!ress) {
+ dev_err(dev, "No I/O memory resource defined\n");
+ goto noress;
+ }
+ docg3->base = ioremap(ress->start, DOC_IOSPACE_SIZE);
+
+ docg3->dev = &pdev->dev;
+ docg3->device_id = 0;
+ doc_set_device_id(docg3, docg3->device_id);
+ doc_set_asic_mode(docg3, DOC_ASICMODE_RESET);
+ doc_set_asic_mode(docg3, DOC_ASICMODE_NORMAL);
+
+ chip_id = doc_register_readw(docg3, DOC_CHIPID);
+ chip_id_inv = doc_register_readw(docg3, DOC_CHIPID_INV);
+
+ ret = -ENODEV;
+ if (chip_id != (u16)(~chip_id_inv)) {
+ doc_info("No device found at IO addr %p\n",
+ (void *)ress->start);
+ goto nochipfound;
+ }
+
+ switch (chip_id) {
+ case DOC_CHIPID_G3:
+ doc_info("Found a G3 DiskOnChip at addr %p\n",
+ (void *)ress->start);
+ break;
+ default:
+ doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
+ goto nochipfound;
+ }
+
+ doc_set_driver_info(chip_id, mtd);
+ platform_set_drvdata(pdev, mtd);
+
+ ret = -ENOMEM;
+ bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1,
+ 8 * DOC_LAYOUT_PAGE_SIZE);
+ docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL);
+ if (!docg3->bbt)
+ goto nochipfound;
+ doc_reload_bbt(docg3);
+
+ ret = mtd_device_parse_register(mtd, part_probes,
+ NULL, NULL, 0);
+ if (ret)
+ goto register_error;
+
+ doc_dbg_register(docg3);
+ return 0;
+
+register_error:
+ kfree(docg3->bbt);
+nochipfound:
+ iounmap(docg3->base);
+noress:
+ kfree(mtd);
+nomem2:
+ kfree(docg3);
+nomem1:
+ return ret;
+}
+
+/**
+ * docg3_release - Release the driver
+ * @pdev: the platform device
+ *
+ * Returns 0
+ */
+static int __exit docg3_release(struct platform_device *pdev)
+{
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct docg3 *docg3 = mtd->priv;
+
+ doc_dbg_unregister(docg3);
+ mtd_device_unregister(mtd);
+ iounmap(docg3->base);
+ kfree(docg3->bbt);
+ kfree(docg3);
+ kfree(mtd);
+ return 0;
+}
+
+static struct platform_driver g3_driver = {
+ .driver = {
+ .name = "docg3",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(docg3_release),
+};
+
+static int __init docg3_init(void)
+{
+ return platform_driver_probe(&g3_driver, docg3_probe);
+}
+module_init(docg3_init);
+
+
+static void __exit docg3_exit(void)
+{
+ platform_driver_unregister(&g3_driver);
+}
+module_exit(docg3_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
+MODULE_DESCRIPTION("MTD driver for DiskOnChip G3");
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
new file mode 100644
index 000000000000..0d407be24594
--- /dev/null
+++ b/drivers/mtd/devices/docg3.h
@@ -0,0 +1,297 @@
+/*
+ * Handles the M-Systems DiskOnChip G3 chip
+ *
+ * Copyright (C) 2011 Robert Jarzmik
+ *
+ * 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 _MTD_DOCG3_H
+#define _MTD_DOCG3_H
+
+/*
+ * Flash memory areas :
+ * - 0x0000 .. 0x07ff : IPL
+ * - 0x0800 .. 0x0fff : Data area
+ * - 0x1000 .. 0x17ff : Registers
+ * - 0x1800 .. 0x1fff : Unknown
+ */
+#define DOC_IOSPACE_IPL 0x0000
+#define DOC_IOSPACE_DATA 0x0800
+#define DOC_IOSPACE_SIZE 0x2000
+
+/*
+ * DOC G3 layout and adressing scheme
+ * A page address for the block "b", plane "P" and page "p":
+ * address = [bbbb bPpp pppp]
+ */
+
+#define DOC_ADDR_PAGE_MASK 0x3f
+#define DOC_ADDR_BLOCK_SHIFT 6
+#define DOC_LAYOUT_NBPLANES 2
+#define DOC_LAYOUT_PAGES_PER_BLOCK 64
+#define DOC_LAYOUT_PAGE_SIZE 512
+#define DOC_LAYOUT_OOB_SIZE 16
+#define DOC_LAYOUT_WEAR_SIZE 8
+#define DOC_LAYOUT_PAGE_OOB_SIZE \
+ (DOC_LAYOUT_PAGE_SIZE + DOC_LAYOUT_OOB_SIZE)
+#define DOC_LAYOUT_WEAR_OFFSET (DOC_LAYOUT_PAGE_OOB_SIZE * 2)
+#define DOC_LAYOUT_BLOCK_SIZE \
+ (DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_PAGE_SIZE)
+#define DOC_ECC_BCH_SIZE 7
+#define DOC_ECC_BCH_COVERED_BYTES \
+ (DOC_LAYOUT_PAGE_SIZE + DOC_LAYOUT_OOB_PAGEINFO_SZ + \
+ DOC_LAYOUT_OOB_HAMMING_SZ + DOC_LAYOUT_OOB_BCH_SZ)
+
+/*
+ * Blocks distribution
+ */
+#define DOC_LAYOUT_BLOCK_BBT 0
+#define DOC_LAYOUT_BLOCK_OTP 0
+#define DOC_LAYOUT_BLOCK_FIRST_DATA 6
+
+#define DOC_LAYOUT_PAGE_BBT 4
+
+/*
+ * Extra page OOB (16 bytes wide) layout
+ */
+#define DOC_LAYOUT_OOB_PAGEINFO_OFS 0
+#define DOC_LAYOUT_OOB_HAMMING_OFS 7
+#define DOC_LAYOUT_OOB_BCH_OFS 8
+#define DOC_LAYOUT_OOB_UNUSED_OFS 15
+#define DOC_LAYOUT_OOB_PAGEINFO_SZ 7
+#define DOC_LAYOUT_OOB_HAMMING_SZ 1
+#define DOC_LAYOUT_OOB_BCH_SZ 7
+#define DOC_LAYOUT_OOB_UNUSED_SZ 1
+
+
+#define DOC_CHIPID_G3 0x200
+#define DOC_ERASE_MARK 0xaa
+/*
+ * Flash registers
+ */
+#define DOC_CHIPID 0x1000
+#define DOC_TEST 0x1004
+#define DOC_BUSLOCK 0x1006
+#define DOC_ENDIANCONTROL 0x1008
+#define DOC_DEVICESELECT 0x100a
+#define DOC_ASICMODE 0x100c
+#define DOC_CONFIGURATION 0x100e
+#define DOC_INTERRUPTCONTROL 0x1010
+#define DOC_READADDRESS 0x101a
+#define DOC_DATAEND 0x101e
+#define DOC_INTERRUPTSTATUS 0x1020
+
+#define DOC_FLASHSEQUENCE 0x1032
+#define DOC_FLASHCOMMAND 0x1034
+#define DOC_FLASHADDRESS 0x1036
+#define DOC_FLASHCONTROL 0x1038
+#define DOC_NOP 0x103e
+
+#define DOC_ECCCONF0 0x1040
+#define DOC_ECCCONF1 0x1042
+#define DOC_ECCPRESET 0x1044
+#define DOC_HAMMINGPARITY 0x1046
+#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 1))
+
+#define DOC_PROTECTION 0x1056
+#define DOC_DPS0_ADDRLOW 0x1060
+#define DOC_DPS0_ADDRHIGH 0x1062
+#define DOC_DPS1_ADDRLOW 0x1064
+#define DOC_DPS1_ADDRHIGH 0x1066
+#define DOC_DPS0_STATUS 0x106c
+#define DOC_DPS1_STATUS 0x106e
+
+#define DOC_ASICMODECONFIRM 0x1072
+#define DOC_CHIPID_INV 0x1074
+
+/*
+ * Flash sequences
+ * A sequence is preset before one or more commands are input to the chip.
+ */
+#define DOC_SEQ_RESET 0x00
+#define DOC_SEQ_PAGE_SIZE_532 0x03
+#define DOC_SEQ_SET_MODE 0x09
+#define DOC_SEQ_READ 0x12
+#define DOC_SEQ_SET_PLANE1 0x0e
+#define DOC_SEQ_SET_PLANE2 0x10
+#define DOC_SEQ_PAGE_SETUP 0x1d
+
+/*
+ * Flash commands
+ */
+#define DOC_CMD_READ_PLANE1 0x00
+#define DOC_CMD_SET_ADDR_READ 0x05
+#define DOC_CMD_READ_ALL_PLANES 0x30
+#define DOC_CMD_READ_PLANE2 0x50
+#define DOC_CMD_READ_FLASH 0xe0
+#define DOC_CMD_PAGE_SIZE_532 0x3c
+
+#define DOC_CMD_PROG_BLOCK_ADDR 0x60
+#define DOC_CMD_PROG_CYCLE1 0x80
+#define DOC_CMD_PROG_CYCLE2 0x10
+#define DOC_CMD_ERASECYCLE2 0xd0
+
+#define DOC_CMD_RELIABLE_MODE 0x22
+#define DOC_CMD_FAST_MODE 0xa2
+
+#define DOC_CMD_RESET 0xff
+
+/*
+ * Flash register : DOC_FLASHCONTROL
+ */
+#define DOC_CTRL_VIOLATION 0x20
+#define DOC_CTRL_CE 0x10
+#define DOC_CTRL_UNKNOWN_BITS 0x08
+#define DOC_CTRL_PROTECTION_ERROR 0x04
+#define DOC_CTRL_SEQUENCE_ERROR 0x02
+#define DOC_CTRL_FLASHREADY 0x01
+
+/*
+ * Flash register : DOC_ASICMODE
+ */
+#define DOC_ASICMODE_RESET 0x00
+#define DOC_ASICMODE_NORMAL 0x01
+#define DOC_ASICMODE_POWERDOWN 0x02
+#define DOC_ASICMODE_MDWREN 0x04
+#define DOC_ASICMODE_BDETCT_RESET 0x08
+#define DOC_ASICMODE_RSTIN_RESET 0x10
+#define DOC_ASICMODE_RAM_WE 0x20
+
+/*
+ * Flash register : DOC_ECCCONF0
+ */
+#define DOC_ECCCONF0_READ_MODE 0x8000
+#define DOC_ECCCONF0_AUTO_ECC_ENABLE 0x4000
+#define DOC_ECCCONF0_HAMMING_ENABLE 0x1000
+#define DOC_ECCCONF0_BCH_ENABLE 0x0800
+#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff
+
+/*
+ * Flash register : DOC_ECCCONF1
+ */
+#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80
+#define DOC_ECCCONF1_UNKOWN1 0x40
+#define DOC_ECCCONF1_UNKOWN2 0x20
+#define DOC_ECCCONF1_UNKOWN3 0x10
+#define DOC_ECCCONF1_HAMMING_BITS_MASK 0x0f
+
+/*
+ * Flash register : DOC_PROTECTION
+ */
+#define DOC_PROTECT_FOUNDRY_OTP_LOCK 0x01
+#define DOC_PROTECT_CUSTOMER_OTP_LOCK 0x02
+#define DOC_PROTECT_LOCK_INPUT 0x04
+#define DOC_PROTECT_STICKY_LOCK 0x08
+#define DOC_PROTECT_PROTECTION_ENABLED 0x10
+#define DOC_PROTECT_IPL_DOWNLOAD_LOCK 0x20
+#define DOC_PROTECT_PROTECTION_ERROR 0x80
+
+/*
+ * Flash register : DOC_DPS0_STATUS and DOC_DPS1_STATUS
+ */
+#define DOC_DPS_OTP_PROTECTED 0x01
+#define DOC_DPS_READ_PROTECTED 0x02
+#define DOC_DPS_WRITE_PROTECTED 0x04
+#define DOC_DPS_HW_LOCK_ENABLED 0x08
+#define DOC_DPS_KEY_OK 0x80
+
+/*
+ * Flash register : DOC_CONFIGURATION
+ */
+#define DOC_CONF_IF_CFG 0x80
+#define DOC_CONF_MAX_ID_MASK 0x30
+#define DOC_CONF_VCCQ_3V 0x01
+
+/*
+ * Flash register : DOC_READADDRESS
+ */
+#define DOC_READADDR_INC 0x8000
+#define DOC_READADDR_ONE_BYTE 0x4000
+#define DOC_READADDR_ADDR_MASK 0x1fff
+
+/**
+ * struct docg3 - DiskOnChip driver private data
+ * @dev: the device currently under control
+ * @base: mapped IO space
+ * @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3)
+ * @if_cfg: if true, reads are on 16bits, else reads are on 8bits
+ * @bbt: bad block table cache
+ * @debugfs_root: debugfs root node
+ */
+struct docg3 {
+ struct device *dev;
+ void __iomem *base;
+ unsigned int device_id:4;
+ unsigned int if_cfg:1;
+ int max_block;
+ u8 *bbt;
+ struct dentry *debugfs_root;
+};
+
+#define doc_err(fmt, arg...) dev_err(docg3->dev, (fmt), ## arg)
+#define doc_info(fmt, arg...) dev_info(docg3->dev, (fmt), ## arg)
+#define doc_dbg(fmt, arg...) dev_dbg(docg3->dev, (fmt), ## arg)
+#define doc_vdbg(fmt, arg...) dev_vdbg(docg3->dev, (fmt), ## arg)
+
+#define DEBUGFS_RO_ATTR(name, show_fct) \
+ static int name##_open(struct inode *inode, struct file *file) \
+ { return single_open(file, show_fct, inode->i_private); } \
+ static const struct file_operations name##_fops = { \
+ .owner = THIS_MODULE, \
+ .open = name##_open, \
+ .llseek = seq_lseek, \
+ .read = seq_read, \
+ .release = single_release \
+ };
+#endif
+
+/*
+ * Trace events part
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM docg3
+
+#if !defined(_MTD_DOCG3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define _MTD_DOCG3_TRACE
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(docg3_io,
+ TP_PROTO(int op, int width, u16 reg, int val),
+ TP_ARGS(op, width, reg, val),
+ TP_STRUCT__entry(
+ __field(int, op)
+ __field(unsigned char, width)
+ __field(u16, reg)
+ __field(int, val)),
+ TP_fast_assign(
+ __entry->op = op;
+ __entry->width = width;
+ __entry->reg = reg;
+ __entry->val = val;),
+ TP_printk("docg3: %s%02d reg=%04x, val=%04x",
+ __entry->op ? "write" : "read", __entry->width,
+ __entry->reg, __entry->val)
+ );
+#endif
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE docg3
+#include <trace/define_trace.h>
diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c
index d374603493a7..45116bb30297 100644
--- a/drivers/mtd/devices/docprobe.c
+++ b/drivers/mtd/devices/docprobe.c
@@ -50,11 +50,6 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/doc2000.h>
-/* Where to look for the devices? */
-#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
-#define CONFIG_MTD_DOCPROBE_ADDRESS 0
-#endif
-
static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
module_param(doc_config_location, ulong, 0);
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c
index 772a0ff89e0f..3a11ea628e58 100644
--- a/drivers/mtd/devices/lart.c
+++ b/drivers/mtd/devices/lart.c
@@ -34,9 +34,6 @@
/* debugging */
//#define LART_DEBUG
-/* partition support */
-#define HAVE_PARTITIONS
-
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -44,9 +41,7 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mtd/mtd.h>
-#ifdef HAVE_PARTITIONS
#include <linux/mtd/partitions.h>
-#endif
#ifndef CONFIG_SA1100_LART
#error This is for LART architecture only
@@ -598,7 +593,6 @@ static struct mtd_erase_region_info erase_regions[] = {
}
};
-#ifdef HAVE_PARTITIONS
static struct mtd_partition lart_partitions[] = {
/* blob */
{
@@ -619,7 +613,7 @@ static struct mtd_partition lart_partitions[] = {
.size = INITRD_LEN, /* MTDPART_SIZ_FULL */
}
};
-#endif
+#define NUM_PARTITIONS ARRAY_SIZE(lart_partitions)
static int __init lart_flash_init (void)
{
@@ -668,7 +662,6 @@ static int __init lart_flash_init (void)
result,mtd.eraseregions[result].erasesize,mtd.eraseregions[result].erasesize / 1024,
result,mtd.eraseregions[result].numblocks);
-#ifdef HAVE_PARTITIONS
printk ("\npartitions = %d\n", ARRAY_SIZE(lart_partitions));
for (result = 0; result < ARRAY_SIZE(lart_partitions); result++)
@@ -681,25 +674,16 @@ static int __init lart_flash_init (void)
result,lart_partitions[result].offset,
result,lart_partitions[result].size,lart_partitions[result].size / 1024);
#endif
-#endif
-#ifndef HAVE_PARTITIONS
- result = mtd_device_register(&mtd, NULL, 0);
-#else
result = mtd_device_register(&mtd, lart_partitions,
ARRAY_SIZE(lart_partitions));
-#endif
return (result);
}
static void __exit lart_flash_exit (void)
{
-#ifndef HAVE_PARTITIONS
- mtd_device_unregister(&mtd);
-#else
mtd_device_unregister(&mtd);
-#endif
}
module_init (lart_flash_init);
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 35180e475c4c..884904d3f9d2 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -30,6 +30,7 @@
#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
+#include <linux/of_platform.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
@@ -88,7 +89,6 @@ struct m25p {
struct spi_device *spi;
struct mutex lock;
struct mtd_info mtd;
- unsigned partitioned:1;
u16 page_size;
u16 addr_width;
u8 erase_opcode;
@@ -209,9 +209,8 @@ static int wait_till_ready(struct m25p *flash)
*/
static int erase_chip(struct m25p *flash)
{
- DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %lldKiB\n",
- dev_name(&flash->spi->dev), __func__,
- (long long)(flash->mtd.size >> 10));
+ pr_debug("%s: %s %lldKiB\n", dev_name(&flash->spi->dev), __func__,
+ (long long)(flash->mtd.size >> 10));
/* Wait until finished previous write command. */
if (wait_till_ready(flash))
@@ -250,9 +249,8 @@ static int m25p_cmdsz(struct m25p *flash)
*/
static int erase_sector(struct m25p *flash, u32 offset)
{
- DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n",
- dev_name(&flash->spi->dev), __func__,
- flash->mtd.erasesize / 1024, offset);
+ pr_debug("%s: %s %dKiB at 0x%08x\n", dev_name(&flash->spi->dev),
+ __func__, flash->mtd.erasesize / 1024, offset);
/* Wait until finished previous write command. */
if (wait_till_ready(flash))
@@ -286,9 +284,9 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
u32 addr,len;
uint32_t rem;
- DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%llx, len %lld\n",
- dev_name(&flash->spi->dev), __func__, "at",
- (long long)instr->addr, (long long)instr->len);
+ pr_debug("%s: %s at 0x%llx, len %lld\n", dev_name(&flash->spi->dev),
+ __func__, (long long)instr->addr,
+ (long long)instr->len);
/* sanity checks */
if (instr->addr + instr->len > flash->mtd.size)
@@ -348,9 +346,8 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
struct spi_transfer t[2];
struct spi_message m;
- DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
- dev_name(&flash->spi->dev), __func__, "from",
- (u32)from, len);
+ pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
+ __func__, (u32)from, len);
/* sanity checks */
if (!len)
@@ -417,9 +414,8 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
struct spi_transfer t[2];
struct spi_message m;
- DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
- dev_name(&flash->spi->dev), __func__, "to",
- (u32)to, len);
+ pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
+ __func__, (u32)to, len);
*retlen = 0;
@@ -510,9 +506,8 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t actual;
int cmd_sz, ret;
- DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
- dev_name(&flash->spi->dev), __func__, "to",
- (u32)to, len);
+ pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
+ __func__, (u32)to, len);
*retlen = 0;
@@ -661,6 +656,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) },
{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) },
+ { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
{ "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
{ "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) },
@@ -671,6 +667,7 @@ static const struct spi_device_id m25p_ids[] = {
/* EON -- en25xxx */
{ "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) },
{ "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) },
+ { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
/* Intel/Numonyx -- xxxs33b */
@@ -788,8 +785,8 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi)
*/
tmp = spi_write_then_read(spi, &code, 1, id, 5);
if (tmp < 0) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
- dev_name(&spi->dev), tmp);
+ pr_debug("%s: error %d reading JEDEC ID\n",
+ dev_name(&spi->dev), tmp);
return ERR_PTR(tmp);
}
jedec = id[0];
@@ -825,8 +822,12 @@ static int __devinit m25p_probe(struct spi_device *spi)
struct m25p *flash;
struct flash_info *info;
unsigned i;
- struct mtd_partition *parts = NULL;
- int nr_parts = 0;
+ struct mtd_part_parser_data ppdata;
+
+#ifdef CONFIG_MTD_OF_PARTS
+ if (!of_device_is_available(spi->dev.of_node))
+ return -ENODEV;
+#endif
/* Platform data helps sort out which chip type we have, as
* well as how this board partitions it. If we don't have
@@ -928,6 +929,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
if (info->flags & M25P_NO_ERASE)
flash->mtd.flags |= MTD_NO_ERASE;
+ ppdata.of_node = spi->dev.of_node;
flash->mtd.dev.parent = &spi->dev;
flash->page_size = info->page_size;
@@ -945,8 +947,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name,
(long long)flash->mtd.size >> 10);
- DEBUG(MTD_DEBUG_LEVEL2,
- "mtd .name = %s, .size = 0x%llx (%lldMiB) "
+ pr_debug("mtd .name = %s, .size = 0x%llx (%lldMiB) "
".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
flash->mtd.name,
(long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
@@ -955,8 +956,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
if (flash->mtd.numeraseregions)
for (i = 0; i < flash->mtd.numeraseregions; i++)
- DEBUG(MTD_DEBUG_LEVEL2,
- "mtd.eraseregions[%d] = { .offset = 0x%llx, "
+ pr_debug("mtd.eraseregions[%d] = { .offset = 0x%llx, "
".erasesize = 0x%.8x (%uKiB), "
".numblocks = %d }\n",
i, (long long)flash->mtd.eraseregions[i].offset,
@@ -968,41 +968,9 @@ static int __devinit m25p_probe(struct spi_device *spi)
/* partitions should match sector boundaries; and it may be good to
* use readonly partitions for writeprotected sectors (BP2..BP0).
*/
- if (mtd_has_cmdlinepart()) {
- static const char *part_probes[]
- = { "cmdlinepart", NULL, };
-
- nr_parts = parse_mtd_partitions(&flash->mtd,
- part_probes, &parts, 0);
- }
-
- if (nr_parts <= 0 && data && data->parts) {
- parts = data->parts;
- nr_parts = data->nr_parts;
- }
-
-#ifdef CONFIG_MTD_OF_PARTS
- if (nr_parts <= 0 && spi->dev.of_node) {
- nr_parts = of_mtd_parse_partitions(&spi->dev,
- spi->dev.of_node, &parts);
- }
-#endif
-
- if (nr_parts > 0) {
- for (i = 0; i < nr_parts; i++) {
- DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
- "{.name = %s, .offset = 0x%llx, "
- ".size = 0x%llx (%lldKiB) }\n",
- i, parts[i].name,
- (long long)parts[i].offset,
- (long long)parts[i].size,
- (long long)(parts[i].size >> 10));
- }
- flash->partitioned = 1;
- }
-
- return mtd_device_register(&flash->mtd, parts, nr_parts) == 1 ?
- -ENODEV : 0;
+ return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,
+ data ? data->parts : NULL,
+ data ? data->nr_parts : 0);
}
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 13749d458a31..d75c7af18a63 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -17,6 +17,8 @@
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/math64.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
@@ -24,7 +26,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
-
/*
* DataFlash is a kind of SPI flash. Most AT45 chips have two buffers in
* each chip, which may be used for double buffered I/O; but this driver
@@ -98,6 +99,16 @@ struct dataflash {
struct mtd_info mtd;
};
+#ifdef CONFIG_OF
+static const struct of_device_id dataflash_dt_ids[] = {
+ { .compatible = "atmel,at45", },
+ { .compatible = "atmel,dataflash", },
+ { /* sentinel */ }
+};
+#else
+#define dataflash_dt_ids NULL
+#endif
+
/* ......................................................................... */
/*
@@ -122,7 +133,7 @@ static int dataflash_waitready(struct spi_device *spi)
for (;;) {
status = dataflash_status(spi);
if (status < 0) {
- DEBUG(MTD_DEBUG_LEVEL1, "%s: status %d?\n",
+ pr_debug("%s: status %d?\n",
dev_name(&spi->dev), status);
status = 0;
}
@@ -149,7 +160,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
uint8_t *command;
uint32_t rem;
- DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%llx len 0x%llx\n",
+ pr_debug("%s: erase addr=0x%llx len 0x%llx\n",
dev_name(&spi->dev), (long long)instr->addr,
(long long)instr->len);
@@ -187,7 +198,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
command[2] = (uint8_t)(pageaddr >> 8);
command[3] = 0;
- DEBUG(MTD_DEBUG_LEVEL3, "ERASE %s: (%x) %x %x %x [%i]\n",
+ pr_debug("ERASE %s: (%x) %x %x %x [%i]\n",
do_block ? "block" : "page",
command[0], command[1], command[2], command[3],
pageaddr);
@@ -238,8 +249,8 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
uint8_t *command;
int status;
- DEBUG(MTD_DEBUG_LEVEL2, "%s: read 0x%x..0x%x\n",
- dev_name(&priv->spi->dev), (unsigned)from, (unsigned)(from + len));
+ pr_debug("%s: read 0x%x..0x%x\n", dev_name(&priv->spi->dev),
+ (unsigned)from, (unsigned)(from + len));
*retlen = 0;
@@ -255,7 +266,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
command = priv->command;
- DEBUG(MTD_DEBUG_LEVEL3, "READ: (%x) %x %x %x\n",
+ pr_debug("READ: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);
spi_message_init(&msg);
@@ -287,7 +298,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
*retlen = msg.actual_length - 8;
status = 0;
} else
- DEBUG(MTD_DEBUG_LEVEL1, "%s: read %x..%x --> %d\n",
+ pr_debug("%s: read %x..%x --> %d\n",
dev_name(&priv->spi->dev),
(unsigned)from, (unsigned)(from + len),
status);
@@ -314,7 +325,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
int status = -EINVAL;
uint8_t *command;
- DEBUG(MTD_DEBUG_LEVEL2, "%s: write 0x%x..0x%x\n",
+ pr_debug("%s: write 0x%x..0x%x\n",
dev_name(&spi->dev), (unsigned)to, (unsigned)(to + len));
*retlen = 0;
@@ -340,7 +351,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
mutex_lock(&priv->lock);
while (remaining > 0) {
- DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n",
+ pr_debug("write @ %i:%i len=%i\n",
pageaddr, offset, writelen);
/* REVISIT:
@@ -368,12 +379,12 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
command[2] = (addr & 0x0000FF00) >> 8;
command[3] = 0;
- DEBUG(MTD_DEBUG_LEVEL3, "TRANSFER: (%x) %x %x %x\n",
+ pr_debug("TRANSFER: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);
status = spi_sync(spi, &msg);
if (status < 0)
- DEBUG(MTD_DEBUG_LEVEL1, "%s: xfer %u -> %d \n",
+ pr_debug("%s: xfer %u -> %d\n",
dev_name(&spi->dev), addr, status);
(void) dataflash_waitready(priv->spi);
@@ -386,7 +397,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
command[2] = (addr & 0x0000FF00) >> 8;
command[3] = (addr & 0x000000FF);
- DEBUG(MTD_DEBUG_LEVEL3, "PROGRAM: (%x) %x %x %x\n",
+ pr_debug("PROGRAM: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);
x[1].tx_buf = writebuf;
@@ -395,7 +406,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
status = spi_sync(spi, &msg);
spi_transfer_del(x + 1);
if (status < 0)
- DEBUG(MTD_DEBUG_LEVEL1, "%s: pgm %u/%u -> %d \n",
+ pr_debug("%s: pgm %u/%u -> %d\n",
dev_name(&spi->dev), addr, writelen, status);
(void) dataflash_waitready(priv->spi);
@@ -410,12 +421,12 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
command[2] = (addr & 0x0000FF00) >> 8;
command[3] = 0;
- DEBUG(MTD_DEBUG_LEVEL3, "COMPARE: (%x) %x %x %x\n",
+ pr_debug("COMPARE: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);
status = spi_sync(spi, &msg);
if (status < 0)
- DEBUG(MTD_DEBUG_LEVEL1, "%s: compare %u -> %d \n",
+ pr_debug("%s: compare %u -> %d\n",
dev_name(&spi->dev), addr, status);
status = dataflash_waitready(priv->spi);
@@ -634,11 +645,10 @@ add_dataflash_otp(struct spi_device *spi, char *name,
{
struct dataflash *priv;
struct mtd_info *device;
+ struct mtd_part_parser_data ppdata;
struct flash_platform_data *pdata = spi->dev.platform_data;
char *otp_tag = "";
int err = 0;
- struct mtd_partition *parts;
- int nr_parts = 0;
priv = kzalloc(sizeof *priv, GFP_KERNEL);
if (!priv)
@@ -677,28 +687,11 @@ add_dataflash_otp(struct spi_device *spi, char *name,
pagesize, otp_tag);
dev_set_drvdata(&spi->dev, priv);
- if (mtd_has_cmdlinepart()) {
- static const char *part_probes[] = { "cmdlinepart", NULL, };
-
- nr_parts = parse_mtd_partitions(device, part_probes, &parts,
- 0);
- }
+ ppdata.of_node = spi->dev.of_node;
+ err = mtd_device_parse_register(device, NULL, &ppdata,
+ pdata ? pdata->parts : NULL,
+ pdata ? pdata->nr_parts : 0);
- if (nr_parts <= 0 && pdata && pdata->parts) {
- parts = pdata->parts;
- nr_parts = pdata->nr_parts;
- }
-
- if (nr_parts > 0) {
- priv->partitioned = 1;
- err = mtd_device_register(device, parts, nr_parts);
- goto out;
- }
-
- if (mtd_device_register(device, NULL, 0) == 1)
- err = -ENODEV;
-
-out:
if (!err)
return 0;
@@ -787,7 +780,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
*/
tmp = spi_write_then_read(spi, &code, 1, id, 3);
if (tmp < 0) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
+ pr_debug("%s: error %d reading JEDEC ID\n",
dev_name(&spi->dev), tmp);
return ERR_PTR(tmp);
}
@@ -804,7 +797,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
tmp < ARRAY_SIZE(dataflash_data);
tmp++, info++) {
if (info->jedec_id == jedec) {
- DEBUG(MTD_DEBUG_LEVEL1, "%s: OTP, sector protect%s\n",
+ pr_debug("%s: OTP, sector protect%s\n",
dev_name(&spi->dev),
(info->flags & SUP_POW2PS)
? ", binary pagesize" : ""
@@ -812,8 +805,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
if (info->flags & SUP_POW2PS) {
status = dataflash_status(spi);
if (status < 0) {
- DEBUG(MTD_DEBUG_LEVEL1,
- "%s: status error %d\n",
+ pr_debug("%s: status error %d\n",
dev_name(&spi->dev), status);
return ERR_PTR(status);
}
@@ -878,7 +870,7 @@ static int __devinit dataflash_probe(struct spi_device *spi)
*/
status = dataflash_status(spi);
if (status <= 0 || status == 0xff) {
- DEBUG(MTD_DEBUG_LEVEL1, "%s: status error %d\n",
+ pr_debug("%s: status error %d\n",
dev_name(&spi->dev), status);
if (status == 0 || status == 0xff)
status = -ENODEV;
@@ -914,14 +906,14 @@ static int __devinit dataflash_probe(struct spi_device *spi)
break;
/* obsolete AT45DB1282 not (yet?) supported */
default:
- DEBUG(MTD_DEBUG_LEVEL1, "%s: unsupported device (%x)\n",
- dev_name(&spi->dev), status & 0x3c);
+ pr_debug("%s: unsupported device (%x)\n", dev_name(&spi->dev),
+ status & 0x3c);
status = -ENODEV;
}
if (status < 0)
- DEBUG(MTD_DEBUG_LEVEL1, "%s: add_dataflash --> %d\n",
- dev_name(&spi->dev), status);
+ pr_debug("%s: add_dataflash --> %d\n", dev_name(&spi->dev),
+ status);
return status;
}
@@ -931,7 +923,7 @@ static int __devexit dataflash_remove(struct spi_device *spi)
struct dataflash *flash = dev_get_drvdata(&spi->dev);
int status;
- DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", dev_name(&spi->dev));
+ pr_debug("%s: remove\n", dev_name(&spi->dev));
status = mtd_device_unregister(&flash->mtd);
if (status == 0) {
@@ -946,6 +938,7 @@ static struct spi_driver dataflash_driver = {
.name = "mtd_dataflash",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
+ .of_match_table = dataflash_dt_ids,
},
.probe = dataflash_probe,
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c
index 1e2c430aaad2..d38ef3bffe8d 100644
--- a/drivers/mtd/devices/sst25l.c
+++ b/drivers/mtd/devices/sst25l.c
@@ -5,7 +5,7 @@
*
* Copyright © 2009 Bluewater Systems Ltd
* Author: Andre Renaud <andre@bluewatersys.com>
- * Author: Ryan Mallon <ryan@bluewatersys.com>
+ * Author: Ryan Mallon
*
* Based on m25p80.c
*
@@ -52,8 +52,6 @@ struct sst25l_flash {
struct spi_device *spi;
struct mutex lock;
struct mtd_info mtd;
-
- int partitioned;
};
struct flash_info {
@@ -381,8 +379,6 @@ static int __devinit sst25l_probe(struct spi_device *spi)
struct sst25l_flash *flash;
struct flash_platform_data *data;
int ret, i;
- struct mtd_partition *parts = NULL;
- int nr_parts = 0;
flash_info = sst25l_match_device(spi);
if (!flash_info)
@@ -414,8 +410,7 @@ static int __devinit sst25l_probe(struct spi_device *spi)
dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name,
(long long)flash->mtd.size >> 10);
- DEBUG(MTD_DEBUG_LEVEL2,
- "mtd .name = %s, .size = 0x%llx (%lldMiB) "
+ pr_debug("mtd .name = %s, .size = 0x%llx (%lldMiB) "
".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
flash->mtd.name,
(long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
@@ -423,37 +418,10 @@ static int __devinit sst25l_probe(struct spi_device *spi)
flash->mtd.numeraseregions);
- if (mtd_has_cmdlinepart()) {
- static const char *part_probes[] = {"cmdlinepart", NULL};
-
- nr_parts = parse_mtd_partitions(&flash->mtd,
- part_probes,
- &parts, 0);
- }
-
- if (nr_parts <= 0 && data && data->parts) {
- parts = data->parts;
- nr_parts = data->nr_parts;
- }
-
- if (nr_parts > 0) {
- for (i = 0; i < nr_parts; i++) {
- DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
- "{.name = %s, .offset = 0x%llx, "
- ".size = 0x%llx (%lldKiB) }\n",
- i, parts[i].name,
- (long long)parts[i].offset,
- (long long)parts[i].size,
- (long long)(parts[i].size >> 10));
- }
-
- flash->partitioned = 1;
- return mtd_device_register(&flash->mtd, parts,
- nr_parts);
- }
-
- ret = mtd_device_register(&flash->mtd, NULL, 0);
- if (ret == 1) {
+ ret = mtd_device_parse_register(&flash->mtd, NULL, 0,
+ data ? data->parts : NULL,
+ data ? data->nr_parts : 0);
+ if (ret) {
kfree(flash);
dev_set_drvdata(&spi->dev, NULL);
return -ENODEV;
@@ -498,5 +466,5 @@ module_exit(sst25l_exit);
MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips");
MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, "
- "Ryan Mallon <ryan@bluewatersys.com>");
+ "Ryan Mallon");
MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c
index 037b399df3f1..c7382bb686c6 100644
--- a/drivers/mtd/ftl.c
+++ b/drivers/mtd/ftl.c
@@ -339,7 +339,7 @@ static int erase_xfer(partition_t *part,
struct erase_info *erase;
xfer = &part->XferInfo[xfernum];
- DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
+ pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
xfer->state = XFER_ERASING;
/* Is there a free erase slot? Always in MTD. */
@@ -415,7 +415,7 @@ static int prepare_xfer(partition_t *part, int i)
xfer = &part->XferInfo[i];
xfer->state = XFER_FAILED;
- DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
+ pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
/* Write the transfer unit header */
header = part->header;
@@ -476,7 +476,7 @@ static int copy_erase_unit(partition_t *part, uint16_t srcunit,
eun = &part->EUNInfo[srcunit];
xfer = &part->XferInfo[xferunit];
- DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n",
+ pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
eun->Offset, xfer->Offset);
@@ -598,7 +598,7 @@ static int copy_erase_unit(partition_t *part, uint16_t srcunit,
unit with the fewest erases, and usually pick the data unit with
the most deleted blocks. But with a small probability, pick the
oldest data unit instead. This means that we generally postpone
- the next reclaimation as long as possible, but shuffle static
+ the next reclamation as long as possible, but shuffle static
stuff around a bit for wear leveling.
======================================================================*/
@@ -609,8 +609,8 @@ static int reclaim_block(partition_t *part)
uint32_t best;
int queued, ret;
- DEBUG(0, "ftl_cs: reclaiming space...\n");
- DEBUG(3, "NumTransferUnits == %x\n", part->header.NumTransferUnits);
+ pr_debug("ftl_cs: reclaiming space...\n");
+ pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
/* Pick the least erased transfer unit */
best = 0xffffffff; xfer = 0xffff;
do {
@@ -618,22 +618,22 @@ static int reclaim_block(partition_t *part)
for (i = 0; i < part->header.NumTransferUnits; i++) {
int n=0;
if (part->XferInfo[i].state == XFER_UNKNOWN) {
- DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i);
+ pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
n=1;
erase_xfer(part, i);
}
if (part->XferInfo[i].state == XFER_ERASING) {
- DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i);
+ pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
n=1;
queued = 1;
}
else if (part->XferInfo[i].state == XFER_ERASED) {
- DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i);
+ pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
n=1;
prepare_xfer(part, i);
}
if (part->XferInfo[i].state == XFER_PREPARED) {
- DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i);
+ pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
n=1;
if (part->XferInfo[i].EraseCount <= best) {
best = part->XferInfo[i].EraseCount;
@@ -641,12 +641,12 @@ static int reclaim_block(partition_t *part)
}
}
if (!n)
- DEBUG(3,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
+ pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
}
if (xfer == 0xffff) {
if (queued) {
- DEBUG(1, "ftl_cs: waiting for transfer "
+ pr_debug("ftl_cs: waiting for transfer "
"unit to be prepared...\n");
if (part->mbd.mtd->sync)
part->mbd.mtd->sync(part->mbd.mtd);
@@ -656,7 +656,7 @@ static int reclaim_block(partition_t *part)
printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
"suitable transfer units!\n");
else
- DEBUG(1, "ftl_cs: reclaim failed: no "
+ pr_debug("ftl_cs: reclaim failed: no "
"suitable transfer units!\n");
return -EIO;
@@ -666,7 +666,7 @@ static int reclaim_block(partition_t *part)
eun = 0;
if ((jiffies % shuffle_freq) == 0) {
- DEBUG(1, "ftl_cs: recycling freshest block...\n");
+ pr_debug("ftl_cs: recycling freshest block...\n");
best = 0xffffffff;
for (i = 0; i < part->DataUnits; i++)
if (part->EUNInfo[i].EraseCount <= best) {
@@ -686,7 +686,7 @@ static int reclaim_block(partition_t *part)
printk(KERN_NOTICE "ftl_cs: reclaim failed: "
"no free blocks!\n");
else
- DEBUG(1,"ftl_cs: reclaim failed: "
+ pr_debug("ftl_cs: reclaim failed: "
"no free blocks!\n");
return -EIO;
@@ -771,7 +771,7 @@ static uint32_t find_free(partition_t *part)
printk(KERN_NOTICE "ftl_cs: bad free list!\n");
return 0;
}
- DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk, eun);
+ pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
return blk;
} /* find_free */
@@ -791,7 +791,7 @@ static int ftl_read(partition_t *part, caddr_t buffer,
int ret;
size_t offset, retlen;
- DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
+ pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
part, sector, nblocks);
if (!(part->state & FTL_FORMATTED)) {
printk(KERN_NOTICE "ftl_cs: bad partition\n");
@@ -840,7 +840,7 @@ static int set_bam_entry(partition_t *part, uint32_t log_addr,
int ret;
size_t retlen, offset;
- DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
+ pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
part, log_addr, virt_addr);
bsize = 1 << part->header.EraseUnitSize;
eun = log_addr / bsize;
@@ -905,7 +905,7 @@ static int ftl_write(partition_t *part, caddr_t buffer,
int ret;
size_t retlen, offset;
- DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
+ pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
part, sector, nblocks);
if (!(part->state & FTL_FORMATTED)) {
printk(KERN_NOTICE "ftl_cs: bad partition\n");
@@ -1011,7 +1011,7 @@ static int ftl_discardsect(struct mtd_blktrans_dev *dev,
partition_t *part = (void *)dev;
uint32_t bsize = 1 << part->header.EraseUnitSize;
- DEBUG(1, "FTL erase sector %ld for %d sectors\n",
+ pr_debug("FTL erase sector %ld for %d sectors\n",
sector, nr_sects);
while (nr_sects) {
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c
index d7592e67d048..dd034efd1875 100644
--- a/drivers/mtd/inftlcore.c
+++ b/drivers/mtd/inftlcore.c
@@ -63,14 +63,12 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
return;
}
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
+ pr_debug("INFTL: add_mtd for %s\n", mtd->name);
inftl = kzalloc(sizeof(*inftl), GFP_KERNEL);
- if (!inftl) {
- printk(KERN_WARNING "INFTL: Out of memory for data structures\n");
+ if (!inftl)
return;
- }
inftl->mbd.mtd = mtd;
inftl->mbd.devnum = -1;
@@ -133,7 +131,7 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
{
struct INFTLrecord *inftl = (void *)dev;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum);
+ pr_debug("INFTL: remove_dev (i=%d)\n", dev->devnum);
del_mtd_blktrans_dev(dev);
@@ -154,7 +152,7 @@ int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
struct mtd_oob_ops ops;
int res;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = offs & (mtd->writesize - 1);
ops.ooblen = len;
ops.oobbuf = buf;
@@ -174,7 +172,7 @@ int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
struct mtd_oob_ops ops;
int res;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = offs & (mtd->writesize - 1);
ops.ooblen = len;
ops.oobbuf = buf;
@@ -194,7 +192,7 @@ static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
struct mtd_oob_ops ops;
int res;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = offs;
ops.ooblen = mtd->oobsize;
ops.oobbuf = oob;
@@ -215,16 +213,16 @@ static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
u16 pot = inftl->LastFreeEUN;
int silly = inftl->nb_blocks;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=%p,"
- "desperate=%d)\n", inftl, desperate);
+ pr_debug("INFTL: INFTL_findfreeblock(inftl=%p,desperate=%d)\n",
+ inftl, desperate);
/*
* Normally, we force a fold to happen before we run out of free
* blocks completely.
*/
if (!desperate && inftl->numfreeEUNs < 2) {
- DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free "
- "EUNs (%d)\n", inftl->numfreeEUNs);
+ pr_debug("INFTL: there are too few free EUNs (%d)\n",
+ inftl->numfreeEUNs);
return BLOCK_NIL;
}
@@ -259,8 +257,8 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
struct inftl_oob oob;
size_t retlen;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,"
- "pending=%d)\n", inftl, thisVUC, pendingblock);
+ pr_debug("INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,pending=%d)\n",
+ inftl, thisVUC, pendingblock);
memset(BlockMap, 0xff, sizeof(BlockMap));
memset(BlockDeleted, 0, sizeof(BlockDeleted));
@@ -323,8 +321,7 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
* Chain, and the Erase Unit into which we are supposed to be copying.
* Go for it.
*/
- DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
- thisVUC, targetEUN);
+ pr_debug("INFTL: folding chain %d into unit %d\n", thisVUC, targetEUN);
for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
unsigned char movebuf[SECTORSIZE];
@@ -349,14 +346,13 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) +
(block * SECTORSIZE), SECTORSIZE, &retlen,
movebuf);
- if (ret < 0 && ret != -EUCLEAN) {
+ if (ret < 0 && !mtd_is_bitflip(ret)) {
ret = mtd->read(mtd,
(inftl->EraseSize * BlockMap[block]) +
(block * SECTORSIZE), SECTORSIZE,
&retlen, movebuf);
if (ret != -EIO)
- DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
- "away on retry?\n");
+ pr_debug("INFTL: error went away on retry?\n");
}
memset(&oob, 0xff, sizeof(struct inftl_oob));
oob.b.Status = oob.b.Status1 = SECTOR_USED;
@@ -372,8 +368,7 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
* is important, by doing oldest first if we crash/reboot then it
* it is relatively simple to clean up the mess).
*/
- DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
- thisVUC);
+ pr_debug("INFTL: want to erase virtual chain %d\n", thisVUC);
for (;;) {
/* Find oldest unit in chain. */
@@ -421,7 +416,7 @@ static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
u16 ChainLength = 0, thislen;
u16 chain, EUN;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=%p,"
+ pr_debug("INFTL: INFTL_makefreeblock(inftl=%p,"
"pending=%d)\n", inftl, pendingblock);
for (chain = 0; chain < inftl->nb_blocks; chain++) {
@@ -484,8 +479,8 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
size_t retlen;
int silly, silly2 = 3;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=%p,"
- "block=%d)\n", inftl, block);
+ pr_debug("INFTL: INFTL_findwriteunit(inftl=%p,block=%d)\n",
+ inftl, block);
do {
/*
@@ -501,8 +496,8 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
blockofs, 8, &retlen, (char *)&bci);
status = bci.Status | bci.Status1;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
- "EUN %d is %x\n", block , writeEUN, status);
+ pr_debug("INFTL: status of block %d in EUN %d is %x\n",
+ block , writeEUN, status);
switch(status) {
case SECTOR_FREE:
@@ -555,9 +550,9 @@ hitused:
* Hopefully we free something, lets try again.
* This time we are desperate...
*/
- DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 "
- "to find free EUN to accommodate write to "
- "VUC %d\n", thisVUC);
+ pr_debug("INFTL: using desperate==1 to find free EUN "
+ "to accommodate write to VUC %d\n",
+ thisVUC);
writeEUN = INFTL_findfreeblock(inftl, 1);
if (writeEUN == BLOCK_NIL) {
/*
@@ -647,7 +642,7 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
struct inftl_bci bci;
size_t retlen;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=%p,"
+ pr_debug("INFTL: INFTL_trydeletechain(inftl=%p,"
"thisVUC=%d)\n", inftl, thisVUC);
memset(BlockUsed, 0, sizeof(BlockUsed));
@@ -711,7 +706,7 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
* For each block in the chain free it and make it available
* for future use. Erase from the oldest unit first.
*/
- DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC);
+ pr_debug("INFTL: deleting empty VUC %d\n", thisVUC);
for (;;) {
u16 *prevEUN = &inftl->VUtable[thisVUC];
@@ -719,7 +714,7 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
/* If the chain is all gone already, we're done */
if (thisEUN == BLOCK_NIL) {
- DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
+ pr_debug("INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
return;
}
@@ -731,7 +726,7 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
thisEUN = *prevEUN;
}
- DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
+ pr_debug("Deleting EUN %d from VUC %d\n",
thisEUN, thisVUC);
if (INFTL_formatblock(inftl, thisEUN) < 0) {
@@ -767,7 +762,7 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
size_t retlen;
struct inftl_bci bci;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=%p,"
+ pr_debug("INFTL: INFTL_deleteblock(inftl=%p,"
"block=%d)\n", inftl, block);
while (thisEUN < inftl->nb_blocks) {
@@ -826,7 +821,7 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
struct inftl_oob oob;
char *p, *pend;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld,"
+ pr_debug("INFTL: inftl_writeblock(inftl=%p,block=%ld,"
"buffer=%p)\n", inftl, block, buffer);
/* Is block all zero? */
@@ -876,7 +871,7 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
struct inftl_bci bci;
size_t retlen;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld,"
+ pr_debug("INFTL: inftl_readblock(inftl=%p,block=%ld,"
"buffer=%p)\n", inftl, block, buffer);
while (thisEUN < inftl->nb_blocks) {
@@ -922,7 +917,7 @@ foundit:
int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer);
/* Handle corrected bit flips gracefully */
- if (ret < 0 && ret != -EUCLEAN)
+ if (ret < 0 && !mtd_is_bitflip(ret))
return -EIO;
}
return 0;
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c
index 104052e774b0..2ff601f816ce 100644
--- a/drivers/mtd/inftlmount.c
+++ b/drivers/mtd/inftlmount.c
@@ -53,7 +53,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
struct INFTLPartition *ip;
size_t retlen;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=%p)\n", inftl);
+ pr_debug("INFTL: find_boot_record(inftl=%p)\n", inftl);
/*
* Assume logical EraseSize == physical erasesize for starting the
@@ -139,24 +139,20 @@ static int find_boot_record(struct INFTLrecord *inftl)
mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
-#ifdef CONFIG_MTD_DEBUG_VERBOSE
- if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {
- printk("INFTL: Media Header ->\n"
- " bootRecordID = %s\n"
- " NoOfBootImageBlocks = %d\n"
- " NoOfBinaryPartitions = %d\n"
- " NoOfBDTLPartitions = %d\n"
- " BlockMultiplerBits = %d\n"
- " FormatFlgs = %d\n"
- " OsakVersion = 0x%x\n"
- " PercentUsed = %d\n",
- mh->bootRecordID, mh->NoOfBootImageBlocks,
- mh->NoOfBinaryPartitions,
- mh->NoOfBDTLPartitions,
- mh->BlockMultiplierBits, mh->FormatFlags,
- mh->OsakVersion, mh->PercentUsed);
- }
-#endif
+ pr_debug("INFTL: Media Header ->\n"
+ " bootRecordID = %s\n"
+ " NoOfBootImageBlocks = %d\n"
+ " NoOfBinaryPartitions = %d\n"
+ " NoOfBDTLPartitions = %d\n"
+ " BlockMultiplerBits = %d\n"
+ " FormatFlgs = %d\n"
+ " OsakVersion = 0x%x\n"
+ " PercentUsed = %d\n",
+ mh->bootRecordID, mh->NoOfBootImageBlocks,
+ mh->NoOfBinaryPartitions,
+ mh->NoOfBDTLPartitions,
+ mh->BlockMultiplierBits, mh->FormatFlags,
+ mh->OsakVersion, mh->PercentUsed);
if (mh->NoOfBDTLPartitions == 0) {
printk(KERN_WARNING "INFTL: Media Header sanity check "
@@ -200,19 +196,15 @@ static int find_boot_record(struct INFTLrecord *inftl)
ip->spareUnits = le32_to_cpu(ip->spareUnits);
ip->Reserved0 = le32_to_cpu(ip->Reserved0);
-#ifdef CONFIG_MTD_DEBUG_VERBOSE
- if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {
- printk(" PARTITION[%d] ->\n"
- " virtualUnits = %d\n"
- " firstUnit = %d\n"
- " lastUnit = %d\n"
- " flags = 0x%x\n"
- " spareUnits = %d\n",
- i, ip->virtualUnits, ip->firstUnit,
- ip->lastUnit, ip->flags,
- ip->spareUnits);
- }
-#endif
+ pr_debug(" PARTITION[%d] ->\n"
+ " virtualUnits = %d\n"
+ " firstUnit = %d\n"
+ " lastUnit = %d\n"
+ " flags = 0x%x\n"
+ " spareUnits = %d\n",
+ i, ip->virtualUnits, ip->firstUnit,
+ ip->lastUnit, ip->flags,
+ ip->spareUnits);
if (ip->Reserved0 != ip->firstUnit) {
struct erase_info *instr = &inftl->instr;
@@ -375,7 +367,7 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
*
* Return: 0 when succeed, -1 on error.
*
- * ToDo: 1. Is it neceressary to check_free_sector after erasing ??
+ * ToDo: 1. Is it necessary to check_free_sector after erasing ??
*/
int INFTL_formatblock(struct INFTLrecord *inftl, int block)
{
@@ -385,8 +377,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
struct mtd_info *mtd = inftl->mbd.mtd;
int physblock;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p,"
- "block=%d)\n", inftl, block);
+ pr_debug("INFTL: INFTL_formatblock(inftl=%p,block=%d)\n", inftl, block);
memset(instr, 0, sizeof(struct erase_info));
@@ -476,30 +467,30 @@ void INFTL_dumptables(struct INFTLrecord *s)
{
int i;
- printk("-------------------------------------------"
+ pr_debug("-------------------------------------------"
"----------------------------------\n");
- printk("VUtable[%d] ->", s->nb_blocks);
+ pr_debug("VUtable[%d] ->", s->nb_blocks);
for (i = 0; i < s->nb_blocks; i++) {
if ((i % 8) == 0)
- printk("\n%04x: ", i);
- printk("%04x ", s->VUtable[i]);
+ pr_debug("\n%04x: ", i);
+ pr_debug("%04x ", s->VUtable[i]);
}
- printk("\n-------------------------------------------"
+ pr_debug("\n-------------------------------------------"
"----------------------------------\n");
- printk("PUtable[%d-%d=%d] ->", s->firstEUN, s->lastEUN, s->nb_blocks);
+ pr_debug("PUtable[%d-%d=%d] ->", s->firstEUN, s->lastEUN, s->nb_blocks);
for (i = 0; i <= s->lastEUN; i++) {
if ((i % 8) == 0)
- printk("\n%04x: ", i);
- printk("%04x ", s->PUtable[i]);
+ pr_debug("\n%04x: ", i);
+ pr_debug("%04x ", s->PUtable[i]);
}
- printk("\n-------------------------------------------"
+ pr_debug("\n-------------------------------------------"
"----------------------------------\n");
- printk("INFTL ->\n"
+ pr_debug("INFTL ->\n"
" EraseSize = %d\n"
" h/s/c = %d/%d/%d\n"
" numvunits = %d\n"
@@ -513,7 +504,7 @@ void INFTL_dumptables(struct INFTLrecord *s)
s->numvunits, s->firstEUN, s->lastEUN, s->numfreeEUNs,
s->LastFreeEUN, s->nb_blocks, s->nb_boot_blocks);
- printk("\n-------------------------------------------"
+ pr_debug("\n-------------------------------------------"
"----------------------------------\n");
}
@@ -521,25 +512,25 @@ void INFTL_dumpVUchains(struct INFTLrecord *s)
{
int logical, block, i;
- printk("-------------------------------------------"
+ pr_debug("-------------------------------------------"
"----------------------------------\n");
- printk("INFTL Virtual Unit Chains:\n");
+ pr_debug("INFTL Virtual Unit Chains:\n");
for (logical = 0; logical < s->nb_blocks; logical++) {
block = s->VUtable[logical];
if (block > s->nb_blocks)
continue;
- printk(" LOGICAL %d --> %d ", logical, block);
+ pr_debug(" LOGICAL %d --> %d ", logical, block);
for (i = 0; i < s->nb_blocks; i++) {
if (s->PUtable[block] == BLOCK_NIL)
break;
block = s->PUtable[block];
- printk("%d ", block);
+ pr_debug("%d ", block);
}
- printk("\n");
+ pr_debug("\n");
}
- printk("-------------------------------------------"
+ pr_debug("-------------------------------------------"
"----------------------------------\n");
}
@@ -555,7 +546,7 @@ int INFTL_mount(struct INFTLrecord *s)
int i;
u8 *ANACtable, ANAC;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=%p)\n", s);
+ pr_debug("INFTL: INFTL_mount(inftl=%p)\n", s);
/* Search for INFTL MediaHeader and Spare INFTL Media Header */
if (find_boot_record(s) < 0) {
@@ -585,7 +576,7 @@ int INFTL_mount(struct INFTLrecord *s)
* NOTEXPLORED state. Then at the end we will try to format it and
* mark it as free.
*/
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 1, explore each unit\n");
+ pr_debug("INFTL: pass 1, explore each unit\n");
for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) {
if (s->PUtable[first_block] != BLOCK_NOTEXPLORED)
continue;
@@ -717,17 +708,14 @@ int INFTL_mount(struct INFTLrecord *s)
logical_block = BLOCK_NIL;
}
-#ifdef CONFIG_MTD_DEBUG_VERBOSE
- if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
- INFTL_dumptables(s);
-#endif
+ INFTL_dumptables(s);
/*
* Second pass, check for infinite loops in chains. These are
* possible because we don't update the previous pointers when
* we fold chains. No big deal, just fix them up in PUtable.
*/
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 2, validate virtual chains\n");
+ pr_debug("INFTL: pass 2, validate virtual chains\n");
for (logical_block = 0; logical_block < s->numvunits; logical_block++) {
block = s->VUtable[logical_block];
last_block = BLOCK_NIL;
@@ -772,12 +760,8 @@ int INFTL_mount(struct INFTLrecord *s)
}
}
-#ifdef CONFIG_MTD_DEBUG_VERBOSE
- if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
- INFTL_dumptables(s);
- if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
- INFTL_dumpVUchains(s);
-#endif
+ INFTL_dumptables(s);
+ INFTL_dumpVUchains(s);
/*
* Third pass, format unreferenced blocks and init free block count.
@@ -785,7 +769,7 @@ int INFTL_mount(struct INFTLrecord *s)
s->numfreeEUNs = 0;
s->LastFreeEUN = BLOCK_NIL;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 3, format unused blocks\n");
+ pr_debug("INFTL: pass 3, format unused blocks\n");
for (block = s->firstEUN; block <= s->lastEUN; block++) {
if (s->PUtable[block] == BLOCK_NOTEXPLORED) {
printk("INFTL: unreferenced block %d, formatting it\n",
diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c
index 65655dd59e1f..1dca31d9a8b3 100644
--- a/drivers/mtd/lpddr/lpddr_cmds.c
+++ b/drivers/mtd/lpddr/lpddr_cmds.c
@@ -27,6 +27,7 @@
#include <linux/mtd/pfow.h>
#include <linux/mtd/qinfo.h>
#include <linux/slab.h>
+#include <linux/module.h>
static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len,
size_t *retlen, u_char *buf);
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index c0c328c5b133..8e0c4bf9f7fb 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -41,8 +41,6 @@ config MTD_PHYSMAP_START
are mapped on your particular target board. Refer to the
memory map which should hopefully be in the documentation for
your board.
- Ignore this option if you use run-time physmap configuration
- (i.e., run-time calling physmap_configure()).
config MTD_PHYSMAP_LEN
hex "Physical length of flash mapping"
@@ -55,8 +53,6 @@ config MTD_PHYSMAP_LEN
than the total amount of flash present. Refer to the memory
map which should hopefully be in the documentation for your
board.
- Ignore this option if you use run-time physmap configuration
- (i.e., run-time calling physmap_configure()).
config MTD_PHYSMAP_BANKWIDTH
int "Bank width in octets"
@@ -67,8 +63,6 @@ config MTD_PHYSMAP_BANKWIDTH
in octets. For example, if you have a data bus width of 32
bits, you would set the bus width octet value to 4. This is
used internally by the CFI drivers.
- Ignore this option if you use run-time physmap configuration
- (i.e., run-time calling physmap_configure()).
config MTD_PHYSMAP_OF
tristate "Flash device in physical memory map based on OF description"
@@ -260,7 +254,6 @@ config MTD_BCM963XX
config MTD_LANTIQ
tristate "Lantiq SoC NOR support"
depends on LANTIQ
- select MTD_PARTITIONS
help
Support for NOR flash attached to the Lantiq SoC's External Bus Unit.
@@ -339,10 +332,6 @@ config MTD_SOLUTIONENGINE
This enables access to the flash chips on the Hitachi SolutionEngine and
similar boards. Say 'Y' if you are building a kernel for such a board.
-config MTD_ARM_INTEGRATOR
- tristate "CFI Flash device mapped on ARM Integrator/P720T"
- depends on ARM && MTD_CFI
-
config MTD_CDB89712
tristate "Cirrus CDB89712 evaluation board mappings"
depends on MTD_CFI && ARCH_CDB89712
@@ -398,13 +387,6 @@ config MTD_AUTCPU12
This enables access to the NV-RAM on autronix autcpu12 board.
If you have such a board, say 'Y'.
-config MTD_EDB7312
- tristate "CFI Flash device mapped on EDB7312"
- depends on ARCH_EDB7312 && MTD_CFI
- help
- This enables access to the CFI Flash on the Cogent EDB7312 board.
- If you have such a board, say 'Y' here.
-
config MTD_IMPA7
tristate "JEDEC Flash device mapped on impA7"
depends on ARM && MTD_JEDECPROBE
@@ -412,14 +394,6 @@ config MTD_IMPA7
This enables access to the NOR Flash on the impA7 board of
implementa GmbH. If you have such a board, say 'Y' here.
-config MTD_CEIVA
- tristate "JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame"
- depends on MTD_JEDECPROBE && ARCH_CEIVA
- help
- This enables access to the flash chips on the Ceiva/Polaroid
- PhotoMax Digital Picture Frame.
- If you have such a device, say 'Y'.
-
config MTD_H720X
tristate "Hynix evaluation board mappings"
depends on MTD_CFI && ( ARCH_H7201 || ARCH_H7202 )
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index cb48b11affff..45dcb8b14f22 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -19,7 +19,6 @@ obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
obj-$(CONFIG_MTD_MBX860) += mbx860.o
-obj-$(CONFIG_MTD_CEIVA) += ceiva.o
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
@@ -40,7 +39,6 @@ obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
obj-$(CONFIG_MTD_PCI) += pci.o
obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o
-obj-$(CONFIG_MTD_EDB7312) += edb7312.o
obj-$(CONFIG_MTD_IMPA7) += impa7.o
obj-$(CONFIG_MTD_FORTUNET) += fortunet.o
obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
diff --git a/drivers/mtd/maps/bcm963xx-flash.c b/drivers/mtd/maps/bcm963xx-flash.c
index 608967fe74c6..736ca10ca9f1 100644
--- a/drivers/mtd/maps/bcm963xx-flash.c
+++ b/drivers/mtd/maps/bcm963xx-flash.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c
index d4297a97e100..6d6b2b5674ee 100644
--- a/drivers/mtd/maps/bfin-async-flash.c
+++ b/drivers/mtd/maps/bfin-async-flash.c
@@ -41,7 +41,6 @@ struct async_state {
uint32_t flash_ambctl0, flash_ambctl1;
uint32_t save_ambctl0, save_ambctl1;
unsigned long irq_flags;
- struct mtd_partition *parts;
};
static void switch_to_flash(struct async_state *state)
@@ -142,7 +141,7 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev)
state->map.write = bfin_flash_write;
state->map.copy_to = bfin_flash_copy_to;
state->map.bankwidth = pdata->width;
- state->map.size = memory->end - memory->start + 1;
+ state->map.size = resource_size(memory);
state->map.virt = (void __iomem *)memory->start;
state->map.phys = memory->start;
state->map.map_priv_1 = (unsigned long)state;
@@ -165,18 +164,8 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev)
return -ENXIO;
}
- ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0);
- if (ret > 0) {
- pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n");
- mtd_device_register(state->mtd, pdata->parts, ret);
- state->parts = pdata->parts;
- } else if (pdata->nr_parts) {
- pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n");
- mtd_device_register(state->mtd, pdata->parts, pdata->nr_parts);
- } else {
- pr_devinit(KERN_NOTICE DRIVER_NAME ": no partition info available, registering whole flash at once\n");
- mtd_device_register(state->mtd, NULL, 0);
- }
+ mtd_device_parse_register(state->mtd, part_probe_types, 0,
+ pdata->parts, pdata->nr_parts);
platform_set_drvdata(pdev, state);
@@ -188,7 +177,6 @@ static int __devexit bfin_flash_remove(struct platform_device *pdev)
struct async_state *state = platform_get_drvdata(pdev);
gpio_free(state->enet_flash_pin);
mtd_device_unregister(state->mtd);
- kfree(state->parts);
map_destroy(state->mtd);
kfree(state);
return 0;
diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c
deleted file mode 100644
index 06f9c9815720..000000000000
--- a/drivers/mtd/maps/ceiva.c
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * Ceiva flash memory driver.
- * Copyright (C) 2002 Rob Scott <rscott@mtrob.fdns.net>
- *
- * Note: this driver supports jedec compatible devices. Modification
- * for CFI compatible devices should be straight forward: change
- * jedec_probe to cfi_probe.
- *
- * Based on: sa1100-flash.c, which has the following copyright:
- * Flash memory access on SA11x0 based devices
- *
- * (C) 2000 Nicolas Pitre <nico@fluxnic.net>
- *
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/concat.h>
-
-#include <mach/hardware.h>
-#include <asm/mach-types.h>
-#include <asm/io.h>
-#include <asm/sizes.h>
-
-/*
- * This isn't complete yet, so...
- */
-#define CONFIG_MTD_CEIVA_STATICMAP
-
-#ifdef CONFIG_MTD_CEIVA_STATICMAP
-/*
- * See include/linux/mtd/partitions.h for definition of the mtd_partition
- * structure.
- *
- * Please note:
- * 1. The flash size given should be the largest flash size that can
- * be accommodated.
- *
- * 2. The bus width must defined in clps_setup_flash.
- *
- * The MTD layer will detect flash chip aliasing and reduce the size of
- * the map accordingly.
- *
- */
-
-#ifdef CONFIG_ARCH_CEIVA
-/* Flash / Partition sizing */
-/* For the 28F8003, we use the block mapping to calcuate the sizes */
-#define MAX_SIZE_KiB (16 + 8 + 8 + 96 + (7*128))
-#define BOOT_PARTITION_SIZE_KiB (16)
-#define PARAMS_PARTITION_SIZE_KiB (8)
-#define KERNEL_PARTITION_SIZE_KiB (4*128)
-/* Use both remaining portion of first flash, and all of second flash */
-#define ROOT_PARTITION_SIZE_KiB (3*128) + (8*128)
-
-static struct mtd_partition ceiva_partitions[] = {
- {
- .name = "Ceiva BOOT partition",
- .size = BOOT_PARTITION_SIZE_KiB*1024,
- .offset = 0,
-
- },{
- .name = "Ceiva parameters partition",
- .size = PARAMS_PARTITION_SIZE_KiB*1024,
- .offset = (16 + 8) * 1024,
- },{
- .name = "Ceiva kernel partition",
- .size = (KERNEL_PARTITION_SIZE_KiB)*1024,
- .offset = 0x20000,
-
- },{
- .name = "Ceiva root filesystem partition",
- .offset = MTDPART_OFS_APPEND,
- .size = (ROOT_PARTITION_SIZE_KiB)*1024,
- }
-};
-#endif
-
-static int __init clps_static_partitions(struct mtd_partition **parts)
-{
- int nb_parts = 0;
-
-#ifdef CONFIG_ARCH_CEIVA
- if (machine_is_ceiva()) {
- *parts = ceiva_partitions;
- nb_parts = ARRAY_SIZE(ceiva_partitions);
- }
-#endif
- return nb_parts;
-}
-#endif
-
-struct clps_info {
- unsigned long base;
- unsigned long size;
- int width;
- void *vbase;
- struct map_info *map;
- struct mtd_info *mtd;
- struct resource *res;
-};
-
-#define NR_SUBMTD 4
-
-static struct clps_info info[NR_SUBMTD];
-
-static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info **rmtd)
-{
- struct mtd_info *subdev[nr];
- struct map_info *maps;
- int i, found = 0, ret = 0;
-
- /*
- * Allocate the map_info structs in one go.
- */
- maps = kzalloc(sizeof(struct map_info) * nr, GFP_KERNEL);
- if (!maps)
- return -ENOMEM;
- /*
- * Claim and then map the memory regions.
- */
- for (i = 0; i < nr; i++) {
- if (clps[i].base == (unsigned long)-1)
- break;
-
- clps[i].res = request_mem_region(clps[i].base, clps[i].size, "clps flash");
- if (!clps[i].res) {
- ret = -EBUSY;
- break;
- }
-
- clps[i].map = maps + i;
-
- clps[i].map->name = "clps flash";
- clps[i].map->phys = clps[i].base;
-
- clps[i].vbase = ioremap(clps[i].base, clps[i].size);
- if (!clps[i].vbase) {
- ret = -ENOMEM;
- break;
- }
-
- clps[i].map->virt = (void __iomem *)clps[i].vbase;
- clps[i].map->bankwidth = clps[i].width;
- clps[i].map->size = clps[i].size;
-
- simple_map_init(&clps[i].map);
-
- clps[i].mtd = do_map_probe("jedec_probe", clps[i].map);
- if (clps[i].mtd == NULL) {
- ret = -ENXIO;
- break;
- }
- clps[i].mtd->owner = THIS_MODULE;
- subdev[i] = clps[i].mtd;
-
- printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, "
- "%d-bit\n", clps[i].base, clps[i].mtd->size >> 20,
- clps[i].width * 8);
- found += 1;
- }
-
- /*
- * ENXIO is special. It means we didn't find a chip when
- * we probed. We need to tear down the mapping, free the
- * resource and mark it as such.
- */
- if (ret == -ENXIO) {
- iounmap(clps[i].vbase);
- clps[i].vbase = NULL;
- release_resource(clps[i].res);
- clps[i].res = NULL;
- }
-
- /*
- * If we found one device, don't bother with concat support.
- * If we found multiple devices, use concat if we have it
- * available, otherwise fail.
- */
- if (ret == 0 || ret == -ENXIO) {
- if (found == 1) {
- *rmtd = subdev[0];
- ret = 0;
- } else if (found > 1) {
- /*
- * We detected multiple devices. Concatenate
- * them together.
- */
- *rmtd = mtd_concat_create(subdev, found,
- "clps flash");
- if (*rmtd == NULL)
- ret = -ENXIO;
- }
- }
-
- /*
- * If we failed, clean up.
- */
- if (ret) {
- do {
- if (clps[i].mtd)
- map_destroy(clps[i].mtd);
- if (clps[i].vbase)
- iounmap(clps[i].vbase);
- if (clps[i].res)
- release_resource(clps[i].res);
- } while (i--);
-
- kfree(maps);
- }
-
- return ret;
-}
-
-static void __exit clps_destroy_mtd(struct clps_info *clps, struct mtd_info *mtd)
-{
- int i;
-
- mtd_device_unregister(mtd);
-
- if (mtd != clps[0].mtd)
- mtd_concat_destroy(mtd);
-
- for (i = NR_SUBMTD; i >= 0; i--) {
- if (clps[i].mtd)
- map_destroy(clps[i].mtd);
- if (clps[i].vbase)
- iounmap(clps[i].vbase);
- if (clps[i].res)
- release_resource(clps[i].res);
- }
- kfree(clps[0].map);
-}
-
-/*
- * We define the memory space, size, and width for the flash memory
- * space here.
- */
-
-static int __init clps_setup_flash(void)
-{
- int nr = 0;
-
-#ifdef CONFIG_ARCH_CEIVA
- if (machine_is_ceiva()) {
- info[0].base = CS0_PHYS_BASE;
- info[0].size = SZ_32M;
- info[0].width = CEIVA_FLASH_WIDTH;
- info[1].base = CS1_PHYS_BASE;
- info[1].size = SZ_32M;
- info[1].width = CEIVA_FLASH_WIDTH;
- nr = 2;
- }
-#endif
- return nr;
-}
-
-static struct mtd_partition *parsed_parts;
-static const char *probes[] = { "cmdlinepart", "RedBoot", NULL };
-
-static void __init clps_locate_partitions(struct mtd_info *mtd)
-{
- const char *part_type = NULL;
- int nr_parts = 0;
- do {
- /*
- * Partition selection stuff.
- */
- nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0);
- if (nr_parts > 0) {
- part_type = "command line";
- break;
- }
-#ifdef CONFIG_MTD_CEIVA_STATICMAP
- nr_parts = clps_static_partitions(&parsed_parts);
- if (nr_parts > 0) {
- part_type = "static";
- break;
- }
- printk("found: %d partitions\n", nr_parts);
-#endif
- } while (0);
-
- if (nr_parts == 0) {
- printk(KERN_NOTICE "clps flash: no partition info "
- "available, registering whole flash\n");
- mtd_device_register(mtd, NULL, 0);
- } else {
- printk(KERN_NOTICE "clps flash: using %s partition "
- "definition\n", part_type);
- mtd_device_register(mtd, parsed_parts, nr_parts);
- }
-
- /* Always succeeds. */
-}
-
-static void __exit clps_destroy_partitions(void)
-{
- kfree(parsed_parts);
-}
-
-static struct mtd_info *mymtd;
-
-static int __init clps_mtd_init(void)
-{
- int ret;
- int nr;
-
- nr = clps_setup_flash();
- if (nr < 0)
- return nr;
-
- ret = clps_setup_mtd(info, nr, &mymtd);
- if (ret)
- return ret;
-
- clps_locate_partitions(mymtd);
-
- return 0;
-}
-
-static void __exit clps_mtd_cleanup(void)
-{
- clps_destroy_mtd(info, mymtd);
- clps_destroy_partitions();
-}
-
-module_init(clps_mtd_init);
-module_exit(clps_mtd_cleanup);
-
-MODULE_AUTHOR("Rob Scott");
-MODULE_DESCRIPTION("Cirrus Logic JEDEC map driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c
index 7a9e1989c977..f43b365b848c 100644
--- a/drivers/mtd/maps/dc21285.c
+++ b/drivers/mtd/maps/dc21285.c
@@ -145,14 +145,10 @@ static struct map_info dc21285_map = {
/* Partition stuff */
-static struct mtd_partition *dc21285_parts;
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
static int __init init_dc21285(void)
{
-
- int nrparts;
-
/* Determine bankwidth */
switch (*CSR_SA110_CNTL & (3<<14)) {
case SA110_CNTL_ROMWIDTH_8:
@@ -200,8 +196,7 @@ static int __init init_dc21285(void)
dc21285_mtd->owner = THIS_MODULE;
- nrparts = parse_mtd_partitions(dc21285_mtd, probes, &dc21285_parts, 0);
- mtd_device_register(dc21285_mtd, dc21285_parts, nrparts);
+ mtd_device_parse_register(dc21285_mtd, probes, 0, NULL, 0);
if(machine_is_ebsa285()) {
/*
@@ -224,8 +219,6 @@ static int __init init_dc21285(void)
static void __exit cleanup_dc21285(void)
{
mtd_device_unregister(dc21285_mtd);
- if (dc21285_parts)
- kfree(dc21285_parts);
map_destroy(dc21285_mtd);
iounmap(dc21285_map.virt);
}
diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c
deleted file mode 100644
index fe42a212bb3e..000000000000
--- a/drivers/mtd/maps/edb7312.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Handle mapping of the NOR flash on Cogent EDB7312 boards
- *
- * Copyright 2002 SYSGO Real-Time Solutions GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#define WINDOW_ADDR 0x00000000 /* physical properties of flash */
-#define WINDOW_SIZE 0x01000000
-#define BUSWIDTH 2
-#define FLASH_BLOCKSIZE_MAIN 0x20000
-#define FLASH_NUMBLOCKS_MAIN 128
-/* can be "cfi_probe", "jedec_probe", "map_rom", NULL }; */
-#define PROBETYPES { "cfi_probe", NULL }
-
-#define MSG_PREFIX "EDB7312-NOR:" /* prefix for our printk()'s */
-#define MTDID "edb7312-nor" /* for mtdparts= partitioning */
-
-static struct mtd_info *mymtd;
-
-struct map_info edb7312nor_map = {
- .name = "NOR flash on EDB7312",
- .size = WINDOW_SIZE,
- .bankwidth = BUSWIDTH,
- .phys = WINDOW_ADDR,
-};
-
-/*
- * MTD partitioning stuff
- */
-static struct mtd_partition static_partitions[3] =
-{
- {
- .name = "ARMboot",
- .size = 0x40000,
- .offset = 0
- },
- {
- .name = "Kernel",
- .size = 0x200000,
- .offset = 0x40000
- },
- {
- .name = "RootFS",
- .size = 0xDC0000,
- .offset = 0x240000
- },
-};
-
-static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
-
-static int mtd_parts_nb = 0;
-static struct mtd_partition *mtd_parts = 0;
-
-static int __init init_edb7312nor(void)
-{
- static const char *rom_probe_types[] = PROBETYPES;
- const char **type;
- const char *part_type = 0;
-
- printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n",
- WINDOW_SIZE, WINDOW_ADDR);
- edb7312nor_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
-
- if (!edb7312nor_map.virt) {
- printk(MSG_PREFIX "failed to ioremap\n");
- return -EIO;
- }
-
- simple_map_init(&edb7312nor_map);
-
- mymtd = 0;
- type = rom_probe_types;
- for(; !mymtd && *type; type++) {
- mymtd = do_map_probe(*type, &edb7312nor_map);
- }
- if (mymtd) {
- mymtd->owner = THIS_MODULE;
-
- mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID);
- if (mtd_parts_nb > 0)
- part_type = "detected";
-
- if (mtd_parts_nb == 0) {
- mtd_parts = static_partitions;
- mtd_parts_nb = ARRAY_SIZE(static_partitions);
- part_type = "static";
- }
-
- if (mtd_parts_nb == 0)
- printk(KERN_NOTICE MSG_PREFIX "no partition info available\n");
- else
- printk(KERN_NOTICE MSG_PREFIX
- "using %s partition definition\n", part_type);
- /* Register the whole device first. */
- mtd_device_register(mymtd, NULL, 0);
- mtd_device_register(mymtd, mtd_parts, mtd_parts_nb);
- return 0;
- }
-
- iounmap((void *)edb7312nor_map.virt);
- return -ENXIO;
-}
-
-static void __exit cleanup_edb7312nor(void)
-{
- if (mymtd) {
- mtd_device_unregister(mymtd);
- map_destroy(mymtd);
- }
- if (edb7312nor_map.virt) {
- iounmap((void *)edb7312nor_map.virt);
- edb7312nor_map.virt = 0;
- }
-}
-
-module_init(init_edb7312nor);
-module_exit(cleanup_edb7312nor);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
-MODULE_DESCRIPTION("Generic configurable MTD map driver");
diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index 7568c5f8b8ae..1ec66f031c51 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -187,7 +187,6 @@ static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
*/
static int __devinit gpio_flash_probe(struct platform_device *pdev)
{
- int nr_parts;
size_t i, arr_size;
struct physmap_flash_data *pdata;
struct resource *memory;
@@ -252,20 +251,9 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
return -ENXIO;
}
- nr_parts = parse_mtd_partitions(state->mtd, part_probe_types,
- &pdata->parts, 0);
- if (nr_parts > 0) {
- pr_devinit(KERN_NOTICE PFX "Using commandline partition definition\n");
- kfree(pdata->parts);
- } else if (pdata->nr_parts) {
- pr_devinit(KERN_NOTICE PFX "Using board partition definition\n");
- nr_parts = pdata->nr_parts;
- } else {
- pr_devinit(KERN_NOTICE PFX "no partition info available, registering whole flash at once\n");
- nr_parts = 0;
- }
- mtd_device_register(state->mtd, pdata->parts, nr_parts);
+ mtd_device_parse_register(state->mtd, part_probe_types, 0,
+ pdata->parts, pdata->nr_parts);
return 0;
}
diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c
index 7f035860a36b..49c14187fc66 100644
--- a/drivers/mtd/maps/h720x-flash.c
+++ b/drivers/mtd/maps/h720x-flash.c
@@ -58,18 +58,11 @@ static struct mtd_partition h720x_partitions[] = {
#define NUM_PARTITIONS ARRAY_SIZE(h720x_partitions)
-static int nr_mtd_parts;
-static struct mtd_partition *mtd_parts;
-static const char *probes[] = { "cmdlinepart", NULL };
-
/*
* Initialize FLASH support
*/
static int __init h720x_mtd_init(void)
{
-
- char *part_type = NULL;
-
h720x_map.virt = ioremap(h720x_map.phys, h720x_map.size);
if (!h720x_map.virt) {
@@ -92,16 +85,8 @@ static int __init h720x_mtd_init(void)
if (mymtd) {
mymtd->owner = THIS_MODULE;
- nr_mtd_parts = parse_mtd_partitions(mymtd, probes, &mtd_parts, 0);
- if (nr_mtd_parts > 0)
- part_type = "command line";
- if (nr_mtd_parts <= 0) {
- mtd_parts = h720x_partitions;
- nr_mtd_parts = NUM_PARTITIONS;
- part_type = "builtin";
- }
- printk(KERN_INFO "Using %s partition table\n", part_type);
- mtd_device_register(mymtd, mtd_parts, nr_mtd_parts);
+ mtd_device_parse_register(mymtd, NULL, 0,
+ h720x_partitions, NUM_PARTITIONS);
return 0;
}
@@ -120,10 +105,6 @@ static void __exit h720x_mtd_cleanup(void)
map_destroy(mymtd);
}
- /* Free partition info, if commandline partition was used */
- if (mtd_parts && (mtd_parts != h720x_partitions))
- kfree (mtd_parts);
-
if (h720x_map.virt) {
iounmap((void *)h720x_map.virt);
h720x_map.virt = 0;
diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c
index 404a50cbafa0..f47aedb24366 100644
--- a/drivers/mtd/maps/impa7.c
+++ b/drivers/mtd/maps/impa7.c
@@ -49,7 +49,7 @@ static struct map_info impa7_map[NUM_FLASHBANKS] = {
/*
* MTD partitioning stuff
*/
-static struct mtd_partition static_partitions[] =
+static struct mtd_partition partitions[] =
{
{
.name = "FileSystem",
@@ -58,16 +58,10 @@ static struct mtd_partition static_partitions[] =
},
};
-static int mtd_parts_nb[NUM_FLASHBANKS];
-static struct mtd_partition *mtd_parts[NUM_FLASHBANKS];
-
-static const char *probes[] = { "cmdlinepart", NULL };
-
static int __init init_impa7(void)
{
static const char *rom_probe_types[] = PROBETYPES;
const char **type;
- const char *part_type = 0;
int i;
static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = {
{ WINDOW_ADDR0, WINDOW_SIZE0 },
@@ -97,23 +91,9 @@ static int __init init_impa7(void)
if (impa7_mtd[i]) {
impa7_mtd[i]->owner = THIS_MODULE;
devicesfound++;
- mtd_parts_nb[i] = parse_mtd_partitions(impa7_mtd[i],
- probes,
- &mtd_parts[i],
- 0);
- if (mtd_parts_nb[i] > 0) {
- part_type = "command line";
- } else {
- mtd_parts[i] = static_partitions;
- mtd_parts_nb[i] = ARRAY_SIZE(static_partitions);
- part_type = "static";
- }
-
- printk(KERN_NOTICE MSG_PREFIX
- "using %s partition definition\n",
- part_type);
- mtd_device_register(impa7_mtd[i],
- mtd_parts[i], mtd_parts_nb[i]);
+ mtd_device_parse_register(impa7_mtd[i], NULL, 0,
+ partitions,
+ ARRAY_SIZE(partitions));
}
else
iounmap((void *)impa7_map[i].virt);
diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c
index d2f47be8754b..08c239604ee4 100644
--- a/drivers/mtd/maps/intel_vr_nor.c
+++ b/drivers/mtd/maps/intel_vr_nor.c
@@ -44,7 +44,6 @@ struct vr_nor_mtd {
void __iomem *csr_base;
struct map_info map;
struct mtd_info *info;
- int nr_parts;
struct pci_dev *dev;
};
@@ -71,13 +70,9 @@ static void __devexit vr_nor_destroy_partitions(struct vr_nor_mtd *p)
static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p)
{
- struct mtd_partition *parts;
- static const char *part_probes[] = { "cmdlinepart", NULL };
-
/* register the flash bank */
/* partition the flash bank */
- p->nr_parts = parse_mtd_partitions(p->info, part_probes, &parts, 0);
- return mtd_device_register(p->info, parts, p->nr_parts);
+ return mtd_device_parse_register(p->info, NULL, 0, NULL, 0);
}
static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c
index c00b9175ba9e..437fcd2f352f 100644
--- a/drivers/mtd/maps/ixp2000.c
+++ b/drivers/mtd/maps/ixp2000.c
@@ -38,7 +38,6 @@
struct ixp2000_flash_info {
struct mtd_info *mtd;
struct map_info map;
- struct mtd_partition *partitions;
struct resource *res;
};
@@ -125,8 +124,6 @@ static int ixp2000_flash_remove(struct platform_device *dev)
if (info->map.map_priv_1)
iounmap((void *) info->map.map_priv_1);
- kfree(info->partitions);
-
if (info->res) {
release_resource(info->res);
kfree(info->res);
@@ -155,7 +152,7 @@ static int ixp2000_flash_probe(struct platform_device *dev)
if (!plat)
return -ENODEV;
- window_size = dev->resource->end - dev->resource->start + 1;
+ window_size = resource_size(dev->resource);
dev_info(&dev->dev, "Probe of IXP2000 flash(%d banks x %dMiB)\n",
ixp_data->nr_banks, ((u32)window_size >> 20));
@@ -194,16 +191,17 @@ static int ixp2000_flash_probe(struct platform_device *dev)
info->map.copy_to = ixp2000_flash_copy_to;
info->res = request_mem_region(dev->resource->start,
- dev->resource->end - dev->resource->start + 1,
- dev_name(&dev->dev));
+ resource_size(dev->resource),
+ dev_name(&dev->dev));
if (!info->res) {
dev_err(&dev->dev, "Could not reserve memory region\n");
err = -ENOMEM;
goto Error;
}
- info->map.map_priv_1 = (unsigned long) ioremap(dev->resource->start,
- dev->resource->end - dev->resource->start + 1);
+ info->map.map_priv_1 =
+ (unsigned long)ioremap(dev->resource->start,
+ resource_size(dev->resource));
if (!info->map.map_priv_1) {
dev_err(&dev->dev, "Failed to ioremap flash region\n");
err = -EIO;
@@ -228,13 +226,7 @@ static int ixp2000_flash_probe(struct platform_device *dev)
}
info->mtd->owner = THIS_MODULE;
- err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0);
- if (err > 0) {
- err = mtd_device_register(info->mtd, info->partitions, err);
- if(err)
- dev_err(&dev->dev, "Could not parse partitions\n");
- }
-
+ err = mtd_device_parse_register(info->mtd, probes, 0, NULL, 0);
if (err)
goto Error;
diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c
index 155b21942f47..30409015a3de 100644
--- a/drivers/mtd/maps/ixp4xx.c
+++ b/drivers/mtd/maps/ixp4xx.c
@@ -145,7 +145,6 @@ static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr)
struct ixp4xx_flash_info {
struct mtd_info *mtd;
struct map_info map;
- struct mtd_partition *partitions;
struct resource *res;
};
@@ -168,8 +167,6 @@ static int ixp4xx_flash_remove(struct platform_device *dev)
if (info->map.virt)
iounmap(info->map.virt);
- kfree(info->partitions);
-
if (info->res) {
release_resource(info->res);
kfree(info->res);
@@ -185,8 +182,6 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
{
struct flash_platform_data *plat = dev->dev.platform_data;
struct ixp4xx_flash_info *info;
- const char *part_type = NULL;
- int nr_parts = 0;
int err = -1;
if (!plat)
@@ -252,28 +247,12 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
/* Use the fast version */
info->map.write = ixp4xx_write16;
- nr_parts = parse_mtd_partitions(info->mtd, probes, &info->partitions,
- dev->resource->start);
- if (nr_parts > 0) {
- part_type = "dynamic";
- } else {
- info->partitions = plat->parts;
- nr_parts = plat->nr_parts;
- part_type = "static";
- }
- if (nr_parts == 0)
- printk(KERN_NOTICE "IXP4xx flash: no partition info "
- "available, registering whole flash\n");
- else
- printk(KERN_NOTICE "IXP4xx flash: using %s partition "
- "definition\n", part_type);
-
- err = mtd_device_register(info->mtd, info->partitions, nr_parts);
- if (err)
+ err = mtd_device_parse_register(info->mtd, probes, dev->resource->start,
+ plat->parts, plat->nr_parts);
+ if (err) {
printk(KERN_ERR "Could not parse partitions\n");
-
- if (err)
goto Error;
+ }
return 0;
diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c
index a90cabd7b84d..4f10e27ada55 100644
--- a/drivers/mtd/maps/lantiq-flash.c
+++ b/drivers/mtd/maps/lantiq-flash.c
@@ -107,16 +107,12 @@ ltq_copy_to(struct map_info *map, unsigned long to,
spin_unlock_irqrestore(&ebu_lock, flags);
}
-static const char const *part_probe_types[] = { "cmdlinepart", NULL };
-
static int __init
ltq_mtd_probe(struct platform_device *pdev)
{
struct physmap_flash_data *ltq_mtd_data = dev_get_platdata(&pdev->dev);
struct ltq_mtd *ltq_mtd;
- struct mtd_partition *parts;
struct resource *res;
- int nr_parts = 0;
struct cfi_private *cfi;
int err;
@@ -172,17 +168,8 @@ ltq_mtd_probe(struct platform_device *pdev)
cfi->addr_unlock1 ^= 1;
cfi->addr_unlock2 ^= 1;
- nr_parts = parse_mtd_partitions(ltq_mtd->mtd,
- part_probe_types, &parts, 0);
- if (nr_parts > 0) {
- dev_info(&pdev->dev,
- "using %d partitions from cmdline", nr_parts);
- } else {
- nr_parts = ltq_mtd_data->nr_parts;
- parts = ltq_mtd_data->parts;
- }
-
- err = add_mtd_partitions(ltq_mtd->mtd, parts, nr_parts);
+ err = mtd_device_parse_register(ltq_mtd->mtd, NULL, 0,
+ ltq_mtd_data->parts, ltq_mtd_data->nr_parts);
if (err) {
dev_err(&pdev->dev, "failed to add partitions\n");
goto err_destroy;
@@ -208,7 +195,7 @@ ltq_mtd_remove(struct platform_device *pdev)
if (ltq_mtd) {
if (ltq_mtd->mtd) {
- del_mtd_partitions(ltq_mtd->mtd);
+ mtd_device_unregister(ltq_mtd->mtd);
map_destroy(ltq_mtd->mtd);
}
if (ltq_mtd->map->virt)
diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c
index 5936c466e901..119baa7d7477 100644
--- a/drivers/mtd/maps/latch-addr-flash.c
+++ b/drivers/mtd/maps/latch-addr-flash.c
@@ -33,9 +33,6 @@ struct latch_addr_flash_info {
/* cache; could be found out of res */
unsigned long win_mask;
- int nr_parts;
- struct mtd_partition *parts;
-
spinlock_t lock;
};
@@ -97,8 +94,6 @@ static void lf_copy_from(struct map_info *map, void *to,
static char *rom_probe_types[] = { "cfi_probe", NULL };
-static char *part_probe_types[] = { "cmdlinepart", NULL };
-
static int latch_addr_flash_remove(struct platform_device *dev)
{
struct latch_addr_flash_info *info;
@@ -112,8 +107,6 @@ static int latch_addr_flash_remove(struct platform_device *dev)
latch_addr_data = dev->dev.platform_data;
if (info->mtd != NULL) {
- if (info->nr_parts)
- kfree(info->parts);
mtd_device_unregister(info->mtd);
map_destroy(info->mtd);
}
@@ -206,21 +199,8 @@ static int __devinit latch_addr_flash_probe(struct platform_device *dev)
}
info->mtd->owner = THIS_MODULE;
- err = parse_mtd_partitions(info->mtd, (const char **)part_probe_types,
- &info->parts, 0);
- if (err > 0) {
- mtd_device_register(info->mtd, info->parts, err);
- return 0;
- }
- if (latch_addr_data->nr_parts) {
- pr_notice("Using latch-addr-flash partition information\n");
- mtd_device_register(info->mtd,
- latch_addr_data->parts,
- latch_addr_data->nr_parts);
- return 0;
- }
-
- mtd_device_register(info->mtd, NULL, 0);
+ mtd_device_parse_register(info->mtd, NULL, 0,
+ latch_addr_data->parts, latch_addr_data->nr_parts);
return 0;
iounmap:
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index bbe168b65c26..e8e9fec23553 100644
--- a/drivers/mtd/maps/pcmciamtd.c
+++ b/drivers/mtd/maps/pcmciamtd.c
@@ -22,22 +22,6 @@
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
-#ifdef CONFIG_MTD_DEBUG
-static int debug = CONFIG_MTD_DEBUG_VERBOSE;
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy");
-#undef DEBUG
-#define DEBUG(n, format, arg...) \
- if (n <= debug) { \
- printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __func__ , ## arg); \
- }
-
-#else
-#undef DEBUG
-#define DEBUG(n, arg...)
-static const int debug = 0;
-#endif
-
#define info(format, arg...) printk(KERN_INFO "pcmciamtd: " format "\n" , ## arg)
#define DRIVER_DESC "PCMCIA Flash memory card driver"
@@ -105,13 +89,13 @@ static caddr_t remap_window(struct map_info *map, unsigned long to)
int ret;
if (!pcmcia_dev_present(dev->p_dev)) {
- DEBUG(1, "device removed");
+ pr_debug("device removed\n");
return 0;
}
offset = to & ~(dev->win_size-1);
if (offset != dev->offset) {
- DEBUG(2, "Remapping window from 0x%8.8x to 0x%8.8x",
+ pr_debug("Remapping window from 0x%8.8x to 0x%8.8x\n",
dev->offset, offset);
ret = pcmcia_map_mem_page(dev->p_dev, win, offset);
if (ret != 0)
@@ -132,7 +116,7 @@ static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs)
return d;
d.x[0] = readb(addr);
- DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02lx", ofs, addr, d.x[0]);
+ pr_debug("ofs = 0x%08lx (%p) data = 0x%02lx\n", ofs, addr, d.x[0]);
return d;
}
@@ -147,7 +131,7 @@ static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs)
return d;
d.x[0] = readw(addr);
- DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04lx", ofs, addr, d.x[0]);
+ pr_debug("ofs = 0x%08lx (%p) data = 0x%04lx\n", ofs, addr, d.x[0]);
return d;
}
@@ -157,7 +141,7 @@ static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long
struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
unsigned long win_size = dev->win_size;
- DEBUG(3, "to = %p from = %lu len = %zd", to, from, len);
+ pr_debug("to = %p from = %lu len = %zd\n", to, from, len);
while(len) {
int toread = win_size - (from & (win_size-1));
caddr_t addr;
@@ -169,7 +153,7 @@ static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long
if(!addr)
return;
- DEBUG(4, "memcpy from %p to %p len = %d", addr, to, toread);
+ pr_debug("memcpy from %p to %p len = %d\n", addr, to, toread);
memcpy_fromio(to, addr, toread);
len -= toread;
to += toread;
@@ -185,7 +169,7 @@ static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long
if(!addr)
return;
- DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02lx", adr, addr, d.x[0]);
+ pr_debug("adr = 0x%08lx (%p) data = 0x%02lx\n", adr, addr, d.x[0]);
writeb(d.x[0], addr);
}
@@ -196,7 +180,7 @@ static void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long
if(!addr)
return;
- DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04lx", adr, addr, d.x[0]);
+ pr_debug("adr = 0x%08lx (%p) data = 0x%04lx\n", adr, addr, d.x[0]);
writew(d.x[0], addr);
}
@@ -206,7 +190,7 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v
struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
unsigned long win_size = dev->win_size;
- DEBUG(3, "to = %lu from = %p len = %zd", to, from, len);
+ pr_debug("to = %lu from = %p len = %zd\n", to, from, len);
while(len) {
int towrite = win_size - (to & (win_size-1));
caddr_t addr;
@@ -218,7 +202,7 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v
if(!addr)
return;
- DEBUG(4, "memcpy from %p to %p len = %d", from, addr, towrite);
+ pr_debug("memcpy from %p to %p len = %d\n", from, addr, towrite);
memcpy_toio(addr, from, towrite);
len -= towrite;
to += towrite;
@@ -240,7 +224,7 @@ static map_word pcmcia_read8(struct map_info *map, unsigned long ofs)
return d;
d.x[0] = readb(win_base + ofs);
- DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02lx",
+ pr_debug("ofs = 0x%08lx (%p) data = 0x%02lx\n",
ofs, win_base + ofs, d.x[0]);
return d;
}
@@ -255,7 +239,7 @@ static map_word pcmcia_read16(struct map_info *map, unsigned long ofs)
return d;
d.x[0] = readw(win_base + ofs);
- DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04lx",
+ pr_debug("ofs = 0x%08lx (%p) data = 0x%04lx\n",
ofs, win_base + ofs, d.x[0]);
return d;
}
@@ -268,7 +252,7 @@ static void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from,
if(DEV_REMOVED(map))
return;
- DEBUG(3, "to = %p from = %lu len = %zd", to, from, len);
+ pr_debug("to = %p from = %lu len = %zd\n", to, from, len);
memcpy_fromio(to, win_base + from, len);
}
@@ -280,7 +264,7 @@ static void pcmcia_write8(struct map_info *map, map_word d, unsigned long adr)
if(DEV_REMOVED(map))
return;
- DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02lx",
+ pr_debug("adr = 0x%08lx (%p) data = 0x%02lx\n",
adr, win_base + adr, d.x[0]);
writeb(d.x[0], win_base + adr);
}
@@ -293,7 +277,7 @@ static void pcmcia_write16(struct map_info *map, map_word d, unsigned long adr)
if(DEV_REMOVED(map))
return;
- DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04lx",
+ pr_debug("adr = 0x%08lx (%p) data = 0x%04lx\n",
adr, win_base + adr, d.x[0]);
writew(d.x[0], win_base + adr);
}
@@ -306,7 +290,7 @@ static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *f
if(DEV_REMOVED(map))
return;
- DEBUG(3, "to = %lu from = %p len = %zd", to, from, len);
+ pr_debug("to = %lu from = %p len = %zd\n", to, from, len);
memcpy_toio(win_base + to, from, len);
}
@@ -316,7 +300,7 @@ static void pcmciamtd_set_vpp(struct map_info *map, int on)
struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
struct pcmcia_device *link = dev->p_dev;
- DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp);
+ pr_debug("dev = %p on = %d vpp = %d\n\n", dev, on, dev->vpp);
pcmcia_fixup_vpp(link, on ? dev->vpp : 0);
}
@@ -325,7 +309,7 @@ static void pcmciamtd_release(struct pcmcia_device *link)
{
struct pcmciamtd_dev *dev = link->priv;
- DEBUG(3, "link = 0x%p", link);
+ pr_debug("link = 0x%p\n", link);
if (link->resource[2]->end) {
if(dev->win_base) {
@@ -337,7 +321,6 @@ static void pcmciamtd_release(struct pcmcia_device *link)
}
-#ifdef CONFIG_MTD_DEBUG
static int pcmciamtd_cistpl_format(struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv_data)
@@ -347,7 +330,7 @@ static int pcmciamtd_cistpl_format(struct pcmcia_device *p_dev,
if (!pcmcia_parse_tuple(tuple, &parse)) {
cistpl_format_t *t = &parse.format;
(void)t; /* Shut up, gcc */
- DEBUG(2, "Format type: %u, Error Detection: %u, offset = %u, length =%u",
+ pr_debug("Format type: %u, Error Detection: %u, offset = %u, length =%u\n",
t->type, t->edc, t->offset, t->length);
}
return -ENOSPC;
@@ -363,12 +346,11 @@ static int pcmciamtd_cistpl_jedec(struct pcmcia_device *p_dev,
if (!pcmcia_parse_tuple(tuple, &parse)) {
cistpl_jedec_t *t = &parse.jedec;
for (i = 0; i < t->nid; i++)
- DEBUG(2, "JEDEC: 0x%02x 0x%02x",
+ pr_debug("JEDEC: 0x%02x 0x%02x\n",
t->id[i].mfr, t->id[i].info);
}
return -ENOSPC;
}
-#endif
static int pcmciamtd_cistpl_device(struct pcmcia_device *p_dev,
tuple_t *tuple,
@@ -382,14 +364,14 @@ static int pcmciamtd_cistpl_device(struct pcmcia_device *p_dev,
if (pcmcia_parse_tuple(tuple, &parse))
return -EINVAL;
- DEBUG(2, "Common memory:");
+ pr_debug("Common memory:\n");
dev->pcmcia_map.size = t->dev[0].size;
/* from here on: DEBUG only */
for (i = 0; i < t->ndev; i++) {
- DEBUG(2, "Region %d, type = %u", i, t->dev[i].type);
- DEBUG(2, "Region %d, wp = %u", i, t->dev[i].wp);
- DEBUG(2, "Region %d, speed = %u ns", i, t->dev[i].speed);
- DEBUG(2, "Region %d, size = %u bytes", i, t->dev[i].size);
+ pr_debug("Region %d, type = %u\n", i, t->dev[i].type);
+ pr_debug("Region %d, wp = %u\n", i, t->dev[i].wp);
+ pr_debug("Region %d, speed = %u ns\n", i, t->dev[i].speed);
+ pr_debug("Region %d, size = %u bytes\n", i, t->dev[i].size);
}
return 0;
}
@@ -409,12 +391,12 @@ static int pcmciamtd_cistpl_geo(struct pcmcia_device *p_dev,
dev->pcmcia_map.bankwidth = t->geo[0].buswidth;
/* from here on: DEBUG only */
for (i = 0; i < t->ngeo; i++) {
- DEBUG(2, "region: %d bankwidth = %u", i, t->geo[i].buswidth);
- DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block);
- DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block);
- DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block);
- DEBUG(2, "region: %d partition = %u", i, t->geo[i].partition);
- DEBUG(2, "region: %d interleave = %u", i, t->geo[i].interleave);
+ pr_debug("region: %d bankwidth = %u\n", i, t->geo[i].buswidth);
+ pr_debug("region: %d erase_block = %u\n", i, t->geo[i].erase_block);
+ pr_debug("region: %d read_block = %u\n", i, t->geo[i].read_block);
+ pr_debug("region: %d write_block = %u\n", i, t->geo[i].write_block);
+ pr_debug("region: %d partition = %u\n", i, t->geo[i].partition);
+ pr_debug("region: %d interleave = %u\n", i, t->geo[i].interleave);
}
return 0;
}
@@ -432,13 +414,11 @@ static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *p_dev
if (p_dev->prod_id[i])
strcat(dev->mtd_name, p_dev->prod_id[i]);
}
- DEBUG(2, "Found name: %s", dev->mtd_name);
+ pr_debug("Found name: %s\n", dev->mtd_name);
}
-#ifdef CONFIG_MTD_DEBUG
pcmcia_loop_tuple(p_dev, CISTPL_FORMAT, pcmciamtd_cistpl_format, NULL);
pcmcia_loop_tuple(p_dev, CISTPL_JEDEC_C, pcmciamtd_cistpl_jedec, NULL);
-#endif
pcmcia_loop_tuple(p_dev, CISTPL_DEVICE, pcmciamtd_cistpl_device, dev);
pcmcia_loop_tuple(p_dev, CISTPL_DEVICE_GEO, pcmciamtd_cistpl_geo, dev);
@@ -450,12 +430,12 @@ static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *p_dev
if(force_size) {
dev->pcmcia_map.size = force_size << 20;
- DEBUG(2, "size forced to %dM", force_size);
+ pr_debug("size forced to %dM\n", force_size);
}
if(bankwidth) {
dev->pcmcia_map.bankwidth = bankwidth;
- DEBUG(2, "bankwidth forced to %d", bankwidth);
+ pr_debug("bankwidth forced to %d\n", bankwidth);
}
dev->pcmcia_map.name = dev->mtd_name;
@@ -464,7 +444,7 @@ static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *p_dev
*new_name = 1;
}
- DEBUG(1, "Device: Size: %lu Width:%d Name: %s",
+ pr_debug("Device: Size: %lu Width:%d Name: %s\n",
dev->pcmcia_map.size,
dev->pcmcia_map.bankwidth << 3, dev->mtd_name);
}
@@ -479,7 +459,7 @@ static int pcmciamtd_config(struct pcmcia_device *link)
static char *probes[] = { "jedec_probe", "cfi_probe" };
int new_name = 0;
- DEBUG(3, "link=0x%p", link);
+ pr_debug("link=0x%p\n", link);
card_settings(dev, link, &new_name);
@@ -512,11 +492,11 @@ static int pcmciamtd_config(struct pcmcia_device *link)
do {
int ret;
- DEBUG(2, "requesting window with size = %luKiB memspeed = %d",
+ pr_debug("requesting window with size = %luKiB memspeed = %d\n",
(unsigned long) resource_size(link->resource[2]) >> 10,
mem_speed);
ret = pcmcia_request_window(link, link->resource[2], mem_speed);
- DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size);
+ pr_debug("ret = %d dev->win_size = %d\n", ret, dev->win_size);
if(ret) {
j++;
link->resource[2]->start = 0;
@@ -524,21 +504,21 @@ static int pcmciamtd_config(struct pcmcia_device *link)
force_size << 20 : MAX_PCMCIA_ADDR;
link->resource[2]->end >>= j;
} else {
- DEBUG(2, "Got window of size %luKiB", (unsigned long)
+ pr_debug("Got window of size %luKiB\n", (unsigned long)
resource_size(link->resource[2]) >> 10);
dev->win_size = resource_size(link->resource[2]);
break;
}
} while (link->resource[2]->end >= 0x1000);
- DEBUG(2, "dev->win_size = %d", dev->win_size);
+ pr_debug("dev->win_size = %d\n", dev->win_size);
if(!dev->win_size) {
dev_err(&dev->p_dev->dev, "Cannot allocate memory window\n");
pcmciamtd_release(link);
return -ENODEV;
}
- DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10);
+ pr_debug("Allocated a window of %dKiB\n", dev->win_size >> 10);
/* Get write protect status */
dev->win_base = ioremap(link->resource[2]->start,
@@ -549,7 +529,7 @@ static int pcmciamtd_config(struct pcmcia_device *link)
pcmciamtd_release(link);
return -ENODEV;
}
- DEBUG(1, "mapped window dev = %p @ %pR, base = %p",
+ pr_debug("mapped window dev = %p @ %pR, base = %p\n",
dev, link->resource[2], dev->win_base);
dev->offset = 0;
@@ -564,7 +544,7 @@ static int pcmciamtd_config(struct pcmcia_device *link)
}
link->config_index = 0;
- DEBUG(2, "Setting Configuration");
+ pr_debug("Setting Configuration\n");
ret = pcmcia_enable_device(link);
if (ret != 0) {
if (dev->win_base) {
@@ -580,17 +560,17 @@ static int pcmciamtd_config(struct pcmcia_device *link)
mtd = do_map_probe("map_rom", &dev->pcmcia_map);
} else {
for(i = 0; i < ARRAY_SIZE(probes); i++) {
- DEBUG(1, "Trying %s", probes[i]);
+ pr_debug("Trying %s\n", probes[i]);
mtd = do_map_probe(probes[i], &dev->pcmcia_map);
if(mtd)
break;
- DEBUG(1, "FAILED: %s", probes[i]);
+ pr_debug("FAILED: %s\n", probes[i]);
}
}
if(!mtd) {
- DEBUG(1, "Can not find an MTD");
+ pr_debug("Can not find an MTD\n");
pcmciamtd_release(link);
return -ENODEV;
}
@@ -617,7 +597,7 @@ static int pcmciamtd_config(struct pcmcia_device *link)
/* If the memory found is fits completely into the mapped PCMCIA window,
use the faster non-remapping read/write functions */
if(mtd->size <= dev->win_size) {
- DEBUG(1, "Using non remapping memory functions");
+ pr_debug("Using non remapping memory functions\n");
dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base;
if (dev->pcmcia_map.bankwidth == 1) {
dev->pcmcia_map.read = pcmcia_read8;
@@ -645,7 +625,7 @@ static int pcmciamtd_config(struct pcmcia_device *link)
static int pcmciamtd_suspend(struct pcmcia_device *dev)
{
- DEBUG(2, "EVENT_PM_RESUME");
+ pr_debug("EVENT_PM_RESUME\n");
/* get_lock(link); */
@@ -654,7 +634,7 @@ static int pcmciamtd_suspend(struct pcmcia_device *dev)
static int pcmciamtd_resume(struct pcmcia_device *dev)
{
- DEBUG(2, "EVENT_PM_SUSPEND");
+ pr_debug("EVENT_PM_SUSPEND\n");
/* free_lock(link); */
@@ -666,7 +646,7 @@ static void pcmciamtd_detach(struct pcmcia_device *link)
{
struct pcmciamtd_dev *dev = link->priv;
- DEBUG(3, "link=0x%p", link);
+ pr_debug("link=0x%p\n", link);
if(dev->mtd_info) {
mtd_device_unregister(dev->mtd_info);
@@ -686,7 +666,7 @@ static int pcmciamtd_probe(struct pcmcia_device *link)
/* Create new memory card device */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) return -ENOMEM;
- DEBUG(1, "dev=0x%p", dev);
+ pr_debug("dev=0x%p\n", dev);
dev->p_dev = link;
link->priv = dev;
@@ -755,7 +735,7 @@ static int __init init_pcmciamtd(void)
static void __exit exit_pcmciamtd(void)
{
- DEBUG(1, DRIVER_DESC " unloading");
+ pr_debug(DRIVER_DESC " unloading");
pcmcia_unregister_driver(&pcmciamtd_driver);
}
diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c
index f64cee4a3bfb..66e8200079c2 100644
--- a/drivers/mtd/maps/physmap.c
+++ b/drivers/mtd/maps/physmap.c
@@ -27,8 +27,6 @@ struct physmap_flash_info {
struct mtd_info *mtd[MAX_RESOURCES];
struct mtd_info *cmtd;
struct map_info map[MAX_RESOURCES];
- int nr_parts;
- struct mtd_partition *parts;
};
static int physmap_flash_remove(struct platform_device *dev)
@@ -46,8 +44,6 @@ static int physmap_flash_remove(struct platform_device *dev)
if (info->cmtd) {
mtd_device_unregister(info->cmtd);
- if (info->nr_parts)
- kfree(info->parts);
if (info->cmtd != info->mtd[0])
mtd_concat_destroy(info->cmtd);
}
@@ -175,23 +171,8 @@ static int physmap_flash_probe(struct platform_device *dev)
if (err)
goto err_out;
- err = parse_mtd_partitions(info->cmtd, part_probe_types,
- &info->parts, 0);
- if (err > 0) {
- mtd_device_register(info->cmtd, info->parts, err);
- info->nr_parts = err;
- return 0;
- }
-
- if (physmap_data->nr_parts) {
- printk(KERN_NOTICE "Using physmap partition information\n");
- mtd_device_register(info->cmtd, physmap_data->parts,
- physmap_data->nr_parts);
- return 0;
- }
-
- mtd_device_register(info->cmtd, NULL, 0);
-
+ mtd_device_parse_register(info->cmtd, part_probe_types, 0,
+ physmap_data->parts, physmap_data->nr_parts);
return 0;
err_out:
@@ -245,21 +226,6 @@ static struct platform_device physmap_flash = {
.num_resources = 1,
.resource = &physmap_flash_resource,
};
-
-void physmap_configure(unsigned long addr, unsigned long size,
- int bankwidth, void (*set_vpp)(struct map_info *, int))
-{
- physmap_flash_resource.start = addr;
- physmap_flash_resource.end = addr + size - 1;
- physmap_flash_data.width = bankwidth;
- physmap_flash_data.set_vpp = set_vpp;
-}
-
-void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
-{
- physmap_flash_data.nr_parts = num_parts;
- physmap_flash_data.parts = parts;
-}
#endif
static int __init physmap_init(void)
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index d251d1db129b..7d65f9d3e690 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -34,58 +34,10 @@ struct of_flash_list {
struct of_flash {
struct mtd_info *cmtd;
- struct mtd_partition *parts;
int list_size; /* number of elements in of_flash_list */
struct of_flash_list list[0];
};
-#define OF_FLASH_PARTS(info) ((info)->parts)
-static int parse_obsolete_partitions(struct platform_device *dev,
- struct of_flash *info,
- struct device_node *dp)
-{
- int i, plen, nr_parts;
- const struct {
- __be32 offset, len;
- } *part;
- const char *names;
-
- part = of_get_property(dp, "partitions", &plen);
- if (!part)
- return 0; /* No partitions found */
-
- dev_warn(&dev->dev, "Device tree uses obsolete partition map binding\n");
-
- nr_parts = plen / sizeof(part[0]);
-
- info->parts = kzalloc(nr_parts * sizeof(*info->parts), GFP_KERNEL);
- if (!info->parts)
- return -ENOMEM;
-
- names = of_get_property(dp, "partition-names", &plen);
-
- for (i = 0; i < nr_parts; i++) {
- info->parts[i].offset = be32_to_cpu(part->offset);
- info->parts[i].size = be32_to_cpu(part->len) & ~1;
- if (be32_to_cpu(part->len) & 1) /* bit 0 set signifies read only partition */
- info->parts[i].mask_flags = MTD_WRITEABLE;
-
- if (names && (plen > 0)) {
- int len = strlen(names) + 1;
-
- info->parts[i].name = (char *)names;
- plen -= len;
- names += len;
- } else {
- info->parts[i].name = "unnamed";
- }
-
- part++;
- }
-
- return nr_parts;
-}
-
static int of_flash_remove(struct platform_device *dev)
{
struct of_flash *info;
@@ -101,11 +53,8 @@ static int of_flash_remove(struct platform_device *dev)
mtd_concat_destroy(info->cmtd);
}
- if (info->cmtd) {
- if (OF_FLASH_PARTS(info))
- kfree(OF_FLASH_PARTS(info));
+ if (info->cmtd)
mtd_device_unregister(info->cmtd);
- }
for (i = 0; i < info->list_size; i++) {
if (info->list[i].mtd)
@@ -165,7 +114,8 @@ static struct mtd_info * __devinit obsolete_probe(struct platform_device *dev,
specifies the list of partition probers to use. If none is given then the
default is use. These take precedence over other device tree
information. */
-static const char *part_probe_types_def[] = { "cmdlinepart", "RedBoot", NULL };
+static const char *part_probe_types_def[] = { "cmdlinepart", "RedBoot",
+ "ofpart", "ofoldpart", NULL };
static const char ** __devinit of_get_probes(struct device_node *dp)
{
const char *cp;
@@ -218,6 +168,7 @@ static int __devinit of_flash_probe(struct platform_device *dev)
int reg_tuple_size;
struct mtd_info **mtd_list = NULL;
resource_size_t res_size;
+ struct mtd_part_parser_data ppdata;
match = of_match_device(of_flash_match, &dev->dev);
if (!match)
@@ -331,29 +282,12 @@ static int __devinit of_flash_probe(struct platform_device *dev)
if (err)
goto err_out;
+ ppdata.of_node = dp;
part_probe_types = of_get_probes(dp);
- err = parse_mtd_partitions(info->cmtd, part_probe_types,
- &info->parts, 0);
- if (err < 0) {
- of_free_probes(part_probe_types);
- goto err_out;
- }
+ mtd_device_parse_register(info->cmtd, part_probe_types, &ppdata,
+ NULL, 0);
of_free_probes(part_probe_types);
- if (err == 0) {
- err = of_mtd_parse_partitions(&dev->dev, dp, &info->parts);
- if (err < 0)
- goto err_out;
- }
-
- if (err == 0) {
- err = parse_obsolete_partitions(dev, info, dp);
- if (err < 0)
- goto err_out;
- }
-
- mtd_device_register(info->cmtd, info->parts, err);
-
kfree(mtd_list);
return 0;
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c
index 9ca1eccba4bc..94f553489725 100644
--- a/drivers/mtd/maps/plat-ram.c
+++ b/drivers/mtd/maps/plat-ram.c
@@ -44,8 +44,6 @@ struct platram_info {
struct device *dev;
struct mtd_info *mtd;
struct map_info map;
- struct mtd_partition *partitions;
- bool free_partitions;
struct resource *area;
struct platdata_mtd_ram *pdata;
};
@@ -95,10 +93,6 @@ static int platram_remove(struct platform_device *pdev)
if (info->mtd) {
mtd_device_unregister(info->mtd);
- if (info->partitions) {
- if (info->free_partitions)
- kfree(info->partitions);
- }
map_destroy(info->mtd);
}
@@ -228,21 +222,8 @@ static int platram_probe(struct platform_device *pdev)
/* check to see if there are any available partitions, or wether
* to add this device whole */
- if (!pdata->nr_partitions) {
- /* try to probe using the supplied probe type */
- if (pdata->probes) {
- err = parse_mtd_partitions(info->mtd, pdata->probes,
- &info->partitions, 0);
- info->free_partitions = 1;
- if (err > 0)
- err = mtd_device_register(info->mtd,
- info->partitions, err);
- }
- }
- /* use the static mapping */
- else
- err = mtd_device_register(info->mtd, pdata->partitions,
- pdata->nr_partitions);
+ err = mtd_device_parse_register(info->mtd, pdata->probes, 0,
+ pdata->partitions, pdata->nr_partitions);
if (!err)
dev_info(&pdev->dev, "registered mtd device\n");
diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c
index f59d62f74d44..411a17df9fc1 100644
--- a/drivers/mtd/maps/pxa2xx-flash.c
+++ b/drivers/mtd/maps/pxa2xx-flash.c
@@ -41,8 +41,6 @@ static void pxa2xx_map_inval_cache(struct map_info *map, unsigned long from,
}
struct pxa2xx_flash_info {
- struct mtd_partition *parts;
- int nr_parts;
struct mtd_info *mtd;
struct map_info map;
};
@@ -55,9 +53,7 @@ static int __devinit pxa2xx_flash_probe(struct platform_device *pdev)
{
struct flash_platform_data *flash = pdev->dev.platform_data;
struct pxa2xx_flash_info *info;
- struct mtd_partition *parts;
struct resource *res;
- int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -70,9 +66,7 @@ static int __devinit pxa2xx_flash_probe(struct platform_device *pdev)
info->map.name = (char *) flash->name;
info->map.bankwidth = flash->width;
info->map.phys = res->start;
- info->map.size = res->end - res->start + 1;
- info->parts = flash->parts;
- info->nr_parts = flash->nr_parts;
+ info->map.size = resource_size(res);
info->map.virt = ioremap(info->map.phys, info->map.size);
if (!info->map.virt) {
@@ -104,18 +98,7 @@ static int __devinit pxa2xx_flash_probe(struct platform_device *pdev)
}
info->mtd->owner = THIS_MODULE;
- ret = parse_mtd_partitions(info->mtd, probes, &parts, 0);
-
- if (ret > 0) {
- info->nr_parts = ret;
- info->parts = parts;
- }
-
- if (!info->nr_parts)
- printk("Registering %s as whole device\n",
- info->map.name);
-
- mtd_device_register(info->mtd, info->parts, info->nr_parts);
+ mtd_device_parse_register(info->mtd, probes, 0, NULL, 0);
platform_set_drvdata(pdev, info);
return 0;
@@ -133,7 +116,6 @@ static int __devexit pxa2xx_flash_remove(struct platform_device *dev)
iounmap(info->map.virt);
if (info->map.cached)
iounmap(info->map.cached);
- kfree(info->parts);
kfree(info);
return 0;
}
diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c
index 761fb459d2c7..0237f197fd12 100644
--- a/drivers/mtd/maps/rbtx4939-flash.c
+++ b/drivers/mtd/maps/rbtx4939-flash.c
@@ -25,8 +25,6 @@
struct rbtx4939_flash_info {
struct mtd_info *mtd;
struct map_info map;
- int nr_parts;
- struct mtd_partition *parts;
};
static int rbtx4939_flash_remove(struct platform_device *dev)
@@ -41,8 +39,6 @@ static int rbtx4939_flash_remove(struct platform_device *dev)
if (info->mtd) {
struct rbtx4939_flash_data *pdata = dev->dev.platform_data;
- if (info->nr_parts)
- kfree(info->parts);
mtd_device_unregister(info->mtd);
map_destroy(info->mtd);
}
@@ -50,7 +46,6 @@ static int rbtx4939_flash_remove(struct platform_device *dev)
}
static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
-static const char *part_probe_types[] = { "cmdlinepart", NULL };
static int rbtx4939_flash_probe(struct platform_device *dev)
{
@@ -107,22 +102,11 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
info->mtd->owner = THIS_MODULE;
if (err)
goto err_out;
+ err = mtd_device_parse_register(info->mtd, NULL, 0,
+ pdata->parts, pdata->nr_parts);
- err = parse_mtd_partitions(info->mtd, part_probe_types,
- &info->parts, 0);
- if (err > 0) {
- mtd_device_register(info->mtd, info->parts, err);
- info->nr_parts = err;
- return 0;
- }
-
- if (pdata->nr_parts) {
- pr_notice("Using rbtx4939 partition information\n");
- mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts);
- return 0;
- }
-
- mtd_device_register(info->mtd, NULL, 0);
+ if (err)
+ goto err_out;
return 0;
err_out:
diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c
index a9b5e0e5c4c5..fa9c0a9670cd 100644
--- a/drivers/mtd/maps/sa1100-flash.c
+++ b/drivers/mtd/maps/sa1100-flash.c
@@ -131,10 +131,8 @@ struct sa_subdev_info {
};
struct sa_info {
- struct mtd_partition *parts;
struct mtd_info *mtd;
int num_subdev;
- unsigned int nr_parts;
struct sa_subdev_info subdev[0];
};
@@ -231,8 +229,6 @@ static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *pla
mtd_concat_destroy(info->mtd);
}
- kfree(info->parts);
-
for (i = info->num_subdev - 1; i >= 0; i--)
sa1100_destroy_subdev(&info->subdev[i]);
kfree(info);
@@ -341,10 +337,8 @@ static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
{
struct flash_platform_data *plat = pdev->dev.platform_data;
- struct mtd_partition *parts;
- const char *part_type = NULL;
struct sa_info *info;
- int err, nr_parts = 0;
+ int err;
if (!plat)
return -ENODEV;
@@ -358,26 +352,8 @@ static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
/*
* Partition selection stuff.
*/
- nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0);
- if (nr_parts > 0) {
- info->parts = parts;
- part_type = "dynamic";
- } else {
- parts = plat->parts;
- nr_parts = plat->nr_parts;
- part_type = "static";
- }
-
- if (nr_parts == 0)
- printk(KERN_NOTICE "SA1100 flash: no partition info "
- "available, registering whole flash\n");
- else
- printk(KERN_NOTICE "SA1100 flash: using %s partition "
- "definition\n", part_type);
-
- mtd_device_register(info->mtd, parts, nr_parts);
-
- info->nr_parts = nr_parts;
+ mtd_device_parse_register(info->mtd, part_probes, 0,
+ plat->parts, plat->nr_parts);
platform_set_drvdata(pdev, info);
err = 0;
diff --git a/drivers/mtd/maps/solutionengine.c b/drivers/mtd/maps/solutionengine.c
index cbf6bade9354..496c40704aff 100644
--- a/drivers/mtd/maps/solutionengine.c
+++ b/drivers/mtd/maps/solutionengine.c
@@ -19,8 +19,6 @@
static struct mtd_info *flash_mtd;
static struct mtd_info *eprom_mtd;
-static struct mtd_partition *parsed_parts;
-
struct map_info soleng_eprom_map = {
.name = "Solution Engine EPROM",
.size = 0x400000,
@@ -51,12 +49,14 @@ static struct mtd_partition superh_se_partitions[] = {
.size = MTDPART_SIZ_FULL,
}
};
+#define NUM_PARTITIONS ARRAY_SIZE(superh_se_partitions)
+#else
+#define superh_se_partitions NULL
+#define NUM_PARTITIONS 0
#endif /* CONFIG_MTD_SUPERH_RESERVE */
static int __init init_soleng_maps(void)
{
- int nr_parts = 0;
-
/* First probe at offset 0 */
soleng_flash_map.phys = 0;
soleng_flash_map.virt = (void __iomem *)P2SEGADDR(0);
@@ -92,21 +92,8 @@ static int __init init_soleng_maps(void)
mtd_device_register(eprom_mtd, NULL, 0);
}
- nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
-
-#ifdef CONFIG_MTD_SUPERH_RESERVE
- if (nr_parts <= 0) {
- printk(KERN_NOTICE "Using configured partition at 0x%08x.\n",
- CONFIG_MTD_SUPERH_RESERVE);
- parsed_parts = superh_se_partitions;
- nr_parts = sizeof(superh_se_partitions)/sizeof(*parsed_parts);
- }
-#endif /* CONFIG_MTD_SUPERH_RESERVE */
-
- if (nr_parts > 0)
- mtd_device_register(flash_mtd, parsed_parts, nr_parts);
- else
- mtd_device_register(flash_mtd, NULL, 0);
+ mtd_device_parse_register(flash_mtd, probes, 0,
+ superh_se_partitions, NUM_PARTITIONS);
return 0;
}
@@ -118,10 +105,7 @@ static void __exit cleanup_soleng_maps(void)
map_destroy(eprom_mtd);
}
- if (parsed_parts)
- mtd_device_unregister(flash_mtd);
- else
- mtd_device_unregister(flash_mtd);
+ mtd_device_unregister(flash_mtd);
map_destroy(flash_mtd);
}
diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c
index 901ce968efae..aa7e0cb2893c 100644
--- a/drivers/mtd/maps/wr_sbc82xx_flash.c
+++ b/drivers/mtd/maps/wr_sbc82xx_flash.c
@@ -20,7 +20,6 @@
#include <asm/immap_cpm2.h>
static struct mtd_info *sbcmtd[3];
-static struct mtd_partition *sbcmtd_parts[3];
struct map_info sbc82xx_flash_map[3] = {
{.name = "Boot flash"},
@@ -101,6 +100,7 @@ static int __init init_sbc82xx_flash(void)
for (i=0; i<3; i++) {
int8_t flashcs[3] = { 0, 6, 1 };
int nr_parts;
+ struct mtd_partition *defparts;
printk(KERN_NOTICE "PowerQUICC II %s (%ld MiB on CS%d",
sbc82xx_flash_map[i].name,
@@ -113,7 +113,8 @@ static int __init init_sbc82xx_flash(void)
}
printk(" at %08lx)\n", sbc82xx_flash_map[i].phys);
- sbc82xx_flash_map[i].virt = ioremap(sbc82xx_flash_map[i].phys, sbc82xx_flash_map[i].size);
+ sbc82xx_flash_map[i].virt = ioremap(sbc82xx_flash_map[i].phys,
+ sbc82xx_flash_map[i].size);
if (!sbc82xx_flash_map[i].virt) {
printk("Failed to ioremap\n");
@@ -129,24 +130,20 @@ static int __init init_sbc82xx_flash(void)
sbcmtd[i]->owner = THIS_MODULE;
- nr_parts = parse_mtd_partitions(sbcmtd[i], part_probes,
- &sbcmtd_parts[i], 0);
- if (nr_parts > 0) {
- mtd_device_register(sbcmtd[i], sbcmtd_parts[i],
- nr_parts);
- continue;
- }
-
/* No partitioning detected. Use default */
if (i == 2) {
- mtd_device_register(sbcmtd[i], NULL, 0);
+ defparts = NULL;
+ nr_parts = 0;
} else if (i == bigflash) {
- mtd_device_register(sbcmtd[i], bigflash_parts,
- ARRAY_SIZE(bigflash_parts));
+ defparts = bigflash_parts;
+ nr_parts = ARRAY_SIZE(bigflash_parts);
} else {
- mtd_device_register(sbcmtd[i], smallflash_parts,
- ARRAY_SIZE(smallflash_parts));
+ defparts = smallflash_parts;
+ nr_parts = ARRAY_SIZE(smallflash_parts);
}
+
+ mtd_device_parse_register(sbcmtd[i], part_probes, 0,
+ defparts, nr_parts);
}
return 0;
}
@@ -159,12 +156,8 @@ static void __exit cleanup_sbc82xx_flash(void)
if (!sbcmtd[i])
continue;
- if (i<2 || sbcmtd_parts[i])
- mtd_device_unregister(sbcmtd[i]);
- else
- mtd_device_unregister(sbcmtd[i]);
+ mtd_device_unregister(sbcmtd[i]);
- kfree(sbcmtd_parts[i]);
map_destroy(sbcmtd[i]);
iounmap((void *)sbc82xx_flash_map[i].virt);
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index ca385697446e..ed8b5e744b12 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -426,6 +426,8 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
new->rq->queuedata = new;
blk_queue_logical_block_size(new->rq, tr->blksize);
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, new->rq);
+
if (tr->discard) {
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, new->rq);
new->rq->limits.max_discard_sectors = UINT_MAX;
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index 3326615ad66b..7c1dc908a174 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -44,7 +44,7 @@ struct mtdblk_dev {
enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
};
-static struct mutex mtdblks_lock;
+static DEFINE_MUTEX(mtdblks_lock);
/*
* Cache stuff...
@@ -119,7 +119,7 @@ static int write_cached_data (struct mtdblk_dev *mtdblk)
if (mtdblk->cache_state != STATE_DIRTY)
return 0;
- DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" "
+ pr_debug("mtdblock: writing cached data for \"%s\" "
"at 0x%lx, size 0x%x\n", mtd->name,
mtdblk->cache_offset, mtdblk->cache_size);
@@ -148,7 +148,7 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
size_t retlen;
int ret;
- DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n",
+ pr_debug("mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n",
mtd->name, pos, len);
if (!sect_size)
@@ -218,7 +218,7 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
size_t retlen;
int ret;
- DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n",
+ pr_debug("mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n",
mtd->name, pos, len);
if (!sect_size)
@@ -283,7 +283,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
{
struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd);
- DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");
+ pr_debug("mtdblock_open\n");
mutex_lock(&mtdblks_lock);
if (mtdblk->count) {
@@ -303,7 +303,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
mutex_unlock(&mtdblks_lock);
- DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
+ pr_debug("ok\n");
return 0;
}
@@ -312,7 +312,7 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
{
struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd);
- DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
+ pr_debug("mtdblock_release\n");
mutex_lock(&mtdblks_lock);
@@ -329,7 +329,7 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
mutex_unlock(&mtdblks_lock);
- DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
+ pr_debug("ok\n");
return 0;
}
@@ -389,8 +389,6 @@ static struct mtd_blktrans_ops mtdblock_tr = {
static int __init init_mtdblock(void)
{
- mutex_init(&mtdblks_lock);
-
return register_mtd_blktrans(&mtdblock_tr);
}
diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c
index 795a8c0a05b8..0470a6e86309 100644
--- a/drivers/mtd/mtdblock_ro.c
+++ b/drivers/mtd/mtdblock_ro.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/blktrans.h>
+#include <linux/module.h>
static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
unsigned long block, char *buf)
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 3f92731a5b9e..e7dc732ddabc 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -43,7 +43,7 @@ static struct vfsmount *mtd_inode_mnt __read_mostly;
/*
* Data structure to hold the pointer to the mtd device as well
- * as mode information ofr various use cases.
+ * as mode information of various use cases.
*/
struct mtd_file_info {
struct mtd_info *mtd;
@@ -86,7 +86,7 @@ static int mtd_open(struct inode *inode, struct file *file)
struct mtd_file_info *mfi;
struct inode *mtd_ino;
- DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
+ pr_debug("MTD_open\n");
/* You can't open the RO devices RW */
if ((file->f_mode & FMODE_WRITE) && (minor & 1))
@@ -151,7 +151,7 @@ static int mtd_close(struct inode *inode, struct file *file)
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
- DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
+ pr_debug("MTD_close\n");
/* Only sync if opened RW */
if ((file->f_mode & FMODE_WRITE) && mtd->sync)
@@ -195,7 +195,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
size_t size = count;
char *kbuf;
- DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
+ pr_debug("MTD_read\n");
if (*ppos + count > mtd->size)
count = mtd->size - *ppos;
@@ -211,17 +211,17 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
len = min_t(size_t, count, size);
switch (mfi->mode) {
- case MTD_MODE_OTP_FACTORY:
+ case MTD_FILE_MODE_OTP_FACTORY:
ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
break;
- case MTD_MODE_OTP_USER:
+ case MTD_FILE_MODE_OTP_USER:
ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
break;
- case MTD_MODE_RAW:
+ case MTD_FILE_MODE_RAW:
{
struct mtd_oob_ops ops;
- ops.mode = MTD_OOB_RAW;
+ ops.mode = MTD_OPS_RAW;
ops.datbuf = kbuf;
ops.oobbuf = NULL;
ops.len = len;
@@ -233,16 +233,16 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
default:
ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);
}
- /* Nand returns -EBADMSG on ecc errors, but it returns
+ /* Nand returns -EBADMSG on ECC errors, but it returns
* the data. For our userspace tools it is important
- * to dump areas with ecc errors !
+ * to dump areas with ECC errors!
* For kernel internal usage it also might return -EUCLEAN
* to signal the caller that a bitflip has occurred and has
* been corrected by the ECC algorithm.
* Userspace software which accesses NAND this way
* must be aware of the fact that it deals with NAND
*/
- if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) {
+ if (!ret || mtd_is_bitflip_or_eccerr(ret)) {
*ppos += retlen;
if (copy_to_user(buf, kbuf, retlen)) {
kfree(kbuf);
@@ -278,7 +278,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
int ret=0;
int len;
- DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
+ pr_debug("MTD_write\n");
if (*ppos == mtd->size)
return -ENOSPC;
@@ -302,10 +302,10 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
}
switch (mfi->mode) {
- case MTD_MODE_OTP_FACTORY:
+ case MTD_FILE_MODE_OTP_FACTORY:
ret = -EROFS;
break;
- case MTD_MODE_OTP_USER:
+ case MTD_FILE_MODE_OTP_USER:
if (!mtd->write_user_prot_reg) {
ret = -EOPNOTSUPP;
break;
@@ -313,13 +313,14 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
break;
- case MTD_MODE_RAW:
+ case MTD_FILE_MODE_RAW:
{
struct mtd_oob_ops ops;
- ops.mode = MTD_OOB_RAW;
+ ops.mode = MTD_OPS_RAW;
ops.datbuf = kbuf;
ops.oobbuf = NULL;
+ ops.ooboffs = 0;
ops.len = len;
ret = mtd->write_oob(mtd, *ppos, &ops);
@@ -367,13 +368,13 @@ static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
if (!mtd->read_fact_prot_reg)
ret = -EOPNOTSUPP;
else
- mfi->mode = MTD_MODE_OTP_FACTORY;
+ mfi->mode = MTD_FILE_MODE_OTP_FACTORY;
break;
case MTD_OTP_USER:
if (!mtd->read_fact_prot_reg)
ret = -EOPNOTSUPP;
else
- mfi->mode = MTD_MODE_OTP_USER;
+ mfi->mode = MTD_FILE_MODE_OTP_USER;
break;
default:
ret = -EINVAL;
@@ -390,6 +391,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
uint64_t start, uint32_t length, void __user *ptr,
uint32_t __user *retp)
{
+ struct mtd_file_info *mfi = file->private_data;
struct mtd_oob_ops ops;
uint32_t retlen;
int ret = 0;
@@ -409,9 +411,10 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
return ret;
ops.ooblen = length;
- ops.ooboffs = start & (mtd->oobsize - 1);
+ ops.ooboffs = start & (mtd->writesize - 1);
ops.datbuf = NULL;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW :
+ MTD_OPS_PLACE_OOB;
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
return -EINVAL;
@@ -420,7 +423,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
if (IS_ERR(ops.oobbuf))
return PTR_ERR(ops.oobbuf);
- start &= ~((uint64_t)mtd->oobsize - 1);
+ start &= ~((uint64_t)mtd->writesize - 1);
ret = mtd->write_oob(mtd, start, &ops);
if (ops.oobretlen > 0xFFFFFFFFU)
@@ -433,9 +436,11 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
return ret;
}
-static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
- uint32_t length, void __user *ptr, uint32_t __user *retp)
+static int mtd_do_readoob(struct file *file, struct mtd_info *mtd,
+ uint64_t start, uint32_t length, void __user *ptr,
+ uint32_t __user *retp)
{
+ struct mtd_file_info *mfi = file->private_data;
struct mtd_oob_ops ops;
int ret = 0;
@@ -451,9 +456,10 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
return ret;
ops.ooblen = length;
- ops.ooboffs = start & (mtd->oobsize - 1);
+ ops.ooboffs = start & (mtd->writesize - 1);
ops.datbuf = NULL;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW :
+ MTD_OPS_PLACE_OOB;
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
return -EINVAL;
@@ -462,7 +468,7 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
if (!ops.oobbuf)
return -ENOMEM;
- start &= ~((uint64_t)mtd->oobsize - 1);
+ start &= ~((uint64_t)mtd->writesize - 1);
ret = mtd->read_oob(mtd, start, &ops);
if (put_user(ops.oobretlen, retp))
@@ -472,13 +478,29 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
ret = -EFAULT;
kfree(ops.oobbuf);
+
+ /*
+ * NAND returns -EBADMSG on ECC errors, but it returns the OOB
+ * data. For our userspace tools it is important to dump areas
+ * with ECC errors!
+ * For kernel internal usage it also might return -EUCLEAN
+ * to signal the caller that a bitflip has occured and has
+ * been corrected by the ECC algorithm.
+ *
+ * Note: currently the standard NAND function, nand_read_oob_std,
+ * does not calculate ECC for the OOB area, so do not rely on
+ * this behavior unless you have replaced it with your own.
+ */
+ if (mtd_is_bitflip_or_eccerr(ret))
+ return 0;
+
return ret;
}
/*
* Copies (and truncates, if necessary) data from the larger struct,
* nand_ecclayout, to the smaller, deprecated layout struct,
- * nand_ecclayout_user. This is necessary only to suppport the deprecated
+ * nand_ecclayout_user. This is necessary only to support the deprecated
* API ioctl ECCGETLAYOUT while allowing all new functionality to use
* nand_ecclayout flexibly (i.e. the struct may change size in new
* releases without requiring major rewrites).
@@ -544,6 +566,55 @@ static int mtd_blkpg_ioctl(struct mtd_info *mtd,
}
}
+static int mtd_write_ioctl(struct mtd_info *mtd,
+ struct mtd_write_req __user *argp)
+{
+ struct mtd_write_req req;
+ struct mtd_oob_ops ops;
+ void __user *usr_data, *usr_oob;
+ int ret;
+
+ if (copy_from_user(&req, argp, sizeof(req)) ||
+ !access_ok(VERIFY_READ, req.usr_data, req.len) ||
+ !access_ok(VERIFY_READ, req.usr_oob, req.ooblen))
+ return -EFAULT;
+ if (!mtd->write_oob)
+ return -EOPNOTSUPP;
+
+ ops.mode = req.mode;
+ ops.len = (size_t)req.len;
+ ops.ooblen = (size_t)req.ooblen;
+ ops.ooboffs = 0;
+
+ usr_data = (void __user *)(uintptr_t)req.usr_data;
+ usr_oob = (void __user *)(uintptr_t)req.usr_oob;
+
+ if (req.usr_data) {
+ ops.datbuf = memdup_user(usr_data, ops.len);
+ if (IS_ERR(ops.datbuf))
+ return PTR_ERR(ops.datbuf);
+ } else {
+ ops.datbuf = NULL;
+ }
+
+ if (req.usr_oob) {
+ ops.oobbuf = memdup_user(usr_oob, ops.ooblen);
+ if (IS_ERR(ops.oobbuf)) {
+ kfree(ops.datbuf);
+ return PTR_ERR(ops.oobbuf);
+ }
+ } else {
+ ops.oobbuf = NULL;
+ }
+
+ ret = mtd->write_oob(mtd, (loff_t)req.start, &ops);
+
+ kfree(ops.datbuf);
+ kfree(ops.oobbuf);
+
+ return ret;
+}
+
static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct mtd_file_info *mfi = file->private_data;
@@ -553,7 +624,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
u_long size;
struct mtd_info_user info;
- DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
+ pr_debug("MTD_ioctl\n");
size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
if (cmd & IOC_IN) {
@@ -601,8 +672,8 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
info.erasesize = mtd->erasesize;
info.writesize = mtd->writesize;
info.oobsize = mtd->oobsize;
- /* The below fields are obsolete */
- info.ecctype = -1;
+ /* The below field is obsolete */
+ info.padding = 0;
if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
return -EFAULT;
break;
@@ -698,7 +769,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else
- ret = mtd_do_readoob(mtd, buf.start, buf.length,
+ ret = mtd_do_readoob(file, mtd, buf.start, buf.length,
buf.ptr, &buf_user->start);
break;
}
@@ -725,12 +796,19 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else
- ret = mtd_do_readoob(mtd, buf.start, buf.length,
+ ret = mtd_do_readoob(file, mtd, buf.start, buf.length,
(void __user *)(uintptr_t)buf.usr_ptr,
&buf_user->length);
break;
}
+ case MEMWRITE:
+ {
+ ret = mtd_write_ioctl(mtd,
+ (struct mtd_write_req __user *)arg);
+ break;
+ }
+
case MEMLOCK:
{
struct erase_info_user einfo;
@@ -827,7 +905,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
if (copy_from_user(&mode, argp, sizeof(int)))
return -EFAULT;
- mfi->mode = MTD_MODE_NORMAL;
+ mfi->mode = MTD_FILE_MODE_NORMAL;
ret = otp_select_filemode(mfi, mode);
@@ -843,11 +921,11 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
return -ENOMEM;
ret = -EOPNOTSUPP;
switch (mfi->mode) {
- case MTD_MODE_OTP_FACTORY:
+ case MTD_FILE_MODE_OTP_FACTORY:
if (mtd->get_fact_prot_info)
ret = mtd->get_fact_prot_info(mtd, buf, 4096);
break;
- case MTD_MODE_OTP_USER:
+ case MTD_FILE_MODE_OTP_USER:
if (mtd->get_user_prot_info)
ret = mtd->get_user_prot_info(mtd, buf, 4096);
break;
@@ -871,7 +949,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct otp_info oinfo;
- if (mfi->mode != MTD_MODE_OTP_USER)
+ if (mfi->mode != MTD_FILE_MODE_OTP_USER)
return -EINVAL;
if (copy_from_user(&oinfo, argp, sizeof(oinfo)))
return -EFAULT;
@@ -882,7 +960,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
}
#endif
- /* This ioctl is being deprecated - it truncates the ecc layout */
+ /* This ioctl is being deprecated - it truncates the ECC layout */
case ECCGETLAYOUT:
{
struct nand_ecclayout_user *usrlay;
@@ -915,17 +993,17 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
mfi->mode = 0;
switch(arg) {
- case MTD_MODE_OTP_FACTORY:
- case MTD_MODE_OTP_USER:
+ case MTD_FILE_MODE_OTP_FACTORY:
+ case MTD_FILE_MODE_OTP_USER:
ret = otp_select_filemode(mfi, arg);
break;
- case MTD_MODE_RAW:
+ case MTD_FILE_MODE_RAW:
if (!mtd->read_oob || !mtd->write_oob)
return -EOPNOTSUPP;
mfi->mode = arg;
- case MTD_MODE_NORMAL:
+ case MTD_FILE_MODE_NORMAL:
break;
default:
ret = -EINVAL;
@@ -1011,7 +1089,7 @@ static long mtd_compat_ioctl(struct file *file, unsigned int cmd,
if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else
- ret = mtd_do_readoob(mtd, buf.start,
+ ret = mtd_do_readoob(file, mtd, buf.start,
buf.length, compat_ptr(buf.ptr),
&buf_user->start);
break;
@@ -1144,7 +1222,7 @@ static void mtdchar_notify_remove(struct mtd_info *mtd)
if (mtd_ino) {
/* Destroy the inode if it exists */
- mtd_ino->i_nlink = 0;
+ clear_nlink(mtd_ino);
iput(mtd_ino);
}
}
@@ -1192,7 +1270,7 @@ err_unregister_chdev:
static void __exit cleanup_mtdchar(void)
{
unregister_mtd_user(&mtdchar_notifier);
- mntput(mtd_inode_mnt);
+ kern_unmount(mtd_inode_mnt);
unregister_filesystem(&mtd_inodefs_type);
__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
}
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index e601672a5305..6df4d4d4eb92 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -95,10 +95,10 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
/* Save information about bitflips! */
if (unlikely(err)) {
- if (err == -EBADMSG) {
+ if (mtd_is_eccerr(err)) {
mtd->ecc_stats.failed++;
ret = err;
- } else if (err == -EUCLEAN) {
+ } else if (mtd_is_bitflip(err)) {
mtd->ecc_stats.corrected++;
/* Do not overwrite -EBADMSG !! */
if (!ret)
@@ -279,10 +279,10 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
/* Save information about bitflips! */
if (unlikely(err)) {
- if (err == -EBADMSG) {
+ if (mtd_is_eccerr(err)) {
mtd->ecc_stats.failed++;
ret = err;
- } else if (err == -EUCLEAN) {
+ } else if (mtd_is_bitflip(err)) {
mtd->ecc_stats.corrected++;
/* Do not overwrite -EBADMSG !! */
if (!ret)
@@ -770,7 +770,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
/*
* Set up the new "super" device's MTD object structure, check for
- * incompatibilites between the subdevices.
+ * incompatibilities between the subdevices.
*/
concat->mtd.type = subdev[0]->type;
concat->mtd.flags = subdev[0]->flags;
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index c510aff289a8..b01993ea260e 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -362,7 +362,7 @@ int add_mtd_device(struct mtd_info *mtd)
MTD_DEVT(i) + 1,
NULL, "mtd%dro", i);
- DEBUG(0, "mtd: Giving out device %d to %s\n", i, mtd->name);
+ pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name);
/* No need to get a refcount on the module containing
the notifier, since we hold the mtd_table_mutex */
list_for_each_entry(not, &mtd_notifiers, list)
@@ -429,27 +429,63 @@ out_error:
}
/**
- * mtd_device_register - register an MTD device.
+ * mtd_device_parse_register - parse partitions and register an MTD device.
*
- * @master: the MTD device to register
- * @parts: the partitions to register - only valid if nr_parts > 0
- * @nr_parts: the number of partitions in parts. If zero then the full MTD
- * device is registered
+ * @mtd: the MTD device to register
+ * @types: the list of MTD partition probes to try, see
+ * 'parse_mtd_partitions()' for more information
+ * @parser_data: MTD partition parser-specific data
+ * @parts: fallback partition information to register, if parsing fails;
+ * only valid if %nr_parts > %0
+ * @nr_parts: the number of partitions in parts, if zero then the full
+ * MTD device is registered if no partition info is found
*
- * Register an MTD device with the system and optionally, a number of
- * partitions. If nr_parts is 0 then the whole device is registered, otherwise
- * only the partitions are registered. To register both the full device *and*
- * the partitions, call mtd_device_register() twice, once with nr_parts == 0
- * and once equal to the number of partitions.
+ * This function aggregates MTD partitions parsing (done by
+ * 'parse_mtd_partitions()') and MTD device and partitions registering. It
+ * basically follows the most common pattern found in many MTD drivers:
+ *
+ * * It first tries to probe partitions on MTD device @mtd using parsers
+ * specified in @types (if @types is %NULL, then the default list of parsers
+ * is used, see 'parse_mtd_partitions()' for more information). If none are
+ * found this functions tries to fallback to information specified in
+ * @parts/@nr_parts.
+ * * If any partitioning info was found, this function registers the found
+ * partitions.
+ * * If no partitions were found this function just registers the MTD device
+ * @mtd and exits.
+ *
+ * Returns zero in case of success and a negative error code in case of failure.
*/
-int mtd_device_register(struct mtd_info *master,
- const struct mtd_partition *parts,
- int nr_parts)
+int mtd_device_parse_register(struct mtd_info *mtd, const char **types,
+ struct mtd_part_parser_data *parser_data,
+ const struct mtd_partition *parts,
+ int nr_parts)
{
- return parts ? add_mtd_partitions(master, parts, nr_parts) :
- add_mtd_device(master);
+ int err;
+ struct mtd_partition *real_parts;
+
+ err = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
+ if (err <= 0 && nr_parts && parts) {
+ real_parts = kmemdup(parts, sizeof(*parts) * nr_parts,
+ GFP_KERNEL);
+ if (!real_parts)
+ err = -ENOMEM;
+ else
+ err = nr_parts;
+ }
+
+ if (err > 0) {
+ err = add_mtd_partitions(mtd, real_parts, err);
+ kfree(real_parts);
+ } else if (err == 0) {
+ err = add_mtd_device(mtd);
+ if (err == 1)
+ err = -ENODEV;
+ }
+
+ return err;
}
-EXPORT_SYMBOL_GPL(mtd_device_register);
+EXPORT_SYMBOL_GPL(mtd_device_parse_register);
/**
* mtd_device_unregister - unregister an existing MTD device.
diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h
index 0ed6126b4c1f..961a38408542 100644
--- a/drivers/mtd/mtdcore.h
+++ b/drivers/mtd/mtdcore.h
@@ -15,6 +15,9 @@ extern int del_mtd_device(struct mtd_info *mtd);
extern int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *,
int);
extern int del_mtd_partitions(struct mtd_info *);
+extern int parse_mtd_partitions(struct mtd_info *master, const char **types,
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data);
#define mtd_for_each_device(mtd) \
for ((mtd) = __mtd_next_device(0); \
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index e3e40f440323..1e2fa6236705 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -258,7 +258,7 @@ static void find_next_position(struct mtdoops_context *cxt)
ret = mtd->read(mtd, page * record_size, MTDOOPS_HEADER_SIZE,
&retlen, (u_char *) &count[0]);
if (retlen != MTDOOPS_HEADER_SIZE ||
- (ret < 0 && ret != -EUCLEAN)) {
+ (ret < 0 && !mtd_is_bitflip(ret))) {
printk(KERN_ERR "mtdoops: read failure at %ld (%td of %d read), err %d\n",
page * record_size, retlen,
MTDOOPS_HEADER_SIZE, ret);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 630be3e7da04..a0bd2de4752b 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -73,9 +73,9 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
res = part->master->read(part->master, from + part->offset,
len, retlen, buf);
if (unlikely(res)) {
- if (res == -EUCLEAN)
+ if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
- if (res == -EBADMSG)
+ if (mtd_is_eccerr(res))
mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;
}
return res;
@@ -130,7 +130,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
if (ops->oobbuf) {
size_t len, pages;
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
len = mtd->oobavail;
else
len = mtd->oobsize;
@@ -142,9 +142,9 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
res = part->master->read_oob(part->master, from + part->offset, ops);
if (unlikely(res)) {
- if (res == -EUCLEAN)
+ if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected++;
- if (res == -EBADMSG)
+ if (mtd_is_eccerr(res))
mtd->ecc_stats.failed++;
}
return res;
@@ -479,6 +479,19 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
(unsigned long long)cur_offset, (unsigned long long)slave->offset);
}
}
+ if (slave->offset == MTDPART_OFS_RETAIN) {
+ slave->offset = cur_offset;
+ if (master->size - slave->offset >= slave->mtd.size) {
+ slave->mtd.size = master->size - slave->offset
+ - slave->mtd.size;
+ } else {
+ printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
+ part->name, master->size - slave->offset,
+ slave->mtd.size);
+ /* register to preserve ordering */
+ goto out_register;
+ }
+ }
if (slave->mtd.size == MTDPART_SIZ_FULL)
slave->mtd.size = master->size - slave->offset;
@@ -693,6 +706,8 @@ static struct mtd_part_parser *get_partition_parser(const char *name)
return ret;
}
+#define put_partition_parser(p) do { module_put((p)->owner); } while (0)
+
int register_mtd_parser(struct mtd_part_parser *p)
{
spin_lock(&part_parser_lock);
@@ -712,19 +727,51 @@ int deregister_mtd_parser(struct mtd_part_parser *p)
}
EXPORT_SYMBOL_GPL(deregister_mtd_parser);
+/*
+ * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
+ * are changing this array!
+ */
+static const char *default_mtd_part_types[] = {
+ "cmdlinepart",
+ "ofpart",
+ NULL
+};
+
+/**
+ * parse_mtd_partitions - parse MTD partitions
+ * @master: the master partition (describes whole MTD device)
+ * @types: names of partition parsers to try or %NULL
+ * @pparts: array of partitions found is returned here
+ * @data: MTD partition parser-specific data
+ *
+ * This function tries to find partition on MTD device @master. It uses MTD
+ * partition parsers, specified in @types. However, if @types is %NULL, then
+ * the default list of parsers is used. The default list contains only the
+ * "cmdlinepart" and "ofpart" parsers ATM.
+ *
+ * This function may return:
+ * o a negative error code in case of failure
+ * o zero if no partitions were found
+ * o a positive number of found partitions, in which case on exit @pparts will
+ * point to an array containing this number of &struct mtd_info objects.
+ */
int parse_mtd_partitions(struct mtd_info *master, const char **types,
- struct mtd_partition **pparts, unsigned long origin)
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
{
struct mtd_part_parser *parser;
int ret = 0;
+ if (!types)
+ types = default_mtd_part_types;
+
for ( ; ret <= 0 && *types; types++) {
parser = get_partition_parser(*types);
if (!parser && !request_module("%s", *types))
parser = get_partition_parser(*types);
if (!parser)
continue;
- ret = (*parser->parse_fn)(master, pparts, origin);
+ ret = (*parser->parse_fn)(master, pparts, data);
if (ret > 0) {
printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
ret, parser->name, master->name);
@@ -733,7 +780,6 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
}
return ret;
}
-EXPORT_SYMBOL_GPL(parse_mtd_partitions);
int mtd_is_partition(struct mtd_info *mtd)
{
diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c
index 16b02a1fc100..a90bfe79916d 100644
--- a/drivers/mtd/mtdsuper.c
+++ b/drivers/mtd/mtdsuper.c
@@ -14,6 +14,7 @@
#include <linux/mtd/super.h>
#include <linux/namei.h>
+#include <linux/export.h>
#include <linux/ctype.h>
#include <linux/slab.h>
@@ -26,12 +27,12 @@ static int get_sb_mtd_compare(struct super_block *sb, void *_mtd)
struct mtd_info *mtd = _mtd;
if (sb->s_mtd == mtd) {
- DEBUG(2, "MTDSB: Match on device %d (\"%s\")\n",
+ pr_debug("MTDSB: Match on device %d (\"%s\")\n",
mtd->index, mtd->name);
return 1;
}
- DEBUG(2, "MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
+ pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name);
return 0;
}
@@ -70,7 +71,7 @@ static struct dentry *mount_mtd_aux(struct file_system_type *fs_type, int flags,
goto already_mounted;
/* fresh new superblock */
- DEBUG(1, "MTDSB: New superblock for device %d (\"%s\")\n",
+ pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
mtd->index, mtd->name);
sb->s_flags = flags;
@@ -87,7 +88,7 @@ static struct dentry *mount_mtd_aux(struct file_system_type *fs_type, int flags,
/* new mountpoint for an already mounted superblock */
already_mounted:
- DEBUG(1, "MTDSB: Device %d (\"%s\") is already mounted\n",
+ pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n",
mtd->index, mtd->name);
put_mtd_device(mtd);
return dget(sb->s_root);
@@ -108,7 +109,7 @@ static struct dentry *mount_mtd_nr(struct file_system_type *fs_type, int flags,
mtd = get_mtd_device(NULL, mtdnr);
if (IS_ERR(mtd)) {
- DEBUG(0, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr);
+ pr_debug("MTDSB: Device #%u doesn't appear to exist\n", mtdnr);
return ERR_CAST(mtd);
}
@@ -131,7 +132,7 @@ struct dentry *mount_mtd(struct file_system_type *fs_type, int flags,
if (!dev_name)
return ERR_PTR(-EINVAL);
- DEBUG(2, "MTDSB: dev_name \"%s\"\n", dev_name);
+ pr_debug("MTDSB: dev_name \"%s\"\n", dev_name);
/* the preferred way of mounting in future; especially when
* CONFIG_BLOCK=n - we specify the underlying MTD device by number or
@@ -142,7 +143,7 @@ struct dentry *mount_mtd(struct file_system_type *fs_type, int flags,
struct mtd_info *mtd;
/* mount by MTD device name */
- DEBUG(1, "MTDSB: mtd:%%s, name \"%s\"\n",
+ pr_debug("MTDSB: mtd:%%s, name \"%s\"\n",
dev_name + 4);
mtd = get_mtd_device_nm(dev_name + 4);
@@ -163,7 +164,7 @@ struct dentry *mount_mtd(struct file_system_type *fs_type, int flags,
mtdnr = simple_strtoul(dev_name + 3, &endptr, 0);
if (!*endptr) {
/* It was a valid number */
- DEBUG(1, "MTDSB: mtd%%d, mtdnr %d\n",
+ pr_debug("MTDSB: mtd%%d, mtdnr %d\n",
mtdnr);
return mount_mtd_nr(fs_type, flags,
dev_name, data,
@@ -179,10 +180,10 @@ struct dentry *mount_mtd(struct file_system_type *fs_type, int flags,
bdev = lookup_bdev(dev_name);
if (IS_ERR(bdev)) {
ret = PTR_ERR(bdev);
- DEBUG(1, "MTDSB: lookup_bdev() returned %d\n", ret);
+ pr_debug("MTDSB: lookup_bdev() returned %d\n", ret);
return ERR_PTR(ret);
}
- DEBUG(1, "MTDSB: lookup_bdev() returned 0\n");
+ pr_debug("MTDSB: lookup_bdev() returned 0\n");
ret = -EINVAL;
diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c
index fd7885327611..bd9590c723e4 100644
--- a/drivers/mtd/mtdswap.c
+++ b/drivers/mtd/mtdswap.c
@@ -86,7 +86,7 @@ struct swap_eb {
unsigned int flags;
unsigned int active_count;
unsigned int erase_count;
- unsigned int pad; /* speeds up pointer decremtnt */
+ unsigned int pad; /* speeds up pointer decrement */
};
#define MTDSWAP_ECNT_MIN(rbroot) (rb_entry(rb_first(rbroot), struct swap_eb, \
@@ -314,7 +314,7 @@ static int mtdswap_read_oob(struct mtdswap_dev *d, loff_t from,
{
int ret = d->mtd->read_oob(d->mtd, from, ops);
- if (ret == -EUCLEAN)
+ if (mtd_is_bitflip(ret))
return ret;
if (ret) {
@@ -350,11 +350,11 @@ static int mtdswap_read_markers(struct mtdswap_dev *d, struct swap_eb *eb)
ops.oobbuf = d->oob_buf;
ops.ooboffs = 0;
ops.datbuf = NULL;
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ret = mtdswap_read_oob(d, offset, &ops);
- if (ret && ret != -EUCLEAN)
+ if (ret && !mtd_is_bitflip(ret))
return ret;
data = (struct mtdswap_oobdata *)d->oob_buf;
@@ -363,7 +363,7 @@ static int mtdswap_read_markers(struct mtdswap_dev *d, struct swap_eb *eb)
if (le16_to_cpu(data->magic) == MTDSWAP_MAGIC_CLEAN) {
eb->erase_count = le32_to_cpu(data->count);
- if (ret == -EUCLEAN)
+ if (mtd_is_bitflip(ret))
ret = MTDSWAP_SCANNED_BITFLIP;
else {
if (le16_to_cpu(data2->magic) == MTDSWAP_MAGIC_DIRTY)
@@ -389,7 +389,7 @@ static int mtdswap_write_marker(struct mtdswap_dev *d, struct swap_eb *eb,
ops.ooboffs = 0;
ops.oobbuf = (uint8_t *)&n;
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.datbuf = NULL;
if (marker == MTDSWAP_TYPE_CLEAN) {
@@ -408,7 +408,7 @@ static int mtdswap_write_marker(struct mtdswap_dev *d, struct swap_eb *eb,
if (ret) {
dev_warn(d->dev, "Write OOB failed for block at %08llx "
"error %d\n", offset, ret);
- if (ret == -EIO || ret == -EBADMSG)
+ if (ret == -EIO || mtd_is_eccerr(ret))
mtdswap_handle_write_error(d, eb);
return ret;
}
@@ -628,7 +628,7 @@ static int mtdswap_map_free_block(struct mtdswap_dev *d, unsigned int page,
TREE_COUNT(d, CLEAN)--;
ret = mtdswap_write_marker(d, eb, MTDSWAP_TYPE_DIRTY);
- } while (ret == -EIO || ret == -EBADMSG);
+ } while (ret == -EIO || mtd_is_eccerr(ret));
if (ret)
return ret;
@@ -678,7 +678,7 @@ retry:
ret = mtdswap_map_free_block(d, page, bp);
eb = d->eb_data + (*bp / d->pages_per_eblk);
- if (ret == -EIO || ret == -EBADMSG) {
+ if (ret == -EIO || mtd_is_eccerr(ret)) {
d->curr_write = NULL;
eb->active_count--;
d->revmap[*bp] = PAGE_UNDEF;
@@ -690,7 +690,7 @@ retry:
writepos = (loff_t)*bp << PAGE_SHIFT;
ret = mtd->write(mtd, writepos, PAGE_SIZE, &retlen, buf);
- if (ret == -EIO || ret == -EBADMSG) {
+ if (ret == -EIO || mtd_is_eccerr(ret)) {
d->curr_write_pos--;
eb->active_count--;
d->revmap[*bp] = PAGE_UNDEF;
@@ -738,7 +738,7 @@ static int mtdswap_move_block(struct mtdswap_dev *d, unsigned int oldblock,
retry:
ret = mtd->read(mtd, readpos, PAGE_SIZE, &retlen, d->page_buf);
- if (ret < 0 && ret != -EUCLEAN) {
+ if (ret < 0 && !mtd_is_bitflip(ret)) {
oldeb = d->eb_data + oldblock / d->pages_per_eblk;
oldeb->flags |= EBLOCK_READERR;
@@ -931,7 +931,7 @@ static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
struct mtd_oob_ops ops;
int ret;
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = mtd->writesize;
ops.ooblen = mtd->ecclayout->oobavail;
ops.ooboffs = 0;
@@ -1016,7 +1016,7 @@ static int mtdswap_gc(struct mtdswap_dev *d, unsigned int background)
if (ret == 0)
mtdswap_rb_add(d, eb, MTDSWAP_CLEAN);
- else if (ret != -EIO && ret != -EBADMSG)
+ else if (ret != -EIO && !mtd_is_eccerr(ret))
mtdswap_rb_add(d, eb, MTDSWAP_DIRTY);
return 0;
@@ -1164,7 +1164,7 @@ retry:
ret = mtd->read(mtd, readpos, PAGE_SIZE, &retlen, buf);
d->mtd_read_count++;
- if (ret == -EUCLEAN) {
+ if (mtd_is_bitflip(ret)) {
eb->flags |= EBLOCK_BITFLIP;
mtdswap_rb_add(d, eb, MTDSWAP_BITFLIP);
ret = 0;
@@ -1374,11 +1374,10 @@ static int mtdswap_init(struct mtdswap_dev *d, unsigned int eblocks,
goto revmap_fail;
eblk_bytes = sizeof(struct swap_eb)*d->eblks;
- d->eb_data = vmalloc(eblk_bytes);
+ d->eb_data = vzalloc(eblk_bytes);
if (!d->eb_data)
goto eb_data_fail;
- memset(d->eb_data, 0, eblk_bytes);
for (i = 0; i < pages; i++)
d->page_data[i] = BLOCK_UNDEF;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 4c3425235adc..cce7b70824c3 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -83,16 +83,9 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
scratch register here to enable this feature. On Intel Moorestown
boards, the scratch register is at 0xFF108018.
-config MTD_NAND_EDB7312
- tristate "Support for Cirrus Logic EBD7312 evaluation board"
- depends on ARCH_EDB7312
- help
- This enables the driver for the Cirrus Logic EBD7312 evaluation
- board to access the onboard NAND Flash.
-
config MTD_NAND_H1900
tristate "iPAQ H1900 flash"
- depends on ARCH_PXA
+ depends on ARCH_PXA && BROKEN
help
This enables the driver for the iPAQ h1900 flash.
@@ -116,10 +109,11 @@ config MTD_NAND_AMS_DELTA
Support for NAND flash on Amstrad E3 (Delta).
config MTD_NAND_OMAP2
- tristate "NAND Flash device on OMAP2 and OMAP3"
- depends on ARM && (ARCH_OMAP2 || ARCH_OMAP3)
+ tristate "NAND Flash device on OMAP2, OMAP3 and OMAP4"
+ depends on ARM && (ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4)
help
- Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms.
+ Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4
+ platforms.
config MTD_NAND_IDS
tristate
@@ -138,7 +132,7 @@ config MTD_NAND_RICOH
config MTD_NAND_AU1550
tristate "Au1550/1200 NAND support"
- depends on SOC_AU1200 || SOC_AU1550
+ depends on MIPS_ALCHEMY
help
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
@@ -423,6 +417,19 @@ config MTD_NAND_NANDSIM
The simulator may simulate various NAND flash chips for the
MTD nand layer.
+config MTD_NAND_GPMI_NAND
+ bool "GPMI NAND Flash Controller driver"
+ depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28)
+ select MTD_PARTITIONS
+ select MTD_CMDLINE_PARTS
+ help
+ Enables NAND Flash support for IMX23 or IMX28.
+ The GPMI controller is very powerful, with the help of BCH
+ module, it can do the hardware ECC. The GPMI supports several
+ NAND flashs at the same time. The GPMI may conflicts with other
+ block, such as SD card. So pay attention to it when you enable
+ the GPMI.
+
config MTD_NAND_PLATFORM
tristate "Support for generic platform NAND driver"
help
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 5745d831168e..618f4ba23699 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_MTD_NAND_SPIA) += spia.o
obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o
obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
-obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
@@ -49,5 +48,6 @@ obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
+obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c
index 78017eb9318e..9e6b498c9beb 100644
--- a/drivers/mtd/nand/ams-delta.c
+++ b/drivers/mtd/nand/ams-delta.c
@@ -26,7 +26,7 @@
#include <asm/io.h>
#include <mach/hardware.h>
#include <asm/sizes.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#include <plat/board-ams-delta.h>
/*
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index b300705d41cb..23e5d77c39fc 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -22,6 +22,7 @@
*
*/
+#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -160,37 +161,6 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
!!host->board->rdy_pin_active_low;
}
-/*
- * Minimal-overhead PIO for data access.
- */
-static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
-{
- struct nand_chip *nand_chip = mtd->priv;
-
- __raw_readsb(nand_chip->IO_ADDR_R, buf, len);
-}
-
-static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
-{
- struct nand_chip *nand_chip = mtd->priv;
-
- __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
-}
-
-static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
-{
- struct nand_chip *nand_chip = mtd->priv;
-
- __raw_writesb(nand_chip->IO_ADDR_W, buf, len);
-}
-
-static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
-{
- struct nand_chip *nand_chip = mtd->priv;
-
- __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
-}
-
static void dma_complete_func(void *completion)
{
complete(completion);
@@ -265,33 +235,27 @@ err_buf:
static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *chip = mtd->priv;
- struct atmel_nand_host *host = chip->priv;
if (use_dma && len > mtd->oobsize)
/* only use DMA for bigger than oob size: better performances */
if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
return;
- if (host->board->bus_width_16)
- atmel_read_buf16(mtd, buf, len);
- else
- atmel_read_buf8(mtd, buf, len);
+ /* if no DMA operation possible, use PIO */
+ memcpy_fromio(buf, chip->IO_ADDR_R, len);
}
static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *chip = mtd->priv;
- struct atmel_nand_host *host = chip->priv;
if (use_dma && len > mtd->oobsize)
/* only use DMA for bigger than oob size: better performances */
if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
return;
- if (host->board->bus_width_16)
- atmel_write_buf16(mtd, buf, len);
- else
- atmel_write_buf8(mtd, buf, len);
+ /* if no DMA operation possible, use PIO */
+ memcpy_toio(chip->IO_ADDR_W, buf, len);
}
/*
@@ -480,10 +444,6 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
}
}
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-static const char *part_probes[] = { "cmdlinepart", NULL };
-#endif
-
/*
* Probe for the NAND device.
*/
@@ -495,8 +455,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
struct resource *regs;
struct resource *mem;
int res;
- struct mtd_partition *partitions = NULL;
- int num_partitions = 0;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
@@ -513,7 +471,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
host->io_phys = (dma_addr_t)mem->start;
- host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
+ host->io_base = ioremap(mem->start, resource_size(mem));
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
res = -EIO;
@@ -547,7 +505,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
if (no_ecc)
nand_chip->ecc.mode = NAND_ECC_NONE;
if (hard_ecc && regs) {
- host->ecc = ioremap(regs->start, regs->end - regs->start + 1);
+ host->ecc = ioremap(regs->start, resource_size(regs));
if (host->ecc == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
res = -EIO;
@@ -582,7 +540,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
if (on_flash_bbt) {
printk(KERN_INFO "atmel_nand: Use On Flash BBT\n");
- nand_chip->options |= NAND_USE_FLASH_BBT;
+ nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
}
if (!cpu_has_dma())
@@ -593,7 +551,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
- host->dma_chan = dma_request_channel(mask, 0, NULL);
+ host->dma_chan = dma_request_channel(mask, NULL, NULL);
if (!host->dma_chan) {
dev_err(host->dev, "Failed to request DMA channel\n");
use_dma = 0;
@@ -654,27 +612,12 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
goto err_scan_tail;
}
-#ifdef CONFIG_MTD_CMDLINE_PARTS
mtd->name = "atmel_nand";
- num_partitions = parse_mtd_partitions(mtd, part_probes,
- &partitions, 0);
-#endif
- if (num_partitions <= 0 && host->board->partition_info)
- partitions = host->board->partition_info(mtd->size,
- &num_partitions);
-
- if ((!partitions) || (num_partitions == 0)) {
- printk(KERN_ERR "atmel_nand: No partitions defined, or unsupported device.\n");
- res = -ENXIO;
- goto err_no_partitions;
- }
-
- res = mtd_device_register(mtd, partitions, num_partitions);
+ res = mtd_device_parse_register(mtd, NULL, 0,
+ host->board->parts, host->board->num_parts);
if (!res)
return res;
-err_no_partitions:
- nand_release(mtd);
err_scan_tail:
err_scan_ident:
err_no_card:
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index e7767eef4505..7dd3700f2303 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -19,7 +19,11 @@
#include <linux/mtd/partitions.h>
#include <asm/io.h>
-#include <asm/mach-au1x00/au1xxx.h>
+#ifdef CONFIG_MIPS_PB1550
+#include <asm/mach-pb1x00/pb1550.h>
+#elif defined(CONFIG_MIPS_DB1550)
+#include <asm/mach-db1x00/db1x00.h>
+#endif
#include <asm/mach-db1x00/bcsr.h>
/*
@@ -48,7 +52,7 @@ static const struct mtd_partition partition_info[] = {
* au_read_byte - read one byte from the chip
* @mtd: MTD device structure
*
- * read function for 8bit buswith
+ * read function for 8bit buswidth
*/
static u_char au_read_byte(struct mtd_info *mtd)
{
@@ -63,7 +67,7 @@ static u_char au_read_byte(struct mtd_info *mtd)
* @mtd: MTD device structure
* @byte: pointer to data byte to write
*
- * write function for 8it buswith
+ * write function for 8it buswidth
*/
static void au_write_byte(struct mtd_info *mtd, u_char byte)
{
@@ -73,11 +77,10 @@ static void au_write_byte(struct mtd_info *mtd, u_char byte)
}
/**
- * au_read_byte16 - read one byte endianess aware from the chip
+ * au_read_byte16 - read one byte endianness aware from the chip
* @mtd: MTD device structure
*
- * read function for 16bit buswith with
- * endianess conversion
+ * read function for 16bit buswidth with endianness conversion
*/
static u_char au_read_byte16(struct mtd_info *mtd)
{
@@ -88,12 +91,11 @@ static u_char au_read_byte16(struct mtd_info *mtd)
}
/**
- * au_write_byte16 - write one byte endianess aware to the chip
+ * au_write_byte16 - write one byte endianness aware to the chip
* @mtd: MTD device structure
* @byte: pointer to data byte to write
*
- * write function for 16bit buswith with
- * endianess conversion
+ * write function for 16bit buswidth with endianness conversion
*/
static void au_write_byte16(struct mtd_info *mtd, u_char byte)
{
@@ -106,8 +108,7 @@ static void au_write_byte16(struct mtd_info *mtd, u_char byte)
* au_read_word - read one word from the chip
* @mtd: MTD device structure
*
- * read function for 16bit buswith without
- * endianess conversion
+ * read function for 16bit buswidth without endianness conversion
*/
static u16 au_read_word(struct mtd_info *mtd)
{
@@ -123,7 +124,7 @@ static u16 au_read_word(struct mtd_info *mtd)
* @buf: data buffer
* @len: number of bytes to write
*
- * write function for 8bit buswith
+ * write function for 8bit buswidth
*/
static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
@@ -142,7 +143,7 @@ static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
* @buf: buffer to store date
* @len: number of bytes to read
*
- * read function for 8bit buswith
+ * read function for 8bit buswidth
*/
static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
@@ -161,7 +162,7 @@ static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*
- * verify function for 8bit buswith
+ * verify function for 8bit buswidth
*/
static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
@@ -183,7 +184,7 @@ static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
* @buf: data buffer
* @len: number of bytes to write
*
- * write function for 16bit buswith
+ * write function for 16bit buswidth
*/
static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
{
@@ -205,7 +206,7 @@ static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
* @buf: buffer to store date
* @len: number of bytes to read
*
- * read function for 16bit buswith
+ * read function for 16bit buswidth
*/
static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
{
@@ -226,7 +227,7 @@ static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*
- * verify function for 16bit buswith
+ * verify function for 16bit buswidth
*/
static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
{
diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c
index eddc9a224985..2e42ec2e8ff4 100644
--- a/drivers/mtd/nand/autcpu12.c
+++ b/drivers/mtd/nand/autcpu12.c
@@ -172,9 +172,9 @@ static int __init autcpu12_init(void)
/* Enable the following for a flash based bad block table */
/*
- this->options = NAND_USE_FLASH_BBT;
+ this->bbt_options = NAND_BBT_USE_FLASH;
*/
- this->options = NAND_USE_FLASH_BBT;
+ this->bbt_options = NAND_BBT_USE_FLASH;
/* Scan to find existence of the device */
if (nand_scan(autcpu12_mtd, 1)) {
diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c
index 9ec280738a9a..46b58d672847 100644
--- a/drivers/mtd/nand/bcm_umi_nand.c
+++ b/drivers/mtd/nand/bcm_umi_nand.c
@@ -52,8 +52,6 @@
static const __devinitconst char gBanner[] = KERN_INFO \
"BCM UMI MTD NAND Driver: 1.00\n";
-const char *part_probes[] = { "cmdlinepart", NULL };
-
#if NAND_ECC_BCH
static uint8_t scan_ff_pattern[] = { 0xff };
@@ -376,16 +374,18 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r)
- return -ENXIO;
+ if (!r) {
+ err = -ENXIO;
+ goto out_free;
+ }
/* map physical address */
- bcm_umi_io_base = ioremap(r->start, r->end - r->start + 1);
+ bcm_umi_io_base = ioremap(r->start, resource_size(r));
if (!bcm_umi_io_base) {
printk(KERN_ERR "ioremap to access BCM UMI NAND chip failed\n");
- kfree(board_mtd);
- return -EIO;
+ err = -EIO;
+ goto out_free;
}
/* Get pointer to private data */
@@ -401,9 +401,8 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
/* Initialize the NAND hardware. */
if (bcm_umi_nand_inithw() < 0) {
printk(KERN_ERR "BCM UMI NAND chip could not be initialized\n");
- iounmap(bcm_umi_io_base);
- kfree(board_mtd);
- return -EIO;
+ err = -EIO;
+ goto out_unmap;
}
/* Set address of NAND IO lines */
@@ -436,7 +435,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
#if USE_DMA
err = nand_dma_init();
if (err != 0)
- return err;
+ goto out_unmap;
#endif
/* Figure out the size of the device that we have.
@@ -447,9 +446,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
err = nand_scan_ident(board_mtd, 1, NULL);
if (err) {
printk(KERN_ERR "nand_scan failed: %d\n", err);
- iounmap(bcm_umi_io_base);
- kfree(board_mtd);
- return err;
+ goto out_unmap;
}
/* Now that we know the nand size, we can setup the ECC layout */
@@ -468,13 +465,14 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
{
printk(KERN_ERR "NAND - Unrecognized pagesize: %d\n",
board_mtd->writesize);
- return -EINVAL;
+ err = -EINVAL;
+ goto out_unmap;
}
}
#if NAND_ECC_BCH
if (board_mtd->writesize > 512) {
- if (this->options & NAND_USE_FLASH_BBT)
+ if (this->bbt_options & NAND_BBT_USE_FLASH)
largepage_bbt.options = NAND_BBT_SCAN2NDPAGE;
this->badblock_pattern = &largepage_bbt;
}
@@ -485,33 +483,20 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
err = nand_scan_tail(board_mtd);
if (err) {
printk(KERN_ERR "nand_scan failed: %d\n", err);
- iounmap(bcm_umi_io_base);
- kfree(board_mtd);
- return err;
+ goto out_unmap;
}
/* Register the partitions */
- {
- int nr_partitions;
- struct mtd_partition *partition_info;
-
- board_mtd->name = "bcm_umi-nand";
- nr_partitions =
- parse_mtd_partitions(board_mtd, part_probes,
- &partition_info, 0);
-
- if (nr_partitions <= 0) {
- printk(KERN_ERR "BCM UMI NAND: Too few partitions - %d\n",
- nr_partitions);
- iounmap(bcm_umi_io_base);
- kfree(board_mtd);
- return -EIO;
- }
- mtd_device_register(board_mtd, partition_info, nr_partitions);
- }
+ board_mtd->name = "bcm_umi-nand";
+ mtd_device_parse_register(board_mtd, NULL, 0, NULL, 0);
/* Return happy */
return 0;
+out_unmap:
+ iounmap(bcm_umi_io_base);
+out_free:
+ kfree(board_mtd);
+ return err;
}
static int bcm_umi_nand_remove(struct platform_device *pdev)
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index 87ebb4e5b0c3..72d3f23490c5 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <asm/io.h>
#define CAFE_NAND_CTRL1 0x00
@@ -57,7 +58,6 @@
struct cafe_priv {
struct nand_chip nand;
- struct mtd_partition *parts;
struct pci_dev *pdev;
void __iomem *mmio;
struct rs_control *rs;
@@ -371,7 +371,7 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
return 1;
}
/**
- * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read
+ * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
@@ -630,8 +630,6 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
struct cafe_priv *cafe;
uint32_t ctrl;
int err = 0;
- struct mtd_partition *parts;
- int nr_parts;
/* Very old versions shared the same PCI ident for all three
functions on the chip. Verify the class too... */
@@ -686,7 +684,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
cafe->nand.chip_delay = 0;
/* Enable the following for a flash based bad block table */
- cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS;
+ cafe->nand.bbt_options = NAND_BBT_USE_FLASH;
+ cafe->nand.options = NAND_NO_AUTOINCR | NAND_OWN_BUFFERS;
if (skipbbt) {
cafe->nand.options |= NAND_SKIP_BBTSCAN;
@@ -799,18 +798,9 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, mtd);
- /* We register the whole device first, separate from the partitions */
- mtd_device_register(mtd, NULL, 0);
-
-#ifdef CONFIG_MTD_CMDLINE_PARTS
mtd->name = "cafe_nand";
-#endif
- nr_parts = parse_mtd_partitions(mtd, part_probes, &parts, 0);
- if (nr_parts > 0) {
- cafe->parts = parts;
- dev_info(&cafe->pdev->dev, "%d partitions found\n", nr_parts);
- mtd_device_register(mtd, parts, nr_parts);
- }
+ mtd_device_parse_register(mtd, part_probes, 0, NULL, 0);
+
goto out;
out_irq:
diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c
index 6fc043a30d1e..737ef9a04fdb 100644
--- a/drivers/mtd/nand/cmx270_nand.c
+++ b/drivers/mtd/nand/cmx270_nand.c
@@ -22,6 +22,7 @@
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
#include <linux/gpio.h>
+#include <linux/module.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -50,8 +51,6 @@ static struct mtd_partition partition_info[] = {
};
#define NUM_PARTITIONS (ARRAY_SIZE(partition_info))
-const char *part_probes[] = { "cmdlinepart", NULL };
-
static u_char cmx270_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
@@ -151,9 +150,6 @@ static int cmx270_device_ready(struct mtd_info *mtd)
static int __init cmx270_init(void)
{
struct nand_chip *this;
- const char *part_type;
- struct mtd_partition *mtd_parts;
- int mtd_parts_nb = 0;
int ret;
if (!(machine_is_armcore() && cpu_is_pxa27x()))
@@ -222,23 +218,9 @@ static int __init cmx270_init(void)
goto err_scan;
}
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- mtd_parts_nb = parse_mtd_partitions(cmx270_nand_mtd, part_probes,
- &mtd_parts, 0);
- if (mtd_parts_nb > 0)
- part_type = "command line";
- else
- mtd_parts_nb = 0;
-#endif
- if (!mtd_parts_nb) {
- mtd_parts = partition_info;
- mtd_parts_nb = NUM_PARTITIONS;
- part_type = "static";
- }
-
/* Register the partitions */
- pr_notice("Using %s partition definition\n", part_type);
- ret = mtd_device_register(cmx270_nand_mtd, mtd_parts, mtd_parts_nb);
+ ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, 0,
+ partition_info, NUM_PARTITIONS);
if (ret)
goto err_scan;
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
index f59ad1f2d5db..414afa793563 100644
--- a/drivers/mtd/nand/cs553x_nand.c
+++ b/drivers/mtd/nand/cs553x_nand.c
@@ -239,7 +239,8 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
this->ecc.correct = nand_correct_data;
/* Enable the following for a flash based bad block table */
- this->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR;
+ this->bbt_options = NAND_BBT_USE_FLASH;
+ this->options = NAND_NO_AUTOINCR;
/* Scan to find existence of the device */
if (nand_scan(new_mtd, 1)) {
@@ -277,15 +278,11 @@ static int is_geode(void)
return 0;
}
-static const char *part_probes[] = { "cmdlinepart", NULL };
-
static int __init cs553x_init(void)
{
int err = -ENXIO;
int i;
uint64_t val;
- int mtd_parts_nb = 0;
- struct mtd_partition *mtd_parts = NULL;
/* If the CPU isn't a Geode GX or LX, abort */
if (!is_geode())
@@ -315,13 +312,9 @@ static int __init cs553x_init(void)
do mtdconcat etc. if we want to. */
for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
if (cs553x_mtd[i]) {
-
/* If any devices registered, return success. Else the last error. */
- mtd_parts_nb = parse_mtd_partitions(cs553x_mtd[i], part_probes, &mtd_parts, 0);
- if (mtd_parts_nb > 0)
- printk(KERN_NOTICE "Using command line partition definition\n");
- mtd_device_register(cs553x_mtd[i], mtd_parts,
- mtd_parts_nb);
+ mtd_device_parse_register(cs553x_mtd[i], NULL, 0,
+ NULL, 0);
err = 0;
}
}
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index 1f34951ae1a7..c153e1f77f90 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -57,7 +57,6 @@ struct davinci_nand_info {
struct device *dev;
struct clk *clk;
- bool partitioned;
bool is_readmode;
@@ -530,8 +529,6 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
int ret;
uint32_t val;
nand_ecc_modes_t ecc_mode;
- struct mtd_partition *mtd_parts = NULL;
- int mtd_parts_nb = 0;
/* insist on board-specific configuration */
if (!pdata)
@@ -581,7 +578,9 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
info->chip.chip_delay = 0;
info->chip.select_chip = nand_davinci_select_chip;
- /* options such as NAND_USE_FLASH_BBT or 16-bit widths */
+ /* options such as NAND_BBT_USE_FLASH */
+ info->chip.bbt_options = pdata->bbt_options;
+ /* options such as 16-bit widths */
info->chip.options = pdata->options;
info->chip.bbt_td = pdata->bbt_td;
info->chip.bbt_md = pdata->bbt_md;
@@ -751,33 +750,8 @@ syndrome_done:
if (ret < 0)
goto err_scan;
- if (mtd_has_cmdlinepart()) {
- static const char *probes[] __initconst = {
- "cmdlinepart", NULL
- };
-
- mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes,
- &mtd_parts, 0);
- }
-
- if (mtd_parts_nb <= 0) {
- mtd_parts = pdata->parts;
- mtd_parts_nb = pdata->nr_parts;
- }
-
- /* Register any partitions */
- if (mtd_parts_nb > 0) {
- ret = mtd_device_register(&info->mtd, mtd_parts,
- mtd_parts_nb);
- if (ret == 0)
- info->partitioned = true;
- }
-
- /* If there's no partition info, just package the whole chip
- * as a single MTD device.
- */
- if (!info->partitioned)
- ret = mtd_device_register(&info->mtd, NULL, 0) ? -ENODEV : 0;
+ ret = mtd_device_parse_register(&info->mtd, NULL, 0,
+ pdata->parts, pdata->nr_parts);
if (ret < 0)
goto err_scan;
@@ -816,9 +790,6 @@ err_nomem:
static int __exit nand_davinci_remove(struct platform_device *pdev)
{
struct davinci_nand_info *info = platform_get_drvdata(pdev);
- int status;
-
- status = mtd_device_unregister(&info->mtd);
spin_lock_irq(&davinci_nand_lock);
if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index d5276218945f..3984d488f9ab 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1346,6 +1346,7 @@ static void denali_hw_init(struct denali_nand_info *denali)
* */
denali->bbtskipbytes = ioread32(denali->flash_reg +
SPARE_AREA_SKIP_BYTES);
+ detect_max_banks(denali);
denali_nand_reset(denali);
iowrite32(0x0F, denali->flash_reg + RB_PIN_ENABLED);
iowrite32(CHIP_EN_DONT_CARE__FLAG,
@@ -1356,7 +1357,6 @@ static void denali_hw_init(struct denali_nand_info *denali)
/* Should set value for these registers when init */
iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
iowrite32(1, denali->flash_reg + ECC_ENABLE);
- detect_max_banks(denali);
denali_nand_timing_set(denali);
denali_irq_init(denali);
}
@@ -1577,7 +1577,8 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
denali->nand.bbt_md = &bbt_mirror_descr;
/* skip the scan for now until we have OOB read and write support */
- denali->nand.options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN;
+ denali->nand.bbt_options |= NAND_BBT_USE_FLASH;
+ denali->nand.options |= NAND_SKIP_BBTSCAN;
denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
/* Denali Controller only support 15bit and 8bit ECC in MRST,
@@ -1676,7 +1677,6 @@ static void denali_pci_remove(struct pci_dev *dev)
struct denali_nand_info *denali = pci_get_drvdata(dev);
nand_release(&denali->mtd);
- mtd_device_unregister(&denali->mtd);
denali_irq_cleanup(dev->irq, denali);
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index 7837728d02ff..5780dbab6113 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -31,6 +31,7 @@
#include <linux/mtd/doc2000.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/inftl.h>
+#include <linux/module.h>
/* Where to look for the devices? */
#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS
@@ -132,7 +133,7 @@ static struct rs_control *rs_decoder;
/*
* The HW decoder in the DoC ASIC's provides us a error syndrome,
- * which we must convert to a standard syndrom usable by the generic
+ * which we must convert to a standard syndrome usable by the generic
* Reed-Solomon library code.
*
* Fabrice Bellard figured this out in the old docecc code. I added
@@ -153,7 +154,7 @@ static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)
ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2);
parity = ecc[1];
- /* Initialize the syndrom buffer */
+ /* Initialize the syndrome buffer */
for (i = 0; i < NROOTS; i++)
s[i] = ds[0];
/*
@@ -1031,7 +1032,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
else
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
- if (no_ecc_failures && (ret == -EBADMSG)) {
+ if (no_ecc_failures && mtd_is_eccerr(ret)) {
printk(KERN_ERR "suppressing ECC failure\n");
ret = 0;
}
@@ -1652,7 +1653,7 @@ static int __init doc_probe(unsigned long physadr)
nand->ecc.mode = NAND_ECC_HW_SYNDROME;
nand->ecc.size = 512;
nand->ecc.bytes = 6;
- nand->options = NAND_USE_FLASH_BBT;
+ nand->bbt_options = NAND_BBT_USE_FLASH;
doc->physadr = physadr;
doc->virtadr = virtadr;
diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c
deleted file mode 100644
index 8400d0f6dada..000000000000
--- a/drivers/mtd/nand/edb7312.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * drivers/mtd/nand/edb7312.c
- *
- * Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
- *
- * Derived from drivers/mtd/nand/autcpu12.c
- * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Overview:
- * This is a device driver for the NAND flash device found on the
- * CLEP7312 board which utilizes the Toshiba TC58V64AFT part. This is
- * a 64Mibit (8MiB x 8 bits) NAND flash device.
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/partitions.h>
-#include <asm/io.h>
-#include <mach/hardware.h> /* for CLPS7111_VIRT_BASE */
-#include <asm/sizes.h>
-#include <asm/hardware/clps7111.h>
-
-/*
- * MTD structure for EDB7312 board
- */
-static struct mtd_info *ep7312_mtd = NULL;
-
-/*
- * Values specific to the EDB7312 board (used with EP7312 processor)
- */
-#define EP7312_FIO_PBASE 0x10000000 /* Phys address of flash */
-#define EP7312_PXDR 0x0001 /*
- * IO offset to Port B data register
- * where the CLE, ALE and NCE pins
- * are wired to.
- */
-#define EP7312_PXDDR 0x0041 /*
- * IO offset to Port B data direction
- * register so we can control the IO
- * lines.
- */
-
-/*
- * Module stuff
- */
-
-static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE;
-static void __iomem *ep7312_pxdr = (void __iomem *)EP7312_PXDR;
-static void __iomem *ep7312_pxddr = (void __iomem *)EP7312_PXDDR;
-
-/*
- * Define static partitions for flash device
- */
-static struct mtd_partition partition_info[] = {
- {.name = "EP7312 Nand Flash",
- .offset = 0,
- .size = 8 * 1024 * 1024}
-};
-
-#define NUM_PARTITIONS 1
-
-/*
- * hardware specific access to control-lines
- *
- * NAND_NCE: bit 0 -> bit 6 (bit 7 = 1)
- * NAND_CLE: bit 1 -> bit 4
- * NAND_ALE: bit 2 -> bit 5
- */
-static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
-{
- struct nand_chip *chip = mtd->priv;
-
- if (ctrl & NAND_CTRL_CHANGE) {
- unsigned char bits = 0x80;
-
- bits |= (ctrl & (NAND_CLE | NAND_ALE)) << 3;
- bits |= (ctrl & NAND_NCE) ? 0x00 : 0x40;
-
- clps_writeb((clps_readb(ep7312_pxdr) & 0xF0) | bits,
- ep7312_pxdr);
- }
- if (cmd != NAND_CMD_NONE)
- writeb(cmd, chip->IO_ADDR_W);
-}
-
-/*
- * read device ready pin
- */
-static int ep7312_device_ready(struct mtd_info *mtd)
-{
- return 1;
-}
-
-const char *part_probes[] = { "cmdlinepart", NULL };
-
-/*
- * Main initialization routine
- */
-static int __init ep7312_init(void)
-{
- struct nand_chip *this;
- const char *part_type = 0;
- int mtd_parts_nb = 0;
- struct mtd_partition *mtd_parts = 0;
- void __iomem *ep7312_fio_base;
-
- /* Allocate memory for MTD device structure and private data */
- ep7312_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
- if (!ep7312_mtd) {
- printk("Unable to allocate EDB7312 NAND MTD device structure.\n");
- return -ENOMEM;
- }
-
- /* map physical address */
- ep7312_fio_base = ioremap(ep7312_fio_pbase, SZ_1K);
- if (!ep7312_fio_base) {
- printk("ioremap EDB7312 NAND flash failed\n");
- kfree(ep7312_mtd);
- return -EIO;
- }
-
- /* Get pointer to private data */
- this = (struct nand_chip *)(&ep7312_mtd[1]);
-
- /* Initialize structures */
- memset(ep7312_mtd, 0, sizeof(struct mtd_info));
- memset(this, 0, sizeof(struct nand_chip));
-
- /* Link the private data with the MTD structure */
- ep7312_mtd->priv = this;
- ep7312_mtd->owner = THIS_MODULE;
-
- /*
- * Set GPIO Port B control register so that the pins are configured
- * to be outputs for controlling the NAND flash.
- */
- clps_writeb(0xf0, ep7312_pxddr);
-
- /* insert callbacks */
- this->IO_ADDR_R = ep7312_fio_base;
- this->IO_ADDR_W = ep7312_fio_base;
- this->cmd_ctrl = ep7312_hwcontrol;
- this->dev_ready = ep7312_device_ready;
- /* 15 us command delay time */
- this->chip_delay = 15;
-
- /* Scan to find existence of the device */
- if (nand_scan(ep7312_mtd, 1)) {
- iounmap((void *)ep7312_fio_base);
- kfree(ep7312_mtd);
- return -ENXIO;
- }
- ep7312_mtd->name = "edb7312-nand";
- mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, &mtd_parts, 0);
- if (mtd_parts_nb > 0)
- part_type = "command line";
- else
- mtd_parts_nb = 0;
- if (mtd_parts_nb == 0) {
- mtd_parts = partition_info;
- mtd_parts_nb = NUM_PARTITIONS;
- part_type = "static";
- }
-
- /* Register the partitions */
- printk(KERN_NOTICE "Using %s partition definition\n", part_type);
- mtd_device_register(ep7312_mtd, mtd_parts, mtd_parts_nb);
-
- /* Return happy */
- return 0;
-}
-
-module_init(ep7312_init);
-
-/*
- * Clean up routine
- */
-static void __exit ep7312_cleanup(void)
-{
- struct nand_chip *this = (struct nand_chip *)&ep7312_mtd[1];
-
- /* Release resources, unregister device */
- nand_release(ap7312_mtd);
-
- /* Release io resource */
- iounmap(this->IO_ADDR_R);
-
- /* Free the MTD device structure */
- kfree(ep7312_mtd);
-}
-
-module_exit(ep7312_cleanup);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
-MODULE_DESCRIPTION("MTD map driver for Cogent EDB7312 board");
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index 33d8aad8bba5..eedd8ee2c9ac 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -75,7 +75,6 @@ struct fsl_elbc_fcm_ctrl {
unsigned int use_mdr; /* Non zero if the MDR is to be set */
unsigned int oob; /* Non zero if operating on OOB data */
unsigned int counter; /* counter for the initializations */
- char *oob_poi; /* Place to write ECC after read back */
};
/* These map to the positions used by the FCM hardware ECC generator */
@@ -244,6 +243,25 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
return -EIO;
}
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return 0;
+
+ if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
+ uint32_t lteccr = in_be32(&lbc->lteccr);
+ /*
+ * if command was a full page read and the ELBC
+ * has the LTECCR register, then bits 12-15 (ppc order) of
+ * LTECCR indicates which 512 byte sub-pages had fixed errors.
+ * bits 28-31 are uncorrectable errors, marked elsewhere.
+ * for small page nand only 1 bit is used.
+ * if the ELBC doesn't have the lteccr register it reads 0
+ */
+ if (lteccr & 0x000F000F)
+ out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
+ if (lteccr & 0x000F0000)
+ mtd->ecc_stats.corrected++;
+ }
+
return 0;
}
@@ -435,7 +453,6 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
case NAND_CMD_PAGEPROG: {
- int full_page;
dev_vdbg(priv->dev,
"fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
"writing %d bytes.\n", elbc_fcm_ctrl->index);
@@ -445,34 +462,12 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
* write so the HW generates the ECC.
*/
if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
- elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) {
+ elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize)
out_be32(&lbc->fbcr, elbc_fcm_ctrl->index);
- full_page = 0;
- } else {
+ else
out_be32(&lbc->fbcr, 0);
- full_page = 1;
- }
fsl_elbc_run_command(mtd);
-
- /* Read back the page in order to fill in the ECC for the
- * caller. Is this really needed?
- */
- if (full_page && elbc_fcm_ctrl->oob_poi) {
- out_be32(&lbc->fbcr, 3);
- set_addr(mtd, 6, page_addr, 1);
-
- elbc_fcm_ctrl->read_bytes = mtd->writesize + 9;
-
- fsl_elbc_do_read(chip, 1);
- fsl_elbc_run_command(mtd);
-
- memcpy_fromio(elbc_fcm_ctrl->oob_poi + 6,
- &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], 3);
- elbc_fcm_ctrl->index += 3;
- }
-
- elbc_fcm_ctrl->oob_poi = NULL;
return;
}
@@ -752,13 +747,8 @@ static void fsl_elbc_write_page(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf)
{
- struct fsl_elbc_mtd *priv = chip->priv;
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
-
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- elbc_fcm_ctrl->oob_poi = chip->oob_poi;
}
static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
@@ -791,8 +781,8 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
chip->bbt_md = &bbt_mirror_descr;
/* set up nand options */
- chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
- NAND_USE_FLASH_BBT;
+ chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR;
+ chip->bbt_options = NAND_BBT_USE_FLASH;
chip->controller = &elbc_fcm_ctrl->controller;
chip->priv = priv;
@@ -829,7 +819,6 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
elbc_fcm_ctrl->chips[priv->bank] = NULL;
kfree(priv);
- kfree(elbc_fcm_ctrl);
return 0;
}
@@ -842,13 +831,14 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev)
struct resource res;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
static const char *part_probe_types[]
- = { "cmdlinepart", "RedBoot", NULL };
- struct mtd_partition *parts;
+ = { "cmdlinepart", "RedBoot", "ofpart", NULL };
int ret;
int bank;
struct device *dev;
struct device_node *node = pdev->dev.of_node;
+ struct mtd_part_parser_data ppdata;
+ ppdata.of_node = pdev->dev.of_node;
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV;
lbc = fsl_lbc_ctrl_dev->regs;
@@ -934,17 +924,8 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev)
/* First look for RedBoot table or partitions on the command
* line, these take precedence over device tree information */
- ret = parse_mtd_partitions(&priv->mtd, part_probe_types, &parts, 0);
- if (ret < 0)
- goto err;
-
- if (ret == 0) {
- ret = of_mtd_parse_partitions(priv->dev, node, &parts);
- if (ret < 0)
- goto err;
- }
-
- mtd_device_register(&priv->mtd, parts, ret);
+ mtd_device_parse_register(&priv->mtd, part_probe_types, &ppdata,
+ NULL, 0);
printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n",
(unsigned long long)res.start, priv->bank);
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
index 23752fd5bc59..b4f3cc9f32fb 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -158,7 +158,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
{
int ret;
struct device_node *flash_np;
- static const char *part_types[] = { "cmdlinepart", NULL, };
+ struct mtd_part_parser_data ppdata;
fun->chip.IO_ADDR_R = fun->io_base;
fun->chip.IO_ADDR_W = fun->io_base;
@@ -192,18 +192,12 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
if (ret)
goto err;
- ret = parse_mtd_partitions(&fun->mtd, part_types, &fun->parts, 0);
-
-#ifdef CONFIG_MTD_OF_PARTS
- if (ret == 0) {
- ret = of_mtd_parse_partitions(fun->dev, flash_np, &fun->parts);
- if (ret < 0)
- goto err;
- }
-#endif
- ret = mtd_device_register(&fun->mtd, fun->parts, ret);
+ ppdata.of_node = flash_np;
+ ret = mtd_device_parse_register(&fun->mtd, NULL, &ppdata, NULL, 0);
err:
of_node_put(flash_np);
+ if (ret)
+ kfree(fun->mtd.name);
return ret;
}
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
index e9b275ac381c..e53b76064133 100644
--- a/drivers/mtd/nand/fsmc_nand.c
+++ b/drivers/mtd/nand/fsmc_nand.c
@@ -146,7 +146,7 @@ static struct mtd_partition partition_info_16KB_blk[] = {
{
.name = "Root File System",
.offset = 0x460000,
- .size = 0,
+ .size = MTDPART_SIZ_FULL,
},
};
@@ -173,13 +173,10 @@ static struct mtd_partition partition_info_128KB_blk[] = {
{
.name = "Root File System",
.offset = 0x800000,
- .size = 0,
+ .size = MTDPART_SIZ_FULL,
},
};
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-const char *part_probes[] = { "cmdlinepart", NULL };
-#endif
/**
* struct fsmc_nand_data - structure for FSMC NAND device state
@@ -187,8 +184,6 @@ const char *part_probes[] = { "cmdlinepart", NULL };
* @pid: Part ID on the AMBA PrimeCell format
* @mtd: MTD info for a NAND flash.
* @nand: Chip related info for a NAND flash.
- * @partitions: Partition info for a NAND Flash.
- * @nr_partitions: Total number of partition of a NAND flash.
*
* @ecc_place: ECC placing locations in oobfree type format.
* @bank: Bank number for probed device.
@@ -203,8 +198,6 @@ struct fsmc_nand_data {
u32 pid;
struct mtd_info mtd;
struct nand_chip nand;
- struct mtd_partition *partitions;
- unsigned int nr_partitions;
struct fsmc_eccplace *ecc_place;
unsigned int bank;
@@ -716,65 +709,17 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
* platform data,
* default partition information present in driver.
*/
-#ifdef CONFIG_MTD_CMDLINE_PARTS
/*
- * Check if partition info passed via command line
+ * Check for partition info passed
*/
host->mtd.name = "nand";
- host->nr_partitions = parse_mtd_partitions(&host->mtd, part_probes,
- &host->partitions, 0);
- if (host->nr_partitions <= 0) {
-#endif
- /*
- * Check if partition info passed via command line
- */
- if (pdata->partitions) {
- host->partitions = pdata->partitions;
- host->nr_partitions = pdata->nr_partitions;
- } else {
- struct mtd_partition *partition;
- int i;
-
- /* Select the default partitions info */
- switch (host->mtd.size) {
- case 0x01000000:
- case 0x02000000:
- case 0x04000000:
- host->partitions = partition_info_16KB_blk;
- host->nr_partitions =
- sizeof(partition_info_16KB_blk) /
- sizeof(struct mtd_partition);
- break;
- case 0x08000000:
- case 0x10000000:
- case 0x20000000:
- case 0x40000000:
- host->partitions = partition_info_128KB_blk;
- host->nr_partitions =
- sizeof(partition_info_128KB_blk) /
- sizeof(struct mtd_partition);
- break;
- default:
- ret = -ENXIO;
- pr_err("Unsupported NAND size\n");
- goto err_probe;
- }
-
- partition = host->partitions;
- for (i = 0; i < host->nr_partitions; i++, partition++) {
- if (partition->size == 0) {
- partition->size = host->mtd.size -
- partition->offset;
- break;
- }
- }
- }
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- }
-#endif
-
- ret = mtd_device_register(&host->mtd, host->partitions,
- host->nr_partitions);
+ ret = mtd_device_parse_register(&host->mtd, NULL, 0,
+ host->mtd.size <= 0x04000000 ?
+ partition_info_16KB_blk :
+ partition_info_128KB_blk,
+ host->mtd.size <= 0x04000000 ?
+ ARRAY_SIZE(partition_info_16KB_blk) :
+ ARRAY_SIZE(partition_info_128KB_blk));
if (ret)
goto err_probe;
@@ -822,7 +767,7 @@ static int fsmc_nand_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
if (host) {
- mtd_device_unregister(&host->mtd);
+ nand_release(&host->mtd);
clk_disable(host->clk);
clk_put(host->clk);
diff --git a/drivers/mtd/nand/gpmi-nand/Makefile b/drivers/mtd/nand/gpmi-nand/Makefile
new file mode 100644
index 000000000000..3a462487c35e
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o
+gpmi_nand-objs += gpmi-nand.o
+gpmi_nand-objs += gpmi-lib.o
diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h
new file mode 100644
index 000000000000..4effb8c579db
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h
@@ -0,0 +1,84 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef __GPMI_NAND_BCH_REGS_H
+#define __GPMI_NAND_BCH_REGS_H
+
+#define HW_BCH_CTRL 0x00000000
+#define HW_BCH_CTRL_SET 0x00000004
+#define HW_BCH_CTRL_CLR 0x00000008
+#define HW_BCH_CTRL_TOG 0x0000000c
+
+#define BM_BCH_CTRL_COMPLETE_IRQ_EN (1 << 8)
+#define BM_BCH_CTRL_COMPLETE_IRQ (1 << 0)
+
+#define HW_BCH_STATUS0 0x00000010
+#define HW_BCH_MODE 0x00000020
+#define HW_BCH_ENCODEPTR 0x00000030
+#define HW_BCH_DATAPTR 0x00000040
+#define HW_BCH_METAPTR 0x00000050
+#define HW_BCH_LAYOUTSELECT 0x00000070
+
+#define HW_BCH_FLASH0LAYOUT0 0x00000080
+
+#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH0LAYOUT0_NBLOCKS (0xff << BP_BCH_FLASH0LAYOUT0_NBLOCKS)
+#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \
+ (((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS)
+
+#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH0LAYOUT0_META_SIZE (0xff << BP_BCH_FLASH0LAYOUT0_META_SIZE)
+#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \
+ (((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\
+ & BM_BCH_FLASH0LAYOUT0_META_SIZE)
+
+#define BP_BCH_FLASH0LAYOUT0_ECC0 12
+#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0)
+#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \
+ (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0)
+
+#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \
+ (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \
+ (((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\
+ & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+
+#define HW_BCH_FLASH0LAYOUT1 0x00000090
+
+#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE \
+ (0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \
+ (((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \
+ & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+
+#define BP_BCH_FLASH0LAYOUT1_ECCN 12
+#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN)
+#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \
+ (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN)
+
+#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \
+ (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \
+ (((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
+ & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+#endif
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
new file mode 100644
index 000000000000..de4db7604a3f
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
@@ -0,0 +1,1057 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <linux/mtd/gpmi-nand.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <mach/mxs.h>
+
+#include "gpmi-nand.h"
+#include "gpmi-regs.h"
+#include "bch-regs.h"
+
+struct timing_threshod timing_default_threshold = {
+ .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
+ BP_GPMI_TIMING0_DATA_SETUP),
+ .internal_data_setup_in_ns = 0,
+ .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >>
+ BP_GPMI_CTRL1_RDN_DELAY),
+ .max_dll_clock_period_in_ns = 32,
+ .max_dll_delay_in_ns = 16,
+};
+
+/*
+ * Clear the bit and poll it cleared. This is usually called with
+ * a reset address and mask being either SFTRST(bit 31) or CLKGATE
+ * (bit 30).
+ */
+static int clear_poll_bit(void __iomem *addr, u32 mask)
+{
+ int timeout = 0x400;
+
+ /* clear the bit */
+ __mxs_clrl(mask, addr);
+
+ /*
+ * SFTRST needs 3 GPMI clocks to settle, the reference manual
+ * recommends to wait 1us.
+ */
+ udelay(1);
+
+ /* poll the bit becoming clear */
+ while ((readl(addr) & mask) && --timeout)
+ /* nothing */;
+
+ return !timeout;
+}
+
+#define MODULE_CLKGATE (1 << 30)
+#define MODULE_SFTRST (1 << 31)
+/*
+ * The current mxs_reset_block() will do two things:
+ * [1] enable the module.
+ * [2] reset the module.
+ *
+ * In most of the cases, it's ok. But there is a hardware bug in the BCH block.
+ * If you try to soft reset the BCH block, it becomes unusable until
+ * the next hard reset. This case occurs in the NAND boot mode. When the board
+ * boots by NAND, the ROM of the chip will initialize the BCH blocks itself.
+ * So If the driver tries to reset the BCH again, the BCH will not work anymore.
+ * You will see a DMA timeout in this case.
+ *
+ * To avoid this bug, just add a new parameter `just_enable` for
+ * the mxs_reset_block(), and rewrite it here.
+ */
+int gpmi_reset_block(void __iomem *reset_addr, bool just_enable)
+{
+ int ret;
+ int timeout = 0x400;
+
+ /* clear and poll SFTRST */
+ ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+ if (unlikely(ret))
+ goto error;
+
+ /* clear CLKGATE */
+ __mxs_clrl(MODULE_CLKGATE, reset_addr);
+
+ if (!just_enable) {
+ /* set SFTRST to reset the block */
+ __mxs_setl(MODULE_SFTRST, reset_addr);
+ udelay(1);
+
+ /* poll CLKGATE becoming set */
+ while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout)
+ /* nothing */;
+ if (unlikely(!timeout))
+ goto error;
+ }
+
+ /* clear and poll SFTRST */
+ ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+ if (unlikely(ret))
+ goto error;
+
+ /* clear and poll CLKGATE */
+ ret = clear_poll_bit(reset_addr, MODULE_CLKGATE);
+ if (unlikely(ret))
+ goto error;
+
+ return 0;
+
+error:
+ pr_err("%s(%p): module reset timeout\n", __func__, reset_addr);
+ return -ETIMEDOUT;
+}
+
+int gpmi_init(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ int ret;
+
+ ret = clk_enable(r->clock);
+ if (ret)
+ goto err_out;
+ ret = gpmi_reset_block(r->gpmi_regs, false);
+ if (ret)
+ goto err_out;
+
+ /* Choose NAND mode. */
+ writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+ /* Set the IRQ polarity. */
+ writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
+ r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Disable Write-Protection. */
+ writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Select BCH ECC. */
+ writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ clk_disable(r->clock);
+ return 0;
+err_out:
+ return ret;
+}
+
+/* This function is very useful. It is called only when the bug occur. */
+void gpmi_dump_info(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ struct bch_geometry *geo = &this->bch_geometry;
+ u32 reg;
+ int i;
+
+ pr_err("Show GPMI registers :\n");
+ for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) {
+ reg = readl(r->gpmi_regs + i * 0x10);
+ pr_err("offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
+ }
+
+ /* start to print out the BCH info */
+ pr_err("BCH Geometry :\n");
+ pr_err("GF length : %u\n", geo->gf_len);
+ pr_err("ECC Strength : %u\n", geo->ecc_strength);
+ pr_err("Page Size in Bytes : %u\n", geo->page_size);
+ pr_err("Metadata Size in Bytes : %u\n", geo->metadata_size);
+ pr_err("ECC Chunk Size in Bytes: %u\n", geo->ecc_chunk_size);
+ pr_err("ECC Chunk Count : %u\n", geo->ecc_chunk_count);
+ pr_err("Payload Size in Bytes : %u\n", geo->payload_size);
+ pr_err("Auxiliary Size in Bytes: %u\n", geo->auxiliary_size);
+ pr_err("Auxiliary Status Offset: %u\n", geo->auxiliary_status_offset);
+ pr_err("Block Mark Byte Offset : %u\n", geo->block_mark_byte_offset);
+ pr_err("Block Mark Bit Offset : %u\n", geo->block_mark_bit_offset);
+}
+
+/* Configures the geometry for BCH. */
+int bch_set_geometry(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ struct bch_geometry *bch_geo = &this->bch_geometry;
+ unsigned int block_count;
+ unsigned int block_size;
+ unsigned int metadata_size;
+ unsigned int ecc_strength;
+ unsigned int page_size;
+ int ret;
+
+ if (common_nfc_set_geometry(this))
+ return !0;
+
+ block_count = bch_geo->ecc_chunk_count - 1;
+ block_size = bch_geo->ecc_chunk_size;
+ metadata_size = bch_geo->metadata_size;
+ ecc_strength = bch_geo->ecc_strength >> 1;
+ page_size = bch_geo->page_size;
+
+ ret = clk_enable(r->clock);
+ if (ret)
+ goto err_out;
+
+ ret = gpmi_reset_block(r->bch_regs, true);
+ if (ret)
+ goto err_out;
+
+ /* Configure layout 0. */
+ writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count)
+ | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size)
+ | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength)
+ | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size),
+ r->bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size)
+ | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength)
+ | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size),
+ r->bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ /* Set *all* chip selects to use layout 0. */
+ writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT);
+
+ /* Enable interrupts. */
+ writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
+ r->bch_regs + HW_BCH_CTRL_SET);
+
+ clk_disable(r->clock);
+ return 0;
+err_out:
+ return ret;
+}
+
+/* Converts time in nanoseconds to cycles. */
+static unsigned int ns_to_cycles(unsigned int time,
+ unsigned int period, unsigned int min)
+{
+ unsigned int k;
+
+ k = (time + period - 1) / period;
+ return max(k, min);
+}
+
+/* Apply timing to current hardware conditions. */
+static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
+ struct gpmi_nfc_hardware_timing *hw)
+{
+ struct gpmi_nand_platform_data *pdata = this->pdata;
+ struct timing_threshod *nfc = &timing_default_threshold;
+ struct nand_chip *nand = &this->nand;
+ struct nand_timing target = this->timing;
+ bool improved_timing_is_available;
+ unsigned long clock_frequency_in_hz;
+ unsigned int clock_period_in_ns;
+ bool dll_use_half_periods;
+ unsigned int dll_delay_shift;
+ unsigned int max_sample_delay_in_ns;
+ unsigned int address_setup_in_cycles;
+ unsigned int data_setup_in_ns;
+ unsigned int data_setup_in_cycles;
+ unsigned int data_hold_in_cycles;
+ int ideal_sample_delay_in_ns;
+ unsigned int sample_delay_factor;
+ int tEYE;
+ unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns;
+ unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns;
+
+ /*
+ * If there are multiple chips, we need to relax the timings to allow
+ * for signal distortion due to higher capacitance.
+ */
+ if (nand->numchips > 2) {
+ target.data_setup_in_ns += 10;
+ target.data_hold_in_ns += 10;
+ target.address_setup_in_ns += 10;
+ } else if (nand->numchips > 1) {
+ target.data_setup_in_ns += 5;
+ target.data_hold_in_ns += 5;
+ target.address_setup_in_ns += 5;
+ }
+
+ /* Check if improved timing information is available. */
+ improved_timing_is_available =
+ (target.tREA_in_ns >= 0) &&
+ (target.tRLOH_in_ns >= 0) &&
+ (target.tRHOH_in_ns >= 0) ;
+
+ /* Inspect the clock. */
+ clock_frequency_in_hz = nfc->clock_frequency_in_hz;
+ clock_period_in_ns = 1000000000 / clock_frequency_in_hz;
+
+ /*
+ * The NFC quantizes setup and hold parameters in terms of clock cycles.
+ * Here, we quantize the setup and hold timing parameters to the
+ * next-highest clock period to make sure we apply at least the
+ * specified times.
+ *
+ * For data setup and data hold, the hardware interprets a value of zero
+ * as the largest possible delay. This is not what's intended by a zero
+ * in the input parameter, so we impose a minimum of one cycle.
+ */
+ data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns,
+ clock_period_in_ns, 1);
+ data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns,
+ clock_period_in_ns, 1);
+ address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns,
+ clock_period_in_ns, 0);
+
+ /*
+ * The clock's period affects the sample delay in a number of ways:
+ *
+ * (1) The NFC HAL tells us the maximum clock period the sample delay
+ * DLL can tolerate. If the clock period is greater than half that
+ * maximum, we must configure the DLL to be driven by half periods.
+ *
+ * (2) We need to convert from an ideal sample delay, in ns, to a
+ * "sample delay factor," which the NFC uses. This factor depends on
+ * whether we're driving the DLL with full or half periods.
+ * Paraphrasing the reference manual:
+ *
+ * AD = SDF x 0.125 x RP
+ *
+ * where:
+ *
+ * AD is the applied delay, in ns.
+ * SDF is the sample delay factor, which is dimensionless.
+ * RP is the reference period, in ns, which is a full clock period
+ * if the DLL is being driven by full periods, or half that if
+ * the DLL is being driven by half periods.
+ *
+ * Let's re-arrange this in a way that's more useful to us:
+ *
+ * 8
+ * SDF = AD x ----
+ * RP
+ *
+ * The reference period is either the clock period or half that, so this
+ * is:
+ *
+ * 8 AD x DDF
+ * SDF = AD x ----- = --------
+ * f x P P
+ *
+ * where:
+ *
+ * f is 1 or 1/2, depending on how we're driving the DLL.
+ * P is the clock period.
+ * DDF is the DLL Delay Factor, a dimensionless value that
+ * incorporates all the constants in the conversion.
+ *
+ * DDF will be either 8 or 16, both of which are powers of two. We can
+ * reduce the cost of this conversion by using bit shifts instead of
+ * multiplication or division. Thus:
+ *
+ * AD << DDS
+ * SDF = ---------
+ * P
+ *
+ * or
+ *
+ * AD = (SDF >> DDS) x P
+ *
+ * where:
+ *
+ * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF.
+ */
+ if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) {
+ dll_use_half_periods = true;
+ dll_delay_shift = 3 + 1;
+ } else {
+ dll_use_half_periods = false;
+ dll_delay_shift = 3;
+ }
+
+ /*
+ * Compute the maximum sample delay the NFC allows, under current
+ * conditions. If the clock is running too slowly, no sample delay is
+ * possible.
+ */
+ if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns)
+ max_sample_delay_in_ns = 0;
+ else {
+ /*
+ * Compute the delay implied by the largest sample delay factor
+ * the NFC allows.
+ */
+ max_sample_delay_in_ns =
+ (nfc->max_sample_delay_factor * clock_period_in_ns) >>
+ dll_delay_shift;
+
+ /*
+ * Check if the implied sample delay larger than the NFC
+ * actually allows.
+ */
+ if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns)
+ max_sample_delay_in_ns = nfc->max_dll_delay_in_ns;
+ }
+
+ /*
+ * Check if improved timing information is available. If not, we have to
+ * use a less-sophisticated algorithm.
+ */
+ if (!improved_timing_is_available) {
+ /*
+ * Fold the read setup time required by the NFC into the ideal
+ * sample delay.
+ */
+ ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns +
+ nfc->internal_data_setup_in_ns;
+
+ /*
+ * The ideal sample delay may be greater than the maximum
+ * allowed by the NFC. If so, we can trade off sample delay time
+ * for more data setup time.
+ *
+ * In each iteration of the following loop, we add a cycle to
+ * the data setup time and subtract a corresponding amount from
+ * the sample delay until we've satisified the constraints or
+ * can't do any better.
+ */
+ while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) &&
+ (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
+
+ data_setup_in_cycles++;
+ ideal_sample_delay_in_ns -= clock_period_in_ns;
+
+ if (ideal_sample_delay_in_ns < 0)
+ ideal_sample_delay_in_ns = 0;
+
+ }
+
+ /*
+ * Compute the sample delay factor that corresponds most closely
+ * to the ideal sample delay. If the result is too large for the
+ * NFC, use the maximum value.
+ *
+ * Notice that we use the ns_to_cycles function to compute the
+ * sample delay factor. We do this because the form of the
+ * computation is the same as that for calculating cycles.
+ */
+ sample_delay_factor =
+ ns_to_cycles(
+ ideal_sample_delay_in_ns << dll_delay_shift,
+ clock_period_in_ns, 0);
+
+ if (sample_delay_factor > nfc->max_sample_delay_factor)
+ sample_delay_factor = nfc->max_sample_delay_factor;
+
+ /* Skip to the part where we return our results. */
+ goto return_results;
+ }
+
+ /*
+ * If control arrives here, we have more detailed timing information,
+ * so we can use a better algorithm.
+ */
+
+ /*
+ * Fold the read setup time required by the NFC into the maximum
+ * propagation delay.
+ */
+ max_prop_delay_in_ns += nfc->internal_data_setup_in_ns;
+
+ /*
+ * Earlier, we computed the number of clock cycles required to satisfy
+ * the data setup time. Now, we need to know the actual nanoseconds.
+ */
+ data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles;
+
+ /*
+ * Compute tEYE, the width of the data eye when reading from the NAND
+ * Flash. The eye width is fundamentally determined by the data setup
+ * time, perturbed by propagation delays and some characteristics of the
+ * NAND Flash device.
+ *
+ * start of the eye = max_prop_delay + tREA
+ * end of the eye = min_prop_delay + tRHOH + data_setup
+ */
+ tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns +
+ (int)data_setup_in_ns;
+
+ tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns;
+
+ /*
+ * The eye must be open. If it's not, we can try to open it by
+ * increasing its main forcer, the data setup time.
+ *
+ * In each iteration of the following loop, we increase the data setup
+ * time by a single clock cycle. We do this until either the eye is
+ * open or we run into NFC limits.
+ */
+ while ((tEYE <= 0) &&
+ (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
+ /* Give a cycle to data setup. */
+ data_setup_in_cycles++;
+ /* Synchronize the data setup time with the cycles. */
+ data_setup_in_ns += clock_period_in_ns;
+ /* Adjust tEYE accordingly. */
+ tEYE += clock_period_in_ns;
+ }
+
+ /*
+ * When control arrives here, the eye is open. The ideal time to sample
+ * the data is in the center of the eye:
+ *
+ * end of the eye + start of the eye
+ * --------------------------------- - data_setup
+ * 2
+ *
+ * After some algebra, this simplifies to the code immediately below.
+ */
+ ideal_sample_delay_in_ns =
+ ((int)max_prop_delay_in_ns +
+ (int)target.tREA_in_ns +
+ (int)min_prop_delay_in_ns +
+ (int)target.tRHOH_in_ns -
+ (int)data_setup_in_ns) >> 1;
+
+ /*
+ * The following figure illustrates some aspects of a NAND Flash read:
+ *
+ *
+ * __ _____________________________________
+ * RDN \_________________/
+ *
+ * <---- tEYE ----->
+ * /-----------------\
+ * Read Data ----------------------------< >---------
+ * \-----------------/
+ * ^ ^ ^ ^
+ * | | | |
+ * |<--Data Setup -->|<--Delay Time -->| |
+ * | | | |
+ * | | |
+ * | |<-- Quantized Delay Time -->|
+ * | | |
+ *
+ *
+ * We have some issues we must now address:
+ *
+ * (1) The *ideal* sample delay time must not be negative. If it is, we
+ * jam it to zero.
+ *
+ * (2) The *ideal* sample delay time must not be greater than that
+ * allowed by the NFC. If it is, we can increase the data setup
+ * time, which will reduce the delay between the end of the data
+ * setup and the center of the eye. It will also make the eye
+ * larger, which might help with the next issue...
+ *
+ * (3) The *quantized* sample delay time must not fall either before the
+ * eye opens or after it closes (the latter is the problem
+ * illustrated in the above figure).
+ */
+
+ /* Jam a negative ideal sample delay to zero. */
+ if (ideal_sample_delay_in_ns < 0)
+ ideal_sample_delay_in_ns = 0;
+
+ /*
+ * Extend the data setup as needed to reduce the ideal sample delay
+ * below the maximum permitted by the NFC.
+ */
+ while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) &&
+ (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
+
+ /* Give a cycle to data setup. */
+ data_setup_in_cycles++;
+ /* Synchronize the data setup time with the cycles. */
+ data_setup_in_ns += clock_period_in_ns;
+ /* Adjust tEYE accordingly. */
+ tEYE += clock_period_in_ns;
+
+ /*
+ * Decrease the ideal sample delay by one half cycle, to keep it
+ * in the middle of the eye.
+ */
+ ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1);
+
+ /* Jam a negative ideal sample delay to zero. */
+ if (ideal_sample_delay_in_ns < 0)
+ ideal_sample_delay_in_ns = 0;
+ }
+
+ /*
+ * Compute the sample delay factor that corresponds to the ideal sample
+ * delay. If the result is too large, then use the maximum allowed
+ * value.
+ *
+ * Notice that we use the ns_to_cycles function to compute the sample
+ * delay factor. We do this because the form of the computation is the
+ * same as that for calculating cycles.
+ */
+ sample_delay_factor =
+ ns_to_cycles(ideal_sample_delay_in_ns << dll_delay_shift,
+ clock_period_in_ns, 0);
+
+ if (sample_delay_factor > nfc->max_sample_delay_factor)
+ sample_delay_factor = nfc->max_sample_delay_factor;
+
+ /*
+ * These macros conveniently encapsulate a computation we'll use to
+ * continuously evaluate whether or not the data sample delay is inside
+ * the eye.
+ */
+ #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns)
+
+ #define QUANTIZED_DELAY \
+ ((int) ((sample_delay_factor * clock_period_in_ns) >> \
+ dll_delay_shift))
+
+ #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY))
+
+ #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1))
+
+ /*
+ * While the quantized sample time falls outside the eye, reduce the
+ * sample delay or extend the data setup to move the sampling point back
+ * toward the eye. Do not allow the number of data setup cycles to
+ * exceed the maximum allowed by the NFC.
+ */
+ while (SAMPLE_IS_NOT_WITHIN_THE_EYE &&
+ (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
+ /*
+ * If control arrives here, the quantized sample delay falls
+ * outside the eye. Check if it's before the eye opens, or after
+ * the eye closes.
+ */
+ if (QUANTIZED_DELAY > IDEAL_DELAY) {
+ /*
+ * If control arrives here, the quantized sample delay
+ * falls after the eye closes. Decrease the quantized
+ * delay time and then go back to re-evaluate.
+ */
+ if (sample_delay_factor != 0)
+ sample_delay_factor--;
+ continue;
+ }
+
+ /*
+ * If control arrives here, the quantized sample delay falls
+ * before the eye opens. Shift the sample point by increasing
+ * data setup time. This will also make the eye larger.
+ */
+
+ /* Give a cycle to data setup. */
+ data_setup_in_cycles++;
+ /* Synchronize the data setup time with the cycles. */
+ data_setup_in_ns += clock_period_in_ns;
+ /* Adjust tEYE accordingly. */
+ tEYE += clock_period_in_ns;
+
+ /*
+ * Decrease the ideal sample delay by one half cycle, to keep it
+ * in the middle of the eye.
+ */
+ ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1);
+
+ /* ...and one less period for the delay time. */
+ ideal_sample_delay_in_ns -= clock_period_in_ns;
+
+ /* Jam a negative ideal sample delay to zero. */
+ if (ideal_sample_delay_in_ns < 0)
+ ideal_sample_delay_in_ns = 0;
+
+ /*
+ * We have a new ideal sample delay, so re-compute the quantized
+ * delay.
+ */
+ sample_delay_factor =
+ ns_to_cycles(
+ ideal_sample_delay_in_ns << dll_delay_shift,
+ clock_period_in_ns, 0);
+
+ if (sample_delay_factor > nfc->max_sample_delay_factor)
+ sample_delay_factor = nfc->max_sample_delay_factor;
+ }
+
+ /* Control arrives here when we're ready to return our results. */
+return_results:
+ hw->data_setup_in_cycles = data_setup_in_cycles;
+ hw->data_hold_in_cycles = data_hold_in_cycles;
+ hw->address_setup_in_cycles = address_setup_in_cycles;
+ hw->use_half_periods = dll_use_half_periods;
+ hw->sample_delay_factor = sample_delay_factor;
+
+ /* Return success. */
+ return 0;
+}
+
+/* Begin the I/O */
+void gpmi_begin(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ struct timing_threshod *nfc = &timing_default_threshold;
+ unsigned char *gpmi_regs = r->gpmi_regs;
+ unsigned int clock_period_in_ns;
+ uint32_t reg;
+ unsigned int dll_wait_time_in_us;
+ struct gpmi_nfc_hardware_timing hw;
+ int ret;
+
+ /* Enable the clock. */
+ ret = clk_enable(r->clock);
+ if (ret) {
+ pr_err("We failed in enable the clk\n");
+ goto err_out;
+ }
+
+ /* set ready/busy timeout */
+ writel(0x500 << BP_GPMI_TIMING1_BUSY_TIMEOUT,
+ gpmi_regs + HW_GPMI_TIMING1);
+
+ /* Get the timing information we need. */
+ nfc->clock_frequency_in_hz = clk_get_rate(r->clock);
+ clock_period_in_ns = 1000000000 / nfc->clock_frequency_in_hz;
+
+ gpmi_nfc_compute_hardware_timing(this, &hw);
+
+ /* Set up all the simple timing parameters. */
+ reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) |
+ BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) |
+ BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles) ;
+
+ writel(reg, gpmi_regs + HW_GPMI_TIMING0);
+
+ /*
+ * DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD.
+ */
+ writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+ /* Clear out the DLL control fields. */
+ writel(BM_GPMI_CTRL1_RDN_DELAY, gpmi_regs + HW_GPMI_CTRL1_CLR);
+ writel(BM_GPMI_CTRL1_HALF_PERIOD, gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+ /* If no sample delay is called for, return immediately. */
+ if (!hw.sample_delay_factor)
+ return;
+
+ /* Configure the HALF_PERIOD flag. */
+ if (hw.use_half_periods)
+ writel(BM_GPMI_CTRL1_HALF_PERIOD,
+ gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Set the delay factor. */
+ writel(BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor),
+ gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Enable the DLL. */
+ writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /*
+ * After we enable the GPMI DLL, we have to wait 64 clock cycles before
+ * we can use the GPMI.
+ *
+ * Calculate the amount of time we need to wait, in microseconds.
+ */
+ dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000;
+
+ if (!dll_wait_time_in_us)
+ dll_wait_time_in_us = 1;
+
+ /* Wait for the DLL to settle. */
+ udelay(dll_wait_time_in_us);
+
+err_out:
+ return;
+}
+
+void gpmi_end(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ clk_disable(r->clock);
+}
+
+/* Clears a BCH interrupt. */
+void gpmi_clear_bch(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR);
+}
+
+/* Returns the Ready/Busy status of the given chip. */
+int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
+{
+ struct resources *r = &this->resources;
+ uint32_t mask = 0;
+ uint32_t reg = 0;
+
+ if (GPMI_IS_MX23(this)) {
+ mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
+ reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
+ } else if (GPMI_IS_MX28(this)) {
+ mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip);
+ reg = readl(r->gpmi_regs + HW_GPMI_STAT);
+ } else
+ pr_err("unknow arch.\n");
+ return reg & mask;
+}
+
+static inline void set_dma_type(struct gpmi_nand_data *this,
+ enum dma_ops_type type)
+{
+ this->last_dma_type = this->dma_type;
+ this->dma_type = type;
+}
+
+int gpmi_send_command(struct gpmi_nand_data *this)
+{
+ struct dma_chan *channel = get_dma_chan(this);
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist *sgl;
+ int chip = this->current_chip;
+ u32 pio[3];
+
+ /* [1] send out the PIO words */
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE)
+ | BM_GPMI_CTRL0_ADDRESS_INCREMENT
+ | BF_GPMI_CTRL0_XFER_COUNT(this->command_length);
+ pio[1] = pio[2] = 0;
+ desc = channel->device->device_prep_slave_sg(channel,
+ (struct scatterlist *)pio,
+ ARRAY_SIZE(pio), DMA_NONE, 0);
+ if (!desc) {
+ pr_err("step 1 error\n");
+ return -1;
+ }
+
+ /* [2] send out the COMMAND + ADDRESS string stored in @buffer */
+ sgl = &this->cmd_sgl;
+
+ sg_init_one(sgl, this->cmd_buffer, this->command_length);
+ dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE);
+ desc = channel->device->device_prep_slave_sg(channel,
+ sgl, 1, DMA_TO_DEVICE, 1);
+ if (!desc) {
+ pr_err("step 2 error\n");
+ return -1;
+ }
+
+ /* [3] submit the DMA */
+ set_dma_type(this, DMA_FOR_COMMAND);
+ return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_send_data(struct gpmi_nand_data *this)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *channel = get_dma_chan(this);
+ int chip = this->current_chip;
+ uint32_t command_mode;
+ uint32_t address;
+ u32 pio[2];
+
+ /* [1] PIO */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
+ pio[1] = 0;
+ desc = channel->device->device_prep_slave_sg(channel,
+ (struct scatterlist *)pio,
+ ARRAY_SIZE(pio), DMA_NONE, 0);
+ if (!desc) {
+ pr_err("step 1 error\n");
+ return -1;
+ }
+
+ /* [2] send DMA request */
+ prepare_data_dma(this, DMA_TO_DEVICE);
+ desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl,
+ 1, DMA_TO_DEVICE, 1);
+ if (!desc) {
+ pr_err("step 2 error\n");
+ return -1;
+ }
+ /* [3] submit the DMA */
+ set_dma_type(this, DMA_FOR_WRITE_DATA);
+ return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_read_data(struct gpmi_nand_data *this)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *channel = get_dma_chan(this);
+ int chip = this->current_chip;
+ u32 pio[2];
+
+ /* [1] : send PIO */
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA)
+ | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
+ pio[1] = 0;
+ desc = channel->device->device_prep_slave_sg(channel,
+ (struct scatterlist *)pio,
+ ARRAY_SIZE(pio), DMA_NONE, 0);
+ if (!desc) {
+ pr_err("step 1 error\n");
+ return -1;
+ }
+
+ /* [2] : send DMA request */
+ prepare_data_dma(this, DMA_FROM_DEVICE);
+ desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl,
+ 1, DMA_FROM_DEVICE, 1);
+ if (!desc) {
+ pr_err("step 2 error\n");
+ return -1;
+ }
+
+ /* [3] : submit the DMA */
+ set_dma_type(this, DMA_FOR_READ_DATA);
+ return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_send_page(struct gpmi_nand_data *this,
+ dma_addr_t payload, dma_addr_t auxiliary)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ uint32_t command_mode;
+ uint32_t address;
+ uint32_t ecc_command;
+ uint32_t buffer_mask;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *channel = get_dma_chan(this);
+ int chip = this->current_chip;
+ u32 pio[6];
+
+ /* A DMA descriptor that does an ECC page read. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE;
+ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
+ BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(0);
+ pio[1] = 0;
+ pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
+ | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
+ | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+ pio[3] = geo->page_size;
+ pio[4] = payload;
+ pio[5] = auxiliary;
+
+ desc = channel->device->device_prep_slave_sg(channel,
+ (struct scatterlist *)pio,
+ ARRAY_SIZE(pio), DMA_NONE, 0);
+ if (!desc) {
+ pr_err("step 2 error\n");
+ return -1;
+ }
+ set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE);
+ return start_dma_with_bch_irq(this, desc);
+}
+
+int gpmi_read_page(struct gpmi_nand_data *this,
+ dma_addr_t payload, dma_addr_t auxiliary)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ uint32_t command_mode;
+ uint32_t address;
+ uint32_t ecc_command;
+ uint32_t buffer_mask;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *channel = get_dma_chan(this);
+ int chip = this->current_chip;
+ u32 pio[6];
+
+ /* [1] Wait for the chip to report ready. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(0);
+ pio[1] = 0;
+ desc = channel->device->device_prep_slave_sg(channel,
+ (struct scatterlist *)pio, 2, DMA_NONE, 0);
+ if (!desc) {
+ pr_err("step 1 error\n");
+ return -1;
+ }
+
+ /* [2] Enable the BCH block and read. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE;
+ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE
+ | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size);
+
+ pio[1] = 0;
+ pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
+ | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
+ | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+ pio[3] = geo->page_size;
+ pio[4] = payload;
+ pio[5] = auxiliary;
+ desc = channel->device->device_prep_slave_sg(channel,
+ (struct scatterlist *)pio,
+ ARRAY_SIZE(pio), DMA_NONE, 1);
+ if (!desc) {
+ pr_err("step 2 error\n");
+ return -1;
+ }
+
+ /* [3] Disable the BCH block */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size);
+ pio[1] = 0;
+ desc = channel->device->device_prep_slave_sg(channel,
+ (struct scatterlist *)pio, 2, DMA_NONE, 1);
+ if (!desc) {
+ pr_err("step 3 error\n");
+ return -1;
+ }
+
+ /* [4] submit the DMA */
+ set_dma_type(this, DMA_FOR_READ_ECC_PAGE);
+ return start_dma_with_bch_irq(this, desc);
+}
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
new file mode 100644
index 000000000000..071b63420f0e
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -0,0 +1,1619 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/gpmi-nand.h>
+#include <linux/mtd/partitions.h>
+
+#include "gpmi-nand.h"
+
+/* add our owner bbt descriptor */
+static uint8_t scan_ff_pattern[] = { 0xff };
+static struct nand_bbt_descr gpmi_bbt_descr = {
+ .options = 0,
+ .offs = 0,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+
+/* We will use all the (page + OOB). */
+static struct nand_ecclayout gpmi_hw_ecclayout = {
+ .eccbytes = 0,
+ .eccpos = { 0, },
+ .oobfree = { {.offset = 0, .length = 0} }
+};
+
+static irqreturn_t bch_irq(int irq, void *cookie)
+{
+ struct gpmi_nand_data *this = cookie;
+
+ gpmi_clear_bch(this);
+ complete(&this->bch_done);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Calculate the ECC strength by hand:
+ * E : The ECC strength.
+ * G : the length of Galois Field.
+ * N : The chunk count of per page.
+ * O : the oobsize of the NAND chip.
+ * M : the metasize of per page.
+ *
+ * The formula is :
+ * E * G * N
+ * ------------ <= (O - M)
+ * 8
+ *
+ * So, we get E by:
+ * (O - M) * 8
+ * E <= -------------
+ * G * N
+ */
+static inline int get_ecc_strength(struct gpmi_nand_data *this)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ struct mtd_info *mtd = &this->mtd;
+ int ecc_strength;
+
+ ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8)
+ / (geo->gf_len * geo->ecc_chunk_count);
+
+ /* We need the minor even number. */
+ return round_down(ecc_strength, 2);
+}
+
+int common_nfc_set_geometry(struct gpmi_nand_data *this)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ struct mtd_info *mtd = &this->mtd;
+ unsigned int metadata_size;
+ unsigned int status_size;
+ unsigned int block_mark_bit_offset;
+
+ /*
+ * The size of the metadata can be changed, though we set it to 10
+ * bytes now. But it can't be too large, because we have to save
+ * enough space for BCH.
+ */
+ geo->metadata_size = 10;
+
+ /* The default for the length of Galois Field. */
+ geo->gf_len = 13;
+
+ /* The default for chunk size. There is no oobsize greater then 512. */
+ geo->ecc_chunk_size = 512;
+ while (geo->ecc_chunk_size < mtd->oobsize)
+ geo->ecc_chunk_size *= 2; /* keep C >= O */
+
+ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
+
+ /* We use the same ECC strength for all chunks. */
+ geo->ecc_strength = get_ecc_strength(this);
+ if (!geo->ecc_strength) {
+ pr_err("We get a wrong ECC strength.\n");
+ return -EINVAL;
+ }
+
+ geo->page_size = mtd->writesize + mtd->oobsize;
+ geo->payload_size = mtd->writesize;
+
+ /*
+ * The auxiliary buffer contains the metadata and the ECC status. The
+ * metadata is padded to the nearest 32-bit boundary. The ECC status
+ * contains one byte for every ECC chunk, and is also padded to the
+ * nearest 32-bit boundary.
+ */
+ metadata_size = ALIGN(geo->metadata_size, 4);
+ status_size = ALIGN(geo->ecc_chunk_count, 4);
+
+ geo->auxiliary_size = metadata_size + status_size;
+ geo->auxiliary_status_offset = metadata_size;
+
+ if (!this->swap_block_mark)
+ return 0;
+
+ /*
+ * We need to compute the byte and bit offsets of
+ * the physical block mark within the ECC-based view of the page.
+ *
+ * NAND chip with 2K page shows below:
+ * (Block Mark)
+ * | |
+ * | D |
+ * |<---->|
+ * V V
+ * +---+----------+-+----------+-+----------+-+----------+-+
+ * | M | data |E| data |E| data |E| data |E|
+ * +---+----------+-+----------+-+----------+-+----------+-+
+ *
+ * The position of block mark moves forward in the ECC-based view
+ * of page, and the delta is:
+ *
+ * E * G * (N - 1)
+ * D = (---------------- + M)
+ * 8
+ *
+ * With the formula to compute the ECC strength, and the condition
+ * : C >= O (C is the ecc chunk size)
+ *
+ * It's easy to deduce to the following result:
+ *
+ * E * G (O - M) C - M C - M
+ * ----------- <= ------- <= -------- < ---------
+ * 8 N N (N - 1)
+ *
+ * So, we get:
+ *
+ * E * G * (N - 1)
+ * D = (---------------- + M) < C
+ * 8
+ *
+ * The above inequality means the position of block mark
+ * within the ECC-based view of the page is still in the data chunk,
+ * and it's NOT in the ECC bits of the chunk.
+ *
+ * Use the following to compute the bit position of the
+ * physical block mark within the ECC-based view of the page:
+ * (page_size - D) * 8
+ *
+ * --Huang Shijie
+ */
+ block_mark_bit_offset = mtd->writesize * 8 -
+ (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
+ + geo->metadata_size * 8);
+
+ geo->block_mark_byte_offset = block_mark_bit_offset / 8;
+ geo->block_mark_bit_offset = block_mark_bit_offset % 8;
+ return 0;
+}
+
+struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
+{
+ int chipnr = this->current_chip;
+
+ return this->dma_chans[chipnr];
+}
+
+/* Can we use the upper's buffer directly for DMA? */
+void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr)
+{
+ struct scatterlist *sgl = &this->data_sgl;
+ int ret;
+
+ this->direct_dma_map_ok = true;
+
+ /* first try to map the upper buffer directly */
+ sg_init_one(sgl, this->upper_buf, this->upper_len);
+ ret = dma_map_sg(this->dev, sgl, 1, dr);
+ if (ret == 0) {
+ /* We have to use our own DMA buffer. */
+ sg_init_one(sgl, this->data_buffer_dma, PAGE_SIZE);
+
+ if (dr == DMA_TO_DEVICE)
+ memcpy(this->data_buffer_dma, this->upper_buf,
+ this->upper_len);
+
+ ret = dma_map_sg(this->dev, sgl, 1, dr);
+ if (ret == 0)
+ pr_err("map failed.\n");
+
+ this->direct_dma_map_ok = false;
+ }
+}
+
+/* This will be called after the DMA operation is finished. */
+static void dma_irq_callback(void *param)
+{
+ struct gpmi_nand_data *this = param;
+ struct completion *dma_c = &this->dma_done;
+
+ complete(dma_c);
+
+ switch (this->dma_type) {
+ case DMA_FOR_COMMAND:
+ dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE);
+ break;
+
+ case DMA_FOR_READ_DATA:
+ dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE);
+ if (this->direct_dma_map_ok == false)
+ memcpy(this->upper_buf, this->data_buffer_dma,
+ this->upper_len);
+ break;
+
+ case DMA_FOR_WRITE_DATA:
+ dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE);
+ break;
+
+ case DMA_FOR_READ_ECC_PAGE:
+ case DMA_FOR_WRITE_ECC_PAGE:
+ /* We have to wait the BCH interrupt to finish. */
+ break;
+
+ default:
+ pr_err("in wrong DMA operation.\n");
+ }
+}
+
+int start_dma_without_bch_irq(struct gpmi_nand_data *this,
+ struct dma_async_tx_descriptor *desc)
+{
+ struct completion *dma_c = &this->dma_done;
+ int err;
+
+ init_completion(dma_c);
+
+ desc->callback = dma_irq_callback;
+ desc->callback_param = this;
+ dmaengine_submit(desc);
+
+ /* Wait for the interrupt from the DMA block. */
+ err = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000));
+ if (!err) {
+ pr_err("DMA timeout, last DMA :%d\n", this->last_dma_type);
+ gpmi_dump_info(this);
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+/*
+ * This function is used in BCH reading or BCH writing pages.
+ * It will wait for the BCH interrupt as long as ONE second.
+ * Actually, we must wait for two interrupts :
+ * [1] firstly the DMA interrupt and
+ * [2] secondly the BCH interrupt.
+ */
+int start_dma_with_bch_irq(struct gpmi_nand_data *this,
+ struct dma_async_tx_descriptor *desc)
+{
+ struct completion *bch_c = &this->bch_done;
+ int err;
+
+ /* Prepare to receive an interrupt from the BCH block. */
+ init_completion(bch_c);
+
+ /* start the DMA */
+ start_dma_without_bch_irq(this, desc);
+
+ /* Wait for the interrupt from the BCH block. */
+ err = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000));
+ if (!err) {
+ pr_err("BCH timeout, last DMA :%d\n", this->last_dma_type);
+ gpmi_dump_info(this);
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static int __devinit
+acquire_register_block(struct gpmi_nand_data *this, const char *res_name)
+{
+ struct platform_device *pdev = this->pdev;
+ struct resources *res = &this->resources;
+ struct resource *r;
+ void *p;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
+ if (!r) {
+ pr_err("Can't get resource for %s\n", res_name);
+ return -ENXIO;
+ }
+
+ p = ioremap(r->start, resource_size(r));
+ if (!p) {
+ pr_err("Can't remap %s\n", res_name);
+ return -ENOMEM;
+ }
+
+ if (!strcmp(res_name, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME))
+ res->gpmi_regs = p;
+ else if (!strcmp(res_name, GPMI_NAND_BCH_REGS_ADDR_RES_NAME))
+ res->bch_regs = p;
+ else
+ pr_err("unknown resource name : %s\n", res_name);
+
+ return 0;
+}
+
+static void release_register_block(struct gpmi_nand_data *this)
+{
+ struct resources *res = &this->resources;
+ if (res->gpmi_regs)
+ iounmap(res->gpmi_regs);
+ if (res->bch_regs)
+ iounmap(res->bch_regs);
+ res->gpmi_regs = NULL;
+ res->bch_regs = NULL;
+}
+
+static int __devinit
+acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h)
+{
+ struct platform_device *pdev = this->pdev;
+ struct resources *res = &this->resources;
+ const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME;
+ struct resource *r;
+ int err;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
+ if (!r) {
+ pr_err("Can't get resource for %s\n", res_name);
+ return -ENXIO;
+ }
+
+ err = request_irq(r->start, irq_h, 0, res_name, this);
+ if (err) {
+ pr_err("Can't own %s\n", res_name);
+ return err;
+ }
+
+ res->bch_low_interrupt = r->start;
+ res->bch_high_interrupt = r->end;
+ return 0;
+}
+
+static void release_bch_irq(struct gpmi_nand_data *this)
+{
+ struct resources *res = &this->resources;
+ int i = res->bch_low_interrupt;
+
+ for (; i <= res->bch_high_interrupt; i++)
+ free_irq(i, this);
+}
+
+static bool gpmi_dma_filter(struct dma_chan *chan, void *param)
+{
+ struct gpmi_nand_data *this = param;
+ struct resource *r = this->private;
+
+ if (!mxs_dma_is_apbh(chan))
+ return false;
+ /*
+ * only catch the GPMI dma channels :
+ * for mx23 : MX23_DMA_GPMI0 ~ MX23_DMA_GPMI3
+ * (These four channels share the same IRQ!)
+ *
+ * for mx28 : MX28_DMA_GPMI0 ~ MX28_DMA_GPMI7
+ * (These eight channels share the same IRQ!)
+ */
+ if (r->start <= chan->chan_id && chan->chan_id <= r->end) {
+ chan->private = &this->dma_data;
+ return true;
+ }
+ return false;
+}
+
+static void release_dma_channels(struct gpmi_nand_data *this)
+{
+ unsigned int i;
+ for (i = 0; i < DMA_CHANS; i++)
+ if (this->dma_chans[i]) {
+ dma_release_channel(this->dma_chans[i]);
+ this->dma_chans[i] = NULL;
+ }
+}
+
+static int __devinit acquire_dma_channels(struct gpmi_nand_data *this)
+{
+ struct platform_device *pdev = this->pdev;
+ struct gpmi_nand_platform_data *pdata = this->pdata;
+ struct resources *res = &this->resources;
+ struct resource *r, *r_dma;
+ unsigned int i;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+ GPMI_NAND_DMA_CHANNELS_RES_NAME);
+ r_dma = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ GPMI_NAND_DMA_INTERRUPT_RES_NAME);
+ if (!r || !r_dma) {
+ pr_err("Can't get resource for DMA\n");
+ return -ENXIO;
+ }
+
+ /* used in gpmi_dma_filter() */
+ this->private = r;
+
+ for (i = r->start; i <= r->end; i++) {
+ struct dma_chan *dma_chan;
+ dma_cap_mask_t mask;
+
+ if (i - r->start >= pdata->max_chip_count)
+ break;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ /* get the DMA interrupt */
+ if (r_dma->start == r_dma->end) {
+ /* only register the first. */
+ if (i == r->start)
+ this->dma_data.chan_irq = r_dma->start;
+ else
+ this->dma_data.chan_irq = NO_IRQ;
+ } else
+ this->dma_data.chan_irq = r_dma->start + (i - r->start);
+
+ dma_chan = dma_request_channel(mask, gpmi_dma_filter, this);
+ if (!dma_chan)
+ goto acquire_err;
+
+ /* fill the first empty item */
+ this->dma_chans[i - r->start] = dma_chan;
+ }
+
+ res->dma_low_channel = r->start;
+ res->dma_high_channel = i;
+ return 0;
+
+acquire_err:
+ pr_err("Can't acquire DMA channel %u\n", i);
+ release_dma_channels(this);
+ return -EINVAL;
+}
+
+static int __devinit acquire_resources(struct gpmi_nand_data *this)
+{
+ struct resources *res = &this->resources;
+ int ret;
+
+ ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME);
+ if (ret)
+ goto exit_regs;
+
+ ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME);
+ if (ret)
+ goto exit_regs;
+
+ ret = acquire_bch_irq(this, bch_irq);
+ if (ret)
+ goto exit_regs;
+
+ ret = acquire_dma_channels(this);
+ if (ret)
+ goto exit_dma_channels;
+
+ res->clock = clk_get(&this->pdev->dev, NULL);
+ if (IS_ERR(res->clock)) {
+ pr_err("can not get the clock\n");
+ ret = -ENOENT;
+ goto exit_clock;
+ }
+ return 0;
+
+exit_clock:
+ release_dma_channels(this);
+exit_dma_channels:
+ release_bch_irq(this);
+exit_regs:
+ release_register_block(this);
+ return ret;
+}
+
+static void release_resources(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+
+ clk_put(r->clock);
+ release_register_block(this);
+ release_bch_irq(this);
+ release_dma_channels(this);
+}
+
+static int __devinit init_hardware(struct gpmi_nand_data *this)
+{
+ int ret;
+
+ /*
+ * This structure contains the "safe" GPMI timing that should succeed
+ * with any NAND Flash device
+ * (although, with less-than-optimal performance).
+ */
+ struct nand_timing safe_timing = {
+ .data_setup_in_ns = 80,
+ .data_hold_in_ns = 60,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ };
+
+ /* Initialize the hardwares. */
+ ret = gpmi_init(this);
+ if (ret)
+ return ret;
+
+ this->timing = safe_timing;
+ return 0;
+}
+
+static int read_page_prepare(struct gpmi_nand_data *this,
+ void *destination, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ void **use_virt, dma_addr_t *use_phys)
+{
+ struct device *dev = this->dev;
+
+ if (virt_addr_valid(destination)) {
+ dma_addr_t dest_phys;
+
+ dest_phys = dma_map_single(dev, destination,
+ length, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, dest_phys)) {
+ if (alt_size < length) {
+ pr_err("Alternate buffer is too small\n");
+ return -ENOMEM;
+ }
+ goto map_failed;
+ }
+ *use_virt = destination;
+ *use_phys = dest_phys;
+ this->direct_dma_map_ok = true;
+ return 0;
+ }
+
+map_failed:
+ *use_virt = alt_virt;
+ *use_phys = alt_phys;
+ this->direct_dma_map_ok = false;
+ return 0;
+}
+
+static inline void read_page_end(struct gpmi_nand_data *this,
+ void *destination, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ void *used_virt, dma_addr_t used_phys)
+{
+ if (this->direct_dma_map_ok)
+ dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE);
+}
+
+static inline void read_page_swap_end(struct gpmi_nand_data *this,
+ void *destination, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ void *used_virt, dma_addr_t used_phys)
+{
+ if (!this->direct_dma_map_ok)
+ memcpy(destination, alt_virt, length);
+}
+
+static int send_page_prepare(struct gpmi_nand_data *this,
+ const void *source, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ const void **use_virt, dma_addr_t *use_phys)
+{
+ struct device *dev = this->dev;
+
+ if (virt_addr_valid(source)) {
+ dma_addr_t source_phys;
+
+ source_phys = dma_map_single(dev, (void *)source, length,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, source_phys)) {
+ if (alt_size < length) {
+ pr_err("Alternate buffer is too small\n");
+ return -ENOMEM;
+ }
+ goto map_failed;
+ }
+ *use_virt = source;
+ *use_phys = source_phys;
+ return 0;
+ }
+map_failed:
+ /*
+ * Copy the content of the source buffer into the alternate
+ * buffer and set up the return values accordingly.
+ */
+ memcpy(alt_virt, source, length);
+
+ *use_virt = alt_virt;
+ *use_phys = alt_phys;
+ return 0;
+}
+
+static void send_page_end(struct gpmi_nand_data *this,
+ const void *source, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ const void *used_virt, dma_addr_t used_phys)
+{
+ struct device *dev = this->dev;
+ if (used_virt == source)
+ dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE);
+}
+
+static void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
+{
+ struct device *dev = this->dev;
+
+ if (this->page_buffer_virt && virt_addr_valid(this->page_buffer_virt))
+ dma_free_coherent(dev, this->page_buffer_size,
+ this->page_buffer_virt,
+ this->page_buffer_phys);
+ kfree(this->cmd_buffer);
+ kfree(this->data_buffer_dma);
+
+ this->cmd_buffer = NULL;
+ this->data_buffer_dma = NULL;
+ this->page_buffer_virt = NULL;
+ this->page_buffer_size = 0;
+}
+
+/* Allocate the DMA buffers */
+static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ struct device *dev = this->dev;
+
+ /* [1] Allocate a command buffer. PAGE_SIZE is enough. */
+ this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA);
+ if (this->cmd_buffer == NULL)
+ goto error_alloc;
+
+ /* [2] Allocate a read/write data buffer. PAGE_SIZE is enough. */
+ this->data_buffer_dma = kzalloc(PAGE_SIZE, GFP_DMA);
+ if (this->data_buffer_dma == NULL)
+ goto error_alloc;
+
+ /*
+ * [3] Allocate the page buffer.
+ *
+ * Both the payload buffer and the auxiliary buffer must appear on
+ * 32-bit boundaries. We presume the size of the payload buffer is a
+ * power of two and is much larger than four, which guarantees the
+ * auxiliary buffer will appear on a 32-bit boundary.
+ */
+ this->page_buffer_size = geo->payload_size + geo->auxiliary_size;
+ this->page_buffer_virt = dma_alloc_coherent(dev, this->page_buffer_size,
+ &this->page_buffer_phys, GFP_DMA);
+ if (!this->page_buffer_virt)
+ goto error_alloc;
+
+
+ /* Slice up the page buffer. */
+ this->payload_virt = this->page_buffer_virt;
+ this->payload_phys = this->page_buffer_phys;
+ this->auxiliary_virt = this->payload_virt + geo->payload_size;
+ this->auxiliary_phys = this->payload_phys + geo->payload_size;
+ return 0;
+
+error_alloc:
+ gpmi_free_dma_buffer(this);
+ pr_err("allocate DMA buffer ret!!\n");
+ return -ENOMEM;
+}
+
+static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *this = chip->priv;
+ int ret;
+
+ /*
+ * Every operation begins with a command byte and a series of zero or
+ * more address bytes. These are distinguished by either the Address
+ * Latch Enable (ALE) or Command Latch Enable (CLE) signals being
+ * asserted. When MTD is ready to execute the command, it will deassert
+ * both latch enables.
+ *
+ * Rather than run a separate DMA operation for every single byte, we
+ * queue them up and run a single DMA operation for the entire series
+ * of command and data bytes. NAND_CMD_NONE means the END of the queue.
+ */
+ if ((ctrl & (NAND_ALE | NAND_CLE))) {
+ if (data != NAND_CMD_NONE)
+ this->cmd_buffer[this->command_length++] = data;
+ return;
+ }
+
+ if (!this->command_length)
+ return;
+
+ ret = gpmi_send_command(this);
+ if (ret)
+ pr_err("Chip: %u, Error %d\n", this->current_chip, ret);
+
+ this->command_length = 0;
+}
+
+static int gpmi_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *this = chip->priv;
+
+ return gpmi_is_ready(this, this->current_chip);
+}
+
+static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *this = chip->priv;
+
+ if ((this->current_chip < 0) && (chipnr >= 0))
+ gpmi_begin(this);
+ else if ((this->current_chip >= 0) && (chipnr < 0))
+ gpmi_end(this);
+
+ this->current_chip = chipnr;
+}
+
+static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *this = chip->priv;
+
+ pr_debug("len is %d\n", len);
+ this->upper_buf = buf;
+ this->upper_len = len;
+
+ gpmi_read_data(this);
+}
+
+static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *this = chip->priv;
+
+ pr_debug("len is %d\n", len);
+ this->upper_buf = (uint8_t *)buf;
+ this->upper_len = len;
+
+ gpmi_send_data(this);
+}
+
+static uint8_t gpmi_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *this = chip->priv;
+ uint8_t *buf = this->data_buffer_dma;
+
+ gpmi_read_buf(mtd, buf, 1);
+ return buf[0];
+}
+
+/*
+ * Handles block mark swapping.
+ * It can be called in swapping the block mark, or swapping it back,
+ * because the the operations are the same.
+ */
+static void block_mark_swapping(struct gpmi_nand_data *this,
+ void *payload, void *auxiliary)
+{
+ struct bch_geometry *nfc_geo = &this->bch_geometry;
+ unsigned char *p;
+ unsigned char *a;
+ unsigned int bit;
+ unsigned char mask;
+ unsigned char from_data;
+ unsigned char from_oob;
+
+ if (!this->swap_block_mark)
+ return;
+
+ /*
+ * If control arrives here, we're swapping. Make some convenience
+ * variables.
+ */
+ bit = nfc_geo->block_mark_bit_offset;
+ p = payload + nfc_geo->block_mark_byte_offset;
+ a = auxiliary;
+
+ /*
+ * Get the byte from the data area that overlays the block mark. Since
+ * the ECC engine applies its own view to the bits in the page, the
+ * physical block mark won't (in general) appear on a byte boundary in
+ * the data.
+ */
+ from_data = (p[0] >> bit) | (p[1] << (8 - bit));
+
+ /* Get the byte from the OOB. */
+ from_oob = a[0];
+
+ /* Swap them. */
+ a[0] = from_data;
+
+ mask = (0x1 << bit) - 1;
+ p[0] = (p[0] & mask) | (from_oob << bit);
+
+ mask = ~0 << bit;
+ p[1] = (p[1] & mask) | (from_oob >> (8 - bit));
+}
+
+static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int page)
+{
+ struct gpmi_nand_data *this = chip->priv;
+ struct bch_geometry *nfc_geo = &this->bch_geometry;
+ void *payload_virt;
+ dma_addr_t payload_phys;
+ void *auxiliary_virt;
+ dma_addr_t auxiliary_phys;
+ unsigned int i;
+ unsigned char *status;
+ unsigned int failed;
+ unsigned int corrected;
+ int ret;
+
+ pr_debug("page number is : %d\n", page);
+ ret = read_page_prepare(this, buf, mtd->writesize,
+ this->payload_virt, this->payload_phys,
+ nfc_geo->payload_size,
+ &payload_virt, &payload_phys);
+ if (ret) {
+ pr_err("Inadequate DMA buffer\n");
+ ret = -ENOMEM;
+ return ret;
+ }
+ auxiliary_virt = this->auxiliary_virt;
+ auxiliary_phys = this->auxiliary_phys;
+
+ /* go! */
+ ret = gpmi_read_page(this, payload_phys, auxiliary_phys);
+ read_page_end(this, buf, mtd->writesize,
+ this->payload_virt, this->payload_phys,
+ nfc_geo->payload_size,
+ payload_virt, payload_phys);
+ if (ret) {
+ pr_err("Error in ECC-based read: %d\n", ret);
+ goto exit_nfc;
+ }
+
+ /* handle the block mark swapping */
+ block_mark_swapping(this, payload_virt, auxiliary_virt);
+
+ /* Loop over status bytes, accumulating ECC status. */
+ failed = 0;
+ corrected = 0;
+ status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
+
+ for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
+ if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
+ continue;
+
+ if (*status == STATUS_UNCORRECTABLE) {
+ failed++;
+ continue;
+ }
+ corrected += *status;
+ }
+
+ /*
+ * Propagate ECC status to the owning MTD only when failed or
+ * corrected times nearly reaches our ECC correction threshold.
+ */
+ if (failed || corrected >= (nfc_geo->ecc_strength - 1)) {
+ mtd->ecc_stats.failed += failed;
+ mtd->ecc_stats.corrected += corrected;
+ }
+
+ /*
+ * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() for
+ * details about our policy for delivering the OOB.
+ *
+ * We fill the caller's buffer with set bits, and then copy the block
+ * mark to th caller's buffer. Note that, if block mark swapping was
+ * necessary, it has already been done, so we can rely on the first
+ * byte of the auxiliary buffer to contain the block mark.
+ */
+ memset(chip->oob_poi, ~0, mtd->oobsize);
+ chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
+
+ read_page_swap_end(this, buf, mtd->writesize,
+ this->payload_virt, this->payload_phys,
+ nfc_geo->payload_size,
+ payload_virt, payload_phys);
+exit_nfc:
+ return ret;
+}
+
+static void gpmi_ecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf)
+{
+ struct gpmi_nand_data *this = chip->priv;
+ struct bch_geometry *nfc_geo = &this->bch_geometry;
+ const void *payload_virt;
+ dma_addr_t payload_phys;
+ const void *auxiliary_virt;
+ dma_addr_t auxiliary_phys;
+ int ret;
+
+ pr_debug("ecc write page.\n");
+ if (this->swap_block_mark) {
+ /*
+ * If control arrives here, we're doing block mark swapping.
+ * Since we can't modify the caller's buffers, we must copy them
+ * into our own.
+ */
+ memcpy(this->payload_virt, buf, mtd->writesize);
+ payload_virt = this->payload_virt;
+ payload_phys = this->payload_phys;
+
+ memcpy(this->auxiliary_virt, chip->oob_poi,
+ nfc_geo->auxiliary_size);
+ auxiliary_virt = this->auxiliary_virt;
+ auxiliary_phys = this->auxiliary_phys;
+
+ /* Handle block mark swapping. */
+ block_mark_swapping(this,
+ (void *) payload_virt, (void *) auxiliary_virt);
+ } else {
+ /*
+ * If control arrives here, we're not doing block mark swapping,
+ * so we can to try and use the caller's buffers.
+ */
+ ret = send_page_prepare(this,
+ buf, mtd->writesize,
+ this->payload_virt, this->payload_phys,
+ nfc_geo->payload_size,
+ &payload_virt, &payload_phys);
+ if (ret) {
+ pr_err("Inadequate payload DMA buffer\n");
+ return;
+ }
+
+ ret = send_page_prepare(this,
+ chip->oob_poi, mtd->oobsize,
+ this->auxiliary_virt, this->auxiliary_phys,
+ nfc_geo->auxiliary_size,
+ &auxiliary_virt, &auxiliary_phys);
+ if (ret) {
+ pr_err("Inadequate auxiliary DMA buffer\n");
+ goto exit_auxiliary;
+ }
+ }
+
+ /* Ask the NFC. */
+ ret = gpmi_send_page(this, payload_phys, auxiliary_phys);
+ if (ret)
+ pr_err("Error in ECC-based write: %d\n", ret);
+
+ if (!this->swap_block_mark) {
+ send_page_end(this, chip->oob_poi, mtd->oobsize,
+ this->auxiliary_virt, this->auxiliary_phys,
+ nfc_geo->auxiliary_size,
+ auxiliary_virt, auxiliary_phys);
+exit_auxiliary:
+ send_page_end(this, buf, mtd->writesize,
+ this->payload_virt, this->payload_phys,
+ nfc_geo->payload_size,
+ payload_virt, payload_phys);
+ }
+}
+
+/*
+ * There are several places in this driver where we have to handle the OOB and
+ * block marks. This is the function where things are the most complicated, so
+ * this is where we try to explain it all. All the other places refer back to
+ * here.
+ *
+ * These are the rules, in order of decreasing importance:
+ *
+ * 1) Nothing the caller does can be allowed to imperil the block mark.
+ *
+ * 2) In read operations, the first byte of the OOB we return must reflect the
+ * true state of the block mark, no matter where that block mark appears in
+ * the physical page.
+ *
+ * 3) ECC-based read operations return an OOB full of set bits (since we never
+ * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads
+ * return).
+ *
+ * 4) "Raw" read operations return a direct view of the physical bytes in the
+ * page, using the conventional definition of which bytes are data and which
+ * are OOB. This gives the caller a way to see the actual, physical bytes
+ * in the page, without the distortions applied by our ECC engine.
+ *
+ *
+ * What we do for this specific read operation depends on two questions:
+ *
+ * 1) Are we doing a "raw" read, or an ECC-based read?
+ *
+ * 2) Are we using block mark swapping or transcription?
+ *
+ * There are four cases, illustrated by the following Karnaugh map:
+ *
+ * | Raw | ECC-based |
+ * -------------+-------------------------+-------------------------+
+ * | Read the conventional | |
+ * | OOB at the end of the | |
+ * Swapping | page and return it. It | |
+ * | contains exactly what | |
+ * | we want. | Read the block mark and |
+ * -------------+-------------------------+ return it in a buffer |
+ * | Read the conventional | full of set bits. |
+ * | OOB at the end of the | |
+ * | page and also the block | |
+ * Transcribing | mark in the metadata. | |
+ * | Copy the block mark | |
+ * | into the first byte of | |
+ * | the OOB. | |
+ * -------------+-------------------------+-------------------------+
+ *
+ * Note that we break rule #4 in the Transcribing/Raw case because we're not
+ * giving an accurate view of the actual, physical bytes in the page (we're
+ * overwriting the block mark). That's OK because it's more important to follow
+ * rule #2.
+ *
+ * It turns out that knowing whether we want an "ECC-based" or "raw" read is not
+ * easy. When reading a page, for example, the NAND Flash MTD code calls our
+ * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an
+ * ECC-based or raw view of the page is implicit in which function it calls
+ * (there is a similar pair of ECC-based/raw functions for writing).
+ *
+ * Since MTD assumes the OOB is not covered by ECC, there is no pair of
+ * ECC-based/raw functions for reading or or writing the OOB. The fact that the
+ * caller wants an ECC-based or raw view of the page is not propagated down to
+ * this driver.
+ */
+static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int sndcmd)
+{
+ struct gpmi_nand_data *this = chip->priv;
+
+ pr_debug("page number is %d\n", page);
+ /* clear the OOB buffer */
+ memset(chip->oob_poi, ~0, mtd->oobsize);
+
+ /* Read out the conventional OOB. */
+ chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ /*
+ * Now, we want to make sure the block mark is correct. In the
+ * Swapping/Raw case, we already have it. Otherwise, we need to
+ * explicitly read it.
+ */
+ if (!this->swap_block_mark) {
+ /* Read the block mark into the first byte of the OOB buffer. */
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ chip->oob_poi[0] = chip->read_byte(mtd);
+ }
+
+ /*
+ * Return true, indicating that the next call to this function must send
+ * a command.
+ */
+ return true;
+}
+
+static int
+gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+ /*
+ * The BCH will use all the (page + oob).
+ * Our gpmi_hw_ecclayout can only prohibit the JFFS2 to write the oob.
+ * But it can not stop some ioctls such MEMWRITEOOB which uses
+ * MTD_OPS_PLACE_OOB. So We have to implement this function to prohibit
+ * these ioctls too.
+ */
+ return -EPERM;
+}
+
+static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *this = chip->priv;
+ int block, ret = 0;
+ uint8_t *block_mark;
+ int column, page, status, chipnr;
+
+ /* Get block number */
+ block = (int)(ofs >> chip->bbt_erase_shift);
+ if (chip->bbt)
+ chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+ /* Do we have a flash based bad block table ? */
+ if (chip->options & NAND_BBT_USE_FLASH)
+ ret = nand_update_bbt(mtd, ofs);
+ else {
+ chipnr = (int)(ofs >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ column = this->swap_block_mark ? mtd->writesize : 0;
+
+ /* Write the block mark. */
+ block_mark = this->data_buffer_dma;
+ block_mark[0] = 0; /* bad block marker */
+
+ /* Shift to get page */
+ page = (int)(ofs >> chip->page_shift);
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page);
+ chip->write_buf(mtd, block_mark, 1);
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ ret = -EIO;
+
+ chip->select_chip(mtd, -1);
+ }
+ if (!ret)
+ mtd->ecc_stats.badblocks++;
+
+ return ret;
+}
+
+static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this)
+{
+ struct boot_rom_geometry *geometry = &this->rom_geometry;
+
+ /*
+ * Set the boot block stride size.
+ *
+ * In principle, we should be reading this from the OTP bits, since
+ * that's where the ROM is going to get it. In fact, we don't have any
+ * way to read the OTP bits, so we go with the default and hope for the
+ * best.
+ */
+ geometry->stride_size_in_pages = 64;
+
+ /*
+ * Set the search area stride exponent.
+ *
+ * In principle, we should be reading this from the OTP bits, since
+ * that's where the ROM is going to get it. In fact, we don't have any
+ * way to read the OTP bits, so we go with the default and hope for the
+ * best.
+ */
+ geometry->search_area_stride_exponent = 2;
+ return 0;
+}
+
+static const char *fingerprint = "STMP";
+static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this)
+{
+ struct boot_rom_geometry *rom_geo = &this->rom_geometry;
+ struct device *dev = this->dev;
+ struct mtd_info *mtd = &this->mtd;
+ struct nand_chip *chip = &this->nand;
+ unsigned int search_area_size_in_strides;
+ unsigned int stride;
+ unsigned int page;
+ loff_t byte;
+ uint8_t *buffer = chip->buffers->databuf;
+ int saved_chip_number;
+ int found_an_ncb_fingerprint = false;
+
+ /* Compute the number of strides in a search area. */
+ search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
+
+ saved_chip_number = this->current_chip;
+ chip->select_chip(mtd, 0);
+
+ /*
+ * Loop through the first search area, looking for the NCB fingerprint.
+ */
+ dev_dbg(dev, "Scanning for an NCB fingerprint...\n");
+
+ for (stride = 0; stride < search_area_size_in_strides; stride++) {
+ /* Compute the page and byte addresses. */
+ page = stride * rom_geo->stride_size_in_pages;
+ byte = page * mtd->writesize;
+
+ dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page);
+
+ /*
+ * Read the NCB fingerprint. The fingerprint is four bytes long
+ * and starts in the 12th byte of the page.
+ */
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 12, page);
+ chip->read_buf(mtd, buffer, strlen(fingerprint));
+
+ /* Look for the fingerprint. */
+ if (!memcmp(buffer, fingerprint, strlen(fingerprint))) {
+ found_an_ncb_fingerprint = true;
+ break;
+ }
+
+ }
+
+ chip->select_chip(mtd, saved_chip_number);
+
+ if (found_an_ncb_fingerprint)
+ dev_dbg(dev, "\tFound a fingerprint\n");
+ else
+ dev_dbg(dev, "\tNo fingerprint found\n");
+ return found_an_ncb_fingerprint;
+}
+
+/* Writes a transcription stamp. */
+static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this)
+{
+ struct device *dev = this->dev;
+ struct boot_rom_geometry *rom_geo = &this->rom_geometry;
+ struct mtd_info *mtd = &this->mtd;
+ struct nand_chip *chip = &this->nand;
+ unsigned int block_size_in_pages;
+ unsigned int search_area_size_in_strides;
+ unsigned int search_area_size_in_pages;
+ unsigned int search_area_size_in_blocks;
+ unsigned int block;
+ unsigned int stride;
+ unsigned int page;
+ loff_t byte;
+ uint8_t *buffer = chip->buffers->databuf;
+ int saved_chip_number;
+ int status;
+
+ /* Compute the search area geometry. */
+ block_size_in_pages = mtd->erasesize / mtd->writesize;
+ search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
+ search_area_size_in_pages = search_area_size_in_strides *
+ rom_geo->stride_size_in_pages;
+ search_area_size_in_blocks =
+ (search_area_size_in_pages + (block_size_in_pages - 1)) /
+ block_size_in_pages;
+
+ dev_dbg(dev, "Search Area Geometry :\n");
+ dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks);
+ dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides);
+ dev_dbg(dev, "\tin Pages : %u\n", search_area_size_in_pages);
+
+ /* Select chip 0. */
+ saved_chip_number = this->current_chip;
+ chip->select_chip(mtd, 0);
+
+ /* Loop over blocks in the first search area, erasing them. */
+ dev_dbg(dev, "Erasing the search area...\n");
+
+ for (block = 0; block < search_area_size_in_blocks; block++) {
+ /* Compute the page address. */
+ page = block * block_size_in_pages;
+
+ /* Erase this block. */
+ dev_dbg(dev, "\tErasing block 0x%x\n", block);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+
+ /* Wait for the erase to finish. */
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ dev_err(dev, "[%s] Erase failed.\n", __func__);
+ }
+
+ /* Write the NCB fingerprint into the page buffer. */
+ memset(buffer, ~0, mtd->writesize);
+ memset(chip->oob_poi, ~0, mtd->oobsize);
+ memcpy(buffer + 12, fingerprint, strlen(fingerprint));
+
+ /* Loop through the first search area, writing NCB fingerprints. */
+ dev_dbg(dev, "Writing NCB fingerprints...\n");
+ for (stride = 0; stride < search_area_size_in_strides; stride++) {
+ /* Compute the page and byte addresses. */
+ page = stride * rom_geo->stride_size_in_pages;
+ byte = page * mtd->writesize;
+
+ /* Write the first page of the current stride. */
+ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+ chip->ecc.write_page_raw(mtd, chip, buffer);
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ /* Wait for the write to finish. */
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ dev_err(dev, "[%s] Write failed.\n", __func__);
+ }
+
+ /* Deselect chip 0. */
+ chip->select_chip(mtd, saved_chip_number);
+ return 0;
+}
+
+static int __devinit mx23_boot_init(struct gpmi_nand_data *this)
+{
+ struct device *dev = this->dev;
+ struct nand_chip *chip = &this->nand;
+ struct mtd_info *mtd = &this->mtd;
+ unsigned int block_count;
+ unsigned int block;
+ int chipnr;
+ int page;
+ loff_t byte;
+ uint8_t block_mark;
+ int ret = 0;
+
+ /*
+ * If control arrives here, we can't use block mark swapping, which
+ * means we're forced to use transcription. First, scan for the
+ * transcription stamp. If we find it, then we don't have to do
+ * anything -- the block marks are already transcribed.
+ */
+ if (mx23_check_transcription_stamp(this))
+ return 0;
+
+ /*
+ * If control arrives here, we couldn't find a transcription stamp, so
+ * so we presume the block marks are in the conventional location.
+ */
+ dev_dbg(dev, "Transcribing bad block marks...\n");
+
+ /* Compute the number of blocks in the entire medium. */
+ block_count = chip->chipsize >> chip->phys_erase_shift;
+
+ /*
+ * Loop over all the blocks in the medium, transcribing block marks as
+ * we go.
+ */
+ for (block = 0; block < block_count; block++) {
+ /*
+ * Compute the chip, page and byte addresses for this block's
+ * conventional mark.
+ */
+ chipnr = block >> (chip->chip_shift - chip->phys_erase_shift);
+ page = block << (chip->phys_erase_shift - chip->page_shift);
+ byte = block << chip->phys_erase_shift;
+
+ /* Send the command to read the conventional block mark. */
+ chip->select_chip(mtd, chipnr);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+ block_mark = chip->read_byte(mtd);
+ chip->select_chip(mtd, -1);
+
+ /*
+ * Check if the block is marked bad. If so, we need to mark it
+ * again, but this time the result will be a mark in the
+ * location where we transcribe block marks.
+ */
+ if (block_mark != 0xff) {
+ dev_dbg(dev, "Transcribing mark in block %u\n", block);
+ ret = chip->block_markbad(mtd, byte);
+ if (ret)
+ dev_err(dev, "Failed to mark block bad with "
+ "ret %d\n", ret);
+ }
+ }
+
+ /* Write the stamp that indicates we've transcribed the block marks. */
+ mx23_write_transcription_stamp(this);
+ return 0;
+}
+
+static int __devinit nand_boot_init(struct gpmi_nand_data *this)
+{
+ nand_boot_set_geometry(this);
+
+ /* This is ROM arch-specific initilization before the BBT scanning. */
+ if (GPMI_IS_MX23(this))
+ return mx23_boot_init(this);
+ return 0;
+}
+
+static int __devinit gpmi_set_geometry(struct gpmi_nand_data *this)
+{
+ int ret;
+
+ /* Free the temporary DMA memory for reading ID. */
+ gpmi_free_dma_buffer(this);
+
+ /* Set up the NFC geometry which is used by BCH. */
+ ret = bch_set_geometry(this);
+ if (ret) {
+ pr_err("set geometry ret : %d\n", ret);
+ return ret;
+ }
+
+ /* Alloc the new DMA buffers according to the pagesize and oobsize */
+ return gpmi_alloc_dma_buffer(this);
+}
+
+static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this)
+{
+ int ret;
+
+ /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
+ if (GPMI_IS_MX23(this))
+ this->swap_block_mark = false;
+ else
+ this->swap_block_mark = true;
+
+ /* Set up the medium geometry */
+ ret = gpmi_set_geometry(this);
+ if (ret)
+ return ret;
+
+ /* NAND boot init, depends on the gpmi_set_geometry(). */
+ return nand_boot_init(this);
+}
+
+static int gpmi_scan_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *this = chip->priv;
+ int ret;
+
+ /* Prepare for the BBT scan. */
+ ret = gpmi_pre_bbt_scan(this);
+ if (ret)
+ return ret;
+
+ /* use the default BBT implementation */
+ return nand_default_bbt(mtd);
+}
+
+void gpmi_nfc_exit(struct gpmi_nand_data *this)
+{
+ nand_release(&this->mtd);
+ gpmi_free_dma_buffer(this);
+}
+
+static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this)
+{
+ struct gpmi_nand_platform_data *pdata = this->pdata;
+ struct mtd_info *mtd = &this->mtd;
+ struct nand_chip *chip = &this->nand;
+ int ret;
+
+ /* init current chip */
+ this->current_chip = -1;
+
+ /* init the MTD data structures */
+ mtd->priv = chip;
+ mtd->name = "gpmi-nand";
+ mtd->owner = THIS_MODULE;
+
+ /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
+ chip->priv = this;
+ chip->select_chip = gpmi_select_chip;
+ chip->cmd_ctrl = gpmi_cmd_ctrl;
+ chip->dev_ready = gpmi_dev_ready;
+ chip->read_byte = gpmi_read_byte;
+ chip->read_buf = gpmi_read_buf;
+ chip->write_buf = gpmi_write_buf;
+ chip->ecc.read_page = gpmi_ecc_read_page;
+ chip->ecc.write_page = gpmi_ecc_write_page;
+ chip->ecc.read_oob = gpmi_ecc_read_oob;
+ chip->ecc.write_oob = gpmi_ecc_write_oob;
+ chip->scan_bbt = gpmi_scan_bbt;
+ chip->badblock_pattern = &gpmi_bbt_descr;
+ chip->block_markbad = gpmi_block_markbad;
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+ chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.size = 1;
+ chip->ecc.layout = &gpmi_hw_ecclayout;
+
+ /* Allocate a temporary DMA buffer for reading ID in the nand_scan() */
+ this->bch_geometry.payload_size = 1024;
+ this->bch_geometry.auxiliary_size = 128;
+ ret = gpmi_alloc_dma_buffer(this);
+ if (ret)
+ goto err_out;
+
+ ret = nand_scan(mtd, pdata->max_chip_count);
+ if (ret) {
+ pr_err("Chip scan failed\n");
+ goto err_out;
+ }
+
+ ret = mtd_device_parse_register(mtd, NULL, NULL,
+ pdata->partitions, pdata->partition_count);
+ if (ret)
+ goto err_out;
+ return 0;
+
+err_out:
+ gpmi_nfc_exit(this);
+ return ret;
+}
+
+static int __devinit gpmi_nand_probe(struct platform_device *pdev)
+{
+ struct gpmi_nand_platform_data *pdata = pdev->dev.platform_data;
+ struct gpmi_nand_data *this;
+ int ret;
+
+ this = kzalloc(sizeof(*this), GFP_KERNEL);
+ if (!this) {
+ pr_err("Failed to allocate per-device memory\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, this);
+ this->pdev = pdev;
+ this->dev = &pdev->dev;
+ this->pdata = pdata;
+
+ if (pdata->platform_init) {
+ ret = pdata->platform_init();
+ if (ret)
+ goto platform_init_error;
+ }
+
+ ret = acquire_resources(this);
+ if (ret)
+ goto exit_acquire_resources;
+
+ ret = init_hardware(this);
+ if (ret)
+ goto exit_nfc_init;
+
+ ret = gpmi_nfc_init(this);
+ if (ret)
+ goto exit_nfc_init;
+
+ return 0;
+
+exit_nfc_init:
+ release_resources(this);
+platform_init_error:
+exit_acquire_resources:
+ platform_set_drvdata(pdev, NULL);
+ kfree(this);
+ return ret;
+}
+
+static int __exit gpmi_nand_remove(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *this = platform_get_drvdata(pdev);
+
+ gpmi_nfc_exit(this);
+ release_resources(this);
+ platform_set_drvdata(pdev, NULL);
+ kfree(this);
+ return 0;
+}
+
+static const struct platform_device_id gpmi_ids[] = {
+ {
+ .name = "imx23-gpmi-nand",
+ .driver_data = IS_MX23,
+ }, {
+ .name = "imx28-gpmi-nand",
+ .driver_data = IS_MX28,
+ }, {},
+};
+
+static struct platform_driver gpmi_nand_driver = {
+ .driver = {
+ .name = "gpmi-nand",
+ },
+ .probe = gpmi_nand_probe,
+ .remove = __exit_p(gpmi_nand_remove),
+ .id_table = gpmi_ids,
+};
+
+static int __init gpmi_nand_init(void)
+{
+ int err;
+
+ err = platform_driver_register(&gpmi_nand_driver);
+ if (err == 0)
+ printk(KERN_INFO "GPMI NAND driver registered. (IMX)\n");
+ else
+ pr_err("i.MX GPMI NAND driver registration failed\n");
+ return err;
+}
+
+static void __exit gpmi_nand_exit(void)
+{
+ platform_driver_unregister(&gpmi_nand_driver);
+}
+
+module_init(gpmi_nand_init);
+module_exit(gpmi_nand_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
new file mode 100644
index 000000000000..e023bccb7781
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
@@ -0,0 +1,273 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H
+#define __DRIVERS_MTD_NAND_GPMI_NAND_H
+
+#include <linux/mtd/nand.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <mach/dma.h>
+
+struct resources {
+ void *gpmi_regs;
+ void *bch_regs;
+ unsigned int bch_low_interrupt;
+ unsigned int bch_high_interrupt;
+ unsigned int dma_low_channel;
+ unsigned int dma_high_channel;
+ struct clk *clock;
+};
+
+/**
+ * struct bch_geometry - BCH geometry description.
+ * @gf_len: The length of Galois Field. (e.g., 13 or 14)
+ * @ecc_strength: A number that describes the strength of the ECC
+ * algorithm.
+ * @page_size: The size, in bytes, of a physical page, including
+ * both data and OOB.
+ * @metadata_size: The size, in bytes, of the metadata.
+ * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note
+ * the first chunk in the page includes both data and
+ * metadata, so it's a bit larger than this value.
+ * @ecc_chunk_count: The number of ECC chunks in the page,
+ * @payload_size: The size, in bytes, of the payload buffer.
+ * @auxiliary_size: The size, in bytes, of the auxiliary buffer.
+ * @auxiliary_status_offset: The offset into the auxiliary buffer at which
+ * the ECC status appears.
+ * @block_mark_byte_offset: The byte offset in the ECC-based page view at
+ * which the underlying physical block mark appears.
+ * @block_mark_bit_offset: The bit offset into the ECC-based page view at
+ * which the underlying physical block mark appears.
+ */
+struct bch_geometry {
+ unsigned int gf_len;
+ unsigned int ecc_strength;
+ unsigned int page_size;
+ unsigned int metadata_size;
+ unsigned int ecc_chunk_size;
+ unsigned int ecc_chunk_count;
+ unsigned int payload_size;
+ unsigned int auxiliary_size;
+ unsigned int auxiliary_status_offset;
+ unsigned int block_mark_byte_offset;
+ unsigned int block_mark_bit_offset;
+};
+
+/**
+ * struct boot_rom_geometry - Boot ROM geometry description.
+ * @stride_size_in_pages: The size of a boot block stride, in pages.
+ * @search_area_stride_exponent: The logarithm to base 2 of the size of a
+ * search area in boot block strides.
+ */
+struct boot_rom_geometry {
+ unsigned int stride_size_in_pages;
+ unsigned int search_area_stride_exponent;
+};
+
+/* DMA operations types */
+enum dma_ops_type {
+ DMA_FOR_COMMAND = 1,
+ DMA_FOR_READ_DATA,
+ DMA_FOR_WRITE_DATA,
+ DMA_FOR_READ_ECC_PAGE,
+ DMA_FOR_WRITE_ECC_PAGE
+};
+
+/**
+ * struct nand_timing - Fundamental timing attributes for NAND.
+ * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the
+ * maximum of tDS and tWP. A negative value
+ * indicates this characteristic isn't known.
+ * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the
+ * maximum of tDH, tWH and tREH. A negative value
+ * indicates this characteristic isn't known.
+ * @address_setup_in_ns: The address setup time, in nanoseconds. Usually
+ * the maximum of tCLS, tCS and tALS. A negative
+ * value indicates this characteristic isn't known.
+ * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value
+ * indicates this characteristic isn't known.
+ * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic isn't
+ * known.
+ * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic isn't
+ * known.
+ * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic isn't
+ * known.
+ */
+struct nand_timing {
+ int8_t data_setup_in_ns;
+ int8_t data_hold_in_ns;
+ int8_t address_setup_in_ns;
+ int8_t gpmi_sample_delay_in_ns;
+ int8_t tREA_in_ns;
+ int8_t tRLOH_in_ns;
+ int8_t tRHOH_in_ns;
+};
+
+struct gpmi_nand_data {
+ /* System Interface */
+ struct device *dev;
+ struct platform_device *pdev;
+ struct gpmi_nand_platform_data *pdata;
+
+ /* Resources */
+ struct resources resources;
+
+ /* Flash Hardware */
+ struct nand_timing timing;
+
+ /* BCH */
+ struct bch_geometry bch_geometry;
+ struct completion bch_done;
+
+ /* NAND Boot issue */
+ bool swap_block_mark;
+ struct boot_rom_geometry rom_geometry;
+
+ /* MTD / NAND */
+ struct nand_chip nand;
+ struct mtd_info mtd;
+
+ /* General-use Variables */
+ int current_chip;
+ unsigned int command_length;
+
+ /* passed from upper layer */
+ uint8_t *upper_buf;
+ int upper_len;
+
+ /* for DMA operations */
+ bool direct_dma_map_ok;
+
+ struct scatterlist cmd_sgl;
+ char *cmd_buffer;
+
+ struct scatterlist data_sgl;
+ char *data_buffer_dma;
+
+ void *page_buffer_virt;
+ dma_addr_t page_buffer_phys;
+ unsigned int page_buffer_size;
+
+ void *payload_virt;
+ dma_addr_t payload_phys;
+
+ void *auxiliary_virt;
+ dma_addr_t auxiliary_phys;
+
+ /* DMA channels */
+#define DMA_CHANS 8
+ struct dma_chan *dma_chans[DMA_CHANS];
+ struct mxs_dma_data dma_data;
+ enum dma_ops_type last_dma_type;
+ enum dma_ops_type dma_type;
+ struct completion dma_done;
+
+ /* private */
+ void *private;
+};
+
+/**
+ * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters.
+ * @data_setup_in_cycles: The data setup time, in cycles.
+ * @data_hold_in_cycles: The data hold time, in cycles.
+ * @address_setup_in_cycles: The address setup time, in cycles.
+ * @use_half_periods: Indicates the clock is running slowly, so the
+ * NFC DLL should use half-periods.
+ * @sample_delay_factor: The sample delay factor.
+ */
+struct gpmi_nfc_hardware_timing {
+ uint8_t data_setup_in_cycles;
+ uint8_t data_hold_in_cycles;
+ uint8_t address_setup_in_cycles;
+ bool use_half_periods;
+ uint8_t sample_delay_factor;
+};
+
+/**
+ * struct timing_threshod - Timing threshold
+ * @max_data_setup_cycles: The maximum number of data setup cycles that
+ * can be expressed in the hardware.
+ * @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires
+ * for data read internal setup. In the Reference
+ * Manual, see the chapter "High-Speed NAND
+ * Timing" for more details.
+ * @max_sample_delay_factor: The maximum sample delay factor that can be
+ * expressed in the hardware.
+ * @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the
+ * sample delay DLL hardware can possibly work
+ * with (the DLL is unusable with longer periods).
+ * If the full-cycle period is greater than HALF
+ * this value, the DLL must be configured to use
+ * half-periods.
+ * @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the
+ * DLL can implement.
+ * @clock_frequency_in_hz: The clock frequency, in Hz, during the current
+ * I/O transaction. If no I/O transaction is in
+ * progress, this is the clock frequency during
+ * the most recent I/O transaction.
+ */
+struct timing_threshod {
+ const unsigned int max_chip_count;
+ const unsigned int max_data_setup_cycles;
+ const unsigned int internal_data_setup_in_ns;
+ const unsigned int max_sample_delay_factor;
+ const unsigned int max_dll_clock_period_in_ns;
+ const unsigned int max_dll_delay_in_ns;
+ unsigned long clock_frequency_in_hz;
+
+};
+
+/* Common Services */
+extern int common_nfc_set_geometry(struct gpmi_nand_data *);
+extern struct dma_chan *get_dma_chan(struct gpmi_nand_data *);
+extern void prepare_data_dma(struct gpmi_nand_data *,
+ enum dma_data_direction dr);
+extern int start_dma_without_bch_irq(struct gpmi_nand_data *,
+ struct dma_async_tx_descriptor *);
+extern int start_dma_with_bch_irq(struct gpmi_nand_data *,
+ struct dma_async_tx_descriptor *);
+
+/* GPMI-NAND helper function library */
+extern int gpmi_init(struct gpmi_nand_data *);
+extern void gpmi_clear_bch(struct gpmi_nand_data *);
+extern void gpmi_dump_info(struct gpmi_nand_data *);
+extern int bch_set_geometry(struct gpmi_nand_data *);
+extern int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
+extern int gpmi_send_command(struct gpmi_nand_data *);
+extern void gpmi_begin(struct gpmi_nand_data *);
+extern void gpmi_end(struct gpmi_nand_data *);
+extern int gpmi_read_data(struct gpmi_nand_data *);
+extern int gpmi_send_data(struct gpmi_nand_data *);
+extern int gpmi_send_page(struct gpmi_nand_data *,
+ dma_addr_t payload, dma_addr_t auxiliary);
+extern int gpmi_read_page(struct gpmi_nand_data *,
+ dma_addr_t payload, dma_addr_t auxiliary);
+
+/* BCH : Status Block Completion Codes */
+#define STATUS_GOOD 0x00
+#define STATUS_ERASED 0xff
+#define STATUS_UNCORRECTABLE 0xfe
+
+/* Use the platform_id to distinguish different Archs. */
+#define IS_MX23 0x1
+#define IS_MX28 0x2
+#define GPMI_IS_MX23(x) ((x)->pdev->id_entry->driver_data == IS_MX23)
+#define GPMI_IS_MX28(x) ((x)->pdev->id_entry->driver_data == IS_MX28)
+#endif
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h
new file mode 100644
index 000000000000..83431240e2f2
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h
@@ -0,0 +1,172 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef __GPMI_NAND_GPMI_REGS_H
+#define __GPMI_NAND_GPMI_REGS_H
+
+#define HW_GPMI_CTRL0 0x00000000
+#define HW_GPMI_CTRL0_SET 0x00000004
+#define HW_GPMI_CTRL0_CLR 0x00000008
+#define HW_GPMI_CTRL0_TOG 0x0000000c
+
+#define BP_GPMI_CTRL0_COMMAND_MODE 24
+#define BM_GPMI_CTRL0_COMMAND_MODE (3 << BP_GPMI_CTRL0_COMMAND_MODE)
+#define BF_GPMI_CTRL0_COMMAND_MODE(v) \
+ (((v) << BP_GPMI_CTRL0_COMMAND_MODE) & BM_GPMI_CTRL0_COMMAND_MODE)
+#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2
+#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3
+
+#define BM_GPMI_CTRL0_WORD_LENGTH (1 << 23)
+#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0
+#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1
+
+/*
+ * Difference in LOCK_CS between imx23 and imx28 :
+ * This bit may impact the _POWER_ consumption. So some chips
+ * do not set it.
+ */
+#define MX23_BP_GPMI_CTRL0_LOCK_CS 22
+#define MX28_BP_GPMI_CTRL0_LOCK_CS 27
+#define LOCK_CS_ENABLE 0x1
+#define BF_GPMI_CTRL0_LOCK_CS(v, x) 0x0
+
+/* Difference in CS between imx23 and imx28 */
+#define BP_GPMI_CTRL0_CS 20
+#define MX23_BM_GPMI_CTRL0_CS (3 << BP_GPMI_CTRL0_CS)
+#define MX28_BM_GPMI_CTRL0_CS (7 << BP_GPMI_CTRL0_CS)
+#define BF_GPMI_CTRL0_CS(v, x) (((v) << BP_GPMI_CTRL0_CS) & \
+ (GPMI_IS_MX23((x)) \
+ ? MX23_BM_GPMI_CTRL0_CS \
+ : MX28_BM_GPMI_CTRL0_CS))
+
+#define BP_GPMI_CTRL0_ADDRESS 17
+#define BM_GPMI_CTRL0_ADDRESS (3 << BP_GPMI_CTRL0_ADDRESS)
+#define BF_GPMI_CTRL0_ADDRESS(v) \
+ (((v) << BP_GPMI_CTRL0_ADDRESS) & BM_GPMI_CTRL0_ADDRESS)
+#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0
+#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1
+#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2
+
+#define BM_GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16)
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1
+
+#define BP_GPMI_CTRL0_XFER_COUNT 0
+#define BM_GPMI_CTRL0_XFER_COUNT (0xffff << BP_GPMI_CTRL0_XFER_COUNT)
+#define BF_GPMI_CTRL0_XFER_COUNT(v) \
+ (((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT)
+
+#define HW_GPMI_COMPARE 0x00000010
+
+#define HW_GPMI_ECCCTRL 0x00000020
+#define HW_GPMI_ECCCTRL_SET 0x00000024
+#define HW_GPMI_ECCCTRL_CLR 0x00000028
+#define HW_GPMI_ECCCTRL_TOG 0x0000002c
+
+#define BP_GPMI_ECCCTRL_ECC_CMD 13
+#define BM_GPMI_ECCCTRL_ECC_CMD (3 << BP_GPMI_ECCCTRL_ECC_CMD)
+#define BF_GPMI_ECCCTRL_ECC_CMD(v) \
+ (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD)
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1
+
+#define BM_GPMI_ECCCTRL_ENABLE_ECC (1 << 12)
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0
+
+#define BP_GPMI_ECCCTRL_BUFFER_MASK 0
+#define BM_GPMI_ECCCTRL_BUFFER_MASK (0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK)
+#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \
+ (((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & BM_GPMI_ECCCTRL_BUFFER_MASK)
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF
+
+#define HW_GPMI_ECCCOUNT 0x00000030
+#define HW_GPMI_PAYLOAD 0x00000040
+#define HW_GPMI_AUXILIARY 0x00000050
+#define HW_GPMI_CTRL1 0x00000060
+#define HW_GPMI_CTRL1_SET 0x00000064
+#define HW_GPMI_CTRL1_CLR 0x00000068
+#define HW_GPMI_CTRL1_TOG 0x0000006c
+
+#define BM_GPMI_CTRL1_BCH_MODE (1 << 18)
+
+#define BP_GPMI_CTRL1_DLL_ENABLE 17
+#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE)
+
+#define BP_GPMI_CTRL1_HALF_PERIOD 16
+#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD)
+
+#define BP_GPMI_CTRL1_RDN_DELAY 12
+#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY)
+#define BF_GPMI_CTRL1_RDN_DELAY(v) \
+ (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY)
+
+#define BM_GPMI_CTRL1_DEV_RESET (1 << 3)
+#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0
+#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1
+
+#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2)
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1
+
+#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1)
+#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0
+#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1
+
+#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0)
+
+#define HW_GPMI_TIMING0 0x00000070
+
+#define BP_GPMI_TIMING0_ADDRESS_SETUP 16
+#define BM_GPMI_TIMING0_ADDRESS_SETUP (0xff << BP_GPMI_TIMING0_ADDRESS_SETUP)
+#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \
+ (((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP)
+
+#define BP_GPMI_TIMING0_DATA_HOLD 8
+#define BM_GPMI_TIMING0_DATA_HOLD (0xff << BP_GPMI_TIMING0_DATA_HOLD)
+#define BF_GPMI_TIMING0_DATA_HOLD(v) \
+ (((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD)
+
+#define BP_GPMI_TIMING0_DATA_SETUP 0
+#define BM_GPMI_TIMING0_DATA_SETUP (0xff << BP_GPMI_TIMING0_DATA_SETUP)
+#define BF_GPMI_TIMING0_DATA_SETUP(v) \
+ (((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP)
+
+#define HW_GPMI_TIMING1 0x00000080
+#define BP_GPMI_TIMING1_BUSY_TIMEOUT 16
+
+#define HW_GPMI_TIMING2 0x00000090
+#define HW_GPMI_DATA 0x000000a0
+
+/* MX28 uses this to detect READY. */
+#define HW_GPMI_STAT 0x000000b0
+#define MX28_BP_GPMI_STAT_READY_BUSY 24
+#define MX28_BM_GPMI_STAT_READY_BUSY (0xff << MX28_BP_GPMI_STAT_READY_BUSY)
+#define MX28_BF_GPMI_STAT_READY_BUSY(v) \
+ (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY)
+
+/* MX23 uses this to detect READY. */
+#define HW_GPMI_DEBUG 0x000000c0
+#define MX23_BP_GPMI_DEBUG_READY0 28
+#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0)
+#endif
diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c
index 02a03e67109c..5dc6f0d92f1a 100644
--- a/drivers/mtd/nand/h1910.c
+++ b/drivers/mtd/nand/h1910.c
@@ -81,9 +81,6 @@ static int h1910_device_ready(struct mtd_info *mtd)
static int __init h1910_init(void)
{
struct nand_chip *this;
- const char *part_type = 0;
- int mtd_parts_nb = 0;
- struct mtd_partition *mtd_parts = 0;
void __iomem *nandaddr;
if (!machine_is_h1900())
@@ -136,22 +133,10 @@ static int __init h1910_init(void)
iounmap((void *)nandaddr);
return -ENXIO;
}
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts, "h1910-nand");
- if (mtd_parts_nb > 0)
- part_type = "command line";
- else
- mtd_parts_nb = 0;
-#endif
- if (mtd_parts_nb == 0) {
- mtd_parts = partition_info;
- mtd_parts_nb = NUM_PARTITIONS;
- part_type = "static";
- }
/* Register the partitions */
- printk(KERN_NOTICE "Using %s partition definition\n", part_type);
- mtd_device_register(h1910_nand_mtd, mtd_parts, mtd_parts_nb);
+ mtd_device_parse_register(h1910_nand_mtd, NULL, 0,
+ partition_info, NUM_PARTITIONS);
/* Return happy */
return 0;
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c
index 6e813daed068..e2664073a89b 100644
--- a/drivers/mtd/nand/jz4740_nand.c
+++ b/drivers/mtd/nand/jz4740_nand.c
@@ -251,10 +251,6 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat,
return 0;
}
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-static const char *part_probes[] = {"cmdline", NULL};
-#endif
-
static int jz_nand_ioremap_resource(struct platform_device *pdev,
const char *name, struct resource **res, void __iomem **base)
{
@@ -299,8 +295,6 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
struct nand_chip *chip;
struct mtd_info *mtd;
struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
- struct mtd_partition *partition_info;
- int num_partitions = 0;
nand = kzalloc(sizeof(*nand), GFP_KERNEL);
if (!nand) {
@@ -373,15 +367,9 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
goto err_gpio_free;
}
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- num_partitions = parse_mtd_partitions(mtd, part_probes,
- &partition_info, 0);
-#endif
- if (num_partitions <= 0 && pdata) {
- num_partitions = pdata->num_partitions;
- partition_info = pdata->partitions;
- }
- ret = mtd_device_register(mtd, partition_info, num_partitions);
+ ret = mtd_device_parse_register(mtd, NULL, 0,
+ pdata ? pdata->partitions : NULL,
+ pdata ? pdata->num_partitions : 0);
if (ret) {
dev_err(&pdev->dev, "Failed to add mtd device\n");
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index 2f7c930872f9..5ede64706346 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -131,8 +131,6 @@ struct mpc5121_nfc_prv {
static void mpc5121_nfc_done(struct mtd_info *mtd);
-static const char *mpc5121_nfc_pprobes[] = { "cmdlinepart", NULL };
-
/* Read NFC register */
static inline u16 nfc_read(struct mtd_info *mtd, uint reg)
{
@@ -656,13 +654,13 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op)
struct mpc5121_nfc_prv *prv;
struct resource res;
struct mtd_info *mtd;
- struct mtd_partition *parts;
struct nand_chip *chip;
unsigned long regs_paddr, regs_size;
const __be32 *chips_no;
int resettime = 0;
int retval = 0;
int rev, len;
+ struct mtd_part_parser_data ppdata;
/*
* Check SoC revision. This driver supports only NFC
@@ -713,7 +711,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op)
}
regs_paddr = res.start;
- regs_size = res.end - res.start + 1;
+ regs_size = resource_size(&res);
if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) {
dev_err(dev, "Error requesting memory region!\n");
@@ -727,6 +725,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op)
}
mtd->name = "MPC5121 NAND";
+ ppdata.of_node = dn;
chip->dev_ready = mpc5121_nfc_dev_ready;
chip->cmdfunc = mpc5121_nfc_command;
chip->read_byte = mpc5121_nfc_read_byte;
@@ -735,7 +734,8 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op)
chip->write_buf = mpc5121_nfc_write_buf;
chip->verify_buf = mpc5121_nfc_verify_buf;
chip->select_chip = mpc5121_nfc_select_chip;
- chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT;
+ chip->options = NAND_NO_AUTOINCR;
+ chip->bbt_options = NAND_BBT_USE_FLASH;
chip->ecc.mode = NAND_ECC_SOFT;
/* Support external chip-select logic on ADS5121 board */
@@ -837,19 +837,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op)
dev_set_drvdata(dev, mtd);
/* Register device in MTD */
- retval = parse_mtd_partitions(mtd, mpc5121_nfc_pprobes, &parts, 0);
-#ifdef CONFIG_MTD_OF_PARTS
- if (retval == 0)
- retval = of_mtd_parse_partitions(dev, dn, &parts);
-#endif
- if (retval < 0) {
- dev_err(dev, "Error parsing MTD partitions!\n");
- devm_free_irq(dev, prv->irq, mtd);
- retval = -EINVAL;
- goto error;
- }
-
- retval = mtd_device_register(mtd, parts, retval);
+ retval = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
if (retval) {
dev_err(dev, "Error adding MTD device!\n");
devm_free_irq(dev, prv->irq, mtd);
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 90df34c4d26c..74a43b818d0e 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -41,7 +41,7 @@
#define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35())
#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21())
-#define nfc_is_v3_2() cpu_is_mx51()
+#define nfc_is_v3_2() (cpu_is_mx51() || cpu_is_mx53())
#define nfc_is_v3() nfc_is_v3_2()
/* Addresses for NFC registers */
@@ -143,7 +143,6 @@
struct mxc_nand_host {
struct mtd_info mtd;
struct nand_chip nand;
- struct mtd_partition *parts;
struct device *dev;
void *spare0;
@@ -350,8 +349,7 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq)
udelay(1);
}
if (max_retries < 0)
- DEBUG(MTD_DEBUG_LEVEL0, "%s: INT not set\n",
- __func__);
+ pr_debug("%s: INT not set\n", __func__);
}
}
@@ -371,7 +369,7 @@ static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq)
* waits for completion. */
static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
{
- DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x, %d)\n", cmd, useirq);
+ pr_debug("send_cmd(host, 0x%x, %d)\n", cmd, useirq);
writew(cmd, NFC_V1_V2_FLASH_CMD);
writew(NFC_CMD, NFC_V1_V2_CONFIG2);
@@ -387,8 +385,7 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
udelay(1);
}
if (max_retries < 0)
- DEBUG(MTD_DEBUG_LEVEL0, "%s: RESET failed\n",
- __func__);
+ pr_debug("%s: RESET failed\n", __func__);
} else {
/* Wait for operation to complete */
wait_op_done(host, useirq);
@@ -411,7 +408,7 @@ static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast)
* a NAND command. */
static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast)
{
- DEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x %d)\n", addr, islast);
+ pr_debug("send_addr(host, 0x%x %d)\n", addr, islast);
writew(addr, NFC_V1_V2_FLASH_ADDR);
writew(NFC_ADDR, NFC_V1_V2_CONFIG2);
@@ -561,8 +558,7 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat,
uint16_t ecc_status = readw(NFC_V1_V2_ECC_STATUS_RESULT);
if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
+ pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
return -1;
}
@@ -849,7 +845,7 @@ static void preset_v1_v2(struct mtd_info *mtd)
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3);
} else if (nfc_is_v1()) {
writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR);
- writew(0x4000, NFC_V1_UNLOCKEND_BLKADDR);
+ writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR);
} else
BUG();
@@ -932,8 +928,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
struct nand_chip *nand_chip = mtd->priv;
struct mxc_nand_host *host = nand_chip->priv;
- DEBUG(MTD_DEBUG_LEVEL3,
- "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
+ pr_debug("mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
command, column, page_addr);
/* Reset command state information */
@@ -1044,7 +1039,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
struct mxc_nand_platform_data *pdata = pdev->dev.platform_data;
struct mxc_nand_host *host;
struct resource *res;
- int err = 0, __maybe_unused nr_parts = 0;
+ int err = 0;
struct nand_ecclayout *oob_smallpage, *oob_largepage;
/* Allocate memory for MTD device structure and private data */
@@ -1179,7 +1174,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
/* update flash based bbt */
- this->options |= NAND_USE_FLASH_BBT;
+ this->bbt_options |= NAND_BBT_USE_FLASH;
}
init_completion(&host->op_completion);
@@ -1231,16 +1226,8 @@ static int __init mxcnd_probe(struct platform_device *pdev)
}
/* Register the partitions */
- nr_parts =
- parse_mtd_partitions(mtd, part_probes, &host->parts, 0);
- if (nr_parts > 0)
- mtd_device_register(mtd, host->parts, nr_parts);
- else if (pdata->parts)
- mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
- else {
- pr_info("Registering %s as whole device\n", mtd->name);
- mtd_device_register(mtd, NULL, 0);
- }
+ mtd_device_parse_register(mtd, part_probes, 0,
+ pdata->parts, pdata->nr_parts);
platform_set_drvdata(pdev, host);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index a46e9bb847bd..3ed9c5e4d34e 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -21,7 +21,7 @@
* TODO:
* Enable cached programming for 2k page size chips
* Check, if mtd->ecctype should be set to MTD_ECC_HW
- * if we have HW ecc support.
+ * if we have HW ECC support.
* The AG-AND chips have nice features for speed improvement,
* which are not supported yet. Read / program 4 pages in one go.
* BBT table is not serialized, has to be fixed
@@ -113,21 +113,19 @@ static int check_offs_len(struct mtd_info *mtd,
/* Start address must align on block boundary */
if (ofs & ((1 << chip->phys_erase_shift) - 1)) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__);
+ pr_debug("%s: unaligned address\n", __func__);
ret = -EINVAL;
}
/* Length must align on block boundary */
if (len & ((1 << chip->phys_erase_shift) - 1)) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n",
- __func__);
+ pr_debug("%s: length not block aligned\n", __func__);
ret = -EINVAL;
}
/* Do not allow past end of device */
if (ofs + len > mtd->size) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n",
- __func__);
+ pr_debug("%s: past end of device\n", __func__);
ret = -EINVAL;
}
@@ -136,9 +134,9 @@ static int check_offs_len(struct mtd_info *mtd,
/**
* nand_release_device - [GENERIC] release chip
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*
- * Deselect, release chip lock and wake up anyone waiting on the device
+ * Deselect, release chip lock and wake up anyone waiting on the device.
*/
static void nand_release_device(struct mtd_info *mtd)
{
@@ -157,9 +155,9 @@ static void nand_release_device(struct mtd_info *mtd)
/**
* nand_read_byte - [DEFAULT] read one byte from the chip
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*
- * Default read function for 8bit buswith
+ * Default read function for 8bit buswidth
*/
static uint8_t nand_read_byte(struct mtd_info *mtd)
{
@@ -169,10 +167,11 @@ static uint8_t nand_read_byte(struct mtd_info *mtd)
/**
* nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
- * @mtd: MTD device structure
+ * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip
+ * @mtd: MTD device structure
+ *
+ * Default read function for 16bit buswidth with endianness conversion.
*
- * Default read function for 16bit buswith with
- * endianess conversion
*/
static uint8_t nand_read_byte16(struct mtd_info *mtd)
{
@@ -182,10 +181,9 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd)
/**
* nand_read_word - [DEFAULT] read one word from the chip
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*
- * Default read function for 16bit buswith without
- * endianess conversion
+ * Default read function for 16bit buswidth without endianness conversion.
*/
static u16 nand_read_word(struct mtd_info *mtd)
{
@@ -195,8 +193,8 @@ static u16 nand_read_word(struct mtd_info *mtd)
/**
* nand_select_chip - [DEFAULT] control CE line
- * @mtd: MTD device structure
- * @chipnr: chipnumber to select, -1 for deselect
+ * @mtd: MTD device structure
+ * @chipnr: chipnumber to select, -1 for deselect
*
* Default select function for 1 chip devices.
*/
@@ -218,11 +216,11 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr)
/**
* nand_write_buf - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
*
- * Default write function for 8bit buswith
+ * Default write function for 8bit buswidth.
*/
static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
@@ -235,11 +233,11 @@ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
/**
* nand_read_buf - [DEFAULT] read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
*
- * Default read function for 8bit buswith
+ * Default read function for 8bit buswidth.
*/
static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
@@ -252,11 +250,11 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
/**
* nand_verify_buf - [DEFAULT] Verify chip data against buffer
- * @mtd: MTD device structure
- * @buf: buffer containing the data to compare
- * @len: number of bytes to compare
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to compare
+ * @len: number of bytes to compare
*
- * Default verify function for 8bit buswith
+ * Default verify function for 8bit buswidth.
*/
static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
@@ -271,11 +269,11 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
/**
* nand_write_buf16 - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
*
- * Default write function for 16bit buswith
+ * Default write function for 16bit buswidth.
*/
static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
{
@@ -291,11 +289,11 @@ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
/**
* nand_read_buf16 - [DEFAULT] read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
*
- * Default read function for 16bit buswith
+ * Default read function for 16bit buswidth.
*/
static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
{
@@ -310,11 +308,11 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
/**
* nand_verify_buf16 - [DEFAULT] Verify chip data against buffer
- * @mtd: MTD device structure
- * @buf: buffer containing the data to compare
- * @len: number of bytes to compare
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to compare
+ * @len: number of bytes to compare
*
- * Default verify function for 16bit buswith
+ * Default verify function for 16bit buswidth.
*/
static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
{
@@ -332,9 +330,9 @@ static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
/**
* nand_block_bad - [DEFAULT] Read bad block marker from the chip
- * @mtd: MTD device structure
- * @ofs: offset from device start
- * @getchip: 0, if the chip is already selected
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ * @getchip: 0, if the chip is already selected
*
* Check, if the block is bad.
*/
@@ -344,7 +342,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
struct nand_chip *chip = mtd->priv;
u16 bad;
- if (chip->options & NAND_BBT_SCANLASTPAGE)
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
ofs += mtd->erasesize - mtd->writesize;
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
@@ -384,11 +382,11 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
/**
* nand_default_block_markbad - [DEFAULT] mark a block bad
- * @mtd: MTD device structure
- * @ofs: offset from device start
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
*
- * This is the default implementation, which can be overridden by
- * a hardware specific driver.
+ * This is the default implementation, which can be overridden by a hardware
+ * specific driver.
*/
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
@@ -396,7 +394,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
uint8_t buf[2] = { 0, 0 };
int block, ret, i = 0;
- if (chip->options & NAND_BBT_SCANLASTPAGE)
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
ofs += mtd->erasesize - mtd->writesize;
/* Get block number */
@@ -404,33 +402,31 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
if (chip->bbt)
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
- /* Do we have a flash based bad block table ? */
- if (chip->options & NAND_USE_FLASH_BBT)
+ /* Do we have a flash based bad block table? */
+ if (chip->bbt_options & NAND_BBT_USE_FLASH)
ret = nand_update_bbt(mtd, ofs);
else {
+ struct mtd_oob_ops ops;
+
nand_get_device(chip, mtd, FL_WRITING);
- /* Write to first two pages and to byte 1 and 6 if necessary.
- * If we write to more than one location, the first error
- * encountered quits the procedure. We write two bytes per
- * location, so we dont have to mess with 16 bit access.
+ /*
+ * Write to first two pages if necessary. If we write to more
+ * than one location, the first error encountered quits the
+ * procedure. We write two bytes per location, so we dont have
+ * to mess with 16 bit access.
*/
+ ops.len = ops.ooblen = 2;
+ ops.datbuf = NULL;
+ ops.oobbuf = buf;
+ ops.ooboffs = chip->badblockpos & ~0x01;
+ ops.mode = MTD_OPS_PLACE_OOB;
do {
- chip->ops.len = chip->ops.ooblen = 2;
- chip->ops.datbuf = NULL;
- chip->ops.oobbuf = buf;
- chip->ops.ooboffs = chip->badblockpos & ~0x01;
-
- ret = nand_do_write_oob(mtd, ofs, &chip->ops);
+ ret = nand_do_write_oob(mtd, ofs, &ops);
- if (!ret && (chip->options & NAND_BBT_SCANBYTE1AND6)) {
- chip->ops.ooboffs = NAND_SMALL_BADBLOCK_POS
- & ~0x01;
- ret = nand_do_write_oob(mtd, ofs, &chip->ops);
- }
i++;
ofs += mtd->writesize;
- } while (!ret && (chip->options & NAND_BBT_SCAN2NDPAGE) &&
+ } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) &&
i < 2);
nand_release_device(mtd);
@@ -443,16 +439,16 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
/**
* nand_check_wp - [GENERIC] check if the chip is write protected
- * @mtd: MTD device structure
- * Check, if the device is write protected
+ * @mtd: MTD device structure
*
- * The function expects, that the device is already selected
+ * Check, if the device is write protected. The function expects, that the
+ * device is already selected.
*/
static int nand_check_wp(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
- /* broken xD cards report WP despite being writable */
+ /* Broken xD cards report WP despite being writable */
if (chip->options & NAND_BROKEN_XD)
return 0;
@@ -463,10 +459,10 @@ static int nand_check_wp(struct mtd_info *mtd)
/**
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
- * @mtd: MTD device structure
- * @ofs: offset from device start
- * @getchip: 0, if the chip is already selected
- * @allowbbt: 1, if its allowed to access the bbt area
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ * @getchip: 0, if the chip is already selected
+ * @allowbbt: 1, if its allowed to access the bbt area
*
* Check, if the block is bad. Either by reading the bad block table or
* calling of the scan function.
@@ -485,8 +481,8 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
/**
* panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
- * @mtd: MTD device structure
- * @timeo: Timeout
+ * @mtd: MTD device structure
+ * @timeo: Timeout
*
* Helper function for nand_wait_ready used when needing to wait in interrupt
* context.
@@ -505,10 +501,7 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
}
}
-/*
- * Wait for the ready pin, after a command
- * The timeout is catched later.
- */
+/* Wait for the ready pin, after a command. The timeout is caught later. */
void nand_wait_ready(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
@@ -519,7 +512,7 @@ void nand_wait_ready(struct mtd_info *mtd)
return panic_nand_wait_ready(mtd, 400);
led_trigger_event(nand_led_trigger, LED_FULL);
- /* wait until command is processed or timeout occures */
+ /* Wait until command is processed or timeout occurs */
do {
if (chip->dev_ready(mtd))
break;
@@ -531,13 +524,13 @@ EXPORT_SYMBOL_GPL(nand_wait_ready);
/**
* nand_command - [DEFAULT] Send command to NAND device
- * @mtd: MTD device structure
- * @command: the command to be sent
- * @column: the column address for this command, -1 if none
- * @page_addr: the page address for this command, -1 if none
+ * @mtd: MTD device structure
+ * @command: the command to be sent
+ * @column: the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
*
- * Send command to NAND device. This function is used for small page
- * devices (256/512 Bytes per page)
+ * Send command to NAND device. This function is used for small page devices
+ * (256/512 Bytes per page).
*/
static void nand_command(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
@@ -545,9 +538,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
register struct nand_chip *chip = mtd->priv;
int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
- /*
- * Write out the command to the device.
- */
+ /* Write out the command to the device */
if (command == NAND_CMD_SEQIN) {
int readcmd;
@@ -567,9 +558,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
}
chip->cmd_ctrl(mtd, command, ctrl);
- /*
- * Address cycle, when necessary
- */
+ /* Address cycle, when necessary */
ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
/* Serially input address */
if (column != -1) {
@@ -590,8 +579,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/*
- * program and erase have their own busy handlers
- * status and sequential in needs no delay
+ * Program and erase have their own busy handlers status and sequential
+ * in needs no delay
*/
switch (command) {
@@ -625,8 +614,10 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
return;
}
}
- /* Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine. */
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine.
+ */
ndelay(100);
nand_wait_ready(mtd);
@@ -634,14 +625,14 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
/**
* nand_command_lp - [DEFAULT] Send command to NAND large page device
- * @mtd: MTD device structure
- * @command: the command to be sent
- * @column: the column address for this command, -1 if none
- * @page_addr: the page address for this command, -1 if none
+ * @mtd: MTD device structure
+ * @command: the command to be sent
+ * @column: the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
*
* Send command to NAND device. This is the version for the new large page
- * devices We dont have the separate regions as we have in the small page
- * devices. We must emulate NAND_CMD_READOOB to keep the code compatible.
+ * devices. We don't have the separate regions as we have in the small page
+ * devices. We must emulate NAND_CMD_READOOB to keep the code compatible.
*/
static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
@@ -683,8 +674,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/*
- * program and erase have their own busy handlers
- * status, sequential in, and deplete1 need no delay
+ * Program and erase have their own busy handlers status, sequential
+ * in, and deplete1 need no delay.
*/
switch (command) {
@@ -698,14 +689,12 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
case NAND_CMD_DEPLETE1:
return;
- /*
- * read error status commands require only a short delay
- */
case NAND_CMD_STATUS_ERROR:
case NAND_CMD_STATUS_ERROR0:
case NAND_CMD_STATUS_ERROR1:
case NAND_CMD_STATUS_ERROR2:
case NAND_CMD_STATUS_ERROR3:
+ /* Read error status commands require only a short delay */
udelay(chip->chip_delay);
return;
@@ -739,7 +728,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
default:
/*
* If we don't have access to the busy pin, we apply the given
- * command delay
+ * command delay.
*/
if (!chip->dev_ready) {
udelay(chip->chip_delay);
@@ -747,8 +736,10 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
}
}
- /* Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine. */
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine.
+ */
ndelay(100);
nand_wait_ready(mtd);
@@ -756,25 +747,25 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
/**
* panic_nand_get_device - [GENERIC] Get chip for selected access
- * @chip: the nand chip descriptor
- * @mtd: MTD device structure
- * @new_state: the state which is requested
+ * @chip: the nand chip descriptor
+ * @mtd: MTD device structure
+ * @new_state: the state which is requested
*
* Used when in panic, no locks are taken.
*/
static void panic_nand_get_device(struct nand_chip *chip,
struct mtd_info *mtd, int new_state)
{
- /* Hardware controller shared among independend devices */
+ /* Hardware controller shared among independent devices */
chip->controller->active = chip;
chip->state = new_state;
}
/**
* nand_get_device - [GENERIC] Get chip for selected access
- * @chip: the nand chip descriptor
- * @mtd: MTD device structure
- * @new_state: the state which is requested
+ * @chip: the nand chip descriptor
+ * @mtd: MTD device structure
+ * @new_state: the state which is requested
*
* Get the device and lock it for exclusive access
*/
@@ -812,10 +803,10 @@ retry:
}
/**
- * panic_nand_wait - [GENERIC] wait until the command is done
- * @mtd: MTD device structure
- * @chip: NAND chip structure
- * @timeo: Timeout
+ * panic_nand_wait - [GENERIC] wait until the command is done
+ * @mtd: MTD device structure
+ * @chip: NAND chip structure
+ * @timeo: timeout
*
* Wait for command done. This is a helper function for nand_wait used when
* we are in interrupt context. May happen when in panic and trying to write
@@ -838,13 +829,13 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_wait - [DEFAULT] wait until the command is done
- * @mtd: MTD device structure
- * @chip: NAND chip structure
+ * nand_wait - [DEFAULT] wait until the command is done
+ * @mtd: MTD device structure
+ * @chip: NAND chip structure
*
- * Wait for command done. This applies to erase and program only
- * Erase can take up to 400ms and program up to 20ms according to
- * general NAND and SmartMedia specs
+ * Wait for command done. This applies to erase and program only. Erase can
+ * take up to 400ms and program up to 20ms according to general NAND and
+ * SmartMedia specs.
*/
static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
@@ -859,8 +850,10 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
led_trigger_event(nand_led_trigger, LED_FULL);
- /* Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine. */
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in any
+ * case on any machine.
+ */
ndelay(100);
if ((state == FL_ERASING) && (chip->options & NAND_IS_AND))
@@ -890,16 +883,15 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
/**
* __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
- *
* @mtd: mtd info
* @ofs: offset to start unlock from
* @len: length to unlock
- * @invert: when = 0, unlock the range of blocks within the lower and
- * upper boundary address
- * when = 1, unlock the range of blocks outside the boundaries
- * of the lower and upper boundary address
+ * @invert: when = 0, unlock the range of blocks within the lower and
+ * upper boundary address
+ * when = 1, unlock the range of blocks outside the boundaries
+ * of the lower and upper boundary address
*
- * return - unlock status
+ * Returs unlock status.
*/
static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
uint64_t len, int invert)
@@ -919,10 +911,9 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
/* Call wait ready function */
status = chip->waitfunc(mtd, chip);
- udelay(1000);
/* See if device thinks it succeeded */
if (status & 0x01) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n",
+ pr_debug("%s: error status = 0x%08x\n",
__func__, status);
ret = -EIO;
}
@@ -932,12 +923,11 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
/**
* nand_unlock - [REPLACEABLE] unlocks specified locked blocks
- *
* @mtd: mtd info
* @ofs: offset to start unlock from
* @len: length to unlock
*
- * return - unlock status
+ * Returns unlock status.
*/
int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
@@ -945,7 +935,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
int chipnr;
struct nand_chip *chip = mtd->priv;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n",
+ pr_debug("%s: start = 0x%012llx, len = %llu\n",
__func__, (unsigned long long)ofs, len);
if (check_offs_len(mtd, ofs, len))
@@ -964,7 +954,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
/* Check, if it is write protected */
if (nand_check_wp(mtd)) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n",
+ pr_debug("%s: device is write protected!\n",
__func__);
ret = -EIO;
goto out;
@@ -981,18 +971,16 @@ EXPORT_SYMBOL(nand_unlock);
/**
* nand_lock - [REPLACEABLE] locks all blocks present in the device
- *
* @mtd: mtd info
* @ofs: offset to start unlock from
* @len: length to unlock
*
- * return - lock status
+ * This feature is not supported in many NAND parts. 'Micron' NAND parts do
+ * have this feature, but it allows only to lock all blocks, not for specified
+ * range for block. Implementing 'lock' feature by making use of 'unlock', for
+ * now.
*
- * This feature is not supported in many NAND parts. 'Micron' NAND parts
- * do have this feature, but it allows only to lock all blocks, not for
- * specified range for block.
- *
- * Implementing 'lock' feature by making use of 'unlock', for now.
+ * Returns lock status.
*/
int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
@@ -1000,7 +988,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
int chipnr, status, page;
struct nand_chip *chip = mtd->priv;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n",
+ pr_debug("%s: start = 0x%012llx, len = %llu\n",
__func__, (unsigned long long)ofs, len);
if (check_offs_len(mtd, ofs, len))
@@ -1015,7 +1003,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
/* Check, if it is write protected */
if (nand_check_wp(mtd)) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n",
+ pr_debug("%s: device is write protected!\n",
__func__);
status = MTD_ERASE_FAILED;
ret = -EIO;
@@ -1028,10 +1016,9 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
/* Call wait ready function */
status = chip->waitfunc(mtd, chip);
- udelay(1000);
/* See if device thinks it succeeded */
if (status & 0x01) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n",
+ pr_debug("%s: error status = 0x%08x\n",
__func__, status);
ret = -EIO;
goto out;
@@ -1047,13 +1034,13 @@ out:
EXPORT_SYMBOL(nand_lock);
/**
- * nand_read_page_raw - [Intern] read raw page data without ecc
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_raw - [INTERN] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @page: page number to read
*
- * Not for syndrome calculating ecc controllers, which use a special oob layout
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int page)
@@ -1064,11 +1051,11 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @page: page number to read
*
* We need a special oob layout and handling even when OOB isn't used.
*/
@@ -1107,11 +1094,11 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
}
/**
- * nand_read_page_swecc - [REPLACABLE] software ecc based page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @page: page number to read
*/
static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int page)
@@ -1148,12 +1135,12 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @data_offs: offset of requested data within the page
- * @readlen: data length
- * @bufpoi: buffer to store read data
+ * nand_read_subpage - [REPLACEABLE] software ECC based sub-page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @data_offs: offset of requested data within the page
+ * @readlen: data length
+ * @bufpoi: buffer to store read data
*/
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
@@ -1166,12 +1153,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
int index = 0;
- /* Column address wihin the page aligned to ECC size (256bytes). */
+ /* Column address within the page aligned to ECC size (256bytes) */
start_step = data_offs / chip->ecc.size;
end_step = (data_offs + readlen - 1) / chip->ecc.size;
num_steps = end_step - start_step + 1;
- /* Data size aligned to ECC ecc.size*/
+ /* Data size aligned to ECC ecc.size */
datafrag_len = num_steps * chip->ecc.size;
eccfrag_len = num_steps * chip->ecc.bytes;
@@ -1183,13 +1170,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
p = bufpoi + data_col_addr;
chip->read_buf(mtd, p, datafrag_len);
- /* Calculate ECC */
+ /* Calculate ECC */
for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
- /* The performance is faster if to position offsets
- according to ecc.pos. Let make sure here that
- there are no gaps in ecc positions */
+ /*
+ * The performance is faster if we position offsets according to
+ * ecc.pos. Let's make sure that there are no gaps in ECC positions.
+ */
for (i = 0; i < eccfrag_len - 1; i++) {
if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
eccpos[i + start_step * chip->ecc.bytes + 1]) {
@@ -1201,8 +1189,10 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
} else {
- /* send the command to read the particular ecc bytes */
- /* take care about buswidth alignment in read_buf */
+ /*
+ * Send the command to read the particular ECC bytes take care
+ * about buswidth alignment in read_buf.
+ */
index = start_step * chip->ecc.bytes;
aligned_pos = eccpos[index] & ~(busw - 1);
@@ -1235,13 +1225,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @page: page number to read
*
- * Not for syndrome calculating ecc controllers which need a special oob layout
+ * Not for syndrome calculating ECC controllers which need a special oob layout.
*/
static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int page)
@@ -1280,18 +1270,17 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @page: page number to read
*
- * Hardware ECC for large page chips, require OOB to be read first.
- * For this ECC mode, the write_page method is re-used from ECC_HW.
- * These methods read/write ECC from the OOB area, unlike the
- * ECC_HW_SYNDROME support with multiple ECC steps, follows the
- * "infix ECC" scheme and reads/writes ECC from the data area, by
- * overwriting the NAND manufacturer bad block markings.
+ * Hardware ECC for large page chips, require OOB to be read first. For this
+ * ECC mode, the write_page method is re-used from ECC_HW. These methods
+ * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with
+ * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from
+ * the data area, by overwriting the NAND manufacturer bad block markings.
*/
static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int page)
@@ -1329,14 +1318,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
}
/**
- * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @page: page number to read
*
- * The hw generator calculates the error syndrome automatically. Therefor
- * we need a special oob layout and handling.
+ * The hw generator calculates the error syndrome automatically. Therefore we
+ * need a special oob layout and handling.
*/
static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int page)
@@ -1384,29 +1373,29 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_transfer_oob - [Internal] Transfer oob to client buffer
- * @chip: nand chip structure
- * @oob: oob destination address
- * @ops: oob ops structure
- * @len: size of oob to transfer
+ * nand_transfer_oob - [INTERN] Transfer oob to client buffer
+ * @chip: nand chip structure
+ * @oob: oob destination address
+ * @ops: oob ops structure
+ * @len: size of oob to transfer
*/
static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
struct mtd_oob_ops *ops, size_t len)
{
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
memcpy(oob, chip->oob_poi + ops->ooboffs, len);
return oob + len;
- case MTD_OOB_AUTO: {
+ case MTD_OPS_AUTO_OOB: {
struct nand_oobfree *free = chip->ecc.layout->oobfree;
uint32_t boffs = 0, roffs = ops->ooboffs;
size_t bytes = 0;
for (; free->length && len; free++, len -= bytes) {
- /* Read request not from offset 0 ? */
+ /* Read request not from offset 0? */
if (unlikely(roffs)) {
if (roffs >= free->length) {
roffs -= free->length;
@@ -1432,11 +1421,10 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
}
/**
- * nand_do_read_ops - [Internal] Read data with ECC
- *
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob ops structure
+ * nand_do_read_ops - [INTERN] Read data with ECC
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob ops structure
*
* Internal function. Called with chip held.
*/
@@ -1451,7 +1439,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
int ret = 0;
uint32_t readlen = ops->len;
uint32_t oobreadlen = ops->ooblen;
- uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ?
+ uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ?
mtd->oobavail : mtd->oobsize;
uint8_t *bufpoi, *oob, *buf;
@@ -1473,7 +1461,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
bytes = min(mtd->writesize - col, readlen);
aligned = (bytes == mtd->writesize);
- /* Is the current page in the buffer ? */
+ /* Is the current page in the buffer? */
if (realpage != chip->pagebuf || oob) {
bufpoi = aligned ? buf : chip->buffers->databuf;
@@ -1483,7 +1471,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
}
/* Now read the page into the buffer */
- if (unlikely(ops->mode == MTD_OOB_RAW))
+ if (unlikely(ops->mode == MTD_OPS_RAW))
ret = chip->ecc.read_page_raw(mtd, chip,
bufpoi, page);
else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
@@ -1492,14 +1480,22 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
else
ret = chip->ecc.read_page(mtd, chip, bufpoi,
page);
- if (ret < 0)
+ if (ret < 0) {
+ if (!aligned)
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
break;
+ }
/* Transfer not aligned data */
if (!aligned) {
if (!NAND_SUBPAGE_READ(chip) && !oob &&
- !(mtd->ecc_stats.failed - stats.failed))
+ !(mtd->ecc_stats.failed - stats.failed) &&
+ (ops->mode != MTD_OPS_RAW))
chip->pagebuf = realpage;
+ else
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
memcpy(buf, chip->buffers->databuf + col, bytes);
}
@@ -1539,7 +1535,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
if (!readlen)
break;
- /* For subsequent reads align to page boundary. */
+ /* For subsequent reads align to page boundary */
col = 0;
/* Increment page address */
realpage++;
@@ -1552,8 +1548,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
chip->select_chip(mtd, chipnr);
}
- /* Check, if the chip supports auto page increment
- * or if we have hit a block boundary.
+ /*
+ * Check, if the chip supports auto page increment or if we
+ * have hit a block boundary.
*/
if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
sndcmd = 1;
@@ -1574,18 +1571,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
/**
* nand_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc
- * @mtd: MTD device structure
- * @from: offset to read from
- * @len: number of bytes to read
- * @retlen: pointer to variable to store the number of read bytes
- * @buf: the databuffer to put data
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @len: number of bytes to read
+ * @retlen: pointer to variable to store the number of read bytes
+ * @buf: the databuffer to put data
*
- * Get hold of the chip and call nand_do_read
+ * Get hold of the chip and call nand_do_read.
*/
static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
+ struct mtd_oob_ops ops;
int ret;
/* Do not allow reads past end of device */
@@ -1596,13 +1594,14 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
nand_get_device(chip, mtd, FL_READING);
- chip->ops.len = len;
- chip->ops.datbuf = buf;
- chip->ops.oobbuf = NULL;
+ ops.len = len;
+ ops.datbuf = buf;
+ ops.oobbuf = NULL;
+ ops.mode = 0;
- ret = nand_do_read_ops(mtd, from, &chip->ops);
+ ret = nand_do_read_ops(mtd, from, &ops);
- *retlen = chip->ops.retlen;
+ *retlen = ops.retlen;
nand_release_device(mtd);
@@ -1610,11 +1609,11 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
}
/**
- * nand_read_oob_std - [REPLACABLE] the most common OOB data read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to read
- * @sndcmd: flag whether to issue read command or not
+ * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ * @sndcmd: flag whether to issue read command or not
*/
static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
int page, int sndcmd)
@@ -1628,12 +1627,12 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC
+ * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
* with syndromes
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to read
- * @sndcmd: flag whether to issue read command or not
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ * @sndcmd: flag whether to issue read command or not
*/
static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
int page, int sndcmd)
@@ -1667,10 +1666,10 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_write_oob_std - [REPLACABLE] the most common OOB data write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to write
+ * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
*/
static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
int page)
@@ -1690,11 +1689,11 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC
- * with syndrome - only for large page flash !
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to write
+ * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
+ * with syndrome - only for large page flash
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
*/
static int nand_write_oob_syndrome(struct mtd_info *mtd,
struct nand_chip *chip, int page)
@@ -1749,34 +1748,37 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
}
/**
- * nand_do_read_oob - [Intern] NAND read out-of-band
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob operations description structure
+ * nand_do_read_oob - [INTERN] NAND read out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operations description structure
*
- * NAND read out-of-band data from the spare area
+ * NAND read out-of-band data from the spare area.
*/
static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int page, realpage, chipnr, sndcmd = 1;
struct nand_chip *chip = mtd->priv;
+ struct mtd_ecc_stats stats;
int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
int readlen = ops->ooblen;
int len;
uint8_t *buf = ops->oobbuf;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n",
+ pr_debug("%s: from = 0x%08Lx, len = %i\n",
__func__, (unsigned long long)from, readlen);
- if (ops->mode == MTD_OOB_AUTO)
+ stats = mtd->ecc_stats;
+
+ if (ops->mode == MTD_OPS_AUTO_OOB)
len = chip->ecc.layout->oobavail;
else
len = mtd->oobsize;
if (unlikely(ops->ooboffs >= len)) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read "
- "outside oob\n", __func__);
+ pr_debug("%s: attempt to start read outside oob\n",
+ __func__);
return -EINVAL;
}
@@ -1784,8 +1786,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
if (unlikely(from >= mtd->size ||
ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
(from >> chip->page_shift)) * len)) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end "
- "of device\n", __func__);
+ pr_debug("%s: attempt to read beyond end of device\n",
+ __func__);
return -EINVAL;
}
@@ -1797,7 +1799,10 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
page = realpage & chip->pagemask;
while (1) {
- sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
+ if (ops->mode == MTD_OPS_RAW)
+ sndcmd = chip->ecc.read_oob_raw(mtd, chip, page, sndcmd);
+ else
+ sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
len = min(len, readlen);
buf = nand_transfer_oob(chip, buf, ops, len);
@@ -1830,24 +1835,29 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
chip->select_chip(mtd, chipnr);
}
- /* Check, if the chip supports auto page increment
- * or if we have hit a block boundary.
+ /*
+ * Check, if the chip supports auto page increment or if we
+ * have hit a block boundary.
*/
if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
sndcmd = 1;
}
ops->oobretlen = ops->ooblen;
- return 0;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
}
/**
* nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob operation description structure
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operation description structure
*
- * NAND read data and/or out-of-band data
+ * NAND read data and/or out-of-band data.
*/
static int nand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
@@ -1859,17 +1869,17 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
/* Do not allow reads past end of device */
if (ops->datbuf && (from + ops->len) > mtd->size) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read "
- "beyond end of device\n", __func__);
+ pr_debug("%s: attempt to read beyond end of device\n",
+ __func__);
return -EINVAL;
}
nand_get_device(chip, mtd, FL_READING);
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
break;
default:
@@ -1888,12 +1898,12 @@ out:
/**
- * nand_write_page_raw - [Intern] raw page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
+ * nand_write_page_raw - [INTERN] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
*
- * Not for syndrome calculating ecc controllers, which use a special oob layout
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
@@ -1903,10 +1913,10 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_write_page_raw_syndrome - [Intern] raw page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
+ * nand_write_page_raw_syndrome - [INTERN] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
*
* We need a special oob layout and handling even when ECC isn't checked.
*/
@@ -1942,10 +1952,10 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
chip->write_buf(mtd, oob, size);
}
/**
- * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
+ * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
*/
static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
@@ -1957,7 +1967,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *p = buf;
uint32_t *eccpos = chip->ecc.layout->eccpos;
- /* Software ecc calculation */
+ /* Software ECC calculation */
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
@@ -1968,10 +1978,10 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
+ * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
*/
static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
@@ -1996,13 +2006,13 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
+ * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
*
- * The hw generator calculates the error syndrome automatically. Therefor
- * we need a special oob layout and handling.
+ * The hw generator calculates the error syndrome automatically. Therefore we
+ * need a special oob layout and handling.
*/
static void nand_write_page_syndrome(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf)
@@ -2041,12 +2051,12 @@ static void nand_write_page_syndrome(struct mtd_info *mtd,
/**
* nand_write_page - [REPLACEABLE] write one page
- * @mtd: MTD device structure
- * @chip: NAND chip descriptor
- * @buf: the data to write
- * @page: page number to write
- * @cached: cached programming
- * @raw: use _raw version of write_page
+ * @mtd: MTD device structure
+ * @chip: NAND chip descriptor
+ * @buf: the data to write
+ * @page: page number to write
+ * @cached: cached programming
+ * @raw: use _raw version of write_page
*/
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, int cached, int raw)
@@ -2061,8 +2071,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->ecc.write_page(mtd, chip, buf);
/*
- * Cached progamming disabled for now, Not sure if its worth the
- * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
+ * Cached progamming disabled for now. Not sure if it's worth the
+ * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s).
*/
cached = 0;
@@ -2072,7 +2082,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
status = chip->waitfunc(mtd, chip);
/*
* See if operation failed and additional status checks are
- * available
+ * available.
*/
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
status = chip->errstat(mtd, chip, FL_WRITING, status,
@@ -2096,29 +2106,37 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_fill_oob - [Internal] Transfer client buffer to oob
- * @chip: nand chip structure
- * @oob: oob data buffer
- * @len: oob data write length
- * @ops: oob ops structure
+ * nand_fill_oob - [INTERN] Transfer client buffer to oob
+ * @mtd: MTD device structure
+ * @oob: oob data buffer
+ * @len: oob data write length
+ * @ops: oob ops structure
*/
-static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
- struct mtd_oob_ops *ops)
+static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
+ struct mtd_oob_ops *ops)
{
+ struct nand_chip *chip = mtd->priv;
+
+ /*
+ * Initialise to all 0xFF, to avoid the possibility of left over OOB
+ * data from a previous OOB read.
+ */
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
memcpy(chip->oob_poi + ops->ooboffs, oob, len);
return oob + len;
- case MTD_OOB_AUTO: {
+ case MTD_OPS_AUTO_OOB: {
struct nand_oobfree *free = chip->ecc.layout->oobfree;
uint32_t boffs = 0, woffs = ops->ooboffs;
size_t bytes = 0;
for (; free->length && len; free++, len -= bytes) {
- /* Write request not from offset 0 ? */
+ /* Write request not from offset 0? */
if (unlikely(woffs)) {
if (woffs >= free->length) {
woffs -= free->length;
@@ -2146,12 +2164,12 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
/**
- * nand_do_write_ops - [Internal] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operations description structure
+ * nand_do_write_ops - [INTERN] NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operations description structure
*
- * NAND write with ECC
+ * NAND write with ECC.
*/
static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
@@ -2161,7 +2179,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
uint32_t writelen = ops->len;
uint32_t oobwritelen = ops->ooblen;
- uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ?
+ uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ?
mtd->oobavail : mtd->oobsize;
uint8_t *oob = ops->oobbuf;
@@ -2172,10 +2190,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
if (!writelen)
return 0;
- /* reject writes, which are not page aligned */
+ /* Reject writes, which are not page aligned */
if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
- printk(KERN_NOTICE "%s: Attempt to write not "
- "page aligned data\n", __func__);
+ pr_notice("%s: attempt to write non page aligned data\n",
+ __func__);
return -EINVAL;
}
@@ -2201,10 +2219,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
(chip->pagebuf << chip->page_shift) < (to + ops->len))
chip->pagebuf = -1;
- /* If we're not given explicit OOB data, let it be 0xFF */
- if (likely(!oob))
- memset(chip->oob_poi, 0xff, mtd->oobsize);
-
/* Don't allow multipage oob writes with offset */
if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
return -EINVAL;
@@ -2214,7 +2228,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
int cached = writelen > bytes && page != blockmask;
uint8_t *wbuf = buf;
- /* Partial page write ? */
+ /* Partial page write? */
if (unlikely(column || writelen < (mtd->writesize - 1))) {
cached = 0;
bytes = min_t(int, bytes - column, (int) writelen);
@@ -2226,12 +2240,15 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
if (unlikely(oob)) {
size_t len = min(oobwritelen, oobmaxlen);
- oob = nand_fill_oob(chip, oob, len, ops);
+ oob = nand_fill_oob(mtd, oob, len, ops);
oobwritelen -= len;
+ } else {
+ /* We still need to erase leftover OOB data */
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
}
ret = chip->write_page(mtd, chip, wbuf, page, cached,
- (ops->mode == MTD_OOB_RAW));
+ (ops->mode == MTD_OPS_RAW));
if (ret)
break;
@@ -2260,11 +2277,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
/**
* panic_nand_write - [MTD Interface] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @len: number of bytes to write
- * @retlen: pointer to variable to store the number of written bytes
- * @buf: the data to write
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @retlen: pointer to variable to store the number of written bytes
+ * @buf: the data to write
*
* NAND write with ECC. Used when performing writes in interrupt context, this
* may for example be called by mtdoops when writing an oops while in panic.
@@ -2273,6 +2290,7 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
+ struct mtd_oob_ops ops;
int ret;
/* Do not allow reads past end of device */
@@ -2281,36 +2299,38 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
if (!len)
return 0;
- /* Wait for the device to get ready. */
+ /* Wait for the device to get ready */
panic_nand_wait(mtd, chip, 400);
- /* Grab the device. */
+ /* Grab the device */
panic_nand_get_device(chip, mtd, FL_WRITING);
- chip->ops.len = len;
- chip->ops.datbuf = (uint8_t *)buf;
- chip->ops.oobbuf = NULL;
+ ops.len = len;
+ ops.datbuf = (uint8_t *)buf;
+ ops.oobbuf = NULL;
+ ops.mode = 0;
- ret = nand_do_write_ops(mtd, to, &chip->ops);
+ ret = nand_do_write_ops(mtd, to, &ops);
- *retlen = chip->ops.retlen;
+ *retlen = ops.retlen;
return ret;
}
/**
* nand_write - [MTD Interface] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @len: number of bytes to write
- * @retlen: pointer to variable to store the number of written bytes
- * @buf: the data to write
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @retlen: pointer to variable to store the number of written bytes
+ * @buf: the data to write
*
- * NAND write with ECC
+ * NAND write with ECC.
*/
static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
+ struct mtd_oob_ops ops;
int ret;
/* Do not allow reads past end of device */
@@ -2321,13 +2341,14 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
nand_get_device(chip, mtd, FL_WRITING);
- chip->ops.len = len;
- chip->ops.datbuf = (uint8_t *)buf;
- chip->ops.oobbuf = NULL;
+ ops.len = len;
+ ops.datbuf = (uint8_t *)buf;
+ ops.oobbuf = NULL;
+ ops.mode = 0;
- ret = nand_do_write_ops(mtd, to, &chip->ops);
+ ret = nand_do_write_ops(mtd, to, &ops);
- *retlen = chip->ops.retlen;
+ *retlen = ops.retlen;
nand_release_device(mtd);
@@ -2336,11 +2357,11 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
/**
* nand_do_write_oob - [MTD Interface] NAND write out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
*
- * NAND write out-of-band
+ * NAND write out-of-band.
*/
static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
@@ -2348,24 +2369,24 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
int chipnr, page, status, len;
struct nand_chip *chip = mtd->priv;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
+ pr_debug("%s: to = 0x%08x, len = %i\n",
__func__, (unsigned int)to, (int)ops->ooblen);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
len = chip->ecc.layout->oobavail;
else
len = mtd->oobsize;
/* Do not allow write past end of page */
if ((ops->ooboffs + ops->ooblen) > len) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write "
- "past end of page\n", __func__);
+ pr_debug("%s: attempt to write past end of page\n",
+ __func__);
return -EINVAL;
}
if (unlikely(ops->ooboffs >= len)) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start "
- "write outside oob\n", __func__);
+ pr_debug("%s: attempt to start write outside oob\n",
+ __func__);
return -EINVAL;
}
@@ -2374,8 +2395,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
ops->ooboffs + ops->ooblen >
((mtd->size >> chip->page_shift) -
(to >> chip->page_shift)) * len)) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond "
- "end of device\n", __func__);
+ pr_debug("%s: attempt to write beyond end of device\n",
+ __func__);
return -EINVAL;
}
@@ -2401,10 +2422,12 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
if (page == chip->pagebuf)
chip->pagebuf = -1;
- memset(chip->oob_poi, 0xff, mtd->oobsize);
- nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);
- status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
- memset(chip->oob_poi, 0xff, mtd->oobsize);
+ nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops);
+
+ if (ops->mode == MTD_OPS_RAW)
+ status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask);
+ else
+ status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
if (status)
return status;
@@ -2416,9 +2439,9 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
/**
* nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
*/
static int nand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
@@ -2430,17 +2453,17 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
/* Do not allow writes past end of device */
if (ops->datbuf && (to + ops->len) > mtd->size) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond "
- "end of device\n", __func__);
+ pr_debug("%s: attempt to write beyond end of device\n",
+ __func__);
return -EINVAL;
}
nand_get_device(chip, mtd, FL_WRITING);
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
break;
default:
@@ -2458,11 +2481,11 @@ out:
}
/**
- * single_erease_cmd - [GENERIC] NAND standard block erase command function
- * @mtd: MTD device structure
- * @page: the page address of the block which will be erased
+ * single_erase_cmd - [GENERIC] NAND standard block erase command function
+ * @mtd: MTD device structure
+ * @page: the page address of the block which will be erased
*
- * Standard erase command for NAND chips
+ * Standard erase command for NAND chips.
*/
static void single_erase_cmd(struct mtd_info *mtd, int page)
{
@@ -2473,12 +2496,11 @@ static void single_erase_cmd(struct mtd_info *mtd, int page)
}
/**
- * multi_erease_cmd - [GENERIC] AND specific block erase command function
- * @mtd: MTD device structure
- * @page: the page address of the block which will be erased
+ * multi_erase_cmd - [GENERIC] AND specific block erase command function
+ * @mtd: MTD device structure
+ * @page: the page address of the block which will be erased
*
- * AND multi block erase command function
- * Erase 4 consecutive blocks
+ * AND multi block erase command function. Erase 4 consecutive blocks.
*/
static void multi_erase_cmd(struct mtd_info *mtd, int page)
{
@@ -2493,10 +2515,10 @@ static void multi_erase_cmd(struct mtd_info *mtd, int page)
/**
* nand_erase - [MTD Interface] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
+ * @mtd: MTD device structure
+ * @instr: erase instruction
*
- * Erase one ore more blocks
+ * Erase one ore more blocks.
*/
static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
{
@@ -2505,12 +2527,12 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
#define BBT_PAGE_MASK 0xffffff3f
/**
- * nand_erase_nand - [Internal] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
- * @allowbbt: allow erasing the bbt area
+ * nand_erase_nand - [INTERN] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ * @allowbbt: allow erasing the bbt area
*
- * Erase one ore more blocks
+ * Erase one ore more blocks.
*/
int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
int allowbbt)
@@ -2521,9 +2543,9 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
unsigned int bbt_masked_page = 0xffffffff;
loff_t len;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n",
- __func__, (unsigned long long)instr->addr,
- (unsigned long long)instr->len);
+ pr_debug("%s: start = 0x%012llx, len = %llu\n",
+ __func__, (unsigned long long)instr->addr,
+ (unsigned long long)instr->len);
if (check_offs_len(mtd, instr->addr, instr->len))
return -EINVAL;
@@ -2545,8 +2567,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/* Check, if it is write protected */
if (nand_check_wp(mtd)) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n",
- __func__);
+ pr_debug("%s: device is write protected!\n",
+ __func__);
instr->state = MTD_ERASE_FAILED;
goto erase_exit;
}
@@ -2555,7 +2577,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
* If BBT requires refresh, set the BBT page mask to see if the BBT
* should be rewritten. Otherwise the mask is set to 0xffffffff which
* can not be matched. This is also done when the bbt is actually
- * erased to avoid recusrsive updates
+ * erased to avoid recursive updates.
*/
if (chip->options & BBT_AUTO_REFRESH && !allowbbt)
bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
@@ -2566,20 +2588,18 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
instr->state = MTD_ERASING;
while (len) {
- /*
- * heck if we have a bad block, we do not erase bad blocks !
- */
+ /* Heck if we have a bad block, we do not erase bad blocks! */
if (nand_block_checkbad(mtd, ((loff_t) page) <<
chip->page_shift, 0, allowbbt)) {
- printk(KERN_WARNING "%s: attempt to erase a bad block "
- "at page 0x%08x\n", __func__, page);
+ pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
+ __func__, page);
instr->state = MTD_ERASE_FAILED;
goto erase_exit;
}
/*
* Invalidate the page cache, if we erase the block which
- * contains the current cached page
+ * contains the current cached page.
*/
if (page <= chip->pagebuf && chip->pagebuf <
(page + pages_per_block))
@@ -2599,8 +2619,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/* See if block erase succeeded */
if (status & NAND_STATUS_FAIL) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, "
- "page 0x%08x\n", __func__, page);
+ pr_debug("%s: failed erase, page 0x%08x\n",
+ __func__, page);
instr->state = MTD_ERASE_FAILED;
instr->fail_addr =
((loff_t)page << chip->page_shift);
@@ -2609,7 +2629,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/*
* If BBT requires refresh, set the BBT rewrite flag to the
- * page being erased
+ * page being erased.
*/
if (bbt_masked_page != 0xffffffff &&
(page & BBT_PAGE_MASK) == bbt_masked_page)
@@ -2628,7 +2648,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/*
* If BBT requires refresh and BBT-PERCHIP, set the BBT
- * page mask to see if this BBT should be rewritten
+ * page mask to see if this BBT should be rewritten.
*/
if (bbt_masked_page != 0xffffffff &&
(chip->bbt_td->options & NAND_BBT_PERCHIP))
@@ -2651,7 +2671,7 @@ erase_exit:
/*
* If BBT requires refresh and erase was successful, rewrite any
- * selected bad block tables
+ * selected bad block tables.
*/
if (bbt_masked_page == 0xffffffff || ret)
return ret;
@@ -2659,10 +2679,10 @@ erase_exit:
for (chipnr = 0; chipnr < chip->numchips; chipnr++) {
if (!rewrite_bbt[chipnr])
continue;
- /* update the BBT for chip */
- DEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt "
- "(%d:0x%0llx 0x%0x)\n", __func__, chipnr,
- rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]);
+ /* Update the BBT for chip */
+ pr_debug("%s: nand_update_bbt (%d:0x%0llx 0x%0x)\n",
+ __func__, chipnr, rewrite_bbt[chipnr],
+ chip->bbt_td->pages[chipnr]);
nand_update_bbt(mtd, rewrite_bbt[chipnr]);
}
@@ -2672,15 +2692,15 @@ erase_exit:
/**
* nand_sync - [MTD Interface] sync
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*
- * Sync is actually a wait for chip ready function
+ * Sync is actually a wait for chip ready function.
*/
static void nand_sync(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__);
+ pr_debug("%s: called\n", __func__);
/* Grab the lock and see if the device is available */
nand_get_device(chip, mtd, FL_SYNCING);
@@ -2690,8 +2710,8 @@ static void nand_sync(struct mtd_info *mtd)
/**
* nand_block_isbad - [MTD Interface] Check if block at offset is bad
- * @mtd: MTD device structure
- * @offs: offset relative to mtd start
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
*/
static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
{
@@ -2704,8 +2724,8 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
/**
* nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
- * @mtd: MTD device structure
- * @ofs: offset relative to mtd start
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
*/
static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
@@ -2714,7 +2734,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
ret = nand_block_isbad(mtd, ofs);
if (ret) {
- /* If it was bad already, return success and do nothing. */
+ /* If it was bad already, return success and do nothing */
if (ret > 0)
return 0;
return ret;
@@ -2725,7 +2745,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
/**
* nand_suspend - [MTD Interface] Suspend the NAND flash
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*/
static int nand_suspend(struct mtd_info *mtd)
{
@@ -2736,7 +2756,7 @@ static int nand_suspend(struct mtd_info *mtd)
/**
* nand_resume - [MTD Interface] Resume the NAND flash
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*/
static void nand_resume(struct mtd_info *mtd)
{
@@ -2745,13 +2765,11 @@ static void nand_resume(struct mtd_info *mtd)
if (chip->state == FL_PM_SUSPENDED)
nand_release_device(mtd);
else
- printk(KERN_ERR "%s called for a chip which is not "
- "in suspended state\n", __func__);
+ pr_err("%s called for a chip which is not in suspended state\n",
+ __func__);
}
-/*
- * Set default functions
- */
+/* Set default functions */
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
/* check for proper chip_delay setup, set 20us if not */
@@ -2793,23 +2811,21 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
}
-/*
- * sanitize ONFI strings so we can safely print them
- */
+/* Sanitize ONFI strings so we can safely print them */
static void sanitize_string(uint8_t *s, size_t len)
{
ssize_t i;
- /* null terminate */
+ /* Null terminate */
s[len - 1] = 0;
- /* remove non printable chars */
+ /* Remove non printable chars */
for (i = 0; i < len - 1; i++) {
if (s[i] < ' ' || s[i] > 127)
s[i] = '?';
}
- /* remove trailing spaces */
+ /* Remove trailing spaces */
strim(s);
}
@@ -2826,28 +2842,28 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
}
/*
- * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
*/
static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
- int busw)
+ int *busw)
{
struct nand_onfi_params *p = &chip->onfi_params;
int i;
int val;
- /* try ONFI for unknow chip or LP */
+ /* Try ONFI for unknown chip or LP */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
return 0;
- printk(KERN_INFO "ONFI flash detected\n");
+ pr_info("ONFI flash detected\n");
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
for (i = 0; i < 3; i++) {
chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
le16_to_cpu(p->crc)) {
- printk(KERN_INFO "ONFI param page %d valid\n", i);
+ pr_info("ONFI param page %d valid\n", i);
break;
}
}
@@ -2855,7 +2871,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
if (i == 3)
return 0;
- /* check version */
+ /* Check version */
val = le16_to_cpu(p->revision);
if (val & (1 << 5))
chip->onfi_version = 23;
@@ -2871,8 +2887,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
chip->onfi_version = 0;
if (!chip->onfi_version) {
- printk(KERN_INFO "%s: unsupported ONFI version: %d\n",
- __func__, val);
+ pr_info("%s: unsupported ONFI version: %d\n", __func__, val);
return 0;
}
@@ -2884,9 +2899,9 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
- busw = 0;
+ *busw = 0;
if (le16_to_cpu(p->features) & 1)
- busw = NAND_BUSWIDTH_16;
+ *busw = NAND_BUSWIDTH_16;
chip->options &= ~NAND_CHIPOPTIONS_MSK;
chip->options |= (NAND_NO_READRDY |
@@ -2896,7 +2911,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
}
/*
- * Get the flash and manufacturer id and lookup if the type is supported
+ * Get the flash and manufacturer id and lookup if the type is supported.
*/
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
@@ -2913,7 +2928,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
- * after power-up
+ * after power-up.
*/
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
@@ -2924,7 +2939,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd);
- /* Try again to make sure, as some systems the bus-hold or other
+ /*
+ * Try again to make sure, as some systems the bus-hold or other
* interface concerns can cause random data which looks like a
* possibly credible NAND flash to appear. If the two results do
* not match, ignore the device completely.
@@ -2936,9 +2952,9 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
id_data[i] = chip->read_byte(mtd);
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
- printk(KERN_INFO "%s: second ID read did not match "
- "%02x,%02x against %02x,%02x\n", __func__,
- *maf_id, *dev_id, id_data[0], id_data[1]);
+ pr_info("%s: second ID read did not match "
+ "%02x,%02x against %02x,%02x\n", __func__,
+ *maf_id, *dev_id, id_data[0], id_data[1]);
return ERR_PTR(-ENODEV);
}
@@ -2952,7 +2968,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->onfi_version = 0;
if (!type->name || !type->pagesize) {
/* Check is chip is ONFI compliant */
- ret = nand_flash_detect_onfi(mtd, chip, busw);
+ ret = nand_flash_detect_onfi(mtd, chip, &busw);
if (ret)
goto ident_done;
}
@@ -2973,7 +2989,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->chipsize = (uint64_t)type->chipsize << 20;
if (!type->pagesize && chip->init_size) {
- /* set the pagesize, oobsize, erasesize by the driver*/
+ /* Set the pagesize, oobsize, erasesize by the driver */
busw = chip->init_size(mtd, chip, id_data);
} else if (!type->pagesize) {
int extid;
@@ -3033,7 +3049,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
}
} else {
/*
- * Old devices have chip data hardcoded in the device id table
+ * Old devices have chip data hardcoded in the device id table.
*/
mtd->erasesize = type->erasesize;
mtd->writesize = type->pagesize;
@@ -3043,7 +3059,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
/*
* Check for Spansion/AMD ID + repeating 5th, 6th byte since
* some Spansion chips have erasesize that conflicts with size
- * listed in nand_ids table
+ * listed in nand_ids table.
* Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
*/
if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 &&
@@ -3057,15 +3073,16 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->options &= ~NAND_CHIPOPTIONS_MSK;
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
- /* Check if chip is a not a samsung device. Do not clear the
- * options for chips which are not having an extended id.
+ /*
+ * Check if chip is not a Samsung device. Do not clear the
+ * options for chips which do not have an extended id.
*/
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
ident_done:
/*
- * Set chip as a default. Board drivers can override it, if necessary
+ * Set chip as a default. Board drivers can override it, if necessary.
*/
chip->options |= NAND_NO_AUTOINCR;
@@ -3077,21 +3094,21 @@ ident_done:
/*
* Check, if buswidth is correct. Hardware drivers should set
- * chip correct !
+ * chip correct!
*/
if (busw != (chip->options & NAND_BUSWIDTH_16)) {
- printk(KERN_INFO "NAND device: Manufacturer ID:"
- " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
- *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
- printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
- (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
- busw ? 16 : 8);
+ pr_info("NAND device: Manufacturer ID:"
+ " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
+ *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
+ pr_warn("NAND bus width %d instead %d bit\n",
+ (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
+ busw ? 16 : 8);
return ERR_PTR(-EINVAL);
}
/* Calculate the address shift from the page size */
chip->page_shift = ffs(mtd->writesize) - 1;
- /* Convert chipsize to number of pages per chip -1. */
+ /* Convert chipsize to number of pages per chip -1 */
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
chip->bbt_erase_shift = chip->phys_erase_shift =
@@ -3121,7 +3138,7 @@ ident_done:
if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
(*maf_id == NAND_MFR_SAMSUNG ||
*maf_id == NAND_MFR_HYNIX))
- chip->options |= NAND_BBT_SCANLASTPAGE;
+ chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
(*maf_id == NAND_MFR_SAMSUNG ||
*maf_id == NAND_MFR_HYNIX ||
@@ -3129,17 +3146,7 @@ ident_done:
*maf_id == NAND_MFR_AMD)) ||
(mtd->writesize == 2048 &&
*maf_id == NAND_MFR_MICRON))
- chip->options |= NAND_BBT_SCAN2NDPAGE;
-
- /*
- * Numonyx/ST 2K pages, x8 bus use BOTH byte 1 and 6
- */
- if (!(busw & NAND_BUSWIDTH_16) &&
- *maf_id == NAND_MFR_STMICRO &&
- mtd->writesize == 2048) {
- chip->options |= NAND_BBT_SCANBYTE1AND6;
- chip->badblockpos = 0;
- }
+ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
/* Check for AND chips with 4 page planes */
if (chip->options & NAND_4PAGE_ARRAY)
@@ -3147,12 +3154,11 @@ ident_done:
else
chip->erase_cmd = single_erase_cmd;
- /* Do not replace user supplied command function ! */
+ /* Do not replace user supplied command function! */
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp;
- /* TODO onfi flash name */
- printk(KERN_INFO "NAND device: Manufacturer ID:"
+ pr_info("NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
nand_manuf_ids[maf_idx].name,
chip->onfi_version ? chip->onfi_params.model : type->name);
@@ -3162,12 +3168,12 @@ ident_done:
/**
* nand_scan_ident - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- * @maxchips: Number of chips to scan for
- * @table: Alternative NAND ID table
+ * @mtd: MTD device structure
+ * @maxchips: number of chips to scan for
+ * @table: alternative NAND ID table
*
- * This is the first phase of the normal nand_scan() function. It
- * reads the flash ID and sets up MTD fields accordingly.
+ * This is the first phase of the normal nand_scan() function. It reads the
+ * flash ID and sets up MTD fields accordingly.
*
* The mtd->owner field must be set to the module of the caller.
*/
@@ -3189,7 +3195,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
if (IS_ERR(type)) {
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
- printk(KERN_WARNING "No NAND device found.\n");
+ pr_warn("No NAND device found\n");
chip->select_chip(mtd, -1);
return PTR_ERR(type);
}
@@ -3207,7 +3213,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
break;
}
if (i > 1)
- printk(KERN_INFO "%d NAND chips detected\n", i);
+ pr_info("%d NAND chips detected\n", i);
/* Store the number of chips and calc total size for mtd */
chip->numchips = i;
@@ -3220,11 +3226,11 @@ EXPORT_SYMBOL(nand_scan_ident);
/**
* nand_scan_tail - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*
- * This is the second phase of the normal nand_scan() function. It
- * fills out all the uninitialized function pointers with the defaults
- * and scans for a bad block table if appropriate.
+ * This is the second phase of the normal nand_scan() function. It fills out
+ * all the uninitialized function pointers with the defaults and scans for a
+ * bad block table if appropriate.
*/
int nand_scan_tail(struct mtd_info *mtd)
{
@@ -3240,7 +3246,7 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
/*
- * If no default placement scheme is given, select an appropriate one
+ * If no default placement scheme is given, select an appropriate one.
*/
if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) {
switch (mtd->oobsize) {
@@ -3257,8 +3263,8 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.layout = &nand_oob_128;
break;
default:
- printk(KERN_WARNING "No oob scheme defined for "
- "oobsize %d\n", mtd->oobsize);
+ pr_warn("No oob scheme defined for oobsize %d\n",
+ mtd->oobsize);
BUG();
}
}
@@ -3267,7 +3273,7 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->write_page = nand_write_page;
/*
- * check ECC mode, default to software if 3byte/512byte hardware ECC is
+ * Check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
*/
@@ -3276,15 +3282,15 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Similar to NAND_ECC_HW, but a separate read_page handle */
if (!chip->ecc.calculate || !chip->ecc.correct ||
!chip->ecc.hwctl) {
- printk(KERN_WARNING "No ECC functions supplied; "
- "Hardware ECC not possible\n");
+ pr_warn("No ECC functions supplied; "
+ "hardware ECC not possible\n");
BUG();
}
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
case NAND_ECC_HW:
- /* Use standard hwecc read page function ? */
+ /* Use standard hwecc read page function? */
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_hwecc;
if (!chip->ecc.write_page)
@@ -3305,11 +3311,11 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.read_page == nand_read_page_hwecc ||
!chip->ecc.write_page ||
chip->ecc.write_page == nand_write_page_hwecc)) {
- printk(KERN_WARNING "No ECC functions supplied; "
- "Hardware ECC not possible\n");
+ pr_warn("No ECC functions supplied; "
+ "hardware ECC not possible\n");
BUG();
}
- /* Use standard syndrome read/write page function ? */
+ /* Use standard syndrome read/write page function? */
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_syndrome;
if (!chip->ecc.write_page)
@@ -3325,9 +3331,9 @@ int nand_scan_tail(struct mtd_info *mtd)
if (mtd->writesize >= chip->ecc.size)
break;
- printk(KERN_WARNING "%d byte HW ECC not possible on "
- "%d byte page size, fallback to SW ECC\n",
- chip->ecc.size, mtd->writesize);
+ pr_warn("%d byte HW ECC not possible on "
+ "%d byte page size, fallback to SW ECC\n",
+ chip->ecc.size, mtd->writesize);
chip->ecc.mode = NAND_ECC_SOFT;
case NAND_ECC_SOFT:
@@ -3347,7 +3353,7 @@ int nand_scan_tail(struct mtd_info *mtd)
case NAND_ECC_SOFT_BCH:
if (!mtd_nand_has_bch()) {
- printk(KERN_WARNING "CONFIG_MTD_ECC_BCH not enabled\n");
+ pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");
BUG();
}
chip->ecc.calculate = nand_bch_calculate_ecc;
@@ -3362,8 +3368,8 @@ int nand_scan_tail(struct mtd_info *mtd)
/*
* Board driver should supply ecc.size and ecc.bytes values to
* select how many bits are correctable; see nand_bch_init()
- * for details.
- * Otherwise, default to 4 bits for large page devices
+ * for details. Otherwise, default to 4 bits for large page
+ * devices.
*/
if (!chip->ecc.size && (mtd->oobsize >= 64)) {
chip->ecc.size = 512;
@@ -3374,14 +3380,14 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.bytes,
&chip->ecc.layout);
if (!chip->ecc.priv) {
- printk(KERN_WARNING "BCH ECC initialization failed!\n");
+ pr_warn("BCH ECC initialization failed!\n");
BUG();
}
break;
case NAND_ECC_NONE:
- printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
- "This is not recommended !!\n");
+ pr_warn("NAND_ECC_NONE selected by board driver. "
+ "This is not recommended!\n");
chip->ecc.read_page = nand_read_page_raw;
chip->ecc.write_page = nand_write_page_raw;
chip->ecc.read_oob = nand_read_oob_std;
@@ -3393,14 +3399,19 @@ int nand_scan_tail(struct mtd_info *mtd)
break;
default:
- printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
- chip->ecc.mode);
+ pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode);
BUG();
}
+ /* For many systems, the standard OOB write also works for raw */
+ if (!chip->ecc.read_oob_raw)
+ chip->ecc.read_oob_raw = chip->ecc.read_oob;
+ if (!chip->ecc.write_oob_raw)
+ chip->ecc.write_oob_raw = chip->ecc.write_oob;
+
/*
* The number of bytes available for a client to place data into
- * the out of band area
+ * the out of band area.
*/
chip->ecc.layout->oobavail = 0;
for (i = 0; chip->ecc.layout->oobfree[i].length
@@ -3411,19 +3422,16 @@ int nand_scan_tail(struct mtd_info *mtd)
/*
* Set the number of read / write steps for one page depending on ECC
- * mode
+ * mode.
*/
chip->ecc.steps = mtd->writesize / chip->ecc.size;
if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
- printk(KERN_WARNING "Invalid ecc parameters\n");
+ pr_warn("Invalid ECC parameters\n");
BUG();
}
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
- /*
- * Allow subpage writes up to ecc.steps. Not possible for MLC
- * FLASH.
- */
+ /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
!(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
switch (chip->ecc.steps) {
@@ -3481,9 +3489,11 @@ int nand_scan_tail(struct mtd_info *mtd)
}
EXPORT_SYMBOL(nand_scan_tail);
-/* is_module_text_address() isn't exported, and it's mostly a pointless
+/*
+ * is_module_text_address() isn't exported, and it's mostly a pointless
* test if this is a module _anyway_ -- they'd have to try _really_ hard
- * to call us from in-kernel code if the core NAND support is modular. */
+ * to call us from in-kernel code if the core NAND support is modular.
+ */
#ifdef MODULE
#define caller_is_module() (1)
#else
@@ -3493,15 +3503,13 @@ EXPORT_SYMBOL(nand_scan_tail);
/**
* nand_scan - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- * @maxchips: Number of chips to scan for
- *
- * This fills out all the uninitialized function pointers
- * with the defaults.
- * The flash ID is read and the mtd/chip structures are
- * filled with the appropriate values.
- * The mtd->owner field must be set to the module of the caller
+ * @mtd: MTD device structure
+ * @maxchips: number of chips to scan for
*
+ * This fills out all the uninitialized function pointers with the defaults.
+ * The flash ID is read and the mtd/chip structures are filled with the
+ * appropriate values. The mtd->owner field must be set to the module of the
+ * caller.
*/
int nand_scan(struct mtd_info *mtd, int maxchips)
{
@@ -3509,8 +3517,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
/* Many callers got this wrong, so check for it for a while... */
if (!mtd->owner && caller_is_module()) {
- printk(KERN_CRIT "%s called with NULL mtd->owner!\n",
- __func__);
+ pr_crit("%s called with NULL mtd->owner!\n", __func__);
BUG();
}
@@ -3523,8 +3530,8 @@ EXPORT_SYMBOL(nand_scan);
/**
* nand_release - [NAND Interface] Free resources held by the NAND device
- * @mtd: MTD device structure
-*/
+ * @mtd: MTD device structure
+ */
void nand_release(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index ccbeaa1e4a8e..69148ae3bf58 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -14,7 +14,7 @@
*
* When nand_scan_bbt is called, then it tries to find the bad block table
* depending on the options in the BBT descriptor(s). If no flash based BBT
- * (NAND_USE_FLASH_BBT) is specified then the device is scanned for factory
+ * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory
* marked good / bad blocks. This information is used to create a memory BBT.
* Once a new bad block is discovered then the "factory" information is updated
* on the device.
@@ -36,9 +36,9 @@
* The table is marked in the OOB area with an ident pattern and a version
* number which indicates which of both tables is more up to date. If the NAND
* controller needs the complete OOB area for the ECC information then the
- * option NAND_USE_FLASH_BBT_NO_OOB should be used: it moves the ident pattern
- * and the version byte into the data area and the OOB area will remain
- * untouched.
+ * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of
+ * course): it moves the ident pattern and the version byte into the data area
+ * and the OOB area will remain untouched.
*
* The table uses 2 bits per block
* 11b: block is good
@@ -67,6 +67,7 @@
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
{
@@ -80,17 +81,15 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
/**
* check_pattern - [GENERIC] check if a pattern is in the buffer
- * @buf: the buffer to search
- * @len: the length of buffer to search
- * @paglen: the pagelength
- * @td: search pattern descriptor
+ * @buf: the buffer to search
+ * @len: the length of buffer to search
+ * @paglen: the pagelength
+ * @td: search pattern descriptor
*
- * Check for a pattern at the given place. Used to search bad block
- * tables and good / bad block identifiers.
- * If the SCAN_EMPTY option is set then check, if all bytes except the
- * pattern area contain 0xff
- *
-*/
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if
+ * all bytes except the pattern area contain 0xff.
+ */
static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
{
int i, end = 0;
@@ -109,32 +108,8 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
p += end;
/* Compare the pattern */
- for (i = 0; i < td->len; i++) {
- if (p[i] != td->pattern[i])
- return -1;
- }
-
- /* Check both positions 1 and 6 for pattern? */
- if (td->options & NAND_BBT_SCANBYTE1AND6) {
- if (td->options & NAND_BBT_SCANEMPTY) {
- p += td->len;
- end += NAND_SMALL_BADBLOCK_POS - td->offs;
- /* Check region between positions 1 and 6 */
- for (i = 0; i < NAND_SMALL_BADBLOCK_POS - td->offs - td->len;
- i++) {
- if (*p++ != 0xff)
- return -1;
- }
- }
- else {
- p += NAND_SMALL_BADBLOCK_POS - td->offs;
- }
- /* Compare the pattern */
- for (i = 0; i < td->len; i++) {
- if (p[i] != td->pattern[i])
- return -1;
- }
- }
+ if (memcmp(p, td->pattern, td->len))
+ return -1;
if (td->options & NAND_BBT_SCANEMPTY) {
p += td->len;
@@ -149,14 +124,13 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
- * @buf: the buffer to search
- * @td: search pattern descriptor
- *
- * Check for a pattern at the given place. Used to search bad block
- * tables and good / bad block identifiers. Same as check_pattern, but
- * no optional empty check
+ * @buf: the buffer to search
+ * @td: search pattern descriptor
*
-*/
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers. Same as check_pattern, but no optional empty
+ * check.
+ */
static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
{
int i;
@@ -167,21 +141,14 @@ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
if (p[td->offs + i] != td->pattern[i])
return -1;
}
- /* Need to check location 1 AND 6? */
- if (td->options & NAND_BBT_SCANBYTE1AND6) {
- for (i = 0; i < td->len; i++) {
- if (p[NAND_SMALL_BADBLOCK_POS + i] != td->pattern[i])
- return -1;
- }
- }
return 0;
}
/**
* add_marker_len - compute the length of the marker in data area
- * @td: BBT descriptor used for computation
+ * @td: BBT descriptor used for computation
*
- * The length will be 0 if the markeris located in OOB area.
+ * The length will be 0 if the marker is located in OOB area.
*/
static u32 add_marker_len(struct nand_bbt_descr *td)
{
@@ -198,34 +165,33 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
/**
* read_bbt - [GENERIC] Read the bad block table starting from page
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @page: the starting page
- * @num: the number of bbt descriptors to read
- * @td: the bbt describtion table
- * @offs: offset in the memory table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @page: the starting page
+ * @num: the number of bbt descriptors to read
+ * @td: the bbt describtion table
+ * @offs: offset in the memory table
*
* Read the bad block table starting from page.
- *
*/
static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
struct nand_bbt_descr *td, int offs)
{
- int res, i, j, act = 0;
+ int res, ret = 0, i, j, act = 0;
struct nand_chip *this = mtd->priv;
size_t retlen, len, totlen;
loff_t from;
int bits = td->options & NAND_BBT_NRBITS_MSK;
- uint8_t msk = (uint8_t) ((1 << bits) - 1);
+ uint8_t msk = (uint8_t)((1 << bits) - 1);
u32 marker_len;
int reserved_block_code = td->reserved_block_code;
totlen = (num * bits) >> 3;
marker_len = add_marker_len(td);
- from = ((loff_t) page) << this->page_shift;
+ from = ((loff_t)page) << this->page_shift;
while (totlen) {
- len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
+ len = min(totlen, (size_t)(1 << this->bbt_erase_shift));
if (marker_len) {
/*
* In case the BBT marker is not in the OOB area it
@@ -237,11 +203,18 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
}
res = mtd->read(mtd, from, len, &retlen, buf);
if (res < 0) {
- if (retlen != len) {
- printk(KERN_INFO "nand_bbt: Error reading bad block table\n");
+ if (mtd_is_eccerr(res)) {
+ pr_info("nand_bbt: ECC error in BBT at "
+ "0x%012llx\n", from & ~mtd->writesize);
+ return res;
+ } else if (mtd_is_bitflip(res)) {
+ pr_info("nand_bbt: corrected error in BBT at "
+ "0x%012llx\n", from & ~mtd->writesize);
+ ret = res;
+ } else {
+ pr_info("nand_bbt: error reading BBT\n");
return res;
}
- printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
}
/* Analyse data */
@@ -252,17 +225,19 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
if (tmp == msk)
continue;
if (reserved_block_code && (tmp == reserved_block_code)) {
- printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n",
- (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+ pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
+ (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
mtd->ecc_stats.bbtblocks++;
continue;
}
- /* Leave it for now, if its matured we can move this
- * message to MTD_DEBUG_LEVEL0 */
- printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%012llx\n",
- (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
- /* Factory marked bad or worn out ? */
+ /*
+ * Leave it for now, if it's matured we can
+ * move this message to pr_debug.
+ */
+ pr_info("nand_read_bbt: bad block at 0x%012llx\n",
+ (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+ /* Factory marked bad or worn out? */
if (tmp == 0)
this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
else
@@ -273,20 +248,20 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
totlen -= len;
from += len;
}
- return 0;
+ return ret;
}
/**
* read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @chip: read the table for a specific chip, -1 read all chips.
- * Applies only if NAND_BBT_PERCHIP option is set
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @chip: read the table for a specific chip, -1 read all chips; applies only if
+ * NAND_BBT_PERCHIP option is set
*
- * Read the bad block table for all chips starting at a given page
- * We assume that the bbt bits are in consecutive order.
-*/
+ * Read the bad block table for all chips starting at a given page. We assume
+ * that the bbt bits are in consecutive order.
+ */
static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
{
struct nand_chip *this = mtd->priv;
@@ -312,9 +287,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
return 0;
}
-/*
- * BBT marker is in the first page, no OOB.
- */
+/* BBT marker is in the first page, no OOB */
static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
struct nand_bbt_descr *td)
{
@@ -328,35 +301,26 @@ static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
return mtd->read(mtd, offs, len, &retlen, buf);
}
-/*
- * Scan read raw data from flash
- */
+/* Scan read raw data from flash */
static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len)
{
struct mtd_oob_ops ops;
int res;
- ops.mode = MTD_OOB_RAW;
+ ops.mode = MTD_OPS_RAW;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
-
while (len > 0) {
- if (len <= mtd->writesize) {
- ops.oobbuf = buf + len;
- ops.datbuf = buf;
- ops.len = len;
- return mtd->read_oob(mtd, offs, &ops);
- } else {
- ops.oobbuf = buf + mtd->writesize;
- ops.datbuf = buf;
- ops.len = mtd->writesize;
- res = mtd->read_oob(mtd, offs, &ops);
+ ops.datbuf = buf;
+ ops.len = min(len, (size_t)mtd->writesize);
+ ops.oobbuf = buf + ops.len;
- if (res)
- return res;
- }
+ res = mtd->read_oob(mtd, offs, &ops);
+
+ if (res)
+ return res;
buf += mtd->oobsize + mtd->writesize;
len -= mtd->writesize;
@@ -373,15 +337,13 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
return scan_read_raw_oob(mtd, buf, offs, len);
}
-/*
- * Scan write data with oob to flash
- */
+/* Scan write data with oob to flash */
static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
uint8_t *buf, uint8_t *oob)
{
struct mtd_oob_ops ops;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
ops.datbuf = buf;
@@ -402,15 +364,14 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
/**
* read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @md: descriptor for the bad block table mirror
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
*
- * Read the bad block table(s) for all chips starting at a given page
- * We assume that the bbt bits are in consecutive order.
- *
-*/
+ * Read the bad block table(s) for all chips starting at a given page. We
+ * assume that the bbt bits are in consecutive order.
+ */
static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{
@@ -421,8 +382,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
mtd->writesize, td);
td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
- printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
- td->pages[0], td->version[0]);
+ pr_info("Bad block table at page %d, version 0x%02X\n",
+ td->pages[0], td->version[0]);
}
/* Read the mirror version, if available */
@@ -430,15 +391,13 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
mtd->writesize, td);
md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
- printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
- md->pages[0], md->version[0]);
+ pr_info("Bad block table at page %d, version 0x%02X\n",
+ md->pages[0], md->version[0]);
}
return 1;
}
-/*
- * Scan a given block full
- */
+/* Scan a given block full */
static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
loff_t offs, uint8_t *buf, size_t readlen,
int scanlen, int len)
@@ -446,7 +405,8 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
int ret, j;
ret = scan_read_raw_oob(mtd, buf, offs, readlen);
- if (ret)
+ /* Ignore ECC errors when checking for BBM */
+ if (ret && !mtd_is_bitflip_or_eccerr(ret))
return ret;
for (j = 0; j < len; j++, buf += scanlen) {
@@ -456,9 +416,7 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
return 0;
}
-/*
- * Scan a given block partially
- */
+/* Scan a given block partially */
static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
loff_t offs, uint8_t *buf, int len)
{
@@ -469,16 +427,16 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
ops.oobbuf = buf;
ops.ooboffs = 0;
ops.datbuf = NULL;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
for (j = 0; j < len; j++) {
/*
- * Read the full oob until read_oob is fixed to
- * handle single byte reads for 16 bit
- * buswidth
+ * Read the full oob until read_oob is fixed to handle single
+ * byte reads for 16 bit buswidth.
*/
ret = mtd->read_oob(mtd, offs, &ops);
- if (ret)
+ /* Ignore ECC errors when checking for BBM */
+ if (ret && !mtd_is_bitflip_or_eccerr(ret))
return ret;
if (check_short_pattern(buf, bd))
@@ -491,14 +449,14 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
/**
* create_bbt - [GENERIC] Create a bad block table by scanning the device
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @bd: descriptor for the good/bad block search pattern
- * @chip: create the table for a specific chip, -1 read all chips.
- * Applies only if NAND_BBT_PERCHIP option is set
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ * @chip: create the table for a specific chip, -1 read all chips; applies only
+ * if NAND_BBT_PERCHIP option is set
*
- * Create a bad block table by scanning the device
- * for the given good/bad block identify pattern
+ * Create a bad block table by scanning the device for the given good/bad block
+ * identify pattern.
*/
static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *bd, int chip)
@@ -509,7 +467,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
loff_t from;
size_t readlen;
- printk(KERN_INFO "Scanning device for bad blocks\n");
+ pr_info("Scanning device for bad blocks\n");
if (bd->options & NAND_BBT_SCANALLPAGES)
len = 1 << (this->bbt_erase_shift - this->page_shift);
@@ -529,14 +487,16 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
}
if (chip == -1) {
- /* Note that numblocks is 2 * (real numblocks) here, see i+=2
- * below as it makes shifting and masking less painful */
+ /*
+ * Note that numblocks is 2 * (real numblocks) here, see i+=2
+ * below as it makes shifting and masking less painful
+ */
numblocks = mtd->size >> (this->bbt_erase_shift - 1);
startblock = 0;
from = 0;
} else {
if (chip >= this->numchips) {
- printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
+ pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n",
chip + 1, this->numchips);
return -EINVAL;
}
@@ -546,7 +506,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
from = (loff_t)startblock << (this->bbt_erase_shift - 1);
}
- if (this->options & NAND_BBT_SCANLASTPAGE)
+ if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
from += mtd->erasesize - (mtd->writesize * len);
for (i = startblock; i < numblocks;) {
@@ -565,8 +525,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
if (ret) {
this->bbt[i >> 3] |= 0x03 << (i & 0x6);
- printk(KERN_WARNING "Bad eraseblock %d at 0x%012llx\n",
- i >> 1, (unsigned long long)from);
+ pr_warn("Bad eraseblock %d at 0x%012llx\n",
+ i >> 1, (unsigned long long)from);
mtd->ecc_stats.badblocks++;
}
@@ -578,20 +538,18 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
/**
* search_bbt - [GENERIC] scan the device for a specific bad block table
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
*
- * Read the bad block table by searching for a given ident pattern.
- * Search is preformed either from the beginning up or from the end of
- * the device downwards. The search starts always at the start of a
- * block.
- * If the option NAND_BBT_PERCHIP is given, each chip is searched
- * for a bbt, which contains the bad block information of this chip.
- * This is necessary to provide support for certain DOC devices.
+ * Read the bad block table by searching for a given ident pattern. Search is
+ * preformed either from the beginning up or from the end of the device
+ * downwards. The search starts always at the start of a block. If the option
+ * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains
+ * the bad block information of this chip. This is necessary to provide support
+ * for certain DOC devices.
*
- * The bbt ident pattern resides in the oob area of the first page
- * in a block.
+ * The bbt ident pattern resides in the oob area of the first page in a block.
*/
static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
{
@@ -602,7 +560,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
int bbtblocks;
int blocktopage = this->bbt_erase_shift - this->page_shift;
- /* Search direction top -> down ? */
+ /* Search direction top -> down? */
if (td->options & NAND_BBT_LASTBLOCK) {
startblock = (mtd->size >> this->bbt_erase_shift) - 1;
dir = -1;
@@ -611,7 +569,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
dir = 1;
}
- /* Do we have a bbt per chip ? */
+ /* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP) {
chips = this->numchips;
bbtblocks = this->chipsize >> this->bbt_erase_shift;
@@ -650,23 +608,23 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
/* Check, if we found a bbt for each requested chip */
for (i = 0; i < chips; i++) {
if (td->pages[i] == -1)
- printk(KERN_WARNING "Bad block table not found for chip %d\n", i);
+ pr_warn("Bad block table not found for chip %d\n", i);
else
- printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i],
- td->version[i]);
+ pr_info("Bad block table found at page %d, version "
+ "0x%02X\n", td->pages[i], td->version[i]);
}
return 0;
}
/**
* search_read_bbts - [GENERIC] scan the device for bad block table(s)
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @md: descriptor for the bad block table mirror
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
*
- * Search and read the bad block table(s)
-*/
+ * Search and read the bad block table(s).
+ */
static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{
/* Search the primary table */
@@ -682,16 +640,14 @@ static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt
/**
* write_bbt - [GENERIC] (Re)write the bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ * @chipsel: selector for a specific chip, -1 for all
*
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @md: descriptor for the bad block table mirror
- * @chipsel: selector for a specific chip, -1 for all
- *
- * (Re)write the bad block table
- *
-*/
+ * (Re)write the bad block table.
+ */
static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md,
int chipsel)
@@ -710,14 +666,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
ops.ooblen = mtd->oobsize;
ops.ooboffs = 0;
ops.datbuf = NULL;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
if (!rcode)
rcode = 0xff;
- /* Write bad block table per chip rather than per device ? */
+ /* Write bad block table per chip rather than per device? */
if (td->options & NAND_BBT_PERCHIP) {
numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
- /* Full device write or specific chip ? */
+ /* Full device write or specific chip? */
if (chipsel == -1) {
nrchips = this->numchips;
} else {
@@ -731,8 +687,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
/* Loop through the chips */
for (; chip < nrchips; chip++) {
-
- /* There was already a version of the table, reuse the page
+ /*
+ * There was already a version of the table, reuse the page
* This applies for absolute placement too, as we have the
* page nr. in td->pages.
*/
@@ -741,8 +697,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
goto write;
}
- /* Automatic placement of the bad block table */
- /* Search direction top -> down ? */
+ /*
+ * Automatic placement of the bad block table. Search direction
+ * top -> down?
+ */
if (td->options & NAND_BBT_LASTBLOCK) {
startblock = numblocks * (chip + 1) - 1;
dir = -1;
@@ -766,7 +724,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
if (!md || md->pages[chip] != page)
goto write;
}
- printk(KERN_ERR "No space left to write bad block table\n");
+ pr_err("No space left to write bad block table\n");
return -ENOSPC;
write:
@@ -791,24 +749,22 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
bbtoffs = chip * (numblocks >> 2);
- to = ((loff_t) page) << this->page_shift;
+ to = ((loff_t)page) << this->page_shift;
- /* Must we save the block contents ? */
+ /* Must we save the block contents? */
if (td->options & NAND_BBT_SAVECONTENT) {
/* Make it block aligned */
- to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
+ to &= ~((loff_t)((1 << this->bbt_erase_shift) - 1));
len = 1 << this->bbt_erase_shift;
res = mtd->read(mtd, to, len, &retlen, buf);
if (res < 0) {
if (retlen != len) {
- printk(KERN_INFO "nand_bbt: Error "
- "reading block for writing "
- "the bad block table\n");
+ pr_info("nand_bbt: error reading block "
+ "for writing the bad block table\n");
return res;
}
- printk(KERN_WARNING "nand_bbt: ECC error "
- "while reading block for writing "
- "bad block table\n");
+ pr_warn("nand_bbt: ECC error while reading "
+ "block for writing bad block table\n");
}
/* Read oob data */
ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
@@ -821,19 +777,19 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
pageoffs = page - (int)(to >> this->page_shift);
offs = pageoffs << this->page_shift;
/* Preset the bbt area with 0xff */
- memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
+ memset(&buf[offs], 0xff, (size_t)(numblocks >> sft));
ooboffs = len + (pageoffs * mtd->oobsize);
} else if (td->options & NAND_BBT_NO_OOB) {
ooboffs = 0;
offs = td->len;
- /* the version byte */
+ /* The version byte */
if (td->options & NAND_BBT_VERSION)
offs++;
/* Calc length */
- len = (size_t) (numblocks >> sft);
+ len = (size_t)(numblocks >> sft);
len += offs;
- /* Make it page aligned ! */
+ /* Make it page aligned! */
len = ALIGN(len, mtd->writesize);
/* Preset the buffer with 0xff */
memset(buf, 0xff, len);
@@ -841,8 +797,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
memcpy(buf, td->pattern, td->len);
} else {
/* Calc length */
- len = (size_t) (numblocks >> sft);
- /* Make it page aligned ! */
+ len = (size_t)(numblocks >> sft);
+ /* Make it page aligned! */
len = ALIGN(len, mtd->writesize);
/* Preset the buffer with 0xff */
memset(buf, 0xff, len +
@@ -856,13 +812,13 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
if (td->options & NAND_BBT_VERSION)
buf[ooboffs + td->veroffs] = td->version[chip];
- /* walk through the memory table */
+ /* Walk through the memory table */
for (i = 0; i < numblocks;) {
uint8_t dat;
dat = this->bbt[bbtoffs + (i >> 2)];
for (j = 0; j < 4; j++, i++) {
int sftcnt = (i << (3 - sft)) & sftmsk;
- /* Do not store the reserved bbt blocks ! */
+ /* Do not store the reserved bbt blocks! */
buf[offs + (i >> sft)] &=
~(msk[dat & 0x03] << sftcnt);
dat >>= 2;
@@ -883,8 +839,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
if (res < 0)
goto outerr;
- printk(KERN_DEBUG "Bad block table written to 0x%012llx, version "
- "0x%02X\n", (unsigned long long)to, td->version[chip]);
+ pr_info("Bad block table written to 0x%012llx, version 0x%02X\n",
+ (unsigned long long)to, td->version[chip]);
/* Mark it as used */
td->pages[chip] = page;
@@ -892,19 +848,18 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
return 0;
outerr:
- printk(KERN_WARNING
- "nand_bbt: Error while writing bad block table %d\n", res);
+ pr_warn("nand_bbt: error while writing bad block table %d\n", res);
return res;
}
/**
* nand_memory_bbt - [GENERIC] create a memory based bad block table
- * @mtd: MTD device structure
- * @bd: descriptor for the good/bad block search pattern
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
*
- * The function creates a memory based bbt by scanning the device
- * for manufacturer / software marked good / bad blocks
-*/
+ * The function creates a memory based bbt by scanning the device for
+ * manufacturer / software marked good / bad blocks.
+ */
static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
@@ -915,25 +870,24 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
/**
* check_create - [GENERIC] create and write bbt(s) if necessary
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @bd: descriptor for the good/bad block search pattern
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
*
- * The function checks the results of the previous call to read_bbt
- * and creates / updates the bbt(s) if necessary
- * Creation is necessary if no bbt was found for the chip/device
- * Update is necessary if one of the tables is missing or the
- * version nr. of one table is less than the other
-*/
+ * The function checks the results of the previous call to read_bbt and creates
+ * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found
+ * for the chip/device. Update is necessary if one of the tables is missing or
+ * the version nr. of one table is less than the other.
+ */
static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
{
- int i, chips, writeops, chipsel, res;
+ int i, chips, writeops, create, chipsel, res, res2;
struct nand_chip *this = mtd->priv;
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
struct nand_bbt_descr *rd, *rd2;
- /* Do we have a bbt per chip ? */
+ /* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP)
chips = this->numchips;
else
@@ -941,86 +895,98 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
for (i = 0; i < chips; i++) {
writeops = 0;
+ create = 0;
rd = NULL;
rd2 = NULL;
- /* Per chip or per device ? */
+ res = res2 = 0;
+ /* Per chip or per device? */
chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
- /* Mirrored table available ? */
+ /* Mirrored table available? */
if (md) {
if (td->pages[i] == -1 && md->pages[i] == -1) {
+ create = 1;
writeops = 0x03;
- goto create;
- }
-
- if (td->pages[i] == -1) {
+ } else if (td->pages[i] == -1) {
rd = md;
- td->version[i] = md->version[i];
- writeops = 1;
- goto writecheck;
- }
-
- if (md->pages[i] == -1) {
+ writeops = 0x01;
+ } else if (md->pages[i] == -1) {
rd = td;
- md->version[i] = td->version[i];
- writeops = 2;
- goto writecheck;
- }
-
- if (td->version[i] == md->version[i]) {
+ writeops = 0x02;
+ } else if (td->version[i] == md->version[i]) {
rd = td;
if (!(td->options & NAND_BBT_VERSION))
rd2 = md;
- goto writecheck;
- }
-
- if (((int8_t) (td->version[i] - md->version[i])) > 0) {
+ } else if (((int8_t)(td->version[i] - md->version[i])) > 0) {
rd = td;
- md->version[i] = td->version[i];
- writeops = 2;
+ writeops = 0x02;
} else {
rd = md;
- td->version[i] = md->version[i];
- writeops = 1;
+ writeops = 0x01;
}
-
- goto writecheck;
-
} else {
if (td->pages[i] == -1) {
+ create = 1;
writeops = 0x01;
- goto create;
+ } else {
+ rd = td;
}
- rd = td;
- goto writecheck;
}
- create:
- /* Create the bad block table by scanning the device ? */
- if (!(td->options & NAND_BBT_CREATE))
- continue;
- /* Create the table in memory by scanning the chip(s) */
- if (!(this->options & NAND_CREATE_EMPTY_BBT))
- create_bbt(mtd, buf, bd, chipsel);
-
- td->version[i] = 1;
- if (md)
- md->version[i] = 1;
- writecheck:
- /* read back first ? */
- if (rd)
- read_abs_bbt(mtd, buf, rd, chipsel);
- /* If they weren't versioned, read both. */
- if (rd2)
- read_abs_bbt(mtd, buf, rd2, chipsel);
-
- /* Write the bad block table to the device ? */
+ if (create) {
+ /* Create the bad block table by scanning the device? */
+ if (!(td->options & NAND_BBT_CREATE))
+ continue;
+
+ /* Create the table in memory by scanning the chip(s) */
+ if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY))
+ create_bbt(mtd, buf, bd, chipsel);
+
+ td->version[i] = 1;
+ if (md)
+ md->version[i] = 1;
+ }
+
+ /* Read back first? */
+ if (rd) {
+ res = read_abs_bbt(mtd, buf, rd, chipsel);
+ if (mtd_is_eccerr(res)) {
+ /* Mark table as invalid */
+ rd->pages[i] = -1;
+ rd->version[i] = 0;
+ i--;
+ continue;
+ }
+ }
+ /* If they weren't versioned, read both */
+ if (rd2) {
+ res2 = read_abs_bbt(mtd, buf, rd2, chipsel);
+ if (mtd_is_eccerr(res2)) {
+ /* Mark table as invalid */
+ rd2->pages[i] = -1;
+ rd2->version[i] = 0;
+ i--;
+ continue;
+ }
+ }
+
+ /* Scrub the flash table(s)? */
+ if (mtd_is_bitflip(res) || mtd_is_bitflip(res2))
+ writeops = 0x03;
+
+ /* Update version numbers before writing */
+ if (md) {
+ td->version[i] = max(td->version[i], md->version[i]);
+ md->version[i] = td->version[i];
+ }
+
+ /* Write the bad block table to the device? */
if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
res = write_bbt(mtd, buf, td, md, chipsel);
if (res < 0)
return res;
}
- /* Write the mirror bad block table to the device ? */
+ /* Write the mirror bad block table to the device? */
if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
res = write_bbt(mtd, buf, md, td, chipsel);
if (res < 0)
@@ -1032,20 +998,19 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
/**
* mark_bbt_regions - [GENERIC] mark the bad block table regions
- * @mtd: MTD device structure
- * @td: bad block table descriptor
+ * @mtd: MTD device structure
+ * @td: bad block table descriptor
*
- * The bad block table regions are marked as "bad" to prevent
- * accidental erasures / writes. The regions are identified by
- * the mark 0x02.
-*/
+ * The bad block table regions are marked as "bad" to prevent accidental
+ * erasures / writes. The regions are identified by the mark 0x02.
+ */
static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
{
struct nand_chip *this = mtd->priv;
int i, j, chips, block, nrblocks, update;
uint8_t oldval, newval;
- /* Do we have a bbt per chip ? */
+ /* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP) {
chips = this->numchips;
nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
@@ -1082,9 +1047,11 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
update = 1;
block += 2;
}
- /* If we want reserved blocks to be recorded to flash, and some
- new ones have been marked, then we need to update the stored
- bbts. This should only happen once. */
+ /*
+ * If we want reserved blocks to be recorded to flash, and some
+ * new ones have been marked, then we need to update the stored
+ * bbts. This should only happen once.
+ */
if (update && td->reserved_block_code)
nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1));
}
@@ -1092,8 +1059,8 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
/**
* verify_bbt_descr - verify the bad block description
- * @mtd: MTD device structure
- * @bd: the table to verify
+ * @mtd: MTD device structure
+ * @bd: the table to verify
*
* This functions performs a few sanity checks on the bad block description
* table.
@@ -1111,16 +1078,16 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
pattern_len = bd->len;
bits = bd->options & NAND_BBT_NRBITS_MSK;
- BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) &&
- !(this->options & NAND_USE_FLASH_BBT));
+ BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) &&
+ !(this->bbt_options & NAND_BBT_USE_FLASH));
BUG_ON(!bits);
if (bd->options & NAND_BBT_VERSION)
pattern_len++;
if (bd->options & NAND_BBT_NO_OOB) {
- BUG_ON(!(this->options & NAND_USE_FLASH_BBT));
- BUG_ON(!(this->options & NAND_USE_FLASH_BBT_NO_OOB));
+ BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH));
+ BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB));
BUG_ON(bd->offs);
if (bd->options & NAND_BBT_VERSION)
BUG_ON(bd->veroffs != bd->len);
@@ -1140,18 +1107,16 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
/**
* nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
- * @mtd: MTD device structure
- * @bd: descriptor for the good/bad block search pattern
- *
- * The function checks, if a bad block table(s) is/are already
- * available. If not it scans the device for manufacturer
- * marked good / bad blocks and writes the bad block table(s) to
- * the selected place.
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
*
- * The bad block table memory is allocated here. It must be freed
- * by calling the nand_free_bbt function.
+ * The function checks, if a bad block table(s) is/are already available. If
+ * not it scans the device for manufacturer marked good / bad blocks and writes
+ * the bad block table(s) to the selected place.
*
-*/
+ * The bad block table memory is allocated here. It must be freed by calling
+ * the nand_free_bbt function.
+ */
int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
@@ -1161,19 +1126,21 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
struct nand_bbt_descr *md = this->bbt_md;
len = mtd->size >> (this->bbt_erase_shift + 2);
- /* Allocate memory (2bit per block) and clear the memory bad block table */
+ /*
+ * Allocate memory (2bit per block) and clear the memory bad block
+ * table.
+ */
this->bbt = kzalloc(len, GFP_KERNEL);
- if (!this->bbt) {
- printk(KERN_ERR "nand_scan_bbt: Out of memory\n");
+ if (!this->bbt)
return -ENOMEM;
- }
- /* If no primary table decriptor is given, scan the device
- * to build a memory based bad block table
+ /*
+ * If no primary table decriptor is given, scan the device to build a
+ * memory based bad block table.
*/
if (!td) {
if ((res = nand_memory_bbt(mtd, bd))) {
- printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
+ pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
kfree(this->bbt);
this->bbt = NULL;
}
@@ -1187,13 +1154,12 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
len += (len >> this->page_shift) * mtd->oobsize;
buf = vmalloc(len);
if (!buf) {
- printk(KERN_ERR "nand_bbt: Out of memory\n");
kfree(this->bbt);
this->bbt = NULL;
return -ENOMEM;
}
- /* Is the bbt at a given page ? */
+ /* Is the bbt at a given page? */
if (td->options & NAND_BBT_ABSPAGE) {
res = read_abs_bbts(mtd, buf, td, md);
} else {
@@ -1215,15 +1181,15 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
/**
* nand_update_bbt - [NAND Interface] update bad block table(s)
- * @mtd: MTD device structure
- * @offs: the offset of the newly marked block
+ * @mtd: MTD device structure
+ * @offs: the offset of the newly marked block
*
- * The function updates the bad block table(s)
-*/
+ * The function updates the bad block table(s).
+ */
int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
{
struct nand_chip *this = mtd->priv;
- int len, res = 0, writeops = 0;
+ int len, res = 0;
int chip, chipsel;
uint8_t *buf;
struct nand_bbt_descr *td = this->bbt_td;
@@ -1236,14 +1202,10 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
len = (1 << this->bbt_erase_shift);
len += (len >> this->page_shift) * mtd->oobsize;
buf = kmalloc(len, GFP_KERNEL);
- if (!buf) {
- printk(KERN_ERR "nand_update_bbt: Out of memory\n");
+ if (!buf)
return -ENOMEM;
- }
-
- writeops = md != NULL ? 0x03 : 0x01;
- /* Do we have a bbt per chip ? */
+ /* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP) {
chip = (int)(offs >> this->chip_shift);
chipsel = chip;
@@ -1256,14 +1218,14 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
if (md)
md->version[chip]++;
- /* Write the bad block table to the device ? */
- if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+ /* Write the bad block table to the device? */
+ if (td->options & NAND_BBT_WRITE) {
res = write_bbt(mtd, buf, td, md, chipsel);
if (res < 0)
goto out;
}
- /* Write the mirror bad block table to the device ? */
- if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+ /* Write the mirror bad block table to the device? */
+ if (md && (md->options & NAND_BBT_WRITE)) {
res = write_bbt(mtd, buf, md, td, chipsel);
}
@@ -1272,8 +1234,10 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
return res;
}
-/* Define some generic bad / good block scan pattern which are used
- * while scanning a device for factory marked good / bad blocks. */
+/*
+ * Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks.
+ */
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
@@ -1285,8 +1249,7 @@ static struct nand_bbt_descr agand_flashbased = {
.pattern = scan_agand_pattern
};
-/* Generic flash bbt decriptors
-*/
+/* Generic flash bbt descriptors */
static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
@@ -1330,31 +1293,27 @@ static struct nand_bbt_descr bbt_mirror_no_bbt_descr = {
.pattern = mirror_pattern
};
-#define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \
- NAND_BBT_SCANBYTE1AND6)
+#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)
/**
- * nand_create_default_bbt_descr - [Internal] Creates a BBT descriptor structure
- * @this: NAND chip to create descriptor for
+ * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure
+ * @this: NAND chip to create descriptor for
*
* This function allocates and initializes a nand_bbt_descr for BBM detection
- * based on the properties of "this". The new descriptor is stored in
+ * based on the properties of @this. The new descriptor is stored in
* this->badblock_pattern. Thus, this->badblock_pattern should be NULL when
* passed to this function.
- *
*/
-static int nand_create_default_bbt_descr(struct nand_chip *this)
+static int nand_create_badblock_pattern(struct nand_chip *this)
{
struct nand_bbt_descr *bd;
if (this->badblock_pattern) {
- printk(KERN_WARNING "BBT descr already allocated; not replacing.\n");
+ pr_warn("Bad block pattern already allocated; not replacing\n");
return -EINVAL;
}
bd = kzalloc(sizeof(*bd), GFP_KERNEL);
- if (!bd) {
- printk(KERN_ERR "nand_create_default_bbt_descr: Out of memory\n");
+ if (!bd)
return -ENOMEM;
- }
- bd->options = this->options & BBT_SCAN_OPTIONS;
+ bd->options = this->bbt_options & BADBLOCK_SCAN_MASK;
bd->offs = this->badblockpos;
bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1;
bd->pattern = scan_ff_pattern;
@@ -1365,22 +1324,20 @@ static int nand_create_default_bbt_descr(struct nand_chip *this)
/**
* nand_default_bbt - [NAND Interface] Select a default bad block table for the device
- * @mtd: MTD device structure
- *
- * This function selects the default bad block table
- * support for the device and calls the nand_scan_bbt function
+ * @mtd: MTD device structure
*
-*/
+ * This function selects the default bad block table support for the device and
+ * calls the nand_scan_bbt function.
+ */
int nand_default_bbt(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
- /* Default for AG-AND. We must use a flash based
- * bad block table as the devices have factory marked
- * _good_ blocks. Erasing those blocks leads to loss
- * of the good / bad information, so we _must_ store
- * this information in a good / bad table during
- * startup
+ /*
+ * Default for AG-AND. We must use a flash based bad block table as the
+ * devices have factory marked _good_ blocks. Erasing those blocks
+ * leads to loss of the good / bad information, so we _must_ store this
+ * information in a good / bad table during startup.
*/
if (this->options & NAND_IS_AND) {
/* Use the default pattern descriptors */
@@ -1388,15 +1345,15 @@ int nand_default_bbt(struct mtd_info *mtd)
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
- this->options |= NAND_USE_FLASH_BBT;
+ this->bbt_options |= NAND_BBT_USE_FLASH;
return nand_scan_bbt(mtd, &agand_flashbased);
}
- /* Is a flash based bad block table requested ? */
- if (this->options & NAND_USE_FLASH_BBT) {
+ /* Is a flash based bad block table requested? */
+ if (this->bbt_options & NAND_BBT_USE_FLASH) {
/* Use the default pattern descriptors */
if (!this->bbt_td) {
- if (this->options & NAND_USE_FLASH_BBT_NO_OOB) {
+ if (this->bbt_options & NAND_BBT_NO_OOB) {
this->bbt_td = &bbt_main_no_bbt_descr;
this->bbt_md = &bbt_mirror_no_bbt_descr;
} else {
@@ -1410,18 +1367,17 @@ int nand_default_bbt(struct mtd_info *mtd)
}
if (!this->badblock_pattern)
- nand_create_default_bbt_descr(this);
+ nand_create_badblock_pattern(this);
return nand_scan_bbt(mtd, this->badblock_pattern);
}
/**
* nand_isbad_bbt - [NAND Interface] Check if a block is bad
- * @mtd: MTD device structure
- * @offs: offset in the device
- * @allowbbt: allow access to bad block table region
- *
-*/
+ * @mtd: MTD device structure
+ * @offs: offset in the device
+ * @allowbbt: allow access to bad block table region
+ */
int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
{
struct nand_chip *this = mtd->priv;
@@ -1432,8 +1388,9 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
block = (int)(offs >> (this->bbt_erase_shift - 1));
res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
- DEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
- (unsigned int)offs, block >> 1, res);
+ pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: "
+ "(block %d) 0x%02x\n",
+ (unsigned int)offs, block >> 1, res);
switch ((int)res) {
case 0x00:
diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c
index 0f931e757116..3803e0bba23b 100644
--- a/drivers/mtd/nand/nand_bch.c
+++ b/drivers/mtd/nand/nand_bch.c
@@ -93,8 +93,8 @@ int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
/* else error in ecc, no action needed */
- DEBUG(MTD_DEBUG_LEVEL0, "%s: corrected bitflip %u\n",
- __func__, errloc[i]);
+ pr_debug("%s: corrected bitflip %u\n", __func__,
+ errloc[i]);
}
} else if (count < 0) {
printk(KERN_ERR "ecc unrecoverable error\n");
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c
index 271b8e735e8f..b7cfe0d37121 100644
--- a/drivers/mtd/nand/nand_ecc.c
+++ b/drivers/mtd/nand/nand_ecc.c
@@ -110,7 +110,7 @@ static const char bitsperbyte[256] = {
/*
* addressbits is a lookup table to filter out the bits from the xor-ed
- * ecc data that identify the faulty location.
+ * ECC data that identify the faulty location.
* this is only used for repairing parity
* see the comments in nand_correct_data for more details
*/
@@ -153,7 +153,7 @@ static const char addressbits[256] = {
* __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
* block
* @buf: input buffer with raw data
- * @eccsize: data bytes per ecc step (256 or 512)
+ * @eccsize: data bytes per ECC step (256 or 512)
* @code: output buffer with ECC
*/
void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
@@ -348,7 +348,7 @@ void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
rp17 = (par ^ rp16) & 0xff;
/*
- * Finally calculate the ecc bits.
+ * Finally calculate the ECC bits.
* Again here it might seem that there are performance optimisations
* possible, but benchmarks showed that on the system this is developed
* the code below is the fastest
@@ -436,7 +436,7 @@ EXPORT_SYMBOL(nand_calculate_ecc);
* @buf: raw data read from the chip
* @read_ecc: ECC from the chip
* @calc_ecc: the ECC calculated from raw data
- * @eccsize: data bytes per ecc step (256 or 512)
+ * @eccsize: data bytes per ECC step (256 or 512)
*
* Detect and correct a 1 bit error for eccsize byte block
*/
@@ -505,7 +505,7 @@ int __nand_correct_data(unsigned char *buf,
}
/* count nr of bits; use table lookup, faster than calculating it */
if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1)
- return 1; /* error in ecc data; no action needed */
+ return 1; /* error in ECC data; no action needed */
printk(KERN_ERR "uncorrectable error : ");
return -1;
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index 357e8c5252a8..34c03be77301 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -2273,9 +2273,9 @@ static int __init ns_init_module(void)
switch (bbt) {
case 2:
- chip->options |= NAND_USE_FLASH_BBT_NO_OOB;
+ chip->bbt_options |= NAND_BBT_NO_OOB;
case 1:
- chip->options |= NAND_USE_FLASH_BBT;
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
case 0:
break;
default:
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index ea2dea8a9c88..ee1713907b92 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -42,7 +42,6 @@ struct ndfc_controller {
struct nand_chip chip;
int chip_select;
struct nand_hw_control ndfc_control;
- struct mtd_partition *parts;
};
static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS];
@@ -159,13 +158,9 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
static int ndfc_chip_init(struct ndfc_controller *ndfc,
struct device_node *node)
{
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- static const char *part_types[] = { "cmdlinepart", NULL };
-#else
- static const char *part_types[] = { NULL };
-#endif
struct device_node *flash_np;
struct nand_chip *chip = &ndfc->chip;
+ struct mtd_part_parser_data ppdata;
int ret;
chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
@@ -193,6 +188,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
if (!flash_np)
return -ENODEV;
+ ppdata->of_node = flash_np;
ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s",
dev_name(&ndfc->ofdev->dev), flash_np->name);
if (!ndfc->mtd.name) {
@@ -204,18 +200,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
if (ret)
goto err;
- ret = parse_mtd_partitions(&ndfc->mtd, part_types, &ndfc->parts, 0);
- if (ret < 0)
- goto err;
-
- if (ret == 0) {
- ret = of_mtd_parse_partitions(&ndfc->ofdev->dev, flash_np,
- &ndfc->parts);
- if (ret < 0)
- goto err;
- }
-
- ret = mtd_device_register(&ndfc->mtd, ndfc->parts, ret);
+ ret = mtd_device_parse_register(&ndfc->mtd, NULL, &ppdata, NULL, 0);
err:
of_node_put(flash_np);
@@ -288,6 +273,7 @@ static int __devexit ndfc_remove(struct platform_device *ofdev)
struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev);
nand_release(&ndfc->mtd);
+ kfree(ndfc->mtd.name);
return 0;
}
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
index b6a5c86ab31e..b463ecfb4c1a 100644
--- a/drivers/mtd/nand/nomadik_nand.c
+++ b/drivers/mtd/nand/nomadik_nand.c
@@ -187,6 +187,7 @@ static int nomadik_nand_remove(struct platform_device *pdev)
pdata->exit();
if (host) {
+ nand_release(&host->mtd);
iounmap(host->cmd_va);
iounmap(host->data_va);
iounmap(host->addr_va);
diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c
index 9c30a0b03171..fa8faedfad6e 100644
--- a/drivers/mtd/nand/nuc900_nand.c
+++ b/drivers/mtd/nand/nuc900_nand.c
@@ -339,6 +339,7 @@ static int __devexit nuc900_nand_remove(struct platform_device *pdev)
struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev);
struct resource *res;
+ nand_release(&nuc900_nand->mtd);
iounmap(nuc900_nand->reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 0db2c0e7656a..f745f00f3167 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -11,6 +11,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
@@ -94,8 +95,6 @@
#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0)
#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1)
-static const char *part_probes[] = { "cmdlinepart", NULL };
-
/* oob info generated runtime depending on ecc algorithm and layout selected */
static struct nand_ecclayout omap_oobinfo;
/* Define some generic bad / good block scan pattern which are used
@@ -114,7 +113,6 @@ struct omap_nand_info {
struct nand_hw_control controller;
struct omap_nand_platform_data *pdata;
struct mtd_info mtd;
- struct mtd_partition *parts;
struct nand_chip nand;
struct platform_device *pdev;
@@ -744,12 +742,12 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
case 1:
/* Uncorrectable error */
- DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n");
+ pr_debug("ECC UNCORRECTED_ERROR 1\n");
return -1;
case 11:
/* UN-Correctable error */
- DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n");
+ pr_debug("ECC UNCORRECTED_ERROR B\n");
return -1;
case 12:
@@ -766,8 +764,8 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
- DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at "
- "offset: %d, bit: %d\n", find_byte, find_bit);
+ pr_debug("Correcting single bit ECC error at offset: "
+ "%d, bit: %d\n", find_byte, find_bit);
page_data[find_byte] ^= (1 << find_bit);
@@ -779,7 +777,7 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
ecc_data2[2] == 0)
return 0;
}
- DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n");
+ pr_debug("UNCORRECTED_ERROR default\n");
return -1;
}
}
@@ -1103,13 +1101,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
goto out_release_mem_region;
}
- err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
- if (err > 0)
- mtd_device_register(&info->mtd, info->parts, err);
- else if (pdata->parts)
- mtd_device_register(&info->mtd, pdata->parts, pdata->nr_parts);
- else
- mtd_device_register(&info->mtd, NULL, 0);
+ mtd_device_parse_register(&info->mtd, NULL, 0,
+ pdata->parts, pdata->nr_parts);
platform_set_drvdata(pdev, &info->mtd);
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c
index 7794d0680f91..29f505adaf84 100644
--- a/drivers/mtd/nand/orion_nand.c
+++ b/drivers/mtd/nand/orion_nand.c
@@ -21,8 +21,6 @@
#include <mach/hardware.h>
#include <plat/orion_nand.h>
-static const char *part_probes[] = { "cmdlinepart", NULL };
-
static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *nc = mtd->priv;
@@ -81,8 +79,6 @@ static int __init orion_nand_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *io_base;
int ret = 0;
- struct mtd_partition *partitions = NULL;
- int num_part = 0;
nc = kzalloc(sizeof(struct nand_chip) + sizeof(struct mtd_info), GFP_KERNEL);
if (!nc) {
@@ -132,17 +128,9 @@ static int __init orion_nand_probe(struct platform_device *pdev)
goto no_dev;
}
-#ifdef CONFIG_MTD_CMDLINE_PARTS
mtd->name = "orion_nand";
- num_part = parse_mtd_partitions(mtd, part_probes, &partitions, 0);
-#endif
- /* If cmdline partitions have been passed, let them be used */
- if (num_part <= 0) {
- num_part = board->nr_parts;
- partitions = board->parts;
- }
-
- ret = mtd_device_register(mtd, partitions, num_part);
+ ret = mtd_device_parse_register(mtd, NULL, 0,
+ board->parts, board->nr_parts);
if (ret) {
nand_release(mtd);
goto no_dev;
diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c
index b1aa41b8a4eb..a97264ececdb 100644
--- a/drivers/mtd/nand/pasemi_nand.c
+++ b/drivers/mtd/nand/pasemi_nand.c
@@ -155,7 +155,8 @@ static int __devinit pasemi_nand_probe(struct platform_device *ofdev)
chip->ecc.mode = NAND_ECC_SOFT;
/* Enable the following for a flash based bad block table */
- chip->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR;
+ chip->options = NAND_NO_AUTOINCR;
+ chip->bbt_options = NAND_BBT_USE_FLASH;
/* Scan to find existence of the device */
if (nand_scan(pasemi_nand_mtd, 1)) {
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c
index 633c04bf76f6..ea8e1234e0e2 100644
--- a/drivers/mtd/nand/plat_nand.c
+++ b/drivers/mtd/nand/plat_nand.c
@@ -21,8 +21,6 @@ struct plat_nand_data {
struct nand_chip chip;
struct mtd_info mtd;
void __iomem *io_base;
- int nr_parts;
- struct mtd_partition *parts;
};
/*
@@ -79,6 +77,7 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
data->chip.read_buf = pdata->ctrl.read_buf;
data->chip.chip_delay = pdata->chip.chip_delay;
data->chip.options |= pdata->chip.options;
+ data->chip.bbt_options |= pdata->chip.bbt_options;
data->chip.ecc.hwctl = pdata->ctrl.hwcontrol;
data->chip.ecc.layout = pdata->chip.ecclayout;
@@ -99,23 +98,9 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
goto out;
}
- if (pdata->chip.part_probe_types) {
- err = parse_mtd_partitions(&data->mtd,
- pdata->chip.part_probe_types,
- &data->parts, 0);
- if (err > 0) {
- mtd_device_register(&data->mtd, data->parts, err);
- return 0;
- }
- }
- if (pdata->chip.set_parts)
- pdata->chip.set_parts(data->mtd.size, &pdata->chip);
- if (pdata->chip.partitions) {
- data->parts = pdata->chip.partitions;
- err = mtd_device_register(&data->mtd, data->parts,
- pdata->chip.nr_partitions);
- } else
- err = mtd_device_register(&data->mtd, NULL, 0);
+ err = mtd_device_parse_register(&data->mtd,
+ pdata->chip.part_probe_types, 0,
+ pdata->chip.partitions, pdata->chip.nr_partitions);
if (!err)
return err;
@@ -145,8 +130,6 @@ static int __devexit plat_nand_remove(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nand_release(&data->mtd);
- if (data->parts && data->parts != pdata->chip.partitions)
- kfree(data->parts);
if (pdata->ctrl.remove)
pdata->ctrl.remove(pdev);
iounmap(data->io_base);
diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c
index 3bbb796b451c..7e52af51a198 100644
--- a/drivers/mtd/nand/ppchameleonevb.c
+++ b/drivers/mtd/nand/ppchameleonevb.c
@@ -99,8 +99,6 @@ static struct mtd_partition partition_info_evb[] = {
#define NUM_PARTITIONS 1
-extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, const char *mtd_id);
-
/*
* hardware specific access to control-lines
*/
@@ -187,18 +185,12 @@ static int ppchameleonevb_device_ready(struct mtd_info *minfo)
}
#endif
-const char *part_probes[] = { "cmdlinepart", NULL };
-const char *part_probes_evb[] = { "cmdlinepart", NULL };
-
/*
* Main initialization routine
*/
static int __init ppchameleonevb_init(void)
{
struct nand_chip *this;
- const char *part_type = 0;
- int mtd_parts_nb = 0;
- struct mtd_partition *mtd_parts = 0;
void __iomem *ppchameleon_fio_base;
void __iomem *ppchameleonevb_fio_base;
@@ -281,24 +273,13 @@ static int __init ppchameleonevb_init(void)
#endif
ppchameleon_mtd->name = "ppchameleon-nand";
- mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0);
- if (mtd_parts_nb > 0)
- part_type = "command line";
- else
- mtd_parts_nb = 0;
-
- if (mtd_parts_nb == 0) {
- if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
- mtd_parts = partition_info_me;
- else
- mtd_parts = partition_info_hi;
- mtd_parts_nb = NUM_PARTITIONS;
- part_type = "static";
- }
/* Register the partitions */
- printk(KERN_NOTICE "Using %s partition definition\n", part_type);
- mtd_device_register(ppchameleon_mtd, mtd_parts, mtd_parts_nb);
+ mtd_device_parse_register(ppchameleon_mtd, NULL, 0,
+ ppchameleon_mtd->size == NAND_SMALL_SIZE ?
+ partition_info_me :
+ partition_info_hi,
+ NUM_PARTITIONS);
nand_evb_init:
/****************************
@@ -382,21 +363,13 @@ static int __init ppchameleonevb_init(void)
}
ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
- mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0);
- if (mtd_parts_nb > 0)
- part_type = "command line";
- else
- mtd_parts_nb = 0;
-
- if (mtd_parts_nb == 0) {
- mtd_parts = partition_info_evb;
- mtd_parts_nb = NUM_PARTITIONS;
- part_type = "static";
- }
/* Register the partitions */
- printk(KERN_NOTICE "Using %s partition definition\n", part_type);
- mtd_device_register(ppchameleonevb_mtd, mtd_parts, mtd_parts_nb);
+ mtd_device_parse_register(ppchameleonevb_mtd, NULL, 0,
+ ppchameleon_mtd->size == NAND_SMALL_SIZE ?
+ partition_info_me :
+ partition_info_hi,
+ NUM_PARTITIONS);
/* Return happy */
return 0;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 1fb3b3a80581..9eb7f879969e 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
enum {
STATE_IDLE = 0,
+ STATE_PREPARED,
STATE_CMD_HANDLE,
STATE_DMA_READING,
STATE_DMA_WRITING,
@@ -120,21 +121,40 @@ enum {
STATE_READY,
};
-struct pxa3xx_nand_info {
- struct nand_chip nand_chip;
+struct pxa3xx_nand_host {
+ struct nand_chip chip;
+ struct pxa3xx_nand_cmdset *cmdset;
+ struct mtd_info *mtd;
+ void *info_data;
+
+ /* page size of attached chip */
+ unsigned int page_size;
+ int use_ecc;
+ int cs;
+ /* calculated from pxa3xx_nand_flash data */
+ unsigned int col_addr_cycles;
+ unsigned int row_addr_cycles;
+ size_t read_id_bytes;
+
+ /* cached register value */
+ uint32_t reg_ndcr;
+ uint32_t ndtr0cs0;
+ uint32_t ndtr1cs0;
+};
+
+struct pxa3xx_nand_info {
struct nand_hw_control controller;
struct platform_device *pdev;
- struct pxa3xx_nand_cmdset *cmdset;
struct clk *clk;
void __iomem *mmio_base;
unsigned long mmio_phys;
+ struct completion cmd_complete;
unsigned int buf_start;
unsigned int buf_count;
- struct mtd_info *mtd;
/* DMA information */
int drcmr_dat;
int drcmr_cmd;
@@ -142,44 +162,27 @@ struct pxa3xx_nand_info {
unsigned char *data_buff;
unsigned char *oob_buff;
dma_addr_t data_buff_phys;
- size_t data_buff_size;
int data_dma_ch;
struct pxa_dma_desc *data_desc;
dma_addr_t data_desc_addr;
- uint32_t reg_ndcr;
-
- /* saved column/page_addr during CMD_SEQIN */
- int seqin_column;
- int seqin_page_addr;
-
- /* relate to the command */
+ struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
unsigned int state;
+ int cs;
int use_ecc; /* use HW ECC ? */
int use_dma; /* use DMA ? */
int is_ready;
unsigned int page_size; /* page size of attached chip */
unsigned int data_size; /* data size in FIFO */
+ unsigned int oob_size;
int retcode;
- struct completion cmd_complete;
/* generated NDCBx register values */
uint32_t ndcb0;
uint32_t ndcb1;
uint32_t ndcb2;
-
- /* timing calcuted from setting */
- uint32_t ndtr0cs0;
- uint32_t ndtr1cs0;
-
- /* calculated from pxa3xx_nand_flash data */
- size_t oob_size;
- size_t read_id_bytes;
-
- unsigned int col_addr_cycles;
- unsigned int row_addr_cycles;
};
static int use_dma = 1;
@@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
/* Define a default flash type setting serve as flash detecting only */
#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
#define NDTR0_tCH(c) (min((c), 7) << 19)
#define NDTR0_tCS(c) (min((c), 7) << 16)
@@ -241,9 +244,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
/* convert nano-seconds to nand flash controller clock cycles */
#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000)
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
const struct pxa3xx_nand_timing *t)
{
+ struct pxa3xx_nand_info *info = host->info_data;
unsigned long nand_clk = clk_get_rate(info->clk);
uint32_t ndtr0, ndtr1;
@@ -258,23 +262,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
- info->ndtr0cs0 = ndtr0;
- info->ndtr1cs0 = ndtr1;
+ host->ndtr0cs0 = ndtr0;
+ host->ndtr1cs0 = ndtr1;
nand_writel(info, NDTR0CS0, ndtr0);
nand_writel(info, NDTR1CS0, ndtr1);
}
static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
{
- int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+ struct pxa3xx_nand_host *host = info->host[info->cs];
+ int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
- info->data_size = info->page_size;
+ info->data_size = host->page_size;
if (!oob_enable) {
info->oob_size = 0;
return;
}
- switch (info->page_size) {
+ switch (host->page_size) {
case 2048:
info->oob_size = (info->use_ecc) ? 40 : 64;
break;
@@ -292,9 +297,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
*/
static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
{
+ struct pxa3xx_nand_host *host = info->host[info->cs];
uint32_t ndcr;
- ndcr = info->reg_ndcr;
+ ndcr = host->reg_ndcr;
ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
ndcr |= NDCR_ND_RUN;
@@ -359,7 +365,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
DIV_ROUND_UP(info->oob_size, 4));
break;
default:
- printk(KERN_ERR "%s: invalid state %d\n", __func__,
+ dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
info->state);
BUG();
}
@@ -385,7 +391,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
break;
default:
- printk(KERN_ERR "%s: invalid state %d\n", __func__,
+ dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
info->state);
BUG();
}
@@ -416,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
{
struct pxa3xx_nand_info *info = devid;
unsigned int status, is_completed = 0;
+ unsigned int ready, cmd_done;
+
+ if (info->cs == 0) {
+ ready = NDSR_FLASH_RDY;
+ cmd_done = NDSR_CS0_CMDD;
+ } else {
+ ready = NDSR_RDY;
+ cmd_done = NDSR_CS1_CMDD;
+ }
status = nand_readl(info, NDSR);
@@ -437,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
handle_data_pio(info);
}
}
- if (status & NDSR_CS0_CMDD) {
+ if (status & cmd_done) {
info->state = STATE_CMD_DONE;
is_completed = 1;
}
- if (status & NDSR_FLASH_RDY) {
+ if (status & ready) {
info->is_ready = 1;
info->state = STATE_READY;
}
@@ -463,12 +478,6 @@ NORMAL_IRQ_EXIT:
return IRQ_HANDLED;
}
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
- struct pxa3xx_nand_info *info = mtd->priv;
- return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
static inline int is_buf_blank(uint8_t *buf, size_t len)
{
for (; len > 0; len--)
@@ -481,10 +490,12 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
uint16_t column, int page_addr)
{
uint16_t cmd;
- int addr_cycle, exec_cmd, ndcb0;
- struct mtd_info *mtd = info->mtd;
+ int addr_cycle, exec_cmd;
+ struct pxa3xx_nand_host *host;
+ struct mtd_info *mtd;
- ndcb0 = 0;
+ host = info->host[info->cs];
+ mtd = host->mtd;
addr_cycle = 0;
exec_cmd = 1;
@@ -495,6 +506,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
info->use_ecc = 0;
info->is_ready = 0;
info->retcode = ERR_NONE;
+ if (info->cs != 0)
+ info->ndcb0 = NDCB0_CSEL;
+ else
+ info->ndcb0 = 0;
switch (command) {
case NAND_CMD_READ0:
@@ -512,20 +527,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
break;
}
- info->ndcb0 = ndcb0;
- addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
- + info->col_addr_cycles);
+ addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+ + host->col_addr_cycles);
switch (command) {
case NAND_CMD_READOOB:
case NAND_CMD_READ0:
- cmd = info->cmdset->read1;
+ cmd = host->cmdset->read1;
if (command == NAND_CMD_READOOB)
info->buf_start = mtd->writesize + column;
else
info->buf_start = column;
- if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+ if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
info->ndcb0 |= NDCB0_CMD_TYPE(0)
| addr_cycle
| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +551,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
case NAND_CMD_SEQIN:
/* small page addr setting */
- if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+ if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
| (column & 0xFF);
@@ -564,7 +578,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
break;
}
- cmd = info->cmdset->program;
+ cmd = host->cmdset->program;
info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
| NDCB0_AUTO_RS
| NDCB0_ST_ROW_EN
@@ -574,8 +588,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
break;
case NAND_CMD_READID:
- cmd = info->cmdset->read_id;
- info->buf_count = info->read_id_bytes;
+ cmd = host->cmdset->read_id;
+ info->buf_count = host->read_id_bytes;
info->ndcb0 |= NDCB0_CMD_TYPE(3)
| NDCB0_ADDR_CYC(1)
| cmd;
@@ -583,7 +597,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
info->data_size = 8;
break;
case NAND_CMD_STATUS:
- cmd = info->cmdset->read_status;
+ cmd = host->cmdset->read_status;
info->buf_count = 1;
info->ndcb0 |= NDCB0_CMD_TYPE(4)
| NDCB0_ADDR_CYC(1)
@@ -593,7 +607,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
break;
case NAND_CMD_ERASE1:
- cmd = info->cmdset->erase;
+ cmd = host->cmdset->erase;
info->ndcb0 |= NDCB0_CMD_TYPE(2)
| NDCB0_AUTO_RS
| NDCB0_ADDR_CYC(3)
@@ -604,7 +618,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
break;
case NAND_CMD_RESET:
- cmd = info->cmdset->reset;
+ cmd = host->cmdset->reset;
info->ndcb0 |= NDCB0_CMD_TYPE(5)
| cmd;
@@ -616,8 +630,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
default:
exec_cmd = 0;
- printk(KERN_ERR "pxa3xx-nand: non-supported"
- " command %x\n", command);
+ dev_err(&info->pdev->dev, "non-supported command %x\n",
+ command);
break;
}
@@ -627,7 +641,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
int column, int page_addr)
{
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_host *host = mtd->priv;
+ struct pxa3xx_nand_info *info = host->info_data;
int ret, exec_cmd;
/*
@@ -635,9 +650,21 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
* "byte" address into a "word" address appropriate
* for indexing a word-oriented device
*/
- if (info->reg_ndcr & NDCR_DWIDTH_M)
+ if (host->reg_ndcr & NDCR_DWIDTH_M)
column /= 2;
+ /*
+ * There may be different NAND chip hooked to
+ * different chip select, so check whether
+ * chip select has been changed, if yes, reset the timing
+ */
+ if (info->cs != host->cs) {
+ info->cs = host->cs;
+ nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+ nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+ }
+
+ info->state = STATE_PREPARED;
exec_cmd = prepare_command_pool(info, command, column, page_addr);
if (exec_cmd) {
init_completion(&info->cmd_complete);
@@ -646,12 +673,12 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
ret = wait_for_completion_timeout(&info->cmd_complete,
CHIP_DELAY_TIMEOUT);
if (!ret) {
- printk(KERN_ERR "Wait time out!!!\n");
+ dev_err(&info->pdev->dev, "Wait time out!!!\n");
/* Stop State Machine for next command cycle */
pxa3xx_nand_stop(info);
}
- info->state = STATE_IDLE;
}
+ info->state = STATE_IDLE;
}
static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +691,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int page)
{
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_host *host = mtd->priv;
+ struct pxa3xx_nand_info *info = host->info_data;
chip->read_buf(mtd, buf, mtd->writesize);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -685,6 +713,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
* OOB, ignore such double bit errors
*/
if (is_buf_blank(buf, mtd->writesize))
+ info->retcode = ERR_NONE;
+ else
mtd->ecc_stats.failed++;
}
@@ -693,7 +723,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
{
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_host *host = mtd->priv;
+ struct pxa3xx_nand_info *info = host->info_data;
char retval = 0xFF;
if (info->buf_start < info->buf_count)
@@ -705,7 +736,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
{
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_host *host = mtd->priv;
+ struct pxa3xx_nand_info *info = host->info_data;
u16 retval = 0xFFFF;
if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -717,7 +749,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_host *host = mtd->priv;
+ struct pxa3xx_nand_info *info = host->info_data;
int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -727,7 +760,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_host *host = mtd->priv;
+ struct pxa3xx_nand_info *info = host->info_data;
int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -747,7 +781,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
{
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_host *host = mtd->priv;
+ struct pxa3xx_nand_info *info = host->info_data;
/* pxa3xx_nand_send_command has waited for command complete */
if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -770,54 +805,70 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
{
struct platform_device *pdev = info->pdev;
struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+ struct pxa3xx_nand_host *host = info->host[info->cs];
uint32_t ndcr = 0x0; /* enable all interrupts */
- if (f->page_size != 2048 && f->page_size != 512)
+ if (f->page_size != 2048 && f->page_size != 512) {
+ dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
return -EINVAL;
+ }
- if (f->flash_width != 16 && f->flash_width != 8)
+ if (f->flash_width != 16 && f->flash_width != 8) {
+ dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
return -EINVAL;
+ }
/* calculate flash information */
- info->cmdset = &default_cmdset;
- info->page_size = f->page_size;
- info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+ host->cmdset = &default_cmdset;
+ host->page_size = f->page_size;
+ host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
/* calculate addressing information */
- info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+ host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
if (f->num_blocks * f->page_per_block > 65536)
- info->row_addr_cycles = 3;
+ host->row_addr_cycles = 3;
else
- info->row_addr_cycles = 2;
+ host->row_addr_cycles = 2;
ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
- ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+ ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0;
ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0;
ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0;
ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0;
- ndcr |= NDCR_RD_ID_CNT(info->read_id_bytes);
+ ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
ndcr |= NDCR_SPARE_EN; /* enable spare by default */
- info->reg_ndcr = ndcr;
+ host->reg_ndcr = ndcr;
- pxa3xx_nand_set_timing(info, f->timing);
+ pxa3xx_nand_set_timing(host, f->timing);
return 0;
}
static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
{
+ /*
+ * We set 0 by hard coding here, for we don't support keep_config
+ * when there is more than one chip attached to the controller
+ */
+ struct pxa3xx_nand_host *host = info->host[0];
uint32_t ndcr = nand_readl(info, NDCR);
- info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
- /* set info fields needed to read id */
- info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
- info->reg_ndcr = ndcr;
- info->cmdset = &default_cmdset;
- info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
- info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+ if (ndcr & NDCR_PAGE_SZ) {
+ host->page_size = 2048;
+ host->read_id_bytes = 4;
+ } else {
+ host->page_size = 512;
+ host->read_id_bytes = 2;
+ }
+
+ host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+ host->cmdset = &default_cmdset;
+
+ host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+ host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
return 0;
}
@@ -847,7 +898,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
return -ENOMEM;
}
- info->data_buff_size = MAX_BUFF_SIZE;
info->data_desc = (void *)info->data_buff + data_desc_offset;
info->data_desc_addr = info->data_buff_phys + data_desc_offset;
@@ -855,7 +905,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
pxa3xx_nand_data_dma_irq, info);
if (info->data_dma_ch < 0) {
dev_err(&pdev->dev, "failed to request data dma\n");
- dma_free_coherent(&pdev->dev, info->data_buff_size,
+ dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
info->data_buff, info->data_buff_phys);
return info->data_dma_ch;
}
@@ -865,24 +915,28 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
{
- struct mtd_info *mtd = info->mtd;
- struct nand_chip *chip = mtd->priv;
-
+ struct mtd_info *mtd;
+ int ret;
+ mtd = info->host[info->cs]->mtd;
/* use the common timing to make a try */
- pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
- chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+ ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+ if (ret)
+ return ret;
+
+ pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
if (info->is_ready)
- return 1;
- else
return 0;
+
+ return -ENODEV;
}
static int pxa3xx_nand_scan(struct mtd_info *mtd)
{
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_host *host = mtd->priv;
+ struct pxa3xx_nand_info *info = host->info_data;
struct platform_device *pdev = info->pdev;
struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
- struct nand_flash_dev pxa3xx_flash_ids[2] = { {NULL,}, {NULL,} };
+ struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
const struct pxa3xx_nand_flash *f = NULL;
struct nand_chip *chip = mtd->priv;
uint32_t id = -1;
@@ -893,22 +947,20 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
goto KEEP_CONFIG;
ret = pxa3xx_nand_sensing(info);
- if (!ret) {
- kfree(mtd);
- info->mtd = NULL;
- printk(KERN_INFO "There is no nand chip on cs 0!\n");
+ if (ret) {
+ dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
+ info->cs);
- return -EINVAL;
+ return ret;
}
chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
id = *((uint16_t *)(info->data_buff));
if (id != 0)
- printk(KERN_INFO "Detect a flash id %x\n", id);
+ dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
else {
- kfree(mtd);
- info->mtd = NULL;
- printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+ dev_warn(&info->pdev->dev,
+ "Read out ID 0, potential timing set wrong!!\n");
return -EINVAL;
}
@@ -926,14 +978,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
}
if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
- kfree(mtd);
- info->mtd = NULL;
- printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+ dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
return -EINVAL;
}
- pxa3xx_nand_config_flash(info, f);
+ ret = pxa3xx_nand_config_flash(info, f);
+ if (ret) {
+ dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+ return ret;
+ }
+
pxa3xx_flash_ids[0].name = f->name;
pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -942,62 +997,78 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
pxa3xx_flash_ids[0].erasesize = f->page_size * f->page_per_block;
if (f->flash_width == 16)
pxa3xx_flash_ids[0].options = NAND_BUSWIDTH_16;
+ pxa3xx_flash_ids[1].name = NULL;
+ def = pxa3xx_flash_ids;
KEEP_CONFIG:
- if (nand_scan_ident(mtd, 1, pxa3xx_flash_ids))
+ chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.size = host->page_size;
+
+ chip->options = NAND_NO_AUTOINCR;
+ chip->options |= NAND_NO_READRDY;
+ if (host->reg_ndcr & NDCR_DWIDTH_M)
+ chip->options |= NAND_BUSWIDTH_16;
+
+ if (nand_scan_ident(mtd, 1, def))
return -ENODEV;
/* calculate addressing information */
- info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+ if (mtd->writesize >= 2048)
+ host->col_addr_cycles = 2;
+ else
+ host->col_addr_cycles = 1;
+
info->oob_buff = info->data_buff + mtd->writesize;
if ((mtd->size >> chip->page_shift) > 65536)
- info->row_addr_cycles = 3;
+ host->row_addr_cycles = 3;
else
- info->row_addr_cycles = 2;
- mtd->name = mtd_names[0];
- chip->ecc.mode = NAND_ECC_HW;
- chip->ecc.size = f->page_size;
-
- chip->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16 : 0;
- chip->options |= NAND_NO_AUTOINCR;
- chip->options |= NAND_NO_READRDY;
+ host->row_addr_cycles = 2;
+ mtd->name = mtd_names[0];
return nand_scan_tail(mtd);
}
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
{
+ struct pxa3xx_nand_platform_data *pdata;
struct pxa3xx_nand_info *info;
+ struct pxa3xx_nand_host *host;
struct nand_chip *chip;
struct mtd_info *mtd;
struct resource *r;
- int ret, irq;
+ int ret, irq, cs;
- mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
- GFP_KERNEL);
- if (!mtd) {
+ pdata = pdev->dev.platform_data;
+ info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+ sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
+ if (!info) {
dev_err(&pdev->dev, "failed to allocate memory\n");
- return NULL;
+ return -ENOMEM;
}
- info = (struct pxa3xx_nand_info *)(&mtd[1]);
- chip = (struct nand_chip *)(&mtd[1]);
info->pdev = pdev;
- info->mtd = mtd;
- mtd->priv = info;
- mtd->owner = THIS_MODULE;
-
- chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
- chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
- chip->controller = &info->controller;
- chip->waitfunc = pxa3xx_nand_waitfunc;
- chip->select_chip = pxa3xx_nand_select_chip;
- chip->dev_ready = pxa3xx_nand_dev_ready;
- chip->cmdfunc = pxa3xx_nand_cmdfunc;
- chip->read_word = pxa3xx_nand_read_word;
- chip->read_byte = pxa3xx_nand_read_byte;
- chip->read_buf = pxa3xx_nand_read_buf;
- chip->write_buf = pxa3xx_nand_write_buf;
- chip->verify_buf = pxa3xx_nand_verify_buf;
+ for (cs = 0; cs < pdata->num_cs; cs++) {
+ mtd = (struct mtd_info *)((unsigned int)&info[1] +
+ (sizeof(*mtd) + sizeof(*host)) * cs);
+ chip = (struct nand_chip *)(&mtd[1]);
+ host = (struct pxa3xx_nand_host *)chip;
+ info->host[cs] = host;
+ host->mtd = mtd;
+ host->cs = cs;
+ host->info_data = info;
+ mtd->priv = host;
+ mtd->owner = THIS_MODULE;
+
+ chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
+ chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
+ chip->controller = &info->controller;
+ chip->waitfunc = pxa3xx_nand_waitfunc;
+ chip->select_chip = pxa3xx_nand_select_chip;
+ chip->cmdfunc = pxa3xx_nand_cmdfunc;
+ chip->read_word = pxa3xx_nand_read_word;
+ chip->read_byte = pxa3xx_nand_read_byte;
+ chip->read_buf = pxa3xx_nand_read_buf;
+ chip->write_buf = pxa3xx_nand_write_buf;
+ chip->verify_buf = pxa3xx_nand_verify_buf;
+ }
spin_lock_init(&chip->controller->lock);
init_waitqueue_head(&chip->controller->wq);
@@ -1070,13 +1141,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
platform_set_drvdata(pdev, info);
- return info;
+ return 0;
fail_free_buf:
free_irq(irq, info);
if (use_dma) {
pxa_free_dma(info->data_dma_ch);
- dma_free_coherent(&pdev->dev, info->data_buff_size,
+ dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
info->data_buff, info->data_buff_phys);
} else
kfree(info->data_buff);
@@ -1088,17 +1159,21 @@ fail_put_clk:
clk_disable(info->clk);
clk_put(info->clk);
fail_free_mtd:
- kfree(mtd);
- return NULL;
+ kfree(info);
+ return ret;
}
static int pxa3xx_nand_remove(struct platform_device *pdev)
{
struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
- struct mtd_info *mtd = info->mtd;
+ struct pxa3xx_nand_platform_data *pdata;
struct resource *r;
- int irq;
+ int irq, cs;
+ if (!info)
+ return 0;
+
+ pdata = pdev->dev.platform_data;
platform_set_drvdata(pdev, NULL);
irq = platform_get_irq(pdev, 0);
@@ -1106,7 +1181,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
free_irq(irq, info);
if (use_dma) {
pxa_free_dma(info->data_dma_ch);
- dma_free_writecombine(&pdev->dev, info->data_buff_size,
+ dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
info->data_buff, info->data_buff_phys);
} else
kfree(info->data_buff);
@@ -1118,10 +1193,9 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
clk_disable(info->clk);
clk_put(info->clk);
- if (mtd) {
- mtd_device_unregister(mtd);
- kfree(mtd);
- }
+ for (cs = 0; cs < pdata->num_cs; cs++)
+ nand_release(info->host[cs]->mtd);
+ kfree(info);
return 0;
}
@@ -1129,6 +1203,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
{
struct pxa3xx_nand_platform_data *pdata;
struct pxa3xx_nand_info *info;
+ int ret, cs, probe_success;
pdata = pdev->dev.platform_data;
if (!pdata) {
@@ -1136,52 +1211,88 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
return -ENODEV;
}
- info = alloc_nand_resource(pdev);
- if (info == NULL)
- return -ENOMEM;
-
- if (pxa3xx_nand_scan(info->mtd)) {
- dev_err(&pdev->dev, "failed to scan nand\n");
- pxa3xx_nand_remove(pdev);
- return -ENODEV;
+ ret = alloc_nand_resource(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "alloc nand resource failed\n");
+ return ret;
}
- if (mtd_has_cmdlinepart()) {
- const char *probes[] = { "cmdlinepart", NULL };
- struct mtd_partition *parts;
- int nr_parts;
+ info = platform_get_drvdata(pdev);
+ probe_success = 0;
+ for (cs = 0; cs < pdata->num_cs; cs++) {
+ info->cs = cs;
+ ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+ if (ret) {
+ dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+ cs);
+ continue;
+ }
- nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0);
+ ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+ pdata->parts[cs], pdata->nr_parts[cs]);
+ if (!ret)
+ probe_success = 1;
+ }
- if (nr_parts)
- return mtd_device_register(info->mtd, parts, nr_parts);
+ if (!probe_success) {
+ pxa3xx_nand_remove(pdev);
+ return -ENODEV;
}
- return mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts);
+ return 0;
}
#ifdef CONFIG_PM
static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
{
struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
- struct mtd_info *mtd = info->mtd;
+ struct pxa3xx_nand_platform_data *pdata;
+ struct mtd_info *mtd;
+ int cs;
+ pdata = pdev->dev.platform_data;
if (info->state) {
dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
return -EAGAIN;
}
+ for (cs = 0; cs < pdata->num_cs; cs++) {
+ mtd = info->host[cs]->mtd;
+ mtd->suspend(mtd);
+ }
+
return 0;
}
static int pxa3xx_nand_resume(struct platform_device *pdev)
{
struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
- struct mtd_info *mtd = info->mtd;
+ struct pxa3xx_nand_platform_data *pdata;
+ struct mtd_info *mtd;
+ int cs;
- nand_writel(info, NDTR0CS0, info->ndtr0cs0);
- nand_writel(info, NDTR1CS0, info->ndtr1cs0);
- clk_enable(info->clk);
+ pdata = pdev->dev.platform_data;
+ /* We don't want to handle interrupt without calling mtd routine */
+ disable_int(info, NDCR_INT_MASK);
+
+ /*
+ * Directly set the chip select to a invalid value,
+ * then the driver would reset the timing according
+ * to current chip select at the beginning of cmdfunc
+ */
+ info->cs = 0xff;
+
+ /*
+ * As the spec says, the NDSR would be updated to 0x1800 when
+ * doing the nand_clk disable/enable.
+ * To prevent it damaging state machine of the driver, clear
+ * all status before resume
+ */
+ nand_writel(info, NDSR, NDSR_MASK);
+ for (cs = 0; cs < pdata->num_cs; cs++) {
+ mtd = info->host[cs]->mtd;
+ mtd->resume(mtd);
+ }
return 0;
}
diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c
index cae2e013c986..f20f393bfda6 100644
--- a/drivers/mtd/nand/r852.c
+++ b/drivers/mtd/nand/r852.c
@@ -1027,7 +1027,7 @@ void r852_shutdown(struct pci_dev *pci_dev)
}
#ifdef CONFIG_PM
-int r852_suspend(struct device *device)
+static int r852_suspend(struct device *device)
{
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
@@ -1048,7 +1048,7 @@ int r852_suspend(struct device *device)
return 0;
}
-int r852_resume(struct device *device)
+static int r852_resume(struct device *device)
{
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
@@ -1092,7 +1092,7 @@ static const struct pci_device_id r852_pci_id_tbl[] = {
MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl);
-SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume);
+static SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume);
static struct pci_driver r852_pci_driver = {
.name = DRV_NAME,
diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c
index c9f9127ff770..f309addc2fa0 100644
--- a/drivers/mtd/nand/rtc_from4.c
+++ b/drivers/mtd/nand/rtc_from4.c
@@ -351,7 +351,7 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
return 0;
}
- /* Read the syndrom pattern from the FPGA and correct the bitorder */
+ /* Read the syndrome pattern from the FPGA and correct the bitorder */
rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC);
for (i = 0; i < 8; i++) {
ecc[i] = bitrev8(*rs_ecc);
@@ -380,7 +380,7 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
/* Let the library code do its magic. */
res = decode_rs8(rs_decoder, (uint8_t *) buf, par, 512, syn, 0, NULL, 0xff, NULL);
if (res > 0) {
- DEBUG(MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: " "ECC corrected %d errors on read\n", res);
+ pr_debug("rtc_from4_correct_data: " "ECC corrected %d errors on read\n", res);
}
return res;
}
@@ -444,7 +444,6 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this,
len = mtd->writesize;
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
- printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n");
er_stat = 1;
goto out;
}
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 4405468f196b..868685db6712 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -723,7 +723,7 @@ static int s3c24xx_nand_remove(struct platform_device *pdev)
/* free the common resources */
- if (info->clk != NULL && !IS_ERR(info->clk)) {
+ if (!IS_ERR(info->clk)) {
s3c2410_nand_clk_set_state(info, CLOCK_DISABLE);
clk_put(info->clk);
}
@@ -744,26 +744,15 @@ static int s3c24xx_nand_remove(struct platform_device *pdev)
return 0;
}
-const char *part_probes[] = { "cmdlinepart", NULL };
static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *mtd,
struct s3c2410_nand_set *set)
{
- struct mtd_partition *part_info;
- int nr_part = 0;
+ if (set)
+ mtd->mtd.name = set->name;
- if (set == NULL)
- return mtd_device_register(&mtd->mtd, NULL, 0);
-
- mtd->mtd.name = set->name;
- nr_part = parse_mtd_partitions(&mtd->mtd, part_probes, &part_info, 0);
-
- if (nr_part <= 0 && set->nr_partitions > 0) {
- nr_part = set->nr_partitions;
- part_info = set->partitions;
- }
-
- return mtd_device_register(&mtd->mtd, part_info, nr_part);
+ return mtd_device_parse_register(&mtd->mtd, NULL, 0,
+ set->partitions, set->nr_partitions);
}
/**
@@ -880,8 +869,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
/* If you use u-boot BBT creation code, specifying this flag will
* let the kernel fish out the BBT from the NAND, and also skip the
* full NAND scan that can take 1/2s or so. Little things... */
- if (set->flash_bbt)
- chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN;
+ if (set->flash_bbt) {
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+ chip->options |= NAND_SKIP_BBTSCAN;
+ }
}
/**
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c
index 19e24ed089ea..619d2a504788 100644
--- a/drivers/mtd/nand/sharpsl.c
+++ b/drivers/mtd/nand/sharpsl.c
@@ -103,16 +103,12 @@ static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat,
return readb(sharpsl->io + ECCCNTR) != 0;
}
-static const char *part_probes[] = { "cmdlinepart", NULL };
-
/*
* Main initialization routine
*/
static int __devinit sharpsl_nand_probe(struct platform_device *pdev)
{
struct nand_chip *this;
- struct mtd_partition *sharpsl_partition_info;
- int nr_partitions;
struct resource *r;
int err = 0;
struct sharpsl_nand *sharpsl;
@@ -184,14 +180,9 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev)
/* Register the partitions */
sharpsl->mtd.name = "sharpsl-nand";
- nr_partitions = parse_mtd_partitions(&sharpsl->mtd, part_probes, &sharpsl_partition_info, 0);
- if (nr_partitions <= 0) {
- nr_partitions = data->nr_partitions;
- sharpsl_partition_info = data->partitions;
- }
- err = mtd_device_register(&sharpsl->mtd, sharpsl_partition_info,
- nr_partitions);
+ err = mtd_device_parse_register(&sharpsl->mtd, NULL, 0,
+ data->partitions, data->nr_partitions);
if (err)
goto err_add;
diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c
index b6332e83b289..32ae5af7444f 100644
--- a/drivers/mtd/nand/sm_common.c
+++ b/drivers/mtd/nand/sm_common.c
@@ -8,6 +8,7 @@
*/
#include <linux/kernel.h>
#include <linux/mtd/nand.h>
+#include <linux/module.h>
#include "sm_common.h"
static struct nand_ecclayout nand_oob_sm = {
@@ -47,7 +48,7 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
/* As long as this function is called on erase block boundaries
it will work correctly for 256 byte nand */
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
ops.oobbuf = (void *)&oob;
diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c
index ca2d0555729e..0fb24f9c2327 100644
--- a/drivers/mtd/nand/socrates_nand.c
+++ b/drivers/mtd/nand/socrates_nand.c
@@ -155,8 +155,6 @@ static int socrates_nand_device_ready(struct mtd_info *mtd)
return 1;
}
-static const char *part_probes[] = { "cmdlinepart", NULL };
-
/*
* Probe for the NAND device.
*/
@@ -166,8 +164,7 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev)
struct mtd_info *mtd;
struct nand_chip *nand_chip;
int res;
- struct mtd_partition *partitions = NULL;
- int num_partitions = 0;
+ struct mtd_part_parser_data ppdata;
/* Allocate memory for the device structure (and zero it) */
host = kzalloc(sizeof(struct socrates_nand_host), GFP_KERNEL);
@@ -193,6 +190,7 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev)
mtd->name = "socrates_nand";
mtd->owner = THIS_MODULE;
mtd->dev.parent = &ofdev->dev;
+ ppdata.of_node = ofdev->dev.of_node;
/*should never be accessed directly */
nand_chip->IO_ADDR_R = (void *)0xdeadbeef;
@@ -225,30 +223,10 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev)
goto out;
}
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- num_partitions = parse_mtd_partitions(mtd, part_probes,
- &partitions, 0);
- if (num_partitions < 0) {
- res = num_partitions;
- goto release;
- }
-#endif
-
- if (num_partitions == 0) {
- num_partitions = of_mtd_parse_partitions(&ofdev->dev,
- ofdev->dev.of_node,
- &partitions);
- if (num_partitions < 0) {
- res = num_partitions;
- goto release;
- }
- }
-
- res = mtd_device_register(mtd, partitions, num_partitions);
+ res = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
if (!res)
return res;
-release:
nand_release(mtd);
out:
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c
index 11e8371b5683..beebd95f7690 100644
--- a/drivers/mtd/nand/tmio_nand.c
+++ b/drivers/mtd/nand/tmio_nand.c
@@ -121,9 +121,6 @@ struct tmio_nand {
#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd)
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-static const char *part_probes[] = { "cmdlinepart", NULL };
-#endif
/*--------------------------------------------------------------------------*/
@@ -381,8 +378,6 @@ static int tmio_probe(struct platform_device *dev)
struct tmio_nand *tmio;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
- struct mtd_partition *parts;
- int nbparts = 0;
int retval;
if (data == NULL)
@@ -461,15 +456,9 @@ static int tmio_probe(struct platform_device *dev)
goto err_scan;
}
/* Register the partitions */
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- nbparts = parse_mtd_partitions(mtd, part_probes, &parts, 0);
-#endif
- if (nbparts <= 0 && data) {
- parts = data->partition;
- nbparts = data->num_partitions;
- }
-
- retval = mtd_device_register(mtd, parts, nbparts);
+ retval = mtd_device_parse_register(mtd, NULL, 0,
+ data ? data->partition : NULL,
+ data ? data->num_partitions : 0);
if (!retval)
return retval;
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c
index bfba4e39a6c5..ace46fdaef58 100644
--- a/drivers/mtd/nand/txx9ndfmc.c
+++ b/drivers/mtd/nand/txx9ndfmc.c
@@ -74,7 +74,6 @@ struct txx9ndfmc_drvdata {
unsigned char hold; /* in gbusclock */
unsigned char spw; /* in gbusclock */
struct nand_hw_control hw_control;
- struct mtd_partition *parts[MAX_TXX9NDFMC_DEV];
};
static struct platform_device *mtd_to_platdev(struct mtd_info *mtd)
@@ -287,7 +286,6 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd)
static int __init txx9ndfmc_probe(struct platform_device *dev)
{
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
- static const char *probes[] = { "cmdlinepart", NULL };
int hold, spw;
int i;
struct txx9ndfmc_drvdata *drvdata;
@@ -333,7 +331,6 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
struct txx9ndfmc_priv *txx9_priv;
struct nand_chip *chip;
struct mtd_info *mtd;
- int nr_parts;
if (!(plat->ch_mask & (1 << i)))
continue;
@@ -393,9 +390,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
}
mtd->name = txx9_priv->mtdname;
- nr_parts = parse_mtd_partitions(mtd, probes,
- &drvdata->parts[i], 0);
- mtd_device_register(mtd, drvdata->parts[i], nr_parts);
+ mtd_device_parse_register(mtd, NULL, 0, NULL, 0);
drvdata->mtds[i] = mtd;
}
@@ -421,7 +416,6 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev)
txx9_priv = chip->priv;
nand_release(mtd);
- kfree(drvdata->parts[i]);
kfree(txx9_priv->mtdname);
kfree(txx9_priv);
}
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c
index b155666acfbe..cda77b562ad4 100644
--- a/drivers/mtd/nftlcore.c
+++ b/drivers/mtd/nftlcore.c
@@ -63,14 +63,12 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
return;
}
- DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
+ pr_debug("NFTL: add_mtd for %s\n", mtd->name);
nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
- if (!nftl) {
- printk(KERN_WARNING "NFTL: out of memory for data structures\n");
+ if (!nftl)
return;
- }
nftl->mbd.mtd = mtd;
nftl->mbd.devnum = -1;
@@ -132,7 +130,7 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
{
struct NFTLrecord *nftl = (void *)dev;
- DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);
+ pr_debug("NFTL: remove_dev (i=%d)\n", dev->devnum);
del_mtd_blktrans_dev(dev);
kfree(nftl->ReplUnitTable);
@@ -149,7 +147,7 @@ int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
struct mtd_oob_ops ops;
int res;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = offs & mask;
ops.ooblen = len;
ops.oobbuf = buf;
@@ -170,7 +168,7 @@ int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
struct mtd_oob_ops ops;
int res;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = offs & mask;
ops.ooblen = len;
ops.oobbuf = buf;
@@ -193,7 +191,7 @@ static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
struct mtd_oob_ops ops;
int res;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = offs & mask;
ops.ooblen = mtd->oobsize;
ops.oobbuf = oob;
@@ -220,7 +218,7 @@ static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
/* Normally, we force a fold to happen before we run out of free blocks completely */
if (!desperate && nftl->numfreeEUNs < 2) {
- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
+ pr_debug("NFTL_findfreeblock: there are too few free EUNs\n");
return BLOCK_NIL;
}
@@ -291,8 +289,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
if (block == 2) {
foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
if (foldmark == FOLD_MARK_IN_PROGRESS) {
- DEBUG(MTD_DEBUG_LEVEL1,
- "Write Inhibited on EUN %d\n", thisEUN);
+ pr_debug("Write Inhibited on EUN %d\n", thisEUN);
inplace = 0;
} else {
/* There's no other reason not to do inplace,
@@ -357,7 +354,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
if (BlockLastState[block] != SECTOR_FREE &&
BlockMap[block] != BLOCK_NIL &&
BlockMap[block] != targetEUN) {
- DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
+ pr_debug("Setting inplace to 0. VUC %d, "
"block %d was %x lastEUN, "
"and is in EUN %d (%s) %d\n",
thisVUC, block, BlockLastState[block],
@@ -373,14 +370,14 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
SECTOR_FREE) {
- DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
+ pr_debug("Pending write not free in EUN %d. "
"Folding out of place.\n", targetEUN);
inplace = 0;
}
}
if (!inplace) {
- DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
+ pr_debug("Cannot fold Virtual Unit Chain %d in place. "
"Trying out-of-place\n", thisVUC);
/* We need to find a targetEUN to fold into. */
targetEUN = NFTL_findfreeblock(nftl, 1);
@@ -410,7 +407,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
and the Erase Unit into which we are supposed to be copying.
Go for it.
*/
- DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
+ pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN);
for (block = 0; block < nftl->EraseSize / 512 ; block++) {
unsigned char movebuf[512];
int ret;
@@ -428,7 +425,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
512, &retlen, movebuf);
- if (ret < 0 && ret != -EUCLEAN) {
+ if (ret < 0 && !mtd_is_bitflip(ret)) {
ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
+ (block * 512), 512, &retlen,
movebuf);
@@ -457,7 +454,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
has duplicate chains, we need to free one of the chains because it's not necessary any more.
*/
thisEUN = nftl->EUNtable[thisVUC];
- DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
+ pr_debug("Want to erase\n");
/* For each block in the old chain (except the targetEUN of course),
free it and make it available for future use */
@@ -570,7 +567,7 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
(writeEUN * nftl->EraseSize) + blockofs,
8, &retlen, (char *)&bci);
- DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
+ pr_debug("Status of block %d in EUN %d is %x\n",
block , writeEUN, le16_to_cpu(bci.Status));
status = bci.Status | bci.Status1;
@@ -623,7 +620,7 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
but they are reserved for when we're
desperate. Well, now we're desperate.
*/
- DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
+ pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
writeEUN = NFTL_findfreeblock(nftl, 1);
}
if (writeEUN == BLOCK_NIL) {
@@ -776,7 +773,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
size_t retlen;
int res = mtd->read(mtd, ptr, 512, &retlen, buffer);
- if (res < 0 && res != -EUCLEAN)
+ if (res < 0 && !mtd_is_bitflip(res))
return -EIO;
}
return 0;
diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c
index e3cd1ffad2f6..ac4092591aea 100644
--- a/drivers/mtd/nftlmount.c
+++ b/drivers/mtd/nftlmount.c
@@ -32,7 +32,7 @@
/* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
* various device information of the NFTL partition and Bad Unit Table. Update
- * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[]
+ * the ReplUnitTable[] table according to the Bad Unit Table. ReplUnitTable[]
* is used for management of Erase Unit in other routines in nftl.c and nftlmount.c
*/
static int find_boot_record(struct NFTLrecord *nftl)
@@ -297,7 +297,7 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int
*
* Return: 0 when succeed, -1 on error.
*
- * ToDo: 1. Is it neceressary to check_free_sector after erasing ??
+ * ToDo: 1. Is it necessary to check_free_sector after erasing ??
*/
int NFTL_formatblock(struct NFTLrecord *nftl, int block)
{
@@ -337,7 +337,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
nb_erases = le32_to_cpu(uci.WearInfo);
nb_erases++;
- /* wrap (almost impossible with current flashs) or free block */
+ /* wrap (almost impossible with current flash) or free block */
if (nb_erases == 0)
nb_erases = 1;
@@ -363,10 +363,10 @@ fail:
* Mark as 'IGNORE' each incorrect sector. This check is only done if the chain
* was being folded when NFTL was interrupted.
*
- * The check_free_sectors in this function is neceressary. There is a possible
+ * The check_free_sectors in this function is necessary. There is a possible
* situation that after writing the Data area, the Block Control Information is
* not updated according (due to power failure or something) which leaves the block
- * in an umconsistent state. So we have to check if a block is really FREE in this
+ * in an inconsistent state. So we have to check if a block is really FREE in this
* case. */
static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
{
@@ -428,7 +428,7 @@ static int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block)
for (;;) {
length++;
- /* avoid infinite loops, although this is guaranted not to
+ /* avoid infinite loops, although this is guaranteed not to
happen because of the previous checks */
if (length >= nftl->nb_blocks) {
printk("nftl: length too long %d !\n", length);
@@ -447,11 +447,11 @@ static int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block)
/* format_chain: Format an invalid Virtual Unit chain. It frees all the Erase Units in a
* Virtual Unit Chain, i.e. all the units are disconnected.
*
- * It is not stricly correct to begin from the first block of the chain because
+ * It is not strictly correct to begin from the first block of the chain because
* if we stop the code, we may see again a valid chain if there was a first_block
* flag in a block inside it. But is it really a problem ?
*
- * FixMe: Figure out what the last statesment means. What if power failure when we are
+ * FixMe: Figure out what the last statement means. What if power failure when we are
* in the for (;;) loop formatting blocks ??
*/
static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
@@ -485,7 +485,7 @@ static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
* totally free (only 0xff).
*
* Definition: Free Erase Unit -- A properly erased/formatted Free Erase Unit should have meet the
- * following critia:
+ * following criteria:
* 1. */
static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
{
@@ -502,7 +502,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
if (erase_mark != ERASE_MARK) {
/* if no erase mark, the block must be totally free. This is
- possible in two cases : empty filsystem or interrupted erase (very unlikely) */
+ possible in two cases : empty filesystem or interrupted erase (very unlikely) */
if (check_free_sectors (nftl, block * nftl->EraseSize, nftl->EraseSize, 1) != 0)
return -1;
@@ -544,7 +544,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
/* get_fold_mark: Read fold mark from Unit Control Information #2, we use FOLD_MARK_IN_PROGRESS
* to indicate that we are in the progression of a Virtual Unit Chain folding. If the UCI #2
* is FOLD_MARK_IN_PROGRESS when mounting the NFTL, the (previous) folding process is interrupted
- * for some reason. A clean up/check of the VUC is neceressary in this case.
+ * for some reason. A clean up/check of the VUC is necessary in this case.
*
* WARNING: return 0 if read error
*/
@@ -657,7 +657,7 @@ int NFTL_mount(struct NFTLrecord *s)
printk("Block %d: incorrect logical block: %d expected: %d\n",
block, logical_block, first_logical_block);
/* the chain is incorrect : we must format it,
- but we need to read it completly */
+ but we need to read it completely */
do_format_chain = 1;
}
if (is_first_block) {
@@ -669,7 +669,7 @@ int NFTL_mount(struct NFTLrecord *s)
printk("Block %d: incorrectly marked as first block in chain\n",
block);
/* the chain is incorrect : we must format it,
- but we need to read it completly */
+ but we need to read it completely */
do_format_chain = 1;
} else {
printk("Block %d: folding in progress - ignoring first block flag\n",
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c
index a996718fa6b0..64be8f0848b0 100644
--- a/drivers/mtd/ofpart.c
+++ b/drivers/mtd/ofpart.c
@@ -20,14 +20,23 @@
#include <linux/slab.h>
#include <linux/mtd/partitions.h>
-int __devinit of_mtd_parse_partitions(struct device *dev,
- struct device_node *node,
- struct mtd_partition **pparts)
+static int parse_ofpart_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
{
+ struct device_node *node;
const char *partname;
struct device_node *pp;
int nr_parts, i;
+
+ if (!data)
+ return 0;
+
+ node = data->of_node;
+ if (!node)
+ return 0;
+
/* First count the subnodes */
pp = NULL;
nr_parts = 0;
@@ -69,7 +78,7 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
if (!i) {
of_node_put(pp);
- dev_err(dev, "No valid partition found on %s\n", node->full_name);
+ pr_err("No valid partition found on %s\n", node->full_name);
kfree(*pparts);
*pparts = NULL;
return -EINVAL;
@@ -77,6 +86,99 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
return nr_parts;
}
-EXPORT_SYMBOL(of_mtd_parse_partitions);
+
+static struct mtd_part_parser ofpart_parser = {
+ .owner = THIS_MODULE,
+ .parse_fn = parse_ofpart_partitions,
+ .name = "ofpart",
+};
+
+static int parse_ofoldpart_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+{
+ struct device_node *dp;
+ int i, plen, nr_parts;
+ const struct {
+ __be32 offset, len;
+ } *part;
+ const char *names;
+
+ if (!data)
+ return 0;
+
+ dp = data->of_node;
+ if (!dp)
+ return 0;
+
+ part = of_get_property(dp, "partitions", &plen);
+ if (!part)
+ return 0; /* No partitions found */
+
+ pr_warning("Device tree uses obsolete partition map binding: %s\n",
+ dp->full_name);
+
+ nr_parts = plen / sizeof(part[0]);
+
+ *pparts = kzalloc(nr_parts * sizeof(*(*pparts)), GFP_KERNEL);
+ if (!pparts)
+ return -ENOMEM;
+
+ names = of_get_property(dp, "partition-names", &plen);
+
+ for (i = 0; i < nr_parts; i++) {
+ (*pparts)[i].offset = be32_to_cpu(part->offset);
+ (*pparts)[i].size = be32_to_cpu(part->len) & ~1;
+ /* bit 0 set signifies read only partition */
+ if (be32_to_cpu(part->len) & 1)
+ (*pparts)[i].mask_flags = MTD_WRITEABLE;
+
+ if (names && (plen > 0)) {
+ int len = strlen(names) + 1;
+
+ (*pparts)[i].name = (char *)names;
+ plen -= len;
+ names += len;
+ } else {
+ (*pparts)[i].name = "unnamed";
+ }
+
+ part++;
+ }
+
+ return nr_parts;
+}
+
+static struct mtd_part_parser ofoldpart_parser = {
+ .owner = THIS_MODULE,
+ .parse_fn = parse_ofoldpart_partitions,
+ .name = "ofoldpart",
+};
+
+static int __init ofpart_parser_init(void)
+{
+ int rc;
+ rc = register_mtd_parser(&ofpart_parser);
+ if (rc)
+ goto out;
+
+ rc = register_mtd_parser(&ofoldpart_parser);
+ if (!rc)
+ return 0;
+
+ deregister_mtd_parser(&ofoldpart_parser);
+out:
+ return rc;
+}
+
+module_init(ofpart_parser_init);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree");
+MODULE_AUTHOR("Vitaly Wool, David Gibson");
+/*
+ * When MTD core cannot find the requested parser, it tries to load the module
+ * with the same name. Since we provide the ofoldpart parser, we should have
+ * the corresponding alias.
+ */
+MODULE_ALIAS("ofoldpart");
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c
index 2d70d354d846..7813095264a5 100644
--- a/drivers/mtd/onenand/generic.c
+++ b/drivers/mtd/onenand/generic.c
@@ -30,11 +30,8 @@
*/
#define DRIVER_NAME "onenand-flash"
-static const char *part_probes[] = { "cmdlinepart", NULL, };
-
struct onenand_info {
struct mtd_info mtd;
- struct mtd_partition *parts;
struct onenand_chip onenand;
};
@@ -73,13 +70,9 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev)
goto out_iounmap;
}
- err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
- if (err > 0)
- mtd_device_register(&info->mtd, info->parts, err);
- else if (err <= 0 && pdata && pdata->parts)
- mtd_device_register(&info->mtd, pdata->parts, pdata->nr_parts);
- else
- err = mtd_device_register(&info->mtd, NULL, 0);
+ err = mtd_device_parse_register(&info->mtd, NULL, 0,
+ pdata ? pdata->parts : NULL,
+ pdata ? pdata->nr_parts : 0);
platform_set_drvdata(pdev, info);
@@ -104,7 +97,6 @@ static int __devexit generic_onenand_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
if (info) {
- mtd_device_unregister(&info->mtd);
onenand_release(&info->mtd);
release_mem_region(res->start, size);
iounmap(info->onenand.base);
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index a916dec29215..7e9ea6852b67 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -40,7 +40,7 @@
#include <asm/mach/flash.h>
#include <plat/gpmc.h>
#include <plat/onenand.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#include <plat/dma.h>
@@ -57,7 +57,6 @@ struct omap2_onenand {
unsigned long phys_base;
int gpio_irq;
struct mtd_info mtd;
- struct mtd_partition *parts;
struct onenand_chip onenand;
struct completion irq_done;
struct completion dma_done;
@@ -67,8 +66,6 @@ struct omap2_onenand {
struct regulator *regulator;
};
-static const char *part_probes[] = { "cmdlinepart", NULL, };
-
static void omap2_onenand_dma_cb(int lch, u16 ch_status, void *data)
{
struct omap2_onenand *c = data;
@@ -741,6 +738,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
c->regulator = regulator_get(&pdev->dev, "vonenand");
if (IS_ERR(c->regulator)) {
dev_err(&pdev->dev, "Failed to get regulator\n");
+ r = PTR_ERR(c->regulator);
goto err_release_dma;
}
c->onenand.enable = omap2_onenand_enable;
@@ -753,13 +751,9 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
if ((r = onenand_scan(&c->mtd, 1)) < 0)
goto err_release_regulator;
- r = parse_mtd_partitions(&c->mtd, part_probes, &c->parts, 0);
- if (r > 0)
- r = mtd_device_register(&c->mtd, c->parts, r);
- else if (pdata->parts != NULL)
- r = mtd_device_register(&c->mtd, pdata->parts, pdata->nr_parts);
- else
- r = mtd_device_register(&c->mtd, NULL, 0);
+ r = mtd_device_parse_register(&c->mtd, NULL, 0,
+ pdata ? pdata->parts : NULL,
+ pdata ? pdata->nr_parts : 0);
if (r)
goto err_release_onenand;
@@ -786,7 +780,6 @@ err_release_mem_region:
err_free_cs:
gpmc_cs_free(c->gpmc_cs);
err_kfree:
- kfree(c->parts);
kfree(c);
return r;
@@ -809,7 +802,6 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev)
iounmap(c->onenand.base);
release_mem_region(c->phys_base, ONENAND_IO_SIZE);
gpmc_cs_free(c->gpmc_cs);
- kfree(c->parts);
kfree(c);
return 0;
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index ac9e959802a7..a8394730b4b6 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -1015,7 +1015,7 @@ static void onenand_release_device(struct mtd_info *mtd)
}
/**
- * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer
* @param mtd MTD device structure
* @param buf destination address
* @param column oob offset to read from
@@ -1079,7 +1079,7 @@ static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
return status;
/* check if we failed due to uncorrectable error */
- if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR)
+ if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR)
return status;
/* check if address lies in MLC region */
@@ -1122,10 +1122,10 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
int ret = 0;
int writesize = this->writesize;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n",
- __func__, (unsigned int) from, (int) len);
+ pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
+ (int)len);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
@@ -1159,7 +1159,7 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
if (unlikely(ret))
ret = onenand_recover_lsb(mtd, from, ret);
onenand_update_bufferram(mtd, from, !ret);
- if (ret == -EBADMSG)
+ if (mtd_is_eccerr(ret))
ret = 0;
if (ret)
break;
@@ -1170,7 +1170,7 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
thisooblen = oobsize - oobcolumn;
thisooblen = min_t(int, thisooblen, ooblen - oobread);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
else
this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
@@ -1226,10 +1226,10 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
int ret = 0, boundary = 0;
int writesize = this->writesize;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n",
- __func__, (unsigned int) from, (int) len);
+ pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
+ (int)len);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
@@ -1255,7 +1255,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
this->command(mtd, ONENAND_CMD_READ, from, writesize);
ret = this->wait(mtd, FL_READING);
onenand_update_bufferram(mtd, from, !ret);
- if (ret == -EBADMSG)
+ if (mtd_is_eccerr(ret))
ret = 0;
}
}
@@ -1291,7 +1291,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
thisooblen = oobsize - oobcolumn;
thisooblen = min_t(int, thisooblen, ooblen - oobread);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
else
this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
@@ -1315,7 +1315,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
/* Now wait for load */
ret = this->wait(mtd, FL_READING);
onenand_update_bufferram(mtd, from, !ret);
- if (ret == -EBADMSG)
+ if (mtd_is_eccerr(ret))
ret = 0;
}
@@ -1351,19 +1351,19 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
struct mtd_ecc_stats stats;
int read = 0, thislen, column, oobsize;
size_t len = ops->ooblen;
- mtd_oob_mode_t mode = ops->mode;
+ unsigned int mode = ops->mode;
u_char *buf = ops->oobbuf;
int ret = 0, readcmd;
from += ops->ooboffs;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n",
- __func__, (unsigned int) from, (int) len);
+ pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
+ (int)len);
/* Initialize return length value */
ops->oobretlen = 0;
- if (mode == MTD_OOB_AUTO)
+ if (mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
@@ -1403,13 +1403,13 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
if (unlikely(ret))
ret = onenand_recover_lsb(mtd, from, ret);
- if (ret && ret != -EBADMSG) {
+ if (ret && !mtd_is_eccerr(ret)) {
printk(KERN_ERR "%s: read failed = 0x%x\n",
__func__, ret);
break;
}
- if (mode == MTD_OOB_AUTO)
+ if (mode == MTD_OPS_AUTO_OOB)
onenand_transfer_auto_oob(mtd, buf, column, thislen);
else
this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
@@ -1487,10 +1487,10 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
int ret;
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
break;
- case MTD_OOB_RAW:
+ case MTD_OPS_RAW:
/* Not implemented yet */
default:
return -EINVAL;
@@ -1576,8 +1576,8 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
size_t len = ops->ooblen;
u_char *buf = ops->oobbuf;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %zi\n",
- __func__, (unsigned int) from, len);
+ pr_debug("%s: from = 0x%08x, len = %zi\n", __func__, (unsigned int)from,
+ len);
/* Initialize return value */
ops->oobretlen = 0;
@@ -1750,8 +1750,8 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
/* Wait for any existing operation to clear */
onenand_panic_wait(mtd);
- DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
- __func__, (unsigned int) to, (int) len);
+ pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
+ (int)len);
/* Initialize retlen, in case of early exit */
*retlen = 0;
@@ -1821,7 +1821,7 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
}
/**
- * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer
* @param mtd MTD device structure
* @param oob_buf oob buffer
* @param buf source address
@@ -1883,8 +1883,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
u_char *oobbuf;
int ret = 0, cmd;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
- __func__, (unsigned int) to, (int) len);
+ pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
+ (int)len);
/* Initialize retlen, in case of early exit */
ops->retlen = 0;
@@ -1908,7 +1908,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
if (!len)
return 0;
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
@@ -1945,7 +1945,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(oobbuf, 0xff, mtd->oobsize);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob, thisooblen);
@@ -2055,7 +2055,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
/**
- * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
+ * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
@@ -2074,17 +2074,17 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
u_char *oobbuf;
size_t len = ops->ooblen;
const u_char *buf = ops->oobbuf;
- mtd_oob_mode_t mode = ops->mode;
+ unsigned int mode = ops->mode;
to += ops->ooboffs;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
- __func__, (unsigned int) to, (int) len);
+ pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
+ (int)len);
/* Initialize retlen, in case of early exit */
ops->oobretlen = 0;
- if (mode == MTD_OOB_AUTO)
+ if (mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
@@ -2128,7 +2128,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(oobbuf, 0xff, mtd->oobsize);
- if (mode == MTD_OOB_AUTO)
+ if (mode == MTD_OPS_AUTO_OOB)
onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
else
memcpy(oobbuf + column, buf, thislen);
@@ -2217,10 +2217,10 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
int ret;
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
break;
- case MTD_OOB_RAW:
+ case MTD_OPS_RAW:
/* Not implemented yet */
default:
return -EINVAL;
@@ -2281,7 +2281,7 @@ static int onenand_multiblock_erase_verify(struct mtd_info *mtd,
}
/**
- * onenand_multiblock_erase - [Internal] erase block(s) using multiblock erase
+ * onenand_multiblock_erase - [INTERN] erase block(s) using multiblock erase
* @param mtd MTD device structure
* @param instr erase instruction
* @param region erase region
@@ -2397,7 +2397,7 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
/**
- * onenand_block_by_block_erase - [Internal] erase block(s) using regular erase
+ * onenand_block_by_block_erase - [INTERN] erase block(s) using regular erase
* @param mtd MTD device structure
* @param instr erase instruction
* @param region erase region
@@ -2489,8 +2489,9 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
struct mtd_erase_region_info *region = NULL;
loff_t region_offset = 0;
- DEBUG(MTD_DEBUG_LEVEL3, "%s: start=0x%012llx, len=%llu\n", __func__,
- (unsigned long long) instr->addr, (unsigned long long) instr->len);
+ pr_debug("%s: start=0x%012llx, len=%llu\n", __func__,
+ (unsigned long long)instr->addr,
+ (unsigned long long)instr->len);
/* Do not allow erase past end of device */
if (unlikely((len + addr) > mtd->size)) {
@@ -2558,7 +2559,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
*/
static void onenand_sync(struct mtd_info *mtd)
{
- DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__);
+ pr_debug("%s: called\n", __func__);
/* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_SYNCING);
@@ -2602,7 +2603,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
struct bbm_info *bbm = this->bbm;
u_char buf[2] = {0, 0};
struct mtd_oob_ops ops = {
- .mode = MTD_OOB_PLACE,
+ .mode = MTD_OPS_PLACE_OOB,
.ooblen = 2,
.oobbuf = buf,
.ooboffs = 0,
@@ -2922,7 +2923,7 @@ static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
}
/**
- * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
+ * onenand_otp_write_oob_nolock - [INTERN] OneNAND write out-of-band, specific to OTP
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
@@ -3170,7 +3171,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
this->wait(mtd, FL_RESETING);
} else {
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooblen = len;
ops.oobbuf = buf;
ops.ooboffs = 0;
@@ -3429,6 +3430,19 @@ static void onenand_check_features(struct mtd_info *mtd)
else if (numbufs == 1) {
this->options |= ONENAND_HAS_4KB_PAGE;
this->options |= ONENAND_HAS_CACHE_PROGRAM;
+ /*
+ * There are two different 4KiB pagesize chips
+ * and no way to detect it by H/W config values.
+ *
+ * To detect the correct NOP for each chips,
+ * It should check the version ID as workaround.
+ *
+ * Now it has as following
+ * KFM4G16Q4M has NOP 4 with version ID 0x0131
+ * KFM4G16Q5M has NOP 1 with versoin ID 0x013e
+ */
+ if ((this->version_id & 0xf) == 0xe)
+ this->options |= ONENAND_HAS_NOP_1;
}
case ONENAND_DEVICE_DENSITY_2Gb:
@@ -3663,7 +3677,7 @@ static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int
int i, ret;
int block;
struct mtd_oob_ops ops = {
- .mode = MTD_OOB_PLACE,
+ .mode = MTD_OPS_PLACE_OOB,
.ooboffs = 0,
.ooblen = mtd->oobsize,
.datbuf = NULL,
@@ -4054,6 +4068,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
this->ecclayout = &onenand_oob_128;
mtd->subpage_sft = 2;
}
+ if (ONENAND_IS_NOP_1(this))
+ mtd->subpage_sft = 0;
break;
case 64:
this->ecclayout = &onenand_oob_64;
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index fc2c16a0fd1c..66fe3b7e7851 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
+#include <linux/export.h>
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -80,7 +81,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
startblock = 0;
from = 0;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooblen = readlen;
ops.oobbuf = buf;
ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
@@ -153,7 +154,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
block = (int) (onenand_block(this, offs) << 1);
res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
- DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+ pr_debug("onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
(unsigned int) offs, block >> 1, res);
switch ((int) res) {
@@ -188,10 +189,8 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
len = this->chipsize >> (this->erase_shift + 2);
/* Allocate memory (2bit per block) and clear the memory bad block table */
bbm->bbt = kzalloc(len, GFP_KERNEL);
- if (!bbm->bbt) {
- printk(KERN_ERR "onenand_scan_bbt: Out of memory\n");
+ if (!bbm->bbt)
return -ENOMEM;
- }
/* Set the bad block position */
bbm->badblockpos = ONENAND_BADBLOCK_POS;
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c
index 3306b5b3c736..5474547eafc2 100644
--- a/drivers/mtd/onenand/samsung.c
+++ b/drivers/mtd/onenand/samsung.c
@@ -147,7 +147,6 @@ struct s3c_onenand {
struct resource *dma_res;
unsigned long phys_base;
struct completion complete;
- struct mtd_partition *parts;
};
#define CMD_MAP_00(dev, addr) (dev->cmd_map(MAP_00, ((addr) << 1)))
@@ -157,8 +156,6 @@ struct s3c_onenand {
static struct s3c_onenand *onenand;
-static const char *part_probes[] = { "cmdlinepart", NULL, };
-
static inline int s3c_read_reg(int offset)
{
return readl(onenand->base + offset);
@@ -1017,13 +1014,9 @@ static int s3c_onenand_probe(struct platform_device *pdev)
if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ)
dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n");
- err = parse_mtd_partitions(mtd, part_probes, &onenand->parts, 0);
- if (err > 0)
- mtd_device_register(mtd, onenand->parts, err);
- else if (err <= 0 && pdata && pdata->parts)
- mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
- else
- err = mtd_device_register(mtd, NULL, 0);
+ err = mtd_device_parse_register(mtd, NULL, 0,
+ pdata ? pdata->parts : NULL,
+ pdata ? pdata->nr_parts : 0);
platform_set_drvdata(pdev, mtd);
diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c
index 7a87d07cd79f..e366b1d84ead 100644
--- a/drivers/mtd/redboot.c
+++ b/drivers/mtd/redboot.c
@@ -28,6 +28,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
+#include <linux/module.h>
struct fis_image_desc {
unsigned char name[16]; // Null terminated name
@@ -56,8 +57,8 @@ static inline int redboot_checksum(struct fis_image_desc *img)
}
static int parse_redboot_partitions(struct mtd_info *master,
- struct mtd_partition **pparts,
- unsigned long fis_origin)
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
{
int nrparts = 0;
struct fis_image_desc *buf;
@@ -197,11 +198,10 @@ static int parse_redboot_partitions(struct mtd_info *master,
goto out;
}
new_fl->img = &buf[i];
- if (fis_origin) {
- buf[i].flash_base -= fis_origin;
- } else {
- buf[i].flash_base &= master->size-1;
- }
+ if (data && data->origin)
+ buf[i].flash_base -= data->origin;
+ else
+ buf[i].flash_base &= master->size-1;
/* I'm sure the JFFS2 code has done me permanent damage.
* I now think the following is _normal_
@@ -297,6 +297,9 @@ static struct mtd_part_parser redboot_parser = {
.name = "RedBoot",
};
+/* mtd parsers will request the module by parser name */
+MODULE_ALIAS("RedBoot");
+
static int __init redboot_parser_init(void)
{
return register_mtd_parser(&redboot_parser);
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c
index cc4d1805b864..73ae217a4252 100644
--- a/drivers/mtd/rfd_ftl.c
+++ b/drivers/mtd/rfd_ftl.c
@@ -18,6 +18,7 @@
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
+#include <linux/module.h>
#include <asm/types.h>
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index ed3d6cd2c6dc..fddb714e323c 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -34,7 +34,7 @@ module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level (0-2)");
-/* ------------------- sysfs attributtes ---------------------------------- */
+/* ------------------- sysfs attributes ---------------------------------- */
struct sm_sysfs_attribute {
struct device_attribute dev_attr;
char *data;
@@ -138,7 +138,7 @@ static int sm_get_lba(uint8_t *lba)
if ((lba[0] & 0xF8) != 0x10)
return -2;
- /* check parity - endianess doesn't matter */
+ /* check parity - endianness doesn't matter */
if (hweight16(*(uint16_t *)lba) & 1)
return -2;
@@ -147,7 +147,7 @@ static int sm_get_lba(uint8_t *lba)
/*
- * Read LBA asscociated with block
+ * Read LBA associated with block
* returns -1, if block is erased
* returns -2 if error happens
*/
@@ -252,11 +252,11 @@ static int sm_read_sector(struct sm_ftl *ftl,
return 0;
}
- /* User might not need the oob, but we do for data vertification */
+ /* User might not need the oob, but we do for data verification */
if (!oob)
oob = &tmp_oob;
- ops.mode = ftl->smallpagenand ? MTD_OOB_RAW : MTD_OOB_PLACE;
+ ops.mode = ftl->smallpagenand ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB;
ops.ooboffs = 0;
ops.ooblen = SM_OOB_SIZE;
ops.oobbuf = (void *)oob;
@@ -276,12 +276,12 @@ again:
return ret;
}
- /* Unfortunelly, oob read will _always_ succeed,
+ /* Unfortunately, oob read will _always_ succeed,
despite card removal..... */
ret = mtd->read_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops);
/* Test for unknown errors */
- if (ret != 0 && ret != -EUCLEAN && ret != -EBADMSG) {
+ if (ret != 0 && !mtd_is_bitflip_or_eccerr(ret)) {
dbg("read of block %d at zone %d, failed due to error (%d)",
block, zone, ret);
goto again;
@@ -306,7 +306,7 @@ again:
}
/* Test ECC*/
- if (ret == -EBADMSG ||
+ if (mtd_is_eccerr(ret) ||
(ftl->smallpagenand && sm_correct_sector(buffer, oob))) {
dbg("read of block %d at zone %d, failed due to ECC error",
@@ -336,7 +336,7 @@ static int sm_write_sector(struct sm_ftl *ftl,
if (ftl->unstable)
return -EIO;
- ops.mode = ftl->smallpagenand ? MTD_OOB_RAW : MTD_OOB_PLACE;
+ ops.mode = ftl->smallpagenand ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB;
ops.len = SM_SECTOR_SIZE;
ops.datbuf = buffer;
ops.ooboffs = 0;
@@ -447,14 +447,14 @@ static void sm_mark_block_bad(struct sm_ftl *ftl, int zone, int block)
/* We aren't checking the return value, because we don't care */
/* This also fails on fake xD cards, but I guess these won't expose
- any bad blocks till fail completly */
+ any bad blocks till fail completely */
for (boffset = 0; boffset < ftl->block_size; boffset += SM_SECTOR_SIZE)
sm_write_sector(ftl, zone, block, boffset, NULL, &oob);
}
/*
* Erase a block within a zone
- * If erase succedes, it updates free block fifo, otherwise marks block as bad
+ * If erase succeeds, it updates free block fifo, otherwise marks block as bad
*/
static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block,
int put_free)
@@ -510,7 +510,7 @@ static void sm_erase_callback(struct erase_info *self)
complete(&ftl->erase_completion);
}
-/* Throughtly test that block is valid. */
+/* Thoroughly test that block is valid. */
static int sm_check_block(struct sm_ftl *ftl, int zone, int block)
{
int boffset;
@@ -526,7 +526,7 @@ static int sm_check_block(struct sm_ftl *ftl, int zone, int block)
for (boffset = 0; boffset < ftl->block_size;
boffset += SM_SECTOR_SIZE) {
- /* This shoudn't happen anyway */
+ /* This shouldn't happen anyway */
if (sm_read_sector(ftl, zone, block, boffset, NULL, &oob))
return -2;
diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c
index 5cd189793332..976e3d28b962 100644
--- a/drivers/mtd/ssfdc.c
+++ b/drivers/mtd/ssfdc.c
@@ -135,8 +135,7 @@ static int get_valid_cis_sector(struct mtd_info *mtd)
/* Found */
cis_sector = (int)(offset >> SECTOR_SHIFT);
} else {
- DEBUG(MTD_DEBUG_LEVEL1,
- "SSFDC_RO: CIS/IDI sector not found"
+ pr_debug("SSFDC_RO: CIS/IDI sector not found"
" on %s (mtd%d)\n", mtd->name,
mtd->index);
}
@@ -170,7 +169,7 @@ static int read_raw_oob(struct mtd_info *mtd, loff_t offs, uint8_t *buf)
struct mtd_oob_ops ops;
int ret;
- ops.mode = MTD_OOB_RAW;
+ ops.mode = MTD_OPS_RAW;
ops.ooboffs = 0;
ops.ooblen = OOB_SIZE;
ops.oobbuf = buf;
@@ -221,8 +220,7 @@ static int get_logical_address(uint8_t *oob_buf)
block_address >>= 1;
if (get_parity(block_address, 10) != parity) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "SSFDC_RO: logical address field%d"
+ pr_debug("SSFDC_RO: logical address field%d"
"parity error(0x%04X)\n", j+1,
block_address);
} else {
@@ -235,7 +233,7 @@ static int get_logical_address(uint8_t *oob_buf)
if (!ok)
block_address = -2;
- DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: get_logical_address() %d\n",
+ pr_debug("SSFDC_RO: get_logical_address() %d\n",
block_address);
return block_address;
@@ -249,7 +247,7 @@ static int build_logical_block_map(struct ssfdcr_record *ssfdc)
int ret, block_address, phys_block;
struct mtd_info *mtd = ssfdc->mbd.mtd;
- DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: build_block_map() nblks=%d (%luK)\n",
+ pr_debug("SSFDC_RO: build_block_map() nblks=%d (%luK)\n",
ssfdc->map_len,
(unsigned long)ssfdc->map_len * ssfdc->erase_size / 1024);
@@ -262,8 +260,7 @@ static int build_logical_block_map(struct ssfdcr_record *ssfdc)
ret = read_raw_oob(mtd, offset, oob_buf);
if (ret < 0) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "SSFDC_RO: mtd read_oob() failed at %lu\n",
+ pr_debug("SSFDC_RO: mtd read_oob() failed at %lu\n",
offset);
return -1;
}
@@ -279,8 +276,7 @@ static int build_logical_block_map(struct ssfdcr_record *ssfdc)
ssfdc->logic_block_map[block_address] =
(unsigned short)phys_block;
- DEBUG(MTD_DEBUG_LEVEL2,
- "SSFDC_RO: build_block_map() phys_block=%d,"
+ pr_debug("SSFDC_RO: build_block_map() phys_block=%d,"
"logic_block_addr=%d, zone=%d\n",
phys_block, block_address, zone_index);
}
@@ -304,11 +300,8 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
return;
ssfdc = kzalloc(sizeof(struct ssfdcr_record), GFP_KERNEL);
- if (!ssfdc) {
- printk(KERN_WARNING
- "SSFDC_RO: out of memory for data structures\n");
+ if (!ssfdc)
return;
- }
ssfdc->mbd.mtd = mtd;
ssfdc->mbd.devnum = -1;
@@ -319,8 +312,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
ssfdc->erase_size = mtd->erasesize;
ssfdc->map_len = (u32)mtd->size / mtd->erasesize;
- DEBUG(MTD_DEBUG_LEVEL1,
- "SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n",
+ pr_debug("SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n",
ssfdc->cis_block, ssfdc->erase_size, ssfdc->map_len,
DIV_ROUND_UP(ssfdc->map_len, MAX_PHYS_BLK_PER_ZONE));
@@ -331,7 +323,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
ssfdc->cylinders = (unsigned short)(((u32)mtd->size >> SECTOR_SHIFT) /
((long)ssfdc->sectors * (long)ssfdc->heads));
- DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n",
+ pr_debug("SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n",
ssfdc->cylinders, ssfdc->heads , ssfdc->sectors,
(long)ssfdc->cylinders * (long)ssfdc->heads *
(long)ssfdc->sectors);
@@ -342,11 +334,8 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
/* Allocate logical block map */
ssfdc->logic_block_map = kmalloc(sizeof(ssfdc->logic_block_map[0]) *
ssfdc->map_len, GFP_KERNEL);
- if (!ssfdc->logic_block_map) {
- printk(KERN_WARNING
- "SSFDC_RO: out of memory for data structures\n");
+ if (!ssfdc->logic_block_map)
goto out_err;
- }
memset(ssfdc->logic_block_map, 0xff, sizeof(ssfdc->logic_block_map[0]) *
ssfdc->map_len);
@@ -371,7 +360,7 @@ static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
- DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: remove_dev (i=%d)\n", dev->devnum);
+ pr_debug("SSFDC_RO: remove_dev (i=%d)\n", dev->devnum);
del_mtd_blktrans_dev(dev);
kfree(ssfdc->logic_block_map);
@@ -387,8 +376,7 @@ static int ssfdcr_readsect(struct mtd_blktrans_dev *dev,
offset = (int)(logic_sect_no % sectors_per_block);
block_address = (int)(logic_sect_no / sectors_per_block);
- DEBUG(MTD_DEBUG_LEVEL3,
- "SSFDC_RO: ssfdcr_readsect(%lu) sec_per_blk=%d, ofst=%d,"
+ pr_debug("SSFDC_RO: ssfdcr_readsect(%lu) sec_per_blk=%d, ofst=%d,"
" block_addr=%d\n", logic_sect_no, sectors_per_block, offset,
block_address);
@@ -397,8 +385,7 @@ static int ssfdcr_readsect(struct mtd_blktrans_dev *dev,
block_address = ssfdc->logic_block_map[block_address];
- DEBUG(MTD_DEBUG_LEVEL3,
- "SSFDC_RO: ssfdcr_readsect() phys_block_addr=%d\n",
+ pr_debug("SSFDC_RO: ssfdcr_readsect() phys_block_addr=%d\n",
block_address);
if (block_address < 0xffff) {
@@ -407,8 +394,7 @@ static int ssfdcr_readsect(struct mtd_blktrans_dev *dev,
sect_no = (unsigned long)block_address * sectors_per_block +
offset;
- DEBUG(MTD_DEBUG_LEVEL3,
- "SSFDC_RO: ssfdcr_readsect() phys_sect_no=%lu\n",
+ pr_debug("SSFDC_RO: ssfdcr_readsect() phys_sect_no=%lu\n",
sect_no);
if (read_physical_sector(ssfdc->mbd.mtd, buf, sect_no) < 0)
@@ -424,7 +410,7 @@ static int ssfdcr_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
- DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: ssfdcr_getgeo() C=%d, H=%d, S=%d\n",
+ pr_debug("SSFDC_RO: ssfdcr_getgeo() C=%d, H=%d, S=%d\n",
ssfdc->cylinders, ssfdc->heads, ssfdc->sectors);
geo->heads = ssfdc->heads;
diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c
index dec92ae6111a..933f7e5f32d3 100644
--- a/drivers/mtd/tests/mtd_oobtest.c
+++ b/drivers/mtd/tests/mtd_oobtest.c
@@ -30,7 +30,7 @@
#define PRINT_PREF KERN_INFO "mtd_oobtest: "
-static int dev;
+static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -131,7 +131,7 @@ static int write_eraseblock(int ebnum)
for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
set_random_data(writebuf, use_len);
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = use_len;
@@ -184,7 +184,7 @@ static int verify_eraseblock(int ebnum)
for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
set_random_data(writebuf, use_len);
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = use_len;
@@ -211,7 +211,7 @@ static int verify_eraseblock(int ebnum)
if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
int k;
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail;
@@ -276,7 +276,7 @@ static int verify_eraseblock_in_one_go(int ebnum)
size_t len = mtd->ecclayout->oobavail * pgcnt;
set_random_data(writebuf, len);
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = len;
@@ -366,6 +366,13 @@ static int __init mtd_oobtest_init(void)
printk(KERN_INFO "\n");
printk(KERN_INFO "=================================================\n");
+
+ if (dev < 0) {
+ printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n");
+ printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n");
+ return -EINVAL;
+ }
+
printk(PRINT_PREF "MTD device: %d\n", dev);
mtd = get_mtd_device(NULL, dev);
@@ -507,7 +514,7 @@ static int __init mtd_oobtest_init(void)
addr0 += mtd->erasesize;
/* Attempt to write off end of OOB */
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = 1;
@@ -527,7 +534,7 @@ static int __init mtd_oobtest_init(void)
}
/* Attempt to read off end of OOB */
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = 1;
@@ -551,7 +558,7 @@ static int __init mtd_oobtest_init(void)
"block is bad\n");
else {
/* Attempt to write off end of device */
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail + 1;
@@ -571,7 +578,7 @@ static int __init mtd_oobtest_init(void)
}
/* Attempt to read off end of device */
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail + 1;
@@ -595,7 +602,7 @@ static int __init mtd_oobtest_init(void)
goto out;
/* Attempt to write off end of device */
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail;
@@ -615,7 +622,7 @@ static int __init mtd_oobtest_init(void)
}
/* Attempt to read off end of device */
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail;
@@ -655,7 +662,7 @@ static int __init mtd_oobtest_init(void)
addr = (i + 1) * mtd->erasesize - mtd->writesize;
for (pg = 0; pg < cnt; ++pg) {
set_random_data(writebuf, sz);
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = sz;
@@ -683,7 +690,7 @@ static int __init mtd_oobtest_init(void)
continue;
set_random_data(writebuf, mtd->ecclayout->oobavail * 2);
addr = (i + 1) * mtd->erasesize - mtd->writesize;
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail * 2;
diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c
index 00b937e38c1d..afafb6935fd0 100644
--- a/drivers/mtd/tests/mtd_pagetest.c
+++ b/drivers/mtd/tests/mtd_pagetest.c
@@ -30,7 +30,7 @@
#define PRINT_PREF KERN_INFO "mtd_pagetest: "
-static int dev;
+static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -128,7 +128,7 @@ static int verify_eraseblock(int ebnum)
for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
/* Do a read to set the internal dataRAMs to different data */
err = mtd->read(mtd, addr0, bufsize, &read, twopages);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -136,7 +136,7 @@ static int verify_eraseblock(int ebnum)
return err;
}
err = mtd->read(mtd, addrn - bufsize, bufsize, &read, twopages);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -146,7 +146,7 @@ static int verify_eraseblock(int ebnum)
memset(twopages, 0, bufsize);
read = 0;
err = mtd->read(mtd, addr, bufsize, &read, twopages);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -164,7 +164,7 @@ static int verify_eraseblock(int ebnum)
unsigned long oldnext = next;
/* Do a read to set the internal dataRAMs to different data */
err = mtd->read(mtd, addr0, bufsize, &read, twopages);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -172,7 +172,7 @@ static int verify_eraseblock(int ebnum)
return err;
}
err = mtd->read(mtd, addrn - bufsize, bufsize, &read, twopages);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -182,7 +182,7 @@ static int verify_eraseblock(int ebnum)
memset(twopages, 0, bufsize);
read = 0;
err = mtd->read(mtd, addr, bufsize, &read, twopages);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -231,7 +231,7 @@ static int crosstest(void)
read = 0;
addr = addrn - pgsize - pgsize;
err = mtd->read(mtd, addr, pgsize, &read, pp1);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -244,7 +244,7 @@ static int crosstest(void)
read = 0;
addr = addrn - pgsize - pgsize - pgsize;
err = mtd->read(mtd, addr, pgsize, &read, pp1);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -258,7 +258,7 @@ static int crosstest(void)
addr = addr0;
printk(PRINT_PREF "reading page at %#llx\n", (long long)addr);
err = mtd->read(mtd, addr, pgsize, &read, pp2);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -272,7 +272,7 @@ static int crosstest(void)
addr = addrn - pgsize;
printk(PRINT_PREF "reading page at %#llx\n", (long long)addr);
err = mtd->read(mtd, addr, pgsize, &read, pp3);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -286,7 +286,7 @@ static int crosstest(void)
addr = addr0;
printk(PRINT_PREF "reading page at %#llx\n", (long long)addr);
err = mtd->read(mtd, addr, pgsize, &read, pp4);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -345,7 +345,7 @@ static int erasecrosstest(void)
printk(PRINT_PREF "reading 1st page of block %d\n", ebnum);
memset(readbuf, 0, pgsize);
err = mtd->read(mtd, addr0, pgsize, &read, readbuf);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -383,7 +383,7 @@ static int erasecrosstest(void)
printk(PRINT_PREF "reading 1st page of block %d\n", ebnum);
memset(readbuf, 0, pgsize);
err = mtd->read(mtd, addr0, pgsize, &read, readbuf);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -439,7 +439,7 @@ static int erasetest(void)
printk(PRINT_PREF "reading 1st page of block %d\n", ebnum);
err = mtd->read(mtd, addr0, pgsize, &read, twopages);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -504,6 +504,13 @@ static int __init mtd_pagetest_init(void)
printk(KERN_INFO "\n");
printk(KERN_INFO "=================================================\n");
+
+ if (dev < 0) {
+ printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n");
+ printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n");
+ return -EINVAL;
+ }
+
printk(PRINT_PREF "MTD device: %d\n", dev);
mtd = get_mtd_device(NULL, dev);
diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c
index afe71aa15c4b..550fe51225a7 100644
--- a/drivers/mtd/tests/mtd_readtest.c
+++ b/drivers/mtd/tests/mtd_readtest.c
@@ -29,7 +29,7 @@
#define PRINT_PREF KERN_INFO "mtd_readtest: "
-static int dev;
+static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -66,7 +66,7 @@ static int read_eraseblock_by_page(int ebnum)
if (mtd->oobsize) {
struct mtd_oob_ops ops;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->oobsize;
@@ -75,7 +75,8 @@ static int read_eraseblock_by_page(int ebnum)
ops.datbuf = NULL;
ops.oobbuf = oobbuf;
ret = mtd->read_oob(mtd, addr, &ops);
- if (ret || ops.oobretlen != mtd->oobsize) {
+ if ((ret && !mtd_is_bitflip(ret)) ||
+ ops.oobretlen != mtd->oobsize) {
printk(PRINT_PREF "error: read oob failed at "
"%#llx\n", (long long)addr);
if (!err)
@@ -169,6 +170,12 @@ static int __init mtd_readtest_init(void)
printk(KERN_INFO "\n");
printk(KERN_INFO "=================================================\n");
+
+ if (dev < 0) {
+ printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n");
+ return -EINVAL;
+ }
+
printk(PRINT_PREF "MTD device: %d\n", dev);
mtd = get_mtd_device(NULL, dev);
diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c
index 627d4e2466a3..493b367bdd35 100644
--- a/drivers/mtd/tests/mtd_speedtest.c
+++ b/drivers/mtd/tests/mtd_speedtest.c
@@ -29,7 +29,7 @@
#define PRINT_PREF KERN_INFO "mtd_speedtest: "
-static int dev;
+static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -216,7 +216,7 @@ static int read_eraseblock(int ebnum)
err = mtd->read(mtd, addr, mtd->erasesize, &read, iobuf);
/* Ignore corrected ECC errors */
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != mtd->erasesize) {
printk(PRINT_PREF "error: read failed at %#llx\n", addr);
@@ -237,7 +237,7 @@ static int read_eraseblock_by_page(int ebnum)
for (i = 0; i < pgcnt; i++) {
err = mtd->read(mtd, addr, pgsize, &read, buf);
/* Ignore corrected ECC errors */
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -263,7 +263,7 @@ static int read_eraseblock_by_2pages(int ebnum)
for (i = 0; i < n; i++) {
err = mtd->read(mtd, addr, sz, &read, buf);
/* Ignore corrected ECC errors */
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != sz) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -278,7 +278,7 @@ static int read_eraseblock_by_2pages(int ebnum)
if (pgcnt % 2) {
err = mtd->read(mtd, addr, pgsize, &read, buf);
/* Ignore corrected ECC errors */
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
printk(PRINT_PREF "error: read failed at %#llx\n",
@@ -361,6 +361,13 @@ static int __init mtd_speedtest_init(void)
printk(KERN_INFO "\n");
printk(KERN_INFO "=================================================\n");
+
+ if (dev < 0) {
+ printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n");
+ printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n");
+ return -EINVAL;
+ }
+
if (count)
printk(PRINT_PREF "MTD device: %d count: %d\n", dev, count);
else
diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c
index 531625fc9259..52ffd9120e0d 100644
--- a/drivers/mtd/tests/mtd_stresstest.c
+++ b/drivers/mtd/tests/mtd_stresstest.c
@@ -30,7 +30,7 @@
#define PRINT_PREF KERN_INFO "mtd_stresstest: "
-static int dev;
+static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -154,7 +154,7 @@ static int do_read(void)
}
addr = eb * mtd->erasesize + offs;
err = mtd->read(mtd, addr, len, &read, readbuf);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
err = 0;
if (unlikely(err || read != len)) {
printk(PRINT_PREF "error: read failed at 0x%llx\n",
@@ -250,6 +250,13 @@ static int __init mtd_stresstest_init(void)
printk(KERN_INFO "\n");
printk(KERN_INFO "=================================================\n");
+
+ if (dev < 0) {
+ printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n");
+ printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n");
+ return -EINVAL;
+ }
+
printk(PRINT_PREF "MTD device: %d\n", dev);
mtd = get_mtd_device(NULL, dev);
diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c
index 334eae53a3db..1a05bfac4eee 100644
--- a/drivers/mtd/tests/mtd_subpagetest.c
+++ b/drivers/mtd/tests/mtd_subpagetest.c
@@ -29,7 +29,7 @@
#define PRINT_PREF KERN_INFO "mtd_subpagetest: "
-static int dev;
+static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -198,7 +198,7 @@ static int verify_eraseblock(int ebnum)
read = 0;
err = mtd->read(mtd, addr, subpgsize, &read, readbuf);
if (unlikely(err || read != subpgsize)) {
- if (err == -EUCLEAN && read == subpgsize) {
+ if (mtd_is_bitflip(err) && read == subpgsize) {
printk(PRINT_PREF "ECC correction at %#llx\n",
(long long)addr);
err = 0;
@@ -226,7 +226,7 @@ static int verify_eraseblock(int ebnum)
read = 0;
err = mtd->read(mtd, addr, subpgsize, &read, readbuf);
if (unlikely(err || read != subpgsize)) {
- if (err == -EUCLEAN && read == subpgsize) {
+ if (mtd_is_bitflip(err) && read == subpgsize) {
printk(PRINT_PREF "ECC correction at %#llx\n",
(long long)addr);
err = 0;
@@ -264,7 +264,7 @@ static int verify_eraseblock2(int ebnum)
read = 0;
err = mtd->read(mtd, addr, subpgsize * k, &read, readbuf);
if (unlikely(err || read != subpgsize * k)) {
- if (err == -EUCLEAN && read == subpgsize * k) {
+ if (mtd_is_bitflip(err) && read == subpgsize * k) {
printk(PRINT_PREF "ECC correction at %#llx\n",
(long long)addr);
err = 0;
@@ -298,7 +298,7 @@ static int verify_eraseblock_ff(int ebnum)
read = 0;
err = mtd->read(mtd, addr, subpgsize, &read, readbuf);
if (unlikely(err || read != subpgsize)) {
- if (err == -EUCLEAN && read == subpgsize) {
+ if (mtd_is_bitflip(err) && read == subpgsize) {
printk(PRINT_PREF "ECC correction at %#llx\n",
(long long)addr);
err = 0;
@@ -379,6 +379,13 @@ static int __init mtd_subpagetest_init(void)
printk(KERN_INFO "\n");
printk(KERN_INFO "=================================================\n");
+
+ if (dev < 0) {
+ printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n");
+ printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n");
+ return -EINVAL;
+ }
+
printk(PRINT_PREF "MTD device: %d\n", dev);
mtd = get_mtd_device(NULL, dev);
diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c
index 5c6c3d248901..03ab649a6964 100644
--- a/drivers/mtd/tests/mtd_torturetest.c
+++ b/drivers/mtd/tests/mtd_torturetest.c
@@ -46,7 +46,7 @@ static int pgcnt;
module_param(pgcnt, int, S_IRUGO);
MODULE_PARM_DESC(pgcnt, "number of pages per eraseblock to torture (0 => all)");
-static int dev;
+static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -138,7 +138,7 @@ static inline int check_eraseblock(int ebnum, unsigned char *buf)
retry:
err = mtd->read(mtd, addr, len, &read, check_buf);
- if (err == -EUCLEAN)
+ if (mtd_is_bitflip(err))
printk(PRINT_PREF "single bit flip occurred at EB %d "
"MTD reported that it was fixed.\n", ebnum);
else if (err) {
@@ -213,6 +213,13 @@ static int __init tort_init(void)
printk(KERN_INFO "=================================================\n");
printk(PRINT_PREF "Warning: this program is trying to wear out your "
"flash, stop it if this is not wanted.\n");
+
+ if (dev < 0) {
+ printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n");
+ printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n");
+ return -EINVAL;
+ }
+
printk(PRINT_PREF "MTD device: %d\n", dev);
printk(PRINT_PREF "torture %d eraseblocks (%d-%d) of mtd%d\n",
ebcnt, eb, eb + ebcnt - 1, dev);
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 65626c1c446d..6c3fb5ab20f5 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -953,10 +953,14 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
if (!ubi->peb_buf2)
goto out_free;
+ err = ubi_debugging_init_dev(ubi);
+ if (err)
+ goto out_free;
+
err = attach_by_scanning(ubi);
if (err) {
dbg_err("failed to attach by scanning, error %d", err);
- goto out_free;
+ goto out_debugging;
}
if (ubi->autoresize_vol_id != -1) {
@@ -969,12 +973,16 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
if (err)
goto out_detach;
+ err = ubi_debugfs_init_dev(ubi);
+ if (err)
+ goto out_uif;
+
ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
if (IS_ERR(ubi->bgt_thread)) {
err = PTR_ERR(ubi->bgt_thread);
ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name,
err);
- goto out_uif;
+ goto out_debugfs;
}
ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num);
@@ -1008,12 +1016,18 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL);
return ubi_num;
+out_debugfs:
+ ubi_debugfs_exit_dev(ubi);
out_uif:
+ get_device(&ubi->dev);
+ ubi_assert(ref);
uif_close(ubi);
out_detach:
ubi_wl_close(ubi);
free_internal_volumes(ubi);
vfree(ubi->vtbl);
+out_debugging:
+ ubi_debugging_exit_dev(ubi);
out_free:
vfree(ubi->peb_buf1);
vfree(ubi->peb_buf2);
@@ -1080,11 +1094,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
*/
get_device(&ubi->dev);
+ ubi_debugfs_exit_dev(ubi);
uif_close(ubi);
ubi_wl_close(ubi);
free_internal_volumes(ubi);
vfree(ubi->vtbl);
put_mtd_device(ubi->mtd);
+ ubi_debugging_exit_dev(ubi);
vfree(ubi->peb_buf1);
vfree(ubi->peb_buf2);
ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num);
@@ -1199,6 +1215,11 @@ static int __init ubi_init(void)
if (!ubi_wl_entry_slab)
goto out_dev_unreg;
+ err = ubi_debugfs_init();
+ if (err)
+ goto out_slab;
+
+
/* Attach MTD devices */
for (i = 0; i < mtd_devs; i++) {
struct mtd_dev_param *p = &mtd_dev_param[i];
@@ -1247,6 +1268,8 @@ out_detach:
ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
mutex_unlock(&ubi_devices_mutex);
}
+ ubi_debugfs_exit();
+out_slab:
kmem_cache_destroy(ubi_wl_entry_slab);
out_dev_unreg:
misc_deregister(&ubi_ctrl_cdev);
@@ -1270,6 +1293,7 @@ static void __exit ubi_exit(void)
ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1);
mutex_unlock(&ubi_devices_mutex);
}
+ ubi_debugfs_exit();
kmem_cache_destroy(ubi_wl_entry_slab);
misc_deregister(&ubi_ctrl_cdev);
class_remove_file(ubi_class, &ubi_version);
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index 191f3bb3c41a..3320a50ba4f0 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -189,12 +189,16 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)
return new_offset;
}
-static int vol_cdev_fsync(struct file *file, int datasync)
+static int vol_cdev_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
struct ubi_volume_desc *desc = file->private_data;
struct ubi_device *ubi = desc->vol->ubi;
-
- return ubi_sync(ubi->ubi_num);
+ struct inode *inode = file->f_path.dentry->d_inode;
+ int err;
+ mutex_lock(&inode->i_mutex);
+ err = ubi_sync(ubi->ubi_num);
+ mutex_unlock(&inode->i_mutex);
+ return err;
}
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
index 2224cbe41ddf..ab80c0debac8 100644
--- a/drivers/mtd/ubi/debug.c
+++ b/drivers/mtd/ubi/debug.c
@@ -27,17 +27,9 @@
#ifdef CONFIG_MTD_UBI_DEBUG
#include "ubi.h"
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
#include <linux/module.h>
-#include <linux/moduleparam.h>
-
-unsigned int ubi_chk_flags;
-unsigned int ubi_tst_flags;
-
-module_param_named(debug_chks, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
-module_param_named(debug_tsts, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
-
-MODULE_PARM_DESC(debug_chks, "Debug check flags");
-MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
/**
* ubi_dbg_dump_ec_hdr - dump an erase counter header.
@@ -239,4 +231,261 @@ out:
return;
}
+/**
+ * ubi_debugging_init_dev - initialize debugging for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function initializes debugging-related data for UBI device @ubi.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubi_debugging_init_dev(struct ubi_device *ubi)
+{
+ ubi->dbg = kzalloc(sizeof(struct ubi_debug_info), GFP_KERNEL);
+ if (!ubi->dbg)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * ubi_debugging_exit_dev - free debugging data for an UBI device.
+ * @ubi: UBI device description object
+ */
+void ubi_debugging_exit_dev(struct ubi_device *ubi)
+{
+ kfree(ubi->dbg);
+}
+
+/*
+ * Root directory for UBI stuff in debugfs. Contains sub-directories which
+ * contain the stuff specific to particular UBI devices.
+ */
+static struct dentry *dfs_rootdir;
+
+/**
+ * ubi_debugfs_init - create UBI debugfs directory.
+ *
+ * Create UBI debugfs directory. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubi_debugfs_init(void)
+{
+ dfs_rootdir = debugfs_create_dir("ubi", NULL);
+ if (IS_ERR_OR_NULL(dfs_rootdir)) {
+ int err = dfs_rootdir ? -ENODEV : PTR_ERR(dfs_rootdir);
+
+ ubi_err("cannot create \"ubi\" debugfs directory, error %d\n",
+ err);
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * ubi_debugfs_exit - remove UBI debugfs directory.
+ */
+void ubi_debugfs_exit(void)
+{
+ debugfs_remove(dfs_rootdir);
+}
+
+/* Read an UBI debugfs file */
+static ssize_t dfs_file_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long ubi_num = (unsigned long)file->private_data;
+ struct dentry *dent = file->f_path.dentry;
+ struct ubi_device *ubi;
+ struct ubi_debug_info *d;
+ char buf[3];
+ int val;
+
+ ubi = ubi_get_device(ubi_num);
+ if (!ubi)
+ return -ENODEV;
+ d = ubi->dbg;
+
+ if (dent == d->dfs_chk_gen)
+ val = d->chk_gen;
+ else if (dent == d->dfs_chk_io)
+ val = d->chk_io;
+ else if (dent == d->dfs_disable_bgt)
+ val = d->disable_bgt;
+ else if (dent == d->dfs_emulate_bitflips)
+ val = d->emulate_bitflips;
+ else if (dent == d->dfs_emulate_io_failures)
+ val = d->emulate_io_failures;
+ else {
+ count = -EINVAL;
+ goto out;
+ }
+
+ if (val)
+ buf[0] = '1';
+ else
+ buf[0] = '0';
+ buf[1] = '\n';
+ buf[2] = 0x00;
+
+ count = simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+
+out:
+ ubi_put_device(ubi);
+ return count;
+}
+
+/* Write an UBI debugfs file */
+static ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long ubi_num = (unsigned long)file->private_data;
+ struct dentry *dent = file->f_path.dentry;
+ struct ubi_device *ubi;
+ struct ubi_debug_info *d;
+ size_t buf_size;
+ char buf[8];
+ int val;
+
+ ubi = ubi_get_device(ubi_num);
+ if (!ubi)
+ return -ENODEV;
+ d = ubi->dbg;
+
+ buf_size = min_t(size_t, count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size)) {
+ count = -EFAULT;
+ goto out;
+ }
+
+ if (buf[0] == '1')
+ val = 1;
+ else if (buf[0] == '0')
+ val = 0;
+ else {
+ count = -EINVAL;
+ goto out;
+ }
+
+ if (dent == d->dfs_chk_gen)
+ d->chk_gen = val;
+ else if (dent == d->dfs_chk_io)
+ d->chk_io = val;
+ else if (dent == d->dfs_disable_bgt)
+ d->disable_bgt = val;
+ else if (dent == d->dfs_emulate_bitflips)
+ d->emulate_bitflips = val;
+ else if (dent == d->dfs_emulate_io_failures)
+ d->emulate_io_failures = val;
+ else
+ count = -EINVAL;
+
+out:
+ ubi_put_device(ubi);
+ return count;
+}
+
+static int default_open(struct inode *inode, struct file *file)
+{
+ if (inode->i_private)
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+/* File operations for all UBI debugfs files */
+static const struct file_operations dfs_fops = {
+ .read = dfs_file_read,
+ .write = dfs_file_write,
+ .open = default_open,
+ .llseek = no_llseek,
+ .owner = THIS_MODULE,
+};
+
+/**
+ * ubi_debugfs_init_dev - initialize debugfs for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function creates all debugfs files for UBI device @ubi. Returns zero in
+ * case of success and a negative error code in case of failure.
+ */
+int ubi_debugfs_init_dev(struct ubi_device *ubi)
+{
+ int err, n;
+ unsigned long ubi_num = ubi->ubi_num;
+ const char *fname;
+ struct dentry *dent;
+ struct ubi_debug_info *d = ubi->dbg;
+
+ n = snprintf(d->dfs_dir_name, UBI_DFS_DIR_LEN + 1, UBI_DFS_DIR_NAME,
+ ubi->ubi_num);
+ if (n == UBI_DFS_DIR_LEN) {
+ /* The array size is too small */
+ fname = UBI_DFS_DIR_NAME;
+ dent = ERR_PTR(-EINVAL);
+ goto out;
+ }
+
+ fname = d->dfs_dir_name;
+ dent = debugfs_create_dir(fname, dfs_rootdir);
+ if (IS_ERR_OR_NULL(dent))
+ goto out;
+ d->dfs_dir = dent;
+
+ fname = "chk_gen";
+ dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_chk_gen = dent;
+
+ fname = "chk_io";
+ dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_chk_io = dent;
+
+ fname = "tst_disable_bgt";
+ dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_disable_bgt = dent;
+
+ fname = "tst_emulate_bitflips";
+ dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_emulate_bitflips = dent;
+
+ fname = "tst_emulate_io_failures";
+ dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_emulate_io_failures = dent;
+
+ return 0;
+
+out_remove:
+ debugfs_remove_recursive(d->dfs_dir);
+out:
+ err = dent ? PTR_ERR(dent) : -ENODEV;
+ ubi_err("cannot create \"%s\" debugfs file or directory, error %d\n",
+ fname, err);
+ return err;
+}
+
+/**
+ * dbg_debug_exit_dev - free all debugfs files corresponding to device @ubi
+ * @ubi: UBI device description object
+ */
+void ubi_debugfs_exit_dev(struct ubi_device *ubi)
+{
+ debugfs_remove_recursive(ubi->dbg->dfs_dir);
+}
+
#endif /* CONFIG_MTD_UBI_DEBUG */
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
index 3f1a09c5c438..64fbb0021825 100644
--- a/drivers/mtd/ubi/debug.h
+++ b/drivers/mtd/ubi/debug.h
@@ -21,14 +21,6 @@
#ifndef __UBI_DEBUG_H__
#define __UBI_DEBUG_H__
-struct ubi_ec_hdr;
-struct ubi_vid_hdr;
-struct ubi_volume;
-struct ubi_vtbl_record;
-struct ubi_scan_volume;
-struct ubi_scan_leb;
-struct ubi_mkvol_req;
-
#ifdef CONFIG_MTD_UBI_DEBUG
#include <linux/random.h>
@@ -71,86 +63,103 @@ void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv);
void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
void ubi_dbg_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len);
-
-extern unsigned int ubi_chk_flags;
-
-/*
- * Debugging check flags.
- *
- * UBI_CHK_GEN: general checks
- * UBI_CHK_IO: check writes and erases
- */
-enum {
- UBI_CHK_GEN = 0x1,
- UBI_CHK_IO = 0x2,
-};
-
int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len);
int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
int offset, int len);
-
-extern unsigned int ubi_tst_flags;
+int ubi_debugging_init_dev(struct ubi_device *ubi);
+void ubi_debugging_exit_dev(struct ubi_device *ubi);
+int ubi_debugfs_init(void);
+void ubi_debugfs_exit(void);
+int ubi_debugfs_init_dev(struct ubi_device *ubi);
+void ubi_debugfs_exit_dev(struct ubi_device *ubi);
/*
- * Special testing flags.
+ * The UBI debugfs directory name pattern and maximum name length (3 for "ubi"
+ * + 2 for the number plus 1 for the trailing zero byte.
+ */
+#define UBI_DFS_DIR_NAME "ubi%d"
+#define UBI_DFS_DIR_LEN (3 + 2 + 1)
+
+/**
+ * struct ubi_debug_info - debugging information for an UBI device.
*
- * UBIFS_TST_DISABLE_BGT: disable the background thread
- * UBI_TST_EMULATE_BITFLIPS: emulate bit-flips
- * UBI_TST_EMULATE_WRITE_FAILURES: emulate write failures
- * UBI_TST_EMULATE_ERASE_FAILURES: emulate erase failures
+ * @chk_gen: if UBI general extra checks are enabled
+ * @chk_io: if UBI I/O extra checks are enabled
+ * @disable_bgt: disable the background task for testing purposes
+ * @emulate_bitflips: emulate bit-flips for testing purposes
+ * @emulate_io_failures: emulate write/erase failures for testing purposes
+ * @dfs_dir_name: name of debugfs directory containing files of this UBI device
+ * @dfs_dir: direntry object of the UBI device debugfs directory
+ * @dfs_chk_gen: debugfs knob to enable UBI general extra checks
+ * @dfs_chk_io: debugfs knob to enable UBI I/O extra checks
+ * @dfs_disable_bgt: debugfs knob to disable the background task
+ * @dfs_emulate_bitflips: debugfs knob to emulate bit-flips
+ * @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures
*/
-enum {
- UBI_TST_DISABLE_BGT = 0x1,
- UBI_TST_EMULATE_BITFLIPS = 0x2,
- UBI_TST_EMULATE_WRITE_FAILURES = 0x4,
- UBI_TST_EMULATE_ERASE_FAILURES = 0x8,
+struct ubi_debug_info {
+ unsigned int chk_gen:1;
+ unsigned int chk_io:1;
+ unsigned int disable_bgt:1;
+ unsigned int emulate_bitflips:1;
+ unsigned int emulate_io_failures:1;
+ char dfs_dir_name[UBI_DFS_DIR_LEN + 1];
+ struct dentry *dfs_dir;
+ struct dentry *dfs_chk_gen;
+ struct dentry *dfs_chk_io;
+ struct dentry *dfs_disable_bgt;
+ struct dentry *dfs_emulate_bitflips;
+ struct dentry *dfs_emulate_io_failures;
};
/**
* ubi_dbg_is_bgt_disabled - if the background thread is disabled.
+ * @ubi: UBI device description object
*
* Returns non-zero if the UBI background thread is disabled for testing
* purposes.
*/
-static inline int ubi_dbg_is_bgt_disabled(void)
+static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi)
{
- return ubi_tst_flags & UBI_TST_DISABLE_BGT;
+ return ubi->dbg->disable_bgt;
}
/**
* ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
+ * @ubi: UBI device description object
*
* Returns non-zero if a bit-flip should be emulated, otherwise returns zero.
*/
-static inline int ubi_dbg_is_bitflip(void)
+static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi)
{
- if (ubi_tst_flags & UBI_TST_EMULATE_BITFLIPS)
+ if (ubi->dbg->emulate_bitflips)
return !(random32() % 200);
return 0;
}
/**
* ubi_dbg_is_write_failure - if it is time to emulate a write failure.
+ * @ubi: UBI device description object
*
* Returns non-zero if a write failure should be emulated, otherwise returns
* zero.
*/
-static inline int ubi_dbg_is_write_failure(void)
+static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi)
{
- if (ubi_tst_flags & UBI_TST_EMULATE_WRITE_FAILURES)
+ if (ubi->dbg->emulate_io_failures)
return !(random32() % 500);
return 0;
}
/**
* ubi_dbg_is_erase_failure - if its time to emulate an erase failure.
+ * @ubi: UBI device description object
*
* Returns non-zero if an erase failure should be emulated, otherwise returns
* zero.
*/
-static inline int ubi_dbg_is_erase_failure(void)
+static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi)
{
- if (ubi_tst_flags & UBI_TST_EMULATE_ERASE_FAILURES)
+ if (ubi->dbg->emulate_io_failures)
return !(random32() % 400);
return 0;
}
@@ -172,7 +181,7 @@ static inline int ubi_dbg_is_erase_failure(void)
#define ubi_dbg_msg(fmt, ...) do { \
if (0) \
- pr_debug(fmt "\n", ##__VA_ARGS__); \
+ printk(KERN_DEBUG fmt "\n", ##__VA_ARGS__); \
} while (0)
#define dbg_msg(fmt, ...) ubi_dbg_msg(fmt, ##__VA_ARGS__)
@@ -201,11 +210,6 @@ static inline void ubi_dbg_dump_flash(struct ubi_device *ubi,
static inline void
ubi_dbg_print_hex_dump(const char *l, const char *ps, int pt, int r,
int g, const void *b, size_t len, bool a) { return; }
-
-static inline int ubi_dbg_is_bgt_disabled(void) { return 0; }
-static inline int ubi_dbg_is_bitflip(void) { return 0; }
-static inline int ubi_dbg_is_write_failure(void) { return 0; }
-static inline int ubi_dbg_is_erase_failure(void) { return 0; }
static inline int ubi_dbg_check_all_ff(struct ubi_device *ubi,
int pnum, int offset,
int len) { return 0; }
@@ -213,5 +217,20 @@ static inline int ubi_dbg_check_write(struct ubi_device *ubi,
const void *buf, int pnum,
int offset, int len) { return 0; }
+static inline int ubi_debugging_init_dev(struct ubi_device *ubi) { return 0; }
+static inline void ubi_debugging_exit_dev(struct ubi_device *ubi) { return; }
+static inline int ubi_debugfs_init(void) { return 0; }
+static inline void ubi_debugfs_exit(void) { return; }
+static inline int ubi_debugfs_init_dev(struct ubi_device *ubi) { return 0; }
+static inline void ubi_debugfs_exit_dev(struct ubi_device *ubi) { return; }
+
+static inline int
+ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi) { return 0; }
+static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi) { return 0; }
+static inline int
+ubi_dbg_is_write_failure(const struct ubi_device *ubi) { return 0; }
+static inline int
+ubi_dbg_is_erase_failure(const struct ubi_device *ubi) { return 0; }
+
#endif /* !CONFIG_MTD_UBI_DEBUG */
#endif /* !__UBI_DEBUG_H__ */
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 4be671815014..fb7f19b62d91 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -443,7 +443,7 @@ retry:
if (err == UBI_IO_BITFLIPS) {
scrub = 1;
err = 0;
- } else if (err == -EBADMSG) {
+ } else if (mtd_is_eccerr(err)) {
if (vol->vol_type == UBI_DYNAMIC_VOLUME)
goto out_unlock;
scrub = 1;
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 8c1b1c7bc4a7..f20b6f22f240 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -172,9 +172,9 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
retry:
err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
if (err) {
- const char *errstr = (err == -EBADMSG) ? " (ECC error)" : "";
+ const char *errstr = mtd_is_eccerr(err) ? " (ECC error)" : "";
- if (err == -EUCLEAN) {
+ if (mtd_is_bitflip(err)) {
/*
* -EUCLEAN is reported if there was a bit-flip which
* was corrected, so this is harmless.
@@ -205,14 +205,14 @@ retry:
* all the requested data. But some buggy drivers might do
* this, so we change it to -EIO.
*/
- if (read != len && err == -EBADMSG) {
+ if (read != len && mtd_is_eccerr(err)) {
ubi_assert(0);
err = -EIO;
}
} else {
ubi_assert(len == read);
- if (ubi_dbg_is_bitflip()) {
+ if (ubi_dbg_is_bitflip(ubi)) {
dbg_gen("bit-flip (emulated)");
err = UBI_IO_BITFLIPS;
}
@@ -281,7 +281,7 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
return err;
}
- if (ubi_dbg_is_write_failure()) {
+ if (ubi_dbg_is_write_failure(ubi)) {
dbg_err("cannot write %d bytes to PEB %d:%d "
"(emulated)", len, pnum, offset);
ubi_dbg_dump_stack();
@@ -396,7 +396,7 @@ retry:
if (err)
return err;
- if (ubi_dbg_is_erase_failure()) {
+ if (ubi_dbg_is_erase_failure(ubi)) {
dbg_err("cannot erase PEB %d (emulated)", pnum);
return -EIO;
}
@@ -469,7 +469,7 @@ static int torture_peb(struct ubi_device *ubi, int pnum)
out:
mutex_unlock(&ubi->buf_mutex);
- if (err == UBI_IO_BITFLIPS || err == -EBADMSG) {
+ if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err)) {
/*
* If a bit-flip or data integrity error was detected, the test
* has not passed because it happened on a freshly erased
@@ -760,7 +760,7 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
read_err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE);
if (read_err) {
- if (read_err != UBI_IO_BITFLIPS && read_err != -EBADMSG)
+ if (read_err != UBI_IO_BITFLIPS && !mtd_is_eccerr(read_err))
return read_err;
/*
@@ -776,7 +776,7 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
magic = be32_to_cpu(ec_hdr->magic);
if (magic != UBI_EC_HDR_MAGIC) {
- if (read_err == -EBADMSG)
+ if (mtd_is_eccerr(read_err))
return UBI_IO_BAD_HDR_EBADMSG;
/*
@@ -1032,12 +1032,12 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
p = (char *)vid_hdr - ubi->vid_hdr_shift;
read_err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
ubi->vid_hdr_alsize);
- if (read_err && read_err != UBI_IO_BITFLIPS && read_err != -EBADMSG)
+ if (read_err && read_err != UBI_IO_BITFLIPS && !mtd_is_eccerr(read_err))
return read_err;
magic = be32_to_cpu(vid_hdr->magic);
if (magic != UBI_VID_HDR_MAGIC) {
- if (read_err == -EBADMSG)
+ if (mtd_is_eccerr(read_err))
return UBI_IO_BAD_HDR_EBADMSG;
if (ubi_check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) {
@@ -1146,7 +1146,7 @@ static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum)
{
int err;
- if (!(ubi_chk_flags & UBI_CHK_IO))
+ if (!ubi->dbg->chk_io)
return 0;
err = ubi_io_is_bad(ubi, pnum);
@@ -1173,7 +1173,7 @@ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
int err;
uint32_t magic;
- if (!(ubi_chk_flags & UBI_CHK_IO))
+ if (!ubi->dbg->chk_io)
return 0;
magic = be32_to_cpu(ec_hdr->magic);
@@ -1211,7 +1211,7 @@ static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
uint32_t crc, hdr_crc;
struct ubi_ec_hdr *ec_hdr;
- if (!(ubi_chk_flags & UBI_CHK_IO))
+ if (!ubi->dbg->chk_io)
return 0;
ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
@@ -1219,7 +1219,7 @@ static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
return -ENOMEM;
err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE);
- if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err))
goto exit;
crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC);
@@ -1255,7 +1255,7 @@ static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
int err;
uint32_t magic;
- if (!(ubi_chk_flags & UBI_CHK_IO))
+ if (!ubi->dbg->chk_io)
return 0;
magic = be32_to_cpu(vid_hdr->magic);
@@ -1296,7 +1296,7 @@ static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
struct ubi_vid_hdr *vid_hdr;
void *p;
- if (!(ubi_chk_flags & UBI_CHK_IO))
+ if (!ubi->dbg->chk_io)
return 0;
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
@@ -1306,7 +1306,7 @@ static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
p = (char *)vid_hdr - ubi->vid_hdr_shift;
err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
ubi->vid_hdr_alsize);
- if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err))
goto exit;
crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC);
@@ -1348,7 +1348,7 @@ int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
void *buf1;
loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
- if (!(ubi_chk_flags & UBI_CHK_IO))
+ if (!ubi->dbg->chk_io)
return 0;
buf1 = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
@@ -1358,7 +1358,7 @@ int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
}
err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf1);
- if (err && err != -EUCLEAN)
+ if (err && !mtd_is_bitflip(err))
goto out_free;
for (i = 0; i < len; i++) {
@@ -1412,7 +1412,7 @@ int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len)
void *buf;
loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
- if (!(ubi_chk_flags & UBI_CHK_IO))
+ if (!ubi->dbg->chk_io)
return 0;
buf = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
@@ -1422,7 +1422,7 @@ int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len)
}
err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
- if (err && err != -EUCLEAN) {
+ if (err && !mtd_is_bitflip(err)) {
ubi_err("error %d while reading %d bytes from PEB %d:%d, "
"read %zd bytes", err, len, pnum, offset, read);
goto error;
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
index d39716e5b204..1a35fc5e3b40 100644
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -410,7 +410,7 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
return 0;
err = ubi_eba_read_leb(ubi, vol, lnum, buf, offset, len, check);
- if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) {
+ if (err && mtd_is_eccerr(err) && vol->vol_type == UBI_STATIC_VOLUME) {
ubi_warn("mark volume %d as corrupted", vol_id);
vol->corrupted = 1;
}
diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c
index ff2a65c37f69..f6a7d7ac4b98 100644
--- a/drivers/mtd/ubi/misc.c
+++ b/drivers/mtd/ubi/misc.c
@@ -81,7 +81,7 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id)
err = ubi_eba_read_leb(ubi, vol, i, buf, 0, size, 1);
if (err) {
- if (err == -EBADMSG)
+ if (mtd_is_eccerr(err))
err = 1;
break;
}
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c
index 2135a53732ff..0cb17d936b5a 100644
--- a/drivers/mtd/ubi/scan.c
+++ b/drivers/mtd/ubi/scan.c
@@ -395,7 +395,7 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb,
}
err = ubi_io_read_data(ubi, buf, pnum, 0, len);
- if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err))
goto out_free_buf;
data_crc = be32_to_cpu(vid_hdr->data_crc);
@@ -793,7 +793,7 @@ static int check_corruption(struct ubi_device *ubi, struct ubi_vid_hdr *vid_hdr,
err = ubi_io_read(ubi, ubi->peb_buf1, pnum, ubi->leb_start,
ubi->leb_size);
- if (err == UBI_IO_BITFLIPS || err == -EBADMSG) {
+ if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err)) {
/*
* Bit-flips or integrity errors while reading the data area.
* It is difficult to say for sure what type of corruption is
@@ -1347,7 +1347,7 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si)
struct ubi_scan_leb *seb, *last_seb;
uint8_t *buf;
- if (!(ubi_chk_flags & UBI_CHK_GEN))
+ if (!ubi->dbg->chk_gen)
return 0;
/*
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index c6c22295898e..dc64c767fd21 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -44,7 +44,6 @@
#include "ubi-media.h"
#include "scan.h"
-#include "debug.h"
/* Maximum number of supported UBI devices */
#define UBI_MAX_DEVICES 32
@@ -390,6 +389,8 @@ struct ubi_wl_entry;
* @peb_buf2: another buffer of PEB size used for different purposes
* @buf_mutex: protects @peb_buf1 and @peb_buf2
* @ckvol_mutex: serializes static volume checking when opening
+ *
+ * @dbg: debugging information for this UBI device
*/
struct ubi_device {
struct cdev cdev;
@@ -472,8 +473,12 @@ struct ubi_device {
void *peb_buf2;
struct mutex buf_mutex;
struct mutex ckvol_mutex;
+
+ struct ubi_debug_info *dbg;
};
+#include "debug.h"
+
extern struct kmem_cache *ubi_wl_entry_slab;
extern const struct file_operations ubi_ctrl_cdev_operations;
extern const struct file_operations ubi_cdev_operations;
@@ -662,6 +667,7 @@ static inline void ubi_ro_mode(struct ubi_device *ubi)
if (!ubi->ro_mode) {
ubi->ro_mode = 1;
ubi_warn("switch to read-only mode");
+ ubi_dbg_dump_stack();
}
}
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index 366eb70219a6..863835f4aefe 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -26,6 +26,7 @@
#include <linux/err.h>
#include <linux/math64.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "ubi.h"
#ifdef CONFIG_MTD_UBI_DEBUG
@@ -871,7 +872,7 @@ static int paranoid_check_volumes(struct ubi_device *ubi)
{
int i, err = 0;
- if (!(ubi_chk_flags & UBI_CHK_GEN))
+ if (!ubi->dbg->chk_gen)
return 0;
for (i = 0; i < ubi->vtbl_slots; i++) {
diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
index fd3bf770f518..9ad18da1891d 100644
--- a/drivers/mtd/ubi/vtbl.c
+++ b/drivers/mtd/ubi/vtbl.c
@@ -307,8 +307,7 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si,
{
int err, tries = 0;
static struct ubi_vid_hdr *vid_hdr;
- struct ubi_scan_volume *sv;
- struct ubi_scan_leb *new_seb, *old_seb = NULL;
+ struct ubi_scan_leb *new_seb;
ubi_msg("create volume table (copy #%d)", copy + 1);
@@ -316,15 +315,6 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si,
if (!vid_hdr)
return -ENOMEM;
- /*
- * Check if there is a logical eraseblock which would have to contain
- * this volume table copy was found during scanning. It has to be wiped
- * out.
- */
- sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID);
- if (sv)
- old_seb = ubi_scan_find_seb(sv, copy);
-
retry:
new_seb = ubi_scan_get_free_peb(ubi, si);
if (IS_ERR(new_seb)) {
@@ -351,8 +341,8 @@ retry:
goto write_error;
/*
- * And add it to the scanning information. Don't delete the old
- * @old_seb as it will be deleted and freed in 'ubi_scan_add_used()'.
+ * And add it to the scanning information. Don't delete the old version
+ * of this LEB as it will be deleted and freed in 'ubi_scan_add_used()'.
*/
err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec,
vid_hdr, 0);
@@ -433,7 +423,7 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0,
ubi->vtbl_size);
- if (err == UBI_IO_BITFLIPS || err == -EBADMSG)
+ if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err))
/*
* Scrub the PEB later. Note, -EBADMSG indicates an
* uncorrectable ECC error, but we have our own CRC and
@@ -876,7 +866,7 @@ out_free:
*/
static void paranoid_vtbl_check(const struct ubi_device *ubi)
{
- if (!(ubi_chk_flags & UBI_CHK_GEN))
+ if (!ubi->dbg->chk_gen)
return;
if (vtbl_check(ubi, ubi->vtbl)) {
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index ff2c4956eeff..42c684cf3688 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1,4 +1,5 @@
/*
+ * @ubi: UBI device description object
* Copyright (c) International Business Machines Corp., 2006
*
* This program is free software; you can redistribute it and/or modify
@@ -163,12 +164,14 @@ struct ubi_work {
#ifdef CONFIG_MTD_UBI_DEBUG
static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec);
-static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
+static int paranoid_check_in_wl_tree(const struct ubi_device *ubi,
+ struct ubi_wl_entry *e,
struct rb_root *root);
-static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e);
+static int paranoid_check_in_pq(const struct ubi_device *ubi,
+ struct ubi_wl_entry *e);
#else
#define paranoid_check_ec(ubi, pnum, ec) 0
-#define paranoid_check_in_wl_tree(e, root)
+#define paranoid_check_in_wl_tree(ubi, e, root)
#define paranoid_check_in_pq(ubi, e) 0
#endif
@@ -449,7 +452,7 @@ retry:
BUG();
}
- paranoid_check_in_wl_tree(e, &ubi->free);
+ paranoid_check_in_wl_tree(ubi, e, &ubi->free);
/*
* Move the physical eraseblock to the protection queue where it will
@@ -613,7 +616,7 @@ static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
list_add_tail(&wrk->list, &ubi->works);
ubi_assert(ubi->works_count >= 0);
ubi->works_count += 1;
- if (ubi->thread_enabled && !ubi_dbg_is_bgt_disabled())
+ if (ubi->thread_enabled && !ubi_dbg_is_bgt_disabled(ubi))
wake_up_process(ubi->bgt_thread);
spin_unlock(&ubi->wl_lock);
}
@@ -712,7 +715,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
e1->ec, e2->ec);
goto out_cancel;
}
- paranoid_check_in_wl_tree(e1, &ubi->used);
+ paranoid_check_in_wl_tree(ubi, e1, &ubi->used);
rb_erase(&e1->u.rb, &ubi->used);
dbg_wl("move PEB %d EC %d to PEB %d EC %d",
e1->pnum, e1->ec, e2->pnum, e2->ec);
@@ -721,12 +724,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
scrubbing = 1;
e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, u.rb);
e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
- paranoid_check_in_wl_tree(e1, &ubi->scrub);
+ paranoid_check_in_wl_tree(ubi, e1, &ubi->scrub);
rb_erase(&e1->u.rb, &ubi->scrub);
dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum);
}
- paranoid_check_in_wl_tree(e2, &ubi->free);
+ paranoid_check_in_wl_tree(ubi, e2, &ubi->free);
rb_erase(&e2->u.rb, &ubi->free);
ubi->move_from = e1;
ubi->move_to = e2;
@@ -1169,13 +1172,13 @@ retry:
return 0;
} else {
if (in_wl_tree(e, &ubi->used)) {
- paranoid_check_in_wl_tree(e, &ubi->used);
+ paranoid_check_in_wl_tree(ubi, e, &ubi->used);
rb_erase(&e->u.rb, &ubi->used);
} else if (in_wl_tree(e, &ubi->scrub)) {
- paranoid_check_in_wl_tree(e, &ubi->scrub);
+ paranoid_check_in_wl_tree(ubi, e, &ubi->scrub);
rb_erase(&e->u.rb, &ubi->scrub);
} else if (in_wl_tree(e, &ubi->erroneous)) {
- paranoid_check_in_wl_tree(e, &ubi->erroneous);
+ paranoid_check_in_wl_tree(ubi, e, &ubi->erroneous);
rb_erase(&e->u.rb, &ubi->erroneous);
ubi->erroneous_peb_count -= 1;
ubi_assert(ubi->erroneous_peb_count >= 0);
@@ -1242,7 +1245,7 @@ retry:
}
if (in_wl_tree(e, &ubi->used)) {
- paranoid_check_in_wl_tree(e, &ubi->used);
+ paranoid_check_in_wl_tree(ubi, e, &ubi->used);
rb_erase(&e->u.rb, &ubi->used);
} else {
int err;
@@ -1364,7 +1367,7 @@ int ubi_thread(void *u)
spin_lock(&ubi->wl_lock);
if (list_empty(&ubi->works) || ubi->ro_mode ||
- !ubi->thread_enabled || ubi_dbg_is_bgt_disabled()) {
+ !ubi->thread_enabled || ubi_dbg_is_bgt_disabled(ubi)) {
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock(&ubi->wl_lock);
schedule();
@@ -1579,7 +1582,7 @@ static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec)
long long read_ec;
struct ubi_ec_hdr *ec_hdr;
- if (!(ubi_chk_flags & UBI_CHK_GEN))
+ if (!ubi->dbg->chk_gen)
return 0;
ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
@@ -1609,16 +1612,18 @@ out_free:
/**
* paranoid_check_in_wl_tree - check that wear-leveling entry is in WL RB-tree.
+ * @ubi: UBI device description object
* @e: the wear-leveling entry to check
* @root: the root of the tree
*
* This function returns zero if @e is in the @root RB-tree and %-EINVAL if it
* is not.
*/
-static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
+static int paranoid_check_in_wl_tree(const struct ubi_device *ubi,
+ struct ubi_wl_entry *e,
struct rb_root *root)
{
- if (!(ubi_chk_flags & UBI_CHK_GEN))
+ if (!ubi->dbg->chk_gen)
return 0;
if (in_wl_tree(e, root))
@@ -1638,12 +1643,13 @@ static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
*
* This function returns zero if @e is in @ubi->pq and %-EINVAL if it is not.
*/
-static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e)
+static int paranoid_check_in_pq(const struct ubi_device *ubi,
+ struct ubi_wl_entry *e)
{
struct ubi_wl_entry *p;
int i;
- if (!(ubi_chk_flags & UBI_CHK_GEN))
+ if (!ubi->dbg->chk_gen)
return 0;
for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 93359fab240e..654a5e94e0e7 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2,9 +2,6 @@
# Network device configuration
#
-config HAVE_NET_MACB
- bool
-
menuconfig NETDEVICES
default y if UML
depends on NET
@@ -28,18 +25,32 @@ menuconfig NETDEVICES
# that for each of the symbols.
if NETDEVICES
-config IFB
- tristate "Intermediate Functional Block support"
- depends on NET_CLS_ACT
+config NET_CORE
+ default y
+ bool "Network core driver support"
---help---
- This is an intermediate driver that allows sharing of
- resources.
+ You can say N here if you do not intend to use any of the
+ networking core drivers (i.e. VLAN, bridging, bonding, etc.)
+
+if NET_CORE
+
+config BONDING
+ tristate "Bonding driver support"
+ depends on INET
+ depends on IPV6 || IPV6=n
+ ---help---
+ Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet
+ Channels together. This is called 'Etherchannel' by Cisco,
+ 'Trunking' by Sun, 802.3ad by the IEEE, and 'Bonding' in Linux.
+
+ The driver supports multiple bonding modes to allow for both high
+ performance and high availability operation.
+
+ Refer to <file:Documentation/networking/bonding.txt> for more
+ information.
+
To compile this driver as a module, choose M here: the module
- will be called ifb. If you want to use more than one ifb
- device at a time, you need to compile this driver as a module.
- Instead of 'ifb', the devices will then be called 'ifb0',
- 'ifb1' etc.
- Look at the iproute2 documentation directory for usage etc
+ will be called bonding.
config DUMMY
tristate "Dummy net driver support"
@@ -60,23 +71,59 @@ config DUMMY
Instead of 'dummy', the devices will then be called 'dummy0',
'dummy1' etc.
-config BONDING
- tristate "Bonding driver support"
- depends on INET
- depends on IPV6 || IPV6=n
+config EQUALIZER
+ tristate "EQL (serial line load balancing) support"
---help---
- Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet
- Channels together. This is called 'Etherchannel' by Cisco,
- 'Trunking' by Sun, 802.3ad by the IEEE, and 'Bonding' in Linux.
+ If you have two serial connections to some other computer (this
+ usually requires two modems and two telephone lines) and you use
+ SLIP (the protocol for sending Internet traffic over telephone
+ lines) or PPP (a better SLIP) on them, you can make them behave like
+ one double speed connection using this driver. Naturally, this has
+ to be supported at the other end as well, either with a similar EQL
+ Linux driver or with a Livingston Portmaster 2e.
- The driver supports multiple bonding modes to allow for both high
- performance and high availability operation.
+ Say Y if you want this and read
+ <file:Documentation/networking/eql.txt>. You may also want to read
+ section 6.2 of the NET-3-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
- Refer to <file:Documentation/networking/bonding.txt> for more
- information.
+ To compile this driver as a module, choose M here: the module
+ will be called eql. If unsure, say N.
+
+config NET_FC
+ bool "Fibre Channel driver support"
+ depends on SCSI && PCI
+ help
+ Fibre Channel is a high speed serial protocol mainly used to connect
+ large storage devices to the computer; it is compatible with and
+ intended to replace SCSI.
+ If you intend to use Fibre Channel, you need to have a Fibre channel
+ adaptor card in your computer; say Y here and to the driver for your
+ adaptor below. You also should have said Y to "SCSI support" and
+ "SCSI generic support".
+
+config MII
+ tristate "Generic Media Independent Interface device support"
+ help
+ Most ethernet controllers have MII transceiver either as an external
+ or internal device. It is safe to say Y or M here even if your
+ ethernet card lacks MII.
+
+source "drivers/ieee802154/Kconfig"
+
+config IFB
+ tristate "Intermediate Functional Block support"
+ depends on NET_CLS_ACT
+ ---help---
+ This is an intermediate driver that allows sharing of
+ resources.
To compile this driver as a module, choose M here: the module
- will be called bonding.
+ will be called ifb. If you want to use more than one ifb
+ device at a time, you need to compile this driver as a module.
+ Instead of 'ifb', the devices will then be called 'ifb0',
+ 'ifb1' etc.
+ Look at the iproute2 documentation directory for usage etc
config MACVLAN
tristate "MAC-VLAN support (EXPERIMENTAL)"
@@ -105,24 +152,46 @@ config MACVTAP
To compile this driver as a module, choose M here: the module
will be called macvtap.
-config EQUALIZER
- tristate "EQL (serial line load balancing) support"
+config NETCONSOLE
+ tristate "Network console logging support"
---help---
- If you have two serial connections to some other computer (this
- usually requires two modems and two telephone lines) and you use
- SLIP (the protocol for sending Internet traffic over telephone
- lines) or PPP (a better SLIP) on them, you can make them behave like
- one double speed connection using this driver. Naturally, this has
- to be supported at the other end as well, either with a similar EQL
- Linux driver or with a Livingston Portmaster 2e.
+ If you want to log kernel messages over the network, enable this.
+ See <file:Documentation/networking/netconsole.txt> for details.
- Say Y if you want this and read
- <file:Documentation/networking/eql.txt>. You may also want to read
- section 6.2 of the NET-3-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
+config NETCONSOLE_DYNAMIC
+ bool "Dynamic reconfiguration of logging targets"
+ depends on NETCONSOLE && SYSFS && CONFIGFS_FS && \
+ !(NETCONSOLE=y && CONFIGFS_FS=m)
+ help
+ This option enables the ability to dynamically reconfigure target
+ parameters (interface, IP addresses, port numbers, MAC addresses)
+ at runtime through a userspace interface exported using configfs.
+ See <file:Documentation/networking/netconsole.txt> for details.
- To compile this driver as a module, choose M here: the module
- will be called eql. If unsure, say N.
+config NETPOLL
+ def_bool NETCONSOLE
+
+config NETPOLL_TRAP
+ bool "Netpoll traffic trapping"
+ default n
+ depends on NETPOLL
+
+config NET_POLL_CONTROLLER
+ def_bool NETPOLL
+
+config RIONET
+ tristate "RapidIO Ethernet over messaging driver support"
+ depends on RAPIDIO
+
+config RIONET_TX_SIZE
+ int "Number of outbound queue entries"
+ depends on RIONET
+ default "128"
+
+config RIONET_RX_SIZE
+ int "Number of inbound queue entries"
+ depends on RIONET
+ default "128"
config TUN
tristate "Universal TUN/TAP device driver support"
@@ -154,6 +223,30 @@ config VETH
When one end receives the packet it appears on its pair and vice
versa.
+config VIRTIO_NET
+ tristate "Virtio network driver (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && VIRTIO
+ ---help---
+ This is the virtual network driver for virtio. It can be used with
+ lguest or QEMU based VMMs (like KVM or Xen). Say Y or M.
+
+endif # NET_CORE
+
+config SUNGEM_PHY
+ tristate
+
+source "drivers/net/arcnet/Kconfig"
+
+source "drivers/atm/Kconfig"
+
+source "drivers/net/caif/Kconfig"
+
+source "drivers/net/ethernet/Kconfig"
+
+source "drivers/net/fddi/Kconfig"
+
+source "drivers/net/hippi/Kconfig"
+
config NET_SB1000
tristate "General Instruments Surfboard 1000"
depends on PNP
@@ -178,2789 +271,26 @@ config NET_SB1000
If you don't have this card, of course say N.
-source "drivers/net/arcnet/Kconfig"
-
-config MII
- tristate "Generic Media Independent Interface device support"
- help
- Most ethernet controllers have MII transceiver either as an external
- or internal device. It is safe to say Y or M here even if your
- ethernet card lacks MII.
-
source "drivers/net/phy/Kconfig"
-#
-# Ethernet
-#
-
-menuconfig NET_ETHERNET
- bool "Ethernet (10 or 100Mbit)"
- depends on !UML
- ---help---
- Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common
- type of Local Area Network (LAN) in universities and companies.
-
- Common varieties of Ethernet are: 10BASE-2 or Thinnet (10 Mbps over
- coaxial cable, linking computers in a chain), 10BASE-T or twisted
- pair (10 Mbps over twisted pair cable, linking computers to central
- hubs), 10BASE-F (10 Mbps over optical fiber links, using hubs),
- 100BASE-TX (100 Mbps over two twisted pair cables, using hubs),
- 100BASE-T4 (100 Mbps over 4 standard voice-grade twisted pair
- cables, using hubs), 100BASE-FX (100 Mbps over optical fiber links)
- [the 100BASE varieties are also known as Fast Ethernet], and Gigabit
- Ethernet (1 Gbps over optical fiber or short copper links).
-
- If your Linux machine will be connected to an Ethernet and you have
- an Ethernet network interface card (NIC) installed in your computer,
- say Y here and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. You will then also have
- to say Y to the driver for your particular NIC.
-
- Note that the answer to this question won't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about Ethernet network cards. If unsure, say N.
-
-if NET_ETHERNET
-
-config MACB
- tristate "Atmel MACB support"
- depends on HAVE_NET_MACB
- select PHYLIB
- help
- The Atmel MACB ethernet interface is found on many AT32 and AT91
- parts. Say Y to include support for the MACB chip.
-
- To compile this driver as a module, choose M here: the module
- will be called macb.
-
-source "drivers/net/arm/Kconfig"
-
-config AX88796
- tristate "ASIX AX88796 NE2000 clone support"
- depends on ARM || MIPS || SUPERH
- select PHYLIB
- select MDIO_BITBANG
- help
- AX88796 driver, using platform bus to provide
- chip detection and resources
-
-config AX88796_93CX6
- bool "ASIX AX88796 external 93CX6 eeprom support"
- depends on AX88796
- select EEPROM_93CX6
- help
- Select this if your platform comes with an external 93CX6 eeprom.
-
-config MACE
- tristate "MACE (Power Mac ethernet) support"
- depends on PPC_PMAC && PPC32
- select CRC32
- help
- Power Macintoshes and clones with Ethernet built-in on the
- motherboard will usually use a MACE (Medium Access Control for
- Ethernet) interface. Say Y to include support for the MACE chip.
-
- To compile this driver as a module, choose M here: the module
- will be called mace.
-
-config MACE_AAUI_PORT
- bool "Use AAUI port instead of TP by default"
- depends on MACE
- help
- Some Apple machines (notably the Apple Network Server) which use the
- MACE ethernet chip have an Apple AUI port (small 15-pin connector),
- instead of an 8-pin RJ45 connector for twisted-pair ethernet. Say
- Y here if you have such a machine. If unsure, say N.
- The driver will default to AAUI on ANS anyway, and if you use it as
- a module, you can provide the port_aaui=0|1 to force the driver.
-
-config BMAC
- tristate "BMAC (G3 ethernet) support"
- depends on PPC_PMAC && PPC32
- select CRC32
- help
- Say Y for support of BMAC Ethernet interfaces. These are used on G3
- computers.
-
- To compile this driver as a module, choose M here: the module
- will be called bmac.
-
-config ARIADNE
- tristate "Ariadne support"
- depends on ZORRO
- help
- If you have a Village Tronic Ariadne Ethernet adapter, say Y.
- Otherwise, say N.
-
- To compile this driver as a module, choose M here: the module
- will be called ariadne.
-
-config A2065
- tristate "A2065 support"
- depends on ZORRO
- select CRC32
- help
- If you have a Commodore A2065 Ethernet adapter, say Y. Otherwise,
- say N.
-
- To compile this driver as a module, choose M here: the module
- will be called a2065.
-
-config HYDRA
- tristate "Hydra support"
- depends on ZORRO
- select CRC32
- help
- If you have a Hydra Ethernet adapter, say Y. Otherwise, say N.
-
- To compile this driver as a module, choose M here: the module
- will be called hydra.
-
-config ZORRO8390
- tristate "Zorro NS8390-based Ethernet support"
- depends on ZORRO
- select CRC32
- help
- This driver is for Zorro Ethernet cards using an NS8390-compatible
- chipset, like the Village Tronic Ariadne II and the Individual
- Computers X-Surf Ethernet cards. If you have such a card, say Y.
- Otherwise, say N.
-
- To compile this driver as a module, choose M here: the module
- will be called zorro8390.
-
-config APNE
- tristate "PCMCIA NE2000 support"
- depends on AMIGA_PCMCIA
- select CRC32
- help
- If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise,
- say N.
-
- To compile this driver as a module, choose M here: the module
- will be called apne.
-
-config MAC8390
- bool "Macintosh NS 8390 based ethernet cards"
- depends on MAC
- select CRC32
- help
- If you want to include a driver to support Nubus or LC-PDS
- Ethernet cards using an NS8390 chipset or its equivalent, say Y
- and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
-config MAC89x0
- tristate "Macintosh CS89x0 based ethernet cards"
- depends on MAC
- ---help---
- Support for CS89x0 chipset based Ethernet cards. If you have a
- Nubus or LC-PDS network (Ethernet) card of this type, say Y and
- read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. This module will
- be called mac89x0.
-
-config MACSONIC
- tristate "Macintosh SONIC based ethernet (onboard, NuBus, LC, CS)"
- depends on MAC
- ---help---
- Support for NatSemi SONIC based Ethernet devices. This includes
- the onboard Ethernet in many Quadras as well as some LC-PDS,
- a few Nubus and all known Comm Slot Ethernet cards. If you have
- one of these say Y and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. This module will
- be called macsonic.
-
-config MACMACE
- bool "Macintosh (AV) onboard MACE ethernet"
- depends on MAC
- select CRC32
- help
- Support for the onboard AMD 79C940 MACE Ethernet controller used in
- the 660AV and 840AV Macintosh. If you have one of these Macintoshes
- say Y and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
-config MVME147_NET
- tristate "MVME147 (Lance) Ethernet support"
- depends on MVME147
- select CRC32
- help
- Support for the on-board Ethernet interface on the Motorola MVME147
- single-board computer. Say Y here to include the
- driver for this chip in your kernel.
- To compile this driver as a module, choose M here.
-
-config MVME16x_NET
- tristate "MVME16x Ethernet support"
- depends on MVME16x
- help
- This is the driver for the Ethernet interface on the Motorola
- MVME162, 166, 167, 172 and 177 boards. Say Y here to include the
- driver for this chip in your kernel.
- To compile this driver as a module, choose M here.
-
-config BVME6000_NET
- tristate "BVME6000 Ethernet support"
- depends on BVME6000
- help
- This is the driver for the Ethernet interface on BVME4000 and
- BVME6000 VME boards. Say Y here to include the driver for this chip
- in your kernel.
- To compile this driver as a module, choose M here.
-
-config ATARILANCE
- tristate "Atari Lance support"
- depends on ATARI
- help
- Say Y to include support for several Atari Ethernet adapters based
- on the AMD Lance chipset: RieblCard (with or without battery), or
- PAMCard VME (also the version by Rhotron, with different addresses).
-
-config SUN3LANCE
- tristate "Sun3/Sun3x on-board LANCE support"
- depends on SUN3 || SUN3X
- help
- Most Sun3 and Sun3x motherboards (including the 3/50, 3/60 and 3/80)
- featured an AMD Lance 10Mbit Ethernet controller on board; say Y
- here to compile in the Linux driver for this and enable Ethernet.
- General Linux information on the Sun 3 and 3x series (now
- discontinued) is at
- <http://www.angelfire.com/ca2/tech68k/sun3.html>.
-
- If you're not building a kernel for a Sun 3, say N.
-
-config SUN3_82586
- bool "Sun3 on-board Intel 82586 support"
- depends on SUN3
- help
- This driver enables support for the on-board Intel 82586 based
- Ethernet adapter found on Sun 3/1xx and 3/2xx motherboards. Note
- that this driver does not support 82586-based adapters on additional
- VME boards.
-
-config HPLANCE
- bool "HP on-board LANCE support"
- depends on DIO
- select CRC32
- help
- If you want to use the builtin "LANCE" Ethernet controller on an
- HP300 machine, say Y here.
-
-config LASI_82596
- tristate "Lasi ethernet"
- depends on GSC
- help
- Say Y here to support the builtin Intel 82596 ethernet controller
- found in Hewlett-Packard PA-RISC machines with 10Mbit ethernet.
-
-config SNI_82596
- tristate "SNI RM ethernet"
- depends on NET_ETHERNET && SNI_RM
- help
- Say Y here to support the on-board Intel 82596 ethernet controller
- built into SNI RM machines.
-
-config KORINA
- tristate "Korina (IDT RC32434) Ethernet support"
- depends on NET_ETHERNET && MIKROTIK_RB532
- help
- If you have a Mikrotik RouterBoard 500 or IDT RC32434
- based system say Y. Otherwise say N.
+source "drivers/net/plip/Kconfig"
-config MIPS_JAZZ_SONIC
- tristate "MIPS JAZZ onboard SONIC Ethernet support"
- depends on MACH_JAZZ
- help
- This is the driver for the onboard card of MIPS Magnum 4000,
- Acer PICA, Olivetti M700-10 and a few other identical OEM systems.
-
-config XTENSA_XT2000_SONIC
- tristate "Xtensa XT2000 onboard SONIC Ethernet support"
- depends on XTENSA_PLATFORM_XT2000
- help
- This is the driver for the onboard card of the Xtensa XT2000 board.
-
-config MIPS_AU1X00_ENET
- tristate "MIPS AU1000 Ethernet support"
- depends on MIPS_ALCHEMY
- select PHYLIB
- select CRC32
- help
- If you have an Alchemy Semi AU1X00 based system
- say Y. Otherwise, say N.
-
-config SGI_IOC3_ETH
- bool "SGI IOC3 Ethernet"
- depends on PCI && SGI_IP27
- select CRC32
- select MII
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
-config MIPS_SIM_NET
- tristate "MIPS simulator Network device"
- depends on MIPS_SIM
- help
- The MIPSNET device is a simple Ethernet network device which is
- emulated by the MIPS Simulator.
- If you are not using a MIPSsim or are unsure, say N.
-
-config SGI_O2MACE_ETH
- tristate "SGI O2 MACE Fast Ethernet support"
- depends on SGI_IP32=y
-
-config STNIC
- tristate "National DP83902AV support"
- depends on SUPERH
- select CRC32
- help
- Support for cards based on the National Semiconductor DP83902AV
- ST-NIC Serial Network Interface Controller for Twisted Pair. This
- is a 10Mbit/sec Ethernet controller. Product overview and specs at
- <http://www.national.com/pf/DP/DP83902A.html>.
-
- If unsure, say N.
-
-config SH_ETH
- tristate "Renesas SuperH Ethernet support"
- depends on SUPERH && \
- (CPU_SUBTYPE_SH7710 || CPU_SUBTYPE_SH7712 || \
- CPU_SUBTYPE_SH7763 || CPU_SUBTYPE_SH7619 || \
- CPU_SUBTYPE_SH7724 || CPU_SUBTYPE_SH7757)
- select CRC32
- select MII
- select MDIO_BITBANG
- select PHYLIB
- help
- Renesas SuperH Ethernet device driver.
- This driver supporting CPUs are:
- - SH7710, SH7712, SH7763, SH7619, SH7724, and SH7757.
-
-config SUNLANCE
- tristate "Sun LANCE support"
- depends on SBUS
- select CRC32
- help
- This driver supports the "le" interface present on all 32-bit Sparc
- systems, on some older Ultra systems and as an Sbus option. These
- cards are based on the AMD Lance chipset, which is better known
- via the NE2100 cards.
-
- To compile this driver as a module, choose M here: the module
- will be called sunlance.
-
-config HAPPYMEAL
- tristate "Sun Happy Meal 10/100baseT support"
- depends on SBUS || PCI
- select CRC32
- help
- This driver supports the "hme" interface present on most Ultra
- systems and as an option on older Sbus systems. This driver supports
- both PCI and Sbus devices. This driver also supports the "qfe" quad
- 100baseT device available in both PCI and Sbus configurations.
-
- To compile this driver as a module, choose M here: the module
- will be called sunhme.
-
-config SUNBMAC
- tristate "Sun BigMAC 10/100baseT support (EXPERIMENTAL)"
- depends on SBUS && EXPERIMENTAL
- select CRC32
- help
- This driver supports the "be" interface available as an Sbus option.
- This is Sun's older 100baseT Ethernet device.
-
- To compile this driver as a module, choose M here: the module
- will be called sunbmac.
-
-config SUNQE
- tristate "Sun QuadEthernet support"
- depends on SBUS
- select CRC32
- help
- This driver supports the "qe" 10baseT Ethernet device, available as
- an Sbus option. Note that this is not the same as Quad FastEthernet
- "qfe" which is supported by the Happy Meal driver instead.
-
- To compile this driver as a module, choose M here: the module
- will be called sunqe.
-
-config SUNGEM
- tristate "Sun GEM support"
- depends on PCI
- select CRC32
- help
- Support for the Sun GEM chip, aka Sun GigabitEthernet/P 2.0. See also
- <http://www.sun.com/products-n-solutions/hardware/docs/pdf/806-3985-10.pdf>.
-
-config CASSINI
- tristate "Sun Cassini support"
- depends on PCI
- select CRC32
- help
- Support for the Sun Cassini chip, aka Sun GigaSwift Ethernet. See also
- <http://www.sun.com/products-n-solutions/hardware/docs/pdf/817-4341-10.pdf>
-
-config SUNVNET
- tristate "Sun Virtual Network support"
- depends on SUN_LDOMS
- help
- Support for virtual network devices under Sun Logical Domains.
-
-config NET_VENDOR_3COM
- bool "3COM cards"
- depends on ISA || EISA || MCA || PCI
- help
- If you have a network (Ethernet) card belonging to this class, say Y
- and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about 3COM cards. If you say Y, you will be asked for
- your specific card in the following questions.
-
-config EL1
- tristate "3c501 \"EtherLink\" support"
- depends on NET_VENDOR_3COM && ISA
- ---help---
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. Also, consider buying a
- new card, since the 3c501 is slow, broken, and obsolete: you will
- have problems. Some people suggest to ping ("man ping") a nearby
- machine every minute ("man cron") when using this card.
-
- To compile this driver as a module, choose M here. The module
- will be called 3c501.
-
-config EL2
- tristate "3c503 \"EtherLink II\" support"
- depends on NET_VENDOR_3COM && ISA
- select CRC32
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called 3c503.
-
-config ELPLUS
- tristate "3c505 \"EtherLink Plus\" support"
- depends on NET_VENDOR_3COM && ISA && ISA_DMA_API
- ---help---
- Information about this network (Ethernet) card can be found in
- <file:Documentation/networking/3c505.txt>. If you have a card of
- this type, say Y and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called 3c505.
-
-config EL16
- tristate "3c507 \"EtherLink 16\" support (EXPERIMENTAL)"
- depends on NET_VENDOR_3COM && ISA && EXPERIMENTAL
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called 3c507.
-
-config EL3
- tristate "3c509/3c529 (MCA)/3c579 \"EtherLink III\" support"
- depends on NET_VENDOR_3COM && (ISA || EISA || MCA)
- ---help---
- If you have a network (Ethernet) card belonging to the 3Com
- EtherLinkIII series, say Y and read the Ethernet-HOWTO, available
- from <http://www.tldp.org/docs.html#howto>.
-
- If your card is not working you may need to use the DOS
- setup disk to disable Plug & Play mode, and to select the default
- media type.
-
- To compile this driver as a module, choose M here. The module
- will be called 3c509.
-
-config 3C515
- tristate "3c515 ISA \"Fast EtherLink\""
- depends on NET_VENDOR_3COM && (ISA || EISA) && ISA_DMA_API
- help
- If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet
- network card, say Y and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called 3c515.
-
-config ELMC
- tristate "3c523 \"EtherLink/MC\" support"
- depends on NET_VENDOR_3COM && MCA_LEGACY
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called 3c523.
-
-config ELMC_II
- tristate "3c527 \"EtherLink/MC 32\" support (EXPERIMENTAL)"
- depends on NET_VENDOR_3COM && MCA && MCA_LEGACY
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called 3c527.
-
-config VORTEX
- tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support"
- depends on NET_VENDOR_3COM && (PCI || EISA)
- select MII
- ---help---
- This option enables driver support for a large number of 10Mbps and
- 10/100Mbps EISA, PCI and PCMCIA 3Com network cards:
-
- "Vortex" (Fast EtherLink 3c590/3c592/3c595/3c597) EISA and PCI
- "Boomerang" (EtherLink XL 3c900 or 3c905) PCI
- "Cyclone" (3c540/3c900/3c905/3c980/3c575/3c656) PCI and Cardbus
- "Tornado" (3c905) PCI
- "Hurricane" (3c555/3cSOHO) PCI
-
- If you have such a card, say Y and read the Ethernet-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>. More
- specific information is in
- <file:Documentation/networking/vortex.txt> and in the comments at
- the beginning of <file:drivers/net/3c59x.c>.
-
- To compile this support as a module, choose M here.
-
-config TYPHOON
- tristate "3cr990 series \"Typhoon\" support"
- depends on NET_VENDOR_3COM && PCI
- select CRC32
- ---help---
- This option enables driver support for the 3cr990 series of cards:
-
- 3C990-TX, 3CR990-TX-95, 3CR990-TX-97, 3CR990-FX-95, 3CR990-FX-97,
- 3CR990SVR, 3CR990SVR95, 3CR990SVR97, 3CR990-FX-95 Server,
- 3CR990-FX-97 Server, 3C990B-TX-M, 3C990BSVR
-
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called typhoon.
-
-config LANCE
- tristate "AMD LANCE and PCnet (AT1500 and NE2100) support"
- depends on ISA && ISA_DMA_API
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. Some LinkSys cards are
- of this type.
-
- To compile this driver as a module, choose M here: the module
- will be called lance. This is recommended.
-
-config NET_VENDOR_SMC
- bool "Western Digital/SMC cards"
- depends on ISA || MCA || EISA || MAC
- help
- If you have a network (Ethernet) card belonging to this class, say Y
- and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about Western Digital cards. If you say Y, you will be
- asked for your specific card in the following questions.
-
-config WD80x3
- tristate "WD80*3 support"
- depends on NET_VENDOR_SMC && ISA
- select CRC32
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called wd.
-
-config ULTRAMCA
- tristate "SMC Ultra MCA support"
- depends on NET_VENDOR_SMC && MCA
- select CRC32
- help
- If you have a network (Ethernet) card of this type and are running
- an MCA based system (PS/2), say Y and read the Ethernet-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called smc-mca.
-
-config ULTRA
- tristate "SMC Ultra support"
- depends on NET_VENDOR_SMC && ISA
- select CRC32
- ---help---
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- Important: There have been many reports that, with some motherboards
- mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible,
- such as some BusLogic models) causes corruption problems with many
- operating systems. The Linux smc-ultra driver has a work-around for
- this but keep it in mind if you have such a SCSI card and have
- problems.
-
- To compile this driver as a module, choose M here. The module
- will be called smc-ultra.
-
-config ULTRA32
- tristate "SMC Ultra32 EISA support"
- depends on NET_VENDOR_SMC && EISA
- select CRC32
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called smc-ultra32.
-
-config BFIN_MAC
- tristate "Blackfin on-chip MAC support"
- depends on NET_ETHERNET && (BF516 || BF518 || BF526 || BF527 || BF536 || BF537)
- select CRC32
- select MII
- select PHYLIB
- select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
- help
- This is the driver for Blackfin on-chip mac device. Say Y if you want it
- compiled into the kernel. This driver is also available as a module
- ( = code which can be inserted in and removed from the running kernel
- whenever you want). The module will be called bfin_mac.
-
-config BFIN_MAC_USE_L1
- bool "Use L1 memory for rx/tx packets"
- depends on BFIN_MAC && (BF527 || BF537)
- default y
- help
- To get maximum network performance, you should use L1 memory as rx/tx buffers.
- Say N here if you want to reserve L1 memory for other uses.
-
-config BFIN_TX_DESC_NUM
- int "Number of transmit buffer packets"
- depends on BFIN_MAC
- range 6 10 if BFIN_MAC_USE_L1
- range 10 100
- default "10"
- help
- Set the number of buffer packets used in driver.
-
-config BFIN_RX_DESC_NUM
- int "Number of receive buffer packets"
- depends on BFIN_MAC
- range 20 100 if BFIN_MAC_USE_L1
- range 20 800
- default "20"
- help
- Set the number of buffer packets used in driver.
-
-config BFIN_MAC_USE_HWSTAMP
- bool "Use IEEE 1588 hwstamp"
- depends on BFIN_MAC && BF518
- default y
- help
- To support the IEEE 1588 Precision Time Protocol (PTP), select y here
-
-config SMC9194
- tristate "SMC 9194 support"
- depends on NET_VENDOR_SMC && (ISA || MAC && BROKEN)
- select CRC32
- ---help---
- This is support for the SMC9xxx based Ethernet cards. Choose this
- option if you have a DELL laptop with the docking station, or
- another SMC9192/9194 based chipset. Say Y if you want it compiled
- into the kernel, and read the file
- <file:Documentation/networking/smc9.txt> and the Ethernet-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called smc9194.
-
-config SMC91X
- tristate "SMC 91C9x/91C1xxx support"
- select CRC32
- select MII
- depends on ARM || M32R || SUPERH || \
- MIPS || BLACKFIN || MN10300 || COLDFIRE
- help
- This is a driver for SMC's 91x series of Ethernet chipsets,
- including the SMC91C94 and the SMC91C111. Say Y if you want it
- compiled into the kernel, and read the file
- <file:Documentation/networking/smc9.txt> and the Ethernet-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>.
-
- This driver is also available as a module ( = code which can be
- inserted in and removed from the running kernel whenever you want).
- The module will be called smc91x. If you want to compile it as a
- module, say M here and read <file:Documentation/kbuild/modules.txt>.
-
-config PXA168_ETH
- tristate "Marvell pxa168 ethernet support"
- depends on CPU_PXA168
- select PHYLIB
- help
- This driver supports the pxa168 Ethernet ports.
-
- To compile this driver as a module, choose M here. The module
- will be called pxa168_eth.
-
-config NET_NETX
- tristate "NetX Ethernet support"
- select MII
- depends on ARCH_NETX
- help
- This is support for the Hilscher netX builtin Ethernet ports
-
- To compile this driver as a module, choose M here. The module
- will be called netx-eth.
-
-config TI_DAVINCI_EMAC
- tristate "TI DaVinci EMAC Support"
- depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
- select TI_DAVINCI_MDIO
- select TI_DAVINCI_CPDMA
- select PHYLIB
- help
- This driver supports TI's DaVinci Ethernet .
-
- To compile this driver as a module, choose M here: the module
- will be called davinci_emac_driver. This is recommended.
-
-config TI_DAVINCI_MDIO
- tristate "TI DaVinci MDIO Support"
- depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
- select PHYLIB
- help
- This driver supports TI's DaVinci MDIO module.
-
- To compile this driver as a module, choose M here: the module
- will be called davinci_mdio. This is recommended.
-
-config TI_DAVINCI_CPDMA
- tristate "TI DaVinci CPDMA Support"
- depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
- help
- This driver supports TI's DaVinci CPDMA dma engine.
-
- To compile this driver as a module, choose M here: the module
- will be called davinci_cpdma. This is recommended.
-
-config DM9000
- tristate "DM9000 support"
- depends on ARM || BLACKFIN || MIPS
- select CRC32
- select MII
- ---help---
- Support for DM9000 chipset.
-
- To compile this driver as a module, choose M here. The module
- will be called dm9000.
-
-config DM9000_DEBUGLEVEL
- int "DM9000 maximum debug level"
- depends on DM9000
- default 4
- help
- The maximum level of debugging code compiled into the DM9000
- driver.
-
-config DM9000_FORCE_SIMPLE_PHY_POLL
- bool "Force simple NSR based PHY polling"
- depends on DM9000
- ---help---
- This configuration forces the DM9000 to use the NSR's LinkStatus
- bit to determine if the link is up or down instead of the more
- costly MII PHY reads. Note, this will not work if the chip is
- operating with an external PHY.
-
-config ENC28J60
- tristate "ENC28J60 support"
- depends on EXPERIMENTAL && SPI && NET_ETHERNET
- select CRC32
- ---help---
- Support for the Microchip EN28J60 ethernet chip.
-
- To compile this driver as a module, choose M here. The module will be
- called enc28j60.
-
-config ENC28J60_WRITEVERIFY
- bool "Enable write verify"
- depends on ENC28J60
- ---help---
- Enable the verify after the buffer write useful for debugging purpose.
- If unsure, say N.
-
-config ETHOC
- tristate "OpenCores 10/100 Mbps Ethernet MAC support"
- depends on NET_ETHERNET && HAS_IOMEM && HAS_DMA
- select MII
- select PHYLIB
- select CRC32
- select BITREVERSE
- help
- Say Y here if you want to use the OpenCores 10/100 Mbps Ethernet MAC.
-
-config GRETH
- tristate "Aeroflex Gaisler GRETH Ethernet MAC support"
- depends on SPARC
- select PHYLIB
- select CRC32
- help
- Say Y here if you want to use the Aeroflex Gaisler GRETH Ethernet MAC.
-
-config SMC911X
- tristate "SMSC LAN911[5678] support"
- select CRC32
- select MII
- depends on ARM || SUPERH || MN10300
- help
- This is a driver for SMSC's LAN911x series of Ethernet chipsets
- including the new LAN9115, LAN9116, LAN9117, and LAN9118.
- Say Y if you want it compiled into the kernel,
- and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- This driver is also available as a module. The module will be
- called smc911x. If you want to compile it as a module, say M
- here and read <file:Documentation/kbuild/modules.txt>
-
-config SMSC911X
- tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
- depends on ARM || SUPERH || BLACKFIN || MIPS || MN10300
- select CRC32
- select MII
- select PHYLIB
- ---help---
- Say Y here if you want support for SMSC LAN911x and LAN921x families
- of ethernet controllers.
-
- To compile this driver as a module, choose M here and read
- <file:Documentation/networking/net-modules.txt>. The module
- will be called smsc911x.
-
-config SMSC911X_ARCH_HOOKS
- def_bool n
- depends on SMSC911X
- help
- If the arch enables this, it allows the arch to implement various
- hooks for more comprehensive interrupt control and also to override
- the source of the MAC address.
-
-config NET_VENDOR_RACAL
- bool "Racal-Interlan (Micom) NI cards"
- depends on ISA
- help
- If you have a network (Ethernet) card belonging to this class, such
- as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>.
-
- Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about NI cards. If you say Y, you will be asked for
- your specific card in the following questions.
-
-config NI5010
- tristate "NI5010 support (EXPERIMENTAL)"
- depends on NET_VENDOR_RACAL && ISA && EXPERIMENTAL && BROKEN_ON_SMP
- ---help---
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. Note that this is still
- experimental code.
-
- To compile this driver as a module, choose M here. The module
- will be called ni5010.
-
-config NI52
- tristate "NI5210 support"
- depends on NET_VENDOR_RACAL && ISA
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called ni52.
-
-config NI65
- tristate "NI6510 support"
- depends on NET_VENDOR_RACAL && ISA && ISA_DMA_API
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called ni65.
-
-config DNET
- tristate "Dave ethernet support (DNET)"
- depends on NET_ETHERNET && HAS_IOMEM
- select PHYLIB
- help
- The Dave ethernet interface (DNET) is found on Qong Board FPGA.
- Say Y to include support for the DNET chip.
-
- To compile this driver as a module, choose M here: the module
- will be called dnet.
-
-source "drivers/net/tulip/Kconfig"
-
-config AT1700
- tristate "AT1700/1720 support (EXPERIMENTAL)"
- depends on (ISA || MCA_LEGACY) && EXPERIMENTAL
- select CRC32
- ---help---
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called at1700.
-
-config DEPCA
- tristate "DEPCA, DE10x, DE200, DE201, DE202, DE422 support"
- depends on ISA || EISA || MCA
- select CRC32
- ---help---
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto> as well as
- <file:drivers/net/depca.c>.
-
- To compile this driver as a module, choose M here. The module
- will be called depca.
-
-config HP100
- tristate "HP 10/100VG PCLAN (ISA, EISA, PCI) support"
- depends on ISA || EISA || PCI
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called hp100.
-
-config NET_ISA
- bool "Other ISA cards"
- depends on ISA
- ---help---
- If your network (Ethernet) card hasn't been mentioned yet and its
- bus system (that's the way the cards talks to the other components
- of your computer) is ISA (as opposed to EISA, VLB or PCI), say Y.
- Make sure you know the name of your card. Read the Ethernet-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>.
-
- If unsure, say Y.
-
- Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the remaining ISA network card questions. If you say Y, you will be
- asked for your specific card in the following questions.
-
-config E2100
- tristate "Cabletron E21xx support"
- depends on NET_ISA
- select CRC32
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called e2100.
-
-config EWRK3
- tristate "EtherWORKS 3 (DE203, DE204, DE205) support"
- depends on NET_ISA
- select CRC32
- ---help---
- This driver supports the DE203, DE204 and DE205 network (Ethernet)
- cards. If this is for you, say Y and read
- <file:Documentation/networking/ewrk3.txt> in the kernel source as
- well as the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called ewrk3.
-
-config EEXPRESS
- tristate "EtherExpress 16 support"
- depends on NET_ISA
- ---help---
- If you have an EtherExpress16 network (Ethernet) card, say Y and
- read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. Note that the Intel
- EtherExpress16 card used to be regarded as a very poor choice
- because the driver was very unreliable. We now have a new driver
- that should do better.
-
- To compile this driver as a module, choose M here. The module
- will be called eexpress.
-
-config EEXPRESS_PRO
- tristate "EtherExpressPro support/EtherExpress 10 (i82595) support"
- depends on NET_ISA
- ---help---
- If you have a network (Ethernet) card of this type, say Y. This
- driver supports Intel i82595{FX,TX} based boards. Note however
- that the EtherExpress PRO/100 Ethernet card has its own separate
- driver. Please read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called eepro.
-
-config HPLAN_PLUS
- tristate "HP PCLAN+ (27247B and 27252A) support"
- depends on NET_ISA
- select CRC32
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called hp-plus.
-
-config HPLAN
- tristate "HP PCLAN (27245 and other 27xxx series) support"
- depends on NET_ISA
- select CRC32
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called hp.
-
-config LP486E
- tristate "LP486E on board Ethernet"
- depends on NET_ISA
- help
- Say Y here to support the 82596-based on-board Ethernet controller
- for the Panther motherboard, which is one of the two shipped in the
- Intel Professional Workstation.
-
-config ETH16I
- tristate "ICL EtherTeam 16i/32 support"
- depends on NET_ISA
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called eth16i.
-
-config NE2000
- tristate "NE2000/NE1000 support"
- depends on NET_ISA || (Q40 && m) || M32R || MACH_TX49XX
- select CRC32
- ---help---
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. Many Ethernet cards
- without a specific driver are compatible with NE2000.
-
- If you have a PCI NE2000 card however, say N here and Y to "PCI
- NE2000 and clone support" under "EISA, VLB, PCI and on board
- controllers" below. If you have a NE2000 card and are running on
- an MCA system (a bus system used on some IBM PS/2 computers and
- laptops), say N here and Y to "NE/2 (ne2000 MCA version) support",
- below.
-
- To compile this driver as a module, choose M here. The module
- will be called ne.
-
-config ZNET
- tristate "Zenith Z-Note support (EXPERIMENTAL)"
- depends on NET_ISA && EXPERIMENTAL && ISA_DMA_API
- help
- The Zenith Z-Note notebook computer has a built-in network
- (Ethernet) card, and this is the Linux driver for it. Note that the
- IBM Thinkpad 300 is compatible with the Z-Note and is also supported
- by this driver. Read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
-config SEEQ8005
- tristate "SEEQ8005 support (EXPERIMENTAL)"
- depends on NET_ISA && EXPERIMENTAL
- help
- This is a driver for the SEEQ 8005 network (Ethernet) card. If this
- is for you, read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called seeq8005.
-
-config NE2_MCA
- tristate "NE/2 (ne2000 MCA version) support"
- depends on MCA_LEGACY
- select CRC32
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called ne2.
-
-config IBMLANA
- tristate "IBM LAN Adapter/A support"
- depends on MCA
- ---help---
- This is a Micro Channel Ethernet adapter. You need to set
- CONFIG_MCA to use this driver. It is both available as an in-kernel
- driver and as a module.
-
- To compile this driver as a module, choose M here. The only
- currently supported card is the IBM LAN Adapter/A for Ethernet. It
- will both support 16K and 32K memory windows, however a 32K window
- gives a better security against packet losses. Usage of multiple
- boards with this driver should be possible, but has not been tested
- up to now due to lack of hardware.
-
-config IBMVETH
- tristate "IBM LAN Virtual Ethernet support"
- depends on PPC_PSERIES
- ---help---
- This driver supports virtual ethernet adapters on newer IBM iSeries
- and pSeries systems.
-
- To compile this driver as a module, choose M here. The module will
- be called ibmveth.
-
-source "drivers/net/ibm_newemac/Kconfig"
-
-config NET_PCI
- bool "EISA, VLB, PCI and on board controllers"
- depends on ISA || EISA || PCI
- help
- This is another class of network cards which attach directly to the
- bus. If you have one of those, say Y and read the Ethernet-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>.
-
- Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about this class of network cards. If you say Y, you
- will be asked for your specific card in the following questions. If
- you are unsure, say Y.
-
-config PCNET32
- tristate "AMD PCnet32 PCI support"
- depends on NET_PCI && PCI
- select CRC32
- select MII
- help
- If you have a PCnet32 or PCnetPCI based network (Ethernet) card,
- answer Y here and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called pcnet32.
-
-config AMD8111_ETH
- tristate "AMD 8111 (new PCI lance) support"
- depends on NET_PCI && PCI
- select CRC32
- select MII
- help
- If you have an AMD 8111-based PCI lance ethernet card,
- answer Y here and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called amd8111e.
-
-config ADAPTEC_STARFIRE
- tristate "Adaptec Starfire/DuraLAN support"
- depends on NET_PCI && PCI
- select CRC32
- select MII
- help
- Say Y here if you have an Adaptec Starfire (or DuraLAN) PCI network
- adapter. The DuraLAN chip is used on the 64 bit PCI boards from
- Adaptec e.g. the ANA-6922A. The older 32 bit boards use the tulip
- driver.
-
- To compile this driver as a module, choose M here: the module
- will be called starfire. This is recommended.
-
-config AC3200
- tristate "Ansel Communications EISA 3200 support (EXPERIMENTAL)"
- depends on NET_PCI && (ISA || EISA) && EXPERIMENTAL
- select CRC32
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called ac3200.
-
-config KSZ884X_PCI
- tristate "Micrel KSZ8841/2 PCI"
- depends on NET_PCI && PCI
- select MII
- select CRC32
- help
- This PCI driver is for Micrel KSZ8841/KSZ8842 PCI Ethernet chip.
-
- To compile this driver as a module, choose M here. The module
- will be called ksz884x.
-
-config APRICOT
- tristate "Apricot Xen-II on board Ethernet"
- depends on NET_PCI && ISA
- help
- If you have a network (Ethernet) controller of this type, say Y and
- read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called apricot.
-
-config B44
- tristate "Broadcom 440x/47xx ethernet support"
- depends on SSB_POSSIBLE && HAS_DMA
- select SSB
- select MII
- help
- If you have a network (Ethernet) controller of this type, say Y
- or M and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called b44.
-
-# Auto-select SSB PCI-HOST support, if possible
-config B44_PCI_AUTOSELECT
- bool
- depends on B44 && SSB_PCIHOST_POSSIBLE
- select SSB_PCIHOST
- default y
-
-# Auto-select SSB PCICORE driver, if possible
-config B44_PCICORE_AUTOSELECT
- bool
- depends on B44 && SSB_DRIVER_PCICORE_POSSIBLE
- select SSB_DRIVER_PCICORE
- default y
-
-config B44_PCI
- bool
- depends on B44_PCI_AUTOSELECT && B44_PCICORE_AUTOSELECT
- default y
-
-config FORCEDETH
- tristate "nForce Ethernet support"
- depends on NET_PCI && PCI
- help
- If you have a network (Ethernet) controller of this type, say Y and
- read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called forcedeth.
-
-config CS89x0
- tristate "CS89x0 support"
- depends on NET_ETHERNET && (ISA || EISA || MACH_IXDP2351 \
- || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440)
- ---help---
- Support for CS89x0 chipset based Ethernet cards. If you have a
- network (Ethernet) card of this type, say Y and read the
- Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto> as well as
- <file:Documentation/networking/cs89x0.txt>.
-
- To compile this driver as a module, choose M here. The module
- will be called cs89x0.
-
-config CS89x0_NONISA_IRQ
- def_bool y
- depends on CS89x0 != n
- depends on MACH_IXDP2351 || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440
-
-config TC35815
- tristate "TOSHIBA TC35815 Ethernet support"
- depends on NET_PCI && PCI && MIPS
- select PHYLIB
-
-config E100
- tristate "Intel(R) PRO/100+ support"
- depends on NET_PCI && PCI
- select MII
- ---help---
- This driver supports Intel(R) PRO/100 family of adapters.
- To verify that your adapter is supported, find the board ID number
- on the adapter. Look for a label that has a barcode and a number
- in the format 123456-001 (six digits hyphen three digits).
-
- Use the above information and the Adapter & Driver ID Guide at:
-
- <http://support.intel.com/support/network/adapter/pro100/21397.htm>
-
- to identify the adapter.
-
- For the latest Intel PRO/100 network driver for Linux, see:
-
- <http://www.intel.com/p/en_US/support/highlights/network/pro100plus>
-
- More specific information on configuring the driver is in
- <file:Documentation/networking/e100.txt>.
-
- To compile this driver as a module, choose M here. The module
- will be called e100.
-
-config LNE390
- tristate "Mylex EISA LNE390A/B support (EXPERIMENTAL)"
- depends on NET_PCI && EISA && EXPERIMENTAL
- select CRC32
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
+source "drivers/net/ppp/Kconfig"
- To compile this driver as a module, choose M here. The module
- will be called lne390.
+source "drivers/net/slip/Kconfig"
-config FEALNX
- tristate "Myson MTD-8xx PCI Ethernet support"
- depends on NET_PCI && PCI
- select CRC32
- select MII
- help
- Say Y here to support the Myson MTD-800 family of PCI-based Ethernet
- cards. <http://www.myson.com.tw/>
-
-config NATSEMI
- tristate "National Semiconductor DP8381x series PCI Ethernet support"
- depends on NET_PCI && PCI
- select CRC32
- help
- This driver is for the National Semiconductor DP83810 series,
- which is used in cards from PureData, NetGear, Linksys
- and others, including the 83815 chip.
- More specific information and updates are available from
- <http://www.scyld.com/network/natsemi.html>.
-
-config NE2K_PCI
- tristate "PCI NE2000 and clones support (see help)"
- depends on NET_PCI && PCI
- select CRC32
- ---help---
- This driver is for NE2000 compatible PCI cards. It will not work
- with ISA NE2000 cards (they have their own driver, "NE2000/NE1000
- support" below). If you have a PCI NE2000 network (Ethernet) card,
- say Y and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- This driver also works for the following NE2000 clone cards:
- RealTek RTL-8029 Winbond 89C940 Compex RL2000 KTI ET32P2
- NetVin NV5000SC Via 86C926 SureCom NE34 Winbond
- Holtek HT80232 Holtek HT80229
-
- To compile this driver as a module, choose M here. The module
- will be called ne2k-pci.
-
-config NE3210
- tristate "Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)"
- depends on NET_PCI && EISA && EXPERIMENTAL
- select CRC32
- ---help---
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. Note that this driver
- will NOT WORK for NE3200 cards as they are completely different.
-
- To compile this driver as a module, choose M here. The module
- will be called ne3210.
-
-config ES3210
- tristate "Racal-Interlan EISA ES3210 support (EXPERIMENTAL)"
- depends on NET_PCI && EISA && EXPERIMENTAL
- select CRC32
- help
- If you have a network (Ethernet) card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module
- will be called es3210.
-
-config 8139CP
- tristate "RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support (EXPERIMENTAL)"
- depends on NET_PCI && PCI && EXPERIMENTAL
- select CRC32
- select MII
- help
- This is a driver for the Fast Ethernet PCI network cards based on
- the RTL8139C+ chips. If you have one of those, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here: the module
- will be called 8139cp. This is recommended.
-
-config 8139TOO
- tristate "RealTek RTL-8129/8130/8139 PCI Fast Ethernet Adapter support"
- depends on NET_PCI && PCI
- select CRC32
- select MII
- ---help---
- This is a driver for the Fast Ethernet PCI network cards based on
- the RTL 8129/8130/8139 chips. If you have one of those, say Y and
- read the Ethernet-HOWTO <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here: the module
- will be called 8139too. This is recommended.
-
-config 8139TOO_PIO
- bool "Use PIO instead of MMIO"
- default y
- depends on 8139TOO
- help
- This instructs the driver to use programmed I/O ports (PIO) instead
- of PCI shared memory (MMIO). This can possibly solve some problems
- in case your mainboard has memory consistency issues. If unsure,
- say N.
-
-config 8139TOO_TUNE_TWISTER
- bool "Support for uncommon RTL-8139 rev. K (automatic channel equalization)"
- depends on 8139TOO
- help
- This implements a function which might come in handy in case you
- are using low quality on long cabling. It is required for RealTek
- RTL-8139 revision K boards, and totally unused otherwise. It tries
- to match the transceiver to the cable characteristics. This is
- experimental since hardly documented by the manufacturer.
- If unsure, say Y.
-
-config 8139TOO_8129
- bool "Support for older RTL-8129/8130 boards"
- depends on 8139TOO
- help
- This enables support for the older and uncommon RTL-8129 and
- RTL-8130 chips, which support MII via an external transceiver,
- instead of an internal one. Disabling this option will save some
- memory by making the code size smaller. If unsure, say Y.
-
-config 8139_OLD_RX_RESET
- bool "Use older RX-reset method"
- depends on 8139TOO
- help
- The 8139too driver was recently updated to contain a more rapid
- reset sequence, in the face of severe receive errors. This "new"
- RX-reset method should be adequate for all boards. But if you
- experience problems, you can enable this option to restore the
- old RX-reset behavior. If unsure, say N.
-
-config R6040
- tristate "RDC R6040 Fast Ethernet Adapter support"
- depends on NET_PCI && PCI
- select CRC32
- select MII
- select PHYLIB
- help
- This is a driver for the R6040 Fast Ethernet MACs found in the
- the RDC R-321x System-on-chips.
-
- To compile this driver as a module, choose M here: the module
- will be called r6040. This is recommended.
-
-config SIS900
- tristate "SiS 900/7016 PCI Fast Ethernet Adapter support"
- depends on NET_PCI && PCI
- select CRC32
- select MII
- ---help---
- This is a driver for the Fast Ethernet PCI network cards based on
- the SiS 900 and SiS 7016 chips. The SiS 900 core is also embedded in
- SiS 630 and SiS 540 chipsets.
-
- This driver also supports AMD 79C901 HomePNA so that you can use
- your phone line as a network cable.
-
- To compile this driver as a module, choose M here: the module
- will be called sis900. This is recommended.
-
-config EPIC100
- tristate "SMC EtherPower II"
- depends on NET_PCI && PCI
- select CRC32
- select MII
- help
- This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC,
- which is based on the SMC83c17x (EPIC/100).
- More specific information and updates are available from
- <http://www.scyld.com/network/epic100.html>.
-
-config SMSC9420
- tristate "SMSC LAN9420 PCI ethernet adapter support"
- depends on NET_PCI && PCI
- select CRC32
- select PHYLIB
- select SMSC_PHY
- help
- This is a driver for SMSC's LAN9420 PCI ethernet adapter.
- Say Y if you want it compiled into the kernel,
- and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- This driver is also available as a module. The module will be
- called smsc9420. If you want to compile it as a module, say M
- here and read <file:Documentation/kbuild/modules.txt>
-
-config SUNDANCE
- tristate "Sundance Alta support"
- depends on NET_PCI && PCI
- select CRC32
- select MII
- help
- This driver is for the Sundance "Alta" chip.
- More specific information and updates are available from
- <http://www.scyld.com/network/sundance.html>.
-
-config SUNDANCE_MMIO
- bool "Use MMIO instead of PIO"
- depends on SUNDANCE
- help
- Enable memory-mapped I/O for interaction with Sundance NIC registers.
- Do NOT enable this by default, PIO (enabled when MMIO is disabled)
- is known to solve bugs on certain chips.
-
- If unsure, say N.
-
-config TLAN
- tristate "TI ThunderLAN support"
- depends on NET_PCI && (PCI || EISA)
- ---help---
- If you have a PCI Ethernet network card based on the ThunderLAN chip
- which is supported by this driver, say Y and read the
- Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- Devices currently supported by this driver are Compaq Netelligent,
- Compaq NetFlex and Olicom cards. Please read the file
- <file:Documentation/networking/tlan.txt> for more details.
-
- To compile this driver as a module, choose M here. The module
- will be called tlan.
-
- Please email feedback to <torben.mathiasen@compaq.com>.
-
-config KS8842
- tristate "Micrel KSZ8841/42 with generic bus interface"
- depends on HAS_IOMEM && DMA_ENGINE
- help
- This platform driver is for KSZ8841(1-port) / KS8842(2-port)
- ethernet switch chip (managed, VLAN, QoS) from Micrel or
- Timberdale(FPGA).
-
-config KS8851
- tristate "Micrel KS8851 SPI"
- depends on SPI
- select MII
- select CRC32
- help
- SPI driver for Micrel KS8851 SPI attached network chip.
-
-config KS8851_MLL
- tristate "Micrel KS8851 MLL"
- depends on HAS_IOMEM
- select MII
- help
- This platform driver is for Micrel KS8851 Address/data bus
- multiplexed network chip.
-
-config VIA_RHINE
- tristate "VIA Rhine support"
- depends on NET_PCI && PCI
- select CRC32
- select MII
- help
- If you have a VIA "Rhine" based network card (Rhine-I (VT86C100A),
- Rhine-II (VT6102), or Rhine-III (VT6105)), say Y here. Rhine-type
- Ethernet functions can also be found integrated on South Bridges
- (e.g. VT8235).
-
- To compile this driver as a module, choose M here. The module
- will be called via-rhine.
-
-config VIA_RHINE_MMIO
- bool "Use MMIO instead of PIO"
- depends on VIA_RHINE
- help
- This instructs the driver to use PCI shared memory (MMIO) instead of
- programmed I/O ports (PIO). Enabling this gives an improvement in
- processing time in parts of the driver.
-
- If unsure, say Y.
-
-config SC92031
- tristate "Silan SC92031 PCI Fast Ethernet Adapter driver (EXPERIMENTAL)"
- depends on NET_PCI && PCI && EXPERIMENTAL
- select CRC32
- ---help---
- This is a driver for the Fast Ethernet PCI network cards based on
- the Silan SC92031 chip (sometimes also called Rsltek 8139D). If you
- have one of these, say Y here.
-
- To compile this driver as a module, choose M here: the module
- will be called sc92031. This is recommended.
-
-config CPMAC
- tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)"
- depends on NET_ETHERNET && EXPERIMENTAL && AR7
- select PHYLIB
- help
- TI AR7 CPMAC Ethernet support
-
-config NET_POCKET
- bool "Pocket and portable adapters"
- depends on PARPORT
- ---help---
- Cute little network (Ethernet) devices which attach to the parallel
- port ("pocket adapters"), commonly used with laptops. If you have
- one of those, say Y and read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- If you want to plug a network (or some other) card into the PCMCIA
- (or PC-card) slot of your laptop instead (PCMCIA is the standard for
- credit card size extension cards used by all modern laptops), you
- need the pcmcia-cs package (location contained in the file
- <file:Documentation/Changes>) and you can say N here.
-
- Laptop users should read the Linux Laptop home page at
- <http://www.linux-on-laptops.com/> or
- Tuxmobil - Linux on Mobile Computers at <http://www.tuxmobil.org/>.
-
- Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about this class of network devices. If you say Y, you
- will be asked for your specific device in the following questions.
-
-config ATP
- tristate "AT-LAN-TEC/RealTek pocket adapter support"
- depends on NET_POCKET && PARPORT && X86
- select CRC32
- ---help---
- This is a network (Ethernet) device which attaches to your parallel
- port. Read <file:drivers/net/atp.c> as well as the Ethernet-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>, if you
- want to use this. If you intend to use this driver, you should have
- said N to the "Parallel printer support", because the two drivers
- don't like each other.
-
- To compile this driver as a module, choose M here: the module
- will be called atp.
-
-config DE600
- tristate "D-Link DE600 pocket adapter support"
- depends on NET_POCKET && PARPORT
- ---help---
- This is a network (Ethernet) device which attaches to your parallel
- port. Read <file:Documentation/networking/DLINK.txt> as well as the
- Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>, if you want to use
- this. It is possible to have several devices share a single parallel
- port and it is safe to compile the corresponding drivers into the
- kernel.
-
- To compile this driver as a module, choose M here: the module
- will be called de600.
-
-config DE620
- tristate "D-Link DE620 pocket adapter support"
- depends on NET_POCKET && PARPORT
- ---help---
- This is a network (Ethernet) device which attaches to your parallel
- port. Read <file:Documentation/networking/DLINK.txt> as well as the
- Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>, if you want to use
- this. It is possible to have several devices share a single parallel
- port and it is safe to compile the corresponding drivers into the
- kernel.
-
- To compile this driver as a module, choose M here: the module
- will be called de620.
-
-config SGISEEQ
- tristate "SGI Seeq ethernet controller support"
- depends on SGI_HAS_SEEQ
- help
- Say Y here if you have an Seeq based Ethernet network card. This is
- used in many Silicon Graphics machines.
-
-config DECLANCE
- tristate "DEC LANCE ethernet controller support"
- depends on MACH_DECSTATION
- select CRC32
- help
- This driver is for the series of Ethernet controllers produced by
- DEC (now Compaq) based on the AMD Lance chipset, including the
- DEPCA series. (This chipset is better known via the NE2100 cards.)
-
-config 68360_ENET
- bool "Motorola 68360 ethernet controller"
- depends on M68360
- help
- Say Y here if you want to use the built-in ethernet controller of
- the Motorola 68360 processor.
-
-config FEC
- bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)"
- depends on M523x || M527x || M5272 || M528x || M520x || M532x || \
- IMX_HAVE_PLATFORM_FEC || MXS_HAVE_PLATFORM_FEC
- default IMX_HAVE_PLATFORM_FEC || MXS_HAVE_PLATFORM_FEC if ARM
- select PHYLIB
- help
- Say Y here if you want to use the built-in 10/100 Fast ethernet
- controller on some Motorola ColdFire and Freescale i.MX processors.
-
-config FEC_MPC52xx
- tristate "MPC52xx FEC driver"
- depends on PPC_MPC52xx && PPC_BESTCOMM
- select CRC32
- select PHYLIB
- select PPC_BESTCOMM_FEC
- ---help---
- This option enables support for the MPC5200's on-chip
- Fast Ethernet Controller
- If compiled as module, it will be called fec_mpc52xx.
-
-config FEC_MPC52xx_MDIO
- bool "MPC52xx FEC MDIO bus driver"
- depends on FEC_MPC52xx
- default y
- ---help---
- The MPC5200's FEC can connect to the Ethernet either with
- an external MII PHY chip or 10 Mbps 7-wire interface
- (Motorola? industry standard).
- If your board uses an external PHY connected to FEC, enable this.
- If not sure, enable.
- If compiled as module, it will be called fec_mpc52xx_phy.
-
-config NE_H8300
- tristate "NE2000 compatible support for H8/300"
- depends on H8300
- help
- Say Y here if you want to use the NE2000 compatible
- controller on the Renesas H8/300 processor.
-
-config ATL2
- tristate "Atheros L2 Fast Ethernet support"
- depends on PCI
- select CRC32
- select MII
- help
- This driver supports the Atheros L2 fast ethernet adapter.
-
- To compile this driver as a module, choose M here. The module
- will be called atl2.
-
-config XILINX_EMACLITE
- tristate "Xilinx 10/100 Ethernet Lite support"
- depends on PPC32 || MICROBLAZE
- select PHYLIB
- help
- This driver supports the 10/100 Ethernet Lite from Xilinx.
-
-config BCM63XX_ENET
- tristate "Broadcom 63xx internal mac support"
- depends on BCM63XX
- select MII
- select PHYLIB
- help
- This driver supports the ethernet MACs in the Broadcom 63xx
- MIPS chipset family (BCM63XX).
-
-config FTMAC100
- tristate "Faraday FTMAC100 10/100 Ethernet support"
- depends on ARM
- select MII
- help
- This driver supports the FTMAC100 10/100 Ethernet controller
- from Faraday. It is used on Faraday A320, Andes AG101 and some
- other ARM/NDS32 SoC's.
-
-config LANTIQ_ETOP
- tristate "Lantiq SoC ETOP driver"
- depends on SOC_TYPE_XWAY
- help
- Support for the MII0 inside the Lantiq SoC
-
-
-source "drivers/net/fs_enet/Kconfig"
-
-source "drivers/net/octeon/Kconfig"
-
-endif # NET_ETHERNET
-
-#
-# Gigabit Ethernet
-#
-
-menuconfig NETDEV_1000
- bool "Ethernet (1000 Mbit)"
- depends on !UML
- default y
- ---help---
- Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common
- type of Local Area Network (LAN) in universities and companies.
-
- Say Y here to get to see options for Gigabit Ethernet drivers.
- This option alone does not add any kernel code.
- Note that drivers supporting both 100 and 1000 MBit may be listed
- under "Ethernet (10 or 100MBit)" instead.
-
- If you say N, all options in this submenu will be skipped and disabled.
-
-if NETDEV_1000
-
-config ACENIC
- tristate "Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support"
- depends on PCI
- ---help---
- Say Y here if you have an Alteon AceNIC, 3Com 3C985(B), NetGear
- GA620, SGI Gigabit or Farallon PN9000-SX PCI Gigabit Ethernet
- adapter. The driver allows for using the Jumbo Frame option (9000
- bytes/frame) however it requires that your switches can handle this
- as well. To enable Jumbo Frames, add `mtu 9000' to your ifconfig
- line.
-
- To compile this driver as a module, choose M here: the
- module will be called acenic.
-
-config ACENIC_OMIT_TIGON_I
- bool "Omit support for old Tigon I based AceNICs"
- depends on ACENIC
- help
- Say Y here if you only have Tigon II based AceNICs and want to leave
- out support for the older Tigon I based cards which are no longer
- being sold (ie. the original Alteon AceNIC and 3Com 3C985 (non B
- version)). This will reduce the size of the driver object by
- app. 100KB. If you are not sure whether your card is a Tigon I or a
- Tigon II, say N here.
-
- The safe and default value for this is N.
-
-config DL2K
- tristate "DL2000/TC902x-based Gigabit Ethernet support"
- depends on PCI
- select CRC32
- help
- This driver supports DL2000/TC902x-based Gigabit ethernet cards,
- which includes
- D-Link DGE-550T Gigabit Ethernet Adapter.
- D-Link DL2000-based Gigabit Ethernet Adapter.
- Sundance/Tamarack TC902x Gigabit Ethernet Adapter.
-
- To compile this driver as a module, choose M here: the
- module will be called dl2k.
-
-config E1000
- tristate "Intel(R) PRO/1000 Gigabit Ethernet support"
- depends on PCI
- ---help---
- This driver supports Intel(R) PRO/1000 gigabit ethernet family of
- adapters. For more information on how to identify your adapter, go
- to the Adapter & Driver ID Guide at:
-
- <http://support.intel.com/support/network/adapter/pro100/21397.htm>
-
- For general information and support, go to the Intel support
- website at:
-
- <http://support.intel.com>
-
- More specific information on configuring the driver is in
- <file:Documentation/networking/e1000.txt>.
-
- To compile this driver as a module, choose M here. The module
- will be called e1000.
-
-config E1000E
- tristate "Intel(R) PRO/1000 PCI-Express Gigabit Ethernet support"
- select CRC32
- depends on PCI && (!SPARC32 || BROKEN)
- select CRC32
- ---help---
- This driver supports the PCI-Express Intel(R) PRO/1000 gigabit
- ethernet family of adapters. For PCI or PCI-X e1000 adapters,
- use the regular e1000 driver For more information on how to
- identify your adapter, go to the Adapter & Driver ID Guide at:
-
- <http://support.intel.com/support/network/adapter/pro100/21397.htm>
-
- For general information and support, go to the Intel support
- website at:
-
- <http://support.intel.com>
-
- To compile this driver as a module, choose M here. The module
- will be called e1000e.
-
-config IP1000
- tristate "IP1000 Gigabit Ethernet support"
- depends on PCI && EXPERIMENTAL
- select MII
- ---help---
- This driver supports IP1000 gigabit Ethernet cards.
-
- To compile this driver as a module, choose M here: the module
- will be called ipg. This is recommended.
-
-config IGB
- tristate "Intel(R) 82575/82576 PCI-Express Gigabit Ethernet support"
- depends on PCI
- ---help---
- This driver supports Intel(R) 82575/82576 gigabit ethernet family of
- adapters. For more information on how to identify your adapter, go
- to the Adapter & Driver ID Guide at:
-
- <http://support.intel.com/support/network/adapter/pro100/21397.htm>
-
- For general information and support, go to the Intel support
- website at:
-
- <http://support.intel.com>
-
- More specific information on configuring the driver is in
- <file:Documentation/networking/e1000.txt>.
-
- To compile this driver as a module, choose M here. The module
- will be called igb.
-
-config IGB_DCA
- bool "Direct Cache Access (DCA) Support"
- default y
- depends on IGB && DCA && !(IGB=y && DCA=m)
- ---help---
- Say Y here if you want to use Direct Cache Access (DCA) in the
- driver. DCA is a method for warming the CPU cache before data
- is used, with the intent of lessening the impact of cache misses.
-
-config IGBVF
- tristate "Intel(R) 82576 Virtual Function Ethernet support"
- depends on PCI
- ---help---
- This driver supports Intel(R) 82576 virtual functions. For more
- information on how to identify your adapter, go to the Adapter &
- Driver ID Guide at:
-
- <http://support.intel.com/support/network/adapter/pro100/21397.htm>
-
- For general information and support, go to the Intel support
- website at:
-
- <http://support.intel.com>
-
- More specific information on configuring the driver is in
- <file:Documentation/networking/e1000.txt>.
-
- To compile this driver as a module, choose M here. The module
- will be called igbvf.
-
-source "drivers/net/ixp2000/Kconfig"
-
-config MYRI_SBUS
- tristate "MyriCOM Gigabit Ethernet support"
- depends on SBUS
- help
- This driver supports MyriCOM Sbus gigabit Ethernet cards.
-
- To compile this driver as a module, choose M here: the module
- will be called myri_sbus. This is recommended.
-
-config NS83820
- tristate "National Semiconductor DP83820 support"
- depends on PCI
- help
- This is a driver for the National Semiconductor DP83820 series
- of gigabit ethernet MACs. Cards using this chipset include
- the D-Link DGE-500T, PureData's PDP8023Z-TG, SMC's SMC9462TX,
- SOHO-GA2000T, SOHO-GA2500T. The driver supports the use of
- zero copy.
-
-config HAMACHI
- tristate "Packet Engines Hamachi GNIC-II support"
- depends on PCI
- select MII
- help
- If you have a Gigabit Ethernet card of this type, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. The module will be
- called hamachi.
-
-config YELLOWFIN
- tristate "Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)"
- depends on PCI && EXPERIMENTAL
- select CRC32
- ---help---
- Say Y here if you have a Packet Engines G-NIC PCI Gigabit Ethernet
- adapter or the SYM53C885 Ethernet controller. The Gigabit adapter is
- used by the Beowulf Linux cluster project. See
- <http://cesdis.gsfc.nasa.gov/linux/drivers/yellowfin.html> for more
- information about this driver in particular and Beowulf in general.
-
- To compile this driver as a module, choose M here: the module
- will be called yellowfin. This is recommended.
-
-config R8169
- tristate "Realtek 8169 gigabit ethernet support"
- depends on PCI
- select FW_LOADER
- select CRC32
- select MII
- ---help---
- Say Y here if you have a Realtek 8169 PCI Gigabit Ethernet adapter.
-
- To compile this driver as a module, choose M here: the module
- will be called r8169. This is recommended.
-
-config SB1250_MAC
- tristate "SB1250 Gigabit Ethernet support"
- depends on SIBYTE_SB1xxx_SOC
- select PHYLIB
- ---help---
- This driver supports Gigabit Ethernet interfaces based on the
- Broadcom SiByte family of System-On-a-Chip parts. They include
- the BCM1120, BCM1125, BCM1125H, BCM1250, BCM1255, BCM1280, BCM1455
- and BCM1480 chips.
-
- To compile this driver as a module, choose M here: the module
- will be called sb1250-mac.
-
-config SIS190
- tristate "SiS190/SiS191 gigabit ethernet support"
- depends on PCI
- select CRC32
- select MII
- ---help---
- Say Y here if you have a SiS 190 PCI Fast Ethernet adapter or
- a SiS 191 PCI Gigabit Ethernet adapter. Both are expected to
- appear in lan on motherboard designs which are based on SiS 965
- and SiS 966 south bridge.
-
- To compile this driver as a module, choose M here: the module
- will be called sis190. This is recommended.
-
-config SKGE
- tristate "New SysKonnect GigaEthernet support"
- depends on PCI
- select CRC32
- ---help---
- This driver support the Marvell Yukon or SysKonnect SK-98xx/SK-95xx
- and related Gigabit Ethernet adapters. It is a new smaller driver
- with better performance and more complete ethtool support.
-
- It does not support the link failover and network management
- features that "portable" vendor supplied sk98lin driver does.
-
- This driver supports adapters based on the original Yukon chipset:
- Marvell 88E8001, Belkin F5D5005, CNet GigaCard, DLink DGE-530T,
- Linksys EG1032/EG1064, 3Com 3C940/3C940B, SysKonnect SK-9871/9872.
-
- It does not support the newer Yukon2 chipset: a separate driver,
- sky2, is provided for Yukon2-based adapters.
-
- To compile this driver as a module, choose M here: the module
- will be called skge. This is recommended.
-
-config SKGE_DEBUG
- bool "Debugging interface"
- depends on SKGE && DEBUG_FS
- help
- This option adds the ability to dump driver state for debugging.
- The file /sys/kernel/debug/skge/ethX displays the state of the internal
- transmit and receive rings.
-
- If unsure, say N.
-
-config SKY2
- tristate "SysKonnect Yukon2 support"
- depends on PCI
- select CRC32
- ---help---
- This driver supports Gigabit Ethernet adapters based on the
- Marvell Yukon 2 chipset:
- Marvell 88E8021/88E8022/88E8035/88E8036/88E8038/88E8050/88E8052/
- 88E8053/88E8055/88E8061/88E8062, SysKonnect SK-9E21D/SK-9S21
-
- There is companion driver for the older Marvell Yukon and
- Genesis based adapters: skge.
-
- To compile this driver as a module, choose M here: the module
- will be called sky2. This is recommended.
-
-config SKY2_DEBUG
- bool "Debugging interface"
- depends on SKY2 && DEBUG_FS
- help
- This option adds the ability to dump driver state for debugging.
- The file /sys/kernel/debug/sky2/ethX displays the state of the internal
- transmit and receive rings.
-
- If unsure, say N.
-
-config VIA_VELOCITY
- tristate "VIA Velocity support"
- depends on PCI
- select CRC32
- select CRC_CCITT
- select MII
- help
- If you have a VIA "Velocity" based network card say Y here.
-
- To compile this driver as a module, choose M here. The module
- will be called via-velocity.
-
-config TIGON3
- tristate "Broadcom Tigon3 support"
- depends on PCI
- select PHYLIB
- help
- This driver supports Broadcom Tigon3 based gigabit Ethernet cards.
-
- To compile this driver as a module, choose M here: the module
- will be called tg3. This is recommended.
-
-config BNX2
- tristate "Broadcom NetXtremeII support"
- depends on PCI
- select CRC32
- select FW_LOADER
- help
- This driver supports Broadcom NetXtremeII gigabit Ethernet cards.
-
- To compile this driver as a module, choose M here: the module
- will be called bnx2. This is recommended.
-
-config CNIC
- tristate "Broadcom CNIC support"
- depends on PCI
- select BNX2
- select UIO
- help
- This driver supports offload features of Broadcom NetXtremeII
- gigabit Ethernet cards.
-
- To compile this driver as a module, choose M here: the module
- will be called cnic. This is recommended.
-
-config SPIDER_NET
- tristate "Spider Gigabit Ethernet driver"
- depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB)
- select FW_LOADER
- help
- This driver supports the Gigabit Ethernet chips present on the
- Cell Processor-Based Blades from IBM.
-
-config TSI108_ETH
- tristate "Tundra TSI108 gigabit Ethernet support"
- depends on TSI108_BRIDGE
- help
- This driver supports Tundra TSI108 gigabit Ethernet ports.
- To compile this driver as a module, choose M here: the module
- will be called tsi108_eth.
-
-config GELIC_NET
- tristate "PS3 Gigabit Ethernet driver"
- depends on PPC_PS3
- select PS3_SYS_MANAGER
- help
- This driver supports the network device on the PS3 game
- console. This driver has built-in support for Ethernet.
-
- To compile this driver as a module, choose M here: the
- module will be called ps3_gelic.
-
-config GELIC_WIRELESS
- bool "PS3 Wireless support"
- depends on WLAN
- depends on GELIC_NET
- select WIRELESS_EXT
- help
- This option adds the support for the wireless feature of PS3.
- If you have the wireless-less model of PS3 or have no plan to
- use wireless feature, disabling this option saves memory. As
- the driver automatically distinguishes the models, you can
- safely enable this option even if you have a wireless-less model.
-
-config FSL_PQ_MDIO
- tristate "Freescale PQ MDIO"
- depends on FSL_SOC
- select PHYLIB
- help
- This driver supports the MDIO bus used by the gianfar and UCC drivers.
-
-config GIANFAR
- tristate "Gianfar Ethernet"
- depends on FSL_SOC
- select FSL_PQ_MDIO
- select PHYLIB
- select CRC32
- help
- This driver supports the Gigabit TSEC on the MPC83xx, MPC85xx,
- and MPC86xx family of chips, and the FEC on the 8540.
-
-config UCC_GETH
- tristate "Freescale QE Gigabit Ethernet"
- depends on QUICC_ENGINE
- select FSL_PQ_MDIO
- select PHYLIB
- help
- This driver supports the Gigabit Ethernet mode of the QUICC Engine,
- which is available on some Freescale SOCs.
-
-config UGETH_TX_ON_DEMAND
- bool "Transmit on Demand support"
- depends on UCC_GETH
-
-config MV643XX_ETH
- tristate "Marvell Discovery (643XX) and Orion ethernet support"
- depends on (MV64X60 || PPC32 || PLAT_ORION) && INET
- select INET_LRO
- select PHYLIB
- help
- This driver supports the gigabit ethernet MACs in the
- Marvell Discovery PPC/MIPS chipset family (MV643XX) and
- in the Marvell Orion ARM SoC family.
-
- Some boards that use the Discovery chipset are the Momenco
- Ocelot C and Jaguar ATX and Pegasos II.
-
-config XILINX_LL_TEMAC
- tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver"
- depends on PPC || MICROBLAZE
- select PHYLIB
- help
- This driver supports the Xilinx 10/100/1000 LocalLink TEMAC
- core used in Xilinx Spartan and Virtex FPGAs
-
-config QLA3XXX
- tristate "QLogic QLA3XXX Network Driver Support"
- depends on PCI
- help
- This driver supports QLogic ISP3XXX gigabit Ethernet cards.
-
- To compile this driver as a module, choose M here: the module
- will be called qla3xxx.
-
-config ATL1
- tristate "Atheros/Attansic L1 Gigabit Ethernet support"
- depends on PCI
- select CRC32
- select MII
- help
- This driver supports the Atheros/Attansic L1 gigabit ethernet
- adapter.
-
- To compile this driver as a module, choose M here. The module
- will be called atl1.
-
-config ATL1E
- tristate "Atheros L1E Gigabit Ethernet support (EXPERIMENTAL)"
- depends on PCI && EXPERIMENTAL
- select CRC32
- select MII
- help
- This driver supports the Atheros L1E gigabit ethernet adapter.
-
- 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
- select CRC32
- select MII
- ---help---
- This driver supports the PCI-Express gigabit ethernet adapters
- based on JMicron JMC250 chipset.
-
- To compile this driver as a module, choose M here. The module
- will be called jme.
-
-config S6GMAC
- tristate "S6105 GMAC ethernet support"
- depends on XTENSA_VARIANT_S6000
- select PHYLIB
- help
- This driver supports the on chip ethernet device on the
- S6105 xtensa processor.
-
- To compile this driver as a module, choose M here. The module
- will be called s6gmac.
-
-source "drivers/net/stmmac/Kconfig"
-
-config PCH_GBE
- tristate "Intel EG20T PCH / OKI SEMICONDUCTOR ML7223 IOH GbE"
- depends on PCI
- select MII
- ---help---
- This is a gigabit ethernet driver for EG20T PCH.
- EG20T PCH is the platform controller hub that is used in Intel's
- general embedded platform.
- EG20T PCH has Gigabit Ethernet interface.
- Using this interface, it is able to access system devices connected
- to Gigabit Ethernet.
- This driver enables Gigabit Ethernet function.
-
- This driver also can be used for OKI SEMICONDUCTOR IOH(Input/
- Output Hub), ML7223.
- ML7223 IOH is for MP(Media Phone) use.
- ML7223 is companion chip for Intel Atom E6xx series.
- ML7223 is completely compatible for Intel EG20T PCH.
-
-endif # NETDEV_1000
-
-#
-# 10 Gigabit Ethernet
-#
-
-menuconfig NETDEV_10000
- bool "Ethernet (10000 Mbit)"
- depends on !UML
- default y
- ---help---
- Say Y here to get to see options for 10 Gigabit Ethernet drivers.
- This option alone does not add any kernel code.
-
- If you say N, all options in this submenu will be skipped and disabled.
-
-if NETDEV_10000
-
-config MDIO
- tristate
-
-config CHELSIO_T1
- tristate "Chelsio 10Gb Ethernet support"
- depends on PCI
- select CRC32
- select MDIO
- help
- This driver supports Chelsio gigabit and 10-gigabit
- Ethernet cards. More information about adapter features and
- performance tuning is in <file:Documentation/networking/cxgb.txt>.
-
- For general information about Chelsio and our products, visit
- our website at <http://www.chelsio.com>.
-
- For customer support, please visit our customer support page at
- <http://www.chelsio.com/support.html>.
-
- Please send feedback to <linux-bugs@chelsio.com>.
-
- To compile this driver as a module, choose M here: the module
- will be called cxgb.
-
-config CHELSIO_T1_1G
- bool "Chelsio gigabit Ethernet support"
- depends on CHELSIO_T1
- help
- Enables support for Chelsio's gigabit Ethernet PCI cards. If you
- are using only 10G cards say 'N' here.
-
-config CHELSIO_T3
- tristate "Chelsio Communications T3 10Gb Ethernet support"
- depends on PCI && INET
- select FW_LOADER
- select MDIO
- help
- This driver supports Chelsio T3-based gigabit and 10Gb Ethernet
- adapters.
-
- For general information about Chelsio and our products, visit
- our website at <http://www.chelsio.com>.
-
- For customer support, please visit our customer support page at
- <http://www.chelsio.com/support.html>.
-
- Please send feedback to <linux-bugs@chelsio.com>.
-
- To compile this driver as a module, choose M here: the module
- will be called cxgb3.
-
-config CHELSIO_T4
- tristate "Chelsio Communications T4 Ethernet support"
- depends on PCI
- select FW_LOADER
- select MDIO
- help
- This driver supports Chelsio T4-based gigabit and 10Gb Ethernet
- adapters.
-
- For general information about Chelsio and our products, visit
- our website at <http://www.chelsio.com>.
-
- For customer support, please visit our customer support page at
- <http://www.chelsio.com/support.html>.
-
- Please send feedback to <linux-bugs@chelsio.com>.
-
- To compile this driver as a module choose M here; the module
- will be called cxgb4.
-
-config CHELSIO_T4VF
- tristate "Chelsio Communications T4 Virtual Function Ethernet support"
- depends on PCI
- help
- This driver supports Chelsio T4-based gigabit and 10Gb Ethernet
- adapters with PCI-E SR-IOV Virtual Functions.
-
- For general information about Chelsio and our products, visit
- our website at <http://www.chelsio.com>.
-
- For customer support, please visit our customer support page at
- <http://www.chelsio.com/support.html>.
-
- Please send feedback to <linux-bugs@chelsio.com>.
-
- To compile this driver as a module choose M here; the module
- will be called cxgb4vf.
-
-config EHEA
- tristate "eHEA Ethernet support"
- depends on IBMEBUS && INET && SPARSEMEM
- select INET_LRO
- ---help---
- This driver supports the IBM pSeries eHEA ethernet adapter.
-
- To compile the driver as a module, choose M here. The module
- will be called ehea.
-
-config ENIC
- tristate "Cisco VIC Ethernet NIC Support"
- depends on PCI && INET
- help
- This enables the support for the Cisco VIC Ethernet card.
-
-config IXGBE
- tristate "Intel(R) 10GbE PCI Express adapters support"
- depends on PCI && INET
- select MDIO
- ---help---
- This driver supports Intel(R) 10GbE PCI Express family of
- adapters. For more information on how to identify your adapter, go
- to the Adapter & Driver ID Guide at:
-
- <http://support.intel.com/support/network/adapter/pro100/21397.htm>
-
- For general information and support, go to the Intel support
- website at:
-
- <http://support.intel.com>
-
- To compile this driver as a module, choose M here. The module
- will be called ixgbe.
-
-config IXGBE_DCA
- bool "Direct Cache Access (DCA) Support"
- default y
- depends on IXGBE && DCA && !(IXGBE=y && DCA=m)
- ---help---
- Say Y here if you want to use Direct Cache Access (DCA) in the
- driver. DCA is a method for warming the CPU cache before data
- is used, with the intent of lessening the impact of cache misses.
-
-config IXGBE_DCB
- bool "Data Center Bridging (DCB) Support"
- default n
- depends on IXGBE && DCB
- ---help---
- Say Y here if you want to use Data Center Bridging (DCB) in the
- driver.
-
- If unsure, say N.
-
-config IXGBEVF
- tristate "Intel(R) 82599 Virtual Function Ethernet support"
- depends on PCI_MSI
- ---help---
- This driver supports Intel(R) 82599 virtual functions. For more
- information on how to identify your adapter, go to the Adapter &
- Driver ID Guide at:
-
- <http://support.intel.com/support/network/sb/CS-008441.htm>
-
- For general information and support, go to the Intel support
- website at:
-
- <http://support.intel.com>
-
- More specific information on configuring the driver is in
- <file:Documentation/networking/ixgbevf.txt>.
-
- To compile this driver as a module, choose M here. The module
- will be called ixgbevf. MSI-X interrupt support is required
- for this driver to work correctly.
-
-config IXGB
- tristate "Intel(R) PRO/10GbE support"
- depends on PCI
- ---help---
- This driver supports Intel(R) PRO/10GbE family of adapters for
- PCI-X type cards. For PCI-E type cards, use the "ixgbe" driver
- instead. For more information on how to identify your adapter, go
- to the Adapter & Driver ID Guide at:
-
- <http://support.intel.com/support/network/adapter/pro100/21397.htm>
-
- For general information and support, go to the Intel support
- website at:
-
- <http://support.intel.com>
-
- More specific information on configuring the driver is in
- <file:Documentation/networking/ixgb.txt>.
-
- To compile this driver as a module, choose M here. The module
- will be called ixgb.
-
-config S2IO
- tristate "Exar Xframe 10Gb Ethernet Adapter"
- depends on PCI
- ---help---
- This driver supports Exar Corp's Xframe Series 10Gb Ethernet Adapters.
-
- More specific information on configuring the driver is in
- <file:Documentation/networking/s2io.txt>.
-
- To compile this driver as a module, choose M here. The module
- will be called s2io.
-
-config VXGE
- tristate "Exar X3100 Series 10GbE PCIe Server Adapter"
- depends on PCI && INET
- ---help---
- This driver supports Exar Corp's X3100 Series 10 GbE PCIe
- I/O Virtualized Server Adapter.
-
- More specific information on configuring the driver is in
- <file:Documentation/networking/vxge.txt>.
-
- To compile this driver as a module, choose M here. The module
- will be called vxge.
-
-config VXGE_DEBUG_TRACE_ALL
- bool "Enabling All Debug trace statments in driver"
- default n
- depends on VXGE
- ---help---
- Say Y here if you want to enabling all the debug trace statements in
- the vxge driver. By default only few debug trace statements are
- enabled.
-
-config MYRI10GE
- tristate "Myricom Myri-10G Ethernet support"
- depends on PCI && INET
- select FW_LOADER
- select CRC32
- select INET_LRO
- ---help---
- This driver supports Myricom Myri-10G Dual Protocol interface in
- Ethernet mode. If the eeprom on your board is not recent enough,
- you will need a newer firmware image.
- You may get this image or more information, at:
-
- <http://www.myri.com/scs/download-Myri10GE.html>
-
- To compile this driver as a module, choose M here. The module
- will be called myri10ge.
-
-config MYRI10GE_DCA
- bool "Direct Cache Access (DCA) Support"
- default y
- depends on MYRI10GE && DCA && !(MYRI10GE=y && DCA=m)
- ---help---
- Say Y here if you want to use Direct Cache Access (DCA) in the
- driver. DCA is a method for warming the CPU cache before data
- is used, with the intent of lessening the impact of cache misses.
-
-config NETXEN_NIC
- tristate "NetXen Multi port (1/10) Gigabit Ethernet NIC"
- depends on PCI
- select FW_LOADER
- help
- This enables the support for NetXen's Gigabit Ethernet card.
-
-config NIU
- tristate "Sun Neptune 10Gbit Ethernet support"
- depends on PCI
- select CRC32
- help
- This enables support for cards based upon Sun's
- Neptune chipset.
-
-config PASEMI_MAC
- tristate "PA Semi 1/10Gbit MAC"
- depends on PPC_PASEMI && PCI && INET
- select PHYLIB
- select INET_LRO
- help
- This driver supports the on-chip 1/10Gbit Ethernet controller on
- PA Semi's PWRficient line of chips.
-
-config MLX4_EN
- tristate "Mellanox Technologies 10Gbit Ethernet support"
- depends on PCI && INET
- select MLX4_CORE
- select INET_LRO
- help
- This driver supports Mellanox Technologies ConnectX Ethernet
- devices.
-
-config MLX4_CORE
- tristate
- depends on PCI
- default n
-
-config MLX4_DEBUG
- bool "Verbose debugging output" if (MLX4_CORE && EXPERT)
- depends on MLX4_CORE
- default y
- ---help---
- This option causes debugging code to be compiled into the
- mlx4_core driver. The output can be turned on via the
- debug_level module parameter (which can also be set after
- the driver is loaded through sysfs).
-
-config TEHUTI
- tristate "Tehuti Networks 10G Ethernet"
- depends on PCI
- help
- Tehuti Networks 10G Ethernet NIC
-
-config BNX2X
- tristate "Broadcom NetXtremeII 10Gb support"
- depends on PCI
- select FW_LOADER
- select ZLIB_INFLATE
- select LIBCRC32C
- select MDIO
- help
- This driver supports Broadcom NetXtremeII 10 gigabit Ethernet cards.
- To compile this driver as a module, choose M here: the module
- will be called bnx2x. This is recommended.
-
-config QLCNIC
- tristate "QLOGIC QLCNIC 1/10Gb Converged Ethernet NIC Support"
- depends on PCI
- select FW_LOADER
- help
- This driver supports QLogic QLE8240 and QLE8242 Converged Ethernet
- devices.
-
-config QLGE
- tristate "QLogic QLGE 10Gb Ethernet Driver Support"
- depends on PCI
- help
- This driver supports QLogic ISP8XXX 10Gb Ethernet cards.
-
- To compile this driver as a module, choose M here: the module
- will be called qlge.
-
-config BNA
- tristate "Brocade 1010/1020 10Gb Ethernet Driver support"
- depends on PCI
- ---help---
- This driver supports Brocade 1010/1020 10Gb CEE capable Ethernet
- cards.
- To compile this driver as a module, choose M here: the module
- will be called bna.
-
- For general information and support, go to the Brocade support
- website at:
-
- <http://support.brocade.com>
-
-source "drivers/net/sfc/Kconfig"
-
-source "drivers/net/benet/Kconfig"
-
-endif # NETDEV_10000
+source "drivers/s390/net/Kconfig"
source "drivers/net/tokenring/Kconfig"
+source "drivers/net/usb/Kconfig"
+
source "drivers/net/wireless/Kconfig"
source "drivers/net/wimax/Kconfig"
-source "drivers/net/usb/Kconfig"
-
-source "drivers/net/pcmcia/Kconfig"
-
source "drivers/net/wan/Kconfig"
-source "drivers/atm/Kconfig"
-
-source "drivers/ieee802154/Kconfig"
-
-source "drivers/s390/net/Kconfig"
-
-source "drivers/net/caif/Kconfig"
-
-config TILE_NET
- tristate "Tilera GBE/XGBE network driver support"
- depends on TILE
- default y
- select CRC32
- help
- This is a standard Linux network device driver for the
- on-chip Tilera Gigabit Ethernet and XAUI interfaces.
-
- To compile this driver as a module, choose M here: the module
- will be called tile_net.
-
config XEN_NETDEV_FRONTEND
tristate "Xen network device frontend driver"
depends on XEN
@@ -3000,448 +330,6 @@ config XEN_NETDEV_BACKEND
compile this driver as a module, chose M here: the module
will be called xen-netback.
-config ISERIES_VETH
- tristate "iSeries Virtual Ethernet driver support"
- depends on PPC_ISERIES
-
-config RIONET
- tristate "RapidIO Ethernet over messaging driver support"
- depends on RAPIDIO
-
-config RIONET_TX_SIZE
- int "Number of outbound queue entries"
- depends on RIONET
- default "128"
-
-config RIONET_RX_SIZE
- int "Number of inbound queue entries"
- depends on RIONET
- default "128"
-
-config FDDI
- tristate "FDDI driver support"
- depends on (PCI || EISA || TC)
- help
- Fiber Distributed Data Interface is a high speed local area network
- design; essentially a replacement for high speed Ethernet. FDDI can
- run over copper or fiber. If you are connected to such a network and
- want a driver for the FDDI card in your computer, say Y here (and
- then also Y to the driver for your FDDI card, below). Most people
- will say N.
-
-config DEFXX
- tristate "Digital DEFTA/DEFEA/DEFPA adapter support"
- depends on FDDI && (PCI || EISA || TC)
- ---help---
- This is support for the DIGITAL series of TURBOchannel (DEFTA),
- EISA (DEFEA) and PCI (DEFPA) controllers which can connect you
- to a local FDDI network.
-
- To compile this driver as a module, choose M here: the module
- will be called defxx. If unsure, say N.
-
-config DEFXX_MMIO
- bool
- prompt "Use MMIO instead of PIO" if PCI || EISA
- depends on DEFXX
- default n if PCI || EISA
- default y
- ---help---
- This instructs the driver to use EISA or PCI memory-mapped I/O
- (MMIO) as appropriate instead of programmed I/O ports (PIO).
- Enabling this gives an improvement in processing time in parts
- of the driver, but it may cause problems with EISA (DEFEA)
- adapters. TURBOchannel does not have the concept of I/O ports,
- so MMIO is always used for these (DEFTA) adapters.
-
- If unsure, say N.
-
-config SKFP
- tristate "SysKonnect FDDI PCI support"
- depends on FDDI && PCI
- select BITREVERSE
- ---help---
- Say Y here if you have a SysKonnect FDDI PCI adapter.
- The following adapters are supported by this driver:
- - SK-5521 (SK-NET FDDI-UP)
- - SK-5522 (SK-NET FDDI-UP DAS)
- - SK-5541 (SK-NET FDDI-FP)
- - SK-5543 (SK-NET FDDI-LP)
- - SK-5544 (SK-NET FDDI-LP DAS)
- - SK-5821 (SK-NET FDDI-UP64)
- - SK-5822 (SK-NET FDDI-UP64 DAS)
- - SK-5841 (SK-NET FDDI-FP64)
- - SK-5843 (SK-NET FDDI-LP64)
- - SK-5844 (SK-NET FDDI-LP64 DAS)
- - Netelligent 100 FDDI DAS Fibre SC
- - Netelligent 100 FDDI SAS Fibre SC
- - Netelligent 100 FDDI DAS UTP
- - Netelligent 100 FDDI SAS UTP
- - Netelligent 100 FDDI SAS Fibre MIC
-
- Read <file:Documentation/networking/skfp.txt> for information about
- the driver.
-
- Questions concerning this driver can be addressed to:
- <linux@syskonnect.de>
-
- To compile this driver as a module, choose M here: the module
- will be called skfp. This is recommended.
-
-config HIPPI
- bool "HIPPI driver support (EXPERIMENTAL)"
- depends on EXPERIMENTAL && INET && PCI
- help
- HIgh Performance Parallel Interface (HIPPI) is a 800Mbit/sec and
- 1600Mbit/sec dual-simplex switched or point-to-point network. HIPPI
- can run over copper (25m) or fiber (300m on multi-mode or 10km on
- single-mode). HIPPI networks are commonly used for clusters and to
- connect to super computers. If you are connected to a HIPPI network
- and have a HIPPI network card in your computer that you want to use
- under Linux, say Y here (you must also remember to enable the driver
- for your HIPPI card below). Most people will say N here.
-
-config ROADRUNNER
- tristate "Essential RoadRunner HIPPI PCI adapter support (EXPERIMENTAL)"
- depends on HIPPI && PCI
- help
- Say Y here if this is your PCI HIPPI network card.
-
- To compile this driver as a module, choose M here: the module
- will be called rrunner. If unsure, say N.
-
-config ROADRUNNER_LARGE_RINGS
- bool "Use large TX/RX rings (EXPERIMENTAL)"
- depends on ROADRUNNER
- help
- If you say Y here, the RoadRunner driver will preallocate up to 2 MB
- of additional memory to allow for fastest operation, both for
- transmitting and receiving. This memory cannot be used by any other
- kernel code or by user space programs. Say Y here only if you have
- the memory.
-
-config PLIP
- tristate "PLIP (parallel port) support"
- depends on PARPORT
- ---help---
- PLIP (Parallel Line Internet Protocol) is used to create a
- reasonably fast mini network consisting of two (or, rarely, more)
- local machines. A PLIP link from a Linux box is a popular means to
- install a Linux distribution on a machine which doesn't have a
- CD-ROM drive (a minimal system has to be transferred with floppies
- first). The kernels on both machines need to have this PLIP option
- enabled for this to work.
-
- The PLIP driver has two modes, mode 0 and mode 1. The parallel
- ports (the connectors at the computers with 25 holes) are connected
- with "null printer" or "Turbo Laplink" cables which can transmit 4
- bits at a time (mode 0) or with special PLIP cables, to be used on
- bidirectional parallel ports only, which can transmit 8 bits at a
- time (mode 1); you can find the wiring of these cables in
- <file:Documentation/networking/PLIP.txt>. The cables can be up to
- 15m long. Mode 0 works also if one of the machines runs DOS/Windows
- and has some PLIP software installed, e.g. the Crynwr PLIP packet
- driver (<http://oak.oakland.edu/simtel.net/msdos/pktdrvr-pre.html>)
- and winsock or NCSA's telnet.
-
- If you want to use PLIP, say Y and read the PLIP mini-HOWTO as well
- as the NET-3-HOWTO, both available from
- <http://www.tldp.org/docs.html#howto>. Note that the PLIP
- protocol has been changed and this PLIP driver won't work together
- with the PLIP support in Linux versions 1.0.x. This option enlarges
- your kernel by about 8 KB.
-
- To compile this driver as a module, choose M here. The module
- will be called plip. If unsure, say Y or M, in case you buy
- a laptop later.
-
-config PPP
- tristate "PPP (point-to-point protocol) support"
- select SLHC
- ---help---
- PPP (Point to Point Protocol) is a newer and better SLIP. It serves
- the same purpose: sending Internet traffic over telephone (and other
- serial) lines. Ask your access provider if they support it, because
- otherwise you can't use it; most Internet access providers these
- days support PPP rather than SLIP.
-
- To use PPP, you need an additional program called pppd as described
- in the PPP-HOWTO, available at
- <http://www.tldp.org/docs.html#howto>. Make sure that you have
- the version of pppd recommended in <file:Documentation/Changes>.
- The PPP option enlarges your kernel by about 16 KB.
-
- There are actually two versions of PPP: the traditional PPP for
- asynchronous lines, such as regular analog phone lines, and
- synchronous PPP which can be used over digital ISDN lines for
- example. If you want to use PPP over phone lines or other
- asynchronous serial lines, you need to say Y (or M) here and also to
- the next option, "PPP support for async serial ports". For PPP over
- synchronous lines, you should say Y (or M) here and to "Support
- synchronous PPP", below.
-
- If you said Y to "Version information on all symbols" above, then
- you cannot compile the PPP driver into the kernel; you can then only
- compile it as a module. To compile this driver as a module, choose M
- here. The module will be called ppp_generic.
-
-config PPP_MULTILINK
- bool "PPP multilink support (EXPERIMENTAL)"
- depends on PPP && EXPERIMENTAL
- help
- PPP multilink is a protocol (defined in RFC 1990) which allows you
- to combine several (logical or physical) lines into one logical PPP
- connection, so that you can utilize your full bandwidth.
-
- This has to be supported at the other end as well and you need a
- version of the pppd daemon which understands the multilink protocol.
-
- If unsure, say N.
-
-config PPP_FILTER
- bool "PPP filtering"
- depends on PPP
- help
- Say Y here if you want to be able to filter the packets passing over
- PPP interfaces. This allows you to control which packets count as
- activity (i.e. which packets will reset the idle timer or bring up
- a demand-dialed link) and which packets are to be dropped entirely.
- You need to say Y here if you wish to use the pass-filter and
- active-filter options to pppd.
-
- If unsure, say N.
-
-config PPP_ASYNC
- tristate "PPP support for async serial ports"
- depends on PPP
- select CRC_CCITT
- ---help---
- Say Y (or M) here if you want to be able to use PPP over standard
- asynchronous serial ports, such as COM1 or COM2 on a PC. If you use
- a modem (not a synchronous or ISDN modem) to contact your ISP, you
- need this option.
-
- To compile this driver as a module, choose M here.
-
- If unsure, say Y.
-
-config PPP_SYNC_TTY
- tristate "PPP support for sync tty ports"
- depends on PPP
- help
- Say Y (or M) here if you want to be able to use PPP over synchronous
- (HDLC) tty devices, such as the SyncLink adapter. These devices
- are often used for high-speed leased lines like T1/E1.
-
- To compile this driver as a module, choose M here.
-
-config PPP_DEFLATE
- tristate "PPP Deflate compression"
- depends on PPP
- select ZLIB_INFLATE
- select ZLIB_DEFLATE
- ---help---
- Support for the Deflate compression method for PPP, which uses the
- Deflate algorithm (the same algorithm that gzip uses) to compress
- each PPP packet before it is sent over the wire. The machine at the
- other end of the PPP link (usually your ISP) has to support the
- Deflate compression method as well for this to be useful. Even if
- they don't support it, it is safe to say Y here.
-
- To compile this driver as a module, choose M here.
-
-config PPP_BSDCOMP
- tristate "PPP BSD-Compress compression"
- depends on PPP
- ---help---
- Support for the BSD-Compress compression method for PPP, which uses
- the LZW compression method to compress each PPP packet before it is
- sent over the wire. The machine at the other end of the PPP link
- (usually your ISP) has to support the BSD-Compress compression
- method as well for this to be useful. Even if they don't support it,
- it is safe to say Y here.
-
- The PPP Deflate compression method ("PPP Deflate compression",
- above) is preferable to BSD-Compress, because it compresses better
- and is patent-free.
-
- Note that the BSD compression code will always be compiled as a
- module; it is called bsd_comp and will show up in the directory
- modules once you have said "make modules". If unsure, say N.
-
-config PPP_MPPE
- tristate "PPP MPPE compression (encryption) (EXPERIMENTAL)"
- depends on PPP && EXPERIMENTAL
- select CRYPTO
- select CRYPTO_SHA1
- select CRYPTO_ARC4
- select CRYPTO_ECB
- ---help---
- Support for the MPPE Encryption protocol, as employed by the
- Microsoft Point-to-Point Tunneling Protocol.
-
- See http://pptpclient.sourceforge.net/ for information on
- configuring PPTP clients and servers to utilize this method.
-
-config PPPOE
- tristate "PPP over Ethernet (EXPERIMENTAL)"
- depends on EXPERIMENTAL && PPP
- help
- Support for PPP over Ethernet.
-
- This driver requires the latest version of pppd from the CVS
- repository at cvs.samba.org. Alternatively, see the
- RoaringPenguin package (<http://www.roaringpenguin.com/pppoe>)
- which contains instruction on how to use this driver (under
- the heading "Kernel mode PPPoE").
-
-config PPTP
- tristate "PPP over IPv4 (PPTP) (EXPERIMENTAL)"
- depends on EXPERIMENTAL && PPP && NET_IPGRE_DEMUX
- help
- Support for PPP over IPv4.(Point-to-Point Tunneling Protocol)
-
- This driver requires pppd plugin to work in client mode or
- modified pptpd (poptop) to work in server mode.
- See http://accel-pptp.sourceforge.net/ for information how to
- utilize this module.
-
-config PPPOATM
- tristate "PPP over ATM"
- depends on ATM && PPP
- help
- Support PPP (Point to Point Protocol) encapsulated in ATM frames.
- This implementation does not yet comply with section 8 of RFC2364,
- which can lead to bad results if the ATM peer loses state and
- changes its encapsulation unilaterally.
-
-config PPPOL2TP
- tristate "PPP over L2TP (EXPERIMENTAL)"
- depends on EXPERIMENTAL && L2TP && PPP
- help
- Support for PPP-over-L2TP socket family. L2TP is a protocol
- used by ISPs and enterprises to tunnel PPP traffic over UDP
- tunnels. L2TP is replacing PPTP for VPN uses.
-
-config SLIP
- tristate "SLIP (serial line) support"
- ---help---
- Say Y if you intend to use SLIP or CSLIP (compressed SLIP) to
- connect to your Internet service provider or to connect to some
- other local Unix box or if you want to configure your Linux box as a
- Slip/CSlip server for other people to dial in. SLIP (Serial Line
- Internet Protocol) is a protocol used to send Internet traffic over
- serial connections such as telephone lines or null modem cables;
- nowadays, the protocol PPP is more commonly used for this same
- purpose.
-
- Normally, your access provider has to support SLIP in order for you
- to be able to use it, but there is now a SLIP emulator called SLiRP
- around (available from
- <ftp://ibiblio.org/pub/Linux/system/network/serial/>) which
- allows you to use SLIP over a regular dial up shell connection. If
- you plan to use SLiRP, make sure to say Y to CSLIP, below. The
- NET-3-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>, explains how to
- configure SLIP. Note that you don't need this option if you just
- want to run term (term is a program which gives you almost full
- Internet connectivity if you have a regular dial up shell account on
- some Internet connected Unix computer. Read
- <http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html>). SLIP
- support will enlarge your kernel by about 4 KB. If unsure, say N.
-
- To compile this driver as a module, choose M here. The module
- will be called slip.
-
-config SLIP_COMPRESSED
- bool "CSLIP compressed headers"
- depends on SLIP
- select SLHC
- ---help---
- This protocol is faster than SLIP because it uses compression on the
- TCP/IP headers (not on the data itself), but it has to be supported
- on both ends. Ask your access provider if you are not sure and
- answer Y, just in case. You will still be able to use plain SLIP. If
- you plan to use SLiRP, the SLIP emulator (available from
- <ftp://ibiblio.org/pub/Linux/system/network/serial/>) which
- allows you to use SLIP over a regular dial up shell connection, you
- definitely want to say Y here. The NET-3-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>, explains how to configure
- CSLIP. This won't enlarge your kernel.
-
-config SLHC
- tristate
- help
- This option enables Van Jacobsen serial line header compression
- routines.
-
-config SLIP_SMART
- bool "Keepalive and linefill"
- depends on SLIP
- help
- Adds additional capabilities to the SLIP driver to support the
- RELCOM line fill and keepalive monitoring. Ideal on poor quality
- analogue lines.
-
-config SLIP_MODE_SLIP6
- bool "Six bit SLIP encapsulation"
- depends on SLIP
- help
- Just occasionally you may need to run IP over hostile serial
- networks that don't pass all control characters or are only seven
- bit. Saying Y here adds an extra mode you can use with SLIP:
- "slip6". In this mode, SLIP will only send normal ASCII symbols over
- the serial device. Naturally, this has to be supported at the other
- end of the link as well. It's good enough, for example, to run IP
- over the async ports of a Camtec JNT Pad. If unsure, say N.
-
-config NET_FC
- bool "Fibre Channel driver support"
- depends on SCSI && PCI
- help
- Fibre Channel is a high speed serial protocol mainly used to connect
- large storage devices to the computer; it is compatible with and
- intended to replace SCSI.
-
- If you intend to use Fibre Channel, you need to have a Fibre channel
- adaptor card in your computer; say Y here and to the driver for your
- adaptor below. You also should have said Y to "SCSI support" and
- "SCSI generic support".
-
-config NETCONSOLE
- tristate "Network console logging support"
- ---help---
- If you want to log kernel messages over the network, enable this.
- See <file:Documentation/networking/netconsole.txt> for details.
-
-config NETCONSOLE_DYNAMIC
- bool "Dynamic reconfiguration of logging targets"
- depends on NETCONSOLE && SYSFS && CONFIGFS_FS && \
- !(NETCONSOLE=y && CONFIGFS_FS=m)
- help
- This option enables the ability to dynamically reconfigure target
- parameters (interface, IP addresses, port numbers, MAC addresses)
- at runtime through a userspace interface exported using configfs.
- See <file:Documentation/networking/netconsole.txt> for details.
-
-config NETPOLL
- def_bool NETCONSOLE
-
-config NETPOLL_TRAP
- bool "Netpoll traffic trapping"
- default n
- depends on NETPOLL
-
-config NET_POLL_CONTROLLER
- def_bool NETPOLL
-
-config VIRTIO_NET
- tristate "Virtio network driver (EXPERIMENTAL)"
- depends on EXPERIMENTAL && VIRTIO
- ---help---
- This is the virtual network driver for virtio. It can be used with
- lguest or QEMU based VMMs (like KVM or Xen). Say Y or M.
-
config VMXNET3
tristate "VMware VMXNET3 ethernet driver"
depends on PCI && INET
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 776a478e6296..fa877cd2b139 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -1,279 +1,61 @@
#
-# Makefile for the Linux network (ethercard) device drivers.
+# Makefile for the Linux network device drivers.
#
-obj-$(CONFIG_MII) += mii.o
-obj-$(CONFIG_MDIO) += mdio.o
-obj-$(CONFIG_PHYLIB) += phy/
-
-obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o
-obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o
-obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o
-
-obj-$(CONFIG_E1000) += e1000/
-obj-$(CONFIG_E1000E) += e1000e/
-obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac/
-obj-$(CONFIG_IGB) += igb/
-obj-$(CONFIG_IGBVF) += igbvf/
-obj-$(CONFIG_IXGBE) += ixgbe/
-obj-$(CONFIG_IXGBEVF) += ixgbevf/
-obj-$(CONFIG_IXGB) += ixgb/
-obj-$(CONFIG_IP1000) += ipg.o
-obj-$(CONFIG_CHELSIO_T1) += chelsio/
-obj-$(CONFIG_CHELSIO_T3) += cxgb3/
-obj-$(CONFIG_CHELSIO_T4) += cxgb4/
-obj-$(CONFIG_CHELSIO_T4VF) += cxgb4vf/
-obj-$(CONFIG_EHEA) += ehea/
-obj-$(CONFIG_CAN) += can/
-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_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o
-obj-$(CONFIG_TEHUTI) += tehuti.o
-obj-$(CONFIG_ENIC) += enic/
-obj-$(CONFIG_JME) += jme.o
-obj-$(CONFIG_BE2NET) += benet/
-obj-$(CONFIG_VMXNET3) += vmxnet3/
-obj-$(CONFIG_BNA) += bna/
-
-gianfar_driver-objs := gianfar.o \
- gianfar_ethtool.o \
- gianfar_sysfs.o
-
-obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o
-ucc_geth_driver-objs := ucc_geth.o ucc_geth_ethtool.o
-
-obj-$(CONFIG_FSL_PQ_MDIO) += fsl_pq_mdio.o
-
-#
-# link order important here
#
-obj-$(CONFIG_PLIP) += plip.o
-
-obj-$(CONFIG_ROADRUNNER) += rrunner.o
-
-obj-$(CONFIG_HAPPYMEAL) += sunhme.o
-obj-$(CONFIG_SUNLANCE) += sunlance.o
-obj-$(CONFIG_SUNQE) += sunqe.o
-obj-$(CONFIG_SUNBMAC) += sunbmac.o
-obj-$(CONFIG_MYRI_SBUS) += myri_sbus.o
-obj-$(CONFIG_SUNGEM) += sungem.o sungem_phy.o
-obj-$(CONFIG_CASSINI) += cassini.o
-obj-$(CONFIG_SUNVNET) += sunvnet.o
-
-obj-$(CONFIG_MACE) += mace.o
-obj-$(CONFIG_BMAC) += bmac.o
-
-obj-$(CONFIG_VORTEX) += 3c59x.o
-obj-$(CONFIG_TYPHOON) += typhoon.o
-obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
-obj-$(CONFIG_PCNET32) += pcnet32.o
-obj-$(CONFIG_E100) += e100.o
-obj-$(CONFIG_TLAN) += tlan.o
-obj-$(CONFIG_EPIC100) += epic100.o
-obj-$(CONFIG_SMSC9420) += smsc9420.o
-obj-$(CONFIG_SIS190) += sis190.o
-obj-$(CONFIG_SIS900) += sis900.o
-obj-$(CONFIG_R6040) += r6040.o
-obj-$(CONFIG_YELLOWFIN) += yellowfin.o
-obj-$(CONFIG_ACENIC) += acenic.o
-obj-$(CONFIG_ISERIES_VETH) += iseries_veth.o
-obj-$(CONFIG_NATSEMI) += natsemi.o
-obj-$(CONFIG_NS83820) += ns83820.o
-obj-$(CONFIG_STNIC) += stnic.o 8390.o
-obj-$(CONFIG_FEALNX) += fealnx.o
-obj-$(CONFIG_TIGON3) += tg3.o
-obj-$(CONFIG_BNX2) += bnx2.o
-obj-$(CONFIG_CNIC) += cnic.o
-obj-$(CONFIG_BNX2X) += bnx2x/
-spidernet-y += spider_net.o spider_net_ethtool.o
-obj-$(CONFIG_SPIDER_NET) += spidernet.o sungem_phy.o
-obj-$(CONFIG_GELIC_NET) += ps3_gelic.o
-gelic_wireless-$(CONFIG_GELIC_WIRELESS) += ps3_gelic_wireless.o
-ps3_gelic-objs += ps3_gelic_net.o $(gelic_wireless-y)
-obj-$(CONFIG_TC35815) += tc35815.o
-obj-$(CONFIG_SKGE) += skge.o
-obj-$(CONFIG_SKY2) += sky2.o
-obj-$(CONFIG_SKFP) += skfp/
-obj-$(CONFIG_KS8842) += ks8842.o
-obj-$(CONFIG_KS8851) += ks8851.o
-obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o
-obj-$(CONFIG_KSZ884X_PCI) += ksz884x.o
-obj-$(CONFIG_VIA_RHINE) += via-rhine.o
-obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o
-obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
-obj-$(CONFIG_RIONET) += rionet.o
-obj-$(CONFIG_SH_ETH) += sh_eth.o
-obj-$(CONFIG_STMMAC_ETH) += stmmac/
-
+# Networking Core Drivers
#
-# end link order section
-#
-
-obj-$(CONFIG_SUNDANCE) += sundance.o
-obj-$(CONFIG_HAMACHI) += hamachi.o
-obj-$(CONFIG_NET) += Space.o loopback.o
-obj-$(CONFIG_SEEQ8005) += seeq8005.o
-obj-$(CONFIG_NET_SB1000) += sb1000.o
-obj-$(CONFIG_MAC8390) += mac8390.o
-obj-$(CONFIG_APNE) += apne.o 8390.o
-obj-$(CONFIG_PCMCIA_PCNET) += 8390.o
-obj-$(CONFIG_HP100) += hp100.o
-obj-$(CONFIG_SMC9194) += smc9194.o
-obj-$(CONFIG_FEC) += fec.o
-obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
-ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
- obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
-endif
-obj-$(CONFIG_68360_ENET) += 68360enet.o
-obj-$(CONFIG_WD80x3) += wd.o 8390.o
-obj-$(CONFIG_EL2) += 3c503.o 8390p.o
-obj-$(CONFIG_NE2000) += ne.o 8390p.o
-obj-$(CONFIG_NE2_MCA) += ne2.o 8390p.o
-obj-$(CONFIG_HPLAN) += hp.o 8390p.o
-obj-$(CONFIG_HPLAN_PLUS) += hp-plus.o 8390p.o
-obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o
-obj-$(CONFIG_ULTRAMCA) += smc-mca.o 8390.o
-obj-$(CONFIG_ULTRA32) += smc-ultra32.o 8390.o
-obj-$(CONFIG_E2100) += e2100.o 8390.o
-obj-$(CONFIG_ES3210) += es3210.o 8390.o
-obj-$(CONFIG_LNE390) += lne390.o 8390.o
-obj-$(CONFIG_NE3210) += ne3210.o 8390.o
-obj-$(CONFIG_SB1250_MAC) += sb1250-mac.o
-obj-$(CONFIG_B44) += b44.o
-obj-$(CONFIG_FORCEDETH) += forcedeth.o
-obj-$(CONFIG_NE_H8300) += ne-h8300.o
-obj-$(CONFIG_AX88796) += ax88796.o
-obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o
-obj-$(CONFIG_FTMAC100) += ftmac100.o
-
-obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
-obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
-ll_temac-objs := ll_temac_main.o ll_temac_mdio.o
-obj-$(CONFIG_XILINX_LL_TEMAC) += ll_temac.o
-obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
-obj-$(CONFIG_QLA3XXX) += qla3xxx.o
-obj-$(CONFIG_QLCNIC) += qlcnic/
-obj-$(CONFIG_QLGE) += qlge/
-
-obj-$(CONFIG_PPP) += ppp_generic.o
-obj-$(CONFIG_PPP_ASYNC) += ppp_async.o
-obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o
-obj-$(CONFIG_PPP_DEFLATE) += ppp_deflate.o
-obj-$(CONFIG_PPP_BSDCOMP) += bsd_comp.o
-obj-$(CONFIG_PPP_MPPE) += ppp_mppe.o
-obj-$(CONFIG_PPPOE) += pppox.o pppoe.o
-obj-$(CONFIG_PPPOL2TP) += pppox.o
-obj-$(CONFIG_PPTP) += pppox.o pptp.o
-
-obj-$(CONFIG_SLIP) += slip.o
-obj-$(CONFIG_SLHC) += slhc.o
-
-obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o
-obj-$(CONFIG_XEN_NETDEV_BACKEND) += xen-netback/
-
+obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_DUMMY) += dummy.o
+obj-$(CONFIG_EQUALIZER) += eql.o
obj-$(CONFIG_IFB) += ifb.o
obj-$(CONFIG_MACVLAN) += macvlan.o
obj-$(CONFIG_MACVTAP) += macvtap.o
-obj-$(CONFIG_DE600) += de600.o
-obj-$(CONFIG_DE620) += de620.o
-obj-$(CONFIG_LANCE) += lance.o
-obj-$(CONFIG_SUN3_82586) += sun3_82586.o
-obj-$(CONFIG_SUN3LANCE) += sun3lance.o
-obj-$(CONFIG_DEFXX) += defxx.o
-obj-$(CONFIG_SGISEEQ) += sgiseeq.o
-obj-$(CONFIG_SGI_O2MACE_ETH) += meth.o
-obj-$(CONFIG_AT1700) += at1700.o
-obj-$(CONFIG_EL1) += 3c501.o
-obj-$(CONFIG_EL16) += 3c507.o
-obj-$(CONFIG_ELMC) += 3c523.o
-obj-$(CONFIG_IBMLANA) += ibmlana.o
-obj-$(CONFIG_ELMC_II) += 3c527.o
-obj-$(CONFIG_EL3) += 3c509.o
-obj-$(CONFIG_3C515) += 3c515.o
-obj-$(CONFIG_EEXPRESS) += eexpress.o
-obj-$(CONFIG_EEXPRESS_PRO) += eepro.o
-obj-$(CONFIG_8139CP) += 8139cp.o
-obj-$(CONFIG_8139TOO) += 8139too.o
-obj-$(CONFIG_ZNET) += znet.o
-obj-$(CONFIG_CPMAC) += cpmac.o
-obj-$(CONFIG_DEPCA) += depca.o
-obj-$(CONFIG_EWRK3) += ewrk3.o
-obj-$(CONFIG_ATP) += atp.o
-obj-$(CONFIG_NI5010) += ni5010.o
-obj-$(CONFIG_NI52) += ni52.o
-obj-$(CONFIG_NI65) += ni65.o
-obj-$(CONFIG_ELPLUS) += 3c505.o
-obj-$(CONFIG_AC3200) += ac3200.o 8390.o
-obj-$(CONFIG_APRICOT) += 82596.o
-obj-$(CONFIG_LASI_82596) += lasi_82596.o
-obj-$(CONFIG_SNI_82596) += sni_82596.o
-obj-$(CONFIG_MVME16x_NET) += 82596.o
-obj-$(CONFIG_BVME6000_NET) += 82596.o
-obj-$(CONFIG_SC92031) += sc92031.o
-
-# This is also a 82596 and should probably be merged
-obj-$(CONFIG_LP486E) += lp486e.o
-
-obj-$(CONFIG_ETH16I) += eth16i.o
-obj-$(CONFIG_ZORRO8390) += zorro8390.o
-obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
-obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o
-obj-$(CONFIG_EQUALIZER) += eql.o
-obj-$(CONFIG_KORINA) += korina.o
-obj-$(CONFIG_MIPS_JAZZ_SONIC) += jazzsonic.o
-obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o
-obj-$(CONFIG_MIPS_SIM_NET) += mipsnet.o
-obj-$(CONFIG_SGI_IOC3_ETH) += ioc3-eth.o
-obj-$(CONFIG_DECLANCE) += declance.o
-obj-$(CONFIG_ATARILANCE) += atarilance.o
-obj-$(CONFIG_A2065) += a2065.o
-obj-$(CONFIG_HYDRA) += hydra.o
-obj-$(CONFIG_ARIADNE) += ariadne.o
-obj-$(CONFIG_CS89x0) += cs89x0.o
-obj-$(CONFIG_MACSONIC) += macsonic.o
-obj-$(CONFIG_MACMACE) += macmace.o
-obj-$(CONFIG_MAC89x0) += mac89x0.o
+obj-$(CONFIG_MII) += mii.o
+obj-$(CONFIG_MDIO) += mdio.o
+obj-$(CONFIG_NET) += Space.o loopback.o
+obj-$(CONFIG_NETCONSOLE) += netconsole.o
+obj-$(CONFIG_PHYLIB) += phy/
+obj-$(CONFIG_RIONET) += rionet.o
obj-$(CONFIG_TUN) += tun.o
obj-$(CONFIG_VETH) += veth.o
-obj-$(CONFIG_NET_NETX) += netx-eth.o
-obj-$(CONFIG_DL2K) += dl2k.o
-obj-$(CONFIG_R8169) += r8169.o
-obj-$(CONFIG_AMD8111_ETH) += amd8111e.o
-obj-$(CONFIG_IBMVETH) += ibmveth.o
-obj-$(CONFIG_S2IO) += s2io.o
-obj-$(CONFIG_VXGE) += vxge/
-obj-$(CONFIG_MYRI10GE) += myri10ge/
-obj-$(CONFIG_SMC91X) += smc91x.o
-obj-$(CONFIG_SMC911X) += smc911x.o
-obj-$(CONFIG_SMSC911X) += smsc911x.o
-obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
-obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
-obj-$(CONFIG_DM9000) += dm9000.o
-obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
-pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o
-obj-$(CONFIG_MLX4_CORE) += mlx4/
-obj-$(CONFIG_ENC28J60) += enc28j60.o
-obj-$(CONFIG_ETHOC) += ethoc.o
-obj-$(CONFIG_GRETH) += greth.o
-obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
-
-obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
-
-obj-$(CONFIG_DNET) += dnet.o
-obj-$(CONFIG_MACB) += macb.o
-obj-$(CONFIG_S6GMAC) += s6gmac.o
+obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
-obj-$(CONFIG_ARM) += arm/
+#
+# Networking Drivers
+#
+obj-$(CONFIG_ARCNET) += arcnet/
obj-$(CONFIG_DEV_APPLETALK) += appletalk/
+obj-$(CONFIG_CAIF) += caif/
+obj-$(CONFIG_CAN) += can/
+obj-$(CONFIG_ETRAX_ETHERNET) += cris/
+obj-$(CONFIG_ETHERNET) += ethernet/
+obj-$(CONFIG_FDDI) += fddi/
+obj-$(CONFIG_HIPPI) += hippi/
+obj-$(CONFIG_HAMRADIO) += hamradio/
+obj-$(CONFIG_IRDA) += irda/
+obj-$(CONFIG_PLIP) += plip/
+obj-$(CONFIG_PPP) += ppp/
+obj-$(CONFIG_PPP_ASYNC) += ppp/
+obj-$(CONFIG_PPP_BSDCOMP) += ppp/
+obj-$(CONFIG_PPP_DEFLATE) += ppp/
+obj-$(CONFIG_PPP_MPPE) += ppp/
+obj-$(CONFIG_PPP_SYNC_TTY) += ppp/
+obj-$(CONFIG_PPPOE) += ppp/
+obj-$(CONFIG_PPPOL2TP) += ppp/
+obj-$(CONFIG_PPTP) += ppp/
+obj-$(CONFIG_SLIP) += slip/
+obj-$(CONFIG_SLHC) += slip/
+obj-$(CONFIG_NET_SB1000) += sb1000.o
+obj-$(CONFIG_SUNGEM_PHY) += sungem_phy.o
obj-$(CONFIG_TR) += tokenring/
obj-$(CONFIG_WAN) += wan/
-obj-$(CONFIG_ARCNET) += arcnet/
-obj-$(CONFIG_NET_PCMCIA) += pcmcia/
+obj-$(CONFIG_WLAN) += wireless/
+obj-$(CONFIG_WIMAX) += wimax/
+
+obj-$(CONFIG_VMXNET3) += vmxnet3/
+obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o
+obj-$(CONFIG_XEN_NETDEV_BACKEND) += xen-netback/
obj-$(CONFIG_USB_CATC) += usb/
obj-$(CONFIG_USB_KAWETH) += usb/
@@ -283,26 +65,4 @@ obj-$(CONFIG_USB_HSO) += usb/
obj-$(CONFIG_USB_USBNET) += usb/
obj-$(CONFIG_USB_ZD1201) += usb/
obj-$(CONFIG_USB_IPHETH) += usb/
-
-obj-$(CONFIG_WLAN) += wireless/
-obj-$(CONFIG_NET_TULIP) += tulip/
-obj-$(CONFIG_HAMRADIO) += hamradio/
-obj-$(CONFIG_IRDA) += irda/
-obj-$(CONFIG_ETRAX_ETHERNET) += cris/
-obj-$(CONFIG_ENP2611_MSF_NET) += ixp2000/
-
-obj-$(CONFIG_NETCONSOLE) += netconsole.o
-
-obj-$(CONFIG_FS_ENET) += fs_enet/
-
-obj-$(CONFIG_NETXEN_NIC) += netxen/
-obj-$(CONFIG_NIU) += niu.o
-obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
-obj-$(CONFIG_SFC) += sfc/
-
-obj-$(CONFIG_WIMAX) += wimax/
-obj-$(CONFIG_CAIF) += caif/
-
-obj-$(CONFIG_OCTEON_MGMT_ETHERNET) += octeon/
-obj-$(CONFIG_PCH_GBE) += pch_gbe/
-obj-$(CONFIG_TILE_NET) += tile/
+obj-$(CONFIG_USB_CDC_PHONET) += usb/
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index 748c9f526e71..9abd4eb86dc1 100644
--- a/drivers/net/appletalk/cops.c
+++ b/drivers/net/appletalk/cops.c
@@ -264,7 +264,7 @@ static const struct net_device_ops cops_netdev_ops = {
.ndo_start_xmit = cops_send_packet,
.ndo_tx_timeout = cops_timeout,
.ndo_do_ioctl = cops_ioctl,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
};
/*
diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c
index e69eead12ec7..6057b30417a2 100644
--- a/drivers/net/appletalk/ltpc.c
+++ b/drivers/net/appletalk/ltpc.c
@@ -652,9 +652,9 @@ static int do_write(struct net_device *dev, void *cbuf, int cbuflen,
int ret;
if(i) {
- qels[i].cbuf = (unsigned char *) cbuf;
+ qels[i].cbuf = cbuf;
qels[i].cbuflen = cbuflen;
- qels[i].dbuf = (unsigned char *) dbuf;
+ qels[i].dbuf = dbuf;
qels[i].dbuflen = dbuflen;
qels[i].QWrite = 1;
qels[i].mailbox = i; /* this should be initted rather */
@@ -676,9 +676,9 @@ static int do_read(struct net_device *dev, void *cbuf, int cbuflen,
int ret;
if(i) {
- qels[i].cbuf = (unsigned char *) cbuf;
+ qels[i].cbuf = cbuf;
qels[i].cbuflen = cbuflen;
- qels[i].dbuf = (unsigned char *) dbuf;
+ qels[i].dbuf = dbuf;
qels[i].dbuflen = dbuflen;
qels[i].QWrite = 0;
qels[i].mailbox = i; /* this should be initted rather */
@@ -1014,7 +1014,7 @@ static int __init ltpc_probe_dma(int base, int dma)
static const struct net_device_ops ltpc_netdev = {
.ndo_start_xmit = ltpc_xmit,
.ndo_do_ioctl = ltpc_ioctl,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
};
struct net_device * __init ltpc_probe(void)
diff --git a/drivers/net/arcnet/Kconfig b/drivers/net/arcnet/Kconfig
index 3b2f7f115464..a73d9dc80ff6 100644
--- a/drivers/net/arcnet/Kconfig
+++ b/drivers/net/arcnet/Kconfig
@@ -3,8 +3,8 @@
#
menuconfig ARCNET
- depends on NETDEVICES && (ISA || PCI)
- tristate "ARCnet support"
+ depends on NETDEVICES && (ISA || PCI || PCMCIA)
+ bool "ARCnet support"
---help---
If you have a network card of this type, say Y and check out the
(arguably) beautiful poetry in
@@ -123,4 +123,14 @@ config ARCNET_COM20020_PCI
tristate "Support for COM20020 on PCI"
depends on ARCNET_COM20020 && PCI
+config ARCNET_COM20020_CS
+ tristate "COM20020 ARCnet PCMCIA support"
+ depends on ARCNET_COM20020 && PCMCIA
+ help
+ Say Y here if you intend to attach this type of ARCnet PCMCIA card
+ to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called com20020_cs. If unsure, say N.
+
endif # ARCNET
diff --git a/drivers/net/arcnet/Makefile b/drivers/net/arcnet/Makefile
index 5861af543d42..5ce8ee63e435 100644
--- a/drivers/net/arcnet/Makefile
+++ b/drivers/net/arcnet/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_ARCNET_RIM_I) += arc-rimi.o
obj-$(CONFIG_ARCNET_COM20020) += com20020.o
obj-$(CONFIG_ARCNET_COM20020_ISA) += com20020-isa.o
obj-$(CONFIG_ARCNET_COM20020_PCI) += com20020-pci.o
+obj-$(CONFIG_ARCNET_COM20020_CS) += com20020_cs.o
diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c
index 9efbbbae47ca..25197b698dd6 100644
--- a/drivers/net/arcnet/arc-rimi.c
+++ b/drivers/net/arcnet/arc-rimi.c
@@ -32,6 +32,7 @@
#include <linux/netdevice.h>
#include <linux/bootmem.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/arcdevice.h>
diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c
index 37272827ee55..45c61a2c5fbd 100644
--- a/drivers/net/arcnet/com20020-isa.c
+++ b/drivers/net/arcnet/com20020-isa.c
@@ -34,6 +34,7 @@
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/bootmem.h>
#include <linux/arcdevice.h>
#include <linux/com20020.h>
diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c
index 48a1dbf01e60..d427493997b6 100644
--- a/drivers/net/arcnet/com20020-pci.c
+++ b/drivers/net/arcnet/com20020-pci.c
@@ -34,6 +34,7 @@
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/arcdevice.h>
#include <linux/com20020.h>
diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c
index c9e459400ff9..7b96c5f47e8d 100644
--- a/drivers/net/arcnet/com20020.c
+++ b/drivers/net/arcnet/com20020.c
@@ -33,6 +33,7 @@
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/arcdevice.h>
#include <linux/com20020.h>
@@ -153,7 +154,7 @@ const struct net_device_ops com20020_netdev_ops = {
.ndo_stop = arcnet_close,
.ndo_start_xmit = arcnet_send_packet,
.ndo_tx_timeout = arcnet_timeout,
- .ndo_set_multicast_list = com20020_set_mc_list,
+ .ndo_set_rx_mode = com20020_set_mc_list,
};
/* Set up the struct net_device associated with this card. Called after
diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c
index 980e65c14936..980e65c14936 100644
--- a/drivers/net/pcmcia/com20020_cs.c
+++ b/drivers/net/arcnet/com20020_cs.c
diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c
index eb27976dab37..487d780ebbdf 100644
--- a/drivers/net/arcnet/com90io.c
+++ b/drivers/net/arcnet/com90io.c
@@ -33,6 +33,7 @@
#include <linux/netdevice.h>
#include <linux/bootmem.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/arcdevice.h>
diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c
index f3b46f71e293..b80fbe40aa0e 100644
--- a/drivers/net/arcnet/com90xx.c
+++ b/drivers/net/arcnet/com90xx.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c
deleted file mode 100644
index b7f45cd756a2..000000000000
--- a/drivers/net/ariadne.c
+++ /dev/null
@@ -1,858 +0,0 @@
-/*
- * Amiga Linux/m68k Ariadne Ethernet Driver
- *
- * © Copyright 1995-2003 by Geert Uytterhoeven (geert@linux-m68k.org)
- * Peter De Schrijver (p2@mind.be)
- *
- * ---------------------------------------------------------------------------
- *
- * This program is based on
- *
- * lance.c: An AMD LANCE ethernet driver for linux.
- * Written 1993-94 by Donald Becker.
- *
- * Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller
- * Advanced Micro Devices
- * Publication #16907, Rev. B, Amendment/0, May 1994
- *
- * MC68230: Parallel Interface/Timer (PI/T)
- * Motorola Semiconductors, December, 1983
- *
- * ---------------------------------------------------------------------------
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of the Linux
- * distribution for more details.
- *
- * ---------------------------------------------------------------------------
- *
- * The Ariadne is a Zorro-II board made by Village Tronic. It contains:
- *
- * - an Am79C960 PCnet-ISA Single-Chip Ethernet Controller with both
- * 10BASE-2 (thin coax) and 10BASE-T (UTP) connectors
- *
- * - an MC68230 Parallel Interface/Timer configured as 2 parallel ports
- */
-
-#include <linux/module.h>
-#include <linux/stddef.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/interrupt.h>
-#include <linux/skbuff.h>
-#include <linux/init.h>
-#include <linux/zorro.h>
-#include <linux/bitops.h>
-
-#include <asm/amigaints.h>
-#include <asm/amigahw.h>
-#include <asm/irq.h>
-
-#include "ariadne.h"
-
-
-#ifdef ARIADNE_DEBUG
-int ariadne_debug = ARIADNE_DEBUG;
-#else
-int ariadne_debug = 1;
-#endif
-
-
- /*
- * Macros to Fix Endianness problems
- */
-
- /* Swap the Bytes in a WORD */
-#define swapw(x) (((x>>8)&0x00ff)|((x<<8)&0xff00))
- /* Get the Low BYTE in a WORD */
-#define lowb(x) (x&0xff)
- /* Get the Swapped High WORD in a LONG */
-#define swhighw(x) ((((x)>>8)&0xff00)|(((x)>>24)&0x00ff))
- /* Get the Swapped Low WORD in a LONG */
-#define swloww(x) ((((x)<<8)&0xff00)|(((x)>>8)&0x00ff))
-
-
- /*
- * Transmit/Receive Ring Definitions
- */
-
-#define TX_RING_SIZE 5
-#define RX_RING_SIZE 16
-
-#define PKT_BUF_SIZE 1520
-
-
- /*
- * Private Device Data
- */
-
-struct ariadne_private {
- volatile struct TDRE *tx_ring[TX_RING_SIZE];
- volatile struct RDRE *rx_ring[RX_RING_SIZE];
- volatile u_short *tx_buff[TX_RING_SIZE];
- volatile u_short *rx_buff[RX_RING_SIZE];
- int cur_tx, cur_rx; /* The next free ring entry */
- int dirty_tx; /* The ring entries to be free()ed. */
- char tx_full;
-};
-
-
- /*
- * Structure Created in the Ariadne's RAM Buffer
- */
-
-struct lancedata {
- struct TDRE tx_ring[TX_RING_SIZE];
- struct RDRE rx_ring[RX_RING_SIZE];
- u_short tx_buff[TX_RING_SIZE][PKT_BUF_SIZE/sizeof(u_short)];
- u_short rx_buff[RX_RING_SIZE][PKT_BUF_SIZE/sizeof(u_short)];
-};
-
-static int ariadne_open(struct net_device *dev);
-static void ariadne_init_ring(struct net_device *dev);
-static netdev_tx_t ariadne_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static void ariadne_tx_timeout(struct net_device *dev);
-static int ariadne_rx(struct net_device *dev);
-static void ariadne_reset(struct net_device *dev);
-static irqreturn_t ariadne_interrupt(int irq, void *data);
-static int ariadne_close(struct net_device *dev);
-static struct net_device_stats *ariadne_get_stats(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-
-
-static void memcpyw(volatile u_short *dest, u_short *src, int len)
-{
- while (len >= 2) {
- *(dest++) = *(src++);
- len -= 2;
- }
- if (len == 1)
- *dest = (*(u_char *)src)<<8;
-}
-
-
-static int __devinit ariadne_init_one(struct zorro_dev *z,
- const struct zorro_device_id *ent);
-static void __devexit ariadne_remove_one(struct zorro_dev *z);
-
-
-static struct zorro_device_id ariadne_zorro_tbl[] __devinitdata = {
- { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE },
- { 0 }
-};
-MODULE_DEVICE_TABLE(zorro, ariadne_zorro_tbl);
-
-static struct zorro_driver ariadne_driver = {
- .name = "ariadne",
- .id_table = ariadne_zorro_tbl,
- .probe = ariadne_init_one,
- .remove = __devexit_p(ariadne_remove_one),
-};
-
-static const struct net_device_ops ariadne_netdev_ops = {
- .ndo_open = ariadne_open,
- .ndo_stop = ariadne_close,
- .ndo_start_xmit = ariadne_start_xmit,
- .ndo_tx_timeout = ariadne_tx_timeout,
- .ndo_get_stats = ariadne_get_stats,
- .ndo_set_multicast_list = set_multicast_list,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_change_mtu = eth_change_mtu,
- .ndo_set_mac_address = eth_mac_addr,
-};
-
-static int __devinit ariadne_init_one(struct zorro_dev *z,
- const struct zorro_device_id *ent)
-{
- unsigned long board = z->resource.start;
- unsigned long base_addr = board+ARIADNE_LANCE;
- unsigned long mem_start = board+ARIADNE_RAM;
- struct resource *r1, *r2;
- struct net_device *dev;
- struct ariadne_private *priv;
- int err;
-
- r1 = request_mem_region(base_addr, sizeof(struct Am79C960), "Am79C960");
- if (!r1)
- return -EBUSY;
- r2 = request_mem_region(mem_start, ARIADNE_RAM_SIZE, "RAM");
- if (!r2) {
- release_mem_region(base_addr, sizeof(struct Am79C960));
- return -EBUSY;
- }
-
- dev = alloc_etherdev(sizeof(struct ariadne_private));
- if (dev == NULL) {
- release_mem_region(base_addr, sizeof(struct Am79C960));
- release_mem_region(mem_start, ARIADNE_RAM_SIZE);
- return -ENOMEM;
- }
-
- priv = netdev_priv(dev);
-
- r1->name = dev->name;
- r2->name = dev->name;
-
- dev->dev_addr[0] = 0x00;
- dev->dev_addr[1] = 0x60;
- dev->dev_addr[2] = 0x30;
- dev->dev_addr[3] = (z->rom.er_SerialNumber>>16) & 0xff;
- dev->dev_addr[4] = (z->rom.er_SerialNumber>>8) & 0xff;
- dev->dev_addr[5] = z->rom.er_SerialNumber & 0xff;
- dev->base_addr = ZTWO_VADDR(base_addr);
- dev->mem_start = ZTWO_VADDR(mem_start);
- dev->mem_end = dev->mem_start+ARIADNE_RAM_SIZE;
-
- dev->netdev_ops = &ariadne_netdev_ops;
- dev->watchdog_timeo = 5*HZ;
-
- err = register_netdev(dev);
- if (err) {
- release_mem_region(base_addr, sizeof(struct Am79C960));
- release_mem_region(mem_start, ARIADNE_RAM_SIZE);
- free_netdev(dev);
- return err;
- }
- zorro_set_drvdata(z, dev);
-
- printk(KERN_INFO "%s: Ariadne at 0x%08lx, Ethernet Address %pM\n",
- dev->name, board, dev->dev_addr);
-
- return 0;
-}
-
-
-static int ariadne_open(struct net_device *dev)
-{
- volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
- u_short in;
- u_long version;
- int i;
-
- /* Reset the LANCE */
- in = lance->Reset;
-
- /* Stop the LANCE */
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
- lance->RDP = STOP;
-
- /* Check the LANCE version */
- lance->RAP = CSR88; /* Chip ID */
- version = swapw(lance->RDP);
- lance->RAP = CSR89; /* Chip ID */
- version |= swapw(lance->RDP)<<16;
- if ((version & 0x00000fff) != 0x00000003) {
- printk(KERN_WARNING "ariadne_open: Couldn't find AMD Ethernet Chip\n");
- return -EAGAIN;
- }
- if ((version & 0x0ffff000) != 0x00003000) {
- printk(KERN_WARNING "ariadne_open: Couldn't find Am79C960 (Wrong part "
- "number = %ld)\n", (version & 0x0ffff000)>>12);
- return -EAGAIN;
- }
-#if 0
- printk(KERN_DEBUG "ariadne_open: Am79C960 (PCnet-ISA) Revision %ld\n",
- (version & 0xf0000000)>>28);
-#endif
-
- ariadne_init_ring(dev);
-
- /* Miscellaneous Stuff */
- lance->RAP = CSR3; /* Interrupt Masks and Deferral Control */
- lance->RDP = 0x0000;
- lance->RAP = CSR4; /* Test and Features Control */
- lance->RDP = DPOLL|APAD_XMT|MFCOM|RCVCCOM|TXSTRTM|JABM;
-
- /* Set the Multicast Table */
- lance->RAP = CSR8; /* Logical Address Filter, LADRF[15:0] */
- lance->RDP = 0x0000;
- lance->RAP = CSR9; /* Logical Address Filter, LADRF[31:16] */
- lance->RDP = 0x0000;
- lance->RAP = CSR10; /* Logical Address Filter, LADRF[47:32] */
- lance->RDP = 0x0000;
- lance->RAP = CSR11; /* Logical Address Filter, LADRF[63:48] */
- lance->RDP = 0x0000;
-
- /* Set the Ethernet Hardware Address */
- lance->RAP = CSR12; /* Physical Address Register, PADR[15:0] */
- lance->RDP = ((u_short *)&dev->dev_addr[0])[0];
- lance->RAP = CSR13; /* Physical Address Register, PADR[31:16] */
- lance->RDP = ((u_short *)&dev->dev_addr[0])[1];
- lance->RAP = CSR14; /* Physical Address Register, PADR[47:32] */
- lance->RDP = ((u_short *)&dev->dev_addr[0])[2];
-
- /* Set the Init Block Mode */
- lance->RAP = CSR15; /* Mode Register */
- lance->RDP = 0x0000;
-
- /* Set the Transmit Descriptor Ring Pointer */
- lance->RAP = CSR30; /* Base Address of Transmit Ring */
- lance->RDP = swloww(ARIADNE_RAM+offsetof(struct lancedata, tx_ring));
- lance->RAP = CSR31; /* Base Address of transmit Ring */
- lance->RDP = swhighw(ARIADNE_RAM+offsetof(struct lancedata, tx_ring));
-
- /* Set the Receive Descriptor Ring Pointer */
- lance->RAP = CSR24; /* Base Address of Receive Ring */
- lance->RDP = swloww(ARIADNE_RAM+offsetof(struct lancedata, rx_ring));
- lance->RAP = CSR25; /* Base Address of Receive Ring */
- lance->RDP = swhighw(ARIADNE_RAM+offsetof(struct lancedata, rx_ring));
-
- /* Set the Number of RX and TX Ring Entries */
- lance->RAP = CSR76; /* Receive Ring Length */
- lance->RDP = swapw(((u_short)-RX_RING_SIZE));
- lance->RAP = CSR78; /* Transmit Ring Length */
- lance->RDP = swapw(((u_short)-TX_RING_SIZE));
-
- /* Enable Media Interface Port Auto Select (10BASE-2/10BASE-T) */
- lance->RAP = ISACSR2; /* Miscellaneous Configuration */
- lance->IDP = ASEL;
-
- /* LED Control */
- lance->RAP = ISACSR5; /* LED1 Status */
- lance->IDP = PSE|XMTE;
- lance->RAP = ISACSR6; /* LED2 Status */
- lance->IDP = PSE|COLE;
- lance->RAP = ISACSR7; /* LED3 Status */
- lance->IDP = PSE|RCVE;
-
- netif_start_queue(dev);
-
- i = request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, IRQF_SHARED,
- dev->name, dev);
- if (i) return i;
-
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
- lance->RDP = INEA|STRT;
-
- return 0;
-}
-
-
-static void ariadne_init_ring(struct net_device *dev)
-{
- struct ariadne_private *priv = netdev_priv(dev);
- volatile struct lancedata *lancedata = (struct lancedata *)dev->mem_start;
- int i;
-
- netif_stop_queue(dev);
-
- priv->tx_full = 0;
- priv->cur_rx = priv->cur_tx = 0;
- priv->dirty_tx = 0;
-
- /* Set up TX Ring */
- for (i = 0; i < TX_RING_SIZE; i++) {
- volatile struct TDRE *t = &lancedata->tx_ring[i];
- t->TMD0 = swloww(ARIADNE_RAM+offsetof(struct lancedata, tx_buff[i]));
- t->TMD1 = swhighw(ARIADNE_RAM+offsetof(struct lancedata, tx_buff[i])) |
- TF_STP | TF_ENP;
- t->TMD2 = swapw((u_short)-PKT_BUF_SIZE);
- t->TMD3 = 0;
- priv->tx_ring[i] = &lancedata->tx_ring[i];
- priv->tx_buff[i] = lancedata->tx_buff[i];
-#if 0
- printk(KERN_DEBUG "TX Entry %2d at %p, Buf at %p\n", i,
- &lancedata->tx_ring[i], lancedata->tx_buff[i]);
-#endif
- }
-
- /* Set up RX Ring */
- for (i = 0; i < RX_RING_SIZE; i++) {
- volatile struct RDRE *r = &lancedata->rx_ring[i];
- r->RMD0 = swloww(ARIADNE_RAM+offsetof(struct lancedata, rx_buff[i]));
- r->RMD1 = swhighw(ARIADNE_RAM+offsetof(struct lancedata, rx_buff[i])) |
- RF_OWN;
- r->RMD2 = swapw((u_short)-PKT_BUF_SIZE);
- r->RMD3 = 0x0000;
- priv->rx_ring[i] = &lancedata->rx_ring[i];
- priv->rx_buff[i] = lancedata->rx_buff[i];
-#if 0
- printk(KERN_DEBUG "RX Entry %2d at %p, Buf at %p\n", i,
- &lancedata->rx_ring[i], lancedata->rx_buff[i]);
-#endif
- }
-}
-
-
-static int ariadne_close(struct net_device *dev)
-{
- volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
-
- netif_stop_queue(dev);
-
- lance->RAP = CSR112; /* Missed Frame Count */
- dev->stats.rx_missed_errors = swapw(lance->RDP);
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
-
- if (ariadne_debug > 1) {
- printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
- dev->name, lance->RDP);
- printk(KERN_DEBUG "%s: %lu packets missed\n", dev->name,
- dev->stats.rx_missed_errors);
- }
-
- /* We stop the LANCE here -- it occasionally polls memory if we don't. */
- lance->RDP = STOP;
-
- free_irq(IRQ_AMIGA_PORTS, dev);
-
- return 0;
-}
-
-
-static inline void ariadne_reset(struct net_device *dev)
-{
- volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
-
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
- lance->RDP = STOP;
- ariadne_init_ring(dev);
- lance->RDP = INEA|STRT;
- netif_start_queue(dev);
-}
-
-
-static irqreturn_t ariadne_interrupt(int irq, void *data)
-{
- struct net_device *dev = (struct net_device *)data;
- volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
- struct ariadne_private *priv;
- int csr0, boguscnt;
- int handled = 0;
-
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
-
- if (!(lance->RDP & INTR)) /* Check if any interrupt has been */
- return IRQ_NONE; /* generated by the board. */
-
- priv = netdev_priv(dev);
-
- boguscnt = 10;
- while ((csr0 = lance->RDP) & (ERR|RINT|TINT) && --boguscnt >= 0) {
- /* Acknowledge all of the current interrupt sources ASAP. */
- lance->RDP = csr0 & ~(INEA|TDMD|STOP|STRT|INIT);
-
-#if 0
- if (ariadne_debug > 5) {
- printk(KERN_DEBUG "%s: interrupt csr0=%#2.2x new csr=%#2.2x.",
- dev->name, csr0, lance->RDP);
- printk("[");
- if (csr0 & INTR)
- printk(" INTR");
- if (csr0 & INEA)
- printk(" INEA");
- if (csr0 & RXON)
- printk(" RXON");
- if (csr0 & TXON)
- printk(" TXON");
- if (csr0 & TDMD)
- printk(" TDMD");
- if (csr0 & STOP)
- printk(" STOP");
- if (csr0 & STRT)
- printk(" STRT");
- if (csr0 & INIT)
- printk(" INIT");
- if (csr0 & ERR)
- printk(" ERR");
- if (csr0 & BABL)
- printk(" BABL");
- if (csr0 & CERR)
- printk(" CERR");
- if (csr0 & MISS)
- printk(" MISS");
- if (csr0 & MERR)
- printk(" MERR");
- if (csr0 & RINT)
- printk(" RINT");
- if (csr0 & TINT)
- printk(" TINT");
- if (csr0 & IDON)
- printk(" IDON");
- printk(" ]\n");
- }
-#endif
-
- if (csr0 & RINT) { /* Rx interrupt */
- handled = 1;
- ariadne_rx(dev);
- }
-
- if (csr0 & TINT) { /* Tx-done interrupt */
- int dirty_tx = priv->dirty_tx;
-
- handled = 1;
- while (dirty_tx < priv->cur_tx) {
- int entry = dirty_tx % TX_RING_SIZE;
- int status = lowb(priv->tx_ring[entry]->TMD1);
-
- if (status & TF_OWN)
- break; /* It still hasn't been Txed */
-
- priv->tx_ring[entry]->TMD1 &= 0xff00;
-
- if (status & TF_ERR) {
- /* There was an major error, log it. */
- int err_status = priv->tx_ring[entry]->TMD3;
- dev->stats.tx_errors++;
- if (err_status & EF_RTRY)
- dev->stats.tx_aborted_errors++;
- if (err_status & EF_LCAR)
- dev->stats.tx_carrier_errors++;
- if (err_status & EF_LCOL)
- dev->stats.tx_window_errors++;
- if (err_status & EF_UFLO) {
- /* Ackk! On FIFO errors the Tx unit is turned off! */
- dev->stats.tx_fifo_errors++;
- /* Remove this verbosity later! */
- printk(KERN_ERR "%s: Tx FIFO error! Status %4.4x.\n",
- dev->name, csr0);
- /* Restart the chip. */
- lance->RDP = STRT;
- }
- } else {
- if (status & (TF_MORE|TF_ONE))
- dev->stats.collisions++;
- dev->stats.tx_packets++;
- }
- dirty_tx++;
- }
-
-#ifndef final_version
- if (priv->cur_tx - dirty_tx >= TX_RING_SIZE) {
- printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d, "
- "full=%d.\n", dirty_tx, priv->cur_tx, priv->tx_full);
- dirty_tx += TX_RING_SIZE;
- }
-#endif
-
- if (priv->tx_full && netif_queue_stopped(dev) &&
- dirty_tx > priv->cur_tx - TX_RING_SIZE + 2) {
- /* The ring is no longer full. */
- priv->tx_full = 0;
- netif_wake_queue(dev);
- }
-
- priv->dirty_tx = dirty_tx;
- }
-
- /* Log misc errors. */
- if (csr0 & BABL) {
- handled = 1;
- dev->stats.tx_errors++; /* Tx babble. */
- }
- if (csr0 & MISS) {
- handled = 1;
- dev->stats.rx_errors++; /* Missed a Rx frame. */
- }
- if (csr0 & MERR) {
- handled = 1;
- printk(KERN_ERR "%s: Bus master arbitration failure, status "
- "%4.4x.\n", dev->name, csr0);
- /* Restart the chip. */
- lance->RDP = STRT;
- }
- }
-
- /* Clear any other interrupt, and set interrupt enable. */
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
- lance->RDP = INEA|BABL|CERR|MISS|MERR|IDON;
-
-#if 0
- if (ariadne_debug > 4)
- printk(KERN_DEBUG "%s: exiting interrupt, csr%d=%#4.4x.\n", dev->name,
- lance->RAP, lance->RDP);
-#endif
- return IRQ_RETVAL(handled);
-}
-
-
-static void ariadne_tx_timeout(struct net_device *dev)
-{
- volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
-
- printk(KERN_ERR "%s: transmit timed out, status %4.4x, resetting.\n",
- dev->name, lance->RDP);
- ariadne_reset(dev);
- netif_wake_queue(dev);
-}
-
-
-static netdev_tx_t ariadne_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct ariadne_private *priv = netdev_priv(dev);
- volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
- int entry;
- unsigned long flags;
- int len = skb->len;
-
-#if 0
- if (ariadne_debug > 3) {
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
- printk(KERN_DEBUG "%s: ariadne_start_xmit() called, csr0 %4.4x.\n",
- dev->name, lance->RDP);
- lance->RDP = 0x0000;
- }
-#endif
-
- /* FIXME: is the 79C960 new enough to do its own padding right ? */
- if (skb->len < ETH_ZLEN)
- {
- if (skb_padto(skb, ETH_ZLEN))
- return NETDEV_TX_OK;
- len = ETH_ZLEN;
- }
-
- /* Fill in a Tx ring entry */
-
-#if 0
-{
- printk(KERN_DEBUG "TX pkt type 0x%04x from %pM to %pM "
- " data 0x%08x len %d\n",
- ((u_short *)skb->data)[6],
- skb->data + 6, skb->data,
- (int)skb->data, (int)skb->len);
-}
-#endif
-
- local_irq_save(flags);
-
- entry = priv->cur_tx % TX_RING_SIZE;
-
- /* Caution: the write order is important here, set the base address with
- the "ownership" bits last. */
-
- priv->tx_ring[entry]->TMD2 = swapw((u_short)-skb->len);
- priv->tx_ring[entry]->TMD3 = 0x0000;
- memcpyw(priv->tx_buff[entry], (u_short *)skb->data, len);
-
-#if 0
- {
- int i, len;
-
- len = skb->len > 64 ? 64 : skb->len;
- len >>= 1;
- for (i = 0; i < len; i += 8) {
- int j;
- printk(KERN_DEBUG "%04x:", i);
- for (j = 0; (j < 8) && ((i+j) < len); j++) {
- if (!(j & 1))
- printk(" ");
- printk("%04x", priv->tx_buff[entry][i+j]);
- }
- printk("\n");
- }
- }
-#endif
-
- priv->tx_ring[entry]->TMD1 = (priv->tx_ring[entry]->TMD1&0xff00)|TF_OWN|TF_STP|TF_ENP;
-
- dev_kfree_skb(skb);
-
- priv->cur_tx++;
- if ((priv->cur_tx >= TX_RING_SIZE) && (priv->dirty_tx >= TX_RING_SIZE)) {
-
-#if 0
- printk(KERN_DEBUG "*** Subtracting TX_RING_SIZE from cur_tx (%d) and "
- "dirty_tx (%d)\n", priv->cur_tx, priv->dirty_tx);
-#endif
-
- priv->cur_tx -= TX_RING_SIZE;
- priv->dirty_tx -= TX_RING_SIZE;
- }
- dev->stats.tx_bytes += len;
-
- /* Trigger an immediate send poll. */
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
- lance->RDP = INEA|TDMD;
-
- if (lowb(priv->tx_ring[(entry+1) % TX_RING_SIZE]->TMD1) != 0) {
- netif_stop_queue(dev);
- priv->tx_full = 1;
- }
- local_irq_restore(flags);
-
- return NETDEV_TX_OK;
-}
-
-
-static int ariadne_rx(struct net_device *dev)
-{
- struct ariadne_private *priv = netdev_priv(dev);
- int entry = priv->cur_rx % RX_RING_SIZE;
- int i;
-
- /* If we own the next entry, it's a new packet. Send it up. */
- while (!(lowb(priv->rx_ring[entry]->RMD1) & RF_OWN)) {
- int status = lowb(priv->rx_ring[entry]->RMD1);
-
- if (status != (RF_STP|RF_ENP)) { /* There was an error. */
- /* There is a tricky error noted by John Murphy,
- <murf@perftech.com> to Russ Nelson: Even with full-sized
- buffers it's possible for a jabber packet to use two
- buffers, with only the last correctly noting the error. */
- if (status & RF_ENP)
- /* Only count a general error at the end of a packet.*/
- dev->stats.rx_errors++;
- if (status & RF_FRAM)
- dev->stats.rx_frame_errors++;
- if (status & RF_OFLO)
- dev->stats.rx_over_errors++;
- if (status & RF_CRC)
- dev->stats.rx_crc_errors++;
- if (status & RF_BUFF)
- dev->stats.rx_fifo_errors++;
- priv->rx_ring[entry]->RMD1 &= 0xff00|RF_STP|RF_ENP;
- } else {
- /* Malloc up new buffer, compatible with net-3. */
- short pkt_len = swapw(priv->rx_ring[entry]->RMD3);
- struct sk_buff *skb;
-
- skb = dev_alloc_skb(pkt_len+2);
- if (skb == NULL) {
- printk(KERN_WARNING "%s: Memory squeeze, deferring packet.\n",
- dev->name);
- for (i = 0; i < RX_RING_SIZE; i++)
- if (lowb(priv->rx_ring[(entry+i) % RX_RING_SIZE]->RMD1) & RF_OWN)
- break;
-
- if (i > RX_RING_SIZE-2) {
- dev->stats.rx_dropped++;
- priv->rx_ring[entry]->RMD1 |= RF_OWN;
- priv->cur_rx++;
- }
- break;
- }
-
-
- skb_reserve(skb,2); /* 16 byte align */
- skb_put(skb,pkt_len); /* Make room */
- skb_copy_to_linear_data(skb, (char *)priv->rx_buff[entry], pkt_len);
- skb->protocol=eth_type_trans(skb,dev);
-#if 0
-{
- printk(KERN_DEBUG "RX pkt type 0x%04x from ",
- ((u_short *)skb->data)[6]);
- {
- u_char *ptr = &((u_char *)skb->data)[6];
- printk("%pM", ptr);
- }
- printk(" to ");
- {
- u_char *ptr = (u_char *)skb->data;
- printk("%pM", ptr);
- }
- printk(" data 0x%08x len %d\n", (int)skb->data, (int)skb->len);
-}
-#endif
-
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- }
-
- priv->rx_ring[entry]->RMD1 |= RF_OWN;
- entry = (++priv->cur_rx) % RX_RING_SIZE;
- }
-
- priv->cur_rx = priv->cur_rx % RX_RING_SIZE;
-
- /* We should check that at least two ring entries are free. If not,
- we should free one and mark stats->rx_dropped++. */
-
- return 0;
-}
-
-
-static struct net_device_stats *ariadne_get_stats(struct net_device *dev)
-{
- volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
- short saved_addr;
- unsigned long flags;
-
- local_irq_save(flags);
- saved_addr = lance->RAP;
- lance->RAP = CSR112; /* Missed Frame Count */
- dev->stats.rx_missed_errors = swapw(lance->RDP);
- lance->RAP = saved_addr;
- local_irq_restore(flags);
-
- return &dev->stats;
-}
-
-
-/* Set or clear the multicast filter for this adaptor.
- num_addrs == -1 Promiscuous mode, receive all packets
- num_addrs == 0 Normal mode, clear multicast list
- num_addrs > 0 Multicast mode, receive normal and MC packets, and do
- best-effort filtering.
- */
-static void set_multicast_list(struct net_device *dev)
-{
- volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
-
- if (!netif_running(dev))
- return;
-
- netif_stop_queue(dev);
-
- /* We take the simple way out and always enable promiscuous mode. */
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
- lance->RDP = STOP; /* Temporarily stop the lance. */
- ariadne_init_ring(dev);
-
- if (dev->flags & IFF_PROMISC) {
- lance->RAP = CSR15; /* Mode Register */
- lance->RDP = PROM; /* Set promiscuous mode */
- } else {
- short multicast_table[4];
- int num_addrs = netdev_mc_count(dev);
- int i;
- /* We don't use the multicast table, but rely on upper-layer filtering. */
- memset(multicast_table, (num_addrs == 0) ? 0 : -1,
- sizeof(multicast_table));
- for (i = 0; i < 4; i++) {
- lance->RAP = CSR8+(i<<8); /* Logical Address Filter */
- lance->RDP = swapw(multicast_table[i]);
- }
- lance->RAP = CSR15; /* Mode Register */
- lance->RDP = 0x0000; /* Unset promiscuous mode */
- }
-
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
- lance->RDP = INEA|STRT|IDON; /* Resume normal operation. */
-
- netif_wake_queue(dev);
-}
-
-
-static void __devexit ariadne_remove_one(struct zorro_dev *z)
-{
- struct net_device *dev = zorro_get_drvdata(z);
-
- unregister_netdev(dev);
- release_mem_region(ZTWO_PADDR(dev->base_addr), sizeof(struct Am79C960));
- release_mem_region(ZTWO_PADDR(dev->mem_start), ARIADNE_RAM_SIZE);
- free_netdev(dev);
-}
-
-static int __init ariadne_init_module(void)
-{
- return zorro_register_driver(&ariadne_driver);
-}
-
-static void __exit ariadne_cleanup_module(void)
-{
- zorro_unregister_driver(&ariadne_driver);
-}
-
-module_init(ariadne_init_module);
-module_exit(ariadne_cleanup_module);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig
deleted file mode 100644
index 39e1c0d39476..000000000000
--- a/drivers/net/arm/Kconfig
+++ /dev/null
@@ -1,74 +0,0 @@
-#
-# Acorn Network device configuration
-# These are for Acorn's Expansion card network interfaces
-#
-config ARM_AM79C961A
- bool "ARM EBSA110 AM79C961A support"
- depends on ARM && ARCH_EBSA110
- select CRC32
- help
- If you wish to compile a kernel for the EBSA-110, then you should
- always answer Y to this.
-
-config ARM_ETHER1
- tristate "Acorn Ether1 support"
- depends on ARM && ARCH_ACORN
- help
- If you have an Acorn system with one of these (AKA25) network cards,
- you should say Y to this option if you wish to use it with Linux.
-
-config ARM_ETHER3
- tristate "Acorn/ANT Ether3 support"
- depends on ARM && ARCH_ACORN
- help
- If you have an Acorn system with one of these network cards, you
- should say Y to this option if you wish to use it with Linux.
-
-config ARM_ETHERH
- tristate "I-cubed EtherH/ANT EtherM support"
- depends on ARM && ARCH_ACORN
- select CRC32
- help
- If you have an Acorn system with one of these network cards, you
- should say Y to this option if you wish to use it with Linux.
-
-config ARM_AT91_ETHER
- tristate "AT91RM9200 Ethernet support"
- depends on ARM && ARCH_AT91RM9200
- select MII
- help
- If you wish to compile a kernel for the AT91RM9200 and enable
- ethernet support, then you should always answer Y to this.
-
-config ARM_KS8695_ETHER
- tristate "KS8695 Ethernet support"
- depends on ARM && ARCH_KS8695
- select MII
- help
- If you wish to compile a kernel for the KS8695 and want to
- use the internal ethernet then you should answer Y to this.
-
-config EP93XX_ETH
- tristate "EP93xx Ethernet support"
- depends on ARM && ARCH_EP93XX
- select MII
- help
- This is a driver for the ethernet hardware included in EP93xx CPUs.
- Say Y if you are building a kernel for EP93xx based devices.
-
-config IXP4XX_ETH
- tristate "Intel IXP4xx Ethernet support"
- depends on ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR
- select PHYLIB
- help
- Say Y here if you want to use built-in Ethernet ports
- on IXP4xx processor.
-
-config W90P910_ETH
- tristate "Nuvoton w90p910 Ethernet support"
- depends on ARM && ARCH_W90X900
- select PHYLIB
- select MII
- help
- Say Y here if you want to use built-in Ethernet ports
- on w90p910 processor.
diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile
deleted file mode 100644
index 303171f589e6..000000000000
--- a/drivers/net/arm/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-# File: drivers/net/arm/Makefile
-#
-# Makefile for the ARM network device drivers
-#
-
-obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o
-obj-$(CONFIG_ARM_ETHERH) += etherh.o
-obj-$(CONFIG_ARM_ETHER3) += ether3.o
-obj-$(CONFIG_ARM_ETHER1) += ether1.o
-obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
-obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
-obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
-obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
-obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o
diff --git a/drivers/net/benet/Kconfig b/drivers/net/benet/Kconfig
deleted file mode 100644
index 1a41a49bb619..000000000000
--- a/drivers/net/benet/Kconfig
+++ /dev/null
@@ -1,6 +0,0 @@
-config BE2NET
- tristate "ServerEngines' 10Gbps NIC - BladeEngine"
- depends on PCI && INET
- help
- This driver implements the NIC functionality for ServerEngines'
- 10Gbps network adapter - BladeEngine.
diff --git a/drivers/net/bna/bfa_ioc_ct.c b/drivers/net/bna/bfa_ioc_ct.c
deleted file mode 100644
index 87aecdf22cf9..000000000000
--- a/drivers/net/bna/bfa_ioc_ct.c
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Linux network driver for Brocade Converged Network Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
- * All rights reserved
- * www.brocade.com
- */
-
-#include "bfa_ioc.h"
-#include "cna.h"
-#include "bfi.h"
-#include "bfi_ctreg.h"
-#include "bfa_defs.h"
-
-#define bfa_ioc_ct_sync_pos(__ioc) \
- ((u32) (1 << bfa_ioc_pcifn(__ioc)))
-#define BFA_IOC_SYNC_REQD_SH 16
-#define bfa_ioc_ct_get_sync_ackd(__val) (__val & 0x0000ffff)
-#define bfa_ioc_ct_clear_sync_ackd(__val) (__val & 0xffff0000)
-#define bfa_ioc_ct_get_sync_reqd(__val) (__val >> BFA_IOC_SYNC_REQD_SH)
-#define bfa_ioc_ct_sync_reqd_pos(__ioc) \
- (bfa_ioc_ct_sync_pos(__ioc) << BFA_IOC_SYNC_REQD_SH)
-
-/*
- * forward declarations
- */
-static bool bfa_ioc_ct_firmware_lock(struct bfa_ioc *ioc);
-static void bfa_ioc_ct_firmware_unlock(struct bfa_ioc *ioc);
-static void bfa_ioc_ct_reg_init(struct bfa_ioc *ioc);
-static void bfa_ioc_ct_map_port(struct bfa_ioc *ioc);
-static void bfa_ioc_ct_isr_mode_set(struct bfa_ioc *ioc, bool msix);
-static void bfa_ioc_ct_notify_fail(struct bfa_ioc *ioc);
-static void bfa_ioc_ct_ownership_reset(struct bfa_ioc *ioc);
-static bool bfa_ioc_ct_sync_start(struct bfa_ioc *ioc);
-static void bfa_ioc_ct_sync_join(struct bfa_ioc *ioc);
-static void bfa_ioc_ct_sync_leave(struct bfa_ioc *ioc);
-static void bfa_ioc_ct_sync_ack(struct bfa_ioc *ioc);
-static bool bfa_ioc_ct_sync_complete(struct bfa_ioc *ioc);
-static enum bfa_status bfa_ioc_ct_pll_init(void __iomem *rb, bool fcmode);
-
-static struct bfa_ioc_hwif nw_hwif_ct;
-
-/**
- * Called from bfa_ioc_attach() to map asic specific calls.
- */
-void
-bfa_nw_ioc_set_ct_hwif(struct bfa_ioc *ioc)
-{
- nw_hwif_ct.ioc_pll_init = bfa_ioc_ct_pll_init;
- nw_hwif_ct.ioc_firmware_lock = bfa_ioc_ct_firmware_lock;
- nw_hwif_ct.ioc_firmware_unlock = bfa_ioc_ct_firmware_unlock;
- nw_hwif_ct.ioc_reg_init = bfa_ioc_ct_reg_init;
- nw_hwif_ct.ioc_map_port = bfa_ioc_ct_map_port;
- nw_hwif_ct.ioc_isr_mode_set = bfa_ioc_ct_isr_mode_set;
- nw_hwif_ct.ioc_notify_fail = bfa_ioc_ct_notify_fail;
- nw_hwif_ct.ioc_ownership_reset = bfa_ioc_ct_ownership_reset;
- nw_hwif_ct.ioc_sync_start = bfa_ioc_ct_sync_start;
- nw_hwif_ct.ioc_sync_join = bfa_ioc_ct_sync_join;
- nw_hwif_ct.ioc_sync_leave = bfa_ioc_ct_sync_leave;
- nw_hwif_ct.ioc_sync_ack = bfa_ioc_ct_sync_ack;
- nw_hwif_ct.ioc_sync_complete = bfa_ioc_ct_sync_complete;
-
- ioc->ioc_hwif = &nw_hwif_ct;
-}
-
-/**
- * Return true if firmware of current driver matches the running firmware.
- */
-static bool
-bfa_ioc_ct_firmware_lock(struct bfa_ioc *ioc)
-{
- enum bfi_ioc_state ioc_fwstate;
- u32 usecnt;
- struct bfi_ioc_image_hdr fwhdr;
-
- /**
- * Firmware match check is relevant only for CNA.
- */
- if (!ioc->cna)
- return true;
-
- /**
- * If bios boot (flash based) -- do not increment usage count
- */
- if (bfa_cb_image_get_size(BFA_IOC_FWIMG_TYPE(ioc)) <
- BFA_IOC_FWIMG_MINSZ)
- return true;
-
- bfa_nw_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg);
- usecnt = readl(ioc->ioc_regs.ioc_usage_reg);
-
- /**
- * If usage count is 0, always return TRUE.
- */
- if (usecnt == 0) {
- writel(1, ioc->ioc_regs.ioc_usage_reg);
- bfa_nw_ioc_sem_release(ioc->ioc_regs.ioc_usage_sem_reg);
- writel(0, ioc->ioc_regs.ioc_fail_sync);
- return true;
- }
-
- ioc_fwstate = readl(ioc->ioc_regs.ioc_fwstate);
-
- /**
- * Use count cannot be non-zero and chip in uninitialized state.
- */
- BUG_ON(!(ioc_fwstate != BFI_IOC_UNINIT));
-
- /**
- * Check if another driver with a different firmware is active
- */
- bfa_nw_ioc_fwver_get(ioc, &fwhdr);
- if (!bfa_nw_ioc_fwver_cmp(ioc, &fwhdr)) {
- bfa_nw_ioc_sem_release(ioc->ioc_regs.ioc_usage_sem_reg);
- return false;
- }
-
- /**
- * Same firmware version. Increment the reference count.
- */
- usecnt++;
- writel(usecnt, ioc->ioc_regs.ioc_usage_reg);
- bfa_nw_ioc_sem_release(ioc->ioc_regs.ioc_usage_sem_reg);
- return true;
-}
-
-static void
-bfa_ioc_ct_firmware_unlock(struct bfa_ioc *ioc)
-{
- u32 usecnt;
-
- /**
- * Firmware lock is relevant only for CNA.
- */
- if (!ioc->cna)
- return;
-
- /**
- * If bios boot (flash based) -- do not decrement usage count
- */
- if (bfa_cb_image_get_size(BFA_IOC_FWIMG_TYPE(ioc)) <
- BFA_IOC_FWIMG_MINSZ)
- return;
-
- /**
- * decrement usage count
- */
- bfa_nw_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg);
- usecnt = readl(ioc->ioc_regs.ioc_usage_reg);
- BUG_ON(!(usecnt > 0));
-
- usecnt--;
- writel(usecnt, ioc->ioc_regs.ioc_usage_reg);
-
- bfa_nw_ioc_sem_release(ioc->ioc_regs.ioc_usage_sem_reg);
-}
-
-/**
- * Notify other functions on HB failure.
- */
-static void
-bfa_ioc_ct_notify_fail(struct bfa_ioc *ioc)
-{
- if (ioc->cna) {
- writel(__FW_INIT_HALT_P, ioc->ioc_regs.ll_halt);
- writel(__FW_INIT_HALT_P, ioc->ioc_regs.alt_ll_halt);
- /* Wait for halt to take effect */
- readl(ioc->ioc_regs.ll_halt);
- readl(ioc->ioc_regs.alt_ll_halt);
- } else {
- writel(__PSS_ERR_STATUS_SET, ioc->ioc_regs.err_set);
- readl(ioc->ioc_regs.err_set);
- }
-}
-
-/**
- * Host to LPU mailbox message addresses
- */
-static struct { u32 hfn_mbox, lpu_mbox, hfn_pgn; } iocreg_fnreg[] = {
- { HOSTFN0_LPU_MBOX0_0, LPU_HOSTFN0_MBOX0_0, HOST_PAGE_NUM_FN0 },
- { HOSTFN1_LPU_MBOX0_8, LPU_HOSTFN1_MBOX0_8, HOST_PAGE_NUM_FN1 },
- { HOSTFN2_LPU_MBOX0_0, LPU_HOSTFN2_MBOX0_0, HOST_PAGE_NUM_FN2 },
- { HOSTFN3_LPU_MBOX0_8, LPU_HOSTFN3_MBOX0_8, HOST_PAGE_NUM_FN3 }
-};
-
-/**
- * Host <-> LPU mailbox command/status registers - port 0
- */
-static struct { u32 hfn, lpu; } iocreg_mbcmd_p0[] = {
- { HOSTFN0_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN0_MBOX0_CMD_STAT },
- { HOSTFN1_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN1_MBOX0_CMD_STAT },
- { HOSTFN2_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN2_MBOX0_CMD_STAT },
- { HOSTFN3_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN3_MBOX0_CMD_STAT }
-};
-
-/**
- * Host <-> LPU mailbox command/status registers - port 1
- */
-static struct { u32 hfn, lpu; } iocreg_mbcmd_p1[] = {
- { HOSTFN0_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN0_MBOX0_CMD_STAT },
- { HOSTFN1_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN1_MBOX0_CMD_STAT },
- { HOSTFN2_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN2_MBOX0_CMD_STAT },
- { HOSTFN3_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN3_MBOX0_CMD_STAT }
-};
-
-static void
-bfa_ioc_ct_reg_init(struct bfa_ioc *ioc)
-{
- void __iomem *rb;
- int pcifn = bfa_ioc_pcifn(ioc);
-
- rb = bfa_ioc_bar0(ioc);
-
- ioc->ioc_regs.hfn_mbox = rb + iocreg_fnreg[pcifn].hfn_mbox;
- ioc->ioc_regs.lpu_mbox = rb + iocreg_fnreg[pcifn].lpu_mbox;
- ioc->ioc_regs.host_page_num_fn = rb + iocreg_fnreg[pcifn].hfn_pgn;
-
- if (ioc->port_id == 0) {
- ioc->ioc_regs.heartbeat = rb + BFA_IOC0_HBEAT_REG;
- ioc->ioc_regs.ioc_fwstate = rb + BFA_IOC0_STATE_REG;
- ioc->ioc_regs.alt_ioc_fwstate = rb + BFA_IOC1_STATE_REG;
- ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd_p0[pcifn].hfn;
- ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd_p0[pcifn].lpu;
- ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P0;
- ioc->ioc_regs.alt_ll_halt = rb + FW_INIT_HALT_P1;
- } else {
- ioc->ioc_regs.heartbeat = (rb + BFA_IOC1_HBEAT_REG);
- ioc->ioc_regs.ioc_fwstate = (rb + BFA_IOC1_STATE_REG);
- ioc->ioc_regs.alt_ioc_fwstate = rb + BFA_IOC0_STATE_REG;
- ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd_p1[pcifn].hfn;
- ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd_p1[pcifn].lpu;
- ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P1;
- ioc->ioc_regs.alt_ll_halt = rb + FW_INIT_HALT_P0;
- }
-
- /*
- * PSS control registers
- */
- ioc->ioc_regs.pss_ctl_reg = (rb + PSS_CTL_REG);
- ioc->ioc_regs.pss_err_status_reg = (rb + PSS_ERR_STATUS_REG);
- ioc->ioc_regs.app_pll_fast_ctl_reg = (rb + APP_PLL_425_CTL_REG);
- ioc->ioc_regs.app_pll_slow_ctl_reg = (rb + APP_PLL_312_CTL_REG);
-
- /*
- * IOC semaphore registers and serialization
- */
- ioc->ioc_regs.ioc_sem_reg = (rb + HOST_SEM0_REG);
- ioc->ioc_regs.ioc_usage_sem_reg = (rb + HOST_SEM1_REG);
- ioc->ioc_regs.ioc_init_sem_reg = (rb + HOST_SEM2_REG);
- ioc->ioc_regs.ioc_usage_reg = (rb + BFA_FW_USE_COUNT);
- ioc->ioc_regs.ioc_fail_sync = (rb + BFA_IOC_FAIL_SYNC);
-
- /**
- * sram memory access
- */
- ioc->ioc_regs.smem_page_start = (rb + PSS_SMEM_PAGE_START);
- ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CT;
-
- /*
- * err set reg : for notification of hb failure in fcmode
- */
- ioc->ioc_regs.err_set = (rb + ERR_SET_REG);
-}
-
-/**
- * Initialize IOC to port mapping.
- */
-
-#define FNC_PERS_FN_SHIFT(__fn) ((__fn) * 8)
-static void
-bfa_ioc_ct_map_port(struct bfa_ioc *ioc)
-{
- void __iomem *rb = ioc->pcidev.pci_bar_kva;
- u32 r32;
-
- /**
- * For catapult, base port id on personality register and IOC type
- */
- r32 = readl(rb + FNC_PERS_REG);
- r32 >>= FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc));
- ioc->port_id = (r32 & __F0_PORT_MAP_MK) >> __F0_PORT_MAP_SH;
-
-}
-
-/**
- * Set interrupt mode for a function: INTX or MSIX
- */
-static void
-bfa_ioc_ct_isr_mode_set(struct bfa_ioc *ioc, bool msix)
-{
- void __iomem *rb = ioc->pcidev.pci_bar_kva;
- u32 r32, mode;
-
- r32 = readl(rb + FNC_PERS_REG);
-
- mode = (r32 >> FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc))) &
- __F0_INTX_STATUS;
-
- /**
- * If already in desired mode, do not change anything
- */
- if (!msix && mode)
- return;
-
- if (msix)
- mode = __F0_INTX_STATUS_MSIX;
- else
- mode = __F0_INTX_STATUS_INTA;
-
- r32 &= ~(__F0_INTX_STATUS << FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc)));
- r32 |= (mode << FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc)));
-
- writel(r32, rb + FNC_PERS_REG);
-}
-
-/**
- * Cleanup hw semaphore and usecnt registers
- */
-static void
-bfa_ioc_ct_ownership_reset(struct bfa_ioc *ioc)
-{
- if (ioc->cna) {
- bfa_nw_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg);
- writel(0, ioc->ioc_regs.ioc_usage_reg);
- bfa_nw_ioc_sem_release(ioc->ioc_regs.ioc_usage_sem_reg);
- }
-
- /*
- * Read the hw sem reg to make sure that it is locked
- * before we clear it. If it is not locked, writing 1
- * will lock it instead of clearing it.
- */
- readl(ioc->ioc_regs.ioc_sem_reg);
- bfa_nw_ioc_hw_sem_release(ioc);
-}
-
-/**
- * Synchronized IOC failure processing routines
- */
-static bool
-bfa_ioc_ct_sync_start(struct bfa_ioc *ioc)
-{
- u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
- u32 sync_reqd = bfa_ioc_ct_get_sync_reqd(r32);
-
- /*
- * Driver load time. If the sync required bit for this PCI fn
- * is set, it is due to an unclean exit by the driver for this
- * PCI fn in the previous incarnation. Whoever comes here first
- * should clean it up, no matter which PCI fn.
- */
-
- if (sync_reqd & bfa_ioc_ct_sync_pos(ioc)) {
- writel(0, ioc->ioc_regs.ioc_fail_sync);
- writel(1, ioc->ioc_regs.ioc_usage_reg);
- writel(BFI_IOC_UNINIT, ioc->ioc_regs.ioc_fwstate);
- writel(BFI_IOC_UNINIT, ioc->ioc_regs.alt_ioc_fwstate);
- return true;
- }
-
- return bfa_ioc_ct_sync_complete(ioc);
-}
-/**
- * Synchronized IOC failure processing routines
- */
-static void
-bfa_ioc_ct_sync_join(struct bfa_ioc *ioc)
-{
- u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
- u32 sync_pos = bfa_ioc_ct_sync_reqd_pos(ioc);
-
- writel((r32 | sync_pos), ioc->ioc_regs.ioc_fail_sync);
-}
-
-static void
-bfa_ioc_ct_sync_leave(struct bfa_ioc *ioc)
-{
- u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
- u32 sync_msk = bfa_ioc_ct_sync_reqd_pos(ioc) |
- bfa_ioc_ct_sync_pos(ioc);
-
- writel((r32 & ~sync_msk), ioc->ioc_regs.ioc_fail_sync);
-}
-
-static void
-bfa_ioc_ct_sync_ack(struct bfa_ioc *ioc)
-{
- u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
-
- writel((r32 | bfa_ioc_ct_sync_pos(ioc)), ioc->ioc_regs.ioc_fail_sync);
-}
-
-static bool
-bfa_ioc_ct_sync_complete(struct bfa_ioc *ioc)
-{
- u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
- u32 sync_reqd = bfa_ioc_ct_get_sync_reqd(r32);
- u32 sync_ackd = bfa_ioc_ct_get_sync_ackd(r32);
- u32 tmp_ackd;
-
- if (sync_ackd == 0)
- return true;
-
- /**
- * The check below is to see whether any other PCI fn
- * has reinitialized the ASIC (reset sync_ackd bits)
- * and failed again while this IOC was waiting for hw
- * semaphore (in bfa_iocpf_sm_semwait()).
- */
- tmp_ackd = sync_ackd;
- if ((sync_reqd & bfa_ioc_ct_sync_pos(ioc)) &&
- !(sync_ackd & bfa_ioc_ct_sync_pos(ioc)))
- sync_ackd |= bfa_ioc_ct_sync_pos(ioc);
-
- if (sync_reqd == sync_ackd) {
- writel(bfa_ioc_ct_clear_sync_ackd(r32),
- ioc->ioc_regs.ioc_fail_sync);
- writel(BFI_IOC_FAIL, ioc->ioc_regs.ioc_fwstate);
- writel(BFI_IOC_FAIL, ioc->ioc_regs.alt_ioc_fwstate);
- return true;
- }
-
- /**
- * If another PCI fn reinitialized and failed again while
- * this IOC was waiting for hw sem, the sync_ackd bit for
- * this IOC need to be set again to allow reinitialization.
- */
- if (tmp_ackd != sync_ackd)
- writel((r32 | sync_ackd), ioc->ioc_regs.ioc_fail_sync);
-
- return false;
-}
-
-static enum bfa_status
-bfa_ioc_ct_pll_init(void __iomem *rb, bool fcmode)
-{
- u32 pll_sclk, pll_fclk, r32;
-
- pll_sclk = __APP_PLL_312_LRESETN | __APP_PLL_312_ENARST |
- __APP_PLL_312_RSEL200500 | __APP_PLL_312_P0_1(3U) |
- __APP_PLL_312_JITLMT0_1(3U) |
- __APP_PLL_312_CNTLMT0_1(1U);
- pll_fclk = __APP_PLL_425_LRESETN | __APP_PLL_425_ENARST |
- __APP_PLL_425_RSEL200500 | __APP_PLL_425_P0_1(3U) |
- __APP_PLL_425_JITLMT0_1(3U) |
- __APP_PLL_425_CNTLMT0_1(1U);
- if (fcmode) {
- writel(0, (rb + OP_MODE));
- writel(__APP_EMS_CMLCKSEL |
- __APP_EMS_REFCKBUFEN2 |
- __APP_EMS_CHANNEL_SEL,
- (rb + ETH_MAC_SER_REG));
- } else {
- writel(__GLOBAL_FCOE_MODE, (rb + OP_MODE));
- writel(__APP_EMS_REFCKBUFEN1,
- (rb + ETH_MAC_SER_REG));
- }
- writel(BFI_IOC_UNINIT, (rb + BFA_IOC0_STATE_REG));
- writel(BFI_IOC_UNINIT, (rb + BFA_IOC1_STATE_REG));
- writel(0xffffffffU, (rb + HOSTFN0_INT_MSK));
- writel(0xffffffffU, (rb + HOSTFN1_INT_MSK));
- writel(0xffffffffU, (rb + HOSTFN0_INT_STATUS));
- writel(0xffffffffU, (rb + HOSTFN1_INT_STATUS));
- writel(0xffffffffU, (rb + HOSTFN0_INT_MSK));
- writel(0xffffffffU, (rb + HOSTFN1_INT_MSK));
- writel(pll_sclk |
- __APP_PLL_312_LOGIC_SOFT_RESET,
- rb + APP_PLL_312_CTL_REG);
- writel(pll_fclk |
- __APP_PLL_425_LOGIC_SOFT_RESET,
- rb + APP_PLL_425_CTL_REG);
- writel(pll_sclk |
- __APP_PLL_312_LOGIC_SOFT_RESET | __APP_PLL_312_ENABLE,
- rb + APP_PLL_312_CTL_REG);
- writel(pll_fclk |
- __APP_PLL_425_LOGIC_SOFT_RESET | __APP_PLL_425_ENABLE,
- rb + APP_PLL_425_CTL_REG);
- readl(rb + HOSTFN0_INT_MSK);
- udelay(2000);
- writel(0xffffffffU, (rb + HOSTFN0_INT_STATUS));
- writel(0xffffffffU, (rb + HOSTFN1_INT_STATUS));
- writel(pll_sclk |
- __APP_PLL_312_ENABLE,
- rb + APP_PLL_312_CTL_REG);
- writel(pll_fclk |
- __APP_PLL_425_ENABLE,
- rb + APP_PLL_425_CTL_REG);
- if (!fcmode) {
- writel(__PMM_1T_RESET_P, (rb + PMM_1T_RESET_REG_P0));
- writel(__PMM_1T_RESET_P, (rb + PMM_1T_RESET_REG_P1));
- }
- r32 = readl((rb + PSS_CTL_REG));
- r32 &= ~__PSS_LMEM_RESET;
- writel(r32, (rb + PSS_CTL_REG));
- udelay(1000);
- if (!fcmode) {
- writel(0, (rb + PMM_1T_RESET_REG_P0));
- writel(0, (rb + PMM_1T_RESET_REG_P1));
- }
-
- writel(__EDRAM_BISTR_START, (rb + MBIST_CTL_REG));
- udelay(1000);
- r32 = readl((rb + MBIST_STAT_REG));
- writel(0, (rb + MBIST_CTL_REG));
- return BFA_STATUS_OK;
-}
diff --git a/drivers/net/bna/bfa_wc.h b/drivers/net/bna/bfa_wc.h
deleted file mode 100644
index d0e4caee67b0..000000000000
--- a/drivers/net/bna/bfa_wc.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Linux network driver for Brocade Converged Network Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
- * All rights reserved
- * www.brocade.com
- */
-
-/**
- * @file bfa_wc.h Generic wait counter.
- */
-
-#ifndef __BFA_WC_H__
-#define __BFA_WC_H__
-
-typedef void (*bfa_wc_resume_t) (void *cbarg);
-
-struct bfa_wc {
- bfa_wc_resume_t wc_resume;
- void *wc_cbarg;
- int wc_count;
-};
-
-static inline void
-bfa_wc_up(struct bfa_wc *wc)
-{
- wc->wc_count++;
-}
-
-static inline void
-bfa_wc_down(struct bfa_wc *wc)
-{
- wc->wc_count--;
- if (wc->wc_count == 0)
- wc->wc_resume(wc->wc_cbarg);
-}
-
-/**
- * Initialize a waiting counter.
- */
-static inline void
-bfa_wc_init(struct bfa_wc *wc, bfa_wc_resume_t wc_resume, void *wc_cbarg)
-{
- wc->wc_resume = wc_resume;
- wc->wc_cbarg = wc_cbarg;
- wc->wc_count = 0;
- bfa_wc_up(wc);
-}
-
-/**
- * Wait for counter to reach zero
- */
-static inline void
-bfa_wc_wait(struct bfa_wc *wc)
-{
- bfa_wc_down(wc);
-}
-
-#endif
diff --git a/drivers/net/bna/bfi_ctreg.h b/drivers/net/bna/bfi_ctreg.h
deleted file mode 100644
index 5130d7918660..000000000000
--- a/drivers/net/bna/bfi_ctreg.h
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * Linux network driver for Brocade Converged Network Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
- * All rights reserved
- * www.brocade.com
- */
-
-/*
- * bfi_ctreg.h catapult host block register definitions
- *
- * !!! Do not edit. Auto generated. !!!
- */
-
-#ifndef __BFI_CTREG_H__
-#define __BFI_CTREG_H__
-
-#define HOSTFN0_LPU_MBOX0_0 0x00019200
-#define HOSTFN1_LPU_MBOX0_8 0x00019260
-#define LPU_HOSTFN0_MBOX0_0 0x00019280
-#define LPU_HOSTFN1_MBOX0_8 0x000192e0
-#define HOSTFN2_LPU_MBOX0_0 0x00019400
-#define HOSTFN3_LPU_MBOX0_8 0x00019460
-#define LPU_HOSTFN2_MBOX0_0 0x00019480
-#define LPU_HOSTFN3_MBOX0_8 0x000194e0
-#define HOSTFN0_INT_STATUS 0x00014000
-#define __HOSTFN0_HALT_OCCURRED 0x01000000
-#define __HOSTFN0_INT_STATUS_LVL_MK 0x00f00000
-#define __HOSTFN0_INT_STATUS_LVL_SH 20
-#define __HOSTFN0_INT_STATUS_LVL(_v) ((_v) << __HOSTFN0_INT_STATUS_LVL_SH)
-#define __HOSTFN0_INT_STATUS_P_MK 0x000f0000
-#define __HOSTFN0_INT_STATUS_P_SH 16
-#define __HOSTFN0_INT_STATUS_P(_v) ((_v) << __HOSTFN0_INT_STATUS_P_SH)
-#define __HOSTFN0_INT_STATUS_F 0x0000ffff
-#define HOSTFN0_INT_MSK 0x00014004
-#define HOST_PAGE_NUM_FN0 0x00014008
-#define __HOST_PAGE_NUM_FN 0x000001ff
-#define HOST_MSIX_ERR_INDEX_FN0 0x0001400c
-#define __MSIX_ERR_INDEX_FN 0x000001ff
-#define HOSTFN1_INT_STATUS 0x00014100
-#define __HOSTFN1_HALT_OCCURRED 0x01000000
-#define __HOSTFN1_INT_STATUS_LVL_MK 0x00f00000
-#define __HOSTFN1_INT_STATUS_LVL_SH 20
-#define __HOSTFN1_INT_STATUS_LVL(_v) ((_v) << __HOSTFN1_INT_STATUS_LVL_SH)
-#define __HOSTFN1_INT_STATUS_P_MK 0x000f0000
-#define __HOSTFN1_INT_STATUS_P_SH 16
-#define __HOSTFN1_INT_STATUS_P(_v) ((_v) << __HOSTFN1_INT_STATUS_P_SH)
-#define __HOSTFN1_INT_STATUS_F 0x0000ffff
-#define HOSTFN1_INT_MSK 0x00014104
-#define HOST_PAGE_NUM_FN1 0x00014108
-#define HOST_MSIX_ERR_INDEX_FN1 0x0001410c
-#define APP_PLL_425_CTL_REG 0x00014204
-#define __P_425_PLL_LOCK 0x80000000
-#define __APP_PLL_425_SRAM_USE_100MHZ 0x00100000
-#define __APP_PLL_425_RESET_TIMER_MK 0x000e0000
-#define __APP_PLL_425_RESET_TIMER_SH 17
-#define __APP_PLL_425_RESET_TIMER(_v) ((_v) << __APP_PLL_425_RESET_TIMER_SH)
-#define __APP_PLL_425_LOGIC_SOFT_RESET 0x00010000
-#define __APP_PLL_425_CNTLMT0_1_MK 0x0000c000
-#define __APP_PLL_425_CNTLMT0_1_SH 14
-#define __APP_PLL_425_CNTLMT0_1(_v) ((_v) << __APP_PLL_425_CNTLMT0_1_SH)
-#define __APP_PLL_425_JITLMT0_1_MK 0x00003000
-#define __APP_PLL_425_JITLMT0_1_SH 12
-#define __APP_PLL_425_JITLMT0_1(_v) ((_v) << __APP_PLL_425_JITLMT0_1_SH)
-#define __APP_PLL_425_HREF 0x00000800
-#define __APP_PLL_425_HDIV 0x00000400
-#define __APP_PLL_425_P0_1_MK 0x00000300
-#define __APP_PLL_425_P0_1_SH 8
-#define __APP_PLL_425_P0_1(_v) ((_v) << __APP_PLL_425_P0_1_SH)
-#define __APP_PLL_425_Z0_2_MK 0x000000e0
-#define __APP_PLL_425_Z0_2_SH 5
-#define __APP_PLL_425_Z0_2(_v) ((_v) << __APP_PLL_425_Z0_2_SH)
-#define __APP_PLL_425_RSEL200500 0x00000010
-#define __APP_PLL_425_ENARST 0x00000008
-#define __APP_PLL_425_BYPASS 0x00000004
-#define __APP_PLL_425_LRESETN 0x00000002
-#define __APP_PLL_425_ENABLE 0x00000001
-#define APP_PLL_312_CTL_REG 0x00014208
-#define __P_312_PLL_LOCK 0x80000000
-#define __ENABLE_MAC_AHB_1 0x00800000
-#define __ENABLE_MAC_AHB_0 0x00400000
-#define __ENABLE_MAC_1 0x00200000
-#define __ENABLE_MAC_0 0x00100000
-#define __APP_PLL_312_RESET_TIMER_MK 0x000e0000
-#define __APP_PLL_312_RESET_TIMER_SH 17
-#define __APP_PLL_312_RESET_TIMER(_v) ((_v) << __APP_PLL_312_RESET_TIMER_SH)
-#define __APP_PLL_312_LOGIC_SOFT_RESET 0x00010000
-#define __APP_PLL_312_CNTLMT0_1_MK 0x0000c000
-#define __APP_PLL_312_CNTLMT0_1_SH 14
-#define __APP_PLL_312_CNTLMT0_1(_v) ((_v) << __APP_PLL_312_CNTLMT0_1_SH)
-#define __APP_PLL_312_JITLMT0_1_MK 0x00003000
-#define __APP_PLL_312_JITLMT0_1_SH 12
-#define __APP_PLL_312_JITLMT0_1(_v) ((_v) << __APP_PLL_312_JITLMT0_1_SH)
-#define __APP_PLL_312_HREF 0x00000800
-#define __APP_PLL_312_HDIV 0x00000400
-#define __APP_PLL_312_P0_1_MK 0x00000300
-#define __APP_PLL_312_P0_1_SH 8
-#define __APP_PLL_312_P0_1(_v) ((_v) << __APP_PLL_312_P0_1_SH)
-#define __APP_PLL_312_Z0_2_MK 0x000000e0
-#define __APP_PLL_312_Z0_2_SH 5
-#define __APP_PLL_312_Z0_2(_v) ((_v) << __APP_PLL_312_Z0_2_SH)
-#define __APP_PLL_312_RSEL200500 0x00000010
-#define __APP_PLL_312_ENARST 0x00000008
-#define __APP_PLL_312_BYPASS 0x00000004
-#define __APP_PLL_312_LRESETN 0x00000002
-#define __APP_PLL_312_ENABLE 0x00000001
-#define MBIST_CTL_REG 0x00014220
-#define __EDRAM_BISTR_START 0x00000004
-#define __MBIST_RESET 0x00000002
-#define __MBIST_START 0x00000001
-#define MBIST_STAT_REG 0x00014224
-#define __EDRAM_BISTR_STATUS 0x00000008
-#define __EDRAM_BISTR_DONE 0x00000004
-#define __MEM_BIT_STATUS 0x00000002
-#define __MBIST_DONE 0x00000001
-#define HOST_SEM0_REG 0x00014230
-#define __HOST_SEMAPHORE 0x00000001
-#define HOST_SEM1_REG 0x00014234
-#define HOST_SEM2_REG 0x00014238
-#define HOST_SEM3_REG 0x0001423c
-#define HOST_SEM0_INFO_REG 0x00014240
-#define HOST_SEM1_INFO_REG 0x00014244
-#define HOST_SEM2_INFO_REG 0x00014248
-#define HOST_SEM3_INFO_REG 0x0001424c
-#define ETH_MAC_SER_REG 0x00014288
-#define __APP_EMS_CKBUFAMPIN 0x00000020
-#define __APP_EMS_REFCLKSEL 0x00000010
-#define __APP_EMS_CMLCKSEL 0x00000008
-#define __APP_EMS_REFCKBUFEN2 0x00000004
-#define __APP_EMS_REFCKBUFEN1 0x00000002
-#define __APP_EMS_CHANNEL_SEL 0x00000001
-#define HOSTFN2_INT_STATUS 0x00014300
-#define __HOSTFN2_HALT_OCCURRED 0x01000000
-#define __HOSTFN2_INT_STATUS_LVL_MK 0x00f00000
-#define __HOSTFN2_INT_STATUS_LVL_SH 20
-#define __HOSTFN2_INT_STATUS_LVL(_v) ((_v) << __HOSTFN2_INT_STATUS_LVL_SH)
-#define __HOSTFN2_INT_STATUS_P_MK 0x000f0000
-#define __HOSTFN2_INT_STATUS_P_SH 16
-#define __HOSTFN2_INT_STATUS_P(_v) ((_v) << __HOSTFN2_INT_STATUS_P_SH)
-#define __HOSTFN2_INT_STATUS_F 0x0000ffff
-#define HOSTFN2_INT_MSK 0x00014304
-#define HOST_PAGE_NUM_FN2 0x00014308
-#define HOST_MSIX_ERR_INDEX_FN2 0x0001430c
-#define HOSTFN3_INT_STATUS 0x00014400
-#define __HALT_OCCURRED 0x01000000
-#define __HOSTFN3_INT_STATUS_LVL_MK 0x00f00000
-#define __HOSTFN3_INT_STATUS_LVL_SH 20
-#define __HOSTFN3_INT_STATUS_LVL(_v) ((_v) << __HOSTFN3_INT_STATUS_LVL_SH)
-#define __HOSTFN3_INT_STATUS_P_MK 0x000f0000
-#define __HOSTFN3_INT_STATUS_P_SH 16
-#define __HOSTFN3_INT_STATUS_P(_v) ((_v) << __HOSTFN3_INT_STATUS_P_SH)
-#define __HOSTFN3_INT_STATUS_F 0x0000ffff
-#define HOSTFN3_INT_MSK 0x00014404
-#define HOST_PAGE_NUM_FN3 0x00014408
-#define HOST_MSIX_ERR_INDEX_FN3 0x0001440c
-#define FNC_ID_REG 0x00014600
-#define __FUNCTION_NUMBER 0x00000007
-#define FNC_PERS_REG 0x00014604
-#define __F3_FUNCTION_ACTIVE 0x80000000
-#define __F3_FUNCTION_MODE 0x40000000
-#define __F3_PORT_MAP_MK 0x30000000
-#define __F3_PORT_MAP_SH 28
-#define __F3_PORT_MAP(_v) ((_v) << __F3_PORT_MAP_SH)
-#define __F3_VM_MODE 0x08000000
-#define __F3_INTX_STATUS_MK 0x07000000
-#define __F3_INTX_STATUS_SH 24
-#define __F3_INTX_STATUS(_v) ((_v) << __F3_INTX_STATUS_SH)
-#define __F2_FUNCTION_ACTIVE 0x00800000
-#define __F2_FUNCTION_MODE 0x00400000
-#define __F2_PORT_MAP_MK 0x00300000
-#define __F2_PORT_MAP_SH 20
-#define __F2_PORT_MAP(_v) ((_v) << __F2_PORT_MAP_SH)
-#define __F2_VM_MODE 0x00080000
-#define __F2_INTX_STATUS_MK 0x00070000
-#define __F2_INTX_STATUS_SH 16
-#define __F2_INTX_STATUS(_v) ((_v) << __F2_INTX_STATUS_SH)
-#define __F1_FUNCTION_ACTIVE 0x00008000
-#define __F1_FUNCTION_MODE 0x00004000
-#define __F1_PORT_MAP_MK 0x00003000
-#define __F1_PORT_MAP_SH 12
-#define __F1_PORT_MAP(_v) ((_v) << __F1_PORT_MAP_SH)
-#define __F1_VM_MODE 0x00000800
-#define __F1_INTX_STATUS_MK 0x00000700
-#define __F1_INTX_STATUS_SH 8
-#define __F1_INTX_STATUS(_v) ((_v) << __F1_INTX_STATUS_SH)
-#define __F0_FUNCTION_ACTIVE 0x00000080
-#define __F0_FUNCTION_MODE 0x00000040
-#define __F0_PORT_MAP_MK 0x00000030
-#define __F0_PORT_MAP_SH 4
-#define __F0_PORT_MAP(_v) ((_v) << __F0_PORT_MAP_SH)
-#define __F0_VM_MODE 0x00000008
-#define __F0_INTX_STATUS 0x00000007
-enum {
- __F0_INTX_STATUS_MSIX = 0x0,
- __F0_INTX_STATUS_INTA = 0x1,
- __F0_INTX_STATUS_INTB = 0x2,
- __F0_INTX_STATUS_INTC = 0x3,
- __F0_INTX_STATUS_INTD = 0x4,
-};
-#define OP_MODE 0x0001460c
-#define __APP_ETH_CLK_LOWSPEED 0x00000004
-#define __GLOBAL_CORECLK_HALFSPEED 0x00000002
-#define __GLOBAL_FCOE_MODE 0x00000001
-#define HOST_SEM4_REG 0x00014610
-#define HOST_SEM5_REG 0x00014614
-#define HOST_SEM6_REG 0x00014618
-#define HOST_SEM7_REG 0x0001461c
-#define HOST_SEM4_INFO_REG 0x00014620
-#define HOST_SEM5_INFO_REG 0x00014624
-#define HOST_SEM6_INFO_REG 0x00014628
-#define HOST_SEM7_INFO_REG 0x0001462c
-#define HOSTFN0_LPU0_MBOX0_CMD_STAT 0x00019000
-#define __HOSTFN0_LPU0_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN0_LPU0_MBOX0_INFO_SH 1
-#define __HOSTFN0_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN0_LPU0_MBOX0_INFO_SH)
-#define __HOSTFN0_LPU0_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN0_LPU1_MBOX0_CMD_STAT 0x00019004
-#define __HOSTFN0_LPU1_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN0_LPU1_MBOX0_INFO_SH 1
-#define __HOSTFN0_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN0_LPU1_MBOX0_INFO_SH)
-#define __HOSTFN0_LPU1_MBOX0_CMD_STATUS 0x00000001
-#define LPU0_HOSTFN0_MBOX0_CMD_STAT 0x00019008
-#define __LPU0_HOSTFN0_MBOX0_INFO_MK 0xfffffffe
-#define __LPU0_HOSTFN0_MBOX0_INFO_SH 1
-#define __LPU0_HOSTFN0_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN0_MBOX0_INFO_SH)
-#define __LPU0_HOSTFN0_MBOX0_CMD_STATUS 0x00000001
-#define LPU1_HOSTFN0_MBOX0_CMD_STAT 0x0001900c
-#define __LPU1_HOSTFN0_MBOX0_INFO_MK 0xfffffffe
-#define __LPU1_HOSTFN0_MBOX0_INFO_SH 1
-#define __LPU1_HOSTFN0_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN0_MBOX0_INFO_SH)
-#define __LPU1_HOSTFN0_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN1_LPU0_MBOX0_CMD_STAT 0x00019010
-#define __HOSTFN1_LPU0_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN1_LPU0_MBOX0_INFO_SH 1
-#define __HOSTFN1_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN1_LPU0_MBOX0_INFO_SH)
-#define __HOSTFN1_LPU0_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN1_LPU1_MBOX0_CMD_STAT 0x00019014
-#define __HOSTFN1_LPU1_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN1_LPU1_MBOX0_INFO_SH 1
-#define __HOSTFN1_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN1_LPU1_MBOX0_INFO_SH)
-#define __HOSTFN1_LPU1_MBOX0_CMD_STATUS 0x00000001
-#define LPU0_HOSTFN1_MBOX0_CMD_STAT 0x00019018
-#define __LPU0_HOSTFN1_MBOX0_INFO_MK 0xfffffffe
-#define __LPU0_HOSTFN1_MBOX0_INFO_SH 1
-#define __LPU0_HOSTFN1_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN1_MBOX0_INFO_SH)
-#define __LPU0_HOSTFN1_MBOX0_CMD_STATUS 0x00000001
-#define LPU1_HOSTFN1_MBOX0_CMD_STAT 0x0001901c
-#define __LPU1_HOSTFN1_MBOX0_INFO_MK 0xfffffffe
-#define __LPU1_HOSTFN1_MBOX0_INFO_SH 1
-#define __LPU1_HOSTFN1_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN1_MBOX0_INFO_SH)
-#define __LPU1_HOSTFN1_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN2_LPU0_MBOX0_CMD_STAT 0x00019150
-#define __HOSTFN2_LPU0_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN2_LPU0_MBOX0_INFO_SH 1
-#define __HOSTFN2_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN2_LPU0_MBOX0_INFO_SH)
-#define __HOSTFN2_LPU0_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN2_LPU1_MBOX0_CMD_STAT 0x00019154
-#define __HOSTFN2_LPU1_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN2_LPU1_MBOX0_INFO_SH 1
-#define __HOSTFN2_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN2_LPU1_MBOX0_INFO_SH)
-#define __HOSTFN2_LPU1_MBOX0BOX0_CMD_STATUS 0x00000001
-#define LPU0_HOSTFN2_MBOX0_CMD_STAT 0x00019158
-#define __LPU0_HOSTFN2_MBOX0_INFO_MK 0xfffffffe
-#define __LPU0_HOSTFN2_MBOX0_INFO_SH 1
-#define __LPU0_HOSTFN2_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN2_MBOX0_INFO_SH)
-#define __LPU0_HOSTFN2_MBOX0_CMD_STATUS 0x00000001
-#define LPU1_HOSTFN2_MBOX0_CMD_STAT 0x0001915c
-#define __LPU1_HOSTFN2_MBOX0_INFO_MK 0xfffffffe
-#define __LPU1_HOSTFN2_MBOX0_INFO_SH 1
-#define __LPU1_HOSTFN2_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN2_MBOX0_INFO_SH)
-#define __LPU1_HOSTFN2_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN3_LPU0_MBOX0_CMD_STAT 0x00019160
-#define __HOSTFN3_LPU0_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN3_LPU0_MBOX0_INFO_SH 1
-#define __HOSTFN3_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN3_LPU0_MBOX0_INFO_SH)
-#define __HOSTFN3_LPU0_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN3_LPU1_MBOX0_CMD_STAT 0x00019164
-#define __HOSTFN3_LPU1_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN3_LPU1_MBOX0_INFO_SH 1
-#define __HOSTFN3_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN3_LPU1_MBOX0_INFO_SH)
-#define __HOSTFN3_LPU1_MBOX0_CMD_STATUS 0x00000001
-#define LPU0_HOSTFN3_MBOX0_CMD_STAT 0x00019168
-#define __LPU0_HOSTFN3_MBOX0_INFO_MK 0xfffffffe
-#define __LPU0_HOSTFN3_MBOX0_INFO_SH 1
-#define __LPU0_HOSTFN3_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN3_MBOX0_INFO_SH)
-#define __LPU0_HOSTFN3_MBOX0_CMD_STATUS 0x00000001
-#define LPU1_HOSTFN3_MBOX0_CMD_STAT 0x0001916c
-#define __LPU1_HOSTFN3_MBOX0_INFO_MK 0xfffffffe
-#define __LPU1_HOSTFN3_MBOX0_INFO_SH 1
-#define __LPU1_HOSTFN3_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN3_MBOX0_INFO_SH)
-#define __LPU1_HOSTFN3_MBOX0_CMD_STATUS 0x00000001
-#define FW_INIT_HALT_P0 0x000191ac
-#define __FW_INIT_HALT_P 0x00000001
-#define FW_INIT_HALT_P1 0x000191bc
-#define CPE_PI_PTR_Q0 0x00038000
-#define __CPE_PI_UNUSED_MK 0xffff0000
-#define __CPE_PI_UNUSED_SH 16
-#define __CPE_PI_UNUSED(_v) ((_v) << __CPE_PI_UNUSED_SH)
-#define __CPE_PI_PTR 0x0000ffff
-#define CPE_PI_PTR_Q1 0x00038040
-#define CPE_CI_PTR_Q0 0x00038004
-#define __CPE_CI_UNUSED_MK 0xffff0000
-#define __CPE_CI_UNUSED_SH 16
-#define __CPE_CI_UNUSED(_v) ((_v) << __CPE_CI_UNUSED_SH)
-#define __CPE_CI_PTR 0x0000ffff
-#define CPE_CI_PTR_Q1 0x00038044
-#define CPE_DEPTH_Q0 0x00038008
-#define __CPE_DEPTH_UNUSED_MK 0xf8000000
-#define __CPE_DEPTH_UNUSED_SH 27
-#define __CPE_DEPTH_UNUSED(_v) ((_v) << __CPE_DEPTH_UNUSED_SH)
-#define __CPE_MSIX_VEC_INDEX_MK 0x07ff0000
-#define __CPE_MSIX_VEC_INDEX_SH 16
-#define __CPE_MSIX_VEC_INDEX(_v) ((_v) << __CPE_MSIX_VEC_INDEX_SH)
-#define __CPE_DEPTH 0x0000ffff
-#define CPE_DEPTH_Q1 0x00038048
-#define CPE_QCTRL_Q0 0x0003800c
-#define __CPE_CTRL_UNUSED30_MK 0xfc000000
-#define __CPE_CTRL_UNUSED30_SH 26
-#define __CPE_CTRL_UNUSED30(_v) ((_v) << __CPE_CTRL_UNUSED30_SH)
-#define __CPE_FUNC_INT_CTRL_MK 0x03000000
-#define __CPE_FUNC_INT_CTRL_SH 24
-#define __CPE_FUNC_INT_CTRL(_v) ((_v) << __CPE_FUNC_INT_CTRL_SH)
-enum {
- __CPE_FUNC_INT_CTRL_DISABLE = 0x0,
- __CPE_FUNC_INT_CTRL_F2NF = 0x1,
- __CPE_FUNC_INT_CTRL_3QUART = 0x2,
- __CPE_FUNC_INT_CTRL_HALF = 0x3,
-};
-#define __CPE_CTRL_UNUSED20_MK 0x00f00000
-#define __CPE_CTRL_UNUSED20_SH 20
-#define __CPE_CTRL_UNUSED20(_v) ((_v) << __CPE_CTRL_UNUSED20_SH)
-#define __CPE_SCI_TH_MK 0x000f0000
-#define __CPE_SCI_TH_SH 16
-#define __CPE_SCI_TH(_v) ((_v) << __CPE_SCI_TH_SH)
-#define __CPE_CTRL_UNUSED10_MK 0x0000c000
-#define __CPE_CTRL_UNUSED10_SH 14
-#define __CPE_CTRL_UNUSED10(_v) ((_v) << __CPE_CTRL_UNUSED10_SH)
-#define __CPE_ACK_PENDING 0x00002000
-#define __CPE_CTRL_UNUSED40_MK 0x00001c00
-#define __CPE_CTRL_UNUSED40_SH 10
-#define __CPE_CTRL_UNUSED40(_v) ((_v) << __CPE_CTRL_UNUSED40_SH)
-#define __CPE_PCIEID_MK 0x00000300
-#define __CPE_PCIEID_SH 8
-#define __CPE_PCIEID(_v) ((_v) << __CPE_PCIEID_SH)
-#define __CPE_CTRL_UNUSED00_MK 0x000000fe
-#define __CPE_CTRL_UNUSED00_SH 1
-#define __CPE_CTRL_UNUSED00(_v) ((_v) << __CPE_CTRL_UNUSED00_SH)
-#define __CPE_ESIZE 0x00000001
-#define CPE_QCTRL_Q1 0x0003804c
-#define __CPE_CTRL_UNUSED31_MK 0xfc000000
-#define __CPE_CTRL_UNUSED31_SH 26
-#define __CPE_CTRL_UNUSED31(_v) ((_v) << __CPE_CTRL_UNUSED31_SH)
-#define __CPE_CTRL_UNUSED21_MK 0x00f00000
-#define __CPE_CTRL_UNUSED21_SH 20
-#define __CPE_CTRL_UNUSED21(_v) ((_v) << __CPE_CTRL_UNUSED21_SH)
-#define __CPE_CTRL_UNUSED11_MK 0x0000c000
-#define __CPE_CTRL_UNUSED11_SH 14
-#define __CPE_CTRL_UNUSED11(_v) ((_v) << __CPE_CTRL_UNUSED11_SH)
-#define __CPE_CTRL_UNUSED41_MK 0x00001c00
-#define __CPE_CTRL_UNUSED41_SH 10
-#define __CPE_CTRL_UNUSED41(_v) ((_v) << __CPE_CTRL_UNUSED41_SH)
-#define __CPE_CTRL_UNUSED01_MK 0x000000fe
-#define __CPE_CTRL_UNUSED01_SH 1
-#define __CPE_CTRL_UNUSED01(_v) ((_v) << __CPE_CTRL_UNUSED01_SH)
-#define RME_PI_PTR_Q0 0x00038020
-#define __LATENCY_TIME_STAMP_MK 0xffff0000
-#define __LATENCY_TIME_STAMP_SH 16
-#define __LATENCY_TIME_STAMP(_v) ((_v) << __LATENCY_TIME_STAMP_SH)
-#define __RME_PI_PTR 0x0000ffff
-#define RME_PI_PTR_Q1 0x00038060
-#define RME_CI_PTR_Q0 0x00038024
-#define __DELAY_TIME_STAMP_MK 0xffff0000
-#define __DELAY_TIME_STAMP_SH 16
-#define __DELAY_TIME_STAMP(_v) ((_v) << __DELAY_TIME_STAMP_SH)
-#define __RME_CI_PTR 0x0000ffff
-#define RME_CI_PTR_Q1 0x00038064
-#define RME_DEPTH_Q0 0x00038028
-#define __RME_DEPTH_UNUSED_MK 0xf8000000
-#define __RME_DEPTH_UNUSED_SH 27
-#define __RME_DEPTH_UNUSED(_v) ((_v) << __RME_DEPTH_UNUSED_SH)
-#define __RME_MSIX_VEC_INDEX_MK 0x07ff0000
-#define __RME_MSIX_VEC_INDEX_SH 16
-#define __RME_MSIX_VEC_INDEX(_v) ((_v) << __RME_MSIX_VEC_INDEX_SH)
-#define __RME_DEPTH 0x0000ffff
-#define RME_DEPTH_Q1 0x00038068
-#define RME_QCTRL_Q0 0x0003802c
-#define __RME_INT_LATENCY_TIMER_MK 0xff000000
-#define __RME_INT_LATENCY_TIMER_SH 24
-#define __RME_INT_LATENCY_TIMER(_v) ((_v) << __RME_INT_LATENCY_TIMER_SH)
-#define __RME_INT_DELAY_TIMER_MK 0x00ff0000
-#define __RME_INT_DELAY_TIMER_SH 16
-#define __RME_INT_DELAY_TIMER(_v) ((_v) << __RME_INT_DELAY_TIMER_SH)
-#define __RME_INT_DELAY_DISABLE 0x00008000
-#define __RME_DLY_DELAY_DISABLE 0x00004000
-#define __RME_ACK_PENDING 0x00002000
-#define __RME_FULL_INTERRUPT_DISABLE 0x00001000
-#define __RME_CTRL_UNUSED10_MK 0x00000c00
-#define __RME_CTRL_UNUSED10_SH 10
-#define __RME_CTRL_UNUSED10(_v) ((_v) << __RME_CTRL_UNUSED10_SH)
-#define __RME_PCIEID_MK 0x00000300
-#define __RME_PCIEID_SH 8
-#define __RME_PCIEID(_v) ((_v) << __RME_PCIEID_SH)
-#define __RME_CTRL_UNUSED00_MK 0x000000fe
-#define __RME_CTRL_UNUSED00_SH 1
-#define __RME_CTRL_UNUSED00(_v) ((_v) << __RME_CTRL_UNUSED00_SH)
-#define __RME_ESIZE 0x00000001
-#define RME_QCTRL_Q1 0x0003806c
-#define __RME_CTRL_UNUSED11_MK 0x00000c00
-#define __RME_CTRL_UNUSED11_SH 10
-#define __RME_CTRL_UNUSED11(_v) ((_v) << __RME_CTRL_UNUSED11_SH)
-#define __RME_CTRL_UNUSED01_MK 0x000000fe
-#define __RME_CTRL_UNUSED01_SH 1
-#define __RME_CTRL_UNUSED01(_v) ((_v) << __RME_CTRL_UNUSED01_SH)
-#define PSS_CTL_REG 0x00018800
-#define __PSS_I2C_CLK_DIV_MK 0x007f0000
-#define __PSS_I2C_CLK_DIV_SH 16
-#define __PSS_I2C_CLK_DIV(_v) ((_v) << __PSS_I2C_CLK_DIV_SH)
-#define __PSS_LMEM_INIT_DONE 0x00001000
-#define __PSS_LMEM_RESET 0x00000200
-#define __PSS_LMEM_INIT_EN 0x00000100
-#define __PSS_LPU1_RESET 0x00000002
-#define __PSS_LPU0_RESET 0x00000001
-#define PSS_ERR_STATUS_REG 0x00018810
-#define __PSS_LPU1_TCM_READ_ERR 0x00200000
-#define __PSS_LPU0_TCM_READ_ERR 0x00100000
-#define __PSS_LMEM5_CORR_ERR 0x00080000
-#define __PSS_LMEM4_CORR_ERR 0x00040000
-#define __PSS_LMEM3_CORR_ERR 0x00020000
-#define __PSS_LMEM2_CORR_ERR 0x00010000
-#define __PSS_LMEM1_CORR_ERR 0x00008000
-#define __PSS_LMEM0_CORR_ERR 0x00004000
-#define __PSS_LMEM5_UNCORR_ERR 0x00002000
-#define __PSS_LMEM4_UNCORR_ERR 0x00001000
-#define __PSS_LMEM3_UNCORR_ERR 0x00000800
-#define __PSS_LMEM2_UNCORR_ERR 0x00000400
-#define __PSS_LMEM1_UNCORR_ERR 0x00000200
-#define __PSS_LMEM0_UNCORR_ERR 0x00000100
-#define __PSS_BAL_PERR 0x00000080
-#define __PSS_DIP_IF_ERR 0x00000040
-#define __PSS_IOH_IF_ERR 0x00000020
-#define __PSS_TDS_IF_ERR 0x00000010
-#define __PSS_RDS_IF_ERR 0x00000008
-#define __PSS_SGM_IF_ERR 0x00000004
-#define __PSS_LPU1_RAM_ERR 0x00000002
-#define __PSS_LPU0_RAM_ERR 0x00000001
-#define ERR_SET_REG 0x00018818
-#define __PSS_ERR_STATUS_SET 0x003fffff
-#define PMM_1T_RESET_REG_P0 0x0002381c
-#define __PMM_1T_RESET_P 0x00000001
-#define PMM_1T_RESET_REG_P1 0x00023c1c
-#define HQM_QSET0_RXQ_DRBL_P0 0x00038000
-#define __RXQ0_ADD_VECTORS_P 0x80000000
-#define __RXQ0_STOP_P 0x40000000
-#define __RXQ0_PRD_PTR_P 0x0000ffff
-#define HQM_QSET1_RXQ_DRBL_P0 0x00038080
-#define __RXQ1_ADD_VECTORS_P 0x80000000
-#define __RXQ1_STOP_P 0x40000000
-#define __RXQ1_PRD_PTR_P 0x0000ffff
-#define HQM_QSET0_RXQ_DRBL_P1 0x0003c000
-#define HQM_QSET1_RXQ_DRBL_P1 0x0003c080
-#define HQM_QSET0_TXQ_DRBL_P0 0x00038020
-#define __TXQ0_ADD_VECTORS_P 0x80000000
-#define __TXQ0_STOP_P 0x40000000
-#define __TXQ0_PRD_PTR_P 0x0000ffff
-#define HQM_QSET1_TXQ_DRBL_P0 0x000380a0
-#define __TXQ1_ADD_VECTORS_P 0x80000000
-#define __TXQ1_STOP_P 0x40000000
-#define __TXQ1_PRD_PTR_P 0x0000ffff
-#define HQM_QSET0_TXQ_DRBL_P1 0x0003c020
-#define HQM_QSET1_TXQ_DRBL_P1 0x0003c0a0
-#define HQM_QSET0_IB_DRBL_1_P0 0x00038040
-#define __IB1_0_ACK_P 0x80000000
-#define __IB1_0_DISABLE_P 0x40000000
-#define __IB1_0_COALESCING_CFG_P_MK 0x00ff0000
-#define __IB1_0_COALESCING_CFG_P_SH 16
-#define __IB1_0_COALESCING_CFG_P(_v) ((_v) << __IB1_0_COALESCING_CFG_P_SH)
-#define __IB1_0_NUM_OF_ACKED_EVENTS_P 0x0000ffff
-#define HQM_QSET1_IB_DRBL_1_P0 0x000380c0
-#define __IB1_1_ACK_P 0x80000000
-#define __IB1_1_DISABLE_P 0x40000000
-#define __IB1_1_COALESCING_CFG_P_MK 0x00ff0000
-#define __IB1_1_COALESCING_CFG_P_SH 16
-#define __IB1_1_COALESCING_CFG_P(_v) ((_v) << __IB1_1_COALESCING_CFG_P_SH)
-#define __IB1_1_NUM_OF_ACKED_EVENTS_P 0x0000ffff
-#define HQM_QSET0_IB_DRBL_1_P1 0x0003c040
-#define HQM_QSET1_IB_DRBL_1_P1 0x0003c0c0
-#define HQM_QSET0_IB_DRBL_2_P0 0x00038060
-#define __IB2_0_ACK_P 0x80000000
-#define __IB2_0_DISABLE_P 0x40000000
-#define __IB2_0_COALESCING_CFG_P_MK 0x00ff0000
-#define __IB2_0_COALESCING_CFG_P_SH 16
-#define __IB2_0_COALESCING_CFG_P(_v) ((_v) << __IB2_0_COALESCING_CFG_P_SH)
-#define __IB2_0_NUM_OF_ACKED_EVENTS_P 0x0000ffff
-#define HQM_QSET1_IB_DRBL_2_P0 0x000380e0
-#define __IB2_1_ACK_P 0x80000000
-#define __IB2_1_DISABLE_P 0x40000000
-#define __IB2_1_COALESCING_CFG_P_MK 0x00ff0000
-#define __IB2_1_COALESCING_CFG_P_SH 16
-#define __IB2_1_COALESCING_CFG_P(_v) ((_v) << __IB2_1_COALESCING_CFG_P_SH)
-#define __IB2_1_NUM_OF_ACKED_EVENTS_P 0x0000ffff
-#define HQM_QSET0_IB_DRBL_2_P1 0x0003c060
-#define HQM_QSET1_IB_DRBL_2_P1 0x0003c0e0
-
-/*
- * These definitions are either in error/missing in spec. Its auto-generated
- * from hard coded values in regparse.pl.
- */
-#define __EMPHPOST_AT_4G_MK_FIX 0x0000001c
-#define __EMPHPOST_AT_4G_SH_FIX 0x00000002
-#define __EMPHPRE_AT_4G_FIX 0x00000003
-#define __SFP_TXRATE_EN_FIX 0x00000100
-#define __SFP_RXRATE_EN_FIX 0x00000080
-
-/*
- * These register definitions are auto-generated from hard coded values
- * in regparse.pl.
- */
-
-/*
- * These register mapping definitions are auto-generated from mapping tables
- * in regparse.pl.
- */
-#define BFA_IOC0_HBEAT_REG HOST_SEM0_INFO_REG
-#define BFA_IOC0_STATE_REG HOST_SEM1_INFO_REG
-#define BFA_IOC1_HBEAT_REG HOST_SEM2_INFO_REG
-#define BFA_IOC1_STATE_REG HOST_SEM3_INFO_REG
-#define BFA_FW_USE_COUNT HOST_SEM4_INFO_REG
-#define BFA_IOC_FAIL_SYNC HOST_SEM5_INFO_REG
-
-#define CPE_DEPTH_Q(__n) \
- (CPE_DEPTH_Q0 + (__n) * (CPE_DEPTH_Q1 - CPE_DEPTH_Q0))
-#define CPE_QCTRL_Q(__n) \
- (CPE_QCTRL_Q0 + (__n) * (CPE_QCTRL_Q1 - CPE_QCTRL_Q0))
-#define CPE_PI_PTR_Q(__n) \
- (CPE_PI_PTR_Q0 + (__n) * (CPE_PI_PTR_Q1 - CPE_PI_PTR_Q0))
-#define CPE_CI_PTR_Q(__n) \
- (CPE_CI_PTR_Q0 + (__n) * (CPE_CI_PTR_Q1 - CPE_CI_PTR_Q0))
-#define RME_DEPTH_Q(__n) \
- (RME_DEPTH_Q0 + (__n) * (RME_DEPTH_Q1 - RME_DEPTH_Q0))
-#define RME_QCTRL_Q(__n) \
- (RME_QCTRL_Q0 + (__n) * (RME_QCTRL_Q1 - RME_QCTRL_Q0))
-#define RME_PI_PTR_Q(__n) \
- (RME_PI_PTR_Q0 + (__n) * (RME_PI_PTR_Q1 - RME_PI_PTR_Q0))
-#define RME_CI_PTR_Q(__n) \
- (RME_CI_PTR_Q0 + (__n) * (RME_CI_PTR_Q1 - RME_CI_PTR_Q0))
-#define HQM_QSET_RXQ_DRBL_P0(__n) \
- (HQM_QSET0_RXQ_DRBL_P0 + (__n) * \
- (HQM_QSET1_RXQ_DRBL_P0 - HQM_QSET0_RXQ_DRBL_P0))
-#define HQM_QSET_TXQ_DRBL_P0(__n) \
- (HQM_QSET0_TXQ_DRBL_P0 + (__n) * \
- (HQM_QSET1_TXQ_DRBL_P0 - HQM_QSET0_TXQ_DRBL_P0))
-#define HQM_QSET_IB_DRBL_1_P0(__n) \
- (HQM_QSET0_IB_DRBL_1_P0 + (__n) * \
- (HQM_QSET1_IB_DRBL_1_P0 - HQM_QSET0_IB_DRBL_1_P0))
-#define HQM_QSET_IB_DRBL_2_P0(__n) \
- (HQM_QSET0_IB_DRBL_2_P0 + (__n) * \
- (HQM_QSET1_IB_DRBL_2_P0 - HQM_QSET0_IB_DRBL_2_P0))
-#define HQM_QSET_RXQ_DRBL_P1(__n) \
- (HQM_QSET0_RXQ_DRBL_P1 + (__n) * \
- (HQM_QSET1_RXQ_DRBL_P1 - HQM_QSET0_RXQ_DRBL_P1))
-#define HQM_QSET_TXQ_DRBL_P1(__n) \
- (HQM_QSET0_TXQ_DRBL_P1 + (__n) * \
- (HQM_QSET1_TXQ_DRBL_P1 - HQM_QSET0_TXQ_DRBL_P1))
-#define HQM_QSET_IB_DRBL_1_P1(__n) \
- (HQM_QSET0_IB_DRBL_1_P1 + (__n) * \
- (HQM_QSET1_IB_DRBL_1_P1 - HQM_QSET0_IB_DRBL_1_P1))
-#define HQM_QSET_IB_DRBL_2_P1(__n) \
- (HQM_QSET0_IB_DRBL_2_P1 + (__n) * \
- (HQM_QSET1_IB_DRBL_2_P1 - HQM_QSET0_IB_DRBL_2_P1))
-
-#define CPE_Q_NUM(__fn, __q) (((__fn) << 2) + (__q))
-#define RME_Q_NUM(__fn, __q) (((__fn) << 2) + (__q))
-#define CPE_Q_MASK(__q) ((__q) & 0x3)
-#define RME_Q_MASK(__q) ((__q) & 0x3)
-
-/*
- * PCI MSI-X vector defines
- */
-enum {
- BFA_MSIX_CPE_Q0 = 0,
- BFA_MSIX_CPE_Q1 = 1,
- BFA_MSIX_CPE_Q2 = 2,
- BFA_MSIX_CPE_Q3 = 3,
- BFA_MSIX_RME_Q0 = 4,
- BFA_MSIX_RME_Q1 = 5,
- BFA_MSIX_RME_Q2 = 6,
- BFA_MSIX_RME_Q3 = 7,
- BFA_MSIX_LPU_ERR = 8,
- BFA_MSIX_CT_MAX = 9,
-};
-
-/*
- * And corresponding host interrupt status bit field defines
- */
-#define __HFN_INT_CPE_Q0 0x00000001U
-#define __HFN_INT_CPE_Q1 0x00000002U
-#define __HFN_INT_CPE_Q2 0x00000004U
-#define __HFN_INT_CPE_Q3 0x00000008U
-#define __HFN_INT_CPE_Q4 0x00000010U
-#define __HFN_INT_CPE_Q5 0x00000020U
-#define __HFN_INT_CPE_Q6 0x00000040U
-#define __HFN_INT_CPE_Q7 0x00000080U
-#define __HFN_INT_RME_Q0 0x00000100U
-#define __HFN_INT_RME_Q1 0x00000200U
-#define __HFN_INT_RME_Q2 0x00000400U
-#define __HFN_INT_RME_Q3 0x00000800U
-#define __HFN_INT_RME_Q4 0x00001000U
-#define __HFN_INT_RME_Q5 0x00002000U
-#define __HFN_INT_RME_Q6 0x00004000U
-#define __HFN_INT_RME_Q7 0x00008000U
-#define __HFN_INT_ERR_EMC 0x00010000U
-#define __HFN_INT_ERR_LPU0 0x00020000U
-#define __HFN_INT_ERR_LPU1 0x00040000U
-#define __HFN_INT_ERR_PSS 0x00080000U
-#define __HFN_INT_MBOX_LPU0 0x00100000U
-#define __HFN_INT_MBOX_LPU1 0x00200000U
-#define __HFN_INT_MBOX1_LPU0 0x00400000U
-#define __HFN_INT_MBOX1_LPU1 0x00800000U
-#define __HFN_INT_LL_HALT 0x01000000U
-#define __HFN_INT_CPE_MASK 0x000000ffU
-#define __HFN_INT_RME_MASK 0x0000ff00U
-
-/*
- * catapult memory map.
- */
-#define LL_PGN_HQM0 0x0096
-#define LL_PGN_HQM1 0x0097
-#define PSS_SMEM_PAGE_START 0x8000
-#define PSS_SMEM_PGNUM(_pg0, _ma) ((_pg0) + ((_ma) >> 15))
-#define PSS_SMEM_PGOFF(_ma) ((_ma) & 0x7fff)
-
-/*
- * End of catapult memory map
- */
-
-#endif /* __BFI_CTREG_H__ */
diff --git a/drivers/net/bna/bfi_ll.h b/drivers/net/bna/bfi_ll.h
deleted file mode 100644
index bee4d054066a..000000000000
--- a/drivers/net/bna/bfi_ll.h
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * Linux network driver for Brocade Converged Network Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
- * All rights reserved
- * www.brocade.com
- */
-#ifndef __BFI_LL_H__
-#define __BFI_LL_H__
-
-#include "bfi.h"
-
-#pragma pack(1)
-
-/**
- * @brief
- * "enums" for all LL mailbox messages other than IOC
- */
-enum {
- BFI_LL_H2I_MAC_UCAST_SET_REQ = 1,
- BFI_LL_H2I_MAC_UCAST_ADD_REQ = 2,
- BFI_LL_H2I_MAC_UCAST_DEL_REQ = 3,
-
- BFI_LL_H2I_MAC_MCAST_ADD_REQ = 4,
- BFI_LL_H2I_MAC_MCAST_DEL_REQ = 5,
- BFI_LL_H2I_MAC_MCAST_FILTER_REQ = 6,
- BFI_LL_H2I_MAC_MCAST_DEL_ALL_REQ = 7,
-
- BFI_LL_H2I_PORT_ADMIN_REQ = 8,
- BFI_LL_H2I_STATS_GET_REQ = 9,
- BFI_LL_H2I_STATS_CLEAR_REQ = 10,
-
- BFI_LL_H2I_RXF_PROMISCUOUS_SET_REQ = 11,
- BFI_LL_H2I_RXF_DEFAULT_SET_REQ = 12,
-
- BFI_LL_H2I_TXQ_STOP_REQ = 13,
- BFI_LL_H2I_RXQ_STOP_REQ = 14,
-
- BFI_LL_H2I_DIAG_LOOPBACK_REQ = 15,
-
- BFI_LL_H2I_SET_PAUSE_REQ = 16,
- BFI_LL_H2I_MTU_INFO_REQ = 17,
-
- BFI_LL_H2I_RX_REQ = 18,
-} ;
-
-enum {
- BFI_LL_I2H_MAC_UCAST_SET_RSP = BFA_I2HM(1),
- BFI_LL_I2H_MAC_UCAST_ADD_RSP = BFA_I2HM(2),
- BFI_LL_I2H_MAC_UCAST_DEL_RSP = BFA_I2HM(3),
-
- BFI_LL_I2H_MAC_MCAST_ADD_RSP = BFA_I2HM(4),
- BFI_LL_I2H_MAC_MCAST_DEL_RSP = BFA_I2HM(5),
- BFI_LL_I2H_MAC_MCAST_FILTER_RSP = BFA_I2HM(6),
- BFI_LL_I2H_MAC_MCAST_DEL_ALL_RSP = BFA_I2HM(7),
-
- BFI_LL_I2H_PORT_ADMIN_RSP = BFA_I2HM(8),
- BFI_LL_I2H_STATS_GET_RSP = BFA_I2HM(9),
- BFI_LL_I2H_STATS_CLEAR_RSP = BFA_I2HM(10),
-
- BFI_LL_I2H_RXF_PROMISCUOUS_SET_RSP = BFA_I2HM(11),
- BFI_LL_I2H_RXF_DEFAULT_SET_RSP = BFA_I2HM(12),
-
- BFI_LL_I2H_TXQ_STOP_RSP = BFA_I2HM(13),
- BFI_LL_I2H_RXQ_STOP_RSP = BFA_I2HM(14),
-
- BFI_LL_I2H_DIAG_LOOPBACK_RSP = BFA_I2HM(15),
-
- BFI_LL_I2H_SET_PAUSE_RSP = BFA_I2HM(16),
-
- BFI_LL_I2H_MTU_INFO_RSP = BFA_I2HM(17),
- BFI_LL_I2H_RX_RSP = BFA_I2HM(18),
-
- BFI_LL_I2H_LINK_DOWN_AEN = BFA_I2HM(19),
- BFI_LL_I2H_LINK_UP_AEN = BFA_I2HM(20),
-
- BFI_LL_I2H_PORT_ENABLE_AEN = BFA_I2HM(21),
- BFI_LL_I2H_PORT_DISABLE_AEN = BFA_I2HM(22),
-} ;
-
-/**
- * @brief bfi_ll_mac_addr_req is used by:
- * BFI_LL_H2I_MAC_UCAST_SET_REQ
- * BFI_LL_H2I_MAC_UCAST_ADD_REQ
- * BFI_LL_H2I_MAC_UCAST_DEL_REQ
- * BFI_LL_H2I_MAC_MCAST_ADD_REQ
- * BFI_LL_H2I_MAC_MCAST_DEL_REQ
- */
-struct bfi_ll_mac_addr_req {
- struct bfi_mhdr mh; /*!< common msg header */
- u8 rxf_id;
- u8 rsvd1[3];
- mac_t mac_addr;
- u8 rsvd2[2];
-};
-
-/**
- * @brief bfi_ll_mcast_filter_req is used by:
- * BFI_LL_H2I_MAC_MCAST_FILTER_REQ
- */
-struct bfi_ll_mcast_filter_req {
- struct bfi_mhdr mh; /*!< common msg header */
- u8 rxf_id;
- u8 enable;
- u8 rsvd[2];
-};
-
-/**
- * @brief bfi_ll_mcast_del_all is used by:
- * BFI_LL_H2I_MAC_MCAST_DEL_ALL_REQ
- */
-struct bfi_ll_mcast_del_all_req {
- struct bfi_mhdr mh; /*!< common msg header */
- u8 rxf_id;
- u8 rsvd[3];
-};
-
-/**
- * @brief bfi_ll_q_stop_req is used by:
- * BFI_LL_H2I_TXQ_STOP_REQ
- * BFI_LL_H2I_RXQ_STOP_REQ
- */
-struct bfi_ll_q_stop_req {
- struct bfi_mhdr mh; /*!< common msg header */
- u32 q_id_mask[2]; /* !< bit-mask for queue ids */
-};
-
-/**
- * @brief bfi_ll_stats_req is used by:
- * BFI_LL_I2H_STATS_GET_REQ
- * BFI_LL_I2H_STATS_CLEAR_REQ
- */
-struct bfi_ll_stats_req {
- struct bfi_mhdr mh; /*!< common msg header */
- u16 stats_mask; /* !< bit-mask for non-function statistics */
- u8 rsvd[2];
- u32 rxf_id_mask[2]; /* !< bit-mask for RxF Statistics */
- u32 txf_id_mask[2]; /* !< bit-mask for TxF Statistics */
- union bfi_addr_u host_buffer; /* !< where statistics are returned */
-};
-
-/**
- * @brief defines for "stats_mask" above.
- */
-#define BFI_LL_STATS_MAC (1 << 0) /* !< MAC Statistics */
-#define BFI_LL_STATS_BPC (1 << 1) /* !< Pause Stats from BPC */
-#define BFI_LL_STATS_RAD (1 << 2) /* !< Rx Admission Statistics */
-#define BFI_LL_STATS_RX_FC (1 << 3) /* !< Rx FC Stats from RxA */
-#define BFI_LL_STATS_TX_FC (1 << 4) /* !< Tx FC Stats from TxA */
-
-#define BFI_LL_STATS_ALL 0x1f
-
-/**
- * @brief bfi_ll_port_admin_req
- */
-struct bfi_ll_port_admin_req {
- struct bfi_mhdr mh; /*!< common msg header */
- u8 up;
- u8 rsvd[3];
-};
-
-/**
- * @brief bfi_ll_rxf_req is used by:
- * BFI_LL_H2I_RXF_PROMISCUOUS_SET_REQ
- * BFI_LL_H2I_RXF_DEFAULT_SET_REQ
- */
-struct bfi_ll_rxf_req {
- struct bfi_mhdr mh; /*!< common msg header */
- u8 rxf_id;
- u8 enable;
- u8 rsvd[2];
-};
-
-/**
- * @brief bfi_ll_rxf_multi_req is used by:
- * BFI_LL_H2I_RX_REQ
- */
-struct bfi_ll_rxf_multi_req {
- struct bfi_mhdr mh; /*!< common msg header */
- u32 rxf_id_mask[2];
- u8 enable;
- u8 rsvd[3];
-};
-
-/**
- * @brief enum for Loopback opmodes
- */
-enum {
- BFI_LL_DIAG_LB_OPMODE_EXT = 0,
- BFI_LL_DIAG_LB_OPMODE_CBL = 1,
-};
-
-/**
- * @brief bfi_ll_set_pause_req is used by:
- * BFI_LL_H2I_SET_PAUSE_REQ
- */
-struct bfi_ll_set_pause_req {
- struct bfi_mhdr mh;
- u8 tx_pause; /* 1 = enable, 0 = disable */
- u8 rx_pause; /* 1 = enable, 0 = disable */
- u8 rsvd[2];
-};
-
-/**
- * @brief bfi_ll_mtu_info_req is used by:
- * BFI_LL_H2I_MTU_INFO_REQ
- */
-struct bfi_ll_mtu_info_req {
- struct bfi_mhdr mh;
- u16 mtu;
- u8 rsvd[2];
-};
-
-/**
- * @brief
- * Response header format used by all responses
- * For both responses and asynchronous notifications
- */
-struct bfi_ll_rsp {
- struct bfi_mhdr mh; /*!< common msg header */
- u8 error;
- u8 rsvd[3];
-};
-
-/**
- * @brief bfi_ll_cee_aen is used by:
- * BFI_LL_I2H_LINK_DOWN_AEN
- * BFI_LL_I2H_LINK_UP_AEN
- */
-struct bfi_ll_aen {
- struct bfi_mhdr mh; /*!< common msg header */
- u32 reason;
- u8 cee_linkup;
- u8 prio_map; /*!< LL priority bit-map */
- u8 rsvd[2];
-};
-
-/**
- * @brief
- * The following error codes can be returned
- * by the mbox commands
- */
-enum {
- BFI_LL_CMD_OK = 0,
- BFI_LL_CMD_FAIL = 1,
- BFI_LL_CMD_DUP_ENTRY = 2, /* !< Duplicate entry in CAM */
- BFI_LL_CMD_CAM_FULL = 3, /* !< CAM is full */
- BFI_LL_CMD_NOT_OWNER = 4, /* !< Not permitted, b'cos not owner */
- BFI_LL_CMD_NOT_EXEC = 5, /* !< Was not sent to f/w at all */
- BFI_LL_CMD_WAITING = 6, /* !< Waiting for completion (VMware) */
- BFI_LL_CMD_PORT_DISABLED = 7, /* !< port in disabled state */
-} ;
-
-/* Statistics */
-#define BFI_LL_TXF_ID_MAX 64
-#define BFI_LL_RXF_ID_MAX 64
-
-/* TxF Frame Statistics */
-struct bfi_ll_stats_txf {
- u64 ucast_octets;
- u64 ucast;
- u64 ucast_vlan;
-
- u64 mcast_octets;
- u64 mcast;
- u64 mcast_vlan;
-
- u64 bcast_octets;
- u64 bcast;
- u64 bcast_vlan;
-
- u64 errors;
- u64 filter_vlan; /* frames filtered due to VLAN */
- u64 filter_mac_sa; /* frames filtered due to SA check */
-};
-
-/* RxF Frame Statistics */
-struct bfi_ll_stats_rxf {
- u64 ucast_octets;
- u64 ucast;
- u64 ucast_vlan;
-
- u64 mcast_octets;
- u64 mcast;
- u64 mcast_vlan;
-
- u64 bcast_octets;
- u64 bcast;
- u64 bcast_vlan;
- u64 frame_drops;
-};
-
-/* FC Tx Frame Statistics */
-struct bfi_ll_stats_fc_tx {
- u64 txf_ucast_octets;
- u64 txf_ucast;
- u64 txf_ucast_vlan;
-
- u64 txf_mcast_octets;
- u64 txf_mcast;
- u64 txf_mcast_vlan;
-
- u64 txf_bcast_octets;
- u64 txf_bcast;
- u64 txf_bcast_vlan;
-
- u64 txf_parity_errors;
- u64 txf_timeout;
- u64 txf_fid_parity_errors;
-};
-
-/* FC Rx Frame Statistics */
-struct bfi_ll_stats_fc_rx {
- u64 rxf_ucast_octets;
- u64 rxf_ucast;
- u64 rxf_ucast_vlan;
-
- u64 rxf_mcast_octets;
- u64 rxf_mcast;
- u64 rxf_mcast_vlan;
-
- u64 rxf_bcast_octets;
- u64 rxf_bcast;
- u64 rxf_bcast_vlan;
-};
-
-/* RAD Frame Statistics */
-struct bfi_ll_stats_rad {
- u64 rx_frames;
- u64 rx_octets;
- u64 rx_vlan_frames;
-
- u64 rx_ucast;
- u64 rx_ucast_octets;
- u64 rx_ucast_vlan;
-
- u64 rx_mcast;
- u64 rx_mcast_octets;
- u64 rx_mcast_vlan;
-
- u64 rx_bcast;
- u64 rx_bcast_octets;
- u64 rx_bcast_vlan;
-
- u64 rx_drops;
-};
-
-/* BPC Tx Registers */
-struct bfi_ll_stats_bpc {
- /* transmit stats */
- u64 tx_pause[8];
- u64 tx_zero_pause[8]; /*!< Pause cancellation */
- /*!<Pause initiation rather than retention */
- u64 tx_first_pause[8];
-
- /* receive stats */
- u64 rx_pause[8];
- u64 rx_zero_pause[8]; /*!< Pause cancellation */
- /*!<Pause initiation rather than retention */
- u64 rx_first_pause[8];
-};
-
-/* MAC Rx Statistics */
-struct bfi_ll_stats_mac {
- u64 frame_64; /* both rx and tx counter */
- u64 frame_65_127; /* both rx and tx counter */
- u64 frame_128_255; /* both rx and tx counter */
- u64 frame_256_511; /* both rx and tx counter */
- u64 frame_512_1023; /* both rx and tx counter */
- u64 frame_1024_1518; /* both rx and tx counter */
- u64 frame_1519_1522; /* both rx and tx counter */
-
- /* receive stats */
- u64 rx_bytes;
- u64 rx_packets;
- u64 rx_fcs_error;
- u64 rx_multicast;
- u64 rx_broadcast;
- u64 rx_control_frames;
- u64 rx_pause;
- u64 rx_unknown_opcode;
- u64 rx_alignment_error;
- u64 rx_frame_length_error;
- u64 rx_code_error;
- u64 rx_carrier_sense_error;
- u64 rx_undersize;
- u64 rx_oversize;
- u64 rx_fragments;
- u64 rx_jabber;
- u64 rx_drop;
-
- /* transmit stats */
- u64 tx_bytes;
- u64 tx_packets;
- u64 tx_multicast;
- u64 tx_broadcast;
- u64 tx_pause;
- u64 tx_deferral;
- u64 tx_excessive_deferral;
- u64 tx_single_collision;
- u64 tx_muliple_collision;
- u64 tx_late_collision;
- u64 tx_excessive_collision;
- u64 tx_total_collision;
- u64 tx_pause_honored;
- u64 tx_drop;
- u64 tx_jabber;
- u64 tx_fcs_error;
- u64 tx_control_frame;
- u64 tx_oversize;
- u64 tx_undersize;
- u64 tx_fragments;
-};
-
-/* Complete statistics */
-struct bfi_ll_stats {
- struct bfi_ll_stats_mac mac_stats;
- struct bfi_ll_stats_bpc bpc_stats;
- struct bfi_ll_stats_rad rad_stats;
- struct bfi_ll_stats_fc_rx fc_rx_stats;
- struct bfi_ll_stats_fc_tx fc_tx_stats;
- struct bfi_ll_stats_rxf rxf_stats[BFI_LL_RXF_ID_MAX];
- struct bfi_ll_stats_txf txf_stats[BFI_LL_TXF_ID_MAX];
-};
-
-#pragma pack()
-
-#endif /* __BFI_LL_H__ */
diff --git a/drivers/net/bna/bna_ctrl.c b/drivers/net/bna/bna_ctrl.c
deleted file mode 100644
index 53b14169e363..000000000000
--- a/drivers/net/bna/bna_ctrl.c
+++ /dev/null
@@ -1,3077 +0,0 @@
-/*
- * Linux network driver for Brocade Converged Network Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
- * All rights reserved
- * www.brocade.com
- */
-#include "bna.h"
-#include "bfa_sm.h"
-#include "bfa_wc.h"
-
-static void bna_device_cb_port_stopped(void *arg, enum bna_cb_status status);
-
-static void
-bna_port_cb_link_up(struct bna_port *port, struct bfi_ll_aen *aen,
- int status)
-{
- int i;
- u8 prio_map;
-
- port->llport.link_status = BNA_LINK_UP;
- if (aen->cee_linkup)
- port->llport.link_status = BNA_CEE_UP;
-
- /* Compute the priority */
- prio_map = aen->prio_map;
- if (prio_map) {
- for (i = 0; i < 8; i++) {
- if ((prio_map >> i) & 0x1)
- break;
- }
- port->priority = i;
- } else
- port->priority = 0;
-
- /* Dispatch events */
- bna_tx_mod_cee_link_status(&port->bna->tx_mod, aen->cee_linkup);
- bna_tx_mod_prio_changed(&port->bna->tx_mod, port->priority);
- port->link_cbfn(port->bna->bnad, port->llport.link_status);
-}
-
-static void
-bna_port_cb_link_down(struct bna_port *port, int status)
-{
- port->llport.link_status = BNA_LINK_DOWN;
-
- /* Dispatch events */
- bna_tx_mod_cee_link_status(&port->bna->tx_mod, BNA_LINK_DOWN);
- port->link_cbfn(port->bna->bnad, BNA_LINK_DOWN);
-}
-
-static inline int
-llport_can_be_up(struct bna_llport *llport)
-{
- int ready = 0;
- if (llport->type == BNA_PORT_T_REGULAR)
- ready = ((llport->flags & BNA_LLPORT_F_ADMIN_UP) &&
- (llport->flags & BNA_LLPORT_F_RX_STARTED) &&
- (llport->flags & BNA_LLPORT_F_PORT_ENABLED));
- else
- ready = ((llport->flags & BNA_LLPORT_F_ADMIN_UP) &&
- (llport->flags & BNA_LLPORT_F_RX_STARTED) &&
- !(llport->flags & BNA_LLPORT_F_PORT_ENABLED));
- return ready;
-}
-
-#define llport_is_up llport_can_be_up
-
-enum bna_llport_event {
- LLPORT_E_START = 1,
- LLPORT_E_STOP = 2,
- LLPORT_E_FAIL = 3,
- LLPORT_E_UP = 4,
- LLPORT_E_DOWN = 5,
- LLPORT_E_FWRESP_UP_OK = 6,
- LLPORT_E_FWRESP_UP_FAIL = 7,
- LLPORT_E_FWRESP_DOWN = 8
-};
-
-static void
-bna_llport_cb_port_enabled(struct bna_llport *llport)
-{
- llport->flags |= BNA_LLPORT_F_PORT_ENABLED;
-
- if (llport_can_be_up(llport))
- bfa_fsm_send_event(llport, LLPORT_E_UP);
-}
-
-static void
-bna_llport_cb_port_disabled(struct bna_llport *llport)
-{
- int llport_up = llport_is_up(llport);
-
- llport->flags &= ~BNA_LLPORT_F_PORT_ENABLED;
-
- if (llport_up)
- bfa_fsm_send_event(llport, LLPORT_E_DOWN);
-}
-
-/**
- * MBOX
- */
-static int
-bna_is_aen(u8 msg_id)
-{
- switch (msg_id) {
- case BFI_LL_I2H_LINK_DOWN_AEN:
- case BFI_LL_I2H_LINK_UP_AEN:
- case BFI_LL_I2H_PORT_ENABLE_AEN:
- case BFI_LL_I2H_PORT_DISABLE_AEN:
- return 1;
-
- default:
- return 0;
- }
-}
-
-static void
-bna_mbox_aen_callback(struct bna *bna, struct bfi_mbmsg *msg)
-{
- struct bfi_ll_aen *aen = (struct bfi_ll_aen *)(msg);
-
- switch (aen->mh.msg_id) {
- case BFI_LL_I2H_LINK_UP_AEN:
- bna_port_cb_link_up(&bna->port, aen, aen->reason);
- break;
- case BFI_LL_I2H_LINK_DOWN_AEN:
- bna_port_cb_link_down(&bna->port, aen->reason);
- break;
- case BFI_LL_I2H_PORT_ENABLE_AEN:
- bna_llport_cb_port_enabled(&bna->port.llport);
- break;
- case BFI_LL_I2H_PORT_DISABLE_AEN:
- bna_llport_cb_port_disabled(&bna->port.llport);
- break;
- default:
- break;
- }
-}
-
-static void
-bna_ll_isr(void *llarg, struct bfi_mbmsg *msg)
-{
- struct bna *bna = (struct bna *)(llarg);
- struct bfi_ll_rsp *mb_rsp = (struct bfi_ll_rsp *)(msg);
- struct bfi_mhdr *cmd_h, *rsp_h;
- struct bna_mbox_qe *mb_qe = NULL;
- int to_post = 0;
- u8 aen = 0;
- char message[BNA_MESSAGE_SIZE];
-
- aen = bna_is_aen(mb_rsp->mh.msg_id);
-
- if (!aen) {
- mb_qe = bfa_q_first(&bna->mbox_mod.posted_q);
- cmd_h = (struct bfi_mhdr *)(&mb_qe->cmd.msg[0]);
- rsp_h = (struct bfi_mhdr *)(&mb_rsp->mh);
-
- if ((BFA_I2HM(cmd_h->msg_id) == rsp_h->msg_id) &&
- (cmd_h->mtag.i2htok == rsp_h->mtag.i2htok)) {
- /* Remove the request from posted_q, update state */
- list_del(&mb_qe->qe);
- bna->mbox_mod.msg_pending--;
- if (list_empty(&bna->mbox_mod.posted_q))
- bna->mbox_mod.state = BNA_MBOX_FREE;
- else
- to_post = 1;
-
- /* Dispatch the cbfn */
- if (mb_qe->cbfn)
- mb_qe->cbfn(mb_qe->cbarg, mb_rsp->error);
-
- /* Post the next entry, if needed */
- if (to_post) {
- mb_qe = bfa_q_first(&bna->mbox_mod.posted_q);
- bfa_nw_ioc_mbox_queue(&bna->device.ioc,
- &mb_qe->cmd);
- }
- } else {
- snprintf(message, BNA_MESSAGE_SIZE,
- "No matching rsp for [%d:%d:%d]\n",
- mb_rsp->mh.msg_class, mb_rsp->mh.msg_id,
- mb_rsp->mh.mtag.i2htok);
- pr_info("%s", message);
- }
-
- } else
- bna_mbox_aen_callback(bna, msg);
-}
-
-static void
-bna_err_handler(struct bna *bna, u32 intr_status)
-{
- u32 init_halt;
-
- if (intr_status & __HALT_STATUS_BITS) {
- init_halt = readl(bna->device.ioc.ioc_regs.ll_halt);
- init_halt &= ~__FW_INIT_HALT_P;
- writel(init_halt, bna->device.ioc.ioc_regs.ll_halt);
- }
-
- bfa_nw_ioc_error_isr(&bna->device.ioc);
-}
-
-void
-bna_mbox_handler(struct bna *bna, u32 intr_status)
-{
- if (BNA_IS_ERR_INTR(intr_status)) {
- bna_err_handler(bna, intr_status);
- return;
- }
- if (BNA_IS_MBOX_INTR(intr_status))
- bfa_nw_ioc_mbox_isr(&bna->device.ioc);
-}
-
-void
-bna_mbox_send(struct bna *bna, struct bna_mbox_qe *mbox_qe)
-{
- struct bfi_mhdr *mh;
-
- mh = (struct bfi_mhdr *)(&mbox_qe->cmd.msg[0]);
-
- mh->mtag.i2htok = htons(bna->mbox_mod.msg_ctr);
- bna->mbox_mod.msg_ctr++;
- bna->mbox_mod.msg_pending++;
- if (bna->mbox_mod.state == BNA_MBOX_FREE) {
- list_add_tail(&mbox_qe->qe, &bna->mbox_mod.posted_q);
- bfa_nw_ioc_mbox_queue(&bna->device.ioc, &mbox_qe->cmd);
- bna->mbox_mod.state = BNA_MBOX_POSTED;
- } else {
- list_add_tail(&mbox_qe->qe, &bna->mbox_mod.posted_q);
- }
-}
-
-static void
-bna_mbox_flush_q(struct bna *bna, struct list_head *q)
-{
- struct bna_mbox_qe *mb_qe = NULL;
- struct list_head *mb_q;
- void (*cbfn)(void *arg, int status);
- void *cbarg;
-
- mb_q = &bna->mbox_mod.posted_q;
-
- while (!list_empty(mb_q)) {
- bfa_q_deq(mb_q, &mb_qe);
- cbfn = mb_qe->cbfn;
- cbarg = mb_qe->cbarg;
- bfa_q_qe_init(mb_qe);
- bna->mbox_mod.msg_pending--;
-
- if (cbfn)
- cbfn(cbarg, BNA_CB_NOT_EXEC);
- }
-
- bna->mbox_mod.state = BNA_MBOX_FREE;
-}
-
-static void
-bna_mbox_mod_start(struct bna_mbox_mod *mbox_mod)
-{
-}
-
-static void
-bna_mbox_mod_stop(struct bna_mbox_mod *mbox_mod)
-{
- bna_mbox_flush_q(mbox_mod->bna, &mbox_mod->posted_q);
-}
-
-static void
-bna_mbox_mod_init(struct bna_mbox_mod *mbox_mod, struct bna *bna)
-{
- bfa_nw_ioc_mbox_regisr(&bna->device.ioc, BFI_MC_LL, bna_ll_isr, bna);
- mbox_mod->state = BNA_MBOX_FREE;
- mbox_mod->msg_ctr = mbox_mod->msg_pending = 0;
- INIT_LIST_HEAD(&mbox_mod->posted_q);
- mbox_mod->bna = bna;
-}
-
-static void
-bna_mbox_mod_uninit(struct bna_mbox_mod *mbox_mod)
-{
- mbox_mod->bna = NULL;
-}
-
-/**
- * LLPORT
- */
-#define call_llport_stop_cbfn(llport, status)\
-do {\
- if ((llport)->stop_cbfn)\
- (llport)->stop_cbfn(&(llport)->bna->port, status);\
- (llport)->stop_cbfn = NULL;\
-} while (0)
-
-static void bna_fw_llport_up(struct bna_llport *llport);
-static void bna_fw_cb_llport_up(void *arg, int status);
-static void bna_fw_llport_down(struct bna_llport *llport);
-static void bna_fw_cb_llport_down(void *arg, int status);
-static void bna_llport_start(struct bna_llport *llport);
-static void bna_llport_stop(struct bna_llport *llport);
-static void bna_llport_fail(struct bna_llport *llport);
-
-enum bna_llport_state {
- BNA_LLPORT_STOPPED = 1,
- BNA_LLPORT_DOWN = 2,
- BNA_LLPORT_UP_RESP_WAIT = 3,
- BNA_LLPORT_DOWN_RESP_WAIT = 4,
- BNA_LLPORT_UP = 5,
- BNA_LLPORT_LAST_RESP_WAIT = 6
-};
-
-bfa_fsm_state_decl(bna_llport, stopped, struct bna_llport,
- enum bna_llport_event);
-bfa_fsm_state_decl(bna_llport, down, struct bna_llport,
- enum bna_llport_event);
-bfa_fsm_state_decl(bna_llport, up_resp_wait, struct bna_llport,
- enum bna_llport_event);
-bfa_fsm_state_decl(bna_llport, down_resp_wait, struct bna_llport,
- enum bna_llport_event);
-bfa_fsm_state_decl(bna_llport, up, struct bna_llport,
- enum bna_llport_event);
-bfa_fsm_state_decl(bna_llport, last_resp_wait, struct bna_llport,
- enum bna_llport_event);
-
-static struct bfa_sm_table llport_sm_table[] = {
- {BFA_SM(bna_llport_sm_stopped), BNA_LLPORT_STOPPED},
- {BFA_SM(bna_llport_sm_down), BNA_LLPORT_DOWN},
- {BFA_SM(bna_llport_sm_up_resp_wait), BNA_LLPORT_UP_RESP_WAIT},
- {BFA_SM(bna_llport_sm_down_resp_wait), BNA_LLPORT_DOWN_RESP_WAIT},
- {BFA_SM(bna_llport_sm_up), BNA_LLPORT_UP},
- {BFA_SM(bna_llport_sm_last_resp_wait), BNA_LLPORT_LAST_RESP_WAIT}
-};
-
-static void
-bna_llport_sm_stopped_entry(struct bna_llport *llport)
-{
- llport->bna->port.link_cbfn((llport)->bna->bnad, BNA_LINK_DOWN);
- call_llport_stop_cbfn(llport, BNA_CB_SUCCESS);
-}
-
-static void
-bna_llport_sm_stopped(struct bna_llport *llport,
- enum bna_llport_event event)
-{
- switch (event) {
- case LLPORT_E_START:
- bfa_fsm_set_state(llport, bna_llport_sm_down);
- break;
-
- case LLPORT_E_STOP:
- call_llport_stop_cbfn(llport, BNA_CB_SUCCESS);
- break;
-
- case LLPORT_E_FAIL:
- break;
-
- case LLPORT_E_DOWN:
- /* This event is received due to Rx objects failing */
- /* No-op */
- break;
-
- case LLPORT_E_FWRESP_UP_OK:
- case LLPORT_E_FWRESP_DOWN:
- /**
- * These events are received due to flushing of mbox when
- * device fails
- */
- /* No-op */
- break;
-
- default:
- bfa_sm_fault(llport->bna, event);
- }
-}
-
-static void
-bna_llport_sm_down_entry(struct bna_llport *llport)
-{
- bnad_cb_port_link_status((llport)->bna->bnad, BNA_LINK_DOWN);
-}
-
-static void
-bna_llport_sm_down(struct bna_llport *llport,
- enum bna_llport_event event)
-{
- switch (event) {
- case LLPORT_E_STOP:
- bfa_fsm_set_state(llport, bna_llport_sm_stopped);
- break;
-
- case LLPORT_E_FAIL:
- bfa_fsm_set_state(llport, bna_llport_sm_stopped);
- break;
-
- case LLPORT_E_UP:
- bfa_fsm_set_state(llport, bna_llport_sm_up_resp_wait);
- bna_fw_llport_up(llport);
- break;
-
- default:
- bfa_sm_fault(llport->bna, event);
- }
-}
-
-static void
-bna_llport_sm_up_resp_wait_entry(struct bna_llport *llport)
-{
- BUG_ON(!llport_can_be_up(llport));
- /**
- * NOTE: Do not call bna_fw_llport_up() here. That will over step
- * mbox due to down_resp_wait -> up_resp_wait transition on event
- * LLPORT_E_UP
- */
-}
-
-static void
-bna_llport_sm_up_resp_wait(struct bna_llport *llport,
- enum bna_llport_event event)
-{
- switch (event) {
- case LLPORT_E_STOP:
- bfa_fsm_set_state(llport, bna_llport_sm_last_resp_wait);
- break;
-
- case LLPORT_E_FAIL:
- bfa_fsm_set_state(llport, bna_llport_sm_stopped);
- break;
-
- case LLPORT_E_DOWN:
- bfa_fsm_set_state(llport, bna_llport_sm_down_resp_wait);
- break;
-
- case LLPORT_E_FWRESP_UP_OK:
- bfa_fsm_set_state(llport, bna_llport_sm_up);
- break;
-
- case LLPORT_E_FWRESP_UP_FAIL:
- bfa_fsm_set_state(llport, bna_llport_sm_down);
- break;
-
- case LLPORT_E_FWRESP_DOWN:
- /* down_resp_wait -> up_resp_wait transition on LLPORT_E_UP */
- bna_fw_llport_up(llport);
- break;
-
- default:
- bfa_sm_fault(llport->bna, event);
- }
-}
-
-static void
-bna_llport_sm_down_resp_wait_entry(struct bna_llport *llport)
-{
- /**
- * NOTE: Do not call bna_fw_llport_down() here. That will over step
- * mbox due to up_resp_wait -> down_resp_wait transition on event
- * LLPORT_E_DOWN
- */
-}
-
-static void
-bna_llport_sm_down_resp_wait(struct bna_llport *llport,
- enum bna_llport_event event)
-{
- switch (event) {
- case LLPORT_E_STOP:
- bfa_fsm_set_state(llport, bna_llport_sm_last_resp_wait);
- break;
-
- case LLPORT_E_FAIL:
- bfa_fsm_set_state(llport, bna_llport_sm_stopped);
- break;
-
- case LLPORT_E_UP:
- bfa_fsm_set_state(llport, bna_llport_sm_up_resp_wait);
- break;
-
- case LLPORT_E_FWRESP_UP_OK:
- /* up_resp_wait->down_resp_wait transition on LLPORT_E_DOWN */
- bna_fw_llport_down(llport);
- break;
-
- case LLPORT_E_FWRESP_UP_FAIL:
- case LLPORT_E_FWRESP_DOWN:
- bfa_fsm_set_state(llport, bna_llport_sm_down);
- break;
-
- default:
- bfa_sm_fault(llport->bna, event);
- }
-}
-
-static void
-bna_llport_sm_up_entry(struct bna_llport *llport)
-{
-}
-
-static void
-bna_llport_sm_up(struct bna_llport *llport,
- enum bna_llport_event event)
-{
- switch (event) {
- case LLPORT_E_STOP:
- bfa_fsm_set_state(llport, bna_llport_sm_last_resp_wait);
- bna_fw_llport_down(llport);
- break;
-
- case LLPORT_E_FAIL:
- bfa_fsm_set_state(llport, bna_llport_sm_stopped);
- break;
-
- case LLPORT_E_DOWN:
- bfa_fsm_set_state(llport, bna_llport_sm_down_resp_wait);
- bna_fw_llport_down(llport);
- break;
-
- default:
- bfa_sm_fault(llport->bna, event);
- }
-}
-
-static void
-bna_llport_sm_last_resp_wait_entry(struct bna_llport *llport)
-{
-}
-
-static void
-bna_llport_sm_last_resp_wait(struct bna_llport *llport,
- enum bna_llport_event event)
-{
- switch (event) {
- case LLPORT_E_FAIL:
- bfa_fsm_set_state(llport, bna_llport_sm_stopped);
- break;
-
- case LLPORT_E_DOWN:
- /**
- * This event is received due to Rx objects stopping in
- * parallel to llport
- */
- /* No-op */
- break;
-
- case LLPORT_E_FWRESP_UP_OK:
- /* up_resp_wait->last_resp_wait transition on LLPORT_T_STOP */
- bna_fw_llport_down(llport);
- break;
-
- case LLPORT_E_FWRESP_UP_FAIL:
- case LLPORT_E_FWRESP_DOWN:
- bfa_fsm_set_state(llport, bna_llport_sm_stopped);
- break;
-
- default:
- bfa_sm_fault(llport->bna, event);
- }
-}
-
-static void
-bna_fw_llport_admin_up(struct bna_llport *llport)
-{
- struct bfi_ll_port_admin_req ll_req;
-
- memset(&ll_req, 0, sizeof(ll_req));
- ll_req.mh.msg_class = BFI_MC_LL;
- ll_req.mh.msg_id = BFI_LL_H2I_PORT_ADMIN_REQ;
- ll_req.mh.mtag.h2i.lpu_id = 0;
-
- ll_req.up = BNA_STATUS_T_ENABLED;
-
- bna_mbox_qe_fill(&llport->mbox_qe, &ll_req, sizeof(ll_req),
- bna_fw_cb_llport_up, llport);
-
- bna_mbox_send(llport->bna, &llport->mbox_qe);
-}
-
-static void
-bna_fw_llport_up(struct bna_llport *llport)
-{
- if (llport->type == BNA_PORT_T_REGULAR)
- bna_fw_llport_admin_up(llport);
-}
-
-static void
-bna_fw_cb_llport_up(void *arg, int status)
-{
- struct bna_llport *llport = (struct bna_llport *)arg;
-
- bfa_q_qe_init(&llport->mbox_qe.qe);
- if (status == BFI_LL_CMD_FAIL) {
- if (llport->type == BNA_PORT_T_REGULAR)
- llport->flags &= ~BNA_LLPORT_F_PORT_ENABLED;
- else
- llport->flags &= ~BNA_LLPORT_F_ADMIN_UP;
- bfa_fsm_send_event(llport, LLPORT_E_FWRESP_UP_FAIL);
- } else
- bfa_fsm_send_event(llport, LLPORT_E_FWRESP_UP_OK);
-}
-
-static void
-bna_fw_llport_admin_down(struct bna_llport *llport)
-{
- struct bfi_ll_port_admin_req ll_req;
-
- memset(&ll_req, 0, sizeof(ll_req));
- ll_req.mh.msg_class = BFI_MC_LL;
- ll_req.mh.msg_id = BFI_LL_H2I_PORT_ADMIN_REQ;
- ll_req.mh.mtag.h2i.lpu_id = 0;
-
- ll_req.up = BNA_STATUS_T_DISABLED;
-
- bna_mbox_qe_fill(&llport->mbox_qe, &ll_req, sizeof(ll_req),
- bna_fw_cb_llport_down, llport);
-
- bna_mbox_send(llport->bna, &llport->mbox_qe);
-}
-
-static void
-bna_fw_llport_down(struct bna_llport *llport)
-{
- if (llport->type == BNA_PORT_T_REGULAR)
- bna_fw_llport_admin_down(llport);
-}
-
-static void
-bna_fw_cb_llport_down(void *arg, int status)
-{
- struct bna_llport *llport = (struct bna_llport *)arg;
-
- bfa_q_qe_init(&llport->mbox_qe.qe);
- bfa_fsm_send_event(llport, LLPORT_E_FWRESP_DOWN);
-}
-
-static void
-bna_port_cb_llport_stopped(struct bna_port *port,
- enum bna_cb_status status)
-{
- bfa_wc_down(&port->chld_stop_wc);
-}
-
-static void
-bna_llport_init(struct bna_llport *llport, struct bna *bna)
-{
- llport->flags |= BNA_LLPORT_F_ADMIN_UP;
- llport->flags |= BNA_LLPORT_F_PORT_ENABLED;
- llport->type = BNA_PORT_T_REGULAR;
- llport->bna = bna;
-
- llport->link_status = BNA_LINK_DOWN;
-
- llport->rx_started_count = 0;
-
- llport->stop_cbfn = NULL;
-
- bfa_q_qe_init(&llport->mbox_qe.qe);
-
- bfa_fsm_set_state(llport, bna_llport_sm_stopped);
-}
-
-static void
-bna_llport_uninit(struct bna_llport *llport)
-{
- llport->flags &= ~BNA_LLPORT_F_ADMIN_UP;
- llport->flags &= ~BNA_LLPORT_F_PORT_ENABLED;
-
- llport->bna = NULL;
-}
-
-static void
-bna_llport_start(struct bna_llport *llport)
-{
- bfa_fsm_send_event(llport, LLPORT_E_START);
-}
-
-static void
-bna_llport_stop(struct bna_llport *llport)
-{
- llport->stop_cbfn = bna_port_cb_llport_stopped;
-
- bfa_fsm_send_event(llport, LLPORT_E_STOP);
-}
-
-static void
-bna_llport_fail(struct bna_llport *llport)
-{
- /* Reset the physical port status to enabled */
- llport->flags |= BNA_LLPORT_F_PORT_ENABLED;
- bfa_fsm_send_event(llport, LLPORT_E_FAIL);
-}
-
-static int
-bna_llport_state_get(struct bna_llport *llport)
-{
- return bfa_sm_to_state(llport_sm_table, llport->fsm);
-}
-
-void
-bna_llport_rx_started(struct bna_llport *llport)
-{
- llport->rx_started_count++;
-
- if (llport->rx_started_count == 1) {
-
- llport->flags |= BNA_LLPORT_F_RX_STARTED;
-
- if (llport_can_be_up(llport))
- bfa_fsm_send_event(llport, LLPORT_E_UP);
- }
-}
-
-void
-bna_llport_rx_stopped(struct bna_llport *llport)
-{
- int llport_up = llport_is_up(llport);
-
- llport->rx_started_count--;
-
- if (llport->rx_started_count == 0) {
-
- llport->flags &= ~BNA_LLPORT_F_RX_STARTED;
-
- if (llport_up)
- bfa_fsm_send_event(llport, LLPORT_E_DOWN);
- }
-}
-
-/**
- * PORT
- */
-#define bna_port_chld_start(port)\
-do {\
- enum bna_tx_type tx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
- BNA_TX_T_REGULAR : BNA_TX_T_LOOPBACK;\
- enum bna_rx_type rx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
- BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;\
- bna_llport_start(&(port)->llport);\
- bna_tx_mod_start(&(port)->bna->tx_mod, tx_type);\
- bna_rx_mod_start(&(port)->bna->rx_mod, rx_type);\
-} while (0)
-
-#define bna_port_chld_stop(port)\
-do {\
- enum bna_tx_type tx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
- BNA_TX_T_REGULAR : BNA_TX_T_LOOPBACK;\
- enum bna_rx_type rx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
- BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;\
- bfa_wc_up(&(port)->chld_stop_wc);\
- bfa_wc_up(&(port)->chld_stop_wc);\
- bfa_wc_up(&(port)->chld_stop_wc);\
- bna_llport_stop(&(port)->llport);\
- bna_tx_mod_stop(&(port)->bna->tx_mod, tx_type);\
- bna_rx_mod_stop(&(port)->bna->rx_mod, rx_type);\
-} while (0)
-
-#define bna_port_chld_fail(port)\
-do {\
- bna_llport_fail(&(port)->llport);\
- bna_tx_mod_fail(&(port)->bna->tx_mod);\
- bna_rx_mod_fail(&(port)->bna->rx_mod);\
-} while (0)
-
-#define bna_port_rx_start(port)\
-do {\
- enum bna_rx_type rx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
- BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;\
- bna_rx_mod_start(&(port)->bna->rx_mod, rx_type);\
-} while (0)
-
-#define bna_port_rx_stop(port)\
-do {\
- enum bna_rx_type rx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
- BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;\
- bfa_wc_up(&(port)->chld_stop_wc);\
- bna_rx_mod_stop(&(port)->bna->rx_mod, rx_type);\
-} while (0)
-
-#define call_port_stop_cbfn(port, status)\
-do {\
- if ((port)->stop_cbfn)\
- (port)->stop_cbfn((port)->stop_cbarg, status);\
- (port)->stop_cbfn = NULL;\
- (port)->stop_cbarg = NULL;\
-} while (0)
-
-#define call_port_pause_cbfn(port, status)\
-do {\
- if ((port)->pause_cbfn)\
- (port)->pause_cbfn((port)->bna->bnad, status);\
- (port)->pause_cbfn = NULL;\
-} while (0)
-
-#define call_port_mtu_cbfn(port, status)\
-do {\
- if ((port)->mtu_cbfn)\
- (port)->mtu_cbfn((port)->bna->bnad, status);\
- (port)->mtu_cbfn = NULL;\
-} while (0)
-
-static void bna_fw_pause_set(struct bna_port *port);
-static void bna_fw_cb_pause_set(void *arg, int status);
-static void bna_fw_mtu_set(struct bna_port *port);
-static void bna_fw_cb_mtu_set(void *arg, int status);
-
-enum bna_port_event {
- PORT_E_START = 1,
- PORT_E_STOP = 2,
- PORT_E_FAIL = 3,
- PORT_E_PAUSE_CFG = 4,
- PORT_E_MTU_CFG = 5,
- PORT_E_CHLD_STOPPED = 6,
- PORT_E_FWRESP_PAUSE = 7,
- PORT_E_FWRESP_MTU = 8
-};
-
-enum bna_port_state {
- BNA_PORT_STOPPED = 1,
- BNA_PORT_MTU_INIT_WAIT = 2,
- BNA_PORT_PAUSE_INIT_WAIT = 3,
- BNA_PORT_LAST_RESP_WAIT = 4,
- BNA_PORT_STARTED = 5,
- BNA_PORT_PAUSE_CFG_WAIT = 6,
- BNA_PORT_RX_STOP_WAIT = 7,
- BNA_PORT_MTU_CFG_WAIT = 8,
- BNA_PORT_CHLD_STOP_WAIT = 9
-};
-
-bfa_fsm_state_decl(bna_port, stopped, struct bna_port,
- enum bna_port_event);
-bfa_fsm_state_decl(bna_port, mtu_init_wait, struct bna_port,
- enum bna_port_event);
-bfa_fsm_state_decl(bna_port, pause_init_wait, struct bna_port,
- enum bna_port_event);
-bfa_fsm_state_decl(bna_port, last_resp_wait, struct bna_port,
- enum bna_port_event);
-bfa_fsm_state_decl(bna_port, started, struct bna_port,
- enum bna_port_event);
-bfa_fsm_state_decl(bna_port, pause_cfg_wait, struct bna_port,
- enum bna_port_event);
-bfa_fsm_state_decl(bna_port, rx_stop_wait, struct bna_port,
- enum bna_port_event);
-bfa_fsm_state_decl(bna_port, mtu_cfg_wait, struct bna_port,
- enum bna_port_event);
-bfa_fsm_state_decl(bna_port, chld_stop_wait, struct bna_port,
- enum bna_port_event);
-
-static struct bfa_sm_table port_sm_table[] = {
- {BFA_SM(bna_port_sm_stopped), BNA_PORT_STOPPED},
- {BFA_SM(bna_port_sm_mtu_init_wait), BNA_PORT_MTU_INIT_WAIT},
- {BFA_SM(bna_port_sm_pause_init_wait), BNA_PORT_PAUSE_INIT_WAIT},
- {BFA_SM(bna_port_sm_last_resp_wait), BNA_PORT_LAST_RESP_WAIT},
- {BFA_SM(bna_port_sm_started), BNA_PORT_STARTED},
- {BFA_SM(bna_port_sm_pause_cfg_wait), BNA_PORT_PAUSE_CFG_WAIT},
- {BFA_SM(bna_port_sm_rx_stop_wait), BNA_PORT_RX_STOP_WAIT},
- {BFA_SM(bna_port_sm_mtu_cfg_wait), BNA_PORT_MTU_CFG_WAIT},
- {BFA_SM(bna_port_sm_chld_stop_wait), BNA_PORT_CHLD_STOP_WAIT}
-};
-
-static void
-bna_port_sm_stopped_entry(struct bna_port *port)
-{
- call_port_pause_cbfn(port, BNA_CB_SUCCESS);
- call_port_mtu_cbfn(port, BNA_CB_SUCCESS);
- call_port_stop_cbfn(port, BNA_CB_SUCCESS);
-}
-
-static void
-bna_port_sm_stopped(struct bna_port *port, enum bna_port_event event)
-{
- switch (event) {
- case PORT_E_START:
- bfa_fsm_set_state(port, bna_port_sm_mtu_init_wait);
- break;
-
- case PORT_E_STOP:
- call_port_stop_cbfn(port, BNA_CB_SUCCESS);
- break;
-
- case PORT_E_FAIL:
- /* No-op */
- break;
-
- case PORT_E_PAUSE_CFG:
- call_port_pause_cbfn(port, BNA_CB_SUCCESS);
- break;
-
- case PORT_E_MTU_CFG:
- call_port_mtu_cbfn(port, BNA_CB_SUCCESS);
- break;
-
- case PORT_E_CHLD_STOPPED:
- /**
- * This event is received due to LLPort, Tx and Rx objects
- * failing
- */
- /* No-op */
- break;
-
- case PORT_E_FWRESP_PAUSE:
- case PORT_E_FWRESP_MTU:
- /**
- * These events are received due to flushing of mbox when
- * device fails
- */
- /* No-op */
- break;
-
- default:
- bfa_sm_fault(port->bna, event);
- }
-}
-
-static void
-bna_port_sm_mtu_init_wait_entry(struct bna_port *port)
-{
- bna_fw_mtu_set(port);
-}
-
-static void
-bna_port_sm_mtu_init_wait(struct bna_port *port, enum bna_port_event event)
-{
- switch (event) {
- case PORT_E_STOP:
- bfa_fsm_set_state(port, bna_port_sm_last_resp_wait);
- break;
-
- case PORT_E_FAIL:
- bfa_fsm_set_state(port, bna_port_sm_stopped);
- break;
-
- case PORT_E_PAUSE_CFG:
- /* No-op */
- break;
-
- case PORT_E_MTU_CFG:
- port->flags |= BNA_PORT_F_MTU_CHANGED;
- break;
-
- case PORT_E_FWRESP_MTU:
- if (port->flags & BNA_PORT_F_MTU_CHANGED) {
- port->flags &= ~BNA_PORT_F_MTU_CHANGED;
- bna_fw_mtu_set(port);
- } else {
- bfa_fsm_set_state(port, bna_port_sm_pause_init_wait);
- }
- break;
-
- default:
- bfa_sm_fault(port->bna, event);
- }
-}
-
-static void
-bna_port_sm_pause_init_wait_entry(struct bna_port *port)
-{
- bna_fw_pause_set(port);
-}
-
-static void
-bna_port_sm_pause_init_wait(struct bna_port *port,
- enum bna_port_event event)
-{
- switch (event) {
- case PORT_E_STOP:
- bfa_fsm_set_state(port, bna_port_sm_last_resp_wait);
- break;
-
- case PORT_E_FAIL:
- bfa_fsm_set_state(port, bna_port_sm_stopped);
- break;
-
- case PORT_E_PAUSE_CFG:
- port->flags |= BNA_PORT_F_PAUSE_CHANGED;
- break;
-
- case PORT_E_MTU_CFG:
- port->flags |= BNA_PORT_F_MTU_CHANGED;
- break;
-
- case PORT_E_FWRESP_PAUSE:
- if (port->flags & BNA_PORT_F_PAUSE_CHANGED) {
- port->flags &= ~BNA_PORT_F_PAUSE_CHANGED;
- bna_fw_pause_set(port);
- } else if (port->flags & BNA_PORT_F_MTU_CHANGED) {
- port->flags &= ~BNA_PORT_F_MTU_CHANGED;
- bfa_fsm_set_state(port, bna_port_sm_mtu_init_wait);
- } else {
- bfa_fsm_set_state(port, bna_port_sm_started);
- bna_port_chld_start(port);
- }
- break;
-
- default:
- bfa_sm_fault(port->bna, event);
- }
-}
-
-static void
-bna_port_sm_last_resp_wait_entry(struct bna_port *port)
-{
-}
-
-static void
-bna_port_sm_last_resp_wait(struct bna_port *port,
- enum bna_port_event event)
-{
- switch (event) {
- case PORT_E_FAIL:
- case PORT_E_FWRESP_PAUSE:
- case PORT_E_FWRESP_MTU:
- bfa_fsm_set_state(port, bna_port_sm_stopped);
- break;
-
- default:
- bfa_sm_fault(port->bna, event);
- }
-}
-
-static void
-bna_port_sm_started_entry(struct bna_port *port)
-{
- /**
- * NOTE: Do not call bna_port_chld_start() here, since it will be
- * inadvertently called during pause_cfg_wait->started transition
- * as well
- */
- call_port_pause_cbfn(port, BNA_CB_SUCCESS);
- call_port_mtu_cbfn(port, BNA_CB_SUCCESS);
-}
-
-static void
-bna_port_sm_started(struct bna_port *port,
- enum bna_port_event event)
-{
- switch (event) {
- case PORT_E_STOP:
- bfa_fsm_set_state(port, bna_port_sm_chld_stop_wait);
- break;
-
- case PORT_E_FAIL:
- bfa_fsm_set_state(port, bna_port_sm_stopped);
- bna_port_chld_fail(port);
- break;
-
- case PORT_E_PAUSE_CFG:
- bfa_fsm_set_state(port, bna_port_sm_pause_cfg_wait);
- break;
-
- case PORT_E_MTU_CFG:
- bfa_fsm_set_state(port, bna_port_sm_rx_stop_wait);
- break;
-
- default:
- bfa_sm_fault(port->bna, event);
- }
-}
-
-static void
-bna_port_sm_pause_cfg_wait_entry(struct bna_port *port)
-{
- bna_fw_pause_set(port);
-}
-
-static void
-bna_port_sm_pause_cfg_wait(struct bna_port *port,
- enum bna_port_event event)
-{
- switch (event) {
- case PORT_E_FAIL:
- bfa_fsm_set_state(port, bna_port_sm_stopped);
- bna_port_chld_fail(port);
- break;
-
- case PORT_E_FWRESP_PAUSE:
- bfa_fsm_set_state(port, bna_port_sm_started);
- break;
-
- default:
- bfa_sm_fault(port->bna, event);
- }
-}
-
-static void
-bna_port_sm_rx_stop_wait_entry(struct bna_port *port)
-{
- bna_port_rx_stop(port);
-}
-
-static void
-bna_port_sm_rx_stop_wait(struct bna_port *port,
- enum bna_port_event event)
-{
- switch (event) {
- case PORT_E_FAIL:
- bfa_fsm_set_state(port, bna_port_sm_stopped);
- bna_port_chld_fail(port);
- break;
-
- case PORT_E_CHLD_STOPPED:
- bfa_fsm_set_state(port, bna_port_sm_mtu_cfg_wait);
- break;
-
- default:
- bfa_sm_fault(port->bna, event);
- }
-}
-
-static void
-bna_port_sm_mtu_cfg_wait_entry(struct bna_port *port)
-{
- bna_fw_mtu_set(port);
-}
-
-static void
-bna_port_sm_mtu_cfg_wait(struct bna_port *port, enum bna_port_event event)
-{
- switch (event) {
- case PORT_E_FAIL:
- bfa_fsm_set_state(port, bna_port_sm_stopped);
- bna_port_chld_fail(port);
- break;
-
- case PORT_E_FWRESP_MTU:
- bfa_fsm_set_state(port, bna_port_sm_started);
- bna_port_rx_start(port);
- break;
-
- default:
- bfa_sm_fault(port->bna, event);
- }
-}
-
-static void
-bna_port_sm_chld_stop_wait_entry(struct bna_port *port)
-{
- bna_port_chld_stop(port);
-}
-
-static void
-bna_port_sm_chld_stop_wait(struct bna_port *port,
- enum bna_port_event event)
-{
- switch (event) {
- case PORT_E_FAIL:
- bfa_fsm_set_state(port, bna_port_sm_stopped);
- bna_port_chld_fail(port);
- break;
-
- case PORT_E_CHLD_STOPPED:
- bfa_fsm_set_state(port, bna_port_sm_stopped);
- break;
-
- default:
- bfa_sm_fault(port->bna, event);
- }
-}
-
-static void
-bna_fw_pause_set(struct bna_port *port)
-{
- struct bfi_ll_set_pause_req ll_req;
-
- memset(&ll_req, 0, sizeof(ll_req));
- ll_req.mh.msg_class = BFI_MC_LL;
- ll_req.mh.msg_id = BFI_LL_H2I_SET_PAUSE_REQ;
- ll_req.mh.mtag.h2i.lpu_id = 0;
-
- ll_req.tx_pause = port->pause_config.tx_pause;
- ll_req.rx_pause = port->pause_config.rx_pause;
-
- bna_mbox_qe_fill(&port->mbox_qe, &ll_req, sizeof(ll_req),
- bna_fw_cb_pause_set, port);
-
- bna_mbox_send(port->bna, &port->mbox_qe);
-}
-
-static void
-bna_fw_cb_pause_set(void *arg, int status)
-{
- struct bna_port *port = (struct bna_port *)arg;
-
- bfa_q_qe_init(&port->mbox_qe.qe);
- bfa_fsm_send_event(port, PORT_E_FWRESP_PAUSE);
-}
-
-void
-bna_fw_mtu_set(struct bna_port *port)
-{
- struct bfi_ll_mtu_info_req ll_req;
-
- bfi_h2i_set(ll_req.mh, BFI_MC_LL, BFI_LL_H2I_MTU_INFO_REQ, 0);
- ll_req.mtu = htons((u16)port->mtu);
-
- bna_mbox_qe_fill(&port->mbox_qe, &ll_req, sizeof(ll_req),
- bna_fw_cb_mtu_set, port);
- bna_mbox_send(port->bna, &port->mbox_qe);
-}
-
-void
-bna_fw_cb_mtu_set(void *arg, int status)
-{
- struct bna_port *port = (struct bna_port *)arg;
-
- bfa_q_qe_init(&port->mbox_qe.qe);
- bfa_fsm_send_event(port, PORT_E_FWRESP_MTU);
-}
-
-static void
-bna_port_cb_chld_stopped(void *arg)
-{
- struct bna_port *port = (struct bna_port *)arg;
-
- bfa_fsm_send_event(port, PORT_E_CHLD_STOPPED);
-}
-
-static void
-bna_port_init(struct bna_port *port, struct bna *bna)
-{
- port->bna = bna;
- port->flags = 0;
- port->mtu = 0;
- port->type = BNA_PORT_T_REGULAR;
-
- port->link_cbfn = bnad_cb_port_link_status;
-
- port->chld_stop_wc.wc_resume = bna_port_cb_chld_stopped;
- port->chld_stop_wc.wc_cbarg = port;
- port->chld_stop_wc.wc_count = 0;
-
- port->stop_cbfn = NULL;
- port->stop_cbarg = NULL;
-
- port->pause_cbfn = NULL;
-
- port->mtu_cbfn = NULL;
-
- bfa_q_qe_init(&port->mbox_qe.qe);
-
- bfa_fsm_set_state(port, bna_port_sm_stopped);
-
- bna_llport_init(&port->llport, bna);
-}
-
-static void
-bna_port_uninit(struct bna_port *port)
-{
- bna_llport_uninit(&port->llport);
-
- port->flags = 0;
-
- port->bna = NULL;
-}
-
-static int
-bna_port_state_get(struct bna_port *port)
-{
- return bfa_sm_to_state(port_sm_table, port->fsm);
-}
-
-static void
-bna_port_start(struct bna_port *port)
-{
- port->flags |= BNA_PORT_F_DEVICE_READY;
- if (port->flags & BNA_PORT_F_ENABLED)
- bfa_fsm_send_event(port, PORT_E_START);
-}
-
-static void
-bna_port_stop(struct bna_port *port)
-{
- port->stop_cbfn = bna_device_cb_port_stopped;
- port->stop_cbarg = &port->bna->device;
-
- port->flags &= ~BNA_PORT_F_DEVICE_READY;
- bfa_fsm_send_event(port, PORT_E_STOP);
-}
-
-static void
-bna_port_fail(struct bna_port *port)
-{
- port->flags &= ~BNA_PORT_F_DEVICE_READY;
- bfa_fsm_send_event(port, PORT_E_FAIL);
-}
-
-void
-bna_port_cb_tx_stopped(struct bna_port *port, enum bna_cb_status status)
-{
- bfa_wc_down(&port->chld_stop_wc);
-}
-
-void
-bna_port_cb_rx_stopped(struct bna_port *port, enum bna_cb_status status)
-{
- bfa_wc_down(&port->chld_stop_wc);
-}
-
-int
-bna_port_mtu_get(struct bna_port *port)
-{
- return port->mtu;
-}
-
-void
-bna_port_enable(struct bna_port *port)
-{
- if (port->fsm != (bfa_sm_t)bna_port_sm_stopped)
- return;
-
- port->flags |= BNA_PORT_F_ENABLED;
-
- if (port->flags & BNA_PORT_F_DEVICE_READY)
- bfa_fsm_send_event(port, PORT_E_START);
-}
-
-void
-bna_port_disable(struct bna_port *port, enum bna_cleanup_type type,
- void (*cbfn)(void *, enum bna_cb_status))
-{
- if (type == BNA_SOFT_CLEANUP) {
- (*cbfn)(port->bna->bnad, BNA_CB_SUCCESS);
- return;
- }
-
- port->stop_cbfn = cbfn;
- port->stop_cbarg = port->bna->bnad;
-
- port->flags &= ~BNA_PORT_F_ENABLED;
-
- bfa_fsm_send_event(port, PORT_E_STOP);
-}
-
-void
-bna_port_pause_config(struct bna_port *port,
- struct bna_pause_config *pause_config,
- void (*cbfn)(struct bnad *, enum bna_cb_status))
-{
- port->pause_config = *pause_config;
-
- port->pause_cbfn = cbfn;
-
- bfa_fsm_send_event(port, PORT_E_PAUSE_CFG);
-}
-
-void
-bna_port_mtu_set(struct bna_port *port, int mtu,
- void (*cbfn)(struct bnad *, enum bna_cb_status))
-{
- port->mtu = mtu;
-
- port->mtu_cbfn = cbfn;
-
- bfa_fsm_send_event(port, PORT_E_MTU_CFG);
-}
-
-void
-bna_port_mac_get(struct bna_port *port, mac_t *mac)
-{
- *mac = bfa_nw_ioc_get_mac(&port->bna->device.ioc);
-}
-
-/**
- * DEVICE
- */
-#define enable_mbox_intr(_device)\
-do {\
- u32 intr_status;\
- bna_intr_status_get((_device)->bna, intr_status);\
- bnad_cb_device_enable_mbox_intr((_device)->bna->bnad);\
- bna_mbox_intr_enable((_device)->bna);\
-} while (0)
-
-#define disable_mbox_intr(_device)\
-do {\
- bna_mbox_intr_disable((_device)->bna);\
- bnad_cb_device_disable_mbox_intr((_device)->bna->bnad);\
-} while (0)
-
-static const struct bna_chip_regs_offset reg_offset[] =
-{{HOST_PAGE_NUM_FN0, HOSTFN0_INT_STATUS,
- HOSTFN0_INT_MASK, HOST_MSIX_ERR_INDEX_FN0},
-{HOST_PAGE_NUM_FN1, HOSTFN1_INT_STATUS,
- HOSTFN1_INT_MASK, HOST_MSIX_ERR_INDEX_FN1},
-{HOST_PAGE_NUM_FN2, HOSTFN2_INT_STATUS,
- HOSTFN2_INT_MASK, HOST_MSIX_ERR_INDEX_FN2},
-{HOST_PAGE_NUM_FN3, HOSTFN3_INT_STATUS,
- HOSTFN3_INT_MASK, HOST_MSIX_ERR_INDEX_FN3},
-};
-
-enum bna_device_event {
- DEVICE_E_ENABLE = 1,
- DEVICE_E_DISABLE = 2,
- DEVICE_E_IOC_READY = 3,
- DEVICE_E_IOC_FAILED = 4,
- DEVICE_E_IOC_DISABLED = 5,
- DEVICE_E_IOC_RESET = 6,
- DEVICE_E_PORT_STOPPED = 7,
-};
-
-enum bna_device_state {
- BNA_DEVICE_STOPPED = 1,
- BNA_DEVICE_IOC_READY_WAIT = 2,
- BNA_DEVICE_READY = 3,
- BNA_DEVICE_PORT_STOP_WAIT = 4,
- BNA_DEVICE_IOC_DISABLE_WAIT = 5,
- BNA_DEVICE_FAILED = 6
-};
-
-bfa_fsm_state_decl(bna_device, stopped, struct bna_device,
- enum bna_device_event);
-bfa_fsm_state_decl(bna_device, ioc_ready_wait, struct bna_device,
- enum bna_device_event);
-bfa_fsm_state_decl(bna_device, ready, struct bna_device,
- enum bna_device_event);
-bfa_fsm_state_decl(bna_device, port_stop_wait, struct bna_device,
- enum bna_device_event);
-bfa_fsm_state_decl(bna_device, ioc_disable_wait, struct bna_device,
- enum bna_device_event);
-bfa_fsm_state_decl(bna_device, failed, struct bna_device,
- enum bna_device_event);
-
-static struct bfa_sm_table device_sm_table[] = {
- {BFA_SM(bna_device_sm_stopped), BNA_DEVICE_STOPPED},
- {BFA_SM(bna_device_sm_ioc_ready_wait), BNA_DEVICE_IOC_READY_WAIT},
- {BFA_SM(bna_device_sm_ready), BNA_DEVICE_READY},
- {BFA_SM(bna_device_sm_port_stop_wait), BNA_DEVICE_PORT_STOP_WAIT},
- {BFA_SM(bna_device_sm_ioc_disable_wait), BNA_DEVICE_IOC_DISABLE_WAIT},
- {BFA_SM(bna_device_sm_failed), BNA_DEVICE_FAILED},
-};
-
-static void
-bna_device_sm_stopped_entry(struct bna_device *device)
-{
- if (device->stop_cbfn)
- device->stop_cbfn(device->stop_cbarg, BNA_CB_SUCCESS);
-
- device->stop_cbfn = NULL;
- device->stop_cbarg = NULL;
-}
-
-static void
-bna_device_sm_stopped(struct bna_device *device,
- enum bna_device_event event)
-{
- switch (event) {
- case DEVICE_E_ENABLE:
- if (device->intr_type == BNA_INTR_T_MSIX)
- bna_mbox_msix_idx_set(device);
- bfa_nw_ioc_enable(&device->ioc);
- bfa_fsm_set_state(device, bna_device_sm_ioc_ready_wait);
- break;
-
- case DEVICE_E_DISABLE:
- bfa_fsm_set_state(device, bna_device_sm_stopped);
- break;
-
- case DEVICE_E_IOC_RESET:
- enable_mbox_intr(device);
- break;
-
- case DEVICE_E_IOC_FAILED:
- bfa_fsm_set_state(device, bna_device_sm_failed);
- break;
-
- default:
- bfa_sm_fault(device->bna, event);
- }
-}
-
-static void
-bna_device_sm_ioc_ready_wait_entry(struct bna_device *device)
-{
- /**
- * Do not call bfa_ioc_enable() here. It must be called in the
- * previous state due to failed -> ioc_ready_wait transition.
- */
-}
-
-static void
-bna_device_sm_ioc_ready_wait(struct bna_device *device,
- enum bna_device_event event)
-{
- switch (event) {
- case DEVICE_E_DISABLE:
- if (device->ready_cbfn)
- device->ready_cbfn(device->ready_cbarg,
- BNA_CB_INTERRUPT);
- device->ready_cbfn = NULL;
- device->ready_cbarg = NULL;
- bfa_fsm_set_state(device, bna_device_sm_ioc_disable_wait);
- break;
-
- case DEVICE_E_IOC_READY:
- bfa_fsm_set_state(device, bna_device_sm_ready);
- break;
-
- case DEVICE_E_IOC_FAILED:
- bfa_fsm_set_state(device, bna_device_sm_failed);
- break;
-
- case DEVICE_E_IOC_RESET:
- enable_mbox_intr(device);
- break;
-
- default:
- bfa_sm_fault(device->bna, event);
- }
-}
-
-static void
-bna_device_sm_ready_entry(struct bna_device *device)
-{
- bna_mbox_mod_start(&device->bna->mbox_mod);
- bna_port_start(&device->bna->port);
-
- if (device->ready_cbfn)
- device->ready_cbfn(device->ready_cbarg,
- BNA_CB_SUCCESS);
- device->ready_cbfn = NULL;
- device->ready_cbarg = NULL;
-}
-
-static void
-bna_device_sm_ready(struct bna_device *device, enum bna_device_event event)
-{
- switch (event) {
- case DEVICE_E_DISABLE:
- bfa_fsm_set_state(device, bna_device_sm_port_stop_wait);
- break;
-
- case DEVICE_E_IOC_FAILED:
- bfa_fsm_set_state(device, bna_device_sm_failed);
- break;
-
- default:
- bfa_sm_fault(device->bna, event);
- }
-}
-
-static void
-bna_device_sm_port_stop_wait_entry(struct bna_device *device)
-{
- bna_port_stop(&device->bna->port);
-}
-
-static void
-bna_device_sm_port_stop_wait(struct bna_device *device,
- enum bna_device_event event)
-{
- switch (event) {
- case DEVICE_E_PORT_STOPPED:
- bna_mbox_mod_stop(&device->bna->mbox_mod);
- bfa_fsm_set_state(device, bna_device_sm_ioc_disable_wait);
- break;
-
- case DEVICE_E_IOC_FAILED:
- disable_mbox_intr(device);
- bna_port_fail(&device->bna->port);
- break;
-
- default:
- bfa_sm_fault(device->bna, event);
- }
-}
-
-static void
-bna_device_sm_ioc_disable_wait_entry(struct bna_device *device)
-{
- bfa_nw_ioc_disable(&device->ioc);
-}
-
-static void
-bna_device_sm_ioc_disable_wait(struct bna_device *device,
- enum bna_device_event event)
-{
- switch (event) {
- case DEVICE_E_IOC_DISABLED:
- disable_mbox_intr(device);
- bfa_fsm_set_state(device, bna_device_sm_stopped);
- break;
-
- default:
- bfa_sm_fault(device->bna, event);
- }
-}
-
-static void
-bna_device_sm_failed_entry(struct bna_device *device)
-{
- disable_mbox_intr(device);
- bna_port_fail(&device->bna->port);
- bna_mbox_mod_stop(&device->bna->mbox_mod);
-
- if (device->ready_cbfn)
- device->ready_cbfn(device->ready_cbarg,
- BNA_CB_FAIL);
- device->ready_cbfn = NULL;
- device->ready_cbarg = NULL;
-}
-
-static void
-bna_device_sm_failed(struct bna_device *device,
- enum bna_device_event event)
-{
- switch (event) {
- case DEVICE_E_DISABLE:
- bfa_fsm_set_state(device, bna_device_sm_ioc_disable_wait);
- break;
-
- case DEVICE_E_IOC_RESET:
- enable_mbox_intr(device);
- bfa_fsm_set_state(device, bna_device_sm_ioc_ready_wait);
- break;
-
- default:
- bfa_sm_fault(device->bna, event);
- }
-}
-
-/* IOC callback functions */
-
-static void
-bna_device_cb_iocll_ready(void *dev, enum bfa_status error)
-{
- struct bna_device *device = (struct bna_device *)dev;
-
- if (error)
- bfa_fsm_send_event(device, DEVICE_E_IOC_FAILED);
- else
- bfa_fsm_send_event(device, DEVICE_E_IOC_READY);
-}
-
-static void
-bna_device_cb_iocll_disabled(void *dev)
-{
- struct bna_device *device = (struct bna_device *)dev;
-
- bfa_fsm_send_event(device, DEVICE_E_IOC_DISABLED);
-}
-
-static void
-bna_device_cb_iocll_failed(void *dev)
-{
- struct bna_device *device = (struct bna_device *)dev;
-
- bfa_fsm_send_event(device, DEVICE_E_IOC_FAILED);
-}
-
-static void
-bna_device_cb_iocll_reset(void *dev)
-{
- struct bna_device *device = (struct bna_device *)dev;
-
- bfa_fsm_send_event(device, DEVICE_E_IOC_RESET);
-}
-
-static struct bfa_ioc_cbfn bfa_iocll_cbfn = {
- bna_device_cb_iocll_ready,
- bna_device_cb_iocll_disabled,
- bna_device_cb_iocll_failed,
- bna_device_cb_iocll_reset
-};
-
-/* device */
-static void
-bna_adv_device_init(struct bna_device *device, struct bna *bna,
- struct bna_res_info *res_info)
-{
- u8 *kva;
- u64 dma;
-
- device->bna = bna;
-
- kva = res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mdl[0].kva;
-
- /**
- * Attach common modules (Diag, SFP, CEE, Port) and claim respective
- * DMA memory.
- */
- BNA_GET_DMA_ADDR(
- &res_info[BNA_RES_MEM_T_COM].res_u.mem_info.mdl[0].dma, dma);
- kva = res_info[BNA_RES_MEM_T_COM].res_u.mem_info.mdl[0].kva;
-
- bfa_nw_cee_attach(&bna->cee, &device->ioc, bna);
- bfa_nw_cee_mem_claim(&bna->cee, kva, dma);
- kva += bfa_nw_cee_meminfo();
- dma += bfa_nw_cee_meminfo();
-
-}
-
-static void
-bna_device_init(struct bna_device *device, struct bna *bna,
- struct bna_res_info *res_info)
-{
- u64 dma;
-
- device->bna = bna;
-
- /**
- * Attach IOC and claim:
- * 1. DMA memory for IOC attributes
- * 2. Kernel memory for FW trace
- */
- bfa_nw_ioc_attach(&device->ioc, device, &bfa_iocll_cbfn);
- bfa_nw_ioc_pci_init(&device->ioc, &bna->pcidev, BFI_MC_LL);
-
- BNA_GET_DMA_ADDR(
- &res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.mdl[0].dma, dma);
- bfa_nw_ioc_mem_claim(&device->ioc,
- res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.mdl[0].kva,
- dma);
-
- bna_adv_device_init(device, bna, res_info);
- /*
- * Initialize mbox_mod only after IOC, so that mbox handler
- * registration goes through
- */
- device->intr_type =
- res_info[BNA_RES_INTR_T_MBOX].res_u.intr_info.intr_type;
- device->vector =
- res_info[BNA_RES_INTR_T_MBOX].res_u.intr_info.idl[0].vector;
- bna_mbox_mod_init(&bna->mbox_mod, bna);
-
- device->ready_cbfn = device->stop_cbfn = NULL;
- device->ready_cbarg = device->stop_cbarg = NULL;
-
- bfa_fsm_set_state(device, bna_device_sm_stopped);
-}
-
-static void
-bna_device_uninit(struct bna_device *device)
-{
- bna_mbox_mod_uninit(&device->bna->mbox_mod);
-
- bfa_nw_ioc_detach(&device->ioc);
-
- device->bna = NULL;
-}
-
-static void
-bna_device_cb_port_stopped(void *arg, enum bna_cb_status status)
-{
- struct bna_device *device = (struct bna_device *)arg;
-
- bfa_fsm_send_event(device, DEVICE_E_PORT_STOPPED);
-}
-
-static int
-bna_device_status_get(struct bna_device *device)
-{
- return device->fsm == (bfa_fsm_t)bna_device_sm_ready;
-}
-
-void
-bna_device_enable(struct bna_device *device)
-{
- if (device->fsm != (bfa_fsm_t)bna_device_sm_stopped) {
- bnad_cb_device_enabled(device->bna->bnad, BNA_CB_BUSY);
- return;
- }
-
- device->ready_cbfn = bnad_cb_device_enabled;
- device->ready_cbarg = device->bna->bnad;
-
- bfa_fsm_send_event(device, DEVICE_E_ENABLE);
-}
-
-void
-bna_device_disable(struct bna_device *device, enum bna_cleanup_type type)
-{
- if (type == BNA_SOFT_CLEANUP) {
- bnad_cb_device_disabled(device->bna->bnad, BNA_CB_SUCCESS);
- return;
- }
-
- device->stop_cbfn = bnad_cb_device_disabled;
- device->stop_cbarg = device->bna->bnad;
-
- bfa_fsm_send_event(device, DEVICE_E_DISABLE);
-}
-
-static int
-bna_device_state_get(struct bna_device *device)
-{
- return bfa_sm_to_state(device_sm_table, device->fsm);
-}
-
-const u32 bna_napi_dim_vector[BNA_LOAD_T_MAX][BNA_BIAS_T_MAX] = {
- {12, 12},
- {6, 10},
- {5, 10},
- {4, 8},
- {3, 6},
- {3, 6},
- {2, 4},
- {1, 2},
-};
-
-/* utils */
-
-static void
-bna_adv_res_req(struct bna_res_info *res_info)
-{
- /* DMA memory for COMMON_MODULE */
- res_info[BNA_RES_MEM_T_COM].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_COM].res_u.mem_info.mem_type = BNA_MEM_T_DMA;
- res_info[BNA_RES_MEM_T_COM].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_COM].res_u.mem_info.len = ALIGN(
- bfa_nw_cee_meminfo(), PAGE_SIZE);
-
- /* Virtual memory for retreiving fw_trc */
- res_info[BNA_RES_MEM_T_FWTRC].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mem_type = BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.num = 0;
- res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.len = 0;
-
- /* DMA memory for retreiving stats */
- res_info[BNA_RES_MEM_T_STATS].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mem_type = BNA_MEM_T_DMA;
- res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.len =
- ALIGN(BFI_HW_STATS_SIZE, PAGE_SIZE);
-
- /* Virtual memory for soft stats */
- res_info[BNA_RES_MEM_T_SWSTATS].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_SWSTATS].res_u.mem_info.mem_type = BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_SWSTATS].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_SWSTATS].res_u.mem_info.len =
- sizeof(struct bna_sw_stats);
-}
-
-static void
-bna_sw_stats_get(struct bna *bna, struct bna_sw_stats *sw_stats)
-{
- struct bna_tx *tx;
- struct bna_txq *txq;
- struct bna_rx *rx;
- struct bna_rxp *rxp;
- struct list_head *qe;
- struct list_head *txq_qe;
- struct list_head *rxp_qe;
- struct list_head *mac_qe;
- int i;
-
- sw_stats->device_state = bna_device_state_get(&bna->device);
- sw_stats->port_state = bna_port_state_get(&bna->port);
- sw_stats->port_flags = bna->port.flags;
- sw_stats->llport_state = bna_llport_state_get(&bna->port.llport);
- sw_stats->priority = bna->port.priority;
-
- i = 0;
- list_for_each(qe, &bna->tx_mod.tx_active_q) {
- tx = (struct bna_tx *)qe;
- sw_stats->tx_stats[i].tx_state = bna_tx_state_get(tx);
- sw_stats->tx_stats[i].tx_flags = tx->flags;
-
- sw_stats->tx_stats[i].num_txqs = 0;
- sw_stats->tx_stats[i].txq_bmap[0] = 0;
- sw_stats->tx_stats[i].txq_bmap[1] = 0;
- list_for_each(txq_qe, &tx->txq_q) {
- txq = (struct bna_txq *)txq_qe;
- if (txq->txq_id < 32)
- sw_stats->tx_stats[i].txq_bmap[0] |=
- ((u32)1 << txq->txq_id);
- else
- sw_stats->tx_stats[i].txq_bmap[1] |=
- ((u32)
- 1 << (txq->txq_id - 32));
- sw_stats->tx_stats[i].num_txqs++;
- }
-
- sw_stats->tx_stats[i].txf_id = tx->txf.txf_id;
-
- i++;
- }
- sw_stats->num_active_tx = i;
-
- i = 0;
- list_for_each(qe, &bna->rx_mod.rx_active_q) {
- rx = (struct bna_rx *)qe;
- sw_stats->rx_stats[i].rx_state = bna_rx_state_get(rx);
- sw_stats->rx_stats[i].rx_flags = rx->rx_flags;
-
- sw_stats->rx_stats[i].num_rxps = 0;
- sw_stats->rx_stats[i].num_rxqs = 0;
- sw_stats->rx_stats[i].rxq_bmap[0] = 0;
- sw_stats->rx_stats[i].rxq_bmap[1] = 0;
- sw_stats->rx_stats[i].cq_bmap[0] = 0;
- sw_stats->rx_stats[i].cq_bmap[1] = 0;
- list_for_each(rxp_qe, &rx->rxp_q) {
- rxp = (struct bna_rxp *)rxp_qe;
-
- sw_stats->rx_stats[i].num_rxqs += 1;
-
- if (rxp->type == BNA_RXP_SINGLE) {
- if (rxp->rxq.single.only->rxq_id < 32) {
- sw_stats->rx_stats[i].rxq_bmap[0] |=
- ((u32)1 <<
- rxp->rxq.single.only->rxq_id);
- } else {
- sw_stats->rx_stats[i].rxq_bmap[1] |=
- ((u32)1 <<
- (rxp->rxq.single.only->rxq_id - 32));
- }
- } else {
- if (rxp->rxq.slr.large->rxq_id < 32) {
- sw_stats->rx_stats[i].rxq_bmap[0] |=
- ((u32)1 <<
- rxp->rxq.slr.large->rxq_id);
- } else {
- sw_stats->rx_stats[i].rxq_bmap[1] |=
- ((u32)1 <<
- (rxp->rxq.slr.large->rxq_id - 32));
- }
-
- if (rxp->rxq.slr.small->rxq_id < 32) {
- sw_stats->rx_stats[i].rxq_bmap[0] |=
- ((u32)1 <<
- rxp->rxq.slr.small->rxq_id);
- } else {
- sw_stats->rx_stats[i].rxq_bmap[1] |=
- ((u32)1 <<
- (rxp->rxq.slr.small->rxq_id - 32));
- }
- sw_stats->rx_stats[i].num_rxqs += 1;
- }
-
- if (rxp->cq.cq_id < 32)
- sw_stats->rx_stats[i].cq_bmap[0] |=
- (1 << rxp->cq.cq_id);
- else
- sw_stats->rx_stats[i].cq_bmap[1] |=
- (1 << (rxp->cq.cq_id - 32));
-
- sw_stats->rx_stats[i].num_rxps++;
- }
-
- sw_stats->rx_stats[i].rxf_id = rx->rxf.rxf_id;
- sw_stats->rx_stats[i].rxf_state = bna_rxf_state_get(&rx->rxf);
- sw_stats->rx_stats[i].rxf_oper_state = rx->rxf.rxf_oper_state;
-
- sw_stats->rx_stats[i].num_active_ucast = 0;
- if (rx->rxf.ucast_active_mac)
- sw_stats->rx_stats[i].num_active_ucast++;
- list_for_each(mac_qe, &rx->rxf.ucast_active_q)
- sw_stats->rx_stats[i].num_active_ucast++;
-
- sw_stats->rx_stats[i].num_active_mcast = 0;
- list_for_each(mac_qe, &rx->rxf.mcast_active_q)
- sw_stats->rx_stats[i].num_active_mcast++;
-
- sw_stats->rx_stats[i].rxmode_active = rx->rxf.rxmode_active;
- sw_stats->rx_stats[i].vlan_filter_status =
- rx->rxf.vlan_filter_status;
- memcpy(sw_stats->rx_stats[i].vlan_filter_table,
- rx->rxf.vlan_filter_table,
- sizeof(u32) * ((BFI_MAX_VLAN + 1) / 32));
-
- sw_stats->rx_stats[i].rss_status = rx->rxf.rss_status;
- sw_stats->rx_stats[i].hds_status = rx->rxf.hds_status;
-
- i++;
- }
- sw_stats->num_active_rx = i;
-}
-
-static void
-bna_fw_cb_stats_get(void *arg, int status)
-{
- struct bna *bna = (struct bna *)arg;
- u64 *p_stats;
- int i, count;
- int rxf_count, txf_count;
- u64 rxf_bmap, txf_bmap;
-
- bfa_q_qe_init(&bna->mbox_qe.qe);
-
- if (status == 0) {
- p_stats = (u64 *)bna->stats.hw_stats;
- count = sizeof(struct bfi_ll_stats) / sizeof(u64);
- for (i = 0; i < count; i++)
- p_stats[i] = cpu_to_be64(p_stats[i]);
-
- rxf_count = 0;
- rxf_bmap = (u64)bna->stats.rxf_bmap[0] |
- ((u64)bna->stats.rxf_bmap[1] << 32);
- for (i = 0; i < BFI_LL_RXF_ID_MAX; i++)
- if (rxf_bmap & ((u64)1 << i))
- rxf_count++;
-
- txf_count = 0;
- txf_bmap = (u64)bna->stats.txf_bmap[0] |
- ((u64)bna->stats.txf_bmap[1] << 32);
- for (i = 0; i < BFI_LL_TXF_ID_MAX; i++)
- if (txf_bmap & ((u64)1 << i))
- txf_count++;
-
- p_stats = (u64 *)&bna->stats.hw_stats->rxf_stats[0] +
- ((rxf_count * sizeof(struct bfi_ll_stats_rxf) +
- txf_count * sizeof(struct bfi_ll_stats_txf))/
- sizeof(u64));
-
- /* Populate the TXF stats from the firmware DMAed copy */
- for (i = (BFI_LL_TXF_ID_MAX - 1); i >= 0; i--)
- if (txf_bmap & ((u64)1 << i)) {
- p_stats -= sizeof(struct bfi_ll_stats_txf)/
- sizeof(u64);
- memcpy(&bna->stats.hw_stats->txf_stats[i],
- p_stats,
- sizeof(struct bfi_ll_stats_txf));
- }
-
- /* Populate the RXF stats from the firmware DMAed copy */
- for (i = (BFI_LL_RXF_ID_MAX - 1); i >= 0; i--)
- if (rxf_bmap & ((u64)1 << i)) {
- p_stats -= sizeof(struct bfi_ll_stats_rxf)/
- sizeof(u64);
- memcpy(&bna->stats.hw_stats->rxf_stats[i],
- p_stats,
- sizeof(struct bfi_ll_stats_rxf));
- }
-
- bna_sw_stats_get(bna, bna->stats.sw_stats);
- bnad_cb_stats_get(bna->bnad, BNA_CB_SUCCESS, &bna->stats);
- } else
- bnad_cb_stats_get(bna->bnad, BNA_CB_FAIL, &bna->stats);
-}
-
-static void
-bna_fw_stats_get(struct bna *bna)
-{
- struct bfi_ll_stats_req ll_req;
-
- bfi_h2i_set(ll_req.mh, BFI_MC_LL, BFI_LL_H2I_STATS_GET_REQ, 0);
- ll_req.stats_mask = htons(BFI_LL_STATS_ALL);
-
- ll_req.rxf_id_mask[0] = htonl(bna->rx_mod.rxf_bmap[0]);
- ll_req.rxf_id_mask[1] = htonl(bna->rx_mod.rxf_bmap[1]);
- ll_req.txf_id_mask[0] = htonl(bna->tx_mod.txf_bmap[0]);
- ll_req.txf_id_mask[1] = htonl(bna->tx_mod.txf_bmap[1]);
-
- ll_req.host_buffer.a32.addr_hi = bna->hw_stats_dma.msb;
- ll_req.host_buffer.a32.addr_lo = bna->hw_stats_dma.lsb;
-
- bna_mbox_qe_fill(&bna->mbox_qe, &ll_req, sizeof(ll_req),
- bna_fw_cb_stats_get, bna);
- bna_mbox_send(bna, &bna->mbox_qe);
-
- bna->stats.rxf_bmap[0] = bna->rx_mod.rxf_bmap[0];
- bna->stats.rxf_bmap[1] = bna->rx_mod.rxf_bmap[1];
- bna->stats.txf_bmap[0] = bna->tx_mod.txf_bmap[0];
- bna->stats.txf_bmap[1] = bna->tx_mod.txf_bmap[1];
-}
-
-void
-bna_stats_get(struct bna *bna)
-{
- if (bna_device_status_get(&bna->device))
- bna_fw_stats_get(bna);
- else
- bnad_cb_stats_get(bna->bnad, BNA_CB_FAIL, &bna->stats);
-}
-
-/* IB */
-static void
-bna_ib_coalescing_timeo_set(struct bna_ib *ib, u8 coalescing_timeo)
-{
- ib->ib_config.coalescing_timeo = coalescing_timeo;
-
- if (ib->start_count)
- ib->door_bell.doorbell_ack = BNA_DOORBELL_IB_INT_ACK(
- (u32)ib->ib_config.coalescing_timeo, 0);
-}
-
-/* RxF */
-void
-bna_rxf_adv_init(struct bna_rxf *rxf,
- struct bna_rx *rx,
- struct bna_rx_config *q_config)
-{
- switch (q_config->rxp_type) {
- case BNA_RXP_SINGLE:
- /* No-op */
- break;
- case BNA_RXP_SLR:
- rxf->ctrl_flags |= BNA_RXF_CF_SM_LG_RXQ;
- break;
- case BNA_RXP_HDS:
- rxf->hds_cfg.hdr_type = q_config->hds_config.hdr_type;
- rxf->hds_cfg.header_size =
- q_config->hds_config.header_size;
- rxf->forced_offset = 0;
- break;
- default:
- break;
- }
-
- if (q_config->rss_status == BNA_STATUS_T_ENABLED) {
- rxf->ctrl_flags |= BNA_RXF_CF_RSS_ENABLE;
- rxf->rss_cfg.hash_type = q_config->rss_config.hash_type;
- rxf->rss_cfg.hash_mask = q_config->rss_config.hash_mask;
- memcpy(&rxf->rss_cfg.toeplitz_hash_key[0],
- &q_config->rss_config.toeplitz_hash_key[0],
- sizeof(rxf->rss_cfg.toeplitz_hash_key));
- }
-}
-
-static void
-rxf_fltr_mbox_cmd(struct bna_rxf *rxf, u8 cmd, enum bna_status status)
-{
- struct bfi_ll_rxf_req req;
-
- bfi_h2i_set(req.mh, BFI_MC_LL, cmd, 0);
-
- req.rxf_id = rxf->rxf_id;
- req.enable = status;
-
- bna_mbox_qe_fill(&rxf->mbox_qe, &req, sizeof(req),
- rxf_cb_cam_fltr_mbox_cmd, rxf);
-
- bna_mbox_send(rxf->rx->bna, &rxf->mbox_qe);
-}
-
-int
-rxf_process_packet_filter_ucast(struct bna_rxf *rxf)
-{
- struct bna_mac *mac = NULL;
- struct list_head *qe;
-
- /* Add additional MAC entries */
- if (!list_empty(&rxf->ucast_pending_add_q)) {
- bfa_q_deq(&rxf->ucast_pending_add_q, &qe);
- bfa_q_qe_init(qe);
- mac = (struct bna_mac *)qe;
- rxf_cam_mbox_cmd(rxf, BFI_LL_H2I_MAC_UCAST_ADD_REQ, mac);
- list_add_tail(&mac->qe, &rxf->ucast_active_q);
- return 1;
- }
-
- /* Delete MAC addresses previousely added */
- if (!list_empty(&rxf->ucast_pending_del_q)) {
- bfa_q_deq(&rxf->ucast_pending_del_q, &qe);
- bfa_q_qe_init(qe);
- mac = (struct bna_mac *)qe;
- rxf_cam_mbox_cmd(rxf, BFI_LL_H2I_MAC_UCAST_DEL_REQ, mac);
- bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
- return 1;
- }
-
- return 0;
-}
-
-int
-rxf_process_packet_filter_promisc(struct bna_rxf *rxf)
-{
- struct bna *bna = rxf->rx->bna;
-
- /* Enable/disable promiscuous mode */
- if (is_promisc_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- /* move promisc configuration from pending -> active */
- promisc_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active |= BNA_RXMODE_PROMISC;
-
- /* Disable VLAN filter to allow all VLANs */
- __rxf_vlan_filter_set(rxf, BNA_STATUS_T_DISABLED);
- rxf_fltr_mbox_cmd(rxf, BFI_LL_H2I_RXF_PROMISCUOUS_SET_REQ,
- BNA_STATUS_T_ENABLED);
- return 1;
- } else if (is_promisc_disable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- /* move promisc configuration from pending -> active */
- promisc_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
- bna->rxf_promisc_id = BFI_MAX_RXF;
-
- /* Revert VLAN filter */
- __rxf_vlan_filter_set(rxf, rxf->vlan_filter_status);
- rxf_fltr_mbox_cmd(rxf, BFI_LL_H2I_RXF_PROMISCUOUS_SET_REQ,
- BNA_STATUS_T_DISABLED);
- return 1;
- }
-
- return 0;
-}
-
-int
-rxf_process_packet_filter_allmulti(struct bna_rxf *rxf)
-{
- /* Enable/disable allmulti mode */
- if (is_allmulti_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- /* move allmulti configuration from pending -> active */
- allmulti_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active |= BNA_RXMODE_ALLMULTI;
-
- rxf_fltr_mbox_cmd(rxf, BFI_LL_H2I_MAC_MCAST_FILTER_REQ,
- BNA_STATUS_T_ENABLED);
- return 1;
- } else if (is_allmulti_disable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- /* move allmulti configuration from pending -> active */
- allmulti_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
-
- rxf_fltr_mbox_cmd(rxf, BFI_LL_H2I_MAC_MCAST_FILTER_REQ,
- BNA_STATUS_T_DISABLED);
- return 1;
- }
-
- return 0;
-}
-
-int
-rxf_clear_packet_filter_ucast(struct bna_rxf *rxf)
-{
- struct bna_mac *mac = NULL;
- struct list_head *qe;
-
- /* 1. delete pending ucast entries */
- if (!list_empty(&rxf->ucast_pending_del_q)) {
- bfa_q_deq(&rxf->ucast_pending_del_q, &qe);
- bfa_q_qe_init(qe);
- mac = (struct bna_mac *)qe;
- rxf_cam_mbox_cmd(rxf, BFI_LL_H2I_MAC_UCAST_DEL_REQ, mac);
- bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
- return 1;
- }
-
- /* 2. clear active ucast entries; move them to pending_add_q */
- if (!list_empty(&rxf->ucast_active_q)) {
- bfa_q_deq(&rxf->ucast_active_q, &qe);
- bfa_q_qe_init(qe);
- mac = (struct bna_mac *)qe;
- rxf_cam_mbox_cmd(rxf, BFI_LL_H2I_MAC_UCAST_DEL_REQ, mac);
- list_add_tail(&mac->qe, &rxf->ucast_pending_add_q);
- return 1;
- }
-
- return 0;
-}
-
-int
-rxf_clear_packet_filter_promisc(struct bna_rxf *rxf)
-{
- struct bna *bna = rxf->rx->bna;
-
- /* 6. Execute pending promisc mode disable command */
- if (is_promisc_disable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- /* move promisc configuration from pending -> active */
- promisc_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
- bna->rxf_promisc_id = BFI_MAX_RXF;
-
- /* Revert VLAN filter */
- __rxf_vlan_filter_set(rxf, rxf->vlan_filter_status);
- rxf_fltr_mbox_cmd(rxf, BFI_LL_H2I_RXF_PROMISCUOUS_SET_REQ,
- BNA_STATUS_T_DISABLED);
- return 1;
- }
-
- /* 7. Clear active promisc mode; move it to pending enable */
- if (rxf->rxmode_active & BNA_RXMODE_PROMISC) {
- /* move promisc configuration from active -> pending */
- promisc_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
-
- /* Revert VLAN filter */
- __rxf_vlan_filter_set(rxf, rxf->vlan_filter_status);
- rxf_fltr_mbox_cmd(rxf, BFI_LL_H2I_RXF_PROMISCUOUS_SET_REQ,
- BNA_STATUS_T_DISABLED);
- return 1;
- }
-
- return 0;
-}
-
-int
-rxf_clear_packet_filter_allmulti(struct bna_rxf *rxf)
-{
- /* 10. Execute pending allmulti mode disable command */
- if (is_allmulti_disable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- /* move allmulti configuration from pending -> active */
- allmulti_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
- rxf_fltr_mbox_cmd(rxf, BFI_LL_H2I_MAC_MCAST_FILTER_REQ,
- BNA_STATUS_T_DISABLED);
- return 1;
- }
-
- /* 11. Clear active allmulti mode; move it to pending enable */
- if (rxf->rxmode_active & BNA_RXMODE_ALLMULTI) {
- /* move allmulti configuration from active -> pending */
- allmulti_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
- rxf_fltr_mbox_cmd(rxf, BFI_LL_H2I_MAC_MCAST_FILTER_REQ,
- BNA_STATUS_T_DISABLED);
- return 1;
- }
-
- return 0;
-}
-
-void
-rxf_reset_packet_filter_ucast(struct bna_rxf *rxf)
-{
- struct list_head *qe;
- struct bna_mac *mac;
-
- /* 1. Move active ucast entries to pending_add_q */
- while (!list_empty(&rxf->ucast_active_q)) {
- bfa_q_deq(&rxf->ucast_active_q, &qe);
- bfa_q_qe_init(qe);
- list_add_tail(qe, &rxf->ucast_pending_add_q);
- }
-
- /* 2. Throw away delete pending ucast entries */
- while (!list_empty(&rxf->ucast_pending_del_q)) {
- bfa_q_deq(&rxf->ucast_pending_del_q, &qe);
- bfa_q_qe_init(qe);
- mac = (struct bna_mac *)qe;
- bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
- }
-}
-
-void
-rxf_reset_packet_filter_promisc(struct bna_rxf *rxf)
-{
- struct bna *bna = rxf->rx->bna;
-
- /* 6. Clear pending promisc mode disable */
- if (is_promisc_disable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- promisc_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
- bna->rxf_promisc_id = BFI_MAX_RXF;
- }
-
- /* 7. Move promisc mode config from active -> pending */
- if (rxf->rxmode_active & BNA_RXMODE_PROMISC) {
- promisc_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
- }
-
-}
-
-void
-rxf_reset_packet_filter_allmulti(struct bna_rxf *rxf)
-{
- /* 10. Clear pending allmulti mode disable */
- if (is_allmulti_disable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- allmulti_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
- }
-
- /* 11. Move allmulti mode config from active -> pending */
- if (rxf->rxmode_active & BNA_RXMODE_ALLMULTI) {
- allmulti_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
- }
-}
-
-/**
- * Should only be called by bna_rxf_mode_set.
- * Helps deciding if h/w configuration is needed or not.
- * Returns:
- * 0 = no h/w change
- * 1 = need h/w change
- */
-static int
-rxf_promisc_enable(struct bna_rxf *rxf)
-{
- struct bna *bna = rxf->rx->bna;
- int ret = 0;
-
- /* There can not be any pending disable command */
-
- /* Do nothing if pending enable or already enabled */
- if (is_promisc_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask) ||
- (rxf->rxmode_active & BNA_RXMODE_PROMISC)) {
- /* Schedule enable */
- } else {
- /* Promisc mode should not be active in the system */
- promisc_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- bna->rxf_promisc_id = rxf->rxf_id;
- ret = 1;
- }
-
- return ret;
-}
-
-/**
- * Should only be called by bna_rxf_mode_set.
- * Helps deciding if h/w configuration is needed or not.
- * Returns:
- * 0 = no h/w change
- * 1 = need h/w change
- */
-static int
-rxf_promisc_disable(struct bna_rxf *rxf)
-{
- struct bna *bna = rxf->rx->bna;
- int ret = 0;
-
- /* There can not be any pending disable */
-
- /* Turn off pending enable command , if any */
- if (is_promisc_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- /* Promisc mode should not be active */
- /* system promisc state should be pending */
- promisc_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- /* Remove the promisc state from the system */
- bna->rxf_promisc_id = BFI_MAX_RXF;
-
- /* Schedule disable */
- } else if (rxf->rxmode_active & BNA_RXMODE_PROMISC) {
- /* Promisc mode should be active in the system */
- promisc_disable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- ret = 1;
-
- /* Do nothing if already disabled */
- } else {
- }
-
- return ret;
-}
-
-/**
- * Should only be called by bna_rxf_mode_set.
- * Helps deciding if h/w configuration is needed or not.
- * Returns:
- * 0 = no h/w change
- * 1 = need h/w change
- */
-static int
-rxf_allmulti_enable(struct bna_rxf *rxf)
-{
- int ret = 0;
-
- /* There can not be any pending disable command */
-
- /* Do nothing if pending enable or already enabled */
- if (is_allmulti_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask) ||
- (rxf->rxmode_active & BNA_RXMODE_ALLMULTI)) {
- /* Schedule enable */
- } else {
- allmulti_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- ret = 1;
- }
-
- return ret;
-}
-
-/**
- * Should only be called by bna_rxf_mode_set.
- * Helps deciding if h/w configuration is needed or not.
- * Returns:
- * 0 = no h/w change
- * 1 = need h/w change
- */
-static int
-rxf_allmulti_disable(struct bna_rxf *rxf)
-{
- int ret = 0;
-
- /* There can not be any pending disable */
-
- /* Turn off pending enable command , if any */
- if (is_allmulti_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- /* Allmulti mode should not be active */
- allmulti_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
-
- /* Schedule disable */
- } else if (rxf->rxmode_active & BNA_RXMODE_ALLMULTI) {
- allmulti_disable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- ret = 1;
- }
-
- return ret;
-}
-
-/* RxF <- bnad */
-enum bna_cb_status
-bna_rx_mode_set(struct bna_rx *rx, enum bna_rxmode new_mode,
- enum bna_rxmode bitmask,
- void (*cbfn)(struct bnad *, struct bna_rx *,
- enum bna_cb_status))
-{
- struct bna_rxf *rxf = &rx->rxf;
- int need_hw_config = 0;
-
- /* Process the commands */
-
- if (is_promisc_enable(new_mode, bitmask)) {
- /* If promisc mode is already enabled elsewhere in the system */
- if ((rx->bna->rxf_promisc_id != BFI_MAX_RXF) &&
- (rx->bna->rxf_promisc_id != rxf->rxf_id))
- goto err_return;
- if (rxf_promisc_enable(rxf))
- need_hw_config = 1;
- } else if (is_promisc_disable(new_mode, bitmask)) {
- if (rxf_promisc_disable(rxf))
- need_hw_config = 1;
- }
-
- if (is_allmulti_enable(new_mode, bitmask)) {
- if (rxf_allmulti_enable(rxf))
- need_hw_config = 1;
- } else if (is_allmulti_disable(new_mode, bitmask)) {
- if (rxf_allmulti_disable(rxf))
- need_hw_config = 1;
- }
-
- /* Trigger h/w if needed */
-
- if (need_hw_config) {
- rxf->cam_fltr_cbfn = cbfn;
- rxf->cam_fltr_cbarg = rx->bna->bnad;
- bfa_fsm_send_event(rxf, RXF_E_CAM_FLTR_MOD);
- } else if (cbfn)
- (*cbfn)(rx->bna->bnad, rx, BNA_CB_SUCCESS);
-
- return BNA_CB_SUCCESS;
-
-err_return:
- return BNA_CB_FAIL;
-}
-
-void
-/* RxF <- bnad */
-bna_rx_vlanfilter_enable(struct bna_rx *rx)
-{
- struct bna_rxf *rxf = &rx->rxf;
-
- if (rxf->vlan_filter_status == BNA_STATUS_T_DISABLED) {
- rxf->rxf_flags |= BNA_RXF_FL_VLAN_CONFIG_PENDING;
- rxf->vlan_filter_status = BNA_STATUS_T_ENABLED;
- bfa_fsm_send_event(rxf, RXF_E_CAM_FLTR_MOD);
- }
-}
-
-/* Rx */
-
-/* Rx <- bnad */
-void
-bna_rx_coalescing_timeo_set(struct bna_rx *rx, int coalescing_timeo)
-{
- struct bna_rxp *rxp;
- struct list_head *qe;
-
- list_for_each(qe, &rx->rxp_q) {
- rxp = (struct bna_rxp *)qe;
- rxp->cq.ccb->rx_coalescing_timeo = coalescing_timeo;
- bna_ib_coalescing_timeo_set(rxp->cq.ib, coalescing_timeo);
- }
-}
-
-/* Rx <- bnad */
-void
-bna_rx_dim_reconfig(struct bna *bna, const u32 vector[][BNA_BIAS_T_MAX])
-{
- int i, j;
-
- for (i = 0; i < BNA_LOAD_T_MAX; i++)
- for (j = 0; j < BNA_BIAS_T_MAX; j++)
- bna->rx_mod.dim_vector[i][j] = vector[i][j];
-}
-
-/* Rx <- bnad */
-void
-bna_rx_dim_update(struct bna_ccb *ccb)
-{
- struct bna *bna = ccb->cq->rx->bna;
- u32 load, bias;
- u32 pkt_rt, small_rt, large_rt;
- u8 coalescing_timeo;
-
- if ((ccb->pkt_rate.small_pkt_cnt == 0) &&
- (ccb->pkt_rate.large_pkt_cnt == 0))
- return;
-
- /* Arrive at preconfigured coalescing timeo value based on pkt rate */
-
- small_rt = ccb->pkt_rate.small_pkt_cnt;
- large_rt = ccb->pkt_rate.large_pkt_cnt;
-
- pkt_rt = small_rt + large_rt;
-
- if (pkt_rt < BNA_PKT_RATE_10K)
- load = BNA_LOAD_T_LOW_4;
- else if (pkt_rt < BNA_PKT_RATE_20K)
- load = BNA_LOAD_T_LOW_3;
- else if (pkt_rt < BNA_PKT_RATE_30K)
- load = BNA_LOAD_T_LOW_2;
- else if (pkt_rt < BNA_PKT_RATE_40K)
- load = BNA_LOAD_T_LOW_1;
- else if (pkt_rt < BNA_PKT_RATE_50K)
- load = BNA_LOAD_T_HIGH_1;
- else if (pkt_rt < BNA_PKT_RATE_60K)
- load = BNA_LOAD_T_HIGH_2;
- else if (pkt_rt < BNA_PKT_RATE_80K)
- load = BNA_LOAD_T_HIGH_3;
- else
- load = BNA_LOAD_T_HIGH_4;
-
- if (small_rt > (large_rt << 1))
- bias = 0;
- else
- bias = 1;
-
- ccb->pkt_rate.small_pkt_cnt = 0;
- ccb->pkt_rate.large_pkt_cnt = 0;
-
- coalescing_timeo = bna->rx_mod.dim_vector[load][bias];
- ccb->rx_coalescing_timeo = coalescing_timeo;
-
- /* Set it to IB */
- bna_ib_coalescing_timeo_set(ccb->cq->ib, coalescing_timeo);
-}
-
-/* Tx */
-/* TX <- bnad */
-void
-bna_tx_coalescing_timeo_set(struct bna_tx *tx, int coalescing_timeo)
-{
- struct bna_txq *txq;
- struct list_head *qe;
-
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- bna_ib_coalescing_timeo_set(txq->ib, coalescing_timeo);
- }
-}
-
-/*
- * Private data
- */
-
-struct bna_ritseg_pool_cfg {
- u32 pool_size;
- u32 pool_entry_size;
-};
-init_ritseg_pool(ritseg_pool_cfg);
-
-/*
- * Private functions
- */
-static void
-bna_ucam_mod_init(struct bna_ucam_mod *ucam_mod, struct bna *bna,
- struct bna_res_info *res_info)
-{
- int i;
-
- ucam_mod->ucmac = (struct bna_mac *)
- res_info[BNA_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.mdl[0].kva;
-
- INIT_LIST_HEAD(&ucam_mod->free_q);
- for (i = 0; i < BFI_MAX_UCMAC; i++) {
- bfa_q_qe_init(&ucam_mod->ucmac[i].qe);
- list_add_tail(&ucam_mod->ucmac[i].qe, &ucam_mod->free_q);
- }
-
- ucam_mod->bna = bna;
-}
-
-static void
-bna_ucam_mod_uninit(struct bna_ucam_mod *ucam_mod)
-{
- struct list_head *qe;
- int i = 0;
-
- list_for_each(qe, &ucam_mod->free_q)
- i++;
-
- ucam_mod->bna = NULL;
-}
-
-static void
-bna_mcam_mod_init(struct bna_mcam_mod *mcam_mod, struct bna *bna,
- struct bna_res_info *res_info)
-{
- int i;
-
- mcam_mod->mcmac = (struct bna_mac *)
- res_info[BNA_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.mdl[0].kva;
-
- INIT_LIST_HEAD(&mcam_mod->free_q);
- for (i = 0; i < BFI_MAX_MCMAC; i++) {
- bfa_q_qe_init(&mcam_mod->mcmac[i].qe);
- list_add_tail(&mcam_mod->mcmac[i].qe, &mcam_mod->free_q);
- }
-
- mcam_mod->bna = bna;
-}
-
-static void
-bna_mcam_mod_uninit(struct bna_mcam_mod *mcam_mod)
-{
- struct list_head *qe;
- int i = 0;
-
- list_for_each(qe, &mcam_mod->free_q)
- i++;
-
- mcam_mod->bna = NULL;
-}
-
-static void
-bna_rit_mod_init(struct bna_rit_mod *rit_mod,
- struct bna_res_info *res_info)
-{
- int i;
- int j;
- int count;
- int offset;
-
- rit_mod->rit = (struct bna_rit_entry *)
- res_info[BNA_RES_MEM_T_RIT_ENTRY].res_u.mem_info.mdl[0].kva;
- rit_mod->rit_segment = (struct bna_rit_segment *)
- res_info[BNA_RES_MEM_T_RIT_SEGMENT].res_u.mem_info.mdl[0].kva;
-
- count = 0;
- offset = 0;
- for (i = 0; i < BFI_RIT_SEG_TOTAL_POOLS; i++) {
- INIT_LIST_HEAD(&rit_mod->rit_seg_pool[i]);
- for (j = 0; j < ritseg_pool_cfg[i].pool_size; j++) {
- bfa_q_qe_init(&rit_mod->rit_segment[count].qe);
- rit_mod->rit_segment[count].max_rit_size =
- ritseg_pool_cfg[i].pool_entry_size;
- rit_mod->rit_segment[count].rit_offset = offset;
- rit_mod->rit_segment[count].rit =
- &rit_mod->rit[offset];
- list_add_tail(&rit_mod->rit_segment[count].qe,
- &rit_mod->rit_seg_pool[i]);
- count++;
- offset += ritseg_pool_cfg[i].pool_entry_size;
- }
- }
-}
-
-/*
- * Public functions
- */
-
-/* Called during probe(), before calling bna_init() */
-void
-bna_res_req(struct bna_res_info *res_info)
-{
- bna_adv_res_req(res_info);
-
- /* DMA memory for retrieving IOC attributes */
- res_info[BNA_RES_MEM_T_ATTR].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.mem_type = BNA_MEM_T_DMA;
- res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.len =
- ALIGN(bfa_nw_ioc_meminfo(), PAGE_SIZE);
-
- /* DMA memory for index segment of an IB */
- res_info[BNA_RES_MEM_T_IBIDX].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_IBIDX].res_u.mem_info.mem_type = BNA_MEM_T_DMA;
- res_info[BNA_RES_MEM_T_IBIDX].res_u.mem_info.len =
- BFI_IBIDX_SIZE * BFI_IBIDX_MAX_SEGSIZE;
- res_info[BNA_RES_MEM_T_IBIDX].res_u.mem_info.num = BFI_MAX_IB;
-
- /* Virtual memory for IB objects - stored by IB module */
- res_info[BNA_RES_MEM_T_IB_ARRAY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_IB_ARRAY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_IB_ARRAY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_IB_ARRAY].res_u.mem_info.len =
- BFI_MAX_IB * sizeof(struct bna_ib);
-
- /* Virtual memory for intr objects - stored by IB module */
- res_info[BNA_RES_MEM_T_INTR_ARRAY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_INTR_ARRAY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_INTR_ARRAY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_INTR_ARRAY].res_u.mem_info.len =
- BFI_MAX_IB * sizeof(struct bna_intr);
-
- /* Virtual memory for idx_seg objects - stored by IB module */
- res_info[BNA_RES_MEM_T_IDXSEG_ARRAY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_IDXSEG_ARRAY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_IDXSEG_ARRAY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_IDXSEG_ARRAY].res_u.mem_info.len =
- BFI_IBIDX_TOTAL_SEGS * sizeof(struct bna_ibidx_seg);
-
- /* Virtual memory for Tx objects - stored by Tx module */
- res_info[BNA_RES_MEM_T_TX_ARRAY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_TX_ARRAY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_TX_ARRAY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_TX_ARRAY].res_u.mem_info.len =
- BFI_MAX_TXQ * sizeof(struct bna_tx);
-
- /* Virtual memory for TxQ - stored by Tx module */
- res_info[BNA_RES_MEM_T_TXQ_ARRAY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.len =
- BFI_MAX_TXQ * sizeof(struct bna_txq);
-
- /* Virtual memory for Rx objects - stored by Rx module */
- res_info[BNA_RES_MEM_T_RX_ARRAY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_RX_ARRAY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_RX_ARRAY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_RX_ARRAY].res_u.mem_info.len =
- BFI_MAX_RXQ * sizeof(struct bna_rx);
-
- /* Virtual memory for RxPath - stored by Rx module */
- res_info[BNA_RES_MEM_T_RXP_ARRAY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_RXP_ARRAY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_RXP_ARRAY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_RXP_ARRAY].res_u.mem_info.len =
- BFI_MAX_RXQ * sizeof(struct bna_rxp);
-
- /* Virtual memory for RxQ - stored by Rx module */
- res_info[BNA_RES_MEM_T_RXQ_ARRAY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.len =
- BFI_MAX_RXQ * sizeof(struct bna_rxq);
-
- /* Virtual memory for Unicast MAC address - stored by ucam module */
- res_info[BNA_RES_MEM_T_UCMAC_ARRAY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.len =
- BFI_MAX_UCMAC * sizeof(struct bna_mac);
-
- /* Virtual memory for Multicast MAC address - stored by mcam module */
- res_info[BNA_RES_MEM_T_MCMAC_ARRAY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.len =
- BFI_MAX_MCMAC * sizeof(struct bna_mac);
-
- /* Virtual memory for RIT entries */
- res_info[BNA_RES_MEM_T_RIT_ENTRY].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_RIT_ENTRY].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_RIT_ENTRY].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_RIT_ENTRY].res_u.mem_info.len =
- BFI_MAX_RIT_SIZE * sizeof(struct bna_rit_entry);
-
- /* Virtual memory for RIT segment table */
- res_info[BNA_RES_MEM_T_RIT_SEGMENT].res_type = BNA_RES_T_MEM;
- res_info[BNA_RES_MEM_T_RIT_SEGMENT].res_u.mem_info.mem_type =
- BNA_MEM_T_KVA;
- res_info[BNA_RES_MEM_T_RIT_SEGMENT].res_u.mem_info.num = 1;
- res_info[BNA_RES_MEM_T_RIT_SEGMENT].res_u.mem_info.len =
- BFI_RIT_TOTAL_SEGS * sizeof(struct bna_rit_segment);
-
- /* Interrupt resource for mailbox interrupt */
- res_info[BNA_RES_INTR_T_MBOX].res_type = BNA_RES_T_INTR;
- res_info[BNA_RES_INTR_T_MBOX].res_u.intr_info.intr_type =
- BNA_INTR_T_MSIX;
- res_info[BNA_RES_INTR_T_MBOX].res_u.intr_info.num = 1;
-}
-
-/* Called during probe() */
-void
-bna_init(struct bna *bna, struct bnad *bnad, struct bfa_pcidev *pcidev,
- struct bna_res_info *res_info)
-{
- bna->bnad = bnad;
- bna->pcidev = *pcidev;
-
- bna->stats.hw_stats = (struct bfi_ll_stats *)
- res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mdl[0].kva;
- bna->hw_stats_dma.msb =
- res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mdl[0].dma.msb;
- bna->hw_stats_dma.lsb =
- res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mdl[0].dma.lsb;
- bna->stats.sw_stats = (struct bna_sw_stats *)
- res_info[BNA_RES_MEM_T_SWSTATS].res_u.mem_info.mdl[0].kva;
-
- bna->regs.page_addr = bna->pcidev.pci_bar_kva +
- reg_offset[bna->pcidev.pci_func].page_addr;
- bna->regs.fn_int_status = bna->pcidev.pci_bar_kva +
- reg_offset[bna->pcidev.pci_func].fn_int_status;
- bna->regs.fn_int_mask = bna->pcidev.pci_bar_kva +
- reg_offset[bna->pcidev.pci_func].fn_int_mask;
-
- if (bna->pcidev.pci_func < 3)
- bna->port_num = 0;
- else
- bna->port_num = 1;
-
- /* Also initializes diag, cee, sfp, phy_port and mbox_mod */
- bna_device_init(&bna->device, bna, res_info);
-
- bna_port_init(&bna->port, bna);
-
- bna_tx_mod_init(&bna->tx_mod, bna, res_info);
-
- bna_rx_mod_init(&bna->rx_mod, bna, res_info);
-
- bna_ib_mod_init(&bna->ib_mod, bna, res_info);
-
- bna_rit_mod_init(&bna->rit_mod, res_info);
-
- bna_ucam_mod_init(&bna->ucam_mod, bna, res_info);
-
- bna_mcam_mod_init(&bna->mcam_mod, bna, res_info);
-
- bna->rxf_promisc_id = BFI_MAX_RXF;
-
- /* Mbox q element for posting stat request to f/w */
- bfa_q_qe_init(&bna->mbox_qe.qe);
-}
-
-void
-bna_uninit(struct bna *bna)
-{
- bna_mcam_mod_uninit(&bna->mcam_mod);
-
- bna_ucam_mod_uninit(&bna->ucam_mod);
-
- bna_ib_mod_uninit(&bna->ib_mod);
-
- bna_rx_mod_uninit(&bna->rx_mod);
-
- bna_tx_mod_uninit(&bna->tx_mod);
-
- bna_port_uninit(&bna->port);
-
- bna_device_uninit(&bna->device);
-
- bna->bnad = NULL;
-}
-
-struct bna_mac *
-bna_ucam_mod_mac_get(struct bna_ucam_mod *ucam_mod)
-{
- struct list_head *qe;
-
- if (list_empty(&ucam_mod->free_q))
- return NULL;
-
- bfa_q_deq(&ucam_mod->free_q, &qe);
-
- return (struct bna_mac *)qe;
-}
-
-void
-bna_ucam_mod_mac_put(struct bna_ucam_mod *ucam_mod, struct bna_mac *mac)
-{
- list_add_tail(&mac->qe, &ucam_mod->free_q);
-}
-
-struct bna_mac *
-bna_mcam_mod_mac_get(struct bna_mcam_mod *mcam_mod)
-{
- struct list_head *qe;
-
- if (list_empty(&mcam_mod->free_q))
- return NULL;
-
- bfa_q_deq(&mcam_mod->free_q, &qe);
-
- return (struct bna_mac *)qe;
-}
-
-void
-bna_mcam_mod_mac_put(struct bna_mcam_mod *mcam_mod, struct bna_mac *mac)
-{
- list_add_tail(&mac->qe, &mcam_mod->free_q);
-}
-
-/**
- * Note: This should be called in the same locking context as the call to
- * bna_rit_mod_seg_get()
- */
-int
-bna_rit_mod_can_satisfy(struct bna_rit_mod *rit_mod, int seg_size)
-{
- int i;
-
- /* Select the pool for seg_size */
- for (i = 0; i < BFI_RIT_SEG_TOTAL_POOLS; i++) {
- if (seg_size <= ritseg_pool_cfg[i].pool_entry_size)
- break;
- }
-
- if (i == BFI_RIT_SEG_TOTAL_POOLS)
- return 0;
-
- if (list_empty(&rit_mod->rit_seg_pool[i]))
- return 0;
-
- return 1;
-}
-
-struct bna_rit_segment *
-bna_rit_mod_seg_get(struct bna_rit_mod *rit_mod, int seg_size)
-{
- struct bna_rit_segment *seg;
- struct list_head *qe;
- int i;
-
- /* Select the pool for seg_size */
- for (i = 0; i < BFI_RIT_SEG_TOTAL_POOLS; i++) {
- if (seg_size <= ritseg_pool_cfg[i].pool_entry_size)
- break;
- }
-
- if (i == BFI_RIT_SEG_TOTAL_POOLS)
- return NULL;
-
- if (list_empty(&rit_mod->rit_seg_pool[i]))
- return NULL;
-
- bfa_q_deq(&rit_mod->rit_seg_pool[i], &qe);
- seg = (struct bna_rit_segment *)qe;
- bfa_q_qe_init(&seg->qe);
- seg->rit_size = seg_size;
-
- return seg;
-}
-
-void
-bna_rit_mod_seg_put(struct bna_rit_mod *rit_mod,
- struct bna_rit_segment *seg)
-{
- int i;
-
- /* Select the pool for seg->max_rit_size */
- for (i = 0; i < BFI_RIT_SEG_TOTAL_POOLS; i++) {
- if (seg->max_rit_size == ritseg_pool_cfg[i].pool_entry_size)
- break;
- }
-
- seg->rit_size = 0;
- list_add_tail(&seg->qe, &rit_mod->rit_seg_pool[i]);
-}
diff --git a/drivers/net/bna/bna_hw.h b/drivers/net/bna/bna_hw.h
deleted file mode 100644
index 6cb89692f5c1..000000000000
--- a/drivers/net/bna/bna_hw.h
+++ /dev/null
@@ -1,1490 +0,0 @@
-/*
- * Linux network driver for Brocade Converged Network Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
- * All rights reserved
- * www.brocade.com
- *
- * File for interrupt macros and functions
- */
-
-#ifndef __BNA_HW_H__
-#define __BNA_HW_H__
-
-#include "bfi_ctreg.h"
-
-/**
- *
- * SW imposed limits
- *
- */
-
-#ifndef BNA_BIOS_BUILD
-
-#define BFI_MAX_TXQ 64
-#define BFI_MAX_RXQ 64
-#define BFI_MAX_RXF 64
-#define BFI_MAX_IB 128
-#define BFI_MAX_RIT_SIZE 256
-#define BFI_RSS_RIT_SIZE 64
-#define BFI_NONRSS_RIT_SIZE 1
-#define BFI_MAX_UCMAC 256
-#define BFI_MAX_MCMAC 512
-#define BFI_IBIDX_SIZE 4
-#define BFI_MAX_VLAN 4095
-
-/**
- * There are 2 free IB index pools:
- * pool1: 120 segments of 1 index each
- * pool8: 1 segment of 8 indexes
- */
-#define BFI_IBIDX_POOL1_SIZE 116
-#define BFI_IBIDX_POOL1_ENTRY_SIZE 1
-#define BFI_IBIDX_POOL2_SIZE 2
-#define BFI_IBIDX_POOL2_ENTRY_SIZE 2
-#define BFI_IBIDX_POOL8_SIZE 1
-#define BFI_IBIDX_POOL8_ENTRY_SIZE 8
-#define BFI_IBIDX_TOTAL_POOLS 3
-#define BFI_IBIDX_TOTAL_SEGS 119 /* (POOL1 + POOL2 + POOL8)_SIZE */
-#define BFI_IBIDX_MAX_SEGSIZE 8
-#define init_ibidx_pool(name) \
-static struct bna_ibidx_pool name[BFI_IBIDX_TOTAL_POOLS] = \
-{ \
- { BFI_IBIDX_POOL1_SIZE, BFI_IBIDX_POOL1_ENTRY_SIZE }, \
- { BFI_IBIDX_POOL2_SIZE, BFI_IBIDX_POOL2_ENTRY_SIZE }, \
- { BFI_IBIDX_POOL8_SIZE, BFI_IBIDX_POOL8_ENTRY_SIZE } \
-}
-
-/**
- * There are 2 free RIT segment pools:
- * Pool1: 192 segments of 1 RIT entry each
- * Pool2: 1 segment of 64 RIT entry
- */
-#define BFI_RIT_SEG_POOL1_SIZE 192
-#define BFI_RIT_SEG_POOL1_ENTRY_SIZE 1
-#define BFI_RIT_SEG_POOLRSS_SIZE 1
-#define BFI_RIT_SEG_POOLRSS_ENTRY_SIZE 64
-#define BFI_RIT_SEG_TOTAL_POOLS 2
-#define BFI_RIT_TOTAL_SEGS 193 /* POOL1_SIZE + POOLRSS_SIZE */
-#define init_ritseg_pool(name) \
-static struct bna_ritseg_pool_cfg name[BFI_RIT_SEG_TOTAL_POOLS] = \
-{ \
- { BFI_RIT_SEG_POOL1_SIZE, BFI_RIT_SEG_POOL1_ENTRY_SIZE }, \
- { BFI_RIT_SEG_POOLRSS_SIZE, BFI_RIT_SEG_POOLRSS_ENTRY_SIZE } \
-}
-
-#else /* BNA_BIOS_BUILD */
-
-#define BFI_MAX_TXQ 1
-#define BFI_MAX_RXQ 1
-#define BFI_MAX_RXF 1
-#define BFI_MAX_IB 2
-#define BFI_MAX_RIT_SIZE 2
-#define BFI_RSS_RIT_SIZE 64
-#define BFI_NONRSS_RIT_SIZE 1
-#define BFI_MAX_UCMAC 1
-#define BFI_MAX_MCMAC 8
-#define BFI_IBIDX_SIZE 4
-#define BFI_MAX_VLAN 4095
-/* There is one free pool: 2 segments of 1 index each */
-#define BFI_IBIDX_POOL1_SIZE 2
-#define BFI_IBIDX_POOL1_ENTRY_SIZE 1
-#define BFI_IBIDX_TOTAL_POOLS 1
-#define BFI_IBIDX_TOTAL_SEGS 2 /* POOL1_SIZE */
-#define BFI_IBIDX_MAX_SEGSIZE 1
-#define init_ibidx_pool(name) \
-static struct bna_ibidx_pool name[BFI_IBIDX_TOTAL_POOLS] = \
-{ \
- { BFI_IBIDX_POOL1_SIZE, BFI_IBIDX_POOL1_ENTRY_SIZE } \
-}
-
-#define BFI_RIT_SEG_POOL1_SIZE 1
-#define BFI_RIT_SEG_POOL1_ENTRY_SIZE 1
-#define BFI_RIT_SEG_TOTAL_POOLS 1
-#define BFI_RIT_TOTAL_SEGS 1 /* POOL1_SIZE */
-#define init_ritseg_pool(name) \
-static struct bna_ritseg_pool_cfg name[BFI_RIT_SEG_TOTAL_POOLS] = \
-{ \
- { BFI_RIT_SEG_POOL1_SIZE, BFI_RIT_SEG_POOL1_ENTRY_SIZE } \
-}
-
-#endif /* BNA_BIOS_BUILD */
-
-#define BFI_RSS_HASH_KEY_LEN 10
-
-#define BFI_COALESCING_TIMER_UNIT 5 /* 5us */
-#define BFI_MAX_COALESCING_TIMEO 0xFF /* in 5us units */
-#define BFI_MAX_INTERPKT_COUNT 0xFF
-#define BFI_MAX_INTERPKT_TIMEO 0xF /* in 0.5us units */
-#define BFI_TX_COALESCING_TIMEO 20 /* 20 * 5 = 100us */
-#define BFI_TX_INTERPKT_COUNT 32
-#define BFI_RX_COALESCING_TIMEO 12 /* 12 * 5 = 60us */
-#define BFI_RX_INTERPKT_COUNT 6 /* Pkt Cnt = 6 */
-#define BFI_RX_INTERPKT_TIMEO 3 /* 3 * 0.5 = 1.5us */
-
-#define BFI_TXQ_WI_SIZE 64 /* bytes */
-#define BFI_RXQ_WI_SIZE 8 /* bytes */
-#define BFI_CQ_WI_SIZE 16 /* bytes */
-#define BFI_TX_MAX_WRR_QUOTA 0xFFF
-
-#define BFI_TX_MAX_VECTORS_PER_WI 4
-#define BFI_TX_MAX_VECTORS_PER_PKT 0xFF
-#define BFI_TX_MAX_DATA_PER_VECTOR 0xFFFF
-#define BFI_TX_MAX_DATA_PER_PKT 0xFFFFFF
-
-/* Small Q buffer size */
-#define BFI_SMALL_RXBUF_SIZE 128
-
-/* Defined separately since BFA_FLASH_DMA_BUF_SZ is in bfa_flash.c */
-#define BFI_FLASH_DMA_BUF_SZ 0x010000 /* 64K DMA */
-#define BFI_HW_STATS_SIZE 0x4000 /* 16K DMA */
-
-/**
- *
- * HW register offsets, macros
- *
- */
-
-/* DMA Block Register Host Window Start Address */
-#define DMA_BLK_REG_ADDR 0x00013000
-
-/* DMA Block Internal Registers */
-#define DMA_CTRL_REG0 (DMA_BLK_REG_ADDR + 0x000)
-#define DMA_CTRL_REG1 (DMA_BLK_REG_ADDR + 0x004)
-#define DMA_ERR_INT_STATUS (DMA_BLK_REG_ADDR + 0x008)
-#define DMA_ERR_INT_ENABLE (DMA_BLK_REG_ADDR + 0x00c)
-#define DMA_ERR_INT_STATUS_SET (DMA_BLK_REG_ADDR + 0x010)
-
-/* APP Block Register Address Offset from BAR0 */
-#define APP_BLK_REG_ADDR 0x00014000
-
-/* Host Function Interrupt Mask Registers */
-#define HOSTFN0_INT_MASK (APP_BLK_REG_ADDR + 0x004)
-#define HOSTFN1_INT_MASK (APP_BLK_REG_ADDR + 0x104)
-#define HOSTFN2_INT_MASK (APP_BLK_REG_ADDR + 0x304)
-#define HOSTFN3_INT_MASK (APP_BLK_REG_ADDR + 0x404)
-
-/**
- * Host Function PCIe Error Registers
- * Duplicates "Correctable" & "Uncorrectable"
- * registers in PCIe Config space.
- */
-#define FN0_PCIE_ERR_REG (APP_BLK_REG_ADDR + 0x014)
-#define FN1_PCIE_ERR_REG (APP_BLK_REG_ADDR + 0x114)
-#define FN2_PCIE_ERR_REG (APP_BLK_REG_ADDR + 0x314)
-#define FN3_PCIE_ERR_REG (APP_BLK_REG_ADDR + 0x414)
-
-/* Host Function Error Type Status Registers */
-#define FN0_ERR_TYPE_STATUS_REG (APP_BLK_REG_ADDR + 0x018)
-#define FN1_ERR_TYPE_STATUS_REG (APP_BLK_REG_ADDR + 0x118)
-#define FN2_ERR_TYPE_STATUS_REG (APP_BLK_REG_ADDR + 0x318)
-#define FN3_ERR_TYPE_STATUS_REG (APP_BLK_REG_ADDR + 0x418)
-
-/* Host Function Error Type Mask Registers */
-#define FN0_ERR_TYPE_MSK_STATUS_REG (APP_BLK_REG_ADDR + 0x01c)
-#define FN1_ERR_TYPE_MSK_STATUS_REG (APP_BLK_REG_ADDR + 0x11c)
-#define FN2_ERR_TYPE_MSK_STATUS_REG (APP_BLK_REG_ADDR + 0x31c)
-#define FN3_ERR_TYPE_MSK_STATUS_REG (APP_BLK_REG_ADDR + 0x41c)
-
-/* Catapult Host Semaphore Status Registers (App block) */
-#define HOST_SEM_STS0_REG (APP_BLK_REG_ADDR + 0x630)
-#define HOST_SEM_STS1_REG (APP_BLK_REG_ADDR + 0x634)
-#define HOST_SEM_STS2_REG (APP_BLK_REG_ADDR + 0x638)
-#define HOST_SEM_STS3_REG (APP_BLK_REG_ADDR + 0x63c)
-#define HOST_SEM_STS4_REG (APP_BLK_REG_ADDR + 0x640)
-#define HOST_SEM_STS5_REG (APP_BLK_REG_ADDR + 0x644)
-#define HOST_SEM_STS6_REG (APP_BLK_REG_ADDR + 0x648)
-#define HOST_SEM_STS7_REG (APP_BLK_REG_ADDR + 0x64c)
-
-/* PCIe Misc Register */
-#define PCIE_MISC_REG (APP_BLK_REG_ADDR + 0x200)
-
-/* Temp Sensor Control Registers */
-#define TEMPSENSE_CNTL_REG (APP_BLK_REG_ADDR + 0x250)
-#define TEMPSENSE_STAT_REG (APP_BLK_REG_ADDR + 0x254)
-
-/* APP Block local error registers */
-#define APP_LOCAL_ERR_STAT (APP_BLK_REG_ADDR + 0x258)
-#define APP_LOCAL_ERR_MSK (APP_BLK_REG_ADDR + 0x25c)
-
-/* PCIe Link Error registers */
-#define PCIE_LNK_ERR_STAT (APP_BLK_REG_ADDR + 0x260)
-#define PCIE_LNK_ERR_MSK (APP_BLK_REG_ADDR + 0x264)
-
-/**
- * FCoE/FIP Ethertype Register
- * 31:16 -- Chip wide value for FIP type
- * 15:0 -- Chip wide value for FCoE type
- */
-#define FCOE_FIP_ETH_TYPE (APP_BLK_REG_ADDR + 0x280)
-
-/**
- * Reserved Ethertype Register
- * 31:16 -- Reserved
- * 15:0 -- Other ethertype
- */
-#define RESV_ETH_TYPE (APP_BLK_REG_ADDR + 0x284)
-
-/**
- * Host Command Status Registers
- * Each set consists of 3 registers :
- * clear, set, cmd
- * 16 such register sets in all
- * See catapult_spec.pdf for detailed functionality
- * Put each type in a single macro accessed by _num ?
- */
-#define HOST_CMDSTS0_CLR_REG (APP_BLK_REG_ADDR + 0x500)
-#define HOST_CMDSTS0_SET_REG (APP_BLK_REG_ADDR + 0x504)
-#define HOST_CMDSTS0_REG (APP_BLK_REG_ADDR + 0x508)
-#define HOST_CMDSTS1_CLR_REG (APP_BLK_REG_ADDR + 0x510)
-#define HOST_CMDSTS1_SET_REG (APP_BLK_REG_ADDR + 0x514)
-#define HOST_CMDSTS1_REG (APP_BLK_REG_ADDR + 0x518)
-#define HOST_CMDSTS2_CLR_REG (APP_BLK_REG_ADDR + 0x520)
-#define HOST_CMDSTS2_SET_REG (APP_BLK_REG_ADDR + 0x524)
-#define HOST_CMDSTS2_REG (APP_BLK_REG_ADDR + 0x528)
-#define HOST_CMDSTS3_CLR_REG (APP_BLK_REG_ADDR + 0x530)
-#define HOST_CMDSTS3_SET_REG (APP_BLK_REG_ADDR + 0x534)
-#define HOST_CMDSTS3_REG (APP_BLK_REG_ADDR + 0x538)
-#define HOST_CMDSTS4_CLR_REG (APP_BLK_REG_ADDR + 0x540)
-#define HOST_CMDSTS4_SET_REG (APP_BLK_REG_ADDR + 0x544)
-#define HOST_CMDSTS4_REG (APP_BLK_REG_ADDR + 0x548)
-#define HOST_CMDSTS5_CLR_REG (APP_BLK_REG_ADDR + 0x550)
-#define HOST_CMDSTS5_SET_REG (APP_BLK_REG_ADDR + 0x554)
-#define HOST_CMDSTS5_REG (APP_BLK_REG_ADDR + 0x558)
-#define HOST_CMDSTS6_CLR_REG (APP_BLK_REG_ADDR + 0x560)
-#define HOST_CMDSTS6_SET_REG (APP_BLK_REG_ADDR + 0x564)
-#define HOST_CMDSTS6_REG (APP_BLK_REG_ADDR + 0x568)
-#define HOST_CMDSTS7_CLR_REG (APP_BLK_REG_ADDR + 0x570)
-#define HOST_CMDSTS7_SET_REG (APP_BLK_REG_ADDR + 0x574)
-#define HOST_CMDSTS7_REG (APP_BLK_REG_ADDR + 0x578)
-#define HOST_CMDSTS8_CLR_REG (APP_BLK_REG_ADDR + 0x580)
-#define HOST_CMDSTS8_SET_REG (APP_BLK_REG_ADDR + 0x584)
-#define HOST_CMDSTS8_REG (APP_BLK_REG_ADDR + 0x588)
-#define HOST_CMDSTS9_CLR_REG (APP_BLK_REG_ADDR + 0x590)
-#define HOST_CMDSTS9_SET_REG (APP_BLK_REG_ADDR + 0x594)
-#define HOST_CMDSTS9_REG (APP_BLK_REG_ADDR + 0x598)
-#define HOST_CMDSTS10_CLR_REG (APP_BLK_REG_ADDR + 0x5A0)
-#define HOST_CMDSTS10_SET_REG (APP_BLK_REG_ADDR + 0x5A4)
-#define HOST_CMDSTS10_REG (APP_BLK_REG_ADDR + 0x5A8)
-#define HOST_CMDSTS11_CLR_REG (APP_BLK_REG_ADDR + 0x5B0)
-#define HOST_CMDSTS11_SET_REG (APP_BLK_REG_ADDR + 0x5B4)
-#define HOST_CMDSTS11_REG (APP_BLK_REG_ADDR + 0x5B8)
-#define HOST_CMDSTS12_CLR_REG (APP_BLK_REG_ADDR + 0x5C0)
-#define HOST_CMDSTS12_SET_REG (APP_BLK_REG_ADDR + 0x5C4)
-#define HOST_CMDSTS12_REG (APP_BLK_REG_ADDR + 0x5C8)
-#define HOST_CMDSTS13_CLR_REG (APP_BLK_REG_ADDR + 0x5D0)
-#define HOST_CMDSTS13_SET_REG (APP_BLK_REG_ADDR + 0x5D4)
-#define HOST_CMDSTS13_REG (APP_BLK_REG_ADDR + 0x5D8)
-#define HOST_CMDSTS14_CLR_REG (APP_BLK_REG_ADDR + 0x5E0)
-#define HOST_CMDSTS14_SET_REG (APP_BLK_REG_ADDR + 0x5E4)
-#define HOST_CMDSTS14_REG (APP_BLK_REG_ADDR + 0x5E8)
-#define HOST_CMDSTS15_CLR_REG (APP_BLK_REG_ADDR + 0x5F0)
-#define HOST_CMDSTS15_SET_REG (APP_BLK_REG_ADDR + 0x5F4)
-#define HOST_CMDSTS15_REG (APP_BLK_REG_ADDR + 0x5F8)
-
-/**
- * LPU0 Block Register Address Offset from BAR0
- * Range 0x18000 - 0x18033
- */
-#define LPU0_BLK_REG_ADDR 0x00018000
-
-/**
- * LPU0 Registers
- * Should they be directly used from host,
- * except for diagnostics ?
- * CTL_REG : Control register
- * CMD_REG : Triggers exec. of cmd. in
- * Mailbox memory
- */
-#define LPU0_MBOX_CTL_REG (LPU0_BLK_REG_ADDR + 0x000)
-#define LPU0_MBOX_CMD_REG (LPU0_BLK_REG_ADDR + 0x004)
-#define LPU0_MBOX_LINK_0REG (LPU0_BLK_REG_ADDR + 0x008)
-#define LPU1_MBOX_LINK_0REG (LPU0_BLK_REG_ADDR + 0x00c)
-#define LPU0_MBOX_STATUS_0REG (LPU0_BLK_REG_ADDR + 0x010)
-#define LPU1_MBOX_STATUS_0REG (LPU0_BLK_REG_ADDR + 0x014)
-#define LPU0_ERR_STATUS_REG (LPU0_BLK_REG_ADDR + 0x018)
-#define LPU0_ERR_SET_REG (LPU0_BLK_REG_ADDR + 0x020)
-
-/**
- * LPU1 Block Register Address Offset from BAR0
- * Range 0x18400 - 0x18433
- */
-#define LPU1_BLK_REG_ADDR 0x00018400
-
-/**
- * LPU1 Registers
- * Same as LPU0 registers above
- */
-#define LPU1_MBOX_CTL_REG (LPU1_BLK_REG_ADDR + 0x000)
-#define LPU1_MBOX_CMD_REG (LPU1_BLK_REG_ADDR + 0x004)
-#define LPU0_MBOX_LINK_1REG (LPU1_BLK_REG_ADDR + 0x008)
-#define LPU1_MBOX_LINK_1REG (LPU1_BLK_REG_ADDR + 0x00c)
-#define LPU0_MBOX_STATUS_1REG (LPU1_BLK_REG_ADDR + 0x010)
-#define LPU1_MBOX_STATUS_1REG (LPU1_BLK_REG_ADDR + 0x014)
-#define LPU1_ERR_STATUS_REG (LPU1_BLK_REG_ADDR + 0x018)
-#define LPU1_ERR_SET_REG (LPU1_BLK_REG_ADDR + 0x020)
-
-/**
- * PSS Block Register Address Offset from BAR0
- * Range 0x18800 - 0x188DB
- */
-#define PSS_BLK_REG_ADDR 0x00018800
-
-/**
- * PSS Registers
- * For details, see catapult_spec.pdf
- * ERR_STATUS_REG : Indicates error in PSS module
- * RAM_ERR_STATUS_REG : Indicates RAM module that detected error
- */
-#define ERR_STATUS_SET (PSS_BLK_REG_ADDR + 0x018)
-#define PSS_RAM_ERR_STATUS_REG (PSS_BLK_REG_ADDR + 0x01C)
-
-/**
- * PSS Semaphore Lock Registers, total 16
- * First read when unlocked returns 0,
- * and is set to 1, atomically.
- * Subsequent reads returns 1.
- * To clear set the value to 0.
- * Range : 0x20 to 0x5c
- */
-#define PSS_SEM_LOCK_REG(_num) \
- (PSS_BLK_REG_ADDR + 0x020 + ((_num) << 2))
-
-/**
- * PSS Semaphore Status Registers,
- * corresponding to the lock registers above
- */
-#define PSS_SEM_STATUS_REG(_num) \
- (PSS_BLK_REG_ADDR + 0x060 + ((_num) << 2))
-
-/**
- * Catapult CPQ Registers
- * Defines for Mailbox Registers
- * Used to send mailbox commands to firmware from
- * host. The data part is written to the MBox
- * memory, registers are used to indicate that
- * a commnad is resident in memory.
- *
- * Note : LPU0<->LPU1 mailboxes are not listed here
- */
-#define CPQ_BLK_REG_ADDR 0x00019000
-
-#define HOSTFN0_LPU0_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x130)
-#define HOSTFN0_LPU1_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x134)
-#define LPU0_HOSTFN0_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x138)
-#define LPU1_HOSTFN0_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x13C)
-
-#define HOSTFN1_LPU0_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x140)
-#define HOSTFN1_LPU1_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x144)
-#define LPU0_HOSTFN1_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x148)
-#define LPU1_HOSTFN1_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x14C)
-
-#define HOSTFN2_LPU0_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x170)
-#define HOSTFN2_LPU1_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x174)
-#define LPU0_HOSTFN2_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x178)
-#define LPU1_HOSTFN2_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x17C)
-
-#define HOSTFN3_LPU0_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x180)
-#define HOSTFN3_LPU1_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x184)
-#define LPU0_HOSTFN3_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x188)
-#define LPU1_HOSTFN3_MBOX1_CMD_STAT (CPQ_BLK_REG_ADDR + 0x18C)
-
-/* Host Function Force Parity Error Registers */
-#define HOSTFN0_LPU_FORCE_PERR (CPQ_BLK_REG_ADDR + 0x120)
-#define HOSTFN1_LPU_FORCE_PERR (CPQ_BLK_REG_ADDR + 0x124)
-#define HOSTFN2_LPU_FORCE_PERR (CPQ_BLK_REG_ADDR + 0x128)
-#define HOSTFN3_LPU_FORCE_PERR (CPQ_BLK_REG_ADDR + 0x12C)
-
-/* LL Port[0|1] Halt Mask Registers */
-#define LL_HALT_MSK_P0 (CPQ_BLK_REG_ADDR + 0x1A0)
-#define LL_HALT_MSK_P1 (CPQ_BLK_REG_ADDR + 0x1B0)
-
-/* LL Port[0|1] Error Mask Registers */
-#define LL_ERR_MSK_P0 (CPQ_BLK_REG_ADDR + 0x1D0)
-#define LL_ERR_MSK_P1 (CPQ_BLK_REG_ADDR + 0x1D4)
-
-/* EMC FLI (Flash Controller) Block Register Address Offset from BAR0 */
-#define FLI_BLK_REG_ADDR 0x0001D000
-
-/* EMC FLI Registers */
-#define FLI_CMD_REG (FLI_BLK_REG_ADDR + 0x000)
-#define FLI_ADDR_REG (FLI_BLK_REG_ADDR + 0x004)
-#define FLI_CTL_REG (FLI_BLK_REG_ADDR + 0x008)
-#define FLI_WRDATA_REG (FLI_BLK_REG_ADDR + 0x00C)
-#define FLI_RDDATA_REG (FLI_BLK_REG_ADDR + 0x010)
-#define FLI_DEV_STATUS_REG (FLI_BLK_REG_ADDR + 0x014)
-#define FLI_SIG_WD_REG (FLI_BLK_REG_ADDR + 0x018)
-
-/**
- * RO register
- * 31:16 -- Vendor Id
- * 15:0 -- Device Id
- */
-#define FLI_DEV_VENDOR_REG (FLI_BLK_REG_ADDR + 0x01C)
-#define FLI_ERR_STATUS_REG (FLI_BLK_REG_ADDR + 0x020)
-
-/**
- * RAD (RxAdm) Block Register Address Offset from BAR0
- * RAD0 Range : 0x20000 - 0x203FF
- * RAD1 Range : 0x20400 - 0x207FF
- */
-#define RAD0_BLK_REG_ADDR 0x00020000
-#define RAD1_BLK_REG_ADDR 0x00020400
-
-/* RAD0 Registers */
-#define RAD0_CTL_REG (RAD0_BLK_REG_ADDR + 0x000)
-#define RAD0_PE_PARM_REG (RAD0_BLK_REG_ADDR + 0x004)
-#define RAD0_BCN_REG (RAD0_BLK_REG_ADDR + 0x008)
-
-/* Default function ID register */
-#define RAD0_DEFAULT_REG (RAD0_BLK_REG_ADDR + 0x00C)
-
-/* Default promiscuous ID register */
-#define RAD0_PROMISC_REG (RAD0_BLK_REG_ADDR + 0x010)
-
-#define RAD0_BCNQ_REG (RAD0_BLK_REG_ADDR + 0x014)
-
-/*
- * This register selects 1 of 8 PM Q's using
- * VLAN pri, for non-BCN packets without a VLAN tag
- */
-#define RAD0_DEFAULTQ_REG (RAD0_BLK_REG_ADDR + 0x018)
-
-#define RAD0_ERR_STS (RAD0_BLK_REG_ADDR + 0x01C)
-#define RAD0_SET_ERR_STS (RAD0_BLK_REG_ADDR + 0x020)
-#define RAD0_ERR_INT_EN (RAD0_BLK_REG_ADDR + 0x024)
-#define RAD0_FIRST_ERR (RAD0_BLK_REG_ADDR + 0x028)
-#define RAD0_FORCE_ERR (RAD0_BLK_REG_ADDR + 0x02C)
-
-#define RAD0_IF_RCVD (RAD0_BLK_REG_ADDR + 0x030)
-#define RAD0_IF_RCVD_OCTETS_HIGH (RAD0_BLK_REG_ADDR + 0x034)
-#define RAD0_IF_RCVD_OCTETS_LOW (RAD0_BLK_REG_ADDR + 0x038)
-#define RAD0_IF_RCVD_VLAN (RAD0_BLK_REG_ADDR + 0x03C)
-#define RAD0_IF_RCVD_UCAST (RAD0_BLK_REG_ADDR + 0x040)
-#define RAD0_IF_RCVD_UCAST_OCTETS_HIGH (RAD0_BLK_REG_ADDR + 0x044)
-#define RAD0_IF_RCVD_UCAST_OCTETS_LOW (RAD0_BLK_REG_ADDR + 0x048)
-#define RAD0_IF_RCVD_UCAST_VLAN (RAD0_BLK_REG_ADDR + 0x04C)
-#define RAD0_IF_RCVD_MCAST (RAD0_BLK_REG_ADDR + 0x050)
-#define RAD0_IF_RCVD_MCAST_OCTETS_HIGH (RAD0_BLK_REG_ADDR + 0x054)
-#define RAD0_IF_RCVD_MCAST_OCTETS_LOW (RAD0_BLK_REG_ADDR + 0x058)
-#define RAD0_IF_RCVD_MCAST_VLAN (RAD0_BLK_REG_ADDR + 0x05C)
-#define RAD0_IF_RCVD_BCAST (RAD0_BLK_REG_ADDR + 0x060)
-#define RAD0_IF_RCVD_BCAST_OCTETS_HIGH (RAD0_BLK_REG_ADDR + 0x064)
-#define RAD0_IF_RCVD_BCAST_OCTETS_LOW (RAD0_BLK_REG_ADDR + 0x068)
-#define RAD0_IF_RCVD_BCAST_VLAN (RAD0_BLK_REG_ADDR + 0x06C)
-#define RAD0_DROPPED_FRAMES (RAD0_BLK_REG_ADDR + 0x070)
-
-#define RAD0_MAC_MAN_1H (RAD0_BLK_REG_ADDR + 0x080)
-#define RAD0_MAC_MAN_1L (RAD0_BLK_REG_ADDR + 0x084)
-#define RAD0_MAC_MAN_2H (RAD0_BLK_REG_ADDR + 0x088)
-#define RAD0_MAC_MAN_2L (RAD0_BLK_REG_ADDR + 0x08C)
-#define RAD0_MAC_MAN_3H (RAD0_BLK_REG_ADDR + 0x090)
-#define RAD0_MAC_MAN_3L (RAD0_BLK_REG_ADDR + 0x094)
-#define RAD0_MAC_MAN_4H (RAD0_BLK_REG_ADDR + 0x098)
-#define RAD0_MAC_MAN_4L (RAD0_BLK_REG_ADDR + 0x09C)
-
-#define RAD0_LAST4_IP (RAD0_BLK_REG_ADDR + 0x100)
-
-/* RAD1 Registers */
-#define RAD1_CTL_REG (RAD1_BLK_REG_ADDR + 0x000)
-#define RAD1_PE_PARM_REG (RAD1_BLK_REG_ADDR + 0x004)
-#define RAD1_BCN_REG (RAD1_BLK_REG_ADDR + 0x008)
-
-/* Default function ID register */
-#define RAD1_DEFAULT_REG (RAD1_BLK_REG_ADDR + 0x00C)
-
-/* Promiscuous function ID register */
-#define RAD1_PROMISC_REG (RAD1_BLK_REG_ADDR + 0x010)
-
-#define RAD1_BCNQ_REG (RAD1_BLK_REG_ADDR + 0x014)
-
-/*
- * This register selects 1 of 8 PM Q's using
- * VLAN pri, for non-BCN packets without a VLAN tag
- */
-#define RAD1_DEFAULTQ_REG (RAD1_BLK_REG_ADDR + 0x018)
-
-#define RAD1_ERR_STS (RAD1_BLK_REG_ADDR + 0x01C)
-#define RAD1_SET_ERR_STS (RAD1_BLK_REG_ADDR + 0x020)
-#define RAD1_ERR_INT_EN (RAD1_BLK_REG_ADDR + 0x024)
-
-/**
- * TXA Block Register Address Offset from BAR0
- * TXA0 Range : 0x21000 - 0x213FF
- * TXA1 Range : 0x21400 - 0x217FF
- */
-#define TXA0_BLK_REG_ADDR 0x00021000
-#define TXA1_BLK_REG_ADDR 0x00021400
-
-/* TXA Registers */
-#define TXA0_CTRL_REG (TXA0_BLK_REG_ADDR + 0x000)
-#define TXA1_CTRL_REG (TXA1_BLK_REG_ADDR + 0x000)
-
-/**
- * TSO Sequence # Registers (RO)
- * Total 8 (for 8 queues)
- * Holds the last seq.# for TSO frames
- * See catapult_spec.pdf for more details
- */
-#define TXA0_TSO_TCP_SEQ_REG(_num) \
- (TXA0_BLK_REG_ADDR + 0x020 + ((_num) << 2))
-
-#define TXA1_TSO_TCP_SEQ_REG(_num) \
- (TXA1_BLK_REG_ADDR + 0x020 + ((_num) << 2))
-
-/**
- * TSO IP ID # Registers (RO)
- * Total 8 (for 8 queues)
- * Holds the last IP ID for TSO frames
- * See catapult_spec.pdf for more details
- */
-#define TXA0_TSO_IP_INFO_REG(_num) \
- (TXA0_BLK_REG_ADDR + 0x040 + ((_num) << 2))
-
-#define TXA1_TSO_IP_INFO_REG(_num) \
- (TXA1_BLK_REG_ADDR + 0x040 + ((_num) << 2))
-
-/**
- * RXA Block Register Address Offset from BAR0
- * RXA0 Range : 0x21800 - 0x21BFF
- * RXA1 Range : 0x21C00 - 0x21FFF
- */
-#define RXA0_BLK_REG_ADDR 0x00021800
-#define RXA1_BLK_REG_ADDR 0x00021C00
-
-/* RXA Registers */
-#define RXA0_CTL_REG (RXA0_BLK_REG_ADDR + 0x040)
-#define RXA1_CTL_REG (RXA1_BLK_REG_ADDR + 0x040)
-
-/**
- * PPLB Block Register Address Offset from BAR0
- * PPLB0 Range : 0x22000 - 0x223FF
- * PPLB1 Range : 0x22400 - 0x227FF
- */
-#define PLB0_BLK_REG_ADDR 0x00022000
-#define PLB1_BLK_REG_ADDR 0x00022400
-
-/**
- * PLB Registers
- * Holds RL timer used time stamps in RLT tagged frames
- */
-#define PLB0_ECM_TIMER_REG (PLB0_BLK_REG_ADDR + 0x05C)
-#define PLB1_ECM_TIMER_REG (PLB1_BLK_REG_ADDR + 0x05C)
-
-/* Controls the rate-limiter on each of the priority class */
-#define PLB0_RL_CTL (PLB0_BLK_REG_ADDR + 0x060)
-#define PLB1_RL_CTL (PLB1_BLK_REG_ADDR + 0x060)
-
-/**
- * Max byte register, total 8, 0-7
- * see catapult_spec.pdf for details
- */
-#define PLB0_RL_MAX_BC(_num) \
- (PLB0_BLK_REG_ADDR + 0x064 + ((_num) << 2))
-#define PLB1_RL_MAX_BC(_num) \
- (PLB1_BLK_REG_ADDR + 0x064 + ((_num) << 2))
-
-/**
- * RL Time Unit Register for priority 0-7
- * 4 bits per priority
- * (2^rl_unit)*1us is the actual time period
- */
-#define PLB0_RL_TU_PRIO (PLB0_BLK_REG_ADDR + 0x084)
-#define PLB1_RL_TU_PRIO (PLB1_BLK_REG_ADDR + 0x084)
-
-/**
- * RL byte count register,
- * bytes transmitted in (rl_unit*1)us time period
- * 1 per priority, 8 in all, 0-7.
- */
-#define PLB0_RL_BYTE_CNT(_num) \
- (PLB0_BLK_REG_ADDR + 0x088 + ((_num) << 2))
-#define PLB1_RL_BYTE_CNT(_num) \
- (PLB1_BLK_REG_ADDR + 0x088 + ((_num) << 2))
-
-/**
- * RL Min factor register
- * 2 bits per priority,
- * 4 factors possible: 1, 0.5, 0.25, 0
- * 2'b00 - 0; 2'b01 - 0.25; 2'b10 - 0.5; 2'b11 - 1
- */
-#define PLB0_RL_MIN_REG (PLB0_BLK_REG_ADDR + 0x0A8)
-#define PLB1_RL_MIN_REG (PLB1_BLK_REG_ADDR + 0x0A8)
-
-/**
- * RL Max factor register
- * 2 bits per priority,
- * 4 factors possible: 1, 0.5, 0.25, 0
- * 2'b00 - 0; 2'b01 - 0.25; 2'b10 - 0.5; 2'b11 - 1
- */
-#define PLB0_RL_MAX_REG (PLB0_BLK_REG_ADDR + 0x0AC)
-#define PLB1_RL_MAX_REG (PLB1_BLK_REG_ADDR + 0x0AC)
-
-/* MAC SERDES Address Paging register */
-#define PLB0_EMS_ADD_REG (PLB0_BLK_REG_ADDR + 0xD0)
-#define PLB1_EMS_ADD_REG (PLB1_BLK_REG_ADDR + 0xD0)
-
-/* LL EMS Registers */
-#define LL_EMS0_BLK_REG_ADDR 0x00026800
-#define LL_EMS1_BLK_REG_ADDR 0x00026C00
-
-/**
- * BPC Block Register Address Offset from BAR0
- * BPC0 Range : 0x23000 - 0x233FF
- * BPC1 Range : 0x23400 - 0x237FF
- */
-#define BPC0_BLK_REG_ADDR 0x00023000
-#define BPC1_BLK_REG_ADDR 0x00023400
-
-/**
- * PMM Block Register Address Offset from BAR0
- * PMM0 Range : 0x23800 - 0x23BFF
- * PMM1 Range : 0x23C00 - 0x23FFF
- */
-#define PMM0_BLK_REG_ADDR 0x00023800
-#define PMM1_BLK_REG_ADDR 0x00023C00
-
-/**
- * HQM Block Register Address Offset from BAR0
- * HQM0 Range : 0x24000 - 0x243FF
- * HQM1 Range : 0x24400 - 0x247FF
- */
-#define HQM0_BLK_REG_ADDR 0x00024000
-#define HQM1_BLK_REG_ADDR 0x00024400
-
-/**
- * HQM Control Register
- * Controls some aspects of IB
- * See catapult_spec.pdf for details
- */
-#define HQM0_CTL_REG (HQM0_BLK_REG_ADDR + 0x000)
-#define HQM1_CTL_REG (HQM1_BLK_REG_ADDR + 0x000)
-
-/**
- * HQM Stop Q Semaphore Registers.
- * Only one Queue resource can be stopped at
- * any given time. This register controls access
- * to the single stop Q resource.
- * See catapult_spec.pdf for details
- */
-#define HQM0_RXQ_STOP_SEM (HQM0_BLK_REG_ADDR + 0x028)
-#define HQM0_TXQ_STOP_SEM (HQM0_BLK_REG_ADDR + 0x02C)
-#define HQM1_RXQ_STOP_SEM (HQM1_BLK_REG_ADDR + 0x028)
-#define HQM1_TXQ_STOP_SEM (HQM1_BLK_REG_ADDR + 0x02C)
-
-/**
- * LUT Block Register Address Offset from BAR0
- * LUT0 Range : 0x25800 - 0x25BFF
- * LUT1 Range : 0x25C00 - 0x25FFF
- */
-#define LUT0_BLK_REG_ADDR 0x00025800
-#define LUT1_BLK_REG_ADDR 0x00025C00
-
-/**
- * LUT Registers
- * See catapult_spec.pdf for details
- */
-#define LUT0_ERR_STS (LUT0_BLK_REG_ADDR + 0x000)
-#define LUT1_ERR_STS (LUT1_BLK_REG_ADDR + 0x000)
-#define LUT0_SET_ERR_STS (LUT0_BLK_REG_ADDR + 0x004)
-#define LUT1_SET_ERR_STS (LUT1_BLK_REG_ADDR + 0x004)
-
-/**
- * TRC (Debug/Trace) Register Offset from BAR0
- * Range : 0x26000 -- 0x263FFF
- */
-#define TRC_BLK_REG_ADDR 0x00026000
-
-/**
- * TRC Registers
- * See catapult_spec.pdf for details of each
- */
-#define TRC_CTL_REG (TRC_BLK_REG_ADDR + 0x000)
-#define TRC_MODS_REG (TRC_BLK_REG_ADDR + 0x004)
-#define TRC_TRGC_REG (TRC_BLK_REG_ADDR + 0x008)
-#define TRC_CNT1_REG (TRC_BLK_REG_ADDR + 0x010)
-#define TRC_CNT2_REG (TRC_BLK_REG_ADDR + 0x014)
-#define TRC_NXTS_REG (TRC_BLK_REG_ADDR + 0x018)
-#define TRC_DIRR_REG (TRC_BLK_REG_ADDR + 0x01C)
-
-/**
- * TRC Trigger match filters, total 10
- * Determines the trigger condition
- */
-#define TRC_TRGM_REG(_num) \
- (TRC_BLK_REG_ADDR + 0x040 + ((_num) << 2))
-
-/**
- * TRC Next State filters, total 10
- * Determines the next state conditions
- */
-#define TRC_NXTM_REG(_num) \
- (TRC_BLK_REG_ADDR + 0x080 + ((_num) << 2))
-
-/**
- * TRC Store Match filters, total 10
- * Determines the store conditions
- */
-#define TRC_STRM_REG(_num) \
- (TRC_BLK_REG_ADDR + 0x0C0 + ((_num) << 2))
-
-/* DOORBELLS ACCESS */
-
-/**
- * Catapult doorbells
- * Each doorbell-queue set has
- * 1 RxQ, 1 TxQ, 2 IBs in that order
- * Size of each entry in 32 bytes, even though only 1 word
- * is used. For Non-VM case each doorbell-q set is
- * separated by 128 bytes, for VM case it is separated
- * by 4K bytes
- * Non VM case Range : 0x38000 - 0x39FFF
- * VM case Range : 0x100000 - 0x11FFFF
- * The range applies to both HQMs
- */
-#define HQM_DOORBELL_BLK_BASE_ADDR 0x00038000
-#define HQM_DOORBELL_VM_BLK_BASE_ADDR 0x00100000
-
-/* MEMORY ACCESS */
-
-/**
- * Catapult H/W Block Memory Access Address
- * To the host a memory space of 32K (page) is visible
- * at a time. The address range is from 0x08000 to 0x0FFFF
- */
-#define HW_BLK_HOST_MEM_ADDR 0x08000
-
-/**
- * Catapult LUT Memory Access Page Numbers
- * Range : LUT0 0xa0-0xa1
- * LUT1 0xa2-0xa3
- */
-#define LUT0_MEM_BLK_BASE_PG_NUM 0x000000A0
-#define LUT1_MEM_BLK_BASE_PG_NUM 0x000000A2
-
-/**
- * Catapult RxFn Database Memory Block Base Offset
- *
- * The Rx function database exists in LUT block.
- * In PCIe space this is accessible as a 256x32
- * bit block. Each entry in this database is 4
- * (4 byte) words. Max. entries is 64.
- * Address of an entry corresponding to a function
- * = base_addr + (function_no. * 16)
- */
-#define RX_FNDB_RAM_BASE_OFFSET 0x0000B400
-
-/**
- * Catapult TxFn Database Memory Block Base Offset Address
- *
- * The Tx function database exists in LUT block.
- * In PCIe space this is accessible as a 64x32
- * bit block. Each entry in this database is 1
- * (4 byte) word. Max. entries is 64.
- * Address of an entry corresponding to a function
- * = base_addr + (function_no. * 4)
- */
-#define TX_FNDB_RAM_BASE_OFFSET 0x0000B800
-
-/**
- * Catapult Unicast CAM Base Offset Address
- *
- * Exists in LUT memory space.
- * Shared by both the LL & FCoE driver.
- * Size is 256x48 bits; mapped to PCIe space
- * 512x32 bit blocks. For each address, bits
- * are written in the order : [47:32] and then
- * [31:0].
- */
-#define UCAST_CAM_BASE_OFFSET 0x0000A800
-
-/**
- * Catapult Unicast RAM Base Offset Address
- *
- * Exists in LUT memory space.
- * Shared by both the LL & FCoE driver.
- * Size is 256x9 bits.
- */
-#define UCAST_RAM_BASE_OFFSET 0x0000B000
-
-/**
- * Catapult Mulicast CAM Base Offset Address
- *
- * Exists in LUT memory space.
- * Shared by both the LL & FCoE driver.
- * Size is 256x48 bits; mapped to PCIe space
- * 512x32 bit blocks. For each address, bits
- * are written in the order : [47:32] and then
- * [31:0].
- */
-#define MCAST_CAM_BASE_OFFSET 0x0000A000
-
-/**
- * Catapult VLAN RAM Base Offset Address
- *
- * Exists in LUT memory space.
- * Size is 4096x66 bits; mapped to PCIe space as
- * 8192x32 bit blocks.
- * All the 4K entries are within the address range
- * 0x0000 to 0x8000, so in the first LUT page.
- */
-#define VLAN_RAM_BASE_OFFSET 0x00000000
-
-/**
- * Catapult Tx Stats RAM Base Offset Address
- *
- * Exists in LUT memory space.
- * Size is 1024x33 bits;
- * Each Tx function has 64 bytes of space
- */
-#define TX_STATS_RAM_BASE_OFFSET 0x00009000
-
-/**
- * Catapult Rx Stats RAM Base Offset Address
- *
- * Exists in LUT memory space.
- * Size is 1024x33 bits;
- * Each Rx function has 64 bytes of space
- */
-#define RX_STATS_RAM_BASE_OFFSET 0x00008000
-
-/* Catapult RXA Memory Access Page Numbers */
-#define RXA0_MEM_BLK_BASE_PG_NUM 0x0000008C
-#define RXA1_MEM_BLK_BASE_PG_NUM 0x0000008D
-
-/**
- * Catapult Multicast Vector Table Base Offset Address
- *
- * Exists in RxA memory space.
- * Organized as 512x65 bit block.
- * However for each entry 16 bytes allocated (power of 2)
- * Total size 512*16 bytes.
- * There are two logical divisions, 256 entries each :
- * a) Entries 0x00 to 0xff (256) -- Approx. MVT
- * Offset 0x000 to 0xFFF
- * b) Entries 0x100 to 0x1ff (256) -- Exact MVT
- * Offsets 0x1000 to 0x1FFF
- */
-#define MCAST_APPROX_MVT_BASE_OFFSET 0x00000000
-#define MCAST_EXACT_MVT_BASE_OFFSET 0x00001000
-
-/**
- * Catapult RxQ Translate Table (RIT) Base Offset Address
- *
- * Exists in RxA memory space
- * Total no. of entries 64
- * Each entry is 1 (4 byte) word.
- * 31:12 -- Reserved
- * 11:0 -- Two 6 bit RxQ Ids
- */
-#define FUNCTION_TO_RXQ_TRANSLATE 0x00002000
-
-/* Catapult RxAdm (RAD) Memory Access Page Numbers */
-#define RAD0_MEM_BLK_BASE_PG_NUM 0x00000086
-#define RAD1_MEM_BLK_BASE_PG_NUM 0x00000087
-
-/**
- * Catapult RSS Table Base Offset Address
- *
- * Exists in RAD memory space.
- * Each entry is 352 bits, but aligned on
- * 64 byte (512 bit) boundary. Accessed
- * 4 byte words, the whole entry can be
- * broken into 11 word accesses.
- */
-#define RSS_TABLE_BASE_OFFSET 0x00000800
-
-/**
- * Catapult CPQ Block Page Number
- * This value is written to the page number registers
- * to access the memory associated with the mailboxes.
- */
-#define CPQ_BLK_PG_NUM 0x00000005
-
-/**
- * Clarification :
- * LL functions are 2 & 3; can HostFn0/HostFn1
- * <-> LPU0/LPU1 memories be used ?
- */
-/**
- * Catapult HostFn0/HostFn1 to LPU0/LPU1 Mbox memory
- * Per catapult_spec.pdf, the offset of the mbox
- * memory is in the register space at an offset of 0x200
- */
-#define CPQ_BLK_REG_MBOX_ADDR (CPQ_BLK_REG_ADDR + 0x200)
-
-#define HOSTFN_LPU_MBOX (CPQ_BLK_REG_MBOX_ADDR + 0x000)
-
-/* Catapult LPU0/LPU1 to HostFn0/HostFn1 Mbox memory */
-#define LPU_HOSTFN_MBOX (CPQ_BLK_REG_MBOX_ADDR + 0x080)
-
-/**
- * Catapult HQM Block Page Number
- * This is written to the page number register for
- * the appropriate function to access the memory
- * associated with HQM
- */
-#define HQM0_BLK_PG_NUM 0x00000096
-#define HQM1_BLK_PG_NUM 0x00000097
-
-/**
- * Note that TxQ and RxQ entries are interlaced
- * the HQM memory, i.e RXQ0, TXQ0, RXQ1, TXQ1.. etc.
- */
-
-#define HQM_RXTX_Q_RAM_BASE_OFFSET 0x00004000
-
-/**
- * CQ Memory
- * Exists in HQM Memory space
- * Each entry is 16 (4 byte) words of which
- * only 12 words are used for configuration
- * Total 64 entries per HQM memory space
- */
-#define HQM_CQ_RAM_BASE_OFFSET 0x00006000
-
-/**
- * Interrupt Block (IB) Memory
- * Exists in HQM Memory space
- * Each entry is 8 (4 byte) words of which
- * only 5 words are used for configuration
- * Total 128 entries per HQM memory space
- */
-#define HQM_IB_RAM_BASE_OFFSET 0x00001000
-
-/**
- * Index Table (IT) Memory
- * Exists in HQM Memory space
- * Each entry is 1 (4 byte) word which
- * is used for configuration
- * Total 128 entries per HQM memory space
- */
-#define HQM_INDX_TBL_RAM_BASE_OFFSET 0x00002000
-
-/**
- * PSS Block Memory Page Number
- * This is written to the appropriate page number
- * register to access the CPU memory.
- * Also known as the PSS secondary memory (SMEM).
- * Range : 0x180 to 0x1CF
- * See catapult_spec.pdf for details
- */
-#define PSS_BLK_PG_NUM 0x00000180
-
-/**
- * Offsets of different instances of PSS SMEM
- * 2.5M of continuous 1T memory space : 2 blocks
- * of 1M each (32 pages each, page=32KB) and 4 smaller
- * blocks of 128K each (4 pages each, page=32KB)
- * PSS_LMEM_INST0 is used for firmware download
- */
-#define PSS_LMEM_INST0 0x00000000
-#define PSS_LMEM_INST1 0x00100000
-#define PSS_LMEM_INST2 0x00200000
-#define PSS_LMEM_INST3 0x00220000
-#define PSS_LMEM_INST4 0x00240000
-#define PSS_LMEM_INST5 0x00260000
-
-#define BNA_PCI_REG_CT_ADDRSZ (0x40000)
-
-#define BNA_GET_PAGE_NUM(_base_page, _offset) \
- ((_base_page) + ((_offset) >> 15))
-
-#define BNA_GET_PAGE_OFFSET(_offset) \
- ((_offset) & 0x7fff)
-
-#define BNA_GET_MEM_BASE_ADDR(_bar0, _base_offset) \
- ((_bar0) + HW_BLK_HOST_MEM_ADDR \
- + BNA_GET_PAGE_OFFSET((_base_offset)))
-
-#define BNA_GET_VLAN_MEM_ENTRY_ADDR(_bar0, _fn_id, _vlan_id)\
- (_bar0 + (HW_BLK_HOST_MEM_ADDR) \
- + (BNA_GET_PAGE_OFFSET(VLAN_RAM_BASE_OFFSET)) \
- + (((_fn_id) & 0x3f) << 9) \
- + (((_vlan_id) & 0xfe0) >> 3))
-
-/**
- *
- * Interrupt related bits, flags and macros
- *
- */
-
-#define __LPU02HOST_MBOX0_STATUS_BITS 0x00100000
-#define __LPU12HOST_MBOX0_STATUS_BITS 0x00200000
-#define __LPU02HOST_MBOX1_STATUS_BITS 0x00400000
-#define __LPU12HOST_MBOX1_STATUS_BITS 0x00800000
-
-#define __LPU02HOST_MBOX0_MASK_BITS 0x00100000
-#define __LPU12HOST_MBOX0_MASK_BITS 0x00200000
-#define __LPU02HOST_MBOX1_MASK_BITS 0x00400000
-#define __LPU12HOST_MBOX1_MASK_BITS 0x00800000
-
-#define __LPU2HOST_MBOX_MASK_BITS \
- (__LPU02HOST_MBOX0_MASK_BITS | __LPU02HOST_MBOX1_MASK_BITS | \
- __LPU12HOST_MBOX0_MASK_BITS | __LPU12HOST_MBOX1_MASK_BITS)
-
-#define __LPU2HOST_IB_STATUS_BITS 0x0000ffff
-
-#define BNA_IS_LPU0_MBOX_INTR(_intr_status) \
- ((_intr_status) & (__LPU02HOST_MBOX0_STATUS_BITS | \
- __LPU02HOST_MBOX1_STATUS_BITS))
-
-#define BNA_IS_LPU1_MBOX_INTR(_intr_status) \
- ((_intr_status) & (__LPU12HOST_MBOX0_STATUS_BITS | \
- __LPU12HOST_MBOX1_STATUS_BITS))
-
-#define BNA_IS_MBOX_INTR(_intr_status) \
- ((_intr_status) & \
- (__LPU02HOST_MBOX0_STATUS_BITS | \
- __LPU02HOST_MBOX1_STATUS_BITS | \
- __LPU12HOST_MBOX0_STATUS_BITS | \
- __LPU12HOST_MBOX1_STATUS_BITS))
-
-#define __EMC_ERROR_STATUS_BITS 0x00010000
-#define __LPU0_ERROR_STATUS_BITS 0x00020000
-#define __LPU1_ERROR_STATUS_BITS 0x00040000
-#define __PSS_ERROR_STATUS_BITS 0x00080000
-
-#define __HALT_STATUS_BITS 0x01000000
-
-#define __EMC_ERROR_MASK_BITS 0x00010000
-#define __LPU0_ERROR_MASK_BITS 0x00020000
-#define __LPU1_ERROR_MASK_BITS 0x00040000
-#define __PSS_ERROR_MASK_BITS 0x00080000
-
-#define __HALT_MASK_BITS 0x01000000
-
-#define __ERROR_MASK_BITS \
- (__EMC_ERROR_MASK_BITS | __LPU0_ERROR_MASK_BITS | \
- __LPU1_ERROR_MASK_BITS | __PSS_ERROR_MASK_BITS | \
- __HALT_MASK_BITS)
-
-#define BNA_IS_ERR_INTR(_intr_status) \
- ((_intr_status) & \
- (__EMC_ERROR_STATUS_BITS | \
- __LPU0_ERROR_STATUS_BITS | \
- __LPU1_ERROR_STATUS_BITS | \
- __PSS_ERROR_STATUS_BITS | \
- __HALT_STATUS_BITS))
-
-#define BNA_IS_MBOX_ERR_INTR(_intr_status) \
- (BNA_IS_MBOX_INTR((_intr_status)) | \
- BNA_IS_ERR_INTR((_intr_status)))
-
-#define BNA_IS_INTX_DATA_INTR(_intr_status) \
- ((_intr_status) & __LPU2HOST_IB_STATUS_BITS)
-
-#define BNA_INTR_STATUS_MBOX_CLR(_intr_status) \
-do { \
- (_intr_status) &= ~(__LPU02HOST_MBOX0_STATUS_BITS | \
- __LPU02HOST_MBOX1_STATUS_BITS | \
- __LPU12HOST_MBOX0_STATUS_BITS | \
- __LPU12HOST_MBOX1_STATUS_BITS); \
-} while (0)
-
-#define BNA_INTR_STATUS_ERR_CLR(_intr_status) \
-do { \
- (_intr_status) &= ~(__EMC_ERROR_STATUS_BITS | \
- __LPU0_ERROR_STATUS_BITS | \
- __LPU1_ERROR_STATUS_BITS | \
- __PSS_ERROR_STATUS_BITS | \
- __HALT_STATUS_BITS); \
-} while (0)
-
-#define bna_intx_disable(_bna, _cur_mask) \
-{ \
- (_cur_mask) = readl((_bna)->regs.fn_int_mask);\
- writel(0xffffffff, (_bna)->regs.fn_int_mask);\
-}
-
-#define bna_intx_enable(bna, new_mask) \
- writel((new_mask), (bna)->regs.fn_int_mask)
-
-#define bna_mbox_intr_disable(bna) \
- writel((readl((bna)->regs.fn_int_mask) | \
- (__LPU2HOST_MBOX_MASK_BITS | __ERROR_MASK_BITS)), \
- (bna)->regs.fn_int_mask)
-
-#define bna_mbox_intr_enable(bna) \
- writel((readl((bna)->regs.fn_int_mask) & \
- ~(__LPU2HOST_MBOX_MASK_BITS | __ERROR_MASK_BITS)), \
- (bna)->regs.fn_int_mask)
-
-#define bna_intr_status_get(_bna, _status) \
-{ \
- (_status) = readl((_bna)->regs.fn_int_status); \
- if ((_status)) { \
- writel((_status) & ~(__LPU02HOST_MBOX0_STATUS_BITS |\
- __LPU02HOST_MBOX1_STATUS_BITS |\
- __LPU12HOST_MBOX0_STATUS_BITS |\
- __LPU12HOST_MBOX1_STATUS_BITS), \
- (_bna)->regs.fn_int_status);\
- } \
-}
-
-#define bna_intr_status_get_no_clr(_bna, _status) \
- (_status) = readl((_bna)->regs.fn_int_status)
-
-#define bna_intr_mask_get(bna, mask) \
- (*mask) = readl((bna)->regs.fn_int_mask)
-
-#define bna_intr_ack(bna, intr_bmap) \
- writel((intr_bmap), (bna)->regs.fn_int_status)
-
-#define bna_ib_intx_disable(bna, ib_id) \
- writel(readl((bna)->regs.fn_int_mask) | \
- (1 << (ib_id)), \
- (bna)->regs.fn_int_mask)
-
-#define bna_ib_intx_enable(bna, ib_id) \
- writel(readl((bna)->regs.fn_int_mask) & \
- ~(1 << (ib_id)), \
- (bna)->regs.fn_int_mask)
-
-#define bna_mbox_msix_idx_set(_device) \
-do {\
- writel(((_device)->vector & 0x000001FF), \
- (_device)->bna->pcidev.pci_bar_kva + \
- reg_offset[(_device)->bna->pcidev.pci_func].msix_idx);\
-} while (0)
-
-/**
- *
- * TxQ, RxQ, CQ related bits, offsets, macros
- *
- */
-
-#define BNA_Q_IDLE_STATE 0x00008001
-
-#define BNA_GET_DOORBELL_BASE_ADDR(_bar0) \
- ((_bar0) + HQM_DOORBELL_BLK_BASE_ADDR)
-
-#define BNA_GET_DOORBELL_ENTRY_OFFSET(_entry) \
- ((HQM_DOORBELL_BLK_BASE_ADDR) \
- + (_entry << 7))
-
-#define BNA_DOORBELL_IB_INT_ACK(_timeout, _events) \
- (0x80000000 | ((_timeout) << 16) | (_events))
-
-#define BNA_DOORBELL_IB_INT_DISABLE (0x40000000)
-
-/* TxQ Entry Opcodes */
-#define BNA_TXQ_WI_SEND (0x402) /* Single Frame Transmission */
-#define BNA_TXQ_WI_SEND_LSO (0x403) /* Multi-Frame Transmission */
-#define BNA_TXQ_WI_EXTENSION (0x104) /* Extension WI */
-
-/* TxQ Entry Control Flags */
-#define BNA_TXQ_WI_CF_FCOE_CRC (1 << 8)
-#define BNA_TXQ_WI_CF_IPID_MODE (1 << 5)
-#define BNA_TXQ_WI_CF_INS_PRIO (1 << 4)
-#define BNA_TXQ_WI_CF_INS_VLAN (1 << 3)
-#define BNA_TXQ_WI_CF_UDP_CKSUM (1 << 2)
-#define BNA_TXQ_WI_CF_TCP_CKSUM (1 << 1)
-#define BNA_TXQ_WI_CF_IP_CKSUM (1 << 0)
-
-#define BNA_TXQ_WI_L4_HDR_N_OFFSET(_hdr_size, _offset) \
- (((_hdr_size) << 10) | ((_offset) & 0x3FF))
-
-/*
- * Completion Q defines
- */
-/* CQ Entry Flags */
-#define BNA_CQ_EF_MAC_ERROR (1 << 0)
-#define BNA_CQ_EF_FCS_ERROR (1 << 1)
-#define BNA_CQ_EF_TOO_LONG (1 << 2)
-#define BNA_CQ_EF_FC_CRC_OK (1 << 3)
-
-#define BNA_CQ_EF_RSVD1 (1 << 4)
-#define BNA_CQ_EF_L4_CKSUM_OK (1 << 5)
-#define BNA_CQ_EF_L3_CKSUM_OK (1 << 6)
-#define BNA_CQ_EF_HDS_HEADER (1 << 7)
-
-#define BNA_CQ_EF_UDP (1 << 8)
-#define BNA_CQ_EF_TCP (1 << 9)
-#define BNA_CQ_EF_IP_OPTIONS (1 << 10)
-#define BNA_CQ_EF_IPV6 (1 << 11)
-
-#define BNA_CQ_EF_IPV4 (1 << 12)
-#define BNA_CQ_EF_VLAN (1 << 13)
-#define BNA_CQ_EF_RSS (1 << 14)
-#define BNA_CQ_EF_RSVD2 (1 << 15)
-
-#define BNA_CQ_EF_MCAST_MATCH (1 << 16)
-#define BNA_CQ_EF_MCAST (1 << 17)
-#define BNA_CQ_EF_BCAST (1 << 18)
-#define BNA_CQ_EF_REMOTE (1 << 19)
-
-#define BNA_CQ_EF_LOCAL (1 << 20)
-
-/**
- *
- * Data structures
- *
- */
-
-enum txf_flags {
- BFI_TXF_CF_ENABLE = 1 << 0,
- BFI_TXF_CF_VLAN_FILTER = 1 << 8,
- BFI_TXF_CF_VLAN_ADMIT = 1 << 9,
- BFI_TXF_CF_VLAN_INSERT = 1 << 10,
- BFI_TXF_CF_RSVD1 = 1 << 11,
- BFI_TXF_CF_MAC_SA_CHECK = 1 << 12,
- BFI_TXF_CF_VLAN_WI_BASED = 1 << 13,
- BFI_TXF_CF_VSWITCH_MCAST = 1 << 14,
- BFI_TXF_CF_VSWITCH_UCAST = 1 << 15,
- BFI_TXF_CF_RSVD2 = 0x7F << 1
-};
-
-enum ib_flags {
- BFI_IB_CF_MASTER_ENABLE = (1 << 0),
- BFI_IB_CF_MSIX_MODE = (1 << 1),
- BFI_IB_CF_COALESCING_MODE = (1 << 2),
- BFI_IB_CF_INTER_PKT_ENABLE = (1 << 3),
- BFI_IB_CF_INT_ENABLE = (1 << 4),
- BFI_IB_CF_INTER_PKT_DMA = (1 << 5),
- BFI_IB_CF_ACK_PENDING = (1 << 6),
- BFI_IB_CF_RESERVED1 = (1 << 7)
-};
-
-enum rss_hash_type {
- BFI_RSS_T_V4_TCP = (1 << 11),
- BFI_RSS_T_V4_IP = (1 << 10),
- BFI_RSS_T_V6_TCP = (1 << 9),
- BFI_RSS_T_V6_IP = (1 << 8)
-};
-enum hds_header_type {
- BNA_HDS_T_V4_TCP = (1 << 11),
- BNA_HDS_T_V4_UDP = (1 << 10),
- BNA_HDS_T_V6_TCP = (1 << 9),
- BNA_HDS_T_V6_UDP = (1 << 8),
- BNA_HDS_FORCED = (1 << 7),
-};
-enum rxf_flags {
- BNA_RXF_CF_SM_LG_RXQ = (1 << 15),
- BNA_RXF_CF_DEFAULT_VLAN = (1 << 14),
- BNA_RXF_CF_DEFAULT_FUNCTION_ENABLE = (1 << 13),
- BNA_RXF_CF_VLAN_STRIP = (1 << 12),
- BNA_RXF_CF_RSS_ENABLE = (1 << 8)
-};
-struct bna_chip_regs_offset {
- u32 page_addr;
- u32 fn_int_status;
- u32 fn_int_mask;
- u32 msix_idx;
-};
-
-struct bna_chip_regs {
- void __iomem *page_addr;
- void __iomem *fn_int_status;
- void __iomem *fn_int_mask;
-};
-
-struct bna_txq_mem {
- u32 pg_tbl_addr_lo;
- u32 pg_tbl_addr_hi;
- u32 cur_q_entry_lo;
- u32 cur_q_entry_hi;
- u32 reserved1;
- u32 reserved2;
- u32 pg_cnt_n_prd_ptr; /* 31:16->total page count */
- /* 15:0 ->producer pointer (index?) */
- u32 entry_n_pg_size; /* 31:16->entry size */
- /* 15:0 ->page size */
- u32 int_blk_n_cns_ptr; /* 31:24->Int Blk Id; */
- /* 23:16->Int Blk Offset */
- /* 15:0 ->consumer pointer(index?) */
- u32 cns_ptr2_n_q_state; /* 31:16->cons. ptr 2; 15:0-> Q state */
- u32 nxt_qid_n_fid_n_pri; /* 17:10->next */
- /* QId;9:3->FID;2:0->Priority */
- u32 wvc_n_cquota_n_rquota; /* 31:24->WI Vector Count; */
- /* 23:12->Cfg Quota; */
- /* 11:0 ->Run Quota */
- u32 reserved3[4];
-};
-
-struct bna_rxq_mem {
- u32 pg_tbl_addr_lo;
- u32 pg_tbl_addr_hi;
- u32 cur_q_entry_lo;
- u32 cur_q_entry_hi;
- u32 reserved1;
- u32 reserved2;
- u32 pg_cnt_n_prd_ptr; /* 31:16->total page count */
- /* 15:0 ->producer pointer (index?) */
- u32 entry_n_pg_size; /* 31:16->entry size */
- /* 15:0 ->page size */
- u32 sg_n_cq_n_cns_ptr; /* 31:28->reserved; 27:24->sg count */
- /* 23:16->CQ; */
- /* 15:0->consumer pointer(index?) */
- u32 buf_sz_n_q_state; /* 31:16->buffer size; 15:0-> Q state */
- u32 next_qid; /* 17:10->next QId */
- u32 reserved3;
- u32 reserved4[4];
-};
-
-struct bna_rxtx_q_mem {
- struct bna_rxq_mem rxq;
- struct bna_txq_mem txq;
-};
-
-struct bna_cq_mem {
- u32 pg_tbl_addr_lo;
- u32 pg_tbl_addr_hi;
- u32 cur_q_entry_lo;
- u32 cur_q_entry_hi;
-
- u32 reserved1;
- u32 reserved2;
- u32 pg_cnt_n_prd_ptr; /* 31:16->total page count */
- /* 15:0 ->producer pointer (index?) */
- u32 entry_n_pg_size; /* 31:16->entry size */
- /* 15:0 ->page size */
- u32 int_blk_n_cns_ptr; /* 31:24->Int Blk Id; */
- /* 23:16->Int Blk Offset */
- /* 15:0 ->consumer pointer(index?) */
- u32 q_state; /* 31:16->reserved; 15:0-> Q state */
- u32 reserved3[2];
- u32 reserved4[4];
-};
-
-struct bna_ib_blk_mem {
- u32 host_addr_lo;
- u32 host_addr_hi;
- u32 clsc_n_ctrl_n_msix; /* 31:24->coalescing; */
- /* 23:16->coalescing cfg; */
- /* 15:8 ->control; */
- /* 7:0 ->msix; */
- u32 ipkt_n_ent_n_idxof;
- u32 ipkt_cnt_cfg_n_unacked;
-
- u32 reserved[3];
-};
-
-struct bna_idx_tbl_mem {
- u32 idx; /* !< 31:16->res;15:0->idx; */
-};
-
-struct bna_doorbell_qset {
- u32 rxq[0x20 >> 2];
- u32 txq[0x20 >> 2];
- u32 ib0[0x20 >> 2];
- u32 ib1[0x20 >> 2];
-};
-
-struct bna_rx_fndb_ram {
- u32 rss_prop;
- u32 size_routing_props;
- u32 rit_hds_mcastq;
- u32 control_flags;
-};
-
-struct bna_tx_fndb_ram {
- u32 vlan_n_ctrl_flags;
-};
-
-/**
- * @brief
- * Structure which maps to RxFn Indirection Table (RIT)
- * Size : 1 word
- * See catapult_spec.pdf, RxA for details
- */
-struct bna_rit_mem {
- u32 rxq_ids; /* !< 31:12->res;11:0->two 6 bit RxQ Ids */
-};
-
-/**
- * @brief
- * Structure which maps to RSS Table entry
- * Size : 16 words
- * See catapult_spec.pdf, RAD for details
- */
-struct bna_rss_mem {
- /*
- * 31:12-> res
- * 11:8 -> protocol type
- * 7:0 -> hash index
- */
- u32 type_n_hash;
- u32 hash_key[10]; /* !< 40 byte Toeplitz hash key */
- u32 reserved[5];
-};
-
-/* TxQ Vector (a.k.a. Tx-Buffer Descriptor) */
-struct bna_dma_addr {
- u32 msb;
- u32 lsb;
-};
-
-struct bna_txq_wi_vector {
- u16 reserved;
- u16 length; /* Only 14 LSB are valid */
- struct bna_dma_addr host_addr; /* Tx-Buf DMA addr */
-};
-
-typedef u16 bna_txq_wi_opcode_t;
-
-typedef u16 bna_txq_wi_ctrl_flag_t;
-
-/**
- * TxQ Entry Structure
- *
- * BEWARE: Load values into this structure with correct endianess.
- */
-struct bna_txq_entry {
- union {
- struct {
- u8 reserved;
- u8 num_vectors; /* number of vectors present */
- bna_txq_wi_opcode_t opcode; /* Either */
- /* BNA_TXQ_WI_SEND or */
- /* BNA_TXQ_WI_SEND_LSO */
- bna_txq_wi_ctrl_flag_t flags; /* OR of all the flags */
- u16 l4_hdr_size_n_offset;
- u16 vlan_tag;
- u16 lso_mss; /* Only 14 LSB are valid */
- u32 frame_length; /* Only 24 LSB are valid */
- } wi;
-
- struct {
- u16 reserved;
- bna_txq_wi_opcode_t opcode; /* Must be */
- /* BNA_TXQ_WI_EXTENSION */
- u32 reserved2[3]; /* Place holder for */
- /* removed vector (12 bytes) */
- } wi_ext;
- } hdr;
- struct bna_txq_wi_vector vector[4];
-};
-#define wi_hdr hdr.wi
-#define wi_ext_hdr hdr.wi_ext
-
-/* RxQ Entry Structure */
-struct bna_rxq_entry { /* Rx-Buffer */
- struct bna_dma_addr host_addr; /* Rx-Buffer DMA address */
-};
-
-typedef u32 bna_cq_e_flag_t;
-
-/* CQ Entry Structure */
-struct bna_cq_entry {
- bna_cq_e_flag_t flags;
- u16 vlan_tag;
- u16 length;
- u32 rss_hash;
- u8 valid;
- u8 reserved1;
- u8 reserved2;
- u8 rxq_id;
-};
-
-#endif /* __BNA_HW_H__ */
diff --git a/drivers/net/bna/bna_txrx.c b/drivers/net/bna/bna_txrx.c
deleted file mode 100644
index 380085cc3088..000000000000
--- a/drivers/net/bna/bna_txrx.c
+++ /dev/null
@@ -1,4185 +0,0 @@
-/*
- * Linux network driver for Brocade Converged Network Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
- * All rights reserved
- * www.brocade.com
- */
-#include "bna.h"
-#include "bfa_sm.h"
-#include "bfi.h"
-
-/**
- * IB
- */
-#define bna_ib_find_free_ibidx(_mask, _pos)\
-do {\
- (_pos) = 0;\
- while (((_pos) < (BFI_IBIDX_MAX_SEGSIZE)) &&\
- ((1 << (_pos)) & (_mask)))\
- (_pos)++;\
-} while (0)
-
-#define bna_ib_count_ibidx(_mask, _count)\
-do {\
- int pos = 0;\
- (_count) = 0;\
- while (pos < (BFI_IBIDX_MAX_SEGSIZE)) {\
- if ((1 << pos) & (_mask))\
- (_count) = pos + 1;\
- pos++;\
- } \
-} while (0)
-
-#define bna_ib_select_segpool(_count, _q_idx)\
-do {\
- int i;\
- (_q_idx) = -1;\
- for (i = 0; i < BFI_IBIDX_TOTAL_POOLS; i++) {\
- if ((_count <= ibidx_pool[i].pool_entry_size)) {\
- (_q_idx) = i;\
- break;\
- } \
- } \
-} while (0)
-
-struct bna_ibidx_pool {
- int pool_size;
- int pool_entry_size;
-};
-init_ibidx_pool(ibidx_pool);
-
-static struct bna_intr *
-bna_intr_get(struct bna_ib_mod *ib_mod, enum bna_intr_type intr_type,
- int vector)
-{
- struct bna_intr *intr;
- struct list_head *qe;
-
- list_for_each(qe, &ib_mod->intr_active_q) {
- intr = (struct bna_intr *)qe;
-
- if ((intr->intr_type == intr_type) &&
- (intr->vector == vector)) {
- intr->ref_count++;
- return intr;
- }
- }
-
- if (list_empty(&ib_mod->intr_free_q))
- return NULL;
-
- bfa_q_deq(&ib_mod->intr_free_q, &intr);
- bfa_q_qe_init(&intr->qe);
-
- intr->ref_count = 1;
- intr->intr_type = intr_type;
- intr->vector = vector;
-
- list_add_tail(&intr->qe, &ib_mod->intr_active_q);
-
- return intr;
-}
-
-static void
-bna_intr_put(struct bna_ib_mod *ib_mod,
- struct bna_intr *intr)
-{
- intr->ref_count--;
-
- if (intr->ref_count == 0) {
- intr->ib = NULL;
- list_del(&intr->qe);
- bfa_q_qe_init(&intr->qe);
- list_add_tail(&intr->qe, &ib_mod->intr_free_q);
- }
-}
-
-void
-bna_ib_mod_init(struct bna_ib_mod *ib_mod, struct bna *bna,
- struct bna_res_info *res_info)
-{
- int i;
- int j;
- int count;
- u8 offset;
- struct bna_doorbell_qset *qset;
- unsigned long off;
-
- ib_mod->bna = bna;
-
- ib_mod->ib = (struct bna_ib *)
- res_info[BNA_RES_MEM_T_IB_ARRAY].res_u.mem_info.mdl[0].kva;
- ib_mod->intr = (struct bna_intr *)
- res_info[BNA_RES_MEM_T_INTR_ARRAY].res_u.mem_info.mdl[0].kva;
- ib_mod->idx_seg = (struct bna_ibidx_seg *)
- res_info[BNA_RES_MEM_T_IDXSEG_ARRAY].res_u.mem_info.mdl[0].kva;
-
- INIT_LIST_HEAD(&ib_mod->ib_free_q);
- INIT_LIST_HEAD(&ib_mod->intr_free_q);
- INIT_LIST_HEAD(&ib_mod->intr_active_q);
-
- for (i = 0; i < BFI_IBIDX_TOTAL_POOLS; i++)
- INIT_LIST_HEAD(&ib_mod->ibidx_seg_pool[i]);
-
- for (i = 0; i < BFI_MAX_IB; i++) {
- ib_mod->ib[i].ib_id = i;
-
- ib_mod->ib[i].ib_seg_host_addr_kva =
- res_info[BNA_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].kva;
- ib_mod->ib[i].ib_seg_host_addr.lsb =
- res_info[BNA_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].dma.lsb;
- ib_mod->ib[i].ib_seg_host_addr.msb =
- res_info[BNA_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].dma.msb;
-
- qset = (struct bna_doorbell_qset *)0;
- off = (unsigned long)(&qset[i >> 1].ib0[(i & 0x1)
- * (0x20 >> 2)]);
- ib_mod->ib[i].door_bell.doorbell_addr = off +
- BNA_GET_DOORBELL_BASE_ADDR(bna->pcidev.pci_bar_kva);
-
- bfa_q_qe_init(&ib_mod->ib[i].qe);
- list_add_tail(&ib_mod->ib[i].qe, &ib_mod->ib_free_q);
-
- bfa_q_qe_init(&ib_mod->intr[i].qe);
- list_add_tail(&ib_mod->intr[i].qe, &ib_mod->intr_free_q);
- }
-
- count = 0;
- offset = 0;
- for (i = 0; i < BFI_IBIDX_TOTAL_POOLS; i++) {
- for (j = 0; j < ibidx_pool[i].pool_size; j++) {
- bfa_q_qe_init(&ib_mod->idx_seg[count]);
- ib_mod->idx_seg[count].ib_seg_size =
- ibidx_pool[i].pool_entry_size;
- ib_mod->idx_seg[count].ib_idx_tbl_offset = offset;
- list_add_tail(&ib_mod->idx_seg[count].qe,
- &ib_mod->ibidx_seg_pool[i]);
- count++;
- offset += ibidx_pool[i].pool_entry_size;
- }
- }
-}
-
-void
-bna_ib_mod_uninit(struct bna_ib_mod *ib_mod)
-{
- int i;
- int j;
- struct list_head *qe;
-
- i = 0;
- list_for_each(qe, &ib_mod->ib_free_q)
- i++;
-
- i = 0;
- list_for_each(qe, &ib_mod->intr_free_q)
- i++;
-
- for (i = 0; i < BFI_IBIDX_TOTAL_POOLS; i++) {
- j = 0;
- list_for_each(qe, &ib_mod->ibidx_seg_pool[i])
- j++;
- }
-
- ib_mod->bna = NULL;
-}
-
-static struct bna_ib *
-bna_ib_get(struct bna_ib_mod *ib_mod,
- enum bna_intr_type intr_type,
- int vector)
-{
- struct bna_ib *ib;
- struct bna_intr *intr;
-
- if (intr_type == BNA_INTR_T_INTX)
- vector = (1 << vector);
-
- intr = bna_intr_get(ib_mod, intr_type, vector);
- if (intr == NULL)
- return NULL;
-
- if (intr->ib) {
- if (intr->ib->ref_count == BFI_IBIDX_MAX_SEGSIZE) {
- bna_intr_put(ib_mod, intr);
- return NULL;
- }
- intr->ib->ref_count++;
- return intr->ib;
- }
-
- if (list_empty(&ib_mod->ib_free_q)) {
- bna_intr_put(ib_mod, intr);
- return NULL;
- }
-
- bfa_q_deq(&ib_mod->ib_free_q, &ib);
- bfa_q_qe_init(&ib->qe);
-
- ib->ref_count = 1;
- ib->start_count = 0;
- ib->idx_mask = 0;
-
- ib->intr = intr;
- ib->idx_seg = NULL;
- intr->ib = ib;
-
- ib->bna = ib_mod->bna;
-
- return ib;
-}
-
-static void
-bna_ib_put(struct bna_ib_mod *ib_mod, struct bna_ib *ib)
-{
- bna_intr_put(ib_mod, ib->intr);
-
- ib->ref_count--;
-
- if (ib->ref_count == 0) {
- ib->intr = NULL;
- ib->bna = NULL;
- list_add_tail(&ib->qe, &ib_mod->ib_free_q);
- }
-}
-
-/* Returns index offset - starting from 0 */
-static int
-bna_ib_reserve_idx(struct bna_ib *ib)
-{
- struct bna_ib_mod *ib_mod = &ib->bna->ib_mod;
- struct bna_ibidx_seg *idx_seg;
- int idx;
- int num_idx;
- int q_idx;
-
- /* Find the first free index position */
- bna_ib_find_free_ibidx(ib->idx_mask, idx);
- if (idx == BFI_IBIDX_MAX_SEGSIZE)
- return -1;
-
- /*
- * Calculate the total number of indexes held by this IB,
- * including the index newly reserved above.
- */
- bna_ib_count_ibidx((ib->idx_mask | (1 << idx)), num_idx);
-
- /* See if there is a free space in the index segment held by this IB */
- if (ib->idx_seg && (num_idx <= ib->idx_seg->ib_seg_size)) {
- ib->idx_mask |= (1 << idx);
- return idx;
- }
-
- if (ib->start_count)
- return -1;
-
- /* Allocate a new segment */
- bna_ib_select_segpool(num_idx, q_idx);
- while (1) {
- if (q_idx == BFI_IBIDX_TOTAL_POOLS)
- return -1;
- if (!list_empty(&ib_mod->ibidx_seg_pool[q_idx]))
- break;
- q_idx++;
- }
- bfa_q_deq(&ib_mod->ibidx_seg_pool[q_idx], &idx_seg);
- bfa_q_qe_init(&idx_seg->qe);
-
- /* Free the old segment */
- if (ib->idx_seg) {
- bna_ib_select_segpool(ib->idx_seg->ib_seg_size, q_idx);
- list_add_tail(&ib->idx_seg->qe, &ib_mod->ibidx_seg_pool[q_idx]);
- }
-
- ib->idx_seg = idx_seg;
-
- ib->idx_mask |= (1 << idx);
-
- return idx;
-}
-
-static void
-bna_ib_release_idx(struct bna_ib *ib, int idx)
-{
- struct bna_ib_mod *ib_mod = &ib->bna->ib_mod;
- struct bna_ibidx_seg *idx_seg;
- int num_idx;
- int cur_q_idx;
- int new_q_idx;
-
- ib->idx_mask &= ~(1 << idx);
-
- if (ib->start_count)
- return;
-
- bna_ib_count_ibidx(ib->idx_mask, num_idx);
-
- /*
- * Free the segment, if there are no more indexes in the segment
- * held by this IB
- */
- if (!num_idx) {
- bna_ib_select_segpool(ib->idx_seg->ib_seg_size, cur_q_idx);
- list_add_tail(&ib->idx_seg->qe,
- &ib_mod->ibidx_seg_pool[cur_q_idx]);
- ib->idx_seg = NULL;
- return;
- }
-
- /* See if we can move to a smaller segment */
- bna_ib_select_segpool(num_idx, new_q_idx);
- bna_ib_select_segpool(ib->idx_seg->ib_seg_size, cur_q_idx);
- while (new_q_idx < cur_q_idx) {
- if (!list_empty(&ib_mod->ibidx_seg_pool[new_q_idx]))
- break;
- new_q_idx++;
- }
- if (new_q_idx < cur_q_idx) {
- /* Select the new smaller segment */
- bfa_q_deq(&ib_mod->ibidx_seg_pool[new_q_idx], &idx_seg);
- bfa_q_qe_init(&idx_seg->qe);
- /* Free the old segment */
- list_add_tail(&ib->idx_seg->qe,
- &ib_mod->ibidx_seg_pool[cur_q_idx]);
- ib->idx_seg = idx_seg;
- }
-}
-
-static int
-bna_ib_config(struct bna_ib *ib, struct bna_ib_config *ib_config)
-{
- if (ib->start_count)
- return -1;
-
- ib->ib_config.coalescing_timeo = ib_config->coalescing_timeo;
- ib->ib_config.interpkt_timeo = ib_config->interpkt_timeo;
- ib->ib_config.interpkt_count = ib_config->interpkt_count;
- ib->ib_config.ctrl_flags = ib_config->ctrl_flags;
-
- ib->ib_config.ctrl_flags |= BFI_IB_CF_MASTER_ENABLE;
- if (ib->intr->intr_type == BNA_INTR_T_MSIX)
- ib->ib_config.ctrl_flags |= BFI_IB_CF_MSIX_MODE;
-
- return 0;
-}
-
-static void
-bna_ib_start(struct bna_ib *ib)
-{
- struct bna_ib_blk_mem ib_cfg;
- struct bna_ib_blk_mem *ib_mem;
- u32 pg_num;
- u32 intx_mask;
- int i;
- void __iomem *base_addr;
- unsigned long off;
-
- ib->start_count++;
-
- if (ib->start_count > 1)
- return;
-
- ib_cfg.host_addr_lo = (u32)(ib->ib_seg_host_addr.lsb);
- ib_cfg.host_addr_hi = (u32)(ib->ib_seg_host_addr.msb);
-
- ib_cfg.clsc_n_ctrl_n_msix = (((u32)
- ib->ib_config.coalescing_timeo << 16) |
- ((u32)ib->ib_config.ctrl_flags << 8) |
- (ib->intr->vector));
- ib_cfg.ipkt_n_ent_n_idxof =
- ((u32)
- (ib->ib_config.interpkt_timeo & 0xf) << 16) |
- ((u32)ib->idx_seg->ib_seg_size << 8) |
- (ib->idx_seg->ib_idx_tbl_offset);
- ib_cfg.ipkt_cnt_cfg_n_unacked = ((u32)
- ib->ib_config.interpkt_count << 24);
-
- pg_num = BNA_GET_PAGE_NUM(HQM0_BLK_PG_NUM + ib->bna->port_num,
- HQM_IB_RAM_BASE_OFFSET);
- writel(pg_num, ib->bna->regs.page_addr);
-
- base_addr = BNA_GET_MEM_BASE_ADDR(ib->bna->pcidev.pci_bar_kva,
- HQM_IB_RAM_BASE_OFFSET);
-
- ib_mem = (struct bna_ib_blk_mem *)0;
- off = (unsigned long)&ib_mem[ib->ib_id].host_addr_lo;
- writel(htonl(ib_cfg.host_addr_lo), base_addr + off);
-
- off = (unsigned long)&ib_mem[ib->ib_id].host_addr_hi;
- writel(htonl(ib_cfg.host_addr_hi), base_addr + off);
-
- off = (unsigned long)&ib_mem[ib->ib_id].clsc_n_ctrl_n_msix;
- writel(ib_cfg.clsc_n_ctrl_n_msix, base_addr + off);
-
- off = (unsigned long)&ib_mem[ib->ib_id].ipkt_n_ent_n_idxof;
- writel(ib_cfg.ipkt_n_ent_n_idxof, base_addr + off);
-
- off = (unsigned long)&ib_mem[ib->ib_id].ipkt_cnt_cfg_n_unacked;
- writel(ib_cfg.ipkt_cnt_cfg_n_unacked, base_addr + off);
-
- ib->door_bell.doorbell_ack = BNA_DOORBELL_IB_INT_ACK(
- (u32)ib->ib_config.coalescing_timeo, 0);
-
- pg_num = BNA_GET_PAGE_NUM(HQM0_BLK_PG_NUM + ib->bna->port_num,
- HQM_INDX_TBL_RAM_BASE_OFFSET);
- writel(pg_num, ib->bna->regs.page_addr);
-
- base_addr = BNA_GET_MEM_BASE_ADDR(ib->bna->pcidev.pci_bar_kva,
- HQM_INDX_TBL_RAM_BASE_OFFSET);
- for (i = 0; i < ib->idx_seg->ib_seg_size; i++) {
- off = (unsigned long)
- ((ib->idx_seg->ib_idx_tbl_offset + i) * BFI_IBIDX_SIZE);
- writel(0, base_addr + off);
- }
-
- if (ib->intr->intr_type == BNA_INTR_T_INTX) {
- bna_intx_disable(ib->bna, intx_mask);
- intx_mask &= ~(ib->intr->vector);
- bna_intx_enable(ib->bna, intx_mask);
- }
-}
-
-static void
-bna_ib_stop(struct bna_ib *ib)
-{
- u32 intx_mask;
-
- ib->start_count--;
-
- if (ib->start_count == 0) {
- writel(BNA_DOORBELL_IB_INT_DISABLE,
- ib->door_bell.doorbell_addr);
- if (ib->intr->intr_type == BNA_INTR_T_INTX) {
- bna_intx_disable(ib->bna, intx_mask);
- intx_mask |= (ib->intr->vector);
- bna_intx_enable(ib->bna, intx_mask);
- }
- }
-}
-
-static void
-bna_ib_fail(struct bna_ib *ib)
-{
- ib->start_count = 0;
-}
-
-/**
- * RXF
- */
-static void rxf_enable(struct bna_rxf *rxf);
-static void rxf_disable(struct bna_rxf *rxf);
-static void __rxf_config_set(struct bna_rxf *rxf);
-static void __rxf_rit_set(struct bna_rxf *rxf);
-static void __bna_rxf_stat_clr(struct bna_rxf *rxf);
-static int rxf_process_packet_filter(struct bna_rxf *rxf);
-static int rxf_clear_packet_filter(struct bna_rxf *rxf);
-static void rxf_reset_packet_filter(struct bna_rxf *rxf);
-static void rxf_cb_enabled(void *arg, int status);
-static void rxf_cb_disabled(void *arg, int status);
-static void bna_rxf_cb_stats_cleared(void *arg, int status);
-static void __rxf_enable(struct bna_rxf *rxf);
-static void __rxf_disable(struct bna_rxf *rxf);
-
-bfa_fsm_state_decl(bna_rxf, stopped, struct bna_rxf,
- enum bna_rxf_event);
-bfa_fsm_state_decl(bna_rxf, start_wait, struct bna_rxf,
- enum bna_rxf_event);
-bfa_fsm_state_decl(bna_rxf, cam_fltr_mod_wait, struct bna_rxf,
- enum bna_rxf_event);
-bfa_fsm_state_decl(bna_rxf, started, struct bna_rxf,
- enum bna_rxf_event);
-bfa_fsm_state_decl(bna_rxf, cam_fltr_clr_wait, struct bna_rxf,
- enum bna_rxf_event);
-bfa_fsm_state_decl(bna_rxf, stop_wait, struct bna_rxf,
- enum bna_rxf_event);
-bfa_fsm_state_decl(bna_rxf, pause_wait, struct bna_rxf,
- enum bna_rxf_event);
-bfa_fsm_state_decl(bna_rxf, resume_wait, struct bna_rxf,
- enum bna_rxf_event);
-bfa_fsm_state_decl(bna_rxf, stat_clr_wait, struct bna_rxf,
- enum bna_rxf_event);
-
-static struct bfa_sm_table rxf_sm_table[] = {
- {BFA_SM(bna_rxf_sm_stopped), BNA_RXF_STOPPED},
- {BFA_SM(bna_rxf_sm_start_wait), BNA_RXF_START_WAIT},
- {BFA_SM(bna_rxf_sm_cam_fltr_mod_wait), BNA_RXF_CAM_FLTR_MOD_WAIT},
- {BFA_SM(bna_rxf_sm_started), BNA_RXF_STARTED},
- {BFA_SM(bna_rxf_sm_cam_fltr_clr_wait), BNA_RXF_CAM_FLTR_CLR_WAIT},
- {BFA_SM(bna_rxf_sm_stop_wait), BNA_RXF_STOP_WAIT},
- {BFA_SM(bna_rxf_sm_pause_wait), BNA_RXF_PAUSE_WAIT},
- {BFA_SM(bna_rxf_sm_resume_wait), BNA_RXF_RESUME_WAIT},
- {BFA_SM(bna_rxf_sm_stat_clr_wait), BNA_RXF_STAT_CLR_WAIT}
-};
-
-static void
-bna_rxf_sm_stopped_entry(struct bna_rxf *rxf)
-{
- call_rxf_stop_cbfn(rxf, BNA_CB_SUCCESS);
-}
-
-static void
-bna_rxf_sm_stopped(struct bna_rxf *rxf, enum bna_rxf_event event)
-{
- switch (event) {
- case RXF_E_START:
- bfa_fsm_set_state(rxf, bna_rxf_sm_start_wait);
- break;
-
- case RXF_E_STOP:
- bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
- break;
-
- case RXF_E_FAIL:
- /* No-op */
- break;
-
- case RXF_E_CAM_FLTR_MOD:
- call_rxf_cam_fltr_cbfn(rxf, BNA_CB_SUCCESS);
- break;
-
- case RXF_E_STARTED:
- case RXF_E_STOPPED:
- case RXF_E_CAM_FLTR_RESP:
- /**
- * These events are received due to flushing of mbox
- * when device fails
- */
- /* No-op */
- break;
-
- case RXF_E_PAUSE:
- rxf->rxf_oper_state = BNA_RXF_OPER_STATE_PAUSED;
- call_rxf_pause_cbfn(rxf, BNA_CB_SUCCESS);
- break;
-
- case RXF_E_RESUME:
- rxf->rxf_oper_state = BNA_RXF_OPER_STATE_RUNNING;
- call_rxf_resume_cbfn(rxf, BNA_CB_SUCCESS);
- break;
-
- default:
- bfa_sm_fault(rxf->rx->bna, event);
- }
-}
-
-static void
-bna_rxf_sm_start_wait_entry(struct bna_rxf *rxf)
-{
- __rxf_config_set(rxf);
- __rxf_rit_set(rxf);
- rxf_enable(rxf);
-}
-
-static void
-bna_rxf_sm_start_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
-{
- switch (event) {
- case RXF_E_STOP:
- /**
- * STOP is originated from bnad. When this happens,
- * it can not be waiting for filter update
- */
- call_rxf_start_cbfn(rxf, BNA_CB_INTERRUPT);
- bfa_fsm_set_state(rxf, bna_rxf_sm_stop_wait);
- break;
-
- case RXF_E_FAIL:
- call_rxf_cam_fltr_cbfn(rxf, BNA_CB_SUCCESS);
- call_rxf_start_cbfn(rxf, BNA_CB_FAIL);
- bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
- break;
-
- case RXF_E_CAM_FLTR_MOD:
- /* No-op */
- break;
-
- case RXF_E_STARTED:
- /**
- * Force rxf_process_filter() to go through initial
- * config
- */
- if ((rxf->ucast_active_mac != NULL) &&
- (rxf->ucast_pending_set == 0))
- rxf->ucast_pending_set = 1;
-
- if (rxf->rss_status == BNA_STATUS_T_ENABLED)
- rxf->rxf_flags |= BNA_RXF_FL_RSS_CONFIG_PENDING;
-
- rxf->rxf_flags |= BNA_RXF_FL_VLAN_CONFIG_PENDING;
-
- bfa_fsm_set_state(rxf, bna_rxf_sm_cam_fltr_mod_wait);
- break;
-
- case RXF_E_PAUSE:
- case RXF_E_RESUME:
- rxf->rxf_flags |= BNA_RXF_FL_OPERSTATE_CHANGED;
- break;
-
- default:
- bfa_sm_fault(rxf->rx->bna, event);
- }
-}
-
-static void
-bna_rxf_sm_cam_fltr_mod_wait_entry(struct bna_rxf *rxf)
-{
- if (!rxf_process_packet_filter(rxf)) {
- /* No more pending CAM entries to update */
- bfa_fsm_set_state(rxf, bna_rxf_sm_started);
- }
-}
-
-static void
-bna_rxf_sm_cam_fltr_mod_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
-{
- switch (event) {
- case RXF_E_STOP:
- /**
- * STOP is originated from bnad. When this happens,
- * it can not be waiting for filter update
- */
- call_rxf_start_cbfn(rxf, BNA_CB_INTERRUPT);
- bfa_fsm_set_state(rxf, bna_rxf_sm_cam_fltr_clr_wait);
- break;
-
- case RXF_E_FAIL:
- rxf_reset_packet_filter(rxf);
- call_rxf_cam_fltr_cbfn(rxf, BNA_CB_SUCCESS);
- call_rxf_start_cbfn(rxf, BNA_CB_FAIL);
- bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
- break;
-
- case RXF_E_CAM_FLTR_MOD:
- /* No-op */
- break;
-
- case RXF_E_CAM_FLTR_RESP:
- if (!rxf_process_packet_filter(rxf)) {
- /* No more pending CAM entries to update */
- call_rxf_cam_fltr_cbfn(rxf, BNA_CB_SUCCESS);
- bfa_fsm_set_state(rxf, bna_rxf_sm_started);
- }
- break;
-
- case RXF_E_PAUSE:
- case RXF_E_RESUME:
- rxf->rxf_flags |= BNA_RXF_FL_OPERSTATE_CHANGED;
- break;
-
- default:
- bfa_sm_fault(rxf->rx->bna, event);
- }
-}
-
-static void
-bna_rxf_sm_started_entry(struct bna_rxf *rxf)
-{
- call_rxf_start_cbfn(rxf, BNA_CB_SUCCESS);
-
- if (rxf->rxf_flags & BNA_RXF_FL_OPERSTATE_CHANGED) {
- if (rxf->rxf_oper_state == BNA_RXF_OPER_STATE_PAUSED)
- bfa_fsm_send_event(rxf, RXF_E_PAUSE);
- else
- bfa_fsm_send_event(rxf, RXF_E_RESUME);
- }
-
-}
-
-static void
-bna_rxf_sm_started(struct bna_rxf *rxf, enum bna_rxf_event event)
-{
- switch (event) {
- case RXF_E_STOP:
- bfa_fsm_set_state(rxf, bna_rxf_sm_cam_fltr_clr_wait);
- /* Hack to get FSM start clearing CAM entries */
- bfa_fsm_send_event(rxf, RXF_E_CAM_FLTR_RESP);
- break;
-
- case RXF_E_FAIL:
- rxf_reset_packet_filter(rxf);
- bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
- break;
-
- case RXF_E_CAM_FLTR_MOD:
- bfa_fsm_set_state(rxf, bna_rxf_sm_cam_fltr_mod_wait);
- break;
-
- case RXF_E_PAUSE:
- bfa_fsm_set_state(rxf, bna_rxf_sm_pause_wait);
- break;
-
- case RXF_E_RESUME:
- bfa_fsm_set_state(rxf, bna_rxf_sm_resume_wait);
- break;
-
- default:
- bfa_sm_fault(rxf->rx->bna, event);
- }
-}
-
-static void
-bna_rxf_sm_cam_fltr_clr_wait_entry(struct bna_rxf *rxf)
-{
- /**
- * Note: Do not add rxf_clear_packet_filter here.
- * It will overstep mbox when this transition happens:
- * cam_fltr_mod_wait -> cam_fltr_clr_wait on RXF_E_STOP event
- */
-}
-
-static void
-bna_rxf_sm_cam_fltr_clr_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
-{
- switch (event) {
- case RXF_E_FAIL:
- /**
- * FSM was in the process of stopping, initiated by
- * bnad. When this happens, no one can be waiting for
- * start or filter update
- */
- rxf_reset_packet_filter(rxf);
- bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
- break;
-
- case RXF_E_CAM_FLTR_RESP:
- if (!rxf_clear_packet_filter(rxf)) {
- /* No more pending CAM entries to clear */
- bfa_fsm_set_state(rxf, bna_rxf_sm_stop_wait);
- rxf_disable(rxf);
- }
- break;
-
- default:
- bfa_sm_fault(rxf->rx->bna, event);
- }
-}
-
-static void
-bna_rxf_sm_stop_wait_entry(struct bna_rxf *rxf)
-{
- /**
- * NOTE: Do not add rxf_disable here.
- * It will overstep mbox when this transition happens:
- * start_wait -> stop_wait on RXF_E_STOP event
- */
-}
-
-static void
-bna_rxf_sm_stop_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
-{
- switch (event) {
- case RXF_E_FAIL:
- /**
- * FSM was in the process of stopping, initiated by
- * bnad. When this happens, no one can be waiting for
- * start or filter update
- */
- bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
- break;
-
- case RXF_E_STARTED:
- /**
- * This event is received due to abrupt transition from
- * bna_rxf_sm_start_wait state on receiving
- * RXF_E_STOP event
- */
- rxf_disable(rxf);
- break;
-
- case RXF_E_STOPPED:
- /**
- * FSM was in the process of stopping, initiated by
- * bnad. When this happens, no one can be waiting for
- * start or filter update
- */
- bfa_fsm_set_state(rxf, bna_rxf_sm_stat_clr_wait);
- break;
-
- case RXF_E_PAUSE:
- rxf->rxf_oper_state = BNA_RXF_OPER_STATE_PAUSED;
- break;
-
- case RXF_E_RESUME:
- rxf->rxf_oper_state = BNA_RXF_OPER_STATE_RUNNING;
- break;
-
- default:
- bfa_sm_fault(rxf->rx->bna, event);
- }
-}
-
-static void
-bna_rxf_sm_pause_wait_entry(struct bna_rxf *rxf)
-{
- rxf->rxf_flags &=
- ~(BNA_RXF_FL_OPERSTATE_CHANGED | BNA_RXF_FL_RXF_ENABLED);
- __rxf_disable(rxf);
-}
-
-static void
-bna_rxf_sm_pause_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
-{
- switch (event) {
- case RXF_E_FAIL:
- /**
- * FSM was in the process of disabling rxf, initiated by
- * bnad.
- */
- call_rxf_pause_cbfn(rxf, BNA_CB_FAIL);
- bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
- break;
-
- case RXF_E_STOPPED:
- rxf->rxf_oper_state = BNA_RXF_OPER_STATE_PAUSED;
- call_rxf_pause_cbfn(rxf, BNA_CB_SUCCESS);
- bfa_fsm_set_state(rxf, bna_rxf_sm_started);
- break;
-
- /*
- * Since PAUSE/RESUME can only be sent by bnad, we don't expect
- * any other event during these states
- */
- default:
- bfa_sm_fault(rxf->rx->bna, event);
- }
-}
-
-static void
-bna_rxf_sm_resume_wait_entry(struct bna_rxf *rxf)
-{
- rxf->rxf_flags &= ~(BNA_RXF_FL_OPERSTATE_CHANGED);
- rxf->rxf_flags |= BNA_RXF_FL_RXF_ENABLED;
- __rxf_enable(rxf);
-}
-
-static void
-bna_rxf_sm_resume_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
-{
- switch (event) {
- case RXF_E_FAIL:
- /**
- * FSM was in the process of disabling rxf, initiated by
- * bnad.
- */
- call_rxf_resume_cbfn(rxf, BNA_CB_FAIL);
- bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
- break;
-
- case RXF_E_STARTED:
- rxf->rxf_oper_state = BNA_RXF_OPER_STATE_RUNNING;
- call_rxf_resume_cbfn(rxf, BNA_CB_SUCCESS);
- bfa_fsm_set_state(rxf, bna_rxf_sm_started);
- break;
-
- /*
- * Since PAUSE/RESUME can only be sent by bnad, we don't expect
- * any other event during these states
- */
- default:
- bfa_sm_fault(rxf->rx->bna, event);
- }
-}
-
-static void
-bna_rxf_sm_stat_clr_wait_entry(struct bna_rxf *rxf)
-{
- __bna_rxf_stat_clr(rxf);
-}
-
-static void
-bna_rxf_sm_stat_clr_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
-{
- switch (event) {
- case RXF_E_FAIL:
- case RXF_E_STAT_CLEARED:
- bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
- break;
-
- default:
- bfa_sm_fault(rxf->rx->bna, event);
- }
-}
-
-static void
-__rxf_enable(struct bna_rxf *rxf)
-{
- struct bfi_ll_rxf_multi_req ll_req;
- u32 bm[2] = {0, 0};
-
- if (rxf->rxf_id < 32)
- bm[0] = 1 << rxf->rxf_id;
- else
- bm[1] = 1 << (rxf->rxf_id - 32);
-
- bfi_h2i_set(ll_req.mh, BFI_MC_LL, BFI_LL_H2I_RX_REQ, 0);
- ll_req.rxf_id_mask[0] = htonl(bm[0]);
- ll_req.rxf_id_mask[1] = htonl(bm[1]);
- ll_req.enable = 1;
-
- bna_mbox_qe_fill(&rxf->mbox_qe, &ll_req, sizeof(ll_req),
- rxf_cb_enabled, rxf);
-
- bna_mbox_send(rxf->rx->bna, &rxf->mbox_qe);
-}
-
-static void
-__rxf_disable(struct bna_rxf *rxf)
-{
- struct bfi_ll_rxf_multi_req ll_req;
- u32 bm[2] = {0, 0};
-
- if (rxf->rxf_id < 32)
- bm[0] = 1 << rxf->rxf_id;
- else
- bm[1] = 1 << (rxf->rxf_id - 32);
-
- bfi_h2i_set(ll_req.mh, BFI_MC_LL, BFI_LL_H2I_RX_REQ, 0);
- ll_req.rxf_id_mask[0] = htonl(bm[0]);
- ll_req.rxf_id_mask[1] = htonl(bm[1]);
- ll_req.enable = 0;
-
- bna_mbox_qe_fill(&rxf->mbox_qe, &ll_req, sizeof(ll_req),
- rxf_cb_disabled, rxf);
-
- bna_mbox_send(rxf->rx->bna, &rxf->mbox_qe);
-}
-
-static void
-__rxf_config_set(struct bna_rxf *rxf)
-{
- u32 i;
- struct bna_rss_mem *rss_mem;
- struct bna_rx_fndb_ram *rx_fndb_ram;
- struct bna *bna = rxf->rx->bna;
- void __iomem *base_addr;
- unsigned long off;
-
- base_addr = BNA_GET_MEM_BASE_ADDR(bna->pcidev.pci_bar_kva,
- RSS_TABLE_BASE_OFFSET);
-
- rss_mem = (struct bna_rss_mem *)0;
-
- /* Configure RSS if required */
- if (rxf->ctrl_flags & BNA_RXF_CF_RSS_ENABLE) {
- /* configure RSS Table */
- writel(BNA_GET_PAGE_NUM(RAD0_MEM_BLK_BASE_PG_NUM +
- bna->port_num, RSS_TABLE_BASE_OFFSET),
- bna->regs.page_addr);
-
- /* temporarily disable RSS, while hash value is written */
- off = (unsigned long)&rss_mem[0].type_n_hash;
- writel(0, base_addr + off);
-
- for (i = 0; i < BFI_RSS_HASH_KEY_LEN; i++) {
- off = (unsigned long)
- &rss_mem[0].hash_key[(BFI_RSS_HASH_KEY_LEN - 1) - i];
- writel(htonl(rxf->rss_cfg.toeplitz_hash_key[i]),
- base_addr + off);
- }
-
- off = (unsigned long)&rss_mem[0].type_n_hash;
- writel(rxf->rss_cfg.hash_type | rxf->rss_cfg.hash_mask,
- base_addr + off);
- }
-
- /* Configure RxF */
- writel(BNA_GET_PAGE_NUM(
- LUT0_MEM_BLK_BASE_PG_NUM + (bna->port_num * 2),
- RX_FNDB_RAM_BASE_OFFSET),
- bna->regs.page_addr);
-
- base_addr = BNA_GET_MEM_BASE_ADDR(bna->pcidev.pci_bar_kva,
- RX_FNDB_RAM_BASE_OFFSET);
-
- rx_fndb_ram = (struct bna_rx_fndb_ram *)0;
-
- /* We always use RSS table 0 */
- off = (unsigned long)&rx_fndb_ram[rxf->rxf_id].rss_prop;
- writel(rxf->ctrl_flags & BNA_RXF_CF_RSS_ENABLE,
- base_addr + off);
-
- /* small large buffer enable/disable */
- off = (unsigned long)&rx_fndb_ram[rxf->rxf_id].size_routing_props;
- writel((rxf->ctrl_flags & BNA_RXF_CF_SM_LG_RXQ) | 0x80,
- base_addr + off);
-
- /* RIT offset, HDS forced offset, multicast RxQ Id */
- off = (unsigned long)&rx_fndb_ram[rxf->rxf_id].rit_hds_mcastq;
- writel((rxf->rit_segment->rit_offset << 16) |
- (rxf->forced_offset << 8) |
- (rxf->hds_cfg.hdr_type & BNA_HDS_FORCED) | rxf->mcast_rxq_id,
- base_addr + off);
-
- /*
- * default vlan tag, default function enable, strip vlan bytes,
- * HDS type, header size
- */
-
- off = (unsigned long)&rx_fndb_ram[rxf->rxf_id].control_flags;
- writel(((u32)rxf->default_vlan_tag << 16) |
- (rxf->ctrl_flags &
- (BNA_RXF_CF_DEFAULT_VLAN |
- BNA_RXF_CF_DEFAULT_FUNCTION_ENABLE |
- BNA_RXF_CF_VLAN_STRIP)) |
- (rxf->hds_cfg.hdr_type & ~BNA_HDS_FORCED) |
- rxf->hds_cfg.header_size,
- base_addr + off);
-}
-
-void
-__rxf_vlan_filter_set(struct bna_rxf *rxf, enum bna_status status)
-{
- struct bna *bna = rxf->rx->bna;
- int i;
-
- writel(BNA_GET_PAGE_NUM(LUT0_MEM_BLK_BASE_PG_NUM +
- (bna->port_num * 2), VLAN_RAM_BASE_OFFSET),
- bna->regs.page_addr);
-
- if (status == BNA_STATUS_T_ENABLED) {
- /* enable VLAN filtering on this function */
- for (i = 0; i <= BFI_MAX_VLAN / 32; i++) {
- writel(rxf->vlan_filter_table[i],
- BNA_GET_VLAN_MEM_ENTRY_ADDR
- (bna->pcidev.pci_bar_kva, rxf->rxf_id,
- i * 32));
- }
- } else {
- /* disable VLAN filtering on this function */
- for (i = 0; i <= BFI_MAX_VLAN / 32; i++) {
- writel(0xffffffff,
- BNA_GET_VLAN_MEM_ENTRY_ADDR
- (bna->pcidev.pci_bar_kva, rxf->rxf_id,
- i * 32));
- }
- }
-}
-
-static void
-__rxf_rit_set(struct bna_rxf *rxf)
-{
- struct bna *bna = rxf->rx->bna;
- struct bna_rit_mem *rit_mem;
- int i;
- void __iomem *base_addr;
- unsigned long off;
-
- base_addr = BNA_GET_MEM_BASE_ADDR(bna->pcidev.pci_bar_kva,
- FUNCTION_TO_RXQ_TRANSLATE);
-
- rit_mem = (struct bna_rit_mem *)0;
-
- writel(BNA_GET_PAGE_NUM(RXA0_MEM_BLK_BASE_PG_NUM + bna->port_num,
- FUNCTION_TO_RXQ_TRANSLATE),
- bna->regs.page_addr);
-
- for (i = 0; i < rxf->rit_segment->rit_size; i++) {
- off = (unsigned long)&rit_mem[i + rxf->rit_segment->rit_offset];
- writel(rxf->rit_segment->rit[i].large_rxq_id << 6 |
- rxf->rit_segment->rit[i].small_rxq_id,
- base_addr + off);
- }
-}
-
-static void
-__bna_rxf_stat_clr(struct bna_rxf *rxf)
-{
- struct bfi_ll_stats_req ll_req;
- u32 bm[2] = {0, 0};
-
- if (rxf->rxf_id < 32)
- bm[0] = 1 << rxf->rxf_id;
- else
- bm[1] = 1 << (rxf->rxf_id - 32);
-
- bfi_h2i_set(ll_req.mh, BFI_MC_LL, BFI_LL_H2I_STATS_CLEAR_REQ, 0);
- ll_req.stats_mask = 0;
- ll_req.txf_id_mask[0] = 0;
- ll_req.txf_id_mask[1] = 0;
-
- ll_req.rxf_id_mask[0] = htonl(bm[0]);
- ll_req.rxf_id_mask[1] = htonl(bm[1]);
-
- bna_mbox_qe_fill(&rxf->mbox_qe, &ll_req, sizeof(ll_req),
- bna_rxf_cb_stats_cleared, rxf);
- bna_mbox_send(rxf->rx->bna, &rxf->mbox_qe);
-}
-
-static void
-rxf_enable(struct bna_rxf *rxf)
-{
- if (rxf->rxf_oper_state == BNA_RXF_OPER_STATE_PAUSED)
- bfa_fsm_send_event(rxf, RXF_E_STARTED);
- else {
- rxf->rxf_flags |= BNA_RXF_FL_RXF_ENABLED;
- __rxf_enable(rxf);
- }
-}
-
-static void
-rxf_cb_enabled(void *arg, int status)
-{
- struct bna_rxf *rxf = (struct bna_rxf *)arg;
-
- bfa_q_qe_init(&rxf->mbox_qe.qe);
- bfa_fsm_send_event(rxf, RXF_E_STARTED);
-}
-
-static void
-rxf_disable(struct bna_rxf *rxf)
-{
- if (rxf->rxf_oper_state == BNA_RXF_OPER_STATE_PAUSED)
- bfa_fsm_send_event(rxf, RXF_E_STOPPED);
- else
- rxf->rxf_flags &= ~BNA_RXF_FL_RXF_ENABLED;
- __rxf_disable(rxf);
-}
-
-static void
-rxf_cb_disabled(void *arg, int status)
-{
- struct bna_rxf *rxf = (struct bna_rxf *)arg;
-
- bfa_q_qe_init(&rxf->mbox_qe.qe);
- bfa_fsm_send_event(rxf, RXF_E_STOPPED);
-}
-
-void
-rxf_cb_cam_fltr_mbox_cmd(void *arg, int status)
-{
- struct bna_rxf *rxf = (struct bna_rxf *)arg;
-
- bfa_q_qe_init(&rxf->mbox_qe.qe);
-
- bfa_fsm_send_event(rxf, RXF_E_CAM_FLTR_RESP);
-}
-
-static void
-bna_rxf_cb_stats_cleared(void *arg, int status)
-{
- struct bna_rxf *rxf = (struct bna_rxf *)arg;
-
- bfa_q_qe_init(&rxf->mbox_qe.qe);
- bfa_fsm_send_event(rxf, RXF_E_STAT_CLEARED);
-}
-
-void
-rxf_cam_mbox_cmd(struct bna_rxf *rxf, u8 cmd,
- const struct bna_mac *mac_addr)
-{
- struct bfi_ll_mac_addr_req req;
-
- bfi_h2i_set(req.mh, BFI_MC_LL, cmd, 0);
-
- req.rxf_id = rxf->rxf_id;
- memcpy(&req.mac_addr, (void *)&mac_addr->addr, ETH_ALEN);
-
- bna_mbox_qe_fill(&rxf->mbox_qe, &req, sizeof(req),
- rxf_cb_cam_fltr_mbox_cmd, rxf);
-
- bna_mbox_send(rxf->rx->bna, &rxf->mbox_qe);
-}
-
-static int
-rxf_process_packet_filter_mcast(struct bna_rxf *rxf)
-{
- struct bna_mac *mac = NULL;
- struct list_head *qe;
-
- /* Add multicast entries */
- if (!list_empty(&rxf->mcast_pending_add_q)) {
- bfa_q_deq(&rxf->mcast_pending_add_q, &qe);
- bfa_q_qe_init(qe);
- mac = (struct bna_mac *)qe;
- rxf_cam_mbox_cmd(rxf, BFI_LL_H2I_MAC_MCAST_ADD_REQ, mac);
- list_add_tail(&mac->qe, &rxf->mcast_active_q);
- return 1;
- }
-
- /* Delete multicast entries previousely added */
- if (!list_empty(&rxf->mcast_pending_del_q)) {
- bfa_q_deq(&rxf->mcast_pending_del_q, &qe);
- bfa_q_qe_init(qe);
- mac = (struct bna_mac *)qe;
- rxf_cam_mbox_cmd(rxf, BFI_LL_H2I_MAC_MCAST_DEL_REQ, mac);
- bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
- return 1;
- }
-
- return 0;
-}
-
-static int
-rxf_process_packet_filter_vlan(struct bna_rxf *rxf)
-{
- /* Apply the VLAN filter */
- if (rxf->rxf_flags & BNA_RXF_FL_VLAN_CONFIG_PENDING) {
- rxf->rxf_flags &= ~BNA_RXF_FL_VLAN_CONFIG_PENDING;
- if (!(rxf->rxmode_active & BNA_RXMODE_PROMISC))
- __rxf_vlan_filter_set(rxf, rxf->vlan_filter_status);
- }
-
- /* Apply RSS configuration */
- if (rxf->rxf_flags & BNA_RXF_FL_RSS_CONFIG_PENDING) {
- rxf->rxf_flags &= ~BNA_RXF_FL_RSS_CONFIG_PENDING;
- if (rxf->rss_status == BNA_STATUS_T_DISABLED) {
- /* RSS is being disabled */
- rxf->ctrl_flags &= ~BNA_RXF_CF_RSS_ENABLE;
- __rxf_rit_set(rxf);
- __rxf_config_set(rxf);
- } else {
- /* RSS is being enabled or reconfigured */
- rxf->ctrl_flags |= BNA_RXF_CF_RSS_ENABLE;
- __rxf_rit_set(rxf);
- __rxf_config_set(rxf);
- }
- }
-
- return 0;
-}
-
-/**
- * Processes pending ucast, mcast entry addition/deletion and issues mailbox
- * command. Also processes pending filter configuration - promiscuous mode,
- * default mode, allmutli mode and issues mailbox command or directly applies
- * to h/w
- */
-static int
-rxf_process_packet_filter(struct bna_rxf *rxf)
-{
- /* Set the default MAC first */
- if (rxf->ucast_pending_set > 0) {
- rxf_cam_mbox_cmd(rxf, BFI_LL_H2I_MAC_UCAST_SET_REQ,
- rxf->ucast_active_mac);
- rxf->ucast_pending_set--;
- return 1;
- }
-
- if (rxf_process_packet_filter_ucast(rxf))
- return 1;
-
- if (rxf_process_packet_filter_mcast(rxf))
- return 1;
-
- if (rxf_process_packet_filter_promisc(rxf))
- return 1;
-
- if (rxf_process_packet_filter_allmulti(rxf))
- return 1;
-
- if (rxf_process_packet_filter_vlan(rxf))
- return 1;
-
- return 0;
-}
-
-static int
-rxf_clear_packet_filter_mcast(struct bna_rxf *rxf)
-{
- struct bna_mac *mac = NULL;
- struct list_head *qe;
-
- /* 3. delete pending mcast entries */
- if (!list_empty(&rxf->mcast_pending_del_q)) {
- bfa_q_deq(&rxf->mcast_pending_del_q, &qe);
- bfa_q_qe_init(qe);
- mac = (struct bna_mac *)qe;
- rxf_cam_mbox_cmd(rxf, BFI_LL_H2I_MAC_MCAST_DEL_REQ, mac);
- bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
- return 1;
- }
-
- /* 4. clear active mcast entries; move them to pending_add_q */
- if (!list_empty(&rxf->mcast_active_q)) {
- bfa_q_deq(&rxf->mcast_active_q, &qe);
- bfa_q_qe_init(qe);
- mac = (struct bna_mac *)qe;
- rxf_cam_mbox_cmd(rxf, BFI_LL_H2I_MAC_MCAST_DEL_REQ, mac);
- list_add_tail(&mac->qe, &rxf->mcast_pending_add_q);
- return 1;
- }
-
- return 0;
-}
-
-/**
- * In the rxf stop path, processes pending ucast/mcast delete queue and issues
- * the mailbox command. Moves the active ucast/mcast entries to pending add q,
- * so that they are added to CAM again in the rxf start path. Moves the current
- * filter settings - promiscuous, default, allmutli - to pending filter
- * configuration
- */
-static int
-rxf_clear_packet_filter(struct bna_rxf *rxf)
-{
- if (rxf_clear_packet_filter_ucast(rxf))
- return 1;
-
- if (rxf_clear_packet_filter_mcast(rxf))
- return 1;
-
- /* 5. clear active default MAC in the CAM */
- if (rxf->ucast_pending_set > 0)
- rxf->ucast_pending_set = 0;
-
- if (rxf_clear_packet_filter_promisc(rxf))
- return 1;
-
- if (rxf_clear_packet_filter_allmulti(rxf))
- return 1;
-
- return 0;
-}
-
-static void
-rxf_reset_packet_filter_mcast(struct bna_rxf *rxf)
-{
- struct list_head *qe;
- struct bna_mac *mac;
-
- /* 3. Move active mcast entries to pending_add_q */
- while (!list_empty(&rxf->mcast_active_q)) {
- bfa_q_deq(&rxf->mcast_active_q, &qe);
- bfa_q_qe_init(qe);
- list_add_tail(qe, &rxf->mcast_pending_add_q);
- }
-
- /* 4. Throw away delete pending mcast entries */
- while (!list_empty(&rxf->mcast_pending_del_q)) {
- bfa_q_deq(&rxf->mcast_pending_del_q, &qe);
- bfa_q_qe_init(qe);
- mac = (struct bna_mac *)qe;
- bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
- }
-}
-
-/**
- * In the rxf fail path, throws away the ucast/mcast entries pending for
- * deletion, moves all active ucast/mcast entries to pending queue so that
- * they are added back to CAM in the rxf start path. Also moves the current
- * filter configuration to pending filter configuration.
- */
-static void
-rxf_reset_packet_filter(struct bna_rxf *rxf)
-{
- rxf_reset_packet_filter_ucast(rxf);
-
- rxf_reset_packet_filter_mcast(rxf);
-
- /* 5. Turn off ucast set flag */
- rxf->ucast_pending_set = 0;
-
- rxf_reset_packet_filter_promisc(rxf);
-
- rxf_reset_packet_filter_allmulti(rxf);
-}
-
-static void
-bna_rxf_init(struct bna_rxf *rxf,
- struct bna_rx *rx,
- struct bna_rx_config *q_config)
-{
- struct list_head *qe;
- struct bna_rxp *rxp;
-
- /* rxf_id is initialized during rx_mod init */
- rxf->rx = rx;
-
- INIT_LIST_HEAD(&rxf->ucast_pending_add_q);
- INIT_LIST_HEAD(&rxf->ucast_pending_del_q);
- rxf->ucast_pending_set = 0;
- INIT_LIST_HEAD(&rxf->ucast_active_q);
- rxf->ucast_active_mac = NULL;
-
- INIT_LIST_HEAD(&rxf->mcast_pending_add_q);
- INIT_LIST_HEAD(&rxf->mcast_pending_del_q);
- INIT_LIST_HEAD(&rxf->mcast_active_q);
-
- bfa_q_qe_init(&rxf->mbox_qe.qe);
-
- if (q_config->vlan_strip_status == BNA_STATUS_T_ENABLED)
- rxf->ctrl_flags |= BNA_RXF_CF_VLAN_STRIP;
-
- rxf->rxf_oper_state = (q_config->paused) ?
- BNA_RXF_OPER_STATE_PAUSED : BNA_RXF_OPER_STATE_RUNNING;
-
- bna_rxf_adv_init(rxf, rx, q_config);
-
- rxf->rit_segment = bna_rit_mod_seg_get(&rxf->rx->bna->rit_mod,
- q_config->num_paths);
-
- list_for_each(qe, &rx->rxp_q) {
- rxp = (struct bna_rxp *)qe;
- if (q_config->rxp_type == BNA_RXP_SINGLE)
- rxf->mcast_rxq_id = rxp->rxq.single.only->rxq_id;
- else
- rxf->mcast_rxq_id = rxp->rxq.slr.large->rxq_id;
- break;
- }
-
- rxf->vlan_filter_status = BNA_STATUS_T_DISABLED;
- memset(rxf->vlan_filter_table, 0,
- (sizeof(u32) * ((BFI_MAX_VLAN + 1) / 32)));
-
- /* Set up VLAN 0 for pure priority tagged packets */
- rxf->vlan_filter_table[0] |= 1;
-
- bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
-}
-
-static void
-bna_rxf_uninit(struct bna_rxf *rxf)
-{
- struct bna *bna = rxf->rx->bna;
- struct bna_mac *mac;
-
- bna_rit_mod_seg_put(&rxf->rx->bna->rit_mod, rxf->rit_segment);
- rxf->rit_segment = NULL;
-
- rxf->ucast_pending_set = 0;
-
- while (!list_empty(&rxf->ucast_pending_add_q)) {
- bfa_q_deq(&rxf->ucast_pending_add_q, &mac);
- bfa_q_qe_init(&mac->qe);
- bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
- }
-
- if (rxf->ucast_active_mac) {
- bfa_q_qe_init(&rxf->ucast_active_mac->qe);
- bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod,
- rxf->ucast_active_mac);
- rxf->ucast_active_mac = NULL;
- }
-
- while (!list_empty(&rxf->mcast_pending_add_q)) {
- bfa_q_deq(&rxf->mcast_pending_add_q, &mac);
- bfa_q_qe_init(&mac->qe);
- bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
- }
-
- /* Turn off pending promisc mode */
- if (is_promisc_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- /* system promisc state should be pending */
- BUG_ON(!(bna->rxf_promisc_id == rxf->rxf_id));
- promisc_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- bna->rxf_promisc_id = BFI_MAX_RXF;
- }
- /* Promisc mode should not be active */
- BUG_ON(rxf->rxmode_active & BNA_RXMODE_PROMISC);
-
- /* Turn off pending all-multi mode */
- if (is_allmulti_enable(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask)) {
- allmulti_inactive(rxf->rxmode_pending,
- rxf->rxmode_pending_bitmask);
- }
- /* Allmulti mode should not be active */
- BUG_ON(rxf->rxmode_active & BNA_RXMODE_ALLMULTI);
-
- rxf->rx = NULL;
-}
-
-static void
-bna_rx_cb_rxf_started(struct bna_rx *rx, enum bna_cb_status status)
-{
- bfa_fsm_send_event(rx, RX_E_RXF_STARTED);
- if (rx->rxf.rxf_id < 32)
- rx->bna->rx_mod.rxf_bmap[0] |= ((u32)1 << rx->rxf.rxf_id);
- else
- rx->bna->rx_mod.rxf_bmap[1] |= ((u32)
- 1 << (rx->rxf.rxf_id - 32));
-}
-
-static void
-bna_rxf_start(struct bna_rxf *rxf)
-{
- rxf->start_cbfn = bna_rx_cb_rxf_started;
- rxf->start_cbarg = rxf->rx;
- rxf->rxf_flags &= ~BNA_RXF_FL_FAILED;
- bfa_fsm_send_event(rxf, RXF_E_START);
-}
-
-static void
-bna_rx_cb_rxf_stopped(struct bna_rx *rx, enum bna_cb_status status)
-{
- bfa_fsm_send_event(rx, RX_E_RXF_STOPPED);
- if (rx->rxf.rxf_id < 32)
- rx->bna->rx_mod.rxf_bmap[0] &= ~(u32)1 << rx->rxf.rxf_id;
- else
- rx->bna->rx_mod.rxf_bmap[1] &= ~(u32)
- 1 << (rx->rxf.rxf_id - 32);
-}
-
-static void
-bna_rxf_stop(struct bna_rxf *rxf)
-{
- rxf->stop_cbfn = bna_rx_cb_rxf_stopped;
- rxf->stop_cbarg = rxf->rx;
- bfa_fsm_send_event(rxf, RXF_E_STOP);
-}
-
-static void
-bna_rxf_fail(struct bna_rxf *rxf)
-{
- rxf->rxf_flags |= BNA_RXF_FL_FAILED;
- bfa_fsm_send_event(rxf, RXF_E_FAIL);
-}
-
-int
-bna_rxf_state_get(struct bna_rxf *rxf)
-{
- return bfa_sm_to_state(rxf_sm_table, rxf->fsm);
-}
-
-enum bna_cb_status
-bna_rx_ucast_set(struct bna_rx *rx, u8 *ucmac,
- void (*cbfn)(struct bnad *, struct bna_rx *,
- enum bna_cb_status))
-{
- struct bna_rxf *rxf = &rx->rxf;
-
- if (rxf->ucast_active_mac == NULL) {
- rxf->ucast_active_mac =
- bna_ucam_mod_mac_get(&rxf->rx->bna->ucam_mod);
- if (rxf->ucast_active_mac == NULL)
- return BNA_CB_UCAST_CAM_FULL;
- bfa_q_qe_init(&rxf->ucast_active_mac->qe);
- }
-
- memcpy(rxf->ucast_active_mac->addr, ucmac, ETH_ALEN);
- rxf->ucast_pending_set++;
- rxf->cam_fltr_cbfn = cbfn;
- rxf->cam_fltr_cbarg = rx->bna->bnad;
-
- bfa_fsm_send_event(rxf, RXF_E_CAM_FLTR_MOD);
-
- return BNA_CB_SUCCESS;
-}
-
-enum bna_cb_status
-bna_rx_mcast_add(struct bna_rx *rx, u8 *addr,
- void (*cbfn)(struct bnad *, struct bna_rx *,
- enum bna_cb_status))
-{
- struct bna_rxf *rxf = &rx->rxf;
- struct list_head *qe;
- struct bna_mac *mac;
-
- /* Check if already added */
- list_for_each(qe, &rxf->mcast_active_q) {
- mac = (struct bna_mac *)qe;
- if (BNA_MAC_IS_EQUAL(mac->addr, addr)) {
- if (cbfn)
- (*cbfn)(rx->bna->bnad, rx, BNA_CB_SUCCESS);
- return BNA_CB_SUCCESS;
- }
- }
-
- /* Check if pending addition */
- list_for_each(qe, &rxf->mcast_pending_add_q) {
- mac = (struct bna_mac *)qe;
- if (BNA_MAC_IS_EQUAL(mac->addr, addr)) {
- if (cbfn)
- (*cbfn)(rx->bna->bnad, rx, BNA_CB_SUCCESS);
- return BNA_CB_SUCCESS;
- }
- }
-
- mac = bna_mcam_mod_mac_get(&rxf->rx->bna->mcam_mod);
- if (mac == NULL)
- return BNA_CB_MCAST_LIST_FULL;
- bfa_q_qe_init(&mac->qe);
- memcpy(mac->addr, addr, ETH_ALEN);
- list_add_tail(&mac->qe, &rxf->mcast_pending_add_q);
-
- rxf->cam_fltr_cbfn = cbfn;
- rxf->cam_fltr_cbarg = rx->bna->bnad;
-
- bfa_fsm_send_event(rxf, RXF_E_CAM_FLTR_MOD);
-
- return BNA_CB_SUCCESS;
-}
-
-enum bna_cb_status
-bna_rx_mcast_listset(struct bna_rx *rx, int count, u8 *mclist,
- void (*cbfn)(struct bnad *, struct bna_rx *,
- enum bna_cb_status))
-{
- struct bna_rxf *rxf = &rx->rxf;
- struct list_head list_head;
- struct list_head *qe;
- u8 *mcaddr;
- struct bna_mac *mac;
- struct bna_mac *mac1;
- int skip;
- int delete;
- int need_hw_config = 0;
- int i;
-
- /* Allocate nodes */
- INIT_LIST_HEAD(&list_head);
- for (i = 0, mcaddr = mclist; i < count; i++) {
- mac = bna_mcam_mod_mac_get(&rxf->rx->bna->mcam_mod);
- if (mac == NULL)
- goto err_return;
- bfa_q_qe_init(&mac->qe);
- memcpy(mac->addr, mcaddr, ETH_ALEN);
- list_add_tail(&mac->qe, &list_head);
-
- mcaddr += ETH_ALEN;
- }
-
- /* Schedule for addition */
- while (!list_empty(&list_head)) {
- bfa_q_deq(&list_head, &qe);
- mac = (struct bna_mac *)qe;
- bfa_q_qe_init(&mac->qe);
-
- skip = 0;
-
- /* Skip if already added */
- list_for_each(qe, &rxf->mcast_active_q) {
- mac1 = (struct bna_mac *)qe;
- if (BNA_MAC_IS_EQUAL(mac1->addr, mac->addr)) {
- bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod,
- mac);
- skip = 1;
- break;
- }
- }
-
- if (skip)
- continue;
-
- /* Skip if pending addition */
- list_for_each(qe, &rxf->mcast_pending_add_q) {
- mac1 = (struct bna_mac *)qe;
- if (BNA_MAC_IS_EQUAL(mac1->addr, mac->addr)) {
- bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod,
- mac);
- skip = 1;
- break;
- }
- }
-
- if (skip)
- continue;
-
- need_hw_config = 1;
- list_add_tail(&mac->qe, &rxf->mcast_pending_add_q);
- }
-
- /**
- * Delete the entries that are in the pending_add_q but not
- * in the new list
- */
- while (!list_empty(&rxf->mcast_pending_add_q)) {
- bfa_q_deq(&rxf->mcast_pending_add_q, &qe);
- mac = (struct bna_mac *)qe;
- bfa_q_qe_init(&mac->qe);
- for (i = 0, mcaddr = mclist, delete = 1; i < count; i++) {
- if (BNA_MAC_IS_EQUAL(mcaddr, mac->addr)) {
- delete = 0;
- break;
- }
- mcaddr += ETH_ALEN;
- }
- if (delete)
- bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
- else
- list_add_tail(&mac->qe, &list_head);
- }
- while (!list_empty(&list_head)) {
- bfa_q_deq(&list_head, &qe);
- mac = (struct bna_mac *)qe;
- bfa_q_qe_init(&mac->qe);
- list_add_tail(&mac->qe, &rxf->mcast_pending_add_q);
- }
-
- /**
- * Schedule entries for deletion that are in the active_q but not
- * in the new list
- */
- while (!list_empty(&rxf->mcast_active_q)) {
- bfa_q_deq(&rxf->mcast_active_q, &qe);
- mac = (struct bna_mac *)qe;
- bfa_q_qe_init(&mac->qe);
- for (i = 0, mcaddr = mclist, delete = 1; i < count; i++) {
- if (BNA_MAC_IS_EQUAL(mcaddr, mac->addr)) {
- delete = 0;
- break;
- }
- mcaddr += ETH_ALEN;
- }
- if (delete) {
- list_add_tail(&mac->qe, &rxf->mcast_pending_del_q);
- need_hw_config = 1;
- } else {
- list_add_tail(&mac->qe, &list_head);
- }
- }
- while (!list_empty(&list_head)) {
- bfa_q_deq(&list_head, &qe);
- mac = (struct bna_mac *)qe;
- bfa_q_qe_init(&mac->qe);
- list_add_tail(&mac->qe, &rxf->mcast_active_q);
- }
-
- if (need_hw_config) {
- rxf->cam_fltr_cbfn = cbfn;
- rxf->cam_fltr_cbarg = rx->bna->bnad;
- bfa_fsm_send_event(rxf, RXF_E_CAM_FLTR_MOD);
- } else if (cbfn)
- (*cbfn)(rx->bna->bnad, rx, BNA_CB_SUCCESS);
-
- return BNA_CB_SUCCESS;
-
-err_return:
- while (!list_empty(&list_head)) {
- bfa_q_deq(&list_head, &qe);
- mac = (struct bna_mac *)qe;
- bfa_q_qe_init(&mac->qe);
- bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
- }
-
- return BNA_CB_MCAST_LIST_FULL;
-}
-
-void
-bna_rx_vlan_add(struct bna_rx *rx, int vlan_id)
-{
- struct bna_rxf *rxf = &rx->rxf;
- int index = (vlan_id >> 5);
- int bit = (1 << (vlan_id & 0x1F));
-
- rxf->vlan_filter_table[index] |= bit;
- if (rxf->vlan_filter_status == BNA_STATUS_T_ENABLED) {
- rxf->rxf_flags |= BNA_RXF_FL_VLAN_CONFIG_PENDING;
- bfa_fsm_send_event(rxf, RXF_E_CAM_FLTR_MOD);
- }
-}
-
-void
-bna_rx_vlan_del(struct bna_rx *rx, int vlan_id)
-{
- struct bna_rxf *rxf = &rx->rxf;
- int index = (vlan_id >> 5);
- int bit = (1 << (vlan_id & 0x1F));
-
- rxf->vlan_filter_table[index] &= ~bit;
- if (rxf->vlan_filter_status == BNA_STATUS_T_ENABLED) {
- rxf->rxf_flags |= BNA_RXF_FL_VLAN_CONFIG_PENDING;
- bfa_fsm_send_event(rxf, RXF_E_CAM_FLTR_MOD);
- }
-}
-
-/**
- * RX
- */
-#define RXQ_RCB_INIT(q, rxp, qdepth, bna, _id, unmapq_mem) do { \
- struct bna_doorbell_qset *_qset; \
- unsigned long off; \
- (q)->rcb->producer_index = (q)->rcb->consumer_index = 0; \
- (q)->rcb->q_depth = (qdepth); \
- (q)->rcb->unmap_q = unmapq_mem; \
- (q)->rcb->rxq = (q); \
- (q)->rcb->cq = &(rxp)->cq; \
- (q)->rcb->bnad = (bna)->bnad; \
- _qset = (struct bna_doorbell_qset *)0; \
- off = (unsigned long)&_qset[(q)->rxq_id].rxq[0]; \
- (q)->rcb->q_dbell = off + \
- BNA_GET_DOORBELL_BASE_ADDR((bna)->pcidev.pci_bar_kva); \
- (q)->rcb->id = _id; \
-} while (0)
-
-#define BNA_GET_RXQS(qcfg) (((qcfg)->rxp_type == BNA_RXP_SINGLE) ? \
- (qcfg)->num_paths : ((qcfg)->num_paths * 2))
-
-#define SIZE_TO_PAGES(size) (((size) >> PAGE_SHIFT) + ((((size) &\
- (PAGE_SIZE - 1)) + (PAGE_SIZE - 1)) >> PAGE_SHIFT))
-
-#define call_rx_stop_callback(rx, status) \
- if ((rx)->stop_cbfn) { \
- (*(rx)->stop_cbfn)((rx)->stop_cbarg, rx, (status)); \
- (rx)->stop_cbfn = NULL; \
- (rx)->stop_cbarg = NULL; \
- }
-
-/*
- * Since rx_enable is synchronous callback, there is no start_cbfn required.
- * Instead, we'll call bnad_rx_post(rxp) so that bnad can post the buffers
- * for each rxpath.
- */
-
-#define call_rx_disable_cbfn(rx, status) \
- if ((rx)->disable_cbfn) { \
- (*(rx)->disable_cbfn)((rx)->disable_cbarg, \
- status); \
- (rx)->disable_cbfn = NULL; \
- (rx)->disable_cbarg = NULL; \
- } \
-
-#define rxqs_reqd(type, num_rxqs) \
- (((type) == BNA_RXP_SINGLE) ? (num_rxqs) : ((num_rxqs) * 2))
-
-#define rx_ib_fail(rx) \
-do { \
- struct bna_rxp *rxp; \
- struct list_head *qe; \
- list_for_each(qe, &(rx)->rxp_q) { \
- rxp = (struct bna_rxp *)qe; \
- bna_ib_fail(rxp->cq.ib); \
- } \
-} while (0)
-
-static void __bna_multi_rxq_stop(struct bna_rxp *, u32 *);
-static void __bna_rxq_start(struct bna_rxq *rxq);
-static void __bna_cq_start(struct bna_cq *cq);
-static void bna_rit_create(struct bna_rx *rx);
-static void bna_rx_cb_multi_rxq_stopped(void *arg, int status);
-static void bna_rx_cb_rxq_stopped_all(void *arg);
-
-bfa_fsm_state_decl(bna_rx, stopped,
- struct bna_rx, enum bna_rx_event);
-bfa_fsm_state_decl(bna_rx, rxf_start_wait,
- struct bna_rx, enum bna_rx_event);
-bfa_fsm_state_decl(bna_rx, started,
- struct bna_rx, enum bna_rx_event);
-bfa_fsm_state_decl(bna_rx, rxf_stop_wait,
- struct bna_rx, enum bna_rx_event);
-bfa_fsm_state_decl(bna_rx, rxq_stop_wait,
- struct bna_rx, enum bna_rx_event);
-
-static const struct bfa_sm_table rx_sm_table[] = {
- {BFA_SM(bna_rx_sm_stopped), BNA_RX_STOPPED},
- {BFA_SM(bna_rx_sm_rxf_start_wait), BNA_RX_RXF_START_WAIT},
- {BFA_SM(bna_rx_sm_started), BNA_RX_STARTED},
- {BFA_SM(bna_rx_sm_rxf_stop_wait), BNA_RX_RXF_STOP_WAIT},
- {BFA_SM(bna_rx_sm_rxq_stop_wait), BNA_RX_RXQ_STOP_WAIT},
-};
-
-static void bna_rx_sm_stopped_entry(struct bna_rx *rx)
-{
- struct bna_rxp *rxp;
- struct list_head *qe_rxp;
-
- list_for_each(qe_rxp, &rx->rxp_q) {
- rxp = (struct bna_rxp *)qe_rxp;
- rx->rx_cleanup_cbfn(rx->bna->bnad, rxp->cq.ccb);
- }
-
- call_rx_stop_callback(rx, BNA_CB_SUCCESS);
-}
-
-static void bna_rx_sm_stopped(struct bna_rx *rx,
- enum bna_rx_event event)
-{
- switch (event) {
- case RX_E_START:
- bfa_fsm_set_state(rx, bna_rx_sm_rxf_start_wait);
- break;
- case RX_E_STOP:
- call_rx_stop_callback(rx, BNA_CB_SUCCESS);
- break;
- case RX_E_FAIL:
- /* no-op */
- break;
- default:
- bfa_sm_fault(rx->bna, event);
- break;
- }
-
-}
-
-static void bna_rx_sm_rxf_start_wait_entry(struct bna_rx *rx)
-{
- struct bna_rxp *rxp;
- struct list_head *qe_rxp;
- struct bna_rxq *q0 = NULL, *q1 = NULL;
-
- /* Setup the RIT */
- bna_rit_create(rx);
-
- list_for_each(qe_rxp, &rx->rxp_q) {
- rxp = (struct bna_rxp *)qe_rxp;
- bna_ib_start(rxp->cq.ib);
- GET_RXQS(rxp, q0, q1);
- q0->buffer_size = bna_port_mtu_get(&rx->bna->port);
- __bna_rxq_start(q0);
- rx->rx_post_cbfn(rx->bna->bnad, q0->rcb);
- if (q1) {
- __bna_rxq_start(q1);
- rx->rx_post_cbfn(rx->bna->bnad, q1->rcb);
- }
- __bna_cq_start(&rxp->cq);
- }
-
- bna_rxf_start(&rx->rxf);
-}
-
-static void bna_rx_sm_rxf_start_wait(struct bna_rx *rx,
- enum bna_rx_event event)
-{
- switch (event) {
- case RX_E_STOP:
- bfa_fsm_set_state(rx, bna_rx_sm_rxf_stop_wait);
- break;
- case RX_E_FAIL:
- bfa_fsm_set_state(rx, bna_rx_sm_stopped);
- rx_ib_fail(rx);
- bna_rxf_fail(&rx->rxf);
- break;
- case RX_E_RXF_STARTED:
- bfa_fsm_set_state(rx, bna_rx_sm_started);
- break;
- default:
- bfa_sm_fault(rx->bna, event);
- break;
- }
-}
-
-void
-bna_rx_sm_started_entry(struct bna_rx *rx)
-{
- struct bna_rxp *rxp;
- struct list_head *qe_rxp;
-
- /* Start IB */
- list_for_each(qe_rxp, &rx->rxp_q) {
- rxp = (struct bna_rxp *)qe_rxp;
- bna_ib_ack(&rxp->cq.ib->door_bell, 0);
- }
-
- bna_llport_rx_started(&rx->bna->port.llport);
-}
-
-void
-bna_rx_sm_started(struct bna_rx *rx, enum bna_rx_event event)
-{
- switch (event) {
- case RX_E_FAIL:
- bna_llport_rx_stopped(&rx->bna->port.llport);
- bfa_fsm_set_state(rx, bna_rx_sm_stopped);
- rx_ib_fail(rx);
- bna_rxf_fail(&rx->rxf);
- break;
- case RX_E_STOP:
- bna_llport_rx_stopped(&rx->bna->port.llport);
- bfa_fsm_set_state(rx, bna_rx_sm_rxf_stop_wait);
- break;
- default:
- bfa_sm_fault(rx->bna, event);
- break;
- }
-}
-
-void
-bna_rx_sm_rxf_stop_wait_entry(struct bna_rx *rx)
-{
- bna_rxf_stop(&rx->rxf);
-}
-
-void
-bna_rx_sm_rxf_stop_wait(struct bna_rx *rx, enum bna_rx_event event)
-{
- switch (event) {
- case RX_E_RXF_STOPPED:
- bfa_fsm_set_state(rx, bna_rx_sm_rxq_stop_wait);
- break;
- case RX_E_RXF_STARTED:
- /**
- * RxF was in the process of starting up when
- * RXF_E_STOP was issued. Ignore this event
- */
- break;
- case RX_E_FAIL:
- bfa_fsm_set_state(rx, bna_rx_sm_stopped);
- rx_ib_fail(rx);
- bna_rxf_fail(&rx->rxf);
- break;
- default:
- bfa_sm_fault(rx->bna, event);
- break;
- }
-
-}
-
-void
-bna_rx_sm_rxq_stop_wait_entry(struct bna_rx *rx)
-{
- struct bna_rxp *rxp = NULL;
- struct bna_rxq *q0 = NULL;
- struct bna_rxq *q1 = NULL;
- struct list_head *qe;
- u32 rxq_mask[2] = {0, 0};
-
- /* Only one call to multi-rxq-stop for all RXPs in this RX */
- bfa_wc_up(&rx->rxq_stop_wc);
- list_for_each(qe, &rx->rxp_q) {
- rxp = (struct bna_rxp *)qe;
- GET_RXQS(rxp, q0, q1);
- if (q0->rxq_id < 32)
- rxq_mask[0] |= ((u32)1 << q0->rxq_id);
- else
- rxq_mask[1] |= ((u32)1 << (q0->rxq_id - 32));
- if (q1) {
- if (q1->rxq_id < 32)
- rxq_mask[0] |= ((u32)1 << q1->rxq_id);
- else
- rxq_mask[1] |= ((u32)
- 1 << (q1->rxq_id - 32));
- }
- }
-
- __bna_multi_rxq_stop(rxp, rxq_mask);
-}
-
-void
-bna_rx_sm_rxq_stop_wait(struct bna_rx *rx, enum bna_rx_event event)
-{
- struct bna_rxp *rxp = NULL;
- struct list_head *qe;
-
- switch (event) {
- case RX_E_RXQ_STOPPED:
- list_for_each(qe, &rx->rxp_q) {
- rxp = (struct bna_rxp *)qe;
- bna_ib_stop(rxp->cq.ib);
- }
- /* Fall through */
- case RX_E_FAIL:
- bfa_fsm_set_state(rx, bna_rx_sm_stopped);
- break;
- default:
- bfa_sm_fault(rx->bna, event);
- break;
- }
-}
-
-void
-__bna_multi_rxq_stop(struct bna_rxp *rxp, u32 * rxq_id_mask)
-{
- struct bfi_ll_q_stop_req ll_req;
-
- bfi_h2i_set(ll_req.mh, BFI_MC_LL, BFI_LL_H2I_RXQ_STOP_REQ, 0);
- ll_req.q_id_mask[0] = htonl(rxq_id_mask[0]);
- ll_req.q_id_mask[1] = htonl(rxq_id_mask[1]);
- bna_mbox_qe_fill(&rxp->mbox_qe, &ll_req, sizeof(ll_req),
- bna_rx_cb_multi_rxq_stopped, rxp);
- bna_mbox_send(rxp->rx->bna, &rxp->mbox_qe);
-}
-
-void
-__bna_rxq_start(struct bna_rxq *rxq)
-{
- struct bna_rxtx_q_mem *q_mem;
- struct bna_rxq_mem rxq_cfg, *rxq_mem;
- struct bna_dma_addr cur_q_addr;
- /* struct bna_doorbell_qset *qset; */
- struct bna_qpt *qpt;
- u32 pg_num;
- struct bna *bna = rxq->rx->bna;
- void __iomem *base_addr;
- unsigned long off;
-
- qpt = &rxq->qpt;
- cur_q_addr = *((struct bna_dma_addr *)(qpt->kv_qpt_ptr));
-
- rxq_cfg.pg_tbl_addr_lo = qpt->hw_qpt_ptr.lsb;
- rxq_cfg.pg_tbl_addr_hi = qpt->hw_qpt_ptr.msb;
- rxq_cfg.cur_q_entry_lo = cur_q_addr.lsb;
- rxq_cfg.cur_q_entry_hi = cur_q_addr.msb;
-
- rxq_cfg.pg_cnt_n_prd_ptr = ((u32)qpt->page_count << 16) | 0x0;
- rxq_cfg.entry_n_pg_size = ((u32)(BFI_RXQ_WI_SIZE >> 2) << 16) |
- (qpt->page_size >> 2);
- rxq_cfg.sg_n_cq_n_cns_ptr =
- ((u32)(rxq->rxp->cq.cq_id & 0xff) << 16) | 0x0;
- rxq_cfg.buf_sz_n_q_state = ((u32)rxq->buffer_size << 16) |
- BNA_Q_IDLE_STATE;
- rxq_cfg.next_qid = 0x0 | (0x3 << 8);
-
- /* Write the page number register */
- pg_num = BNA_GET_PAGE_NUM(HQM0_BLK_PG_NUM + bna->port_num,
- HQM_RXTX_Q_RAM_BASE_OFFSET);
- writel(pg_num, bna->regs.page_addr);
-
- /* Write to h/w */
- base_addr = BNA_GET_MEM_BASE_ADDR(bna->pcidev.pci_bar_kva,
- HQM_RXTX_Q_RAM_BASE_OFFSET);
-
- q_mem = (struct bna_rxtx_q_mem *)0;
- rxq_mem = &q_mem[rxq->rxq_id].rxq;
-
- off = (unsigned long)&rxq_mem->pg_tbl_addr_lo;
- writel(htonl(rxq_cfg.pg_tbl_addr_lo), base_addr + off);
-
- off = (unsigned long)&rxq_mem->pg_tbl_addr_hi;
- writel(htonl(rxq_cfg.pg_tbl_addr_hi), base_addr + off);
-
- off = (unsigned long)&rxq_mem->cur_q_entry_lo;
- writel(htonl(rxq_cfg.cur_q_entry_lo), base_addr + off);
-
- off = (unsigned long)&rxq_mem->cur_q_entry_hi;
- writel(htonl(rxq_cfg.cur_q_entry_hi), base_addr + off);
-
- off = (unsigned long)&rxq_mem->pg_cnt_n_prd_ptr;
- writel(rxq_cfg.pg_cnt_n_prd_ptr, base_addr + off);
-
- off = (unsigned long)&rxq_mem->entry_n_pg_size;
- writel(rxq_cfg.entry_n_pg_size, base_addr + off);
-
- off = (unsigned long)&rxq_mem->sg_n_cq_n_cns_ptr;
- writel(rxq_cfg.sg_n_cq_n_cns_ptr, base_addr + off);
-
- off = (unsigned long)&rxq_mem->buf_sz_n_q_state;
- writel(rxq_cfg.buf_sz_n_q_state, base_addr + off);
-
- off = (unsigned long)&rxq_mem->next_qid;
- writel(rxq_cfg.next_qid, base_addr + off);
-
- rxq->rcb->producer_index = 0;
- rxq->rcb->consumer_index = 0;
-}
-
-void
-__bna_cq_start(struct bna_cq *cq)
-{
- struct bna_cq_mem cq_cfg, *cq_mem;
- const struct bna_qpt *qpt;
- struct bna_dma_addr cur_q_addr;
- u32 pg_num;
- struct bna *bna = cq->rx->bna;
- void __iomem *base_addr;
- unsigned long off;
-
- qpt = &cq->qpt;
- cur_q_addr = *((struct bna_dma_addr *)(qpt->kv_qpt_ptr));
-
- /*
- * Fill out structure, to be subsequently written
- * to hardware
- */
- cq_cfg.pg_tbl_addr_lo = qpt->hw_qpt_ptr.lsb;
- cq_cfg.pg_tbl_addr_hi = qpt->hw_qpt_ptr.msb;
- cq_cfg.cur_q_entry_lo = cur_q_addr.lsb;
- cq_cfg.cur_q_entry_hi = cur_q_addr.msb;
-
- cq_cfg.pg_cnt_n_prd_ptr = (qpt->page_count << 16) | 0x0;
- cq_cfg.entry_n_pg_size =
- ((u32)(BFI_CQ_WI_SIZE >> 2) << 16) | (qpt->page_size >> 2);
- cq_cfg.int_blk_n_cns_ptr = ((((u32)cq->ib_seg_offset) << 24) |
- ((u32)(cq->ib->ib_id & 0xff) << 16) | 0x0);
- cq_cfg.q_state = BNA_Q_IDLE_STATE;
-
- /* Write the page number register */
- pg_num = BNA_GET_PAGE_NUM(HQM0_BLK_PG_NUM + bna->port_num,
- HQM_CQ_RAM_BASE_OFFSET);
-
- writel(pg_num, bna->regs.page_addr);
-
- /* H/W write */
- base_addr = BNA_GET_MEM_BASE_ADDR(bna->pcidev.pci_bar_kva,
- HQM_CQ_RAM_BASE_OFFSET);
-
- cq_mem = (struct bna_cq_mem *)0;
-
- off = (unsigned long)&cq_mem[cq->cq_id].pg_tbl_addr_lo;
- writel(htonl(cq_cfg.pg_tbl_addr_lo), base_addr + off);
-
- off = (unsigned long)&cq_mem[cq->cq_id].pg_tbl_addr_hi;
- writel(htonl(cq_cfg.pg_tbl_addr_hi), base_addr + off);
-
- off = (unsigned long)&cq_mem[cq->cq_id].cur_q_entry_lo;
- writel(htonl(cq_cfg.cur_q_entry_lo), base_addr + off);
-
- off = (unsigned long)&cq_mem[cq->cq_id].cur_q_entry_hi;
- writel(htonl(cq_cfg.cur_q_entry_hi), base_addr + off);
-
- off = (unsigned long)&cq_mem[cq->cq_id].pg_cnt_n_prd_ptr;
- writel(cq_cfg.pg_cnt_n_prd_ptr, base_addr + off);
-
- off = (unsigned long)&cq_mem[cq->cq_id].entry_n_pg_size;
- writel(cq_cfg.entry_n_pg_size, base_addr + off);
-
- off = (unsigned long)&cq_mem[cq->cq_id].int_blk_n_cns_ptr;
- writel(cq_cfg.int_blk_n_cns_ptr, base_addr + off);
-
- off = (unsigned long)&cq_mem[cq->cq_id].q_state;
- writel(cq_cfg.q_state, base_addr + off);
-
- cq->ccb->producer_index = 0;
- *(cq->ccb->hw_producer_index) = 0;
-}
-
-void
-bna_rit_create(struct bna_rx *rx)
-{
- struct list_head *qe_rxp;
- struct bna_rxp *rxp;
- struct bna_rxq *q0 = NULL;
- struct bna_rxq *q1 = NULL;
- int offset;
-
- offset = 0;
- list_for_each(qe_rxp, &rx->rxp_q) {
- rxp = (struct bna_rxp *)qe_rxp;
- GET_RXQS(rxp, q0, q1);
- rx->rxf.rit_segment->rit[offset].large_rxq_id = q0->rxq_id;
- rx->rxf.rit_segment->rit[offset].small_rxq_id =
- (q1 ? q1->rxq_id : 0);
- offset++;
- }
-}
-
-static int
-_rx_can_satisfy(struct bna_rx_mod *rx_mod,
- struct bna_rx_config *rx_cfg)
-{
- if ((rx_mod->rx_free_count == 0) ||
- (rx_mod->rxp_free_count == 0) ||
- (rx_mod->rxq_free_count == 0))
- return 0;
-
- if (rx_cfg->rxp_type == BNA_RXP_SINGLE) {
- if ((rx_mod->rxp_free_count < rx_cfg->num_paths) ||
- (rx_mod->rxq_free_count < rx_cfg->num_paths))
- return 0;
- } else {
- if ((rx_mod->rxp_free_count < rx_cfg->num_paths) ||
- (rx_mod->rxq_free_count < (2 * rx_cfg->num_paths)))
- return 0;
- }
-
- if (!bna_rit_mod_can_satisfy(&rx_mod->bna->rit_mod, rx_cfg->num_paths))
- return 0;
-
- return 1;
-}
-
-static struct bna_rxq *
-_get_free_rxq(struct bna_rx_mod *rx_mod)
-{
- struct bna_rxq *rxq = NULL;
- struct list_head *qe = NULL;
-
- bfa_q_deq(&rx_mod->rxq_free_q, &qe);
- if (qe) {
- rx_mod->rxq_free_count--;
- rxq = (struct bna_rxq *)qe;
- }
- return rxq;
-}
-
-static void
-_put_free_rxq(struct bna_rx_mod *rx_mod, struct bna_rxq *rxq)
-{
- bfa_q_qe_init(&rxq->qe);
- list_add_tail(&rxq->qe, &rx_mod->rxq_free_q);
- rx_mod->rxq_free_count++;
-}
-
-static struct bna_rxp *
-_get_free_rxp(struct bna_rx_mod *rx_mod)
-{
- struct list_head *qe = NULL;
- struct bna_rxp *rxp = NULL;
-
- bfa_q_deq(&rx_mod->rxp_free_q, &qe);
- if (qe) {
- rx_mod->rxp_free_count--;
-
- rxp = (struct bna_rxp *)qe;
- }
-
- return rxp;
-}
-
-static void
-_put_free_rxp(struct bna_rx_mod *rx_mod, struct bna_rxp *rxp)
-{
- bfa_q_qe_init(&rxp->qe);
- list_add_tail(&rxp->qe, &rx_mod->rxp_free_q);
- rx_mod->rxp_free_count++;
-}
-
-static struct bna_rx *
-_get_free_rx(struct bna_rx_mod *rx_mod)
-{
- struct list_head *qe = NULL;
- struct bna_rx *rx = NULL;
-
- bfa_q_deq(&rx_mod->rx_free_q, &qe);
- if (qe) {
- rx_mod->rx_free_count--;
-
- rx = (struct bna_rx *)qe;
- bfa_q_qe_init(qe);
- list_add_tail(&rx->qe, &rx_mod->rx_active_q);
- }
-
- return rx;
-}
-
-static void
-_put_free_rx(struct bna_rx_mod *rx_mod, struct bna_rx *rx)
-{
- bfa_q_qe_init(&rx->qe);
- list_add_tail(&rx->qe, &rx_mod->rx_free_q);
- rx_mod->rx_free_count++;
-}
-
-static void
-_rx_init(struct bna_rx *rx, struct bna *bna)
-{
- rx->bna = bna;
- rx->rx_flags = 0;
-
- INIT_LIST_HEAD(&rx->rxp_q);
-
- rx->rxq_stop_wc.wc_resume = bna_rx_cb_rxq_stopped_all;
- rx->rxq_stop_wc.wc_cbarg = rx;
- rx->rxq_stop_wc.wc_count = 0;
-
- rx->stop_cbfn = NULL;
- rx->stop_cbarg = NULL;
-}
-
-static void
-_rxp_add_rxqs(struct bna_rxp *rxp,
- struct bna_rxq *q0,
- struct bna_rxq *q1)
-{
- switch (rxp->type) {
- case BNA_RXP_SINGLE:
- rxp->rxq.single.only = q0;
- rxp->rxq.single.reserved = NULL;
- break;
- case BNA_RXP_SLR:
- rxp->rxq.slr.large = q0;
- rxp->rxq.slr.small = q1;
- break;
- case BNA_RXP_HDS:
- rxp->rxq.hds.data = q0;
- rxp->rxq.hds.hdr = q1;
- break;
- default:
- break;
- }
-}
-
-static void
-_rxq_qpt_init(struct bna_rxq *rxq,
- struct bna_rxp *rxp,
- u32 page_count,
- u32 page_size,
- struct bna_mem_descr *qpt_mem,
- struct bna_mem_descr *swqpt_mem,
- struct bna_mem_descr *page_mem)
-{
- int i;
-
- rxq->qpt.hw_qpt_ptr.lsb = qpt_mem->dma.lsb;
- rxq->qpt.hw_qpt_ptr.msb = qpt_mem->dma.msb;
- rxq->qpt.kv_qpt_ptr = qpt_mem->kva;
- rxq->qpt.page_count = page_count;
- rxq->qpt.page_size = page_size;
-
- rxq->rcb->sw_qpt = (void **) swqpt_mem->kva;
-
- for (i = 0; i < rxq->qpt.page_count; i++) {
- rxq->rcb->sw_qpt[i] = page_mem[i].kva;
- ((struct bna_dma_addr *)rxq->qpt.kv_qpt_ptr)[i].lsb =
- page_mem[i].dma.lsb;
- ((struct bna_dma_addr *)rxq->qpt.kv_qpt_ptr)[i].msb =
- page_mem[i].dma.msb;
-
- }
-}
-
-static void
-_rxp_cqpt_setup(struct bna_rxp *rxp,
- u32 page_count,
- u32 page_size,
- struct bna_mem_descr *qpt_mem,
- struct bna_mem_descr *swqpt_mem,
- struct bna_mem_descr *page_mem)
-{
- int i;
-
- rxp->cq.qpt.hw_qpt_ptr.lsb = qpt_mem->dma.lsb;
- rxp->cq.qpt.hw_qpt_ptr.msb = qpt_mem->dma.msb;
- rxp->cq.qpt.kv_qpt_ptr = qpt_mem->kva;
- rxp->cq.qpt.page_count = page_count;
- rxp->cq.qpt.page_size = page_size;
-
- rxp->cq.ccb->sw_qpt = (void **) swqpt_mem->kva;
-
- for (i = 0; i < rxp->cq.qpt.page_count; i++) {
- rxp->cq.ccb->sw_qpt[i] = page_mem[i].kva;
-
- ((struct bna_dma_addr *)rxp->cq.qpt.kv_qpt_ptr)[i].lsb =
- page_mem[i].dma.lsb;
- ((struct bna_dma_addr *)rxp->cq.qpt.kv_qpt_ptr)[i].msb =
- page_mem[i].dma.msb;
-
- }
-}
-
-static void
-_rx_add_rxp(struct bna_rx *rx, struct bna_rxp *rxp)
-{
- list_add_tail(&rxp->qe, &rx->rxp_q);
-}
-
-static void
-_init_rxmod_queues(struct bna_rx_mod *rx_mod)
-{
- INIT_LIST_HEAD(&rx_mod->rx_free_q);
- INIT_LIST_HEAD(&rx_mod->rxq_free_q);
- INIT_LIST_HEAD(&rx_mod->rxp_free_q);
- INIT_LIST_HEAD(&rx_mod->rx_active_q);
-
- rx_mod->rx_free_count = 0;
- rx_mod->rxq_free_count = 0;
- rx_mod->rxp_free_count = 0;
-}
-
-static void
-_rx_ctor(struct bna_rx *rx, int id)
-{
- bfa_q_qe_init(&rx->qe);
- INIT_LIST_HEAD(&rx->rxp_q);
- rx->bna = NULL;
-
- rx->rxf.rxf_id = id;
-
- /* FIXME: mbox_qe ctor()?? */
- bfa_q_qe_init(&rx->mbox_qe.qe);
-
- rx->stop_cbfn = NULL;
- rx->stop_cbarg = NULL;
-}
-
-void
-bna_rx_cb_multi_rxq_stopped(void *arg, int status)
-{
- struct bna_rxp *rxp = (struct bna_rxp *)arg;
-
- bfa_wc_down(&rxp->rx->rxq_stop_wc);
-}
-
-void
-bna_rx_cb_rxq_stopped_all(void *arg)
-{
- struct bna_rx *rx = (struct bna_rx *)arg;
-
- bfa_fsm_send_event(rx, RX_E_RXQ_STOPPED);
-}
-
-static void
-bna_rx_mod_cb_rx_stopped(void *arg, struct bna_rx *rx,
- enum bna_cb_status status)
-{
- struct bna_rx_mod *rx_mod = (struct bna_rx_mod *)arg;
-
- bfa_wc_down(&rx_mod->rx_stop_wc);
-}
-
-static void
-bna_rx_mod_cb_rx_stopped_all(void *arg)
-{
- struct bna_rx_mod *rx_mod = (struct bna_rx_mod *)arg;
-
- if (rx_mod->stop_cbfn)
- rx_mod->stop_cbfn(&rx_mod->bna->port, BNA_CB_SUCCESS);
- rx_mod->stop_cbfn = NULL;
-}
-
-static void
-bna_rx_start(struct bna_rx *rx)
-{
- rx->rx_flags |= BNA_RX_F_PORT_ENABLED;
- if (rx->rx_flags & BNA_RX_F_ENABLE)
- bfa_fsm_send_event(rx, RX_E_START);
-}
-
-static void
-bna_rx_stop(struct bna_rx *rx)
-{
- rx->rx_flags &= ~BNA_RX_F_PORT_ENABLED;
- if (rx->fsm == (bfa_fsm_t) bna_rx_sm_stopped)
- bna_rx_mod_cb_rx_stopped(&rx->bna->rx_mod, rx, BNA_CB_SUCCESS);
- else {
- rx->stop_cbfn = bna_rx_mod_cb_rx_stopped;
- rx->stop_cbarg = &rx->bna->rx_mod;
- bfa_fsm_send_event(rx, RX_E_STOP);
- }
-}
-
-static void
-bna_rx_fail(struct bna_rx *rx)
-{
- /* Indicate port is not enabled, and failed */
- rx->rx_flags &= ~BNA_RX_F_PORT_ENABLED;
- rx->rx_flags |= BNA_RX_F_PORT_FAILED;
- bfa_fsm_send_event(rx, RX_E_FAIL);
-}
-
-void
-bna_rx_mod_start(struct bna_rx_mod *rx_mod, enum bna_rx_type type)
-{
- struct bna_rx *rx;
- struct list_head *qe;
-
- rx_mod->flags |= BNA_RX_MOD_F_PORT_STARTED;
- if (type == BNA_RX_T_LOOPBACK)
- rx_mod->flags |= BNA_RX_MOD_F_PORT_LOOPBACK;
-
- list_for_each(qe, &rx_mod->rx_active_q) {
- rx = (struct bna_rx *)qe;
- if (rx->type == type)
- bna_rx_start(rx);
- }
-}
-
-void
-bna_rx_mod_stop(struct bna_rx_mod *rx_mod, enum bna_rx_type type)
-{
- struct bna_rx *rx;
- struct list_head *qe;
-
- rx_mod->flags &= ~BNA_RX_MOD_F_PORT_STARTED;
- rx_mod->flags &= ~BNA_RX_MOD_F_PORT_LOOPBACK;
-
- rx_mod->stop_cbfn = bna_port_cb_rx_stopped;
-
- /**
- * Before calling bna_rx_stop(), increment rx_stop_wc as many times
- * as we are going to call bna_rx_stop
- */
- list_for_each(qe, &rx_mod->rx_active_q) {
- rx = (struct bna_rx *)qe;
- if (rx->type == type)
- bfa_wc_up(&rx_mod->rx_stop_wc);
- }
-
- if (rx_mod->rx_stop_wc.wc_count == 0) {
- rx_mod->stop_cbfn(&rx_mod->bna->port, BNA_CB_SUCCESS);
- rx_mod->stop_cbfn = NULL;
- return;
- }
-
- list_for_each(qe, &rx_mod->rx_active_q) {
- rx = (struct bna_rx *)qe;
- if (rx->type == type)
- bna_rx_stop(rx);
- }
-}
-
-void
-bna_rx_mod_fail(struct bna_rx_mod *rx_mod)
-{
- struct bna_rx *rx;
- struct list_head *qe;
-
- rx_mod->flags &= ~BNA_RX_MOD_F_PORT_STARTED;
- rx_mod->flags &= ~BNA_RX_MOD_F_PORT_LOOPBACK;
-
- list_for_each(qe, &rx_mod->rx_active_q) {
- rx = (struct bna_rx *)qe;
- bna_rx_fail(rx);
- }
-}
-
-void bna_rx_mod_init(struct bna_rx_mod *rx_mod, struct bna *bna,
- struct bna_res_info *res_info)
-{
- int index;
- struct bna_rx *rx_ptr;
- struct bna_rxp *rxp_ptr;
- struct bna_rxq *rxq_ptr;
-
- rx_mod->bna = bna;
- rx_mod->flags = 0;
-
- rx_mod->rx = (struct bna_rx *)
- res_info[BNA_RES_MEM_T_RX_ARRAY].res_u.mem_info.mdl[0].kva;
- rx_mod->rxp = (struct bna_rxp *)
- res_info[BNA_RES_MEM_T_RXP_ARRAY].res_u.mem_info.mdl[0].kva;
- rx_mod->rxq = (struct bna_rxq *)
- res_info[BNA_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.mdl[0].kva;
-
- /* Initialize the queues */
- _init_rxmod_queues(rx_mod);
-
- /* Build RX queues */
- for (index = 0; index < BFI_MAX_RXQ; index++) {
- rx_ptr = &rx_mod->rx[index];
- _rx_ctor(rx_ptr, index);
- list_add_tail(&rx_ptr->qe, &rx_mod->rx_free_q);
- rx_mod->rx_free_count++;
- }
-
- /* build RX-path queue */
- for (index = 0; index < BFI_MAX_RXQ; index++) {
- rxp_ptr = &rx_mod->rxp[index];
- rxp_ptr->cq.cq_id = index;
- bfa_q_qe_init(&rxp_ptr->qe);
- list_add_tail(&rxp_ptr->qe, &rx_mod->rxp_free_q);
- rx_mod->rxp_free_count++;
- }
-
- /* build RXQ queue */
- for (index = 0; index < BFI_MAX_RXQ; index++) {
- rxq_ptr = &rx_mod->rxq[index];
- rxq_ptr->rxq_id = index;
-
- bfa_q_qe_init(&rxq_ptr->qe);
- list_add_tail(&rxq_ptr->qe, &rx_mod->rxq_free_q);
- rx_mod->rxq_free_count++;
- }
-
- rx_mod->rx_stop_wc.wc_resume = bna_rx_mod_cb_rx_stopped_all;
- rx_mod->rx_stop_wc.wc_cbarg = rx_mod;
- rx_mod->rx_stop_wc.wc_count = 0;
-}
-
-void
-bna_rx_mod_uninit(struct bna_rx_mod *rx_mod)
-{
- struct list_head *qe;
- int i;
-
- i = 0;
- list_for_each(qe, &rx_mod->rx_free_q)
- i++;
-
- i = 0;
- list_for_each(qe, &rx_mod->rxp_free_q)
- i++;
-
- i = 0;
- list_for_each(qe, &rx_mod->rxq_free_q)
- i++;
-
- rx_mod->bna = NULL;
-}
-
-int
-bna_rx_state_get(struct bna_rx *rx)
-{
- return bfa_sm_to_state(rx_sm_table, rx->fsm);
-}
-
-void
-bna_rx_res_req(struct bna_rx_config *q_cfg, struct bna_res_info *res_info)
-{
- u32 cq_size, hq_size, dq_size;
- u32 cpage_count, hpage_count, dpage_count;
- struct bna_mem_info *mem_info;
- u32 cq_depth;
- u32 hq_depth;
- u32 dq_depth;
-
- dq_depth = q_cfg->q_depth;
- hq_depth = ((q_cfg->rxp_type == BNA_RXP_SINGLE) ? 0 : q_cfg->q_depth);
- cq_depth = dq_depth + hq_depth;
-
- BNA_TO_POWER_OF_2_HIGH(cq_depth);
- cq_size = cq_depth * BFI_CQ_WI_SIZE;
- cq_size = ALIGN(cq_size, PAGE_SIZE);
- cpage_count = SIZE_TO_PAGES(cq_size);
-
- BNA_TO_POWER_OF_2_HIGH(dq_depth);
- dq_size = dq_depth * BFI_RXQ_WI_SIZE;
- dq_size = ALIGN(dq_size, PAGE_SIZE);
- dpage_count = SIZE_TO_PAGES(dq_size);
-
- if (BNA_RXP_SINGLE != q_cfg->rxp_type) {
- BNA_TO_POWER_OF_2_HIGH(hq_depth);
- hq_size = hq_depth * BFI_RXQ_WI_SIZE;
- hq_size = ALIGN(hq_size, PAGE_SIZE);
- hpage_count = SIZE_TO_PAGES(hq_size);
- } else {
- hpage_count = 0;
- }
-
- /* CCB structures */
- res_info[BNA_RX_RES_MEM_T_CCB].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_CCB].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_KVA;
- mem_info->len = sizeof(struct bna_ccb);
- mem_info->num = q_cfg->num_paths;
-
- /* RCB structures */
- res_info[BNA_RX_RES_MEM_T_RCB].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_RCB].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_KVA;
- mem_info->len = sizeof(struct bna_rcb);
- mem_info->num = BNA_GET_RXQS(q_cfg);
-
- /* Completion QPT */
- res_info[BNA_RX_RES_MEM_T_CQPT].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_CQPT].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_DMA;
- mem_info->len = cpage_count * sizeof(struct bna_dma_addr);
- mem_info->num = q_cfg->num_paths;
-
- /* Completion s/w QPT */
- res_info[BNA_RX_RES_MEM_T_CSWQPT].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_CSWQPT].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_KVA;
- mem_info->len = cpage_count * sizeof(void *);
- mem_info->num = q_cfg->num_paths;
-
- /* Completion QPT pages */
- res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_DMA;
- mem_info->len = PAGE_SIZE;
- mem_info->num = cpage_count * q_cfg->num_paths;
-
- /* Data QPTs */
- res_info[BNA_RX_RES_MEM_T_DQPT].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_DQPT].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_DMA;
- mem_info->len = dpage_count * sizeof(struct bna_dma_addr);
- mem_info->num = q_cfg->num_paths;
-
- /* Data s/w QPTs */
- res_info[BNA_RX_RES_MEM_T_DSWQPT].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_DSWQPT].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_KVA;
- mem_info->len = dpage_count * sizeof(void *);
- mem_info->num = q_cfg->num_paths;
-
- /* Data QPT pages */
- res_info[BNA_RX_RES_MEM_T_DPAGE].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_DPAGE].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_DMA;
- mem_info->len = PAGE_SIZE;
- mem_info->num = dpage_count * q_cfg->num_paths;
-
- /* Hdr QPTs */
- res_info[BNA_RX_RES_MEM_T_HQPT].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_HQPT].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_DMA;
- mem_info->len = hpage_count * sizeof(struct bna_dma_addr);
- mem_info->num = (hpage_count ? q_cfg->num_paths : 0);
-
- /* Hdr s/w QPTs */
- res_info[BNA_RX_RES_MEM_T_HSWQPT].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_HSWQPT].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_KVA;
- mem_info->len = hpage_count * sizeof(void *);
- mem_info->num = (hpage_count ? q_cfg->num_paths : 0);
-
- /* Hdr QPT pages */
- res_info[BNA_RX_RES_MEM_T_HPAGE].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_RX_RES_MEM_T_HPAGE].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_DMA;
- mem_info->len = (hpage_count ? PAGE_SIZE : 0);
- mem_info->num = (hpage_count ? (hpage_count * q_cfg->num_paths) : 0);
-
- /* RX Interrupts */
- res_info[BNA_RX_RES_T_INTR].res_type = BNA_RES_T_INTR;
- res_info[BNA_RX_RES_T_INTR].res_u.intr_info.intr_type = BNA_INTR_T_MSIX;
- res_info[BNA_RX_RES_T_INTR].res_u.intr_info.num = q_cfg->num_paths;
-}
-
-struct bna_rx *
-bna_rx_create(struct bna *bna, struct bnad *bnad,
- struct bna_rx_config *rx_cfg,
- struct bna_rx_event_cbfn *rx_cbfn,
- struct bna_res_info *res_info,
- void *priv)
-{
- struct bna_rx_mod *rx_mod = &bna->rx_mod;
- struct bna_rx *rx;
- struct bna_rxp *rxp;
- struct bna_rxq *q0;
- struct bna_rxq *q1;
- struct bna_intr_info *intr_info;
- u32 page_count;
- struct bna_mem_descr *ccb_mem;
- struct bna_mem_descr *rcb_mem;
- struct bna_mem_descr *unmapq_mem;
- struct bna_mem_descr *cqpt_mem;
- struct bna_mem_descr *cswqpt_mem;
- struct bna_mem_descr *cpage_mem;
- struct bna_mem_descr *hqpt_mem; /* Header/Small Q qpt */
- struct bna_mem_descr *dqpt_mem; /* Data/Large Q qpt */
- struct bna_mem_descr *hsqpt_mem; /* s/w qpt for hdr */
- struct bna_mem_descr *dsqpt_mem; /* s/w qpt for data */
- struct bna_mem_descr *hpage_mem; /* hdr page mem */
- struct bna_mem_descr *dpage_mem; /* data page mem */
- int i, cpage_idx = 0, dpage_idx = 0, hpage_idx = 0;
- int dpage_count, hpage_count, rcb_idx;
- struct bna_ib_config ibcfg;
- /* Fail if we don't have enough RXPs, RXQs */
- if (!_rx_can_satisfy(rx_mod, rx_cfg))
- return NULL;
-
- /* Initialize resource pointers */
- intr_info = &res_info[BNA_RX_RES_T_INTR].res_u.intr_info;
- ccb_mem = &res_info[BNA_RX_RES_MEM_T_CCB].res_u.mem_info.mdl[0];
- rcb_mem = &res_info[BNA_RX_RES_MEM_T_RCB].res_u.mem_info.mdl[0];
- unmapq_mem = &res_info[BNA_RX_RES_MEM_T_UNMAPQ].res_u.mem_info.mdl[0];
- cqpt_mem = &res_info[BNA_RX_RES_MEM_T_CQPT].res_u.mem_info.mdl[0];
- cswqpt_mem = &res_info[BNA_RX_RES_MEM_T_CSWQPT].res_u.mem_info.mdl[0];
- cpage_mem = &res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_u.mem_info.mdl[0];
- hqpt_mem = &res_info[BNA_RX_RES_MEM_T_HQPT].res_u.mem_info.mdl[0];
- dqpt_mem = &res_info[BNA_RX_RES_MEM_T_DQPT].res_u.mem_info.mdl[0];
- hsqpt_mem = &res_info[BNA_RX_RES_MEM_T_HSWQPT].res_u.mem_info.mdl[0];
- dsqpt_mem = &res_info[BNA_RX_RES_MEM_T_DSWQPT].res_u.mem_info.mdl[0];
- hpage_mem = &res_info[BNA_RX_RES_MEM_T_HPAGE].res_u.mem_info.mdl[0];
- dpage_mem = &res_info[BNA_RX_RES_MEM_T_DPAGE].res_u.mem_info.mdl[0];
-
- /* Compute q depth & page count */
- page_count = res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_u.mem_info.num /
- rx_cfg->num_paths;
-
- dpage_count = res_info[BNA_RX_RES_MEM_T_DPAGE].res_u.mem_info.num /
- rx_cfg->num_paths;
-
- hpage_count = res_info[BNA_RX_RES_MEM_T_HPAGE].res_u.mem_info.num /
- rx_cfg->num_paths;
- /* Get RX pointer */
- rx = _get_free_rx(rx_mod);
- _rx_init(rx, bna);
- rx->priv = priv;
- rx->type = rx_cfg->rx_type;
-
- rx->rcb_setup_cbfn = rx_cbfn->rcb_setup_cbfn;
- rx->rcb_destroy_cbfn = rx_cbfn->rcb_destroy_cbfn;
- rx->ccb_setup_cbfn = rx_cbfn->ccb_setup_cbfn;
- rx->ccb_destroy_cbfn = rx_cbfn->ccb_destroy_cbfn;
- /* Following callbacks are mandatory */
- rx->rx_cleanup_cbfn = rx_cbfn->rx_cleanup_cbfn;
- rx->rx_post_cbfn = rx_cbfn->rx_post_cbfn;
-
- if (rx->bna->rx_mod.flags & BNA_RX_MOD_F_PORT_STARTED) {
- switch (rx->type) {
- case BNA_RX_T_REGULAR:
- if (!(rx->bna->rx_mod.flags &
- BNA_RX_MOD_F_PORT_LOOPBACK))
- rx->rx_flags |= BNA_RX_F_PORT_ENABLED;
- break;
- case BNA_RX_T_LOOPBACK:
- if (rx->bna->rx_mod.flags & BNA_RX_MOD_F_PORT_LOOPBACK)
- rx->rx_flags |= BNA_RX_F_PORT_ENABLED;
- break;
- }
- }
-
- for (i = 0, rcb_idx = 0; i < rx_cfg->num_paths; i++) {
- rxp = _get_free_rxp(rx_mod);
- rxp->type = rx_cfg->rxp_type;
- rxp->rx = rx;
- rxp->cq.rx = rx;
-
- /* Get required RXQs, and queue them to rx-path */
- q0 = _get_free_rxq(rx_mod);
- if (BNA_RXP_SINGLE == rx_cfg->rxp_type)
- q1 = NULL;
- else
- q1 = _get_free_rxq(rx_mod);
-
- /* Initialize IB */
- if (1 == intr_info->num) {
- rxp->cq.ib = bna_ib_get(&bna->ib_mod,
- intr_info->intr_type,
- intr_info->idl[0].vector);
- rxp->vector = intr_info->idl[0].vector;
- } else {
- rxp->cq.ib = bna_ib_get(&bna->ib_mod,
- intr_info->intr_type,
- intr_info->idl[i].vector);
-
- /* Map the MSI-x vector used for this RXP */
- rxp->vector = intr_info->idl[i].vector;
- }
-
- rxp->cq.ib_seg_offset = bna_ib_reserve_idx(rxp->cq.ib);
-
- ibcfg.coalescing_timeo = BFI_RX_COALESCING_TIMEO;
- ibcfg.interpkt_count = BFI_RX_INTERPKT_COUNT;
- ibcfg.interpkt_timeo = BFI_RX_INTERPKT_TIMEO;
- ibcfg.ctrl_flags = BFI_IB_CF_INT_ENABLE;
-
- bna_ib_config(rxp->cq.ib, &ibcfg);
-
- /* Link rxqs to rxp */
- _rxp_add_rxqs(rxp, q0, q1);
-
- /* Link rxp to rx */
- _rx_add_rxp(rx, rxp);
-
- q0->rx = rx;
- q0->rxp = rxp;
-
- /* Initialize RCB for the large / data q */
- q0->rcb = (struct bna_rcb *) rcb_mem[rcb_idx].kva;
- RXQ_RCB_INIT(q0, rxp, rx_cfg->q_depth, bna, 0,
- (void *)unmapq_mem[rcb_idx].kva);
- rcb_idx++;
- (q0)->rx_packets = (q0)->rx_bytes = 0;
- (q0)->rx_packets_with_error = (q0)->rxbuf_alloc_failed = 0;
-
- /* Initialize RXQs */
- _rxq_qpt_init(q0, rxp, dpage_count, PAGE_SIZE,
- &dqpt_mem[i], &dsqpt_mem[i], &dpage_mem[dpage_idx]);
- q0->rcb->page_idx = dpage_idx;
- q0->rcb->page_count = dpage_count;
- dpage_idx += dpage_count;
-
- /* Call bnad to complete rcb setup */
- if (rx->rcb_setup_cbfn)
- rx->rcb_setup_cbfn(bnad, q0->rcb);
-
- if (q1) {
- q1->rx = rx;
- q1->rxp = rxp;
-
- q1->rcb = (struct bna_rcb *) rcb_mem[rcb_idx].kva;
- RXQ_RCB_INIT(q1, rxp, rx_cfg->q_depth, bna, 1,
- (void *)unmapq_mem[rcb_idx].kva);
- rcb_idx++;
- (q1)->buffer_size = (rx_cfg)->small_buff_size;
- (q1)->rx_packets = (q1)->rx_bytes = 0;
- (q1)->rx_packets_with_error =
- (q1)->rxbuf_alloc_failed = 0;
-
- _rxq_qpt_init(q1, rxp, hpage_count, PAGE_SIZE,
- &hqpt_mem[i], &hsqpt_mem[i],
- &hpage_mem[hpage_idx]);
- q1->rcb->page_idx = hpage_idx;
- q1->rcb->page_count = hpage_count;
- hpage_idx += hpage_count;
-
- /* Call bnad to complete rcb setup */
- if (rx->rcb_setup_cbfn)
- rx->rcb_setup_cbfn(bnad, q1->rcb);
- }
- /* Setup RXP::CQ */
- rxp->cq.ccb = (struct bna_ccb *) ccb_mem[i].kva;
- _rxp_cqpt_setup(rxp, page_count, PAGE_SIZE,
- &cqpt_mem[i], &cswqpt_mem[i], &cpage_mem[cpage_idx]);
- rxp->cq.ccb->page_idx = cpage_idx;
- rxp->cq.ccb->page_count = page_count;
- cpage_idx += page_count;
-
- rxp->cq.ccb->pkt_rate.small_pkt_cnt = 0;
- rxp->cq.ccb->pkt_rate.large_pkt_cnt = 0;
-
- rxp->cq.ccb->producer_index = 0;
- rxp->cq.ccb->q_depth = rx_cfg->q_depth +
- ((rx_cfg->rxp_type == BNA_RXP_SINGLE) ?
- 0 : rx_cfg->q_depth);
- rxp->cq.ccb->i_dbell = &rxp->cq.ib->door_bell;
- rxp->cq.ccb->rcb[0] = q0->rcb;
- if (q1)
- rxp->cq.ccb->rcb[1] = q1->rcb;
- rxp->cq.ccb->cq = &rxp->cq;
- rxp->cq.ccb->bnad = bna->bnad;
- rxp->cq.ccb->hw_producer_index =
- ((volatile u32 *)rxp->cq.ib->ib_seg_host_addr_kva +
- (rxp->cq.ib_seg_offset * BFI_IBIDX_SIZE));
- *(rxp->cq.ccb->hw_producer_index) = 0;
- rxp->cq.ccb->intr_type = intr_info->intr_type;
- rxp->cq.ccb->intr_vector = (intr_info->num == 1) ?
- intr_info->idl[0].vector :
- intr_info->idl[i].vector;
- rxp->cq.ccb->rx_coalescing_timeo =
- rxp->cq.ib->ib_config.coalescing_timeo;
- rxp->cq.ccb->id = i;
-
- /* Call bnad to complete CCB setup */
- if (rx->ccb_setup_cbfn)
- rx->ccb_setup_cbfn(bnad, rxp->cq.ccb);
-
- } /* for each rx-path */
-
- bna_rxf_init(&rx->rxf, rx, rx_cfg);
-
- bfa_fsm_set_state(rx, bna_rx_sm_stopped);
-
- return rx;
-}
-
-void
-bna_rx_destroy(struct bna_rx *rx)
-{
- struct bna_rx_mod *rx_mod = &rx->bna->rx_mod;
- struct bna_ib_mod *ib_mod = &rx->bna->ib_mod;
- struct bna_rxq *q0 = NULL;
- struct bna_rxq *q1 = NULL;
- struct bna_rxp *rxp;
- struct list_head *qe;
-
- bna_rxf_uninit(&rx->rxf);
-
- while (!list_empty(&rx->rxp_q)) {
- bfa_q_deq(&rx->rxp_q, &rxp);
- GET_RXQS(rxp, q0, q1);
- /* Callback to bnad for destroying RCB */
- if (rx->rcb_destroy_cbfn)
- rx->rcb_destroy_cbfn(rx->bna->bnad, q0->rcb);
- q0->rcb = NULL;
- q0->rxp = NULL;
- q0->rx = NULL;
- _put_free_rxq(rx_mod, q0);
- if (q1) {
- /* Callback to bnad for destroying RCB */
- if (rx->rcb_destroy_cbfn)
- rx->rcb_destroy_cbfn(rx->bna->bnad, q1->rcb);
- q1->rcb = NULL;
- q1->rxp = NULL;
- q1->rx = NULL;
- _put_free_rxq(rx_mod, q1);
- }
- rxp->rxq.slr.large = NULL;
- rxp->rxq.slr.small = NULL;
- if (rxp->cq.ib) {
- if (rxp->cq.ib_seg_offset != 0xff)
- bna_ib_release_idx(rxp->cq.ib,
- rxp->cq.ib_seg_offset);
- bna_ib_put(ib_mod, rxp->cq.ib);
- rxp->cq.ib = NULL;
- }
- /* Callback to bnad for destroying CCB */
- if (rx->ccb_destroy_cbfn)
- rx->ccb_destroy_cbfn(rx->bna->bnad, rxp->cq.ccb);
- rxp->cq.ccb = NULL;
- rxp->rx = NULL;
- _put_free_rxp(rx_mod, rxp);
- }
-
- list_for_each(qe, &rx_mod->rx_active_q) {
- if (qe == &rx->qe) {
- list_del(&rx->qe);
- bfa_q_qe_init(&rx->qe);
- break;
- }
- }
-
- rx->bna = NULL;
- rx->priv = NULL;
- _put_free_rx(rx_mod, rx);
-}
-
-void
-bna_rx_enable(struct bna_rx *rx)
-{
- if (rx->fsm != (bfa_sm_t)bna_rx_sm_stopped)
- return;
-
- rx->rx_flags |= BNA_RX_F_ENABLE;
- if (rx->rx_flags & BNA_RX_F_PORT_ENABLED)
- bfa_fsm_send_event(rx, RX_E_START);
-}
-
-void
-bna_rx_disable(struct bna_rx *rx, enum bna_cleanup_type type,
- void (*cbfn)(void *, struct bna_rx *,
- enum bna_cb_status))
-{
- if (type == BNA_SOFT_CLEANUP) {
- /* h/w should not be accessed. Treat we're stopped */
- (*cbfn)(rx->bna->bnad, rx, BNA_CB_SUCCESS);
- } else {
- rx->stop_cbfn = cbfn;
- rx->stop_cbarg = rx->bna->bnad;
-
- rx->rx_flags &= ~BNA_RX_F_ENABLE;
-
- bfa_fsm_send_event(rx, RX_E_STOP);
- }
-}
-
-/**
- * TX
- */
-#define call_tx_stop_cbfn(tx, status)\
-do {\
- if ((tx)->stop_cbfn)\
- (tx)->stop_cbfn((tx)->stop_cbarg, (tx), status);\
- (tx)->stop_cbfn = NULL;\
- (tx)->stop_cbarg = NULL;\
-} while (0)
-
-#define call_tx_prio_change_cbfn(tx, status)\
-do {\
- if ((tx)->prio_change_cbfn)\
- (tx)->prio_change_cbfn((tx)->bna->bnad, (tx), status);\
- (tx)->prio_change_cbfn = NULL;\
-} while (0)
-
-static void bna_tx_mod_cb_tx_stopped(void *tx_mod, struct bna_tx *tx,
- enum bna_cb_status status);
-static void bna_tx_cb_txq_stopped(void *arg, int status);
-static void bna_tx_cb_stats_cleared(void *arg, int status);
-static void __bna_tx_stop(struct bna_tx *tx);
-static void __bna_tx_start(struct bna_tx *tx);
-static void __bna_txf_stat_clr(struct bna_tx *tx);
-
-enum bna_tx_event {
- TX_E_START = 1,
- TX_E_STOP = 2,
- TX_E_FAIL = 3,
- TX_E_TXQ_STOPPED = 4,
- TX_E_PRIO_CHANGE = 5,
- TX_E_STAT_CLEARED = 6,
-};
-
-enum bna_tx_state {
- BNA_TX_STOPPED = 1,
- BNA_TX_STARTED = 2,
- BNA_TX_TXQ_STOP_WAIT = 3,
- BNA_TX_PRIO_STOP_WAIT = 4,
- BNA_TX_STAT_CLR_WAIT = 5,
-};
-
-bfa_fsm_state_decl(bna_tx, stopped, struct bna_tx,
- enum bna_tx_event);
-bfa_fsm_state_decl(bna_tx, started, struct bna_tx,
- enum bna_tx_event);
-bfa_fsm_state_decl(bna_tx, txq_stop_wait, struct bna_tx,
- enum bna_tx_event);
-bfa_fsm_state_decl(bna_tx, prio_stop_wait, struct bna_tx,
- enum bna_tx_event);
-bfa_fsm_state_decl(bna_tx, stat_clr_wait, struct bna_tx,
- enum bna_tx_event);
-
-static struct bfa_sm_table tx_sm_table[] = {
- {BFA_SM(bna_tx_sm_stopped), BNA_TX_STOPPED},
- {BFA_SM(bna_tx_sm_started), BNA_TX_STARTED},
- {BFA_SM(bna_tx_sm_txq_stop_wait), BNA_TX_TXQ_STOP_WAIT},
- {BFA_SM(bna_tx_sm_prio_stop_wait), BNA_TX_PRIO_STOP_WAIT},
- {BFA_SM(bna_tx_sm_stat_clr_wait), BNA_TX_STAT_CLR_WAIT},
-};
-
-static void
-bna_tx_sm_stopped_entry(struct bna_tx *tx)
-{
- struct bna_txq *txq;
- struct list_head *qe;
-
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- (tx->tx_cleanup_cbfn)(tx->bna->bnad, txq->tcb);
- }
-
- call_tx_stop_cbfn(tx, BNA_CB_SUCCESS);
-}
-
-static void
-bna_tx_sm_stopped(struct bna_tx *tx, enum bna_tx_event event)
-{
- switch (event) {
- case TX_E_START:
- bfa_fsm_set_state(tx, bna_tx_sm_started);
- break;
-
- case TX_E_STOP:
- bfa_fsm_set_state(tx, bna_tx_sm_stopped);
- break;
-
- case TX_E_FAIL:
- /* No-op */
- break;
-
- case TX_E_PRIO_CHANGE:
- call_tx_prio_change_cbfn(tx, BNA_CB_SUCCESS);
- break;
-
- case TX_E_TXQ_STOPPED:
- /**
- * This event is received due to flushing of mbox when
- * device fails
- */
- /* No-op */
- break;
-
- default:
- bfa_sm_fault(tx->bna, event);
- }
-}
-
-static void
-bna_tx_sm_started_entry(struct bna_tx *tx)
-{
- struct bna_txq *txq;
- struct list_head *qe;
-
- __bna_tx_start(tx);
-
- /* Start IB */
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- bna_ib_ack(&txq->ib->door_bell, 0);
- }
-}
-
-static void
-bna_tx_sm_started(struct bna_tx *tx, enum bna_tx_event event)
-{
- struct bna_txq *txq;
- struct list_head *qe;
-
- switch (event) {
- case TX_E_STOP:
- bfa_fsm_set_state(tx, bna_tx_sm_txq_stop_wait);
- __bna_tx_stop(tx);
- break;
-
- case TX_E_FAIL:
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- bna_ib_fail(txq->ib);
- (tx->tx_stall_cbfn)(tx->bna->bnad, txq->tcb);
- }
- bfa_fsm_set_state(tx, bna_tx_sm_stopped);
- break;
-
- case TX_E_PRIO_CHANGE:
- bfa_fsm_set_state(tx, bna_tx_sm_prio_stop_wait);
- break;
-
- default:
- bfa_sm_fault(tx->bna, event);
- }
-}
-
-static void
-bna_tx_sm_txq_stop_wait_entry(struct bna_tx *tx)
-{
-}
-
-static void
-bna_tx_sm_txq_stop_wait(struct bna_tx *tx, enum bna_tx_event event)
-{
- struct bna_txq *txq;
- struct list_head *qe;
-
- switch (event) {
- case TX_E_FAIL:
- bfa_fsm_set_state(tx, bna_tx_sm_stopped);
- break;
-
- case TX_E_TXQ_STOPPED:
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- bna_ib_stop(txq->ib);
- }
- bfa_fsm_set_state(tx, bna_tx_sm_stat_clr_wait);
- break;
-
- case TX_E_PRIO_CHANGE:
- /* No-op */
- break;
-
- default:
- bfa_sm_fault(tx->bna, event);
- }
-}
-
-static void
-bna_tx_sm_prio_stop_wait_entry(struct bna_tx *tx)
-{
- __bna_tx_stop(tx);
-}
-
-static void
-bna_tx_sm_prio_stop_wait(struct bna_tx *tx, enum bna_tx_event event)
-{
- struct bna_txq *txq;
- struct list_head *qe;
-
- switch (event) {
- case TX_E_STOP:
- bfa_fsm_set_state(tx, bna_tx_sm_txq_stop_wait);
- break;
-
- case TX_E_FAIL:
- call_tx_prio_change_cbfn(tx, BNA_CB_FAIL);
- bfa_fsm_set_state(tx, bna_tx_sm_stopped);
- break;
-
- case TX_E_TXQ_STOPPED:
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- bna_ib_stop(txq->ib);
- (tx->tx_cleanup_cbfn)(tx->bna->bnad, txq->tcb);
- }
- call_tx_prio_change_cbfn(tx, BNA_CB_SUCCESS);
- bfa_fsm_set_state(tx, bna_tx_sm_started);
- break;
-
- case TX_E_PRIO_CHANGE:
- /* No-op */
- break;
-
- default:
- bfa_sm_fault(tx->bna, event);
- }
-}
-
-static void
-bna_tx_sm_stat_clr_wait_entry(struct bna_tx *tx)
-{
- __bna_txf_stat_clr(tx);
-}
-
-static void
-bna_tx_sm_stat_clr_wait(struct bna_tx *tx, enum bna_tx_event event)
-{
- switch (event) {
- case TX_E_FAIL:
- case TX_E_STAT_CLEARED:
- bfa_fsm_set_state(tx, bna_tx_sm_stopped);
- break;
-
- default:
- bfa_sm_fault(tx->bna, event);
- }
-}
-
-static void
-__bna_txq_start(struct bna_tx *tx, struct bna_txq *txq)
-{
- struct bna_rxtx_q_mem *q_mem;
- struct bna_txq_mem txq_cfg;
- struct bna_txq_mem *txq_mem;
- struct bna_dma_addr cur_q_addr;
- u32 pg_num;
- void __iomem *base_addr;
- unsigned long off;
-
- /* Fill out structure, to be subsequently written to hardware */
- txq_cfg.pg_tbl_addr_lo = txq->qpt.hw_qpt_ptr.lsb;
- txq_cfg.pg_tbl_addr_hi = txq->qpt.hw_qpt_ptr.msb;
- cur_q_addr = *((struct bna_dma_addr *)(txq->qpt.kv_qpt_ptr));
- txq_cfg.cur_q_entry_lo = cur_q_addr.lsb;
- txq_cfg.cur_q_entry_hi = cur_q_addr.msb;
-
- txq_cfg.pg_cnt_n_prd_ptr = (txq->qpt.page_count << 16) | 0x0;
-
- txq_cfg.entry_n_pg_size = ((u32)(BFI_TXQ_WI_SIZE >> 2) << 16) |
- (txq->qpt.page_size >> 2);
- txq_cfg.int_blk_n_cns_ptr = ((((u32)txq->ib_seg_offset) << 24) |
- ((u32)(txq->ib->ib_id & 0xff) << 16) | 0x0);
-
- txq_cfg.cns_ptr2_n_q_state = BNA_Q_IDLE_STATE;
- txq_cfg.nxt_qid_n_fid_n_pri = (((tx->txf.txf_id & 0x3f) << 3) |
- (txq->priority & 0x7));
- txq_cfg.wvc_n_cquota_n_rquota =
- ((((u32)BFI_TX_MAX_WRR_QUOTA & 0xfff) << 12) |
- (BFI_TX_MAX_WRR_QUOTA & 0xfff));
-
- /* Setup the page and write to H/W */
-
- pg_num = BNA_GET_PAGE_NUM(HQM0_BLK_PG_NUM + tx->bna->port_num,
- HQM_RXTX_Q_RAM_BASE_OFFSET);
- writel(pg_num, tx->bna->regs.page_addr);
-
- base_addr = BNA_GET_MEM_BASE_ADDR(tx->bna->pcidev.pci_bar_kva,
- HQM_RXTX_Q_RAM_BASE_OFFSET);
- q_mem = (struct bna_rxtx_q_mem *)0;
- txq_mem = &q_mem[txq->txq_id].txq;
-
- /*
- * The following 4 lines, is a hack b'cos the H/W needs to read
- * these DMA addresses as little endian
- */
-
- off = (unsigned long)&txq_mem->pg_tbl_addr_lo;
- writel(htonl(txq_cfg.pg_tbl_addr_lo), base_addr + off);
-
- off = (unsigned long)&txq_mem->pg_tbl_addr_hi;
- writel(htonl(txq_cfg.pg_tbl_addr_hi), base_addr + off);
-
- off = (unsigned long)&txq_mem->cur_q_entry_lo;
- writel(htonl(txq_cfg.cur_q_entry_lo), base_addr + off);
-
- off = (unsigned long)&txq_mem->cur_q_entry_hi;
- writel(htonl(txq_cfg.cur_q_entry_hi), base_addr + off);
-
- off = (unsigned long)&txq_mem->pg_cnt_n_prd_ptr;
- writel(txq_cfg.pg_cnt_n_prd_ptr, base_addr + off);
-
- off = (unsigned long)&txq_mem->entry_n_pg_size;
- writel(txq_cfg.entry_n_pg_size, base_addr + off);
-
- off = (unsigned long)&txq_mem->int_blk_n_cns_ptr;
- writel(txq_cfg.int_blk_n_cns_ptr, base_addr + off);
-
- off = (unsigned long)&txq_mem->cns_ptr2_n_q_state;
- writel(txq_cfg.cns_ptr2_n_q_state, base_addr + off);
-
- off = (unsigned long)&txq_mem->nxt_qid_n_fid_n_pri;
- writel(txq_cfg.nxt_qid_n_fid_n_pri, base_addr + off);
-
- off = (unsigned long)&txq_mem->wvc_n_cquota_n_rquota;
- writel(txq_cfg.wvc_n_cquota_n_rquota, base_addr + off);
-
- txq->tcb->producer_index = 0;
- txq->tcb->consumer_index = 0;
- *(txq->tcb->hw_consumer_index) = 0;
-
-}
-
-static void
-__bna_txq_stop(struct bna_tx *tx, struct bna_txq *txq)
-{
- struct bfi_ll_q_stop_req ll_req;
- u32 bit_mask[2] = {0, 0};
- if (txq->txq_id < 32)
- bit_mask[0] = (u32)1 << txq->txq_id;
- else
- bit_mask[1] = (u32)1 << (txq->txq_id - 32);
-
- memset(&ll_req, 0, sizeof(ll_req));
- ll_req.mh.msg_class = BFI_MC_LL;
- ll_req.mh.msg_id = BFI_LL_H2I_TXQ_STOP_REQ;
- ll_req.mh.mtag.h2i.lpu_id = 0;
- ll_req.q_id_mask[0] = htonl(bit_mask[0]);
- ll_req.q_id_mask[1] = htonl(bit_mask[1]);
-
- bna_mbox_qe_fill(&tx->mbox_qe, &ll_req, sizeof(ll_req),
- bna_tx_cb_txq_stopped, tx);
-
- bna_mbox_send(tx->bna, &tx->mbox_qe);
-}
-
-static void
-__bna_txf_start(struct bna_tx *tx)
-{
- struct bna_tx_fndb_ram *tx_fndb;
- struct bna_txf *txf = &tx->txf;
- void __iomem *base_addr;
- unsigned long off;
-
- writel(BNA_GET_PAGE_NUM(LUT0_MEM_BLK_BASE_PG_NUM +
- (tx->bna->port_num * 2), TX_FNDB_RAM_BASE_OFFSET),
- tx->bna->regs.page_addr);
-
- base_addr = BNA_GET_MEM_BASE_ADDR(tx->bna->pcidev.pci_bar_kva,
- TX_FNDB_RAM_BASE_OFFSET);
-
- tx_fndb = (struct bna_tx_fndb_ram *)0;
- off = (unsigned long)&tx_fndb[txf->txf_id].vlan_n_ctrl_flags;
-
- writel(((u32)txf->vlan << 16) | txf->ctrl_flags,
- base_addr + off);
-
- if (tx->txf.txf_id < 32)
- tx->bna->tx_mod.txf_bmap[0] |= ((u32)1 << tx->txf.txf_id);
- else
- tx->bna->tx_mod.txf_bmap[1] |= ((u32)
- 1 << (tx->txf.txf_id - 32));
-}
-
-static void
-__bna_txf_stop(struct bna_tx *tx)
-{
- struct bna_tx_fndb_ram *tx_fndb;
- u32 page_num;
- u32 ctl_flags;
- struct bna_txf *txf = &tx->txf;
- void __iomem *base_addr;
- unsigned long off;
-
- /* retrieve the running txf_flags & turn off enable bit */
- page_num = BNA_GET_PAGE_NUM(LUT0_MEM_BLK_BASE_PG_NUM +
- (tx->bna->port_num * 2), TX_FNDB_RAM_BASE_OFFSET);
- writel(page_num, tx->bna->regs.page_addr);
-
- base_addr = BNA_GET_MEM_BASE_ADDR(tx->bna->pcidev.pci_bar_kva,
- TX_FNDB_RAM_BASE_OFFSET);
- tx_fndb = (struct bna_tx_fndb_ram *)0;
- off = (unsigned long)&tx_fndb[txf->txf_id].vlan_n_ctrl_flags;
-
- ctl_flags = readl(base_addr + off);
- ctl_flags &= ~BFI_TXF_CF_ENABLE;
-
- writel(ctl_flags, base_addr + off);
-
- if (tx->txf.txf_id < 32)
- tx->bna->tx_mod.txf_bmap[0] &= ~((u32)1 << tx->txf.txf_id);
- else
- tx->bna->tx_mod.txf_bmap[0] &= ~((u32)
- 1 << (tx->txf.txf_id - 32));
-}
-
-static void
-__bna_txf_stat_clr(struct bna_tx *tx)
-{
- struct bfi_ll_stats_req ll_req;
- u32 txf_bmap[2] = {0, 0};
- if (tx->txf.txf_id < 32)
- txf_bmap[0] = ((u32)1 << tx->txf.txf_id);
- else
- txf_bmap[1] = ((u32)1 << (tx->txf.txf_id - 32));
- bfi_h2i_set(ll_req.mh, BFI_MC_LL, BFI_LL_H2I_STATS_CLEAR_REQ, 0);
- ll_req.stats_mask = 0;
- ll_req.rxf_id_mask[0] = 0;
- ll_req.rxf_id_mask[1] = 0;
- ll_req.txf_id_mask[0] = htonl(txf_bmap[0]);
- ll_req.txf_id_mask[1] = htonl(txf_bmap[1]);
-
- bna_mbox_qe_fill(&tx->mbox_qe, &ll_req, sizeof(ll_req),
- bna_tx_cb_stats_cleared, tx);
- bna_mbox_send(tx->bna, &tx->mbox_qe);
-}
-
-static void
-__bna_tx_start(struct bna_tx *tx)
-{
- struct bna_txq *txq;
- struct list_head *qe;
-
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- bna_ib_start(txq->ib);
- __bna_txq_start(tx, txq);
- }
-
- __bna_txf_start(tx);
-
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- txq->tcb->priority = txq->priority;
- (tx->tx_resume_cbfn)(tx->bna->bnad, txq->tcb);
- }
-}
-
-static void
-__bna_tx_stop(struct bna_tx *tx)
-{
- struct bna_txq *txq;
- struct list_head *qe;
-
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- (tx->tx_stall_cbfn)(tx->bna->bnad, txq->tcb);
- }
-
- __bna_txf_stop(tx);
-
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- bfa_wc_up(&tx->txq_stop_wc);
- }
-
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- __bna_txq_stop(tx, txq);
- }
-}
-
-static void
-bna_txq_qpt_setup(struct bna_txq *txq, int page_count, int page_size,
- struct bna_mem_descr *qpt_mem,
- struct bna_mem_descr *swqpt_mem,
- struct bna_mem_descr *page_mem)
-{
- int i;
-
- txq->qpt.hw_qpt_ptr.lsb = qpt_mem->dma.lsb;
- txq->qpt.hw_qpt_ptr.msb = qpt_mem->dma.msb;
- txq->qpt.kv_qpt_ptr = qpt_mem->kva;
- txq->qpt.page_count = page_count;
- txq->qpt.page_size = page_size;
-
- txq->tcb->sw_qpt = (void **) swqpt_mem->kva;
-
- for (i = 0; i < page_count; i++) {
- txq->tcb->sw_qpt[i] = page_mem[i].kva;
-
- ((struct bna_dma_addr *)txq->qpt.kv_qpt_ptr)[i].lsb =
- page_mem[i].dma.lsb;
- ((struct bna_dma_addr *)txq->qpt.kv_qpt_ptr)[i].msb =
- page_mem[i].dma.msb;
-
- }
-}
-
-static void
-bna_tx_free(struct bna_tx *tx)
-{
- struct bna_tx_mod *tx_mod = &tx->bna->tx_mod;
- struct bna_txq *txq;
- struct bna_ib_mod *ib_mod = &tx->bna->ib_mod;
- struct list_head *qe;
-
- while (!list_empty(&tx->txq_q)) {
- bfa_q_deq(&tx->txq_q, &txq);
- bfa_q_qe_init(&txq->qe);
- if (txq->ib) {
- if (txq->ib_seg_offset != -1)
- bna_ib_release_idx(txq->ib,
- txq->ib_seg_offset);
- bna_ib_put(ib_mod, txq->ib);
- txq->ib = NULL;
- }
- txq->tcb = NULL;
- txq->tx = NULL;
- list_add_tail(&txq->qe, &tx_mod->txq_free_q);
- }
-
- list_for_each(qe, &tx_mod->tx_active_q) {
- if (qe == &tx->qe) {
- list_del(&tx->qe);
- bfa_q_qe_init(&tx->qe);
- break;
- }
- }
-
- tx->bna = NULL;
- tx->priv = NULL;
- list_add_tail(&tx->qe, &tx_mod->tx_free_q);
-}
-
-static void
-bna_tx_cb_txq_stopped(void *arg, int status)
-{
- struct bna_tx *tx = (struct bna_tx *)arg;
-
- bfa_q_qe_init(&tx->mbox_qe.qe);
- bfa_wc_down(&tx->txq_stop_wc);
-}
-
-static void
-bna_tx_cb_txq_stopped_all(void *arg)
-{
- struct bna_tx *tx = (struct bna_tx *)arg;
-
- bfa_fsm_send_event(tx, TX_E_TXQ_STOPPED);
-}
-
-static void
-bna_tx_cb_stats_cleared(void *arg, int status)
-{
- struct bna_tx *tx = (struct bna_tx *)arg;
-
- bfa_q_qe_init(&tx->mbox_qe.qe);
-
- bfa_fsm_send_event(tx, TX_E_STAT_CLEARED);
-}
-
-static void
-bna_tx_start(struct bna_tx *tx)
-{
- tx->flags |= BNA_TX_F_PORT_STARTED;
- if (tx->flags & BNA_TX_F_ENABLED)
- bfa_fsm_send_event(tx, TX_E_START);
-}
-
-static void
-bna_tx_stop(struct bna_tx *tx)
-{
- tx->stop_cbfn = bna_tx_mod_cb_tx_stopped;
- tx->stop_cbarg = &tx->bna->tx_mod;
-
- tx->flags &= ~BNA_TX_F_PORT_STARTED;
- bfa_fsm_send_event(tx, TX_E_STOP);
-}
-
-static void
-bna_tx_fail(struct bna_tx *tx)
-{
- tx->flags &= ~BNA_TX_F_PORT_STARTED;
- bfa_fsm_send_event(tx, TX_E_FAIL);
-}
-
-static void
-bna_tx_prio_changed(struct bna_tx *tx, int prio)
-{
- struct bna_txq *txq;
- struct list_head *qe;
-
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- txq->priority = prio;
- }
-
- bfa_fsm_send_event(tx, TX_E_PRIO_CHANGE);
-}
-
-static void
-bna_tx_cee_link_status(struct bna_tx *tx, int cee_link)
-{
- if (cee_link)
- tx->flags |= BNA_TX_F_PRIO_LOCK;
- else
- tx->flags &= ~BNA_TX_F_PRIO_LOCK;
-}
-
-static void
-bna_tx_mod_cb_tx_stopped(void *arg, struct bna_tx *tx,
- enum bna_cb_status status)
-{
- struct bna_tx_mod *tx_mod = (struct bna_tx_mod *)arg;
-
- bfa_wc_down(&tx_mod->tx_stop_wc);
-}
-
-static void
-bna_tx_mod_cb_tx_stopped_all(void *arg)
-{
- struct bna_tx_mod *tx_mod = (struct bna_tx_mod *)arg;
-
- if (tx_mod->stop_cbfn)
- tx_mod->stop_cbfn(&tx_mod->bna->port, BNA_CB_SUCCESS);
- tx_mod->stop_cbfn = NULL;
-}
-
-void
-bna_tx_res_req(int num_txq, int txq_depth, struct bna_res_info *res_info)
-{
- u32 q_size;
- u32 page_count;
- struct bna_mem_info *mem_info;
-
- res_info[BNA_TX_RES_MEM_T_TCB].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_TX_RES_MEM_T_TCB].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_KVA;
- mem_info->len = sizeof(struct bna_tcb);
- mem_info->num = num_txq;
-
- q_size = txq_depth * BFI_TXQ_WI_SIZE;
- q_size = ALIGN(q_size, PAGE_SIZE);
- page_count = q_size >> PAGE_SHIFT;
-
- res_info[BNA_TX_RES_MEM_T_QPT].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_TX_RES_MEM_T_QPT].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_DMA;
- mem_info->len = page_count * sizeof(struct bna_dma_addr);
- mem_info->num = num_txq;
-
- res_info[BNA_TX_RES_MEM_T_SWQPT].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_TX_RES_MEM_T_SWQPT].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_KVA;
- mem_info->len = page_count * sizeof(void *);
- mem_info->num = num_txq;
-
- res_info[BNA_TX_RES_MEM_T_PAGE].res_type = BNA_RES_T_MEM;
- mem_info = &res_info[BNA_TX_RES_MEM_T_PAGE].res_u.mem_info;
- mem_info->mem_type = BNA_MEM_T_DMA;
- mem_info->len = PAGE_SIZE;
- mem_info->num = num_txq * page_count;
-
- res_info[BNA_TX_RES_INTR_T_TXCMPL].res_type = BNA_RES_T_INTR;
- res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info.intr_type =
- BNA_INTR_T_MSIX;
- res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info.num = num_txq;
-}
-
-struct bna_tx *
-bna_tx_create(struct bna *bna, struct bnad *bnad,
- struct bna_tx_config *tx_cfg,
- struct bna_tx_event_cbfn *tx_cbfn,
- struct bna_res_info *res_info, void *priv)
-{
- struct bna_intr_info *intr_info;
- struct bna_tx_mod *tx_mod = &bna->tx_mod;
- struct bna_tx *tx;
- struct bna_txq *txq;
- struct list_head *qe;
- struct bna_ib_mod *ib_mod = &bna->ib_mod;
- struct bna_doorbell_qset *qset;
- struct bna_ib_config ib_config;
- int page_count;
- int page_size;
- int page_idx;
- int i;
- unsigned long off;
-
- intr_info = &res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info;
- page_count = (res_info[BNA_TX_RES_MEM_T_PAGE].res_u.mem_info.num) /
- tx_cfg->num_txq;
- page_size = res_info[BNA_TX_RES_MEM_T_PAGE].res_u.mem_info.len;
-
- /**
- * Get resources
- */
-
- if ((intr_info->num != 1) && (intr_info->num != tx_cfg->num_txq))
- return NULL;
-
- /* Tx */
-
- if (list_empty(&tx_mod->tx_free_q))
- return NULL;
- bfa_q_deq(&tx_mod->tx_free_q, &tx);
- bfa_q_qe_init(&tx->qe);
-
- /* TxQs */
-
- INIT_LIST_HEAD(&tx->txq_q);
- for (i = 0; i < tx_cfg->num_txq; i++) {
- if (list_empty(&tx_mod->txq_free_q))
- goto err_return;
-
- bfa_q_deq(&tx_mod->txq_free_q, &txq);
- bfa_q_qe_init(&txq->qe);
- list_add_tail(&txq->qe, &tx->txq_q);
- txq->ib = NULL;
- txq->ib_seg_offset = -1;
- txq->tx = tx;
- }
-
- /* IBs */
- i = 0;
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
-
- if (intr_info->num == 1)
- txq->ib = bna_ib_get(ib_mod, intr_info->intr_type,
- intr_info->idl[0].vector);
- else
- txq->ib = bna_ib_get(ib_mod, intr_info->intr_type,
- intr_info->idl[i].vector);
-
- if (txq->ib == NULL)
- goto err_return;
-
- txq->ib_seg_offset = bna_ib_reserve_idx(txq->ib);
- if (txq->ib_seg_offset == -1)
- goto err_return;
-
- i++;
- }
-
- /*
- * Initialize
- */
-
- /* Tx */
-
- tx->tcb_setup_cbfn = tx_cbfn->tcb_setup_cbfn;
- tx->tcb_destroy_cbfn = tx_cbfn->tcb_destroy_cbfn;
- /* Following callbacks are mandatory */
- tx->tx_stall_cbfn = tx_cbfn->tx_stall_cbfn;
- tx->tx_resume_cbfn = tx_cbfn->tx_resume_cbfn;
- tx->tx_cleanup_cbfn = tx_cbfn->tx_cleanup_cbfn;
-
- list_add_tail(&tx->qe, &tx_mod->tx_active_q);
- tx->bna = bna;
- tx->priv = priv;
- tx->txq_stop_wc.wc_resume = bna_tx_cb_txq_stopped_all;
- tx->txq_stop_wc.wc_cbarg = tx;
- tx->txq_stop_wc.wc_count = 0;
-
- tx->type = tx_cfg->tx_type;
-
- tx->flags = 0;
- if (tx->bna->tx_mod.flags & BNA_TX_MOD_F_PORT_STARTED) {
- switch (tx->type) {
- case BNA_TX_T_REGULAR:
- if (!(tx->bna->tx_mod.flags &
- BNA_TX_MOD_F_PORT_LOOPBACK))
- tx->flags |= BNA_TX_F_PORT_STARTED;
- break;
- case BNA_TX_T_LOOPBACK:
- if (tx->bna->tx_mod.flags & BNA_TX_MOD_F_PORT_LOOPBACK)
- tx->flags |= BNA_TX_F_PORT_STARTED;
- break;
- }
- }
- if (tx->bna->tx_mod.cee_link)
- tx->flags |= BNA_TX_F_PRIO_LOCK;
-
- /* TxQ */
-
- i = 0;
- page_idx = 0;
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- txq->priority = tx_mod->priority;
- txq->tcb = (struct bna_tcb *)
- res_info[BNA_TX_RES_MEM_T_TCB].res_u.mem_info.mdl[i].kva;
- txq->tx_packets = 0;
- txq->tx_bytes = 0;
-
- /* IB */
-
- ib_config.coalescing_timeo = BFI_TX_COALESCING_TIMEO;
- ib_config.interpkt_timeo = 0; /* Not used */
- ib_config.interpkt_count = BFI_TX_INTERPKT_COUNT;
- ib_config.ctrl_flags = (BFI_IB_CF_INTER_PKT_DMA |
- BFI_IB_CF_INT_ENABLE |
- BFI_IB_CF_COALESCING_MODE);
- bna_ib_config(txq->ib, &ib_config);
-
- /* TCB */
-
- txq->tcb->producer_index = 0;
- txq->tcb->consumer_index = 0;
- txq->tcb->hw_consumer_index = (volatile u32 *)
- ((volatile u8 *)txq->ib->ib_seg_host_addr_kva +
- (txq->ib_seg_offset * BFI_IBIDX_SIZE));
- *(txq->tcb->hw_consumer_index) = 0;
- txq->tcb->q_depth = tx_cfg->txq_depth;
- txq->tcb->unmap_q = (void *)
- res_info[BNA_TX_RES_MEM_T_UNMAPQ].res_u.mem_info.mdl[i].kva;
- qset = (struct bna_doorbell_qset *)0;
- off = (unsigned long)&qset[txq->txq_id].txq[0];
- txq->tcb->q_dbell = off +
- BNA_GET_DOORBELL_BASE_ADDR(bna->pcidev.pci_bar_kva);
- txq->tcb->i_dbell = &txq->ib->door_bell;
- txq->tcb->intr_type = intr_info->intr_type;
- txq->tcb->intr_vector = (intr_info->num == 1) ?
- intr_info->idl[0].vector :
- intr_info->idl[i].vector;
- txq->tcb->txq = txq;
- txq->tcb->bnad = bnad;
- txq->tcb->id = i;
-
- /* QPT, SWQPT, Pages */
- bna_txq_qpt_setup(txq, page_count, page_size,
- &res_info[BNA_TX_RES_MEM_T_QPT].res_u.mem_info.mdl[i],
- &res_info[BNA_TX_RES_MEM_T_SWQPT].res_u.mem_info.mdl[i],
- &res_info[BNA_TX_RES_MEM_T_PAGE].
- res_u.mem_info.mdl[page_idx]);
- txq->tcb->page_idx = page_idx;
- txq->tcb->page_count = page_count;
- page_idx += page_count;
-
- /* Callback to bnad for setting up TCB */
- if (tx->tcb_setup_cbfn)
- (tx->tcb_setup_cbfn)(bna->bnad, txq->tcb);
-
- i++;
- }
-
- /* TxF */
-
- tx->txf.ctrl_flags = BFI_TXF_CF_ENABLE | BFI_TXF_CF_VLAN_WI_BASED;
- tx->txf.vlan = 0;
-
- /* Mbox element */
- bfa_q_qe_init(&tx->mbox_qe.qe);
-
- bfa_fsm_set_state(tx, bna_tx_sm_stopped);
-
- return tx;
-
-err_return:
- bna_tx_free(tx);
- return NULL;
-}
-
-void
-bna_tx_destroy(struct bna_tx *tx)
-{
- /* Callback to bnad for destroying TCB */
- if (tx->tcb_destroy_cbfn) {
- struct bna_txq *txq;
- struct list_head *qe;
-
- list_for_each(qe, &tx->txq_q) {
- txq = (struct bna_txq *)qe;
- (tx->tcb_destroy_cbfn)(tx->bna->bnad, txq->tcb);
- }
- }
-
- bna_tx_free(tx);
-}
-
-void
-bna_tx_enable(struct bna_tx *tx)
-{
- if (tx->fsm != (bfa_sm_t)bna_tx_sm_stopped)
- return;
-
- tx->flags |= BNA_TX_F_ENABLED;
-
- if (tx->flags & BNA_TX_F_PORT_STARTED)
- bfa_fsm_send_event(tx, TX_E_START);
-}
-
-void
-bna_tx_disable(struct bna_tx *tx, enum bna_cleanup_type type,
- void (*cbfn)(void *, struct bna_tx *, enum bna_cb_status))
-{
- if (type == BNA_SOFT_CLEANUP) {
- (*cbfn)(tx->bna->bnad, tx, BNA_CB_SUCCESS);
- return;
- }
-
- tx->stop_cbfn = cbfn;
- tx->stop_cbarg = tx->bna->bnad;
-
- tx->flags &= ~BNA_TX_F_ENABLED;
-
- bfa_fsm_send_event(tx, TX_E_STOP);
-}
-
-int
-bna_tx_state_get(struct bna_tx *tx)
-{
- return bfa_sm_to_state(tx_sm_table, tx->fsm);
-}
-
-void
-bna_tx_mod_init(struct bna_tx_mod *tx_mod, struct bna *bna,
- struct bna_res_info *res_info)
-{
- int i;
-
- tx_mod->bna = bna;
- tx_mod->flags = 0;
-
- tx_mod->tx = (struct bna_tx *)
- res_info[BNA_RES_MEM_T_TX_ARRAY].res_u.mem_info.mdl[0].kva;
- tx_mod->txq = (struct bna_txq *)
- res_info[BNA_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.mdl[0].kva;
-
- INIT_LIST_HEAD(&tx_mod->tx_free_q);
- INIT_LIST_HEAD(&tx_mod->tx_active_q);
-
- INIT_LIST_HEAD(&tx_mod->txq_free_q);
-
- for (i = 0; i < BFI_MAX_TXQ; i++) {
- tx_mod->tx[i].txf.txf_id = i;
- bfa_q_qe_init(&tx_mod->tx[i].qe);
- list_add_tail(&tx_mod->tx[i].qe, &tx_mod->tx_free_q);
-
- tx_mod->txq[i].txq_id = i;
- bfa_q_qe_init(&tx_mod->txq[i].qe);
- list_add_tail(&tx_mod->txq[i].qe, &tx_mod->txq_free_q);
- }
-
- tx_mod->tx_stop_wc.wc_resume = bna_tx_mod_cb_tx_stopped_all;
- tx_mod->tx_stop_wc.wc_cbarg = tx_mod;
- tx_mod->tx_stop_wc.wc_count = 0;
-}
-
-void
-bna_tx_mod_uninit(struct bna_tx_mod *tx_mod)
-{
- struct list_head *qe;
- int i;
-
- i = 0;
- list_for_each(qe, &tx_mod->tx_free_q)
- i++;
-
- i = 0;
- list_for_each(qe, &tx_mod->txq_free_q)
- i++;
-
- tx_mod->bna = NULL;
-}
-
-void
-bna_tx_mod_start(struct bna_tx_mod *tx_mod, enum bna_tx_type type)
-{
- struct bna_tx *tx;
- struct list_head *qe;
-
- tx_mod->flags |= BNA_TX_MOD_F_PORT_STARTED;
- if (type == BNA_TX_T_LOOPBACK)
- tx_mod->flags |= BNA_TX_MOD_F_PORT_LOOPBACK;
-
- list_for_each(qe, &tx_mod->tx_active_q) {
- tx = (struct bna_tx *)qe;
- if (tx->type == type)
- bna_tx_start(tx);
- }
-}
-
-void
-bna_tx_mod_stop(struct bna_tx_mod *tx_mod, enum bna_tx_type type)
-{
- struct bna_tx *tx;
- struct list_head *qe;
-
- tx_mod->flags &= ~BNA_TX_MOD_F_PORT_STARTED;
- tx_mod->flags &= ~BNA_TX_MOD_F_PORT_LOOPBACK;
-
- tx_mod->stop_cbfn = bna_port_cb_tx_stopped;
-
- /**
- * Before calling bna_tx_stop(), increment tx_stop_wc as many times
- * as we are going to call bna_tx_stop
- */
- list_for_each(qe, &tx_mod->tx_active_q) {
- tx = (struct bna_tx *)qe;
- if (tx->type == type)
- bfa_wc_up(&tx_mod->tx_stop_wc);
- }
-
- if (tx_mod->tx_stop_wc.wc_count == 0) {
- tx_mod->stop_cbfn(&tx_mod->bna->port, BNA_CB_SUCCESS);
- tx_mod->stop_cbfn = NULL;
- return;
- }
-
- list_for_each(qe, &tx_mod->tx_active_q) {
- tx = (struct bna_tx *)qe;
- if (tx->type == type)
- bna_tx_stop(tx);
- }
-}
-
-void
-bna_tx_mod_fail(struct bna_tx_mod *tx_mod)
-{
- struct bna_tx *tx;
- struct list_head *qe;
-
- tx_mod->flags &= ~BNA_TX_MOD_F_PORT_STARTED;
- tx_mod->flags &= ~BNA_TX_MOD_F_PORT_LOOPBACK;
-
- list_for_each(qe, &tx_mod->tx_active_q) {
- tx = (struct bna_tx *)qe;
- bna_tx_fail(tx);
- }
-}
-
-void
-bna_tx_mod_prio_changed(struct bna_tx_mod *tx_mod, int prio)
-{
- struct bna_tx *tx;
- struct list_head *qe;
-
- if (prio != tx_mod->priority) {
- tx_mod->priority = prio;
-
- list_for_each(qe, &tx_mod->tx_active_q) {
- tx = (struct bna_tx *)qe;
- bna_tx_prio_changed(tx, prio);
- }
- }
-}
-
-void
-bna_tx_mod_cee_link_status(struct bna_tx_mod *tx_mod, int cee_link)
-{
- struct bna_tx *tx;
- struct list_head *qe;
-
- tx_mod->cee_link = cee_link;
-
- list_for_each(qe, &tx_mod->tx_active_q) {
- tx = (struct bna_tx *)qe;
- bna_tx_cee_link_status(tx, cee_link);
- }
-}
diff --git a/drivers/net/bnx2x/bnx2x_dump.h b/drivers/net/bnx2x/bnx2x_dump.h
deleted file mode 100644
index fb3ff7c4d7ca..000000000000
--- a/drivers/net/bnx2x/bnx2x_dump.h
+++ /dev/null
@@ -1,713 +0,0 @@
-/* bnx2x_dump.h: Broadcom Everest network driver.
- *
- * Copyright (c) 2011 Broadcom Corporation
- *
- * Unless you and Broadcom execute a separate written software license
- * agreement governing use of this software, this software is licensed to you
- * under the terms of the GNU General Public License version 2, available
- * at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html (the "GPL").
- *
- * Notwithstanding the above, under no circumstances may you combine this
- * software in any way with any other Broadcom software provided under a
- * license other than the GPL, without Broadcom's express prior written
- * consent.
- */
-
-
-/* This struct holds a signature to ensure the dump returned from the driver
- * match the meta data file inserted to grc_dump.tcl
- * The signature is time stamp, diag version and grc_dump version
- */
-
-#ifndef BNX2X_DUMP_H
-#define BNX2X_DUMP_H
-
-
-
-/*definitions */
-#define XSTORM_WAITP_ADDR 0x2b8a80
-#define TSTORM_WAITP_ADDR 0x1b8a80
-#define USTORM_WAITP_ADDR 0x338a80
-#define CSTORM_WAITP_ADDR 0x238a80
-#define TSTORM_CAM_MODE 0x1B1440
-
-#define MAX_TIMER_PENDING 200
-#define TIMER_SCAN_DONT_CARE 0xFF
-#define RI_E1 0x1
-#define RI_E1H 0x2
-#define RI_E2 0x4
-#define RI_ONLINE 0x100
-#define RI_PATH0_DUMP 0x200
-#define RI_PATH1_DUMP 0x400
-#define RI_E1_OFFLINE (RI_E1)
-#define RI_E1_ONLINE (RI_E1 | RI_ONLINE)
-#define RI_E1H_OFFLINE (RI_E1H)
-#define RI_E1H_ONLINE (RI_E1H | RI_ONLINE)
-#define RI_E2_OFFLINE (RI_E2)
-#define RI_E2_ONLINE (RI_E2 | RI_ONLINE)
-#define RI_E1E1H_OFFLINE (RI_E1 | RI_E1H)
-#define RI_E1E1H_ONLINE (RI_E1 | RI_E1H | RI_ONLINE)
-#define RI_E1HE2_OFFLINE (RI_E2 | RI_E1H)
-#define RI_E1HE2_ONLINE (RI_E2 | RI_E1H | RI_ONLINE)
-#define RI_E1E2_OFFLINE (RI_E2 | RI_E1)
-#define RI_E1E2_ONLINE (RI_E2 | RI_E1 | RI_ONLINE)
-#define RI_ALL_OFFLINE (RI_E1 | RI_E1H | RI_E2)
-#define RI_ALL_ONLINE (RI_E1 | RI_E1H | RI_E2 | RI_ONLINE)
-
-struct dump_sign {
- u32 time_stamp;
- u32 diag_ver;
- u32 grc_dump_ver;
-};
-
-struct dump_hdr {
- u32 hdr_size; /* in dwords, excluding this field */
- struct dump_sign dump_sign;
- u32 xstorm_waitp;
- u32 tstorm_waitp;
- u32 ustorm_waitp;
- u32 cstorm_waitp;
- u16 info;
- u8 idle_chk;
- u8 reserved;
-};
-
-struct reg_addr {
- u32 addr;
- u32 size;
- u16 info;
-};
-
-struct wreg_addr {
- u32 addr;
- u32 size;
- u32 read_regs_count;
- const u32 *read_regs;
- u16 info;
-};
-
-#define REGS_COUNT 834
-static const struct reg_addr reg_addrs[REGS_COUNT] = {
- { 0x2000, 341, RI_ALL_ONLINE }, { 0x2800, 103, RI_ALL_ONLINE },
- { 0x3000, 287, RI_ALL_ONLINE }, { 0x3800, 331, RI_ALL_ONLINE },
- { 0x8800, 6, RI_ALL_ONLINE }, { 0x8818, 1, RI_E1HE2_ONLINE },
- { 0x9000, 164, RI_E2_ONLINE }, { 0x9400, 33, RI_E2_ONLINE },
- { 0xa000, 27, RI_ALL_ONLINE }, { 0xa06c, 1, RI_E1E1H_ONLINE },
- { 0xa070, 71, RI_ALL_ONLINE }, { 0xa18c, 4, RI_E1E1H_ONLINE },
- { 0xa19c, 62, RI_ALL_ONLINE }, { 0xa294, 2, RI_E1E1H_ONLINE },
- { 0xa29c, 56, RI_ALL_ONLINE }, { 0xa39c, 7, RI_E1HE2_ONLINE },
- { 0xa3c0, 3, RI_E1HE2_ONLINE }, { 0xa3d0, 1, RI_E1HE2_ONLINE },
- { 0xa3d8, 1, RI_E1HE2_ONLINE }, { 0xa3e0, 1, RI_E1HE2_ONLINE },
- { 0xa3e8, 1, RI_E1HE2_ONLINE }, { 0xa3f0, 1, RI_E1HE2_ONLINE },
- { 0xa3f8, 1, RI_E1HE2_ONLINE }, { 0xa400, 43, RI_ALL_ONLINE },
- { 0xa4ac, 2, RI_E1E1H_ONLINE }, { 0xa4b4, 1, RI_ALL_ONLINE },
- { 0xa4b8, 2, RI_E1E1H_ONLINE }, { 0xa4c0, 3, RI_ALL_ONLINE },
- { 0xa4cc, 5, RI_E1E1H_ONLINE }, { 0xa4e0, 9, RI_ALL_ONLINE },
- { 0xa504, 1, RI_E1E1H_ONLINE }, { 0xa508, 3, RI_ALL_ONLINE },
- { 0xa518, 1, RI_ALL_ONLINE }, { 0xa520, 1, RI_ALL_ONLINE },
- { 0xa528, 1, RI_ALL_ONLINE }, { 0xa530, 1, RI_ALL_ONLINE },
- { 0xa538, 1, RI_ALL_ONLINE }, { 0xa540, 1, RI_ALL_ONLINE },
- { 0xa548, 1, RI_E1E1H_ONLINE }, { 0xa550, 1, RI_E1E1H_ONLINE },
- { 0xa558, 1, RI_E1E1H_ONLINE }, { 0xa560, 1, RI_E1E1H_ONLINE },
- { 0xa568, 1, RI_E1E1H_ONLINE }, { 0xa570, 1, RI_ALL_ONLINE },
- { 0xa580, 1, RI_ALL_ONLINE }, { 0xa590, 1, RI_ALL_ONLINE },
- { 0xa5a0, 1, RI_ALL_ONLINE }, { 0xa5c0, 1, RI_ALL_ONLINE },
- { 0xa5e0, 1, RI_E1HE2_ONLINE }, { 0xa5e8, 1, RI_E1HE2_ONLINE },
- { 0xa5f0, 1, RI_E1HE2_ONLINE }, { 0xa5f8, 10, RI_E1HE2_ONLINE },
- { 0xa620, 111, RI_E2_ONLINE }, { 0xa800, 51, RI_E2_ONLINE },
- { 0xa8d4, 4, RI_E2_ONLINE }, { 0xa8e8, 1, RI_E2_ONLINE },
- { 0xa8f0, 1, RI_E2_ONLINE }, { 0x10000, 236, RI_ALL_ONLINE },
- { 0x10400, 57, RI_ALL_ONLINE }, { 0x104e8, 2, RI_ALL_ONLINE },
- { 0x104f4, 2, RI_ALL_ONLINE }, { 0x10500, 146, RI_ALL_ONLINE },
- { 0x10750, 2, RI_ALL_ONLINE }, { 0x10760, 2, RI_ALL_ONLINE },
- { 0x10770, 2, RI_ALL_ONLINE }, { 0x10780, 2, RI_ALL_ONLINE },
- { 0x10790, 2, RI_ALL_ONLINE }, { 0x107a0, 2, RI_ALL_ONLINE },
- { 0x107b0, 2, RI_ALL_ONLINE }, { 0x107c0, 2, RI_ALL_ONLINE },
- { 0x107d0, 2, RI_ALL_ONLINE }, { 0x107e0, 2, RI_ALL_ONLINE },
- { 0x10880, 2, RI_ALL_ONLINE }, { 0x10900, 2, RI_ALL_ONLINE },
- { 0x16000, 26, RI_E1HE2_ONLINE }, { 0x16070, 18, RI_E1HE2_ONLINE },
- { 0x160c0, 27, RI_E1HE2_ONLINE }, { 0x16140, 1, RI_E1HE2_ONLINE },
- { 0x16160, 1, RI_E1HE2_ONLINE }, { 0x16180, 2, RI_E1HE2_ONLINE },
- { 0x161c0, 2, RI_E1HE2_ONLINE }, { 0x16204, 5, RI_E1HE2_ONLINE },
- { 0x18000, 1, RI_E1HE2_ONLINE }, { 0x18008, 1, RI_E1HE2_ONLINE },
- { 0x18010, 35, RI_E2_ONLINE }, { 0x180a4, 2, RI_E2_ONLINE },
- { 0x180c0, 191, RI_E2_ONLINE }, { 0x18440, 1, RI_E2_ONLINE },
- { 0x18460, 1, RI_E2_ONLINE }, { 0x18480, 2, RI_E2_ONLINE },
- { 0x184c0, 2, RI_E2_ONLINE }, { 0x18500, 15, RI_E2_ONLINE },
- { 0x20000, 24, RI_ALL_ONLINE }, { 0x20060, 8, RI_ALL_ONLINE },
- { 0x20080, 94, RI_ALL_ONLINE }, { 0x201f8, 1, RI_E1E1H_ONLINE },
- { 0x201fc, 1, RI_ALL_ONLINE }, { 0x20200, 1, RI_E1E1H_ONLINE },
- { 0x20204, 1, RI_ALL_ONLINE }, { 0x20208, 1, RI_E1E1H_ONLINE },
- { 0x2020c, 39, RI_ALL_ONLINE }, { 0x202c8, 1, RI_E2_ONLINE },
- { 0x202d8, 4, RI_E2_ONLINE }, { 0x20400, 2, RI_ALL_ONLINE },
- { 0x2040c, 8, RI_ALL_ONLINE }, { 0x2042c, 18, RI_E1HE2_ONLINE },
- { 0x20480, 1, RI_ALL_ONLINE }, { 0x20500, 1, RI_ALL_ONLINE },
- { 0x20600, 1, RI_ALL_ONLINE }, { 0x28000, 1, RI_ALL_ONLINE },
- { 0x28004, 8191, RI_ALL_OFFLINE }, { 0x30000, 1, RI_ALL_ONLINE },
- { 0x30004, 16383, RI_ALL_OFFLINE }, { 0x40000, 98, RI_ALL_ONLINE },
- { 0x401a8, 8, RI_E1HE2_ONLINE }, { 0x401c8, 1, RI_E1H_ONLINE },
- { 0x401cc, 2, RI_E1HE2_ONLINE }, { 0x401d4, 2, RI_E2_ONLINE },
- { 0x40200, 4, RI_ALL_ONLINE }, { 0x40220, 18, RI_E2_ONLINE },
- { 0x40400, 43, RI_ALL_ONLINE }, { 0x404cc, 3, RI_E1HE2_ONLINE },
- { 0x404e0, 1, RI_E2_ONLINE }, { 0x40500, 2, RI_ALL_ONLINE },
- { 0x40510, 2, RI_ALL_ONLINE }, { 0x40520, 2, RI_ALL_ONLINE },
- { 0x40530, 2, RI_ALL_ONLINE }, { 0x40540, 2, RI_ALL_ONLINE },
- { 0x40550, 10, RI_E2_ONLINE }, { 0x40610, 2, RI_E2_ONLINE },
- { 0x42000, 164, RI_ALL_ONLINE }, { 0x422c0, 4, RI_E2_ONLINE },
- { 0x422d4, 5, RI_E1HE2_ONLINE }, { 0x422e8, 1, RI_E2_ONLINE },
- { 0x42400, 49, RI_ALL_ONLINE }, { 0x424c8, 38, RI_ALL_ONLINE },
- { 0x42568, 2, RI_ALL_ONLINE }, { 0x42640, 5, RI_E2_ONLINE },
- { 0x42800, 1, RI_ALL_ONLINE }, { 0x50000, 1, RI_ALL_ONLINE },
- { 0x50004, 19, RI_ALL_ONLINE }, { 0x50050, 8, RI_ALL_ONLINE },
- { 0x50070, 88, RI_ALL_ONLINE }, { 0x501f0, 4, RI_E1HE2_ONLINE },
- { 0x50200, 2, RI_ALL_ONLINE }, { 0x5020c, 7, RI_ALL_ONLINE },
- { 0x50228, 6, RI_E1HE2_ONLINE }, { 0x50240, 1, RI_ALL_ONLINE },
- { 0x50280, 1, RI_ALL_ONLINE }, { 0x50300, 1, RI_E2_ONLINE },
- { 0x5030c, 1, RI_E2_ONLINE }, { 0x50318, 1, RI_E2_ONLINE },
- { 0x5031c, 1, RI_E2_ONLINE }, { 0x50320, 2, RI_E2_ONLINE },
- { 0x52000, 1, RI_ALL_ONLINE }, { 0x54000, 1, RI_ALL_ONLINE },
- { 0x54004, 3327, RI_ALL_OFFLINE }, { 0x58000, 1, RI_ALL_ONLINE },
- { 0x58004, 8191, RI_E1E1H_OFFLINE }, { 0x60000, 26, RI_ALL_ONLINE },
- { 0x60068, 8, RI_E1E1H_ONLINE }, { 0x60088, 12, RI_ALL_ONLINE },
- { 0x600b8, 9, RI_E1E1H_ONLINE }, { 0x600dc, 1, RI_ALL_ONLINE },
- { 0x600e0, 5, RI_E1E1H_ONLINE }, { 0x600f4, 1, RI_ALL_ONLINE },
- { 0x600f8, 1, RI_E1E1H_ONLINE }, { 0x600fc, 8, RI_ALL_ONLINE },
- { 0x6013c, 24, RI_E1H_ONLINE }, { 0x6019c, 2, RI_E2_ONLINE },
- { 0x601ac, 18, RI_E2_ONLINE }, { 0x60200, 1, RI_ALL_ONLINE },
- { 0x60204, 2, RI_ALL_OFFLINE }, { 0x60210, 13, RI_E2_ONLINE },
- { 0x61000, 1, RI_ALL_ONLINE }, { 0x61004, 511, RI_ALL_OFFLINE },
- { 0x70000, 8, RI_ALL_ONLINE }, { 0x70020, 8184, RI_ALL_OFFLINE },
- { 0x85000, 3, RI_ALL_ONLINE }, { 0x8501c, 7, RI_ALL_ONLINE },
- { 0x85048, 1, RI_ALL_ONLINE }, { 0x85200, 32, RI_ALL_ONLINE },
- { 0xc1000, 7, RI_ALL_ONLINE }, { 0xc103c, 2, RI_E2_ONLINE },
- { 0xc1800, 2, RI_ALL_ONLINE }, { 0xc2000, 164, RI_ALL_ONLINE },
- { 0xc22c0, 5, RI_E2_ONLINE }, { 0xc22d8, 4, RI_E2_ONLINE },
- { 0xc2400, 49, RI_ALL_ONLINE }, { 0xc24c8, 38, RI_ALL_ONLINE },
- { 0xc2568, 2, RI_ALL_ONLINE }, { 0xc2600, 1, RI_ALL_ONLINE },
- { 0xc4000, 165, RI_ALL_ONLINE }, { 0xc42d8, 2, RI_E2_ONLINE },
- { 0xc42e0, 7, RI_E1HE2_ONLINE }, { 0xc42fc, 1, RI_E2_ONLINE },
- { 0xc4400, 51, RI_ALL_ONLINE }, { 0xc44d0, 38, RI_ALL_ONLINE },
- { 0xc4570, 2, RI_ALL_ONLINE }, { 0xc4578, 5, RI_E2_ONLINE },
- { 0xc4600, 1, RI_ALL_ONLINE }, { 0xd0000, 19, RI_ALL_ONLINE },
- { 0xd004c, 8, RI_ALL_ONLINE }, { 0xd006c, 91, RI_ALL_ONLINE },
- { 0xd01fc, 1, RI_E2_ONLINE }, { 0xd0200, 2, RI_ALL_ONLINE },
- { 0xd020c, 7, RI_ALL_ONLINE }, { 0xd0228, 18, RI_E1HE2_ONLINE },
- { 0xd0280, 1, RI_ALL_ONLINE }, { 0xd0300, 1, RI_ALL_ONLINE },
- { 0xd0400, 1, RI_ALL_ONLINE }, { 0xd4000, 1, RI_ALL_ONLINE },
- { 0xd4004, 2559, RI_ALL_OFFLINE }, { 0xd8000, 1, RI_ALL_ONLINE },
- { 0xd8004, 8191, RI_ALL_OFFLINE }, { 0xe0000, 21, RI_ALL_ONLINE },
- { 0xe0054, 8, RI_ALL_ONLINE }, { 0xe0074, 49, RI_ALL_ONLINE },
- { 0xe0138, 1, RI_E1E1H_ONLINE }, { 0xe013c, 35, RI_ALL_ONLINE },
- { 0xe01f4, 2, RI_E2_ONLINE }, { 0xe0200, 2, RI_ALL_ONLINE },
- { 0xe020c, 8, RI_ALL_ONLINE }, { 0xe022c, 18, RI_E1HE2_ONLINE },
- { 0xe0280, 1, RI_ALL_ONLINE }, { 0xe0300, 1, RI_ALL_ONLINE },
- { 0xe1000, 1, RI_ALL_ONLINE }, { 0xe2000, 1, RI_ALL_ONLINE },
- { 0xe2004, 2047, RI_ALL_OFFLINE }, { 0xf0000, 1, RI_ALL_ONLINE },
- { 0xf0004, 16383, RI_ALL_OFFLINE }, { 0x101000, 12, RI_ALL_ONLINE },
- { 0x101050, 1, RI_E1HE2_ONLINE }, { 0x101054, 3, RI_E2_ONLINE },
- { 0x101100, 1, RI_ALL_ONLINE }, { 0x101800, 8, RI_ALL_ONLINE },
- { 0x102000, 18, RI_ALL_ONLINE }, { 0x102068, 6, RI_E2_ONLINE },
- { 0x102080, 17, RI_ALL_ONLINE }, { 0x1020c8, 8, RI_E1H_ONLINE },
- { 0x1020e8, 9, RI_E2_ONLINE }, { 0x102400, 1, RI_ALL_ONLINE },
- { 0x103000, 26, RI_ALL_ONLINE }, { 0x103098, 5, RI_E1HE2_ONLINE },
- { 0x1030ac, 10, RI_E2_ONLINE }, { 0x1030d8, 8, RI_E2_ONLINE },
- { 0x103400, 1, RI_E2_ONLINE }, { 0x103404, 135, RI_E2_OFFLINE },
- { 0x103800, 8, RI_ALL_ONLINE }, { 0x104000, 63, RI_ALL_ONLINE },
- { 0x10411c, 16, RI_E2_ONLINE }, { 0x104200, 17, RI_ALL_ONLINE },
- { 0x104400, 64, RI_ALL_ONLINE }, { 0x104500, 192, RI_ALL_OFFLINE },
- { 0x104800, 64, RI_ALL_ONLINE }, { 0x104900, 192, RI_ALL_OFFLINE },
- { 0x105000, 256, RI_ALL_ONLINE }, { 0x105400, 768, RI_ALL_OFFLINE },
- { 0x107000, 7, RI_E2_ONLINE }, { 0x108000, 33, RI_E1E1H_ONLINE },
- { 0x1080ac, 5, RI_E1H_ONLINE }, { 0x108100, 5, RI_E1E1H_ONLINE },
- { 0x108120, 5, RI_E1E1H_ONLINE }, { 0x108200, 74, RI_E1E1H_ONLINE },
- { 0x108400, 74, RI_E1E1H_ONLINE }, { 0x108800, 152, RI_E1E1H_ONLINE },
- { 0x110000, 111, RI_E2_ONLINE }, { 0x110200, 4, RI_E2_ONLINE },
- { 0x120000, 2, RI_ALL_ONLINE }, { 0x120008, 4, RI_ALL_ONLINE },
- { 0x120018, 3, RI_ALL_ONLINE }, { 0x120024, 4, RI_ALL_ONLINE },
- { 0x120034, 3, RI_ALL_ONLINE }, { 0x120040, 4, RI_ALL_ONLINE },
- { 0x120050, 3, RI_ALL_ONLINE }, { 0x12005c, 4, RI_ALL_ONLINE },
- { 0x12006c, 3, RI_ALL_ONLINE }, { 0x120078, 4, RI_ALL_ONLINE },
- { 0x120088, 3, RI_ALL_ONLINE }, { 0x120094, 4, RI_ALL_ONLINE },
- { 0x1200a4, 3, RI_ALL_ONLINE }, { 0x1200b0, 4, RI_ALL_ONLINE },
- { 0x1200c0, 3, RI_ALL_ONLINE }, { 0x1200cc, 4, RI_ALL_ONLINE },
- { 0x1200dc, 3, RI_ALL_ONLINE }, { 0x1200e8, 4, RI_ALL_ONLINE },
- { 0x1200f8, 3, RI_ALL_ONLINE }, { 0x120104, 4, RI_ALL_ONLINE },
- { 0x120114, 1, RI_ALL_ONLINE }, { 0x120118, 22, RI_ALL_ONLINE },
- { 0x120170, 2, RI_E1E1H_ONLINE }, { 0x120178, 243, RI_ALL_ONLINE },
- { 0x120544, 4, RI_E1E1H_ONLINE }, { 0x120554, 7, RI_ALL_ONLINE },
- { 0x12059c, 6, RI_E1HE2_ONLINE }, { 0x1205b4, 1, RI_E1HE2_ONLINE },
- { 0x1205b8, 16, RI_E1HE2_ONLINE }, { 0x1205f8, 4, RI_E2_ONLINE },
- { 0x120618, 1, RI_E2_ONLINE }, { 0x12061c, 20, RI_E1HE2_ONLINE },
- { 0x12066c, 11, RI_E1HE2_ONLINE }, { 0x120698, 5, RI_E2_ONLINE },
- { 0x1206b0, 76, RI_E2_ONLINE }, { 0x1207fc, 1, RI_E2_ONLINE },
- { 0x120808, 66, RI_ALL_ONLINE }, { 0x120910, 7, RI_E2_ONLINE },
- { 0x120930, 9, RI_E2_ONLINE }, { 0x120a00, 2, RI_ALL_ONLINE },
- { 0x122000, 2, RI_ALL_ONLINE }, { 0x122008, 2046, RI_E1_OFFLINE },
- { 0x128000, 2, RI_E1HE2_ONLINE }, { 0x128008, 6142, RI_E1HE2_OFFLINE },
- { 0x130000, 35, RI_E2_ONLINE }, { 0x130100, 29, RI_E2_ONLINE },
- { 0x130180, 1, RI_E2_ONLINE }, { 0x130200, 1, RI_E2_ONLINE },
- { 0x130280, 1, RI_E2_ONLINE }, { 0x130300, 5, RI_E2_ONLINE },
- { 0x130380, 1, RI_E2_ONLINE }, { 0x130400, 1, RI_E2_ONLINE },
- { 0x130480, 5, RI_E2_ONLINE }, { 0x130800, 72, RI_E2_ONLINE },
- { 0x131000, 136, RI_E2_ONLINE }, { 0x132000, 148, RI_E2_ONLINE },
- { 0x134000, 544, RI_E2_ONLINE }, { 0x140000, 64, RI_ALL_ONLINE },
- { 0x140100, 5, RI_E1E1H_ONLINE }, { 0x140114, 45, RI_ALL_ONLINE },
- { 0x140200, 6, RI_ALL_ONLINE }, { 0x140220, 4, RI_E2_ONLINE },
- { 0x140240, 4, RI_E2_ONLINE }, { 0x140260, 4, RI_E2_ONLINE },
- { 0x140280, 4, RI_E2_ONLINE }, { 0x1402a0, 4, RI_E2_ONLINE },
- { 0x1402c0, 4, RI_E2_ONLINE }, { 0x1402e0, 13, RI_E2_ONLINE },
- { 0x144000, 4, RI_E1E1H_ONLINE }, { 0x148000, 4, RI_E1E1H_ONLINE },
- { 0x14c000, 4, RI_E1E1H_ONLINE }, { 0x150000, 4, RI_E1E1H_ONLINE },
- { 0x154000, 4, RI_E1E1H_ONLINE }, { 0x158000, 4, RI_E1E1H_ONLINE },
- { 0x15c000, 2, RI_E1HE2_ONLINE }, { 0x15c008, 5, RI_E1H_ONLINE },
- { 0x15c020, 27, RI_E2_ONLINE }, { 0x15c090, 13, RI_E2_ONLINE },
- { 0x15c0c8, 34, RI_E2_ONLINE }, { 0x161000, 7, RI_ALL_ONLINE },
- { 0x16103c, 2, RI_E2_ONLINE }, { 0x161800, 2, RI_ALL_ONLINE },
- { 0x164000, 60, RI_ALL_ONLINE }, { 0x164110, 2, RI_E1HE2_ONLINE },
- { 0x164118, 15, RI_E2_ONLINE }, { 0x164200, 1, RI_ALL_ONLINE },
- { 0x164208, 1, RI_ALL_ONLINE }, { 0x164210, 1, RI_ALL_ONLINE },
- { 0x164218, 1, RI_ALL_ONLINE }, { 0x164220, 1, RI_ALL_ONLINE },
- { 0x164228, 1, RI_ALL_ONLINE }, { 0x164230, 1, RI_ALL_ONLINE },
- { 0x164238, 1, RI_ALL_ONLINE }, { 0x164240, 1, RI_ALL_ONLINE },
- { 0x164248, 1, RI_ALL_ONLINE }, { 0x164250, 1, RI_ALL_ONLINE },
- { 0x164258, 1, RI_ALL_ONLINE }, { 0x164260, 1, RI_ALL_ONLINE },
- { 0x164270, 2, RI_ALL_ONLINE }, { 0x164280, 2, RI_ALL_ONLINE },
- { 0x164800, 2, RI_ALL_ONLINE }, { 0x165000, 2, RI_ALL_ONLINE },
- { 0x166000, 164, RI_ALL_ONLINE }, { 0x1662cc, 7, RI_E2_ONLINE },
- { 0x166400, 49, RI_ALL_ONLINE }, { 0x1664c8, 38, RI_ALL_ONLINE },
- { 0x166568, 2, RI_ALL_ONLINE }, { 0x166570, 5, RI_E2_ONLINE },
- { 0x166800, 1, RI_ALL_ONLINE }, { 0x168000, 137, RI_ALL_ONLINE },
- { 0x168224, 2, RI_E1E1H_ONLINE }, { 0x16822c, 29, RI_ALL_ONLINE },
- { 0x1682a0, 12, RI_E1E1H_ONLINE }, { 0x1682d0, 12, RI_ALL_ONLINE },
- { 0x168300, 2, RI_E1E1H_ONLINE }, { 0x168308, 68, RI_ALL_ONLINE },
- { 0x168418, 2, RI_E1E1H_ONLINE }, { 0x168420, 6, RI_ALL_ONLINE },
- { 0x168800, 19, RI_ALL_ONLINE }, { 0x168900, 1, RI_ALL_ONLINE },
- { 0x168a00, 128, RI_ALL_ONLINE }, { 0x16a000, 1, RI_ALL_ONLINE },
- { 0x16a004, 1535, RI_ALL_OFFLINE }, { 0x16c000, 1, RI_ALL_ONLINE },
- { 0x16c004, 1535, RI_ALL_OFFLINE }, { 0x16e000, 16, RI_E1H_ONLINE },
- { 0x16e040, 8, RI_E2_ONLINE }, { 0x16e100, 1, RI_E1H_ONLINE },
- { 0x16e200, 2, RI_E1H_ONLINE }, { 0x16e400, 161, RI_E1H_ONLINE },
- { 0x16e684, 2, RI_E1HE2_ONLINE }, { 0x16e68c, 12, RI_E1H_ONLINE },
- { 0x16e6bc, 4, RI_E1HE2_ONLINE }, { 0x16e6cc, 4, RI_E1H_ONLINE },
- { 0x16e6e0, 12, RI_E2_ONLINE }, { 0x16e768, 17, RI_E2_ONLINE },
- { 0x170000, 24, RI_ALL_ONLINE }, { 0x170060, 4, RI_E1E1H_ONLINE },
- { 0x170070, 65, RI_ALL_ONLINE }, { 0x170194, 11, RI_E2_ONLINE },
- { 0x1701c4, 1, RI_E2_ONLINE }, { 0x1701cc, 7, RI_E2_ONLINE },
- { 0x1701ec, 1, RI_E2_ONLINE }, { 0x1701f4, 1, RI_E2_ONLINE },
- { 0x170200, 4, RI_ALL_ONLINE }, { 0x170214, 1, RI_ALL_ONLINE },
- { 0x170218, 77, RI_E2_ONLINE }, { 0x170400, 64, RI_E2_ONLINE },
- { 0x178000, 1, RI_ALL_ONLINE }, { 0x180000, 61, RI_ALL_ONLINE },
- { 0x18013c, 2, RI_E1HE2_ONLINE }, { 0x180200, 58, RI_ALL_ONLINE },
- { 0x180340, 4, RI_ALL_ONLINE }, { 0x180380, 1, RI_E2_ONLINE },
- { 0x180388, 1, RI_E2_ONLINE }, { 0x180390, 1, RI_E2_ONLINE },
- { 0x180398, 1, RI_E2_ONLINE }, { 0x1803a0, 5, RI_E2_ONLINE },
- { 0x180400, 1, RI_ALL_ONLINE }, { 0x180404, 255, RI_E1E1H_OFFLINE },
- { 0x181000, 4, RI_ALL_ONLINE }, { 0x181010, 1020, RI_ALL_OFFLINE },
- { 0x1a0000, 1, RI_ALL_ONLINE }, { 0x1a0004, 5631, RI_ALL_OFFLINE },
- { 0x1a5800, 2560, RI_E1HE2_OFFLINE }, { 0x1a8000, 1, RI_ALL_ONLINE },
- { 0x1a8004, 8191, RI_E1HE2_OFFLINE }, { 0x1b0000, 1, RI_ALL_ONLINE },
- { 0x1b0004, 15, RI_E1H_OFFLINE }, { 0x1b0040, 1, RI_E1HE2_ONLINE },
- { 0x1b0044, 239, RI_E1H_OFFLINE }, { 0x1b0400, 1, RI_ALL_ONLINE },
- { 0x1b0404, 255, RI_E1H_OFFLINE }, { 0x1b0800, 1, RI_ALL_ONLINE },
- { 0x1b0840, 1, RI_E1HE2_ONLINE }, { 0x1b0c00, 1, RI_ALL_ONLINE },
- { 0x1b1000, 1, RI_ALL_ONLINE }, { 0x1b1040, 1, RI_E1HE2_ONLINE },
- { 0x1b1400, 1, RI_ALL_ONLINE }, { 0x1b1440, 1, RI_E1HE2_ONLINE },
- { 0x1b1480, 1, RI_E1HE2_ONLINE }, { 0x1b14c0, 1, RI_E1HE2_ONLINE },
- { 0x1b1800, 128, RI_ALL_OFFLINE }, { 0x1b1c00, 128, RI_ALL_OFFLINE },
- { 0x1b2000, 1, RI_ALL_ONLINE }, { 0x1b2400, 1, RI_E1HE2_ONLINE },
- { 0x1b2404, 5631, RI_E2_OFFLINE }, { 0x1b8000, 1, RI_ALL_ONLINE },
- { 0x1b8040, 1, RI_ALL_ONLINE }, { 0x1b8080, 1, RI_ALL_ONLINE },
- { 0x1b80c0, 1, RI_ALL_ONLINE }, { 0x1b8100, 1, RI_ALL_ONLINE },
- { 0x1b8140, 1, RI_ALL_ONLINE }, { 0x1b8180, 1, RI_ALL_ONLINE },
- { 0x1b81c0, 1, RI_ALL_ONLINE }, { 0x1b8200, 1, RI_ALL_ONLINE },
- { 0x1b8240, 1, RI_ALL_ONLINE }, { 0x1b8280, 1, RI_ALL_ONLINE },
- { 0x1b82c0, 1, RI_ALL_ONLINE }, { 0x1b8300, 1, RI_ALL_ONLINE },
- { 0x1b8340, 1, RI_ALL_ONLINE }, { 0x1b8380, 1, RI_ALL_ONLINE },
- { 0x1b83c0, 1, RI_ALL_ONLINE }, { 0x1b8400, 1, RI_ALL_ONLINE },
- { 0x1b8440, 1, RI_ALL_ONLINE }, { 0x1b8480, 1, RI_ALL_ONLINE },
- { 0x1b84c0, 1, RI_ALL_ONLINE }, { 0x1b8500, 1, RI_ALL_ONLINE },
- { 0x1b8540, 1, RI_ALL_ONLINE }, { 0x1b8580, 1, RI_ALL_ONLINE },
- { 0x1b85c0, 19, RI_E2_ONLINE }, { 0x1b8800, 1, RI_ALL_ONLINE },
- { 0x1b8840, 1, RI_ALL_ONLINE }, { 0x1b8880, 1, RI_ALL_ONLINE },
- { 0x1b88c0, 1, RI_ALL_ONLINE }, { 0x1b8900, 1, RI_ALL_ONLINE },
- { 0x1b8940, 1, RI_ALL_ONLINE }, { 0x1b8980, 1, RI_ALL_ONLINE },
- { 0x1b89c0, 1, RI_ALL_ONLINE }, { 0x1b8a00, 1, RI_ALL_ONLINE },
- { 0x1b8a40, 1, RI_ALL_ONLINE }, { 0x1b8a80, 1, RI_ALL_ONLINE },
- { 0x1b8ac0, 1, RI_ALL_ONLINE }, { 0x1b8b00, 1, RI_ALL_ONLINE },
- { 0x1b8b40, 1, RI_ALL_ONLINE }, { 0x1b8b80, 1, RI_ALL_ONLINE },
- { 0x1b8bc0, 1, RI_ALL_ONLINE }, { 0x1b8c00, 1, RI_ALL_ONLINE },
- { 0x1b8c40, 1, RI_ALL_ONLINE }, { 0x1b8c80, 1, RI_ALL_ONLINE },
- { 0x1b8cc0, 1, RI_ALL_ONLINE }, { 0x1b8cc4, 1, RI_E2_ONLINE },
- { 0x1b8d00, 1, RI_ALL_ONLINE }, { 0x1b8d40, 1, RI_ALL_ONLINE },
- { 0x1b8d80, 1, RI_ALL_ONLINE }, { 0x1b8dc0, 1, RI_ALL_ONLINE },
- { 0x1b8e00, 1, RI_ALL_ONLINE }, { 0x1b8e40, 1, RI_ALL_ONLINE },
- { 0x1b8e80, 1, RI_ALL_ONLINE }, { 0x1b8e84, 1, RI_E2_ONLINE },
- { 0x1b8ec0, 1, RI_E1HE2_ONLINE }, { 0x1b8f00, 1, RI_E1HE2_ONLINE },
- { 0x1b8f40, 1, RI_E1HE2_ONLINE }, { 0x1b8f80, 1, RI_E1HE2_ONLINE },
- { 0x1b8fc0, 1, RI_E1HE2_ONLINE }, { 0x1b8fc4, 2, RI_E2_ONLINE },
- { 0x1b8fd0, 6, RI_E2_ONLINE }, { 0x1b9000, 1, RI_E2_ONLINE },
- { 0x1b9040, 3, RI_E2_ONLINE }, { 0x1b9400, 14, RI_E2_ONLINE },
- { 0x1b943c, 19, RI_E2_ONLINE }, { 0x1b9490, 10, RI_E2_ONLINE },
- { 0x1c0000, 2, RI_ALL_ONLINE }, { 0x200000, 65, RI_ALL_ONLINE },
- { 0x20014c, 2, RI_E1HE2_ONLINE }, { 0x200200, 58, RI_ALL_ONLINE },
- { 0x200340, 4, RI_ALL_ONLINE }, { 0x200380, 1, RI_E2_ONLINE },
- { 0x200388, 1, RI_E2_ONLINE }, { 0x200390, 1, RI_E2_ONLINE },
- { 0x200398, 1, RI_E2_ONLINE }, { 0x2003a0, 1, RI_E2_ONLINE },
- { 0x2003a8, 2, RI_E2_ONLINE }, { 0x200400, 1, RI_ALL_ONLINE },
- { 0x200404, 255, RI_E1E1H_OFFLINE }, { 0x202000, 4, RI_ALL_ONLINE },
- { 0x202010, 2044, RI_ALL_OFFLINE }, { 0x220000, 1, RI_ALL_ONLINE },
- { 0x220004, 5631, RI_ALL_OFFLINE }, { 0x225800, 2560, RI_E1HE2_OFFLINE},
- { 0x228000, 1, RI_ALL_ONLINE }, { 0x228004, 8191, RI_E1HE2_OFFLINE },
- { 0x230000, 1, RI_ALL_ONLINE }, { 0x230004, 15, RI_E1H_OFFLINE },
- { 0x230040, 1, RI_E1HE2_ONLINE }, { 0x230044, 239, RI_E1H_OFFLINE },
- { 0x230400, 1, RI_ALL_ONLINE }, { 0x230404, 255, RI_E1H_OFFLINE },
- { 0x230800, 1, RI_ALL_ONLINE }, { 0x230840, 1, RI_E1HE2_ONLINE },
- { 0x230c00, 1, RI_ALL_ONLINE }, { 0x231000, 1, RI_ALL_ONLINE },
- { 0x231040, 1, RI_E1HE2_ONLINE }, { 0x231400, 1, RI_ALL_ONLINE },
- { 0x231440, 1, RI_E1HE2_ONLINE }, { 0x231480, 1, RI_E1HE2_ONLINE },
- { 0x2314c0, 1, RI_E1HE2_ONLINE }, { 0x231800, 128, RI_ALL_OFFLINE },
- { 0x231c00, 128, RI_ALL_OFFLINE }, { 0x232000, 1, RI_ALL_ONLINE },
- { 0x232400, 1, RI_E1HE2_ONLINE }, { 0x232404, 5631, RI_E2_OFFLINE },
- { 0x238000, 1, RI_ALL_ONLINE }, { 0x238040, 1, RI_ALL_ONLINE },
- { 0x238080, 1, RI_ALL_ONLINE }, { 0x2380c0, 1, RI_ALL_ONLINE },
- { 0x238100, 1, RI_ALL_ONLINE }, { 0x238140, 1, RI_ALL_ONLINE },
- { 0x238180, 1, RI_ALL_ONLINE }, { 0x2381c0, 1, RI_ALL_ONLINE },
- { 0x238200, 1, RI_ALL_ONLINE }, { 0x238240, 1, RI_ALL_ONLINE },
- { 0x238280, 1, RI_ALL_ONLINE }, { 0x2382c0, 1, RI_ALL_ONLINE },
- { 0x238300, 1, RI_ALL_ONLINE }, { 0x238340, 1, RI_ALL_ONLINE },
- { 0x238380, 1, RI_ALL_ONLINE }, { 0x2383c0, 1, RI_ALL_ONLINE },
- { 0x238400, 1, RI_ALL_ONLINE }, { 0x238440, 1, RI_ALL_ONLINE },
- { 0x238480, 1, RI_ALL_ONLINE }, { 0x2384c0, 1, RI_ALL_ONLINE },
- { 0x238500, 1, RI_ALL_ONLINE }, { 0x238540, 1, RI_ALL_ONLINE },
- { 0x238580, 1, RI_ALL_ONLINE }, { 0x2385c0, 19, RI_E2_ONLINE },
- { 0x238800, 1, RI_ALL_ONLINE }, { 0x238840, 1, RI_ALL_ONLINE },
- { 0x238880, 1, RI_ALL_ONLINE }, { 0x2388c0, 1, RI_ALL_ONLINE },
- { 0x238900, 1, RI_ALL_ONLINE }, { 0x238940, 1, RI_ALL_ONLINE },
- { 0x238980, 1, RI_ALL_ONLINE }, { 0x2389c0, 1, RI_ALL_ONLINE },
- { 0x238a00, 1, RI_ALL_ONLINE }, { 0x238a40, 1, RI_ALL_ONLINE },
- { 0x238a80, 1, RI_ALL_ONLINE }, { 0x238ac0, 1, RI_ALL_ONLINE },
- { 0x238b00, 1, RI_ALL_ONLINE }, { 0x238b40, 1, RI_ALL_ONLINE },
- { 0x238b80, 1, RI_ALL_ONLINE }, { 0x238bc0, 1, RI_ALL_ONLINE },
- { 0x238c00, 1, RI_ALL_ONLINE }, { 0x238c40, 1, RI_ALL_ONLINE },
- { 0x238c80, 1, RI_ALL_ONLINE }, { 0x238cc0, 1, RI_ALL_ONLINE },
- { 0x238cc4, 1, RI_E2_ONLINE }, { 0x238d00, 1, RI_ALL_ONLINE },
- { 0x238d40, 1, RI_ALL_ONLINE }, { 0x238d80, 1, RI_ALL_ONLINE },
- { 0x238dc0, 1, RI_ALL_ONLINE }, { 0x238e00, 1, RI_ALL_ONLINE },
- { 0x238e40, 1, RI_ALL_ONLINE }, { 0x238e80, 1, RI_ALL_ONLINE },
- { 0x238e84, 1, RI_E2_ONLINE }, { 0x238ec0, 1, RI_E1HE2_ONLINE },
- { 0x238f00, 1, RI_E1HE2_ONLINE }, { 0x238f40, 1, RI_E1HE2_ONLINE },
- { 0x238f80, 1, RI_E1HE2_ONLINE }, { 0x238fc0, 1, RI_E1HE2_ONLINE },
- { 0x238fc4, 2, RI_E2_ONLINE }, { 0x238fd0, 6, RI_E2_ONLINE },
- { 0x239000, 1, RI_E2_ONLINE }, { 0x239040, 3, RI_E2_ONLINE },
- { 0x240000, 2, RI_ALL_ONLINE }, { 0x280000, 65, RI_ALL_ONLINE },
- { 0x28014c, 2, RI_E1HE2_ONLINE }, { 0x280200, 58, RI_ALL_ONLINE },
- { 0x280340, 4, RI_ALL_ONLINE }, { 0x280380, 1, RI_E2_ONLINE },
- { 0x280388, 1, RI_E2_ONLINE }, { 0x280390, 1, RI_E2_ONLINE },
- { 0x280398, 1, RI_E2_ONLINE }, { 0x2803a0, 1, RI_E2_ONLINE },
- { 0x2803a8, 2, RI_E2_ONLINE }, { 0x280400, 1, RI_ALL_ONLINE },
- { 0x280404, 255, RI_E1E1H_OFFLINE }, { 0x282000, 4, RI_ALL_ONLINE },
- { 0x282010, 2044, RI_ALL_OFFLINE }, { 0x2a0000, 1, RI_ALL_ONLINE },
- { 0x2a0004, 5631, RI_ALL_OFFLINE }, { 0x2a5800, 2560, RI_E1HE2_OFFLINE},
- { 0x2a8000, 1, RI_ALL_ONLINE }, { 0x2a8004, 8191, RI_E1HE2_OFFLINE },
- { 0x2b0000, 1, RI_ALL_ONLINE }, { 0x2b0004, 15, RI_E1H_OFFLINE },
- { 0x2b0040, 1, RI_E1HE2_ONLINE }, { 0x2b0044, 239, RI_E1H_OFFLINE },
- { 0x2b0400, 1, RI_ALL_ONLINE }, { 0x2b0404, 255, RI_E1H_OFFLINE },
- { 0x2b0800, 1, RI_ALL_ONLINE }, { 0x2b0840, 1, RI_E1HE2_ONLINE },
- { 0x2b0c00, 1, RI_ALL_ONLINE }, { 0x2b1000, 1, RI_ALL_ONLINE },
- { 0x2b1040, 1, RI_E1HE2_ONLINE }, { 0x2b1400, 1, RI_ALL_ONLINE },
- { 0x2b1440, 1, RI_E1HE2_ONLINE }, { 0x2b1480, 1, RI_E1HE2_ONLINE },
- { 0x2b14c0, 1, RI_E1HE2_ONLINE }, { 0x2b1800, 128, RI_ALL_OFFLINE },
- { 0x2b1c00, 128, RI_ALL_OFFLINE }, { 0x2b2000, 1, RI_ALL_ONLINE },
- { 0x2b2400, 1, RI_E1HE2_ONLINE }, { 0x2b2404, 5631, RI_E2_OFFLINE },
- { 0x2b8000, 1, RI_ALL_ONLINE }, { 0x2b8040, 1, RI_ALL_ONLINE },
- { 0x2b8080, 1, RI_ALL_ONLINE }, { 0x2b80c0, 1, RI_ALL_ONLINE },
- { 0x2b8100, 1, RI_ALL_ONLINE }, { 0x2b8140, 1, RI_ALL_ONLINE },
- { 0x2b8180, 1, RI_ALL_ONLINE }, { 0x2b81c0, 1, RI_ALL_ONLINE },
- { 0x2b8200, 1, RI_ALL_ONLINE }, { 0x2b8240, 1, RI_ALL_ONLINE },
- { 0x2b8280, 1, RI_ALL_ONLINE }, { 0x2b82c0, 1, RI_ALL_ONLINE },
- { 0x2b8300, 1, RI_ALL_ONLINE }, { 0x2b8340, 1, RI_ALL_ONLINE },
- { 0x2b8380, 1, RI_ALL_ONLINE }, { 0x2b83c0, 1, RI_ALL_ONLINE },
- { 0x2b8400, 1, RI_ALL_ONLINE }, { 0x2b8440, 1, RI_ALL_ONLINE },
- { 0x2b8480, 1, RI_ALL_ONLINE }, { 0x2b84c0, 1, RI_ALL_ONLINE },
- { 0x2b8500, 1, RI_ALL_ONLINE }, { 0x2b8540, 1, RI_ALL_ONLINE },
- { 0x2b8580, 1, RI_ALL_ONLINE }, { 0x2b85c0, 19, RI_E2_ONLINE },
- { 0x2b8800, 1, RI_ALL_ONLINE }, { 0x2b8840, 1, RI_ALL_ONLINE },
- { 0x2b8880, 1, RI_ALL_ONLINE }, { 0x2b88c0, 1, RI_ALL_ONLINE },
- { 0x2b8900, 1, RI_ALL_ONLINE }, { 0x2b8940, 1, RI_ALL_ONLINE },
- { 0x2b8980, 1, RI_ALL_ONLINE }, { 0x2b89c0, 1, RI_ALL_ONLINE },
- { 0x2b8a00, 1, RI_ALL_ONLINE }, { 0x2b8a40, 1, RI_ALL_ONLINE },
- { 0x2b8a80, 1, RI_ALL_ONLINE }, { 0x2b8ac0, 1, RI_ALL_ONLINE },
- { 0x2b8b00, 1, RI_ALL_ONLINE }, { 0x2b8b40, 1, RI_ALL_ONLINE },
- { 0x2b8b80, 1, RI_ALL_ONLINE }, { 0x2b8bc0, 1, RI_ALL_ONLINE },
- { 0x2b8c00, 1, RI_ALL_ONLINE }, { 0x2b8c40, 1, RI_ALL_ONLINE },
- { 0x2b8c80, 1, RI_ALL_ONLINE }, { 0x2b8cc0, 1, RI_ALL_ONLINE },
- { 0x2b8cc4, 1, RI_E2_ONLINE }, { 0x2b8d00, 1, RI_ALL_ONLINE },
- { 0x2b8d40, 1, RI_ALL_ONLINE }, { 0x2b8d80, 1, RI_ALL_ONLINE },
- { 0x2b8dc0, 1, RI_ALL_ONLINE }, { 0x2b8e00, 1, RI_ALL_ONLINE },
- { 0x2b8e40, 1, RI_ALL_ONLINE }, { 0x2b8e80, 1, RI_ALL_ONLINE },
- { 0x2b8e84, 1, RI_E2_ONLINE }, { 0x2b8ec0, 1, RI_E1HE2_ONLINE },
- { 0x2b8f00, 1, RI_E1HE2_ONLINE }, { 0x2b8f40, 1, RI_E1HE2_ONLINE },
- { 0x2b8f80, 1, RI_E1HE2_ONLINE }, { 0x2b8fc0, 1, RI_E1HE2_ONLINE },
- { 0x2b8fc4, 2, RI_E2_ONLINE }, { 0x2b8fd0, 6, RI_E2_ONLINE },
- { 0x2b9000, 1, RI_E2_ONLINE }, { 0x2b9040, 3, RI_E2_ONLINE },
- { 0x2b9400, 14, RI_E2_ONLINE }, { 0x2b943c, 19, RI_E2_ONLINE },
- { 0x2b9490, 10, RI_E2_ONLINE }, { 0x2c0000, 2, RI_ALL_ONLINE },
- { 0x300000, 65, RI_ALL_ONLINE }, { 0x30014c, 2, RI_E1HE2_ONLINE },
- { 0x300200, 58, RI_ALL_ONLINE }, { 0x300340, 4, RI_ALL_ONLINE },
- { 0x300380, 1, RI_E2_ONLINE }, { 0x300388, 1, RI_E2_ONLINE },
- { 0x300390, 1, RI_E2_ONLINE }, { 0x300398, 1, RI_E2_ONLINE },
- { 0x3003a0, 1, RI_E2_ONLINE }, { 0x3003a8, 2, RI_E2_ONLINE },
- { 0x300400, 1, RI_ALL_ONLINE }, { 0x300404, 255, RI_E1E1H_OFFLINE },
- { 0x302000, 4, RI_ALL_ONLINE }, { 0x302010, 2044, RI_ALL_OFFLINE },
- { 0x320000, 1, RI_ALL_ONLINE }, { 0x320004, 5631, RI_ALL_OFFLINE },
- { 0x325800, 2560, RI_E1HE2_OFFLINE }, { 0x328000, 1, RI_ALL_ONLINE },
- { 0x328004, 8191, RI_E1HE2_OFFLINE }, { 0x330000, 1, RI_ALL_ONLINE },
- { 0x330004, 15, RI_E1H_OFFLINE }, { 0x330040, 1, RI_E1HE2_ONLINE },
- { 0x330044, 239, RI_E1H_OFFLINE }, { 0x330400, 1, RI_ALL_ONLINE },
- { 0x330404, 255, RI_E1H_OFFLINE }, { 0x330800, 1, RI_ALL_ONLINE },
- { 0x330840, 1, RI_E1HE2_ONLINE }, { 0x330c00, 1, RI_ALL_ONLINE },
- { 0x331000, 1, RI_ALL_ONLINE }, { 0x331040, 1, RI_E1HE2_ONLINE },
- { 0x331400, 1, RI_ALL_ONLINE }, { 0x331440, 1, RI_E1HE2_ONLINE },
- { 0x331480, 1, RI_E1HE2_ONLINE }, { 0x3314c0, 1, RI_E1HE2_ONLINE },
- { 0x331800, 128, RI_ALL_OFFLINE }, { 0x331c00, 128, RI_ALL_OFFLINE },
- { 0x332000, 1, RI_ALL_ONLINE }, { 0x332400, 1, RI_E1HE2_ONLINE },
- { 0x332404, 5631, RI_E2_OFFLINE }, { 0x338000, 1, RI_ALL_ONLINE },
- { 0x338040, 1, RI_ALL_ONLINE }, { 0x338080, 1, RI_ALL_ONLINE },
- { 0x3380c0, 1, RI_ALL_ONLINE }, { 0x338100, 1, RI_ALL_ONLINE },
- { 0x338140, 1, RI_ALL_ONLINE }, { 0x338180, 1, RI_ALL_ONLINE },
- { 0x3381c0, 1, RI_ALL_ONLINE }, { 0x338200, 1, RI_ALL_ONLINE },
- { 0x338240, 1, RI_ALL_ONLINE }, { 0x338280, 1, RI_ALL_ONLINE },
- { 0x3382c0, 1, RI_ALL_ONLINE }, { 0x338300, 1, RI_ALL_ONLINE },
- { 0x338340, 1, RI_ALL_ONLINE }, { 0x338380, 1, RI_ALL_ONLINE },
- { 0x3383c0, 1, RI_ALL_ONLINE }, { 0x338400, 1, RI_ALL_ONLINE },
- { 0x338440, 1, RI_ALL_ONLINE }, { 0x338480, 1, RI_ALL_ONLINE },
- { 0x3384c0, 1, RI_ALL_ONLINE }, { 0x338500, 1, RI_ALL_ONLINE },
- { 0x338540, 1, RI_ALL_ONLINE }, { 0x338580, 1, RI_ALL_ONLINE },
- { 0x3385c0, 19, RI_E2_ONLINE }, { 0x338800, 1, RI_ALL_ONLINE },
- { 0x338840, 1, RI_ALL_ONLINE }, { 0x338880, 1, RI_ALL_ONLINE },
- { 0x3388c0, 1, RI_ALL_ONLINE }, { 0x338900, 1, RI_ALL_ONLINE },
- { 0x338940, 1, RI_ALL_ONLINE }, { 0x338980, 1, RI_ALL_ONLINE },
- { 0x3389c0, 1, RI_ALL_ONLINE }, { 0x338a00, 1, RI_ALL_ONLINE },
- { 0x338a40, 1, RI_ALL_ONLINE }, { 0x338a80, 1, RI_ALL_ONLINE },
- { 0x338ac0, 1, RI_ALL_ONLINE }, { 0x338b00, 1, RI_ALL_ONLINE },
- { 0x338b40, 1, RI_ALL_ONLINE }, { 0x338b80, 1, RI_ALL_ONLINE },
- { 0x338bc0, 1, RI_ALL_ONLINE }, { 0x338c00, 1, RI_ALL_ONLINE },
- { 0x338c40, 1, RI_ALL_ONLINE }, { 0x338c80, 1, RI_ALL_ONLINE },
- { 0x338cc0, 1, RI_ALL_ONLINE }, { 0x338cc4, 1, RI_E2_ONLINE },
- { 0x338d00, 1, RI_ALL_ONLINE }, { 0x338d40, 1, RI_ALL_ONLINE },
- { 0x338d80, 1, RI_ALL_ONLINE }, { 0x338dc0, 1, RI_ALL_ONLINE },
- { 0x338e00, 1, RI_ALL_ONLINE }, { 0x338e40, 1, RI_ALL_ONLINE },
- { 0x338e80, 1, RI_ALL_ONLINE }, { 0x338e84, 1, RI_E2_ONLINE },
- { 0x338ec0, 1, RI_E1HE2_ONLINE }, { 0x338f00, 1, RI_E1HE2_ONLINE },
- { 0x338f40, 1, RI_E1HE2_ONLINE }, { 0x338f80, 1, RI_E1HE2_ONLINE },
- { 0x338fc0, 1, RI_E1HE2_ONLINE }, { 0x338fc4, 2, RI_E2_ONLINE },
- { 0x338fd0, 6, RI_E2_ONLINE }, { 0x339000, 1, RI_E2_ONLINE },
- { 0x339040, 3, RI_E2_ONLINE }, { 0x340000, 2, RI_ALL_ONLINE },
-};
-
-#define IDLE_REGS_COUNT 237
-static const struct reg_addr idle_addrs[IDLE_REGS_COUNT] = {
- { 0x2104, 1, RI_ALL_ONLINE }, { 0x2110, 2, RI_ALL_ONLINE },
- { 0x211c, 8, RI_ALL_ONLINE }, { 0x2814, 1, RI_ALL_ONLINE },
- { 0x281c, 2, RI_ALL_ONLINE }, { 0x2854, 1, RI_ALL_ONLINE },
- { 0x285c, 1, RI_ALL_ONLINE }, { 0x9010, 7, RI_E2_ONLINE },
- { 0x9030, 1, RI_E2_ONLINE }, { 0x9068, 16, RI_E2_ONLINE },
- { 0x9230, 2, RI_E2_ONLINE }, { 0x9244, 1, RI_E2_ONLINE },
- { 0x9298, 1, RI_E2_ONLINE }, { 0x92a8, 1, RI_E2_ONLINE },
- { 0xa38c, 1, RI_ALL_ONLINE }, { 0xa3c4, 1, RI_E1HE2_ONLINE },
- { 0xa408, 1, RI_ALL_ONLINE }, { 0xa42c, 12, RI_ALL_ONLINE },
- { 0xa600, 5, RI_E1HE2_ONLINE }, { 0xa618, 1, RI_E1HE2_ONLINE },
- { 0xa714, 1, RI_E2_ONLINE }, { 0xa720, 1, RI_E2_ONLINE },
- { 0xa750, 1, RI_E2_ONLINE }, { 0xc09c, 1, RI_E1E1H_ONLINE },
- { 0x103b0, 1, RI_ALL_ONLINE }, { 0x103c0, 1, RI_ALL_ONLINE },
- { 0x103d0, 1, RI_E1H_ONLINE }, { 0x183bc, 1, RI_E2_ONLINE },
- { 0x183cc, 1, RI_E2_ONLINE }, { 0x2021c, 11, RI_ALL_ONLINE },
- { 0x202a8, 1, RI_ALL_ONLINE }, { 0x202b8, 1, RI_ALL_ONLINE },
- { 0x20404, 1, RI_ALL_ONLINE }, { 0x2040c, 2, RI_ALL_ONLINE },
- { 0x2041c, 2, RI_ALL_ONLINE }, { 0x40154, 14, RI_ALL_ONLINE },
- { 0x40198, 1, RI_ALL_ONLINE }, { 0x404ac, 1, RI_ALL_ONLINE },
- { 0x404bc, 1, RI_ALL_ONLINE }, { 0x42290, 1, RI_ALL_ONLINE },
- { 0x422a0, 1, RI_ALL_ONLINE }, { 0x422b0, 1, RI_ALL_ONLINE },
- { 0x42548, 1, RI_ALL_ONLINE }, { 0x42550, 1, RI_ALL_ONLINE },
- { 0x42558, 1, RI_ALL_ONLINE }, { 0x50160, 8, RI_ALL_ONLINE },
- { 0x501d0, 1, RI_ALL_ONLINE }, { 0x501e0, 1, RI_ALL_ONLINE },
- { 0x50204, 1, RI_ALL_ONLINE }, { 0x5020c, 2, RI_ALL_ONLINE },
- { 0x5021c, 1, RI_ALL_ONLINE }, { 0x60090, 1, RI_ALL_ONLINE },
- { 0x6011c, 1, RI_ALL_ONLINE }, { 0x6012c, 1, RI_ALL_ONLINE },
- { 0xc101c, 1, RI_ALL_ONLINE }, { 0xc102c, 1, RI_ALL_ONLINE },
- { 0xc2290, 1, RI_ALL_ONLINE }, { 0xc22a0, 1, RI_ALL_ONLINE },
- { 0xc22b0, 1, RI_ALL_ONLINE }, { 0xc2548, 1, RI_ALL_ONLINE },
- { 0xc2550, 1, RI_ALL_ONLINE }, { 0xc2558, 1, RI_ALL_ONLINE },
- { 0xc4294, 1, RI_ALL_ONLINE }, { 0xc42a4, 1, RI_ALL_ONLINE },
- { 0xc42b4, 1, RI_ALL_ONLINE }, { 0xc4550, 1, RI_ALL_ONLINE },
- { 0xc4558, 1, RI_ALL_ONLINE }, { 0xc4560, 1, RI_ALL_ONLINE },
- { 0xd016c, 8, RI_ALL_ONLINE }, { 0xd01d8, 1, RI_ALL_ONLINE },
- { 0xd01e8, 1, RI_ALL_ONLINE }, { 0xd0204, 1, RI_ALL_ONLINE },
- { 0xd020c, 3, RI_ALL_ONLINE }, { 0xe0154, 8, RI_ALL_ONLINE },
- { 0xe01c8, 1, RI_ALL_ONLINE }, { 0xe01d8, 1, RI_ALL_ONLINE },
- { 0xe0204, 1, RI_ALL_ONLINE }, { 0xe020c, 2, RI_ALL_ONLINE },
- { 0xe021c, 2, RI_ALL_ONLINE }, { 0x101014, 1, RI_ALL_ONLINE },
- { 0x101030, 1, RI_ALL_ONLINE }, { 0x101040, 1, RI_ALL_ONLINE },
- { 0x102058, 1, RI_ALL_ONLINE }, { 0x102080, 16, RI_ALL_ONLINE },
- { 0x103004, 2, RI_ALL_ONLINE }, { 0x103068, 1, RI_ALL_ONLINE },
- { 0x103078, 1, RI_ALL_ONLINE }, { 0x103088, 1, RI_ALL_ONLINE },
- { 0x10309c, 2, RI_E1HE2_ONLINE }, { 0x1030b8, 2, RI_E2_ONLINE },
- { 0x1030cc, 1, RI_E2_ONLINE }, { 0x1030e0, 1, RI_E2_ONLINE },
- { 0x104004, 1, RI_ALL_ONLINE }, { 0x104018, 1, RI_ALL_ONLINE },
- { 0x104020, 1, RI_ALL_ONLINE }, { 0x10403c, 1, RI_ALL_ONLINE },
- { 0x1040fc, 1, RI_ALL_ONLINE }, { 0x10410c, 1, RI_ALL_ONLINE },
- { 0x104400, 64, RI_ALL_ONLINE }, { 0x104800, 64, RI_ALL_ONLINE },
- { 0x105000, 256, RI_ALL_ONLINE }, { 0x108094, 1, RI_E1E1H_ONLINE },
- { 0x1201b0, 2, RI_ALL_ONLINE }, { 0x12032c, 1, RI_ALL_ONLINE },
- { 0x12036c, 3, RI_ALL_ONLINE }, { 0x120408, 2, RI_ALL_ONLINE },
- { 0x120414, 15, RI_ALL_ONLINE }, { 0x120478, 2, RI_ALL_ONLINE },
- { 0x12052c, 1, RI_ALL_ONLINE }, { 0x120564, 3, RI_ALL_ONLINE },
- { 0x12057c, 1, RI_ALL_ONLINE }, { 0x12058c, 1, RI_ALL_ONLINE },
- { 0x120608, 1, RI_E1HE2_ONLINE }, { 0x120738, 1, RI_E2_ONLINE },
- { 0x120778, 2, RI_E2_ONLINE }, { 0x120808, 3, RI_ALL_ONLINE },
- { 0x120818, 1, RI_ALL_ONLINE }, { 0x120820, 1, RI_ALL_ONLINE },
- { 0x120828, 1, RI_ALL_ONLINE }, { 0x120830, 1, RI_ALL_ONLINE },
- { 0x120838, 1, RI_ALL_ONLINE }, { 0x120840, 1, RI_ALL_ONLINE },
- { 0x120848, 1, RI_ALL_ONLINE }, { 0x120850, 1, RI_ALL_ONLINE },
- { 0x120858, 1, RI_ALL_ONLINE }, { 0x120860, 1, RI_ALL_ONLINE },
- { 0x120868, 1, RI_ALL_ONLINE }, { 0x120870, 1, RI_ALL_ONLINE },
- { 0x120878, 1, RI_ALL_ONLINE }, { 0x120880, 1, RI_ALL_ONLINE },
- { 0x120888, 1, RI_ALL_ONLINE }, { 0x120890, 1, RI_ALL_ONLINE },
- { 0x120898, 1, RI_ALL_ONLINE }, { 0x1208a0, 1, RI_ALL_ONLINE },
- { 0x1208a8, 1, RI_ALL_ONLINE }, { 0x1208b0, 1, RI_ALL_ONLINE },
- { 0x1208b8, 1, RI_ALL_ONLINE }, { 0x1208c0, 1, RI_ALL_ONLINE },
- { 0x1208c8, 1, RI_ALL_ONLINE }, { 0x1208d0, 1, RI_ALL_ONLINE },
- { 0x1208d8, 1, RI_ALL_ONLINE }, { 0x1208e0, 1, RI_ALL_ONLINE },
- { 0x1208e8, 1, RI_ALL_ONLINE }, { 0x1208f0, 1, RI_ALL_ONLINE },
- { 0x1208f8, 1, RI_ALL_ONLINE }, { 0x120900, 1, RI_ALL_ONLINE },
- { 0x120908, 1, RI_ALL_ONLINE }, { 0x120940, 5, RI_E2_ONLINE },
- { 0x130030, 1, RI_E2_ONLINE }, { 0x13004c, 3, RI_E2_ONLINE },
- { 0x130064, 2, RI_E2_ONLINE }, { 0x13009c, 1, RI_E2_ONLINE },
- { 0x130130, 1, RI_E2_ONLINE }, { 0x13016c, 1, RI_E2_ONLINE },
- { 0x130300, 1, RI_E2_ONLINE }, { 0x130480, 1, RI_E2_ONLINE },
- { 0x14005c, 2, RI_ALL_ONLINE }, { 0x1400d0, 2, RI_ALL_ONLINE },
- { 0x1400e0, 1, RI_ALL_ONLINE }, { 0x1401c8, 1, RI_ALL_ONLINE },
- { 0x140200, 6, RI_ALL_ONLINE }, { 0x16101c, 1, RI_ALL_ONLINE },
- { 0x16102c, 1, RI_ALL_ONLINE }, { 0x164014, 2, RI_ALL_ONLINE },
- { 0x1640f0, 1, RI_ALL_ONLINE }, { 0x166290, 1, RI_ALL_ONLINE },
- { 0x1662a0, 1, RI_ALL_ONLINE }, { 0x1662b0, 1, RI_ALL_ONLINE },
- { 0x166548, 1, RI_ALL_ONLINE }, { 0x166550, 1, RI_ALL_ONLINE },
- { 0x166558, 1, RI_ALL_ONLINE }, { 0x168000, 1, RI_ALL_ONLINE },
- { 0x168008, 1, RI_ALL_ONLINE }, { 0x168010, 1, RI_ALL_ONLINE },
- { 0x168018, 1, RI_ALL_ONLINE }, { 0x168028, 2, RI_ALL_ONLINE },
- { 0x168058, 4, RI_ALL_ONLINE }, { 0x168070, 1, RI_ALL_ONLINE },
- { 0x168238, 1, RI_ALL_ONLINE }, { 0x1682d0, 2, RI_ALL_ONLINE },
- { 0x1682e0, 1, RI_ALL_ONLINE }, { 0x168300, 2, RI_E1E1H_ONLINE },
- { 0x168308, 65, RI_ALL_ONLINE }, { 0x168410, 2, RI_ALL_ONLINE },
- { 0x168438, 1, RI_ALL_ONLINE }, { 0x168448, 1, RI_ALL_ONLINE },
- { 0x168a00, 128, RI_ALL_ONLINE }, { 0x16e200, 128, RI_E1H_ONLINE },
- { 0x16e404, 2, RI_E1H_ONLINE }, { 0x16e584, 64, RI_E1H_ONLINE },
- { 0x16e684, 2, RI_E1HE2_ONLINE }, { 0x16e68c, 4, RI_E1H_ONLINE },
- { 0x16e6fc, 4, RI_E2_ONLINE }, { 0x1700a4, 1, RI_ALL_ONLINE },
- { 0x1700ac, 2, RI_ALL_ONLINE }, { 0x1700c0, 1, RI_ALL_ONLINE },
- { 0x170174, 1, RI_ALL_ONLINE }, { 0x170184, 1, RI_ALL_ONLINE },
- { 0x1800f4, 1, RI_ALL_ONLINE }, { 0x180104, 1, RI_ALL_ONLINE },
- { 0x180114, 1, RI_ALL_ONLINE }, { 0x180124, 1, RI_ALL_ONLINE },
- { 0x18026c, 1, RI_ALL_ONLINE }, { 0x1802a0, 1, RI_ALL_ONLINE },
- { 0x1b8000, 1, RI_ALL_ONLINE }, { 0x1b8040, 1, RI_ALL_ONLINE },
- { 0x1b8080, 1, RI_ALL_ONLINE }, { 0x1b80c0, 1, RI_ALL_ONLINE },
- { 0x200104, 1, RI_ALL_ONLINE }, { 0x200114, 1, RI_ALL_ONLINE },
- { 0x200124, 1, RI_ALL_ONLINE }, { 0x200134, 1, RI_ALL_ONLINE },
- { 0x20026c, 1, RI_ALL_ONLINE }, { 0x2002a0, 1, RI_ALL_ONLINE },
- { 0x238000, 1, RI_ALL_ONLINE }, { 0x238040, 1, RI_ALL_ONLINE },
- { 0x238080, 1, RI_ALL_ONLINE }, { 0x2380c0, 1, RI_ALL_ONLINE },
- { 0x280104, 1, RI_ALL_ONLINE }, { 0x280114, 1, RI_ALL_ONLINE },
- { 0x280124, 1, RI_ALL_ONLINE }, { 0x280134, 1, RI_ALL_ONLINE },
- { 0x28026c, 1, RI_ALL_ONLINE }, { 0x2802a0, 1, RI_ALL_ONLINE },
- { 0x2b8000, 1, RI_ALL_ONLINE }, { 0x2b8040, 1, RI_ALL_ONLINE },
- { 0x2b8080, 1, RI_ALL_ONLINE }, { 0x300104, 1, RI_ALL_ONLINE },
- { 0x300114, 1, RI_ALL_ONLINE }, { 0x300124, 1, RI_ALL_ONLINE },
- { 0x300134, 1, RI_ALL_ONLINE }, { 0x30026c, 1, RI_ALL_ONLINE },
- { 0x3002a0, 1, RI_ALL_ONLINE }, { 0x338000, 1, RI_ALL_ONLINE },
- { 0x338040, 1, RI_ALL_ONLINE }, { 0x338080, 1, RI_ALL_ONLINE },
- { 0x3380c0, 1, RI_ALL_ONLINE }
-};
-
-#define WREGS_COUNT_E1 1
-static const u32 read_reg_e1_0[] = { 0x1b1000 };
-
-static const struct wreg_addr wreg_addrs_e1[WREGS_COUNT_E1] = {
- { 0x1b0c00, 192, 1, read_reg_e1_0, RI_E1_OFFLINE }
-};
-
-#define WREGS_COUNT_E1H 1
-static const u32 read_reg_e1h_0[] = { 0x1b1040, 0x1b1000 };
-
-static const struct wreg_addr wreg_addrs_e1h[WREGS_COUNT_E1H] = {
- { 0x1b0c00, 256, 2, read_reg_e1h_0, RI_E1H_OFFLINE }
-};
-
-#define WREGS_COUNT_E2 1
-static const u32 read_reg_e2_0[] = { 0x1b1040, 0x1b1000 };
-
-static const struct wreg_addr wreg_addrs_e2[WREGS_COUNT_E2] = {
- { 0x1b0c00, 128, 2, read_reg_e2_0, RI_E2_OFFLINE }
-};
-
-static const struct dump_sign dump_sign_all = { 0x4d18b0a4, 0x60010, 0x3a };
-
-#define TIMER_REGS_COUNT_E1 2
-
-static const u32 timer_status_regs_e1[TIMER_REGS_COUNT_E1] = {
- 0x164014, 0x164018 };
-static const u32 timer_scan_regs_e1[TIMER_REGS_COUNT_E1] = {
- 0x1640d0, 0x1640d4 };
-
-#define TIMER_REGS_COUNT_E1H 2
-
-static const u32 timer_status_regs_e1h[TIMER_REGS_COUNT_E1H] = {
- 0x164014, 0x164018 };
-static const u32 timer_scan_regs_e1h[TIMER_REGS_COUNT_E1H] = {
- 0x1640d0, 0x1640d4 };
-
-#define TIMER_REGS_COUNT_E2 2
-
-static const u32 timer_status_regs_e2[TIMER_REGS_COUNT_E2] = {
- 0x164014, 0x164018 };
-static const u32 timer_scan_regs_e2[TIMER_REGS_COUNT_E2] = {
- 0x1640d0, 0x1640d4 };
-
-#define PAGE_MODE_VALUES_E1 0
-
-#define PAGE_READ_REGS_E1 0
-
-#define PAGE_WRITE_REGS_E1 0
-
-static const u32 page_vals_e1[] = { 0 };
-
-static const u32 page_write_regs_e1[] = { 0 };
-
-static const struct reg_addr page_read_regs_e1[] = { { 0x0, 0, RI_E1_ONLINE } };
-
-#define PAGE_MODE_VALUES_E1H 0
-
-#define PAGE_READ_REGS_E1H 0
-
-#define PAGE_WRITE_REGS_E1H 0
-
-static const u32 page_vals_e1h[] = { 0 };
-
-static const u32 page_write_regs_e1h[] = { 0 };
-
-static const struct reg_addr page_read_regs_e1h[] = {
- { 0x0, 0, RI_E1H_ONLINE } };
-
-#define PAGE_MODE_VALUES_E2 2
-
-#define PAGE_READ_REGS_E2 1
-
-#define PAGE_WRITE_REGS_E2 1
-
-static const u32 page_vals_e2[PAGE_MODE_VALUES_E2] = { 0, 128 };
-
-static const u32 page_write_regs_e2[PAGE_WRITE_REGS_E2] = { 328476 };
-
-static const struct reg_addr page_read_regs_e2[PAGE_READ_REGS_E2] = {
- { 0x58000, 4608, RI_E2_ONLINE } };
-
-#endif /* BNX2X_DUMP_H */
diff --git a/drivers/net/bnx2x/bnx2x_fw_defs.h b/drivers/net/bnx2x/bnx2x_fw_defs.h
deleted file mode 100644
index 9fe367836a57..000000000000
--- a/drivers/net/bnx2x/bnx2x_fw_defs.h
+++ /dev/null
@@ -1,531 +0,0 @@
-/* bnx2x_fw_defs.h: Broadcom Everest network driver.
- *
- * Copyright (c) 2007-2011 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation.
- */
-
-#ifndef BNX2X_FW_DEFS_H
-#define BNX2X_FW_DEFS_H
-
-#define CSTORM_ASSERT_LIST_INDEX_OFFSET (IRO[142].base)
-#define CSTORM_ASSERT_LIST_OFFSET(assertListEntry) \
- (IRO[141].base + ((assertListEntry) * IRO[141].m1))
-#define CSTORM_ETH_STATS_QUERY_ADDR_OFFSET(pfId) \
- (IRO[144].base + ((pfId) * IRO[144].m1))
-#define CSTORM_EVENT_RING_DATA_OFFSET(pfId) \
- (IRO[149].base + (((pfId)>>1) * IRO[149].m1) + (((pfId)&1) * \
- IRO[149].m2))
-#define CSTORM_EVENT_RING_PROD_OFFSET(pfId) \
- (IRO[150].base + (((pfId)>>1) * IRO[150].m1) + (((pfId)&1) * \
- IRO[150].m2))
-#define CSTORM_FINAL_CLEANUP_COMPLETE_OFFSET(funcId) \
- (IRO[156].base + ((funcId) * IRO[156].m1))
-#define CSTORM_FUNC_EN_OFFSET(funcId) \
- (IRO[146].base + ((funcId) * IRO[146].m1))
-#define CSTORM_FUNCTION_MODE_OFFSET (IRO[153].base)
-#define CSTORM_IGU_MODE_OFFSET (IRO[154].base)
-#define CSTORM_ISCSI_CQ_SIZE_OFFSET(pfId) \
- (IRO[311].base + ((pfId) * IRO[311].m1))
-#define CSTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \
- (IRO[312].base + ((pfId) * IRO[312].m1))
- #define CSTORM_ISCSI_EQ_CONS_OFFSET(pfId, iscsiEqId) \
- (IRO[304].base + ((pfId) * IRO[304].m1) + ((iscsiEqId) * \
- IRO[304].m2))
- #define CSTORM_ISCSI_EQ_NEXT_EQE_ADDR_OFFSET(pfId, iscsiEqId) \
- (IRO[306].base + ((pfId) * IRO[306].m1) + ((iscsiEqId) * \
- IRO[306].m2))
- #define CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_OFFSET(pfId, iscsiEqId) \
- (IRO[305].base + ((pfId) * IRO[305].m1) + ((iscsiEqId) * \
- IRO[305].m2))
- #define \
- CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_VALID_OFFSET(pfId, iscsiEqId) \
- (IRO[307].base + ((pfId) * IRO[307].m1) + ((iscsiEqId) * \
- IRO[307].m2))
- #define CSTORM_ISCSI_EQ_PROD_OFFSET(pfId, iscsiEqId) \
- (IRO[303].base + ((pfId) * IRO[303].m1) + ((iscsiEqId) * \
- IRO[303].m2))
- #define CSTORM_ISCSI_EQ_SB_INDEX_OFFSET(pfId, iscsiEqId) \
- (IRO[309].base + ((pfId) * IRO[309].m1) + ((iscsiEqId) * \
- IRO[309].m2))
- #define CSTORM_ISCSI_EQ_SB_NUM_OFFSET(pfId, iscsiEqId) \
- (IRO[308].base + ((pfId) * IRO[308].m1) + ((iscsiEqId) * \
- IRO[308].m2))
-#define CSTORM_ISCSI_HQ_SIZE_OFFSET(pfId) \
- (IRO[310].base + ((pfId) * IRO[310].m1))
-#define CSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
- (IRO[302].base + ((pfId) * IRO[302].m1))
-#define CSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
- (IRO[301].base + ((pfId) * IRO[301].m1))
-#define CSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
- (IRO[300].base + ((pfId) * IRO[300].m1))
-#define CSTORM_PATH_ID_OFFSET (IRO[159].base)
-#define CSTORM_SP_STATUS_BLOCK_DATA_OFFSET(pfId) \
- (IRO[137].base + ((pfId) * IRO[137].m1))
-#define CSTORM_SP_STATUS_BLOCK_OFFSET(pfId) \
- (IRO[136].base + ((pfId) * IRO[136].m1))
-#define CSTORM_SP_STATUS_BLOCK_SIZE (IRO[136].size)
-#define CSTORM_SP_SYNC_BLOCK_OFFSET(pfId) \
- (IRO[138].base + ((pfId) * IRO[138].m1))
-#define CSTORM_SP_SYNC_BLOCK_SIZE (IRO[138].size)
-#define CSTORM_STATS_FLAGS_OFFSET(pfId) \
- (IRO[143].base + ((pfId) * IRO[143].m1))
-#define CSTORM_STATUS_BLOCK_DATA_OFFSET(sbId) \
- (IRO[129].base + ((sbId) * IRO[129].m1))
-#define CSTORM_STATUS_BLOCK_OFFSET(sbId) \
- (IRO[128].base + ((sbId) * IRO[128].m1))
-#define CSTORM_STATUS_BLOCK_SIZE (IRO[128].size)
-#define CSTORM_SYNC_BLOCK_OFFSET(sbId) \
- (IRO[132].base + ((sbId) * IRO[132].m1))
-#define CSTORM_SYNC_BLOCK_SIZE (IRO[132].size)
-#define CSTORM_VF_PF_CHANNEL_STATE_OFFSET(vfId) \
- (IRO[151].base + ((vfId) * IRO[151].m1))
-#define CSTORM_VF_PF_CHANNEL_VALID_OFFSET(vfId) \
- (IRO[152].base + ((vfId) * IRO[152].m1))
-#define CSTORM_VF_TO_PF_OFFSET(funcId) \
- (IRO[147].base + ((funcId) * IRO[147].m1))
-#define TSTORM_ACCEPT_CLASSIFY_FAILED_OFFSET (IRO[199].base)
-#define TSTORM_APPROXIMATE_MATCH_MULTICAST_FILTERING_OFFSET(pfId) \
- (IRO[198].base + ((pfId) * IRO[198].m1))
-#define TSTORM_ASSERT_LIST_INDEX_OFFSET (IRO[99].base)
-#define TSTORM_ASSERT_LIST_OFFSET(assertListEntry) \
- (IRO[98].base + ((assertListEntry) * IRO[98].m1))
- #define TSTORM_CLIENT_CONFIG_OFFSET(portId, clientId) \
- (IRO[197].base + ((portId) * IRO[197].m1) + ((clientId) * \
- IRO[197].m2))
-#define TSTORM_COMMON_SAFC_WORKAROUND_ENABLE_OFFSET (IRO[104].base)
-#define TSTORM_COMMON_SAFC_WORKAROUND_TIMEOUT_10USEC_OFFSET \
- (IRO[105].base)
-#define TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(pfId) \
- (IRO[96].base + ((pfId) * IRO[96].m1))
-#define TSTORM_FUNC_EN_OFFSET(funcId) \
- (IRO[101].base + ((funcId) * IRO[101].m1))
-#define TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(pfId) \
- (IRO[195].base + ((pfId) * IRO[195].m1))
-#define TSTORM_FUNCTION_MODE_OFFSET (IRO[103].base)
-#define TSTORM_INDIRECTION_TABLE_OFFSET(pfId) \
- (IRO[91].base + ((pfId) * IRO[91].m1))
-#define TSTORM_INDIRECTION_TABLE_SIZE (IRO[91].size)
- #define \
- TSTORM_ISCSI_CONN_BUF_PBL_OFFSET(pfId, iscsiConBufPblEntry) \
- (IRO[260].base + ((pfId) * IRO[260].m1) + ((iscsiConBufPblEntry) \
- * IRO[260].m2))
-#define TSTORM_ISCSI_ERROR_BITMAP_OFFSET(pfId) \
- (IRO[264].base + ((pfId) * IRO[264].m1))
-#define TSTORM_ISCSI_L2_ISCSI_OOO_CID_TABLE_OFFSET(pfId) \
- (IRO[265].base + ((pfId) * IRO[265].m1))
-#define TSTORM_ISCSI_L2_ISCSI_OOO_CLIENT_ID_TABLE_OFFSET(pfId) \
- (IRO[266].base + ((pfId) * IRO[266].m1))
-#define TSTORM_ISCSI_L2_ISCSI_OOO_PROD_OFFSET(pfId) \
- (IRO[267].base + ((pfId) * IRO[267].m1))
-#define TSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
- (IRO[263].base + ((pfId) * IRO[263].m1))
-#define TSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
- (IRO[262].base + ((pfId) * IRO[262].m1))
-#define TSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
- (IRO[261].base + ((pfId) * IRO[261].m1))
-#define TSTORM_ISCSI_RQ_SIZE_OFFSET(pfId) \
- (IRO[259].base + ((pfId) * IRO[259].m1))
-#define TSTORM_ISCSI_TCP_LOCAL_ADV_WND_OFFSET(pfId) \
- (IRO[269].base + ((pfId) * IRO[269].m1))
-#define TSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(pfId) \
- (IRO[256].base + ((pfId) * IRO[256].m1))
-#define TSTORM_ISCSI_TCP_VARS_LSB_LOCAL_MAC_ADDR_OFFSET(pfId) \
- (IRO[257].base + ((pfId) * IRO[257].m1))
-#define TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(pfId) \
- (IRO[258].base + ((pfId) * IRO[258].m1))
-#define TSTORM_MAC_FILTER_CONFIG_OFFSET(pfId) \
- (IRO[196].base + ((pfId) * IRO[196].m1))
- #define TSTORM_PER_COUNTER_ID_STATS_OFFSET(portId, tStatCntId) \
- (IRO[100].base + ((portId) * IRO[100].m1) + ((tStatCntId) * \
- IRO[100].m2))
-#define TSTORM_STATS_FLAGS_OFFSET(pfId) \
- (IRO[95].base + ((pfId) * IRO[95].m1))
-#define TSTORM_TCP_MAX_CWND_OFFSET(pfId) \
- (IRO[211].base + ((pfId) * IRO[211].m1))
-#define TSTORM_VF_TO_PF_OFFSET(funcId) \
- (IRO[102].base + ((funcId) * IRO[102].m1))
-#define USTORM_AGG_DATA_OFFSET (IRO[201].base)
-#define USTORM_AGG_DATA_SIZE (IRO[201].size)
-#define USTORM_ASSERT_LIST_INDEX_OFFSET (IRO[170].base)
-#define USTORM_ASSERT_LIST_OFFSET(assertListEntry) \
- (IRO[169].base + ((assertListEntry) * IRO[169].m1))
-#define USTORM_ETH_PAUSE_ENABLED_OFFSET(portId) \
- (IRO[178].base + ((portId) * IRO[178].m1))
-#define USTORM_ETH_STATS_QUERY_ADDR_OFFSET(pfId) \
- (IRO[172].base + ((pfId) * IRO[172].m1))
-#define USTORM_FCOE_EQ_PROD_OFFSET(pfId) \
- (IRO[313].base + ((pfId) * IRO[313].m1))
-#define USTORM_FUNC_EN_OFFSET(funcId) \
- (IRO[174].base + ((funcId) * IRO[174].m1))
-#define USTORM_FUNCTION_MODE_OFFSET (IRO[177].base)
-#define USTORM_ISCSI_CQ_SIZE_OFFSET(pfId) \
- (IRO[277].base + ((pfId) * IRO[277].m1))
-#define USTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \
- (IRO[278].base + ((pfId) * IRO[278].m1))
-#define USTORM_ISCSI_ERROR_BITMAP_OFFSET(pfId) \
- (IRO[282].base + ((pfId) * IRO[282].m1))
-#define USTORM_ISCSI_GLOBAL_BUF_PHYS_ADDR_OFFSET(pfId) \
- (IRO[279].base + ((pfId) * IRO[279].m1))
-#define USTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
- (IRO[275].base + ((pfId) * IRO[275].m1))
-#define USTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
- (IRO[274].base + ((pfId) * IRO[274].m1))
-#define USTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
- (IRO[273].base + ((pfId) * IRO[273].m1))
-#define USTORM_ISCSI_R2TQ_SIZE_OFFSET(pfId) \
- (IRO[276].base + ((pfId) * IRO[276].m1))
-#define USTORM_ISCSI_RQ_BUFFER_SIZE_OFFSET(pfId) \
- (IRO[280].base + ((pfId) * IRO[280].m1))
-#define USTORM_ISCSI_RQ_SIZE_OFFSET(pfId) \
- (IRO[281].base + ((pfId) * IRO[281].m1))
-#define USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(pfId) \
- (IRO[176].base + ((pfId) * IRO[176].m1))
- #define USTORM_PER_COUNTER_ID_STATS_OFFSET(portId, uStatCntId) \
- (IRO[173].base + ((portId) * IRO[173].m1) + ((uStatCntId) * \
- IRO[173].m2))
- #define USTORM_RX_PRODS_E1X_OFFSET(portId, clientId) \
- (IRO[204].base + ((portId) * IRO[204].m1) + ((clientId) * \
- IRO[204].m2))
-#define USTORM_RX_PRODS_E2_OFFSET(qzoneId) \
- (IRO[205].base + ((qzoneId) * IRO[205].m1))
-#define USTORM_STATS_FLAGS_OFFSET(pfId) \
- (IRO[171].base + ((pfId) * IRO[171].m1))
-#define USTORM_TPA_BTR_OFFSET (IRO[202].base)
-#define USTORM_TPA_BTR_SIZE (IRO[202].size)
-#define USTORM_VF_TO_PF_OFFSET(funcId) \
- (IRO[175].base + ((funcId) * IRO[175].m1))
-#define XSTORM_AGG_INT_FINAL_CLEANUP_COMP_TYPE (IRO[59].base)
-#define XSTORM_AGG_INT_FINAL_CLEANUP_INDEX (IRO[58].base)
-#define XSTORM_ASSERT_LIST_INDEX_OFFSET (IRO[54].base)
-#define XSTORM_ASSERT_LIST_OFFSET(assertListEntry) \
- (IRO[53].base + ((assertListEntry) * IRO[53].m1))
-#define XSTORM_CMNG_PER_PORT_VARS_OFFSET(portId) \
- (IRO[47].base + ((portId) * IRO[47].m1))
-#define XSTORM_E1HOV_OFFSET(pfId) \
- (IRO[55].base + ((pfId) * IRO[55].m1))
-#define XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(pfId) \
- (IRO[45].base + ((pfId) * IRO[45].m1))
-#define XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(pfId) \
- (IRO[49].base + ((pfId) * IRO[49].m1))
-#define XSTORM_FUNC_EN_OFFSET(funcId) \
- (IRO[51].base + ((funcId) * IRO[51].m1))
-#define XSTORM_FUNCTION_MODE_OFFSET (IRO[56].base)
-#define XSTORM_ISCSI_HQ_SIZE_OFFSET(pfId) \
- (IRO[290].base + ((pfId) * IRO[290].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR0_OFFSET(pfId) \
- (IRO[293].base + ((pfId) * IRO[293].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR1_OFFSET(pfId) \
- (IRO[294].base + ((pfId) * IRO[294].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR2_OFFSET(pfId) \
- (IRO[295].base + ((pfId) * IRO[295].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR3_OFFSET(pfId) \
- (IRO[296].base + ((pfId) * IRO[296].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR4_OFFSET(pfId) \
- (IRO[297].base + ((pfId) * IRO[297].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR5_OFFSET(pfId) \
- (IRO[298].base + ((pfId) * IRO[298].m1))
-#define XSTORM_ISCSI_LOCAL_VLAN_OFFSET(pfId) \
- (IRO[299].base + ((pfId) * IRO[299].m1))
-#define XSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
- (IRO[289].base + ((pfId) * IRO[289].m1))
-#define XSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
- (IRO[288].base + ((pfId) * IRO[288].m1))
-#define XSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
- (IRO[287].base + ((pfId) * IRO[287].m1))
-#define XSTORM_ISCSI_R2TQ_SIZE_OFFSET(pfId) \
- (IRO[292].base + ((pfId) * IRO[292].m1))
-#define XSTORM_ISCSI_SQ_SIZE_OFFSET(pfId) \
- (IRO[291].base + ((pfId) * IRO[291].m1))
-#define XSTORM_ISCSI_TCP_VARS_ADV_WND_SCL_OFFSET(pfId) \
- (IRO[286].base + ((pfId) * IRO[286].m1))
-#define XSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(pfId) \
- (IRO[285].base + ((pfId) * IRO[285].m1))
-#define XSTORM_ISCSI_TCP_VARS_TOS_OFFSET(pfId) \
- (IRO[284].base + ((pfId) * IRO[284].m1))
-#define XSTORM_ISCSI_TCP_VARS_TTL_OFFSET(pfId) \
- (IRO[283].base + ((pfId) * IRO[283].m1))
-#define XSTORM_PATH_ID_OFFSET (IRO[65].base)
- #define XSTORM_PER_COUNTER_ID_STATS_OFFSET(portId, xStatCntId) \
- (IRO[50].base + ((portId) * IRO[50].m1) + ((xStatCntId) * \
- IRO[50].m2))
-#define XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(pfId) \
- (IRO[48].base + ((pfId) * IRO[48].m1))
-#define XSTORM_SPQ_DATA_OFFSET(funcId) \
- (IRO[32].base + ((funcId) * IRO[32].m1))
-#define XSTORM_SPQ_DATA_SIZE (IRO[32].size)
-#define XSTORM_SPQ_PAGE_BASE_OFFSET(funcId) \
- (IRO[30].base + ((funcId) * IRO[30].m1))
-#define XSTORM_SPQ_PROD_OFFSET(funcId) \
- (IRO[31].base + ((funcId) * IRO[31].m1))
-#define XSTORM_STATS_FLAGS_OFFSET(pfId) \
- (IRO[43].base + ((pfId) * IRO[43].m1))
-#define XSTORM_TCP_GLOBAL_DEL_ACK_COUNTER_ENABLED_OFFSET(portId) \
- (IRO[206].base + ((portId) * IRO[206].m1))
-#define XSTORM_TCP_GLOBAL_DEL_ACK_COUNTER_MAX_COUNT_OFFSET(portId) \
- (IRO[207].base + ((portId) * IRO[207].m1))
-#define XSTORM_TCP_TX_SWS_TIMER_VAL_OFFSET(pfId) \
- (IRO[209].base + (((pfId)>>1) * IRO[209].m1) + (((pfId)&1) * \
- IRO[209].m2))
-#define XSTORM_VF_TO_PF_OFFSET(funcId) \
- (IRO[52].base + ((funcId) * IRO[52].m1))
-#define COMMON_ASM_INVALID_ASSERT_OPCODE 0x0
-
-/* RSS hash types */
-#define DEFAULT_HASH_TYPE 0
-#define IPV4_HASH_TYPE 1
-#define TCP_IPV4_HASH_TYPE 2
-#define IPV6_HASH_TYPE 3
-#define TCP_IPV6_HASH_TYPE 4
-#define VLAN_PRI_HASH_TYPE 5
-#define E1HOV_PRI_HASH_TYPE 6
-#define DSCP_HASH_TYPE 7
-
-
-/* Ethernet Ring parameters */
-#define X_ETH_LOCAL_RING_SIZE 13
-#define FIRST_BD_IN_PKT 0
-#define PARSE_BD_INDEX 1
-#define NUM_OF_ETH_BDS_IN_PAGE ((PAGE_SIZE)/(STRUCT_SIZE(eth_tx_bd)/8))
-#define U_ETH_NUM_OF_SGES_TO_FETCH 8
-#define U_ETH_MAX_SGES_FOR_PACKET 3
-
-/*Tx params*/
-#define X_ETH_NO_VLAN 0
-#define X_ETH_OUTBAND_VLAN 1
-#define X_ETH_INBAND_VLAN 2
-/* Rx ring params */
-#define U_ETH_LOCAL_BD_RING_SIZE 8
-#define U_ETH_LOCAL_SGE_RING_SIZE 10
-#define U_ETH_SGL_SIZE 8
- /* The fw will padd the buffer with this value, so the IP header \
- will be align to 4 Byte */
-#define IP_HEADER_ALIGNMENT_PADDING 2
-
-#define U_ETH_SGES_PER_PAGE_INVERSE_MASK \
- (0xFFFF - ((PAGE_SIZE/((STRUCT_SIZE(eth_rx_sge))/8))-1))
-
-#define TU_ETH_CQES_PER_PAGE (PAGE_SIZE/(STRUCT_SIZE(eth_rx_cqe)/8))
-#define U_ETH_BDS_PER_PAGE (PAGE_SIZE/(STRUCT_SIZE(eth_rx_bd)/8))
-#define U_ETH_SGES_PER_PAGE (PAGE_SIZE/(STRUCT_SIZE(eth_rx_sge)/8))
-
-#define U_ETH_BDS_PER_PAGE_MASK (U_ETH_BDS_PER_PAGE-1)
-#define U_ETH_CQE_PER_PAGE_MASK (TU_ETH_CQES_PER_PAGE-1)
-#define U_ETH_SGES_PER_PAGE_MASK (U_ETH_SGES_PER_PAGE-1)
-
-#define U_ETH_UNDEFINED_Q 0xFF
-
-/* values of command IDs in the ramrod message */
-#define RAMROD_CMD_ID_ETH_UNUSED 0
-#define RAMROD_CMD_ID_ETH_CLIENT_SETUP 1
-#define RAMROD_CMD_ID_ETH_UPDATE 2
-#define RAMROD_CMD_ID_ETH_HALT 3
-#define RAMROD_CMD_ID_ETH_FORWARD_SETUP 4
-#define RAMROD_CMD_ID_ETH_ACTIVATE 5
-#define RAMROD_CMD_ID_ETH_DEACTIVATE 6
-#define RAMROD_CMD_ID_ETH_EMPTY 7
-#define RAMROD_CMD_ID_ETH_TERMINATE 8
-
-/* command values for set mac command */
-#define T_ETH_MAC_COMMAND_SET 0
-#define T_ETH_MAC_COMMAND_INVALIDATE 1
-
-#define T_ETH_INDIRECTION_TABLE_SIZE 128
-
-/*The CRC32 seed, that is used for the hash(reduction) multicast address */
-#define T_ETH_CRC32_HASH_SEED 0x00000000
-
-/* Maximal L2 clients supported */
-#define ETH_MAX_RX_CLIENTS_E1 18
-#define ETH_MAX_RX_CLIENTS_E1H 28
-
-#define MAX_STAT_COUNTER_ID ETH_MAX_RX_CLIENTS_E1H
-
-/* Maximal aggregation queues supported */
-#define ETH_MAX_AGGREGATION_QUEUES_E1 32
-#define ETH_MAX_AGGREGATION_QUEUES_E1H 64
-
-/* ETH RSS modes */
-#define ETH_RSS_MODE_DISABLED 0
-#define ETH_RSS_MODE_REGULAR 1
-#define ETH_RSS_MODE_VLAN_PRI 2
-#define ETH_RSS_MODE_E1HOV_PRI 3
-#define ETH_RSS_MODE_IP_DSCP 4
-#define ETH_RSS_MODE_E2_INTEG 5
-
-
-/* ETH vlan filtering modes */
-#define ETH_VLAN_FILTER_ANY_VLAN 0 /* Don't filter by vlan */
-#define ETH_VLAN_FILTER_SPECIFIC_VLAN \
- 1 /* Only the vlan_id is allowed */
-#define ETH_VLAN_FILTER_CLASSIFY \
- 2 /* vlan will be added to CAM for classification */
-
-/* Fast path CQE selection */
-#define ETH_FP_CQE_REGULAR 0
-#define ETH_FP_CQE_SGL 1
-#define ETH_FP_CQE_RAW 2
-
-
-/**
-* This file defines HSI constants common to all microcode flows
-*/
-
-/* Connection types */
-#define ETH_CONNECTION_TYPE 0
-#define TOE_CONNECTION_TYPE 1
-#define RDMA_CONNECTION_TYPE 2
-#define ISCSI_CONNECTION_TYPE 3
-#define FCOE_CONNECTION_TYPE 4
-#define RESERVED_CONNECTION_TYPE_0 5
-#define RESERVED_CONNECTION_TYPE_1 6
-#define RESERVED_CONNECTION_TYPE_2 7
-#define NONE_CONNECTION_TYPE 8
-
-
-#define PROTOCOL_STATE_BIT_OFFSET 6
-
-#define ETH_STATE (ETH_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET)
-#define TOE_STATE (TOE_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET)
-#define RDMA_STATE (RDMA_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET)
-
-/* values of command IDs in the ramrod message */
-#define RAMROD_CMD_ID_COMMON_FUNCTION_START 1
-#define RAMROD_CMD_ID_COMMON_FUNCTION_STOP 2
-#define RAMROD_CMD_ID_COMMON_CFC_DEL 3
-#define RAMROD_CMD_ID_COMMON_CFC_DEL_WB 4
-#define RAMROD_CMD_ID_COMMON_SET_MAC 5
-#define RAMROD_CMD_ID_COMMON_STAT_QUERY 6
-#define RAMROD_CMD_ID_COMMON_STOP_TRAFFIC 7
-#define RAMROD_CMD_ID_COMMON_START_TRAFFIC 8
-
-/* microcode fixed page page size 4K (chains and ring segments) */
-#define MC_PAGE_SIZE 4096
-
-
-/* Host coalescing constants */
-#define HC_IGU_BC_MODE 0
-#define HC_IGU_NBC_MODE 1
-/* Host coalescing constants. E1 includes E1H as well */
-
-/* Number of indices per slow-path SB */
-#define HC_SP_SB_MAX_INDICES 16
-
-/* Number of indices per SB */
-#define HC_SB_MAX_INDICES_E1X 8
-#define HC_SB_MAX_INDICES_E2 8
-
-#define HC_SB_MAX_SB_E1X 32
-#define HC_SB_MAX_SB_E2 136
-
-#define HC_SP_SB_ID 0xde
-
-#define HC_REGULAR_SEGMENT 0
-#define HC_DEFAULT_SEGMENT 1
-#define HC_SB_MAX_SM 2
-
-#define HC_SB_MAX_DYNAMIC_INDICES 4
-#define HC_FUNCTION_DISABLED 0xff
-/* used by the driver to get the SB offset */
-#define USTORM_ID 0
-#define CSTORM_ID 1
-#define XSTORM_ID 2
-#define TSTORM_ID 3
-#define ATTENTION_ID 4
-
-/* max number of slow path commands per port */
-#define MAX_RAMRODS_PER_PORT 8
-
-/* values for RX ETH CQE type field */
-#define RX_ETH_CQE_TYPE_ETH_FASTPATH 0
-#define RX_ETH_CQE_TYPE_ETH_RAMROD 1
-
-
-/**** DEFINES FOR TIMERS/CLOCKS RESOLUTIONS ****/
-
-#define TIMERS_TICK_SIZE_CHIP (1e-3)
-
-#define TSEMI_CLK1_RESUL_CHIP (1e-3)
-
-#define XSEMI_CLK1_RESUL_CHIP (1e-3)
-
-#define SDM_TIMER_TICK_RESUL_CHIP (4*(1e-6))
-
-/**** END DEFINES FOR TIMERS/CLOCKS RESOLUTIONS ****/
-
-#define XSTORM_IP_ID_ROLL_HALF 0x8000
-#define XSTORM_IP_ID_ROLL_ALL 0
-
-#define FW_LOG_LIST_SIZE 50
-
-#define NUM_OF_PROTOCOLS 4
-#define NUM_OF_SAFC_BITS 16
-#define MAX_COS_NUMBER 4
-
-#define FAIRNESS_COS_WRR_MODE 0
-#define FAIRNESS_COS_ETS_MODE 1
-
-
-/* Priority Flow Control (PFC) */
-#define MAX_PFC_PRIORITIES 8
-#define MAX_PFC_TRAFFIC_TYPES 8
-
-/* Available Traffic Types for Link Layer Flow Control */
-#define LLFC_TRAFFIC_TYPE_NW 0
-#define LLFC_TRAFFIC_TYPE_FCOE 1
-#define LLFC_TRAFFIC_TYPE_ISCSI 2
- /***************** START OF E2 INTEGRATION \
- CODE***************************************/
-#define LLFC_TRAFFIC_TYPE_NW_COS1_E2INTEG 3
- /***************** END OF E2 INTEGRATION \
- CODE***************************************/
-#define LLFC_TRAFFIC_TYPE_MAX 4
-
- /* used by array traffic_type_to_priority[] to mark traffic type \
- that is not mapped to priority*/
-#define LLFC_TRAFFIC_TYPE_TO_PRIORITY_UNMAPPED 0xFF
-
-#define LLFC_MODE_NONE 0
-#define LLFC_MODE_PFC 1
-#define LLFC_MODE_SAFC 2
-
-#define DCB_DISABLED 0
-#define DCB_ENABLED 1
-
-#define UNKNOWN_ADDRESS 0
-#define UNICAST_ADDRESS 1
-#define MULTICAST_ADDRESS 2
-#define BROADCAST_ADDRESS 3
-
-#define SINGLE_FUNCTION 0
-#define MULTI_FUNCTION_SD 1
-#define MULTI_FUNCTION_SI 2
-
-#define IP_V4 0
-#define IP_V6 1
-
-
-#define C_ERES_PER_PAGE \
- (PAGE_SIZE / BITS_TO_BYTES(STRUCT_SIZE(event_ring_elem)))
-#define C_ERE_PER_PAGE_MASK (C_ERES_PER_PAGE - 1)
-
-#define EVENT_RING_OPCODE_VF_PF_CHANNEL 0
-#define EVENT_RING_OPCODE_FUNCTION_START 1
-#define EVENT_RING_OPCODE_FUNCTION_STOP 2
-#define EVENT_RING_OPCODE_CFC_DEL 3
-#define EVENT_RING_OPCODE_CFC_DEL_WB 4
-#define EVENT_RING_OPCODE_SET_MAC 5
-#define EVENT_RING_OPCODE_STAT_QUERY 6
-#define EVENT_RING_OPCODE_STOP_TRAFFIC 7
-#define EVENT_RING_OPCODE_START_TRAFFIC 8
-#define EVENT_RING_OPCODE_FORWARD_SETUP 9
-
-#define VF_PF_CHANNEL_STATE_READY 0
-#define VF_PF_CHANNEL_STATE_WAITING_FOR_ACK 1
-
-#define VF_PF_CHANNEL_STATE_MAX_NUMBER 2
-
-
-#endif /* BNX2X_FW_DEFS_H */
diff --git a/drivers/net/bnx2x/bnx2x_hsi.h b/drivers/net/bnx2x/bnx2x_hsi.h
deleted file mode 100644
index cdf19fe7c7f6..000000000000
--- a/drivers/net/bnx2x/bnx2x_hsi.h
+++ /dev/null
@@ -1,3732 +0,0 @@
-/* bnx2x_hsi.h: Broadcom Everest network driver.
- *
- * Copyright (c) 2007-2011 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation.
- */
-#ifndef BNX2X_HSI_H
-#define BNX2X_HSI_H
-
-#include "bnx2x_fw_defs.h"
-
-#define FW_ENCODE_32BIT_PATTERN 0x1e1e1e1e
-
-struct license_key {
- u32 reserved[6];
-
- u32 max_iscsi_conn;
-#define BNX2X_MAX_ISCSI_TRGT_CONN_MASK 0xFFFF
-#define BNX2X_MAX_ISCSI_TRGT_CONN_SHIFT 0
-#define BNX2X_MAX_ISCSI_INIT_CONN_MASK 0xFFFF0000
-#define BNX2X_MAX_ISCSI_INIT_CONN_SHIFT 16
-
- u32 reserved_a;
-
- u32 max_fcoe_conn;
-#define BNX2X_MAX_FCOE_TRGT_CONN_MASK 0xFFFF
-#define BNX2X_MAX_FCOE_TRGT_CONN_SHIFT 0
-#define BNX2X_MAX_FCOE_INIT_CONN_MASK 0xFFFF0000
-#define BNX2X_MAX_FCOE_INIT_CONN_SHIFT 16
-
- u32 reserved_b[4];
-};
-
-#define PORT_0 0
-#define PORT_1 1
-#define PORT_MAX 2
-
-/****************************************************************************
- * Shared HW configuration *
- ****************************************************************************/
-struct shared_hw_cfg { /* NVRAM Offset */
- /* Up to 16 bytes of NULL-terminated string */
- u8 part_num[16]; /* 0x104 */
-
- u32 config; /* 0x114 */
-#define SHARED_HW_CFG_MDIO_VOLTAGE_MASK 0x00000001
-#define SHARED_HW_CFG_MDIO_VOLTAGE_SHIFT 0
-#define SHARED_HW_CFG_MDIO_VOLTAGE_1_2V 0x00000000
-#define SHARED_HW_CFG_MDIO_VOLTAGE_2_5V 0x00000001
-#define SHARED_HW_CFG_MCP_RST_ON_CORE_RST_EN 0x00000002
-
-#define SHARED_HW_CFG_PORT_SWAP 0x00000004
-
-#define SHARED_HW_CFG_BEACON_WOL_EN 0x00000008
-
-#define SHARED_HW_CFG_MFW_SELECT_MASK 0x00000700
-#define SHARED_HW_CFG_MFW_SELECT_SHIFT 8
- /* Whatever MFW found in NVM
- (if multiple found, priority order is: NC-SI, UMP, IPMI) */
-#define SHARED_HW_CFG_MFW_SELECT_DEFAULT 0x00000000
-#define SHARED_HW_CFG_MFW_SELECT_NC_SI 0x00000100
-#define SHARED_HW_CFG_MFW_SELECT_UMP 0x00000200
-#define SHARED_HW_CFG_MFW_SELECT_IPMI 0x00000300
- /* Use SPIO4 as an arbiter between: 0-NC_SI, 1-IPMI
- (can only be used when an add-in board, not BMC, pulls-down SPIO4) */
-#define SHARED_HW_CFG_MFW_SELECT_SPIO4_NC_SI_IPMI 0x00000400
- /* Use SPIO4 as an arbiter between: 0-UMP, 1-IPMI
- (can only be used when an add-in board, not BMC, pulls-down SPIO4) */
-#define SHARED_HW_CFG_MFW_SELECT_SPIO4_UMP_IPMI 0x00000500
- /* Use SPIO4 as an arbiter between: 0-NC-SI, 1-UMP
- (can only be used when an add-in board, not BMC, pulls-down SPIO4) */
-#define SHARED_HW_CFG_MFW_SELECT_SPIO4_NC_SI_UMP 0x00000600
-
-#define SHARED_HW_CFG_LED_MODE_MASK 0x000f0000
-#define SHARED_HW_CFG_LED_MODE_SHIFT 16
-#define SHARED_HW_CFG_LED_MAC1 0x00000000
-#define SHARED_HW_CFG_LED_PHY1 0x00010000
-#define SHARED_HW_CFG_LED_PHY2 0x00020000
-#define SHARED_HW_CFG_LED_PHY3 0x00030000
-#define SHARED_HW_CFG_LED_MAC2 0x00040000
-#define SHARED_HW_CFG_LED_PHY4 0x00050000
-#define SHARED_HW_CFG_LED_PHY5 0x00060000
-#define SHARED_HW_CFG_LED_PHY6 0x00070000
-#define SHARED_HW_CFG_LED_MAC3 0x00080000
-#define SHARED_HW_CFG_LED_PHY7 0x00090000
-#define SHARED_HW_CFG_LED_PHY9 0x000a0000
-#define SHARED_HW_CFG_LED_PHY11 0x000b0000
-#define SHARED_HW_CFG_LED_MAC4 0x000c0000
-#define SHARED_HW_CFG_LED_PHY8 0x000d0000
-#define SHARED_HW_CFG_LED_EXTPHY1 0x000e0000
-
-
-#define SHARED_HW_CFG_AN_ENABLE_MASK 0x3f000000
-#define SHARED_HW_CFG_AN_ENABLE_SHIFT 24
-#define SHARED_HW_CFG_AN_ENABLE_CL37 0x01000000
-#define SHARED_HW_CFG_AN_ENABLE_CL73 0x02000000
-#define SHARED_HW_CFG_AN_ENABLE_BAM 0x04000000
-#define SHARED_HW_CFG_AN_ENABLE_PARALLEL_DETECTION 0x08000000
-#define SHARED_HW_CFG_AN_EN_SGMII_FIBER_AUTO_DETECT 0x10000000
-#define SHARED_HW_CFG_AN_ENABLE_REMOTE_PHY 0x20000000
-
- u32 config2; /* 0x118 */
- /* one time auto detect grace period (in sec) */
-#define SHARED_HW_CFG_GRACE_PERIOD_MASK 0x000000ff
-#define SHARED_HW_CFG_GRACE_PERIOD_SHIFT 0
-
-#define SHARED_HW_CFG_PCIE_GEN2_ENABLED 0x00000100
-
- /* The default value for the core clock is 250MHz and it is
- achieved by setting the clock change to 4 */
-#define SHARED_HW_CFG_CLOCK_CHANGE_MASK 0x00000e00
-#define SHARED_HW_CFG_CLOCK_CHANGE_SHIFT 9
-
-#define SHARED_HW_CFG_SMBUS_TIMING_100KHZ 0x00000000
-#define SHARED_HW_CFG_SMBUS_TIMING_400KHZ 0x00001000
-
-#define SHARED_HW_CFG_HIDE_PORT1 0x00002000
-
- /* The fan failure mechanism is usually related to the PHY type
- since the power consumption of the board is determined by the PHY.
- Currently, fan is required for most designs with SFX7101, BCM8727
- and BCM8481. If a fan is not required for a board which uses one
- of those PHYs, this field should be set to "Disabled". If a fan is
- required for a different PHY type, this option should be set to
- "Enabled".
- The fan failure indication is expected on
- SPIO5 */
-#define SHARED_HW_CFG_FAN_FAILURE_MASK 0x00180000
-#define SHARED_HW_CFG_FAN_FAILURE_SHIFT 19
-#define SHARED_HW_CFG_FAN_FAILURE_PHY_TYPE 0x00000000
-#define SHARED_HW_CFG_FAN_FAILURE_DISABLED 0x00080000
-#define SHARED_HW_CFG_FAN_FAILURE_ENABLED 0x00100000
-
- /* Set the MDC/MDIO access for the first external phy */
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS1_MASK 0x1C000000
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS1_SHIFT 26
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS1_PHY_TYPE 0x00000000
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS1_EMAC0 0x04000000
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS1_EMAC1 0x08000000
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS1_BOTH 0x0c000000
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS1_SWAPPED 0x10000000
-
- /* Set the MDC/MDIO access for the second external phy */
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS2_MASK 0xE0000000
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS2_SHIFT 29
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS2_PHY_TYPE 0x00000000
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS2_EMAC0 0x20000000
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS2_EMAC1 0x40000000
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS2_BOTH 0x60000000
-#define SHARED_HW_CFG_MDC_MDIO_ACCESS2_SWAPPED 0x80000000
- u32 power_dissipated; /* 0x11c */
-#define SHARED_HW_CFG_POWER_DIS_CMN_MASK 0xff000000
-#define SHARED_HW_CFG_POWER_DIS_CMN_SHIFT 24
-
-#define SHARED_HW_CFG_POWER_MGNT_SCALE_MASK 0x00ff0000
-#define SHARED_HW_CFG_POWER_MGNT_SCALE_SHIFT 16
-#define SHARED_HW_CFG_POWER_MGNT_UNKNOWN_SCALE 0x00000000
-#define SHARED_HW_CFG_POWER_MGNT_DOT_1_WATT 0x00010000
-#define SHARED_HW_CFG_POWER_MGNT_DOT_01_WATT 0x00020000
-#define SHARED_HW_CFG_POWER_MGNT_DOT_001_WATT 0x00030000
-
- u32 ump_nc_si_config; /* 0x120 */
-#define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_MASK 0x00000003
-#define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_SHIFT 0
-#define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_MAC 0x00000000
-#define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_PHY 0x00000001
-#define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_MII 0x00000000
-#define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_RMII 0x00000002
-
-#define SHARED_HW_CFG_UMP_NC_SI_NUM_DEVS_MASK 0x00000f00
-#define SHARED_HW_CFG_UMP_NC_SI_NUM_DEVS_SHIFT 8
-
-#define SHARED_HW_CFG_UMP_NC_SI_EXT_PHY_TYPE_MASK 0x00ff0000
-#define SHARED_HW_CFG_UMP_NC_SI_EXT_PHY_TYPE_SHIFT 16
-#define SHARED_HW_CFG_UMP_NC_SI_EXT_PHY_TYPE_NONE 0x00000000
-#define SHARED_HW_CFG_UMP_NC_SI_EXT_PHY_TYPE_BCM5221 0x00010000
-
- u32 board; /* 0x124 */
-#define SHARED_HW_CFG_BOARD_REV_MASK 0x00FF0000
-#define SHARED_HW_CFG_BOARD_REV_SHIFT 16
-
-#define SHARED_HW_CFG_BOARD_MAJOR_VER_MASK 0x0F000000
-#define SHARED_HW_CFG_BOARD_MAJOR_VER_SHIFT 24
-
-#define SHARED_HW_CFG_BOARD_MINOR_VER_MASK 0xF0000000
-#define SHARED_HW_CFG_BOARD_MINOR_VER_SHIFT 28
-
- u32 reserved; /* 0x128 */
-
-};
-
-
-/****************************************************************************
- * Port HW configuration *
- ****************************************************************************/
-struct port_hw_cfg { /* port 0: 0x12c port 1: 0x2bc */
-
- u32 pci_id;
-#define PORT_HW_CFG_PCI_VENDOR_ID_MASK 0xffff0000
-#define PORT_HW_CFG_PCI_DEVICE_ID_MASK 0x0000ffff
-
- u32 pci_sub_id;
-#define PORT_HW_CFG_PCI_SUBSYS_DEVICE_ID_MASK 0xffff0000
-#define PORT_HW_CFG_PCI_SUBSYS_VENDOR_ID_MASK 0x0000ffff
-
- u32 power_dissipated;
-#define PORT_HW_CFG_POWER_DIS_D3_MASK 0xff000000
-#define PORT_HW_CFG_POWER_DIS_D3_SHIFT 24
-#define PORT_HW_CFG_POWER_DIS_D2_MASK 0x00ff0000
-#define PORT_HW_CFG_POWER_DIS_D2_SHIFT 16
-#define PORT_HW_CFG_POWER_DIS_D1_MASK 0x0000ff00
-#define PORT_HW_CFG_POWER_DIS_D1_SHIFT 8
-#define PORT_HW_CFG_POWER_DIS_D0_MASK 0x000000ff
-#define PORT_HW_CFG_POWER_DIS_D0_SHIFT 0
-
- u32 power_consumed;
-#define PORT_HW_CFG_POWER_CONS_D3_MASK 0xff000000
-#define PORT_HW_CFG_POWER_CONS_D3_SHIFT 24
-#define PORT_HW_CFG_POWER_CONS_D2_MASK 0x00ff0000
-#define PORT_HW_CFG_POWER_CONS_D2_SHIFT 16
-#define PORT_HW_CFG_POWER_CONS_D1_MASK 0x0000ff00
-#define PORT_HW_CFG_POWER_CONS_D1_SHIFT 8
-#define PORT_HW_CFG_POWER_CONS_D0_MASK 0x000000ff
-#define PORT_HW_CFG_POWER_CONS_D0_SHIFT 0
-
- u32 mac_upper;
-#define PORT_HW_CFG_UPPERMAC_MASK 0x0000ffff
-#define PORT_HW_CFG_UPPERMAC_SHIFT 0
- u32 mac_lower;
-
- u32 iscsi_mac_upper; /* Upper 16 bits are always zeroes */
- u32 iscsi_mac_lower;
-
- u32 rdma_mac_upper; /* Upper 16 bits are always zeroes */
- u32 rdma_mac_lower;
-
- u32 serdes_config;
-#define PORT_HW_CFG_SERDES_TX_DRV_PRE_EMPHASIS_MASK 0x0000FFFF
-#define PORT_HW_CFG_SERDES_TX_DRV_PRE_EMPHASIS_SHIFT 0
-
-#define PORT_HW_CFG_SERDES_RX_DRV_EQUALIZER_MASK 0xFFFF0000
-#define PORT_HW_CFG_SERDES_RX_DRV_EQUALIZER_SHIFT 16
-
-
- u32 Reserved0[3]; /* 0x158 */
- /* Controls the TX laser of the SFP+ module */
- u32 sfp_ctrl; /* 0x164 */
-#define PORT_HW_CFG_TX_LASER_MASK 0x000000FF
-#define PORT_HW_CFG_TX_LASER_SHIFT 0
-#define PORT_HW_CFG_TX_LASER_MDIO 0x00000000
-#define PORT_HW_CFG_TX_LASER_GPIO0 0x00000001
-#define PORT_HW_CFG_TX_LASER_GPIO1 0x00000002
-#define PORT_HW_CFG_TX_LASER_GPIO2 0x00000003
-#define PORT_HW_CFG_TX_LASER_GPIO3 0x00000004
-
- /* Controls the fault module LED of the SFP+ */
-#define PORT_HW_CFG_FAULT_MODULE_LED_MASK 0x0000FF00
-#define PORT_HW_CFG_FAULT_MODULE_LED_SHIFT 8
-#define PORT_HW_CFG_FAULT_MODULE_LED_GPIO0 0x00000000
-#define PORT_HW_CFG_FAULT_MODULE_LED_GPIO1 0x00000100
-#define PORT_HW_CFG_FAULT_MODULE_LED_GPIO2 0x00000200
-#define PORT_HW_CFG_FAULT_MODULE_LED_GPIO3 0x00000300
-#define PORT_HW_CFG_FAULT_MODULE_LED_DISABLED 0x00000400
- u32 Reserved01[12]; /* 0x158 */
- /* for external PHY, or forced mode or during AN */
- u16 xgxs_config_rx[4]; /* 0x198 */
-
- u16 xgxs_config_tx[4]; /* 0x1A0 */
-
- u32 Reserved1[56]; /* 0x1A8 */
- u32 default_cfg; /* 0x288 */
-#define PORT_HW_CFG_GPIO0_CONFIG_MASK 0x00000003
-#define PORT_HW_CFG_GPIO0_CONFIG_SHIFT 0
-#define PORT_HW_CFG_GPIO0_CONFIG_NA 0x00000000
-#define PORT_HW_CFG_GPIO0_CONFIG_LOW 0x00000001
-#define PORT_HW_CFG_GPIO0_CONFIG_HIGH 0x00000002
-#define PORT_HW_CFG_GPIO0_CONFIG_INPUT 0x00000003
-
-#define PORT_HW_CFG_GPIO1_CONFIG_MASK 0x0000000C
-#define PORT_HW_CFG_GPIO1_CONFIG_SHIFT 2
-#define PORT_HW_CFG_GPIO1_CONFIG_NA 0x00000000
-#define PORT_HW_CFG_GPIO1_CONFIG_LOW 0x00000004
-#define PORT_HW_CFG_GPIO1_CONFIG_HIGH 0x00000008
-#define PORT_HW_CFG_GPIO1_CONFIG_INPUT 0x0000000c
-
-#define PORT_HW_CFG_GPIO2_CONFIG_MASK 0x00000030
-#define PORT_HW_CFG_GPIO2_CONFIG_SHIFT 4
-#define PORT_HW_CFG_GPIO2_CONFIG_NA 0x00000000
-#define PORT_HW_CFG_GPIO2_CONFIG_LOW 0x00000010
-#define PORT_HW_CFG_GPIO2_CONFIG_HIGH 0x00000020
-#define PORT_HW_CFG_GPIO2_CONFIG_INPUT 0x00000030
-
-#define PORT_HW_CFG_GPIO3_CONFIG_MASK 0x000000C0
-#define PORT_HW_CFG_GPIO3_CONFIG_SHIFT 6
-#define PORT_HW_CFG_GPIO3_CONFIG_NA 0x00000000
-#define PORT_HW_CFG_GPIO3_CONFIG_LOW 0x00000040
-#define PORT_HW_CFG_GPIO3_CONFIG_HIGH 0x00000080
-#define PORT_HW_CFG_GPIO3_CONFIG_INPUT 0x000000c0
-
- /*
- * When KR link is required to be set to force which is not
- * KR-compliant, this parameter determine what is the trigger for it.
- * When GPIO is selected, low input will force the speed. Currently
- * default speed is 1G. In the future, it may be widen to select the
- * forced speed in with another parameter. Note when force-1G is
- * enabled, it override option 56: Link Speed option.
- */
-#define PORT_HW_CFG_FORCE_KR_ENABLER_MASK 0x00000F00
-#define PORT_HW_CFG_FORCE_KR_ENABLER_SHIFT 8
-#define PORT_HW_CFG_FORCE_KR_ENABLER_NOT_FORCED 0x00000000
-#define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO0_P0 0x00000100
-#define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO1_P0 0x00000200
-#define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO2_P0 0x00000300
-#define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO3_P0 0x00000400
-#define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO0_P1 0x00000500
-#define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO1_P1 0x00000600
-#define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO2_P1 0x00000700
-#define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO3_P1 0x00000800
-#define PORT_HW_CFG_FORCE_KR_ENABLER_FORCED 0x00000900
- /* Enable to determine with which GPIO to reset the external phy */
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_MASK 0x000F0000
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_SHIFT 16
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_PHY_TYPE 0x00000000
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO0_P0 0x00010000
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO1_P0 0x00020000
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO2_P0 0x00030000
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO3_P0 0x00040000
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO0_P1 0x00050000
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO1_P1 0x00060000
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO2_P1 0x00070000
-#define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO3_P1 0x00080000
- /* Enable BAM on KR */
-#define PORT_HW_CFG_ENABLE_BAM_ON_KR_MASK 0x00100000
-#define PORT_HW_CFG_ENABLE_BAM_ON_KR_SHIFT 20
-#define PORT_HW_CFG_ENABLE_BAM_ON_KR_DISABLED 0x00000000
-#define PORT_HW_CFG_ENABLE_BAM_ON_KR_ENABLED 0x00100000
-
- /* Enable Common Mode Sense */
-#define PORT_HW_CFG_ENABLE_CMS_MASK 0x00200000
-#define PORT_HW_CFG_ENABLE_CMS_SHIFT 21
-#define PORT_HW_CFG_ENABLE_CMS_DISABLED 0x00000000
-#define PORT_HW_CFG_ENABLE_CMS_ENABLED 0x00200000
-
- u32 speed_capability_mask2; /* 0x28C */
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_MASK 0x0000FFFF
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_SHIFT 0
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_10M_FULL 0x00000001
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3__ 0x00000002
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3___ 0x00000004
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_100M_FULL 0x00000008
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_1G 0x00000010
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_2_DOT_5G 0x00000020
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_10G 0x00000040
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_12G 0x00000080
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_12_DOT_5G 0x00000100
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_13G 0x00000200
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_15G 0x00000400
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D3_16G 0x00000800
-
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_MASK 0xFFFF0000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_SHIFT 16
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_10M_FULL 0x00010000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0__ 0x00020000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0___ 0x00040000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_100M_FULL 0x00080000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_1G 0x00100000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_2_DOT_5G 0x00200000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_10G 0x00400000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_12G 0x00800000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_12_DOT_5G 0x01000000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_13G 0x02000000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_15G 0x04000000
-#define PORT_HW_CFG_SPEED_CAPABILITY2_D0_16G 0x08000000
-
- /* In the case where two media types (e.g. copper and fiber) are
- present and electrically active at the same time, PHY Selection
- will determine which of the two PHYs will be designated as the
- Active PHY and used for a connection to the network. */
- u32 multi_phy_config; /* 0x290 */
-#define PORT_HW_CFG_PHY_SELECTION_MASK 0x00000007
-#define PORT_HW_CFG_PHY_SELECTION_SHIFT 0
-#define PORT_HW_CFG_PHY_SELECTION_HARDWARE_DEFAULT 0x00000000
-#define PORT_HW_CFG_PHY_SELECTION_FIRST_PHY 0x00000001
-#define PORT_HW_CFG_PHY_SELECTION_SECOND_PHY 0x00000002
-#define PORT_HW_CFG_PHY_SELECTION_FIRST_PHY_PRIORITY 0x00000003
-#define PORT_HW_CFG_PHY_SELECTION_SECOND_PHY_PRIORITY 0x00000004
-
- /* When enabled, all second phy nvram parameters will be swapped
- with the first phy parameters */
-#define PORT_HW_CFG_PHY_SWAPPED_MASK 0x00000008
-#define PORT_HW_CFG_PHY_SWAPPED_SHIFT 3
-#define PORT_HW_CFG_PHY_SWAPPED_DISABLED 0x00000000
-#define PORT_HW_CFG_PHY_SWAPPED_ENABLED 0x00000008
-
-
- /* Address of the second external phy */
- u32 external_phy_config2; /* 0x294 */
-#define PORT_HW_CFG_XGXS_EXT_PHY2_ADDR_MASK 0x000000FF
-#define PORT_HW_CFG_XGXS_EXT_PHY2_ADDR_SHIFT 0
-
- /* The second XGXS external PHY type */
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_MASK 0x0000FF00
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_SHIFT 8
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_DIRECT 0x00000000
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8071 0x00000100
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8072 0x00000200
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8073 0x00000300
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8705 0x00000400
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8706 0x00000500
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8726 0x00000600
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8481 0x00000700
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_SFX7101 0x00000800
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8727 0x00000900
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8727_NOC 0x00000a00
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM84823 0x00000b00
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM54640 0x00000c00
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM84833 0x00000d00
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_FAILURE 0x0000fd00
-#define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_NOT_CONN 0x0000ff00
-
- /* 4 times 16 bits for all 4 lanes. For some external PHYs (such as
- 8706, 8726 and 8727) not all 4 values are needed. */
- u16 xgxs_config2_rx[4]; /* 0x296 */
- u16 xgxs_config2_tx[4]; /* 0x2A0 */
-
- u32 lane_config;
-#define PORT_HW_CFG_LANE_SWAP_CFG_MASK 0x0000ffff
-#define PORT_HW_CFG_LANE_SWAP_CFG_SHIFT 0
-
-#define PORT_HW_CFG_LANE_SWAP_CFG_TX_MASK 0x000000ff
-#define PORT_HW_CFG_LANE_SWAP_CFG_TX_SHIFT 0
-#define PORT_HW_CFG_LANE_SWAP_CFG_RX_MASK 0x0000ff00
-#define PORT_HW_CFG_LANE_SWAP_CFG_RX_SHIFT 8
-#define PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK 0x0000c000
-#define PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT 14
- /* AN and forced */
-#define PORT_HW_CFG_LANE_SWAP_CFG_01230123 0x00001b1b
- /* forced only */
-#define PORT_HW_CFG_LANE_SWAP_CFG_01233210 0x00001be4
- /* forced only */
-#define PORT_HW_CFG_LANE_SWAP_CFG_31203120 0x0000d8d8
- /* forced only */
-#define PORT_HW_CFG_LANE_SWAP_CFG_32103210 0x0000e4e4
- /* Indicate whether to swap the external phy polarity */
-#define PORT_HW_CFG_SWAP_PHY_POLARITY_MASK 0x00010000
-#define PORT_HW_CFG_SWAP_PHY_POLARITY_DISABLED 0x00000000
-#define PORT_HW_CFG_SWAP_PHY_POLARITY_ENABLED 0x00010000
-
- u32 external_phy_config;
-#define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_MASK 0xff000000
-#define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_SHIFT 24
-#define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT 0x00000000
-#define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482 0x01000000
-#define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_NOT_CONN 0xff000000
-
-#define PORT_HW_CFG_SERDES_EXT_PHY_ADDR_MASK 0x00ff0000
-#define PORT_HW_CFG_SERDES_EXT_PHY_ADDR_SHIFT 16
-
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_MASK 0x0000ff00
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SHIFT 8
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT 0x00000000
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8071 0x00000100
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072 0x00000200
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073 0x00000300
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705 0x00000400
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706 0x00000500
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726 0x00000600
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481 0x00000700
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101 0x00000800
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727 0x00000900
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727_NOC 0x00000a00
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823 0x00000b00
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833 0x00000d00
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE 0x0000fd00
-#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN 0x0000ff00
-
-#define PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK 0x000000ff
-#define PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT 0
-
- u32 speed_capability_mask;
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_MASK 0xffff0000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_SHIFT 16
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL 0x00010000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF 0x00020000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF 0x00040000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL 0x00080000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_1G 0x00100000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G 0x00200000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_10G 0x00400000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_12G 0x00800000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_12_5G 0x01000000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_13G 0x02000000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_15G 0x04000000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_16G 0x08000000
-#define PORT_HW_CFG_SPEED_CAPABILITY_D0_RESERVED 0xf0000000
-
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_MASK 0x0000ffff
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_SHIFT 0
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_10M_FULL 0x00000001
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_10M_HALF 0x00000002
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_100M_HALF 0x00000004
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_100M_FULL 0x00000008
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_1G 0x00000010
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_2_5G 0x00000020
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_10G 0x00000040
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_12G 0x00000080
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_12_5G 0x00000100
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_13G 0x00000200
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_15G 0x00000400
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_16G 0x00000800
-#define PORT_HW_CFG_SPEED_CAPABILITY_D3_RESERVED 0x0000f000
-
- u32 reserved[2];
-
-};
-
-
-/****************************************************************************
- * Shared Feature configuration *
- ****************************************************************************/
-struct shared_feat_cfg { /* NVRAM Offset */
-
- u32 config; /* 0x450 */
-#define SHARED_FEATURE_BMC_ECHO_MODE_EN 0x00000001
-
- /* Use the values from options 47 and 48 instead of the HW default
- values */
-#define SHARED_FEAT_CFG_OVERRIDE_PREEMPHASIS_CFG_DISABLED 0x00000000
-#define SHARED_FEAT_CFG_OVERRIDE_PREEMPHASIS_CFG_ENABLED 0x00000002
-
-#define SHARED_FEAT_CFG_FORCE_SF_MODE_MASK 0x00000700
-#define SHARED_FEAT_CFG_FORCE_SF_MODE_SHIFT 8
-#define SHARED_FEAT_CFG_FORCE_SF_MODE_MF_ALLOWED 0x00000000
-#define SHARED_FEAT_CFG_FORCE_SF_MODE_FORCED_SF 0x00000100
-#define SHARED_FEAT_CFG_FORCE_SF_MODE_SPIO4 0x00000200
-#define SHARED_FEAT_CFG_FORCE_SF_MODE_SWITCH_INDEPT 0x00000300
-
-};
-
-
-/****************************************************************************
- * Port Feature configuration *
- ****************************************************************************/
-struct port_feat_cfg { /* port 0: 0x454 port 1: 0x4c8 */
-
- u32 config;
-#define PORT_FEATURE_BAR1_SIZE_MASK 0x0000000f
-#define PORT_FEATURE_BAR1_SIZE_SHIFT 0
-#define PORT_FEATURE_BAR1_SIZE_DISABLED 0x00000000
-#define PORT_FEATURE_BAR1_SIZE_64K 0x00000001
-#define PORT_FEATURE_BAR1_SIZE_128K 0x00000002
-#define PORT_FEATURE_BAR1_SIZE_256K 0x00000003
-#define PORT_FEATURE_BAR1_SIZE_512K 0x00000004
-#define PORT_FEATURE_BAR1_SIZE_1M 0x00000005
-#define PORT_FEATURE_BAR1_SIZE_2M 0x00000006
-#define PORT_FEATURE_BAR1_SIZE_4M 0x00000007
-#define PORT_FEATURE_BAR1_SIZE_8M 0x00000008
-#define PORT_FEATURE_BAR1_SIZE_16M 0x00000009
-#define PORT_FEATURE_BAR1_SIZE_32M 0x0000000a
-#define PORT_FEATURE_BAR1_SIZE_64M 0x0000000b
-#define PORT_FEATURE_BAR1_SIZE_128M 0x0000000c
-#define PORT_FEATURE_BAR1_SIZE_256M 0x0000000d
-#define PORT_FEATURE_BAR1_SIZE_512M 0x0000000e
-#define PORT_FEATURE_BAR1_SIZE_1G 0x0000000f
-#define PORT_FEATURE_BAR2_SIZE_MASK 0x000000f0
-#define PORT_FEATURE_BAR2_SIZE_SHIFT 4
-#define PORT_FEATURE_BAR2_SIZE_DISABLED 0x00000000
-#define PORT_FEATURE_BAR2_SIZE_64K 0x00000010
-#define PORT_FEATURE_BAR2_SIZE_128K 0x00000020
-#define PORT_FEATURE_BAR2_SIZE_256K 0x00000030
-#define PORT_FEATURE_BAR2_SIZE_512K 0x00000040
-#define PORT_FEATURE_BAR2_SIZE_1M 0x00000050
-#define PORT_FEATURE_BAR2_SIZE_2M 0x00000060
-#define PORT_FEATURE_BAR2_SIZE_4M 0x00000070
-#define PORT_FEATURE_BAR2_SIZE_8M 0x00000080
-#define PORT_FEATURE_BAR2_SIZE_16M 0x00000090
-#define PORT_FEATURE_BAR2_SIZE_32M 0x000000a0
-#define PORT_FEATURE_BAR2_SIZE_64M 0x000000b0
-#define PORT_FEATURE_BAR2_SIZE_128M 0x000000c0
-#define PORT_FEATURE_BAR2_SIZE_256M 0x000000d0
-#define PORT_FEATURE_BAR2_SIZE_512M 0x000000e0
-#define PORT_FEATURE_BAR2_SIZE_1G 0x000000f0
-#define PORT_FEATURE_EN_SIZE_MASK 0x07000000
-#define PORT_FEATURE_EN_SIZE_SHIFT 24
-#define PORT_FEATURE_WOL_ENABLED 0x01000000
-#define PORT_FEATURE_MBA_ENABLED 0x02000000
-#define PORT_FEATURE_MFW_ENABLED 0x04000000
-
- /* Reserved bits: 28-29 */
- /* Check the optic vendor via i2c against a list of approved modules
- in a separate nvram image */
-#define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK 0xE0000000
-#define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_SHIFT 29
-#define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_NO_ENFORCEMENT 0x00000000
-#define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER 0x20000000
-#define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_WARNING_MSG 0x40000000
-#define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_POWER_DOWN 0x60000000
-
-
- u32 wol_config;
- /* Default is used when driver sets to "auto" mode */
-#define PORT_FEATURE_WOL_DEFAULT_MASK 0x00000003
-#define PORT_FEATURE_WOL_DEFAULT_SHIFT 0
-#define PORT_FEATURE_WOL_DEFAULT_DISABLE 0x00000000
-#define PORT_FEATURE_WOL_DEFAULT_MAGIC 0x00000001
-#define PORT_FEATURE_WOL_DEFAULT_ACPI 0x00000002
-#define PORT_FEATURE_WOL_DEFAULT_MAGIC_AND_ACPI 0x00000003
-#define PORT_FEATURE_WOL_RES_PAUSE_CAP 0x00000004
-#define PORT_FEATURE_WOL_RES_ASYM_PAUSE_CAP 0x00000008
-#define PORT_FEATURE_WOL_ACPI_UPON_MGMT 0x00000010
-
- u32 mba_config;
-#define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_MASK 0x00000003
-#define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_SHIFT 0
-#define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_PXE 0x00000000
-#define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_RPL 0x00000001
-#define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_BOOTP 0x00000002
-#define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_ISCSIB 0x00000003
-#define PORT_FEATURE_MBA_RES_PAUSE_CAP 0x00000100
-#define PORT_FEATURE_MBA_RES_ASYM_PAUSE_CAP 0x00000200
-#define PORT_FEATURE_MBA_SETUP_PROMPT_ENABLE 0x00000400
-#define PORT_FEATURE_MBA_HOTKEY_CTRL_S 0x00000000
-#define PORT_FEATURE_MBA_HOTKEY_CTRL_B 0x00000800
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_MASK 0x000ff000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_SHIFT 12
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_DISABLED 0x00000000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_2K 0x00001000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_4K 0x00002000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_8K 0x00003000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_16K 0x00004000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_32K 0x00005000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_64K 0x00006000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_128K 0x00007000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_256K 0x00008000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_512K 0x00009000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_1M 0x0000a000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_2M 0x0000b000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_4M 0x0000c000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_8M 0x0000d000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_16M 0x0000e000
-#define PORT_FEATURE_MBA_EXP_ROM_SIZE_32M 0x0000f000
-#define PORT_FEATURE_MBA_MSG_TIMEOUT_MASK 0x00f00000
-#define PORT_FEATURE_MBA_MSG_TIMEOUT_SHIFT 20
-#define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_MASK 0x03000000
-#define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_SHIFT 24
-#define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_AUTO 0x00000000
-#define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_BBS 0x01000000
-#define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_INT18H 0x02000000
-#define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_INT19H 0x03000000
-#define PORT_FEATURE_MBA_LINK_SPEED_MASK 0x3c000000
-#define PORT_FEATURE_MBA_LINK_SPEED_SHIFT 26
-#define PORT_FEATURE_MBA_LINK_SPEED_AUTO 0x00000000
-#define PORT_FEATURE_MBA_LINK_SPEED_10HD 0x04000000
-#define PORT_FEATURE_MBA_LINK_SPEED_10FD 0x08000000
-#define PORT_FEATURE_MBA_LINK_SPEED_100HD 0x0c000000
-#define PORT_FEATURE_MBA_LINK_SPEED_100FD 0x10000000
-#define PORT_FEATURE_MBA_LINK_SPEED_1GBPS 0x14000000
-#define PORT_FEATURE_MBA_LINK_SPEED_2_5GBPS 0x18000000
-#define PORT_FEATURE_MBA_LINK_SPEED_10GBPS_CX4 0x1c000000
-#define PORT_FEATURE_MBA_LINK_SPEED_10GBPS_KX4 0x20000000
-#define PORT_FEATURE_MBA_LINK_SPEED_10GBPS_KR 0x24000000
-#define PORT_FEATURE_MBA_LINK_SPEED_12GBPS 0x28000000
-#define PORT_FEATURE_MBA_LINK_SPEED_12_5GBPS 0x2c000000
-#define PORT_FEATURE_MBA_LINK_SPEED_13GBPS 0x30000000
-#define PORT_FEATURE_MBA_LINK_SPEED_15GBPS 0x34000000
-#define PORT_FEATURE_MBA_LINK_SPEED_16GBPS 0x38000000
-
- u32 bmc_config;
-#define PORT_FEATURE_BMC_LINK_OVERRIDE_DEFAULT 0x00000000
-#define PORT_FEATURE_BMC_LINK_OVERRIDE_EN 0x00000001
-
- u32 mba_vlan_cfg;
-#define PORT_FEATURE_MBA_VLAN_TAG_MASK 0x0000ffff
-#define PORT_FEATURE_MBA_VLAN_TAG_SHIFT 0
-#define PORT_FEATURE_MBA_VLAN_EN 0x00010000
-
- u32 resource_cfg;
-#define PORT_FEATURE_RESOURCE_CFG_VALID 0x00000001
-#define PORT_FEATURE_RESOURCE_CFG_DIAG 0x00000002
-#define PORT_FEATURE_RESOURCE_CFG_L2 0x00000004
-#define PORT_FEATURE_RESOURCE_CFG_ISCSI 0x00000008
-#define PORT_FEATURE_RESOURCE_CFG_RDMA 0x00000010
-
- u32 smbus_config;
- /* Obsolete */
-#define PORT_FEATURE_SMBUS_EN 0x00000001
-#define PORT_FEATURE_SMBUS_ADDR_MASK 0x000000fe
-#define PORT_FEATURE_SMBUS_ADDR_SHIFT 1
-
- u32 reserved1;
-
- u32 link_config; /* Used as HW defaults for the driver */
-#define PORT_FEATURE_CONNECTED_SWITCH_MASK 0x03000000
-#define PORT_FEATURE_CONNECTED_SWITCH_SHIFT 24
- /* (forced) low speed switch (< 10G) */
-#define PORT_FEATURE_CON_SWITCH_1G_SWITCH 0x00000000
- /* (forced) high speed switch (>= 10G) */
-#define PORT_FEATURE_CON_SWITCH_10G_SWITCH 0x01000000
-#define PORT_FEATURE_CON_SWITCH_AUTO_DETECT 0x02000000
-#define PORT_FEATURE_CON_SWITCH_ONE_TIME_DETECT 0x03000000
-
-#define PORT_FEATURE_LINK_SPEED_MASK 0x000f0000
-#define PORT_FEATURE_LINK_SPEED_SHIFT 16
-#define PORT_FEATURE_LINK_SPEED_AUTO 0x00000000
-#define PORT_FEATURE_LINK_SPEED_10M_FULL 0x00010000
-#define PORT_FEATURE_LINK_SPEED_10M_HALF 0x00020000
-#define PORT_FEATURE_LINK_SPEED_100M_HALF 0x00030000
-#define PORT_FEATURE_LINK_SPEED_100M_FULL 0x00040000
-#define PORT_FEATURE_LINK_SPEED_1G 0x00050000
-#define PORT_FEATURE_LINK_SPEED_2_5G 0x00060000
-#define PORT_FEATURE_LINK_SPEED_10G_CX4 0x00070000
-#define PORT_FEATURE_LINK_SPEED_10G_KX4 0x00080000
-#define PORT_FEATURE_LINK_SPEED_10G_KR 0x00090000
-#define PORT_FEATURE_LINK_SPEED_12G 0x000a0000
-#define PORT_FEATURE_LINK_SPEED_12_5G 0x000b0000
-#define PORT_FEATURE_LINK_SPEED_13G 0x000c0000
-#define PORT_FEATURE_LINK_SPEED_15G 0x000d0000
-#define PORT_FEATURE_LINK_SPEED_16G 0x000e0000
-
-#define PORT_FEATURE_FLOW_CONTROL_MASK 0x00000700
-#define PORT_FEATURE_FLOW_CONTROL_SHIFT 8
-#define PORT_FEATURE_FLOW_CONTROL_AUTO 0x00000000
-#define PORT_FEATURE_FLOW_CONTROL_TX 0x00000100
-#define PORT_FEATURE_FLOW_CONTROL_RX 0x00000200
-#define PORT_FEATURE_FLOW_CONTROL_BOTH 0x00000300
-#define PORT_FEATURE_FLOW_CONTROL_NONE 0x00000400
-
- /* The default for MCP link configuration,
- uses the same defines as link_config */
- u32 mfw_wol_link_cfg;
- /* The default for the driver of the second external phy,
- uses the same defines as link_config */
- u32 link_config2; /* 0x47C */
-
- /* The default for MCP of the second external phy,
- uses the same defines as link_config */
- u32 mfw_wol_link_cfg2; /* 0x480 */
-
- u32 Reserved2[17]; /* 0x484 */
-
-};
-
-
-/****************************************************************************
- * Device Information *
- ****************************************************************************/
-struct shm_dev_info { /* size */
-
- u32 bc_rev; /* 8 bits each: major, minor, build */ /* 4 */
-
- struct shared_hw_cfg shared_hw_config; /* 40 */
-
- struct port_hw_cfg port_hw_config[PORT_MAX]; /* 400*2=800 */
-
- struct shared_feat_cfg shared_feature_config; /* 4 */
-
- struct port_feat_cfg port_feature_config[PORT_MAX];/* 116*2=232 */
-
-};
-
-
-#define FUNC_0 0
-#define FUNC_1 1
-#define FUNC_2 2
-#define FUNC_3 3
-#define FUNC_4 4
-#define FUNC_5 5
-#define FUNC_6 6
-#define FUNC_7 7
-#define E1_FUNC_MAX 2
-#define E1H_FUNC_MAX 8
-#define E2_FUNC_MAX 4 /* per path */
-
-#define VN_0 0
-#define VN_1 1
-#define VN_2 2
-#define VN_3 3
-#define E1VN_MAX 1
-#define E1HVN_MAX 4
-
-#define E2_VF_MAX 64
-/* This value (in milliseconds) determines the frequency of the driver
- * issuing the PULSE message code. The firmware monitors this periodic
- * pulse to determine when to switch to an OS-absent mode. */
-#define DRV_PULSE_PERIOD_MS 250
-
-/* This value (in milliseconds) determines how long the driver should
- * wait for an acknowledgement from the firmware before timing out. Once
- * the firmware has timed out, the driver will assume there is no firmware
- * running and there won't be any firmware-driver synchronization during a
- * driver reset. */
-#define FW_ACK_TIME_OUT_MS 5000
-
-#define FW_ACK_POLL_TIME_MS 1
-
-#define FW_ACK_NUM_OF_POLL (FW_ACK_TIME_OUT_MS/FW_ACK_POLL_TIME_MS)
-
-/* LED Blink rate that will achieve ~15.9Hz */
-#define LED_BLINK_RATE_VAL 480
-
-/****************************************************************************
- * Driver <-> FW Mailbox *
- ****************************************************************************/
-struct drv_port_mb {
-
- u32 link_status;
- /* Driver should update this field on any link change event */
-
-#define LINK_STATUS_LINK_FLAG_MASK 0x00000001
-#define LINK_STATUS_LINK_UP 0x00000001
-#define LINK_STATUS_SPEED_AND_DUPLEX_MASK 0x0000001E
-#define LINK_STATUS_SPEED_AND_DUPLEX_AN_NOT_COMPLETE (0<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_10THD (1<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_10TFD (2<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_100TXHD (3<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_100T4 (4<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_100TXFD (5<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_1000THD (6<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_1000TFD (7<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_1000XFD (7<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_2500THD (8<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_2500TFD (9<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_2500XFD (9<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_10GTFD (10<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_10GXFD (10<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_12GTFD (11<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_12GXFD (11<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_12_5GTFD (12<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_12_5GXFD (12<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_13GTFD (13<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_13GXFD (13<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_15GTFD (14<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_15GXFD (14<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_16GTFD (15<<1)
-#define LINK_STATUS_SPEED_AND_DUPLEX_16GXFD (15<<1)
-
-#define LINK_STATUS_AUTO_NEGOTIATE_FLAG_MASK 0x00000020
-#define LINK_STATUS_AUTO_NEGOTIATE_ENABLED 0x00000020
-
-#define LINK_STATUS_AUTO_NEGOTIATE_COMPLETE 0x00000040
-#define LINK_STATUS_PARALLEL_DETECTION_FLAG_MASK 0x00000080
-#define LINK_STATUS_PARALLEL_DETECTION_USED 0x00000080
-
-#define LINK_STATUS_LINK_PARTNER_1000TFD_CAPABLE 0x00000200
-#define LINK_STATUS_LINK_PARTNER_1000THD_CAPABLE 0x00000400
-#define LINK_STATUS_LINK_PARTNER_100T4_CAPABLE 0x00000800
-#define LINK_STATUS_LINK_PARTNER_100TXFD_CAPABLE 0x00001000
-#define LINK_STATUS_LINK_PARTNER_100TXHD_CAPABLE 0x00002000
-#define LINK_STATUS_LINK_PARTNER_10TFD_CAPABLE 0x00004000
-#define LINK_STATUS_LINK_PARTNER_10THD_CAPABLE 0x00008000
-
-#define LINK_STATUS_TX_FLOW_CONTROL_FLAG_MASK 0x00010000
-#define LINK_STATUS_TX_FLOW_CONTROL_ENABLED 0x00010000
-
-#define LINK_STATUS_RX_FLOW_CONTROL_FLAG_MASK 0x00020000
-#define LINK_STATUS_RX_FLOW_CONTROL_ENABLED 0x00020000
-
-#define LINK_STATUS_LINK_PARTNER_FLOW_CONTROL_MASK 0x000C0000
-#define LINK_STATUS_LINK_PARTNER_NOT_PAUSE_CAPABLE (0<<18)
-#define LINK_STATUS_LINK_PARTNER_SYMMETRIC_PAUSE (1<<18)
-#define LINK_STATUS_LINK_PARTNER_ASYMMETRIC_PAUSE (2<<18)
-#define LINK_STATUS_LINK_PARTNER_BOTH_PAUSE (3<<18)
-
-#define LINK_STATUS_SERDES_LINK 0x00100000
-
-#define LINK_STATUS_LINK_PARTNER_2500XFD_CAPABLE 0x00200000
-#define LINK_STATUS_LINK_PARTNER_2500XHD_CAPABLE 0x00400000
-#define LINK_STATUS_LINK_PARTNER_10GXFD_CAPABLE 0x00800000
-#define LINK_STATUS_LINK_PARTNER_12GXFD_CAPABLE 0x01000000
-#define LINK_STATUS_LINK_PARTNER_12_5GXFD_CAPABLE 0x02000000
-#define LINK_STATUS_LINK_PARTNER_13GXFD_CAPABLE 0x04000000
-#define LINK_STATUS_LINK_PARTNER_15GXFD_CAPABLE 0x08000000
-#define LINK_STATUS_LINK_PARTNER_16GXFD_CAPABLE 0x10000000
-
- u32 port_stx;
-
- u32 stat_nig_timer;
-
- /* MCP firmware does not use this field */
- u32 ext_phy_fw_version;
-
-};
-
-
-struct drv_func_mb {
-
- u32 drv_mb_header;
-#define DRV_MSG_CODE_MASK 0xffff0000
-#define DRV_MSG_CODE_LOAD_REQ 0x10000000
-#define DRV_MSG_CODE_LOAD_DONE 0x11000000
-#define DRV_MSG_CODE_UNLOAD_REQ_WOL_EN 0x20000000
-#define DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS 0x20010000
-#define DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP 0x20020000
-#define DRV_MSG_CODE_UNLOAD_DONE 0x21000000
-#define DRV_MSG_CODE_DCC_OK 0x30000000
-#define DRV_MSG_CODE_DCC_FAILURE 0x31000000
-#define DRV_MSG_CODE_DIAG_ENTER_REQ 0x50000000
-#define DRV_MSG_CODE_DIAG_EXIT_REQ 0x60000000
-#define DRV_MSG_CODE_VALIDATE_KEY 0x70000000
-#define DRV_MSG_CODE_GET_CURR_KEY 0x80000000
-#define DRV_MSG_CODE_GET_UPGRADE_KEY 0x81000000
-#define DRV_MSG_CODE_GET_MANUF_KEY 0x82000000
-#define DRV_MSG_CODE_LOAD_L2B_PRAM 0x90000000
- /*
- * The optic module verification commands require bootcode
- * v5.0.6 or later
- */
-#define DRV_MSG_CODE_VRFY_FIRST_PHY_OPT_MDL 0xa0000000
-#define REQ_BC_VER_4_VRFY_FIRST_PHY_OPT_MDL 0x00050006
- /*
- * The specific optic module verification command requires bootcode
- * v5.2.12 or later
- */
-#define DRV_MSG_CODE_VRFY_SPECIFIC_PHY_OPT_MDL 0xa1000000
-#define REQ_BC_VER_4_VRFY_SPECIFIC_PHY_OPT_MDL 0x00050234
-
-#define DRV_MSG_CODE_DCBX_ADMIN_PMF_MSG 0xb0000000
-#define DRV_MSG_CODE_DCBX_PMF_DRV_OK 0xb2000000
-#define DRV_MSG_CODE_SET_MF_BW 0xe0000000
-#define REQ_BC_VER_4_SET_MF_BW 0x00060202
-#define DRV_MSG_CODE_SET_MF_BW_ACK 0xe1000000
-#define BIOS_MSG_CODE_LIC_CHALLENGE 0xff010000
-#define BIOS_MSG_CODE_LIC_RESPONSE 0xff020000
-#define BIOS_MSG_CODE_VIRT_MAC_PRIM 0xff030000
-#define BIOS_MSG_CODE_VIRT_MAC_ISCSI 0xff040000
-
-#define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff
-
- u32 drv_mb_param;
-
- u32 fw_mb_header;
-#define FW_MSG_CODE_MASK 0xffff0000
-#define FW_MSG_CODE_DRV_LOAD_COMMON 0x10100000
-#define FW_MSG_CODE_DRV_LOAD_PORT 0x10110000
-#define FW_MSG_CODE_DRV_LOAD_FUNCTION 0x10120000
- /* Load common chip is supported from bc 6.0.0 */
-#define REQ_BC_VER_4_DRV_LOAD_COMMON_CHIP 0x00060000
-#define FW_MSG_CODE_DRV_LOAD_COMMON_CHIP 0x10130000
-#define FW_MSG_CODE_DRV_LOAD_REFUSED 0x10200000
-#define FW_MSG_CODE_DRV_LOAD_DONE 0x11100000
-#define FW_MSG_CODE_DRV_UNLOAD_COMMON 0x20100000
-#define FW_MSG_CODE_DRV_UNLOAD_PORT 0x20110000
-#define FW_MSG_CODE_DRV_UNLOAD_FUNCTION 0x20120000
-#define FW_MSG_CODE_DRV_UNLOAD_DONE 0x21100000
-#define FW_MSG_CODE_DCC_DONE 0x30100000
-#define FW_MSG_CODE_DIAG_ENTER_DONE 0x50100000
-#define FW_MSG_CODE_DIAG_REFUSE 0x50200000
-#define FW_MSG_CODE_DIAG_EXIT_DONE 0x60100000
-#define FW_MSG_CODE_VALIDATE_KEY_SUCCESS 0x70100000
-#define FW_MSG_CODE_VALIDATE_KEY_FAILURE 0x70200000
-#define FW_MSG_CODE_GET_KEY_DONE 0x80100000
-#define FW_MSG_CODE_NO_KEY 0x80f00000
-#define FW_MSG_CODE_LIC_INFO_NOT_READY 0x80f80000
-#define FW_MSG_CODE_L2B_PRAM_LOADED 0x90100000
-#define FW_MSG_CODE_L2B_PRAM_T_LOAD_FAILURE 0x90210000
-#define FW_MSG_CODE_L2B_PRAM_C_LOAD_FAILURE 0x90220000
-#define FW_MSG_CODE_L2B_PRAM_X_LOAD_FAILURE 0x90230000
-#define FW_MSG_CODE_L2B_PRAM_U_LOAD_FAILURE 0x90240000
-#define FW_MSG_CODE_VRFY_OPT_MDL_SUCCESS 0xa0100000
-#define FW_MSG_CODE_VRFY_OPT_MDL_INVLD_IMG 0xa0200000
-#define FW_MSG_CODE_VRFY_OPT_MDL_UNAPPROVED 0xa0300000
-
-#define FW_MSG_CODE_LIC_CHALLENGE 0xff010000
-#define FW_MSG_CODE_LIC_RESPONSE 0xff020000
-#define FW_MSG_CODE_VIRT_MAC_PRIM 0xff030000
-#define FW_MSG_CODE_VIRT_MAC_ISCSI 0xff040000
-
-#define FW_MSG_SEQ_NUMBER_MASK 0x0000ffff
-
- u32 fw_mb_param;
-
- u32 drv_pulse_mb;
-#define DRV_PULSE_SEQ_MASK 0x00007fff
-#define DRV_PULSE_SYSTEM_TIME_MASK 0xffff0000
- /* The system time is in the format of
- * (year-2001)*12*32 + month*32 + day. */
-#define DRV_PULSE_ALWAYS_ALIVE 0x00008000
- /* Indicate to the firmware not to go into the
- * OS-absent when it is not getting driver pulse.
- * This is used for debugging as well for PXE(MBA). */
-
- u32 mcp_pulse_mb;
-#define MCP_PULSE_SEQ_MASK 0x00007fff
-#define MCP_PULSE_ALWAYS_ALIVE 0x00008000
- /* Indicates to the driver not to assert due to lack
- * of MCP response */
-#define MCP_EVENT_MASK 0xffff0000
-#define MCP_EVENT_OTHER_DRIVER_RESET_REQ 0x00010000
-
- u32 iscsi_boot_signature;
- u32 iscsi_boot_block_offset;
-
- u32 drv_status;
-#define DRV_STATUS_PMF 0x00000001
-#define DRV_STATUS_SET_MF_BW 0x00000004
-
-#define DRV_STATUS_DCC_EVENT_MASK 0x0000ff00
-#define DRV_STATUS_DCC_DISABLE_ENABLE_PF 0x00000100
-#define DRV_STATUS_DCC_BANDWIDTH_ALLOCATION 0x00000200
-#define DRV_STATUS_DCC_CHANGE_MAC_ADDRESS 0x00000400
-#define DRV_STATUS_DCC_RESERVED1 0x00000800
-#define DRV_STATUS_DCC_SET_PROTOCOL 0x00001000
-#define DRV_STATUS_DCC_SET_PRIORITY 0x00002000
-#define DRV_STATUS_DCBX_EVENT_MASK 0x000f0000
-#define DRV_STATUS_DCBX_NEGOTIATION_RESULTS 0x00010000
-
- u32 virt_mac_upper;
-#define VIRT_MAC_SIGN_MASK 0xffff0000
-#define VIRT_MAC_SIGNATURE 0x564d0000
- u32 virt_mac_lower;
-
-};
-
-
-/****************************************************************************
- * Management firmware state *
- ****************************************************************************/
-/* Allocate 440 bytes for management firmware */
-#define MGMTFW_STATE_WORD_SIZE 110
-
-struct mgmtfw_state {
- u32 opaque[MGMTFW_STATE_WORD_SIZE];
-};
-
-
-/****************************************************************************
- * Multi-Function configuration *
- ****************************************************************************/
-struct shared_mf_cfg {
-
- u32 clp_mb;
-#define SHARED_MF_CLP_SET_DEFAULT 0x00000000
- /* set by CLP */
-#define SHARED_MF_CLP_EXIT 0x00000001
- /* set by MCP */
-#define SHARED_MF_CLP_EXIT_DONE 0x00010000
-
-};
-
-struct port_mf_cfg {
-
- u32 dynamic_cfg; /* device control channel */
-#define PORT_MF_CFG_E1HOV_TAG_MASK 0x0000ffff
-#define PORT_MF_CFG_E1HOV_TAG_SHIFT 0
-#define PORT_MF_CFG_E1HOV_TAG_DEFAULT PORT_MF_CFG_E1HOV_TAG_MASK
-
- u32 reserved[3];
-
-};
-
-struct func_mf_cfg {
-
- u32 config;
- /* E/R/I/D */
- /* function 0 of each port cannot be hidden */
-#define FUNC_MF_CFG_FUNC_HIDE 0x00000001
-
-#define FUNC_MF_CFG_PROTOCOL_MASK 0x00000007
-#define FUNC_MF_CFG_PROTOCOL_ETHERNET 0x00000002
-#define FUNC_MF_CFG_PROTOCOL_ETHERNET_WITH_RDMA 0x00000004
-#define FUNC_MF_CFG_PROTOCOL_ISCSI 0x00000006
-#define FUNC_MF_CFG_PROTOCOL_DEFAULT\
- FUNC_MF_CFG_PROTOCOL_ETHERNET_WITH_RDMA
-
-#define FUNC_MF_CFG_FUNC_DISABLED 0x00000008
-
- /* PRI */
- /* 0 - low priority, 3 - high priority */
-#define FUNC_MF_CFG_TRANSMIT_PRIORITY_MASK 0x00000300
-#define FUNC_MF_CFG_TRANSMIT_PRIORITY_SHIFT 8
-#define FUNC_MF_CFG_TRANSMIT_PRIORITY_DEFAULT 0x00000000
-
- /* MINBW, MAXBW */
- /* value range - 0..100, increments in 100Mbps */
-#define FUNC_MF_CFG_MIN_BW_MASK 0x00ff0000
-#define FUNC_MF_CFG_MIN_BW_SHIFT 16
-#define FUNC_MF_CFG_MIN_BW_DEFAULT 0x00000000
-#define FUNC_MF_CFG_MAX_BW_MASK 0xff000000
-#define FUNC_MF_CFG_MAX_BW_SHIFT 24
-#define FUNC_MF_CFG_MAX_BW_DEFAULT 0x64000000
-
- u32 mac_upper; /* MAC */
-#define FUNC_MF_CFG_UPPERMAC_MASK 0x0000ffff
-#define FUNC_MF_CFG_UPPERMAC_SHIFT 0
-#define FUNC_MF_CFG_UPPERMAC_DEFAULT FUNC_MF_CFG_UPPERMAC_MASK
- u32 mac_lower;
-#define FUNC_MF_CFG_LOWERMAC_DEFAULT 0xffffffff
-
- u32 e1hov_tag; /* VNI */
-#define FUNC_MF_CFG_E1HOV_TAG_MASK 0x0000ffff
-#define FUNC_MF_CFG_E1HOV_TAG_SHIFT 0
-#define FUNC_MF_CFG_E1HOV_TAG_DEFAULT FUNC_MF_CFG_E1HOV_TAG_MASK
-
- u32 reserved[2];
-
-};
-
-/* This structure is not applicable and should not be accessed on 57711 */
-struct func_ext_cfg {
- u32 func_cfg;
-#define MACP_FUNC_CFG_FLAGS_MASK 0x000000FF
-#define MACP_FUNC_CFG_FLAGS_SHIFT 0
-#define MACP_FUNC_CFG_FLAGS_ENABLED 0x00000001
-#define MACP_FUNC_CFG_FLAGS_ETHERNET 0x00000002
-#define MACP_FUNC_CFG_FLAGS_ISCSI_OFFLOAD 0x00000004
-#define MACP_FUNC_CFG_FLAGS_FCOE_OFFLOAD 0x00000008
-
- u32 iscsi_mac_addr_upper;
- u32 iscsi_mac_addr_lower;
-
- u32 fcoe_mac_addr_upper;
- u32 fcoe_mac_addr_lower;
-
- u32 fcoe_wwn_port_name_upper;
- u32 fcoe_wwn_port_name_lower;
-
- u32 fcoe_wwn_node_name_upper;
- u32 fcoe_wwn_node_name_lower;
-
- u32 preserve_data;
-#define MF_FUNC_CFG_PRESERVE_L2_MAC (1<<0)
-#define MF_FUNC_CFG_PRESERVE_ISCSI_MAC (1<<1)
-#define MF_FUNC_CFG_PRESERVE_FCOE_MAC (1<<2)
-#define MF_FUNC_CFG_PRESERVE_FCOE_WWN_P (1<<3)
-#define MF_FUNC_CFG_PRESERVE_FCOE_WWN_N (1<<4)
-};
-
-struct mf_cfg {
-
- struct shared_mf_cfg shared_mf_config;
- struct port_mf_cfg port_mf_config[PORT_MAX];
- struct func_mf_cfg func_mf_config[E1H_FUNC_MAX];
-
- struct func_ext_cfg func_ext_config[E1H_FUNC_MAX];
-};
-
-
-/****************************************************************************
- * Shared Memory Region *
- ****************************************************************************/
-struct shmem_region { /* SharedMem Offset (size) */
-
- u32 validity_map[PORT_MAX]; /* 0x0 (4*2 = 0x8) */
-#define SHR_MEM_FORMAT_REV_ID ('A'<<24)
-#define SHR_MEM_FORMAT_REV_MASK 0xff000000
- /* validity bits */
-#define SHR_MEM_VALIDITY_PCI_CFG 0x00100000
-#define SHR_MEM_VALIDITY_MB 0x00200000
-#define SHR_MEM_VALIDITY_DEV_INFO 0x00400000
-#define SHR_MEM_VALIDITY_RESERVED 0x00000007
- /* One licensing bit should be set */
-#define SHR_MEM_VALIDITY_LIC_KEY_IN_EFFECT_MASK 0x00000038
-#define SHR_MEM_VALIDITY_LIC_MANUF_KEY_IN_EFFECT 0x00000008
-#define SHR_MEM_VALIDITY_LIC_UPGRADE_KEY_IN_EFFECT 0x00000010
-#define SHR_MEM_VALIDITY_LIC_NO_KEY_IN_EFFECT 0x00000020
- /* Active MFW */
-#define SHR_MEM_VALIDITY_ACTIVE_MFW_UNKNOWN 0x00000000
-#define SHR_MEM_VALIDITY_ACTIVE_MFW_IPMI 0x00000040
-#define SHR_MEM_VALIDITY_ACTIVE_MFW_UMP 0x00000080
-#define SHR_MEM_VALIDITY_ACTIVE_MFW_NCSI 0x000000c0
-#define SHR_MEM_VALIDITY_ACTIVE_MFW_NONE 0x000001c0
-#define SHR_MEM_VALIDITY_ACTIVE_MFW_MASK 0x000001c0
-
- struct shm_dev_info dev_info; /* 0x8 (0x438) */
-
- struct license_key drv_lic_key[PORT_MAX]; /* 0x440 (52*2=0x68) */
-
- /* FW information (for internal FW use) */
- u32 fw_info_fio_offset; /* 0x4a8 (0x4) */
- struct mgmtfw_state mgmtfw_state; /* 0x4ac (0x1b8) */
-
- struct drv_port_mb port_mb[PORT_MAX]; /* 0x664 (16*2=0x20) */
- struct drv_func_mb func_mb[]; /* 0x684
- (44*2/4/8=0x58/0xb0/0x160) */
-
-}; /* 57710 = 0x6dc | 57711 = 0x7E4 | 57712 = 0x734 */
-
-struct fw_flr_ack {
- u32 pf_ack;
- u32 vf_ack[1];
- u32 iov_dis_ack;
-};
-
-struct fw_flr_mb {
- u32 aggint;
- u32 opgen_addr;
- struct fw_flr_ack ack;
-};
-
-/**** SUPPORT FOR SHMEM ARRRAYS ***
- * The SHMEM HSI is aligned on 32 bit boundaries which makes it difficult to
- * define arrays with storage types smaller then unsigned dwords.
- * The macros below add generic support for SHMEM arrays with numeric elements
- * that can span 2,4,8 or 16 bits. The array underlying type is a 32 bit dword
- * array with individual bit-filed elements accessed using shifts and masks.
- *
- */
-
-/* eb is the bitwidth of a single element */
-#define SHMEM_ARRAY_MASK(eb) ((1<<(eb))-1)
-#define SHMEM_ARRAY_ENTRY(i, eb) ((i)/(32/(eb)))
-
-/* the bit-position macro allows the used to flip the order of the arrays
- * elements on a per byte or word boundary.
- *
- * example: an array with 8 entries each 4 bit wide. This array will fit into
- * a single dword. The diagrmas below show the array order of the nibbles.
- *
- * SHMEM_ARRAY_BITPOS(i, 4, 4) defines the stadard ordering:
- *
- * | | | |
- * 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
- * | | | |
- *
- * SHMEM_ARRAY_BITPOS(i, 4, 8) defines a flip ordering per byte:
- *
- * | | | |
- * 1 | 0 | 3 | 2 | 5 | 4 | 7 | 6 |
- * | | | |
- *
- * SHMEM_ARRAY_BITPOS(i, 4, 16) defines a flip ordering per word:
- *
- * | | | |
- * 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 |
- * | | | |
- */
-#define SHMEM_ARRAY_BITPOS(i, eb, fb) \
- ((((32/(fb)) - 1 - ((i)/((fb)/(eb))) % (32/(fb))) * (fb)) + \
- (((i)%((fb)/(eb))) * (eb)))
-
-#define SHMEM_ARRAY_GET(a, i, eb, fb) \
- ((a[SHMEM_ARRAY_ENTRY(i, eb)] >> SHMEM_ARRAY_BITPOS(i, eb, fb)) & \
- SHMEM_ARRAY_MASK(eb))
-
-#define SHMEM_ARRAY_SET(a, i, eb, fb, val) \
-do { \
- a[SHMEM_ARRAY_ENTRY(i, eb)] &= ~(SHMEM_ARRAY_MASK(eb) << \
- SHMEM_ARRAY_BITPOS(i, eb, fb)); \
- a[SHMEM_ARRAY_ENTRY(i, eb)] |= (((val) & SHMEM_ARRAY_MASK(eb)) << \
- SHMEM_ARRAY_BITPOS(i, eb, fb)); \
-} while (0)
-
-
-/****START OF DCBX STRUCTURES DECLARATIONS****/
-#define DCBX_MAX_NUM_PRI_PG_ENTRIES 8
-#define DCBX_PRI_PG_BITWIDTH 4
-#define DCBX_PRI_PG_FBITS 8
-#define DCBX_PRI_PG_GET(a, i) \
- SHMEM_ARRAY_GET(a, i, DCBX_PRI_PG_BITWIDTH, DCBX_PRI_PG_FBITS)
-#define DCBX_PRI_PG_SET(a, i, val) \
- SHMEM_ARRAY_SET(a, i, DCBX_PRI_PG_BITWIDTH, DCBX_PRI_PG_FBITS, val)
-#define DCBX_MAX_NUM_PG_BW_ENTRIES 8
-#define DCBX_BW_PG_BITWIDTH 8
-#define DCBX_PG_BW_GET(a, i) \
- SHMEM_ARRAY_GET(a, i, DCBX_BW_PG_BITWIDTH, DCBX_BW_PG_BITWIDTH)
-#define DCBX_PG_BW_SET(a, i, val) \
- SHMEM_ARRAY_SET(a, i, DCBX_BW_PG_BITWIDTH, DCBX_BW_PG_BITWIDTH, val)
-#define DCBX_STRICT_PRI_PG 15
-#define DCBX_MAX_APP_PROTOCOL 16
-#define FCOE_APP_IDX 0
-#define ISCSI_APP_IDX 1
-#define PREDEFINED_APP_IDX_MAX 2
-
-struct dcbx_ets_feature {
- u32 enabled;
- u32 pg_bw_tbl[2];
- u32 pri_pg_tbl[1];
-};
-
-struct dcbx_pfc_feature {
-#ifdef __BIG_ENDIAN
- u8 pri_en_bitmap;
-#define DCBX_PFC_PRI_0 0x01
-#define DCBX_PFC_PRI_1 0x02
-#define DCBX_PFC_PRI_2 0x04
-#define DCBX_PFC_PRI_3 0x08
-#define DCBX_PFC_PRI_4 0x10
-#define DCBX_PFC_PRI_5 0x20
-#define DCBX_PFC_PRI_6 0x40
-#define DCBX_PFC_PRI_7 0x80
- u8 pfc_caps;
- u8 reserved;
- u8 enabled;
-#elif defined(__LITTLE_ENDIAN)
- u8 enabled;
- u8 reserved;
- u8 pfc_caps;
- u8 pri_en_bitmap;
-#define DCBX_PFC_PRI_0 0x01
-#define DCBX_PFC_PRI_1 0x02
-#define DCBX_PFC_PRI_2 0x04
-#define DCBX_PFC_PRI_3 0x08
-#define DCBX_PFC_PRI_4 0x10
-#define DCBX_PFC_PRI_5 0x20
-#define DCBX_PFC_PRI_6 0x40
-#define DCBX_PFC_PRI_7 0x80
-#endif
-};
-
-struct dcbx_app_priority_entry {
-#ifdef __BIG_ENDIAN
- u16 app_id;
- u8 pri_bitmap;
- u8 appBitfield;
-#define DCBX_APP_ENTRY_VALID 0x01
-#define DCBX_APP_ENTRY_SF_MASK 0x30
-#define DCBX_APP_ENTRY_SF_SHIFT 4
-#define DCBX_APP_SF_ETH_TYPE 0x10
-#define DCBX_APP_SF_PORT 0x20
-#elif defined(__LITTLE_ENDIAN)
- u8 appBitfield;
-#define DCBX_APP_ENTRY_VALID 0x01
-#define DCBX_APP_ENTRY_SF_MASK 0x30
-#define DCBX_APP_ENTRY_SF_SHIFT 4
-#define DCBX_APP_SF_ETH_TYPE 0x10
-#define DCBX_APP_SF_PORT 0x20
- u8 pri_bitmap;
- u16 app_id;
-#endif
-};
-
-struct dcbx_app_priority_feature {
-#ifdef __BIG_ENDIAN
- u8 reserved;
- u8 default_pri;
- u8 tc_supported;
- u8 enabled;
-#elif defined(__LITTLE_ENDIAN)
- u8 enabled;
- u8 tc_supported;
- u8 default_pri;
- u8 reserved;
-#endif
- struct dcbx_app_priority_entry app_pri_tbl[DCBX_MAX_APP_PROTOCOL];
-};
-
-struct dcbx_features {
- struct dcbx_ets_feature ets;
- struct dcbx_pfc_feature pfc;
- struct dcbx_app_priority_feature app;
-};
-
-struct lldp_params {
-#ifdef __BIG_ENDIAN
- u8 msg_fast_tx_interval;
- u8 msg_tx_hold;
- u8 msg_tx_interval;
- u8 admin_status;
-#define LLDP_TX_ONLY 0x01
-#define LLDP_RX_ONLY 0x02
-#define LLDP_TX_RX 0x03
-#define LLDP_DISABLED 0x04
- u8 reserved1;
- u8 tx_fast;
- u8 tx_crd_max;
- u8 tx_crd;
-#elif defined(__LITTLE_ENDIAN)
- u8 admin_status;
-#define LLDP_TX_ONLY 0x01
-#define LLDP_RX_ONLY 0x02
-#define LLDP_TX_RX 0x03
-#define LLDP_DISABLED 0x04
- u8 msg_tx_interval;
- u8 msg_tx_hold;
- u8 msg_fast_tx_interval;
- u8 tx_crd;
- u8 tx_crd_max;
- u8 tx_fast;
- u8 reserved1;
-#endif
-#define REM_CHASSIS_ID_STAT_LEN 4
-#define REM_PORT_ID_STAT_LEN 4
- u32 peer_chassis_id[REM_CHASSIS_ID_STAT_LEN];
- u32 peer_port_id[REM_PORT_ID_STAT_LEN];
-};
-
-struct lldp_dcbx_stat {
-#define LOCAL_CHASSIS_ID_STAT_LEN 2
-#define LOCAL_PORT_ID_STAT_LEN 2
- u32 local_chassis_id[LOCAL_CHASSIS_ID_STAT_LEN];
- u32 local_port_id[LOCAL_PORT_ID_STAT_LEN];
- u32 num_tx_dcbx_pkts;
- u32 num_rx_dcbx_pkts;
-};
-
-struct lldp_admin_mib {
- u32 ver_cfg_flags;
-#define DCBX_ETS_CONFIG_TX_ENABLED 0x00000001
-#define DCBX_PFC_CONFIG_TX_ENABLED 0x00000002
-#define DCBX_APP_CONFIG_TX_ENABLED 0x00000004
-#define DCBX_ETS_RECO_TX_ENABLED 0x00000008
-#define DCBX_ETS_RECO_VALID 0x00000010
-#define DCBX_ETS_WILLING 0x00000020
-#define DCBX_PFC_WILLING 0x00000040
-#define DCBX_APP_WILLING 0x00000080
-#define DCBX_VERSION_CEE 0x00000100
-#define DCBX_VERSION_IEEE 0x00000200
-#define DCBX_DCBX_ENABLED 0x00000400
-#define DCBX_CEE_VERSION_MASK 0x0000f000
-#define DCBX_CEE_VERSION_SHIFT 12
-#define DCBX_CEE_MAX_VERSION_MASK 0x000f0000
-#define DCBX_CEE_MAX_VERSION_SHIFT 16
- struct dcbx_features features;
-};
-
-struct lldp_remote_mib {
- u32 prefix_seq_num;
- u32 flags;
-#define DCBX_ETS_TLV_RX 0x00000001
-#define DCBX_PFC_TLV_RX 0x00000002
-#define DCBX_APP_TLV_RX 0x00000004
-#define DCBX_ETS_RX_ERROR 0x00000010
-#define DCBX_PFC_RX_ERROR 0x00000020
-#define DCBX_APP_RX_ERROR 0x00000040
-#define DCBX_ETS_REM_WILLING 0x00000100
-#define DCBX_PFC_REM_WILLING 0x00000200
-#define DCBX_APP_REM_WILLING 0x00000400
-#define DCBX_REMOTE_ETS_RECO_VALID 0x00001000
- struct dcbx_features features;
- u32 suffix_seq_num;
-};
-
-struct lldp_local_mib {
- u32 prefix_seq_num;
- u32 error;
-#define DCBX_LOCAL_ETS_ERROR 0x00000001
-#define DCBX_LOCAL_PFC_ERROR 0x00000002
-#define DCBX_LOCAL_APP_ERROR 0x00000004
-#define DCBX_LOCAL_PFC_MISMATCH 0x00000010
-#define DCBX_LOCAL_APP_MISMATCH 0x00000020
- struct dcbx_features features;
- u32 suffix_seq_num;
-};
-/***END OF DCBX STRUCTURES DECLARATIONS***/
-
-struct shmem2_region {
-
- u32 size;
-
- u32 dcc_support;
-#define SHMEM_DCC_SUPPORT_NONE 0x00000000
-#define SHMEM_DCC_SUPPORT_DISABLE_ENABLE_PF_TLV 0x00000001
-#define SHMEM_DCC_SUPPORT_BANDWIDTH_ALLOCATION_TLV 0x00000004
-#define SHMEM_DCC_SUPPORT_CHANGE_MAC_ADDRESS_TLV 0x00000008
-#define SHMEM_DCC_SUPPORT_SET_PROTOCOL_TLV 0x00000040
-#define SHMEM_DCC_SUPPORT_SET_PRIORITY_TLV 0x00000080
-#define SHMEM_DCC_SUPPORT_DEFAULT SHMEM_DCC_SUPPORT_NONE
- u32 ext_phy_fw_version2[PORT_MAX];
- /*
- * For backwards compatibility, if the mf_cfg_addr does not exist
- * (the size filed is smaller than 0xc) the mf_cfg resides at the
- * end of struct shmem_region
- */
- u32 mf_cfg_addr;
-#define SHMEM_MF_CFG_ADDR_NONE 0x00000000
-
- struct fw_flr_mb flr_mb;
- u32 dcbx_lldp_params_offset;
-#define SHMEM_LLDP_DCBX_PARAMS_NONE 0x00000000
- u32 dcbx_neg_res_offset;
-#define SHMEM_DCBX_NEG_RES_NONE 0x00000000
- u32 dcbx_remote_mib_offset;
-#define SHMEM_DCBX_REMOTE_MIB_NONE 0x00000000
- /*
- * The other shmemX_base_addr holds the other path's shmem address
- * required for example in case of common phy init, or for path1 to know
- * the address of mcp debug trace which is located in offset from shmem
- * of path0
- */
- u32 other_shmem_base_addr;
- u32 other_shmem2_base_addr;
- u32 reserved1[E2_VF_MAX / 32];
- u32 reserved2[E2_FUNC_MAX][E2_VF_MAX / 32];
- u32 dcbx_lldp_dcbx_stat_offset;
-#define SHMEM_LLDP_DCBX_STAT_NONE 0x00000000
-};
-
-
-struct emac_stats {
- u32 rx_stat_ifhcinoctets;
- u32 rx_stat_ifhcinbadoctets;
- u32 rx_stat_etherstatsfragments;
- u32 rx_stat_ifhcinucastpkts;
- u32 rx_stat_ifhcinmulticastpkts;
- u32 rx_stat_ifhcinbroadcastpkts;
- u32 rx_stat_dot3statsfcserrors;
- u32 rx_stat_dot3statsalignmenterrors;
- u32 rx_stat_dot3statscarriersenseerrors;
- u32 rx_stat_xonpauseframesreceived;
- u32 rx_stat_xoffpauseframesreceived;
- u32 rx_stat_maccontrolframesreceived;
- u32 rx_stat_xoffstateentered;
- u32 rx_stat_dot3statsframestoolong;
- u32 rx_stat_etherstatsjabbers;
- u32 rx_stat_etherstatsundersizepkts;
- u32 rx_stat_etherstatspkts64octets;
- u32 rx_stat_etherstatspkts65octetsto127octets;
- u32 rx_stat_etherstatspkts128octetsto255octets;
- u32 rx_stat_etherstatspkts256octetsto511octets;
- u32 rx_stat_etherstatspkts512octetsto1023octets;
- u32 rx_stat_etherstatspkts1024octetsto1522octets;
- u32 rx_stat_etherstatspktsover1522octets;
-
- u32 rx_stat_falsecarriererrors;
-
- u32 tx_stat_ifhcoutoctets;
- u32 tx_stat_ifhcoutbadoctets;
- u32 tx_stat_etherstatscollisions;
- u32 tx_stat_outxonsent;
- u32 tx_stat_outxoffsent;
- u32 tx_stat_flowcontroldone;
- u32 tx_stat_dot3statssinglecollisionframes;
- u32 tx_stat_dot3statsmultiplecollisionframes;
- u32 tx_stat_dot3statsdeferredtransmissions;
- u32 tx_stat_dot3statsexcessivecollisions;
- u32 tx_stat_dot3statslatecollisions;
- u32 tx_stat_ifhcoutucastpkts;
- u32 tx_stat_ifhcoutmulticastpkts;
- u32 tx_stat_ifhcoutbroadcastpkts;
- u32 tx_stat_etherstatspkts64octets;
- u32 tx_stat_etherstatspkts65octetsto127octets;
- u32 tx_stat_etherstatspkts128octetsto255octets;
- u32 tx_stat_etherstatspkts256octetsto511octets;
- u32 tx_stat_etherstatspkts512octetsto1023octets;
- u32 tx_stat_etherstatspkts1024octetsto1522octets;
- u32 tx_stat_etherstatspktsover1522octets;
- u32 tx_stat_dot3statsinternalmactransmiterrors;
-};
-
-
-struct bmac1_stats {
- u32 tx_stat_gtpkt_lo;
- u32 tx_stat_gtpkt_hi;
- u32 tx_stat_gtxpf_lo;
- u32 tx_stat_gtxpf_hi;
- u32 tx_stat_gtfcs_lo;
- u32 tx_stat_gtfcs_hi;
- u32 tx_stat_gtmca_lo;
- u32 tx_stat_gtmca_hi;
- u32 tx_stat_gtbca_lo;
- u32 tx_stat_gtbca_hi;
- u32 tx_stat_gtfrg_lo;
- u32 tx_stat_gtfrg_hi;
- u32 tx_stat_gtovr_lo;
- u32 tx_stat_gtovr_hi;
- u32 tx_stat_gt64_lo;
- u32 tx_stat_gt64_hi;
- u32 tx_stat_gt127_lo;
- u32 tx_stat_gt127_hi;
- u32 tx_stat_gt255_lo;
- u32 tx_stat_gt255_hi;
- u32 tx_stat_gt511_lo;
- u32 tx_stat_gt511_hi;
- u32 tx_stat_gt1023_lo;
- u32 tx_stat_gt1023_hi;
- u32 tx_stat_gt1518_lo;
- u32 tx_stat_gt1518_hi;
- u32 tx_stat_gt2047_lo;
- u32 tx_stat_gt2047_hi;
- u32 tx_stat_gt4095_lo;
- u32 tx_stat_gt4095_hi;
- u32 tx_stat_gt9216_lo;
- u32 tx_stat_gt9216_hi;
- u32 tx_stat_gt16383_lo;
- u32 tx_stat_gt16383_hi;
- u32 tx_stat_gtmax_lo;
- u32 tx_stat_gtmax_hi;
- u32 tx_stat_gtufl_lo;
- u32 tx_stat_gtufl_hi;
- u32 tx_stat_gterr_lo;
- u32 tx_stat_gterr_hi;
- u32 tx_stat_gtbyt_lo;
- u32 tx_stat_gtbyt_hi;
-
- u32 rx_stat_gr64_lo;
- u32 rx_stat_gr64_hi;
- u32 rx_stat_gr127_lo;
- u32 rx_stat_gr127_hi;
- u32 rx_stat_gr255_lo;
- u32 rx_stat_gr255_hi;
- u32 rx_stat_gr511_lo;
- u32 rx_stat_gr511_hi;
- u32 rx_stat_gr1023_lo;
- u32 rx_stat_gr1023_hi;
- u32 rx_stat_gr1518_lo;
- u32 rx_stat_gr1518_hi;
- u32 rx_stat_gr2047_lo;
- u32 rx_stat_gr2047_hi;
- u32 rx_stat_gr4095_lo;
- u32 rx_stat_gr4095_hi;
- u32 rx_stat_gr9216_lo;
- u32 rx_stat_gr9216_hi;
- u32 rx_stat_gr16383_lo;
- u32 rx_stat_gr16383_hi;
- u32 rx_stat_grmax_lo;
- u32 rx_stat_grmax_hi;
- u32 rx_stat_grpkt_lo;
- u32 rx_stat_grpkt_hi;
- u32 rx_stat_grfcs_lo;
- u32 rx_stat_grfcs_hi;
- u32 rx_stat_grmca_lo;
- u32 rx_stat_grmca_hi;
- u32 rx_stat_grbca_lo;
- u32 rx_stat_grbca_hi;
- u32 rx_stat_grxcf_lo;
- u32 rx_stat_grxcf_hi;
- u32 rx_stat_grxpf_lo;
- u32 rx_stat_grxpf_hi;
- u32 rx_stat_grxuo_lo;
- u32 rx_stat_grxuo_hi;
- u32 rx_stat_grjbr_lo;
- u32 rx_stat_grjbr_hi;
- u32 rx_stat_grovr_lo;
- u32 rx_stat_grovr_hi;
- u32 rx_stat_grflr_lo;
- u32 rx_stat_grflr_hi;
- u32 rx_stat_grmeg_lo;
- u32 rx_stat_grmeg_hi;
- u32 rx_stat_grmeb_lo;
- u32 rx_stat_grmeb_hi;
- u32 rx_stat_grbyt_lo;
- u32 rx_stat_grbyt_hi;
- u32 rx_stat_grund_lo;
- u32 rx_stat_grund_hi;
- u32 rx_stat_grfrg_lo;
- u32 rx_stat_grfrg_hi;
- u32 rx_stat_grerb_lo;
- u32 rx_stat_grerb_hi;
- u32 rx_stat_grfre_lo;
- u32 rx_stat_grfre_hi;
- u32 rx_stat_gripj_lo;
- u32 rx_stat_gripj_hi;
-};
-
-struct bmac2_stats {
- u32 tx_stat_gtpk_lo; /* gtpok */
- u32 tx_stat_gtpk_hi; /* gtpok */
- u32 tx_stat_gtxpf_lo; /* gtpf */
- u32 tx_stat_gtxpf_hi; /* gtpf */
- u32 tx_stat_gtpp_lo; /* NEW BMAC2 */
- u32 tx_stat_gtpp_hi; /* NEW BMAC2 */
- u32 tx_stat_gtfcs_lo;
- u32 tx_stat_gtfcs_hi;
- u32 tx_stat_gtuca_lo; /* NEW BMAC2 */
- u32 tx_stat_gtuca_hi; /* NEW BMAC2 */
- u32 tx_stat_gtmca_lo;
- u32 tx_stat_gtmca_hi;
- u32 tx_stat_gtbca_lo;
- u32 tx_stat_gtbca_hi;
- u32 tx_stat_gtovr_lo;
- u32 tx_stat_gtovr_hi;
- u32 tx_stat_gtfrg_lo;
- u32 tx_stat_gtfrg_hi;
- u32 tx_stat_gtpkt1_lo; /* gtpkt */
- u32 tx_stat_gtpkt1_hi; /* gtpkt */
- u32 tx_stat_gt64_lo;
- u32 tx_stat_gt64_hi;
- u32 tx_stat_gt127_lo;
- u32 tx_stat_gt127_hi;
- u32 tx_stat_gt255_lo;
- u32 tx_stat_gt255_hi;
- u32 tx_stat_gt511_lo;
- u32 tx_stat_gt511_hi;
- u32 tx_stat_gt1023_lo;
- u32 tx_stat_gt1023_hi;
- u32 tx_stat_gt1518_lo;
- u32 tx_stat_gt1518_hi;
- u32 tx_stat_gt2047_lo;
- u32 tx_stat_gt2047_hi;
- u32 tx_stat_gt4095_lo;
- u32 tx_stat_gt4095_hi;
- u32 tx_stat_gt9216_lo;
- u32 tx_stat_gt9216_hi;
- u32 tx_stat_gt16383_lo;
- u32 tx_stat_gt16383_hi;
- u32 tx_stat_gtmax_lo;
- u32 tx_stat_gtmax_hi;
- u32 tx_stat_gtufl_lo;
- u32 tx_stat_gtufl_hi;
- u32 tx_stat_gterr_lo;
- u32 tx_stat_gterr_hi;
- u32 tx_stat_gtbyt_lo;
- u32 tx_stat_gtbyt_hi;
-
- u32 rx_stat_gr64_lo;
- u32 rx_stat_gr64_hi;
- u32 rx_stat_gr127_lo;
- u32 rx_stat_gr127_hi;
- u32 rx_stat_gr255_lo;
- u32 rx_stat_gr255_hi;
- u32 rx_stat_gr511_lo;
- u32 rx_stat_gr511_hi;
- u32 rx_stat_gr1023_lo;
- u32 rx_stat_gr1023_hi;
- u32 rx_stat_gr1518_lo;
- u32 rx_stat_gr1518_hi;
- u32 rx_stat_gr2047_lo;
- u32 rx_stat_gr2047_hi;
- u32 rx_stat_gr4095_lo;
- u32 rx_stat_gr4095_hi;
- u32 rx_stat_gr9216_lo;
- u32 rx_stat_gr9216_hi;
- u32 rx_stat_gr16383_lo;
- u32 rx_stat_gr16383_hi;
- u32 rx_stat_grmax_lo;
- u32 rx_stat_grmax_hi;
- u32 rx_stat_grpkt_lo;
- u32 rx_stat_grpkt_hi;
- u32 rx_stat_grfcs_lo;
- u32 rx_stat_grfcs_hi;
- u32 rx_stat_gruca_lo;
- u32 rx_stat_gruca_hi;
- u32 rx_stat_grmca_lo;
- u32 rx_stat_grmca_hi;
- u32 rx_stat_grbca_lo;
- u32 rx_stat_grbca_hi;
- u32 rx_stat_grxpf_lo; /* grpf */
- u32 rx_stat_grxpf_hi; /* grpf */
- u32 rx_stat_grpp_lo;
- u32 rx_stat_grpp_hi;
- u32 rx_stat_grxuo_lo; /* gruo */
- u32 rx_stat_grxuo_hi; /* gruo */
- u32 rx_stat_grjbr_lo;
- u32 rx_stat_grjbr_hi;
- u32 rx_stat_grovr_lo;
- u32 rx_stat_grovr_hi;
- u32 rx_stat_grxcf_lo; /* grcf */
- u32 rx_stat_grxcf_hi; /* grcf */
- u32 rx_stat_grflr_lo;
- u32 rx_stat_grflr_hi;
- u32 rx_stat_grpok_lo;
- u32 rx_stat_grpok_hi;
- u32 rx_stat_grmeg_lo;
- u32 rx_stat_grmeg_hi;
- u32 rx_stat_grmeb_lo;
- u32 rx_stat_grmeb_hi;
- u32 rx_stat_grbyt_lo;
- u32 rx_stat_grbyt_hi;
- u32 rx_stat_grund_lo;
- u32 rx_stat_grund_hi;
- u32 rx_stat_grfrg_lo;
- u32 rx_stat_grfrg_hi;
- u32 rx_stat_grerb_lo; /* grerrbyt */
- u32 rx_stat_grerb_hi; /* grerrbyt */
- u32 rx_stat_grfre_lo; /* grfrerr */
- u32 rx_stat_grfre_hi; /* grfrerr */
- u32 rx_stat_gripj_lo;
- u32 rx_stat_gripj_hi;
-};
-
-union mac_stats {
- struct emac_stats emac_stats;
- struct bmac1_stats bmac1_stats;
- struct bmac2_stats bmac2_stats;
-};
-
-
-struct mac_stx {
- /* in_bad_octets */
- u32 rx_stat_ifhcinbadoctets_hi;
- u32 rx_stat_ifhcinbadoctets_lo;
-
- /* out_bad_octets */
- u32 tx_stat_ifhcoutbadoctets_hi;
- u32 tx_stat_ifhcoutbadoctets_lo;
-
- /* crc_receive_errors */
- u32 rx_stat_dot3statsfcserrors_hi;
- u32 rx_stat_dot3statsfcserrors_lo;
- /* alignment_errors */
- u32 rx_stat_dot3statsalignmenterrors_hi;
- u32 rx_stat_dot3statsalignmenterrors_lo;
- /* carrier_sense_errors */
- u32 rx_stat_dot3statscarriersenseerrors_hi;
- u32 rx_stat_dot3statscarriersenseerrors_lo;
- /* false_carrier_detections */
- u32 rx_stat_falsecarriererrors_hi;
- u32 rx_stat_falsecarriererrors_lo;
-
- /* runt_packets_received */
- u32 rx_stat_etherstatsundersizepkts_hi;
- u32 rx_stat_etherstatsundersizepkts_lo;
- /* jabber_packets_received */
- u32 rx_stat_dot3statsframestoolong_hi;
- u32 rx_stat_dot3statsframestoolong_lo;
-
- /* error_runt_packets_received */
- u32 rx_stat_etherstatsfragments_hi;
- u32 rx_stat_etherstatsfragments_lo;
- /* error_jabber_packets_received */
- u32 rx_stat_etherstatsjabbers_hi;
- u32 rx_stat_etherstatsjabbers_lo;
-
- /* control_frames_received */
- u32 rx_stat_maccontrolframesreceived_hi;
- u32 rx_stat_maccontrolframesreceived_lo;
- u32 rx_stat_bmac_xpf_hi;
- u32 rx_stat_bmac_xpf_lo;
- u32 rx_stat_bmac_xcf_hi;
- u32 rx_stat_bmac_xcf_lo;
-
- /* xoff_state_entered */
- u32 rx_stat_xoffstateentered_hi;
- u32 rx_stat_xoffstateentered_lo;
- /* pause_xon_frames_received */
- u32 rx_stat_xonpauseframesreceived_hi;
- u32 rx_stat_xonpauseframesreceived_lo;
- /* pause_xoff_frames_received */
- u32 rx_stat_xoffpauseframesreceived_hi;
- u32 rx_stat_xoffpauseframesreceived_lo;
- /* pause_xon_frames_transmitted */
- u32 tx_stat_outxonsent_hi;
- u32 tx_stat_outxonsent_lo;
- /* pause_xoff_frames_transmitted */
- u32 tx_stat_outxoffsent_hi;
- u32 tx_stat_outxoffsent_lo;
- /* flow_control_done */
- u32 tx_stat_flowcontroldone_hi;
- u32 tx_stat_flowcontroldone_lo;
-
- /* ether_stats_collisions */
- u32 tx_stat_etherstatscollisions_hi;
- u32 tx_stat_etherstatscollisions_lo;
- /* single_collision_transmit_frames */
- u32 tx_stat_dot3statssinglecollisionframes_hi;
- u32 tx_stat_dot3statssinglecollisionframes_lo;
- /* multiple_collision_transmit_frames */
- u32 tx_stat_dot3statsmultiplecollisionframes_hi;
- u32 tx_stat_dot3statsmultiplecollisionframes_lo;
- /* deferred_transmissions */
- u32 tx_stat_dot3statsdeferredtransmissions_hi;
- u32 tx_stat_dot3statsdeferredtransmissions_lo;
- /* excessive_collision_frames */
- u32 tx_stat_dot3statsexcessivecollisions_hi;
- u32 tx_stat_dot3statsexcessivecollisions_lo;
- /* late_collision_frames */
- u32 tx_stat_dot3statslatecollisions_hi;
- u32 tx_stat_dot3statslatecollisions_lo;
-
- /* frames_transmitted_64_bytes */
- u32 tx_stat_etherstatspkts64octets_hi;
- u32 tx_stat_etherstatspkts64octets_lo;
- /* frames_transmitted_65_127_bytes */
- u32 tx_stat_etherstatspkts65octetsto127octets_hi;
- u32 tx_stat_etherstatspkts65octetsto127octets_lo;
- /* frames_transmitted_128_255_bytes */
- u32 tx_stat_etherstatspkts128octetsto255octets_hi;
- u32 tx_stat_etherstatspkts128octetsto255octets_lo;
- /* frames_transmitted_256_511_bytes */
- u32 tx_stat_etherstatspkts256octetsto511octets_hi;
- u32 tx_stat_etherstatspkts256octetsto511octets_lo;
- /* frames_transmitted_512_1023_bytes */
- u32 tx_stat_etherstatspkts512octetsto1023octets_hi;
- u32 tx_stat_etherstatspkts512octetsto1023octets_lo;
- /* frames_transmitted_1024_1522_bytes */
- u32 tx_stat_etherstatspkts1024octetsto1522octets_hi;
- u32 tx_stat_etherstatspkts1024octetsto1522octets_lo;
- /* frames_transmitted_1523_9022_bytes */
- u32 tx_stat_etherstatspktsover1522octets_hi;
- u32 tx_stat_etherstatspktsover1522octets_lo;
- u32 tx_stat_bmac_2047_hi;
- u32 tx_stat_bmac_2047_lo;
- u32 tx_stat_bmac_4095_hi;
- u32 tx_stat_bmac_4095_lo;
- u32 tx_stat_bmac_9216_hi;
- u32 tx_stat_bmac_9216_lo;
- u32 tx_stat_bmac_16383_hi;
- u32 tx_stat_bmac_16383_lo;
-
- /* internal_mac_transmit_errors */
- u32 tx_stat_dot3statsinternalmactransmiterrors_hi;
- u32 tx_stat_dot3statsinternalmactransmiterrors_lo;
-
- /* if_out_discards */
- u32 tx_stat_bmac_ufl_hi;
- u32 tx_stat_bmac_ufl_lo;
-};
-
-
-#define MAC_STX_IDX_MAX 2
-
-struct host_port_stats {
- u32 host_port_stats_start;
-
- struct mac_stx mac_stx[MAC_STX_IDX_MAX];
-
- u32 brb_drop_hi;
- u32 brb_drop_lo;
-
- u32 host_port_stats_end;
-};
-
-
-struct host_func_stats {
- u32 host_func_stats_start;
-
- u32 total_bytes_received_hi;
- u32 total_bytes_received_lo;
-
- u32 total_bytes_transmitted_hi;
- u32 total_bytes_transmitted_lo;
-
- u32 total_unicast_packets_received_hi;
- u32 total_unicast_packets_received_lo;
-
- u32 total_multicast_packets_received_hi;
- u32 total_multicast_packets_received_lo;
-
- u32 total_broadcast_packets_received_hi;
- u32 total_broadcast_packets_received_lo;
-
- u32 total_unicast_packets_transmitted_hi;
- u32 total_unicast_packets_transmitted_lo;
-
- u32 total_multicast_packets_transmitted_hi;
- u32 total_multicast_packets_transmitted_lo;
-
- u32 total_broadcast_packets_transmitted_hi;
- u32 total_broadcast_packets_transmitted_lo;
-
- u32 valid_bytes_received_hi;
- u32 valid_bytes_received_lo;
-
- u32 host_func_stats_end;
-};
-
-
-#define BCM_5710_FW_MAJOR_VERSION 6
-#define BCM_5710_FW_MINOR_VERSION 2
-#define BCM_5710_FW_REVISION_VERSION 9
-#define BCM_5710_FW_ENGINEERING_VERSION 0
-#define BCM_5710_FW_COMPILE_FLAGS 1
-
-
-/*
- * attention bits
- */
-struct atten_sp_status_block {
- __le32 attn_bits;
- __le32 attn_bits_ack;
- u8 status_block_id;
- u8 reserved0;
- __le16 attn_bits_index;
- __le32 reserved1;
-};
-
-
-/*
- * common data for all protocols
- */
-struct doorbell_hdr {
- u8 header;
-#define DOORBELL_HDR_RX (0x1<<0)
-#define DOORBELL_HDR_RX_SHIFT 0
-#define DOORBELL_HDR_DB_TYPE (0x1<<1)
-#define DOORBELL_HDR_DB_TYPE_SHIFT 1
-#define DOORBELL_HDR_DPM_SIZE (0x3<<2)
-#define DOORBELL_HDR_DPM_SIZE_SHIFT 2
-#define DOORBELL_HDR_CONN_TYPE (0xF<<4)
-#define DOORBELL_HDR_CONN_TYPE_SHIFT 4
-};
-
-/*
- * doorbell message sent to the chip
- */
-struct doorbell {
-#if defined(__BIG_ENDIAN)
- u16 zero_fill2;
- u8 zero_fill1;
- struct doorbell_hdr header;
-#elif defined(__LITTLE_ENDIAN)
- struct doorbell_hdr header;
- u8 zero_fill1;
- u16 zero_fill2;
-#endif
-};
-
-
-/*
- * doorbell message sent to the chip
- */
-struct doorbell_set_prod {
-#if defined(__BIG_ENDIAN)
- u16 prod;
- u8 zero_fill1;
- struct doorbell_hdr header;
-#elif defined(__LITTLE_ENDIAN)
- struct doorbell_hdr header;
- u8 zero_fill1;
- u16 prod;
-#endif
-};
-
-
-/*
- * 3 lines. status block
- */
-struct hc_status_block_e1x {
- __le16 index_values[HC_SB_MAX_INDICES_E1X];
- __le16 running_index[HC_SB_MAX_SM];
- u32 rsrv;
-};
-
-/*
- * host status block
- */
-struct host_hc_status_block_e1x {
- struct hc_status_block_e1x sb;
-};
-
-
-/*
- * 3 lines. status block
- */
-struct hc_status_block_e2 {
- __le16 index_values[HC_SB_MAX_INDICES_E2];
- __le16 running_index[HC_SB_MAX_SM];
- u32 reserved;
-};
-
-/*
- * host status block
- */
-struct host_hc_status_block_e2 {
- struct hc_status_block_e2 sb;
-};
-
-
-/*
- * 5 lines. slow-path status block
- */
-struct hc_sp_status_block {
- __le16 index_values[HC_SP_SB_MAX_INDICES];
- __le16 running_index;
- __le16 rsrv;
- u32 rsrv1;
-};
-
-/*
- * host status block
- */
-struct host_sp_status_block {
- struct atten_sp_status_block atten_status_block;
- struct hc_sp_status_block sp_sb;
-};
-
-
-/*
- * IGU driver acknowledgment register
- */
-struct igu_ack_register {
-#if defined(__BIG_ENDIAN)
- u16 sb_id_and_flags;
-#define IGU_ACK_REGISTER_STATUS_BLOCK_ID (0x1F<<0)
-#define IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT 0
-#define IGU_ACK_REGISTER_STORM_ID (0x7<<5)
-#define IGU_ACK_REGISTER_STORM_ID_SHIFT 5
-#define IGU_ACK_REGISTER_UPDATE_INDEX (0x1<<8)
-#define IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT 8
-#define IGU_ACK_REGISTER_INTERRUPT_MODE (0x3<<9)
-#define IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT 9
-#define IGU_ACK_REGISTER_RESERVED (0x1F<<11)
-#define IGU_ACK_REGISTER_RESERVED_SHIFT 11
- u16 status_block_index;
-#elif defined(__LITTLE_ENDIAN)
- u16 status_block_index;
- u16 sb_id_and_flags;
-#define IGU_ACK_REGISTER_STATUS_BLOCK_ID (0x1F<<0)
-#define IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT 0
-#define IGU_ACK_REGISTER_STORM_ID (0x7<<5)
-#define IGU_ACK_REGISTER_STORM_ID_SHIFT 5
-#define IGU_ACK_REGISTER_UPDATE_INDEX (0x1<<8)
-#define IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT 8
-#define IGU_ACK_REGISTER_INTERRUPT_MODE (0x3<<9)
-#define IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT 9
-#define IGU_ACK_REGISTER_RESERVED (0x1F<<11)
-#define IGU_ACK_REGISTER_RESERVED_SHIFT 11
-#endif
-};
-
-
-/*
- * IGU driver acknowledgement register
- */
-struct igu_backward_compatible {
- u32 sb_id_and_flags;
-#define IGU_BACKWARD_COMPATIBLE_SB_INDEX (0xFFFF<<0)
-#define IGU_BACKWARD_COMPATIBLE_SB_INDEX_SHIFT 0
-#define IGU_BACKWARD_COMPATIBLE_SB_SELECT (0x1F<<16)
-#define IGU_BACKWARD_COMPATIBLE_SB_SELECT_SHIFT 16
-#define IGU_BACKWARD_COMPATIBLE_SEGMENT_ACCESS (0x7<<21)
-#define IGU_BACKWARD_COMPATIBLE_SEGMENT_ACCESS_SHIFT 21
-#define IGU_BACKWARD_COMPATIBLE_BUPDATE (0x1<<24)
-#define IGU_BACKWARD_COMPATIBLE_BUPDATE_SHIFT 24
-#define IGU_BACKWARD_COMPATIBLE_ENABLE_INT (0x3<<25)
-#define IGU_BACKWARD_COMPATIBLE_ENABLE_INT_SHIFT 25
-#define IGU_BACKWARD_COMPATIBLE_RESERVED_0 (0x1F<<27)
-#define IGU_BACKWARD_COMPATIBLE_RESERVED_0_SHIFT 27
- u32 reserved_2;
-};
-
-
-/*
- * IGU driver acknowledgement register
- */
-struct igu_regular {
- u32 sb_id_and_flags;
-#define IGU_REGULAR_SB_INDEX (0xFFFFF<<0)
-#define IGU_REGULAR_SB_INDEX_SHIFT 0
-#define IGU_REGULAR_RESERVED0 (0x1<<20)
-#define IGU_REGULAR_RESERVED0_SHIFT 20
-#define IGU_REGULAR_SEGMENT_ACCESS (0x7<<21)
-#define IGU_REGULAR_SEGMENT_ACCESS_SHIFT 21
-#define IGU_REGULAR_BUPDATE (0x1<<24)
-#define IGU_REGULAR_BUPDATE_SHIFT 24
-#define IGU_REGULAR_ENABLE_INT (0x3<<25)
-#define IGU_REGULAR_ENABLE_INT_SHIFT 25
-#define IGU_REGULAR_RESERVED_1 (0x1<<27)
-#define IGU_REGULAR_RESERVED_1_SHIFT 27
-#define IGU_REGULAR_CLEANUP_TYPE (0x3<<28)
-#define IGU_REGULAR_CLEANUP_TYPE_SHIFT 28
-#define IGU_REGULAR_CLEANUP_SET (0x1<<30)
-#define IGU_REGULAR_CLEANUP_SET_SHIFT 30
-#define IGU_REGULAR_BCLEANUP (0x1<<31)
-#define IGU_REGULAR_BCLEANUP_SHIFT 31
- u32 reserved_2;
-};
-
-/*
- * IGU driver acknowledgement register
- */
-union igu_consprod_reg {
- struct igu_regular regular;
- struct igu_backward_compatible backward_compatible;
-};
-
-
-/*
- * Control register for the IGU command register
- */
-struct igu_ctrl_reg {
- u32 ctrl_data;
-#define IGU_CTRL_REG_ADDRESS (0xFFF<<0)
-#define IGU_CTRL_REG_ADDRESS_SHIFT 0
-#define IGU_CTRL_REG_FID (0x7F<<12)
-#define IGU_CTRL_REG_FID_SHIFT 12
-#define IGU_CTRL_REG_RESERVED (0x1<<19)
-#define IGU_CTRL_REG_RESERVED_SHIFT 19
-#define IGU_CTRL_REG_TYPE (0x1<<20)
-#define IGU_CTRL_REG_TYPE_SHIFT 20
-#define IGU_CTRL_REG_UNUSED (0x7FF<<21)
-#define IGU_CTRL_REG_UNUSED_SHIFT 21
-};
-
-
-/*
- * Parser parsing flags field
- */
-struct parsing_flags {
- __le16 flags;
-#define PARSING_FLAGS_ETHERNET_ADDRESS_TYPE (0x1<<0)
-#define PARSING_FLAGS_ETHERNET_ADDRESS_TYPE_SHIFT 0
-#define PARSING_FLAGS_VLAN (0x1<<1)
-#define PARSING_FLAGS_VLAN_SHIFT 1
-#define PARSING_FLAGS_EXTRA_VLAN (0x1<<2)
-#define PARSING_FLAGS_EXTRA_VLAN_SHIFT 2
-#define PARSING_FLAGS_OVER_ETHERNET_PROTOCOL (0x3<<3)
-#define PARSING_FLAGS_OVER_ETHERNET_PROTOCOL_SHIFT 3
-#define PARSING_FLAGS_IP_OPTIONS (0x1<<5)
-#define PARSING_FLAGS_IP_OPTIONS_SHIFT 5
-#define PARSING_FLAGS_FRAGMENTATION_STATUS (0x1<<6)
-#define PARSING_FLAGS_FRAGMENTATION_STATUS_SHIFT 6
-#define PARSING_FLAGS_OVER_IP_PROTOCOL (0x3<<7)
-#define PARSING_FLAGS_OVER_IP_PROTOCOL_SHIFT 7
-#define PARSING_FLAGS_PURE_ACK_INDICATION (0x1<<9)
-#define PARSING_FLAGS_PURE_ACK_INDICATION_SHIFT 9
-#define PARSING_FLAGS_TCP_OPTIONS_EXIST (0x1<<10)
-#define PARSING_FLAGS_TCP_OPTIONS_EXIST_SHIFT 10
-#define PARSING_FLAGS_TIME_STAMP_EXIST_FLAG (0x1<<11)
-#define PARSING_FLAGS_TIME_STAMP_EXIST_FLAG_SHIFT 11
-#define PARSING_FLAGS_CONNECTION_MATCH (0x1<<12)
-#define PARSING_FLAGS_CONNECTION_MATCH_SHIFT 12
-#define PARSING_FLAGS_LLC_SNAP (0x1<<13)
-#define PARSING_FLAGS_LLC_SNAP_SHIFT 13
-#define PARSING_FLAGS_RESERVED0 (0x3<<14)
-#define PARSING_FLAGS_RESERVED0_SHIFT 14
-};
-
-
-struct regpair {
- __le32 lo;
- __le32 hi;
-};
-
-
-/*
- * dmae command structure
- */
-struct dmae_command {
- u32 opcode;
-#define DMAE_COMMAND_SRC (0x1<<0)
-#define DMAE_COMMAND_SRC_SHIFT 0
-#define DMAE_COMMAND_DST (0x3<<1)
-#define DMAE_COMMAND_DST_SHIFT 1
-#define DMAE_COMMAND_C_DST (0x1<<3)
-#define DMAE_COMMAND_C_DST_SHIFT 3
-#define DMAE_COMMAND_C_TYPE_ENABLE (0x1<<4)
-#define DMAE_COMMAND_C_TYPE_ENABLE_SHIFT 4
-#define DMAE_COMMAND_C_TYPE_CRC_ENABLE (0x1<<5)
-#define DMAE_COMMAND_C_TYPE_CRC_ENABLE_SHIFT 5
-#define DMAE_COMMAND_C_TYPE_CRC_OFFSET (0x7<<6)
-#define DMAE_COMMAND_C_TYPE_CRC_OFFSET_SHIFT 6
-#define DMAE_COMMAND_ENDIANITY (0x3<<9)
-#define DMAE_COMMAND_ENDIANITY_SHIFT 9
-#define DMAE_COMMAND_PORT (0x1<<11)
-#define DMAE_COMMAND_PORT_SHIFT 11
-#define DMAE_COMMAND_CRC_RESET (0x1<<12)
-#define DMAE_COMMAND_CRC_RESET_SHIFT 12
-#define DMAE_COMMAND_SRC_RESET (0x1<<13)
-#define DMAE_COMMAND_SRC_RESET_SHIFT 13
-#define DMAE_COMMAND_DST_RESET (0x1<<14)
-#define DMAE_COMMAND_DST_RESET_SHIFT 14
-#define DMAE_COMMAND_E1HVN (0x3<<15)
-#define DMAE_COMMAND_E1HVN_SHIFT 15
-#define DMAE_COMMAND_DST_VN (0x3<<17)
-#define DMAE_COMMAND_DST_VN_SHIFT 17
-#define DMAE_COMMAND_C_FUNC (0x1<<19)
-#define DMAE_COMMAND_C_FUNC_SHIFT 19
-#define DMAE_COMMAND_ERR_POLICY (0x3<<20)
-#define DMAE_COMMAND_ERR_POLICY_SHIFT 20
-#define DMAE_COMMAND_RESERVED0 (0x3FF<<22)
-#define DMAE_COMMAND_RESERVED0_SHIFT 22
- u32 src_addr_lo;
- u32 src_addr_hi;
- u32 dst_addr_lo;
- u32 dst_addr_hi;
-#if defined(__BIG_ENDIAN)
- u16 reserved1;
- u16 len;
-#elif defined(__LITTLE_ENDIAN)
- u16 len;
- u16 reserved1;
-#endif
- u32 comp_addr_lo;
- u32 comp_addr_hi;
- u32 comp_val;
- u32 crc32;
- u32 crc32_c;
-#if defined(__BIG_ENDIAN)
- u16 crc16_c;
- u16 crc16;
-#elif defined(__LITTLE_ENDIAN)
- u16 crc16;
- u16 crc16_c;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 reserved3;
- u16 crc_t10;
-#elif defined(__LITTLE_ENDIAN)
- u16 crc_t10;
- u16 reserved3;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 xsum8;
- u16 xsum16;
-#elif defined(__LITTLE_ENDIAN)
- u16 xsum16;
- u16 xsum8;
-#endif
-};
-
-
-struct double_regpair {
- u32 regpair0_lo;
- u32 regpair0_hi;
- u32 regpair1_lo;
- u32 regpair1_hi;
-};
-
-
-/*
- * SDM operation gen command (generate aggregative interrupt)
- */
-struct sdm_op_gen {
- __le32 command;
-#define SDM_OP_GEN_COMP_PARAM (0x1F<<0)
-#define SDM_OP_GEN_COMP_PARAM_SHIFT 0
-#define SDM_OP_GEN_COMP_TYPE (0x7<<5)
-#define SDM_OP_GEN_COMP_TYPE_SHIFT 5
-#define SDM_OP_GEN_AGG_VECT_IDX (0xFF<<8)
-#define SDM_OP_GEN_AGG_VECT_IDX_SHIFT 8
-#define SDM_OP_GEN_AGG_VECT_IDX_VALID (0x1<<16)
-#define SDM_OP_GEN_AGG_VECT_IDX_VALID_SHIFT 16
-#define SDM_OP_GEN_RESERVED (0x7FFF<<17)
-#define SDM_OP_GEN_RESERVED_SHIFT 17
-};
-
-/*
- * The eth Rx Buffer Descriptor
- */
-struct eth_rx_bd {
- __le32 addr_lo;
- __le32 addr_hi;
-};
-
-/*
- * The eth Rx SGE Descriptor
- */
-struct eth_rx_sge {
- __le32 addr_lo;
- __le32 addr_hi;
-};
-
-
-
-/*
- * The eth storm context of Ustorm
- */
-struct ustorm_eth_st_context {
- u32 reserved0[48];
-};
-
-/*
- * The eth storm context of Tstorm
- */
-struct tstorm_eth_st_context {
- u32 __reserved0[28];
-};
-
-/*
- * The eth aggregative context of Xstorm
- */
-struct xstorm_eth_ag_context {
- u32 reserved0;
-#if defined(__BIG_ENDIAN)
- u8 cdu_reserved;
- u8 reserved2;
- u16 reserved1;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved1;
- u8 reserved2;
- u8 cdu_reserved;
-#endif
- u32 reserved3[30];
-};
-
-/*
- * The eth aggregative context of Tstorm
- */
-struct tstorm_eth_ag_context {
- u32 __reserved0[14];
-};
-
-
-/*
- * The eth aggregative context of Cstorm
- */
-struct cstorm_eth_ag_context {
- u32 __reserved0[10];
-};
-
-
-/*
- * The eth aggregative context of Ustorm
- */
-struct ustorm_eth_ag_context {
- u32 __reserved0;
-#if defined(__BIG_ENDIAN)
- u8 cdu_usage;
- u8 __reserved2;
- u16 __reserved1;
-#elif defined(__LITTLE_ENDIAN)
- u16 __reserved1;
- u8 __reserved2;
- u8 cdu_usage;
-#endif
- u32 __reserved3[6];
-};
-
-/*
- * Timers connection context
- */
-struct timers_block_context {
- u32 __reserved_0;
- u32 __reserved_1;
- u32 __reserved_2;
- u32 flags;
-#define __TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS (0x3<<0)
-#define __TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS_SHIFT 0
-#define TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG (0x1<<2)
-#define TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG_SHIFT 2
-#define __TIMERS_BLOCK_CONTEXT_RESERVED0 (0x1FFFFFFF<<3)
-#define __TIMERS_BLOCK_CONTEXT_RESERVED0_SHIFT 3
-};
-
-/*
- * structure for easy accessibility to assembler
- */
-struct eth_tx_bd_flags {
- u8 as_bitfield;
-#define ETH_TX_BD_FLAGS_IP_CSUM (0x1<<0)
-#define ETH_TX_BD_FLAGS_IP_CSUM_SHIFT 0
-#define ETH_TX_BD_FLAGS_L4_CSUM (0x1<<1)
-#define ETH_TX_BD_FLAGS_L4_CSUM_SHIFT 1
-#define ETH_TX_BD_FLAGS_VLAN_MODE (0x3<<2)
-#define ETH_TX_BD_FLAGS_VLAN_MODE_SHIFT 2
-#define ETH_TX_BD_FLAGS_START_BD (0x1<<4)
-#define ETH_TX_BD_FLAGS_START_BD_SHIFT 4
-#define ETH_TX_BD_FLAGS_IS_UDP (0x1<<5)
-#define ETH_TX_BD_FLAGS_IS_UDP_SHIFT 5
-#define ETH_TX_BD_FLAGS_SW_LSO (0x1<<6)
-#define ETH_TX_BD_FLAGS_SW_LSO_SHIFT 6
-#define ETH_TX_BD_FLAGS_IPV6 (0x1<<7)
-#define ETH_TX_BD_FLAGS_IPV6_SHIFT 7
-};
-
-/*
- * The eth Tx Buffer Descriptor
- */
-struct eth_tx_start_bd {
- __le32 addr_lo;
- __le32 addr_hi;
- __le16 nbd;
- __le16 nbytes;
- __le16 vlan_or_ethertype;
- struct eth_tx_bd_flags bd_flags;
- u8 general_data;
-#define ETH_TX_START_BD_HDR_NBDS (0x3F<<0)
-#define ETH_TX_START_BD_HDR_NBDS_SHIFT 0
-#define ETH_TX_START_BD_ETH_ADDR_TYPE (0x3<<6)
-#define ETH_TX_START_BD_ETH_ADDR_TYPE_SHIFT 6
-};
-
-/*
- * Tx regular BD structure
- */
-struct eth_tx_bd {
- __le32 addr_lo;
- __le32 addr_hi;
- __le16 total_pkt_bytes;
- __le16 nbytes;
- u8 reserved[4];
-};
-
-/*
- * Tx parsing BD structure for ETH E1/E1h
- */
-struct eth_tx_parse_bd_e1x {
- u8 global_data;
-#define ETH_TX_PARSE_BD_E1X_IP_HDR_START_OFFSET_W (0xF<<0)
-#define ETH_TX_PARSE_BD_E1X_IP_HDR_START_OFFSET_W_SHIFT 0
-#define ETH_TX_PARSE_BD_E1X_RESERVED0 (0x1<<4)
-#define ETH_TX_PARSE_BD_E1X_RESERVED0_SHIFT 4
-#define ETH_TX_PARSE_BD_E1X_PSEUDO_CS_WITHOUT_LEN (0x1<<5)
-#define ETH_TX_PARSE_BD_E1X_PSEUDO_CS_WITHOUT_LEN_SHIFT 5
-#define ETH_TX_PARSE_BD_E1X_LLC_SNAP_EN (0x1<<6)
-#define ETH_TX_PARSE_BD_E1X_LLC_SNAP_EN_SHIFT 6
-#define ETH_TX_PARSE_BD_E1X_NS_FLG (0x1<<7)
-#define ETH_TX_PARSE_BD_E1X_NS_FLG_SHIFT 7
- u8 tcp_flags;
-#define ETH_TX_PARSE_BD_E1X_FIN_FLG (0x1<<0)
-#define ETH_TX_PARSE_BD_E1X_FIN_FLG_SHIFT 0
-#define ETH_TX_PARSE_BD_E1X_SYN_FLG (0x1<<1)
-#define ETH_TX_PARSE_BD_E1X_SYN_FLG_SHIFT 1
-#define ETH_TX_PARSE_BD_E1X_RST_FLG (0x1<<2)
-#define ETH_TX_PARSE_BD_E1X_RST_FLG_SHIFT 2
-#define ETH_TX_PARSE_BD_E1X_PSH_FLG (0x1<<3)
-#define ETH_TX_PARSE_BD_E1X_PSH_FLG_SHIFT 3
-#define ETH_TX_PARSE_BD_E1X_ACK_FLG (0x1<<4)
-#define ETH_TX_PARSE_BD_E1X_ACK_FLG_SHIFT 4
-#define ETH_TX_PARSE_BD_E1X_URG_FLG (0x1<<5)
-#define ETH_TX_PARSE_BD_E1X_URG_FLG_SHIFT 5
-#define ETH_TX_PARSE_BD_E1X_ECE_FLG (0x1<<6)
-#define ETH_TX_PARSE_BD_E1X_ECE_FLG_SHIFT 6
-#define ETH_TX_PARSE_BD_E1X_CWR_FLG (0x1<<7)
-#define ETH_TX_PARSE_BD_E1X_CWR_FLG_SHIFT 7
- u8 ip_hlen_w;
- s8 reserved;
- __le16 total_hlen_w;
- __le16 tcp_pseudo_csum;
- __le16 lso_mss;
- __le16 ip_id;
- __le32 tcp_send_seq;
-};
-
-/*
- * Tx parsing BD structure for ETH E2
- */
-struct eth_tx_parse_bd_e2 {
- __le16 dst_mac_addr_lo;
- __le16 dst_mac_addr_mid;
- __le16 dst_mac_addr_hi;
- __le16 src_mac_addr_lo;
- __le16 src_mac_addr_mid;
- __le16 src_mac_addr_hi;
- __le32 parsing_data;
-#define ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W (0x1FFF<<0)
-#define ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W_SHIFT 0
-#define ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW (0xF<<13)
-#define ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW_SHIFT 13
-#define ETH_TX_PARSE_BD_E2_LSO_MSS (0x3FFF<<17)
-#define ETH_TX_PARSE_BD_E2_LSO_MSS_SHIFT 17
-#define ETH_TX_PARSE_BD_E2_IPV6_WITH_EXT_HDR (0x1<<31)
-#define ETH_TX_PARSE_BD_E2_IPV6_WITH_EXT_HDR_SHIFT 31
-};
-
-/*
- * The last BD in the BD memory will hold a pointer to the next BD memory
- */
-struct eth_tx_next_bd {
- __le32 addr_lo;
- __le32 addr_hi;
- u8 reserved[8];
-};
-
-/*
- * union for 4 Bd types
- */
-union eth_tx_bd_types {
- struct eth_tx_start_bd start_bd;
- struct eth_tx_bd reg_bd;
- struct eth_tx_parse_bd_e1x parse_bd_e1x;
- struct eth_tx_parse_bd_e2 parse_bd_e2;
- struct eth_tx_next_bd next_bd;
-};
-
-
-/*
- * The eth storm context of Xstorm
- */
-struct xstorm_eth_st_context {
- u32 reserved0[60];
-};
-
-/*
- * The eth storm context of Cstorm
- */
-struct cstorm_eth_st_context {
- u32 __reserved0[4];
-};
-
-/*
- * Ethernet connection context
- */
-struct eth_context {
- struct ustorm_eth_st_context ustorm_st_context;
- struct tstorm_eth_st_context tstorm_st_context;
- struct xstorm_eth_ag_context xstorm_ag_context;
- struct tstorm_eth_ag_context tstorm_ag_context;
- struct cstorm_eth_ag_context cstorm_ag_context;
- struct ustorm_eth_ag_context ustorm_ag_context;
- struct timers_block_context timers_context;
- struct xstorm_eth_st_context xstorm_st_context;
- struct cstorm_eth_st_context cstorm_st_context;
-};
-
-
-/*
- * Ethernet doorbell
- */
-struct eth_tx_doorbell {
-#if defined(__BIG_ENDIAN)
- u16 npackets;
- u8 params;
-#define ETH_TX_DOORBELL_NUM_BDS (0x3F<<0)
-#define ETH_TX_DOORBELL_NUM_BDS_SHIFT 0
-#define ETH_TX_DOORBELL_RESERVED_TX_FIN_FLAG (0x1<<6)
-#define ETH_TX_DOORBELL_RESERVED_TX_FIN_FLAG_SHIFT 6
-#define ETH_TX_DOORBELL_SPARE (0x1<<7)
-#define ETH_TX_DOORBELL_SPARE_SHIFT 7
- struct doorbell_hdr hdr;
-#elif defined(__LITTLE_ENDIAN)
- struct doorbell_hdr hdr;
- u8 params;
-#define ETH_TX_DOORBELL_NUM_BDS (0x3F<<0)
-#define ETH_TX_DOORBELL_NUM_BDS_SHIFT 0
-#define ETH_TX_DOORBELL_RESERVED_TX_FIN_FLAG (0x1<<6)
-#define ETH_TX_DOORBELL_RESERVED_TX_FIN_FLAG_SHIFT 6
-#define ETH_TX_DOORBELL_SPARE (0x1<<7)
-#define ETH_TX_DOORBELL_SPARE_SHIFT 7
- u16 npackets;
-#endif
-};
-
-
-/*
- * client init fc data
- */
-struct client_init_fc_data {
- __le16 cqe_pause_thr_low;
- __le16 cqe_pause_thr_high;
- __le16 bd_pause_thr_low;
- __le16 bd_pause_thr_high;
- __le16 sge_pause_thr_low;
- __le16 sge_pause_thr_high;
- __le16 rx_cos_mask;
- u8 safc_group_num;
- u8 safc_group_en_flg;
- u8 traffic_type;
- u8 reserved0;
- __le16 reserved1;
- __le32 reserved2;
-};
-
-
-/*
- * client init ramrod data
- */
-struct client_init_general_data {
- u8 client_id;
- u8 statistics_counter_id;
- u8 statistics_en_flg;
- u8 is_fcoe_flg;
- u8 activate_flg;
- u8 sp_client_id;
- __le16 reserved0;
- __le32 reserved1[2];
-};
-
-
-/*
- * client init rx data
- */
-struct client_init_rx_data {
- u8 tpa_en_flg;
- u8 vmqueue_mode_en_flg;
- u8 extra_data_over_sgl_en_flg;
- u8 cache_line_alignment_log_size;
- u8 enable_dynamic_hc;
- u8 max_sges_for_packet;
- u8 client_qzone_id;
- u8 drop_ip_cs_err_flg;
- u8 drop_tcp_cs_err_flg;
- u8 drop_ttl0_flg;
- u8 drop_udp_cs_err_flg;
- u8 inner_vlan_removal_enable_flg;
- u8 outer_vlan_removal_enable_flg;
- u8 status_block_id;
- u8 rx_sb_index_number;
- u8 reserved0[3];
- __le16 bd_buff_size;
- __le16 sge_buff_size;
- __le16 mtu;
- struct regpair bd_page_base;
- struct regpair sge_page_base;
- struct regpair cqe_page_base;
- u8 is_leading_rss;
- u8 is_approx_mcast;
- __le16 max_agg_size;
- __le32 reserved2[3];
-};
-
-/*
- * client init tx data
- */
-struct client_init_tx_data {
- u8 enforce_security_flg;
- u8 tx_status_block_id;
- u8 tx_sb_index_number;
- u8 reserved0;
- __le16 mtu;
- __le16 reserved1;
- struct regpair tx_bd_page_base;
- __le32 reserved2[2];
-};
-
-/*
- * client init ramrod data
- */
-struct client_init_ramrod_data {
- struct client_init_general_data general;
- struct client_init_rx_data rx;
- struct client_init_tx_data tx;
- struct client_init_fc_data fc;
-};
-
-
-/*
- * The data contain client ID need to the ramrod
- */
-struct eth_common_ramrod_data {
- u32 client_id;
- u32 reserved1;
-};
-
-
-/*
- * union for sgl and raw data.
- */
-union eth_sgl_or_raw_data {
- __le16 sgl[8];
- u32 raw_data[4];
-};
-
-/*
- * regular eth FP CQE parameters struct
- */
-struct eth_fast_path_rx_cqe {
- u8 type_error_flags;
-#define ETH_FAST_PATH_RX_CQE_TYPE (0x1<<0)
-#define ETH_FAST_PATH_RX_CQE_TYPE_SHIFT 0
-#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG (0x1<<1)
-#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG_SHIFT 1
-#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG (0x1<<2)
-#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG_SHIFT 2
-#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG (0x1<<3)
-#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG_SHIFT 3
-#define ETH_FAST_PATH_RX_CQE_START_FLG (0x1<<4)
-#define ETH_FAST_PATH_RX_CQE_START_FLG_SHIFT 4
-#define ETH_FAST_PATH_RX_CQE_END_FLG (0x1<<5)
-#define ETH_FAST_PATH_RX_CQE_END_FLG_SHIFT 5
-#define ETH_FAST_PATH_RX_CQE_SGL_RAW_SEL (0x3<<6)
-#define ETH_FAST_PATH_RX_CQE_SGL_RAW_SEL_SHIFT 6
- u8 status_flags;
-#define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE (0x7<<0)
-#define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE_SHIFT 0
-#define ETH_FAST_PATH_RX_CQE_RSS_HASH_FLG (0x1<<3)
-#define ETH_FAST_PATH_RX_CQE_RSS_HASH_FLG_SHIFT 3
-#define ETH_FAST_PATH_RX_CQE_BROADCAST_FLG (0x1<<4)
-#define ETH_FAST_PATH_RX_CQE_BROADCAST_FLG_SHIFT 4
-#define ETH_FAST_PATH_RX_CQE_MAC_MATCH_FLG (0x1<<5)
-#define ETH_FAST_PATH_RX_CQE_MAC_MATCH_FLG_SHIFT 5
-#define ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG (0x1<<6)
-#define ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG_SHIFT 6
-#define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG (0x1<<7)
-#define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG_SHIFT 7
- u8 placement_offset;
- u8 queue_index;
- __le32 rss_hash_result;
- __le16 vlan_tag;
- __le16 pkt_len;
- __le16 len_on_bd;
- struct parsing_flags pars_flags;
- union eth_sgl_or_raw_data sgl_or_raw_data;
-};
-
-
-/*
- * The data for RSS setup ramrod
- */
-struct eth_halt_ramrod_data {
- u32 client_id;
- u32 reserved0;
-};
-
-/*
- * The data for statistics query ramrod
- */
-struct common_query_ramrod_data {
-#if defined(__BIG_ENDIAN)
- u8 reserved0;
- u8 collect_port;
- u16 drv_counter;
-#elif defined(__LITTLE_ENDIAN)
- u16 drv_counter;
- u8 collect_port;
- u8 reserved0;
-#endif
- u32 ctr_id_vector;
-};
-
-
-/*
- * Place holder for ramrods protocol specific data
- */
-struct ramrod_data {
- __le32 data_lo;
- __le32 data_hi;
-};
-
-/*
- * union for ramrod data for Ethernet protocol (CQE) (force size of 16 bits)
- */
-union eth_ramrod_data {
- struct ramrod_data general;
-};
-
-
-/*
- * Eth Rx Cqe structure- general structure for ramrods
- */
-struct common_ramrod_eth_rx_cqe {
- u8 ramrod_type;
-#define COMMON_RAMROD_ETH_RX_CQE_TYPE (0x1<<0)
-#define COMMON_RAMROD_ETH_RX_CQE_TYPE_SHIFT 0
-#define COMMON_RAMROD_ETH_RX_CQE_ERROR (0x1<<1)
-#define COMMON_RAMROD_ETH_RX_CQE_ERROR_SHIFT 1
-#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0 (0x3F<<2)
-#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0_SHIFT 2
- u8 conn_type;
- __le16 reserved1;
- __le32 conn_and_cmd_data;
-#define COMMON_RAMROD_ETH_RX_CQE_CID (0xFFFFFF<<0)
-#define COMMON_RAMROD_ETH_RX_CQE_CID_SHIFT 0
-#define COMMON_RAMROD_ETH_RX_CQE_CMD_ID (0xFF<<24)
-#define COMMON_RAMROD_ETH_RX_CQE_CMD_ID_SHIFT 24
- struct ramrod_data protocol_data;
- __le32 reserved2[4];
-};
-
-/*
- * Rx Last CQE in page (in ETH)
- */
-struct eth_rx_cqe_next_page {
- __le32 addr_lo;
- __le32 addr_hi;
- __le32 reserved[6];
-};
-
-/*
- * union for all eth rx cqe types (fix their sizes)
- */
-union eth_rx_cqe {
- struct eth_fast_path_rx_cqe fast_path_cqe;
- struct common_ramrod_eth_rx_cqe ramrod_cqe;
- struct eth_rx_cqe_next_page next_page_cqe;
-};
-
-
-/*
- * common data for all protocols
- */
-struct spe_hdr {
- __le32 conn_and_cmd_data;
-#define SPE_HDR_CID (0xFFFFFF<<0)
-#define SPE_HDR_CID_SHIFT 0
-#define SPE_HDR_CMD_ID (0xFF<<24)
-#define SPE_HDR_CMD_ID_SHIFT 24
- __le16 type;
-#define SPE_HDR_CONN_TYPE (0xFF<<0)
-#define SPE_HDR_CONN_TYPE_SHIFT 0
-#define SPE_HDR_FUNCTION_ID (0xFF<<8)
-#define SPE_HDR_FUNCTION_ID_SHIFT 8
- __le16 reserved1;
-};
-
-/*
- * Ethernet slow path element
- */
-union eth_specific_data {
- u8 protocol_data[8];
- struct regpair client_init_ramrod_init_data;
- struct eth_halt_ramrod_data halt_ramrod_data;
- struct regpair update_data_addr;
- struct eth_common_ramrod_data common_ramrod_data;
-};
-
-/*
- * Ethernet slow path element
- */
-struct eth_spe {
- struct spe_hdr hdr;
- union eth_specific_data data;
-};
-
-
-/*
- * array of 13 bds as appears in the eth xstorm context
- */
-struct eth_tx_bds_array {
- union eth_tx_bd_types bds[13];
-};
-
-
-/*
- * Common configuration parameters per function in Tstorm
- */
-struct tstorm_eth_function_common_config {
-#if defined(__BIG_ENDIAN)
- u8 reserved1;
- u8 rss_result_mask;
- u16 config_flags;
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY (0x1<<0)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY_SHIFT 0
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY (0x1<<1)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY_SHIFT 1
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY (0x1<<2)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY_SHIFT 2
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY (0x1<<3)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY_SHIFT 3
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_MODE (0x7<<4)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_MODE_SHIFT 4
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_ENABLE_TPA (0x1<<7)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_ENABLE_TPA_SHIFT 7
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_FILTERING_ENABLE (0x1<<8)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_FILTERING_ENABLE_SHIFT 8
-#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0 (0x7F<<9)
-#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0_SHIFT 9
-#elif defined(__LITTLE_ENDIAN)
- u16 config_flags;
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY (0x1<<0)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY_SHIFT 0
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY (0x1<<1)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY_SHIFT 1
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY (0x1<<2)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY_SHIFT 2
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY (0x1<<3)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY_SHIFT 3
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_MODE (0x7<<4)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_MODE_SHIFT 4
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_ENABLE_TPA (0x1<<7)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_ENABLE_TPA_SHIFT 7
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_FILTERING_ENABLE (0x1<<8)
-#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_FILTERING_ENABLE_SHIFT 8
-#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0 (0x7F<<9)
-#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0_SHIFT 9
- u8 rss_result_mask;
- u8 reserved1;
-#endif
- u16 vlan_id[2];
-};
-
-/*
- * RSS idirection table update configuration
- */
-struct rss_update_config {
-#if defined(__BIG_ENDIAN)
- u16 toe_rss_bitmap;
- u16 flags;
-#define RSS_UPDATE_CONFIG_ETH_UPDATE_ENABLE (0x1<<0)
-#define RSS_UPDATE_CONFIG_ETH_UPDATE_ENABLE_SHIFT 0
-#define RSS_UPDATE_CONFIG_TOE_UPDATE_ENABLE (0x1<<1)
-#define RSS_UPDATE_CONFIG_TOE_UPDATE_ENABLE_SHIFT 1
-#define __RSS_UPDATE_CONFIG_RESERVED0 (0x3FFF<<2)
-#define __RSS_UPDATE_CONFIG_RESERVED0_SHIFT 2
-#elif defined(__LITTLE_ENDIAN)
- u16 flags;
-#define RSS_UPDATE_CONFIG_ETH_UPDATE_ENABLE (0x1<<0)
-#define RSS_UPDATE_CONFIG_ETH_UPDATE_ENABLE_SHIFT 0
-#define RSS_UPDATE_CONFIG_TOE_UPDATE_ENABLE (0x1<<1)
-#define RSS_UPDATE_CONFIG_TOE_UPDATE_ENABLE_SHIFT 1
-#define __RSS_UPDATE_CONFIG_RESERVED0 (0x3FFF<<2)
-#define __RSS_UPDATE_CONFIG_RESERVED0_SHIFT 2
- u16 toe_rss_bitmap;
-#endif
- u32 reserved1;
-};
-
-/*
- * parameters for eth update ramrod
- */
-struct eth_update_ramrod_data {
- struct tstorm_eth_function_common_config func_config;
- u8 indirectionTable[128];
- struct rss_update_config rss_config;
-};
-
-
-/*
- * MAC filtering configuration command header
- */
-struct mac_configuration_hdr {
- u8 length;
- u8 offset;
- u16 client_id;
- u16 echo;
- u16 reserved1;
-};
-
-/*
- * MAC address in list for ramrod
- */
-struct mac_configuration_entry {
- __le16 lsb_mac_addr;
- __le16 middle_mac_addr;
- __le16 msb_mac_addr;
- __le16 vlan_id;
- u8 pf_id;
- u8 flags;
-#define MAC_CONFIGURATION_ENTRY_ACTION_TYPE (0x1<<0)
-#define MAC_CONFIGURATION_ENTRY_ACTION_TYPE_SHIFT 0
-#define MAC_CONFIGURATION_ENTRY_RDMA_MAC (0x1<<1)
-#define MAC_CONFIGURATION_ENTRY_RDMA_MAC_SHIFT 1
-#define MAC_CONFIGURATION_ENTRY_VLAN_FILTERING_MODE (0x3<<2)
-#define MAC_CONFIGURATION_ENTRY_VLAN_FILTERING_MODE_SHIFT 2
-#define MAC_CONFIGURATION_ENTRY_OVERRIDE_VLAN_REMOVAL (0x1<<4)
-#define MAC_CONFIGURATION_ENTRY_OVERRIDE_VLAN_REMOVAL_SHIFT 4
-#define MAC_CONFIGURATION_ENTRY_BROADCAST (0x1<<5)
-#define MAC_CONFIGURATION_ENTRY_BROADCAST_SHIFT 5
-#define MAC_CONFIGURATION_ENTRY_RESERVED1 (0x3<<6)
-#define MAC_CONFIGURATION_ENTRY_RESERVED1_SHIFT 6
- u16 reserved0;
- u32 clients_bit_vector;
-};
-
-/*
- * MAC filtering configuration command
- */
-struct mac_configuration_cmd {
- struct mac_configuration_hdr hdr;
- struct mac_configuration_entry config_table[64];
-};
-
-
-/*
- * approximate-match multicast filtering for E1H per function in Tstorm
- */
-struct tstorm_eth_approximate_match_multicast_filtering {
- u32 mcast_add_hash_bit_array[8];
-};
-
-
-/*
- * MAC filtering configuration parameters per port in Tstorm
- */
-struct tstorm_eth_mac_filter_config {
- u32 ucast_drop_all;
- u32 ucast_accept_all;
- u32 mcast_drop_all;
- u32 mcast_accept_all;
- u32 bcast_drop_all;
- u32 bcast_accept_all;
- u32 vlan_filter[2];
- u32 unmatched_unicast;
- u32 reserved;
-};
-
-
-/*
- * common flag to indicate existence of TPA.
- */
-struct tstorm_eth_tpa_exist {
-#if defined(__BIG_ENDIAN)
- u16 reserved1;
- u8 reserved0;
- u8 tpa_exist;
-#elif defined(__LITTLE_ENDIAN)
- u8 tpa_exist;
- u8 reserved0;
- u16 reserved1;
-#endif
- u32 reserved2;
-};
-
-
-/*
- * Three RX producers for ETH
- */
-struct ustorm_eth_rx_producers {
-#if defined(__BIG_ENDIAN)
- u16 bd_prod;
- u16 cqe_prod;
-#elif defined(__LITTLE_ENDIAN)
- u16 cqe_prod;
- u16 bd_prod;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 reserved;
- u16 sge_prod;
-#elif defined(__LITTLE_ENDIAN)
- u16 sge_prod;
- u16 reserved;
-#endif
-};
-
-
-/*
- * cfc delete event data
- */
-struct cfc_del_event_data {
- u32 cid;
- u8 error;
- u8 reserved0;
- u16 reserved1;
- u32 reserved2;
-};
-
-
-/*
- * per-port SAFC demo variables
- */
-struct cmng_flags_per_port {
- u8 con_number[NUM_OF_PROTOCOLS];
- u32 cmng_enables;
-#define CMNG_FLAGS_PER_PORT_FAIRNESS_VN (0x1<<0)
-#define CMNG_FLAGS_PER_PORT_FAIRNESS_VN_SHIFT 0
-#define CMNG_FLAGS_PER_PORT_RATE_SHAPING_VN (0x1<<1)
-#define CMNG_FLAGS_PER_PORT_RATE_SHAPING_VN_SHIFT 1
-#define CMNG_FLAGS_PER_PORT_FAIRNESS_PROTOCOL (0x1<<2)
-#define CMNG_FLAGS_PER_PORT_FAIRNESS_PROTOCOL_SHIFT 2
-#define CMNG_FLAGS_PER_PORT_RATE_SHAPING_PROTOCOL (0x1<<3)
-#define CMNG_FLAGS_PER_PORT_RATE_SHAPING_PROTOCOL_SHIFT 3
-#define CMNG_FLAGS_PER_PORT_FAIRNESS_COS (0x1<<4)
-#define CMNG_FLAGS_PER_PORT_FAIRNESS_COS_SHIFT 4
-#define CMNG_FLAGS_PER_PORT_FAIRNESS_COS_MODE (0x1<<5)
-#define CMNG_FLAGS_PER_PORT_FAIRNESS_COS_MODE_SHIFT 5
-#define __CMNG_FLAGS_PER_PORT_RESERVED0 (0x3FFFFFF<<6)
-#define __CMNG_FLAGS_PER_PORT_RESERVED0_SHIFT 6
-};
-
-
-/*
- * per-port rate shaping variables
- */
-struct rate_shaping_vars_per_port {
- u32 rs_periodic_timeout;
- u32 rs_threshold;
-};
-
-/*
- * per-port fairness variables
- */
-struct fairness_vars_per_port {
- u32 upper_bound;
- u32 fair_threshold;
- u32 fairness_timeout;
-};
-
-/*
- * per-port SAFC variables
- */
-struct safc_struct_per_port {
-#if defined(__BIG_ENDIAN)
- u16 __reserved1;
- u8 __reserved0;
- u8 safc_timeout_usec;
-#elif defined(__LITTLE_ENDIAN)
- u8 safc_timeout_usec;
- u8 __reserved0;
- u16 __reserved1;
-#endif
- u8 cos_to_traffic_types[MAX_COS_NUMBER];
- u32 __reserved2;
- u16 cos_to_pause_mask[NUM_OF_SAFC_BITS];
-};
-
-/*
- * per-port PFC variables
- */
-struct pfc_struct_per_port {
- u8 priority_to_traffic_types[MAX_PFC_PRIORITIES];
-#if defined(__BIG_ENDIAN)
- u16 pfc_pause_quanta_in_nanosec;
- u8 __reserved0;
- u8 priority_non_pausable_mask;
-#elif defined(__LITTLE_ENDIAN)
- u8 priority_non_pausable_mask;
- u8 __reserved0;
- u16 pfc_pause_quanta_in_nanosec;
-#endif
-};
-
-/*
- * Priority and cos
- */
-struct priority_cos {
-#if defined(__BIG_ENDIAN)
- u16 reserved1;
- u8 cos;
- u8 priority;
-#elif defined(__LITTLE_ENDIAN)
- u8 priority;
- u8 cos;
- u16 reserved1;
-#endif
- u32 reserved2;
-};
-
-/*
- * Per-port congestion management variables
- */
-struct cmng_struct_per_port {
- struct rate_shaping_vars_per_port rs_vars;
- struct fairness_vars_per_port fair_vars;
- struct safc_struct_per_port safc_vars;
- struct pfc_struct_per_port pfc_vars;
-#if defined(__BIG_ENDIAN)
- u16 __reserved1;
- u8 dcb_enabled;
- u8 llfc_mode;
-#elif defined(__LITTLE_ENDIAN)
- u8 llfc_mode;
- u8 dcb_enabled;
- u16 __reserved1;
-#endif
- struct priority_cos
- traffic_type_to_priority_cos[MAX_PFC_TRAFFIC_TYPES];
- struct cmng_flags_per_port flags;
-};
-
-
-
-/*
- * Dynamic HC counters set by the driver
- */
-struct hc_dynamic_drv_counter {
- u32 val[HC_SB_MAX_DYNAMIC_INDICES];
-};
-
-/*
- * zone A per-queue data
- */
-struct cstorm_queue_zone_data {
- struct hc_dynamic_drv_counter hc_dyn_drv_cnt;
- struct regpair reserved[2];
-};
-
-/*
- * Dynamic host coalescing init parameters
- */
-struct dynamic_hc_config {
- u32 threshold[3];
- u8 shift_per_protocol[HC_SB_MAX_DYNAMIC_INDICES];
- u8 hc_timeout0[HC_SB_MAX_DYNAMIC_INDICES];
- u8 hc_timeout1[HC_SB_MAX_DYNAMIC_INDICES];
- u8 hc_timeout2[HC_SB_MAX_DYNAMIC_INDICES];
- u8 hc_timeout3[HC_SB_MAX_DYNAMIC_INDICES];
-};
-
-
-/*
- * Protocol-common statistics collected by the Xstorm (per client)
- */
-struct xstorm_per_client_stats {
- __le32 reserved0;
- __le32 unicast_pkts_sent;
- struct regpair unicast_bytes_sent;
- struct regpair multicast_bytes_sent;
- __le32 multicast_pkts_sent;
- __le32 broadcast_pkts_sent;
- struct regpair broadcast_bytes_sent;
- __le16 stats_counter;
- __le16 reserved1;
- __le32 reserved2;
-};
-
-/*
- * Common statistics collected by the Xstorm (per port)
- */
-struct xstorm_common_stats {
- struct xstorm_per_client_stats client_statistics[MAX_STAT_COUNTER_ID];
-};
-
-/*
- * Protocol-common statistics collected by the Tstorm (per port)
- */
-struct tstorm_per_port_stats {
- __le32 mac_filter_discard;
- __le32 xxoverflow_discard;
- __le32 brb_truncate_discard;
- __le32 mac_discard;
-};
-
-/*
- * Protocol-common statistics collected by the Tstorm (per client)
- */
-struct tstorm_per_client_stats {
- struct regpair rcv_unicast_bytes;
- struct regpair rcv_broadcast_bytes;
- struct regpair rcv_multicast_bytes;
- struct regpair rcv_error_bytes;
- __le32 checksum_discard;
- __le32 packets_too_big_discard;
- __le32 rcv_unicast_pkts;
- __le32 rcv_broadcast_pkts;
- __le32 rcv_multicast_pkts;
- __le32 no_buff_discard;
- __le32 ttl0_discard;
- __le16 stats_counter;
- __le16 reserved0;
-};
-
-/*
- * Protocol-common statistics collected by the Tstorm
- */
-struct tstorm_common_stats {
- struct tstorm_per_port_stats port_statistics;
- struct tstorm_per_client_stats client_statistics[MAX_STAT_COUNTER_ID];
-};
-
-/*
- * Protocol-common statistics collected by the Ustorm (per client)
- */
-struct ustorm_per_client_stats {
- struct regpair ucast_no_buff_bytes;
- struct regpair mcast_no_buff_bytes;
- struct regpair bcast_no_buff_bytes;
- __le32 ucast_no_buff_pkts;
- __le32 mcast_no_buff_pkts;
- __le32 bcast_no_buff_pkts;
- __le16 stats_counter;
- __le16 reserved0;
-};
-
-/*
- * Protocol-common statistics collected by the Ustorm
- */
-struct ustorm_common_stats {
- struct ustorm_per_client_stats client_statistics[MAX_STAT_COUNTER_ID];
-};
-
-/*
- * Eth statistics query structure for the eth_stats_query ramrod
- */
-struct eth_stats_query {
- struct xstorm_common_stats xstorm_common;
- struct tstorm_common_stats tstorm_common;
- struct ustorm_common_stats ustorm_common;
-};
-
-
-/*
- * set mac event data
- */
-struct set_mac_event_data {
- u16 echo;
- u16 reserved0;
- u32 reserved1;
- u32 reserved2;
-};
-
-/*
- * union for all event ring message types
- */
-union event_data {
- struct set_mac_event_data set_mac_event;
- struct cfc_del_event_data cfc_del_event;
-};
-
-
-/*
- * per PF event ring data
- */
-struct event_ring_data {
- struct regpair base_addr;
-#if defined(__BIG_ENDIAN)
- u8 index_id;
- u8 sb_id;
- u16 producer;
-#elif defined(__LITTLE_ENDIAN)
- u16 producer;
- u8 sb_id;
- u8 index_id;
-#endif
- u32 reserved0;
-};
-
-
-/*
- * event ring message element (each element is 128 bits)
- */
-struct event_ring_msg {
- u8 opcode;
- u8 reserved0;
- u16 reserved1;
- union event_data data;
-};
-
-/*
- * event ring next page element (128 bits)
- */
-struct event_ring_next {
- struct regpair addr;
- u32 reserved[2];
-};
-
-/*
- * union for event ring element types (each element is 128 bits)
- */
-union event_ring_elem {
- struct event_ring_msg message;
- struct event_ring_next next_page;
-};
-
-
-/*
- * per-vnic fairness variables
- */
-struct fairness_vars_per_vn {
- u32 cos_credit_delta[MAX_COS_NUMBER];
- u32 protocol_credit_delta[NUM_OF_PROTOCOLS];
- u32 vn_credit_delta;
- u32 __reserved0;
-};
-
-
-/*
- * The data for flow control configuration
- */
-struct flow_control_configuration {
- struct priority_cos
- traffic_type_to_priority_cos[MAX_PFC_TRAFFIC_TYPES];
-#if defined(__BIG_ENDIAN)
- u16 reserved1;
- u8 dcb_version;
- u8 dcb_enabled;
-#elif defined(__LITTLE_ENDIAN)
- u8 dcb_enabled;
- u8 dcb_version;
- u16 reserved1;
-#endif
- u32 reserved2;
-};
-
-
-/*
- * FW version stored in the Xstorm RAM
- */
-struct fw_version {
-#if defined(__BIG_ENDIAN)
- u8 engineering;
- u8 revision;
- u8 minor;
- u8 major;
-#elif defined(__LITTLE_ENDIAN)
- u8 major;
- u8 minor;
- u8 revision;
- u8 engineering;
-#endif
- u32 flags;
-#define FW_VERSION_OPTIMIZED (0x1<<0)
-#define FW_VERSION_OPTIMIZED_SHIFT 0
-#define FW_VERSION_BIG_ENDIEN (0x1<<1)
-#define FW_VERSION_BIG_ENDIEN_SHIFT 1
-#define FW_VERSION_CHIP_VERSION (0x3<<2)
-#define FW_VERSION_CHIP_VERSION_SHIFT 2
-#define __FW_VERSION_RESERVED (0xFFFFFFF<<4)
-#define __FW_VERSION_RESERVED_SHIFT 4
-};
-
-
-/*
- * Dynamic Host-Coalescing - Driver(host) counters
- */
-struct hc_dynamic_sb_drv_counters {
- u32 dynamic_hc_drv_counter[HC_SB_MAX_DYNAMIC_INDICES];
-};
-
-
-/*
- * 2 bytes. configuration/state parameters for a single protocol index
- */
-struct hc_index_data {
-#if defined(__BIG_ENDIAN)
- u8 flags;
-#define HC_INDEX_DATA_SM_ID (0x1<<0)
-#define HC_INDEX_DATA_SM_ID_SHIFT 0
-#define HC_INDEX_DATA_HC_ENABLED (0x1<<1)
-#define HC_INDEX_DATA_HC_ENABLED_SHIFT 1
-#define HC_INDEX_DATA_DYNAMIC_HC_ENABLED (0x1<<2)
-#define HC_INDEX_DATA_DYNAMIC_HC_ENABLED_SHIFT 2
-#define HC_INDEX_DATA_RESERVE (0x1F<<3)
-#define HC_INDEX_DATA_RESERVE_SHIFT 3
- u8 timeout;
-#elif defined(__LITTLE_ENDIAN)
- u8 timeout;
- u8 flags;
-#define HC_INDEX_DATA_SM_ID (0x1<<0)
-#define HC_INDEX_DATA_SM_ID_SHIFT 0
-#define HC_INDEX_DATA_HC_ENABLED (0x1<<1)
-#define HC_INDEX_DATA_HC_ENABLED_SHIFT 1
-#define HC_INDEX_DATA_DYNAMIC_HC_ENABLED (0x1<<2)
-#define HC_INDEX_DATA_DYNAMIC_HC_ENABLED_SHIFT 2
-#define HC_INDEX_DATA_RESERVE (0x1F<<3)
-#define HC_INDEX_DATA_RESERVE_SHIFT 3
-#endif
-};
-
-
-/*
- * HC state-machine
- */
-struct hc_status_block_sm {
-#if defined(__BIG_ENDIAN)
- u8 igu_seg_id;
- u8 igu_sb_id;
- u8 timer_value;
- u8 __flags;
-#elif defined(__LITTLE_ENDIAN)
- u8 __flags;
- u8 timer_value;
- u8 igu_sb_id;
- u8 igu_seg_id;
-#endif
- u32 time_to_expire;
-};
-
-/*
- * hold PCI identification variables- used in various places in firmware
- */
-struct pci_entity {
-#if defined(__BIG_ENDIAN)
- u8 vf_valid;
- u8 vf_id;
- u8 vnic_id;
- u8 pf_id;
-#elif defined(__LITTLE_ENDIAN)
- u8 pf_id;
- u8 vnic_id;
- u8 vf_id;
- u8 vf_valid;
-#endif
-};
-
-/*
- * The fast-path status block meta-data, common to all chips
- */
-struct hc_sb_data {
- struct regpair host_sb_addr;
- struct hc_status_block_sm state_machine[HC_SB_MAX_SM];
- struct pci_entity p_func;
-#if defined(__BIG_ENDIAN)
- u8 rsrv0;
- u8 dhc_qzone_id;
- u8 __dynamic_hc_level;
- u8 same_igu_sb_1b;
-#elif defined(__LITTLE_ENDIAN)
- u8 same_igu_sb_1b;
- u8 __dynamic_hc_level;
- u8 dhc_qzone_id;
- u8 rsrv0;
-#endif
- struct regpair rsrv1[2];
-};
-
-
-/*
- * The fast-path status block meta-data
- */
-struct hc_sp_status_block_data {
- struct regpair host_sb_addr;
-#if defined(__BIG_ENDIAN)
- u16 rsrv;
- u8 igu_seg_id;
- u8 igu_sb_id;
-#elif defined(__LITTLE_ENDIAN)
- u8 igu_sb_id;
- u8 igu_seg_id;
- u16 rsrv;
-#endif
- struct pci_entity p_func;
-};
-
-
-/*
- * The fast-path status block meta-data
- */
-struct hc_status_block_data_e1x {
- struct hc_index_data index_data[HC_SB_MAX_INDICES_E1X];
- struct hc_sb_data common;
-};
-
-
-/*
- * The fast-path status block meta-data
- */
-struct hc_status_block_data_e2 {
- struct hc_index_data index_data[HC_SB_MAX_INDICES_E2];
- struct hc_sb_data common;
-};
-
-
-/*
- * FW version stored in first line of pram
- */
-struct pram_fw_version {
- u8 major;
- u8 minor;
- u8 revision;
- u8 engineering;
- u8 flags;
-#define PRAM_FW_VERSION_OPTIMIZED (0x1<<0)
-#define PRAM_FW_VERSION_OPTIMIZED_SHIFT 0
-#define PRAM_FW_VERSION_STORM_ID (0x3<<1)
-#define PRAM_FW_VERSION_STORM_ID_SHIFT 1
-#define PRAM_FW_VERSION_BIG_ENDIEN (0x1<<3)
-#define PRAM_FW_VERSION_BIG_ENDIEN_SHIFT 3
-#define PRAM_FW_VERSION_CHIP_VERSION (0x3<<4)
-#define PRAM_FW_VERSION_CHIP_VERSION_SHIFT 4
-#define __PRAM_FW_VERSION_RESERVED0 (0x3<<6)
-#define __PRAM_FW_VERSION_RESERVED0_SHIFT 6
-};
-
-
-/*
- * Ethernet slow path element
- */
-union protocol_common_specific_data {
- u8 protocol_data[8];
- struct regpair phy_address;
- struct regpair mac_config_addr;
- struct common_query_ramrod_data query_ramrod_data;
-};
-
-/*
- * The send queue element
- */
-struct protocol_common_spe {
- struct spe_hdr hdr;
- union protocol_common_specific_data data;
-};
-
-
-/*
- * a single rate shaping counter. can be used as protocol or vnic counter
- */
-struct rate_shaping_counter {
- u32 quota;
-#if defined(__BIG_ENDIAN)
- u16 __reserved0;
- u16 rate;
-#elif defined(__LITTLE_ENDIAN)
- u16 rate;
- u16 __reserved0;
-#endif
-};
-
-
-/*
- * per-vnic rate shaping variables
- */
-struct rate_shaping_vars_per_vn {
- struct rate_shaping_counter protocol_counters[NUM_OF_PROTOCOLS];
- struct rate_shaping_counter vn_counter;
-};
-
-
-/*
- * The send queue element
- */
-struct slow_path_element {
- struct spe_hdr hdr;
- struct regpair protocol_data;
-};
-
-
-/*
- * eth/toe flags that indicate if to query
- */
-struct stats_indication_flags {
- u32 collect_eth;
- u32 collect_toe;
-};
-
-
-/*
- * per-port PFC variables
- */
-struct storm_pfc_struct_per_port {
-#if defined(__BIG_ENDIAN)
- u16 mid_mac_addr;
- u16 msb_mac_addr;
-#elif defined(__LITTLE_ENDIAN)
- u16 msb_mac_addr;
- u16 mid_mac_addr;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 pfc_pause_quanta_in_nanosec;
- u16 lsb_mac_addr;
-#elif defined(__LITTLE_ENDIAN)
- u16 lsb_mac_addr;
- u16 pfc_pause_quanta_in_nanosec;
-#endif
-};
-
-/*
- * Per-port congestion management variables
- */
-struct storm_cmng_struct_per_port {
- struct storm_pfc_struct_per_port pfc_vars;
-};
-
-
-/*
- * zone A per-queue data
- */
-struct tstorm_queue_zone_data {
- struct regpair reserved[4];
-};
-
-
-/*
- * zone B per-VF data
- */
-struct tstorm_vf_zone_data {
- struct regpair reserved;
-};
-
-
-/*
- * zone A per-queue data
- */
-struct ustorm_queue_zone_data {
- struct ustorm_eth_rx_producers eth_rx_producers;
- struct regpair reserved[3];
-};
-
-
-/*
- * zone B per-VF data
- */
-struct ustorm_vf_zone_data {
- struct regpair reserved;
-};
-
-
-/*
- * data per VF-PF channel
- */
-struct vf_pf_channel_data {
-#if defined(__BIG_ENDIAN)
- u16 reserved0;
- u8 valid;
- u8 state;
-#elif defined(__LITTLE_ENDIAN)
- u8 state;
- u8 valid;
- u16 reserved0;
-#endif
- u32 reserved1;
-};
-
-
-/*
- * zone A per-queue data
- */
-struct xstorm_queue_zone_data {
- struct regpair reserved[4];
-};
-
-
-/*
- * zone B per-VF data
- */
-struct xstorm_vf_zone_data {
- struct regpair reserved;
-};
-
-#endif /* BNX2X_HSI_H */
diff --git a/drivers/net/bnx2x/bnx2x_init.h b/drivers/net/bnx2x/bnx2x_init.h
deleted file mode 100644
index d5399206f66e..000000000000
--- a/drivers/net/bnx2x/bnx2x_init.h
+++ /dev/null
@@ -1,416 +0,0 @@
-/* bnx2x_init.h: Broadcom Everest network driver.
- * Structures and macroes needed during the initialization.
- *
- * Copyright (c) 2007-2011 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation.
- *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Eliezer Tamir
- * Modified by: Vladislav Zolotarov <vladz@broadcom.com>
- */
-
-#ifndef BNX2X_INIT_H
-#define BNX2X_INIT_H
-
-/* RAM0 size in bytes */
-#define STORM_INTMEM_SIZE_E1 0x5800
-#define STORM_INTMEM_SIZE_E1H 0x10000
-#define STORM_INTMEM_SIZE(bp) ((CHIP_IS_E1(bp) ? STORM_INTMEM_SIZE_E1 : \
- STORM_INTMEM_SIZE_E1H) / 4)
-
-
-/* Init operation types and structures */
-/* Common for both E1 and E1H */
-#define OP_RD 0x1 /* read single register */
-#define OP_WR 0x2 /* write single register */
-#define OP_IW 0x3 /* write single register using mailbox */
-#define OP_SW 0x4 /* copy a string to the device */
-#define OP_SI 0x5 /* copy a string using mailbox */
-#define OP_ZR 0x6 /* clear memory */
-#define OP_ZP 0x7 /* unzip then copy with DMAE */
-#define OP_WR_64 0x8 /* write 64 bit pattern */
-#define OP_WB 0x9 /* copy a string using DMAE */
-
-/* FPGA and EMUL specific operations */
-#define OP_WR_EMUL 0xa /* write single register on Emulation */
-#define OP_WR_FPGA 0xb /* write single register on FPGA */
-#define OP_WR_ASIC 0xc /* write single register on ASIC */
-
-/* Init stages */
-/* Never reorder stages !!! */
-#define COMMON_STAGE 0
-#define PORT0_STAGE 1
-#define PORT1_STAGE 2
-#define FUNC0_STAGE 3
-#define FUNC1_STAGE 4
-#define FUNC2_STAGE 5
-#define FUNC3_STAGE 6
-#define FUNC4_STAGE 7
-#define FUNC5_STAGE 8
-#define FUNC6_STAGE 9
-#define FUNC7_STAGE 10
-#define STAGE_IDX_MAX 11
-
-#define STAGE_START 0
-#define STAGE_END 1
-
-
-/* Indices of blocks */
-#define PRS_BLOCK 0
-#define SRCH_BLOCK 1
-#define TSDM_BLOCK 2
-#define TCM_BLOCK 3
-#define BRB1_BLOCK 4
-#define TSEM_BLOCK 5
-#define PXPCS_BLOCK 6
-#define EMAC0_BLOCK 7
-#define EMAC1_BLOCK 8
-#define DBU_BLOCK 9
-#define MISC_BLOCK 10
-#define DBG_BLOCK 11
-#define NIG_BLOCK 12
-#define MCP_BLOCK 13
-#define UPB_BLOCK 14
-#define CSDM_BLOCK 15
-#define USDM_BLOCK 16
-#define CCM_BLOCK 17
-#define UCM_BLOCK 18
-#define USEM_BLOCK 19
-#define CSEM_BLOCK 20
-#define XPB_BLOCK 21
-#define DQ_BLOCK 22
-#define TIMERS_BLOCK 23
-#define XSDM_BLOCK 24
-#define QM_BLOCK 25
-#define PBF_BLOCK 26
-#define XCM_BLOCK 27
-#define XSEM_BLOCK 28
-#define CDU_BLOCK 29
-#define DMAE_BLOCK 30
-#define PXP_BLOCK 31
-#define CFC_BLOCK 32
-#define HC_BLOCK 33
-#define PXP2_BLOCK 34
-#define MISC_AEU_BLOCK 35
-#define PGLUE_B_BLOCK 36
-#define IGU_BLOCK 37
-#define ATC_BLOCK 38
-#define QM_4PORT_BLOCK 39
-#define XSEM_4PORT_BLOCK 40
-
-
-/* Returns the index of start or end of a specific block stage in ops array*/
-#define BLOCK_OPS_IDX(block, stage, end) \
- (2*(((block)*STAGE_IDX_MAX) + (stage)) + (end))
-
-
-struct raw_op {
- u32 op:8;
- u32 offset:24;
- u32 raw_data;
-};
-
-struct op_read {
- u32 op:8;
- u32 offset:24;
- u32 pad;
-};
-
-struct op_write {
- u32 op:8;
- u32 offset:24;
- u32 val;
-};
-
-struct op_string_write {
- u32 op:8;
- u32 offset:24;
-#ifdef __LITTLE_ENDIAN
- u16 data_off;
- u16 data_len;
-#else /* __BIG_ENDIAN */
- u16 data_len;
- u16 data_off;
-#endif
-};
-
-struct op_zero {
- u32 op:8;
- u32 offset:24;
- u32 len;
-};
-
-union init_op {
- struct op_read read;
- struct op_write write;
- struct op_string_write str_wr;
- struct op_zero zero;
- struct raw_op raw;
-};
-
-#define INITOP_SET 0 /* set the HW directly */
-#define INITOP_CLEAR 1 /* clear the HW directly */
-#define INITOP_INIT 2 /* set the init-value array */
-
-/****************************************************************************
-* ILT management
-****************************************************************************/
-struct ilt_line {
- dma_addr_t page_mapping;
- void *page;
- u32 size;
-};
-
-struct ilt_client_info {
- u32 page_size;
- u16 start;
- u16 end;
- u16 client_num;
- u16 flags;
-#define ILT_CLIENT_SKIP_INIT 0x1
-#define ILT_CLIENT_SKIP_MEM 0x2
-};
-
-struct bnx2x_ilt {
- u32 start_line;
- struct ilt_line *lines;
- struct ilt_client_info clients[4];
-#define ILT_CLIENT_CDU 0
-#define ILT_CLIENT_QM 1
-#define ILT_CLIENT_SRC 2
-#define ILT_CLIENT_TM 3
-};
-
-/****************************************************************************
-* SRC configuration
-****************************************************************************/
-struct src_ent {
- u8 opaque[56];
- u64 next;
-};
-
-/****************************************************************************
-* Parity configuration
-****************************************************************************/
-#define BLOCK_PRTY_INFO(block, en_mask, m1, m1h, m2) \
-{ \
- block##_REG_##block##_PRTY_MASK, \
- block##_REG_##block##_PRTY_STS_CLR, \
- en_mask, {m1, m1h, m2}, #block \
-}
-
-#define BLOCK_PRTY_INFO_0(block, en_mask, m1, m1h, m2) \
-{ \
- block##_REG_##block##_PRTY_MASK_0, \
- block##_REG_##block##_PRTY_STS_CLR_0, \
- en_mask, {m1, m1h, m2}, #block"_0" \
-}
-
-#define BLOCK_PRTY_INFO_1(block, en_mask, m1, m1h, m2) \
-{ \
- block##_REG_##block##_PRTY_MASK_1, \
- block##_REG_##block##_PRTY_STS_CLR_1, \
- en_mask, {m1, m1h, m2}, #block"_1" \
-}
-
-static const struct {
- u32 mask_addr;
- u32 sts_clr_addr;
- u32 en_mask; /* Mask to enable parity attentions */
- struct {
- u32 e1; /* 57710 */
- u32 e1h; /* 57711 */
- u32 e2; /* 57712 */
- } reg_mask; /* Register mask (all valid bits) */
- char name[7]; /* Block's longest name is 6 characters long
- * (name + suffix)
- */
-} bnx2x_blocks_parity_data[] = {
- /* bit 19 masked */
- /* REG_WR(bp, PXP_REG_PXP_PRTY_MASK, 0x80000); */
- /* bit 5,18,20-31 */
- /* REG_WR(bp, PXP2_REG_PXP2_PRTY_MASK_0, 0xfff40020); */
- /* bit 5 */
- /* REG_WR(bp, PXP2_REG_PXP2_PRTY_MASK_1, 0x20); */
- /* REG_WR(bp, HC_REG_HC_PRTY_MASK, 0x0); */
- /* REG_WR(bp, MISC_REG_MISC_PRTY_MASK, 0x0); */
-
- /* Block IGU, MISC, PXP and PXP2 parity errors as long as we don't
- * want to handle "system kill" flow at the moment.
- */
- BLOCK_PRTY_INFO(PXP, 0x7ffffff, 0x3ffffff, 0x3ffffff, 0x7ffffff),
- BLOCK_PRTY_INFO_0(PXP2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff),
- BLOCK_PRTY_INFO_1(PXP2, 0x7ff, 0x7f, 0x7f, 0x7ff),
- BLOCK_PRTY_INFO(HC, 0x7, 0x7, 0x7, 0),
- BLOCK_PRTY_INFO(IGU, 0x7ff, 0, 0, 0x7ff),
- BLOCK_PRTY_INFO(MISC, 0x1, 0x1, 0x1, 0x1),
- BLOCK_PRTY_INFO(QM, 0, 0x1ff, 0xfff, 0xfff),
- BLOCK_PRTY_INFO(DORQ, 0, 0x3, 0x3, 0x3),
- {GRCBASE_UPB + PB_REG_PB_PRTY_MASK,
- GRCBASE_UPB + PB_REG_PB_PRTY_STS_CLR, 0,
- {0xf, 0xf, 0xf}, "UPB"},
- {GRCBASE_XPB + PB_REG_PB_PRTY_MASK,
- GRCBASE_XPB + PB_REG_PB_PRTY_STS_CLR, 0,
- {0xf, 0xf, 0xf}, "XPB"},
- BLOCK_PRTY_INFO(SRC, 0x4, 0x7, 0x7, 0x7),
- BLOCK_PRTY_INFO(CDU, 0, 0x1f, 0x1f, 0x1f),
- BLOCK_PRTY_INFO(CFC, 0, 0xf, 0xf, 0xf),
- BLOCK_PRTY_INFO(DBG, 0, 0x1, 0x1, 0x1),
- BLOCK_PRTY_INFO(DMAE, 0, 0xf, 0xf, 0xf),
- BLOCK_PRTY_INFO(BRB1, 0, 0xf, 0xf, 0xf),
- BLOCK_PRTY_INFO(PRS, (1<<6), 0xff, 0xff, 0xff),
- BLOCK_PRTY_INFO(TSDM, 0x18, 0x7ff, 0x7ff, 0x7ff),
- BLOCK_PRTY_INFO(CSDM, 0x8, 0x7ff, 0x7ff, 0x7ff),
- BLOCK_PRTY_INFO(USDM, 0x38, 0x7ff, 0x7ff, 0x7ff),
- BLOCK_PRTY_INFO(XSDM, 0x8, 0x7ff, 0x7ff, 0x7ff),
- BLOCK_PRTY_INFO_0(TSEM, 0, 0xffffffff, 0xffffffff, 0xffffffff),
- BLOCK_PRTY_INFO_1(TSEM, 0, 0x3, 0x1f, 0x3f),
- BLOCK_PRTY_INFO_0(USEM, 0, 0xffffffff, 0xffffffff, 0xffffffff),
- BLOCK_PRTY_INFO_1(USEM, 0, 0x3, 0x1f, 0x1f),
- BLOCK_PRTY_INFO_0(CSEM, 0, 0xffffffff, 0xffffffff, 0xffffffff),
- BLOCK_PRTY_INFO_1(CSEM, 0, 0x3, 0x1f, 0x1f),
- BLOCK_PRTY_INFO_0(XSEM, 0, 0xffffffff, 0xffffffff, 0xffffffff),
- BLOCK_PRTY_INFO_1(XSEM, 0, 0x3, 0x1f, 0x3f),
-};
-
-
-/* [28] MCP Latched rom_parity
- * [29] MCP Latched ump_rx_parity
- * [30] MCP Latched ump_tx_parity
- * [31] MCP Latched scpad_parity
- */
-#define MISC_AEU_ENABLE_MCP_PRTY_BITS \
- (AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY | \
- AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY | \
- AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY | \
- AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY)
-
-/* Below registers control the MCP parity attention output. When
- * MISC_AEU_ENABLE_MCP_PRTY_BITS are set - attentions are
- * enabled, when cleared - disabled.
- */
-static const u32 mcp_attn_ctl_regs[] = {
- MISC_REG_AEU_ENABLE4_FUNC_0_OUT_0,
- MISC_REG_AEU_ENABLE4_NIG_0,
- MISC_REG_AEU_ENABLE4_PXP_0,
- MISC_REG_AEU_ENABLE4_FUNC_1_OUT_0,
- MISC_REG_AEU_ENABLE4_NIG_1,
- MISC_REG_AEU_ENABLE4_PXP_1
-};
-
-static inline void bnx2x_set_mcp_parity(struct bnx2x *bp, u8 enable)
-{
- int i;
- u32 reg_val;
-
- for (i = 0; i < ARRAY_SIZE(mcp_attn_ctl_regs); i++) {
- reg_val = REG_RD(bp, mcp_attn_ctl_regs[i]);
-
- if (enable)
- reg_val |= MISC_AEU_ENABLE_MCP_PRTY_BITS;
- else
- reg_val &= ~MISC_AEU_ENABLE_MCP_PRTY_BITS;
-
- REG_WR(bp, mcp_attn_ctl_regs[i], reg_val);
- }
-}
-
-static inline u32 bnx2x_parity_reg_mask(struct bnx2x *bp, int idx)
-{
- if (CHIP_IS_E1(bp))
- return bnx2x_blocks_parity_data[idx].reg_mask.e1;
- else if (CHIP_IS_E1H(bp))
- return bnx2x_blocks_parity_data[idx].reg_mask.e1h;
- else
- return bnx2x_blocks_parity_data[idx].reg_mask.e2;
-}
-
-static inline void bnx2x_disable_blocks_parity(struct bnx2x *bp)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bnx2x_blocks_parity_data); i++) {
- u32 dis_mask = bnx2x_parity_reg_mask(bp, i);
-
- if (dis_mask) {
- REG_WR(bp, bnx2x_blocks_parity_data[i].mask_addr,
- dis_mask);
- DP(NETIF_MSG_HW, "Setting parity mask "
- "for %s to\t\t0x%x\n",
- bnx2x_blocks_parity_data[i].name, dis_mask);
- }
- }
-
- /* Disable MCP parity attentions */
- bnx2x_set_mcp_parity(bp, false);
-}
-
-/**
- * Clear the parity error status registers.
- */
-static inline void bnx2x_clear_blocks_parity(struct bnx2x *bp)
-{
- int i;
- u32 reg_val, mcp_aeu_bits =
- AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY |
- AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY |
- AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY |
- AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY;
-
- /* Clear SEM_FAST parities */
- REG_WR(bp, XSEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
- REG_WR(bp, TSEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
- REG_WR(bp, USEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
- REG_WR(bp, CSEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
-
- for (i = 0; i < ARRAY_SIZE(bnx2x_blocks_parity_data); i++) {
- u32 reg_mask = bnx2x_parity_reg_mask(bp, i);
-
- if (reg_mask) {
- reg_val = REG_RD(bp, bnx2x_blocks_parity_data[i].
- sts_clr_addr);
- if (reg_val & reg_mask)
- DP(NETIF_MSG_HW,
- "Parity errors in %s: 0x%x\n",
- bnx2x_blocks_parity_data[i].name,
- reg_val & reg_mask);
- }
- }
-
- /* Check if there were parity attentions in MCP */
- reg_val = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_4_MCP);
- if (reg_val & mcp_aeu_bits)
- DP(NETIF_MSG_HW, "Parity error in MCP: 0x%x\n",
- reg_val & mcp_aeu_bits);
-
- /* Clear parity attentions in MCP:
- * [7] clears Latched rom_parity
- * [8] clears Latched ump_rx_parity
- * [9] clears Latched ump_tx_parity
- * [10] clears Latched scpad_parity (both ports)
- */
- REG_WR(bp, MISC_REG_AEU_CLR_LATCH_SIGNAL, 0x780);
-}
-
-static inline void bnx2x_enable_blocks_parity(struct bnx2x *bp)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bnx2x_blocks_parity_data); i++) {
- u32 reg_mask = bnx2x_parity_reg_mask(bp, i);
-
- if (reg_mask)
- REG_WR(bp, bnx2x_blocks_parity_data[i].mask_addr,
- bnx2x_blocks_parity_data[i].en_mask & reg_mask);
- }
-
- /* Enable MCP parity attentions */
- bnx2x_set_mcp_parity(bp, true);
-}
-
-
-#endif /* BNX2X_INIT_H */
-
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index c7537abca4f2..0ae0d7c54ccf 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -262,7 +262,7 @@ static inline u32 __get_agg_selection_mode(struct port *port)
if (bond == NULL)
return BOND_AD_STABLE;
- return BOND_AD_INFO(bond).agg_select_mode;
+ return bond->params.ad_select;
}
/**
@@ -1135,13 +1135,6 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
__record_pdu(lacpdu, port);
port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT));
port->actor_oper_port_state &= ~AD_STATE_EXPIRED;
- // verify that if the aggregator is enabled, the port is enabled too.
- //(because if the link goes down for a short time, the 802.3ad will not
- // catch it, and the port will continue to be disabled)
- if (port->aggregator
- && port->aggregator->is_active
- && !__port_is_enabled(port))
- __enable_port(port);
break;
default: //to silence the compiler
break;
@@ -1859,7 +1852,6 @@ static void ad_marker_response_received(struct bond_marker *marker,
void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout)
{
BOND_AD_INFO(bond).agg_select_timer = timeout;
- BOND_AD_INFO(bond).agg_select_mode = bond->params.ad_select;
}
static u16 aggregator_identifier;
@@ -1868,11 +1860,10 @@ static u16 aggregator_identifier;
* bond_3ad_initialize - initialize a bond's 802.3ad parameters and structures
* @bond: bonding struct to work on
* @tick_resolution: tick duration (millisecond resolution)
- * @lacp_fast: boolean. whether fast periodic should be used
*
* Can be called only after the mac address of the bond is set.
*/
-void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fast)
+void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution)
{
// check that the bond is not initialized yet
if (MAC_ADDRESS_COMPARE(&(BOND_AD_INFO(bond).system.sys_mac_addr),
@@ -1880,7 +1871,6 @@ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fas
aggregator_identifier = 0;
- BOND_AD_INFO(bond).lacp_fast = lacp_fast;
BOND_AD_INFO(bond).system.sys_priority = 0xFFFF;
BOND_AD_INFO(bond).system.sys_mac_addr = *((struct mac_addr *)bond->dev->dev_addr);
@@ -1918,7 +1908,7 @@ int bond_3ad_bind_slave(struct slave *slave)
// port initialization
port = &(SLAVE_AD_INFO(slave).port);
- ad_initialize_port(port, BOND_AD_INFO(bond).lacp_fast);
+ ad_initialize_port(port, bond->params.lacp_fast);
port->slave = slave;
port->actor_port_number = SLAVE_AD_INFO(slave).id;
@@ -2120,9 +2110,6 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
read_lock(&bond->lock);
- if (bond->kill_timers)
- goto out;
-
//check if there are any slaves
if (bond->slave_cnt == 0)
goto re_arm;
@@ -2172,7 +2159,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
re_arm:
queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks);
-out:
+
read_unlock(&bond->lock);
}
@@ -2345,8 +2332,17 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
*/
int bond_3ad_set_carrier(struct bonding *bond)
{
- if (__get_active_agg(&(SLAVE_AD_INFO(bond->first_slave).aggregator))) {
- if (!netif_carrier_ok(bond->dev)) {
+ struct aggregator *active;
+
+ active = __get_active_agg(&(SLAVE_AD_INFO(bond->first_slave).aggregator));
+ if (active) {
+ /* are enough slaves available to consider link up? */
+ if (active->num_of_ports < bond->params.min_links) {
+ if (netif_carrier_ok(bond->dev)) {
+ netif_carrier_off(bond->dev);
+ return 1;
+ }
+ } else if (!netif_carrier_ok(bond->dev)) {
netif_carrier_on(bond->dev);
return 1;
}
@@ -2473,3 +2469,34 @@ void bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond,
bond_3ad_rx_indication((struct lacpdu *) skb->data, slave, skb->len);
read_unlock(&bond->lock);
}
+
+/*
+ * When modify lacp_rate parameter via sysfs,
+ * update actor_oper_port_state of each port.
+ *
+ * Hold slave->state_machine_lock,
+ * so we can modify port->actor_oper_port_state,
+ * no matter bond is up or down.
+ */
+void bond_3ad_update_lacp_rate(struct bonding *bond)
+{
+ int i;
+ struct slave *slave;
+ struct port *port = NULL;
+ int lacp_fast;
+
+ read_lock(&bond->lock);
+ lacp_fast = bond->params.lacp_fast;
+
+ bond_for_each_slave(bond, slave, i) {
+ port = &(SLAVE_AD_INFO(slave).port);
+ __get_state_machine_lock(port);
+ if (lacp_fast)
+ port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT;
+ else
+ port->actor_oper_port_state &= ~AD_STATE_LACP_TIMEOUT;
+ __release_state_machine_lock(port);
+ }
+
+ read_unlock(&bond->lock);
+}
diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h
index 0ee3f1632c46..235b2cc58b28 100644
--- a/drivers/net/bonding/bond_3ad.h
+++ b/drivers/net/bonding/bond_3ad.h
@@ -253,11 +253,6 @@ struct ad_system {
struct ad_bond_info {
struct ad_system system; /* 802.3ad system structure */
u32 agg_select_timer; // Timer to select aggregator after all adapter's hand shakes
- u32 agg_select_mode; // Mode of selection of active aggregator(bandwidth/count)
- int lacp_fast; /* whether fast periodic tx should be
- * requested
- */
- struct timer_list ad_timer;
};
struct ad_slave_info {
@@ -269,7 +264,7 @@ struct ad_slave_info {
};
// ================= AD Exported functions to the main bonding code ==================
-void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fast);
+void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution);
int bond_3ad_bind_slave(struct slave *slave);
void bond_3ad_unbind_slave(struct slave *slave);
void bond_3ad_state_machine_handler(struct work_struct *);
@@ -282,5 +277,6 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev);
void bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond,
struct slave *slave);
int bond_3ad_set_carrier(struct bonding *bond);
+void bond_3ad_update_lacp_rate(struct bonding *bond);
#endif //__BOND_3AD_H__
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 2df9276720a0..106b88a04738 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -635,7 +635,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
client_info->ntt = 0;
}
- if (bond->vlgrp) {
+ if (bond_vlan_used(bond)) {
if (!vlan_get_tag(skb, &client_info->vlan_id))
client_info->tag = 1;
}
@@ -847,7 +847,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
skb->priority = TC_PRIO_CONTROL;
skb->dev = slave->dev;
- if (bond->vlgrp) {
+ if (bond_vlan_used(bond)) {
struct vlan_entry *vlan;
vlan = bond_next_vlan(bond,
@@ -1343,10 +1343,6 @@ void bond_alb_monitor(struct work_struct *work)
read_lock(&bond->lock);
- if (bond->kill_timers) {
- goto out;
- }
-
if (bond->slave_cnt == 0) {
bond_info->tx_rebalance_counter = 0;
bond_info->lp_counter = 0;
@@ -1401,10 +1397,13 @@ void bond_alb_monitor(struct work_struct *work)
/*
* dev_set_promiscuity requires rtnl and
- * nothing else.
+ * nothing else. Avoid race with bond_close.
*/
read_unlock(&bond->lock);
- rtnl_lock();
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ goto re_arm;
+ }
bond_info->rlb_promisc_timeout_counter = 0;
@@ -1441,7 +1440,7 @@ void bond_alb_monitor(struct work_struct *work)
re_arm:
queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks);
-out:
+
read_unlock(&bond->lock);
}
diff --git a/drivers/net/bonding/bond_ipv6.c b/drivers/net/bonding/bond_ipv6.c
index 84fbd4ebd778..027a0ee7d85b 100644
--- a/drivers/net/bonding/bond_ipv6.c
+++ b/drivers/net/bonding/bond_ipv6.c
@@ -183,10 +183,10 @@ static int bond_inet6addr_event(struct notifier_block *this,
}
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- if (!bond->vlgrp)
- continue;
- vlan_dev = vlan_group_get_device(bond->vlgrp,
- vlan->vlan_id);
+ rcu_read_lock();
+ vlan_dev = __vlan_find_dev_deep(bond->dev,
+ vlan->vlan_id);
+ rcu_read_unlock();
if (vlan_dev == event_dev) {
switch (event) {
case NETDEV_UP:
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index eafe44a528ac..b0c577256487 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -98,6 +98,7 @@ static char *mode;
static char *primary;
static char *primary_reselect;
static char *lacp_rate;
+static int min_links;
static char *ad_select;
static char *xmit_hash_policy;
static int arp_interval = BOND_LINK_ARP_INTERV;
@@ -150,6 +151,9 @@ module_param(ad_select, charp, 0);
MODULE_PARM_DESC(ad_select, "803.ad aggregation selection logic; "
"0 for stable (default), 1 for bandwidth, "
"2 for count");
+module_param(min_links, int, 0);
+MODULE_PARM_DESC(min_links, "Minimum number of available links before turning on carrier");
+
module_param(xmit_hash_policy, charp, 0);
MODULE_PARM_DESC(xmit_hash_policy, "balance-xor and 802.3ad hashing method; "
"0 for layer 2 (default), 1 for layer 3+4, "
@@ -329,16 +333,6 @@ static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
kfree(vlan);
- if (list_empty(&bond->vlan_list) &&
- (bond->slave_cnt == 0)) {
- /* Last VLAN removed and no slaves, so
- * restore block on adding VLANs. This will
- * be removed once new slaves that are not
- * VLAN challenged will be added.
- */
- bond->dev->features |= NETIF_F_VLAN_CHALLENGED;
- }
-
res = 0;
goto out;
}
@@ -401,7 +395,6 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
struct net_device *slave_dev)
{
skb->dev = slave_dev;
- skb->priority = 1;
skb->queue_mapping = bond_queue_mapping(skb);
@@ -414,9 +407,8 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
}
/*
- * In the following 3 functions, bond_vlan_rx_register(), bond_vlan_rx_add_vid
- * and bond_vlan_rx_kill_vid, We don't protect the slave list iteration with a
- * lock because:
+ * In the following 2 functions, bond_vlan_rx_add_vid and bond_vlan_rx_kill_vid,
+ * We don't protect the slave list iteration with a lock because:
* a. This operation is performed in IOCTL context,
* b. The operation is protected by the RTNL semaphore in the 8021q code,
* c. Holding a lock with BH disabled while directly calling a base driver
@@ -432,33 +424,6 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
*/
/**
- * bond_vlan_rx_register - Propagates registration to slaves
- * @bond_dev: bonding net device that got called
- * @grp: vlan group being registered
- */
-static void bond_vlan_rx_register(struct net_device *bond_dev,
- struct vlan_group *grp)
-{
- struct bonding *bond = netdev_priv(bond_dev);
- struct slave *slave;
- int i;
-
- write_lock_bh(&bond->lock);
- bond->vlgrp = grp;
- write_unlock_bh(&bond->lock);
-
- bond_for_each_slave(bond, slave, i) {
- struct net_device *slave_dev = slave->dev;
- const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
-
- if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
- slave_ops->ndo_vlan_rx_register) {
- slave_ops->ndo_vlan_rx_register(slave_dev, grp);
- }
- }
-}
-
-/**
* bond_vlan_rx_add_vid - Propagates adding an id to slaves
* @bond_dev: bonding net device that got called
* @vid: vlan id being added
@@ -495,7 +460,6 @@ static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
{
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave;
- struct net_device *vlan_dev;
int i, res;
bond_for_each_slave(bond, slave, i) {
@@ -504,12 +468,7 @@ static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
if ((slave_dev->features & NETIF_F_HW_VLAN_FILTER) &&
slave_ops->ndo_vlan_rx_kill_vid) {
- /* Save and then restore vlan_dev in the grp array,
- * since the slave's driver might clear it.
- */
- vlan_dev = vlan_group_get_device(bond->vlgrp, vid);
slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vid);
- vlan_group_set_device(bond->vlgrp, vid, vlan_dev);
}
}
@@ -525,13 +484,6 @@ static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *sla
struct vlan_entry *vlan;
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
- if (!bond->vlgrp)
- return;
-
- if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
- slave_ops->ndo_vlan_rx_register)
- slave_ops->ndo_vlan_rx_register(slave_dev, bond->vlgrp);
-
if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) ||
!(slave_ops->ndo_vlan_rx_add_vid))
return;
@@ -545,30 +497,16 @@ static void bond_del_vlans_from_slave(struct bonding *bond,
{
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
struct vlan_entry *vlan;
- struct net_device *vlan_dev;
-
- if (!bond->vlgrp)
- return;
if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) ||
!(slave_ops->ndo_vlan_rx_kill_vid))
- goto unreg;
+ return;
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
if (!vlan->vlan_id)
continue;
- /* Save and then restore vlan_dev in the grp array,
- * since the slave's driver might clear it.
- */
- vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vlan->vlan_id);
- vlan_group_set_device(bond->vlgrp, vlan->vlan_id, vlan_dev);
}
-
-unreg:
- if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
- slave_ops->ndo_vlan_rx_register)
- slave_ops->ndo_vlan_rx_register(slave_dev, NULL);
}
/*------------------------------- Link status -------------------------------*/
@@ -612,39 +550,28 @@ down:
/*
* Get link speed and duplex from the slave's base driver
* using ethtool. If for some reason the call fails or the
- * values are invalid, fake speed and duplex to 100/Full
+ * values are invalid, set speed and duplex to -1,
* and return error.
*/
static int bond_update_speed_duplex(struct slave *slave)
{
struct net_device *slave_dev = slave->dev;
- struct ethtool_cmd etool = { .cmd = ETHTOOL_GSET };
+ struct ethtool_cmd ecmd;
u32 slave_speed;
int res;
- /* Fake speed and duplex */
- slave->speed = SPEED_100;
- slave->duplex = DUPLEX_FULL;
-
- if (!slave_dev->ethtool_ops || !slave_dev->ethtool_ops->get_settings)
- return -1;
+ slave->speed = SPEED_UNKNOWN;
+ slave->duplex = DUPLEX_UNKNOWN;
- res = slave_dev->ethtool_ops->get_settings(slave_dev, &etool);
+ res = __ethtool_get_settings(slave_dev, &ecmd);
if (res < 0)
return -1;
- slave_speed = ethtool_cmd_speed(&etool);
- switch (slave_speed) {
- case SPEED_10:
- case SPEED_100:
- case SPEED_1000:
- case SPEED_10000:
- break;
- default:
+ slave_speed = ethtool_cmd_speed(&ecmd);
+ if (slave_speed == 0 || slave_speed == ((__u32) -1))
return -1;
- }
- switch (etool.duplex) {
+ switch (ecmd.duplex) {
case DUPLEX_FULL:
case DUPLEX_HALF:
break;
@@ -653,7 +580,7 @@ static int bond_update_speed_duplex(struct slave *slave)
}
slave->speed = slave_speed;
- slave->duplex = etool.duplex;
+ slave->duplex = ecmd.duplex;
return 0;
}
@@ -849,13 +776,13 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
__bond_resend_igmp_join_requests(bond->dev);
/* rejoin all groups on vlan devices */
- if (bond->vlgrp) {
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- vlan_dev = vlan_group_get_device(bond->vlgrp,
- vlan->vlan_id);
- if (vlan_dev)
- __bond_resend_igmp_join_requests(vlan_dev);
- }
+ list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
+ rcu_read_lock();
+ vlan_dev = __vlan_find_dev_deep(bond->dev,
+ vlan->vlan_id);
+ rcu_read_unlock();
+ if (vlan_dev)
+ __bond_resend_igmp_join_requests(vlan_dev);
}
if (--bond->igmp_retrans > 0)
@@ -1428,9 +1355,9 @@ out:
return features;
}
-#define BOND_VLAN_FEATURES (NETIF_F_ALL_TX_OFFLOADS | \
- NETIF_F_SOFT_FEATURES | \
- NETIF_F_LRO)
+#define BOND_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
+ NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
+ NETIF_F_HIGHDMA | NETIF_F_LRO)
static void bond_compute_features(struct bonding *bond)
{
@@ -1500,6 +1427,8 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
struct sk_buff *skb = *pskb;
struct slave *slave;
struct bonding *bond;
+ void (*recv_probe)(struct sk_buff *, struct bonding *,
+ struct slave *);
skb = skb_share_check(skb, GFP_ATOMIC);
if (unlikely(!skb))
@@ -1513,11 +1442,12 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
if (bond->params.arp_interval)
slave->dev->last_rx = jiffies;
- if (bond->recv_probe) {
+ recv_probe = ACCESS_ONCE(bond->recv_probe);
+ if (recv_probe) {
struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
if (likely(nskb)) {
- bond->recv_probe(nskb, bond, slave);
+ recv_probe(nskb, bond, slave);
dev_kfree_skb(nskb);
}
}
@@ -1570,7 +1500,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
/* no need to lock since we're protected by rtnl_lock */
if (slave_dev->features & NETIF_F_VLAN_CHALLENGED) {
pr_debug("%s: NETIF_F_VLAN_CHALLENGED\n", slave_dev->name);
- if (bond->vlgrp) {
+ if (bond_vlan_used(bond)) {
pr_err("%s: Error: cannot enslave VLAN challenged slave %s on VLAN enabled bond %s\n",
bond_dev->name, slave_dev->name, bond_dev->name);
return -EPERM;
@@ -1625,8 +1555,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
if (slave_dev->type != ARPHRD_ETHER)
bond_setup_by_slave(bond_dev, slave_dev);
- else
+ else {
ether_setup(bond_dev);
+ bond_dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+ }
netdev_bonding_change(bond_dev,
NETDEV_POST_TYPE_CHANGE);
@@ -1818,16 +1750,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
new_slave->link = BOND_LINK_DOWN;
}
- if (bond_update_speed_duplex(new_slave) &&
- (new_slave->link != BOND_LINK_DOWN)) {
- pr_warning("%s: Warning: failed to get speed and duplex from %s, assumed to be 100Mb/sec and Full.\n",
- bond_dev->name, new_slave->dev->name);
-
- if (bond->params.mode == BOND_MODE_8023AD) {
- pr_warning("%s: Warning: Operation of 802.3ad mode requires ETHTOOL support in base driver for proper aggregator selection.\n",
- bond_dev->name);
- }
- }
+ bond_update_speed_duplex(new_slave);
if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) {
/* if there is a primary slave, remember it */
@@ -1856,8 +1779,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
/* Initialize AD with the number of times that the AD timer is called in 1 second
* can be called only after the mac address of the bond is set
*/
- bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL,
- bond->params.lacp_fast);
+ bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL);
} else {
SLAVE_AD_INFO(new_slave).id =
SLAVE_AD_INFO(new_slave->prev).id + 1;
@@ -2079,7 +2001,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
*/
memset(bond_dev->dev_addr, 0, bond_dev->addr_len);
- if (bond->vlgrp) {
+ if (bond_vlan_used(bond)) {
pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
bond_dev->name, bond_dev->name);
pr_warning("%s: When re-adding slaves, make sure the bond's HW address matches its VLANs'.\n",
@@ -2261,7 +2183,7 @@ static int bond_release_all(struct net_device *bond_dev)
*/
memset(bond_dev->dev_addr, 0, bond_dev->addr_len);
- if (bond->vlgrp) {
+ if (bond_vlan_used(bond)) {
pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
bond_dev->name, bond_dev->name);
pr_warning("%s: When re-adding slaves, make sure the bond's HW address matches its VLANs'.\n",
@@ -2582,10 +2504,11 @@ void bond_mii_monitor(struct work_struct *work)
struct bonding *bond = container_of(work, struct bonding,
mii_work.work);
bool should_notify_peers = false;
+ unsigned long delay;
read_lock(&bond->lock);
- if (bond->kill_timers)
- goto out;
+
+ delay = msecs_to_jiffies(bond->params.miimon);
if (bond->slave_cnt == 0)
goto re_arm;
@@ -2594,7 +2517,15 @@ void bond_mii_monitor(struct work_struct *work)
if (bond_miimon_inspect(bond)) {
read_unlock(&bond->lock);
- rtnl_lock();
+
+ /* Race avoidance with bond_close cancel of workqueue */
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ delay = 1;
+ should_notify_peers = false;
+ goto re_arm;
+ }
+
read_lock(&bond->lock);
bond_miimon_commit(bond);
@@ -2606,13 +2537,17 @@ void bond_mii_monitor(struct work_struct *work)
re_arm:
if (bond->params.miimon)
- queue_delayed_work(bond->wq, &bond->mii_work,
- msecs_to_jiffies(bond->params.miimon));
-out:
+ queue_delayed_work(bond->wq, &bond->mii_work, delay);
+
read_unlock(&bond->lock);
if (should_notify_peers) {
- rtnl_lock();
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ bond->send_peer_notif++;
+ read_unlock(&bond->lock);
+ return;
+ }
netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
rtnl_unlock();
}
@@ -2699,7 +2634,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
if (!targets[i])
break;
pr_debug("basa: target %x\n", targets[i]);
- if (!bond->vlgrp) {
+ if (!bond_vlan_used(bond)) {
pr_debug("basa: empty vlan: arp_send\n");
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
bond->master_ip, 0);
@@ -2734,7 +2669,10 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
vlan_id = 0;
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
+ rcu_read_lock();
+ vlan_dev = __vlan_find_dev_deep(bond->dev,
+ vlan->vlan_id);
+ rcu_read_unlock();
if (vlan_dev == rt->dst.dev) {
vlan_id = vlan->vlan_id;
pr_debug("basa: vlan match on %s %d\n",
@@ -2851,9 +2789,6 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
- if (bond->kill_timers)
- goto out;
-
if (bond->slave_cnt == 0)
goto re_arm;
@@ -2952,7 +2887,7 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
re_arm:
if (bond->params.arp_interval)
queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
-out:
+
read_unlock(&bond->lock);
}
@@ -3193,9 +3128,6 @@ void bond_activebackup_arp_mon(struct work_struct *work)
read_lock(&bond->lock);
- if (bond->kill_timers)
- goto out;
-
delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
if (bond->slave_cnt == 0)
@@ -3205,7 +3137,15 @@ void bond_activebackup_arp_mon(struct work_struct *work)
if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
read_unlock(&bond->lock);
- rtnl_lock();
+
+ /* Race avoidance with bond_close flush of workqueue */
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ delta_in_ticks = 1;
+ should_notify_peers = false;
+ goto re_arm;
+ }
+
read_lock(&bond->lock);
bond_ab_arp_commit(bond, delta_in_ticks);
@@ -3220,11 +3160,16 @@ void bond_activebackup_arp_mon(struct work_struct *work)
re_arm:
if (bond->params.arp_interval)
queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
-out:
+
read_unlock(&bond->lock);
if (should_notify_peers) {
- rtnl_lock();
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ bond->send_peer_notif++;
+ read_unlock(&bond->lock);
+ return;
+ }
netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
rtnl_unlock();
}
@@ -3265,6 +3210,7 @@ static int bond_slave_netdev_event(unsigned long event,
{
struct net_device *bond_dev = slave_dev->master;
struct bonding *bond = netdev_priv(bond_dev);
+ struct slave *slave = NULL;
switch (event) {
case NETDEV_UNREGISTER:
@@ -3275,20 +3221,16 @@ static int bond_slave_netdev_event(unsigned long event,
bond_release(bond_dev, slave_dev);
}
break;
+ case NETDEV_UP:
case NETDEV_CHANGE:
- if (bond->params.mode == BOND_MODE_8023AD || bond_is_lb(bond)) {
- struct slave *slave;
-
- slave = bond_get_slave_by_dev(bond, slave_dev);
- if (slave) {
- u32 old_speed = slave->speed;
- u8 old_duplex = slave->duplex;
+ slave = bond_get_slave_by_dev(bond, slave_dev);
+ if (slave) {
+ u32 old_speed = slave->speed;
+ u8 old_duplex = slave->duplex;
- bond_update_speed_duplex(slave);
-
- if (bond_is_lb(bond))
- break;
+ bond_update_speed_duplex(slave);
+ if (bond->params.mode == BOND_MODE_8023AD) {
if (old_speed != slave->speed)
bond_3ad_adapter_speed_changed(slave);
if (old_duplex != slave->duplex)
@@ -3395,9 +3337,8 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event,
}
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- if (!bond->vlgrp)
- continue;
- vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
+ vlan_dev = __vlan_find_dev_deep(bond->dev,
+ vlan->vlan_id);
if (vlan_dev == event_dev) {
switch (event) {
case NETDEV_UP:
@@ -3456,7 +3397,7 @@ static int bond_xmit_hash_policy_l34(struct sk_buff *skb, int count)
int layer4_xor = 0;
if (skb->protocol == htons(ETH_P_IP)) {
- if (!(iph->frag_off & htons(IP_MF|IP_OFFSET)) &&
+ if (!ip_is_fragment(iph) &&
(iph->protocol == IPPROTO_TCP ||
iph->protocol == IPPROTO_UDP)) {
layer4_xor = ntohs((*layer4hdr ^ *(layer4hdr + 1)));
@@ -3484,8 +3425,24 @@ static int bond_xmit_hash_policy_l2(struct sk_buff *skb, int count)
static int bond_open(struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
+ struct slave *slave;
+ int i;
- bond->kill_timers = 0;
+ /* reset slave->backup and slave->inactive */
+ read_lock(&bond->lock);
+ if (bond->slave_cnt > 0) {
+ read_lock(&bond->curr_slave_lock);
+ bond_for_each_slave(bond, slave, i) {
+ if ((bond->params.mode == BOND_MODE_ACTIVEBACKUP)
+ && (slave != bond->curr_active_slave)) {
+ bond_set_slave_inactive_flags(slave);
+ } else {
+ bond_set_slave_active_flags(slave);
+ }
+ }
+ read_unlock(&bond->curr_slave_lock);
+ }
+ read_unlock(&bond->lock);
INIT_DELAYED_WORK(&bond->mcast_work, bond_resend_igmp_join_requests_delayed);
@@ -3539,33 +3496,30 @@ static int bond_close(struct net_device *bond_dev)
bond->send_peer_notif = 0;
- /* signal timers not to re-arm */
- bond->kill_timers = 1;
-
write_unlock_bh(&bond->lock);
if (bond->params.miimon) { /* link check interval, in milliseconds. */
- cancel_delayed_work(&bond->mii_work);
+ cancel_delayed_work_sync(&bond->mii_work);
}
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
- cancel_delayed_work(&bond->arp_work);
+ cancel_delayed_work_sync(&bond->arp_work);
}
switch (bond->params.mode) {
case BOND_MODE_8023AD:
- cancel_delayed_work(&bond->ad_work);
+ cancel_delayed_work_sync(&bond->ad_work);
break;
case BOND_MODE_TLB:
case BOND_MODE_ALB:
- cancel_delayed_work(&bond->alb_work);
+ cancel_delayed_work_sync(&bond->alb_work);
break;
default:
break;
}
if (delayed_work_pending(&bond->mcast_work))
- cancel_delayed_work(&bond->mcast_work);
+ cancel_delayed_work_sync(&bond->mcast_work);
if (bond_is_lb(bond)) {
/* Must be called only after all
@@ -3751,44 +3705,27 @@ static bool bond_addr_in_mc_list(unsigned char *addr,
return false;
}
-static void bond_set_multicast_list(struct net_device *bond_dev)
+static void bond_change_rx_flags(struct net_device *bond_dev, int change)
{
struct bonding *bond = netdev_priv(bond_dev);
- struct netdev_hw_addr *ha;
- bool found;
-
- /*
- * Do promisc before checking multicast_mode
- */
- if ((bond_dev->flags & IFF_PROMISC) && !(bond->flags & IFF_PROMISC))
- /*
- * FIXME: Need to handle the error when one of the multi-slaves
- * encounters error.
- */
- bond_set_promiscuity(bond, 1);
-
-
- if (!(bond_dev->flags & IFF_PROMISC) && (bond->flags & IFF_PROMISC))
- bond_set_promiscuity(bond, -1);
-
- /* set allmulti flag to slaves */
- if ((bond_dev->flags & IFF_ALLMULTI) && !(bond->flags & IFF_ALLMULTI))
- /*
- * FIXME: Need to handle the error when one of the multi-slaves
- * encounters error.
- */
- bond_set_allmulti(bond, 1);
+ if (change & IFF_PROMISC)
+ bond_set_promiscuity(bond,
+ bond_dev->flags & IFF_PROMISC ? 1 : -1);
+ if (change & IFF_ALLMULTI)
+ bond_set_allmulti(bond,
+ bond_dev->flags & IFF_ALLMULTI ? 1 : -1);
+}
- if (!(bond_dev->flags & IFF_ALLMULTI) && (bond->flags & IFF_ALLMULTI))
- bond_set_allmulti(bond, -1);
-
+static void bond_set_multicast_list(struct net_device *bond_dev)
+{
+ struct bonding *bond = netdev_priv(bond_dev);
+ struct netdev_hw_addr *ha;
+ bool found;
read_lock(&bond->lock);
- bond->flags = bond_dev->flags;
-
/* looking for addresses to add to slaves' mc list */
netdev_for_each_mc_addr(ha, bond_dev) {
found = bond_addr_in_mc_list(ha->addr, &bond->mc_list,
@@ -4347,12 +4284,12 @@ static const struct net_device_ops bond_netdev_ops = {
.ndo_select_queue = bond_select_queue,
.ndo_get_stats64 = bond_get_stats,
.ndo_do_ioctl = bond_do_ioctl,
- .ndo_set_multicast_list = bond_set_multicast_list,
+ .ndo_change_rx_flags = bond_change_rx_flags,
+ .ndo_set_rx_mode = bond_set_multicast_list,
.ndo_change_mtu = bond_change_mtu,
- .ndo_set_mac_address = bond_set_mac_address,
+ .ndo_set_mac_address = bond_set_mac_address,
.ndo_neigh_setup = bond_neigh_setup,
- .ndo_vlan_rx_register = bond_vlan_rx_register,
- .ndo_vlan_rx_add_vid = bond_vlan_rx_add_vid,
+ .ndo_vlan_rx_add_vid = bond_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_netpoll_setup = bond_netpoll_setup,
@@ -4398,7 +4335,7 @@ static void bond_setup(struct net_device *bond_dev)
bond_dev->tx_queue_len = 0;
bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
bond_dev->priv_flags |= IFF_BONDING;
- bond_dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+ bond_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
/* At first, we block adding VLANs. That's the only way to
* prevent problems that occur when adding VLANs over an
@@ -4429,26 +4366,22 @@ static void bond_setup(struct net_device *bond_dev)
static void bond_work_cancel_all(struct bonding *bond)
{
- write_lock_bh(&bond->lock);
- bond->kill_timers = 1;
- write_unlock_bh(&bond->lock);
-
if (bond->params.miimon && delayed_work_pending(&bond->mii_work))
- cancel_delayed_work(&bond->mii_work);
+ cancel_delayed_work_sync(&bond->mii_work);
if (bond->params.arp_interval && delayed_work_pending(&bond->arp_work))
- cancel_delayed_work(&bond->arp_work);
+ cancel_delayed_work_sync(&bond->arp_work);
if (bond->params.mode == BOND_MODE_ALB &&
delayed_work_pending(&bond->alb_work))
- cancel_delayed_work(&bond->alb_work);
+ cancel_delayed_work_sync(&bond->alb_work);
if (bond->params.mode == BOND_MODE_8023AD &&
delayed_work_pending(&bond->ad_work))
- cancel_delayed_work(&bond->ad_work);
+ cancel_delayed_work_sync(&bond->ad_work);
if (delayed_work_pending(&bond->mcast_work))
- cancel_delayed_work(&bond->mcast_work);
+ cancel_delayed_work_sync(&bond->mcast_work);
}
/*
@@ -4759,7 +4692,7 @@ static int bond_check_params(struct bond_params *params)
/* miimon and arp_interval not set, we need one so things
* work as expected, see bonding.txt for details
*/
- pr_warning("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details.\n");
+ pr_debug("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details.\n");
}
if (primary && !USES_PRIMARY(bond_mode)) {
@@ -4816,6 +4749,7 @@ static int bond_check_params(struct bond_params *params)
params->tx_queues = tx_queues;
params->all_slaves_active = all_slaves_active;
params->resend_igmp = resend_igmp;
+ params->min_links = min_links;
if (primary) {
strncpy(params->primary, primary, IFNAMSIZ);
@@ -4893,11 +4827,20 @@ static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
return 0;
}
+static int bond_get_tx_queues(struct net *net, struct nlattr *tb[],
+ unsigned int *num_queues,
+ unsigned int *real_num_queues)
+{
+ *num_queues = tx_queues;
+ return 0;
+}
+
static struct rtnl_link_ops bond_link_ops __read_mostly = {
.kind = "bond",
.priv_size = sizeof(struct bonding),
.setup = bond_setup,
.validate = bond_validate,
+ .get_tx_queues = bond_get_tx_queues,
};
/* Create a new bond based on the specified name and bonding parameters.
@@ -4942,6 +4885,7 @@ static int __net_init bond_net_init(struct net *net)
INIT_LIST_HEAD(&bn->dev_list);
bond_create_proc_dir(bn);
+ bond_create_sysfs(bn);
return 0;
}
@@ -4950,6 +4894,7 @@ static void __net_exit bond_net_exit(struct net *net)
{
struct bond_net *bn = net_generic(net, bond_net_id);
+ bond_destroy_sysfs(bn);
bond_destroy_proc_dir(bn);
}
@@ -4987,10 +4932,6 @@ static int __init bonding_init(void)
goto err;
}
- res = bond_create_sysfs();
- if (res)
- goto err;
-
register_netdevice_notifier(&bond_netdev_notifier);
register_inetaddr_notifier(&bond_inetaddr_notifier);
out:
@@ -5008,7 +4949,6 @@ static void __exit bonding_exit(void)
unregister_netdevice_notifier(&bond_netdev_notifier);
unregister_inetaddr_notifier(&bond_inetaddr_notifier);
- bond_destroy_sysfs();
bond_destroy_debugfs();
rtnl_link_unregister(&bond_link_ops);
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c
index c97307ddd1c9..ad284baafe87 100644
--- a/drivers/net/bonding/bond_procfs.c
+++ b/drivers/net/bonding/bond_procfs.c
@@ -1,4 +1,5 @@
#include <linux/proc_fs.h>
+#include <linux/export.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include "bonding.h"
@@ -125,6 +126,7 @@ static void bond_info_show_master(struct seq_file *seq)
seq_puts(seq, "\n802.3ad info\n");
seq_printf(seq, "LACP rate: %s\n",
(bond->params.lacp_fast) ? "fast" : "slow");
+ seq_printf(seq, "Min links: %d\n", bond->params.min_links);
seq_printf(seq, "Aggregator selection policy (ad_select): %s\n",
ad_select_tbl[bond->params.ad_select].modename);
@@ -156,8 +158,16 @@ static void bond_info_show_slave(struct seq_file *seq,
seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name);
seq_printf(seq, "MII Status: %s\n",
(slave->link == BOND_LINK_UP) ? "up" : "down");
- seq_printf(seq, "Speed: %d Mbps\n", slave->speed);
- seq_printf(seq, "Duplex: %s\n", slave->duplex ? "full" : "half");
+ if (slave->speed == SPEED_UNKNOWN)
+ seq_printf(seq, "Speed: %s\n", "Unknown");
+ else
+ seq_printf(seq, "Speed: %d Mbps\n", slave->speed);
+
+ if (slave->duplex == DUPLEX_UNKNOWN)
+ seq_printf(seq, "Duplex: %s\n", "Unknown");
+ else
+ seq_printf(seq, "Duplex: %s\n", slave->duplex ? "full" : "half");
+
seq_printf(seq, "Link Failure Count: %u\n",
slave->link_failure_count);
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 88fcb25e554a..4ef7e2fd9fe6 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -55,8 +55,8 @@ static ssize_t bonding_show_bonds(struct class *cls,
struct class_attribute *attr,
char *buf)
{
- struct net *net = current->nsproxy->net_ns;
- struct bond_net *bn = net_generic(net, bond_net_id);
+ struct bond_net *bn =
+ container_of(attr, struct bond_net, class_attr_bonding_masters);
int res = 0;
struct bonding *bond;
@@ -79,9 +79,8 @@ static ssize_t bonding_show_bonds(struct class *cls,
return res;
}
-static struct net_device *bond_get_by_name(struct net *net, const char *ifname)
+static struct net_device *bond_get_by_name(struct bond_net *bn, const char *ifname)
{
- struct bond_net *bn = net_generic(net, bond_net_id);
struct bonding *bond;
list_for_each_entry(bond, &bn->dev_list, bond_list) {
@@ -103,7 +102,8 @@ static ssize_t bonding_store_bonds(struct class *cls,
struct class_attribute *attr,
const char *buffer, size_t count)
{
- struct net *net = current->nsproxy->net_ns;
+ struct bond_net *bn =
+ container_of(attr, struct bond_net, class_attr_bonding_masters);
char command[IFNAMSIZ + 1] = {0, };
char *ifname;
int rv, res = count;
@@ -116,7 +116,7 @@ static ssize_t bonding_store_bonds(struct class *cls,
if (command[0] == '+') {
pr_info("%s is being created...\n", ifname);
- rv = bond_create(net, ifname);
+ rv = bond_create(bn->net, ifname);
if (rv) {
if (rv == -EEXIST)
pr_info("%s already exists.\n", ifname);
@@ -128,7 +128,7 @@ static ssize_t bonding_store_bonds(struct class *cls,
struct net_device *bond_dev;
rtnl_lock();
- bond_dev = bond_get_by_name(net, ifname);
+ bond_dev = bond_get_by_name(bn, ifname);
if (bond_dev) {
pr_info("%s is being deleted...\n", ifname);
unregister_netdevice(bond_dev);
@@ -150,9 +150,24 @@ err_no_cmd:
return -EPERM;
}
+static const void *bonding_namespace(struct class *cls,
+ const struct class_attribute *attr)
+{
+ const struct bond_net *bn =
+ container_of(attr, struct bond_net, class_attr_bonding_masters);
+ return bn->net;
+}
+
/* class attribute for bond_masters file. This ends up in /sys/class/net */
-static CLASS_ATTR(bonding_masters, S_IWUSR | S_IRUGO,
- bonding_show_bonds, bonding_store_bonds);
+static const struct class_attribute class_attr_bonding_masters = {
+ .attr = {
+ .name = "bonding_masters",
+ .mode = S_IWUSR | S_IRUGO,
+ },
+ .show = bonding_show_bonds,
+ .store = bonding_store_bonds,
+ .namespace = bonding_namespace,
+};
int bond_create_slave_symlinks(struct net_device *master,
struct net_device *slave)
@@ -304,6 +319,13 @@ static ssize_t bonding_store_mode(struct device *d,
goto out;
}
+ if (bond->slave_cnt > 0) {
+ pr_err("unable to update mode of %s because it has slaves.\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
new_value = bond_parse_parm(buf, bond_mode_tbl);
if (new_value < 0) {
pr_err("%s: Ignoring invalid mode value %.*s.\n",
@@ -804,6 +826,7 @@ static ssize_t bonding_store_lacp(struct device *d,
if ((new_value == 1) || (new_value == 0)) {
bond->params.lacp_fast = new_value;
+ bond_3ad_update_lacp_rate(bond);
pr_info("%s: Setting LACP rate to %s (%d).\n",
bond->dev->name, bond_lacp_tbl[new_value].modename,
new_value);
@@ -818,6 +841,38 @@ out:
static DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR,
bonding_show_lacp, bonding_store_lacp);
+static ssize_t bonding_show_min_links(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bonding *bond = to_bond(d);
+
+ return sprintf(buf, "%d\n", bond->params.min_links);
+}
+
+static ssize_t bonding_store_min_links(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bonding *bond = to_bond(d);
+ int ret;
+ unsigned int new_value;
+
+ ret = kstrtouint(buf, 0, &new_value);
+ if (ret < 0) {
+ pr_err("%s: Ignoring invalid min links value %s.\n",
+ bond->dev->name, buf);
+ return ret;
+ }
+
+ pr_info("%s: Setting min links value to %u\n",
+ bond->dev->name, new_value);
+ bond->params.min_links = new_value;
+ return count;
+}
+static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR,
+ bonding_show_min_links, bonding_store_min_links);
+
static ssize_t bonding_show_ad_select(struct device *d,
struct device_attribute *attr,
char *buf)
@@ -992,6 +1047,7 @@ static ssize_t bonding_store_primary(struct device *d,
int i;
struct slave *slave;
struct bonding *bond = to_bond(d);
+ char ifname[IFNAMSIZ];
if (!rtnl_trylock())
return restart_syscall();
@@ -1002,32 +1058,33 @@ static ssize_t bonding_store_primary(struct device *d,
if (!USES_PRIMARY(bond->params.mode)) {
pr_info("%s: Unable to set primary slave; %s is in mode %d\n",
bond->dev->name, bond->dev->name, bond->params.mode);
- } else {
- bond_for_each_slave(bond, slave, i) {
- if (strnicmp
- (slave->dev->name, buf,
- strlen(slave->dev->name)) == 0) {
- pr_info("%s: Setting %s as primary slave.\n",
- bond->dev->name, slave->dev->name);
- bond->primary_slave = slave;
- strcpy(bond->params.primary, slave->dev->name);
- bond_select_active_slave(bond);
- goto out;
- }
- }
+ goto out;
+ }
- /* if we got here, then we didn't match the name of any slave */
+ sscanf(buf, "%16s", ifname); /* IFNAMSIZ */
- if (strlen(buf) == 0 || buf[0] == '\n') {
- pr_info("%s: Setting primary slave to None.\n",
- bond->dev->name);
- bond->primary_slave = NULL;
- bond_select_active_slave(bond);
- } else {
- pr_info("%s: Unable to set %.*s as primary slave as it is not a slave.\n",
- bond->dev->name, (int)strlen(buf) - 1, buf);
+ /* check to see if we are clearing primary */
+ if (!strlen(ifname) || buf[0] == '\n') {
+ pr_info("%s: Setting primary slave to None.\n",
+ bond->dev->name);
+ bond->primary_slave = NULL;
+ bond_select_active_slave(bond);
+ goto out;
+ }
+
+ bond_for_each_slave(bond, slave, i) {
+ if (strncmp(slave->dev->name, ifname, IFNAMSIZ) == 0) {
+ pr_info("%s: Setting %s as primary slave.\n",
+ bond->dev->name, slave->dev->name);
+ bond->primary_slave = slave;
+ strcpy(bond->params.primary, slave->dev->name);
+ bond_select_active_slave(bond);
+ goto out;
}
}
+
+ pr_info("%s: Unable to set %.*s as primary slave.\n",
+ bond->dev->name, (int)strlen(buf) - 1, buf);
out:
write_unlock_bh(&bond->curr_slave_lock);
read_unlock(&bond->lock);
@@ -1162,6 +1219,7 @@ static ssize_t bonding_store_active_slave(struct device *d,
struct slave *old_active = NULL;
struct slave *new_active = NULL;
struct bonding *bond = to_bond(d);
+ char ifname[IFNAMSIZ];
if (!rtnl_trylock())
return restart_syscall();
@@ -1170,56 +1228,62 @@ static ssize_t bonding_store_active_slave(struct device *d,
read_lock(&bond->lock);
write_lock_bh(&bond->curr_slave_lock);
- if (!USES_PRIMARY(bond->params.mode))
+ if (!USES_PRIMARY(bond->params.mode)) {
pr_info("%s: Unable to change active slave; %s is in mode %d\n",
bond->dev->name, bond->dev->name, bond->params.mode);
- else {
- bond_for_each_slave(bond, slave, i) {
- if (strnicmp
- (slave->dev->name, buf,
- strlen(slave->dev->name)) == 0) {
- old_active = bond->curr_active_slave;
- new_active = slave;
- if (new_active == old_active) {
- /* do nothing */
- pr_info("%s: %s is already the current active slave.\n",
+ goto out;
+ }
+
+ sscanf(buf, "%16s", ifname); /* IFNAMSIZ */
+
+ /* check to see if we are clearing active */
+ if (!strlen(ifname) || buf[0] == '\n') {
+ pr_info("%s: Clearing current active slave.\n",
+ bond->dev->name);
+ bond->curr_active_slave = NULL;
+ bond_select_active_slave(bond);
+ goto out;
+ }
+
+ bond_for_each_slave(bond, slave, i) {
+ if (strncmp(slave->dev->name, ifname, IFNAMSIZ) == 0) {
+ old_active = bond->curr_active_slave;
+ new_active = slave;
+ if (new_active == old_active) {
+ /* do nothing */
+ pr_info("%s: %s is already the current"
+ " active slave.\n",
+ bond->dev->name,
+ slave->dev->name);
+ goto out;
+ }
+ else {
+ if ((new_active) &&
+ (old_active) &&
+ (new_active->link == BOND_LINK_UP) &&
+ IS_UP(new_active->dev)) {
+ pr_info("%s: Setting %s as active"
+ " slave.\n",
bond->dev->name,
slave->dev->name);
- goto out;
+ bond_change_active_slave(bond,
+ new_active);
}
else {
- if ((new_active) &&
- (old_active) &&
- (new_active->link == BOND_LINK_UP) &&
- IS_UP(new_active->dev)) {
- pr_info("%s: Setting %s as active slave.\n",
- bond->dev->name,
- slave->dev->name);
- bond_change_active_slave(bond, new_active);
- }
- else {
- pr_info("%s: Could not set %s as active slave; either %s is down or the link is down.\n",
- bond->dev->name,
- slave->dev->name,
- slave->dev->name);
- }
- goto out;
+ pr_info("%s: Could not set %s as"
+ " active slave; either %s is"
+ " down or the link is down.\n",
+ bond->dev->name,
+ slave->dev->name,
+ slave->dev->name);
}
+ goto out;
}
}
-
- /* if we got here, then we didn't match the name of any slave */
-
- if (strlen(buf) == 0 || buf[0] == '\n') {
- pr_info("%s: Setting active slave to None.\n",
- bond->dev->name);
- bond->primary_slave = NULL;
- bond_select_active_slave(bond);
- } else {
- pr_info("%s: Unable to set %.*s as active slave as it is not a slave.\n",
- bond->dev->name, (int)strlen(buf) - 1, buf);
- }
}
+
+ pr_info("%s: Unable to set %.*s as active slave.\n",
+ bond->dev->name, (int)strlen(buf) - 1, buf);
out:
write_unlock_bh(&bond->curr_slave_lock);
read_unlock(&bond->lock);
@@ -1600,6 +1664,7 @@ static struct attribute *per_bond_attrs[] = {
&dev_attr_queue_id.attr,
&dev_attr_all_slaves_active.attr,
&dev_attr_resend_igmp.attr,
+ &dev_attr_min_links.attr,
NULL,
};
@@ -1612,11 +1677,14 @@ static struct attribute_group bonding_group = {
* Initialize sysfs. This sets up the bonding_masters file in
* /sys/class/net.
*/
-int bond_create_sysfs(void)
+int bond_create_sysfs(struct bond_net *bn)
{
int ret;
- ret = netdev_class_create_file(&class_attr_bonding_masters);
+ bn->class_attr_bonding_masters = class_attr_bonding_masters;
+ sysfs_attr_init(&bn->class_attr_bonding_masters.attr);
+
+ ret = netdev_class_create_file(&bn->class_attr_bonding_masters);
/*
* Permit multiple loads of the module by ignoring failures to
* create the bonding_masters sysfs file. Bonding devices
@@ -1630,7 +1698,7 @@ int bond_create_sysfs(void)
*/
if (ret == -EEXIST) {
/* Is someone being kinky and naming a device bonding_master? */
- if (__dev_get_by_name(&init_net,
+ if (__dev_get_by_name(bn->net,
class_attr_bonding_masters.attr.name))
pr_err("network device named %s already exists in sysfs",
class_attr_bonding_masters.attr.name);
@@ -1644,9 +1712,9 @@ int bond_create_sysfs(void)
/*
* Remove /sys/class/net/bonding_masters.
*/
-void bond_destroy_sysfs(void)
+void bond_destroy_sysfs(struct bond_net *bn)
{
- netdev_class_remove_file(&class_attr_bonding_masters);
+ netdev_class_remove_file(&bn->class_attr_bonding_masters);
}
/*
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index ea1d005be92d..1aecc37e5b4d 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -147,6 +147,7 @@ struct bond_params {
int updelay;
int downdelay;
int lacp_fast;
+ unsigned int min_links;
int ad_select;
char primary[IFNAMSIZ];
int primary_reselect;
@@ -221,7 +222,6 @@ struct bonding {
struct slave *);
rwlock_t lock;
rwlock_t curr_slave_lock;
- s8 kill_timers;
u8 send_peer_notif;
s8 setup_by_slave;
s8 igmp_retrans;
@@ -233,14 +233,11 @@ struct bonding {
struct netdev_hw_addr_list mc_list;
int (*xmit_hash_policy)(struct sk_buff *, int);
__be32 master_ip;
- u16 flags;
u16 rr_tx_counter;
struct ad_bond_info ad_info;
struct alb_bond_info alb_info;
struct bond_params params;
struct list_head vlan_list;
- struct vlan_group *vlgrp;
- struct packet_type arp_mon_pt;
struct workqueue_struct *wq;
struct delayed_work mii_work;
struct delayed_work arp_work;
@@ -253,6 +250,11 @@ struct bonding {
#endif /* CONFIG_DEBUG_FS */
};
+static inline bool bond_vlan_used(struct bonding *bond)
+{
+ return !list_empty(&bond->vlan_list);
+}
+
#define bond_slave_get_rcu(dev) \
((struct slave *) rcu_dereference(dev->rx_handler_data))
@@ -376,11 +378,13 @@ static inline bool bond_is_slave_inactive(struct slave *slave)
return slave->inactive;
}
+struct bond_net;
+
struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr);
int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev);
int bond_create(struct net *net, const char *name);
-int bond_create_sysfs(void);
-void bond_destroy_sysfs(void);
+int bond_create_sysfs(struct bond_net *net);
+void bond_destroy_sysfs(struct bond_net *net);
void bond_prepare_sysfs_group(struct bonding *bond);
int bond_create_slave_symlinks(struct net_device *master, struct net_device *slave);
void bond_destroy_slave_symlinks(struct net_device *master, struct net_device *slave);
@@ -406,6 +410,7 @@ struct bond_net {
#ifdef CONFIG_PROC_FS
struct proc_dir_entry * proc_dir;
#endif
+ struct class_attribute class_attr_bonding_masters;
};
#ifdef CONFIG_PROC_FS
diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig
index 09ed3f42d673..abf4d7a9dcce 100644
--- a/drivers/net/caif/Kconfig
+++ b/drivers/net/caif/Kconfig
@@ -38,3 +38,12 @@ config CAIF_SHM
default n
---help---
The CAIF shared memory protocol driver for the STE UX5500 platform.
+
+config CAIF_HSI
+ tristate "CAIF HSI transport driver"
+ depends on CAIF
+ default n
+ ---help---
+ The caif low level driver for CAIF over HSI.
+ Be aware that if you enable this then you also need to
+ enable a low-level HSI driver.
diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile
index 9560b9d624bd..91dff861560f 100644
--- a/drivers/net/caif/Makefile
+++ b/drivers/net/caif/Makefile
@@ -10,3 +10,6 @@ obj-$(CONFIG_CAIF_SPI_SLAVE) += cfspi_slave.o
# Shared memory
caif_shm-objs := caif_shmcore.o caif_shm_u5500.o
obj-$(CONFIG_CAIF_SHM) += caif_shm.o
+
+# HSI interface
+obj-$(CONFIG_CAIF_HSI) += caif_hsi.o
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
new file mode 100644
index 000000000000..073352517adc
--- /dev/null
+++ b/drivers/net/caif/caif_hsi.c
@@ -0,0 +1,1285 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * Author: Daniel Martensson / daniel.martensson@stericsson.com
+ * Dmitry.Tarnyagin / dmitry.tarnyagin@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/if_arp.h>
+#include <linux/timer.h>
+#include <linux/rtnetlink.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/caif_hsi.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Daniel Martensson<daniel.martensson@stericsson.com>");
+MODULE_DESCRIPTION("CAIF HSI driver");
+
+/* Returns the number of padding bytes for alignment. */
+#define PAD_POW2(x, pow) ((((x)&((pow)-1)) == 0) ? 0 :\
+ (((pow)-((x)&((pow)-1)))))
+
+static int inactivity_timeout = 1000;
+module_param(inactivity_timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(inactivity_timeout, "Inactivity timeout on HSI, ms.");
+
+/*
+ * HSI padding options.
+ * Warning: must be a base of 2 (& operation used) and can not be zero !
+ */
+static int hsi_head_align = 4;
+module_param(hsi_head_align, int, S_IRUGO);
+MODULE_PARM_DESC(hsi_head_align, "HSI head alignment.");
+
+static int hsi_tail_align = 4;
+module_param(hsi_tail_align, int, S_IRUGO);
+MODULE_PARM_DESC(hsi_tail_align, "HSI tail alignment.");
+
+/*
+ * HSI link layer flowcontrol thresholds.
+ * Warning: A high threshold value migth increase throughput but it will at
+ * the same time prevent channel prioritization and increase the risk of
+ * flooding the modem. The high threshold should be above the low.
+ */
+static int hsi_high_threshold = 100;
+module_param(hsi_high_threshold, int, S_IRUGO);
+MODULE_PARM_DESC(hsi_high_threshold, "HSI high threshold (FLOW OFF).");
+
+static int hsi_low_threshold = 50;
+module_param(hsi_low_threshold, int, S_IRUGO);
+MODULE_PARM_DESC(hsi_low_threshold, "HSI high threshold (FLOW ON).");
+
+#define ON 1
+#define OFF 0
+
+/*
+ * Threshold values for the HSI packet queue. Flowcontrol will be asserted
+ * when the number of packets exceeds HIGH_WATER_MARK. It will not be
+ * de-asserted before the number of packets drops below LOW_WATER_MARK.
+ */
+#define LOW_WATER_MARK hsi_low_threshold
+#define HIGH_WATER_MARK hsi_high_threshold
+
+static LIST_HEAD(cfhsi_list);
+static spinlock_t cfhsi_list_lock;
+
+static void cfhsi_inactivity_tout(unsigned long arg)
+{
+ struct cfhsi *cfhsi = (struct cfhsi *)arg;
+
+ dev_dbg(&cfhsi->ndev->dev, "%s.\n",
+ __func__);
+
+ /* Schedule power down work queue. */
+ if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
+ queue_work(cfhsi->wq, &cfhsi->wake_down_work);
+}
+
+static void cfhsi_abort_tx(struct cfhsi *cfhsi)
+{
+ struct sk_buff *skb;
+
+ for (;;) {
+ spin_lock_bh(&cfhsi->lock);
+ skb = skb_dequeue(&cfhsi->qhead);
+ if (!skb)
+ break;
+
+ cfhsi->ndev->stats.tx_errors++;
+ cfhsi->ndev->stats.tx_dropped++;
+ spin_unlock_bh(&cfhsi->lock);
+ kfree_skb(skb);
+ }
+ cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
+ if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
+ mod_timer(&cfhsi->timer,
+ jiffies + cfhsi->inactivity_timeout);
+ spin_unlock_bh(&cfhsi->lock);
+}
+
+static int cfhsi_flush_fifo(struct cfhsi *cfhsi)
+{
+ char buffer[32]; /* Any reasonable value */
+ size_t fifo_occupancy;
+ int ret;
+
+ dev_dbg(&cfhsi->ndev->dev, "%s.\n",
+ __func__);
+
+
+ ret = cfhsi->dev->cfhsi_wake_up(cfhsi->dev);
+ if (ret) {
+ dev_warn(&cfhsi->ndev->dev,
+ "%s: can't wake up HSI interface: %d.\n",
+ __func__, ret);
+ return ret;
+ }
+
+ do {
+ ret = cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
+ &fifo_occupancy);
+ if (ret) {
+ dev_warn(&cfhsi->ndev->dev,
+ "%s: can't get FIFO occupancy: %d.\n",
+ __func__, ret);
+ break;
+ } else if (!fifo_occupancy)
+ /* No more data, exitting normally */
+ break;
+
+ fifo_occupancy = min(sizeof(buffer), fifo_occupancy);
+ set_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits);
+ ret = cfhsi->dev->cfhsi_rx(buffer, fifo_occupancy,
+ cfhsi->dev);
+ if (ret) {
+ clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits);
+ dev_warn(&cfhsi->ndev->dev,
+ "%s: can't read data: %d.\n",
+ __func__, ret);
+ break;
+ }
+
+ ret = 5 * HZ;
+ ret = wait_event_interruptible_timeout(cfhsi->flush_fifo_wait,
+ !test_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits), ret);
+
+ if (ret < 0) {
+ dev_warn(&cfhsi->ndev->dev,
+ "%s: can't wait for flush complete: %d.\n",
+ __func__, ret);
+ break;
+ } else if (!ret) {
+ ret = -ETIMEDOUT;
+ dev_warn(&cfhsi->ndev->dev,
+ "%s: timeout waiting for flush complete.\n",
+ __func__);
+ break;
+ }
+ } while (1);
+
+ cfhsi->dev->cfhsi_wake_down(cfhsi->dev);
+
+ return ret;
+}
+
+static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
+{
+ int nfrms = 0;
+ int pld_len = 0;
+ struct sk_buff *skb;
+ u8 *pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ;
+
+ skb = skb_dequeue(&cfhsi->qhead);
+ if (!skb)
+ return 0;
+
+ /* Clear offset. */
+ desc->offset = 0;
+
+ /* Check if we can embed a CAIF frame. */
+ if (skb->len < CFHSI_MAX_EMB_FRM_SZ) {
+ struct caif_payload_info *info;
+ int hpad = 0;
+ int tpad = 0;
+
+ /* Calculate needed head alignment and tail alignment. */
+ info = (struct caif_payload_info *)&skb->cb;
+
+ hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align);
+ tpad = PAD_POW2((skb->len + hpad), hsi_tail_align);
+
+ /* Check if frame still fits with added alignment. */
+ if ((skb->len + hpad + tpad) <= CFHSI_MAX_EMB_FRM_SZ) {
+ u8 *pemb = desc->emb_frm;
+ desc->offset = CFHSI_DESC_SHORT_SZ;
+ *pemb = (u8)(hpad - 1);
+ pemb += hpad;
+
+ /* Update network statistics. */
+ cfhsi->ndev->stats.tx_packets++;
+ cfhsi->ndev->stats.tx_bytes += skb->len;
+
+ /* Copy in embedded CAIF frame. */
+ skb_copy_bits(skb, 0, pemb, skb->len);
+ consume_skb(skb);
+ skb = NULL;
+ }
+ }
+
+ /* Create payload CAIF frames. */
+ pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ;
+ while (nfrms < CFHSI_MAX_PKTS) {
+ struct caif_payload_info *info;
+ int hpad = 0;
+ int tpad = 0;
+
+ if (!skb)
+ skb = skb_dequeue(&cfhsi->qhead);
+
+ if (!skb)
+ break;
+
+ /* Calculate needed head alignment and tail alignment. */
+ info = (struct caif_payload_info *)&skb->cb;
+
+ hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align);
+ tpad = PAD_POW2((skb->len + hpad), hsi_tail_align);
+
+ /* Fill in CAIF frame length in descriptor. */
+ desc->cffrm_len[nfrms] = hpad + skb->len + tpad;
+
+ /* Fill head padding information. */
+ *pfrm = (u8)(hpad - 1);
+ pfrm += hpad;
+
+ /* Update network statistics. */
+ cfhsi->ndev->stats.tx_packets++;
+ cfhsi->ndev->stats.tx_bytes += skb->len;
+
+ /* Copy in CAIF frame. */
+ skb_copy_bits(skb, 0, pfrm, skb->len);
+
+ /* Update payload length. */
+ pld_len += desc->cffrm_len[nfrms];
+
+ /* Update frame pointer. */
+ pfrm += skb->len + tpad;
+ consume_skb(skb);
+ skb = NULL;
+
+ /* Update number of frames. */
+ nfrms++;
+ }
+
+ /* Unused length fields should be zero-filled (according to SPEC). */
+ while (nfrms < CFHSI_MAX_PKTS) {
+ desc->cffrm_len[nfrms] = 0x0000;
+ nfrms++;
+ }
+
+ /* Check if we can piggy-back another descriptor. */
+ skb = skb_peek(&cfhsi->qhead);
+ if (skb)
+ desc->header |= CFHSI_PIGGY_DESC;
+ else
+ desc->header &= ~CFHSI_PIGGY_DESC;
+
+ return CFHSI_DESC_SZ + pld_len;
+}
+
+static void cfhsi_tx_done(struct cfhsi *cfhsi)
+{
+ struct cfhsi_desc *desc = NULL;
+ int len = 0;
+ int res;
+
+ dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__);
+
+ if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
+ return;
+
+ desc = (struct cfhsi_desc *)cfhsi->tx_buf;
+
+ do {
+ /*
+ * Send flow on if flow off has been previously signalled
+ * and number of packets is below low water mark.
+ */
+ spin_lock_bh(&cfhsi->lock);
+ if (cfhsi->flow_off_sent &&
+ cfhsi->qhead.qlen <= cfhsi->q_low_mark &&
+ cfhsi->cfdev.flowctrl) {
+
+ cfhsi->flow_off_sent = 0;
+ cfhsi->cfdev.flowctrl(cfhsi->ndev, ON);
+ }
+ spin_unlock_bh(&cfhsi->lock);
+
+ /* Create HSI frame. */
+ do {
+ len = cfhsi_tx_frm(desc, cfhsi);
+ if (!len) {
+ spin_lock_bh(&cfhsi->lock);
+ if (unlikely(skb_peek(&cfhsi->qhead))) {
+ spin_unlock_bh(&cfhsi->lock);
+ continue;
+ }
+ cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
+ /* Start inactivity timer. */
+ mod_timer(&cfhsi->timer,
+ jiffies + cfhsi->inactivity_timeout);
+ spin_unlock_bh(&cfhsi->lock);
+ goto done;
+ }
+ } while (!len);
+
+ /* Set up new transfer. */
+ res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev);
+ if (WARN_ON(res < 0)) {
+ dev_err(&cfhsi->ndev->dev, "%s: TX error %d.\n",
+ __func__, res);
+ }
+ } while (res < 0);
+
+done:
+ return;
+}
+
+static void cfhsi_tx_done_cb(struct cfhsi_drv *drv)
+{
+ struct cfhsi *cfhsi;
+
+ cfhsi = container_of(drv, struct cfhsi, drv);
+ dev_dbg(&cfhsi->ndev->dev, "%s.\n",
+ __func__);
+
+ if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
+ return;
+ cfhsi_tx_done(cfhsi);
+}
+
+static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
+{
+ int xfer_sz = 0;
+ int nfrms = 0;
+ u16 *plen = NULL;
+ u8 *pfrm = NULL;
+
+ if ((desc->header & ~CFHSI_PIGGY_DESC) ||
+ (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) {
+ dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
+ __func__);
+ return -EPROTO;
+ }
+
+ /* Check for embedded CAIF frame. */
+ if (desc->offset) {
+ struct sk_buff *skb;
+ u8 *dst = NULL;
+ int len = 0;
+ pfrm = ((u8 *)desc) + desc->offset;
+
+ /* Remove offset padding. */
+ pfrm += *pfrm + 1;
+
+ /* Read length of CAIF frame (little endian). */
+ len = *pfrm;
+ len |= ((*(pfrm+1)) << 8) & 0xFF00;
+ len += 2; /* Add FCS fields. */
+
+ /* Sanity check length of CAIF frame. */
+ if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) {
+ dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n",
+ __func__);
+ return -EPROTO;
+ }
+
+ /* Allocate SKB (OK even in IRQ context). */
+ skb = alloc_skb(len + 1, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n",
+ __func__);
+ return -ENOMEM;
+ }
+ caif_assert(skb != NULL);
+
+ dst = skb_put(skb, len);
+ memcpy(dst, pfrm, len);
+
+ skb->protocol = htons(ETH_P_CAIF);
+ skb_reset_mac_header(skb);
+ skb->dev = cfhsi->ndev;
+
+ /*
+ * We are called from a arch specific platform device.
+ * Unfortunately we don't know what context we're
+ * running in.
+ */
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ netif_rx_ni(skb);
+
+ /* Update network statistics. */
+ cfhsi->ndev->stats.rx_packets++;
+ cfhsi->ndev->stats.rx_bytes += len;
+ }
+
+ /* Calculate transfer length. */
+ plen = desc->cffrm_len;
+ while (nfrms < CFHSI_MAX_PKTS && *plen) {
+ xfer_sz += *plen;
+ plen++;
+ nfrms++;
+ }
+
+ /* Check for piggy-backed descriptor. */
+ if (desc->header & CFHSI_PIGGY_DESC)
+ xfer_sz += CFHSI_DESC_SZ;
+
+ if ((xfer_sz % 4) || (xfer_sz > (CFHSI_BUF_SZ_RX - CFHSI_DESC_SZ))) {
+ dev_err(&cfhsi->ndev->dev,
+ "%s: Invalid payload len: %d, ignored.\n",
+ __func__, xfer_sz);
+ return -EPROTO;
+ }
+ return xfer_sz;
+}
+
+static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
+{
+ int rx_sz = 0;
+ int nfrms = 0;
+ u16 *plen = NULL;
+ u8 *pfrm = NULL;
+
+ /* Sanity check header and offset. */
+ if (WARN_ON((desc->header & ~CFHSI_PIGGY_DESC) ||
+ (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) {
+ dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
+ __func__);
+ return -EPROTO;
+ }
+
+ /* Set frame pointer to start of payload. */
+ pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ;
+ plen = desc->cffrm_len;
+
+ /* Skip already processed frames. */
+ while (nfrms < cfhsi->rx_state.nfrms) {
+ pfrm += *plen;
+ rx_sz += *plen;
+ plen++;
+ nfrms++;
+ }
+
+ /* Parse payload. */
+ while (nfrms < CFHSI_MAX_PKTS && *plen) {
+ struct sk_buff *skb;
+ u8 *dst = NULL;
+ u8 *pcffrm = NULL;
+ int len = 0;
+
+ /* CAIF frame starts after head padding. */
+ pcffrm = pfrm + *pfrm + 1;
+
+ /* Read length of CAIF frame (little endian). */
+ len = *pcffrm;
+ len |= ((*(pcffrm + 1)) << 8) & 0xFF00;
+ len += 2; /* Add FCS fields. */
+
+ /* Sanity check length of CAIF frames. */
+ if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) {
+ dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n",
+ __func__);
+ return -EPROTO;
+ }
+
+ /* Allocate SKB (OK even in IRQ context). */
+ skb = alloc_skb(len + 1, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n",
+ __func__);
+ cfhsi->rx_state.nfrms = nfrms;
+ return -ENOMEM;
+ }
+ caif_assert(skb != NULL);
+
+ dst = skb_put(skb, len);
+ memcpy(dst, pcffrm, len);
+
+ skb->protocol = htons(ETH_P_CAIF);
+ skb_reset_mac_header(skb);
+ skb->dev = cfhsi->ndev;
+
+ /*
+ * We're called from a platform device,
+ * and don't know the context we're running in.
+ */
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ netif_rx_ni(skb);
+
+ /* Update network statistics. */
+ cfhsi->ndev->stats.rx_packets++;
+ cfhsi->ndev->stats.rx_bytes += len;
+
+ pfrm += *plen;
+ rx_sz += *plen;
+ plen++;
+ nfrms++;
+ }
+
+ return rx_sz;
+}
+
+static void cfhsi_rx_done(struct cfhsi *cfhsi)
+{
+ int res;
+ int desc_pld_len = 0;
+ struct cfhsi_desc *desc = NULL;
+
+ desc = (struct cfhsi_desc *)cfhsi->rx_buf;
+
+ dev_dbg(&cfhsi->ndev->dev, "%s\n", __func__);
+
+ if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
+ return;
+
+ /* Update inactivity timer if pending. */
+ spin_lock_bh(&cfhsi->lock);
+ mod_timer_pending(&cfhsi->timer,
+ jiffies + cfhsi->inactivity_timeout);
+ spin_unlock_bh(&cfhsi->lock);
+
+ if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) {
+ desc_pld_len = cfhsi_rx_desc(desc, cfhsi);
+ if (desc_pld_len == -ENOMEM)
+ goto restart;
+ if (desc_pld_len == -EPROTO)
+ goto out_of_sync;
+ } else {
+ int pld_len;
+
+ if (!cfhsi->rx_state.piggy_desc) {
+ pld_len = cfhsi_rx_pld(desc, cfhsi);
+ if (pld_len == -ENOMEM)
+ goto restart;
+ if (pld_len == -EPROTO)
+ goto out_of_sync;
+ cfhsi->rx_state.pld_len = pld_len;
+ } else {
+ pld_len = cfhsi->rx_state.pld_len;
+ }
+
+ if ((pld_len > 0) && (desc->header & CFHSI_PIGGY_DESC)) {
+ struct cfhsi_desc *piggy_desc;
+ piggy_desc = (struct cfhsi_desc *)
+ (desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ +
+ pld_len);
+ cfhsi->rx_state.piggy_desc = true;
+
+ /* Extract piggy-backed descriptor. */
+ desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi);
+ if (desc_pld_len == -ENOMEM)
+ goto restart;
+
+ /*
+ * Copy needed information from the piggy-backed
+ * descriptor to the descriptor in the start.
+ */
+ memcpy((u8 *)desc, (u8 *)piggy_desc,
+ CFHSI_DESC_SHORT_SZ);
+
+ if (desc_pld_len == -EPROTO)
+ goto out_of_sync;
+ }
+ }
+
+ memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state));
+ if (desc_pld_len) {
+ cfhsi->rx_state.state = CFHSI_RX_STATE_PAYLOAD;
+ cfhsi->rx_ptr = cfhsi->rx_buf + CFHSI_DESC_SZ;
+ cfhsi->rx_len = desc_pld_len;
+ } else {
+ cfhsi->rx_state.state = CFHSI_RX_STATE_DESC;
+ cfhsi->rx_ptr = cfhsi->rx_buf;
+ cfhsi->rx_len = CFHSI_DESC_SZ;
+ }
+
+ if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) {
+ /* Set up new transfer. */
+ dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n",
+ __func__);
+ res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len,
+ cfhsi->dev);
+ if (WARN_ON(res < 0)) {
+ dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n",
+ __func__, res);
+ cfhsi->ndev->stats.rx_errors++;
+ cfhsi->ndev->stats.rx_dropped++;
+ }
+ }
+ return;
+
+restart:
+ if (++cfhsi->rx_state.retries > CFHSI_MAX_RX_RETRIES) {
+ dev_err(&cfhsi->ndev->dev, "%s: No memory available "
+ "in %d iterations.\n",
+ __func__, CFHSI_MAX_RX_RETRIES);
+ BUG();
+ }
+ mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1);
+ return;
+
+out_of_sync:
+ dev_err(&cfhsi->ndev->dev, "%s: Out of sync.\n", __func__);
+ print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE,
+ cfhsi->rx_buf, CFHSI_DESC_SZ);
+ schedule_work(&cfhsi->out_of_sync_work);
+}
+
+static void cfhsi_rx_slowpath(unsigned long arg)
+{
+ struct cfhsi *cfhsi = (struct cfhsi *)arg;
+
+ dev_dbg(&cfhsi->ndev->dev, "%s.\n",
+ __func__);
+
+ cfhsi_rx_done(cfhsi);
+}
+
+static void cfhsi_rx_done_cb(struct cfhsi_drv *drv)
+{
+ struct cfhsi *cfhsi;
+
+ cfhsi = container_of(drv, struct cfhsi, drv);
+ dev_dbg(&cfhsi->ndev->dev, "%s.\n",
+ __func__);
+
+ if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
+ return;
+
+ if (test_and_clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits))
+ wake_up_interruptible(&cfhsi->flush_fifo_wait);
+ else
+ cfhsi_rx_done(cfhsi);
+}
+
+static void cfhsi_wake_up(struct work_struct *work)
+{
+ struct cfhsi *cfhsi = NULL;
+ int res;
+ int len;
+ long ret;
+
+ cfhsi = container_of(work, struct cfhsi, wake_up_work);
+
+ if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
+ return;
+
+ if (unlikely(test_bit(CFHSI_AWAKE, &cfhsi->bits))) {
+ /* It happenes when wakeup is requested by
+ * both ends at the same time. */
+ clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
+ clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
+ return;
+ }
+
+ /* Activate wake line. */
+ cfhsi->dev->cfhsi_wake_up(cfhsi->dev);
+
+ dev_dbg(&cfhsi->ndev->dev, "%s: Start waiting.\n",
+ __func__);
+
+ /* Wait for acknowledge. */
+ ret = CFHSI_WAKE_TOUT;
+ ret = wait_event_interruptible_timeout(cfhsi->wake_up_wait,
+ test_and_clear_bit(CFHSI_WAKE_UP_ACK,
+ &cfhsi->bits), ret);
+ if (unlikely(ret < 0)) {
+ /* Interrupted by signal. */
+ dev_err(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n",
+ __func__, ret);
+
+ clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
+ cfhsi->dev->cfhsi_wake_down(cfhsi->dev);
+ return;
+ } else if (!ret) {
+ bool ca_wake = false;
+ size_t fifo_occupancy = 0;
+
+ /* Wakeup timeout */
+ dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n",
+ __func__);
+
+ /* Check FIFO to check if modem has sent something. */
+ WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
+ &fifo_occupancy));
+
+ dev_err(&cfhsi->ndev->dev, "%s: Bytes in FIFO: %u.\n",
+ __func__, (unsigned) fifo_occupancy);
+
+ /* Check if we misssed the interrupt. */
+ WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev,
+ &ca_wake));
+
+ if (ca_wake) {
+ dev_err(&cfhsi->ndev->dev, "%s: CA Wake missed !.\n",
+ __func__);
+
+ /* Clear the CFHSI_WAKE_UP_ACK bit to prevent race. */
+ clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
+
+ /* Continue execution. */
+ goto wake_ack;
+ }
+
+ clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
+ cfhsi->dev->cfhsi_wake_down(cfhsi->dev);
+ return;
+ }
+wake_ack:
+ dev_dbg(&cfhsi->ndev->dev, "%s: Woken.\n",
+ __func__);
+
+ /* Clear power up bit. */
+ set_bit(CFHSI_AWAKE, &cfhsi->bits);
+ clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
+
+ /* Resume read operation. */
+ dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n", __func__);
+ res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, cfhsi->dev);
+
+ if (WARN_ON(res < 0))
+ dev_err(&cfhsi->ndev->dev, "%s: RX err %d.\n", __func__, res);
+
+ /* Clear power up acknowledment. */
+ clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
+
+ spin_lock_bh(&cfhsi->lock);
+
+ /* Resume transmit if queue is not empty. */
+ if (!skb_peek(&cfhsi->qhead)) {
+ dev_dbg(&cfhsi->ndev->dev, "%s: Peer wake, start timer.\n",
+ __func__);
+ /* Start inactivity timer. */
+ mod_timer(&cfhsi->timer,
+ jiffies + cfhsi->inactivity_timeout);
+ spin_unlock_bh(&cfhsi->lock);
+ return;
+ }
+
+ dev_dbg(&cfhsi->ndev->dev, "%s: Host wake.\n",
+ __func__);
+
+ spin_unlock_bh(&cfhsi->lock);
+
+ /* Create HSI frame. */
+ len = cfhsi_tx_frm((struct cfhsi_desc *)cfhsi->tx_buf, cfhsi);
+
+ if (likely(len > 0)) {
+ /* Set up new transfer. */
+ res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev);
+ if (WARN_ON(res < 0)) {
+ dev_err(&cfhsi->ndev->dev, "%s: TX error %d.\n",
+ __func__, res);
+ cfhsi_abort_tx(cfhsi);
+ }
+ } else {
+ dev_err(&cfhsi->ndev->dev,
+ "%s: Failed to create HSI frame: %d.\n",
+ __func__, len);
+ }
+}
+
+static void cfhsi_wake_down(struct work_struct *work)
+{
+ long ret;
+ struct cfhsi *cfhsi = NULL;
+ size_t fifo_occupancy = 0;
+ int retry = CFHSI_WAKE_TOUT;
+
+ cfhsi = container_of(work, struct cfhsi, wake_down_work);
+ dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__);
+
+ if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
+ return;
+
+ /* Deactivate wake line. */
+ cfhsi->dev->cfhsi_wake_down(cfhsi->dev);
+
+ /* Wait for acknowledge. */
+ ret = CFHSI_WAKE_TOUT;
+ ret = wait_event_interruptible_timeout(cfhsi->wake_down_wait,
+ test_and_clear_bit(CFHSI_WAKE_DOWN_ACK,
+ &cfhsi->bits), ret);
+ if (ret < 0) {
+ /* Interrupted by signal. */
+ dev_err(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n",
+ __func__, ret);
+ return;
+ } else if (!ret) {
+ bool ca_wake = true;
+
+ /* Timeout */
+ dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", __func__);
+
+ /* Check if we misssed the interrupt. */
+ WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev,
+ &ca_wake));
+ if (!ca_wake)
+ dev_err(&cfhsi->ndev->dev, "%s: CA Wake missed !.\n",
+ __func__);
+ }
+
+ /* Check FIFO occupancy. */
+ while (retry) {
+ WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
+ &fifo_occupancy));
+
+ if (!fifo_occupancy)
+ break;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ retry--;
+ }
+
+ if (!retry)
+ dev_err(&cfhsi->ndev->dev, "%s: FIFO Timeout.\n", __func__);
+
+ /* Clear AWAKE condition. */
+ clear_bit(CFHSI_AWAKE, &cfhsi->bits);
+
+ /* Cancel pending RX requests. */
+ cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev);
+
+}
+
+static void cfhsi_out_of_sync(struct work_struct *work)
+{
+ struct cfhsi *cfhsi = NULL;
+
+ cfhsi = container_of(work, struct cfhsi, out_of_sync_work);
+
+ rtnl_lock();
+ dev_close(cfhsi->ndev);
+ rtnl_unlock();
+}
+
+static void cfhsi_wake_up_cb(struct cfhsi_drv *drv)
+{
+ struct cfhsi *cfhsi = NULL;
+
+ cfhsi = container_of(drv, struct cfhsi, drv);
+ dev_dbg(&cfhsi->ndev->dev, "%s.\n",
+ __func__);
+
+ set_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
+ wake_up_interruptible(&cfhsi->wake_up_wait);
+
+ if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
+ return;
+
+ /* Schedule wake up work queue if the peer initiates. */
+ if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits))
+ queue_work(cfhsi->wq, &cfhsi->wake_up_work);
+}
+
+static void cfhsi_wake_down_cb(struct cfhsi_drv *drv)
+{
+ struct cfhsi *cfhsi = NULL;
+
+ cfhsi = container_of(drv, struct cfhsi, drv);
+ dev_dbg(&cfhsi->ndev->dev, "%s.\n",
+ __func__);
+
+ /* Initiating low power is only permitted by the host (us). */
+ set_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits);
+ wake_up_interruptible(&cfhsi->wake_down_wait);
+}
+
+static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct cfhsi *cfhsi = NULL;
+ int start_xfer = 0;
+ int timer_active;
+
+ if (!dev)
+ return -EINVAL;
+
+ cfhsi = netdev_priv(dev);
+
+ spin_lock_bh(&cfhsi->lock);
+
+ skb_queue_tail(&cfhsi->qhead, skb);
+
+ /* Sanity check; xmit should not be called after unregister_netdev */
+ if (WARN_ON(test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))) {
+ spin_unlock_bh(&cfhsi->lock);
+ cfhsi_abort_tx(cfhsi);
+ return -EINVAL;
+ }
+
+ /* Send flow off if number of packets is above high water mark. */
+ if (!cfhsi->flow_off_sent &&
+ cfhsi->qhead.qlen > cfhsi->q_high_mark &&
+ cfhsi->cfdev.flowctrl) {
+ cfhsi->flow_off_sent = 1;
+ cfhsi->cfdev.flowctrl(cfhsi->ndev, OFF);
+ }
+
+ if (cfhsi->tx_state == CFHSI_TX_STATE_IDLE) {
+ cfhsi->tx_state = CFHSI_TX_STATE_XFER;
+ start_xfer = 1;
+ }
+
+ if (!start_xfer) {
+ spin_unlock_bh(&cfhsi->lock);
+ return 0;
+ }
+
+ /* Delete inactivity timer if started. */
+ timer_active = del_timer_sync(&cfhsi->timer);
+
+ spin_unlock_bh(&cfhsi->lock);
+
+ if (timer_active) {
+ struct cfhsi_desc *desc = (struct cfhsi_desc *)cfhsi->tx_buf;
+ int len;
+ int res;
+
+ /* Create HSI frame. */
+ len = cfhsi_tx_frm(desc, cfhsi);
+ BUG_ON(!len);
+
+ /* Set up new transfer. */
+ res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev);
+ if (WARN_ON(res < 0)) {
+ dev_err(&cfhsi->ndev->dev, "%s: TX error %d.\n",
+ __func__, res);
+ cfhsi_abort_tx(cfhsi);
+ }
+ } else {
+ /* Schedule wake up work queue if the we initiate. */
+ if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits))
+ queue_work(cfhsi->wq, &cfhsi->wake_up_work);
+ }
+
+ return 0;
+}
+
+static int cfhsi_open(struct net_device *dev)
+{
+ netif_wake_queue(dev);
+
+ return 0;
+}
+
+static int cfhsi_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+static const struct net_device_ops cfhsi_ops = {
+ .ndo_open = cfhsi_open,
+ .ndo_stop = cfhsi_close,
+ .ndo_start_xmit = cfhsi_xmit
+};
+
+static void cfhsi_setup(struct net_device *dev)
+{
+ struct cfhsi *cfhsi = netdev_priv(dev);
+ dev->features = 0;
+ dev->netdev_ops = &cfhsi_ops;
+ dev->type = ARPHRD_CAIF;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+ dev->mtu = CFHSI_MAX_PAYLOAD_SZ;
+ dev->tx_queue_len = 0;
+ dev->destructor = free_netdev;
+ skb_queue_head_init(&cfhsi->qhead);
+ cfhsi->cfdev.link_select = CAIF_LINK_HIGH_BANDW;
+ cfhsi->cfdev.use_frag = false;
+ cfhsi->cfdev.use_stx = false;
+ cfhsi->cfdev.use_fcs = false;
+ cfhsi->ndev = dev;
+}
+
+int cfhsi_probe(struct platform_device *pdev)
+{
+ struct cfhsi *cfhsi = NULL;
+ struct net_device *ndev;
+ struct cfhsi_dev *dev;
+ int res;
+
+ ndev = alloc_netdev(sizeof(struct cfhsi), "cfhsi%d", cfhsi_setup);
+ if (!ndev)
+ return -ENODEV;
+
+ cfhsi = netdev_priv(ndev);
+ cfhsi->ndev = ndev;
+ cfhsi->pdev = pdev;
+
+ /* Initialize state vaiables. */
+ cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
+ cfhsi->rx_state.state = CFHSI_RX_STATE_DESC;
+
+ /* Set flow info */
+ cfhsi->flow_off_sent = 0;
+ cfhsi->q_low_mark = LOW_WATER_MARK;
+ cfhsi->q_high_mark = HIGH_WATER_MARK;
+
+ /* Assign the HSI device. */
+ dev = (struct cfhsi_dev *)pdev->dev.platform_data;
+ cfhsi->dev = dev;
+
+ /* Assign the driver to this HSI device. */
+ dev->drv = &cfhsi->drv;
+
+ /*
+ * Allocate a TX buffer with the size of a HSI packet descriptors
+ * and the necessary room for CAIF payload frames.
+ */
+ cfhsi->tx_buf = kzalloc(CFHSI_BUF_SZ_TX, GFP_KERNEL);
+ if (!cfhsi->tx_buf) {
+ res = -ENODEV;
+ goto err_alloc_tx;
+ }
+
+ /*
+ * Allocate a RX buffer with the size of two HSI packet descriptors and
+ * the necessary room for CAIF payload frames.
+ */
+ cfhsi->rx_buf = kzalloc(CFHSI_BUF_SZ_RX, GFP_KERNEL);
+ if (!cfhsi->rx_buf) {
+ res = -ENODEV;
+ goto err_alloc_rx;
+ }
+
+ /* Pre-calculate inactivity timeout. */
+ if (inactivity_timeout != -1) {
+ cfhsi->inactivity_timeout =
+ inactivity_timeout * HZ / 1000;
+ if (!cfhsi->inactivity_timeout)
+ cfhsi->inactivity_timeout = 1;
+ else if (cfhsi->inactivity_timeout > NEXT_TIMER_MAX_DELTA)
+ cfhsi->inactivity_timeout = NEXT_TIMER_MAX_DELTA;
+ } else {
+ cfhsi->inactivity_timeout = NEXT_TIMER_MAX_DELTA;
+ }
+
+ /* Initialize recieve vaiables. */
+ cfhsi->rx_ptr = cfhsi->rx_buf;
+ cfhsi->rx_len = CFHSI_DESC_SZ;
+
+ /* Initialize spin locks. */
+ spin_lock_init(&cfhsi->lock);
+
+ /* Set up the driver. */
+ cfhsi->drv.tx_done_cb = cfhsi_tx_done_cb;
+ cfhsi->drv.rx_done_cb = cfhsi_rx_done_cb;
+ cfhsi->drv.wake_up_cb = cfhsi_wake_up_cb;
+ cfhsi->drv.wake_down_cb = cfhsi_wake_down_cb;
+
+ /* Initialize the work queues. */
+ INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up);
+ INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down);
+ INIT_WORK(&cfhsi->out_of_sync_work, cfhsi_out_of_sync);
+
+ /* Clear all bit fields. */
+ clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
+ clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits);
+ clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
+ clear_bit(CFHSI_AWAKE, &cfhsi->bits);
+
+ /* Create work thread. */
+ cfhsi->wq = create_singlethread_workqueue(pdev->name);
+ if (!cfhsi->wq) {
+ dev_err(&ndev->dev, "%s: Failed to create work queue.\n",
+ __func__);
+ res = -ENODEV;
+ goto err_create_wq;
+ }
+
+ /* Initialize wait queues. */
+ init_waitqueue_head(&cfhsi->wake_up_wait);
+ init_waitqueue_head(&cfhsi->wake_down_wait);
+ init_waitqueue_head(&cfhsi->flush_fifo_wait);
+
+ /* Setup the inactivity timer. */
+ init_timer(&cfhsi->timer);
+ cfhsi->timer.data = (unsigned long)cfhsi;
+ cfhsi->timer.function = cfhsi_inactivity_tout;
+ /* Setup the slowpath RX timer. */
+ init_timer(&cfhsi->rx_slowpath_timer);
+ cfhsi->rx_slowpath_timer.data = (unsigned long)cfhsi;
+ cfhsi->rx_slowpath_timer.function = cfhsi_rx_slowpath;
+
+ /* Add CAIF HSI device to list. */
+ spin_lock(&cfhsi_list_lock);
+ list_add_tail(&cfhsi->list, &cfhsi_list);
+ spin_unlock(&cfhsi_list_lock);
+
+ /* Activate HSI interface. */
+ res = cfhsi->dev->cfhsi_up(cfhsi->dev);
+ if (res) {
+ dev_err(&cfhsi->ndev->dev,
+ "%s: can't activate HSI interface: %d.\n",
+ __func__, res);
+ goto err_activate;
+ }
+
+ /* Flush FIFO */
+ res = cfhsi_flush_fifo(cfhsi);
+ if (res) {
+ dev_err(&ndev->dev, "%s: Can't flush FIFO: %d.\n",
+ __func__, res);
+ goto err_net_reg;
+ }
+
+ /* Register network device. */
+ res = register_netdev(ndev);
+ if (res) {
+ dev_err(&ndev->dev, "%s: Registration error: %d.\n",
+ __func__, res);
+ goto err_net_reg;
+ }
+
+ netif_stop_queue(ndev);
+
+ return res;
+
+ err_net_reg:
+ cfhsi->dev->cfhsi_down(cfhsi->dev);
+ err_activate:
+ destroy_workqueue(cfhsi->wq);
+ err_create_wq:
+ kfree(cfhsi->rx_buf);
+ err_alloc_rx:
+ kfree(cfhsi->tx_buf);
+ err_alloc_tx:
+ free_netdev(ndev);
+
+ return res;
+}
+
+static void cfhsi_shutdown(struct cfhsi *cfhsi)
+{
+ u8 *tx_buf, *rx_buf;
+
+ /* Stop TXing */
+ netif_tx_stop_all_queues(cfhsi->ndev);
+
+ /* going to shutdown driver */
+ set_bit(CFHSI_SHUTDOWN, &cfhsi->bits);
+
+ /* Flush workqueue */
+ flush_workqueue(cfhsi->wq);
+
+ /* Delete timers if pending */
+ del_timer_sync(&cfhsi->timer);
+ del_timer_sync(&cfhsi->rx_slowpath_timer);
+
+ /* Cancel pending RX request (if any) */
+ cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev);
+
+ /* Destroy workqueue */
+ destroy_workqueue(cfhsi->wq);
+
+ /* Store bufferes: will be freed later. */
+ tx_buf = cfhsi->tx_buf;
+ rx_buf = cfhsi->rx_buf;
+
+ /* Flush transmit queues. */
+ cfhsi_abort_tx(cfhsi);
+
+ /* Deactivate interface */
+ cfhsi->dev->cfhsi_down(cfhsi->dev);
+
+ /* Finally unregister the network device. */
+ unregister_netdev(cfhsi->ndev);
+
+ /* Free buffers. */
+ kfree(tx_buf);
+ kfree(rx_buf);
+}
+
+int cfhsi_remove(struct platform_device *pdev)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ struct cfhsi *cfhsi = NULL;
+ struct cfhsi_dev *dev;
+
+ dev = (struct cfhsi_dev *)pdev->dev.platform_data;
+ spin_lock(&cfhsi_list_lock);
+ list_for_each_safe(list_node, n, &cfhsi_list) {
+ cfhsi = list_entry(list_node, struct cfhsi, list);
+ /* Find the corresponding device. */
+ if (cfhsi->dev == dev) {
+ /* Remove from list. */
+ list_del(list_node);
+ spin_unlock(&cfhsi_list_lock);
+
+ /* Shutdown driver. */
+ cfhsi_shutdown(cfhsi);
+
+ return 0;
+ }
+ }
+ spin_unlock(&cfhsi_list_lock);
+ return -ENODEV;
+}
+
+struct platform_driver cfhsi_plat_drv = {
+ .probe = cfhsi_probe,
+ .remove = cfhsi_remove,
+ .driver = {
+ .name = "cfhsi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __exit cfhsi_exit_module(void)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ struct cfhsi *cfhsi = NULL;
+
+ spin_lock(&cfhsi_list_lock);
+ list_for_each_safe(list_node, n, &cfhsi_list) {
+ cfhsi = list_entry(list_node, struct cfhsi, list);
+
+ /* Remove from list. */
+ list_del(list_node);
+ spin_unlock(&cfhsi_list_lock);
+
+ /* Shutdown driver. */
+ cfhsi_shutdown(cfhsi);
+
+ spin_lock(&cfhsi_list_lock);
+ }
+ spin_unlock(&cfhsi_list_lock);
+
+ /* Unregister platform driver. */
+ platform_driver_unregister(&cfhsi_plat_drv);
+}
+
+static int __init cfhsi_init_module(void)
+{
+ int result;
+
+ /* Initialize spin lock. */
+ spin_lock_init(&cfhsi_list_lock);
+
+ /* Register platform driver. */
+ result = platform_driver_register(&cfhsi_plat_drv);
+ if (result) {
+ printk(KERN_ERR "Could not register platform HSI driver: %d.\n",
+ result);
+ goto err_dev_register;
+ }
+
+ return result;
+
+ err_dev_register:
+ return result;
+}
+
+module_init(cfhsi_init_module);
+module_exit(cfhsi_exit_module);
diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c
index 3df0c0f8b8bf..23406e62c0b0 100644
--- a/drivers/net/caif/caif_serial.c
+++ b/drivers/net/caif/caif_serial.c
@@ -4,8 +4,8 @@
* License terms: GNU General Public License (GPL) version 2
*/
+#include <linux/hardirq.h>
#include <linux/init.h>
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/types.h>
diff --git a/drivers/net/caif/caif_shm_u5500.c b/drivers/net/caif/caif_shm_u5500.c
index 5f771ab712c4..89d76b7b325a 100644
--- a/drivers/net/caif/caif_shm_u5500.c
+++ b/drivers/net/caif/caif_shm_u5500.c
@@ -7,7 +7,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netdevice.h>
diff --git a/drivers/net/caif/caif_shmcore.c b/drivers/net/caif/caif_shmcore.c
index 731aa1193770..d4b26fb24ed9 100644
--- a/drivers/net/caif/caif_shmcore.c
+++ b/drivers/net/caif/caif_shmcore.c
@@ -134,7 +134,7 @@ int caif_shmdrv_rx_cb(u32 mbx_msg, void *priv)
u32 avail_emptybuff = 0;
unsigned long flags = 0;
- pshm_drv = (struct shmdrv_layer *)priv;
+ pshm_drv = priv;
/* Check for received buffers. */
if (mbx_msg & SHM_FULL_MASK) {
diff --git a/drivers/net/caif/caif_spi.c b/drivers/net/caif/caif_spi.c
index 57e639373815..05e791f46aef 100644
--- a/drivers/net/caif/caif_spi.c
+++ b/drivers/net/caif/caif_spi.c
@@ -5,7 +5,6 @@
* License terms: GNU General Public License (GPL) version 2.
*/
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -665,8 +664,6 @@ int cfspi_spi_probe(struct platform_device *pdev)
/* Allocate DMA buffers. */
cfspi->xfer.va_tx = dma_alloc(&cfspi->xfer.pa_tx);
if (!cfspi->xfer.va_tx) {
- printk(KERN_WARNING
- "CFSPI: failed to allocate dma TX buffer.\n");
res = -ENODEV;
goto err_dma_alloc_tx;
}
@@ -674,8 +671,6 @@ int cfspi_spi_probe(struct platform_device *pdev)
cfspi->xfer.va_rx = dma_alloc(&cfspi->xfer.pa_rx);
if (!cfspi->xfer.va_rx) {
- printk(KERN_WARNING
- "CFSPI: failed to allocate dma TX buffer.\n");
res = -ENODEV;
goto err_dma_alloc_rx;
}
diff --git a/drivers/net/caif/caif_spi_slave.c b/drivers/net/caif/caif_spi_slave.c
index b009e03cda9e..e139e133fc79 100644
--- a/drivers/net/caif/caif_spi_slave.c
+++ b/drivers/net/caif/caif_spi_slave.c
@@ -4,7 +4,6 @@
* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
* License terms: GNU General Public License (GPL) version 2.
*/
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 754df5ef1729..f6c98fb4a517 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -4,7 +4,6 @@ menu "CAN Device Drivers"
config CAN_VCAN
tristate "Virtual Local CAN Interface (vcan)"
depends on CAN
- default N
---help---
Similar to the network loopback devices, vcan offers a
virtual local CAN interface.
@@ -15,7 +14,6 @@ config CAN_VCAN
config CAN_SLCAN
tristate "Serial / USB serial CAN Adaptors (slcan)"
depends on CAN
- default N
---help---
CAN driver for several 'low cost' CAN interfaces that are attached
via serial lines or via USB-to-serial adapters using the LAWICEL
@@ -58,9 +56,10 @@ config CAN_CALC_BITTIMING
config CAN_AT91
tristate "Atmel AT91 onchip CAN controller"
- depends on CAN_DEV && ARCH_AT91SAM9263
+ depends on CAN_DEV && (ARCH_AT91SAM9263 || ARCH_AT91SAM9X5)
---help---
- This is a driver for the SoC CAN controller in Atmel's AT91SAM9263.
+ This is a driver for the SoC CAN controller in Atmel's AT91SAM9263
+ and AT91SAM9X5 processors.
config CAN_TI_HECC
depends on CAN_DEV && ARCH_OMAP3
@@ -124,7 +123,6 @@ source "drivers/net/can/softing/Kconfig"
config CAN_DEBUG_DEVICES
bool "CAN devices debugging messages"
depends on CAN
- default N
---help---
Say Y here if you want the CAN device drivers to produce a bunch of
debug messages to the system log. Select this if you are having
diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
index 74efb5a2ad41..044ea0647b04 100644
--- a/drivers/net/can/at91_can.c
+++ b/drivers/net/can/at91_can.c
@@ -8,8 +8,6 @@
* Public License ("GPL") version 2 as distributed in the 'COPYING'
* file from the main directory of the linux kernel source.
*
- * Send feedback to <socketcan-users@lists.berlios.de>
- *
*
* Your platform definition file should specify something like:
*
@@ -41,32 +39,7 @@
#include <mach/board.h>
-#define AT91_NAPI_WEIGHT 11
-
-/*
- * RX/TX Mailbox split
- * don't dare to touch
- */
-#define AT91_MB_RX_NUM 11
-#define AT91_MB_TX_SHIFT 2
-
-#define AT91_MB_RX_FIRST 1
-#define AT91_MB_RX_LAST (AT91_MB_RX_FIRST + AT91_MB_RX_NUM - 1)
-
-#define AT91_MB_RX_MASK(i) ((1 << (i)) - 1)
-#define AT91_MB_RX_SPLIT 8
-#define AT91_MB_RX_LOW_LAST (AT91_MB_RX_SPLIT - 1)
-#define AT91_MB_RX_LOW_MASK (AT91_MB_RX_MASK(AT91_MB_RX_SPLIT) & \
- ~AT91_MB_RX_MASK(AT91_MB_RX_FIRST))
-
-#define AT91_MB_TX_NUM (1 << AT91_MB_TX_SHIFT)
-#define AT91_MB_TX_FIRST (AT91_MB_RX_LAST + 1)
-#define AT91_MB_TX_LAST (AT91_MB_TX_FIRST + AT91_MB_TX_NUM - 1)
-
-#define AT91_NEXT_PRIO_SHIFT (AT91_MB_TX_SHIFT)
-#define AT91_NEXT_PRIO_MASK (0xf << AT91_MB_TX_SHIFT)
-#define AT91_NEXT_MB_MASK (AT91_MB_TX_NUM - 1)
-#define AT91_NEXT_MASK ((AT91_MB_TX_NUM - 1) | AT91_NEXT_PRIO_MASK)
+#define AT91_MB_MASK(i) ((1 << (i)) - 1)
/* Common registers */
enum at91_reg {
@@ -128,12 +101,6 @@ enum at91_mb_mode {
};
/* Interrupt mask bits */
-#define AT91_IRQ_MB_RX ((1 << (AT91_MB_RX_LAST + 1)) \
- - (1 << AT91_MB_RX_FIRST))
-#define AT91_IRQ_MB_TX ((1 << (AT91_MB_TX_LAST + 1)) \
- - (1 << AT91_MB_TX_FIRST))
-#define AT91_IRQ_MB_ALL (AT91_IRQ_MB_RX | AT91_IRQ_MB_TX)
-
#define AT91_IRQ_ERRA (1 << 16)
#define AT91_IRQ_WARN (1 << 17)
#define AT91_IRQ_ERRP (1 << 18)
@@ -156,22 +123,51 @@ enum at91_mb_mode {
#define AT91_IRQ_ALL (0x1fffffff)
+enum at91_devtype {
+ AT91_DEVTYPE_SAM9263,
+ AT91_DEVTYPE_SAM9X5,
+};
+
+struct at91_devtype_data {
+ unsigned int rx_first;
+ unsigned int rx_split;
+ unsigned int rx_last;
+ unsigned int tx_shift;
+ enum at91_devtype type;
+};
+
struct at91_priv {
- struct can_priv can; /* must be the first member! */
- struct net_device *dev;
- struct napi_struct napi;
+ struct can_priv can; /* must be the first member! */
+ struct net_device *dev;
+ struct napi_struct napi;
- void __iomem *reg_base;
+ void __iomem *reg_base;
- u32 reg_sr;
- unsigned int tx_next;
- unsigned int tx_echo;
- unsigned int rx_next;
+ u32 reg_sr;
+ unsigned int tx_next;
+ unsigned int tx_echo;
+ unsigned int rx_next;
+ struct at91_devtype_data devtype_data;
- struct clk *clk;
- struct at91_can_data *pdata;
+ struct clk *clk;
+ struct at91_can_data *pdata;
- canid_t mb0_id;
+ canid_t mb0_id;
+};
+
+static const struct at91_devtype_data at91_devtype_data[] __devinitconst = {
+ [AT91_DEVTYPE_SAM9263] = {
+ .rx_first = 1,
+ .rx_split = 8,
+ .rx_last = 11,
+ .tx_shift = 2,
+ },
+ [AT91_DEVTYPE_SAM9X5] = {
+ .rx_first = 0,
+ .rx_split = 4,
+ .rx_last = 5,
+ .tx_shift = 1,
+ },
};
static struct can_bittiming_const at91_bittiming_const = {
@@ -186,19 +182,111 @@ static struct can_bittiming_const at91_bittiming_const = {
.brp_inc = 1,
};
-static inline int get_tx_next_mb(const struct at91_priv *priv)
+#define AT91_IS(_model) \
+static inline int at91_is_sam##_model(const struct at91_priv *priv) \
+{ \
+ return priv->devtype_data.type == AT91_DEVTYPE_SAM##_model; \
+}
+
+AT91_IS(9263);
+AT91_IS(9X5);
+
+static inline unsigned int get_mb_rx_first(const struct at91_priv *priv)
+{
+ return priv->devtype_data.rx_first;
+}
+
+static inline unsigned int get_mb_rx_last(const struct at91_priv *priv)
{
- return (priv->tx_next & AT91_NEXT_MB_MASK) + AT91_MB_TX_FIRST;
+ return priv->devtype_data.rx_last;
}
-static inline int get_tx_next_prio(const struct at91_priv *priv)
+static inline unsigned int get_mb_rx_split(const struct at91_priv *priv)
{
- return (priv->tx_next >> AT91_NEXT_PRIO_SHIFT) & 0xf;
+ return priv->devtype_data.rx_split;
}
-static inline int get_tx_echo_mb(const struct at91_priv *priv)
+static inline unsigned int get_mb_rx_num(const struct at91_priv *priv)
{
- return (priv->tx_echo & AT91_NEXT_MB_MASK) + AT91_MB_TX_FIRST;
+ return get_mb_rx_last(priv) - get_mb_rx_first(priv) + 1;
+}
+
+static inline unsigned int get_mb_rx_low_last(const struct at91_priv *priv)
+{
+ return get_mb_rx_split(priv) - 1;
+}
+
+static inline unsigned int get_mb_rx_low_mask(const struct at91_priv *priv)
+{
+ return AT91_MB_MASK(get_mb_rx_split(priv)) &
+ ~AT91_MB_MASK(get_mb_rx_first(priv));
+}
+
+static inline unsigned int get_mb_tx_shift(const struct at91_priv *priv)
+{
+ return priv->devtype_data.tx_shift;
+}
+
+static inline unsigned int get_mb_tx_num(const struct at91_priv *priv)
+{
+ return 1 << get_mb_tx_shift(priv);
+}
+
+static inline unsigned int get_mb_tx_first(const struct at91_priv *priv)
+{
+ return get_mb_rx_last(priv) + 1;
+}
+
+static inline unsigned int get_mb_tx_last(const struct at91_priv *priv)
+{
+ return get_mb_tx_first(priv) + get_mb_tx_num(priv) - 1;
+}
+
+static inline unsigned int get_next_prio_shift(const struct at91_priv *priv)
+{
+ return get_mb_tx_shift(priv);
+}
+
+static inline unsigned int get_next_prio_mask(const struct at91_priv *priv)
+{
+ return 0xf << get_mb_tx_shift(priv);
+}
+
+static inline unsigned int get_next_mb_mask(const struct at91_priv *priv)
+{
+ return AT91_MB_MASK(get_mb_tx_shift(priv));
+}
+
+static inline unsigned int get_next_mask(const struct at91_priv *priv)
+{
+ return get_next_mb_mask(priv) | get_next_prio_mask(priv);
+}
+
+static inline unsigned int get_irq_mb_rx(const struct at91_priv *priv)
+{
+ return AT91_MB_MASK(get_mb_rx_last(priv) + 1) &
+ ~AT91_MB_MASK(get_mb_rx_first(priv));
+}
+
+static inline unsigned int get_irq_mb_tx(const struct at91_priv *priv)
+{
+ return AT91_MB_MASK(get_mb_tx_last(priv) + 1) &
+ ~AT91_MB_MASK(get_mb_tx_first(priv));
+}
+
+static inline unsigned int get_tx_next_mb(const struct at91_priv *priv)
+{
+ return (priv->tx_next & get_next_mb_mask(priv)) + get_mb_tx_first(priv);
+}
+
+static inline unsigned int get_tx_next_prio(const struct at91_priv *priv)
+{
+ return (priv->tx_next >> get_next_prio_shift(priv)) & 0xf;
+}
+
+static inline unsigned int get_tx_echo_mb(const struct at91_priv *priv)
+{
+ return (priv->tx_echo & get_next_mb_mask(priv)) + get_mb_tx_first(priv);
}
static inline u32 at91_read(const struct at91_priv *priv, enum at91_reg reg)
@@ -259,29 +347,29 @@ static void at91_setup_mailboxes(struct net_device *dev)
* overflow.
*/
reg_mid = at91_can_id_to_reg_mid(priv->mb0_id);
- for (i = 0; i < AT91_MB_RX_FIRST; i++) {
+ for (i = 0; i < get_mb_rx_first(priv); i++) {
set_mb_mode(priv, i, AT91_MB_MODE_DISABLED);
at91_write(priv, AT91_MID(i), reg_mid);
at91_write(priv, AT91_MCR(i), 0x0); /* clear dlc */
}
- for (i = AT91_MB_RX_FIRST; i < AT91_MB_RX_LAST; i++)
+ for (i = get_mb_rx_first(priv); i < get_mb_rx_last(priv); i++)
set_mb_mode(priv, i, AT91_MB_MODE_RX);
- set_mb_mode(priv, AT91_MB_RX_LAST, AT91_MB_MODE_RX_OVRWR);
+ set_mb_mode(priv, get_mb_rx_last(priv), AT91_MB_MODE_RX_OVRWR);
/* reset acceptance mask and id register */
- for (i = AT91_MB_RX_FIRST; i <= AT91_MB_RX_LAST; i++) {
- at91_write(priv, AT91_MAM(i), 0x0 );
+ for (i = get_mb_rx_first(priv); i <= get_mb_rx_last(priv); i++) {
+ at91_write(priv, AT91_MAM(i), 0x0);
at91_write(priv, AT91_MID(i), AT91_MID_MIDE);
}
/* The last 4 mailboxes are used for transmitting. */
- for (i = AT91_MB_TX_FIRST; i <= AT91_MB_TX_LAST; i++)
+ for (i = get_mb_tx_first(priv); i <= get_mb_tx_last(priv); i++)
set_mb_mode_prio(priv, i, AT91_MB_MODE_TX, 0);
/* Reset tx and rx helper pointers */
priv->tx_next = priv->tx_echo = 0;
- priv->rx_next = AT91_MB_RX_FIRST;
+ priv->rx_next = get_mb_rx_first(priv);
}
static int at91_set_bittiming(struct net_device *dev)
@@ -336,7 +424,7 @@ static void at91_chip_start(struct net_device *dev)
priv->can.state = CAN_STATE_ERROR_ACTIVE;
/* Enable interrupts */
- reg_ier = AT91_IRQ_MB_RX | AT91_IRQ_ERRP | AT91_IRQ_ERR_FRAME;
+ reg_ier = get_irq_mb_rx(priv) | AT91_IRQ_ERRP | AT91_IRQ_ERR_FRAME;
at91_write(priv, AT91_IDR, AT91_IRQ_ALL);
at91_write(priv, AT91_IER, reg_ier);
}
@@ -375,8 +463,8 @@ static void at91_chip_stop(struct net_device *dev, enum can_state state)
* mailbox, but without the offset AT91_MB_TX_FIRST. The lower bits
* encode the mailbox number, the upper 4 bits the mailbox priority:
*
- * priv->tx_next = (prio << AT91_NEXT_PRIO_SHIFT) ||
- * (mb - AT91_MB_TX_FIRST);
+ * priv->tx_next = (prio << get_next_prio_shift(priv)) |
+ * (mb - get_mb_tx_first(priv));
*
*/
static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev)
@@ -417,7 +505,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev)
stats->tx_bytes += cf->can_dlc;
/* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */
- can_put_echo_skb(skb, dev, mb - AT91_MB_TX_FIRST);
+ can_put_echo_skb(skb, dev, mb - get_mb_tx_first(priv));
/*
* we have to stop the queue and deliver all messages in case
@@ -430,7 +518,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev)
priv->tx_next++;
if (!(at91_read(priv, AT91_MSR(get_tx_next_mb(priv))) &
AT91_MSR_MRDY) ||
- (priv->tx_next & AT91_NEXT_MASK) == 0)
+ (priv->tx_next & get_next_mask(priv)) == 0)
netif_stop_queue(dev);
/* Enable interrupt for this mailbox */
@@ -447,7 +535,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev)
*/
static inline void at91_activate_rx_low(const struct at91_priv *priv)
{
- u32 mask = AT91_MB_RX_LOW_MASK;
+ u32 mask = get_mb_rx_low_mask(priv);
at91_write(priv, AT91_TCR, mask);
}
@@ -513,17 +601,19 @@ static void at91_read_mb(struct net_device *dev, unsigned int mb,
cf->can_id = (reg_mid >> 18) & CAN_SFF_MASK;
reg_msr = at91_read(priv, AT91_MSR(mb));
- if (reg_msr & AT91_MSR_MRTR)
- cf->can_id |= CAN_RTR_FLAG;
cf->can_dlc = get_can_dlc((reg_msr >> 16) & 0xf);
- *(u32 *)(cf->data + 0) = at91_read(priv, AT91_MDL(mb));
- *(u32 *)(cf->data + 4) = at91_read(priv, AT91_MDH(mb));
+ if (reg_msr & AT91_MSR_MRTR)
+ cf->can_id |= CAN_RTR_FLAG;
+ else {
+ *(u32 *)(cf->data + 0) = at91_read(priv, AT91_MDL(mb));
+ *(u32 *)(cf->data + 4) = at91_read(priv, AT91_MDH(mb));
+ }
/* allow RX of extended frames */
at91_write(priv, AT91_MID(mb), AT91_MID_MIDE);
- if (unlikely(mb == AT91_MB_RX_LAST && reg_msr & AT91_MSR_MMI))
+ if (unlikely(mb == get_mb_rx_last(priv) && reg_msr & AT91_MSR_MMI))
at91_rx_overflow_err(dev);
}
@@ -561,8 +651,9 @@ static void at91_read_msg(struct net_device *dev, unsigned int mb)
*
* Theory of Operation:
*
- * 11 of the 16 mailboxes on the chip are reserved for RX. we split
- * them into 2 groups. The lower group holds 7 and upper 4 mailboxes.
+ * About 3/4 of the mailboxes (get_mb_rx_first()...get_mb_rx_last())
+ * on the chip are reserved for RX. We split them into 2 groups. The
+ * lower group ranges from get_mb_rx_first() to get_mb_rx_low_last().
*
* Like it or not, but the chip always saves a received CAN message
* into the first free mailbox it finds (starting with the
@@ -610,23 +701,23 @@ static int at91_poll_rx(struct net_device *dev, int quota)
unsigned int mb;
int received = 0;
- if (priv->rx_next > AT91_MB_RX_LOW_LAST &&
- reg_sr & AT91_MB_RX_LOW_MASK)
+ if (priv->rx_next > get_mb_rx_low_last(priv) &&
+ reg_sr & get_mb_rx_low_mask(priv))
netdev_info(dev,
"order of incoming frames cannot be guaranteed\n");
again:
- for (mb = find_next_bit(addr, AT91_MB_RX_LAST + 1, priv->rx_next);
- mb < AT91_MB_RX_LAST + 1 && quota > 0;
+ for (mb = find_next_bit(addr, get_mb_tx_first(priv), priv->rx_next);
+ mb < get_mb_tx_first(priv) && quota > 0;
reg_sr = at91_read(priv, AT91_SR),
- mb = find_next_bit(addr, AT91_MB_RX_LAST + 1, ++priv->rx_next)) {
+ mb = find_next_bit(addr, get_mb_tx_first(priv), ++priv->rx_next)) {
at91_read_msg(dev, mb);
/* reactivate mailboxes */
- if (mb == AT91_MB_RX_LOW_LAST)
+ if (mb == get_mb_rx_low_last(priv))
/* all lower mailboxed, if just finished it */
at91_activate_rx_low(priv);
- else if (mb > AT91_MB_RX_LOW_LAST)
+ else if (mb > get_mb_rx_low_last(priv))
/* only the mailbox we read */
at91_activate_rx_mb(priv, mb);
@@ -635,9 +726,9 @@ static int at91_poll_rx(struct net_device *dev, int quota)
}
/* upper group completed, look again in lower */
- if (priv->rx_next > AT91_MB_RX_LOW_LAST &&
- quota > 0 && mb > AT91_MB_RX_LAST) {
- priv->rx_next = AT91_MB_RX_FIRST;
+ if (priv->rx_next > get_mb_rx_low_last(priv) &&
+ quota > 0 && mb > get_mb_rx_last(priv)) {
+ priv->rx_next = get_mb_rx_first(priv);
goto again;
}
@@ -720,7 +811,7 @@ static int at91_poll(struct napi_struct *napi, int quota)
u32 reg_sr = at91_read(priv, AT91_SR);
int work_done = 0;
- if (reg_sr & AT91_IRQ_MB_RX)
+ if (reg_sr & get_irq_mb_rx(priv))
work_done += at91_poll_rx(dev, quota - work_done);
/*
@@ -734,7 +825,7 @@ static int at91_poll(struct napi_struct *napi, int quota)
if (work_done < quota) {
/* enable IRQs for frame errors and all mailboxes >= rx_next */
u32 reg_ier = AT91_IRQ_ERR_FRAME;
- reg_ier |= AT91_IRQ_MB_RX & ~AT91_MB_RX_MASK(priv->rx_next);
+ reg_ier |= get_irq_mb_rx(priv) & ~AT91_MB_MASK(priv->rx_next);
napi_complete(napi);
at91_write(priv, AT91_IER, reg_ier);
@@ -783,7 +874,7 @@ static void at91_irq_tx(struct net_device *dev, u32 reg_sr)
if (likely(reg_msr & AT91_MSR_MRDY &&
~reg_msr & AT91_MSR_MABT)) {
/* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */
- can_get_echo_skb(dev, mb - AT91_MB_TX_FIRST);
+ can_get_echo_skb(dev, mb - get_mb_tx_first(priv));
dev->stats.tx_packets++;
}
}
@@ -793,8 +884,8 @@ static void at91_irq_tx(struct net_device *dev, u32 reg_sr)
* we get a TX int for the last can frame directly before a
* wrap around.
*/
- if ((priv->tx_next & AT91_NEXT_MASK) != 0 ||
- (priv->tx_echo & AT91_NEXT_MASK) == 0)
+ if ((priv->tx_next & get_next_mask(priv)) != 0 ||
+ (priv->tx_echo & get_next_mask(priv)) == 0)
netif_wake_queue(dev);
}
@@ -906,6 +997,29 @@ static void at91_irq_err_state(struct net_device *dev,
at91_write(priv, AT91_IER, reg_ier);
}
+static int at91_get_state_by_bec(const struct net_device *dev,
+ enum can_state *state)
+{
+ struct can_berr_counter bec;
+ int err;
+
+ err = at91_get_berr_counter(dev, &bec);
+ if (err)
+ return err;
+
+ if (bec.txerr < 96 && bec.rxerr < 96)
+ *state = CAN_STATE_ERROR_ACTIVE;
+ else if (bec.txerr < 128 && bec.rxerr < 128)
+ *state = CAN_STATE_ERROR_WARNING;
+ else if (bec.txerr < 256 && bec.rxerr < 256)
+ *state = CAN_STATE_ERROR_PASSIVE;
+ else
+ *state = CAN_STATE_BUS_OFF;
+
+ return 0;
+}
+
+
static void at91_irq_err(struct net_device *dev)
{
struct at91_priv *priv = netdev_priv(dev);
@@ -913,21 +1027,28 @@ static void at91_irq_err(struct net_device *dev)
struct can_frame *cf;
enum can_state new_state;
u32 reg_sr;
+ int err;
- reg_sr = at91_read(priv, AT91_SR);
-
- /* we need to look at the unmasked reg_sr */
- if (unlikely(reg_sr & AT91_IRQ_BOFF))
- new_state = CAN_STATE_BUS_OFF;
- else if (unlikely(reg_sr & AT91_IRQ_ERRP))
- new_state = CAN_STATE_ERROR_PASSIVE;
- else if (unlikely(reg_sr & AT91_IRQ_WARN))
- new_state = CAN_STATE_ERROR_WARNING;
- else if (likely(reg_sr & AT91_IRQ_ERRA))
- new_state = CAN_STATE_ERROR_ACTIVE;
- else {
- netdev_err(dev, "BUG! hardware in undefined state\n");
- return;
+ if (at91_is_sam9263(priv)) {
+ reg_sr = at91_read(priv, AT91_SR);
+
+ /* we need to look at the unmasked reg_sr */
+ if (unlikely(reg_sr & AT91_IRQ_BOFF))
+ new_state = CAN_STATE_BUS_OFF;
+ else if (unlikely(reg_sr & AT91_IRQ_ERRP))
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ else if (unlikely(reg_sr & AT91_IRQ_WARN))
+ new_state = CAN_STATE_ERROR_WARNING;
+ else if (likely(reg_sr & AT91_IRQ_ERRA))
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ else {
+ netdev_err(dev, "BUG! hardware in undefined state\n");
+ return;
+ }
+ } else {
+ err = at91_get_state_by_bec(dev, &new_state);
+ if (err)
+ return;
}
/* state hasn't changed */
@@ -968,19 +1089,19 @@ static irqreturn_t at91_irq(int irq, void *dev_id)
handled = IRQ_HANDLED;
/* Receive or error interrupt? -> napi */
- if (reg_sr & (AT91_IRQ_MB_RX | AT91_IRQ_ERR_FRAME)) {
+ if (reg_sr & (get_irq_mb_rx(priv) | AT91_IRQ_ERR_FRAME)) {
/*
* The error bits are clear on read,
* save for later use.
*/
priv->reg_sr = reg_sr;
at91_write(priv, AT91_IDR,
- AT91_IRQ_MB_RX | AT91_IRQ_ERR_FRAME);
+ get_irq_mb_rx(priv) | AT91_IRQ_ERR_FRAME);
napi_schedule(&priv->napi);
}
/* Transmission complete interrupt */
- if (reg_sr & AT91_IRQ_MB_TX)
+ if (reg_sr & get_irq_mb_tx(priv))
at91_irq_tx(dev, reg_sr);
at91_irq_err(dev);
@@ -1123,6 +1244,8 @@ static struct attribute_group at91_sysfs_attr_group = {
static int __devinit at91_can_probe(struct platform_device *pdev)
{
+ const struct at91_devtype_data *devtype_data;
+ enum at91_devtype devtype;
struct net_device *dev;
struct at91_priv *priv;
struct resource *res;
@@ -1130,6 +1253,9 @@ static int __devinit at91_can_probe(struct platform_device *pdev)
void __iomem *addr;
int err, irq;
+ devtype = pdev->id_entry->driver_data;
+ devtype_data = &at91_devtype_data[devtype];
+
clk = clk_get(&pdev->dev, "can_clk");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "no clock defined\n");
@@ -1157,7 +1283,8 @@ static int __devinit at91_can_probe(struct platform_device *pdev)
goto exit_release;
}
- dev = alloc_candev(sizeof(struct at91_priv), AT91_MB_TX_NUM);
+ dev = alloc_candev(sizeof(struct at91_priv),
+ 1 << devtype_data->tx_shift);
if (!dev) {
err = -ENOMEM;
goto exit_iounmap;
@@ -1166,7 +1293,6 @@ static int __devinit at91_can_probe(struct platform_device *pdev)
dev->netdev_ops = &at91_netdev_ops;
dev->irq = irq;
dev->flags |= IFF_ECHO;
- dev->sysfs_groups[0] = &at91_sysfs_attr_group;
priv = netdev_priv(dev);
priv->can.clock.freq = clk_get_rate(clk);
@@ -1174,13 +1300,18 @@ static int __devinit at91_can_probe(struct platform_device *pdev)
priv->can.do_set_mode = at91_set_mode;
priv->can.do_get_berr_counter = at91_get_berr_counter;
priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
- priv->reg_base = addr;
priv->dev = dev;
+ priv->reg_base = addr;
+ priv->devtype_data = *devtype_data;
+ priv->devtype_data.type = devtype;
priv->clk = clk;
priv->pdata = pdev->dev.platform_data;
priv->mb0_id = 0x7ff;
- netif_napi_add(dev, &priv->napi, at91_poll, AT91_NAPI_WEIGHT);
+ netif_napi_add(dev, &priv->napi, at91_poll, get_mb_rx_num(priv));
+
+ if (at91_is_sam9263(priv))
+ dev->sysfs_groups[0] = &at91_sysfs_attr_group;
dev_set_drvdata(&pdev->dev, dev);
SET_NETDEV_DEV(dev, &pdev->dev);
@@ -1230,13 +1361,26 @@ static int __devexit at91_can_remove(struct platform_device *pdev)
return 0;
}
+static const struct platform_device_id at91_can_id_table[] = {
+ {
+ .name = "at91_can",
+ .driver_data = AT91_DEVTYPE_SAM9263,
+ }, {
+ .name = "at91sam9x5_can",
+ .driver_data = AT91_DEVTYPE_SAM9X5,
+ }, {
+ /* sentinel */
+ }
+};
+
static struct platform_driver at91_can_driver = {
- .probe = at91_can_probe,
- .remove = __devexit_p(at91_can_remove),
- .driver = {
- .name = KBUILD_MODNAME,
- .owner = THIS_MODULE,
+ .probe = at91_can_probe,
+ .remove = __devexit_p(at91_can_remove),
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
},
+ .id_table = at91_can_id_table,
};
static int __init at91_can_module_init(void)
diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c
index b6e890d28366..a1c5abc38cd2 100644
--- a/drivers/net/can/bfin_can.c
+++ b/drivers/net/can/bfin_can.c
@@ -79,8 +79,8 @@ static int bfin_can_set_bittiming(struct net_device *dev)
if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
timing |= SAM;
- bfin_write16(&reg->clock, clk);
- bfin_write16(&reg->timing, timing);
+ bfin_write(&reg->clock, clk);
+ bfin_write(&reg->timing, timing);
dev_info(dev->dev.parent, "setting CLOCK=0x%04x TIMING=0x%04x\n",
clk, timing);
@@ -96,16 +96,16 @@ static void bfin_can_set_reset_mode(struct net_device *dev)
int i;
/* disable interrupts */
- bfin_write16(&reg->mbim1, 0);
- bfin_write16(&reg->mbim2, 0);
- bfin_write16(&reg->gim, 0);
+ bfin_write(&reg->mbim1, 0);
+ bfin_write(&reg->mbim2, 0);
+ bfin_write(&reg->gim, 0);
/* reset can and enter configuration mode */
- bfin_write16(&reg->control, SRS | CCR);
+ bfin_write(&reg->control, SRS | CCR);
SSYNC();
- bfin_write16(&reg->control, CCR);
+ bfin_write(&reg->control, CCR);
SSYNC();
- while (!(bfin_read16(&reg->control) & CCA)) {
+ while (!(bfin_read(&reg->control) & CCA)) {
udelay(10);
if (--timeout == 0) {
dev_err(dev->dev.parent,
@@ -119,33 +119,33 @@ static void bfin_can_set_reset_mode(struct net_device *dev)
* by writing to CAN Mailbox Configuration Registers 1 and 2
* For all bits: 0 - Mailbox disabled, 1 - Mailbox enabled
*/
- bfin_write16(&reg->mc1, 0);
- bfin_write16(&reg->mc2, 0);
+ bfin_write(&reg->mc1, 0);
+ bfin_write(&reg->mc2, 0);
/* Set Mailbox Direction */
- bfin_write16(&reg->md1, 0xFFFF); /* mailbox 1-16 are RX */
- bfin_write16(&reg->md2, 0); /* mailbox 17-32 are TX */
+ bfin_write(&reg->md1, 0xFFFF); /* mailbox 1-16 are RX */
+ bfin_write(&reg->md2, 0); /* mailbox 17-32 are TX */
/* RECEIVE_STD_CHL */
for (i = 0; i < 2; i++) {
- bfin_write16(&reg->chl[RECEIVE_STD_CHL + i].id0, 0);
- bfin_write16(&reg->chl[RECEIVE_STD_CHL + i].id1, AME);
- bfin_write16(&reg->chl[RECEIVE_STD_CHL + i].dlc, 0);
- bfin_write16(&reg->msk[RECEIVE_STD_CHL + i].amh, 0x1FFF);
- bfin_write16(&reg->msk[RECEIVE_STD_CHL + i].aml, 0xFFFF);
+ bfin_write(&reg->chl[RECEIVE_STD_CHL + i].id0, 0);
+ bfin_write(&reg->chl[RECEIVE_STD_CHL + i].id1, AME);
+ bfin_write(&reg->chl[RECEIVE_STD_CHL + i].dlc, 0);
+ bfin_write(&reg->msk[RECEIVE_STD_CHL + i].amh, 0x1FFF);
+ bfin_write(&reg->msk[RECEIVE_STD_CHL + i].aml, 0xFFFF);
}
/* RECEIVE_EXT_CHL */
for (i = 0; i < 2; i++) {
- bfin_write16(&reg->chl[RECEIVE_EXT_CHL + i].id0, 0);
- bfin_write16(&reg->chl[RECEIVE_EXT_CHL + i].id1, AME | IDE);
- bfin_write16(&reg->chl[RECEIVE_EXT_CHL + i].dlc, 0);
- bfin_write16(&reg->msk[RECEIVE_EXT_CHL + i].amh, 0x1FFF);
- bfin_write16(&reg->msk[RECEIVE_EXT_CHL + i].aml, 0xFFFF);
+ bfin_write(&reg->chl[RECEIVE_EXT_CHL + i].id0, 0);
+ bfin_write(&reg->chl[RECEIVE_EXT_CHL + i].id1, AME | IDE);
+ bfin_write(&reg->chl[RECEIVE_EXT_CHL + i].dlc, 0);
+ bfin_write(&reg->msk[RECEIVE_EXT_CHL + i].amh, 0x1FFF);
+ bfin_write(&reg->msk[RECEIVE_EXT_CHL + i].aml, 0xFFFF);
}
- bfin_write16(&reg->mc2, BIT(TRANSMIT_CHL - 16));
- bfin_write16(&reg->mc1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL));
+ bfin_write(&reg->mc2, BIT(TRANSMIT_CHL - 16));
+ bfin_write(&reg->mc1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL));
SSYNC();
priv->can.state = CAN_STATE_STOPPED;
@@ -160,9 +160,9 @@ static void bfin_can_set_normal_mode(struct net_device *dev)
/*
* leave configuration mode
*/
- bfin_write16(&reg->control, bfin_read16(&reg->control) & ~CCR);
+ bfin_write(&reg->control, bfin_read(&reg->control) & ~CCR);
- while (bfin_read16(&reg->status) & CCA) {
+ while (bfin_read(&reg->status) & CCA) {
udelay(10);
if (--timeout == 0) {
dev_err(dev->dev.parent,
@@ -174,25 +174,25 @@ static void bfin_can_set_normal_mode(struct net_device *dev)
/*
* clear _All_ tx and rx interrupts
*/
- bfin_write16(&reg->mbtif1, 0xFFFF);
- bfin_write16(&reg->mbtif2, 0xFFFF);
- bfin_write16(&reg->mbrif1, 0xFFFF);
- bfin_write16(&reg->mbrif2, 0xFFFF);
+ bfin_write(&reg->mbtif1, 0xFFFF);
+ bfin_write(&reg->mbtif2, 0xFFFF);
+ bfin_write(&reg->mbrif1, 0xFFFF);
+ bfin_write(&reg->mbrif2, 0xFFFF);
/*
* clear global interrupt status register
*/
- bfin_write16(&reg->gis, 0x7FF); /* overwrites with '1' */
+ bfin_write(&reg->gis, 0x7FF); /* overwrites with '1' */
/*
* Initialize Interrupts
* - set bits in the mailbox interrupt mask register
* - global interrupt mask
*/
- bfin_write16(&reg->mbim1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL));
- bfin_write16(&reg->mbim2, BIT(TRANSMIT_CHL - 16));
+ bfin_write(&reg->mbim1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL));
+ bfin_write(&reg->mbim2, BIT(TRANSMIT_CHL - 16));
- bfin_write16(&reg->gim, EPIM | BOIM | RMLIM);
+ bfin_write(&reg->gim, EPIM | BOIM | RMLIM);
SSYNC();
}
@@ -242,37 +242,28 @@ static int bfin_can_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* fill id */
if (id & CAN_EFF_FLAG) {
- bfin_write16(&reg->chl[TRANSMIT_CHL].id0, id);
- if (id & CAN_RTR_FLAG)
- writew(((id & 0x1FFF0000) >> 16) | IDE | AME | RTR,
- &reg->chl[TRANSMIT_CHL].id1);
- else
- writew(((id & 0x1FFF0000) >> 16) | IDE | AME,
- &reg->chl[TRANSMIT_CHL].id1);
-
- } else {
- if (id & CAN_RTR_FLAG)
- writew((id << 2) | AME | RTR,
- &reg->chl[TRANSMIT_CHL].id1);
- else
- bfin_write16(&reg->chl[TRANSMIT_CHL].id1,
- (id << 2) | AME);
- }
+ bfin_write(&reg->chl[TRANSMIT_CHL].id0, id);
+ val = ((id & 0x1FFF0000) >> 16) | IDE;
+ } else
+ val = (id << 2);
+ if (id & CAN_RTR_FLAG)
+ val |= RTR;
+ bfin_write(&reg->chl[TRANSMIT_CHL].id1, val | AME);
/* fill payload */
for (i = 0; i < 8; i += 2) {
val = ((7 - i) < dlc ? (data[7 - i]) : 0) +
((6 - i) < dlc ? (data[6 - i] << 8) : 0);
- bfin_write16(&reg->chl[TRANSMIT_CHL].data[i], val);
+ bfin_write(&reg->chl[TRANSMIT_CHL].data[i], val);
}
/* fill data length code */
- bfin_write16(&reg->chl[TRANSMIT_CHL].dlc, dlc);
+ bfin_write(&reg->chl[TRANSMIT_CHL].dlc, dlc);
can_put_echo_skb(skb, dev, 0);
/* set transmit request */
- bfin_write16(&reg->trs2, BIT(TRANSMIT_CHL - 16));
+ bfin_write(&reg->trs2, BIT(TRANSMIT_CHL - 16));
return 0;
}
@@ -295,26 +286,26 @@ static void bfin_can_rx(struct net_device *dev, u16 isrc)
/* get id */
if (isrc & BIT(RECEIVE_EXT_CHL)) {
/* extended frame format (EFF) */
- cf->can_id = ((bfin_read16(&reg->chl[RECEIVE_EXT_CHL].id1)
+ cf->can_id = ((bfin_read(&reg->chl[RECEIVE_EXT_CHL].id1)
& 0x1FFF) << 16)
- + bfin_read16(&reg->chl[RECEIVE_EXT_CHL].id0);
+ + bfin_read(&reg->chl[RECEIVE_EXT_CHL].id0);
cf->can_id |= CAN_EFF_FLAG;
obj = RECEIVE_EXT_CHL;
} else {
/* standard frame format (SFF) */
- cf->can_id = (bfin_read16(&reg->chl[RECEIVE_STD_CHL].id1)
+ cf->can_id = (bfin_read(&reg->chl[RECEIVE_STD_CHL].id1)
& 0x1ffc) >> 2;
obj = RECEIVE_STD_CHL;
}
- if (bfin_read16(&reg->chl[obj].id1) & RTR)
+ if (bfin_read(&reg->chl[obj].id1) & RTR)
cf->can_id |= CAN_RTR_FLAG;
/* get data length code */
- cf->can_dlc = get_can_dlc(bfin_read16(&reg->chl[obj].dlc) & 0xF);
+ cf->can_dlc = get_can_dlc(bfin_read(&reg->chl[obj].dlc) & 0xF);
/* get payload */
for (i = 0; i < 8; i += 2) {
- val = bfin_read16(&reg->chl[obj].data[i]);
+ val = bfin_read(&reg->chl[obj].data[i]);
cf->data[7 - i] = (7 - i) < cf->can_dlc ? val : 0;
cf->data[6 - i] = (6 - i) < cf->can_dlc ? (val >> 8) : 0;
}
@@ -368,7 +359,7 @@ static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status)
if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING ||
state == CAN_STATE_ERROR_PASSIVE)) {
- u16 cec = bfin_read16(&reg->cec);
+ u16 cec = bfin_read(&reg->cec);
u8 rxerr = cec;
u8 txerr = cec >> 8;
@@ -419,23 +410,23 @@ irqreturn_t bfin_can_interrupt(int irq, void *dev_id)
struct net_device_stats *stats = &dev->stats;
u16 status, isrc;
- if ((irq == priv->tx_irq) && bfin_read16(&reg->mbtif2)) {
+ if ((irq == priv->tx_irq) && bfin_read(&reg->mbtif2)) {
/* transmission complete interrupt */
- bfin_write16(&reg->mbtif2, 0xFFFF);
+ bfin_write(&reg->mbtif2, 0xFFFF);
stats->tx_packets++;
- stats->tx_bytes += bfin_read16(&reg->chl[TRANSMIT_CHL].dlc);
+ stats->tx_bytes += bfin_read(&reg->chl[TRANSMIT_CHL].dlc);
can_get_echo_skb(dev, 0);
netif_wake_queue(dev);
- } else if ((irq == priv->rx_irq) && bfin_read16(&reg->mbrif1)) {
+ } else if ((irq == priv->rx_irq) && bfin_read(&reg->mbrif1)) {
/* receive interrupt */
- isrc = bfin_read16(&reg->mbrif1);
- bfin_write16(&reg->mbrif1, 0xFFFF);
+ isrc = bfin_read(&reg->mbrif1);
+ bfin_write(&reg->mbrif1, 0xFFFF);
bfin_can_rx(dev, isrc);
- } else if ((irq == priv->err_irq) && bfin_read16(&reg->gis)) {
+ } else if ((irq == priv->err_irq) && bfin_read(&reg->gis)) {
/* error interrupt */
- isrc = bfin_read16(&reg->gis);
- status = bfin_read16(&reg->esr);
- bfin_write16(&reg->gis, 0x7FF);
+ isrc = bfin_read(&reg->gis);
+ status = bfin_read(&reg->esr);
+ bfin_write(&reg->gis, 0x7FF);
bfin_can_err(dev, isrc, status);
} else {
return IRQ_NONE;
@@ -640,9 +631,9 @@ static int bfin_can_suspend(struct platform_device *pdev, pm_message_t mesg)
if (netif_running(dev)) {
/* enter sleep mode */
- bfin_write16(&reg->control, bfin_read16(&reg->control) | SMR);
+ bfin_write(&reg->control, bfin_read(&reg->control) | SMR);
SSYNC();
- while (!(bfin_read16(&reg->intr) & SMACK)) {
+ while (!(bfin_read(&reg->intr) & SMACK)) {
udelay(10);
if (--timeout == 0) {
dev_err(dev->dev.parent,
@@ -663,7 +654,7 @@ static int bfin_can_resume(struct platform_device *pdev)
if (netif_running(dev)) {
/* leave sleep mode */
- bfin_write16(&reg->intr, 0);
+ bfin_write(&reg->intr, 0);
SSYNC();
}
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c
index 7e5cc0bd913d..536bda072a16 100644
--- a/drivers/net/can/c_can/c_can.c
+++ b/drivers/net/can/c_can/c_can.c
@@ -26,7 +26,6 @@
*/
#include <linux/kernel.h>
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
@@ -34,7 +33,6 @@
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/list.h>
-#include <linux/delay.h>
#include <linux/io.h>
#include <linux/can.h>
diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c
index cc90824f2c9c..0b5c6f8bdd34 100644
--- a/drivers/net/can/c_can/c_can_platform.c
+++ b/drivers/net/can/c_can/c_can_platform.c
@@ -20,7 +20,6 @@
*/
#include <linux/kernel.h>
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
@@ -28,7 +27,6 @@
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/list.h>
-#include <linux/delay.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index d0f8c7e67e7d..25695bde0549 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -150,7 +150,19 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt)
bt->prop_seg = tseg1 / 2;
bt->phase_seg1 = tseg1 - bt->prop_seg;
bt->phase_seg2 = tseg2;
- bt->sjw = 1;
+
+ /* check for sjw user settings */
+ if (!bt->sjw || !btc->sjw_max)
+ bt->sjw = 1;
+ else {
+ /* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */
+ if (bt->sjw > btc->sjw_max)
+ bt->sjw = btc->sjw_max;
+ /* bt->sjw must not be higher than tseg2 */
+ if (tseg2 < bt->sjw)
+ bt->sjw = tseg2;
+ }
+
bt->brp = best_brp;
/* real bit-rate */
bt->bitrate = priv->clock.freq / (bt->brp * (tseg1 + tseg2 + 1));
@@ -208,7 +220,7 @@ static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt)
return 0;
}
-int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt)
+static int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt)
{
struct can_priv *priv = netdev_priv(dev);
int err;
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 17678117ed69..e02337953f41 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -33,10 +33,9 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
-#include <mach/clock.h>
-
#define DRV_NAME "flexcan"
/* 8 for RX fifo and 2 error handling */
@@ -192,6 +191,31 @@ static struct can_bittiming_const flexcan_bittiming_const = {
};
/*
+ * Abstract off the read/write for arm versus ppc.
+ */
+#if defined(__BIG_ENDIAN)
+static inline u32 flexcan_read(void __iomem *addr)
+{
+ return in_be32(addr);
+}
+
+static inline void flexcan_write(u32 val, void __iomem *addr)
+{
+ out_be32(addr, val);
+}
+#else
+static inline u32 flexcan_read(void __iomem *addr)
+{
+ return readl(addr);
+}
+
+static inline void flexcan_write(u32 val, void __iomem *addr)
+{
+ writel(val, addr);
+}
+#endif
+
+/*
* Swtich transceiver on or off
*/
static void flexcan_transceiver_switch(const struct flexcan_priv *priv, int on)
@@ -212,9 +236,9 @@ static inline void flexcan_chip_enable(struct flexcan_priv *priv)
struct flexcan_regs __iomem *regs = priv->base;
u32 reg;
- reg = readl(&regs->mcr);
+ reg = flexcan_read(&regs->mcr);
reg &= ~FLEXCAN_MCR_MDIS;
- writel(reg, &regs->mcr);
+ flexcan_write(reg, &regs->mcr);
udelay(10);
}
@@ -224,9 +248,9 @@ static inline void flexcan_chip_disable(struct flexcan_priv *priv)
struct flexcan_regs __iomem *regs = priv->base;
u32 reg;
- reg = readl(&regs->mcr);
+ reg = flexcan_read(&regs->mcr);
reg |= FLEXCAN_MCR_MDIS;
- writel(reg, &regs->mcr);
+ flexcan_write(reg, &regs->mcr);
}
static int flexcan_get_berr_counter(const struct net_device *dev,
@@ -234,7 +258,7 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
{
const struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->base;
- u32 reg = readl(&regs->ecr);
+ u32 reg = flexcan_read(&regs->ecr);
bec->txerr = (reg >> 0) & 0xff;
bec->rxerr = (reg >> 8) & 0xff;
@@ -268,15 +292,15 @@ static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (cf->can_dlc > 0) {
u32 data = be32_to_cpup((__be32 *)&cf->data[0]);
- writel(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[0]);
+ flexcan_write(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[0]);
}
if (cf->can_dlc > 3) {
u32 data = be32_to_cpup((__be32 *)&cf->data[4]);
- writel(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[1]);
+ flexcan_write(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[1]);
}
- writel(can_id, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_id);
- writel(ctrl, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
+ flexcan_write(can_id, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_id);
+ flexcan_write(ctrl, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
kfree_skb(skb);
@@ -464,8 +488,8 @@ static void flexcan_read_fifo(const struct net_device *dev,
struct flexcan_mb __iomem *mb = &regs->cantxfg[0];
u32 reg_ctrl, reg_id;
- reg_ctrl = readl(&mb->can_ctrl);
- reg_id = readl(&mb->can_id);
+ reg_ctrl = flexcan_read(&mb->can_ctrl);
+ reg_id = flexcan_read(&mb->can_id);
if (reg_ctrl & FLEXCAN_MB_CNT_IDE)
cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG;
else
@@ -475,12 +499,12 @@ static void flexcan_read_fifo(const struct net_device *dev,
cf->can_id |= CAN_RTR_FLAG;
cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf);
- *(__be32 *)(cf->data + 0) = cpu_to_be32(readl(&mb->data[0]));
- *(__be32 *)(cf->data + 4) = cpu_to_be32(readl(&mb->data[1]));
+ *(__be32 *)(cf->data + 0) = cpu_to_be32(flexcan_read(&mb->data[0]));
+ *(__be32 *)(cf->data + 4) = cpu_to_be32(flexcan_read(&mb->data[1]));
/* mark as read */
- writel(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->iflag1);
- readl(&regs->timer);
+ flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->iflag1);
+ flexcan_read(&regs->timer);
}
static int flexcan_read_frame(struct net_device *dev)
@@ -516,17 +540,17 @@ static int flexcan_poll(struct napi_struct *napi, int quota)
* The error bits are cleared on read,
* use saved value from irq handler.
*/
- reg_esr = readl(&regs->esr) | priv->reg_esr;
+ reg_esr = flexcan_read(&regs->esr) | priv->reg_esr;
/* handle state changes */
work_done += flexcan_poll_state(dev, reg_esr);
/* handle RX-FIFO */
- reg_iflag1 = readl(&regs->iflag1);
+ reg_iflag1 = flexcan_read(&regs->iflag1);
while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE &&
work_done < quota) {
work_done += flexcan_read_frame(dev);
- reg_iflag1 = readl(&regs->iflag1);
+ reg_iflag1 = flexcan_read(&regs->iflag1);
}
/* report bus errors */
@@ -536,8 +560,8 @@ static int flexcan_poll(struct napi_struct *napi, int quota)
if (work_done < quota) {
napi_complete(napi);
/* enable IRQs */
- writel(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
- writel(priv->reg_ctrl_default, &regs->ctrl);
+ flexcan_write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
+ flexcan_write(priv->reg_ctrl_default, &regs->ctrl);
}
return work_done;
@@ -551,9 +575,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
struct flexcan_regs __iomem *regs = priv->base;
u32 reg_iflag1, reg_esr;
- reg_iflag1 = readl(&regs->iflag1);
- reg_esr = readl(&regs->esr);
- writel(FLEXCAN_ESR_ERR_INT, &regs->esr); /* ACK err IRQ */
+ reg_iflag1 = flexcan_read(&regs->iflag1);
+ reg_esr = flexcan_read(&regs->esr);
+ flexcan_write(FLEXCAN_ESR_ERR_INT, &regs->esr); /* ACK err IRQ */
/*
* schedule NAPI in case of:
@@ -569,16 +593,16 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
* save them for later use.
*/
priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS;
- writel(FLEXCAN_IFLAG_DEFAULT & ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE,
- &regs->imask1);
- writel(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
+ flexcan_write(FLEXCAN_IFLAG_DEFAULT &
+ ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->imask1);
+ flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
&regs->ctrl);
napi_schedule(&priv->napi);
}
/* FIFO overflow */
if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) {
- writel(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, &regs->iflag1);
+ flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, &regs->iflag1);
dev->stats.rx_over_errors++;
dev->stats.rx_errors++;
}
@@ -587,7 +611,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
if (reg_iflag1 & (1 << FLEXCAN_TX_BUF_ID)) {
/* tx_bytes is incremented in flexcan_start_xmit */
stats->tx_packets++;
- writel((1 << FLEXCAN_TX_BUF_ID), &regs->iflag1);
+ flexcan_write((1 << FLEXCAN_TX_BUF_ID), &regs->iflag1);
netif_wake_queue(dev);
}
@@ -601,7 +625,7 @@ static void flexcan_set_bittiming(struct net_device *dev)
struct flexcan_regs __iomem *regs = priv->base;
u32 reg;
- reg = readl(&regs->ctrl);
+ reg = flexcan_read(&regs->ctrl);
reg &= ~(FLEXCAN_CTRL_PRESDIV(0xff) |
FLEXCAN_CTRL_RJW(0x3) |
FLEXCAN_CTRL_PSEG1(0x7) |
@@ -625,11 +649,11 @@ static void flexcan_set_bittiming(struct net_device *dev)
reg |= FLEXCAN_CTRL_SMP;
dev_info(dev->dev.parent, "writing ctrl=0x%08x\n", reg);
- writel(reg, &regs->ctrl);
+ flexcan_write(reg, &regs->ctrl);
/* print chip status */
dev_dbg(dev->dev.parent, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__,
- readl(&regs->mcr), readl(&regs->ctrl));
+ flexcan_read(&regs->mcr), flexcan_read(&regs->ctrl));
}
/*
@@ -650,10 +674,10 @@ static int flexcan_chip_start(struct net_device *dev)
flexcan_chip_enable(priv);
/* soft reset */
- writel(FLEXCAN_MCR_SOFTRST, &regs->mcr);
+ flexcan_write(FLEXCAN_MCR_SOFTRST, &regs->mcr);
udelay(10);
- reg_mcr = readl(&regs->mcr);
+ reg_mcr = flexcan_read(&regs->mcr);
if (reg_mcr & FLEXCAN_MCR_SOFTRST) {
dev_err(dev->dev.parent,
"Failed to softreset can module (mcr=0x%08x)\n",
@@ -675,12 +699,12 @@ static int flexcan_chip_start(struct net_device *dev)
* choose format C
*
*/
- reg_mcr = readl(&regs->mcr);
+ reg_mcr = flexcan_read(&regs->mcr);
reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN |
FLEXCAN_MCR_IDAM_C;
dev_dbg(dev->dev.parent, "%s: writing mcr=0x%08x", __func__, reg_mcr);
- writel(reg_mcr, &regs->mcr);
+ flexcan_write(reg_mcr, &regs->mcr);
/*
* CTRL
@@ -698,7 +722,7 @@ static int flexcan_chip_start(struct net_device *dev)
* (FLEXCAN_CTRL_ERR_MSK), too. Otherwise we don't get any
* warning or bus passive interrupts.
*/
- reg_ctrl = readl(&regs->ctrl);
+ reg_ctrl = flexcan_read(&regs->ctrl);
reg_ctrl &= ~FLEXCAN_CTRL_TSYN;
reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF |
FLEXCAN_CTRL_ERR_STATE | FLEXCAN_CTRL_ERR_MSK;
@@ -706,38 +730,39 @@ static int flexcan_chip_start(struct net_device *dev)
/* save for later use */
priv->reg_ctrl_default = reg_ctrl;
dev_dbg(dev->dev.parent, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);
- writel(reg_ctrl, &regs->ctrl);
+ flexcan_write(reg_ctrl, &regs->ctrl);
for (i = 0; i < ARRAY_SIZE(regs->cantxfg); i++) {
- writel(0, &regs->cantxfg[i].can_ctrl);
- writel(0, &regs->cantxfg[i].can_id);
- writel(0, &regs->cantxfg[i].data[0]);
- writel(0, &regs->cantxfg[i].data[1]);
+ flexcan_write(0, &regs->cantxfg[i].can_ctrl);
+ flexcan_write(0, &regs->cantxfg[i].can_id);
+ flexcan_write(0, &regs->cantxfg[i].data[0]);
+ flexcan_write(0, &regs->cantxfg[i].data[1]);
/* put MB into rx queue */
- writel(FLEXCAN_MB_CNT_CODE(0x4), &regs->cantxfg[i].can_ctrl);
+ flexcan_write(FLEXCAN_MB_CNT_CODE(0x4),
+ &regs->cantxfg[i].can_ctrl);
}
/* acceptance mask/acceptance code (accept everything) */
- writel(0x0, &regs->rxgmask);
- writel(0x0, &regs->rx14mask);
- writel(0x0, &regs->rx15mask);
+ flexcan_write(0x0, &regs->rxgmask);
+ flexcan_write(0x0, &regs->rx14mask);
+ flexcan_write(0x0, &regs->rx15mask);
flexcan_transceiver_switch(priv, 1);
/* synchronize with the can bus */
- reg_mcr = readl(&regs->mcr);
+ reg_mcr = flexcan_read(&regs->mcr);
reg_mcr &= ~FLEXCAN_MCR_HALT;
- writel(reg_mcr, &regs->mcr);
+ flexcan_write(reg_mcr, &regs->mcr);
priv->can.state = CAN_STATE_ERROR_ACTIVE;
/* enable FIFO interrupts */
- writel(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
+ flexcan_write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
/* print chip status */
dev_dbg(dev->dev.parent, "%s: reading mcr=0x%08x ctrl=0x%08x\n",
- __func__, readl(&regs->mcr), readl(&regs->ctrl));
+ __func__, flexcan_read(&regs->mcr), flexcan_read(&regs->ctrl));
return 0;
@@ -759,12 +784,12 @@ static void flexcan_chip_stop(struct net_device *dev)
u32 reg;
/* Disable all interrupts */
- writel(0, &regs->imask1);
+ flexcan_write(0, &regs->imask1);
/* Disable + halt module */
- reg = readl(&regs->mcr);
+ reg = flexcan_read(&regs->mcr);
reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT;
- writel(reg, &regs->mcr);
+ flexcan_write(reg, &regs->mcr);
flexcan_transceiver_switch(priv, 0);
priv->can.state = CAN_STATE_STOPPED;
@@ -856,24 +881,24 @@ static int __devinit register_flexcandev(struct net_device *dev)
/* select "bus clock", chip must be disabled */
flexcan_chip_disable(priv);
- reg = readl(&regs->ctrl);
+ reg = flexcan_read(&regs->ctrl);
reg |= FLEXCAN_CTRL_CLK_SRC;
- writel(reg, &regs->ctrl);
+ flexcan_write(reg, &regs->ctrl);
flexcan_chip_enable(priv);
/* set freeze, halt and activate FIFO, restrict register access */
- reg = readl(&regs->mcr);
+ reg = flexcan_read(&regs->mcr);
reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT |
FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV;
- writel(reg, &regs->mcr);
+ flexcan_write(reg, &regs->mcr);
/*
* Currently we only support newer versions of this core
* featuring a RX FIFO. Older cores found on some Coldfire
* derivates are not yet supported.
*/
- reg = readl(&regs->mcr);
+ reg = flexcan_read(&regs->mcr);
if (!(reg & FLEXCAN_MCR_FEN)) {
dev_err(dev->dev.parent,
"Could not enable RX FIFO, unsupported core\n");
@@ -901,16 +926,29 @@ static int __devinit flexcan_probe(struct platform_device *pdev)
struct net_device *dev;
struct flexcan_priv *priv;
struct resource *mem;
- struct clk *clk;
+ struct clk *clk = NULL;
void __iomem *base;
resource_size_t mem_size;
int err, irq;
+ u32 clock_freq = 0;
+
+ if (pdev->dev.of_node) {
+ const u32 *clock_freq_p;
- clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "no clock defined\n");
- err = PTR_ERR(clk);
- goto failed_clock;
+ clock_freq_p = of_get_property(pdev->dev.of_node,
+ "clock-frequency", NULL);
+ if (clock_freq_p)
+ clock_freq = *clock_freq_p;
+ }
+
+ if (!clock_freq) {
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "no clock defined\n");
+ err = PTR_ERR(clk);
+ goto failed_clock;
+ }
+ clock_freq = clk_get_rate(clk);
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -943,7 +981,7 @@ static int __devinit flexcan_probe(struct platform_device *pdev)
dev->flags |= IFF_ECHO; /* we support local echo in hardware */
priv = netdev_priv(dev);
- priv->can.clock.freq = clk_get_rate(clk);
+ priv->can.clock.freq = clock_freq;
priv->can.bittiming_const = &flexcan_bittiming_const;
priv->can.do_set_mode = flexcan_set_mode;
priv->can.do_get_berr_counter = flexcan_get_berr_counter;
@@ -978,7 +1016,8 @@ static int __devinit flexcan_probe(struct platform_device *pdev)
failed_map:
release_mem_region(mem->start, mem_size);
failed_get:
- clk_put(clk);
+ if (clk)
+ clk_put(clk);
failed_clock:
return err;
}
@@ -996,15 +1035,27 @@ static int __devexit flexcan_remove(struct platform_device *pdev)
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, resource_size(mem));
- clk_put(priv->clk);
+ if (priv->clk)
+ clk_put(priv->clk);
free_candev(dev);
return 0;
}
+static struct of_device_id flexcan_of_match[] = {
+ {
+ .compatible = "fsl,p1010-flexcan",
+ },
+ {},
+};
+
static struct platform_driver flexcan_driver = {
- .driver.name = DRV_NAME,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = flexcan_of_match,
+ },
.probe = flexcan_probe,
.remove = __devexit_p(flexcan_remove),
};
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
index f1942cab35f6..32778d56d330 100644
--- a/drivers/net/can/janz-ican3.c
+++ b/drivers/net/can/janz-ican3.c
@@ -22,6 +22,7 @@
#include <linux/can/error.h>
#include <linux/mfd/janz.h>
+#include <asm/io.h>
/* the DPM has 64k of memory, organized into 256x 256 byte pages */
#define DPM_NUM_PAGES 256
diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c
index 92feac68b66e..ec4a3119e2c9 100644
--- a/drivers/net/can/mscan/mscan.c
+++ b/drivers/net/can/mscan/mscan.c
@@ -62,7 +62,7 @@ static enum can_state state_map[] = {
static int mscan_set_mode(struct net_device *dev, u8 mode)
{
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
int ret = 0;
int i;
u8 canctl1;
@@ -138,7 +138,7 @@ static int mscan_set_mode(struct net_device *dev, u8 mode)
static int mscan_start(struct net_device *dev)
{
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
u8 canrflg;
int err;
@@ -178,7 +178,7 @@ static int mscan_restart(struct net_device *dev)
struct mscan_priv *priv = netdev_priv(dev);
if (priv->type == MSCAN_TYPE_MPC5121) {
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
priv->can.state = CAN_STATE_ERROR_ACTIVE;
WARN(!(in_8(&regs->canmisc) & MSCAN_BOHOLD),
@@ -199,7 +199,7 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct can_frame *frame = (struct can_frame *)skb->data;
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
int i, rtr, buf_id;
u32 can_id;
@@ -261,11 +261,13 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
void __iomem *data = &regs->tx.dsr1_0;
u16 *payload = (u16 *)frame->data;
- /* It is safe to write into dsr[dlc+1] */
- for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ for (i = 0; i < frame->can_dlc / 2; i++) {
out_be16(data, *payload++);
data += 2 + _MSCAN_RESERVED_DSR_SIZE;
}
+ /* write remaining byte if necessary */
+ if (frame->can_dlc & 1)
+ out_8(data, frame->data[frame->can_dlc - 1]);
}
out_8(&regs->tx.dlr, frame->can_dlc);
@@ -305,7 +307,7 @@ static enum can_state check_set_state(struct net_device *dev, u8 canrflg)
static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame)
{
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
u32 can_id;
int i;
@@ -330,10 +332,13 @@ static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame)
void __iomem *data = &regs->rx.dsr1_0;
u16 *payload = (u16 *)frame->data;
- for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ for (i = 0; i < frame->can_dlc / 2; i++) {
*payload++ = in_be16(data);
data += 2 + _MSCAN_RESERVED_DSR_SIZE;
}
+ /* read remaining byte if necessary */
+ if (frame->can_dlc & 1)
+ frame->data[frame->can_dlc - 1] = in_8(data);
}
out_8(&regs->canrflg, MSCAN_RXF);
@@ -343,7 +348,7 @@ static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame,
u8 canrflg)
{
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
struct net_device_stats *stats = &dev->stats;
enum can_state old_state;
@@ -406,7 +411,7 @@ static int mscan_rx_poll(struct napi_struct *napi, int quota)
{
struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
struct net_device *dev = napi->dev;
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
struct net_device_stats *stats = &dev->stats;
int npackets = 0;
int ret = 1;
@@ -453,7 +458,7 @@ static irqreturn_t mscan_isr(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *)dev_id;
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
struct net_device_stats *stats = &dev->stats;
u8 cantier, cantflg, canrflg;
irqreturn_t ret = IRQ_NONE;
@@ -537,7 +542,7 @@ static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode)
static int mscan_do_set_bittiming(struct net_device *dev)
{
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
struct can_bittiming *bt = &priv->can.bittiming;
u8 btr0, btr1;
@@ -559,7 +564,7 @@ static int mscan_open(struct net_device *dev)
{
int ret;
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
/* common open */
ret = open_candev(dev);
@@ -598,7 +603,7 @@ exit_napi_disable:
static int mscan_close(struct net_device *dev)
{
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
netif_stop_queue(dev);
napi_disable(&priv->napi);
@@ -622,7 +627,7 @@ static const struct net_device_ops mscan_netdev_ops = {
int register_mscandev(struct net_device *dev, int mscan_clksrc)
{
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
u8 ctl1;
ctl1 = in_8(&regs->canctl1);
@@ -659,7 +664,7 @@ int register_mscandev(struct net_device *dev, int mscan_clksrc)
void unregister_mscandev(struct net_device *dev)
{
struct mscan_priv *priv = netdev_priv(dev);
- struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct mscan_regs __iomem *regs = priv->reg_base;
mscan_set_mode(dev, MSCAN_INIT_MODE);
clrbits8(&regs->canctl1, MSCAN_CANE);
unregister_candev(dev);
diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig
index 6fdc031daaae..fe9e64d476eb 100644
--- a/drivers/net/can/sja1000/Kconfig
+++ b/drivers/net/can/sja1000/Kconfig
@@ -29,6 +29,13 @@ config CAN_SJA1000_OF_PLATFORM
OpenFirmware bindings, e.g. if you have a PowerPC based system
you may want to enable this option.
+config CAN_EMS_PCMCIA
+ tristate "EMS CPC-CARD Card"
+ depends on PCMCIA
+ ---help---
+ This driver is for the one or two channel CPC-CARD cards from
+ EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+
config CAN_EMS_PCI
tristate "EMS CPC-PCI, CPC-PCIe and CPC-104P Card"
depends on PCI
@@ -37,6 +44,13 @@ config CAN_EMS_PCI
CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche
(http://www.ems-wuensche.de).
+config CAN_PEAK_PCI
+ tristate "PEAK PCAN PCI/PCIe Cards"
+ depends on PCI
+ ---help---
+ This driver is for the PCAN PCI/PCIe cards (1, 2, 3 or 4 channels)
+ from PEAK Systems (http://www.peak-system.com).
+
config CAN_KVASER_PCI
tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards"
depends on PCI
diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile
index 2c591eb321c7..0604f240c8b1 100644
--- a/drivers/net/can/sja1000/Makefile
+++ b/drivers/net/can/sja1000/Makefile
@@ -6,8 +6,10 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000.o
obj-$(CONFIG_CAN_SJA1000_ISA) += sja1000_isa.o
obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o
obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o
+obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o
obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o
obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o
+obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o
obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o
obj-$(CONFIG_CAN_TSCAN1) += tscan1.o
diff --git a/drivers/net/can/sja1000/ems_pcmcia.c b/drivers/net/can/sja1000/ems_pcmcia.c
new file mode 100644
index 000000000000..075a5457a190
--- /dev/null
+++ b/drivers/net/can/sja1000/ems_pcmcia.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2008 Sebastian Haas (initial chardev implementation)
+ * Copyright (C) 2010 Markus Plessing <plessing@ems-wuensche.com>
+ * Rework for mainline by Oliver Hartkopp <socketcan@hartkopp.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include "sja1000.h"
+
+#define DRV_NAME "ems_pcmcia"
+
+MODULE_AUTHOR("Markus Plessing <plessing@ems-wuensche.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-CARD cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-CARD CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define EMS_PCMCIA_MAX_CHAN 2
+
+struct ems_pcmcia_card {
+ int channels;
+ struct pcmcia_device *pcmcia_dev;
+ struct net_device *net_dev[EMS_PCMCIA_MAX_CHAN];
+ void __iomem *base_addr;
+};
+
+#define EMS_PCMCIA_CAN_CLOCK (16000000 / 2)
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define EMS_PCMCIA_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 7
+ * (meaning direct oscillator output) because the second SJA1000 chip
+ * is driven by the first one CLKOUT output.
+ */
+#define EMS_PCMCIA_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+#define EMS_PCMCIA_MEM_SIZE 4096 /* Size of the remapped io-memory */
+#define EMS_PCMCIA_CAN_BASE_OFFSET 0x100 /* Offset where controllers starts */
+#define EMS_PCMCIA_CAN_CTRL_SIZE 0x80 /* Memory size for each controller */
+
+#define EMS_CMD_RESET 0x00 /* Perform a reset of the card */
+#define EMS_CMD_MAP 0x03 /* Map CAN controllers into card' memory */
+#define EMS_CMD_UMAP 0x02 /* Unmap CAN controllers from card' memory */
+
+static struct pcmcia_device_id ems_pcmcia_tbl[] = {
+ PCMCIA_DEVICE_PROD_ID123("EMS_T_W", "CPC-Card", "V2.0", 0xeab1ea23,
+ 0xa338573f, 0xe4575800),
+ PCMCIA_DEVICE_NULL,
+};
+
+MODULE_DEVICE_TABLE(pcmcia, ems_pcmcia_tbl);
+
+static u8 ems_pcmcia_read_reg(const struct sja1000_priv *priv, int port)
+{
+ return readb(priv->reg_base + port);
+}
+
+static void ems_pcmcia_write_reg(const struct sja1000_priv *priv, int port,
+ u8 val)
+{
+ writeb(val, priv->reg_base + port);
+}
+
+static irqreturn_t ems_pcmcia_interrupt(int irq, void *dev_id)
+{
+ struct ems_pcmcia_card *card = dev_id;
+ struct net_device *dev;
+ irqreturn_t retval = IRQ_NONE;
+ int i, again;
+
+ /* Card not present */
+ if (readw(card->base_addr) != 0xAA55)
+ return IRQ_HANDLED;
+
+ do {
+ again = 0;
+
+ /* Check interrupt for each channel */
+ for (i = 0; i < card->channels; i++) {
+ dev = card->net_dev[i];
+ if (!dev)
+ continue;
+
+ if (sja1000_interrupt(irq, dev) == IRQ_HANDLED)
+ again = 1;
+ }
+ /* At least one channel handled the interrupt */
+ if (again)
+ retval = IRQ_HANDLED;
+
+ } while (again);
+
+ return retval;
+}
+
+/*
+ * Check if a CAN controller is present at the specified location
+ * by trying to set 'em into the PeliCAN mode
+ */
+static inline int ems_pcmcia_check_chan(struct sja1000_priv *priv)
+{
+ /* Make sure SJA1000 is in reset mode */
+ ems_pcmcia_write_reg(priv, REG_MOD, 1);
+ ems_pcmcia_write_reg(priv, REG_CDR, CDR_PELICAN);
+
+ /* read reset-values */
+ if (ems_pcmcia_read_reg(priv, REG_CDR) == CDR_PELICAN)
+ return 1;
+
+ return 0;
+}
+
+static void ems_pcmcia_del_card(struct pcmcia_device *pdev)
+{
+ struct ems_pcmcia_card *card = pdev->priv;
+ struct net_device *dev;
+ int i;
+
+ free_irq(pdev->irq, card);
+
+ for (i = 0; i < card->channels; i++) {
+ dev = card->net_dev[i];
+ if (!dev)
+ continue;
+
+ printk(KERN_INFO "%s: removing %s on channel #%d\n",
+ DRV_NAME, dev->name, i);
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+ }
+
+ writeb(EMS_CMD_UMAP, card->base_addr);
+ iounmap(card->base_addr);
+ kfree(card);
+
+ pdev->priv = NULL;
+}
+
+/*
+ * Probe PCI device for EMS CAN signature and register each available
+ * CAN channel to SJA1000 Socket-CAN subsystem.
+ */
+static int __devinit ems_pcmcia_add_card(struct pcmcia_device *pdev,
+ unsigned long base)
+{
+ struct sja1000_priv *priv;
+ struct net_device *dev;
+ struct ems_pcmcia_card *card;
+ int err, i;
+
+ /* Allocating card structures to hold addresses, ... */
+ card = kzalloc(sizeof(struct ems_pcmcia_card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ pdev->priv = card;
+ card->channels = 0;
+
+ card->base_addr = ioremap(base, EMS_PCMCIA_MEM_SIZE);
+ if (!card->base_addr) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ /* Check for unique EMS CAN signature */
+ if (readw(card->base_addr) != 0xAA55) {
+ err = -ENODEV;
+ goto failure_cleanup;
+ }
+
+ /* Request board reset */
+ writeb(EMS_CMD_RESET, card->base_addr);
+
+ /* Make sure CAN controllers are mapped into card's memory space */
+ writeb(EMS_CMD_MAP, card->base_addr);
+
+ /* Detect available channels */
+ for (i = 0; i < EMS_PCMCIA_MAX_CHAN; i++) {
+ dev = alloc_sja1000dev(0);
+ if (!dev) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ card->net_dev[i] = dev;
+ priv = netdev_priv(dev);
+ priv->priv = card;
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ priv->irq_flags = IRQF_SHARED;
+ dev->irq = pdev->irq;
+ priv->reg_base = card->base_addr + EMS_PCMCIA_CAN_BASE_OFFSET +
+ (i * EMS_PCMCIA_CAN_CTRL_SIZE);
+
+ /* Check if channel is present */
+ if (ems_pcmcia_check_chan(priv)) {
+ priv->read_reg = ems_pcmcia_read_reg;
+ priv->write_reg = ems_pcmcia_write_reg;
+ priv->can.clock.freq = EMS_PCMCIA_CAN_CLOCK;
+ priv->ocr = EMS_PCMCIA_OCR;
+ priv->cdr = EMS_PCMCIA_CDR;
+ priv->flags |= SJA1000_CUSTOM_IRQ_HANDLER;
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ free_sja1000dev(dev);
+ goto failure_cleanup;
+ }
+
+ card->channels++;
+
+ printk(KERN_INFO "%s: registered %s on channel "
+ "#%d at 0x%p, irq %d\n", DRV_NAME, dev->name,
+ i, priv->reg_base, dev->irq);
+ } else
+ free_sja1000dev(dev);
+ }
+
+ err = request_irq(dev->irq, &ems_pcmcia_interrupt, IRQF_SHARED,
+ DRV_NAME, card);
+ if (!err)
+ return 0;
+
+failure_cleanup:
+ ems_pcmcia_del_card(pdev);
+ return err;
+}
+
+/*
+ * Setup PCMCIA socket and probe for EMS CPC-CARD
+ */
+static int __devinit ems_pcmcia_probe(struct pcmcia_device *dev)
+{
+ int csval;
+
+ /* General socket configuration */
+ dev->config_flags |= CONF_ENABLE_IRQ;
+ dev->config_index = 1;
+ dev->config_regs = PRESENT_OPTION;
+
+ /* The io structure describes IO port mapping */
+ dev->resource[0]->end = 16;
+ dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ dev->resource[1]->end = 16;
+ dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
+ dev->io_lines = 5;
+
+ /* Allocate a memory window */
+ dev->resource[2]->flags =
+ (WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE);
+ dev->resource[2]->start = dev->resource[2]->end = 0;
+
+ csval = pcmcia_request_window(dev, dev->resource[2], 0);
+ if (csval) {
+ dev_err(&dev->dev, "pcmcia_request_window failed (err=%d)\n",
+ csval);
+ return 0;
+ }
+
+ csval = pcmcia_map_mem_page(dev, dev->resource[2], dev->config_base);
+ if (csval) {
+ dev_err(&dev->dev, "pcmcia_map_mem_page failed (err=%d)\n",
+ csval);
+ return 0;
+ }
+
+ csval = pcmcia_enable_device(dev);
+ if (csval) {
+ dev_err(&dev->dev, "pcmcia_enable_device failed (err=%d)\n",
+ csval);
+ return 0;
+ }
+
+ ems_pcmcia_add_card(dev, dev->resource[2]->start);
+ return 0;
+}
+
+/*
+ * Release claimed resources
+ */
+static void ems_pcmcia_remove(struct pcmcia_device *dev)
+{
+ ems_pcmcia_del_card(dev);
+ pcmcia_disable_device(dev);
+}
+
+static struct pcmcia_driver ems_pcmcia_driver = {
+ .name = DRV_NAME,
+ .probe = ems_pcmcia_probe,
+ .remove = ems_pcmcia_remove,
+ .id_table = ems_pcmcia_tbl,
+};
+
+static int __init ems_pcmcia_init(void)
+{
+ return pcmcia_register_driver(&ems_pcmcia_driver);
+}
+module_init(ems_pcmcia_init);
+
+static void __exit ems_pcmcia_exit(void)
+{
+ pcmcia_unregister_driver(&ems_pcmcia_driver);
+}
+module_exit(ems_pcmcia_exit);
diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c
new file mode 100644
index 000000000000..905bce0b3a43
--- /dev/null
+++ b/drivers/net/can/sja1000/peak_pci.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Derived from the PCAN project file driver/src/pcan_pci.c:
+ *
+ * Copyright (C) 2001-2006 PEAK System-Technik GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+#include "sja1000.h"
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI/PCIe cards");
+MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI/PCIe CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define DRV_NAME "peak_pci"
+
+struct peak_pci_chan {
+ void __iomem *cfg_base; /* Common for all channels */
+ struct net_device *next_dev; /* Chain of network devices */
+ u16 icr_mask; /* Interrupt mask for fast ack */
+};
+
+#define PEAK_PCI_CAN_CLOCK (16000000 / 2)
+
+#define PEAK_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+#define PEAK_PCI_OCR OCR_TX0_PUSHPULL
+
+/*
+ * Important PITA registers
+ */
+#define PITA_ICR 0x00 /* Interrupt control register */
+#define PITA_GPIOICR 0x18 /* GPIO interface control register */
+#define PITA_MISC 0x1C /* Miscellaneous register */
+
+#define PEAK_PCI_CFG_SIZE 0x1000 /* Size of the config PCI bar */
+#define PEAK_PCI_CHAN_SIZE 0x0400 /* Size used by the channel */
+
+#define PEAK_PCI_VENDOR_ID 0x001C /* The PCI device and vendor IDs */
+#define PEAK_PCI_DEVICE_ID 0x0001 /* for PCI/PCIe slot cards */
+
+static const u16 peak_pci_icr_masks[] = {0x02, 0x01, 0x40, 0x80};
+
+static DEFINE_PCI_DEVICE_TABLE(peak_pci_tbl) = {
+ {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, peak_pci_tbl);
+
+static u8 peak_pci_read_reg(const struct sja1000_priv *priv, int port)
+{
+ return readb(priv->reg_base + (port << 2));
+}
+
+static void peak_pci_write_reg(const struct sja1000_priv *priv,
+ int port, u8 val)
+{
+ writeb(val, priv->reg_base + (port << 2));
+}
+
+static void peak_pci_post_irq(const struct sja1000_priv *priv)
+{
+ struct peak_pci_chan *chan = priv->priv;
+ u16 icr;
+
+ /* Select and clear in PITA stored interrupt */
+ icr = readw(chan->cfg_base + PITA_ICR);
+ if (icr & chan->icr_mask)
+ writew(chan->icr_mask, chan->cfg_base + PITA_ICR);
+}
+
+static int __devinit peak_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct sja1000_priv *priv;
+ struct peak_pci_chan *chan;
+ struct net_device *dev, *dev0 = NULL;
+ void __iomem *cfg_base, *reg_base;
+ u16 sub_sys_id, icr;
+ int i, err, channels;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure_disable_pci;
+
+ err = pci_read_config_word(pdev, 0x2e, &sub_sys_id);
+ if (err)
+ goto failure_release_regions;
+
+ dev_dbg(&pdev->dev, "probing device %04x:%04x:%04x\n",
+ pdev->vendor, pdev->device, sub_sys_id);
+
+ err = pci_write_config_word(pdev, 0x44, 0);
+ if (err)
+ goto failure_release_regions;
+
+ if (sub_sys_id >= 12)
+ channels = 4;
+ else if (sub_sys_id >= 10)
+ channels = 3;
+ else if (sub_sys_id >= 4)
+ channels = 2;
+ else
+ channels = 1;
+
+ cfg_base = pci_iomap(pdev, 0, PEAK_PCI_CFG_SIZE);
+ if (!cfg_base) {
+ dev_err(&pdev->dev, "failed to map PCI resource #0\n");
+ goto failure_release_regions;
+ }
+
+ reg_base = pci_iomap(pdev, 1, PEAK_PCI_CHAN_SIZE * channels);
+ if (!reg_base) {
+ dev_err(&pdev->dev, "failed to map PCI resource #1\n");
+ goto failure_unmap_cfg_base;
+ }
+
+ /* Set GPIO control register */
+ writew(0x0005, cfg_base + PITA_GPIOICR + 2);
+ /* Enable all channels of this card */
+ writeb(0x00, cfg_base + PITA_GPIOICR);
+ /* Toggle reset */
+ writeb(0x05, cfg_base + PITA_MISC + 3);
+ mdelay(5);
+ /* Leave parport mux mode */
+ writeb(0x04, cfg_base + PITA_MISC + 3);
+
+ icr = readw(cfg_base + PITA_ICR + 2);
+
+ for (i = 0; i < channels; i++) {
+ dev = alloc_sja1000dev(sizeof(struct peak_pci_chan));
+ if (!dev) {
+ err = -ENOMEM;
+ goto failure_remove_channels;
+ }
+
+ priv = netdev_priv(dev);
+ chan = priv->priv;
+
+ chan->cfg_base = cfg_base;
+ priv->reg_base = reg_base + i * PEAK_PCI_CHAN_SIZE;
+
+ priv->read_reg = peak_pci_read_reg;
+ priv->write_reg = peak_pci_write_reg;
+ priv->post_irq = peak_pci_post_irq;
+
+ priv->can.clock.freq = PEAK_PCI_CAN_CLOCK;
+ priv->ocr = PEAK_PCI_OCR;
+ priv->cdr = PEAK_PCI_CDR;
+ /* Neither a slave nor a single device distributes the clock */
+ if (channels == 1 || i > 0)
+ priv->cdr |= CDR_CLK_OFF;
+
+ /* Setup interrupt handling */
+ priv->irq_flags = IRQF_SHARED;
+ dev->irq = pdev->irq;
+
+ chan->icr_mask = peak_pci_icr_masks[i];
+ icr |= chan->icr_mask;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register device\n");
+ free_sja1000dev(dev);
+ goto failure_remove_channels;
+ }
+
+ /* Create chain of SJA1000 devices */
+ if (i == 0)
+ dev0 = dev;
+ else
+ chan->next_dev = dev;
+
+ dev_info(&pdev->dev,
+ "%s at reg_base=0x%p cfg_base=0x%p irq=%d\n",
+ dev->name, priv->reg_base, chan->cfg_base, dev->irq);
+ }
+
+ pci_set_drvdata(pdev, dev0);
+
+ /* Enable interrupts */
+ writew(icr, cfg_base + PITA_ICR + 2);
+
+ return 0;
+
+failure_remove_channels:
+ /* Disable interrupts */
+ writew(0x0, cfg_base + PITA_ICR + 2);
+
+ for (dev = dev0; dev; dev = chan->next_dev) {
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+ priv = netdev_priv(dev);
+ chan = priv->priv;
+ dev = chan->next_dev;
+ }
+
+ pci_iounmap(pdev, reg_base);
+
+failure_unmap_cfg_base:
+ pci_iounmap(pdev, cfg_base);
+
+failure_release_regions:
+ pci_release_regions(pdev);
+
+failure_disable_pci:
+ pci_disable_device(pdev);
+
+ return err;
+}
+
+static void __devexit peak_pci_remove(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev); /* First device */
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci_chan *chan = priv->priv;
+ void __iomem *cfg_base = chan->cfg_base;
+ void __iomem *reg_base = priv->reg_base;
+
+ /* Disable interrupts */
+ writew(0x0, cfg_base + PITA_ICR + 2);
+
+ /* Loop over all registered devices */
+ while (1) {
+ dev_info(&pdev->dev, "removing device %s\n", dev->name);
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+ dev = chan->next_dev;
+ if (!dev)
+ break;
+ priv = netdev_priv(dev);
+ chan = priv->priv;
+ }
+
+ pci_iounmap(pdev, reg_base);
+ pci_iounmap(pdev, cfg_base);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+
+ pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver peak_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = peak_pci_tbl,
+ .probe = peak_pci_probe,
+ .remove = __devexit_p(peak_pci_remove),
+};
+
+static int __init peak_pci_init(void)
+{
+ return pci_register_driver(&peak_pci_driver);
+}
+module_init(peak_pci_init);
+
+static void __exit peak_pci_exit(void)
+{
+ pci_unregister_driver(&peak_pci_driver);
+}
+module_exit(peak_pci_exit);
diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c
index 231385b8e08f..c7f3d4ea1167 100644
--- a/drivers/net/can/sja1000/plx_pci.c
+++ b/drivers/net/can/sja1000/plx_pci.c
@@ -408,7 +408,7 @@ static void plx_pci_del_card(struct pci_dev *pdev)
struct sja1000_priv *priv;
int i = 0;
- for (i = 0; i < card->channels; i++) {
+ for (i = 0; i < PLX_PCI_MAX_CHAN; i++) {
dev = card->net_dev[i];
if (!dev)
continue;
@@ -536,7 +536,6 @@ static int __devinit plx_pci_add_card(struct pci_dev *pdev,
if (err) {
dev_err(&pdev->dev, "Registering device failed "
"(err=%d)\n", err);
- free_sja1000dev(dev);
goto failure_cleanup;
}
@@ -549,6 +548,7 @@ static int __devinit plx_pci_add_card(struct pci_dev *pdev,
dev_err(&pdev->dev, "Channel #%d not detected\n",
i + 1);
free_sja1000dev(dev);
+ card->net_dev[i] = NULL;
}
}
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c
index f501bba1fc6f..04a3f1b756a8 100644
--- a/drivers/net/can/sja1000/sja1000.c
+++ b/drivers/net/can/sja1000/sja1000.c
@@ -40,8 +40,6 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
- * Send feedback to <socketcan-users@lists.berlios.de>
- *
*/
#include <linux/module.h>
diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h
index de8e778f6832..23fff06875f5 100644
--- a/drivers/net/can/sja1000/sja1000.h
+++ b/drivers/net/can/sja1000/sja1000.h
@@ -40,13 +40,12 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
- * Send feedback to <socketcan-users@lists.berlios.de>
- *
*/
#ifndef SJA1000_DEV_H
#define SJA1000_DEV_H
+#include <linux/irqreturn.h>
#include <linux/can/dev.h>
#include <linux/can/platform/sja1000.h>
diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c
index 9793df6e3455..c3dd9d09be57 100644
--- a/drivers/net/can/sja1000/sja1000_of_platform.c
+++ b/drivers/net/can/sja1000/sja1000_of_platform.c
@@ -29,7 +29,7 @@
* nxp,external-clock-frequency = <16000000>;
* };
*
- * See "Documentation/powerpc/dts-bindings/can/sja1000.txt" for further
+ * See "Documentation/devicetree/bindings/net/can/sja1000.txt" for further
* information.
*/
@@ -38,6 +38,7 @@
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
+#include <linux/io.h>
#include <linux/can/dev.h>
#include <linux/of_platform.h>
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
index 1b49df6b2470..a979b006f459 100644
--- a/drivers/net/can/slcan.c
+++ b/drivers/net/can/slcan.c
@@ -35,8 +35,6 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
- * Send feedback to <socketcan-users@lists.berlios.de>
- *
*/
#include <linux/module.h>
@@ -56,6 +54,7 @@
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/can.h>
static __initdata const char banner[] =
@@ -95,10 +94,6 @@ struct slcan {
unsigned long flags; /* Flag values/ mode etc */
#define SLF_INUSE 0 /* Channel in use */
#define SLF_ERROR 1 /* Parity, etc. error */
-
- unsigned char leased;
- dev_t line;
- pid_t pid;
};
static struct net_device **slcan_devs;
@@ -142,21 +137,6 @@ static struct net_device **slcan_devs;
* STANDARD SLCAN DECAPSULATION *
************************************************************************/
-static int asc2nibble(char c)
-{
-
- if ((c >= '0') && (c <= '9'))
- return c - '0';
-
- if ((c >= 'A') && (c <= 'F'))
- return c - 'A' + 10;
-
- if ((c >= 'a') && (c <= 'f'))
- return c - 'a' + 10;
-
- return 16; /* error */
-}
-
/* Send one completely decapsulated can_frame to the network layer */
static void slc_bump(struct slcan *sl)
{
@@ -195,18 +175,16 @@ static void slc_bump(struct slcan *sl)
*(u64 *) (&cf.data) = 0; /* clear payload */
for (i = 0, dlc_pos++; i < cf.can_dlc; i++) {
-
- tmp = asc2nibble(sl->rbuff[dlc_pos++]);
- if (tmp > 0x0F)
+ tmp = hex_to_bin(sl->rbuff[dlc_pos++]);
+ if (tmp < 0)
return;
cf.data[i] = (tmp << 4);
- tmp = asc2nibble(sl->rbuff[dlc_pos++]);
- if (tmp > 0x0F)
+ tmp = hex_to_bin(sl->rbuff[dlc_pos++]);
+ if (tmp < 0)
return;
cf.data[i] |= tmp;
}
-
skb = dev_alloc_skb(sizeof(struct can_frame));
if (!skb)
return;
@@ -217,7 +195,7 @@ static void slc_bump(struct slcan *sl)
skb->ip_summed = CHECKSUM_UNNECESSARY;
memcpy(skb_put(skb, sizeof(struct can_frame)),
&cf, sizeof(struct can_frame));
- netif_rx(skb);
+ netif_rx_ni(skb);
sl->dev->stats.rx_packets++;
sl->dev->stats.rx_bytes += cf.can_dlc;
@@ -462,7 +440,7 @@ static void slc_sync(void)
break;
sl = netdev_priv(dev);
- if (sl->tty || sl->leased)
+ if (sl->tty)
continue;
if (dev->flags & IFF_UP)
dev_close(dev);
@@ -473,12 +451,10 @@ static void slc_sync(void)
static struct slcan *slc_alloc(dev_t line)
{
int i;
+ char name[IFNAMSIZ];
struct net_device *dev = NULL;
struct slcan *sl;
- if (slcan_devs == NULL)
- return NULL; /* Master array missing ! */
-
for (i = 0; i < maxdev; i++) {
dev = slcan_devs[i];
if (dev == NULL)
@@ -490,25 +466,12 @@ static struct slcan *slc_alloc(dev_t line)
if (i >= maxdev)
return NULL;
- if (dev) {
- sl = netdev_priv(dev);
- if (test_bit(SLF_INUSE, &sl->flags)) {
- unregister_netdevice(dev);
- dev = NULL;
- slcan_devs[i] = NULL;
- }
- }
-
- if (!dev) {
- char name[IFNAMSIZ];
- sprintf(name, "slcan%d", i);
-
- dev = alloc_netdev(sizeof(*sl), name, slc_setup);
- if (!dev)
- return NULL;
- dev->base_addr = i;
- }
+ sprintf(name, "slcan%d", i);
+ dev = alloc_netdev(sizeof(*sl), name, slc_setup);
+ if (!dev)
+ return NULL;
+ dev->base_addr = i;
sl = netdev_priv(dev);
/* Initialize channel control data */
@@ -565,8 +528,6 @@ static int slcan_open(struct tty_struct *tty)
sl->tty = tty;
tty->disc_data = sl;
- sl->line = tty_devnum(tty);
- sl->pid = current->pid;
if (!test_bit(SLF_INUSE, &sl->flags)) {
/* Perform the low-level SLCAN initialization. */
@@ -617,8 +578,6 @@ static void slcan_close(struct tty_struct *tty)
tty->disc_data = NULL;
sl->tty = NULL;
- if (!sl->leased)
- sl->line = 0;
/* Flush network side */
unregister_netdev(sl->dev);
diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c
index b520784fb197..310596175676 100644
--- a/drivers/net/can/softing/softing_fw.c
+++ b/drivers/net/can/softing/softing_fw.c
@@ -20,6 +20,7 @@
#include <linux/firmware.h>
#include <linux/sched.h>
#include <asm/div64.h>
+#include <asm/io.h>
#include "softing.h"
diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c
index 60a49e5a2a53..09a8b86cf1ac 100644
--- a/drivers/net/can/softing/softing_main.c
+++ b/drivers/net/can/softing/softing_main.c
@@ -17,10 +17,10 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <asm/io.h>
#include "softing.h"
@@ -799,7 +799,7 @@ static __devinit int softing_pdev_probe(struct platform_device *pdev)
if (!pres)
goto platform_resource_failed;
card->dpram_phys = pres->start;
- card->dpram_size = pres->end - pres->start + 1;
+ card->dpram_size = resource_size(pres);
card->dpram = ioremap_nocache(card->dpram_phys, card->dpram_size);
if (!card->dpram) {
dev_alert(&card->pdev->dev, "dpram ioremap failed\n");
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index f7bbde9eb2cb..2adc294f512a 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -46,6 +46,7 @@
#include <linux/skbuff.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/io.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
@@ -503,9 +504,9 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev)
spin_unlock_irqrestore(&priv->mbx_lock, flags);
/* Prepare mailbox for transmission */
+ data = cf->can_dlc | (get_tx_head_prio(priv) << 8);
if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */
data |= HECC_CANMCF_RTR;
- data |= get_tx_head_prio(priv) << 8;
hecc_write_mbx(priv, mbxno, HECC_CANMCF, data);
if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */
@@ -923,6 +924,7 @@ static int ti_hecc_probe(struct platform_device *pdev)
priv->can.do_get_state = ti_hecc_get_state;
priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ spin_lock_init(&priv->mbx_lock);
ndev->irq = irq->start;
ndev->flags |= IFF_ECHO;
platform_set_drvdata(pdev, ndev);
diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c
index a30b8f480f61..f93e2d6fc88c 100644
--- a/drivers/net/can/vcan.c
+++ b/drivers/net/can/vcan.c
@@ -37,8 +37,6 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
- * Send feedback to <socketcan-users@lists.berlios.de>
- *
*/
#include <linux/module.h>
diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c
index e66aceb57cef..7cb2785e209d 100644
--- a/drivers/net/cris/eth_v10.c
+++ b/drivers/net/cris/eth_v10.c
@@ -261,7 +261,7 @@ static const struct net_device_ops e100_netdev_ops = {
.ndo_start_xmit = e100_send_packet,
.ndo_tx_timeout = e100_tx_timeout,
.ndo_get_stats = e100_get_stats,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_do_ioctl = e100_ioctl,
.ndo_set_mac_address = e100_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index 39cf9b9bd673..a7c5e8831e8c 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -116,7 +116,7 @@ static const struct net_device_ops dummy_netdev_ops = {
.ndo_init = dummy_dev_init,
.ndo_start_xmit = dummy_xmit,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_set_mac_address = dummy_set_address,
.ndo_get_stats64 = dummy_get_stats64,
};
diff --git a/drivers/net/3c501.c b/drivers/net/ethernet/3com/3c501.c
index 5420f6de27df..68da81d476f3 100644
--- a/drivers/net/3c501.c
+++ b/drivers/net/ethernet/3com/3c501.c
@@ -201,7 +201,7 @@ static const struct net_device_ops el_netdev_ops = {
.ndo_stop = el1_close,
.ndo_start_xmit = el_start_xmit,
.ndo_tx_timeout = el_timeout,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/3c501.h b/drivers/net/ethernet/3com/3c501.h
index 183fd55f03cb..183fd55f03cb 100644
--- a/drivers/net/3c501.h
+++ b/drivers/net/ethernet/3com/3c501.h
diff --git a/drivers/net/3c509.c b/drivers/net/ethernet/3com/3c509.c
index 44b28b2d7003..92053e6fc980 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/ethernet/3com/3c509.c
@@ -545,7 +545,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_stop = el3_close,
.ndo_start_xmit = el3_start_xmit,
.ndo_get_stats = el3_get_stats,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_tx_timeout = el3_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/3c515.c b/drivers/net/ethernet/3com/3c515.c
index d2bb4b254c57..f67a5d3a200c 100644
--- a/drivers/net/3c515.c
+++ b/drivers/net/ethernet/3com/3c515.c
@@ -569,7 +569,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_start_xmit = corkscrew_start_xmit,
.ndo_tx_timeout = corkscrew_timeout,
.ndo_get_stats = corkscrew_get_stats,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c
index 34c5e1cbf65d..9c01bc9235b3 100644
--- a/drivers/net/pcmcia/3c574_cs.c
+++ b/drivers/net/ethernet/3com/3c574_cs.c
@@ -255,7 +255,7 @@ static const struct net_device_ops el3_netdev_ops = {
.ndo_tx_timeout = el3_tx_timeout,
.ndo_get_stats = el3_get_stats,
.ndo_do_ioctl = el3_ioctl,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c
index 4a1a35809807..972f80ecc510 100644
--- a/drivers/net/pcmcia/3c589_cs.c
+++ b/drivers/net/ethernet/3com/3c589_cs.c
@@ -184,7 +184,7 @@ static const struct net_device_ops el3_netdev_ops = {
.ndo_tx_timeout = el3_tx_timeout,
.ndo_set_config = el3_config,
.ndo_get_stats = el3_get_stats,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/3c59x.c b/drivers/net/ethernet/3com/3c59x.c
index 8cc22568ebd3..b42c06baba89 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/ethernet/3com/3c59x.c
@@ -1055,7 +1055,7 @@ static const struct net_device_ops boomrang_netdev_ops = {
#ifdef CONFIG_PCI
.ndo_do_ioctl = vortex_ioctl,
#endif
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
@@ -1073,7 +1073,7 @@ static const struct net_device_ops vortex_netdev_ops = {
#ifdef CONFIG_PCI
.ndo_do_ioctl = vortex_ioctl,
#endif
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
@@ -2179,14 +2179,15 @@ boomerang_start_xmit(struct sk_buff *skb, struct net_device *dev)
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
vp->tx_ring[entry].frag[i+1].addr =
- cpu_to_le32(pci_map_single(VORTEX_PCI(vp),
- (void*)page_address(frag->page) + frag->page_offset,
- frag->size, PCI_DMA_TODEVICE));
+ cpu_to_le32(pci_map_single(
+ VORTEX_PCI(vp),
+ (void *)skb_frag_address(frag),
+ skb_frag_size(frag), PCI_DMA_TODEVICE));
if (i == skb_shinfo(skb)->nr_frags-1)
- vp->tx_ring[entry].frag[i+1].length = cpu_to_le32(frag->size|LAST_FRAG);
+ vp->tx_ring[entry].frag[i+1].length = cpu_to_le32(skb_frag_size(frag)|LAST_FRAG);
else
- vp->tx_ring[entry].frag[i+1].length = cpu_to_le32(frag->size);
+ vp->tx_ring[entry].frag[i+1].length = cpu_to_le32(skb_frag_size(frag));
}
}
#else
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
new file mode 100644
index 000000000000..a8bb30cf512d
--- /dev/null
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -0,0 +1,122 @@
+#
+# 3Com Ethernet device configuration
+#
+
+config NET_VENDOR_3COM
+ bool "3Com devices"
+ default y
+ depends on ISA || EISA || MCA || PCI || PCMCIA
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about 3Com cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_3COM
+
+config EL1
+ tristate "3c501 \"EtherLink\" support"
+ depends on ISA
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Also, consider buying a
+ new card, since the 3c501 is slow, broken, and obsolete: you will
+ have problems. Some people suggest to ping ("man ping") a nearby
+ machine every minute ("man cron") when using this card.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c501.
+
+config EL3
+ tristate "3c509/3c529 (MCA)/3c579 \"EtherLink III\" support"
+ depends on (ISA || EISA || MCA)
+ ---help---
+ If you have a network (Ethernet) card belonging to the 3Com
+ EtherLinkIII series, say Y and read the Ethernet-HOWTO, available
+ from <http://www.tldp.org/docs.html#howto>.
+
+ If your card is not working you may need to use the DOS
+ setup disk to disable Plug & Play mode, and to select the default
+ media type.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c509.
+
+config 3C515
+ tristate "3c515 ISA \"Fast EtherLink\""
+ depends on (ISA || EISA) && ISA_DMA_API
+ ---help---
+ If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet
+ network card, say Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c515.
+
+config PCMCIA_3C574
+ tristate "3Com 3c574 PCMCIA support"
+ depends on PCMCIA
+ ---help---
+ Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA
+ (PC-card) Fast Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called 3c574_cs. If unsure, say N.
+
+config PCMCIA_3C589
+ tristate "3Com 3c589 PCMCIA support"
+ depends on PCMCIA
+ ---help---
+ Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA
+ (PC-card) Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called 3c589_cs. If unsure, say N.
+
+config VORTEX
+ tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support"
+ depends on (PCI || EISA)
+ select NET_CORE
+ select MII
+ ---help---
+ This option enables driver support for a large number of 10Mbps and
+ 10/100Mbps EISA, PCI and PCMCIA 3Com network cards:
+
+ "Vortex" (Fast EtherLink 3c590/3c592/3c595/3c597) EISA and PCI
+ "Boomerang" (EtherLink XL 3c900 or 3c905) PCI
+ "Cyclone" (3c540/3c900/3c905/3c980/3c575/3c656) PCI and Cardbus
+ "Tornado" (3c905) PCI
+ "Hurricane" (3c555/3cSOHO) PCI
+
+ If you have such a card, say Y and read the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>. More
+ specific information is in
+ <file:Documentation/networking/vortex.txt> and in the comments at
+ the beginning of <file:drivers/net/3c59x.c>.
+
+ To compile this support as a module, choose M here.
+
+config TYPHOON
+ tristate "3cr990 series \"Typhoon\" support"
+ depends on PCI
+ select CRC32
+ ---help---
+ This option enables driver support for the 3cr990 series of cards:
+
+ 3C990-TX, 3CR990-TX-95, 3CR990-TX-97, 3CR990-FX-95, 3CR990-FX-97,
+ 3CR990SVR, 3CR990SVR95, 3CR990SVR97, 3CR990-FX-95 Server,
+ 3CR990-FX-97 Server, 3C990B-TX-M, 3C990BSVR
+
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called typhoon.
+
+endif # NET_VENDOR_3COM
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
new file mode 100644
index 000000000000..1e5382a30ead
--- /dev/null
+++ b/drivers/net/ethernet/3com/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the 3Com Ethernet device drivers
+#
+
+obj-$(CONFIG_EL1) += 3c501.o
+obj-$(CONFIG_EL3) += 3c509.o
+obj-$(CONFIG_3C515) += 3c515.o
+obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
+obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
+obj-$(CONFIG_VORTEX) += 3c59x.o
+obj-$(CONFIG_TYPHOON) += typhoon.o
diff --git a/drivers/net/typhoon.c b/drivers/net/ethernet/3com/typhoon.c
index 3de4283344e9..20ea07508ac7 100644
--- a/drivers/net/typhoon.c
+++ b/drivers/net/ethernet/3com/typhoon.c
@@ -810,17 +810,16 @@ typhoon_start_tx(struct sk_buff *skb, struct net_device *dev)
txd->frag.addrHi = 0;
first_txd->numDesc++;
- for(i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
void *frag_addr;
txd = (struct tx_desc *) (txRing->ringBase +
txRing->lastWrite);
typhoon_inc_tx_index(&txRing->lastWrite, 1);
- len = frag->size;
- frag_addr = (void *) page_address(frag->page) +
- frag->page_offset;
+ len = skb_frag_size(frag);
+ frag_addr = skb_frag_address(frag);
skb_dma = pci_map_single(tp->tx_pdev, frag_addr, len,
PCI_DMA_TODEVICE);
txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
@@ -1149,13 +1148,9 @@ static void
typhoon_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering)
{
ering->rx_max_pending = RXENT_ENTRIES;
- ering->rx_mini_max_pending = 0;
- ering->rx_jumbo_max_pending = 0;
ering->tx_max_pending = TXLO_ENTRIES - 1;
ering->rx_pending = RXENT_ENTRIES;
- ering->rx_mini_pending = 0;
- ering->rx_jumbo_pending = 0;
ering->tx_pending = TXLO_ENTRIES - 1;
}
@@ -2266,7 +2261,7 @@ static const struct net_device_ops typhoon_netdev_ops = {
.ndo_open = typhoon_open,
.ndo_stop = typhoon_close,
.ndo_start_xmit = typhoon_start_tx,
- .ndo_set_multicast_list = typhoon_set_rx_mode,
+ .ndo_set_rx_mode = typhoon_set_rx_mode,
.ndo_tx_timeout = typhoon_tx_timeout,
.ndo_get_stats = typhoon_get_stats,
.ndo_validate_addr = eth_validate_addr,
@@ -2367,7 +2362,7 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->irq = pdev->irq;
tp = netdev_priv(dev);
- tp->shared = (struct typhoon_shared *) shared;
+ tp->shared = shared;
tp->shared_dma = shared_dma;
tp->pdev = pdev;
tp->tx_pdev = pdev;
diff --git a/drivers/net/typhoon.h b/drivers/net/ethernet/3com/typhoon.h
index 88187fc84aa3..88187fc84aa3 100644
--- a/drivers/net/typhoon.h
+++ b/drivers/net/ethernet/3com/typhoon.h
diff --git a/drivers/net/3c503.c b/drivers/net/ethernet/8390/3c503.c
index 5b732988d493..fbab1367505f 100644
--- a/drivers/net/3c503.c
+++ b/drivers/net/ethernet/8390/3c503.c
@@ -49,6 +49,7 @@ static const char version[] =
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/ethtool.h>
#include <asm/uaccess.h>
@@ -175,7 +176,7 @@ static const struct net_device_ops el2_netdev_ops = {
.ndo_start_xmit = eip_start_xmit,
.ndo_tx_timeout = eip_tx_timeout,
.ndo_get_stats = eip_get_stats,
- .ndo_set_multicast_list = eip_set_multicast_list,
+ .ndo_set_rx_mode = eip_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/3c503.h b/drivers/net/ethernet/8390/3c503.h
index e2367b82a2ec..e2367b82a2ec 100644
--- a/drivers/net/3c503.h
+++ b/drivers/net/ethernet/8390/3c503.h
diff --git a/drivers/net/8390.c b/drivers/net/ethernet/8390/8390.c
index 7c7518be1756..5db1f55abef4 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/ethernet/8390/8390.c
@@ -61,7 +61,7 @@ const struct net_device_ops ei_netdev_ops = {
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
- .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/8390.h b/drivers/net/ethernet/8390/8390.h
index 3d9e8fb4fbee..58a12e4c78f9 100644
--- a/drivers/net/8390.h
+++ b/drivers/net/ethernet/8390/8390.h
@@ -9,6 +9,7 @@
#include <linux/if_ether.h>
#include <linux/ioport.h>
+#include <linux/irqreturn.h>
#include <linux/skbuff.h>
#define TX_PAGES 12 /* Two Tx slots */
diff --git a/drivers/net/8390p.c b/drivers/net/ethernet/8390/8390p.c
index a2a64ea0b691..e8fc2e87e840 100644
--- a/drivers/net/8390p.c
+++ b/drivers/net/ethernet/8390/8390p.c
@@ -66,7 +66,7 @@ const struct net_device_ops eip_netdev_ops = {
.ndo_start_xmit = eip_start_xmit,
.ndo_tx_timeout = eip_tx_timeout,
.ndo_get_stats = eip_get_stats,
- .ndo_set_multicast_list = eip_set_multicast_list,
+ .ndo_set_rx_mode = eip_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
new file mode 100644
index 000000000000..e04ade444247
--- /dev/null
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -0,0 +1,337 @@
+#
+# 8390 device configuration
+#
+
+config NET_VENDOR_8390
+ bool "National Semi-conductor 8390 devices"
+ default y
+ depends on NET_VENDOR_NATSEMI && (AMIGA_PCMCIA || PCI || SUPERH || \
+ ISA || MCA || EISA || MAC || M32R || MACH_TX49XX || \
+ MCA_LEGACY || H8300 || ARM || MIPS || ZORRO || PCMCIA || \
+ EXPERIMENTAL)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Western Digital cards. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_VENDOR_8390
+
+config EL2
+ tristate "3c503 \"EtherLink II\" support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c503.
+
+config AC3200
+ tristate "Ansel Communications EISA 3200 support (EXPERIMENTAL)"
+ depends on PCI && (ISA || EISA) && EXPERIMENTAL
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ac3200.
+
+config PCMCIA_AXNET
+ tristate "Asix AX88190 PCMCIA support"
+ depends on PCMCIA
+ ---help---
+ Say Y here if you intend to attach an Asix AX88190-based PCMCIA
+ (PC-card) Fast Ethernet card to your computer. These cards are
+ nearly NE2000 compatible but need a separate driver due to a few
+ misfeatures.
+
+ To compile this driver as a module, choose M here: the module will be
+ called axnet_cs. If unsure, say N.
+
+config AX88796
+ tristate "ASIX AX88796 NE2000 clone support"
+ depends on (ARM || MIPS || SUPERH)
+ select PHYLIB
+ select MDIO_BITBANG
+ ---help---
+ AX88796 driver, using platform bus to provide
+ chip detection and resources
+
+config AX88796_93CX6
+ bool "ASIX AX88796 external 93CX6 eeprom support"
+ depends on AX88796
+ select EEPROM_93CX6
+ ---help---
+ Select this if your platform comes with an external 93CX6 eeprom.
+
+config E2100
+ tristate "Cabletron E21xx support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called e2100.
+
+config ES3210
+ tristate "Racal-Interlan EISA ES3210 support (EXPERIMENTAL)"
+ depends on PCI && EISA && EXPERIMENTAL
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called es3210.
+
+config HPLAN_PLUS
+ tristate "HP PCLAN+ (27247B and 27252A) support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called hp-plus.
+
+config HPLAN
+ tristate "HP PCLAN (27245 and other 27xxx series) support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called hp.
+
+config HYDRA
+ tristate "Hydra support"
+ depends on ZORRO
+ select CRC32
+ ---help---
+ If you have a Hydra Ethernet adapter, say Y. Otherwise, say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called hydra.
+
+config ARM_ETHERH
+ tristate "I-cubed EtherH/ANT EtherM support"
+ depends on ARM && ARCH_ACORN
+ select CRC32
+ ---help---
+ If you have an Acorn system with one of these network cards, you
+ should say Y to this option if you wish to use it with Linux.
+
+config LNE390
+ tristate "Mylex EISA LNE390A/B support (EXPERIMENTAL)"
+ depends on PCI && EISA && EXPERIMENTAL
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called lne390.
+
+config MAC8390
+ bool "Macintosh NS 8390 based ethernet cards"
+ depends on MAC
+ select CRC32
+ ---help---
+ If you want to include a driver to support Nubus or LC-PDS
+ Ethernet cards using an NS8390 chipset or its equivalent, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+config NE2000
+ tristate "NE2000/NE1000 support"
+ depends on (ISA || (Q40 && m) || M32R || MACH_TX49XX)
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Many Ethernet cards
+ without a specific driver are compatible with NE2000.
+
+ If you have a PCI NE2000 card however, say N here and Y to "PCI
+ NE2000 and clone support" under "EISA, VLB, PCI and on board
+ controllers" below. If you have a NE2000 card and are running on
+ an MCA system (a bus system used on some IBM PS/2 computers and
+ laptops), say N here and Y to "NE/2 (ne2000 MCA version) support",
+ below.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ne.
+
+config NE2_MCA
+ tristate "NE/2 (ne2000 MCA version) support"
+ depends on MCA_LEGACY
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ne2.
+
+config NE2K_PCI
+ tristate "PCI NE2000 and clones support (see help)"
+ depends on PCI
+ select CRC32
+ ---help---
+ This driver is for NE2000 compatible PCI cards. It will not work
+ with ISA NE2000 cards (they have their own driver, "NE2000/NE1000
+ support" below). If you have a PCI NE2000 network (Ethernet) card,
+ say Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ This driver also works for the following NE2000 clone cards:
+ RealTek RTL-8029 Winbond 89C940 Compex RL2000 KTI ET32P2
+ NetVin NV5000SC Via 86C926 SureCom NE34 Winbond
+ Holtek HT80232 Holtek HT80229
+
+ To compile this driver as a module, choose M here. The module
+ will be called ne2k-pci.
+
+config APNE
+ tristate "PCMCIA NE2000 support"
+ depends on AMIGA_PCMCIA
+ select CRC32
+ ---help---
+ If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise,
+ say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called apne.
+
+config NE3210
+ tristate "Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)"
+ depends on PCI && EISA && EXPERIMENTAL
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Note that this driver
+ will NOT WORK for NE3200 cards as they are completely different.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ne3210.
+
+config PCMCIA_PCNET
+ tristate "NE2000 compatible PCMCIA support"
+ depends on PCMCIA
+ select CRC32
+ ---help---
+ Say Y here if you intend to attach an NE2000 compatible PCMCIA
+ (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcnet_cs. If unsure, say N.
+
+config NE_H8300
+ tristate "NE2000 compatible support for H8/300"
+ depends on H8300
+ ---help---
+ Say Y here if you want to use the NE2000 compatible
+ controller on the Renesas H8/300 processor.
+
+config STNIC
+ tristate "National DP83902AV support"
+ depends on SUPERH
+ select CRC32
+ ---help---
+ Support for cards based on the National Semiconductor DP83902AV
+ ST-NIC Serial Network Interface Controller for Twisted Pair. This
+ is a 10Mbit/sec Ethernet controller. Product overview and specs at
+ <http://www.national.com/pf/DP/DP83902A.html>.
+
+ If unsure, say N.
+
+config ULTRAMCA
+ tristate "SMC Ultra MCA support"
+ depends on MCA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type and are running
+ an MCA based system (PS/2), say Y and read the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called smc-mca.
+
+config ULTRA
+ tristate "SMC Ultra support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Important: There have been many reports that, with some motherboards
+ mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible,
+ such as some BusLogic models) causes corruption problems with many
+ operating systems. The Linux smc-ultra driver has a work-around for
+ this but keep it in mind if you have such a SCSI card and have
+ problems.
+
+ To compile this driver as a module, choose M here. The module
+ will be called smc-ultra.
+
+config ULTRA32
+ tristate "SMC Ultra32 EISA support"
+ depends on EISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called smc-ultra32.
+
+config WD80x3
+ tristate "WD80*3 support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called wd.
+
+config ZORRO8390
+ tristate "Zorro NS8390-based Ethernet support"
+ depends on ZORRO
+ select CRC32
+ ---help---
+ This driver is for Zorro Ethernet cards using an NS8390-compatible
+ chipset, like the Village Tronic Ariadne II and the Individual
+ Computers X-Surf Ethernet cards. If you have such a card, say Y.
+ Otherwise, say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called zorro8390.
+
+endif # NET_VENDOR_8390
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
new file mode 100644
index 000000000000..3337d7fb4344
--- /dev/null
+++ b/drivers/net/ethernet/8390/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for the 8390 network device drivers.
+#
+
+obj-$(CONFIG_MAC8390) += mac8390.o
+obj-$(CONFIG_AC3200) += ac3200.o 8390.o
+obj-$(CONFIG_APNE) += apne.o 8390.o
+obj-$(CONFIG_ARM_ETHERH) += etherh.o
+obj-$(CONFIG_AX88796) += ax88796.o
+obj-$(CONFIG_E2100) += e2100.o 8390.o
+obj-$(CONFIG_EL2) += 3c503.o 8390p.o
+obj-$(CONFIG_ES3210) += es3210.o 8390.o
+obj-$(CONFIG_HPLAN_PLUS) += hp-plus.o 8390p.o
+obj-$(CONFIG_HPLAN) += hp.o 8390p.o
+obj-$(CONFIG_HYDRA) += hydra.o 8390.o
+obj-$(CONFIG_LNE390) += lne390.o 8390.o
+obj-$(CONFIG_NE2000) += ne.o 8390p.o
+obj-$(CONFIG_NE2_MCA) += ne2.o 8390p.o
+obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
+obj-$(CONFIG_NE3210) += ne3210.o 8390.o
+obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
+obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o
+obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o
+obj-$(CONFIG_STNIC) += stnic.o 8390.o
+obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o
+obj-$(CONFIG_ULTRA32) += smc-ultra32.o 8390.o
+obj-$(CONFIG_ULTRAMCA) += smc-mca.o 8390.o
+obj-$(CONFIG_WD80x3) += wd.o 8390.o
+obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o
diff --git a/drivers/net/ac3200.c b/drivers/net/ethernet/8390/ac3200.c
index 5181e9322119..5337dd0a59b0 100644
--- a/drivers/net/ac3200.c
+++ b/drivers/net/ethernet/8390/ac3200.c
@@ -32,6 +32,7 @@ static const char version[] =
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -150,7 +151,7 @@ static const struct net_device_ops ac_netdev_ops = {
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
- .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/apne.c b/drivers/net/ethernet/8390/apne.c
index 2fe60f168108..547737340cbb 100644
--- a/drivers/net/apne.c
+++ b/drivers/net/ethernet/8390/apne.c
@@ -36,6 +36,7 @@
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <asm/system.h>
diff --git a/drivers/net/ax88796.c b/drivers/net/ethernet/8390/ax88796.c
index e7cb8c8b9776..e9f8432f55b4 100644
--- a/drivers/net/ax88796.c
+++ b/drivers/net/ethernet/8390/ax88796.c
@@ -543,7 +543,7 @@ static const struct net_device_ops ax_netdev_ops = {
.ndo_start_xmit = ax_ei_start_xmit,
.ndo_tx_timeout = ax_ei_tx_timeout,
.ndo_get_stats = ax_ei_get_stats,
- .ndo_set_multicast_list = ax_ei_set_multicast_list,
+ .ndo_set_rx_mode = ax_ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c
index 9953db711969..bba51cdc74a1 100644
--- a/drivers/net/pcmcia/axnet_cs.c
+++ b/drivers/net/ethernet/8390/axnet_cs.c
@@ -38,7 +38,7 @@
#include <linux/etherdevice.h>
#include <linux/crc32.h>
#include <linux/mii.h>
-#include "../8390.h"
+#include "8390.h"
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
@@ -134,7 +134,7 @@ static const struct net_device_ops axnet_netdev_ops = {
.ndo_start_xmit = axnet_start_xmit,
.ndo_tx_timeout = axnet_tx_timeout,
.ndo_get_stats = get_stats,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/e2100.c b/drivers/net/ethernet/8390/e2100.c
index 94ec973b2bdc..d16dc53c1813 100644
--- a/drivers/net/e2100.c
+++ b/drivers/net/ethernet/8390/e2100.c
@@ -44,6 +44,7 @@ static const char version[] =
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/io.h>
@@ -167,7 +168,7 @@ static const struct net_device_ops e21_netdev_ops = {
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
- .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/es3210.c b/drivers/net/ethernet/8390/es3210.c
index 0ba5e7b90584..7a09575ecff0 100644
--- a/drivers/net/es3210.c
+++ b/drivers/net/ethernet/8390/es3210.c
@@ -54,6 +54,7 @@ static const char version[] =
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
diff --git a/drivers/net/arm/etherh.c b/drivers/net/ethernet/8390/etherh.c
index 03e217a868d4..48c4948750d1 100644
--- a/drivers/net/arm/etherh.c
+++ b/drivers/net/ethernet/8390/etherh.c
@@ -65,7 +65,7 @@
static char version[] __initdata =
"EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n";
-#include "../lib8390.c"
+#include "lib8390.c"
static unsigned int net_debug = NET_DEBUG;
@@ -644,7 +644,7 @@ static const struct net_device_ops etherh_netdev_ops = {
.ndo_start_xmit = __ei_start_xmit,
.ndo_tx_timeout = __ei_tx_timeout,
.ndo_get_stats = __ei_get_stats,
- .ndo_set_multicast_list = __ei_set_multicast_list,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/hp-plus.c b/drivers/net/ethernet/8390/hp-plus.c
index 82bffc3cabdf..eeac843dcd2d 100644
--- a/drivers/net/hp-plus.c
+++ b/drivers/net/ethernet/8390/hp-plus.c
@@ -30,6 +30,7 @@ static const char version[] =
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/system.h>
@@ -164,7 +165,7 @@ static const struct net_device_ops hpp_netdev_ops = {
.ndo_start_xmit = eip_start_xmit,
.ndo_tx_timeout = eip_tx_timeout,
.ndo_get_stats = eip_get_stats,
- .ndo_set_multicast_list = eip_set_multicast_list,
+ .ndo_set_rx_mode = eip_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/hp.c b/drivers/net/ethernet/8390/hp.c
index ef2014375e62..18564d4a7c04 100644
--- a/drivers/net/hp.c
+++ b/drivers/net/ethernet/8390/hp.c
@@ -30,6 +30,7 @@ static const char version[] =
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/system.h>
diff --git a/drivers/net/hydra.c b/drivers/net/ethernet/8390/hydra.c
index 1cd481c04202..3dac937a67c4 100644
--- a/drivers/net/hydra.c
+++ b/drivers/net/ethernet/8390/hydra.c
@@ -101,7 +101,7 @@ static const struct net_device_ops hydra_netdev_ops = {
.ndo_start_xmit = __ei_start_xmit,
.ndo_tx_timeout = __ei_tx_timeout,
.ndo_get_stats = __ei_get_stats,
- .ndo_set_multicast_list = __ei_set_multicast_list,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
index 17b75e5f1b0a..05ae21435bfd 100644
--- a/drivers/net/lib8390.c
+++ b/drivers/net/ethernet/8390/lib8390.c
@@ -36,7 +36,7 @@
Paul Gortmaker : tweak ANK's above multicast changes a bit.
Paul Gortmaker : update packet statistics for v2.1.x
Alan Cox : support arbitrary stupid port mappings on the
- 68K Macintosh. Support >16bit I/O spaces
+ 68K Macintosh. Support >16bit I/O spaces
Paul Gortmaker : add kmod support for auto-loading of the 8390
module by all drivers that require it.
Alan Cox : Spinlocking work, added 'BUG_83C690'
@@ -58,8 +58,8 @@
#include <linux/string.h>
#include <linux/bitops.h>
#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
#include <asm/irq.h>
#include <linux/delay.h>
#include <linux/errno.h>
@@ -108,7 +108,6 @@ int ei_debug = 1;
/* Index to functions. */
static void ei_tx_intr(struct net_device *dev);
static void ei_tx_err(struct net_device *dev);
-void ei_tx_timeout(struct net_device *dev);
static void ei_receive(struct net_device *dev);
static void ei_rx_overrun(struct net_device *dev);
@@ -206,19 +205,19 @@ static int __ei_open(struct net_device *dev)
struct ei_device *ei_local = netdev_priv(dev);
if (dev->watchdog_timeo <= 0)
- dev->watchdog_timeo = TX_TIMEOUT;
+ dev->watchdog_timeo = TX_TIMEOUT;
/*
* Grab the page lock so we own the register set, then call
* the init function.
*/
- spin_lock_irqsave(&ei_local->page_lock, flags);
+ spin_lock_irqsave(&ei_local->page_lock, flags);
__NS8390_init(dev, 1);
/* Set the flag before we drop the lock, That way the IRQ arrives
after its set and we get no silly warnings */
netif_start_queue(dev);
- spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
ei_local->irqlock = 0;
return 0;
}
@@ -238,9 +237,9 @@ static int __ei_close(struct net_device *dev)
* Hold the page lock during close
*/
- spin_lock_irqsave(&ei_local->page_lock, flags);
+ spin_lock_irqsave(&ei_local->page_lock, flags);
__NS8390_init(dev, 0);
- spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
netif_stop_queue(dev);
return 0;
}
@@ -267,12 +266,12 @@ static void __ei_tx_timeout(struct net_device *dev)
isr = ei_inb(e8390_base+EN0_ISR);
spin_unlock_irqrestore(&ei_local->page_lock, flags);
- printk(KERN_DEBUG "%s: Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",
- dev->name, (txsr & ENTSR_ABT) ? "excess collisions." :
- (isr) ? "lost interrupt?" : "cable problem?", txsr, isr, tickssofar);
+ netdev_dbg(dev, "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d\n",
+ (txsr & ENTSR_ABT) ? "excess collisions." :
+ (isr) ? "lost interrupt?" : "cable problem?",
+ txsr, isr, tickssofar);
- if (!isr && !dev->stats.tx_packets)
- {
+ if (!isr && !dev->stats.tx_packets) {
/* The 8390 probably hasn't gotten on the cable yet. */
ei_local->interface_num ^= 1; /* Try a different xcvr. */
}
@@ -344,27 +343,22 @@ static netdev_tx_t __ei_start_xmit(struct sk_buff *skb,
* card, leaving a substantial gap between each transmitted packet.
*/
- if (ei_local->tx1 == 0)
- {
+ if (ei_local->tx1 == 0) {
output_page = ei_local->tx_start_page;
ei_local->tx1 = send_length;
if (ei_debug && ei_local->tx2 > 0)
- printk(KERN_DEBUG "%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
- dev->name, ei_local->tx2, ei_local->lasttx, ei_local->txing);
- }
- else if (ei_local->tx2 == 0)
- {
+ netdev_dbg(dev, "idle transmitter tx2=%d, lasttx=%d, txing=%d\n",
+ ei_local->tx2, ei_local->lasttx, ei_local->txing);
+ } else if (ei_local->tx2 == 0) {
output_page = ei_local->tx_start_page + TX_PAGES/2;
ei_local->tx2 = send_length;
if (ei_debug && ei_local->tx1 > 0)
- printk(KERN_DEBUG "%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
- dev->name, ei_local->tx1, ei_local->lasttx, ei_local->txing);
- }
- else
- { /* We should never get here. */
+ netdev_dbg(dev, "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n",
+ ei_local->tx1, ei_local->lasttx, ei_local->txing);
+ } else { /* We should never get here. */
if (ei_debug)
- printk(KERN_DEBUG "%s: No Tx buffers free! tx1=%d tx2=%d last=%d\n",
- dev->name, ei_local->tx1, ei_local->tx2, ei_local->lasttx);
+ netdev_dbg(dev, "No Tx buffers free! tx1=%d tx2=%d last=%d\n",
+ ei_local->tx1, ei_local->tx2, ei_local->lasttx);
ei_local->irqlock = 0;
netif_stop_queue(dev);
ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
@@ -382,22 +376,18 @@ static netdev_tx_t __ei_start_xmit(struct sk_buff *skb,
ei_block_output(dev, send_length, data, output_page);
- if (! ei_local->txing)
- {
+ if (!ei_local->txing) {
ei_local->txing = 1;
NS8390_trigger_send(dev, send_length, output_page);
- if (output_page == ei_local->tx_start_page)
- {
+ if (output_page == ei_local->tx_start_page) {
ei_local->tx1 = -1;
ei_local->lasttx = -1;
- }
- else
- {
+ } else {
ei_local->tx2 = -1;
ei_local->lasttx = -2;
}
- }
- else ei_local->txqueue++;
+ } else
+ ei_local->txqueue++;
if (ei_local->tx1 && ei_local->tx2)
netif_stop_queue(dev);
@@ -410,8 +400,8 @@ static netdev_tx_t __ei_start_xmit(struct sk_buff *skb,
spin_unlock(&ei_local->page_lock);
enable_irq_lockdep_irqrestore(dev->irq, &flags);
-
- dev_kfree_skb (skb);
+ skb_tx_timestamp(skb);
+ dev_kfree_skb(skb);
dev->stats.tx_bytes += send_length;
return NETDEV_TX_OK;
@@ -442,15 +432,13 @@ static irqreturn_t __ei_interrupt(int irq, void *dev_id)
spin_lock(&ei_local->page_lock);
- if (ei_local->irqlock)
- {
+ if (ei_local->irqlock) {
/*
* This might just be an interrupt for a PCI device sharing
* this line
*/
- printk("%s: Interrupted while interrupts are masked!"
- " isr=%#2x imr=%#2x.\n",
- dev->name, ei_inb_p(e8390_base + EN0_ISR),
+ netdev_err(dev, "Interrupted while interrupts are masked! isr=%#2x imr=%#2x\n",
+ ei_inb_p(e8390_base + EN0_ISR),
ei_inb_p(e8390_base + EN0_IMR));
spin_unlock(&ei_local->page_lock);
return IRQ_NONE;
@@ -459,15 +447,14 @@ static irqreturn_t __ei_interrupt(int irq, void *dev_id)
/* Change to page 0 and read the intr status reg. */
ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
if (ei_debug > 3)
- printk(KERN_DEBUG "%s: interrupt(isr=%#2.2x).\n", dev->name,
+ netdev_dbg(dev, "interrupt(isr=%#2.2x)\n",
ei_inb_p(e8390_base + EN0_ISR));
/* !!Assumption!! -- we stay in page 0. Don't break this. */
while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0 &&
- ++nr_serviced < MAX_SERVICE)
- {
+ ++nr_serviced < MAX_SERVICE) {
if (!netif_running(dev)) {
- printk(KERN_WARNING "%s: interrupt from stopped card\n", dev->name);
+ netdev_warn(dev, "interrupt from stopped card\n");
/* rmk - acknowledge the interrupts */
ei_outb_p(interrupts, e8390_base + EN0_ISR);
interrupts = 0;
@@ -475,8 +462,7 @@ static irqreturn_t __ei_interrupt(int irq, void *dev_id)
}
if (interrupts & ENISR_OVER)
ei_rx_overrun(dev);
- else if (interrupts & (ENISR_RX+ENISR_RX_ERR))
- {
+ else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
/* Got a good (?) packet. */
ei_receive(dev);
}
@@ -486,35 +472,30 @@ static irqreturn_t __ei_interrupt(int irq, void *dev_id)
else if (interrupts & ENISR_TX_ERR)
ei_tx_err(dev);
- if (interrupts & ENISR_COUNTERS)
- {
+ if (interrupts & ENISR_COUNTERS) {
dev->stats.rx_frame_errors += ei_inb_p(e8390_base + EN0_COUNTER0);
dev->stats.rx_crc_errors += ei_inb_p(e8390_base + EN0_COUNTER1);
- dev->stats.rx_missed_errors+= ei_inb_p(e8390_base + EN0_COUNTER2);
+ dev->stats.rx_missed_errors += ei_inb_p(e8390_base + EN0_COUNTER2);
ei_outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
}
/* Ignore any RDC interrupts that make it back to here. */
if (interrupts & ENISR_RDC)
- {
ei_outb_p(ENISR_RDC, e8390_base + EN0_ISR);
- }
ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
}
- if (interrupts && ei_debug)
- {
+ if (interrupts && ei_debug) {
ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
- if (nr_serviced >= MAX_SERVICE)
- {
+ if (nr_serviced >= MAX_SERVICE) {
/* 0xFF is valid for a card removal */
- if(interrupts!=0xFF)
- printk(KERN_WARNING "%s: Too much work at interrupt, status %#2.2x\n",
- dev->name, interrupts);
+ if (interrupts != 0xFF)
+ netdev_warn(dev, "Too much work at interrupt, status %#2.2x\n",
+ interrupts);
ei_outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
} else {
- printk(KERN_WARNING "%s: unknown interrupt %#2x\n", dev->name, interrupts);
+ netdev_warn(dev, "unknown interrupt %#2x\n", interrupts);
ei_outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
}
}
@@ -554,30 +535,32 @@ static void ei_tx_err(struct net_device *dev)
unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
#ifdef VERBOSE_ERROR_DUMP
- printk(KERN_DEBUG "%s: transmitter error (%#2x): ", dev->name, txsr);
+ netdev_dbg(dev, "transmitter error (%#2x):", txsr);
if (txsr & ENTSR_ABT)
- printk("excess-collisions ");
+ pr_cont(" excess-collisions ");
if (txsr & ENTSR_ND)
- printk("non-deferral ");
+ pr_cont(" non-deferral ");
if (txsr & ENTSR_CRS)
- printk("lost-carrier ");
+ pr_cont(" lost-carrier ");
if (txsr & ENTSR_FU)
- printk("FIFO-underrun ");
+ pr_cont(" FIFO-underrun ");
if (txsr & ENTSR_CDH)
- printk("lost-heartbeat ");
- printk("\n");
+ pr_cont(" lost-heartbeat ");
+ pr_cont("\n");
#endif
ei_outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
if (tx_was_aborted)
ei_tx_intr(dev);
- else
- {
+ else {
dev->stats.tx_errors++;
- if (txsr & ENTSR_CRS) dev->stats.tx_carrier_errors++;
- if (txsr & ENTSR_CDH) dev->stats.tx_heartbeat_errors++;
- if (txsr & ENTSR_OWC) dev->stats.tx_window_errors++;
+ if (txsr & ENTSR_CRS)
+ dev->stats.tx_carrier_errors++;
+ if (txsr & ENTSR_CDH)
+ dev->stats.tx_heartbeat_errors++;
+ if (txsr & ENTSR_OWC)
+ dev->stats.tx_window_errors++;
}
}
@@ -603,52 +586,45 @@ static void ei_tx_intr(struct net_device *dev)
*/
ei_local->txqueue--;
- if (ei_local->tx1 < 0)
- {
+ if (ei_local->tx1 < 0) {
if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
- printk(KERN_ERR "%s: bogus last_tx_buffer %d, tx1=%d.\n",
- ei_local->name, ei_local->lasttx, ei_local->tx1);
+ pr_err("%s: bogus last_tx_buffer %d, tx1=%d\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx1);
ei_local->tx1 = 0;
- if (ei_local->tx2 > 0)
- {
+ if (ei_local->tx2 > 0) {
ei_local->txing = 1;
NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
dev->trans_start = jiffies;
ei_local->tx2 = -1,
ei_local->lasttx = 2;
- }
- else ei_local->lasttx = 20, ei_local->txing = 0;
- }
- else if (ei_local->tx2 < 0)
- {
+ } else
+ ei_local->lasttx = 20, ei_local->txing = 0;
+ } else if (ei_local->tx2 < 0) {
if (ei_local->lasttx != 2 && ei_local->lasttx != -2)
- printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
- ei_local->name, ei_local->lasttx, ei_local->tx2);
+ pr_err("%s: bogus last_tx_buffer %d, tx2=%d\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx2);
ei_local->tx2 = 0;
- if (ei_local->tx1 > 0)
- {
+ if (ei_local->tx1 > 0) {
ei_local->txing = 1;
NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
dev->trans_start = jiffies;
ei_local->tx1 = -1;
ei_local->lasttx = 1;
- }
- else
+ } else
ei_local->lasttx = 10, ei_local->txing = 0;
- }
-// else printk(KERN_WARNING "%s: unexpected TX-done interrupt, lasttx=%d.\n",
-// dev->name, ei_local->lasttx);
+ } /* else
+ netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n",
+ ei_local->lasttx);
+*/
/* Minimize Tx latency: update the statistics after we restart TXing. */
if (status & ENTSR_COL)
dev->stats.collisions++;
if (status & ENTSR_PTX)
dev->stats.tx_packets++;
- else
- {
+ else {
dev->stats.tx_errors++;
- if (status & ENTSR_ABT)
- {
+ if (status & ENTSR_ABT) {
dev->stats.tx_aborted_errors++;
dev->stats.collisions += 16;
}
@@ -682,8 +658,7 @@ static void ei_receive(struct net_device *dev)
struct e8390_pkt_hdr rx_frame;
int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
- while (++rx_pkt_count < 10)
- {
+ while (++rx_pkt_count < 10) {
int pkt_len, pkt_stat;
/* Get the rx page (incoming packet pointer). */
@@ -702,9 +677,11 @@ static void ei_receive(struct net_device *dev)
Keep quiet if it looks like a card removal. One problem here
is that some clones crash in roughly the same way.
*/
- if (ei_debug > 0 && this_frame != ei_local->current_page && (this_frame!=0x0 || rxing_page!=0xFF))
- printk(KERN_ERR "%s: mismatched read page pointers %2x vs %2x.\n",
- dev->name, this_frame, ei_local->current_page);
+ if (ei_debug > 0 &&
+ this_frame != ei_local->current_page &&
+ (this_frame != 0x0 || rxing_page != 0xFF))
+ netdev_err(dev, "mismatched read page pointers %2x vs %2x\n",
+ this_frame, ei_local->current_page);
if (this_frame == rxing_page) /* Read all the frames? */
break; /* Done for now */
@@ -730,46 +707,39 @@ static void ei_receive(struct net_device *dev)
continue;
}
- if (pkt_len < 60 || pkt_len > 1518)
- {
+ if (pkt_len < 60 || pkt_len > 1518) {
if (ei_debug)
- printk(KERN_DEBUG "%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n",
- dev->name, rx_frame.count, rx_frame.status,
+ netdev_dbg(dev, "bogus packet size: %d, status=%#2x nxpg=%#2x\n",
+ rx_frame.count, rx_frame.status,
rx_frame.next);
dev->stats.rx_errors++;
dev->stats.rx_length_errors++;
- }
- else if ((pkt_stat & 0x0F) == ENRSR_RXOK)
- {
+ } else if ((pkt_stat & 0x0F) == ENRSR_RXOK) {
struct sk_buff *skb;
skb = dev_alloc_skb(pkt_len+2);
- if (skb == NULL)
- {
+ if (skb == NULL) {
if (ei_debug > 1)
- printk(KERN_DEBUG "%s: Couldn't allocate a sk_buff of size %d.\n",
- dev->name, pkt_len);
+ netdev_dbg(dev, "Couldn't allocate a sk_buff of size %d\n",
+ pkt_len);
dev->stats.rx_dropped++;
break;
- }
- else
- {
- skb_reserve(skb,2); /* IP headers on 16 byte boundaries */
+ } else {
+ skb_reserve(skb, 2); /* IP headers on 16 byte boundaries */
skb_put(skb, pkt_len); /* Make room */
ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
+ skb->protocol = eth_type_trans(skb, dev);
+ if (!skb_defer_rx_timestamp(skb))
+ netif_rx(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
if (pkt_stat & ENRSR_PHY)
dev->stats.multicast++;
}
- }
- else
- {
+ } else {
if (ei_debug)
- printk(KERN_DEBUG "%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n",
- dev->name, rx_frame.status, rx_frame.next,
+ netdev_dbg(dev, "bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+ rx_frame.status, rx_frame.next,
rx_frame.count);
dev->stats.rx_errors++;
/* NB: The NIC counts CRC, frame and missed errors. */
@@ -780,8 +750,8 @@ static void ei_receive(struct net_device *dev)
/* This _should_ never happen: it's here for avoiding bad clones. */
if (next_frame >= ei_local->stop_page) {
- printk("%s: next frame inconsistency, %#2x\n", dev->name,
- next_frame);
+ netdev_notice(dev, "next frame inconsistency, %#2x\n",
+ next_frame);
next_frame = ei_local->rx_start_page;
}
ei_local->current_page = next_frame;
@@ -821,7 +791,7 @@ static void ei_rx_overrun(struct net_device *dev)
ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
if (ei_debug > 1)
- printk(KERN_DEBUG "%s: Receiver overrun.\n", dev->name);
+ netdev_dbg(dev, "Receiver overrun\n");
dev->stats.rx_over_errors++;
/*
@@ -844,8 +814,7 @@ static void ei_rx_overrun(struct net_device *dev)
* step is vital, and skipping it will cause no end of havoc.
*/
- if (was_txing)
- {
+ if (was_txing) {
unsigned char tx_completed = ei_inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR);
if (!tx_completed)
must_resend = 1;
@@ -869,7 +838,7 @@ static void ei_rx_overrun(struct net_device *dev)
*/
ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR);
if (must_resend)
- ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
+ ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
}
/*
@@ -886,11 +855,11 @@ static struct net_device_stats *__ei_get_stats(struct net_device *dev)
if (!netif_running(dev))
return &dev->stats;
- spin_lock_irqsave(&ei_local->page_lock,flags);
+ spin_lock_irqsave(&ei_local->page_lock, flags);
/* Read the counter registers, assuming we are in page 0. */
- dev->stats.rx_frame_errors += ei_inb_p(ioaddr + EN0_COUNTER0);
- dev->stats.rx_crc_errors += ei_inb_p(ioaddr + EN0_COUNTER1);
- dev->stats.rx_missed_errors+= ei_inb_p(ioaddr + EN0_COUNTER2);
+ dev->stats.rx_frame_errors += ei_inb_p(ioaddr + EN0_COUNTER0);
+ dev->stats.rx_crc_errors += ei_inb_p(ioaddr + EN0_COUNTER1);
+ dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
spin_unlock_irqrestore(&ei_local->page_lock, flags);
return &dev->stats;
@@ -929,13 +898,11 @@ static void do_set_multicast_list(struct net_device *dev)
int i;
struct ei_device *ei_local = netdev_priv(dev);
- if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI)))
- {
+ if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
memset(ei_local->mcfilter, 0, 8);
if (!netdev_mc_empty(dev))
make_mc_bits(ei_local->mcfilter, dev);
- }
- else
+ } else
memset(ei_local->mcfilter, 0xFF, 8); /* mcast set to accept-all */
/*
@@ -954,23 +921,23 @@ static void do_set_multicast_list(struct net_device *dev)
if (netif_running(dev))
ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
ei_outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD);
- for(i = 0; i < 8; i++)
- {
+ for (i = 0; i < 8; i++) {
ei_outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
#ifndef BUG_83C690
- if(ei_inb_p(e8390_base + EN1_MULT_SHIFT(i))!=ei_local->mcfilter[i])
- printk(KERN_ERR "Multicast filter read/write mismap %d\n",i);
+ if (ei_inb_p(e8390_base + EN1_MULT_SHIFT(i)) != ei_local->mcfilter[i])
+ netdev_err(dev, "Multicast filter read/write mismap %d\n",
+ i);
#endif
}
ei_outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
- if(dev->flags&IFF_PROMISC)
- ei_outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR);
+ if (dev->flags&IFF_PROMISC)
+ ei_outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR);
else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev))
- ei_outb_p(E8390_RXCONFIG | 0x08, e8390_base + EN0_RXCR);
- else
- ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
- }
+ ei_outb_p(E8390_RXCONFIG | 0x08, e8390_base + EN0_RXCR);
+ else
+ ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
+}
/*
* Called without lock held. This is invoked from user context and may
@@ -1042,8 +1009,8 @@ static void __NS8390_init(struct net_device *dev, int startp)
? (0x48 | ENDCFG_WTS | (ei_local->bigendian ? ENDCFG_BOS : 0))
: 0x48;
- if(sizeof(struct e8390_pkt_hdr)!=4)
- panic("8390.c: header struct mispacked\n");
+ if (sizeof(struct e8390_pkt_hdr) != 4)
+ panic("8390.c: header struct mispacked\n");
/* Follow National Semi's recommendations for initing the DP83902. */
ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */
ei_outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
@@ -1067,11 +1034,11 @@ static void __NS8390_init(struct net_device *dev, int startp)
/* Copy the station address into the DS8390 registers. */
ei_outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */
- for(i = 0; i < 6; i++)
- {
+ for (i = 0; i < 6; i++) {
ei_outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
- if (ei_debug > 1 && ei_inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i])
- printk(KERN_ERR "Hw. address read/write mismap %d\n",i);
+ if (ei_debug > 1 &&
+ ei_inb_p(e8390_base + EN1_PHYS_SHIFT(i)) != dev->dev_addr[i])
+ netdev_err(dev, "Hw. address read/write mismap %d\n", i);
}
ei_outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
@@ -1080,8 +1047,7 @@ static void __NS8390_init(struct net_device *dev, int startp)
ei_local->tx1 = ei_local->tx2 = 0;
ei_local->txing = 0;
- if (startp)
- {
+ if (startp) {
ei_outb_p(0xff, e8390_base + EN0_ISR);
ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
@@ -1099,14 +1065,12 @@ static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
int start_page)
{
unsigned long e8390_base = dev->base_addr;
- struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev);
+ struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev);
ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base+E8390_CMD);
- if (ei_inb_p(e8390_base + E8390_CMD) & E8390_TRANS)
- {
- printk(KERN_WARNING "%s: trigger_send() called with the transmitter busy.\n",
- dev->name);
+ if (ei_inb_p(e8390_base + E8390_CMD) & E8390_TRANS) {
+ netdev_warn(dev, "trigger_send() called with the transmitter busy\n");
return;
}
ei_outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
diff --git a/drivers/net/lne390.c b/drivers/net/ethernet/8390/lne390.c
index 8a1097cf8a83..f9888d20177b 100644
--- a/drivers/net/lne390.c
+++ b/drivers/net/ethernet/8390/lne390.c
@@ -41,6 +41,7 @@ static const char *version =
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
diff --git a/drivers/net/mac8390.c b/drivers/net/ethernet/8390/mac8390.c
index f84f5e6ededb..af5d9822cad9 100644
--- a/drivers/net/mac8390.c
+++ b/drivers/net/ethernet/8390/mac8390.c
@@ -494,7 +494,7 @@ static const struct net_device_ops mac8390_netdev_ops = {
.ndo_start_xmit = __ei_start_xmit,
.ndo_tx_timeout = __ei_tx_timeout,
.ndo_get_stats = __ei_get_stats,
- .ndo_set_multicast_list = __ei_set_multicast_list,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/ne-h8300.c b/drivers/net/ethernet/8390/ne-h8300.c
index 7298a34bc795..cd36a6a5f408 100644
--- a/drivers/net/ne-h8300.c
+++ b/drivers/net/ethernet/8390/ne-h8300.c
@@ -200,7 +200,7 @@ static const struct net_device_ops ne_netdev_ops = {
.ndo_start_xmit = __ei_start_xmit,
.ndo_tx_timeout = __ei_tx_timeout,
.ndo_get_stats = __ei_get_stats,
- .ndo_set_multicast_list = __ei_set_multicast_list,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/ne.c b/drivers/net/ethernet/8390/ne.c
index 1063093b3afc..1063093b3afc 100644
--- a/drivers/net/ne.c
+++ b/drivers/net/ethernet/8390/ne.c
diff --git a/drivers/net/ne2.c b/drivers/net/ethernet/8390/ne2.c
index 70cdc6996342..70cdc6996342 100644
--- a/drivers/net/ne2.c
+++ b/drivers/net/ethernet/8390/ne2.c
diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c
index 3c333cb5d34e..39923425ba25 100644
--- a/drivers/net/ne2k-pci.c
+++ b/drivers/net/ethernet/8390/ne2k-pci.c
@@ -207,7 +207,7 @@ static const struct net_device_ops ne2k_netdev_ops = {
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
- .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/ne3210.c b/drivers/net/ethernet/8390/ne3210.c
index 243ed2aee88e..243ed2aee88e 100644
--- a/drivers/net/ne3210.c
+++ b/drivers/net/ethernet/8390/ne3210.c
diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c
index b4fd7c3ed077..053b2551a72d 100644
--- a/drivers/net/pcmcia/pcnet_cs.c
+++ b/drivers/net/ethernet/8390/pcnet_cs.c
@@ -41,7 +41,7 @@
#include <linux/log2.h>
#include <linux/etherdevice.h>
#include <linux/mii.h>
-#include "../8390.h"
+#include "8390.h"
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
@@ -227,7 +227,7 @@ static const struct net_device_ops pcnet_netdev_ops = {
.ndo_start_xmit = ei_start_xmit,
.ndo_get_stats = ei_get_stats,
.ndo_do_ioctl = ei_ioctl,
- .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_set_rx_mode = ei_set_multicast_list,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/smc-mca.c b/drivers/net/ethernet/8390/smc-mca.c
index d07c39cb4daf..77efec44fea0 100644
--- a/drivers/net/smc-mca.c
+++ b/drivers/net/ethernet/8390/smc-mca.c
@@ -42,6 +42,7 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -190,7 +191,7 @@ static const struct net_device_ops ultramca_netdev_ops = {
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
- .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c
index 235a3c6c9f91..1cc306a83ff7 100644
--- a/drivers/net/smc-ultra.c
+++ b/drivers/net/ethernet/8390/smc-ultra.c
@@ -62,6 +62,7 @@ static const char version[] =
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/isapnp.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -191,7 +192,7 @@ static const struct net_device_ops ultra_netdev_ops = {
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
- .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/smc-ultra32.c b/drivers/net/ethernet/8390/smc-ultra32.c
index e459c3b2510a..bb87053eb3da 100644
--- a/drivers/net/smc-ultra32.c
+++ b/drivers/net/ethernet/8390/smc-ultra32.c
@@ -160,7 +160,7 @@ static const struct net_device_ops ultra32_netdev_ops = {
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
- .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/stnic.c b/drivers/net/ethernet/8390/stnic.c
index d85f0a84bc7b..d85f0a84bc7b 100644
--- a/drivers/net/stnic.c
+++ b/drivers/net/ethernet/8390/stnic.c
diff --git a/drivers/net/wd.c b/drivers/net/ethernet/8390/wd.c
index 8831a3393ecf..c175fadb597b 100644
--- a/drivers/net/wd.c
+++ b/drivers/net/ethernet/8390/wd.c
@@ -153,7 +153,7 @@ static const struct net_device_ops wd_netdev_ops = {
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
- .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/ethernet/8390/zorro8390.c b/drivers/net/ethernet/8390/zorro8390.c
new file mode 100644
index 000000000000..3aa9fe9999b5
--- /dev/null
+++ b/drivers/net/ethernet/8390/zorro8390.c
@@ -0,0 +1,452 @@
+/*
+ * Amiga Linux/m68k and Linux/PPC Zorro NS8390 Ethernet Driver
+ *
+ * (C) Copyright 1998-2000 by some Elitist 680x0 Users(TM)
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This program is based on all the other NE2000 drivers for Linux
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * The Ariadne II and X-Surf are Zorro-II boards containing Realtek RTL8019AS
+ * Ethernet Controllers.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/zorro.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#define EI_SHIFT(x) (ei_local->reg_offset[x])
+#define ei_inb(port) in_8(port)
+#define ei_outb(val, port) out_8(port, val)
+#define ei_inb_p(port) in_8(port)
+#define ei_outb_p(val, port) out_8(port, val)
+
+static const char version[] =
+ "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+#define DRV_NAME "zorro8390"
+
+#define NE_BASE (dev->base_addr)
+#define NE_CMD (0x00 * 2)
+#define NE_DATAPORT (0x10 * 2) /* NatSemi-defined port window offset */
+#define NE_RESET (0x1f * 2) /* Issue a read to reset,
+ * a write to clear. */
+#define NE_IO_EXTENT (0x20 * 2)
+
+#define NE_EN0_ISR (0x07 * 2)
+#define NE_EN0_DCFG (0x0e * 2)
+
+#define NE_EN0_RSARLO (0x08 * 2)
+#define NE_EN0_RSARHI (0x09 * 2)
+#define NE_EN0_RCNTLO (0x0a * 2)
+#define NE_EN0_RXCR (0x0c * 2)
+#define NE_EN0_TXCR (0x0d * 2)
+#define NE_EN0_RCNTHI (0x0b * 2)
+#define NE_EN0_IMR (0x0f * 2)
+
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+#define WORDSWAP(a) ((((a) >> 8) & 0xff) | ((a) << 8))
+
+static struct card_info {
+ zorro_id id;
+ const char *name;
+ unsigned int offset;
+} cards[] __devinitdata = {
+ { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, "Ariadne II", 0x0600 },
+ { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, "X-Surf", 0x8600 },
+};
+
+/* Hard reset the card. This used to pause for the same period that a
+ * 8390 reset command required, but that shouldn't be necessary.
+ */
+static void zorro8390_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+
+ if (ei_debug > 1)
+ netdev_dbg(dev, "resetting - t=%ld...\n", jiffies);
+
+ z_writeb(z_readb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+ ei_status.txing = 0;
+ ei_status.dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
+ netdev_warn(dev, "%s: did not complete\n", __func__);
+ break;
+ }
+ z_writeb(ENISR_RESET, NE_BASE + NE_EN0_ISR); /* Ack intr */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ * we don't need to be concerned with ring wrap as the header will be at
+ * the start of a page, so we optimize accordingly.
+ */
+static void zorro8390_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int nic_base = dev->base_addr;
+ int cnt;
+ short *ptrs;
+
+ /* This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+ z_writeb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
+ z_writeb(0, nic_base + NE_EN0_RCNTHI);
+ z_writeb(0, nic_base + NE_EN0_RSARLO); /* On page boundary */
+ z_writeb(ring_page, nic_base + NE_EN0_RSARHI);
+ z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ ptrs = (short *)hdr;
+ for (cnt = 0; cnt < sizeof(struct e8390_pkt_hdr) >> 1; cnt++)
+ *ptrs++ = z_readw(NE_BASE + NE_DATAPORT);
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
+
+ hdr->count = WORDSWAP(hdr->count);
+
+ ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver.
+ * If you are porting to a new ethercard, look at the packet driver source
+ * for hints. The NEx000 doesn't share the on-board packet memory --
+ * you have to put the packet out through the "remote DMA" dataport
+ * using z_writeb.
+ */
+static void zorro8390_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ int nic_base = dev->base_addr;
+ char *buf = skb->data;
+ short *ptrs;
+ int cnt;
+
+ /* This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+ z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+ z_writeb(count >> 8, nic_base + NE_EN0_RCNTHI);
+ z_writeb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
+ z_writeb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
+ z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ ptrs = (short *)buf;
+ for (cnt = 0; cnt < count >> 1; cnt++)
+ *ptrs++ = z_readw(NE_BASE + NE_DATAPORT);
+ if (count & 0x01)
+ buf[count - 1] = z_readb(NE_BASE + NE_DATAPORT);
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void zorro8390_block_output(struct net_device *dev, int count,
+ const unsigned char *buf,
+ const int start_page)
+{
+ int nic_base = NE_BASE;
+ unsigned long dma_start;
+ short *ptrs;
+ int cnt;
+
+ /* Round the count up for word writes. Do we need to do this?
+ * What effect will an odd byte count have on the 8390?
+ * I should check someday.
+ */
+ if (count & 0x01)
+ count++;
+
+ /* This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ z_writeb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+
+ /* Now the normal output. */
+ z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+ z_writeb(count >> 8, nic_base + NE_EN0_RCNTHI);
+ z_writeb(0x00, nic_base + NE_EN0_RSARLO);
+ z_writeb(start_page, nic_base + NE_EN0_RSARHI);
+
+ z_writeb(E8390_RWRITE + E8390_START, nic_base + NE_CMD);
+ ptrs = (short *)buf;
+ for (cnt = 0; cnt < count >> 1; cnt++)
+ z_writew(*ptrs++, NE_BASE + NE_DATAPORT);
+
+ dma_start = jiffies;
+
+ while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + 2 * HZ / 100)) {
+ /* 20ms */
+ netdev_err(dev, "timeout waiting for Tx RDC\n");
+ zorro8390_reset_8390(dev);
+ __NS8390_init(dev, 1);
+ break;
+ }
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
+ ei_status.dmaing &= ~0x01;
+}
+
+static int zorro8390_open(struct net_device *dev)
+{
+ __ei_open(dev);
+ return 0;
+}
+
+static int zorro8390_close(struct net_device *dev)
+{
+ if (ei_debug > 1)
+ netdev_dbg(dev, "Shutting down ethercard\n");
+ __ei_close(dev);
+ return 0;
+}
+
+static void __devexit zorro8390_remove_one(struct zorro_dev *z)
+{
+ struct net_device *dev = zorro_get_drvdata(z);
+
+ unregister_netdev(dev);
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ release_mem_region(ZTWO_PADDR(dev->base_addr), NE_IO_EXTENT * 2);
+ free_netdev(dev);
+}
+
+static struct zorro_device_id zorro8390_zorro_tbl[] __devinitdata = {
+ { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, },
+ { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(zorro, zorro8390_zorro_tbl);
+
+static const struct net_device_ops zorro8390_netdev_ops = {
+ .ndo_open = zorro8390_open,
+ .ndo_stop = zorro8390_close,
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static int __devinit zorro8390_init(struct net_device *dev,
+ unsigned long board, const char *name,
+ unsigned long ioaddr)
+{
+ int i;
+ int err;
+ unsigned char SA_prom[32];
+ int start_page, stop_page;
+ static u32 zorro8390_offsets[16] = {
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+ 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+ };
+
+ /* Reset card. Who knows what dain-bramaged state it was left in. */
+ {
+ unsigned long reset_start_time = jiffies;
+
+ z_writeb(z_readb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+ while ((z_readb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies,
+ reset_start_time + 2 * HZ / 100)) {
+ netdev_warn(dev, "not found (no reset ack)\n");
+ return -ENODEV;
+ }
+
+ z_writeb(0xff, ioaddr + NE_EN0_ISR); /* Ack all intr. */
+ }
+
+ /* Read the 16 bytes of station address PROM.
+ * We must first initialize registers,
+ * similar to NS8390_init(eifdev, 0).
+ * We can't reliably read the SAPROM address without this.
+ * (I learned the hard way!).
+ */
+ {
+ static const struct {
+ u32 value;
+ u32 offset;
+ } program_seq[] = {
+ {E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD},
+ /* Select page 0 */
+ {0x48, NE_EN0_DCFG}, /* 0x48: Set byte-wide access */
+ {0x00, NE_EN0_RCNTLO}, /* Clear the count regs */
+ {0x00, NE_EN0_RCNTHI},
+ {0x00, NE_EN0_IMR}, /* Mask completion irq */
+ {0xFF, NE_EN0_ISR},
+ {E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode */
+ {32, NE_EN0_RCNTLO},
+ {0x00, NE_EN0_RCNTHI},
+ {0x00, NE_EN0_RSARLO}, /* DMA starting at 0x0000 */
+ {0x00, NE_EN0_RSARHI},
+ {E8390_RREAD + E8390_START, NE_CMD},
+ };
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+ z_writeb(program_seq[i].value,
+ ioaddr + program_seq[i].offset);
+ }
+ for (i = 0; i < 16; i++) {
+ SA_prom[i] = z_readb(ioaddr + NE_DATAPORT);
+ (void)z_readb(ioaddr + NE_DATAPORT);
+ }
+
+ /* We must set the 8390 for word mode. */
+ z_writeb(0x49, ioaddr + NE_EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+
+ dev->base_addr = ioaddr;
+ dev->irq = IRQ_AMIGA_PORTS;
+
+ /* Install the Interrupt handler */
+ i = request_irq(IRQ_AMIGA_PORTS, __ei_interrupt,
+ IRQF_SHARED, DRV_NAME, dev);
+ if (i)
+ return i;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = SA_prom[i];
+
+ pr_debug("Found ethernet address: %pM\n", dev->dev_addr);
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = 1;
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+
+ ei_status.reset_8390 = zorro8390_reset_8390;
+ ei_status.block_input = zorro8390_block_input;
+ ei_status.block_output = zorro8390_block_output;
+ ei_status.get_8390_hdr = zorro8390_get_8390_hdr;
+ ei_status.reg_offset = zorro8390_offsets;
+
+ dev->netdev_ops = &zorro8390_netdev_ops;
+ __NS8390_init(dev, 0);
+ err = register_netdev(dev);
+ if (err) {
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ return err;
+ }
+
+ netdev_info(dev, "%s at 0x%08lx, Ethernet Address %pM\n",
+ name, board, dev->dev_addr);
+
+ return 0;
+}
+
+static int __devinit zorro8390_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent)
+{
+ struct net_device *dev;
+ unsigned long board, ioaddr;
+ int err, i;
+
+ for (i = ARRAY_SIZE(cards) - 1; i >= 0; i--)
+ if (z->id == cards[i].id)
+ break;
+ if (i < 0)
+ return -ENODEV;
+
+ board = z->resource.start;
+ ioaddr = board + cards[i].offset;
+ dev = ____alloc_ei_netdev(0);
+ if (!dev)
+ return -ENOMEM;
+ if (!request_mem_region(ioaddr, NE_IO_EXTENT * 2, DRV_NAME)) {
+ free_netdev(dev);
+ return -EBUSY;
+ }
+ err = zorro8390_init(dev, board, cards[i].name, ZTWO_VADDR(ioaddr));
+ if (err) {
+ release_mem_region(ioaddr, NE_IO_EXTENT * 2);
+ free_netdev(dev);
+ return err;
+ }
+ zorro_set_drvdata(z, dev);
+ return 0;
+}
+
+static struct zorro_driver zorro8390_driver = {
+ .name = "zorro8390",
+ .id_table = zorro8390_zorro_tbl,
+ .probe = zorro8390_init_one,
+ .remove = __devexit_p(zorro8390_remove_one),
+};
+
+static int __init zorro8390_init_module(void)
+{
+ return zorro_register_driver(&zorro8390_driver);
+}
+
+static void __exit zorro8390_cleanup_module(void)
+{
+ zorro_unregister_driver(&zorro8390_driver);
+}
+
+module_init(zorro8390_init_module);
+module_exit(zorro8390_cleanup_module);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
new file mode 100644
index 000000000000..597f4d45c632
--- /dev/null
+++ b/drivers/net/ethernet/Kconfig
@@ -0,0 +1,178 @@
+#
+# Ethernet LAN device configuration
+#
+
+menuconfig ETHERNET
+ bool "Ethernet driver support"
+ depends on NET
+ default y
+ ---help---
+ This section contains all the Ethernet device drivers.
+
+if ETHERNET
+
+config MDIO
+ tristate
+
+config SUNGEM_PHY
+ tristate
+
+source "drivers/net/ethernet/3com/Kconfig"
+source "drivers/net/ethernet/adaptec/Kconfig"
+source "drivers/net/ethernet/aeroflex/Kconfig"
+source "drivers/net/ethernet/alteon/Kconfig"
+source "drivers/net/ethernet/amd/Kconfig"
+source "drivers/net/ethernet/apple/Kconfig"
+source "drivers/net/ethernet/atheros/Kconfig"
+source "drivers/net/ethernet/cadence/Kconfig"
+source "drivers/net/ethernet/adi/Kconfig"
+source "drivers/net/ethernet/broadcom/Kconfig"
+source "drivers/net/ethernet/brocade/Kconfig"
+source "drivers/net/ethernet/chelsio/Kconfig"
+source "drivers/net/ethernet/cirrus/Kconfig"
+source "drivers/net/ethernet/cisco/Kconfig"
+source "drivers/net/ethernet/davicom/Kconfig"
+
+config DNET
+ tristate "Dave ethernet support (DNET)"
+ depends on HAS_IOMEM
+ select PHYLIB
+ ---help---
+ The Dave ethernet interface (DNET) is found on Qong Board FPGA.
+ Say Y to include support for the DNET chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called dnet.
+
+source "drivers/net/ethernet/dec/Kconfig"
+source "drivers/net/ethernet/dlink/Kconfig"
+source "drivers/net/ethernet/emulex/Kconfig"
+source "drivers/net/ethernet/neterion/Kconfig"
+source "drivers/net/ethernet/faraday/Kconfig"
+source "drivers/net/ethernet/freescale/Kconfig"
+source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hp/Kconfig"
+source "drivers/net/ethernet/ibm/Kconfig"
+source "drivers/net/ethernet/intel/Kconfig"
+source "drivers/net/ethernet/i825xx/Kconfig"
+source "drivers/net/ethernet/xscale/Kconfig"
+source "drivers/net/ethernet/icplus/Kconfig"
+
+config JME
+ tristate "JMicron(R) PCI-Express Gigabit Ethernet support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports the PCI-Express gigabit ethernet adapters
+ based on JMicron JMC250 chipset.
+
+ To compile this driver as a module, choose M here. The module
+ will be called jme.
+
+config KORINA
+ tristate "Korina (IDT RC32434) Ethernet support"
+ depends on MIKROTIK_RB532
+ ---help---
+ If you have a Mikrotik RouterBoard 500 or IDT RC32434
+ based system say Y. Otherwise say N.
+
+config LANTIQ_ETOP
+ tristate "Lantiq SoC ETOP driver"
+ depends on SOC_TYPE_XWAY
+ ---help---
+ Support for the MII0 inside the Lantiq SoC
+
+source "drivers/net/ethernet/marvell/Kconfig"
+source "drivers/net/ethernet/mellanox/Kconfig"
+source "drivers/net/ethernet/micrel/Kconfig"
+source "drivers/net/ethernet/microchip/Kconfig"
+
+config MIPS_SIM_NET
+ tristate "MIPS simulator Network device"
+ depends on MIPS_SIM
+ ---help---
+ The MIPSNET device is a simple Ethernet network device which is
+ emulated by the MIPS Simulator.
+ If you are not using a MIPSsim or are unsure, say N.
+
+source "drivers/net/ethernet/myricom/Kconfig"
+
+config FEALNX
+ tristate "Myson MTD-8xx PCI Ethernet support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ Say Y here to support the Myson MTD-800 family of PCI-based Ethernet
+ cards. <http://www.myson.com.tw/>
+
+source "drivers/net/ethernet/natsemi/Kconfig"
+source "drivers/net/ethernet/8390/Kconfig"
+
+config NET_NETX
+ tristate "NetX Ethernet support"
+ select NET_CORE
+ select MII
+ depends on ARCH_NETX
+ ---help---
+ This is support for the Hilscher netX builtin Ethernet ports
+
+ To compile this driver as a module, choose M here. The module
+ will be called netx-eth.
+
+source "drivers/net/ethernet/nuvoton/Kconfig"
+source "drivers/net/ethernet/nvidia/Kconfig"
+source "drivers/net/ethernet/octeon/Kconfig"
+source "drivers/net/ethernet/oki-semi/Kconfig"
+
+config ETHOC
+ tristate "OpenCores 10/100 Mbps Ethernet MAC support"
+ depends on HAS_IOMEM && HAS_DMA
+ select NET_CORE
+ select MII
+ select PHYLIB
+ select CRC32
+ select BITREVERSE
+ ---help---
+ Say Y here if you want to use the OpenCores 10/100 Mbps Ethernet MAC.
+
+source "drivers/net/ethernet/packetengines/Kconfig"
+source "drivers/net/ethernet/pasemi/Kconfig"
+source "drivers/net/ethernet/qlogic/Kconfig"
+source "drivers/net/ethernet/racal/Kconfig"
+source "drivers/net/ethernet/realtek/Kconfig"
+source "drivers/net/ethernet/renesas/Kconfig"
+source "drivers/net/ethernet/rdc/Kconfig"
+
+config S6GMAC
+ tristate "S6105 GMAC ethernet support"
+ depends on XTENSA_VARIANT_S6000
+ select PHYLIB
+ ---help---
+ This driver supports the on chip ethernet device on the
+ S6105 xtensa processor.
+
+ To compile this driver as a module, choose M here. The module
+ will be called s6gmac.
+
+source "drivers/net/ethernet/seeq/Kconfig"
+source "drivers/net/ethernet/silan/Kconfig"
+source "drivers/net/ethernet/sis/Kconfig"
+source "drivers/net/ethernet/sfc/Kconfig"
+source "drivers/net/ethernet/sgi/Kconfig"
+source "drivers/net/ethernet/smsc/Kconfig"
+source "drivers/net/ethernet/stmicro/Kconfig"
+source "drivers/net/ethernet/sun/Kconfig"
+source "drivers/net/ethernet/tehuti/Kconfig"
+source "drivers/net/ethernet/ti/Kconfig"
+source "drivers/net/ethernet/tile/Kconfig"
+source "drivers/net/ethernet/toshiba/Kconfig"
+source "drivers/net/ethernet/tundra/Kconfig"
+source "drivers/net/ethernet/via/Kconfig"
+source "drivers/net/ethernet/xilinx/Kconfig"
+source "drivers/net/ethernet/xircom/Kconfig"
+
+endif # ETHERNET
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
new file mode 100644
index 000000000000..be5dde040261
--- /dev/null
+++ b/drivers/net/ethernet/Makefile
@@ -0,0 +1,75 @@
+#
+# Makefile for the Linux network Ethernet device drivers.
+#
+
+obj-$(CONFIG_NET_VENDOR_3COM) += 3com/
+obj-$(CONFIG_NET_VENDOR_8390) += 8390/
+obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/
+obj-$(CONFIG_GRETH) += aeroflex/
+obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/
+obj-$(CONFIG_NET_VENDOR_AMD) += amd/
+obj-$(CONFIG_NET_VENDOR_APPLE) += apple/
+obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/
+obj-$(CONFIG_NET_ATMEL) += cadence/
+obj-$(CONFIG_NET_BFIN) += adi/
+obj-$(CONFIG_NET_VENDOR_BROADCOM) += broadcom/
+obj-$(CONFIG_NET_VENDOR_BROCADE) += brocade/
+obj-$(CONFIG_NET_VENDOR_CHELSIO) += chelsio/
+obj-$(CONFIG_NET_VENDOR_CIRRUS) += cirrus/
+obj-$(CONFIG_NET_VENDOR_CISCO) += cisco/
+obj-$(CONFIG_DM9000) += davicom/
+obj-$(CONFIG_DNET) += dnet.o
+obj-$(CONFIG_NET_VENDOR_DEC) += dec/
+obj-$(CONFIG_NET_VENDOR_DLINK) += dlink/
+obj-$(CONFIG_NET_VENDOR_EMULEX) += emulex/
+obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
+obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
+obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
+obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HP) += hp/
+obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
+obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
+obj-$(CONFIG_NET_VENDOR_I825XX) += i825xx/
+obj-$(CONFIG_NET_VENDOR_XSCALE) += xscale/
+obj-$(CONFIG_IP1000) += icplus/
+obj-$(CONFIG_JME) += jme.o
+obj-$(CONFIG_KORINA) += korina.o
+obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
+obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
+obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
+obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
+obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_MIPS_SIM_NET) += mipsnet.o
+obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
+obj-$(CONFIG_FEALNX) += fealnx.o
+obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
+obj-$(CONFIG_NET_NETX) += netx-eth.o
+obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/
+obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/
+obj-$(CONFIG_OCTEON_MGMT_ETHERNET) += octeon/
+obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/
+obj-$(CONFIG_ETHOC) += ethoc.o
+obj-$(CONFIG_NET_PACKET_ENGINE) += packetengines/
+obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/
+obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/
+obj-$(CONFIG_NET_VENDOR_RACAL) += racal/
+obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
+obj-$(CONFIG_SH_ETH) += renesas/
+obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
+obj-$(CONFIG_S6GMAC) += s6gmac.o
+obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
+obj-$(CONFIG_NET_VENDOR_SILAN) += silan/
+obj-$(CONFIG_NET_VENDOR_SIS) += sis/
+obj-$(CONFIG_SFC) += sfc/
+obj-$(CONFIG_NET_VENDOR_SGI) += sgi/
+obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/
+obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/
+obj-$(CONFIG_NET_VENDOR_SUN) += sun/
+obj-$(CONFIG_NET_VENDOR_TEHUTI) += tehuti/
+obj-$(CONFIG_NET_VENDOR_TI) += ti/
+obj-$(CONFIG_TILE_NET) += tile/
+obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/
+obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/
+obj-$(CONFIG_NET_VENDOR_VIA) += via/
+obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/
+obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
diff --git a/drivers/net/ethernet/adaptec/Kconfig b/drivers/net/ethernet/adaptec/Kconfig
new file mode 100644
index 000000000000..0bff571b1bb3
--- /dev/null
+++ b/drivers/net/ethernet/adaptec/Kconfig
@@ -0,0 +1,36 @@
+#
+# Adaptec network device configuration
+#
+
+config NET_VENDOR_ADAPTEC
+ bool "Adaptec devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Adaptec cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_ADAPTEC
+
+config ADAPTEC_STARFIRE
+ tristate "Adaptec Starfire/DuraLAN support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ Say Y here if you have an Adaptec Starfire (or DuraLAN) PCI network
+ adapter. The DuraLAN chip is used on the 64 bit PCI boards from
+ Adaptec e.g. the ANA-6922A. The older 32 bit boards use the tulip
+ driver.
+
+ To compile this driver as a module, choose M here: the module
+ will be called starfire. This is recommended.
+
+endif # NET_VENDOR_ADAPTEC
diff --git a/drivers/net/ethernet/adaptec/Makefile b/drivers/net/ethernet/adaptec/Makefile
new file mode 100644
index 000000000000..6c07b758ac0a
--- /dev/null
+++ b/drivers/net/ethernet/adaptec/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Adaptec network device drivers.
+#
+
+obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
diff --git a/drivers/net/starfire.c b/drivers/net/ethernet/adaptec/starfire.c
index 36045f3b0327..6d9f6911000f 100644
--- a/drivers/net/starfire.c
+++ b/drivers/net/ethernet/adaptec/starfire.c
@@ -30,6 +30,7 @@
#define DRV_VERSION "2.1"
#define DRV_RELDATE "July 6, 2008"
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
@@ -560,7 +561,7 @@ struct netdev_private {
struct net_device *dev;
struct pci_dev *pci_dev;
#ifdef VLAN_SUPPORT
- struct vlan_group *vlgrp;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
#endif
void *queue_mem;
dma_addr_t queue_mem_dma;
@@ -606,18 +607,6 @@ static const struct ethtool_ops ethtool_ops;
#ifdef VLAN_SUPPORT
-static void netdev_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
-{
- struct netdev_private *np = netdev_priv(dev);
-
- spin_lock(&np->lock);
- if (debug > 2)
- printk("%s: Setting vlgrp to %p\n", dev->name, grp);
- np->vlgrp = grp;
- set_rx_mode(dev);
- spin_unlock(&np->lock);
-}
-
static void netdev_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
{
struct netdev_private *np = netdev_priv(dev);
@@ -625,6 +614,7 @@ static void netdev_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
spin_lock(&np->lock);
if (debug > 1)
printk("%s: Adding vlanid %d to vlan filter\n", dev->name, vid);
+ set_bit(vid, np->active_vlans);
set_rx_mode(dev);
spin_unlock(&np->lock);
}
@@ -636,7 +626,7 @@ static void netdev_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
spin_lock(&np->lock);
if (debug > 1)
printk("%s: removing vlanid %d from vlan filter\n", dev->name, vid);
- vlan_group_set_device(np->vlgrp, vid, NULL);
+ clear_bit(vid, np->active_vlans);
set_rx_mode(dev);
spin_unlock(&np->lock);
}
@@ -647,15 +637,14 @@ static const struct net_device_ops netdev_ops = {
.ndo_open = netdev_open,
.ndo_stop = netdev_close,
.ndo_start_xmit = start_tx,
- .ndo_tx_timeout = tx_timeout,
- .ndo_get_stats = get_stats,
- .ndo_set_multicast_list = &set_rx_mode,
- .ndo_do_ioctl = netdev_ioctl,
+ .ndo_tx_timeout = tx_timeout,
+ .ndo_get_stats = get_stats,
+ .ndo_set_rx_mode = set_rx_mode,
+ .ndo_do_ioctl = netdev_ioctl,
.ndo_change_mtu = eth_change_mtu,
- .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
#ifdef VLAN_SUPPORT
- .ndo_vlan_rx_register = netdev_vlan_rx_register,
.ndo_vlan_rx_add_vid = netdev_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = netdev_vlan_rx_kill_vid,
#endif
@@ -1267,10 +1256,13 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
np->tx_info[entry].mapping =
pci_map_single(np->pci_dev, skb->data, skb_first_frag_len(skb), PCI_DMA_TODEVICE);
} else {
- skb_frag_t *this_frag = &skb_shinfo(skb)->frags[i - 1];
- status |= this_frag->size;
+ const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[i - 1];
+ status |= skb_frag_size(this_frag);
np->tx_info[entry].mapping =
- pci_map_single(np->pci_dev, page_address(this_frag->page) + this_frag->page_offset, this_frag->size, PCI_DMA_TODEVICE);
+ pci_map_single(np->pci_dev,
+ skb_frag_address(this_frag),
+ skb_frag_size(this_frag),
+ PCI_DMA_TODEVICE);
}
np->tx_ring[entry].addr = cpu_to_dma(np->tx_info[entry].mapping);
@@ -1386,7 +1378,7 @@ static irqreturn_t intr_handler(int irq, void *dev_instance)
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
pci_unmap_single(np->pci_dev,
np->tx_info[entry].mapping,
- skb_shinfo(skb)->frags[i].size,
+ skb_frag_size(&skb_shinfo(skb)->frags[i]),
PCI_DMA_TODEVICE);
np->dirty_tx++;
entry++;
@@ -1527,21 +1519,17 @@ static int __netdev_rx(struct net_device *dev, int *quota)
printk(KERN_DEBUG "%s: checksum_hw, status2 = %#x\n", dev->name, le16_to_cpu(desc->status2));
}
#ifdef VLAN_SUPPORT
- if (np->vlgrp && le16_to_cpu(desc->status2) & 0x0200) {
+ if (le16_to_cpu(desc->status2) & 0x0200) {
u16 vlid = le16_to_cpu(desc->vlanid);
if (debug > 4) {
printk(KERN_DEBUG " netdev_rx() vlanid = %d\n",
vlid);
}
- /*
- * vlan_hwaccel_rx expects a packet with the VLAN tag
- * stripped out.
- */
- vlan_hwaccel_rx(skb, np->vlgrp, vlid);
- } else
+ __vlan_hwaccel_put_tag(skb, vlid);
+ }
#endif /* VLAN_SUPPORT */
- netif_receive_skb(skb);
+ netif_receive_skb(skb);
dev->stats.rx_packets++;
next_rx:
@@ -1751,6 +1739,32 @@ static struct net_device_stats *get_stats(struct net_device *dev)
return &dev->stats;
}
+#ifdef VLAN_SUPPORT
+static u32 set_vlan_mode(struct netdev_private *np)
+{
+ u32 ret = VlanMode;
+ u16 vid;
+ void __iomem *filter_addr = np->base + HashTable + 8;
+ int vlan_count = 0;
+
+ for_each_set_bit(vid, np->active_vlans, VLAN_N_VID) {
+ if (vlan_count == 32)
+ break;
+ writew(vid, filter_addr);
+ filter_addr += 16;
+ vlan_count++;
+ }
+ if (vlan_count == 32) {
+ ret |= PerfectFilterVlan;
+ while (vlan_count < 32) {
+ writew(0, filter_addr);
+ filter_addr += 16;
+ vlan_count++;
+ }
+ }
+ return ret;
+}
+#endif /* VLAN_SUPPORT */
static void set_rx_mode(struct net_device *dev)
{
@@ -1759,30 +1773,9 @@ static void set_rx_mode(struct net_device *dev)
u32 rx_mode = MinVLANPrio;
struct netdev_hw_addr *ha;
int i;
-#ifdef VLAN_SUPPORT
- rx_mode |= VlanMode;
- if (np->vlgrp) {
- int vlan_count = 0;
- void __iomem *filter_addr = ioaddr + HashTable + 8;
- for (i = 0; i < VLAN_VID_MASK; i++) {
- if (vlan_group_get_device(np->vlgrp, i)) {
- if (vlan_count >= 32)
- break;
- writew(i, filter_addr);
- filter_addr += 16;
- vlan_count++;
- }
- }
- if (i == VLAN_VID_MASK) {
- rx_mode |= PerfectFilterVlan;
- while (vlan_count < 32) {
- writew(0, filter_addr);
- filter_addr += 16;
- vlan_count++;
- }
- }
- }
+#ifdef VLAN_SUPPORT
+ rx_mode |= set_vlan_mode(np);
#endif /* VLAN_SUPPORT */
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig
new file mode 100644
index 000000000000..49a30d37ae4a
--- /dev/null
+++ b/drivers/net/ethernet/adi/Kconfig
@@ -0,0 +1,69 @@
+#
+# Blackfin device configuration
+#
+
+config NET_BFIN
+ bool "Blackfin devices"
+ depends on BF516 || BF518 || BF526 || BF527 || BF536 || BF537
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y.
+ Make sure you know the name of your card. Read the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ If unsure, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the remaining Blackfin card questions. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_BFIN
+
+config BFIN_MAC
+ tristate "Blackfin on-chip MAC support"
+ depends on (BF516 || BF518 || BF526 || BF527 || BF536 || BF537)
+ select CRC32
+ select NET_CORE
+ select MII
+ select PHYLIB
+ select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
+ ---help---
+ This is the driver for Blackfin on-chip mac device. Say Y if you want
+ it compiled into the kernel. This driver is also available as a
+ module ( = code which can be inserted in and removed from the running
+ kernel whenever you want). The module will be called bfin_mac.
+
+config BFIN_MAC_USE_L1
+ bool "Use L1 memory for rx/tx packets"
+ depends on BFIN_MAC && (BF527 || BF537)
+ default y
+ ---help---
+ To get maximum network performance, you should use L1 memory as rx/tx
+ buffers. Say N here if you want to reserve L1 memory for other uses.
+
+config BFIN_TX_DESC_NUM
+ int "Number of transmit buffer packets"
+ depends on BFIN_MAC
+ range 6 10 if BFIN_MAC_USE_L1
+ range 10 100
+ default "10"
+ ---help---
+ Set the number of buffer packets used in driver.
+
+config BFIN_RX_DESC_NUM
+ int "Number of receive buffer packets"
+ depends on BFIN_MAC
+ range 20 100 if BFIN_MAC_USE_L1
+ range 20 800
+ default "20"
+ ---help---
+ Set the number of buffer packets used in driver.
+
+config BFIN_MAC_USE_HWSTAMP
+ bool "Use IEEE 1588 hwstamp"
+ depends on BFIN_MAC && BF518
+ default y
+ ---help---
+ To support the IEEE 1588 Precision Time Protocol (PTP), select y here
+
+endif # NET_BFIN
diff --git a/drivers/net/ethernet/adi/Makefile b/drivers/net/ethernet/adi/Makefile
new file mode 100644
index 000000000000..b1fbe195d0e8
--- /dev/null
+++ b/drivers/net/ethernet/adi/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Blackfin device drivers.
+#
+
+obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
diff --git a/drivers/net/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c
index 6c019e148546..b6d69c91db96 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/ethernet/adi/bfin_mac.c
@@ -1449,7 +1449,7 @@ static const struct net_device_ops bfin_mac_netdev_ops = {
.ndo_start_xmit = bfin_mac_hard_start_xmit,
.ndo_set_mac_address = bfin_mac_set_mac_address,
.ndo_tx_timeout = bfin_mac_timeout,
- .ndo_set_multicast_list = bfin_mac_set_multicast_list,
+ .ndo_set_rx_mode = bfin_mac_set_multicast_list,
.ndo_do_ioctl = bfin_mac_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/bfin_mac.h b/drivers/net/ethernet/adi/bfin_mac.h
index f8559ac9a403..f8559ac9a403 100644
--- a/drivers/net/bfin_mac.h
+++ b/drivers/net/ethernet/adi/bfin_mac.h
diff --git a/drivers/net/ethernet/aeroflex/Kconfig b/drivers/net/ethernet/aeroflex/Kconfig
new file mode 100644
index 000000000000..4f4a8d78fd54
--- /dev/null
+++ b/drivers/net/ethernet/aeroflex/Kconfig
@@ -0,0 +1,11 @@
+#
+# Aeroflex Gaisler network device configuration
+#
+
+config GRETH
+ tristate "Aeroflex Gaisler GRETH Ethernet MAC support"
+ depends on SPARC
+ select PHYLIB
+ select CRC32
+ ---help---
+ Say Y here if you want to use the Aeroflex Gaisler GRETH Ethernet MAC.
diff --git a/drivers/net/ethernet/aeroflex/Makefile b/drivers/net/ethernet/aeroflex/Makefile
new file mode 100644
index 000000000000..6e62a679282f
--- /dev/null
+++ b/drivers/net/ethernet/aeroflex/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Aeroflex Gaisler network device drivers.
+#
+
+obj-$(CONFIG_GRETH) += greth.o
diff --git a/drivers/net/greth.c b/drivers/net/ethernet/aeroflex/greth.c
index 672f096fe090..442fefa4f2ca 100644
--- a/drivers/net/greth.c
+++ b/drivers/net/ethernet/aeroflex/greth.c
@@ -22,9 +22,11 @@
* Marko Isomaki
*/
+#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
@@ -111,9 +113,8 @@ static void greth_print_tx_packet(struct sk_buff *skb)
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
print_hex_dump(KERN_DEBUG, "TX: ", DUMP_PREFIX_OFFSET, 16, 1,
- phys_to_virt(page_to_phys(skb_shinfo(skb)->frags[i].page)) +
- skb_shinfo(skb)->frags[i].page_offset,
- length, true);
+ skb_frag_address(&skb_shinfo(skb)->frags[i]),
+ skb_shinfo(skb)->frags[i].size, true);
}
}
@@ -197,7 +198,7 @@ static void greth_clean_rings(struct greth_private *greth)
dma_unmap_page(greth->dev,
greth_read_bd(&tx_bdp->addr),
- frag->size,
+ skb_frag_size(frag),
DMA_TO_DEVICE);
greth->tx_last = NEXT_TX(greth->tx_last);
@@ -426,6 +427,7 @@ greth_start_xmit(struct sk_buff *skb, struct net_device *dev)
dma_sync_single_for_device(greth->dev, dma_addr, skb->len, DMA_TO_DEVICE);
status = GRETH_BD_EN | GRETH_BD_IE | (skb->len & GRETH_BD_LEN);
+ greth->tx_bufs_length[greth->tx_next] = skb->len & GRETH_BD_LEN;
/* Wrap around descriptor ring */
if (greth->tx_next == GRETH_TXBD_NUM_MASK) {
@@ -488,7 +490,8 @@ greth_start_xmit_gbit(struct sk_buff *skb, struct net_device *dev)
if (nr_frags != 0)
status = GRETH_TXBD_MORE;
- status |= GRETH_TXBD_CSALL;
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ status |= GRETH_TXBD_CSALL;
status |= skb_headlen(skb) & GRETH_BD_LEN;
if (greth->tx_next == GRETH_TXBD_NUM_MASK)
status |= GRETH_BD_WR;
@@ -511,8 +514,10 @@ greth_start_xmit_gbit(struct sk_buff *skb, struct net_device *dev)
greth->tx_skbuff[curr_tx] = NULL;
bdp = greth->tx_bd_base + curr_tx;
- status = GRETH_TXBD_CSALL | GRETH_BD_EN;
- status |= frag->size & GRETH_BD_LEN;
+ status = GRETH_BD_EN;
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ status |= GRETH_TXBD_CSALL;
+ status |= skb_frag_size(frag) & GRETH_BD_LEN;
/* Wrap around descriptor ring */
if (curr_tx == GRETH_TXBD_NUM_MASK)
@@ -526,11 +531,8 @@ greth_start_xmit_gbit(struct sk_buff *skb, struct net_device *dev)
greth_write_bd(&bdp->stat, status);
- dma_addr = dma_map_page(greth->dev,
- frag->page,
- frag->page_offset,
- frag->size,
- DMA_TO_DEVICE);
+ dma_addr = skb_frag_dma_map(greth->dev, frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(greth->dev, dma_addr)))
goto frag_map_error;
@@ -639,6 +641,7 @@ static void greth_clean_tx(struct net_device *dev)
dev->stats.tx_fifo_errors++;
}
dev->stats.tx_packets++;
+ dev->stats.tx_bytes += greth->tx_bufs_length[greth->tx_last];
greth->tx_last = NEXT_TX(greth->tx_last);
greth->tx_free++;
}
@@ -693,6 +696,7 @@ static void greth_clean_tx_gbit(struct net_device *dev)
greth->tx_skbuff[greth->tx_last] = NULL;
greth_update_tx_stats(dev, stat);
+ dev->stats.tx_bytes += skb->len;
bdp = greth->tx_bd_base + greth->tx_last;
@@ -709,7 +713,7 @@ static void greth_clean_tx_gbit(struct net_device *dev)
dma_unmap_page(greth->dev,
greth_read_bd(&bdp->addr),
- frag->size,
+ skb_frag_size(frag),
DMA_TO_DEVICE);
greth->tx_last = NEXT_TX(greth->tx_last);
@@ -794,6 +798,7 @@ static int greth_rx(struct net_device *dev, int limit)
memcpy(skb_put(skb, pkt_len), phys_to_virt(dma_addr), pkt_len);
skb->protocol = eth_type_trans(skb, dev);
+ dev->stats.rx_bytes += pkt_len;
dev->stats.rx_packets++;
netif_receive_skb(skb);
}
@@ -908,6 +913,7 @@ static int greth_rx_gbit(struct net_device *dev, int limit)
skb->protocol = eth_type_trans(skb, dev);
dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
netif_receive_skb(skb);
greth->rx_skbuff[greth->rx_cur] = newskb;
@@ -1537,7 +1543,7 @@ static int __devinit greth_of_probe(struct platform_device *ofdev)
}
if (greth->multicast) {
- greth_netdev_ops.ndo_set_multicast_list = greth_set_multicast_list;
+ greth_netdev_ops.ndo_set_rx_mode = greth_set_multicast_list;
dev->flags |= IFF_MULTICAST;
} else {
dev->flags &= ~IFF_MULTICAST;
diff --git a/drivers/net/greth.h b/drivers/net/ethernet/aeroflex/greth.h
index 9a0040dee4da..232a622a85b7 100644
--- a/drivers/net/greth.h
+++ b/drivers/net/ethernet/aeroflex/greth.h
@@ -103,6 +103,7 @@ struct greth_private {
unsigned char *tx_bufs[GRETH_TXBD_NUM];
unsigned char *rx_bufs[GRETH_RXBD_NUM];
+ u16 tx_bufs_length[GRETH_TXBD_NUM];
u16 tx_next;
u16 tx_last;
diff --git a/drivers/net/ethernet/alteon/Kconfig b/drivers/net/ethernet/alteon/Kconfig
new file mode 100644
index 000000000000..799a85282070
--- /dev/null
+++ b/drivers/net/ethernet/alteon/Kconfig
@@ -0,0 +1,48 @@
+#
+# Alteon network device configuration
+#
+
+config NET_VENDOR_ALTEON
+ bool "Alteon devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Alteon cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_ALTEON
+
+config ACENIC
+ tristate "Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support"
+ depends on PCI
+ ---help---
+ Say Y here if you have an Alteon AceNIC, 3Com 3C985(B), NetGear
+ GA620, SGI Gigabit or Farallon PN9000-SX PCI Gigabit Ethernet
+ adapter. The driver allows for using the Jumbo Frame option (9000
+ bytes/frame) however it requires that your switches can handle this
+ as well. To enable Jumbo Frames, add `mtu 9000' to your ifconfig
+ line.
+
+ To compile this driver as a module, choose M here: the
+ module will be called acenic.
+
+config ACENIC_OMIT_TIGON_I
+ bool "Omit support for old Tigon I based AceNICs"
+ depends on ACENIC
+ ---help---
+ Say Y here if you only have Tigon II based AceNICs and want to leave
+ out support for the older Tigon I based cards which are no longer
+ being sold (ie. the original Alteon AceNIC and 3Com 3C985 (non B
+ version)). This will reduce the size of the driver object by
+ app. 100KB. If you are not sure whether your card is a Tigon I or a
+ Tigon II, say N here.
+
+ The safe and default value for this is N.
+
+endif # NET_VENDOR_ALTEON
diff --git a/drivers/net/ethernet/alteon/Makefile b/drivers/net/ethernet/alteon/Makefile
new file mode 100644
index 000000000000..a2ca173f2a50
--- /dev/null
+++ b/drivers/net/ethernet/alteon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Alteon network device drivers.
+#
+
+obj-$(CONFIG_ACENIC) += acenic.o
diff --git a/drivers/net/acenic.c b/drivers/net/ethernet/alteon/acenic.c
index d7c1bfe4b6ec..f872748ab4e6 100644
--- a/drivers/net/acenic.c
+++ b/drivers/net/ethernet/alteon/acenic.c
@@ -69,10 +69,7 @@
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/prefetch.h>
-
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
#include <linux/if_vlan.h>
-#endif
#ifdef SIOCETHTOOL
#include <linux/ethtool.h>
@@ -171,15 +168,6 @@ MODULE_DEVICE_TABLE(pci, acenic_pci_tbl);
#define BOARD_IDX_STATIC 0
#define BOARD_IDX_OVERFLOW -1
-#if (defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)) && \
- defined(NETIF_F_HW_VLAN_RX)
-#define ACENIC_DO_VLAN 1
-#define ACE_RCB_VLAN_FLAG RCB_FLG_VLAN_ASSIST
-#else
-#define ACENIC_DO_VLAN 0
-#define ACE_RCB_VLAN_FLAG 0
-#endif
-
#include "acenic.h"
/*
@@ -461,13 +449,10 @@ static const struct net_device_ops ace_netdev_ops = {
.ndo_tx_timeout = ace_watchdog,
.ndo_get_stats = ace_get_stats,
.ndo_start_xmit = ace_start_xmit,
- .ndo_set_multicast_list = ace_set_multicast_list,
+ .ndo_set_rx_mode = ace_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = ace_set_mac_addr,
.ndo_change_mtu = ace_change_mtu,
-#if ACENIC_DO_VLAN
- .ndo_vlan_rx_register = ace_vlan_rx_register,
-#endif
};
static int __devinit acenic_probe_one(struct pci_dev *pdev,
@@ -491,9 +476,7 @@ static int __devinit acenic_probe_one(struct pci_dev *pdev,
ap->name = pci_name(pdev);
dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
-#if ACENIC_DO_VLAN
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-#endif
dev->watchdog_timeo = 5*HZ;
@@ -1248,7 +1231,7 @@ static int __devinit ace_init(struct net_device *dev)
set_aceaddr(&info->rx_std_ctrl.rngptr, ap->rx_ring_base_dma);
info->rx_std_ctrl.max_len = ACE_STD_BUFSIZE;
info->rx_std_ctrl.flags =
- RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | ACE_RCB_VLAN_FLAG;
+ RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | RCB_FLG_VLAN_ASSIST;
memset(ap->rx_std_ring, 0,
RX_STD_RING_ENTRIES * sizeof(struct rx_desc));
@@ -1264,7 +1247,7 @@ static int __devinit ace_init(struct net_device *dev)
(sizeof(struct rx_desc) * RX_STD_RING_ENTRIES)));
info->rx_jumbo_ctrl.max_len = 0;
info->rx_jumbo_ctrl.flags =
- RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | ACE_RCB_VLAN_FLAG;
+ RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | RCB_FLG_VLAN_ASSIST;
memset(ap->rx_jumbo_ring, 0,
RX_JUMBO_RING_ENTRIES * sizeof(struct rx_desc));
@@ -1286,7 +1269,7 @@ static int __devinit ace_init(struct net_device *dev)
RX_JUMBO_RING_ENTRIES))));
info->rx_mini_ctrl.max_len = ACE_MINI_SIZE;
info->rx_mini_ctrl.flags =
- RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR|ACE_RCB_VLAN_FLAG;
+ RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR|RCB_FLG_VLAN_ASSIST;
for (i = 0; i < RX_MINI_RING_ENTRIES; i++)
ap->rx_mini_ring[i].flags =
@@ -1332,7 +1315,7 @@ static int __devinit ace_init(struct net_device *dev)
}
info->tx_ctrl.max_len = ACE_TX_RING_ENTRIES(ap);
- tmp = RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | ACE_RCB_VLAN_FLAG;
+ tmp = RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | RCB_FLG_VLAN_ASSIST;
/*
* The Tigon I does not like having the TX ring in host memory ;-(
@@ -1519,13 +1502,13 @@ static int __devinit ace_init(struct net_device *dev)
* firmware to wipe the ring without re-initializing it.
*/
if (!test_and_set_bit(0, &ap->std_refill_busy))
- ace_load_std_rx_ring(ap, RX_RING_SIZE);
+ ace_load_std_rx_ring(dev, RX_RING_SIZE);
else
printk(KERN_ERR "%s: Someone is busy refilling the RX ring\n",
ap->name);
if (ap->version >= 2) {
if (!test_and_set_bit(0, &ap->mini_refill_busy))
- ace_load_mini_rx_ring(ap, RX_MINI_SIZE);
+ ace_load_mini_rx_ring(dev, RX_MINI_SIZE);
else
printk(KERN_ERR "%s: Someone is busy refilling "
"the RX mini ring\n", ap->name);
@@ -1601,9 +1584,10 @@ static void ace_watchdog(struct net_device *data)
}
-static void ace_tasklet(unsigned long dev)
+static void ace_tasklet(unsigned long arg)
{
- struct ace_private *ap = netdev_priv((struct net_device *)dev);
+ struct net_device *dev = (struct net_device *) arg;
+ struct ace_private *ap = netdev_priv(dev);
int cur_size;
cur_size = atomic_read(&ap->cur_rx_bufs);
@@ -1612,7 +1596,7 @@ static void ace_tasklet(unsigned long dev)
#ifdef DEBUG
printk("refilling buffers (current %i)\n", cur_size);
#endif
- ace_load_std_rx_ring(ap, RX_RING_SIZE - cur_size);
+ ace_load_std_rx_ring(dev, RX_RING_SIZE - cur_size);
}
if (ap->version >= 2) {
@@ -1623,7 +1607,7 @@ static void ace_tasklet(unsigned long dev)
printk("refilling mini buffers (current %i)\n",
cur_size);
#endif
- ace_load_mini_rx_ring(ap, RX_MINI_SIZE - cur_size);
+ ace_load_mini_rx_ring(dev, RX_MINI_SIZE - cur_size);
}
}
@@ -1633,7 +1617,7 @@ static void ace_tasklet(unsigned long dev)
#ifdef DEBUG
printk("refilling jumbo buffers (current %i)\n", cur_size);
#endif
- ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE - cur_size);
+ ace_load_jumbo_rx_ring(dev, RX_JUMBO_SIZE - cur_size);
}
ap->tasklet_pending = 0;
}
@@ -1659,8 +1643,9 @@ static void ace_dump_trace(struct ace_private *ap)
* done only before the device is enabled, thus no interrupts are
* generated and by the interrupt handler/tasklet handler.
*/
-static void ace_load_std_rx_ring(struct ace_private *ap, int nr_bufs)
+static void ace_load_std_rx_ring(struct net_device *dev, int nr_bufs)
{
+ struct ace_private *ap = netdev_priv(dev);
struct ace_regs __iomem *regs = ap->regs;
short i, idx;
@@ -1674,11 +1659,10 @@ static void ace_load_std_rx_ring(struct ace_private *ap, int nr_bufs)
struct rx_desc *rd;
dma_addr_t mapping;
- skb = alloc_skb(ACE_STD_BUFSIZE + NET_IP_ALIGN, GFP_ATOMIC);
+ skb = netdev_alloc_skb_ip_align(dev, ACE_STD_BUFSIZE);
if (!skb)
break;
- skb_reserve(skb, NET_IP_ALIGN);
mapping = pci_map_page(ap->pdev, virt_to_page(skb->data),
offset_in_page(skb->data),
ACE_STD_BUFSIZE,
@@ -1722,8 +1706,9 @@ static void ace_load_std_rx_ring(struct ace_private *ap, int nr_bufs)
}
-static void ace_load_mini_rx_ring(struct ace_private *ap, int nr_bufs)
+static void ace_load_mini_rx_ring(struct net_device *dev, int nr_bufs)
{
+ struct ace_private *ap = netdev_priv(dev);
struct ace_regs __iomem *regs = ap->regs;
short i, idx;
@@ -1735,11 +1720,10 @@ static void ace_load_mini_rx_ring(struct ace_private *ap, int nr_bufs)
struct rx_desc *rd;
dma_addr_t mapping;
- skb = alloc_skb(ACE_MINI_BUFSIZE + NET_IP_ALIGN, GFP_ATOMIC);
+ skb = netdev_alloc_skb_ip_align(dev, ACE_MINI_BUFSIZE);
if (!skb)
break;
- skb_reserve(skb, NET_IP_ALIGN);
mapping = pci_map_page(ap->pdev, virt_to_page(skb->data),
offset_in_page(skb->data),
ACE_MINI_BUFSIZE,
@@ -1779,8 +1763,9 @@ static void ace_load_mini_rx_ring(struct ace_private *ap, int nr_bufs)
* Load the jumbo rx ring, this may happen at any time if the MTU
* is changed to a value > 1500.
*/
-static void ace_load_jumbo_rx_ring(struct ace_private *ap, int nr_bufs)
+static void ace_load_jumbo_rx_ring(struct net_device *dev, int nr_bufs)
{
+ struct ace_private *ap = netdev_priv(dev);
struct ace_regs __iomem *regs = ap->regs;
short i, idx;
@@ -1791,11 +1776,10 @@ static void ace_load_jumbo_rx_ring(struct ace_private *ap, int nr_bufs)
struct rx_desc *rd;
dma_addr_t mapping;
- skb = alloc_skb(ACE_JUMBO_BUFSIZE + NET_IP_ALIGN, GFP_ATOMIC);
+ skb = netdev_alloc_skb_ip_align(dev, ACE_JUMBO_BUFSIZE);
if (!skb)
break;
- skb_reserve(skb, NET_IP_ALIGN);
mapping = pci_map_page(ap->pdev, virt_to_page(skb->data),
offset_in_page(skb->data),
ACE_JUMBO_BUFSIZE,
@@ -2038,12 +2022,9 @@ static void ace_rx_int(struct net_device *dev, u32 rxretprd, u32 rxretcsm)
}
/* send it up */
-#if ACENIC_DO_VLAN
- if (ap->vlgrp && (bd_flags & BD_FLG_VLAN_TAG)) {
- vlan_hwaccel_rx(skb, ap->vlgrp, retdesc->vlan);
- } else
-#endif
- netif_rx(skb);
+ if ((bd_flags & BD_FLG_VLAN_TAG))
+ __vlan_hwaccel_put_tag(skb, retdesc->vlan);
+ netif_rx(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += retdesc->size;
@@ -2216,7 +2197,7 @@ static irqreturn_t ace_interrupt(int irq, void *dev_id)
#ifdef DEBUG
printk("low on std buffers %i\n", cur_size);
#endif
- ace_load_std_rx_ring(ap,
+ ace_load_std_rx_ring(dev,
RX_RING_SIZE - cur_size);
} else
run_tasklet = 1;
@@ -2232,7 +2213,8 @@ static irqreturn_t ace_interrupt(int irq, void *dev_id)
printk("low on mini buffers %i\n",
cur_size);
#endif
- ace_load_mini_rx_ring(ap, RX_MINI_SIZE - cur_size);
+ ace_load_mini_rx_ring(dev,
+ RX_MINI_SIZE - cur_size);
} else
run_tasklet = 1;
}
@@ -2248,7 +2230,8 @@ static irqreturn_t ace_interrupt(int irq, void *dev_id)
printk("low on jumbo buffers %i\n",
cur_size);
#endif
- ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE - cur_size);
+ ace_load_jumbo_rx_ring(dev,
+ RX_JUMBO_SIZE - cur_size);
} else
run_tasklet = 1;
}
@@ -2262,24 +2245,6 @@ static irqreturn_t ace_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-
-#if ACENIC_DO_VLAN
-static void ace_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
-{
- struct ace_private *ap = netdev_priv(dev);
- unsigned long flags;
-
- local_irq_save(flags);
- ace_mask_irq(dev);
-
- ap->vlgrp = grp;
-
- ace_unmask_irq(dev);
- local_irq_restore(flags);
-}
-#endif /* ACENIC_DO_VLAN */
-
-
static int ace_open(struct net_device *dev)
{
struct ace_private *ap = netdev_priv(dev);
@@ -2305,7 +2270,7 @@ static int ace_open(struct net_device *dev)
if (ap->jumbo &&
!test_and_set_bit(0, &ap->jumbo_refill_busy))
- ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE);
+ ace_load_jumbo_rx_ring(dev, RX_JUMBO_SIZE);
if (dev->flags & IFF_PROMISC) {
cmd.evt = C_SET_PROMISC_MODE;
@@ -2449,16 +2414,12 @@ ace_load_tx_bd(struct ace_private *ap, struct tx_desc *desc, u64 addr,
writel(addr >> 32, &io->addr.addrhi);
writel(addr & 0xffffffff, &io->addr.addrlo);
writel(flagsize, &io->flagsize);
-#if ACENIC_DO_VLAN
writel(vlan_tag, &io->vlanres);
-#endif
} else {
desc->addr.addrhi = addr >> 32;
desc->addr.addrlo = addr;
desc->flagsize = flagsize;
-#if ACENIC_DO_VLAN
desc->vlanres = vlan_tag;
-#endif
}
}
@@ -2486,12 +2447,10 @@ restart:
flagsize = (skb->len << 16) | (BD_FLG_END);
if (skb->ip_summed == CHECKSUM_PARTIAL)
flagsize |= BD_FLG_TCP_UDP_SUM;
-#if ACENIC_DO_VLAN
if (vlan_tx_tag_present(skb)) {
flagsize |= BD_FLG_VLAN_TAG;
vlan_tag = vlan_tx_tag_get(skb);
}
-#endif
desc = ap->tx_ring + idx;
idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
@@ -2509,30 +2468,28 @@ restart:
flagsize = (skb_headlen(skb) << 16);
if (skb->ip_summed == CHECKSUM_PARTIAL)
flagsize |= BD_FLG_TCP_UDP_SUM;
-#if ACENIC_DO_VLAN
if (vlan_tx_tag_present(skb)) {
flagsize |= BD_FLG_VLAN_TAG;
vlan_tag = vlan_tx_tag_get(skb);
}
-#endif
ace_load_tx_bd(ap, ap->tx_ring + idx, mapping, flagsize, vlan_tag);
idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
struct tx_ring_info *info;
- len += frag->size;
+ len += skb_frag_size(frag);
info = ap->skb->tx_skbuff + idx;
desc = ap->tx_ring + idx;
- mapping = pci_map_page(ap->pdev, frag->page,
- frag->page_offset, frag->size,
- PCI_DMA_TODEVICE);
+ mapping = skb_frag_dma_map(&ap->pdev->dev, frag, 0,
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
- flagsize = (frag->size << 16);
+ flagsize = skb_frag_size(frag) << 16;
if (skb->ip_summed == CHECKSUM_PARTIAL)
flagsize |= BD_FLG_TCP_UDP_SUM;
idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
@@ -2551,7 +2508,7 @@ restart:
info->skb = NULL;
}
dma_unmap_addr_set(info, mapping, mapping);
- dma_unmap_len_set(info, maplen, frag->size);
+ dma_unmap_len_set(info, maplen, skb_frag_size(frag));
ace_load_tx_bd(ap, desc, mapping, flagsize, vlan_tag);
}
}
@@ -2621,7 +2578,7 @@ static int ace_change_mtu(struct net_device *dev, int new_mtu)
"support\n", dev->name);
ap->jumbo = 1;
if (!test_and_set_bit(0, &ap->jumbo_refill_busy))
- ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE);
+ ace_load_jumbo_rx_ring(dev, RX_JUMBO_SIZE);
ace_set_rxtx_parms(dev, 1);
}
} else {
diff --git a/drivers/net/acenic.h b/drivers/net/ethernet/alteon/acenic.h
index 0681da7e8753..51c486cfbb8c 100644
--- a/drivers/net/acenic.h
+++ b/drivers/net/ethernet/alteon/acenic.h
@@ -1,5 +1,6 @@
#ifndef _ACENIC_H_
#define _ACENIC_H_
+#include <linux/interrupt.h>
/*
@@ -664,10 +665,6 @@ struct ace_private
struct rx_desc *rx_mini_ring;
struct rx_desc *rx_return_ring;
-#if ACENIC_DO_VLAN
- struct vlan_group *vlgrp;
-#endif
-
int tasklet_pending, jumbo;
struct tasklet_struct ace_tasklet;
@@ -769,9 +766,9 @@ static inline void ace_unmask_irq(struct net_device *dev)
* Prototypes
*/
static int ace_init(struct net_device *dev);
-static void ace_load_std_rx_ring(struct ace_private *ap, int nr_bufs);
-static void ace_load_mini_rx_ring(struct ace_private *ap, int nr_bufs);
-static void ace_load_jumbo_rx_ring(struct ace_private *ap, int nr_bufs);
+static void ace_load_std_rx_ring(struct net_device *dev, int nr_bufs);
+static void ace_load_mini_rx_ring(struct net_device *dev, int nr_bufs);
+static void ace_load_jumbo_rx_ring(struct net_device *dev, int nr_bufs);
static irqreturn_t ace_interrupt(int irq, void *dev_id);
static int ace_load_firmware(struct net_device *dev);
static int ace_open(struct net_device *dev);
@@ -789,8 +786,5 @@ static void ace_free_descriptors(struct net_device *dev);
static void ace_init_cleanup(struct net_device *dev);
static struct net_device_stats *ace_get_stats(struct net_device *dev);
static int read_eeprom_byte(struct net_device *dev, unsigned long offset);
-#if ACENIC_DO_VLAN
-static void ace_vlan_rx_register(struct net_device *dev, struct vlan_group *grp);
-#endif
#endif /* _ACENIC_H_ */
diff --git a/drivers/net/7990.c b/drivers/net/ethernet/amd/7990.c
index 903bcb3ef5bd..60b35fb5f524 100644
--- a/drivers/net/7990.c
+++ b/drivers/net/ethernet/amd/7990.c
@@ -594,7 +594,6 @@ static void lance_load_multicast (struct net_device *dev)
volatile struct lance_init_block *ib = lp->init_block;
volatile u16 *mcast_table = (u16 *)&ib->filter;
struct netdev_hw_addr *ha;
- char *addrs;
u32 crc;
/* set all multicast bits */
@@ -609,13 +608,7 @@ static void lance_load_multicast (struct net_device *dev)
/* Add addresses */
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
-
- /* multicast address? */
- if (!(*addrs & 1))
- continue;
-
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc = crc >> 26;
mcast_table [crc >> 4] |= 1 << (crc & 0xf);
}
diff --git a/drivers/net/7990.h b/drivers/net/ethernet/amd/7990.h
index 0a5837b96421..0a5837b96421 100644
--- a/drivers/net/7990.h
+++ b/drivers/net/ethernet/amd/7990.h
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
new file mode 100644
index 000000000000..238b537b68fe
--- /dev/null
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -0,0 +1,195 @@
+#
+# AMD network device configuration
+#
+
+config NET_VENDOR_AMD
+ bool "AMD devices"
+ default y
+ depends on DIO || MACH_DECSTATION || MVME147 || ATARI || SUN3 || \
+ SUN3X || SBUS || PCI || ZORRO || (ISA && ISA_DMA_API) || \
+ (ARM && ARCH_EBSA110) || ISA || EISA || MCA || PCMCIA
+ ---help---
+ If you have a network (Ethernet) chipset belonging to this class,
+ say Y.
+
+ Note that the answer to this question does not directly affect
+ the kernel: saying N will just case the configurator to skip all
+ the questions regarding AMD chipsets. If you say Y, you will be asked
+ for your specific chipset/driver in the following questions.
+
+if NET_VENDOR_AMD
+
+config A2065
+ tristate "A2065 support"
+ depends on ZORRO
+ select CRC32
+ ---help---
+ If you have a Commodore A2065 Ethernet adapter, say Y. Otherwise,
+ say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called a2065.
+
+config AMD8111_ETH
+ tristate "AMD 8111 (new PCI LANCE) support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ If you have an AMD 8111-based PCI LANCE ethernet card,
+ answer Y here and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called amd8111e.
+
+config LANCE
+ tristate "AMD LANCE and PCnet (AT1500 and NE2100) support"
+ depends on ISA && ISA_DMA_API
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Some LinkSys cards are
+ of this type.
+
+ To compile this driver as a module, choose M here: the module
+ will be called lance. This is recommended.
+
+config PCNET32
+ tristate "AMD PCnet32 PCI support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ If you have a PCnet32 or PCnetPCI based network (Ethernet) card,
+ answer Y here and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called pcnet32.
+
+config ARIADNE
+ tristate "Ariadne support"
+ depends on ZORRO
+ ---help---
+ If you have a Village Tronic Ariadne Ethernet adapter, say Y.
+ Otherwise, say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ariadne.
+
+config ARM_AM79C961A
+ bool "ARM EBSA110 AM79C961A support"
+ depends on ARM && ARCH_EBSA110
+ select CRC32
+ ---help---
+ If you wish to compile a kernel for the EBSA-110, then you should
+ always answer Y to this.
+
+config ATARILANCE
+ tristate "Atari LANCE support"
+ depends on ATARI
+ ---help---
+ Say Y to include support for several Atari Ethernet adapters based
+ on the AMD LANCE chipset: RieblCard (with or without battery), or
+ PAMCard VME (also the version by Rhotron, with different addresses).
+
+config DECLANCE
+ tristate "DEC LANCE ethernet controller support"
+ depends on MACH_DECSTATION
+ select CRC32
+ ---help---
+ This driver is for the series of Ethernet controllers produced by
+ DEC (now Compaq) based on the AMD LANCE chipset, including the
+ DEPCA series. (This chipset is better known via the NE2100 cards.)
+
+config DEPCA
+ tristate "DEPCA, DE10x, DE200, DE201, DE202, DE422 support"
+ depends on (ISA || EISA || MCA)
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto> as well as
+ <file:drivers/net/depca.c>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called depca.
+
+config HPLANCE
+ bool "HP on-board LANCE support"
+ depends on DIO
+ select CRC32
+ ---help---
+ If you want to use the builtin "LANCE" Ethernet controller on an
+ HP300 machine, say Y here.
+
+config MIPS_AU1X00_ENET
+ tristate "MIPS AU1000 Ethernet support"
+ depends on MIPS_ALCHEMY
+ select PHYLIB
+ select CRC32
+ ---help---
+ If you have an Alchemy Semi AU1X00 based system
+ say Y. Otherwise, say N.
+
+config MVME147_NET
+ tristate "MVME147 (LANCE) Ethernet support"
+ depends on MVME147
+ select CRC32
+ ---help---
+ Support for the on-board Ethernet interface on the Motorola MVME147
+ single-board computer. Say Y here to include the
+ driver for this chip in your kernel.
+ To compile this driver as a module, choose M here.
+
+config PCMCIA_NMCLAN
+ tristate "New Media PCMCIA support"
+ depends on PCMCIA
+ help
+ Say Y here if you intend to attach a New Media Ethernet or LiveWire
+ PCMCIA (PC-card) Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called nmclan_cs. If unsure, say N.
+
+config NI65
+ tristate "NI6510 support"
+ depends on ISA && ISA_DMA_API
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ni65.
+
+config SUN3LANCE
+ tristate "Sun3/Sun3x on-board LANCE support"
+ depends on (SUN3 || SUN3X)
+ ---help---
+ Most Sun3 and Sun3x motherboards (including the 3/50, 3/60 and 3/80)
+ featured an AMD LANCE 10Mbit Ethernet controller on board; say Y
+ here to compile in the Linux driver for this and enable Ethernet.
+ General Linux information on the Sun 3 and 3x series (now
+ discontinued) is at
+ <http://www.angelfire.com/ca2/tech68k/sun3.html>.
+
+ If you're not building a kernel for a Sun 3, say N.
+
+config SUNLANCE
+ tristate "Sun LANCE support"
+ depends on SBUS
+ select CRC32
+ ---help---
+ This driver supports the "le" interface present on all 32-bit Sparc
+ systems, on some older Ultra systems and as an Sbus option. These
+ cards are based on the AMD LANCE chipset, which is better known
+ via the NE2100 cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sunlance.
+
+endif # NET_VENDOR_AMD
diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile
new file mode 100644
index 000000000000..175caa5328c9
--- /dev/null
+++ b/drivers/net/ethernet/amd/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the AMD network device drivers.
+#
+
+obj-$(CONFIG_A2065) += a2065.o
+obj-$(CONFIG_AMD8111_ETH) += amd8111e.o
+obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o
+obj-$(CONFIG_ARIADNE) += ariadne.o
+obj-$(CONFIG_ATARILANCE) += atarilance.o
+obj-$(CONFIG_DECLANCE) += declance.o
+obj-$(CONFIG_DEPCA) += depca.o
+obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
+obj-$(CONFIG_LANCE) += lance.o
+obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o
+obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o
+obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o
+obj-$(CONFIG_NI65) += ni65.o
+obj-$(CONFIG_PCNET32) += pcnet32.o
+obj-$(CONFIG_SUN3LANCE) += sun3lance.o
+obj-$(CONFIG_SUNLANCE) += sunlance.o
diff --git a/drivers/net/a2065.c b/drivers/net/ethernet/amd/a2065.c
index deaa8bc16cf8..825e5d4ef4c3 100644
--- a/drivers/net/a2065.c
+++ b/drivers/net/ethernet/amd/a2065.c
@@ -37,6 +37,11 @@
* both 10BASE-2 (thin coax) and AUI (DB-15) connectors
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+/*#define DEBUG*/
+/*#define TEST_HITS*/
+
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -58,29 +63,22 @@
#include "a2065.h"
-
- /*
- * Transmit/Receive Ring Definitions
- */
+/* Transmit/Receive Ring Definitions */
#define LANCE_LOG_TX_BUFFERS (2)
#define LANCE_LOG_RX_BUFFERS (4)
-#define TX_RING_SIZE (1<<LANCE_LOG_TX_BUFFERS)
-#define RX_RING_SIZE (1<<LANCE_LOG_RX_BUFFERS)
+#define TX_RING_SIZE (1 << LANCE_LOG_TX_BUFFERS)
+#define RX_RING_SIZE (1 << LANCE_LOG_RX_BUFFERS)
-#define TX_RING_MOD_MASK (TX_RING_SIZE-1)
-#define RX_RING_MOD_MASK (RX_RING_SIZE-1)
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
#define PKT_BUF_SIZE (1544)
#define RX_BUFF_SIZE PKT_BUF_SIZE
#define TX_BUFF_SIZE PKT_BUF_SIZE
-
- /*
- * Layout of the Lance's RAM Buffer
- */
-
+/* Layout of the Lance's RAM Buffer */
struct lance_init_block {
unsigned short mode; /* Pre-set mode (reg. 15) */
@@ -97,14 +95,11 @@ struct lance_init_block {
struct lance_rx_desc brx_ring[RX_RING_SIZE];
struct lance_tx_desc btx_ring[TX_RING_SIZE];
- char rx_buf [RX_RING_SIZE][RX_BUFF_SIZE];
- char tx_buf [TX_RING_SIZE][TX_BUFF_SIZE];
+ char rx_buf[RX_RING_SIZE][RX_BUFF_SIZE];
+ char tx_buf[TX_RING_SIZE][TX_BUFF_SIZE];
};
-
- /*
- * Private Device Data
- */
+/* Private Device Data */
struct lance_private {
char *name;
@@ -129,21 +124,14 @@ struct lance_private {
struct timer_list multicast_timer;
};
-#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
- lp->tx_old+lp->tx_ring_mod_mask-lp->tx_new:\
- lp->tx_old - lp->tx_new-1)
-
-
#define LANCE_ADDR(x) ((int)(x) & ~0xff000000)
/* Load the CSR registers */
-static void load_csrs (struct lance_private *lp)
+static void load_csrs(struct lance_private *lp)
{
volatile struct lance_regs *ll = lp->ll;
volatile struct lance_init_block *aib = lp->lance_init_block;
- int leptr;
-
- leptr = LANCE_ADDR (aib);
+ int leptr = LANCE_ADDR(aib);
ll->rap = LE_CSR1;
ll->rdp = (leptr & 0xFFFF);
@@ -156,19 +144,16 @@ static void load_csrs (struct lance_private *lp)
ll->rap = LE_CSR0;
}
-#define ZERO 0
-
/* Setup the Lance Rx and Tx rings */
-static void lance_init_ring (struct net_device *dev)
+static void lance_init_ring(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
- volatile struct lance_init_block *aib; /* for LANCE_ADDR computations */
+ volatile struct lance_init_block *aib = lp->lance_init_block;
+ /* for LANCE_ADDR computations */
int leptr;
int i;
- aib = lp->lance_init_block;
-
/* Lock out other processes while setting up hardware */
netif_stop_queue(dev);
lp->rx_new = lp->tx_new = 0;
@@ -179,41 +164,38 @@ static void lance_init_ring (struct net_device *dev)
/* Copy the ethernet address to the lance init block
* Note that on the sparc you need to swap the ethernet address.
*/
- ib->phys_addr [0] = dev->dev_addr [1];
- ib->phys_addr [1] = dev->dev_addr [0];
- ib->phys_addr [2] = dev->dev_addr [3];
- ib->phys_addr [3] = dev->dev_addr [2];
- ib->phys_addr [4] = dev->dev_addr [5];
- ib->phys_addr [5] = dev->dev_addr [4];
-
- if (ZERO)
- printk(KERN_DEBUG "TX rings:\n");
+ ib->phys_addr[0] = dev->dev_addr[1];
+ ib->phys_addr[1] = dev->dev_addr[0];
+ ib->phys_addr[2] = dev->dev_addr[3];
+ ib->phys_addr[3] = dev->dev_addr[2];
+ ib->phys_addr[4] = dev->dev_addr[5];
+ ib->phys_addr[5] = dev->dev_addr[4];
/* Setup the Tx ring entries */
- for (i = 0; i <= (1<<lp->lance_log_tx_bufs); i++) {
+ netdev_dbg(dev, "TX rings:\n");
+ for (i = 0; i <= 1 << lp->lance_log_tx_bufs; i++) {
leptr = LANCE_ADDR(&aib->tx_buf[i][0]);
- ib->btx_ring [i].tmd0 = leptr;
- ib->btx_ring [i].tmd1_hadr = leptr >> 16;
- ib->btx_ring [i].tmd1_bits = 0;
- ib->btx_ring [i].length = 0xf000; /* The ones required by tmd2 */
- ib->btx_ring [i].misc = 0;
- if (i < 3 && ZERO)
- printk(KERN_DEBUG "%d: 0x%8.8x\n", i, leptr);
+ ib->btx_ring[i].tmd0 = leptr;
+ ib->btx_ring[i].tmd1_hadr = leptr >> 16;
+ ib->btx_ring[i].tmd1_bits = 0;
+ ib->btx_ring[i].length = 0xf000; /* The ones required by tmd2 */
+ ib->btx_ring[i].misc = 0;
+ if (i < 3)
+ netdev_dbg(dev, "%d: 0x%08x\n", i, leptr);
}
/* Setup the Rx ring entries */
- if (ZERO)
- printk(KERN_DEBUG "RX rings:\n");
- for (i = 0; i < (1<<lp->lance_log_rx_bufs); i++) {
+ netdev_dbg(dev, "RX rings:\n");
+ for (i = 0; i < 1 << lp->lance_log_rx_bufs; i++) {
leptr = LANCE_ADDR(&aib->rx_buf[i][0]);
- ib->brx_ring [i].rmd0 = leptr;
- ib->brx_ring [i].rmd1_hadr = leptr >> 16;
- ib->brx_ring [i].rmd1_bits = LE_R1_OWN;
- ib->brx_ring [i].length = -RX_BUFF_SIZE | 0xf000;
- ib->brx_ring [i].mblength = 0;
- if (i < 3 && ZERO)
- printk(KERN_DEBUG "%d: 0x%8.8x\n", i, leptr);
+ ib->brx_ring[i].rmd0 = leptr;
+ ib->brx_ring[i].rmd1_hadr = leptr >> 16;
+ ib->brx_ring[i].rmd1_bits = LE_R1_OWN;
+ ib->brx_ring[i].length = -RX_BUFF_SIZE | 0xf000;
+ ib->brx_ring[i].mblength = 0;
+ if (i < 3)
+ netdev_dbg(dev, "%d: 0x%08x\n", i, leptr);
}
/* Setup the initialization block */
@@ -222,22 +204,20 @@ static void lance_init_ring (struct net_device *dev)
leptr = LANCE_ADDR(&aib->brx_ring);
ib->rx_len = (lp->lance_log_rx_bufs << 13) | (leptr >> 16);
ib->rx_ptr = leptr;
- if (ZERO)
- printk(KERN_DEBUG "RX ptr: %8.8x\n", leptr);
+ netdev_dbg(dev, "RX ptr: %08x\n", leptr);
/* Setup tx descriptor pointer */
leptr = LANCE_ADDR(&aib->btx_ring);
ib->tx_len = (lp->lance_log_tx_bufs << 13) | (leptr >> 16);
ib->tx_ptr = leptr;
- if (ZERO)
- printk(KERN_DEBUG "TX ptr: %8.8x\n", leptr);
+ netdev_dbg(dev, "TX ptr: %08x\n", leptr);
/* Clear the multicast filter */
- ib->filter [0] = 0;
- ib->filter [1] = 0;
+ ib->filter[0] = 0;
+ ib->filter[1] = 0;
}
-static int init_restart_lance (struct lance_private *lp)
+static int init_restart_lance(struct lance_private *lp)
{
volatile struct lance_regs *ll = lp->ll;
int i;
@@ -249,8 +229,7 @@ static int init_restart_lance (struct lance_private *lp)
for (i = 0; (i < 100) && !(ll->rdp & (LE_C0_ERR | LE_C0_IDON)); i++)
barrier();
if ((i == 100) || (ll->rdp & LE_C0_ERR)) {
- printk(KERN_ERR "LANCE unopened after %d ticks, csr0=%4.4x.\n",
- i, ll->rdp);
+ pr_err("unopened after %d ticks, csr0=%04x\n", i, ll->rdp);
return -EIO;
}
@@ -261,7 +240,7 @@ static int init_restart_lance (struct lance_private *lp)
return 0;
}
-static int lance_rx (struct net_device *dev)
+static int lance_rx(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
@@ -271,22 +250,24 @@ static int lance_rx (struct net_device *dev)
#ifdef TEST_HITS
int i;
- printk(KERN_DEBUG "[");
+ char buf[RX_RING_SIZE + 1];
+
for (i = 0; i < RX_RING_SIZE; i++) {
+ char r1_own = ib->brx_ring[i].rmd1_bits & LE_R1_OWN;
if (i == lp->rx_new)
- printk ("%s",
- ib->brx_ring [i].rmd1_bits & LE_R1_OWN ? "_" : "X");
+ buf[i] = r1_own ? '_' : 'X';
else
- printk ("%s",
- ib->brx_ring [i].rmd1_bits & LE_R1_OWN ? "." : "1");
+ buf[i] = r1_own ? '.' : '1';
}
- printk ("]\n");
+ buf[RX_RING_SIZE] = 0;
+
+ pr_debug("RxRing TestHits: [%s]\n", buf);
#endif
- ll->rdp = LE_C0_RINT|LE_C0_INEA;
- for (rd = &ib->brx_ring [lp->rx_new];
+ ll->rdp = LE_C0_RINT | LE_C0_INEA;
+ for (rd = &ib->brx_ring[lp->rx_new];
!((bits = rd->rmd1_bits) & LE_R1_OWN);
- rd = &ib->brx_ring [lp->rx_new]) {
+ rd = &ib->brx_ring[lp->rx_new]) {
/* We got an incomplete frame? */
if ((bits & LE_R1_POK) != LE_R1_POK) {
@@ -297,18 +278,22 @@ static int lance_rx (struct net_device *dev)
/* Count only the end frame as a rx error,
* not the beginning
*/
- if (bits & LE_R1_BUF) dev->stats.rx_fifo_errors++;
- if (bits & LE_R1_CRC) dev->stats.rx_crc_errors++;
- if (bits & LE_R1_OFL) dev->stats.rx_over_errors++;
- if (bits & LE_R1_FRA) dev->stats.rx_frame_errors++;
- if (bits & LE_R1_EOP) dev->stats.rx_errors++;
+ if (bits & LE_R1_BUF)
+ dev->stats.rx_fifo_errors++;
+ if (bits & LE_R1_CRC)
+ dev->stats.rx_crc_errors++;
+ if (bits & LE_R1_OFL)
+ dev->stats.rx_over_errors++;
+ if (bits & LE_R1_FRA)
+ dev->stats.rx_frame_errors++;
+ if (bits & LE_R1_EOP)
+ dev->stats.rx_errors++;
} else {
int len = (rd->mblength & 0xfff) - 4;
- struct sk_buff *skb = dev_alloc_skb (len+2);
+ struct sk_buff *skb = dev_alloc_skb(len + 2);
if (!skb) {
- printk(KERN_WARNING "%s: Memory squeeze, "
- "deferring packet.\n", dev->name);
+ netdev_warn(dev, "Memory squeeze, deferring packet\n");
dev->stats.rx_dropped++;
rd->mblength = 0;
rd->rmd1_bits = LE_R1_OWN;
@@ -316,13 +301,13 @@ static int lance_rx (struct net_device *dev)
return 0;
}
- skb_reserve (skb, 2); /* 16 byte align */
- skb_put (skb, len); /* make room */
+ skb_reserve(skb, 2); /* 16 byte align */
+ skb_put(skb, len); /* make room */
skb_copy_to_linear_data(skb,
- (unsigned char *)&(ib->rx_buf [lp->rx_new][0]),
- len);
- skb->protocol = eth_type_trans (skb, dev);
- netif_rx (skb);
+ (unsigned char *)&ib->rx_buf[lp->rx_new][0],
+ len);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
}
@@ -335,7 +320,7 @@ static int lance_rx (struct net_device *dev)
return 0;
}
-static int lance_tx (struct net_device *dev)
+static int lance_tx(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
@@ -350,7 +335,7 @@ static int lance_tx (struct net_device *dev)
j = lp->tx_old;
for (i = j; i != lp->tx_new; i = j) {
- td = &ib->btx_ring [i];
+ td = &ib->btx_ring[i];
/* If we hit a packet not owned by us, stop */
if (td->tmd1_bits & LE_T1_OWN)
@@ -360,45 +345,44 @@ static int lance_tx (struct net_device *dev)
status = td->misc;
dev->stats.tx_errors++;
- if (status & LE_T3_RTY) dev->stats.tx_aborted_errors++;
- if (status & LE_T3_LCOL) dev->stats.tx_window_errors++;
+ if (status & LE_T3_RTY)
+ dev->stats.tx_aborted_errors++;
+ if (status & LE_T3_LCOL)
+ dev->stats.tx_window_errors++;
if (status & LE_T3_CLOS) {
dev->stats.tx_carrier_errors++;
if (lp->auto_select) {
lp->tpe = 1 - lp->tpe;
- printk(KERN_ERR "%s: Carrier Lost, "
- "trying %s\n", dev->name,
- lp->tpe?"TPE":"AUI");
+ netdev_err(dev, "Carrier Lost, trying %s\n",
+ lp->tpe ? "TPE" : "AUI");
/* Stop the lance */
ll->rap = LE_CSR0;
ll->rdp = LE_C0_STOP;
- lance_init_ring (dev);
- load_csrs (lp);
- init_restart_lance (lp);
+ lance_init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
return 0;
}
}
- /* buffer errors and underflows turn off the transmitter */
- /* Restart the adapter */
- if (status & (LE_T3_BUF|LE_T3_UFL)) {
+ /* buffer errors and underflows turn off
+ * the transmitter, so restart the adapter
+ */
+ if (status & (LE_T3_BUF | LE_T3_UFL)) {
dev->stats.tx_fifo_errors++;
- printk(KERN_ERR "%s: Tx: ERR_BUF|ERR_UFL, "
- "restarting\n", dev->name);
+ netdev_err(dev, "Tx: ERR_BUF|ERR_UFL, restarting\n");
/* Stop the lance */
ll->rap = LE_CSR0;
ll->rdp = LE_C0_STOP;
- lance_init_ring (dev);
- load_csrs (lp);
- init_restart_lance (lp);
+ lance_init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
return 0;
}
} else if ((td->tmd1_bits & LE_T1_POK) == LE_T1_POK) {
- /*
- * So we don't count the packet more than once.
- */
+ /* So we don't count the packet more than once. */
td->tmd1_bits &= ~(LE_T1_POK);
/* One collision before packet was sent. */
@@ -419,17 +403,19 @@ static int lance_tx (struct net_device *dev)
return 0;
}
-static irqreturn_t lance_interrupt (int irq, void *dev_id)
+static int lance_tx_buffs_avail(struct lance_private *lp)
{
- struct net_device *dev;
- struct lance_private *lp;
- volatile struct lance_regs *ll;
- int csr0;
-
- dev = (struct net_device *) dev_id;
+ if (lp->tx_old <= lp->tx_new)
+ return lp->tx_old + lp->tx_ring_mod_mask - lp->tx_new;
+ return lp->tx_old - lp->tx_new - 1;
+}
- lp = netdev_priv(dev);
- ll = lp->ll;
+static irqreturn_t lance_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+ int csr0;
ll->rap = LE_CSR0; /* LANCE Controller Status */
csr0 = ll->rdp;
@@ -438,19 +424,19 @@ static irqreturn_t lance_interrupt (int irq, void *dev_id)
return IRQ_NONE; /* been generated by the Lance. */
/* Acknowledge all the interrupt sources ASAP */
- ll->rdp = csr0 & ~(LE_C0_INEA|LE_C0_TDMD|LE_C0_STOP|LE_C0_STRT|
+ ll->rdp = csr0 & ~(LE_C0_INEA | LE_C0_TDMD | LE_C0_STOP | LE_C0_STRT |
LE_C0_INIT);
- if ((csr0 & LE_C0_ERR)) {
+ if (csr0 & LE_C0_ERR) {
/* Clear the error condition */
- ll->rdp = LE_C0_BABL|LE_C0_ERR|LE_C0_MISS|LE_C0_INEA;
+ ll->rdp = LE_C0_BABL | LE_C0_ERR | LE_C0_MISS | LE_C0_INEA;
}
if (csr0 & LE_C0_RINT)
- lance_rx (dev);
+ lance_rx(dev);
if (csr0 & LE_C0_TINT)
- lance_tx (dev);
+ lance_tx(dev);
/* Log misc errors. */
if (csr0 & LE_C0_BABL)
@@ -458,22 +444,22 @@ static irqreturn_t lance_interrupt (int irq, void *dev_id)
if (csr0 & LE_C0_MISS)
dev->stats.rx_errors++; /* Missed a Rx frame. */
if (csr0 & LE_C0_MERR) {
- printk(KERN_ERR "%s: Bus master arbitration failure, status "
- "%4.4x.\n", dev->name, csr0);
+ netdev_err(dev, "Bus master arbitration failure, status %04x\n",
+ csr0);
/* Restart the chip. */
ll->rdp = LE_C0_STRT;
}
- if (netif_queue_stopped(dev) && TX_BUFFS_AVAIL > 0)
+ if (netif_queue_stopped(dev) && lance_tx_buffs_avail(lp) > 0)
netif_wake_queue(dev);
ll->rap = LE_CSR0;
- ll->rdp = LE_C0_BABL|LE_C0_CERR|LE_C0_MISS|LE_C0_MERR|
- LE_C0_IDON|LE_C0_INEA;
+ ll->rdp = (LE_C0_BABL | LE_C0_CERR | LE_C0_MISS | LE_C0_MERR |
+ LE_C0_IDON | LE_C0_INEA);
return IRQ_HANDLED;
}
-static int lance_open (struct net_device *dev)
+static int lance_open(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
@@ -486,17 +472,18 @@ static int lance_open (struct net_device *dev)
/* Install the Interrupt handler */
ret = request_irq(IRQ_AMIGA_PORTS, lance_interrupt, IRQF_SHARED,
dev->name, dev);
- if (ret) return ret;
+ if (ret)
+ return ret;
- load_csrs (lp);
- lance_init_ring (dev);
+ load_csrs(lp);
+ lance_init_ring(dev);
netif_start_queue(dev);
- return init_restart_lance (lp);
+ return init_restart_lance(lp);
}
-static int lance_close (struct net_device *dev)
+static int lance_close(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
@@ -512,7 +499,7 @@ static int lance_close (struct net_device *dev)
return 0;
}
-static inline int lance_reset (struct net_device *dev)
+static inline int lance_reset(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
@@ -522,16 +509,15 @@ static inline int lance_reset (struct net_device *dev)
ll->rap = LE_CSR0;
ll->rdp = LE_C0_STOP;
- load_csrs (lp);
+ load_csrs(lp);
- lance_init_ring (dev);
+ lance_init_ring(dev);
dev->trans_start = jiffies; /* prevent tx timeout */
netif_start_queue(dev);
- status = init_restart_lance (lp);
-#ifdef DEBUG_DRIVER
- printk(KERN_DEBUG "Lance restart=%d\n", status);
-#endif
+ status = init_restart_lance(lp);
+ netdev_dbg(dev, "Lance restart=%d\n", status);
+
return status;
}
@@ -540,14 +526,13 @@ static void lance_tx_timeout(struct net_device *dev)
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
- printk(KERN_ERR "%s: transmit timed out, status %04x, reset\n",
- dev->name, ll->rdp);
+ netdev_err(dev, "transmit timed out, status %04x, reset\n", ll->rdp);
lance_reset(dev);
netif_wake_queue(dev);
}
-static netdev_tx_t lance_start_xmit (struct sk_buff *skb,
- struct net_device *dev)
+static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
@@ -562,33 +547,33 @@ static netdev_tx_t lance_start_xmit (struct sk_buff *skb,
local_irq_save(flags);
- if (!TX_BUFFS_AVAIL){
+ if (!lance_tx_buffs_avail(lp)) {
local_irq_restore(flags);
return NETDEV_TX_LOCKED;
}
-#ifdef DEBUG_DRIVER
+#ifdef DEBUG
/* dump the packet */
print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
16, 1, skb->data, 64, true);
#endif
entry = lp->tx_new & lp->tx_ring_mod_mask;
- ib->btx_ring [entry].length = (-skblen) | 0xf000;
- ib->btx_ring [entry].misc = 0;
+ ib->btx_ring[entry].length = (-skblen) | 0xf000;
+ ib->btx_ring[entry].misc = 0;
- skb_copy_from_linear_data(skb, (void *)&ib->tx_buf [entry][0], skblen);
+ skb_copy_from_linear_data(skb, (void *)&ib->tx_buf[entry][0], skblen);
/* Now, give the packet to the lance */
- ib->btx_ring [entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN);
+ ib->btx_ring[entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN);
lp->tx_new = (lp->tx_new+1) & lp->tx_ring_mod_mask;
dev->stats.tx_bytes += skblen;
- if (TX_BUFFS_AVAIL <= 0)
+ if (lance_tx_buffs_avail(lp) <= 0)
netif_stop_queue(dev);
/* Kick the lance: transmit now */
ll->rdp = LE_C0_INEA | LE_C0_TDMD;
- dev_kfree_skb (skb);
+ dev_kfree_skb(skb);
local_irq_restore(flags);
@@ -596,40 +581,33 @@ static netdev_tx_t lance_start_xmit (struct sk_buff *skb,
}
/* taken from the depca driver */
-static void lance_load_multicast (struct net_device *dev)
+static void lance_load_multicast(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
volatile u16 *mcast_table = (u16 *)&ib->filter;
struct netdev_hw_addr *ha;
- char *addrs;
u32 crc;
/* set all multicast bits */
- if (dev->flags & IFF_ALLMULTI){
- ib->filter [0] = 0xffffffff;
- ib->filter [1] = 0xffffffff;
+ if (dev->flags & IFF_ALLMULTI) {
+ ib->filter[0] = 0xffffffff;
+ ib->filter[1] = 0xffffffff;
return;
}
/* clear the multicast filter */
- ib->filter [0] = 0;
- ib->filter [1] = 0;
+ ib->filter[0] = 0;
+ ib->filter[1] = 0;
/* Add addresses */
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
-
- /* multicast address? */
- if (!(*addrs & 1))
- continue;
-
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc = crc >> 26;
- mcast_table [crc >> 4] |= 1 << (crc & 0xf);
+ mcast_table[crc >> 4] |= 1 << (crc & 0xf);
}
}
-static void lance_set_multicast (struct net_device *dev)
+static void lance_set_multicast(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
@@ -648,16 +626,16 @@ static void lance_set_multicast (struct net_device *dev)
ll->rap = LE_CSR0;
ll->rdp = LE_C0_STOP;
- lance_init_ring (dev);
+ lance_init_ring(dev);
if (dev->flags & IFF_PROMISC) {
ib->mode |= LE_MO_PROM;
} else {
ib->mode &= ~LE_MO_PROM;
- lance_load_multicast (dev);
+ lance_load_multicast(dev);
}
- load_csrs (lp);
- init_restart_lance (lp);
+ load_csrs(lp);
+ init_restart_lance(lp);
netif_wake_queue(dev);
}
@@ -686,7 +664,7 @@ static const struct net_device_ops lance_netdev_ops = {
.ndo_stop = lance_close,
.ndo_start_xmit = lance_start_xmit,
.ndo_tx_timeout = lance_tx_timeout,
- .ndo_set_multicast_list = lance_set_multicast,
+ .ndo_set_rx_mode = lance_set_multicast,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
@@ -697,14 +675,12 @@ static int __devinit a2065_init_one(struct zorro_dev *z,
{
struct net_device *dev;
struct lance_private *priv;
- unsigned long board, base_addr, mem_start;
+ unsigned long board = z->resource.start;
+ unsigned long base_addr = board + A2065_LANCE;
+ unsigned long mem_start = board + A2065_RAM;
struct resource *r1, *r2;
int err;
- board = z->resource.start;
- base_addr = board+A2065_LANCE;
- mem_start = board+A2065_RAM;
-
r1 = request_mem_region(base_addr, sizeof(struct lance_regs),
"Am7990");
if (!r1)
@@ -735,12 +711,12 @@ static int __devinit a2065_init_one(struct zorro_dev *z,
dev->dev_addr[1] = 0x00;
dev->dev_addr[2] = 0x9f;
}
- dev->dev_addr[3] = (z->rom.er_SerialNumber>>16) & 0xff;
- dev->dev_addr[4] = (z->rom.er_SerialNumber>>8) & 0xff;
+ dev->dev_addr[3] = (z->rom.er_SerialNumber >> 16) & 0xff;
+ dev->dev_addr[4] = (z->rom.er_SerialNumber >> 8) & 0xff;
dev->dev_addr[5] = z->rom.er_SerialNumber & 0xff;
dev->base_addr = ZTWO_VADDR(base_addr);
dev->mem_start = ZTWO_VADDR(mem_start);
- dev->mem_end = dev->mem_start+A2065_RAM_SIZE;
+ dev->mem_end = dev->mem_start + A2065_RAM_SIZE;
priv->ll = (volatile struct lance_regs *)dev->base_addr;
priv->init_block = (struct lance_init_block *)dev->mem_start;
@@ -760,7 +736,7 @@ static int __devinit a2065_init_one(struct zorro_dev *z,
init_timer(&priv->multicast_timer);
priv->multicast_timer.data = (unsigned long) dev;
priv->multicast_timer.function =
- (void (*)(unsigned long)) &lance_set_multicast;
+ (void (*)(unsigned long))lance_set_multicast;
err = register_netdev(dev);
if (err) {
@@ -771,8 +747,8 @@ static int __devinit a2065_init_one(struct zorro_dev *z,
}
zorro_set_drvdata(z, dev);
- printk(KERN_INFO "%s: A2065 at 0x%08lx, Ethernet Address "
- "%pM\n", dev->name, board, dev->dev_addr);
+ netdev_info(dev, "A2065 at 0x%08lx, Ethernet Address %pM\n",
+ board, dev->dev_addr);
return 0;
}
diff --git a/drivers/net/a2065.h b/drivers/net/ethernet/amd/a2065.h
index 5117759d4e9c..5117759d4e9c 100644
--- a/drivers/net/a2065.h
+++ b/drivers/net/ethernet/amd/a2065.h
diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/ethernet/amd/am79c961a.c
index 7b3e23f38913..7d5ded80d2d7 100644
--- a/drivers/net/arm/am79c961a.c
+++ b/drivers/net/ethernet/amd/am79c961a.c
@@ -199,17 +199,15 @@ am79c961_ramtest(struct net_device *dev, unsigned int val)
static void am79c961_mc_hash(char *addr, u16 *hash)
{
- if (addr[0] & 0x01) {
- int idx, bit;
- u32 crc;
+ int idx, bit;
+ u32 crc;
- crc = ether_crc_le(ETH_ALEN, addr);
+ crc = ether_crc_le(ETH_ALEN, addr);
- idx = crc >> 30;
- bit = (crc >> 26) & 15;
+ idx = crc >> 30;
+ bit = (crc >> 26) & 15;
- hash[idx] |= 1 << bit;
- }
+ hash[idx] |= 1 << bit;
}
static unsigned int am79c961_get_rx_mode(struct net_device *dev, u16 *hash)
@@ -310,8 +308,11 @@ static void am79c961_timer(unsigned long data)
struct net_device *dev = (struct net_device *)data;
struct dev_priv *priv = netdev_priv(dev);
unsigned int lnkstat, carrier;
+ unsigned long flags;
+ spin_lock_irqsave(&priv->chip_lock, flags);
lnkstat = read_ireg(dev->base_addr, ISALED0) & ISALED0_LNKST;
+ spin_unlock_irqrestore(&priv->chip_lock, flags);
carrier = netif_carrier_ok(dev);
if (lnkstat && !carrier) {
@@ -661,7 +662,7 @@ static const struct net_device_ops am79c961_netdev_ops = {
.ndo_open = am79c961_open,
.ndo_stop = am79c961_close,
.ndo_start_xmit = am79c961_sendpacket,
- .ndo_set_multicast_list = am79c961_setmulticastlist,
+ .ndo_set_rx_mode = am79c961_setmulticastlist,
.ndo_tx_timeout = am79c961_timeout,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/arm/am79c961a.h b/drivers/net/ethernet/amd/am79c961a.h
index fd634d32756b..fd634d32756b 100644
--- a/drivers/net/arm/am79c961a.h
+++ b/drivers/net/ethernet/amd/am79c961a.h
diff --git a/drivers/net/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c
index 241b185e6569..a9745f4ddbfe 100644
--- a/drivers/net/amd8111e.c
+++ b/drivers/net/ethernet/amd/amd8111e.c
@@ -75,6 +75,7 @@ Revision History:
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
@@ -660,15 +661,6 @@ static void amd8111e_free_ring(struct amd8111e_priv* lp)
}
}
-#if AMD8111E_VLAN_TAG_USED
-/*
-This is the receive indication function for packets with vlan tag.
-*/
-static int amd8111e_vlan_rx(struct amd8111e_priv *lp, struct sk_buff *skb, u16 vlan_tag)
-{
- return vlan_hwaccel_receive_skb(skb, lp->vlgrp,vlan_tag);
-}
-#endif
/*
This function will free all the transmit skbs that are actually transmitted by the device. It will check the ownership of the skb before freeing the skb.
@@ -763,7 +755,7 @@ static int amd8111e_rx_poll(struct napi_struct *napi, int budget)
#if AMD8111E_VLAN_TAG_USED
vtag = status & TT_MASK;
/*MAC will strip vlan tag*/
- if(lp->vlgrp != NULL && vtag !=0)
+ if (vtag != 0)
min_pkt_len =MIN_PKT_LEN - 4;
else
#endif
@@ -798,12 +790,12 @@ static int amd8111e_rx_poll(struct napi_struct *napi, int budget)
skb->protocol = eth_type_trans(skb, dev);
#if AMD8111E_VLAN_TAG_USED
- if(lp->vlgrp != NULL && (vtag == TT_VLAN_TAGGED)){
- amd8111e_vlan_rx(lp, skb,
- le16_to_cpu(lp->rx_ring[rx_index].tag_ctrl_info));
- } else
+ if (vtag == TT_VLAN_TAGGED){
+ u16 vlan_tag = le16_to_cpu(lp->rx_ring[rx_index].tag_ctrl_info);
+ __vlan_hwaccel_put_tag(skb, vlan_tag);
+ }
#endif
- netif_receive_skb(skb);
+ netif_receive_skb(skb);
/*COAL update rx coalescing parameters*/
lp->coal_conf.rx_packets++;
lp->coal_conf.rx_bytes += pkt_len;
@@ -1597,16 +1589,6 @@ static int amd8111e_change_mtu(struct net_device *dev, int new_mtu)
return err;
}
-#if AMD8111E_VLAN_TAG_USED
-static void amd8111e_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
-{
- struct amd8111e_priv *lp = netdev_priv(dev);
- spin_lock_irq(&lp->lock);
- lp->vlgrp = grp;
- spin_unlock_irq(&lp->lock);
-}
-#endif
-
static int amd8111e_enable_magicpkt(struct amd8111e_priv* lp)
{
writel( VAL1|MPPLBA, lp->mmio + CMD3);
@@ -1816,14 +1798,11 @@ static const struct net_device_ops amd8111e_netdev_ops = {
.ndo_start_xmit = amd8111e_start_xmit,
.ndo_tx_timeout = amd8111e_tx_timeout,
.ndo_get_stats = amd8111e_get_stats,
- .ndo_set_multicast_list = amd8111e_set_multicast_list,
+ .ndo_set_rx_mode = amd8111e_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = amd8111e_set_mac_address,
.ndo_do_ioctl = amd8111e_ioctl,
.ndo_change_mtu = amd8111e_change_mtu,
-#if AMD8111E_VLAN_TAG_USED
- .ndo_vlan_rx_register = amd8111e_vlan_rx_register,
-#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = amd8111e_poll,
#endif
@@ -1958,7 +1937,7 @@ static int __devinit amd8111e_probe_one(struct pci_dev *pdev,
IPG_CONVERGE_JIFFIES;
lp->ipg_data.ipg = DEFAULT_IPG;
lp->ipg_data.ipg_state = CSTATE;
- };
+ }
/* display driver and device information */
diff --git a/drivers/net/amd8111e.h b/drivers/net/ethernet/amd/amd8111e.h
index b5926af03a7e..2ff2e7a12dd0 100644
--- a/drivers/net/amd8111e.h
+++ b/drivers/net/ethernet/amd/amd8111e.h
@@ -783,9 +783,6 @@ struct amd8111e_priv{
struct net_device *next;
int mii;
struct mii_if_info mii_if;
-#if AMD8111E_VLAN_TAG_USED
- struct vlan_group *vlgrp;
-#endif
char opened;
unsigned int drv_rx_errors;
struct amd8111e_coalesce_conf coal_conf;
diff --git a/drivers/net/ethernet/amd/ariadne.c b/drivers/net/ethernet/amd/ariadne.c
new file mode 100644
index 000000000000..eb18e1fe65c8
--- /dev/null
+++ b/drivers/net/ethernet/amd/ariadne.c
@@ -0,0 +1,793 @@
+/*
+ * Amiga Linux/m68k Ariadne Ethernet Driver
+ *
+ * © Copyright 1995-2003 by Geert Uytterhoeven (geert@linux-m68k.org)
+ * Peter De Schrijver (p2@mind.be)
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This program is based on
+ *
+ * lance.c: An AMD LANCE ethernet driver for linux.
+ * Written 1993-94 by Donald Becker.
+ *
+ * Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller
+ * Advanced Micro Devices
+ * Publication #16907, Rev. B, Amendment/0, May 1994
+ *
+ * MC68230: Parallel Interface/Timer (PI/T)
+ * Motorola Semiconductors, December, 1983
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * The Ariadne is a Zorro-II board made by Village Tronic. It contains:
+ *
+ * - an Am79C960 PCnet-ISA Single-Chip Ethernet Controller with both
+ * 10BASE-2 (thin coax) and 10BASE-T (UTP) connectors
+ *
+ * - an MC68230 Parallel Interface/Timer configured as 2 parallel ports
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+/*#define DEBUG*/
+
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/zorro.h>
+#include <linux/bitops.h>
+
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/irq.h>
+
+#include "ariadne.h"
+
+#ifdef ARIADNE_DEBUG
+int ariadne_debug = ARIADNE_DEBUG;
+#else
+int ariadne_debug = 1;
+#endif
+
+/* Macros to Fix Endianness problems */
+
+/* Swap the Bytes in a WORD */
+#define swapw(x) (((x >> 8) & 0x00ff) | ((x << 8) & 0xff00))
+/* Get the Low BYTE in a WORD */
+#define lowb(x) (x & 0xff)
+/* Get the Swapped High WORD in a LONG */
+#define swhighw(x) ((((x) >> 8) & 0xff00) | (((x) >> 24) & 0x00ff))
+/* Get the Swapped Low WORD in a LONG */
+#define swloww(x) ((((x) << 8) & 0xff00) | (((x) >> 8) & 0x00ff))
+
+/* Transmit/Receive Ring Definitions */
+
+#define TX_RING_SIZE 5
+#define RX_RING_SIZE 16
+
+#define PKT_BUF_SIZE 1520
+
+/* Private Device Data */
+
+struct ariadne_private {
+ volatile struct TDRE *tx_ring[TX_RING_SIZE];
+ volatile struct RDRE *rx_ring[RX_RING_SIZE];
+ volatile u_short *tx_buff[TX_RING_SIZE];
+ volatile u_short *rx_buff[RX_RING_SIZE];
+ int cur_tx, cur_rx; /* The next free ring entry */
+ int dirty_tx; /* The ring entries to be free()ed */
+ char tx_full;
+};
+
+/* Structure Created in the Ariadne's RAM Buffer */
+
+struct lancedata {
+ struct TDRE tx_ring[TX_RING_SIZE];
+ struct RDRE rx_ring[RX_RING_SIZE];
+ u_short tx_buff[TX_RING_SIZE][PKT_BUF_SIZE / sizeof(u_short)];
+ u_short rx_buff[RX_RING_SIZE][PKT_BUF_SIZE / sizeof(u_short)];
+};
+
+static void memcpyw(volatile u_short *dest, u_short *src, int len)
+{
+ while (len >= 2) {
+ *(dest++) = *(src++);
+ len -= 2;
+ }
+ if (len == 1)
+ *dest = (*(u_char *)src) << 8;
+}
+
+static void ariadne_init_ring(struct net_device *dev)
+{
+ struct ariadne_private *priv = netdev_priv(dev);
+ volatile struct lancedata *lancedata = (struct lancedata *)dev->mem_start;
+ int i;
+
+ netif_stop_queue(dev);
+
+ priv->tx_full = 0;
+ priv->cur_rx = priv->cur_tx = 0;
+ priv->dirty_tx = 0;
+
+ /* Set up TX Ring */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ volatile struct TDRE *t = &lancedata->tx_ring[i];
+ t->TMD0 = swloww(ARIADNE_RAM +
+ offsetof(struct lancedata, tx_buff[i]));
+ t->TMD1 = swhighw(ARIADNE_RAM +
+ offsetof(struct lancedata, tx_buff[i])) |
+ TF_STP | TF_ENP;
+ t->TMD2 = swapw((u_short)-PKT_BUF_SIZE);
+ t->TMD3 = 0;
+ priv->tx_ring[i] = &lancedata->tx_ring[i];
+ priv->tx_buff[i] = lancedata->tx_buff[i];
+ netdev_dbg(dev, "TX Entry %2d at %p, Buf at %p\n",
+ i, &lancedata->tx_ring[i], lancedata->tx_buff[i]);
+ }
+
+ /* Set up RX Ring */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ volatile struct RDRE *r = &lancedata->rx_ring[i];
+ r->RMD0 = swloww(ARIADNE_RAM +
+ offsetof(struct lancedata, rx_buff[i]));
+ r->RMD1 = swhighw(ARIADNE_RAM +
+ offsetof(struct lancedata, rx_buff[i])) |
+ RF_OWN;
+ r->RMD2 = swapw((u_short)-PKT_BUF_SIZE);
+ r->RMD3 = 0x0000;
+ priv->rx_ring[i] = &lancedata->rx_ring[i];
+ priv->rx_buff[i] = lancedata->rx_buff[i];
+ netdev_dbg(dev, "RX Entry %2d at %p, Buf at %p\n",
+ i, &lancedata->rx_ring[i], lancedata->rx_buff[i]);
+ }
+}
+
+static int ariadne_rx(struct net_device *dev)
+{
+ struct ariadne_private *priv = netdev_priv(dev);
+ int entry = priv->cur_rx % RX_RING_SIZE;
+ int i;
+
+ /* If we own the next entry, it's a new packet. Send it up */
+ while (!(lowb(priv->rx_ring[entry]->RMD1) & RF_OWN)) {
+ int status = lowb(priv->rx_ring[entry]->RMD1);
+
+ if (status != (RF_STP | RF_ENP)) { /* There was an error */
+ /* There is a tricky error noted by
+ * John Murphy <murf@perftech.com> to Russ Nelson:
+ * Even with full-sized buffers it's possible for a
+ * jabber packet to use two buffers, with only the
+ * last correctly noting the error
+ */
+ /* Only count a general error at the end of a packet */
+ if (status & RF_ENP)
+ dev->stats.rx_errors++;
+ if (status & RF_FRAM)
+ dev->stats.rx_frame_errors++;
+ if (status & RF_OFLO)
+ dev->stats.rx_over_errors++;
+ if (status & RF_CRC)
+ dev->stats.rx_crc_errors++;
+ if (status & RF_BUFF)
+ dev->stats.rx_fifo_errors++;
+ priv->rx_ring[entry]->RMD1 &= 0xff00 | RF_STP | RF_ENP;
+ } else {
+ /* Malloc up new buffer, compatible with net-3 */
+ short pkt_len = swapw(priv->rx_ring[entry]->RMD3);
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len + 2);
+ if (skb == NULL) {
+ netdev_warn(dev, "Memory squeeze, deferring packet\n");
+ for (i = 0; i < RX_RING_SIZE; i++)
+ if (lowb(priv->rx_ring[(entry + i) % RX_RING_SIZE]->RMD1) & RF_OWN)
+ break;
+
+ if (i > RX_RING_SIZE - 2) {
+ dev->stats.rx_dropped++;
+ priv->rx_ring[entry]->RMD1 |= RF_OWN;
+ priv->cur_rx++;
+ }
+ break;
+ }
+
+
+ skb_reserve(skb, 2); /* 16 byte align */
+ skb_put(skb, pkt_len); /* Make room */
+ skb_copy_to_linear_data(skb,
+ (const void *)priv->rx_buff[entry],
+ pkt_len);
+ skb->protocol = eth_type_trans(skb, dev);
+ netdev_dbg(dev, "RX pkt type 0x%04x from %pM to %pM data 0x%08x len %d\n",
+ ((u_short *)skb->data)[6],
+ skb->data + 6, skb->data,
+ (int)skb->data, (int)skb->len);
+
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ }
+
+ priv->rx_ring[entry]->RMD1 |= RF_OWN;
+ entry = (++priv->cur_rx) % RX_RING_SIZE;
+ }
+
+ priv->cur_rx = priv->cur_rx % RX_RING_SIZE;
+
+ /* We should check that at least two ring entries are free.
+ * If not, we should free one and mark stats->rx_dropped++
+ */
+
+ return 0;
+}
+
+static irqreturn_t ariadne_interrupt(int irq, void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+ struct ariadne_private *priv;
+ int csr0, boguscnt;
+ int handled = 0;
+
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+
+ if (!(lance->RDP & INTR)) /* Check if any interrupt has been */
+ return IRQ_NONE; /* generated by the board */
+
+ priv = netdev_priv(dev);
+
+ boguscnt = 10;
+ while ((csr0 = lance->RDP) & (ERR | RINT | TINT) && --boguscnt >= 0) {
+ /* Acknowledge all of the current interrupt sources ASAP */
+ lance->RDP = csr0 & ~(INEA | TDMD | STOP | STRT | INIT);
+
+#ifdef DEBUG
+ if (ariadne_debug > 5) {
+ netdev_dbg(dev, "interrupt csr0=%#02x new csr=%#02x [",
+ csr0, lance->RDP);
+ if (csr0 & INTR)
+ pr_cont(" INTR");
+ if (csr0 & INEA)
+ pr_cont(" INEA");
+ if (csr0 & RXON)
+ pr_cont(" RXON");
+ if (csr0 & TXON)
+ pr_cont(" TXON");
+ if (csr0 & TDMD)
+ pr_cont(" TDMD");
+ if (csr0 & STOP)
+ pr_cont(" STOP");
+ if (csr0 & STRT)
+ pr_cont(" STRT");
+ if (csr0 & INIT)
+ pr_cont(" INIT");
+ if (csr0 & ERR)
+ pr_cont(" ERR");
+ if (csr0 & BABL)
+ pr_cont(" BABL");
+ if (csr0 & CERR)
+ pr_cont(" CERR");
+ if (csr0 & MISS)
+ pr_cont(" MISS");
+ if (csr0 & MERR)
+ pr_cont(" MERR");
+ if (csr0 & RINT)
+ pr_cont(" RINT");
+ if (csr0 & TINT)
+ pr_cont(" TINT");
+ if (csr0 & IDON)
+ pr_cont(" IDON");
+ pr_cont(" ]\n");
+ }
+#endif
+
+ if (csr0 & RINT) { /* Rx interrupt */
+ handled = 1;
+ ariadne_rx(dev);
+ }
+
+ if (csr0 & TINT) { /* Tx-done interrupt */
+ int dirty_tx = priv->dirty_tx;
+
+ handled = 1;
+ while (dirty_tx < priv->cur_tx) {
+ int entry = dirty_tx % TX_RING_SIZE;
+ int status = lowb(priv->tx_ring[entry]->TMD1);
+
+ if (status & TF_OWN)
+ break; /* It still hasn't been Txed */
+
+ priv->tx_ring[entry]->TMD1 &= 0xff00;
+
+ if (status & TF_ERR) {
+ /* There was an major error, log it */
+ int err_status = priv->tx_ring[entry]->TMD3;
+ dev->stats.tx_errors++;
+ if (err_status & EF_RTRY)
+ dev->stats.tx_aborted_errors++;
+ if (err_status & EF_LCAR)
+ dev->stats.tx_carrier_errors++;
+ if (err_status & EF_LCOL)
+ dev->stats.tx_window_errors++;
+ if (err_status & EF_UFLO) {
+ /* Ackk! On FIFO errors the Tx unit is turned off! */
+ dev->stats.tx_fifo_errors++;
+ /* Remove this verbosity later! */
+ netdev_err(dev, "Tx FIFO error! Status %04x\n",
+ csr0);
+ /* Restart the chip */
+ lance->RDP = STRT;
+ }
+ } else {
+ if (status & (TF_MORE | TF_ONE))
+ dev->stats.collisions++;
+ dev->stats.tx_packets++;
+ }
+ dirty_tx++;
+ }
+
+#ifndef final_version
+ if (priv->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ netdev_err(dev, "out-of-sync dirty pointer, %d vs. %d, full=%d\n",
+ dirty_tx, priv->cur_tx,
+ priv->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+
+ if (priv->tx_full && netif_queue_stopped(dev) &&
+ dirty_tx > priv->cur_tx - TX_RING_SIZE + 2) {
+ /* The ring is no longer full */
+ priv->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+
+ priv->dirty_tx = dirty_tx;
+ }
+
+ /* Log misc errors */
+ if (csr0 & BABL) {
+ handled = 1;
+ dev->stats.tx_errors++; /* Tx babble */
+ }
+ if (csr0 & MISS) {
+ handled = 1;
+ dev->stats.rx_errors++; /* Missed a Rx frame */
+ }
+ if (csr0 & MERR) {
+ handled = 1;
+ netdev_err(dev, "Bus master arbitration failure, status %04x\n",
+ csr0);
+ /* Restart the chip */
+ lance->RDP = STRT;
+ }
+ }
+
+ /* Clear any other interrupt, and set interrupt enable */
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = INEA | BABL | CERR | MISS | MERR | IDON;
+
+ if (ariadne_debug > 4)
+ netdev_dbg(dev, "exiting interrupt, csr%d=%#04x\n",
+ lance->RAP, lance->RDP);
+
+ return IRQ_RETVAL(handled);
+}
+
+static int ariadne_open(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+ u_short in;
+ u_long version;
+ int i;
+
+ /* Reset the LANCE */
+ in = lance->Reset;
+
+ /* Stop the LANCE */
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = STOP;
+
+ /* Check the LANCE version */
+ lance->RAP = CSR88; /* Chip ID */
+ version = swapw(lance->RDP);
+ lance->RAP = CSR89; /* Chip ID */
+ version |= swapw(lance->RDP) << 16;
+ if ((version & 0x00000fff) != 0x00000003) {
+ pr_warn("Couldn't find AMD Ethernet Chip\n");
+ return -EAGAIN;
+ }
+ if ((version & 0x0ffff000) != 0x00003000) {
+ pr_warn("Couldn't find Am79C960 (Wrong part number = %ld)\n",
+ (version & 0x0ffff000) >> 12);
+ return -EAGAIN;
+ }
+
+ netdev_dbg(dev, "Am79C960 (PCnet-ISA) Revision %ld\n",
+ (version & 0xf0000000) >> 28);
+
+ ariadne_init_ring(dev);
+
+ /* Miscellaneous Stuff */
+ lance->RAP = CSR3; /* Interrupt Masks and Deferral Control */
+ lance->RDP = 0x0000;
+ lance->RAP = CSR4; /* Test and Features Control */
+ lance->RDP = DPOLL | APAD_XMT | MFCOM | RCVCCOM | TXSTRTM | JABM;
+
+ /* Set the Multicast Table */
+ lance->RAP = CSR8; /* Logical Address Filter, LADRF[15:0] */
+ lance->RDP = 0x0000;
+ lance->RAP = CSR9; /* Logical Address Filter, LADRF[31:16] */
+ lance->RDP = 0x0000;
+ lance->RAP = CSR10; /* Logical Address Filter, LADRF[47:32] */
+ lance->RDP = 0x0000;
+ lance->RAP = CSR11; /* Logical Address Filter, LADRF[63:48] */
+ lance->RDP = 0x0000;
+
+ /* Set the Ethernet Hardware Address */
+ lance->RAP = CSR12; /* Physical Address Register, PADR[15:0] */
+ lance->RDP = ((u_short *)&dev->dev_addr[0])[0];
+ lance->RAP = CSR13; /* Physical Address Register, PADR[31:16] */
+ lance->RDP = ((u_short *)&dev->dev_addr[0])[1];
+ lance->RAP = CSR14; /* Physical Address Register, PADR[47:32] */
+ lance->RDP = ((u_short *)&dev->dev_addr[0])[2];
+
+ /* Set the Init Block Mode */
+ lance->RAP = CSR15; /* Mode Register */
+ lance->RDP = 0x0000;
+
+ /* Set the Transmit Descriptor Ring Pointer */
+ lance->RAP = CSR30; /* Base Address of Transmit Ring */
+ lance->RDP = swloww(ARIADNE_RAM + offsetof(struct lancedata, tx_ring));
+ lance->RAP = CSR31; /* Base Address of transmit Ring */
+ lance->RDP = swhighw(ARIADNE_RAM + offsetof(struct lancedata, tx_ring));
+
+ /* Set the Receive Descriptor Ring Pointer */
+ lance->RAP = CSR24; /* Base Address of Receive Ring */
+ lance->RDP = swloww(ARIADNE_RAM + offsetof(struct lancedata, rx_ring));
+ lance->RAP = CSR25; /* Base Address of Receive Ring */
+ lance->RDP = swhighw(ARIADNE_RAM + offsetof(struct lancedata, rx_ring));
+
+ /* Set the Number of RX and TX Ring Entries */
+ lance->RAP = CSR76; /* Receive Ring Length */
+ lance->RDP = swapw(((u_short)-RX_RING_SIZE));
+ lance->RAP = CSR78; /* Transmit Ring Length */
+ lance->RDP = swapw(((u_short)-TX_RING_SIZE));
+
+ /* Enable Media Interface Port Auto Select (10BASE-2/10BASE-T) */
+ lance->RAP = ISACSR2; /* Miscellaneous Configuration */
+ lance->IDP = ASEL;
+
+ /* LED Control */
+ lance->RAP = ISACSR5; /* LED1 Status */
+ lance->IDP = PSE|XMTE;
+ lance->RAP = ISACSR6; /* LED2 Status */
+ lance->IDP = PSE|COLE;
+ lance->RAP = ISACSR7; /* LED3 Status */
+ lance->IDP = PSE|RCVE;
+
+ netif_start_queue(dev);
+
+ i = request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, IRQF_SHARED,
+ dev->name, dev);
+ if (i)
+ return i;
+
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = INEA | STRT;
+
+ return 0;
+}
+
+static int ariadne_close(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+
+ netif_stop_queue(dev);
+
+ lance->RAP = CSR112; /* Missed Frame Count */
+ dev->stats.rx_missed_errors = swapw(lance->RDP);
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+
+ if (ariadne_debug > 1) {
+ netdev_dbg(dev, "Shutting down ethercard, status was %02x\n",
+ lance->RDP);
+ netdev_dbg(dev, "%lu packets missed\n",
+ dev->stats.rx_missed_errors);
+ }
+
+ /* We stop the LANCE here -- it occasionally polls memory if we don't */
+ lance->RDP = STOP;
+
+ free_irq(IRQ_AMIGA_PORTS, dev);
+
+ return 0;
+}
+
+static inline void ariadne_reset(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = STOP;
+ ariadne_init_ring(dev);
+ lance->RDP = INEA | STRT;
+ netif_start_queue(dev);
+}
+
+static void ariadne_tx_timeout(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+
+ netdev_err(dev, "transmit timed out, status %04x, resetting\n",
+ lance->RDP);
+ ariadne_reset(dev);
+ netif_wake_queue(dev);
+}
+
+static netdev_tx_t ariadne_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ariadne_private *priv = netdev_priv(dev);
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+ int entry;
+ unsigned long flags;
+ int len = skb->len;
+
+#if 0
+ if (ariadne_debug > 3) {
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ netdev_dbg(dev, "%s: csr0 %04x\n", __func__, lance->RDP);
+ lance->RDP = 0x0000;
+ }
+#endif
+
+ /* FIXME: is the 79C960 new enough to do its own padding right ? */
+ if (skb->len < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ len = ETH_ZLEN;
+ }
+
+ /* Fill in a Tx ring entry */
+
+ netdev_dbg(dev, "TX pkt type 0x%04x from %pM to %pM data 0x%08x len %d\n",
+ ((u_short *)skb->data)[6],
+ skb->data + 6, skb->data,
+ (int)skb->data, (int)skb->len);
+
+ local_irq_save(flags);
+
+ entry = priv->cur_tx % TX_RING_SIZE;
+
+ /* Caution: the write order is important here, set the base address with
+ the "ownership" bits last */
+
+ priv->tx_ring[entry]->TMD2 = swapw((u_short)-skb->len);
+ priv->tx_ring[entry]->TMD3 = 0x0000;
+ memcpyw(priv->tx_buff[entry], (u_short *)skb->data, len);
+
+#ifdef DEBUG
+ print_hex_dump(KERN_DEBUG, "tx_buff: ", DUMP_PREFIX_OFFSET, 16, 1,
+ (void *)priv->tx_buff[entry],
+ skb->len > 64 ? 64 : skb->len, true);
+#endif
+
+ priv->tx_ring[entry]->TMD1 = (priv->tx_ring[entry]->TMD1 & 0xff00)
+ | TF_OWN | TF_STP | TF_ENP;
+
+ dev_kfree_skb(skb);
+
+ priv->cur_tx++;
+ if ((priv->cur_tx >= TX_RING_SIZE) &&
+ (priv->dirty_tx >= TX_RING_SIZE)) {
+
+ netdev_dbg(dev, "*** Subtracting TX_RING_SIZE from cur_tx (%d) and dirty_tx (%d)\n",
+ priv->cur_tx, priv->dirty_tx);
+
+ priv->cur_tx -= TX_RING_SIZE;
+ priv->dirty_tx -= TX_RING_SIZE;
+ }
+ dev->stats.tx_bytes += len;
+
+ /* Trigger an immediate send poll */
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = INEA | TDMD;
+
+ if (lowb(priv->tx_ring[(entry + 1) % TX_RING_SIZE]->TMD1) != 0) {
+ netif_stop_queue(dev);
+ priv->tx_full = 1;
+ }
+ local_irq_restore(flags);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *ariadne_get_stats(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+ short saved_addr;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ saved_addr = lance->RAP;
+ lance->RAP = CSR112; /* Missed Frame Count */
+ dev->stats.rx_missed_errors = swapw(lance->RDP);
+ lance->RAP = saved_addr;
+ local_irq_restore(flags);
+
+ return &dev->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, clear multicast list
+ * num_addrs > 0 Multicast mode, receive normal and MC packets,
+ * and do best-effort filtering.
+ */
+static void set_multicast_list(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+
+ if (!netif_running(dev))
+ return;
+
+ netif_stop_queue(dev);
+
+ /* We take the simple way out and always enable promiscuous mode */
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = STOP; /* Temporarily stop the lance */
+ ariadne_init_ring(dev);
+
+ if (dev->flags & IFF_PROMISC) {
+ lance->RAP = CSR15; /* Mode Register */
+ lance->RDP = PROM; /* Set promiscuous mode */
+ } else {
+ short multicast_table[4];
+ int num_addrs = netdev_mc_count(dev);
+ int i;
+ /* We don't use the multicast table,
+ * but rely on upper-layer filtering
+ */
+ memset(multicast_table, (num_addrs == 0) ? 0 : -1,
+ sizeof(multicast_table));
+ for (i = 0; i < 4; i++) {
+ lance->RAP = CSR8 + (i << 8);
+ /* Logical Address Filter */
+ lance->RDP = swapw(multicast_table[i]);
+ }
+ lance->RAP = CSR15; /* Mode Register */
+ lance->RDP = 0x0000; /* Unset promiscuous mode */
+ }
+
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = INEA | STRT | IDON;/* Resume normal operation */
+
+ netif_wake_queue(dev);
+}
+
+
+static void __devexit ariadne_remove_one(struct zorro_dev *z)
+{
+ struct net_device *dev = zorro_get_drvdata(z);
+
+ unregister_netdev(dev);
+ release_mem_region(ZTWO_PADDR(dev->base_addr), sizeof(struct Am79C960));
+ release_mem_region(ZTWO_PADDR(dev->mem_start), ARIADNE_RAM_SIZE);
+ free_netdev(dev);
+}
+
+static struct zorro_device_id ariadne_zorro_tbl[] __devinitdata = {
+ { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(zorro, ariadne_zorro_tbl);
+
+static const struct net_device_ops ariadne_netdev_ops = {
+ .ndo_open = ariadne_open,
+ .ndo_stop = ariadne_close,
+ .ndo_start_xmit = ariadne_start_xmit,
+ .ndo_tx_timeout = ariadne_tx_timeout,
+ .ndo_get_stats = ariadne_get_stats,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+};
+
+static int __devinit ariadne_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent)
+{
+ unsigned long board = z->resource.start;
+ unsigned long base_addr = board + ARIADNE_LANCE;
+ unsigned long mem_start = board + ARIADNE_RAM;
+ struct resource *r1, *r2;
+ struct net_device *dev;
+ struct ariadne_private *priv;
+ int err;
+
+ r1 = request_mem_region(base_addr, sizeof(struct Am79C960), "Am79C960");
+ if (!r1)
+ return -EBUSY;
+ r2 = request_mem_region(mem_start, ARIADNE_RAM_SIZE, "RAM");
+ if (!r2) {
+ release_mem_region(base_addr, sizeof(struct Am79C960));
+ return -EBUSY;
+ }
+
+ dev = alloc_etherdev(sizeof(struct ariadne_private));
+ if (dev == NULL) {
+ release_mem_region(base_addr, sizeof(struct Am79C960));
+ release_mem_region(mem_start, ARIADNE_RAM_SIZE);
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(dev);
+
+ r1->name = dev->name;
+ r2->name = dev->name;
+
+ dev->dev_addr[0] = 0x00;
+ dev->dev_addr[1] = 0x60;
+ dev->dev_addr[2] = 0x30;
+ dev->dev_addr[3] = (z->rom.er_SerialNumber >> 16) & 0xff;
+ dev->dev_addr[4] = (z->rom.er_SerialNumber >> 8) & 0xff;
+ dev->dev_addr[5] = z->rom.er_SerialNumber & 0xff;
+ dev->base_addr = ZTWO_VADDR(base_addr);
+ dev->mem_start = ZTWO_VADDR(mem_start);
+ dev->mem_end = dev->mem_start + ARIADNE_RAM_SIZE;
+
+ dev->netdev_ops = &ariadne_netdev_ops;
+ dev->watchdog_timeo = 5 * HZ;
+
+ err = register_netdev(dev);
+ if (err) {
+ release_mem_region(base_addr, sizeof(struct Am79C960));
+ release_mem_region(mem_start, ARIADNE_RAM_SIZE);
+ free_netdev(dev);
+ return err;
+ }
+ zorro_set_drvdata(z, dev);
+
+ netdev_info(dev, "Ariadne at 0x%08lx, Ethernet Address %pM\n",
+ board, dev->dev_addr);
+
+ return 0;
+}
+
+static struct zorro_driver ariadne_driver = {
+ .name = "ariadne",
+ .id_table = ariadne_zorro_tbl,
+ .probe = ariadne_init_one,
+ .remove = __devexit_p(ariadne_remove_one),
+};
+
+static int __init ariadne_init_module(void)
+{
+ return zorro_register_driver(&ariadne_driver);
+}
+
+static void __exit ariadne_cleanup_module(void)
+{
+ zorro_unregister_driver(&ariadne_driver);
+}
+
+module_init(ariadne_init_module);
+module_exit(ariadne_cleanup_module);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ariadne.h b/drivers/net/ethernet/amd/ariadne.h
index 727be5cdd1ea..727be5cdd1ea 100644
--- a/drivers/net/ariadne.h
+++ b/drivers/net/ethernet/amd/ariadne.h
diff --git a/drivers/net/atarilance.c b/drivers/net/ethernet/amd/atarilance.c
index 1264d781b554..15bfa28d6c53 100644
--- a/drivers/net/atarilance.c
+++ b/drivers/net/ethernet/amd/atarilance.c
@@ -456,7 +456,7 @@ static const struct net_device_ops lance_netdev_ops = {
.ndo_open = lance_open,
.ndo_stop = lance_close,
.ndo_start_xmit = lance_start_xmit,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_set_mac_address = lance_set_mac_address,
.ndo_tx_timeout = lance_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c
index b9debcfb61a0..4865ff14bebf 100644
--- a/drivers/net/au1000_eth.c
+++ b/drivers/net/ethernet/amd/au1000_eth.c
@@ -541,19 +541,17 @@ static void au1000_reset_mac(struct net_device *dev)
* these are not descriptors sitting in memory.
*/
static void
-au1000_setup_hw_rings(struct au1000_private *aup, u32 rx_base, u32 tx_base)
+au1000_setup_hw_rings(struct au1000_private *aup, void __iomem *tx_base)
{
int i;
for (i = 0; i < NUM_RX_DMA; i++) {
- aup->rx_dma_ring[i] =
- (struct rx_dma *)
- (rx_base + sizeof(struct rx_dma)*i);
+ aup->rx_dma_ring[i] = (struct rx_dma *)
+ (tx_base + 0x100 + sizeof(struct rx_dma) * i);
}
for (i = 0; i < NUM_TX_DMA; i++) {
- aup->tx_dma_ring[i] =
- (struct tx_dma *)
- (tx_base + sizeof(struct tx_dma)*i);
+ aup->tx_dma_ring[i] = (struct tx_dma *)
+ (tx_base + sizeof(struct tx_dma) * i);
}
}
@@ -1010,7 +1008,7 @@ static const struct net_device_ops au1000_netdev_ops = {
.ndo_open = au1000_open,
.ndo_stop = au1000_close,
.ndo_start_xmit = au1000_tx,
- .ndo_set_multicast_list = au1000_multicast_list,
+ .ndo_set_rx_mode = au1000_multicast_list,
.ndo_do_ioctl = au1000_ioctl,
.ndo_tx_timeout = au1000_tx_timeout,
.ndo_set_mac_address = eth_mac_addr,
@@ -1026,7 +1024,7 @@ static int __devinit au1000_probe(struct platform_device *pdev)
struct net_device *dev = NULL;
struct db_dest *pDB, *pDBfree;
int irq, i, err = 0;
- struct resource *base, *macen;
+ struct resource *base, *macen, *macdma;
base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!base) {
@@ -1049,6 +1047,13 @@ static int __devinit au1000_probe(struct platform_device *pdev)
goto out;
}
+ macdma = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!macdma) {
+ dev_err(&pdev->dev, "failed to retrieve MACDMA registers\n");
+ err = -ENODEV;
+ goto out;
+ }
+
if (!request_mem_region(base->start, resource_size(base),
pdev->name)) {
dev_err(&pdev->dev, "failed to request memory region for base registers\n");
@@ -1063,6 +1068,13 @@ static int __devinit au1000_probe(struct platform_device *pdev)
goto err_request;
}
+ if (!request_mem_region(macdma->start, resource_size(macdma),
+ pdev->name)) {
+ dev_err(&pdev->dev, "failed to request MACDMA memory region\n");
+ err = -ENXIO;
+ goto err_macdma;
+ }
+
dev = alloc_etherdev(sizeof(struct au1000_private));
if (!dev) {
dev_err(&pdev->dev, "alloc_etherdev failed\n");
@@ -1109,10 +1121,14 @@ static int __devinit au1000_probe(struct platform_device *pdev)
}
aup->mac_id = pdev->id;
- if (pdev->id == 0)
- au1000_setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR);
- else if (pdev->id == 1)
- au1000_setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR);
+ aup->macdma = ioremap_nocache(macdma->start, resource_size(macdma));
+ if (!aup->macdma) {
+ dev_err(&pdev->dev, "failed to ioremap MACDMA registers\n");
+ err = -ENXIO;
+ goto err_remap3;
+ }
+
+ au1000_setup_hw_rings(aup, aup->macdma);
/* set a random MAC now in case platform_data doesn't provide one */
random_ether_addr(dev->dev_addr);
@@ -1252,6 +1268,8 @@ err_out:
err_mdiobus_reg:
mdiobus_free(aup->mii_bus);
err_mdiobus_alloc:
+ iounmap(aup->macdma);
+err_remap3:
iounmap(aup->enable);
err_remap2:
iounmap(aup->mac);
@@ -1261,6 +1279,8 @@ err_remap1:
err_vaddr:
free_netdev(dev);
err_alloc:
+ release_mem_region(macdma->start, resource_size(macdma));
+err_macdma:
release_mem_region(macen->start, resource_size(macen));
err_request:
release_mem_region(base->start, resource_size(base));
@@ -1293,9 +1313,13 @@ static int __devexit au1000_remove(struct platform_device *pdev)
(NUM_TX_BUFFS + NUM_RX_BUFFS),
(void *)aup->vaddr, aup->dma_addr);
+ iounmap(aup->macdma);
iounmap(aup->mac);
iounmap(aup->enable);
+ base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ release_mem_region(base->start, resource_size(base));
+
base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(base->start, resource_size(base));
diff --git a/drivers/net/au1000_eth.h b/drivers/net/ethernet/amd/au1000_eth.h
index 6229c774552c..4b7f7ad62bb8 100644
--- a/drivers/net/au1000_eth.h
+++ b/drivers/net/ethernet/amd/au1000_eth.h
@@ -124,7 +124,7 @@ struct au1000_private {
*/
struct mac_reg *mac; /* mac registers */
u32 *enable; /* address of MAC Enable Register */
-
+ void __iomem *macdma; /* base of MAC DMA port */
u32 vaddr; /* virtual address of rx/tx buffers */
dma_addr_t dma_addr; /* dma address of rx/tx buffers */
diff --git a/drivers/net/declance.c b/drivers/net/ethernet/amd/declance.c
index 219eb5ad5c12..73f8d4fa682d 100644
--- a/drivers/net/declance.c
+++ b/drivers/net/ethernet/amd/declance.c
@@ -326,15 +326,18 @@ static void load_csrs(struct lance_private *lp)
*/
static void cp_to_buf(const int type, void *to, const void *from, int len)
{
- unsigned short *tp, *fp, clen;
- unsigned char *rtp, *rfp;
+ unsigned short *tp;
+ const unsigned short *fp;
+ unsigned short clen;
+ unsigned char *rtp;
+ const unsigned char *rfp;
if (type == PMAD_LANCE) {
memcpy(to, from, len);
} else if (type == PMAX_LANCE) {
clen = len >> 1;
- tp = (unsigned short *) to;
- fp = (unsigned short *) from;
+ tp = to;
+ fp = from;
while (clen--) {
*tp++ = *fp++;
@@ -342,8 +345,8 @@ static void cp_to_buf(const int type, void *to, const void *from, int len)
}
clen = len & 1;
- rtp = (unsigned char *) tp;
- rfp = (unsigned char *) fp;
+ rtp = tp;
+ rfp = fp;
while (clen--) {
*rtp++ = *rfp++;
}
@@ -352,8 +355,8 @@ static void cp_to_buf(const int type, void *to, const void *from, int len)
* copy 16 Byte chunks
*/
clen = len >> 4;
- tp = (unsigned short *) to;
- fp = (unsigned short *) from;
+ tp = to;
+ fp = from;
while (clen--) {
*tp++ = *fp++;
*tp++ = *fp++;
@@ -382,15 +385,18 @@ static void cp_to_buf(const int type, void *to, const void *from, int len)
static void cp_from_buf(const int type, void *to, const void *from, int len)
{
- unsigned short *tp, *fp, clen;
- unsigned char *rtp, *rfp;
+ unsigned short *tp;
+ const unsigned short *fp;
+ unsigned short clen;
+ unsigned char *rtp;
+ const unsigned char *rfp;
if (type == PMAD_LANCE) {
memcpy(to, from, len);
} else if (type == PMAX_LANCE) {
clen = len >> 1;
- tp = (unsigned short *) to;
- fp = (unsigned short *) from;
+ tp = to;
+ fp = from;
while (clen--) {
*tp++ = *fp++;
fp++;
@@ -398,8 +404,8 @@ static void cp_from_buf(const int type, void *to, const void *from, int len)
clen = len & 1;
- rtp = (unsigned char *) tp;
- rfp = (unsigned char *) fp;
+ rtp = tp;
+ rfp = fp;
while (clen--) {
*rtp++ = *rfp++;
@@ -410,8 +416,8 @@ static void cp_from_buf(const int type, void *to, const void *from, int len)
* copy 16 Byte chunks
*/
clen = len >> 4;
- tp = (unsigned short *) to;
- fp = (unsigned short *) from;
+ tp = to;
+ fp = from;
while (clen--) {
*tp++ = *fp++;
*tp++ = *fp++;
@@ -940,7 +946,6 @@ static void lance_load_multicast(struct net_device *dev)
struct lance_private *lp = netdev_priv(dev);
volatile u16 *ib = (volatile u16 *)dev->mem_start;
struct netdev_hw_addr *ha;
- char *addrs;
u32 crc;
/* set all multicast bits */
@@ -959,13 +964,7 @@ static void lance_load_multicast(struct net_device *dev)
/* Add addresses */
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
-
- /* multicast address? */
- if (!(*addrs & 1))
- continue;
-
- crc = ether_crc_le(ETH_ALEN, addrs);
+ crc = ether_crc_le(ETH_ALEN, ha->addr);
crc = crc >> 26;
*lib_ptr(ib, filter[crc >> 4], lp->type) |= 1 << (crc & 0xf);
}
@@ -1016,7 +1015,7 @@ static const struct net_device_ops lance_netdev_ops = {
.ndo_stop = lance_close,
.ndo_start_xmit = lance_start_xmit,
.ndo_tx_timeout = lance_tx_timeout,
- .ndo_set_multicast_list = lance_set_multicast,
+ .ndo_set_rx_mode = lance_set_multicast,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/depca.c b/drivers/net/ethernet/amd/depca.c
index 8b0084d17c8c..681970c07f22 100644
--- a/drivers/net/depca.c
+++ b/drivers/net/ethernet/amd/depca.c
@@ -572,7 +572,7 @@ static const struct net_device_ops depca_netdev_ops = {
.ndo_open = depca_open,
.ndo_start_xmit = depca_start_xmit,
.ndo_stop = depca_close,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_do_ioctl = depca_ioctl,
.ndo_tx_timeout = depca_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
@@ -708,11 +708,11 @@ static int __init depca_hw_init (struct net_device *dev, struct device *device)
/* Tx & Rx descriptors (aligned to a quadword boundary) */
offset = (offset + DEPCA_ALIGN) & ~DEPCA_ALIGN;
- lp->rx_ring = (struct depca_rx_desc __iomem *) (lp->sh_mem + offset);
+ lp->rx_ring = lp->sh_mem + offset;
lp->rx_ring_offset = offset;
offset += (sizeof(struct depca_rx_desc) * NUM_RX_DESC);
- lp->tx_ring = (struct depca_tx_desc __iomem *) (lp->sh_mem + offset);
+ lp->tx_ring = lp->sh_mem + offset;
lp->tx_ring_offset = offset;
offset += (sizeof(struct depca_tx_desc) * NUM_TX_DESC);
@@ -1073,13 +1073,13 @@ static int depca_rx(struct net_device *dev)
i = DEPCA_PKT_STAT_SZ;
}
}
- if (buf[0] & 0x01) { /* Multicast/Broadcast */
- if ((*(s16 *) & buf[0] == -1) && (*(s16 *) & buf[2] == -1) && (*(s16 *) & buf[4] == -1)) {
+ if (is_multicast_ether_addr(buf)) {
+ if (is_broadcast_ether_addr(buf)) {
lp->pktStats.broadcast++;
} else {
lp->pktStats.multicast++;
}
- } else if ((*(s16 *) & buf[0] == *(s16 *) & dev->dev_addr[0]) && (*(s16 *) & buf[2] == *(s16 *) & dev->dev_addr[2]) && (*(s16 *) & buf[4] == *(s16 *) & dev->dev_addr[4])) {
+ } else if (compare_ether_addr(buf, dev->dev_addr) == 0) {
lp->pktStats.unicast++;
}
@@ -1270,7 +1270,6 @@ static void SetMulticastFilter(struct net_device *dev)
{
struct depca_private *lp = netdev_priv(dev);
struct netdev_hw_addr *ha;
- char *addrs;
int i, j, bit, byte;
u16 hashcode;
u32 crc;
@@ -1285,19 +1284,15 @@ static void SetMulticastFilter(struct net_device *dev)
}
/* Add multicast addresses */
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
- if ((*addrs & 0x01) == 1) { /* multicast address? */
- crc = ether_crc(ETH_ALEN, addrs);
- hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */
- for (j = 0; j < 5; j++) { /* ... in reverse order. */
- hashcode = (hashcode << 1) | ((crc >>= 1) & 1);
- }
-
-
- byte = hashcode >> 3; /* bit[3-5] -> byte in filter */
- bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
- lp->init_block.mcast_table[byte] |= bit;
+ crc = ether_crc(ETH_ALEN, ha->addr);
+ hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */
+ for (j = 0; j < 5; j++) { /* ... in reverse order. */
+ hashcode = (hashcode << 1) | ((crc >>= 1) & 1);
}
+
+ byte = hashcode >> 3; /* bit[3-5] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+ lp->init_block.mcast_table[byte] |= bit;
}
}
}
diff --git a/drivers/net/depca.h b/drivers/net/ethernet/amd/depca.h
index ee42648dbde6..cdcfe4252c16 100644
--- a/drivers/net/depca.h
+++ b/drivers/net/ethernet/amd/depca.h
@@ -157,8 +157,6 @@
*/
#include <linux/sockios.h>
-#define DEPCAIOCTL SIOCDEVPRIVATE
-
struct depca_ioctl {
unsigned short cmd; /* Command to run */
unsigned short len; /* Length of the data buffer */
diff --git a/drivers/net/hplance.c b/drivers/net/ethernet/amd/hplance.c
index a900d5bf2948..86aa0d546a5b 100644
--- a/drivers/net/hplance.c
+++ b/drivers/net/ethernet/amd/hplance.c
@@ -74,7 +74,7 @@ static const struct net_device_ops hplance_netdev_ops = {
.ndo_open = hplance_open,
.ndo_stop = hplance_close,
.ndo_start_xmit = lance_start_xmit,
- .ndo_set_multicast_list = lance_set_multicast,
+ .ndo_set_rx_mode = lance_set_multicast,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/hplance.h b/drivers/net/ethernet/amd/hplance.h
index 04aee9e0376a..04aee9e0376a 100644
--- a/drivers/net/hplance.h
+++ b/drivers/net/ethernet/amd/hplance.h
diff --git a/drivers/net/lance.c b/drivers/net/ethernet/amd/lance.c
index 02336edce748..a6e2e840884e 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/ethernet/amd/lance.c
@@ -459,7 +459,7 @@ static const struct net_device_ops lance_netdev_ops = {
.ndo_start_xmit = lance_start_xmit,
.ndo_stop = lance_close,
.ndo_get_stats = lance_get_stats,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_tx_timeout = lance_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/mvme147.c b/drivers/net/ethernet/amd/mvme147.c
index 3a7ad840d5b5..56bc47a94186 100644
--- a/drivers/net/mvme147.c
+++ b/drivers/net/ethernet/amd/mvme147.c
@@ -61,7 +61,7 @@ static const struct net_device_ops lance_netdev_ops = {
.ndo_open = m147lance_open,
.ndo_stop = m147lance_close,
.ndo_start_xmit = lance_start_xmit,
- .ndo_set_multicast_list = lance_set_multicast,
+ .ndo_set_rx_mode = lance_set_multicast,
.ndo_tx_timeout = lance_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/ni65.c b/drivers/net/ethernet/amd/ni65.c
index c75ae85eb918..6e6aa7213aab 100644
--- a/drivers/net/ni65.c
+++ b/drivers/net/ethernet/amd/ni65.c
@@ -406,7 +406,7 @@ static const struct net_device_ops ni65_netdev_ops = {
.ndo_stop = ni65_close,
.ndo_start_xmit = ni65_send_packet,
.ndo_tx_timeout = ni65_timeout,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/ni65.h b/drivers/net/ethernet/amd/ni65.h
index e6217e35edf0..e6217e35edf0 100644
--- a/drivers/net/ni65.h
+++ b/drivers/net/ethernet/amd/ni65.h
diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c
index 9d70b6595220..3accd5d21b08 100644
--- a/drivers/net/pcmcia/nmclan_cs.c
+++ b/drivers/net/ethernet/amd/nmclan_cs.c
@@ -430,7 +430,7 @@ static const struct net_device_ops mace_netdev_ops = {
.ndo_tx_timeout = mace_tx_timeout,
.ndo_set_config = mace_config,
.ndo_get_stats = mace_get_stats,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c
index b48aba9e4227..f92bc6e34828 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/ethernet/amd/pcnet32.c
@@ -82,7 +82,7 @@ static int cards_found;
/*
* VLB I/O addresses
*/
-static unsigned int pcnet32_portlist[] __initdata =
+static unsigned int pcnet32_portlist[] =
{ 0x300, 0x320, 0x340, 0x360, 0 };
static int pcnet32_debug;
@@ -270,7 +270,7 @@ struct pcnet32_private {
struct sk_buff **rx_skbuff;
dma_addr_t *tx_dma_addr;
dma_addr_t *rx_dma_addr;
- struct pcnet32_access a;
+ const struct pcnet32_access *a;
spinlock_t lock; /* Guard lock */
unsigned int cur_rx, cur_tx; /* The next free ring entry */
unsigned int rx_ring_size; /* current rx ring size */
@@ -379,7 +379,7 @@ static int pcnet32_wio_check(unsigned long addr)
return inw(addr + PCNET32_WIO_RAP) == 88;
}
-static struct pcnet32_access pcnet32_wio = {
+static const struct pcnet32_access pcnet32_wio = {
.read_csr = pcnet32_wio_read_csr,
.write_csr = pcnet32_wio_write_csr,
.read_bcr = pcnet32_wio_read_bcr,
@@ -434,7 +434,7 @@ static int pcnet32_dwio_check(unsigned long addr)
return (inl(addr + PCNET32_DWIO_RAP) & 0xffff) == 88;
}
-static struct pcnet32_access pcnet32_dwio = {
+static const struct pcnet32_access pcnet32_dwio = {
.read_csr = pcnet32_dwio_read_csr,
.write_csr = pcnet32_dwio_write_csr,
.read_bcr = pcnet32_dwio_read_bcr,
@@ -460,9 +460,9 @@ static void pcnet32_netif_start(struct net_device *dev)
u16 val;
netif_wake_queue(dev);
- val = lp->a.read_csr(ioaddr, CSR3);
+ val = lp->a->read_csr(ioaddr, CSR3);
val &= 0x00ff;
- lp->a.write_csr(ioaddr, CSR3, val);
+ lp->a->write_csr(ioaddr, CSR3, val);
napi_enable(&lp->napi);
}
@@ -730,7 +730,7 @@ static u32 pcnet32_get_link(struct net_device *dev)
r = mii_link_ok(&lp->mii_if);
} else if (lp->chip_version >= PCNET32_79C970A) {
ulong ioaddr = dev->base_addr; /* card base I/O address */
- r = (lp->a.read_bcr(ioaddr, 4) != 0xc0);
+ r = (lp->a->read_bcr(ioaddr, 4) != 0xc0);
} else { /* can not detect link on really old chips */
r = 1;
}
@@ -792,7 +792,7 @@ static int pcnet32_set_ringparam(struct net_device *dev,
pcnet32_netif_stop(dev);
spin_lock_irqsave(&lp->lock, flags);
- lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* stop the chip */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP); /* stop the chip */
size = min(ering->tx_pending, (unsigned int)TX_MAX_RING_SIZE);
@@ -868,7 +868,7 @@ static void pcnet32_ethtool_test(struct net_device *dev,
static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1)
{
struct pcnet32_private *lp = netdev_priv(dev);
- struct pcnet32_access *a = &lp->a; /* access to registers */
+ const struct pcnet32_access *a = lp->a; /* access to registers */
ulong ioaddr = dev->base_addr; /* card base I/O address */
struct sk_buff *skb; /* sk buff */
int x, i; /* counters */
@@ -888,21 +888,21 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1)
pcnet32_netif_stop(dev);
spin_lock_irqsave(&lp->lock, flags);
- lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* stop the chip */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP); /* stop the chip */
numbuffs = min(numbuffs, (int)min(lp->rx_ring_size, lp->tx_ring_size));
/* Reset the PCNET32 */
- lp->a.reset(ioaddr);
- lp->a.write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */
+ lp->a->reset(ioaddr);
+ lp->a->write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */
/* switch pcnet32 to 32bit mode */
- lp->a.write_bcr(ioaddr, 20, 2);
+ lp->a->write_bcr(ioaddr, 20, 2);
/* purge & init rings but don't actually restart */
pcnet32_restart(dev, 0x0000);
- lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* Set STOP bit */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP); /* Set STOP bit */
/* Initialize Transmit buffers. */
size = data_len + 15;
@@ -947,10 +947,10 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1)
/* set int loopback in CSR15 */
x = a->read_csr(ioaddr, CSR15) & 0xfffc;
- lp->a.write_csr(ioaddr, CSR15, x | 0x0044);
+ lp->a->write_csr(ioaddr, CSR15, x | 0x0044);
teststatus = cpu_to_le16(0x8000);
- lp->a.write_csr(ioaddr, CSR0, CSR0_START); /* Set STRT bit */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_START); /* Set STRT bit */
/* Check status of descriptors */
for (x = 0; x < numbuffs; x++) {
@@ -969,7 +969,7 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1)
}
}
- lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* Set STOP bit */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP); /* Set STOP bit */
wmb();
if (netif_msg_hw(lp) && netif_msg_pktdata(lp)) {
netdev_printk(KERN_DEBUG, dev, "RX loopback packets:\n");
@@ -1015,7 +1015,7 @@ clean_up:
pcnet32_restart(dev, CSR0_NORMAL);
} else {
pcnet32_purge_rx_ring(dev);
- lp->a.write_bcr(ioaddr, 20, 4); /* return to 16bit mode */
+ lp->a->write_bcr(ioaddr, 20, 4); /* return to 16bit mode */
}
spin_unlock_irqrestore(&lp->lock, flags);
@@ -1026,7 +1026,7 @@ static int pcnet32_set_phys_id(struct net_device *dev,
enum ethtool_phys_id_state state)
{
struct pcnet32_private *lp = netdev_priv(dev);
- struct pcnet32_access *a = &lp->a;
+ const struct pcnet32_access *a = lp->a;
ulong ioaddr = dev->base_addr;
unsigned long flags;
int i;
@@ -1067,7 +1067,7 @@ static int pcnet32_suspend(struct net_device *dev, unsigned long *flags,
{
int csr5;
struct pcnet32_private *lp = netdev_priv(dev);
- struct pcnet32_access *a = &lp->a;
+ const struct pcnet32_access *a = lp->a;
ulong ioaddr = dev->base_addr;
int ticks;
@@ -1324,8 +1324,8 @@ static int pcnet32_poll(struct napi_struct *napi, int budget)
spin_lock_irqsave(&lp->lock, flags);
if (pcnet32_tx(dev)) {
/* reset the chip to clear the error condition, then restart */
- lp->a.reset(ioaddr);
- lp->a.write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */
+ lp->a->reset(ioaddr);
+ lp->a->write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */
pcnet32_restart(dev, CSR0_START);
netif_wake_queue(dev);
}
@@ -1337,12 +1337,12 @@ static int pcnet32_poll(struct napi_struct *napi, int budget)
__napi_complete(napi);
/* clear interrupt masks */
- val = lp->a.read_csr(ioaddr, CSR3);
+ val = lp->a->read_csr(ioaddr, CSR3);
val &= 0x00ff;
- lp->a.write_csr(ioaddr, CSR3, val);
+ lp->a->write_csr(ioaddr, CSR3, val);
/* Set interrupt enable. */
- lp->a.write_csr(ioaddr, CSR0, CSR0_INTEN);
+ lp->a->write_csr(ioaddr, CSR0, CSR0_INTEN);
spin_unlock_irqrestore(&lp->lock, flags);
}
@@ -1365,7 +1365,7 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
int i, csr0;
u16 *buff = ptr;
struct pcnet32_private *lp = netdev_priv(dev);
- struct pcnet32_access *a = &lp->a;
+ const struct pcnet32_access *a = lp->a;
ulong ioaddr = dev->base_addr;
unsigned long flags;
@@ -1401,9 +1401,9 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
for (j = 0; j < PCNET32_MAX_PHYS; j++) {
if (lp->phymask & (1 << j)) {
for (i = 0; i < PCNET32_REGS_PER_PHY; i++) {
- lp->a.write_bcr(ioaddr, 33,
+ lp->a->write_bcr(ioaddr, 33,
(j << 5) | i);
- *buff++ = lp->a.read_bcr(ioaddr, 34);
+ *buff++ = lp->a->read_bcr(ioaddr, 34);
}
}
}
@@ -1505,7 +1505,7 @@ static const struct net_device_ops pcnet32_netdev_ops = {
.ndo_start_xmit = pcnet32_start_xmit,
.ndo_tx_timeout = pcnet32_tx_timeout,
.ndo_get_stats = pcnet32_get_stats,
- .ndo_set_multicast_list = pcnet32_set_multicast_list,
+ .ndo_set_rx_mode = pcnet32_set_multicast_list,
.ndo_do_ioctl = pcnet32_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
@@ -1528,7 +1528,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
int chip_version;
char *chipname;
struct net_device *dev;
- struct pcnet32_access *a = NULL;
+ const struct pcnet32_access *a = NULL;
u8 promaddr[6];
int ret = -ENODEV;
@@ -1785,7 +1785,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
((cards_found >= MAX_UNITS) || full_duplex[cards_found]))
lp->options |= PCNET32_PORT_FD;
- lp->a = *a;
+ lp->a = a;
/* prior to register_netdev, dev->name is not yet correct */
if (pcnet32_alloc_ring(dev, pci_name(lp->pci_dev))) {
@@ -1844,7 +1844,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
if (lp->mii) {
/* lp->phycount and lp->phymask are set to 0 by memset above */
- lp->mii_if.phy_id = ((lp->a.read_bcr(ioaddr, 33)) >> 5) & 0x1f;
+ lp->mii_if.phy_id = ((lp->a->read_bcr(ioaddr, 33)) >> 5) & 0x1f;
/* scan for PHYs */
for (i = 0; i < PCNET32_MAX_PHYS; i++) {
unsigned short id1, id2;
@@ -1864,7 +1864,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
pr_info("Found PHY %04x:%04x at address %d\n",
id1, id2, i);
}
- lp->a.write_bcr(ioaddr, 33, (lp->mii_if.phy_id) << 5);
+ lp->a->write_bcr(ioaddr, 33, (lp->mii_if.phy_id) << 5);
if (lp->phycount > 1)
lp->options |= PCNET32_PORT_MII;
}
@@ -2020,10 +2020,10 @@ static int pcnet32_open(struct net_device *dev)
}
/* Reset the PCNET32 */
- lp->a.reset(ioaddr);
+ lp->a->reset(ioaddr);
/* switch pcnet32 to 32bit mode */
- lp->a.write_bcr(ioaddr, 20, 2);
+ lp->a->write_bcr(ioaddr, 20, 2);
netif_printk(lp, ifup, KERN_DEBUG, dev,
"%s() irq %d tx/rx rings %#x/%#x init %#x\n",
@@ -2032,14 +2032,14 @@ static int pcnet32_open(struct net_device *dev)
(u32) (lp->init_dma_addr));
/* set/reset autoselect bit */
- val = lp->a.read_bcr(ioaddr, 2) & ~2;
+ val = lp->a->read_bcr(ioaddr, 2) & ~2;
if (lp->options & PCNET32_PORT_ASEL)
val |= 2;
- lp->a.write_bcr(ioaddr, 2, val);
+ lp->a->write_bcr(ioaddr, 2, val);
/* handle full duplex setting */
if (lp->mii_if.full_duplex) {
- val = lp->a.read_bcr(ioaddr, 9) & ~3;
+ val = lp->a->read_bcr(ioaddr, 9) & ~3;
if (lp->options & PCNET32_PORT_FD) {
val |= 1;
if (lp->options == (PCNET32_PORT_FD | PCNET32_PORT_AUI))
@@ -2049,14 +2049,14 @@ static int pcnet32_open(struct net_device *dev)
if (lp->chip_version == 0x2627)
val |= 3;
}
- lp->a.write_bcr(ioaddr, 9, val);
+ lp->a->write_bcr(ioaddr, 9, val);
}
/* set/reset GPSI bit in test register */
- val = lp->a.read_csr(ioaddr, 124) & ~0x10;
+ val = lp->a->read_csr(ioaddr, 124) & ~0x10;
if ((lp->options & PCNET32_PORT_PORTSEL) == PCNET32_PORT_GPSI)
val |= 0x10;
- lp->a.write_csr(ioaddr, 124, val);
+ lp->a->write_csr(ioaddr, 124, val);
/* Allied Telesyn AT 2700/2701 FX are 100Mbit only and do not negotiate */
if (pdev && pdev->subsystem_vendor == PCI_VENDOR_ID_AT &&
@@ -2075,24 +2075,24 @@ static int pcnet32_open(struct net_device *dev)
* duplex, and/or enable auto negotiation, and clear DANAS
*/
if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) {
- lp->a.write_bcr(ioaddr, 32,
- lp->a.read_bcr(ioaddr, 32) | 0x0080);
+ lp->a->write_bcr(ioaddr, 32,
+ lp->a->read_bcr(ioaddr, 32) | 0x0080);
/* disable Auto Negotiation, set 10Mpbs, HD */
- val = lp->a.read_bcr(ioaddr, 32) & ~0xb8;
+ val = lp->a->read_bcr(ioaddr, 32) & ~0xb8;
if (lp->options & PCNET32_PORT_FD)
val |= 0x10;
if (lp->options & PCNET32_PORT_100)
val |= 0x08;
- lp->a.write_bcr(ioaddr, 32, val);
+ lp->a->write_bcr(ioaddr, 32, val);
} else {
if (lp->options & PCNET32_PORT_ASEL) {
- lp->a.write_bcr(ioaddr, 32,
- lp->a.read_bcr(ioaddr,
+ lp->a->write_bcr(ioaddr, 32,
+ lp->a->read_bcr(ioaddr,
32) | 0x0080);
/* enable auto negotiate, setup, disable fd */
- val = lp->a.read_bcr(ioaddr, 32) & ~0x98;
+ val = lp->a->read_bcr(ioaddr, 32) & ~0x98;
val |= 0x20;
- lp->a.write_bcr(ioaddr, 32, val);
+ lp->a->write_bcr(ioaddr, 32, val);
}
}
} else {
@@ -2105,10 +2105,10 @@ static int pcnet32_open(struct net_device *dev)
* There is really no good other way to handle multiple PHYs
* other than turning off all automatics
*/
- val = lp->a.read_bcr(ioaddr, 2);
- lp->a.write_bcr(ioaddr, 2, val & ~2);
- val = lp->a.read_bcr(ioaddr, 32);
- lp->a.write_bcr(ioaddr, 32, val & ~(1 << 7)); /* stop MII manager */
+ val = lp->a->read_bcr(ioaddr, 2);
+ lp->a->write_bcr(ioaddr, 2, val & ~2);
+ val = lp->a->read_bcr(ioaddr, 32);
+ lp->a->write_bcr(ioaddr, 32, val & ~(1 << 7)); /* stop MII manager */
if (!(lp->options & PCNET32_PORT_ASEL)) {
/* setup ecmd */
@@ -2118,7 +2118,7 @@ static int pcnet32_open(struct net_device *dev)
ethtool_cmd_speed_set(&ecmd,
(lp->options & PCNET32_PORT_100) ?
SPEED_100 : SPEED_10);
- bcr9 = lp->a.read_bcr(ioaddr, 9);
+ bcr9 = lp->a->read_bcr(ioaddr, 9);
if (lp->options & PCNET32_PORT_FD) {
ecmd.duplex = DUPLEX_FULL;
@@ -2127,7 +2127,7 @@ static int pcnet32_open(struct net_device *dev)
ecmd.duplex = DUPLEX_HALF;
bcr9 |= ~(1 << 0);
}
- lp->a.write_bcr(ioaddr, 9, bcr9);
+ lp->a->write_bcr(ioaddr, 9, bcr9);
}
for (i = 0; i < PCNET32_MAX_PHYS; i++) {
@@ -2158,9 +2158,9 @@ static int pcnet32_open(struct net_device *dev)
#ifdef DO_DXSUFLO
if (lp->dxsuflo) { /* Disable transmit stop on underflow */
- val = lp->a.read_csr(ioaddr, CSR3);
+ val = lp->a->read_csr(ioaddr, CSR3);
val |= 0x40;
- lp->a.write_csr(ioaddr, CSR3, val);
+ lp->a->write_csr(ioaddr, CSR3, val);
}
#endif
@@ -2176,11 +2176,11 @@ static int pcnet32_open(struct net_device *dev)
napi_enable(&lp->napi);
/* Re-initialize the PCNET32, and start it when done. */
- lp->a.write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff));
- lp->a.write_csr(ioaddr, 2, (lp->init_dma_addr >> 16));
+ lp->a->write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff));
+ lp->a->write_csr(ioaddr, 2, (lp->init_dma_addr >> 16));
- lp->a.write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */
- lp->a.write_csr(ioaddr, CSR0, CSR0_INIT);
+ lp->a->write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_INIT);
netif_start_queue(dev);
@@ -2192,19 +2192,19 @@ static int pcnet32_open(struct net_device *dev)
i = 0;
while (i++ < 100)
- if (lp->a.read_csr(ioaddr, CSR0) & CSR0_IDON)
+ if (lp->a->read_csr(ioaddr, CSR0) & CSR0_IDON)
break;
/*
* We used to clear the InitDone bit, 0x0100, here but Mark Stockton
* reports that doing so triggers a bug in the '974.
*/
- lp->a.write_csr(ioaddr, CSR0, CSR0_NORMAL);
+ lp->a->write_csr(ioaddr, CSR0, CSR0_NORMAL);
netif_printk(lp, ifup, KERN_DEBUG, dev,
"pcnet32 open after %d ticks, init block %#x csr0 %4.4x\n",
i,
(u32) (lp->init_dma_addr),
- lp->a.read_csr(ioaddr, CSR0));
+ lp->a->read_csr(ioaddr, CSR0));
spin_unlock_irqrestore(&lp->lock, flags);
@@ -2218,7 +2218,7 @@ err_free_ring:
* Switch back to 16bit mode to avoid problems with dumb
* DOS packet driver after a warm reboot
*/
- lp->a.write_bcr(ioaddr, 20, 4);
+ lp->a->write_bcr(ioaddr, 20, 4);
err_free_irq:
spin_unlock_irqrestore(&lp->lock, flags);
@@ -2323,7 +2323,7 @@ static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits)
/* wait for stop */
for (i = 0; i < 100; i++)
- if (lp->a.read_csr(ioaddr, CSR0) & CSR0_STOP)
+ if (lp->a->read_csr(ioaddr, CSR0) & CSR0_STOP)
break;
if (i >= 100)
@@ -2335,13 +2335,13 @@ static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits)
return;
/* ReInit Ring */
- lp->a.write_csr(ioaddr, CSR0, CSR0_INIT);
+ lp->a->write_csr(ioaddr, CSR0, CSR0_INIT);
i = 0;
while (i++ < 1000)
- if (lp->a.read_csr(ioaddr, CSR0) & CSR0_IDON)
+ if (lp->a->read_csr(ioaddr, CSR0) & CSR0_IDON)
break;
- lp->a.write_csr(ioaddr, CSR0, csr0_bits);
+ lp->a->write_csr(ioaddr, CSR0, csr0_bits);
}
static void pcnet32_tx_timeout(struct net_device *dev)
@@ -2353,8 +2353,8 @@ static void pcnet32_tx_timeout(struct net_device *dev)
/* Transmitter timeout, serious problems. */
if (pcnet32_debug & NETIF_MSG_DRV)
pr_err("%s: transmit timed out, status %4.4x, resetting\n",
- dev->name, lp->a.read_csr(ioaddr, CSR0));
- lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);
+ dev->name, lp->a->read_csr(ioaddr, CSR0));
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP);
dev->stats.tx_errors++;
if (netif_msg_tx_err(lp)) {
int i;
@@ -2397,7 +2397,7 @@ static netdev_tx_t pcnet32_start_xmit(struct sk_buff *skb,
netif_printk(lp, tx_queued, KERN_DEBUG, dev,
"%s() called, csr0 %4.4x\n",
- __func__, lp->a.read_csr(ioaddr, CSR0));
+ __func__, lp->a->read_csr(ioaddr, CSR0));
/* Default status -- will not enable Successful-TxDone
* interrupt when that option is available to us.
@@ -2427,7 +2427,7 @@ static netdev_tx_t pcnet32_start_xmit(struct sk_buff *skb,
dev->stats.tx_bytes += skb->len;
/* Trigger an immediate send poll. */
- lp->a.write_csr(ioaddr, CSR0, CSR0_INTEN | CSR0_TXPOLL);
+ lp->a->write_csr(ioaddr, CSR0, CSR0_INTEN | CSR0_TXPOLL);
if (lp->tx_ring[(entry + 1) & lp->tx_mod_mask].base != 0) {
lp->tx_full = 1;
@@ -2452,16 +2452,16 @@ pcnet32_interrupt(int irq, void *dev_id)
spin_lock(&lp->lock);
- csr0 = lp->a.read_csr(ioaddr, CSR0);
+ csr0 = lp->a->read_csr(ioaddr, CSR0);
while ((csr0 & 0x8f00) && --boguscnt >= 0) {
if (csr0 == 0xffff)
break; /* PCMCIA remove happened */
/* Acknowledge all of the current interrupt sources ASAP. */
- lp->a.write_csr(ioaddr, CSR0, csr0 & ~0x004f);
+ lp->a->write_csr(ioaddr, CSR0, csr0 & ~0x004f);
netif_printk(lp, intr, KERN_DEBUG, dev,
"interrupt csr0=%#2.2x new csr=%#2.2x\n",
- csr0, lp->a.read_csr(ioaddr, CSR0));
+ csr0, lp->a->read_csr(ioaddr, CSR0));
/* Log misc errors. */
if (csr0 & 0x4000)
@@ -2488,19 +2488,19 @@ pcnet32_interrupt(int irq, void *dev_id)
if (napi_schedule_prep(&lp->napi)) {
u16 val;
/* set interrupt masks */
- val = lp->a.read_csr(ioaddr, CSR3);
+ val = lp->a->read_csr(ioaddr, CSR3);
val |= 0x5f00;
- lp->a.write_csr(ioaddr, CSR3, val);
+ lp->a->write_csr(ioaddr, CSR3, val);
__napi_schedule(&lp->napi);
break;
}
- csr0 = lp->a.read_csr(ioaddr, CSR0);
+ csr0 = lp->a->read_csr(ioaddr, CSR0);
}
netif_printk(lp, intr, KERN_DEBUG, dev,
"exiting interrupt, csr0=%#4.4x\n",
- lp->a.read_csr(ioaddr, CSR0));
+ lp->a->read_csr(ioaddr, CSR0));
spin_unlock(&lp->lock);
@@ -2520,20 +2520,20 @@ static int pcnet32_close(struct net_device *dev)
spin_lock_irqsave(&lp->lock, flags);
- dev->stats.rx_missed_errors = lp->a.read_csr(ioaddr, 112);
+ dev->stats.rx_missed_errors = lp->a->read_csr(ioaddr, 112);
netif_printk(lp, ifdown, KERN_DEBUG, dev,
"Shutting down ethercard, status was %2.2x\n",
- lp->a.read_csr(ioaddr, CSR0));
+ lp->a->read_csr(ioaddr, CSR0));
/* We stop the PCNET32 here -- it occasionally polls memory if we don't. */
- lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP);
/*
* Switch back to 16bit mode to avoid problems with dumb
* DOS packet driver after a warm reboot
*/
- lp->a.write_bcr(ioaddr, 20, 4);
+ lp->a->write_bcr(ioaddr, 20, 4);
spin_unlock_irqrestore(&lp->lock, flags);
@@ -2556,7 +2556,7 @@ static struct net_device_stats *pcnet32_get_stats(struct net_device *dev)
unsigned long flags;
spin_lock_irqsave(&lp->lock, flags);
- dev->stats.rx_missed_errors = lp->a.read_csr(ioaddr, 112);
+ dev->stats.rx_missed_errors = lp->a->read_csr(ioaddr, 112);
spin_unlock_irqrestore(&lp->lock, flags);
return &dev->stats;
@@ -2570,7 +2570,6 @@ static void pcnet32_load_multicast(struct net_device *dev)
volatile __le16 *mcast_table = (__le16 *)ib->filter;
struct netdev_hw_addr *ha;
unsigned long ioaddr = dev->base_addr;
- char *addrs;
int i;
u32 crc;
@@ -2578,10 +2577,10 @@ static void pcnet32_load_multicast(struct net_device *dev)
if (dev->flags & IFF_ALLMULTI) {
ib->filter[0] = cpu_to_le32(~0U);
ib->filter[1] = cpu_to_le32(~0U);
- lp->a.write_csr(ioaddr, PCNET32_MC_FILTER, 0xffff);
- lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+1, 0xffff);
- lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+2, 0xffff);
- lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+3, 0xffff);
+ lp->a->write_csr(ioaddr, PCNET32_MC_FILTER, 0xffff);
+ lp->a->write_csr(ioaddr, PCNET32_MC_FILTER+1, 0xffff);
+ lp->a->write_csr(ioaddr, PCNET32_MC_FILTER+2, 0xffff);
+ lp->a->write_csr(ioaddr, PCNET32_MC_FILTER+3, 0xffff);
return;
}
/* clear the multicast filter */
@@ -2590,18 +2589,12 @@ static void pcnet32_load_multicast(struct net_device *dev)
/* Add addresses */
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
-
- /* multicast address? */
- if (!(*addrs & 1))
- continue;
-
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc = crc >> 26;
mcast_table[crc >> 4] |= cpu_to_le16(1 << (crc & 0xf));
}
for (i = 0; i < 4; i++)
- lp->a.write_csr(ioaddr, PCNET32_MC_FILTER + i,
+ lp->a->write_csr(ioaddr, PCNET32_MC_FILTER + i,
le16_to_cpu(mcast_table[i]));
}
@@ -2616,28 +2609,28 @@ static void pcnet32_set_multicast_list(struct net_device *dev)
spin_lock_irqsave(&lp->lock, flags);
suspended = pcnet32_suspend(dev, &flags, 0);
- csr15 = lp->a.read_csr(ioaddr, CSR15);
+ csr15 = lp->a->read_csr(ioaddr, CSR15);
if (dev->flags & IFF_PROMISC) {
/* Log any net taps. */
netif_info(lp, hw, dev, "Promiscuous mode enabled\n");
lp->init_block->mode =
cpu_to_le16(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) <<
7);
- lp->a.write_csr(ioaddr, CSR15, csr15 | 0x8000);
+ lp->a->write_csr(ioaddr, CSR15, csr15 | 0x8000);
} else {
lp->init_block->mode =
cpu_to_le16((lp->options & PCNET32_PORT_PORTSEL) << 7);
- lp->a.write_csr(ioaddr, CSR15, csr15 & 0x7fff);
+ lp->a->write_csr(ioaddr, CSR15, csr15 & 0x7fff);
pcnet32_load_multicast(dev);
}
if (suspended) {
int csr5;
/* clear SUSPEND (SPND) - CSR5 bit 0 */
- csr5 = lp->a.read_csr(ioaddr, CSR5);
- lp->a.write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND));
+ csr5 = lp->a->read_csr(ioaddr, CSR5);
+ lp->a->write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND));
} else {
- lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP);
pcnet32_restart(dev, CSR0_NORMAL);
netif_wake_queue(dev);
}
@@ -2655,8 +2648,8 @@ static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
if (!lp->mii)
return 0;
- lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
- val_out = lp->a.read_bcr(ioaddr, 34);
+ lp->a->write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+ val_out = lp->a->read_bcr(ioaddr, 34);
return val_out;
}
@@ -2670,8 +2663,8 @@ static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
if (!lp->mii)
return;
- lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
- lp->a.write_bcr(ioaddr, 34, val);
+ lp->a->write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+ lp->a->write_bcr(ioaddr, 34, val);
}
static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
@@ -2748,7 +2741,7 @@ static void pcnet32_check_media(struct net_device *dev, int verbose)
curr_link = mii_link_ok(&lp->mii_if);
} else {
ulong ioaddr = dev->base_addr; /* card base I/O address */
- curr_link = (lp->a.read_bcr(ioaddr, 4) != 0xc0);
+ curr_link = (lp->a->read_bcr(ioaddr, 4) != 0xc0);
}
if (!curr_link) {
if (prev_link || verbose) {
@@ -2771,13 +2764,13 @@ static void pcnet32_check_media(struct net_device *dev, int verbose)
(ecmd.duplex == DUPLEX_FULL)
? "full" : "half");
}
- bcr9 = lp->a.read_bcr(dev->base_addr, 9);
+ bcr9 = lp->a->read_bcr(dev->base_addr, 9);
if ((bcr9 & (1 << 0)) != lp->mii_if.full_duplex) {
if (lp->mii_if.full_duplex)
bcr9 |= (1 << 0);
else
bcr9 &= ~(1 << 0);
- lp->a.write_bcr(dev->base_addr, 9, bcr9);
+ lp->a->write_bcr(dev->base_addr, 9, bcr9);
}
} else {
netif_info(lp, link, dev, "link up\n");
diff --git a/drivers/net/sun3lance.c b/drivers/net/ethernet/amd/sun3lance.c
index 7d9ec23aabf6..080b71fcc683 100644
--- a/drivers/net/sun3lance.c
+++ b/drivers/net/ethernet/amd/sun3lance.c
@@ -297,7 +297,7 @@ static const struct net_device_ops lance_netdev_ops = {
.ndo_open = lance_open,
.ndo_stop = lance_close,
.ndo_start_xmit = lance_start_xmit,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_set_mac_address = NULL,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/sunlance.c b/drivers/net/ethernet/amd/sunlance.c
index 32a5c7f63c43..8fda457f94cf 100644
--- a/drivers/net/sunlance.c
+++ b/drivers/net/ethernet/amd/sunlance.c
@@ -1170,7 +1170,6 @@ static void lance_load_multicast(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
struct netdev_hw_addr *ha;
- char *addrs;
u32 crc;
u32 val;
@@ -1195,12 +1194,7 @@ static void lance_load_multicast(struct net_device *dev)
/* Add addresses */
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
-
- /* multicast address? */
- if (!(*addrs & 1))
- continue;
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc = crc >> 26;
if (lp->pio_buffer) {
struct lance_init_block __iomem *ib = lp->init_block_iomem;
@@ -1304,7 +1298,7 @@ static const struct net_device_ops sparc_lance_ops = {
.ndo_open = lance_open,
.ndo_stop = lance_close,
.ndo_start_xmit = lance_start_xmit,
- .ndo_set_multicast_list = lance_set_multicast,
+ .ndo_set_rx_mode = lance_set_multicast,
.ndo_tx_timeout = lance_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/ethernet/apple/Kconfig b/drivers/net/ethernet/apple/Kconfig
new file mode 100644
index 000000000000..1375e2dc9468
--- /dev/null
+++ b/drivers/net/ethernet/apple/Kconfig
@@ -0,0 +1,65 @@
+#
+# Apple device configuration
+#
+
+config NET_VENDOR_APPLE
+ bool "Apple devices"
+ default y
+ depends on (PPC_PMAC && PPC32) || MAC
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about IBM devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_APPLE
+
+config MACE
+ tristate "MACE (Power Mac ethernet) support"
+ depends on PPC_PMAC && PPC32
+ select CRC32
+ ---help---
+ Power Macintoshes and clones with Ethernet built-in on the
+ motherboard will usually use a MACE (Medium Access Control for
+ Ethernet) interface. Say Y to include support for the MACE chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mace.
+
+config MACE_AAUI_PORT
+ bool "Use AAUI port instead of TP by default"
+ depends on MACE
+ ---help---
+ Some Apple machines (notably the Apple Network Server) which use the
+ MACE ethernet chip have an Apple AUI port (small 15-pin connector),
+ instead of an 8-pin RJ45 connector for twisted-pair ethernet. Say
+ Y here if you have such a machine. If unsure, say N.
+ The driver will default to AAUI on ANS anyway, and if you use it as
+ a module, you can provide the port_aaui=0|1 to force the driver.
+
+config BMAC
+ tristate "BMAC (G3 ethernet) support"
+ depends on PPC_PMAC && PPC32
+ select CRC32
+ ---help---
+ Say Y for support of BMAC Ethernet interfaces. These are used on G3
+ computers.
+
+ To compile this driver as a module, choose M here: the module
+ will be called bmac.
+
+config MACMACE
+ bool "Macintosh (AV) onboard MACE ethernet"
+ depends on MAC
+ select CRC32
+ ---help---
+ Support for the onboard AMD 79C940 MACE Ethernet controller used in
+ the 660AV and 840AV Macintosh. If you have one of these Macintoshes
+ say Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+endif # NET_VENDOR_APPLE
diff --git a/drivers/net/ethernet/apple/Makefile b/drivers/net/ethernet/apple/Makefile
new file mode 100644
index 000000000000..86eaa17af0f4
--- /dev/null
+++ b/drivers/net/ethernet/apple/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Apple network device drivers.
+#
+
+obj-$(CONFIG_MACE) += mace.o
+obj-$(CONFIG_BMAC) += bmac.o
+obj-$(CONFIG_MACMACE) += macmace.o
diff --git a/drivers/net/bmac.c b/drivers/net/ethernet/apple/bmac.c
index a1b8c8b8010b..d070b229dbf7 100644
--- a/drivers/net/bmac.c
+++ b/drivers/net/ethernet/apple/bmac.c
@@ -7,6 +7,7 @@
* May 1999, Al Viro: proper release of /proc/net/bmac entry, switched to
* dynamic procfs inode.
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
@@ -1014,7 +1015,6 @@ static void bmac_set_multicast(struct net_device *dev)
static void bmac_set_multicast(struct net_device *dev)
{
struct netdev_hw_addr *ha;
- char *addrs;
int i;
unsigned short rx_cfg;
u32 crc;
@@ -1038,12 +1038,7 @@ static void bmac_set_multicast(struct net_device *dev)
for(i = 0; i < 4; i++) hash_table[i] = 0;
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
-
- if(!(*addrs & 1))
- continue;
-
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc >>= 26;
hash_table[crc >> 4] |= 1 << (crc & 0xf);
}
@@ -1242,7 +1237,7 @@ static const struct net_device_ops bmac_netdev_ops = {
.ndo_open = bmac_open,
.ndo_stop = bmac_close,
.ndo_start_xmit = bmac_output,
- .ndo_set_multicast_list = bmac_set_multicast,
+ .ndo_set_rx_mode = bmac_set_multicast,
.ndo_set_mac_address = bmac_set_address,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/bmac.h b/drivers/net/ethernet/apple/bmac.h
index a1d19d867ba5..a1d19d867ba5 100644
--- a/drivers/net/bmac.h
+++ b/drivers/net/ethernet/apple/bmac.h
diff --git a/drivers/net/mace.c b/drivers/net/ethernet/apple/mace.c
index 1c5221f79d6f..bec87bd9195c 100644
--- a/drivers/net/mace.c
+++ b/drivers/net/ethernet/apple/mace.c
@@ -13,6 +13,7 @@
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/crc32.h>
#include <linux/spinlock.h>
#include <linux/bitrev.h>
@@ -99,7 +100,7 @@ static const struct net_device_ops mace_netdev_ops = {
.ndo_open = mace_open,
.ndo_stop = mace_close,
.ndo_start_xmit = mace_xmit_start,
- .ndo_set_multicast_list = mace_set_multicast,
+ .ndo_set_rx_mode = mace_set_multicast,
.ndo_set_mac_address = mace_set_address,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/mace.h b/drivers/net/ethernet/apple/mace.h
index 30b7ec0cedb5..30b7ec0cedb5 100644
--- a/drivers/net/mace.h
+++ b/drivers/net/ethernet/apple/mace.h
diff --git a/drivers/net/macmace.c b/drivers/net/ethernet/apple/macmace.c
index c685a4656878..7cf81bbffe0e 100644
--- a/drivers/net/macmace.c
+++ b/drivers/net/ethernet/apple/macmace.c
@@ -31,9 +31,8 @@
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/gfp.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_psc.h>
#include <asm/page.h>
@@ -185,7 +184,7 @@ static const struct net_device_ops mace_netdev_ops = {
.ndo_stop = mace_close,
.ndo_start_xmit = mace_xmit_start,
.ndo_tx_timeout = mace_tx_timeout,
- .ndo_set_multicast_list = mace_set_multicast,
+ .ndo_set_rx_mode = mace_set_multicast,
.ndo_set_mac_address = mace_set_address,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
@@ -203,14 +202,8 @@ static int __devinit mace_probe(struct platform_device *pdev)
unsigned char *addr;
struct net_device *dev;
unsigned char checksum = 0;
- static int found = 0;
int err;
- if (found || macintosh_config->ether_type != MAC_ETHER_MACE)
- return -ENODEV;
-
- found = 1; /* prevent 'finding' one on every device probe */
-
dev = alloc_etherdev(PRIV_BYTES);
if (!dev)
return -ENOMEM;
@@ -221,7 +214,7 @@ static int __devinit mace_probe(struct platform_device *pdev)
SET_NETDEV_DEV(dev, &pdev->dev);
dev->base_addr = (u32)MACE_BASE;
- mp->mace = (volatile struct mace *) MACE_BASE;
+ mp->mace = MACE_BASE;
dev->irq = IRQ_MAC_MACE;
mp->dma_intr = IRQ_MAC_MACE_DMA;
diff --git a/drivers/net/ethernet/atheros/Kconfig b/drivers/net/ethernet/atheros/Kconfig
new file mode 100644
index 000000000000..1ed886d421f8
--- /dev/null
+++ b/drivers/net/ethernet/atheros/Kconfig
@@ -0,0 +1,70 @@
+#
+# Atheros device configuration
+#
+
+config NET_VENDOR_ATHEROS
+ bool "Atheros devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Atheros devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_ATHEROS
+
+config ATL2
+ tristate "Atheros L2 Fast Ethernet support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports the Atheros L2 fast ethernet adapter.
+
+ To compile this driver as a module, choose M here. The module
+ will be called atl2.
+
+config ATL1
+ tristate "Atheros/Attansic L1 Gigabit Ethernet support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports the Atheros/Attansic L1 gigabit ethernet
+ adapter.
+
+ To compile this driver as a module, choose M here. The module
+ will be called atl1.
+
+config ATL1E
+ tristate "Atheros L1E Gigabit Ethernet support (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports the Atheros L1E gigabit ethernet adapter.
+
+ 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 NET_CORE
+ 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.
+
+endif # NET_VENDOR_ATHEROS
diff --git a/drivers/net/ethernet/atheros/Makefile b/drivers/net/ethernet/atheros/Makefile
new file mode 100644
index 000000000000..e7e76fb576ff
--- /dev/null
+++ b/drivers/net/ethernet/atheros/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the Atheros network device drivers.
+#
+
+obj-$(CONFIG_ATL1) += atlx/
+obj-$(CONFIG_ATL2) += atlx/
+obj-$(CONFIG_ATL1E) += atl1e/
+obj-$(CONFIG_ATL1C) += atl1c/
diff --git a/drivers/net/atl1c/Makefile b/drivers/net/ethernet/atheros/atl1c/Makefile
index c37d966952ee..c37d966952ee 100644
--- a/drivers/net/atl1c/Makefile
+++ b/drivers/net/ethernet/atheros/atl1c/Makefile
diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/ethernet/atheros/atl1c/atl1c.h
index 925929d764ca..ca70e16b6e2c 100644
--- a/drivers/net/atl1c/atl1c.h
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c.h
@@ -22,8 +22,8 @@
#ifndef _ATL1C_H_
#define _ATL1C_H_
-#include <linux/version.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/module.h>
@@ -555,7 +555,6 @@ struct atl1c_smb {
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;
diff --git a/drivers/net/atl1c/atl1c_ethtool.c b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c
index 7be884d0aaf6..7be884d0aaf6 100644
--- a/drivers/net/atl1c/atl1c_ethtool.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c
diff --git a/drivers/net/atl1c/atl1c_hw.c b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
index 23f2ab0f2fa8..23f2ab0f2fa8 100644
--- a/drivers/net/atl1c/atl1c_hw.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
diff --git a/drivers/net/atl1c/atl1c_hw.h b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h
index 655fc6c4a8a4..655fc6c4a8a4 100644
--- a/drivers/net/atl1c/atl1c_hw.h
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h
diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
index 1269ba5d6e56..02c7ed8d9eca 100644
--- a/drivers/net/atl1c/atl1c_main.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
@@ -411,29 +411,29 @@ static void atl1c_set_multi(struct net_device *netdev)
}
}
-static void atl1c_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *grp)
+static void __atl1c_vlan_mode(u32 features, u32 *mac_ctrl_data)
+{
+ if (features & NETIF_F_HW_VLAN_RX) {
+ /* 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;
+ }
+}
+
+static void atl1c_vlan_mode(struct net_device *netdev, u32 features)
{
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");
+ dev_dbg(&pdev->dev, "atl1c_vlan_mode\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;
- }
-
+ __atl1c_vlan_mode(features, &mac_ctrl_data);
AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data);
atl1c_irq_enable(adapter);
}
@@ -443,9 +443,10 @@ 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);
+ dev_dbg(&pdev->dev, "atl1c_restore_vlan\n");
+ atl1c_vlan_mode(adapter->netdev, adapter->netdev->features);
}
+
/*
* atl1c_set_mac - Change the Ethernet Address of the NIC
* @netdev: network interface device structure
@@ -483,12 +484,31 @@ static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter,
static u32 atl1c_fix_features(struct net_device *netdev, u32 features)
{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
if (netdev->mtu > MAX_TSO_FRAME_SIZE)
features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
return features;
}
+static int atl1c_set_features(struct net_device *netdev, u32 features)
+{
+ u32 changed = netdev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ atl1c_vlan_mode(netdev, features);
+
+ return 0;
+}
+
/*
* atl1c_change_mtu - Change the Maximum Transfer Unit
* @netdev: network interface device structure
@@ -1433,8 +1453,7 @@ static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter)
mac_ctrl_data |= ((hw->preamble_len & MAC_CTRL_PRMLEN_MASK) <<
MAC_CTRL_PRMLEN_SHIFT);
- if (adapter->vlgrp)
- mac_ctrl_data |= MAC_CTRL_RMV_VLAN;
+ __atl1c_vlan_mode(netdev->features, &mac_ctrl_data);
mac_ctrl_data |= MAC_CTRL_BC_EN;
if (netdev->flags & IFF_PROMISC)
@@ -1878,14 +1897,14 @@ rrs_checked:
skb_put(skb, length - ETH_FCS_LEN);
skb->protocol = eth_type_trans(skb, netdev);
atl1c_rx_checksum(adapter, skb, rrs);
- if (unlikely(adapter->vlgrp) && rrs->word3 & RRS_VLAN_INS) {
+ if (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);
+ __vlan_hwaccel_put_tag(skb, vlan);
+ }
+ netif_receive_skb(skb);
(*work_done)++;
count++;
@@ -2160,12 +2179,11 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter,
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->length = skb_frag_size(frag);
+ buffer_info->dma = skb_frag_dma_map(&adapter->pdev->dev,
+ frag, 0,
+ buffer_info->length,
+ DMA_TO_DEVICE);
ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_PAGE,
ATL1C_PCIMAP_TODEVICE);
@@ -2507,8 +2525,7 @@ static int atl1c_suspend(struct device *dev)
/* clear phy interrupt */
atl1c_read_phy_reg(hw, MII_ISR, &mii_intr_status_data);
/* Config MAC Ctrl register */
- if (adapter->vlgrp)
- mac_ctrl_data |= MAC_CTRL_RMV_VLAN;
+ __atl1c_vlan_mode(netdev->features, &mac_ctrl_data);
/* magic packet maybe Broadcast&multicast&Unicast frame */
if (wufc & AT_WUFC_MAG)
@@ -2581,14 +2598,14 @@ static const struct net_device_ops atl1c_netdev_ops = {
.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_set_mac_address = atl1c_set_mac_addr,
+ .ndo_set_rx_mode = atl1c_set_multi,
.ndo_change_mtu = atl1c_change_mtu,
.ndo_fix_features = atl1c_fix_features,
+ .ndo_set_features = atl1c_set_features,
.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
@@ -2607,11 +2624,11 @@ static int atl1c_init_netdev(struct net_device *netdev, struct pci_dev *pdev)
/* TODO: add when ready */
netdev->hw_features = NETIF_F_SG |
NETIF_F_HW_CSUM |
- NETIF_F_HW_VLAN_TX |
+ NETIF_F_HW_VLAN_RX |
NETIF_F_TSO |
NETIF_F_TSO6;
netdev->features = netdev->hw_features |
- NETIF_F_HW_VLAN_RX;
+ NETIF_F_HW_VLAN_TX;
return 0;
}
diff --git a/drivers/net/atl1e/Makefile b/drivers/net/ethernet/atheros/atl1e/Makefile
index bc11be824e76..bc11be824e76 100644
--- a/drivers/net/atl1e/Makefile
+++ b/drivers/net/ethernet/atheros/atl1e/Makefile
diff --git a/drivers/net/atl1e/atl1e.h b/drivers/net/ethernet/atheros/atl1e/atl1e.h
index 490d3b38e0cb..829b5ad71d0d 100644
--- a/drivers/net/atl1e/atl1e.h
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e.h
@@ -23,8 +23,8 @@
#ifndef _ATL1E_H_
#define _ATL1E_H_
-#include <linux/version.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/module.h>
@@ -433,7 +433,6 @@ struct atl1e_rx_ring {
struct atl1e_adapter {
struct net_device *netdev;
struct pci_dev *pdev;
- struct vlan_group *vlgrp;
struct napi_struct napi;
struct mii_if_info mii; /* MII interface info */
struct atl1e_hw hw;
diff --git a/drivers/net/atl1e/atl1e_ethtool.c b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c
index 6269438d365f..6269438d365f 100644
--- a/drivers/net/atl1e/atl1e_ethtool.c
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c
diff --git a/drivers/net/atl1e/atl1e_hw.c b/drivers/net/ethernet/atheros/atl1e/atl1e_hw.c
index 923063d2e5bb..923063d2e5bb 100644
--- a/drivers/net/atl1e/atl1e_hw.c
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_hw.c
diff --git a/drivers/net/atl1e/atl1e_hw.h b/drivers/net/ethernet/atheros/atl1e/atl1e_hw.h
index 74df16aef793..74df16aef793 100644
--- a/drivers/net/atl1e/atl1e_hw.h
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_hw.h
diff --git a/drivers/net/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
index 86a912283134..95483bcac1d0 100644
--- a/drivers/net/atl1e/atl1e_main.c
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
@@ -313,8 +313,18 @@ static void atl1e_set_multi(struct net_device *netdev)
}
}
-static void atl1e_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *grp)
+static void __atl1e_vlan_mode(u32 features, u32 *mac_ctrl_data)
+{
+ if (features & NETIF_F_HW_VLAN_RX) {
+ /* 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;
+ }
+}
+
+static void atl1e_vlan_mode(struct net_device *netdev, u32 features)
{
struct atl1e_adapter *adapter = netdev_priv(netdev);
u32 mac_ctrl_data = 0;
@@ -322,18 +332,8 @@ static void atl1e_vlan_rx_register(struct net_device *netdev,
netdev_dbg(adapter->netdev, "%s\n", __func__);
atl1e_irq_disable(adapter);
-
- adapter->vlgrp = grp;
mac_ctrl_data = AT_READ_REG(&adapter->hw, REG_MAC_CTRL);
-
- 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;
- }
-
+ __atl1e_vlan_mode(features, &mac_ctrl_data);
AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data);
atl1e_irq_enable(adapter);
}
@@ -341,8 +341,9 @@ static void atl1e_vlan_rx_register(struct net_device *netdev,
static void atl1e_restore_vlan(struct atl1e_adapter *adapter)
{
netdev_dbg(adapter->netdev, "%s\n", __func__);
- atl1e_vlan_rx_register(adapter->netdev, adapter->vlgrp);
+ atl1e_vlan_mode(adapter->netdev, adapter->netdev->features);
}
+
/*
* atl1e_set_mac - Change the Ethernet Address of the NIC
* @netdev: network interface device structure
@@ -369,6 +370,30 @@ static int atl1e_set_mac_addr(struct net_device *netdev, void *p)
return 0;
}
+static u32 atl1e_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int atl1e_set_features(struct net_device *netdev, u32 features)
+{
+ u32 changed = netdev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ atl1e_vlan_mode(netdev, features);
+
+ return 0;
+}
+
/*
* atl1e_change_mtu - Change the Maximum Transfer Unit
* @netdev: network interface device structure
@@ -800,8 +825,7 @@ static int atl1e_setup_ring_resources(struct atl1e_adapter *adapter)
/* Init TPD Ring */
tx_ring->dma = roundup(adapter->ring_dma, 8);
offset = tx_ring->dma - adapter->ring_dma;
- tx_ring->desc = (struct atl1e_tpd_desc *)
- (adapter->ring_vir_addr + offset);
+ tx_ring->desc = adapter->ring_vir_addr + offset;
size = sizeof(struct atl1e_tx_buffer) * (tx_ring->count);
tx_ring->tx_buffer = kzalloc(size, GFP_KERNEL);
if (tx_ring->tx_buffer == NULL) {
@@ -827,7 +851,7 @@ static int atl1e_setup_ring_resources(struct atl1e_adapter *adapter)
/* Init CMB dma address */
tx_ring->cmb_dma = adapter->ring_dma + offset;
- tx_ring->cmb = (u32 *)(adapter->ring_vir_addr + offset);
+ tx_ring->cmb = adapter->ring_vir_addr + offset;
offset += sizeof(u32);
for (i = 0; i < adapter->num_rx_queues; i++) {
@@ -1040,8 +1064,7 @@ static void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter)
value |= (((u32)adapter->hw.preamble_len &
MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
- if (adapter->vlgrp)
- value |= MAC_CTRL_RMV_VLAN;
+ __atl1e_vlan_mode(netdev->features, &value);
value |= MAC_CTRL_BC_EN;
if (netdev->flags & IFF_PROMISC)
@@ -1424,19 +1447,16 @@ static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que,
skb->protocol = eth_type_trans(skb, netdev);
atl1e_rx_checksum(adapter, skb, prrs);
- if (unlikely(adapter->vlgrp &&
- (prrs->pkt_flag & RRS_IS_VLAN_TAG))) {
+ if (prrs->pkt_flag & RRS_IS_VLAN_TAG) {
u16 vlan_tag = (prrs->vtag >> 4) |
((prrs->vtag & 7) << 13) |
((prrs->vtag & 8) << 9);
netdev_dbg(netdev,
"RXD VLAN TAG<RRD>=0x%04x\n",
prrs->vtag);
- vlan_hwaccel_receive_skb(skb, adapter->vlgrp,
- vlan_tag);
- } else {
- netif_receive_skb(skb);
+ __vlan_hwaccel_put_tag(skb, vlan_tag);
}
+ netif_receive_skb(skb);
skip_pkt:
/* skip current packet whether it's ok or not. */
@@ -1573,7 +1593,7 @@ static u16 atl1e_cal_tdp_req(const struct sk_buff *skb)
u16 proto_hdr_len = 0;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- fg_size = skb_shinfo(skb)->frags[i].size;
+ fg_size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
tpd_req += ((fg_size + MAX_TX_BUF_LEN - 1) >> MAX_TX_BUF_SHIFT);
}
@@ -1724,12 +1744,12 @@ static void atl1e_tx_map(struct atl1e_adapter *adapter,
}
for (f = 0; f < nr_frags; f++) {
- struct skb_frag_struct *frag;
+ const struct skb_frag_struct *frag;
u16 i;
u16 seg_num;
frag = &skb_shinfo(skb)->frags[f];
- buf_len = frag->size;
+ buf_len = skb_frag_size(frag);
seg_num = (buf_len + MAX_TX_BUF_LEN - 1) / MAX_TX_BUF_LEN;
for (i = 0; i < seg_num; i++) {
@@ -1745,12 +1765,11 @@ static void atl1e_tx_map(struct atl1e_adapter *adapter,
MAX_TX_BUF_LEN : buf_len;
buf_len -= tx_buffer->length;
- tx_buffer->dma =
- pci_map_page(adapter->pdev, frag->page,
- frag->page_offset +
- (i * MAX_TX_BUF_LEN),
- tx_buffer->length,
- PCI_DMA_TODEVICE);
+ tx_buffer->dma = skb_frag_dma_map(&adapter->pdev->dev,
+ frag,
+ (i * MAX_TX_BUF_LEN),
+ tx_buffer->length,
+ DMA_TO_DEVICE);
ATL1E_SET_PCIMAP_TYPE(tx_buffer, ATL1E_TX_PCIMAP_PAGE);
use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma);
use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) |
@@ -1812,7 +1831,7 @@ static netdev_tx_t atl1e_xmit_frame(struct sk_buff *skb,
tpd = atl1e_get_tpd(adapter);
- if (unlikely(vlan_tx_tag_present(skb))) {
+ if (vlan_tx_tag_present(skb)) {
u16 vlan_tag = vlan_tx_tag_get(skb);
u16 atl1e_vlan_tag;
@@ -2094,8 +2113,7 @@ static int atl1e_suspend(struct pci_dev *pdev, pm_message_t state)
MAC_CTRL_PRMLEN_MASK) <<
MAC_CTRL_PRMLEN_SHIFT);
- if (adapter->vlgrp)
- mac_ctrl_data |= MAC_CTRL_RMV_VLAN;
+ __atl1e_vlan_mode(netdev->features, &mac_ctrl_data);
/* magic packet maybe Broadcast&multicast&Unicast frame */
if (wufc & AT_WUFC_MAG)
@@ -2193,13 +2211,14 @@ static const struct net_device_ops atl1e_netdev_ops = {
.ndo_stop = atl1e_close,
.ndo_start_xmit = atl1e_xmit_frame,
.ndo_get_stats = atl1e_get_stats,
- .ndo_set_multicast_list = atl1e_set_multi,
+ .ndo_set_rx_mode = atl1e_set_multi,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = atl1e_set_mac_addr,
+ .ndo_fix_features = atl1e_fix_features,
+ .ndo_set_features = atl1e_set_features,
.ndo_change_mtu = atl1e_change_mtu,
.ndo_do_ioctl = atl1e_ioctl,
.ndo_tx_timeout = atl1e_tx_timeout,
- .ndo_vlan_rx_register = atl1e_vlan_rx_register,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = atl1e_netpoll,
#endif
@@ -2218,9 +2237,9 @@ static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev)
atl1e_set_ethtool_ops(netdev);
netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO |
- NETIF_F_HW_VLAN_TX;
- netdev->features = netdev->hw_features |
- NETIF_F_HW_VLAN_RX | NETIF_F_LLTX;
+ NETIF_F_HW_VLAN_RX;
+ netdev->features = netdev->hw_features | NETIF_F_LLTX |
+ NETIF_F_HW_VLAN_TX;
return 0;
}
diff --git a/drivers/net/atl1e/atl1e_param.c b/drivers/net/ethernet/atheros/atl1e/atl1e_param.c
index 0ce60b6e7ef0..0ce60b6e7ef0 100644
--- a/drivers/net/atl1e/atl1e_param.c
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_param.c
diff --git a/drivers/net/atlx/Makefile b/drivers/net/ethernet/atheros/atlx/Makefile
index e4f6022ca552..e4f6022ca552 100644
--- a/drivers/net/atlx/Makefile
+++ b/drivers/net/ethernet/atheros/atlx/Makefile
diff --git a/drivers/net/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c
index cd5789ff3726..33a4e35f5ee8 100644
--- a/drivers/net/atlx/atl1.c
+++ b/drivers/net/ethernet/atheros/atlx/atl1.c
@@ -44,7 +44,7 @@
* SMP torture testing
*/
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/byteorder.h>
#include <linux/compiler.h>
@@ -858,7 +858,7 @@ static s32 atl1_init_hw(struct atl1_hw *hw)
atl1_init_flash_opcode(hw);
if (!hw->phy_configured) {
- /* enable GPHY LinkChange Interrrupt */
+ /* enable GPHY LinkChange Interrupt */
ret_val = atl1_write_phy_reg(hw, 18, 0xC00);
if (ret_val)
return ret_val;
@@ -1285,8 +1285,7 @@ static void atl1_setup_mac_ctrl(struct atl1_adapter *adapter)
value |= (((u32) adapter->hw.preamble_len
& MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
/* vlan */
- if (adapter->vlgrp)
- value |= MAC_CTRL_RMV_VLAN;
+ __atlx_vlan_mode(netdev->features, &value);
/* rx checksum
if (adapter->rx_csum)
value |= MAC_CTRL_RX_CHKSUM_EN;
@@ -2023,13 +2022,14 @@ rrd_ok:
atl1_rx_checksum(adapter, rrd, skb);
skb->protocol = eth_type_trans(skb, adapter->netdev);
- if (adapter->vlgrp && (rrd->pkt_flg & PACKET_FLAG_VLAN_INS)) {
+ if (rrd->pkt_flg & PACKET_FLAG_VLAN_INS) {
u16 vlan_tag = (rrd->vlan_tag >> 4) |
((rrd->vlan_tag & 7) << 13) |
((rrd->vlan_tag & 8) << 9);
- vlan_hwaccel_rx(skb, adapter->vlgrp, vlan_tag);
- } else
- netif_rx(skb);
+
+ __vlan_hwaccel_put_tag(skb, vlan_tag);
+ }
+ netif_rx(skb);
/* let protocol layer free skb */
buffer_info->skb = NULL;
@@ -2267,11 +2267,11 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb,
}
for (f = 0; f < nr_frags; f++) {
- struct skb_frag_struct *frag;
+ const struct skb_frag_struct *frag;
u16 i, nseg;
frag = &skb_shinfo(skb)->frags[f];
- buf_len = frag->size;
+ buf_len = skb_frag_size(frag);
nseg = (buf_len + ATL1_MAX_TX_BUF_LEN - 1) /
ATL1_MAX_TX_BUF_LEN;
@@ -2283,10 +2283,9 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb,
buffer_info->length = (buf_len > ATL1_MAX_TX_BUF_LEN) ?
ATL1_MAX_TX_BUF_LEN : buf_len;
buf_len -= buffer_info->length;
- buffer_info->dma = pci_map_page(adapter->pdev,
- frag->page,
- frag->page_offset + (i * ATL1_MAX_TX_BUF_LEN),
- buffer_info->length, PCI_DMA_TODEVICE);
+ buffer_info->dma = skb_frag_dma_map(&adapter->pdev->dev,
+ frag, i * ATL1_MAX_TX_BUF_LEN,
+ buffer_info->length, DMA_TO_DEVICE);
if (++next_to_use == tpd_ring->count)
next_to_use = 0;
@@ -2357,7 +2356,6 @@ static netdev_tx_t atl1_xmit_frame(struct sk_buff *skb,
int count = 1;
int ret_val;
struct tx_packet_desc *ptpd;
- u16 frag_size;
u16 vlan_tag;
unsigned int nr_frags = 0;
unsigned int mss = 0;
@@ -2373,10 +2371,9 @@ static netdev_tx_t atl1_xmit_frame(struct sk_buff *skb,
nr_frags = skb_shinfo(skb)->nr_frags;
for (f = 0; f < nr_frags; f++) {
- frag_size = skb_shinfo(skb)->frags[f].size;
- if (frag_size)
- count += (frag_size + ATL1_MAX_TX_BUF_LEN - 1) /
- ATL1_MAX_TX_BUF_LEN;
+ unsigned int f_size = skb_frag_size(&skb_shinfo(skb)->frags[f]);
+ count += (f_size + ATL1_MAX_TX_BUF_LEN - 1) /
+ ATL1_MAX_TX_BUF_LEN;
}
mss = skb_shinfo(skb)->gso_size;
@@ -2783,8 +2780,7 @@ static int atl1_suspend(struct device *dev)
ctrl |= MAC_CTRL_DUPLX;
ctrl |= (((u32)adapter->hw.preamble_len &
MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
- if (adapter->vlgrp)
- ctrl |= MAC_CTRL_RMV_VLAN;
+ __atlx_vlan_mode(netdev->features, &ctrl);
if (wufc & ATLX_WUFC_MAG)
ctrl |= MAC_CTRL_BC_EN;
iowrite32(ctrl, hw->hw_addr + REG_MAC_CTRL);
@@ -2870,13 +2866,14 @@ static const struct net_device_ops atl1_netdev_ops = {
.ndo_open = atl1_open,
.ndo_stop = atl1_close,
.ndo_start_xmit = atl1_xmit_frame,
- .ndo_set_multicast_list = atlx_set_multi,
+ .ndo_set_rx_mode = atlx_set_multi,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = atl1_set_mac,
.ndo_change_mtu = atl1_change_mtu,
+ .ndo_fix_features = atlx_fix_features,
+ .ndo_set_features = atlx_set_features,
.ndo_do_ioctl = atlx_ioctl,
.ndo_tx_timeout = atlx_tx_timeout,
- .ndo_vlan_rx_register = atlx_vlan_rx_register,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = atl1_poll_controller,
#endif
@@ -2984,7 +2981,8 @@ static int __devinit atl1_probe(struct pci_dev *pdev,
netdev->features |= NETIF_F_SG;
netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX);
- netdev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_TSO;
+ netdev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_TSO |
+ NETIF_F_HW_VLAN_RX;
/* is this valid? see atl1_setup_mac_ctrl() */
netdev->features |= NETIF_F_RXCSUM;
@@ -3473,12 +3471,8 @@ static void atl1_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = ATL1_MAX_RFD;
ring->tx_max_pending = ATL1_MAX_TPD;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = rxdr->count;
ring->tx_pending = txdr->count;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
static int atl1_set_ringparam(struct net_device *netdev,
diff --git a/drivers/net/atlx/atl1.h b/drivers/net/ethernet/atheros/atlx/atl1.h
index 68de8cbfb3ec..109d6da8be97 100644
--- a/drivers/net/atlx/atl1.h
+++ b/drivers/net/ethernet/atheros/atlx/atl1.h
@@ -753,7 +753,6 @@ struct atl1_adapter {
struct pci_dev *pdev;
struct atl1_sft_stats soft_stats;
- struct vlan_group *vlgrp;
u32 rx_buffer_len;
u32 wol;
u16 link_speed;
diff --git a/drivers/net/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c
index 16249e9b6b95..1feae5928a4b 100644
--- a/drivers/net/atlx/atl2.c
+++ b/drivers/net/ethernet/atheros/atlx/atl2.c
@@ -20,7 +20,7 @@
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/crc32.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
@@ -311,8 +311,7 @@ static s32 atl2_setup_ring_resources(struct atl2_adapter *adapter)
adapter->txd_dma = adapter->ring_dma ;
offset = (adapter->txd_dma & 0x7) ? (8 - (adapter->txd_dma & 0x7)) : 0;
adapter->txd_dma += offset;
- adapter->txd_ring = (struct tx_pkt_header *) (adapter->ring_vir_addr +
- offset);
+ adapter->txd_ring = adapter->ring_vir_addr + offset;
/* Init TXS Ring */
adapter->txs_dma = adapter->txd_dma + adapter->txd_ring_size;
@@ -362,36 +361,59 @@ static inline void atl2_irq_disable(struct atl2_adapter *adapter)
synchronize_irq(adapter->pdev->irq);
}
-#ifdef NETIF_F_HW_VLAN_TX
-static void atl2_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *grp)
+static void __atl2_vlan_mode(u32 features, u32 *ctrl)
+{
+ if (features & NETIF_F_HW_VLAN_RX) {
+ /* enable VLAN tag insert/strip */
+ *ctrl |= MAC_CTRL_RMV_VLAN;
+ } else {
+ /* disable VLAN tag insert/strip */
+ *ctrl &= ~MAC_CTRL_RMV_VLAN;
+ }
+}
+
+static void atl2_vlan_mode(struct net_device *netdev, u32 features)
{
struct atl2_adapter *adapter = netdev_priv(netdev);
u32 ctrl;
atl2_irq_disable(adapter);
- adapter->vlgrp = grp;
- if (grp) {
- /* enable VLAN tag insert/strip */
- ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL);
- ctrl |= MAC_CTRL_RMV_VLAN;
- ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl);
- } else {
- /* disable VLAN tag insert/strip */
- ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL);
- ctrl &= ~MAC_CTRL_RMV_VLAN;
- ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl);
- }
+ ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL);
+ __atl2_vlan_mode(features, &ctrl);
+ ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl);
atl2_irq_enable(adapter);
}
static void atl2_restore_vlan(struct atl2_adapter *adapter)
{
- atl2_vlan_rx_register(adapter->netdev, adapter->vlgrp);
+ atl2_vlan_mode(adapter->netdev, adapter->netdev->features);
+}
+
+static u32 atl2_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int atl2_set_features(struct net_device *netdev, u32 features)
+{
+ u32 changed = netdev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ atl2_vlan_mode(netdev, features);
+
+ return 0;
}
-#endif
static void atl2_intr_rx(struct atl2_adapter *adapter)
{
@@ -425,14 +447,13 @@ static void atl2_intr_rx(struct atl2_adapter *adapter)
memcpy(skb->data, rxd->packet, rx_size);
skb_put(skb, rx_size);
skb->protocol = eth_type_trans(skb, netdev);
-#ifdef NETIF_F_HW_VLAN_TX
- if (adapter->vlgrp && (rxd->status.vlan)) {
+ if (rxd->status.vlan) {
u16 vlan_tag = (rxd->status.vtag>>4) |
((rxd->status.vtag&7) << 13) |
((rxd->status.vtag&8) << 9);
- vlan_hwaccel_rx(skb, adapter->vlgrp, vlan_tag);
- } else
-#endif
+
+ __vlan_hwaccel_put_tag(skb, vlan_tag);
+ }
netif_rx(skb);
netdev->stats.rx_bytes += rx_size;
netdev->stats.rx_packets++;
@@ -705,9 +726,7 @@ static int atl2_open(struct net_device *netdev)
atl2_set_multi(netdev);
init_ring_ptrs(adapter);
-#ifdef NETIF_F_HW_VLAN_TX
atl2_restore_vlan(adapter);
-#endif
if (atl2_configure(adapter)) {
err = -EIO;
@@ -1083,9 +1102,7 @@ static int atl2_up(struct atl2_adapter *adapter)
atl2_set_multi(netdev);
init_ring_ptrs(adapter);
-#ifdef NETIF_F_HW_VLAN_TX
atl2_restore_vlan(adapter);
-#endif
if (atl2_configure(adapter)) {
err = -EIO;
@@ -1146,8 +1163,7 @@ static void atl2_setup_mac_ctrl(struct atl2_adapter *adapter)
MAC_CTRL_PRMLEN_SHIFT);
/* vlan */
- if (adapter->vlgrp)
- value |= MAC_CTRL_RMV_VLAN;
+ __atl2_vlan_mode(netdev->features, &value);
/* filter mode */
value |= MAC_CTRL_BC_EN;
@@ -1309,13 +1325,14 @@ static const struct net_device_ops atl2_netdev_ops = {
.ndo_open = atl2_open,
.ndo_stop = atl2_close,
.ndo_start_xmit = atl2_xmit_frame,
- .ndo_set_multicast_list = atl2_set_multi,
+ .ndo_set_rx_mode = atl2_set_multi,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = atl2_set_mac,
.ndo_change_mtu = atl2_change_mtu,
+ .ndo_fix_features = atl2_fix_features,
+ .ndo_set_features = atl2_set_features,
.ndo_do_ioctl = atl2_ioctl,
.ndo_tx_timeout = atl2_tx_timeout,
- .ndo_vlan_rx_register = atl2_vlan_rx_register,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = atl2_poll_controller,
#endif
@@ -1411,7 +1428,7 @@ static int __devinit atl2_probe(struct pci_dev *pdev,
err = -EIO;
- netdev->hw_features = NETIF_F_SG;
+ netdev->hw_features = NETIF_F_SG | NETIF_F_HW_VLAN_RX;
netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX);
/* Init PHY as early as possible due to power saving issue */
diff --git a/drivers/net/atlx/atl2.h b/drivers/net/ethernet/atheros/atlx/atl2.h
index 927e4de6474d..bf9016ebdd9b 100644
--- a/drivers/net/atlx/atl2.h
+++ b/drivers/net/ethernet/atheros/atlx/atl2.h
@@ -25,7 +25,7 @@
#ifndef _ATL2_H_
#define _ATL2_H_
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/netdevice.h>
#ifndef _ATL2_HW_H_
@@ -453,9 +453,6 @@ struct atl2_adapter {
/* OS defined structs */
struct net_device *netdev;
struct pci_dev *pdev;
-#ifdef NETIF_F_HW_VLAN_TX
- struct vlan_group *vlgrp;
-#endif
u32 wol;
u16 link_speed;
u16 link_duplex;
diff --git a/drivers/net/atlx/atlx.c b/drivers/net/ethernet/atheros/atlx/atlx.c
index afb7f7dd1bb1..aabcf4b5745a 100644
--- a/drivers/net/atlx/atlx.c
+++ b/drivers/net/ethernet/atheros/atlx/atlx.c
@@ -211,8 +211,18 @@ static void atlx_link_chg_task(struct work_struct *work)
spin_unlock_irqrestore(&adapter->lock, flags);
}
-static void atlx_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *grp)
+static void __atlx_vlan_mode(u32 features, u32 *ctrl)
+{
+ if (features & NETIF_F_HW_VLAN_RX) {
+ /* enable VLAN tag insert/strip */
+ *ctrl |= MAC_CTRL_RMV_VLAN;
+ } else {
+ /* disable VLAN tag insert/strip */
+ *ctrl &= ~MAC_CTRL_RMV_VLAN;
+ }
+}
+
+static void atlx_vlan_mode(struct net_device *netdev, u32 features)
{
struct atlx_adapter *adapter = netdev_priv(netdev);
unsigned long flags;
@@ -220,27 +230,40 @@ static void atlx_vlan_rx_register(struct net_device *netdev,
spin_lock_irqsave(&adapter->lock, flags);
/* atlx_irq_disable(adapter); FIXME: confirm/remove */
- adapter->vlgrp = grp;
-
- if (grp) {
- /* enable VLAN tag insert/strip */
- ctrl = ioread32(adapter->hw.hw_addr + REG_MAC_CTRL);
- ctrl |= MAC_CTRL_RMV_VLAN;
- iowrite32(ctrl, adapter->hw.hw_addr + REG_MAC_CTRL);
- } else {
- /* disable VLAN tag insert/strip */
- ctrl = ioread32(adapter->hw.hw_addr + REG_MAC_CTRL);
- ctrl &= ~MAC_CTRL_RMV_VLAN;
- iowrite32(ctrl, adapter->hw.hw_addr + REG_MAC_CTRL);
- }
-
+ ctrl = ioread32(adapter->hw.hw_addr + REG_MAC_CTRL);
+ __atlx_vlan_mode(features, &ctrl);
+ iowrite32(ctrl, adapter->hw.hw_addr + REG_MAC_CTRL);
/* atlx_irq_enable(adapter); FIXME */
spin_unlock_irqrestore(&adapter->lock, flags);
}
static void atlx_restore_vlan(struct atlx_adapter *adapter)
{
- atlx_vlan_rx_register(adapter->netdev, adapter->vlgrp);
+ atlx_vlan_mode(adapter->netdev, adapter->netdev->features);
+}
+
+static u32 atlx_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int atlx_set_features(struct net_device *netdev, u32 features)
+{
+ u32 changed = netdev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ atlx_vlan_mode(netdev, features);
+
+ return 0;
}
#endif /* ATLX_C */
diff --git a/drivers/net/atlx/atlx.h b/drivers/net/ethernet/atheros/atlx/atlx.h
index 14054b75aa62..14054b75aa62 100644
--- a/drivers/net/atlx/atlx.h
+++ b/drivers/net/ethernet/atheros/atlx/atlx.h
diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig
new file mode 100644
index 000000000000..f15e72e81ac4
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/Kconfig
@@ -0,0 +1,122 @@
+#
+# Broadcom device configuration
+#
+
+config NET_VENDOR_BROADCOM
+ bool "Broadcom devices"
+ default y
+ depends on (SSB_POSSIBLE && HAS_DMA) || PCI || BCM63XX || \
+ SIBYTE_SB1xxx_SOC
+ ---help---
+ If you have a network (Ethernet) chipset belonging to this class,
+ say Y.
+
+ Note that the answer to this question does not directly affect
+ the kernel: saying N will just case the configurator to skip all
+ the questions regarding AMD chipsets. If you say Y, you will be asked
+ for your specific chipset/driver in the following questions.
+
+if NET_VENDOR_BROADCOM
+
+config B44
+ tristate "Broadcom 440x/47xx ethernet support"
+ depends on SSB_POSSIBLE && HAS_DMA
+ select SSB
+ select NET_CORE
+ select MII
+ ---help---
+ If you have a network (Ethernet) controller of this type, say Y
+ or M and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called b44.
+
+# Auto-select SSB PCI-HOST support, if possible
+config B44_PCI_AUTOSELECT
+ bool
+ depends on B44 && SSB_PCIHOST_POSSIBLE
+ select SSB_PCIHOST
+ default y
+
+# Auto-select SSB PCICORE driver, if possible
+config B44_PCICORE_AUTOSELECT
+ bool
+ depends on B44 && SSB_DRIVER_PCICORE_POSSIBLE
+ select SSB_DRIVER_PCICORE
+ default y
+
+config B44_PCI
+ bool
+ depends on B44_PCI_AUTOSELECT && B44_PCICORE_AUTOSELECT
+ default y
+
+config BCM63XX_ENET
+ tristate "Broadcom 63xx internal mac support"
+ depends on BCM63XX
+ select NET_CORE
+ select MII
+ select PHYLIB
+ help
+ This driver supports the ethernet MACs in the Broadcom 63xx
+ MIPS chipset family (BCM63XX).
+
+config BNX2
+ tristate "Broadcom NetXtremeII support"
+ depends on PCI
+ select CRC32
+ select FW_LOADER
+ ---help---
+ This driver supports Broadcom NetXtremeII gigabit Ethernet cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called bnx2. This is recommended.
+
+config CNIC
+ tristate "Broadcom CNIC support"
+ depends on PCI
+ select BNX2
+ select UIO
+ ---help---
+ This driver supports offload features of Broadcom NetXtremeII
+ gigabit Ethernet cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called cnic. This is recommended.
+
+config SB1250_MAC
+ tristate "SB1250 Gigabit Ethernet support"
+ depends on SIBYTE_SB1xxx_SOC
+ select PHYLIB
+ ---help---
+ This driver supports Gigabit Ethernet interfaces based on the
+ Broadcom SiByte family of System-On-a-Chip parts. They include
+ the BCM1120, BCM1125, BCM1125H, BCM1250, BCM1255, BCM1280, BCM1455
+ and BCM1480 chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sb1250-mac.
+
+config TIGON3
+ tristate "Broadcom Tigon3 support"
+ depends on PCI
+ select PHYLIB
+ ---help---
+ This driver supports Broadcom Tigon3 based gigabit Ethernet cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called tg3. This is recommended.
+
+config BNX2X
+ tristate "Broadcom NetXtremeII 10Gb support"
+ depends on PCI
+ select FW_LOADER
+ select ZLIB_INFLATE
+ select LIBCRC32C
+ select MDIO
+ ---help---
+ This driver supports Broadcom NetXtremeII 10 gigabit Ethernet cards.
+ To compile this driver as a module, choose M here: the module
+ will be called bnx2x. This is recommended.
+
+endif # NET_VENDOR_BROADCOM
diff --git a/drivers/net/ethernet/broadcom/Makefile b/drivers/net/ethernet/broadcom/Makefile
new file mode 100644
index 000000000000..b7896051d54e
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the Broadcom network device drivers.
+#
+
+obj-$(CONFIG_B44) += b44.o
+obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o
+obj-$(CONFIG_BNX2) += bnx2.o
+obj-$(CONFIG_CNIC) += cnic.o
+obj-$(CONFIG_BNX2X) += bnx2x/
+obj-$(CONFIG_SB1250_MAC) += sb1250-mac.o
+obj-$(CONFIG_TIGON3) += tg3.o
diff --git a/drivers/net/b44.c b/drivers/net/ethernet/broadcom/b44.c
index a69331e06b8d..4cf835dbc122 100644
--- a/drivers/net/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -5,7 +5,7 @@
* Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org)
* Copyright (C) 2006 Felix Fietkau (nbd@openwrt.org)
* Copyright (C) 2006 Broadcom Corporation.
- * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ * Copyright (C) 2007 Michael Buesch <m@bues.ch>
*
* Distribute under GPL.
*/
@@ -25,6 +25,7 @@
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/ssb/ssb.h>
#include <linux/slab.h>
@@ -38,6 +39,7 @@
#define DRV_MODULE_NAME "b44"
#define DRV_MODULE_VERSION "2.0"
+#define DRV_DESCRIPTION "Broadcom 44xx/47xx 10/100 PCI ethernet driver"
#define B44_DEF_MSG_ENABLE \
(NETIF_MSG_DRV | \
@@ -90,11 +92,8 @@
#define B44_ETHIPV6UDP_HLEN 62
#define B44_ETHIPV4UDP_HLEN 42
-static char version[] __devinitdata =
- DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION "\n";
-
MODULE_AUTHOR("Felix Fietkau, Florian Schirmer, Pekka Pietikainen, David S. Miller");
-MODULE_DESCRIPTION("Broadcom 44xx/47xx 10/100 PCI ethernet driver");
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
@@ -609,7 +608,7 @@ static void b44_tx(struct b44 *bp)
skb->len,
DMA_TO_DEVICE);
rp->skb = NULL;
- dev_kfree_skb_irq(skb);
+ dev_kfree_skb(skb);
}
bp->tx_cons = cons;
@@ -2115,7 +2114,7 @@ static const struct net_device_ops b44_netdev_ops = {
.ndo_stop = b44_close,
.ndo_start_xmit = b44_start_xmit,
.ndo_get_stats = b44_get_stats,
- .ndo_set_multicast_list = b44_set_rx_mode,
+ .ndo_set_rx_mode = b44_set_rx_mode,
.ndo_set_mac_address = b44_set_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = b44_ioctl,
@@ -2129,16 +2128,13 @@ static const struct net_device_ops b44_netdev_ops = {
static int __devinit b44_init_one(struct ssb_device *sdev,
const struct ssb_device_id *ent)
{
- static int b44_version_printed = 0;
struct net_device *dev;
struct b44 *bp;
int err;
instance++;
- if (b44_version_printed++ == 0)
- pr_info("%s", version);
-
+ pr_info_once("%s version %s\n", DRV_DESCRIPTION, DRV_MODULE_VERSION);
dev = alloc_etherdev(sizeof(*bp));
if (!dev) {
@@ -2224,8 +2220,7 @@ static int __devinit b44_init_one(struct ssb_device *sdev,
if (b44_phy_reset(bp) < 0)
bp->phy_addr = B44_PHY_ADDR_NO_PHY;
- netdev_info(dev, "Broadcom 44xx/47xx 10/100BaseT Ethernet %pM\n",
- dev->dev_addr);
+ netdev_info(dev, "%s %pM\n", DRV_DESCRIPTION, dev->dev_addr);
return 0;
@@ -2335,7 +2330,7 @@ static struct ssb_driver b44_ssb_driver = {
.resume = b44_resume,
};
-static inline int b44_pci_init(void)
+static inline int __init b44_pci_init(void)
{
int err = 0;
#ifdef CONFIG_B44_PCI
@@ -2344,7 +2339,7 @@ static inline int b44_pci_init(void)
return err;
}
-static inline void b44_pci_exit(void)
+static inline void __exit b44_pci_exit(void)
{
#ifdef CONFIG_B44_PCI
ssb_pcihost_unregister(&b44_pci_driver);
diff --git a/drivers/net/b44.h b/drivers/net/ethernet/broadcom/b44.h
index e1905a49279f..e1905a49279f 100644
--- a/drivers/net/b44.h
+++ b/drivers/net/ethernet/broadcom/b44.h
diff --git a/drivers/net/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
index f1573d492e90..a11a8ad94226 100644
--- a/drivers/net/bcm63xx_enet.c
+++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
@@ -18,6 +18,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/etherdevice.h>
@@ -1397,8 +1398,6 @@ static void bcm_enet_get_ringparam(struct net_device *dev,
/* rx/tx ring is actually only limited by memory */
ering->rx_max_pending = 8192;
ering->tx_max_pending = 8192;
- ering->rx_mini_max_pending = 0;
- ering->rx_jumbo_max_pending = 0;
ering->rx_pending = priv->rx_ring_size;
ering->tx_pending = priv->tx_ring_size;
}
@@ -1602,7 +1601,7 @@ static const struct net_device_ops bcm_enet_ops = {
.ndo_stop = bcm_enet_stop,
.ndo_start_xmit = bcm_enet_start_xmit,
.ndo_set_mac_address = bcm_enet_set_mac_address,
- .ndo_set_multicast_list = bcm_enet_set_multicast_list,
+ .ndo_set_rx_mode = bcm_enet_set_multicast_list,
.ndo_do_ioctl = bcm_enet_ioctl,
.ndo_change_mtu = bcm_enet_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -1646,7 +1645,7 @@ static int __devinit bcm_enet_probe(struct platform_device *pdev)
if (ret)
goto out;
- iomem_size = res_mem->end - res_mem->start + 1;
+ iomem_size = resource_size(res_mem);
if (!request_mem_region(res_mem->start, iomem_size, "bcm63xx_enet")) {
ret = -EBUSY;
goto out;
@@ -1861,7 +1860,7 @@ static int __devexit bcm_enet_remove(struct platform_device *pdev)
/* release device resources */
iounmap(priv->base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
/* disable hw block clocks */
if (priv->phy_clk) {
@@ -1897,7 +1896,7 @@ static int __devinit bcm_enet_shared_probe(struct platform_device *pdev)
if (!res)
return -ENODEV;
- iomem_size = res->end - res->start + 1;
+ iomem_size = resource_size(res);
if (!request_mem_region(res->start, iomem_size, "bcm63xx_enet_dma"))
return -EBUSY;
@@ -1915,7 +1914,7 @@ static int __devexit bcm_enet_shared_remove(struct platform_device *pdev)
iounmap(bcm_enet_shared_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
return 0;
}
diff --git a/drivers/net/bcm63xx_enet.h b/drivers/net/ethernet/broadcom/bcm63xx_enet.h
index 0e3048b788c2..0e3048b788c2 100644
--- a/drivers/net/bcm63xx_enet.h
+++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.h
diff --git a/drivers/net/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c
index 57d3293c65bd..965c7235804d 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/ethernet/broadcom/bnx2.c
@@ -36,6 +36,7 @@
#include <linux/time.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
+#include <linux/if.h>
#include <linux/if_vlan.h>
#include <net/ip.h>
#include <net/tcp.h>
@@ -56,8 +57,8 @@
#include "bnx2_fw.h"
#define DRV_MODULE_NAME "bnx2"
-#define DRV_MODULE_VERSION "2.1.6"
-#define DRV_MODULE_RELDATE "Mar 7, 2011"
+#define DRV_MODULE_VERSION "2.1.11"
+#define DRV_MODULE_RELDATE "July 20, 2011"
#define FW_MIPS_FILE_06 "bnx2/bnx2-mips-06-6.2.1.fw"
#define FW_RV2P_FILE_06 "bnx2/bnx2-rv2p-06-6.0.15.fw"
#define FW_MIPS_FILE_09 "bnx2/bnx2-mips-09-6.2.1a.fw"
@@ -385,6 +386,9 @@ static int bnx2_register_cnic(struct net_device *dev, struct cnic_ops *ops,
if (cp->drv_state & CNIC_DRV_STATE_REGD)
return -EBUSY;
+ if (!bnx2_reg_rd_ind(bp, BNX2_FW_MAX_ISCSI_CONN))
+ return -ENODEV;
+
bp->cnic_data = data;
rcu_assign_pointer(bp->cnic_ops, ops);
@@ -416,6 +420,9 @@ struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev)
struct bnx2 *bp = netdev_priv(dev);
struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+ if (!cp->max_iscsi_conn)
+ return NULL;
+
cp->drv_owner = THIS_MODULE;
cp->chip_id = bp->chip_id;
cp->pdev = bp->pdev;
@@ -2440,6 +2447,48 @@ bnx2_set_phy_loopback(struct bnx2 *bp)
return 0;
}
+static void
+bnx2_dump_mcp_state(struct bnx2 *bp)
+{
+ struct net_device *dev = bp->dev;
+ u32 mcp_p0, mcp_p1;
+
+ netdev_err(dev, "<--- start MCP states dump --->\n");
+ if (CHIP_NUM(bp) == CHIP_NUM_5709) {
+ mcp_p0 = BNX2_MCP_STATE_P0;
+ mcp_p1 = BNX2_MCP_STATE_P1;
+ } else {
+ mcp_p0 = BNX2_MCP_STATE_P0_5708;
+ mcp_p1 = BNX2_MCP_STATE_P1_5708;
+ }
+ netdev_err(dev, "DEBUG: MCP_STATE_P0[%08x] MCP_STATE_P1[%08x]\n",
+ bnx2_reg_rd_ind(bp, mcp_p0), bnx2_reg_rd_ind(bp, mcp_p1));
+ netdev_err(dev, "DEBUG: MCP mode[%08x] state[%08x] evt_mask[%08x]\n",
+ bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_MODE),
+ bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_STATE),
+ bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_EVENT_MASK));
+ netdev_err(dev, "DEBUG: pc[%08x] pc[%08x] instr[%08x]\n",
+ bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_PROGRAM_COUNTER),
+ bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_PROGRAM_COUNTER),
+ bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_INSTRUCTION));
+ netdev_err(dev, "DEBUG: shmem states:\n");
+ netdev_err(dev, "DEBUG: drv_mb[%08x] fw_mb[%08x] link_status[%08x]",
+ bnx2_shmem_rd(bp, BNX2_DRV_MB),
+ bnx2_shmem_rd(bp, BNX2_FW_MB),
+ bnx2_shmem_rd(bp, BNX2_LINK_STATUS));
+ pr_cont(" drv_pulse_mb[%08x]\n", bnx2_shmem_rd(bp, BNX2_DRV_PULSE_MB));
+ netdev_err(dev, "DEBUG: dev_info_signature[%08x] reset_type[%08x]",
+ bnx2_shmem_rd(bp, BNX2_DEV_INFO_SIGNATURE),
+ bnx2_shmem_rd(bp, BNX2_BC_STATE_RESET_TYPE));
+ pr_cont(" condition[%08x]\n",
+ bnx2_shmem_rd(bp, BNX2_BC_STATE_CONDITION));
+ DP_SHMEM_LINE(bp, 0x3cc);
+ DP_SHMEM_LINE(bp, 0x3dc);
+ DP_SHMEM_LINE(bp, 0x3ec);
+ netdev_err(dev, "DEBUG: 0x3fc[%08x]\n", bnx2_shmem_rd(bp, 0x3fc));
+ netdev_err(dev, "<--- end MCP states dump --->\n");
+}
+
static int
bnx2_fw_sync(struct bnx2 *bp, u32 msg_data, int ack, int silent)
{
@@ -2468,13 +2517,14 @@ bnx2_fw_sync(struct bnx2 *bp, u32 msg_data, int ack, int silent)
/* If we timed out, inform the firmware that this is the case. */
if ((val & BNX2_FW_MSG_ACK) != (msg_data & BNX2_DRV_MSG_SEQ)) {
- if (!silent)
- pr_err("fw sync timeout, reset code = %x\n", msg_data);
-
msg_data &= ~BNX2_DRV_MSG_CODE;
msg_data |= BNX2_DRV_MSG_CODE_FW_TIMEOUT;
bnx2_shmem_wr(bp, BNX2_DRV_MB, msg_data);
+ if (!silent) {
+ pr_err("fw sync timeout, reset code = %x\n", msg_data);
+ bnx2_dump_mcp_state(bp);
+ }
return -EBUSY;
}
@@ -2821,7 +2871,7 @@ bnx2_tx_int(struct bnx2 *bp, struct bnx2_napi *bnapi, int budget)
dma_unmap_addr(
&txr->tx_buf_ring[TX_RING_IDX(sw_cons)],
mapping),
- skb_shinfo(skb)->frags[i].size,
+ skb_frag_size(&skb_shinfo(skb)->frags[i]),
PCI_DMA_TODEVICE);
}
@@ -2880,8 +2930,8 @@ bnx2_reuse_rx_skb_pages(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr,
shinfo = skb_shinfo(skb);
shinfo->nr_frags--;
- page = shinfo->frags[shinfo->nr_frags].page;
- shinfo->frags[shinfo->nr_frags].page = NULL;
+ page = skb_frag_page(&shinfo->frags[shinfo->nr_frags]);
+ __skb_frag_set_page(&shinfo->frags[shinfo->nr_frags], NULL);
cons_rx_pg->page = page;
dev_kfree_skb(skb);
@@ -2999,9 +3049,8 @@ bnx2_rx_skb(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr, struct sk_buff *skb,
} else {
skb_frag_t *frag =
&skb_shinfo(skb)->frags[i - 1];
- frag->size -= tail;
+ skb_frag_size_sub(frag, tail);
skb->data_len -= tail;
- skb->truesize -= tail;
}
return 0;
}
@@ -3033,7 +3082,7 @@ bnx2_rx_skb(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr, struct sk_buff *skb,
frag_size -= frag_len;
skb->data_len += frag_len;
- skb->truesize += frag_len;
+ skb->truesize += PAGE_SIZE;
skb->len += frag_len;
pg_prod = NEXT_RX_BD(pg_prod);
@@ -3573,7 +3622,7 @@ bnx2_set_rx_mode(struct net_device *dev)
spin_unlock_bh(&bp->phy_lock);
}
-static int __devinit
+static int
check_fw_section(const struct firmware *fw,
const struct bnx2_fw_file_section *section,
u32 alignment, bool non_empty)
@@ -3589,7 +3638,7 @@ check_fw_section(const struct firmware *fw,
return 0;
}
-static int __devinit
+static int
check_mips_fw_entry(const struct firmware *fw,
const struct bnx2_mips_fw_file_entry *entry)
{
@@ -3600,8 +3649,16 @@ check_mips_fw_entry(const struct firmware *fw,
return 0;
}
-static int __devinit
-bnx2_request_firmware(struct bnx2 *bp)
+static void bnx2_release_firmware(struct bnx2 *bp)
+{
+ if (bp->rv2p_firmware) {
+ release_firmware(bp->mips_firmware);
+ release_firmware(bp->rv2p_firmware);
+ bp->rv2p_firmware = NULL;
+ }
+}
+
+static int bnx2_request_uncached_firmware(struct bnx2 *bp)
{
const char *mips_fw_file, *rv2p_fw_file;
const struct bnx2_mips_fw_file *mips_fw;
@@ -3623,13 +3680,13 @@ bnx2_request_firmware(struct bnx2 *bp)
rc = request_firmware(&bp->mips_firmware, mips_fw_file, &bp->pdev->dev);
if (rc) {
pr_err("Can't load firmware file \"%s\"\n", mips_fw_file);
- return rc;
+ goto out;
}
rc = request_firmware(&bp->rv2p_firmware, rv2p_fw_file, &bp->pdev->dev);
if (rc) {
pr_err("Can't load firmware file \"%s\"\n", rv2p_fw_file);
- return rc;
+ goto err_release_mips_firmware;
}
mips_fw = (const struct bnx2_mips_fw_file *) bp->mips_firmware->data;
rv2p_fw = (const struct bnx2_rv2p_fw_file *) bp->rv2p_firmware->data;
@@ -3640,16 +3697,30 @@ bnx2_request_firmware(struct bnx2 *bp)
check_mips_fw_entry(bp->mips_firmware, &mips_fw->tpat) ||
check_mips_fw_entry(bp->mips_firmware, &mips_fw->txp)) {
pr_err("Firmware file \"%s\" is invalid\n", mips_fw_file);
- return -EINVAL;
+ rc = -EINVAL;
+ goto err_release_firmware;
}
if (bp->rv2p_firmware->size < sizeof(*rv2p_fw) ||
check_fw_section(bp->rv2p_firmware, &rv2p_fw->proc1.rv2p, 8, true) ||
check_fw_section(bp->rv2p_firmware, &rv2p_fw->proc2.rv2p, 8, true)) {
pr_err("Firmware file \"%s\" is invalid\n", rv2p_fw_file);
- return -EINVAL;
+ rc = -EINVAL;
+ goto err_release_firmware;
}
+out:
+ return rc;
- return 0;
+err_release_firmware:
+ release_firmware(bp->rv2p_firmware);
+ bp->rv2p_firmware = NULL;
+err_release_mips_firmware:
+ release_firmware(bp->mips_firmware);
+ goto out;
+}
+
+static int bnx2_request_firmware(struct bnx2 *bp)
+{
+ return bp->rv2p_firmware ? 0 : bnx2_request_uncached_firmware(bp);
}
static u32
@@ -5324,7 +5395,7 @@ bnx2_free_tx_skbs(struct bnx2 *bp)
tx_buf = &txr->tx_buf_ring[TX_RING_IDX(j)];
dma_unmap_page(&bp->pdev->dev,
dma_unmap_addr(tx_buf, mapping),
- skb_shinfo(skb)->frags[k].size,
+ skb_frag_size(&skb_shinfo(skb)->frags[k]),
PCI_DMA_TODEVICE);
}
dev_kfree_skb(skb);
@@ -6217,6 +6288,10 @@ bnx2_open(struct net_device *dev)
struct bnx2 *bp = netdev_priv(dev);
int rc;
+ rc = bnx2_request_firmware(bp);
+ if (rc < 0)
+ goto out;
+
netif_carrier_off(dev);
bnx2_set_power_state(bp, PCI_D0);
@@ -6277,8 +6352,8 @@ bnx2_open(struct net_device *dev)
netdev_info(dev, "using MSIX\n");
netif_tx_start_all_queues(dev);
-
- return 0;
+out:
+ return rc;
open_err:
bnx2_napi_disable(bp);
@@ -6286,13 +6361,15 @@ open_err:
bnx2_free_irq(bp);
bnx2_free_mem(bp);
bnx2_del_napi(bp);
- return rc;
+ bnx2_release_firmware(bp);
+ goto out;
}
static void
bnx2_reset_task(struct work_struct *work)
{
struct bnx2 *bp = container_of(work, struct bnx2, reset_task);
+ int rc;
rtnl_lock();
if (!netif_running(bp->dev)) {
@@ -6302,7 +6379,14 @@ bnx2_reset_task(struct work_struct *work)
bnx2_netif_stop(bp, true);
- bnx2_init_nic(bp, 1);
+ rc = bnx2_init_nic(bp, 1);
+ if (rc) {
+ netdev_err(bp->dev, "failed to reset NIC, closing\n");
+ bnx2_napi_enable(bp);
+ dev_close(bp->dev);
+ rtnl_unlock();
+ return;
+ }
atomic_set(&bp->intr_sem, 1);
bnx2_netif_start(bp, true);
@@ -6313,7 +6397,7 @@ static void
bnx2_dump_state(struct bnx2 *bp)
{
struct net_device *dev = bp->dev;
- u32 mcp_p0, mcp_p1, val1, val2;
+ u32 val1, val2;
pci_read_config_dword(bp->pdev, PCI_COMMAND, &val1);
netdev_err(dev, "DEBUG: intr_sem[%x] PCI_CMD[%08x]\n",
@@ -6326,15 +6410,6 @@ bnx2_dump_state(struct bnx2 *bp)
REG_RD(bp, BNX2_EMAC_RX_STATUS));
netdev_err(dev, "DEBUG: RPM_MGMT_PKT_CTRL[%08x]\n",
REG_RD(bp, BNX2_RPM_MGMT_PKT_CTRL));
- if (CHIP_NUM(bp) == CHIP_NUM_5709) {
- mcp_p0 = BNX2_MCP_STATE_P0;
- mcp_p1 = BNX2_MCP_STATE_P1;
- } else {
- mcp_p0 = BNX2_MCP_STATE_P0_5708;
- mcp_p1 = BNX2_MCP_STATE_P1_5708;
- }
- netdev_err(dev, "DEBUG: MCP_STATE_P0[%08x] MCP_STATE_P1[%08x]\n",
- bnx2_reg_rd_ind(bp, mcp_p0), bnx2_reg_rd_ind(bp, mcp_p1));
netdev_err(dev, "DEBUG: HC_STATS_INTERRUPT_STATUS[%08x]\n",
REG_RD(bp, BNX2_HC_STATS_INTERRUPT_STATUS));
if (bp->flags & BNX2_FLAG_USING_MSIX)
@@ -6348,6 +6423,7 @@ bnx2_tx_timeout(struct net_device *dev)
struct bnx2 *bp = netdev_priv(dev);
bnx2_dump_state(bp);
+ bnx2_dump_mcp_state(bp);
/* This allows the netif to be shutdown gracefully before resetting */
schedule_work(&bp->reset_task);
@@ -6454,15 +6530,15 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_buf->is_gso = skb_is_gso(skb);
for (i = 0; i < last_frag; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
prod = NEXT_TX_BD(prod);
ring_prod = TX_RING_IDX(prod);
txbd = &txr->tx_desc_ring[ring_prod];
- len = frag->size;
- mapping = dma_map_page(&bp->pdev->dev, frag->page, frag->page_offset,
- len, PCI_DMA_TODEVICE);
+ len = skb_frag_size(frag);
+ mapping = skb_frag_dma_map(&bp->pdev->dev, frag, 0, len,
+ DMA_TO_DEVICE);
if (dma_mapping_error(&bp->pdev->dev, mapping))
goto dma_error;
dma_unmap_addr_set(&txr->tx_buf_ring[ring_prod], mapping,
@@ -6518,7 +6594,7 @@ dma_error:
ring_prod = TX_RING_IDX(prod);
tx_buf = &txr->tx_buf_ring[ring_prod];
dma_unmap_page(&bp->pdev->dev, dma_unmap_addr(tx_buf, mapping),
- skb_shinfo(skb)->frags[i].size,
+ skb_frag_size(&skb_shinfo(skb)->frags[i]),
PCI_DMA_TODEVICE);
}
@@ -6532,8 +6608,6 @@ bnx2_close(struct net_device *dev)
{
struct bnx2 *bp = netdev_priv(dev);
- cancel_work_sync(&bp->reset_task);
-
bnx2_disable_int_sync(bp);
bnx2_napi_disable(bp);
del_timer_sync(&bp->timer);
@@ -7080,11 +7154,9 @@ bnx2_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering)
struct bnx2 *bp = netdev_priv(dev);
ering->rx_max_pending = MAX_TOTAL_RX_DESC_CNT;
- ering->rx_mini_max_pending = 0;
ering->rx_jumbo_max_pending = MAX_TOTAL_RX_PG_DESC_CNT;
ering->rx_pending = bp->rx_ring_size;
- ering->rx_mini_pending = 0;
ering->rx_jumbo_pending = bp->rx_pg_ring_size;
ering->tx_max_pending = MAX_TX_DESC_CNT;
@@ -7908,9 +7980,8 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
bp->chip_id = REG_RD(bp, BNX2_MISC_ID);
if (CHIP_NUM(bp) == CHIP_NUM_5709) {
- if (pci_find_capability(pdev, PCI_CAP_ID_EXP) == 0) {
- dev_err(&pdev->dev,
- "Cannot find PCIE capability, aborting\n");
+ if (!pci_is_pcie(pdev)) {
+ dev_err(&pdev->dev, "Not PCIE, aborting\n");
rc = -EIO;
goto err_out_unmap;
}
@@ -8051,7 +8122,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
bp->fw_version[j++] = ' ';
for (i = 0; i < 3 && j < 28; i++) {
reg = bnx2_reg_rd_ind(bp, addr + i * 4);
- reg = swab32(reg);
+ reg = be32_to_cpu(reg);
memcpy(&bp->fw_version[j], &reg, 4);
j += 4;
}
@@ -8177,6 +8248,12 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
bp->timer.data = (unsigned long) bp;
bp->timer.function = bnx2_timer;
+#ifdef BCM_CNIC
+ if (bnx2_shmem_rd(bp, BNX2_ISCSI_INITIATOR) & BNX2_ISCSI_INITIATOR_EN)
+ bp->cnic_eth_dev.max_iscsi_conn =
+ (bnx2_shmem_rd(bp, BNX2_ISCSI_MAX_CONN) &
+ BNX2_ISCSI_MAX_CONN_MASK) >> BNX2_ISCSI_MAX_CONN_SHIFT;
+#endif
pci_save_state(pdev);
return 0;
@@ -8301,10 +8378,6 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_drvdata(pdev, dev);
- rc = bnx2_request_firmware(bp);
- if (rc)
- goto error;
-
memcpy(dev->dev_addr, bp->mac_addr, 6);
memcpy(dev->perm_addr, bp->mac_addr, 6);
@@ -8318,6 +8391,7 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->vlan_features = dev->hw_features;
dev->hw_features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
dev->features |= dev->hw_features;
+ dev->priv_flags |= IFF_UNICAST_FLT;
if ((rc = register_netdev(dev))) {
dev_err(&pdev->dev, "Cannot register net device\n");
@@ -8335,11 +8409,6 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
error:
- if (bp->mips_firmware)
- release_firmware(bp->mips_firmware);
- if (bp->rv2p_firmware)
- release_firmware(bp->rv2p_firmware);
-
if (bp->regview)
iounmap(bp->regview);
pci_release_regions(pdev);
@@ -8358,11 +8427,7 @@ bnx2_remove_one(struct pci_dev *pdev)
unregister_netdev(dev);
del_timer_sync(&bp->timer);
-
- if (bp->mips_firmware)
- release_firmware(bp->mips_firmware);
- if (bp->rv2p_firmware)
- release_firmware(bp->rv2p_firmware);
+ cancel_work_sync(&bp->reset_task);
if (bp->regview)
iounmap(bp->regview);
@@ -8374,6 +8439,8 @@ bnx2_remove_one(struct pci_dev *pdev)
bp->flags &= ~BNX2_FLAG_AER_ENABLED;
}
+ bnx2_release_firmware(bp);
+
free_netdev(dev);
pci_release_regions(pdev);
diff --git a/drivers/net/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h
index bf371f6fe154..99d31a7d6aaa 100644
--- a/drivers/net/bnx2.h
+++ b/drivers/net/ethernet/broadcom/bnx2.h
@@ -5617,7 +5617,7 @@ struct l2_fhdr {
#define BNX2_TXP_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
#define BNX2_TXP_CPU_STATE_SOFT_HALTED (1L<<10)
#define BNX2_TXP_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
-#define BNX2_TXP_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_TXP_CPU_STATE_INTERRUPT (1L<<12)
#define BNX2_TXP_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
#define BNX2_TXP_CPU_STATE_INST_FETCH_STALL (1L<<15)
#define BNX2_TXP_CPU_STATE_BLOCKED_READ (1L<<31)
@@ -5712,7 +5712,7 @@ struct l2_fhdr {
#define BNX2_TPAT_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
#define BNX2_TPAT_CPU_STATE_SOFT_HALTED (1L<<10)
#define BNX2_TPAT_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
-#define BNX2_TPAT_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_TPAT_CPU_STATE_INTERRUPT (1L<<12)
#define BNX2_TPAT_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
#define BNX2_TPAT_CPU_STATE_INST_FETCH_STALL (1L<<15)
#define BNX2_TPAT_CPU_STATE_BLOCKED_READ (1L<<31)
@@ -5807,7 +5807,7 @@ struct l2_fhdr {
#define BNX2_RXP_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
#define BNX2_RXP_CPU_STATE_SOFT_HALTED (1L<<10)
#define BNX2_RXP_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
-#define BNX2_RXP_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_RXP_CPU_STATE_INTERRUPT (1L<<12)
#define BNX2_RXP_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
#define BNX2_RXP_CPU_STATE_INST_FETCH_STALL (1L<<15)
#define BNX2_RXP_CPU_STATE_BLOCKED_READ (1L<<31)
@@ -5953,7 +5953,7 @@ struct l2_fhdr {
#define BNX2_COM_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
#define BNX2_COM_CPU_STATE_SOFT_HALTED (1L<<10)
#define BNX2_COM_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
-#define BNX2_COM_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_COM_CPU_STATE_INTERRUPT (1L<<12)
#define BNX2_COM_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
#define BNX2_COM_CPU_STATE_INST_FETCH_STALL (1L<<15)
#define BNX2_COM_CPU_STATE_BLOCKED_READ (1L<<31)
@@ -6119,7 +6119,7 @@ struct l2_fhdr {
#define BNX2_CP_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
#define BNX2_CP_CPU_STATE_SOFT_HALTED (1L<<10)
#define BNX2_CP_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
-#define BNX2_CP_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_CP_CPU_STATE_INTERRUPT (1L<<12)
#define BNX2_CP_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
#define BNX2_CP_CPU_STATE_INST_FETCH_STALL (1L<<15)
#define BNX2_CP_CPU_STATE_BLOCKED_READ (1L<<31)
@@ -6291,7 +6291,7 @@ struct l2_fhdr {
#define BNX2_MCP_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
#define BNX2_MCP_CPU_STATE_SOFT_HALTED (1L<<10)
#define BNX2_MCP_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
-#define BNX2_MCP_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_MCP_CPU_STATE_INTERRUPT (1L<<12)
#define BNX2_MCP_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
#define BNX2_MCP_CPU_STATE_INST_FETCH_STALL (1L<<15)
#define BNX2_MCP_CPU_STATE_BLOCKED_READ (1L<<31)
@@ -7368,6 +7368,21 @@ struct bnx2_rv2p_fw_file {
#define BNX2_RPHY_SERDES_LINK 0x374
#define BNX2_RPHY_COPPER_LINK 0x378
+#define BNX2_ISCSI_INITIATOR 0x3dc
+#define BNX2_ISCSI_INITIATOR_EN 0x00080000
+
+#define BNX2_ISCSI_MAX_CONN 0x3e4
+#define BNX2_ISCSI_MAX_CONN_MASK 0xffff0000
+#define BNX2_ISCSI_MAX_CONN_SHIFT 16
+
#define HOST_VIEW_SHMEM_BASE 0x167c00
+#define DP_SHMEM_LINE(bp, offset) \
+ netdev_err(bp->dev, "DEBUG: %08x: %08x %08x %08x %08x\n", \
+ offset, \
+ bnx2_shmem_rd(bp, offset), \
+ bnx2_shmem_rd(bp, offset + 4), \
+ bnx2_shmem_rd(bp, offset + 8), \
+ bnx2_shmem_rd(bp, offset + 12))
+
#endif
diff --git a/drivers/net/bnx2_fw.h b/drivers/net/ethernet/broadcom/bnx2_fw.h
index 940eb91f209d..940eb91f209d 100644
--- a/drivers/net/bnx2_fw.h
+++ b/drivers/net/ethernet/broadcom/bnx2_fw.h
diff --git a/drivers/net/bnx2x/Makefile b/drivers/net/ethernet/broadcom/bnx2x/Makefile
index bb83a2961273..48fbdd48f88f 100644
--- a/drivers/net/bnx2x/Makefile
+++ b/drivers/net/ethernet/broadcom/bnx2x/Makefile
@@ -4,4 +4,4 @@
obj-$(CONFIG_BNX2X) += bnx2x.o
-bnx2x-objs := bnx2x_main.o bnx2x_link.o bnx2x_cmn.o bnx2x_ethtool.o bnx2x_stats.o bnx2x_dcb.o
+bnx2x-objs := bnx2x_main.o bnx2x_link.o bnx2x_cmn.o bnx2x_ethtool.o bnx2x_stats.o bnx2x_dcb.o bnx2x_sp.o
diff --git a/drivers/net/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index 668a578c49e9..aec7212ac983 100644
--- a/drivers/net/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -14,6 +14,7 @@
#ifndef BNX2X_H
#define BNX2X_H
#include <linux/netdevice.h>
+#include <linux/dma-mapping.h>
#include <linux/types.h>
/* compilation time flags */
@@ -22,14 +23,10 @@
* (you will need to reboot afterwards) */
/* #define BNX2X_STOP_ON_ERROR */
-#define DRV_MODULE_VERSION "1.62.12-0"
-#define DRV_MODULE_RELDATE "2011/03/20"
+#define DRV_MODULE_VERSION "1.70.30-0"
+#define DRV_MODULE_RELDATE "2011/10/25"
#define BNX2X_BC_VER 0x040200
-#define BNX2X_MULTI_QUEUE
-
-#define BNX2X_NEW_NAPI
-
#if defined(CONFIG_DCB)
#define BCM_DCBNL
#endif
@@ -47,11 +44,12 @@
#endif
#include <linux/mdio.h>
-#include <linux/pci.h>
+
#include "bnx2x_reg.h"
#include "bnx2x_fw_defs.h"
#include "bnx2x_hsi.h"
#include "bnx2x_link.h"
+#include "bnx2x_sp.h"
#include "bnx2x_dcb.h"
#include "bnx2x_stats.h"
@@ -68,64 +66,68 @@
#define BNX2X_MSG_SP 0x100000 /* was: NETIF_MSG_INTR */
#define BNX2X_MSG_FP 0x200000 /* was: NETIF_MSG_INTR */
-#define DP_LEVEL KERN_NOTICE /* was: KERN_DEBUG */
-
/* regular debug print */
-#define DP(__mask, __fmt, __args...) \
+#define DP(__mask, fmt, ...) \
do { \
if (bp->msg_enable & (__mask)) \
- printk(DP_LEVEL "[%s:%d(%s)]" __fmt, \
- __func__, __LINE__, \
- bp->dev ? (bp->dev->name) : "?", \
- ##__args); \
+ pr_notice("[%s:%d(%s)]" fmt, \
+ __func__, __LINE__, \
+ bp->dev ? (bp->dev->name) : "?", \
+ ##__VA_ARGS__); \
+} while (0)
+
+#define DP_CONT(__mask, fmt, ...) \
+do { \
+ if (bp->msg_enable & (__mask)) \
+ pr_cont(fmt, ##__VA_ARGS__); \
} while (0)
/* errors debug print */
-#define BNX2X_DBG_ERR(__fmt, __args...) \
+#define BNX2X_DBG_ERR(fmt, ...) \
do { \
if (netif_msg_probe(bp)) \
- pr_err("[%s:%d(%s)]" __fmt, \
+ pr_err("[%s:%d(%s)]" fmt, \
__func__, __LINE__, \
bp->dev ? (bp->dev->name) : "?", \
- ##__args); \
+ ##__VA_ARGS__); \
} while (0)
/* for errors (never masked) */
-#define BNX2X_ERR(__fmt, __args...) \
+#define BNX2X_ERR(fmt, ...) \
do { \
- pr_err("[%s:%d(%s)]" __fmt, \
+ pr_err("[%s:%d(%s)]" fmt, \
__func__, __LINE__, \
bp->dev ? (bp->dev->name) : "?", \
- ##__args); \
- } while (0)
+ ##__VA_ARGS__); \
+} while (0)
-#define BNX2X_ERROR(__fmt, __args...) do { \
- pr_err("[%s:%d]" __fmt, __func__, __LINE__, ##__args); \
- } while (0)
+#define BNX2X_ERROR(fmt, ...) \
+ pr_err("[%s:%d]" fmt, __func__, __LINE__, ##__VA_ARGS__)
/* before we have a dev->name use dev_info() */
-#define BNX2X_DEV_INFO(__fmt, __args...) \
+#define BNX2X_DEV_INFO(fmt, ...) \
do { \
if (netif_msg_probe(bp)) \
- dev_info(&bp->pdev->dev, __fmt, ##__args); \
+ dev_info(&bp->pdev->dev, fmt, ##__VA_ARGS__); \
} while (0)
-void bnx2x_panic_dump(struct bnx2x *bp);
-
#ifdef BNX2X_STOP_ON_ERROR
-#define bnx2x_panic() do { \
- bp->panic = 1; \
- BNX2X_ERR("driver assert\n"); \
- bnx2x_int_disable(bp); \
- bnx2x_panic_dump(bp); \
- } while (0)
+void bnx2x_int_disable(struct bnx2x *bp);
+#define bnx2x_panic() \
+do { \
+ bp->panic = 1; \
+ BNX2X_ERR("driver assert\n"); \
+ bnx2x_int_disable(bp); \
+ bnx2x_panic_dump(bp); \
+} while (0)
#else
-#define bnx2x_panic() do { \
- bp->panic = 1; \
- BNX2X_ERR("driver assert\n"); \
- bnx2x_panic_dump(bp); \
- } while (0)
+#define bnx2x_panic() \
+do { \
+ bp->panic = 1; \
+ BNX2X_ERR("driver assert\n"); \
+ bnx2x_panic_dump(bp); \
+} while (0)
#endif
#define bnx2x_mc_addr(ha) ((ha)->addr)
@@ -232,23 +234,29 @@ void bnx2x_panic_dump(struct bnx2x *bp);
* FUNC_N_CLID_X = N * NUM_SPECIAL_CLIENTS + FUNC_0_CLID_X
*
*/
-/* iSCSI L2 */
-#define BNX2X_ISCSI_ETH_CL_ID 17
-#define BNX2X_ISCSI_ETH_CID 17
+enum {
+ BNX2X_ISCSI_ETH_CL_ID_IDX,
+ BNX2X_FCOE_ETH_CL_ID_IDX,
+ BNX2X_MAX_CNIC_ETH_CL_ID_IDX,
+};
-/* FCoE L2 */
-#define BNX2X_FCOE_ETH_CL_ID 18
-#define BNX2X_FCOE_ETH_CID 18
+#define BNX2X_CNIC_START_ETH_CID 48
+enum {
+ /* iSCSI L2 */
+ BNX2X_ISCSI_ETH_CID = BNX2X_CNIC_START_ETH_CID,
+ /* FCoE L2 */
+ BNX2X_FCOE_ETH_CID,
+};
/** Additional rings budgeting */
#ifdef BCM_CNIC
-#define CNIC_CONTEXT_USE 1
-#define FCOE_CONTEXT_USE 1
+#define CNIC_PRESENT 1
+#define FCOE_PRESENT 1
#else
-#define CNIC_CONTEXT_USE 0
-#define FCOE_CONTEXT_USE 0
+#define CNIC_PRESENT 0
+#define FCOE_PRESENT 0
#endif /* BCM_CNIC */
-#define NONE_ETH_CONTEXT_USE (FCOE_CONTEXT_USE)
+#define NON_ETH_CONTEXT_USE (FCOE_PRESENT)
#define AEU_IN_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR \
AEU_INPUTS_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR
@@ -256,8 +264,35 @@ void bnx2x_panic_dump(struct bnx2x *bp);
#define SM_RX_ID 0
#define SM_TX_ID 1
-/* fast path */
+/* defines for multiple tx priority indices */
+#define FIRST_TX_ONLY_COS_INDEX 1
+#define FIRST_TX_COS_INDEX 0
+
+/* defines for decodeing the fastpath index and the cos index out of the
+ * transmission queue index
+ */
+#define MAX_TXQS_PER_COS FP_SB_MAX_E1x
+#define TXQ_TO_FP(txq_index) ((txq_index) % MAX_TXQS_PER_COS)
+#define TXQ_TO_COS(txq_index) ((txq_index) / MAX_TXQS_PER_COS)
+
+/* rules for calculating the cids of tx-only connections */
+#define CID_TO_FP(cid) ((cid) % MAX_TXQS_PER_COS)
+#define CID_COS_TO_TX_ONLY_CID(cid, cos) (cid + cos * MAX_TXQS_PER_COS)
+
+/* fp index inside class of service range */
+#define FP_COS_TO_TXQ(fp, cos) ((fp)->index + cos * MAX_TXQS_PER_COS)
+
+/*
+ * 0..15 eth cos0
+ * 16..31 eth cos1 if applicable
+ * 32..47 eth cos2 If applicable
+ * fcoe queue follows eth queues (16, 32, 48 depending on cos)
+ */
+#define MAX_ETH_TXQ_IDX(bp) (MAX_TXQS_PER_COS * (bp)->max_cos)
+#define FCOE_TXQ_IDX(bp) (MAX_ETH_TXQ_IDX(bp))
+
+/* fast path */
struct sw_rx_bd {
struct sk_buff *skb;
DEFINE_DMA_UNMAP_ADDR(mapping);
@@ -281,46 +316,103 @@ union db_prod {
u32 raw;
};
+/* dropless fc FW/HW related params */
+#define BRB_SIZE(bp) (CHIP_IS_E3(bp) ? 1024 : 512)
+#define MAX_AGG_QS(bp) (CHIP_IS_E1(bp) ? \
+ ETH_MAX_AGGREGATION_QUEUES_E1 :\
+ ETH_MAX_AGGREGATION_QUEUES_E1H_E2)
+#define FW_DROP_LEVEL(bp) (3 + MAX_SPQ_PENDING + MAX_AGG_QS(bp))
+#define FW_PREFETCH_CNT 16
+#define DROPLESS_FC_HEADROOM 100
/* MC hsi */
-#define BCM_PAGE_SHIFT 12
-#define BCM_PAGE_SIZE (1 << BCM_PAGE_SHIFT)
-#define BCM_PAGE_MASK (~(BCM_PAGE_SIZE - 1))
+#define BCM_PAGE_SHIFT 12
+#define BCM_PAGE_SIZE (1 << BCM_PAGE_SHIFT)
+#define BCM_PAGE_MASK (~(BCM_PAGE_SIZE - 1))
#define BCM_PAGE_ALIGN(addr) (((addr) + BCM_PAGE_SIZE - 1) & BCM_PAGE_MASK)
-#define PAGES_PER_SGE_SHIFT 0
-#define PAGES_PER_SGE (1 << PAGES_PER_SGE_SHIFT)
-#define SGE_PAGE_SIZE PAGE_SIZE
-#define SGE_PAGE_SHIFT PAGE_SHIFT
-#define SGE_PAGE_ALIGN(addr) PAGE_ALIGN((typeof(PAGE_SIZE))(addr))
+#define PAGES_PER_SGE_SHIFT 0
+#define PAGES_PER_SGE (1 << PAGES_PER_SGE_SHIFT)
+#define SGE_PAGE_SIZE PAGE_SIZE
+#define SGE_PAGE_SHIFT PAGE_SHIFT
+#define SGE_PAGE_ALIGN(addr) PAGE_ALIGN((typeof(PAGE_SIZE))(addr))
/* SGE ring related macros */
-#define NUM_RX_SGE_PAGES 2
+#define NUM_RX_SGE_PAGES 2
#define RX_SGE_CNT (BCM_PAGE_SIZE / sizeof(struct eth_rx_sge))
-#define MAX_RX_SGE_CNT (RX_SGE_CNT - 2)
+#define NEXT_PAGE_SGE_DESC_CNT 2
+#define MAX_RX_SGE_CNT (RX_SGE_CNT - NEXT_PAGE_SGE_DESC_CNT)
/* RX_SGE_CNT is promised to be a power of 2 */
-#define RX_SGE_MASK (RX_SGE_CNT - 1)
-#define NUM_RX_SGE (RX_SGE_CNT * NUM_RX_SGE_PAGES)
-#define MAX_RX_SGE (NUM_RX_SGE - 1)
+#define RX_SGE_MASK (RX_SGE_CNT - 1)
+#define NUM_RX_SGE (RX_SGE_CNT * NUM_RX_SGE_PAGES)
+#define MAX_RX_SGE (NUM_RX_SGE - 1)
#define NEXT_SGE_IDX(x) ((((x) & RX_SGE_MASK) == \
- (MAX_RX_SGE_CNT - 1)) ? (x) + 3 : (x) + 1)
-#define RX_SGE(x) ((x) & MAX_RX_SGE)
+ (MAX_RX_SGE_CNT - 1)) ? \
+ (x) + 1 + NEXT_PAGE_SGE_DESC_CNT : \
+ (x) + 1)
+#define RX_SGE(x) ((x) & MAX_RX_SGE)
+
+/*
+ * Number of required SGEs is the sum of two:
+ * 1. Number of possible opened aggregations (next packet for
+ * these aggregations will probably consume SGE immidiatelly)
+ * 2. Rest of BRB blocks divided by 2 (block will consume new SGE only
+ * after placement on BD for new TPA aggregation)
+ *
+ * Takes into account NEXT_PAGE_SGE_DESC_CNT "next" elements on each page
+ */
+#define NUM_SGE_REQ (MAX_AGG_QS(bp) + \
+ (BRB_SIZE(bp) - MAX_AGG_QS(bp)) / 2)
+#define NUM_SGE_PG_REQ ((NUM_SGE_REQ + MAX_RX_SGE_CNT - 1) / \
+ MAX_RX_SGE_CNT)
+#define SGE_TH_LO(bp) (NUM_SGE_REQ + \
+ NUM_SGE_PG_REQ * NEXT_PAGE_SGE_DESC_CNT)
+#define SGE_TH_HI(bp) (SGE_TH_LO(bp) + DROPLESS_FC_HEADROOM)
+
+/* Manipulate a bit vector defined as an array of u64 */
-/* SGE producer mask related macros */
/* Number of bits in one sge_mask array element */
-#define RX_SGE_MASK_ELEM_SZ 64
-#define RX_SGE_MASK_ELEM_SHIFT 6
-#define RX_SGE_MASK_ELEM_MASK ((u64)RX_SGE_MASK_ELEM_SZ - 1)
+#define BIT_VEC64_ELEM_SZ 64
+#define BIT_VEC64_ELEM_SHIFT 6
+#define BIT_VEC64_ELEM_MASK ((u64)BIT_VEC64_ELEM_SZ - 1)
+
+
+#define __BIT_VEC64_SET_BIT(el, bit) \
+ do { \
+ el = ((el) | ((u64)0x1 << (bit))); \
+ } while (0)
+
+#define __BIT_VEC64_CLEAR_BIT(el, bit) \
+ do { \
+ el = ((el) & (~((u64)0x1 << (bit)))); \
+ } while (0)
+
+
+#define BIT_VEC64_SET_BIT(vec64, idx) \
+ __BIT_VEC64_SET_BIT((vec64)[(idx) >> BIT_VEC64_ELEM_SHIFT], \
+ (idx) & BIT_VEC64_ELEM_MASK)
+
+#define BIT_VEC64_CLEAR_BIT(vec64, idx) \
+ __BIT_VEC64_CLEAR_BIT((vec64)[(idx) >> BIT_VEC64_ELEM_SHIFT], \
+ (idx) & BIT_VEC64_ELEM_MASK)
+
+#define BIT_VEC64_TEST_BIT(vec64, idx) \
+ (((vec64)[(idx) >> BIT_VEC64_ELEM_SHIFT] >> \
+ ((idx) & BIT_VEC64_ELEM_MASK)) & 0x1)
/* Creates a bitmask of all ones in less significant bits.
idx - index of the most significant bit in the created mask */
-#define RX_SGE_ONES_MASK(idx) \
- (((u64)0x1 << (((idx) & RX_SGE_MASK_ELEM_MASK) + 1)) - 1)
-#define RX_SGE_MASK_ELEM_ONE_MASK ((u64)(~0))
+#define BIT_VEC64_ONES_MASK(idx) \
+ (((u64)0x1 << (((idx) & BIT_VEC64_ELEM_MASK) + 1)) - 1)
+#define BIT_VEC64_ELEM_ONE_MASK ((u64)(~0))
+
+/*******************************************************/
+
+
/* Number of u64 elements in SGE mask array */
#define RX_SGE_MASK_LEN ((NUM_RX_SGE_PAGES * RX_SGE_CNT) / \
- RX_SGE_MASK_ELEM_SZ)
+ BIT_VEC64_ELEM_SZ)
#define RX_SGE_MASK_LEN_MASK (RX_SGE_MASK_LEN - 1)
#define NEXT_SGE_MASK_ELEM(el) (((el) + 1) & RX_SGE_MASK_LEN_MASK)
@@ -331,7 +423,53 @@ union host_hc_status_block {
struct host_hc_status_block_e2 *e2_sb;
};
+struct bnx2x_agg_info {
+ /*
+ * First aggregation buffer is an skb, the following - are pages.
+ * We will preallocate the skbs for each aggregation when
+ * we open the interface and will replace the BD at the consumer
+ * with this one when we receive the TPA_START CQE in order to
+ * keep the Rx BD ring consistent.
+ */
+ struct sw_rx_bd first_buf;
+ u8 tpa_state;
+#define BNX2X_TPA_START 1
+#define BNX2X_TPA_STOP 2
+#define BNX2X_TPA_ERROR 3
+ u8 placement_offset;
+ u16 parsing_flags;
+ u16 vlan_tag;
+ u16 len_on_bd;
+};
+
+#define Q_STATS_OFFSET32(stat_name) \
+ (offsetof(struct bnx2x_eth_q_stats, stat_name) / 4)
+
+struct bnx2x_fp_txdata {
+
+ struct sw_tx_bd *tx_buf_ring;
+
+ union eth_tx_bd_types *tx_desc_ring;
+ dma_addr_t tx_desc_mapping;
+
+ u32 cid;
+
+ union db_prod tx_db;
+
+ u16 tx_pkt_prod;
+ u16 tx_pkt_cons;
+ u16 tx_bd_prod;
+ u16 tx_bd_cons;
+
+ unsigned long tx_pkt;
+
+ __le16 *tx_cons_sb;
+
+ int txq_index;
+};
+
struct bnx2x_fastpath {
+ struct bnx2x *bp; /* parent */
#define BNX2X_NAPI_WEIGHT 128
struct napi_struct napi;
@@ -346,10 +484,8 @@ struct bnx2x_fastpath {
dma_addr_t status_blk_mapping;
- struct sw_tx_bd *tx_buf_ring;
-
- union eth_tx_bd_types *tx_desc_ring;
- dma_addr_t tx_desc_mapping;
+ u8 max_cos; /* actual number of active tx coses */
+ struct bnx2x_fp_txdata txdata[BNX2X_MULTI_TX_COS];
struct sw_rx_bd *rx_buf_ring; /* BDs mappings ring */
struct sw_rx_page *rx_page_ring; /* SGE pages mappings ring */
@@ -366,32 +502,15 @@ struct bnx2x_fastpath {
u64 sge_mask[RX_SGE_MASK_LEN];
- int state;
-#define BNX2X_FP_STATE_CLOSED 0
-#define BNX2X_FP_STATE_IRQ 0x80000
-#define BNX2X_FP_STATE_OPENING 0x90000
-#define BNX2X_FP_STATE_OPEN 0xa0000
-#define BNX2X_FP_STATE_HALTING 0xb0000
-#define BNX2X_FP_STATE_HALTED 0xc0000
-#define BNX2X_FP_STATE_TERMINATING 0xd0000
-#define BNX2X_FP_STATE_TERMINATED 0xe0000
+ u32 cid;
+
+ __le16 fp_hc_idx;
u8 index; /* number in fp array */
u8 cl_id; /* eth client id */
u8 cl_qzone_id;
u8 fw_sb_id; /* status block number in FW */
u8 igu_sb_id; /* status block number in HW */
- u32 cid;
-
- union db_prod tx_db;
-
- u16 tx_pkt_prod;
- u16 tx_pkt_cons;
- u16 tx_bd_prod;
- u16 tx_bd_cons;
- __le16 *tx_cons_sb;
-
- __le16 fp_hc_idx;
u16 rx_bd_prod;
u16 rx_bd_cons;
@@ -401,24 +520,19 @@ struct bnx2x_fastpath {
/* The last maximal completed SGE */
u16 last_max_sge;
__le16 *rx_cons_sb;
-
- unsigned long tx_pkt,
- rx_pkt,
+ unsigned long rx_pkt,
rx_calls;
/* TPA related */
- struct sw_rx_bd tpa_pool[ETH_MAX_AGGREGATION_QUEUES_E1H];
- u8 tpa_state[ETH_MAX_AGGREGATION_QUEUES_E1H];
-#define BNX2X_TPA_START 1
-#define BNX2X_TPA_STOP 2
+ struct bnx2x_agg_info tpa_info[ETH_MAX_AGGREGATION_QUEUES_E1H_E2];
u8 disable_tpa;
#ifdef BNX2X_STOP_ON_ERROR
u64 tpa_queue_used;
#endif
- struct tstorm_per_client_stats old_tclient;
- struct ustorm_per_client_stats old_uclient;
- struct xstorm_per_client_stats old_xclient;
+ struct tstorm_per_queue_stats old_tclient;
+ struct ustorm_per_queue_stats old_uclient;
+ struct xstorm_per_queue_stats old_xclient;
struct bnx2x_eth_q_stats eth_q_stats;
/* The size is calculated using the following:
@@ -427,7 +541,13 @@ struct bnx2x_fastpath {
4 (for the digits and to make it DWORD aligned) */
#define FP_NAME_SIZE (sizeof(((struct net_device *)0)->name) + 8)
char name[FP_NAME_SIZE];
- struct bnx2x *bp; /* parent */
+
+ /* MACs object */
+ struct bnx2x_vlan_mac_obj mac_obj;
+
+ /* Queue State object */
+ struct bnx2x_queue_sp_obj q_obj;
+
};
#define bnx2x_fp(bp, nr, var) (bp->fp[nr].var)
@@ -435,11 +555,17 @@ struct bnx2x_fastpath {
/* Use 2500 as a mini-jumbo MTU for FCoE */
#define BNX2X_FCOE_MINI_JUMBO_MTU 2500
-#ifdef BCM_CNIC
-/* FCoE L2 `fastpath' is right after the eth entries */
+/* FCoE L2 `fastpath' entry is right after the eth entries */
#define FCOE_IDX BNX2X_NUM_ETH_QUEUES(bp)
#define bnx2x_fcoe_fp(bp) (&bp->fp[FCOE_IDX])
#define bnx2x_fcoe(bp, var) (bnx2x_fcoe_fp(bp)->var)
+#define bnx2x_fcoe_tx(bp, var) (bnx2x_fcoe_fp(bp)-> \
+ txdata[FIRST_TX_COS_INDEX].var)
+
+
+#define IS_ETH_FP(fp) (fp->index < \
+ BNX2X_NUM_ETH_QUEUES(fp->bp))
+#ifdef BCM_CNIC
#define IS_FCOE_FP(fp) (fp->index == FCOE_IDX)
#define IS_FCOE_IDX(idx) ((idx) == FCOE_IDX)
#else
@@ -449,77 +575,106 @@ struct bnx2x_fastpath {
/* MC hsi */
-#define MAX_FETCH_BD 13 /* HW max BDs per packet */
-#define RX_COPY_THRESH 92
+#define MAX_FETCH_BD 13 /* HW max BDs per packet */
+#define RX_COPY_THRESH 92
-#define NUM_TX_RINGS 16
+#define NUM_TX_RINGS 16
#define TX_DESC_CNT (BCM_PAGE_SIZE / sizeof(union eth_tx_bd_types))
-#define MAX_TX_DESC_CNT (TX_DESC_CNT - 1)
-#define NUM_TX_BD (TX_DESC_CNT * NUM_TX_RINGS)
-#define MAX_TX_BD (NUM_TX_BD - 1)
-#define MAX_TX_AVAIL (MAX_TX_DESC_CNT * NUM_TX_RINGS - 2)
-#define INIT_JUMBO_TX_RING_SIZE MAX_TX_AVAIL
-#define INIT_TX_RING_SIZE MAX_TX_AVAIL
+#define NEXT_PAGE_TX_DESC_CNT 1
+#define MAX_TX_DESC_CNT (TX_DESC_CNT - NEXT_PAGE_TX_DESC_CNT)
+#define NUM_TX_BD (TX_DESC_CNT * NUM_TX_RINGS)
+#define MAX_TX_BD (NUM_TX_BD - 1)
+#define MAX_TX_AVAIL (MAX_TX_DESC_CNT * NUM_TX_RINGS - 2)
#define NEXT_TX_IDX(x) ((((x) & MAX_TX_DESC_CNT) == \
- (MAX_TX_DESC_CNT - 1)) ? (x) + 2 : (x) + 1)
-#define TX_BD(x) ((x) & MAX_TX_BD)
-#define TX_BD_POFF(x) ((x) & MAX_TX_DESC_CNT)
+ (MAX_TX_DESC_CNT - 1)) ? \
+ (x) + 1 + NEXT_PAGE_TX_DESC_CNT : \
+ (x) + 1)
+#define TX_BD(x) ((x) & MAX_TX_BD)
+#define TX_BD_POFF(x) ((x) & MAX_TX_DESC_CNT)
/* The RX BD ring is special, each bd is 8 bytes but the last one is 16 */
-#define NUM_RX_RINGS 8
+#define NUM_RX_RINGS 8
#define RX_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct eth_rx_bd))
-#define MAX_RX_DESC_CNT (RX_DESC_CNT - 2)
-#define RX_DESC_MASK (RX_DESC_CNT - 1)
-#define NUM_RX_BD (RX_DESC_CNT * NUM_RX_RINGS)
-#define MAX_RX_BD (NUM_RX_BD - 1)
-#define MAX_RX_AVAIL (MAX_RX_DESC_CNT * NUM_RX_RINGS - 2)
-#define MIN_RX_SIZE_TPA 72
-#define MIN_RX_SIZE_NONTPA 10
-#define INIT_JUMBO_RX_RING_SIZE MAX_RX_AVAIL
-#define INIT_RX_RING_SIZE MAX_RX_AVAIL
+#define NEXT_PAGE_RX_DESC_CNT 2
+#define MAX_RX_DESC_CNT (RX_DESC_CNT - NEXT_PAGE_RX_DESC_CNT)
+#define RX_DESC_MASK (RX_DESC_CNT - 1)
+#define NUM_RX_BD (RX_DESC_CNT * NUM_RX_RINGS)
+#define MAX_RX_BD (NUM_RX_BD - 1)
+#define MAX_RX_AVAIL (MAX_RX_DESC_CNT * NUM_RX_RINGS - 2)
+
+/* dropless fc calculations for BDs
+ *
+ * Number of BDs should as number of buffers in BRB:
+ * Low threshold takes into account NEXT_PAGE_RX_DESC_CNT
+ * "next" elements on each page
+ */
+#define NUM_BD_REQ BRB_SIZE(bp)
+#define NUM_BD_PG_REQ ((NUM_BD_REQ + MAX_RX_DESC_CNT - 1) / \
+ MAX_RX_DESC_CNT)
+#define BD_TH_LO(bp) (NUM_BD_REQ + \
+ NUM_BD_PG_REQ * NEXT_PAGE_RX_DESC_CNT + \
+ FW_DROP_LEVEL(bp))
+#define BD_TH_HI(bp) (BD_TH_LO(bp) + DROPLESS_FC_HEADROOM)
+
+#define MIN_RX_AVAIL ((bp)->dropless_fc ? BD_TH_HI(bp) + 128 : 128)
+
+#define MIN_RX_SIZE_TPA_HW (CHIP_IS_E1(bp) ? \
+ ETH_MIN_RX_CQES_WITH_TPA_E1 : \
+ ETH_MIN_RX_CQES_WITH_TPA_E1H_E2)
+#define MIN_RX_SIZE_NONTPA_HW ETH_MIN_RX_CQES_WITHOUT_TPA
+#define MIN_RX_SIZE_TPA (max_t(u32, MIN_RX_SIZE_TPA_HW, MIN_RX_AVAIL))
+#define MIN_RX_SIZE_NONTPA (max_t(u32, MIN_RX_SIZE_NONTPA_HW,\
+ MIN_RX_AVAIL))
+
#define NEXT_RX_IDX(x) ((((x) & RX_DESC_MASK) == \
- (MAX_RX_DESC_CNT - 1)) ? (x) + 3 : (x) + 1)
-#define RX_BD(x) ((x) & MAX_RX_BD)
+ (MAX_RX_DESC_CNT - 1)) ? \
+ (x) + 1 + NEXT_PAGE_RX_DESC_CNT : \
+ (x) + 1)
+#define RX_BD(x) ((x) & MAX_RX_BD)
-/* As long as CQE is 4 times bigger than BD entry we have to allocate
- 4 times more pages for CQ ring in order to keep it balanced with
- BD ring */
-#define NUM_RCQ_RINGS (NUM_RX_RINGS * 4)
+/*
+ * As long as CQE is X times bigger than BD entry we have to allocate X times
+ * more pages for CQ ring in order to keep it balanced with BD ring
+ */
+#define CQE_BD_REL (sizeof(union eth_rx_cqe) / sizeof(struct eth_rx_bd))
+#define NUM_RCQ_RINGS (NUM_RX_RINGS * CQE_BD_REL)
#define RCQ_DESC_CNT (BCM_PAGE_SIZE / sizeof(union eth_rx_cqe))
-#define MAX_RCQ_DESC_CNT (RCQ_DESC_CNT - 1)
-#define NUM_RCQ_BD (RCQ_DESC_CNT * NUM_RCQ_RINGS)
-#define MAX_RCQ_BD (NUM_RCQ_BD - 1)
-#define MAX_RCQ_AVAIL (MAX_RCQ_DESC_CNT * NUM_RCQ_RINGS - 2)
+#define NEXT_PAGE_RCQ_DESC_CNT 1
+#define MAX_RCQ_DESC_CNT (RCQ_DESC_CNT - NEXT_PAGE_RCQ_DESC_CNT)
+#define NUM_RCQ_BD (RCQ_DESC_CNT * NUM_RCQ_RINGS)
+#define MAX_RCQ_BD (NUM_RCQ_BD - 1)
+#define MAX_RCQ_AVAIL (MAX_RCQ_DESC_CNT * NUM_RCQ_RINGS - 2)
#define NEXT_RCQ_IDX(x) ((((x) & MAX_RCQ_DESC_CNT) == \
- (MAX_RCQ_DESC_CNT - 1)) ? (x) + 2 : (x) + 1)
-#define RCQ_BD(x) ((x) & MAX_RCQ_BD)
+ (MAX_RCQ_DESC_CNT - 1)) ? \
+ (x) + 1 + NEXT_PAGE_RCQ_DESC_CNT : \
+ (x) + 1)
+#define RCQ_BD(x) ((x) & MAX_RCQ_BD)
+/* dropless fc calculations for RCQs
+ *
+ * Number of RCQs should be as number of buffers in BRB:
+ * Low threshold takes into account NEXT_PAGE_RCQ_DESC_CNT
+ * "next" elements on each page
+ */
+#define NUM_RCQ_REQ BRB_SIZE(bp)
+#define NUM_RCQ_PG_REQ ((NUM_BD_REQ + MAX_RCQ_DESC_CNT - 1) / \
+ MAX_RCQ_DESC_CNT)
+#define RCQ_TH_LO(bp) (NUM_RCQ_REQ + \
+ NUM_RCQ_PG_REQ * NEXT_PAGE_RCQ_DESC_CNT + \
+ FW_DROP_LEVEL(bp))
+#define RCQ_TH_HI(bp) (RCQ_TH_LO(bp) + DROPLESS_FC_HEADROOM)
-/* This is needed for determining of last_max */
-#define SUB_S16(a, b) (s16)((s16)(a) - (s16)(b))
-#define __SGE_MASK_SET_BIT(el, bit) \
- do { \
- el = ((el) | ((u64)0x1 << (bit))); \
- } while (0)
-
-#define __SGE_MASK_CLEAR_BIT(el, bit) \
- do { \
- el = ((el) & (~((u64)0x1 << (bit)))); \
- } while (0)
-
-#define SGE_MASK_SET_BIT(fp, idx) \
- __SGE_MASK_SET_BIT(fp->sge_mask[(idx) >> RX_SGE_MASK_ELEM_SHIFT], \
- ((idx) & RX_SGE_MASK_ELEM_MASK))
+/* This is needed for determining of last_max */
+#define SUB_S16(a, b) (s16)((s16)(a) - (s16)(b))
+#define SUB_S32(a, b) (s32)((s32)(a) - (s32)(b))
-#define SGE_MASK_CLEAR_BIT(fp, idx) \
- __SGE_MASK_CLEAR_BIT(fp->sge_mask[(idx) >> RX_SGE_MASK_ELEM_SHIFT], \
- ((idx) & RX_SGE_MASK_ELEM_MASK))
+#define BNX2X_SWCID_SHIFT 17
+#define BNX2X_SWCID_MASK ((0x1 << BNX2X_SWCID_SHIFT) - 1)
/* used on a CID received from the HW */
-#define SW_CID(x) (le32_to_cpu(x) & \
- (COMMON_RAMROD_ETH_RX_CQE_CID >> 7))
+#define SW_CID(x) (le32_to_cpu(x) & BNX2X_SWCID_MASK)
#define CQE_CMD(x) (le32_to_cpu(x) >> \
COMMON_RAMROD_ETH_RX_CQE_CMD_ID_SHIFT)
@@ -529,6 +684,9 @@ struct bnx2x_fastpath {
#define BNX2X_DB_MIN_SHIFT 3 /* 8 bytes */
#define BNX2X_DB_SHIFT 7 /* 128 bytes*/
+#if (BNX2X_DB_SHIFT < BNX2X_DB_MIN_SHIFT)
+#error "Min DB doorbell stride is 8"
+#endif
#define DPM_TRIGER_TYPE 0x40
#define DOORBELL(bp, cid, val) \
do { \
@@ -557,13 +715,11 @@ struct bnx2x_fastpath {
/* stuff added to make the code fit 80Col */
-
-#define CQE_TYPE(cqe_fp_flags) ((cqe_fp_flags) & ETH_FAST_PATH_RX_CQE_TYPE)
-
-#define TPA_TYPE_START ETH_FAST_PATH_RX_CQE_START_FLG
-#define TPA_TYPE_END ETH_FAST_PATH_RX_CQE_END_FLG
-#define TPA_TYPE(cqe_fp_flags) ((cqe_fp_flags) & \
- (TPA_TYPE_START | TPA_TYPE_END))
+#define CQE_TYPE(cqe_fp_flags) ((cqe_fp_flags) & ETH_FAST_PATH_RX_CQE_TYPE)
+#define CQE_TYPE_START(cqe_type) ((cqe_type) == RX_ETH_CQE_TYPE_ETH_START_AGG)
+#define CQE_TYPE_STOP(cqe_type) ((cqe_type) == RX_ETH_CQE_TYPE_ETH_STOP_AGG)
+#define CQE_TYPE_SLOW(cqe_type) ((cqe_type) == RX_ETH_CQE_TYPE_ETH_RAMROD)
+#define CQE_TYPE_FAST(cqe_type) ((cqe_type) == RX_ETH_CQE_TYPE_ETH_FASTPATH)
#define ETH_RX_ERROR_FALGS ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG
@@ -590,15 +746,31 @@ struct bnx2x_fastpath {
#define BNX2X_RX_SUM_FIX(cqe) \
BNX2X_PRS_FLAG_OVERETH_IPV4(cqe->fast_path_cqe.pars_flags.flags)
-#define U_SB_ETH_RX_CQ_INDEX 1
-#define U_SB_ETH_RX_BD_INDEX 2
-#define C_SB_ETH_TX_CQ_INDEX 5
+
+#define FP_USB_FUNC_OFF \
+ offsetof(struct cstorm_status_block_u, func)
+#define FP_CSB_FUNC_OFF \
+ offsetof(struct cstorm_status_block_c, func)
+
+#define HC_INDEX_ETH_RX_CQ_CONS 1
+
+#define HC_INDEX_OOO_TX_CQ_CONS 4
+
+#define HC_INDEX_ETH_TX_CQ_CONS_COS0 5
+
+#define HC_INDEX_ETH_TX_CQ_CONS_COS1 6
+
+#define HC_INDEX_ETH_TX_CQ_CONS_COS2 7
+
+#define HC_INDEX_ETH_FIRST_TX_CQ_CONS HC_INDEX_ETH_TX_CQ_CONS_COS0
#define BNX2X_RX_SB_INDEX \
- (&fp->sb_index_values[U_SB_ETH_RX_CQ_INDEX])
+ (&fp->sb_index_values[HC_INDEX_ETH_RX_CQ_CONS])
-#define BNX2X_TX_SB_INDEX \
- (&fp->sb_index_values[C_SB_ETH_TX_CQ_INDEX])
+#define BNX2X_TX_SB_INDEX_BASE BNX2X_TX_SB_INDEX_COS0
+
+#define BNX2X_TX_SB_INDEX_COS0 \
+ (&fp->sb_index_values[HC_INDEX_ETH_TX_CQ_CONS_COS0])
/* end of fast path */
@@ -615,41 +787,74 @@ struct bnx2x_common {
#define CHIP_NUM_57711 0x164f
#define CHIP_NUM_57711E 0x1650
#define CHIP_NUM_57712 0x1662
-#define CHIP_NUM_57712E 0x1663
+#define CHIP_NUM_57712_MF 0x1663
+#define CHIP_NUM_57713 0x1651
+#define CHIP_NUM_57713E 0x1652
+#define CHIP_NUM_57800 0x168a
+#define CHIP_NUM_57800_MF 0x16a5
+#define CHIP_NUM_57810 0x168e
+#define CHIP_NUM_57810_MF 0x16ae
+#define CHIP_NUM_57840 0x168d
+#define CHIP_NUM_57840_MF 0x16ab
#define CHIP_IS_E1(bp) (CHIP_NUM(bp) == CHIP_NUM_57710)
#define CHIP_IS_57711(bp) (CHIP_NUM(bp) == CHIP_NUM_57711)
#define CHIP_IS_57711E(bp) (CHIP_NUM(bp) == CHIP_NUM_57711E)
#define CHIP_IS_57712(bp) (CHIP_NUM(bp) == CHIP_NUM_57712)
-#define CHIP_IS_57712E(bp) (CHIP_NUM(bp) == CHIP_NUM_57712E)
+#define CHIP_IS_57712_MF(bp) (CHIP_NUM(bp) == CHIP_NUM_57712_MF)
+#define CHIP_IS_57800(bp) (CHIP_NUM(bp) == CHIP_NUM_57800)
+#define CHIP_IS_57800_MF(bp) (CHIP_NUM(bp) == CHIP_NUM_57800_MF)
+#define CHIP_IS_57810(bp) (CHIP_NUM(bp) == CHIP_NUM_57810)
+#define CHIP_IS_57810_MF(bp) (CHIP_NUM(bp) == CHIP_NUM_57810_MF)
+#define CHIP_IS_57840(bp) (CHIP_NUM(bp) == CHIP_NUM_57840)
+#define CHIP_IS_57840_MF(bp) (CHIP_NUM(bp) == CHIP_NUM_57840_MF)
#define CHIP_IS_E1H(bp) (CHIP_IS_57711(bp) || \
CHIP_IS_57711E(bp))
#define CHIP_IS_E2(bp) (CHIP_IS_57712(bp) || \
- CHIP_IS_57712E(bp))
+ CHIP_IS_57712_MF(bp))
+#define CHIP_IS_E3(bp) (CHIP_IS_57800(bp) || \
+ CHIP_IS_57800_MF(bp) || \
+ CHIP_IS_57810(bp) || \
+ CHIP_IS_57810_MF(bp) || \
+ CHIP_IS_57840(bp) || \
+ CHIP_IS_57840_MF(bp))
#define CHIP_IS_E1x(bp) (CHIP_IS_E1((bp)) || CHIP_IS_E1H((bp)))
-#define IS_E1H_OFFSET (CHIP_IS_E1H(bp) || CHIP_IS_E2(bp))
-
-#define CHIP_REV(bp) (bp->common.chip_id & 0x0000f000)
-#define CHIP_REV_Ax 0x00000000
+#define USES_WARPCORE(bp) (CHIP_IS_E3(bp))
+#define IS_E1H_OFFSET (!CHIP_IS_E1(bp))
+
+#define CHIP_REV_SHIFT 12
+#define CHIP_REV_MASK (0xF << CHIP_REV_SHIFT)
+#define CHIP_REV_VAL(bp) (bp->common.chip_id & CHIP_REV_MASK)
+#define CHIP_REV_Ax (0x0 << CHIP_REV_SHIFT)
+#define CHIP_REV_Bx (0x1 << CHIP_REV_SHIFT)
/* assume maximum 5 revisions */
-#define CHIP_REV_IS_SLOW(bp) (CHIP_REV(bp) > 0x00005000)
+#define CHIP_REV_IS_SLOW(bp) (CHIP_REV_VAL(bp) > 0x00005000)
/* Emul versions are A=>0xe, B=>0xc, C=>0xa, D=>8, E=>6 */
#define CHIP_REV_IS_EMUL(bp) ((CHIP_REV_IS_SLOW(bp)) && \
- !(CHIP_REV(bp) & 0x00001000))
+ !(CHIP_REV_VAL(bp) & 0x00001000))
/* FPGA versions are A=>0xf, B=>0xd, C=>0xb, D=>9, E=>7 */
#define CHIP_REV_IS_FPGA(bp) ((CHIP_REV_IS_SLOW(bp)) && \
- (CHIP_REV(bp) & 0x00001000))
+ (CHIP_REV_VAL(bp) & 0x00001000))
#define CHIP_TIME(bp) ((CHIP_REV_IS_EMUL(bp)) ? 2000 : \
((CHIP_REV_IS_FPGA(bp)) ? 200 : 1))
#define CHIP_METAL(bp) (bp->common.chip_id & 0x00000ff0)
#define CHIP_BOND_ID(bp) (bp->common.chip_id & 0x0000000f)
-#define CHIP_PARITY_ENABLED(bp) (CHIP_IS_E1(bp) || CHIP_IS_E1H(bp))
+#define CHIP_REV_SIM(bp) (((CHIP_REV_MASK - CHIP_REV_VAL(bp)) >>\
+ (CHIP_REV_SHIFT + 1)) \
+ << CHIP_REV_SHIFT)
+#define CHIP_REV(bp) (CHIP_REV_IS_SLOW(bp) ? \
+ CHIP_REV_SIM(bp) :\
+ CHIP_REV_VAL(bp))
+#define CHIP_IS_E3B0(bp) (CHIP_IS_E3(bp) && \
+ (CHIP_REV(bp) == CHIP_REV_Bx))
+#define CHIP_IS_E3A0(bp) (CHIP_IS_E3(bp) && \
+ (CHIP_REV(bp) == CHIP_REV_Ax))
int flash_size;
-#define NVRAM_1MB_SIZE 0x20000 /* 1M bit in bytes */
-#define NVRAM_TIMEOUT_COUNT 30000
-#define NVRAM_PAGE_SIZE 256
+#define BNX2X_NVRAM_1MB_SIZE 0x20000 /* 1M bit in bytes */
+#define BNX2X_NVRAM_TIMEOUT_COUNT 30000
+#define BNX2X_NVRAM_PAGE_SIZE 256
u32 shmem_base;
u32 shmem2_base;
@@ -666,7 +871,7 @@ struct bnx2x_common {
#define INT_BLOCK_MODE_NORMAL 0
#define INT_BLOCK_MODE_BW_COMP 2
#define CHIP_INT_MODE_IS_NBC(bp) \
- (CHIP_IS_E2(bp) && \
+ (!CHIP_IS_E1x(bp) && \
!((bp)->common.int_block & INT_BLOCK_MODE_BW_COMP))
#define CHIP_INT_MODE_IS_BC(bp) (!CHIP_INT_MODE_IS_NBC(bp))
@@ -712,19 +917,15 @@ struct bnx2x_port {
/* end of port */
-/* e1h Classification CAM line allocations */
-enum {
- CAM_ETH_LINE = 0,
- CAM_ISCSI_ETH_LINE,
- CAM_FIP_ETH_LINE,
- CAM_FIP_MCAST_LINE,
- CAM_MAX_PF_LINE = CAM_FIP_MCAST_LINE
-};
-/* number of MACs per function in NIG memory - used for SI mode */
-#define NIG_LLH_FUNC_MEM_SIZE 16
-/* number of entries in NIG_REG_LLHX_FUNC_MEM */
-#define NIG_LLH_FUNC_MEM_MAX_OFFSET 8
+#define STATS_OFFSET32(stat_name) \
+ (offsetof(struct bnx2x_eth_stats, stat_name) / 4)
+/* slow path */
+
+/* slow path work-queue */
+extern struct workqueue_struct *bnx2x_wq;
+
+#define BNX2X_MAX_NUM_OF_VFS 64
#define BNX2X_VF_ID_INVALID 0xFF
/*
@@ -749,27 +950,10 @@ enum {
* L2 queue is supported. the cid for the FCoE L2 queue is always X.
*/
-#define FP_SB_MAX_E1x 16 /* fast-path interrupt contexts E1x */
-#define FP_SB_MAX_E2 16 /* fast-path interrupt contexts E2 */
-
-/*
- * cid_cnt paramter below refers to the value returned by
- * 'bnx2x_get_l2_cid_count()' routine
- */
-
-/*
- * The number of FP context allocated by the driver == max number of regular
- * L2 queues + 1 for the FCoE L2 queue
- */
-#define L2_FP_COUNT(cid_cnt) ((cid_cnt) - CNIC_CONTEXT_USE)
-
-/*
- * The number of FP-SB allocated by the driver == max number of regular L2
- * queues + 1 for the CNIC which also consumes an FP-SB
- */
-#define FP_SB_COUNT(cid_cnt) ((cid_cnt) - FCOE_CONTEXT_USE)
-#define NUM_IGU_SB_REQUIRED(cid_cnt) \
- (FP_SB_COUNT(cid_cnt) - NONE_ETH_CONTEXT_USE)
+/* fast-path interrupt contexts E1x */
+#define FP_SB_MAX_E1x 16
+/* fast-path interrupt contexts E2 */
+#define FP_SB_MAX_E2 HC_SB_MAX_SB_E2
union cdu_context {
struct eth_context eth;
@@ -778,7 +962,7 @@ union cdu_context {
/* CDU host DB constants */
#define CDU_ILT_PAGE_SZ_HW 3
-#define CDU_ILT_PAGE_SZ (4096 << CDU_ILT_PAGE_SZ_HW) /* 32K */
+#define CDU_ILT_PAGE_SZ (8192 << CDU_ILT_PAGE_SZ_HW) /* 64K */
#define ILT_PAGE_CIDS (CDU_ILT_PAGE_SZ / sizeof(union cdu_context))
#ifdef BCM_CNIC
@@ -788,38 +972,63 @@ union cdu_context {
#define CNIC_ILT_LINES DIV_ROUND_UP(CNIC_CID_MAX, ILT_PAGE_CIDS)
#endif
-#define QM_ILT_PAGE_SZ_HW 3
-#define QM_ILT_PAGE_SZ (4096 << QM_ILT_PAGE_SZ_HW) /* 32K */
+#define QM_ILT_PAGE_SZ_HW 0
+#define QM_ILT_PAGE_SZ (4096 << QM_ILT_PAGE_SZ_HW) /* 4K */
#define QM_CID_ROUND 1024
#ifdef BCM_CNIC
/* TM (timers) host DB constants */
-#define TM_ILT_PAGE_SZ_HW 2
-#define TM_ILT_PAGE_SZ (4096 << TM_ILT_PAGE_SZ_HW) /* 16K */
+#define TM_ILT_PAGE_SZ_HW 0
+#define TM_ILT_PAGE_SZ (4096 << TM_ILT_PAGE_SZ_HW) /* 4K */
/* #define TM_CONN_NUM (CNIC_STARTING_CID+CNIC_ISCSI_CXT_MAX) */
#define TM_CONN_NUM 1024
#define TM_ILT_SZ (8 * TM_CONN_NUM)
#define TM_ILT_LINES DIV_ROUND_UP(TM_ILT_SZ, TM_ILT_PAGE_SZ)
/* SRC (Searcher) host DB constants */
-#define SRC_ILT_PAGE_SZ_HW 3
-#define SRC_ILT_PAGE_SZ (4096 << SRC_ILT_PAGE_SZ_HW) /* 32K */
+#define SRC_ILT_PAGE_SZ_HW 0
+#define SRC_ILT_PAGE_SZ (4096 << SRC_ILT_PAGE_SZ_HW) /* 4K */
#define SRC_HASH_BITS 10
#define SRC_CONN_NUM (1 << SRC_HASH_BITS) /* 1024 */
#define SRC_ILT_SZ (sizeof(struct src_ent) * SRC_CONN_NUM)
#define SRC_T2_SZ SRC_ILT_SZ
#define SRC_ILT_LINES DIV_ROUND_UP(SRC_ILT_SZ, SRC_ILT_PAGE_SZ)
+
#endif
-#define MAX_DMAE_C 8
+#define MAX_DMAE_C 8
/* DMA memory not used in fastpath */
struct bnx2x_slowpath {
- struct eth_stats_query fw_stats;
- struct mac_configuration_cmd mac_config;
- struct mac_configuration_cmd mcast_config;
- struct mac_configuration_cmd uc_mac_config;
- struct client_init_ramrod_data client_init_data;
+ union {
+ struct mac_configuration_cmd e1x;
+ struct eth_classify_rules_ramrod_data e2;
+ } mac_rdata;
+
+
+ union {
+ struct tstorm_eth_mac_filter_config e1x;
+ struct eth_filter_rules_ramrod_data e2;
+ } rx_mode_rdata;
+
+ union {
+ struct mac_configuration_cmd e1;
+ struct eth_multicast_rules_ramrod_data e2;
+ } mcast_rdata;
+
+ struct eth_rss_update_ramrod_data rss_rdata;
+
+ /* Queue State related ramrods are always sent under rtnl_lock */
+ union {
+ struct client_init_ramrod_data init_data;
+ struct client_update_ramrod_data update_data;
+ } q_rdata;
+
+ union {
+ struct function_start_data func_start;
+ /* pfc configuration for DCBX ramrod */
+ struct flow_control_configuration pfc_config;
+ } func_rdata;
/* used by dmae command executer */
struct dmae_command dmae[MAX_DMAE_C];
@@ -833,8 +1042,6 @@ struct bnx2x_slowpath {
u32 wb_comp;
u32 wb_data[4];
- /* pfc configuration for DCBX ramrod */
- struct flow_control_configuration pfc_config;
};
#define bnx2x_sp(bp, var) (&bp->slowpath->var)
@@ -846,7 +1053,7 @@ struct bnx2x_slowpath {
#define MAX_DYNAMIC_ATTN_GRPS 8
struct attn_route {
- u32 sig[5];
+ u32 sig[5];
};
struct iro {
@@ -866,13 +1073,15 @@ struct hw_context {
/* forward */
struct bnx2x_ilt;
-typedef enum {
+
+enum bnx2x_recovery_state {
BNX2X_RECOVERY_DONE,
BNX2X_RECOVERY_INIT,
BNX2X_RECOVERY_WAIT,
-} bnx2x_recovery_state_t;
+ BNX2X_RECOVERY_FAILED
+};
-/**
+/*
* Event queue (EQ or event ring) MC hsi
* NUM_EQ_PAGES and EQ_DESC_CNT_PAGE must be power of 2
*/
@@ -910,6 +1119,31 @@ enum {
BNX2X_LINK_REPORT_TX_FC_ON,
};
+enum {
+ BNX2X_PORT_QUERY_IDX,
+ BNX2X_PF_QUERY_IDX,
+ BNX2X_FIRST_QUEUE_QUERY_IDX,
+};
+
+struct bnx2x_fw_stats_req {
+ struct stats_query_header hdr;
+ struct stats_query_entry query[STATS_QUERY_CMD_COUNT];
+};
+
+struct bnx2x_fw_stats_data {
+ struct stats_counter storm_counters;
+ struct per_port_stats port;
+ struct per_pf_stats pf;
+ struct per_queue_stats queue_stats[1];
+};
+
+/* Public slow path states */
+enum {
+ BNX2X_SP_RTNL_SETUP_TC,
+ BNX2X_SP_RTNL_TX_TIMEOUT,
+};
+
+
struct bnx2x {
/* Fields used in the tx and intr/napi performance paths
* are grouped together in the beginning of the structure
@@ -919,19 +1153,29 @@ struct bnx2x {
void __iomem *doorbells;
u16 db_size;
+ u8 pf_num; /* absolute PF number */
+ u8 pfid; /* per-path PF number */
+ int base_fw_ndsb; /**/
+#define BP_PATH(bp) (CHIP_IS_E1x(bp) ? 0 : (bp->pf_num & 1))
+#define BP_PORT(bp) (bp->pfid & 1)
+#define BP_FUNC(bp) (bp->pfid)
+#define BP_ABS_FUNC(bp) (bp->pf_num)
+#define BP_VN(bp) ((bp)->pfid >> 1)
+#define BP_MAX_VN_NUM(bp) (CHIP_MODE_IS_4_PORT(bp) ? 2 : 4)
+#define BP_L_ID(bp) (BP_VN(bp) << 2)
+#define BP_FW_MB_IDX_VN(bp, vn) (BP_PORT(bp) +\
+ (vn) * ((CHIP_IS_E1x(bp) || (CHIP_MODE_IS_4_PORT(bp))) ? 2 : 1))
+#define BP_FW_MB_IDX(bp) BP_FW_MB_IDX_VN(bp, BP_VN(bp))
+
struct net_device *dev;
struct pci_dev *pdev;
- struct iro *iro_arr;
+ const struct iro *iro_arr;
#define IRO (bp->iro_arr)
- atomic_t intr_sem;
-
- bnx2x_recovery_state_t recovery_state;
+ enum bnx2x_recovery_state recovery_state;
int is_leader;
struct msix_entry *msix_table;
-#define INT_MODE_INTx 1
-#define INT_MODE_MSI 2
int tx_ring_size;
@@ -944,7 +1188,8 @@ struct bnx2x {
/* Max supported alignment is 256 (8 shift) */
#define BNX2X_RX_ALIGN_SHIFT ((L1_CACHE_SHIFT < 8) ? \
L1_CACHE_SHIFT : 8)
-#define BNX2X_RX_ALIGN (1 << BNX2X_RX_ALIGN_SHIFT)
+ /* FW use 2 Cache lines Alignment for start packet and size */
+#define BNX2X_FW_RX_ALIGN (2 << BNX2X_RX_ALIGN_SHIFT)
#define BNX2X_PXP_DRAM_ALIGN (BNX2X_RX_ALIGN_SHIFT - 5)
struct host_sp_status_block *def_status_blk;
@@ -974,10 +1219,12 @@ struct bnx2x {
__le16 *eq_cons_sb;
atomic_t eq_spq_left; /* COMMON_XXX ramrods credit */
- /* Flags for marking that there is a STAT_QUERY or
- SET_MAC ramrod pending */
- int stats_pending;
- int set_mac_pending;
+
+
+ /* Counter for marking that there is a STAT_QUERY ramrod pending */
+ u16 stats_pending;
+ /* Counter for completed statistics ramrods */
+ u16 stats_comp;
/* End of fields used in the performance code paths */
@@ -985,54 +1232,35 @@ struct bnx2x {
int msg_enable;
u32 flags;
-#define PCIX_FLAG 1
-#define PCI_32BIT_FLAG 2
-#define ONE_PORT_FLAG 4
-#define NO_WOL_FLAG 8
-#define USING_DAC_FLAG 0x10
-#define USING_MSIX_FLAG 0x20
-#define USING_MSI_FLAG 0x40
-
-#define TPA_ENABLE_FLAG 0x80
-#define NO_MCP_FLAG 0x100
-#define DISABLE_MSI_FLAG 0x200
+#define PCIX_FLAG (1 << 0)
+#define PCI_32BIT_FLAG (1 << 1)
+#define ONE_PORT_FLAG (1 << 2)
+#define NO_WOL_FLAG (1 << 3)
+#define USING_DAC_FLAG (1 << 4)
+#define USING_MSIX_FLAG (1 << 5)
+#define USING_MSI_FLAG (1 << 6)
+#define DISABLE_MSI_FLAG (1 << 7)
+#define TPA_ENABLE_FLAG (1 << 8)
+#define NO_MCP_FLAG (1 << 9)
+
#define BP_NOMCP(bp) (bp->flags & NO_MCP_FLAG)
-#define MF_FUNC_DIS 0x1000
-#define FCOE_MACS_SET 0x2000
-#define NO_FCOE_FLAG 0x4000
-#define NO_ISCSI_OOO_FLAG 0x8000
-#define NO_ISCSI_FLAG 0x10000
+#define MF_FUNC_DIS (1 << 11)
+#define OWN_CNIC_IRQ (1 << 12)
+#define NO_ISCSI_OOO_FLAG (1 << 13)
+#define NO_ISCSI_FLAG (1 << 14)
+#define NO_FCOE_FLAG (1 << 15)
-#define NO_FCOE(bp) ((bp)->flags & NO_FCOE_FLAG)
#define NO_ISCSI(bp) ((bp)->flags & NO_ISCSI_FLAG)
#define NO_ISCSI_OOO(bp) ((bp)->flags & NO_ISCSI_OOO_FLAG)
-
- int pf_num; /* absolute PF number */
- int pfid; /* per-path PF number */
- int base_fw_ndsb;
-#define BP_PATH(bp) (!CHIP_IS_E2(bp) ? \
- 0 : (bp->pf_num & 1))
-#define BP_PORT(bp) (bp->pfid & 1)
-#define BP_FUNC(bp) (bp->pfid)
-#define BP_ABS_FUNC(bp) (bp->pf_num)
-#define BP_E1HVN(bp) (bp->pfid >> 1)
-#define BP_VN(bp) (CHIP_MODE_IS_4_PORT(bp) ? \
- 0 : BP_E1HVN(bp))
-#define BP_L_ID(bp) (BP_E1HVN(bp) << 2)
-#define BP_FW_MB_IDX(bp) (BP_PORT(bp) +\
- BP_VN(bp) * (CHIP_IS_E1x(bp) ? 2 : 1))
-
-#ifdef BCM_CNIC
-#define BCM_CNIC_CID_START 16
-#define BCM_ISCSI_ETH_CL_ID 17
-#endif
+#define NO_FCOE(bp) ((bp)->flags & NO_FCOE_FLAG)
int pm_cap;
- int pcie_cap;
int mrrs;
struct delayed_work sp_task;
- struct delayed_work reset_task;
+ struct delayed_work sp_rtnl_task;
+
+ struct delayed_work period_task;
struct timer_list timer;
int current_interval;
@@ -1052,9 +1280,9 @@ struct bnx2x {
struct cmng_struct_per_port cmng;
u32 vn_weight_sum;
-
u32 mf_config[E1HVN_MAX];
u32 mf2_config[E2_FUNC_MAX];
+ u32 path_has_ovlan; /* E3 */
u16 mf_ov;
u8 mf_mode;
#define IS_MF(bp) (bp->mf_mode != 0)
@@ -1079,33 +1307,24 @@ struct bnx2x {
u32 lin_cnt;
- int state;
+ u16 state;
#define BNX2X_STATE_CLOSED 0
#define BNX2X_STATE_OPENING_WAIT4_LOAD 0x1000
#define BNX2X_STATE_OPENING_WAIT4_PORT 0x2000
#define BNX2X_STATE_OPEN 0x3000
#define BNX2X_STATE_CLOSING_WAIT4_HALT 0x4000
#define BNX2X_STATE_CLOSING_WAIT4_DELETE 0x5000
-#define BNX2X_STATE_CLOSING_WAIT4_UNLOAD 0x6000
-#define BNX2X_STATE_FUNC_STARTED 0x7000
+
#define BNX2X_STATE_DIAG 0xe000
#define BNX2X_STATE_ERROR 0xf000
int multi_mode;
+#define BNX2X_MAX_PRIORITY 8
+#define BNX2X_MAX_ENTRIES_PER_PRI 16
+#define BNX2X_MAX_COS 3
+#define BNX2X_MAX_TX_COS 2
int num_queues;
int disable_tpa;
- int int_mode;
- u32 *rx_indir_table;
-
- struct tstorm_eth_mac_filter_config mac_filters;
-#define BNX2X_ACCEPT_NONE 0x0000
-#define BNX2X_ACCEPT_UNICAST 0x0001
-#define BNX2X_ACCEPT_MULTICAST 0x0002
-#define BNX2X_ACCEPT_ALL_UNICAST 0x0004
-#define BNX2X_ACCEPT_ALL_MULTICAST 0x0008
-#define BNX2X_ACCEPT_BROADCAST 0x0010
-#define BNX2X_ACCEPT_UNMATCHED_UCAST 0x0020
-#define BNX2X_PROMISCUOUS_MODE 0x10000
u32 rx_mode;
#define BNX2X_RX_MODE_NONE 0
@@ -1113,7 +1332,6 @@ struct bnx2x {
#define BNX2X_RX_MODE_ALLMULTI 2
#define BNX2X_RX_MODE_PROMISC 3
#define BNX2X_MAX_MULTICAST 64
-#define BNX2X_MAX_EMUL_MULTI 16
u8 igu_dsb_id;
u8 igu_base_sb;
@@ -1122,16 +1340,53 @@ struct bnx2x {
struct bnx2x_slowpath *slowpath;
dma_addr_t slowpath_mapping;
+
+ /* Total number of FW statistics requests */
+ u8 fw_stats_num;
+
+ /*
+ * This is a memory buffer that will contain both statistics
+ * ramrod request and data.
+ */
+ void *fw_stats;
+ dma_addr_t fw_stats_mapping;
+
+ /*
+ * FW statistics request shortcut (points at the
+ * beginning of fw_stats buffer).
+ */
+ struct bnx2x_fw_stats_req *fw_stats_req;
+ dma_addr_t fw_stats_req_mapping;
+ int fw_stats_req_sz;
+
+ /*
+ * FW statistics data shortcut (points at the begining of
+ * fw_stats buffer + fw_stats_req_sz).
+ */
+ struct bnx2x_fw_stats_data *fw_stats_data;
+ dma_addr_t fw_stats_data_mapping;
+ int fw_stats_data_sz;
+
struct hw_context context;
struct bnx2x_ilt *ilt;
#define BP_ILT(bp) ((bp)->ilt)
-#define ILT_MAX_LINES 128
+#define ILT_MAX_LINES 256
+/*
+ * Maximum supported number of RSS queues: number of IGU SBs minus one that goes
+ * to CNIC.
+ */
+#define BNX2X_MAX_RSS_COUNT(bp) ((bp)->igu_sb_cnt - CNIC_PRESENT)
- int l2_cid_count;
-#define L2_ILT_LINES(bp) (DIV_ROUND_UP((bp)->l2_cid_count, \
- ILT_PAGE_CIDS))
-#define BNX2X_DB_SIZE(bp) ((bp)->l2_cid_count * (1 << BNX2X_DB_SHIFT))
+/*
+ * Maximum CID count that might be required by the bnx2x:
+ * Max Tss * Max_Tx_Multi_Cos + CNIC L2 Clients (FCoE and iSCSI related)
+ */
+#define BNX2X_L2_CID_COUNT(bp) (MAX_TXQS_PER_COS * BNX2X_MULTI_TX_COS +\
+ NON_ETH_CONTEXT_USE + CNIC_PRESENT)
+#define L2_ILT_LINES(bp) (DIV_ROUND_UP(BNX2X_L2_CID_COUNT(bp),\
+ ILT_PAGE_CIDS))
+#define BNX2X_DB_SIZE(bp) (BNX2X_L2_CID_COUNT(bp) * (1 << BNX2X_DB_SHIFT))
int qm_cid_count;
@@ -1148,16 +1403,18 @@ struct bnx2x {
struct cnic_eth_dev cnic_eth_dev;
union host_hc_status_block cnic_sb;
dma_addr_t cnic_sb_mapping;
-#define CNIC_SB_ID(bp) ((bp)->base_fw_ndsb + BP_L_ID(bp))
-#define CNIC_IGU_SB_ID(bp) ((bp)->igu_base_sb)
struct eth_spe *cnic_kwq;
struct eth_spe *cnic_kwq_prod;
struct eth_spe *cnic_kwq_cons;
struct eth_spe *cnic_kwq_last;
u16 cnic_kwq_pending;
u16 cnic_spq_pending;
- struct mutex cnic_mutex;
u8 fip_mac[ETH_ALEN];
+ struct mutex cnic_mutex;
+ struct bnx2x_vlan_mac_obj iscsi_l2_mac_obj;
+
+ /* Start index of the "special" (CNIC related) L2 cleints */
+ u8 cnic_base_cl_id;
#endif
int dmae_ready;
@@ -1194,6 +1451,8 @@ struct bnx2x {
u16 *init_ops_offsets;
/* Data blob - has 32 bit granularity */
u32 *init_data;
+ u32 init_mode_flags;
+#define INIT_MODE_FLAGS(bp) (bp->init_mode_flags)
/* Zipped PRAM blobs - raw data */
const u8 *tsem_int_table_data;
const u8 *tsem_pram_data;
@@ -1215,10 +1474,9 @@ struct bnx2x {
#define INIT_CSEM_INT_TABLE_DATA(bp) (bp->csem_int_table_data)
#define INIT_CSEM_PRAM_DATA(bp) (bp->csem_pram_data)
+#define PHY_FW_VER_LEN 20
char fw_ver[32];
const struct firmware *firmware;
- /* LLDP params */
- struct bnx2x_config_lldp_params lldp_config_params;
/* DCB support on/off */
u16 dcb_state;
@@ -1235,59 +1493,56 @@ struct bnx2x {
bool dcbx_mode_uset;
struct bnx2x_config_dcbx_params dcbx_config_params;
-
struct bnx2x_dcbx_port_params dcbx_port_params;
int dcb_version;
- /* DCBX Negotiation results */
+ /* CAM credit pools */
+ struct bnx2x_credit_pool_obj macs_pool;
+
+ /* RX_MODE object */
+ struct bnx2x_rx_mode_obj rx_mode_obj;
+
+ /* MCAST object */
+ struct bnx2x_mcast_obj mcast_obj;
+
+ /* RSS configuration object */
+ struct bnx2x_rss_config_obj rss_conf_obj;
+
+ /* Function State controlling object */
+ struct bnx2x_func_sp_obj func_obj;
+
+ unsigned long sp_state;
+
+ /* operation indication for the sp_rtnl task */
+ unsigned long sp_rtnl_state;
+
+ /* DCBX Negotation results */
struct dcbx_features dcbx_local_feat;
u32 dcbx_error;
+
#ifdef BCM_DCBNL
struct dcbx_features dcbx_remote_feat;
u32 dcbx_remote_flags;
#endif
u32 pending_max;
-};
-/**
- * Init queue/func interface
- */
-/* queue init flags */
-#define QUEUE_FLG_TPA 0x0001
-#define QUEUE_FLG_CACHE_ALIGN 0x0002
-#define QUEUE_FLG_STATS 0x0004
-#define QUEUE_FLG_OV 0x0008
-#define QUEUE_FLG_VLAN 0x0010
-#define QUEUE_FLG_COS 0x0020
-#define QUEUE_FLG_HC 0x0040
-#define QUEUE_FLG_DHC 0x0080
-#define QUEUE_FLG_OOO 0x0100
-
-#define QUEUE_DROP_IP_CS_ERR TSTORM_ETH_CLIENT_CONFIG_DROP_IP_CS_ERR
-#define QUEUE_DROP_TCP_CS_ERR TSTORM_ETH_CLIENT_CONFIG_DROP_TCP_CS_ERR
-#define QUEUE_DROP_TTL0 TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0
-#define QUEUE_DROP_UDP_CS_ERR TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR
-
-
-
-/* rss capabilities */
-#define RSS_IPV4_CAP 0x0001
-#define RSS_IPV4_TCP_CAP 0x0002
-#define RSS_IPV6_CAP 0x0004
-#define RSS_IPV6_TCP_CAP 0x0008
+ /* multiple tx classes of service */
+ u8 max_cos;
-#define BNX2X_NUM_QUEUES(bp) (bp->num_queues)
-#define BNX2X_NUM_ETH_QUEUES(bp) (BNX2X_NUM_QUEUES(bp) - NONE_ETH_CONTEXT_USE)
+ /* priority to cos mapping */
+ u8 prio_to_cos[8];
+};
-/* ethtool statistics are displayed for all regular ethernet queues and the
- * fcoe L2 queue if not disabled
- */
-#define BNX2X_NUM_STAT_QUEUES(bp) (NO_FCOE(bp) ? BNX2X_NUM_ETH_QUEUES(bp) : \
- (BNX2X_NUM_ETH_QUEUES(bp) + FCOE_CONTEXT_USE))
+/* Tx queues may be less or equal to Rx queues */
+extern int num_queues;
+#define BNX2X_NUM_QUEUES(bp) (bp->num_queues)
+#define BNX2X_NUM_ETH_QUEUES(bp) (BNX2X_NUM_QUEUES(bp) - NON_ETH_CONTEXT_USE)
+#define BNX2X_NUM_RX_QUEUES(bp) BNX2X_NUM_QUEUES(bp)
#define is_multi(bp) (BNX2X_NUM_QUEUES(bp) > 1)
-#define BNX2X_MAX_QUEUES(bp) (bp->igu_sb_cnt - CNIC_CONTEXT_USE)
+#define BNX2X_MAX_QUEUES(bp) BNX2X_MAX_RSS_COUNT(bp)
+/* #define is_eth_multi(bp) (BNX2X_NUM_ETH_QUEUES(bp) > 1) */
#define RSS_IPV4_CAP_MASK \
TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY
@@ -1302,107 +1557,15 @@ struct bnx2x {
TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY
/* func init flags */
-#define FUNC_FLG_STATS 0x0001
-#define FUNC_FLG_TPA 0x0002
-#define FUNC_FLG_SPQ 0x0004
-#define FUNC_FLG_LEADING 0x0008 /* PF only */
-
-struct rxq_pause_params {
- u16 bd_th_lo;
- u16 bd_th_hi;
- u16 rcq_th_lo;
- u16 rcq_th_hi;
- u16 sge_th_lo; /* valid iff QUEUE_FLG_TPA */
- u16 sge_th_hi; /* valid iff QUEUE_FLG_TPA */
- u16 pri_map;
-};
-
-struct bnx2x_rxq_init_params {
- /* cxt*/
- struct eth_context *cxt;
-
- /* dma */
- dma_addr_t dscr_map;
- dma_addr_t sge_map;
- dma_addr_t rcq_map;
- dma_addr_t rcq_np_map;
-
- u16 flags;
- u16 drop_flags;
- u16 mtu;
- u16 buf_sz;
- u16 fw_sb_id;
- u16 cl_id;
- u16 spcl_id;
- u16 cl_qzone_id;
-
- /* valid iff QUEUE_FLG_STATS */
- u16 stat_id;
-
- /* valid iff QUEUE_FLG_TPA */
- u16 tpa_agg_sz;
- u16 sge_buf_sz;
- u16 max_sges_pkt;
-
- /* valid iff QUEUE_FLG_CACHE_ALIGN */
- u8 cache_line_log;
-
- u8 sb_cq_index;
- u32 cid;
-
- /* desired interrupts per sec. valid iff QUEUE_FLG_HC */
- u32 hc_rate;
-};
+#define FUNC_FLG_RSS 0x0001
+#define FUNC_FLG_STATS 0x0002
+/* removed FUNC_FLG_UNMATCHED 0x0004 */
+#define FUNC_FLG_TPA 0x0008
+#define FUNC_FLG_SPQ 0x0010
+#define FUNC_FLG_LEADING 0x0020 /* PF only */
-struct bnx2x_txq_init_params {
- /* cxt*/
- struct eth_context *cxt;
-
- /* dma */
- dma_addr_t dscr_map;
-
- u16 flags;
- u16 fw_sb_id;
- u8 sb_cq_index;
- u8 cos; /* valid iff QUEUE_FLG_COS */
- u16 stat_id; /* valid iff QUEUE_FLG_STATS */
- u16 traffic_type;
- u32 cid;
- u16 hc_rate; /* desired interrupts per sec.*/
- /* valid iff QUEUE_FLG_HC */
-
-};
-
-struct bnx2x_client_ramrod_params {
- int *pstate;
- int state;
- u16 index;
- u16 cl_id;
- u32 cid;
- u8 poll;
-#define CLIENT_IS_FCOE 0x01
-#define CLIENT_IS_LEADING_RSS 0x02
- u8 flags;
-};
-
-struct bnx2x_client_init_params {
- struct rxq_pause_params pause;
- struct bnx2x_rxq_init_params rxq_params;
- struct bnx2x_txq_init_params txq_params;
- struct bnx2x_client_ramrod_params ramrod_params;
-};
-
-struct bnx2x_rss_params {
- int mode;
- u16 cap;
- u16 result_mask;
-};
struct bnx2x_func_init_params {
-
- /* rss */
- struct bnx2x_rss_params *rss; /* valid iff FUNC_FLG_RSS */
-
/* dma */
dma_addr_t fw_stat_map; /* valid iff FUNC_FLG_STATS */
dma_addr_t spq_map; /* valid iff FUNC_FLG_SPQ */
@@ -1414,42 +1577,40 @@ struct bnx2x_func_init_params {
};
#define for_each_eth_queue(bp, var) \
- for (var = 0; var < BNX2X_NUM_ETH_QUEUES(bp); var++)
+ for ((var) = 0; (var) < BNX2X_NUM_ETH_QUEUES(bp); (var)++)
#define for_each_nondefault_eth_queue(bp, var) \
- for (var = 1; var < BNX2X_NUM_ETH_QUEUES(bp); var++)
-
-#define for_each_napi_queue(bp, var) \
- for (var = 0; \
- var < BNX2X_NUM_ETH_QUEUES(bp) + FCOE_CONTEXT_USE; var++) \
- if (skip_queue(bp, var)) \
- continue; \
- else
+ for ((var) = 1; (var) < BNX2X_NUM_ETH_QUEUES(bp); (var)++)
#define for_each_queue(bp, var) \
- for (var = 0; var < BNX2X_NUM_QUEUES(bp); var++) \
+ for ((var) = 0; (var) < BNX2X_NUM_QUEUES(bp); (var)++) \
if (skip_queue(bp, var)) \
continue; \
else
+/* Skip forwarding FP */
#define for_each_rx_queue(bp, var) \
- for (var = 0; var < BNX2X_NUM_QUEUES(bp); var++) \
+ for ((var) = 0; (var) < BNX2X_NUM_QUEUES(bp); (var)++) \
if (skip_rx_queue(bp, var)) \
continue; \
else
+/* Skip OOO FP */
#define for_each_tx_queue(bp, var) \
- for (var = 0; var < BNX2X_NUM_QUEUES(bp); var++) \
+ for ((var) = 0; (var) < BNX2X_NUM_QUEUES(bp); (var)++) \
if (skip_tx_queue(bp, var)) \
continue; \
else
#define for_each_nondefault_queue(bp, var) \
- for (var = 1; var < BNX2X_NUM_QUEUES(bp); var++) \
+ for ((var) = 1; (var) < BNX2X_NUM_QUEUES(bp); (var)++) \
if (skip_queue(bp, var)) \
continue; \
else
+#define for_each_cos_in_tx_queue(fp, var) \
+ for ((var) = 0; (var) < (fp)->max_cos; (var)++)
+
/* skip rx queue
* if FCOE l2 support is disabled and this is the fcoe L2 queue
*/
@@ -1462,11 +1623,66 @@ struct bnx2x_func_init_params {
#define skip_queue(bp, idx) (NO_FCOE(bp) && IS_FCOE_IDX(idx))
-#define WAIT_RAMROD_POLL 0x01
-#define WAIT_RAMROD_COMMON 0x02
+
+
+/**
+ * bnx2x_set_mac_one - configure a single MAC address
+ *
+ * @bp: driver handle
+ * @mac: MAC to configure
+ * @obj: MAC object handle
+ * @set: if 'true' add a new MAC, otherwise - delete
+ * @mac_type: the type of the MAC to configure (e.g. ETH, UC list)
+ * @ramrod_flags: RAMROD_XXX flags (e.g. RAMROD_CONT, RAMROD_COMP_WAIT)
+ *
+ * Configures one MAC according to provided parameters or continues the
+ * execution of previously scheduled commands if RAMROD_CONT is set in
+ * ramrod_flags.
+ *
+ * Returns zero if operation has successfully completed, a positive value if the
+ * operation has been successfully scheduled and a negative - if a requested
+ * operations has failed.
+ */
+int bnx2x_set_mac_one(struct bnx2x *bp, u8 *mac,
+ struct bnx2x_vlan_mac_obj *obj, bool set,
+ int mac_type, unsigned long *ramrod_flags);
+/**
+ * Deletes all MACs configured for the specific MAC object.
+ *
+ * @param bp Function driver instance
+ * @param mac_obj MAC object to cleanup
+ *
+ * @return zero if all MACs were cleaned
+ */
+
+/**
+ * bnx2x_del_all_macs - delete all MACs configured for the specific MAC object
+ *
+ * @bp: driver handle
+ * @mac_obj: MAC object handle
+ * @mac_type: type of the MACs to clear (BNX2X_XXX_MAC)
+ * @wait_for_comp: if 'true' block until completion
+ *
+ * Deletes all MACs of the specific type (e.g. ETH, UC list).
+ *
+ * Returns zero if operation has successfully completed, a positive value if the
+ * operation has been successfully scheduled and a negative - if a requested
+ * operations has failed.
+ */
+int bnx2x_del_all_macs(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *mac_obj,
+ int mac_type, bool wait_for_comp);
+
+/* Init Function API */
+void bnx2x_func_init(struct bnx2x *bp, struct bnx2x_func_init_params *p);
+int bnx2x_get_gpio(struct bnx2x *bp, int gpio_num, u8 port);
+int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode, u8 port);
+int bnx2x_set_mult_gpio(struct bnx2x *bp, u8 pins, u32 mode);
+int bnx2x_set_gpio_int(struct bnx2x *bp, int gpio_num, u32 mode, u8 port);
void bnx2x_read_mf_cfg(struct bnx2x *bp);
+
/* dmae */
void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32);
void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
@@ -1477,22 +1693,12 @@ u32 bnx2x_dmae_opcode_clr_src_reset(u32 opcode);
u32 bnx2x_dmae_opcode(struct bnx2x *bp, u8 src_type, u8 dst_type,
bool with_comp, u8 comp_type);
-int bnx2x_get_gpio(struct bnx2x *bp, int gpio_num, u8 port);
-int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode, u8 port);
-int bnx2x_set_gpio_int(struct bnx2x *bp, int gpio_num, u32 mode, u8 port);
-u32 bnx2x_fw_command(struct bnx2x *bp, u32 command, u32 param);
void bnx2x_calc_fc_adv(struct bnx2x *bp);
int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
- u32 data_hi, u32 data_lo, int common);
-
-/* Clears multicast and unicast list configuration in the chip. */
-void bnx2x_invalidate_e1_mc_list(struct bnx2x *bp);
-void bnx2x_invalidate_e1h_mc_list(struct bnx2x *bp);
-void bnx2x_invalidate_uc_list(struct bnx2x *bp);
-
+ u32 data_hi, u32 data_lo, int cmd_type);
void bnx2x_update_coalesce(struct bnx2x *bp);
-int bnx2x_get_link_cfg_idx(struct bnx2x *bp);
+int bnx2x_get_cur_phy_idx(struct bnx2x *bp);
static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
int wait)
@@ -1622,7 +1828,7 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
#define MAX_DMAE_C_PER_PORT 8
#define INIT_DMAE_C(bp) (BP_PORT(bp) * MAX_DMAE_C_PER_PORT + \
- BP_E1HVN(bp))
+ BP_VN(bp))
#define PMF_DMAE_C(bp) (BP_PORT(bp) * MAX_DMAE_C_PER_PORT + \
E1HVN_MAX)
@@ -1648,7 +1854,8 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
/* must be used on a CID before placing it on a HW ring */
#define HW_CID(bp, x) ((BP_PORT(bp) << 23) | \
- (BP_E1HVN(bp) << 17) | (x))
+ (BP_VN(bp) << BNX2X_SWCID_SHIFT) | \
+ (x))
#define SP_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct eth_spe))
#define MAX_SP_DESC_CNT (SP_DESC_CNT - 1)
@@ -1718,12 +1925,14 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
(AEU_INPUTS_ATTN_BITS_TSDM_HW_INTERRUPT | \
AEU_INPUTS_ATTN_BITS_TCM_HW_INTERRUPT | \
AEU_INPUTS_ATTN_BITS_TSEMI_HW_INTERRUPT | \
- AEU_INPUTS_ATTN_BITS_PBF_HW_INTERRUPT)
+ AEU_INPUTS_ATTN_BITS_PBCLIENT_HW_INTERRUPT)
#define HW_PRTY_ASSERT_SET_0 (AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR | \
AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR | \
AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR | \
AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR |\
- AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR)
+ AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR |\
+ AEU_INPUTS_ATTN_BITS_TCM_PARITY_ERROR |\
+ AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR)
#define HW_INTERRUT_ASSERT_SET_1 \
(AEU_INPUTS_ATTN_BITS_QM_HW_INTERRUPT | \
AEU_INPUTS_ATTN_BITS_TIMERS_HW_INTERRUPT | \
@@ -1736,17 +1945,22 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
AEU_INPUTS_ATTN_BITS_UPB_HW_INTERRUPT | \
AEU_INPUTS_ATTN_BITS_CSDM_HW_INTERRUPT | \
AEU_INPUTS_ATTN_BITS_CCM_HW_INTERRUPT)
-#define HW_PRTY_ASSERT_SET_1 (AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR |\
+#define HW_PRTY_ASSERT_SET_1 (AEU_INPUTS_ATTN_BITS_PBF_PARITY_ERROR |\
AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR | \
+ AEU_INPUTS_ATTN_BITS_TIMERS_PARITY_ERROR |\
AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR | \
+ AEU_INPUTS_ATTN_BITS_XCM_PARITY_ERROR |\
AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR | \
AEU_INPUTS_ATTN_BITS_DOORBELLQ_PARITY_ERROR |\
+ AEU_INPUTS_ATTN_BITS_NIG_PARITY_ERROR |\
AEU_INPUTS_ATTN_BITS_VAUX_PCI_CORE_PARITY_ERROR |\
AEU_INPUTS_ATTN_BITS_DEBUG_PARITY_ERROR | \
AEU_INPUTS_ATTN_BITS_USDM_PARITY_ERROR | \
+ AEU_INPUTS_ATTN_BITS_UCM_PARITY_ERROR |\
AEU_INPUTS_ATTN_BITS_USEMI_PARITY_ERROR | \
AEU_INPUTS_ATTN_BITS_UPB_PARITY_ERROR | \
- AEU_INPUTS_ATTN_BITS_CSDM_PARITY_ERROR)
+ AEU_INPUTS_ATTN_BITS_CSDM_PARITY_ERROR |\
+ AEU_INPUTS_ATTN_BITS_CCM_PARITY_ERROR)
#define HW_INTERRUT_ASSERT_SET_2 \
(AEU_INPUTS_ATTN_BITS_CSEMI_HW_INTERRUPT | \
AEU_INPUTS_ATTN_BITS_CDU_HW_INTERRUPT | \
@@ -1758,6 +1972,7 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
AEU_INPUTS_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR |\
AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR | \
AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR | \
+ AEU_INPUTS_ATTN_BITS_DMAE_PARITY_ERROR |\
AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR | \
AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR)
@@ -1766,6 +1981,9 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY | \
AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY)
+#define HW_PRTY_ASSERT_SET_4 (AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR | \
+ AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR)
+
#define RSS_FLAGS(bp) \
(TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY | \
TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY | \
@@ -1775,6 +1993,30 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_MODE_SHIFT))
#define MULTI_MASK 0x7f
+
+#define DEF_USB_FUNC_OFF offsetof(struct cstorm_def_status_block_u, func)
+#define DEF_CSB_FUNC_OFF offsetof(struct cstorm_def_status_block_c, func)
+#define DEF_XSB_FUNC_OFF offsetof(struct xstorm_def_status_block, func)
+#define DEF_TSB_FUNC_OFF offsetof(struct tstorm_def_status_block, func)
+
+#define DEF_USB_IGU_INDEX_OFF \
+ offsetof(struct cstorm_def_status_block_u, igu_index)
+#define DEF_CSB_IGU_INDEX_OFF \
+ offsetof(struct cstorm_def_status_block_c, igu_index)
+#define DEF_XSB_IGU_INDEX_OFF \
+ offsetof(struct xstorm_def_status_block, igu_index)
+#define DEF_TSB_IGU_INDEX_OFF \
+ offsetof(struct tstorm_def_status_block, igu_index)
+
+#define DEF_USB_SEGMENT_OFF \
+ offsetof(struct cstorm_def_status_block_u, segment)
+#define DEF_CSB_SEGMENT_OFF \
+ offsetof(struct cstorm_def_status_block_c, segment)
+#define DEF_XSB_SEGMENT_OFF \
+ offsetof(struct xstorm_def_status_block, segment)
+#define DEF_TSB_SEGMENT_OFF \
+ offsetof(struct tstorm_def_status_block, segment)
+
#define BNX2X_SP_DSB_INDEX \
(&bp->def_status_blk->sp_sb.\
index_values[HC_SP_INDEX_ETH_DEF_CONS])
@@ -1786,7 +2028,7 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
} while (0)
#define GET_FLAG(value, mask) \
- (((value) &= (mask)) >> (mask##_SHIFT))
+ (((value) & (mask)) >> (mask##_SHIFT))
#define GET_FIELD(value, fname) \
(((value) & (fname##_MASK)) >> (fname##_SHIFT))
@@ -1821,15 +2063,13 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
#define HC_SEG_ACCESS_ATTN 4
#define HC_SEG_ACCESS_NORM 0 /*Driver decision 0-1*/
-#ifdef BNX2X_MAIN
-#define BNX2X_EXTERN
-#else
-#define BNX2X_EXTERN extern
-#endif
-
-BNX2X_EXTERN int load_count[2][3]; /* per path: 0-common, 1-port0, 2-port1 */
-
-extern void bnx2x_set_ethtool_ops(struct net_device *netdev);
-void bnx2x_push_indir_table(struct bnx2x *bp);
+static const u32 dmae_reg_go_c[] = {
+ DMAE_REG_GO_C0, DMAE_REG_GO_C1, DMAE_REG_GO_C2, DMAE_REG_GO_C3,
+ DMAE_REG_GO_C4, DMAE_REG_GO_C5, DMAE_REG_GO_C6, DMAE_REG_GO_C7,
+ DMAE_REG_GO_C8, DMAE_REG_GO_C9, DMAE_REG_GO_C10, DMAE_REG_GO_C11,
+ DMAE_REG_GO_C12, DMAE_REG_GO_C13, DMAE_REG_GO_C14, DMAE_REG_GO_C15
+};
+void bnx2x_set_ethtool_ops(struct net_device *netdev);
+void bnx2x_notify_link_changed(struct bnx2x *bp);
#endif /* bnx2x.h */
diff --git a/drivers/net/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index 289044332ed8..580b44edb066 100644
--- a/drivers/net/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -15,18 +15,21 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
+#include <linux/interrupt.h>
#include <linux/ip.h>
#include <net/ipv6.h>
#include <net/ip6_checksum.h>
#include <linux/firmware.h>
#include <linux/prefetch.h>
#include "bnx2x_cmn.h"
-
#include "bnx2x_init.h"
+#include "bnx2x_sp.h"
+
-static int bnx2x_setup_irqs(struct bnx2x *bp);
/**
* bnx2x_bz_fp - zero content of the fastpath structure.
@@ -46,6 +49,26 @@ static inline void bnx2x_bz_fp(struct bnx2x *bp, int index)
/* Restore the NAPI object as it has been already initialized */
fp->napi = orig_napi;
+
+ fp->bp = bp;
+ fp->index = index;
+ if (IS_ETH_FP(fp))
+ fp->max_cos = bp->max_cos;
+ else
+ /* Special queues support only one CoS */
+ fp->max_cos = 1;
+
+ /*
+ * set the tpa flag for each queue. The tpa flag determines the queue
+ * minimal size so it must be set prior to queue memory allocation
+ */
+ fp->disable_tpa = ((bp->flags & TPA_ENABLE_FLAG) == 0);
+
+#ifdef BCM_CNIC
+ /* We don't want TPA on an FCoE L2 ring */
+ if (IS_FCOE_FP(fp))
+ fp->disable_tpa = 1;
+#endif
}
/**
@@ -71,13 +94,15 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to)
to_fp->napi = orig_napi;
}
+int load_count[2][3] = { {0} }; /* per-path: 0-common, 1-port0, 2-port1 */
+
/* free skb in the packet ring at pos idx
* return idx of last bd freed
*/
-static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp,
+static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fp_txdata *txdata,
u16 idx)
{
- struct sw_tx_bd *tx_buf = &fp->tx_buf_ring[idx];
+ struct sw_tx_bd *tx_buf = &txdata->tx_buf_ring[idx];
struct eth_tx_start_bd *tx_start_bd;
struct eth_tx_bd *tx_data_bd;
struct sk_buff *skb = tx_buf->skb;
@@ -87,15 +112,16 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp,
/* prefetch skb end pointer to speedup dev_kfree_skb() */
prefetch(&skb->end);
- DP(BNX2X_MSG_OFF, "pkt_idx %d buff @(%p)->skb %p\n",
- idx, tx_buf, skb);
+ DP(BNX2X_MSG_FP, "fp[%d]: pkt_idx %d buff @(%p)->skb %p\n",
+ txdata->txq_index, idx, tx_buf, skb);
/* unmap first bd */
DP(BNX2X_MSG_OFF, "free bd_idx %d\n", bd_idx);
- tx_start_bd = &fp->tx_desc_ring[bd_idx].start_bd;
+ tx_start_bd = &txdata->tx_desc_ring[bd_idx].start_bd;
dma_unmap_single(&bp->pdev->dev, BD_UNMAP_ADDR(tx_start_bd),
BD_UNMAP_LEN(tx_start_bd), DMA_TO_DEVICE);
+
nbd = le16_to_cpu(tx_start_bd->nbd) - 1;
#ifdef BNX2X_STOP_ON_ERROR
if ((nbd - 1) > (MAX_SKB_FRAGS + 2)) {
@@ -122,7 +148,7 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp,
while (nbd > 0) {
DP(BNX2X_MSG_OFF, "free frag bd_idx %d\n", bd_idx);
- tx_data_bd = &fp->tx_desc_ring[bd_idx].reg_bd;
+ tx_data_bd = &txdata->tx_desc_ring[bd_idx].reg_bd;
dma_unmap_page(&bp->pdev->dev, BD_UNMAP_ADDR(tx_data_bd),
BD_UNMAP_LEN(tx_data_bd), DMA_TO_DEVICE);
if (--nbd)
@@ -138,20 +164,19 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp,
return new_cons;
}
-int bnx2x_tx_int(struct bnx2x_fastpath *fp)
+int bnx2x_tx_int(struct bnx2x *bp, struct bnx2x_fp_txdata *txdata)
{
- struct bnx2x *bp = fp->bp;
struct netdev_queue *txq;
- u16 hw_cons, sw_cons, bd_cons = fp->tx_bd_cons;
+ u16 hw_cons, sw_cons, bd_cons = txdata->tx_bd_cons;
#ifdef BNX2X_STOP_ON_ERROR
if (unlikely(bp->panic))
return -1;
#endif
- txq = netdev_get_tx_queue(bp->dev, fp->index);
- hw_cons = le16_to_cpu(*fp->tx_cons_sb);
- sw_cons = fp->tx_pkt_cons;
+ txq = netdev_get_tx_queue(bp->dev, txdata->txq_index);
+ hw_cons = le16_to_cpu(*txdata->tx_cons_sb);
+ sw_cons = txdata->tx_pkt_cons;
while (sw_cons != hw_cons) {
u16 pkt_cons;
@@ -160,20 +185,23 @@ int bnx2x_tx_int(struct bnx2x_fastpath *fp)
DP(NETIF_MSG_TX_DONE, "queue[%d]: hw_cons %u sw_cons %u "
" pkt_cons %u\n",
- fp->index, hw_cons, sw_cons, pkt_cons);
+ txdata->txq_index, hw_cons, sw_cons, pkt_cons);
- bd_cons = bnx2x_free_tx_pkt(bp, fp, pkt_cons);
+ bd_cons = bnx2x_free_tx_pkt(bp, txdata, pkt_cons);
sw_cons++;
}
- fp->tx_pkt_cons = sw_cons;
- fp->tx_bd_cons = bd_cons;
+ txdata->tx_pkt_cons = sw_cons;
+ txdata->tx_bd_cons = bd_cons;
/* Need to make the tx_bd_cons update visible to start_xmit()
* before checking for netif_tx_queue_stopped(). Without the
* memory barrier, there is a small possibility that
* start_xmit() will miss it and cause the queue to be stopped
* forever.
+ * On the other hand we need an rmb() here to ensure the proper
+ * ordering of bit testing in the following
+ * netif_tx_queue_stopped(txq) call.
*/
smp_mb();
@@ -192,7 +220,7 @@ int bnx2x_tx_int(struct bnx2x_fastpath *fp)
if ((netif_tx_queue_stopped(txq)) &&
(bp->state == BNX2X_STATE_OPEN) &&
- (bnx2x_tx_avail(fp) >= MAX_SKB_FRAGS + 3))
+ (bnx2x_tx_avail(bp, txdata) >= MAX_SKB_FRAGS + 3))
netif_tx_wake_queue(txq);
__netif_tx_unlock(txq);
@@ -225,7 +253,7 @@ static void bnx2x_update_sge_prod(struct bnx2x_fastpath *fp,
/* First mark all used pages */
for (i = 0; i < sge_len; i++)
- SGE_MASK_CLEAR_BIT(fp,
+ BIT_VEC64_CLEAR_BIT(fp->sge_mask,
RX_SGE(le16_to_cpu(fp_cqe->sgl_or_raw_data.sgl[i])));
DP(NETIF_MSG_RX_STATUS, "fp_cqe->sgl[%d] = %d\n",
@@ -237,8 +265,8 @@ static void bnx2x_update_sge_prod(struct bnx2x_fastpath *fp,
le16_to_cpu(fp_cqe->sgl_or_raw_data.sgl[sge_len - 1]));
last_max = RX_SGE(fp->last_max_sge);
- last_elem = last_max >> RX_SGE_MASK_ELEM_SHIFT;
- first_elem = RX_SGE(fp->rx_sge_prod) >> RX_SGE_MASK_ELEM_SHIFT;
+ last_elem = last_max >> BIT_VEC64_ELEM_SHIFT;
+ first_elem = RX_SGE(fp->rx_sge_prod) >> BIT_VEC64_ELEM_SHIFT;
/* If ring is not full */
if (last_elem + 1 != first_elem)
@@ -249,8 +277,8 @@ static void bnx2x_update_sge_prod(struct bnx2x_fastpath *fp,
if (likely(fp->sge_mask[i]))
break;
- fp->sge_mask[i] = RX_SGE_MASK_ELEM_ONE_MASK;
- delta += RX_SGE_MASK_ELEM_SZ;
+ fp->sge_mask[i] = BIT_VEC64_ELEM_ONE_MASK;
+ delta += BIT_VEC64_ELEM_SZ;
}
if (delta > 0) {
@@ -265,33 +293,56 @@ static void bnx2x_update_sge_prod(struct bnx2x_fastpath *fp,
}
static void bnx2x_tpa_start(struct bnx2x_fastpath *fp, u16 queue,
- struct sk_buff *skb, u16 cons, u16 prod)
+ struct sk_buff *skb, u16 cons, u16 prod,
+ struct eth_fast_path_rx_cqe *cqe)
{
struct bnx2x *bp = fp->bp;
struct sw_rx_bd *cons_rx_buf = &fp->rx_buf_ring[cons];
struct sw_rx_bd *prod_rx_buf = &fp->rx_buf_ring[prod];
struct eth_rx_bd *prod_bd = &fp->rx_desc_ring[prod];
dma_addr_t mapping;
+ struct bnx2x_agg_info *tpa_info = &fp->tpa_info[queue];
+ struct sw_rx_bd *first_buf = &tpa_info->first_buf;
- /* move empty skb from pool to prod and map it */
- prod_rx_buf->skb = fp->tpa_pool[queue].skb;
- mapping = dma_map_single(&bp->pdev->dev, fp->tpa_pool[queue].skb->data,
- fp->rx_buf_size, DMA_FROM_DEVICE);
- dma_unmap_addr_set(prod_rx_buf, mapping, mapping);
-
- /* move partial skb from cons to pool (don't unmap yet) */
- fp->tpa_pool[queue] = *cons_rx_buf;
-
- /* mark bin state as start - print error if current state != stop */
- if (fp->tpa_state[queue] != BNX2X_TPA_STOP)
+ /* print error if current state != stop */
+ if (tpa_info->tpa_state != BNX2X_TPA_STOP)
BNX2X_ERR("start of bin not in stop [%d]\n", queue);
- fp->tpa_state[queue] = BNX2X_TPA_START;
+ /* Try to map an empty skb from the aggregation info */
+ mapping = dma_map_single(&bp->pdev->dev,
+ first_buf->skb->data,
+ fp->rx_buf_size, DMA_FROM_DEVICE);
+ /*
+ * ...if it fails - move the skb from the consumer to the producer
+ * and set the current aggregation state as ERROR to drop it
+ * when TPA_STOP arrives.
+ */
+ if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) {
+ /* Move the BD from the consumer to the producer */
+ bnx2x_reuse_rx_skb(fp, cons, prod);
+ tpa_info->tpa_state = BNX2X_TPA_ERROR;
+ return;
+ }
+
+ /* move empty skb from pool to prod */
+ prod_rx_buf->skb = first_buf->skb;
+ dma_unmap_addr_set(prod_rx_buf, mapping, mapping);
/* point prod_bd to new skb */
prod_bd->addr_hi = cpu_to_le32(U64_HI(mapping));
prod_bd->addr_lo = cpu_to_le32(U64_LO(mapping));
+ /* move partial skb from cons to pool (don't unmap yet) */
+ *first_buf = *cons_rx_buf;
+
+ /* mark bin state as START */
+ tpa_info->parsing_flags =
+ le16_to_cpu(cqe->pars_flags.flags);
+ tpa_info->vlan_tag = le16_to_cpu(cqe->vlan_tag);
+ tpa_info->tpa_state = BNX2X_TPA_START;
+ tpa_info->len_on_bd = le16_to_cpu(cqe->len_on_bd);
+ tpa_info->placement_offset = cqe->placement_offset;
+
#ifdef BNX2X_STOP_ON_ERROR
fp->tpa_queue_used |= (1 << queue);
#ifdef _ASM_GENERIC_INT_L64_H
@@ -322,10 +373,17 @@ static void bnx2x_tpa_start(struct bnx2x_fastpath *fp, u16 queue,
static inline u16 bnx2x_set_lro_mss(struct bnx2x *bp, u16 parsing_flags,
u16 len_on_bd)
{
- /* TPA arrgregation won't have an IP options and TCP options
- * other than timestamp.
+ /*
+ * TPA arrgregation won't have either IP options or TCP options
+ * other than timestamp or IPv6 extension headers.
*/
- u16 hdrs_len = ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr);
+ u16 hdrs_len = ETH_HLEN + sizeof(struct tcphdr);
+
+ if (GET_FLAG(parsing_flags, PARSING_FLAGS_OVER_ETHERNET_PROTOCOL) ==
+ PRS_FLAG_OVERETH_IPV6)
+ hdrs_len += sizeof(struct ipv6hdr);
+ else /* IPv4 */
+ hdrs_len += sizeof(struct iphdr);
/* Check if there was a TCP timestamp, if there is it's will
@@ -340,30 +398,30 @@ static inline u16 bnx2x_set_lro_mss(struct bnx2x *bp, u16 parsing_flags,
}
static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp,
- struct sk_buff *skb,
- struct eth_fast_path_rx_cqe *fp_cqe,
- u16 cqe_idx, u16 parsing_flags)
+ u16 queue, struct sk_buff *skb,
+ struct eth_end_agg_rx_cqe *cqe,
+ u16 cqe_idx)
{
struct sw_rx_page *rx_pg, old_rx_pg;
- u16 len_on_bd = le16_to_cpu(fp_cqe->len_on_bd);
u32 i, frag_len, frag_size, pages;
int err;
int j;
+ struct bnx2x_agg_info *tpa_info = &fp->tpa_info[queue];
+ u16 len_on_bd = tpa_info->len_on_bd;
- frag_size = le16_to_cpu(fp_cqe->pkt_len) - len_on_bd;
+ frag_size = le16_to_cpu(cqe->pkt_len) - len_on_bd;
pages = SGE_PAGE_ALIGN(frag_size) >> SGE_PAGE_SHIFT;
/* This is needed in order to enable forwarding support */
if (frag_size)
- skb_shinfo(skb)->gso_size = bnx2x_set_lro_mss(bp, parsing_flags,
- len_on_bd);
+ skb_shinfo(skb)->gso_size = bnx2x_set_lro_mss(bp,
+ tpa_info->parsing_flags, len_on_bd);
#ifdef BNX2X_STOP_ON_ERROR
if (pages > min_t(u32, 8, MAX_SKB_FRAGS)*SGE_PAGE_SIZE*PAGES_PER_SGE) {
BNX2X_ERR("SGL length is too long: %d. CQE index is %d\n",
pages, cqe_idx);
- BNX2X_ERR("fp_cqe->pkt_len = %d fp_cqe->len_on_bd = %d\n",
- fp_cqe->pkt_len, len_on_bd);
+ BNX2X_ERR("cqe->pkt_len = %d\n", cqe->pkt_len);
bnx2x_panic();
return -EINVAL;
}
@@ -371,8 +429,7 @@ static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp,
/* Run through the SGL and compose the fragmented skb */
for (i = 0, j = 0; i < pages; i += PAGES_PER_SGE, j++) {
- u16 sge_idx =
- RX_SGE(le16_to_cpu(fp_cqe->sgl_or_raw_data.sgl[j]));
+ u16 sge_idx = RX_SGE(le16_to_cpu(cqe->sgl_or_raw_data.sgl[j]));
/* FW gives the indices of the SGE as if the ring is an array
(meaning that "next" element will consume 2 indices) */
@@ -397,7 +454,7 @@ static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp,
skb_fill_page_desc(skb, j, old_rx_pg.page, 0, frag_len);
skb->data_len += frag_len;
- skb->truesize += frag_len;
+ skb->truesize += SGE_PAGE_SIZE * PAGES_PER_SGE;
skb->len += frag_len;
frag_size -= frag_len;
@@ -407,13 +464,28 @@ static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp,
}
static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp,
- u16 queue, int pad, int len, union eth_rx_cqe *cqe,
+ u16 queue, struct eth_end_agg_rx_cqe *cqe,
u16 cqe_idx)
{
- struct sw_rx_bd *rx_buf = &fp->tpa_pool[queue];
+ struct bnx2x_agg_info *tpa_info = &fp->tpa_info[queue];
+ struct sw_rx_bd *rx_buf = &tpa_info->first_buf;
+ u8 pad = tpa_info->placement_offset;
+ u16 len = tpa_info->len_on_bd;
struct sk_buff *skb = rx_buf->skb;
/* alloc new skb */
- struct sk_buff *new_skb = netdev_alloc_skb(bp->dev, fp->rx_buf_size);
+ struct sk_buff *new_skb;
+ u8 old_tpa_state = tpa_info->tpa_state;
+
+ tpa_info->tpa_state = BNX2X_TPA_STOP;
+
+ /* If we there was an error during the handling of the TPA_START -
+ * drop this aggregation.
+ */
+ if (old_tpa_state == BNX2X_TPA_ERROR)
+ goto drop;
+
+ /* Try to allocate the new skb */
+ new_skb = netdev_alloc_skb(bp->dev, fp->rx_buf_size);
/* Unmap skb in the pool anyway, as we are going to change
pool entry status to BNX2X_TPA_STOP even if new skb allocation
@@ -422,11 +494,6 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp,
fp->rx_buf_size, DMA_FROM_DEVICE);
if (likely(new_skb)) {
- /* fix ip xsum and give it to the stack */
- /* (no need to map the new skb) */
- u16 parsing_flags =
- le16_to_cpu(cqe->fast_path_cqe.pars_flags.flags);
-
prefetch(skb);
prefetch(((char *)(skb)) + L1_CACHE_BYTES);
@@ -446,21 +513,9 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp,
skb->protocol = eth_type_trans(skb, bp->dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
- {
- struct iphdr *iph;
-
- iph = (struct iphdr *)skb->data;
- iph->check = 0;
- iph->check = ip_fast_csum((u8 *)iph, iph->ihl);
- }
-
- if (!bnx2x_fill_frag_skb(bp, fp, skb,
- &cqe->fast_path_cqe, cqe_idx,
- parsing_flags)) {
- if (parsing_flags & PARSING_FLAGS_VLAN)
- __vlan_hwaccel_put_tag(skb,
- le16_to_cpu(cqe->fast_path_cqe.
- vlan_tag));
+ if (!bnx2x_fill_frag_skb(bp, fp, queue, skb, cqe, cqe_idx)) {
+ if (tpa_info->parsing_flags & PARSING_FLAGS_VLAN)
+ __vlan_hwaccel_put_tag(skb, tpa_info->vlan_tag);
napi_gro_receive(&fp->napi, skb);
} else {
DP(NETIF_MSG_RX_STATUS, "Failed to allocate new pages"
@@ -470,16 +525,16 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp,
/* put new skb in bin */
- fp->tpa_pool[queue].skb = new_skb;
+ rx_buf->skb = new_skb;
- } else {
- /* else drop the packet and keep the buffer in the bin */
- DP(NETIF_MSG_RX_STATUS,
- "Failed to allocate new skb - dropping packet!\n");
- fp->eth_q_stats.rx_skb_alloc_failed++;
+ return;
}
- fp->tpa_state[queue] = BNX2X_TPA_STOP;
+drop:
+ /* drop the packet and keep the buffer in the bin */
+ DP(NETIF_MSG_RX_STATUS,
+ "Failed to allocate or map a new skb - dropping packet!\n");
+ fp->eth_q_stats.rx_skb_alloc_failed++;
}
/* Set Toeplitz hash value in the skb using the value from the
@@ -533,9 +588,16 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
struct sw_rx_bd *rx_buf = NULL;
struct sk_buff *skb;
union eth_rx_cqe *cqe;
+ struct eth_fast_path_rx_cqe *cqe_fp;
u8 cqe_fp_flags;
+ enum eth_rx_cqe_type cqe_fp_type;
u16 len, pad;
+#ifdef BNX2X_STOP_ON_ERROR
+ if (unlikely(bp->panic))
+ return 0;
+#endif
+
comp_ring_cons = RCQ_BD(sw_comp_cons);
bd_prod = RX_BD(bd_prod);
bd_cons = RX_BD(bd_cons);
@@ -548,17 +610,18 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
PAGE_SIZE + 1));
cqe = &fp->rx_comp_ring[comp_ring_cons];
- cqe_fp_flags = cqe->fast_path_cqe.type_error_flags;
+ cqe_fp = &cqe->fast_path_cqe;
+ cqe_fp_flags = cqe_fp->type_error_flags;
+ cqe_fp_type = cqe_fp_flags & ETH_FAST_PATH_RX_CQE_TYPE;
DP(NETIF_MSG_RX_STATUS, "CQE type %x err %x status %x"
" queue %x vlan %x len %u\n", CQE_TYPE(cqe_fp_flags),
- cqe_fp_flags, cqe->fast_path_cqe.status_flags,
- le32_to_cpu(cqe->fast_path_cqe.rss_hash_result),
- le16_to_cpu(cqe->fast_path_cqe.vlan_tag),
- le16_to_cpu(cqe->fast_path_cqe.pkt_len));
+ cqe_fp_flags, cqe_fp->status_flags,
+ le32_to_cpu(cqe_fp->rss_hash_result),
+ le16_to_cpu(cqe_fp->vlan_tag), le16_to_cpu(cqe_fp->pkt_len));
/* is this a slowpath msg? */
- if (unlikely(CQE_TYPE(cqe_fp_flags))) {
+ if (unlikely(CQE_TYPE_SLOW(cqe_fp_type))) {
bnx2x_sp_event(fp, cqe);
goto next_cqe;
@@ -567,61 +630,59 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
rx_buf = &fp->rx_buf_ring[bd_cons];
skb = rx_buf->skb;
prefetch(skb);
- len = le16_to_cpu(cqe->fast_path_cqe.pkt_len);
- pad = cqe->fast_path_cqe.placement_offset;
- /* - If CQE is marked both TPA_START and TPA_END it is
- * a non-TPA CQE.
- * - FP CQE will always have either TPA_START or/and
- * TPA_STOP flags set.
- */
- if ((!fp->disable_tpa) &&
- (TPA_TYPE(cqe_fp_flags) !=
- (TPA_TYPE_START | TPA_TYPE_END))) {
- u16 queue = cqe->fast_path_cqe.queue_index;
+ if (!CQE_TYPE_FAST(cqe_fp_type)) {
+#ifdef BNX2X_STOP_ON_ERROR
+ /* sanity check */
+ if (fp->disable_tpa &&
+ (CQE_TYPE_START(cqe_fp_type) ||
+ CQE_TYPE_STOP(cqe_fp_type)))
+ BNX2X_ERR("START/STOP packet while "
+ "disable_tpa type %x\n",
+ CQE_TYPE(cqe_fp_type));
+#endif
- if (TPA_TYPE(cqe_fp_flags) == TPA_TYPE_START) {
+ if (CQE_TYPE_START(cqe_fp_type)) {
+ u16 queue = cqe_fp->queue_index;
DP(NETIF_MSG_RX_STATUS,
"calling tpa_start on queue %d\n",
queue);
bnx2x_tpa_start(fp, queue, skb,
- bd_cons, bd_prod);
+ bd_cons, bd_prod,
+ cqe_fp);
- /* Set Toeplitz hash for an LRO skb */
+ /* Set Toeplitz hash for LRO skb */
bnx2x_set_skb_rxhash(bp, cqe, skb);
goto next_rx;
- } else { /* TPA_STOP */
+
+ } else {
+ u16 queue =
+ cqe->end_agg_cqe.queue_index;
DP(NETIF_MSG_RX_STATUS,
"calling tpa_stop on queue %d\n",
queue);
- if (!BNX2X_RX_SUM_FIX(cqe))
- BNX2X_ERR("STOP on none TCP "
- "data\n");
-
- /* This is a size of the linear data
- on this skb */
- len = le16_to_cpu(cqe->fast_path_cqe.
- len_on_bd);
- bnx2x_tpa_stop(bp, fp, queue, pad,
- len, cqe, comp_ring_cons);
+ bnx2x_tpa_stop(bp, fp, queue,
+ &cqe->end_agg_cqe,
+ comp_ring_cons);
#ifdef BNX2X_STOP_ON_ERROR
if (bp->panic)
return 0;
#endif
- bnx2x_update_sge_prod(fp,
- &cqe->fast_path_cqe);
+ bnx2x_update_sge_prod(fp, cqe_fp);
goto next_cqe;
}
}
-
- dma_sync_single_for_device(&bp->pdev->dev,
+ /* non TPA */
+ len = le16_to_cpu(cqe_fp->pkt_len);
+ pad = cqe_fp->placement_offset;
+ dma_sync_single_for_cpu(&bp->pdev->dev,
dma_unmap_addr(rx_buf, mapping),
- pad + RX_COPY_THRESH,
- DMA_FROM_DEVICE);
+ pad + RX_COPY_THRESH,
+ DMA_FROM_DEVICE);
prefetch(((char *)(skb)) + L1_CACHE_BYTES);
/* is this an error packet? */
@@ -640,8 +701,7 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
(len <= RX_COPY_THRESH)) {
struct sk_buff *new_skb;
- new_skb = netdev_alloc_skb(bp->dev,
- len + pad);
+ new_skb = netdev_alloc_skb(bp->dev, len + pad);
if (new_skb == NULL) {
DP(NETIF_MSG_RX_ERR,
"ERROR packet dropped "
@@ -687,6 +747,7 @@ reuse_rx:
skb_checksum_none_assert(skb);
if (bp->dev->features & NETIF_F_RXCSUM) {
+
if (likely(BNX2X_RX_CSUM_OK(cqe)))
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
@@ -696,10 +757,10 @@ reuse_rx:
skb_record_rx_queue(skb, fp->index);
- if (le16_to_cpu(cqe->fast_path_cqe.pars_flags.flags) &
- PARSING_FLAGS_VLAN)
+ if (le16_to_cpu(cqe_fp->pars_flags.flags) &
+ PARSING_FLAGS_VLAN)
__vlan_hwaccel_put_tag(skb,
- le16_to_cpu(cqe->fast_path_cqe.vlan_tag));
+ le16_to_cpu(cqe_fp->vlan_tag));
napi_gro_receive(&fp->napi, skb);
@@ -737,12 +798,7 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie)
{
struct bnx2x_fastpath *fp = fp_cookie;
struct bnx2x *bp = fp->bp;
-
- /* Return here if interrupt is disabled */
- if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
- DP(NETIF_MSG_INTR, "called but intr_sem not 0, returning\n");
- return IRQ_HANDLED;
- }
+ u8 cos;
DP(BNX2X_MSG_FP, "got an MSI-X interrupt on IDX:SB "
"[fp %d fw_sd %d igusb %d]\n",
@@ -756,7 +812,10 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie)
/* Handle Rx and Tx according to MSI-X vector */
prefetch(fp->rx_cons_sb);
- prefetch(fp->tx_cons_sb);
+
+ for_each_cos_in_tx_queue(fp, cos)
+ prefetch(fp->txdata[cos].tx_cons_sb);
+
prefetch(&fp->sb_running_index[SM_RX_ID]);
napi_schedule(&bnx2x_fp(bp, fp->index, napi));
@@ -897,15 +956,16 @@ void __bnx2x_link_report(struct bnx2x *bp)
netdev_err(bp->dev, "NIC Link is Down\n");
return;
} else {
+ const char *duplex;
+ const char *flow;
+
netif_carrier_on(bp->dev);
- netdev_info(bp->dev, "NIC Link is Up, ");
- pr_cont("%d Mbps ", cur_data.line_speed);
if (test_and_clear_bit(BNX2X_LINK_REPORT_FD,
&cur_data.link_report_flags))
- pr_cont("full duplex");
+ duplex = "full";
else
- pr_cont("half duplex");
+ duplex = "half";
/* Handle the FC at the end so that only these flags would be
* possibly set. This way we may easily check if there is no FC
@@ -914,24 +974,25 @@ void __bnx2x_link_report(struct bnx2x *bp)
if (cur_data.link_report_flags) {
if (test_bit(BNX2X_LINK_REPORT_RX_FC_ON,
&cur_data.link_report_flags)) {
- pr_cont(", receive ");
if (test_bit(BNX2X_LINK_REPORT_TX_FC_ON,
&cur_data.link_report_flags))
- pr_cont("& transmit ");
+ flow = "ON - receive & transmit";
+ else
+ flow = "ON - receive";
} else {
- pr_cont(", transmit ");
+ flow = "ON - transmit";
}
- pr_cont("flow control ON");
+ } else {
+ flow = "none";
}
- pr_cont("\n");
+ netdev_info(bp->dev, "NIC Link is Up, %d Mbps %s duplex, Flow control: %s\n",
+ cur_data.line_speed, duplex, flow);
}
}
void bnx2x_init_rx_rings(struct bnx2x *bp)
{
int func = BP_FUNC(bp);
- int max_agg_queues = CHIP_IS_E1(bp) ? ETH_MAX_AGGREGATION_QUEUES_E1 :
- ETH_MAX_AGGREGATION_QUEUES_E1H;
u16 ring_prod;
int i, j;
@@ -943,11 +1004,16 @@ void bnx2x_init_rx_rings(struct bnx2x *bp)
"mtu %d rx_buf_size %d\n", bp->dev->mtu, fp->rx_buf_size);
if (!fp->disable_tpa) {
- /* Fill the per-aggregation pool */
- for (i = 0; i < max_agg_queues; i++) {
- fp->tpa_pool[i].skb =
- netdev_alloc_skb(bp->dev, fp->rx_buf_size);
- if (!fp->tpa_pool[i].skb) {
+ /* Fill the per-aggregtion pool */
+ for (i = 0; i < MAX_AGG_QS(bp); i++) {
+ struct bnx2x_agg_info *tpa_info =
+ &fp->tpa_info[i];
+ struct sw_rx_bd *first_buf =
+ &tpa_info->first_buf;
+
+ first_buf->skb = netdev_alloc_skb(bp->dev,
+ fp->rx_buf_size);
+ if (!first_buf->skb) {
BNX2X_ERR("Failed to allocate TPA "
"skb pool for queue[%d] - "
"disabling TPA on this "
@@ -956,10 +1022,8 @@ void bnx2x_init_rx_rings(struct bnx2x *bp)
fp->disable_tpa = 1;
break;
}
- dma_unmap_addr_set((struct sw_rx_bd *)
- &bp->fp->tpa_pool[i],
- mapping, 0);
- fp->tpa_state[i] = BNX2X_TPA_STOP;
+ dma_unmap_addr_set(first_buf, mapping, 0);
+ tpa_info->tpa_state = BNX2X_TPA_STOP;
}
/* "next page" elements initialization */
@@ -975,13 +1039,13 @@ void bnx2x_init_rx_rings(struct bnx2x *bp)
if (bnx2x_alloc_rx_sge(bp, fp, ring_prod) < 0) {
BNX2X_ERR("was only able to allocate "
"%d rx sges\n", i);
- BNX2X_ERR("disabling TPA for"
- " queue[%d]\n", j);
+ BNX2X_ERR("disabling TPA for "
+ "queue[%d]\n", j);
/* Cleanup already allocated elements */
- bnx2x_free_rx_sge_range(bp,
- fp, ring_prod);
- bnx2x_free_tpa_pool(bp,
- fp, max_agg_queues);
+ bnx2x_free_rx_sge_range(bp, fp,
+ ring_prod);
+ bnx2x_free_tpa_pool(bp, fp,
+ MAX_AGG_QS(bp));
fp->disable_tpa = 1;
ring_prod = 0;
break;
@@ -1009,7 +1073,7 @@ void bnx2x_init_rx_rings(struct bnx2x *bp)
if (j != 0)
continue;
- if (!CHIP_IS_E2(bp)) {
+ if (CHIP_IS_E1(bp)) {
REG_WR(bp, BAR_USTRORM_INTMEM +
USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(func),
U64_LO(fp->rx_comp_mapping));
@@ -1023,17 +1087,22 @@ void bnx2x_init_rx_rings(struct bnx2x *bp)
static void bnx2x_free_tx_skbs(struct bnx2x *bp)
{
int i;
+ u8 cos;
for_each_tx_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
+ for_each_cos_in_tx_queue(fp, cos) {
+ struct bnx2x_fp_txdata *txdata = &fp->txdata[cos];
- u16 bd_cons = fp->tx_bd_cons;
- u16 sw_prod = fp->tx_pkt_prod;
- u16 sw_cons = fp->tx_pkt_cons;
+ u16 bd_cons = txdata->tx_bd_cons;
+ u16 sw_prod = txdata->tx_pkt_prod;
+ u16 sw_cons = txdata->tx_pkt_cons;
- while (sw_cons != sw_prod) {
- bd_cons = bnx2x_free_tx_pkt(bp, fp, TX_BD(sw_cons));
- sw_cons++;
+ while (sw_cons != sw_prod) {
+ bd_cons = bnx2x_free_tx_pkt(bp, txdata,
+ TX_BD(sw_cons));
+ sw_cons++;
+ }
}
}
}
@@ -1053,7 +1122,6 @@ static void bnx2x_free_rx_bds(struct bnx2x_fastpath *fp)
if (skb == NULL)
continue;
-
dma_unmap_single(&bp->pdev->dev,
dma_unmap_addr(rx_buf, mapping),
fp->rx_buf_size, DMA_FROM_DEVICE);
@@ -1073,9 +1141,7 @@ static void bnx2x_free_rx_skbs(struct bnx2x *bp)
bnx2x_free_rx_bds(fp);
if (!fp->disable_tpa)
- bnx2x_free_tpa_pool(bp, fp, CHIP_IS_E1(bp) ?
- ETH_MAX_AGGREGATION_QUEUES_E1 :
- ETH_MAX_AGGREGATION_QUEUES_E1H);
+ bnx2x_free_tpa_pool(bp, fp, MAX_AGG_QS(bp));
}
}
@@ -1102,30 +1168,43 @@ void bnx2x_update_max_mf_config(struct bnx2x *bp, u32 value)
}
}
-static void bnx2x_free_msix_irqs(struct bnx2x *bp)
+/**
+ * bnx2x_free_msix_irqs - free previously requested MSI-X IRQ vectors
+ *
+ * @bp: driver handle
+ * @nvecs: number of vectors to be released
+ */
+static void bnx2x_free_msix_irqs(struct bnx2x *bp, int nvecs)
{
- int i, offset = 1;
+ int i, offset = 0;
- free_irq(bp->msix_table[0].vector, bp->dev);
+ if (nvecs == offset)
+ return;
+ free_irq(bp->msix_table[offset].vector, bp->dev);
DP(NETIF_MSG_IFDOWN, "released sp irq (%d)\n",
- bp->msix_table[0].vector);
-
+ bp->msix_table[offset].vector);
+ offset++;
#ifdef BCM_CNIC
+ if (nvecs == offset)
+ return;
offset++;
#endif
+
for_each_eth_queue(bp, i) {
- DP(NETIF_MSG_IFDOWN, "about to release fp #%d->%d irq "
- "state %x\n", i, bp->msix_table[i + offset].vector,
- bnx2x_fp(bp, i, state));
+ if (nvecs == offset)
+ return;
+ DP(NETIF_MSG_IFDOWN, "about to release fp #%d->%d "
+ "irq\n", i, bp->msix_table[offset].vector);
- free_irq(bp->msix_table[i + offset].vector, &bp->fp[i]);
+ free_irq(bp->msix_table[offset++].vector, &bp->fp[i]);
}
}
void bnx2x_free_irq(struct bnx2x *bp)
{
if (bp->flags & USING_MSIX_FLAG)
- bnx2x_free_msix_irqs(bp);
+ bnx2x_free_msix_irqs(bp, BNX2X_NUM_ETH_QUEUES(bp) +
+ CNIC_PRESENT + 1);
else if (bp->flags & USING_MSI_FLAG)
free_irq(bp->pdev->irq, bp->dev);
else
@@ -1147,6 +1226,7 @@ int bnx2x_enable_msix(struct bnx2x *bp)
bp->msix_table[msix_vec].entry, bp->msix_table[msix_vec].entry);
msix_vec++;
#endif
+ /* We need separate vectors for ETH queues only (not FCoE) */
for_each_eth_queue(bp, i) {
bp->msix_table[msix_vec].entry = msix_vec;
DP(NETIF_MSG_IFUP, "msix_table[%d].entry = %d "
@@ -1154,7 +1234,7 @@ int bnx2x_enable_msix(struct bnx2x *bp)
msix_vec++;
}
- req_cnt = BNX2X_NUM_ETH_QUEUES(bp) + CNIC_CONTEXT_USE + 1;
+ req_cnt = BNX2X_NUM_ETH_QUEUES(bp) + CNIC_PRESENT + 1;
rc = pci_enable_msix(bp->pdev, &bp->msix_table[0], req_cnt);
@@ -1198,9 +1278,10 @@ int bnx2x_enable_msix(struct bnx2x *bp)
static int bnx2x_req_msix_irqs(struct bnx2x *bp)
{
- int i, rc, offset = 1;
+ int i, rc, offset = 0;
- rc = request_irq(bp->msix_table[0].vector, bnx2x_msix_sp_int, 0,
+ rc = request_irq(bp->msix_table[offset++].vector,
+ bnx2x_msix_sp_int, 0,
bp->dev->name, bp->dev);
if (rc) {
BNX2X_ERR("request sp irq failed\n");
@@ -1218,17 +1299,17 @@ static int bnx2x_req_msix_irqs(struct bnx2x *bp)
rc = request_irq(bp->msix_table[offset].vector,
bnx2x_msix_fp_int, 0, fp->name, fp);
if (rc) {
- BNX2X_ERR("request fp #%d irq failed rc %d\n", i, rc);
- bnx2x_free_msix_irqs(bp);
+ BNX2X_ERR("request fp #%d irq (%d) failed rc %d\n", i,
+ bp->msix_table[offset].vector, rc);
+ bnx2x_free_msix_irqs(bp, offset);
return -EBUSY;
}
offset++;
- fp->state = BNX2X_FP_STATE_IRQ;
}
i = BNX2X_NUM_ETH_QUEUES(bp);
- offset = 1 + CNIC_CONTEXT_USE;
+ offset = 1 + CNIC_PRESENT;
netdev_info(bp->dev, "using MSI-X IRQs: sp %d fp[%d] %d"
" ... fp[%d] %d\n",
bp->msix_table[0].vector,
@@ -1264,42 +1345,56 @@ static int bnx2x_req_irq(struct bnx2x *bp)
rc = request_irq(bp->pdev->irq, bnx2x_interrupt, flags,
bp->dev->name, bp->dev);
- if (!rc)
- bnx2x_fp(bp, 0, state) = BNX2X_FP_STATE_IRQ;
-
return rc;
}
-static void bnx2x_napi_enable(struct bnx2x *bp)
+static inline int bnx2x_setup_irqs(struct bnx2x *bp)
+{
+ int rc = 0;
+ if (bp->flags & USING_MSIX_FLAG) {
+ rc = bnx2x_req_msix_irqs(bp);
+ if (rc)
+ return rc;
+ } else {
+ bnx2x_ack_int(bp);
+ rc = bnx2x_req_irq(bp);
+ if (rc) {
+ BNX2X_ERR("IRQ request failed rc %d, aborting\n", rc);
+ return rc;
+ }
+ if (bp->flags & USING_MSI_FLAG) {
+ bp->dev->irq = bp->pdev->irq;
+ netdev_info(bp->dev, "using MSI IRQ %d\n",
+ bp->pdev->irq);
+ }
+ }
+
+ return 0;
+}
+
+static inline void bnx2x_napi_enable(struct bnx2x *bp)
{
int i;
- for_each_napi_queue(bp, i)
+ for_each_rx_queue(bp, i)
napi_enable(&bnx2x_fp(bp, i, napi));
}
-static void bnx2x_napi_disable(struct bnx2x *bp)
+static inline void bnx2x_napi_disable(struct bnx2x *bp)
{
int i;
- for_each_napi_queue(bp, i)
+ for_each_rx_queue(bp, i)
napi_disable(&bnx2x_fp(bp, i, napi));
}
void bnx2x_netif_start(struct bnx2x *bp)
{
- int intr_sem;
-
- intr_sem = atomic_dec_and_test(&bp->intr_sem);
- smp_wmb(); /* Ensure that bp->intr_sem update is SMP-safe */
-
- if (intr_sem) {
- if (netif_running(bp->dev)) {
- bnx2x_napi_enable(bp);
- bnx2x_int_enable(bp);
- if (bp->state == BNX2X_STATE_OPEN)
- netif_tx_wake_all_queues(bp->dev);
- }
+ if (netif_running(bp->dev)) {
+ bnx2x_napi_enable(bp);
+ bnx2x_int_enable(bp);
+ if (bp->state == BNX2X_STATE_OPEN)
+ netif_tx_wake_all_queues(bp->dev);
}
}
@@ -1307,16 +1402,14 @@ void bnx2x_netif_stop(struct bnx2x *bp, int disable_hw)
{
bnx2x_int_disable_sync(bp, disable_hw);
bnx2x_napi_disable(bp);
- netif_tx_disable(bp->dev);
}
u16 bnx2x_select_queue(struct net_device *dev, struct sk_buff *skb)
{
-#ifdef BCM_CNIC
struct bnx2x *bp = netdev_priv(dev);
- if (NO_FCOE(bp))
- return skb_tx_hash(dev, skb);
- else {
+
+#ifdef BCM_CNIC
+ if (!NO_FCOE(bp)) {
struct ethhdr *hdr = (struct ethhdr *)skb->data;
u16 ether_type = ntohs(hdr->h_proto);
@@ -1330,13 +1423,11 @@ u16 bnx2x_select_queue(struct net_device *dev, struct sk_buff *skb)
/* If ethertype is FCoE or FIP - use FCoE ring */
if ((ether_type == ETH_P_FCOE) || (ether_type == ETH_P_FIP))
- return bnx2x_fcoe(bp, index);
+ return bnx2x_fcoe_tx(bp, txq_index);
}
#endif
- /* Select a none-FCoE queue: if FCoE is enabled, exclude FCoE L2 ring
- */
- return __skb_tx_hash(dev, skb,
- dev->real_num_tx_queues - FCOE_CONTEXT_USE);
+ /* select a non-FCoE queue */
+ return __skb_tx_hash(dev, skb, BNX2X_NUM_ETH_QUEUES(bp));
}
void bnx2x_set_num_queues(struct bnx2x *bp)
@@ -1355,40 +1446,60 @@ void bnx2x_set_num_queues(struct bnx2x *bp)
}
/* Add special queues */
- bp->num_queues += NONE_ETH_CONTEXT_USE;
+ bp->num_queues += NON_ETH_CONTEXT_USE;
}
-#ifdef BCM_CNIC
-static inline void bnx2x_set_fcoe_eth_macs(struct bnx2x *bp)
+/**
+ * bnx2x_set_real_num_queues - configure netdev->real_num_[tx,rx]_queues
+ *
+ * @bp: Driver handle
+ *
+ * We currently support for at most 16 Tx queues for each CoS thus we will
+ * allocate a multiple of 16 for ETH L2 rings according to the value of the
+ * bp->max_cos.
+ *
+ * If there is an FCoE L2 queue the appropriate Tx queue will have the next
+ * index after all ETH L2 indices.
+ *
+ * If the actual number of Tx queues (for each CoS) is less than 16 then there
+ * will be the holes at the end of each group of 16 ETh L2 indices (0..15,
+ * 16..31,...) with indicies that are not coupled with any real Tx queue.
+ *
+ * The proper configuration of skb->queue_mapping is handled by
+ * bnx2x_select_queue() and __skb_tx_hash().
+ *
+ * bnx2x_setup_tc() takes care of the proper TC mappings so that __skb_tx_hash()
+ * will return a proper Tx index if TC is enabled (netdev->num_tc > 0).
+ */
+static inline int bnx2x_set_real_num_queues(struct bnx2x *bp)
{
+ int rc, tx, rx;
+
+ tx = MAX_TXQS_PER_COS * bp->max_cos;
+ rx = BNX2X_NUM_ETH_QUEUES(bp);
+
+/* account for fcoe queue */
+#ifdef BCM_CNIC
if (!NO_FCOE(bp)) {
- if (!IS_MF_SD(bp))
- bnx2x_set_fip_eth_mac_addr(bp, 1);
- bnx2x_set_all_enode_macs(bp, 1);
- bp->flags |= FCOE_MACS_SET;
+ rx += FCOE_PRESENT;
+ tx += FCOE_PRESENT;
}
-}
#endif
-static void bnx2x_release_firmware(struct bnx2x *bp)
-{
- kfree(bp->init_ops_offsets);
- kfree(bp->init_ops);
- kfree(bp->init_data);
- release_firmware(bp->firmware);
-}
-
-static inline int bnx2x_set_real_num_queues(struct bnx2x *bp)
-{
- int rc, num = bp->num_queues;
+ rc = netif_set_real_num_tx_queues(bp->dev, tx);
+ if (rc) {
+ BNX2X_ERR("Failed to set real number of Tx queues: %d\n", rc);
+ return rc;
+ }
+ rc = netif_set_real_num_rx_queues(bp->dev, rx);
+ if (rc) {
+ BNX2X_ERR("Failed to set real number of Rx queues: %d\n", rc);
+ return rc;
+ }
-#ifdef BCM_CNIC
- if (NO_FCOE(bp))
- num -= FCOE_CONTEXT_USE;
+ DP(NETIF_MSG_DRV, "Setting real num queues to (tx, rx) (%d, %d)\n",
+ tx, rx);
-#endif
- netif_set_real_num_tx_queues(bp->dev, num);
- rc = netif_set_real_num_rx_queues(bp->dev, num);
return rc;
}
@@ -1409,27 +1520,198 @@ static inline void bnx2x_set_rx_buf_size(struct bnx2x *bp)
*/
fp->rx_buf_size =
BNX2X_FCOE_MINI_JUMBO_MTU + ETH_OVREHEAD +
- BNX2X_RX_ALIGN + IP_HEADER_ALIGNMENT_PADDING;
+ BNX2X_FW_RX_ALIGN + IP_HEADER_ALIGNMENT_PADDING;
else
fp->rx_buf_size =
- bp->dev->mtu + ETH_OVREHEAD + BNX2X_RX_ALIGN +
- IP_HEADER_ALIGNMENT_PADDING;
+ bp->dev->mtu + ETH_OVREHEAD +
+ BNX2X_FW_RX_ALIGN + IP_HEADER_ALIGNMENT_PADDING;
+ }
+}
+
+static inline int bnx2x_init_rss_pf(struct bnx2x *bp)
+{
+ int i;
+ u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
+ u8 num_eth_queues = BNX2X_NUM_ETH_QUEUES(bp);
+
+ /*
+ * Prepare the inital contents fo the indirection table if RSS is
+ * enabled
+ */
+ if (bp->multi_mode != ETH_RSS_MODE_DISABLED) {
+ for (i = 0; i < sizeof(ind_table); i++)
+ ind_table[i] =
+ bp->fp->cl_id + (i % num_eth_queues);
+ }
+
+ /*
+ * For 57710 and 57711 SEARCHER configuration (rss_keys) is
+ * per-port, so if explicit configuration is needed , do it only
+ * for a PMF.
+ *
+ * For 57712 and newer on the other hand it's a per-function
+ * configuration.
+ */
+ return bnx2x_config_rss_pf(bp, ind_table,
+ bp->port.pmf || !CHIP_IS_E1x(bp));
+}
+
+int bnx2x_config_rss_pf(struct bnx2x *bp, u8 *ind_table, bool config_hash)
+{
+ struct bnx2x_config_rss_params params = {0};
+ int i;
+
+ /* Although RSS is meaningless when there is a single HW queue we
+ * still need it enabled in order to have HW Rx hash generated.
+ *
+ * if (!is_eth_multi(bp))
+ * bp->multi_mode = ETH_RSS_MODE_DISABLED;
+ */
+
+ params.rss_obj = &bp->rss_conf_obj;
+
+ __set_bit(RAMROD_COMP_WAIT, &params.ramrod_flags);
+
+ /* RSS mode */
+ switch (bp->multi_mode) {
+ case ETH_RSS_MODE_DISABLED:
+ __set_bit(BNX2X_RSS_MODE_DISABLED, &params.rss_flags);
+ break;
+ case ETH_RSS_MODE_REGULAR:
+ __set_bit(BNX2X_RSS_MODE_REGULAR, &params.rss_flags);
+ break;
+ case ETH_RSS_MODE_VLAN_PRI:
+ __set_bit(BNX2X_RSS_MODE_VLAN_PRI, &params.rss_flags);
+ break;
+ case ETH_RSS_MODE_E1HOV_PRI:
+ __set_bit(BNX2X_RSS_MODE_E1HOV_PRI, &params.rss_flags);
+ break;
+ case ETH_RSS_MODE_IP_DSCP:
+ __set_bit(BNX2X_RSS_MODE_IP_DSCP, &params.rss_flags);
+ break;
+ default:
+ BNX2X_ERR("Unknown multi_mode: %d\n", bp->multi_mode);
+ return -EINVAL;
+ }
+
+ /* If RSS is enabled */
+ if (bp->multi_mode != ETH_RSS_MODE_DISABLED) {
+ /* RSS configuration */
+ __set_bit(BNX2X_RSS_IPV4, &params.rss_flags);
+ __set_bit(BNX2X_RSS_IPV4_TCP, &params.rss_flags);
+ __set_bit(BNX2X_RSS_IPV6, &params.rss_flags);
+ __set_bit(BNX2X_RSS_IPV6_TCP, &params.rss_flags);
+
+ /* Hash bits */
+ params.rss_result_mask = MULTI_MASK;
+
+ memcpy(params.ind_table, ind_table, sizeof(params.ind_table));
+
+ if (config_hash) {
+ /* RSS keys */
+ for (i = 0; i < sizeof(params.rss_key) / 4; i++)
+ params.rss_key[i] = random32();
+
+ __set_bit(BNX2X_RSS_SET_SRCH, &params.rss_flags);
+ }
+ }
+
+ return bnx2x_config_rss(bp, &params);
+}
+
+static inline int bnx2x_init_hw(struct bnx2x *bp, u32 load_code)
+{
+ struct bnx2x_func_state_params func_params = {0};
+
+ /* Prepare parameters for function state transitions */
+ __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags);
+
+ func_params.f_obj = &bp->func_obj;
+ func_params.cmd = BNX2X_F_CMD_HW_INIT;
+
+ func_params.params.hw_init.load_phase = load_code;
+
+ return bnx2x_func_state_change(bp, &func_params);
+}
+
+/*
+ * Cleans the object that have internal lists without sending
+ * ramrods. Should be run when interrutps are disabled.
+ */
+static void bnx2x_squeeze_objects(struct bnx2x *bp)
+{
+ int rc;
+ unsigned long ramrod_flags = 0, vlan_mac_flags = 0;
+ struct bnx2x_mcast_ramrod_params rparam = {0};
+ struct bnx2x_vlan_mac_obj *mac_obj = &bp->fp->mac_obj;
+
+ /***************** Cleanup MACs' object first *************************/
+
+ /* Wait for completion of requested */
+ __set_bit(RAMROD_COMP_WAIT, &ramrod_flags);
+ /* Perform a dry cleanup */
+ __set_bit(RAMROD_DRV_CLR_ONLY, &ramrod_flags);
+
+ /* Clean ETH primary MAC */
+ __set_bit(BNX2X_ETH_MAC, &vlan_mac_flags);
+ rc = mac_obj->delete_all(bp, &bp->fp->mac_obj, &vlan_mac_flags,
+ &ramrod_flags);
+ if (rc != 0)
+ BNX2X_ERR("Failed to clean ETH MACs: %d\n", rc);
+
+ /* Cleanup UC list */
+ vlan_mac_flags = 0;
+ __set_bit(BNX2X_UC_LIST_MAC, &vlan_mac_flags);
+ rc = mac_obj->delete_all(bp, mac_obj, &vlan_mac_flags,
+ &ramrod_flags);
+ if (rc != 0)
+ BNX2X_ERR("Failed to clean UC list MACs: %d\n", rc);
+
+ /***************** Now clean mcast object *****************************/
+ rparam.mcast_obj = &bp->mcast_obj;
+ __set_bit(RAMROD_DRV_CLR_ONLY, &rparam.ramrod_flags);
+
+ /* Add a DEL command... */
+ rc = bnx2x_config_mcast(bp, &rparam, BNX2X_MCAST_CMD_DEL);
+ if (rc < 0)
+ BNX2X_ERR("Failed to add a new DEL command to a multi-cast "
+ "object: %d\n", rc);
+
+ /* ...and wait until all pending commands are cleared */
+ rc = bnx2x_config_mcast(bp, &rparam, BNX2X_MCAST_CMD_CONT);
+ while (rc != 0) {
+ if (rc < 0) {
+ BNX2X_ERR("Failed to clean multi-cast object: %d\n",
+ rc);
+ return;
+ }
+
+ rc = bnx2x_config_mcast(bp, &rparam, BNX2X_MCAST_CMD_CONT);
}
}
+#ifndef BNX2X_STOP_ON_ERROR
+#define LOAD_ERROR_EXIT(bp, label) \
+ do { \
+ (bp)->state = BNX2X_STATE_ERROR; \
+ goto label; \
+ } while (0)
+#else
+#define LOAD_ERROR_EXIT(bp, label) \
+ do { \
+ (bp)->state = BNX2X_STATE_ERROR; \
+ (bp)->panic = 1; \
+ return -EBUSY; \
+ } while (0)
+#endif
+
/* must be called with rtnl_lock */
int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
{
+ int port = BP_PORT(bp);
u32 load_code;
int i, rc;
- /* Set init arrays */
- rc = bnx2x_init_firmware(bp);
- if (rc) {
- BNX2X_ERR("Error loading firmware\n");
- return rc;
- }
-
#ifdef BNX2X_STOP_ON_ERROR
if (unlikely(bp->panic))
return -EPERM;
@@ -1447,24 +1729,18 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
/* must be called before memory allocation and HW init */
bnx2x_ilt_set_info(bp);
- /* zero fastpath structures preserving invariants like napi which are
- * allocated only once
+ /*
+ * Zero fastpath structures preserving invariants like napi, which are
+ * allocated only once, fp index, max_cos, bp pointer.
+ * Also set fp->disable_tpa.
*/
for_each_queue(bp, i)
bnx2x_bz_fp(bp, i);
+
/* Set the receive queues buffer size */
bnx2x_set_rx_buf_size(bp);
- for_each_queue(bp, i)
- bnx2x_fp(bp, i, disable_tpa) =
- ((bp->flags & TPA_ENABLE_FLAG) == 0);
-
-#ifdef BCM_CNIC
- /* We don't want TPA on FCoE L2 ring */
- bnx2x_fcoe(bp, disable_tpa) = 1;
-#endif
-
if (bnx2x_alloc_mem(bp))
return -ENOMEM;
@@ -1475,31 +1751,36 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
rc = bnx2x_set_real_num_queues(bp);
if (rc) {
BNX2X_ERR("Unable to set real_num_queues\n");
- goto load_error0;
+ LOAD_ERROR_EXIT(bp, load_error0);
}
+ /* configure multi cos mappings in kernel.
+ * this configuration may be overriden by a multi class queue discipline
+ * or by a dcbx negotiation result.
+ */
+ bnx2x_setup_tc(bp->dev, bp->max_cos);
+
bnx2x_napi_enable(bp);
/* Send LOAD_REQUEST command to MCP
- Returns the type of LOAD command:
- if it is the first port to be initialized
- common blocks should be initialized, otherwise - not
- */
+ * Returns the type of LOAD command:
+ * if it is the first port to be initialized
+ * common blocks should be initialized, otherwise - not
+ */
if (!BP_NOMCP(bp)) {
load_code = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ, 0);
if (!load_code) {
BNX2X_ERR("MCP response failure, aborting\n");
rc = -EBUSY;
- goto load_error1;
+ LOAD_ERROR_EXIT(bp, load_error1);
}
if (load_code == FW_MSG_CODE_DRV_LOAD_REFUSED) {
rc = -EBUSY; /* other port in diagnostic mode */
- goto load_error1;
+ LOAD_ERROR_EXIT(bp, load_error1);
}
} else {
int path = BP_PATH(bp);
- int port = BP_PORT(bp);
DP(NETIF_MSG_IFUP, "NO MCP - load counts[%d] %d, %d, %d\n",
path, load_count[path][0], load_count[path][1],
@@ -1519,36 +1800,60 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
if ((load_code == FW_MSG_CODE_DRV_LOAD_COMMON) ||
(load_code == FW_MSG_CODE_DRV_LOAD_COMMON_CHIP) ||
- (load_code == FW_MSG_CODE_DRV_LOAD_PORT))
+ (load_code == FW_MSG_CODE_DRV_LOAD_PORT)) {
bp->port.pmf = 1;
- else
+ /*
+ * We need the barrier to ensure the ordering between the
+ * writing to bp->port.pmf here and reading it from the
+ * bnx2x_periodic_task().
+ */
+ smp_mb();
+ queue_delayed_work(bnx2x_wq, &bp->period_task, 0);
+ } else
bp->port.pmf = 0;
+
DP(NETIF_MSG_LINK, "pmf %d\n", bp->port.pmf);
+ /* Init Function state controlling object */
+ bnx2x__init_func_obj(bp);
+
/* Initialize HW */
rc = bnx2x_init_hw(bp, load_code);
if (rc) {
BNX2X_ERR("HW init failed, aborting\n");
bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_DONE, 0);
- goto load_error2;
+ LOAD_ERROR_EXIT(bp, load_error2);
}
/* Connect to IRQs */
rc = bnx2x_setup_irqs(bp);
if (rc) {
bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_DONE, 0);
- goto load_error2;
+ LOAD_ERROR_EXIT(bp, load_error2);
}
/* Setup NIC internals and enable interrupts */
bnx2x_nic_init(bp, load_code);
+ /* Init per-function objects */
+ bnx2x_init_bp_objs(bp);
+
if (((load_code == FW_MSG_CODE_DRV_LOAD_COMMON) ||
(load_code == FW_MSG_CODE_DRV_LOAD_COMMON_CHIP)) &&
- (bp->common.shmem2_base))
- SHMEM2_WR(bp, dcc_support,
- (SHMEM_DCC_SUPPORT_DISABLE_ENABLE_PF_TLV |
- SHMEM_DCC_SUPPORT_BANDWIDTH_ALLOCATION_TLV));
+ (bp->common.shmem2_base)) {
+ if (SHMEM2_HAS(bp, dcc_support))
+ SHMEM2_WR(bp, dcc_support,
+ (SHMEM_DCC_SUPPORT_DISABLE_ENABLE_PF_TLV |
+ SHMEM_DCC_SUPPORT_BANDWIDTH_ALLOCATION_TLV));
+ }
+
+ bp->state = BNX2X_STATE_OPENING_WAIT4_PORT;
+ rc = bnx2x_func_start(bp);
+ if (rc) {
+ BNX2X_ERR("Function start failed!\n");
+ bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_DONE, 0);
+ LOAD_ERROR_EXIT(bp, load_error3);
+ }
/* Send LOAD_DONE command to MCP */
if (!BP_NOMCP(bp)) {
@@ -1556,74 +1861,38 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
if (!load_code) {
BNX2X_ERR("MCP response failure, aborting\n");
rc = -EBUSY;
- goto load_error3;
+ LOAD_ERROR_EXIT(bp, load_error3);
}
}
- bnx2x_dcbx_init(bp);
-
- bp->state = BNX2X_STATE_OPENING_WAIT4_PORT;
-
- rc = bnx2x_func_start(bp);
- if (rc) {
- BNX2X_ERR("Function start failed!\n");
-#ifndef BNX2X_STOP_ON_ERROR
- goto load_error3;
-#else
- bp->panic = 1;
- return -EBUSY;
-#endif
- }
-
- rc = bnx2x_setup_client(bp, &bp->fp[0], 1 /* Leading */);
+ rc = bnx2x_setup_leading(bp);
if (rc) {
BNX2X_ERR("Setup leading failed!\n");
-#ifndef BNX2X_STOP_ON_ERROR
- goto load_error3;
-#else
- bp->panic = 1;
- return -EBUSY;
-#endif
- }
-
- if (!CHIP_IS_E1(bp) &&
- (bp->mf_config[BP_VN(bp)] & FUNC_MF_CFG_FUNC_DISABLED)) {
- DP(NETIF_MSG_IFUP, "mf_cfg function disabled\n");
- bp->flags |= MF_FUNC_DIS;
+ LOAD_ERROR_EXIT(bp, load_error3);
}
#ifdef BCM_CNIC
/* Enable Timer scan */
- REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + BP_PORT(bp)*4, 1);
+ REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + port*4, 1);
#endif
for_each_nondefault_queue(bp, i) {
- rc = bnx2x_setup_client(bp, &bp->fp[i], 0);
+ rc = bnx2x_setup_queue(bp, &bp->fp[i], 0);
if (rc)
-#ifdef BCM_CNIC
- goto load_error4;
-#else
- goto load_error3;
-#endif
+ LOAD_ERROR_EXIT(bp, load_error4);
}
+ rc = bnx2x_init_rss_pf(bp);
+ if (rc)
+ LOAD_ERROR_EXIT(bp, load_error4);
+
/* Now when Clients are configured we are ready to work */
bp->state = BNX2X_STATE_OPEN;
-#ifdef BCM_CNIC
- bnx2x_set_fcoe_eth_macs(bp);
-#endif
-
- bnx2x_set_eth_mac(bp, 1);
-
- /* Clear MC configuration */
- if (CHIP_IS_E1(bp))
- bnx2x_invalidate_e1_mc_list(bp);
- else
- bnx2x_invalidate_e1h_mc_list(bp);
-
- /* Clear UC lists configuration */
- bnx2x_invalidate_uc_list(bp);
+ /* Configure a ucast MAC */
+ rc = bnx2x_set_eth_mac(bp, true);
+ if (rc)
+ LOAD_ERROR_EXIT(bp, load_error4);
if (bp->pending_max) {
bnx2x_update_max_mf_config(bp, bp->pending_max);
@@ -1633,15 +1902,18 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
if (bp->port.pmf)
bnx2x_initial_phy_init(bp, load_mode);
- /* Initialize Rx filtering */
+ /* Start fast path */
+
+ /* Initialize Rx filter. */
+ netif_addr_lock_bh(bp->dev);
bnx2x_set_rx_mode(bp->dev);
+ netif_addr_unlock_bh(bp->dev);
- /* Start fast path */
+ /* Start the Tx */
switch (load_mode) {
case LOAD_NORMAL:
/* Tx queue should be only reenabled */
netif_tx_wake_all_queues(bp->dev);
- /* Initialize the receive filter. */
break;
case LOAD_OPEN:
@@ -1670,18 +1942,28 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
#endif
bnx2x_inc_load_cnt(bp);
- bnx2x_release_firmware(bp);
+ /* Wait for all pending SP commands to complete */
+ if (!bnx2x_wait_sp_comp(bp, ~0x0UL)) {
+ BNX2X_ERR("Timeout waiting for SP elements to complete\n");
+ bnx2x_nic_unload(bp, UNLOAD_CLOSE);
+ return -EBUSY;
+ }
+ bnx2x_dcbx_init(bp);
return 0;
-#ifdef BCM_CNIC
+#ifndef BNX2X_STOP_ON_ERROR
load_error4:
+#ifdef BCM_CNIC
/* Disable Timer scan */
- REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + BP_PORT(bp)*4, 0);
+ REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + port*4, 0);
#endif
load_error3:
bnx2x_int_disable_sync(bp, 1);
+ /* Clean queueable objects */
+ bnx2x_squeeze_objects(bp);
+
/* Free SKBs, SGEs, TPA pool and driver internals */
bnx2x_free_skbs(bp);
for_each_rx_queue(bp, i)
@@ -1701,42 +1983,58 @@ load_error1:
load_error0:
bnx2x_free_mem(bp);
- bnx2x_release_firmware(bp);
-
return rc;
+#endif /* ! BNX2X_STOP_ON_ERROR */
}
/* must be called with rtnl_lock */
int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
{
int i;
-
- if (bp->state == BNX2X_STATE_CLOSED) {
- /* Interface has been removed - nothing to recover */
+ bool global = false;
+
+ if ((bp->state == BNX2X_STATE_CLOSED) ||
+ (bp->state == BNX2X_STATE_ERROR)) {
+ /* We can get here if the driver has been unloaded
+ * during parity error recovery and is either waiting for a
+ * leader to complete or for other functions to unload and
+ * then ifdown has been issued. In this case we want to
+ * unload and let other functions to complete a recovery
+ * process.
+ */
bp->recovery_state = BNX2X_RECOVERY_DONE;
bp->is_leader = 0;
- bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_RESERVED_08);
- smp_wmb();
+ bnx2x_release_leader_lock(bp);
+ smp_mb();
+
+ DP(NETIF_MSG_HW, "Releasing a leadership...\n");
return -EINVAL;
}
+ /*
+ * It's important to set the bp->state to the value different from
+ * BNX2X_STATE_OPEN and only then stop the Tx. Otherwise bnx2x_tx_int()
+ * may restart the Tx from the NAPI context (see bnx2x_tx_int()).
+ */
+ bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
+ smp_mb();
+
+ /* Stop Tx */
+ bnx2x_tx_disable(bp);
+
#ifdef BCM_CNIC
bnx2x_cnic_notify(bp, CNIC_CTL_STOP_CMD);
#endif
- bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
- /* Set "drop all" */
bp->rx_mode = BNX2X_RX_MODE_NONE;
- bnx2x_set_storm_rx_mode(bp);
-
- /* Stop Tx */
- bnx2x_tx_disable(bp);
del_timer_sync(&bp->timer);
- SHMEM_WR(bp, func_mb[BP_FW_MB_IDX(bp)].drv_pulse_mb,
- (DRV_PULSE_ALWAYS_ALIVE | bp->fw_drv_pulse_wr_seq));
+ /* Set ALWAYS_ALIVE bit in shmem */
+ bp->fw_drv_pulse_wr_seq |= DRV_PULSE_ALWAYS_ALIVE;
+
+ bnx2x_drv_pulse(bp);
bnx2x_stats_handle(bp, STATS_EVENT_STOP);
@@ -1744,13 +2042,38 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
if (unload_mode != UNLOAD_RECOVERY)
bnx2x_chip_cleanup(bp, unload_mode);
else {
- /* Disable HW interrupts, NAPI and Tx */
+ /* Send the UNLOAD_REQUEST to the MCP */
+ bnx2x_send_unload_req(bp, unload_mode);
+
+ /*
+ * Prevent transactions to host from the functions on the
+ * engine that doesn't reset global blocks in case of global
+ * attention once gloabl blocks are reset and gates are opened
+ * (the engine which leader will perform the recovery
+ * last).
+ */
+ if (!CHIP_IS_E1x(bp))
+ bnx2x_pf_disable(bp);
+
+ /* Disable HW interrupts, NAPI */
bnx2x_netif_stop(bp, 1);
/* Release IRQs */
bnx2x_free_irq(bp);
+
+ /* Report UNLOAD_DONE to MCP */
+ bnx2x_send_unload_done(bp);
}
+ /*
+ * At this stage no more interrupts will arrive so we may safly clean
+ * the queueable objects here in case they failed to get cleaned so far.
+ */
+ bnx2x_squeeze_objects(bp);
+
+ /* There should be no more pending SP commands at this stage */
+ bp->sp_state = 0;
+
bp->port.pmf = 0;
/* Free SKBs, SGEs, TPA pool and driver internals */
@@ -1762,17 +2085,24 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
bp->state = BNX2X_STATE_CLOSED;
+ /* Check if there are pending parity attentions. If there are - set
+ * RECOVERY_IN_PROGRESS.
+ */
+ if (bnx2x_chk_parity_attn(bp, &global, false)) {
+ bnx2x_set_reset_in_progress(bp);
+
+ /* Set RESET_IS_GLOBAL if needed */
+ if (global)
+ bnx2x_set_reset_global(bp);
+ }
+
+
/* The last driver must disable a "close the gate" if there is no
* parity attention or "process kill" pending.
*/
- if ((!bnx2x_dec_load_cnt(bp)) && (!bnx2x_chk_parity_attn(bp)) &&
- bnx2x_reset_is_done(bp))
+ if (!bnx2x_dec_load_cnt(bp) && bnx2x_reset_is_done(bp, BP_PATH(bp)))
bnx2x_disable_close_the_gate(bp);
- /* Reset MCP mail box sequence if there is on going recovery */
- if (unload_mode == UNLOAD_RECOVERY)
- bp->fw_seq = 0;
-
return 0;
}
@@ -1834,6 +2164,7 @@ int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state)
int bnx2x_poll(struct napi_struct *napi, int budget)
{
int work_done = 0;
+ u8 cos;
struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath,
napi);
struct bnx2x *bp = fp->bp;
@@ -1846,8 +2177,10 @@ int bnx2x_poll(struct napi_struct *napi, int budget)
}
#endif
- if (bnx2x_has_tx_work(fp))
- bnx2x_tx_int(fp);
+ for_each_cos_in_tx_queue(fp, cos)
+ if (bnx2x_tx_queue_has_work(&fp->txdata[cos]))
+ bnx2x_tx_int(bp, &fp->txdata[cos]);
+
if (bnx2x_has_rx_work(fp)) {
work_done += bnx2x_rx_int(fp, budget - work_done);
@@ -1909,7 +2242,7 @@ int bnx2x_poll(struct napi_struct *napi, int budget)
* in Other Operating Systems(TM)
*/
static noinline u16 bnx2x_tx_split(struct bnx2x *bp,
- struct bnx2x_fastpath *fp,
+ struct bnx2x_fp_txdata *txdata,
struct sw_tx_bd *tx_buf,
struct eth_tx_start_bd **tx_bd, u16 hlen,
u16 bd_prod, int nbd)
@@ -1930,7 +2263,7 @@ static noinline u16 bnx2x_tx_split(struct bnx2x *bp,
/* now get a new data BD
* (after the pbd) and fill it */
bd_prod = TX_BD(NEXT_TX_IDX(bd_prod));
- d_tx_bd = &fp->tx_desc_ring[bd_prod].reg_bd;
+ d_tx_bd = &txdata->tx_desc_ring[bd_prod].reg_bd;
mapping = HILO_U64(le32_to_cpu(h_tx_bd->addr_hi),
le32_to_cpu(h_tx_bd->addr_lo)) + hlen;
@@ -2030,7 +2363,7 @@ static int bnx2x_pkt_req_lin(struct bnx2x *bp, struct sk_buff *skb,
/* Calculate the first sum - it's special */
for (frag_idx = 0; frag_idx < wnd_size - 1; frag_idx++)
wnd_sum +=
- skb_shinfo(skb)->frags[frag_idx].size;
+ skb_frag_size(&skb_shinfo(skb)->frags[frag_idx]);
/* If there was data on linear skb data - check it */
if (first_bd_sz > 0) {
@@ -2046,14 +2379,14 @@ static int bnx2x_pkt_req_lin(struct bnx2x *bp, struct sk_buff *skb,
check all windows */
for (wnd_idx = 0; wnd_idx <= num_wnds; wnd_idx++) {
wnd_sum +=
- skb_shinfo(skb)->frags[wnd_idx + wnd_size - 1].size;
+ skb_frag_size(&skb_shinfo(skb)->frags[wnd_idx + wnd_size - 1]);
if (unlikely(wnd_sum < lso_mss)) {
to_copy = 1;
break;
}
wnd_sum -=
- skb_shinfo(skb)->frags[wnd_idx].size;
+ skb_frag_size(&skb_shinfo(skb)->frags[wnd_idx]);
}
} else {
/* in non-LSO too fragmented packet should always
@@ -2148,6 +2481,22 @@ static inline u8 bnx2x_set_pbd_csum_e2(struct bnx2x *bp, struct sk_buff *skb,
sizeof(struct udphdr) - skb->data;
}
+static inline void bnx2x_set_sbd_csum(struct bnx2x *bp, struct sk_buff *skb,
+ struct eth_tx_start_bd *tx_start_bd, u32 xmit_type)
+{
+ tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_L4_CSUM;
+
+ if (xmit_type & XMIT_CSUM_V4)
+ tx_start_bd->bd_flags.as_bitfield |=
+ ETH_TX_BD_FLAGS_IP_CSUM;
+ else
+ tx_start_bd->bd_flags.as_bitfield |=
+ ETH_TX_BD_FLAGS_IPV6;
+
+ if (!(xmit_type & XMIT_CSUM_TCP))
+ tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_IS_UDP;
+}
+
/**
* bnx2x_set_pbd_csum - update PBD with checksum and return header length
*
@@ -2210,16 +2559,18 @@ static inline u8 bnx2x_set_pbd_csum(struct bnx2x *bp, struct sk_buff *skb,
netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct bnx2x *bp = netdev_priv(dev);
+
struct bnx2x_fastpath *fp;
struct netdev_queue *txq;
+ struct bnx2x_fp_txdata *txdata;
struct sw_tx_bd *tx_buf;
- struct eth_tx_start_bd *tx_start_bd;
+ struct eth_tx_start_bd *tx_start_bd, *first_bd;
struct eth_tx_bd *tx_data_bd, *total_pkt_bd = NULL;
struct eth_tx_parse_bd_e1x *pbd_e1x = NULL;
struct eth_tx_parse_bd_e2 *pbd_e2 = NULL;
u32 pbd_e2_parsing_data = 0;
u16 pkt_prod, bd_prod;
- int nbd, fp_index;
+ int nbd, txq_index, fp_index, txdata_index;
dma_addr_t mapping;
u32 xmit_type = bnx2x_xmit_type(bp, skb);
int i;
@@ -2233,12 +2584,43 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_BUSY;
#endif
- fp_index = skb_get_queue_mapping(skb);
- txq = netdev_get_tx_queue(dev, fp_index);
+ txq_index = skb_get_queue_mapping(skb);
+ txq = netdev_get_tx_queue(dev, txq_index);
+
+ BUG_ON(txq_index >= MAX_ETH_TXQ_IDX(bp) + FCOE_PRESENT);
+ /* decode the fastpath index and the cos index from the txq */
+ fp_index = TXQ_TO_FP(txq_index);
+ txdata_index = TXQ_TO_COS(txq_index);
+
+#ifdef BCM_CNIC
+ /*
+ * Override the above for the FCoE queue:
+ * - FCoE fp entry is right after the ETH entries.
+ * - FCoE L2 queue uses bp->txdata[0] only.
+ */
+ if (unlikely(!NO_FCOE(bp) && (txq_index ==
+ bnx2x_fcoe_tx(bp, txq_index)))) {
+ fp_index = FCOE_IDX;
+ txdata_index = 0;
+ }
+#endif
+
+ /* enable this debug print to view the transmission queue being used
+ DP(BNX2X_MSG_FP, "indices: txq %d, fp %d, txdata %d\n",
+ txq_index, fp_index, txdata_index); */
+
+ /* locate the fastpath and the txdata */
fp = &bp->fp[fp_index];
+ txdata = &fp->txdata[txdata_index];
- if (unlikely(bnx2x_tx_avail(fp) < (skb_shinfo(skb)->nr_frags + 3))) {
+ /* enable this debug print to view the tranmission details
+ DP(BNX2X_MSG_FP,"transmitting packet cid %d fp index %d txdata_index %d"
+ " tx_data ptr %p fp pointer %p\n",
+ txdata->cid, fp_index, txdata_index, txdata, fp); */
+
+ if (unlikely(bnx2x_tx_avail(bp, txdata) <
+ (skb_shinfo(skb)->nr_frags + 3))) {
fp->eth_q_stats.driver_xoff++;
netif_tx_stop_queue(txq);
BNX2X_ERR("BUG! Tx ring full when queue awake!\n");
@@ -2247,7 +2629,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
DP(NETIF_MSG_TX_QUEUED, "queue[%d]: SKB: summed %x protocol %x "
"protocol(%x,%x) gso type %x xmit_type %x\n",
- fp_index, skb->ip_summed, skb->protocol, ipv6_hdr(skb)->nexthdr,
+ txq_index, skb->ip_summed, skb->protocol, ipv6_hdr(skb)->nexthdr,
ip_hdr(skb)->protocol, skb_shinfo(skb)->gso_type, xmit_type);
eth = (struct ethhdr *)skb->data;
@@ -2275,7 +2657,15 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
#endif
-
+ /* Map skb linear data for DMA */
+ mapping = dma_map_single(&bp->pdev->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) {
+ DP(NETIF_MSG_TX_QUEUED, "SKB mapping failed - "
+ "silently dropping this SKB\n");
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
/*
Please read carefully. First we use one BD which we mark as start,
then we have a parsing info BD (used for TSO or xsum),
@@ -2285,12 +2675,19 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
And above all, all pdb sizes are in words - NOT DWORDS!
*/
- pkt_prod = fp->tx_pkt_prod++;
- bd_prod = TX_BD(fp->tx_bd_prod);
+ /* get current pkt produced now - advance it just before sending packet
+ * since mapping of pages may fail and cause packet to be dropped
+ */
+ pkt_prod = txdata->tx_pkt_prod;
+ bd_prod = TX_BD(txdata->tx_bd_prod);
- /* get a tx_buf and first BD */
- tx_buf = &fp->tx_buf_ring[TX_BD(pkt_prod)];
- tx_start_bd = &fp->tx_desc_ring[bd_prod].start_bd;
+ /* get a tx_buf and first BD
+ * tx_start_bd may be changed during SPLIT,
+ * but first_bd will always stay first
+ */
+ tx_buf = &txdata->tx_buf_ring[TX_BD(pkt_prod)];
+ tx_start_bd = &txdata->tx_desc_ring[bd_prod].start_bd;
+ first_bd = tx_start_bd;
tx_start_bd->bd_flags.as_bitfield = ETH_TX_BD_FLAGS_START_BD;
SET_FLAG(tx_start_bd->general_data, ETH_TX_START_BD_ETH_ADDR_TYPE,
@@ -2300,13 +2697,13 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
SET_FLAG(tx_start_bd->general_data, ETH_TX_START_BD_HDR_NBDS, 1);
/* remember the first BD of the packet */
- tx_buf->first_bd = fp->tx_bd_prod;
+ tx_buf->first_bd = txdata->tx_bd_prod;
tx_buf->skb = skb;
tx_buf->flags = 0;
DP(NETIF_MSG_TX_QUEUED,
"sending pkt %u @%p next_idx %u bd %u @%p\n",
- pkt_prod, tx_buf, fp->tx_pkt_prod, bd_prod, tx_start_bd);
+ pkt_prod, tx_buf, txdata->tx_pkt_prod, bd_prod, tx_start_bd);
if (vlan_tx_tag_present(skb)) {
tx_start_bd->vlan_or_ethertype =
@@ -2319,31 +2716,33 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* turn on parsing and get a BD */
bd_prod = TX_BD(NEXT_TX_IDX(bd_prod));
- if (xmit_type & XMIT_CSUM) {
- tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_L4_CSUM;
-
- if (xmit_type & XMIT_CSUM_V4)
- tx_start_bd->bd_flags.as_bitfield |=
- ETH_TX_BD_FLAGS_IP_CSUM;
- else
- tx_start_bd->bd_flags.as_bitfield |=
- ETH_TX_BD_FLAGS_IPV6;
-
- if (!(xmit_type & XMIT_CSUM_TCP))
- tx_start_bd->bd_flags.as_bitfield |=
- ETH_TX_BD_FLAGS_IS_UDP;
- }
+ if (xmit_type & XMIT_CSUM)
+ bnx2x_set_sbd_csum(bp, skb, tx_start_bd, xmit_type);
- if (CHIP_IS_E2(bp)) {
- pbd_e2 = &fp->tx_desc_ring[bd_prod].parse_bd_e2;
+ if (!CHIP_IS_E1x(bp)) {
+ pbd_e2 = &txdata->tx_desc_ring[bd_prod].parse_bd_e2;
memset(pbd_e2, 0, sizeof(struct eth_tx_parse_bd_e2));
/* Set PBD in checksum offload case */
if (xmit_type & XMIT_CSUM)
hlen = bnx2x_set_pbd_csum_e2(bp, skb,
&pbd_e2_parsing_data,
xmit_type);
+ if (IS_MF_SI(bp)) {
+ /*
+ * fill in the MAC addresses in the PBD - for local
+ * switching
+ */
+ bnx2x_set_fw_mac_addr(&pbd_e2->src_mac_addr_hi,
+ &pbd_e2->src_mac_addr_mid,
+ &pbd_e2->src_mac_addr_lo,
+ eth->h_source);
+ bnx2x_set_fw_mac_addr(&pbd_e2->dst_mac_addr_hi,
+ &pbd_e2->dst_mac_addr_mid,
+ &pbd_e2->dst_mac_addr_lo,
+ eth->h_dest);
+ }
} else {
- pbd_e1x = &fp->tx_desc_ring[bd_prod].parse_bd_e1x;
+ pbd_e1x = &txdata->tx_desc_ring[bd_prod].parse_bd_e1x;
memset(pbd_e1x, 0, sizeof(struct eth_tx_parse_bd_e1x));
/* Set PBD in checksum offload case */
if (xmit_type & XMIT_CSUM)
@@ -2351,15 +2750,10 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
- /* Map skb linear data for DMA */
- mapping = dma_map_single(&bp->pdev->dev, skb->data,
- skb_headlen(skb), DMA_TO_DEVICE);
-
/* Setup the data pointer of the first BD of the packet */
tx_start_bd->addr_hi = cpu_to_le32(U64_HI(mapping));
tx_start_bd->addr_lo = cpu_to_le32(U64_LO(mapping));
- nbd = skb_shinfo(skb)->nr_frags + 2; /* start_bd + pbd + frags */
- tx_start_bd->nbd = cpu_to_le16(nbd);
+ nbd = 2; /* start_bd + pbd + frags (updated when pages are mapped) */
tx_start_bd->nbytes = cpu_to_le16(skb_headlen(skb));
pkt_size = tx_start_bd->nbytes;
@@ -2380,9 +2774,10 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_SW_LSO;
if (unlikely(skb_headlen(skb) > hlen))
- bd_prod = bnx2x_tx_split(bp, fp, tx_buf, &tx_start_bd,
- hlen, bd_prod, ++nbd);
- if (CHIP_IS_E2(bp))
+ bd_prod = bnx2x_tx_split(bp, txdata, tx_buf,
+ &tx_start_bd, hlen,
+ bd_prod, ++nbd);
+ if (!CHIP_IS_E1x(bp))
bnx2x_set_pbd_gso_e2(skb, &pbd_e2_parsing_data,
xmit_type);
else
@@ -2401,19 +2796,34 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ mapping = skb_frag_dma_map(&bp->pdev->dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) {
+
+ DP(NETIF_MSG_TX_QUEUED, "Unable to map page - "
+ "dropping packet...\n");
+
+ /* we need unmap all buffers already mapped
+ * for this SKB;
+ * first_bd->nbd need to be properly updated
+ * before call to bnx2x_free_tx_pkt
+ */
+ first_bd->nbd = cpu_to_le16(nbd);
+ bnx2x_free_tx_pkt(bp, txdata,
+ TX_BD(txdata->tx_pkt_prod));
+ return NETDEV_TX_OK;
+ }
+
bd_prod = TX_BD(NEXT_TX_IDX(bd_prod));
- tx_data_bd = &fp->tx_desc_ring[bd_prod].reg_bd;
+ tx_data_bd = &txdata->tx_desc_ring[bd_prod].reg_bd;
if (total_pkt_bd == NULL)
- total_pkt_bd = &fp->tx_desc_ring[bd_prod].reg_bd;
-
- mapping = dma_map_page(&bp->pdev->dev, frag->page,
- frag->page_offset,
- frag->size, DMA_TO_DEVICE);
+ total_pkt_bd = &txdata->tx_desc_ring[bd_prod].reg_bd;
tx_data_bd->addr_hi = cpu_to_le32(U64_HI(mapping));
tx_data_bd->addr_lo = cpu_to_le32(U64_LO(mapping));
- tx_data_bd->nbytes = cpu_to_le16(frag->size);
- le16_add_cpu(&pkt_size, frag->size);
+ tx_data_bd->nbytes = cpu_to_le16(skb_frag_size(frag));
+ le16_add_cpu(&pkt_size, skb_frag_size(frag));
+ nbd++;
DP(NETIF_MSG_TX_QUEUED,
"frag %d bd @%p addr (%x:%x) nbytes %d\n",
@@ -2423,6 +2833,9 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
DP(NETIF_MSG_TX_QUEUED, "last bd @%p\n", tx_data_bd);
+ /* update with actual num BDs */
+ first_bd->nbd = cpu_to_le16(nbd);
+
bd_prod = TX_BD(NEXT_TX_IDX(bd_prod));
/* now send a tx doorbell, counting the next BD
@@ -2431,6 +2844,13 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (TX_BD_POFF(bd_prod) < nbd)
nbd++;
+ /* total_pkt_bytes should be set on the first data BD if
+ * it's not an LSO packet and there is more than one
+ * data BD. In this case pkt_size is limited by an MTU value.
+ * However we prefer to set it for an LSO packet (while we don't
+ * have to) in order to save some CPU cycles in a none-LSO
+ * case, when we much more care about them.
+ */
if (total_pkt_bd != NULL)
total_pkt_bd->total_pkt_bytes = pkt_size;
@@ -2451,6 +2871,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
pbd_e2->parsing_data);
DP(NETIF_MSG_TX_QUEUED, "doorbell: nbd %d bd %u\n", nbd, bd_prod);
+ txdata->tx_pkt_prod++;
/*
* Make sure that the BD data is updated before updating the producer
* since FW might read the BD right after the producer is updated.
@@ -2460,16 +2881,16 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
*/
wmb();
- fp->tx_db.data.prod += nbd;
+ txdata->tx_db.data.prod += nbd;
barrier();
- DOORBELL(bp, fp->cid, fp->tx_db.raw);
+ DOORBELL(bp, txdata->cid, txdata->tx_db.raw);
mmiowb();
- fp->tx_bd_prod += nbd;
+ txdata->tx_bd_prod += nbd;
- if (unlikely(bnx2x_tx_avail(fp) < MAX_SKB_FRAGS + 3)) {
+ if (unlikely(bnx2x_tx_avail(bp, txdata) < MAX_SKB_FRAGS + 3)) {
netif_tx_stop_queue(txq);
/* paired memory barrier is in bnx2x_tx_int(), we have to keep
@@ -2478,34 +2899,110 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
smp_mb();
fp->eth_q_stats.driver_xoff++;
- if (bnx2x_tx_avail(fp) >= MAX_SKB_FRAGS + 3)
+ if (bnx2x_tx_avail(bp, txdata) >= MAX_SKB_FRAGS + 3)
netif_tx_wake_queue(txq);
}
- fp->tx_pkt++;
+ txdata->tx_pkt++;
return NETDEV_TX_OK;
}
+/**
+ * bnx2x_setup_tc - routine to configure net_device for multi tc
+ *
+ * @netdev: net device to configure
+ * @tc: number of traffic classes to enable
+ *
+ * callback connected to the ndo_setup_tc function pointer
+ */
+int bnx2x_setup_tc(struct net_device *dev, u8 num_tc)
+{
+ int cos, prio, count, offset;
+ struct bnx2x *bp = netdev_priv(dev);
+
+ /* setup tc must be called under rtnl lock */
+ ASSERT_RTNL();
+
+ /* no traffic classes requested. aborting */
+ if (!num_tc) {
+ netdev_reset_tc(dev);
+ return 0;
+ }
+
+ /* requested to support too many traffic classes */
+ if (num_tc > bp->max_cos) {
+ DP(NETIF_MSG_TX_ERR, "support for too many traffic classes"
+ " requested: %d. max supported is %d\n",
+ num_tc, bp->max_cos);
+ return -EINVAL;
+ }
+
+ /* declare amount of supported traffic classes */
+ if (netdev_set_num_tc(dev, num_tc)) {
+ DP(NETIF_MSG_TX_ERR, "failed to declare %d traffic classes\n",
+ num_tc);
+ return -EINVAL;
+ }
+
+ /* configure priority to traffic class mapping */
+ for (prio = 0; prio < BNX2X_MAX_PRIORITY; prio++) {
+ netdev_set_prio_tc_map(dev, prio, bp->prio_to_cos[prio]);
+ DP(BNX2X_MSG_SP, "mapping priority %d to tc %d\n",
+ prio, bp->prio_to_cos[prio]);
+ }
+
+
+ /* Use this configuration to diffrentiate tc0 from other COSes
+ This can be used for ets or pfc, and save the effort of setting
+ up a multio class queue disc or negotiating DCBX with a switch
+ netdev_set_prio_tc_map(dev, 0, 0);
+ DP(BNX2X_MSG_SP, "mapping priority %d to tc %d\n", 0, 0);
+ for (prio = 1; prio < 16; prio++) {
+ netdev_set_prio_tc_map(dev, prio, 1);
+ DP(BNX2X_MSG_SP, "mapping priority %d to tc %d\n", prio, 1);
+ } */
+
+ /* configure traffic class to transmission queue mapping */
+ for (cos = 0; cos < bp->max_cos; cos++) {
+ count = BNX2X_NUM_ETH_QUEUES(bp);
+ offset = cos * MAX_TXQS_PER_COS;
+ netdev_set_tc_queue(dev, cos, count, offset);
+ DP(BNX2X_MSG_SP, "mapping tc %d to offset %d count %d\n",
+ cos, offset, count);
+ }
+
+ return 0;
+}
+
/* called with rtnl_lock */
int bnx2x_change_mac_addr(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
struct bnx2x *bp = netdev_priv(dev);
+ int rc = 0;
if (!is_valid_ether_addr((u8 *)(addr->sa_data)))
return -EINVAL;
+ if (netif_running(dev)) {
+ rc = bnx2x_set_eth_mac(bp, false);
+ if (rc)
+ return rc;
+ }
+
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
if (netif_running(dev))
- bnx2x_set_eth_mac(bp, 1);
+ rc = bnx2x_set_eth_mac(bp, true);
- return 0;
+ return rc;
}
static void bnx2x_free_fp_mem_at(struct bnx2x *bp, int fp_index)
{
union host_hc_status_block *sb = &bnx2x_fp(bp, fp_index, status_blk);
struct bnx2x_fastpath *fp = &bp->fp[fp_index];
+ u8 cos;
/* Common */
#ifdef BCM_CNIC
@@ -2516,7 +3013,7 @@ static void bnx2x_free_fp_mem_at(struct bnx2x *bp, int fp_index)
} else {
#endif
/* status blocks */
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
BNX2X_PCI_FREE(sb->e2_sb,
bnx2x_fp(bp, fp_index,
status_blk_mapping),
@@ -2554,10 +3051,18 @@ static void bnx2x_free_fp_mem_at(struct bnx2x *bp, int fp_index)
/* Tx */
if (!skip_tx_queue(bp, fp_index)) {
/* fastpath tx rings: tx_buf tx_desc */
- BNX2X_FREE(bnx2x_fp(bp, fp_index, tx_buf_ring));
- BNX2X_PCI_FREE(bnx2x_fp(bp, fp_index, tx_desc_ring),
- bnx2x_fp(bp, fp_index, tx_desc_mapping),
- sizeof(union eth_tx_bd_types) * NUM_TX_BD);
+ for_each_cos_in_tx_queue(fp, cos) {
+ struct bnx2x_fp_txdata *txdata = &fp->txdata[cos];
+
+ DP(BNX2X_MSG_SP,
+ "freeing tx memory of fp %d cos %d cid %d\n",
+ fp_index, cos, txdata->cid);
+
+ BNX2X_FREE(txdata->tx_buf_ring);
+ BNX2X_PCI_FREE(txdata->tx_desc_ring,
+ txdata->tx_desc_mapping,
+ sizeof(union eth_tx_bd_types) * NUM_TX_BD);
+ }
}
/* end of fastpath */
}
@@ -2572,7 +3077,7 @@ void bnx2x_free_fp_mem(struct bnx2x *bp)
static inline void set_sb_shortcuts(struct bnx2x *bp, int index)
{
union host_hc_status_block status_blk = bnx2x_fp(bp, index, status_blk);
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
bnx2x_fp(bp, index, sb_index_values) =
(__le16 *)status_blk.e2_sb->sb.index_values;
bnx2x_fp(bp, index, sb_running_index) =
@@ -2590,18 +3095,21 @@ static int bnx2x_alloc_fp_mem_at(struct bnx2x *bp, int index)
union host_hc_status_block *sb;
struct bnx2x_fastpath *fp = &bp->fp[index];
int ring_size = 0;
+ u8 cos;
+ int rx_ring_size = 0;
/* if rx_ring_size specified - use it */
- int rx_ring_size = bp->rx_ring_size ? bp->rx_ring_size :
- MAX_RX_AVAIL/bp->num_queues;
+ if (!bp->rx_ring_size) {
+
+ rx_ring_size = MAX_RX_AVAIL/BNX2X_NUM_RX_QUEUES(bp);
- /* allocate at least number of buffers required by FW */
- rx_ring_size = max_t(int, fp->disable_tpa ? MIN_RX_SIZE_NONTPA :
- MIN_RX_SIZE_TPA,
- rx_ring_size);
+ /* allocate at least number of buffers required by FW */
+ rx_ring_size = max_t(int, bp->disable_tpa ? MIN_RX_SIZE_NONTPA :
+ MIN_RX_SIZE_TPA, rx_ring_size);
- bnx2x_fp(bp, index, bp) = bp;
- bnx2x_fp(bp, index, index) = index;
+ bp->rx_ring_size = rx_ring_size;
+ } else
+ rx_ring_size = bp->rx_ring_size;
/* Common */
sb = &bnx2x_fp(bp, index, status_blk);
@@ -2609,7 +3117,7 @@ static int bnx2x_alloc_fp_mem_at(struct bnx2x *bp, int index)
if (!IS_FCOE_IDX(index)) {
#endif
/* status blocks */
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
BNX2X_PCI_ALLOC(sb->e2_sb,
&bnx2x_fp(bp, index, status_blk_mapping),
sizeof(struct host_hc_status_block_e2));
@@ -2620,16 +3128,29 @@ static int bnx2x_alloc_fp_mem_at(struct bnx2x *bp, int index)
#ifdef BCM_CNIC
}
#endif
- set_sb_shortcuts(bp, index);
+
+ /* FCoE Queue uses Default SB and doesn't ACK the SB, thus no need to
+ * set shortcuts for it.
+ */
+ if (!IS_FCOE_IDX(index))
+ set_sb_shortcuts(bp, index);
/* Tx */
if (!skip_tx_queue(bp, index)) {
/* fastpath tx rings: tx_buf tx_desc */
- BNX2X_ALLOC(bnx2x_fp(bp, index, tx_buf_ring),
+ for_each_cos_in_tx_queue(fp, cos) {
+ struct bnx2x_fp_txdata *txdata = &fp->txdata[cos];
+
+ DP(BNX2X_MSG_SP, "allocating tx memory of "
+ "fp %d cos %d\n",
+ index, cos);
+
+ BNX2X_ALLOC(txdata->tx_buf_ring,
sizeof(struct sw_tx_bd) * NUM_TX_BD);
- BNX2X_PCI_ALLOC(bnx2x_fp(bp, index, tx_desc_ring),
- &bnx2x_fp(bp, index, tx_desc_mapping),
+ BNX2X_PCI_ALLOC(txdata->tx_desc_ring,
+ &txdata->tx_desc_mapping,
sizeof(union eth_tx_bd_types) * NUM_TX_BD);
+ }
}
/* Rx */
@@ -2672,7 +3193,7 @@ alloc_mem_err:
index, ring_size);
/* FW will drop all packets if queue is not big enough,
* In these cases we disable the queue
- * Min size diferent for TPA and non-TPA queues
+ * Min size is different for OOO, TPA and non-TPA queues
*/
if (ring_size < (fp->disable_tpa ?
MIN_RX_SIZE_NONTPA : MIN_RX_SIZE_TPA)) {
@@ -2690,17 +3211,24 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp)
/**
* 1. Allocate FP for leading - fatal if error
* 2. {CNIC} Allocate FCoE FP - fatal if error
- * 3. Allocate RSS - fix number of queues if error
+ * 3. {CNIC} Allocate OOO + FWD - disable OOO if error
+ * 4. Allocate RSS - fix number of queues if error
*/
/* leading */
if (bnx2x_alloc_fp_mem_at(bp, 0))
return -ENOMEM;
+
#ifdef BCM_CNIC
- /* FCoE */
- if (bnx2x_alloc_fp_mem_at(bp, FCOE_IDX))
- return -ENOMEM;
+ if (!NO_FCOE(bp))
+ /* FCoE */
+ if (bnx2x_alloc_fp_mem_at(bp, FCOE_IDX))
+ /* we will fail load process instead of mark
+ * NO_FCOE_FLAG
+ */
+ return -ENOMEM;
#endif
+
/* RSS */
for_each_nondefault_eth_queue(bp, i)
if (bnx2x_alloc_fp_mem_at(bp, i))
@@ -2718,7 +3246,7 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp)
* FCOE_IDX < FWD_IDX < OOO_IDX
*/
- /* move FCoE fp */
+ /* move FCoE fp even NO_FCOE_FLAG is on */
bnx2x_move_fp(bp, FCOE_IDX, FCOE_IDX - delta);
#endif
bp->num_queues -= delta;
@@ -2729,30 +3257,6 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp)
return 0;
}
-static int bnx2x_setup_irqs(struct bnx2x *bp)
-{
- int rc = 0;
- if (bp->flags & USING_MSIX_FLAG) {
- rc = bnx2x_req_msix_irqs(bp);
- if (rc)
- return rc;
- } else {
- bnx2x_ack_int(bp);
- rc = bnx2x_req_irq(bp);
- if (rc) {
- BNX2X_ERR("IRQ request failed rc %d, aborting\n", rc);
- return rc;
- }
- if (bp->flags & USING_MSI_FLAG) {
- bp->dev->irq = bp->pdev->irq;
- netdev_info(bp->dev, "using MSI IRQ %d\n",
- bp->pdev->irq);
- }
- }
-
- return 0;
-}
-
void bnx2x_free_mem_bp(struct bnx2x *bp)
{
kfree(bp->fp);
@@ -2765,16 +3269,23 @@ int __devinit bnx2x_alloc_mem_bp(struct bnx2x *bp)
struct bnx2x_fastpath *fp;
struct msix_entry *tbl;
struct bnx2x_ilt *ilt;
+ int msix_table_size = 0;
+
+ /*
+ * The biggest MSI-X table we might need is as a maximum number of fast
+ * path IGU SBs plus default SB (for PF).
+ */
+ msix_table_size = bp->igu_sb_cnt + 1;
- /* fp array */
- fp = kzalloc(L2_FP_COUNT(bp->l2_cid_count)*sizeof(*fp), GFP_KERNEL);
+ /* fp array: RSS plus CNIC related L2 queues */
+ fp = kzalloc((BNX2X_MAX_RSS_COUNT(bp) + NON_ETH_CONTEXT_USE) *
+ sizeof(*fp), GFP_KERNEL);
if (!fp)
goto alloc_err;
bp->fp = fp;
/* msix table */
- tbl = kzalloc((FP_SB_COUNT(bp->l2_cid_count) + 1) * sizeof(*tbl),
- GFP_KERNEL);
+ tbl = kzalloc(msix_table_size * sizeof(*tbl), GFP_KERNEL);
if (!tbl)
goto alloc_err;
bp->msix_table = tbl;
@@ -2792,7 +3303,7 @@ alloc_err:
}
-static int bnx2x_reload_if_running(struct net_device *dev)
+int bnx2x_reload_if_running(struct net_device *dev)
{
struct bnx2x *bp = netdev_priv(dev);
@@ -2803,13 +3314,85 @@ static int bnx2x_reload_if_running(struct net_device *dev)
return bnx2x_nic_load(bp, LOAD_NORMAL);
}
+int bnx2x_get_cur_phy_idx(struct bnx2x *bp)
+{
+ u32 sel_phy_idx = 0;
+ if (bp->link_params.num_phys <= 1)
+ return INT_PHY;
+
+ if (bp->link_vars.link_up) {
+ sel_phy_idx = EXT_PHY1;
+ /* In case link is SERDES, check if the EXT_PHY2 is the one */
+ if ((bp->link_vars.link_status & LINK_STATUS_SERDES_LINK) &&
+ (bp->link_params.phy[EXT_PHY2].supported & SUPPORTED_FIBRE))
+ sel_phy_idx = EXT_PHY2;
+ } else {
+
+ switch (bnx2x_phy_selection(&bp->link_params)) {
+ case PORT_HW_CFG_PHY_SELECTION_HARDWARE_DEFAULT:
+ case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY:
+ case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY_PRIORITY:
+ sel_phy_idx = EXT_PHY1;
+ break;
+ case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY:
+ case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY_PRIORITY:
+ sel_phy_idx = EXT_PHY2;
+ break;
+ }
+ }
+
+ return sel_phy_idx;
+
+}
+int bnx2x_get_link_cfg_idx(struct bnx2x *bp)
+{
+ u32 sel_phy_idx = bnx2x_get_cur_phy_idx(bp);
+ /*
+ * The selected actived PHY is always after swapping (in case PHY
+ * swapping is enabled). So when swapping is enabled, we need to reverse
+ * the configuration
+ */
+
+ if (bp->link_params.multi_phy_config &
+ PORT_HW_CFG_PHY_SWAPPED_ENABLED) {
+ if (sel_phy_idx == EXT_PHY1)
+ sel_phy_idx = EXT_PHY2;
+ else if (sel_phy_idx == EXT_PHY2)
+ sel_phy_idx = EXT_PHY1;
+ }
+ return LINK_CONFIG_IDX(sel_phy_idx);
+}
+
+#if defined(NETDEV_FCOE_WWNN) && defined(BCM_CNIC)
+int bnx2x_fcoe_get_wwn(struct net_device *dev, u64 *wwn, int type)
+{
+ struct bnx2x *bp = netdev_priv(dev);
+ struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+ switch (type) {
+ case NETDEV_FCOE_WWNN:
+ *wwn = HILO_U64(cp->fcoe_wwn_node_name_hi,
+ cp->fcoe_wwn_node_name_lo);
+ break;
+ case NETDEV_FCOE_WWPN:
+ *wwn = HILO_U64(cp->fcoe_wwn_port_name_hi,
+ cp->fcoe_wwn_port_name_lo);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+#endif
+
/* called with rtnl_lock */
int bnx2x_change_mtu(struct net_device *dev, int new_mtu)
{
struct bnx2x *bp = netdev_priv(dev);
if (bp->recovery_state != BNX2X_RECOVERY_DONE) {
- printk(KERN_ERR "Handling parity error recovery. Try again later\n");
+ pr_err("Handling parity error recovery. Try again later\n");
return -EAGAIN;
}
@@ -2882,8 +3465,13 @@ void bnx2x_tx_timeout(struct net_device *dev)
if (!bp->panic)
bnx2x_panic();
#endif
+
+ smp_mb__before_clear_bit();
+ set_bit(BNX2X_SP_RTNL_TX_TIMEOUT, &bp->sp_rtnl_state);
+ smp_mb__after_clear_bit();
+
/* This allows the netif to be shutdown gracefully before resetting */
- schedule_delayed_work(&bp->reset_task, 0);
+ schedule_delayed_work(&bp->sp_rtnl_task, 0);
}
int bnx2x_suspend(struct pci_dev *pdev, pm_message_t state)
@@ -2930,7 +3518,7 @@ int bnx2x_resume(struct pci_dev *pdev)
bp = netdev_priv(dev);
if (bp->recovery_state != BNX2X_RECOVERY_DONE) {
- printk(KERN_ERR "Handling parity error recovery. Try again later\n");
+ pr_err("Handling parity error recovery. Try again later\n");
return -EAGAIN;
}
@@ -2954,3 +3542,57 @@ int bnx2x_resume(struct pci_dev *pdev)
return rc;
}
+
+
+void bnx2x_set_ctx_validation(struct bnx2x *bp, struct eth_context *cxt,
+ u32 cid)
+{
+ /* ustorm cxt validation */
+ cxt->ustorm_ag_context.cdu_usage =
+ CDU_RSRVD_VALUE_TYPE_A(HW_CID(bp, cid),
+ CDU_REGION_NUMBER_UCM_AG, ETH_CONNECTION_TYPE);
+ /* xcontext validation */
+ cxt->xstorm_ag_context.cdu_reserved =
+ CDU_RSRVD_VALUE_TYPE_A(HW_CID(bp, cid),
+ CDU_REGION_NUMBER_XCM_AG, ETH_CONNECTION_TYPE);
+}
+
+static inline void storm_memset_hc_timeout(struct bnx2x *bp, u8 port,
+ u8 fw_sb_id, u8 sb_index,
+ u8 ticks)
+{
+
+ u32 addr = BAR_CSTRORM_INTMEM +
+ CSTORM_STATUS_BLOCK_DATA_TIMEOUT_OFFSET(fw_sb_id, sb_index);
+ REG_WR8(bp, addr, ticks);
+ DP(NETIF_MSG_HW, "port %x fw_sb_id %d sb_index %d ticks %d\n",
+ port, fw_sb_id, sb_index, ticks);
+}
+
+static inline void storm_memset_hc_disable(struct bnx2x *bp, u8 port,
+ u16 fw_sb_id, u8 sb_index,
+ u8 disable)
+{
+ u32 enable_flag = disable ? 0 : (1 << HC_INDEX_DATA_HC_ENABLED_SHIFT);
+ u32 addr = BAR_CSTRORM_INTMEM +
+ CSTORM_STATUS_BLOCK_DATA_FLAGS_OFFSET(fw_sb_id, sb_index);
+ u16 flags = REG_RD16(bp, addr);
+ /* clear and set */
+ flags &= ~HC_INDEX_DATA_HC_ENABLED;
+ flags |= enable_flag;
+ REG_WR16(bp, addr, flags);
+ DP(NETIF_MSG_HW, "port %x fw_sb_id %d sb_index %d disable %d\n",
+ port, fw_sb_id, sb_index, disable);
+}
+
+void bnx2x_update_coalesce_sb_index(struct bnx2x *bp, u8 fw_sb_id,
+ u8 sb_index, u8 disable, u16 usec)
+{
+ int port = BP_PORT(bp);
+ u8 ticks = usec / BNX2X_BTR;
+
+ storm_memset_hc_timeout(bp, port, fw_sb_id, sb_index, ticks);
+
+ disable = disable ? 1 : (usec ? 0 : 1);
+ storm_memset_hc_disable(bp, port, fw_sb_id, sb_index, disable);
+}
diff --git a/drivers/net/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
index 1a3545bd8a92..283d663da180 100644
--- a/drivers/net/bnx2x/bnx2x_cmn.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
@@ -18,11 +18,15 @@
#define BNX2X_CMN_H
#include <linux/types.h>
+#include <linux/pci.h>
#include <linux/netdevice.h>
#include "bnx2x.h"
+/* This is used as a replacement for an MCP if it's not present */
+extern int load_count[2][3]; /* per-path: 0-common, 1-port0, 2-port1 */
+
extern int num_queues;
/************************ Macros ********************************/
@@ -61,6 +65,73 @@ extern int num_queues;
/*********************** Interfaces ****************************
* Functions that need to be implemented by each driver version
*/
+/* Init */
+
+/**
+ * bnx2x_send_unload_req - request unload mode from the MCP.
+ *
+ * @bp: driver handle
+ * @unload_mode: requested function's unload mode
+ *
+ * Return unload mode returned by the MCP: COMMON, PORT or FUNC.
+ */
+u32 bnx2x_send_unload_req(struct bnx2x *bp, int unload_mode);
+
+/**
+ * bnx2x_send_unload_done - send UNLOAD_DONE command to the MCP.
+ *
+ * @bp: driver handle
+ */
+void bnx2x_send_unload_done(struct bnx2x *bp);
+
+/**
+ * bnx2x_config_rss_pf - configure RSS parameters.
+ *
+ * @bp: driver handle
+ * @ind_table: indirection table to configure
+ * @config_hash: re-configure RSS hash keys configuration
+ */
+int bnx2x_config_rss_pf(struct bnx2x *bp, u8 *ind_table, bool config_hash);
+
+/**
+ * bnx2x__init_func_obj - init function object
+ *
+ * @bp: driver handle
+ *
+ * Initializes the Function Object with the appropriate
+ * parameters which include a function slow path driver
+ * interface.
+ */
+void bnx2x__init_func_obj(struct bnx2x *bp);
+
+/**
+ * bnx2x_setup_queue - setup eth queue.
+ *
+ * @bp: driver handle
+ * @fp: pointer to the fastpath structure
+ * @leading: boolean
+ *
+ */
+int bnx2x_setup_queue(struct bnx2x *bp, struct bnx2x_fastpath *fp,
+ bool leading);
+
+/**
+ * bnx2x_setup_leading - bring up a leading eth queue.
+ *
+ * @bp: driver handle
+ */
+int bnx2x_setup_leading(struct bnx2x *bp);
+
+/**
+ * bnx2x_fw_command - send the MCP a request
+ *
+ * @bp: driver handle
+ * @command: request
+ * @param: request's parameter
+ *
+ * block until there is a reply
+ */
+u32 bnx2x_fw_command(struct bnx2x *bp, u32 command, u32 param);
/**
* bnx2x_initial_phy_init - initialize link parameters structure variables.
@@ -88,6 +159,32 @@ void bnx2x_link_set(struct bnx2x *bp);
u8 bnx2x_link_test(struct bnx2x *bp, u8 is_serdes);
/**
+ * bnx2x_drv_pulse - write driver pulse to shmem
+ *
+ * @bp: driver handle
+ *
+ * writes the value in bp->fw_drv_pulse_wr_seq to drv_pulse mbox
+ * in the shmem.
+ */
+void bnx2x_drv_pulse(struct bnx2x *bp);
+
+/**
+ * bnx2x_igu_ack_sb - update IGU with current SB value
+ *
+ * @bp: driver handle
+ * @igu_sb_id: SB id
+ * @segment: SB segment
+ * @index: SB index
+ * @op: SB operation
+ * @update: is HW update required
+ */
+void bnx2x_igu_ack_sb(struct bnx2x *bp, u8 igu_sb_id, u8 segment,
+ u16 index, u8 op, u8 update);
+
+/* Disable transactions from chip to host */
+void bnx2x_pf_disable(struct bnx2x *bp);
+
+/**
* bnx2x__link_status_update - handles link status change.
*
* @bp: driver handle
@@ -165,21 +262,6 @@ void bnx2x_int_enable(struct bnx2x *bp);
void bnx2x_int_disable_sync(struct bnx2x *bp, int disable_hw);
/**
- * bnx2x_init_firmware - loads device firmware
- *
- * @bp: driver handle
- */
-int bnx2x_init_firmware(struct bnx2x *bp);
-
-/**
- * bnx2x_init_hw - init HW blocks according to current initialization stage.
- *
- * @bp: driver handle
- * @load_code: COMMON, PORT or FUNCTION
- */
-int bnx2x_init_hw(struct bnx2x *bp, u32 load_code);
-
-/**
* bnx2x_nic_init - init driver internals.
*
* @bp: driver handle
@@ -207,16 +289,6 @@ int bnx2x_alloc_mem(struct bnx2x *bp);
void bnx2x_free_mem(struct bnx2x *bp);
/**
- * bnx2x_setup_client - setup eth client.
- *
- * @bp: driver handle
- * @fp: pointer to fastpath structure
- * @is_leading: boolean
- */
-int bnx2x_setup_client(struct bnx2x *bp, struct bnx2x_fastpath *fp,
- int is_leading);
-
-/**
* bnx2x_set_num_queues - set number of queues according to mode.
*
* @bp: driver handle
@@ -252,36 +324,21 @@ int bnx2x_acquire_hw_lock(struct bnx2x *bp, u32 resource);
int bnx2x_release_hw_lock(struct bnx2x *bp, u32 resource);
/**
- * bnx2x_set_eth_mac - configure eth MAC address in the HW
- *
- * @bp: driver handle
- * @set: set or clear
- *
- * Configures according to the value in netdev->dev_addr.
- */
-void bnx2x_set_eth_mac(struct bnx2x *bp, int set);
-
-#ifdef BCM_CNIC
-/**
- * bnx2x_set_fip_eth_mac_addr - Set/Clear FIP MAC(s)
+ * bnx2x_release_leader_lock - release recovery leader lock
*
* @bp: driver handle
- * @set: set or clear the CAM entry
- *
- * Used next enties in the CAM after the ETH MAC(s).
- * This function will wait until the ramdord completion returns.
- * Return 0 if cussess, -ENODEV if ramrod doesn't return.
*/
-int bnx2x_set_fip_eth_mac_addr(struct bnx2x *bp, int set);
+int bnx2x_release_leader_lock(struct bnx2x *bp);
/**
- * bnx2x_set_all_enode_macs - Set/Clear ALL_ENODE mcast MAC.
+ * bnx2x_set_eth_mac - configure eth MAC address in the HW
*
* @bp: driver handle
* @set: set or clear
+ *
+ * Configures according to the value in netdev->dev_addr.
*/
-int bnx2x_set_all_enode_macs(struct bnx2x *bp, int set);
-#endif
+int bnx2x_set_eth_mac(struct bnx2x *bp, bool set);
/**
* bnx2x_set_rx_mode - set MAC filtering configurations.
@@ -289,6 +346,8 @@ int bnx2x_set_all_enode_macs(struct bnx2x *bp, int set);
* @dev: netdevice
*
* called with netif_tx_lock from dev_mcast.c
+ * If bp->state is OPEN, should be called with
+ * netif_addr_lock_bh()
*/
void bnx2x_set_rx_mode(struct net_device *dev);
@@ -296,25 +355,38 @@ void bnx2x_set_rx_mode(struct net_device *dev);
* bnx2x_set_storm_rx_mode - configure MAC filtering rules in a FW.
*
* @bp: driver handle
+ *
+ * If bp->state is OPEN, should be called with
+ * netif_addr_lock_bh().
*/
void bnx2x_set_storm_rx_mode(struct bnx2x *bp);
+/**
+ * bnx2x_set_q_rx_mode - configures rx_mode for a single queue.
+ *
+ * @bp: driver handle
+ * @cl_id: client id
+ * @rx_mode_flags: rx mode configuration
+ * @rx_accept_flags: rx accept configuration
+ * @tx_accept_flags: tx accept configuration (tx switch)
+ * @ramrod_flags: ramrod configuration
+ */
+void bnx2x_set_q_rx_mode(struct bnx2x *bp, u8 cl_id,
+ unsigned long rx_mode_flags,
+ unsigned long rx_accept_flags,
+ unsigned long tx_accept_flags,
+ unsigned long ramrod_flags);
+
/* Parity errors related */
void bnx2x_inc_load_cnt(struct bnx2x *bp);
u32 bnx2x_dec_load_cnt(struct bnx2x *bp);
-bool bnx2x_chk_parity_attn(struct bnx2x *bp);
-bool bnx2x_reset_is_done(struct bnx2x *bp);
+bool bnx2x_chk_parity_attn(struct bnx2x *bp, bool *global, bool print);
+bool bnx2x_reset_is_done(struct bnx2x *bp, int engine);
+void bnx2x_set_reset_in_progress(struct bnx2x *bp);
+void bnx2x_set_reset_global(struct bnx2x *bp);
void bnx2x_disable_close_the_gate(struct bnx2x *bp);
/**
- * bnx2x_stats_handle - perform statistics handling according to event.
- *
- * @bp: driver handle
- * @event: bnx2x_stats_event
- */
-void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event);
-
-/**
* bnx2x_sp_event - handle ramrods completion.
*
* @fp: fastpath handle for the event
@@ -323,15 +395,6 @@ void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event);
void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe);
/**
- * bnx2x_func_start - init function
- *
- * @bp: driver handle
- *
- * Must be called before sending CLIENT_SETUP for the first client.
- */
-int bnx2x_func_start(struct bnx2x *bp);
-
-/**
* bnx2x_ilt_set_info - prepare ILT configurations.
*
* @bp: driver handle
@@ -362,6 +425,10 @@ int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state);
* @value: new value
*/
void bnx2x_update_max_mf_config(struct bnx2x *bp, u32 value);
+/* Error handling */
+void bnx2x_panic_dump(struct bnx2x *bp);
+
+void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl);
/* dev_close main block */
int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode);
@@ -372,16 +439,25 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode);
/* hard_xmit callback */
netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev);
+/* setup_tc callback */
+int bnx2x_setup_tc(struct net_device *dev, u8 num_tc);
+
/* select_queue callback */
u16 bnx2x_select_queue(struct net_device *dev, struct sk_buff *skb);
+/* reload helper */
+int bnx2x_reload_if_running(struct net_device *dev);
+
int bnx2x_change_mac_addr(struct net_device *dev, void *p);
/* NAPI poll Rx part */
int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget);
+void bnx2x_update_rx_prod(struct bnx2x *bp, struct bnx2x_fastpath *fp,
+ u16 bd_prod, u16 rx_comp_prod, u16 rx_sge_prod);
+
/* NAPI poll Tx part */
-int bnx2x_tx_int(struct bnx2x_fastpath *fp);
+int bnx2x_tx_int(struct bnx2x *bp, struct bnx2x_fp_txdata *txdata);
/* suspend/resume callbacks */
int bnx2x_suspend(struct pci_dev *pdev, pm_message_t state);
@@ -392,7 +468,6 @@ void bnx2x_free_irq(struct bnx2x *bp);
void bnx2x_free_fp_mem(struct bnx2x *bp);
int bnx2x_alloc_fp_mem(struct bnx2x *bp);
-
void bnx2x_init_rx_rings(struct bnx2x *bp);
void bnx2x_free_skbs(struct bnx2x *bp);
void bnx2x_netif_stop(struct bnx2x *bp, int disable_hw);
@@ -447,6 +522,17 @@ void bnx2x_free_mem_bp(struct bnx2x *bp);
*/
int bnx2x_change_mtu(struct net_device *dev, int new_mtu);
+#if defined(NETDEV_FCOE_WWNN) && defined(BCM_CNIC)
+/**
+ * bnx2x_fcoe_get_wwn - return the requested WWN value for this port
+ *
+ * @dev: net_device
+ * @wwn: output buffer
+ * @type: WWN type: NETDEV_FCOE_WWNN (node) or NETDEV_FCOE_WWPN (port)
+ *
+ */
+int bnx2x_fcoe_get_wwn(struct net_device *dev, u64 *wwn, int type);
+#endif
u32 bnx2x_fix_features(struct net_device *dev, u32 features);
int bnx2x_set_features(struct net_device *dev, u32 features);
@@ -457,19 +543,20 @@ int bnx2x_set_features(struct net_device *dev, u32 features);
*/
void bnx2x_tx_timeout(struct net_device *dev);
+/*********************** Inlines **********************************/
+/*********************** Fast path ********************************/
static inline void bnx2x_update_fpsb_idx(struct bnx2x_fastpath *fp)
{
barrier(); /* status block is written to by the chip */
fp->fp_hc_idx = fp->sb_running_index[SM_RX_ID];
}
-static inline void bnx2x_update_rx_prod(struct bnx2x *bp,
- struct bnx2x_fastpath *fp,
- u16 bd_prod, u16 rx_comp_prod,
- u16 rx_sge_prod)
+static inline void bnx2x_update_rx_prod_gen(struct bnx2x *bp,
+ struct bnx2x_fastpath *fp, u16 bd_prod,
+ u16 rx_comp_prod, u16 rx_sge_prod, u32 start)
{
struct ustorm_eth_rx_producers rx_prods = {0};
- int i;
+ u32 i;
/* Update producers */
rx_prods.bd_prod = bd_prod;
@@ -486,10 +573,8 @@ static inline void bnx2x_update_rx_prod(struct bnx2x *bp,
*/
wmb();
- for (i = 0; i < sizeof(struct ustorm_eth_rx_producers)/4; i++)
- REG_WR(bp,
- BAR_USTRORM_INTMEM + fp->ustorm_rx_prods_offset + i*4,
- ((u32 *)&rx_prods)[i]);
+ for (i = 0; i < sizeof(rx_prods)/4; i++)
+ REG_WR(bp, start + i*4, ((u32 *)&rx_prods)[i]);
mmiowb(); /* keep prod updates ordered */
@@ -519,7 +604,7 @@ static inline void bnx2x_igu_ack_sb_gen(struct bnx2x *bp, u8 igu_sb_id,
barrier();
}
-static inline void bnx2x_igu_clear_sb_gen(struct bnx2x *bp,
+static inline void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func,
u8 idu_sb_id, bool is_Pf)
{
u32 data, ctl, cnt = 100;
@@ -527,7 +612,7 @@ static inline void bnx2x_igu_clear_sb_gen(struct bnx2x *bp,
u32 igu_addr_ctl = IGU_REG_COMMAND_REG_CTRL;
u32 igu_addr_ack = IGU_REG_CSTORM_TYPE_0_SB_CLEANUP + (idu_sb_id/32)*4;
u32 sb_bit = 1 << (idu_sb_id%32);
- u32 func_encode = BP_FUNC(bp) |
+ u32 func_encode = func |
((is_Pf == true ? 1 : 0) << IGU_FID_ENCODE_IS_PF_SHIFT);
u32 addr_encode = IGU_CMD_E2_PROD_UPD_BASE + idu_sb_id;
@@ -590,15 +675,6 @@ static inline void bnx2x_hc_ack_sb(struct bnx2x *bp, u8 sb_id,
barrier();
}
-static inline void bnx2x_igu_ack_sb(struct bnx2x *bp, u8 igu_sb_id, u8 segment,
- u16 index, u8 op, u8 update)
-{
- u32 igu_addr = BAR_IGU_INTMEM + (IGU_CMD_INT_ACK_BASE + igu_sb_id)*8;
-
- bnx2x_igu_ack_sb_gen(bp, igu_sb_id, segment, index, op, update,
- igu_addr);
-}
-
static inline void bnx2x_ack_sb(struct bnx2x *bp, u8 igu_sb_id, u8 storm,
u16 index, u8 op, u8 update)
{
@@ -653,21 +729,22 @@ static inline u16 bnx2x_ack_int(struct bnx2x *bp)
return bnx2x_igu_ack_int(bp);
}
-static inline int bnx2x_has_tx_work_unload(struct bnx2x_fastpath *fp)
+static inline int bnx2x_has_tx_work_unload(struct bnx2x_fp_txdata *txdata)
{
/* Tell compiler that consumer and producer can change */
barrier();
- return fp->tx_pkt_prod != fp->tx_pkt_cons;
+ return txdata->tx_pkt_prod != txdata->tx_pkt_cons;
}
-static inline u16 bnx2x_tx_avail(struct bnx2x_fastpath *fp)
+static inline u16 bnx2x_tx_avail(struct bnx2x *bp,
+ struct bnx2x_fp_txdata *txdata)
{
s16 used;
u16 prod;
u16 cons;
- prod = fp->tx_bd_prod;
- cons = fp->tx_bd_cons;
+ prod = txdata->tx_bd_prod;
+ cons = txdata->tx_bd_cons;
/* NUM_TX_RINGS = number of "next-page" entries
It will be used as a threshold */
@@ -675,21 +752,30 @@ static inline u16 bnx2x_tx_avail(struct bnx2x_fastpath *fp)
#ifdef BNX2X_STOP_ON_ERROR
WARN_ON(used < 0);
- WARN_ON(used > fp->bp->tx_ring_size);
- WARN_ON((fp->bp->tx_ring_size - used) > MAX_TX_AVAIL);
+ WARN_ON(used > bp->tx_ring_size);
+ WARN_ON((bp->tx_ring_size - used) > MAX_TX_AVAIL);
#endif
- return (s16)(fp->bp->tx_ring_size) - used;
+ return (s16)(bp->tx_ring_size) - used;
}
-static inline int bnx2x_has_tx_work(struct bnx2x_fastpath *fp)
+static inline int bnx2x_tx_queue_has_work(struct bnx2x_fp_txdata *txdata)
{
u16 hw_cons;
/* Tell compiler that status block fields can change */
barrier();
- hw_cons = le16_to_cpu(*fp->tx_cons_sb);
- return hw_cons != fp->tx_pkt_cons;
+ hw_cons = le16_to_cpu(*txdata->tx_cons_sb);
+ return hw_cons != txdata->tx_pkt_cons;
+}
+
+static inline bool bnx2x_has_tx_work(struct bnx2x_fastpath *fp)
+{
+ u8 cos;
+ for_each_cos_in_tx_queue(fp, cos)
+ if (bnx2x_tx_queue_has_work(&fp->txdata[cos]))
+ return true;
+ return false;
}
static inline int bnx2x_has_rx_work(struct bnx2x_fastpath *fp)
@@ -705,7 +791,7 @@ static inline int bnx2x_has_rx_work(struct bnx2x_fastpath *fp)
}
/**
- * disables tx from stack point of view
+ * bnx2x_tx_disable - disables tx from stack point of view
*
* @bp: driver handle
*/
@@ -740,7 +826,7 @@ static inline void bnx2x_add_all_napi(struct bnx2x *bp)
int i;
/* Add NAPI objects */
- for_each_napi_queue(bp, i)
+ for_each_rx_queue(bp, i)
netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
bnx2x_poll, BNX2X_NAPI_WEIGHT);
}
@@ -749,7 +835,7 @@ static inline void bnx2x_del_all_napi(struct bnx2x *bp)
{
int i;
- for_each_napi_queue(bp, i)
+ for_each_rx_queue(bp, i)
netif_napi_del(&bnx2x_fp(bp, i, napi));
}
@@ -779,7 +865,7 @@ static inline void bnx2x_clear_sge_mask_next_elems(struct bnx2x_fastpath *fp)
int idx = RX_SGE_CNT * i - 1;
for (j = 0; j < 2; j++) {
- SGE_MASK_CLEAR_BIT(fp, idx);
+ BIT_VEC64_CLEAR_BIT(fp->sge_mask, idx);
idx--;
}
}
@@ -789,7 +875,7 @@ static inline void bnx2x_init_sge_ring_bit_mask(struct bnx2x_fastpath *fp)
{
/* Set the mask to all 1-s: it's faster to compare to 0 than to 0xf-s */
memset(fp->sge_mask, 0xff,
- (NUM_RX_SGE >> RX_SGE_MASK_ELEM_SHIFT)*sizeof(u64));
+ (NUM_RX_SGE >> BIT_VEC64_ELEM_SHIFT)*sizeof(u64));
/* Clear the two last indices in the page to 1:
these are the indices that correspond to the "next" element,
@@ -861,22 +947,69 @@ static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp,
static inline void bnx2x_reuse_rx_skb(struct bnx2x_fastpath *fp,
u16 cons, u16 prod)
{
- struct bnx2x *bp = fp->bp;
struct sw_rx_bd *cons_rx_buf = &fp->rx_buf_ring[cons];
struct sw_rx_bd *prod_rx_buf = &fp->rx_buf_ring[prod];
struct eth_rx_bd *cons_bd = &fp->rx_desc_ring[cons];
struct eth_rx_bd *prod_bd = &fp->rx_desc_ring[prod];
- dma_sync_single_for_device(&bp->pdev->dev,
- dma_unmap_addr(cons_rx_buf, mapping),
- RX_COPY_THRESH, DMA_FROM_DEVICE);
-
- prod_rx_buf->skb = cons_rx_buf->skb;
dma_unmap_addr_set(prod_rx_buf, mapping,
dma_unmap_addr(cons_rx_buf, mapping));
+ prod_rx_buf->skb = cons_rx_buf->skb;
*prod_bd = *cons_bd;
}
+/************************* Init ******************************************/
+
+/**
+ * bnx2x_func_start - init function
+ *
+ * @bp: driver handle
+ *
+ * Must be called before sending CLIENT_SETUP for the first client.
+ */
+static inline int bnx2x_func_start(struct bnx2x *bp)
+{
+ struct bnx2x_func_state_params func_params = {0};
+ struct bnx2x_func_start_params *start_params =
+ &func_params.params.start;
+
+ /* Prepare parameters for function state transitions */
+ __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags);
+
+ func_params.f_obj = &bp->func_obj;
+ func_params.cmd = BNX2X_F_CMD_START;
+
+ /* Function parameters */
+ start_params->mf_mode = bp->mf_mode;
+ start_params->sd_vlan_tag = bp->mf_ov;
+ if (CHIP_IS_E1x(bp))
+ start_params->network_cos_mode = OVERRIDE_COS;
+ else
+ start_params->network_cos_mode = STATIC_COS;
+
+ return bnx2x_func_state_change(bp, &func_params);
+}
+
+
+/**
+ * bnx2x_set_fw_mac_addr - fill in a MAC address in FW format
+ *
+ * @fw_hi: pointer to upper part
+ * @fw_mid: pointer to middle part
+ * @fw_lo: pointer to lower part
+ * @mac: pointer to MAC address
+ */
+static inline void bnx2x_set_fw_mac_addr(u16 *fw_hi, u16 *fw_mid, u16 *fw_lo,
+ u8 *mac)
+{
+ ((u8 *)fw_hi)[0] = mac[1];
+ ((u8 *)fw_hi)[1] = mac[0];
+ ((u8 *)fw_mid)[0] = mac[3];
+ ((u8 *)fw_mid)[1] = mac[2];
+ ((u8 *)fw_lo)[0] = mac[5];
+ ((u8 *)fw_lo)[1] = mac[4];
+}
+
static inline void bnx2x_free_rx_sge_range(struct bnx2x *bp,
struct bnx2x_fastpath *fp, int last)
{
@@ -895,57 +1028,58 @@ static inline void bnx2x_free_tpa_pool(struct bnx2x *bp,
int i;
for (i = 0; i < last; i++) {
- struct sw_rx_bd *rx_buf = &(fp->tpa_pool[i]);
- struct sk_buff *skb = rx_buf->skb;
+ struct bnx2x_agg_info *tpa_info = &fp->tpa_info[i];
+ struct sw_rx_bd *first_buf = &tpa_info->first_buf;
+ struct sk_buff *skb = first_buf->skb;
if (skb == NULL) {
DP(NETIF_MSG_IFDOWN, "tpa bin %d empty on free\n", i);
continue;
}
-
- if (fp->tpa_state[i] == BNX2X_TPA_START)
+ if (tpa_info->tpa_state == BNX2X_TPA_START)
dma_unmap_single(&bp->pdev->dev,
- dma_unmap_addr(rx_buf, mapping),
+ dma_unmap_addr(first_buf, mapping),
fp->rx_buf_size, DMA_FROM_DEVICE);
-
dev_kfree_skb(skb);
- rx_buf->skb = NULL;
+ first_buf->skb = NULL;
}
}
-static inline void bnx2x_init_tx_ring_one(struct bnx2x_fastpath *fp)
+static inline void bnx2x_init_tx_ring_one(struct bnx2x_fp_txdata *txdata)
{
int i;
for (i = 1; i <= NUM_TX_RINGS; i++) {
struct eth_tx_next_bd *tx_next_bd =
- &fp->tx_desc_ring[TX_DESC_CNT * i - 1].next_bd;
+ &txdata->tx_desc_ring[TX_DESC_CNT * i - 1].next_bd;
tx_next_bd->addr_hi =
- cpu_to_le32(U64_HI(fp->tx_desc_mapping +
+ cpu_to_le32(U64_HI(txdata->tx_desc_mapping +
BCM_PAGE_SIZE*(i % NUM_TX_RINGS)));
tx_next_bd->addr_lo =
- cpu_to_le32(U64_LO(fp->tx_desc_mapping +
+ cpu_to_le32(U64_LO(txdata->tx_desc_mapping +
BCM_PAGE_SIZE*(i % NUM_TX_RINGS)));
}
- SET_FLAG(fp->tx_db.data.header.header, DOORBELL_HDR_DB_TYPE, 1);
- fp->tx_db.data.zero_fill1 = 0;
- fp->tx_db.data.prod = 0;
+ SET_FLAG(txdata->tx_db.data.header.header, DOORBELL_HDR_DB_TYPE, 1);
+ txdata->tx_db.data.zero_fill1 = 0;
+ txdata->tx_db.data.prod = 0;
- fp->tx_pkt_prod = 0;
- fp->tx_pkt_cons = 0;
- fp->tx_bd_prod = 0;
- fp->tx_bd_cons = 0;
- fp->tx_pkt = 0;
+ txdata->tx_pkt_prod = 0;
+ txdata->tx_pkt_cons = 0;
+ txdata->tx_bd_prod = 0;
+ txdata->tx_bd_cons = 0;
+ txdata->tx_pkt = 0;
}
static inline void bnx2x_init_tx_rings(struct bnx2x *bp)
{
int i;
+ u8 cos;
for_each_tx_queue(bp, i)
- bnx2x_init_tx_ring_one(&bp->fp[i]);
+ for_each_cos_in_tx_queue(&bp->fp[i], cos)
+ bnx2x_init_tx_ring_one(&bp->fp[i].txdata[cos]);
}
static inline void bnx2x_set_next_page_rx_bd(struct bnx2x_fastpath *fp)
@@ -1038,31 +1172,220 @@ static inline int bnx2x_alloc_rx_bds(struct bnx2x_fastpath *fp,
return i - fp->eth_q_stats.rx_skb_alloc_failed;
}
+/* Statistics ID are global per chip/path, while Client IDs for E1x are per
+ * port.
+ */
+static inline u8 bnx2x_stats_id(struct bnx2x_fastpath *fp)
+{
+ if (!CHIP_IS_E1x(fp->bp))
+ return fp->cl_id;
+ else
+ return fp->cl_id + BP_PORT(fp->bp) * FP_SB_MAX_E1x;
+}
+
+static inline void bnx2x_init_vlan_mac_fp_objs(struct bnx2x_fastpath *fp,
+ bnx2x_obj_type obj_type)
+{
+ struct bnx2x *bp = fp->bp;
+
+ /* Configure classification DBs */
+ bnx2x_init_mac_obj(bp, &fp->mac_obj, fp->cl_id, fp->cid,
+ BP_FUNC(bp), bnx2x_sp(bp, mac_rdata),
+ bnx2x_sp_mapping(bp, mac_rdata),
+ BNX2X_FILTER_MAC_PENDING,
+ &bp->sp_state, obj_type,
+ &bp->macs_pool);
+}
+
+/**
+ * bnx2x_get_path_func_num - get number of active functions
+ *
+ * @bp: driver handle
+ *
+ * Calculates the number of active (not hidden) functions on the
+ * current path.
+ */
+static inline u8 bnx2x_get_path_func_num(struct bnx2x *bp)
+{
+ u8 func_num = 0, i;
+
+ /* 57710 has only one function per-port */
+ if (CHIP_IS_E1(bp))
+ return 1;
+
+ /* Calculate a number of functions enabled on the current
+ * PATH/PORT.
+ */
+ if (CHIP_REV_IS_SLOW(bp)) {
+ if (IS_MF(bp))
+ func_num = 4;
+ else
+ func_num = 2;
+ } else {
+ for (i = 0; i < E1H_FUNC_MAX / 2; i++) {
+ u32 func_config =
+ MF_CFG_RD(bp,
+ func_mf_config[BP_PORT(bp) + 2 * i].
+ config);
+ func_num +=
+ ((func_config & FUNC_MF_CFG_FUNC_HIDE) ? 0 : 1);
+ }
+ }
+
+ WARN_ON(!func_num);
+
+ return func_num;
+}
+
+static inline void bnx2x_init_bp_objs(struct bnx2x *bp)
+{
+ /* RX_MODE controlling object */
+ bnx2x_init_rx_mode_obj(bp, &bp->rx_mode_obj);
+
+ /* multicast configuration controlling object */
+ bnx2x_init_mcast_obj(bp, &bp->mcast_obj, bp->fp->cl_id, bp->fp->cid,
+ BP_FUNC(bp), BP_FUNC(bp),
+ bnx2x_sp(bp, mcast_rdata),
+ bnx2x_sp_mapping(bp, mcast_rdata),
+ BNX2X_FILTER_MCAST_PENDING, &bp->sp_state,
+ BNX2X_OBJ_TYPE_RX);
+
+ /* Setup CAM credit pools */
+ bnx2x_init_mac_credit_pool(bp, &bp->macs_pool, BP_FUNC(bp),
+ bnx2x_get_path_func_num(bp));
+
+ /* RSS configuration object */
+ bnx2x_init_rss_config_obj(bp, &bp->rss_conf_obj, bp->fp->cl_id,
+ bp->fp->cid, BP_FUNC(bp), BP_FUNC(bp),
+ bnx2x_sp(bp, rss_rdata),
+ bnx2x_sp_mapping(bp, rss_rdata),
+ BNX2X_FILTER_RSS_CONF_PENDING, &bp->sp_state,
+ BNX2X_OBJ_TYPE_RX);
+}
+
+static inline u8 bnx2x_fp_qzone_id(struct bnx2x_fastpath *fp)
+{
+ if (CHIP_IS_E1x(fp->bp))
+ return fp->cl_id + BP_PORT(fp->bp) * ETH_MAX_RX_CLIENTS_E1H;
+ else
+ return fp->cl_id;
+}
+
+static inline u32 bnx2x_rx_ustorm_prods_offset(struct bnx2x_fastpath *fp)
+{
+ struct bnx2x *bp = fp->bp;
+
+ if (!CHIP_IS_E1x(bp))
+ return USTORM_RX_PRODS_E2_OFFSET(fp->cl_qzone_id);
+ else
+ return USTORM_RX_PRODS_E1X_OFFSET(BP_PORT(bp), fp->cl_id);
+}
+
+static inline void bnx2x_init_txdata(struct bnx2x *bp,
+ struct bnx2x_fp_txdata *txdata, u32 cid, int txq_index,
+ __le16 *tx_cons_sb)
+{
+ txdata->cid = cid;
+ txdata->txq_index = txq_index;
+ txdata->tx_cons_sb = tx_cons_sb;
+
+ DP(BNX2X_MSG_SP, "created tx data cid %d, txq %d\n",
+ txdata->cid, txdata->txq_index);
+}
+
#ifdef BCM_CNIC
+static inline u8 bnx2x_cnic_eth_cl_id(struct bnx2x *bp, u8 cl_idx)
+{
+ return bp->cnic_base_cl_id + cl_idx +
+ (bp->pf_num >> 1) * BNX2X_MAX_CNIC_ETH_CL_ID_IDX;
+}
+
+static inline u8 bnx2x_cnic_fw_sb_id(struct bnx2x *bp)
+{
+
+ /* the 'first' id is allocated for the cnic */
+ return bp->base_fw_ndsb;
+}
+
+static inline u8 bnx2x_cnic_igu_sb_id(struct bnx2x *bp)
+{
+ return bp->igu_base_sb;
+}
+
+
static inline void bnx2x_init_fcoe_fp(struct bnx2x *bp)
{
- bnx2x_fcoe(bp, cl_id) = BNX2X_FCOE_ETH_CL_ID +
- BP_E1HVN(bp) * NONE_ETH_CONTEXT_USE;
+ struct bnx2x_fastpath *fp = bnx2x_fcoe_fp(bp);
+ unsigned long q_type = 0;
+
+ bnx2x_fcoe(bp, cl_id) = bnx2x_cnic_eth_cl_id(bp,
+ BNX2X_FCOE_ETH_CL_ID_IDX);
+ /** Current BNX2X_FCOE_ETH_CID deffinition implies not more than
+ * 16 ETH clients per function when CNIC is enabled!
+ *
+ * Fix it ASAP!!!
+ */
bnx2x_fcoe(bp, cid) = BNX2X_FCOE_ETH_CID;
bnx2x_fcoe(bp, fw_sb_id) = DEF_SB_ID;
bnx2x_fcoe(bp, igu_sb_id) = bp->igu_dsb_id;
- bnx2x_fcoe(bp, bp) = bp;
- bnx2x_fcoe(bp, state) = BNX2X_FP_STATE_CLOSED;
- bnx2x_fcoe(bp, index) = FCOE_IDX;
bnx2x_fcoe(bp, rx_cons_sb) = BNX2X_FCOE_L2_RX_INDEX;
- bnx2x_fcoe(bp, tx_cons_sb) = BNX2X_FCOE_L2_TX_INDEX;
+
+ bnx2x_init_txdata(bp, &bnx2x_fcoe(bp, txdata[0]),
+ fp->cid, FCOE_TXQ_IDX(bp), BNX2X_FCOE_L2_TX_INDEX);
+
+ DP(BNX2X_MSG_SP, "created fcoe tx data (fp index %d)\n", fp->index);
+
/* qZone id equals to FW (per path) client id */
- bnx2x_fcoe(bp, cl_qzone_id) = bnx2x_fcoe(bp, cl_id) +
- BP_PORT(bp)*(CHIP_IS_E2(bp) ? ETH_MAX_RX_CLIENTS_E2 :
- ETH_MAX_RX_CLIENTS_E1H);
+ bnx2x_fcoe(bp, cl_qzone_id) = bnx2x_fp_qzone_id(fp);
/* init shortcut */
- bnx2x_fcoe(bp, ustorm_rx_prods_offset) = CHIP_IS_E2(bp) ?
- USTORM_RX_PRODS_E2_OFFSET(bnx2x_fcoe(bp, cl_qzone_id)) :
- USTORM_RX_PRODS_E1X_OFFSET(BP_PORT(bp), bnx2x_fcoe_fp(bp)->cl_id);
+ bnx2x_fcoe(bp, ustorm_rx_prods_offset) =
+ bnx2x_rx_ustorm_prods_offset(fp);
+
+ /* Configure Queue State object */
+ __set_bit(BNX2X_Q_TYPE_HAS_RX, &q_type);
+ __set_bit(BNX2X_Q_TYPE_HAS_TX, &q_type);
+ /* No multi-CoS for FCoE L2 client */
+ BUG_ON(fp->max_cos != 1);
+
+ bnx2x_init_queue_obj(bp, &fp->q_obj, fp->cl_id, &fp->cid, 1,
+ BP_FUNC(bp), bnx2x_sp(bp, q_rdata),
+ bnx2x_sp_mapping(bp, q_rdata), q_type);
+
+ DP(NETIF_MSG_IFUP, "queue[%d]: bnx2x_init_sb(%p,%p) cl_id %d fw_sb %d "
+ "igu_sb %d\n",
+ fp->index, bp, fp->status_blk.e2_sb, fp->cl_id, fp->fw_sb_id,
+ fp->igu_sb_id);
}
#endif
+static inline int bnx2x_clean_tx_queue(struct bnx2x *bp,
+ struct bnx2x_fp_txdata *txdata)
+{
+ int cnt = 1000;
+
+ while (bnx2x_has_tx_work_unload(txdata)) {
+ if (!cnt) {
+ BNX2X_ERR("timeout waiting for queue[%d]: "
+ "txdata->tx_pkt_prod(%d) != txdata->tx_pkt_cons(%d)\n",
+ txdata->txq_index, txdata->tx_pkt_prod,
+ txdata->tx_pkt_cons);
+#ifdef BNX2X_STOP_ON_ERROR
+ bnx2x_panic();
+ return -EBUSY;
+#else
+ break;
+#endif
+ }
+ cnt--;
+ usleep_range(1000, 1000);
+ }
+
+ return 0;
+}
+
+int bnx2x_get_link_cfg_idx(struct bnx2x *bp);
+
static inline void __storm_memset_struct(struct bnx2x *bp,
u32 addr, size_t size, u32 *data)
{
@@ -1071,42 +1394,78 @@ static inline void __storm_memset_struct(struct bnx2x *bp,
REG_WR(bp, addr + (i * 4), data[i]);
}
-static inline void storm_memset_mac_filters(struct bnx2x *bp,
- struct tstorm_eth_mac_filter_config *mac_filters,
- u16 abs_fid)
+static inline void storm_memset_func_cfg(struct bnx2x *bp,
+ struct tstorm_eth_function_common_config *tcfg,
+ u16 abs_fid)
{
- size_t size = sizeof(struct tstorm_eth_mac_filter_config);
+ size_t size = sizeof(struct tstorm_eth_function_common_config);
u32 addr = BAR_TSTRORM_INTMEM +
- TSTORM_MAC_FILTER_CONFIG_OFFSET(abs_fid);
+ TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(abs_fid);
- __storm_memset_struct(bp, addr, size, (u32 *)mac_filters);
+ __storm_memset_struct(bp, addr, size, (u32 *)tcfg);
}
static inline void storm_memset_cmng(struct bnx2x *bp,
struct cmng_struct_per_port *cmng,
u8 port)
{
- size_t size =
- sizeof(struct rate_shaping_vars_per_port) +
- sizeof(struct fairness_vars_per_port) +
- sizeof(struct safc_struct_per_port) +
- sizeof(struct pfc_struct_per_port);
+ size_t size = sizeof(struct cmng_struct_per_port);
u32 addr = BAR_XSTRORM_INTMEM +
XSTORM_CMNG_PER_PORT_VARS_OFFSET(port);
__storm_memset_struct(bp, addr, size, (u32 *)cmng);
+}
- addr += size + 4 /* SKIP DCB+LLFC */;
- size = sizeof(struct cmng_struct_per_port) -
- size /* written */ - 4 /*skipped*/;
+/**
+ * bnx2x_wait_sp_comp - wait for the outstanding SP commands.
+ *
+ * @bp: driver handle
+ * @mask: bits that need to be cleared
+ */
+static inline bool bnx2x_wait_sp_comp(struct bnx2x *bp, unsigned long mask)
+{
+ int tout = 5000; /* Wait for 5 secs tops */
+
+ while (tout--) {
+ smp_mb();
+ netif_addr_lock_bh(bp->dev);
+ if (!(bp->sp_state & mask)) {
+ netif_addr_unlock_bh(bp->dev);
+ return true;
+ }
+ netif_addr_unlock_bh(bp->dev);
+
+ usleep_range(1000, 1000);
+ }
+
+ smp_mb();
- __storm_memset_struct(bp, addr, size,
- (u32 *)(cmng->traffic_type_to_priority_cos));
+ netif_addr_lock_bh(bp->dev);
+ if (bp->sp_state & mask) {
+ BNX2X_ERR("Filtering completion timed out. sp_state 0x%lx, "
+ "mask 0x%lx\n", bp->sp_state, mask);
+ netif_addr_unlock_bh(bp->dev);
+ return false;
+ }
+ netif_addr_unlock_bh(bp->dev);
+
+ return true;
}
-/* HW Lock for shared dual port PHYs */
+/**
+ * bnx2x_set_ctx_validation - set CDU context validation values
+ *
+ * @bp: driver handle
+ * @cxt: context of the connection on the host memory
+ * @cid: SW CID of the connection to be configured
+ */
+void bnx2x_set_ctx_validation(struct bnx2x *bp, struct eth_context *cxt,
+ u32 cid);
+
+void bnx2x_update_coalesce_sb_index(struct bnx2x *bp, u8 fw_sb_id,
+ u8 sb_index, u8 disable, u16 usec);
void bnx2x_acquire_phy_lock(struct bnx2x *bp);
void bnx2x_release_phy_lock(struct bnx2x *bp);
@@ -1122,8 +1481,8 @@ static inline u16 bnx2x_extract_max_cfg(struct bnx2x *bp, u32 mf_cfg)
u16 max_cfg = (mf_cfg & FUNC_MF_CFG_MAX_BW_MASK) >>
FUNC_MF_CFG_MAX_BW_SHIFT;
if (!max_cfg) {
- BNX2X_ERR("Illegal configuration detected for Max BW - "
- "using 100 instead\n");
+ DP(NETIF_MSG_LINK,
+ "Max BW configured to 0 - using 100 instead\n");
max_cfg = 100;
}
return max_cfg;
diff --git a/drivers/net/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c
index 410a49e571ac..51bd7485ab18 100644
--- a/drivers/net/bnx2x/bnx2x_dcb.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c
@@ -16,23 +16,24 @@
* Written by: Dmitry Kravkov
*
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/errno.h>
-#ifdef BCM_DCBNL
-#include <linux/dcbnl.h>
-#endif
+#include <linux/rtnetlink.h>
+#include <net/dcbnl.h>
#include "bnx2x.h"
#include "bnx2x_cmn.h"
#include "bnx2x_dcb.h"
-
/* forward declarations of dcbx related functions */
-static void bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp);
+static int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp);
static void bnx2x_pfc_set_pfc(struct bnx2x *bp);
static void bnx2x_dcbx_update_ets_params(struct bnx2x *bp);
-static void bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp);
+static int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp);
static void bnx2x_dcbx_get_ets_pri_pg_tbl(struct bnx2x *bp,
u32 *set_configuration_ets_pg,
u32 *pri_pg_tbl);
@@ -47,34 +48,56 @@ static void bnx2x_dcbx_separate_pauseable_from_non(struct bnx2x *bp,
struct cos_help_data *cos_data,
u32 *pg_pri_orginal_spread,
struct dcbx_ets_feature *ets);
-static void bnx2x_pfc_fw_struct_e2(struct bnx2x *bp);
+static void bnx2x_dcbx_fw_struct(struct bnx2x *bp,
+ struct bnx2x_func_tx_start_params*);
+
+/* helpers: read/write len bytes from addr into buff by REG_RD/REG_WR */
+static void bnx2x_read_data(struct bnx2x *bp, u32 *buff,
+ u32 addr, u32 len)
+{
+ int i;
+ for (i = 0; i < len; i += 4, buff++)
+ *buff = REG_RD(bp, addr + i);
+}
+static void bnx2x_write_data(struct bnx2x *bp, u32 *buff,
+ u32 addr, u32 len)
+{
+ int i;
+ for (i = 0; i < len; i += 4, buff++)
+ REG_WR(bp, addr + i, *buff);
+}
static void bnx2x_pfc_set(struct bnx2x *bp)
{
struct bnx2x_nig_brb_pfc_port_params pfc_params = {0};
u32 pri_bit, val = 0;
- u8 pri;
+ int i;
- /* Tx COS configuration */
- if (bp->dcbx_port_params.ets.cos_params[0].pauseable)
- pfc_params.rx_cos0_priority_mask =
- bp->dcbx_port_params.ets.cos_params[0].pri_bitmask;
- if (bp->dcbx_port_params.ets.cos_params[1].pauseable)
- pfc_params.rx_cos1_priority_mask =
- bp->dcbx_port_params.ets.cos_params[1].pri_bitmask;
+ pfc_params.num_of_rx_cos_priority_mask =
+ bp->dcbx_port_params.ets.num_of_cos;
+ /* Tx COS configuration */
+ for (i = 0; i < bp->dcbx_port_params.ets.num_of_cos; i++)
+ /*
+ * We configure only the pauseable bits (non pauseable aren't
+ * configured at all) it's done to avoid false pauses from
+ * network
+ */
+ pfc_params.rx_cos_priority_mask[i] =
+ bp->dcbx_port_params.ets.cos_params[i].pri_bitmask
+ & DCBX_PFC_PRI_PAUSE_MASK(bp);
- /**
+ /*
* Rx COS configuration
* Changing PFC RX configuration .
* In RX COS0 will always be configured to lossy and COS1 to lossless
*/
- for (pri = 0 ; pri < MAX_PFC_PRIORITIES ; pri++) {
- pri_bit = 1 << pri;
+ for (i = 0 ; i < MAX_PFC_PRIORITIES ; i++) {
+ pri_bit = 1 << i;
if (pri_bit & DCBX_PFC_PRI_PAUSE_MASK(bp))
- val |= 1 << (pri * 4);
+ val |= 1 << (i * 4);
}
pfc_params.pkt_priority_to_cos = val;
@@ -200,7 +223,11 @@ static void bnx2x_dcbx_get_ap_feature(struct bnx2x *bp,
if (GET_FLAGS(error, DCBX_LOCAL_APP_ERROR))
DP(NETIF_MSG_LINK, "DCBX_LOCAL_APP_ERROR\n");
- if (app->enabled && !GET_FLAGS(error, DCBX_LOCAL_APP_ERROR)) {
+ if (GET_FLAGS(error, DCBX_LOCAL_APP_MISMATCH))
+ DP(NETIF_MSG_LINK, "DCBX_LOCAL_APP_MISMATCH\n");
+
+ if (app->enabled &&
+ !GET_FLAGS(error, DCBX_LOCAL_APP_ERROR | DCBX_LOCAL_APP_MISMATCH)) {
bp->dcbx_port_params.app.enabled = true;
@@ -253,12 +280,11 @@ static void bnx2x_dcbx_get_ets_feature(struct bnx2x *bp,
/* Clean up old settings of ets on COS */
- for (i = 0; i < E2_NUM_OF_COS ; i++) {
-
+ for (i = 0; i < ARRAY_SIZE(bp->dcbx_port_params.ets.cos_params) ; i++) {
cos_params[i].pauseable = false;
- cos_params[i].strict = BNX2X_DCBX_COS_NOT_STRICT;
+ cos_params[i].strict = BNX2X_DCBX_STRICT_INVALID;
cos_params[i].bw_tbl = DCBX_INVALID_COS_BW;
- cos_params[i].pri_bitmask = DCBX_PFC_PRI_GET_NON_PAUSE(bp, 0);
+ cos_params[i].pri_bitmask = 0;
}
if (bp->dcbx_port_params.app.enabled &&
@@ -296,7 +322,7 @@ static void bnx2x_dcbx_get_pfc_feature(struct bnx2x *bp,
DP(NETIF_MSG_LINK, "DCBX_LOCAL_PFC_ERROR\n");
if (bp->dcbx_port_params.app.enabled &&
- !GET_FLAGS(error, DCBX_LOCAL_PFC_ERROR) &&
+ !GET_FLAGS(error, DCBX_LOCAL_PFC_ERROR | DCBX_LOCAL_PFC_MISMATCH) &&
pfc->enabled) {
bp->dcbx_port_params.pfc.enabled = true;
bp->dcbx_port_params.pfc.priority_non_pauseable_mask =
@@ -308,6 +334,32 @@ static void bnx2x_dcbx_get_pfc_feature(struct bnx2x *bp,
}
}
+/* maps unmapped priorities to to the same COS as L2 */
+static void bnx2x_dcbx_map_nw(struct bnx2x *bp)
+{
+ int i;
+ u32 unmapped = (1 << MAX_PFC_PRIORITIES) - 1; /* all ones */
+ u32 *ttp = bp->dcbx_port_params.app.traffic_type_priority;
+ u32 nw_prio = 1 << ttp[LLFC_TRAFFIC_TYPE_NW];
+ struct bnx2x_dcbx_cos_params *cos_params =
+ bp->dcbx_port_params.ets.cos_params;
+
+ /* get unmapped priorities by clearing mapped bits */
+ for (i = 0; i < LLFC_DRIVER_TRAFFIC_TYPE_MAX; i++)
+ unmapped &= ~(1 << ttp[i]);
+
+ /* find cos for nw prio and extend it with unmapped */
+ for (i = 0; i < ARRAY_SIZE(bp->dcbx_port_params.ets.cos_params); i++) {
+ if (cos_params[i].pri_bitmask & nw_prio) {
+ /* extend the bitmask with unmapped */
+ DP(NETIF_MSG_LINK,
+ "cos %d extended with 0x%08x\n", i, unmapped);
+ cos_params[i].pri_bitmask |= unmapped;
+ break;
+ }
+ }
+}
+
static void bnx2x_get_dcbx_drv_param(struct bnx2x *bp,
struct dcbx_features *features,
u32 error)
@@ -317,6 +369,8 @@ static void bnx2x_get_dcbx_drv_param(struct bnx2x *bp,
bnx2x_dcbx_get_pfc_feature(bp, &features->pfc, error);
bnx2x_dcbx_get_ets_feature(bp, &features->ets, error);
+
+ bnx2x_dcbx_map_nw(bp);
}
#define DCBX_LOCAL_MIB_MAX_TRY_READ (100)
@@ -325,8 +379,8 @@ static int bnx2x_dcbx_read_mib(struct bnx2x *bp,
u32 offset,
int read_mib_type)
{
- int max_try_read = 0, i;
- u32 *buff, mib_size, prefix_seq_num, suffix_seq_num;
+ int max_try_read = 0;
+ u32 mib_size, prefix_seq_num, suffix_seq_num;
struct lldp_remote_mib *remote_mib ;
struct lldp_local_mib *local_mib;
@@ -345,9 +399,7 @@ static int bnx2x_dcbx_read_mib(struct bnx2x *bp,
offset += BP_PORT(bp) * mib_size;
do {
- buff = base_mib_addr;
- for (i = 0; i < mib_size; i += 4, buff++)
- *buff = REG_RD(bp, offset + i);
+ bnx2x_read_data(bp, base_mib_addr, offset, mib_size);
max_try_read++;
@@ -378,60 +430,50 @@ static int bnx2x_dcbx_read_mib(struct bnx2x *bp,
static void bnx2x_pfc_set_pfc(struct bnx2x *bp)
{
- if (CHIP_IS_E2(bp)) {
- if (BP_PORT(bp)) {
- BNX2X_ERR("4 port mode is not supported");
- return;
- }
+ if (bp->dcbx_port_params.pfc.enabled &&
+ !(bp->dcbx_error & DCBX_REMOTE_MIB_ERROR))
+ /*
+ * 1. Fills up common PFC structures if required
+ * 2. Configure NIG, MAC and BRB via the elink
+ */
+ bnx2x_pfc_set(bp);
+ else
+ bnx2x_pfc_clear(bp);
+}
- if (bp->dcbx_port_params.pfc.enabled)
+static int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp)
+{
+ struct bnx2x_func_state_params func_params = {0};
- /* 1. Fills up common PFC structures if required.*/
- /* 2. Configure NIG, MAC and BRB via the elink:
- * elink must first check if BMAC is not in reset
- * and only then configures the BMAC
- * Or, configure EMAC.
- */
- bnx2x_pfc_set(bp);
+ func_params.f_obj = &bp->func_obj;
+ func_params.cmd = BNX2X_F_CMD_TX_STOP;
- else
- bnx2x_pfc_clear(bp);
- }
+ DP(NETIF_MSG_LINK, "STOP TRAFFIC\n");
+ return bnx2x_func_state_change(bp, &func_params);
}
-static void bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp)
+static int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp)
{
- DP(NETIF_MSG_LINK, "sending STOP TRAFFIC\n");
- bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STOP_TRAFFIC,
- 0 /* connectionless */,
- 0 /* dataHi is zero */,
- 0 /* dataLo is zero */,
- 1 /* common */);
-}
+ struct bnx2x_func_state_params func_params = {0};
+ struct bnx2x_func_tx_start_params *tx_params =
+ &func_params.params.tx_start;
-static void bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp)
-{
- bnx2x_pfc_fw_struct_e2(bp);
- DP(NETIF_MSG_LINK, "sending START TRAFFIC\n");
- bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_START_TRAFFIC,
- 0, /* connectionless */
- U64_HI(bnx2x_sp_mapping(bp, pfc_config)),
- U64_LO(bnx2x_sp_mapping(bp, pfc_config)),
- 1 /* commmon */);
+ func_params.f_obj = &bp->func_obj;
+ func_params.cmd = BNX2X_F_CMD_TX_START;
+
+ bnx2x_dcbx_fw_struct(bp, tx_params);
+
+ DP(NETIF_MSG_LINK, "START TRAFFIC\n");
+ return bnx2x_func_state_change(bp, &func_params);
}
-static void bnx2x_dcbx_update_ets_params(struct bnx2x *bp)
+static void bnx2x_dcbx_2cos_limit_update_ets_config(struct bnx2x *bp)
{
struct bnx2x_dcbx_pg_params *ets = &(bp->dcbx_port_params.ets);
- u8 status = 0;
-
- bnx2x_ets_disabled(&bp->link_params);
-
- if (!ets->enabled)
- return;
+ int rc = 0;
- if ((ets->num_of_cos == 0) || (ets->num_of_cos > E2_NUM_OF_COS)) {
- BNX2X_ERR("illegal num of cos= %x", ets->num_of_cos);
+ if (ets->num_of_cos == 0 || ets->num_of_cos > DCBX_COS_MAX_NUM_E2) {
+ BNX2X_ERR("Illegal number of COSes %d\n", ets->num_of_cos);
return;
}
@@ -440,9 +482,9 @@ static void bnx2x_dcbx_update_ets_params(struct bnx2x *bp)
return;
/* sanity */
- if (((BNX2X_DCBX_COS_NOT_STRICT == ets->cos_params[0].strict) &&
+ if (((BNX2X_DCBX_STRICT_INVALID == ets->cos_params[0].strict) &&
(DCBX_INVALID_COS_BW == ets->cos_params[0].bw_tbl)) ||
- ((BNX2X_DCBX_COS_NOT_STRICT == ets->cos_params[1].strict) &&
+ ((BNX2X_DCBX_STRICT_INVALID == ets->cos_params[1].strict) &&
(DCBX_INVALID_COS_BW == ets->cos_params[1].bw_tbl))) {
BNX2X_ERR("all COS should have at least bw_limit or strict"
"ets->cos_params[0].strict= %x"
@@ -474,17 +516,71 @@ static void bnx2x_dcbx_update_ets_params(struct bnx2x *bp)
bnx2x_ets_bw_limit(&bp->link_params, bw_tbl_0, bw_tbl_1);
} else {
- if (ets->cos_params[0].strict == BNX2X_DCBX_COS_HIGH_STRICT)
- status = bnx2x_ets_strict(&bp->link_params, 0);
+ if (ets->cos_params[0].strict == BNX2X_DCBX_STRICT_COS_HIGHEST)
+ rc = bnx2x_ets_strict(&bp->link_params, 0);
else if (ets->cos_params[1].strict
- == BNX2X_DCBX_COS_HIGH_STRICT)
- status = bnx2x_ets_strict(&bp->link_params, 1);
-
- if (status)
+ == BNX2X_DCBX_STRICT_COS_HIGHEST)
+ rc = bnx2x_ets_strict(&bp->link_params, 1);
+ if (rc)
BNX2X_ERR("update_ets_params failed\n");
}
}
+/*
+ * In E3B0 the configuration may have more than 2 COS.
+ */
+void bnx2x_dcbx_update_ets_config(struct bnx2x *bp)
+{
+ struct bnx2x_dcbx_pg_params *ets = &(bp->dcbx_port_params.ets);
+ struct bnx2x_ets_params ets_params = { 0 };
+ u8 i;
+
+ ets_params.num_of_cos = ets->num_of_cos;
+
+ for (i = 0; i < ets->num_of_cos; i++) {
+ /* COS is SP */
+ if (ets->cos_params[i].strict != BNX2X_DCBX_STRICT_INVALID) {
+ if (ets->cos_params[i].bw_tbl != DCBX_INVALID_COS_BW) {
+ BNX2X_ERR("COS can't be not BW and not SP\n");
+ return;
+ }
+
+ ets_params.cos[i].state = bnx2x_cos_state_strict;
+ ets_params.cos[i].params.sp_params.pri =
+ ets->cos_params[i].strict;
+ } else { /* COS is BW */
+ if (ets->cos_params[i].bw_tbl == DCBX_INVALID_COS_BW) {
+ BNX2X_ERR("COS can't be not BW and not SP\n");
+ return;
+ }
+ ets_params.cos[i].state = bnx2x_cos_state_bw;
+ ets_params.cos[i].params.bw_params.bw =
+ (u8)ets->cos_params[i].bw_tbl;
+ }
+ }
+
+ /* Configure the ETS in HW */
+ if (bnx2x_ets_e3b0_config(&bp->link_params, &bp->link_vars,
+ &ets_params)) {
+ BNX2X_ERR("bnx2x_ets_e3b0_config failed\n");
+ bnx2x_ets_disabled(&bp->link_params, &bp->link_vars);
+ }
+}
+
+static void bnx2x_dcbx_update_ets_params(struct bnx2x *bp)
+{
+ bnx2x_ets_disabled(&bp->link_params, &bp->link_vars);
+
+ if (!bp->dcbx_port_params.ets.enabled ||
+ (bp->dcbx_error & DCBX_REMOTE_MIB_ERROR))
+ return;
+
+ if (CHIP_IS_E3B0(bp))
+ bnx2x_dcbx_update_ets_config(bp);
+ else
+ bnx2x_dcbx_2cos_limit_update_ets_config(bp);
+}
+
#ifdef BCM_DCBNL
static int bnx2x_dcbx_read_shmem_remote_mib(struct bnx2x *bp)
{
@@ -527,6 +623,7 @@ static int bnx2x_dcbx_read_shmem_neg_results(struct bnx2x *bp)
BNX2X_ERR("FW doesn't support dcbx_neg_res_offset\n");
return -EINVAL;
}
+
rc = bnx2x_dcbx_read_mib(bp, (u32 *)&local_mib, dcbx_neg_res_offset,
DCBX_READ_LOCAL_MIB);
@@ -563,15 +660,6 @@ u8 bnx2x_dcbx_dcbnl_app_idtype(struct dcbx_app_priority_entry *ent)
DCB_APP_IDTYPE_ETHTYPE;
}
-static inline
-void bnx2x_dcbx_invalidate_local_apps(struct bnx2x *bp)
-{
- int i;
- for (i = 0; i < DCBX_MAX_APP_PROTOCOL; i++)
- bp->dcbx_local_feat.app.app_pri_tbl[i].appBitfield &=
- ~DCBX_APP_ENTRY_VALID;
-}
-
int bnx2x_dcbnl_update_applist(struct bnx2x *bp, bool delall)
{
int i, err = 0;
@@ -597,32 +685,50 @@ int bnx2x_dcbnl_update_applist(struct bnx2x *bp, bool delall)
}
#endif
-void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state)
+static inline void bnx2x_update_drv_flags(struct bnx2x *bp, u32 flags, u32 set)
{
- switch (state) {
- case BNX2X_DCBX_STATE_NEG_RECEIVED:
-#ifdef BCM_CNIC
- if (bp->state != BNX2X_STATE_OPENING_WAIT4_LOAD) {
- struct cnic_ops *c_ops;
- struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
- bp->flags |= NO_ISCSI_OOO_FLAG | NO_ISCSI_FLAG;
- cp->drv_state |= CNIC_DRV_STATE_NO_ISCSI_OOO;
- cp->drv_state |= CNIC_DRV_STATE_NO_ISCSI;
-
- rcu_read_lock();
- c_ops = rcu_dereference(bp->cnic_ops);
- if (c_ops) {
- bnx2x_cnic_notify(bp, CNIC_CTL_STOP_ISCSI_CMD);
- rcu_read_unlock();
- return;
+ if (SHMEM2_HAS(bp, drv_flags)) {
+ u32 drv_flags;
+ bnx2x_acquire_hw_lock(bp, HW_LOCK_DRV_FLAGS);
+ drv_flags = SHMEM2_RD(bp, drv_flags);
+
+ if (set)
+ SET_FLAGS(drv_flags, flags);
+ else
+ RESET_FLAGS(drv_flags, flags);
+
+ SHMEM2_WR(bp, drv_flags, drv_flags);
+ DP(NETIF_MSG_HW, "drv_flags 0x%08x\n", drv_flags);
+ bnx2x_release_hw_lock(bp, HW_LOCK_DRV_FLAGS);
+ }
+}
+
+static inline void bnx2x_dcbx_update_tc_mapping(struct bnx2x *bp)
+{
+ u8 prio, cos;
+ for (cos = 0; cos < bp->dcbx_port_params.ets.num_of_cos; cos++) {
+ for (prio = 0; prio < BNX2X_MAX_PRIORITY; prio++) {
+ if (bp->dcbx_port_params.ets.cos_params[cos].pri_bitmask
+ & (1 << prio)) {
+ bp->prio_to_cos[prio] = cos;
+ DP(NETIF_MSG_LINK,
+ "tx_mapping %d --> %d\n", prio, cos);
}
- rcu_read_unlock();
}
+ }
- /* fall through if no CNIC initialized */
- case BNX2X_DCBX_STATE_ISCSI_STOPPED:
-#endif
+ /* setup tc must be called under rtnl lock, but we can't take it here
+ * as we are handling an attetntion on a work queue which must be
+ * flushed at some rtnl-locked contexts (e.g. if down)
+ */
+ if (!test_and_set_bit(BNX2X_SP_RTNL_SETUP_TC, &bp->sp_rtnl_state))
+ schedule_delayed_work(&bp->sp_rtnl_task, 0);
+}
+void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state)
+{
+ switch (state) {
+ case BNX2X_DCBX_STATE_NEG_RECEIVED:
{
DP(NETIF_MSG_LINK, "BNX2X_DCBX_STATE_NEG_RECEIVED\n");
#ifdef BCM_DCBNL
@@ -646,102 +752,53 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state)
bnx2x_get_dcbx_drv_param(bp, &bp->dcbx_local_feat,
bp->dcbx_error);
- if (bp->state != BNX2X_STATE_OPENING_WAIT4_LOAD) {
-#ifdef BCM_DCBNL
- /**
- * Add new app tlvs to dcbnl
- */
- bnx2x_dcbnl_update_applist(bp, false);
-#endif
- bnx2x_dcbx_stop_hw_tx(bp);
- return;
- }
- /* fall through */
+ /* mark DCBX result for PMF migration */
+ bnx2x_update_drv_flags(bp, DRV_FLAGS_DCB_CONFIGURED, 1);
#ifdef BCM_DCBNL
/**
- * Invalidate the local app tlvs if they are not added
- * to the dcbnl app list to avoid deleting them from
- * the list later on
+ * Add new app tlvs to dcbnl
*/
- bnx2x_dcbx_invalidate_local_apps(bp);
+ bnx2x_dcbnl_update_applist(bp, false);
#endif
+ bnx2x_dcbx_stop_hw_tx(bp);
+
+ /* reconfigure the netdevice with the results of the new
+ * dcbx negotiation.
+ */
+ bnx2x_dcbx_update_tc_mapping(bp);
+
+ return;
}
case BNX2X_DCBX_STATE_TX_PAUSED:
DP(NETIF_MSG_LINK, "BNX2X_DCBX_STATE_TX_PAUSED\n");
bnx2x_pfc_set_pfc(bp);
bnx2x_dcbx_update_ets_params(bp);
- if (bp->state != BNX2X_STATE_OPENING_WAIT4_LOAD) {
- bnx2x_dcbx_resume_hw_tx(bp);
- return;
- }
- /* fall through */
+ bnx2x_dcbx_resume_hw_tx(bp);
+ return;
case BNX2X_DCBX_STATE_TX_RELEASED:
DP(NETIF_MSG_LINK, "BNX2X_DCBX_STATE_TX_RELEASED\n");
- if (bp->state != BNX2X_STATE_OPENING_WAIT4_LOAD)
- bnx2x_fw_command(bp, DRV_MSG_CODE_DCBX_PMF_DRV_OK, 0);
-
+ bnx2x_fw_command(bp, DRV_MSG_CODE_DCBX_PMF_DRV_OK, 0);
+#ifdef BCM_DCBNL
+ /*
+ * Send a notification for the new negotiated parameters
+ */
+ dcbnl_cee_notify(bp->dev, RTM_GETDCB, DCB_CMD_CEE_GET, 0, 0);
+#endif
return;
default:
BNX2X_ERR("Unknown DCBX_STATE\n");
}
}
-
-#define LLDP_STATS_OFFSET(bp) (BP_PORT(bp)*\
- sizeof(struct lldp_dcbx_stat))
-
-/* calculate struct offset in array according to chip information */
-#define LLDP_PARAMS_OFFSET(bp) (BP_PORT(bp)*sizeof(struct lldp_params))
-
#define LLDP_ADMIN_MIB_OFFSET(bp) (PORT_MAX*sizeof(struct lldp_params) + \
BP_PORT(bp)*sizeof(struct lldp_admin_mib))
-static void bnx2x_dcbx_lldp_updated_params(struct bnx2x *bp,
- u32 dcbx_lldp_params_offset)
-{
- struct lldp_params lldp_params = {0};
- u32 i = 0, *buff = NULL;
- u32 offset = dcbx_lldp_params_offset + LLDP_PARAMS_OFFSET(bp);
-
- DP(NETIF_MSG_LINK, "lldp_offset 0x%x\n", offset);
-
- if ((bp->lldp_config_params.overwrite_settings ==
- BNX2X_DCBX_OVERWRITE_SETTINGS_ENABLE)) {
- /* Read the data first */
- buff = (u32 *)&lldp_params;
- for (i = 0; i < sizeof(struct lldp_params); i += 4, buff++)
- *buff = REG_RD(bp, (offset + i));
-
- lldp_params.msg_tx_hold =
- (u8)bp->lldp_config_params.msg_tx_hold;
- lldp_params.msg_fast_tx_interval =
- (u8)bp->lldp_config_params.msg_fast_tx;
- lldp_params.tx_crd_max =
- (u8)bp->lldp_config_params.tx_credit_max;
- lldp_params.msg_tx_interval =
- (u8)bp->lldp_config_params.msg_tx_interval;
- lldp_params.tx_fast =
- (u8)bp->lldp_config_params.tx_fast;
-
- /* Write the data.*/
- buff = (u32 *)&lldp_params;
- for (i = 0; i < sizeof(struct lldp_params); i += 4, buff++)
- REG_WR(bp, (offset + i) , *buff);
-
-
- } else if (BNX2X_DCBX_OVERWRITE_SETTINGS_ENABLE ==
- bp->lldp_config_params.overwrite_settings)
- bp->lldp_config_params.overwrite_settings =
- BNX2X_DCBX_OVERWRITE_SETTINGS_INVALID;
-}
-
static void bnx2x_dcbx_admin_mib_updated_params(struct bnx2x *bp,
u32 dcbx_lldp_params_offset)
{
struct lldp_admin_mib admin_mib;
u32 i, other_traf_type = PREDEFINED_APP_IDX_MAX, traf_type = 0;
- u32 *buff;
u32 offset = dcbx_lldp_params_offset + LLDP_ADMIN_MIB_OFFSET(bp);
/*shortcuts*/
@@ -749,18 +806,18 @@ static void bnx2x_dcbx_admin_mib_updated_params(struct bnx2x *bp,
struct bnx2x_config_dcbx_params *dp = &bp->dcbx_config_params;
memset(&admin_mib, 0, sizeof(struct lldp_admin_mib));
- buff = (u32 *)&admin_mib;
+
/* Read the data first */
- for (i = 0; i < sizeof(struct lldp_admin_mib); i += 4, buff++)
- *buff = REG_RD(bp, (offset + i));
+ bnx2x_read_data(bp, (u32 *)&admin_mib, offset,
+ sizeof(struct lldp_admin_mib));
if (bp->dcbx_enabled == BNX2X_DCBX_ENABLED_ON_NEG_ON)
SET_FLAGS(admin_mib.ver_cfg_flags, DCBX_DCBX_ENABLED);
else
RESET_FLAGS(admin_mib.ver_cfg_flags, DCBX_DCBX_ENABLED);
- if ((BNX2X_DCBX_OVERWRITE_SETTINGS_ENABLE ==
- dp->overwrite_settings)) {
+ if (dp->overwrite_settings == BNX2X_DCBX_OVERWRITE_SETTINGS_ENABLE) {
+
RESET_FLAGS(admin_mib.ver_cfg_flags, DCBX_CEE_VERSION_MASK);
admin_mib.ver_cfg_flags |=
(dp->admin_dcbx_version << DCBX_CEE_VERSION_SHIFT) &
@@ -856,19 +913,17 @@ static void bnx2x_dcbx_admin_mib_updated_params(struct bnx2x *bp,
af->app.default_pri = (u8)dp->admin_default_priority;
- } else if (BNX2X_DCBX_OVERWRITE_SETTINGS_ENABLE ==
- dp->overwrite_settings)
- dp->overwrite_settings = BNX2X_DCBX_OVERWRITE_SETTINGS_INVALID;
+ }
/* Write the data. */
- buff = (u32 *)&admin_mib;
- for (i = 0; i < sizeof(struct lldp_admin_mib); i += 4, buff++)
- REG_WR(bp, (offset + i), *buff);
+ bnx2x_write_data(bp, (u32 *)&admin_mib, offset,
+ sizeof(struct lldp_admin_mib));
+
}
void bnx2x_dcbx_set_state(struct bnx2x *bp, bool dcb_on, u32 dcbx_enabled)
{
- if (CHIP_IS_E2(bp) && !CHIP_MODE_IS_4_PORT(bp)) {
+ if (!CHIP_IS_E1x(bp) && !CHIP_IS_E3(bp)) {
bp->dcb_state = dcb_on;
bp->dcbx_enabled = dcbx_enabled;
} else {
@@ -966,7 +1021,7 @@ void bnx2x_dcbx_init(struct bnx2x *bp)
DP(NETIF_MSG_LINK, "dcb_state %d bp->port.pmf %d\n",
bp->dcb_state, bp->port.pmf);
- if (bp->dcb_state == BNX2X_DCB_STATE_ON && bp->port.pmf &&
+ if (bp->dcb_state == BNX2X_DCB_STATE_ON && bp->port.pmf &&
SHMEM2_HAS(bp, dcbx_lldp_params_offset)) {
dcbx_lldp_params_offset =
SHMEM2_RD(bp, dcbx_lldp_params_offset);
@@ -974,56 +1029,21 @@ void bnx2x_dcbx_init(struct bnx2x *bp)
DP(NETIF_MSG_LINK, "dcbx_lldp_params_offset 0x%x\n",
dcbx_lldp_params_offset);
- if (SHMEM_LLDP_DCBX_PARAMS_NONE != dcbx_lldp_params_offset) {
- bnx2x_dcbx_lldp_updated_params(bp,
- dcbx_lldp_params_offset);
+ bnx2x_update_drv_flags(bp, DRV_FLAGS_DCB_CONFIGURED, 0);
+ if (SHMEM_LLDP_DCBX_PARAMS_NONE != dcbx_lldp_params_offset) {
bnx2x_dcbx_admin_mib_updated_params(bp,
dcbx_lldp_params_offset);
- /* set default configuration BC has */
- bnx2x_dcbx_set_params(bp,
- BNX2X_DCBX_STATE_NEG_RECEIVED);
-
+ /* Let HW start negotiation */
bnx2x_fw_command(bp,
DRV_MSG_CODE_DCBX_ADMIN_PMF_MSG, 0);
}
}
}
-
-void bnx2x_dcb_init_intmem_pfc(struct bnx2x *bp)
-{
- struct priority_cos pricos[MAX_PFC_TRAFFIC_TYPES];
- u32 i = 0, addr;
- memset(pricos, 0, sizeof(pricos));
- /* Default initialization */
- for (i = 0; i < MAX_PFC_TRAFFIC_TYPES; i++)
- pricos[i].priority = LLFC_TRAFFIC_TYPE_TO_PRIORITY_UNMAPPED;
-
- /* Store per port struct to internal memory */
- addr = BAR_XSTRORM_INTMEM +
- XSTORM_CMNG_PER_PORT_VARS_OFFSET(BP_PORT(bp)) +
- offsetof(struct cmng_struct_per_port,
- traffic_type_to_priority_cos);
- __storm_memset_struct(bp, addr, sizeof(pricos), (u32 *)pricos);
-
-
- /* LLFC disabled.*/
- REG_WR8(bp , BAR_XSTRORM_INTMEM +
- XSTORM_CMNG_PER_PORT_VARS_OFFSET(BP_PORT(bp)) +
- offsetof(struct cmng_struct_per_port, llfc_mode),
- LLFC_MODE_NONE);
-
- /* DCBX disabled.*/
- REG_WR8(bp , BAR_XSTRORM_INTMEM +
- XSTORM_CMNG_PER_PORT_VARS_OFFSET(BP_PORT(bp)) +
- offsetof(struct cmng_struct_per_port, dcb_enabled),
- DCB_DISABLED);
-}
-
static void
bnx2x_dcbx_print_cos_params(struct bnx2x *bp,
- struct flow_control_configuration *pfc_fw_cfg)
+ struct bnx2x_func_tx_start_params *pfc_fw_cfg)
{
u8 pri = 0;
u8 cos = 0;
@@ -1171,7 +1191,7 @@ static void bnx2x_dcbx_separate_pauseable_from_non(struct bnx2x *bp,
/* If we join a group and one is strict
* than the bw rulls */
cos_data->data[entry].strict =
- BNX2X_DCBX_COS_HIGH_STRICT;
+ BNX2X_DCBX_STRICT_COS_HIGHEST;
}
if ((0 == cos_data->data[0].pri_join_mask) &&
(0 == cos_data->data[1].pri_join_mask))
@@ -1183,7 +1203,7 @@ static void bnx2x_dcbx_separate_pauseable_from_non(struct bnx2x *bp,
#define POWER_OF_2(x) ((0 != x) && (0 == (x & (x-1))))
#endif
-static void bxn2x_dcbx_single_pg_to_cos_params(struct bnx2x *bp,
+static void bnx2x_dcbx_2cos_limit_cee_single_pg_to_cos_params(struct bnx2x *bp,
struct pg_help_data *pg_help_data,
struct cos_help_data *cos_data,
u32 pri_join_mask,
@@ -1263,14 +1283,16 @@ static void bxn2x_dcbx_single_pg_to_cos_params(struct bnx2x *bp,
if (DCBX_PFC_PRI_GET_PAUSE(bp, pri_join_mask) >
DCBX_PFC_PRI_GET_NON_PAUSE(bp, pri_join_mask)) {
cos_data->data[0].strict =
- BNX2X_DCBX_COS_HIGH_STRICT;
+ BNX2X_DCBX_STRICT_COS_HIGHEST;
cos_data->data[1].strict =
- BNX2X_DCBX_COS_LOW_STRICT;
+ BNX2X_DCBX_STRICT_COS_NEXT_LOWER_PRI(
+ BNX2X_DCBX_STRICT_COS_HIGHEST);
} else {
cos_data->data[0].strict =
- BNX2X_DCBX_COS_LOW_STRICT;
+ BNX2X_DCBX_STRICT_COS_NEXT_LOWER_PRI(
+ BNX2X_DCBX_STRICT_COS_HIGHEST);
cos_data->data[1].strict =
- BNX2X_DCBX_COS_HIGH_STRICT;
+ BNX2X_DCBX_STRICT_COS_HIGHEST;
}
/* Pauseable */
cos_data->data[0].pausable = true;
@@ -1306,13 +1328,16 @@ static void bxn2x_dcbx_single_pg_to_cos_params(struct bnx2x *bp,
* and that with the highest priority
* gets the highest strict priority in the arbiter.
*/
- cos_data->data[0].strict = BNX2X_DCBX_COS_LOW_STRICT;
- cos_data->data[1].strict = BNX2X_DCBX_COS_HIGH_STRICT;
+ cos_data->data[0].strict =
+ BNX2X_DCBX_STRICT_COS_NEXT_LOWER_PRI(
+ BNX2X_DCBX_STRICT_COS_HIGHEST);
+ cos_data->data[1].strict =
+ BNX2X_DCBX_STRICT_COS_HIGHEST;
}
}
}
-static void bnx2x_dcbx_two_pg_to_cos_params(
+static void bnx2x_dcbx_2cos_limit_cee_two_pg_to_cos_params(
struct bnx2x *bp,
struct pg_help_data *pg_help_data,
struct dcbx_ets_feature *ets,
@@ -1322,7 +1347,7 @@ static void bnx2x_dcbx_two_pg_to_cos_params(
u8 num_of_dif_pri)
{
u8 i = 0;
- u8 pg[E2_NUM_OF_COS] = {0};
+ u8 pg[DCBX_COS_MAX_NUM_E2] = { 0 };
/* If there are both pauseable and non-pauseable priorities,
* the pauseable priorities go to the first queue and
@@ -1378,16 +1403,68 @@ static void bnx2x_dcbx_two_pg_to_cos_params(
}
/* There can be only one strict pg */
- for (i = 0 ; i < E2_NUM_OF_COS; i++) {
+ for (i = 0 ; i < ARRAY_SIZE(pg); i++) {
if (pg[i] < DCBX_MAX_NUM_PG_BW_ENTRIES)
cos_data->data[i].cos_bw =
DCBX_PG_BW_GET(ets->pg_bw_tbl, pg[i]);
else
- cos_data->data[i].strict = BNX2X_DCBX_COS_HIGH_STRICT;
+ cos_data->data[i].strict =
+ BNX2X_DCBX_STRICT_COS_HIGHEST;
+ }
+}
+
+static int bnx2x_dcbx_join_pgs(
+ struct bnx2x *bp,
+ struct dcbx_ets_feature *ets,
+ struct pg_help_data *pg_help_data,
+ u8 required_num_of_pg)
+{
+ u8 entry_joined = pg_help_data->num_of_pg - 1;
+ u8 entry_removed = entry_joined + 1;
+ u8 pg_joined = 0;
+
+ if (required_num_of_pg == 0 || ARRAY_SIZE(pg_help_data->data)
+ <= pg_help_data->num_of_pg) {
+
+ BNX2X_ERR("required_num_of_pg can't be zero\n");
+ return -EINVAL;
}
+
+ while (required_num_of_pg < pg_help_data->num_of_pg) {
+ entry_joined = pg_help_data->num_of_pg - 2;
+ entry_removed = entry_joined + 1;
+ /* protect index */
+ entry_removed %= ARRAY_SIZE(pg_help_data->data);
+
+ pg_help_data->data[entry_joined].pg_priority |=
+ pg_help_data->data[entry_removed].pg_priority;
+
+ pg_help_data->data[entry_joined].num_of_dif_pri +=
+ pg_help_data->data[entry_removed].num_of_dif_pri;
+
+ if (pg_help_data->data[entry_joined].pg == DCBX_STRICT_PRI_PG ||
+ pg_help_data->data[entry_removed].pg == DCBX_STRICT_PRI_PG)
+ /* Entries joined strict priority rules */
+ pg_help_data->data[entry_joined].pg =
+ DCBX_STRICT_PRI_PG;
+ else {
+ /* Entries can be joined join BW */
+ pg_joined = DCBX_PG_BW_GET(ets->pg_bw_tbl,
+ pg_help_data->data[entry_joined].pg) +
+ DCBX_PG_BW_GET(ets->pg_bw_tbl,
+ pg_help_data->data[entry_removed].pg);
+
+ DCBX_PG_BW_SET(ets->pg_bw_tbl,
+ pg_help_data->data[entry_joined].pg, pg_joined);
+ }
+ /* Joined the entries */
+ pg_help_data->num_of_pg--;
+ }
+
+ return 0;
}
-static void bnx2x_dcbx_three_pg_to_cos_params(
+static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params(
struct bnx2x *bp,
struct pg_help_data *pg_help_data,
struct dcbx_ets_feature *ets,
@@ -1459,102 +1536,271 @@ static void bnx2x_dcbx_three_pg_to_cos_params(
/* If we join a group and one is strict
* than the bw rulls */
cos_data->data[1].strict =
- BNX2X_DCBX_COS_HIGH_STRICT;
+ BNX2X_DCBX_STRICT_COS_HIGHEST;
}
}
}
}
-static void bnx2x_dcbx_fill_cos_params(struct bnx2x *bp,
+static void bnx2x_dcbx_2cos_limit_cee_fill_cos_params(struct bnx2x *bp,
struct pg_help_data *help_data,
struct dcbx_ets_feature *ets,
- u32 *pg_pri_orginal_spread)
+ struct cos_help_data *cos_data,
+ u32 *pg_pri_orginal_spread,
+ u32 pri_join_mask,
+ u8 num_of_dif_pri)
{
- struct cos_help_data cos_data ;
- u8 i = 0;
- u32 pri_join_mask = 0;
- u8 num_of_dif_pri = 0;
-
- memset(&cos_data, 0, sizeof(cos_data));
- /* Validate the pg value */
- for (i = 0; i < help_data->num_of_pg ; i++) {
- if (DCBX_STRICT_PRIORITY != help_data->data[i].pg &&
- DCBX_MAX_NUM_PG_BW_ENTRIES <= help_data->data[i].pg)
- BNX2X_ERR("Invalid pg[%d] data %x\n", i,
- help_data->data[i].pg);
- pri_join_mask |= help_data->data[i].pg_priority;
- num_of_dif_pri += help_data->data[i].num_of_dif_pri;
- }
- /* default settings */
- cos_data.num_of_cos = 2;
- for (i = 0; i < E2_NUM_OF_COS ; i++) {
- cos_data.data[i].pri_join_mask = pri_join_mask;
- cos_data.data[i].pausable = false;
- cos_data.data[i].strict = BNX2X_DCBX_COS_NOT_STRICT;
- cos_data.data[i].cos_bw = DCBX_INVALID_COS_BW;
- }
+ /* default E2 settings */
+ cos_data->num_of_cos = DCBX_COS_MAX_NUM_E2;
switch (help_data->num_of_pg) {
case 1:
-
- bxn2x_dcbx_single_pg_to_cos_params(
+ bnx2x_dcbx_2cos_limit_cee_single_pg_to_cos_params(
bp,
help_data,
- &cos_data,
+ cos_data,
pri_join_mask,
num_of_dif_pri);
break;
case 2:
- bnx2x_dcbx_two_pg_to_cos_params(
+ bnx2x_dcbx_2cos_limit_cee_two_pg_to_cos_params(
bp,
help_data,
ets,
- &cos_data,
+ cos_data,
pg_pri_orginal_spread,
pri_join_mask,
num_of_dif_pri);
break;
case 3:
- bnx2x_dcbx_three_pg_to_cos_params(
+ bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params(
bp,
help_data,
ets,
- &cos_data,
+ cos_data,
pg_pri_orginal_spread,
pri_join_mask,
num_of_dif_pri);
-
break;
default:
BNX2X_ERR("Wrong pg_help_data.num_of_pg\n");
bnx2x_dcbx_ets_disabled_entry_data(bp,
- &cos_data, pri_join_mask);
+ cos_data, pri_join_mask);
+ }
+}
+
+static int bnx2x_dcbx_spread_strict_pri(struct bnx2x *bp,
+ struct cos_help_data *cos_data,
+ u8 entry,
+ u8 num_spread_of_entries,
+ u8 strict_app_pris)
+{
+ u8 strict_pri = BNX2X_DCBX_STRICT_COS_HIGHEST;
+ u8 num_of_app_pri = MAX_PFC_PRIORITIES;
+ u8 app_pri_bit = 0;
+
+ while (num_spread_of_entries && num_of_app_pri > 0) {
+ app_pri_bit = 1 << (num_of_app_pri - 1);
+ if (app_pri_bit & strict_app_pris) {
+ struct cos_entry_help_data *data = &cos_data->
+ data[entry];
+ num_spread_of_entries--;
+ if (num_spread_of_entries == 0) {
+ /* last entry needed put all the entries left */
+ data->cos_bw = DCBX_INVALID_COS_BW;
+ data->strict = strict_pri;
+ data->pri_join_mask = strict_app_pris;
+ data->pausable = DCBX_IS_PFC_PRI_SOME_PAUSE(bp,
+ data->pri_join_mask);
+ } else {
+ strict_app_pris &= ~app_pri_bit;
+
+ data->cos_bw = DCBX_INVALID_COS_BW;
+ data->strict = strict_pri;
+ data->pri_join_mask = app_pri_bit;
+ data->pausable = DCBX_IS_PFC_PRI_SOME_PAUSE(bp,
+ data->pri_join_mask);
+ }
+
+ strict_pri =
+ BNX2X_DCBX_STRICT_COS_NEXT_LOWER_PRI(strict_pri);
+ entry++;
+ }
+
+ num_of_app_pri--;
+ }
+
+ if (num_spread_of_entries)
+ return -EINVAL;
+
+ return 0;
+}
+
+static u8 bnx2x_dcbx_cee_fill_strict_pri(struct bnx2x *bp,
+ struct cos_help_data *cos_data,
+ u8 entry,
+ u8 num_spread_of_entries,
+ u8 strict_app_pris)
+{
+
+ if (bnx2x_dcbx_spread_strict_pri(bp, cos_data, entry,
+ num_spread_of_entries,
+ strict_app_pris)) {
+ struct cos_entry_help_data *data = &cos_data->
+ data[entry];
+ /* Fill BW entry */
+ data->cos_bw = DCBX_INVALID_COS_BW;
+ data->strict = BNX2X_DCBX_STRICT_COS_HIGHEST;
+ data->pri_join_mask = strict_app_pris;
+ data->pausable = DCBX_IS_PFC_PRI_SOME_PAUSE(bp,
+ data->pri_join_mask);
+ return 1;
+ }
+
+ return num_spread_of_entries;
+}
+
+static void bnx2x_dcbx_cee_fill_cos_params(struct bnx2x *bp,
+ struct pg_help_data *help_data,
+ struct dcbx_ets_feature *ets,
+ struct cos_help_data *cos_data,
+ u32 pri_join_mask)
+
+{
+ u8 need_num_of_entries = 0;
+ u8 i = 0;
+ u8 entry = 0;
+
+ /*
+ * if the number of requested PG-s in CEE is greater than 3
+ * then the results are not determined since this is a violation
+ * of the standard.
+ */
+ if (help_data->num_of_pg > DCBX_COS_MAX_NUM_E3B0) {
+ if (bnx2x_dcbx_join_pgs(bp, ets, help_data,
+ DCBX_COS_MAX_NUM_E3B0)) {
+ BNX2X_ERR("Unable to reduce the number of PGs -"
+ "we will disables ETS\n");
+ bnx2x_dcbx_ets_disabled_entry_data(bp, cos_data,
+ pri_join_mask);
+ return;
+ }
+ }
+
+ for (i = 0 ; i < help_data->num_of_pg; i++) {
+ struct pg_entry_help_data *pg = &help_data->data[i];
+ if (pg->pg < DCBX_MAX_NUM_PG_BW_ENTRIES) {
+ struct cos_entry_help_data *data = &cos_data->
+ data[entry];
+ /* Fill BW entry */
+ data->cos_bw = DCBX_PG_BW_GET(ets->pg_bw_tbl, pg->pg);
+ data->strict = BNX2X_DCBX_STRICT_INVALID;
+ data->pri_join_mask = pg->pg_priority;
+ data->pausable = DCBX_IS_PFC_PRI_SOME_PAUSE(bp,
+ data->pri_join_mask);
+
+ entry++;
+ } else {
+ need_num_of_entries = min_t(u8,
+ (u8)pg->num_of_dif_pri,
+ (u8)DCBX_COS_MAX_NUM_E3B0 -
+ help_data->num_of_pg + 1);
+ /*
+ * If there are still VOQ-s which have no associated PG,
+ * then associate these VOQ-s to PG15. These PG-s will
+ * be used for SP between priorities on PG15.
+ */
+ entry += bnx2x_dcbx_cee_fill_strict_pri(bp, cos_data,
+ entry, need_num_of_entries, pg->pg_priority);
+ }
+ }
+
+ /* the entry will represent the number of COSes used */
+ cos_data->num_of_cos = entry;
+}
+static void bnx2x_dcbx_fill_cos_params(struct bnx2x *bp,
+ struct pg_help_data *help_data,
+ struct dcbx_ets_feature *ets,
+ u32 *pg_pri_orginal_spread)
+{
+ struct cos_help_data cos_data;
+ u8 i = 0;
+ u32 pri_join_mask = 0;
+ u8 num_of_dif_pri = 0;
+
+ memset(&cos_data, 0, sizeof(cos_data));
+
+ /* Validate the pg value */
+ for (i = 0; i < help_data->num_of_pg ; i++) {
+ if (DCBX_STRICT_PRIORITY != help_data->data[i].pg &&
+ DCBX_MAX_NUM_PG_BW_ENTRIES <= help_data->data[i].pg)
+ BNX2X_ERR("Invalid pg[%d] data %x\n", i,
+ help_data->data[i].pg);
+ pri_join_mask |= help_data->data[i].pg_priority;
+ num_of_dif_pri += help_data->data[i].num_of_dif_pri;
}
+ /* defaults */
+ cos_data.num_of_cos = 1;
+ for (i = 0; i < ARRAY_SIZE(cos_data.data); i++) {
+ cos_data.data[i].pri_join_mask = 0;
+ cos_data.data[i].pausable = false;
+ cos_data.data[i].strict = BNX2X_DCBX_STRICT_INVALID;
+ cos_data.data[i].cos_bw = DCBX_INVALID_COS_BW;
+ }
+
+ if (CHIP_IS_E3B0(bp))
+ bnx2x_dcbx_cee_fill_cos_params(bp, help_data, ets,
+ &cos_data, pri_join_mask);
+ else /* E2 + E3A0 */
+ bnx2x_dcbx_2cos_limit_cee_fill_cos_params(bp,
+ help_data, ets,
+ &cos_data,
+ pg_pri_orginal_spread,
+ pri_join_mask,
+ num_of_dif_pri);
+
for (i = 0; i < cos_data.num_of_cos ; i++) {
- struct bnx2x_dcbx_cos_params *params =
+ struct bnx2x_dcbx_cos_params *p =
&bp->dcbx_port_params.ets.cos_params[i];
- params->pauseable = cos_data.data[i].pausable;
- params->strict = cos_data.data[i].strict;
- params->bw_tbl = cos_data.data[i].cos_bw;
- if (params->pauseable) {
- params->pri_bitmask =
- DCBX_PFC_PRI_GET_PAUSE(bp,
- cos_data.data[i].pri_join_mask);
+ p->strict = cos_data.data[i].strict;
+ p->bw_tbl = cos_data.data[i].cos_bw;
+ p->pri_bitmask = cos_data.data[i].pri_join_mask;
+ p->pauseable = cos_data.data[i].pausable;
+
+ /* sanity */
+ if (p->bw_tbl != DCBX_INVALID_COS_BW ||
+ p->strict != BNX2X_DCBX_STRICT_INVALID) {
+ if (p->pri_bitmask == 0)
+ BNX2X_ERR("Invalid pri_bitmask for %d\n", i);
+
+ if (CHIP_IS_E2(bp) || CHIP_IS_E3A0(bp)) {
+
+ if (p->pauseable &&
+ DCBX_PFC_PRI_GET_NON_PAUSE(bp,
+ p->pri_bitmask) != 0)
+ BNX2X_ERR("Inconsistent config for "
+ "pausable COS %d\n", i);
+
+ if (!p->pauseable &&
+ DCBX_PFC_PRI_GET_PAUSE(bp,
+ p->pri_bitmask) != 0)
+ BNX2X_ERR("Inconsistent config for "
+ "nonpausable COS %d\n", i);
+ }
+ }
+
+ if (p->pauseable)
DP(NETIF_MSG_LINK, "COS %d PAUSABLE prijoinmask 0x%x\n",
i, cos_data.data[i].pri_join_mask);
- } else {
- params->pri_bitmask =
- DCBX_PFC_PRI_GET_NON_PAUSE(bp,
- cos_data.data[i].pri_join_mask);
+ else
DP(NETIF_MSG_LINK, "COS %d NONPAUSABLE prijoinmask "
"0x%x\n",
i, cos_data.data[i].pri_join_mask);
- }
}
bp->dcbx_port_params.ets.num_of_cos = cos_data.num_of_cos ;
@@ -1574,30 +1820,26 @@ static void bnx2x_dcbx_get_ets_pri_pg_tbl(struct bnx2x *bp,
}
}
-static void bnx2x_pfc_fw_struct_e2(struct bnx2x *bp)
+static void bnx2x_dcbx_fw_struct(struct bnx2x *bp,
+ struct bnx2x_func_tx_start_params *pfc_fw_cfg)
{
- struct flow_control_configuration *pfc_fw_cfg = NULL;
u16 pri_bit = 0;
u8 cos = 0, pri = 0;
struct priority_cos *tt2cos;
u32 *ttp = bp->dcbx_port_params.app.traffic_type_priority;
- pfc_fw_cfg = (struct flow_control_configuration *)
- bnx2x_sp(bp, pfc_config);
- memset(pfc_fw_cfg, 0, sizeof(struct flow_control_configuration));
+ memset(pfc_fw_cfg, 0, sizeof(*pfc_fw_cfg));
+
+ /* to disable DCB - the structure must be zeroed */
+ if (bp->dcbx_error & DCBX_REMOTE_MIB_ERROR)
+ return;
/*shortcut*/
tt2cos = pfc_fw_cfg->traffic_type_to_priority_cos;
/* Fw version should be incremented each update */
pfc_fw_cfg->dcb_version = ++bp->dcb_version;
- pfc_fw_cfg->dcb_enabled = DCB_ENABLED;
-
- /* Default initialization */
- for (pri = 0; pri < MAX_PFC_TRAFFIC_TYPES ; pri++) {
- tt2cos[pri].priority = LLFC_TRAFFIC_TYPE_TO_PRIORITY_UNMAPPED;
- tt2cos[pri].cos = 0;
- }
+ pfc_fw_cfg->dcb_enabled = 1;
/* Fill priority parameters */
for (pri = 0; pri < LLFC_DRIVER_TRAFFIC_TYPE_MAX; pri++) {
@@ -1605,14 +1847,37 @@ static void bnx2x_pfc_fw_struct_e2(struct bnx2x *bp)
pri_bit = 1 << tt2cos[pri].priority;
/* Fill COS parameters based on COS calculated to
- * make it more generally for future use */
+ * make it more general for future use */
for (cos = 0; cos < bp->dcbx_port_params.ets.num_of_cos; cos++)
if (bp->dcbx_port_params.ets.cos_params[cos].
pri_bitmask & pri_bit)
tt2cos[pri].cos = cos;
}
+
+ /* we never want the FW to add a 0 vlan tag */
+ pfc_fw_cfg->dont_add_pri_0_en = 1;
+
bnx2x_dcbx_print_cos_params(bp, pfc_fw_cfg);
}
+
+void bnx2x_dcbx_pmf_update(struct bnx2x *bp)
+{
+ /* if we need to syncronize DCBX result from prev PMF
+ * read it from shmem and update bp accordingly
+ */
+ if (SHMEM2_HAS(bp, drv_flags) &&
+ GET_FLAGS(SHMEM2_RD(bp, drv_flags), DRV_FLAGS_DCB_CONFIGURED)) {
+ /* Read neg results if dcbx is in the FW */
+ if (bnx2x_dcbx_read_shmem_neg_results(bp))
+ return;
+
+ bnx2x_dump_dcbx_drv_param(bp, &bp->dcbx_local_feat,
+ bp->dcbx_error);
+ bnx2x_get_dcbx_drv_param(bp, &bp->dcbx_local_feat,
+ bp->dcbx_error);
+ }
+}
+
/* DCB netlink */
#ifdef BCM_DCBNL
@@ -1858,6 +2123,7 @@ static u8 bnx2x_dcbnl_get_cap(struct net_device *netdev, int capid, u8 *cap)
break;
case DCB_CAP_ATTR_DCBX:
*cap = BNX2X_DCBX_CAPS;
+ break;
default:
rval = -EINVAL;
break;
@@ -1879,10 +2145,12 @@ static u8 bnx2x_dcbnl_get_numtcs(struct net_device *netdev, int tcid, u8 *num)
if (bp->dcb_state) {
switch (tcid) {
case DCB_NUMTCS_ATTR_PG:
- *num = E2_NUM_OF_COS;
+ *num = CHIP_IS_E3B0(bp) ? DCBX_COS_MAX_NUM_E3B0 :
+ DCBX_COS_MAX_NUM_E2;
break;
case DCB_NUMTCS_ATTR_PFC:
- *num = E2_NUM_OF_COS;
+ *num = CHIP_IS_E3B0(bp) ? DCBX_COS_MAX_NUM_E3B0 :
+ DCBX_COS_MAX_NUM_E2;
break;
default:
rval = -EINVAL;
diff --git a/drivers/net/bnx2x/bnx2x_dcb.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h
index bed369d67e02..2c6a3bca6f28 100644
--- a/drivers/net/bnx2x/bnx2x_dcb.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h
@@ -27,22 +27,30 @@ struct bnx2x_dcbx_app_params {
u32 traffic_type_priority[LLFC_DRIVER_TRAFFIC_TYPE_MAX];
};
-#define E2_NUM_OF_COS 2
-#define BNX2X_DCBX_COS_NOT_STRICT 0
-#define BNX2X_DCBX_COS_LOW_STRICT 1
-#define BNX2X_DCBX_COS_HIGH_STRICT 2
+#define DCBX_COS_MAX_NUM_E2 DCBX_E2E3_MAX_NUM_COS
+/* bnx2x currently limits numbers of supported COSes to 3 to be extended to 6 */
+#define BNX2X_MAX_COS_SUPPORT 3
+#define DCBX_COS_MAX_NUM_E3B0 BNX2X_MAX_COS_SUPPORT
+#define DCBX_COS_MAX_NUM BNX2X_MAX_COS_SUPPORT
struct bnx2x_dcbx_cos_params {
u32 bw_tbl;
u32 pri_bitmask;
+ /*
+ * strict priority: valid values are 0..5; 0 is highest priority.
+ * There can't be two COSes with the same priority.
+ */
u8 strict;
+#define BNX2X_DCBX_STRICT_INVALID DCBX_COS_MAX_NUM
+#define BNX2X_DCBX_STRICT_COS_HIGHEST 0
+#define BNX2X_DCBX_STRICT_COS_NEXT_LOWER_PRI(sp) ((sp) + 1)
u8 pauseable;
};
struct bnx2x_dcbx_pg_params {
u32 enabled;
u8 num_of_cos; /* valid COS entries */
- struct bnx2x_dcbx_cos_params cos_params[E2_NUM_OF_COS];
+ struct bnx2x_dcbx_cos_params cos_params[DCBX_COS_MAX_NUM];
};
struct bnx2x_dcbx_pfc_params {
@@ -60,6 +68,8 @@ struct bnx2x_dcbx_port_params {
#define BNX2X_DCBX_OVERWRITE_SETTINGS_DISABLE 0
#define BNX2X_DCBX_OVERWRITE_SETTINGS_ENABLE 1
#define BNX2X_DCBX_OVERWRITE_SETTINGS_INVALID (BNX2X_DCBX_CONFIG_INV_VALUE)
+#define BNX2X_IS_ETS_ENABLED(bp) ((bp)->dcb_state == BNX2X_DCB_STATE_ON &&\
+ (bp)->dcbx_port_params.ets.enabled)
struct bnx2x_config_lldp_params {
u32 overwrite_settings;
@@ -132,7 +142,7 @@ struct cos_entry_help_data {
};
struct cos_help_data {
- struct cos_entry_help_data data[E2_NUM_OF_COS];
+ struct cos_entry_help_data data[DCBX_COS_MAX_NUM];
u8 num_of_cos;
};
@@ -148,6 +158,8 @@ struct cos_help_data {
((pg_pri) & (DCBX_PFC_PRI_PAUSE_MASK(bp)))
#define DCBX_PFC_PRI_GET_NON_PAUSE(bp, pg_pri) \
(DCBX_PFC_PRI_NON_PAUSE_MASK(bp) & (pg_pri))
+#define DCBX_IS_PFC_PRI_SOME_PAUSE(bp, pg_pri) \
+ (0 != DCBX_PFC_PRI_GET_PAUSE(bp, pg_pri))
#define IS_DCBX_PFC_PRI_ONLY_PAUSE(bp, pg_pri) \
(pg_pri == DCBX_PFC_PRI_GET_PAUSE((bp), (pg_pri)))
#define IS_DCBX_PFC_PRI_ONLY_NON_PAUSE(bp, pg_pri)\
@@ -170,22 +182,18 @@ struct pg_help_data {
/* forward DCB/PFC related declarations */
struct bnx2x;
-void bnx2x_dcb_init_intmem_pfc(struct bnx2x *bp);
void bnx2x_dcbx_update(struct work_struct *work);
void bnx2x_dcbx_init_params(struct bnx2x *bp);
void bnx2x_dcbx_set_state(struct bnx2x *bp, bool dcb_on, u32 dcbx_enabled);
enum {
BNX2X_DCBX_STATE_NEG_RECEIVED = 0x1,
-#ifdef BCM_CNIC
- BNX2X_DCBX_STATE_ISCSI_STOPPED,
-#endif
BNX2X_DCBX_STATE_TX_PAUSED,
BNX2X_DCBX_STATE_TX_RELEASED
};
void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state);
-
+void bnx2x_dcbx_pmf_update(struct bnx2x *bp);
/* DCB netlink */
#ifdef BCM_DCBNL
extern const struct dcbnl_rtnl_ops bnx2x_dcbnl_ops;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h
new file mode 100644
index 000000000000..b983825d0ee9
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h
@@ -0,0 +1,1156 @@
+/* bnx2x_dump.h: Broadcom Everest network driver.
+ *
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2, available
+ * at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html (the "GPL").
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a
+ * license other than the GPL, without Broadcom's express prior written
+ * consent.
+ */
+
+
+/* This struct holds a signature to ensure the dump returned from the driver
+ * match the meta data file inserted to grc_dump.tcl
+ * The signature is time stamp, diag version and grc_dump version
+ */
+
+#ifndef BNX2X_DUMP_H
+#define BNX2X_DUMP_H
+
+
+
+/*definitions */
+#define XSTORM_WAITP_ADDR 0x2b8a80
+#define TSTORM_WAITP_ADDR 0x1b8a80
+#define USTORM_WAITP_ADDR 0x338a80
+#define CSTORM_WAITP_ADDR 0x238a80
+#define TSTORM_CAM_MODE 0x1B1440
+
+#define MAX_TIMER_PENDING 200
+#define TIMER_SCAN_DONT_CARE 0xFF
+#define RI_E1 0x1
+#define RI_E1H 0x2
+#define RI_E2 0x4
+#define RI_E3 0x8
+#define RI_E3B0 0x10
+#define RI_ONLINE 0x100
+#define RI_OFFLINE 0x0
+#define RI_PATH0_DUMP 0x200
+#define RI_PATH1_DUMP 0x400
+
+#define RI_E1_ONLINE (RI_E1 | RI_ONLINE)
+#define RI_E1H_ONLINE (RI_E1H | RI_ONLINE)
+#define RI_E1E1H_ONLINE (RI_E1 | RI_E1H | RI_ONLINE)
+#define RI_E2_ONLINE (RI_E2 | RI_ONLINE)
+#define RI_E1E2_ONLINE (RI_E1 | RI_E2 | RI_ONLINE)
+#define RI_E1HE2_ONLINE (RI_E1H | RI_E2 | RI_ONLINE)
+#define RI_E1E1HE2_ONLINE (RI_E1 | RI_E1H | RI_E2 | RI_ONLINE)
+#define RI_E3_ONLINE (RI_E3 | RI_ONLINE)
+#define RI_E1E3_ONLINE (RI_E1 | RI_E3 | RI_ONLINE)
+#define RI_E1HE3_ONLINE (RI_E1H | RI_E3 | RI_ONLINE)
+#define RI_E1E1HE3_ONLINE (RI_E1 | RI_E1H | RI_E3 | RI_ONLINE)
+#define RI_E2E3_ONLINE (RI_E2 | RI_E3 | RI_ONLINE)
+#define RI_E1E2E3_ONLINE (RI_E1 | RI_E2 | RI_E3 | RI_ONLINE)
+#define RI_E1HE2E3_ONLINE (RI_E1H | RI_E2 | RI_E3 | RI_ONLINE)
+#define RI_E1E1HE2E3_ONLINE (RI_E1 | RI_E1H | RI_E2 | RI_E3 | RI_ONLINE)
+#define RI_E3B0_ONLINE (RI_E3B0 | RI_ONLINE)
+#define RI_E1E3B0_ONLINE (RI_E1 | RI_E3B0 | RI_ONLINE)
+#define RI_E1HE3B0_ONLINE (RI_E1H | RI_E3B0 | RI_ONLINE)
+#define RI_E1E1HE3B0_ONLINE (RI_E1 | RI_E1H | RI_E3B0 | RI_ONLINE)
+#define RI_E2E3B0_ONLINE (RI_E2 | RI_E3B0 | RI_ONLINE)
+#define RI_E1E2E3B0_ONLINE (RI_E1 | RI_E2 | RI_E3B0 | RI_ONLINE)
+#define RI_E1HE2E3B0_ONLINE (RI_E1H | RI_E2 | RI_E3B0 | RI_ONLINE)
+#define RI_E1E1HE2E3B0_ONLINE (RI_E1 | RI_E1H | RI_E2 | RI_E3B0 | RI_ONLINE)
+#define RI_E3E3B0_ONLINE (RI_E3 | RI_E3B0 | RI_ONLINE)
+#define RI_E1E3E3B0_ONLINE (RI_E1 | RI_E3 | RI_E3B0 | RI_ONLINE)
+#define RI_E1HE3E3B0_ONLINE (RI_E1H | RI_E3 | RI_E3B0 | RI_ONLINE)
+#define RI_E1E1HE3E3B0_ONLINE (RI_E1 | RI_E1H | RI_E3 | RI_E3B0 | RI_ONLINE)
+#define RI_E2E3E3B0_ONLINE (RI_E2 | RI_E3 | RI_E3B0 | RI_ONLINE)
+#define RI_E1E2E3E3B0_ONLINE (RI_E1 | RI_E2 | RI_E3 | RI_E3B0 | RI_ONLINE)
+#define RI_E1HE2E3E3B0_ONLINE (RI_E1H | RI_E2 | RI_E3 | RI_E3B0 | RI_ONLINE)
+#define RI_E1E1HE2E3E3B0_ONLINE \
+ (RI_E1 | RI_E1H | RI_E2 | RI_E3 | RI_E3B0 | RI_ONLINE)
+#define RI_E1_OFFLINE (RI_E1 | RI_OFFLINE)
+#define RI_E1H_OFFLINE (RI_E1H | RI_OFFLINE)
+#define RI_E1E1H_OFFLINE (RI_E1 | RI_E1H | RI_OFFLINE)
+#define RI_E2_OFFLINE (RI_E2 | RI_OFFLINE)
+#define RI_E1E2_OFFLINE (RI_E1 | RI_E2 | RI_OFFLINE)
+#define RI_E1HE2_OFFLINE (RI_E1H | RI_E2 | RI_OFFLINE)
+#define RI_E1E1HE2_OFFLINE (RI_E1 | RI_E1H | RI_E2 | RI_OFFLINE)
+#define RI_E3_OFFLINE (RI_E3 | RI_OFFLINE)
+#define RI_E1E3_OFFLINE (RI_E1 | RI_E3 | RI_OFFLINE)
+#define RI_E1HE3_OFFLINE (RI_E1H | RI_E3 | RI_OFFLINE)
+#define RI_E1E1HE3_OFFLINE (RI_E1 | RI_E1H | RI_E3 | RI_OFFLINE)
+#define RI_E2E3_OFFLINE (RI_E2 | RI_E3 | RI_OFFLINE)
+#define RI_E1E2E3_OFFLINE (RI_E1 | RI_E2 | RI_E3 | RI_OFFLINE)
+#define RI_E1HE2E3_OFFLINE (RI_E1H | RI_E2 | RI_E3 | RI_OFFLINE)
+#define RI_E1E1HE2E3_OFFLINE (RI_E1 | RI_E1H | RI_E2 | RI_E3 | RI_OFFLINE)
+#define RI_E3B0_OFFLINE (RI_E3B0 | RI_OFFLINE)
+#define RI_E1E3B0_OFFLINE (RI_E1 | RI_E3B0 | RI_OFFLINE)
+#define RI_E1HE3B0_OFFLINE (RI_E1H | RI_E3B0 | RI_OFFLINE)
+#define RI_E1E1HE3B0_OFFLINE (RI_E1 | RI_E1H | RI_E3B0 | RI_OFFLINE)
+#define RI_E2E3B0_OFFLINE (RI_E2 | RI_E3B0 | RI_OFFLINE)
+#define RI_E1E2E3B0_OFFLINE (RI_E1 | RI_E2 | RI_E3B0 | RI_OFFLINE)
+#define RI_E1HE2E3B0_OFFLINE (RI_E1H | RI_E2 | RI_E3B0 | RI_OFFLINE)
+#define RI_E1E1HE2E3B0_OFFLINE (RI_E1 | RI_E1H | RI_E2 | RI_E3B0 | RI_OFFLINE)
+#define RI_E3E3B0_OFFLINE (RI_E3 | RI_E3B0 | RI_OFFLINE)
+#define RI_E1E3E3B0_OFFLINE (RI_E1 | RI_E3 | RI_E3B0 | RI_OFFLINE)
+#define RI_E1HE3E3B0_OFFLINE (RI_E1H | RI_E3 | RI_E3B0 | RI_OFFLINE)
+#define RI_E1E1HE3E3B0_OFFLINE (RI_E1 | RI_E1H | RI_E3 | RI_E3B0 | RI_OFFLINE)
+#define RI_E2E3E3B0_OFFLINE (RI_E2 | RI_E3 | RI_E3B0 | RI_OFFLINE)
+#define RI_E1E2E3E3B0_OFFLINE (RI_E1 | RI_E2 | RI_E3 | RI_E3B0 | RI_OFFLINE)
+#define RI_E1HE2E3E3B0_OFFLINE (RI_E1H | RI_E2 | RI_E3 | RI_E3B0 | RI_OFFLINE)
+#define RI_E1E1HE2E3E3B0_OFFLINE \
+ (RI_E1 | RI_E1H | RI_E2 | RI_E3 | RI_E3B0 | RI_OFFLINE)
+#define RI_ALL_ONLINE RI_E1E1HE2E3E3B0_ONLINE
+#define RI_ALL_OFFLINE RI_E1E1HE2E3E3B0_OFFLINE
+
+#define DBG_DMP_TRACE_BUFFER_SIZE 0x800
+#define DBG_DMP_TRACE_BUFFER_OFFSET(shmem0_offset) \
+ ((shmem0_offset) - DBG_DMP_TRACE_BUFFER_SIZE)
+
+struct dump_sign {
+ u32 time_stamp;
+ u32 diag_ver;
+ u32 grc_dump_ver;
+};
+
+struct dump_hdr {
+ u32 hdr_size; /* in dwords, excluding this field */
+ struct dump_sign dump_sign;
+ u32 xstorm_waitp;
+ u32 tstorm_waitp;
+ u32 ustorm_waitp;
+ u32 cstorm_waitp;
+ u16 info;
+ u8 idle_chk;
+ u8 reserved;
+};
+
+struct reg_addr {
+ u32 addr;
+ u32 size;
+ u16 info;
+};
+
+struct wreg_addr {
+ u32 addr;
+ u32 size;
+ u32 read_regs_count;
+ const u32 *read_regs;
+ u16 info;
+};
+
+static const struct reg_addr reg_addrs[] = {
+ { 0x2000, 341, RI_ALL_ONLINE },
+ { 0x2800, 103, RI_ALL_ONLINE },
+ { 0x3000, 287, RI_ALL_ONLINE },
+ { 0x3800, 331, RI_ALL_ONLINE },
+ { 0x8800, 6, RI_ALL_ONLINE },
+ { 0x8818, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x9000, 147, RI_E2E3E3B0_ONLINE },
+ { 0x924c, 1, RI_E2_ONLINE },
+ { 0x9250, 16, RI_E2E3E3B0_ONLINE },
+ { 0x9400, 33, RI_E2E3E3B0_ONLINE },
+ { 0x9484, 5, RI_E3E3B0_ONLINE },
+ { 0xa000, 27, RI_ALL_ONLINE },
+ { 0xa06c, 1, RI_E1E1H_ONLINE },
+ { 0xa070, 71, RI_ALL_ONLINE },
+ { 0xa18c, 4, RI_E1E1H_ONLINE },
+ { 0xa19c, 62, RI_ALL_ONLINE },
+ { 0xa294, 2, RI_E1E1H_ONLINE },
+ { 0xa29c, 2, RI_ALL_ONLINE },
+ { 0xa2a4, 2, RI_E1E1HE2_ONLINE },
+ { 0xa2ac, 52, RI_ALL_ONLINE },
+ { 0xa39c, 7, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa3b8, 2, RI_E3E3B0_ONLINE },
+ { 0xa3c0, 3, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa3d0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa3d8, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa3e0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa3e8, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa3f0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa3f8, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa400, 40, RI_ALL_ONLINE },
+ { 0xa4a0, 1, RI_E1E1HE2_ONLINE },
+ { 0xa4a4, 2, RI_ALL_ONLINE },
+ { 0xa4ac, 2, RI_E1E1H_ONLINE },
+ { 0xa4b4, 1, RI_E1E1HE2_ONLINE },
+ { 0xa4b8, 2, RI_E1E1H_ONLINE },
+ { 0xa4c0, 3, RI_ALL_ONLINE },
+ { 0xa4cc, 5, RI_E1E1H_ONLINE },
+ { 0xa4e0, 3, RI_ALL_ONLINE },
+ { 0xa4fc, 2, RI_ALL_ONLINE },
+ { 0xa504, 1, RI_E1E1H_ONLINE },
+ { 0xa508, 3, RI_ALL_ONLINE },
+ { 0xa518, 1, RI_ALL_ONLINE },
+ { 0xa520, 1, RI_ALL_ONLINE },
+ { 0xa528, 1, RI_ALL_ONLINE },
+ { 0xa530, 1, RI_ALL_ONLINE },
+ { 0xa538, 1, RI_ALL_ONLINE },
+ { 0xa540, 1, RI_ALL_ONLINE },
+ { 0xa548, 1, RI_E1E1H_ONLINE },
+ { 0xa550, 1, RI_E1E1H_ONLINE },
+ { 0xa558, 1, RI_E1E1H_ONLINE },
+ { 0xa560, 1, RI_E1E1H_ONLINE },
+ { 0xa568, 1, RI_E1E1H_ONLINE },
+ { 0xa570, 1, RI_ALL_ONLINE },
+ { 0xa580, 1, RI_ALL_ONLINE },
+ { 0xa590, 1, RI_ALL_ONLINE },
+ { 0xa5a0, 1, RI_E1E1HE2_ONLINE },
+ { 0xa5c0, 1, RI_ALL_ONLINE },
+ { 0xa5e0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa5e8, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa5f0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa5f8, 1, RI_E1HE2_ONLINE },
+ { 0xa5fc, 9, RI_E1HE2E3E3B0_ONLINE },
+ { 0xa620, 6, RI_E2E3E3B0_ONLINE },
+ { 0xa638, 20, RI_E2_ONLINE },
+ { 0xa688, 42, RI_E2E3E3B0_ONLINE },
+ { 0xa730, 1, RI_E2_ONLINE },
+ { 0xa734, 2, RI_E2E3E3B0_ONLINE },
+ { 0xa73c, 4, RI_E2_ONLINE },
+ { 0xa74c, 5, RI_E2E3E3B0_ONLINE },
+ { 0xa760, 5, RI_E2_ONLINE },
+ { 0xa774, 7, RI_E2E3E3B0_ONLINE },
+ { 0xa790, 15, RI_E2_ONLINE },
+ { 0xa7cc, 4, RI_E2E3E3B0_ONLINE },
+ { 0xa7e0, 6, RI_E3E3B0_ONLINE },
+ { 0xa800, 18, RI_E2_ONLINE },
+ { 0xa848, 33, RI_E2E3E3B0_ONLINE },
+ { 0xa8cc, 2, RI_E3E3B0_ONLINE },
+ { 0xa8d4, 4, RI_E2E3E3B0_ONLINE },
+ { 0xa8e4, 1, RI_E3E3B0_ONLINE },
+ { 0xa8e8, 1, RI_E2E3E3B0_ONLINE },
+ { 0xa8f0, 1, RI_E2E3E3B0_ONLINE },
+ { 0xa8f8, 30, RI_E3E3B0_ONLINE },
+ { 0xa974, 73, RI_E3E3B0_ONLINE },
+ { 0xac30, 1, RI_E3E3B0_ONLINE },
+ { 0xac40, 1, RI_E3E3B0_ONLINE },
+ { 0xac50, 1, RI_E3E3B0_ONLINE },
+ { 0xac60, 1, RI_E3B0_ONLINE },
+ { 0x10000, 9, RI_ALL_ONLINE },
+ { 0x10024, 1, RI_E1E1HE2_ONLINE },
+ { 0x10028, 5, RI_ALL_ONLINE },
+ { 0x1003c, 6, RI_E1E1HE2_ONLINE },
+ { 0x10054, 20, RI_ALL_ONLINE },
+ { 0x100a4, 4, RI_E1E1HE2_ONLINE },
+ { 0x100b4, 11, RI_ALL_ONLINE },
+ { 0x100e0, 4, RI_E1E1HE2_ONLINE },
+ { 0x100f0, 8, RI_ALL_ONLINE },
+ { 0x10110, 6, RI_E1E1HE2_ONLINE },
+ { 0x10128, 110, RI_ALL_ONLINE },
+ { 0x102e0, 4, RI_E1E1HE2_ONLINE },
+ { 0x102f0, 18, RI_ALL_ONLINE },
+ { 0x10338, 20, RI_E1E1HE2_ONLINE },
+ { 0x10388, 10, RI_ALL_ONLINE },
+ { 0x10400, 6, RI_E1E1HE2_ONLINE },
+ { 0x10418, 6, RI_ALL_ONLINE },
+ { 0x10430, 10, RI_E1E1HE2_ONLINE },
+ { 0x10458, 22, RI_ALL_ONLINE },
+ { 0x104b0, 12, RI_E1E1HE2_ONLINE },
+ { 0x104e0, 1, RI_ALL_ONLINE },
+ { 0x104e8, 2, RI_ALL_ONLINE },
+ { 0x104f4, 2, RI_ALL_ONLINE },
+ { 0x10500, 146, RI_ALL_ONLINE },
+ { 0x10750, 2, RI_E1E1HE2_ONLINE },
+ { 0x10760, 2, RI_E1E1HE2_ONLINE },
+ { 0x10770, 2, RI_E1E1HE2_ONLINE },
+ { 0x10780, 2, RI_E1E1HE2_ONLINE },
+ { 0x10790, 2, RI_ALL_ONLINE },
+ { 0x107a0, 2, RI_E1E1HE2_ONLINE },
+ { 0x107b0, 2, RI_E1E1HE2_ONLINE },
+ { 0x107c0, 2, RI_E1E1HE2_ONLINE },
+ { 0x107d0, 2, RI_E1E1HE2_ONLINE },
+ { 0x107e0, 2, RI_ALL_ONLINE },
+ { 0x10880, 2, RI_ALL_ONLINE },
+ { 0x10900, 2, RI_ALL_ONLINE },
+ { 0x16000, 1, RI_E1HE2_ONLINE },
+ { 0x16004, 25, RI_E1HE2E3E3B0_ONLINE },
+ { 0x16070, 8, RI_E1HE2E3E3B0_ONLINE },
+ { 0x16090, 4, RI_E1HE2E3_ONLINE },
+ { 0x160a0, 6, RI_E1HE2E3E3B0_ONLINE },
+ { 0x160c0, 7, RI_E1HE2E3E3B0_ONLINE },
+ { 0x160dc, 2, RI_E1HE2_ONLINE },
+ { 0x160e4, 10, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1610c, 2, RI_E1HE2_ONLINE },
+ { 0x16114, 6, RI_E1HE2E3E3B0_ONLINE },
+ { 0x16140, 48, RI_E1HE2E3E3B0_ONLINE },
+ { 0x16204, 5, RI_E1HE2E3E3B0_ONLINE },
+ { 0x18000, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x18008, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x18010, 35, RI_E2E3E3B0_ONLINE },
+ { 0x180a4, 2, RI_E2E3E3B0_ONLINE },
+ { 0x180c0, 9, RI_E2E3E3B0_ONLINE },
+ { 0x180e4, 1, RI_E2E3_ONLINE },
+ { 0x180e8, 2, RI_E2E3E3B0_ONLINE },
+ { 0x180f0, 1, RI_E2E3_ONLINE },
+ { 0x180f4, 79, RI_E2E3E3B0_ONLINE },
+ { 0x18230, 1, RI_E2E3_ONLINE },
+ { 0x18234, 2, RI_E2E3E3B0_ONLINE },
+ { 0x1823c, 1, RI_E2E3_ONLINE },
+ { 0x18240, 13, RI_E2E3E3B0_ONLINE },
+ { 0x18274, 1, RI_E2_ONLINE },
+ { 0x18278, 81, RI_E2E3E3B0_ONLINE },
+ { 0x18440, 63, RI_E2E3E3B0_ONLINE },
+ { 0x18570, 42, RI_E3E3B0_ONLINE },
+ { 0x18618, 25, RI_E3B0_ONLINE },
+ { 0x18680, 44, RI_E3B0_ONLINE },
+ { 0x18748, 12, RI_E3B0_ONLINE },
+ { 0x18788, 1, RI_E3B0_ONLINE },
+ { 0x1879c, 6, RI_E3B0_ONLINE },
+ { 0x187c4, 51, RI_E3B0_ONLINE },
+ { 0x18a00, 48, RI_E3B0_ONLINE },
+ { 0x20000, 24, RI_ALL_ONLINE },
+ { 0x20060, 8, RI_ALL_ONLINE },
+ { 0x20080, 94, RI_ALL_ONLINE },
+ { 0x201f8, 1, RI_E1E1H_ONLINE },
+ { 0x201fc, 1, RI_ALL_ONLINE },
+ { 0x20200, 1, RI_E1E1H_ONLINE },
+ { 0x20204, 1, RI_ALL_ONLINE },
+ { 0x20208, 1, RI_E1E1H_ONLINE },
+ { 0x2020c, 39, RI_ALL_ONLINE },
+ { 0x202c8, 1, RI_E2E3E3B0_ONLINE },
+ { 0x202d8, 4, RI_E2E3E3B0_ONLINE },
+ { 0x202f0, 1, RI_E3B0_ONLINE },
+ { 0x20400, 2, RI_ALL_ONLINE },
+ { 0x2040c, 8, RI_ALL_ONLINE },
+ { 0x2042c, 18, RI_E1HE2E3E3B0_ONLINE },
+ { 0x20480, 1, RI_ALL_ONLINE },
+ { 0x20500, 1, RI_ALL_ONLINE },
+ { 0x20600, 1, RI_ALL_ONLINE },
+ { 0x28000, 1, RI_ALL_ONLINE },
+ { 0x28004, 8191, RI_ALL_OFFLINE },
+ { 0x30000, 1, RI_ALL_ONLINE },
+ { 0x30004, 16383, RI_ALL_OFFLINE },
+ { 0x40000, 98, RI_ALL_ONLINE },
+ { 0x401a8, 8, RI_E1HE2E3E3B0_ONLINE },
+ { 0x401c8, 1, RI_E1H_ONLINE },
+ { 0x401cc, 2, RI_E1HE2E3E3B0_ONLINE },
+ { 0x401d4, 2, RI_E2E3E3B0_ONLINE },
+ { 0x40200, 4, RI_ALL_ONLINE },
+ { 0x40220, 6, RI_E2E3E3B0_ONLINE },
+ { 0x40238, 8, RI_E2E3_ONLINE },
+ { 0x40258, 4, RI_E2E3E3B0_ONLINE },
+ { 0x40268, 2, RI_E3E3B0_ONLINE },
+ { 0x40270, 17, RI_E3B0_ONLINE },
+ { 0x40400, 43, RI_ALL_ONLINE },
+ { 0x404cc, 3, RI_E1HE2E3E3B0_ONLINE },
+ { 0x404e0, 1, RI_E2E3E3B0_ONLINE },
+ { 0x40500, 2, RI_ALL_ONLINE },
+ { 0x40510, 2, RI_ALL_ONLINE },
+ { 0x40520, 2, RI_ALL_ONLINE },
+ { 0x40530, 2, RI_ALL_ONLINE },
+ { 0x40540, 2, RI_ALL_ONLINE },
+ { 0x40550, 10, RI_E2E3E3B0_ONLINE },
+ { 0x40610, 2, RI_E2E3E3B0_ONLINE },
+ { 0x42000, 164, RI_ALL_ONLINE },
+ { 0x422c0, 4, RI_E2E3E3B0_ONLINE },
+ { 0x422d4, 5, RI_E1HE2E3E3B0_ONLINE },
+ { 0x422e8, 1, RI_E2E3E3B0_ONLINE },
+ { 0x42400, 49, RI_ALL_ONLINE },
+ { 0x424c8, 38, RI_ALL_ONLINE },
+ { 0x42568, 2, RI_ALL_ONLINE },
+ { 0x42640, 5, RI_E2E3E3B0_ONLINE },
+ { 0x42800, 1, RI_ALL_ONLINE },
+ { 0x50000, 1, RI_ALL_ONLINE },
+ { 0x50004, 19, RI_ALL_ONLINE },
+ { 0x50050, 8, RI_ALL_ONLINE },
+ { 0x50070, 88, RI_ALL_ONLINE },
+ { 0x501f0, 4, RI_E1HE2E3E3B0_ONLINE },
+ { 0x50200, 2, RI_ALL_ONLINE },
+ { 0x5020c, 7, RI_ALL_ONLINE },
+ { 0x50228, 6, RI_E1HE2E3E3B0_ONLINE },
+ { 0x50240, 1, RI_ALL_ONLINE },
+ { 0x50280, 1, RI_ALL_ONLINE },
+ { 0x50300, 1, RI_E2E3E3B0_ONLINE },
+ { 0x5030c, 1, RI_E2E3E3B0_ONLINE },
+ { 0x50318, 1, RI_E2E3E3B0_ONLINE },
+ { 0x5031c, 1, RI_E2E3E3B0_ONLINE },
+ { 0x50320, 2, RI_E2E3E3B0_ONLINE },
+ { 0x50330, 1, RI_E3B0_ONLINE },
+ { 0x52000, 1, RI_ALL_ONLINE },
+ { 0x54000, 1, RI_ALL_ONLINE },
+ { 0x54004, 3327, RI_ALL_OFFLINE },
+ { 0x58000, 1, RI_ALL_ONLINE },
+ { 0x58004, 8191, RI_E1E1H_OFFLINE },
+ { 0x60000, 26, RI_ALL_ONLINE },
+ { 0x60068, 8, RI_E1E1H_ONLINE },
+ { 0x60088, 12, RI_ALL_ONLINE },
+ { 0x600b8, 9, RI_E1E1H_ONLINE },
+ { 0x600dc, 1, RI_ALL_ONLINE },
+ { 0x600e0, 5, RI_E1E1H_ONLINE },
+ { 0x600f4, 1, RI_E1E1HE2_ONLINE },
+ { 0x600f8, 1, RI_E1E1H_ONLINE },
+ { 0x600fc, 8, RI_ALL_ONLINE },
+ { 0x6013c, 24, RI_E1H_ONLINE },
+ { 0x6019c, 2, RI_E2E3E3B0_ONLINE },
+ { 0x601ac, 18, RI_E2E3E3B0_ONLINE },
+ { 0x60200, 1, RI_ALL_ONLINE },
+ { 0x60204, 2, RI_ALL_OFFLINE },
+ { 0x60210, 13, RI_E2E3E3B0_ONLINE },
+ { 0x60244, 16, RI_E3B0_ONLINE },
+ { 0x61000, 1, RI_ALL_ONLINE },
+ { 0x61004, 511, RI_ALL_OFFLINE },
+ { 0x61800, 512, RI_E3E3B0_OFFLINE },
+ { 0x70000, 8, RI_ALL_ONLINE },
+ { 0x70020, 8184, RI_ALL_OFFLINE },
+ { 0x78000, 8192, RI_E3E3B0_OFFLINE },
+ { 0x85000, 3, RI_ALL_ONLINE },
+ { 0x8501c, 7, RI_ALL_ONLINE },
+ { 0x85048, 1, RI_ALL_ONLINE },
+ { 0x85200, 32, RI_ALL_ONLINE },
+ { 0xb0000, 16384, RI_E1H_ONLINE },
+ { 0xc1000, 7, RI_ALL_ONLINE },
+ { 0xc103c, 2, RI_E2E3E3B0_ONLINE },
+ { 0xc1800, 2, RI_ALL_ONLINE },
+ { 0xc2000, 164, RI_ALL_ONLINE },
+ { 0xc22c0, 5, RI_E2E3E3B0_ONLINE },
+ { 0xc22d8, 4, RI_E2E3E3B0_ONLINE },
+ { 0xc2400, 49, RI_ALL_ONLINE },
+ { 0xc24c8, 38, RI_ALL_ONLINE },
+ { 0xc2568, 2, RI_ALL_ONLINE },
+ { 0xc2600, 1, RI_ALL_ONLINE },
+ { 0xc4000, 165, RI_ALL_ONLINE },
+ { 0xc42d8, 2, RI_E2E3E3B0_ONLINE },
+ { 0xc42e0, 7, RI_E1HE2E3E3B0_ONLINE },
+ { 0xc42fc, 1, RI_E2E3E3B0_ONLINE },
+ { 0xc4400, 51, RI_ALL_ONLINE },
+ { 0xc44d0, 38, RI_ALL_ONLINE },
+ { 0xc4570, 2, RI_ALL_ONLINE },
+ { 0xc4578, 5, RI_E2E3E3B0_ONLINE },
+ { 0xc4600, 1, RI_ALL_ONLINE },
+ { 0xd0000, 19, RI_ALL_ONLINE },
+ { 0xd004c, 8, RI_ALL_ONLINE },
+ { 0xd006c, 91, RI_ALL_ONLINE },
+ { 0xd01fc, 1, RI_E2E3E3B0_ONLINE },
+ { 0xd0200, 2, RI_ALL_ONLINE },
+ { 0xd020c, 7, RI_ALL_ONLINE },
+ { 0xd0228, 18, RI_E1HE2E3E3B0_ONLINE },
+ { 0xd0280, 1, RI_ALL_ONLINE },
+ { 0xd0300, 1, RI_ALL_ONLINE },
+ { 0xd0400, 1, RI_ALL_ONLINE },
+ { 0xd0818, 1, RI_E3B0_ONLINE },
+ { 0xd4000, 1, RI_ALL_ONLINE },
+ { 0xd4004, 2559, RI_ALL_OFFLINE },
+ { 0xd8000, 1, RI_ALL_ONLINE },
+ { 0xd8004, 8191, RI_ALL_OFFLINE },
+ { 0xe0000, 21, RI_ALL_ONLINE },
+ { 0xe0054, 8, RI_ALL_ONLINE },
+ { 0xe0074, 49, RI_ALL_ONLINE },
+ { 0xe0138, 1, RI_E1E1H_ONLINE },
+ { 0xe013c, 35, RI_ALL_ONLINE },
+ { 0xe01f4, 1, RI_E2_ONLINE },
+ { 0xe01f8, 1, RI_E2E3E3B0_ONLINE },
+ { 0xe0200, 2, RI_ALL_ONLINE },
+ { 0xe020c, 8, RI_ALL_ONLINE },
+ { 0xe022c, 18, RI_E1HE2E3E3B0_ONLINE },
+ { 0xe0280, 1, RI_ALL_ONLINE },
+ { 0xe0300, 1, RI_ALL_ONLINE },
+ { 0xe0400, 1, RI_E3B0_ONLINE },
+ { 0xe1000, 1, RI_ALL_ONLINE },
+ { 0xe2000, 1, RI_ALL_ONLINE },
+ { 0xe2004, 2047, RI_ALL_OFFLINE },
+ { 0xf0000, 1, RI_ALL_ONLINE },
+ { 0xf0004, 16383, RI_ALL_OFFLINE },
+ { 0x101000, 12, RI_ALL_ONLINE },
+ { 0x101050, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x101054, 3, RI_E2E3E3B0_ONLINE },
+ { 0x101100, 1, RI_ALL_ONLINE },
+ { 0x101800, 8, RI_ALL_ONLINE },
+ { 0x102000, 18, RI_ALL_ONLINE },
+ { 0x102068, 6, RI_E2E3E3B0_ONLINE },
+ { 0x102080, 17, RI_ALL_ONLINE },
+ { 0x1020c8, 8, RI_E1H_ONLINE },
+ { 0x1020e8, 9, RI_E2E3E3B0_ONLINE },
+ { 0x102400, 1, RI_ALL_ONLINE },
+ { 0x103000, 26, RI_ALL_ONLINE },
+ { 0x103098, 5, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1030ac, 2, RI_E2E3E3B0_ONLINE },
+ { 0x1030b4, 1, RI_E2_ONLINE },
+ { 0x1030b8, 7, RI_E2E3E3B0_ONLINE },
+ { 0x1030d8, 8, RI_E2E3E3B0_ONLINE },
+ { 0x103400, 1, RI_E2E3E3B0_ONLINE },
+ { 0x103404, 135, RI_E2E3E3B0_OFFLINE },
+ { 0x103800, 8, RI_ALL_ONLINE },
+ { 0x104000, 63, RI_ALL_ONLINE },
+ { 0x10411c, 16, RI_E2E3E3B0_ONLINE },
+ { 0x104200, 17, RI_ALL_ONLINE },
+ { 0x104400, 64, RI_ALL_ONLINE },
+ { 0x104500, 192, RI_ALL_OFFLINE },
+ { 0x104800, 64, RI_ALL_ONLINE },
+ { 0x104900, 192, RI_ALL_OFFLINE },
+ { 0x105000, 256, RI_ALL_ONLINE },
+ { 0x105400, 768, RI_ALL_OFFLINE },
+ { 0x107000, 7, RI_E2E3E3B0_ONLINE },
+ { 0x10701c, 1, RI_E3E3B0_ONLINE },
+ { 0x108000, 33, RI_E1E1H_ONLINE },
+ { 0x1080ac, 5, RI_E1H_ONLINE },
+ { 0x108100, 5, RI_E1E1H_ONLINE },
+ { 0x108120, 5, RI_E1E1H_ONLINE },
+ { 0x108200, 74, RI_E1E1H_ONLINE },
+ { 0x108400, 74, RI_E1E1H_ONLINE },
+ { 0x108800, 152, RI_E1E1H_ONLINE },
+ { 0x110000, 111, RI_E2E3E3B0_ONLINE },
+ { 0x1101dc, 1, RI_E3E3B0_ONLINE },
+ { 0x110200, 4, RI_E2E3E3B0_ONLINE },
+ { 0x120000, 2, RI_ALL_ONLINE },
+ { 0x120008, 4, RI_ALL_ONLINE },
+ { 0x120018, 3, RI_ALL_ONLINE },
+ { 0x120024, 4, RI_ALL_ONLINE },
+ { 0x120034, 3, RI_ALL_ONLINE },
+ { 0x120040, 4, RI_ALL_ONLINE },
+ { 0x120050, 3, RI_ALL_ONLINE },
+ { 0x12005c, 4, RI_ALL_ONLINE },
+ { 0x12006c, 3, RI_ALL_ONLINE },
+ { 0x120078, 4, RI_ALL_ONLINE },
+ { 0x120088, 3, RI_ALL_ONLINE },
+ { 0x120094, 4, RI_ALL_ONLINE },
+ { 0x1200a4, 3, RI_ALL_ONLINE },
+ { 0x1200b0, 4, RI_ALL_ONLINE },
+ { 0x1200c0, 3, RI_ALL_ONLINE },
+ { 0x1200cc, 4, RI_ALL_ONLINE },
+ { 0x1200dc, 3, RI_ALL_ONLINE },
+ { 0x1200e8, 4, RI_ALL_ONLINE },
+ { 0x1200f8, 3, RI_ALL_ONLINE },
+ { 0x120104, 4, RI_ALL_ONLINE },
+ { 0x120114, 1, RI_ALL_ONLINE },
+ { 0x120118, 22, RI_ALL_ONLINE },
+ { 0x120170, 2, RI_E1E1H_ONLINE },
+ { 0x120178, 243, RI_ALL_ONLINE },
+ { 0x120544, 4, RI_E1E1H_ONLINE },
+ { 0x120554, 6, RI_ALL_ONLINE },
+ { 0x12059c, 6, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1205b4, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1205b8, 15, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1205f4, 1, RI_E1HE2_ONLINE },
+ { 0x1205f8, 4, RI_E2E3E3B0_ONLINE },
+ { 0x120618, 1, RI_E2E3E3B0_ONLINE },
+ { 0x12061c, 20, RI_E1HE2E3E3B0_ONLINE },
+ { 0x12066c, 11, RI_E1HE2E3E3B0_ONLINE },
+ { 0x120698, 3, RI_E2E3E3B0_ONLINE },
+ { 0x1206a4, 1, RI_E2_ONLINE },
+ { 0x1206a8, 1, RI_E2E3E3B0_ONLINE },
+ { 0x1206b0, 75, RI_E2E3E3B0_ONLINE },
+ { 0x1207dc, 1, RI_E2_ONLINE },
+ { 0x1207fc, 1, RI_E2E3E3B0_ONLINE },
+ { 0x12080c, 65, RI_ALL_ONLINE },
+ { 0x120910, 7, RI_E2E3E3B0_ONLINE },
+ { 0x120930, 9, RI_E2E3E3B0_ONLINE },
+ { 0x12095c, 37, RI_E3E3B0_ONLINE },
+ { 0x120a00, 2, RI_E1E1HE2_ONLINE },
+ { 0x120b00, 1, RI_E3E3B0_ONLINE },
+ { 0x122000, 2, RI_ALL_ONLINE },
+ { 0x122008, 2046, RI_E1_OFFLINE },
+ { 0x128000, 2, RI_E1HE2E3E3B0_ONLINE },
+ { 0x128008, 6142, RI_E1HE2E3E3B0_OFFLINE },
+ { 0x130000, 35, RI_E2E3E3B0_ONLINE },
+ { 0x130100, 29, RI_E2E3E3B0_ONLINE },
+ { 0x130180, 1, RI_E2E3E3B0_ONLINE },
+ { 0x130200, 1, RI_E2E3E3B0_ONLINE },
+ { 0x130280, 1, RI_E2E3E3B0_ONLINE },
+ { 0x130300, 5, RI_E2E3E3B0_ONLINE },
+ { 0x130380, 1, RI_E2E3E3B0_ONLINE },
+ { 0x130400, 1, RI_E2E3E3B0_ONLINE },
+ { 0x130480, 5, RI_E2E3E3B0_ONLINE },
+ { 0x130800, 72, RI_E2E3E3B0_ONLINE },
+ { 0x131000, 136, RI_E2E3E3B0_ONLINE },
+ { 0x132000, 148, RI_E2E3E3B0_ONLINE },
+ { 0x134000, 544, RI_E2E3E3B0_ONLINE },
+ { 0x140000, 1, RI_ALL_ONLINE },
+ { 0x140004, 9, RI_E1E1HE2E3_ONLINE },
+ { 0x140028, 8, RI_ALL_ONLINE },
+ { 0x140048, 10, RI_E1E1HE2E3_ONLINE },
+ { 0x140070, 1, RI_ALL_ONLINE },
+ { 0x140074, 10, RI_E1E1HE2E3_ONLINE },
+ { 0x14009c, 1, RI_ALL_ONLINE },
+ { 0x1400a0, 5, RI_E1E1HE2E3_ONLINE },
+ { 0x1400b4, 7, RI_ALL_ONLINE },
+ { 0x1400d0, 10, RI_E1E1HE2E3_ONLINE },
+ { 0x1400f8, 2, RI_ALL_ONLINE },
+ { 0x140100, 5, RI_E1E1H_ONLINE },
+ { 0x140114, 5, RI_E1E1HE2E3_ONLINE },
+ { 0x140128, 7, RI_ALL_ONLINE },
+ { 0x140144, 9, RI_E1E1HE2E3_ONLINE },
+ { 0x140168, 8, RI_ALL_ONLINE },
+ { 0x140188, 3, RI_E1E1HE2E3_ONLINE },
+ { 0x140194, 13, RI_ALL_ONLINE },
+ { 0x140200, 6, RI_E1E1HE2E3_ONLINE },
+ { 0x140220, 4, RI_E2E3_ONLINE },
+ { 0x140240, 4, RI_E2E3_ONLINE },
+ { 0x140260, 4, RI_E2E3_ONLINE },
+ { 0x140280, 4, RI_E2E3_ONLINE },
+ { 0x1402a0, 4, RI_E2E3_ONLINE },
+ { 0x1402c0, 4, RI_E2E3_ONLINE },
+ { 0x1402e0, 2, RI_E2E3_ONLINE },
+ { 0x1402e8, 2, RI_E2E3E3B0_ONLINE },
+ { 0x1402f0, 9, RI_E2E3_ONLINE },
+ { 0x140314, 44, RI_E3B0_ONLINE },
+ { 0x1403d0, 70, RI_E3B0_ONLINE },
+ { 0x144000, 4, RI_E1E1H_ONLINE },
+ { 0x148000, 4, RI_E1E1H_ONLINE },
+ { 0x14c000, 4, RI_E1E1H_ONLINE },
+ { 0x150000, 4, RI_E1E1H_ONLINE },
+ { 0x154000, 4, RI_E1E1H_ONLINE },
+ { 0x158000, 4, RI_E1E1H_ONLINE },
+ { 0x15c000, 2, RI_E1HE2E3E3B0_ONLINE },
+ { 0x15c008, 5, RI_E1H_ONLINE },
+ { 0x15c020, 8, RI_E2E3E3B0_ONLINE },
+ { 0x15c040, 1, RI_E2E3_ONLINE },
+ { 0x15c044, 2, RI_E2E3E3B0_ONLINE },
+ { 0x15c04c, 8, RI_E2E3_ONLINE },
+ { 0x15c06c, 8, RI_E2E3E3B0_ONLINE },
+ { 0x15c090, 13, RI_E2E3E3B0_ONLINE },
+ { 0x15c0c8, 24, RI_E2E3E3B0_ONLINE },
+ { 0x15c128, 2, RI_E2E3_ONLINE },
+ { 0x15c130, 8, RI_E2E3E3B0_ONLINE },
+ { 0x15c150, 2, RI_E3E3B0_ONLINE },
+ { 0x15c158, 2, RI_E3_ONLINE },
+ { 0x15c160, 149, RI_E3B0_ONLINE },
+ { 0x161000, 7, RI_ALL_ONLINE },
+ { 0x16103c, 2, RI_E2E3E3B0_ONLINE },
+ { 0x161800, 2, RI_ALL_ONLINE },
+ { 0x162000, 54, RI_E3E3B0_ONLINE },
+ { 0x162200, 60, RI_E3E3B0_ONLINE },
+ { 0x162400, 54, RI_E3E3B0_ONLINE },
+ { 0x162600, 60, RI_E3E3B0_ONLINE },
+ { 0x162800, 54, RI_E3E3B0_ONLINE },
+ { 0x162a00, 60, RI_E3E3B0_ONLINE },
+ { 0x162c00, 54, RI_E3E3B0_ONLINE },
+ { 0x162e00, 60, RI_E3E3B0_ONLINE },
+ { 0x164000, 60, RI_ALL_ONLINE },
+ { 0x164110, 2, RI_E1HE2E3E3B0_ONLINE },
+ { 0x164118, 15, RI_E2E3E3B0_ONLINE },
+ { 0x164200, 1, RI_ALL_ONLINE },
+ { 0x164208, 1, RI_ALL_ONLINE },
+ { 0x164210, 1, RI_ALL_ONLINE },
+ { 0x164218, 1, RI_ALL_ONLINE },
+ { 0x164220, 1, RI_ALL_ONLINE },
+ { 0x164228, 1, RI_ALL_ONLINE },
+ { 0x164230, 1, RI_ALL_ONLINE },
+ { 0x164238, 1, RI_ALL_ONLINE },
+ { 0x164240, 1, RI_ALL_ONLINE },
+ { 0x164248, 1, RI_ALL_ONLINE },
+ { 0x164250, 1, RI_ALL_ONLINE },
+ { 0x164258, 1, RI_ALL_ONLINE },
+ { 0x164260, 1, RI_ALL_ONLINE },
+ { 0x164270, 2, RI_ALL_ONLINE },
+ { 0x164280, 2, RI_ALL_ONLINE },
+ { 0x164800, 2, RI_ALL_ONLINE },
+ { 0x165000, 2, RI_ALL_ONLINE },
+ { 0x166000, 164, RI_ALL_ONLINE },
+ { 0x1662cc, 7, RI_E2E3E3B0_ONLINE },
+ { 0x166400, 49, RI_ALL_ONLINE },
+ { 0x1664c8, 38, RI_ALL_ONLINE },
+ { 0x166568, 2, RI_ALL_ONLINE },
+ { 0x166570, 5, RI_E2E3E3B0_ONLINE },
+ { 0x166800, 1, RI_ALL_ONLINE },
+ { 0x168000, 137, RI_ALL_ONLINE },
+ { 0x168224, 2, RI_E1E1H_ONLINE },
+ { 0x16822c, 29, RI_ALL_ONLINE },
+ { 0x1682a0, 12, RI_E1E1H_ONLINE },
+ { 0x1682d0, 12, RI_ALL_ONLINE },
+ { 0x168300, 2, RI_E1E1H_ONLINE },
+ { 0x168308, 68, RI_ALL_ONLINE },
+ { 0x168418, 2, RI_E1E1H_ONLINE },
+ { 0x168420, 6, RI_ALL_ONLINE },
+ { 0x168800, 19, RI_ALL_ONLINE },
+ { 0x168900, 1, RI_ALL_ONLINE },
+ { 0x168a00, 128, RI_ALL_ONLINE },
+ { 0x16a000, 1, RI_ALL_ONLINE },
+ { 0x16a004, 1535, RI_ALL_OFFLINE },
+ { 0x16c000, 1, RI_ALL_ONLINE },
+ { 0x16c004, 1535, RI_ALL_OFFLINE },
+ { 0x16e000, 16, RI_E1H_ONLINE },
+ { 0x16e040, 8, RI_E2E3E3B0_ONLINE },
+ { 0x16e100, 1, RI_E1H_ONLINE },
+ { 0x16e200, 2, RI_E1H_ONLINE },
+ { 0x16e400, 161, RI_E1H_ONLINE },
+ { 0x16e684, 2, RI_E1HE2E3E3B0_ONLINE },
+ { 0x16e68c, 12, RI_E1H_ONLINE },
+ { 0x16e6bc, 4, RI_E1HE2E3E3B0_ONLINE },
+ { 0x16e6cc, 4, RI_E1H_ONLINE },
+ { 0x16e6e0, 2, RI_E2E3E3B0_ONLINE },
+ { 0x16e6e8, 5, RI_E2E3_ONLINE },
+ { 0x16e6fc, 5, RI_E2E3E3B0_ONLINE },
+ { 0x16e768, 17, RI_E2E3E3B0_ONLINE },
+ { 0x16e7ac, 12, RI_E3B0_ONLINE },
+ { 0x170000, 24, RI_ALL_ONLINE },
+ { 0x170060, 4, RI_E1E1H_ONLINE },
+ { 0x170070, 65, RI_ALL_ONLINE },
+ { 0x170194, 11, RI_E2E3E3B0_ONLINE },
+ { 0x1701c4, 1, RI_E2E3E3B0_ONLINE },
+ { 0x1701cc, 7, RI_E2E3E3B0_ONLINE },
+ { 0x1701e8, 1, RI_E3E3B0_ONLINE },
+ { 0x1701ec, 1, RI_E2E3E3B0_ONLINE },
+ { 0x1701f4, 1, RI_E2E3E3B0_ONLINE },
+ { 0x170200, 4, RI_ALL_ONLINE },
+ { 0x170214, 1, RI_ALL_ONLINE },
+ { 0x170218, 77, RI_E2E3E3B0_ONLINE },
+ { 0x170400, 64, RI_E2E3E3B0_ONLINE },
+ { 0x178000, 1, RI_ALL_ONLINE },
+ { 0x180000, 61, RI_ALL_ONLINE },
+ { 0x18013c, 2, RI_E1HE2E3E3B0_ONLINE },
+ { 0x180200, 58, RI_ALL_ONLINE },
+ { 0x180340, 4, RI_ALL_ONLINE },
+ { 0x180380, 1, RI_E2E3E3B0_ONLINE },
+ { 0x180388, 1, RI_E2E3E3B0_ONLINE },
+ { 0x180390, 1, RI_E2E3E3B0_ONLINE },
+ { 0x180398, 1, RI_E2E3E3B0_ONLINE },
+ { 0x1803a0, 5, RI_E2E3E3B0_ONLINE },
+ { 0x1803b4, 2, RI_E3E3B0_ONLINE },
+ { 0x180400, 1, RI_ALL_ONLINE },
+ { 0x180404, 255, RI_E1E1H_OFFLINE },
+ { 0x181000, 4, RI_ALL_ONLINE },
+ { 0x181010, 1020, RI_ALL_OFFLINE },
+ { 0x182000, 4, RI_E3E3B0_ONLINE },
+ { 0x1a0000, 1, RI_ALL_ONLINE },
+ { 0x1a0004, 5631, RI_ALL_OFFLINE },
+ { 0x1a5800, 2560, RI_E1HE2E3E3B0_OFFLINE },
+ { 0x1a8000, 1, RI_ALL_ONLINE },
+ { 0x1a8004, 8191, RI_E1HE2E3E3B0_OFFLINE },
+ { 0x1b0000, 1, RI_ALL_ONLINE },
+ { 0x1b0004, 15, RI_E1H_OFFLINE },
+ { 0x1b0040, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b0044, 239, RI_E1H_OFFLINE },
+ { 0x1b0400, 1, RI_ALL_ONLINE },
+ { 0x1b0404, 255, RI_E1H_OFFLINE },
+ { 0x1b0800, 1, RI_ALL_ONLINE },
+ { 0x1b0840, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b0c00, 1, RI_ALL_ONLINE },
+ { 0x1b1000, 1, RI_ALL_ONLINE },
+ { 0x1b1040, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b1400, 1, RI_ALL_ONLINE },
+ { 0x1b1440, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b1480, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b14c0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b1800, 128, RI_ALL_OFFLINE },
+ { 0x1b1c00, 128, RI_ALL_OFFLINE },
+ { 0x1b2000, 1, RI_ALL_ONLINE },
+ { 0x1b2400, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b2404, 5631, RI_E2E3E3B0_OFFLINE },
+ { 0x1b8000, 1, RI_ALL_ONLINE },
+ { 0x1b8040, 1, RI_ALL_ONLINE },
+ { 0x1b8080, 1, RI_ALL_ONLINE },
+ { 0x1b80c0, 1, RI_ALL_ONLINE },
+ { 0x1b8100, 1, RI_ALL_ONLINE },
+ { 0x1b8140, 1, RI_ALL_ONLINE },
+ { 0x1b8180, 1, RI_ALL_ONLINE },
+ { 0x1b81c0, 1, RI_ALL_ONLINE },
+ { 0x1b8200, 1, RI_ALL_ONLINE },
+ { 0x1b8240, 1, RI_ALL_ONLINE },
+ { 0x1b8280, 1, RI_ALL_ONLINE },
+ { 0x1b82c0, 1, RI_ALL_ONLINE },
+ { 0x1b8300, 1, RI_ALL_ONLINE },
+ { 0x1b8340, 1, RI_ALL_ONLINE },
+ { 0x1b8380, 1, RI_ALL_ONLINE },
+ { 0x1b83c0, 1, RI_ALL_ONLINE },
+ { 0x1b8400, 1, RI_ALL_ONLINE },
+ { 0x1b8440, 1, RI_ALL_ONLINE },
+ { 0x1b8480, 1, RI_ALL_ONLINE },
+ { 0x1b84c0, 1, RI_ALL_ONLINE },
+ { 0x1b8500, 1, RI_ALL_ONLINE },
+ { 0x1b8540, 1, RI_ALL_ONLINE },
+ { 0x1b8580, 1, RI_ALL_ONLINE },
+ { 0x1b85c0, 19, RI_E2E3E3B0_ONLINE },
+ { 0x1b8800, 1, RI_ALL_ONLINE },
+ { 0x1b8840, 1, RI_ALL_ONLINE },
+ { 0x1b8880, 1, RI_ALL_ONLINE },
+ { 0x1b88c0, 1, RI_ALL_ONLINE },
+ { 0x1b8900, 1, RI_ALL_ONLINE },
+ { 0x1b8940, 1, RI_ALL_ONLINE },
+ { 0x1b8980, 1, RI_ALL_ONLINE },
+ { 0x1b89c0, 1, RI_ALL_ONLINE },
+ { 0x1b8a00, 1, RI_ALL_ONLINE },
+ { 0x1b8a40, 1, RI_ALL_ONLINE },
+ { 0x1b8a80, 1, RI_ALL_ONLINE },
+ { 0x1b8ac0, 1, RI_ALL_ONLINE },
+ { 0x1b8b00, 1, RI_ALL_ONLINE },
+ { 0x1b8b40, 1, RI_ALL_ONLINE },
+ { 0x1b8b80, 1, RI_ALL_ONLINE },
+ { 0x1b8bc0, 1, RI_ALL_ONLINE },
+ { 0x1b8c00, 1, RI_ALL_ONLINE },
+ { 0x1b8c40, 1, RI_ALL_ONLINE },
+ { 0x1b8c80, 1, RI_ALL_ONLINE },
+ { 0x1b8cc0, 1, RI_ALL_ONLINE },
+ { 0x1b8cc4, 1, RI_E2E3E3B0_ONLINE },
+ { 0x1b8d00, 1, RI_ALL_ONLINE },
+ { 0x1b8d40, 1, RI_ALL_ONLINE },
+ { 0x1b8d80, 1, RI_ALL_ONLINE },
+ { 0x1b8dc0, 1, RI_ALL_ONLINE },
+ { 0x1b8e00, 1, RI_ALL_ONLINE },
+ { 0x1b8e40, 1, RI_ALL_ONLINE },
+ { 0x1b8e80, 1, RI_ALL_ONLINE },
+ { 0x1b8e84, 1, RI_E2E3E3B0_ONLINE },
+ { 0x1b8ec0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b8f00, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b8f40, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b8f80, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b8fc0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x1b8fc4, 2, RI_E2E3E3B0_ONLINE },
+ { 0x1b8fd0, 6, RI_E2E3E3B0_ONLINE },
+ { 0x1b8fe8, 2, RI_E3E3B0_ONLINE },
+ { 0x1b9000, 1, RI_E2E3E3B0_ONLINE },
+ { 0x1b9040, 3, RI_E2E3E3B0_ONLINE },
+ { 0x1b905c, 1, RI_E3E3B0_ONLINE },
+ { 0x1b9064, 1, RI_E3B0_ONLINE },
+ { 0x1b9080, 10, RI_E3B0_ONLINE },
+ { 0x1b9400, 14, RI_E2E3E3B0_ONLINE },
+ { 0x1b943c, 19, RI_E2E3E3B0_ONLINE },
+ { 0x1b9490, 10, RI_E2E3E3B0_ONLINE },
+ { 0x1c0000, 2, RI_ALL_ONLINE },
+ { 0x200000, 65, RI_ALL_ONLINE },
+ { 0x20014c, 2, RI_E1HE2E3E3B0_ONLINE },
+ { 0x200200, 58, RI_ALL_ONLINE },
+ { 0x200340, 4, RI_ALL_ONLINE },
+ { 0x200380, 1, RI_E2E3E3B0_ONLINE },
+ { 0x200388, 1, RI_E2E3E3B0_ONLINE },
+ { 0x200390, 1, RI_E2E3E3B0_ONLINE },
+ { 0x200398, 1, RI_E2E3E3B0_ONLINE },
+ { 0x2003a0, 1, RI_E2E3E3B0_ONLINE },
+ { 0x2003a8, 2, RI_E2E3E3B0_ONLINE },
+ { 0x200400, 1, RI_ALL_ONLINE },
+ { 0x200404, 255, RI_E1E1H_OFFLINE },
+ { 0x202000, 4, RI_ALL_ONLINE },
+ { 0x202010, 2044, RI_ALL_OFFLINE },
+ { 0x204000, 4, RI_E3E3B0_ONLINE },
+ { 0x220000, 1, RI_ALL_ONLINE },
+ { 0x220004, 5631, RI_ALL_OFFLINE },
+ { 0x225800, 2560, RI_E1HE2E3E3B0_OFFLINE },
+ { 0x228000, 1, RI_ALL_ONLINE },
+ { 0x228004, 8191, RI_E1HE2E3E3B0_OFFLINE },
+ { 0x230000, 1, RI_ALL_ONLINE },
+ { 0x230004, 15, RI_E1H_OFFLINE },
+ { 0x230040, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x230044, 239, RI_E1H_OFFLINE },
+ { 0x230400, 1, RI_ALL_ONLINE },
+ { 0x230404, 255, RI_E1H_OFFLINE },
+ { 0x230800, 1, RI_ALL_ONLINE },
+ { 0x230840, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x230c00, 1, RI_ALL_ONLINE },
+ { 0x231000, 1, RI_ALL_ONLINE },
+ { 0x231040, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x231400, 1, RI_ALL_ONLINE },
+ { 0x231440, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x231480, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2314c0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x231800, 128, RI_ALL_OFFLINE },
+ { 0x231c00, 128, RI_ALL_OFFLINE },
+ { 0x232000, 1, RI_ALL_ONLINE },
+ { 0x232400, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x232404, 5631, RI_E2E3E3B0_OFFLINE },
+ { 0x238000, 1, RI_ALL_ONLINE },
+ { 0x238040, 1, RI_ALL_ONLINE },
+ { 0x238080, 1, RI_ALL_ONLINE },
+ { 0x2380c0, 1, RI_ALL_ONLINE },
+ { 0x238100, 1, RI_ALL_ONLINE },
+ { 0x238140, 1, RI_ALL_ONLINE },
+ { 0x238180, 1, RI_ALL_ONLINE },
+ { 0x2381c0, 1, RI_ALL_ONLINE },
+ { 0x238200, 1, RI_ALL_ONLINE },
+ { 0x238240, 1, RI_ALL_ONLINE },
+ { 0x238280, 1, RI_ALL_ONLINE },
+ { 0x2382c0, 1, RI_ALL_ONLINE },
+ { 0x238300, 1, RI_ALL_ONLINE },
+ { 0x238340, 1, RI_ALL_ONLINE },
+ { 0x238380, 1, RI_ALL_ONLINE },
+ { 0x2383c0, 1, RI_ALL_ONLINE },
+ { 0x238400, 1, RI_ALL_ONLINE },
+ { 0x238440, 1, RI_ALL_ONLINE },
+ { 0x238480, 1, RI_ALL_ONLINE },
+ { 0x2384c0, 1, RI_ALL_ONLINE },
+ { 0x238500, 1, RI_ALL_ONLINE },
+ { 0x238540, 1, RI_ALL_ONLINE },
+ { 0x238580, 1, RI_ALL_ONLINE },
+ { 0x2385c0, 19, RI_E2E3E3B0_ONLINE },
+ { 0x238800, 1, RI_ALL_ONLINE },
+ { 0x238840, 1, RI_ALL_ONLINE },
+ { 0x238880, 1, RI_ALL_ONLINE },
+ { 0x2388c0, 1, RI_ALL_ONLINE },
+ { 0x238900, 1, RI_ALL_ONLINE },
+ { 0x238940, 1, RI_ALL_ONLINE },
+ { 0x238980, 1, RI_ALL_ONLINE },
+ { 0x2389c0, 1, RI_ALL_ONLINE },
+ { 0x238a00, 1, RI_ALL_ONLINE },
+ { 0x238a40, 1, RI_ALL_ONLINE },
+ { 0x238a80, 1, RI_ALL_ONLINE },
+ { 0x238ac0, 1, RI_ALL_ONLINE },
+ { 0x238b00, 1, RI_ALL_ONLINE },
+ { 0x238b40, 1, RI_ALL_ONLINE },
+ { 0x238b80, 1, RI_ALL_ONLINE },
+ { 0x238bc0, 1, RI_ALL_ONLINE },
+ { 0x238c00, 1, RI_ALL_ONLINE },
+ { 0x238c40, 1, RI_ALL_ONLINE },
+ { 0x238c80, 1, RI_ALL_ONLINE },
+ { 0x238cc0, 1, RI_ALL_ONLINE },
+ { 0x238cc4, 1, RI_E2E3E3B0_ONLINE },
+ { 0x238d00, 1, RI_ALL_ONLINE },
+ { 0x238d40, 1, RI_ALL_ONLINE },
+ { 0x238d80, 1, RI_ALL_ONLINE },
+ { 0x238dc0, 1, RI_ALL_ONLINE },
+ { 0x238e00, 1, RI_ALL_ONLINE },
+ { 0x238e40, 1, RI_ALL_ONLINE },
+ { 0x238e80, 1, RI_ALL_ONLINE },
+ { 0x238e84, 1, RI_E2E3E3B0_ONLINE },
+ { 0x238ec0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x238f00, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x238f40, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x238f80, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x238fc0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x238fc4, 2, RI_E2E3E3B0_ONLINE },
+ { 0x238fd0, 6, RI_E2E3E3B0_ONLINE },
+ { 0x238fe8, 2, RI_E3E3B0_ONLINE },
+ { 0x239000, 1, RI_E2E3E3B0_ONLINE },
+ { 0x239040, 3, RI_E2E3E3B0_ONLINE },
+ { 0x23905c, 1, RI_E3E3B0_ONLINE },
+ { 0x239064, 1, RI_E3B0_ONLINE },
+ { 0x239080, 10, RI_E3B0_ONLINE },
+ { 0x240000, 2, RI_ALL_ONLINE },
+ { 0x280000, 65, RI_ALL_ONLINE },
+ { 0x28014c, 2, RI_E1HE2E3E3B0_ONLINE },
+ { 0x280200, 58, RI_ALL_ONLINE },
+ { 0x280340, 4, RI_ALL_ONLINE },
+ { 0x280380, 1, RI_E2E3E3B0_ONLINE },
+ { 0x280388, 1, RI_E2E3E3B0_ONLINE },
+ { 0x280390, 1, RI_E2E3E3B0_ONLINE },
+ { 0x280398, 1, RI_E2E3E3B0_ONLINE },
+ { 0x2803a0, 1, RI_E2E3E3B0_ONLINE },
+ { 0x2803a8, 2, RI_E2E3E3B0_ONLINE },
+ { 0x280400, 1, RI_ALL_ONLINE },
+ { 0x280404, 255, RI_E1E1H_OFFLINE },
+ { 0x282000, 4, RI_ALL_ONLINE },
+ { 0x282010, 2044, RI_ALL_OFFLINE },
+ { 0x284000, 4, RI_E3E3B0_ONLINE },
+ { 0x2a0000, 1, RI_ALL_ONLINE },
+ { 0x2a0004, 5631, RI_ALL_OFFLINE },
+ { 0x2a5800, 2560, RI_E1HE2E3E3B0_OFFLINE },
+ { 0x2a8000, 1, RI_ALL_ONLINE },
+ { 0x2a8004, 8191, RI_E1HE2E3E3B0_OFFLINE },
+ { 0x2b0000, 1, RI_ALL_ONLINE },
+ { 0x2b0004, 15, RI_E1H_OFFLINE },
+ { 0x2b0040, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b0044, 239, RI_E1H_OFFLINE },
+ { 0x2b0400, 1, RI_ALL_ONLINE },
+ { 0x2b0404, 255, RI_E1H_OFFLINE },
+ { 0x2b0800, 1, RI_ALL_ONLINE },
+ { 0x2b0840, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b0c00, 1, RI_ALL_ONLINE },
+ { 0x2b1000, 1, RI_ALL_ONLINE },
+ { 0x2b1040, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b1400, 1, RI_ALL_ONLINE },
+ { 0x2b1440, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b1480, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b14c0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b1800, 128, RI_ALL_OFFLINE },
+ { 0x2b1c00, 128, RI_ALL_OFFLINE },
+ { 0x2b2000, 1, RI_ALL_ONLINE },
+ { 0x2b2400, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b2404, 5631, RI_E2E3E3B0_OFFLINE },
+ { 0x2b8000, 1, RI_ALL_ONLINE },
+ { 0x2b8040, 1, RI_ALL_ONLINE },
+ { 0x2b8080, 1, RI_ALL_ONLINE },
+ { 0x2b80c0, 1, RI_ALL_ONLINE },
+ { 0x2b8100, 1, RI_ALL_ONLINE },
+ { 0x2b8140, 1, RI_ALL_ONLINE },
+ { 0x2b8180, 1, RI_ALL_ONLINE },
+ { 0x2b81c0, 1, RI_ALL_ONLINE },
+ { 0x2b8200, 1, RI_ALL_ONLINE },
+ { 0x2b8240, 1, RI_ALL_ONLINE },
+ { 0x2b8280, 1, RI_ALL_ONLINE },
+ { 0x2b82c0, 1, RI_ALL_ONLINE },
+ { 0x2b8300, 1, RI_ALL_ONLINE },
+ { 0x2b8340, 1, RI_ALL_ONLINE },
+ { 0x2b8380, 1, RI_ALL_ONLINE },
+ { 0x2b83c0, 1, RI_ALL_ONLINE },
+ { 0x2b8400, 1, RI_ALL_ONLINE },
+ { 0x2b8440, 1, RI_ALL_ONLINE },
+ { 0x2b8480, 1, RI_ALL_ONLINE },
+ { 0x2b84c0, 1, RI_ALL_ONLINE },
+ { 0x2b8500, 1, RI_ALL_ONLINE },
+ { 0x2b8540, 1, RI_ALL_ONLINE },
+ { 0x2b8580, 1, RI_ALL_ONLINE },
+ { 0x2b85c0, 19, RI_E2E3E3B0_ONLINE },
+ { 0x2b8800, 1, RI_ALL_ONLINE },
+ { 0x2b8840, 1, RI_ALL_ONLINE },
+ { 0x2b8880, 1, RI_ALL_ONLINE },
+ { 0x2b88c0, 1, RI_ALL_ONLINE },
+ { 0x2b8900, 1, RI_ALL_ONLINE },
+ { 0x2b8940, 1, RI_ALL_ONLINE },
+ { 0x2b8980, 1, RI_ALL_ONLINE },
+ { 0x2b89c0, 1, RI_ALL_ONLINE },
+ { 0x2b8a00, 1, RI_ALL_ONLINE },
+ { 0x2b8a40, 1, RI_ALL_ONLINE },
+ { 0x2b8a80, 1, RI_ALL_ONLINE },
+ { 0x2b8ac0, 1, RI_ALL_ONLINE },
+ { 0x2b8b00, 1, RI_ALL_ONLINE },
+ { 0x2b8b40, 1, RI_ALL_ONLINE },
+ { 0x2b8b80, 1, RI_ALL_ONLINE },
+ { 0x2b8bc0, 1, RI_ALL_ONLINE },
+ { 0x2b8c00, 1, RI_ALL_ONLINE },
+ { 0x2b8c40, 1, RI_ALL_ONLINE },
+ { 0x2b8c80, 1, RI_ALL_ONLINE },
+ { 0x2b8cc0, 1, RI_ALL_ONLINE },
+ { 0x2b8cc4, 1, RI_E2E3E3B0_ONLINE },
+ { 0x2b8d00, 1, RI_ALL_ONLINE },
+ { 0x2b8d40, 1, RI_ALL_ONLINE },
+ { 0x2b8d80, 1, RI_ALL_ONLINE },
+ { 0x2b8dc0, 1, RI_ALL_ONLINE },
+ { 0x2b8e00, 1, RI_ALL_ONLINE },
+ { 0x2b8e40, 1, RI_ALL_ONLINE },
+ { 0x2b8e80, 1, RI_ALL_ONLINE },
+ { 0x2b8e84, 1, RI_E2E3E3B0_ONLINE },
+ { 0x2b8ec0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b8f00, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b8f40, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b8f80, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b8fc0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x2b8fc4, 2, RI_E2E3E3B0_ONLINE },
+ { 0x2b8fd0, 6, RI_E2E3E3B0_ONLINE },
+ { 0x2b8fe8, 2, RI_E3E3B0_ONLINE },
+ { 0x2b9000, 1, RI_E2E3E3B0_ONLINE },
+ { 0x2b9040, 3, RI_E2E3E3B0_ONLINE },
+ { 0x2b905c, 1, RI_E3E3B0_ONLINE },
+ { 0x2b9064, 1, RI_E3B0_ONLINE },
+ { 0x2b9080, 10, RI_E3B0_ONLINE },
+ { 0x2b9400, 14, RI_E2E3E3B0_ONLINE },
+ { 0x2b943c, 19, RI_E2E3E3B0_ONLINE },
+ { 0x2b9490, 10, RI_E2E3E3B0_ONLINE },
+ { 0x2c0000, 2, RI_ALL_ONLINE },
+ { 0x300000, 65, RI_ALL_ONLINE },
+ { 0x30014c, 2, RI_E1HE2E3E3B0_ONLINE },
+ { 0x300200, 58, RI_ALL_ONLINE },
+ { 0x300340, 4, RI_ALL_ONLINE },
+ { 0x300380, 1, RI_E2E3E3B0_ONLINE },
+ { 0x300388, 1, RI_E2E3E3B0_ONLINE },
+ { 0x300390, 1, RI_E2E3E3B0_ONLINE },
+ { 0x300398, 1, RI_E2E3E3B0_ONLINE },
+ { 0x3003a0, 1, RI_E2E3E3B0_ONLINE },
+ { 0x3003a8, 2, RI_E2E3E3B0_ONLINE },
+ { 0x300400, 1, RI_ALL_ONLINE },
+ { 0x300404, 255, RI_E1E1H_OFFLINE },
+ { 0x302000, 4, RI_ALL_ONLINE },
+ { 0x302010, 2044, RI_ALL_OFFLINE },
+ { 0x304000, 4, RI_E3E3B0_ONLINE },
+ { 0x320000, 1, RI_ALL_ONLINE },
+ { 0x320004, 5631, RI_ALL_OFFLINE },
+ { 0x325800, 2560, RI_E1HE2E3E3B0_OFFLINE },
+ { 0x328000, 1, RI_ALL_ONLINE },
+ { 0x328004, 8191, RI_E1HE2E3E3B0_OFFLINE },
+ { 0x330000, 1, RI_ALL_ONLINE },
+ { 0x330004, 15, RI_E1H_OFFLINE },
+ { 0x330040, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x330044, 239, RI_E1H_OFFLINE },
+ { 0x330400, 1, RI_ALL_ONLINE },
+ { 0x330404, 255, RI_E1H_OFFLINE },
+ { 0x330800, 1, RI_ALL_ONLINE },
+ { 0x330840, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x330c00, 1, RI_ALL_ONLINE },
+ { 0x331000, 1, RI_ALL_ONLINE },
+ { 0x331040, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x331400, 1, RI_ALL_ONLINE },
+ { 0x331440, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x331480, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x3314c0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x331800, 128, RI_ALL_OFFLINE },
+ { 0x331c00, 128, RI_ALL_OFFLINE },
+ { 0x332000, 1, RI_ALL_ONLINE },
+ { 0x332400, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x332404, 5631, RI_E2E3E3B0_OFFLINE },
+ { 0x338000, 1, RI_ALL_ONLINE },
+ { 0x338040, 1, RI_ALL_ONLINE },
+ { 0x338080, 1, RI_ALL_ONLINE },
+ { 0x3380c0, 1, RI_ALL_ONLINE },
+ { 0x338100, 1, RI_ALL_ONLINE },
+ { 0x338140, 1, RI_ALL_ONLINE },
+ { 0x338180, 1, RI_ALL_ONLINE },
+ { 0x3381c0, 1, RI_ALL_ONLINE },
+ { 0x338200, 1, RI_ALL_ONLINE },
+ { 0x338240, 1, RI_ALL_ONLINE },
+ { 0x338280, 1, RI_ALL_ONLINE },
+ { 0x3382c0, 1, RI_ALL_ONLINE },
+ { 0x338300, 1, RI_ALL_ONLINE },
+ { 0x338340, 1, RI_ALL_ONLINE },
+ { 0x338380, 1, RI_ALL_ONLINE },
+ { 0x3383c0, 1, RI_ALL_ONLINE },
+ { 0x338400, 1, RI_ALL_ONLINE },
+ { 0x338440, 1, RI_ALL_ONLINE },
+ { 0x338480, 1, RI_ALL_ONLINE },
+ { 0x3384c0, 1, RI_ALL_ONLINE },
+ { 0x338500, 1, RI_ALL_ONLINE },
+ { 0x338540, 1, RI_ALL_ONLINE },
+ { 0x338580, 1, RI_ALL_ONLINE },
+ { 0x3385c0, 19, RI_E2E3E3B0_ONLINE },
+ { 0x338800, 1, RI_ALL_ONLINE },
+ { 0x338840, 1, RI_ALL_ONLINE },
+ { 0x338880, 1, RI_ALL_ONLINE },
+ { 0x3388c0, 1, RI_ALL_ONLINE },
+ { 0x338900, 1, RI_ALL_ONLINE },
+ { 0x338940, 1, RI_ALL_ONLINE },
+ { 0x338980, 1, RI_ALL_ONLINE },
+ { 0x3389c0, 1, RI_ALL_ONLINE },
+ { 0x338a00, 1, RI_ALL_ONLINE },
+ { 0x338a40, 1, RI_ALL_ONLINE },
+ { 0x338a80, 1, RI_ALL_ONLINE },
+ { 0x338ac0, 1, RI_ALL_ONLINE },
+ { 0x338b00, 1, RI_ALL_ONLINE },
+ { 0x338b40, 1, RI_ALL_ONLINE },
+ { 0x338b80, 1, RI_ALL_ONLINE },
+ { 0x338bc0, 1, RI_ALL_ONLINE },
+ { 0x338c00, 1, RI_ALL_ONLINE },
+ { 0x338c40, 1, RI_ALL_ONLINE },
+ { 0x338c80, 1, RI_ALL_ONLINE },
+ { 0x338cc0, 1, RI_ALL_ONLINE },
+ { 0x338cc4, 1, RI_E2E3E3B0_ONLINE },
+ { 0x338d00, 1, RI_ALL_ONLINE },
+ { 0x338d40, 1, RI_ALL_ONLINE },
+ { 0x338d80, 1, RI_ALL_ONLINE },
+ { 0x338dc0, 1, RI_ALL_ONLINE },
+ { 0x338e00, 1, RI_ALL_ONLINE },
+ { 0x338e40, 1, RI_ALL_ONLINE },
+ { 0x338e80, 1, RI_ALL_ONLINE },
+ { 0x338e84, 1, RI_E2E3E3B0_ONLINE },
+ { 0x338ec0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x338f00, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x338f40, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x338f80, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x338fc0, 1, RI_E1HE2E3E3B0_ONLINE },
+ { 0x338fc4, 2, RI_E2E3E3B0_ONLINE },
+ { 0x338fd0, 6, RI_E2E3E3B0_ONLINE },
+ { 0x338fe8, 2, RI_E3E3B0_ONLINE },
+ { 0x339000, 1, RI_E2E3E3B0_ONLINE },
+ { 0x339040, 3, RI_E2E3E3B0_ONLINE },
+ { 0x33905c, 1, RI_E3E3B0_ONLINE },
+ { 0x339064, 1, RI_E3B0_ONLINE },
+ { 0x339080, 10, RI_E3B0_ONLINE },
+ { 0x340000, 2, RI_ALL_ONLINE },
+};
+#define REGS_COUNT ARRAY_SIZE(reg_addrs)
+
+static const struct dump_sign dump_sign_all = { 0x4e23fde1, 0x70017, 0x3a };
+
+static const u32 page_vals_e2[] = { 0, 128 };
+#define PAGE_MODE_VALUES_E2 ARRAY_SIZE(page_vals_e2)
+
+static const u32 page_write_regs_e2[] = { 328476 };
+#define PAGE_WRITE_REGS_E2 ARRAY_SIZE(page_write_regs_e2)
+
+static const struct reg_addr page_read_regs_e2[] = {
+ { 0x58000, 4608, RI_E2_ONLINE } };
+#define PAGE_READ_REGS_E2 ARRAY_SIZE(page_read_regs_e2)
+
+static const u32 page_vals_e3[] = { 0, 128 };
+#define PAGE_MODE_VALUES_E3 ARRAY_SIZE(page_vals_e3)
+
+static const u32 page_write_regs_e3[] = { 328476 };
+#define PAGE_WRITE_REGS_E3 ARRAY_SIZE(page_write_regs_e3)
+
+static const struct reg_addr page_read_regs_e3[] = {
+ { 0x58000, 4608, RI_E3E3B0_ONLINE } };
+#define PAGE_READ_REGS_E3 ARRAY_SIZE(page_read_regs_e3)
+
+#endif /* BNX2X_DUMP_H */
diff --git a/drivers/net/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
index 727fe89ff37f..f0ca8b27a55e 100644
--- a/drivers/net/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
@@ -14,6 +14,9 @@
* Statistics and Link management by Yitchak Gertner
*
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/types.h>
@@ -25,6 +28,7 @@
#include "bnx2x_cmn.h"
#include "bnx2x_dump.h"
#include "bnx2x_init.h"
+#include "bnx2x_sp.h"
/* Note: in the format strings below %s is replaced by the queue-name which is
* either its index or 'fcoe' for the fcoe queue. Make sure the format string
@@ -37,8 +41,6 @@ static const struct {
char string[ETH_GSTRING_LEN];
} bnx2x_q_stats_arr[] = {
/* 1 */ { Q_STATS_OFFSET32(total_bytes_received_hi), 8, "[%s]: rx_bytes" },
- { Q_STATS_OFFSET32(error_bytes_received_hi),
- 8, "[%s]: rx_error_bytes" },
{ Q_STATS_OFFSET32(total_unicast_packets_received_hi),
8, "[%s]: rx_ucast_packets" },
{ Q_STATS_OFFSET32(total_multicast_packets_received_hi),
@@ -52,13 +54,18 @@ static const struct {
4, "[%s]: rx_skb_alloc_discard" },
{ Q_STATS_OFFSET32(hw_csum_err), 4, "[%s]: rx_csum_offload_errors" },
-/* 10 */{ Q_STATS_OFFSET32(total_bytes_transmitted_hi), 8, "[%s]: tx_bytes" },
- { Q_STATS_OFFSET32(total_unicast_packets_transmitted_hi),
+ { Q_STATS_OFFSET32(total_bytes_transmitted_hi), 8, "[%s]: tx_bytes" },
+/* 10 */{ Q_STATS_OFFSET32(total_unicast_packets_transmitted_hi),
8, "[%s]: tx_ucast_packets" },
{ Q_STATS_OFFSET32(total_multicast_packets_transmitted_hi),
8, "[%s]: tx_mcast_packets" },
{ Q_STATS_OFFSET32(total_broadcast_packets_transmitted_hi),
- 8, "[%s]: tx_bcast_packets" }
+ 8, "[%s]: tx_bcast_packets" },
+ { Q_STATS_OFFSET32(total_tpa_aggregations_hi),
+ 8, "[%s]: tpa_aggregations" },
+ { Q_STATS_OFFSET32(total_tpa_aggregated_frames_hi),
+ 8, "[%s]: tpa_aggregated_frames"},
+ { Q_STATS_OFFSET32(total_tpa_bytes_hi), 8, "[%s]: tpa_bytes"}
};
#define BNX2X_NUM_Q_STATS ARRAY_SIZE(bnx2x_q_stats_arr)
@@ -98,8 +105,8 @@ static const struct {
8, STATS_FLAGS_BOTH, "rx_discards" },
{ STATS_OFFSET32(mac_filter_discard),
4, STATS_FLAGS_PORT, "rx_filtered_packets" },
- { STATS_OFFSET32(xxoverflow_discard),
- 4, STATS_FLAGS_PORT, "rx_fw_discards" },
+ { STATS_OFFSET32(mf_tag_discard),
+ 4, STATS_FLAGS_PORT, "rx_mf_tag_discard" },
{ STATS_OFFSET32(brb_drop_hi),
8, STATS_FLAGS_PORT, "rx_brb_discard" },
{ STATS_OFFSET32(brb_truncate_hi),
@@ -158,10 +165,43 @@ static const struct {
{ STATS_OFFSET32(etherstatspktsover1522octets_hi),
8, STATS_FLAGS_PORT, "tx_1523_to_9022_byte_packets" },
{ STATS_OFFSET32(pause_frames_sent_hi),
- 8, STATS_FLAGS_PORT, "tx_pause_frames" }
+ 8, STATS_FLAGS_PORT, "tx_pause_frames" },
+ { STATS_OFFSET32(total_tpa_aggregations_hi),
+ 8, STATS_FLAGS_FUNC, "tpa_aggregations" },
+ { STATS_OFFSET32(total_tpa_aggregated_frames_hi),
+ 8, STATS_FLAGS_FUNC, "tpa_aggregated_frames"},
+ { STATS_OFFSET32(total_tpa_bytes_hi),
+ 8, STATS_FLAGS_FUNC, "tpa_bytes"}
};
#define BNX2X_NUM_STATS ARRAY_SIZE(bnx2x_stats_arr)
+static int bnx2x_get_port_type(struct bnx2x *bp)
+{
+ int port_type;
+ u32 phy_idx = bnx2x_get_cur_phy_idx(bp);
+ switch (bp->link_params.phy[phy_idx].media_type) {
+ case ETH_PHY_SFP_FIBER:
+ case ETH_PHY_XFP_FIBER:
+ case ETH_PHY_KR:
+ case ETH_PHY_CX4:
+ port_type = PORT_FIBRE;
+ break;
+ case ETH_PHY_DA_TWINAX:
+ port_type = PORT_DA;
+ break;
+ case ETH_PHY_BASE_T:
+ port_type = PORT_TP;
+ break;
+ case ETH_PHY_NOT_PRESENT:
+ port_type = PORT_NONE;
+ break;
+ case ETH_PHY_UNSPECIFIED:
+ default:
+ port_type = PORT_OTHER;
+ break;
+ }
+ return port_type;
+}
static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
@@ -188,12 +228,7 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
if (IS_MF(bp))
ethtool_cmd_speed_set(cmd, bnx2x_get_mf_speed(bp));
- if (bp->port.supported[cfg_idx] & SUPPORTED_TP)
- cmd->port = PORT_TP;
- else if (bp->port.supported[cfg_idx] & SUPPORTED_FIBRE)
- cmd->port = PORT_FIBRE;
- else
- BNX2X_ERR("XGXS PHY Failure detected\n");
+ cmd->port = bnx2x_get_port_type(bp);
cmd->phy_address = bp->mdio.prtad;
cmd->transceiver = XCVR_INTERNAL;
@@ -207,9 +242,9 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
cmd->maxrxpkt = 0;
DP(NETIF_MSG_LINK, "ethtool_cmd: cmd %d\n"
- DP_LEVEL " supported 0x%x advertising 0x%x speed %u\n"
- DP_LEVEL " duplex %d port %d phy_address %d transceiver %d\n"
- DP_LEVEL " autoneg %d maxtxpkt %d maxrxpkt %d\n",
+ " supported 0x%x advertising 0x%x speed %u\n"
+ " duplex %d port %d phy_address %d transceiver %d\n"
+ " autoneg %d maxtxpkt %d maxrxpkt %d\n",
cmd->cmd, cmd->supported, cmd->advertising,
ethtool_cmd_speed(cmd),
cmd->duplex, cmd->port, cmd->phy_address, cmd->transceiver,
@@ -294,6 +329,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
PORT_HW_CFG_PHY_SELECTION_FIRST_PHY;
break;
case PORT_FIBRE:
+ case PORT_DA:
if (bp->port.supported[cfg_idx] & SUPPORTED_FIBRE)
break; /* no port change */
@@ -331,13 +367,50 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
}
/* advertise the requested speed and duplex if supported */
- cmd->advertising &= bp->port.supported[cfg_idx];
+ if (cmd->advertising & ~(bp->port.supported[cfg_idx])) {
+ DP(NETIF_MSG_LINK, "Advertisement parameters "
+ "are not supported\n");
+ return -EINVAL;
+ }
bp->link_params.req_line_speed[cfg_idx] = SPEED_AUTO_NEG;
- bp->link_params.req_duplex[cfg_idx] = DUPLEX_FULL;
- bp->port.advertising[cfg_idx] |= (ADVERTISED_Autoneg |
+ bp->link_params.req_duplex[cfg_idx] = cmd->duplex;
+ bp->port.advertising[cfg_idx] = (ADVERTISED_Autoneg |
cmd->advertising);
+ if (cmd->advertising) {
+ bp->link_params.speed_cap_mask[cfg_idx] = 0;
+ if (cmd->advertising & ADVERTISED_10baseT_Half) {
+ bp->link_params.speed_cap_mask[cfg_idx] |=
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF;
+ }
+ if (cmd->advertising & ADVERTISED_10baseT_Full)
+ bp->link_params.speed_cap_mask[cfg_idx] |=
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL;
+
+ if (cmd->advertising & ADVERTISED_100baseT_Full)
+ bp->link_params.speed_cap_mask[cfg_idx] |=
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL;
+
+ if (cmd->advertising & ADVERTISED_100baseT_Half) {
+ bp->link_params.speed_cap_mask[cfg_idx] |=
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF;
+ }
+ if (cmd->advertising & ADVERTISED_1000baseT_Half) {
+ bp->link_params.speed_cap_mask[cfg_idx] |=
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_1G;
+ }
+ if (cmd->advertising & (ADVERTISED_1000baseT_Full |
+ ADVERTISED_1000baseKX_Full))
+ bp->link_params.speed_cap_mask[cfg_idx] |=
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_1G;
+
+ if (cmd->advertising & (ADVERTISED_10000baseT_Full |
+ ADVERTISED_10000baseKX4_Full |
+ ADVERTISED_10000baseKR_Full))
+ bp->link_params.speed_cap_mask[cfg_idx] |=
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_10G;
+ }
} else { /* forced speed */
/* advertise the requested speed and duplex if supported */
switch (speed) {
@@ -450,7 +523,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
}
DP(NETIF_MSG_LINK, "req_line_speed %d\n"
- DP_LEVEL " req_duplex %d advertising 0x%x\n",
+ " req_duplex %d advertising 0x%x\n",
bp->link_params.req_line_speed[cfg_idx],
bp->link_params.req_duplex[cfg_idx],
bp->port.advertising[cfg_idx]);
@@ -468,78 +541,179 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
#define IS_E1_ONLINE(info) (((info) & RI_E1_ONLINE) == RI_E1_ONLINE)
#define IS_E1H_ONLINE(info) (((info) & RI_E1H_ONLINE) == RI_E1H_ONLINE)
#define IS_E2_ONLINE(info) (((info) & RI_E2_ONLINE) == RI_E2_ONLINE)
+#define IS_E3_ONLINE(info) (((info) & RI_E3_ONLINE) == RI_E3_ONLINE)
+#define IS_E3B0_ONLINE(info) (((info) & RI_E3B0_ONLINE) == RI_E3B0_ONLINE)
+
+static inline bool bnx2x_is_reg_online(struct bnx2x *bp,
+ const struct reg_addr *reg_info)
+{
+ if (CHIP_IS_E1(bp))
+ return IS_E1_ONLINE(reg_info->info);
+ else if (CHIP_IS_E1H(bp))
+ return IS_E1H_ONLINE(reg_info->info);
+ else if (CHIP_IS_E2(bp))
+ return IS_E2_ONLINE(reg_info->info);
+ else if (CHIP_IS_E3A0(bp))
+ return IS_E3_ONLINE(reg_info->info);
+ else if (CHIP_IS_E3B0(bp))
+ return IS_E3B0_ONLINE(reg_info->info);
+ else
+ return false;
+}
+
+/******* Paged registers info selectors ********/
+static inline const u32 *__bnx2x_get_page_addr_ar(struct bnx2x *bp)
+{
+ if (CHIP_IS_E2(bp))
+ return page_vals_e2;
+ else if (CHIP_IS_E3(bp))
+ return page_vals_e3;
+ else
+ return NULL;
+}
+
+static inline u32 __bnx2x_get_page_reg_num(struct bnx2x *bp)
+{
+ if (CHIP_IS_E2(bp))
+ return PAGE_MODE_VALUES_E2;
+ else if (CHIP_IS_E3(bp))
+ return PAGE_MODE_VALUES_E3;
+ else
+ return 0;
+}
+
+static inline const u32 *__bnx2x_get_page_write_ar(struct bnx2x *bp)
+{
+ if (CHIP_IS_E2(bp))
+ return page_write_regs_e2;
+ else if (CHIP_IS_E3(bp))
+ return page_write_regs_e3;
+ else
+ return NULL;
+}
+
+static inline u32 __bnx2x_get_page_write_num(struct bnx2x *bp)
+{
+ if (CHIP_IS_E2(bp))
+ return PAGE_WRITE_REGS_E2;
+ else if (CHIP_IS_E3(bp))
+ return PAGE_WRITE_REGS_E3;
+ else
+ return 0;
+}
+
+static inline const struct reg_addr *__bnx2x_get_page_read_ar(struct bnx2x *bp)
+{
+ if (CHIP_IS_E2(bp))
+ return page_read_regs_e2;
+ else if (CHIP_IS_E3(bp))
+ return page_read_regs_e3;
+ else
+ return NULL;
+}
+
+static inline u32 __bnx2x_get_page_read_num(struct bnx2x *bp)
+{
+ if (CHIP_IS_E2(bp))
+ return PAGE_READ_REGS_E2;
+ else if (CHIP_IS_E3(bp))
+ return PAGE_READ_REGS_E3;
+ else
+ return 0;
+}
+
+static inline int __bnx2x_get_regs_len(struct bnx2x *bp)
+{
+ int num_pages = __bnx2x_get_page_reg_num(bp);
+ int page_write_num = __bnx2x_get_page_write_num(bp);
+ const struct reg_addr *page_read_addr = __bnx2x_get_page_read_ar(bp);
+ int page_read_num = __bnx2x_get_page_read_num(bp);
+ int regdump_len = 0;
+ int i, j, k;
+
+ for (i = 0; i < REGS_COUNT; i++)
+ if (bnx2x_is_reg_online(bp, &reg_addrs[i]))
+ regdump_len += reg_addrs[i].size;
+
+ for (i = 0; i < num_pages; i++)
+ for (j = 0; j < page_write_num; j++)
+ for (k = 0; k < page_read_num; k++)
+ if (bnx2x_is_reg_online(bp, &page_read_addr[k]))
+ regdump_len += page_read_addr[k].size;
+
+ return regdump_len;
+}
static int bnx2x_get_regs_len(struct net_device *dev)
{
struct bnx2x *bp = netdev_priv(dev);
int regdump_len = 0;
- int i, j, k;
- if (CHIP_IS_E1(bp)) {
- for (i = 0; i < REGS_COUNT; i++)
- if (IS_E1_ONLINE(reg_addrs[i].info))
- regdump_len += reg_addrs[i].size;
-
- for (i = 0; i < WREGS_COUNT_E1; i++)
- if (IS_E1_ONLINE(wreg_addrs_e1[i].info))
- regdump_len += wreg_addrs_e1[i].size *
- (1 + wreg_addrs_e1[i].read_regs_count);
-
- } else if (CHIP_IS_E1H(bp)) {
- for (i = 0; i < REGS_COUNT; i++)
- if (IS_E1H_ONLINE(reg_addrs[i].info))
- regdump_len += reg_addrs[i].size;
-
- for (i = 0; i < WREGS_COUNT_E1H; i++)
- if (IS_E1H_ONLINE(wreg_addrs_e1h[i].info))
- regdump_len += wreg_addrs_e1h[i].size *
- (1 + wreg_addrs_e1h[i].read_regs_count);
- } else if (CHIP_IS_E2(bp)) {
- for (i = 0; i < REGS_COUNT; i++)
- if (IS_E2_ONLINE(reg_addrs[i].info))
- regdump_len += reg_addrs[i].size;
-
- for (i = 0; i < WREGS_COUNT_E2; i++)
- if (IS_E2_ONLINE(wreg_addrs_e2[i].info))
- regdump_len += wreg_addrs_e2[i].size *
- (1 + wreg_addrs_e2[i].read_regs_count);
-
- for (i = 0; i < PAGE_MODE_VALUES_E2; i++)
- for (j = 0; j < PAGE_WRITE_REGS_E2; j++) {
- for (k = 0; k < PAGE_READ_REGS_E2; k++)
- if (IS_E2_ONLINE(page_read_regs_e2[k].
- info))
- regdump_len +=
- page_read_regs_e2[k].size;
- }
- }
+ regdump_len = __bnx2x_get_regs_len(bp);
regdump_len *= 4;
regdump_len += sizeof(struct dump_hdr);
return regdump_len;
}
-static inline void bnx2x_read_pages_regs_e2(struct bnx2x *bp, u32 *p)
+/**
+ * bnx2x_read_pages_regs - read "paged" registers
+ *
+ * @bp device handle
+ * @p output buffer
+ *
+ * Reads "paged" memories: memories that may only be read by first writing to a
+ * specific address ("write address") and then reading from a specific address
+ * ("read address"). There may be more than one write address per "page" and
+ * more than one read address per write address.
+ */
+static inline void bnx2x_read_pages_regs(struct bnx2x *bp, u32 *p)
{
u32 i, j, k, n;
-
- for (i = 0; i < PAGE_MODE_VALUES_E2; i++) {
- for (j = 0; j < PAGE_WRITE_REGS_E2; j++) {
- REG_WR(bp, page_write_regs_e2[j], page_vals_e2[i]);
- for (k = 0; k < PAGE_READ_REGS_E2; k++)
- if (IS_E2_ONLINE(page_read_regs_e2[k].info))
+ /* addresses of the paged registers */
+ const u32 *page_addr = __bnx2x_get_page_addr_ar(bp);
+ /* number of paged registers */
+ int num_pages = __bnx2x_get_page_reg_num(bp);
+ /* write addresses */
+ const u32 *write_addr = __bnx2x_get_page_write_ar(bp);
+ /* number of write addresses */
+ int write_num = __bnx2x_get_page_write_num(bp);
+ /* read addresses info */
+ const struct reg_addr *read_addr = __bnx2x_get_page_read_ar(bp);
+ /* number of read addresses */
+ int read_num = __bnx2x_get_page_read_num(bp);
+
+ for (i = 0; i < num_pages; i++) {
+ for (j = 0; j < write_num; j++) {
+ REG_WR(bp, write_addr[j], page_addr[i]);
+ for (k = 0; k < read_num; k++)
+ if (bnx2x_is_reg_online(bp, &read_addr[k]))
for (n = 0; n <
- page_read_regs_e2[k].size; n++)
+ read_addr[k].size; n++)
*p++ = REG_RD(bp,
- page_read_regs_e2[k].addr + n*4);
+ read_addr[k].addr + n*4);
}
}
}
+static inline void __bnx2x_get_regs(struct bnx2x *bp, u32 *p)
+{
+ u32 i, j;
+
+ /* Read the regular registers */
+ for (i = 0; i < REGS_COUNT; i++)
+ if (bnx2x_is_reg_online(bp, &reg_addrs[i]))
+ for (j = 0; j < reg_addrs[i].size; j++)
+ *p++ = REG_RD(bp, reg_addrs[i].addr + j*4);
+
+ /* Read "paged" registes */
+ bnx2x_read_pages_regs(bp, p);
+}
+
static void bnx2x_get_regs(struct net_device *dev,
struct ethtool_regs *regs, void *_p)
{
- u32 *p = _p, i, j;
+ u32 *p = _p;
struct bnx2x *bp = netdev_priv(dev);
struct dump_hdr dump_hdr = {0};
@@ -566,44 +740,21 @@ static void bnx2x_get_regs(struct net_device *dev,
dump_hdr.info = RI_E1_ONLINE;
else if (CHIP_IS_E1H(bp))
dump_hdr.info = RI_E1H_ONLINE;
- else if (CHIP_IS_E2(bp))
+ else if (!CHIP_IS_E1x(bp))
dump_hdr.info = RI_E2_ONLINE |
(BP_PATH(bp) ? RI_PATH1_DUMP : RI_PATH0_DUMP);
memcpy(p, &dump_hdr, sizeof(struct dump_hdr));
p += dump_hdr.hdr_size + 1;
- if (CHIP_IS_E1(bp)) {
- for (i = 0; i < REGS_COUNT; i++)
- if (IS_E1_ONLINE(reg_addrs[i].info))
- for (j = 0; j < reg_addrs[i].size; j++)
- *p++ = REG_RD(bp,
- reg_addrs[i].addr + j*4);
-
- } else if (CHIP_IS_E1H(bp)) {
- for (i = 0; i < REGS_COUNT; i++)
- if (IS_E1H_ONLINE(reg_addrs[i].info))
- for (j = 0; j < reg_addrs[i].size; j++)
- *p++ = REG_RD(bp,
- reg_addrs[i].addr + j*4);
-
- } else if (CHIP_IS_E2(bp)) {
- for (i = 0; i < REGS_COUNT; i++)
- if (IS_E2_ONLINE(reg_addrs[i].info))
- for (j = 0; j < reg_addrs[i].size; j++)
- *p++ = REG_RD(bp,
- reg_addrs[i].addr + j*4);
-
- bnx2x_read_pages_regs_e2(bp, p);
- }
+ /* Actually read the registers */
+ __bnx2x_get_regs(bp, p);
+
/* Re-enable parity attentions */
bnx2x_clear_blocks_parity(bp);
- if (CHIP_PARITY_ENABLED(bp))
- bnx2x_enable_blocks_parity(bp);
+ bnx2x_enable_blocks_parity(bp);
}
-#define PHY_FW_VER_LEN 20
-
static void bnx2x_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
@@ -682,8 +833,12 @@ static void bnx2x_set_msglevel(struct net_device *dev, u32 level)
{
struct bnx2x *bp = netdev_priv(dev);
- if (capable(CAP_NET_ADMIN))
+ if (capable(CAP_NET_ADMIN)) {
+ /* dump MCP trace */
+ if (level & BNX2X_MSG_MCP)
+ bnx2x_fw_dump_lvl(bp, KERN_INFO);
bp->msg_enable = level;
+ }
}
static int bnx2x_nway_reset(struct net_device *dev)
@@ -725,7 +880,7 @@ static int bnx2x_acquire_nvram_lock(struct bnx2x *bp)
u32 val = 0;
/* adjust timeout for emulation/FPGA */
- count = NVRAM_TIMEOUT_COUNT;
+ count = BNX2X_NVRAM_TIMEOUT_COUNT;
if (CHIP_REV_IS_SLOW(bp))
count *= 100;
@@ -756,7 +911,7 @@ static int bnx2x_release_nvram_lock(struct bnx2x *bp)
u32 val = 0;
/* adjust timeout for emulation/FPGA */
- count = NVRAM_TIMEOUT_COUNT;
+ count = BNX2X_NVRAM_TIMEOUT_COUNT;
if (CHIP_REV_IS_SLOW(bp))
count *= 100;
@@ -824,7 +979,7 @@ static int bnx2x_nvram_read_dword(struct bnx2x *bp, u32 offset, __be32 *ret_val,
REG_WR(bp, MCP_REG_MCPR_NVM_COMMAND, cmd_flags);
/* adjust timeout for emulation/FPGA */
- count = NVRAM_TIMEOUT_COUNT;
+ count = BNX2X_NVRAM_TIMEOUT_COUNT;
if (CHIP_REV_IS_SLOW(bp))
count *= 100;
@@ -914,7 +1069,7 @@ static int bnx2x_get_eeprom(struct net_device *dev,
return -EAGAIN;
DP(BNX2X_MSG_NVM, "ethtool_eeprom: cmd %d\n"
- DP_LEVEL " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n",
+ " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n",
eeprom->cmd, eeprom->magic, eeprom->offset, eeprom->offset,
eeprom->len, eeprom->len);
@@ -947,7 +1102,7 @@ static int bnx2x_nvram_write_dword(struct bnx2x *bp, u32 offset, u32 val,
REG_WR(bp, MCP_REG_MCPR_NVM_COMMAND, cmd_flags);
/* adjust timeout for emulation/FPGA */
- count = NVRAM_TIMEOUT_COUNT;
+ count = BNX2X_NVRAM_TIMEOUT_COUNT;
if (CHIP_REV_IS_SLOW(bp))
count *= 100;
@@ -1051,9 +1206,9 @@ static int bnx2x_nvram_write(struct bnx2x *bp, u32 offset, u8 *data_buf,
while ((written_so_far < buf_size) && (rc == 0)) {
if (written_so_far == (buf_size - sizeof(u32)))
cmd_flags |= MCPR_NVM_COMMAND_LAST;
- else if (((offset + 4) % NVRAM_PAGE_SIZE) == 0)
+ else if (((offset + 4) % BNX2X_NVRAM_PAGE_SIZE) == 0)
cmd_flags |= MCPR_NVM_COMMAND_LAST;
- else if ((offset % NVRAM_PAGE_SIZE) == 0)
+ else if ((offset % BNX2X_NVRAM_PAGE_SIZE) == 0)
cmd_flags |= MCPR_NVM_COMMAND_FIRST;
memcpy(&val, data_buf, 4);
@@ -1085,7 +1240,7 @@ static int bnx2x_set_eeprom(struct net_device *dev,
return -EAGAIN;
DP(BNX2X_MSG_NVM, "ethtool_eeprom: cmd %d\n"
- DP_LEVEL " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n",
+ " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n",
eeprom->cmd, eeprom->magic, eeprom->offset, eeprom->offset,
eeprom->len, eeprom->len);
@@ -1190,19 +1345,11 @@ static void bnx2x_get_ringparam(struct net_device *dev,
struct bnx2x *bp = netdev_priv(dev);
ering->rx_max_pending = MAX_RX_AVAIL;
- ering->rx_mini_max_pending = 0;
- ering->rx_jumbo_max_pending = 0;
if (bp->rx_ring_size)
ering->rx_pending = bp->rx_ring_size;
else
- if (bp->state == BNX2X_STATE_OPEN && bp->num_queues)
- ering->rx_pending = MAX_RX_AVAIL/bp->num_queues;
- else
- ering->rx_pending = MAX_RX_AVAIL;
-
- ering->rx_mini_pending = 0;
- ering->rx_jumbo_pending = 0;
+ ering->rx_pending = MAX_RX_AVAIL;
ering->tx_max_pending = MAX_TX_AVAIL;
ering->tx_pending = bp->tx_ring_size;
@@ -1212,10 +1359,9 @@ static int bnx2x_set_ringparam(struct net_device *dev,
struct ethtool_ringparam *ering)
{
struct bnx2x *bp = netdev_priv(dev);
- int rc = 0;
if (bp->recovery_state != BNX2X_RECOVERY_DONE) {
- printk(KERN_ERR "Handling parity error recovery. Try again later\n");
+ pr_err("Handling parity error recovery. Try again later\n");
return -EAGAIN;
}
@@ -1229,12 +1375,7 @@ static int bnx2x_set_ringparam(struct net_device *dev,
bp->rx_ring_size = ering->rx_pending;
bp->tx_ring_size = ering->tx_pending;
- if (netif_running(dev)) {
- bnx2x_nic_unload(bp, UNLOAD_NORMAL);
- rc = bnx2x_nic_load(bp, LOAD_NORMAL);
- }
-
- return rc;
+ return bnx2x_reload_if_running(dev);
}
static void bnx2x_get_pauseparam(struct net_device *dev,
@@ -1251,7 +1392,7 @@ static void bnx2x_get_pauseparam(struct net_device *dev,
BNX2X_FLOW_CTRL_TX);
DP(NETIF_MSG_LINK, "ethtool_pauseparam: cmd %d\n"
- DP_LEVEL " autoneg %d rx_pause %d tx_pause %d\n",
+ " autoneg %d rx_pause %d tx_pause %d\n",
epause->cmd, epause->autoneg, epause->rx_pause, epause->tx_pause);
}
@@ -1264,7 +1405,7 @@ static int bnx2x_set_pauseparam(struct net_device *dev,
return 0;
DP(NETIF_MSG_LINK, "ethtool_pauseparam: cmd %d\n"
- DP_LEVEL " autoneg %d rx_pause %d tx_pause %d\n",
+ " autoneg %d rx_pause %d tx_pause %d\n",
epause->cmd, epause->autoneg, epause->rx_pause, epause->tx_pause);
bp->link_params.req_flow_ctrl[cfg_idx] = BNX2X_FLOW_CTRL_AUTO;
@@ -1313,60 +1454,129 @@ static const struct {
{ "idle check (online)" }
};
+enum {
+ BNX2X_CHIP_E1_OFST = 0,
+ BNX2X_CHIP_E1H_OFST,
+ BNX2X_CHIP_E2_OFST,
+ BNX2X_CHIP_E3_OFST,
+ BNX2X_CHIP_E3B0_OFST,
+ BNX2X_CHIP_MAX_OFST
+};
+
+#define BNX2X_CHIP_MASK_E1 (1 << BNX2X_CHIP_E1_OFST)
+#define BNX2X_CHIP_MASK_E1H (1 << BNX2X_CHIP_E1H_OFST)
+#define BNX2X_CHIP_MASK_E2 (1 << BNX2X_CHIP_E2_OFST)
+#define BNX2X_CHIP_MASK_E3 (1 << BNX2X_CHIP_E3_OFST)
+#define BNX2X_CHIP_MASK_E3B0 (1 << BNX2X_CHIP_E3B0_OFST)
+
+#define BNX2X_CHIP_MASK_ALL ((1 << BNX2X_CHIP_MAX_OFST) - 1)
+#define BNX2X_CHIP_MASK_E1X (BNX2X_CHIP_MASK_E1 | BNX2X_CHIP_MASK_E1H)
+
static int bnx2x_test_registers(struct bnx2x *bp)
{
int idx, i, rc = -ENODEV;
- u32 wr_val = 0;
+ u32 wr_val = 0, hw;
int port = BP_PORT(bp);
static const struct {
+ u32 hw;
u32 offset0;
u32 offset1;
u32 mask;
} reg_tbl[] = {
-/* 0 */ { BRB1_REG_PAUSE_LOW_THRESHOLD_0, 4, 0x000003ff },
- { DORQ_REG_DB_ADDR0, 4, 0xffffffff },
- { HC_REG_AGG_INT_0, 4, 0x000003ff },
- { PBF_REG_MAC_IF0_ENABLE, 4, 0x00000001 },
- { PBF_REG_P0_INIT_CRD, 4, 0x000007ff },
- { PRS_REG_CID_PORT_0, 4, 0x00ffffff },
- { PXP2_REG_PSWRQ_CDU0_L2P, 4, 0x000fffff },
- { PXP2_REG_RQ_CDU0_EFIRST_MEM_ADDR, 8, 0x0003ffff },
- { PXP2_REG_PSWRQ_TM0_L2P, 4, 0x000fffff },
- { PXP2_REG_RQ_USDM0_EFIRST_MEM_ADDR, 8, 0x0003ffff },
-/* 10 */ { PXP2_REG_PSWRQ_TSDM0_L2P, 4, 0x000fffff },
- { QM_REG_CONNNUM_0, 4, 0x000fffff },
- { TM_REG_LIN0_MAX_ACTIVE_CID, 4, 0x0003ffff },
- { SRC_REG_KEYRSS0_0, 40, 0xffffffff },
- { SRC_REG_KEYRSS0_7, 40, 0xffffffff },
- { XCM_REG_WU_DA_SET_TMR_CNT_FLG_CMD00, 4, 0x00000001 },
- { XCM_REG_WU_DA_CNT_CMD00, 4, 0x00000003 },
- { XCM_REG_GLB_DEL_ACK_MAX_CNT_0, 4, 0x000000ff },
- { NIG_REG_LLH0_T_BIT, 4, 0x00000001 },
- { NIG_REG_EMAC0_IN_EN, 4, 0x00000001 },
-/* 20 */ { NIG_REG_BMAC0_IN_EN, 4, 0x00000001 },
- { NIG_REG_XCM0_OUT_EN, 4, 0x00000001 },
- { NIG_REG_BRB0_OUT_EN, 4, 0x00000001 },
- { NIG_REG_LLH0_XCM_MASK, 4, 0x00000007 },
- { NIG_REG_LLH0_ACPI_PAT_6_LEN, 68, 0x000000ff },
- { NIG_REG_LLH0_ACPI_PAT_0_CRC, 68, 0xffffffff },
- { NIG_REG_LLH0_DEST_MAC_0_0, 160, 0xffffffff },
- { NIG_REG_LLH0_DEST_IP_0_1, 160, 0xffffffff },
- { NIG_REG_LLH0_IPV4_IPV6_0, 160, 0x00000001 },
- { NIG_REG_LLH0_DEST_UDP_0, 160, 0x0000ffff },
-/* 30 */ { NIG_REG_LLH0_DEST_TCP_0, 160, 0x0000ffff },
- { NIG_REG_LLH0_VLAN_ID_0, 160, 0x00000fff },
- { NIG_REG_XGXS_SERDES0_MODE_SEL, 4, 0x00000001 },
- { NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0, 4, 0x00000001 },
- { NIG_REG_STATUS_INTERRUPT_PORT0, 4, 0x07ffffff },
- { NIG_REG_XGXS0_CTRL_EXTREMOTEMDIOST, 24, 0x00000001 },
- { NIG_REG_SERDES0_CTRL_PHY_ADDR, 16, 0x0000001f },
-
- { 0xffffffff, 0, 0x00000000 }
+/* 0 */ { BNX2X_CHIP_MASK_ALL,
+ BRB1_REG_PAUSE_LOW_THRESHOLD_0, 4, 0x000003ff },
+ { BNX2X_CHIP_MASK_ALL,
+ DORQ_REG_DB_ADDR0, 4, 0xffffffff },
+ { BNX2X_CHIP_MASK_E1X,
+ HC_REG_AGG_INT_0, 4, 0x000003ff },
+ { BNX2X_CHIP_MASK_ALL,
+ PBF_REG_MAC_IF0_ENABLE, 4, 0x00000001 },
+ { BNX2X_CHIP_MASK_E1X | BNX2X_CHIP_MASK_E2 | BNX2X_CHIP_MASK_E3,
+ PBF_REG_P0_INIT_CRD, 4, 0x000007ff },
+ { BNX2X_CHIP_MASK_E3B0,
+ PBF_REG_INIT_CRD_Q0, 4, 0x000007ff },
+ { BNX2X_CHIP_MASK_ALL,
+ PRS_REG_CID_PORT_0, 4, 0x00ffffff },
+ { BNX2X_CHIP_MASK_ALL,
+ PXP2_REG_PSWRQ_CDU0_L2P, 4, 0x000fffff },
+ { BNX2X_CHIP_MASK_ALL,
+ PXP2_REG_RQ_CDU0_EFIRST_MEM_ADDR, 8, 0x0003ffff },
+ { BNX2X_CHIP_MASK_ALL,
+ PXP2_REG_PSWRQ_TM0_L2P, 4, 0x000fffff },
+/* 10 */ { BNX2X_CHIP_MASK_ALL,
+ PXP2_REG_RQ_USDM0_EFIRST_MEM_ADDR, 8, 0x0003ffff },
+ { BNX2X_CHIP_MASK_ALL,
+ PXP2_REG_PSWRQ_TSDM0_L2P, 4, 0x000fffff },
+ { BNX2X_CHIP_MASK_ALL,
+ QM_REG_CONNNUM_0, 4, 0x000fffff },
+ { BNX2X_CHIP_MASK_ALL,
+ TM_REG_LIN0_MAX_ACTIVE_CID, 4, 0x0003ffff },
+ { BNX2X_CHIP_MASK_ALL,
+ SRC_REG_KEYRSS0_0, 40, 0xffffffff },
+ { BNX2X_CHIP_MASK_ALL,
+ SRC_REG_KEYRSS0_7, 40, 0xffffffff },
+ { BNX2X_CHIP_MASK_ALL,
+ XCM_REG_WU_DA_SET_TMR_CNT_FLG_CMD00, 4, 0x00000001 },
+ { BNX2X_CHIP_MASK_ALL,
+ XCM_REG_WU_DA_CNT_CMD00, 4, 0x00000003 },
+ { BNX2X_CHIP_MASK_ALL,
+ XCM_REG_GLB_DEL_ACK_MAX_CNT_0, 4, 0x000000ff },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LLH0_T_BIT, 4, 0x00000001 },
+/* 20 */ { BNX2X_CHIP_MASK_E1X | BNX2X_CHIP_MASK_E2,
+ NIG_REG_EMAC0_IN_EN, 4, 0x00000001 },
+ { BNX2X_CHIP_MASK_E1X | BNX2X_CHIP_MASK_E2,
+ NIG_REG_BMAC0_IN_EN, 4, 0x00000001 },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_XCM0_OUT_EN, 4, 0x00000001 },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_BRB0_OUT_EN, 4, 0x00000001 },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LLH0_XCM_MASK, 4, 0x00000007 },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LLH0_ACPI_PAT_6_LEN, 68, 0x000000ff },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LLH0_ACPI_PAT_0_CRC, 68, 0xffffffff },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LLH0_DEST_MAC_0_0, 160, 0xffffffff },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LLH0_DEST_IP_0_1, 160, 0xffffffff },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LLH0_IPV4_IPV6_0, 160, 0x00000001 },
+/* 30 */ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LLH0_DEST_UDP_0, 160, 0x0000ffff },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LLH0_DEST_TCP_0, 160, 0x0000ffff },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LLH0_VLAN_ID_0, 160, 0x00000fff },
+ { BNX2X_CHIP_MASK_E1X | BNX2X_CHIP_MASK_E2,
+ NIG_REG_XGXS_SERDES0_MODE_SEL, 4, 0x00000001 },
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0, 4, 0x00000001},
+ { BNX2X_CHIP_MASK_ALL,
+ NIG_REG_STATUS_INTERRUPT_PORT0, 4, 0x07ffffff },
+ { BNX2X_CHIP_MASK_E1X | BNX2X_CHIP_MASK_E2,
+ NIG_REG_XGXS0_CTRL_EXTREMOTEMDIOST, 24, 0x00000001 },
+ { BNX2X_CHIP_MASK_E1X | BNX2X_CHIP_MASK_E2,
+ NIG_REG_SERDES0_CTRL_PHY_ADDR, 16, 0x0000001f },
+
+ { BNX2X_CHIP_MASK_ALL, 0xffffffff, 0, 0x00000000 }
};
if (!netif_running(bp->dev))
return rc;
+ if (CHIP_IS_E1(bp))
+ hw = BNX2X_CHIP_MASK_E1;
+ else if (CHIP_IS_E1H(bp))
+ hw = BNX2X_CHIP_MASK_E1H;
+ else if (CHIP_IS_E2(bp))
+ hw = BNX2X_CHIP_MASK_E2;
+ else if (CHIP_IS_E3B0(bp))
+ hw = BNX2X_CHIP_MASK_E3B0;
+ else /* e3 A0 */
+ hw = BNX2X_CHIP_MASK_E3;
+
/* Repeat the test twice:
First by writing 0x00000000, second by writing 0xffffffff */
for (idx = 0; idx < 2; idx++) {
@@ -1382,8 +1592,7 @@ static int bnx2x_test_registers(struct bnx2x *bp)
for (i = 0; reg_tbl[i].offset0 != 0xffffffff; i++) {
u32 offset, mask, save_val, val;
- if (CHIP_IS_E2(bp) &&
- reg_tbl[i].offset0 == HC_REG_AGG_INT_0)
+ if (!(hw & reg_tbl[i].hw))
continue;
offset = reg_tbl[i].offset0 + port*reg_tbl[i].offset1;
@@ -1400,7 +1609,7 @@ static int bnx2x_test_registers(struct bnx2x *bp)
/* verify value is as expected */
if ((val & mask) != (wr_val & mask)) {
- DP(NETIF_MSG_PROBE,
+ DP(NETIF_MSG_HW,
"offset 0x%x: val 0x%x != 0x%x mask 0x%x\n",
offset, val, wr_val, mask);
goto test_reg_exit;
@@ -1417,7 +1626,7 @@ test_reg_exit:
static int bnx2x_test_memory(struct bnx2x *bp)
{
int i, j, rc = -ENODEV;
- u32 val;
+ u32 val, index;
static const struct {
u32 offset;
int size;
@@ -1432,32 +1641,44 @@ static int bnx2x_test_memory(struct bnx2x *bp)
{ 0xffffffff, 0 }
};
+
static const struct {
char *name;
u32 offset;
- u32 e1_mask;
- u32 e1h_mask;
- u32 e2_mask;
+ u32 hw_mask[BNX2X_CHIP_MAX_OFST];
} prty_tbl[] = {
- { "CCM_PRTY_STS", CCM_REG_CCM_PRTY_STS, 0x3ffc0, 0, 0 },
- { "CFC_PRTY_STS", CFC_REG_CFC_PRTY_STS, 0x2, 0x2, 0 },
- { "DMAE_PRTY_STS", DMAE_REG_DMAE_PRTY_STS, 0, 0, 0 },
- { "TCM_PRTY_STS", TCM_REG_TCM_PRTY_STS, 0x3ffc0, 0, 0 },
- { "UCM_PRTY_STS", UCM_REG_UCM_PRTY_STS, 0x3ffc0, 0, 0 },
- { "XCM_PRTY_STS", XCM_REG_XCM_PRTY_STS, 0x3ffc1, 0, 0 },
-
- { NULL, 0xffffffff, 0, 0, 0 }
+ { "CCM_PRTY_STS", CCM_REG_CCM_PRTY_STS,
+ {0x3ffc0, 0, 0, 0} },
+ { "CFC_PRTY_STS", CFC_REG_CFC_PRTY_STS,
+ {0x2, 0x2, 0, 0} },
+ { "DMAE_PRTY_STS", DMAE_REG_DMAE_PRTY_STS,
+ {0, 0, 0, 0} },
+ { "TCM_PRTY_STS", TCM_REG_TCM_PRTY_STS,
+ {0x3ffc0, 0, 0, 0} },
+ { "UCM_PRTY_STS", UCM_REG_UCM_PRTY_STS,
+ {0x3ffc0, 0, 0, 0} },
+ { "XCM_PRTY_STS", XCM_REG_XCM_PRTY_STS,
+ {0x3ffc1, 0, 0, 0} },
+
+ { NULL, 0xffffffff, {0, 0, 0, 0} }
};
if (!netif_running(bp->dev))
return rc;
+ if (CHIP_IS_E1(bp))
+ index = BNX2X_CHIP_E1_OFST;
+ else if (CHIP_IS_E1H(bp))
+ index = BNX2X_CHIP_E1H_OFST;
+ else if (CHIP_IS_E2(bp))
+ index = BNX2X_CHIP_E2_OFST;
+ else /* e3 */
+ index = BNX2X_CHIP_E3_OFST;
+
/* pre-Check the parity status */
for (i = 0; prty_tbl[i].offset != 0xffffffff; i++) {
val = REG_RD(bp, prty_tbl[i].offset);
- if ((CHIP_IS_E1(bp) && (val & ~(prty_tbl[i].e1_mask))) ||
- (CHIP_IS_E1H(bp) && (val & ~(prty_tbl[i].e1h_mask))) ||
- (CHIP_IS_E2(bp) && (val & ~(prty_tbl[i].e2_mask)))) {
+ if (val & ~(prty_tbl[i].hw_mask[index])) {
DP(NETIF_MSG_HW,
"%s is 0x%x\n", prty_tbl[i].name, val);
goto test_mem_exit;
@@ -1472,9 +1693,7 @@ static int bnx2x_test_memory(struct bnx2x *bp)
/* Check the parity status */
for (i = 0; prty_tbl[i].offset != 0xffffffff; i++) {
val = REG_RD(bp, prty_tbl[i].offset);
- if ((CHIP_IS_E1(bp) && (val & ~(prty_tbl[i].e1_mask))) ||
- (CHIP_IS_E1H(bp) && (val & ~(prty_tbl[i].e1h_mask))) ||
- (CHIP_IS_E2(bp) && (val & ~(prty_tbl[i].e2_mask)))) {
+ if (val & ~(prty_tbl[i].hw_mask[index])) {
DP(NETIF_MSG_HW,
"%s is 0x%x\n", prty_tbl[i].name, val);
goto test_mem_exit;
@@ -1491,28 +1710,33 @@ static void bnx2x_wait_for_link(struct bnx2x *bp, u8 link_up, u8 is_serdes)
{
int cnt = 1400;
- if (link_up)
+ if (link_up) {
while (bnx2x_link_test(bp, is_serdes) && cnt--)
- msleep(10);
+ msleep(20);
+
+ if (cnt <= 0 && bnx2x_link_test(bp, is_serdes))
+ DP(NETIF_MSG_LINK, "Timeout waiting for link up\n");
+ }
}
-static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
+static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode)
{
unsigned int pkt_size, num_pkts, i;
struct sk_buff *skb;
unsigned char *packet;
struct bnx2x_fastpath *fp_rx = &bp->fp[0];
struct bnx2x_fastpath *fp_tx = &bp->fp[0];
+ struct bnx2x_fp_txdata *txdata = &fp_tx->txdata[0];
u16 tx_start_idx, tx_idx;
u16 rx_start_idx, rx_idx;
- u16 pkt_prod, bd_prod;
+ u16 pkt_prod, bd_prod, rx_comp_cons;
struct sw_tx_bd *tx_buf;
struct eth_tx_start_bd *tx_start_bd;
struct eth_tx_parse_bd_e1x *pbd_e1x = NULL;
struct eth_tx_parse_bd_e2 *pbd_e2 = NULL;
dma_addr_t mapping;
union eth_rx_cqe *cqe;
- u8 cqe_fp_flags;
+ u8 cqe_fp_flags, cqe_fp_type;
struct sw_rx_bd *rx_buf;
u16 len;
int rc = -ENODEV;
@@ -1524,7 +1748,8 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
return -EINVAL;
break;
case BNX2X_MAC_LOOPBACK:
- bp->link_params.loopback_mode = LOOPBACK_BMAC;
+ bp->link_params.loopback_mode = CHIP_IS_E3(bp) ?
+ LOOPBACK_XMAC : LOOPBACK_BMAC;
bnx2x_phy_init(&bp->link_params, &bp->link_vars);
break;
default:
@@ -1545,22 +1770,28 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
memset(packet + 2*ETH_ALEN, 0x77, (ETH_HLEN - 2*ETH_ALEN));
for (i = ETH_HLEN; i < pkt_size; i++)
packet[i] = (unsigned char) (i & 0xff);
+ mapping = dma_map_single(&bp->pdev->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) {
+ rc = -ENOMEM;
+ dev_kfree_skb(skb);
+ BNX2X_ERR("Unable to map SKB\n");
+ goto test_loopback_exit;
+ }
/* send the loopback packet */
num_pkts = 0;
- tx_start_idx = le16_to_cpu(*fp_tx->tx_cons_sb);
+ tx_start_idx = le16_to_cpu(*txdata->tx_cons_sb);
rx_start_idx = le16_to_cpu(*fp_rx->rx_cons_sb);
- pkt_prod = fp_tx->tx_pkt_prod++;
- tx_buf = &fp_tx->tx_buf_ring[TX_BD(pkt_prod)];
- tx_buf->first_bd = fp_tx->tx_bd_prod;
+ pkt_prod = txdata->tx_pkt_prod++;
+ tx_buf = &txdata->tx_buf_ring[TX_BD(pkt_prod)];
+ tx_buf->first_bd = txdata->tx_bd_prod;
tx_buf->skb = skb;
tx_buf->flags = 0;
- bd_prod = TX_BD(fp_tx->tx_bd_prod);
- tx_start_bd = &fp_tx->tx_desc_ring[bd_prod].start_bd;
- mapping = dma_map_single(&bp->pdev->dev, skb->data,
- skb_headlen(skb), DMA_TO_DEVICE);
+ bd_prod = TX_BD(txdata->tx_bd_prod);
+ tx_start_bd = &txdata->tx_desc_ring[bd_prod].start_bd;
tx_start_bd->addr_hi = cpu_to_le32(U64_HI(mapping));
tx_start_bd->addr_lo = cpu_to_le32(U64_LO(mapping));
tx_start_bd->nbd = cpu_to_le16(2); /* start + pbd */
@@ -1577,26 +1808,27 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
/* turn on parsing and get a BD */
bd_prod = TX_BD(NEXT_TX_IDX(bd_prod));
- pbd_e1x = &fp_tx->tx_desc_ring[bd_prod].parse_bd_e1x;
- pbd_e2 = &fp_tx->tx_desc_ring[bd_prod].parse_bd_e2;
+ pbd_e1x = &txdata->tx_desc_ring[bd_prod].parse_bd_e1x;
+ pbd_e2 = &txdata->tx_desc_ring[bd_prod].parse_bd_e2;
memset(pbd_e2, 0, sizeof(struct eth_tx_parse_bd_e2));
memset(pbd_e1x, 0, sizeof(struct eth_tx_parse_bd_e1x));
wmb();
- fp_tx->tx_db.data.prod += 2;
+ txdata->tx_db.data.prod += 2;
barrier();
- DOORBELL(bp, fp_tx->index, fp_tx->tx_db.raw);
+ DOORBELL(bp, txdata->cid, txdata->tx_db.raw);
mmiowb();
+ barrier();
num_pkts++;
- fp_tx->tx_bd_prod += 2; /* start + pbd */
+ txdata->tx_bd_prod += 2; /* start + pbd */
udelay(100);
- tx_idx = le16_to_cpu(*fp_tx->tx_cons_sb);
+ tx_idx = le16_to_cpu(*txdata->tx_cons_sb);
if (tx_idx != tx_start_idx + num_pkts)
goto test_loopback_exit;
@@ -1610,7 +1842,7 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
* bnx2x_tx_int()), as both are taking netif_tx_lock().
*/
local_bh_disable();
- bnx2x_tx_int(fp_tx);
+ bnx2x_tx_int(bp, txdata);
local_bh_enable();
}
@@ -1618,9 +1850,11 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
if (rx_idx != rx_start_idx + num_pkts)
goto test_loopback_exit;
- cqe = &fp_rx->rx_comp_ring[RCQ_BD(fp_rx->rx_comp_cons)];
+ rx_comp_cons = le16_to_cpu(fp_rx->rx_comp_cons);
+ cqe = &fp_rx->rx_comp_ring[RCQ_BD(rx_comp_cons)];
cqe_fp_flags = cqe->fast_path_cqe.type_error_flags;
- if (CQE_TYPE(cqe_fp_flags) || (cqe_fp_flags & ETH_RX_ERROR_FALGS))
+ cqe_fp_type = cqe_fp_flags & ETH_FAST_PATH_RX_CQE_TYPE;
+ if (!CQE_TYPE_FAST(cqe_fp_type) || (cqe_fp_flags & ETH_RX_ERROR_FALGS))
goto test_loopback_rx_exit;
len = le16_to_cpu(cqe->fast_path_cqe.pkt_len);
@@ -1628,6 +1862,9 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
goto test_loopback_rx_exit;
rx_buf = &fp_rx->rx_buf_ring[RX_BD(fp_rx->rx_bd_cons)];
+ dma_sync_single_for_cpu(&bp->pdev->dev,
+ dma_unmap_addr(rx_buf, mapping),
+ fp_rx->rx_buf_size, DMA_FROM_DEVICE);
skb = rx_buf->skb;
skb_reserve(skb, cqe->fast_path_cqe.placement_offset);
for (i = ETH_HLEN; i < pkt_size; i++)
@@ -1653,7 +1890,7 @@ test_loopback_exit:
return rc;
}
-static int bnx2x_test_loopback(struct bnx2x *bp, u8 link_up)
+static int bnx2x_test_loopback(struct bnx2x *bp)
{
int rc = 0, res;
@@ -1666,13 +1903,13 @@ static int bnx2x_test_loopback(struct bnx2x *bp, u8 link_up)
bnx2x_netif_stop(bp, 1);
bnx2x_acquire_phy_lock(bp);
- res = bnx2x_run_loopback(bp, BNX2X_PHY_LOOPBACK, link_up);
+ res = bnx2x_run_loopback(bp, BNX2X_PHY_LOOPBACK);
if (res) {
DP(NETIF_MSG_PROBE, " PHY loopback failed (res %d)\n", res);
rc |= BNX2X_PHY_LOOPBACK_FAILED;
}
- res = bnx2x_run_loopback(bp, BNX2X_MAC_LOOPBACK, link_up);
+ res = bnx2x_run_loopback(bp, BNX2X_MAC_LOOPBACK);
if (res) {
DP(NETIF_MSG_PROBE, " MAC loopback failed (res %d)\n", res);
rc |= BNX2X_MAC_LOOPBACK_FAILED;
@@ -1744,39 +1981,20 @@ test_nvram_exit:
return rc;
}
+/* Send an EMPTY ramrod on the first queue */
static int bnx2x_test_intr(struct bnx2x *bp)
{
- struct mac_configuration_cmd *config = bnx2x_sp(bp, mac_config);
- int i, rc;
+ struct bnx2x_queue_state_params params = {0};
if (!netif_running(bp->dev))
return -ENODEV;
- config->hdr.length = 0;
- if (CHIP_IS_E1(bp))
- config->hdr.offset = (BP_PORT(bp) ? 32 : 0);
- else
- config->hdr.offset = BP_FUNC(bp);
- config->hdr.client_id = bp->fp->cl_id;
- config->hdr.reserved1 = 0;
-
- bp->set_mac_pending = 1;
- smp_wmb();
- rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_SET_MAC, 0,
- U64_HI(bnx2x_sp_mapping(bp, mac_config)),
- U64_LO(bnx2x_sp_mapping(bp, mac_config)), 1);
- if (rc == 0) {
- for (i = 0; i < 10; i++) {
- if (!bp->set_mac_pending)
- break;
- smp_rmb();
- msleep_interruptible(10);
- }
- if (i == 10)
- rc = -ENODEV;
- }
+ params.q_obj = &bp->fp->q_obj;
+ params.cmd = BNX2X_Q_CMD_EMPTY;
- return rc;
+ __set_bit(RAMROD_COMP_WAIT, &params.ramrod_flags);
+
+ return bnx2x_queue_state_change(bp, &params);
}
static void bnx2x_self_test(struct net_device *dev,
@@ -1785,7 +2003,7 @@ static void bnx2x_self_test(struct net_device *dev,
struct bnx2x *bp = netdev_priv(dev);
u8 is_serdes;
if (bp->recovery_state != BNX2X_RECOVERY_DONE) {
- printk(KERN_ERR "Handling parity error recovery. Try again later\n");
+ pr_err("Handling parity error recovery. Try again later\n");
etest->flags |= ETH_TEST_FL_FAILED;
return;
}
@@ -1815,7 +2033,7 @@ static void bnx2x_self_test(struct net_device *dev,
bnx2x_nic_unload(bp, UNLOAD_NORMAL);
bnx2x_nic_load(bp, LOAD_DIAG);
/* wait until link state is restored */
- bnx2x_wait_for_link(bp, link_up, is_serdes);
+ bnx2x_wait_for_link(bp, 1, is_serdes);
if (bnx2x_test_registers(bp) != 0) {
buf[0] = 1;
@@ -1826,7 +2044,7 @@ static void bnx2x_self_test(struct net_device *dev,
etest->flags |= ETH_TEST_FL_FAILED;
}
- buf[2] = bnx2x_test_loopback(bp, link_up);
+ buf[2] = bnx2x_test_loopback(bp);
if (buf[2] != 0)
etest->flags |= ETH_TEST_FL_FAILED;
@@ -1864,6 +2082,14 @@ static void bnx2x_self_test(struct net_device *dev,
#define IS_MF_MODE_STAT(bp) \
(IS_MF(bp) && !(bp->msg_enable & BNX2X_MSG_STATS))
+/* ethtool statistics are displayed for all regular ethernet queues and the
+ * fcoe L2 queue if not disabled
+ */
+static inline int bnx2x_num_stat_queues(struct bnx2x *bp)
+{
+ return BNX2X_NUM_ETH_QUEUES(bp);
+}
+
static int bnx2x_get_sset_count(struct net_device *dev, int stringset)
{
struct bnx2x *bp = netdev_priv(dev);
@@ -1872,7 +2098,7 @@ static int bnx2x_get_sset_count(struct net_device *dev, int stringset)
switch (stringset) {
case ETH_SS_STATS:
if (is_multi(bp)) {
- num_stats = BNX2X_NUM_STAT_QUEUES(bp) *
+ num_stats = bnx2x_num_stat_queues(bp) *
BNX2X_NUM_Q_STATS;
if (!IS_MF_MODE_STAT(bp))
num_stats += BNX2X_NUM_STATS;
@@ -1905,14 +2131,9 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
case ETH_SS_STATS:
if (is_multi(bp)) {
k = 0;
- for_each_napi_queue(bp, i) {
+ for_each_eth_queue(bp, i) {
memset(queue_name, 0, sizeof(queue_name));
-
- if (IS_FCOE_IDX(i))
- sprintf(queue_name, "fcoe");
- else
- sprintf(queue_name, "%d", i);
-
+ sprintf(queue_name, "%d", i);
for (j = 0; j < BNX2X_NUM_Q_STATS; j++)
snprintf(buf + (k + j)*ETH_GSTRING_LEN,
ETH_GSTRING_LEN,
@@ -1951,7 +2172,7 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev,
if (is_multi(bp)) {
k = 0;
- for_each_napi_queue(bp, i) {
+ for_each_eth_queue(bp, i) {
hw_stats = (u32 *)&bp->fp[i].eth_q_stats;
for (j = 0; j < BNX2X_NUM_Q_STATS; j++) {
if (bnx2x_q_stats_arr[j].size == 0) {
@@ -2050,7 +2271,7 @@ static int bnx2x_set_phys_id(struct net_device *dev,
}
static int bnx2x_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
- void *rules __always_unused)
+ u32 *rules __always_unused)
{
struct bnx2x *bp = netdev_priv(dev);
@@ -2069,14 +2290,30 @@ static int bnx2x_get_rxfh_indir(struct net_device *dev,
{
struct bnx2x *bp = netdev_priv(dev);
size_t copy_size =
- min_t(size_t, indir->size, TSTORM_INDIRECTION_TABLE_SIZE);
+ min_t(size_t, indir->size, T_ETH_INDIRECTION_TABLE_SIZE);
+ u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
+ size_t i;
if (bp->multi_mode == ETH_RSS_MODE_DISABLED)
return -EOPNOTSUPP;
- indir->size = TSTORM_INDIRECTION_TABLE_SIZE;
- memcpy(indir->ring_index, bp->rx_indir_table,
- copy_size * sizeof(bp->rx_indir_table[0]));
+ /* Get the current configuration of the RSS indirection table */
+ bnx2x_get_rss_ind_table(&bp->rss_conf_obj, ind_table);
+
+ /*
+ * We can't use a memcpy() as an internal storage of an
+ * indirection table is a u8 array while indir->ring_index
+ * points to an array of u32.
+ *
+ * Indirection table contains the FW Client IDs, so we need to
+ * align the returned table to the Client ID of the leading RSS
+ * queue.
+ */
+ for (i = 0; i < copy_size; i++)
+ indir->ring_index[i] = ind_table[i] - bp->fp->cl_id;
+
+ indir->size = T_ETH_INDIRECTION_TABLE_SIZE;
+
return 0;
}
@@ -2085,21 +2322,33 @@ static int bnx2x_set_rxfh_indir(struct net_device *dev,
{
struct bnx2x *bp = netdev_priv(dev);
size_t i;
+ u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
+ u32 num_eth_queues = BNX2X_NUM_ETH_QUEUES(bp);
if (bp->multi_mode == ETH_RSS_MODE_DISABLED)
return -EOPNOTSUPP;
- /* Validate size and indices */
- if (indir->size != TSTORM_INDIRECTION_TABLE_SIZE)
+ /* validate the size */
+ if (indir->size != T_ETH_INDIRECTION_TABLE_SIZE)
return -EINVAL;
- for (i = 0; i < TSTORM_INDIRECTION_TABLE_SIZE; i++)
- if (indir->ring_index[i] >= BNX2X_NUM_ETH_QUEUES(bp))
+
+ for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) {
+ /* validate the indices */
+ if (indir->ring_index[i] >= num_eth_queues)
return -EINVAL;
+ /*
+ * The same as in bnx2x_get_rxfh_indir: we can't use a memcpy()
+ * as an internal storage of an indirection table is a u8 array
+ * while indir->ring_index points to an array of u32.
+ *
+ * Indirection table contains the FW Client IDs, so we need to
+ * align the received table to the Client ID of the leading RSS
+ * queue
+ */
+ ind_table[i] = indir->ring_index[i] + bp->fp->cl_id;
+ }
- memcpy(bp->rx_indir_table, indir->ring_index,
- indir->size * sizeof(bp->rx_indir_table[0]));
- bnx2x_push_indir_table(bp);
- return 0;
+ return bnx2x_config_rss_pf(bp, ind_table, false);
}
static const struct ethtool_ops bnx2x_ethtool_ops = {
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h
new file mode 100644
index 000000000000..998652a1b858
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h
@@ -0,0 +1,410 @@
+/* bnx2x_fw_defs.h: Broadcom Everest network driver.
+ *
+ * Copyright (c) 2007-2011 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef BNX2X_FW_DEFS_H
+#define BNX2X_FW_DEFS_H
+
+#define CSTORM_ASSERT_LIST_INDEX_OFFSET (IRO[148].base)
+#define CSTORM_ASSERT_LIST_OFFSET(assertListEntry) \
+ (IRO[147].base + ((assertListEntry) * IRO[147].m1))
+#define CSTORM_EVENT_RING_DATA_OFFSET(pfId) \
+ (IRO[153].base + (((pfId)>>1) * IRO[153].m1) + (((pfId)&1) * \
+ IRO[153].m2))
+#define CSTORM_EVENT_RING_PROD_OFFSET(pfId) \
+ (IRO[154].base + (((pfId)>>1) * IRO[154].m1) + (((pfId)&1) * \
+ IRO[154].m2))
+#define CSTORM_FINAL_CLEANUP_COMPLETE_OFFSET(funcId) \
+ (IRO[159].base + ((funcId) * IRO[159].m1))
+#define CSTORM_FUNC_EN_OFFSET(funcId) \
+ (IRO[149].base + ((funcId) * IRO[149].m1))
+#define CSTORM_IGU_MODE_OFFSET (IRO[157].base)
+#define CSTORM_ISCSI_CQ_SIZE_OFFSET(pfId) \
+ (IRO[315].base + ((pfId) * IRO[315].m1))
+#define CSTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \
+ (IRO[316].base + ((pfId) * IRO[316].m1))
+#define CSTORM_ISCSI_EQ_CONS_OFFSET(pfId, iscsiEqId) \
+ (IRO[308].base + ((pfId) * IRO[308].m1) + ((iscsiEqId) * IRO[308].m2))
+#define CSTORM_ISCSI_EQ_NEXT_EQE_ADDR_OFFSET(pfId, iscsiEqId) \
+ (IRO[310].base + ((pfId) * IRO[310].m1) + ((iscsiEqId) * IRO[310].m2))
+#define CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_OFFSET(pfId, iscsiEqId) \
+ (IRO[309].base + ((pfId) * IRO[309].m1) + ((iscsiEqId) * IRO[309].m2))
+#define CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_VALID_OFFSET(pfId, iscsiEqId) \
+ (IRO[311].base + ((pfId) * IRO[311].m1) + ((iscsiEqId) * IRO[311].m2))
+#define CSTORM_ISCSI_EQ_PROD_OFFSET(pfId, iscsiEqId) \
+ (IRO[307].base + ((pfId) * IRO[307].m1) + ((iscsiEqId) * IRO[307].m2))
+#define CSTORM_ISCSI_EQ_SB_INDEX_OFFSET(pfId, iscsiEqId) \
+ (IRO[313].base + ((pfId) * IRO[313].m1) + ((iscsiEqId) * IRO[313].m2))
+#define CSTORM_ISCSI_EQ_SB_NUM_OFFSET(pfId, iscsiEqId) \
+ (IRO[312].base + ((pfId) * IRO[312].m1) + ((iscsiEqId) * IRO[312].m2))
+#define CSTORM_ISCSI_HQ_SIZE_OFFSET(pfId) \
+ (IRO[314].base + ((pfId) * IRO[314].m1))
+#define CSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
+ (IRO[306].base + ((pfId) * IRO[306].m1))
+#define CSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
+ (IRO[305].base + ((pfId) * IRO[305].m1))
+#define CSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
+ (IRO[304].base + ((pfId) * IRO[304].m1))
+#define CSTORM_RECORD_SLOW_PATH_OFFSET(funcId) \
+ (IRO[151].base + ((funcId) * IRO[151].m1))
+#define CSTORM_SP_STATUS_BLOCK_DATA_OFFSET(pfId) \
+ (IRO[142].base + ((pfId) * IRO[142].m1))
+#define CSTORM_SP_STATUS_BLOCK_DATA_STATE_OFFSET(pfId) \
+ (IRO[143].base + ((pfId) * IRO[143].m1))
+#define CSTORM_SP_STATUS_BLOCK_OFFSET(pfId) \
+ (IRO[141].base + ((pfId) * IRO[141].m1))
+#define CSTORM_SP_STATUS_BLOCK_SIZE (IRO[141].size)
+#define CSTORM_SP_SYNC_BLOCK_OFFSET(pfId) \
+ (IRO[144].base + ((pfId) * IRO[144].m1))
+#define CSTORM_SP_SYNC_BLOCK_SIZE (IRO[144].size)
+#define CSTORM_STATUS_BLOCK_DATA_FLAGS_OFFSET(sbId, hcIndex) \
+ (IRO[136].base + ((sbId) * IRO[136].m1) + ((hcIndex) * IRO[136].m2))
+#define CSTORM_STATUS_BLOCK_DATA_OFFSET(sbId) \
+ (IRO[133].base + ((sbId) * IRO[133].m1))
+#define CSTORM_STATUS_BLOCK_DATA_STATE_OFFSET(sbId) \
+ (IRO[134].base + ((sbId) * IRO[134].m1))
+#define CSTORM_STATUS_BLOCK_DATA_TIMEOUT_OFFSET(sbId, hcIndex) \
+ (IRO[135].base + ((sbId) * IRO[135].m1) + ((hcIndex) * IRO[135].m2))
+#define CSTORM_STATUS_BLOCK_OFFSET(sbId) \
+ (IRO[132].base + ((sbId) * IRO[132].m1))
+#define CSTORM_STATUS_BLOCK_SIZE (IRO[132].size)
+#define CSTORM_SYNC_BLOCK_OFFSET(sbId) \
+ (IRO[137].base + ((sbId) * IRO[137].m1))
+#define CSTORM_SYNC_BLOCK_SIZE (IRO[137].size)
+#define CSTORM_VF_PF_CHANNEL_STATE_OFFSET(vfId) \
+ (IRO[155].base + ((vfId) * IRO[155].m1))
+#define CSTORM_VF_PF_CHANNEL_VALID_OFFSET(vfId) \
+ (IRO[156].base + ((vfId) * IRO[156].m1))
+#define CSTORM_VF_TO_PF_OFFSET(funcId) \
+ (IRO[150].base + ((funcId) * IRO[150].m1))
+#define TSTORM_ACCEPT_CLASSIFY_FAILED_OFFSET (IRO[204].base)
+#define TSTORM_APPROXIMATE_MATCH_MULTICAST_FILTERING_OFFSET(pfId) \
+ (IRO[203].base + ((pfId) * IRO[203].m1))
+#define TSTORM_ASSERT_LIST_INDEX_OFFSET (IRO[102].base)
+#define TSTORM_ASSERT_LIST_OFFSET(assertListEntry) \
+ (IRO[101].base + ((assertListEntry) * IRO[101].m1))
+#define TSTORM_COMMON_SAFC_WORKAROUND_ENABLE_OFFSET (IRO[107].base)
+#define TSTORM_COMMON_SAFC_WORKAROUND_TIMEOUT_10USEC_OFFSET \
+ (IRO[108].base)
+#define TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(pfId) \
+ (IRO[201].base + ((pfId) * IRO[201].m1))
+#define TSTORM_FUNC_EN_OFFSET(funcId) \
+ (IRO[103].base + ((funcId) * IRO[103].m1))
+#define TSTORM_ISCSI_ERROR_BITMAP_OFFSET(pfId) \
+ (IRO[271].base + ((pfId) * IRO[271].m1))
+#define TSTORM_ISCSI_L2_ISCSI_OOO_CID_TABLE_OFFSET(pfId) \
+ (IRO[272].base + ((pfId) * IRO[272].m1))
+#define TSTORM_ISCSI_L2_ISCSI_OOO_CLIENT_ID_TABLE_OFFSET(pfId) \
+ (IRO[273].base + ((pfId) * IRO[273].m1))
+#define TSTORM_ISCSI_L2_ISCSI_OOO_PROD_OFFSET(pfId) \
+ (IRO[274].base + ((pfId) * IRO[274].m1))
+#define TSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
+ (IRO[270].base + ((pfId) * IRO[270].m1))
+#define TSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
+ (IRO[269].base + ((pfId) * IRO[269].m1))
+#define TSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
+ (IRO[268].base + ((pfId) * IRO[268].m1))
+#define TSTORM_ISCSI_RQ_SIZE_OFFSET(pfId) \
+ (IRO[267].base + ((pfId) * IRO[267].m1))
+#define TSTORM_ISCSI_TCP_LOCAL_ADV_WND_OFFSET(pfId) \
+ (IRO[276].base + ((pfId) * IRO[276].m1))
+#define TSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(pfId) \
+ (IRO[263].base + ((pfId) * IRO[263].m1))
+#define TSTORM_ISCSI_TCP_VARS_LSB_LOCAL_MAC_ADDR_OFFSET(pfId) \
+ (IRO[264].base + ((pfId) * IRO[264].m1))
+#define TSTORM_ISCSI_TCP_VARS_MID_LOCAL_MAC_ADDR_OFFSET(pfId) \
+ (IRO[265].base + ((pfId) * IRO[265].m1))
+#define TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(pfId) \
+ (IRO[266].base + ((pfId) * IRO[266].m1))
+#define TSTORM_MAC_FILTER_CONFIG_OFFSET(pfId) \
+ (IRO[202].base + ((pfId) * IRO[202].m1))
+#define TSTORM_RECORD_SLOW_PATH_OFFSET(funcId) \
+ (IRO[105].base + ((funcId) * IRO[105].m1))
+#define TSTORM_TCP_MAX_CWND_OFFSET(pfId) \
+ (IRO[216].base + ((pfId) * IRO[216].m1))
+#define TSTORM_VF_TO_PF_OFFSET(funcId) \
+ (IRO[104].base + ((funcId) * IRO[104].m1))
+#define USTORM_AGG_DATA_OFFSET (IRO[206].base)
+#define USTORM_AGG_DATA_SIZE (IRO[206].size)
+#define USTORM_ASSERT_LIST_INDEX_OFFSET (IRO[177].base)
+#define USTORM_ASSERT_LIST_OFFSET(assertListEntry) \
+ (IRO[176].base + ((assertListEntry) * IRO[176].m1))
+#define USTORM_CQE_PAGE_NEXT_OFFSET(portId, clientId) \
+ (IRO[205].base + ((portId) * IRO[205].m1) + ((clientId) * \
+ IRO[205].m2))
+#define USTORM_ETH_PAUSE_ENABLED_OFFSET(portId) \
+ (IRO[183].base + ((portId) * IRO[183].m1))
+#define USTORM_FCOE_EQ_PROD_OFFSET(pfId) \
+ (IRO[317].base + ((pfId) * IRO[317].m1))
+#define USTORM_FUNC_EN_OFFSET(funcId) \
+ (IRO[178].base + ((funcId) * IRO[178].m1))
+#define USTORM_ISCSI_CQ_SIZE_OFFSET(pfId) \
+ (IRO[281].base + ((pfId) * IRO[281].m1))
+#define USTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \
+ (IRO[282].base + ((pfId) * IRO[282].m1))
+#define USTORM_ISCSI_ERROR_BITMAP_OFFSET(pfId) \
+ (IRO[286].base + ((pfId) * IRO[286].m1))
+#define USTORM_ISCSI_GLOBAL_BUF_PHYS_ADDR_OFFSET(pfId) \
+ (IRO[283].base + ((pfId) * IRO[283].m1))
+#define USTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
+ (IRO[279].base + ((pfId) * IRO[279].m1))
+#define USTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
+ (IRO[278].base + ((pfId) * IRO[278].m1))
+#define USTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
+ (IRO[277].base + ((pfId) * IRO[277].m1))
+#define USTORM_ISCSI_R2TQ_SIZE_OFFSET(pfId) \
+ (IRO[280].base + ((pfId) * IRO[280].m1))
+#define USTORM_ISCSI_RQ_BUFFER_SIZE_OFFSET(pfId) \
+ (IRO[284].base + ((pfId) * IRO[284].m1))
+#define USTORM_ISCSI_RQ_SIZE_OFFSET(pfId) \
+ (IRO[285].base + ((pfId) * IRO[285].m1))
+#define USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(pfId) \
+ (IRO[182].base + ((pfId) * IRO[182].m1))
+#define USTORM_RECORD_SLOW_PATH_OFFSET(funcId) \
+ (IRO[180].base + ((funcId) * IRO[180].m1))
+#define USTORM_RX_PRODS_E1X_OFFSET(portId, clientId) \
+ (IRO[209].base + ((portId) * IRO[209].m1) + ((clientId) * \
+ IRO[209].m2))
+#define USTORM_RX_PRODS_E2_OFFSET(qzoneId) \
+ (IRO[210].base + ((qzoneId) * IRO[210].m1))
+#define USTORM_TPA_BTR_OFFSET (IRO[207].base)
+#define USTORM_TPA_BTR_SIZE (IRO[207].size)
+#define USTORM_VF_TO_PF_OFFSET(funcId) \
+ (IRO[179].base + ((funcId) * IRO[179].m1))
+#define XSTORM_AGG_INT_FINAL_CLEANUP_COMP_TYPE (IRO[67].base)
+#define XSTORM_AGG_INT_FINAL_CLEANUP_INDEX (IRO[66].base)
+#define XSTORM_ASSERT_LIST_INDEX_OFFSET (IRO[51].base)
+#define XSTORM_ASSERT_LIST_OFFSET(assertListEntry) \
+ (IRO[50].base + ((assertListEntry) * IRO[50].m1))
+#define XSTORM_CMNG_PER_PORT_VARS_OFFSET(portId) \
+ (IRO[43].base + ((portId) * IRO[43].m1))
+#define XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(pfId) \
+ (IRO[45].base + ((pfId) * IRO[45].m1))
+#define XSTORM_FUNC_EN_OFFSET(funcId) \
+ (IRO[47].base + ((funcId) * IRO[47].m1))
+#define XSTORM_ISCSI_HQ_SIZE_OFFSET(pfId) \
+ (IRO[294].base + ((pfId) * IRO[294].m1))
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR0_OFFSET(pfId) \
+ (IRO[297].base + ((pfId) * IRO[297].m1))
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR1_OFFSET(pfId) \
+ (IRO[298].base + ((pfId) * IRO[298].m1))
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR2_OFFSET(pfId) \
+ (IRO[299].base + ((pfId) * IRO[299].m1))
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR3_OFFSET(pfId) \
+ (IRO[300].base + ((pfId) * IRO[300].m1))
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR4_OFFSET(pfId) \
+ (IRO[301].base + ((pfId) * IRO[301].m1))
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR5_OFFSET(pfId) \
+ (IRO[302].base + ((pfId) * IRO[302].m1))
+#define XSTORM_ISCSI_LOCAL_VLAN_OFFSET(pfId) \
+ (IRO[303].base + ((pfId) * IRO[303].m1))
+#define XSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
+ (IRO[293].base + ((pfId) * IRO[293].m1))
+#define XSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
+ (IRO[292].base + ((pfId) * IRO[292].m1))
+#define XSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
+ (IRO[291].base + ((pfId) * IRO[291].m1))
+#define XSTORM_ISCSI_R2TQ_SIZE_OFFSET(pfId) \
+ (IRO[296].base + ((pfId) * IRO[296].m1))
+#define XSTORM_ISCSI_SQ_SIZE_OFFSET(pfId) \
+ (IRO[295].base + ((pfId) * IRO[295].m1))
+#define XSTORM_ISCSI_TCP_VARS_ADV_WND_SCL_OFFSET(pfId) \
+ (IRO[290].base + ((pfId) * IRO[290].m1))
+#define XSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(pfId) \
+ (IRO[289].base + ((pfId) * IRO[289].m1))
+#define XSTORM_ISCSI_TCP_VARS_TOS_OFFSET(pfId) \
+ (IRO[288].base + ((pfId) * IRO[288].m1))
+#define XSTORM_ISCSI_TCP_VARS_TTL_OFFSET(pfId) \
+ (IRO[287].base + ((pfId) * IRO[287].m1))
+#define XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(pfId) \
+ (IRO[44].base + ((pfId) * IRO[44].m1))
+#define XSTORM_RECORD_SLOW_PATH_OFFSET(funcId) \
+ (IRO[49].base + ((funcId) * IRO[49].m1))
+#define XSTORM_SPQ_DATA_OFFSET(funcId) \
+ (IRO[32].base + ((funcId) * IRO[32].m1))
+#define XSTORM_SPQ_DATA_SIZE (IRO[32].size)
+#define XSTORM_SPQ_PAGE_BASE_OFFSET(funcId) \
+ (IRO[30].base + ((funcId) * IRO[30].m1))
+#define XSTORM_SPQ_PROD_OFFSET(funcId) \
+ (IRO[31].base + ((funcId) * IRO[31].m1))
+#define XSTORM_TCP_GLOBAL_DEL_ACK_COUNTER_ENABLED_OFFSET(portId) \
+ (IRO[211].base + ((portId) * IRO[211].m1))
+#define XSTORM_TCP_GLOBAL_DEL_ACK_COUNTER_MAX_COUNT_OFFSET(portId) \
+ (IRO[212].base + ((portId) * IRO[212].m1))
+#define XSTORM_TCP_TX_SWS_TIMER_VAL_OFFSET(pfId) \
+ (IRO[214].base + (((pfId)>>1) * IRO[214].m1) + (((pfId)&1) * \
+ IRO[214].m2))
+#define XSTORM_VF_TO_PF_OFFSET(funcId) \
+ (IRO[48].base + ((funcId) * IRO[48].m1))
+#define COMMON_ASM_INVALID_ASSERT_OPCODE 0x0
+
+/**
+* This file defines HSI constants for the ETH flow
+*/
+#ifdef _EVEREST_MICROCODE
+#include "Microcode\Generated\DataTypes\eth_rx_bd.h"
+#include "Microcode\Generated\DataTypes\eth_tx_bd.h"
+#include "Microcode\Generated\DataTypes\eth_rx_cqe.h"
+#include "Microcode\Generated\DataTypes\eth_rx_sge.h"
+#include "Microcode\Generated\DataTypes\eth_rx_cqe_next_page.h"
+#endif
+
+
+/* Ethernet Ring parameters */
+#define X_ETH_LOCAL_RING_SIZE 13
+#define FIRST_BD_IN_PKT 0
+#define PARSE_BD_INDEX 1
+#define NUM_OF_ETH_BDS_IN_PAGE ((PAGE_SIZE)/(STRUCT_SIZE(eth_tx_bd)/8))
+#define U_ETH_NUM_OF_SGES_TO_FETCH 8
+#define U_ETH_MAX_SGES_FOR_PACKET 3
+
+/* Rx ring params */
+#define U_ETH_LOCAL_BD_RING_SIZE 8
+#define U_ETH_LOCAL_SGE_RING_SIZE 10
+#define U_ETH_SGL_SIZE 8
+ /* The fw will padd the buffer with this value, so the IP header \
+ will be align to 4 Byte */
+#define IP_HEADER_ALIGNMENT_PADDING 2
+
+#define U_ETH_SGES_PER_PAGE_INVERSE_MASK \
+ (0xFFFF - ((PAGE_SIZE/((STRUCT_SIZE(eth_rx_sge))/8))-1))
+
+#define TU_ETH_CQES_PER_PAGE (PAGE_SIZE/(STRUCT_SIZE(eth_rx_cqe)/8))
+#define U_ETH_BDS_PER_PAGE (PAGE_SIZE/(STRUCT_SIZE(eth_rx_bd)/8))
+#define U_ETH_SGES_PER_PAGE (PAGE_SIZE/(STRUCT_SIZE(eth_rx_sge)/8))
+
+#define U_ETH_BDS_PER_PAGE_MASK (U_ETH_BDS_PER_PAGE-1)
+#define U_ETH_CQE_PER_PAGE_MASK (TU_ETH_CQES_PER_PAGE-1)
+#define U_ETH_SGES_PER_PAGE_MASK (U_ETH_SGES_PER_PAGE-1)
+
+#define U_ETH_UNDEFINED_Q 0xFF
+
+#define T_ETH_INDIRECTION_TABLE_SIZE 128
+#define T_ETH_RSS_KEY 10
+#define ETH_NUM_OF_RSS_ENGINES_E2 72
+
+#define FILTER_RULES_COUNT 16
+#define MULTICAST_RULES_COUNT 16
+#define CLASSIFY_RULES_COUNT 16
+
+/*The CRC32 seed, that is used for the hash(reduction) multicast address */
+#define ETH_CRC32_HASH_SEED 0x00000000
+
+#define ETH_CRC32_HASH_BIT_SIZE (8)
+#define ETH_CRC32_HASH_MASK EVAL((1<<ETH_CRC32_HASH_BIT_SIZE)-1)
+
+/* Maximal L2 clients supported */
+#define ETH_MAX_RX_CLIENTS_E1 18
+#define ETH_MAX_RX_CLIENTS_E1H 28
+#define ETH_MAX_RX_CLIENTS_E2 152
+
+/* Maximal statistics client Ids */
+#define MAX_STAT_COUNTER_ID_E1 36
+#define MAX_STAT_COUNTER_ID_E1H 56
+#define MAX_STAT_COUNTER_ID_E2 140
+
+#define MAX_MAC_CREDIT_E1 192 /* Per Chip */
+#define MAX_MAC_CREDIT_E1H 256 /* Per Chip */
+#define MAX_MAC_CREDIT_E2 272 /* Per Path */
+#define MAX_VLAN_CREDIT_E1 0 /* Per Chip */
+#define MAX_VLAN_CREDIT_E1H 0 /* Per Chip */
+#define MAX_VLAN_CREDIT_E2 272 /* Per Path */
+
+
+/* Maximal aggregation queues supported */
+#define ETH_MAX_AGGREGATION_QUEUES_E1 32
+#define ETH_MAX_AGGREGATION_QUEUES_E1H_E2 64
+
+
+#define ETH_NUM_OF_MCAST_BINS 256
+#define ETH_NUM_OF_MCAST_ENGINES_E2 72
+
+#define ETH_MIN_RX_CQES_WITHOUT_TPA (MAX_RAMRODS_PER_PORT + 3)
+#define ETH_MIN_RX_CQES_WITH_TPA_E1 \
+ (ETH_MAX_AGGREGATION_QUEUES_E1 + ETH_MIN_RX_CQES_WITHOUT_TPA)
+#define ETH_MIN_RX_CQES_WITH_TPA_E1H_E2 \
+ (ETH_MAX_AGGREGATION_QUEUES_E1H_E2 + ETH_MIN_RX_CQES_WITHOUT_TPA)
+
+#define DISABLE_STATISTIC_COUNTER_ID_VALUE 0
+
+
+/**
+ * This file defines HSI constants common to all microcode flows
+ */
+
+#define PROTOCOL_STATE_BIT_OFFSET 6
+
+#define ETH_STATE (ETH_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET)
+#define TOE_STATE (TOE_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET)
+#define RDMA_STATE (RDMA_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET)
+
+/* microcode fixed page page size 4K (chains and ring segments) */
+#define MC_PAGE_SIZE 4096
+
+/* Number of indices per slow-path SB */
+#define HC_SP_SB_MAX_INDICES 16
+
+/* Number of indices per SB */
+#define HC_SB_MAX_INDICES_E1X 8
+#define HC_SB_MAX_INDICES_E2 8
+
+#define HC_SB_MAX_SB_E1X 32
+#define HC_SB_MAX_SB_E2 136
+
+#define HC_SP_SB_ID 0xde
+
+#define HC_SB_MAX_SM 2
+
+#define HC_SB_MAX_DYNAMIC_INDICES 4
+
+/* max number of slow path commands per port */
+#define MAX_RAMRODS_PER_PORT 8
+
+
+/**** DEFINES FOR TIMERS/CLOCKS RESOLUTIONS ****/
+
+#define TIMERS_TICK_SIZE_CHIP (1e-3)
+
+#define TSEMI_CLK1_RESUL_CHIP (1e-3)
+
+#define XSEMI_CLK1_RESUL_CHIP (1e-3)
+
+#define SDM_TIMER_TICK_RESUL_CHIP (4 * (1e-6))
+
+/**** END DEFINES FOR TIMERS/CLOCKS RESOLUTIONS ****/
+
+#define XSTORM_IP_ID_ROLL_HALF 0x8000
+#define XSTORM_IP_ID_ROLL_ALL 0
+
+#define FW_LOG_LIST_SIZE 50
+
+#define NUM_OF_SAFC_BITS 16
+#define MAX_COS_NUMBER 4
+#define MAX_TRAFFIC_TYPES 8
+#define MAX_PFC_PRIORITIES 8
+
+ /* used by array traffic_type_to_priority[] to mark traffic type \
+ that is not mapped to priority*/
+#define LLFC_TRAFFIC_TYPE_TO_PRIORITY_UNMAPPED 0xFF
+
+
+#define C_ERES_PER_PAGE \
+ (PAGE_SIZE / BITS_TO_BYTES(STRUCT_SIZE(event_ring_elem)))
+#define C_ERE_PER_PAGE_MASK (C_ERES_PER_PAGE - 1)
+
+#define STATS_QUERY_CMD_COUNT 16
+
+#define NIV_LIST_TABLE_SIZE 4096
+
+#define INVALID_VNIC_ID 0xFF
+
+
+#define UNDEF_IRO 0x80000000
+
+
+#endif /* BNX2X_FW_DEFS_H */
diff --git a/drivers/net/bnx2x/bnx2x_fw_file_hdr.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_file_hdr.h
index f4a07fbaed05..f4a07fbaed05 100644
--- a/drivers/net/bnx2x/bnx2x_fw_file_hdr.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_file_hdr.h
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
new file mode 100644
index 000000000000..fc754cb6cc0f
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
@@ -0,0 +1,5133 @@
+/* bnx2x_hsi.h: Broadcom Everest network driver.
+ *
+ * Copyright (c) 2007-2011 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+#ifndef BNX2X_HSI_H
+#define BNX2X_HSI_H
+
+#include "bnx2x_fw_defs.h"
+
+#define FW_ENCODE_32BIT_PATTERN 0x1e1e1e1e
+
+struct license_key {
+ u32 reserved[6];
+
+ u32 max_iscsi_conn;
+#define BNX2X_MAX_ISCSI_TRGT_CONN_MASK 0xFFFF
+#define BNX2X_MAX_ISCSI_TRGT_CONN_SHIFT 0
+#define BNX2X_MAX_ISCSI_INIT_CONN_MASK 0xFFFF0000
+#define BNX2X_MAX_ISCSI_INIT_CONN_SHIFT 16
+
+ u32 reserved_a;
+
+ u32 max_fcoe_conn;
+#define BNX2X_MAX_FCOE_TRGT_CONN_MASK 0xFFFF
+#define BNX2X_MAX_FCOE_TRGT_CONN_SHIFT 0
+#define BNX2X_MAX_FCOE_INIT_CONN_MASK 0xFFFF0000
+#define BNX2X_MAX_FCOE_INIT_CONN_SHIFT 16
+
+ u32 reserved_b[4];
+};
+
+
+#define PORT_0 0
+#define PORT_1 1
+#define PORT_MAX 2
+
+/****************************************************************************
+ * Shared HW configuration *
+ ****************************************************************************/
+#define PIN_CFG_NA 0x00000000
+#define PIN_CFG_GPIO0_P0 0x00000001
+#define PIN_CFG_GPIO1_P0 0x00000002
+#define PIN_CFG_GPIO2_P0 0x00000003
+#define PIN_CFG_GPIO3_P0 0x00000004
+#define PIN_CFG_GPIO0_P1 0x00000005
+#define PIN_CFG_GPIO1_P1 0x00000006
+#define PIN_CFG_GPIO2_P1 0x00000007
+#define PIN_CFG_GPIO3_P1 0x00000008
+#define PIN_CFG_EPIO0 0x00000009
+#define PIN_CFG_EPIO1 0x0000000a
+#define PIN_CFG_EPIO2 0x0000000b
+#define PIN_CFG_EPIO3 0x0000000c
+#define PIN_CFG_EPIO4 0x0000000d
+#define PIN_CFG_EPIO5 0x0000000e
+#define PIN_CFG_EPIO6 0x0000000f
+#define PIN_CFG_EPIO7 0x00000010
+#define PIN_CFG_EPIO8 0x00000011
+#define PIN_CFG_EPIO9 0x00000012
+#define PIN_CFG_EPIO10 0x00000013
+#define PIN_CFG_EPIO11 0x00000014
+#define PIN_CFG_EPIO12 0x00000015
+#define PIN_CFG_EPIO13 0x00000016
+#define PIN_CFG_EPIO14 0x00000017
+#define PIN_CFG_EPIO15 0x00000018
+#define PIN_CFG_EPIO16 0x00000019
+#define PIN_CFG_EPIO17 0x0000001a
+#define PIN_CFG_EPIO18 0x0000001b
+#define PIN_CFG_EPIO19 0x0000001c
+#define PIN_CFG_EPIO20 0x0000001d
+#define PIN_CFG_EPIO21 0x0000001e
+#define PIN_CFG_EPIO22 0x0000001f
+#define PIN_CFG_EPIO23 0x00000020
+#define PIN_CFG_EPIO24 0x00000021
+#define PIN_CFG_EPIO25 0x00000022
+#define PIN_CFG_EPIO26 0x00000023
+#define PIN_CFG_EPIO27 0x00000024
+#define PIN_CFG_EPIO28 0x00000025
+#define PIN_CFG_EPIO29 0x00000026
+#define PIN_CFG_EPIO30 0x00000027
+#define PIN_CFG_EPIO31 0x00000028
+
+/* EPIO definition */
+#define EPIO_CFG_NA 0x00000000
+#define EPIO_CFG_EPIO0 0x00000001
+#define EPIO_CFG_EPIO1 0x00000002
+#define EPIO_CFG_EPIO2 0x00000003
+#define EPIO_CFG_EPIO3 0x00000004
+#define EPIO_CFG_EPIO4 0x00000005
+#define EPIO_CFG_EPIO5 0x00000006
+#define EPIO_CFG_EPIO6 0x00000007
+#define EPIO_CFG_EPIO7 0x00000008
+#define EPIO_CFG_EPIO8 0x00000009
+#define EPIO_CFG_EPIO9 0x0000000a
+#define EPIO_CFG_EPIO10 0x0000000b
+#define EPIO_CFG_EPIO11 0x0000000c
+#define EPIO_CFG_EPIO12 0x0000000d
+#define EPIO_CFG_EPIO13 0x0000000e
+#define EPIO_CFG_EPIO14 0x0000000f
+#define EPIO_CFG_EPIO15 0x00000010
+#define EPIO_CFG_EPIO16 0x00000011
+#define EPIO_CFG_EPIO17 0x00000012
+#define EPIO_CFG_EPIO18 0x00000013
+#define EPIO_CFG_EPIO19 0x00000014
+#define EPIO_CFG_EPIO20 0x00000015
+#define EPIO_CFG_EPIO21 0x00000016
+#define EPIO_CFG_EPIO22 0x00000017
+#define EPIO_CFG_EPIO23 0x00000018
+#define EPIO_CFG_EPIO24 0x00000019
+#define EPIO_CFG_EPIO25 0x0000001a
+#define EPIO_CFG_EPIO26 0x0000001b
+#define EPIO_CFG_EPIO27 0x0000001c
+#define EPIO_CFG_EPIO28 0x0000001d
+#define EPIO_CFG_EPIO29 0x0000001e
+#define EPIO_CFG_EPIO30 0x0000001f
+#define EPIO_CFG_EPIO31 0x00000020
+
+
+struct shared_hw_cfg { /* NVRAM Offset */
+ /* Up to 16 bytes of NULL-terminated string */
+ u8 part_num[16]; /* 0x104 */
+
+ u32 config; /* 0x114 */
+ #define SHARED_HW_CFG_MDIO_VOLTAGE_MASK 0x00000001
+ #define SHARED_HW_CFG_MDIO_VOLTAGE_SHIFT 0
+ #define SHARED_HW_CFG_MDIO_VOLTAGE_1_2V 0x00000000
+ #define SHARED_HW_CFG_MDIO_VOLTAGE_2_5V 0x00000001
+ #define SHARED_HW_CFG_MCP_RST_ON_CORE_RST_EN 0x00000002
+
+ #define SHARED_HW_CFG_PORT_SWAP 0x00000004
+
+ #define SHARED_HW_CFG_BEACON_WOL_EN 0x00000008
+
+ #define SHARED_HW_CFG_PCIE_GEN3_DISABLED 0x00000000
+ #define SHARED_HW_CFG_PCIE_GEN3_ENABLED 0x00000010
+
+ #define SHARED_HW_CFG_MFW_SELECT_MASK 0x00000700
+ #define SHARED_HW_CFG_MFW_SELECT_SHIFT 8
+ /* Whatever MFW found in NVM
+ (if multiple found, priority order is: NC-SI, UMP, IPMI) */
+ #define SHARED_HW_CFG_MFW_SELECT_DEFAULT 0x00000000
+ #define SHARED_HW_CFG_MFW_SELECT_NC_SI 0x00000100
+ #define SHARED_HW_CFG_MFW_SELECT_UMP 0x00000200
+ #define SHARED_HW_CFG_MFW_SELECT_IPMI 0x00000300
+ /* Use SPIO4 as an arbiter between: 0-NC_SI, 1-IPMI
+ (can only be used when an add-in board, not BMC, pulls-down SPIO4) */
+ #define SHARED_HW_CFG_MFW_SELECT_SPIO4_NC_SI_IPMI 0x00000400
+ /* Use SPIO4 as an arbiter between: 0-UMP, 1-IPMI
+ (can only be used when an add-in board, not BMC, pulls-down SPIO4) */
+ #define SHARED_HW_CFG_MFW_SELECT_SPIO4_UMP_IPMI 0x00000500
+ /* Use SPIO4 as an arbiter between: 0-NC-SI, 1-UMP
+ (can only be used when an add-in board, not BMC, pulls-down SPIO4) */
+ #define SHARED_HW_CFG_MFW_SELECT_SPIO4_NC_SI_UMP 0x00000600
+
+ #define SHARED_HW_CFG_LED_MODE_MASK 0x000f0000
+ #define SHARED_HW_CFG_LED_MODE_SHIFT 16
+ #define SHARED_HW_CFG_LED_MAC1 0x00000000
+ #define SHARED_HW_CFG_LED_PHY1 0x00010000
+ #define SHARED_HW_CFG_LED_PHY2 0x00020000
+ #define SHARED_HW_CFG_LED_PHY3 0x00030000
+ #define SHARED_HW_CFG_LED_MAC2 0x00040000
+ #define SHARED_HW_CFG_LED_PHY4 0x00050000
+ #define SHARED_HW_CFG_LED_PHY5 0x00060000
+ #define SHARED_HW_CFG_LED_PHY6 0x00070000
+ #define SHARED_HW_CFG_LED_MAC3 0x00080000
+ #define SHARED_HW_CFG_LED_PHY7 0x00090000
+ #define SHARED_HW_CFG_LED_PHY9 0x000a0000
+ #define SHARED_HW_CFG_LED_PHY11 0x000b0000
+ #define SHARED_HW_CFG_LED_MAC4 0x000c0000
+ #define SHARED_HW_CFG_LED_PHY8 0x000d0000
+ #define SHARED_HW_CFG_LED_EXTPHY1 0x000e0000
+
+
+ #define SHARED_HW_CFG_AN_ENABLE_MASK 0x3f000000
+ #define SHARED_HW_CFG_AN_ENABLE_SHIFT 24
+ #define SHARED_HW_CFG_AN_ENABLE_CL37 0x01000000
+ #define SHARED_HW_CFG_AN_ENABLE_CL73 0x02000000
+ #define SHARED_HW_CFG_AN_ENABLE_BAM 0x04000000
+ #define SHARED_HW_CFG_AN_ENABLE_PARALLEL_DETECTION 0x08000000
+ #define SHARED_HW_CFG_AN_EN_SGMII_FIBER_AUTO_DETECT 0x10000000
+ #define SHARED_HW_CFG_AN_ENABLE_REMOTE_PHY 0x20000000
+
+ #define SHARED_HW_CFG_SRIOV_MASK 0x40000000
+ #define SHARED_HW_CFG_SRIOV_DISABLED 0x00000000
+ #define SHARED_HW_CFG_SRIOV_ENABLED 0x40000000
+
+ #define SHARED_HW_CFG_ATC_MASK 0x80000000
+ #define SHARED_HW_CFG_ATC_DISABLED 0x00000000
+ #define SHARED_HW_CFG_ATC_ENABLED 0x80000000
+
+ u32 config2; /* 0x118 */
+ /* one time auto detect grace period (in sec) */
+ #define SHARED_HW_CFG_GRACE_PERIOD_MASK 0x000000ff
+ #define SHARED_HW_CFG_GRACE_PERIOD_SHIFT 0
+
+ #define SHARED_HW_CFG_PCIE_GEN2_ENABLED 0x00000100
+ #define SHARED_HW_CFG_PCIE_GEN2_DISABLED 0x00000000
+
+ /* The default value for the core clock is 250MHz and it is
+ achieved by setting the clock change to 4 */
+ #define SHARED_HW_CFG_CLOCK_CHANGE_MASK 0x00000e00
+ #define SHARED_HW_CFG_CLOCK_CHANGE_SHIFT 9
+
+ #define SHARED_HW_CFG_SMBUS_TIMING_MASK 0x00001000
+ #define SHARED_HW_CFG_SMBUS_TIMING_100KHZ 0x00000000
+ #define SHARED_HW_CFG_SMBUS_TIMING_400KHZ 0x00001000
+
+ #define SHARED_HW_CFG_HIDE_PORT1 0x00002000
+
+ #define SHARED_HW_CFG_WOL_CAPABLE_MASK 0x00004000
+ #define SHARED_HW_CFG_WOL_CAPABLE_DISABLED 0x00000000
+ #define SHARED_HW_CFG_WOL_CAPABLE_ENABLED 0x00004000
+
+ /* Output low when PERST is asserted */
+ #define SHARED_HW_CFG_SPIO4_FOLLOW_PERST_MASK 0x00008000
+ #define SHARED_HW_CFG_SPIO4_FOLLOW_PERST_DISABLED 0x00000000
+ #define SHARED_HW_CFG_SPIO4_FOLLOW_PERST_ENABLED 0x00008000
+
+ #define SHARED_HW_CFG_PCIE_GEN2_PREEMPHASIS_MASK 0x00070000
+ #define SHARED_HW_CFG_PCIE_GEN2_PREEMPHASIS_SHIFT 16
+ #define SHARED_HW_CFG_PCIE_GEN2_PREEMPHASIS_HW 0x00000000
+ #define SHARED_HW_CFG_PCIE_GEN2_PREEMPHASIS_0DB 0x00010000
+ #define SHARED_HW_CFG_PCIE_GEN2_PREEMPHASIS_3_5DB 0x00020000
+ #define SHARED_HW_CFG_PCIE_GEN2_PREEMPHASIS_6_0DB 0x00030000
+
+ /* The fan failure mechanism is usually related to the PHY type
+ since the power consumption of the board is determined by the PHY.
+ Currently, fan is required for most designs with SFX7101, BCM8727
+ and BCM8481. If a fan is not required for a board which uses one
+ of those PHYs, this field should be set to "Disabled". If a fan is
+ required for a different PHY type, this option should be set to
+ "Enabled". The fan failure indication is expected on SPIO5 */
+ #define SHARED_HW_CFG_FAN_FAILURE_MASK 0x00180000
+ #define SHARED_HW_CFG_FAN_FAILURE_SHIFT 19
+ #define SHARED_HW_CFG_FAN_FAILURE_PHY_TYPE 0x00000000
+ #define SHARED_HW_CFG_FAN_FAILURE_DISABLED 0x00080000
+ #define SHARED_HW_CFG_FAN_FAILURE_ENABLED 0x00100000
+
+ /* ASPM Power Management support */
+ #define SHARED_HW_CFG_ASPM_SUPPORT_MASK 0x00600000
+ #define SHARED_HW_CFG_ASPM_SUPPORT_SHIFT 21
+ #define SHARED_HW_CFG_ASPM_SUPPORT_L0S_L1_ENABLED 0x00000000
+ #define SHARED_HW_CFG_ASPM_SUPPORT_L0S_DISABLED 0x00200000
+ #define SHARED_HW_CFG_ASPM_SUPPORT_L1_DISABLED 0x00400000
+ #define SHARED_HW_CFG_ASPM_SUPPORT_L0S_L1_DISABLED 0x00600000
+
+ /* The value of PM_TL_IGNORE_REQS (bit0) in PCI register
+ tl_control_0 (register 0x2800) */
+ #define SHARED_HW_CFG_PREVENT_L1_ENTRY_MASK 0x00800000
+ #define SHARED_HW_CFG_PREVENT_L1_ENTRY_DISABLED 0x00000000
+ #define SHARED_HW_CFG_PREVENT_L1_ENTRY_ENABLED 0x00800000
+
+ #define SHARED_HW_CFG_PORT_MODE_MASK 0x01000000
+ #define SHARED_HW_CFG_PORT_MODE_2 0x00000000
+ #define SHARED_HW_CFG_PORT_MODE_4 0x01000000
+
+ #define SHARED_HW_CFG_PATH_SWAP_MASK 0x02000000
+ #define SHARED_HW_CFG_PATH_SWAP_DISABLED 0x00000000
+ #define SHARED_HW_CFG_PATH_SWAP_ENABLED 0x02000000
+
+ /* Set the MDC/MDIO access for the first external phy */
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS1_MASK 0x1C000000
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS1_SHIFT 26
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS1_PHY_TYPE 0x00000000
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS1_EMAC0 0x04000000
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS1_EMAC1 0x08000000
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS1_BOTH 0x0c000000
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS1_SWAPPED 0x10000000
+
+ /* Set the MDC/MDIO access for the second external phy */
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS2_MASK 0xE0000000
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS2_SHIFT 29
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS2_PHY_TYPE 0x00000000
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS2_EMAC0 0x20000000
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS2_EMAC1 0x40000000
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS2_BOTH 0x60000000
+ #define SHARED_HW_CFG_MDC_MDIO_ACCESS2_SWAPPED 0x80000000
+
+
+ u32 power_dissipated; /* 0x11c */
+ #define SHARED_HW_CFG_POWER_MGNT_SCALE_MASK 0x00ff0000
+ #define SHARED_HW_CFG_POWER_MGNT_SCALE_SHIFT 16
+ #define SHARED_HW_CFG_POWER_MGNT_UNKNOWN_SCALE 0x00000000
+ #define SHARED_HW_CFG_POWER_MGNT_DOT_1_WATT 0x00010000
+ #define SHARED_HW_CFG_POWER_MGNT_DOT_01_WATT 0x00020000
+ #define SHARED_HW_CFG_POWER_MGNT_DOT_001_WATT 0x00030000
+
+ #define SHARED_HW_CFG_POWER_DIS_CMN_MASK 0xff000000
+ #define SHARED_HW_CFG_POWER_DIS_CMN_SHIFT 24
+
+ u32 ump_nc_si_config; /* 0x120 */
+ #define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_MASK 0x00000003
+ #define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_SHIFT 0
+ #define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_MAC 0x00000000
+ #define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_PHY 0x00000001
+ #define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_MII 0x00000000
+ #define SHARED_HW_CFG_UMP_NC_SI_MII_MODE_RMII 0x00000002
+
+ #define SHARED_HW_CFG_UMP_NC_SI_NUM_DEVS_MASK 0x00000f00
+ #define SHARED_HW_CFG_UMP_NC_SI_NUM_DEVS_SHIFT 8
+
+ #define SHARED_HW_CFG_UMP_NC_SI_EXT_PHY_TYPE_MASK 0x00ff0000
+ #define SHARED_HW_CFG_UMP_NC_SI_EXT_PHY_TYPE_SHIFT 16
+ #define SHARED_HW_CFG_UMP_NC_SI_EXT_PHY_TYPE_NONE 0x00000000
+ #define SHARED_HW_CFG_UMP_NC_SI_EXT_PHY_TYPE_BCM5221 0x00010000
+
+ u32 board; /* 0x124 */
+ #define SHARED_HW_CFG_E3_I2C_MUX0_MASK 0x0000003F
+ #define SHARED_HW_CFG_E3_I2C_MUX0_SHIFT 0
+ #define SHARED_HW_CFG_E3_I2C_MUX1_MASK 0x00000FC0
+ #define SHARED_HW_CFG_E3_I2C_MUX1_SHIFT 6
+ /* Use the PIN_CFG_XXX defines on top */
+ #define SHARED_HW_CFG_BOARD_REV_MASK 0x00ff0000
+ #define SHARED_HW_CFG_BOARD_REV_SHIFT 16
+
+ #define SHARED_HW_CFG_BOARD_MAJOR_VER_MASK 0x0f000000
+ #define SHARED_HW_CFG_BOARD_MAJOR_VER_SHIFT 24
+
+ #define SHARED_HW_CFG_BOARD_MINOR_VER_MASK 0xf0000000
+ #define SHARED_HW_CFG_BOARD_MINOR_VER_SHIFT 28
+
+ u32 wc_lane_config; /* 0x128 */
+ #define SHARED_HW_CFG_LANE_SWAP_CFG_MASK 0x0000FFFF
+ #define SHARED_HW_CFG_LANE_SWAP_CFG_SHIFT 0
+ #define SHARED_HW_CFG_LANE_SWAP_CFG_32103210 0x00001b1b
+ #define SHARED_HW_CFG_LANE_SWAP_CFG_32100123 0x00001be4
+ #define SHARED_HW_CFG_LANE_SWAP_CFG_01233210 0x0000e41b
+ #define SHARED_HW_CFG_LANE_SWAP_CFG_01230123 0x0000e4e4
+ #define SHARED_HW_CFG_LANE_SWAP_CFG_TX_MASK 0x000000FF
+ #define SHARED_HW_CFG_LANE_SWAP_CFG_TX_SHIFT 0
+ #define SHARED_HW_CFG_LANE_SWAP_CFG_RX_MASK 0x0000FF00
+ #define SHARED_HW_CFG_LANE_SWAP_CFG_RX_SHIFT 8
+
+ /* TX lane Polarity swap */
+ #define SHARED_HW_CFG_TX_LANE0_POL_FLIP_ENABLED 0x00010000
+ #define SHARED_HW_CFG_TX_LANE1_POL_FLIP_ENABLED 0x00020000
+ #define SHARED_HW_CFG_TX_LANE2_POL_FLIP_ENABLED 0x00040000
+ #define SHARED_HW_CFG_TX_LANE3_POL_FLIP_ENABLED 0x00080000
+ /* TX lane Polarity swap */
+ #define SHARED_HW_CFG_RX_LANE0_POL_FLIP_ENABLED 0x00100000
+ #define SHARED_HW_CFG_RX_LANE1_POL_FLIP_ENABLED 0x00200000
+ #define SHARED_HW_CFG_RX_LANE2_POL_FLIP_ENABLED 0x00400000
+ #define SHARED_HW_CFG_RX_LANE3_POL_FLIP_ENABLED 0x00800000
+
+ /* Selects the port layout of the board */
+ #define SHARED_HW_CFG_E3_PORT_LAYOUT_MASK 0x0F000000
+ #define SHARED_HW_CFG_E3_PORT_LAYOUT_SHIFT 24
+ #define SHARED_HW_CFG_E3_PORT_LAYOUT_2P_01 0x00000000
+ #define SHARED_HW_CFG_E3_PORT_LAYOUT_2P_10 0x01000000
+ #define SHARED_HW_CFG_E3_PORT_LAYOUT_4P_0123 0x02000000
+ #define SHARED_HW_CFG_E3_PORT_LAYOUT_4P_1032 0x03000000
+ #define SHARED_HW_CFG_E3_PORT_LAYOUT_4P_2301 0x04000000
+ #define SHARED_HW_CFG_E3_PORT_LAYOUT_4P_3210 0x05000000
+};
+
+
+/****************************************************************************
+ * Port HW configuration *
+ ****************************************************************************/
+struct port_hw_cfg { /* port 0: 0x12c port 1: 0x2bc */
+
+ u32 pci_id;
+ #define PORT_HW_CFG_PCI_VENDOR_ID_MASK 0xffff0000
+ #define PORT_HW_CFG_PCI_DEVICE_ID_MASK 0x0000ffff
+
+ u32 pci_sub_id;
+ #define PORT_HW_CFG_PCI_SUBSYS_DEVICE_ID_MASK 0xffff0000
+ #define PORT_HW_CFG_PCI_SUBSYS_VENDOR_ID_MASK 0x0000ffff
+
+ u32 power_dissipated;
+ #define PORT_HW_CFG_POWER_DIS_D0_MASK 0x000000ff
+ #define PORT_HW_CFG_POWER_DIS_D0_SHIFT 0
+ #define PORT_HW_CFG_POWER_DIS_D1_MASK 0x0000ff00
+ #define PORT_HW_CFG_POWER_DIS_D1_SHIFT 8
+ #define PORT_HW_CFG_POWER_DIS_D2_MASK 0x00ff0000
+ #define PORT_HW_CFG_POWER_DIS_D2_SHIFT 16
+ #define PORT_HW_CFG_POWER_DIS_D3_MASK 0xff000000
+ #define PORT_HW_CFG_POWER_DIS_D3_SHIFT 24
+
+ u32 power_consumed;
+ #define PORT_HW_CFG_POWER_CONS_D0_MASK 0x000000ff
+ #define PORT_HW_CFG_POWER_CONS_D0_SHIFT 0
+ #define PORT_HW_CFG_POWER_CONS_D1_MASK 0x0000ff00
+ #define PORT_HW_CFG_POWER_CONS_D1_SHIFT 8
+ #define PORT_HW_CFG_POWER_CONS_D2_MASK 0x00ff0000
+ #define PORT_HW_CFG_POWER_CONS_D2_SHIFT 16
+ #define PORT_HW_CFG_POWER_CONS_D3_MASK 0xff000000
+ #define PORT_HW_CFG_POWER_CONS_D3_SHIFT 24
+
+ u32 mac_upper;
+ #define PORT_HW_CFG_UPPERMAC_MASK 0x0000ffff
+ #define PORT_HW_CFG_UPPERMAC_SHIFT 0
+ u32 mac_lower;
+
+ u32 iscsi_mac_upper; /* Upper 16 bits are always zeroes */
+ u32 iscsi_mac_lower;
+
+ u32 rdma_mac_upper; /* Upper 16 bits are always zeroes */
+ u32 rdma_mac_lower;
+
+ u32 serdes_config;
+ #define PORT_HW_CFG_SERDES_TX_DRV_PRE_EMPHASIS_MASK 0x0000ffff
+ #define PORT_HW_CFG_SERDES_TX_DRV_PRE_EMPHASIS_SHIFT 0
+
+ #define PORT_HW_CFG_SERDES_RX_DRV_EQUALIZER_MASK 0xffff0000
+ #define PORT_HW_CFG_SERDES_RX_DRV_EQUALIZER_SHIFT 16
+
+
+ /* Default values: 2P-64, 4P-32 */
+ u32 pf_config; /* 0x158 */
+ #define PORT_HW_CFG_PF_NUM_VF_MASK 0x0000007F
+ #define PORT_HW_CFG_PF_NUM_VF_SHIFT 0
+
+ /* Default values: 17 */
+ #define PORT_HW_CFG_PF_NUM_MSIX_VECTORS_MASK 0x00007F00
+ #define PORT_HW_CFG_PF_NUM_MSIX_VECTORS_SHIFT 8
+
+ #define PORT_HW_CFG_ENABLE_FLR_MASK 0x00010000
+ #define PORT_HW_CFG_FLR_ENABLED 0x00010000
+
+ u32 vf_config; /* 0x15C */
+ #define PORT_HW_CFG_VF_NUM_MSIX_VECTORS_MASK 0x0000007F
+ #define PORT_HW_CFG_VF_NUM_MSIX_VECTORS_SHIFT 0
+
+ #define PORT_HW_CFG_VF_PCI_DEVICE_ID_MASK 0xFFFF0000
+ #define PORT_HW_CFG_VF_PCI_DEVICE_ID_SHIFT 16
+
+ u32 mf_pci_id; /* 0x160 */
+ #define PORT_HW_CFG_MF_PCI_DEVICE_ID_MASK 0x0000FFFF
+ #define PORT_HW_CFG_MF_PCI_DEVICE_ID_SHIFT 0
+
+ /* Controls the TX laser of the SFP+ module */
+ u32 sfp_ctrl; /* 0x164 */
+ #define PORT_HW_CFG_TX_LASER_MASK 0x000000FF
+ #define PORT_HW_CFG_TX_LASER_SHIFT 0
+ #define PORT_HW_CFG_TX_LASER_MDIO 0x00000000
+ #define PORT_HW_CFG_TX_LASER_GPIO0 0x00000001
+ #define PORT_HW_CFG_TX_LASER_GPIO1 0x00000002
+ #define PORT_HW_CFG_TX_LASER_GPIO2 0x00000003
+ #define PORT_HW_CFG_TX_LASER_GPIO3 0x00000004
+
+ /* Controls the fault module LED of the SFP+ */
+ #define PORT_HW_CFG_FAULT_MODULE_LED_MASK 0x0000FF00
+ #define PORT_HW_CFG_FAULT_MODULE_LED_SHIFT 8
+ #define PORT_HW_CFG_FAULT_MODULE_LED_GPIO0 0x00000000
+ #define PORT_HW_CFG_FAULT_MODULE_LED_GPIO1 0x00000100
+ #define PORT_HW_CFG_FAULT_MODULE_LED_GPIO2 0x00000200
+ #define PORT_HW_CFG_FAULT_MODULE_LED_GPIO3 0x00000300
+ #define PORT_HW_CFG_FAULT_MODULE_LED_DISABLED 0x00000400
+
+ /* The output pin TX_DIS that controls the TX laser of the SFP+
+ module. Use the PIN_CFG_XXX defines on top */
+ u32 e3_sfp_ctrl; /* 0x168 */
+ #define PORT_HW_CFG_E3_TX_LASER_MASK 0x000000FF
+ #define PORT_HW_CFG_E3_TX_LASER_SHIFT 0
+
+ /* The output pin for SFPP_TYPE which turns on the Fault module LED */
+ #define PORT_HW_CFG_E3_FAULT_MDL_LED_MASK 0x0000FF00
+ #define PORT_HW_CFG_E3_FAULT_MDL_LED_SHIFT 8
+
+ /* The input pin MOD_ABS that indicates whether SFP+ module is
+ present or not. Use the PIN_CFG_XXX defines on top */
+ #define PORT_HW_CFG_E3_MOD_ABS_MASK 0x00FF0000
+ #define PORT_HW_CFG_E3_MOD_ABS_SHIFT 16
+
+ /* The output pin PWRDIS_SFP_X which disable the power of the SFP+
+ module. Use the PIN_CFG_XXX defines on top */
+ #define PORT_HW_CFG_E3_PWR_DIS_MASK 0xFF000000
+ #define PORT_HW_CFG_E3_PWR_DIS_SHIFT 24
+
+ /*
+ * The input pin which signals module transmit fault. Use the
+ * PIN_CFG_XXX defines on top
+ */
+ u32 e3_cmn_pin_cfg; /* 0x16C */
+ #define PORT_HW_CFG_E3_TX_FAULT_MASK 0x000000FF
+ #define PORT_HW_CFG_E3_TX_FAULT_SHIFT 0
+
+ /* The output pin which reset the PHY. Use the PIN_CFG_XXX defines on
+ top */
+ #define PORT_HW_CFG_E3_PHY_RESET_MASK 0x0000FF00
+ #define PORT_HW_CFG_E3_PHY_RESET_SHIFT 8
+
+ /*
+ * The output pin which powers down the PHY. Use the PIN_CFG_XXX
+ * defines on top
+ */
+ #define PORT_HW_CFG_E3_PWR_DOWN_MASK 0x00FF0000
+ #define PORT_HW_CFG_E3_PWR_DOWN_SHIFT 16
+
+ /* The output pin values BSC_SEL which selects the I2C for this port
+ in the I2C Mux */
+ #define PORT_HW_CFG_E3_I2C_MUX0_MASK 0x01000000
+ #define PORT_HW_CFG_E3_I2C_MUX1_MASK 0x02000000
+
+
+ /*
+ * The input pin I_FAULT which indicate over-current has occurred.
+ * Use the PIN_CFG_XXX defines on top
+ */
+ u32 e3_cmn_pin_cfg1; /* 0x170 */
+ #define PORT_HW_CFG_E3_OVER_CURRENT_MASK 0x000000FF
+ #define PORT_HW_CFG_E3_OVER_CURRENT_SHIFT 0
+ u32 reserved0[7]; /* 0x174 */
+
+ u32 aeu_int_mask; /* 0x190 */
+
+ u32 media_type; /* 0x194 */
+ #define PORT_HW_CFG_MEDIA_TYPE_PHY0_MASK 0x000000FF
+ #define PORT_HW_CFG_MEDIA_TYPE_PHY0_SHIFT 0
+
+ #define PORT_HW_CFG_MEDIA_TYPE_PHY1_MASK 0x0000FF00
+ #define PORT_HW_CFG_MEDIA_TYPE_PHY1_SHIFT 8
+
+ #define PORT_HW_CFG_MEDIA_TYPE_PHY2_MASK 0x00FF0000
+ #define PORT_HW_CFG_MEDIA_TYPE_PHY2_SHIFT 16
+
+ /* 4 times 16 bits for all 4 lanes. In case external PHY is present
+ (not direct mode), those values will not take effect on the 4 XGXS
+ lanes. For some external PHYs (such as 8706 and 8726) the values
+ will be used to configure the external PHY in those cases, not
+ all 4 values are needed. */
+ u16 xgxs_config_rx[4]; /* 0x198 */
+ u16 xgxs_config_tx[4]; /* 0x1A0 */
+
+ /* For storing FCOE mac on shared memory */
+ u32 fcoe_fip_mac_upper;
+ #define PORT_HW_CFG_FCOE_UPPERMAC_MASK 0x0000ffff
+ #define PORT_HW_CFG_FCOE_UPPERMAC_SHIFT 0
+ u32 fcoe_fip_mac_lower;
+
+ u32 fcoe_wwn_port_name_upper;
+ u32 fcoe_wwn_port_name_lower;
+
+ u32 fcoe_wwn_node_name_upper;
+ u32 fcoe_wwn_node_name_lower;
+
+ u32 Reserved1[49]; /* 0x1C0 */
+
+ /* Enable RJ45 magjack pair swapping on 10GBase-T PHY (0=default),
+ 84833 only */
+ u32 xgbt_phy_cfg; /* 0x284 */
+ #define PORT_HW_CFG_RJ45_PAIR_SWAP_MASK 0x000000FF
+ #define PORT_HW_CFG_RJ45_PAIR_SWAP_SHIFT 0
+
+ u32 default_cfg; /* 0x288 */
+ #define PORT_HW_CFG_GPIO0_CONFIG_MASK 0x00000003
+ #define PORT_HW_CFG_GPIO0_CONFIG_SHIFT 0
+ #define PORT_HW_CFG_GPIO0_CONFIG_NA 0x00000000
+ #define PORT_HW_CFG_GPIO0_CONFIG_LOW 0x00000001
+ #define PORT_HW_CFG_GPIO0_CONFIG_HIGH 0x00000002
+ #define PORT_HW_CFG_GPIO0_CONFIG_INPUT 0x00000003
+
+ #define PORT_HW_CFG_GPIO1_CONFIG_MASK 0x0000000C
+ #define PORT_HW_CFG_GPIO1_CONFIG_SHIFT 2
+ #define PORT_HW_CFG_GPIO1_CONFIG_NA 0x00000000
+ #define PORT_HW_CFG_GPIO1_CONFIG_LOW 0x00000004
+ #define PORT_HW_CFG_GPIO1_CONFIG_HIGH 0x00000008
+ #define PORT_HW_CFG_GPIO1_CONFIG_INPUT 0x0000000c
+
+ #define PORT_HW_CFG_GPIO2_CONFIG_MASK 0x00000030
+ #define PORT_HW_CFG_GPIO2_CONFIG_SHIFT 4
+ #define PORT_HW_CFG_GPIO2_CONFIG_NA 0x00000000
+ #define PORT_HW_CFG_GPIO2_CONFIG_LOW 0x00000010
+ #define PORT_HW_CFG_GPIO2_CONFIG_HIGH 0x00000020
+ #define PORT_HW_CFG_GPIO2_CONFIG_INPUT 0x00000030
+
+ #define PORT_HW_CFG_GPIO3_CONFIG_MASK 0x000000C0
+ #define PORT_HW_CFG_GPIO3_CONFIG_SHIFT 6
+ #define PORT_HW_CFG_GPIO3_CONFIG_NA 0x00000000
+ #define PORT_HW_CFG_GPIO3_CONFIG_LOW 0x00000040
+ #define PORT_HW_CFG_GPIO3_CONFIG_HIGH 0x00000080
+ #define PORT_HW_CFG_GPIO3_CONFIG_INPUT 0x000000c0
+
+ /* When KR link is required to be set to force which is not
+ KR-compliant, this parameter determine what is the trigger for it.
+ When GPIO is selected, low input will force the speed. Currently
+ default speed is 1G. In the future, it may be widen to select the
+ forced speed in with another parameter. Note when force-1G is
+ enabled, it override option 56: Link Speed option. */
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_MASK 0x00000F00
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_SHIFT 8
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_NOT_FORCED 0x00000000
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO0_P0 0x00000100
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO1_P0 0x00000200
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO2_P0 0x00000300
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO3_P0 0x00000400
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO0_P1 0x00000500
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO1_P1 0x00000600
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO2_P1 0x00000700
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_GPIO3_P1 0x00000800
+ #define PORT_HW_CFG_FORCE_KR_ENABLER_FORCED 0x00000900
+ /* Enable to determine with which GPIO to reset the external phy */
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_MASK 0x000F0000
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_SHIFT 16
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_PHY_TYPE 0x00000000
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO0_P0 0x00010000
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO1_P0 0x00020000
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO2_P0 0x00030000
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO3_P0 0x00040000
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO0_P1 0x00050000
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO1_P1 0x00060000
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO2_P1 0x00070000
+ #define PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO3_P1 0x00080000
+
+ /* Enable BAM on KR */
+ #define PORT_HW_CFG_ENABLE_BAM_ON_KR_MASK 0x00100000
+ #define PORT_HW_CFG_ENABLE_BAM_ON_KR_SHIFT 20
+ #define PORT_HW_CFG_ENABLE_BAM_ON_KR_DISABLED 0x00000000
+ #define PORT_HW_CFG_ENABLE_BAM_ON_KR_ENABLED 0x00100000
+
+ /* Enable Common Mode Sense */
+ #define PORT_HW_CFG_ENABLE_CMS_MASK 0x00200000
+ #define PORT_HW_CFG_ENABLE_CMS_SHIFT 21
+ #define PORT_HW_CFG_ENABLE_CMS_DISABLED 0x00000000
+ #define PORT_HW_CFG_ENABLE_CMS_ENABLED 0x00200000
+
+ /* Enable RJ45 magjack pair swapping on 10GBase-T PHY, 84833 only */
+ #define PORT_HW_CFG_RJ45_PR_SWP_MASK 0x00400000
+ #define PORT_HW_CFG_RJ45_PR_SWP_SHIFT 22
+ #define PORT_HW_CFG_RJ45_PR_SWP_DISABLED 0x00000000
+ #define PORT_HW_CFG_RJ45_PR_SWP_ENABLED 0x00400000
+
+ /* Determine the Serdes electrical interface */
+ #define PORT_HW_CFG_NET_SERDES_IF_MASK 0x0F000000
+ #define PORT_HW_CFG_NET_SERDES_IF_SHIFT 24
+ #define PORT_HW_CFG_NET_SERDES_IF_SGMII 0x00000000
+ #define PORT_HW_CFG_NET_SERDES_IF_XFI 0x01000000
+ #define PORT_HW_CFG_NET_SERDES_IF_SFI 0x02000000
+ #define PORT_HW_CFG_NET_SERDES_IF_KR 0x03000000
+ #define PORT_HW_CFG_NET_SERDES_IF_DXGXS 0x04000000
+ #define PORT_HW_CFG_NET_SERDES_IF_KR2 0x05000000
+
+
+ u32 speed_capability_mask2; /* 0x28C */
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3_MASK 0x0000FFFF
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3_SHIFT 0
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3_10M_FULL 0x00000001
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3__ 0x00000002
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3___ 0x00000004
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3_100M_FULL 0x00000008
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3_1G 0x00000010
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3_2_DOT_5G 0x00000020
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3_10G 0x00000040
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3_20G 0x00000080
+
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D0_MASK 0xFFFF0000
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D0_SHIFT 16
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D0_10M_FULL 0x00010000
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D0__ 0x00020000
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D0___ 0x00040000
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D0_100M_FULL 0x00080000
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D0_1G 0x00100000
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D0_2_DOT_5G 0x00200000
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D0_10G 0x00400000
+ #define PORT_HW_CFG_SPEED_CAPABILITY2_D0_20G 0x00800000
+
+
+ /* In the case where two media types (e.g. copper and fiber) are
+ present and electrically active at the same time, PHY Selection
+ will determine which of the two PHYs will be designated as the
+ Active PHY and used for a connection to the network. */
+ u32 multi_phy_config; /* 0x290 */
+ #define PORT_HW_CFG_PHY_SELECTION_MASK 0x00000007
+ #define PORT_HW_CFG_PHY_SELECTION_SHIFT 0
+ #define PORT_HW_CFG_PHY_SELECTION_HARDWARE_DEFAULT 0x00000000
+ #define PORT_HW_CFG_PHY_SELECTION_FIRST_PHY 0x00000001
+ #define PORT_HW_CFG_PHY_SELECTION_SECOND_PHY 0x00000002
+ #define PORT_HW_CFG_PHY_SELECTION_FIRST_PHY_PRIORITY 0x00000003
+ #define PORT_HW_CFG_PHY_SELECTION_SECOND_PHY_PRIORITY 0x00000004
+
+ /* When enabled, all second phy nvram parameters will be swapped
+ with the first phy parameters */
+ #define PORT_HW_CFG_PHY_SWAPPED_MASK 0x00000008
+ #define PORT_HW_CFG_PHY_SWAPPED_SHIFT 3
+ #define PORT_HW_CFG_PHY_SWAPPED_DISABLED 0x00000000
+ #define PORT_HW_CFG_PHY_SWAPPED_ENABLED 0x00000008
+
+
+ /* Address of the second external phy */
+ u32 external_phy_config2; /* 0x294 */
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_ADDR_MASK 0x000000FF
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_ADDR_SHIFT 0
+
+ /* The second XGXS external PHY type */
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_MASK 0x0000FF00
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_SHIFT 8
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_DIRECT 0x00000000
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8071 0x00000100
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8072 0x00000200
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8073 0x00000300
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8705 0x00000400
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8706 0x00000500
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8726 0x00000600
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8481 0x00000700
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_SFX7101 0x00000800
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8727 0x00000900
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8727_NOC 0x00000a00
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM84823 0x00000b00
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM54640 0x00000c00
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM84833 0x00000d00
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM54618SE 0x00000e00
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8722 0x00000f00
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM54616 0x00001000
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_FAILURE 0x0000fd00
+ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_NOT_CONN 0x0000ff00
+
+
+ /* 4 times 16 bits for all 4 lanes. For some external PHYs (such as
+ 8706, 8726 and 8727) not all 4 values are needed. */
+ u16 xgxs_config2_rx[4]; /* 0x296 */
+ u16 xgxs_config2_tx[4]; /* 0x2A0 */
+
+ u32 lane_config;
+ #define PORT_HW_CFG_LANE_SWAP_CFG_MASK 0x0000ffff
+ #define PORT_HW_CFG_LANE_SWAP_CFG_SHIFT 0
+ /* AN and forced */
+ #define PORT_HW_CFG_LANE_SWAP_CFG_01230123 0x00001b1b
+ /* forced only */
+ #define PORT_HW_CFG_LANE_SWAP_CFG_01233210 0x00001be4
+ /* forced only */
+ #define PORT_HW_CFG_LANE_SWAP_CFG_31203120 0x0000d8d8
+ /* forced only */
+ #define PORT_HW_CFG_LANE_SWAP_CFG_32103210 0x0000e4e4
+ #define PORT_HW_CFG_LANE_SWAP_CFG_TX_MASK 0x000000ff
+ #define PORT_HW_CFG_LANE_SWAP_CFG_TX_SHIFT 0
+ #define PORT_HW_CFG_LANE_SWAP_CFG_RX_MASK 0x0000ff00
+ #define PORT_HW_CFG_LANE_SWAP_CFG_RX_SHIFT 8
+ #define PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK 0x0000c000
+ #define PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT 14
+
+ /* Indicate whether to swap the external phy polarity */
+ #define PORT_HW_CFG_SWAP_PHY_POLARITY_MASK 0x00010000
+ #define PORT_HW_CFG_SWAP_PHY_POLARITY_DISABLED 0x00000000
+ #define PORT_HW_CFG_SWAP_PHY_POLARITY_ENABLED 0x00010000
+
+
+ u32 external_phy_config;
+ #define PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK 0x000000ff
+ #define PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT 0
+
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_MASK 0x0000ff00
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SHIFT 8
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT 0x00000000
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8071 0x00000100
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072 0x00000200
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073 0x00000300
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705 0x00000400
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706 0x00000500
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726 0x00000600
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481 0x00000700
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101 0x00000800
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727 0x00000900
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727_NOC 0x00000a00
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823 0x00000b00
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54640 0x00000c00
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833 0x00000d00
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE 0x00000e00
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8722 0x00000f00
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54616 0x00001000
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT_WC 0x0000fc00
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE 0x0000fd00
+ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN 0x0000ff00
+
+ #define PORT_HW_CFG_SERDES_EXT_PHY_ADDR_MASK 0x00ff0000
+ #define PORT_HW_CFG_SERDES_EXT_PHY_ADDR_SHIFT 16
+
+ #define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_MASK 0xff000000
+ #define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_SHIFT 24
+ #define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT 0x00000000
+ #define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482 0x01000000
+ #define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT_SD 0x02000000
+ #define PORT_HW_CFG_SERDES_EXT_PHY_TYPE_NOT_CONN 0xff000000
+
+ u32 speed_capability_mask;
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_MASK 0x0000ffff
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_SHIFT 0
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_10M_FULL 0x00000001
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_10M_HALF 0x00000002
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_100M_HALF 0x00000004
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_100M_FULL 0x00000008
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_1G 0x00000010
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_2_5G 0x00000020
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_10G 0x00000040
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_20G 0x00000080
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D3_RESERVED 0x0000f000
+
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_MASK 0xffff0000
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_SHIFT 16
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL 0x00010000
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF 0x00020000
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF 0x00040000
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL 0x00080000
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_1G 0x00100000
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G 0x00200000
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_10G 0x00400000
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_20G 0x00800000
+ #define PORT_HW_CFG_SPEED_CAPABILITY_D0_RESERVED 0xf0000000
+
+ /* A place to hold the original MAC address as a backup */
+ u32 backup_mac_upper; /* 0x2B4 */
+ u32 backup_mac_lower; /* 0x2B8 */
+
+};
+
+
+/****************************************************************************
+ * Shared Feature configuration *
+ ****************************************************************************/
+struct shared_feat_cfg { /* NVRAM Offset */
+
+ u32 config; /* 0x450 */
+ #define SHARED_FEATURE_BMC_ECHO_MODE_EN 0x00000001
+
+ /* Use NVRAM values instead of HW default values */
+ #define SHARED_FEAT_CFG_OVERRIDE_PREEMPHASIS_CFG_MASK \
+ 0x00000002
+ #define SHARED_FEAT_CFG_OVERRIDE_PREEMPHASIS_CFG_DISABLED \
+ 0x00000000
+ #define SHARED_FEAT_CFG_OVERRIDE_PREEMPHASIS_CFG_ENABLED \
+ 0x00000002
+
+ #define SHARED_FEAT_CFG_NCSI_ID_METHOD_MASK 0x00000008
+ #define SHARED_FEAT_CFG_NCSI_ID_METHOD_SPIO 0x00000000
+ #define SHARED_FEAT_CFG_NCSI_ID_METHOD_NVRAM 0x00000008
+
+ #define SHARED_FEAT_CFG_NCSI_ID_MASK 0x00000030
+ #define SHARED_FEAT_CFG_NCSI_ID_SHIFT 4
+
+ /* Override the OTP back to single function mode. When using GPIO,
+ high means only SF, 0 is according to CLP configuration */
+ #define SHARED_FEAT_CFG_FORCE_SF_MODE_MASK 0x00000700
+ #define SHARED_FEAT_CFG_FORCE_SF_MODE_SHIFT 8
+ #define SHARED_FEAT_CFG_FORCE_SF_MODE_MF_ALLOWED 0x00000000
+ #define SHARED_FEAT_CFG_FORCE_SF_MODE_FORCED_SF 0x00000100
+ #define SHARED_FEAT_CFG_FORCE_SF_MODE_SPIO4 0x00000200
+ #define SHARED_FEAT_CFG_FORCE_SF_MODE_SWITCH_INDEPT 0x00000300
+
+ /* The interval in seconds between sending LLDP packets. Set to zero
+ to disable the feature */
+ #define SHARED_FEAT_CFG_LLDP_XMIT_INTERVAL_MASK 0x00ff0000
+ #define SHARED_FEAT_CFG_LLDP_XMIT_INTERVAL_SHIFT 16
+
+ /* The assigned device type ID for LLDP usage */
+ #define SHARED_FEAT_CFG_LLDP_DEVICE_TYPE_ID_MASK 0xff000000
+ #define SHARED_FEAT_CFG_LLDP_DEVICE_TYPE_ID_SHIFT 24
+
+};
+
+
+/****************************************************************************
+ * Port Feature configuration *
+ ****************************************************************************/
+struct port_feat_cfg { /* port 0: 0x454 port 1: 0x4c8 */
+
+ u32 config;
+ #define PORT_FEATURE_BAR1_SIZE_MASK 0x0000000f
+ #define PORT_FEATURE_BAR1_SIZE_SHIFT 0
+ #define PORT_FEATURE_BAR1_SIZE_DISABLED 0x00000000
+ #define PORT_FEATURE_BAR1_SIZE_64K 0x00000001
+ #define PORT_FEATURE_BAR1_SIZE_128K 0x00000002
+ #define PORT_FEATURE_BAR1_SIZE_256K 0x00000003
+ #define PORT_FEATURE_BAR1_SIZE_512K 0x00000004
+ #define PORT_FEATURE_BAR1_SIZE_1M 0x00000005
+ #define PORT_FEATURE_BAR1_SIZE_2M 0x00000006
+ #define PORT_FEATURE_BAR1_SIZE_4M 0x00000007
+ #define PORT_FEATURE_BAR1_SIZE_8M 0x00000008
+ #define PORT_FEATURE_BAR1_SIZE_16M 0x00000009
+ #define PORT_FEATURE_BAR1_SIZE_32M 0x0000000a
+ #define PORT_FEATURE_BAR1_SIZE_64M 0x0000000b
+ #define PORT_FEATURE_BAR1_SIZE_128M 0x0000000c
+ #define PORT_FEATURE_BAR1_SIZE_256M 0x0000000d
+ #define PORT_FEATURE_BAR1_SIZE_512M 0x0000000e
+ #define PORT_FEATURE_BAR1_SIZE_1G 0x0000000f
+ #define PORT_FEATURE_BAR2_SIZE_MASK 0x000000f0
+ #define PORT_FEATURE_BAR2_SIZE_SHIFT 4
+ #define PORT_FEATURE_BAR2_SIZE_DISABLED 0x00000000
+ #define PORT_FEATURE_BAR2_SIZE_64K 0x00000010
+ #define PORT_FEATURE_BAR2_SIZE_128K 0x00000020
+ #define PORT_FEATURE_BAR2_SIZE_256K 0x00000030
+ #define PORT_FEATURE_BAR2_SIZE_512K 0x00000040
+ #define PORT_FEATURE_BAR2_SIZE_1M 0x00000050
+ #define PORT_FEATURE_BAR2_SIZE_2M 0x00000060
+ #define PORT_FEATURE_BAR2_SIZE_4M 0x00000070
+ #define PORT_FEATURE_BAR2_SIZE_8M 0x00000080
+ #define PORT_FEATURE_BAR2_SIZE_16M 0x00000090
+ #define PORT_FEATURE_BAR2_SIZE_32M 0x000000a0
+ #define PORT_FEATURE_BAR2_SIZE_64M 0x000000b0
+ #define PORT_FEATURE_BAR2_SIZE_128M 0x000000c0
+ #define PORT_FEATURE_BAR2_SIZE_256M 0x000000d0
+ #define PORT_FEATURE_BAR2_SIZE_512M 0x000000e0
+ #define PORT_FEATURE_BAR2_SIZE_1G 0x000000f0
+
+ #define PORT_FEAT_CFG_DCBX_MASK 0x00000100
+ #define PORT_FEAT_CFG_DCBX_DISABLED 0x00000000
+ #define PORT_FEAT_CFG_DCBX_ENABLED 0x00000100
+
+ #define PORT_FEAT_CFG_AUTOGREEN_MASK 0x00000200
+ #define PORT_FEAT_CFG_AUTOGREEN_SHIFT 9
+ #define PORT_FEAT_CFG_AUTOGREEN_DISABLED 0x00000000
+ #define PORT_FEAT_CFG_AUTOGREEN_ENABLED 0x00000200
+
+ #define PORT_FEATURE_EN_SIZE_MASK 0x0f000000
+ #define PORT_FEATURE_EN_SIZE_SHIFT 24
+ #define PORT_FEATURE_WOL_ENABLED 0x01000000
+ #define PORT_FEATURE_MBA_ENABLED 0x02000000
+ #define PORT_FEATURE_MFW_ENABLED 0x04000000
+
+ /* Advertise expansion ROM even if MBA is disabled */
+ #define PORT_FEAT_CFG_FORCE_EXP_ROM_ADV_MASK 0x08000000
+ #define PORT_FEAT_CFG_FORCE_EXP_ROM_ADV_DISABLED 0x00000000
+ #define PORT_FEAT_CFG_FORCE_EXP_ROM_ADV_ENABLED 0x08000000
+
+ /* Check the optic vendor via i2c against a list of approved modules
+ in a separate nvram image */
+ #define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK 0xe0000000
+ #define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_SHIFT 29
+ #define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_NO_ENFORCEMENT \
+ 0x00000000
+ #define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER \
+ 0x20000000
+ #define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_WARNING_MSG 0x40000000
+ #define PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_POWER_DOWN 0x60000000
+
+ u32 wol_config;
+ /* Default is used when driver sets to "auto" mode */
+ #define PORT_FEATURE_WOL_DEFAULT_MASK 0x00000003
+ #define PORT_FEATURE_WOL_DEFAULT_SHIFT 0
+ #define PORT_FEATURE_WOL_DEFAULT_DISABLE 0x00000000
+ #define PORT_FEATURE_WOL_DEFAULT_MAGIC 0x00000001
+ #define PORT_FEATURE_WOL_DEFAULT_ACPI 0x00000002
+ #define PORT_FEATURE_WOL_DEFAULT_MAGIC_AND_ACPI 0x00000003
+ #define PORT_FEATURE_WOL_RES_PAUSE_CAP 0x00000004
+ #define PORT_FEATURE_WOL_RES_ASYM_PAUSE_CAP 0x00000008
+ #define PORT_FEATURE_WOL_ACPI_UPON_MGMT 0x00000010
+
+ u32 mba_config;
+ #define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_MASK 0x00000007
+ #define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_SHIFT 0
+ #define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_PXE 0x00000000
+ #define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_RPL 0x00000001
+ #define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_BOOTP 0x00000002
+ #define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_ISCSIB 0x00000003
+ #define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_FCOE_BOOT 0x00000004
+ #define PORT_FEATURE_MBA_BOOT_AGENT_TYPE_NONE 0x00000007
+
+ #define PORT_FEATURE_MBA_BOOT_RETRY_MASK 0x00000038
+ #define PORT_FEATURE_MBA_BOOT_RETRY_SHIFT 3
+
+ #define PORT_FEATURE_MBA_RES_PAUSE_CAP 0x00000100
+ #define PORT_FEATURE_MBA_RES_ASYM_PAUSE_CAP 0x00000200
+ #define PORT_FEATURE_MBA_SETUP_PROMPT_ENABLE 0x00000400
+ #define PORT_FEATURE_MBA_HOTKEY_MASK 0x00000800
+ #define PORT_FEATURE_MBA_HOTKEY_CTRL_S 0x00000000
+ #define PORT_FEATURE_MBA_HOTKEY_CTRL_B 0x00000800
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_MASK 0x000ff000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_SHIFT 12
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_DISABLED 0x00000000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_2K 0x00001000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_4K 0x00002000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_8K 0x00003000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_16K 0x00004000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_32K 0x00005000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_64K 0x00006000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_128K 0x00007000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_256K 0x00008000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_512K 0x00009000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_1M 0x0000a000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_2M 0x0000b000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_4M 0x0000c000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_8M 0x0000d000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_16M 0x0000e000
+ #define PORT_FEATURE_MBA_EXP_ROM_SIZE_32M 0x0000f000
+ #define PORT_FEATURE_MBA_MSG_TIMEOUT_MASK 0x00f00000
+ #define PORT_FEATURE_MBA_MSG_TIMEOUT_SHIFT 20
+ #define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_MASK 0x03000000
+ #define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_SHIFT 24
+ #define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_AUTO 0x00000000
+ #define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_BBS 0x01000000
+ #define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_INT18H 0x02000000
+ #define PORT_FEATURE_MBA_BIOS_BOOTSTRAP_INT19H 0x03000000
+ #define PORT_FEATURE_MBA_LINK_SPEED_MASK 0x3c000000
+ #define PORT_FEATURE_MBA_LINK_SPEED_SHIFT 26
+ #define PORT_FEATURE_MBA_LINK_SPEED_AUTO 0x00000000
+ #define PORT_FEATURE_MBA_LINK_SPEED_10HD 0x04000000
+ #define PORT_FEATURE_MBA_LINK_SPEED_10FD 0x08000000
+ #define PORT_FEATURE_MBA_LINK_SPEED_100HD 0x0c000000
+ #define PORT_FEATURE_MBA_LINK_SPEED_100FD 0x10000000
+ #define PORT_FEATURE_MBA_LINK_SPEED_1GBPS 0x14000000
+ #define PORT_FEATURE_MBA_LINK_SPEED_2_5GBPS 0x18000000
+ #define PORT_FEATURE_MBA_LINK_SPEED_10GBPS_CX4 0x1c000000
+ #define PORT_FEATURE_MBA_LINK_SPEED_20GBPS 0x20000000
+ u32 bmc_config;
+ #define PORT_FEATURE_BMC_LINK_OVERRIDE_MASK 0x00000001
+ #define PORT_FEATURE_BMC_LINK_OVERRIDE_DEFAULT 0x00000000
+ #define PORT_FEATURE_BMC_LINK_OVERRIDE_EN 0x00000001
+
+ u32 mba_vlan_cfg;
+ #define PORT_FEATURE_MBA_VLAN_TAG_MASK 0x0000ffff
+ #define PORT_FEATURE_MBA_VLAN_TAG_SHIFT 0
+ #define PORT_FEATURE_MBA_VLAN_EN 0x00010000
+
+ u32 resource_cfg;
+ #define PORT_FEATURE_RESOURCE_CFG_VALID 0x00000001
+ #define PORT_FEATURE_RESOURCE_CFG_DIAG 0x00000002
+ #define PORT_FEATURE_RESOURCE_CFG_L2 0x00000004
+ #define PORT_FEATURE_RESOURCE_CFG_ISCSI 0x00000008
+ #define PORT_FEATURE_RESOURCE_CFG_RDMA 0x00000010
+
+ u32 smbus_config;
+ #define PORT_FEATURE_SMBUS_ADDR_MASK 0x000000fe
+ #define PORT_FEATURE_SMBUS_ADDR_SHIFT 1
+
+ u32 vf_config;
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_MASK 0x0000000f
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_SHIFT 0
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_DISABLED 0x00000000
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_4K 0x00000001
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_8K 0x00000002
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_16K 0x00000003
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_32K 0x00000004
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_64K 0x00000005
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_128K 0x00000006
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_256K 0x00000007
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_512K 0x00000008
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_1M 0x00000009
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_2M 0x0000000a
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_4M 0x0000000b
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_8M 0x0000000c
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_16M 0x0000000d
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_32M 0x0000000e
+ #define PORT_FEAT_CFG_VF_BAR2_SIZE_64M 0x0000000f
+
+ u32 link_config; /* Used as HW defaults for the driver */
+ #define PORT_FEATURE_CONNECTED_SWITCH_MASK 0x03000000
+ #define PORT_FEATURE_CONNECTED_SWITCH_SHIFT 24
+ /* (forced) low speed switch (< 10G) */
+ #define PORT_FEATURE_CON_SWITCH_1G_SWITCH 0x00000000
+ /* (forced) high speed switch (>= 10G) */
+ #define PORT_FEATURE_CON_SWITCH_10G_SWITCH 0x01000000
+ #define PORT_FEATURE_CON_SWITCH_AUTO_DETECT 0x02000000
+ #define PORT_FEATURE_CON_SWITCH_ONE_TIME_DETECT 0x03000000
+
+ #define PORT_FEATURE_LINK_SPEED_MASK 0x000f0000
+ #define PORT_FEATURE_LINK_SPEED_SHIFT 16
+ #define PORT_FEATURE_LINK_SPEED_AUTO 0x00000000
+ #define PORT_FEATURE_LINK_SPEED_10M_FULL 0x00010000
+ #define PORT_FEATURE_LINK_SPEED_10M_HALF 0x00020000
+ #define PORT_FEATURE_LINK_SPEED_100M_HALF 0x00030000
+ #define PORT_FEATURE_LINK_SPEED_100M_FULL 0x00040000
+ #define PORT_FEATURE_LINK_SPEED_1G 0x00050000
+ #define PORT_FEATURE_LINK_SPEED_2_5G 0x00060000
+ #define PORT_FEATURE_LINK_SPEED_10G_CX4 0x00070000
+ #define PORT_FEATURE_LINK_SPEED_20G 0x00080000
+
+ #define PORT_FEATURE_FLOW_CONTROL_MASK 0x00000700
+ #define PORT_FEATURE_FLOW_CONTROL_SHIFT 8
+ #define PORT_FEATURE_FLOW_CONTROL_AUTO 0x00000000
+ #define PORT_FEATURE_FLOW_CONTROL_TX 0x00000100
+ #define PORT_FEATURE_FLOW_CONTROL_RX 0x00000200
+ #define PORT_FEATURE_FLOW_CONTROL_BOTH 0x00000300
+ #define PORT_FEATURE_FLOW_CONTROL_NONE 0x00000400
+
+ /* The default for MCP link configuration,
+ uses the same defines as link_config */
+ u32 mfw_wol_link_cfg;
+
+ /* The default for the driver of the second external phy,
+ uses the same defines as link_config */
+ u32 link_config2; /* 0x47C */
+
+ /* The default for MCP of the second external phy,
+ uses the same defines as link_config */
+ u32 mfw_wol_link_cfg2; /* 0x480 */
+
+ u32 Reserved2[17]; /* 0x484 */
+
+};
+
+
+/****************************************************************************
+ * Device Information *
+ ****************************************************************************/
+struct shm_dev_info { /* size */
+
+ u32 bc_rev; /* 8 bits each: major, minor, build */ /* 4 */
+
+ struct shared_hw_cfg shared_hw_config; /* 40 */
+
+ struct port_hw_cfg port_hw_config[PORT_MAX]; /* 400*2=800 */
+
+ struct shared_feat_cfg shared_feature_config; /* 4 */
+
+ struct port_feat_cfg port_feature_config[PORT_MAX];/* 116*2=232 */
+
+};
+
+
+#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
+ #error "Missing either LITTLE_ENDIAN or BIG_ENDIAN definition."
+#endif
+
+#define FUNC_0 0
+#define FUNC_1 1
+#define FUNC_2 2
+#define FUNC_3 3
+#define FUNC_4 4
+#define FUNC_5 5
+#define FUNC_6 6
+#define FUNC_7 7
+#define E1_FUNC_MAX 2
+#define E1H_FUNC_MAX 8
+#define E2_FUNC_MAX 4 /* per path */
+
+#define VN_0 0
+#define VN_1 1
+#define VN_2 2
+#define VN_3 3
+#define E1VN_MAX 1
+#define E1HVN_MAX 4
+
+#define E2_VF_MAX 64 /* HC_REG_VF_CONFIGURATION_SIZE */
+/* This value (in milliseconds) determines the frequency of the driver
+ * issuing the PULSE message code. The firmware monitors this periodic
+ * pulse to determine when to switch to an OS-absent mode. */
+#define DRV_PULSE_PERIOD_MS 250
+
+/* This value (in milliseconds) determines how long the driver should
+ * wait for an acknowledgement from the firmware before timing out. Once
+ * the firmware has timed out, the driver will assume there is no firmware
+ * running and there won't be any firmware-driver synchronization during a
+ * driver reset. */
+#define FW_ACK_TIME_OUT_MS 5000
+
+#define FW_ACK_POLL_TIME_MS 1
+
+#define FW_ACK_NUM_OF_POLL (FW_ACK_TIME_OUT_MS/FW_ACK_POLL_TIME_MS)
+
+/* LED Blink rate that will achieve ~15.9Hz */
+#define LED_BLINK_RATE_VAL 480
+
+/****************************************************************************
+ * Driver <-> FW Mailbox *
+ ****************************************************************************/
+struct drv_port_mb {
+
+ u32 link_status;
+ /* Driver should update this field on any link change event */
+
+ #define LINK_STATUS_LINK_FLAG_MASK 0x00000001
+ #define LINK_STATUS_LINK_UP 0x00000001
+ #define LINK_STATUS_SPEED_AND_DUPLEX_MASK 0x0000001E
+ #define LINK_STATUS_SPEED_AND_DUPLEX_AN_NOT_COMPLETE (0<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_10THD (1<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_10TFD (2<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_100TXHD (3<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_100T4 (4<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_100TXFD (5<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_1000THD (6<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_1000TFD (7<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_1000XFD (7<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_2500THD (8<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_2500TFD (9<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_2500XFD (9<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_10GTFD (10<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_10GXFD (10<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_20GTFD (11<<1)
+ #define LINK_STATUS_SPEED_AND_DUPLEX_20GXFD (11<<1)
+
+ #define LINK_STATUS_AUTO_NEGOTIATE_FLAG_MASK 0x00000020
+ #define LINK_STATUS_AUTO_NEGOTIATE_ENABLED 0x00000020
+
+ #define LINK_STATUS_AUTO_NEGOTIATE_COMPLETE 0x00000040
+ #define LINK_STATUS_PARALLEL_DETECTION_FLAG_MASK 0x00000080
+ #define LINK_STATUS_PARALLEL_DETECTION_USED 0x00000080
+
+ #define LINK_STATUS_LINK_PARTNER_1000TFD_CAPABLE 0x00000200
+ #define LINK_STATUS_LINK_PARTNER_1000THD_CAPABLE 0x00000400
+ #define LINK_STATUS_LINK_PARTNER_100T4_CAPABLE 0x00000800
+ #define LINK_STATUS_LINK_PARTNER_100TXFD_CAPABLE 0x00001000
+ #define LINK_STATUS_LINK_PARTNER_100TXHD_CAPABLE 0x00002000
+ #define LINK_STATUS_LINK_PARTNER_10TFD_CAPABLE 0x00004000
+ #define LINK_STATUS_LINK_PARTNER_10THD_CAPABLE 0x00008000
+
+ #define LINK_STATUS_TX_FLOW_CONTROL_FLAG_MASK 0x00010000
+ #define LINK_STATUS_TX_FLOW_CONTROL_ENABLED 0x00010000
+
+ #define LINK_STATUS_RX_FLOW_CONTROL_FLAG_MASK 0x00020000
+ #define LINK_STATUS_RX_FLOW_CONTROL_ENABLED 0x00020000
+
+ #define LINK_STATUS_LINK_PARTNER_FLOW_CONTROL_MASK 0x000C0000
+ #define LINK_STATUS_LINK_PARTNER_NOT_PAUSE_CAPABLE (0<<18)
+ #define LINK_STATUS_LINK_PARTNER_SYMMETRIC_PAUSE (1<<18)
+ #define LINK_STATUS_LINK_PARTNER_ASYMMETRIC_PAUSE (2<<18)
+ #define LINK_STATUS_LINK_PARTNER_BOTH_PAUSE (3<<18)
+
+ #define LINK_STATUS_SERDES_LINK 0x00100000
+
+ #define LINK_STATUS_LINK_PARTNER_2500XFD_CAPABLE 0x00200000
+ #define LINK_STATUS_LINK_PARTNER_2500XHD_CAPABLE 0x00400000
+ #define LINK_STATUS_LINK_PARTNER_10GXFD_CAPABLE 0x00800000
+ #define LINK_STATUS_LINK_PARTNER_20GXFD_CAPABLE 0x10000000
+
+ #define LINK_STATUS_PFC_ENABLED 0x20000000
+
+ #define LINK_STATUS_PHYSICAL_LINK_FLAG 0x40000000
+
+ u32 port_stx;
+
+ u32 stat_nig_timer;
+
+ /* MCP firmware does not use this field */
+ u32 ext_phy_fw_version;
+
+};
+
+
+struct drv_func_mb {
+
+ u32 drv_mb_header;
+ #define DRV_MSG_CODE_MASK 0xffff0000
+ #define DRV_MSG_CODE_LOAD_REQ 0x10000000
+ #define DRV_MSG_CODE_LOAD_DONE 0x11000000
+ #define DRV_MSG_CODE_UNLOAD_REQ_WOL_EN 0x20000000
+ #define DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS 0x20010000
+ #define DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP 0x20020000
+ #define DRV_MSG_CODE_UNLOAD_DONE 0x21000000
+ #define DRV_MSG_CODE_DCC_OK 0x30000000
+ #define DRV_MSG_CODE_DCC_FAILURE 0x31000000
+ #define DRV_MSG_CODE_DIAG_ENTER_REQ 0x50000000
+ #define DRV_MSG_CODE_DIAG_EXIT_REQ 0x60000000
+ #define DRV_MSG_CODE_VALIDATE_KEY 0x70000000
+ #define DRV_MSG_CODE_GET_CURR_KEY 0x80000000
+ #define DRV_MSG_CODE_GET_UPGRADE_KEY 0x81000000
+ #define DRV_MSG_CODE_GET_MANUF_KEY 0x82000000
+ #define DRV_MSG_CODE_LOAD_L2B_PRAM 0x90000000
+ /*
+ * The optic module verification command requires bootcode
+ * v5.0.6 or later, te specific optic module verification command
+ * requires bootcode v5.2.12 or later
+ */
+ #define DRV_MSG_CODE_VRFY_FIRST_PHY_OPT_MDL 0xa0000000
+ #define REQ_BC_VER_4_VRFY_FIRST_PHY_OPT_MDL 0x00050006
+ #define DRV_MSG_CODE_VRFY_SPECIFIC_PHY_OPT_MDL 0xa1000000
+ #define REQ_BC_VER_4_VRFY_SPECIFIC_PHY_OPT_MDL 0x00050234
+ #define REQ_BC_VER_4_SFP_TX_DISABLE_SUPPORTED 0x00070014
+
+ #define DRV_MSG_CODE_DCBX_ADMIN_PMF_MSG 0xb0000000
+ #define DRV_MSG_CODE_DCBX_PMF_DRV_OK 0xb2000000
+
+ #define DRV_MSG_CODE_VF_DISABLED_DONE 0xc0000000
+
+ #define DRV_MSG_CODE_SET_MF_BW 0xe0000000
+ #define REQ_BC_VER_4_SET_MF_BW 0x00060202
+ #define DRV_MSG_CODE_SET_MF_BW_ACK 0xe1000000
+
+ #define DRV_MSG_CODE_LINK_STATUS_CHANGED 0x01000000
+
+ #define BIOS_MSG_CODE_LIC_CHALLENGE 0xff010000
+ #define BIOS_MSG_CODE_LIC_RESPONSE 0xff020000
+ #define BIOS_MSG_CODE_VIRT_MAC_PRIM 0xff030000
+ #define BIOS_MSG_CODE_VIRT_MAC_ISCSI 0xff040000
+
+ #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff
+
+ u32 drv_mb_param;
+ #define DRV_MSG_CODE_SET_MF_BW_MIN_MASK 0x00ff0000
+ #define DRV_MSG_CODE_SET_MF_BW_MAX_MASK 0xff000000
+
+ u32 fw_mb_header;
+ #define FW_MSG_CODE_MASK 0xffff0000
+ #define FW_MSG_CODE_DRV_LOAD_COMMON 0x10100000
+ #define FW_MSG_CODE_DRV_LOAD_PORT 0x10110000
+ #define FW_MSG_CODE_DRV_LOAD_FUNCTION 0x10120000
+ /* Load common chip is supported from bc 6.0.0 */
+ #define REQ_BC_VER_4_DRV_LOAD_COMMON_CHIP 0x00060000
+ #define FW_MSG_CODE_DRV_LOAD_COMMON_CHIP 0x10130000
+
+ #define FW_MSG_CODE_DRV_LOAD_REFUSED 0x10200000
+ #define FW_MSG_CODE_DRV_LOAD_DONE 0x11100000
+ #define FW_MSG_CODE_DRV_UNLOAD_COMMON 0x20100000
+ #define FW_MSG_CODE_DRV_UNLOAD_PORT 0x20110000
+ #define FW_MSG_CODE_DRV_UNLOAD_FUNCTION 0x20120000
+ #define FW_MSG_CODE_DRV_UNLOAD_DONE 0x21100000
+ #define FW_MSG_CODE_DCC_DONE 0x30100000
+ #define FW_MSG_CODE_LLDP_DONE 0x40100000
+ #define FW_MSG_CODE_DIAG_ENTER_DONE 0x50100000
+ #define FW_MSG_CODE_DIAG_REFUSE 0x50200000
+ #define FW_MSG_CODE_DIAG_EXIT_DONE 0x60100000
+ #define FW_MSG_CODE_VALIDATE_KEY_SUCCESS 0x70100000
+ #define FW_MSG_CODE_VALIDATE_KEY_FAILURE 0x70200000
+ #define FW_MSG_CODE_GET_KEY_DONE 0x80100000
+ #define FW_MSG_CODE_NO_KEY 0x80f00000
+ #define FW_MSG_CODE_LIC_INFO_NOT_READY 0x80f80000
+ #define FW_MSG_CODE_L2B_PRAM_LOADED 0x90100000
+ #define FW_MSG_CODE_L2B_PRAM_T_LOAD_FAILURE 0x90210000
+ #define FW_MSG_CODE_L2B_PRAM_C_LOAD_FAILURE 0x90220000
+ #define FW_MSG_CODE_L2B_PRAM_X_LOAD_FAILURE 0x90230000
+ #define FW_MSG_CODE_L2B_PRAM_U_LOAD_FAILURE 0x90240000
+ #define FW_MSG_CODE_VRFY_OPT_MDL_SUCCESS 0xa0100000
+ #define FW_MSG_CODE_VRFY_OPT_MDL_INVLD_IMG 0xa0200000
+ #define FW_MSG_CODE_VRFY_OPT_MDL_UNAPPROVED 0xa0300000
+ #define FW_MSG_CODE_VF_DISABLED_DONE 0xb0000000
+
+ #define FW_MSG_CODE_SET_MF_BW_SENT 0xe0000000
+ #define FW_MSG_CODE_SET_MF_BW_DONE 0xe1000000
+
+ #define FW_MSG_CODE_LINK_CHANGED_ACK 0x01100000
+
+ #define FW_MSG_CODE_LIC_CHALLENGE 0xff010000
+ #define FW_MSG_CODE_LIC_RESPONSE 0xff020000
+ #define FW_MSG_CODE_VIRT_MAC_PRIM 0xff030000
+ #define FW_MSG_CODE_VIRT_MAC_ISCSI 0xff040000
+
+ #define FW_MSG_SEQ_NUMBER_MASK 0x0000ffff
+
+ u32 fw_mb_param;
+
+ u32 drv_pulse_mb;
+ #define DRV_PULSE_SEQ_MASK 0x00007fff
+ #define DRV_PULSE_SYSTEM_TIME_MASK 0xffff0000
+ /*
+ * The system time is in the format of
+ * (year-2001)*12*32 + month*32 + day.
+ */
+ #define DRV_PULSE_ALWAYS_ALIVE 0x00008000
+ /*
+ * Indicate to the firmware not to go into the
+ * OS-absent when it is not getting driver pulse.
+ * This is used for debugging as well for PXE(MBA).
+ */
+
+ u32 mcp_pulse_mb;
+ #define MCP_PULSE_SEQ_MASK 0x00007fff
+ #define MCP_PULSE_ALWAYS_ALIVE 0x00008000
+ /* Indicates to the driver not to assert due to lack
+ * of MCP response */
+ #define MCP_EVENT_MASK 0xffff0000
+ #define MCP_EVENT_OTHER_DRIVER_RESET_REQ 0x00010000
+
+ u32 iscsi_boot_signature;
+ u32 iscsi_boot_block_offset;
+
+ u32 drv_status;
+ #define DRV_STATUS_PMF 0x00000001
+ #define DRV_STATUS_VF_DISABLED 0x00000002
+ #define DRV_STATUS_SET_MF_BW 0x00000004
+ #define DRV_STATUS_LINK_EVENT 0x00000008
+
+ #define DRV_STATUS_DCC_EVENT_MASK 0x0000ff00
+ #define DRV_STATUS_DCC_DISABLE_ENABLE_PF 0x00000100
+ #define DRV_STATUS_DCC_BANDWIDTH_ALLOCATION 0x00000200
+ #define DRV_STATUS_DCC_CHANGE_MAC_ADDRESS 0x00000400
+ #define DRV_STATUS_DCC_RESERVED1 0x00000800
+ #define DRV_STATUS_DCC_SET_PROTOCOL 0x00001000
+ #define DRV_STATUS_DCC_SET_PRIORITY 0x00002000
+
+ #define DRV_STATUS_DCBX_EVENT_MASK 0x000f0000
+ #define DRV_STATUS_DCBX_NEGOTIATION_RESULTS 0x00010000
+
+ u32 virt_mac_upper;
+ #define VIRT_MAC_SIGN_MASK 0xffff0000
+ #define VIRT_MAC_SIGNATURE 0x564d0000
+ u32 virt_mac_lower;
+
+};
+
+
+/****************************************************************************
+ * Management firmware state *
+ ****************************************************************************/
+/* Allocate 440 bytes for management firmware */
+#define MGMTFW_STATE_WORD_SIZE 110
+
+struct mgmtfw_state {
+ u32 opaque[MGMTFW_STATE_WORD_SIZE];
+};
+
+
+/****************************************************************************
+ * Multi-Function configuration *
+ ****************************************************************************/
+struct shared_mf_cfg {
+
+ u32 clp_mb;
+ #define SHARED_MF_CLP_SET_DEFAULT 0x00000000
+ /* set by CLP */
+ #define SHARED_MF_CLP_EXIT 0x00000001
+ /* set by MCP */
+ #define SHARED_MF_CLP_EXIT_DONE 0x00010000
+
+};
+
+struct port_mf_cfg {
+
+ u32 dynamic_cfg; /* device control channel */
+ #define PORT_MF_CFG_E1HOV_TAG_MASK 0x0000ffff
+ #define PORT_MF_CFG_E1HOV_TAG_SHIFT 0
+ #define PORT_MF_CFG_E1HOV_TAG_DEFAULT PORT_MF_CFG_E1HOV_TAG_MASK
+
+ u32 reserved[3];
+
+};
+
+struct func_mf_cfg {
+
+ u32 config;
+ /* E/R/I/D */
+ /* function 0 of each port cannot be hidden */
+ #define FUNC_MF_CFG_FUNC_HIDE 0x00000001
+
+ #define FUNC_MF_CFG_PROTOCOL_MASK 0x00000006
+ #define FUNC_MF_CFG_PROTOCOL_FCOE 0x00000000
+ #define FUNC_MF_CFG_PROTOCOL_ETHERNET 0x00000002
+ #define FUNC_MF_CFG_PROTOCOL_ETHERNET_WITH_RDMA 0x00000004
+ #define FUNC_MF_CFG_PROTOCOL_ISCSI 0x00000006
+ #define FUNC_MF_CFG_PROTOCOL_DEFAULT \
+ FUNC_MF_CFG_PROTOCOL_ETHERNET_WITH_RDMA
+
+ #define FUNC_MF_CFG_FUNC_DISABLED 0x00000008
+ #define FUNC_MF_CFG_FUNC_DELETED 0x00000010
+
+ /* PRI */
+ /* 0 - low priority, 3 - high priority */
+ #define FUNC_MF_CFG_TRANSMIT_PRIORITY_MASK 0x00000300
+ #define FUNC_MF_CFG_TRANSMIT_PRIORITY_SHIFT 8
+ #define FUNC_MF_CFG_TRANSMIT_PRIORITY_DEFAULT 0x00000000
+
+ /* MINBW, MAXBW */
+ /* value range - 0..100, increments in 100Mbps */
+ #define FUNC_MF_CFG_MIN_BW_MASK 0x00ff0000
+ #define FUNC_MF_CFG_MIN_BW_SHIFT 16
+ #define FUNC_MF_CFG_MIN_BW_DEFAULT 0x00000000
+ #define FUNC_MF_CFG_MAX_BW_MASK 0xff000000
+ #define FUNC_MF_CFG_MAX_BW_SHIFT 24
+ #define FUNC_MF_CFG_MAX_BW_DEFAULT 0x64000000
+
+ u32 mac_upper; /* MAC */
+ #define FUNC_MF_CFG_UPPERMAC_MASK 0x0000ffff
+ #define FUNC_MF_CFG_UPPERMAC_SHIFT 0
+ #define FUNC_MF_CFG_UPPERMAC_DEFAULT FUNC_MF_CFG_UPPERMAC_MASK
+ u32 mac_lower;
+ #define FUNC_MF_CFG_LOWERMAC_DEFAULT 0xffffffff
+
+ u32 e1hov_tag; /* VNI */
+ #define FUNC_MF_CFG_E1HOV_TAG_MASK 0x0000ffff
+ #define FUNC_MF_CFG_E1HOV_TAG_SHIFT 0
+ #define FUNC_MF_CFG_E1HOV_TAG_DEFAULT FUNC_MF_CFG_E1HOV_TAG_MASK
+
+ u32 reserved[2];
+};
+
+/* This structure is not applicable and should not be accessed on 57711 */
+struct func_ext_cfg {
+ u32 func_cfg;
+ #define MACP_FUNC_CFG_FLAGS_MASK 0x000000FF
+ #define MACP_FUNC_CFG_FLAGS_SHIFT 0
+ #define MACP_FUNC_CFG_FLAGS_ENABLED 0x00000001
+ #define MACP_FUNC_CFG_FLAGS_ETHERNET 0x00000002
+ #define MACP_FUNC_CFG_FLAGS_ISCSI_OFFLOAD 0x00000004
+ #define MACP_FUNC_CFG_FLAGS_FCOE_OFFLOAD 0x00000008
+
+ u32 iscsi_mac_addr_upper;
+ u32 iscsi_mac_addr_lower;
+
+ u32 fcoe_mac_addr_upper;
+ u32 fcoe_mac_addr_lower;
+
+ u32 fcoe_wwn_port_name_upper;
+ u32 fcoe_wwn_port_name_lower;
+
+ u32 fcoe_wwn_node_name_upper;
+ u32 fcoe_wwn_node_name_lower;
+
+ u32 preserve_data;
+ #define MF_FUNC_CFG_PRESERVE_L2_MAC (1<<0)
+ #define MF_FUNC_CFG_PRESERVE_ISCSI_MAC (1<<1)
+ #define MF_FUNC_CFG_PRESERVE_FCOE_MAC (1<<2)
+ #define MF_FUNC_CFG_PRESERVE_FCOE_WWN_P (1<<3)
+ #define MF_FUNC_CFG_PRESERVE_FCOE_WWN_N (1<<4)
+ #define MF_FUNC_CFG_PRESERVE_TX_BW (1<<5)
+};
+
+struct mf_cfg {
+
+ struct shared_mf_cfg shared_mf_config; /* 0x4 */
+ struct port_mf_cfg port_mf_config[PORT_MAX]; /* 0x10 * 2 = 0x20 */
+ /* for all chips, there are 8 mf functions */
+ struct func_mf_cfg func_mf_config[E1H_FUNC_MAX]; /* 0x18 * 8 = 0xc0 */
+ /*
+ * Extended configuration per function - this array does not exist and
+ * should not be accessed on 57711
+ */
+ struct func_ext_cfg func_ext_config[E1H_FUNC_MAX]; /* 0x28 * 8 = 0x140*/
+}; /* 0x224 */
+
+/****************************************************************************
+ * Shared Memory Region *
+ ****************************************************************************/
+struct shmem_region { /* SharedMem Offset (size) */
+
+ u32 validity_map[PORT_MAX]; /* 0x0 (4*2 = 0x8) */
+ #define SHR_MEM_FORMAT_REV_MASK 0xff000000
+ #define SHR_MEM_FORMAT_REV_ID ('A'<<24)
+ /* validity bits */
+ #define SHR_MEM_VALIDITY_PCI_CFG 0x00100000
+ #define SHR_MEM_VALIDITY_MB 0x00200000
+ #define SHR_MEM_VALIDITY_DEV_INFO 0x00400000
+ #define SHR_MEM_VALIDITY_RESERVED 0x00000007
+ /* One licensing bit should be set */
+ #define SHR_MEM_VALIDITY_LIC_KEY_IN_EFFECT_MASK 0x00000038
+ #define SHR_MEM_VALIDITY_LIC_MANUF_KEY_IN_EFFECT 0x00000008
+ #define SHR_MEM_VALIDITY_LIC_UPGRADE_KEY_IN_EFFECT 0x00000010
+ #define SHR_MEM_VALIDITY_LIC_NO_KEY_IN_EFFECT 0x00000020
+ /* Active MFW */
+ #define SHR_MEM_VALIDITY_ACTIVE_MFW_UNKNOWN 0x00000000
+ #define SHR_MEM_VALIDITY_ACTIVE_MFW_MASK 0x000001c0
+ #define SHR_MEM_VALIDITY_ACTIVE_MFW_IPMI 0x00000040
+ #define SHR_MEM_VALIDITY_ACTIVE_MFW_UMP 0x00000080
+ #define SHR_MEM_VALIDITY_ACTIVE_MFW_NCSI 0x000000c0
+ #define SHR_MEM_VALIDITY_ACTIVE_MFW_NONE 0x000001c0
+
+ struct shm_dev_info dev_info; /* 0x8 (0x438) */
+
+ struct license_key drv_lic_key[PORT_MAX]; /* 0x440 (52*2=0x68) */
+
+ /* FW information (for internal FW use) */
+ u32 fw_info_fio_offset; /* 0x4a8 (0x4) */
+ struct mgmtfw_state mgmtfw_state; /* 0x4ac (0x1b8) */
+
+ struct drv_port_mb port_mb[PORT_MAX]; /* 0x664 (16*2=0x20) */
+
+#ifdef BMAPI
+ /* This is a variable length array */
+ /* the number of function depends on the chip type */
+ struct drv_func_mb func_mb[1]; /* 0x684 (44*2/4/8=0x58/0xb0/0x160) */
+#else
+ /* the number of function depends on the chip type */
+ struct drv_func_mb func_mb[]; /* 0x684 (44*2/4/8=0x58/0xb0/0x160) */
+#endif /* BMAPI */
+
+}; /* 57710 = 0x6dc | 57711 = 0x7E4 | 57712 = 0x734 */
+
+/****************************************************************************
+ * Shared Memory 2 Region *
+ ****************************************************************************/
+/* The fw_flr_ack is actually built in the following way: */
+/* 8 bit: PF ack */
+/* 64 bit: VF ack */
+/* 8 bit: ios_dis_ack */
+/* In order to maintain endianity in the mailbox hsi, we want to keep using */
+/* u32. The fw must have the VF right after the PF since this is how it */
+/* access arrays(it expects always the VF to reside after the PF, and that */
+/* makes the calculation much easier for it. ) */
+/* In order to answer both limitations, and keep the struct small, the code */
+/* will abuse the structure defined here to achieve the actual partition */
+/* above */
+/****************************************************************************/
+struct fw_flr_ack {
+ u32 pf_ack;
+ u32 vf_ack[1];
+ u32 iov_dis_ack;
+};
+
+struct fw_flr_mb {
+ u32 aggint;
+ u32 opgen_addr;
+ struct fw_flr_ack ack;
+};
+
+/**** SUPPORT FOR SHMEM ARRRAYS ***
+ * The SHMEM HSI is aligned on 32 bit boundaries which makes it difficult to
+ * define arrays with storage types smaller then unsigned dwords.
+ * The macros below add generic support for SHMEM arrays with numeric elements
+ * that can span 2,4,8 or 16 bits. The array underlying type is a 32 bit dword
+ * array with individual bit-filed elements accessed using shifts and masks.
+ *
+ */
+
+/* eb is the bitwidth of a single element */
+#define SHMEM_ARRAY_MASK(eb) ((1<<(eb))-1)
+#define SHMEM_ARRAY_ENTRY(i, eb) ((i)/(32/(eb)))
+
+/* the bit-position macro allows the used to flip the order of the arrays
+ * elements on a per byte or word boundary.
+ *
+ * example: an array with 8 entries each 4 bit wide. This array will fit into
+ * a single dword. The diagrmas below show the array order of the nibbles.
+ *
+ * SHMEM_ARRAY_BITPOS(i, 4, 4) defines the stadard ordering:
+ *
+ * | | | |
+ * 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+ * | | | |
+ *
+ * SHMEM_ARRAY_BITPOS(i, 4, 8) defines a flip ordering per byte:
+ *
+ * | | | |
+ * 1 | 0 | 3 | 2 | 5 | 4 | 7 | 6 |
+ * | | | |
+ *
+ * SHMEM_ARRAY_BITPOS(i, 4, 16) defines a flip ordering per word:
+ *
+ * | | | |
+ * 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 |
+ * | | | |
+ */
+#define SHMEM_ARRAY_BITPOS(i, eb, fb) \
+ ((((32/(fb)) - 1 - ((i)/((fb)/(eb))) % (32/(fb))) * (fb)) + \
+ (((i)%((fb)/(eb))) * (eb)))
+
+#define SHMEM_ARRAY_GET(a, i, eb, fb) \
+ ((a[SHMEM_ARRAY_ENTRY(i, eb)] >> SHMEM_ARRAY_BITPOS(i, eb, fb)) & \
+ SHMEM_ARRAY_MASK(eb))
+
+#define SHMEM_ARRAY_SET(a, i, eb, fb, val) \
+do { \
+ a[SHMEM_ARRAY_ENTRY(i, eb)] &= ~(SHMEM_ARRAY_MASK(eb) << \
+ SHMEM_ARRAY_BITPOS(i, eb, fb)); \
+ a[SHMEM_ARRAY_ENTRY(i, eb)] |= (((val) & SHMEM_ARRAY_MASK(eb)) << \
+ SHMEM_ARRAY_BITPOS(i, eb, fb)); \
+} while (0)
+
+
+/****START OF DCBX STRUCTURES DECLARATIONS****/
+#define DCBX_MAX_NUM_PRI_PG_ENTRIES 8
+#define DCBX_PRI_PG_BITWIDTH 4
+#define DCBX_PRI_PG_FBITS 8
+#define DCBX_PRI_PG_GET(a, i) \
+ SHMEM_ARRAY_GET(a, i, DCBX_PRI_PG_BITWIDTH, DCBX_PRI_PG_FBITS)
+#define DCBX_PRI_PG_SET(a, i, val) \
+ SHMEM_ARRAY_SET(a, i, DCBX_PRI_PG_BITWIDTH, DCBX_PRI_PG_FBITS, val)
+#define DCBX_MAX_NUM_PG_BW_ENTRIES 8
+#define DCBX_BW_PG_BITWIDTH 8
+#define DCBX_PG_BW_GET(a, i) \
+ SHMEM_ARRAY_GET(a, i, DCBX_BW_PG_BITWIDTH, DCBX_BW_PG_BITWIDTH)
+#define DCBX_PG_BW_SET(a, i, val) \
+ SHMEM_ARRAY_SET(a, i, DCBX_BW_PG_BITWIDTH, DCBX_BW_PG_BITWIDTH, val)
+#define DCBX_STRICT_PRI_PG 15
+#define DCBX_MAX_APP_PROTOCOL 16
+#define FCOE_APP_IDX 0
+#define ISCSI_APP_IDX 1
+#define PREDEFINED_APP_IDX_MAX 2
+
+
+/* Big/Little endian have the same representation. */
+struct dcbx_ets_feature {
+ /*
+ * For Admin MIB - is this feature supported by the
+ * driver | For Local MIB - should this feature be enabled.
+ */
+ u32 enabled;
+ u32 pg_bw_tbl[2];
+ u32 pri_pg_tbl[1];
+};
+
+/* Driver structure in LE */
+struct dcbx_pfc_feature {
+#ifdef __BIG_ENDIAN
+ u8 pri_en_bitmap;
+ #define DCBX_PFC_PRI_0 0x01
+ #define DCBX_PFC_PRI_1 0x02
+ #define DCBX_PFC_PRI_2 0x04
+ #define DCBX_PFC_PRI_3 0x08
+ #define DCBX_PFC_PRI_4 0x10
+ #define DCBX_PFC_PRI_5 0x20
+ #define DCBX_PFC_PRI_6 0x40
+ #define DCBX_PFC_PRI_7 0x80
+ u8 pfc_caps;
+ u8 reserved;
+ u8 enabled;
+#elif defined(__LITTLE_ENDIAN)
+ u8 enabled;
+ u8 reserved;
+ u8 pfc_caps;
+ u8 pri_en_bitmap;
+ #define DCBX_PFC_PRI_0 0x01
+ #define DCBX_PFC_PRI_1 0x02
+ #define DCBX_PFC_PRI_2 0x04
+ #define DCBX_PFC_PRI_3 0x08
+ #define DCBX_PFC_PRI_4 0x10
+ #define DCBX_PFC_PRI_5 0x20
+ #define DCBX_PFC_PRI_6 0x40
+ #define DCBX_PFC_PRI_7 0x80
+#endif
+};
+
+struct dcbx_app_priority_entry {
+#ifdef __BIG_ENDIAN
+ u16 app_id;
+ u8 pri_bitmap;
+ u8 appBitfield;
+ #define DCBX_APP_ENTRY_VALID 0x01
+ #define DCBX_APP_ENTRY_SF_MASK 0x30
+ #define DCBX_APP_ENTRY_SF_SHIFT 4
+ #define DCBX_APP_SF_ETH_TYPE 0x10
+ #define DCBX_APP_SF_PORT 0x20
+#elif defined(__LITTLE_ENDIAN)
+ u8 appBitfield;
+ #define DCBX_APP_ENTRY_VALID 0x01
+ #define DCBX_APP_ENTRY_SF_MASK 0x30
+ #define DCBX_APP_ENTRY_SF_SHIFT 4
+ #define DCBX_APP_SF_ETH_TYPE 0x10
+ #define DCBX_APP_SF_PORT 0x20
+ u8 pri_bitmap;
+ u16 app_id;
+#endif
+};
+
+
+/* FW structure in BE */
+struct dcbx_app_priority_feature {
+#ifdef __BIG_ENDIAN
+ u8 reserved;
+ u8 default_pri;
+ u8 tc_supported;
+ u8 enabled;
+#elif defined(__LITTLE_ENDIAN)
+ u8 enabled;
+ u8 tc_supported;
+ u8 default_pri;
+ u8 reserved;
+#endif
+ struct dcbx_app_priority_entry app_pri_tbl[DCBX_MAX_APP_PROTOCOL];
+};
+
+/* FW structure in BE */
+struct dcbx_features {
+ /* PG feature */
+ struct dcbx_ets_feature ets;
+ /* PFC feature */
+ struct dcbx_pfc_feature pfc;
+ /* APP feature */
+ struct dcbx_app_priority_feature app;
+};
+
+/* LLDP protocol parameters */
+/* FW structure in BE */
+struct lldp_params {
+#ifdef __BIG_ENDIAN
+ u8 msg_fast_tx_interval;
+ u8 msg_tx_hold;
+ u8 msg_tx_interval;
+ u8 admin_status;
+ #define LLDP_TX_ONLY 0x01
+ #define LLDP_RX_ONLY 0x02
+ #define LLDP_TX_RX 0x03
+ #define LLDP_DISABLED 0x04
+ u8 reserved1;
+ u8 tx_fast;
+ u8 tx_crd_max;
+ u8 tx_crd;
+#elif defined(__LITTLE_ENDIAN)
+ u8 admin_status;
+ #define LLDP_TX_ONLY 0x01
+ #define LLDP_RX_ONLY 0x02
+ #define LLDP_TX_RX 0x03
+ #define LLDP_DISABLED 0x04
+ u8 msg_tx_interval;
+ u8 msg_tx_hold;
+ u8 msg_fast_tx_interval;
+ u8 tx_crd;
+ u8 tx_crd_max;
+ u8 tx_fast;
+ u8 reserved1;
+#endif
+ #define REM_CHASSIS_ID_STAT_LEN 4
+ #define REM_PORT_ID_STAT_LEN 4
+ /* Holds remote Chassis ID TLV header, subtype and 9B of payload. */
+ u32 peer_chassis_id[REM_CHASSIS_ID_STAT_LEN];
+ /* Holds remote Port ID TLV header, subtype and 9B of payload. */
+ u32 peer_port_id[REM_PORT_ID_STAT_LEN];
+};
+
+struct lldp_dcbx_stat {
+ #define LOCAL_CHASSIS_ID_STAT_LEN 2
+ #define LOCAL_PORT_ID_STAT_LEN 2
+ /* Holds local Chassis ID 8B payload of constant subtype 4. */
+ u32 local_chassis_id[LOCAL_CHASSIS_ID_STAT_LEN];
+ /* Holds local Port ID 8B payload of constant subtype 3. */
+ u32 local_port_id[LOCAL_PORT_ID_STAT_LEN];
+ /* Number of DCBX frames transmitted. */
+ u32 num_tx_dcbx_pkts;
+ /* Number of DCBX frames received. */
+ u32 num_rx_dcbx_pkts;
+};
+
+/* ADMIN MIB - DCBX local machine default configuration. */
+struct lldp_admin_mib {
+ u32 ver_cfg_flags;
+ #define DCBX_ETS_CONFIG_TX_ENABLED 0x00000001
+ #define DCBX_PFC_CONFIG_TX_ENABLED 0x00000002
+ #define DCBX_APP_CONFIG_TX_ENABLED 0x00000004
+ #define DCBX_ETS_RECO_TX_ENABLED 0x00000008
+ #define DCBX_ETS_RECO_VALID 0x00000010
+ #define DCBX_ETS_WILLING 0x00000020
+ #define DCBX_PFC_WILLING 0x00000040
+ #define DCBX_APP_WILLING 0x00000080
+ #define DCBX_VERSION_CEE 0x00000100
+ #define DCBX_VERSION_IEEE 0x00000200
+ #define DCBX_DCBX_ENABLED 0x00000400
+ #define DCBX_CEE_VERSION_MASK 0x0000f000
+ #define DCBX_CEE_VERSION_SHIFT 12
+ #define DCBX_CEE_MAX_VERSION_MASK 0x000f0000
+ #define DCBX_CEE_MAX_VERSION_SHIFT 16
+ struct dcbx_features features;
+};
+
+/* REMOTE MIB - remote machine DCBX configuration. */
+struct lldp_remote_mib {
+ u32 prefix_seq_num;
+ u32 flags;
+ #define DCBX_ETS_TLV_RX 0x00000001
+ #define DCBX_PFC_TLV_RX 0x00000002
+ #define DCBX_APP_TLV_RX 0x00000004
+ #define DCBX_ETS_RX_ERROR 0x00000010
+ #define DCBX_PFC_RX_ERROR 0x00000020
+ #define DCBX_APP_RX_ERROR 0x00000040
+ #define DCBX_ETS_REM_WILLING 0x00000100
+ #define DCBX_PFC_REM_WILLING 0x00000200
+ #define DCBX_APP_REM_WILLING 0x00000400
+ #define DCBX_REMOTE_ETS_RECO_VALID 0x00001000
+ #define DCBX_REMOTE_MIB_VALID 0x00002000
+ struct dcbx_features features;
+ u32 suffix_seq_num;
+};
+
+/* LOCAL MIB - operational DCBX configuration - transmitted on Tx LLDPDU. */
+struct lldp_local_mib {
+ u32 prefix_seq_num;
+ /* Indicates if there is mismatch with negotiation results. */
+ u32 error;
+ #define DCBX_LOCAL_ETS_ERROR 0x00000001
+ #define DCBX_LOCAL_PFC_ERROR 0x00000002
+ #define DCBX_LOCAL_APP_ERROR 0x00000004
+ #define DCBX_LOCAL_PFC_MISMATCH 0x00000010
+ #define DCBX_LOCAL_APP_MISMATCH 0x00000020
+ #define DCBX_REMOTE_MIB_ERROR 0x00000040
+ struct dcbx_features features;
+ u32 suffix_seq_num;
+};
+/***END OF DCBX STRUCTURES DECLARATIONS***/
+
+struct ncsi_oem_fcoe_features {
+ u32 fcoe_features1;
+ #define FCOE_FEATURES1_IOS_PER_CONNECTION_MASK 0x0000FFFF
+ #define FCOE_FEATURES1_IOS_PER_CONNECTION_OFFSET 0
+
+ #define FCOE_FEATURES1_LOGINS_PER_PORT_MASK 0xFFFF0000
+ #define FCOE_FEATURES1_LOGINS_PER_PORT_OFFSET 16
+
+ u32 fcoe_features2;
+ #define FCOE_FEATURES2_EXCHANGES_MASK 0x0000FFFF
+ #define FCOE_FEATURES2_EXCHANGES_OFFSET 0
+
+ #define FCOE_FEATURES2_NPIV_WWN_PER_PORT_MASK 0xFFFF0000
+ #define FCOE_FEATURES2_NPIV_WWN_PER_PORT_OFFSET 16
+
+ u32 fcoe_features3;
+ #define FCOE_FEATURES3_TARGETS_SUPPORTED_MASK 0x0000FFFF
+ #define FCOE_FEATURES3_TARGETS_SUPPORTED_OFFSET 0
+
+ #define FCOE_FEATURES3_OUTSTANDING_COMMANDS_MASK 0xFFFF0000
+ #define FCOE_FEATURES3_OUTSTANDING_COMMANDS_OFFSET 16
+
+ u32 fcoe_features4;
+ #define FCOE_FEATURES4_FEATURE_SETTINGS_MASK 0x0000000F
+ #define FCOE_FEATURES4_FEATURE_SETTINGS_OFFSET 0
+};
+
+struct ncsi_oem_data {
+ u32 driver_version[4];
+ struct ncsi_oem_fcoe_features ncsi_oem_fcoe_features;
+};
+
+struct shmem2_region {
+
+ u32 size; /* 0x0000 */
+
+ u32 dcc_support; /* 0x0004 */
+ #define SHMEM_DCC_SUPPORT_NONE 0x00000000
+ #define SHMEM_DCC_SUPPORT_DISABLE_ENABLE_PF_TLV 0x00000001
+ #define SHMEM_DCC_SUPPORT_BANDWIDTH_ALLOCATION_TLV 0x00000004
+ #define SHMEM_DCC_SUPPORT_CHANGE_MAC_ADDRESS_TLV 0x00000008
+ #define SHMEM_DCC_SUPPORT_SET_PROTOCOL_TLV 0x00000040
+ #define SHMEM_DCC_SUPPORT_SET_PRIORITY_TLV 0x00000080
+
+ u32 ext_phy_fw_version2[PORT_MAX]; /* 0x0008 */
+ /*
+ * For backwards compatibility, if the mf_cfg_addr does not exist
+ * (the size filed is smaller than 0xc) the mf_cfg resides at the
+ * end of struct shmem_region
+ */
+ u32 mf_cfg_addr; /* 0x0010 */
+ #define SHMEM_MF_CFG_ADDR_NONE 0x00000000
+
+ struct fw_flr_mb flr_mb; /* 0x0014 */
+ u32 dcbx_lldp_params_offset; /* 0x0028 */
+ #define SHMEM_LLDP_DCBX_PARAMS_NONE 0x00000000
+ u32 dcbx_neg_res_offset; /* 0x002c */
+ #define SHMEM_DCBX_NEG_RES_NONE 0x00000000
+ u32 dcbx_remote_mib_offset; /* 0x0030 */
+ #define SHMEM_DCBX_REMOTE_MIB_NONE 0x00000000
+ /*
+ * The other shmemX_base_addr holds the other path's shmem address
+ * required for example in case of common phy init, or for path1 to know
+ * the address of mcp debug trace which is located in offset from shmem
+ * of path0
+ */
+ u32 other_shmem_base_addr; /* 0x0034 */
+ u32 other_shmem2_base_addr; /* 0x0038 */
+ /*
+ * mcp_vf_disabled is set by the MCP to indicate the driver about VFs
+ * which were disabled/flred
+ */
+ u32 mcp_vf_disabled[E2_VF_MAX / 32]; /* 0x003c */
+
+ /*
+ * drv_ack_vf_disabled is set by the PF driver to ack handled disabled
+ * VFs
+ */
+ u32 drv_ack_vf_disabled[E2_FUNC_MAX][E2_VF_MAX / 32]; /* 0x0044 */
+
+ u32 dcbx_lldp_dcbx_stat_offset; /* 0x0064 */
+ #define SHMEM_LLDP_DCBX_STAT_NONE 0x00000000
+
+ /*
+ * edebug_driver_if field is used to transfer messages between edebug
+ * app to the driver through shmem2.
+ *
+ * message format:
+ * bits 0-2 - function number / instance of driver to perform request
+ * bits 3-5 - op code / is_ack?
+ * bits 6-63 - data
+ */
+ u32 edebug_driver_if[2]; /* 0x0068 */
+ #define EDEBUG_DRIVER_IF_OP_CODE_GET_PHYS_ADDR 1
+ #define EDEBUG_DRIVER_IF_OP_CODE_GET_BUS_ADDR 2
+ #define EDEBUG_DRIVER_IF_OP_CODE_DISABLE_STAT 3
+
+ u32 nvm_retain_bitmap_addr; /* 0x0070 */
+
+ u32 reserved1; /* 0x0074 */
+
+ u32 reserved2[E2_FUNC_MAX];
+
+ u32 reserved3[E2_FUNC_MAX];/* 0x0088 */
+ u32 reserved4[E2_FUNC_MAX];/* 0x0098 */
+
+ u32 swim_base_addr; /* 0x0108 */
+ u32 swim_funcs;
+ u32 swim_main_cb;
+
+ u32 reserved5[2];
+
+ /* generic flags controlled by the driver */
+ u32 drv_flags;
+ #define DRV_FLAGS_DCB_CONFIGURED 0x1
+
+ /* pointer to extended dev_info shared data copied from nvm image */
+ u32 extended_dev_info_shared_addr;
+ u32 ncsi_oem_data_addr;
+
+ u32 ocsd_host_addr;
+ u32 ocbb_host_addr;
+ u32 ocsd_req_update_interval;
+};
+
+
+struct emac_stats {
+ u32 rx_stat_ifhcinoctets;
+ u32 rx_stat_ifhcinbadoctets;
+ u32 rx_stat_etherstatsfragments;
+ u32 rx_stat_ifhcinucastpkts;
+ u32 rx_stat_ifhcinmulticastpkts;
+ u32 rx_stat_ifhcinbroadcastpkts;
+ u32 rx_stat_dot3statsfcserrors;
+ u32 rx_stat_dot3statsalignmenterrors;
+ u32 rx_stat_dot3statscarriersenseerrors;
+ u32 rx_stat_xonpauseframesreceived;
+ u32 rx_stat_xoffpauseframesreceived;
+ u32 rx_stat_maccontrolframesreceived;
+ u32 rx_stat_xoffstateentered;
+ u32 rx_stat_dot3statsframestoolong;
+ u32 rx_stat_etherstatsjabbers;
+ u32 rx_stat_etherstatsundersizepkts;
+ u32 rx_stat_etherstatspkts64octets;
+ u32 rx_stat_etherstatspkts65octetsto127octets;
+ u32 rx_stat_etherstatspkts128octetsto255octets;
+ u32 rx_stat_etherstatspkts256octetsto511octets;
+ u32 rx_stat_etherstatspkts512octetsto1023octets;
+ u32 rx_stat_etherstatspkts1024octetsto1522octets;
+ u32 rx_stat_etherstatspktsover1522octets;
+
+ u32 rx_stat_falsecarriererrors;
+
+ u32 tx_stat_ifhcoutoctets;
+ u32 tx_stat_ifhcoutbadoctets;
+ u32 tx_stat_etherstatscollisions;
+ u32 tx_stat_outxonsent;
+ u32 tx_stat_outxoffsent;
+ u32 tx_stat_flowcontroldone;
+ u32 tx_stat_dot3statssinglecollisionframes;
+ u32 tx_stat_dot3statsmultiplecollisionframes;
+ u32 tx_stat_dot3statsdeferredtransmissions;
+ u32 tx_stat_dot3statsexcessivecollisions;
+ u32 tx_stat_dot3statslatecollisions;
+ u32 tx_stat_ifhcoutucastpkts;
+ u32 tx_stat_ifhcoutmulticastpkts;
+ u32 tx_stat_ifhcoutbroadcastpkts;
+ u32 tx_stat_etherstatspkts64octets;
+ u32 tx_stat_etherstatspkts65octetsto127octets;
+ u32 tx_stat_etherstatspkts128octetsto255octets;
+ u32 tx_stat_etherstatspkts256octetsto511octets;
+ u32 tx_stat_etherstatspkts512octetsto1023octets;
+ u32 tx_stat_etherstatspkts1024octetsto1522octets;
+ u32 tx_stat_etherstatspktsover1522octets;
+ u32 tx_stat_dot3statsinternalmactransmiterrors;
+};
+
+
+struct bmac1_stats {
+ u32 tx_stat_gtpkt_lo;
+ u32 tx_stat_gtpkt_hi;
+ u32 tx_stat_gtxpf_lo;
+ u32 tx_stat_gtxpf_hi;
+ u32 tx_stat_gtfcs_lo;
+ u32 tx_stat_gtfcs_hi;
+ u32 tx_stat_gtmca_lo;
+ u32 tx_stat_gtmca_hi;
+ u32 tx_stat_gtbca_lo;
+ u32 tx_stat_gtbca_hi;
+ u32 tx_stat_gtfrg_lo;
+ u32 tx_stat_gtfrg_hi;
+ u32 tx_stat_gtovr_lo;
+ u32 tx_stat_gtovr_hi;
+ u32 tx_stat_gt64_lo;
+ u32 tx_stat_gt64_hi;
+ u32 tx_stat_gt127_lo;
+ u32 tx_stat_gt127_hi;
+ u32 tx_stat_gt255_lo;
+ u32 tx_stat_gt255_hi;
+ u32 tx_stat_gt511_lo;
+ u32 tx_stat_gt511_hi;
+ u32 tx_stat_gt1023_lo;
+ u32 tx_stat_gt1023_hi;
+ u32 tx_stat_gt1518_lo;
+ u32 tx_stat_gt1518_hi;
+ u32 tx_stat_gt2047_lo;
+ u32 tx_stat_gt2047_hi;
+ u32 tx_stat_gt4095_lo;
+ u32 tx_stat_gt4095_hi;
+ u32 tx_stat_gt9216_lo;
+ u32 tx_stat_gt9216_hi;
+ u32 tx_stat_gt16383_lo;
+ u32 tx_stat_gt16383_hi;
+ u32 tx_stat_gtmax_lo;
+ u32 tx_stat_gtmax_hi;
+ u32 tx_stat_gtufl_lo;
+ u32 tx_stat_gtufl_hi;
+ u32 tx_stat_gterr_lo;
+ u32 tx_stat_gterr_hi;
+ u32 tx_stat_gtbyt_lo;
+ u32 tx_stat_gtbyt_hi;
+
+ u32 rx_stat_gr64_lo;
+ u32 rx_stat_gr64_hi;
+ u32 rx_stat_gr127_lo;
+ u32 rx_stat_gr127_hi;
+ u32 rx_stat_gr255_lo;
+ u32 rx_stat_gr255_hi;
+ u32 rx_stat_gr511_lo;
+ u32 rx_stat_gr511_hi;
+ u32 rx_stat_gr1023_lo;
+ u32 rx_stat_gr1023_hi;
+ u32 rx_stat_gr1518_lo;
+ u32 rx_stat_gr1518_hi;
+ u32 rx_stat_gr2047_lo;
+ u32 rx_stat_gr2047_hi;
+ u32 rx_stat_gr4095_lo;
+ u32 rx_stat_gr4095_hi;
+ u32 rx_stat_gr9216_lo;
+ u32 rx_stat_gr9216_hi;
+ u32 rx_stat_gr16383_lo;
+ u32 rx_stat_gr16383_hi;
+ u32 rx_stat_grmax_lo;
+ u32 rx_stat_grmax_hi;
+ u32 rx_stat_grpkt_lo;
+ u32 rx_stat_grpkt_hi;
+ u32 rx_stat_grfcs_lo;
+ u32 rx_stat_grfcs_hi;
+ u32 rx_stat_grmca_lo;
+ u32 rx_stat_grmca_hi;
+ u32 rx_stat_grbca_lo;
+ u32 rx_stat_grbca_hi;
+ u32 rx_stat_grxcf_lo;
+ u32 rx_stat_grxcf_hi;
+ u32 rx_stat_grxpf_lo;
+ u32 rx_stat_grxpf_hi;
+ u32 rx_stat_grxuo_lo;
+ u32 rx_stat_grxuo_hi;
+ u32 rx_stat_grjbr_lo;
+ u32 rx_stat_grjbr_hi;
+ u32 rx_stat_grovr_lo;
+ u32 rx_stat_grovr_hi;
+ u32 rx_stat_grflr_lo;
+ u32 rx_stat_grflr_hi;
+ u32 rx_stat_grmeg_lo;
+ u32 rx_stat_grmeg_hi;
+ u32 rx_stat_grmeb_lo;
+ u32 rx_stat_grmeb_hi;
+ u32 rx_stat_grbyt_lo;
+ u32 rx_stat_grbyt_hi;
+ u32 rx_stat_grund_lo;
+ u32 rx_stat_grund_hi;
+ u32 rx_stat_grfrg_lo;
+ u32 rx_stat_grfrg_hi;
+ u32 rx_stat_grerb_lo;
+ u32 rx_stat_grerb_hi;
+ u32 rx_stat_grfre_lo;
+ u32 rx_stat_grfre_hi;
+ u32 rx_stat_gripj_lo;
+ u32 rx_stat_gripj_hi;
+};
+
+struct bmac2_stats {
+ u32 tx_stat_gtpk_lo; /* gtpok */
+ u32 tx_stat_gtpk_hi; /* gtpok */
+ u32 tx_stat_gtxpf_lo; /* gtpf */
+ u32 tx_stat_gtxpf_hi; /* gtpf */
+ u32 tx_stat_gtpp_lo; /* NEW BMAC2 */
+ u32 tx_stat_gtpp_hi; /* NEW BMAC2 */
+ u32 tx_stat_gtfcs_lo;
+ u32 tx_stat_gtfcs_hi;
+ u32 tx_stat_gtuca_lo; /* NEW BMAC2 */
+ u32 tx_stat_gtuca_hi; /* NEW BMAC2 */
+ u32 tx_stat_gtmca_lo;
+ u32 tx_stat_gtmca_hi;
+ u32 tx_stat_gtbca_lo;
+ u32 tx_stat_gtbca_hi;
+ u32 tx_stat_gtovr_lo;
+ u32 tx_stat_gtovr_hi;
+ u32 tx_stat_gtfrg_lo;
+ u32 tx_stat_gtfrg_hi;
+ u32 tx_stat_gtpkt1_lo; /* gtpkt */
+ u32 tx_stat_gtpkt1_hi; /* gtpkt */
+ u32 tx_stat_gt64_lo;
+ u32 tx_stat_gt64_hi;
+ u32 tx_stat_gt127_lo;
+ u32 tx_stat_gt127_hi;
+ u32 tx_stat_gt255_lo;
+ u32 tx_stat_gt255_hi;
+ u32 tx_stat_gt511_lo;
+ u32 tx_stat_gt511_hi;
+ u32 tx_stat_gt1023_lo;
+ u32 tx_stat_gt1023_hi;
+ u32 tx_stat_gt1518_lo;
+ u32 tx_stat_gt1518_hi;
+ u32 tx_stat_gt2047_lo;
+ u32 tx_stat_gt2047_hi;
+ u32 tx_stat_gt4095_lo;
+ u32 tx_stat_gt4095_hi;
+ u32 tx_stat_gt9216_lo;
+ u32 tx_stat_gt9216_hi;
+ u32 tx_stat_gt16383_lo;
+ u32 tx_stat_gt16383_hi;
+ u32 tx_stat_gtmax_lo;
+ u32 tx_stat_gtmax_hi;
+ u32 tx_stat_gtufl_lo;
+ u32 tx_stat_gtufl_hi;
+ u32 tx_stat_gterr_lo;
+ u32 tx_stat_gterr_hi;
+ u32 tx_stat_gtbyt_lo;
+ u32 tx_stat_gtbyt_hi;
+
+ u32 rx_stat_gr64_lo;
+ u32 rx_stat_gr64_hi;
+ u32 rx_stat_gr127_lo;
+ u32 rx_stat_gr127_hi;
+ u32 rx_stat_gr255_lo;
+ u32 rx_stat_gr255_hi;
+ u32 rx_stat_gr511_lo;
+ u32 rx_stat_gr511_hi;
+ u32 rx_stat_gr1023_lo;
+ u32 rx_stat_gr1023_hi;
+ u32 rx_stat_gr1518_lo;
+ u32 rx_stat_gr1518_hi;
+ u32 rx_stat_gr2047_lo;
+ u32 rx_stat_gr2047_hi;
+ u32 rx_stat_gr4095_lo;
+ u32 rx_stat_gr4095_hi;
+ u32 rx_stat_gr9216_lo;
+ u32 rx_stat_gr9216_hi;
+ u32 rx_stat_gr16383_lo;
+ u32 rx_stat_gr16383_hi;
+ u32 rx_stat_grmax_lo;
+ u32 rx_stat_grmax_hi;
+ u32 rx_stat_grpkt_lo;
+ u32 rx_stat_grpkt_hi;
+ u32 rx_stat_grfcs_lo;
+ u32 rx_stat_grfcs_hi;
+ u32 rx_stat_gruca_lo;
+ u32 rx_stat_gruca_hi;
+ u32 rx_stat_grmca_lo;
+ u32 rx_stat_grmca_hi;
+ u32 rx_stat_grbca_lo;
+ u32 rx_stat_grbca_hi;
+ u32 rx_stat_grxpf_lo; /* grpf */
+ u32 rx_stat_grxpf_hi; /* grpf */
+ u32 rx_stat_grpp_lo;
+ u32 rx_stat_grpp_hi;
+ u32 rx_stat_grxuo_lo; /* gruo */
+ u32 rx_stat_grxuo_hi; /* gruo */
+ u32 rx_stat_grjbr_lo;
+ u32 rx_stat_grjbr_hi;
+ u32 rx_stat_grovr_lo;
+ u32 rx_stat_grovr_hi;
+ u32 rx_stat_grxcf_lo; /* grcf */
+ u32 rx_stat_grxcf_hi; /* grcf */
+ u32 rx_stat_grflr_lo;
+ u32 rx_stat_grflr_hi;
+ u32 rx_stat_grpok_lo;
+ u32 rx_stat_grpok_hi;
+ u32 rx_stat_grmeg_lo;
+ u32 rx_stat_grmeg_hi;
+ u32 rx_stat_grmeb_lo;
+ u32 rx_stat_grmeb_hi;
+ u32 rx_stat_grbyt_lo;
+ u32 rx_stat_grbyt_hi;
+ u32 rx_stat_grund_lo;
+ u32 rx_stat_grund_hi;
+ u32 rx_stat_grfrg_lo;
+ u32 rx_stat_grfrg_hi;
+ u32 rx_stat_grerb_lo; /* grerrbyt */
+ u32 rx_stat_grerb_hi; /* grerrbyt */
+ u32 rx_stat_grfre_lo; /* grfrerr */
+ u32 rx_stat_grfre_hi; /* grfrerr */
+ u32 rx_stat_gripj_lo;
+ u32 rx_stat_gripj_hi;
+};
+
+struct mstat_stats {
+ struct {
+ /* OTE MSTAT on E3 has a bug where this register's contents are
+ * actually tx_gtxpok + tx_gtxpf + (possibly)tx_gtxpp
+ */
+ u32 tx_gtxpok_lo;
+ u32 tx_gtxpok_hi;
+ u32 tx_gtxpf_lo;
+ u32 tx_gtxpf_hi;
+ u32 tx_gtxpp_lo;
+ u32 tx_gtxpp_hi;
+ u32 tx_gtfcs_lo;
+ u32 tx_gtfcs_hi;
+ u32 tx_gtuca_lo;
+ u32 tx_gtuca_hi;
+ u32 tx_gtmca_lo;
+ u32 tx_gtmca_hi;
+ u32 tx_gtgca_lo;
+ u32 tx_gtgca_hi;
+ u32 tx_gtpkt_lo;
+ u32 tx_gtpkt_hi;
+ u32 tx_gt64_lo;
+ u32 tx_gt64_hi;
+ u32 tx_gt127_lo;
+ u32 tx_gt127_hi;
+ u32 tx_gt255_lo;
+ u32 tx_gt255_hi;
+ u32 tx_gt511_lo;
+ u32 tx_gt511_hi;
+ u32 tx_gt1023_lo;
+ u32 tx_gt1023_hi;
+ u32 tx_gt1518_lo;
+ u32 tx_gt1518_hi;
+ u32 tx_gt2047_lo;
+ u32 tx_gt2047_hi;
+ u32 tx_gt4095_lo;
+ u32 tx_gt4095_hi;
+ u32 tx_gt9216_lo;
+ u32 tx_gt9216_hi;
+ u32 tx_gt16383_lo;
+ u32 tx_gt16383_hi;
+ u32 tx_gtufl_lo;
+ u32 tx_gtufl_hi;
+ u32 tx_gterr_lo;
+ u32 tx_gterr_hi;
+ u32 tx_gtbyt_lo;
+ u32 tx_gtbyt_hi;
+ u32 tx_collisions_lo;
+ u32 tx_collisions_hi;
+ u32 tx_singlecollision_lo;
+ u32 tx_singlecollision_hi;
+ u32 tx_multiplecollisions_lo;
+ u32 tx_multiplecollisions_hi;
+ u32 tx_deferred_lo;
+ u32 tx_deferred_hi;
+ u32 tx_excessivecollisions_lo;
+ u32 tx_excessivecollisions_hi;
+ u32 tx_latecollisions_lo;
+ u32 tx_latecollisions_hi;
+ } stats_tx;
+
+ struct {
+ u32 rx_gr64_lo;
+ u32 rx_gr64_hi;
+ u32 rx_gr127_lo;
+ u32 rx_gr127_hi;
+ u32 rx_gr255_lo;
+ u32 rx_gr255_hi;
+ u32 rx_gr511_lo;
+ u32 rx_gr511_hi;
+ u32 rx_gr1023_lo;
+ u32 rx_gr1023_hi;
+ u32 rx_gr1518_lo;
+ u32 rx_gr1518_hi;
+ u32 rx_gr2047_lo;
+ u32 rx_gr2047_hi;
+ u32 rx_gr4095_lo;
+ u32 rx_gr4095_hi;
+ u32 rx_gr9216_lo;
+ u32 rx_gr9216_hi;
+ u32 rx_gr16383_lo;
+ u32 rx_gr16383_hi;
+ u32 rx_grpkt_lo;
+ u32 rx_grpkt_hi;
+ u32 rx_grfcs_lo;
+ u32 rx_grfcs_hi;
+ u32 rx_gruca_lo;
+ u32 rx_gruca_hi;
+ u32 rx_grmca_lo;
+ u32 rx_grmca_hi;
+ u32 rx_grbca_lo;
+ u32 rx_grbca_hi;
+ u32 rx_grxpf_lo;
+ u32 rx_grxpf_hi;
+ u32 rx_grxpp_lo;
+ u32 rx_grxpp_hi;
+ u32 rx_grxuo_lo;
+ u32 rx_grxuo_hi;
+ u32 rx_grovr_lo;
+ u32 rx_grovr_hi;
+ u32 rx_grxcf_lo;
+ u32 rx_grxcf_hi;
+ u32 rx_grflr_lo;
+ u32 rx_grflr_hi;
+ u32 rx_grpok_lo;
+ u32 rx_grpok_hi;
+ u32 rx_grbyt_lo;
+ u32 rx_grbyt_hi;
+ u32 rx_grund_lo;
+ u32 rx_grund_hi;
+ u32 rx_grfrg_lo;
+ u32 rx_grfrg_hi;
+ u32 rx_grerb_lo;
+ u32 rx_grerb_hi;
+ u32 rx_grfre_lo;
+ u32 rx_grfre_hi;
+
+ u32 rx_alignmenterrors_lo;
+ u32 rx_alignmenterrors_hi;
+ u32 rx_falsecarrier_lo;
+ u32 rx_falsecarrier_hi;
+ u32 rx_llfcmsgcnt_lo;
+ u32 rx_llfcmsgcnt_hi;
+ } stats_rx;
+};
+
+union mac_stats {
+ struct emac_stats emac_stats;
+ struct bmac1_stats bmac1_stats;
+ struct bmac2_stats bmac2_stats;
+ struct mstat_stats mstat_stats;
+};
+
+
+struct mac_stx {
+ /* in_bad_octets */
+ u32 rx_stat_ifhcinbadoctets_hi;
+ u32 rx_stat_ifhcinbadoctets_lo;
+
+ /* out_bad_octets */
+ u32 tx_stat_ifhcoutbadoctets_hi;
+ u32 tx_stat_ifhcoutbadoctets_lo;
+
+ /* crc_receive_errors */
+ u32 rx_stat_dot3statsfcserrors_hi;
+ u32 rx_stat_dot3statsfcserrors_lo;
+ /* alignment_errors */
+ u32 rx_stat_dot3statsalignmenterrors_hi;
+ u32 rx_stat_dot3statsalignmenterrors_lo;
+ /* carrier_sense_errors */
+ u32 rx_stat_dot3statscarriersenseerrors_hi;
+ u32 rx_stat_dot3statscarriersenseerrors_lo;
+ /* false_carrier_detections */
+ u32 rx_stat_falsecarriererrors_hi;
+ u32 rx_stat_falsecarriererrors_lo;
+
+ /* runt_packets_received */
+ u32 rx_stat_etherstatsundersizepkts_hi;
+ u32 rx_stat_etherstatsundersizepkts_lo;
+ /* jabber_packets_received */
+ u32 rx_stat_dot3statsframestoolong_hi;
+ u32 rx_stat_dot3statsframestoolong_lo;
+
+ /* error_runt_packets_received */
+ u32 rx_stat_etherstatsfragments_hi;
+ u32 rx_stat_etherstatsfragments_lo;
+ /* error_jabber_packets_received */
+ u32 rx_stat_etherstatsjabbers_hi;
+ u32 rx_stat_etherstatsjabbers_lo;
+
+ /* control_frames_received */
+ u32 rx_stat_maccontrolframesreceived_hi;
+ u32 rx_stat_maccontrolframesreceived_lo;
+ u32 rx_stat_mac_xpf_hi;
+ u32 rx_stat_mac_xpf_lo;
+ u32 rx_stat_mac_xcf_hi;
+ u32 rx_stat_mac_xcf_lo;
+
+ /* xoff_state_entered */
+ u32 rx_stat_xoffstateentered_hi;
+ u32 rx_stat_xoffstateentered_lo;
+ /* pause_xon_frames_received */
+ u32 rx_stat_xonpauseframesreceived_hi;
+ u32 rx_stat_xonpauseframesreceived_lo;
+ /* pause_xoff_frames_received */
+ u32 rx_stat_xoffpauseframesreceived_hi;
+ u32 rx_stat_xoffpauseframesreceived_lo;
+ /* pause_xon_frames_transmitted */
+ u32 tx_stat_outxonsent_hi;
+ u32 tx_stat_outxonsent_lo;
+ /* pause_xoff_frames_transmitted */
+ u32 tx_stat_outxoffsent_hi;
+ u32 tx_stat_outxoffsent_lo;
+ /* flow_control_done */
+ u32 tx_stat_flowcontroldone_hi;
+ u32 tx_stat_flowcontroldone_lo;
+
+ /* ether_stats_collisions */
+ u32 tx_stat_etherstatscollisions_hi;
+ u32 tx_stat_etherstatscollisions_lo;
+ /* single_collision_transmit_frames */
+ u32 tx_stat_dot3statssinglecollisionframes_hi;
+ u32 tx_stat_dot3statssinglecollisionframes_lo;
+ /* multiple_collision_transmit_frames */
+ u32 tx_stat_dot3statsmultiplecollisionframes_hi;
+ u32 tx_stat_dot3statsmultiplecollisionframes_lo;
+ /* deferred_transmissions */
+ u32 tx_stat_dot3statsdeferredtransmissions_hi;
+ u32 tx_stat_dot3statsdeferredtransmissions_lo;
+ /* excessive_collision_frames */
+ u32 tx_stat_dot3statsexcessivecollisions_hi;
+ u32 tx_stat_dot3statsexcessivecollisions_lo;
+ /* late_collision_frames */
+ u32 tx_stat_dot3statslatecollisions_hi;
+ u32 tx_stat_dot3statslatecollisions_lo;
+
+ /* frames_transmitted_64_bytes */
+ u32 tx_stat_etherstatspkts64octets_hi;
+ u32 tx_stat_etherstatspkts64octets_lo;
+ /* frames_transmitted_65_127_bytes */
+ u32 tx_stat_etherstatspkts65octetsto127octets_hi;
+ u32 tx_stat_etherstatspkts65octetsto127octets_lo;
+ /* frames_transmitted_128_255_bytes */
+ u32 tx_stat_etherstatspkts128octetsto255octets_hi;
+ u32 tx_stat_etherstatspkts128octetsto255octets_lo;
+ /* frames_transmitted_256_511_bytes */
+ u32 tx_stat_etherstatspkts256octetsto511octets_hi;
+ u32 tx_stat_etherstatspkts256octetsto511octets_lo;
+ /* frames_transmitted_512_1023_bytes */
+ u32 tx_stat_etherstatspkts512octetsto1023octets_hi;
+ u32 tx_stat_etherstatspkts512octetsto1023octets_lo;
+ /* frames_transmitted_1024_1522_bytes */
+ u32 tx_stat_etherstatspkts1024octetsto1522octets_hi;
+ u32 tx_stat_etherstatspkts1024octetsto1522octets_lo;
+ /* frames_transmitted_1523_9022_bytes */
+ u32 tx_stat_etherstatspktsover1522octets_hi;
+ u32 tx_stat_etherstatspktsover1522octets_lo;
+ u32 tx_stat_mac_2047_hi;
+ u32 tx_stat_mac_2047_lo;
+ u32 tx_stat_mac_4095_hi;
+ u32 tx_stat_mac_4095_lo;
+ u32 tx_stat_mac_9216_hi;
+ u32 tx_stat_mac_9216_lo;
+ u32 tx_stat_mac_16383_hi;
+ u32 tx_stat_mac_16383_lo;
+
+ /* internal_mac_transmit_errors */
+ u32 tx_stat_dot3statsinternalmactransmiterrors_hi;
+ u32 tx_stat_dot3statsinternalmactransmiterrors_lo;
+
+ /* if_out_discards */
+ u32 tx_stat_mac_ufl_hi;
+ u32 tx_stat_mac_ufl_lo;
+};
+
+
+#define MAC_STX_IDX_MAX 2
+
+struct host_port_stats {
+ u32 host_port_stats_start;
+
+ struct mac_stx mac_stx[MAC_STX_IDX_MAX];
+
+ u32 brb_drop_hi;
+ u32 brb_drop_lo;
+
+ u32 host_port_stats_end;
+};
+
+
+struct host_func_stats {
+ u32 host_func_stats_start;
+
+ u32 total_bytes_received_hi;
+ u32 total_bytes_received_lo;
+
+ u32 total_bytes_transmitted_hi;
+ u32 total_bytes_transmitted_lo;
+
+ u32 total_unicast_packets_received_hi;
+ u32 total_unicast_packets_received_lo;
+
+ u32 total_multicast_packets_received_hi;
+ u32 total_multicast_packets_received_lo;
+
+ u32 total_broadcast_packets_received_hi;
+ u32 total_broadcast_packets_received_lo;
+
+ u32 total_unicast_packets_transmitted_hi;
+ u32 total_unicast_packets_transmitted_lo;
+
+ u32 total_multicast_packets_transmitted_hi;
+ u32 total_multicast_packets_transmitted_lo;
+
+ u32 total_broadcast_packets_transmitted_hi;
+ u32 total_broadcast_packets_transmitted_lo;
+
+ u32 valid_bytes_received_hi;
+ u32 valid_bytes_received_lo;
+
+ u32 host_func_stats_end;
+};
+
+/* VIC definitions */
+#define VICSTATST_UIF_INDEX 2
+
+#define BCM_5710_FW_MAJOR_VERSION 7
+#define BCM_5710_FW_MINOR_VERSION 0
+#define BCM_5710_FW_REVISION_VERSION 29
+#define BCM_5710_FW_ENGINEERING_VERSION 0
+#define BCM_5710_FW_COMPILE_FLAGS 1
+
+
+/*
+ * attention bits
+ */
+struct atten_sp_status_block {
+ __le32 attn_bits;
+ __le32 attn_bits_ack;
+ u8 status_block_id;
+ u8 reserved0;
+ __le16 attn_bits_index;
+ __le32 reserved1;
+};
+
+
+/*
+ * The eth aggregative context of Cstorm
+ */
+struct cstorm_eth_ag_context {
+ u32 __reserved0[10];
+};
+
+
+/*
+ * dmae command structure
+ */
+struct dmae_command {
+ u32 opcode;
+#define DMAE_COMMAND_SRC (0x1<<0)
+#define DMAE_COMMAND_SRC_SHIFT 0
+#define DMAE_COMMAND_DST (0x3<<1)
+#define DMAE_COMMAND_DST_SHIFT 1
+#define DMAE_COMMAND_C_DST (0x1<<3)
+#define DMAE_COMMAND_C_DST_SHIFT 3
+#define DMAE_COMMAND_C_TYPE_ENABLE (0x1<<4)
+#define DMAE_COMMAND_C_TYPE_ENABLE_SHIFT 4
+#define DMAE_COMMAND_C_TYPE_CRC_ENABLE (0x1<<5)
+#define DMAE_COMMAND_C_TYPE_CRC_ENABLE_SHIFT 5
+#define DMAE_COMMAND_C_TYPE_CRC_OFFSET (0x7<<6)
+#define DMAE_COMMAND_C_TYPE_CRC_OFFSET_SHIFT 6
+#define DMAE_COMMAND_ENDIANITY (0x3<<9)
+#define DMAE_COMMAND_ENDIANITY_SHIFT 9
+#define DMAE_COMMAND_PORT (0x1<<11)
+#define DMAE_COMMAND_PORT_SHIFT 11
+#define DMAE_COMMAND_CRC_RESET (0x1<<12)
+#define DMAE_COMMAND_CRC_RESET_SHIFT 12
+#define DMAE_COMMAND_SRC_RESET (0x1<<13)
+#define DMAE_COMMAND_SRC_RESET_SHIFT 13
+#define DMAE_COMMAND_DST_RESET (0x1<<14)
+#define DMAE_COMMAND_DST_RESET_SHIFT 14
+#define DMAE_COMMAND_E1HVN (0x3<<15)
+#define DMAE_COMMAND_E1HVN_SHIFT 15
+#define DMAE_COMMAND_DST_VN (0x3<<17)
+#define DMAE_COMMAND_DST_VN_SHIFT 17
+#define DMAE_COMMAND_C_FUNC (0x1<<19)
+#define DMAE_COMMAND_C_FUNC_SHIFT 19
+#define DMAE_COMMAND_ERR_POLICY (0x3<<20)
+#define DMAE_COMMAND_ERR_POLICY_SHIFT 20
+#define DMAE_COMMAND_RESERVED0 (0x3FF<<22)
+#define DMAE_COMMAND_RESERVED0_SHIFT 22
+ u32 src_addr_lo;
+ u32 src_addr_hi;
+ u32 dst_addr_lo;
+ u32 dst_addr_hi;
+#if defined(__BIG_ENDIAN)
+ u16 opcode_iov;
+#define DMAE_COMMAND_SRC_VFID (0x3F<<0)
+#define DMAE_COMMAND_SRC_VFID_SHIFT 0
+#define DMAE_COMMAND_SRC_VFPF (0x1<<6)
+#define DMAE_COMMAND_SRC_VFPF_SHIFT 6
+#define DMAE_COMMAND_RESERVED1 (0x1<<7)
+#define DMAE_COMMAND_RESERVED1_SHIFT 7
+#define DMAE_COMMAND_DST_VFID (0x3F<<8)
+#define DMAE_COMMAND_DST_VFID_SHIFT 8
+#define DMAE_COMMAND_DST_VFPF (0x1<<14)
+#define DMAE_COMMAND_DST_VFPF_SHIFT 14
+#define DMAE_COMMAND_RESERVED2 (0x1<<15)
+#define DMAE_COMMAND_RESERVED2_SHIFT 15
+ u16 len;
+#elif defined(__LITTLE_ENDIAN)
+ u16 len;
+ u16 opcode_iov;
+#define DMAE_COMMAND_SRC_VFID (0x3F<<0)
+#define DMAE_COMMAND_SRC_VFID_SHIFT 0
+#define DMAE_COMMAND_SRC_VFPF (0x1<<6)
+#define DMAE_COMMAND_SRC_VFPF_SHIFT 6
+#define DMAE_COMMAND_RESERVED1 (0x1<<7)
+#define DMAE_COMMAND_RESERVED1_SHIFT 7
+#define DMAE_COMMAND_DST_VFID (0x3F<<8)
+#define DMAE_COMMAND_DST_VFID_SHIFT 8
+#define DMAE_COMMAND_DST_VFPF (0x1<<14)
+#define DMAE_COMMAND_DST_VFPF_SHIFT 14
+#define DMAE_COMMAND_RESERVED2 (0x1<<15)
+#define DMAE_COMMAND_RESERVED2_SHIFT 15
+#endif
+ u32 comp_addr_lo;
+ u32 comp_addr_hi;
+ u32 comp_val;
+ u32 crc32;
+ u32 crc32_c;
+#if defined(__BIG_ENDIAN)
+ u16 crc16_c;
+ u16 crc16;
+#elif defined(__LITTLE_ENDIAN)
+ u16 crc16;
+ u16 crc16_c;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 reserved3;
+ u16 crc_t10;
+#elif defined(__LITTLE_ENDIAN)
+ u16 crc_t10;
+ u16 reserved3;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 xsum8;
+ u16 xsum16;
+#elif defined(__LITTLE_ENDIAN)
+ u16 xsum16;
+ u16 xsum8;
+#endif
+};
+
+
+/*
+ * common data for all protocols
+ */
+struct doorbell_hdr {
+ u8 header;
+#define DOORBELL_HDR_RX (0x1<<0)
+#define DOORBELL_HDR_RX_SHIFT 0
+#define DOORBELL_HDR_DB_TYPE (0x1<<1)
+#define DOORBELL_HDR_DB_TYPE_SHIFT 1
+#define DOORBELL_HDR_DPM_SIZE (0x3<<2)
+#define DOORBELL_HDR_DPM_SIZE_SHIFT 2
+#define DOORBELL_HDR_CONN_TYPE (0xF<<4)
+#define DOORBELL_HDR_CONN_TYPE_SHIFT 4
+};
+
+/*
+ * Ethernet doorbell
+ */
+struct eth_tx_doorbell {
+#if defined(__BIG_ENDIAN)
+ u16 npackets;
+ u8 params;
+#define ETH_TX_DOORBELL_NUM_BDS (0x3F<<0)
+#define ETH_TX_DOORBELL_NUM_BDS_SHIFT 0
+#define ETH_TX_DOORBELL_RESERVED_TX_FIN_FLAG (0x1<<6)
+#define ETH_TX_DOORBELL_RESERVED_TX_FIN_FLAG_SHIFT 6
+#define ETH_TX_DOORBELL_SPARE (0x1<<7)
+#define ETH_TX_DOORBELL_SPARE_SHIFT 7
+ struct doorbell_hdr hdr;
+#elif defined(__LITTLE_ENDIAN)
+ struct doorbell_hdr hdr;
+ u8 params;
+#define ETH_TX_DOORBELL_NUM_BDS (0x3F<<0)
+#define ETH_TX_DOORBELL_NUM_BDS_SHIFT 0
+#define ETH_TX_DOORBELL_RESERVED_TX_FIN_FLAG (0x1<<6)
+#define ETH_TX_DOORBELL_RESERVED_TX_FIN_FLAG_SHIFT 6
+#define ETH_TX_DOORBELL_SPARE (0x1<<7)
+#define ETH_TX_DOORBELL_SPARE_SHIFT 7
+ u16 npackets;
+#endif
+};
+
+
+/*
+ * 3 lines. status block
+ */
+struct hc_status_block_e1x {
+ __le16 index_values[HC_SB_MAX_INDICES_E1X];
+ __le16 running_index[HC_SB_MAX_SM];
+ __le32 rsrv[11];
+};
+
+/*
+ * host status block
+ */
+struct host_hc_status_block_e1x {
+ struct hc_status_block_e1x sb;
+};
+
+
+/*
+ * 3 lines. status block
+ */
+struct hc_status_block_e2 {
+ __le16 index_values[HC_SB_MAX_INDICES_E2];
+ __le16 running_index[HC_SB_MAX_SM];
+ __le32 reserved[11];
+};
+
+/*
+ * host status block
+ */
+struct host_hc_status_block_e2 {
+ struct hc_status_block_e2 sb;
+};
+
+
+/*
+ * 5 lines. slow-path status block
+ */
+struct hc_sp_status_block {
+ __le16 index_values[HC_SP_SB_MAX_INDICES];
+ __le16 running_index;
+ __le16 rsrv;
+ u32 rsrv1;
+};
+
+/*
+ * host status block
+ */
+struct host_sp_status_block {
+ struct atten_sp_status_block atten_status_block;
+ struct hc_sp_status_block sp_sb;
+};
+
+
+/*
+ * IGU driver acknowledgment register
+ */
+struct igu_ack_register {
+#if defined(__BIG_ENDIAN)
+ u16 sb_id_and_flags;
+#define IGU_ACK_REGISTER_STATUS_BLOCK_ID (0x1F<<0)
+#define IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT 0
+#define IGU_ACK_REGISTER_STORM_ID (0x7<<5)
+#define IGU_ACK_REGISTER_STORM_ID_SHIFT 5
+#define IGU_ACK_REGISTER_UPDATE_INDEX (0x1<<8)
+#define IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT 8
+#define IGU_ACK_REGISTER_INTERRUPT_MODE (0x3<<9)
+#define IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT 9
+#define IGU_ACK_REGISTER_RESERVED (0x1F<<11)
+#define IGU_ACK_REGISTER_RESERVED_SHIFT 11
+ u16 status_block_index;
+#elif defined(__LITTLE_ENDIAN)
+ u16 status_block_index;
+ u16 sb_id_and_flags;
+#define IGU_ACK_REGISTER_STATUS_BLOCK_ID (0x1F<<0)
+#define IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT 0
+#define IGU_ACK_REGISTER_STORM_ID (0x7<<5)
+#define IGU_ACK_REGISTER_STORM_ID_SHIFT 5
+#define IGU_ACK_REGISTER_UPDATE_INDEX (0x1<<8)
+#define IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT 8
+#define IGU_ACK_REGISTER_INTERRUPT_MODE (0x3<<9)
+#define IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT 9
+#define IGU_ACK_REGISTER_RESERVED (0x1F<<11)
+#define IGU_ACK_REGISTER_RESERVED_SHIFT 11
+#endif
+};
+
+
+/*
+ * IGU driver acknowledgement register
+ */
+struct igu_backward_compatible {
+ u32 sb_id_and_flags;
+#define IGU_BACKWARD_COMPATIBLE_SB_INDEX (0xFFFF<<0)
+#define IGU_BACKWARD_COMPATIBLE_SB_INDEX_SHIFT 0
+#define IGU_BACKWARD_COMPATIBLE_SB_SELECT (0x1F<<16)
+#define IGU_BACKWARD_COMPATIBLE_SB_SELECT_SHIFT 16
+#define IGU_BACKWARD_COMPATIBLE_SEGMENT_ACCESS (0x7<<21)
+#define IGU_BACKWARD_COMPATIBLE_SEGMENT_ACCESS_SHIFT 21
+#define IGU_BACKWARD_COMPATIBLE_BUPDATE (0x1<<24)
+#define IGU_BACKWARD_COMPATIBLE_BUPDATE_SHIFT 24
+#define IGU_BACKWARD_COMPATIBLE_ENABLE_INT (0x3<<25)
+#define IGU_BACKWARD_COMPATIBLE_ENABLE_INT_SHIFT 25
+#define IGU_BACKWARD_COMPATIBLE_RESERVED_0 (0x1F<<27)
+#define IGU_BACKWARD_COMPATIBLE_RESERVED_0_SHIFT 27
+ u32 reserved_2;
+};
+
+
+/*
+ * IGU driver acknowledgement register
+ */
+struct igu_regular {
+ u32 sb_id_and_flags;
+#define IGU_REGULAR_SB_INDEX (0xFFFFF<<0)
+#define IGU_REGULAR_SB_INDEX_SHIFT 0
+#define IGU_REGULAR_RESERVED0 (0x1<<20)
+#define IGU_REGULAR_RESERVED0_SHIFT 20
+#define IGU_REGULAR_SEGMENT_ACCESS (0x7<<21)
+#define IGU_REGULAR_SEGMENT_ACCESS_SHIFT 21
+#define IGU_REGULAR_BUPDATE (0x1<<24)
+#define IGU_REGULAR_BUPDATE_SHIFT 24
+#define IGU_REGULAR_ENABLE_INT (0x3<<25)
+#define IGU_REGULAR_ENABLE_INT_SHIFT 25
+#define IGU_REGULAR_RESERVED_1 (0x1<<27)
+#define IGU_REGULAR_RESERVED_1_SHIFT 27
+#define IGU_REGULAR_CLEANUP_TYPE (0x3<<28)
+#define IGU_REGULAR_CLEANUP_TYPE_SHIFT 28
+#define IGU_REGULAR_CLEANUP_SET (0x1<<30)
+#define IGU_REGULAR_CLEANUP_SET_SHIFT 30
+#define IGU_REGULAR_BCLEANUP (0x1<<31)
+#define IGU_REGULAR_BCLEANUP_SHIFT 31
+ u32 reserved_2;
+};
+
+/*
+ * IGU driver acknowledgement register
+ */
+union igu_consprod_reg {
+ struct igu_regular regular;
+ struct igu_backward_compatible backward_compatible;
+};
+
+
+/*
+ * Igu control commands
+ */
+enum igu_ctrl_cmd {
+ IGU_CTRL_CMD_TYPE_RD,
+ IGU_CTRL_CMD_TYPE_WR,
+ MAX_IGU_CTRL_CMD
+};
+
+
+/*
+ * Control register for the IGU command register
+ */
+struct igu_ctrl_reg {
+ u32 ctrl_data;
+#define IGU_CTRL_REG_ADDRESS (0xFFF<<0)
+#define IGU_CTRL_REG_ADDRESS_SHIFT 0
+#define IGU_CTRL_REG_FID (0x7F<<12)
+#define IGU_CTRL_REG_FID_SHIFT 12
+#define IGU_CTRL_REG_RESERVED (0x1<<19)
+#define IGU_CTRL_REG_RESERVED_SHIFT 19
+#define IGU_CTRL_REG_TYPE (0x1<<20)
+#define IGU_CTRL_REG_TYPE_SHIFT 20
+#define IGU_CTRL_REG_UNUSED (0x7FF<<21)
+#define IGU_CTRL_REG_UNUSED_SHIFT 21
+};
+
+
+/*
+ * Igu interrupt command
+ */
+enum igu_int_cmd {
+ IGU_INT_ENABLE,
+ IGU_INT_DISABLE,
+ IGU_INT_NOP,
+ IGU_INT_NOP2,
+ MAX_IGU_INT_CMD
+};
+
+
+/*
+ * Igu segments
+ */
+enum igu_seg_access {
+ IGU_SEG_ACCESS_NORM,
+ IGU_SEG_ACCESS_DEF,
+ IGU_SEG_ACCESS_ATTN,
+ MAX_IGU_SEG_ACCESS
+};
+
+
+/*
+ * Parser parsing flags field
+ */
+struct parsing_flags {
+ __le16 flags;
+#define PARSING_FLAGS_ETHERNET_ADDRESS_TYPE (0x1<<0)
+#define PARSING_FLAGS_ETHERNET_ADDRESS_TYPE_SHIFT 0
+#define PARSING_FLAGS_VLAN (0x1<<1)
+#define PARSING_FLAGS_VLAN_SHIFT 1
+#define PARSING_FLAGS_EXTRA_VLAN (0x1<<2)
+#define PARSING_FLAGS_EXTRA_VLAN_SHIFT 2
+#define PARSING_FLAGS_OVER_ETHERNET_PROTOCOL (0x3<<3)
+#define PARSING_FLAGS_OVER_ETHERNET_PROTOCOL_SHIFT 3
+#define PARSING_FLAGS_IP_OPTIONS (0x1<<5)
+#define PARSING_FLAGS_IP_OPTIONS_SHIFT 5
+#define PARSING_FLAGS_FRAGMENTATION_STATUS (0x1<<6)
+#define PARSING_FLAGS_FRAGMENTATION_STATUS_SHIFT 6
+#define PARSING_FLAGS_OVER_IP_PROTOCOL (0x3<<7)
+#define PARSING_FLAGS_OVER_IP_PROTOCOL_SHIFT 7
+#define PARSING_FLAGS_PURE_ACK_INDICATION (0x1<<9)
+#define PARSING_FLAGS_PURE_ACK_INDICATION_SHIFT 9
+#define PARSING_FLAGS_TCP_OPTIONS_EXIST (0x1<<10)
+#define PARSING_FLAGS_TCP_OPTIONS_EXIST_SHIFT 10
+#define PARSING_FLAGS_TIME_STAMP_EXIST_FLAG (0x1<<11)
+#define PARSING_FLAGS_TIME_STAMP_EXIST_FLAG_SHIFT 11
+#define PARSING_FLAGS_CONNECTION_MATCH (0x1<<12)
+#define PARSING_FLAGS_CONNECTION_MATCH_SHIFT 12
+#define PARSING_FLAGS_LLC_SNAP (0x1<<13)
+#define PARSING_FLAGS_LLC_SNAP_SHIFT 13
+#define PARSING_FLAGS_RESERVED0 (0x3<<14)
+#define PARSING_FLAGS_RESERVED0_SHIFT 14
+};
+
+
+/*
+ * Parsing flags for TCP ACK type
+ */
+enum prs_flags_ack_type {
+ PRS_FLAG_PUREACK_PIGGY,
+ PRS_FLAG_PUREACK_PURE,
+ MAX_PRS_FLAGS_ACK_TYPE
+};
+
+
+/*
+ * Parsing flags for Ethernet address type
+ */
+enum prs_flags_eth_addr_type {
+ PRS_FLAG_ETHTYPE_NON_UNICAST,
+ PRS_FLAG_ETHTYPE_UNICAST,
+ MAX_PRS_FLAGS_ETH_ADDR_TYPE
+};
+
+
+/*
+ * Parsing flags for over-ethernet protocol
+ */
+enum prs_flags_over_eth {
+ PRS_FLAG_OVERETH_UNKNOWN,
+ PRS_FLAG_OVERETH_IPV4,
+ PRS_FLAG_OVERETH_IPV6,
+ PRS_FLAG_OVERETH_LLCSNAP_UNKNOWN,
+ MAX_PRS_FLAGS_OVER_ETH
+};
+
+
+/*
+ * Parsing flags for over-IP protocol
+ */
+enum prs_flags_over_ip {
+ PRS_FLAG_OVERIP_UNKNOWN,
+ PRS_FLAG_OVERIP_TCP,
+ PRS_FLAG_OVERIP_UDP,
+ MAX_PRS_FLAGS_OVER_IP
+};
+
+
+/*
+ * SDM operation gen command (generate aggregative interrupt)
+ */
+struct sdm_op_gen {
+ __le32 command;
+#define SDM_OP_GEN_COMP_PARAM (0x1F<<0)
+#define SDM_OP_GEN_COMP_PARAM_SHIFT 0
+#define SDM_OP_GEN_COMP_TYPE (0x7<<5)
+#define SDM_OP_GEN_COMP_TYPE_SHIFT 5
+#define SDM_OP_GEN_AGG_VECT_IDX (0xFF<<8)
+#define SDM_OP_GEN_AGG_VECT_IDX_SHIFT 8
+#define SDM_OP_GEN_AGG_VECT_IDX_VALID (0x1<<16)
+#define SDM_OP_GEN_AGG_VECT_IDX_VALID_SHIFT 16
+#define SDM_OP_GEN_RESERVED (0x7FFF<<17)
+#define SDM_OP_GEN_RESERVED_SHIFT 17
+};
+
+
+/*
+ * Timers connection context
+ */
+struct timers_block_context {
+ u32 __reserved_0;
+ u32 __reserved_1;
+ u32 __reserved_2;
+ u32 flags;
+#define __TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS (0x3<<0)
+#define __TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS_SHIFT 0
+#define TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG (0x1<<2)
+#define TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG_SHIFT 2
+#define __TIMERS_BLOCK_CONTEXT_RESERVED0 (0x1FFFFFFF<<3)
+#define __TIMERS_BLOCK_CONTEXT_RESERVED0_SHIFT 3
+};
+
+
+/*
+ * The eth aggregative context of Tstorm
+ */
+struct tstorm_eth_ag_context {
+ u32 __reserved0[14];
+};
+
+
+/*
+ * The eth aggregative context of Ustorm
+ */
+struct ustorm_eth_ag_context {
+ u32 __reserved0;
+#if defined(__BIG_ENDIAN)
+ u8 cdu_usage;
+ u8 __reserved2;
+ u16 __reserved1;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __reserved1;
+ u8 __reserved2;
+ u8 cdu_usage;
+#endif
+ u32 __reserved3[6];
+};
+
+
+/*
+ * The eth aggregative context of Xstorm
+ */
+struct xstorm_eth_ag_context {
+ u32 reserved0;
+#if defined(__BIG_ENDIAN)
+ u8 cdu_reserved;
+ u8 reserved2;
+ u16 reserved1;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved1;
+ u8 reserved2;
+ u8 cdu_reserved;
+#endif
+ u32 reserved3[30];
+};
+
+
+/*
+ * doorbell message sent to the chip
+ */
+struct doorbell {
+#if defined(__BIG_ENDIAN)
+ u16 zero_fill2;
+ u8 zero_fill1;
+ struct doorbell_hdr header;
+#elif defined(__LITTLE_ENDIAN)
+ struct doorbell_hdr header;
+ u8 zero_fill1;
+ u16 zero_fill2;
+#endif
+};
+
+
+/*
+ * doorbell message sent to the chip
+ */
+struct doorbell_set_prod {
+#if defined(__BIG_ENDIAN)
+ u16 prod;
+ u8 zero_fill1;
+ struct doorbell_hdr header;
+#elif defined(__LITTLE_ENDIAN)
+ struct doorbell_hdr header;
+ u8 zero_fill1;
+ u16 prod;
+#endif
+};
+
+
+struct regpair {
+ __le32 lo;
+ __le32 hi;
+};
+
+
+/*
+ * Classify rule opcodes in E2/E3
+ */
+enum classify_rule {
+ CLASSIFY_RULE_OPCODE_MAC,
+ CLASSIFY_RULE_OPCODE_VLAN,
+ CLASSIFY_RULE_OPCODE_PAIR,
+ MAX_CLASSIFY_RULE
+};
+
+
+/*
+ * Classify rule types in E2/E3
+ */
+enum classify_rule_action_type {
+ CLASSIFY_RULE_REMOVE,
+ CLASSIFY_RULE_ADD,
+ MAX_CLASSIFY_RULE_ACTION_TYPE
+};
+
+
+/*
+ * client init ramrod data
+ */
+struct client_init_general_data {
+ u8 client_id;
+ u8 statistics_counter_id;
+ u8 statistics_en_flg;
+ u8 is_fcoe_flg;
+ u8 activate_flg;
+ u8 sp_client_id;
+ __le16 mtu;
+ u8 statistics_zero_flg;
+ u8 func_id;
+ u8 cos;
+ u8 traffic_type;
+ u32 reserved0;
+};
+
+
+/*
+ * client init rx data
+ */
+struct client_init_rx_data {
+ u8 tpa_en;
+#define CLIENT_INIT_RX_DATA_TPA_EN_IPV4 (0x1<<0)
+#define CLIENT_INIT_RX_DATA_TPA_EN_IPV4_SHIFT 0
+#define CLIENT_INIT_RX_DATA_TPA_EN_IPV6 (0x1<<1)
+#define CLIENT_INIT_RX_DATA_TPA_EN_IPV6_SHIFT 1
+#define CLIENT_INIT_RX_DATA_RESERVED5 (0x3F<<2)
+#define CLIENT_INIT_RX_DATA_RESERVED5_SHIFT 2
+ u8 vmqueue_mode_en_flg;
+ u8 extra_data_over_sgl_en_flg;
+ u8 cache_line_alignment_log_size;
+ u8 enable_dynamic_hc;
+ u8 max_sges_for_packet;
+ u8 client_qzone_id;
+ u8 drop_ip_cs_err_flg;
+ u8 drop_tcp_cs_err_flg;
+ u8 drop_ttl0_flg;
+ u8 drop_udp_cs_err_flg;
+ u8 inner_vlan_removal_enable_flg;
+ u8 outer_vlan_removal_enable_flg;
+ u8 status_block_id;
+ u8 rx_sb_index_number;
+ u8 reserved0;
+ u8 max_tpa_queues;
+ u8 silent_vlan_removal_flg;
+ __le16 max_bytes_on_bd;
+ __le16 sge_buff_size;
+ u8 approx_mcast_engine_id;
+ u8 rss_engine_id;
+ struct regpair bd_page_base;
+ struct regpair sge_page_base;
+ struct regpair cqe_page_base;
+ u8 is_leading_rss;
+ u8 is_approx_mcast;
+ __le16 max_agg_size;
+ __le16 state;
+#define CLIENT_INIT_RX_DATA_UCAST_DROP_ALL (0x1<<0)
+#define CLIENT_INIT_RX_DATA_UCAST_DROP_ALL_SHIFT 0
+#define CLIENT_INIT_RX_DATA_UCAST_ACCEPT_ALL (0x1<<1)
+#define CLIENT_INIT_RX_DATA_UCAST_ACCEPT_ALL_SHIFT 1
+#define CLIENT_INIT_RX_DATA_UCAST_ACCEPT_UNMATCHED (0x1<<2)
+#define CLIENT_INIT_RX_DATA_UCAST_ACCEPT_UNMATCHED_SHIFT 2
+#define CLIENT_INIT_RX_DATA_MCAST_DROP_ALL (0x1<<3)
+#define CLIENT_INIT_RX_DATA_MCAST_DROP_ALL_SHIFT 3
+#define CLIENT_INIT_RX_DATA_MCAST_ACCEPT_ALL (0x1<<4)
+#define CLIENT_INIT_RX_DATA_MCAST_ACCEPT_ALL_SHIFT 4
+#define CLIENT_INIT_RX_DATA_BCAST_ACCEPT_ALL (0x1<<5)
+#define CLIENT_INIT_RX_DATA_BCAST_ACCEPT_ALL_SHIFT 5
+#define CLIENT_INIT_RX_DATA_ACCEPT_ANY_VLAN (0x1<<6)
+#define CLIENT_INIT_RX_DATA_ACCEPT_ANY_VLAN_SHIFT 6
+#define CLIENT_INIT_RX_DATA_RESERVED2 (0x1FF<<7)
+#define CLIENT_INIT_RX_DATA_RESERVED2_SHIFT 7
+ __le16 cqe_pause_thr_low;
+ __le16 cqe_pause_thr_high;
+ __le16 bd_pause_thr_low;
+ __le16 bd_pause_thr_high;
+ __le16 sge_pause_thr_low;
+ __le16 sge_pause_thr_high;
+ __le16 rx_cos_mask;
+ __le16 silent_vlan_value;
+ __le16 silent_vlan_mask;
+ __le32 reserved6[2];
+};
+
+/*
+ * client init tx data
+ */
+struct client_init_tx_data {
+ u8 enforce_security_flg;
+ u8 tx_status_block_id;
+ u8 tx_sb_index_number;
+ u8 tss_leading_client_id;
+ u8 tx_switching_flg;
+ u8 anti_spoofing_flg;
+ __le16 default_vlan;
+ struct regpair tx_bd_page_base;
+ __le16 state;
+#define CLIENT_INIT_TX_DATA_UCAST_ACCEPT_ALL (0x1<<0)
+#define CLIENT_INIT_TX_DATA_UCAST_ACCEPT_ALL_SHIFT 0
+#define CLIENT_INIT_TX_DATA_MCAST_ACCEPT_ALL (0x1<<1)
+#define CLIENT_INIT_TX_DATA_MCAST_ACCEPT_ALL_SHIFT 1
+#define CLIENT_INIT_TX_DATA_BCAST_ACCEPT_ALL (0x1<<2)
+#define CLIENT_INIT_TX_DATA_BCAST_ACCEPT_ALL_SHIFT 2
+#define CLIENT_INIT_TX_DATA_ACCEPT_ANY_VLAN (0x1<<3)
+#define CLIENT_INIT_TX_DATA_ACCEPT_ANY_VLAN_SHIFT 3
+#define CLIENT_INIT_TX_DATA_RESERVED1 (0xFFF<<4)
+#define CLIENT_INIT_TX_DATA_RESERVED1_SHIFT 4
+ u8 default_vlan_flg;
+ u8 reserved2;
+ __le32 reserved3;
+};
+
+/*
+ * client init ramrod data
+ */
+struct client_init_ramrod_data {
+ struct client_init_general_data general;
+ struct client_init_rx_data rx;
+ struct client_init_tx_data tx;
+};
+
+
+/*
+ * client update ramrod data
+ */
+struct client_update_ramrod_data {
+ u8 client_id;
+ u8 func_id;
+ u8 inner_vlan_removal_enable_flg;
+ u8 inner_vlan_removal_change_flg;
+ u8 outer_vlan_removal_enable_flg;
+ u8 outer_vlan_removal_change_flg;
+ u8 anti_spoofing_enable_flg;
+ u8 anti_spoofing_change_flg;
+ u8 activate_flg;
+ u8 activate_change_flg;
+ __le16 default_vlan;
+ u8 default_vlan_enable_flg;
+ u8 default_vlan_change_flg;
+ __le16 silent_vlan_value;
+ __le16 silent_vlan_mask;
+ u8 silent_vlan_removal_flg;
+ u8 silent_vlan_change_flg;
+ __le32 echo;
+};
+
+
+/*
+ * The eth storm context of Cstorm
+ */
+struct cstorm_eth_st_context {
+ u32 __reserved0[4];
+};
+
+
+struct double_regpair {
+ u32 regpair0_lo;
+ u32 regpair0_hi;
+ u32 regpair1_lo;
+ u32 regpair1_hi;
+};
+
+
+/*
+ * Ethernet address typesm used in ethernet tx BDs
+ */
+enum eth_addr_type {
+ UNKNOWN_ADDRESS,
+ UNICAST_ADDRESS,
+ MULTICAST_ADDRESS,
+ BROADCAST_ADDRESS,
+ MAX_ETH_ADDR_TYPE
+};
+
+
+/*
+ *
+ */
+struct eth_classify_cmd_header {
+ u8 cmd_general_data;
+#define ETH_CLASSIFY_CMD_HEADER_RX_CMD (0x1<<0)
+#define ETH_CLASSIFY_CMD_HEADER_RX_CMD_SHIFT 0
+#define ETH_CLASSIFY_CMD_HEADER_TX_CMD (0x1<<1)
+#define ETH_CLASSIFY_CMD_HEADER_TX_CMD_SHIFT 1
+#define ETH_CLASSIFY_CMD_HEADER_OPCODE (0x3<<2)
+#define ETH_CLASSIFY_CMD_HEADER_OPCODE_SHIFT 2
+#define ETH_CLASSIFY_CMD_HEADER_IS_ADD (0x1<<4)
+#define ETH_CLASSIFY_CMD_HEADER_IS_ADD_SHIFT 4
+#define ETH_CLASSIFY_CMD_HEADER_RESERVED0 (0x7<<5)
+#define ETH_CLASSIFY_CMD_HEADER_RESERVED0_SHIFT 5
+ u8 func_id;
+ u8 client_id;
+ u8 reserved1;
+};
+
+
+/*
+ * header for eth classification config ramrod
+ */
+struct eth_classify_header {
+ u8 rule_cnt;
+ u8 reserved0;
+ __le16 reserved1;
+ __le32 echo;
+};
+
+
+/*
+ * Command for adding/removing a MAC classification rule
+ */
+struct eth_classify_mac_cmd {
+ struct eth_classify_cmd_header header;
+ __le32 reserved0;
+ __le16 mac_lsb;
+ __le16 mac_mid;
+ __le16 mac_msb;
+ __le16 reserved1;
+};
+
+
+/*
+ * Command for adding/removing a MAC-VLAN pair classification rule
+ */
+struct eth_classify_pair_cmd {
+ struct eth_classify_cmd_header header;
+ __le32 reserved0;
+ __le16 mac_lsb;
+ __le16 mac_mid;
+ __le16 mac_msb;
+ __le16 vlan;
+};
+
+
+/*
+ * Command for adding/removing a VLAN classification rule
+ */
+struct eth_classify_vlan_cmd {
+ struct eth_classify_cmd_header header;
+ __le32 reserved0;
+ __le32 reserved1;
+ __le16 reserved2;
+ __le16 vlan;
+};
+
+/*
+ * union for eth classification rule
+ */
+union eth_classify_rule_cmd {
+ struct eth_classify_mac_cmd mac;
+ struct eth_classify_vlan_cmd vlan;
+ struct eth_classify_pair_cmd pair;
+};
+
+/*
+ * parameters for eth classification configuration ramrod
+ */
+struct eth_classify_rules_ramrod_data {
+ struct eth_classify_header header;
+ union eth_classify_rule_cmd rules[CLASSIFY_RULES_COUNT];
+};
+
+
+/*
+ * The data contain client ID need to the ramrod
+ */
+struct eth_common_ramrod_data {
+ __le32 client_id;
+ __le32 reserved1;
+};
+
+
+/*
+ * The eth storm context of Ustorm
+ */
+struct ustorm_eth_st_context {
+ u32 reserved0[52];
+};
+
+/*
+ * The eth storm context of Tstorm
+ */
+struct tstorm_eth_st_context {
+ u32 __reserved0[28];
+};
+
+/*
+ * The eth storm context of Xstorm
+ */
+struct xstorm_eth_st_context {
+ u32 reserved0[60];
+};
+
+/*
+ * Ethernet connection context
+ */
+struct eth_context {
+ struct ustorm_eth_st_context ustorm_st_context;
+ struct tstorm_eth_st_context tstorm_st_context;
+ struct xstorm_eth_ag_context xstorm_ag_context;
+ struct tstorm_eth_ag_context tstorm_ag_context;
+ struct cstorm_eth_ag_context cstorm_ag_context;
+ struct ustorm_eth_ag_context ustorm_ag_context;
+ struct timers_block_context timers_context;
+ struct xstorm_eth_st_context xstorm_st_context;
+ struct cstorm_eth_st_context cstorm_st_context;
+};
+
+
+/*
+ * union for sgl and raw data.
+ */
+union eth_sgl_or_raw_data {
+ __le16 sgl[8];
+ u32 raw_data[4];
+};
+
+/*
+ * eth FP end aggregation CQE parameters struct
+ */
+struct eth_end_agg_rx_cqe {
+ u8 type_error_flags;
+#define ETH_END_AGG_RX_CQE_TYPE (0x3<<0)
+#define ETH_END_AGG_RX_CQE_TYPE_SHIFT 0
+#define ETH_END_AGG_RX_CQE_SGL_RAW_SEL (0x1<<2)
+#define ETH_END_AGG_RX_CQE_SGL_RAW_SEL_SHIFT 2
+#define ETH_END_AGG_RX_CQE_RESERVED0 (0x1F<<3)
+#define ETH_END_AGG_RX_CQE_RESERVED0_SHIFT 3
+ u8 reserved1;
+ u8 queue_index;
+ u8 reserved2;
+ __le32 timestamp_delta;
+ __le16 num_of_coalesced_segs;
+ __le16 pkt_len;
+ u8 pure_ack_count;
+ u8 reserved3;
+ __le16 reserved4;
+ union eth_sgl_or_raw_data sgl_or_raw_data;
+ __le32 reserved5[8];
+};
+
+
+/*
+ * regular eth FP CQE parameters struct
+ */
+struct eth_fast_path_rx_cqe {
+ u8 type_error_flags;
+#define ETH_FAST_PATH_RX_CQE_TYPE (0x3<<0)
+#define ETH_FAST_PATH_RX_CQE_TYPE_SHIFT 0
+#define ETH_FAST_PATH_RX_CQE_SGL_RAW_SEL (0x1<<2)
+#define ETH_FAST_PATH_RX_CQE_SGL_RAW_SEL_SHIFT 2
+#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG (0x1<<3)
+#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG_SHIFT 3
+#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG (0x1<<4)
+#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG_SHIFT 4
+#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG (0x1<<5)
+#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG_SHIFT 5
+#define ETH_FAST_PATH_RX_CQE_RESERVED0 (0x3<<6)
+#define ETH_FAST_PATH_RX_CQE_RESERVED0_SHIFT 6
+ u8 status_flags;
+#define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE (0x7<<0)
+#define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE_SHIFT 0
+#define ETH_FAST_PATH_RX_CQE_RSS_HASH_FLG (0x1<<3)
+#define ETH_FAST_PATH_RX_CQE_RSS_HASH_FLG_SHIFT 3
+#define ETH_FAST_PATH_RX_CQE_BROADCAST_FLG (0x1<<4)
+#define ETH_FAST_PATH_RX_CQE_BROADCAST_FLG_SHIFT 4
+#define ETH_FAST_PATH_RX_CQE_MAC_MATCH_FLG (0x1<<5)
+#define ETH_FAST_PATH_RX_CQE_MAC_MATCH_FLG_SHIFT 5
+#define ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG (0x1<<6)
+#define ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG_SHIFT 6
+#define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG (0x1<<7)
+#define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG_SHIFT 7
+ u8 queue_index;
+ u8 placement_offset;
+ __le32 rss_hash_result;
+ __le16 vlan_tag;
+ __le16 pkt_len;
+ __le16 len_on_bd;
+ struct parsing_flags pars_flags;
+ union eth_sgl_or_raw_data sgl_or_raw_data;
+ __le32 reserved1[8];
+};
+
+
+/*
+ * Command for setting classification flags for a client
+ */
+struct eth_filter_rules_cmd {
+ u8 cmd_general_data;
+#define ETH_FILTER_RULES_CMD_RX_CMD (0x1<<0)
+#define ETH_FILTER_RULES_CMD_RX_CMD_SHIFT 0
+#define ETH_FILTER_RULES_CMD_TX_CMD (0x1<<1)
+#define ETH_FILTER_RULES_CMD_TX_CMD_SHIFT 1
+#define ETH_FILTER_RULES_CMD_RESERVED0 (0x3F<<2)
+#define ETH_FILTER_RULES_CMD_RESERVED0_SHIFT 2
+ u8 func_id;
+ u8 client_id;
+ u8 reserved1;
+ __le16 state;
+#define ETH_FILTER_RULES_CMD_UCAST_DROP_ALL (0x1<<0)
+#define ETH_FILTER_RULES_CMD_UCAST_DROP_ALL_SHIFT 0
+#define ETH_FILTER_RULES_CMD_UCAST_ACCEPT_ALL (0x1<<1)
+#define ETH_FILTER_RULES_CMD_UCAST_ACCEPT_ALL_SHIFT 1
+#define ETH_FILTER_RULES_CMD_UCAST_ACCEPT_UNMATCHED (0x1<<2)
+#define ETH_FILTER_RULES_CMD_UCAST_ACCEPT_UNMATCHED_SHIFT 2
+#define ETH_FILTER_RULES_CMD_MCAST_DROP_ALL (0x1<<3)
+#define ETH_FILTER_RULES_CMD_MCAST_DROP_ALL_SHIFT 3
+#define ETH_FILTER_RULES_CMD_MCAST_ACCEPT_ALL (0x1<<4)
+#define ETH_FILTER_RULES_CMD_MCAST_ACCEPT_ALL_SHIFT 4
+#define ETH_FILTER_RULES_CMD_BCAST_ACCEPT_ALL (0x1<<5)
+#define ETH_FILTER_RULES_CMD_BCAST_ACCEPT_ALL_SHIFT 5
+#define ETH_FILTER_RULES_CMD_ACCEPT_ANY_VLAN (0x1<<6)
+#define ETH_FILTER_RULES_CMD_ACCEPT_ANY_VLAN_SHIFT 6
+#define ETH_FILTER_RULES_CMD_RESERVED2 (0x1FF<<7)
+#define ETH_FILTER_RULES_CMD_RESERVED2_SHIFT 7
+ __le16 reserved3;
+ struct regpair reserved4;
+};
+
+
+/*
+ * parameters for eth classification filters ramrod
+ */
+struct eth_filter_rules_ramrod_data {
+ struct eth_classify_header header;
+ struct eth_filter_rules_cmd rules[FILTER_RULES_COUNT];
+};
+
+
+/*
+ * parameters for eth classification configuration ramrod
+ */
+struct eth_general_rules_ramrod_data {
+ struct eth_classify_header header;
+ union eth_classify_rule_cmd rules[CLASSIFY_RULES_COUNT];
+};
+
+
+/*
+ * The data for Halt ramrod
+ */
+struct eth_halt_ramrod_data {
+ __le32 client_id;
+ __le32 reserved0;
+};
+
+
+/*
+ * Command for setting multicast classification for a client
+ */
+struct eth_multicast_rules_cmd {
+ u8 cmd_general_data;
+#define ETH_MULTICAST_RULES_CMD_RX_CMD (0x1<<0)
+#define ETH_MULTICAST_RULES_CMD_RX_CMD_SHIFT 0
+#define ETH_MULTICAST_RULES_CMD_TX_CMD (0x1<<1)
+#define ETH_MULTICAST_RULES_CMD_TX_CMD_SHIFT 1
+#define ETH_MULTICAST_RULES_CMD_IS_ADD (0x1<<2)
+#define ETH_MULTICAST_RULES_CMD_IS_ADD_SHIFT 2
+#define ETH_MULTICAST_RULES_CMD_RESERVED0 (0x1F<<3)
+#define ETH_MULTICAST_RULES_CMD_RESERVED0_SHIFT 3
+ u8 func_id;
+ u8 bin_id;
+ u8 engine_id;
+ __le32 reserved2;
+ struct regpair reserved3;
+};
+
+
+/*
+ * parameters for multicast classification ramrod
+ */
+struct eth_multicast_rules_ramrod_data {
+ struct eth_classify_header header;
+ struct eth_multicast_rules_cmd rules[MULTICAST_RULES_COUNT];
+};
+
+
+/*
+ * Place holder for ramrods protocol specific data
+ */
+struct ramrod_data {
+ __le32 data_lo;
+ __le32 data_hi;
+};
+
+/*
+ * union for ramrod data for Ethernet protocol (CQE) (force size of 16 bits)
+ */
+union eth_ramrod_data {
+ struct ramrod_data general;
+};
+
+
+/*
+ * RSS toeplitz hash type, as reported in CQE
+ */
+enum eth_rss_hash_type {
+ DEFAULT_HASH_TYPE,
+ IPV4_HASH_TYPE,
+ TCP_IPV4_HASH_TYPE,
+ IPV6_HASH_TYPE,
+ TCP_IPV6_HASH_TYPE,
+ VLAN_PRI_HASH_TYPE,
+ E1HOV_PRI_HASH_TYPE,
+ DSCP_HASH_TYPE,
+ MAX_ETH_RSS_HASH_TYPE
+};
+
+
+/*
+ * Ethernet RSS mode
+ */
+enum eth_rss_mode {
+ ETH_RSS_MODE_DISABLED,
+ ETH_RSS_MODE_REGULAR,
+ ETH_RSS_MODE_VLAN_PRI,
+ ETH_RSS_MODE_E1HOV_PRI,
+ ETH_RSS_MODE_IP_DSCP,
+ MAX_ETH_RSS_MODE
+};
+
+
+/*
+ * parameters for RSS update ramrod (E2)
+ */
+struct eth_rss_update_ramrod_data {
+ u8 rss_engine_id;
+ u8 capabilities;
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV4_CAPABILITY (0x1<<0)
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV4_CAPABILITY_SHIFT 0
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV4_TCP_CAPABILITY (0x1<<1)
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV4_TCP_CAPABILITY_SHIFT 1
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV4_UDP_CAPABILITY (0x1<<2)
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV4_UDP_CAPABILITY_SHIFT 2
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV6_CAPABILITY (0x1<<3)
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV6_CAPABILITY_SHIFT 3
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV6_TCP_CAPABILITY (0x1<<4)
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV6_TCP_CAPABILITY_SHIFT 4
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV6_UDP_CAPABILITY (0x1<<5)
+#define ETH_RSS_UPDATE_RAMROD_DATA_IPV6_UDP_CAPABILITY_SHIFT 5
+#define ETH_RSS_UPDATE_RAMROD_DATA_UPDATE_RSS_KEY (0x1<<6)
+#define ETH_RSS_UPDATE_RAMROD_DATA_UPDATE_RSS_KEY_SHIFT 6
+#define __ETH_RSS_UPDATE_RAMROD_DATA_RESERVED0 (0x1<<7)
+#define __ETH_RSS_UPDATE_RAMROD_DATA_RESERVED0_SHIFT 7
+ u8 rss_result_mask;
+ u8 rss_mode;
+ __le32 __reserved2;
+ u8 indirection_table[T_ETH_INDIRECTION_TABLE_SIZE];
+ __le32 rss_key[T_ETH_RSS_KEY];
+ __le32 echo;
+ __le32 reserved3;
+};
+
+
+/*
+ * The eth Rx Buffer Descriptor
+ */
+struct eth_rx_bd {
+ __le32 addr_lo;
+ __le32 addr_hi;
+};
+
+
+/*
+ * Eth Rx Cqe structure- general structure for ramrods
+ */
+struct common_ramrod_eth_rx_cqe {
+ u8 ramrod_type;
+#define COMMON_RAMROD_ETH_RX_CQE_TYPE (0x3<<0)
+#define COMMON_RAMROD_ETH_RX_CQE_TYPE_SHIFT 0
+#define COMMON_RAMROD_ETH_RX_CQE_ERROR (0x1<<2)
+#define COMMON_RAMROD_ETH_RX_CQE_ERROR_SHIFT 2
+#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0 (0x1F<<3)
+#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0_SHIFT 3
+ u8 conn_type;
+ __le16 reserved1;
+ __le32 conn_and_cmd_data;
+#define COMMON_RAMROD_ETH_RX_CQE_CID (0xFFFFFF<<0)
+#define COMMON_RAMROD_ETH_RX_CQE_CID_SHIFT 0
+#define COMMON_RAMROD_ETH_RX_CQE_CMD_ID (0xFF<<24)
+#define COMMON_RAMROD_ETH_RX_CQE_CMD_ID_SHIFT 24
+ struct ramrod_data protocol_data;
+ __le32 echo;
+ __le32 reserved2[11];
+};
+
+/*
+ * Rx Last CQE in page (in ETH)
+ */
+struct eth_rx_cqe_next_page {
+ __le32 addr_lo;
+ __le32 addr_hi;
+ __le32 reserved[14];
+};
+
+/*
+ * union for all eth rx cqe types (fix their sizes)
+ */
+union eth_rx_cqe {
+ struct eth_fast_path_rx_cqe fast_path_cqe;
+ struct common_ramrod_eth_rx_cqe ramrod_cqe;
+ struct eth_rx_cqe_next_page next_page_cqe;
+ struct eth_end_agg_rx_cqe end_agg_cqe;
+};
+
+
+/*
+ * Values for RX ETH CQE type field
+ */
+enum eth_rx_cqe_type {
+ RX_ETH_CQE_TYPE_ETH_FASTPATH,
+ RX_ETH_CQE_TYPE_ETH_RAMROD,
+ RX_ETH_CQE_TYPE_ETH_START_AGG,
+ RX_ETH_CQE_TYPE_ETH_STOP_AGG,
+ MAX_ETH_RX_CQE_TYPE
+};
+
+
+/*
+ * Type of SGL/Raw field in ETH RX fast path CQE
+ */
+enum eth_rx_fp_sel {
+ ETH_FP_CQE_REGULAR,
+ ETH_FP_CQE_RAW,
+ MAX_ETH_RX_FP_SEL
+};
+
+
+/*
+ * The eth Rx SGE Descriptor
+ */
+struct eth_rx_sge {
+ __le32 addr_lo;
+ __le32 addr_hi;
+};
+
+
+/*
+ * common data for all protocols
+ */
+struct spe_hdr {
+ __le32 conn_and_cmd_data;
+#define SPE_HDR_CID (0xFFFFFF<<0)
+#define SPE_HDR_CID_SHIFT 0
+#define SPE_HDR_CMD_ID (0xFF<<24)
+#define SPE_HDR_CMD_ID_SHIFT 24
+ __le16 type;
+#define SPE_HDR_CONN_TYPE (0xFF<<0)
+#define SPE_HDR_CONN_TYPE_SHIFT 0
+#define SPE_HDR_FUNCTION_ID (0xFF<<8)
+#define SPE_HDR_FUNCTION_ID_SHIFT 8
+ __le16 reserved1;
+};
+
+/*
+ * specific data for ethernet slow path element
+ */
+union eth_specific_data {
+ u8 protocol_data[8];
+ struct regpair client_update_ramrod_data;
+ struct regpair client_init_ramrod_init_data;
+ struct eth_halt_ramrod_data halt_ramrod_data;
+ struct regpair update_data_addr;
+ struct eth_common_ramrod_data common_ramrod_data;
+ struct regpair classify_cfg_addr;
+ struct regpair filter_cfg_addr;
+ struct regpair mcast_cfg_addr;
+};
+
+/*
+ * Ethernet slow path element
+ */
+struct eth_spe {
+ struct spe_hdr hdr;
+ union eth_specific_data data;
+};
+
+
+/*
+ * Ethernet command ID for slow path elements
+ */
+enum eth_spqe_cmd_id {
+ RAMROD_CMD_ID_ETH_UNUSED,
+ RAMROD_CMD_ID_ETH_CLIENT_SETUP,
+ RAMROD_CMD_ID_ETH_HALT,
+ RAMROD_CMD_ID_ETH_FORWARD_SETUP,
+ RAMROD_CMD_ID_ETH_TX_QUEUE_SETUP,
+ RAMROD_CMD_ID_ETH_CLIENT_UPDATE,
+ RAMROD_CMD_ID_ETH_EMPTY,
+ RAMROD_CMD_ID_ETH_TERMINATE,
+ RAMROD_CMD_ID_ETH_TPA_UPDATE,
+ RAMROD_CMD_ID_ETH_CLASSIFICATION_RULES,
+ RAMROD_CMD_ID_ETH_FILTER_RULES,
+ RAMROD_CMD_ID_ETH_MULTICAST_RULES,
+ RAMROD_CMD_ID_ETH_RSS_UPDATE,
+ RAMROD_CMD_ID_ETH_SET_MAC,
+ MAX_ETH_SPQE_CMD_ID
+};
+
+
+/*
+ * eth tpa update command
+ */
+enum eth_tpa_update_command {
+ TPA_UPDATE_NONE_COMMAND,
+ TPA_UPDATE_ENABLE_COMMAND,
+ TPA_UPDATE_DISABLE_COMMAND,
+ MAX_ETH_TPA_UPDATE_COMMAND
+};
+
+
+/*
+ * Tx regular BD structure
+ */
+struct eth_tx_bd {
+ __le32 addr_lo;
+ __le32 addr_hi;
+ __le16 total_pkt_bytes;
+ __le16 nbytes;
+ u8 reserved[4];
+};
+
+
+/*
+ * structure for easy accessibility to assembler
+ */
+struct eth_tx_bd_flags {
+ u8 as_bitfield;
+#define ETH_TX_BD_FLAGS_IP_CSUM (0x1<<0)
+#define ETH_TX_BD_FLAGS_IP_CSUM_SHIFT 0
+#define ETH_TX_BD_FLAGS_L4_CSUM (0x1<<1)
+#define ETH_TX_BD_FLAGS_L4_CSUM_SHIFT 1
+#define ETH_TX_BD_FLAGS_VLAN_MODE (0x3<<2)
+#define ETH_TX_BD_FLAGS_VLAN_MODE_SHIFT 2
+#define ETH_TX_BD_FLAGS_START_BD (0x1<<4)
+#define ETH_TX_BD_FLAGS_START_BD_SHIFT 4
+#define ETH_TX_BD_FLAGS_IS_UDP (0x1<<5)
+#define ETH_TX_BD_FLAGS_IS_UDP_SHIFT 5
+#define ETH_TX_BD_FLAGS_SW_LSO (0x1<<6)
+#define ETH_TX_BD_FLAGS_SW_LSO_SHIFT 6
+#define ETH_TX_BD_FLAGS_IPV6 (0x1<<7)
+#define ETH_TX_BD_FLAGS_IPV6_SHIFT 7
+};
+
+/*
+ * The eth Tx Buffer Descriptor
+ */
+struct eth_tx_start_bd {
+ __le32 addr_lo;
+ __le32 addr_hi;
+ __le16 nbd;
+ __le16 nbytes;
+ __le16 vlan_or_ethertype;
+ struct eth_tx_bd_flags bd_flags;
+ u8 general_data;
+#define ETH_TX_START_BD_HDR_NBDS (0xF<<0)
+#define ETH_TX_START_BD_HDR_NBDS_SHIFT 0
+#define ETH_TX_START_BD_FORCE_VLAN_MODE (0x1<<4)
+#define ETH_TX_START_BD_FORCE_VLAN_MODE_SHIFT 4
+#define ETH_TX_START_BD_RESREVED (0x1<<5)
+#define ETH_TX_START_BD_RESREVED_SHIFT 5
+#define ETH_TX_START_BD_ETH_ADDR_TYPE (0x3<<6)
+#define ETH_TX_START_BD_ETH_ADDR_TYPE_SHIFT 6
+};
+
+/*
+ * Tx parsing BD structure for ETH E1/E1h
+ */
+struct eth_tx_parse_bd_e1x {
+ u8 global_data;
+#define ETH_TX_PARSE_BD_E1X_IP_HDR_START_OFFSET_W (0xF<<0)
+#define ETH_TX_PARSE_BD_E1X_IP_HDR_START_OFFSET_W_SHIFT 0
+#define ETH_TX_PARSE_BD_E1X_RESERVED0 (0x1<<4)
+#define ETH_TX_PARSE_BD_E1X_RESERVED0_SHIFT 4
+#define ETH_TX_PARSE_BD_E1X_PSEUDO_CS_WITHOUT_LEN (0x1<<5)
+#define ETH_TX_PARSE_BD_E1X_PSEUDO_CS_WITHOUT_LEN_SHIFT 5
+#define ETH_TX_PARSE_BD_E1X_LLC_SNAP_EN (0x1<<6)
+#define ETH_TX_PARSE_BD_E1X_LLC_SNAP_EN_SHIFT 6
+#define ETH_TX_PARSE_BD_E1X_NS_FLG (0x1<<7)
+#define ETH_TX_PARSE_BD_E1X_NS_FLG_SHIFT 7
+ u8 tcp_flags;
+#define ETH_TX_PARSE_BD_E1X_FIN_FLG (0x1<<0)
+#define ETH_TX_PARSE_BD_E1X_FIN_FLG_SHIFT 0
+#define ETH_TX_PARSE_BD_E1X_SYN_FLG (0x1<<1)
+#define ETH_TX_PARSE_BD_E1X_SYN_FLG_SHIFT 1
+#define ETH_TX_PARSE_BD_E1X_RST_FLG (0x1<<2)
+#define ETH_TX_PARSE_BD_E1X_RST_FLG_SHIFT 2
+#define ETH_TX_PARSE_BD_E1X_PSH_FLG (0x1<<3)
+#define ETH_TX_PARSE_BD_E1X_PSH_FLG_SHIFT 3
+#define ETH_TX_PARSE_BD_E1X_ACK_FLG (0x1<<4)
+#define ETH_TX_PARSE_BD_E1X_ACK_FLG_SHIFT 4
+#define ETH_TX_PARSE_BD_E1X_URG_FLG (0x1<<5)
+#define ETH_TX_PARSE_BD_E1X_URG_FLG_SHIFT 5
+#define ETH_TX_PARSE_BD_E1X_ECE_FLG (0x1<<6)
+#define ETH_TX_PARSE_BD_E1X_ECE_FLG_SHIFT 6
+#define ETH_TX_PARSE_BD_E1X_CWR_FLG (0x1<<7)
+#define ETH_TX_PARSE_BD_E1X_CWR_FLG_SHIFT 7
+ u8 ip_hlen_w;
+ s8 reserved;
+ __le16 total_hlen_w;
+ __le16 tcp_pseudo_csum;
+ __le16 lso_mss;
+ __le16 ip_id;
+ __le32 tcp_send_seq;
+};
+
+/*
+ * Tx parsing BD structure for ETH E2
+ */
+struct eth_tx_parse_bd_e2 {
+ __le16 dst_mac_addr_lo;
+ __le16 dst_mac_addr_mid;
+ __le16 dst_mac_addr_hi;
+ __le16 src_mac_addr_lo;
+ __le16 src_mac_addr_mid;
+ __le16 src_mac_addr_hi;
+ __le32 parsing_data;
+#define ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W (0x1FFF<<0)
+#define ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W_SHIFT 0
+#define ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW (0xF<<13)
+#define ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW_SHIFT 13
+#define ETH_TX_PARSE_BD_E2_LSO_MSS (0x3FFF<<17)
+#define ETH_TX_PARSE_BD_E2_LSO_MSS_SHIFT 17
+#define ETH_TX_PARSE_BD_E2_IPV6_WITH_EXT_HDR (0x1<<31)
+#define ETH_TX_PARSE_BD_E2_IPV6_WITH_EXT_HDR_SHIFT 31
+};
+
+/*
+ * The last BD in the BD memory will hold a pointer to the next BD memory
+ */
+struct eth_tx_next_bd {
+ __le32 addr_lo;
+ __le32 addr_hi;
+ u8 reserved[8];
+};
+
+/*
+ * union for 4 Bd types
+ */
+union eth_tx_bd_types {
+ struct eth_tx_start_bd start_bd;
+ struct eth_tx_bd reg_bd;
+ struct eth_tx_parse_bd_e1x parse_bd_e1x;
+ struct eth_tx_parse_bd_e2 parse_bd_e2;
+ struct eth_tx_next_bd next_bd;
+};
+
+/*
+ * array of 13 bds as appears in the eth xstorm context
+ */
+struct eth_tx_bds_array {
+ union eth_tx_bd_types bds[13];
+};
+
+
+/*
+ * VLAN mode on TX BDs
+ */
+enum eth_tx_vlan_type {
+ X_ETH_NO_VLAN,
+ X_ETH_OUTBAND_VLAN,
+ X_ETH_INBAND_VLAN,
+ X_ETH_FW_ADDED_VLAN,
+ MAX_ETH_TX_VLAN_TYPE
+};
+
+
+/*
+ * Ethernet VLAN filtering mode in E1x
+ */
+enum eth_vlan_filter_mode {
+ ETH_VLAN_FILTER_ANY_VLAN,
+ ETH_VLAN_FILTER_SPECIFIC_VLAN,
+ ETH_VLAN_FILTER_CLASSIFY,
+ MAX_ETH_VLAN_FILTER_MODE
+};
+
+
+/*
+ * MAC filtering configuration command header
+ */
+struct mac_configuration_hdr {
+ u8 length;
+ u8 offset;
+ __le16 client_id;
+ __le32 echo;
+};
+
+/*
+ * MAC address in list for ramrod
+ */
+struct mac_configuration_entry {
+ __le16 lsb_mac_addr;
+ __le16 middle_mac_addr;
+ __le16 msb_mac_addr;
+ __le16 vlan_id;
+ u8 pf_id;
+ u8 flags;
+#define MAC_CONFIGURATION_ENTRY_ACTION_TYPE (0x1<<0)
+#define MAC_CONFIGURATION_ENTRY_ACTION_TYPE_SHIFT 0
+#define MAC_CONFIGURATION_ENTRY_RDMA_MAC (0x1<<1)
+#define MAC_CONFIGURATION_ENTRY_RDMA_MAC_SHIFT 1
+#define MAC_CONFIGURATION_ENTRY_VLAN_FILTERING_MODE (0x3<<2)
+#define MAC_CONFIGURATION_ENTRY_VLAN_FILTERING_MODE_SHIFT 2
+#define MAC_CONFIGURATION_ENTRY_OVERRIDE_VLAN_REMOVAL (0x1<<4)
+#define MAC_CONFIGURATION_ENTRY_OVERRIDE_VLAN_REMOVAL_SHIFT 4
+#define MAC_CONFIGURATION_ENTRY_BROADCAST (0x1<<5)
+#define MAC_CONFIGURATION_ENTRY_BROADCAST_SHIFT 5
+#define MAC_CONFIGURATION_ENTRY_RESERVED1 (0x3<<6)
+#define MAC_CONFIGURATION_ENTRY_RESERVED1_SHIFT 6
+ __le16 reserved0;
+ __le32 clients_bit_vector;
+};
+
+/*
+ * MAC filtering configuration command
+ */
+struct mac_configuration_cmd {
+ struct mac_configuration_hdr hdr;
+ struct mac_configuration_entry config_table[64];
+};
+
+
+/*
+ * Set-MAC command type (in E1x)
+ */
+enum set_mac_action_type {
+ T_ETH_MAC_COMMAND_INVALIDATE,
+ T_ETH_MAC_COMMAND_SET,
+ MAX_SET_MAC_ACTION_TYPE
+};
+
+
+/*
+ * tpa update ramrod data
+ */
+struct tpa_update_ramrod_data {
+ u8 update_ipv4;
+ u8 update_ipv6;
+ u8 client_id;
+ u8 max_tpa_queues;
+ u8 max_sges_for_packet;
+ u8 complete_on_both_clients;
+ __le16 reserved1;
+ __le16 sge_buff_size;
+ __le16 max_agg_size;
+ __le32 sge_page_base_lo;
+ __le32 sge_page_base_hi;
+ __le16 sge_pause_thr_low;
+ __le16 sge_pause_thr_high;
+};
+
+
+/*
+ * approximate-match multicast filtering for E1H per function in Tstorm
+ */
+struct tstorm_eth_approximate_match_multicast_filtering {
+ u32 mcast_add_hash_bit_array[8];
+};
+
+
+/*
+ * Common configuration parameters per function in Tstorm
+ */
+struct tstorm_eth_function_common_config {
+ __le16 config_flags;
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY (0x1<<0)
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY_SHIFT 0
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY (0x1<<1)
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY_SHIFT 1
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY (0x1<<2)
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY_SHIFT 2
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY (0x1<<3)
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY_SHIFT 3
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_MODE (0x7<<4)
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_MODE_SHIFT 4
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_FILTERING_ENABLE (0x1<<7)
+#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_FILTERING_ENABLE_SHIFT 7
+#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0 (0xFF<<8)
+#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0_SHIFT 8
+ u8 rss_result_mask;
+ u8 reserved1;
+ __le16 vlan_id[2];
+};
+
+
+/*
+ * MAC filtering configuration parameters per port in Tstorm
+ */
+struct tstorm_eth_mac_filter_config {
+ __le32 ucast_drop_all;
+ __le32 ucast_accept_all;
+ __le32 mcast_drop_all;
+ __le32 mcast_accept_all;
+ __le32 bcast_accept_all;
+ __le32 vlan_filter[2];
+ __le32 unmatched_unicast;
+};
+
+
+/*
+ * tx only queue init ramrod data
+ */
+struct tx_queue_init_ramrod_data {
+ struct client_init_general_data general;
+ struct client_init_tx_data tx;
+};
+
+
+/*
+ * Three RX producers for ETH
+ */
+struct ustorm_eth_rx_producers {
+#if defined(__BIG_ENDIAN)
+ u16 bd_prod;
+ u16 cqe_prod;
+#elif defined(__LITTLE_ENDIAN)
+ u16 cqe_prod;
+ u16 bd_prod;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 reserved;
+ u16 sge_prod;
+#elif defined(__LITTLE_ENDIAN)
+ u16 sge_prod;
+ u16 reserved;
+#endif
+};
+
+
+/*
+ * cfc delete event data
+ */
+struct cfc_del_event_data {
+ u32 cid;
+ u32 reserved0;
+ u32 reserved1;
+};
+
+
+/*
+ * per-port SAFC demo variables
+ */
+struct cmng_flags_per_port {
+ u32 cmng_enables;
+#define CMNG_FLAGS_PER_PORT_FAIRNESS_VN (0x1<<0)
+#define CMNG_FLAGS_PER_PORT_FAIRNESS_VN_SHIFT 0
+#define CMNG_FLAGS_PER_PORT_RATE_SHAPING_VN (0x1<<1)
+#define CMNG_FLAGS_PER_PORT_RATE_SHAPING_VN_SHIFT 1
+#define CMNG_FLAGS_PER_PORT_FAIRNESS_COS (0x1<<2)
+#define CMNG_FLAGS_PER_PORT_FAIRNESS_COS_SHIFT 2
+#define CMNG_FLAGS_PER_PORT_FAIRNESS_COS_MODE (0x1<<3)
+#define CMNG_FLAGS_PER_PORT_FAIRNESS_COS_MODE_SHIFT 3
+#define __CMNG_FLAGS_PER_PORT_RESERVED0 (0xFFFFFFF<<4)
+#define __CMNG_FLAGS_PER_PORT_RESERVED0_SHIFT 4
+ u32 __reserved1;
+};
+
+
+/*
+ * per-port rate shaping variables
+ */
+struct rate_shaping_vars_per_port {
+ u32 rs_periodic_timeout;
+ u32 rs_threshold;
+};
+
+/*
+ * per-port fairness variables
+ */
+struct fairness_vars_per_port {
+ u32 upper_bound;
+ u32 fair_threshold;
+ u32 fairness_timeout;
+ u32 reserved0;
+};
+
+/*
+ * per-port SAFC variables
+ */
+struct safc_struct_per_port {
+#if defined(__BIG_ENDIAN)
+ u16 __reserved1;
+ u8 __reserved0;
+ u8 safc_timeout_usec;
+#elif defined(__LITTLE_ENDIAN)
+ u8 safc_timeout_usec;
+ u8 __reserved0;
+ u16 __reserved1;
+#endif
+ u8 cos_to_traffic_types[MAX_COS_NUMBER];
+ u16 cos_to_pause_mask[NUM_OF_SAFC_BITS];
+};
+
+/*
+ * Per-port congestion management variables
+ */
+struct cmng_struct_per_port {
+ struct rate_shaping_vars_per_port rs_vars;
+ struct fairness_vars_per_port fair_vars;
+ struct safc_struct_per_port safc_vars;
+ struct cmng_flags_per_port flags;
+};
+
+
+/*
+ * Protocol-common command ID for slow path elements
+ */
+enum common_spqe_cmd_id {
+ RAMROD_CMD_ID_COMMON_UNUSED,
+ RAMROD_CMD_ID_COMMON_FUNCTION_START,
+ RAMROD_CMD_ID_COMMON_FUNCTION_STOP,
+ RAMROD_CMD_ID_COMMON_CFC_DEL,
+ RAMROD_CMD_ID_COMMON_CFC_DEL_WB,
+ RAMROD_CMD_ID_COMMON_STAT_QUERY,
+ RAMROD_CMD_ID_COMMON_STOP_TRAFFIC,
+ RAMROD_CMD_ID_COMMON_START_TRAFFIC,
+ RAMROD_CMD_ID_COMMON_RESERVED1,
+ RAMROD_CMD_ID_COMMON_RESERVED2,
+ MAX_COMMON_SPQE_CMD_ID
+};
+
+
+/*
+ * Per-protocol connection types
+ */
+enum connection_type {
+ ETH_CONNECTION_TYPE,
+ TOE_CONNECTION_TYPE,
+ RDMA_CONNECTION_TYPE,
+ ISCSI_CONNECTION_TYPE,
+ FCOE_CONNECTION_TYPE,
+ RESERVED_CONNECTION_TYPE_0,
+ RESERVED_CONNECTION_TYPE_1,
+ RESERVED_CONNECTION_TYPE_2,
+ NONE_CONNECTION_TYPE,
+ MAX_CONNECTION_TYPE
+};
+
+
+/*
+ * Cos modes
+ */
+enum cos_mode {
+ OVERRIDE_COS,
+ STATIC_COS,
+ FW_WRR,
+ MAX_COS_MODE
+};
+
+
+/*
+ * Dynamic HC counters set by the driver
+ */
+struct hc_dynamic_drv_counter {
+ u32 val[HC_SB_MAX_DYNAMIC_INDICES];
+};
+
+/*
+ * zone A per-queue data
+ */
+struct cstorm_queue_zone_data {
+ struct hc_dynamic_drv_counter hc_dyn_drv_cnt;
+ struct regpair reserved[2];
+};
+
+
+/*
+ * Vf-PF channel data in cstorm ram (non-triggered zone)
+ */
+struct vf_pf_channel_zone_data {
+ u32 msg_addr_lo;
+ u32 msg_addr_hi;
+};
+
+/*
+ * zone for VF non-triggered data
+ */
+struct non_trigger_vf_zone {
+ struct vf_pf_channel_zone_data vf_pf_channel;
+};
+
+/*
+ * Vf-PF channel trigger zone in cstorm ram
+ */
+struct vf_pf_channel_zone_trigger {
+ u8 addr_valid;
+};
+
+/*
+ * zone that triggers the in-bound interrupt
+ */
+struct trigger_vf_zone {
+#if defined(__BIG_ENDIAN)
+ u16 reserved1;
+ u8 reserved0;
+ struct vf_pf_channel_zone_trigger vf_pf_channel;
+#elif defined(__LITTLE_ENDIAN)
+ struct vf_pf_channel_zone_trigger vf_pf_channel;
+ u8 reserved0;
+ u16 reserved1;
+#endif
+ u32 reserved2;
+};
+
+/*
+ * zone B per-VF data
+ */
+struct cstorm_vf_zone_data {
+ struct non_trigger_vf_zone non_trigger;
+ struct trigger_vf_zone trigger;
+};
+
+
+/*
+ * Dynamic host coalescing init parameters, per state machine
+ */
+struct dynamic_hc_sm_config {
+ u32 threshold[3];
+ u8 shift_per_protocol[HC_SB_MAX_DYNAMIC_INDICES];
+ u8 hc_timeout0[HC_SB_MAX_DYNAMIC_INDICES];
+ u8 hc_timeout1[HC_SB_MAX_DYNAMIC_INDICES];
+ u8 hc_timeout2[HC_SB_MAX_DYNAMIC_INDICES];
+ u8 hc_timeout3[HC_SB_MAX_DYNAMIC_INDICES];
+};
+
+/*
+ * Dynamic host coalescing init parameters
+ */
+struct dynamic_hc_config {
+ struct dynamic_hc_sm_config sm_config[HC_SB_MAX_SM];
+};
+
+
+struct e2_integ_data {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define E2_INTEG_DATA_TESTING_EN (0x1<<0)
+#define E2_INTEG_DATA_TESTING_EN_SHIFT 0
+#define E2_INTEG_DATA_LB_TX (0x1<<1)
+#define E2_INTEG_DATA_LB_TX_SHIFT 1
+#define E2_INTEG_DATA_COS_TX (0x1<<2)
+#define E2_INTEG_DATA_COS_TX_SHIFT 2
+#define E2_INTEG_DATA_OPPORTUNISTICQM (0x1<<3)
+#define E2_INTEG_DATA_OPPORTUNISTICQM_SHIFT 3
+#define E2_INTEG_DATA_DPMTESTRELEASEDQ (0x1<<4)
+#define E2_INTEG_DATA_DPMTESTRELEASEDQ_SHIFT 4
+#define E2_INTEG_DATA_RESERVED (0x7<<5)
+#define E2_INTEG_DATA_RESERVED_SHIFT 5
+ u8 cos;
+ u8 voq;
+ u8 pbf_queue;
+#elif defined(__LITTLE_ENDIAN)
+ u8 pbf_queue;
+ u8 voq;
+ u8 cos;
+ u8 flags;
+#define E2_INTEG_DATA_TESTING_EN (0x1<<0)
+#define E2_INTEG_DATA_TESTING_EN_SHIFT 0
+#define E2_INTEG_DATA_LB_TX (0x1<<1)
+#define E2_INTEG_DATA_LB_TX_SHIFT 1
+#define E2_INTEG_DATA_COS_TX (0x1<<2)
+#define E2_INTEG_DATA_COS_TX_SHIFT 2
+#define E2_INTEG_DATA_OPPORTUNISTICQM (0x1<<3)
+#define E2_INTEG_DATA_OPPORTUNISTICQM_SHIFT 3
+#define E2_INTEG_DATA_DPMTESTRELEASEDQ (0x1<<4)
+#define E2_INTEG_DATA_DPMTESTRELEASEDQ_SHIFT 4
+#define E2_INTEG_DATA_RESERVED (0x7<<5)
+#define E2_INTEG_DATA_RESERVED_SHIFT 5
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 reserved3;
+ u8 reserved2;
+ u8 ramEn;
+#elif defined(__LITTLE_ENDIAN)
+ u8 ramEn;
+ u8 reserved2;
+ u16 reserved3;
+#endif
+};
+
+
+/*
+ * set mac event data
+ */
+struct eth_event_data {
+ u32 echo;
+ u32 reserved0;
+ u32 reserved1;
+};
+
+
+/*
+ * pf-vf event data
+ */
+struct vf_pf_event_data {
+ u8 vf_id;
+ u8 reserved0;
+ u16 reserved1;
+ u32 msg_addr_lo;
+ u32 msg_addr_hi;
+};
+
+/*
+ * VF FLR event data
+ */
+struct vf_flr_event_data {
+ u8 vf_id;
+ u8 reserved0;
+ u16 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+};
+
+/*
+ * malicious VF event data
+ */
+struct malicious_vf_event_data {
+ u8 vf_id;
+ u8 reserved0;
+ u16 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+};
+
+/*
+ * union for all event ring message types
+ */
+union event_data {
+ struct vf_pf_event_data vf_pf_event;
+ struct eth_event_data eth_event;
+ struct cfc_del_event_data cfc_del_event;
+ struct vf_flr_event_data vf_flr_event;
+ struct malicious_vf_event_data malicious_vf_event;
+};
+
+
+/*
+ * per PF event ring data
+ */
+struct event_ring_data {
+ struct regpair base_addr;
+#if defined(__BIG_ENDIAN)
+ u8 index_id;
+ u8 sb_id;
+ u16 producer;
+#elif defined(__LITTLE_ENDIAN)
+ u16 producer;
+ u8 sb_id;
+ u8 index_id;
+#endif
+ u32 reserved0;
+};
+
+
+/*
+ * event ring message element (each element is 128 bits)
+ */
+struct event_ring_msg {
+ u8 opcode;
+ u8 error;
+ u16 reserved1;
+ union event_data data;
+};
+
+/*
+ * event ring next page element (128 bits)
+ */
+struct event_ring_next {
+ struct regpair addr;
+ u32 reserved[2];
+};
+
+/*
+ * union for event ring element types (each element is 128 bits)
+ */
+union event_ring_elem {
+ struct event_ring_msg message;
+ struct event_ring_next next_page;
+};
+
+
+/*
+ * Common event ring opcodes
+ */
+enum event_ring_opcode {
+ EVENT_RING_OPCODE_VF_PF_CHANNEL,
+ EVENT_RING_OPCODE_FUNCTION_START,
+ EVENT_RING_OPCODE_FUNCTION_STOP,
+ EVENT_RING_OPCODE_CFC_DEL,
+ EVENT_RING_OPCODE_CFC_DEL_WB,
+ EVENT_RING_OPCODE_STAT_QUERY,
+ EVENT_RING_OPCODE_STOP_TRAFFIC,
+ EVENT_RING_OPCODE_START_TRAFFIC,
+ EVENT_RING_OPCODE_VF_FLR,
+ EVENT_RING_OPCODE_MALICIOUS_VF,
+ EVENT_RING_OPCODE_FORWARD_SETUP,
+ EVENT_RING_OPCODE_RSS_UPDATE_RULES,
+ EVENT_RING_OPCODE_RESERVED1,
+ EVENT_RING_OPCODE_RESERVED2,
+ EVENT_RING_OPCODE_SET_MAC,
+ EVENT_RING_OPCODE_CLASSIFICATION_RULES,
+ EVENT_RING_OPCODE_FILTERS_RULES,
+ EVENT_RING_OPCODE_MULTICAST_RULES,
+ MAX_EVENT_RING_OPCODE
+};
+
+
+/*
+ * Modes for fairness algorithm
+ */
+enum fairness_mode {
+ FAIRNESS_COS_WRR_MODE,
+ FAIRNESS_COS_ETS_MODE,
+ MAX_FAIRNESS_MODE
+};
+
+
+/*
+ * per-vnic fairness variables
+ */
+struct fairness_vars_per_vn {
+ u32 cos_credit_delta[MAX_COS_NUMBER];
+ u32 vn_credit_delta;
+ u32 __reserved0;
+};
+
+
+/*
+ * Priority and cos
+ */
+struct priority_cos {
+ u8 priority;
+ u8 cos;
+ __le16 reserved1;
+};
+
+/*
+ * The data for flow control configuration
+ */
+struct flow_control_configuration {
+ struct priority_cos traffic_type_to_priority_cos[MAX_TRAFFIC_TYPES];
+ u8 dcb_enabled;
+ u8 dcb_version;
+ u8 dont_add_pri_0_en;
+ u8 reserved1;
+ __le32 reserved2;
+};
+
+
+/*
+ *
+ */
+struct function_start_data {
+ __le16 function_mode;
+ __le16 sd_vlan_tag;
+ u16 reserved;
+ u8 path_id;
+ u8 network_cos_mode;
+};
+
+
+/*
+ * FW version stored in the Xstorm RAM
+ */
+struct fw_version {
+#if defined(__BIG_ENDIAN)
+ u8 engineering;
+ u8 revision;
+ u8 minor;
+ u8 major;
+#elif defined(__LITTLE_ENDIAN)
+ u8 major;
+ u8 minor;
+ u8 revision;
+ u8 engineering;
+#endif
+ u32 flags;
+#define FW_VERSION_OPTIMIZED (0x1<<0)
+#define FW_VERSION_OPTIMIZED_SHIFT 0
+#define FW_VERSION_BIG_ENDIEN (0x1<<1)
+#define FW_VERSION_BIG_ENDIEN_SHIFT 1
+#define FW_VERSION_CHIP_VERSION (0x3<<2)
+#define FW_VERSION_CHIP_VERSION_SHIFT 2
+#define __FW_VERSION_RESERVED (0xFFFFFFF<<4)
+#define __FW_VERSION_RESERVED_SHIFT 4
+};
+
+
+/*
+ * Dynamic Host-Coalescing - Driver(host) counters
+ */
+struct hc_dynamic_sb_drv_counters {
+ u32 dynamic_hc_drv_counter[HC_SB_MAX_DYNAMIC_INDICES];
+};
+
+
+/*
+ * 2 bytes. configuration/state parameters for a single protocol index
+ */
+struct hc_index_data {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define HC_INDEX_DATA_SM_ID (0x1<<0)
+#define HC_INDEX_DATA_SM_ID_SHIFT 0
+#define HC_INDEX_DATA_HC_ENABLED (0x1<<1)
+#define HC_INDEX_DATA_HC_ENABLED_SHIFT 1
+#define HC_INDEX_DATA_DYNAMIC_HC_ENABLED (0x1<<2)
+#define HC_INDEX_DATA_DYNAMIC_HC_ENABLED_SHIFT 2
+#define HC_INDEX_DATA_RESERVE (0x1F<<3)
+#define HC_INDEX_DATA_RESERVE_SHIFT 3
+ u8 timeout;
+#elif defined(__LITTLE_ENDIAN)
+ u8 timeout;
+ u8 flags;
+#define HC_INDEX_DATA_SM_ID (0x1<<0)
+#define HC_INDEX_DATA_SM_ID_SHIFT 0
+#define HC_INDEX_DATA_HC_ENABLED (0x1<<1)
+#define HC_INDEX_DATA_HC_ENABLED_SHIFT 1
+#define HC_INDEX_DATA_DYNAMIC_HC_ENABLED (0x1<<2)
+#define HC_INDEX_DATA_DYNAMIC_HC_ENABLED_SHIFT 2
+#define HC_INDEX_DATA_RESERVE (0x1F<<3)
+#define HC_INDEX_DATA_RESERVE_SHIFT 3
+#endif
+};
+
+
+/*
+ * HC state-machine
+ */
+struct hc_status_block_sm {
+#if defined(__BIG_ENDIAN)
+ u8 igu_seg_id;
+ u8 igu_sb_id;
+ u8 timer_value;
+ u8 __flags;
+#elif defined(__LITTLE_ENDIAN)
+ u8 __flags;
+ u8 timer_value;
+ u8 igu_sb_id;
+ u8 igu_seg_id;
+#endif
+ u32 time_to_expire;
+};
+
+/*
+ * hold PCI identification variables- used in various places in firmware
+ */
+struct pci_entity {
+#if defined(__BIG_ENDIAN)
+ u8 vf_valid;
+ u8 vf_id;
+ u8 vnic_id;
+ u8 pf_id;
+#elif defined(__LITTLE_ENDIAN)
+ u8 pf_id;
+ u8 vnic_id;
+ u8 vf_id;
+ u8 vf_valid;
+#endif
+};
+
+/*
+ * The fast-path status block meta-data, common to all chips
+ */
+struct hc_sb_data {
+ struct regpair host_sb_addr;
+ struct hc_status_block_sm state_machine[HC_SB_MAX_SM];
+ struct pci_entity p_func;
+#if defined(__BIG_ENDIAN)
+ u8 rsrv0;
+ u8 state;
+ u8 dhc_qzone_id;
+ u8 same_igu_sb_1b;
+#elif defined(__LITTLE_ENDIAN)
+ u8 same_igu_sb_1b;
+ u8 dhc_qzone_id;
+ u8 state;
+ u8 rsrv0;
+#endif
+ struct regpair rsrv1[2];
+};
+
+
+/*
+ * Segment types for host coaslescing
+ */
+enum hc_segment {
+ HC_REGULAR_SEGMENT,
+ HC_DEFAULT_SEGMENT,
+ MAX_HC_SEGMENT
+};
+
+
+/*
+ * The fast-path status block meta-data
+ */
+struct hc_sp_status_block_data {
+ struct regpair host_sb_addr;
+#if defined(__BIG_ENDIAN)
+ u8 rsrv1;
+ u8 state;
+ u8 igu_seg_id;
+ u8 igu_sb_id;
+#elif defined(__LITTLE_ENDIAN)
+ u8 igu_sb_id;
+ u8 igu_seg_id;
+ u8 state;
+ u8 rsrv1;
+#endif
+ struct pci_entity p_func;
+};
+
+
+/*
+ * The fast-path status block meta-data
+ */
+struct hc_status_block_data_e1x {
+ struct hc_index_data index_data[HC_SB_MAX_INDICES_E1X];
+ struct hc_sb_data common;
+};
+
+
+/*
+ * The fast-path status block meta-data
+ */
+struct hc_status_block_data_e2 {
+ struct hc_index_data index_data[HC_SB_MAX_INDICES_E2];
+ struct hc_sb_data common;
+};
+
+
+/*
+ * IGU block operartion modes (in Everest2)
+ */
+enum igu_mode {
+ HC_IGU_BC_MODE,
+ HC_IGU_NBC_MODE,
+ MAX_IGU_MODE
+};
+
+
+/*
+ * IP versions
+ */
+enum ip_ver {
+ IP_V4,
+ IP_V6,
+ MAX_IP_VER
+};
+
+
+/*
+ * Multi-function modes
+ */
+enum mf_mode {
+ SINGLE_FUNCTION,
+ MULTI_FUNCTION_SD,
+ MULTI_FUNCTION_SI,
+ MULTI_FUNCTION_RESERVED,
+ MAX_MF_MODE
+};
+
+/*
+ * Protocol-common statistics collected by the Tstorm (per pf)
+ */
+struct tstorm_per_pf_stats {
+ struct regpair rcv_error_bytes;
+};
+
+/*
+ *
+ */
+struct per_pf_stats {
+ struct tstorm_per_pf_stats tstorm_pf_statistics;
+};
+
+
+/*
+ * Protocol-common statistics collected by the Tstorm (per port)
+ */
+struct tstorm_per_port_stats {
+ __le32 mac_discard;
+ __le32 mac_filter_discard;
+ __le32 brb_truncate_discard;
+ __le32 mf_tag_discard;
+ __le32 packet_drop;
+ __le32 reserved;
+};
+
+/*
+ *
+ */
+struct per_port_stats {
+ struct tstorm_per_port_stats tstorm_port_statistics;
+};
+
+
+/*
+ * Protocol-common statistics collected by the Tstorm (per client)
+ */
+struct tstorm_per_queue_stats {
+ struct regpair rcv_ucast_bytes;
+ __le32 rcv_ucast_pkts;
+ __le32 checksum_discard;
+ struct regpair rcv_bcast_bytes;
+ __le32 rcv_bcast_pkts;
+ __le32 pkts_too_big_discard;
+ struct regpair rcv_mcast_bytes;
+ __le32 rcv_mcast_pkts;
+ __le32 ttl0_discard;
+ __le16 no_buff_discard;
+ __le16 reserved0;
+ __le32 reserved1;
+};
+
+/*
+ * Protocol-common statistics collected by the Ustorm (per client)
+ */
+struct ustorm_per_queue_stats {
+ struct regpair ucast_no_buff_bytes;
+ struct regpair mcast_no_buff_bytes;
+ struct regpair bcast_no_buff_bytes;
+ __le32 ucast_no_buff_pkts;
+ __le32 mcast_no_buff_pkts;
+ __le32 bcast_no_buff_pkts;
+ __le32 coalesced_pkts;
+ struct regpair coalesced_bytes;
+ __le32 coalesced_events;
+ __le32 coalesced_aborts;
+};
+
+/*
+ * Protocol-common statistics collected by the Xstorm (per client)
+ */
+struct xstorm_per_queue_stats {
+ struct regpair ucast_bytes_sent;
+ struct regpair mcast_bytes_sent;
+ struct regpair bcast_bytes_sent;
+ __le32 ucast_pkts_sent;
+ __le32 mcast_pkts_sent;
+ __le32 bcast_pkts_sent;
+ __le32 error_drop_pkts;
+};
+
+/*
+ *
+ */
+struct per_queue_stats {
+ struct tstorm_per_queue_stats tstorm_queue_statistics;
+ struct ustorm_per_queue_stats ustorm_queue_statistics;
+ struct xstorm_per_queue_stats xstorm_queue_statistics;
+};
+
+
+/*
+ * FW version stored in first line of pram
+ */
+struct pram_fw_version {
+ u8 major;
+ u8 minor;
+ u8 revision;
+ u8 engineering;
+ u8 flags;
+#define PRAM_FW_VERSION_OPTIMIZED (0x1<<0)
+#define PRAM_FW_VERSION_OPTIMIZED_SHIFT 0
+#define PRAM_FW_VERSION_STORM_ID (0x3<<1)
+#define PRAM_FW_VERSION_STORM_ID_SHIFT 1
+#define PRAM_FW_VERSION_BIG_ENDIEN (0x1<<3)
+#define PRAM_FW_VERSION_BIG_ENDIEN_SHIFT 3
+#define PRAM_FW_VERSION_CHIP_VERSION (0x3<<4)
+#define PRAM_FW_VERSION_CHIP_VERSION_SHIFT 4
+#define __PRAM_FW_VERSION_RESERVED0 (0x3<<6)
+#define __PRAM_FW_VERSION_RESERVED0_SHIFT 6
+};
+
+
+/*
+ * Ethernet slow path element
+ */
+union protocol_common_specific_data {
+ u8 protocol_data[8];
+ struct regpair phy_address;
+ struct regpair mac_config_addr;
+};
+
+/*
+ * The send queue element
+ */
+struct protocol_common_spe {
+ struct spe_hdr hdr;
+ union protocol_common_specific_data data;
+};
+
+
+/*
+ * a single rate shaping counter. can be used as protocol or vnic counter
+ */
+struct rate_shaping_counter {
+ u32 quota;
+#if defined(__BIG_ENDIAN)
+ u16 __reserved0;
+ u16 rate;
+#elif defined(__LITTLE_ENDIAN)
+ u16 rate;
+ u16 __reserved0;
+#endif
+};
+
+
+/*
+ * per-vnic rate shaping variables
+ */
+struct rate_shaping_vars_per_vn {
+ struct rate_shaping_counter vn_counter;
+};
+
+
+/*
+ * The send queue element
+ */
+struct slow_path_element {
+ struct spe_hdr hdr;
+ struct regpair protocol_data;
+};
+
+
+/*
+ * Protocol-common statistics counter
+ */
+struct stats_counter {
+ __le16 xstats_counter;
+ __le16 reserved0;
+ __le32 reserved1;
+ __le16 tstats_counter;
+ __le16 reserved2;
+ __le32 reserved3;
+ __le16 ustats_counter;
+ __le16 reserved4;
+ __le32 reserved5;
+ __le16 cstats_counter;
+ __le16 reserved6;
+ __le32 reserved7;
+};
+
+
+/*
+ *
+ */
+struct stats_query_entry {
+ u8 kind;
+ u8 index;
+ __le16 funcID;
+ __le32 reserved;
+ struct regpair address;
+};
+
+/*
+ * statistic command
+ */
+struct stats_query_cmd_group {
+ struct stats_query_entry query[STATS_QUERY_CMD_COUNT];
+};
+
+
+/*
+ * statistic command header
+ */
+struct stats_query_header {
+ u8 cmd_num;
+ u8 reserved0;
+ __le16 drv_stats_counter;
+ __le32 reserved1;
+ struct regpair stats_counters_addrs;
+};
+
+
+/*
+ * Types of statistcis query entry
+ */
+enum stats_query_type {
+ STATS_TYPE_QUEUE,
+ STATS_TYPE_PORT,
+ STATS_TYPE_PF,
+ STATS_TYPE_TOE,
+ STATS_TYPE_FCOE,
+ MAX_STATS_QUERY_TYPE
+};
+
+
+/*
+ * Indicate of the function status block state
+ */
+enum status_block_state {
+ SB_DISABLED,
+ SB_ENABLED,
+ SB_CLEANED,
+ MAX_STATUS_BLOCK_STATE
+};
+
+
+/*
+ * Storm IDs (including attentions for IGU related enums)
+ */
+enum storm_id {
+ USTORM_ID,
+ CSTORM_ID,
+ XSTORM_ID,
+ TSTORM_ID,
+ ATTENTION_ID,
+ MAX_STORM_ID
+};
+
+
+/*
+ * Taffic types used in ETS and flow control algorithms
+ */
+enum traffic_type {
+ LLFC_TRAFFIC_TYPE_NW,
+ LLFC_TRAFFIC_TYPE_FCOE,
+ LLFC_TRAFFIC_TYPE_ISCSI,
+ MAX_TRAFFIC_TYPE
+};
+
+
+/*
+ * zone A per-queue data
+ */
+struct tstorm_queue_zone_data {
+ struct regpair reserved[4];
+};
+
+
+/*
+ * zone B per-VF data
+ */
+struct tstorm_vf_zone_data {
+ struct regpair reserved;
+};
+
+
+/*
+ * zone A per-queue data
+ */
+struct ustorm_queue_zone_data {
+ struct ustorm_eth_rx_producers eth_rx_producers;
+ struct regpair reserved[3];
+};
+
+
+/*
+ * zone B per-VF data
+ */
+struct ustorm_vf_zone_data {
+ struct regpair reserved;
+};
+
+
+/*
+ * data per VF-PF channel
+ */
+struct vf_pf_channel_data {
+#if defined(__BIG_ENDIAN)
+ u16 reserved0;
+ u8 valid;
+ u8 state;
+#elif defined(__LITTLE_ENDIAN)
+ u8 state;
+ u8 valid;
+ u16 reserved0;
+#endif
+ u32 reserved1;
+};
+
+
+/*
+ * State of VF-PF channel
+ */
+enum vf_pf_channel_state {
+ VF_PF_CHANNEL_STATE_READY,
+ VF_PF_CHANNEL_STATE_WAITING_FOR_ACK,
+ MAX_VF_PF_CHANNEL_STATE
+};
+
+
+/*
+ * zone A per-queue data
+ */
+struct xstorm_queue_zone_data {
+ struct regpair reserved[4];
+};
+
+
+/*
+ * zone B per-VF data
+ */
+struct xstorm_vf_zone_data {
+ struct regpair reserved;
+};
+
+#endif /* BNX2X_HSI_H */
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h
new file mode 100644
index 000000000000..4d748e77d1ac
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h
@@ -0,0 +1,567 @@
+/* bnx2x_init.h: Broadcom Everest network driver.
+ * Structures and macroes needed during the initialization.
+ *
+ * Copyright (c) 2007-2011 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Written by: Eliezer Tamir
+ * Modified by: Vladislav Zolotarov <vladz@broadcom.com>
+ */
+
+#ifndef BNX2X_INIT_H
+#define BNX2X_INIT_H
+
+/* Init operation types and structures */
+enum {
+ OP_RD = 0x1, /* read a single register */
+ OP_WR, /* write a single register */
+ OP_SW, /* copy a string to the device */
+ OP_ZR, /* clear memory */
+ OP_ZP, /* unzip then copy with DMAE */
+ OP_WR_64, /* write 64 bit pattern */
+ OP_WB, /* copy a string using DMAE */
+ OP_WB_ZR, /* Clear a string using DMAE or indirect-wr */
+ /* Skip the following ops if all of the init modes don't match */
+ OP_IF_MODE_OR,
+ /* Skip the following ops if any of the init modes don't match */
+ OP_IF_MODE_AND,
+ OP_MAX
+};
+
+enum {
+ STAGE_START,
+ STAGE_END,
+};
+
+/* Returns the index of start or end of a specific block stage in ops array*/
+#define BLOCK_OPS_IDX(block, stage, end) \
+ (2*(((block)*NUM_OF_INIT_PHASES) + (stage)) + (end))
+
+
+/* structs for the various opcodes */
+struct raw_op {
+ u32 op:8;
+ u32 offset:24;
+ u32 raw_data;
+};
+
+struct op_read {
+ u32 op:8;
+ u32 offset:24;
+ u32 val;
+};
+
+struct op_write {
+ u32 op:8;
+ u32 offset:24;
+ u32 val;
+};
+
+struct op_arr_write {
+ u32 op:8;
+ u32 offset:24;
+#ifdef __BIG_ENDIAN
+ u16 data_len;
+ u16 data_off;
+#else /* __LITTLE_ENDIAN */
+ u16 data_off;
+ u16 data_len;
+#endif
+};
+
+struct op_zero {
+ u32 op:8;
+ u32 offset:24;
+ u32 len;
+};
+
+struct op_if_mode {
+ u32 op:8;
+ u32 cmd_offset:24;
+ u32 mode_bit_map;
+};
+
+
+union init_op {
+ struct op_read read;
+ struct op_write write;
+ struct op_arr_write arr_wr;
+ struct op_zero zero;
+ struct raw_op raw;
+ struct op_if_mode if_mode;
+};
+
+
+/* Init Phases */
+enum {
+ PHASE_COMMON,
+ PHASE_PORT0,
+ PHASE_PORT1,
+ PHASE_PF0,
+ PHASE_PF1,
+ PHASE_PF2,
+ PHASE_PF3,
+ PHASE_PF4,
+ PHASE_PF5,
+ PHASE_PF6,
+ PHASE_PF7,
+ NUM_OF_INIT_PHASES
+};
+
+/* Init Modes */
+enum {
+ MODE_ASIC = 0x00000001,
+ MODE_FPGA = 0x00000002,
+ MODE_EMUL = 0x00000004,
+ MODE_E2 = 0x00000008,
+ MODE_E3 = 0x00000010,
+ MODE_PORT2 = 0x00000020,
+ MODE_PORT4 = 0x00000040,
+ MODE_SF = 0x00000080,
+ MODE_MF = 0x00000100,
+ MODE_MF_SD = 0x00000200,
+ MODE_MF_SI = 0x00000400,
+ MODE_MF_NIV = 0x00000800,
+ MODE_E3_A0 = 0x00001000,
+ MODE_E3_B0 = 0x00002000,
+ MODE_COS3 = 0x00004000,
+ MODE_COS6 = 0x00008000,
+ MODE_LITTLE_ENDIAN = 0x00010000,
+ MODE_BIG_ENDIAN = 0x00020000,
+};
+
+/* Init Blocks */
+enum {
+ BLOCK_ATC,
+ BLOCK_BRB1,
+ BLOCK_CCM,
+ BLOCK_CDU,
+ BLOCK_CFC,
+ BLOCK_CSDM,
+ BLOCK_CSEM,
+ BLOCK_DBG,
+ BLOCK_DMAE,
+ BLOCK_DORQ,
+ BLOCK_HC,
+ BLOCK_IGU,
+ BLOCK_MISC,
+ BLOCK_NIG,
+ BLOCK_PBF,
+ BLOCK_PGLUE_B,
+ BLOCK_PRS,
+ BLOCK_PXP2,
+ BLOCK_PXP,
+ BLOCK_QM,
+ BLOCK_SRC,
+ BLOCK_TCM,
+ BLOCK_TM,
+ BLOCK_TSDM,
+ BLOCK_TSEM,
+ BLOCK_UCM,
+ BLOCK_UPB,
+ BLOCK_USDM,
+ BLOCK_USEM,
+ BLOCK_XCM,
+ BLOCK_XPB,
+ BLOCK_XSDM,
+ BLOCK_XSEM,
+ BLOCK_MISC_AEU,
+ NUM_OF_INIT_BLOCKS
+};
+
+/* QM queue numbers */
+#define BNX2X_ETH_Q 0
+#define BNX2X_TOE_Q 3
+#define BNX2X_TOE_ACK_Q 6
+#define BNX2X_ISCSI_Q 9
+#define BNX2X_ISCSI_ACK_Q 11
+#define BNX2X_FCOE_Q 10
+
+/* Vnics per mode */
+#define BNX2X_PORT2_MODE_NUM_VNICS 4
+#define BNX2X_PORT4_MODE_NUM_VNICS 2
+
+/* COS offset for port1 in E3 B0 4port mode */
+#define BNX2X_E3B0_PORT1_COS_OFFSET 3
+
+/* QM Register addresses */
+#define BNX2X_Q_VOQ_REG_ADDR(pf_q_num)\
+ (QM_REG_QVOQIDX_0 + 4 * (pf_q_num))
+#define BNX2X_VOQ_Q_REG_ADDR(cos, pf_q_num)\
+ (QM_REG_VOQQMASK_0_LSB + 4 * ((cos) * 2 + ((pf_q_num) >> 5)))
+#define BNX2X_Q_CMDQ_REG_ADDR(pf_q_num)\
+ (QM_REG_BYTECRDCMDQ_0 + 4 * ((pf_q_num) >> 4))
+
+/* extracts the QM queue number for the specified port and vnic */
+#define BNX2X_PF_Q_NUM(q_num, port, vnic)\
+ ((((port) << 1) | (vnic)) * 16 + (q_num))
+
+
+/* Maps the specified queue to the specified COS */
+static inline void bnx2x_map_q_cos(struct bnx2x *bp, u32 q_num, u32 new_cos)
+{
+ /* find current COS mapping */
+ u32 curr_cos = REG_RD(bp, QM_REG_QVOQIDX_0 + q_num * 4);
+
+ /* check if queue->COS mapping has changed */
+ if (curr_cos != new_cos) {
+ u32 num_vnics = BNX2X_PORT2_MODE_NUM_VNICS;
+ u32 reg_addr, reg_bit_map, vnic;
+
+ /* update parameters for 4port mode */
+ if (INIT_MODE_FLAGS(bp) & MODE_PORT4) {
+ num_vnics = BNX2X_PORT4_MODE_NUM_VNICS;
+ if (BP_PORT(bp)) {
+ curr_cos += BNX2X_E3B0_PORT1_COS_OFFSET;
+ new_cos += BNX2X_E3B0_PORT1_COS_OFFSET;
+ }
+ }
+
+ /* change queue mapping for each VNIC */
+ for (vnic = 0; vnic < num_vnics; vnic++) {
+ u32 pf_q_num =
+ BNX2X_PF_Q_NUM(q_num, BP_PORT(bp), vnic);
+ u32 q_bit_map = 1 << (pf_q_num & 0x1f);
+
+ /* overwrite queue->VOQ mapping */
+ REG_WR(bp, BNX2X_Q_VOQ_REG_ADDR(pf_q_num), new_cos);
+
+ /* clear queue bit from current COS bit map */
+ reg_addr = BNX2X_VOQ_Q_REG_ADDR(curr_cos, pf_q_num);
+ reg_bit_map = REG_RD(bp, reg_addr);
+ REG_WR(bp, reg_addr, reg_bit_map & (~q_bit_map));
+
+ /* set queue bit in new COS bit map */
+ reg_addr = BNX2X_VOQ_Q_REG_ADDR(new_cos, pf_q_num);
+ reg_bit_map = REG_RD(bp, reg_addr);
+ REG_WR(bp, reg_addr, reg_bit_map | q_bit_map);
+
+ /* set/clear queue bit in command-queue bit map
+ (E2/E3A0 only, valid COS values are 0/1) */
+ if (!(INIT_MODE_FLAGS(bp) & MODE_E3_B0)) {
+ reg_addr = BNX2X_Q_CMDQ_REG_ADDR(pf_q_num);
+ reg_bit_map = REG_RD(bp, reg_addr);
+ q_bit_map = 1 << (2 * (pf_q_num & 0xf));
+ reg_bit_map = new_cos ?
+ (reg_bit_map | q_bit_map) :
+ (reg_bit_map & (~q_bit_map));
+ REG_WR(bp, reg_addr, reg_bit_map);
+ }
+ }
+ }
+}
+
+/* Configures the QM according to the specified per-traffic-type COSes */
+static inline void bnx2x_dcb_config_qm(struct bnx2x *bp, enum cos_mode mode,
+ struct priority_cos *traffic_cos)
+{
+ bnx2x_map_q_cos(bp, BNX2X_FCOE_Q,
+ traffic_cos[LLFC_TRAFFIC_TYPE_FCOE].cos);
+ bnx2x_map_q_cos(bp, BNX2X_ISCSI_Q,
+ traffic_cos[LLFC_TRAFFIC_TYPE_ISCSI].cos);
+ bnx2x_map_q_cos(bp, BNX2X_ISCSI_ACK_Q,
+ traffic_cos[LLFC_TRAFFIC_TYPE_ISCSI].cos);
+ if (mode != STATIC_COS) {
+ /* required only in backward compatible COS mode */
+ bnx2x_map_q_cos(bp, BNX2X_ETH_Q,
+ traffic_cos[LLFC_TRAFFIC_TYPE_NW].cos);
+ bnx2x_map_q_cos(bp, BNX2X_TOE_Q,
+ traffic_cos[LLFC_TRAFFIC_TYPE_NW].cos);
+ bnx2x_map_q_cos(bp, BNX2X_TOE_ACK_Q,
+ traffic_cos[LLFC_TRAFFIC_TYPE_NW].cos);
+ }
+}
+
+
+/* Returns the index of start or end of a specific block stage in ops array*/
+#define BLOCK_OPS_IDX(block, stage, end) \
+ (2*(((block)*NUM_OF_INIT_PHASES) + (stage)) + (end))
+
+
+#define INITOP_SET 0 /* set the HW directly */
+#define INITOP_CLEAR 1 /* clear the HW directly */
+#define INITOP_INIT 2 /* set the init-value array */
+
+/****************************************************************************
+* ILT management
+****************************************************************************/
+struct ilt_line {
+ dma_addr_t page_mapping;
+ void *page;
+ u32 size;
+};
+
+struct ilt_client_info {
+ u32 page_size;
+ u16 start;
+ u16 end;
+ u16 client_num;
+ u16 flags;
+#define ILT_CLIENT_SKIP_INIT 0x1
+#define ILT_CLIENT_SKIP_MEM 0x2
+};
+
+struct bnx2x_ilt {
+ u32 start_line;
+ struct ilt_line *lines;
+ struct ilt_client_info clients[4];
+#define ILT_CLIENT_CDU 0
+#define ILT_CLIENT_QM 1
+#define ILT_CLIENT_SRC 2
+#define ILT_CLIENT_TM 3
+};
+
+/****************************************************************************
+* SRC configuration
+****************************************************************************/
+struct src_ent {
+ u8 opaque[56];
+ u64 next;
+};
+
+/****************************************************************************
+* Parity configuration
+****************************************************************************/
+#define BLOCK_PRTY_INFO(block, en_mask, m1, m1h, m2, m3) \
+{ \
+ block##_REG_##block##_PRTY_MASK, \
+ block##_REG_##block##_PRTY_STS_CLR, \
+ en_mask, {m1, m1h, m2, m3}, #block \
+}
+
+#define BLOCK_PRTY_INFO_0(block, en_mask, m1, m1h, m2, m3) \
+{ \
+ block##_REG_##block##_PRTY_MASK_0, \
+ block##_REG_##block##_PRTY_STS_CLR_0, \
+ en_mask, {m1, m1h, m2, m3}, #block"_0" \
+}
+
+#define BLOCK_PRTY_INFO_1(block, en_mask, m1, m1h, m2, m3) \
+{ \
+ block##_REG_##block##_PRTY_MASK_1, \
+ block##_REG_##block##_PRTY_STS_CLR_1, \
+ en_mask, {m1, m1h, m2, m3}, #block"_1" \
+}
+
+static const struct {
+ u32 mask_addr;
+ u32 sts_clr_addr;
+ u32 en_mask; /* Mask to enable parity attentions */
+ struct {
+ u32 e1; /* 57710 */
+ u32 e1h; /* 57711 */
+ u32 e2; /* 57712 */
+ u32 e3; /* 578xx */
+ } reg_mask; /* Register mask (all valid bits) */
+ char name[7]; /* Block's longest name is 6 characters long
+ * (name + suffix)
+ */
+} bnx2x_blocks_parity_data[] = {
+ /* bit 19 masked */
+ /* REG_WR(bp, PXP_REG_PXP_PRTY_MASK, 0x80000); */
+ /* bit 5,18,20-31 */
+ /* REG_WR(bp, PXP2_REG_PXP2_PRTY_MASK_0, 0xfff40020); */
+ /* bit 5 */
+ /* REG_WR(bp, PXP2_REG_PXP2_PRTY_MASK_1, 0x20); */
+ /* REG_WR(bp, HC_REG_HC_PRTY_MASK, 0x0); */
+ /* REG_WR(bp, MISC_REG_MISC_PRTY_MASK, 0x0); */
+
+ /* Block IGU, MISC, PXP and PXP2 parity errors as long as we don't
+ * want to handle "system kill" flow at the moment.
+ */
+ BLOCK_PRTY_INFO(PXP, 0x7ffffff, 0x3ffffff, 0x3ffffff, 0x7ffffff,
+ 0x7ffffff),
+ BLOCK_PRTY_INFO_0(PXP2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff),
+ BLOCK_PRTY_INFO_1(PXP2, 0x1ffffff, 0x7f, 0x7f, 0x7ff, 0x1ffffff),
+ BLOCK_PRTY_INFO(HC, 0x7, 0x7, 0x7, 0, 0),
+ BLOCK_PRTY_INFO(NIG, 0xffffffff, 0x3fffffff, 0xffffffff, 0, 0),
+ BLOCK_PRTY_INFO_0(NIG, 0xffffffff, 0, 0, 0xffffffff, 0xffffffff),
+ BLOCK_PRTY_INFO_1(NIG, 0xffff, 0, 0, 0xff, 0xffff),
+ BLOCK_PRTY_INFO(IGU, 0x7ff, 0, 0, 0x7ff, 0x7ff),
+ BLOCK_PRTY_INFO(MISC, 0x1, 0x1, 0x1, 0x1, 0x1),
+ BLOCK_PRTY_INFO(QM, 0, 0x1ff, 0xfff, 0xfff, 0xfff),
+ BLOCK_PRTY_INFO(ATC, 0x1f, 0, 0, 0x1f, 0x1f),
+ BLOCK_PRTY_INFO(PGLUE_B, 0x3, 0, 0, 0x3, 0x3),
+ BLOCK_PRTY_INFO(DORQ, 0, 0x3, 0x3, 0x3, 0x3),
+ {GRCBASE_UPB + PB_REG_PB_PRTY_MASK,
+ GRCBASE_UPB + PB_REG_PB_PRTY_STS_CLR, 0xf,
+ {0xf, 0xf, 0xf, 0xf}, "UPB"},
+ {GRCBASE_XPB + PB_REG_PB_PRTY_MASK,
+ GRCBASE_XPB + PB_REG_PB_PRTY_STS_CLR, 0,
+ {0xf, 0xf, 0xf, 0xf}, "XPB"},
+ BLOCK_PRTY_INFO(SRC, 0x4, 0x7, 0x7, 0x7, 0x7),
+ BLOCK_PRTY_INFO(CDU, 0, 0x1f, 0x1f, 0x1f, 0x1f),
+ BLOCK_PRTY_INFO(CFC, 0, 0xf, 0xf, 0xf, 0x3f),
+ BLOCK_PRTY_INFO(DBG, 0, 0x1, 0x1, 0x1, 0x1),
+ BLOCK_PRTY_INFO(DMAE, 0, 0xf, 0xf, 0xf, 0xf),
+ BLOCK_PRTY_INFO(BRB1, 0, 0xf, 0xf, 0xf, 0xf),
+ BLOCK_PRTY_INFO(PRS, (1<<6), 0xff, 0xff, 0xff, 0xff),
+ BLOCK_PRTY_INFO(PBF, 0, 0, 0x3ffff, 0xfffff, 0xfffffff),
+ BLOCK_PRTY_INFO(TM, 0, 0, 0x7f, 0x7f, 0x7f),
+ BLOCK_PRTY_INFO(TSDM, 0x18, 0x7ff, 0x7ff, 0x7ff, 0x7ff),
+ BLOCK_PRTY_INFO(CSDM, 0x8, 0x7ff, 0x7ff, 0x7ff, 0x7ff),
+ BLOCK_PRTY_INFO(USDM, 0x38, 0x7ff, 0x7ff, 0x7ff, 0x7ff),
+ BLOCK_PRTY_INFO(XSDM, 0x8, 0x7ff, 0x7ff, 0x7ff, 0x7ff),
+ BLOCK_PRTY_INFO(TCM, 0, 0, 0x7ffffff, 0x7ffffff, 0x7ffffff),
+ BLOCK_PRTY_INFO(CCM, 0, 0, 0x7ffffff, 0x7ffffff, 0x7ffffff),
+ BLOCK_PRTY_INFO(UCM, 0, 0, 0x7ffffff, 0x7ffffff, 0x7ffffff),
+ BLOCK_PRTY_INFO(XCM, 0, 0, 0x3fffffff, 0x3fffffff, 0x3fffffff),
+ BLOCK_PRTY_INFO_0(TSEM, 0, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff),
+ BLOCK_PRTY_INFO_1(TSEM, 0, 0x3, 0x1f, 0x3f, 0x3f),
+ BLOCK_PRTY_INFO_0(USEM, 0, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff),
+ BLOCK_PRTY_INFO_1(USEM, 0, 0x3, 0x1f, 0x1f, 0x1f),
+ BLOCK_PRTY_INFO_0(CSEM, 0, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff),
+ BLOCK_PRTY_INFO_1(CSEM, 0, 0x3, 0x1f, 0x1f, 0x1f),
+ BLOCK_PRTY_INFO_0(XSEM, 0, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff),
+ BLOCK_PRTY_INFO_1(XSEM, 0, 0x3, 0x1f, 0x3f, 0x3f),
+};
+
+
+/* [28] MCP Latched rom_parity
+ * [29] MCP Latched ump_rx_parity
+ * [30] MCP Latched ump_tx_parity
+ * [31] MCP Latched scpad_parity
+ */
+#define MISC_AEU_ENABLE_MCP_PRTY_BITS \
+ (AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY | \
+ AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY | \
+ AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY | \
+ AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY)
+
+/* Below registers control the MCP parity attention output. When
+ * MISC_AEU_ENABLE_MCP_PRTY_BITS are set - attentions are
+ * enabled, when cleared - disabled.
+ */
+static const u32 mcp_attn_ctl_regs[] = {
+ MISC_REG_AEU_ENABLE4_FUNC_0_OUT_0,
+ MISC_REG_AEU_ENABLE4_NIG_0,
+ MISC_REG_AEU_ENABLE4_PXP_0,
+ MISC_REG_AEU_ENABLE4_FUNC_1_OUT_0,
+ MISC_REG_AEU_ENABLE4_NIG_1,
+ MISC_REG_AEU_ENABLE4_PXP_1
+};
+
+static inline void bnx2x_set_mcp_parity(struct bnx2x *bp, u8 enable)
+{
+ int i;
+ u32 reg_val;
+
+ for (i = 0; i < ARRAY_SIZE(mcp_attn_ctl_regs); i++) {
+ reg_val = REG_RD(bp, mcp_attn_ctl_regs[i]);
+
+ if (enable)
+ reg_val |= MISC_AEU_ENABLE_MCP_PRTY_BITS;
+ else
+ reg_val &= ~MISC_AEU_ENABLE_MCP_PRTY_BITS;
+
+ REG_WR(bp, mcp_attn_ctl_regs[i], reg_val);
+ }
+}
+
+static inline u32 bnx2x_parity_reg_mask(struct bnx2x *bp, int idx)
+{
+ if (CHIP_IS_E1(bp))
+ return bnx2x_blocks_parity_data[idx].reg_mask.e1;
+ else if (CHIP_IS_E1H(bp))
+ return bnx2x_blocks_parity_data[idx].reg_mask.e1h;
+ else if (CHIP_IS_E2(bp))
+ return bnx2x_blocks_parity_data[idx].reg_mask.e2;
+ else /* CHIP_IS_E3 */
+ return bnx2x_blocks_parity_data[idx].reg_mask.e3;
+}
+
+static inline void bnx2x_disable_blocks_parity(struct bnx2x *bp)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bnx2x_blocks_parity_data); i++) {
+ u32 dis_mask = bnx2x_parity_reg_mask(bp, i);
+
+ if (dis_mask) {
+ REG_WR(bp, bnx2x_blocks_parity_data[i].mask_addr,
+ dis_mask);
+ DP(NETIF_MSG_HW, "Setting parity mask "
+ "for %s to\t\t0x%x\n",
+ bnx2x_blocks_parity_data[i].name, dis_mask);
+ }
+ }
+
+ /* Disable MCP parity attentions */
+ bnx2x_set_mcp_parity(bp, false);
+}
+
+/**
+ * Clear the parity error status registers.
+ */
+static inline void bnx2x_clear_blocks_parity(struct bnx2x *bp)
+{
+ int i;
+ u32 reg_val, mcp_aeu_bits =
+ AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY |
+ AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY |
+ AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY |
+ AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY;
+
+ /* Clear SEM_FAST parities */
+ REG_WR(bp, XSEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
+ REG_WR(bp, TSEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
+ REG_WR(bp, USEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
+ REG_WR(bp, CSEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
+
+ for (i = 0; i < ARRAY_SIZE(bnx2x_blocks_parity_data); i++) {
+ u32 reg_mask = bnx2x_parity_reg_mask(bp, i);
+
+ if (reg_mask) {
+ reg_val = REG_RD(bp, bnx2x_blocks_parity_data[i].
+ sts_clr_addr);
+ if (reg_val & reg_mask)
+ DP(NETIF_MSG_HW,
+ "Parity errors in %s: 0x%x\n",
+ bnx2x_blocks_parity_data[i].name,
+ reg_val & reg_mask);
+ }
+ }
+
+ /* Check if there were parity attentions in MCP */
+ reg_val = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_4_MCP);
+ if (reg_val & mcp_aeu_bits)
+ DP(NETIF_MSG_HW, "Parity error in MCP: 0x%x\n",
+ reg_val & mcp_aeu_bits);
+
+ /* Clear parity attentions in MCP:
+ * [7] clears Latched rom_parity
+ * [8] clears Latched ump_rx_parity
+ * [9] clears Latched ump_tx_parity
+ * [10] clears Latched scpad_parity (both ports)
+ */
+ REG_WR(bp, MISC_REG_AEU_CLR_LATCH_SIGNAL, 0x780);
+}
+
+static inline void bnx2x_enable_blocks_parity(struct bnx2x *bp)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bnx2x_blocks_parity_data); i++) {
+ u32 reg_mask = bnx2x_parity_reg_mask(bp, i);
+
+ if (reg_mask)
+ REG_WR(bp, bnx2x_blocks_parity_data[i].mask_addr,
+ bnx2x_blocks_parity_data[i].en_mask & reg_mask);
+ }
+
+ /* Enable MCP parity attentions */
+ bnx2x_set_mcp_parity(bp, true);
+}
+
+
+#endif /* BNX2X_INIT_H */
+
diff --git a/drivers/net/bnx2x/bnx2x_init_ops.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init_ops.h
index aafd0232393f..7ec1724753ad 100644
--- a/drivers/net/bnx2x/bnx2x_init_ops.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init_ops.h
@@ -15,13 +15,39 @@
#ifndef BNX2X_INIT_OPS_H
#define BNX2X_INIT_OPS_H
+
+#ifndef BP_ILT
+#define BP_ILT(bp) NULL
+#endif
+
+#ifndef BP_FUNC
+#define BP_FUNC(bp) 0
+#endif
+
+#ifndef BP_PORT
+#define BP_PORT(bp) 0
+#endif
+
+#ifndef BNX2X_ILT_FREE
+#define BNX2X_ILT_FREE(x, y, sz)
+#endif
+
+#ifndef BNX2X_ILT_ZALLOC
+#define BNX2X_ILT_ZALLOC(x, y, sz)
+#endif
+
+#ifndef ILOG2
+#define ILOG2(x) x
+#endif
+
static int bnx2x_gunzip(struct bnx2x *bp, const u8 *zbuf, int len);
static void bnx2x_reg_wr_ind(struct bnx2x *bp, u32 addr, u32 val);
-static void bnx2x_write_dmae_phys_len(struct bnx2x *bp, dma_addr_t phys_addr,
- u32 addr, u32 len);
+static void bnx2x_write_dmae_phys_len(struct bnx2x *bp,
+ dma_addr_t phys_addr, u32 addr,
+ u32 len);
-static void bnx2x_init_str_wr(struct bnx2x *bp, u32 addr, const u32 *data,
- u32 len)
+static void bnx2x_init_str_wr(struct bnx2x *bp, u32 addr,
+ const u32 *data, u32 len)
{
u32 i;
@@ -29,24 +55,32 @@ static void bnx2x_init_str_wr(struct bnx2x *bp, u32 addr, const u32 *data,
REG_WR(bp, addr + i*4, data[i]);
}
-static void bnx2x_init_ind_wr(struct bnx2x *bp, u32 addr, const u32 *data,
- u32 len)
+static void bnx2x_init_ind_wr(struct bnx2x *bp, u32 addr,
+ const u32 *data, u32 len)
{
u32 i;
for (i = 0; i < len; i++)
- REG_WR_IND(bp, addr + i*4, data[i]);
+ bnx2x_reg_wr_ind(bp, addr + i*4, data[i]);
}
-static void bnx2x_write_big_buf(struct bnx2x *bp, u32 addr, u32 len)
+static void bnx2x_write_big_buf(struct bnx2x *bp, u32 addr, u32 len,
+ u8 wb)
{
if (bp->dmae_ready)
bnx2x_write_dmae_phys_len(bp, GUNZIP_PHYS(bp), addr, len);
+ else if (wb)
+ /*
+ * Wide bus registers with no dmae need to be written
+ * using indirect write.
+ */
+ bnx2x_init_ind_wr(bp, addr, GUNZIP_BUF(bp), len);
else
bnx2x_init_str_wr(bp, addr, GUNZIP_BUF(bp), len);
}
-static void bnx2x_init_fill(struct bnx2x *bp, u32 addr, int fill, u32 len)
+static void bnx2x_init_fill(struct bnx2x *bp, u32 addr, int fill,
+ u32 len, u8 wb)
{
u32 buf_len = (((len*4) > FW_BUF_SIZE) ? FW_BUF_SIZE : (len*4));
u32 buf_len32 = buf_len/4;
@@ -57,12 +91,20 @@ static void bnx2x_init_fill(struct bnx2x *bp, u32 addr, int fill, u32 len)
for (i = 0; i < len; i += buf_len32) {
u32 cur_len = min(buf_len32, len - i);
- bnx2x_write_big_buf(bp, addr + i*4, cur_len);
+ bnx2x_write_big_buf(bp, addr + i*4, cur_len, wb);
}
}
-static void bnx2x_init_wr_64(struct bnx2x *bp, u32 addr, const u32 *data,
- u32 len64)
+static void bnx2x_write_big_buf_wb(struct bnx2x *bp, u32 addr, u32 len)
+{
+ if (bp->dmae_ready)
+ bnx2x_write_dmae_phys_len(bp, GUNZIP_PHYS(bp), addr, len);
+ else
+ bnx2x_init_ind_wr(bp, addr, GUNZIP_BUF(bp), len);
+}
+
+static void bnx2x_init_wr_64(struct bnx2x *bp, u32 addr,
+ const u32 *data, u32 len64)
{
u32 buf_len32 = FW_BUF_SIZE/4;
u32 len = len64*2;
@@ -82,7 +124,7 @@ static void bnx2x_init_wr_64(struct bnx2x *bp, u32 addr, const u32 *data,
for (i = 0; i < len; i += buf_len32) {
u32 cur_len = min(buf_len32, len - i);
- bnx2x_write_big_buf(bp, addr + i*4, cur_len);
+ bnx2x_write_big_buf_wb(bp, addr + i*4, cur_len);
}
}
@@ -100,7 +142,8 @@ static void bnx2x_init_wr_64(struct bnx2x *bp, u32 addr, const u32 *data,
#define IF_IS_PRAM_ADDR(base, addr) \
if (((base) <= (addr)) && ((base) + 0x40000 >= (addr)))
-static const u8 *bnx2x_sel_blob(struct bnx2x *bp, u32 addr, const u8 *data)
+static const u8 *bnx2x_sel_blob(struct bnx2x *bp, u32 addr,
+ const u8 *data)
{
IF_IS_INT_TABLE_ADDR(TSEM_REG_INT_TABLE, addr)
data = INIT_TSEM_INT_TABLE_DATA(bp);
@@ -129,31 +172,17 @@ static const u8 *bnx2x_sel_blob(struct bnx2x *bp, u32 addr, const u8 *data)
return data;
}
-static void bnx2x_write_big_buf_wb(struct bnx2x *bp, u32 addr, u32 len)
+static void bnx2x_init_wr_wb(struct bnx2x *bp, u32 addr,
+ const u32 *data, u32 len)
{
if (bp->dmae_ready)
- bnx2x_write_dmae_phys_len(bp, GUNZIP_PHYS(bp), addr, len);
+ VIRT_WR_DMAE_LEN(bp, data, addr, len, 0);
else
- bnx2x_init_ind_wr(bp, addr, GUNZIP_BUF(bp), len);
-}
-
-static void bnx2x_init_wr_wb(struct bnx2x *bp, u32 addr, const u32 *data,
- u32 len)
-{
- const u32 *old_data = data;
-
- data = (const u32 *)bnx2x_sel_blob(bp, addr, (const u8 *)data);
-
- if (bp->dmae_ready) {
- if (old_data != data)
- VIRT_WR_DMAE_LEN(bp, data, addr, len, 1);
- else
- VIRT_WR_DMAE_LEN(bp, data, addr, len, 0);
- } else
bnx2x_init_ind_wr(bp, addr, data, len);
}
-static void bnx2x_wr_64(struct bnx2x *bp, u32 reg, u32 val_lo, u32 val_hi)
+static void bnx2x_wr_64(struct bnx2x *bp, u32 reg, u32 val_lo,
+ u32 val_hi)
{
u32 wb_write[2];
@@ -161,8 +190,8 @@ static void bnx2x_wr_64(struct bnx2x *bp, u32 reg, u32 val_lo, u32 val_hi)
wb_write[1] = val_hi;
REG_WR_DMAE_LEN(bp, reg, wb_write, 2);
}
-
-static void bnx2x_init_wr_zp(struct bnx2x *bp, u32 addr, u32 len, u32 blob_off)
+static void bnx2x_init_wr_zp(struct bnx2x *bp, u32 addr, u32 len,
+ u32 blob_off)
{
const u8 *data = NULL;
int rc;
@@ -186,39 +215,33 @@ static void bnx2x_init_wr_zp(struct bnx2x *bp, u32 addr, u32 len, u32 blob_off)
static void bnx2x_init_block(struct bnx2x *bp, u32 block, u32 stage)
{
u16 op_start =
- INIT_OPS_OFFSETS(bp)[BLOCK_OPS_IDX(block, stage, STAGE_START)];
+ INIT_OPS_OFFSETS(bp)[BLOCK_OPS_IDX(block, stage,
+ STAGE_START)];
u16 op_end =
- INIT_OPS_OFFSETS(bp)[BLOCK_OPS_IDX(block, stage, STAGE_END)];
+ INIT_OPS_OFFSETS(bp)[BLOCK_OPS_IDX(block, stage,
+ STAGE_END)];
union init_op *op;
- int hw_wr;
- u32 i, op_type, addr, len;
+ u32 op_idx, op_type, addr, len;
const u32 *data, *data_base;
/* If empty block */
if (op_start == op_end)
return;
- if (CHIP_REV_IS_FPGA(bp))
- hw_wr = OP_WR_FPGA;
- else if (CHIP_REV_IS_EMUL(bp))
- hw_wr = OP_WR_EMUL;
- else
- hw_wr = OP_WR_ASIC;
-
data_base = INIT_DATA(bp);
- for (i = op_start; i < op_end; i++) {
-
- op = (union init_op *)&(INIT_OPS(bp)[i]);
+ for (op_idx = op_start; op_idx < op_end; op_idx++) {
- op_type = op->str_wr.op;
- addr = op->str_wr.offset;
- len = op->str_wr.data_len;
- data = data_base + op->str_wr.data_off;
-
- /* HW/EMUL specific */
- if ((op_type > OP_WB) && (op_type == hw_wr))
- op_type = OP_WR;
+ op = (union init_op *)&(INIT_OPS(bp)[op_idx]);
+ /* Get generic data */
+ op_type = op->raw.op;
+ addr = op->raw.offset;
+ /* Get data that's used for OP_SW, OP_WB, OP_FW, OP_ZP and
+ * OP_WR64 (we assume that op_arr_write and op_write have the
+ * same structure).
+ */
+ len = op->arr_wr.data_len;
+ data = data_base + op->arr_wr.data_off;
switch (op_type) {
case OP_RD:
@@ -233,21 +256,39 @@ static void bnx2x_init_block(struct bnx2x *bp, u32 block, u32 stage)
case OP_WB:
bnx2x_init_wr_wb(bp, addr, data, len);
break;
- case OP_SI:
- bnx2x_init_ind_wr(bp, addr, data, len);
- break;
case OP_ZR:
- bnx2x_init_fill(bp, addr, 0, op->zero.len);
+ bnx2x_init_fill(bp, addr, 0, op->zero.len, 0);
+ break;
+ case OP_WB_ZR:
+ bnx2x_init_fill(bp, addr, 0, op->zero.len, 1);
break;
case OP_ZP:
bnx2x_init_wr_zp(bp, addr, len,
- op->str_wr.data_off);
+ op->arr_wr.data_off);
break;
case OP_WR_64:
bnx2x_init_wr_64(bp, addr, data, len);
break;
+ case OP_IF_MODE_AND:
+ /* if any of the flags doesn't match, skip the
+ * conditional block.
+ */
+ if ((INIT_MODE_FLAGS(bp) &
+ op->if_mode.mode_bit_map) !=
+ op->if_mode.mode_bit_map)
+ op_idx += op->if_mode.cmd_offset;
+ break;
+ case OP_IF_MODE_OR:
+ /* if all the flags don't match, skip the conditional
+ * block.
+ */
+ if ((INIT_MODE_FLAGS(bp) &
+ op->if_mode.mode_bit_map) == 0)
+ op_idx += op->if_mode.cmd_offset;
+ break;
default:
- /* happens whenever an op is of a diff HW */
+ /* Should never get here! */
+
break;
}
}
@@ -417,7 +458,8 @@ static const struct arb_line write_arb_addr[NUM_WR_Q-1] = {
PXP2_REG_RQ_BW_WR_UBOUND30}
};
-static void bnx2x_init_pxp_arb(struct bnx2x *bp, int r_order, int w_order)
+static void bnx2x_init_pxp_arb(struct bnx2x *bp, int r_order,
+ int w_order)
{
u32 val, i;
@@ -491,19 +533,21 @@ static void bnx2x_init_pxp_arb(struct bnx2x *bp, int r_order, int w_order)
if ((CHIP_IS_E1(bp) || CHIP_IS_E1H(bp)) && (r_order == MAX_RD_ORD))
REG_WR(bp, PXP2_REG_RQ_PDR_LIMIT, 0xe00);
- if (CHIP_IS_E2(bp))
+ if (CHIP_IS_E3(bp))
+ REG_WR(bp, PXP2_REG_WR_USDMDP_TH, (0x4 << w_order));
+ else if (CHIP_IS_E2(bp))
REG_WR(bp, PXP2_REG_WR_USDMDP_TH, (0x8 << w_order));
else
REG_WR(bp, PXP2_REG_WR_USDMDP_TH, (0x18 << w_order));
- if (CHIP_IS_E1H(bp) || CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1(bp)) {
/* MPS w_order optimal TH presently TH
* 128 0 0 2
* 256 1 1 3
* >=512 2 2 3
*/
/* DMAE is special */
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1H(bp)) {
/* E2 can use optimal TH */
val = w_order;
REG_WR(bp, PXP2_REG_WR_DMAE_MPS, val);
@@ -557,8 +601,8 @@ static void bnx2x_init_pxp_arb(struct bnx2x *bp, int r_order, int w_order)
#define ILT_ADDR2(x) ((u32)((1 << 20) | ((u64)x >> 44)))
#define ILT_RANGE(f, l) (((l) << 10) | f)
-static int bnx2x_ilt_line_mem_op(struct bnx2x *bp, struct ilt_line *line,
- u32 size, u8 memop)
+static int bnx2x_ilt_line_mem_op(struct bnx2x *bp,
+ struct ilt_line *line, u32 size, u8 memop)
{
if (memop == ILT_MEMOP_FREE) {
BNX2X_ILT_FREE(line->page, line->page_mapping, line->size);
@@ -572,7 +616,8 @@ static int bnx2x_ilt_line_mem_op(struct bnx2x *bp, struct ilt_line *line,
}
-static int bnx2x_ilt_client_mem_op(struct bnx2x *bp, int cli_num, u8 memop)
+static int bnx2x_ilt_client_mem_op(struct bnx2x *bp, int cli_num,
+ u8 memop)
{
int i, rc;
struct bnx2x_ilt *ilt = BP_ILT(bp);
@@ -617,8 +662,8 @@ static void bnx2x_ilt_line_wr(struct bnx2x *bp, int abs_idx,
bnx2x_wr_64(bp, reg, ILT_ADDR1(page_mapping), ILT_ADDR2(page_mapping));
}
-static void bnx2x_ilt_line_init_op(struct bnx2x *bp, struct bnx2x_ilt *ilt,
- int idx, u8 initop)
+static void bnx2x_ilt_line_init_op(struct bnx2x *bp,
+ struct bnx2x_ilt *ilt, int idx, u8 initop)
{
dma_addr_t null_mapping;
int abs_idx = ilt->start_line + idx;
@@ -733,7 +778,7 @@ static void bnx2x_ilt_init_op(struct bnx2x *bp, u8 initop)
}
static void bnx2x_ilt_init_client_psz(struct bnx2x *bp, int cli_num,
- u32 psz_reg, u8 initop)
+ u32 psz_reg, u8 initop)
{
struct bnx2x_ilt *ilt = BP_ILT(bp);
struct ilt_client_info *ilt_cli = &ilt->clients[cli_num];
@@ -848,7 +893,8 @@ static void bnx2x_src_init_t2(struct bnx2x *bp, struct src_ent *t2,
/* Initialize T2 */
for (i = 0; i < src_cid_count-1; i++)
- t2[i].next = (u64)(t2_mapping + (i+1)*sizeof(struct src_ent));
+ t2[i].next = (u64)(t2_mapping +
+ (i+1)*sizeof(struct src_ent));
/* tell the searcher where the T2 table is */
REG_WR(bp, SRC_REG_COUNTFREE0 + port*4, src_cid_count);
diff --git a/drivers/net/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
index 076e11f5769f..bce203fa4b9e 100644
--- a/drivers/net/bnx2x/bnx2x_link.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
@@ -25,6 +25,8 @@
#include <linux/mutex.h>
#include "bnx2x.h"
+#include "bnx2x_cmn.h"
+
/********************************************************/
#define ETH_HLEN 14
@@ -35,7 +37,17 @@
#define ETH_MAX_JUMBO_PACKET_SIZE 9600
#define MDIO_ACCESS_TIMEOUT 1000
#define BMAC_CONTROL_RX_ENABLE 2
-
+#define WC_LANE_MAX 4
+#define I2C_SWITCH_WIDTH 2
+#define I2C_BSC0 0
+#define I2C_BSC1 1
+#define I2C_WA_RETRY_CNT 3
+#define MCPR_IMC_COMMAND_READ_OP 1
+#define MCPR_IMC_COMMAND_WRITE_OP 2
+
+/* LED Blink rate that will achieve ~15.9Hz */
+#define LED_BLINK_RATE_VAL_E3 354
+#define LED_BLINK_RATE_VAL_E1X_E2 480
/***********************************************************/
/* Shortcut definitions */
/***********************************************************/
@@ -103,16 +115,13 @@
MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_HIG
#define GP_STATUS_10G_CX4 \
MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_CX4
-#define GP_STATUS_12G_HIG \
- MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_12G_HIG
-#define GP_STATUS_12_5G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_12_5G
-#define GP_STATUS_13G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_13G
-#define GP_STATUS_15G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_15G
-#define GP_STATUS_16G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_16G
#define GP_STATUS_1G_KX MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_1G_KX
#define GP_STATUS_10G_KX4 \
MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_KX4
-
+#define GP_STATUS_10G_KR MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_KR
+#define GP_STATUS_10G_XFI MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_XFI
+#define GP_STATUS_20G_DXGXS MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_20G_DXGXS
+#define GP_STATUS_10G_SFI MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_SFI
#define LINK_10THD LINK_STATUS_SPEED_AND_DUPLEX_10THD
#define LINK_10TFD LINK_STATUS_SPEED_AND_DUPLEX_10TFD
#define LINK_100TXHD LINK_STATUS_SPEED_AND_DUPLEX_100TXHD
@@ -126,20 +135,10 @@
#define LINK_2500XFD LINK_STATUS_SPEED_AND_DUPLEX_2500XFD
#define LINK_10GTFD LINK_STATUS_SPEED_AND_DUPLEX_10GTFD
#define LINK_10GXFD LINK_STATUS_SPEED_AND_DUPLEX_10GXFD
-#define LINK_12GTFD LINK_STATUS_SPEED_AND_DUPLEX_12GTFD
-#define LINK_12GXFD LINK_STATUS_SPEED_AND_DUPLEX_12GXFD
-#define LINK_12_5GTFD LINK_STATUS_SPEED_AND_DUPLEX_12_5GTFD
-#define LINK_12_5GXFD LINK_STATUS_SPEED_AND_DUPLEX_12_5GXFD
-#define LINK_13GTFD LINK_STATUS_SPEED_AND_DUPLEX_13GTFD
-#define LINK_13GXFD LINK_STATUS_SPEED_AND_DUPLEX_13GXFD
-#define LINK_15GTFD LINK_STATUS_SPEED_AND_DUPLEX_15GTFD
-#define LINK_15GXFD LINK_STATUS_SPEED_AND_DUPLEX_15GXFD
-#define LINK_16GTFD LINK_STATUS_SPEED_AND_DUPLEX_16GTFD
-#define LINK_16GXFD LINK_STATUS_SPEED_AND_DUPLEX_16GXFD
-
-#define PHY_XGXS_FLAG 0x1
-#define PHY_SGMII_FLAG 0x2
-#define PHY_SERDES_FLAG 0x4
+#define LINK_20GTFD LINK_STATUS_SPEED_AND_DUPLEX_20GTFD
+#define LINK_20GXFD LINK_STATUS_SPEED_AND_DUPLEX_20GXFD
+
+
/* */
#define SFP_EEPROM_CON_TYPE_ADDR 0x2
@@ -165,8 +164,105 @@
#define EDC_MODE_PASSIVE_DAC 0x0055
+/* BRB thresholds for E2*/
+#define PFC_E2_BRB_MAC_PAUSE_XOFF_THR_PAUSE 170
+#define PFC_E2_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE 0
+
+#define PFC_E2_BRB_MAC_PAUSE_XON_THR_PAUSE 250
+#define PFC_E2_BRB_MAC_PAUSE_XON_THR_NON_PAUSE 0
+
+#define PFC_E2_BRB_MAC_FULL_XOFF_THR_PAUSE 10
+#define PFC_E2_BRB_MAC_FULL_XOFF_THR_NON_PAUSE 90
+
+#define PFC_E2_BRB_MAC_FULL_XON_THR_PAUSE 50
+#define PFC_E2_BRB_MAC_FULL_XON_THR_NON_PAUSE 250
+
+/* BRB thresholds for E3A0 */
+#define PFC_E3A0_BRB_MAC_PAUSE_XOFF_THR_PAUSE 290
+#define PFC_E3A0_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE 0
+
+#define PFC_E3A0_BRB_MAC_PAUSE_XON_THR_PAUSE 410
+#define PFC_E3A0_BRB_MAC_PAUSE_XON_THR_NON_PAUSE 0
+
+#define PFC_E3A0_BRB_MAC_FULL_XOFF_THR_PAUSE 10
+#define PFC_E3A0_BRB_MAC_FULL_XOFF_THR_NON_PAUSE 170
+
+#define PFC_E3A0_BRB_MAC_FULL_XON_THR_PAUSE 50
+#define PFC_E3A0_BRB_MAC_FULL_XON_THR_NON_PAUSE 410
+
+
+/* BRB thresholds for E3B0 2 port mode*/
+#define PFC_E3B0_2P_BRB_MAC_PAUSE_XOFF_THR_PAUSE 1025
+#define PFC_E3B0_2P_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE 0
+
+#define PFC_E3B0_2P_BRB_MAC_PAUSE_XON_THR_PAUSE 1025
+#define PFC_E3B0_2P_BRB_MAC_PAUSE_XON_THR_NON_PAUSE 0
+
+#define PFC_E3B0_2P_BRB_MAC_FULL_XOFF_THR_PAUSE 10
+#define PFC_E3B0_2P_BRB_MAC_FULL_XOFF_THR_NON_PAUSE 1025
+
+#define PFC_E3B0_2P_BRB_MAC_FULL_XON_THR_PAUSE 50
+#define PFC_E3B0_2P_BRB_MAC_FULL_XON_THR_NON_PAUSE 1025
+
+/* only for E3B0*/
+#define PFC_E3B0_2P_BRB_FULL_LB_XOFF_THR 1025
+#define PFC_E3B0_2P_BRB_FULL_LB_XON_THR 1025
+
+/* Lossy +Lossless GUARANTIED == GUART */
+#define PFC_E3B0_2P_MIX_PAUSE_LB_GUART 284
+/* Lossless +Lossless*/
+#define PFC_E3B0_2P_PAUSE_LB_GUART 236
+/* Lossy +Lossy*/
+#define PFC_E3B0_2P_NON_PAUSE_LB_GUART 342
+
+/* Lossy +Lossless*/
+#define PFC_E3B0_2P_MIX_PAUSE_MAC_0_CLASS_T_GUART 284
+/* Lossless +Lossless*/
+#define PFC_E3B0_2P_PAUSE_MAC_0_CLASS_T_GUART 236
+/* Lossy +Lossy*/
+#define PFC_E3B0_2P_NON_PAUSE_MAC_0_CLASS_T_GUART 336
+#define PFC_E3B0_2P_BRB_MAC_0_CLASS_T_GUART_HYST 80
+
+#define PFC_E3B0_2P_BRB_MAC_1_CLASS_T_GUART 0
+#define PFC_E3B0_2P_BRB_MAC_1_CLASS_T_GUART_HYST 0
+
+/* BRB thresholds for E3B0 4 port mode */
+#define PFC_E3B0_4P_BRB_MAC_PAUSE_XOFF_THR_PAUSE 304
+#define PFC_E3B0_4P_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE 0
+
+#define PFC_E3B0_4P_BRB_MAC_PAUSE_XON_THR_PAUSE 384
+#define PFC_E3B0_4P_BRB_MAC_PAUSE_XON_THR_NON_PAUSE 0
+
+#define PFC_E3B0_4P_BRB_MAC_FULL_XOFF_THR_PAUSE 10
+#define PFC_E3B0_4P_BRB_MAC_FULL_XOFF_THR_NON_PAUSE 304
+
+#define PFC_E3B0_4P_BRB_MAC_FULL_XON_THR_PAUSE 50
+#define PFC_E3B0_4P_BRB_MAC_FULL_XON_THR_NON_PAUSE 384
+
+
+/* only for E3B0*/
+#define PFC_E3B0_4P_BRB_FULL_LB_XOFF_THR 304
+#define PFC_E3B0_4P_BRB_FULL_LB_XON_THR 384
+#define PFC_E3B0_4P_LB_GUART 120
+
+#define PFC_E3B0_4P_BRB_MAC_0_CLASS_T_GUART 120
+#define PFC_E3B0_4P_BRB_MAC_0_CLASS_T_GUART_HYST 80
+
+#define PFC_E3B0_4P_BRB_MAC_1_CLASS_T_GUART 80
+#define PFC_E3B0_4P_BRB_MAC_1_CLASS_T_GUART_HYST 120
+
+#define DCBX_INVALID_COS (0xFF)
+
#define ETS_BW_LIMIT_CREDIT_UPPER_BOUND (0x5000)
#define ETS_BW_LIMIT_CREDIT_WEIGHT (0x5000)
+#define ETS_E3B0_NIG_MIN_W_VAL_UP_TO_10GBPS (1360)
+#define ETS_E3B0_NIG_MIN_W_VAL_20GBPS (2720)
+#define ETS_E3B0_PBF_MIN_W_VAL (10000)
+
+#define MAX_PACKET_SIZE (9700)
+#define WC_UC_TIMEOUT 100
+#define MAX_KR_LINK_RETRY 4
+
/**********************************************************/
/* INTERFACE */
/**********************************************************/
@@ -202,14 +298,86 @@ static u32 bnx2x_bits_dis(struct bnx2x *bp, u32 reg, u32 bits)
}
/******************************************************************/
+/* EPIO/GPIO section */
+/******************************************************************/
+static void bnx2x_get_epio(struct bnx2x *bp, u32 epio_pin, u32 *en)
+{
+ u32 epio_mask, gp_oenable;
+ *en = 0;
+ /* Sanity check */
+ if (epio_pin > 31) {
+ DP(NETIF_MSG_LINK, "Invalid EPIO pin %d to get\n", epio_pin);
+ return;
+ }
+
+ epio_mask = 1 << epio_pin;
+ /* Set this EPIO to output */
+ gp_oenable = REG_RD(bp, MCP_REG_MCPR_GP_OENABLE);
+ REG_WR(bp, MCP_REG_MCPR_GP_OENABLE, gp_oenable & ~epio_mask);
+
+ *en = (REG_RD(bp, MCP_REG_MCPR_GP_INPUTS) & epio_mask) >> epio_pin;
+}
+static void bnx2x_set_epio(struct bnx2x *bp, u32 epio_pin, u32 en)
+{
+ u32 epio_mask, gp_output, gp_oenable;
+
+ /* Sanity check */
+ if (epio_pin > 31) {
+ DP(NETIF_MSG_LINK, "Invalid EPIO pin %d to set\n", epio_pin);
+ return;
+ }
+ DP(NETIF_MSG_LINK, "Setting EPIO pin %d to %d\n", epio_pin, en);
+ epio_mask = 1 << epio_pin;
+ /* Set this EPIO to output */
+ gp_output = REG_RD(bp, MCP_REG_MCPR_GP_OUTPUTS);
+ if (en)
+ gp_output |= epio_mask;
+ else
+ gp_output &= ~epio_mask;
+
+ REG_WR(bp, MCP_REG_MCPR_GP_OUTPUTS, gp_output);
+
+ /* Set the value for this EPIO */
+ gp_oenable = REG_RD(bp, MCP_REG_MCPR_GP_OENABLE);
+ REG_WR(bp, MCP_REG_MCPR_GP_OENABLE, gp_oenable | epio_mask);
+}
+
+static void bnx2x_set_cfg_pin(struct bnx2x *bp, u32 pin_cfg, u32 val)
+{
+ if (pin_cfg == PIN_CFG_NA)
+ return;
+ if (pin_cfg >= PIN_CFG_EPIO0) {
+ bnx2x_set_epio(bp, pin_cfg - PIN_CFG_EPIO0, val);
+ } else {
+ u8 gpio_num = (pin_cfg - PIN_CFG_GPIO0_P0) & 0x3;
+ u8 gpio_port = (pin_cfg - PIN_CFG_GPIO0_P0) >> 2;
+ bnx2x_set_gpio(bp, gpio_num, (u8)val, gpio_port);
+ }
+}
+
+static u32 bnx2x_get_cfg_pin(struct bnx2x *bp, u32 pin_cfg, u32 *val)
+{
+ if (pin_cfg == PIN_CFG_NA)
+ return -EINVAL;
+ if (pin_cfg >= PIN_CFG_EPIO0) {
+ bnx2x_get_epio(bp, pin_cfg - PIN_CFG_EPIO0, val);
+ } else {
+ u8 gpio_num = (pin_cfg - PIN_CFG_GPIO0_P0) & 0x3;
+ u8 gpio_port = (pin_cfg - PIN_CFG_GPIO0_P0) >> 2;
+ *val = bnx2x_get_gpio(bp, gpio_num, gpio_port);
+ }
+ return 0;
+
+}
+/******************************************************************/
/* ETS section */
/******************************************************************/
-void bnx2x_ets_disabled(struct link_params *params)
+static void bnx2x_ets_e2e3a0_disabled(struct link_params *params)
{
/* ETS disabled configuration*/
struct bnx2x *bp = params->bp;
- DP(NETIF_MSG_LINK, "ETS disabled configuration\n");
+ DP(NETIF_MSG_LINK, "ETS E2E3 disabled configuration\n");
/*
* mapping between entry priority to client number (0,1,2 -debug and
@@ -262,7 +430,750 @@ void bnx2x_ets_disabled(struct link_params *params)
/* Defines the number of consecutive slots for the strict priority */
REG_WR(bp, PBF_REG_NUM_STRICT_ARB_SLOTS, 0);
}
+/******************************************************************************
+* Description:
+* Getting min_w_val will be set according to line speed .
+*.
+******************************************************************************/
+static u32 bnx2x_ets_get_min_w_val_nig(const struct link_vars *vars)
+{
+ u32 min_w_val = 0;
+ /* Calculate min_w_val.*/
+ if (vars->link_up) {
+ if (SPEED_20000 == vars->line_speed)
+ min_w_val = ETS_E3B0_NIG_MIN_W_VAL_20GBPS;
+ else
+ min_w_val = ETS_E3B0_NIG_MIN_W_VAL_UP_TO_10GBPS;
+ } else
+ min_w_val = ETS_E3B0_NIG_MIN_W_VAL_20GBPS;
+ /**
+ * If the link isn't up (static configuration for example ) The
+ * link will be according to 20GBPS.
+ */
+ return min_w_val;
+}
+/******************************************************************************
+* Description:
+* Getting credit upper bound form min_w_val.
+*.
+******************************************************************************/
+static u32 bnx2x_ets_get_credit_upper_bound(const u32 min_w_val)
+{
+ const u32 credit_upper_bound = (u32)MAXVAL((150 * min_w_val),
+ MAX_PACKET_SIZE);
+ return credit_upper_bound;
+}
+/******************************************************************************
+* Description:
+* Set credit upper bound for NIG.
+*.
+******************************************************************************/
+static void bnx2x_ets_e3b0_set_credit_upper_bound_nig(
+ const struct link_params *params,
+ const u32 min_w_val)
+{
+ struct bnx2x *bp = params->bp;
+ const u8 port = params->port;
+ const u32 credit_upper_bound =
+ bnx2x_ets_get_credit_upper_bound(min_w_val);
+
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_0 :
+ NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_0, credit_upper_bound);
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_1 :
+ NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_1, credit_upper_bound);
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_2 :
+ NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_2, credit_upper_bound);
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_3 :
+ NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_3, credit_upper_bound);
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_4 :
+ NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_4, credit_upper_bound);
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_5 :
+ NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_5, credit_upper_bound);
+
+ if (0 == port) {
+ REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_6,
+ credit_upper_bound);
+ REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_7,
+ credit_upper_bound);
+ REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_8,
+ credit_upper_bound);
+ }
+}
+/******************************************************************************
+* Description:
+* Will return the NIG ETS registers to init values.Except
+* credit_upper_bound.
+* That isn't used in this configuration (No WFQ is enabled) and will be
+* configured acording to spec
+*.
+******************************************************************************/
+static void bnx2x_ets_e3b0_nig_disabled(const struct link_params *params,
+ const struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ const u8 port = params->port;
+ const u32 min_w_val = bnx2x_ets_get_min_w_val_nig(vars);
+ /**
+ * mapping between entry priority to client number (0,1,2 -debug and
+ * management clients, 3 - COS0 client, 4 - COS1, ... 8 -
+ * COS5)(HIGHEST) 4bits client num.TODO_ETS - Should be done by
+ * reset value or init tool
+ */
+ if (port) {
+ REG_WR(bp, NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_LSB, 0x543210);
+ REG_WR(bp, NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_MSB, 0x0);
+ } else {
+ REG_WR(bp, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_LSB, 0x76543210);
+ REG_WR(bp, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_MSB, 0x8);
+ }
+ /**
+ * For strict priority entries defines the number of consecutive
+ * slots for the highest priority.
+ */
+ /* TODO_ETS - Should be done by reset value or init tool */
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_NUM_STRICT_ARB_SLOTS :
+ NIG_REG_P1_TX_ARB_NUM_STRICT_ARB_SLOTS, 0x100);
+ /**
+ * mapping between the CREDIT_WEIGHT registers and actual client
+ * numbers
+ */
+ /* TODO_ETS - Should be done by reset value or init tool */
+ if (port) {
+ /*Port 1 has 6 COS*/
+ REG_WR(bp, NIG_REG_P1_TX_ARB_CLIENT_CREDIT_MAP2_LSB, 0x210543);
+ REG_WR(bp, NIG_REG_P1_TX_ARB_CLIENT_CREDIT_MAP2_MSB, 0x0);
+ } else {
+ /*Port 0 has 9 COS*/
+ REG_WR(bp, NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP2_LSB,
+ 0x43210876);
+ REG_WR(bp, NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP2_MSB, 0x5);
+ }
+
+ /**
+ * Bitmap of 5bits length. Each bit specifies whether the entry behaves
+ * as strict. Bits 0,1,2 - debug and management entries, 3 -
+ * COS0 entry, 4 - COS1 entry.
+ * COS1 | COS0 | DEBUG1 | DEBUG0 | MGMT
+ * bit4 bit3 bit2 bit1 bit0
+ * MCP and debug are strict
+ */
+ if (port)
+ REG_WR(bp, NIG_REG_P1_TX_ARB_CLIENT_IS_STRICT, 0x3f);
+ else
+ REG_WR(bp, NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, 0x1ff);
+ /* defines which entries (clients) are subjected to WFQ arbitration */
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CLIENT_IS_SUBJECT2WFQ :
+ NIG_REG_P0_TX_ARB_CLIENT_IS_SUBJECT2WFQ, 0);
+
+ /**
+ * Please notice the register address are note continuous and a
+ * for here is note appropriate.In 2 port mode port0 only COS0-5
+ * can be used. DEBUG1,DEBUG1,MGMT are never used for WFQ* In 4
+ * port mode port1 only COS0-2 can be used. DEBUG1,DEBUG1,MGMT
+ * are never used for WFQ
+ */
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_0 :
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_0, 0x0);
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_1 :
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_1, 0x0);
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_2 :
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_2, 0x0);
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_3 :
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_3, 0x0);
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_4 :
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_4, 0x0);
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_5 :
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_5, 0x0);
+ if (0 == port) {
+ REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_6, 0x0);
+ REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_7, 0x0);
+ REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_8, 0x0);
+ }
+
+ bnx2x_ets_e3b0_set_credit_upper_bound_nig(params, min_w_val);
+}
+/******************************************************************************
+* Description:
+* Set credit upper bound for PBF.
+*.
+******************************************************************************/
+static void bnx2x_ets_e3b0_set_credit_upper_bound_pbf(
+ const struct link_params *params,
+ const u32 min_w_val)
+{
+ struct bnx2x *bp = params->bp;
+ const u32 credit_upper_bound =
+ bnx2x_ets_get_credit_upper_bound(min_w_val);
+ const u8 port = params->port;
+ u32 base_upper_bound = 0;
+ u8 max_cos = 0;
+ u8 i = 0;
+ /**
+ * In 2 port mode port0 has COS0-5 that can be used for WFQ.In 4
+ * port mode port1 has COS0-2 that can be used for WFQ.
+ */
+ if (0 == port) {
+ base_upper_bound = PBF_REG_COS0_UPPER_BOUND_P0;
+ max_cos = DCBX_E3B0_MAX_NUM_COS_PORT0;
+ } else {
+ base_upper_bound = PBF_REG_COS0_UPPER_BOUND_P1;
+ max_cos = DCBX_E3B0_MAX_NUM_COS_PORT1;
+ }
+
+ for (i = 0; i < max_cos; i++)
+ REG_WR(bp, base_upper_bound + (i << 2), credit_upper_bound);
+}
+
+/******************************************************************************
+* Description:
+* Will return the PBF ETS registers to init values.Except
+* credit_upper_bound.
+* That isn't used in this configuration (No WFQ is enabled) and will be
+* configured acording to spec
+*.
+******************************************************************************/
+static void bnx2x_ets_e3b0_pbf_disabled(const struct link_params *params)
+{
+ struct bnx2x *bp = params->bp;
+ const u8 port = params->port;
+ const u32 min_w_val_pbf = ETS_E3B0_PBF_MIN_W_VAL;
+ u8 i = 0;
+ u32 base_weight = 0;
+ u8 max_cos = 0;
+
+ /**
+ * mapping between entry priority to client number 0 - COS0
+ * client, 2 - COS1, ... 5 - COS5)(HIGHEST) 4bits client num.
+ * TODO_ETS - Should be done by reset value or init tool
+ */
+ if (port)
+ /* 0x688 (|011|0 10|00 1|000) */
+ REG_WR(bp, PBF_REG_ETS_ARB_PRIORITY_CLIENT_P1 , 0x688);
+ else
+ /* (10 1|100 |011|0 10|00 1|000) */
+ REG_WR(bp, PBF_REG_ETS_ARB_PRIORITY_CLIENT_P0 , 0x2C688);
+
+ /* TODO_ETS - Should be done by reset value or init tool */
+ if (port)
+ /* 0x688 (|011|0 10|00 1|000)*/
+ REG_WR(bp, PBF_REG_ETS_ARB_CLIENT_CREDIT_MAP_P1, 0x688);
+ else
+ /* 0x2C688 (10 1|100 |011|0 10|00 1|000) */
+ REG_WR(bp, PBF_REG_ETS_ARB_CLIENT_CREDIT_MAP_P0, 0x2C688);
+
+ REG_WR(bp, (port) ? PBF_REG_ETS_ARB_NUM_STRICT_ARB_SLOTS_P1 :
+ PBF_REG_ETS_ARB_NUM_STRICT_ARB_SLOTS_P0 , 0x100);
+
+
+ REG_WR(bp, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P1 :
+ PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P0 , 0);
+
+ REG_WR(bp, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P1 :
+ PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P0 , 0);
+ /**
+ * In 2 port mode port0 has COS0-5 that can be used for WFQ.
+ * In 4 port mode port1 has COS0-2 that can be used for WFQ.
+ */
+ if (0 == port) {
+ base_weight = PBF_REG_COS0_WEIGHT_P0;
+ max_cos = DCBX_E3B0_MAX_NUM_COS_PORT0;
+ } else {
+ base_weight = PBF_REG_COS0_WEIGHT_P1;
+ max_cos = DCBX_E3B0_MAX_NUM_COS_PORT1;
+ }
+
+ for (i = 0; i < max_cos; i++)
+ REG_WR(bp, base_weight + (0x4 * i), 0);
+
+ bnx2x_ets_e3b0_set_credit_upper_bound_pbf(params, min_w_val_pbf);
+}
+/******************************************************************************
+* Description:
+* E3B0 disable will return basicly the values to init values.
+*.
+******************************************************************************/
+static int bnx2x_ets_e3b0_disabled(const struct link_params *params,
+ const struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+
+ if (!CHIP_IS_E3B0(bp)) {
+ DP(NETIF_MSG_LINK,
+ "bnx2x_ets_e3b0_disabled the chip isn't E3B0\n");
+ return -EINVAL;
+ }
+
+ bnx2x_ets_e3b0_nig_disabled(params, vars);
+
+ bnx2x_ets_e3b0_pbf_disabled(params);
+
+ return 0;
+}
+
+/******************************************************************************
+* Description:
+* Disable will return basicly the values to init values.
+*.
+******************************************************************************/
+int bnx2x_ets_disabled(struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ int bnx2x_status = 0;
+
+ if ((CHIP_IS_E2(bp)) || (CHIP_IS_E3A0(bp)))
+ bnx2x_ets_e2e3a0_disabled(params);
+ else if (CHIP_IS_E3B0(bp))
+ bnx2x_status = bnx2x_ets_e3b0_disabled(params, vars);
+ else {
+ DP(NETIF_MSG_LINK, "bnx2x_ets_disabled - chip not supported\n");
+ return -EINVAL;
+ }
+
+ return bnx2x_status;
+}
+
+/******************************************************************************
+* Description
+* Set the COS mappimg to SP and BW until this point all the COS are not
+* set as SP or BW.
+******************************************************************************/
+static int bnx2x_ets_e3b0_cli_map(const struct link_params *params,
+ const struct bnx2x_ets_params *ets_params,
+ const u8 cos_sp_bitmap,
+ const u8 cos_bw_bitmap)
+{
+ struct bnx2x *bp = params->bp;
+ const u8 port = params->port;
+ const u8 nig_cli_sp_bitmap = 0x7 | (cos_sp_bitmap << 3);
+ const u8 pbf_cli_sp_bitmap = cos_sp_bitmap;
+ const u8 nig_cli_subject2wfq_bitmap = cos_bw_bitmap << 3;
+ const u8 pbf_cli_subject2wfq_bitmap = cos_bw_bitmap;
+
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CLIENT_IS_STRICT :
+ NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, nig_cli_sp_bitmap);
+
+ REG_WR(bp, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P1 :
+ PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P0 , pbf_cli_sp_bitmap);
+
+ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CLIENT_IS_SUBJECT2WFQ :
+ NIG_REG_P0_TX_ARB_CLIENT_IS_SUBJECT2WFQ,
+ nig_cli_subject2wfq_bitmap);
+
+ REG_WR(bp, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P1 :
+ PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P0,
+ pbf_cli_subject2wfq_bitmap);
+
+ return 0;
+}
+
+/******************************************************************************
+* Description:
+* This function is needed because NIG ARB_CREDIT_WEIGHT_X are
+* not continues and ARB_CREDIT_WEIGHT_0 + offset is suitable.
+******************************************************************************/
+static int bnx2x_ets_e3b0_set_cos_bw(struct bnx2x *bp,
+ const u8 cos_entry,
+ const u32 min_w_val_nig,
+ const u32 min_w_val_pbf,
+ const u16 total_bw,
+ const u8 bw,
+ const u8 port)
+{
+ u32 nig_reg_adress_crd_weight = 0;
+ u32 pbf_reg_adress_crd_weight = 0;
+ /* Calculate and set BW for this COS - use 1 instead of 0 for BW */
+ const u32 cos_bw_nig = ((bw ? bw : 1) * min_w_val_nig) / total_bw;
+ const u32 cos_bw_pbf = ((bw ? bw : 1) * min_w_val_pbf) / total_bw;
+
+ switch (cos_entry) {
+ case 0:
+ nig_reg_adress_crd_weight =
+ (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_0 :
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_0;
+ pbf_reg_adress_crd_weight = (port) ?
+ PBF_REG_COS0_WEIGHT_P1 : PBF_REG_COS0_WEIGHT_P0;
+ break;
+ case 1:
+ nig_reg_adress_crd_weight = (port) ?
+ NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_1 :
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_1;
+ pbf_reg_adress_crd_weight = (port) ?
+ PBF_REG_COS1_WEIGHT_P1 : PBF_REG_COS1_WEIGHT_P0;
+ break;
+ case 2:
+ nig_reg_adress_crd_weight = (port) ?
+ NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_2 :
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_2;
+
+ pbf_reg_adress_crd_weight = (port) ?
+ PBF_REG_COS2_WEIGHT_P1 : PBF_REG_COS2_WEIGHT_P0;
+ break;
+ case 3:
+ if (port)
+ return -EINVAL;
+ nig_reg_adress_crd_weight =
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_3;
+ pbf_reg_adress_crd_weight =
+ PBF_REG_COS3_WEIGHT_P0;
+ break;
+ case 4:
+ if (port)
+ return -EINVAL;
+ nig_reg_adress_crd_weight =
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_4;
+ pbf_reg_adress_crd_weight = PBF_REG_COS4_WEIGHT_P0;
+ break;
+ case 5:
+ if (port)
+ return -EINVAL;
+ nig_reg_adress_crd_weight =
+ NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_5;
+ pbf_reg_adress_crd_weight = PBF_REG_COS5_WEIGHT_P0;
+ break;
+ }
+
+ REG_WR(bp, nig_reg_adress_crd_weight, cos_bw_nig);
+
+ REG_WR(bp, pbf_reg_adress_crd_weight, cos_bw_pbf);
+
+ return 0;
+}
+/******************************************************************************
+* Description:
+* Calculate the total BW.A value of 0 isn't legal.
+*.
+******************************************************************************/
+static int bnx2x_ets_e3b0_get_total_bw(
+ const struct link_params *params,
+ const struct bnx2x_ets_params *ets_params,
+ u16 *total_bw)
+{
+ struct bnx2x *bp = params->bp;
+ u8 cos_idx = 0;
+
+ *total_bw = 0 ;
+ /* Calculate total BW requested */
+ for (cos_idx = 0; cos_idx < ets_params->num_of_cos; cos_idx++) {
+ if (bnx2x_cos_state_bw == ets_params->cos[cos_idx].state) {
+ *total_bw +=
+ ets_params->cos[cos_idx].params.bw_params.bw;
+ }
+ }
+
+ /* Check total BW is valid */
+ if ((100 != *total_bw) || (0 == *total_bw)) {
+ if (0 == *total_bw) {
+ DP(NETIF_MSG_LINK,
+ "bnx2x_ets_E3B0_config toatl BW shouldn't be 0\n");
+ return -EINVAL;
+ }
+ DP(NETIF_MSG_LINK,
+ "bnx2x_ets_E3B0_config toatl BW should be 100\n");
+ /**
+ * We can handle a case whre the BW isn't 100 this can happen
+ * if the TC are joined.
+ */
+ }
+ return 0;
+}
+
+/******************************************************************************
+* Description:
+* Invalidate all the sp_pri_to_cos.
+*.
+******************************************************************************/
+static void bnx2x_ets_e3b0_sp_pri_to_cos_init(u8 *sp_pri_to_cos)
+{
+ u8 pri = 0;
+ for (pri = 0; pri < DCBX_MAX_NUM_COS; pri++)
+ sp_pri_to_cos[pri] = DCBX_INVALID_COS;
+}
+/******************************************************************************
+* Description:
+* Calculate and set the SP (ARB_PRIORITY_CLIENT) NIG and PBF registers
+* according to sp_pri_to_cos.
+*.
+******************************************************************************/
+static int bnx2x_ets_e3b0_sp_pri_to_cos_set(const struct link_params *params,
+ u8 *sp_pri_to_cos, const u8 pri,
+ const u8 cos_entry)
+{
+ struct bnx2x *bp = params->bp;
+ const u8 port = params->port;
+ const u8 max_num_of_cos = (port) ? DCBX_E3B0_MAX_NUM_COS_PORT1 :
+ DCBX_E3B0_MAX_NUM_COS_PORT0;
+
+ if (DCBX_INVALID_COS != sp_pri_to_cos[pri]) {
+ DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_pri_to_cos_set invalid "
+ "parameter There can't be two COS's with "
+ "the same strict pri\n");
+ return -EINVAL;
+ }
+
+ if (pri > max_num_of_cos) {
+ DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_pri_to_cos_set invalid "
+ "parameter Illegal strict priority\n");
+ return -EINVAL;
+ }
+
+ sp_pri_to_cos[pri] = cos_entry;
+ return 0;
+
+}
+
+/******************************************************************************
+* Description:
+* Returns the correct value according to COS and priority in
+* the sp_pri_cli register.
+*.
+******************************************************************************/
+static u64 bnx2x_e3b0_sp_get_pri_cli_reg(const u8 cos, const u8 cos_offset,
+ const u8 pri_set,
+ const u8 pri_offset,
+ const u8 entry_size)
+{
+ u64 pri_cli_nig = 0;
+ pri_cli_nig = ((u64)(cos + cos_offset)) << (entry_size *
+ (pri_set + pri_offset));
+
+ return pri_cli_nig;
+}
+/******************************************************************************
+* Description:
+* Returns the correct value according to COS and priority in the
+* sp_pri_cli register for NIG.
+*.
+******************************************************************************/
+static u64 bnx2x_e3b0_sp_get_pri_cli_reg_nig(const u8 cos, const u8 pri_set)
+{
+ /* MCP Dbg0 and dbg1 are always with higher strict pri*/
+ const u8 nig_cos_offset = 3;
+ const u8 nig_pri_offset = 3;
+
+ return bnx2x_e3b0_sp_get_pri_cli_reg(cos, nig_cos_offset, pri_set,
+ nig_pri_offset, 4);
+}
+/******************************************************************************
+* Description:
+* Returns the correct value according to COS and priority in the
+* sp_pri_cli register for PBF.
+*.
+******************************************************************************/
+static u64 bnx2x_e3b0_sp_get_pri_cli_reg_pbf(const u8 cos, const u8 pri_set)
+{
+ const u8 pbf_cos_offset = 0;
+ const u8 pbf_pri_offset = 0;
+
+ return bnx2x_e3b0_sp_get_pri_cli_reg(cos, pbf_cos_offset, pri_set,
+ pbf_pri_offset, 3);
+
+}
+
+/******************************************************************************
+* Description:
+* Calculate and set the SP (ARB_PRIORITY_CLIENT) NIG and PBF registers
+* according to sp_pri_to_cos.(which COS has higher priority)
+*.
+******************************************************************************/
+static int bnx2x_ets_e3b0_sp_set_pri_cli_reg(const struct link_params *params,
+ u8 *sp_pri_to_cos)
+{
+ struct bnx2x *bp = params->bp;
+ u8 i = 0;
+ const u8 port = params->port;
+ /* MCP Dbg0 and dbg1 are always with higher strict pri*/
+ u64 pri_cli_nig = 0x210;
+ u32 pri_cli_pbf = 0x0;
+ u8 pri_set = 0;
+ u8 pri_bitmask = 0;
+ const u8 max_num_of_cos = (port) ? DCBX_E3B0_MAX_NUM_COS_PORT1 :
+ DCBX_E3B0_MAX_NUM_COS_PORT0;
+
+ u8 cos_bit_to_set = (1 << max_num_of_cos) - 1;
+
+ /* Set all the strict priority first */
+ for (i = 0; i < max_num_of_cos; i++) {
+ if (DCBX_INVALID_COS != sp_pri_to_cos[i]) {
+ if (DCBX_MAX_NUM_COS <= sp_pri_to_cos[i]) {
+ DP(NETIF_MSG_LINK,
+ "bnx2x_ets_e3b0_sp_set_pri_cli_reg "
+ "invalid cos entry\n");
+ return -EINVAL;
+ }
+
+ pri_cli_nig |= bnx2x_e3b0_sp_get_pri_cli_reg_nig(
+ sp_pri_to_cos[i], pri_set);
+
+ pri_cli_pbf |= bnx2x_e3b0_sp_get_pri_cli_reg_pbf(
+ sp_pri_to_cos[i], pri_set);
+ pri_bitmask = 1 << sp_pri_to_cos[i];
+ /* COS is used remove it from bitmap.*/
+ if (0 == (pri_bitmask & cos_bit_to_set)) {
+ DP(NETIF_MSG_LINK,
+ "bnx2x_ets_e3b0_sp_set_pri_cli_reg "
+ "invalid There can't be two COS's with"
+ " the same strict pri\n");
+ return -EINVAL;
+ }
+ cos_bit_to_set &= ~pri_bitmask;
+ pri_set++;
+ }
+ }
+
+ /* Set all the Non strict priority i= COS*/
+ for (i = 0; i < max_num_of_cos; i++) {
+ pri_bitmask = 1 << i;
+ /* Check if COS was already used for SP */
+ if (pri_bitmask & cos_bit_to_set) {
+ /* COS wasn't used for SP */
+ pri_cli_nig |= bnx2x_e3b0_sp_get_pri_cli_reg_nig(
+ i, pri_set);
+
+ pri_cli_pbf |= bnx2x_e3b0_sp_get_pri_cli_reg_pbf(
+ i, pri_set);
+ /* COS is used remove it from bitmap.*/
+ cos_bit_to_set &= ~pri_bitmask;
+ pri_set++;
+ }
+ }
+
+ if (pri_set != max_num_of_cos) {
+ DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_set_pri_cli_reg not all "
+ "entries were set\n");
+ return -EINVAL;
+ }
+
+ if (port) {
+ /* Only 6 usable clients*/
+ REG_WR(bp, NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_LSB,
+ (u32)pri_cli_nig);
+
+ REG_WR(bp, PBF_REG_ETS_ARB_PRIORITY_CLIENT_P1 , pri_cli_pbf);
+ } else {
+ /* Only 9 usable clients*/
+ const u32 pri_cli_nig_lsb = (u32) (pri_cli_nig);
+ const u32 pri_cli_nig_msb = (u32) ((pri_cli_nig >> 32) & 0xF);
+
+ REG_WR(bp, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_LSB,
+ pri_cli_nig_lsb);
+ REG_WR(bp, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_MSB,
+ pri_cli_nig_msb);
+
+ REG_WR(bp, PBF_REG_ETS_ARB_PRIORITY_CLIENT_P0 , pri_cli_pbf);
+ }
+ return 0;
+}
+
+/******************************************************************************
+* Description:
+* Configure the COS to ETS according to BW and SP settings.
+******************************************************************************/
+int bnx2x_ets_e3b0_config(const struct link_params *params,
+ const struct link_vars *vars,
+ const struct bnx2x_ets_params *ets_params)
+{
+ struct bnx2x *bp = params->bp;
+ int bnx2x_status = 0;
+ const u8 port = params->port;
+ u16 total_bw = 0;
+ const u32 min_w_val_nig = bnx2x_ets_get_min_w_val_nig(vars);
+ const u32 min_w_val_pbf = ETS_E3B0_PBF_MIN_W_VAL;
+ u8 cos_bw_bitmap = 0;
+ u8 cos_sp_bitmap = 0;
+ u8 sp_pri_to_cos[DCBX_MAX_NUM_COS] = {0};
+ const u8 max_num_of_cos = (port) ? DCBX_E3B0_MAX_NUM_COS_PORT1 :
+ DCBX_E3B0_MAX_NUM_COS_PORT0;
+ u8 cos_entry = 0;
+
+ if (!CHIP_IS_E3B0(bp)) {
+ DP(NETIF_MSG_LINK,
+ "bnx2x_ets_e3b0_disabled the chip isn't E3B0\n");
+ return -EINVAL;
+ }
+
+ if ((ets_params->num_of_cos > max_num_of_cos)) {
+ DP(NETIF_MSG_LINK, "bnx2x_ets_E3B0_config the number of COS "
+ "isn't supported\n");
+ return -EINVAL;
+ }
+
+ /* Prepare sp strict priority parameters*/
+ bnx2x_ets_e3b0_sp_pri_to_cos_init(sp_pri_to_cos);
+
+ /* Prepare BW parameters*/
+ bnx2x_status = bnx2x_ets_e3b0_get_total_bw(params, ets_params,
+ &total_bw);
+ if (0 != bnx2x_status) {
+ DP(NETIF_MSG_LINK,
+ "bnx2x_ets_E3B0_config get_total_bw failed\n");
+ return -EINVAL;
+ }
+
+ /**
+ * Upper bound is set according to current link speed (min_w_val
+ * should be the same for upper bound and COS credit val).
+ */
+ bnx2x_ets_e3b0_set_credit_upper_bound_nig(params, min_w_val_nig);
+ bnx2x_ets_e3b0_set_credit_upper_bound_pbf(params, min_w_val_pbf);
+
+
+ for (cos_entry = 0; cos_entry < ets_params->num_of_cos; cos_entry++) {
+ if (bnx2x_cos_state_bw == ets_params->cos[cos_entry].state) {
+ cos_bw_bitmap |= (1 << cos_entry);
+ /**
+ * The function also sets the BW in HW(not the mappin
+ * yet)
+ */
+ bnx2x_status = bnx2x_ets_e3b0_set_cos_bw(
+ bp, cos_entry, min_w_val_nig, min_w_val_pbf,
+ total_bw,
+ ets_params->cos[cos_entry].params.bw_params.bw,
+ port);
+ } else if (bnx2x_cos_state_strict ==
+ ets_params->cos[cos_entry].state){
+ cos_sp_bitmap |= (1 << cos_entry);
+
+ bnx2x_status = bnx2x_ets_e3b0_sp_pri_to_cos_set(
+ params,
+ sp_pri_to_cos,
+ ets_params->cos[cos_entry].params.sp_params.pri,
+ cos_entry);
+
+ } else {
+ DP(NETIF_MSG_LINK,
+ "bnx2x_ets_e3b0_config cos state not valid\n");
+ return -EINVAL;
+ }
+ if (0 != bnx2x_status) {
+ DP(NETIF_MSG_LINK,
+ "bnx2x_ets_e3b0_config set cos bw failed\n");
+ return bnx2x_status;
+ }
+ }
+
+ /* Set SP register (which COS has higher priority) */
+ bnx2x_status = bnx2x_ets_e3b0_sp_set_pri_cli_reg(params,
+ sp_pri_to_cos);
+
+ if (0 != bnx2x_status) {
+ DP(NETIF_MSG_LINK,
+ "bnx2x_ets_E3B0_config set_pri_cli_reg failed\n");
+ return bnx2x_status;
+ }
+
+ /* Set client mapping of BW and strict */
+ bnx2x_status = bnx2x_ets_e3b0_cli_map(params, ets_params,
+ cos_sp_bitmap,
+ cos_bw_bitmap);
+
+ if (0 != bnx2x_status) {
+ DP(NETIF_MSG_LINK, "bnx2x_ets_E3B0_config SP failed\n");
+ return bnx2x_status;
+ }
+ return 0;
+}
static void bnx2x_ets_bw_limit_common(const struct link_params *params)
{
/* ETS disabled configuration */
@@ -342,7 +1253,7 @@ void bnx2x_ets_bw_limit(const struct link_params *params, const u32 cos0_bw,
REG_WR(bp, PBF_REG_COS1_WEIGHT, cos1_credit_weight);
}
-u8 bnx2x_ets_strict(const struct link_params *params, const u8 strict_cos)
+int bnx2x_ets_strict(const struct link_params *params, const u8 strict_cos)
{
/* ETS disabled configuration*/
struct bnx2x *bp = params->bp;
@@ -388,24 +1299,64 @@ u8 bnx2x_ets_strict(const struct link_params *params, const u8 strict_cos)
/* PFC section */
/******************************************************************/
-static void bnx2x_bmac2_get_pfc_stat(struct link_params *params,
- u32 pfc_frames_sent[2],
- u32 pfc_frames_received[2])
+static void bnx2x_update_pfc_xmac(struct link_params *params,
+ struct link_vars *vars,
+ u8 is_lb)
{
- /* Read pfc statistic */
struct bnx2x *bp = params->bp;
- u32 bmac_addr = params->port ? NIG_REG_INGRESS_BMAC1_MEM :
- NIG_REG_INGRESS_BMAC0_MEM;
+ u32 xmac_base;
+ u32 pause_val, pfc0_val, pfc1_val;
+
+ /* XMAC base adrr */
+ xmac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
- DP(NETIF_MSG_LINK, "pfc statistic read from BMAC\n");
+ /* Initialize pause and pfc registers */
+ pause_val = 0x18000;
+ pfc0_val = 0xFFFF8000;
+ pfc1_val = 0x2;
- REG_RD_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_TX_STAT_GTPP,
- pfc_frames_sent, 2);
+ /* No PFC support */
+ if (!(params->feature_config_flags &
+ FEATURE_CONFIG_PFC_ENABLED)) {
+
+ /*
+ * RX flow control - Process pause frame in receive direction
+ */
+ if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX)
+ pause_val |= XMAC_PAUSE_CTRL_REG_RX_PAUSE_EN;
- REG_RD_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_RX_STAT_GRPP,
- pfc_frames_received, 2);
+ /*
+ * TX flow control - Send pause packet when buffer is full
+ */
+ if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)
+ pause_val |= XMAC_PAUSE_CTRL_REG_TX_PAUSE_EN;
+ } else {/* PFC support */
+ pfc1_val |= XMAC_PFC_CTRL_HI_REG_PFC_REFRESH_EN |
+ XMAC_PFC_CTRL_HI_REG_PFC_STATS_EN |
+ XMAC_PFC_CTRL_HI_REG_RX_PFC_EN |
+ XMAC_PFC_CTRL_HI_REG_TX_PFC_EN;
+ }
+
+ /* Write pause and PFC registers */
+ REG_WR(bp, xmac_base + XMAC_REG_PAUSE_CTRL, pause_val);
+ REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL, pfc0_val);
+ REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI, pfc1_val);
+
+
+ /* Set MAC address for source TX Pause/PFC frames */
+ REG_WR(bp, xmac_base + XMAC_REG_CTRL_SA_LO,
+ ((params->mac_addr[2] << 24) |
+ (params->mac_addr[3] << 16) |
+ (params->mac_addr[4] << 8) |
+ (params->mac_addr[5])));
+ REG_WR(bp, xmac_base + XMAC_REG_CTRL_SA_HI,
+ ((params->mac_addr[0] << 8) |
+ (params->mac_addr[1])));
+ udelay(30);
}
+
+
static void bnx2x_emac_get_pfc_stat(struct link_params *params,
u32 pfc_frames_sent[2],
u32 pfc_frames_received[2])
@@ -437,33 +1388,54 @@ static void bnx2x_emac_get_pfc_stat(struct link_params *params,
pfc_frames_sent[0] = val_xon + val_xoff;
}
+/* Read pfc statistic*/
void bnx2x_pfc_statistic(struct link_params *params, struct link_vars *vars,
u32 pfc_frames_sent[2],
u32 pfc_frames_received[2])
{
/* Read pfc statistic */
struct bnx2x *bp = params->bp;
- u32 val = 0;
+
DP(NETIF_MSG_LINK, "pfc statistic\n");
if (!vars->link_up)
return;
- val = REG_RD(bp, MISC_REG_RESET_REG_2);
- if ((val & (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port))
- == 0) {
- DP(NETIF_MSG_LINK, "About to read stats from EMAC\n");
+ if (MAC_TYPE_EMAC == vars->mac_type) {
+ DP(NETIF_MSG_LINK, "About to read PFC stats from EMAC\n");
bnx2x_emac_get_pfc_stat(params, pfc_frames_sent,
pfc_frames_received);
- } else {
- DP(NETIF_MSG_LINK, "About to read stats from BMAC\n");
- bnx2x_bmac2_get_pfc_stat(params, pfc_frames_sent,
- pfc_frames_received);
}
}
/******************************************************************/
/* MAC/PBF section */
/******************************************************************/
+static void bnx2x_set_mdio_clk(struct bnx2x *bp, u32 chip_id, u8 port)
+{
+ u32 mode, emac_base;
+ /**
+ * Set clause 45 mode, slow down the MDIO clock to 2.5MHz
+ * (a value of 49==0x31) and make sure that the AUTO poll is off
+ */
+
+ if (CHIP_IS_E2(bp))
+ emac_base = GRCBASE_EMAC0;
+ else
+ emac_base = (port) ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
+ mode = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE);
+ mode &= ~(EMAC_MDIO_MODE_AUTO_POLL |
+ EMAC_MDIO_MODE_CLOCK_CNT);
+ if (USES_WARPCORE(bp))
+ mode |= (74L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT);
+ else
+ mode |= (49L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT);
+
+ mode |= (EMAC_MDIO_MODE_CLAUSE_45);
+ REG_WR(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE, mode);
+
+ udelay(40);
+}
+
static void bnx2x_emac_init(struct link_params *params,
struct link_vars *vars)
{
@@ -495,7 +1467,7 @@ static void bnx2x_emac_init(struct link_params *params,
}
timeout--;
} while (val & EMAC_MODE_RESET);
-
+ bnx2x_set_mdio_clk(bp, params->chip_id, port);
/* Set mac address */
val = ((params->mac_addr[0] << 8) |
params->mac_addr[1]);
@@ -508,9 +1480,272 @@ static void bnx2x_emac_init(struct link_params *params,
EMAC_WR(bp, EMAC_REG_EMAC_MAC_MATCH + 4, val);
}
-static u8 bnx2x_emac_enable(struct link_params *params,
+static void bnx2x_set_xumac_nig(struct link_params *params,
+ u16 tx_pause_en,
+ u8 enable)
+{
+ struct bnx2x *bp = params->bp;
+
+ REG_WR(bp, params->port ? NIG_REG_P1_MAC_IN_EN : NIG_REG_P0_MAC_IN_EN,
+ enable);
+ REG_WR(bp, params->port ? NIG_REG_P1_MAC_OUT_EN : NIG_REG_P0_MAC_OUT_EN,
+ enable);
+ REG_WR(bp, params->port ? NIG_REG_P1_MAC_PAUSE_OUT_EN :
+ NIG_REG_P0_MAC_PAUSE_OUT_EN, tx_pause_en);
+}
+
+static void bnx2x_umac_disable(struct link_params *params)
+{
+ u32 umac_base = params->port ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
+ struct bnx2x *bp = params->bp;
+ if (!(REG_RD(bp, MISC_REG_RESET_REG_2) &
+ (MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port)))
+ return;
+
+ /* Disable RX and TX */
+ REG_WR(bp, umac_base + UMAC_REG_COMMAND_CONFIG, 0);
+}
+
+static void bnx2x_umac_enable(struct link_params *params,
struct link_vars *vars, u8 lb)
{
+ u32 val;
+ u32 umac_base = params->port ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
+ struct bnx2x *bp = params->bp;
+ /* Reset UMAC */
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
+ (MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port));
+ usleep_range(1000, 1000);
+
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET,
+ (MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port));
+
+ DP(NETIF_MSG_LINK, "enabling UMAC\n");
+
+ /**
+ * This register determines on which events the MAC will assert
+ * error on the i/f to the NIG along w/ EOP.
+ */
+
+ /**
+ * BD REG_WR(bp, NIG_REG_P0_MAC_RSV_ERR_MASK +
+ * params->port*0x14, 0xfffff.
+ */
+ /* This register opens the gate for the UMAC despite its name */
+ REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + params->port*4, 1);
+
+ val = UMAC_COMMAND_CONFIG_REG_PROMIS_EN |
+ UMAC_COMMAND_CONFIG_REG_PAD_EN |
+ UMAC_COMMAND_CONFIG_REG_SW_RESET |
+ UMAC_COMMAND_CONFIG_REG_NO_LGTH_CHECK;
+ switch (vars->line_speed) {
+ case SPEED_10:
+ val |= (0<<2);
+ break;
+ case SPEED_100:
+ val |= (1<<2);
+ break;
+ case SPEED_1000:
+ val |= (2<<2);
+ break;
+ case SPEED_2500:
+ val |= (3<<2);
+ break;
+ default:
+ DP(NETIF_MSG_LINK, "Invalid speed for UMAC %d\n",
+ vars->line_speed);
+ break;
+ }
+ if (!(vars->flow_ctrl & BNX2X_FLOW_CTRL_TX))
+ val |= UMAC_COMMAND_CONFIG_REG_IGNORE_TX_PAUSE;
+
+ if (!(vars->flow_ctrl & BNX2X_FLOW_CTRL_RX))
+ val |= UMAC_COMMAND_CONFIG_REG_PAUSE_IGNORE;
+
+ REG_WR(bp, umac_base + UMAC_REG_COMMAND_CONFIG, val);
+ udelay(50);
+
+ /* Set MAC address for source TX Pause/PFC frames (under SW reset) */
+ REG_WR(bp, umac_base + UMAC_REG_MAC_ADDR0,
+ ((params->mac_addr[2] << 24) |
+ (params->mac_addr[3] << 16) |
+ (params->mac_addr[4] << 8) |
+ (params->mac_addr[5])));
+ REG_WR(bp, umac_base + UMAC_REG_MAC_ADDR1,
+ ((params->mac_addr[0] << 8) |
+ (params->mac_addr[1])));
+
+ /* Enable RX and TX */
+ val &= ~UMAC_COMMAND_CONFIG_REG_PAD_EN;
+ val |= UMAC_COMMAND_CONFIG_REG_TX_ENA |
+ UMAC_COMMAND_CONFIG_REG_RX_ENA;
+ REG_WR(bp, umac_base + UMAC_REG_COMMAND_CONFIG, val);
+ udelay(50);
+
+ /* Remove SW Reset */
+ val &= ~UMAC_COMMAND_CONFIG_REG_SW_RESET;
+
+ /* Check loopback mode */
+ if (lb)
+ val |= UMAC_COMMAND_CONFIG_REG_LOOP_ENA;
+ REG_WR(bp, umac_base + UMAC_REG_COMMAND_CONFIG, val);
+
+ /*
+ * Maximum Frame Length (RW). Defines a 14-Bit maximum frame
+ * length used by the MAC receive logic to check frames.
+ */
+ REG_WR(bp, umac_base + UMAC_REG_MAXFR, 0x2710);
+ bnx2x_set_xumac_nig(params,
+ ((vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) != 0), 1);
+ vars->mac_type = MAC_TYPE_UMAC;
+
+}
+
+static u8 bnx2x_is_4_port_mode(struct bnx2x *bp)
+{
+ u32 port4mode_ovwr_val;
+ /* Check 4-port override enabled */
+ port4mode_ovwr_val = REG_RD(bp, MISC_REG_PORT4MODE_EN_OVWR);
+ if (port4mode_ovwr_val & (1<<0)) {
+ /* Return 4-port mode override value */
+ return ((port4mode_ovwr_val & (1<<1)) == (1<<1));
+ }
+ /* Return 4-port mode from input pin */
+ return (u8)REG_RD(bp, MISC_REG_PORT4MODE_EN);
+}
+
+/* Define the XMAC mode */
+static void bnx2x_xmac_init(struct link_params *params, u32 max_speed)
+{
+ struct bnx2x *bp = params->bp;
+ u32 is_port4mode = bnx2x_is_4_port_mode(bp);
+
+ /**
+ * In 4-port mode, need to set the mode only once, so if XMAC is
+ * already out of reset, it means the mode has already been set,
+ * and it must not* reset the XMAC again, since it controls both
+ * ports of the path
+ **/
+
+ if ((CHIP_NUM(bp) == CHIP_NUM_57840) &&
+ (REG_RD(bp, MISC_REG_RESET_REG_2) &
+ MISC_REGISTERS_RESET_REG_2_XMAC)) {
+ DP(NETIF_MSG_LINK,
+ "XMAC already out of reset in 4-port mode\n");
+ return;
+ }
+
+ /* Hard reset */
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
+ MISC_REGISTERS_RESET_REG_2_XMAC);
+ usleep_range(1000, 1000);
+
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET,
+ MISC_REGISTERS_RESET_REG_2_XMAC);
+ if (is_port4mode) {
+ DP(NETIF_MSG_LINK, "Init XMAC to 2 ports x 10G per path\n");
+
+ /* Set the number of ports on the system side to up to 2 */
+ REG_WR(bp, MISC_REG_XMAC_CORE_PORT_MODE, 1);
+
+ /* Set the number of ports on the Warp Core to 10G */
+ REG_WR(bp, MISC_REG_XMAC_PHY_PORT_MODE, 3);
+ } else {
+ /* Set the number of ports on the system side to 1 */
+ REG_WR(bp, MISC_REG_XMAC_CORE_PORT_MODE, 0);
+ if (max_speed == SPEED_10000) {
+ DP(NETIF_MSG_LINK,
+ "Init XMAC to 10G x 1 port per path\n");
+ /* Set the number of ports on the Warp Core to 10G */
+ REG_WR(bp, MISC_REG_XMAC_PHY_PORT_MODE, 3);
+ } else {
+ DP(NETIF_MSG_LINK,
+ "Init XMAC to 20G x 2 ports per path\n");
+ /* Set the number of ports on the Warp Core to 20G */
+ REG_WR(bp, MISC_REG_XMAC_PHY_PORT_MODE, 1);
+ }
+ }
+ /* Soft reset */
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
+ MISC_REGISTERS_RESET_REG_2_XMAC_SOFT);
+ usleep_range(1000, 1000);
+
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET,
+ MISC_REGISTERS_RESET_REG_2_XMAC_SOFT);
+
+}
+
+static void bnx2x_xmac_disable(struct link_params *params)
+{
+ u8 port = params->port;
+ struct bnx2x *bp = params->bp;
+ u32 pfc_ctrl, xmac_base = (port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
+
+ if (REG_RD(bp, MISC_REG_RESET_REG_2) &
+ MISC_REGISTERS_RESET_REG_2_XMAC) {
+ /*
+ * Send an indication to change the state in the NIG back to XON
+ * Clearing this bit enables the next set of this bit to get
+ * rising edge
+ */
+ pfc_ctrl = REG_RD(bp, xmac_base + XMAC_REG_PFC_CTRL_HI);
+ REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI,
+ (pfc_ctrl & ~(1<<1)));
+ REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI,
+ (pfc_ctrl | (1<<1)));
+ DP(NETIF_MSG_LINK, "Disable XMAC on port %x\n", port);
+ REG_WR(bp, xmac_base + XMAC_REG_CTRL, 0);
+ }
+}
+
+static int bnx2x_xmac_enable(struct link_params *params,
+ struct link_vars *vars, u8 lb)
+{
+ u32 val, xmac_base;
+ struct bnx2x *bp = params->bp;
+ DP(NETIF_MSG_LINK, "enabling XMAC\n");
+
+ xmac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
+
+ bnx2x_xmac_init(params, vars->line_speed);
+
+ /*
+ * This register determines on which events the MAC will assert
+ * error on the i/f to the NIG along w/ EOP.
+ */
+
+ /*
+ * This register tells the NIG whether to send traffic to UMAC
+ * or XMAC
+ */
+ REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + params->port*4, 0);
+
+ /* Set Max packet size */
+ REG_WR(bp, xmac_base + XMAC_REG_RX_MAX_SIZE, 0x2710);
+
+ /* CRC append for Tx packets */
+ REG_WR(bp, xmac_base + XMAC_REG_TX_CTRL, 0xC800);
+
+ /* update PFC */
+ bnx2x_update_pfc_xmac(params, vars, 0);
+
+ /* Enable TX and RX */
+ val = XMAC_CTRL_REG_TX_EN | XMAC_CTRL_REG_RX_EN;
+
+ /* Check loopback mode */
+ if (lb)
+ val |= XMAC_CTRL_REG_LINE_LOCAL_LPBK;
+ REG_WR(bp, xmac_base + XMAC_REG_CTRL, val);
+ bnx2x_set_xumac_nig(params,
+ ((vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) != 0), 1);
+
+ vars->mac_type = MAC_TYPE_XMAC;
+
+ return 0;
+}
+static int bnx2x_emac_enable(struct link_params *params,
+ struct link_vars *vars, u8 lb)
+{
struct bnx2x *bp = params->bp;
u8 port = params->port;
u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
@@ -518,6 +1753,10 @@ static u8 bnx2x_emac_enable(struct link_params *params,
DP(NETIF_MSG_LINK, "enabling EMAC\n");
+ /* Disable BMAC */
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
+ (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
+
/* enable emac and not bmac */
REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + port*4, 1);
@@ -760,95 +1999,398 @@ static void bnx2x_update_pfc_bmac2(struct link_params *params,
REG_WR_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_BMAC_CONTROL, wb_data, 2);
}
-static void bnx2x_update_pfc_brb(struct link_params *params,
- struct link_vars *vars,
- struct bnx2x_nig_brb_pfc_port_params *pfc_params)
+
+/* PFC BRB internal port configuration params */
+struct bnx2x_pfc_brb_threshold_val {
+ u32 pause_xoff;
+ u32 pause_xon;
+ u32 full_xoff;
+ u32 full_xon;
+};
+
+struct bnx2x_pfc_brb_e3b0_val {
+ u32 full_lb_xoff_th;
+ u32 full_lb_xon_threshold;
+ u32 lb_guarantied;
+ u32 mac_0_class_t_guarantied;
+ u32 mac_0_class_t_guarantied_hyst;
+ u32 mac_1_class_t_guarantied;
+ u32 mac_1_class_t_guarantied_hyst;
+};
+
+struct bnx2x_pfc_brb_th_val {
+ struct bnx2x_pfc_brb_threshold_val pauseable_th;
+ struct bnx2x_pfc_brb_threshold_val non_pauseable_th;
+};
+static int bnx2x_pfc_brb_get_config_params(
+ struct link_params *params,
+ struct bnx2x_pfc_brb_th_val *config_val)
+{
+ struct bnx2x *bp = params->bp;
+ DP(NETIF_MSG_LINK, "Setting PFC BRB configuration\n");
+ if (CHIP_IS_E2(bp)) {
+ config_val->pauseable_th.pause_xoff =
+ PFC_E2_BRB_MAC_PAUSE_XOFF_THR_PAUSE;
+ config_val->pauseable_th.pause_xon =
+ PFC_E2_BRB_MAC_PAUSE_XON_THR_PAUSE;
+ config_val->pauseable_th.full_xoff =
+ PFC_E2_BRB_MAC_FULL_XOFF_THR_PAUSE;
+ config_val->pauseable_th.full_xon =
+ PFC_E2_BRB_MAC_FULL_XON_THR_PAUSE;
+ /* non pause able*/
+ config_val->non_pauseable_th.pause_xoff =
+ PFC_E2_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE;
+ config_val->non_pauseable_th.pause_xon =
+ PFC_E2_BRB_MAC_PAUSE_XON_THR_NON_PAUSE;
+ config_val->non_pauseable_th.full_xoff =
+ PFC_E2_BRB_MAC_FULL_XOFF_THR_NON_PAUSE;
+ config_val->non_pauseable_th.full_xon =
+ PFC_E2_BRB_MAC_FULL_XON_THR_NON_PAUSE;
+ } else if (CHIP_IS_E3A0(bp)) {
+ config_val->pauseable_th.pause_xoff =
+ PFC_E3A0_BRB_MAC_PAUSE_XOFF_THR_PAUSE;
+ config_val->pauseable_th.pause_xon =
+ PFC_E3A0_BRB_MAC_PAUSE_XON_THR_PAUSE;
+ config_val->pauseable_th.full_xoff =
+ PFC_E3A0_BRB_MAC_FULL_XOFF_THR_PAUSE;
+ config_val->pauseable_th.full_xon =
+ PFC_E3A0_BRB_MAC_FULL_XON_THR_PAUSE;
+ /* non pause able*/
+ config_val->non_pauseable_th.pause_xoff =
+ PFC_E3A0_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE;
+ config_val->non_pauseable_th.pause_xon =
+ PFC_E3A0_BRB_MAC_PAUSE_XON_THR_NON_PAUSE;
+ config_val->non_pauseable_th.full_xoff =
+ PFC_E3A0_BRB_MAC_FULL_XOFF_THR_NON_PAUSE;
+ config_val->non_pauseable_th.full_xon =
+ PFC_E3A0_BRB_MAC_FULL_XON_THR_NON_PAUSE;
+ } else if (CHIP_IS_E3B0(bp)) {
+ if (params->phy[INT_PHY].flags &
+ FLAGS_4_PORT_MODE) {
+ config_val->pauseable_th.pause_xoff =
+ PFC_E3B0_4P_BRB_MAC_PAUSE_XOFF_THR_PAUSE;
+ config_val->pauseable_th.pause_xon =
+ PFC_E3B0_4P_BRB_MAC_PAUSE_XON_THR_PAUSE;
+ config_val->pauseable_th.full_xoff =
+ PFC_E3B0_4P_BRB_MAC_FULL_XOFF_THR_PAUSE;
+ config_val->pauseable_th.full_xon =
+ PFC_E3B0_4P_BRB_MAC_FULL_XON_THR_PAUSE;
+ /* non pause able*/
+ config_val->non_pauseable_th.pause_xoff =
+ PFC_E3B0_4P_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE;
+ config_val->non_pauseable_th.pause_xon =
+ PFC_E3B0_4P_BRB_MAC_PAUSE_XON_THR_NON_PAUSE;
+ config_val->non_pauseable_th.full_xoff =
+ PFC_E3B0_4P_BRB_MAC_FULL_XOFF_THR_NON_PAUSE;
+ config_val->non_pauseable_th.full_xon =
+ PFC_E3B0_4P_BRB_MAC_FULL_XON_THR_NON_PAUSE;
+ } else {
+ config_val->pauseable_th.pause_xoff =
+ PFC_E3B0_2P_BRB_MAC_PAUSE_XOFF_THR_PAUSE;
+ config_val->pauseable_th.pause_xon =
+ PFC_E3B0_2P_BRB_MAC_PAUSE_XON_THR_PAUSE;
+ config_val->pauseable_th.full_xoff =
+ PFC_E3B0_2P_BRB_MAC_FULL_XOFF_THR_PAUSE;
+ config_val->pauseable_th.full_xon =
+ PFC_E3B0_2P_BRB_MAC_FULL_XON_THR_PAUSE;
+ /* non pause able*/
+ config_val->non_pauseable_th.pause_xoff =
+ PFC_E3B0_2P_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE;
+ config_val->non_pauseable_th.pause_xon =
+ PFC_E3B0_2P_BRB_MAC_PAUSE_XON_THR_NON_PAUSE;
+ config_val->non_pauseable_th.full_xoff =
+ PFC_E3B0_2P_BRB_MAC_FULL_XOFF_THR_NON_PAUSE;
+ config_val->non_pauseable_th.full_xon =
+ PFC_E3B0_2P_BRB_MAC_FULL_XON_THR_NON_PAUSE;
+ }
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static void bnx2x_pfc_brb_get_e3b0_config_params(struct link_params *params,
+ struct bnx2x_pfc_brb_e3b0_val
+ *e3b0_val,
+ u32 cos0_pauseable,
+ u32 cos1_pauseable)
+{
+ if (params->phy[INT_PHY].flags & FLAGS_4_PORT_MODE) {
+ e3b0_val->full_lb_xoff_th =
+ PFC_E3B0_4P_BRB_FULL_LB_XOFF_THR;
+ e3b0_val->full_lb_xon_threshold =
+ PFC_E3B0_4P_BRB_FULL_LB_XON_THR;
+ e3b0_val->lb_guarantied =
+ PFC_E3B0_4P_LB_GUART;
+ e3b0_val->mac_0_class_t_guarantied =
+ PFC_E3B0_4P_BRB_MAC_0_CLASS_T_GUART;
+ e3b0_val->mac_0_class_t_guarantied_hyst =
+ PFC_E3B0_4P_BRB_MAC_0_CLASS_T_GUART_HYST;
+ e3b0_val->mac_1_class_t_guarantied =
+ PFC_E3B0_4P_BRB_MAC_1_CLASS_T_GUART;
+ e3b0_val->mac_1_class_t_guarantied_hyst =
+ PFC_E3B0_4P_BRB_MAC_1_CLASS_T_GUART_HYST;
+ } else {
+ e3b0_val->full_lb_xoff_th =
+ PFC_E3B0_2P_BRB_FULL_LB_XOFF_THR;
+ e3b0_val->full_lb_xon_threshold =
+ PFC_E3B0_2P_BRB_FULL_LB_XON_THR;
+ e3b0_val->mac_0_class_t_guarantied_hyst =
+ PFC_E3B0_2P_BRB_MAC_0_CLASS_T_GUART_HYST;
+ e3b0_val->mac_1_class_t_guarantied =
+ PFC_E3B0_2P_BRB_MAC_1_CLASS_T_GUART;
+ e3b0_val->mac_1_class_t_guarantied_hyst =
+ PFC_E3B0_2P_BRB_MAC_1_CLASS_T_GUART_HYST;
+
+ if (cos0_pauseable != cos1_pauseable) {
+ /* nonpauseable= Lossy + pauseable = Lossless*/
+ e3b0_val->lb_guarantied =
+ PFC_E3B0_2P_MIX_PAUSE_LB_GUART;
+ e3b0_val->mac_0_class_t_guarantied =
+ PFC_E3B0_2P_MIX_PAUSE_MAC_0_CLASS_T_GUART;
+ } else if (cos0_pauseable) {
+ /* Lossless +Lossless*/
+ e3b0_val->lb_guarantied =
+ PFC_E3B0_2P_PAUSE_LB_GUART;
+ e3b0_val->mac_0_class_t_guarantied =
+ PFC_E3B0_2P_PAUSE_MAC_0_CLASS_T_GUART;
+ } else {
+ /* Lossy +Lossy*/
+ e3b0_val->lb_guarantied =
+ PFC_E3B0_2P_NON_PAUSE_LB_GUART;
+ e3b0_val->mac_0_class_t_guarantied =
+ PFC_E3B0_2P_NON_PAUSE_MAC_0_CLASS_T_GUART;
+ }
+ }
+}
+static int bnx2x_update_pfc_brb(struct link_params *params,
+ struct link_vars *vars,
+ struct bnx2x_nig_brb_pfc_port_params
+ *pfc_params)
{
struct bnx2x *bp = params->bp;
+ struct bnx2x_pfc_brb_th_val config_val = { {0} };
+ struct bnx2x_pfc_brb_threshold_val *reg_th_config =
+ &config_val.pauseable_th;
+ struct bnx2x_pfc_brb_e3b0_val e3b0_val = {0};
int set_pfc = params->feature_config_flags &
FEATURE_CONFIG_PFC_ENABLED;
+ int bnx2x_status = 0;
+ u8 port = params->port;
/* default - pause configuration */
- u32 pause_xoff_th = PFC_BRB_MAC_PAUSE_XOFF_THRESHOLD_PAUSEABLE;
- u32 pause_xon_th = PFC_BRB_MAC_PAUSE_XON_THRESHOLD_PAUSEABLE;
- u32 full_xoff_th = PFC_BRB_MAC_FULL_XOFF_THRESHOLD_PAUSEABLE;
- u32 full_xon_th = PFC_BRB_MAC_FULL_XON_THRESHOLD_PAUSEABLE;
+ reg_th_config = &config_val.pauseable_th;
+ bnx2x_status = bnx2x_pfc_brb_get_config_params(params, &config_val);
+ if (0 != bnx2x_status)
+ return bnx2x_status;
if (set_pfc && pfc_params)
/* First COS */
- if (!pfc_params->cos0_pauseable) {
- pause_xoff_th =
- PFC_BRB_MAC_PAUSE_XOFF_THRESHOLD_NON_PAUSEABLE;
- pause_xon_th =
- PFC_BRB_MAC_PAUSE_XON_THRESHOLD_NON_PAUSEABLE;
- full_xoff_th =
- PFC_BRB_MAC_FULL_XOFF_THRESHOLD_NON_PAUSEABLE;
- full_xon_th =
- PFC_BRB_MAC_FULL_XON_THRESHOLD_NON_PAUSEABLE;
- }
+ if (!pfc_params->cos0_pauseable)
+ reg_th_config = &config_val.non_pauseable_th;
/*
* The number of free blocks below which the pause signal to class 0
* of MAC #n is asserted. n=0,1
*/
- REG_WR(bp, BRB1_REG_PAUSE_0_XOFF_THRESHOLD_0 , pause_xoff_th);
+ REG_WR(bp, (port) ? BRB1_REG_PAUSE_0_XOFF_THRESHOLD_1 :
+ BRB1_REG_PAUSE_0_XOFF_THRESHOLD_0 ,
+ reg_th_config->pause_xoff);
/*
* The number of free blocks above which the pause signal to class 0
* of MAC #n is de-asserted. n=0,1
*/
- REG_WR(bp, BRB1_REG_PAUSE_0_XON_THRESHOLD_0 , pause_xon_th);
+ REG_WR(bp, (port) ? BRB1_REG_PAUSE_0_XON_THRESHOLD_1 :
+ BRB1_REG_PAUSE_0_XON_THRESHOLD_0 , reg_th_config->pause_xon);
/*
* The number of free blocks below which the full signal to class 0
* of MAC #n is asserted. n=0,1
*/
- REG_WR(bp, BRB1_REG_FULL_0_XOFF_THRESHOLD_0 , full_xoff_th);
+ REG_WR(bp, (port) ? BRB1_REG_FULL_0_XOFF_THRESHOLD_1 :
+ BRB1_REG_FULL_0_XOFF_THRESHOLD_0 , reg_th_config->full_xoff);
/*
* The number of free blocks above which the full signal to class 0
* of MAC #n is de-asserted. n=0,1
*/
- REG_WR(bp, BRB1_REG_FULL_0_XON_THRESHOLD_0 , full_xon_th);
+ REG_WR(bp, (port) ? BRB1_REG_FULL_0_XON_THRESHOLD_1 :
+ BRB1_REG_FULL_0_XON_THRESHOLD_0 , reg_th_config->full_xon);
if (set_pfc && pfc_params) {
/* Second COS */
- if (pfc_params->cos1_pauseable) {
- pause_xoff_th =
- PFC_BRB_MAC_PAUSE_XOFF_THRESHOLD_PAUSEABLE;
- pause_xon_th =
- PFC_BRB_MAC_PAUSE_XON_THRESHOLD_PAUSEABLE;
- full_xoff_th =
- PFC_BRB_MAC_FULL_XOFF_THRESHOLD_PAUSEABLE;
- full_xon_th =
- PFC_BRB_MAC_FULL_XON_THRESHOLD_PAUSEABLE;
- } else {
- pause_xoff_th =
- PFC_BRB_MAC_PAUSE_XOFF_THRESHOLD_NON_PAUSEABLE;
- pause_xon_th =
- PFC_BRB_MAC_PAUSE_XON_THRESHOLD_NON_PAUSEABLE;
- full_xoff_th =
- PFC_BRB_MAC_FULL_XOFF_THRESHOLD_NON_PAUSEABLE;
- full_xon_th =
- PFC_BRB_MAC_FULL_XON_THRESHOLD_NON_PAUSEABLE;
- }
+ if (pfc_params->cos1_pauseable)
+ reg_th_config = &config_val.pauseable_th;
+ else
+ reg_th_config = &config_val.non_pauseable_th;
/*
* The number of free blocks below which the pause signal to
* class 1 of MAC #n is asserted. n=0,1
- */
- REG_WR(bp, BRB1_REG_PAUSE_1_XOFF_THRESHOLD_0, pause_xoff_th);
+ **/
+ REG_WR(bp, (port) ? BRB1_REG_PAUSE_1_XOFF_THRESHOLD_1 :
+ BRB1_REG_PAUSE_1_XOFF_THRESHOLD_0,
+ reg_th_config->pause_xoff);
/*
* The number of free blocks above which the pause signal to
* class 1 of MAC #n is de-asserted. n=0,1
*/
- REG_WR(bp, BRB1_REG_PAUSE_1_XON_THRESHOLD_0, pause_xon_th);
+ REG_WR(bp, (port) ? BRB1_REG_PAUSE_1_XON_THRESHOLD_1 :
+ BRB1_REG_PAUSE_1_XON_THRESHOLD_0,
+ reg_th_config->pause_xon);
/*
* The number of free blocks below which the full signal to
* class 1 of MAC #n is asserted. n=0,1
*/
- REG_WR(bp, BRB1_REG_FULL_1_XOFF_THRESHOLD_0, full_xoff_th);
+ REG_WR(bp, (port) ? BRB1_REG_FULL_1_XOFF_THRESHOLD_1 :
+ BRB1_REG_FULL_1_XOFF_THRESHOLD_0,
+ reg_th_config->full_xoff);
/*
* The number of free blocks above which the full signal to
* class 1 of MAC #n is de-asserted. n=0,1
*/
- REG_WR(bp, BRB1_REG_FULL_1_XON_THRESHOLD_0, full_xon_th);
+ REG_WR(bp, (port) ? BRB1_REG_FULL_1_XON_THRESHOLD_1 :
+ BRB1_REG_FULL_1_XON_THRESHOLD_0,
+ reg_th_config->full_xon);
+
+
+ if (CHIP_IS_E3B0(bp)) {
+ /*Should be done by init tool */
+ /*
+ * BRB_empty_for_dup = BRB1_REG_BRB_EMPTY_THRESHOLD
+ * reset value
+ * 944
+ */
+
+ /**
+ * The hysteresis on the guarantied buffer space for the Lb port
+ * before signaling XON.
+ **/
+ REG_WR(bp, BRB1_REG_LB_GUARANTIED_HYST, 80);
+
+ bnx2x_pfc_brb_get_e3b0_config_params(
+ params,
+ &e3b0_val,
+ pfc_params->cos0_pauseable,
+ pfc_params->cos1_pauseable);
+ /**
+ * The number of free blocks below which the full signal to the
+ * LB port is asserted.
+ */
+ REG_WR(bp, BRB1_REG_FULL_LB_XOFF_THRESHOLD,
+ e3b0_val.full_lb_xoff_th);
+ /**
+ * The number of free blocks above which the full signal to the
+ * LB port is de-asserted.
+ */
+ REG_WR(bp, BRB1_REG_FULL_LB_XON_THRESHOLD,
+ e3b0_val.full_lb_xon_threshold);
+ /**
+ * The number of blocks guarantied for the MAC #n port. n=0,1
+ */
+
+ /*The number of blocks guarantied for the LB port.*/
+ REG_WR(bp, BRB1_REG_LB_GUARANTIED,
+ e3b0_val.lb_guarantied);
+
+ /**
+ * The number of blocks guarantied for the MAC #n port.
+ */
+ REG_WR(bp, BRB1_REG_MAC_GUARANTIED_0,
+ 2 * e3b0_val.mac_0_class_t_guarantied);
+ REG_WR(bp, BRB1_REG_MAC_GUARANTIED_1,
+ 2 * e3b0_val.mac_1_class_t_guarantied);
+ /**
+ * The number of blocks guarantied for class #t in MAC0. t=0,1
+ */
+ REG_WR(bp, BRB1_REG_MAC_0_CLASS_0_GUARANTIED,
+ e3b0_val.mac_0_class_t_guarantied);
+ REG_WR(bp, BRB1_REG_MAC_0_CLASS_1_GUARANTIED,
+ e3b0_val.mac_0_class_t_guarantied);
+ /**
+ * The hysteresis on the guarantied buffer space for class in
+ * MAC0. t=0,1
+ */
+ REG_WR(bp, BRB1_REG_MAC_0_CLASS_0_GUARANTIED_HYST,
+ e3b0_val.mac_0_class_t_guarantied_hyst);
+ REG_WR(bp, BRB1_REG_MAC_0_CLASS_1_GUARANTIED_HYST,
+ e3b0_val.mac_0_class_t_guarantied_hyst);
+
+ /**
+ * The number of blocks guarantied for class #t in MAC1.t=0,1
+ */
+ REG_WR(bp, BRB1_REG_MAC_1_CLASS_0_GUARANTIED,
+ e3b0_val.mac_1_class_t_guarantied);
+ REG_WR(bp, BRB1_REG_MAC_1_CLASS_1_GUARANTIED,
+ e3b0_val.mac_1_class_t_guarantied);
+ /**
+ * The hysteresis on the guarantied buffer space for class #t
+ * in MAC1. t=0,1
+ */
+ REG_WR(bp, BRB1_REG_MAC_1_CLASS_0_GUARANTIED_HYST,
+ e3b0_val.mac_1_class_t_guarantied_hyst);
+ REG_WR(bp, BRB1_REG_MAC_1_CLASS_1_GUARANTIED_HYST,
+ e3b0_val.mac_1_class_t_guarantied_hyst);
+
+ }
+
+ }
+
+ return bnx2x_status;
+}
+
+/******************************************************************************
+* Description:
+* This function is needed because NIG ARB_CREDIT_WEIGHT_X are
+* not continues and ARB_CREDIT_WEIGHT_0 + offset is suitable.
+******************************************************************************/
+int bnx2x_pfc_nig_rx_priority_mask(struct bnx2x *bp,
+ u8 cos_entry,
+ u32 priority_mask, u8 port)
+{
+ u32 nig_reg_rx_priority_mask_add = 0;
+
+ switch (cos_entry) {
+ case 0:
+ nig_reg_rx_priority_mask_add = (port) ?
+ NIG_REG_P1_RX_COS0_PRIORITY_MASK :
+ NIG_REG_P0_RX_COS0_PRIORITY_MASK;
+ break;
+ case 1:
+ nig_reg_rx_priority_mask_add = (port) ?
+ NIG_REG_P1_RX_COS1_PRIORITY_MASK :
+ NIG_REG_P0_RX_COS1_PRIORITY_MASK;
+ break;
+ case 2:
+ nig_reg_rx_priority_mask_add = (port) ?
+ NIG_REG_P1_RX_COS2_PRIORITY_MASK :
+ NIG_REG_P0_RX_COS2_PRIORITY_MASK;
+ break;
+ case 3:
+ if (port)
+ return -EINVAL;
+ nig_reg_rx_priority_mask_add = NIG_REG_P0_RX_COS3_PRIORITY_MASK;
+ break;
+ case 4:
+ if (port)
+ return -EINVAL;
+ nig_reg_rx_priority_mask_add = NIG_REG_P0_RX_COS4_PRIORITY_MASK;
+ break;
+ case 5:
+ if (port)
+ return -EINVAL;
+ nig_reg_rx_priority_mask_add = NIG_REG_P0_RX_COS5_PRIORITY_MASK;
+ break;
}
+
+ REG_WR(bp, nig_reg_rx_priority_mask_add, priority_mask);
+
+ return 0;
+}
+static void bnx2x_update_mng(struct link_params *params, u32 link_status)
+{
+ struct bnx2x *bp = params->bp;
+
+ REG_WR(bp, params->shmem_base +
+ offsetof(struct shmem_region,
+ port_mb[params->port].link_status), link_status);
}
static void bnx2x_update_pfc_nig(struct link_params *params,
@@ -858,9 +2400,9 @@ static void bnx2x_update_pfc_nig(struct link_params *params,
u32 xcm_mask = 0, ppp_enable = 0, pause_enable = 0, llfc_out_en = 0;
u32 llfc_enable = 0, xcm0_out_en = 0, p0_hwpfc_enable = 0;
u32 pkt_priority_to_cos = 0;
- u32 val;
struct bnx2x *bp = params->bp;
- int port = params->port;
+ u8 port = params->port;
+
int set_pfc = params->feature_config_flags &
FEATURE_CONFIG_PFC_ENABLED;
DP(NETIF_MSG_LINK, "updating pfc nig parameters\n");
@@ -881,6 +2423,9 @@ static void bnx2x_update_pfc_nig(struct link_params *params,
pause_enable = 0;
llfc_out_en = 0;
llfc_enable = 0;
+ if (CHIP_IS_E3(bp))
+ ppp_enable = 0;
+ else
ppp_enable = 1;
xcm_mask &= ~(port ? NIG_LLH1_XCM_MASK_REG_LLH1_XCM_MASK_BCN :
NIG_LLH0_XCM_MASK_REG_LLH0_XCM_MASK_BCN);
@@ -899,6 +2444,9 @@ static void bnx2x_update_pfc_nig(struct link_params *params,
xcm0_out_en = 1;
}
+ if (CHIP_IS_E3(bp))
+ REG_WR(bp, port ? NIG_REG_BRB1_PAUSE_IN_EN :
+ NIG_REG_BRB0_PAUSE_IN_EN, pause_enable);
REG_WR(bp, port ? NIG_REG_LLFC_OUT_EN_1 :
NIG_REG_LLFC_OUT_EN_0, llfc_out_en);
REG_WR(bp, port ? NIG_REG_LLFC_ENABLE_1 :
@@ -920,30 +2468,13 @@ static void bnx2x_update_pfc_nig(struct link_params *params,
/* HW PFC TX enable */
REG_WR(bp, NIG_REG_P0_HWPFC_ENABLE, p0_hwpfc_enable);
- /* 0x2 = BMAC, 0x1= EMAC */
- switch (vars->mac_type) {
- case MAC_TYPE_EMAC:
- val = 1;
- break;
- case MAC_TYPE_BMAC:
- val = 0;
- break;
- default:
- val = 0;
- break;
- }
- REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT, val);
-
if (nig_params) {
+ u8 i = 0;
pkt_priority_to_cos = nig_params->pkt_priority_to_cos;
- REG_WR(bp, port ? NIG_REG_P1_RX_COS0_PRIORITY_MASK :
- NIG_REG_P0_RX_COS0_PRIORITY_MASK,
- nig_params->rx_cos0_priority_mask);
-
- REG_WR(bp, port ? NIG_REG_P1_RX_COS1_PRIORITY_MASK :
- NIG_REG_P0_RX_COS1_PRIORITY_MASK,
- nig_params->rx_cos1_priority_mask);
+ for (i = 0; i < nig_params->num_of_rx_cos_priority_mask; i++)
+ bnx2x_pfc_nig_rx_priority_mask(bp, i,
+ nig_params->rx_cos_priority_mask[i], port);
REG_WR(bp, port ? NIG_REG_LLFC_HIGH_PRIORITY_CLASSES_1 :
NIG_REG_LLFC_HIGH_PRIORITY_CLASSES_0,
@@ -958,8 +2489,7 @@ static void bnx2x_update_pfc_nig(struct link_params *params,
pkt_priority_to_cos);
}
-
-void bnx2x_update_pfc(struct link_params *params,
+int bnx2x_update_pfc(struct link_params *params,
struct link_vars *vars,
struct bnx2x_nig_brb_pfc_port_params *pfc_params)
{
@@ -970,41 +2500,59 @@ void bnx2x_update_pfc(struct link_params *params,
*/
u32 val;
struct bnx2x *bp = params->bp;
+ int bnx2x_status = 0;
+ u8 bmac_loopback = (params->loopback_mode == LOOPBACK_BMAC);
+
+ if (params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED)
+ vars->link_status |= LINK_STATUS_PFC_ENABLED;
+ else
+ vars->link_status &= ~LINK_STATUS_PFC_ENABLED;
+
+ bnx2x_update_mng(params, vars->link_status);
/* update NIG params */
bnx2x_update_pfc_nig(params, vars, pfc_params);
/* update BRB params */
- bnx2x_update_pfc_brb(params, vars, pfc_params);
+ bnx2x_status = bnx2x_update_pfc_brb(params, vars, pfc_params);
+ if (0 != bnx2x_status)
+ return bnx2x_status;
if (!vars->link_up)
- return;
-
- val = REG_RD(bp, MISC_REG_RESET_REG_2);
- if ((val & (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port))
- == 0) {
- DP(NETIF_MSG_LINK, "About to update PFC in EMAC\n");
- bnx2x_emac_enable(params, vars, 0);
- return;
- }
+ return bnx2x_status;
DP(NETIF_MSG_LINK, "About to update PFC in BMAC\n");
- if (CHIP_IS_E2(bp))
- bnx2x_update_pfc_bmac2(params, vars, 0);
- else
- bnx2x_update_pfc_bmac1(params, vars);
+ if (CHIP_IS_E3(bp))
+ bnx2x_update_pfc_xmac(params, vars, 0);
+ else {
+ val = REG_RD(bp, MISC_REG_RESET_REG_2);
+ if ((val &
+ (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port))
+ == 0) {
+ DP(NETIF_MSG_LINK, "About to update PFC in EMAC\n");
+ bnx2x_emac_enable(params, vars, 0);
+ return bnx2x_status;
+ }
- val = 0;
- if ((params->feature_config_flags &
- FEATURE_CONFIG_PFC_ENABLED) ||
- (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX))
- val = 1;
- REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + params->port*4, val);
+ if (CHIP_IS_E2(bp))
+ bnx2x_update_pfc_bmac2(params, vars, bmac_loopback);
+ else
+ bnx2x_update_pfc_bmac1(params, vars);
+
+ val = 0;
+ if ((params->feature_config_flags &
+ FEATURE_CONFIG_PFC_ENABLED) ||
+ (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX))
+ val = 1;
+ REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + params->port*4, val);
+ }
+ return bnx2x_status;
}
-static u8 bnx2x_bmac1_enable(struct link_params *params,
- struct link_vars *vars,
- u8 is_lb)
+
+static int bnx2x_bmac1_enable(struct link_params *params,
+ struct link_vars *vars,
+ u8 is_lb)
{
struct bnx2x *bp = params->bp;
u8 port = params->port;
@@ -1066,9 +2614,9 @@ static u8 bnx2x_bmac1_enable(struct link_params *params,
return 0;
}
-static u8 bnx2x_bmac2_enable(struct link_params *params,
- struct link_vars *vars,
- u8 is_lb)
+static int bnx2x_bmac2_enable(struct link_params *params,
+ struct link_vars *vars,
+ u8 is_lb)
{
struct bnx2x *bp = params->bp;
u8 port = params->port;
@@ -1131,11 +2679,12 @@ static u8 bnx2x_bmac2_enable(struct link_params *params,
return 0;
}
-static u8 bnx2x_bmac_enable(struct link_params *params,
- struct link_vars *vars,
- u8 is_lb)
+static int bnx2x_bmac_enable(struct link_params *params,
+ struct link_vars *vars,
+ u8 is_lb)
{
- u8 rc, port = params->port;
+ int rc = 0;
+ u8 port = params->port;
struct bnx2x *bp = params->bp;
u32 val;
/* reset and unreset the BigMac */
@@ -1173,16 +2722,6 @@ static u8 bnx2x_bmac_enable(struct link_params *params,
return rc;
}
-
-static void bnx2x_update_mng(struct link_params *params, u32 link_status)
-{
- struct bnx2x *bp = params->bp;
-
- REG_WR(bp, params->shmem_base +
- offsetof(struct shmem_region,
- port_mb[params->port].link_status), link_status);
-}
-
static void bnx2x_bmac_rx_disable(struct bnx2x *bp, u8 port)
{
u32 bmac_addr = port ? NIG_REG_INGRESS_BMAC1_MEM :
@@ -1218,8 +2757,8 @@ static void bnx2x_bmac_rx_disable(struct bnx2x *bp, u8 port)
}
}
-static u8 bnx2x_pbf_update(struct link_params *params, u32 flow_ctrl,
- u32 line_speed)
+static int bnx2x_pbf_update(struct link_params *params, u32 flow_ctrl,
+ u32 line_speed)
{
struct bnx2x *bp = params->bp;
u8 port = params->port;
@@ -1269,18 +2808,6 @@ static u8 bnx2x_pbf_update(struct link_params *params, u32 flow_ctrl,
case SPEED_10000:
init_crd = thresh + 553 - 22;
break;
-
- case SPEED_12000:
- init_crd = thresh + 664 - 22;
- break;
-
- case SPEED_13000:
- init_crd = thresh + 742 - 22;
- break;
-
- case SPEED_16000:
- init_crd = thresh + 778 - 22;
- break;
default:
DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n",
line_speed);
@@ -1349,31 +2876,23 @@ static u32 bnx2x_get_emac_base(struct bnx2x *bp,
}
/******************************************************************/
-/* CL45 access functions */
+/* CL22 access functions */
/******************************************************************/
-static u8 bnx2x_cl45_write(struct bnx2x *bp, struct bnx2x_phy *phy,
- u8 devad, u16 reg, u16 val)
+static int bnx2x_cl22_write(struct bnx2x *bp,
+ struct bnx2x_phy *phy,
+ u16 reg, u16 val)
{
- u32 tmp, saved_mode;
- u8 i, rc = 0;
- /*
- * Set clause 45 mode, slow down the MDIO clock to 2.5MHz
- * (a value of 49==0x31) and make sure that the AUTO poll is off
- */
-
- saved_mode = REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE);
- tmp = saved_mode & ~(EMAC_MDIO_MODE_AUTO_POLL |
- EMAC_MDIO_MODE_CLOCK_CNT);
- tmp |= (EMAC_MDIO_MODE_CLAUSE_45 |
- (49 << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT));
- REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, tmp);
- REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE);
- udelay(40);
+ u32 tmp, mode;
+ u8 i;
+ int rc = 0;
+ /* Switch to CL22 */
+ mode = REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE);
+ REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE,
+ mode & ~EMAC_MDIO_MODE_CLAUSE_45);
/* address */
-
- tmp = ((phy->addr << 21) | (devad << 16) | reg |
- EMAC_MDIO_COMM_COMMAND_ADDRESS |
+ tmp = ((phy->addr << 21) | (reg << 16) | val |
+ EMAC_MDIO_COMM_COMMAND_WRITE_22 |
EMAC_MDIO_COMM_START_BUSY);
REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp);
@@ -1388,58 +2907,63 @@ static u8 bnx2x_cl45_write(struct bnx2x *bp, struct bnx2x_phy *phy,
}
if (tmp & EMAC_MDIO_COMM_START_BUSY) {
DP(NETIF_MSG_LINK, "write phy register failed\n");
- netdev_err(bp->dev, "MDC/MDIO access timeout\n");
rc = -EFAULT;
- } else {
- /* data */
- tmp = ((phy->addr << 21) | (devad << 16) | val |
- EMAC_MDIO_COMM_COMMAND_WRITE_45 |
- EMAC_MDIO_COMM_START_BUSY);
- REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp);
+ }
+ REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, mode);
+ return rc;
+}
- for (i = 0; i < 50; i++) {
- udelay(10);
+static int bnx2x_cl22_read(struct bnx2x *bp,
+ struct bnx2x_phy *phy,
+ u16 reg, u16 *ret_val)
+{
+ u32 val, mode;
+ u16 i;
+ int rc = 0;
- tmp = REG_RD(bp, phy->mdio_ctrl +
- EMAC_REG_EMAC_MDIO_COMM);
- if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) {
- udelay(5);
- break;
- }
- }
- if (tmp & EMAC_MDIO_COMM_START_BUSY) {
- DP(NETIF_MSG_LINK, "write phy register failed\n");
- netdev_err(bp->dev, "MDC/MDIO access timeout\n");
- rc = -EFAULT;
+ /* Switch to CL22 */
+ mode = REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE);
+ REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE,
+ mode & ~EMAC_MDIO_MODE_CLAUSE_45);
+
+ /* address */
+ val = ((phy->addr << 21) | (reg << 16) |
+ EMAC_MDIO_COMM_COMMAND_READ_22 |
+ EMAC_MDIO_COMM_START_BUSY);
+ REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, val);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ val = REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM);
+ if (!(val & EMAC_MDIO_COMM_START_BUSY)) {
+ *ret_val = (u16)(val & EMAC_MDIO_COMM_DATA);
+ udelay(5);
+ break;
}
}
+ if (val & EMAC_MDIO_COMM_START_BUSY) {
+ DP(NETIF_MSG_LINK, "read phy register failed\n");
- /* Restore the saved mode */
- REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, saved_mode);
-
+ *ret_val = 0;
+ rc = -EFAULT;
+ }
+ REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, mode);
return rc;
}
-static u8 bnx2x_cl45_read(struct bnx2x *bp, struct bnx2x_phy *phy,
- u8 devad, u16 reg, u16 *ret_val)
+/******************************************************************/
+/* CL45 access functions */
+/******************************************************************/
+static int bnx2x_cl45_read(struct bnx2x *bp, struct bnx2x_phy *phy,
+ u8 devad, u16 reg, u16 *ret_val)
{
- u32 val, saved_mode;
+ u32 val;
u16 i;
- u8 rc = 0;
- /*
- * Set clause 45 mode, slow down the MDIO clock to 2.5MHz
- * (a value of 49==0x31) and make sure that the AUTO poll is off
- */
-
- saved_mode = REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE);
- val = saved_mode & ~((EMAC_MDIO_MODE_AUTO_POLL |
- EMAC_MDIO_MODE_CLOCK_CNT));
- val |= (EMAC_MDIO_MODE_CLAUSE_45 |
- (49L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT));
- REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, val);
- REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE);
- udelay(40);
-
+ int rc = 0;
+ if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+ bnx2x_bits_en(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+ EMAC_MDIO_STATUS_10MB);
/* address */
val = ((phy->addr << 21) | (devad << 16) | reg |
EMAC_MDIO_COMM_COMMAND_ADDRESS |
@@ -1460,7 +2984,6 @@ static u8 bnx2x_cl45_read(struct bnx2x *bp, struct bnx2x_phy *phy,
netdev_err(bp->dev, "MDC/MDIO access timeout\n");
*ret_val = 0;
rc = -EFAULT;
-
} else {
/* data */
val = ((phy->addr << 21) | (devad << 16) |
@@ -1485,15 +3008,222 @@ static u8 bnx2x_cl45_read(struct bnx2x *bp, struct bnx2x_phy *phy,
rc = -EFAULT;
}
}
+ /* Work around for E3 A0 */
+ if (phy->flags & FLAGS_MDC_MDIO_WA) {
+ phy->flags ^= FLAGS_DUMMY_READ;
+ if (phy->flags & FLAGS_DUMMY_READ) {
+ u16 temp_val;
+ bnx2x_cl45_read(bp, phy, devad, 0xf, &temp_val);
+ }
+ }
+
+ if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+ bnx2x_bits_dis(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+ EMAC_MDIO_STATUS_10MB);
+ return rc;
+}
+
+static int bnx2x_cl45_write(struct bnx2x *bp, struct bnx2x_phy *phy,
+ u8 devad, u16 reg, u16 val)
+{
+ u32 tmp;
+ u8 i;
+ int rc = 0;
+ if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+ bnx2x_bits_en(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+ EMAC_MDIO_STATUS_10MB);
+
+ /* address */
+
+ tmp = ((phy->addr << 21) | (devad << 16) | reg |
+ EMAC_MDIO_COMM_COMMAND_ADDRESS |
+ EMAC_MDIO_COMM_START_BUSY);
+ REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ tmp = REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM);
+ if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) {
+ udelay(5);
+ break;
+ }
+ }
+ if (tmp & EMAC_MDIO_COMM_START_BUSY) {
+ DP(NETIF_MSG_LINK, "write phy register failed\n");
+ netdev_err(bp->dev, "MDC/MDIO access timeout\n");
+ rc = -EFAULT;
+
+ } else {
+ /* data */
+ tmp = ((phy->addr << 21) | (devad << 16) | val |
+ EMAC_MDIO_COMM_COMMAND_WRITE_45 |
+ EMAC_MDIO_COMM_START_BUSY);
+ REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ tmp = REG_RD(bp, phy->mdio_ctrl +
+ EMAC_REG_EMAC_MDIO_COMM);
+ if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) {
+ udelay(5);
+ break;
+ }
+ }
+ if (tmp & EMAC_MDIO_COMM_START_BUSY) {
+ DP(NETIF_MSG_LINK, "write phy register failed\n");
+ netdev_err(bp->dev, "MDC/MDIO access timeout\n");
+ rc = -EFAULT;
+ }
+ }
+ /* Work around for E3 A0 */
+ if (phy->flags & FLAGS_MDC_MDIO_WA) {
+ phy->flags ^= FLAGS_DUMMY_READ;
+ if (phy->flags & FLAGS_DUMMY_READ) {
+ u16 temp_val;
+ bnx2x_cl45_read(bp, phy, devad, 0xf, &temp_val);
+ }
+ }
+ if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+ bnx2x_bits_dis(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+ EMAC_MDIO_STATUS_10MB);
+ return rc;
+}
+
+
+/******************************************************************/
+/* BSC access functions from E3 */
+/******************************************************************/
+static void bnx2x_bsc_module_sel(struct link_params *params)
+{
+ int idx;
+ u32 board_cfg, sfp_ctrl;
+ u32 i2c_pins[I2C_SWITCH_WIDTH], i2c_val[I2C_SWITCH_WIDTH];
+ struct bnx2x *bp = params->bp;
+ u8 port = params->port;
+ /* Read I2C output PINs */
+ board_cfg = REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.shared_hw_config.board));
+ i2c_pins[I2C_BSC0] = board_cfg & SHARED_HW_CFG_E3_I2C_MUX0_MASK;
+ i2c_pins[I2C_BSC1] = (board_cfg & SHARED_HW_CFG_E3_I2C_MUX1_MASK) >>
+ SHARED_HW_CFG_E3_I2C_MUX1_SHIFT;
+
+ /* Read I2C output value */
+ sfp_ctrl = REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[port].e3_cmn_pin_cfg));
+ i2c_val[I2C_BSC0] = (sfp_ctrl & PORT_HW_CFG_E3_I2C_MUX0_MASK) > 0;
+ i2c_val[I2C_BSC1] = (sfp_ctrl & PORT_HW_CFG_E3_I2C_MUX1_MASK) > 0;
+ DP(NETIF_MSG_LINK, "Setting BSC switch\n");
+ for (idx = 0; idx < I2C_SWITCH_WIDTH; idx++)
+ bnx2x_set_cfg_pin(bp, i2c_pins[idx], i2c_val[idx]);
+}
+
+static int bnx2x_bsc_read(struct link_params *params,
+ struct bnx2x_phy *phy,
+ u8 sl_devid,
+ u16 sl_addr,
+ u8 lc_addr,
+ u8 xfer_cnt,
+ u32 *data_array)
+{
+ u32 val, i;
+ int rc = 0;
+ struct bnx2x *bp = params->bp;
+
+ if ((sl_devid != 0xa0) && (sl_devid != 0xa2)) {
+ DP(NETIF_MSG_LINK, "invalid sl_devid 0x%x\n", sl_devid);
+ return -EINVAL;
+ }
+
+ if (xfer_cnt > 16) {
+ DP(NETIF_MSG_LINK, "invalid xfer_cnt %d. Max is 16 bytes\n",
+ xfer_cnt);
+ return -EINVAL;
+ }
+ bnx2x_bsc_module_sel(params);
+
+ xfer_cnt = 16 - lc_addr;
+
+ /* enable the engine */
+ val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND);
+ val |= MCPR_IMC_COMMAND_ENABLE;
+ REG_WR(bp, MCP_REG_MCPR_IMC_COMMAND, val);
- /* Restore the saved mode */
- REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, saved_mode);
+ /* program slave device ID */
+ val = (sl_devid << 16) | sl_addr;
+ REG_WR(bp, MCP_REG_MCPR_IMC_SLAVE_CONTROL, val);
+ /* start xfer with 0 byte to update the address pointer ???*/
+ val = (MCPR_IMC_COMMAND_ENABLE) |
+ (MCPR_IMC_COMMAND_WRITE_OP <<
+ MCPR_IMC_COMMAND_OPERATION_BITSHIFT) |
+ (lc_addr << MCPR_IMC_COMMAND_TRANSFER_ADDRESS_BITSHIFT) | (0);
+ REG_WR(bp, MCP_REG_MCPR_IMC_COMMAND, val);
+
+ /* poll for completion */
+ i = 0;
+ val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND);
+ while (((val >> MCPR_IMC_COMMAND_IMC_STATUS_BITSHIFT) & 0x3) != 1) {
+ udelay(10);
+ val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND);
+ if (i++ > 1000) {
+ DP(NETIF_MSG_LINK, "wr 0 byte timed out after %d try\n",
+ i);
+ rc = -EFAULT;
+ break;
+ }
+ }
+ if (rc == -EFAULT)
+ return rc;
+
+ /* start xfer with read op */
+ val = (MCPR_IMC_COMMAND_ENABLE) |
+ (MCPR_IMC_COMMAND_READ_OP <<
+ MCPR_IMC_COMMAND_OPERATION_BITSHIFT) |
+ (lc_addr << MCPR_IMC_COMMAND_TRANSFER_ADDRESS_BITSHIFT) |
+ (xfer_cnt);
+ REG_WR(bp, MCP_REG_MCPR_IMC_COMMAND, val);
+
+ /* poll for completion */
+ i = 0;
+ val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND);
+ while (((val >> MCPR_IMC_COMMAND_IMC_STATUS_BITSHIFT) & 0x3) != 1) {
+ udelay(10);
+ val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND);
+ if (i++ > 1000) {
+ DP(NETIF_MSG_LINK, "rd op timed out after %d try\n", i);
+ rc = -EFAULT;
+ break;
+ }
+ }
+ if (rc == -EFAULT)
+ return rc;
+
+ for (i = (lc_addr >> 2); i < 4; i++) {
+ data_array[i] = REG_RD(bp, (MCP_REG_MCPR_IMC_DATAREG0 + i*4));
+#ifdef __BIG_ENDIAN
+ data_array[i] = ((data_array[i] & 0x000000ff) << 24) |
+ ((data_array[i] & 0x0000ff00) << 8) |
+ ((data_array[i] & 0x00ff0000) >> 8) |
+ ((data_array[i] & 0xff000000) >> 24);
+#endif
+ }
return rc;
}
-u8 bnx2x_phy_read(struct link_params *params, u8 phy_addr,
- u8 devad, u16 reg, u16 *ret_val)
+static void bnx2x_cl45_read_or_write(struct bnx2x *bp, struct bnx2x_phy *phy,
+ u8 devad, u16 reg, u16 or_val)
+{
+ u16 val;
+ bnx2x_cl45_read(bp, phy, devad, reg, &val);
+ bnx2x_cl45_write(bp, phy, devad, reg, val | or_val);
+}
+
+int bnx2x_phy_read(struct link_params *params, u8 phy_addr,
+ u8 devad, u16 reg, u16 *ret_val)
{
u8 phy_index;
/*
@@ -1510,8 +3240,8 @@ u8 bnx2x_phy_read(struct link_params *params, u8 phy_addr,
return -EINVAL;
}
-u8 bnx2x_phy_write(struct link_params *params, u8 phy_addr,
- u8 devad, u16 reg, u16 val)
+int bnx2x_phy_write(struct link_params *params, u8 phy_addr,
+ u8 devad, u16 reg, u16 val)
{
u8 phy_index;
/*
@@ -1527,9 +3257,62 @@ u8 bnx2x_phy_write(struct link_params *params, u8 phy_addr,
}
return -EINVAL;
}
+static u8 bnx2x_get_warpcore_lane(struct bnx2x_phy *phy,
+ struct link_params *params)
+{
+ u8 lane = 0;
+ struct bnx2x *bp = params->bp;
+ u32 path_swap, path_swap_ovr;
+ u8 path, port;
+
+ path = BP_PATH(bp);
+ port = params->port;
+
+ if (bnx2x_is_4_port_mode(bp)) {
+ u32 port_swap, port_swap_ovr;
+
+ /*figure out path swap value */
+ path_swap_ovr = REG_RD(bp, MISC_REG_FOUR_PORT_PATH_SWAP_OVWR);
+ if (path_swap_ovr & 0x1)
+ path_swap = (path_swap_ovr & 0x2);
+ else
+ path_swap = REG_RD(bp, MISC_REG_FOUR_PORT_PATH_SWAP);
+
+ if (path_swap)
+ path = path ^ 1;
+
+ /*figure out port swap value */
+ port_swap_ovr = REG_RD(bp, MISC_REG_FOUR_PORT_PORT_SWAP_OVWR);
+ if (port_swap_ovr & 0x1)
+ port_swap = (port_swap_ovr & 0x2);
+ else
+ port_swap = REG_RD(bp, MISC_REG_FOUR_PORT_PORT_SWAP);
+
+ if (port_swap)
+ port = port ^ 1;
+
+ lane = (port<<1) + path;
+ } else { /* two port mode - no port swap */
+
+ /*figure out path swap value */
+ path_swap_ovr =
+ REG_RD(bp, MISC_REG_TWO_PORT_PATH_SWAP_OVWR);
+ if (path_swap_ovr & 0x1) {
+ path_swap = (path_swap_ovr & 0x2);
+ } else {
+ path_swap =
+ REG_RD(bp, MISC_REG_TWO_PORT_PATH_SWAP);
+ }
+ if (path_swap)
+ path = path ^ 1;
+
+ lane = path << 1 ;
+ }
+ return lane;
+}
-static void bnx2x_set_aer_mmd_xgxs(struct link_params *params,
- struct bnx2x_phy *phy)
+static void bnx2x_set_aer_mmd(struct link_params *params,
+ struct bnx2x_phy *phy)
{
u32 ser_lane;
u16 offset, aer_val;
@@ -1538,20 +3321,28 @@ static void bnx2x_set_aer_mmd_xgxs(struct link_params *params,
PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >>
PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT);
- offset = phy->addr + ser_lane;
- if (CHIP_IS_E2(bp))
+ offset = (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) ?
+ (phy->addr + ser_lane) : 0;
+
+ if (USES_WARPCORE(bp)) {
+ aer_val = bnx2x_get_warpcore_lane(phy, params);
+ /*
+ * In Dual-lane mode, two lanes are joined together,
+ * so in order to configure them, the AER broadcast method is
+ * used here.
+ * 0x200 is the broadcast address for lanes 0,1
+ * 0x201 is the broadcast address for lanes 2,3
+ */
+ if (phy->flags & FLAGS_WC_DUAL_MODE)
+ aer_val = (aer_val >> 1) | 0x200;
+ } else if (CHIP_IS_E2(bp))
aer_val = 0x3800 + offset - 1;
else
aer_val = 0x3800 + offset;
+ DP(NETIF_MSG_LINK, "Set AER to 0x%x\n", aer_val);
CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK,
MDIO_AER_BLOCK_AER_REG, aer_val);
-}
-static void bnx2x_set_aer_mmd_serdes(struct bnx2x *bp,
- struct bnx2x_phy *phy)
-{
- CL22_WR_OVER_CL45(bp, phy,
- MDIO_REG_BANK_AER_BLOCK,
- MDIO_AER_BLOCK_AER_REG, 0x3800);
+
}
/******************************************************************/
@@ -1611,19 +3402,1072 @@ static void bnx2x_xgxs_deassert(struct link_params *params)
params->phy[INT_PHY].def_md_devad);
}
+static void bnx2x_calc_ieee_aneg_adv(struct bnx2x_phy *phy,
+ struct link_params *params, u16 *ieee_fc)
+{
+ struct bnx2x *bp = params->bp;
+ *ieee_fc = MDIO_COMBO_IEEE0_AUTO_NEG_ADV_FULL_DUPLEX;
+ /**
+ * resolve pause mode and advertisement Please refer to Table
+ * 28B-3 of the 802.3ab-1999 spec
+ */
+
+ switch (phy->req_flow_ctrl) {
+ case BNX2X_FLOW_CTRL_AUTO:
+ if (params->req_fc_auto_adv == BNX2X_FLOW_CTRL_BOTH)
+ *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH;
+ else
+ *ieee_fc |=
+ MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC;
+ break;
+
+ case BNX2X_FLOW_CTRL_TX:
+ *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC;
+ break;
+
+ case BNX2X_FLOW_CTRL_RX:
+ case BNX2X_FLOW_CTRL_BOTH:
+ *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH;
+ break;
+
+ case BNX2X_FLOW_CTRL_NONE:
+ default:
+ *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE;
+ break;
+ }
+ DP(NETIF_MSG_LINK, "ieee_fc = 0x%x\n", *ieee_fc);
+}
+
+static void set_phy_vars(struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u8 actual_phy_idx, phy_index, link_cfg_idx;
+ u8 phy_config_swapped = params->multi_phy_config &
+ PORT_HW_CFG_PHY_SWAPPED_ENABLED;
+ for (phy_index = INT_PHY; phy_index < params->num_phys;
+ phy_index++) {
+ link_cfg_idx = LINK_CONFIG_IDX(phy_index);
+ actual_phy_idx = phy_index;
+ if (phy_config_swapped) {
+ if (phy_index == EXT_PHY1)
+ actual_phy_idx = EXT_PHY2;
+ else if (phy_index == EXT_PHY2)
+ actual_phy_idx = EXT_PHY1;
+ }
+ params->phy[actual_phy_idx].req_flow_ctrl =
+ params->req_flow_ctrl[link_cfg_idx];
+
+ params->phy[actual_phy_idx].req_line_speed =
+ params->req_line_speed[link_cfg_idx];
+
+ params->phy[actual_phy_idx].speed_cap_mask =
+ params->speed_cap_mask[link_cfg_idx];
+
+ params->phy[actual_phy_idx].req_duplex =
+ params->req_duplex[link_cfg_idx];
+
+ if (params->req_line_speed[link_cfg_idx] ==
+ SPEED_AUTO_NEG)
+ vars->link_status |= LINK_STATUS_AUTO_NEGOTIATE_ENABLED;
+
+ DP(NETIF_MSG_LINK, "req_flow_ctrl %x, req_line_speed %x,"
+ " speed_cap_mask %x\n",
+ params->phy[actual_phy_idx].req_flow_ctrl,
+ params->phy[actual_phy_idx].req_line_speed,
+ params->phy[actual_phy_idx].speed_cap_mask);
+ }
+}
+
+static void bnx2x_ext_phy_set_pause(struct link_params *params,
+ struct bnx2x_phy *phy,
+ struct link_vars *vars)
+{
+ u16 val;
+ struct bnx2x *bp = params->bp;
+ /* read modify write pause advertizing */
+ bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, &val);
+
+ val &= ~MDIO_AN_REG_ADV_PAUSE_BOTH;
+
+ /* Please refer to Table 28B-3 of 802.3ab-1999 spec. */
+ bnx2x_calc_ieee_aneg_adv(phy, params, &vars->ieee_fc);
+ if ((vars->ieee_fc &
+ MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) ==
+ MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) {
+ val |= MDIO_AN_REG_ADV_PAUSE_ASYMMETRIC;
+ }
+ if ((vars->ieee_fc &
+ MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) ==
+ MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) {
+ val |= MDIO_AN_REG_ADV_PAUSE_PAUSE;
+ }
+ DP(NETIF_MSG_LINK, "Ext phy AN advertize 0x%x\n", val);
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, val);
+}
+
+static void bnx2x_pause_resolve(struct link_vars *vars, u32 pause_result)
+{ /* LD LP */
+ switch (pause_result) { /* ASYM P ASYM P */
+ case 0xb: /* 1 0 1 1 */
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_TX;
+ break;
+
+ case 0xe: /* 1 1 1 0 */
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_RX;
+ break;
+
+ case 0x5: /* 0 1 0 1 */
+ case 0x7: /* 0 1 1 1 */
+ case 0xd: /* 1 1 0 1 */
+ case 0xf: /* 1 1 1 1 */
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_BOTH;
+ break;
+
+ default:
+ break;
+ }
+ if (pause_result & (1<<0))
+ vars->link_status |= LINK_STATUS_LINK_PARTNER_SYMMETRIC_PAUSE;
+ if (pause_result & (1<<1))
+ vars->link_status |= LINK_STATUS_LINK_PARTNER_ASYMMETRIC_PAUSE;
+}
+
+static u8 bnx2x_ext_phy_resolve_fc(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u16 ld_pause; /* local */
+ u16 lp_pause; /* link partner */
+ u16 pause_result;
+ u8 ret = 0;
+ /* read twice */
+
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
+
+ if (phy->req_flow_ctrl != BNX2X_FLOW_CTRL_AUTO)
+ vars->flow_ctrl = phy->req_flow_ctrl;
+ else if (phy->req_line_speed != SPEED_AUTO_NEG)
+ vars->flow_ctrl = params->req_fc_auto_adv;
+ else if (vars->link_status & LINK_STATUS_AUTO_NEGOTIATE_COMPLETE) {
+ ret = 1;
+ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE) {
+ bnx2x_cl22_read(bp, phy,
+ 0x4, &ld_pause);
+ bnx2x_cl22_read(bp, phy,
+ 0x5, &lp_pause);
+ } else {
+ bnx2x_cl45_read(bp, phy,
+ MDIO_AN_DEVAD,
+ MDIO_AN_REG_ADV_PAUSE, &ld_pause);
+ bnx2x_cl45_read(bp, phy,
+ MDIO_AN_DEVAD,
+ MDIO_AN_REG_LP_AUTO_NEG, &lp_pause);
+ }
+ pause_result = (ld_pause &
+ MDIO_AN_REG_ADV_PAUSE_MASK) >> 8;
+ pause_result |= (lp_pause &
+ MDIO_AN_REG_ADV_PAUSE_MASK) >> 10;
+ DP(NETIF_MSG_LINK, "Ext PHY pause result 0x%x\n",
+ pause_result);
+ bnx2x_pause_resolve(vars, pause_result);
+ }
+ return ret;
+}
+/******************************************************************/
+/* Warpcore section */
+/******************************************************************/
+/* The init_internal_warpcore should mirror the xgxs,
+ * i.e. reset the lane (if needed), set aer for the
+ * init configuration, and set/clear SGMII flag. Internal
+ * phy init is done purely in phy_init stage.
+ */
+static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars) {
+ u16 val16 = 0, lane, bam37 = 0;
+ struct bnx2x *bp = params->bp;
+ DP(NETIF_MSG_LINK, "Enable Auto Negotiation for KR\n");
+
+ /* Disable Autoneg: re-enable it after adv is done. */
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0);
+
+ /* Check adding advertisement for 1G KX */
+ if (((vars->line_speed == SPEED_AUTO_NEG) &&
+ (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) ||
+ (vars->line_speed == SPEED_1000)) {
+ u16 sd_digital;
+ val16 |= (1<<5);
+
+ /* Enable CL37 1G Parallel Detect */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, &sd_digital);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2,
+ (sd_digital | 0x1));
+
+ DP(NETIF_MSG_LINK, "Advertize 1G\n");
+ }
+ if (((vars->line_speed == SPEED_AUTO_NEG) &&
+ (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)) ||
+ (vars->line_speed == SPEED_10000)) {
+ /* Check adding advertisement for 10G KR */
+ val16 |= (1<<7);
+ /* Enable 10G Parallel Detect */
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_PAR_DET_10G_CTRL, 1);
+
+ DP(NETIF_MSG_LINK, "Advertize 10G\n");
+ }
+
+ /* Set Transmit PMD settings */
+ lane = bnx2x_get_warpcore_lane(phy, params);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_TX0_TX_DRIVER + 0x10*lane,
+ ((0x02 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) |
+ (0x06 << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) |
+ (0x09 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET)));
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_CL72_USERB0_CL72_OS_DEF_CTRL,
+ 0x03f0);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_CL72_USERB0_CL72_2P5_DEF_CTRL,
+ 0x03f0);
+
+ /* Advertised speeds */
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_AN_IEEE1BLK_AN_ADVERTISEMENT1, val16);
+
+ /* Advertised and set FEC (Forward Error Correction) */
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_AN_IEEE1BLK_AN_ADVERTISEMENT2,
+ (MDIO_WC_REG_AN_IEEE1BLK_AN_ADV2_FEC_ABILITY |
+ MDIO_WC_REG_AN_IEEE1BLK_AN_ADV2_FEC_REQ));
+
+ /* Enable CL37 BAM */
+ if (REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region, dev_info.
+ port_hw_config[params->port].default_cfg)) &
+ PORT_HW_CFG_ENABLE_BAM_ON_KR_ENABLED) {
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL6_MP5_NEXTPAGECTRL, &bam37);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL6_MP5_NEXTPAGECTRL, bam37 | 1);
+ DP(NETIF_MSG_LINK, "Enable CL37 BAM on KR\n");
+ }
+
+ /* Advertise pause */
+ bnx2x_ext_phy_set_pause(params, phy, vars);
+
+ vars->rx_tx_asic_rst = MAX_KR_LINK_RETRY;
+
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL5_MISC7, &val16);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL5_MISC7, val16 | 0x100);
+
+ /* Over 1G - AN local device user page 1 */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL3_UP1, 0x1f);
+
+ /* Enable Autoneg */
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x1000);
+
+}
+
+static void bnx2x_warpcore_set_10G_KR(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u16 val;
+
+ /* Disable Autoneg */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x7);
+
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_PAR_DET_10G_CTRL, 0);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, 0x3f00);
+
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_AN_IEEE1BLK_AN_ADVERTISEMENT1, 0);
+
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x0);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL3_UP1, 0x1);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL5_MISC7, 0xa);
+
+ /* Disable CL36 PCS Tx */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_XGXSBLK1_LANECTRL0, 0x0);
+
+ /* Double Wide Single Data Rate @ pll rate */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_XGXSBLK1_LANECTRL1, 0xFFFF);
+
+ /* Leave cl72 training enable, needed for KR */
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD,
+ MDIO_WC_REG_PMD_IEEE9BLK_TENGBASE_KR_PMD_CONTROL_REGISTER_150,
+ 0x2);
+
+ /* Leave CL72 enabled */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL,
+ &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL,
+ val | 0x3800);
+
+ /* Set speed via PMA/PMD register */
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x2040);
+
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_AUTONEGNP, 0xB);
+
+ /*Enable encoded forced speed */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_MISC2, 0x30);
+
+ /* Turn TX scramble payload only the 64/66 scrambler */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_TX66_CONTROL, 0x9);
+
+ /* Turn RX scramble payload only the 64/66 scrambler */
+ bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_CONTROL, 0xF9);
+
+ /* set and clear loopback to cause a reset to 64/66 decoder */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x4000);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x0);
+
+}
+
+static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy,
+ struct link_params *params,
+ u8 is_xfi)
+{
+ struct bnx2x *bp = params->bp;
+ u16 misc1_val, tap_val, tx_driver_val, lane, val;
+ /* Hold rxSeqStart */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, (val | 0x8000));
+
+ /* Hold tx_fifo_reset */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, (val | 0x1));
+
+ /* Disable CL73 AN */
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0);
+
+ /* Disable 100FX Enable and Auto-Detect */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_FX100_CTRL1, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_FX100_CTRL1, (val & 0xFFFA));
+
+ /* Disable 100FX Idle detect */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_FX100_CTRL3, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_FX100_CTRL3, (val | 0x0080));
+
+ /* Set Block address to Remote PHY & Clear forced_speed[5] */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL4_MISC3, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL4_MISC3, (val & 0xFF7F));
+
+ /* Turn off auto-detect & fiber mode */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1,
+ (val & 0xFFEE));
+
+ /* Set filter_force_link, disable_false_link and parallel_detect */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2,
+ ((val | 0x0006) & 0xFFFE));
+
+ /* Set XFI / SFI */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_MISC1, &misc1_val);
+
+ misc1_val &= ~(0x1f);
+
+ if (is_xfi) {
+ misc1_val |= 0x5;
+ tap_val = ((0x08 << MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET) |
+ (0x37 << MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_OFFSET) |
+ (0x00 << MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_OFFSET));
+ tx_driver_val =
+ ((0x00 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) |
+ (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) |
+ (0x03 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET));
+
+ } else {
+ misc1_val |= 0x9;
+ tap_val = ((0x12 << MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET) |
+ (0x2d << MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_OFFSET) |
+ (0x00 << MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_OFFSET));
+ tx_driver_val =
+ ((0x02 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) |
+ (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) |
+ (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET));
+ }
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_MISC1, misc1_val);
+
+ /* Set Transmit PMD settings */
+ lane = bnx2x_get_warpcore_lane(phy, params);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_TX_FIR_TAP,
+ tap_val | MDIO_WC_REG_TX_FIR_TAP_ENABLE);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_TX0_TX_DRIVER + 0x10*lane,
+ tx_driver_val);
+
+ /* Enable fiber mode, enable and invert sig_det */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, val | 0xd);
+
+ /* Set Block address to Remote PHY & Set forced_speed[5], 40bit mode */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL4_MISC3, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL4_MISC3, val | 0x8080);
+
+ /* 10G XFI Full Duplex */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x100);
+
+ /* Release tx_fifo_reset */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, val & 0xFFFE);
+
+ /* Release rxSeqStart */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, &val);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, (val & 0x7FFF));
+}
+
+static void bnx2x_warpcore_set_20G_KR2(struct bnx2x *bp,
+ struct bnx2x_phy *phy)
+{
+ DP(NETIF_MSG_LINK, "KR2 still not supported !!!\n");
+}
+
+static void bnx2x_warpcore_set_20G_DXGXS(struct bnx2x *bp,
+ struct bnx2x_phy *phy,
+ u16 lane)
+{
+ /* Rx0 anaRxControl1G */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX0_ANARXCONTROL1G, 0x90);
+
+ /* Rx2 anaRxControl1G */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX2_ANARXCONTROL1G, 0x90);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_SCW0, 0xE070);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_SCW1, 0xC0D0);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_SCW2, 0xA0B0);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_SCW3, 0x8090);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_SCW0_MASK, 0xF0F0);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_SCW1_MASK, 0xF0F0);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_SCW2_MASK, 0xF0F0);
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_SCW3_MASK, 0xF0F0);
+
+ /* Serdes Digital Misc1 */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_MISC1, 0x6008);
+
+ /* Serdes Digital4 Misc3 */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL4_MISC3, 0x8088);
+
+ /* Set Transmit PMD settings */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_TX_FIR_TAP,
+ ((0x12 << MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET) |
+ (0x2d << MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_OFFSET) |
+ (0x00 << MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_OFFSET) |
+ MDIO_WC_REG_TX_FIR_TAP_ENABLE));
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_TX0_TX_DRIVER + 0x10*lane,
+ ((0x02 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) |
+ (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) |
+ (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET)));
+}
+
+static void bnx2x_warpcore_set_sgmii_speed(struct bnx2x_phy *phy,
+ struct link_params *params,
+ u8 fiber_mode)
+{
+ struct bnx2x *bp = params->bp;
+ u16 val16, digctrl_kx1, digctrl_kx2;
+ u8 lane;
+
+ lane = bnx2x_get_warpcore_lane(phy, params);
+
+ /* Clear XFI clock comp in non-10G single lane mode. */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_CONTROL, &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_CONTROL, val16 & ~(3<<13));
+
+ if (phy->req_line_speed == SPEED_AUTO_NEG) {
+ /* SGMII Autoneg */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL,
+ val16 | 0x1000);
+ DP(NETIF_MSG_LINK, "set SGMII AUTONEG\n");
+ } else {
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, &val16);
+ val16 &= 0xcfbf;
+ switch (phy->req_line_speed) {
+ case SPEED_10:
+ break;
+ case SPEED_100:
+ val16 |= 0x2000;
+ break;
+ case SPEED_1000:
+ val16 |= 0x0040;
+ break;
+ default:
+ DP(NETIF_MSG_LINK,
+ "Speed not supported: 0x%x\n", phy->req_line_speed);
+ return;
+ }
+
+ if (phy->req_duplex == DUPLEX_FULL)
+ val16 |= 0x0100;
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, val16);
+
+ DP(NETIF_MSG_LINK, "set SGMII force speed %d\n",
+ phy->req_line_speed);
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, &val16);
+ DP(NETIF_MSG_LINK, " (readback) %x\n", val16);
+ }
+
+ /* SGMII Slave mode and disable signal detect */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, &digctrl_kx1);
+ if (fiber_mode)
+ digctrl_kx1 = 1;
+ else
+ digctrl_kx1 &= 0xff4a;
+
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1,
+ digctrl_kx1);
+
+ /* Turn off parallel detect */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, &digctrl_kx2);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2,
+ (digctrl_kx2 & ~(1<<2)));
+
+ /* Re-enable parallel detect */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2,
+ (digctrl_kx2 | (1<<2)));
+
+ /* Enable autodet */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1,
+ (digctrl_kx1 | 0x10));
+}
+
+static void bnx2x_warpcore_reset_lane(struct bnx2x *bp,
+ struct bnx2x_phy *phy,
+ u8 reset)
+{
+ u16 val;
+ /* Take lane out of reset after configuration is finished */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL5_MISC6, &val);
+ if (reset)
+ val |= 0xC000;
+ else
+ val &= 0x3FFF;
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL5_MISC6, val);
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL5_MISC6, &val);
+}
+
+
+ /* Clear SFI/XFI link settings registers */
+static void bnx2x_warpcore_clear_regs(struct bnx2x_phy *phy,
+ struct link_params *params,
+ u16 lane)
+{
+ struct bnx2x *bp = params->bp;
+ u16 val16;
+
+ /* Set XFI clock comp as default. */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_CONTROL, &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_RX66_CONTROL, val16 | (3<<13));
+
+ bnx2x_warpcore_reset_lane(bp, phy, 1);
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_FX100_CTRL1, 0x014a);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_FX100_CTRL3, 0x0800);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL4_MISC3, 0x8008);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, 0x0195);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x0007);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, 0x0002);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_SERDESDIGITAL_MISC1, 0x6000);
+ lane = bnx2x_get_warpcore_lane(phy, params);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_TX_FIR_TAP, 0x0000);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_TX0_TX_DRIVER + 0x10*lane, 0x0990);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x2040);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, 0x0140);
+ bnx2x_warpcore_reset_lane(bp, phy, 0);
+}
+
+static int bnx2x_get_mod_abs_int_cfg(struct bnx2x *bp,
+ u32 chip_id,
+ u32 shmem_base, u8 port,
+ u8 *gpio_num, u8 *gpio_port)
+{
+ u32 cfg_pin;
+ *gpio_num = 0;
+ *gpio_port = 0;
+ if (CHIP_IS_E3(bp)) {
+ cfg_pin = (REG_RD(bp, shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[port].e3_sfp_ctrl)) &
+ PORT_HW_CFG_E3_MOD_ABS_MASK) >>
+ PORT_HW_CFG_E3_MOD_ABS_SHIFT;
+
+ /*
+ * Should not happen. This function called upon interrupt
+ * triggered by GPIO ( since EPIO can only generate interrupts
+ * to MCP).
+ * So if this function was called and none of the GPIOs was set,
+ * it means the shit hit the fan.
+ */
+ if ((cfg_pin < PIN_CFG_GPIO0_P0) ||
+ (cfg_pin > PIN_CFG_GPIO3_P1)) {
+ DP(NETIF_MSG_LINK,
+ "ERROR: Invalid cfg pin %x for module detect indication\n",
+ cfg_pin);
+ return -EINVAL;
+ }
+
+ *gpio_num = (cfg_pin - PIN_CFG_GPIO0_P0) & 0x3;
+ *gpio_port = (cfg_pin - PIN_CFG_GPIO0_P0) >> 2;
+ } else {
+ *gpio_num = MISC_REGISTERS_GPIO_3;
+ *gpio_port = port;
+ }
+ DP(NETIF_MSG_LINK, "MOD_ABS int GPIO%d_P%d\n", *gpio_num, *gpio_port);
+ return 0;
+}
+
+static int bnx2x_is_sfp_module_plugged(struct bnx2x_phy *phy,
+ struct link_params *params)
+{
+ struct bnx2x *bp = params->bp;
+ u8 gpio_num, gpio_port;
+ u32 gpio_val;
+ if (bnx2x_get_mod_abs_int_cfg(bp, params->chip_id,
+ params->shmem_base, params->port,
+ &gpio_num, &gpio_port) != 0)
+ return 0;
+ gpio_val = bnx2x_get_gpio(bp, gpio_num, gpio_port);
+
+ /* Call the handling function in case module is detected */
+ if (gpio_val == 0)
+ return 1;
+ else
+ return 0;
+}
+static int bnx2x_warpcore_get_sigdet(struct bnx2x_phy *phy,
+ struct link_params *params)
+{
+ u16 gp2_status_reg0, lane;
+ struct bnx2x *bp = params->bp;
+
+ lane = bnx2x_get_warpcore_lane(phy, params);
+
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_GP2_STATUS_GP_2_0,
+ &gp2_status_reg0);
+
+ return (gp2_status_reg0 >> (8+lane)) & 0x1;
+}
+
+static void bnx2x_warpcore_config_runtime(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u32 serdes_net_if;
+ u16 gp_status1 = 0, lnkup = 0, lnkup_kr = 0;
+ u16 lane = bnx2x_get_warpcore_lane(phy, params);
+
+ vars->turn_to_run_wc_rt = vars->turn_to_run_wc_rt ? 0 : 1;
+
+ if (!vars->turn_to_run_wc_rt)
+ return;
+
+ /* return if there is no link partner */
+ if (!(bnx2x_warpcore_get_sigdet(phy, params))) {
+ DP(NETIF_MSG_LINK, "bnx2x_warpcore_get_sigdet false\n");
+ return;
+ }
+
+ if (vars->rx_tx_asic_rst) {
+ serdes_net_if = (REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region, dev_info.
+ port_hw_config[params->port].default_cfg)) &
+ PORT_HW_CFG_NET_SERDES_IF_MASK);
+
+ switch (serdes_net_if) {
+ case PORT_HW_CFG_NET_SERDES_IF_KR:
+ /* Do we get link yet? */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, 0x81d1,
+ &gp_status1);
+ lnkup = (gp_status1 >> (8+lane)) & 0x1;/* 1G */
+ /*10G KR*/
+ lnkup_kr = (gp_status1 >> (12+lane)) & 0x1;
+
+ DP(NETIF_MSG_LINK,
+ "gp_status1 0x%x\n", gp_status1);
+
+ if (lnkup_kr || lnkup) {
+ vars->rx_tx_asic_rst = 0;
+ DP(NETIF_MSG_LINK,
+ "link up, rx_tx_asic_rst 0x%x\n",
+ vars->rx_tx_asic_rst);
+ } else {
+ /*reset the lane to see if link comes up.*/
+ bnx2x_warpcore_reset_lane(bp, phy, 1);
+ bnx2x_warpcore_reset_lane(bp, phy, 0);
+
+ /* restart Autoneg */
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x1200);
+
+ vars->rx_tx_asic_rst--;
+ DP(NETIF_MSG_LINK, "0x%x retry left\n",
+ vars->rx_tx_asic_rst);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ } /*params->rx_tx_asic_rst*/
+
+}
+
+static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u32 serdes_net_if;
+ u8 fiber_mode;
+ u16 lane = bnx2x_get_warpcore_lane(phy, params);
+ serdes_net_if = (REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region, dev_info.
+ port_hw_config[params->port].default_cfg)) &
+ PORT_HW_CFG_NET_SERDES_IF_MASK);
+ DP(NETIF_MSG_LINK, "Begin Warpcore init, link_speed %d, "
+ "serdes_net_if = 0x%x\n",
+ vars->line_speed, serdes_net_if);
+ bnx2x_set_aer_mmd(params, phy);
+
+ vars->phy_flags |= PHY_XGXS_FLAG;
+ if ((serdes_net_if == PORT_HW_CFG_NET_SERDES_IF_SGMII) ||
+ (phy->req_line_speed &&
+ ((phy->req_line_speed == SPEED_100) ||
+ (phy->req_line_speed == SPEED_10)))) {
+ vars->phy_flags |= PHY_SGMII_FLAG;
+ DP(NETIF_MSG_LINK, "Setting SGMII mode\n");
+ bnx2x_warpcore_clear_regs(phy, params, lane);
+ bnx2x_warpcore_set_sgmii_speed(phy, params, 0);
+ } else {
+ switch (serdes_net_if) {
+ case PORT_HW_CFG_NET_SERDES_IF_KR:
+ /* Enable KR Auto Neg */
+ if (params->loopback_mode == LOOPBACK_NONE)
+ bnx2x_warpcore_enable_AN_KR(phy, params, vars);
+ else {
+ DP(NETIF_MSG_LINK, "Setting KR 10G-Force\n");
+ bnx2x_warpcore_set_10G_KR(phy, params, vars);
+ }
+ break;
+
+ case PORT_HW_CFG_NET_SERDES_IF_XFI:
+ bnx2x_warpcore_clear_regs(phy, params, lane);
+ if (vars->line_speed == SPEED_10000) {
+ DP(NETIF_MSG_LINK, "Setting 10G XFI\n");
+ bnx2x_warpcore_set_10G_XFI(phy, params, 1);
+ } else {
+ if (SINGLE_MEDIA_DIRECT(params)) {
+ DP(NETIF_MSG_LINK, "1G Fiber\n");
+ fiber_mode = 1;
+ } else {
+ DP(NETIF_MSG_LINK, "10/100/1G SGMII\n");
+ fiber_mode = 0;
+ }
+ bnx2x_warpcore_set_sgmii_speed(phy,
+ params,
+ fiber_mode);
+ }
+
+ break;
+
+ case PORT_HW_CFG_NET_SERDES_IF_SFI:
+
+ bnx2x_warpcore_clear_regs(phy, params, lane);
+ if (vars->line_speed == SPEED_10000) {
+ DP(NETIF_MSG_LINK, "Setting 10G SFI\n");
+ bnx2x_warpcore_set_10G_XFI(phy, params, 0);
+ } else if (vars->line_speed == SPEED_1000) {
+ DP(NETIF_MSG_LINK, "Setting 1G Fiber\n");
+ bnx2x_warpcore_set_sgmii_speed(phy, params, 1);
+ }
+ /* Issue Module detection */
+ if (bnx2x_is_sfp_module_plugged(phy, params))
+ bnx2x_sfp_module_detection(phy, params);
+ break;
+
+ case PORT_HW_CFG_NET_SERDES_IF_DXGXS:
+ if (vars->line_speed != SPEED_20000) {
+ DP(NETIF_MSG_LINK, "Speed not supported yet\n");
+ return;
+ }
+ DP(NETIF_MSG_LINK, "Setting 20G DXGXS\n");
+ bnx2x_warpcore_set_20G_DXGXS(bp, phy, lane);
+ /* Issue Module detection */
+
+ bnx2x_sfp_module_detection(phy, params);
+ break;
+
+ case PORT_HW_CFG_NET_SERDES_IF_KR2:
+ if (vars->line_speed != SPEED_20000) {
+ DP(NETIF_MSG_LINK, "Speed not supported yet\n");
+ return;
+ }
+ DP(NETIF_MSG_LINK, "Setting 20G KR2\n");
+ bnx2x_warpcore_set_20G_KR2(bp, phy);
+ break;
+
+ default:
+ DP(NETIF_MSG_LINK,
+ "Unsupported Serdes Net Interface 0x%x\n",
+ serdes_net_if);
+ return;
+ }
+ }
+
+ /* Take lane out of reset after configuration is finished */
+ bnx2x_warpcore_reset_lane(bp, phy, 0);
+ DP(NETIF_MSG_LINK, "Exit config init\n");
+}
+
+static void bnx2x_sfp_e3_set_transmitter(struct link_params *params,
+ struct bnx2x_phy *phy,
+ u8 tx_en)
+{
+ struct bnx2x *bp = params->bp;
+ u32 cfg_pin;
+ u8 port = params->port;
+
+ cfg_pin = REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[port].e3_sfp_ctrl)) &
+ PORT_HW_CFG_TX_LASER_MASK;
+ /* Set the !tx_en since this pin is DISABLE_TX_LASER */
+ DP(NETIF_MSG_LINK, "Setting WC TX to %d\n", tx_en);
+ /* For 20G, the expected pin to be used is 3 pins after the current */
+
+ bnx2x_set_cfg_pin(bp, cfg_pin, tx_en ^ 1);
+ if (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_20G)
+ bnx2x_set_cfg_pin(bp, cfg_pin + 3, tx_en ^ 1);
+}
+
+static void bnx2x_warpcore_link_reset(struct bnx2x_phy *phy,
+ struct link_params *params)
+{
+ struct bnx2x *bp = params->bp;
+ u16 val16;
+ bnx2x_sfp_e3_set_transmitter(params, phy, 0);
+ bnx2x_set_mdio_clk(bp, params->chip_id, params->port);
+ bnx2x_set_aer_mmd(params, phy);
+ /* Global register */
+ bnx2x_warpcore_reset_lane(bp, phy, 1);
+
+ /* Clear loopback settings (if any) */
+ /* 10G & 20G */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, val16 &
+ 0xBFFF);
+
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, val16 & 0xfffe);
+
+ /* Update those 1-copy registers */
+ CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK,
+ MDIO_AER_BLOCK_AER_REG, 0);
+ /* Enable 1G MDIO (1-copy) */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_XGXSBLK0_XGXSCONTROL,
+ &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_XGXSBLK0_XGXSCONTROL,
+ val16 & ~0x10);
+
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_XGXSBLK1_LANECTRL2, &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_XGXSBLK1_LANECTRL2,
+ val16 & 0xff00);
+
+}
+
+static void bnx2x_set_warpcore_loopback(struct bnx2x_phy *phy,
+ struct link_params *params)
+{
+ struct bnx2x *bp = params->bp;
+ u16 val16;
+ u32 lane;
+ DP(NETIF_MSG_LINK, "Setting Warpcore loopback type %x, speed %d\n",
+ params->loopback_mode, phy->req_line_speed);
+
+ if (phy->req_line_speed < SPEED_10000) {
+ /* 10/100/1000 */
+
+ /* Update those 1-copy registers */
+ CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK,
+ MDIO_AER_BLOCK_AER_REG, 0);
+ /* Enable 1G MDIO (1-copy) */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_XGXSBLK0_XGXSCONTROL,
+ &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_XGXSBLK0_XGXSCONTROL,
+ val16 | 0x10);
+ /* Set 1G loopback based on lane (1-copy) */
+ lane = bnx2x_get_warpcore_lane(phy, params);
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_XGXSBLK1_LANECTRL2, &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_XGXSBLK1_LANECTRL2,
+ val16 | (1<<lane));
+
+ /* Switch back to 4-copy registers */
+ bnx2x_set_aer_mmd(params, phy);
+ /* Global loopback, not recommended. */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, val16 |
+ 0x4000);
+ } else {
+ /* 10G & 20G */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_COMBO_IEEE0_MIICTRL, val16 |
+ 0x4000);
+
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, &val16);
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, val16 | 0x1);
+ }
+}
+
void bnx2x_link_status_update(struct link_params *params,
struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
- u8 link_10g;
+ u8 link_10g_plus;
u8 port = params->port;
+ u32 sync_offset, media_types;
+ /* Update PHY configuration */
+ set_phy_vars(params, vars);
vars->link_status = REG_RD(bp, params->shmem_base +
offsetof(struct shmem_region,
port_mb[port].link_status));
vars->link_up = (vars->link_status & LINK_STATUS_LINK_UP);
+ vars->phy_flags = PHY_XGXS_FLAG;
+ if (vars->link_status & LINK_STATUS_PHYSICAL_LINK_FLAG)
+ vars->phy_flags |= PHY_PHYSICAL_LINK_FLAG;
if (vars->link_up) {
DP(NETIF_MSG_LINK, "phy link up\n");
@@ -1664,27 +4508,9 @@ void bnx2x_link_status_update(struct link_params *params,
case LINK_10GTFD:
vars->line_speed = SPEED_10000;
break;
-
- case LINK_12GTFD:
- vars->line_speed = SPEED_12000;
- break;
-
- case LINK_12_5GTFD:
- vars->line_speed = SPEED_12500;
- break;
-
- case LINK_13GTFD:
- vars->line_speed = SPEED_13000;
- break;
-
- case LINK_15GTFD:
- vars->line_speed = SPEED_15000;
- break;
-
- case LINK_16GTFD:
- vars->line_speed = SPEED_16000;
+ case LINK_20GTFD:
+ vars->line_speed = SPEED_20000;
break;
-
default:
break;
}
@@ -1705,19 +4531,24 @@ void bnx2x_link_status_update(struct link_params *params,
} else {
vars->phy_flags &= ~PHY_SGMII_FLAG;
}
-
+ if (vars->line_speed &&
+ USES_WARPCORE(bp) &&
+ (vars->line_speed == SPEED_1000))
+ vars->phy_flags |= PHY_SGMII_FLAG;
/* anything 10 and over uses the bmac */
- link_10g = ((vars->line_speed == SPEED_10000) ||
- (vars->line_speed == SPEED_12000) ||
- (vars->line_speed == SPEED_12500) ||
- (vars->line_speed == SPEED_13000) ||
- (vars->line_speed == SPEED_15000) ||
- (vars->line_speed == SPEED_16000));
- if (link_10g)
- vars->mac_type = MAC_TYPE_BMAC;
- else
- vars->mac_type = MAC_TYPE_EMAC;
+ link_10g_plus = (vars->line_speed >= SPEED_10000);
+ if (link_10g_plus) {
+ if (USES_WARPCORE(bp))
+ vars->mac_type = MAC_TYPE_XMAC;
+ else
+ vars->mac_type = MAC_TYPE_BMAC;
+ } else {
+ if (USES_WARPCORE(bp))
+ vars->mac_type = MAC_TYPE_UMAC;
+ else
+ vars->mac_type = MAC_TYPE_EMAC;
+ }
} else { /* link down */
DP(NETIF_MSG_LINK, "phy link down\n");
@@ -1729,10 +4560,44 @@ void bnx2x_link_status_update(struct link_params *params,
/* indicate no mac active */
vars->mac_type = MAC_TYPE_NONE;
+ if (vars->link_status & LINK_STATUS_PHYSICAL_LINK_FLAG)
+ vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG;
}
- DP(NETIF_MSG_LINK, "link_status 0x%x phy_link_up %x\n",
- vars->link_status, vars->phy_link_up);
+ /* Sync media type */
+ sync_offset = params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[port].media_type);
+ media_types = REG_RD(bp, sync_offset);
+
+ params->phy[INT_PHY].media_type =
+ (media_types & PORT_HW_CFG_MEDIA_TYPE_PHY0_MASK) >>
+ PORT_HW_CFG_MEDIA_TYPE_PHY0_SHIFT;
+ params->phy[EXT_PHY1].media_type =
+ (media_types & PORT_HW_CFG_MEDIA_TYPE_PHY1_MASK) >>
+ PORT_HW_CFG_MEDIA_TYPE_PHY1_SHIFT;
+ params->phy[EXT_PHY2].media_type =
+ (media_types & PORT_HW_CFG_MEDIA_TYPE_PHY2_MASK) >>
+ PORT_HW_CFG_MEDIA_TYPE_PHY2_SHIFT;
+ DP(NETIF_MSG_LINK, "media_types = 0x%x\n", media_types);
+
+ /* Sync AEU offset */
+ sync_offset = params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[port].aeu_int_mask);
+
+ vars->aeu_int_mask = REG_RD(bp, sync_offset);
+
+ /* Sync PFC status */
+ if (vars->link_status & LINK_STATUS_PFC_ENABLED)
+ params->feature_config_flags |=
+ FEATURE_CONFIG_PFC_ENABLED;
+ else
+ params->feature_config_flags &=
+ ~FEATURE_CONFIG_PFC_ENABLED;
+
+ DP(NETIF_MSG_LINK, "link_status 0x%x phy_link_up %x int_mask 0x%x\n",
+ vars->link_status, vars->phy_link_up, vars->aeu_int_mask);
DP(NETIF_MSG_LINK, "line_speed %x duplex %x flow_ctrl 0x%x\n",
vars->line_speed, vars->duplex, vars->flow_ctrl);
}
@@ -1759,9 +4624,9 @@ static void bnx2x_set_master_ln(struct link_params *params,
(new_master_ln | ser_lane));
}
-static u8 bnx2x_reset_unicore(struct link_params *params,
- struct bnx2x_phy *phy,
- u8 set_serdes)
+static int bnx2x_reset_unicore(struct link_params *params,
+ struct bnx2x_phy *phy,
+ u8 set_serdes)
{
struct bnx2x *bp = params->bp;
u16 mii_control;
@@ -2048,9 +4913,6 @@ static void bnx2x_program_serdes(struct bnx2x_phy *phy,
if (vars->line_speed == SPEED_10000)
reg_val |=
MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_10G_CX4;
- if (vars->line_speed == SPEED_13000)
- reg_val |=
- MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_13G;
}
CL22_WR_OVER_CL45(bp, phy,
@@ -2059,8 +4921,8 @@ static void bnx2x_program_serdes(struct bnx2x_phy *phy,
}
-static void bnx2x_set_brcm_cl37_advertisment(struct bnx2x_phy *phy,
- struct link_params *params)
+static void bnx2x_set_brcm_cl37_advertisement(struct bnx2x_phy *phy,
+ struct link_params *params)
{
struct bnx2x *bp = params->bp;
u16 val = 0;
@@ -2081,44 +4943,9 @@ static void bnx2x_set_brcm_cl37_advertisment(struct bnx2x_phy *phy,
MDIO_OVER_1G_UP3, 0x400);
}
-static void bnx2x_calc_ieee_aneg_adv(struct bnx2x_phy *phy,
- struct link_params *params, u16 *ieee_fc)
-{
- struct bnx2x *bp = params->bp;
- *ieee_fc = MDIO_COMBO_IEEE0_AUTO_NEG_ADV_FULL_DUPLEX;
- /*
- * Resolve pause mode and advertisement.
- * Please refer to Table 28B-3 of the 802.3ab-1999 spec
- */
-
- switch (phy->req_flow_ctrl) {
- case BNX2X_FLOW_CTRL_AUTO:
- if (params->req_fc_auto_adv == BNX2X_FLOW_CTRL_BOTH)
- *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH;
- else
- *ieee_fc |=
- MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC;
- break;
- case BNX2X_FLOW_CTRL_TX:
- *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC;
- break;
-
- case BNX2X_FLOW_CTRL_RX:
- case BNX2X_FLOW_CTRL_BOTH:
- *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH;
- break;
-
- case BNX2X_FLOW_CTRL_NONE:
- default:
- *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE;
- break;
- }
- DP(NETIF_MSG_LINK, "ieee_fc = 0x%x\n", *ieee_fc);
-}
-
-static void bnx2x_set_ieee_aneg_advertisment(struct bnx2x_phy *phy,
- struct link_params *params,
- u16 ieee_fc)
+static void bnx2x_set_ieee_aneg_advertisement(struct bnx2x_phy *phy,
+ struct link_params *params,
+ u16 ieee_fc)
{
struct bnx2x *bp = params->bp;
u16 val;
@@ -2252,35 +5079,8 @@ static void bnx2x_initialize_sgmii_process(struct bnx2x_phy *phy,
* link management
*/
-static void bnx2x_pause_resolve(struct link_vars *vars, u32 pause_result)
-{ /* LD LP */
- switch (pause_result) { /* ASYM P ASYM P */
- case 0xb: /* 1 0 1 1 */
- vars->flow_ctrl = BNX2X_FLOW_CTRL_TX;
- break;
-
- case 0xe: /* 1 1 1 0 */
- vars->flow_ctrl = BNX2X_FLOW_CTRL_RX;
- break;
-
- case 0x5: /* 0 1 0 1 */
- case 0x7: /* 0 1 1 1 */
- case 0xd: /* 1 1 0 1 */
- case 0xf: /* 1 1 1 1 */
- vars->flow_ctrl = BNX2X_FLOW_CTRL_BOTH;
- break;
-
- default:
- break;
- }
- if (pause_result & (1<<0))
- vars->link_status |= LINK_STATUS_LINK_PARTNER_SYMMETRIC_PAUSE;
- if (pause_result & (1<<1))
- vars->link_status |= LINK_STATUS_LINK_PARTNER_ASYMMETRIC_PAUSE;
-}
-
-static u8 bnx2x_direct_parallel_detect_used(struct bnx2x_phy *phy,
- struct link_params *params)
+static int bnx2x_direct_parallel_detect_used(struct bnx2x_phy *phy,
+ struct link_params *params)
{
struct bnx2x *bp = params->bp;
u16 pd_10g, status2_1000x;
@@ -2383,7 +5183,7 @@ static void bnx2x_check_fallback_to_cl37(struct bnx2x_phy *phy,
struct link_params *params)
{
struct bnx2x *bp = params->bp;
- u16 rx_status, ustat_val, cl37_fsm_recieved;
+ u16 rx_status, ustat_val, cl37_fsm_received;
DP(NETIF_MSG_LINK, "bnx2x_check_fallback_to_cl37\n");
/* Step 1: Make sure signal is detected */
CL22_RD_OVER_CL45(bp, phy,
@@ -2421,15 +5221,15 @@ static void bnx2x_check_fallback_to_cl37(struct bnx2x_phy *phy,
CL22_RD_OVER_CL45(bp, phy,
MDIO_REG_BANK_REMOTE_PHY,
MDIO_REMOTE_PHY_MISC_RX_STATUS,
- &cl37_fsm_recieved);
- if ((cl37_fsm_recieved &
+ &cl37_fsm_received);
+ if ((cl37_fsm_received &
(MDIO_REMOTE_PHY_MISC_RX_STATUS_CL37_FSM_RECEIVED_OVER1G_MSG |
MDIO_REMOTE_PHY_MISC_RX_STATUS_CL37_FSM_RECEIVED_BRCM_OUI_MSG)) !=
(MDIO_REMOTE_PHY_MISC_RX_STATUS_CL37_FSM_RECEIVED_OVER1G_MSG |
MDIO_REMOTE_PHY_MISC_RX_STATUS_CL37_FSM_RECEIVED_BRCM_OUI_MSG)) {
DP(NETIF_MSG_LINK, "No CL37 FSM were received. "
"misc_rx_status(0x8330) = 0x%x\n",
- cl37_fsm_recieved);
+ cl37_fsm_received);
return;
}
/*
@@ -2462,45 +5262,25 @@ static void bnx2x_xgxs_an_resolve(struct bnx2x_phy *phy,
vars->link_status |=
LINK_STATUS_PARALLEL_DETECTION_USED;
}
-
-static u8 bnx2x_link_settings_status(struct bnx2x_phy *phy,
+static int bnx2x_get_link_speed_duplex(struct bnx2x_phy *phy,
struct link_params *params,
- struct link_vars *vars)
+ struct link_vars *vars,
+ u16 is_link_up,
+ u16 speed_mask,
+ u16 is_duplex)
{
struct bnx2x *bp = params->bp;
- u16 new_line_speed, gp_status;
- u8 rc = 0;
-
- /* Read gp_status */
- CL22_RD_OVER_CL45(bp, phy,
- MDIO_REG_BANK_GP_STATUS,
- MDIO_GP_STATUS_TOP_AN_STATUS1,
- &gp_status);
-
if (phy->req_line_speed == SPEED_AUTO_NEG)
vars->link_status |= LINK_STATUS_AUTO_NEGOTIATE_ENABLED;
- if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) {
- DP(NETIF_MSG_LINK, "phy link up gp_status=0x%x\n",
- gp_status);
+ if (is_link_up) {
+ DP(NETIF_MSG_LINK, "phy link up\n");
vars->phy_link_up = 1;
vars->link_status |= LINK_STATUS_LINK_UP;
- if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_DUPLEX_STATUS)
- vars->duplex = DUPLEX_FULL;
- else
- vars->duplex = DUPLEX_HALF;
-
- if (SINGLE_MEDIA_DIRECT(params)) {
- bnx2x_flow_ctrl_resolve(phy, params, vars, gp_status);
- if (phy->req_line_speed == SPEED_AUTO_NEG)
- bnx2x_xgxs_an_resolve(phy, params, vars,
- gp_status);
- }
-
- switch (gp_status & GP_STATUS_SPEED_MASK) {
+ switch (speed_mask) {
case GP_STATUS_10M:
- new_line_speed = SPEED_10;
+ vars->line_speed = SPEED_10;
if (vars->duplex == DUPLEX_FULL)
vars->link_status |= LINK_10TFD;
else
@@ -2508,7 +5288,7 @@ static u8 bnx2x_link_settings_status(struct bnx2x_phy *phy,
break;
case GP_STATUS_100M:
- new_line_speed = SPEED_100;
+ vars->line_speed = SPEED_100;
if (vars->duplex == DUPLEX_FULL)
vars->link_status |= LINK_100TXFD;
else
@@ -2517,7 +5297,7 @@ static u8 bnx2x_link_settings_status(struct bnx2x_phy *phy,
case GP_STATUS_1G:
case GP_STATUS_1G_KX:
- new_line_speed = SPEED_1000;
+ vars->line_speed = SPEED_1000;
if (vars->duplex == DUPLEX_FULL)
vars->link_status |= LINK_1000TFD;
else
@@ -2525,7 +5305,7 @@ static u8 bnx2x_link_settings_status(struct bnx2x_phy *phy,
break;
case GP_STATUS_2_5G:
- new_line_speed = SPEED_2500;
+ vars->line_speed = SPEED_2500;
if (vars->duplex == DUPLEX_FULL)
vars->link_status |= LINK_2500TFD;
else
@@ -2536,50 +5316,28 @@ static u8 bnx2x_link_settings_status(struct bnx2x_phy *phy,
case GP_STATUS_6G:
DP(NETIF_MSG_LINK,
"link speed unsupported gp_status 0x%x\n",
- gp_status);
+ speed_mask);
return -EINVAL;
case GP_STATUS_10G_KX4:
case GP_STATUS_10G_HIG:
case GP_STATUS_10G_CX4:
- new_line_speed = SPEED_10000;
+ case GP_STATUS_10G_KR:
+ case GP_STATUS_10G_SFI:
+ case GP_STATUS_10G_XFI:
+ vars->line_speed = SPEED_10000;
vars->link_status |= LINK_10GTFD;
break;
-
- case GP_STATUS_12G_HIG:
- new_line_speed = SPEED_12000;
- vars->link_status |= LINK_12GTFD;
- break;
-
- case GP_STATUS_12_5G:
- new_line_speed = SPEED_12500;
- vars->link_status |= LINK_12_5GTFD;
+ case GP_STATUS_20G_DXGXS:
+ vars->line_speed = SPEED_20000;
+ vars->link_status |= LINK_20GTFD;
break;
-
- case GP_STATUS_13G:
- new_line_speed = SPEED_13000;
- vars->link_status |= LINK_13GTFD;
- break;
-
- case GP_STATUS_15G:
- new_line_speed = SPEED_15000;
- vars->link_status |= LINK_15GTFD;
- break;
-
- case GP_STATUS_16G:
- new_line_speed = SPEED_16000;
- vars->link_status |= LINK_16GTFD;
- break;
-
default:
DP(NETIF_MSG_LINK,
"link speed unsupported gp_status 0x%x\n",
- gp_status);
+ speed_mask);
return -EINVAL;
}
-
- vars->line_speed = new_line_speed;
-
} else { /* link_down */
DP(NETIF_MSG_LINK, "phy link down\n");
@@ -2588,7 +5346,47 @@ static u8 bnx2x_link_settings_status(struct bnx2x_phy *phy,
vars->duplex = DUPLEX_FULL;
vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
vars->mac_type = MAC_TYPE_NONE;
+ }
+ DP(NETIF_MSG_LINK, " phy_link_up %x line_speed %d\n",
+ vars->phy_link_up, vars->line_speed);
+ return 0;
+}
+
+static int bnx2x_link_settings_status(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+
+ u16 gp_status, duplex = DUPLEX_HALF, link_up = 0, speed_mask;
+ int rc = 0;
+
+ /* Read gp_status */
+ CL22_RD_OVER_CL45(bp, phy,
+ MDIO_REG_BANK_GP_STATUS,
+ MDIO_GP_STATUS_TOP_AN_STATUS1,
+ &gp_status);
+ if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_DUPLEX_STATUS)
+ duplex = DUPLEX_FULL;
+ if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS)
+ link_up = 1;
+ speed_mask = gp_status & GP_STATUS_SPEED_MASK;
+ DP(NETIF_MSG_LINK, "gp_status 0x%x, is_link_up %d, speed_mask 0x%x\n",
+ gp_status, link_up, speed_mask);
+ rc = bnx2x_get_link_speed_duplex(phy, params, vars, link_up, speed_mask,
+ duplex);
+ if (rc == -EINVAL)
+ return rc;
+
+ if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) {
+ if (SINGLE_MEDIA_DIRECT(params)) {
+ bnx2x_flow_ctrl_resolve(phy, params, vars, gp_status);
+ if (phy->req_line_speed == SPEED_AUTO_NEG)
+ bnx2x_xgxs_an_resolve(phy, params, vars,
+ gp_status);
+ }
+ } else { /* link_down */
if ((phy->req_line_speed == SPEED_AUTO_NEG) &&
SINGLE_MEDIA_DIRECT(params)) {
/* Check signal is detected */
@@ -2596,13 +5394,86 @@ static u8 bnx2x_link_settings_status(struct bnx2x_phy *phy,
}
}
- DP(NETIF_MSG_LINK, "gp_status 0x%x phy_link_up %x line_speed %x\n",
- gp_status, vars->phy_link_up, vars->line_speed);
DP(NETIF_MSG_LINK, "duplex %x flow_ctrl 0x%x link_status 0x%x\n",
vars->duplex, vars->flow_ctrl, vars->link_status);
return rc;
}
+static int bnx2x_warpcore_read_status(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
+{
+
+ struct bnx2x *bp = params->bp;
+
+ u8 lane;
+ u16 gp_status1, gp_speed, link_up, duplex = DUPLEX_FULL;
+ int rc = 0;
+ lane = bnx2x_get_warpcore_lane(phy, params);
+ /* Read gp_status */
+ if (phy->req_line_speed > SPEED_10000) {
+ u16 temp_link_up;
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ 1, &temp_link_up);
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ 1, &link_up);
+ DP(NETIF_MSG_LINK, "PCS RX link status = 0x%x-->0x%x\n",
+ temp_link_up, link_up);
+ link_up &= (1<<2);
+ if (link_up)
+ bnx2x_ext_phy_resolve_fc(phy, params, vars);
+ } else {
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_GP2_STATUS_GP_2_1, &gp_status1);
+ DP(NETIF_MSG_LINK, "0x81d1 = 0x%x\n", gp_status1);
+ /* Check for either KR or generic link up. */
+ gp_status1 = ((gp_status1 >> 8) & 0xf) |
+ ((gp_status1 >> 12) & 0xf);
+ link_up = gp_status1 & (1 << lane);
+ if (link_up && SINGLE_MEDIA_DIRECT(params)) {
+ u16 pd, gp_status4;
+ if (phy->req_line_speed == SPEED_AUTO_NEG) {
+ /* Check Autoneg complete */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_GP2_STATUS_GP_2_4,
+ &gp_status4);
+ if (gp_status4 & ((1<<12)<<lane))
+ vars->link_status |=
+ LINK_STATUS_AUTO_NEGOTIATE_COMPLETE;
+
+ /* Check parallel detect used */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_PAR_DET_10G_STATUS,
+ &pd);
+ if (pd & (1<<15))
+ vars->link_status |=
+ LINK_STATUS_PARALLEL_DETECTION_USED;
+ }
+ bnx2x_ext_phy_resolve_fc(phy, params, vars);
+ }
+ }
+
+ if (lane < 2) {
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_GP2_STATUS_GP_2_2, &gp_speed);
+ } else {
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_GP2_STATUS_GP_2_3, &gp_speed);
+ }
+ DP(NETIF_MSG_LINK, "lane %d gp_speed 0x%x\n", lane, gp_speed);
+
+ if ((lane & 1) == 0)
+ gp_speed <<= 8;
+ gp_speed &= 0x3f00;
+
+
+ rc = bnx2x_get_link_speed_duplex(phy, params, vars, link_up, gp_speed,
+ duplex);
+
+ DP(NETIF_MSG_LINK, "duplex %x flow_ctrl 0x%x link_status 0x%x\n",
+ vars->duplex, vars->flow_ctrl, vars->link_status);
+ return rc;
+}
static void bnx2x_set_gmii_tx_driver(struct link_params *params)
{
struct bnx2x *bp = params->bp;
@@ -2642,8 +5513,8 @@ static void bnx2x_set_gmii_tx_driver(struct link_params *params)
}
}
-static u8 bnx2x_emac_program(struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_emac_program(struct link_params *params,
+ struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
u8 port = params->port;
@@ -2713,9 +5584,9 @@ static void bnx2x_set_preemphasis(struct bnx2x_phy *phy,
}
}
-static void bnx2x_init_internal_phy(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
+static void bnx2x_xgxs_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
u8 enable_cl73 = (SINGLE_MEDIA_DIRECT(params) ||
@@ -2742,11 +5613,11 @@ static void bnx2x_init_internal_phy(struct bnx2x_phy *phy,
DP(NETIF_MSG_LINK, "not SGMII, AN\n");
/* AN enabled */
- bnx2x_set_brcm_cl37_advertisment(phy, params);
+ bnx2x_set_brcm_cl37_advertisement(phy, params);
/* program duplex & pause advertisement (for aneg) */
- bnx2x_set_ieee_aneg_advertisment(phy, params,
- vars->ieee_fc);
+ bnx2x_set_ieee_aneg_advertisement(phy, params,
+ vars->ieee_fc);
/* enable autoneg */
bnx2x_set_autoneg(phy, params, vars, enable_cl73);
@@ -2762,29 +5633,12 @@ static void bnx2x_init_internal_phy(struct bnx2x_phy *phy,
}
}
-static u8 bnx2x_init_serdes(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
-{
- u8 rc;
- vars->phy_flags |= PHY_SGMII_FLAG;
- bnx2x_calc_ieee_aneg_adv(phy, params, &vars->ieee_fc);
- bnx2x_set_aer_mmd_serdes(params->bp, phy);
- rc = bnx2x_reset_unicore(params, phy, 1);
- /* reset the SerDes and wait for reset bit return low */
- if (rc != 0)
- return rc;
- bnx2x_set_aer_mmd_serdes(params->bp, phy);
-
- return rc;
-}
-
-static u8 bnx2x_init_xgxs(struct bnx2x_phy *phy,
+static int bnx2x_prepare_xgxs(struct bnx2x_phy *phy,
struct link_params *params,
struct link_vars *vars)
{
- u8 rc;
- vars->phy_flags = PHY_XGXS_FLAG;
+ int rc;
+ vars->phy_flags |= PHY_XGXS_FLAG;
if ((phy->req_line_speed &&
((phy->req_line_speed == SPEED_100) ||
(phy->req_line_speed == SPEED_10))) ||
@@ -2792,26 +5646,28 @@ static u8 bnx2x_init_xgxs(struct bnx2x_phy *phy,
(phy->speed_cap_mask >=
PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL) &&
(phy->speed_cap_mask <
- PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)
- ))
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) ||
+ (phy->type == PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT_SD))
vars->phy_flags |= PHY_SGMII_FLAG;
else
vars->phy_flags &= ~PHY_SGMII_FLAG;
bnx2x_calc_ieee_aneg_adv(phy, params, &vars->ieee_fc);
- bnx2x_set_aer_mmd_xgxs(params, phy);
- bnx2x_set_master_ln(params, phy);
+ bnx2x_set_aer_mmd(params, phy);
+ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT)
+ bnx2x_set_master_ln(params, phy);
rc = bnx2x_reset_unicore(params, phy, 0);
/* reset the SerDes and wait for reset bit return low */
if (rc != 0)
return rc;
- bnx2x_set_aer_mmd_xgxs(params, phy);
-
+ bnx2x_set_aer_mmd(params, phy);
/* setting the masterLn_def again after the reset */
- bnx2x_set_master_ln(params, phy);
- bnx2x_set_swap_lanes(params, phy);
+ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) {
+ bnx2x_set_master_ln(params, phy);
+ bnx2x_set_swap_lanes(params, phy);
+ }
return rc;
}
@@ -2823,8 +5679,13 @@ static u16 bnx2x_wait_reset_complete(struct bnx2x *bp,
u16 cnt, ctrl;
/* Wait for soft reset to get cleared up to 1 sec */
for (cnt = 0; cnt < 1000; cnt++) {
- bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, &ctrl);
+ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE)
+ bnx2x_cl22_read(bp, phy,
+ MDIO_PMA_REG_CTRL, &ctrl);
+ else
+ bnx2x_cl45_read(bp, phy,
+ MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_CTRL, &ctrl);
if (!(ctrl & (1<<15)))
break;
msleep(1);
@@ -2845,7 +5706,11 @@ static void bnx2x_link_int_enable(struct link_params *params)
struct bnx2x *bp = params->bp;
/* Setting the status to report on link up for either XGXS or SerDes */
- if (params->switch_cfg == SWITCH_CFG_10G) {
+ if (CHIP_IS_E3(bp)) {
+ mask = NIG_MASK_XGXS0_LINK_STATUS;
+ if (!(SINGLE_MEDIA_DIRECT(params)))
+ mask |= NIG_MASK_MI_INT;
+ } else if (params->switch_cfg == SWITCH_CFG_10G) {
mask = (NIG_MASK_XGXS0_LINK10G |
NIG_MASK_XGXS0_LINK_STATUS);
DP(NETIF_MSG_LINK, "enabled XGXS interrupt\n");
@@ -2918,11 +5783,11 @@ static void bnx2x_rearm_latch_signal(struct bnx2x *bp, u8 port,
}
static void bnx2x_link_int_ack(struct link_params *params,
- struct link_vars *vars, u8 is_10g)
+ struct link_vars *vars, u8 is_10g_plus)
{
struct bnx2x *bp = params->bp;
u8 port = params->port;
-
+ u32 mask;
/*
* First reset all status we assume only one line will be
* change at a time
@@ -2932,47 +5797,34 @@ static void bnx2x_link_int_ack(struct link_params *params,
NIG_STATUS_XGXS0_LINK_STATUS |
NIG_STATUS_SERDES0_LINK_STATUS));
if (vars->phy_link_up) {
- if (is_10g) {
- /*
- * Disable the 10G link interrupt by writing 1 to the
- * status register
- */
- DP(NETIF_MSG_LINK, "10G XGXS phy link up\n");
- bnx2x_bits_en(bp,
- NIG_REG_STATUS_INTERRUPT_PORT0 + port*4,
- NIG_STATUS_XGXS0_LINK10G);
-
- } else if (params->switch_cfg == SWITCH_CFG_10G) {
- /*
- * Disable the link interrupt by writing 1 to the
- * relevant lane in the status register
- */
- u32 ser_lane = ((params->lane_config &
+ if (USES_WARPCORE(bp))
+ mask = NIG_STATUS_XGXS0_LINK_STATUS;
+ else {
+ if (is_10g_plus)
+ mask = NIG_STATUS_XGXS0_LINK10G;
+ else if (params->switch_cfg == SWITCH_CFG_10G) {
+ /*
+ * Disable the link interrupt by writing 1 to
+ * the relevant lane in the status register
+ */
+ u32 ser_lane =
+ ((params->lane_config &
PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >>
PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT);
-
- DP(NETIF_MSG_LINK, "%d speed XGXS phy link up\n",
- vars->line_speed);
- bnx2x_bits_en(bp,
- NIG_REG_STATUS_INTERRUPT_PORT0 + port*4,
- ((1 << ser_lane) <<
- NIG_STATUS_XGXS0_LINK_STATUS_SIZE));
-
- } else { /* SerDes */
- DP(NETIF_MSG_LINK, "SerDes phy link up\n");
- /*
- * Disable the link interrupt by writing 1 to the status
- * register
- */
- bnx2x_bits_en(bp,
- NIG_REG_STATUS_INTERRUPT_PORT0 + port*4,
- NIG_STATUS_SERDES0_LINK_STATUS);
+ mask = ((1 << ser_lane) <<
+ NIG_STATUS_XGXS0_LINK_STATUS_SIZE);
+ } else
+ mask = NIG_STATUS_SERDES0_LINK_STATUS;
}
-
+ DP(NETIF_MSG_LINK, "Ack link up interrupt with mask 0x%x\n",
+ mask);
+ bnx2x_bits_en(bp,
+ NIG_REG_STATUS_INTERRUPT_PORT0 + port*4,
+ mask);
}
}
-static u8 bnx2x_format_ver(u32 num, u8 *str, u16 *len)
+static int bnx2x_format_ver(u32 num, u8 *str, u16 *len)
{
u8 *str_ptr = str;
u32 mask = 0xf0000000;
@@ -3011,19 +5863,19 @@ static u8 bnx2x_format_ver(u32 num, u8 *str, u16 *len)
}
-static u8 bnx2x_null_format_ver(u32 spirom_ver, u8 *str, u16 *len)
+static int bnx2x_null_format_ver(u32 spirom_ver, u8 *str, u16 *len)
{
str[0] = '\0';
(*len)--;
return 0;
}
-u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded,
- u8 *version, u16 len)
+int bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded,
+ u8 *version, u16 len)
{
struct bnx2x *bp;
u32 spirom_ver = 0;
- u8 status = 0;
+ int status = 0;
u8 *ver_p = version;
u16 remain_len = len;
if (version == NULL || params == NULL)
@@ -3065,15 +5917,18 @@ static void bnx2x_set_xgxs_loopback(struct bnx2x_phy *phy,
struct bnx2x *bp = params->bp;
if (phy->req_line_speed != SPEED_1000) {
- u32 md_devad;
+ u32 md_devad = 0;
DP(NETIF_MSG_LINK, "XGXS 10G loopback enable\n");
- /* change the uni_phy_addr in the nig */
- md_devad = REG_RD(bp, (NIG_REG_XGXS0_CTRL_MD_DEVAD +
- port*0x18));
+ if (!CHIP_IS_E3(bp)) {
+ /* change the uni_phy_addr in the nig */
+ md_devad = REG_RD(bp, (NIG_REG_XGXS0_CTRL_MD_DEVAD +
+ port*0x18));
- REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18, 0x5);
+ REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18,
+ 0x5);
+ }
bnx2x_cl45_write(bp, phy,
5,
@@ -3088,10 +5943,13 @@ static void bnx2x_set_xgxs_loopback(struct bnx2x_phy *phy,
0x6041);
msleep(200);
/* set aer mmd back */
- bnx2x_set_aer_mmd_xgxs(params, phy);
+ bnx2x_set_aer_mmd(params, phy);
- /* and md_devad */
- REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18, md_devad);
+ if (!CHIP_IS_E3(bp)) {
+ /* and md_devad */
+ REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18,
+ md_devad);
+ }
} else {
u16 mii_ctrl;
DP(NETIF_MSG_LINK, "XGXS 1G loopback enable\n");
@@ -3107,12 +5965,13 @@ static void bnx2x_set_xgxs_loopback(struct bnx2x_phy *phy,
}
}
-u8 bnx2x_set_led(struct link_params *params,
- struct link_vars *vars, u8 mode, u32 speed)
+int bnx2x_set_led(struct link_params *params,
+ struct link_vars *vars, u8 mode, u32 speed)
{
u8 port = params->port;
u16 hw_led_mode = params->hw_led_mode;
- u8 rc = 0, phy_idx;
+ int rc = 0;
+ u8 phy_idx;
u32 tmp;
u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
struct bnx2x *bp = params->bp;
@@ -3135,7 +5994,13 @@ u8 bnx2x_set_led(struct link_params *params,
SHARED_HW_CFG_LED_MAC1);
tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
- EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp | EMAC_LED_OVERRIDE));
+ if (params->phy[EXT_PHY1].type ==
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE)
+ EMAC_WR(bp, EMAC_REG_EMAC_LED, tmp & 0xfff1);
+ else {
+ EMAC_WR(bp, EMAC_REG_EMAC_LED,
+ (tmp | EMAC_LED_OVERRIDE));
+ }
break;
case LED_MODE_OPER:
@@ -3146,8 +6011,10 @@ u8 bnx2x_set_led(struct link_params *params,
if (!vars->link_up)
break;
case LED_MODE_ON:
- if (params->phy[EXT_PHY1].type ==
- PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727 &&
+ if (((params->phy[EXT_PHY1].type ==
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727) ||
+ (params->phy[EXT_PHY1].type ==
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8722)) &&
CHIP_IS_E2(bp) && params->num_phys == 2) {
/*
* This is a work-around for E2+8727 Configurations
@@ -3160,27 +6027,59 @@ u8 bnx2x_set_led(struct link_params *params,
tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
EMAC_WR(bp, EMAC_REG_EMAC_LED,
(tmp | EMAC_LED_OVERRIDE));
- return rc;
+ /*
+ * return here without enabling traffic
+ * LED blink and setting rate in ON mode.
+ * In oper mode, enabling LED blink
+ * and setting rate is needed.
+ */
+ if (mode == LED_MODE_ON)
+ return rc;
}
} else if (SINGLE_MEDIA_DIRECT(params)) {
/*
* This is a work-around for HW issue found when link
* is up in CL73
*/
+ if ((!CHIP_IS_E3(bp)) ||
+ (CHIP_IS_E3(bp) &&
+ mode == LED_MODE_ON))
+ REG_WR(bp, NIG_REG_LED_10G_P0 + port*4, 1);
+
+ if (CHIP_IS_E1x(bp) ||
+ CHIP_IS_E2(bp) ||
+ (mode == LED_MODE_ON))
+ REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, 0);
+ else
+ REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4,
+ hw_led_mode);
+ } else if ((params->phy[EXT_PHY1].type ==
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE) &&
+ (mode != LED_MODE_OPER)) {
REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, 0);
- REG_WR(bp, NIG_REG_LED_10G_P0 + port*4, 1);
- } else {
- REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, hw_led_mode);
- }
+ tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
+ EMAC_WR(bp, EMAC_REG_EMAC_LED, tmp | 0x3);
+ } else
+ REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4,
+ hw_led_mode);
REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0);
/* Set blinking rate to ~15.9Hz */
- REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_P0 + port*4,
- LED_BLINK_RATE_VAL);
+ if (CHIP_IS_E3(bp))
+ REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_P0 + port*4,
+ LED_BLINK_RATE_VAL_E3);
+ else
+ REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_P0 + port*4,
+ LED_BLINK_RATE_VAL_E1X_E2);
REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_ENA_P0 +
port*4, 1);
- tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
- EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp & (~EMAC_LED_OVERRIDE)));
+ if ((params->phy[EXT_PHY1].type !=
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE) &&
+ (mode != LED_MODE_OPER)) {
+ tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
+ EMAC_WR(bp, EMAC_REG_EMAC_LED,
+ (tmp & (~EMAC_LED_OVERRIDE)));
+ }
if (CHIP_IS_E1(bp) &&
((speed == SPEED_2500) ||
@@ -3214,21 +6113,49 @@ u8 bnx2x_set_led(struct link_params *params,
* This function comes to reflect the actual link state read DIRECTLY from the
* HW
*/
-u8 bnx2x_test_link(struct link_params *params, struct link_vars *vars,
- u8 is_serdes)
+int bnx2x_test_link(struct link_params *params, struct link_vars *vars,
+ u8 is_serdes)
{
struct bnx2x *bp = params->bp;
u16 gp_status = 0, phy_index = 0;
u8 ext_phy_link_up = 0, serdes_phy_type;
struct link_vars temp_vars;
-
- CL22_RD_OVER_CL45(bp, &params->phy[INT_PHY],
+ struct bnx2x_phy *int_phy = &params->phy[INT_PHY];
+
+ if (CHIP_IS_E3(bp)) {
+ u16 link_up;
+ if (params->req_line_speed[LINK_CONFIG_IDX(INT_PHY)]
+ > SPEED_10000) {
+ /* Check 20G link */
+ bnx2x_cl45_read(bp, int_phy, MDIO_WC_DEVAD,
+ 1, &link_up);
+ bnx2x_cl45_read(bp, int_phy, MDIO_WC_DEVAD,
+ 1, &link_up);
+ link_up &= (1<<2);
+ } else {
+ /* Check 10G link and below*/
+ u8 lane = bnx2x_get_warpcore_lane(int_phy, params);
+ bnx2x_cl45_read(bp, int_phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_GP2_STATUS_GP_2_1,
+ &gp_status);
+ gp_status = ((gp_status >> 8) & 0xf) |
+ ((gp_status >> 12) & 0xf);
+ link_up = gp_status & (1 << lane);
+ }
+ if (!link_up)
+ return -ESRCH;
+ } else {
+ CL22_RD_OVER_CL45(bp, int_phy,
MDIO_REG_BANK_GP_STATUS,
MDIO_GP_STATUS_TOP_AN_STATUS1,
&gp_status);
/* link is up only if both local phy and external phy are up */
if (!(gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS))
return -ESRCH;
+ }
+ /* In XGXS loopback mode, do not check external PHY */
+ if (params->loopback_mode == LOOPBACK_XGXS)
+ return 0;
switch (params->num_phys) {
case 1:
@@ -3245,7 +6172,9 @@ u8 bnx2x_test_link(struct link_params *params, struct link_vars *vars,
serdes_phy_type = ((params->phy[phy_index].media_type ==
ETH_PHY_SFP_FIBER) ||
(params->phy[phy_index].media_type ==
- ETH_PHY_XFP_FIBER));
+ ETH_PHY_XFP_FIBER) ||
+ (params->phy[phy_index].media_type ==
+ ETH_PHY_DA_TWINAX));
if (is_serdes != serdes_phy_type)
continue;
@@ -3263,10 +6192,10 @@ u8 bnx2x_test_link(struct link_params *params, struct link_vars *vars,
return -ESRCH;
}
-static u8 bnx2x_link_initialize(struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_link_initialize(struct link_params *params,
+ struct link_vars *vars)
{
- u8 rc = 0;
+ int rc = 0;
u8 phy_index, non_ext_phy;
struct bnx2x *bp = params->bp;
/*
@@ -3282,12 +6211,8 @@ static u8 bnx2x_link_initialize(struct link_params *params,
* (no external phys), or this board has external phy which requires
* to first.
*/
-
- if (params->phy[INT_PHY].config_init)
- params->phy[INT_PHY].config_init(
- &params->phy[INT_PHY],
- params, vars);
-
+ if (!USES_WARPCORE(bp))
+ bnx2x_prepare_xgxs(&params->phy[INT_PHY], params, vars);
/* init ext phy and enable link state int */
non_ext_phy = (SINGLE_MEDIA_DIRECT(params) ||
(params->loopback_mode == LOOPBACK_XGXS));
@@ -3296,13 +6221,22 @@ static u8 bnx2x_link_initialize(struct link_params *params,
(params->phy[EXT_PHY1].flags & FLAGS_INIT_XGXS_FIRST) ||
(params->loopback_mode == LOOPBACK_EXT_PHY)) {
struct bnx2x_phy *phy = &params->phy[INT_PHY];
- if (vars->line_speed == SPEED_AUTO_NEG)
+ if (vars->line_speed == SPEED_AUTO_NEG &&
+ (CHIP_IS_E1x(bp) ||
+ CHIP_IS_E2(bp)))
bnx2x_set_parallel_detection(phy, params);
- bnx2x_init_internal_phy(phy, params, vars);
+ if (params->phy[INT_PHY].config_init)
+ params->phy[INT_PHY].config_init(phy,
+ params,
+ vars);
}
/* Init external phy*/
- if (!non_ext_phy)
+ if (non_ext_phy) {
+ if (params->phy[INT_PHY].supported &
+ SUPPORTED_FIBRE)
+ vars->link_status |= LINK_STATUS_SERDES_LINK;
+ } else {
for (phy_index = EXT_PHY1; phy_index < params->num_phys;
phy_index++) {
/*
@@ -3311,17 +6245,22 @@ static u8 bnx2x_link_initialize(struct link_params *params,
* need to initialize the first phy, since they are
* connected.
*/
+ if (params->phy[phy_index].supported &
+ SUPPORTED_FIBRE)
+ vars->link_status |= LINK_STATUS_SERDES_LINK;
+
if (phy_index == EXT_PHY2 &&
(bnx2x_phy_selection(params) ==
PORT_HW_CFG_PHY_SELECTION_FIRST_PHY)) {
- DP(NETIF_MSG_LINK, "Ignoring second phy\n");
+ DP(NETIF_MSG_LINK,
+ "Not initializing second phy\n");
continue;
}
params->phy[phy_index].config_init(
&params->phy[phy_index],
params, vars);
}
-
+ }
/* Reset the interrupt indication after phy was initialized */
bnx2x_bits_dis(bp, NIG_REG_STATUS_INTERRUPT_PORT0 +
params->port*4,
@@ -3329,6 +6268,7 @@ static u8 bnx2x_link_initialize(struct link_params *params,
NIG_STATUS_XGXS0_LINK_STATUS |
NIG_STATUS_SERDES0_LINK_STATUS |
NIG_MASK_MI_INT));
+ bnx2x_update_mng(params, vars->link_status);
return rc;
}
@@ -3359,20 +6299,26 @@ static void bnx2x_common_ext_link_reset(struct bnx2x_phy *phy,
DP(NETIF_MSG_LINK, "reset external PHY\n");
}
-static u8 bnx2x_update_link_down(struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_update_link_down(struct link_params *params,
+ struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
u8 port = params->port;
DP(NETIF_MSG_LINK, "Port %x: Link is down\n", port);
bnx2x_set_led(params, vars, LED_MODE_OFF, 0);
-
+ vars->phy_flags &= ~PHY_PHYSICAL_LINK_FLAG;
/* indicate no mac active */
vars->mac_type = MAC_TYPE_NONE;
/* update shared memory */
- vars->link_status = 0;
+ vars->link_status &= ~(LINK_STATUS_SPEED_AND_DUPLEX_MASK |
+ LINK_STATUS_LINK_UP |
+ LINK_STATUS_PHYSICAL_LINK_FLAG |
+ LINK_STATUS_AUTO_NEGOTIATE_COMPLETE |
+ LINK_STATUS_RX_FLOW_CONTROL_FLAG_MASK |
+ LINK_STATUS_TX_FLOW_CONTROL_FLAG_MASK |
+ LINK_STATUS_PARALLEL_DETECTION_FLAG_MASK);
vars->line_speed = 0;
bnx2x_update_mng(params, vars->link_status);
@@ -3380,26 +6326,37 @@ static u8 bnx2x_update_link_down(struct link_params *params,
REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 1);
/* disable emac */
- REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0);
+ if (!CHIP_IS_E3(bp))
+ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0);
msleep(10);
-
- /* reset BigMac */
- bnx2x_bmac_rx_disable(bp, params->port);
- REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
+ /* reset BigMac/Xmac */
+ if (CHIP_IS_E1x(bp) ||
+ CHIP_IS_E2(bp)) {
+ bnx2x_bmac_rx_disable(bp, params->port);
+ REG_WR(bp, GRCBASE_MISC +
+ MISC_REGISTERS_RESET_REG_2_CLEAR,
(MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
+ }
+ if (CHIP_IS_E3(bp)) {
+ bnx2x_xmac_disable(params);
+ bnx2x_umac_disable(params);
+ }
+
return 0;
}
-static u8 bnx2x_update_link_up(struct link_params *params,
- struct link_vars *vars,
- u8 link_10g)
+static int bnx2x_update_link_up(struct link_params *params,
+ struct link_vars *vars,
+ u8 link_10g)
{
struct bnx2x *bp = params->bp;
u8 port = params->port;
- u8 rc = 0;
+ int rc = 0;
- vars->link_status |= LINK_STATUS_LINK_UP;
+ vars->link_status |= (LINK_STATUS_LINK_UP |
+ LINK_STATUS_PHYSICAL_LINK_FLAG);
+ vars->phy_flags |= PHY_PHYSICAL_LINK_FLAG;
if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)
vars->link_status |=
@@ -3408,25 +6365,48 @@ static u8 bnx2x_update_link_up(struct link_params *params,
if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX)
vars->link_status |=
LINK_STATUS_RX_FLOW_CONTROL_ENABLED;
-
- if (link_10g) {
- bnx2x_bmac_enable(params, vars, 0);
+ if (USES_WARPCORE(bp)) {
+ if (link_10g) {
+ if (bnx2x_xmac_enable(params, vars, 0) ==
+ -ESRCH) {
+ DP(NETIF_MSG_LINK, "Found errors on XMAC\n");
+ vars->link_up = 0;
+ vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG;
+ vars->link_status &= ~LINK_STATUS_LINK_UP;
+ }
+ } else
+ bnx2x_umac_enable(params, vars, 0);
bnx2x_set_led(params, vars,
- LED_MODE_OPER, SPEED_10000);
- } else {
- rc = bnx2x_emac_program(params, vars);
+ LED_MODE_OPER, vars->line_speed);
+ }
+ if ((CHIP_IS_E1x(bp) ||
+ CHIP_IS_E2(bp))) {
+ if (link_10g) {
+ if (bnx2x_bmac_enable(params, vars, 0) ==
+ -ESRCH) {
+ DP(NETIF_MSG_LINK, "Found errors on BMAC\n");
+ vars->link_up = 0;
+ vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG;
+ vars->link_status &= ~LINK_STATUS_LINK_UP;
+ }
- bnx2x_emac_enable(params, vars, 0);
+ bnx2x_set_led(params, vars,
+ LED_MODE_OPER, SPEED_10000);
+ } else {
+ rc = bnx2x_emac_program(params, vars);
+ bnx2x_emac_enable(params, vars, 0);
- /* AN complete? */
- if ((vars->link_status & LINK_STATUS_AUTO_NEGOTIATE_COMPLETE)
- && (!(vars->phy_flags & PHY_SGMII_FLAG)) &&
- SINGLE_MEDIA_DIRECT(params))
- bnx2x_set_gmii_tx_driver(params);
+ /* AN complete? */
+ if ((vars->link_status &
+ LINK_STATUS_AUTO_NEGOTIATE_COMPLETE)
+ && (!(vars->phy_flags & PHY_SGMII_FLAG)) &&
+ SINGLE_MEDIA_DIRECT(params))
+ bnx2x_set_gmii_tx_driver(params);
+ }
}
/* PBF - link up */
- if (!(CHIP_IS_E2(bp)))
+ if (CHIP_IS_E1x(bp))
rc |= bnx2x_pbf_update(params, vars->flow_ctrl,
vars->line_speed);
@@ -3451,17 +6431,18 @@ static u8 bnx2x_update_link_up(struct link_params *params,
* external phy needs to be up, and at least one of the 2
* external phy link must be up.
*/
-u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars)
+int bnx2x_link_update(struct link_params *params, struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
struct link_vars phy_vars[MAX_PHYS];
u8 port = params->port;
- u8 link_10g, phy_index;
- u8 ext_phy_link_up = 0, cur_link_up, rc = 0;
+ u8 link_10g_plus, phy_index;
+ u8 ext_phy_link_up = 0, cur_link_up;
+ int rc = 0;
u8 is_mi_int = 0;
u16 ext_phy_line_speed = 0, prev_line_speed = vars->line_speed;
u8 active_external_phy = INT_PHY;
- vars->link_status = 0;
+ vars->phy_flags &= ~PHY_HALF_OPEN_CONN_FLAG;
for (phy_index = INT_PHY; phy_index < params->num_phys;
phy_index++) {
phy_vars[phy_index].flow_ctrl = 0;
@@ -3470,8 +6451,12 @@ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars)
phy_vars[phy_index].duplex = DUPLEX_FULL;
phy_vars[phy_index].phy_link_up = 0;
phy_vars[phy_index].link_up = 0;
+ phy_vars[phy_index].fault_detected = 0;
}
+ if (USES_WARPCORE(bp))
+ bnx2x_set_aer_mmd(params, &params->phy[INT_PHY]);
+
DP(NETIF_MSG_LINK, "port %x, XGXS?%x, int_status 0x%x\n",
port, (vars->phy_flags & PHY_XGXS_FLAG),
REG_RD(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4));
@@ -3488,13 +6473,14 @@ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars)
REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK_STATUS + port*0x68));
/* disable emac */
- REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0);
+ if (!CHIP_IS_E3(bp))
+ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0);
/*
* Step 1:
* Check external link change only for external phys, and apply
* priority selection between them in case the link on both phys
- * is up. Note that the instead of the common vars, a temporary
+ * is up. Note that instead of the common vars, a temporary
* vars argument is used since each phy may have different link/
* speed/duplex result
*/
@@ -3588,8 +6574,8 @@ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars)
*/
if (active_external_phy == EXT_PHY1) {
if (params->phy[EXT_PHY2].phy_specific_func) {
- DP(NETIF_MSG_LINK, "Disabling TX on"
- " EXT_PHY2\n");
+ DP(NETIF_MSG_LINK,
+ "Disabling TX on EXT_PHY2\n");
params->phy[EXT_PHY2].phy_specific_func(
&params->phy[EXT_PHY2],
params, DISABLE_TX);
@@ -3601,6 +6587,8 @@ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars)
if (params->phy[active_external_phy].supported &
SUPPORTED_FIBRE)
vars->link_status |= LINK_STATUS_SERDES_LINK;
+ else
+ vars->link_status &= ~LINK_STATUS_SERDES_LINK;
DP(NETIF_MSG_LINK, "Active external phy selected: %x\n",
active_external_phy);
}
@@ -3640,14 +6628,9 @@ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars)
}
/* anything 10 and over uses the bmac */
- link_10g = ((vars->line_speed == SPEED_10000) ||
- (vars->line_speed == SPEED_12000) ||
- (vars->line_speed == SPEED_12500) ||
- (vars->line_speed == SPEED_13000) ||
- (vars->line_speed == SPEED_15000) ||
- (vars->line_speed == SPEED_16000));
+ link_10g_plus = (vars->line_speed >= SPEED_10000);
- bnx2x_link_int_ack(params, vars, link_10g);
+ bnx2x_link_int_ack(params, vars, link_10g_plus);
/*
* In case external phy link is up, and internal link is down
@@ -3671,21 +6654,24 @@ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars)
vars->phy_flags |= PHY_SGMII_FLAG;
else
vars->phy_flags &= ~PHY_SGMII_FLAG;
- bnx2x_init_internal_phy(&params->phy[INT_PHY],
- params,
+
+ if (params->phy[INT_PHY].config_init)
+ params->phy[INT_PHY].config_init(
+ &params->phy[INT_PHY], params,
vars);
}
}
/*
* Link is up only if both local phy and external phy (in case of
- * non-direct board) are up
+ * non-direct board) are up and no fault detected on active PHY.
*/
vars->link_up = (vars->phy_link_up &&
(ext_phy_link_up ||
- SINGLE_MEDIA_DIRECT(params)));
+ SINGLE_MEDIA_DIRECT(params)) &&
+ (phy_vars[active_external_phy].fault_detected == 0));
if (vars->link_up)
- rc = bnx2x_update_link_up(params, vars, link_10g);
+ rc = bnx2x_update_link_up(params, vars, link_10g_plus);
else
rc = bnx2x_update_link_down(params, vars);
@@ -3729,69 +6715,6 @@ static void bnx2x_save_bcm_spirom_ver(struct bnx2x *bp,
phy->ver_addr);
}
-static void bnx2x_ext_phy_set_pause(struct link_params *params,
- struct bnx2x_phy *phy,
- struct link_vars *vars)
-{
- u16 val;
- struct bnx2x *bp = params->bp;
- /* read modify write pause advertizing */
- bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, &val);
-
- val &= ~MDIO_AN_REG_ADV_PAUSE_BOTH;
-
- /* Please refer to Table 28B-3 of 802.3ab-1999 spec. */
- bnx2x_calc_ieee_aneg_adv(phy, params, &vars->ieee_fc);
- if ((vars->ieee_fc &
- MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) ==
- MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) {
- val |= MDIO_AN_REG_ADV_PAUSE_ASYMMETRIC;
- }
- if ((vars->ieee_fc &
- MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) ==
- MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) {
- val |= MDIO_AN_REG_ADV_PAUSE_PAUSE;
- }
- DP(NETIF_MSG_LINK, "Ext phy AN advertize 0x%x\n", val);
- bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, val);
-}
-
-static u8 bnx2x_ext_phy_resolve_fc(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
-{
- struct bnx2x *bp = params->bp;
- u16 ld_pause; /* local */
- u16 lp_pause; /* link partner */
- u16 pause_result;
- u8 ret = 0;
- /* read twice */
-
- vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
-
- if (phy->req_flow_ctrl != BNX2X_FLOW_CTRL_AUTO)
- vars->flow_ctrl = phy->req_flow_ctrl;
- else if (phy->req_line_speed != SPEED_AUTO_NEG)
- vars->flow_ctrl = params->req_fc_auto_adv;
- else if (vars->link_status & LINK_STATUS_AUTO_NEGOTIATE_COMPLETE) {
- ret = 1;
- bnx2x_cl45_read(bp, phy,
- MDIO_AN_DEVAD,
- MDIO_AN_REG_ADV_PAUSE, &ld_pause);
- bnx2x_cl45_read(bp, phy,
- MDIO_AN_DEVAD,
- MDIO_AN_REG_LP_AUTO_NEG, &lp_pause);
- pause_result = (ld_pause &
- MDIO_AN_REG_ADV_PAUSE_MASK) >> 8;
- pause_result |= (lp_pause &
- MDIO_AN_REG_ADV_PAUSE_MASK) >> 10;
- DP(NETIF_MSG_LINK, "Ext PHY pause result 0x%x\n",
- pause_result);
- bnx2x_pause_resolve(vars, pause_result);
- }
- return ret;
-}
-
static void bnx2x_ext_phy_10G_an_resolve(struct bnx2x *bp,
struct bnx2x_phy *phy,
struct link_vars *vars)
@@ -3845,13 +6768,13 @@ static void bnx2x_8073_resolve_fc(struct bnx2x_phy *phy,
pause_result);
}
}
-static u8 bnx2x_8073_8727_external_rom_boot(struct bnx2x *bp,
- struct bnx2x_phy *phy,
- u8 port)
+static int bnx2x_8073_8727_external_rom_boot(struct bnx2x *bp,
+ struct bnx2x_phy *phy,
+ u8 port)
{
u32 count = 0;
u16 fw_ver1, fw_msgout;
- u8 rc = 0;
+ int rc = 0;
/* Boot port from external ROM */
/* EDC grst */
@@ -3926,7 +6849,7 @@ static u8 bnx2x_8073_8727_external_rom_boot(struct bnx2x *bp,
/******************************************************************/
/* BCM8073 PHY SECTION */
/******************************************************************/
-static u8 bnx2x_8073_is_snr_needed(struct bnx2x *bp, struct bnx2x_phy *phy)
+static int bnx2x_8073_is_snr_needed(struct bnx2x *bp, struct bnx2x_phy *phy)
{
/* This is only required for 8073A1, version 102 only */
u16 val;
@@ -3952,7 +6875,7 @@ static u8 bnx2x_8073_is_snr_needed(struct bnx2x *bp, struct bnx2x_phy *phy)
return 1;
}
-static u8 bnx2x_8073_xaui_wa(struct bnx2x *bp, struct bnx2x_phy *phy)
+static int bnx2x_8073_xaui_wa(struct bnx2x *bp, struct bnx2x_phy *phy)
{
u16 val, cnt, cnt1 ;
@@ -4059,9 +6982,9 @@ static void bnx2x_8073_set_pause_cl37(struct link_params *params,
msleep(500);
}
-static u8 bnx2x_8073_config_init(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_8073_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
u16 val = 0, tmp1;
@@ -4081,9 +7004,9 @@ static u8 bnx2x_8073_config_init(struct bnx2x_phy *phy,
/* enable LASI */
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, (1<<2));
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXCTRL, (1<<2));
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x0004);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_CTRL, 0x0004);
bnx2x_8073_set_pause_cl37(params, phy, vars);
@@ -4091,7 +7014,7 @@ static u8 bnx2x_8073_config_init(struct bnx2x_phy *phy,
MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &tmp1);
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &tmp1);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXSTAT, &tmp1);
DP(NETIF_MSG_LINK, "Before rom RX_ALARM(port1): 0x%x\n", tmp1);
@@ -4225,7 +7148,7 @@ static u8 bnx2x_8073_read_status(struct bnx2x_phy *phy,
u16 an1000_status = 0;
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_STAT, &val1);
DP(NETIF_MSG_LINK, "8703 LASI status 0x%x\n", val1);
@@ -4241,7 +7164,7 @@ static u8 bnx2x_8073_read_status(struct bnx2x_phy *phy,
/* Check the LASI */
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &val2);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXSTAT, &val2);
DP(NETIF_MSG_LINK, "KR 0x9003 0x%x\n", val2);
@@ -4367,9 +7290,9 @@ static void bnx2x_8073_link_reset(struct bnx2x_phy *phy,
/******************************************************************/
/* BCM8705 PHY SECTION */
/******************************************************************/
-static u8 bnx2x_8705_config_init(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_8705_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
DP(NETIF_MSG_LINK, "init 8705\n");
@@ -4430,6 +7353,30 @@ static u8 bnx2x_8705_read_status(struct bnx2x_phy *phy,
/******************************************************************/
/* SFP+ module Section */
/******************************************************************/
+static void bnx2x_set_disable_pmd_transmit(struct link_params *params,
+ struct bnx2x_phy *phy,
+ u8 pmd_dis)
+{
+ struct bnx2x *bp = params->bp;
+ /*
+ * Disable transmitter only for bootcodes which can enable it afterwards
+ * (for D3 link)
+ */
+ if (pmd_dis) {
+ if (params->feature_config_flags &
+ FEATURE_CONFIG_BC_SUPPORTS_SFP_TX_DISABLED)
+ DP(NETIF_MSG_LINK, "Disabling PMD transmitter\n");
+ else {
+ DP(NETIF_MSG_LINK, "NOT disabling PMD transmitter\n");
+ return;
+ }
+ } else
+ DP(NETIF_MSG_LINK, "Enabling PMD transmitter\n");
+ bnx2x_cl45_write(bp, phy,
+ MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_TX_DISABLE, pmd_dis);
+}
+
static u8 bnx2x_get_gpio_port(struct link_params *params)
{
u8 gpio_port;
@@ -4443,9 +7390,10 @@ static u8 bnx2x_get_gpio_port(struct link_params *params)
swap_override = REG_RD(bp, NIG_REG_STRAP_OVERRIDE);
return gpio_port ^ (swap_val && swap_override);
}
-static void bnx2x_sfp_set_transmitter(struct link_params *params,
- struct bnx2x_phy *phy,
- u8 tx_en)
+
+static void bnx2x_sfp_e1e2_set_transmitter(struct link_params *params,
+ struct bnx2x_phy *phy,
+ u8 tx_en)
{
u16 val;
u8 port = params->port;
@@ -4500,16 +7448,28 @@ static void bnx2x_sfp_set_transmitter(struct link_params *params,
}
}
-static u8 bnx2x_8726_read_sfp_module_eeprom(struct bnx2x_phy *phy,
- struct link_params *params,
- u16 addr, u8 byte_cnt, u8 *o_buf)
+static void bnx2x_sfp_set_transmitter(struct link_params *params,
+ struct bnx2x_phy *phy,
+ u8 tx_en)
+{
+ struct bnx2x *bp = params->bp;
+ DP(NETIF_MSG_LINK, "Setting SFP+ transmitter to %d\n", tx_en);
+ if (CHIP_IS_E3(bp))
+ bnx2x_sfp_e3_set_transmitter(params, phy, tx_en);
+ else
+ bnx2x_sfp_e1e2_set_transmitter(params, phy, tx_en);
+}
+
+static int bnx2x_8726_read_sfp_module_eeprom(struct bnx2x_phy *phy,
+ struct link_params *params,
+ u16 addr, u8 byte_cnt, u8 *o_buf)
{
struct bnx2x *bp = params->bp;
u16 val = 0;
u16 i;
if (byte_cnt > 16) {
- DP(NETIF_MSG_LINK, "Reading from eeprom is"
- " is limited to 0xf\n");
+ DP(NETIF_MSG_LINK,
+ "Reading from eeprom is limited to 0xf\n");
return -EINVAL;
}
/* Set the read command byte count */
@@ -4566,16 +7526,52 @@ static u8 bnx2x_8726_read_sfp_module_eeprom(struct bnx2x_phy *phy,
return -EINVAL;
}
-static u8 bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy,
- struct link_params *params,
- u16 addr, u8 byte_cnt, u8 *o_buf)
+static int bnx2x_warpcore_read_sfp_module_eeprom(struct bnx2x_phy *phy,
+ struct link_params *params,
+ u16 addr, u8 byte_cnt,
+ u8 *o_buf)
+{
+ int rc = 0;
+ u8 i, j = 0, cnt = 0;
+ u32 data_array[4];
+ u16 addr32;
+ struct bnx2x *bp = params->bp;
+ /*DP(NETIF_MSG_LINK, "bnx2x_direct_read_sfp_module_eeprom:"
+ " addr %d, cnt %d\n",
+ addr, byte_cnt);*/
+ if (byte_cnt > 16) {
+ DP(NETIF_MSG_LINK,
+ "Reading from eeprom is limited to 16 bytes\n");
+ return -EINVAL;
+ }
+
+ /* 4 byte aligned address */
+ addr32 = addr & (~0x3);
+ do {
+ rc = bnx2x_bsc_read(params, phy, 0xa0, addr32, 0, byte_cnt,
+ data_array);
+ } while ((rc != 0) && (++cnt < I2C_WA_RETRY_CNT));
+
+ if (rc == 0) {
+ for (i = (addr - addr32); i < byte_cnt + (addr - addr32); i++) {
+ o_buf[j] = *((u8 *)data_array + i);
+ j++;
+ }
+ }
+
+ return rc;
+}
+
+static int bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy,
+ struct link_params *params,
+ u16 addr, u8 byte_cnt, u8 *o_buf)
{
struct bnx2x *bp = params->bp;
u16 val, i;
if (byte_cnt > 16) {
- DP(NETIF_MSG_LINK, "Reading from eeprom is"
- " is limited to 0xf\n");
+ DP(NETIF_MSG_LINK,
+ "Reading from eeprom is limited to 0xf\n");
return -EINVAL;
}
@@ -4653,27 +7649,39 @@ static u8 bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy,
return -EINVAL;
}
-u8 bnx2x_read_sfp_module_eeprom(struct bnx2x_phy *phy,
- struct link_params *params, u16 addr,
- u8 byte_cnt, u8 *o_buf)
+int bnx2x_read_sfp_module_eeprom(struct bnx2x_phy *phy,
+ struct link_params *params, u16 addr,
+ u8 byte_cnt, u8 *o_buf)
{
- if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726)
- return bnx2x_8726_read_sfp_module_eeprom(phy, params, addr,
- byte_cnt, o_buf);
- else if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727)
- return bnx2x_8727_read_sfp_module_eeprom(phy, params, addr,
- byte_cnt, o_buf);
- return -EINVAL;
+ int rc = -EINVAL;
+ switch (phy->type) {
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726:
+ rc = bnx2x_8726_read_sfp_module_eeprom(phy, params, addr,
+ byte_cnt, o_buf);
+ break;
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727:
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8722:
+ rc = bnx2x_8727_read_sfp_module_eeprom(phy, params, addr,
+ byte_cnt, o_buf);
+ break;
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT:
+ rc = bnx2x_warpcore_read_sfp_module_eeprom(phy, params, addr,
+ byte_cnt, o_buf);
+ break;
+ }
+ return rc;
}
-static u8 bnx2x_get_edc_mode(struct bnx2x_phy *phy,
- struct link_params *params,
- u16 *edc_mode)
+static int bnx2x_get_edc_mode(struct bnx2x_phy *phy,
+ struct link_params *params,
+ u16 *edc_mode)
{
struct bnx2x *bp = params->bp;
+ u32 sync_offset = 0, phy_idx, media_types;
u8 val, check_limiting_mode = 0;
*edc_mode = EDC_MODE_LIMITING;
+ phy->media_type = ETH_PHY_UNSPECIFIED;
/* First check for copper cable */
if (bnx2x_read_sfp_module_eeprom(phy,
params,
@@ -4688,7 +7696,7 @@ static u8 bnx2x_get_edc_mode(struct bnx2x_phy *phy,
case SFP_EEPROM_CON_TYPE_VAL_COPPER:
{
u8 copper_module_type;
-
+ phy->media_type = ETH_PHY_DA_TWINAX;
/*
* Check if its active cable (includes SFP+ module)
* of passive cable
@@ -4697,8 +7705,7 @@ static u8 bnx2x_get_edc_mode(struct bnx2x_phy *phy,
params,
SFP_EEPROM_FC_TX_TECH_ADDR,
1,
- &copper_module_type) !=
- 0) {
+ &copper_module_type) != 0) {
DP(NETIF_MSG_LINK,
"Failed to read copper-cable-type"
" from SFP+ EEPROM\n");
@@ -4711,18 +7718,20 @@ static u8 bnx2x_get_edc_mode(struct bnx2x_phy *phy,
check_limiting_mode = 1;
} else if (copper_module_type &
SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_PASSIVE) {
- DP(NETIF_MSG_LINK, "Passive Copper"
- " cable detected\n");
+ DP(NETIF_MSG_LINK,
+ "Passive Copper cable detected\n");
*edc_mode =
EDC_MODE_PASSIVE_DAC;
} else {
- DP(NETIF_MSG_LINK, "Unknown copper-cable-"
- "type 0x%x !!!\n", copper_module_type);
+ DP(NETIF_MSG_LINK,
+ "Unknown copper-cable-type 0x%x !!!\n",
+ copper_module_type);
return -EINVAL;
}
break;
}
case SFP_EEPROM_CON_TYPE_VAL_LC:
+ phy->media_type = ETH_PHY_SFP_FIBER;
DP(NETIF_MSG_LINK, "Optic module detected\n");
check_limiting_mode = 1;
break;
@@ -4731,7 +7740,22 @@ static u8 bnx2x_get_edc_mode(struct bnx2x_phy *phy,
val);
return -EINVAL;
}
-
+ sync_offset = params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[params->port].media_type);
+ media_types = REG_RD(bp, sync_offset);
+ /* Update media type for non-PMF sync */
+ for (phy_idx = INT_PHY; phy_idx < MAX_PHYS; phy_idx++) {
+ if (&(params->phy[phy_idx]) == phy) {
+ media_types &= ~(PORT_HW_CFG_MEDIA_TYPE_PHY0_MASK <<
+ (PORT_HW_CFG_MEDIA_TYPE_PHY1_SHIFT * phy_idx));
+ media_types |= ((phy->media_type &
+ PORT_HW_CFG_MEDIA_TYPE_PHY0_MASK) <<
+ (PORT_HW_CFG_MEDIA_TYPE_PHY1_SHIFT * phy_idx));
+ break;
+ }
+ }
+ REG_WR(bp, sync_offset, media_types);
if (check_limiting_mode) {
u8 options[SFP_EEPROM_OPTIONS_SIZE];
if (bnx2x_read_sfp_module_eeprom(phy,
@@ -4739,8 +7763,8 @@ static u8 bnx2x_get_edc_mode(struct bnx2x_phy *phy,
SFP_EEPROM_OPTIONS_ADDR,
SFP_EEPROM_OPTIONS_SIZE,
options) != 0) {
- DP(NETIF_MSG_LINK, "Failed to read Option"
- " field from module EEPROM\n");
+ DP(NETIF_MSG_LINK,
+ "Failed to read Option field from module EEPROM\n");
return -EINVAL;
}
if ((options[0] & SFP_EEPROM_OPTIONS_LINEAR_RX_OUT_MASK))
@@ -4755,8 +7779,8 @@ static u8 bnx2x_get_edc_mode(struct bnx2x_phy *phy,
* This function read the relevant field from the module (SFP+), and verify it
* is compliant with this board
*/
-static u8 bnx2x_verify_sfp_module(struct bnx2x_phy *phy,
- struct link_params *params)
+static int bnx2x_verify_sfp_module(struct bnx2x_phy *phy,
+ struct link_params *params)
{
struct bnx2x *bp = params->bp;
u32 val, cmd;
@@ -4781,15 +7805,15 @@ static u8 bnx2x_verify_sfp_module(struct bnx2x_phy *phy,
FEATURE_CONFIG_BC_SUPPORTS_OPT_MDL_VRFY) {
/* Use first phy request only in case of non-dual media*/
if (DUAL_MEDIA(params)) {
- DP(NETIF_MSG_LINK, "FW does not support OPT MDL "
- "verification\n");
+ DP(NETIF_MSG_LINK,
+ "FW does not support OPT MDL verification\n");
return -EINVAL;
}
cmd = DRV_MSG_CODE_VRFY_FIRST_PHY_OPT_MDL;
} else {
/* No support in OPT MDL detection */
- DP(NETIF_MSG_LINK, "FW does not support OPT MDL "
- "verification\n");
+ DP(NETIF_MSG_LINK,
+ "FW does not support OPT MDL verification\n");
return -EINVAL;
}
@@ -4825,8 +7849,8 @@ static u8 bnx2x_verify_sfp_module(struct bnx2x_phy *phy,
return -EINVAL;
}
-static u8 bnx2x_wait_for_sfp_module_initialized(struct bnx2x_phy *phy,
- struct link_params *params)
+static int bnx2x_wait_for_sfp_module_initialized(struct bnx2x_phy *phy,
+ struct link_params *params)
{
u8 val;
@@ -4840,8 +7864,9 @@ static u8 bnx2x_wait_for_sfp_module_initialized(struct bnx2x_phy *phy,
for (timeout = 0; timeout < 60; timeout++) {
if (bnx2x_read_sfp_module_eeprom(phy, params, 1, 1, &val)
== 0) {
- DP(NETIF_MSG_LINK, "SFP+ module initialization "
- "took %d ms\n", timeout * 5);
+ DP(NETIF_MSG_LINK,
+ "SFP+ module initialization took %d ms\n",
+ timeout * 5);
return 0;
}
msleep(5);
@@ -4858,8 +7883,8 @@ static void bnx2x_8727_power_module(struct bnx2x *bp,
* In the GPIO register, bit 4 is use to determine if the GPIOs are
* operating as INPUT or as OUTPUT. Bit 1 is for input, and 0 for
* output
- * Bits 0-1 determine the gpios value for OUTPUT in case bit 4 val is 0
- * Bits 8-9 determine the gpios value for INPUT in case bit 4 val is 1
+ * Bits 0-1 determine the GPIOs value for OUTPUT in case bit 4 val is 0
+ * Bits 8-9 determine the GPIOs value for INPUT in case bit 4 val is 1
* where the 1st bit is the over-current(only input), and 2nd bit is
* for power( only output )
*
@@ -4868,15 +7893,14 @@ static void bnx2x_8727_power_module(struct bnx2x *bp,
*/
if (phy->flags & FLAGS_NOC)
return;
- if (!(phy->flags &
- FLAGS_NOC) && is_power_up)
+ if (is_power_up)
val = (1<<4);
else
/*
* Set GPIO control to OUTPUT, and set the power bit
* to according to the is_power_up
*/
- val = ((!(is_power_up)) << 1);
+ val = (1<<1);
bnx2x_cl45_write(bp, phy,
MDIO_PMA_DEVAD,
@@ -4884,9 +7908,9 @@ static void bnx2x_8727_power_module(struct bnx2x *bp,
val);
}
-static u8 bnx2x_8726_set_limiting_mode(struct bnx2x *bp,
- struct bnx2x_phy *phy,
- u16 edc_mode)
+static int bnx2x_8726_set_limiting_mode(struct bnx2x *bp,
+ struct bnx2x_phy *phy,
+ u16 edc_mode)
{
u16 cur_limiting_mode;
@@ -4934,9 +7958,9 @@ static u8 bnx2x_8726_set_limiting_mode(struct bnx2x *bp,
return 0;
}
-static u8 bnx2x_8727_set_limiting_mode(struct bnx2x *bp,
- struct bnx2x_phy *phy,
- u16 edc_mode)
+static int bnx2x_8727_set_limiting_mode(struct bnx2x *bp,
+ struct bnx2x_phy *phy,
+ u16 edc_mode)
{
u16 phy_identifier;
u16 rom_ver2_val;
@@ -4989,7 +8013,7 @@ static void bnx2x_8727_specific_func(struct bnx2x_phy *phy,
}
}
-static void bnx2x_set_sfp_module_fault_led(struct link_params *params,
+static void bnx2x_set_e1e2_module_fault_led(struct link_params *params,
u8 gpio_mode)
{
struct bnx2x *bp = params->bp;
@@ -5021,12 +8045,149 @@ static void bnx2x_set_sfp_module_fault_led(struct link_params *params,
}
}
-static u8 bnx2x_sfp_module_detection(struct bnx2x_phy *phy,
- struct link_params *params)
+static void bnx2x_set_e3_module_fault_led(struct link_params *params,
+ u8 gpio_mode)
+{
+ u32 pin_cfg;
+ u8 port = params->port;
+ struct bnx2x *bp = params->bp;
+ pin_cfg = (REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[port].e3_sfp_ctrl)) &
+ PORT_HW_CFG_E3_FAULT_MDL_LED_MASK) >>
+ PORT_HW_CFG_E3_FAULT_MDL_LED_SHIFT;
+ DP(NETIF_MSG_LINK, "Setting Fault LED to %d using pin cfg %d\n",
+ gpio_mode, pin_cfg);
+ bnx2x_set_cfg_pin(bp, pin_cfg, gpio_mode);
+}
+
+static void bnx2x_set_sfp_module_fault_led(struct link_params *params,
+ u8 gpio_mode)
+{
+ struct bnx2x *bp = params->bp;
+ DP(NETIF_MSG_LINK, "Setting SFP+ module fault LED to %d\n", gpio_mode);
+ if (CHIP_IS_E3(bp)) {
+ /*
+ * Low ==> if SFP+ module is supported otherwise
+ * High ==> if SFP+ module is not on the approved vendor list
+ */
+ bnx2x_set_e3_module_fault_led(params, gpio_mode);
+ } else
+ bnx2x_set_e1e2_module_fault_led(params, gpio_mode);
+}
+
+static void bnx2x_warpcore_power_module(struct link_params *params,
+ struct bnx2x_phy *phy,
+ u8 power)
+{
+ u32 pin_cfg;
+ struct bnx2x *bp = params->bp;
+
+ pin_cfg = (REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[params->port].e3_sfp_ctrl)) &
+ PORT_HW_CFG_E3_PWR_DIS_MASK) >>
+ PORT_HW_CFG_E3_PWR_DIS_SHIFT;
+
+ if (pin_cfg == PIN_CFG_NA)
+ return;
+ DP(NETIF_MSG_LINK, "Setting SFP+ module power to %d using pin cfg %d\n",
+ power, pin_cfg);
+ /*
+ * Low ==> corresponding SFP+ module is powered
+ * high ==> the SFP+ module is powered down
+ */
+ bnx2x_set_cfg_pin(bp, pin_cfg, power ^ 1);
+}
+
+static void bnx2x_warpcore_hw_reset(struct bnx2x_phy *phy,
+ struct link_params *params)
+{
+ bnx2x_warpcore_power_module(params, phy, 0);
+}
+
+static void bnx2x_power_sfp_module(struct link_params *params,
+ struct bnx2x_phy *phy,
+ u8 power)
+{
+ struct bnx2x *bp = params->bp;
+ DP(NETIF_MSG_LINK, "Setting SFP+ power to %x\n", power);
+
+ switch (phy->type) {
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727:
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8722:
+ bnx2x_8727_power_module(params->bp, phy, power);
+ break;
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT:
+ bnx2x_warpcore_power_module(params, phy, power);
+ break;
+ default:
+ break;
+ }
+}
+static void bnx2x_warpcore_set_limiting_mode(struct link_params *params,
+ struct bnx2x_phy *phy,
+ u16 edc_mode)
+{
+ u16 val = 0;
+ u16 mode = MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE_DEFAULT;
+ struct bnx2x *bp = params->bp;
+
+ u8 lane = bnx2x_get_warpcore_lane(phy, params);
+ /* This is a global register which controls all lanes */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE, &val);
+ val &= ~(0xf << (lane << 2));
+
+ switch (edc_mode) {
+ case EDC_MODE_LINEAR:
+ case EDC_MODE_LIMITING:
+ mode = MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE_DEFAULT;
+ break;
+ case EDC_MODE_PASSIVE_DAC:
+ mode = MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE_SFP_DAC;
+ break;
+ default:
+ break;
+ }
+
+ val |= (mode << (lane << 2));
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE, val);
+ /* A must read */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE, &val);
+
+ /* Restart microcode to re-read the new mode */
+ bnx2x_warpcore_reset_lane(bp, phy, 1);
+ bnx2x_warpcore_reset_lane(bp, phy, 0);
+
+}
+
+static void bnx2x_set_limiting_mode(struct link_params *params,
+ struct bnx2x_phy *phy,
+ u16 edc_mode)
+{
+ switch (phy->type) {
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726:
+ bnx2x_8726_set_limiting_mode(params->bp, phy, edc_mode);
+ break;
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727:
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8722:
+ bnx2x_8727_set_limiting_mode(params->bp, phy, edc_mode);
+ break;
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT:
+ bnx2x_warpcore_set_limiting_mode(params, phy, edc_mode);
+ break;
+ }
+}
+
+int bnx2x_sfp_module_detection(struct bnx2x_phy *phy,
+ struct link_params *params)
{
struct bnx2x *bp = params->bp;
u16 edc_mode;
- u8 rc = 0;
+ int rc = 0;
u32 val = REG_RD(bp, params->shmem_base +
offsetof(struct shmem_region, dev_info.
@@ -5034,7 +8195,8 @@ static u8 bnx2x_sfp_module_detection(struct bnx2x_phy *phy,
DP(NETIF_MSG_LINK, "SFP+ module plugged in/out detected on port %d\n",
params->port);
-
+ /* Power up module */
+ bnx2x_power_sfp_module(params, phy, 1);
if (bnx2x_get_edc_mode(phy, params, &edc_mode) != 0) {
DP(NETIF_MSG_LINK, "Failed to get valid module type\n");
return -EINVAL;
@@ -5046,12 +8208,11 @@ static u8 bnx2x_sfp_module_detection(struct bnx2x_phy *phy,
bnx2x_set_sfp_module_fault_led(params,
MISC_REGISTERS_GPIO_HIGH);
- if ((phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727) &&
- ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
- PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_POWER_DOWN)) {
- /* Shutdown SFP+ module */
+ /* Check if need to power down the SFP+ module */
+ if ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
+ PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_POWER_DOWN) {
DP(NETIF_MSG_LINK, "Shutdown SFP+ module!!\n");
- bnx2x_8727_power_module(bp, phy, 0);
+ bnx2x_power_sfp_module(params, phy, 0);
return rc;
}
} else {
@@ -5059,18 +8220,12 @@ static u8 bnx2x_sfp_module_detection(struct bnx2x_phy *phy,
bnx2x_set_sfp_module_fault_led(params, MISC_REGISTERS_GPIO_LOW);
}
- /* power up the SFP module */
- if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727)
- bnx2x_8727_power_module(bp, phy, 1);
-
/*
* Check and set limiting mode / LRM mode on 8726. On 8727 it
* is done automatically
*/
- if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726)
- bnx2x_8726_set_limiting_mode(bp, phy, edc_mode);
- else
- bnx2x_8727_set_limiting_mode(bp, phy, edc_mode);
+ bnx2x_set_limiting_mode(params, phy, edc_mode);
+
/*
* Enable transmit for this module if the module is approved, or
* if unapproved modules should also enable the Tx laser
@@ -5088,23 +8243,33 @@ static u8 bnx2x_sfp_module_detection(struct bnx2x_phy *phy,
void bnx2x_handle_module_detect_int(struct link_params *params)
{
struct bnx2x *bp = params->bp;
- struct bnx2x_phy *phy = &params->phy[EXT_PHY1];
+ struct bnx2x_phy *phy;
u32 gpio_val;
- u8 port = params->port;
+ u8 gpio_num, gpio_port;
+ if (CHIP_IS_E3(bp))
+ phy = &params->phy[INT_PHY];
+ else
+ phy = &params->phy[EXT_PHY1];
+
+ if (bnx2x_get_mod_abs_int_cfg(bp, params->chip_id, params->shmem_base,
+ params->port, &gpio_num, &gpio_port) ==
+ -EINVAL) {
+ DP(NETIF_MSG_LINK, "Failed to get MOD_ABS interrupt config\n");
+ return;
+ }
/* Set valid module led off */
bnx2x_set_sfp_module_fault_led(params, MISC_REGISTERS_GPIO_HIGH);
/* Get current gpio val reflecting module plugged in / out*/
- gpio_val = bnx2x_get_gpio(bp, MISC_REGISTERS_GPIO_3, port);
+ gpio_val = bnx2x_get_gpio(bp, gpio_num, gpio_port);
/* Call the handling function in case module is detected */
if (gpio_val == 0) {
-
- bnx2x_set_gpio_int(bp, MISC_REGISTERS_GPIO_3,
+ bnx2x_power_sfp_module(params, phy, 1);
+ bnx2x_set_gpio_int(bp, gpio_num,
MISC_REGISTERS_GPIO_INT_OUTPUT_CLR,
- port);
-
+ gpio_port);
if (bnx2x_wait_for_sfp_module_initialized(phy, params) == 0)
bnx2x_sfp_module_detection(phy, params);
else
@@ -5114,21 +8279,45 @@ void bnx2x_handle_module_detect_int(struct link_params *params)
offsetof(struct shmem_region, dev_info.
port_feature_config[params->port].
config));
-
- bnx2x_set_gpio_int(bp, MISC_REGISTERS_GPIO_3,
+ bnx2x_set_gpio_int(bp, gpio_num,
MISC_REGISTERS_GPIO_INT_OUTPUT_SET,
- port);
+ gpio_port);
/*
* Module was plugged out.
* Disable transmit for this module
*/
- if ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
- PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER)
+ phy->media_type = ETH_PHY_NOT_PRESENT;
+ if (((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
+ PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) ||
+ CHIP_IS_E3(bp))
bnx2x_sfp_set_transmitter(params, phy, 0);
}
}
/******************************************************************/
+/* Used by 8706 and 8727 */
+/******************************************************************/
+static void bnx2x_sfp_mask_fault(struct bnx2x *bp,
+ struct bnx2x_phy *phy,
+ u16 alarm_status_offset,
+ u16 alarm_ctrl_offset)
+{
+ u16 alarm_status, val;
+ bnx2x_cl45_read(bp, phy,
+ MDIO_PMA_DEVAD, alarm_status_offset,
+ &alarm_status);
+ bnx2x_cl45_read(bp, phy,
+ MDIO_PMA_DEVAD, alarm_status_offset,
+ &alarm_status);
+ /* Mask or enable the fault event. */
+ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, alarm_ctrl_offset, &val);
+ if (alarm_status & (1<<0))
+ val &= ~(1<<0);
+ else
+ val |= (1<<0);
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, alarm_ctrl_offset, val);
+}
+/******************************************************************/
/* common BCM8706/BCM8726 PHY SECTION */
/******************************************************************/
static u8 bnx2x_8706_8726_read_status(struct bnx2x_phy *phy,
@@ -5141,12 +8330,16 @@ static u8 bnx2x_8706_8726_read_status(struct bnx2x_phy *phy,
DP(NETIF_MSG_LINK, "XGXS 8706/8726\n");
/* Clear RX Alarm*/
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &val2);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXSTAT, &val2);
+
+ bnx2x_sfp_mask_fault(bp, phy, MDIO_PMA_LASI_TXSTAT,
+ MDIO_PMA_LASI_TXCTRL);
+
/* clear LASI indication*/
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_STAT, &val1);
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val2);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_STAT, &val2);
DP(NETIF_MSG_LINK, "8706/8726 LASI status 0x%x--> 0x%x\n", val1, val2);
bnx2x_cl45_read(bp, phy,
@@ -5173,6 +8366,17 @@ static u8 bnx2x_8706_8726_read_status(struct bnx2x_phy *phy,
bnx2x_ext_phy_resolve_fc(phy, params, vars);
vars->duplex = DUPLEX_FULL;
}
+
+ /* Capture 10G link fault. Read twice to clear stale value. */
+ if (vars->line_speed == SPEED_10000) {
+ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD,
+ MDIO_PMA_LASI_TXSTAT, &val1);
+ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD,
+ MDIO_PMA_LASI_TXSTAT, &val1);
+ if (val1 & (1<<0))
+ vars->fault_detected = 1;
+ }
+
return link_up;
}
@@ -5186,6 +8390,7 @@ static u8 bnx2x_8706_config_init(struct bnx2x_phy *phy,
u32 tx_en_mode;
u16 cnt, val, tmp1;
struct bnx2x *bp = params->bp;
+
bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2,
MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port);
/* HW reset */
@@ -5228,7 +8433,11 @@ static u8 bnx2x_8706_config_init(struct bnx2x_phy *phy,
MDIO_PMA_DEVAD,
MDIO_PMA_REG_DIGITAL_CTRL, 0x400);
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 1);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_TXCTRL,
+ 0);
+ /* Arm LASI for link and Tx fault. */
+ bnx2x_cl45_write(bp, phy,
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_CTRL, 3);
} else {
/* Force 1Gbps using autoneg with 1G advertisement */
@@ -5251,10 +8460,10 @@ static u8 bnx2x_8706_config_init(struct bnx2x_phy *phy,
bnx2x_cl45_write(bp, phy,
MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x1200);
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL,
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXCTRL,
0x0400);
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL,
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_CTRL,
0x0004);
}
bnx2x_save_bcm_spirom_ver(bp, phy, params->port);
@@ -5281,9 +8490,9 @@ static u8 bnx2x_8706_config_init(struct bnx2x_phy *phy,
return 0;
}
-static u8 bnx2x_8706_read_status(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_8706_read_status(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
{
return bnx2x_8706_8726_read_status(phy, params, vars);
}
@@ -5358,13 +8567,11 @@ static u8 bnx2x_8726_read_status(struct bnx2x_phy *phy,
}
-static u8 bnx2x_8726_config_init(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_8726_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
- u32 val;
- u32 swap_val, swap_override, aeu_gpio_mask, offset;
DP(NETIF_MSG_LINK, "Initializing BCM8726\n");
bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15);
@@ -5387,9 +8594,9 @@ static u8 bnx2x_8726_config_init(struct bnx2x_phy *phy,
bnx2x_cl45_write(bp, phy,
MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, 0xD);
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x5);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_CTRL, 0x5);
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL,
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXCTRL,
0x400);
} else if ((phy->req_line_speed == SPEED_AUTO_NEG) &&
(phy->speed_cap_mask &
@@ -5415,21 +8622,21 @@ static u8 bnx2x_8726_config_init(struct bnx2x_phy *phy,
* change
*/
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x4);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_CTRL, 0x4);
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL,
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXCTRL,
0x400);
} else { /* Default 10G. Set only LASI control */
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 1);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_CTRL, 1);
}
/* Set TX PreEmphasis if needed */
if ((params->feature_config_flags &
FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED)) {
- DP(NETIF_MSG_LINK, "Setting TX_CTRL1 0x%x,"
- "TX_CTRL2 0x%x\n",
+ DP(NETIF_MSG_LINK,
+ "Setting TX_CTRL1 0x%x, TX_CTRL2 0x%x\n",
phy->tx_preemphasis[0],
phy->tx_preemphasis[1]);
bnx2x_cl45_write(bp, phy,
@@ -5443,30 +8650,6 @@ static u8 bnx2x_8726_config_init(struct bnx2x_phy *phy,
phy->tx_preemphasis[1]);
}
- /* Set GPIO3 to trigger SFP+ module insertion/removal */
- bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_3,
- MISC_REGISTERS_GPIO_INPUT_HI_Z, params->port);
-
- /* The GPIO should be swapped if the swap register is set and active */
- swap_val = REG_RD(bp, NIG_REG_PORT_SWAP);
- swap_override = REG_RD(bp, NIG_REG_STRAP_OVERRIDE);
-
- /* Select function upon port-swap configuration */
- if (params->port == 0) {
- offset = MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0;
- aeu_gpio_mask = (swap_val && swap_override) ?
- AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1 :
- AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0;
- } else {
- offset = MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0;
- aeu_gpio_mask = (swap_val && swap_override) ?
- AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0 :
- AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1;
- }
- val = REG_RD(bp, offset);
- /* add GPIO3 to group */
- val |= aeu_gpio_mask;
- REG_WR(bp, offset, val);
return 0;
}
@@ -5548,9 +8731,9 @@ static void bnx2x_8727_hw_reset(struct bnx2x_phy *phy,
MISC_REGISTERS_GPIO_OUTPUT_LOW, port);
}
-static u8 bnx2x_8727_config_init(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_8727_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
{
u32 tx_en_mode;
u16 tmp1, val, mod_abs, tmp2;
@@ -5561,16 +8744,19 @@ static u8 bnx2x_8727_config_init(struct bnx2x_phy *phy,
bnx2x_wait_reset_complete(bp, phy, params);
rx_alarm_ctrl_val = (1<<2) | (1<<5) ;
- lasi_ctrl_val = 0x0004;
+ /* Should be 0x6 to enable XS on Tx side. */
+ lasi_ctrl_val = 0x0006;
DP(NETIF_MSG_LINK, "Initializing BCM8727\n");
/* enable LASI */
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL,
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXCTRL,
rx_alarm_ctrl_val);
-
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, lasi_ctrl_val);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_TXCTRL,
+ 0);
+ bnx2x_cl45_write(bp, phy,
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_CTRL, lasi_ctrl_val);
/*
* Initially configure MOD_ABS to interrupt when module is
@@ -5590,6 +8776,9 @@ static u8 bnx2x_8727_config_init(struct bnx2x_phy *phy,
MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, mod_abs);
+ /* Enable/Disable PHY transmitter output */
+ bnx2x_set_disable_pmd_transmit(params, phy, 0);
+
/* Make MOD_ABS give interrupt on change */
bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_PCS_OPT_CTRL,
&val);
@@ -5612,7 +8801,7 @@ static u8 bnx2x_8727_config_init(struct bnx2x_phy *phy,
MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &tmp1);
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &tmp1);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXSTAT, &tmp1);
/* Set option 1G speed */
if (phy->req_line_speed == SPEED_1000) {
@@ -5728,9 +8917,9 @@ static void bnx2x_8727_handle_mod_abs(struct bnx2x_phy *phy,
if (mod_abs & (1<<8)) {
/* Module is absent */
- DP(NETIF_MSG_LINK, "MOD_ABS indication "
- "show module is absent\n");
-
+ DP(NETIF_MSG_LINK,
+ "MOD_ABS indication show module is absent\n");
+ phy->media_type = ETH_PHY_NOT_PRESENT;
/*
* 1. Set mod_abs to detect next module
* presence event
@@ -5752,12 +8941,12 @@ static void bnx2x_8727_handle_mod_abs(struct bnx2x_phy *phy,
*/
bnx2x_cl45_read(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_RX_ALARM, &rx_alarm_status);
+ MDIO_PMA_LASI_RXSTAT, &rx_alarm_status);
} else {
/* Module is present */
- DP(NETIF_MSG_LINK, "MOD_ABS indication "
- "show module is present\n");
+ DP(NETIF_MSG_LINK,
+ "MOD_ABS indication show module is present\n");
/*
* First disable transmitter, and if the module is ok, the
* module_detection will enable it
@@ -5781,7 +8970,7 @@ static void bnx2x_8727_handle_mod_abs(struct bnx2x_phy *phy,
*/
bnx2x_cl45_read(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_RX_ALARM, &rx_alarm_status);
+ MDIO_PMA_LASI_RXSTAT, &rx_alarm_status);
if ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
@@ -5805,26 +8994,29 @@ static u8 bnx2x_8727_read_status(struct bnx2x_phy *phy,
{
struct bnx2x *bp = params->bp;
- u8 link_up = 0;
+ u8 link_up = 0, oc_port = params->port;
u16 link_status = 0;
u16 rx_alarm_status, lasi_ctrl, val1;
/* If PHY is not initialized, do not check link status */
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL,
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_CTRL,
&lasi_ctrl);
if (!lasi_ctrl)
return 0;
- /* Check the LASI */
+ /* Check the LASI on Rx */
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM,
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXSTAT,
&rx_alarm_status);
vars->line_speed = 0;
DP(NETIF_MSG_LINK, "8727 RX_ALARM_STATUS 0x%x\n", rx_alarm_status);
+ bnx2x_sfp_mask_fault(bp, phy, MDIO_PMA_LASI_TXSTAT,
+ MDIO_PMA_LASI_TXCTRL);
+
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_STAT, &val1);
DP(NETIF_MSG_LINK, "8727 LASI status 0x%x\n", val1);
@@ -5843,8 +9035,11 @@ static u8 bnx2x_8727_read_status(struct bnx2x_phy *phy,
&val1);
if ((val1 & (1<<8)) == 0) {
- DP(NETIF_MSG_LINK, "8727 Power fault has been detected"
- " on port %d\n", params->port);
+ if (!CHIP_IS_E1x(bp))
+ oc_port = BP_PATH(bp) + (params->port << 1);
+ DP(NETIF_MSG_LINK,
+ "8727 Power fault has been detected on port %d\n",
+ oc_port);
netdev_err(bp->dev, "Error: Power fault on Port %d has"
" been detected and the power to "
"that SFP+ module has been removed"
@@ -5852,11 +9047,11 @@ static u8 bnx2x_8727_read_status(struct bnx2x_phy *phy,
" Please remove the SFP+ module and"
" restart the system to clear this"
" error.\n",
- params->port);
+ oc_port);
/* Disable all RX_ALARMs except for mod_abs */
bnx2x_cl45_write(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_RX_ALARM_CTRL, (1<<5));
+ MDIO_PMA_LASI_RXCTRL, (1<<5));
bnx2x_cl45_read(bp, phy,
MDIO_PMA_DEVAD,
@@ -5869,7 +9064,7 @@ static u8 bnx2x_8727_read_status(struct bnx2x_phy *phy,
/* Clear RX alarm */
bnx2x_cl45_read(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_RX_ALARM, &rx_alarm_status);
+ MDIO_PMA_LASI_RXSTAT, &rx_alarm_status);
return 0;
}
} /* Over current check */
@@ -5879,7 +9074,7 @@ static u8 bnx2x_8727_read_status(struct bnx2x_phy *phy,
bnx2x_8727_handle_mod_abs(phy, params);
/* Enable all mod_abs and link detection bits */
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL,
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXCTRL,
((1<<5) | (1<<2)));
}
DP(NETIF_MSG_LINK, "Enabling 8727 TX laser if SFP is approved\n");
@@ -5915,6 +9110,20 @@ static u8 bnx2x_8727_read_status(struct bnx2x_phy *phy,
DP(NETIF_MSG_LINK, "port %x: External link is down\n",
params->port);
}
+
+ /* Capture 10G link fault. */
+ if (vars->line_speed == SPEED_10000) {
+ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD,
+ MDIO_PMA_LASI_TXSTAT, &val1);
+
+ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD,
+ MDIO_PMA_LASI_TXSTAT, &val1);
+
+ if (val1 & (1<<0)) {
+ vars->fault_detected = 1;
+ }
+ }
+
if (link_up) {
bnx2x_ext_phy_resolve_fc(phy, params, vars);
vars->duplex = DUPLEX_FULL;
@@ -5945,10 +9154,14 @@ static void bnx2x_8727_link_reset(struct bnx2x_phy *phy,
struct link_params *params)
{
struct bnx2x *bp = params->bp;
+
+ /* Enable/Disable PHY transmitter output */
+ bnx2x_set_disable_pmd_transmit(params, phy, 1);
+
/* Disable Transmitter */
bnx2x_sfp_set_transmitter(params, phy, 0);
/* Clear LASI */
- bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0);
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_LASI_CTRL, 0);
}
@@ -5958,111 +9171,106 @@ static void bnx2x_8727_link_reset(struct bnx2x_phy *phy,
static void bnx2x_save_848xx_spirom_version(struct bnx2x_phy *phy,
struct link_params *params)
{
- u16 val, fw_ver1, fw_ver2, cnt, adj;
+ u16 val, fw_ver1, fw_ver2, cnt;
+ u8 port;
struct bnx2x *bp = params->bp;
- adj = 0;
- if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833)
- adj = -1;
+ port = params->port;
/* For the 32 bits registers in 848xx, access via MDIO2ARM interface.*/
/* (1) set register 0xc200_0014(SPI_BRIDGE_CTRL_2) to 0x03000000 */
- bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA819 + adj, 0x0014);
- bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81A + adj, 0xc200);
- bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81B + adj, 0x0000);
- bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81C + adj, 0x0300);
- bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA817 + adj, 0x0009);
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA819, 0x0014);
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81A, 0xc200);
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81B, 0x0000);
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81C, 0x0300);
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA817, 0x0009);
for (cnt = 0; cnt < 100; cnt++) {
- bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA818 + adj, &val);
+ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA818, &val);
if (val & 1)
break;
udelay(5);
}
if (cnt == 100) {
DP(NETIF_MSG_LINK, "Unable to read 848xx phy fw version(1)\n");
- bnx2x_save_spirom_version(bp, params->port, 0,
+ bnx2x_save_spirom_version(bp, port, 0,
phy->ver_addr);
return;
}
/* 2) read register 0xc200_0000 (SPI_FW_STATUS) */
- bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA819 + adj, 0x0000);
- bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81A + adj, 0xc200);
- bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA817 + adj, 0x000A);
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA819, 0x0000);
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81A, 0xc200);
+ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA817, 0x000A);
for (cnt = 0; cnt < 100; cnt++) {
- bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA818 + adj, &val);
+ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA818, &val);
if (val & 1)
break;
udelay(5);
}
if (cnt == 100) {
DP(NETIF_MSG_LINK, "Unable to read 848xx phy fw version(2)\n");
- bnx2x_save_spirom_version(bp, params->port, 0,
+ bnx2x_save_spirom_version(bp, port, 0,
phy->ver_addr);
return;
}
/* lower 16 bits of the register SPI_FW_STATUS */
- bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA81B + adj, &fw_ver1);
+ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA81B, &fw_ver1);
/* upper 16 bits of register SPI_FW_STATUS */
- bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA81C + adj, &fw_ver2);
+ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA81C, &fw_ver2);
- bnx2x_save_spirom_version(bp, params->port, (fw_ver2<<16) | fw_ver1,
+ bnx2x_save_spirom_version(bp, port, (fw_ver2<<16) | fw_ver1,
phy->ver_addr);
}
static void bnx2x_848xx_set_led(struct bnx2x *bp,
struct bnx2x_phy *phy)
{
- u16 val, adj;
-
- adj = 0;
- if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833)
- adj = -1;
+ u16 val;
/* PHYC_CTL_LED_CTL */
bnx2x_cl45_read(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_8481_LINK_SIGNAL + adj, &val);
+ MDIO_PMA_REG_8481_LINK_SIGNAL, &val);
val &= 0xFE00;
val |= 0x0092;
bnx2x_cl45_write(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_8481_LINK_SIGNAL + adj, val);
+ MDIO_PMA_REG_8481_LINK_SIGNAL, val);
bnx2x_cl45_write(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_8481_LED1_MASK + adj,
+ MDIO_PMA_REG_8481_LED1_MASK,
0x80);
bnx2x_cl45_write(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_8481_LED2_MASK + adj,
+ MDIO_PMA_REG_8481_LED2_MASK,
0x18);
/* Select activity source by Tx and Rx, as suggested by PHY AE */
bnx2x_cl45_write(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_8481_LED3_MASK + adj,
+ MDIO_PMA_REG_8481_LED3_MASK,
0x0006);
/* Select the closest activity blink rate to that in 10/100/1000 */
bnx2x_cl45_write(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_8481_LED3_BLINK + adj,
+ MDIO_PMA_REG_8481_LED3_BLINK,
0);
bnx2x_cl45_read(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_84823_CTL_LED_CTL_1 + adj, &val);
+ MDIO_PMA_REG_84823_CTL_LED_CTL_1, &val);
val |= MDIO_PMA_REG_84823_LED3_STRETCH_EN; /* stretch_en for LED3*/
bnx2x_cl45_write(bp, phy,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_84823_CTL_LED_CTL_1 + adj, val);
+ MDIO_PMA_REG_84823_CTL_LED_CTL_1, val);
/* 'Interrupt Mask' */
bnx2x_cl45_write(bp, phy,
@@ -6070,12 +9278,19 @@ static void bnx2x_848xx_set_led(struct bnx2x *bp,
0xFFFB, 0xFFFD);
}
-static u8 bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
u16 autoneg_val, an_1000_val, an_10_100_val;
+ u16 tmp_req_line_speed;
+
+ tmp_req_line_speed = phy->req_line_speed;
+ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833)
+ if (phy->req_line_speed == SPEED_10000)
+ phy->req_line_speed = SPEED_AUTO_NEG;
+
/*
* This phy uses the NIG latch mechanism since link indication
* arrives through its LED4 and not via its LASI signal, so we
@@ -6122,11 +9337,14 @@ static u8 bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
MDIO_AN_DEVAD, MDIO_AN_REG_8481_1000T_CTRL,
an_1000_val);
- /* set 10 speed advertisement */
+ /* set 100 speed advertisement */
if (((phy->req_line_speed == SPEED_AUTO_NEG) &&
(phy->speed_cap_mask &
- (PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL |
- PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF)))) {
+ (PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL |
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF)) &&
+ (phy->supported &
+ (SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full)))) {
an_10_100_val |= (1<<7);
/* Enable autoneg and restart autoneg for legacy speeds */
autoneg_val |= (1<<9 | 1<<12);
@@ -6137,9 +9355,12 @@ static u8 bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
}
/* set 10 speed advertisement */
if (((phy->req_line_speed == SPEED_AUTO_NEG) &&
- (phy->speed_cap_mask &
- (PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL |
- PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF)))) {
+ (phy->speed_cap_mask &
+ (PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL |
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF)) &&
+ (phy->supported &
+ (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full)))) {
an_10_100_val |= (1<<5);
autoneg_val |= (1<<9 | 1<<12);
if (phy->req_duplex == DUPLEX_FULL)
@@ -6148,7 +9369,10 @@ static u8 bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
}
/* Only 10/100 are allowed to work in FORCE mode */
- if (phy->req_line_speed == SPEED_100) {
+ if ((phy->req_line_speed == SPEED_100) &&
+ (phy->supported &
+ (SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full))) {
autoneg_val |= (1<<13);
/* Enabled AUTO-MDIX when autoneg is disabled */
bnx2x_cl45_write(bp, phy,
@@ -6156,7 +9380,10 @@ static u8 bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
(1<<15 | 1<<9 | 7<<0));
DP(NETIF_MSG_LINK, "Setting 100M force\n");
}
- if (phy->req_line_speed == SPEED_10) {
+ if ((phy->req_line_speed == SPEED_10) &&
+ (phy->supported &
+ (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full))) {
/* Enabled AUTO-MDIX when autoneg is disabled */
bnx2x_cl45_write(bp, phy,
MDIO_AN_DEVAD, MDIO_AN_REG_8481_AUX_CTRL,
@@ -6171,7 +9398,13 @@ static u8 bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
if (phy->req_duplex == DUPLEX_FULL)
autoneg_val |= (1<<8);
- bnx2x_cl45_write(bp, phy,
+ /*
+ * Always write this if this is not 84833.
+ * For 84833, write it only when it's a forced speed.
+ */
+ if ((phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) ||
+ ((autoneg_val & (1<<12)) == 0))
+ bnx2x_cl45_write(bp, phy,
MDIO_AN_DEVAD,
MDIO_AN_REG_8481_LEGACY_MII_CTRL, autoneg_val);
@@ -6179,28 +9412,29 @@ static u8 bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
(phy->speed_cap_mask &
PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)) ||
(phy->req_line_speed == SPEED_10000)) {
- DP(NETIF_MSG_LINK, "Advertising 10G\n");
- /* Restart autoneg for 10G*/
+ DP(NETIF_MSG_LINK, "Advertising 10G\n");
+ /* Restart autoneg for 10G*/
- bnx2x_cl45_write(bp, phy,
+ bnx2x_cl45_write(bp, phy,
MDIO_AN_DEVAD, MDIO_AN_REG_CTRL,
0x3200);
- } else if (phy->req_line_speed != SPEED_10 &&
- phy->req_line_speed != SPEED_100) {
+ } else
bnx2x_cl45_write(bp, phy,
MDIO_AN_DEVAD,
MDIO_AN_REG_8481_10GBASE_T_AN_CTRL,
1);
- }
+
/* Save spirom version */
bnx2x_save_848xx_spirom_version(phy, params);
+ phy->req_line_speed = tmp_req_line_speed;
+
return 0;
}
-static u8 bnx2x_8481_config_init(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_8481_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
/* Restore normal power mode*/
@@ -6215,33 +9449,200 @@ static u8 bnx2x_8481_config_init(struct bnx2x_phy *phy,
return bnx2x_848xx_cmn_config_init(phy, params, vars);
}
-static u8 bnx2x_848x3_config_init(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
+
+#define PHY84833_HDSHK_WAIT 300
+static int bnx2x_84833_pair_swap_cfg(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
+{
+ u32 idx;
+ u32 pair_swap;
+ u16 val;
+ u16 data;
+ struct bnx2x *bp = params->bp;
+ /* Do pair swap */
+
+ /* Check for configuration. */
+ pair_swap = REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[params->port].xgbt_phy_cfg)) &
+ PORT_HW_CFG_RJ45_PAIR_SWAP_MASK;
+
+ if (pair_swap == 0)
+ return 0;
+
+ data = (u16)pair_swap;
+
+ /* Write CMD_OPEN_OVERRIDE to STATUS reg */
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG2,
+ PHY84833_CMD_OPEN_OVERRIDE);
+ for (idx = 0; idx < PHY84833_HDSHK_WAIT; idx++) {
+ bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG2, &val);
+ if (val == PHY84833_CMD_OPEN_FOR_CMDS)
+ break;
+ msleep(1);
+ }
+ if (idx >= PHY84833_HDSHK_WAIT) {
+ DP(NETIF_MSG_LINK, "Pairswap: FW not ready.\n");
+ return -EINVAL;
+ }
+
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG4,
+ data);
+ /* Issue pair swap command */
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG0,
+ PHY84833_DIAG_CMD_PAIR_SWAP_CHANGE);
+ for (idx = 0; idx < PHY84833_HDSHK_WAIT; idx++) {
+ bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG2, &val);
+ if ((val == PHY84833_CMD_COMPLETE_PASS) ||
+ (val == PHY84833_CMD_COMPLETE_ERROR))
+ break;
+ msleep(1);
+ }
+ if ((idx >= PHY84833_HDSHK_WAIT) ||
+ (val == PHY84833_CMD_COMPLETE_ERROR)) {
+ DP(NETIF_MSG_LINK, "Pairswap: override failed.\n");
+ return -EINVAL;
+ }
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG2,
+ PHY84833_CMD_CLEAR_COMPLETE);
+ DP(NETIF_MSG_LINK, "Pairswap OK, val=0x%x\n", data);
+ return 0;
+}
+
+
+static u8 bnx2x_84833_get_reset_gpios(struct bnx2x *bp,
+ u32 shmem_base_path[],
+ u32 chip_id)
+{
+ u32 reset_pin[2];
+ u32 idx;
+ u8 reset_gpios;
+ if (CHIP_IS_E3(bp)) {
+ /* Assume that these will be GPIOs, not EPIOs. */
+ for (idx = 0; idx < 2; idx++) {
+ /* Map config param to register bit. */
+ reset_pin[idx] = REG_RD(bp, shmem_base_path[idx] +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[0].e3_cmn_pin_cfg));
+ reset_pin[idx] = (reset_pin[idx] &
+ PORT_HW_CFG_E3_PHY_RESET_MASK) >>
+ PORT_HW_CFG_E3_PHY_RESET_SHIFT;
+ reset_pin[idx] -= PIN_CFG_GPIO0_P0;
+ reset_pin[idx] = (1 << reset_pin[idx]);
+ }
+ reset_gpios = (u8)(reset_pin[0] | reset_pin[1]);
+ } else {
+ /* E2, look from diff place of shmem. */
+ for (idx = 0; idx < 2; idx++) {
+ reset_pin[idx] = REG_RD(bp, shmem_base_path[idx] +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[0].default_cfg));
+ reset_pin[idx] &= PORT_HW_CFG_EXT_PHY_GPIO_RST_MASK;
+ reset_pin[idx] -= PORT_HW_CFG_EXT_PHY_GPIO_RST_GPIO0_P0;
+ reset_pin[idx] >>= PORT_HW_CFG_EXT_PHY_GPIO_RST_SHIFT;
+ reset_pin[idx] = (1 << reset_pin[idx]);
+ }
+ reset_gpios = (u8)(reset_pin[0] | reset_pin[1]);
+ }
+
+ return reset_gpios;
+}
+
+static int bnx2x_84833_hw_reset_phy(struct bnx2x_phy *phy,
+ struct link_params *params)
+{
+ struct bnx2x *bp = params->bp;
+ u8 reset_gpios;
+ u32 other_shmem_base_addr = REG_RD(bp, params->shmem2_base +
+ offsetof(struct shmem2_region,
+ other_shmem_base_addr));
+
+ u32 shmem_base_path[2];
+ shmem_base_path[0] = params->shmem_base;
+ shmem_base_path[1] = other_shmem_base_addr;
+
+ reset_gpios = bnx2x_84833_get_reset_gpios(bp, shmem_base_path,
+ params->chip_id);
+
+ bnx2x_set_mult_gpio(bp, reset_gpios, MISC_REGISTERS_GPIO_OUTPUT_LOW);
+ udelay(10);
+ DP(NETIF_MSG_LINK, "84833 hw reset on pin values 0x%x\n",
+ reset_gpios);
+
+ return 0;
+}
+
+static int bnx2x_84833_common_init_phy(struct bnx2x *bp,
+ u32 shmem_base_path[],
+ u32 chip_id)
+{
+ u8 reset_gpios;
+
+ reset_gpios = bnx2x_84833_get_reset_gpios(bp, shmem_base_path, chip_id);
+
+ bnx2x_set_mult_gpio(bp, reset_gpios, MISC_REGISTERS_GPIO_OUTPUT_LOW);
+ udelay(10);
+ bnx2x_set_mult_gpio(bp, reset_gpios, MISC_REGISTERS_GPIO_OUTPUT_HIGH);
+ msleep(800);
+ DP(NETIF_MSG_LINK, "84833 reset pulse on pin values 0x%x\n",
+ reset_gpios);
+
+ return 0;
+}
+
+#define PHY84833_CONSTANT_LATENCY 1193
+static int bnx2x_848x3_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
u8 port, initialize = 1;
- u16 val, adj;
+ u16 val;
u16 temp;
- u32 actual_phy_selection, cms_enable;
- u8 rc = 0;
-
- /* This is just for MDIO_CTL_REG_84823_MEDIA register. */
- adj = 0;
- if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833)
- adj = 3;
+ u32 actual_phy_selection, cms_enable, idx;
+ int rc = 0;
msleep(1);
- if (CHIP_IS_E2(bp))
+
+ if (!(CHIP_IS_E1(bp)))
port = BP_PATH(bp);
else
port = params->port;
- bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_3,
- MISC_REGISTERS_GPIO_OUTPUT_HIGH,
- port);
+
+ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823) {
+ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_3,
+ MISC_REGISTERS_GPIO_OUTPUT_HIGH,
+ port);
+ } else {
+ /* MDIO reset */
+ bnx2x_cl45_write(bp, phy,
+ MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_CTRL, 0x8000);
+ /* Bring PHY out of super isolate mode */
+ bnx2x_cl45_read(bp, phy,
+ MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_XGPHY_STRAP1, &val);
+ val &= ~MDIO_84833_SUPER_ISOLATE;
+ bnx2x_cl45_write(bp, phy,
+ MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_XGPHY_STRAP1, val);
+ }
+
bnx2x_wait_reset_complete(bp, phy, params);
+
/* Wait for GPHY to come out of reset */
msleep(50);
+
+ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833)
+ bnx2x_84833_pair_swap_cfg(phy, params, vars);
+
/*
* BCM84823 requires that XGXS links up first @ 10G for normal behavior
*/
@@ -6254,14 +9655,20 @@ static u8 bnx2x_848x3_config_init(struct bnx2x_phy *phy,
/* Set dual-media configuration according to configuration */
bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD,
- MDIO_CTL_REG_84823_MEDIA + adj, &val);
+ MDIO_CTL_REG_84823_MEDIA, &val);
val &= ~(MDIO_CTL_REG_84823_MEDIA_MAC_MASK |
MDIO_CTL_REG_84823_MEDIA_LINE_MASK |
MDIO_CTL_REG_84823_MEDIA_COPPER_CORE_DOWN |
MDIO_CTL_REG_84823_MEDIA_PRIORITY_MASK |
MDIO_CTL_REG_84823_MEDIA_FIBER_1G);
- val |= MDIO_CTL_REG_84823_CTRL_MAC_XFI |
- MDIO_CTL_REG_84823_MEDIA_LINE_XAUI_L;
+
+ if (CHIP_IS_E3(bp)) {
+ val &= ~(MDIO_CTL_REG_84823_MEDIA_MAC_MASK |
+ MDIO_CTL_REG_84823_MEDIA_LINE_MASK);
+ } else {
+ val |= (MDIO_CTL_REG_84823_CTRL_MAC_XFI |
+ MDIO_CTL_REG_84823_MEDIA_LINE_XAUI_L);
+ }
actual_phy_selection = bnx2x_phy_selection(params);
@@ -6287,28 +9694,90 @@ static u8 bnx2x_848x3_config_init(struct bnx2x_phy *phy,
val |= MDIO_CTL_REG_84823_MEDIA_FIBER_1G;
bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
- MDIO_CTL_REG_84823_MEDIA + adj, val);
+ MDIO_CTL_REG_84823_MEDIA, val);
DP(NETIF_MSG_LINK, "Multi_phy config = 0x%x, Media control = 0x%x\n",
params->multi_phy_config, val);
+ /* AutogrEEEn */
+ if (params->feature_config_flags &
+ FEATURE_CONFIG_AUTOGREEEN_ENABLED) {
+ /* Ensure that f/w is ready */
+ for (idx = 0; idx < PHY84833_HDSHK_WAIT; idx++) {
+ bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG2, &val);
+ if (val == PHY84833_CMD_OPEN_FOR_CMDS)
+ break;
+ usleep_range(1000, 1000);
+ }
+ if (idx >= PHY84833_HDSHK_WAIT) {
+ DP(NETIF_MSG_LINK, "AutogrEEEn: FW not ready.\n");
+ return -EINVAL;
+ }
+
+ /* Select EEE mode */
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG3,
+ 0x2);
+
+ /* Set Idle and Latency */
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG4,
+ PHY84833_CONSTANT_LATENCY + 1);
+
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_DATA3_REG,
+ PHY84833_CONSTANT_LATENCY + 1);
+
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_DATA4_REG,
+ PHY84833_CONSTANT_LATENCY);
+
+ /* Send EEE instruction to command register */
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG0,
+ PHY84833_DIAG_CMD_SET_EEE_MODE);
+
+ /* Ensure that the command has completed */
+ for (idx = 0; idx < PHY84833_HDSHK_WAIT; idx++) {
+ bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG2, &val);
+ if ((val == PHY84833_CMD_COMPLETE_PASS) ||
+ (val == PHY84833_CMD_COMPLETE_ERROR))
+ break;
+ usleep_range(1000, 1000);
+ }
+ if ((idx >= PHY84833_HDSHK_WAIT) ||
+ (val == PHY84833_CMD_COMPLETE_ERROR)) {
+ DP(NETIF_MSG_LINK, "AutogrEEEn: command failed.\n");
+ return -EINVAL;
+ }
+
+ /* Reset command handler */
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_84833_TOP_CFG_SCRATCH_REG2,
+ PHY84833_CMD_CLEAR_COMPLETE);
+ }
+
if (initialize)
rc = bnx2x_848xx_cmn_config_init(phy, params, vars);
else
bnx2x_save_848xx_spirom_version(phy, params);
- cms_enable = REG_RD(bp, params->shmem_base +
+ /* 84833 PHY has a better feature and doesn't need to support this. */
+ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823) {
+ cms_enable = REG_RD(bp, params->shmem_base +
offsetof(struct shmem_region,
dev_info.port_hw_config[params->port].default_cfg)) &
PORT_HW_CFG_ENABLE_CMS_MASK;
- bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD,
- MDIO_CTL_REG_84823_USER_CTRL_REG, &val);
- if (cms_enable)
- val |= MDIO_CTL_REG_84823_USER_CTRL_CMS;
- else
- val &= ~MDIO_CTL_REG_84823_USER_CTRL_CMS;
- bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
- MDIO_CTL_REG_84823_USER_CTRL_REG, val);
-
+ bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_CTL_REG_84823_USER_CTRL_REG, &val);
+ if (cms_enable)
+ val |= MDIO_CTL_REG_84823_USER_CTRL_CMS;
+ else
+ val &= ~MDIO_CTL_REG_84823_USER_CTRL_CMS;
+ bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
+ MDIO_CTL_REG_84823_USER_CTRL_REG, val);
+ }
return rc;
}
@@ -6318,20 +9787,16 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy,
struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
- u16 val, val1, val2, adj;
+ u16 val, val1, val2;
u8 link_up = 0;
- /* Reg offset adjustment for 84833 */
- adj = 0;
- if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833)
- adj = -1;
/* Check 10G-BaseT link status */
/* Check PMD signal ok */
bnx2x_cl45_read(bp, phy,
MDIO_AN_DEVAD, 0xFFFA, &val1);
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_PMD_SIGNAL + adj,
+ MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_PMD_SIGNAL,
&val2);
DP(NETIF_MSG_LINK, "BCM848xx: PMD_SIGNAL 1.a811 = 0x%x\n", val2);
@@ -6355,8 +9820,8 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy,
MDIO_AN_REG_8481_EXPANSION_REG_RD_RW,
&legacy_status);
- DP(NETIF_MSG_LINK, "Legacy speed status"
- " = 0x%x\n", legacy_status);
+ DP(NETIF_MSG_LINK, "Legacy speed status = 0x%x\n",
+ legacy_status);
link_up = ((legacy_status & (1<<11)) == (1<<11));
if (link_up) {
legacy_speed = (legacy_status & (3<<9));
@@ -6374,9 +9839,10 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy,
else
vars->duplex = DUPLEX_HALF;
- DP(NETIF_MSG_LINK, "Link is up in %dMbps,"
- " is_duplex_full= %d\n", vars->line_speed,
- (vars->duplex == DUPLEX_FULL));
+ DP(NETIF_MSG_LINK,
+ "Link is up in %dMbps, is_duplex_full= %d\n",
+ vars->line_speed,
+ (vars->duplex == DUPLEX_FULL));
/* Check legacy speed AN resolution */
bnx2x_cl45_read(bp, phy,
MDIO_AN_DEVAD,
@@ -6403,9 +9869,10 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy,
return link_up;
}
-static u8 bnx2x_848xx_format_ver(u32 raw_ver, u8 *str, u16 *len)
+
+static int bnx2x_848xx_format_ver(u32 raw_ver, u8 *str, u16 *len)
{
- u8 status = 0;
+ int status = 0;
u32 spirom_ver;
spirom_ver = ((raw_ver & 0xF80) >> 7) << 16 | (raw_ver & 0x7F);
status = bnx2x_format_ver(spirom_ver, str, len);
@@ -6435,13 +9902,25 @@ static void bnx2x_848x3_link_reset(struct bnx2x_phy *phy,
{
struct bnx2x *bp = params->bp;
u8 port;
- if (CHIP_IS_E2(bp))
+ u16 val16;
+
+ if (!(CHIP_IS_E1(bp)))
port = BP_PATH(bp);
else
port = params->port;
- bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_3,
- MISC_REGISTERS_GPIO_OUTPUT_LOW,
- port);
+
+ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823) {
+ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_3,
+ MISC_REGISTERS_GPIO_OUTPUT_LOW,
+ port);
+ } else {
+ bnx2x_cl45_read(bp, phy,
+ MDIO_CTL_DEVAD,
+ 0x400f, &val16);
+ bnx2x_cl45_write(bp, phy,
+ MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_CTRL, 0x800);
+ }
}
static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy,
@@ -6449,11 +9928,17 @@ static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy,
{
struct bnx2x *bp = params->bp;
u16 val;
+ u8 port;
+
+ if (!(CHIP_IS_E1(bp)))
+ port = BP_PATH(bp);
+ else
+ port = params->port;
switch (mode) {
case LED_MODE_OFF:
- DP(NETIF_MSG_LINK, "Port 0x%x: LED MODE OFF\n", params->port);
+ DP(NETIF_MSG_LINK, "Port 0x%x: LED MODE OFF\n", port);
if ((params->hw_led_mode << SHARED_HW_CFG_LED_MODE_SHIFT) ==
SHARED_HW_CFG_LED_EXTPHY1) {
@@ -6489,7 +9974,7 @@ static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy,
case LED_MODE_FRONT_PANEL_OFF:
DP(NETIF_MSG_LINK, "Port 0x%x: LED MODE FRONT PANEL OFF\n",
- params->port);
+ port);
if ((params->hw_led_mode << SHARED_HW_CFG_LED_MODE_SHIFT) ==
SHARED_HW_CFG_LED_EXTPHY1) {
@@ -6524,7 +10009,7 @@ static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy,
break;
case LED_MODE_ON:
- DP(NETIF_MSG_LINK, "Port 0x%x: LED MODE ON\n", params->port);
+ DP(NETIF_MSG_LINK, "Port 0x%x: LED MODE ON\n", port);
if ((params->hw_led_mode << SHARED_HW_CFG_LED_MODE_SHIFT) ==
SHARED_HW_CFG_LED_EXTPHY1) {
@@ -6571,7 +10056,7 @@ static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy,
case LED_MODE_OPER:
- DP(NETIF_MSG_LINK, "Port 0x%x: LED MODE OPER\n", params->port);
+ DP(NETIF_MSG_LINK, "Port 0x%x: LED MODE OPER\n", port);
if ((params->hw_led_mode << SHARED_HW_CFG_LED_MODE_SHIFT) ==
SHARED_HW_CFG_LED_EXTPHY1) {
@@ -6633,7 +10118,380 @@ static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy,
}
break;
}
+
+ /*
+ * This is a workaround for E3+84833 until autoneg
+ * restart is fixed in f/w
+ */
+ if (CHIP_IS_E3(bp)) {
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_GP2_STATUS_GP_2_1, &val);
+ }
}
+
+/******************************************************************/
+/* 54618SE PHY SECTION */
+/******************************************************************/
+static int bnx2x_54618se_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u8 port;
+ u16 autoneg_val, an_1000_val, an_10_100_val, fc_val, temp;
+ u32 cfg_pin;
+
+ DP(NETIF_MSG_LINK, "54618SE cfg init\n");
+ usleep_range(1000, 1000);
+
+ /* This works with E3 only, no need to check the chip
+ before determining the port. */
+ port = params->port;
+
+ cfg_pin = (REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[port].e3_cmn_pin_cfg)) &
+ PORT_HW_CFG_E3_PHY_RESET_MASK) >>
+ PORT_HW_CFG_E3_PHY_RESET_SHIFT;
+
+ /* Drive pin high to bring the GPHY out of reset. */
+ bnx2x_set_cfg_pin(bp, cfg_pin, 1);
+
+ /* wait for GPHY to reset */
+ msleep(50);
+
+ /* reset phy */
+ bnx2x_cl22_write(bp, phy,
+ MDIO_PMA_REG_CTRL, 0x8000);
+ bnx2x_wait_reset_complete(bp, phy, params);
+
+ /*wait for GPHY to reset */
+ msleep(50);
+
+ /* Configure LED4: set to INTR (0x6). */
+ /* Accessing shadow register 0xe. */
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_SHADOW,
+ MDIO_REG_GPHY_SHADOW_LED_SEL2);
+ bnx2x_cl22_read(bp, phy,
+ MDIO_REG_GPHY_SHADOW,
+ &temp);
+ temp &= ~(0xf << 4);
+ temp |= (0x6 << 4);
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_SHADOW,
+ MDIO_REG_GPHY_SHADOW_WR_ENA | temp);
+ /* Configure INTR based on link status change. */
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_INTR_MASK,
+ ~MDIO_REG_INTR_MASK_LINK_STATUS);
+
+ /* Flip the signal detect polarity (set 0x1c.0x1e[8]). */
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_SHADOW,
+ MDIO_REG_GPHY_SHADOW_AUTO_DET_MED);
+ bnx2x_cl22_read(bp, phy,
+ MDIO_REG_GPHY_SHADOW,
+ &temp);
+ temp |= MDIO_REG_GPHY_SHADOW_INVERT_FIB_SD;
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_SHADOW,
+ MDIO_REG_GPHY_SHADOW_WR_ENA | temp);
+
+ /* Set up fc */
+ /* Please refer to Table 28B-3 of 802.3ab-1999 spec. */
+ bnx2x_calc_ieee_aneg_adv(phy, params, &vars->ieee_fc);
+ fc_val = 0;
+ if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) ==
+ MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC)
+ fc_val |= MDIO_AN_REG_ADV_PAUSE_ASYMMETRIC;
+
+ if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) ==
+ MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH)
+ fc_val |= MDIO_AN_REG_ADV_PAUSE_PAUSE;
+
+ /* read all advertisement */
+ bnx2x_cl22_read(bp, phy,
+ 0x09,
+ &an_1000_val);
+
+ bnx2x_cl22_read(bp, phy,
+ 0x04,
+ &an_10_100_val);
+
+ bnx2x_cl22_read(bp, phy,
+ MDIO_PMA_REG_CTRL,
+ &autoneg_val);
+
+ /* Disable forced speed */
+ autoneg_val &= ~((1<<6) | (1<<8) | (1<<9) | (1<<12) | (1<<13));
+ an_10_100_val &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8) | (1<<10) |
+ (1<<11));
+
+ if (((phy->req_line_speed == SPEED_AUTO_NEG) &&
+ (phy->speed_cap_mask &
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) ||
+ (phy->req_line_speed == SPEED_1000)) {
+ an_1000_val |= (1<<8);
+ autoneg_val |= (1<<9 | 1<<12);
+ if (phy->req_duplex == DUPLEX_FULL)
+ an_1000_val |= (1<<9);
+ DP(NETIF_MSG_LINK, "Advertising 1G\n");
+ } else
+ an_1000_val &= ~((1<<8) | (1<<9));
+
+ bnx2x_cl22_write(bp, phy,
+ 0x09,
+ an_1000_val);
+ bnx2x_cl22_read(bp, phy,
+ 0x09,
+ &an_1000_val);
+
+ /* set 100 speed advertisement */
+ if (((phy->req_line_speed == SPEED_AUTO_NEG) &&
+ (phy->speed_cap_mask &
+ (PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL |
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF)))) {
+ an_10_100_val |= (1<<7);
+ /* Enable autoneg and restart autoneg for legacy speeds */
+ autoneg_val |= (1<<9 | 1<<12);
+
+ if (phy->req_duplex == DUPLEX_FULL)
+ an_10_100_val |= (1<<8);
+ DP(NETIF_MSG_LINK, "Advertising 100M\n");
+ }
+
+ /* set 10 speed advertisement */
+ if (((phy->req_line_speed == SPEED_AUTO_NEG) &&
+ (phy->speed_cap_mask &
+ (PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL |
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF)))) {
+ an_10_100_val |= (1<<5);
+ autoneg_val |= (1<<9 | 1<<12);
+ if (phy->req_duplex == DUPLEX_FULL)
+ an_10_100_val |= (1<<6);
+ DP(NETIF_MSG_LINK, "Advertising 10M\n");
+ }
+
+ /* Only 10/100 are allowed to work in FORCE mode */
+ if (phy->req_line_speed == SPEED_100) {
+ autoneg_val |= (1<<13);
+ /* Enabled AUTO-MDIX when autoneg is disabled */
+ bnx2x_cl22_write(bp, phy,
+ 0x18,
+ (1<<15 | 1<<9 | 7<<0));
+ DP(NETIF_MSG_LINK, "Setting 100M force\n");
+ }
+ if (phy->req_line_speed == SPEED_10) {
+ /* Enabled AUTO-MDIX when autoneg is disabled */
+ bnx2x_cl22_write(bp, phy,
+ 0x18,
+ (1<<15 | 1<<9 | 7<<0));
+ DP(NETIF_MSG_LINK, "Setting 10M force\n");
+ }
+
+ /* Check if we should turn on Auto-GrEEEn */
+ bnx2x_cl22_read(bp, phy, MDIO_REG_GPHY_PHYID_LSB, &temp);
+ if (temp == MDIO_REG_GPHY_ID_54618SE) {
+ if (params->feature_config_flags &
+ FEATURE_CONFIG_AUTOGREEEN_ENABLED) {
+ temp = 6;
+ DP(NETIF_MSG_LINK, "Enabling Auto-GrEEEn\n");
+ } else {
+ temp = 0;
+ DP(NETIF_MSG_LINK, "Disabling Auto-GrEEEn\n");
+ }
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_CL45_ADDR_REG, MDIO_AN_DEVAD);
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_CL45_DATA_REG,
+ MDIO_REG_GPHY_EEE_ADV);
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_CL45_ADDR_REG,
+ (0x1 << 14) | MDIO_AN_DEVAD);
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_CL45_DATA_REG,
+ temp);
+ }
+
+ bnx2x_cl22_write(bp, phy,
+ 0x04,
+ an_10_100_val | fc_val);
+
+ if (phy->req_duplex == DUPLEX_FULL)
+ autoneg_val |= (1<<8);
+
+ bnx2x_cl22_write(bp, phy,
+ MDIO_PMA_REG_CTRL, autoneg_val);
+
+ return 0;
+}
+
+static void bnx2x_54618se_link_reset(struct bnx2x_phy *phy,
+ struct link_params *params)
+{
+ struct bnx2x *bp = params->bp;
+ u32 cfg_pin;
+ u8 port;
+
+ /*
+ * In case of no EPIO routed to reset the GPHY, put it
+ * in low power mode.
+ */
+ bnx2x_cl22_write(bp, phy, MDIO_PMA_REG_CTRL, 0x800);
+ /*
+ * This works with E3 only, no need to check the chip
+ * before determining the port.
+ */
+ port = params->port;
+ cfg_pin = (REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[port].e3_cmn_pin_cfg)) &
+ PORT_HW_CFG_E3_PHY_RESET_MASK) >>
+ PORT_HW_CFG_E3_PHY_RESET_SHIFT;
+
+ /* Drive pin low to put GPHY in reset. */
+ bnx2x_set_cfg_pin(bp, cfg_pin, 0);
+}
+
+static u8 bnx2x_54618se_read_status(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u16 val;
+ u8 link_up = 0;
+ u16 legacy_status, legacy_speed;
+
+ /* Get speed operation status */
+ bnx2x_cl22_read(bp, phy,
+ 0x19,
+ &legacy_status);
+ DP(NETIF_MSG_LINK, "54618SE read_status: 0x%x\n", legacy_status);
+
+ /* Read status to clear the PHY interrupt. */
+ bnx2x_cl22_read(bp, phy,
+ MDIO_REG_INTR_STATUS,
+ &val);
+
+ link_up = ((legacy_status & (1<<2)) == (1<<2));
+
+ if (link_up) {
+ legacy_speed = (legacy_status & (7<<8));
+ if (legacy_speed == (7<<8)) {
+ vars->line_speed = SPEED_1000;
+ vars->duplex = DUPLEX_FULL;
+ } else if (legacy_speed == (6<<8)) {
+ vars->line_speed = SPEED_1000;
+ vars->duplex = DUPLEX_HALF;
+ } else if (legacy_speed == (5<<8)) {
+ vars->line_speed = SPEED_100;
+ vars->duplex = DUPLEX_FULL;
+ }
+ /* Omitting 100Base-T4 for now */
+ else if (legacy_speed == (3<<8)) {
+ vars->line_speed = SPEED_100;
+ vars->duplex = DUPLEX_HALF;
+ } else if (legacy_speed == (2<<8)) {
+ vars->line_speed = SPEED_10;
+ vars->duplex = DUPLEX_FULL;
+ } else if (legacy_speed == (1<<8)) {
+ vars->line_speed = SPEED_10;
+ vars->duplex = DUPLEX_HALF;
+ } else /* Should not happen */
+ vars->line_speed = 0;
+
+ DP(NETIF_MSG_LINK,
+ "Link is up in %dMbps, is_duplex_full= %d\n",
+ vars->line_speed,
+ (vars->duplex == DUPLEX_FULL));
+
+ /* Check legacy speed AN resolution */
+ bnx2x_cl22_read(bp, phy,
+ 0x01,
+ &val);
+ if (val & (1<<5))
+ vars->link_status |=
+ LINK_STATUS_AUTO_NEGOTIATE_COMPLETE;
+ bnx2x_cl22_read(bp, phy,
+ 0x06,
+ &val);
+ if ((val & (1<<0)) == 0)
+ vars->link_status |=
+ LINK_STATUS_PARALLEL_DETECTION_USED;
+
+ DP(NETIF_MSG_LINK, "BCM54618SE: link speed is %d\n",
+ vars->line_speed);
+
+ /* Report whether EEE is resolved. */
+ bnx2x_cl22_read(bp, phy, MDIO_REG_GPHY_PHYID_LSB, &val);
+ if (val == MDIO_REG_GPHY_ID_54618SE) {
+ if (vars->link_status &
+ LINK_STATUS_AUTO_NEGOTIATE_COMPLETE)
+ val = 0;
+ else {
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_CL45_ADDR_REG,
+ MDIO_AN_DEVAD);
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_CL45_DATA_REG,
+ MDIO_REG_GPHY_EEE_RESOLVED);
+ bnx2x_cl22_write(bp, phy,
+ MDIO_REG_GPHY_CL45_ADDR_REG,
+ (0x1 << 14) | MDIO_AN_DEVAD);
+ bnx2x_cl22_read(bp, phy,
+ MDIO_REG_GPHY_CL45_DATA_REG,
+ &val);
+ }
+ DP(NETIF_MSG_LINK, "EEE resolution: 0x%x\n", val);
+ }
+
+ bnx2x_ext_phy_resolve_fc(phy, params, vars);
+ }
+ return link_up;
+}
+
+static void bnx2x_54618se_config_loopback(struct bnx2x_phy *phy,
+ struct link_params *params)
+{
+ struct bnx2x *bp = params->bp;
+ u16 val;
+ u32 umac_base = params->port ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
+
+ DP(NETIF_MSG_LINK, "2PMA/PMD ext_phy_loopback: 54618se\n");
+
+ /* Enable master/slave manual mmode and set to master */
+ /* mii write 9 [bits set 11 12] */
+ bnx2x_cl22_write(bp, phy, 0x09, 3<<11);
+
+ /* forced 1G and disable autoneg */
+ /* set val [mii read 0] */
+ /* set val [expr $val & [bits clear 6 12 13]] */
+ /* set val [expr $val | [bits set 6 8]] */
+ /* mii write 0 $val */
+ bnx2x_cl22_read(bp, phy, 0x00, &val);
+ val &= ~((1<<6) | (1<<12) | (1<<13));
+ val |= (1<<6) | (1<<8);
+ bnx2x_cl22_write(bp, phy, 0x00, val);
+
+ /* Set external loopback and Tx using 6dB coding */
+ /* mii write 0x18 7 */
+ /* set val [mii read 0x18] */
+ /* mii write 0x18 [expr $val | [bits set 10 15]] */
+ bnx2x_cl22_write(bp, phy, 0x18, 7);
+ bnx2x_cl22_read(bp, phy, 0x18, &val);
+ bnx2x_cl22_write(bp, phy, 0x18, val | (1<<10) | (1<<15));
+
+ /* This register opens the gate for the UMAC despite its name */
+ REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + params->port*4, 1);
+
+ /*
+ * Maximum Frame Length (RW). Defines a 14-Bit maximum frame
+ * length used by the MAC receive logic to check frames.
+ */
+ REG_WR(bp, umac_base + UMAC_REG_MAXFR, 0x2710);
+}
+
/******************************************************************/
/* SFX7101 PHY SECTION */
/******************************************************************/
@@ -6646,9 +10504,9 @@ static void bnx2x_7101_config_loopback(struct bnx2x_phy *phy,
MDIO_XS_DEVAD, MDIO_XS_SFX7101_XGXS_TEST1, 0x100);
}
-static u8 bnx2x_7101_config_init(struct bnx2x_phy *phy,
- struct link_params *params,
- struct link_vars *vars)
+static int bnx2x_7101_config_init(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
{
u16 fw_ver1, fw_ver2, val;
struct bnx2x *bp = params->bp;
@@ -6662,7 +10520,7 @@ static u8 bnx2x_7101_config_init(struct bnx2x_phy *phy,
bnx2x_wait_reset_complete(bp, phy, params);
bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x1);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_CTRL, 0x1);
DP(NETIF_MSG_LINK, "Setting the SFX7101 LED to blink on traffic\n");
bnx2x_cl45_write(bp, phy,
MDIO_PMA_DEVAD, MDIO_PMA_REG_7107_LED_CNTL, (1<<3));
@@ -6694,9 +10552,9 @@ static u8 bnx2x_7101_read_status(struct bnx2x_phy *phy,
u8 link_up;
u16 val1, val2;
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val2);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_STAT, &val2);
bnx2x_cl45_read(bp, phy,
- MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1);
+ MDIO_PMA_DEVAD, MDIO_PMA_LASI_STAT, &val1);
DP(NETIF_MSG_LINK, "10G-base-T LASI status 0x%x->0x%x\n",
val2, val1);
bnx2x_cl45_read(bp, phy,
@@ -6721,8 +10579,7 @@ static u8 bnx2x_7101_read_status(struct bnx2x_phy *phy,
return link_up;
}
-
-static u8 bnx2x_7101_format_ver(u32 spirom_ver, u8 *str, u16 *len)
+static int bnx2x_7101_format_ver(u32 spirom_ver, u8 *str, u16 *len)
{
if (*len < 5)
return -EINVAL;
@@ -6800,9 +10657,8 @@ static void bnx2x_7101_set_link_led(struct bnx2x_phy *phy,
static struct bnx2x_phy phy_null = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN,
.addr = 0,
- .flags = FLAGS_INIT_XGXS_FIRST,
.def_md_devad = 0,
- .reserved = 0,
+ .flags = FLAGS_INIT_XGXS_FIRST,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -6827,9 +10683,8 @@ static struct bnx2x_phy phy_null = {
static struct bnx2x_phy phy_serdes = {
.type = PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT,
.addr = 0xff,
- .flags = 0,
.def_md_devad = 0,
- .reserved = 0,
+ .flags = 0,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -6843,14 +10698,14 @@ static struct bnx2x_phy phy_serdes = {
SUPPORTED_Autoneg |
SUPPORTED_Pause |
SUPPORTED_Asym_Pause),
- .media_type = ETH_PHY_UNSPECIFIED,
+ .media_type = ETH_PHY_BASE_T,
.ver_addr = 0,
.req_flow_ctrl = 0,
.req_line_speed = 0,
.speed_cap_mask = 0,
.req_duplex = 0,
.rsrv = 0,
- .config_init = (config_init_t)bnx2x_init_serdes,
+ .config_init = (config_init_t)bnx2x_xgxs_config_init,
.read_status = (read_status_t)bnx2x_link_settings_status,
.link_reset = (link_reset_t)bnx2x_int_link_reset,
.config_loopback = (config_loopback_t)NULL,
@@ -6863,9 +10718,8 @@ static struct bnx2x_phy phy_serdes = {
static struct bnx2x_phy phy_xgxs = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT,
.addr = 0xff,
- .flags = 0,
.def_md_devad = 0,
- .reserved = 0,
+ .flags = 0,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -6880,14 +10734,14 @@ static struct bnx2x_phy phy_xgxs = {
SUPPORTED_Autoneg |
SUPPORTED_Pause |
SUPPORTED_Asym_Pause),
- .media_type = ETH_PHY_UNSPECIFIED,
+ .media_type = ETH_PHY_CX4,
.ver_addr = 0,
.req_flow_ctrl = 0,
.req_line_speed = 0,
.speed_cap_mask = 0,
.req_duplex = 0,
.rsrv = 0,
- .config_init = (config_init_t)bnx2x_init_xgxs,
+ .config_init = (config_init_t)bnx2x_xgxs_config_init,
.read_status = (read_status_t)bnx2x_link_settings_status,
.link_reset = (link_reset_t)bnx2x_int_link_reset,
.config_loopback = (config_loopback_t)bnx2x_set_xgxs_loopback,
@@ -6896,13 +10750,49 @@ static struct bnx2x_phy phy_xgxs = {
.set_link_led = (set_link_led_t)NULL,
.phy_specific_func = (phy_specific_func_t)NULL
};
+static struct bnx2x_phy phy_warpcore = {
+ .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT,
+ .addr = 0xff,
+ .def_md_devad = 0,
+ .flags = FLAGS_HW_LOCK_REQUIRED,
+ .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
+ .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
+ .mdio_ctrl = 0,
+ .supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_10000baseT_Full |
+ SUPPORTED_20000baseKR2_Full |
+ SUPPORTED_20000baseMLD2_Full |
+ SUPPORTED_FIBRE |
+ SUPPORTED_Autoneg |
+ SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause),
+ .media_type = ETH_PHY_UNSPECIFIED,
+ .ver_addr = 0,
+ .req_flow_ctrl = 0,
+ .req_line_speed = 0,
+ .speed_cap_mask = 0,
+ /* req_duplex = */0,
+ /* rsrv = */0,
+ .config_init = (config_init_t)bnx2x_warpcore_config_init,
+ .read_status = (read_status_t)bnx2x_warpcore_read_status,
+ .link_reset = (link_reset_t)bnx2x_warpcore_link_reset,
+ .config_loopback = (config_loopback_t)bnx2x_set_warpcore_loopback,
+ .format_fw_ver = (format_fw_ver_t)NULL,
+ .hw_reset = (hw_reset_t)bnx2x_warpcore_hw_reset,
+ .set_link_led = (set_link_led_t)NULL,
+ .phy_specific_func = (phy_specific_func_t)NULL
+};
+
static struct bnx2x_phy phy_7101 = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101,
.addr = 0xff,
- .flags = FLAGS_FAN_FAILURE_DET_REQ,
.def_md_devad = 0,
- .reserved = 0,
+ .flags = FLAGS_FAN_FAILURE_DET_REQ,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -6930,9 +10820,8 @@ static struct bnx2x_phy phy_7101 = {
static struct bnx2x_phy phy_8073 = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073,
.addr = 0xff,
- .flags = FLAGS_HW_LOCK_REQUIRED,
.def_md_devad = 0,
- .reserved = 0,
+ .flags = FLAGS_HW_LOCK_REQUIRED,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -6943,7 +10832,7 @@ static struct bnx2x_phy phy_8073 = {
SUPPORTED_Autoneg |
SUPPORTED_Pause |
SUPPORTED_Asym_Pause),
- .media_type = ETH_PHY_UNSPECIFIED,
+ .media_type = ETH_PHY_KR,
.ver_addr = 0,
.req_flow_ctrl = 0,
.req_line_speed = 0,
@@ -6962,9 +10851,8 @@ static struct bnx2x_phy phy_8073 = {
static struct bnx2x_phy phy_8705 = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705,
.addr = 0xff,
- .flags = FLAGS_INIT_XGXS_FIRST,
.def_md_devad = 0,
- .reserved = 0,
+ .flags = FLAGS_INIT_XGXS_FIRST,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -6991,9 +10879,8 @@ static struct bnx2x_phy phy_8705 = {
static struct bnx2x_phy phy_8706 = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706,
.addr = 0xff,
- .flags = FLAGS_INIT_XGXS_FIRST,
.def_md_devad = 0,
- .reserved = 0,
+ .flags = FLAGS_INIT_XGXS_FIRST,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -7022,10 +10909,9 @@ static struct bnx2x_phy phy_8706 = {
static struct bnx2x_phy phy_8726 = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726,
.addr = 0xff,
+ .def_md_devad = 0,
.flags = (FLAGS_HW_LOCK_REQUIRED |
FLAGS_INIT_XGXS_FIRST),
- .def_md_devad = 0,
- .reserved = 0,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -7035,7 +10921,7 @@ static struct bnx2x_phy phy_8726 = {
SUPPORTED_FIBRE |
SUPPORTED_Pause |
SUPPORTED_Asym_Pause),
- .media_type = ETH_PHY_SFP_FIBER,
+ .media_type = ETH_PHY_NOT_PRESENT,
.ver_addr = 0,
.req_flow_ctrl = 0,
.req_line_speed = 0,
@@ -7055,9 +10941,8 @@ static struct bnx2x_phy phy_8726 = {
static struct bnx2x_phy phy_8727 = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727,
.addr = 0xff,
- .flags = FLAGS_FAN_FAILURE_DET_REQ,
.def_md_devad = 0,
- .reserved = 0,
+ .flags = FLAGS_FAN_FAILURE_DET_REQ,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -7066,7 +10951,7 @@ static struct bnx2x_phy phy_8727 = {
SUPPORTED_FIBRE |
SUPPORTED_Pause |
SUPPORTED_Asym_Pause),
- .media_type = ETH_PHY_SFP_FIBER,
+ .media_type = ETH_PHY_NOT_PRESENT,
.ver_addr = 0,
.req_flow_ctrl = 0,
.req_line_speed = 0,
@@ -7085,10 +10970,9 @@ static struct bnx2x_phy phy_8727 = {
static struct bnx2x_phy phy_8481 = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481,
.addr = 0xff,
+ .def_md_devad = 0,
.flags = FLAGS_FAN_FAILURE_DET_REQ |
FLAGS_REARM_LATCH_SIGNAL,
- .def_md_devad = 0,
- .reserved = 0,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -7122,10 +11006,9 @@ static struct bnx2x_phy phy_8481 = {
static struct bnx2x_phy phy_84823 = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823,
.addr = 0xff,
+ .def_md_devad = 0,
.flags = FLAGS_FAN_FAILURE_DET_REQ |
FLAGS_REARM_LATCH_SIGNAL,
- .def_md_devad = 0,
- .reserved = 0,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -7159,16 +11042,13 @@ static struct bnx2x_phy phy_84823 = {
static struct bnx2x_phy phy_84833 = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833,
.addr = 0xff,
+ .def_md_devad = 0,
.flags = FLAGS_FAN_FAILURE_DET_REQ |
FLAGS_REARM_LATCH_SIGNAL,
- .def_md_devad = 0,
- .reserved = 0,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
- .supported = (SUPPORTED_10baseT_Half |
- SUPPORTED_10baseT_Full |
- SUPPORTED_100baseT_Half |
+ .supported = (SUPPORTED_100baseT_Half |
SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Full |
SUPPORTED_10000baseT_Full |
@@ -7188,11 +11068,44 @@ static struct bnx2x_phy phy_84833 = {
.link_reset = (link_reset_t)bnx2x_848x3_link_reset,
.config_loopback = (config_loopback_t)NULL,
.format_fw_ver = (format_fw_ver_t)bnx2x_848xx_format_ver,
- .hw_reset = (hw_reset_t)NULL,
+ .hw_reset = (hw_reset_t)bnx2x_84833_hw_reset_phy,
.set_link_led = (set_link_led_t)bnx2x_848xx_set_link_led,
.phy_specific_func = (phy_specific_func_t)NULL
};
+static struct bnx2x_phy phy_54618se = {
+ .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE,
+ .addr = 0xff,
+ .def_md_devad = 0,
+ .flags = FLAGS_INIT_XGXS_FIRST,
+ .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
+ .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
+ .mdio_ctrl = 0,
+ .supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_TP |
+ SUPPORTED_Autoneg |
+ SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause),
+ .media_type = ETH_PHY_BASE_T,
+ .ver_addr = 0,
+ .req_flow_ctrl = 0,
+ .req_line_speed = 0,
+ .speed_cap_mask = 0,
+ /* req_duplex = */0,
+ /* rsrv = */0,
+ .config_init = (config_init_t)bnx2x_54618se_config_init,
+ .read_status = (read_status_t)bnx2x_54618se_read_status,
+ .link_reset = (link_reset_t)bnx2x_54618se_link_reset,
+ .config_loopback = (config_loopback_t)bnx2x_54618se_config_loopback,
+ .format_fw_ver = (format_fw_ver_t)NULL,
+ .hw_reset = (hw_reset_t)NULL,
+ .set_link_led = (set_link_led_t)NULL,
+ .phy_specific_func = (phy_specific_func_t)NULL
+};
/*****************************************************************/
/* */
/* Populate the phy according. Main function: bnx2x_populate_phy */
@@ -7259,8 +11172,8 @@ static u32 bnx2x_get_ext_phy_config(struct bnx2x *bp, u32 shmem_base,
return ext_phy_config;
}
-static u8 bnx2x_populate_int_phy(struct bnx2x *bp, u32 shmem_base, u8 port,
- struct bnx2x_phy *phy)
+static int bnx2x_populate_int_phy(struct bnx2x *bp, u32 shmem_base, u8 port,
+ struct bnx2x_phy *phy)
{
u32 phy_addr;
u32 chip_id;
@@ -7269,22 +11182,107 @@ static u8 bnx2x_populate_int_phy(struct bnx2x *bp, u32 shmem_base, u8 port,
dev_info.port_feature_config[port].link_config)) &
PORT_FEATURE_CONNECTED_SWITCH_MASK);
chip_id = REG_RD(bp, MISC_REG_CHIP_NUM) << 16;
- switch (switch_cfg) {
- case SWITCH_CFG_1G:
+ DP(NETIF_MSG_LINK, ":chip_id = 0x%x\n", chip_id);
+ if (USES_WARPCORE(bp)) {
+ u32 serdes_net_if;
phy_addr = REG_RD(bp,
- NIG_REG_SERDES0_CTRL_PHY_ADDR +
- port * 0x10);
- *phy = phy_serdes;
- break;
- case SWITCH_CFG_10G:
- phy_addr = REG_RD(bp,
- NIG_REG_XGXS0_CTRL_PHY_ADDR +
- port * 0x18);
- *phy = phy_xgxs;
- break;
- default:
- DP(NETIF_MSG_LINK, "Invalid switch_cfg\n");
- return -EINVAL;
+ MISC_REG_WC0_CTRL_PHY_ADDR);
+ *phy = phy_warpcore;
+ if (REG_RD(bp, MISC_REG_PORT4MODE_EN_OVWR) == 0x3)
+ phy->flags |= FLAGS_4_PORT_MODE;
+ else
+ phy->flags &= ~FLAGS_4_PORT_MODE;
+ /* Check Dual mode */
+ serdes_net_if = (REG_RD(bp, shmem_base +
+ offsetof(struct shmem_region, dev_info.
+ port_hw_config[port].default_cfg)) &
+ PORT_HW_CFG_NET_SERDES_IF_MASK);
+ /*
+ * Set the appropriate supported and flags indications per
+ * interface type of the chip
+ */
+ switch (serdes_net_if) {
+ case PORT_HW_CFG_NET_SERDES_IF_SGMII:
+ phy->supported &= (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_FIBRE |
+ SUPPORTED_Autoneg |
+ SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ phy->media_type = ETH_PHY_BASE_T;
+ break;
+ case PORT_HW_CFG_NET_SERDES_IF_XFI:
+ phy->media_type = ETH_PHY_XFP_FIBER;
+ break;
+ case PORT_HW_CFG_NET_SERDES_IF_SFI:
+ phy->supported &= (SUPPORTED_1000baseT_Full |
+ SUPPORTED_10000baseT_Full |
+ SUPPORTED_FIBRE |
+ SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ phy->media_type = ETH_PHY_SFP_FIBER;
+ break;
+ case PORT_HW_CFG_NET_SERDES_IF_KR:
+ phy->media_type = ETH_PHY_KR;
+ phy->supported &= (SUPPORTED_1000baseT_Full |
+ SUPPORTED_10000baseT_Full |
+ SUPPORTED_FIBRE |
+ SUPPORTED_Autoneg |
+ SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ break;
+ case PORT_HW_CFG_NET_SERDES_IF_DXGXS:
+ phy->media_type = ETH_PHY_KR;
+ phy->flags |= FLAGS_WC_DUAL_MODE;
+ phy->supported &= (SUPPORTED_20000baseMLD2_Full |
+ SUPPORTED_FIBRE |
+ SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ break;
+ case PORT_HW_CFG_NET_SERDES_IF_KR2:
+ phy->media_type = ETH_PHY_KR;
+ phy->flags |= FLAGS_WC_DUAL_MODE;
+ phy->supported &= (SUPPORTED_20000baseKR2_Full |
+ SUPPORTED_FIBRE |
+ SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ break;
+ default:
+ DP(NETIF_MSG_LINK, "Unknown WC interface type 0x%x\n",
+ serdes_net_if);
+ break;
+ }
+
+ /*
+ * Enable MDC/MDIO work-around for E3 A0 since free running MDC
+ * was not set as expected. For B0, ECO will be enabled so there
+ * won't be an issue there
+ */
+ if (CHIP_REV(bp) == CHIP_REV_Ax)
+ phy->flags |= FLAGS_MDC_MDIO_WA;
+ else
+ phy->flags |= FLAGS_MDC_MDIO_WA_B0;
+ } else {
+ switch (switch_cfg) {
+ case SWITCH_CFG_1G:
+ phy_addr = REG_RD(bp,
+ NIG_REG_SERDES0_CTRL_PHY_ADDR +
+ port * 0x10);
+ *phy = phy_serdes;
+ break;
+ case SWITCH_CFG_10G:
+ phy_addr = REG_RD(bp,
+ NIG_REG_XGXS0_CTRL_PHY_ADDR +
+ port * 0x18);
+ *phy = phy_xgxs;
+ break;
+ default:
+ DP(NETIF_MSG_LINK, "Invalid switch_cfg\n");
+ return -EINVAL;
+ }
}
phy->addr = (u8)phy_addr;
phy->mdio_ctrl = bnx2x_get_emac_base(bp,
@@ -7302,12 +11300,12 @@ static u8 bnx2x_populate_int_phy(struct bnx2x *bp, u32 shmem_base, u8 port,
return 0;
}
-static u8 bnx2x_populate_ext_phy(struct bnx2x *bp,
- u8 phy_index,
- u32 shmem_base,
- u32 shmem2_base,
- u8 port,
- struct bnx2x_phy *phy)
+static int bnx2x_populate_ext_phy(struct bnx2x *bp,
+ u8 phy_index,
+ u32 shmem_base,
+ u32 shmem2_base,
+ u8 port,
+ struct bnx2x_phy *phy)
{
u32 ext_phy_config, phy_type, config2;
u32 mdc_mdio_access = SHARED_HW_CFG_MDC_MDIO_ACCESS1_BOTH;
@@ -7336,6 +11334,7 @@ static u8 bnx2x_populate_ext_phy(struct bnx2x *bp,
*phy = phy_8727;
phy->flags |= FLAGS_NOC;
break;
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8722:
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727:
mdc_mdio_access = SHARED_HW_CFG_MDC_MDIO_ACCESS1_EMAC1;
*phy = phy_8727;
@@ -7349,6 +11348,10 @@ static u8 bnx2x_populate_ext_phy(struct bnx2x *bp,
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833:
*phy = phy_84833;
break;
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54616:
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE:
+ *phy = phy_54618se;
+ break;
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101:
*phy = phy_7101;
break;
@@ -7410,10 +11413,10 @@ static u8 bnx2x_populate_ext_phy(struct bnx2x *bp,
return 0;
}
-static u8 bnx2x_populate_phy(struct bnx2x *bp, u8 phy_index, u32 shmem_base,
- u32 shmem2_base, u8 port, struct bnx2x_phy *phy)
+static int bnx2x_populate_phy(struct bnx2x *bp, u8 phy_index, u32 shmem_base,
+ u32 shmem2_base, u8 port, struct bnx2x_phy *phy)
{
- u8 status = 0;
+ int status = 0;
phy->type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN;
if (phy_index == INT_PHY)
return bnx2x_populate_int_phy(bp, shmem_base, port, phy);
@@ -7446,8 +11449,9 @@ static void bnx2x_phy_def_cfg(struct link_params *params,
dev_info.
port_hw_config[params->port].speed_capability_mask));
}
- DP(NETIF_MSG_LINK, "Default config phy idx %x cfg 0x%x speed_cap_mask"
- " 0x%x\n", phy_index, link_config, phy->speed_cap_mask);
+ DP(NETIF_MSG_LINK,
+ "Default config phy idx %x cfg 0x%x speed_cap_mask 0x%x\n",
+ phy_index, link_config, phy->speed_cap_mask);
phy->req_duplex = DUPLEX_FULL;
switch (link_config & PORT_FEATURE_LINK_SPEED_MASK) {
@@ -7527,10 +11531,10 @@ u32 bnx2x_phy_selection(struct link_params *params)
}
-u8 bnx2x_phy_probe(struct link_params *params)
+int bnx2x_phy_probe(struct link_params *params)
{
u8 phy_index, actual_phy_idx, link_cfg_idx;
- u32 phy_config_swapped;
+ u32 phy_config_swapped, sync_offset, media_types;
struct bnx2x *bp = params->bp;
struct bnx2x_phy *phy;
params->num_phys = 0;
@@ -7567,6 +11571,26 @@ u8 bnx2x_phy_probe(struct link_params *params)
if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN)
break;
+ sync_offset = params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[params->port].media_type);
+ media_types = REG_RD(bp, sync_offset);
+
+ /*
+ * Update media type for non-PMF sync only for the first time
+ * In case the media type changes afterwards, it will be updated
+ * using the update_status function
+ */
+ if ((media_types & (PORT_HW_CFG_MEDIA_TYPE_PHY0_MASK <<
+ (PORT_HW_CFG_MEDIA_TYPE_PHY1_SHIFT *
+ actual_phy_idx))) == 0) {
+ media_types |= ((phy->media_type &
+ PORT_HW_CFG_MEDIA_TYPE_PHY0_MASK) <<
+ (PORT_HW_CFG_MEDIA_TYPE_PHY1_SHIFT *
+ actual_phy_idx));
+ }
+ REG_WR(bp, sync_offset, media_types);
+
bnx2x_phy_def_cfg(params, phy, phy_index);
params->num_phys++;
}
@@ -7575,77 +11599,10 @@ u8 bnx2x_phy_probe(struct link_params *params)
return 0;
}
-static void set_phy_vars(struct link_params *params)
-{
- struct bnx2x *bp = params->bp;
- u8 actual_phy_idx, phy_index, link_cfg_idx;
- u8 phy_config_swapped = params->multi_phy_config &
- PORT_HW_CFG_PHY_SWAPPED_ENABLED;
- for (phy_index = INT_PHY; phy_index < params->num_phys;
- phy_index++) {
- link_cfg_idx = LINK_CONFIG_IDX(phy_index);
- actual_phy_idx = phy_index;
- if (phy_config_swapped) {
- if (phy_index == EXT_PHY1)
- actual_phy_idx = EXT_PHY2;
- else if (phy_index == EXT_PHY2)
- actual_phy_idx = EXT_PHY1;
- }
- params->phy[actual_phy_idx].req_flow_ctrl =
- params->req_flow_ctrl[link_cfg_idx];
-
- params->phy[actual_phy_idx].req_line_speed =
- params->req_line_speed[link_cfg_idx];
-
- params->phy[actual_phy_idx].speed_cap_mask =
- params->speed_cap_mask[link_cfg_idx];
-
- params->phy[actual_phy_idx].req_duplex =
- params->req_duplex[link_cfg_idx];
-
- DP(NETIF_MSG_LINK, "req_flow_ctrl %x, req_line_speed %x,"
- " speed_cap_mask %x\n",
- params->phy[actual_phy_idx].req_flow_ctrl,
- params->phy[actual_phy_idx].req_line_speed,
- params->phy[actual_phy_idx].speed_cap_mask);
- }
-}
-
-u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
+void bnx2x_init_bmac_loopback(struct link_params *params,
+ struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
- DP(NETIF_MSG_LINK, "Phy Initialization started\n");
- DP(NETIF_MSG_LINK, "(1) req_speed %d, req_flowctrl %d\n",
- params->req_line_speed[0], params->req_flow_ctrl[0]);
- DP(NETIF_MSG_LINK, "(2) req_speed %d, req_flowctrl %d\n",
- params->req_line_speed[1], params->req_flow_ctrl[1]);
- vars->link_status = 0;
- vars->phy_link_up = 0;
- vars->link_up = 0;
- vars->line_speed = 0;
- vars->duplex = DUPLEX_FULL;
- vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
- vars->mac_type = MAC_TYPE_NONE;
- vars->phy_flags = 0;
-
- /* disable attentions */
- bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + params->port*4,
- (NIG_MASK_XGXS0_LINK_STATUS |
- NIG_MASK_XGXS0_LINK10G |
- NIG_MASK_SERDES0_LINK_STATUS |
- NIG_MASK_MI_INT));
-
- bnx2x_emac_init(params, vars);
-
- if (params->num_phys == 0) {
- DP(NETIF_MSG_LINK, "No phy found for initialization !!\n");
- return -EINVAL;
- }
- set_phy_vars(params);
-
- DP(NETIF_MSG_LINK, "Num of phys on board: %d\n", params->num_phys);
- if (params->loopback_mode == LOOPBACK_BMAC) {
-
vars->link_up = 1;
vars->line_speed = SPEED_10000;
vars->duplex = DUPLEX_FULL;
@@ -7660,9 +11617,12 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
bnx2x_bmac_enable(params, vars, 1);
REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0);
+}
- } else if (params->loopback_mode == LOOPBACK_EMAC) {
-
+void bnx2x_init_emac_loopback(struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
vars->link_up = 1;
vars->line_speed = SPEED_1000;
vars->duplex = DUPLEX_FULL;
@@ -7676,29 +11636,80 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
bnx2x_emac_enable(params, vars, 1);
bnx2x_emac_program(params, vars);
REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0);
+}
+
+void bnx2x_init_xmac_loopback(struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ vars->link_up = 1;
+ if (!params->req_line_speed[0])
+ vars->line_speed = SPEED_10000;
+ else
+ vars->line_speed = params->req_line_speed[0];
+ vars->duplex = DUPLEX_FULL;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
+ vars->mac_type = MAC_TYPE_XMAC;
+ vars->phy_flags = PHY_XGXS_FLAG;
+ /*
+ * Set WC to loopback mode since link is required to provide clock
+ * to the XMAC in 20G mode
+ */
+ bnx2x_set_aer_mmd(params, &params->phy[0]);
+ bnx2x_warpcore_reset_lane(bp, &params->phy[0], 0);
+ params->phy[INT_PHY].config_loopback(
+ &params->phy[INT_PHY],
+ params);
+
+ bnx2x_xmac_enable(params, vars, 1);
+ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0);
+}
+
+void bnx2x_init_umac_loopback(struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ vars->link_up = 1;
+ vars->line_speed = SPEED_1000;
+ vars->duplex = DUPLEX_FULL;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
+ vars->mac_type = MAC_TYPE_UMAC;
+ vars->phy_flags = PHY_XGXS_FLAG;
+ bnx2x_umac_enable(params, vars, 1);
- } else if ((params->loopback_mode == LOOPBACK_XGXS) ||
- (params->loopback_mode == LOOPBACK_EXT_PHY)) {
+ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0);
+}
+void bnx2x_init_xgxs_loopback(struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
vars->link_up = 1;
vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
vars->duplex = DUPLEX_FULL;
- if (params->req_line_speed[0] == SPEED_1000) {
+ if (params->req_line_speed[0] == SPEED_1000)
vars->line_speed = SPEED_1000;
- vars->mac_type = MAC_TYPE_EMAC;
- } else {
+ else
vars->line_speed = SPEED_10000;
- vars->mac_type = MAC_TYPE_BMAC;
- }
+ if (!USES_WARPCORE(bp))
bnx2x_xgxs_deassert(params);
- bnx2x_link_initialize(params, vars);
+ bnx2x_link_initialize(params, vars);
- if (params->req_line_speed[0] == SPEED_1000) {
+ if (params->req_line_speed[0] == SPEED_1000) {
+ if (USES_WARPCORE(bp))
+ bnx2x_umac_enable(params, vars, 0);
+ else {
bnx2x_emac_program(params, vars);
bnx2x_emac_enable(params, vars, 0);
- } else
+ }
+ } else {
+ if (USES_WARPCORE(bp))
+ bnx2x_xmac_enable(params, vars, 0);
+ else
bnx2x_bmac_enable(params, vars, 0);
+ }
+
if (params->loopback_mode == LOOPBACK_XGXS) {
/* set 10G XGXS loopback */
params->phy[INT_PHY].config_loopback(
@@ -7718,24 +11729,76 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
}
REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0);
- bnx2x_set_led(params, vars,
- LED_MODE_OPER, vars->line_speed);
- } else
- /* No loopback */
- {
- if (params->switch_cfg == SWITCH_CFG_10G)
- bnx2x_xgxs_deassert(params);
- else
- bnx2x_serdes_deassert(bp, params->port);
+ bnx2x_set_led(params, vars, LED_MODE_OPER, vars->line_speed);
+}
+
+int bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ DP(NETIF_MSG_LINK, "Phy Initialization started\n");
+ DP(NETIF_MSG_LINK, "(1) req_speed %d, req_flowctrl %d\n",
+ params->req_line_speed[0], params->req_flow_ctrl[0]);
+ DP(NETIF_MSG_LINK, "(2) req_speed %d, req_flowctrl %d\n",
+ params->req_line_speed[1], params->req_flow_ctrl[1]);
+ vars->link_status = 0;
+ vars->phy_link_up = 0;
+ vars->link_up = 0;
+ vars->line_speed = 0;
+ vars->duplex = DUPLEX_FULL;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
+ vars->mac_type = MAC_TYPE_NONE;
+ vars->phy_flags = 0;
+
+ /* disable attentions */
+ bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + params->port*4,
+ (NIG_MASK_XGXS0_LINK_STATUS |
+ NIG_MASK_XGXS0_LINK10G |
+ NIG_MASK_SERDES0_LINK_STATUS |
+ NIG_MASK_MI_INT));
+
+ bnx2x_emac_init(params, vars);
+ if (params->num_phys == 0) {
+ DP(NETIF_MSG_LINK, "No phy found for initialization !!\n");
+ return -EINVAL;
+ }
+ set_phy_vars(params, vars);
+
+ DP(NETIF_MSG_LINK, "Num of phys on board: %d\n", params->num_phys);
+ switch (params->loopback_mode) {
+ case LOOPBACK_BMAC:
+ bnx2x_init_bmac_loopback(params, vars);
+ break;
+ case LOOPBACK_EMAC:
+ bnx2x_init_emac_loopback(params, vars);
+ break;
+ case LOOPBACK_XMAC:
+ bnx2x_init_xmac_loopback(params, vars);
+ break;
+ case LOOPBACK_UMAC:
+ bnx2x_init_umac_loopback(params, vars);
+ break;
+ case LOOPBACK_XGXS:
+ case LOOPBACK_EXT_PHY:
+ bnx2x_init_xgxs_loopback(params, vars);
+ break;
+ default:
+ if (!CHIP_IS_E3(bp)) {
+ if (params->switch_cfg == SWITCH_CFG_10G)
+ bnx2x_xgxs_deassert(params);
+ else
+ bnx2x_serdes_deassert(bp, params->port);
+ }
bnx2x_link_initialize(params, vars);
msleep(30);
bnx2x_link_int_enable(params);
+ break;
}
return 0;
}
-u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
- u8 reset_ext_phy)
+
+int bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
+ u8 reset_ext_phy)
{
struct bnx2x *bp = params->bp;
u8 phy_index, port = params->port, clear_latch_ind = 0;
@@ -7753,14 +11816,21 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 1);
/* disable nig egress interface */
- REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0);
- REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0);
+ if (!CHIP_IS_E3(bp)) {
+ REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0);
+ REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0);
+ }
/* Stop BigMac rx */
- bnx2x_bmac_rx_disable(bp, port);
-
+ if (!CHIP_IS_E3(bp))
+ bnx2x_bmac_rx_disable(bp, port);
+ else {
+ bnx2x_xmac_disable(params);
+ bnx2x_umac_disable(params);
+ }
/* disable emac */
- REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0);
+ if (!CHIP_IS_E3(bp))
+ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0);
msleep(10);
/* The PHY reset is controlled by GPIO 1
@@ -7770,12 +11840,16 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
bnx2x_set_led(params, vars, LED_MODE_OFF, 0);
if (reset_ext_phy) {
+ bnx2x_set_mdio_clk(bp, params->chip_id, port);
for (phy_index = EXT_PHY1; phy_index < params->num_phys;
phy_index++) {
- if (params->phy[phy_index].link_reset)
+ if (params->phy[phy_index].link_reset) {
+ bnx2x_set_aer_mmd(params,
+ &params->phy[phy_index]);
params->phy[phy_index].link_reset(
&params->phy[phy_index],
params);
+ }
if (params->phy[phy_index].flags &
FLAGS_REARM_LATCH_SIGNAL)
clear_latch_ind = 1;
@@ -7791,26 +11865,34 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
if (params->phy[INT_PHY].link_reset)
params->phy[INT_PHY].link_reset(
&params->phy[INT_PHY], params);
- /* reset BigMac */
- REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
- (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
/* disable nig ingress interface */
- REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0);
- REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0);
- REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0);
- REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0);
+ if (!CHIP_IS_E3(bp)) {
+ /* reset BigMac */
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
+ (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
+ REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0);
+ REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0);
+ } else {
+ u32 xmac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
+ bnx2x_set_xumac_nig(params, 0, 0);
+ if (REG_RD(bp, MISC_REG_RESET_REG_2) &
+ MISC_REGISTERS_RESET_REG_2_XMAC)
+ REG_WR(bp, xmac_base + XMAC_REG_CTRL,
+ XMAC_CTRL_REG_SOFT_RESET);
+ }
vars->link_up = 0;
+ vars->phy_flags = 0;
return 0;
}
/****************************************************************************/
/* Common function */
/****************************************************************************/
-static u8 bnx2x_8073_common_init_phy(struct bnx2x *bp,
- u32 shmem_base_path[],
- u32 shmem2_base_path[], u8 phy_index,
- u32 chip_id)
+static int bnx2x_8073_common_init_phy(struct bnx2x *bp,
+ u32 shmem_base_path[],
+ u32 shmem2_base_path[], u8 phy_index,
+ u32 chip_id)
{
struct bnx2x_phy phy[PORT_MAX];
struct bnx2x_phy *phy_blk[PORT_MAX];
@@ -7826,14 +11908,14 @@ static u8 bnx2x_8073_common_init_phy(struct bnx2x *bp,
for (port = PORT_MAX - 1; port >= PORT_0; port--) {
u32 shmem_base, shmem2_base;
/* In E2, same phy is using for port0 of the two paths */
- if (CHIP_IS_E2(bp)) {
- shmem_base = shmem_base_path[port];
- shmem2_base = shmem2_base_path[port];
- port_of_path = 0;
- } else {
+ if (CHIP_IS_E1x(bp)) {
shmem_base = shmem_base_path[0];
shmem2_base = shmem2_base_path[0];
port_of_path = port;
+ } else {
+ shmem_base = shmem_base_path[port];
+ shmem2_base = shmem2_base_path[port];
+ port_of_path = 0;
}
/* Extract the ext phy address for the port */
@@ -7877,10 +11959,10 @@ static u8 bnx2x_8073_common_init_phy(struct bnx2x *bp,
/* PART2 - Download firmware to both phys */
for (port = PORT_MAX - 1; port >= PORT_0; port--) {
- if (CHIP_IS_E2(bp))
- port_of_path = 0;
- else
+ if (CHIP_IS_E1x(bp))
port_of_path = port;
+ else
+ port_of_path = 0;
DP(NETIF_MSG_LINK, "Loading spirom for phy address 0x%x\n",
phy_blk[port]->addr);
@@ -7933,10 +12015,10 @@ static u8 bnx2x_8073_common_init_phy(struct bnx2x *bp,
}
return 0;
}
-static u8 bnx2x_8726_common_init_phy(struct bnx2x *bp,
- u32 shmem_base_path[],
- u32 shmem2_base_path[], u8 phy_index,
- u32 chip_id)
+static int bnx2x_8726_common_init_phy(struct bnx2x *bp,
+ u32 shmem_base_path[],
+ u32 shmem2_base_path[], u8 phy_index,
+ u32 chip_id)
{
u32 val;
s8 port;
@@ -7954,12 +12036,12 @@ static u8 bnx2x_8726_common_init_phy(struct bnx2x *bp,
u32 shmem_base, shmem2_base;
/* In E2, same phy is using for port0 of the two paths */
- if (CHIP_IS_E2(bp)) {
- shmem_base = shmem_base_path[port];
- shmem2_base = shmem2_base_path[port];
- } else {
+ if (CHIP_IS_E1x(bp)) {
shmem_base = shmem_base_path[0];
shmem2_base = shmem2_base_path[0];
+ } else {
+ shmem_base = shmem_base_path[port];
+ shmem2_base = shmem2_base_path[port];
}
/* Extract the ext phy address for the port */
if (bnx2x_populate_phy(bp, phy_index, shmem_base, shmem2_base,
@@ -8027,10 +12109,11 @@ static void bnx2x_get_ext_phy_reset_gpio(struct bnx2x *bp, u32 shmem_base,
break;
}
}
-static u8 bnx2x_8727_common_init_phy(struct bnx2x *bp,
- u32 shmem_base_path[],
- u32 shmem2_base_path[], u8 phy_index,
- u32 chip_id)
+
+static int bnx2x_8727_common_init_phy(struct bnx2x *bp,
+ u32 shmem_base_path[],
+ u32 shmem2_base_path[], u8 phy_index,
+ u32 chip_id)
{
s8 port, reset_gpio;
u32 swap_val, swap_override;
@@ -8067,14 +12150,14 @@ static u8 bnx2x_8727_common_init_phy(struct bnx2x *bp,
u32 shmem_base, shmem2_base;
/* In E2, same phy is using for port0 of the two paths */
- if (CHIP_IS_E2(bp)) {
- shmem_base = shmem_base_path[port];
- shmem2_base = shmem2_base_path[port];
- port_of_path = 0;
- } else {
+ if (CHIP_IS_E1x(bp)) {
shmem_base = shmem_base_path[0];
shmem2_base = shmem2_base_path[0];
port_of_path = port;
+ } else {
+ shmem_base = shmem_base_path[port];
+ shmem2_base = shmem2_base_path[port];
+ port_of_path = 0;
}
/* Extract the ext phy address for the port */
@@ -8109,25 +12192,29 @@ static u8 bnx2x_8727_common_init_phy(struct bnx2x *bp,
}
/* PART2 - Download firmware to both phys */
for (port = PORT_MAX - 1; port >= PORT_0; port--) {
- if (CHIP_IS_E2(bp))
- port_of_path = 0;
- else
+ if (CHIP_IS_E1x(bp))
port_of_path = port;
+ else
+ port_of_path = 0;
DP(NETIF_MSG_LINK, "Loading spirom for phy address 0x%x\n",
phy_blk[port]->addr);
if (bnx2x_8073_8727_external_rom_boot(bp, phy_blk[port],
port_of_path))
return -EINVAL;
+ /* Disable PHY transmitter output */
+ bnx2x_cl45_write(bp, phy_blk[port],
+ MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_TX_DISABLE, 1);
}
return 0;
}
-static u8 bnx2x_ext_phy_common_init(struct bnx2x *bp, u32 shmem_base_path[],
- u32 shmem2_base_path[], u8 phy_index,
- u32 ext_phy_type, u32 chip_id)
+static int bnx2x_ext_phy_common_init(struct bnx2x *bp, u32 shmem_base_path[],
+ u32 shmem2_base_path[], u8 phy_index,
+ u32 ext_phy_type, u32 chip_id)
{
- u8 rc = 0;
+ int rc = 0;
switch (ext_phy_type) {
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073:
@@ -8135,7 +12222,7 @@ static u8 bnx2x_ext_phy_common_init(struct bnx2x *bp, u32 shmem_base_path[],
shmem2_base_path,
phy_index, chip_id);
break;
-
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8722:
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727:
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727_NOC:
rc = bnx2x_8727_common_init_phy(bp, shmem_base_path,
@@ -8152,6 +12239,13 @@ static u8 bnx2x_ext_phy_common_init(struct bnx2x *bp, u32 shmem_base_path[],
shmem2_base_path,
phy_index, chip_id);
break;
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833:
+ /*
+ * GPIO3's are linked, and so both need to be toggled
+ * to obtain required 2us pulse.
+ */
+ rc = bnx2x_84833_common_init_phy(bp, shmem_base_path, chip_id);
+ break;
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE:
rc = -EINVAL;
break;
@@ -8169,15 +12263,21 @@ static u8 bnx2x_ext_phy_common_init(struct bnx2x *bp, u32 shmem_base_path[],
return rc;
}
-u8 bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base_path[],
- u32 shmem2_base_path[], u32 chip_id)
+int bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base_path[],
+ u32 shmem2_base_path[], u32 chip_id)
{
- u8 rc = 0;
- u32 phy_ver;
- u8 phy_index;
+ int rc = 0;
+ u32 phy_ver, val;
+ u8 phy_index = 0;
u32 ext_phy_type, ext_phy_config;
+ bnx2x_set_mdio_clk(bp, chip_id, PORT_0);
+ bnx2x_set_mdio_clk(bp, chip_id, PORT_1);
DP(NETIF_MSG_LINK, "Begin common phy init\n");
-
+ if (CHIP_IS_E3(bp)) {
+ /* Enable EPIO */
+ val = REG_RD(bp, MISC_REG_GEN_PURP_HWG);
+ REG_WR(bp, MISC_REG_GEN_PURP_HWG, val | 1);
+ }
/* Check if common init was already done */
phy_ver = REG_RD(bp, shmem_base_path[0] +
offsetof(struct shmem_region,
@@ -8203,6 +12303,167 @@ u8 bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base_path[],
return rc;
}
+static void bnx2x_check_over_curr(struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u32 cfg_pin;
+ u8 port = params->port;
+ u32 pin_val;
+
+ cfg_pin = (REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[port].e3_cmn_pin_cfg1)) &
+ PORT_HW_CFG_E3_OVER_CURRENT_MASK) >>
+ PORT_HW_CFG_E3_OVER_CURRENT_SHIFT;
+
+ /* Ignore check if no external input PIN available */
+ if (bnx2x_get_cfg_pin(bp, cfg_pin, &pin_val) != 0)
+ return;
+
+ if (!pin_val) {
+ if ((vars->phy_flags & PHY_OVER_CURRENT_FLAG) == 0) {
+ netdev_err(bp->dev, "Error: Power fault on Port %d has"
+ " been detected and the power to "
+ "that SFP+ module has been removed"
+ " to prevent failure of the card."
+ " Please remove the SFP+ module and"
+ " restart the system to clear this"
+ " error.\n",
+ params->port);
+ vars->phy_flags |= PHY_OVER_CURRENT_FLAG;
+ }
+ } else
+ vars->phy_flags &= ~PHY_OVER_CURRENT_FLAG;
+}
+
+static void bnx2x_analyze_link_error(struct link_params *params,
+ struct link_vars *vars, u32 lss_status)
+{
+ struct bnx2x *bp = params->bp;
+ /* Compare new value with previous value */
+ u8 led_mode;
+ u32 half_open_conn = (vars->phy_flags & PHY_HALF_OPEN_CONN_FLAG) > 0;
+
+ if ((lss_status ^ half_open_conn) == 0)
+ return;
+
+ /* If values differ */
+ DP(NETIF_MSG_LINK, "Link changed:%x %x->%x\n", vars->link_up,
+ half_open_conn, lss_status);
+
+ /*
+ * a. Update shmem->link_status accordingly
+ * b. Update link_vars->link_up
+ */
+ if (lss_status) {
+ DP(NETIF_MSG_LINK, "Remote Fault detected !!!\n");
+ vars->link_status &= ~LINK_STATUS_LINK_UP;
+ vars->link_up = 0;
+ vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG;
+ /*
+ * Set LED mode to off since the PHY doesn't know about these
+ * errors
+ */
+ led_mode = LED_MODE_OFF;
+ } else {
+ DP(NETIF_MSG_LINK, "Remote Fault cleared\n");
+ vars->link_status |= LINK_STATUS_LINK_UP;
+ vars->link_up = 1;
+ vars->phy_flags &= ~PHY_HALF_OPEN_CONN_FLAG;
+ led_mode = LED_MODE_OPER;
+ }
+ /* Update the LED according to the link state */
+ bnx2x_set_led(params, vars, led_mode, SPEED_10000);
+
+ /* Update link status in the shared memory */
+ bnx2x_update_mng(params, vars->link_status);
+
+ /* C. Trigger General Attention */
+ vars->periodic_flags |= PERIODIC_FLAGS_LINK_EVENT;
+ bnx2x_notify_link_changed(bp);
+}
+
+/******************************************************************************
+* Description:
+* This function checks for half opened connection change indication.
+* When such change occurs, it calls the bnx2x_analyze_link_error
+* to check if Remote Fault is set or cleared. Reception of remote fault
+* status message in the MAC indicates that the peer's MAC has detected
+* a fault, for example, due to break in the TX side of fiber.
+*
+******************************************************************************/
+static void bnx2x_check_half_open_conn(struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u32 lss_status = 0;
+ u32 mac_base;
+ /* In case link status is physically up @ 10G do */
+ if ((vars->phy_flags & PHY_PHYSICAL_LINK_FLAG) == 0)
+ return;
+
+ if (CHIP_IS_E3(bp) &&
+ (REG_RD(bp, MISC_REG_RESET_REG_2) &
+ (MISC_REGISTERS_RESET_REG_2_XMAC))) {
+ /* Check E3 XMAC */
+ /*
+ * Note that link speed cannot be queried here, since it may be
+ * zero while link is down. In case UMAC is active, LSS will
+ * simply not be set
+ */
+ mac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
+
+ /* Clear stick bits (Requires rising edge) */
+ REG_WR(bp, mac_base + XMAC_REG_CLEAR_RX_LSS_STATUS, 0);
+ REG_WR(bp, mac_base + XMAC_REG_CLEAR_RX_LSS_STATUS,
+ XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_LOCAL_FAULT_STATUS |
+ XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_REMOTE_FAULT_STATUS);
+ if (REG_RD(bp, mac_base + XMAC_REG_RX_LSS_STATUS))
+ lss_status = 1;
+
+ bnx2x_analyze_link_error(params, vars, lss_status);
+ } else if (REG_RD(bp, MISC_REG_RESET_REG_2) &
+ (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port)) {
+ /* Check E1X / E2 BMAC */
+ u32 lss_status_reg;
+ u32 wb_data[2];
+ mac_base = params->port ? NIG_REG_INGRESS_BMAC1_MEM :
+ NIG_REG_INGRESS_BMAC0_MEM;
+ /* Read BIGMAC_REGISTER_RX_LSS_STATUS */
+ if (CHIP_IS_E2(bp))
+ lss_status_reg = BIGMAC2_REGISTER_RX_LSS_STAT;
+ else
+ lss_status_reg = BIGMAC_REGISTER_RX_LSS_STATUS;
+
+ REG_RD_DMAE(bp, mac_base + lss_status_reg, wb_data, 2);
+ lss_status = (wb_data[0] > 0);
+
+ bnx2x_analyze_link_error(params, vars, lss_status);
+ }
+}
+
+void bnx2x_period_func(struct link_params *params, struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u16 phy_idx;
+ for (phy_idx = INT_PHY; phy_idx < MAX_PHYS; phy_idx++) {
+ if (params->phy[phy_idx].flags & FLAGS_TX_ERROR_CHECK) {
+ bnx2x_set_aer_mmd(params, &params->phy[phy_idx]);
+ bnx2x_check_half_open_conn(params, vars);
+ break;
+ }
+ }
+
+ if (CHIP_IS_E3(bp)) {
+ struct bnx2x_phy *phy = &params->phy[INT_PHY];
+ bnx2x_set_aer_mmd(params, phy);
+ bnx2x_check_over_curr(params, vars);
+ bnx2x_warpcore_config_runtime(phy, params, vars);
+ }
+
+}
+
u8 bnx2x_hw_lock_required(struct bnx2x *bp, u32 shmem_base, u32 shmem2_base)
{
u8 phy_index;
@@ -8245,7 +12506,15 @@ u8 bnx2x_fan_failure_det_req(struct bnx2x *bp,
void bnx2x_hw_reset_phy(struct link_params *params)
{
u8 phy_index;
- for (phy_index = EXT_PHY1; phy_index < MAX_PHYS;
+ struct bnx2x *bp = params->bp;
+ bnx2x_update_mng(params, 0);
+ bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + params->port*4,
+ (NIG_MASK_XGXS0_LINK_STATUS |
+ NIG_MASK_XGXS0_LINK10G |
+ NIG_MASK_SERDES0_LINK_STATUS |
+ NIG_MASK_MI_INT));
+
+ for (phy_index = INT_PHY; phy_index < MAX_PHYS;
phy_index++) {
if (params->phy[phy_index].hw_reset) {
params->phy[phy_index].hw_reset(
@@ -8255,3 +12524,72 @@ void bnx2x_hw_reset_phy(struct link_params *params)
}
}
}
+
+void bnx2x_init_mod_abs_int(struct bnx2x *bp, struct link_vars *vars,
+ u32 chip_id, u32 shmem_base, u32 shmem2_base,
+ u8 port)
+{
+ u8 gpio_num = 0xff, gpio_port = 0xff, phy_index;
+ u32 val;
+ u32 offset, aeu_mask, swap_val, swap_override, sync_offset;
+ if (CHIP_IS_E3(bp)) {
+ if (bnx2x_get_mod_abs_int_cfg(bp, chip_id,
+ shmem_base,
+ port,
+ &gpio_num,
+ &gpio_port) != 0)
+ return;
+ } else {
+ struct bnx2x_phy phy;
+ for (phy_index = EXT_PHY1; phy_index < MAX_PHYS;
+ phy_index++) {
+ if (bnx2x_populate_phy(bp, phy_index, shmem_base,
+ shmem2_base, port, &phy)
+ != 0) {
+ DP(NETIF_MSG_LINK, "populate phy failed\n");
+ return;
+ }
+ if (phy.type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) {
+ gpio_num = MISC_REGISTERS_GPIO_3;
+ gpio_port = port;
+ break;
+ }
+ }
+ }
+
+ if (gpio_num == 0xff)
+ return;
+
+ /* Set GPIO3 to trigger SFP+ module insertion/removal */
+ bnx2x_set_gpio(bp, gpio_num, MISC_REGISTERS_GPIO_INPUT_HI_Z, gpio_port);
+
+ swap_val = REG_RD(bp, NIG_REG_PORT_SWAP);
+ swap_override = REG_RD(bp, NIG_REG_STRAP_OVERRIDE);
+ gpio_port ^= (swap_val && swap_override);
+
+ vars->aeu_int_mask = AEU_INPUTS_ATTN_BITS_GPIO0_FUNCTION_0 <<
+ (gpio_num + (gpio_port << 2));
+
+ sync_offset = shmem_base +
+ offsetof(struct shmem_region,
+ dev_info.port_hw_config[port].aeu_int_mask);
+ REG_WR(bp, sync_offset, vars->aeu_int_mask);
+
+ DP(NETIF_MSG_LINK, "Setting MOD_ABS (GPIO%d_P%d) AEU to 0x%x\n",
+ gpio_num, gpio_port, vars->aeu_int_mask);
+
+ if (port == 0)
+ offset = MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0;
+ else
+ offset = MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0;
+
+ /* Open appropriate AEU for interrupts */
+ aeu_mask = REG_RD(bp, offset);
+ aeu_mask |= vars->aeu_int_mask;
+ REG_WR(bp, offset, aeu_mask);
+
+ /* Enable the GPIO to trigger interrupt */
+ val = REG_RD(bp, MISC_REG_GPIO_EVENT_EN);
+ val |= 1 << (gpio_num + (gpio_port << 2));
+ REG_WR(bp, MISC_REG_GPIO_EVENT_EN, val);
+}
diff --git a/drivers/net/bnx2x/bnx2x_link.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h
index 92f36b6950dc..2a46e633abe9 100644
--- a/drivers/net/bnx2x/bnx2x_link.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h
@@ -33,12 +33,13 @@
#define BNX2X_FLOW_CTRL_BOTH PORT_FEATURE_FLOW_CONTROL_BOTH
#define BNX2X_FLOW_CTRL_NONE PORT_FEATURE_FLOW_CONTROL_NONE
+#define NET_SERDES_IF_XFI 1
+#define NET_SERDES_IF_SFI 2
+#define NET_SERDES_IF_KR 3
+#define NET_SERDES_IF_DXGXS 4
+
#define SPEED_AUTO_NEG 0
-#define SPEED_12000 12000
-#define SPEED_12500 12500
-#define SPEED_13000 13000
-#define SPEED_15000 15000
-#define SPEED_16000 16000
+#define SPEED_20000 20000
#define SFP_EEPROM_VENDOR_NAME_ADDR 0x14
#define SFP_EEPROM_VENDOR_NAME_SIZE 16
@@ -46,6 +47,12 @@
#define SFP_EEPROM_VENDOR_OUI_SIZE 3
#define SFP_EEPROM_PART_NO_ADDR 0x28
#define SFP_EEPROM_PART_NO_SIZE 16
+#define SFP_EEPROM_REVISION_ADDR 0x38
+#define SFP_EEPROM_REVISION_SIZE 4
+#define SFP_EEPROM_SERIAL_ADDR 0x44
+#define SFP_EEPROM_SERIAL_SIZE 16
+#define SFP_EEPROM_DATE_ADDR 0x54 /* ASCII YYMMDD */
+#define SFP_EEPROM_DATE_SIZE 6
#define PWR_FLT_ERR_MSG_LEN 250
#define XGXS_EXT_PHY_TYPE(ext_phy_config) \
@@ -62,25 +69,26 @@
#define SINGLE_MEDIA(params) (params->num_phys == 2)
/* Dual Media board contains two external phy with different media */
#define DUAL_MEDIA(params) (params->num_phys == 3)
+
+#define FW_PARAM_PHY_ADDR_MASK 0x000000FF
+#define FW_PARAM_PHY_TYPE_MASK 0x0000FF00
+#define FW_PARAM_MDIO_CTRL_MASK 0xFFFF0000
#define FW_PARAM_MDIO_CTRL_OFFSET 16
+#define FW_PARAM_PHY_ADDR(fw_param) (fw_param & \
+ FW_PARAM_PHY_ADDR_MASK)
+#define FW_PARAM_PHY_TYPE(fw_param) (fw_param & \
+ FW_PARAM_PHY_TYPE_MASK)
+#define FW_PARAM_MDIO_CTRL(fw_param) ((fw_param & \
+ FW_PARAM_MDIO_CTRL_MASK) >> \
+ FW_PARAM_MDIO_CTRL_OFFSET)
#define FW_PARAM_SET(phy_addr, phy_type, mdio_access) \
(phy_addr | phy_type | mdio_access << FW_PARAM_MDIO_CTRL_OFFSET)
-#define PFC_BRB_MAC_PAUSE_XOFF_THRESHOLD_PAUSEABLE 170
-#define PFC_BRB_MAC_PAUSE_XOFF_THRESHOLD_NON_PAUSEABLE 0
-
-#define PFC_BRB_MAC_PAUSE_XON_THRESHOLD_PAUSEABLE 250
-#define PFC_BRB_MAC_PAUSE_XON_THRESHOLD_NON_PAUSEABLE 0
-
-#define PFC_BRB_MAC_FULL_XOFF_THRESHOLD_PAUSEABLE 10
-#define PFC_BRB_MAC_FULL_XOFF_THRESHOLD_NON_PAUSEABLE 90
-
-#define PFC_BRB_MAC_FULL_XON_THRESHOLD_PAUSEABLE 50
-#define PFC_BRB_MAC_FULL_XON_THRESHOLD_NON_PAUSEABLE 250
#define PFC_BRB_FULL_LB_XOFF_THRESHOLD 170
#define PFC_BRB_FULL_LB_XON_THRESHOLD 250
+#define MAXVAL(a, b) (((a) > (b)) ? (a) : (b))
/***********************************************************/
/* Structs */
/***********************************************************/
@@ -121,8 +129,8 @@ struct bnx2x_phy {
/* Loaded during init */
u8 addr;
-
- u8 flags;
+ u8 def_md_devad;
+ u16 flags;
/* Require HW lock */
#define FLAGS_HW_LOCK_REQUIRED (1<<0)
/* No Over-Current detection */
@@ -131,11 +139,15 @@ struct bnx2x_phy {
#define FLAGS_FAN_FAILURE_DET_REQ (1<<2)
/* Initialize first the XGXS and only then the phy itself */
#define FLAGS_INIT_XGXS_FIRST (1<<3)
+#define FLAGS_WC_DUAL_MODE (1<<4)
+#define FLAGS_4_PORT_MODE (1<<5)
#define FLAGS_REARM_LATCH_SIGNAL (1<<6)
#define FLAGS_SFP_NOT_APPROVED (1<<7)
+#define FLAGS_MDC_MDIO_WA (1<<8)
+#define FLAGS_DUMMY_READ (1<<9)
+#define FLAGS_MDC_MDIO_WA_B0 (1<<10)
+#define FLAGS_TX_ERROR_CHECK (1<<12)
- u8 def_md_devad;
- u8 reserved;
/* preemphasis values for the rx side */
u16 rx_preemphasis[4];
@@ -153,6 +165,8 @@ struct bnx2x_phy {
#define ETH_PHY_XFP_FIBER 0x2
#define ETH_PHY_DA_TWINAX 0x3
#define ETH_PHY_BASE_T 0x4
+#define ETH_PHY_KR 0xf0
+#define ETH_PHY_CX4 0xf1
#define ETH_PHY_NOT_PRESENT 0xff
/* The address in which version is located*/
@@ -238,6 +252,8 @@ struct link_params {
#define FEATURE_CONFIG_PFC_ENABLED (1<<1)
#define FEATURE_CONFIG_BC_SUPPORTS_OPT_MDL_VRFY (1<<2)
#define FEATURE_CONFIG_BC_SUPPORTS_DUAL_PHY_OPT_MDL_VRFY (1<<3)
+#define FEATURE_CONFIG_AUTOGREEEN_ENABLED (1<<9)
+#define FEATURE_CONFIG_BC_SUPPORTS_SFP_TX_DISABLED (1<<10)
/* Will be populated during common init */
struct bnx2x_phy phy[MAX_PHYS];
@@ -257,11 +273,18 @@ struct link_params {
/* Output parameters */
struct link_vars {
u8 phy_flags;
+#define PHY_XGXS_FLAG (1<<0)
+#define PHY_SGMII_FLAG (1<<1)
+#define PHY_PHYSICAL_LINK_FLAG (1<<2)
+#define PHY_HALF_OPEN_CONN_FLAG (1<<3)
+#define PHY_OVER_CURRENT_FLAG (1<<4)
u8 mac_type;
#define MAC_TYPE_NONE 0
#define MAC_TYPE_EMAC 1
#define MAC_TYPE_BMAC 2
+#define MAC_TYPE_UMAC 3
+#define MAC_TYPE_XMAC 4
u8 phy_link_up; /* internal phy link indication */
u8 link_up;
@@ -274,45 +297,55 @@ struct link_vars {
/* The same definitions as the shmem parameter */
u32 link_status;
+ u8 fault_detected;
+ u8 rsrv1;
+ u16 periodic_flags;
+#define PERIODIC_FLAGS_LINK_EVENT 0x0001
+
+ u32 aeu_int_mask;
+ u8 rx_tx_asic_rst;
+ u8 turn_to_run_wc_rt;
+ u16 rsrv2;
};
/***********************************************************/
/* Functions */
/***********************************************************/
-u8 bnx2x_phy_init(struct link_params *input, struct link_vars *output);
+int bnx2x_phy_init(struct link_params *params, struct link_vars *vars);
/* Reset the link. Should be called when driver or interface goes down
Before calling phy firmware upgrade, the reset_ext_phy should be set
to 0 */
-u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
- u8 reset_ext_phy);
+int bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
+ u8 reset_ext_phy);
/* bnx2x_link_update should be called upon link interrupt */
-u8 bnx2x_link_update(struct link_params *input, struct link_vars *output);
+int bnx2x_link_update(struct link_params *params, struct link_vars *vars);
/* use the following phy functions to read/write from external_phy
In order to use it to read/write internal phy registers, use
DEFAULT_PHY_DEV_ADDR as devad, and (_bank + (_addr & 0xf)) as
the register */
-u8 bnx2x_phy_read(struct link_params *params, u8 phy_addr,
- u8 devad, u16 reg, u16 *ret_val);
+int bnx2x_phy_read(struct link_params *params, u8 phy_addr,
+ u8 devad, u16 reg, u16 *ret_val);
+
+int bnx2x_phy_write(struct link_params *params, u8 phy_addr,
+ u8 devad, u16 reg, u16 val);
-u8 bnx2x_phy_write(struct link_params *params, u8 phy_addr,
- u8 devad, u16 reg, u16 val);
/* Reads the link_status from the shmem,
and update the link vars accordingly */
void bnx2x_link_status_update(struct link_params *input,
struct link_vars *output);
/* returns string representing the fw_version of the external phy */
-u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded,
- u8 *version, u16 len);
+int bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded,
+ u8 *version, u16 len);
/* Set/Unset the led
Basically, the CLC takes care of the led for the link, but in case one needs
to set/unset the led unnaturally, set the "mode" to LED_MODE_OPER to
blink the led, and LED_MODE_OFF to set the led off.*/
-u8 bnx2x_set_led(struct link_params *params, struct link_vars *vars,
- u8 mode, u32 speed);
+int bnx2x_set_led(struct link_params *params,
+ struct link_vars *vars, u8 mode, u32 speed);
#define LED_MODE_OFF 0
#define LED_MODE_ON 1
#define LED_MODE_OPER 2
@@ -324,12 +357,12 @@ void bnx2x_handle_module_detect_int(struct link_params *params);
/* Get the actual link status. In case it returns 0, link is up,
otherwise link is down*/
-u8 bnx2x_test_link(struct link_params *input, struct link_vars *vars,
- u8 is_serdes);
+int bnx2x_test_link(struct link_params *params, struct link_vars *vars,
+ u8 is_serdes);
/* One-time initialization for external phy after power up */
-u8 bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base_path[],
- u32 shmem2_base_path[], u32 chip_id);
+int bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base_path[],
+ u32 shmem2_base_path[], u32 chip_id);
/* Reset the external PHY using GPIO */
void bnx2x_ext_phy_hw_reset(struct bnx2x *bp, u8 port);
@@ -338,9 +371,9 @@ void bnx2x_ext_phy_hw_reset(struct bnx2x *bp, u8 port);
void bnx2x_sfx7101_sp_sw_reset(struct bnx2x *bp, struct bnx2x_phy *phy);
/* Read "byte_cnt" bytes from address "addr" from the SFP+ EEPROM */
-u8 bnx2x_read_sfp_module_eeprom(struct bnx2x_phy *phy,
- struct link_params *params, u16 addr,
- u8 byte_cnt, u8 *o_buf);
+int bnx2x_read_sfp_module_eeprom(struct bnx2x_phy *phy,
+ struct link_params *params, u16 addr,
+ u8 byte_cnt, u8 *o_buf);
void bnx2x_hw_reset_phy(struct link_params *params);
@@ -352,11 +385,28 @@ u8 bnx2x_hw_lock_required(struct bnx2x *bp, u32 shmem_base,
u32 bnx2x_phy_selection(struct link_params *params);
/* Probe the phys on board, and populate them in "params" */
-u8 bnx2x_phy_probe(struct link_params *params);
+int bnx2x_phy_probe(struct link_params *params);
+
/* Checks if fan failure detection is required on one of the phys on board */
u8 bnx2x_fan_failure_det_req(struct bnx2x *bp, u32 shmem_base,
u32 shmem2_base, u8 port);
+
+
+/* DCBX structs */
+
+/* Number of maximum COS per chip */
+#define DCBX_E2E3_MAX_NUM_COS (2)
+#define DCBX_E3B0_MAX_NUM_COS_PORT0 (6)
+#define DCBX_E3B0_MAX_NUM_COS_PORT1 (3)
+#define DCBX_E3B0_MAX_NUM_COS ( \
+ MAXVAL(DCBX_E3B0_MAX_NUM_COS_PORT0, \
+ DCBX_E3B0_MAX_NUM_COS_PORT1))
+
+#define DCBX_MAX_NUM_COS ( \
+ MAXVAL(DCBX_E3B0_MAX_NUM_COS, \
+ DCBX_E2E3_MAX_NUM_COS))
+
/* PFC port configuration params */
struct bnx2x_nig_brb_pfc_port_params {
/* NIG */
@@ -364,8 +414,8 @@ struct bnx2x_nig_brb_pfc_port_params {
u32 llfc_out_en;
u32 llfc_enable;
u32 pkt_priority_to_cos;
- u32 rx_cos0_priority_mask;
- u32 rx_cos1_priority_mask;
+ u8 num_of_rx_cos_priority_mask;
+ u32 rx_cos_priority_mask[DCBX_MAX_NUM_COS];
u32 llfc_high_priority_classes;
u32 llfc_low_priority_classes;
/* BRB */
@@ -373,27 +423,74 @@ struct bnx2x_nig_brb_pfc_port_params {
u32 cos1_pauseable;
};
+
+/* ETS port configuration params */
+struct bnx2x_ets_bw_params {
+ u8 bw;
+};
+
+struct bnx2x_ets_sp_params {
+ /**
+ * valid values are 0 - 5. 0 is highest strict priority.
+ * There can't be two COS's with the same pri.
+ */
+ u8 pri;
+};
+
+enum bnx2x_cos_state {
+ bnx2x_cos_state_strict = 0,
+ bnx2x_cos_state_bw = 1,
+};
+
+struct bnx2x_ets_cos_params {
+ enum bnx2x_cos_state state ;
+ union {
+ struct bnx2x_ets_bw_params bw_params;
+ struct bnx2x_ets_sp_params sp_params;
+ } params;
+};
+
+struct bnx2x_ets_params {
+ u8 num_of_cos; /* Number of valid COS entries*/
+ struct bnx2x_ets_cos_params cos[DCBX_MAX_NUM_COS];
+};
+
/**
* Used to update the PFC attributes in EMAC, BMAC, NIG and BRB
* when link is already up
*/
-void bnx2x_update_pfc(struct link_params *params,
+int bnx2x_update_pfc(struct link_params *params,
struct link_vars *vars,
struct bnx2x_nig_brb_pfc_port_params *pfc_params);
/* Used to configure the ETS to disable */
-void bnx2x_ets_disabled(struct link_params *params);
+int bnx2x_ets_disabled(struct link_params *params,
+ struct link_vars *vars);
/* Used to configure the ETS to BW limited */
void bnx2x_ets_bw_limit(const struct link_params *params, const u32 cos0_bw,
const u32 cos1_bw);
/* Used to configure the ETS to strict */
-u8 bnx2x_ets_strict(const struct link_params *params, const u8 strict_cos);
+int bnx2x_ets_strict(const struct link_params *params, const u8 strict_cos);
+
+/* Configure the COS to ETS according to BW and SP settings.*/
+int bnx2x_ets_e3b0_config(const struct link_params *params,
+ const struct link_vars *vars,
+ const struct bnx2x_ets_params *ets_params);
/* Read pfc statistic*/
void bnx2x_pfc_statistic(struct link_params *params, struct link_vars *vars,
u32 pfc_frames_sent[2],
u32 pfc_frames_received[2]);
+void bnx2x_init_mod_abs_int(struct bnx2x *bp, struct link_vars *vars,
+ u32 chip_id, u32 shmem_base, u32 shmem2_base,
+ u8 port);
+
+int bnx2x_sfp_module_detection(struct bnx2x_phy *phy,
+ struct link_params *params);
+
+void bnx2x_period_func(struct link_params *params, struct link_vars *vars);
+
#endif /* BNX2X_LINK_H */
diff --git a/drivers/net/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 74be989f51c5..2f6361e949f0 100644
--- a/drivers/net/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -15,6 +15,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -37,8 +39,10 @@
#include <linux/time.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
+#include <linux/if.h>
#include <linux/if_vlan.h>
#include <net/ip.h>
+#include <net/ipv6.h>
#include <net/tcp.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
@@ -51,12 +55,12 @@
#include <linux/stringify.h>
#include <linux/vmalloc.h>
-#define BNX2X_MAIN
#include "bnx2x.h"
#include "bnx2x_init.h"
#include "bnx2x_init_ops.h"
#include "bnx2x_cmn.h"
#include "bnx2x_dcb.h"
+#include "bnx2x_sp.h"
#include <linux/firmware.h>
#include "bnx2x_fw_file_hdr.h"
@@ -74,12 +78,14 @@
#define TX_TIMEOUT (5*HZ)
static char version[] __devinitdata =
- "Broadcom NetXtreme II 5771x 10Gigabit Ethernet Driver "
+ "Broadcom NetXtreme II 5771x/578xx 10/20-Gigabit Ethernet Driver "
DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("Eliezer Tamir");
MODULE_DESCRIPTION("Broadcom NetXtreme II "
- "BCM57710/57711/57711E/57712/57712E Driver");
+ "BCM57710/57711/57711E/"
+ "57712/57712_MF/57800/57800_MF/57810/57810_MF/"
+ "57840/57840_MF Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
MODULE_FIRMWARE(FW_FILE_NAME_E1);
@@ -100,9 +106,11 @@ static int disable_tpa;
module_param(disable_tpa, int, 0);
MODULE_PARM_DESC(disable_tpa, " Disable the TPA (LRO) feature");
+#define INT_MODE_INTx 1
+#define INT_MODE_MSI 2
static int int_mode;
module_param(int_mode, int, 0);
-MODULE_PARM_DESC(int_mode, " Force interrupt mode other then MSI-X "
+MODULE_PARM_DESC(int_mode, " Force interrupt mode other than MSI-X "
"(1 INT#x; 2 MSI)");
static int dropless_fc;
@@ -121,37 +129,87 @@ static int debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, " Default debug msglevel");
-static struct workqueue_struct *bnx2x_wq;
-#ifdef BCM_CNIC
-static u8 ALL_ENODE_MACS[] = {0x01, 0x10, 0x18, 0x01, 0x00, 0x01};
-#endif
+
+struct workqueue_struct *bnx2x_wq;
enum bnx2x_board_type {
BCM57710 = 0,
- BCM57711 = 1,
- BCM57711E = 2,
- BCM57712 = 3,
- BCM57712E = 4
+ BCM57711,
+ BCM57711E,
+ BCM57712,
+ BCM57712_MF,
+ BCM57800,
+ BCM57800_MF,
+ BCM57810,
+ BCM57810_MF,
+ BCM57840,
+ BCM57840_MF
};
/* indexed by board_type, above */
static struct {
char *name;
} board_info[] __devinitdata = {
- { "Broadcom NetXtreme II BCM57710 XGb" },
- { "Broadcom NetXtreme II BCM57711 XGb" },
- { "Broadcom NetXtreme II BCM57711E XGb" },
- { "Broadcom NetXtreme II BCM57712 XGb" },
- { "Broadcom NetXtreme II BCM57712E XGb" }
+ { "Broadcom NetXtreme II BCM57710 10 Gigabit PCIe [Everest]" },
+ { "Broadcom NetXtreme II BCM57711 10 Gigabit PCIe" },
+ { "Broadcom NetXtreme II BCM57711E 10 Gigabit PCIe" },
+ { "Broadcom NetXtreme II BCM57712 10 Gigabit Ethernet" },
+ { "Broadcom NetXtreme II BCM57712 10 Gigabit Ethernet Multi Function" },
+ { "Broadcom NetXtreme II BCM57800 10 Gigabit Ethernet" },
+ { "Broadcom NetXtreme II BCM57800 10 Gigabit Ethernet Multi Function" },
+ { "Broadcom NetXtreme II BCM57810 10 Gigabit Ethernet" },
+ { "Broadcom NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function" },
+ { "Broadcom NetXtreme II BCM57840 10/20 Gigabit Ethernet" },
+ { "Broadcom NetXtreme II BCM57840 10/20 Gigabit "
+ "Ethernet Multi Function"}
};
+#ifndef PCI_DEVICE_ID_NX2_57710
+#define PCI_DEVICE_ID_NX2_57710 CHIP_NUM_57710
+#endif
+#ifndef PCI_DEVICE_ID_NX2_57711
+#define PCI_DEVICE_ID_NX2_57711 CHIP_NUM_57711
+#endif
+#ifndef PCI_DEVICE_ID_NX2_57711E
+#define PCI_DEVICE_ID_NX2_57711E CHIP_NUM_57711E
+#endif
+#ifndef PCI_DEVICE_ID_NX2_57712
+#define PCI_DEVICE_ID_NX2_57712 CHIP_NUM_57712
+#endif
+#ifndef PCI_DEVICE_ID_NX2_57712_MF
+#define PCI_DEVICE_ID_NX2_57712_MF CHIP_NUM_57712_MF
+#endif
+#ifndef PCI_DEVICE_ID_NX2_57800
+#define PCI_DEVICE_ID_NX2_57800 CHIP_NUM_57800
+#endif
+#ifndef PCI_DEVICE_ID_NX2_57800_MF
+#define PCI_DEVICE_ID_NX2_57800_MF CHIP_NUM_57800_MF
+#endif
+#ifndef PCI_DEVICE_ID_NX2_57810
+#define PCI_DEVICE_ID_NX2_57810 CHIP_NUM_57810
+#endif
+#ifndef PCI_DEVICE_ID_NX2_57810_MF
+#define PCI_DEVICE_ID_NX2_57810_MF CHIP_NUM_57810_MF
+#endif
+#ifndef PCI_DEVICE_ID_NX2_57840
+#define PCI_DEVICE_ID_NX2_57840 CHIP_NUM_57840
+#endif
+#ifndef PCI_DEVICE_ID_NX2_57840_MF
+#define PCI_DEVICE_ID_NX2_57840_MF CHIP_NUM_57840_MF
+#endif
static DEFINE_PCI_DEVICE_TABLE(bnx2x_pci_tbl) = {
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57710), BCM57710 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57711), BCM57711 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57711E), BCM57711E },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712), BCM57712 },
- { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712E), BCM57712E },
+ { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712_MF), BCM57712_MF },
+ { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800), BCM57800 },
+ { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800_MF), BCM57800_MF },
+ { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810), BCM57810 },
+ { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810_MF), BCM57810_MF },
+ { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840), BCM57840 },
+ { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_MF), BCM57840_MF },
{ 0 }
};
@@ -168,48 +226,6 @@ static inline void __storm_memset_dma_mapping(struct bnx2x *bp,
REG_WR(bp, addr + 4, U64_HI(mapping));
}
-static inline void __storm_memset_fill(struct bnx2x *bp,
- u32 addr, size_t size, u32 val)
-{
- int i;
- for (i = 0; i < size/4; i++)
- REG_WR(bp, addr + (i * 4), val);
-}
-
-static inline void storm_memset_ustats_zero(struct bnx2x *bp,
- u8 port, u16 stat_id)
-{
- size_t size = sizeof(struct ustorm_per_client_stats);
-
- u32 addr = BAR_USTRORM_INTMEM +
- USTORM_PER_COUNTER_ID_STATS_OFFSET(port, stat_id);
-
- __storm_memset_fill(bp, addr, size, 0);
-}
-
-static inline void storm_memset_tstats_zero(struct bnx2x *bp,
- u8 port, u16 stat_id)
-{
- size_t size = sizeof(struct tstorm_per_client_stats);
-
- u32 addr = BAR_TSTRORM_INTMEM +
- TSTORM_PER_COUNTER_ID_STATS_OFFSET(port, stat_id);
-
- __storm_memset_fill(bp, addr, size, 0);
-}
-
-static inline void storm_memset_xstats_zero(struct bnx2x *bp,
- u8 port, u16 stat_id)
-{
- size_t size = sizeof(struct xstorm_per_client_stats);
-
- u32 addr = BAR_XSTRORM_INTMEM +
- XSTORM_PER_COUNTER_ID_STATS_OFFSET(port, stat_id);
-
- __storm_memset_fill(bp, addr, size, 0);
-}
-
-
static inline void storm_memset_spq_addr(struct bnx2x *bp,
dma_addr_t mapping, u16 abs_fid)
{
@@ -219,103 +235,6 @@ static inline void storm_memset_spq_addr(struct bnx2x *bp,
__storm_memset_dma_mapping(bp, addr, mapping);
}
-static inline void storm_memset_ov(struct bnx2x *bp, u16 ov, u16 abs_fid)
-{
- REG_WR16(bp, BAR_XSTRORM_INTMEM + XSTORM_E1HOV_OFFSET(abs_fid), ov);
-}
-
-static inline void storm_memset_func_cfg(struct bnx2x *bp,
- struct tstorm_eth_function_common_config *tcfg,
- u16 abs_fid)
-{
- size_t size = sizeof(struct tstorm_eth_function_common_config);
-
- u32 addr = BAR_TSTRORM_INTMEM +
- TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(abs_fid);
-
- __storm_memset_struct(bp, addr, size, (u32 *)tcfg);
-}
-
-static inline void storm_memset_xstats_flags(struct bnx2x *bp,
- struct stats_indication_flags *flags,
- u16 abs_fid)
-{
- size_t size = sizeof(struct stats_indication_flags);
-
- u32 addr = BAR_XSTRORM_INTMEM + XSTORM_STATS_FLAGS_OFFSET(abs_fid);
-
- __storm_memset_struct(bp, addr, size, (u32 *)flags);
-}
-
-static inline void storm_memset_tstats_flags(struct bnx2x *bp,
- struct stats_indication_flags *flags,
- u16 abs_fid)
-{
- size_t size = sizeof(struct stats_indication_flags);
-
- u32 addr = BAR_TSTRORM_INTMEM + TSTORM_STATS_FLAGS_OFFSET(abs_fid);
-
- __storm_memset_struct(bp, addr, size, (u32 *)flags);
-}
-
-static inline void storm_memset_ustats_flags(struct bnx2x *bp,
- struct stats_indication_flags *flags,
- u16 abs_fid)
-{
- size_t size = sizeof(struct stats_indication_flags);
-
- u32 addr = BAR_USTRORM_INTMEM + USTORM_STATS_FLAGS_OFFSET(abs_fid);
-
- __storm_memset_struct(bp, addr, size, (u32 *)flags);
-}
-
-static inline void storm_memset_cstats_flags(struct bnx2x *bp,
- struct stats_indication_flags *flags,
- u16 abs_fid)
-{
- size_t size = sizeof(struct stats_indication_flags);
-
- u32 addr = BAR_CSTRORM_INTMEM + CSTORM_STATS_FLAGS_OFFSET(abs_fid);
-
- __storm_memset_struct(bp, addr, size, (u32 *)flags);
-}
-
-static inline void storm_memset_xstats_addr(struct bnx2x *bp,
- dma_addr_t mapping, u16 abs_fid)
-{
- u32 addr = BAR_XSTRORM_INTMEM +
- XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(abs_fid);
-
- __storm_memset_dma_mapping(bp, addr, mapping);
-}
-
-static inline void storm_memset_tstats_addr(struct bnx2x *bp,
- dma_addr_t mapping, u16 abs_fid)
-{
- u32 addr = BAR_TSTRORM_INTMEM +
- TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(abs_fid);
-
- __storm_memset_dma_mapping(bp, addr, mapping);
-}
-
-static inline void storm_memset_ustats_addr(struct bnx2x *bp,
- dma_addr_t mapping, u16 abs_fid)
-{
- u32 addr = BAR_USTRORM_INTMEM +
- USTORM_ETH_STATS_QUERY_ADDR_OFFSET(abs_fid);
-
- __storm_memset_dma_mapping(bp, addr, mapping);
-}
-
-static inline void storm_memset_cstats_addr(struct bnx2x *bp,
- dma_addr_t mapping, u16 abs_fid)
-{
- u32 addr = BAR_CSTRORM_INTMEM +
- CSTORM_ETH_STATS_QUERY_ADDR_OFFSET(abs_fid);
-
- __storm_memset_dma_mapping(bp, addr, mapping);
-}
-
static inline void storm_memset_vf_to_pf(struct bnx2x *bp, u16 abs_fid,
u16 pf_id)
{
@@ -360,45 +279,6 @@ static inline void storm_memset_eq_prod(struct bnx2x *bp, u16 eq_prod,
REG_WR16(bp, addr, eq_prod);
}
-static inline void storm_memset_hc_timeout(struct bnx2x *bp, u8 port,
- u16 fw_sb_id, u8 sb_index,
- u8 ticks)
-{
-
- int index_offset = CHIP_IS_E2(bp) ?
- offsetof(struct hc_status_block_data_e2, index_data) :
- offsetof(struct hc_status_block_data_e1x, index_data);
- u32 addr = BAR_CSTRORM_INTMEM +
- CSTORM_STATUS_BLOCK_DATA_OFFSET(fw_sb_id) +
- index_offset +
- sizeof(struct hc_index_data)*sb_index +
- offsetof(struct hc_index_data, timeout);
- REG_WR8(bp, addr, ticks);
- DP(NETIF_MSG_HW, "port %x fw_sb_id %d sb_index %d ticks %d\n",
- port, fw_sb_id, sb_index, ticks);
-}
-static inline void storm_memset_hc_disable(struct bnx2x *bp, u8 port,
- u16 fw_sb_id, u8 sb_index,
- u8 disable)
-{
- u32 enable_flag = disable ? 0 : (1 << HC_INDEX_DATA_HC_ENABLED_SHIFT);
- int index_offset = CHIP_IS_E2(bp) ?
- offsetof(struct hc_status_block_data_e2, index_data) :
- offsetof(struct hc_status_block_data_e1x, index_data);
- u32 addr = BAR_CSTRORM_INTMEM +
- CSTORM_STATUS_BLOCK_DATA_OFFSET(fw_sb_id) +
- index_offset +
- sizeof(struct hc_index_data)*sb_index +
- offsetof(struct hc_index_data, flags);
- u16 flags = REG_RD16(bp, addr);
- /* clear and set */
- flags &= ~HC_INDEX_DATA_HC_ENABLED;
- flags |= enable_flag;
- REG_WR16(bp, addr, flags);
- DP(NETIF_MSG_HW, "port %x fw_sb_id %d sb_index %d disable %d\n",
- port, fw_sb_id, sb_index, disable);
-}
-
/* used only at init
* locking is done by mcp
*/
@@ -473,17 +353,15 @@ static void bnx2x_dp_dmae(struct bnx2x *bp, struct dmae_command *dmae,
default:
if (src_type == DMAE_CMD_SRC_PCI)
DP(msglvl, "DMAE: opcode 0x%08x\n"
- DP_LEVEL "src_addr [%x:%08x] len [%d * 4] "
- "dst_addr [none]\n"
- DP_LEVEL "comp_addr [%x:%08x] comp_val 0x%08x\n",
+ "src_addr [%x:%08x] len [%d * 4] dst_addr [none]\n"
+ "comp_addr [%x:%08x] comp_val 0x%08x\n",
dmae->opcode, dmae->src_addr_hi, dmae->src_addr_lo,
dmae->len, dmae->comp_addr_hi, dmae->comp_addr_lo,
dmae->comp_val);
else
DP(msglvl, "DMAE: opcode 0x%08x\n"
- DP_LEVEL "src_addr [%08x] len [%d * 4] "
- "dst_addr [none]\n"
- DP_LEVEL "comp_addr [%x:%08x] comp_val 0x%08x\n",
+ "src_addr [%08x] len [%d * 4] dst_addr [none]\n"
+ "comp_addr [%x:%08x] comp_val 0x%08x\n",
dmae->opcode, dmae->src_addr_lo >> 2,
dmae->len, dmae->comp_addr_hi, dmae->comp_addr_lo,
dmae->comp_val);
@@ -492,13 +370,6 @@ static void bnx2x_dp_dmae(struct bnx2x *bp, struct dmae_command *dmae,
}
-const u32 dmae_reg_go_c[] = {
- DMAE_REG_GO_C0, DMAE_REG_GO_C1, DMAE_REG_GO_C2, DMAE_REG_GO_C3,
- DMAE_REG_GO_C4, DMAE_REG_GO_C5, DMAE_REG_GO_C6, DMAE_REG_GO_C7,
- DMAE_REG_GO_C8, DMAE_REG_GO_C9, DMAE_REG_GO_C10, DMAE_REG_GO_C11,
- DMAE_REG_GO_C12, DMAE_REG_GO_C13, DMAE_REG_GO_C14, DMAE_REG_GO_C15
-};
-
/* copy command into DMAE command memory and set DMAE command go */
void bnx2x_post_dmae(struct bnx2x *bp, struct dmae_command *dmae, int idx)
{
@@ -537,8 +408,8 @@ u32 bnx2x_dmae_opcode(struct bnx2x *bp, u8 src_type, u8 dst_type,
opcode |= (DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET);
opcode |= (BP_PORT(bp) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0);
- opcode |= ((BP_E1HVN(bp) << DMAE_CMD_E1HVN_SHIFT) |
- (BP_E1HVN(bp) << DMAE_COMMAND_DST_VN_SHIFT));
+ opcode |= ((BP_VN(bp) << DMAE_CMD_E1HVN_SHIFT) |
+ (BP_VN(bp) << DMAE_COMMAND_DST_VN_SHIFT));
opcode |= (DMAE_COM_SET_ERR << DMAE_COMMAND_ERR_POLICY_SHIFT);
#ifdef __BIG_ENDIAN
@@ -579,7 +450,11 @@ static int bnx2x_issue_dmae_with_comp(struct bnx2x *bp,
bp->slowpath->wb_data[0], bp->slowpath->wb_data[1],
bp->slowpath->wb_data[2], bp->slowpath->wb_data[3]);
- /* lock the dmae channel */
+ /*
+ * Lock the dmae channel. Disable BHs to prevent a dead-lock
+ * as long as this code is called both from syscall context and
+ * from ndo_set_rx_mode() flow that may be called from BH.
+ */
spin_lock_bh(&bp->dmae_lock);
/* reset completion */
@@ -834,9 +709,9 @@ static int bnx2x_mc_assert(struct bnx2x *bp)
return rc;
}
-static void bnx2x_fw_dump(struct bnx2x *bp)
+void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl)
{
- u32 addr;
+ u32 addr, val;
u32 mark, offset;
__be32 data[9];
int word;
@@ -845,6 +720,14 @@ static void bnx2x_fw_dump(struct bnx2x *bp)
BNX2X_ERR("NO MCP - can not dump\n");
return;
}
+ netdev_printk(lvl, bp->dev, "bc %d.%d.%d\n",
+ (bp->common.bc_ver & 0xff0000) >> 16,
+ (bp->common.bc_ver & 0xff00) >> 8,
+ (bp->common.bc_ver & 0xff));
+
+ val = REG_RD(bp, MCP_REG_MCPR_CPU_PROGRAM_COUNTER);
+ if (val == REG_RD(bp, MCP_REG_MCPR_CPU_PROGRAM_COUNTER))
+ printk("%s" "MCP PC at 0x%x\n", lvl, val);
if (BP_PATH(bp) == 0)
trace_shmem_base = bp->common.shmem_base;
@@ -854,9 +737,9 @@ static void bnx2x_fw_dump(struct bnx2x *bp)
mark = REG_RD(bp, addr);
mark = (CHIP_IS_E1x(bp) ? MCP_REG_MCPR_SCRATCH : MCP_A_REG_MCPR_SCRATCH)
+ ((mark + 0x3) & ~0x3) - 0x08000000;
- pr_err("begin fw dump (mark 0x%x)\n", mark);
+ printk("%s" "begin fw dump (mark 0x%x)\n", lvl, mark);
- pr_err("");
+ printk("%s", lvl);
for (offset = mark; offset <= trace_shmem_base; offset += 0x8*4) {
for (word = 0; word < 8; word++)
data[word] = htonl(REG_RD(bp, offset + 4*word));
@@ -869,7 +752,12 @@ static void bnx2x_fw_dump(struct bnx2x *bp)
data[8] = 0x0;
pr_cont("%s", (char *)data);
}
- pr_err("end of fw dump\n");
+ printk("%s" "end of fw dump\n", lvl);
+}
+
+static inline void bnx2x_fw_dump(struct bnx2x *bp)
+{
+ bnx2x_fw_dump_lvl(bp, KERN_ERR);
}
void bnx2x_panic_dump(struct bnx2x *bp)
@@ -880,6 +768,7 @@ void bnx2x_panic_dump(struct bnx2x *bp)
int func = BP_FUNC(bp);
#ifdef BNX2X_STOP_ON_ERROR
u16 start = 0, end = 0;
+ u8 cos;
#endif
bp->stats_state = STATS_STATE_DISABLED;
@@ -890,9 +779,9 @@ void bnx2x_panic_dump(struct bnx2x *bp)
/* Indices */
/* Common */
BNX2X_ERR("def_idx(0x%x) def_att_idx(0x%x) attn_state(0x%x)"
- " spq_prod_idx(0x%x)\n",
- bp->def_idx, bp->def_att_idx,
- bp->attn_state, bp->spq_prod_idx);
+ " spq_prod_idx(0x%x) next_stats_cnt(0x%x)\n",
+ bp->def_idx, bp->def_att_idx, bp->attn_state,
+ bp->spq_prod_idx, bp->stats_counter);
BNX2X_ERR("DSB: attn bits(0x%x) ack(0x%x) id(0x%x) idx(0x%x)\n",
bp->def_status_blk->atten_status_block.attn_bits,
bp->def_status_blk->atten_status_block.attn_bits_ack,
@@ -901,23 +790,22 @@ void bnx2x_panic_dump(struct bnx2x *bp)
BNX2X_ERR(" def (");
for (i = 0; i < HC_SP_SB_MAX_INDICES; i++)
pr_cont("0x%x%s",
- bp->def_status_blk->sp_sb.index_values[i],
- (i == HC_SP_SB_MAX_INDICES - 1) ? ") " : " ");
+ bp->def_status_blk->sp_sb.index_values[i],
+ (i == HC_SP_SB_MAX_INDICES - 1) ? ") " : " ");
for (i = 0; i < sizeof(struct hc_sp_status_block_data)/sizeof(u32); i++)
*((u32 *)&sp_sb_data + i) = REG_RD(bp, BAR_CSTRORM_INTMEM +
CSTORM_SP_STATUS_BLOCK_DATA_OFFSET(func) +
i*sizeof(u32));
- pr_cont("igu_sb_id(0x%x) igu_seg_id (0x%x) "
- "pf_id(0x%x) vnic_id(0x%x) "
- "vf_id(0x%x) vf_valid (0x%x)\n",
+ pr_cont("igu_sb_id(0x%x) igu_seg_id(0x%x) pf_id(0x%x) vnic_id(0x%x) vf_id(0x%x) vf_valid (0x%x) state(0x%x)\n",
sp_sb_data.igu_sb_id,
sp_sb_data.igu_seg_id,
sp_sb_data.p_func.pf_id,
sp_sb_data.p_func.vnic_id,
sp_sb_data.p_func.vf_id,
- sp_sb_data.p_func.vf_valid);
+ sp_sb_data.p_func.vf_valid,
+ sp_sb_data.state);
for_each_eth_queue(bp, i) {
@@ -926,15 +814,16 @@ void bnx2x_panic_dump(struct bnx2x *bp)
struct hc_status_block_data_e2 sb_data_e2;
struct hc_status_block_data_e1x sb_data_e1x;
struct hc_status_block_sm *hc_sm_p =
- CHIP_IS_E2(bp) ?
- sb_data_e2.common.state_machine :
- sb_data_e1x.common.state_machine;
+ CHIP_IS_E1x(bp) ?
+ sb_data_e1x.common.state_machine :
+ sb_data_e2.common.state_machine;
struct hc_index_data *hc_index_p =
- CHIP_IS_E2(bp) ?
- sb_data_e2.index_data :
- sb_data_e1x.index_data;
- int data_size;
+ CHIP_IS_E1x(bp) ?
+ sb_data_e1x.index_data :
+ sb_data_e2.index_data;
+ u8 data_size, cos;
u32 *sb_data_p;
+ struct bnx2x_fp_txdata txdata;
/* Rx */
BNX2X_ERR("fp%d: rx_bd_prod(0x%x) rx_bd_cons(0x%x)"
@@ -949,14 +838,20 @@ void bnx2x_panic_dump(struct bnx2x *bp)
le16_to_cpu(fp->fp_hc_idx));
/* Tx */
- BNX2X_ERR("fp%d: tx_pkt_prod(0x%x) tx_pkt_cons(0x%x)"
- " tx_bd_prod(0x%x) tx_bd_cons(0x%x)"
- " *tx_cons_sb(0x%x)\n",
- i, fp->tx_pkt_prod, fp->tx_pkt_cons, fp->tx_bd_prod,
- fp->tx_bd_cons, le16_to_cpu(*fp->tx_cons_sb));
+ for_each_cos_in_tx_queue(fp, cos)
+ {
+ txdata = fp->txdata[cos];
+ BNX2X_ERR("fp%d: tx_pkt_prod(0x%x) tx_pkt_cons(0x%x)"
+ " tx_bd_prod(0x%x) tx_bd_cons(0x%x)"
+ " *tx_cons_sb(0x%x)\n",
+ i, txdata.tx_pkt_prod,
+ txdata.tx_pkt_cons, txdata.tx_bd_prod,
+ txdata.tx_bd_cons,
+ le16_to_cpu(*txdata.tx_cons_sb));
+ }
- loop = CHIP_IS_E2(bp) ?
- HC_SB_MAX_INDICES_E2 : HC_SB_MAX_INDICES_E1X;
+ loop = CHIP_IS_E1x(bp) ?
+ HC_SB_MAX_INDICES_E1X : HC_SB_MAX_INDICES_E2;
/* host sb data */
@@ -976,35 +871,39 @@ void bnx2x_panic_dump(struct bnx2x *bp)
fp->sb_index_values[j],
(j == loop - 1) ? ")" : " ");
/* fw sb data */
- data_size = CHIP_IS_E2(bp) ?
- sizeof(struct hc_status_block_data_e2) :
- sizeof(struct hc_status_block_data_e1x);
+ data_size = CHIP_IS_E1x(bp) ?
+ sizeof(struct hc_status_block_data_e1x) :
+ sizeof(struct hc_status_block_data_e2);
data_size /= sizeof(u32);
- sb_data_p = CHIP_IS_E2(bp) ?
- (u32 *)&sb_data_e2 :
- (u32 *)&sb_data_e1x;
+ sb_data_p = CHIP_IS_E1x(bp) ?
+ (u32 *)&sb_data_e1x :
+ (u32 *)&sb_data_e2;
/* copy sb data in here */
for (j = 0; j < data_size; j++)
*(sb_data_p + j) = REG_RD(bp, BAR_CSTRORM_INTMEM +
CSTORM_STATUS_BLOCK_DATA_OFFSET(fp->fw_sb_id) +
j * sizeof(u32));
- if (CHIP_IS_E2(bp)) {
- pr_cont("pf_id(0x%x) vf_id (0x%x) vf_valid(0x%x) "
- "vnic_id(0x%x) same_igu_sb_1b(0x%x)\n",
+ if (!CHIP_IS_E1x(bp)) {
+ pr_cont("pf_id(0x%x) vf_id(0x%x) vf_valid(0x%x) "
+ "vnic_id(0x%x) same_igu_sb_1b(0x%x) "
+ "state(0x%x)\n",
sb_data_e2.common.p_func.pf_id,
sb_data_e2.common.p_func.vf_id,
sb_data_e2.common.p_func.vf_valid,
sb_data_e2.common.p_func.vnic_id,
- sb_data_e2.common.same_igu_sb_1b);
+ sb_data_e2.common.same_igu_sb_1b,
+ sb_data_e2.common.state);
} else {
- pr_cont("pf_id(0x%x) vf_id (0x%x) vf_valid(0x%x) "
- "vnic_id(0x%x) same_igu_sb_1b(0x%x)\n",
+ pr_cont("pf_id(0x%x) vf_id(0x%x) vf_valid(0x%x) "
+ "vnic_id(0x%x) same_igu_sb_1b(0x%x) "
+ "state(0x%x)\n",
sb_data_e1x.common.p_func.pf_id,
sb_data_e1x.common.p_func.vf_id,
sb_data_e1x.common.p_func.vf_valid,
sb_data_e1x.common.p_func.vnic_id,
- sb_data_e1x.common.same_igu_sb_1b);
+ sb_data_e1x.common.same_igu_sb_1b,
+ sb_data_e1x.common.state);
}
/* SB_SMs data */
@@ -1068,23 +967,31 @@ void bnx2x_panic_dump(struct bnx2x *bp)
/* Tx */
for_each_tx_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
+ for_each_cos_in_tx_queue(fp, cos) {
+ struct bnx2x_fp_txdata *txdata = &fp->txdata[cos];
+
+ start = TX_BD(le16_to_cpu(*txdata->tx_cons_sb) - 10);
+ end = TX_BD(le16_to_cpu(*txdata->tx_cons_sb) + 245);
+ for (j = start; j != end; j = TX_BD(j + 1)) {
+ struct sw_tx_bd *sw_bd =
+ &txdata->tx_buf_ring[j];
+
+ BNX2X_ERR("fp%d: txdata %d, "
+ "packet[%x]=[%p,%x]\n",
+ i, cos, j, sw_bd->skb,
+ sw_bd->first_bd);
+ }
- start = TX_BD(le16_to_cpu(*fp->tx_cons_sb) - 10);
- end = TX_BD(le16_to_cpu(*fp->tx_cons_sb) + 245);
- for (j = start; j != end; j = TX_BD(j + 1)) {
- struct sw_tx_bd *sw_bd = &fp->tx_buf_ring[j];
-
- BNX2X_ERR("fp%d: packet[%x]=[%p,%x]\n",
- i, j, sw_bd->skb, sw_bd->first_bd);
- }
-
- start = TX_BD(fp->tx_bd_cons - 10);
- end = TX_BD(fp->tx_bd_cons + 254);
- for (j = start; j != end; j = TX_BD(j + 1)) {
- u32 *tx_bd = (u32 *)&fp->tx_desc_ring[j];
+ start = TX_BD(txdata->tx_bd_cons - 10);
+ end = TX_BD(txdata->tx_bd_cons + 254);
+ for (j = start; j != end; j = TX_BD(j + 1)) {
+ u32 *tx_bd = (u32 *)&txdata->tx_desc_ring[j];
- BNX2X_ERR("fp%d: tx_bd[%x]=[%x:%x:%x:%x]\n",
- i, j, tx_bd[0], tx_bd[1], tx_bd[2], tx_bd[3]);
+ BNX2X_ERR("fp%d: txdata %d, tx_bd[%x]="
+ "[%x:%x:%x:%x]\n",
+ i, cos, j, tx_bd[0], tx_bd[1],
+ tx_bd[2], tx_bd[3]);
+ }
}
}
#endif
@@ -1093,6 +1000,373 @@ void bnx2x_panic_dump(struct bnx2x *bp)
BNX2X_ERR("end crash dump -----------------\n");
}
+/*
+ * FLR Support for E2
+ *
+ * bnx2x_pf_flr_clnup() is called during nic_load in the per function HW
+ * initialization.
+ */
+#define FLR_WAIT_USEC 10000 /* 10 miliseconds */
+#define FLR_WAIT_INTERAVAL 50 /* usec */
+#define FLR_POLL_CNT (FLR_WAIT_USEC/FLR_WAIT_INTERAVAL) /* 200 */
+
+struct pbf_pN_buf_regs {
+ int pN;
+ u32 init_crd;
+ u32 crd;
+ u32 crd_freed;
+};
+
+struct pbf_pN_cmd_regs {
+ int pN;
+ u32 lines_occup;
+ u32 lines_freed;
+};
+
+static void bnx2x_pbf_pN_buf_flushed(struct bnx2x *bp,
+ struct pbf_pN_buf_regs *regs,
+ u32 poll_count)
+{
+ u32 init_crd, crd, crd_start, crd_freed, crd_freed_start;
+ u32 cur_cnt = poll_count;
+
+ crd_freed = crd_freed_start = REG_RD(bp, regs->crd_freed);
+ crd = crd_start = REG_RD(bp, regs->crd);
+ init_crd = REG_RD(bp, regs->init_crd);
+
+ DP(BNX2X_MSG_SP, "INIT CREDIT[%d] : %x\n", regs->pN, init_crd);
+ DP(BNX2X_MSG_SP, "CREDIT[%d] : s:%x\n", regs->pN, crd);
+ DP(BNX2X_MSG_SP, "CREDIT_FREED[%d]: s:%x\n", regs->pN, crd_freed);
+
+ while ((crd != init_crd) && ((u32)SUB_S32(crd_freed, crd_freed_start) <
+ (init_crd - crd_start))) {
+ if (cur_cnt--) {
+ udelay(FLR_WAIT_INTERAVAL);
+ crd = REG_RD(bp, regs->crd);
+ crd_freed = REG_RD(bp, regs->crd_freed);
+ } else {
+ DP(BNX2X_MSG_SP, "PBF tx buffer[%d] timed out\n",
+ regs->pN);
+ DP(BNX2X_MSG_SP, "CREDIT[%d] : c:%x\n",
+ regs->pN, crd);
+ DP(BNX2X_MSG_SP, "CREDIT_FREED[%d]: c:%x\n",
+ regs->pN, crd_freed);
+ break;
+ }
+ }
+ DP(BNX2X_MSG_SP, "Waited %d*%d usec for PBF tx buffer[%d]\n",
+ poll_count-cur_cnt, FLR_WAIT_INTERAVAL, regs->pN);
+}
+
+static void bnx2x_pbf_pN_cmd_flushed(struct bnx2x *bp,
+ struct pbf_pN_cmd_regs *regs,
+ u32 poll_count)
+{
+ u32 occup, to_free, freed, freed_start;
+ u32 cur_cnt = poll_count;
+
+ occup = to_free = REG_RD(bp, regs->lines_occup);
+ freed = freed_start = REG_RD(bp, regs->lines_freed);
+
+ DP(BNX2X_MSG_SP, "OCCUPANCY[%d] : s:%x\n", regs->pN, occup);
+ DP(BNX2X_MSG_SP, "LINES_FREED[%d] : s:%x\n", regs->pN, freed);
+
+ while (occup && ((u32)SUB_S32(freed, freed_start) < to_free)) {
+ if (cur_cnt--) {
+ udelay(FLR_WAIT_INTERAVAL);
+ occup = REG_RD(bp, regs->lines_occup);
+ freed = REG_RD(bp, regs->lines_freed);
+ } else {
+ DP(BNX2X_MSG_SP, "PBF cmd queue[%d] timed out\n",
+ regs->pN);
+ DP(BNX2X_MSG_SP, "OCCUPANCY[%d] : s:%x\n",
+ regs->pN, occup);
+ DP(BNX2X_MSG_SP, "LINES_FREED[%d] : s:%x\n",
+ regs->pN, freed);
+ break;
+ }
+ }
+ DP(BNX2X_MSG_SP, "Waited %d*%d usec for PBF cmd queue[%d]\n",
+ poll_count-cur_cnt, FLR_WAIT_INTERAVAL, regs->pN);
+}
+
+static inline u32 bnx2x_flr_clnup_reg_poll(struct bnx2x *bp, u32 reg,
+ u32 expected, u32 poll_count)
+{
+ u32 cur_cnt = poll_count;
+ u32 val;
+
+ while ((val = REG_RD(bp, reg)) != expected && cur_cnt--)
+ udelay(FLR_WAIT_INTERAVAL);
+
+ return val;
+}
+
+static inline int bnx2x_flr_clnup_poll_hw_counter(struct bnx2x *bp, u32 reg,
+ char *msg, u32 poll_cnt)
+{
+ u32 val = bnx2x_flr_clnup_reg_poll(bp, reg, 0, poll_cnt);
+ if (val != 0) {
+ BNX2X_ERR("%s usage count=%d\n", msg, val);
+ return 1;
+ }
+ return 0;
+}
+
+static u32 bnx2x_flr_clnup_poll_count(struct bnx2x *bp)
+{
+ /* adjust polling timeout */
+ if (CHIP_REV_IS_EMUL(bp))
+ return FLR_POLL_CNT * 2000;
+
+ if (CHIP_REV_IS_FPGA(bp))
+ return FLR_POLL_CNT * 120;
+
+ return FLR_POLL_CNT;
+}
+
+static void bnx2x_tx_hw_flushed(struct bnx2x *bp, u32 poll_count)
+{
+ struct pbf_pN_cmd_regs cmd_regs[] = {
+ {0, (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_TQ_OCCUPANCY_Q0 :
+ PBF_REG_P0_TQ_OCCUPANCY,
+ (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_TQ_LINES_FREED_CNT_Q0 :
+ PBF_REG_P0_TQ_LINES_FREED_CNT},
+ {1, (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_TQ_OCCUPANCY_Q1 :
+ PBF_REG_P1_TQ_OCCUPANCY,
+ (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_TQ_LINES_FREED_CNT_Q1 :
+ PBF_REG_P1_TQ_LINES_FREED_CNT},
+ {4, (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_TQ_OCCUPANCY_LB_Q :
+ PBF_REG_P4_TQ_OCCUPANCY,
+ (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_TQ_LINES_FREED_CNT_LB_Q :
+ PBF_REG_P4_TQ_LINES_FREED_CNT}
+ };
+
+ struct pbf_pN_buf_regs buf_regs[] = {
+ {0, (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_INIT_CRD_Q0 :
+ PBF_REG_P0_INIT_CRD ,
+ (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_CREDIT_Q0 :
+ PBF_REG_P0_CREDIT,
+ (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_INTERNAL_CRD_FREED_CNT_Q0 :
+ PBF_REG_P0_INTERNAL_CRD_FREED_CNT},
+ {1, (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_INIT_CRD_Q1 :
+ PBF_REG_P1_INIT_CRD,
+ (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_CREDIT_Q1 :
+ PBF_REG_P1_CREDIT,
+ (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_INTERNAL_CRD_FREED_CNT_Q1 :
+ PBF_REG_P1_INTERNAL_CRD_FREED_CNT},
+ {4, (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_INIT_CRD_LB_Q :
+ PBF_REG_P4_INIT_CRD,
+ (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_CREDIT_LB_Q :
+ PBF_REG_P4_CREDIT,
+ (CHIP_IS_E3B0(bp)) ?
+ PBF_REG_INTERNAL_CRD_FREED_CNT_LB_Q :
+ PBF_REG_P4_INTERNAL_CRD_FREED_CNT},
+ };
+
+ int i;
+
+ /* Verify the command queues are flushed P0, P1, P4 */
+ for (i = 0; i < ARRAY_SIZE(cmd_regs); i++)
+ bnx2x_pbf_pN_cmd_flushed(bp, &cmd_regs[i], poll_count);
+
+
+ /* Verify the transmission buffers are flushed P0, P1, P4 */
+ for (i = 0; i < ARRAY_SIZE(buf_regs); i++)
+ bnx2x_pbf_pN_buf_flushed(bp, &buf_regs[i], poll_count);
+}
+
+#define OP_GEN_PARAM(param) \
+ (((param) << SDM_OP_GEN_COMP_PARAM_SHIFT) & SDM_OP_GEN_COMP_PARAM)
+
+#define OP_GEN_TYPE(type) \
+ (((type) << SDM_OP_GEN_COMP_TYPE_SHIFT) & SDM_OP_GEN_COMP_TYPE)
+
+#define OP_GEN_AGG_VECT(index) \
+ (((index) << SDM_OP_GEN_AGG_VECT_IDX_SHIFT) & SDM_OP_GEN_AGG_VECT_IDX)
+
+
+static inline int bnx2x_send_final_clnup(struct bnx2x *bp, u8 clnup_func,
+ u32 poll_cnt)
+{
+ struct sdm_op_gen op_gen = {0};
+
+ u32 comp_addr = BAR_CSTRORM_INTMEM +
+ CSTORM_FINAL_CLEANUP_COMPLETE_OFFSET(clnup_func);
+ int ret = 0;
+
+ if (REG_RD(bp, comp_addr)) {
+ BNX2X_ERR("Cleanup complete is not 0\n");
+ return 1;
+ }
+
+ op_gen.command |= OP_GEN_PARAM(XSTORM_AGG_INT_FINAL_CLEANUP_INDEX);
+ op_gen.command |= OP_GEN_TYPE(XSTORM_AGG_INT_FINAL_CLEANUP_COMP_TYPE);
+ op_gen.command |= OP_GEN_AGG_VECT(clnup_func);
+ op_gen.command |= 1 << SDM_OP_GEN_AGG_VECT_IDX_VALID_SHIFT;
+
+ DP(BNX2X_MSG_SP, "FW Final cleanup\n");
+ REG_WR(bp, XSDM_REG_OPERATION_GEN, op_gen.command);
+
+ if (bnx2x_flr_clnup_reg_poll(bp, comp_addr, 1, poll_cnt) != 1) {
+ BNX2X_ERR("FW final cleanup did not succeed\n");
+ ret = 1;
+ }
+ /* Zero completion for nxt FLR */
+ REG_WR(bp, comp_addr, 0);
+
+ return ret;
+}
+
+static inline u8 bnx2x_is_pcie_pending(struct pci_dev *dev)
+{
+ int pos;
+ u16 status;
+
+ pos = pci_pcie_cap(dev);
+ if (!pos)
+ return false;
+
+ pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status);
+ return status & PCI_EXP_DEVSTA_TRPND;
+}
+
+/* PF FLR specific routines
+*/
+static int bnx2x_poll_hw_usage_counters(struct bnx2x *bp, u32 poll_cnt)
+{
+
+ /* wait for CFC PF usage-counter to zero (includes all the VFs) */
+ if (bnx2x_flr_clnup_poll_hw_counter(bp,
+ CFC_REG_NUM_LCIDS_INSIDE_PF,
+ "CFC PF usage counter timed out",
+ poll_cnt))
+ return 1;
+
+
+ /* Wait for DQ PF usage-counter to zero (until DQ cleanup) */
+ if (bnx2x_flr_clnup_poll_hw_counter(bp,
+ DORQ_REG_PF_USAGE_CNT,
+ "DQ PF usage counter timed out",
+ poll_cnt))
+ return 1;
+
+ /* Wait for QM PF usage-counter to zero (until DQ cleanup) */
+ if (bnx2x_flr_clnup_poll_hw_counter(bp,
+ QM_REG_PF_USG_CNT_0 + 4*BP_FUNC(bp),
+ "QM PF usage counter timed out",
+ poll_cnt))
+ return 1;
+
+ /* Wait for Timer PF usage-counters to zero (until DQ cleanup) */
+ if (bnx2x_flr_clnup_poll_hw_counter(bp,
+ TM_REG_LIN0_VNIC_UC + 4*BP_PORT(bp),
+ "Timers VNIC usage counter timed out",
+ poll_cnt))
+ return 1;
+ if (bnx2x_flr_clnup_poll_hw_counter(bp,
+ TM_REG_LIN0_NUM_SCANS + 4*BP_PORT(bp),
+ "Timers NUM_SCANS usage counter timed out",
+ poll_cnt))
+ return 1;
+
+ /* Wait DMAE PF usage counter to zero */
+ if (bnx2x_flr_clnup_poll_hw_counter(bp,
+ dmae_reg_go_c[INIT_DMAE_C(bp)],
+ "DMAE dommand register timed out",
+ poll_cnt))
+ return 1;
+
+ return 0;
+}
+
+static void bnx2x_hw_enable_status(struct bnx2x *bp)
+{
+ u32 val;
+
+ val = REG_RD(bp, CFC_REG_WEAK_ENABLE_PF);
+ DP(BNX2X_MSG_SP, "CFC_REG_WEAK_ENABLE_PF is 0x%x\n", val);
+
+ val = REG_RD(bp, PBF_REG_DISABLE_PF);
+ DP(BNX2X_MSG_SP, "PBF_REG_DISABLE_PF is 0x%x\n", val);
+
+ val = REG_RD(bp, IGU_REG_PCI_PF_MSI_EN);
+ DP(BNX2X_MSG_SP, "IGU_REG_PCI_PF_MSI_EN is 0x%x\n", val);
+
+ val = REG_RD(bp, IGU_REG_PCI_PF_MSIX_EN);
+ DP(BNX2X_MSG_SP, "IGU_REG_PCI_PF_MSIX_EN is 0x%x\n", val);
+
+ val = REG_RD(bp, IGU_REG_PCI_PF_MSIX_FUNC_MASK);
+ DP(BNX2X_MSG_SP, "IGU_REG_PCI_PF_MSIX_FUNC_MASK is 0x%x\n", val);
+
+ val = REG_RD(bp, PGLUE_B_REG_SHADOW_BME_PF_7_0_CLR);
+ DP(BNX2X_MSG_SP, "PGLUE_B_REG_SHADOW_BME_PF_7_0_CLR is 0x%x\n", val);
+
+ val = REG_RD(bp, PGLUE_B_REG_FLR_REQUEST_PF_7_0_CLR);
+ DP(BNX2X_MSG_SP, "PGLUE_B_REG_FLR_REQUEST_PF_7_0_CLR is 0x%x\n", val);
+
+ val = REG_RD(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER);
+ DP(BNX2X_MSG_SP, "PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER is 0x%x\n",
+ val);
+}
+
+static int bnx2x_pf_flr_clnup(struct bnx2x *bp)
+{
+ u32 poll_cnt = bnx2x_flr_clnup_poll_count(bp);
+
+ DP(BNX2X_MSG_SP, "Cleanup after FLR PF[%d]\n", BP_ABS_FUNC(bp));
+
+ /* Re-enable PF target read access */
+ REG_WR(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_TARGET_READ, 1);
+
+ /* Poll HW usage counters */
+ if (bnx2x_poll_hw_usage_counters(bp, poll_cnt))
+ return -EBUSY;
+
+ /* Zero the igu 'trailing edge' and 'leading edge' */
+
+ /* Send the FW cleanup command */
+ if (bnx2x_send_final_clnup(bp, (u8)BP_FUNC(bp), poll_cnt))
+ return -EBUSY;
+
+ /* ATC cleanup */
+
+ /* Verify TX hw is flushed */
+ bnx2x_tx_hw_flushed(bp, poll_cnt);
+
+ /* Wait 100ms (not adjusted according to platform) */
+ msleep(100);
+
+ /* Verify no pending pci transactions */
+ if (bnx2x_is_pcie_pending(bp->pdev))
+ BNX2X_ERR("PCIE Transactions still pending\n");
+
+ /* Debug */
+ bnx2x_hw_enable_status(bp);
+
+ /*
+ * Master enable - Due to WB DMAE writes performed before this
+ * register is re-initialized as part of the regular function init
+ */
+ REG_WR(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER, 1);
+
+ return 0;
+}
+
static void bnx2x_hc_int_enable(struct bnx2x *bp)
{
int port = BP_PORT(bp);
@@ -1143,7 +1417,7 @@ static void bnx2x_hc_int_enable(struct bnx2x *bp)
if (!CHIP_IS_E1(bp)) {
/* init leading/trailing edge */
if (IS_MF(bp)) {
- val = (0xee0f | (1 << (BP_E1HVN(bp) + 4)));
+ val = (0xee0f | (1 << (BP_VN(bp) + 4)));
if (bp->port.pmf)
/* enable nig and gpio3 attention */
val |= 0x1100;
@@ -1195,7 +1469,7 @@ static void bnx2x_igu_int_enable(struct bnx2x *bp)
/* init leading/trailing edge */
if (IS_MF(bp)) {
- val = (0xee0f | (1 << (BP_E1HVN(bp) + 4)));
+ val = (0xee0f | (1 << (BP_VN(bp) + 4)));
if (bp->port.pmf)
/* enable nig and gpio3 attention */
val |= 0x1100;
@@ -1273,7 +1547,7 @@ static void bnx2x_igu_int_disable(struct bnx2x *bp)
BNX2X_ERR("BUG! proper val not read from IGU!\n");
}
-static void bnx2x_int_disable(struct bnx2x *bp)
+void bnx2x_int_disable(struct bnx2x *bp)
{
if (bp->common.int_block == INT_BLOCK_HC)
bnx2x_hc_int_disable(bp);
@@ -1286,10 +1560,6 @@ void bnx2x_int_disable_sync(struct bnx2x *bp, int disable_hw)
int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0;
int i, offset;
- /* disable interrupt handling */
- atomic_inc(&bp->intr_sem);
- smp_wmb(); /* Ensure that bp->intr_sem update is SMP-safe */
-
if (disable_hw)
/* prevent the HW from sending interrupts */
bnx2x_int_disable(bp);
@@ -1302,12 +1572,13 @@ void bnx2x_int_disable_sync(struct bnx2x *bp, int disable_hw)
offset++;
#endif
for_each_eth_queue(bp, i)
- synchronize_irq(bp->msix_table[i + offset].vector);
+ synchronize_irq(bp->msix_table[offset++].vector);
} else
synchronize_irq(bp->pdev->irq);
/* make sure sp_task is not running */
cancel_delayed_work(&bp->sp_task);
+ cancel_delayed_work(&bp->period_task);
flush_workqueue(bnx2x_wq);
}
@@ -1351,59 +1622,129 @@ static bool bnx2x_trylock_hw_lock(struct bnx2x *bp, u32 resource)
return false;
}
+/**
+ * bnx2x_get_leader_lock_resource - get the recovery leader resource id
+ *
+ * @bp: driver handle
+ *
+ * Returns the recovery leader resource id according to the engine this function
+ * belongs to. Currently only only 2 engines is supported.
+ */
+static inline int bnx2x_get_leader_lock_resource(struct bnx2x *bp)
+{
+ if (BP_PATH(bp))
+ return HW_LOCK_RESOURCE_RECOVERY_LEADER_1;
+ else
+ return HW_LOCK_RESOURCE_RECOVERY_LEADER_0;
+}
+
+/**
+ * bnx2x_trylock_leader_lock- try to aquire a leader lock.
+ *
+ * @bp: driver handle
+ *
+ * Tries to aquire a leader lock for cuurent engine.
+ */
+static inline bool bnx2x_trylock_leader_lock(struct bnx2x *bp)
+{
+ return bnx2x_trylock_hw_lock(bp, bnx2x_get_leader_lock_resource(bp));
+}
+
#ifdef BCM_CNIC
-static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid);
+static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid, u8 err);
#endif
-void bnx2x_sp_event(struct bnx2x_fastpath *fp,
- union eth_rx_cqe *rr_cqe)
+void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe)
{
struct bnx2x *bp = fp->bp;
int cid = SW_CID(rr_cqe->ramrod_cqe.conn_and_cmd_data);
int command = CQE_CMD(rr_cqe->ramrod_cqe.conn_and_cmd_data);
+ enum bnx2x_queue_cmd drv_cmd = BNX2X_Q_CMD_MAX;
+ struct bnx2x_queue_sp_obj *q_obj = &fp->q_obj;
DP(BNX2X_MSG_SP,
"fp %d cid %d got ramrod #%d state is %x type is %d\n",
fp->index, cid, command, bp->state,
rr_cqe->ramrod_cqe.ramrod_type);
- switch (command | fp->state) {
- case (RAMROD_CMD_ID_ETH_CLIENT_SETUP | BNX2X_FP_STATE_OPENING):
- DP(NETIF_MSG_IFUP, "got MULTI[%d] setup ramrod\n", cid);
- fp->state = BNX2X_FP_STATE_OPEN;
+ switch (command) {
+ case (RAMROD_CMD_ID_ETH_CLIENT_UPDATE):
+ DP(BNX2X_MSG_SP, "got UPDATE ramrod. CID %d\n", cid);
+ drv_cmd = BNX2X_Q_CMD_UPDATE;
break;
- case (RAMROD_CMD_ID_ETH_HALT | BNX2X_FP_STATE_HALTING):
- DP(NETIF_MSG_IFDOWN, "got MULTI[%d] halt ramrod\n", cid);
- fp->state = BNX2X_FP_STATE_HALTED;
+ case (RAMROD_CMD_ID_ETH_CLIENT_SETUP):
+ DP(BNX2X_MSG_SP, "got MULTI[%d] setup ramrod\n", cid);
+ drv_cmd = BNX2X_Q_CMD_SETUP;
break;
- case (RAMROD_CMD_ID_ETH_TERMINATE | BNX2X_FP_STATE_TERMINATING):
- DP(NETIF_MSG_IFDOWN, "got MULTI[%d] teminate ramrod\n", cid);
- fp->state = BNX2X_FP_STATE_TERMINATED;
+ case (RAMROD_CMD_ID_ETH_TX_QUEUE_SETUP):
+ DP(NETIF_MSG_IFUP, "got MULTI[%d] tx-only setup ramrod\n", cid);
+ drv_cmd = BNX2X_Q_CMD_SETUP_TX_ONLY;
break;
- default:
- BNX2X_ERR("unexpected MC reply (%d) "
- "fp[%d] state is %x\n",
- command, fp->index, fp->state);
+ case (RAMROD_CMD_ID_ETH_HALT):
+ DP(BNX2X_MSG_SP, "got MULTI[%d] halt ramrod\n", cid);
+ drv_cmd = BNX2X_Q_CMD_HALT;
break;
+
+ case (RAMROD_CMD_ID_ETH_TERMINATE):
+ DP(BNX2X_MSG_SP, "got MULTI[%d] teminate ramrod\n", cid);
+ drv_cmd = BNX2X_Q_CMD_TERMINATE;
+ break;
+
+ case (RAMROD_CMD_ID_ETH_EMPTY):
+ DP(BNX2X_MSG_SP, "got MULTI[%d] empty ramrod\n", cid);
+ drv_cmd = BNX2X_Q_CMD_EMPTY;
+ break;
+
+ default:
+ BNX2X_ERR("unexpected MC reply (%d) on fp[%d]\n",
+ command, fp->index);
+ return;
}
+ if ((drv_cmd != BNX2X_Q_CMD_MAX) &&
+ q_obj->complete_cmd(bp, q_obj, drv_cmd))
+ /* q_obj->complete_cmd() failure means that this was
+ * an unexpected completion.
+ *
+ * In this case we don't want to increase the bp->spq_left
+ * because apparently we haven't sent this command the first
+ * place.
+ */
+#ifdef BNX2X_STOP_ON_ERROR
+ bnx2x_panic();
+#else
+ return;
+#endif
+
smp_mb__before_atomic_inc();
atomic_inc(&bp->cq_spq_left);
- /* push the change in fp->state and towards the memory */
- smp_wmb();
+ /* push the change in bp->spq_left and towards the memory */
+ smp_mb__after_atomic_inc();
+
+ DP(BNX2X_MSG_SP, "bp->cq_spq_left %x\n", atomic_read(&bp->cq_spq_left));
return;
}
+void bnx2x_update_rx_prod(struct bnx2x *bp, struct bnx2x_fastpath *fp,
+ u16 bd_prod, u16 rx_comp_prod, u16 rx_sge_prod)
+{
+ u32 start = BAR_USTRORM_INTMEM + fp->ustorm_rx_prods_offset;
+
+ bnx2x_update_rx_prod_gen(bp, fp, bd_prod, rx_comp_prod, rx_sge_prod,
+ start);
+}
+
irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
{
struct bnx2x *bp = netdev_priv(dev_instance);
u16 status = bnx2x_ack_int(bp);
u16 mask;
int i;
+ u8 cos;
/* Return here if interrupt is shared and it's not for us */
if (unlikely(status == 0)) {
@@ -1412,12 +1753,6 @@ irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
}
DP(NETIF_MSG_INTR, "got an interrupt status 0x%x\n", status);
- /* Return here if interrupt is disabled */
- if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
- DP(NETIF_MSG_INTR, "called but intr_sem not 0, returning\n");
- return IRQ_HANDLED;
- }
-
#ifdef BNX2X_STOP_ON_ERROR
if (unlikely(bp->panic))
return IRQ_HANDLED;
@@ -1426,11 +1761,12 @@ irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
for_each_eth_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
- mask = 0x2 << (fp->index + CNIC_CONTEXT_USE);
+ mask = 0x2 << (fp->index + CNIC_PRESENT);
if (status & mask) {
- /* Handle Rx and Tx according to SB id */
+ /* Handle Rx or Tx according to SB id */
prefetch(fp->rx_cons_sb);
- prefetch(fp->tx_cons_sb);
+ for_each_cos_in_tx_queue(fp, cos)
+ prefetch(fp->txdata[cos].tx_cons_sb);
prefetch(&fp->sb_running_index[SM_RX_ID]);
napi_schedule(&bnx2x_fp(bp, fp->index, napi));
status &= ~mask;
@@ -1442,11 +1778,13 @@ irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
if (status & (mask | 0x1)) {
struct cnic_ops *c_ops = NULL;
- rcu_read_lock();
- c_ops = rcu_dereference(bp->cnic_ops);
- if (c_ops)
- c_ops->cnic_handler(bp->cnic_data, NULL);
- rcu_read_unlock();
+ if (likely(bp->state == BNX2X_STATE_OPEN)) {
+ rcu_read_lock();
+ c_ops = rcu_dereference(bp->cnic_ops);
+ if (c_ops)
+ c_ops->cnic_handler(bp->cnic_data, NULL);
+ rcu_read_unlock();
+ }
status &= ~mask;
}
@@ -1467,9 +1805,6 @@ irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
return IRQ_HANDLED;
}
-/* end of fast path */
-
-
/* Link */
/*
@@ -1521,6 +1856,11 @@ int bnx2x_acquire_hw_lock(struct bnx2x *bp, u32 resource)
return -EAGAIN;
}
+int bnx2x_release_leader_lock(struct bnx2x *bp)
+{
+ return bnx2x_release_hw_lock(bp, bnx2x_get_leader_lock_resource(bp));
+}
+
int bnx2x_release_hw_lock(struct bnx2x *bp, u32 resource)
{
u32 lock_status;
@@ -1641,6 +1981,53 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode, u8 port)
return 0;
}
+int bnx2x_set_mult_gpio(struct bnx2x *bp, u8 pins, u32 mode)
+{
+ u32 gpio_reg = 0;
+ int rc = 0;
+
+ /* Any port swapping should be handled by caller. */
+
+ bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_GPIO);
+ /* read GPIO and mask except the float bits */
+ gpio_reg = REG_RD(bp, MISC_REG_GPIO);
+ gpio_reg &= ~(pins << MISC_REGISTERS_GPIO_FLOAT_POS);
+ gpio_reg &= ~(pins << MISC_REGISTERS_GPIO_CLR_POS);
+ gpio_reg &= ~(pins << MISC_REGISTERS_GPIO_SET_POS);
+
+ switch (mode) {
+ case MISC_REGISTERS_GPIO_OUTPUT_LOW:
+ DP(NETIF_MSG_LINK, "Set GPIO 0x%x -> output low\n", pins);
+ /* set CLR */
+ gpio_reg |= (pins << MISC_REGISTERS_GPIO_CLR_POS);
+ break;
+
+ case MISC_REGISTERS_GPIO_OUTPUT_HIGH:
+ DP(NETIF_MSG_LINK, "Set GPIO 0x%x -> output high\n", pins);
+ /* set SET */
+ gpio_reg |= (pins << MISC_REGISTERS_GPIO_SET_POS);
+ break;
+
+ case MISC_REGISTERS_GPIO_INPUT_HI_Z:
+ DP(NETIF_MSG_LINK, "Set GPIO 0x%x -> input\n", pins);
+ /* set FLOAT */
+ gpio_reg |= (pins << MISC_REGISTERS_GPIO_FLOAT_POS);
+ break;
+
+ default:
+ BNX2X_ERR("Invalid GPIO mode assignment %d\n", mode);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc == 0)
+ REG_WR(bp, MISC_REG_GPIO, gpio_reg);
+
+ bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_GPIO);
+
+ return rc;
+}
+
int bnx2x_set_gpio_int(struct bnx2x *bp, int gpio_num, u32 mode, u8 port)
{
/* The GPIO should be swapped if swap register is set and active */
@@ -1733,45 +2120,6 @@ static int bnx2x_set_spio(struct bnx2x *bp, int spio_num, u32 mode)
return 0;
}
-int bnx2x_get_link_cfg_idx(struct bnx2x *bp)
-{
- u32 sel_phy_idx = 0;
- if (bp->link_vars.link_up) {
- sel_phy_idx = EXT_PHY1;
- /* In case link is SERDES, check if the EXT_PHY2 is the one */
- if ((bp->link_vars.link_status & LINK_STATUS_SERDES_LINK) &&
- (bp->link_params.phy[EXT_PHY2].supported & SUPPORTED_FIBRE))
- sel_phy_idx = EXT_PHY2;
- } else {
-
- switch (bnx2x_phy_selection(&bp->link_params)) {
- case PORT_HW_CFG_PHY_SELECTION_HARDWARE_DEFAULT:
- case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY:
- case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY_PRIORITY:
- sel_phy_idx = EXT_PHY1;
- break;
- case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY:
- case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY_PRIORITY:
- sel_phy_idx = EXT_PHY2;
- break;
- }
- }
- /*
- * The selected actived PHY is always after swapping (in case PHY
- * swapping is enabled). So when swapping is enabled, we need to reverse
- * the configuration
- */
-
- if (bp->link_params.multi_phy_config &
- PORT_HW_CFG_PHY_SWAPPED_ENABLED) {
- if (sel_phy_idx == EXT_PHY1)
- sel_phy_idx = EXT_PHY2;
- else if (sel_phy_idx == EXT_PHY2)
- sel_phy_idx = EXT_PHY1;
- }
- return LINK_CONFIG_IDX(sel_phy_idx);
-}
-
void bnx2x_calc_fc_adv(struct bnx2x *bp)
{
u8 cfg_idx = bnx2x_get_link_cfg_idx(bp);
@@ -1804,10 +2152,12 @@ u8 bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode)
u8 rc;
int cfx_idx = bnx2x_get_link_cfg_idx(bp);
u16 req_line_speed = bp->link_params.req_line_speed[cfx_idx];
- /* Initialize link parameters structure variables */
- /* It is recommended to turn off RX FC for jumbo frames
- for better performance */
- if ((CHIP_IS_E1x(bp)) && (bp->dev->mtu > 5000))
+ /*
+ * Initialize link parameters structure variables
+ * It is recommended to turn off RX FC for jumbo frames
+ * for better performance
+ */
+ if (CHIP_IS_E1x(bp) && (bp->dev->mtu > 5000))
bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_TX;
else
bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_BOTH;
@@ -1815,8 +2165,18 @@ u8 bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode)
bnx2x_acquire_phy_lock(bp);
if (load_mode == LOAD_DIAG) {
- bp->link_params.loopback_mode = LOOPBACK_XGXS;
- bp->link_params.req_line_speed[cfx_idx] = SPEED_10000;
+ struct link_params *lp = &bp->link_params;
+ lp->loopback_mode = LOOPBACK_XGXS;
+ /* do PHY loopback at 10G speed, if possible */
+ if (lp->req_line_speed[cfx_idx] < SPEED_10000) {
+ if (lp->speed_cap_mask[cfx_idx] &
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)
+ lp->req_line_speed[cfx_idx] =
+ SPEED_10000;
+ else
+ lp->req_line_speed[cfx_idx] =
+ SPEED_1000;
+ }
}
rc = bnx2x_phy_init(&bp->link_params, &bp->link_vars);
@@ -1828,7 +2188,8 @@ u8 bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode)
if (CHIP_REV_IS_SLOW(bp) && bp->link_vars.link_up) {
bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP);
bnx2x_link_report(bp);
- }
+ } else
+ queue_delayed_work(bnx2x_wq, &bp->period_task, 0);
bp->link_params.req_line_speed[cfx_idx] = req_line_speed;
return rc;
}
@@ -1924,7 +2285,7 @@ static void bnx2x_calc_vn_weight_sum(struct bnx2x *bp)
int vn;
bp->vn_weight_sum = 0;
- for (vn = VN_0; vn < E1HVN_MAX; vn++) {
+ for (vn = VN_0; vn < BP_MAX_VN_NUM(bp); vn++) {
u32 vn_cfg = bp->mf_config[vn];
u32 vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >>
FUNC_MF_CFG_MIN_BW_SHIFT) * 100;
@@ -1942,8 +2303,12 @@ static void bnx2x_calc_vn_weight_sum(struct bnx2x *bp)
bp->vn_weight_sum += vn_min_rate;
}
- /* ... only if all min rates are zeros - disable fairness */
- if (all_zero) {
+ /* if ETS or all min rates are zeros - disable fairness */
+ if (BNX2X_IS_ETS_ENABLED(bp)) {
+ bp->cmng.flags.cmng_enables &=
+ ~CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
+ DP(NETIF_MSG_IFUP, "Fairness will be disabled due to ETS\n");
+ } else if (all_zero) {
bp->cmng.flags.cmng_enables &=
~CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
DP(NETIF_MSG_IFUP, "All MIN values are zeroes"
@@ -1953,12 +2318,18 @@ static void bnx2x_calc_vn_weight_sum(struct bnx2x *bp)
CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
}
+/* returns func by VN for current port */
+static inline int func_by_vn(struct bnx2x *bp, int vn)
+{
+ return 2 * vn + BP_PORT(bp);
+}
+
static void bnx2x_init_vn_minmax(struct bnx2x *bp, int vn)
{
struct rate_shaping_vars_per_vn m_rs_vn;
struct fairness_vars_per_vn m_fair_vn;
u32 vn_cfg = bp->mf_config[vn];
- int func = 2*vn + BP_PORT(bp);
+ int func = func_by_vn(bp, vn);
u16 vn_min_rate, vn_max_rate;
int i;
@@ -2055,7 +2426,7 @@ void bnx2x_read_mf_cfg(struct bnx2x *bp)
*
* and there are 2 functions per port
*/
- for (vn = VN_0; vn < E1HVN_MAX; vn++) {
+ for (vn = VN_0; vn < BP_MAX_VN_NUM(bp); vn++) {
int /*abs*/func = n * (2 * vn + BP_PORT(bp)) + BP_PATH(bp);
if (func >= E1H_FUNC_MAX)
@@ -2087,7 +2458,7 @@ static void bnx2x_cmng_fns_init(struct bnx2x *bp, u8 read_cfg, u8 cmng_type)
/* calculate and set min-max rate for each vn */
if (bp->port.pmf)
- for (vn = VN_0; vn < E1HVN_MAX; vn++)
+ for (vn = VN_0; vn < BP_MAX_VN_NUM(bp); vn++)
bnx2x_init_vn_minmax(bp, vn);
/* always enable rate shaping and fairness */
@@ -2106,16 +2477,15 @@ static void bnx2x_cmng_fns_init(struct bnx2x *bp, u8 read_cfg, u8 cmng_type)
static inline void bnx2x_link_sync_notify(struct bnx2x *bp)
{
- int port = BP_PORT(bp);
int func;
int vn;
/* Set the attention towards other drivers on the same port */
- for (vn = VN_0; vn < E1HVN_MAX; vn++) {
- if (vn == BP_E1HVN(bp))
+ for (vn = VN_0; vn < BP_MAX_VN_NUM(bp); vn++) {
+ if (vn == BP_VN(bp))
continue;
- func = ((vn << 1) | port);
+ func = func_by_vn(bp, vn);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_0 +
(LINK_SYNC_ATTENTION_BIT_FUNC_0 + func)*4, 1);
}
@@ -2144,11 +2514,11 @@ static void bnx2x_link_attn(struct bnx2x *bp)
pause_enabled);
}
- if (bp->link_vars.mac_type == MAC_TYPE_BMAC) {
+ if (bp->link_vars.mac_type != MAC_TYPE_EMAC) {
struct host_port_stats *pstats;
pstats = bnx2x_sp(bp, port_stats);
- /* reset old bmac stats */
+ /* reset old mac stats */
memset(&(pstats->mac_stx[0]), 0,
sizeof(struct mac_stx));
}
@@ -2198,12 +2568,23 @@ static void bnx2x_pmf_update(struct bnx2x *bp)
bp->port.pmf = 1;
DP(NETIF_MSG_LINK, "pmf %d\n", bp->port.pmf);
+ /*
+ * We need the mb() to ensure the ordering between the writing to
+ * bp->port.pmf here and reading it from the bnx2x_periodic_task().
+ */
+ smp_mb();
+
+ /* queue a periodic task */
+ queue_delayed_work(bnx2x_wq, &bp->period_task, 0);
+
+ bnx2x_dcbx_pmf_update(bp);
+
/* enable nig attention */
- val = (0xff0f | (1 << (BP_E1HVN(bp) + 4)));
+ val = (0xff0f | (1 << (BP_VN(bp) + 4)));
if (bp->common.int_block == INT_BLOCK_HC) {
REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, val);
REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, val);
- } else if (CHIP_IS_E2(bp)) {
+ } else if (!CHIP_IS_E1x(bp)) {
REG_WR(bp, IGU_REG_TRAILING_EDGE_LATCH, val);
REG_WR(bp, IGU_REG_LEADING_EDGE_LATCH, val);
}
@@ -2233,7 +2614,8 @@ u32 bnx2x_fw_command(struct bnx2x *bp, u32 command, u32 param)
SHMEM_WR(bp, func_mb[mb_idx].drv_mb_param, param);
SHMEM_WR(bp, func_mb[mb_idx].drv_mb_header, (command | seq));
- DP(BNX2X_MSG_MCP, "wrote command (%x) to FW MB\n", (command | seq));
+ DP(BNX2X_MSG_MCP, "wrote command (%x) to FW MB param 0x%08x\n",
+ (command | seq), param);
do {
/* let the FW do it's magic ... */
@@ -2264,185 +2646,127 @@ u32 bnx2x_fw_command(struct bnx2x *bp, u32 command, u32 param)
static u8 stat_counter_valid(struct bnx2x *bp, struct bnx2x_fastpath *fp)
{
#ifdef BCM_CNIC
- if (IS_FCOE_FP(fp) && IS_MF(bp))
+ /* Statistics are not supported for CNIC Clients at the moment */
+ if (IS_FCOE_FP(fp))
return false;
#endif
return true;
}
-/* must be called under rtnl_lock */
-static void bnx2x_rxq_set_mac_filters(struct bnx2x *bp, u16 cl_id, u32 filters)
+void bnx2x_func_init(struct bnx2x *bp, struct bnx2x_func_init_params *p)
{
- u32 mask = (1 << cl_id);
-
- /* initial seeting is BNX2X_ACCEPT_NONE */
- u8 drop_all_ucast = 1, drop_all_bcast = 1, drop_all_mcast = 1;
- u8 accp_all_ucast = 0, accp_all_bcast = 0, accp_all_mcast = 0;
- u8 unmatched_unicast = 0;
-
- if (filters & BNX2X_ACCEPT_UNMATCHED_UCAST)
- unmatched_unicast = 1;
-
- if (filters & BNX2X_PROMISCUOUS_MODE) {
- /* promiscious - accept all, drop none */
- drop_all_ucast = drop_all_bcast = drop_all_mcast = 0;
- accp_all_ucast = accp_all_bcast = accp_all_mcast = 1;
- if (IS_MF_SI(bp)) {
- /*
- * SI mode defines to accept in promiscuos mode
- * only unmatched packets
- */
- unmatched_unicast = 1;
- accp_all_ucast = 0;
- }
- }
- if (filters & BNX2X_ACCEPT_UNICAST) {
- /* accept matched ucast */
- drop_all_ucast = 0;
- }
- if (filters & BNX2X_ACCEPT_MULTICAST)
- /* accept matched mcast */
- drop_all_mcast = 0;
+ if (CHIP_IS_E1x(bp)) {
+ struct tstorm_eth_function_common_config tcfg = {0};
- if (filters & BNX2X_ACCEPT_ALL_UNICAST) {
- /* accept all mcast */
- drop_all_ucast = 0;
- accp_all_ucast = 1;
- }
- if (filters & BNX2X_ACCEPT_ALL_MULTICAST) {
- /* accept all mcast */
- drop_all_mcast = 0;
- accp_all_mcast = 1;
+ storm_memset_func_cfg(bp, &tcfg, p->func_id);
}
- if (filters & BNX2X_ACCEPT_BROADCAST) {
- /* accept (all) bcast */
- drop_all_bcast = 0;
- accp_all_bcast = 1;
- }
-
- bp->mac_filters.ucast_drop_all = drop_all_ucast ?
- bp->mac_filters.ucast_drop_all | mask :
- bp->mac_filters.ucast_drop_all & ~mask;
-
- bp->mac_filters.mcast_drop_all = drop_all_mcast ?
- bp->mac_filters.mcast_drop_all | mask :
- bp->mac_filters.mcast_drop_all & ~mask;
-
- bp->mac_filters.bcast_drop_all = drop_all_bcast ?
- bp->mac_filters.bcast_drop_all | mask :
- bp->mac_filters.bcast_drop_all & ~mask;
- bp->mac_filters.ucast_accept_all = accp_all_ucast ?
- bp->mac_filters.ucast_accept_all | mask :
- bp->mac_filters.ucast_accept_all & ~mask;
-
- bp->mac_filters.mcast_accept_all = accp_all_mcast ?
- bp->mac_filters.mcast_accept_all | mask :
- bp->mac_filters.mcast_accept_all & ~mask;
-
- bp->mac_filters.bcast_accept_all = accp_all_bcast ?
- bp->mac_filters.bcast_accept_all | mask :
- bp->mac_filters.bcast_accept_all & ~mask;
+ /* Enable the function in the FW */
+ storm_memset_vf_to_pf(bp, p->func_id, p->pf_id);
+ storm_memset_func_en(bp, p->func_id, 1);
- bp->mac_filters.unmatched_unicast = unmatched_unicast ?
- bp->mac_filters.unmatched_unicast | mask :
- bp->mac_filters.unmatched_unicast & ~mask;
+ /* spq */
+ if (p->func_flgs & FUNC_FLG_SPQ) {
+ storm_memset_spq_addr(bp, p->spq_map, p->func_id);
+ REG_WR(bp, XSEM_REG_FAST_MEMORY +
+ XSTORM_SPQ_PROD_OFFSET(p->func_id), p->spq_prod);
+ }
}
-static void bnx2x_func_init(struct bnx2x *bp, struct bnx2x_func_init_params *p)
+/**
+ * bnx2x_get_tx_only_flags - Return common flags
+ *
+ * @bp device handle
+ * @fp queue handle
+ * @zero_stats TRUE if statistics zeroing is needed
+ *
+ * Return the flags that are common for the Tx-only and not normal connections.
+ */
+static inline unsigned long bnx2x_get_common_flags(struct bnx2x *bp,
+ struct bnx2x_fastpath *fp,
+ bool zero_stats)
{
- struct tstorm_eth_function_common_config tcfg = {0};
- u16 rss_flgs;
-
- /* tpa */
- if (p->func_flgs & FUNC_FLG_TPA)
- tcfg.config_flags |=
- TSTORM_ETH_FUNCTION_COMMON_CONFIG_ENABLE_TPA;
-
- /* set rss flags */
- rss_flgs = (p->rss->mode <<
- TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_MODE_SHIFT);
-
- if (p->rss->cap & RSS_IPV4_CAP)
- rss_flgs |= RSS_IPV4_CAP_MASK;
- if (p->rss->cap & RSS_IPV4_TCP_CAP)
- rss_flgs |= RSS_IPV4_TCP_CAP_MASK;
- if (p->rss->cap & RSS_IPV6_CAP)
- rss_flgs |= RSS_IPV6_CAP_MASK;
- if (p->rss->cap & RSS_IPV6_TCP_CAP)
- rss_flgs |= RSS_IPV6_TCP_CAP_MASK;
+ unsigned long flags = 0;
- tcfg.config_flags |= rss_flgs;
- tcfg.rss_result_mask = p->rss->result_mask;
+ /* PF driver will always initialize the Queue to an ACTIVE state */
+ __set_bit(BNX2X_Q_FLG_ACTIVE, &flags);
- storm_memset_func_cfg(bp, &tcfg, p->func_id);
-
- /* Enable the function in the FW */
- storm_memset_vf_to_pf(bp, p->func_id, p->pf_id);
- storm_memset_func_en(bp, p->func_id, 1);
+ /* tx only connections collect statistics (on the same index as the
+ * parent connection). The statistics are zeroed when the parent
+ * connection is initialized.
+ */
+ if (stat_counter_valid(bp, fp)) {
+ __set_bit(BNX2X_Q_FLG_STATS, &flags);
+ if (zero_stats)
+ __set_bit(BNX2X_Q_FLG_ZERO_STATS, &flags);
+ }
- /* statistics */
- if (p->func_flgs & FUNC_FLG_STATS) {
- struct stats_indication_flags stats_flags = {0};
- stats_flags.collect_eth = 1;
+ return flags;
+}
- storm_memset_xstats_flags(bp, &stats_flags, p->func_id);
- storm_memset_xstats_addr(bp, p->fw_stat_map, p->func_id);
+static inline unsigned long bnx2x_get_q_flags(struct bnx2x *bp,
+ struct bnx2x_fastpath *fp,
+ bool leading)
+{
+ unsigned long flags = 0;
- storm_memset_tstats_flags(bp, &stats_flags, p->func_id);
- storm_memset_tstats_addr(bp, p->fw_stat_map, p->func_id);
+ /* calculate other queue flags */
+ if (IS_MF_SD(bp))
+ __set_bit(BNX2X_Q_FLG_OV, &flags);
- storm_memset_ustats_flags(bp, &stats_flags, p->func_id);
- storm_memset_ustats_addr(bp, p->fw_stat_map, p->func_id);
+ if (IS_FCOE_FP(fp))
+ __set_bit(BNX2X_Q_FLG_FCOE, &flags);
- storm_memset_cstats_flags(bp, &stats_flags, p->func_id);
- storm_memset_cstats_addr(bp, p->fw_stat_map, p->func_id);
+ if (!fp->disable_tpa) {
+ __set_bit(BNX2X_Q_FLG_TPA, &flags);
+ __set_bit(BNX2X_Q_FLG_TPA_IPV6, &flags);
}
- /* spq */
- if (p->func_flgs & FUNC_FLG_SPQ) {
- storm_memset_spq_addr(bp, p->spq_map, p->func_id);
- REG_WR(bp, XSEM_REG_FAST_MEMORY +
- XSTORM_SPQ_PROD_OFFSET(p->func_id), p->spq_prod);
+ if (leading) {
+ __set_bit(BNX2X_Q_FLG_LEADING_RSS, &flags);
+ __set_bit(BNX2X_Q_FLG_MCAST, &flags);
}
-}
-static inline u16 bnx2x_get_cl_flags(struct bnx2x *bp,
- struct bnx2x_fastpath *fp)
-{
- u16 flags = 0;
+ /* Always set HW VLAN stripping */
+ __set_bit(BNX2X_Q_FLG_VLAN, &flags);
- /* calculate queue flags */
- flags |= QUEUE_FLG_CACHE_ALIGN;
- flags |= QUEUE_FLG_HC;
- flags |= IS_MF_SD(bp) ? QUEUE_FLG_OV : 0;
- flags |= QUEUE_FLG_VLAN;
- DP(NETIF_MSG_IFUP, "vlan removal enabled\n");
+ return flags | bnx2x_get_common_flags(bp, fp, true);
+}
- if (!fp->disable_tpa)
- flags |= QUEUE_FLG_TPA;
+static void bnx2x_pf_q_prep_general(struct bnx2x *bp,
+ struct bnx2x_fastpath *fp, struct bnx2x_general_setup_params *gen_init,
+ u8 cos)
+{
+ gen_init->stat_id = bnx2x_stats_id(fp);
+ gen_init->spcl_id = fp->cl_id;
- flags = stat_counter_valid(bp, fp) ?
- (flags | QUEUE_FLG_STATS) : (flags & ~QUEUE_FLG_STATS);
+ /* Always use mini-jumbo MTU for FCoE L2 ring */
+ if (IS_FCOE_FP(fp))
+ gen_init->mtu = BNX2X_FCOE_MINI_JUMBO_MTU;
+ else
+ gen_init->mtu = bp->dev->mtu;
- return flags;
+ gen_init->cos = cos;
}
-static void bnx2x_pf_rx_cl_prep(struct bnx2x *bp,
+static void bnx2x_pf_rx_q_prep(struct bnx2x *bp,
struct bnx2x_fastpath *fp, struct rxq_pause_params *pause,
- struct bnx2x_rxq_init_params *rxq_init)
+ struct bnx2x_rxq_setup_params *rxq_init)
{
- u16 max_sge = 0;
+ u8 max_sge = 0;
u16 sge_sz = 0;
u16 tpa_agg_size = 0;
- /* calculate queue flags */
- u16 flags = bnx2x_get_cl_flags(bp, fp);
-
if (!fp->disable_tpa) {
- pause->sge_th_hi = 250;
- pause->sge_th_lo = 150;
+ pause->sge_th_lo = SGE_TH_LO(bp);
+ pause->sge_th_hi = SGE_TH_HI(bp);
+
+ /* validate SGE ring has enough to cross high threshold */
+ WARN_ON(bp->dropless_fc &&
+ pause->sge_th_hi + FW_PREFETCH_CNT >
+ MAX_RX_SGE_CNT * NUM_RX_SGE_PAGES);
+
tpa_agg_size = min_t(u32,
(min_t(u32, 8, MAX_SKB_FRAGS) *
SGE_PAGE_SIZE * PAGES_PER_SGE), 0xffff);
@@ -2456,84 +2780,87 @@ static void bnx2x_pf_rx_cl_prep(struct bnx2x *bp,
/* pause - not for e1 */
if (!CHIP_IS_E1(bp)) {
- pause->bd_th_hi = 350;
- pause->bd_th_lo = 250;
- pause->rcq_th_hi = 350;
- pause->rcq_th_lo = 250;
- pause->sge_th_hi = 0;
- pause->sge_th_lo = 0;
+ pause->bd_th_lo = BD_TH_LO(bp);
+ pause->bd_th_hi = BD_TH_HI(bp);
+
+ pause->rcq_th_lo = RCQ_TH_LO(bp);
+ pause->rcq_th_hi = RCQ_TH_HI(bp);
+ /*
+ * validate that rings have enough entries to cross
+ * high thresholds
+ */
+ WARN_ON(bp->dropless_fc &&
+ pause->bd_th_hi + FW_PREFETCH_CNT >
+ bp->rx_ring_size);
+ WARN_ON(bp->dropless_fc &&
+ pause->rcq_th_hi + FW_PREFETCH_CNT >
+ NUM_RCQ_RINGS * MAX_RCQ_DESC_CNT);
+
pause->pri_map = 1;
}
/* rxq setup */
- rxq_init->flags = flags;
- rxq_init->cxt = &bp->context.vcxt[fp->cid].eth;
rxq_init->dscr_map = fp->rx_desc_mapping;
rxq_init->sge_map = fp->rx_sge_mapping;
rxq_init->rcq_map = fp->rx_comp_mapping;
rxq_init->rcq_np_map = fp->rx_comp_mapping + BCM_PAGE_SIZE;
- /* Always use mini-jumbo MTU for FCoE L2 ring */
- if (IS_FCOE_FP(fp))
- rxq_init->mtu = BNX2X_FCOE_MINI_JUMBO_MTU;
- else
- rxq_init->mtu = bp->dev->mtu;
+ /* This should be a maximum number of data bytes that may be
+ * placed on the BD (not including paddings).
+ */
+ rxq_init->buf_sz = fp->rx_buf_size - BNX2X_FW_RX_ALIGN -
+ IP_HEADER_ALIGNMENT_PADDING;
- rxq_init->buf_sz = fp->rx_buf_size;
rxq_init->cl_qzone_id = fp->cl_qzone_id;
- rxq_init->cl_id = fp->cl_id;
- rxq_init->spcl_id = fp->cl_id;
- rxq_init->stat_id = fp->cl_id;
rxq_init->tpa_agg_sz = tpa_agg_size;
rxq_init->sge_buf_sz = sge_sz;
rxq_init->max_sges_pkt = max_sge;
+ rxq_init->rss_engine_id = BP_FUNC(bp);
+
+ /* Maximum number or simultaneous TPA aggregation for this Queue.
+ *
+ * For PF Clients it should be the maximum avaliable number.
+ * VF driver(s) may want to define it to a smaller value.
+ */
+ rxq_init->max_tpa_queues = MAX_AGG_QS(bp);
+
rxq_init->cache_line_log = BNX2X_RX_ALIGN_SHIFT;
rxq_init->fw_sb_id = fp->fw_sb_id;
if (IS_FCOE_FP(fp))
rxq_init->sb_cq_index = HC_SP_INDEX_ETH_FCOE_RX_CQ_CONS;
else
- rxq_init->sb_cq_index = U_SB_ETH_RX_CQ_INDEX;
-
- rxq_init->cid = HW_CID(bp, fp->cid);
-
- rxq_init->hc_rate = bp->rx_ticks ? (1000000 / bp->rx_ticks) : 0;
+ rxq_init->sb_cq_index = HC_INDEX_ETH_RX_CQ_CONS;
}
-static void bnx2x_pf_tx_cl_prep(struct bnx2x *bp,
- struct bnx2x_fastpath *fp, struct bnx2x_txq_init_params *txq_init)
+static void bnx2x_pf_tx_q_prep(struct bnx2x *bp,
+ struct bnx2x_fastpath *fp, struct bnx2x_txq_setup_params *txq_init,
+ u8 cos)
{
- u16 flags = bnx2x_get_cl_flags(bp, fp);
-
- txq_init->flags = flags;
- txq_init->cxt = &bp->context.vcxt[fp->cid].eth;
- txq_init->dscr_map = fp->tx_desc_mapping;
- txq_init->stat_id = fp->cl_id;
- txq_init->cid = HW_CID(bp, fp->cid);
- txq_init->sb_cq_index = C_SB_ETH_TX_CQ_INDEX;
+ txq_init->dscr_map = fp->txdata[cos].tx_desc_mapping;
+ txq_init->sb_cq_index = HC_INDEX_ETH_FIRST_TX_CQ_CONS + cos;
txq_init->traffic_type = LLFC_TRAFFIC_TYPE_NW;
txq_init->fw_sb_id = fp->fw_sb_id;
+ /*
+ * set the tss leading client id for TX classfication ==
+ * leading RSS client id
+ */
+ txq_init->tss_leading_cl_id = bnx2x_fp(bp, 0, cl_id);
+
if (IS_FCOE_FP(fp)) {
txq_init->sb_cq_index = HC_SP_INDEX_ETH_FCOE_TX_CQ_CONS;
txq_init->traffic_type = LLFC_TRAFFIC_TYPE_FCOE;
}
-
- txq_init->hc_rate = bp->tx_ticks ? (1000000 / bp->tx_ticks) : 0;
}
static void bnx2x_pf_init(struct bnx2x *bp)
{
struct bnx2x_func_init_params func_init = {0};
- struct bnx2x_rss_params rss = {0};
struct event_ring_data eq_data = { {0} };
u16 flags;
- /* pf specific setups */
- if (!CHIP_IS_E1(bp))
- storm_memset_ov(bp, bp->mf_ov, BP_FUNC(bp));
-
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
/* reset IGU PF statistics: MSIX + ATTN */
/* PF */
REG_WR(bp, IGU_REG_STATISTIC_NUM_MESSAGE_SENT +
@@ -2551,27 +2878,14 @@ static void bnx2x_pf_init(struct bnx2x *bp)
/* function setup flags */
flags = (FUNC_FLG_STATS | FUNC_FLG_LEADING | FUNC_FLG_SPQ);
- if (CHIP_IS_E1x(bp))
- flags |= (bp->flags & TPA_ENABLE_FLAG) ? FUNC_FLG_TPA : 0;
- else
- flags |= FUNC_FLG_TPA;
-
- /* function setup */
-
- /**
- * Although RSS is meaningless when there is a single HW queue we
- * still need it enabled in order to have HW Rx hash generated.
+ /* This flag is relevant for E1x only.
+ * E2 doesn't have a TPA configuration in a function level.
*/
- rss.cap = (RSS_IPV4_CAP | RSS_IPV4_TCP_CAP |
- RSS_IPV6_CAP | RSS_IPV6_TCP_CAP);
- rss.mode = bp->multi_mode;
- rss.result_mask = MULTI_MASK;
- func_init.rss = &rss;
+ flags |= (bp->flags & TPA_ENABLE_FLAG) ? FUNC_FLG_TPA : 0;
func_init.func_flgs = flags;
func_init.pf_id = BP_FUNC(bp);
func_init.func_id = BP_FUNC(bp);
- func_init.fw_stat_map = bnx2x_sp_mapping(bp, fw_stats);
func_init.spq_map = bp->spq_mapping;
func_init.spq_prod = bp->spq_prod_idx;
@@ -2580,11 +2894,11 @@ static void bnx2x_pf_init(struct bnx2x *bp)
memset(&(bp->cmng), 0, sizeof(struct cmng_struct_per_port));
/*
- Congestion management values depend on the link rate
- There is no active link so initial link rate is set to 10 Gbps.
- When the link comes up The congestion management values are
- re-calculated according to the actual link rate.
- */
+ * Congestion management values depend on the link rate
+ * There is no active link so initial link rate is set to 10 Gbps.
+ * When the link comes up The congestion management values are
+ * re-calculated according to the actual link rate.
+ */
bp->link_vars.line_speed = SPEED_10000;
bnx2x_cmng_fns_init(bp, true, bnx2x_get_cmng_fns_mode(bp));
@@ -2592,10 +2906,6 @@ static void bnx2x_pf_init(struct bnx2x *bp)
if (bp->port.pmf)
storm_memset_cmng(bp, &bp->cmng, BP_PORT(bp));
- /* no rx until link is up */
- bp->rx_mode = BNX2X_RX_MODE_NONE;
- bnx2x_set_storm_rx_mode(bp);
-
/* init Event Queue */
eq_data.base_addr.hi = U64_HI(bp->eq_mapping);
eq_data.base_addr.lo = U64_LO(bp->eq_mapping);
@@ -2610,11 +2920,9 @@ static void bnx2x_e1h_disable(struct bnx2x *bp)
{
int port = BP_PORT(bp);
- netif_tx_disable(bp->dev);
+ bnx2x_tx_disable(bp);
REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 0);
-
- netif_carrier_off(bp->dev);
}
static void bnx2x_e1h_enable(struct bnx2x *bp)
@@ -2709,20 +3017,60 @@ static inline void bnx2x_sp_prod_update(struct bnx2x *bp)
{
int func = BP_FUNC(bp);
- /* Make sure that BD data is updated before writing the producer */
- wmb();
+ /*
+ * Make sure that BD data is updated before writing the producer:
+ * BD data is written to the memory, the producer is read from the
+ * memory, thus we need a full memory barrier to ensure the ordering.
+ */
+ mb();
REG_WR16(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func),
bp->spq_prod_idx);
mmiowb();
}
-/* the slow path queue is odd since completions arrive on the fastpath ring */
+/**
+ * bnx2x_is_contextless_ramrod - check if the current command ends on EQ
+ *
+ * @cmd: command to check
+ * @cmd_type: command type
+ */
+static inline bool bnx2x_is_contextless_ramrod(int cmd, int cmd_type)
+{
+ if ((cmd_type == NONE_CONNECTION_TYPE) ||
+ (cmd == RAMROD_CMD_ID_ETH_FORWARD_SETUP) ||
+ (cmd == RAMROD_CMD_ID_ETH_CLASSIFICATION_RULES) ||
+ (cmd == RAMROD_CMD_ID_ETH_FILTER_RULES) ||
+ (cmd == RAMROD_CMD_ID_ETH_MULTICAST_RULES) ||
+ (cmd == RAMROD_CMD_ID_ETH_SET_MAC) ||
+ (cmd == RAMROD_CMD_ID_ETH_RSS_UPDATE))
+ return true;
+ else
+ return false;
+
+}
+
+
+/**
+ * bnx2x_sp_post - place a single command on an SP ring
+ *
+ * @bp: driver handle
+ * @command: command to place (e.g. SETUP, FILTER_RULES, etc.)
+ * @cid: SW CID the command is related to
+ * @data_hi: command private data address (high 32 bits)
+ * @data_lo: command private data address (low 32 bits)
+ * @cmd_type: command type (e.g. NONE, ETH)
+ *
+ * SP data is handled as if it's always an address pair, thus data fields are
+ * not swapped to little endian in upper functions. Instead this function swaps
+ * data as if it's two u32 fields.
+ */
int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
- u32 data_hi, u32 data_lo, int common)
+ u32 data_hi, u32 data_lo, int cmd_type)
{
struct eth_spe *spe;
u16 type;
+ bool common = bnx2x_is_contextless_ramrod(command, cmd_type);
#ifdef BNX2X_STOP_ON_ERROR
if (unlikely(bp->panic))
@@ -2752,17 +3100,7 @@ int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
cpu_to_le32((command << SPE_HDR_CMD_ID_SHIFT) |
HW_CID(bp, cid));
- if (common)
- /* Common ramrods:
- * FUNC_START, FUNC_STOP, CFC_DEL, STATS, SET_MAC
- * TRAFFIC_STOP, TRAFFIC_START
- */
- type = (NONE_CONNECTION_TYPE << SPE_HDR_CONN_TYPE_SHIFT)
- & SPE_HDR_CONN_TYPE;
- else
- /* ETH ramrods: SETUP, HALT */
- type = (ETH_CONNECTION_TYPE << SPE_HDR_CONN_TYPE_SHIFT)
- & SPE_HDR_CONN_TYPE;
+ type = (cmd_type << SPE_HDR_CONN_TYPE_SHIFT) & SPE_HDR_CONN_TYPE;
type |= ((BP_FUNC(bp) << SPE_HDR_FUNCTION_ID_SHIFT) &
SPE_HDR_FUNCTION_ID);
@@ -2772,25 +3110,23 @@ int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
spe->data.update_data_addr.hi = cpu_to_le32(data_hi);
spe->data.update_data_addr.lo = cpu_to_le32(data_lo);
- /* stats ramrod has it's own slot on the spq */
- if (command != RAMROD_CMD_ID_COMMON_STAT_QUERY) {
- /* It's ok if the actual decrement is issued towards the memory
- * somewhere between the spin_lock and spin_unlock. Thus no
- * more explict memory barrier is needed.
- */
- if (common)
- atomic_dec(&bp->eq_spq_left);
- else
- atomic_dec(&bp->cq_spq_left);
- }
+ /*
+ * It's ok if the actual decrement is issued towards the memory
+ * somewhere between the spin_lock and spin_unlock. Thus no
+ * more explict memory barrier is needed.
+ */
+ if (common)
+ atomic_dec(&bp->eq_spq_left);
+ else
+ atomic_dec(&bp->cq_spq_left);
DP(BNX2X_MSG_SP/*NETIF_MSG_TIMER*/,
- "SPQE[%x] (%x:%x) command %d hw_cid %x data (%x:%x) "
- "type(0x%x) left (ETH, COMMON) (%x,%x)\n",
+ "SPQE[%x] (%x:%x) (cmd, common?) (%d,%d) hw_cid %x data (%x:%x) "
+ "type(0x%x) left (CQ, EQ) (%x,%x)\n",
bp->spq_prod_idx, (u32)U64_HI(bp->spq_mapping),
(u32)(U64_LO(bp->spq_mapping) +
- (void *)bp->spq_prod_bd - (void *)bp->spq), command,
+ (void *)bp->spq_prod_bd - (void *)bp->spq), command, common,
HW_CID(bp, cid), data_hi, data_lo, type,
atomic_read(&bp->cq_spq_left), atomic_read(&bp->eq_spq_left));
@@ -2893,9 +3229,15 @@ static void bnx2x_attn_int_asserted(struct bnx2x *bp, u32 asserted)
/* save nig interrupt mask */
nig_mask = REG_RD(bp, nig_int_mask_addr);
- REG_WR(bp, nig_int_mask_addr, 0);
- bnx2x_link_attn(bp);
+ /* If nig_mask is not set, no need to call the update
+ * function.
+ */
+ if (nig_mask) {
+ REG_WR(bp, nig_int_mask_addr, 0);
+
+ bnx2x_link_attn(bp);
+ }
/* handle unicore attn? */
}
@@ -3000,8 +3342,7 @@ static inline void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn)
bnx2x_fan_failure(bp);
}
- if (attn & (AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0 |
- AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1)) {
+ if ((attn & bp->link_vars.aeu_int_mask) && bp->port.pmf) {
bnx2x_acquire_phy_lock(bp);
bnx2x_handle_module_detect_int(&bp->link_params);
bnx2x_release_phy_lock(bp);
@@ -3064,13 +3405,13 @@ static inline void bnx2x_attn_int_deasserted2(struct bnx2x *bp, u32 attn)
}
if (attn & AEU_INPUTS_ATTN_BITS_PXP_HW_INTERRUPT) {
-
val = REG_RD(bp, PXP_REG_PXP_INT_STS_CLR_0);
- BNX2X_ERR("PXP hw attention 0x%x\n", val);
+ BNX2X_ERR("PXP hw attention-0 0x%x\n", val);
/* RQ_USDMDP_FIFO_OVERFLOW */
if (val & 0x18000)
BNX2X_ERR("FATAL error from PXP\n");
- if (CHIP_IS_E2(bp)) {
+
+ if (!CHIP_IS_E1x(bp)) {
val = REG_RD(bp, PXP_REG_PXP_INT_STS_CLR_1);
BNX2X_ERR("PXP hw attention-1 0x%x\n", val);
}
@@ -3118,20 +3459,31 @@ static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn)
if ((bp->port.pmf == 0) && (val & DRV_STATUS_PMF))
bnx2x_pmf_update(bp);
- /* Always call it here: bnx2x_link_report() will
- * prevent the link indication duplication.
- */
- bnx2x__link_status_update(bp);
-
if (bp->port.pmf &&
(val & DRV_STATUS_DCBX_NEGOTIATION_RESULTS) &&
bp->dcbx_enabled > 0)
/* start dcbx state machine */
bnx2x_dcbx_set_params(bp,
BNX2X_DCBX_STATE_NEG_RECEIVED);
+ if (bp->link_vars.periodic_flags &
+ PERIODIC_FLAGS_LINK_EVENT) {
+ /* sync with link */
+ bnx2x_acquire_phy_lock(bp);
+ bp->link_vars.periodic_flags &=
+ ~PERIODIC_FLAGS_LINK_EVENT;
+ bnx2x_release_phy_lock(bp);
+ if (IS_MF(bp))
+ bnx2x_link_sync_notify(bp);
+ bnx2x_link_report(bp);
+ }
+ /* Always call it here: bnx2x_link_report() will
+ * prevent the link indication duplication.
+ */
+ bnx2x__link_status_update(bp);
} else if (attn & BNX2X_MC_ASSERT_BITS) {
BNX2X_ERR("MC assert!\n");
+ bnx2x_mc_assert(bp);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_10, 0);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_9, 0);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_8, 0);
@@ -3164,72 +3516,185 @@ static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn)
}
}
-#define BNX2X_MISC_GEN_REG MISC_REG_GENERIC_POR_1
-#define LOAD_COUNTER_BITS 16 /* Number of bits for load counter */
-#define LOAD_COUNTER_MASK (((u32)0x1 << LOAD_COUNTER_BITS) - 1)
-#define RESET_DONE_FLAG_MASK (~LOAD_COUNTER_MASK)
-#define RESET_DONE_FLAG_SHIFT LOAD_COUNTER_BITS
+/*
+ * Bits map:
+ * 0-7 - Engine0 load counter.
+ * 8-15 - Engine1 load counter.
+ * 16 - Engine0 RESET_IN_PROGRESS bit.
+ * 17 - Engine1 RESET_IN_PROGRESS bit.
+ * 18 - Engine0 ONE_IS_LOADED. Set when there is at least one active function
+ * on the engine
+ * 19 - Engine1 ONE_IS_LOADED.
+ * 20 - Chip reset flow bit. When set none-leader must wait for both engines
+ * leader to complete (check for both RESET_IN_PROGRESS bits and not for
+ * just the one belonging to its engine).
+ *
+ */
+#define BNX2X_RECOVERY_GLOB_REG MISC_REG_GENERIC_POR_1
+
+#define BNX2X_PATH0_LOAD_CNT_MASK 0x000000ff
+#define BNX2X_PATH0_LOAD_CNT_SHIFT 0
+#define BNX2X_PATH1_LOAD_CNT_MASK 0x0000ff00
+#define BNX2X_PATH1_LOAD_CNT_SHIFT 8
+#define BNX2X_PATH0_RST_IN_PROG_BIT 0x00010000
+#define BNX2X_PATH1_RST_IN_PROG_BIT 0x00020000
+#define BNX2X_GLOBAL_RESET_BIT 0x00040000
+
+/*
+ * Set the GLOBAL_RESET bit.
+ *
+ * Should be run under rtnl lock
+ */
+void bnx2x_set_reset_global(struct bnx2x *bp)
+{
+ u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+
+ REG_WR(bp, BNX2X_RECOVERY_GLOB_REG, val | BNX2X_GLOBAL_RESET_BIT);
+ barrier();
+ mmiowb();
+}
+
+/*
+ * Clear the GLOBAL_RESET bit.
+ *
+ * Should be run under rtnl lock
+ */
+static inline void bnx2x_clear_reset_global(struct bnx2x *bp)
+{
+ u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+
+ REG_WR(bp, BNX2X_RECOVERY_GLOB_REG, val & (~BNX2X_GLOBAL_RESET_BIT));
+ barrier();
+ mmiowb();
+}
/*
+ * Checks the GLOBAL_RESET bit.
+ *
* should be run under rtnl lock
*/
+static inline bool bnx2x_reset_is_global(struct bnx2x *bp)
+{
+ u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+
+ DP(NETIF_MSG_HW, "GEN_REG_VAL=0x%08x\n", val);
+ return (val & BNX2X_GLOBAL_RESET_BIT) ? true : false;
+}
+
+/*
+ * Clear RESET_IN_PROGRESS bit for the current engine.
+ *
+ * Should be run under rtnl lock
+ */
static inline void bnx2x_set_reset_done(struct bnx2x *bp)
{
- u32 val = REG_RD(bp, BNX2X_MISC_GEN_REG);
- val &= ~(1 << RESET_DONE_FLAG_SHIFT);
- REG_WR(bp, BNX2X_MISC_GEN_REG, val);
+ u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+ u32 bit = BP_PATH(bp) ?
+ BNX2X_PATH1_RST_IN_PROG_BIT : BNX2X_PATH0_RST_IN_PROG_BIT;
+
+ /* Clear the bit */
+ val &= ~bit;
+ REG_WR(bp, BNX2X_RECOVERY_GLOB_REG, val);
barrier();
mmiowb();
}
/*
+ * Set RESET_IN_PROGRESS for the current engine.
+ *
* should be run under rtnl lock
*/
-static inline void bnx2x_set_reset_in_progress(struct bnx2x *bp)
+void bnx2x_set_reset_in_progress(struct bnx2x *bp)
{
- u32 val = REG_RD(bp, BNX2X_MISC_GEN_REG);
- val |= (1 << 16);
- REG_WR(bp, BNX2X_MISC_GEN_REG, val);
+ u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+ u32 bit = BP_PATH(bp) ?
+ BNX2X_PATH1_RST_IN_PROG_BIT : BNX2X_PATH0_RST_IN_PROG_BIT;
+
+ /* Set the bit */
+ val |= bit;
+ REG_WR(bp, BNX2X_RECOVERY_GLOB_REG, val);
barrier();
mmiowb();
}
/*
+ * Checks the RESET_IN_PROGRESS bit for the given engine.
* should be run under rtnl lock
*/
-bool bnx2x_reset_is_done(struct bnx2x *bp)
+bool bnx2x_reset_is_done(struct bnx2x *bp, int engine)
{
- u32 val = REG_RD(bp, BNX2X_MISC_GEN_REG);
- DP(NETIF_MSG_HW, "GEN_REG_VAL=0x%08x\n", val);
- return (val & RESET_DONE_FLAG_MASK) ? false : true;
+ u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+ u32 bit = engine ?
+ BNX2X_PATH1_RST_IN_PROG_BIT : BNX2X_PATH0_RST_IN_PROG_BIT;
+
+ /* return false if bit is set */
+ return (val & bit) ? false : true;
}
/*
+ * Increment the load counter for the current engine.
+ *
* should be run under rtnl lock
*/
-inline void bnx2x_inc_load_cnt(struct bnx2x *bp)
+void bnx2x_inc_load_cnt(struct bnx2x *bp)
{
- u32 val1, val = REG_RD(bp, BNX2X_MISC_GEN_REG);
+ u32 val1, val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+ u32 mask = BP_PATH(bp) ? BNX2X_PATH1_LOAD_CNT_MASK :
+ BNX2X_PATH0_LOAD_CNT_MASK;
+ u32 shift = BP_PATH(bp) ? BNX2X_PATH1_LOAD_CNT_SHIFT :
+ BNX2X_PATH0_LOAD_CNT_SHIFT;
DP(NETIF_MSG_HW, "Old GEN_REG_VAL=0x%08x\n", val);
- val1 = ((val & LOAD_COUNTER_MASK) + 1) & LOAD_COUNTER_MASK;
- REG_WR(bp, BNX2X_MISC_GEN_REG, (val & RESET_DONE_FLAG_MASK) | val1);
+ /* get the current counter value */
+ val1 = (val & mask) >> shift;
+
+ /* increment... */
+ val1++;
+
+ /* clear the old value */
+ val &= ~mask;
+
+ /* set the new one */
+ val |= ((val1 << shift) & mask);
+
+ REG_WR(bp, BNX2X_RECOVERY_GLOB_REG, val);
barrier();
mmiowb();
}
-/*
- * should be run under rtnl lock
+/**
+ * bnx2x_dec_load_cnt - decrement the load counter
+ *
+ * @bp: driver handle
+ *
+ * Should be run under rtnl lock.
+ * Decrements the load counter for the current engine. Returns
+ * the new counter value.
*/
u32 bnx2x_dec_load_cnt(struct bnx2x *bp)
{
- u32 val1, val = REG_RD(bp, BNX2X_MISC_GEN_REG);
+ u32 val1, val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+ u32 mask = BP_PATH(bp) ? BNX2X_PATH1_LOAD_CNT_MASK :
+ BNX2X_PATH0_LOAD_CNT_MASK;
+ u32 shift = BP_PATH(bp) ? BNX2X_PATH1_LOAD_CNT_SHIFT :
+ BNX2X_PATH0_LOAD_CNT_SHIFT;
DP(NETIF_MSG_HW, "Old GEN_REG_VAL=0x%08x\n", val);
- val1 = ((val & LOAD_COUNTER_MASK) - 1) & LOAD_COUNTER_MASK;
- REG_WR(bp, BNX2X_MISC_GEN_REG, (val & RESET_DONE_FLAG_MASK) | val1);
+ /* get the current counter value */
+ val1 = (val & mask) >> shift;
+
+ /* decrement... */
+ val1--;
+
+ /* clear the old value */
+ val &= ~mask;
+
+ /* set the new one */
+ val |= ((val1 << shift) & mask);
+
+ REG_WR(bp, BNX2X_RECOVERY_GLOB_REG, val);
barrier();
mmiowb();
@@ -3237,27 +3702,48 @@ u32 bnx2x_dec_load_cnt(struct bnx2x *bp)
}
/*
+ * Read the load counter for the current engine.
+ *
* should be run under rtnl lock
*/
-static inline u32 bnx2x_get_load_cnt(struct bnx2x *bp)
+static inline u32 bnx2x_get_load_cnt(struct bnx2x *bp, int engine)
{
- return REG_RD(bp, BNX2X_MISC_GEN_REG) & LOAD_COUNTER_MASK;
+ u32 mask = (engine ? BNX2X_PATH1_LOAD_CNT_MASK :
+ BNX2X_PATH0_LOAD_CNT_MASK);
+ u32 shift = (engine ? BNX2X_PATH1_LOAD_CNT_SHIFT :
+ BNX2X_PATH0_LOAD_CNT_SHIFT);
+ u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+
+ DP(NETIF_MSG_HW, "GLOB_REG=0x%08x\n", val);
+
+ val = (val & mask) >> shift;
+
+ DP(NETIF_MSG_HW, "load_cnt for engine %d = %d\n", engine, val);
+
+ return val;
}
+/*
+ * Reset the load counter for the current engine.
+ *
+ * should be run under rtnl lock
+ */
static inline void bnx2x_clear_load_cnt(struct bnx2x *bp)
{
- u32 val = REG_RD(bp, BNX2X_MISC_GEN_REG);
- REG_WR(bp, BNX2X_MISC_GEN_REG, val & (~LOAD_COUNTER_MASK));
+ u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+ u32 mask = (BP_PATH(bp) ? BNX2X_PATH1_LOAD_CNT_MASK :
+ BNX2X_PATH0_LOAD_CNT_MASK);
+
+ REG_WR(bp, BNX2X_RECOVERY_GLOB_REG, val & (~mask));
}
static inline void _print_next_block(int idx, const char *blk)
{
- if (idx)
- pr_cont(", ");
- pr_cont("%s", blk);
+ pr_cont("%s%s", idx ? ", " : "", blk);
}
-static inline int bnx2x_print_blocks_with_parity0(u32 sig, int par_num)
+static inline int bnx2x_check_blocks_with_parity0(u32 sig, int par_num,
+ bool print)
{
int i = 0;
u32 cur_bit = 0;
@@ -3266,19 +3752,33 @@ static inline int bnx2x_print_blocks_with_parity0(u32 sig, int par_num)
if (sig & cur_bit) {
switch (cur_bit) {
case AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR:
- _print_next_block(par_num++, "BRB");
+ if (print)
+ _print_next_block(par_num++, "BRB");
break;
case AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR:
- _print_next_block(par_num++, "PARSER");
+ if (print)
+ _print_next_block(par_num++, "PARSER");
break;
case AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR:
- _print_next_block(par_num++, "TSDM");
+ if (print)
+ _print_next_block(par_num++, "TSDM");
break;
case AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR:
- _print_next_block(par_num++, "SEARCHER");
+ if (print)
+ _print_next_block(par_num++,
+ "SEARCHER");
+ break;
+ case AEU_INPUTS_ATTN_BITS_TCM_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "TCM");
break;
case AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR:
- _print_next_block(par_num++, "TSEMI");
+ if (print)
+ _print_next_block(par_num++, "TSEMI");
+ break;
+ case AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "XPB");
break;
}
@@ -3290,7 +3790,8 @@ static inline int bnx2x_print_blocks_with_parity0(u32 sig, int par_num)
return par_num;
}
-static inline int bnx2x_print_blocks_with_parity1(u32 sig, int par_num)
+static inline int bnx2x_check_blocks_with_parity1(u32 sig, int par_num,
+ bool *global, bool print)
{
int i = 0;
u32 cur_bit = 0;
@@ -3298,38 +3799,72 @@ static inline int bnx2x_print_blocks_with_parity1(u32 sig, int par_num)
cur_bit = ((u32)0x1 << i);
if (sig & cur_bit) {
switch (cur_bit) {
- case AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR:
- _print_next_block(par_num++, "PBCLIENT");
+ case AEU_INPUTS_ATTN_BITS_PBF_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "PBF");
break;
case AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR:
- _print_next_block(par_num++, "QM");
+ if (print)
+ _print_next_block(par_num++, "QM");
+ break;
+ case AEU_INPUTS_ATTN_BITS_TIMERS_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "TM");
break;
case AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR:
- _print_next_block(par_num++, "XSDM");
+ if (print)
+ _print_next_block(par_num++, "XSDM");
+ break;
+ case AEU_INPUTS_ATTN_BITS_XCM_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "XCM");
break;
case AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR:
- _print_next_block(par_num++, "XSEMI");
+ if (print)
+ _print_next_block(par_num++, "XSEMI");
break;
case AEU_INPUTS_ATTN_BITS_DOORBELLQ_PARITY_ERROR:
- _print_next_block(par_num++, "DOORBELLQ");
+ if (print)
+ _print_next_block(par_num++,
+ "DOORBELLQ");
+ break;
+ case AEU_INPUTS_ATTN_BITS_NIG_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "NIG");
break;
case AEU_INPUTS_ATTN_BITS_VAUX_PCI_CORE_PARITY_ERROR:
- _print_next_block(par_num++, "VAUX PCI CORE");
+ if (print)
+ _print_next_block(par_num++,
+ "VAUX PCI CORE");
+ *global = true;
break;
case AEU_INPUTS_ATTN_BITS_DEBUG_PARITY_ERROR:
- _print_next_block(par_num++, "DEBUG");
+ if (print)
+ _print_next_block(par_num++, "DEBUG");
break;
case AEU_INPUTS_ATTN_BITS_USDM_PARITY_ERROR:
- _print_next_block(par_num++, "USDM");
+ if (print)
+ _print_next_block(par_num++, "USDM");
+ break;
+ case AEU_INPUTS_ATTN_BITS_UCM_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "UCM");
break;
case AEU_INPUTS_ATTN_BITS_USEMI_PARITY_ERROR:
- _print_next_block(par_num++, "USEMI");
+ if (print)
+ _print_next_block(par_num++, "USEMI");
break;
case AEU_INPUTS_ATTN_BITS_UPB_PARITY_ERROR:
- _print_next_block(par_num++, "UPB");
+ if (print)
+ _print_next_block(par_num++, "UPB");
break;
case AEU_INPUTS_ATTN_BITS_CSDM_PARITY_ERROR:
- _print_next_block(par_num++, "CSDM");
+ if (print)
+ _print_next_block(par_num++, "CSDM");
+ break;
+ case AEU_INPUTS_ATTN_BITS_CCM_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "CCM");
break;
}
@@ -3341,7 +3876,8 @@ static inline int bnx2x_print_blocks_with_parity1(u32 sig, int par_num)
return par_num;
}
-static inline int bnx2x_print_blocks_with_parity2(u32 sig, int par_num)
+static inline int bnx2x_check_blocks_with_parity2(u32 sig, int par_num,
+ bool print)
{
int i = 0;
u32 cur_bit = 0;
@@ -3350,26 +3886,37 @@ static inline int bnx2x_print_blocks_with_parity2(u32 sig, int par_num)
if (sig & cur_bit) {
switch (cur_bit) {
case AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR:
- _print_next_block(par_num++, "CSEMI");
+ if (print)
+ _print_next_block(par_num++, "CSEMI");
break;
case AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR:
- _print_next_block(par_num++, "PXP");
+ if (print)
+ _print_next_block(par_num++, "PXP");
break;
case AEU_IN_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR:
- _print_next_block(par_num++,
+ if (print)
+ _print_next_block(par_num++,
"PXPPCICLOCKCLIENT");
break;
case AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR:
- _print_next_block(par_num++, "CFC");
+ if (print)
+ _print_next_block(par_num++, "CFC");
break;
case AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR:
- _print_next_block(par_num++, "CDU");
+ if (print)
+ _print_next_block(par_num++, "CDU");
+ break;
+ case AEU_INPUTS_ATTN_BITS_DMAE_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "DMAE");
break;
case AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR:
- _print_next_block(par_num++, "IGU");
+ if (print)
+ _print_next_block(par_num++, "IGU");
break;
case AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR:
- _print_next_block(par_num++, "MISC");
+ if (print)
+ _print_next_block(par_num++, "MISC");
break;
}
@@ -3381,7 +3928,8 @@ static inline int bnx2x_print_blocks_with_parity2(u32 sig, int par_num)
return par_num;
}
-static inline int bnx2x_print_blocks_with_parity3(u32 sig, int par_num)
+static inline int bnx2x_check_blocks_with_parity3(u32 sig, int par_num,
+ bool *global, bool print)
{
int i = 0;
u32 cur_bit = 0;
@@ -3390,16 +3938,54 @@ static inline int bnx2x_print_blocks_with_parity3(u32 sig, int par_num)
if (sig & cur_bit) {
switch (cur_bit) {
case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY:
- _print_next_block(par_num++, "MCP ROM");
+ if (print)
+ _print_next_block(par_num++, "MCP ROM");
+ *global = true;
break;
case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY:
- _print_next_block(par_num++, "MCP UMP RX");
+ if (print)
+ _print_next_block(par_num++,
+ "MCP UMP RX");
+ *global = true;
break;
case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY:
- _print_next_block(par_num++, "MCP UMP TX");
+ if (print)
+ _print_next_block(par_num++,
+ "MCP UMP TX");
+ *global = true;
break;
case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY:
- _print_next_block(par_num++, "MCP SCPAD");
+ if (print)
+ _print_next_block(par_num++,
+ "MCP SCPAD");
+ *global = true;
+ break;
+ }
+
+ /* Clear the bit */
+ sig &= ~cur_bit;
+ }
+ }
+
+ return par_num;
+}
+
+static inline int bnx2x_check_blocks_with_parity4(u32 sig, int par_num,
+ bool print)
+{
+ int i = 0;
+ u32 cur_bit = 0;
+ for (i = 0; sig; i++) {
+ cur_bit = ((u32)0x1 << i);
+ if (sig & cur_bit) {
+ switch (cur_bit) {
+ case AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "PGLUE_B");
+ break;
+ case AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR:
+ if (print)
+ _print_next_block(par_num++, "ATC");
break;
}
@@ -3411,38 +3997,55 @@ static inline int bnx2x_print_blocks_with_parity3(u32 sig, int par_num)
return par_num;
}
-static inline bool bnx2x_parity_attn(struct bnx2x *bp, u32 sig0, u32 sig1,
- u32 sig2, u32 sig3)
+static inline bool bnx2x_parity_attn(struct bnx2x *bp, bool *global, bool print,
+ u32 *sig)
{
- if ((sig0 & HW_PRTY_ASSERT_SET_0) || (sig1 & HW_PRTY_ASSERT_SET_1) ||
- (sig2 & HW_PRTY_ASSERT_SET_2) || (sig3 & HW_PRTY_ASSERT_SET_3)) {
+ if ((sig[0] & HW_PRTY_ASSERT_SET_0) ||
+ (sig[1] & HW_PRTY_ASSERT_SET_1) ||
+ (sig[2] & HW_PRTY_ASSERT_SET_2) ||
+ (sig[3] & HW_PRTY_ASSERT_SET_3) ||
+ (sig[4] & HW_PRTY_ASSERT_SET_4)) {
int par_num = 0;
DP(NETIF_MSG_HW, "Was parity error: HW block parity attention: "
- "[0]:0x%08x [1]:0x%08x "
- "[2]:0x%08x [3]:0x%08x\n",
- sig0 & HW_PRTY_ASSERT_SET_0,
- sig1 & HW_PRTY_ASSERT_SET_1,
- sig2 & HW_PRTY_ASSERT_SET_2,
- sig3 & HW_PRTY_ASSERT_SET_3);
- printk(KERN_ERR"%s: Parity errors detected in blocks: ",
- bp->dev->name);
- par_num = bnx2x_print_blocks_with_parity0(
- sig0 & HW_PRTY_ASSERT_SET_0, par_num);
- par_num = bnx2x_print_blocks_with_parity1(
- sig1 & HW_PRTY_ASSERT_SET_1, par_num);
- par_num = bnx2x_print_blocks_with_parity2(
- sig2 & HW_PRTY_ASSERT_SET_2, par_num);
- par_num = bnx2x_print_blocks_with_parity3(
- sig3 & HW_PRTY_ASSERT_SET_3, par_num);
- printk("\n");
+ "[0]:0x%08x [1]:0x%08x [2]:0x%08x [3]:0x%08x "
+ "[4]:0x%08x\n",
+ sig[0] & HW_PRTY_ASSERT_SET_0,
+ sig[1] & HW_PRTY_ASSERT_SET_1,
+ sig[2] & HW_PRTY_ASSERT_SET_2,
+ sig[3] & HW_PRTY_ASSERT_SET_3,
+ sig[4] & HW_PRTY_ASSERT_SET_4);
+ if (print)
+ netdev_err(bp->dev,
+ "Parity errors detected in blocks: ");
+ par_num = bnx2x_check_blocks_with_parity0(
+ sig[0] & HW_PRTY_ASSERT_SET_0, par_num, print);
+ par_num = bnx2x_check_blocks_with_parity1(
+ sig[1] & HW_PRTY_ASSERT_SET_1, par_num, global, print);
+ par_num = bnx2x_check_blocks_with_parity2(
+ sig[2] & HW_PRTY_ASSERT_SET_2, par_num, print);
+ par_num = bnx2x_check_blocks_with_parity3(
+ sig[3] & HW_PRTY_ASSERT_SET_3, par_num, global, print);
+ par_num = bnx2x_check_blocks_with_parity4(
+ sig[4] & HW_PRTY_ASSERT_SET_4, par_num, print);
+
+ if (print)
+ pr_cont("\n");
+
return true;
} else
return false;
}
-bool bnx2x_chk_parity_attn(struct bnx2x *bp)
+/**
+ * bnx2x_chk_parity_attn - checks for parity attentions.
+ *
+ * @bp: driver handle
+ * @global: true if there was a global attention
+ * @print: show parity attention in syslog
+ */
+bool bnx2x_chk_parity_attn(struct bnx2x *bp, bool *global, bool print)
{
- struct attn_route attn;
+ struct attn_route attn = { {0} };
int port = BP_PORT(bp);
attn.sig[0] = REG_RD(bp,
@@ -3458,8 +4061,12 @@ bool bnx2x_chk_parity_attn(struct bnx2x *bp)
MISC_REG_AEU_AFTER_INVERT_4_FUNC_0 +
port*4);
- return bnx2x_parity_attn(bp, attn.sig[0], attn.sig[1], attn.sig[2],
- attn.sig[3]);
+ if (!CHIP_IS_E1x(bp))
+ attn.sig[4] = REG_RD(bp,
+ MISC_REG_AEU_AFTER_INVERT_5_FUNC_0 +
+ port*4);
+
+ return bnx2x_parity_attn(bp, global, print, attn.sig);
}
@@ -3538,21 +4145,25 @@ static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted)
u32 reg_addr;
u32 val;
u32 aeu_mask;
+ bool global = false;
/* need to take HW lock because MCP or other port might also
try to handle this event */
bnx2x_acquire_alr(bp);
- if (CHIP_PARITY_ENABLED(bp) && bnx2x_chk_parity_attn(bp)) {
+ if (bnx2x_chk_parity_attn(bp, &global, true)) {
+#ifndef BNX2X_STOP_ON_ERROR
bp->recovery_state = BNX2X_RECOVERY_INIT;
- bnx2x_set_reset_in_progress(bp);
- schedule_delayed_work(&bp->reset_task, 0);
+ schedule_delayed_work(&bp->sp_rtnl_task, 0);
/* Disable HW interrupts */
bnx2x_int_disable(bp);
- bnx2x_release_alr(bp);
/* In case of parity errors don't handle attentions so that
* other function would "see" parity errors.
*/
+#else
+ bnx2x_panic();
+#endif
+ bnx2x_release_alr(bp);
return;
}
@@ -3560,7 +4171,7 @@ static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted)
attn.sig[1] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_2_FUNC_0 + port*4);
attn.sig[2] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_3_FUNC_0 + port*4);
attn.sig[3] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_4_FUNC_0 + port*4);
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
attn.sig[4] =
REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_5_FUNC_0 + port*4);
else
@@ -3656,6 +4267,15 @@ static void bnx2x_attn_int(struct bnx2x *bp)
bnx2x_attn_int_deasserted(bp, deasserted);
}
+void bnx2x_igu_ack_sb(struct bnx2x *bp, u8 igu_sb_id, u8 segment,
+ u16 index, u8 op, u8 update)
+{
+ u32 igu_addr = BAR_IGU_INTMEM + (IGU_CMD_INT_ACK_BASE + igu_sb_id)*8;
+
+ bnx2x_igu_ack_sb_gen(bp, igu_sb_id, segment, index, op, update,
+ igu_addr);
+}
+
static inline void bnx2x_update_eq_prod(struct bnx2x *bp, u16 prod)
{
/* No memory barriers */
@@ -3667,6 +4287,8 @@ static inline void bnx2x_update_eq_prod(struct bnx2x *bp, u16 prod)
static int bnx2x_cnic_handle_cfc_del(struct bnx2x *bp, u32 cid,
union event_ring_elem *elem)
{
+ u8 err = elem->message.error;
+
if (!bp->cnic_eth_dev.starting_cid ||
(cid < bp->cnic_eth_dev.starting_cid &&
cid != bp->cnic_eth_dev.iscsi_l2_cid))
@@ -3674,16 +4296,121 @@ static int bnx2x_cnic_handle_cfc_del(struct bnx2x *bp, u32 cid,
DP(BNX2X_MSG_SP, "got delete ramrod for CNIC CID %d\n", cid);
- if (unlikely(elem->message.data.cfc_del_event.error)) {
+ if (unlikely(err)) {
+
BNX2X_ERR("got delete ramrod for CNIC CID %d with error!\n",
cid);
bnx2x_panic_dump(bp);
}
- bnx2x_cnic_cfc_comp(bp, cid);
+ bnx2x_cnic_cfc_comp(bp, cid, err);
return 0;
}
#endif
+static inline void bnx2x_handle_mcast_eqe(struct bnx2x *bp)
+{
+ struct bnx2x_mcast_ramrod_params rparam;
+ int rc;
+
+ memset(&rparam, 0, sizeof(rparam));
+
+ rparam.mcast_obj = &bp->mcast_obj;
+
+ netif_addr_lock_bh(bp->dev);
+
+ /* Clear pending state for the last command */
+ bp->mcast_obj.raw.clear_pending(&bp->mcast_obj.raw);
+
+ /* If there are pending mcast commands - send them */
+ if (bp->mcast_obj.check_pending(&bp->mcast_obj)) {
+ rc = bnx2x_config_mcast(bp, &rparam, BNX2X_MCAST_CMD_CONT);
+ if (rc < 0)
+ BNX2X_ERR("Failed to send pending mcast commands: %d\n",
+ rc);
+ }
+
+ netif_addr_unlock_bh(bp->dev);
+}
+
+static inline void bnx2x_handle_classification_eqe(struct bnx2x *bp,
+ union event_ring_elem *elem)
+{
+ unsigned long ramrod_flags = 0;
+ int rc = 0;
+ u32 cid = elem->message.data.eth_event.echo & BNX2X_SWCID_MASK;
+ struct bnx2x_vlan_mac_obj *vlan_mac_obj;
+
+ /* Always push next commands out, don't wait here */
+ __set_bit(RAMROD_CONT, &ramrod_flags);
+
+ switch (elem->message.data.eth_event.echo >> BNX2X_SWCID_SHIFT) {
+ case BNX2X_FILTER_MAC_PENDING:
+#ifdef BCM_CNIC
+ if (cid == BNX2X_ISCSI_ETH_CID)
+ vlan_mac_obj = &bp->iscsi_l2_mac_obj;
+ else
+#endif
+ vlan_mac_obj = &bp->fp[cid].mac_obj;
+
+ break;
+ case BNX2X_FILTER_MCAST_PENDING:
+ /* This is only relevant for 57710 where multicast MACs are
+ * configured as unicast MACs using the same ramrod.
+ */
+ bnx2x_handle_mcast_eqe(bp);
+ return;
+ default:
+ BNX2X_ERR("Unsupported classification command: %d\n",
+ elem->message.data.eth_event.echo);
+ return;
+ }
+
+ rc = vlan_mac_obj->complete(bp, vlan_mac_obj, elem, &ramrod_flags);
+
+ if (rc < 0)
+ BNX2X_ERR("Failed to schedule new commands: %d\n", rc);
+ else if (rc > 0)
+ DP(BNX2X_MSG_SP, "Scheduled next pending commands...\n");
+
+}
+
+#ifdef BCM_CNIC
+static void bnx2x_set_iscsi_eth_rx_mode(struct bnx2x *bp, bool start);
+#endif
+
+static inline void bnx2x_handle_rx_mode_eqe(struct bnx2x *bp)
+{
+ netif_addr_lock_bh(bp->dev);
+
+ clear_bit(BNX2X_FILTER_RX_MODE_PENDING, &bp->sp_state);
+
+ /* Send rx_mode command again if was requested */
+ if (test_and_clear_bit(BNX2X_FILTER_RX_MODE_SCHED, &bp->sp_state))
+ bnx2x_set_storm_rx_mode(bp);
+#ifdef BCM_CNIC
+ else if (test_and_clear_bit(BNX2X_FILTER_ISCSI_ETH_START_SCHED,
+ &bp->sp_state))
+ bnx2x_set_iscsi_eth_rx_mode(bp, true);
+ else if (test_and_clear_bit(BNX2X_FILTER_ISCSI_ETH_STOP_SCHED,
+ &bp->sp_state))
+ bnx2x_set_iscsi_eth_rx_mode(bp, false);
+#endif
+
+ netif_addr_unlock_bh(bp->dev);
+}
+
+static inline struct bnx2x_queue_sp_obj *bnx2x_cid_to_q_obj(
+ struct bnx2x *bp, u32 cid)
+{
+ DP(BNX2X_MSG_SP, "retrieving fp from cid %d\n", cid);
+#ifdef BCM_CNIC
+ if (cid == BNX2X_FCOE_ETH_CID)
+ return &bnx2x_fcoe(bp, q_obj);
+ else
+#endif
+ return &bnx2x_fp(bp, CID_TO_FP(cid), q_obj);
+}
+
static void bnx2x_eq_int(struct bnx2x *bp)
{
u16 hw_cons, sw_cons, sw_prod;
@@ -3691,6 +4418,9 @@ static void bnx2x_eq_int(struct bnx2x *bp)
u32 cid;
u8 opcode;
int spqe_cnt = 0;
+ struct bnx2x_queue_sp_obj *q_obj;
+ struct bnx2x_func_sp_obj *f_obj = &bp->func_obj;
+ struct bnx2x_raw_obj *rss_raw = &bp->rss_conf_obj.raw;
hw_cons = le16_to_cpu(*bp->eq_cons_sb);
@@ -3709,7 +4439,7 @@ static void bnx2x_eq_int(struct bnx2x *bp)
sw_cons = bp->eq_cons;
sw_prod = bp->eq_prod;
- DP(BNX2X_MSG_SP, "EQ: hw_cons %u sw_cons %u bp->cq_spq_left %u\n",
+ DP(BNX2X_MSG_SP, "EQ: hw_cons %u sw_cons %u bp->eq_spq_left %x\n",
hw_cons, sw_cons, atomic_read(&bp->eq_spq_left));
for (; sw_cons != hw_cons;
@@ -3725,9 +4455,10 @@ static void bnx2x_eq_int(struct bnx2x *bp)
/* handle eq element */
switch (opcode) {
case EVENT_RING_OPCODE_STAT_QUERY:
- DP(NETIF_MSG_TIMER, "got statistics comp event\n");
+ DP(NETIF_MSG_TIMER, "got statistics comp event %d\n",
+ bp->stats_comp++);
/* nothing to do with stats comp */
- continue;
+ goto next_spqe;
case EVENT_RING_OPCODE_CFC_DEL:
/* handle according to cid range */
@@ -3735,60 +4466,100 @@ static void bnx2x_eq_int(struct bnx2x *bp)
* we may want to verify here that the bp state is
* HALTING
*/
- DP(NETIF_MSG_IFDOWN,
+ DP(BNX2X_MSG_SP,
"got delete ramrod for MULTI[%d]\n", cid);
#ifdef BCM_CNIC
if (!bnx2x_cnic_handle_cfc_del(bp, cid, elem))
goto next_spqe;
- if (cid == BNX2X_FCOE_ETH_CID)
- bnx2x_fcoe(bp, state) = BNX2X_FP_STATE_CLOSED;
- else
#endif
- bnx2x_fp(bp, cid, state) =
- BNX2X_FP_STATE_CLOSED;
+ q_obj = bnx2x_cid_to_q_obj(bp, cid);
+
+ if (q_obj->complete_cmd(bp, q_obj, BNX2X_Q_CMD_CFC_DEL))
+ break;
+
+
goto next_spqe;
case EVENT_RING_OPCODE_STOP_TRAFFIC:
- DP(NETIF_MSG_IFUP, "got STOP TRAFFIC\n");
+ DP(BNX2X_MSG_SP, "got STOP TRAFFIC\n");
+ if (f_obj->complete_cmd(bp, f_obj,
+ BNX2X_F_CMD_TX_STOP))
+ break;
bnx2x_dcbx_set_params(bp, BNX2X_DCBX_STATE_TX_PAUSED);
goto next_spqe;
+
case EVENT_RING_OPCODE_START_TRAFFIC:
- DP(NETIF_MSG_IFUP, "got START TRAFFIC\n");
+ DP(BNX2X_MSG_SP, "got START TRAFFIC\n");
+ if (f_obj->complete_cmd(bp, f_obj,
+ BNX2X_F_CMD_TX_START))
+ break;
bnx2x_dcbx_set_params(bp, BNX2X_DCBX_STATE_TX_RELEASED);
goto next_spqe;
+ case EVENT_RING_OPCODE_FUNCTION_START:
+ DP(BNX2X_MSG_SP, "got FUNC_START ramrod\n");
+ if (f_obj->complete_cmd(bp, f_obj, BNX2X_F_CMD_START))
+ break;
+
+ goto next_spqe;
+
+ case EVENT_RING_OPCODE_FUNCTION_STOP:
+ DP(BNX2X_MSG_SP, "got FUNC_STOP ramrod\n");
+ if (f_obj->complete_cmd(bp, f_obj, BNX2X_F_CMD_STOP))
+ break;
+
+ goto next_spqe;
}
switch (opcode | bp->state) {
- case (EVENT_RING_OPCODE_FUNCTION_START |
+ case (EVENT_RING_OPCODE_RSS_UPDATE_RULES |
+ BNX2X_STATE_OPEN):
+ case (EVENT_RING_OPCODE_RSS_UPDATE_RULES |
BNX2X_STATE_OPENING_WAIT4_PORT):
- DP(NETIF_MSG_IFUP, "got setup ramrod\n");
- bp->state = BNX2X_STATE_FUNC_STARTED;
+ cid = elem->message.data.eth_event.echo &
+ BNX2X_SWCID_MASK;
+ DP(BNX2X_MSG_SP, "got RSS_UPDATE ramrod. CID %d\n",
+ cid);
+ rss_raw->clear_pending(rss_raw);
break;
- case (EVENT_RING_OPCODE_FUNCTION_STOP |
+ case (EVENT_RING_OPCODE_SET_MAC | BNX2X_STATE_OPEN):
+ case (EVENT_RING_OPCODE_SET_MAC | BNX2X_STATE_DIAG):
+ case (EVENT_RING_OPCODE_SET_MAC |
+ BNX2X_STATE_CLOSING_WAIT4_HALT):
+ case (EVENT_RING_OPCODE_CLASSIFICATION_RULES |
+ BNX2X_STATE_OPEN):
+ case (EVENT_RING_OPCODE_CLASSIFICATION_RULES |
+ BNX2X_STATE_DIAG):
+ case (EVENT_RING_OPCODE_CLASSIFICATION_RULES |
BNX2X_STATE_CLOSING_WAIT4_HALT):
- DP(NETIF_MSG_IFDOWN, "got halt ramrod\n");
- bp->state = BNX2X_STATE_CLOSING_WAIT4_UNLOAD;
+ DP(BNX2X_MSG_SP, "got (un)set mac ramrod\n");
+ bnx2x_handle_classification_eqe(bp, elem);
break;
- case (EVENT_RING_OPCODE_SET_MAC | BNX2X_STATE_OPEN):
- case (EVENT_RING_OPCODE_SET_MAC | BNX2X_STATE_DIAG):
- DP(NETIF_MSG_IFUP, "got set mac ramrod\n");
- if (elem->message.data.set_mac_event.echo)
- bp->set_mac_pending = 0;
+ case (EVENT_RING_OPCODE_MULTICAST_RULES |
+ BNX2X_STATE_OPEN):
+ case (EVENT_RING_OPCODE_MULTICAST_RULES |
+ BNX2X_STATE_DIAG):
+ case (EVENT_RING_OPCODE_MULTICAST_RULES |
+ BNX2X_STATE_CLOSING_WAIT4_HALT):
+ DP(BNX2X_MSG_SP, "got mcast ramrod\n");
+ bnx2x_handle_mcast_eqe(bp);
break;
- case (EVENT_RING_OPCODE_SET_MAC |
+ case (EVENT_RING_OPCODE_FILTERS_RULES |
+ BNX2X_STATE_OPEN):
+ case (EVENT_RING_OPCODE_FILTERS_RULES |
+ BNX2X_STATE_DIAG):
+ case (EVENT_RING_OPCODE_FILTERS_RULES |
BNX2X_STATE_CLOSING_WAIT4_HALT):
- DP(NETIF_MSG_IFDOWN, "got (un)set mac ramrod\n");
- if (elem->message.data.set_mac_event.echo)
- bp->set_mac_pending = 0;
+ DP(BNX2X_MSG_SP, "got rx_mode ramrod\n");
+ bnx2x_handle_rx_mode_eqe(bp);
break;
default:
/* unknown event log error and continue */
- BNX2X_ERR("Unknown EQ event %d\n",
- elem->message.opcode);
+ BNX2X_ERR("Unknown EQ event %d, bp->state 0x%x\n",
+ elem->message.opcode, bp->state);
}
next_spqe:
spqe_cnt++;
@@ -3811,12 +4582,6 @@ static void bnx2x_sp_task(struct work_struct *work)
struct bnx2x *bp = container_of(work, struct bnx2x, sp_task.work);
u16 status;
- /* Return here if interrupt is disabled */
- if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
- DP(NETIF_MSG_INTR, "called but intr_sem not 0, returning\n");
- return;
- }
-
status = bnx2x_update_dsb_idx(bp);
/* if (status == 0) */
/* BNX2X_ERR("spurious slowpath interrupt!\n"); */
@@ -3835,8 +4600,15 @@ static void bnx2x_sp_task(struct work_struct *work)
struct bnx2x_fastpath *fp = bnx2x_fcoe_fp(bp);
if ((!NO_FCOE(bp)) &&
- (bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp)))
+ (bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
+ /*
+ * Prevent local bottom-halves from running as
+ * we are going to change the local NAPI list.
+ */
+ local_bh_disable();
napi_schedule(&bnx2x_fcoe(bp, napi));
+ local_bh_enable();
+ }
#endif
/* Handle EQ completions */
bnx2x_eq_int(bp);
@@ -3860,12 +4632,6 @@ irqreturn_t bnx2x_msix_sp_int(int irq, void *dev_instance)
struct net_device *dev = dev_instance;
struct bnx2x *bp = netdev_priv(dev);
- /* Return here if interrupt is disabled */
- if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
- DP(NETIF_MSG_INTR, "called but intr_sem not 0, returning\n");
- return IRQ_HANDLED;
- }
-
bnx2x_ack_sb(bp, bp->igu_dsb_id, USTORM_ID, 0,
IGU_INT_DISABLE, 0);
@@ -3892,20 +4658,27 @@ irqreturn_t bnx2x_msix_sp_int(int irq, void *dev_instance)
/* end of slow path */
+
+void bnx2x_drv_pulse(struct bnx2x *bp)
+{
+ SHMEM_WR(bp, func_mb[BP_FW_MB_IDX(bp)].drv_pulse_mb,
+ bp->fw_drv_pulse_wr_seq);
+}
+
+
static void bnx2x_timer(unsigned long data)
{
+ u8 cos;
struct bnx2x *bp = (struct bnx2x *) data;
if (!netif_running(bp->dev))
return;
- if (atomic_read(&bp->intr_sem) != 0)
- goto timer_restart;
-
if (poll) {
struct bnx2x_fastpath *fp = &bp->fp[0];
- bnx2x_tx_int(fp);
+ for_each_cos_in_tx_queue(fp, cos)
+ bnx2x_tx_int(bp, &fp->txdata[cos]);
bnx2x_rx_int(fp, 1000);
}
@@ -3918,7 +4691,7 @@ static void bnx2x_timer(unsigned long data)
bp->fw_drv_pulse_wr_seq &= DRV_PULSE_SEQ_MASK;
/* TBD - add SYSTEM_TIME */
drv_pulse = bp->fw_drv_pulse_wr_seq;
- SHMEM_WR(bp, func_mb[mb_idx].drv_pulse_mb, drv_pulse);
+ bnx2x_drv_pulse(bp);
mcp_pulse = (SHMEM_RD(bp, func_mb[mb_idx].mcp_pulse_mb) &
MCP_PULSE_SEQ_MASK);
@@ -3936,7 +4709,6 @@ static void bnx2x_timer(unsigned long data)
if (bp->state == BNX2X_STATE_OPEN)
bnx2x_stats_handle(bp, STATS_EVENT_UPDATE);
-timer_restart:
mod_timer(&bp->timer, jiffies + bp->current_interval);
}
@@ -3982,18 +4754,16 @@ static inline void bnx2x_zero_fp_sb(struct bnx2x *bp, int fw_sb_id)
struct hc_status_block_data_e1x sb_data_e1x;
/* disable the function first */
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
memset(&sb_data_e2, 0, sizeof(struct hc_status_block_data_e2));
- sb_data_e2.common.p_func.pf_id = HC_FUNCTION_DISABLED;
- sb_data_e2.common.p_func.vf_id = HC_FUNCTION_DISABLED;
+ sb_data_e2.common.state = SB_DISABLED;
sb_data_e2.common.p_func.vf_valid = false;
sb_data_p = (u32 *)&sb_data_e2;
data_size = sizeof(struct hc_status_block_data_e2)/sizeof(u32);
} else {
memset(&sb_data_e1x, 0,
sizeof(struct hc_status_block_data_e1x));
- sb_data_e1x.common.p_func.pf_id = HC_FUNCTION_DISABLED;
- sb_data_e1x.common.p_func.vf_id = HC_FUNCTION_DISABLED;
+ sb_data_e1x.common.state = SB_DISABLED;
sb_data_e1x.common.p_func.vf_valid = false;
sb_data_p = (u32 *)&sb_data_e1x;
data_size = sizeof(struct hc_status_block_data_e1x)/sizeof(u32);
@@ -4027,8 +4797,7 @@ static inline void bnx2x_zero_sp_sb(struct bnx2x *bp)
struct hc_sp_status_block_data sp_sb_data;
memset(&sp_sb_data, 0, sizeof(struct hc_sp_status_block_data));
- sp_sb_data.p_func.pf_id = HC_FUNCTION_DISABLED;
- sp_sb_data.p_func.vf_id = HC_FUNCTION_DISABLED;
+ sp_sb_data.state = SB_DISABLED;
sp_sb_data.p_func.vf_valid = false;
bnx2x_wr_sp_sb_data(bp, &sp_sb_data);
@@ -4053,6 +4822,37 @@ void bnx2x_setup_ndsb_state_machine(struct hc_status_block_sm *hc_sm,
hc_sm->time_to_expire = 0xFFFFFFFF;
}
+
+/* allocates state machine ids. */
+static inline
+void bnx2x_map_sb_state_machines(struct hc_index_data *index_data)
+{
+ /* zero out state machine indices */
+ /* rx indices */
+ index_data[HC_INDEX_ETH_RX_CQ_CONS].flags &= ~HC_INDEX_DATA_SM_ID;
+
+ /* tx indices */
+ index_data[HC_INDEX_OOO_TX_CQ_CONS].flags &= ~HC_INDEX_DATA_SM_ID;
+ index_data[HC_INDEX_ETH_TX_CQ_CONS_COS0].flags &= ~HC_INDEX_DATA_SM_ID;
+ index_data[HC_INDEX_ETH_TX_CQ_CONS_COS1].flags &= ~HC_INDEX_DATA_SM_ID;
+ index_data[HC_INDEX_ETH_TX_CQ_CONS_COS2].flags &= ~HC_INDEX_DATA_SM_ID;
+
+ /* map indices */
+ /* rx indices */
+ index_data[HC_INDEX_ETH_RX_CQ_CONS].flags |=
+ SM_RX_ID << HC_INDEX_DATA_SM_ID_SHIFT;
+
+ /* tx indices */
+ index_data[HC_INDEX_OOO_TX_CQ_CONS].flags |=
+ SM_TX_ID << HC_INDEX_DATA_SM_ID_SHIFT;
+ index_data[HC_INDEX_ETH_TX_CQ_CONS_COS0].flags |=
+ SM_TX_ID << HC_INDEX_DATA_SM_ID_SHIFT;
+ index_data[HC_INDEX_ETH_TX_CQ_CONS_COS1].flags |=
+ SM_TX_ID << HC_INDEX_DATA_SM_ID_SHIFT;
+ index_data[HC_INDEX_ETH_TX_CQ_CONS_COS2].flags |=
+ SM_TX_ID << HC_INDEX_DATA_SM_ID_SHIFT;
+}
+
static void bnx2x_init_sb(struct bnx2x *bp, dma_addr_t mapping, int vfid,
u8 vf_valid, int fw_sb_id, int igu_sb_id)
{
@@ -4071,8 +4871,9 @@ static void bnx2x_init_sb(struct bnx2x *bp, dma_addr_t mapping, int vfid,
bnx2x_zero_fp_sb(bp, fw_sb_id);
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
memset(&sb_data_e2, 0, sizeof(struct hc_status_block_data_e2));
+ sb_data_e2.common.state = SB_ENABLED;
sb_data_e2.common.p_func.pf_id = BP_FUNC(bp);
sb_data_e2.common.p_func.vf_id = vfid;
sb_data_e2.common.p_func.vf_valid = vf_valid;
@@ -4083,9 +4884,11 @@ static void bnx2x_init_sb(struct bnx2x *bp, dma_addr_t mapping, int vfid,
hc_sm_p = sb_data_e2.common.state_machine;
sb_data_p = (u32 *)&sb_data_e2;
data_size = sizeof(struct hc_status_block_data_e2)/sizeof(u32);
+ bnx2x_map_sb_state_machines(sb_data_e2.index_data);
} else {
memset(&sb_data_e1x, 0,
sizeof(struct hc_status_block_data_e1x));
+ sb_data_e1x.common.state = SB_ENABLED;
sb_data_e1x.common.p_func.pf_id = BP_FUNC(bp);
sb_data_e1x.common.p_func.vf_id = 0xff;
sb_data_e1x.common.p_func.vf_valid = false;
@@ -4096,6 +4899,7 @@ static void bnx2x_init_sb(struct bnx2x *bp, dma_addr_t mapping, int vfid,
hc_sm_p = sb_data_e1x.common.state_machine;
sb_data_p = (u32 *)&sb_data_e1x;
data_size = sizeof(struct hc_status_block_data_e1x)/sizeof(u32);
+ bnx2x_map_sb_state_machines(sb_data_e1x.index_data);
}
bnx2x_setup_ndsb_state_machine(&hc_sm_p[SM_RX_ID],
@@ -4109,25 +4913,20 @@ static void bnx2x_init_sb(struct bnx2x *bp, dma_addr_t mapping, int vfid,
bnx2x_wr_fp_sb_data(bp, fw_sb_id, sb_data_p, data_size);
}
-static void bnx2x_update_coalesce_sb_index(struct bnx2x *bp, u16 fw_sb_id,
- u8 sb_index, u8 disable, u16 usec)
-{
- int port = BP_PORT(bp);
- u8 ticks = usec / BNX2X_BTR;
-
- storm_memset_hc_timeout(bp, port, fw_sb_id, sb_index, ticks);
-
- disable = disable ? 1 : (usec ? 0 : 1);
- storm_memset_hc_disable(bp, port, fw_sb_id, sb_index, disable);
-}
-
-static void bnx2x_update_coalesce_sb(struct bnx2x *bp, u16 fw_sb_id,
+static void bnx2x_update_coalesce_sb(struct bnx2x *bp, u8 fw_sb_id,
u16 tx_usec, u16 rx_usec)
{
- bnx2x_update_coalesce_sb_index(bp, fw_sb_id, U_SB_ETH_RX_CQ_INDEX,
+ bnx2x_update_coalesce_sb_index(bp, fw_sb_id, HC_INDEX_ETH_RX_CQ_CONS,
false, rx_usec);
- bnx2x_update_coalesce_sb_index(bp, fw_sb_id, C_SB_ETH_TX_CQ_INDEX,
- false, tx_usec);
+ bnx2x_update_coalesce_sb_index(bp, fw_sb_id,
+ HC_INDEX_ETH_TX_CQ_CONS_COS0, false,
+ tx_usec);
+ bnx2x_update_coalesce_sb_index(bp, fw_sb_id,
+ HC_INDEX_ETH_TX_CQ_CONS_COS1, false,
+ tx_usec);
+ bnx2x_update_coalesce_sb_index(bp, fw_sb_id,
+ HC_INDEX_ETH_TX_CQ_CONS_COS2, false,
+ tx_usec);
}
static void bnx2x_init_def_sb(struct bnx2x *bp)
@@ -4138,7 +4937,7 @@ static void bnx2x_init_def_sb(struct bnx2x *bp)
int igu_seg_id;
int port = BP_PORT(bp);
int func = BP_FUNC(bp);
- int reg_offset;
+ int reg_offset, reg_offset_en5;
u64 section;
int index;
struct hc_sp_status_block_data sp_sb_data;
@@ -4161,6 +4960,8 @@ static void bnx2x_init_def_sb(struct bnx2x *bp)
reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 :
MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0);
+ reg_offset_en5 = (port ? MISC_REG_AEU_ENABLE5_FUNC_1_OUT_0 :
+ MISC_REG_AEU_ENABLE5_FUNC_0_OUT_0);
for (index = 0; index < MAX_DYNAMIC_ATTN_GRPS; index++) {
int sindex;
/* take care of sig[0]..sig[4] */
@@ -4168,14 +4969,14 @@ static void bnx2x_init_def_sb(struct bnx2x *bp)
bp->attn_group[index].sig[sindex] =
REG_RD(bp, reg_offset + sindex*0x4 + 0x10*index);
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
/*
* enable5 is separate from the rest of the registers,
* and therefore the address skip is 4
* and not 16 between the different groups
*/
bp->attn_group[index].sig[4] = REG_RD(bp,
- reg_offset + 0x10 + 0x4*index);
+ reg_offset_en5 + 0x4*index);
else
bp->attn_group[index].sig[4] = 0;
}
@@ -4186,7 +4987,7 @@ static void bnx2x_init_def_sb(struct bnx2x *bp)
REG_WR(bp, reg_offset, U64_LO(section));
REG_WR(bp, reg_offset + 4, U64_HI(section));
- } else if (CHIP_IS_E2(bp)) {
+ } else if (!CHIP_IS_E1x(bp)) {
REG_WR(bp, IGU_REG_ATTN_MSG_ADDR_L, U64_LO(section));
REG_WR(bp, IGU_REG_ATTN_MSG_ADDR_H, U64_HI(section));
}
@@ -4196,6 +4997,7 @@ static void bnx2x_init_def_sb(struct bnx2x *bp)
bnx2x_zero_sp_sb(bp);
+ sp_sb_data.state = SB_ENABLED;
sp_sb_data.host_sb_addr.lo = U64_LO(section);
sp_sb_data.host_sb_addr.hi = U64_HI(section);
sp_sb_data.igu_sb_id = igu_sp_sb_index;
@@ -4206,9 +5008,6 @@ static void bnx2x_init_def_sb(struct bnx2x *bp)
bnx2x_wr_sp_sb_data(bp, &sp_sb_data);
- bp->stats_pending = 0;
- bp->set_mac_pending = 0;
-
bnx2x_ack_sb(bp, bp->igu_dsb_id, USTORM_ID, 0, IGU_INT_ENABLE, 0);
}
@@ -4254,146 +5053,129 @@ static void bnx2x_init_eq_ring(struct bnx2x *bp)
min_t(int, MAX_SP_DESC_CNT - MAX_SPQ_PENDING, NUM_EQ_DESC) - 1);
}
-void bnx2x_push_indir_table(struct bnx2x *bp)
+
+/* called with netif_addr_lock_bh() */
+void bnx2x_set_q_rx_mode(struct bnx2x *bp, u8 cl_id,
+ unsigned long rx_mode_flags,
+ unsigned long rx_accept_flags,
+ unsigned long tx_accept_flags,
+ unsigned long ramrod_flags)
{
- int func = BP_FUNC(bp);
- int i;
+ struct bnx2x_rx_mode_ramrod_params ramrod_param;
+ int rc;
- if (bp->multi_mode == ETH_RSS_MODE_DISABLED)
- return;
+ memset(&ramrod_param, 0, sizeof(ramrod_param));
- for (i = 0; i < TSTORM_INDIRECTION_TABLE_SIZE; i++)
- REG_WR8(bp, BAR_TSTRORM_INTMEM +
- TSTORM_INDIRECTION_TABLE_OFFSET(func) + i,
- bp->fp->cl_id + bp->rx_indir_table[i]);
-}
+ /* Prepare ramrod parameters */
+ ramrod_param.cid = 0;
+ ramrod_param.cl_id = cl_id;
+ ramrod_param.rx_mode_obj = &bp->rx_mode_obj;
+ ramrod_param.func_id = BP_FUNC(bp);
-static void bnx2x_init_ind_table(struct bnx2x *bp)
-{
- int i;
+ ramrod_param.pstate = &bp->sp_state;
+ ramrod_param.state = BNX2X_FILTER_RX_MODE_PENDING;
+
+ ramrod_param.rdata = bnx2x_sp(bp, rx_mode_rdata);
+ ramrod_param.rdata_mapping = bnx2x_sp_mapping(bp, rx_mode_rdata);
+
+ set_bit(BNX2X_FILTER_RX_MODE_PENDING, &bp->sp_state);
+
+ ramrod_param.ramrod_flags = ramrod_flags;
+ ramrod_param.rx_mode_flags = rx_mode_flags;
- for (i = 0; i < TSTORM_INDIRECTION_TABLE_SIZE; i++)
- bp->rx_indir_table[i] = i % BNX2X_NUM_ETH_QUEUES(bp);
+ ramrod_param.rx_accept_flags = rx_accept_flags;
+ ramrod_param.tx_accept_flags = tx_accept_flags;
- bnx2x_push_indir_table(bp);
+ rc = bnx2x_config_rx_mode(bp, &ramrod_param);
+ if (rc < 0) {
+ BNX2X_ERR("Set rx_mode %d failed\n", bp->rx_mode);
+ return;
+ }
}
+/* called with netif_addr_lock_bh() */
void bnx2x_set_storm_rx_mode(struct bnx2x *bp)
{
- int mode = bp->rx_mode;
- int port = BP_PORT(bp);
- u16 cl_id;
- u32 def_q_filters = 0;
-
- /* All but management unicast packets should pass to the host as well */
- u32 llh_mask =
- NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_BRCST |
- NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_MLCST |
- NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_VLAN |
- NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_NO_VLAN;
+ unsigned long rx_mode_flags = 0, ramrod_flags = 0;
+ unsigned long rx_accept_flags = 0, tx_accept_flags = 0;
- switch (mode) {
- case BNX2X_RX_MODE_NONE: /* no Rx */
- def_q_filters = BNX2X_ACCEPT_NONE;
#ifdef BCM_CNIC
- if (!NO_FCOE(bp)) {
- cl_id = bnx2x_fcoe(bp, cl_id);
- bnx2x_rxq_set_mac_filters(bp, cl_id, BNX2X_ACCEPT_NONE);
- }
-#endif
- break;
+ if (!NO_FCOE(bp))
- case BNX2X_RX_MODE_NORMAL:
- def_q_filters |= BNX2X_ACCEPT_UNICAST | BNX2X_ACCEPT_BROADCAST |
- BNX2X_ACCEPT_MULTICAST;
-#ifdef BCM_CNIC
- if (!NO_FCOE(bp)) {
- cl_id = bnx2x_fcoe(bp, cl_id);
- bnx2x_rxq_set_mac_filters(bp, cl_id,
- BNX2X_ACCEPT_UNICAST |
- BNX2X_ACCEPT_MULTICAST);
- }
+ /* Configure rx_mode of FCoE Queue */
+ __set_bit(BNX2X_RX_MODE_FCOE_ETH, &rx_mode_flags);
#endif
- break;
- case BNX2X_RX_MODE_ALLMULTI:
- def_q_filters |= BNX2X_ACCEPT_UNICAST | BNX2X_ACCEPT_BROADCAST |
- BNX2X_ACCEPT_ALL_MULTICAST;
-#ifdef BCM_CNIC
+ switch (bp->rx_mode) {
+ case BNX2X_RX_MODE_NONE:
/*
- * Prevent duplication of multicast packets by configuring FCoE
- * L2 Client to receive only matched unicast frames.
+ * 'drop all' supersedes any accept flags that may have been
+ * passed to the function.
*/
- if (!NO_FCOE(bp)) {
- cl_id = bnx2x_fcoe(bp, cl_id);
- bnx2x_rxq_set_mac_filters(bp, cl_id,
- BNX2X_ACCEPT_UNICAST);
- }
-#endif
break;
+ case BNX2X_RX_MODE_NORMAL:
+ __set_bit(BNX2X_ACCEPT_UNICAST, &rx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_MULTICAST, &rx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_BROADCAST, &rx_accept_flags);
+
+ /* internal switching mode */
+ __set_bit(BNX2X_ACCEPT_UNICAST, &tx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_MULTICAST, &tx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_BROADCAST, &tx_accept_flags);
+
+ break;
+ case BNX2X_RX_MODE_ALLMULTI:
+ __set_bit(BNX2X_ACCEPT_UNICAST, &rx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_ALL_MULTICAST, &rx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_BROADCAST, &rx_accept_flags);
+
+ /* internal switching mode */
+ __set_bit(BNX2X_ACCEPT_UNICAST, &tx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_ALL_MULTICAST, &tx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_BROADCAST, &tx_accept_flags);
+ break;
case BNX2X_RX_MODE_PROMISC:
- def_q_filters |= BNX2X_PROMISCUOUS_MODE;
-#ifdef BCM_CNIC
- /*
- * Prevent packets duplication by configuring DROP_ALL for FCoE
- * L2 Client.
+ /* According to deffinition of SI mode, iface in promisc mode
+ * should receive matched and unmatched (in resolution of port)
+ * unicast packets.
*/
- if (!NO_FCOE(bp)) {
- cl_id = bnx2x_fcoe(bp, cl_id);
- bnx2x_rxq_set_mac_filters(bp, cl_id, BNX2X_ACCEPT_NONE);
- }
-#endif
- /* pass management unicast packets as well */
- llh_mask |= NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_UNCST;
- break;
+ __set_bit(BNX2X_ACCEPT_UNMATCHED, &rx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_UNICAST, &rx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_ALL_MULTICAST, &rx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_BROADCAST, &rx_accept_flags);
+
+ /* internal switching mode */
+ __set_bit(BNX2X_ACCEPT_ALL_MULTICAST, &tx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_BROADCAST, &tx_accept_flags);
+
+ if (IS_MF_SI(bp))
+ __set_bit(BNX2X_ACCEPT_ALL_UNICAST, &tx_accept_flags);
+ else
+ __set_bit(BNX2X_ACCEPT_UNICAST, &tx_accept_flags);
- default:
- BNX2X_ERR("BAD rx mode (%d)\n", mode);
break;
+ default:
+ BNX2X_ERR("Unknown rx_mode: %d\n", bp->rx_mode);
+ return;
}
- cl_id = BP_L_ID(bp);
- bnx2x_rxq_set_mac_filters(bp, cl_id, def_q_filters);
-
- REG_WR(bp,
- (port ? NIG_REG_LLH1_BRB1_DRV_MASK :
- NIG_REG_LLH0_BRB1_DRV_MASK), llh_mask);
+ if (bp->rx_mode != BNX2X_RX_MODE_NONE) {
+ __set_bit(BNX2X_ACCEPT_ANY_VLAN, &rx_accept_flags);
+ __set_bit(BNX2X_ACCEPT_ANY_VLAN, &tx_accept_flags);
+ }
- DP(NETIF_MSG_IFUP, "rx mode %d\n"
- "drop_ucast 0x%x\ndrop_mcast 0x%x\ndrop_bcast 0x%x\n"
- "accp_ucast 0x%x\naccp_mcast 0x%x\naccp_bcast 0x%x\n"
- "unmatched_ucast 0x%x\n", mode,
- bp->mac_filters.ucast_drop_all,
- bp->mac_filters.mcast_drop_all,
- bp->mac_filters.bcast_drop_all,
- bp->mac_filters.ucast_accept_all,
- bp->mac_filters.mcast_accept_all,
- bp->mac_filters.bcast_accept_all,
- bp->mac_filters.unmatched_unicast
- );
+ __set_bit(RAMROD_RX, &ramrod_flags);
+ __set_bit(RAMROD_TX, &ramrod_flags);
- storm_memset_mac_filters(bp, &bp->mac_filters, BP_FUNC(bp));
+ bnx2x_set_q_rx_mode(bp, bp->fp->cl_id, rx_mode_flags, rx_accept_flags,
+ tx_accept_flags, ramrod_flags);
}
static void bnx2x_init_internal_common(struct bnx2x *bp)
{
int i;
- if (!CHIP_IS_E1(bp)) {
-
- /* xstorm needs to know whether to add ovlan to packets or not,
- * in switch-independent we'll write 0 to here... */
- REG_WR8(bp, BAR_XSTRORM_INTMEM + XSTORM_FUNCTION_MODE_OFFSET,
- bp->mf_mode);
- REG_WR8(bp, BAR_TSTRORM_INTMEM + TSTORM_FUNCTION_MODE_OFFSET,
- bp->mf_mode);
- REG_WR8(bp, BAR_CSTRORM_INTMEM + CSTORM_FUNCTION_MODE_OFFSET,
- bp->mf_mode);
- REG_WR8(bp, BAR_USTRORM_INTMEM + USTORM_FUNCTION_MODE_OFFSET,
- bp->mf_mode);
- }
-
if (IS_MF_SI(bp))
/*
* In switch independent mode, the TSTORM needs to accept
@@ -4402,25 +5184,22 @@ static void bnx2x_init_internal_common(struct bnx2x *bp)
*/
REG_WR8(bp, BAR_TSTRORM_INTMEM +
TSTORM_ACCEPT_CLASSIFY_FAILED_OFFSET, 2);
+ else if (!CHIP_IS_E1(bp)) /* 57710 doesn't support MF */
+ REG_WR8(bp, BAR_TSTRORM_INTMEM +
+ TSTORM_ACCEPT_CLASSIFY_FAILED_OFFSET, 0);
/* Zero this manually as its initialization is
currently missing in the initTool */
for (i = 0; i < (USTORM_AGG_DATA_SIZE >> 2); i++)
REG_WR(bp, BAR_USTRORM_INTMEM +
USTORM_AGG_DATA_OFFSET + i * 4, 0);
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
REG_WR8(bp, BAR_CSTRORM_INTMEM + CSTORM_IGU_MODE_OFFSET,
CHIP_INT_MODE_IS_BC(bp) ?
HC_IGU_BC_MODE : HC_IGU_NBC_MODE);
}
}
-static void bnx2x_init_internal_port(struct bnx2x *bp)
-{
- /* port */
- bnx2x_dcb_init_intmem_pfc(bp);
-}
-
static void bnx2x_init_internal(struct bnx2x *bp, u32 load_code)
{
switch (load_code) {
@@ -4430,7 +5209,7 @@ static void bnx2x_init_internal(struct bnx2x *bp, u32 load_code)
/* no break */
case FW_MSG_CODE_DRV_LOAD_PORT:
- bnx2x_init_internal_port(bp);
+ /* nothing to do */
/* no break */
case FW_MSG_CODE_DRV_LOAD_FUNCTION:
@@ -4444,31 +5223,70 @@ static void bnx2x_init_internal(struct bnx2x *bp, u32 load_code)
}
}
-static void bnx2x_init_fp_sb(struct bnx2x *bp, int fp_idx)
+static inline u8 bnx2x_fp_igu_sb_id(struct bnx2x_fastpath *fp)
{
- struct bnx2x_fastpath *fp = &bp->fp[fp_idx];
+ return fp->bp->igu_base_sb + fp->index + CNIC_PRESENT;
+}
+
+static inline u8 bnx2x_fp_fw_sb_id(struct bnx2x_fastpath *fp)
+{
+ return fp->bp->base_fw_ndsb + fp->index + CNIC_PRESENT;
+}
- fp->state = BNX2X_FP_STATE_CLOSED;
+static inline u8 bnx2x_fp_cl_id(struct bnx2x_fastpath *fp)
+{
+ if (CHIP_IS_E1x(fp->bp))
+ return BP_L_ID(fp->bp) + fp->index;
+ else /* We want Client ID to be the same as IGU SB ID for 57712 */
+ return bnx2x_fp_igu_sb_id(fp);
+}
+
+static void bnx2x_init_eth_fp(struct bnx2x *bp, int fp_idx)
+{
+ struct bnx2x_fastpath *fp = &bp->fp[fp_idx];
+ u8 cos;
+ unsigned long q_type = 0;
+ u32 cids[BNX2X_MULTI_TX_COS] = { 0 };
fp->cid = fp_idx;
- fp->cl_id = BP_L_ID(bp) + fp_idx;
- fp->fw_sb_id = bp->base_fw_ndsb + fp->cl_id + CNIC_CONTEXT_USE;
- fp->igu_sb_id = bp->igu_base_sb + fp_idx + CNIC_CONTEXT_USE;
+ fp->cl_id = bnx2x_fp_cl_id(fp);
+ fp->fw_sb_id = bnx2x_fp_fw_sb_id(fp);
+ fp->igu_sb_id = bnx2x_fp_igu_sb_id(fp);
/* qZone id equals to FW (per path) client id */
- fp->cl_qzone_id = fp->cl_id +
- BP_PORT(bp)*(CHIP_IS_E2(bp) ? ETH_MAX_RX_CLIENTS_E2 :
- ETH_MAX_RX_CLIENTS_E1H);
+ fp->cl_qzone_id = bnx2x_fp_qzone_id(fp);
+
/* init shortcut */
- fp->ustorm_rx_prods_offset = CHIP_IS_E2(bp) ?
- USTORM_RX_PRODS_E2_OFFSET(fp->cl_qzone_id) :
- USTORM_RX_PRODS_E1X_OFFSET(BP_PORT(bp), fp->cl_id);
+ fp->ustorm_rx_prods_offset = bnx2x_rx_ustorm_prods_offset(fp);
/* Setup SB indicies */
fp->rx_cons_sb = BNX2X_RX_SB_INDEX;
- fp->tx_cons_sb = BNX2X_TX_SB_INDEX;
+
+ /* Configure Queue State object */
+ __set_bit(BNX2X_Q_TYPE_HAS_RX, &q_type);
+ __set_bit(BNX2X_Q_TYPE_HAS_TX, &q_type);
+
+ BUG_ON(fp->max_cos > BNX2X_MULTI_TX_COS);
+
+ /* init tx data */
+ for_each_cos_in_tx_queue(fp, cos) {
+ bnx2x_init_txdata(bp, &fp->txdata[cos],
+ CID_COS_TO_TX_ONLY_CID(fp->cid, cos),
+ FP_COS_TO_TXQ(fp, cos),
+ BNX2X_TX_SB_INDEX_BASE + cos);
+ cids[cos] = fp->txdata[cos].cid;
+ }
+
+ bnx2x_init_queue_obj(bp, &fp->q_obj, fp->cl_id, cids, fp->max_cos,
+ BP_FUNC(bp), bnx2x_sp(bp, q_rdata),
+ bnx2x_sp_mapping(bp, q_rdata), q_type);
+
+ /**
+ * Configure classification DBs: Always enable Tx switching
+ */
+ bnx2x_init_vlan_mac_fp_objs(fp, BNX2X_OBJ_TYPE_RX_TX);
DP(NETIF_MSG_IFUP, "queue[%d]: bnx2x_init_sb(%p,%p) "
"cl_id %d fw_sb %d igu_sb %d\n",
- fp_idx, bp, fp->status_blk.e1x_sb, fp->cl_id, fp->fw_sb_id,
+ fp_idx, bp, fp->status_blk.e2_sb, fp->cl_id, fp->fw_sb_id,
fp->igu_sb_id);
bnx2x_init_sb(bp, fp->status_blk_mapping, BNX2X_VF_ID_INVALID, false,
fp->fw_sb_id, fp->igu_sb_id);
@@ -4481,17 +5299,21 @@ void bnx2x_nic_init(struct bnx2x *bp, u32 load_code)
int i;
for_each_eth_queue(bp, i)
- bnx2x_init_fp_sb(bp, i);
+ bnx2x_init_eth_fp(bp, i);
#ifdef BCM_CNIC
if (!NO_FCOE(bp))
bnx2x_init_fcoe_fp(bp);
bnx2x_init_sb(bp, bp->cnic_sb_mapping,
BNX2X_VF_ID_INVALID, false,
- CNIC_SB_ID(bp), CNIC_IGU_SB_ID(bp));
+ bnx2x_cnic_fw_sb_id(bp), bnx2x_cnic_igu_sb_id(bp));
#endif
+ /* Initialize MOD_ABS interrupts */
+ bnx2x_init_mod_abs_int(bp, &bp->link_vars, bp->common.chip_id,
+ bp->common.shmem_base, bp->common.shmem2_base,
+ BP_PORT(bp));
/* ensure status block indices were read */
rmb();
@@ -4503,12 +5325,8 @@ void bnx2x_nic_init(struct bnx2x *bp, u32 load_code)
bnx2x_init_eq_ring(bp);
bnx2x_init_internal(bp, load_code);
bnx2x_pf_init(bp);
- bnx2x_init_ind_table(bp);
bnx2x_stats_init(bp);
- /* At this point, we are ready for interrupts */
- atomic_set(&bp->intr_sem, 0);
-
/* flush all before enabling interrupts */
mb();
mmiowb();
@@ -4711,8 +5529,8 @@ static int bnx2x_int_mem_test(struct bnx2x *bp)
msleep(50);
REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x03);
msleep(50);
- bnx2x_init_block(bp, BRB1_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, PRS_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_BRB1, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_PRS, PHASE_COMMON);
DP(NETIF_MSG_HW, "part2\n");
@@ -4776,8 +5594,8 @@ static int bnx2x_int_mem_test(struct bnx2x *bp)
msleep(50);
REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x03);
msleep(50);
- bnx2x_init_block(bp, BRB1_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, PRS_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_BRB1, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_PRS, PHASE_COMMON);
#ifndef BCM_CNIC
/* set NIC mode */
REG_WR(bp, PRS_REG_NIC_MODE, 1);
@@ -4797,7 +5615,7 @@ static int bnx2x_int_mem_test(struct bnx2x *bp)
static void bnx2x_enable_blocks_attention(struct bnx2x *bp)
{
REG_WR(bp, PXP_REG_PXP_INT_MASK_0, 0);
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
REG_WR(bp, PXP_REG_PXP_INT_MASK_1, 0x40);
else
REG_WR(bp, PXP_REG_PXP_INT_MASK_1, 0);
@@ -4831,7 +5649,7 @@ static void bnx2x_enable_blocks_attention(struct bnx2x *bp)
if (CHIP_REV_IS_FPGA(bp))
REG_WR(bp, PXP2_REG_PXP2_INT_MASK_0, 0x580000);
- else if (CHIP_IS_E2(bp))
+ else if (!CHIP_IS_E1x(bp))
REG_WR(bp, PXP2_REG_PXP2_INT_MASK_0,
(PXP2_PXP2_INT_MASK_0_REG_PGL_CPL_OF
| PXP2_PXP2_INT_MASK_0_REG_PGL_CPL_AFT
@@ -4844,7 +5662,11 @@ static void bnx2x_enable_blocks_attention(struct bnx2x *bp)
REG_WR(bp, TSDM_REG_TSDM_INT_MASK_1, 0);
REG_WR(bp, TCM_REG_TCM_INT_MASK, 0);
/* REG_WR(bp, TSEM_REG_TSEM_INT_MASK_0, 0); */
-/* REG_WR(bp, TSEM_REG_TSEM_INT_MASK_1, 0); */
+
+ if (!CHIP_IS_E1x(bp))
+ /* enable VFC attentions: bits 11 and 12, bits 31:13 reserved */
+ REG_WR(bp, TSEM_REG_TSEM_INT_MASK_1, 0x07ff);
+
REG_WR(bp, CDU_REG_CDU_INT_MASK, 0);
REG_WR(bp, DMAE_REG_DMAE_INT_MASK, 0);
/* REG_WR(bp, MISC_REG_MISC_INT_MASK, 0); */
@@ -4853,10 +5675,24 @@ static void bnx2x_enable_blocks_attention(struct bnx2x *bp)
static void bnx2x_reset_common(struct bnx2x *bp)
{
+ u32 val = 0x1400;
+
/* reset_common */
REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR,
0xd3ffff7f);
- REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, 0x1403);
+
+ if (CHIP_IS_E3(bp)) {
+ val |= MISC_REGISTERS_RESET_REG_2_MSTAT0;
+ val |= MISC_REGISTERS_RESET_REG_2_MSTAT1;
+ }
+
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, val);
+}
+
+static void bnx2x_setup_dmae(struct bnx2x *bp)
+{
+ bp->dmae_ready = 0;
+ spin_lock_init(&bp->dmae_lock);
}
static void bnx2x_init_pxp(struct bnx2x *bp)
@@ -4865,7 +5701,7 @@ static void bnx2x_init_pxp(struct bnx2x *bp)
int r_order, w_order;
pci_read_config_word(bp->pdev,
- bp->pcie_cap + PCI_EXP_DEVCTL, &devctl);
+ pci_pcie_cap(bp->pdev) + PCI_EXP_DEVCTL, &devctl);
DP(NETIF_MSG_HW, "read 0x%x from devctl\n", devctl);
w_order = ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
if (bp->mrrs == -1)
@@ -4973,7 +5809,7 @@ static void bnx2x_pretend_func(struct bnx2x *bp, u8 pretend_func_num)
DP(NETIF_MSG_HW, "Pretending to func %d\n", pretend_func_num);
}
-static void bnx2x_pf_disable(struct bnx2x *bp)
+void bnx2x_pf_disable(struct bnx2x *bp)
{
u32 val = REG_RD(bp, IGU_REG_PF_CONFIGURATION);
val &= ~IGU_PF_CONF_FUNC_EN;
@@ -4983,22 +5819,56 @@ static void bnx2x_pf_disable(struct bnx2x *bp)
REG_WR(bp, CFC_REG_WEAK_ENABLE_PF, 0);
}
-static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
+static inline void bnx2x__common_init_phy(struct bnx2x *bp)
{
- u32 val, i;
+ u32 shmem_base[2], shmem2_base[2];
+ shmem_base[0] = bp->common.shmem_base;
+ shmem2_base[0] = bp->common.shmem2_base;
+ if (!CHIP_IS_E1x(bp)) {
+ shmem_base[1] =
+ SHMEM2_RD(bp, other_shmem_base_addr);
+ shmem2_base[1] =
+ SHMEM2_RD(bp, other_shmem2_base_addr);
+ }
+ bnx2x_acquire_phy_lock(bp);
+ bnx2x_common_init_phy(bp, shmem_base, shmem2_base,
+ bp->common.chip_id);
+ bnx2x_release_phy_lock(bp);
+}
+
+/**
+ * bnx2x_init_hw_common - initialize the HW at the COMMON phase.
+ *
+ * @bp: driver handle
+ */
+static int bnx2x_init_hw_common(struct bnx2x *bp)
+{
+ u32 val;
DP(BNX2X_MSG_MCP, "starting common init func %d\n", BP_ABS_FUNC(bp));
+ /*
+ * take the UNDI lock to protect undi_unload flow from accessing
+ * registers while we're resetting the chip
+ */
+ bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_RESET);
+
bnx2x_reset_common(bp);
REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0xffffffff);
- REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, 0xfffc);
- bnx2x_init_block(bp, MISC_BLOCK, COMMON_STAGE);
- if (!CHIP_IS_E1(bp))
- REG_WR(bp, MISC_REG_E1HMF_MODE, IS_MF(bp));
+ val = 0xfffc;
+ if (CHIP_IS_E3(bp)) {
+ val |= MISC_REGISTERS_RESET_REG_2_MSTAT0;
+ val |= MISC_REGISTERS_RESET_REG_2_MSTAT1;
+ }
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, val);
+
+ bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_RESET);
- if (CHIP_IS_E2(bp)) {
- u8 fid;
+ bnx2x_init_block(bp, BLOCK_MISC, PHASE_COMMON);
+
+ if (!CHIP_IS_E1x(bp)) {
+ u8 abs_func_id;
/**
* 4-port mode or 2-port mode we need to turn of master-enable
@@ -5007,29 +5877,30 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
* for all functions on the given path, this means 0,2,4,6 for
* path 0 and 1,3,5,7 for path 1
*/
- for (fid = BP_PATH(bp); fid < E2_FUNC_MAX*2; fid += 2) {
- if (fid == BP_ABS_FUNC(bp)) {
+ for (abs_func_id = BP_PATH(bp);
+ abs_func_id < E2_FUNC_MAX*2; abs_func_id += 2) {
+ if (abs_func_id == BP_ABS_FUNC(bp)) {
REG_WR(bp,
PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER,
1);
continue;
}
- bnx2x_pretend_func(bp, fid);
+ bnx2x_pretend_func(bp, abs_func_id);
/* clear pf enable */
bnx2x_pf_disable(bp);
bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
}
}
- bnx2x_init_block(bp, PXP_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_PXP, PHASE_COMMON);
if (CHIP_IS_E1(bp)) {
/* enable HW interrupt from PXP on USDM overflow
bit 16 on INT_MASK_0 */
REG_WR(bp, PXP_REG_PXP_INT_MASK_0, 0);
}
- bnx2x_init_block(bp, PXP2_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_PXP2, PHASE_COMMON);
bnx2x_init_pxp(bp);
#ifdef __BIG_ENDIAN
@@ -5072,7 +5943,69 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
* This needs to be done by the first PF that is loaded in a path
* (i.e. common phase)
*/
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
+/* In E2 there is a bug in the timers block that can cause function 6 / 7
+ * (i.e. vnic3) to start even if it is marked as "scan-off".
+ * This occurs when a different function (func2,3) is being marked
+ * as "scan-off". Real-life scenario for example: if a driver is being
+ * load-unloaded while func6,7 are down. This will cause the timer to access
+ * the ilt, translate to a logical address and send a request to read/write.
+ * Since the ilt for the function that is down is not valid, this will cause
+ * a translation error which is unrecoverable.
+ * The Workaround is intended to make sure that when this happens nothing fatal
+ * will occur. The workaround:
+ * 1. First PF driver which loads on a path will:
+ * a. After taking the chip out of reset, by using pretend,
+ * it will write "0" to the following registers of
+ * the other vnics.
+ * REG_WR(pdev, PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER, 0);
+ * REG_WR(pdev, CFC_REG_WEAK_ENABLE_PF,0);
+ * REG_WR(pdev, CFC_REG_STRONG_ENABLE_PF,0);
+ * And for itself it will write '1' to
+ * PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER to enable
+ * dmae-operations (writing to pram for example.)
+ * note: can be done for only function 6,7 but cleaner this
+ * way.
+ * b. Write zero+valid to the entire ILT.
+ * c. Init the first_timers_ilt_entry, last_timers_ilt_entry of
+ * VNIC3 (of that port). The range allocated will be the
+ * entire ILT. This is needed to prevent ILT range error.
+ * 2. Any PF driver load flow:
+ * a. ILT update with the physical addresses of the allocated
+ * logical pages.
+ * b. Wait 20msec. - note that this timeout is needed to make
+ * sure there are no requests in one of the PXP internal
+ * queues with "old" ILT addresses.
+ * c. PF enable in the PGLC.
+ * d. Clear the was_error of the PF in the PGLC. (could have
+ * occured while driver was down)
+ * e. PF enable in the CFC (WEAK + STRONG)
+ * f. Timers scan enable
+ * 3. PF driver unload flow:
+ * a. Clear the Timers scan_en.
+ * b. Polling for scan_on=0 for that PF.
+ * c. Clear the PF enable bit in the PXP.
+ * d. Clear the PF enable in the CFC (WEAK + STRONG)
+ * e. Write zero+valid to all ILT entries (The valid bit must
+ * stay set)
+ * f. If this is VNIC 3 of a port then also init
+ * first_timers_ilt_entry to zero and last_timers_ilt_entry
+ * to the last enrty in the ILT.
+ *
+ * Notes:
+ * Currently the PF error in the PGLC is non recoverable.
+ * In the future the there will be a recovery routine for this error.
+ * Currently attention is masked.
+ * Having an MCP lock on the load/unload process does not guarantee that
+ * there is no Timer disable during Func6/7 enable. This is because the
+ * Timers scan is currently being cleared by the MCP on FLR.
+ * Step 2.d can be done only for PF6/7 and the driver can also check if
+ * there is error before clearing it. But the flow above is simpler and
+ * more general.
+ * All ILT entries are written by zero+valid and not just PF6/7
+ * ILT entries since in the future the ILT entries allocation for
+ * PF-s might be dynamic.
+ */
struct ilt_client_info ilt_cli;
struct bnx2x_ilt ilt;
memset(&ilt_cli, 0, sizeof(struct ilt_client_info));
@@ -5086,7 +6019,7 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
/* Step 1: set zeroes to all ilt page entries with valid bit on
* Step 2: set the timers first/last ilt entry to point
* to the entire range to prevent ILT range error for 3rd/4th
- * vnic (this code assumes existence of the vnic)
+ * vnic (this code assumes existance of the vnic)
*
* both steps performed by call to bnx2x_ilt_client_init_op()
* with dummy TM client
@@ -5107,12 +6040,12 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
REG_WR(bp, PXP2_REG_RQ_DISABLE_INPUTS, 0);
REG_WR(bp, PXP2_REG_RD_DISABLE_INPUTS, 0);
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
int factor = CHIP_REV_IS_EMUL(bp) ? 1000 :
(CHIP_REV_IS_FPGA(bp) ? 400 : 0);
- bnx2x_init_block(bp, PGLUE_B_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_PGLUE_B, PHASE_COMMON);
- bnx2x_init_block(bp, ATC_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_ATC, PHASE_COMMON);
/* let the HW do it's magic ... */
do {
@@ -5126,26 +6059,27 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
}
}
- bnx2x_init_block(bp, DMAE_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_DMAE, PHASE_COMMON);
/* clean the DMAE memory */
bp->dmae_ready = 1;
- bnx2x_init_fill(bp, TSEM_REG_PRAM, 0, 8);
+ bnx2x_init_fill(bp, TSEM_REG_PRAM, 0, 8, 1);
+
+ bnx2x_init_block(bp, BLOCK_TCM, PHASE_COMMON);
+
+ bnx2x_init_block(bp, BLOCK_UCM, PHASE_COMMON);
+
+ bnx2x_init_block(bp, BLOCK_CCM, PHASE_COMMON);
- bnx2x_init_block(bp, TCM_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, UCM_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, CCM_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, XCM_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_XCM, PHASE_COMMON);
bnx2x_read_dmae(bp, XSEM_REG_PASSIVE_BUFFER, 3);
bnx2x_read_dmae(bp, CSEM_REG_PASSIVE_BUFFER, 3);
bnx2x_read_dmae(bp, TSEM_REG_PASSIVE_BUFFER, 3);
bnx2x_read_dmae(bp, USEM_REG_PASSIVE_BUFFER, 3);
- bnx2x_init_block(bp, QM_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_QM, PHASE_COMMON);
- if (CHIP_MODE_IS_4_PORT(bp))
- bnx2x_init_block(bp, QM_4PORT_BLOCK, COMMON_STAGE);
/* QM queues pointers table */
bnx2x_qm_init_ptr_table(bp, bp->qm_cid_count, INITOP_SET);
@@ -5155,57 +6089,51 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
REG_WR(bp, QM_REG_SOFT_RESET, 0);
#ifdef BCM_CNIC
- bnx2x_init_block(bp, TIMERS_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_TM, PHASE_COMMON);
#endif
- bnx2x_init_block(bp, DQ_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_DORQ, PHASE_COMMON);
REG_WR(bp, DORQ_REG_DPM_CID_OFST, BNX2X_DB_SHIFT);
-
- if (!CHIP_REV_IS_SLOW(bp)) {
+ if (!CHIP_REV_IS_SLOW(bp))
/* enable hw interrupt from doorbell Q */
REG_WR(bp, DORQ_REG_DORQ_INT_MASK, 0);
- }
- bnx2x_init_block(bp, BRB1_BLOCK, COMMON_STAGE);
- if (CHIP_MODE_IS_4_PORT(bp)) {
- REG_WR(bp, BRB1_REG_FULL_LB_XOFF_THRESHOLD, 248);
- REG_WR(bp, BRB1_REG_FULL_LB_XON_THRESHOLD, 328);
- }
+ bnx2x_init_block(bp, BLOCK_BRB1, PHASE_COMMON);
- bnx2x_init_block(bp, PRS_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_PRS, PHASE_COMMON);
REG_WR(bp, PRS_REG_A_PRSU_20, 0xf);
-#ifndef BCM_CNIC
- /* set NIC mode */
- REG_WR(bp, PRS_REG_NIC_MODE, 1);
-#endif
+
if (!CHIP_IS_E1(bp))
- REG_WR(bp, PRS_REG_E1HOV_MODE, IS_MF_SD(bp));
+ REG_WR(bp, PRS_REG_E1HOV_MODE, bp->path_has_ovlan);
- if (CHIP_IS_E2(bp)) {
- /* Bit-map indicating which L2 hdrs may appear after the
- basic Ethernet header */
- int has_ovlan = IS_MF_SD(bp);
- REG_WR(bp, PRS_REG_HDRS_AFTER_BASIC, (has_ovlan ? 7 : 6));
- REG_WR(bp, PRS_REG_MUST_HAVE_HDRS, (has_ovlan ? 1 : 0));
- }
+ if (!CHIP_IS_E1x(bp) && !CHIP_IS_E3B0(bp))
+ /* Bit-map indicating which L2 hdrs may appear
+ * after the basic Ethernet header
+ */
+ REG_WR(bp, PRS_REG_HDRS_AFTER_BASIC,
+ bp->path_has_ovlan ? 7 : 6);
- bnx2x_init_block(bp, TSDM_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, CSDM_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, USDM_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, XSDM_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_TSDM, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_CSDM, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_USDM, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_XSDM, PHASE_COMMON);
- bnx2x_init_fill(bp, TSEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(bp));
- bnx2x_init_fill(bp, USEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(bp));
- bnx2x_init_fill(bp, CSEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(bp));
- bnx2x_init_fill(bp, XSEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(bp));
+ if (!CHIP_IS_E1x(bp)) {
+ /* reset VFC memories */
+ REG_WR(bp, TSEM_REG_FAST_MEMORY + VFC_REG_MEMORIES_RST,
+ VFC_MEMORIES_RST_REG_CAM_RST |
+ VFC_MEMORIES_RST_REG_RAM_RST);
+ REG_WR(bp, XSEM_REG_FAST_MEMORY + VFC_REG_MEMORIES_RST,
+ VFC_MEMORIES_RST_REG_CAM_RST |
+ VFC_MEMORIES_RST_REG_RAM_RST);
- bnx2x_init_block(bp, TSEM_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, USEM_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, CSEM_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, XSEM_BLOCK, COMMON_STAGE);
+ msleep(20);
+ }
- if (CHIP_MODE_IS_4_PORT(bp))
- bnx2x_init_block(bp, XSEM_4PORT_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_TSEM, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_USEM, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_CSEM, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_XSEM, PHASE_COMMON);
/* sync semi rtc */
REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR,
@@ -5213,21 +6141,18 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET,
0x80000000);
- bnx2x_init_block(bp, UPB_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, XPB_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, PBF_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_UPB, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_XPB, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_PBF, PHASE_COMMON);
- if (CHIP_IS_E2(bp)) {
- int has_ovlan = IS_MF_SD(bp);
- REG_WR(bp, PBF_REG_HDRS_AFTER_BASIC, (has_ovlan ? 7 : 6));
- REG_WR(bp, PBF_REG_MUST_HAVE_HDRS, (has_ovlan ? 1 : 0));
- }
+ if (!CHIP_IS_E1x(bp))
+ REG_WR(bp, PBF_REG_HDRS_AFTER_BASIC,
+ bp->path_has_ovlan ? 7 : 6);
REG_WR(bp, SRC_REG_SOFT_RST, 1);
- for (i = SRC_REG_KEYRSS0_0; i <= SRC_REG_KEYRSS1_9; i += 4)
- REG_WR(bp, i, random32());
- bnx2x_init_block(bp, SRCH_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_SRC, PHASE_COMMON);
+
#ifdef BCM_CNIC
REG_WR(bp, SRC_REG_KEYSEARCH_0, 0x63285672);
REG_WR(bp, SRC_REG_KEYSEARCH_1, 0x24b8f2cc);
@@ -5248,11 +6173,11 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
"of cdu_context(%ld)\n",
(long)sizeof(union cdu_context));
- bnx2x_init_block(bp, CDU_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_CDU, PHASE_COMMON);
val = (4 << 24) + (0 << 12) + 1024;
REG_WR(bp, CDU_REG_CDU_GLOBAL_PARAMS, val);
- bnx2x_init_block(bp, CFC_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_CFC, PHASE_COMMON);
REG_WR(bp, CFC_REG_INIT_REG, 0x7FF);
/* enable context validation interrupt from CFC */
REG_WR(bp, CFC_REG_CFC_INT_MASK, 0);
@@ -5260,20 +6185,19 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
/* set the thresholds to prevent CFC/CDU race */
REG_WR(bp, CFC_REG_DEBUG0, 0x20020000);
- bnx2x_init_block(bp, HC_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_HC, PHASE_COMMON);
- if (CHIP_IS_E2(bp) && BP_NOMCP(bp))
+ if (!CHIP_IS_E1x(bp) && BP_NOMCP(bp))
REG_WR(bp, IGU_REG_RESET_MEMORIES, 0x36);
- bnx2x_init_block(bp, IGU_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, MISC_AEU_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_IGU, PHASE_COMMON);
+ bnx2x_init_block(bp, BLOCK_MISC_AEU, PHASE_COMMON);
- bnx2x_init_block(bp, PXPCS_BLOCK, COMMON_STAGE);
/* Reset PCIE errors for debug */
REG_WR(bp, 0x2814, 0xffffffff);
REG_WR(bp, 0x3820, 0xffffffff);
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
REG_WR(bp, PCICFG_OFFSET + PXPCS_TL_CONTROL_5,
(PXPCS_TL_CONTROL_5_ERR_UNSPPORT1 |
PXPCS_TL_CONTROL_5_ERR_UNSPPORT));
@@ -5287,21 +6211,15 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
PXPCS_TL_FUNC678_STAT_ERR_UNSPPORT5));
}
- bnx2x_init_block(bp, EMAC0_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, EMAC1_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, DBU_BLOCK, COMMON_STAGE);
- bnx2x_init_block(bp, DBG_BLOCK, COMMON_STAGE);
-
- bnx2x_init_block(bp, NIG_BLOCK, COMMON_STAGE);
+ bnx2x_init_block(bp, BLOCK_NIG, PHASE_COMMON);
if (!CHIP_IS_E1(bp)) {
- REG_WR(bp, NIG_REG_LLH_MF_MODE, IS_MF(bp));
- REG_WR(bp, NIG_REG_LLH_E1HOV_MODE, IS_MF_SD(bp));
- }
- if (CHIP_IS_E2(bp)) {
- /* Bit-map indicating which L2 hdrs may appear after the
- basic Ethernet header */
- REG_WR(bp, NIG_REG_P0_HDRS_AFTER_BASIC, (IS_MF_SD(bp) ? 7 : 6));
+ /* in E3 this done in per-port section */
+ if (!CHIP_IS_E3(bp))
+ REG_WR(bp, NIG_REG_LLH_MF_MODE, IS_MF(bp));
}
+ if (CHIP_IS_E1H(bp))
+ /* not applicable for E2 (and above ...) */
+ REG_WR(bp, NIG_REG_LLH_E1HOV_MODE, IS_MF_SD(bp));
if (CHIP_REV_IS_SLOW(bp))
msleep(200);
@@ -5343,127 +6261,136 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
REG_RD(bp, PXP2_REG_PXP2_INT_STS_CLR_0);
bnx2x_enable_blocks_attention(bp);
- if (CHIP_PARITY_ENABLED(bp))
- bnx2x_enable_blocks_parity(bp);
+ bnx2x_enable_blocks_parity(bp);
if (!BP_NOMCP(bp)) {
- /* In E2 2-PORT mode, same ext phy is used for the two paths */
- if ((load_code == FW_MSG_CODE_DRV_LOAD_COMMON_CHIP) ||
- CHIP_IS_E1x(bp)) {
- u32 shmem_base[2], shmem2_base[2];
- shmem_base[0] = bp->common.shmem_base;
- shmem2_base[0] = bp->common.shmem2_base;
- if (CHIP_IS_E2(bp)) {
- shmem_base[1] =
- SHMEM2_RD(bp, other_shmem_base_addr);
- shmem2_base[1] =
- SHMEM2_RD(bp, other_shmem2_base_addr);
- }
- bnx2x_acquire_phy_lock(bp);
- bnx2x_common_init_phy(bp, shmem_base, shmem2_base,
- bp->common.chip_id);
- bnx2x_release_phy_lock(bp);
- }
+ if (CHIP_IS_E1x(bp))
+ bnx2x__common_init_phy(bp);
} else
BNX2X_ERR("Bootcode is missing - can not initialize link\n");
return 0;
}
+/**
+ * bnx2x_init_hw_common_chip - init HW at the COMMON_CHIP phase.
+ *
+ * @bp: driver handle
+ */
+static int bnx2x_init_hw_common_chip(struct bnx2x *bp)
+{
+ int rc = bnx2x_init_hw_common(bp);
+
+ if (rc)
+ return rc;
+
+ /* In E2 2-PORT mode, same ext phy is used for the two paths */
+ if (!BP_NOMCP(bp))
+ bnx2x__common_init_phy(bp);
+
+ return 0;
+}
+
static int bnx2x_init_hw_port(struct bnx2x *bp)
{
int port = BP_PORT(bp);
- int init_stage = port ? PORT1_STAGE : PORT0_STAGE;
+ int init_phase = port ? PHASE_PORT1 : PHASE_PORT0;
u32 low, high;
u32 val;
+ bnx2x__link_reset(bp);
+
DP(BNX2X_MSG_MCP, "starting port init port %d\n", port);
REG_WR(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, 0);
- bnx2x_init_block(bp, PXP_BLOCK, init_stage);
- bnx2x_init_block(bp, PXP2_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_MISC, init_phase);
+ bnx2x_init_block(bp, BLOCK_PXP, init_phase);
+ bnx2x_init_block(bp, BLOCK_PXP2, init_phase);
/* Timers bug workaround: disables the pf_master bit in pglue at
* common phase, we need to enable it here before any dmae access are
* attempted. Therefore we manually added the enable-master to the
* port phase (it also happens in the function phase)
*/
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
REG_WR(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER, 1);
- bnx2x_init_block(bp, TCM_BLOCK, init_stage);
- bnx2x_init_block(bp, UCM_BLOCK, init_stage);
- bnx2x_init_block(bp, CCM_BLOCK, init_stage);
- bnx2x_init_block(bp, XCM_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_ATC, init_phase);
+ bnx2x_init_block(bp, BLOCK_DMAE, init_phase);
+ bnx2x_init_block(bp, BLOCK_PGLUE_B, init_phase);
+ bnx2x_init_block(bp, BLOCK_QM, init_phase);
+
+ bnx2x_init_block(bp, BLOCK_TCM, init_phase);
+ bnx2x_init_block(bp, BLOCK_UCM, init_phase);
+ bnx2x_init_block(bp, BLOCK_CCM, init_phase);
+ bnx2x_init_block(bp, BLOCK_XCM, init_phase);
/* QM cid (connection) count */
bnx2x_qm_init_cid_count(bp, bp->qm_cid_count, INITOP_SET);
#ifdef BCM_CNIC
- bnx2x_init_block(bp, TIMERS_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_TM, init_phase);
REG_WR(bp, TM_REG_LIN0_SCAN_TIME + port*4, 20);
REG_WR(bp, TM_REG_LIN0_MAX_ACTIVE_CID + port*4, 31);
#endif
- bnx2x_init_block(bp, DQ_BLOCK, init_stage);
-
- if (CHIP_MODE_IS_4_PORT(bp))
- bnx2x_init_block(bp, QM_4PORT_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_DORQ, init_phase);
if (CHIP_IS_E1(bp) || CHIP_IS_E1H(bp)) {
- bnx2x_init_block(bp, BRB1_BLOCK, init_stage);
- if (CHIP_REV_IS_SLOW(bp) && CHIP_IS_E1(bp)) {
- /* no pause for emulation and FPGA */
- low = 0;
- high = 513;
- } else {
- if (IS_MF(bp))
- low = ((bp->flags & ONE_PORT_FLAG) ? 160 : 246);
- else if (bp->dev->mtu > 4096) {
- if (bp->flags & ONE_PORT_FLAG)
- low = 160;
- else {
- val = bp->dev->mtu;
- /* (24*1024 + val*4)/256 */
- low = 96 + (val/64) +
- ((val % 64) ? 1 : 0);
- }
- } else
- low = ((bp->flags & ONE_PORT_FLAG) ? 80 : 160);
- high = low + 56; /* 14*1024/256 */
- }
+ bnx2x_init_block(bp, BLOCK_BRB1, init_phase);
+
+ if (IS_MF(bp))
+ low = ((bp->flags & ONE_PORT_FLAG) ? 160 : 246);
+ else if (bp->dev->mtu > 4096) {
+ if (bp->flags & ONE_PORT_FLAG)
+ low = 160;
+ else {
+ val = bp->dev->mtu;
+ /* (24*1024 + val*4)/256 */
+ low = 96 + (val/64) +
+ ((val % 64) ? 1 : 0);
+ }
+ } else
+ low = ((bp->flags & ONE_PORT_FLAG) ? 80 : 160);
+ high = low + 56; /* 14*1024/256 */
REG_WR(bp, BRB1_REG_PAUSE_LOW_THRESHOLD_0 + port*4, low);
REG_WR(bp, BRB1_REG_PAUSE_HIGH_THRESHOLD_0 + port*4, high);
}
- if (CHIP_MODE_IS_4_PORT(bp)) {
- REG_WR(bp, BRB1_REG_PAUSE_0_XOFF_THRESHOLD_0 + port*8, 248);
- REG_WR(bp, BRB1_REG_PAUSE_0_XON_THRESHOLD_0 + port*8, 328);
- REG_WR(bp, (BP_PORT(bp) ? BRB1_REG_MAC_GUARANTIED_1 :
- BRB1_REG_MAC_GUARANTIED_0), 40);
- }
+ if (CHIP_MODE_IS_4_PORT(bp))
+ REG_WR(bp, (BP_PORT(bp) ?
+ BRB1_REG_MAC_GUARANTIED_1 :
+ BRB1_REG_MAC_GUARANTIED_0), 40);
- bnx2x_init_block(bp, PRS_BLOCK, init_stage);
- bnx2x_init_block(bp, TSDM_BLOCK, init_stage);
- bnx2x_init_block(bp, CSDM_BLOCK, init_stage);
- bnx2x_init_block(bp, USDM_BLOCK, init_stage);
- bnx2x_init_block(bp, XSDM_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_PRS, init_phase);
+ if (CHIP_IS_E3B0(bp))
+ /* Ovlan exists only if we are in multi-function +
+ * switch-dependent mode, in switch-independent there
+ * is no ovlan headers
+ */
+ REG_WR(bp, BP_PORT(bp) ?
+ PRS_REG_HDRS_AFTER_BASIC_PORT_1 :
+ PRS_REG_HDRS_AFTER_BASIC_PORT_0,
+ (bp->path_has_ovlan ? 7 : 6));
- bnx2x_init_block(bp, TSEM_BLOCK, init_stage);
- bnx2x_init_block(bp, USEM_BLOCK, init_stage);
- bnx2x_init_block(bp, CSEM_BLOCK, init_stage);
- bnx2x_init_block(bp, XSEM_BLOCK, init_stage);
- if (CHIP_MODE_IS_4_PORT(bp))
- bnx2x_init_block(bp, XSEM_4PORT_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_TSDM, init_phase);
+ bnx2x_init_block(bp, BLOCK_CSDM, init_phase);
+ bnx2x_init_block(bp, BLOCK_USDM, init_phase);
+ bnx2x_init_block(bp, BLOCK_XSDM, init_phase);
- bnx2x_init_block(bp, UPB_BLOCK, init_stage);
- bnx2x_init_block(bp, XPB_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_TSEM, init_phase);
+ bnx2x_init_block(bp, BLOCK_USEM, init_phase);
+ bnx2x_init_block(bp, BLOCK_CSEM, init_phase);
+ bnx2x_init_block(bp, BLOCK_XSEM, init_phase);
- bnx2x_init_block(bp, PBF_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_UPB, init_phase);
+ bnx2x_init_block(bp, BLOCK_XPB, init_phase);
- if (!CHIP_IS_E2(bp)) {
+ bnx2x_init_block(bp, BLOCK_PBF, init_phase);
+
+ if (CHIP_IS_E1x(bp)) {
/* configure PBF to work without PAUSE mtu 9000 */
REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 0);
@@ -5479,20 +6406,20 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
}
#ifdef BCM_CNIC
- bnx2x_init_block(bp, SRCH_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_SRC, init_phase);
#endif
- bnx2x_init_block(bp, CDU_BLOCK, init_stage);
- bnx2x_init_block(bp, CFC_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_CDU, init_phase);
+ bnx2x_init_block(bp, BLOCK_CFC, init_phase);
if (CHIP_IS_E1(bp)) {
REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, 0);
REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, 0);
}
- bnx2x_init_block(bp, HC_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_HC, init_phase);
- bnx2x_init_block(bp, IGU_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_IGU, init_phase);
- bnx2x_init_block(bp, MISC_AEU_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_MISC_AEU, init_phase);
/* init aeu_mask_attn_func_0/1:
* - SF mode: bits 3-7 are masked. only bits 0-2 are in use
* - MF mode: bit 3 is masked. bits 0-2 are in use as in SF
@@ -5502,22 +6429,31 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
val |= CHIP_IS_E1(bp) ? 0 : 0x10;
REG_WR(bp, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port*4, val);
- bnx2x_init_block(bp, PXPCS_BLOCK, init_stage);
- bnx2x_init_block(bp, EMAC0_BLOCK, init_stage);
- bnx2x_init_block(bp, EMAC1_BLOCK, init_stage);
- bnx2x_init_block(bp, DBU_BLOCK, init_stage);
- bnx2x_init_block(bp, DBG_BLOCK, init_stage);
+ bnx2x_init_block(bp, BLOCK_NIG, init_phase);
- bnx2x_init_block(bp, NIG_BLOCK, init_stage);
+ if (!CHIP_IS_E1x(bp)) {
+ /* Bit-map indicating which L2 hdrs may appear after the
+ * basic Ethernet header
+ */
+ REG_WR(bp, BP_PORT(bp) ?
+ NIG_REG_P1_HDRS_AFTER_BASIC :
+ NIG_REG_P0_HDRS_AFTER_BASIC,
+ IS_MF_SD(bp) ? 7 : 6);
- REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 1);
+ if (CHIP_IS_E3(bp))
+ REG_WR(bp, BP_PORT(bp) ?
+ NIG_REG_LLH1_MF_MODE :
+ NIG_REG_LLH_MF_MODE, IS_MF(bp));
+ }
+ if (!CHIP_IS_E3(bp))
+ REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 1);
if (!CHIP_IS_E1(bp)) {
/* 0x2 disable mf_ov, 0x1 enable */
REG_WR(bp, NIG_REG_LLH0_BRB1_DRV_MASK_MF + port*4,
(IS_MF_SD(bp) ? 0x1 : 0x2));
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
val = 0;
switch (bp->mf_mode) {
case MULTI_FUNCTION_SD:
@@ -5538,17 +6474,16 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
}
}
- bnx2x_init_block(bp, MCP_BLOCK, init_stage);
- bnx2x_init_block(bp, DMAE_BLOCK, init_stage);
- if (bnx2x_fan_failure_det_req(bp, bp->common.shmem_base,
- bp->common.shmem2_base, port)) {
+
+ /* If SPIO5 is set to generate interrupts, enable it for this port */
+ val = REG_RD(bp, MISC_REG_SPIO_EVENT_EN);
+ if (val & (1 << MISC_REGISTERS_SPIO_5)) {
u32 reg_addr = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 :
MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0);
val = REG_RD(bp, reg_addr);
val |= AEU_INPUTS_ATTN_BITS_SPIO5;
REG_WR(bp, reg_addr, val);
}
- bnx2x__link_reset(bp);
return 0;
}
@@ -5567,7 +6502,7 @@ static void bnx2x_ilt_wr(struct bnx2x *bp, u32 index, dma_addr_t addr)
static inline void bnx2x_igu_clear_sb(struct bnx2x *bp, u8 idu_sb_id)
{
- bnx2x_igu_clear_sb_gen(bp, idu_sb_id, true /*PF*/);
+ bnx2x_igu_clear_sb_gen(bp, BP_FUNC(bp), idu_sb_id, true /*PF*/);
}
static inline void bnx2x_clear_func_ilt(struct bnx2x *bp, u32 func)
@@ -5581,6 +6516,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
{
int port = BP_PORT(bp);
int func = BP_FUNC(bp);
+ int init_phase = PHASE_PF0 + func;
struct bnx2x_ilt *ilt = BP_ILT(bp);
u16 cdu_ilt_start;
u32 addr, val;
@@ -5589,6 +6525,10 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
DP(BNX2X_MSG_MCP, "starting func init func %d\n", func);
+ /* FLR cleanup - hmmm */
+ if (!CHIP_IS_E1x(bp))
+ bnx2x_pf_flr_clnup(bp);
+
/* set MSI reconfigure capability */
if (bp->common.int_block == INT_BLOCK_HC) {
addr = (port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0);
@@ -5597,6 +6537,9 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
REG_WR(bp, addr, val);
}
+ bnx2x_init_block(bp, BLOCK_PXP, init_phase);
+ bnx2x_init_block(bp, BLOCK_PXP2, init_phase);
+
ilt = BP_ILT(bp);
cdu_ilt_start = ilt->clients[ILT_CLIENT_CDU].start;
@@ -5622,7 +6565,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
REG_WR(bp, PRS_REG_NIC_MODE, 1);
#endif /* BCM_CNIC */
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
u32 pf_conf = IGU_PF_CONF_FUNC_EN;
/* Turn on a single ISR mode in IGU if driver is going to use
@@ -5649,58 +6592,55 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
bp->dmae_ready = 1;
- bnx2x_init_block(bp, PGLUE_B_BLOCK, FUNC0_STAGE + func);
+ bnx2x_init_block(bp, BLOCK_PGLUE_B, init_phase);
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, func);
- bnx2x_init_block(bp, MISC_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, TCM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, UCM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, CCM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, XCM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, TSEM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, USEM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, CSEM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, XSEM_BLOCK, FUNC0_STAGE + func);
-
- if (CHIP_IS_E2(bp)) {
- REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_PATH_ID_OFFSET,
- BP_PATH(bp));
- REG_WR(bp, BAR_CSTRORM_INTMEM + CSTORM_PATH_ID_OFFSET,
- BP_PATH(bp));
- }
-
- if (CHIP_MODE_IS_4_PORT(bp))
- bnx2x_init_block(bp, XSEM_4PORT_BLOCK, FUNC0_STAGE + func);
-
- if (CHIP_IS_E2(bp))
+ bnx2x_init_block(bp, BLOCK_ATC, init_phase);
+ bnx2x_init_block(bp, BLOCK_DMAE, init_phase);
+ bnx2x_init_block(bp, BLOCK_NIG, init_phase);
+ bnx2x_init_block(bp, BLOCK_SRC, init_phase);
+ bnx2x_init_block(bp, BLOCK_MISC, init_phase);
+ bnx2x_init_block(bp, BLOCK_TCM, init_phase);
+ bnx2x_init_block(bp, BLOCK_UCM, init_phase);
+ bnx2x_init_block(bp, BLOCK_CCM, init_phase);
+ bnx2x_init_block(bp, BLOCK_XCM, init_phase);
+ bnx2x_init_block(bp, BLOCK_TSEM, init_phase);
+ bnx2x_init_block(bp, BLOCK_USEM, init_phase);
+ bnx2x_init_block(bp, BLOCK_CSEM, init_phase);
+ bnx2x_init_block(bp, BLOCK_XSEM, init_phase);
+
+ if (!CHIP_IS_E1x(bp))
REG_WR(bp, QM_REG_PF_EN, 1);
- bnx2x_init_block(bp, QM_BLOCK, FUNC0_STAGE + func);
-
- if (CHIP_MODE_IS_4_PORT(bp))
- bnx2x_init_block(bp, QM_4PORT_BLOCK, FUNC0_STAGE + func);
-
- bnx2x_init_block(bp, TIMERS_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, DQ_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, BRB1_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, PRS_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, TSDM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, CSDM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, USDM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, XSDM_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, UPB_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, XPB_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, PBF_BLOCK, FUNC0_STAGE + func);
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp)) {
+ REG_WR(bp, TSEM_REG_VFPF_ERR_NUM, BNX2X_MAX_NUM_OF_VFS + func);
+ REG_WR(bp, USEM_REG_VFPF_ERR_NUM, BNX2X_MAX_NUM_OF_VFS + func);
+ REG_WR(bp, CSEM_REG_VFPF_ERR_NUM, BNX2X_MAX_NUM_OF_VFS + func);
+ REG_WR(bp, XSEM_REG_VFPF_ERR_NUM, BNX2X_MAX_NUM_OF_VFS + func);
+ }
+ bnx2x_init_block(bp, BLOCK_QM, init_phase);
+
+ bnx2x_init_block(bp, BLOCK_TM, init_phase);
+ bnx2x_init_block(bp, BLOCK_DORQ, init_phase);
+ bnx2x_init_block(bp, BLOCK_BRB1, init_phase);
+ bnx2x_init_block(bp, BLOCK_PRS, init_phase);
+ bnx2x_init_block(bp, BLOCK_TSDM, init_phase);
+ bnx2x_init_block(bp, BLOCK_CSDM, init_phase);
+ bnx2x_init_block(bp, BLOCK_USDM, init_phase);
+ bnx2x_init_block(bp, BLOCK_XSDM, init_phase);
+ bnx2x_init_block(bp, BLOCK_UPB, init_phase);
+ bnx2x_init_block(bp, BLOCK_XPB, init_phase);
+ bnx2x_init_block(bp, BLOCK_PBF, init_phase);
+ if (!CHIP_IS_E1x(bp))
REG_WR(bp, PBF_REG_DISABLE_PF, 0);
- bnx2x_init_block(bp, CDU_BLOCK, FUNC0_STAGE + func);
+ bnx2x_init_block(bp, BLOCK_CDU, init_phase);
- bnx2x_init_block(bp, CFC_BLOCK, FUNC0_STAGE + func);
+ bnx2x_init_block(bp, BLOCK_CFC, init_phase);
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
REG_WR(bp, CFC_REG_WEAK_ENABLE_PF, 1);
if (IS_MF(bp)) {
@@ -5708,7 +6648,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
REG_WR(bp, NIG_REG_LLH0_FUNC_VLAN_ID + port*8, bp->mf_ov);
}
- bnx2x_init_block(bp, MISC_AEU_BLOCK, FUNC0_STAGE + func);
+ bnx2x_init_block(bp, BLOCK_MISC_AEU, init_phase);
/* HC init per function */
if (bp->common.int_block == INT_BLOCK_HC) {
@@ -5718,21 +6658,21 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, 0);
REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, 0);
}
- bnx2x_init_block(bp, HC_BLOCK, FUNC0_STAGE + func);
+ bnx2x_init_block(bp, BLOCK_HC, init_phase);
} else {
int num_segs, sb_idx, prod_offset;
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_12 + func*4, 0);
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
REG_WR(bp, IGU_REG_LEADING_EDGE_LATCH, 0);
REG_WR(bp, IGU_REG_TRAILING_EDGE_LATCH, 0);
}
- bnx2x_init_block(bp, IGU_BLOCK, FUNC0_STAGE + func);
+ bnx2x_init_block(bp, BLOCK_IGU, init_phase);
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
int dsb_idx = 0;
/**
* Producer memory:
@@ -5780,12 +6720,16 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
if (CHIP_MODE_IS_4_PORT(bp))
dsb_idx = BP_FUNC(bp);
else
- dsb_idx = BP_E1HVN(bp);
+ dsb_idx = BP_VN(bp);
prod_offset = (CHIP_INT_MODE_IS_BC(bp) ?
IGU_BC_BASE_DSB_PROD + dsb_idx :
IGU_NORM_BASE_DSB_PROD + dsb_idx);
+ /*
+ * igu prods come in chunks of E1HVN_MAX (4) -
+ * does not matters what is the current chip mode
+ */
for (i = 0; i < (num_segs * E1HVN_MAX);
i += E1HVN_MAX) {
addr = IGU_REG_PROD_CONS_MEMORY +
@@ -5827,13 +6771,6 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
REG_WR(bp, 0x2114, 0xffffffff);
REG_WR(bp, 0x2120, 0xffffffff);
- bnx2x_init_block(bp, EMAC0_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, EMAC1_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, DBU_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, DBG_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, MCP_BLOCK, FUNC0_STAGE + func);
- bnx2x_init_block(bp, DMAE_BLOCK, FUNC0_STAGE + func);
-
if (CHIP_IS_E1x(bp)) {
main_mem_size = HC_REG_MAIN_MEMORY_SIZE / 2; /*dwords*/
main_mem_base = HC_REG_MAIN_MEMORY +
@@ -5859,65 +6796,26 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
REG_RD(bp, main_mem_prty_clr);
}
+#ifdef BNX2X_STOP_ON_ERROR
+ /* Enable STORMs SP logging */
+ REG_WR8(bp, BAR_USTRORM_INTMEM +
+ USTORM_RECORD_SLOW_PATH_OFFSET(BP_FUNC(bp)), 1);
+ REG_WR8(bp, BAR_TSTRORM_INTMEM +
+ TSTORM_RECORD_SLOW_PATH_OFFSET(BP_FUNC(bp)), 1);
+ REG_WR8(bp, BAR_CSTRORM_INTMEM +
+ CSTORM_RECORD_SLOW_PATH_OFFSET(BP_FUNC(bp)), 1);
+ REG_WR8(bp, BAR_XSTRORM_INTMEM +
+ XSTORM_RECORD_SLOW_PATH_OFFSET(BP_FUNC(bp)), 1);
+#endif
+
bnx2x_phy_probe(&bp->link_params);
return 0;
}
-int bnx2x_init_hw(struct bnx2x *bp, u32 load_code)
-{
- int rc = 0;
-
- DP(BNX2X_MSG_MCP, "function %d load_code %x\n",
- BP_ABS_FUNC(bp), load_code);
-
- bp->dmae_ready = 0;
- spin_lock_init(&bp->dmae_lock);
-
- switch (load_code) {
- case FW_MSG_CODE_DRV_LOAD_COMMON:
- case FW_MSG_CODE_DRV_LOAD_COMMON_CHIP:
- rc = bnx2x_init_hw_common(bp, load_code);
- if (rc)
- goto init_hw_err;
- /* no break */
-
- case FW_MSG_CODE_DRV_LOAD_PORT:
- rc = bnx2x_init_hw_port(bp);
- if (rc)
- goto init_hw_err;
- /* no break */
-
- case FW_MSG_CODE_DRV_LOAD_FUNCTION:
- rc = bnx2x_init_hw_func(bp);
- if (rc)
- goto init_hw_err;
- break;
-
- default:
- BNX2X_ERR("Unknown load_code (0x%x) from MCP\n", load_code);
- break;
- }
-
- if (!BP_NOMCP(bp)) {
- int mb_idx = BP_FW_MB_IDX(bp);
-
- bp->fw_drv_pulse_wr_seq =
- (SHMEM_RD(bp, func_mb[mb_idx].drv_pulse_mb) &
- DRV_PULSE_SEQ_MASK);
- DP(BNX2X_MSG_MCP, "drv_pulse 0x%x\n", bp->fw_drv_pulse_wr_seq);
- }
-
-init_hw_err:
- bnx2x_gunzip_end(bp);
-
- return rc;
-}
void bnx2x_free_mem(struct bnx2x *bp)
{
- bnx2x_gunzip_end(bp);
-
/* fastpath */
bnx2x_free_fp_mem(bp);
/* end of fastpath */
@@ -5925,6 +6823,9 @@ void bnx2x_free_mem(struct bnx2x *bp)
BNX2X_PCI_FREE(bp->def_status_blk, bp->def_status_blk_mapping,
sizeof(struct host_sp_status_block));
+ BNX2X_PCI_FREE(bp->fw_stats, bp->fw_stats_mapping,
+ bp->fw_stats_data_sz + bp->fw_stats_req_sz);
+
BNX2X_PCI_FREE(bp->slowpath, bp->slowpath_mapping,
sizeof(struct bnx2x_slowpath));
@@ -5936,7 +6837,7 @@ void bnx2x_free_mem(struct bnx2x *bp)
BNX2X_FREE(bp->ilt->lines);
#ifdef BCM_CNIC
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
BNX2X_PCI_FREE(bp->cnic_sb.e2_sb, bp->cnic_sb_mapping,
sizeof(struct host_hc_status_block_e2));
else
@@ -5950,18 +6851,67 @@ void bnx2x_free_mem(struct bnx2x *bp)
BNX2X_PCI_FREE(bp->eq_ring, bp->eq_mapping,
BCM_PAGE_SIZE * NUM_EQ_PAGES);
+}
+
+static inline int bnx2x_alloc_fw_stats_mem(struct bnx2x *bp)
+{
+ int num_groups;
+
+ /* number of eth_queues */
+ u8 num_queue_stats = BNX2X_NUM_ETH_QUEUES(bp);
+
+ /* Total number of FW statistics requests =
+ * 1 for port stats + 1 for PF stats + num_eth_queues */
+ bp->fw_stats_num = 2 + num_queue_stats;
- BNX2X_FREE(bp->rx_indir_table);
+
+ /* Request is built from stats_query_header and an array of
+ * stats_query_cmd_group each of which contains
+ * STATS_QUERY_CMD_COUNT rules. The real number or requests is
+ * configured in the stats_query_header.
+ */
+ num_groups = (2 + num_queue_stats) / STATS_QUERY_CMD_COUNT +
+ (((2 + num_queue_stats) % STATS_QUERY_CMD_COUNT) ? 1 : 0);
+
+ bp->fw_stats_req_sz = sizeof(struct stats_query_header) +
+ num_groups * sizeof(struct stats_query_cmd_group);
+
+ /* Data for statistics requests + stats_conter
+ *
+ * stats_counter holds per-STORM counters that are incremented
+ * when STORM has finished with the current request.
+ */
+ bp->fw_stats_data_sz = sizeof(struct per_port_stats) +
+ sizeof(struct per_pf_stats) +
+ sizeof(struct per_queue_stats) * num_queue_stats +
+ sizeof(struct stats_counter);
+
+ BNX2X_PCI_ALLOC(bp->fw_stats, &bp->fw_stats_mapping,
+ bp->fw_stats_data_sz + bp->fw_stats_req_sz);
+
+ /* Set shortcuts */
+ bp->fw_stats_req = (struct bnx2x_fw_stats_req *)bp->fw_stats;
+ bp->fw_stats_req_mapping = bp->fw_stats_mapping;
+
+ bp->fw_stats_data = (struct bnx2x_fw_stats_data *)
+ ((u8 *)bp->fw_stats + bp->fw_stats_req_sz);
+
+ bp->fw_stats_data_mapping = bp->fw_stats_mapping +
+ bp->fw_stats_req_sz;
+ return 0;
+
+alloc_mem_err:
+ BNX2X_PCI_FREE(bp->fw_stats, bp->fw_stats_mapping,
+ bp->fw_stats_data_sz + bp->fw_stats_req_sz);
+ return -ENOMEM;
}
int bnx2x_alloc_mem(struct bnx2x *bp)
{
- if (bnx2x_gunzip_init(bp))
- return -ENOMEM;
-
#ifdef BCM_CNIC
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
+ /* size = the status block + ramrod buffers */
BNX2X_PCI_ALLOC(bp->cnic_sb.e2_sb, &bp->cnic_sb_mapping,
sizeof(struct host_hc_status_block_e2));
else
@@ -5979,7 +6929,11 @@ int bnx2x_alloc_mem(struct bnx2x *bp)
BNX2X_PCI_ALLOC(bp->slowpath, &bp->slowpath_mapping,
sizeof(struct bnx2x_slowpath));
- bp->context.size = sizeof(union cdu_context) * bp->l2_cid_count;
+ /* Allocated memory for FW statistics */
+ if (bnx2x_alloc_fw_stats_mem(bp))
+ goto alloc_mem_err;
+
+ bp->context.size = sizeof(union cdu_context) * BNX2X_L2_CID_COUNT(bp);
BNX2X_PCI_ALLOC(bp->context.vcxt, &bp->context.cxt_mapping,
bp->context.size);
@@ -5996,8 +6950,6 @@ int bnx2x_alloc_mem(struct bnx2x *bp)
BNX2X_PCI_ALLOC(bp->eq_ring, &bp->eq_mapping,
BCM_PAGE_SIZE * NUM_EQ_PAGES);
- BNX2X_ALLOC(bp->rx_indir_table, sizeof(bp->rx_indir_table[0]) *
- TSTORM_INDIRECTION_TABLE_SIZE);
/* fastpath */
/* need to be done at the end, since it's self adjusting to amount
@@ -6015,629 +6967,75 @@ alloc_mem_err:
/*
* Init service functions
*/
-static int bnx2x_wait_ramrod(struct bnx2x *bp, int state, int idx,
- int *state_p, int flags);
-
-int bnx2x_func_start(struct bnx2x *bp)
-{
- bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_FUNCTION_START, 0, 0, 0, 1);
-
- /* Wait for completion */
- return bnx2x_wait_ramrod(bp, BNX2X_STATE_FUNC_STARTED, 0, &(bp->state),
- WAIT_RAMROD_COMMON);
-}
-
-static int bnx2x_func_stop(struct bnx2x *bp)
-{
- bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_FUNCTION_STOP, 0, 0, 0, 1);
-
- /* Wait for completion */
- return bnx2x_wait_ramrod(bp, BNX2X_STATE_CLOSING_WAIT4_UNLOAD,
- 0, &(bp->state), WAIT_RAMROD_COMMON);
-}
-
-/**
- * bnx2x_set_mac_addr_gen - set a MAC in a CAM for a few L2 Clients for E1x chips
- *
- * @bp: driver handle
- * @set: set or clear an entry (1 or 0)
- * @mac: pointer to a buffer containing a MAC
- * @cl_bit_vec: bit vector of clients to register a MAC for
- * @cam_offset: offset in a CAM to use
- * @is_bcast: is the set MAC a broadcast address (for E1 only)
- */
-static void bnx2x_set_mac_addr_gen(struct bnx2x *bp, int set, const u8 *mac,
- u32 cl_bit_vec, u8 cam_offset,
- u8 is_bcast)
-{
- struct mac_configuration_cmd *config =
- (struct mac_configuration_cmd *)bnx2x_sp(bp, mac_config);
- int ramrod_flags = WAIT_RAMROD_COMMON;
-
- bp->set_mac_pending = 1;
-
- config->hdr.length = 1;
- config->hdr.offset = cam_offset;
- config->hdr.client_id = 0xff;
- /* Mark the single MAC configuration ramrod as opposed to a
- * UC/MC list configuration).
- */
- config->hdr.echo = 1;
-
- /* primary MAC */
- config->config_table[0].msb_mac_addr =
- swab16(*(u16 *)&mac[0]);
- config->config_table[0].middle_mac_addr =
- swab16(*(u16 *)&mac[2]);
- config->config_table[0].lsb_mac_addr =
- swab16(*(u16 *)&mac[4]);
- config->config_table[0].clients_bit_vector =
- cpu_to_le32(cl_bit_vec);
- config->config_table[0].vlan_id = 0;
- config->config_table[0].pf_id = BP_FUNC(bp);
- if (set)
- SET_FLAG(config->config_table[0].flags,
- MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
- T_ETH_MAC_COMMAND_SET);
- else
- SET_FLAG(config->config_table[0].flags,
- MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
- T_ETH_MAC_COMMAND_INVALIDATE);
-
- if (is_bcast)
- SET_FLAG(config->config_table[0].flags,
- MAC_CONFIGURATION_ENTRY_BROADCAST, 1);
-
- DP(NETIF_MSG_IFUP, "%s MAC (%04x:%04x:%04x) PF_ID %d CLID mask %d\n",
- (set ? "setting" : "clearing"),
- config->config_table[0].msb_mac_addr,
- config->config_table[0].middle_mac_addr,
- config->config_table[0].lsb_mac_addr, BP_FUNC(bp), cl_bit_vec);
-
- mb();
-
- bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_SET_MAC, 0,
- U64_HI(bnx2x_sp_mapping(bp, mac_config)),
- U64_LO(bnx2x_sp_mapping(bp, mac_config)), 1);
-
- /* Wait for a completion */
- bnx2x_wait_ramrod(bp, 0, 0, &bp->set_mac_pending, ramrod_flags);
-}
-
-static int bnx2x_wait_ramrod(struct bnx2x *bp, int state, int idx,
- int *state_p, int flags)
-{
- /* can take a while if any port is running */
- int cnt = 5000;
- u8 poll = flags & WAIT_RAMROD_POLL;
- u8 common = flags & WAIT_RAMROD_COMMON;
-
- DP(NETIF_MSG_IFUP, "%s for state to become %x on IDX [%d]\n",
- poll ? "polling" : "waiting", state, idx);
-
- might_sleep();
- while (cnt--) {
- if (poll) {
- if (common)
- bnx2x_eq_int(bp);
- else {
- bnx2x_rx_int(bp->fp, 10);
- /* if index is different from 0
- * the reply for some commands will
- * be on the non default queue
- */
- if (idx)
- bnx2x_rx_int(&bp->fp[idx], 10);
- }
- }
-
- mb(); /* state is changed by bnx2x_sp_event() */
- if (*state_p == state) {
-#ifdef BNX2X_STOP_ON_ERROR
- DP(NETIF_MSG_IFUP, "exit (cnt %d)\n", 5000 - cnt);
-#endif
- return 0;
- }
-
- msleep(1);
-
- if (bp->panic)
- return -EIO;
- }
-
- /* timeout! */
- BNX2X_ERR("timeout %s for state %x on IDX [%d]\n",
- poll ? "polling" : "waiting", state, idx);
-#ifdef BNX2X_STOP_ON_ERROR
- bnx2x_panic();
-#endif
-
- return -EBUSY;
-}
-
-static u8 bnx2x_e1h_cam_offset(struct bnx2x *bp, u8 rel_offset)
-{
- if (CHIP_IS_E1H(bp))
- return E1H_FUNC_MAX * rel_offset + BP_FUNC(bp);
- else if (CHIP_MODE_IS_4_PORT(bp))
- return E2_FUNC_MAX * rel_offset + BP_FUNC(bp);
- else
- return E2_FUNC_MAX * rel_offset + BP_VN(bp);
-}
-
-/**
- * LLH CAM line allocations: currently only iSCSI and ETH macs are
- * relevant. In addition, current implementation is tuned for a
- * single ETH MAC.
- */
-enum {
- LLH_CAM_ISCSI_ETH_LINE = 0,
- LLH_CAM_ETH_LINE,
- LLH_CAM_MAX_PF_LINE = NIG_REG_LLH1_FUNC_MEM_SIZE
-};
-
-static void bnx2x_set_mac_in_nig(struct bnx2x *bp,
- int set,
- unsigned char *dev_addr,
- int index)
-{
- u32 wb_data[2];
- u32 mem_offset, ena_offset, mem_index;
- /**
- * indexes mapping:
- * 0..7 - goes to MEM
- * 8..15 - goes to MEM2
- */
-
- if (!IS_MF_SI(bp) || index > LLH_CAM_MAX_PF_LINE)
- return;
-
- /* calculate memory start offset according to the mapping
- * and index in the memory */
- if (index < NIG_LLH_FUNC_MEM_MAX_OFFSET) {
- mem_offset = BP_PORT(bp) ? NIG_REG_LLH1_FUNC_MEM :
- NIG_REG_LLH0_FUNC_MEM;
- ena_offset = BP_PORT(bp) ? NIG_REG_LLH1_FUNC_MEM_ENABLE :
- NIG_REG_LLH0_FUNC_MEM_ENABLE;
- mem_index = index;
- } else {
- mem_offset = BP_PORT(bp) ? NIG_REG_P1_LLH_FUNC_MEM2 :
- NIG_REG_P0_LLH_FUNC_MEM2;
- ena_offset = BP_PORT(bp) ? NIG_REG_P1_LLH_FUNC_MEM2_ENABLE :
- NIG_REG_P0_LLH_FUNC_MEM2_ENABLE;
- mem_index = index - NIG_LLH_FUNC_MEM_MAX_OFFSET;
- }
-
- if (set) {
- /* LLH_FUNC_MEM is a u64 WB register */
- mem_offset += 8*mem_index;
-
- wb_data[0] = ((dev_addr[2] << 24) | (dev_addr[3] << 16) |
- (dev_addr[4] << 8) | dev_addr[5]);
- wb_data[1] = ((dev_addr[0] << 8) | dev_addr[1]);
-
- REG_WR_DMAE(bp, mem_offset, wb_data, 2);
- }
-
- /* enable/disable the entry */
- REG_WR(bp, ena_offset + 4*mem_index, set);
-
-}
-void bnx2x_set_eth_mac(struct bnx2x *bp, int set)
+int bnx2x_set_mac_one(struct bnx2x *bp, u8 *mac,
+ struct bnx2x_vlan_mac_obj *obj, bool set,
+ int mac_type, unsigned long *ramrod_flags)
{
- u8 cam_offset = (CHIP_IS_E1(bp) ? (BP_PORT(bp) ? 32 : 0) :
- bnx2x_e1h_cam_offset(bp, CAM_ETH_LINE));
+ int rc;
+ struct bnx2x_vlan_mac_ramrod_params ramrod_param;
- /* networking MAC */
- bnx2x_set_mac_addr_gen(bp, set, bp->dev->dev_addr,
- (1 << bp->fp->cl_id), cam_offset , 0);
+ memset(&ramrod_param, 0, sizeof(ramrod_param));
- bnx2x_set_mac_in_nig(bp, set, bp->dev->dev_addr, LLH_CAM_ETH_LINE);
+ /* Fill general parameters */
+ ramrod_param.vlan_mac_obj = obj;
+ ramrod_param.ramrod_flags = *ramrod_flags;
- if (CHIP_IS_E1(bp)) {
- /* broadcast MAC */
- static const u8 bcast[ETH_ALEN] = {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
- };
- bnx2x_set_mac_addr_gen(bp, set, bcast, 0, cam_offset + 1, 1);
- }
-}
-
-static inline u8 bnx2x_e1_cam_mc_offset(struct bnx2x *bp)
-{
- return CHIP_REV_IS_SLOW(bp) ?
- (BNX2X_MAX_EMUL_MULTI * (1 + BP_PORT(bp))) :
- (BNX2X_MAX_MULTICAST * (1 + BP_PORT(bp)));
-}
-
-/* set mc list, do not wait as wait implies sleep and
- * set_rx_mode can be invoked from non-sleepable context.
- *
- * Instead we use the same ramrod data buffer each time we need
- * to configure a list of addresses, and use the fact that the
- * list of MACs is changed in an incremental way and that the
- * function is called under the netif_addr_lock. A temporary
- * inconsistent CAM configuration (possible in case of a very fast
- * sequence of add/del/add on the host side) will shortly be
- * restored by the handler of the last ramrod.
- */
-static int bnx2x_set_e1_mc_list(struct bnx2x *bp)
-{
- int i = 0, old;
- struct net_device *dev = bp->dev;
- u8 offset = bnx2x_e1_cam_mc_offset(bp);
- struct netdev_hw_addr *ha;
- struct mac_configuration_cmd *config_cmd = bnx2x_sp(bp, mcast_config);
- dma_addr_t config_cmd_map = bnx2x_sp_mapping(bp, mcast_config);
+ /* Fill a user request section if needed */
+ if (!test_bit(RAMROD_CONT, ramrod_flags)) {
+ memcpy(ramrod_param.user_req.u.mac.mac, mac, ETH_ALEN);
- if (netdev_mc_count(dev) > BNX2X_MAX_MULTICAST)
- return -EINVAL;
+ __set_bit(mac_type, &ramrod_param.user_req.vlan_mac_flags);
- netdev_for_each_mc_addr(ha, dev) {
- /* copy mac */
- config_cmd->config_table[i].msb_mac_addr =
- swab16(*(u16 *)&bnx2x_mc_addr(ha)[0]);
- config_cmd->config_table[i].middle_mac_addr =
- swab16(*(u16 *)&bnx2x_mc_addr(ha)[2]);
- config_cmd->config_table[i].lsb_mac_addr =
- swab16(*(u16 *)&bnx2x_mc_addr(ha)[4]);
-
- config_cmd->config_table[i].vlan_id = 0;
- config_cmd->config_table[i].pf_id = BP_FUNC(bp);
- config_cmd->config_table[i].clients_bit_vector =
- cpu_to_le32(1 << BP_L_ID(bp));
-
- SET_FLAG(config_cmd->config_table[i].flags,
- MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
- T_ETH_MAC_COMMAND_SET);
-
- DP(NETIF_MSG_IFUP,
- "setting MCAST[%d] (%04x:%04x:%04x)\n", i,
- config_cmd->config_table[i].msb_mac_addr,
- config_cmd->config_table[i].middle_mac_addr,
- config_cmd->config_table[i].lsb_mac_addr);
- i++;
- }
- old = config_cmd->hdr.length;
- if (old > i) {
- for (; i < old; i++) {
- if (CAM_IS_INVALID(config_cmd->
- config_table[i])) {
- /* already invalidated */
- break;
- }
- /* invalidate */
- SET_FLAG(config_cmd->config_table[i].flags,
- MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
- T_ETH_MAC_COMMAND_INVALIDATE);
- }
+ /* Set the command: ADD or DEL */
+ if (set)
+ ramrod_param.user_req.cmd = BNX2X_VLAN_MAC_ADD;
+ else
+ ramrod_param.user_req.cmd = BNX2X_VLAN_MAC_DEL;
}
- wmb();
-
- config_cmd->hdr.length = i;
- config_cmd->hdr.offset = offset;
- config_cmd->hdr.client_id = 0xff;
- /* Mark that this ramrod doesn't use bp->set_mac_pending for
- * synchronization.
- */
- config_cmd->hdr.echo = 0;
-
- mb();
-
- return bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_SET_MAC, 0,
- U64_HI(config_cmd_map), U64_LO(config_cmd_map), 1);
-}
-
-void bnx2x_invalidate_e1_mc_list(struct bnx2x *bp)
-{
- int i;
- struct mac_configuration_cmd *config_cmd = bnx2x_sp(bp, mcast_config);
- dma_addr_t config_cmd_map = bnx2x_sp_mapping(bp, mcast_config);
- int ramrod_flags = WAIT_RAMROD_COMMON;
- u8 offset = bnx2x_e1_cam_mc_offset(bp);
-
- for (i = 0; i < BNX2X_MAX_MULTICAST; i++)
- SET_FLAG(config_cmd->config_table[i].flags,
- MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
- T_ETH_MAC_COMMAND_INVALIDATE);
-
- wmb();
-
- config_cmd->hdr.length = BNX2X_MAX_MULTICAST;
- config_cmd->hdr.offset = offset;
- config_cmd->hdr.client_id = 0xff;
- /* We'll wait for a completion this time... */
- config_cmd->hdr.echo = 1;
-
- bp->set_mac_pending = 1;
-
- mb();
-
- bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_SET_MAC, 0,
- U64_HI(config_cmd_map), U64_LO(config_cmd_map), 1);
-
- /* Wait for a completion */
- bnx2x_wait_ramrod(bp, 0, 0, &bp->set_mac_pending,
- ramrod_flags);
-
+ rc = bnx2x_config_vlan_mac(bp, &ramrod_param);
+ if (rc < 0)
+ BNX2X_ERR("%s MAC failed\n", (set ? "Set" : "Del"));
+ return rc;
}
-/* Accept one or more multicasts */
-static int bnx2x_set_e1h_mc_list(struct bnx2x *bp)
+int bnx2x_del_all_macs(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *mac_obj,
+ int mac_type, bool wait_for_comp)
{
- struct net_device *dev = bp->dev;
- struct netdev_hw_addr *ha;
- u32 mc_filter[MC_HASH_SIZE];
- u32 crc, bit, regidx;
- int i;
-
- memset(mc_filter, 0, 4 * MC_HASH_SIZE);
-
- netdev_for_each_mc_addr(ha, dev) {
- DP(NETIF_MSG_IFUP, "Adding mcast MAC: %pM\n",
- bnx2x_mc_addr(ha));
-
- crc = crc32c_le(0, bnx2x_mc_addr(ha),
- ETH_ALEN);
- bit = (crc >> 24) & 0xff;
- regidx = bit >> 5;
- bit &= 0x1f;
- mc_filter[regidx] |= (1 << bit);
- }
+ int rc;
+ unsigned long ramrod_flags = 0, vlan_mac_flags = 0;
- for (i = 0; i < MC_HASH_SIZE; i++)
- REG_WR(bp, MC_HASH_OFFSET(bp, i),
- mc_filter[i]);
+ /* Wait for completion of requested */
+ if (wait_for_comp)
+ __set_bit(RAMROD_COMP_WAIT, &ramrod_flags);
- return 0;
-}
+ /* Set the mac type of addresses we want to clear */
+ __set_bit(mac_type, &vlan_mac_flags);
-void bnx2x_invalidate_e1h_mc_list(struct bnx2x *bp)
-{
- int i;
+ rc = mac_obj->delete_all(bp, mac_obj, &vlan_mac_flags, &ramrod_flags);
+ if (rc < 0)
+ BNX2X_ERR("Failed to delete MACs: %d\n", rc);
- for (i = 0; i < MC_HASH_SIZE; i++)
- REG_WR(bp, MC_HASH_OFFSET(bp, i), 0);
+ return rc;
}
-#ifdef BCM_CNIC
-/**
- * bnx2x_set_iscsi_eth_mac_addr - set iSCSI MAC(s).
- *
- * @bp: driver handle
- * @set: set or clear the CAM entry
- *
- * This function will wait until the ramdord completion returns.
- * Return 0 if success, -ENODEV if ramrod doesn't return.
- */
-static int bnx2x_set_iscsi_eth_mac_addr(struct bnx2x *bp, int set)
+int bnx2x_set_eth_mac(struct bnx2x *bp, bool set)
{
- u8 cam_offset = (CHIP_IS_E1(bp) ? ((BP_PORT(bp) ? 32 : 0) + 2) :
- bnx2x_e1h_cam_offset(bp, CAM_ISCSI_ETH_LINE));
- u32 iscsi_l2_cl_id = BNX2X_ISCSI_ETH_CL_ID +
- BP_E1HVN(bp) * NONE_ETH_CONTEXT_USE;
- u32 cl_bit_vec = (1 << iscsi_l2_cl_id);
- u8 *iscsi_mac = bp->cnic_eth_dev.iscsi_mac;
-
- /* Send a SET_MAC ramrod */
- bnx2x_set_mac_addr_gen(bp, set, iscsi_mac, cl_bit_vec,
- cam_offset, 0);
+ unsigned long ramrod_flags = 0;
- bnx2x_set_mac_in_nig(bp, set, iscsi_mac, LLH_CAM_ISCSI_ETH_LINE);
-
- return 0;
-}
-
-/**
- * bnx2x_set_fip_eth_mac_addr - set FCoE L2 MAC(s)
- *
- * @bp: driver handle
- * @set: set or clear the CAM entry
- *
- * This function will wait until the ramrod completion returns.
- * Returns 0 if success, -ENODEV if ramrod doesn't return.
- */
-int bnx2x_set_fip_eth_mac_addr(struct bnx2x *bp, int set)
-{
- u32 cl_bit_vec = (1 << bnx2x_fcoe(bp, cl_id));
- /**
- * CAM allocation for E1H
- * eth unicasts: by func number
- * iscsi: by func number
- * fip unicast: by func number
- * fip multicast: by func number
- */
- bnx2x_set_mac_addr_gen(bp, set, bp->fip_mac,
- cl_bit_vec, bnx2x_e1h_cam_offset(bp, CAM_FIP_ETH_LINE), 0);
+ DP(NETIF_MSG_IFUP, "Adding Eth MAC\n");
- return 0;
+ __set_bit(RAMROD_COMP_WAIT, &ramrod_flags);
+ /* Eth MAC is set on RSS leading client (fp[0]) */
+ return bnx2x_set_mac_one(bp, bp->dev->dev_addr, &bp->fp->mac_obj, set,
+ BNX2X_ETH_MAC, &ramrod_flags);
}
-int bnx2x_set_all_enode_macs(struct bnx2x *bp, int set)
+int bnx2x_setup_leading(struct bnx2x *bp)
{
- u32 cl_bit_vec = (1 << bnx2x_fcoe(bp, cl_id));
-
- /**
- * CAM allocation for E1H
- * eth unicasts: by func number
- * iscsi: by func number
- * fip unicast: by func number
- * fip multicast: by func number
- */
- bnx2x_set_mac_addr_gen(bp, set, ALL_ENODE_MACS, cl_bit_vec,
- bnx2x_e1h_cam_offset(bp, CAM_FIP_MCAST_LINE), 0);
-
- return 0;
-}
-#endif
-
-static void bnx2x_fill_cl_init_data(struct bnx2x *bp,
- struct bnx2x_client_init_params *params,
- u8 activate,
- struct client_init_ramrod_data *data)
-{
- /* Clear the buffer */
- memset(data, 0, sizeof(*data));
-
- /* general */
- data->general.client_id = params->rxq_params.cl_id;
- data->general.statistics_counter_id = params->rxq_params.stat_id;
- data->general.statistics_en_flg =
- (params->rxq_params.flags & QUEUE_FLG_STATS) ? 1 : 0;
- data->general.is_fcoe_flg =
- (params->ramrod_params.flags & CLIENT_IS_FCOE) ? 1 : 0;
- data->general.activate_flg = activate;
- data->general.sp_client_id = params->rxq_params.spcl_id;
-
- /* Rx data */
- data->rx.tpa_en_flg =
- (params->rxq_params.flags & QUEUE_FLG_TPA) ? 1 : 0;
- data->rx.vmqueue_mode_en_flg = 0;
- data->rx.cache_line_alignment_log_size =
- params->rxq_params.cache_line_log;
- data->rx.enable_dynamic_hc =
- (params->rxq_params.flags & QUEUE_FLG_DHC) ? 1 : 0;
- data->rx.max_sges_for_packet = params->rxq_params.max_sges_pkt;
- data->rx.client_qzone_id = params->rxq_params.cl_qzone_id;
- data->rx.max_agg_size = params->rxq_params.tpa_agg_sz;
-
- /* We don't set drop flags */
- data->rx.drop_ip_cs_err_flg = 0;
- data->rx.drop_tcp_cs_err_flg = 0;
- data->rx.drop_ttl0_flg = 0;
- data->rx.drop_udp_cs_err_flg = 0;
-
- data->rx.inner_vlan_removal_enable_flg =
- (params->rxq_params.flags & QUEUE_FLG_VLAN) ? 1 : 0;
- data->rx.outer_vlan_removal_enable_flg =
- (params->rxq_params.flags & QUEUE_FLG_OV) ? 1 : 0;
- data->rx.status_block_id = params->rxq_params.fw_sb_id;
- data->rx.rx_sb_index_number = params->rxq_params.sb_cq_index;
- data->rx.bd_buff_size = cpu_to_le16(params->rxq_params.buf_sz);
- data->rx.sge_buff_size = cpu_to_le16(params->rxq_params.sge_buf_sz);
- data->rx.mtu = cpu_to_le16(params->rxq_params.mtu);
- data->rx.bd_page_base.lo =
- cpu_to_le32(U64_LO(params->rxq_params.dscr_map));
- data->rx.bd_page_base.hi =
- cpu_to_le32(U64_HI(params->rxq_params.dscr_map));
- data->rx.sge_page_base.lo =
- cpu_to_le32(U64_LO(params->rxq_params.sge_map));
- data->rx.sge_page_base.hi =
- cpu_to_le32(U64_HI(params->rxq_params.sge_map));
- data->rx.cqe_page_base.lo =
- cpu_to_le32(U64_LO(params->rxq_params.rcq_map));
- data->rx.cqe_page_base.hi =
- cpu_to_le32(U64_HI(params->rxq_params.rcq_map));
- data->rx.is_leading_rss =
- (params->ramrod_params.flags & CLIENT_IS_LEADING_RSS) ? 1 : 0;
- data->rx.is_approx_mcast = data->rx.is_leading_rss;
-
- /* Tx data */
- data->tx.enforce_security_flg = 0; /* VF specific */
- data->tx.tx_status_block_id = params->txq_params.fw_sb_id;
- data->tx.tx_sb_index_number = params->txq_params.sb_cq_index;
- data->tx.mtu = 0; /* VF specific */
- data->tx.tx_bd_page_base.lo =
- cpu_to_le32(U64_LO(params->txq_params.dscr_map));
- data->tx.tx_bd_page_base.hi =
- cpu_to_le32(U64_HI(params->txq_params.dscr_map));
-
- /* flow control data */
- data->fc.cqe_pause_thr_low = cpu_to_le16(params->pause.rcq_th_lo);
- data->fc.cqe_pause_thr_high = cpu_to_le16(params->pause.rcq_th_hi);
- data->fc.bd_pause_thr_low = cpu_to_le16(params->pause.bd_th_lo);
- data->fc.bd_pause_thr_high = cpu_to_le16(params->pause.bd_th_hi);
- data->fc.sge_pause_thr_low = cpu_to_le16(params->pause.sge_th_lo);
- data->fc.sge_pause_thr_high = cpu_to_le16(params->pause.sge_th_hi);
- data->fc.rx_cos_mask = cpu_to_le16(params->pause.pri_map);
-
- data->fc.safc_group_num = params->txq_params.cos;
- data->fc.safc_group_en_flg =
- (params->txq_params.flags & QUEUE_FLG_COS) ? 1 : 0;
- data->fc.traffic_type =
- (params->ramrod_params.flags & CLIENT_IS_FCOE) ?
- LLFC_TRAFFIC_TYPE_FCOE : LLFC_TRAFFIC_TYPE_NW;
-}
-
-static inline void bnx2x_set_ctx_validation(struct eth_context *cxt, u32 cid)
-{
- /* ustorm cxt validation */
- cxt->ustorm_ag_context.cdu_usage =
- CDU_RSRVD_VALUE_TYPE_A(cid, CDU_REGION_NUMBER_UCM_AG,
- ETH_CONNECTION_TYPE);
- /* xcontext validation */
- cxt->xstorm_ag_context.cdu_reserved =
- CDU_RSRVD_VALUE_TYPE_A(cid, CDU_REGION_NUMBER_XCM_AG,
- ETH_CONNECTION_TYPE);
-}
-
-static int bnx2x_setup_fw_client(struct bnx2x *bp,
- struct bnx2x_client_init_params *params,
- u8 activate,
- struct client_init_ramrod_data *data,
- dma_addr_t data_mapping)
-{
- u16 hc_usec;
- int ramrod = RAMROD_CMD_ID_ETH_CLIENT_SETUP;
- int ramrod_flags = 0, rc;
-
- /* HC and context validation values */
- hc_usec = params->txq_params.hc_rate ?
- 1000000 / params->txq_params.hc_rate : 0;
- bnx2x_update_coalesce_sb_index(bp,
- params->txq_params.fw_sb_id,
- params->txq_params.sb_cq_index,
- !(params->txq_params.flags & QUEUE_FLG_HC),
- hc_usec);
-
- *(params->ramrod_params.pstate) = BNX2X_FP_STATE_OPENING;
-
- hc_usec = params->rxq_params.hc_rate ?
- 1000000 / params->rxq_params.hc_rate : 0;
- bnx2x_update_coalesce_sb_index(bp,
- params->rxq_params.fw_sb_id,
- params->rxq_params.sb_cq_index,
- !(params->rxq_params.flags & QUEUE_FLG_HC),
- hc_usec);
-
- bnx2x_set_ctx_validation(params->rxq_params.cxt,
- params->rxq_params.cid);
-
- /* zero stats */
- if (params->txq_params.flags & QUEUE_FLG_STATS)
- storm_memset_xstats_zero(bp, BP_PORT(bp),
- params->txq_params.stat_id);
-
- if (params->rxq_params.flags & QUEUE_FLG_STATS) {
- storm_memset_ustats_zero(bp, BP_PORT(bp),
- params->rxq_params.stat_id);
- storm_memset_tstats_zero(bp, BP_PORT(bp),
- params->rxq_params.stat_id);
- }
-
- /* Fill the ramrod data */
- bnx2x_fill_cl_init_data(bp, params, activate, data);
-
- /* SETUP ramrod.
- *
- * bnx2x_sp_post() takes a spin_lock thus no other explict memory
- * barrier except from mmiowb() is needed to impose a
- * proper ordering of memory operations.
- */
- mmiowb();
-
-
- bnx2x_sp_post(bp, ramrod, params->ramrod_params.cid,
- U64_HI(data_mapping), U64_LO(data_mapping), 0);
-
- /* Wait for completion */
- rc = bnx2x_wait_ramrod(bp, params->ramrod_params.state,
- params->ramrod_params.index,
- params->ramrod_params.pstate,
- ramrod_flags);
- return rc;
+ return bnx2x_setup_queue(bp, &bp->fp[0], 1);
}
/**
@@ -6647,16 +7045,14 @@ static int bnx2x_setup_fw_client(struct bnx2x *bp,
*
* In case of MSI-X it will also try to enable MSI-X.
*/
-static int __devinit bnx2x_set_int_mode(struct bnx2x *bp)
+static void __devinit bnx2x_set_int_mode(struct bnx2x *bp)
{
- int rc = 0;
-
- switch (bp->int_mode) {
+ switch (int_mode) {
case INT_MODE_MSI:
bnx2x_enable_msi(bp);
/* falling through... */
case INT_MODE_INTx:
- bp->num_queues = 1 + NONE_ETH_CONTEXT_USE;
+ bp->num_queues = 1 + NON_ETH_CONTEXT_USE;
DP(NETIF_MSG_IFUP, "set number of queues to 1\n");
break;
default:
@@ -6670,8 +7066,7 @@ static int __devinit bnx2x_set_int_mode(struct bnx2x *bp)
* so try to enable MSI-X with the requested number of fp's
* and fallback to MSI or legacy INTx with one fp
*/
- rc = bnx2x_enable_msix(bp);
- if (rc) {
+ if (bnx2x_enable_msix(bp)) {
/* failed to enable MSI-X */
if (bp->multi_mode)
DP(NETIF_MSG_IFUP,
@@ -6679,17 +7074,15 @@ static int __devinit bnx2x_set_int_mode(struct bnx2x *bp)
"enable MSI-X (%d), "
"set number of queues to %d\n",
bp->num_queues,
- 1 + NONE_ETH_CONTEXT_USE);
- bp->num_queues = 1 + NONE_ETH_CONTEXT_USE;
+ 1 + NON_ETH_CONTEXT_USE);
+ bp->num_queues = 1 + NON_ETH_CONTEXT_USE;
+ /* Try to enable MSI */
if (!(bp->flags & DISABLE_MSI_FLAG))
bnx2x_enable_msi(bp);
}
-
break;
}
-
- return rc;
}
/* must be called prioir to any HW initializations */
@@ -6713,7 +7106,7 @@ void bnx2x_ilt_set_info(struct bnx2x *bp)
ilt_client->page_size = CDU_ILT_PAGE_SZ;
ilt_client->flags = ILT_CLIENT_SKIP_MEM;
ilt_client->start = line;
- line += L2_ILT_LINES(bp);
+ line += bnx2x_cid_ilt_lines(bp);
#ifdef BCM_CNIC
line += CNIC_ILT_LINES;
#endif
@@ -6793,92 +7186,258 @@ void bnx2x_ilt_set_info(struct bnx2x *bp)
#else
ilt_client->flags = (ILT_CLIENT_SKIP_INIT | ILT_CLIENT_SKIP_MEM);
#endif
+ BUG_ON(line > ILT_MAX_LINES);
+}
+
+/**
+ * bnx2x_pf_q_prep_init - prepare INIT transition parameters
+ *
+ * @bp: driver handle
+ * @fp: pointer to fastpath
+ * @init_params: pointer to parameters structure
+ *
+ * parameters configured:
+ * - HC configuration
+ * - Queue's CDU context
+ */
+static inline void bnx2x_pf_q_prep_init(struct bnx2x *bp,
+ struct bnx2x_fastpath *fp, struct bnx2x_queue_init_params *init_params)
+{
+
+ u8 cos;
+ /* FCoE Queue uses Default SB, thus has no HC capabilities */
+ if (!IS_FCOE_FP(fp)) {
+ __set_bit(BNX2X_Q_FLG_HC, &init_params->rx.flags);
+ __set_bit(BNX2X_Q_FLG_HC, &init_params->tx.flags);
+
+ /* If HC is supporterd, enable host coalescing in the transition
+ * to INIT state.
+ */
+ __set_bit(BNX2X_Q_FLG_HC_EN, &init_params->rx.flags);
+ __set_bit(BNX2X_Q_FLG_HC_EN, &init_params->tx.flags);
+
+ /* HC rate */
+ init_params->rx.hc_rate = bp->rx_ticks ?
+ (1000000 / bp->rx_ticks) : 0;
+ init_params->tx.hc_rate = bp->tx_ticks ?
+ (1000000 / bp->tx_ticks) : 0;
+
+ /* FW SB ID */
+ init_params->rx.fw_sb_id = init_params->tx.fw_sb_id =
+ fp->fw_sb_id;
+
+ /*
+ * CQ index among the SB indices: FCoE clients uses the default
+ * SB, therefore it's different.
+ */
+ init_params->rx.sb_cq_index = HC_INDEX_ETH_RX_CQ_CONS;
+ init_params->tx.sb_cq_index = HC_INDEX_ETH_FIRST_TX_CQ_CONS;
+ }
+
+ /* set maximum number of COSs supported by this queue */
+ init_params->max_cos = fp->max_cos;
+
+ DP(BNX2X_MSG_SP, "fp: %d setting queue params max cos to: %d\n",
+ fp->index, init_params->max_cos);
+
+ /* set the context pointers queue object */
+ for (cos = FIRST_TX_COS_INDEX; cos < init_params->max_cos; cos++)
+ init_params->cxts[cos] =
+ &bp->context.vcxt[fp->txdata[cos].cid].eth;
}
-int bnx2x_setup_client(struct bnx2x *bp, struct bnx2x_fastpath *fp,
- int is_leading)
+int bnx2x_setup_tx_only(struct bnx2x *bp, struct bnx2x_fastpath *fp,
+ struct bnx2x_queue_state_params *q_params,
+ struct bnx2x_queue_setup_tx_only_params *tx_only_params,
+ int tx_index, bool leading)
+{
+ memset(tx_only_params, 0, sizeof(*tx_only_params));
+
+ /* Set the command */
+ q_params->cmd = BNX2X_Q_CMD_SETUP_TX_ONLY;
+
+ /* Set tx-only QUEUE flags: don't zero statistics */
+ tx_only_params->flags = bnx2x_get_common_flags(bp, fp, false);
+
+ /* choose the index of the cid to send the slow path on */
+ tx_only_params->cid_index = tx_index;
+
+ /* Set general TX_ONLY_SETUP parameters */
+ bnx2x_pf_q_prep_general(bp, fp, &tx_only_params->gen_params, tx_index);
+
+ /* Set Tx TX_ONLY_SETUP parameters */
+ bnx2x_pf_tx_q_prep(bp, fp, &tx_only_params->txq_params, tx_index);
+
+ DP(BNX2X_MSG_SP, "preparing to send tx-only ramrod for connection:"
+ "cos %d, primary cid %d, cid %d, "
+ "client id %d, sp-client id %d, flags %lx\n",
+ tx_index, q_params->q_obj->cids[FIRST_TX_COS_INDEX],
+ q_params->q_obj->cids[tx_index], q_params->q_obj->cl_id,
+ tx_only_params->gen_params.spcl_id, tx_only_params->flags);
+
+ /* send the ramrod */
+ return bnx2x_queue_state_change(bp, q_params);
+}
+
+
+/**
+ * bnx2x_setup_queue - setup queue
+ *
+ * @bp: driver handle
+ * @fp: pointer to fastpath
+ * @leading: is leading
+ *
+ * This function performs 2 steps in a Queue state machine
+ * actually: 1) RESET->INIT 2) INIT->SETUP
+ */
+
+int bnx2x_setup_queue(struct bnx2x *bp, struct bnx2x_fastpath *fp,
+ bool leading)
{
- struct bnx2x_client_init_params params = { {0} };
+ struct bnx2x_queue_state_params q_params = {0};
+ struct bnx2x_queue_setup_params *setup_params =
+ &q_params.params.setup;
+ struct bnx2x_queue_setup_tx_only_params *tx_only_params =
+ &q_params.params.tx_only;
int rc;
+ u8 tx_index;
+
+ DP(BNX2X_MSG_SP, "setting up queue %d\n", fp->index);
/* reset IGU state skip FCoE L2 queue */
if (!IS_FCOE_FP(fp))
bnx2x_ack_sb(bp, fp->igu_sb_id, USTORM_ID, 0,
IGU_INT_ENABLE, 0);
- params.ramrod_params.pstate = &fp->state;
- params.ramrod_params.state = BNX2X_FP_STATE_OPEN;
- params.ramrod_params.index = fp->index;
- params.ramrod_params.cid = fp->cid;
+ q_params.q_obj = &fp->q_obj;
+ /* We want to wait for completion in this context */
+ __set_bit(RAMROD_COMP_WAIT, &q_params.ramrod_flags);
-#ifdef BCM_CNIC
- if (IS_FCOE_FP(fp))
- params.ramrod_params.flags |= CLIENT_IS_FCOE;
+ /* Prepare the INIT parameters */
+ bnx2x_pf_q_prep_init(bp, fp, &q_params.params.init);
-#endif
+ /* Set the command */
+ q_params.cmd = BNX2X_Q_CMD_INIT;
- if (is_leading)
- params.ramrod_params.flags |= CLIENT_IS_LEADING_RSS;
+ /* Change the state to INIT */
+ rc = bnx2x_queue_state_change(bp, &q_params);
+ if (rc) {
+ BNX2X_ERR("Queue(%d) INIT failed\n", fp->index);
+ return rc;
+ }
- bnx2x_pf_rx_cl_prep(bp, fp, &params.pause, &params.rxq_params);
+ DP(BNX2X_MSG_SP, "init complete\n");
- bnx2x_pf_tx_cl_prep(bp, fp, &params.txq_params);
- rc = bnx2x_setup_fw_client(bp, &params, 1,
- bnx2x_sp(bp, client_init_data),
- bnx2x_sp_mapping(bp, client_init_data));
- return rc;
-}
+ /* Now move the Queue to the SETUP state... */
+ memset(setup_params, 0, sizeof(*setup_params));
-static int bnx2x_stop_fw_client(struct bnx2x *bp,
- struct bnx2x_client_ramrod_params *p)
-{
- int rc;
+ /* Set QUEUE flags */
+ setup_params->flags = bnx2x_get_q_flags(bp, fp, leading);
- int poll_flag = p->poll ? WAIT_RAMROD_POLL : 0;
+ /* Set general SETUP parameters */
+ bnx2x_pf_q_prep_general(bp, fp, &setup_params->gen_params,
+ FIRST_TX_COS_INDEX);
- /* halt the connection */
- *p->pstate = BNX2X_FP_STATE_HALTING;
- bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_HALT, p->cid, 0,
- p->cl_id, 0);
+ bnx2x_pf_rx_q_prep(bp, fp, &setup_params->pause_params,
+ &setup_params->rxq_params);
- /* Wait for completion */
- rc = bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_HALTED, p->index,
- p->pstate, poll_flag);
- if (rc) /* timeout */
- return rc;
+ bnx2x_pf_tx_q_prep(bp, fp, &setup_params->txq_params,
+ FIRST_TX_COS_INDEX);
+
+ /* Set the command */
+ q_params.cmd = BNX2X_Q_CMD_SETUP;
- *p->pstate = BNX2X_FP_STATE_TERMINATING;
- bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_TERMINATE, p->cid, 0,
- p->cl_id, 0);
- /* Wait for completion */
- rc = bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_TERMINATED, p->index,
- p->pstate, poll_flag);
- if (rc) /* timeout */
+ /* Change the state to SETUP */
+ rc = bnx2x_queue_state_change(bp, &q_params);
+ if (rc) {
+ BNX2X_ERR("Queue(%d) SETUP failed\n", fp->index);
return rc;
+ }
+ /* loop through the relevant tx-only indices */
+ for (tx_index = FIRST_TX_ONLY_COS_INDEX;
+ tx_index < fp->max_cos;
+ tx_index++) {
- /* delete cfc entry */
- bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_CFC_DEL, p->cid, 0, 0, 1);
+ /* prepare and send tx-only ramrod*/
+ rc = bnx2x_setup_tx_only(bp, fp, &q_params,
+ tx_only_params, tx_index, leading);
+ if (rc) {
+ BNX2X_ERR("Queue(%d.%d) TX_ONLY_SETUP failed\n",
+ fp->index, tx_index);
+ return rc;
+ }
+ }
- /* Wait for completion */
- rc = bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_CLOSED, p->index,
- p->pstate, WAIT_RAMROD_COMMON);
return rc;
}
-static int bnx2x_stop_client(struct bnx2x *bp, int index)
+static int bnx2x_stop_queue(struct bnx2x *bp, int index)
{
- struct bnx2x_client_ramrod_params client_stop = {0};
struct bnx2x_fastpath *fp = &bp->fp[index];
+ struct bnx2x_fp_txdata *txdata;
+ struct bnx2x_queue_state_params q_params = {0};
+ int rc, tx_index;
+
+ DP(BNX2X_MSG_SP, "stopping queue %d cid %d\n", index, fp->cid);
+
+ q_params.q_obj = &fp->q_obj;
+ /* We want to wait for completion in this context */
+ __set_bit(RAMROD_COMP_WAIT, &q_params.ramrod_flags);
+
- client_stop.index = index;
- client_stop.cid = fp->cid;
- client_stop.cl_id = fp->cl_id;
- client_stop.pstate = &(fp->state);
- client_stop.poll = 0;
+ /* close tx-only connections */
+ for (tx_index = FIRST_TX_ONLY_COS_INDEX;
+ tx_index < fp->max_cos;
+ tx_index++){
- return bnx2x_stop_fw_client(bp, &client_stop);
+ /* ascertain this is a normal queue*/
+ txdata = &fp->txdata[tx_index];
+
+ DP(BNX2X_MSG_SP, "stopping tx-only queue %d\n",
+ txdata->txq_index);
+
+ /* send halt terminate on tx-only connection */
+ q_params.cmd = BNX2X_Q_CMD_TERMINATE;
+ memset(&q_params.params.terminate, 0,
+ sizeof(q_params.params.terminate));
+ q_params.params.terminate.cid_index = tx_index;
+
+ rc = bnx2x_queue_state_change(bp, &q_params);
+ if (rc)
+ return rc;
+
+ /* send halt terminate on tx-only connection */
+ q_params.cmd = BNX2X_Q_CMD_CFC_DEL;
+ memset(&q_params.params.cfc_del, 0,
+ sizeof(q_params.params.cfc_del));
+ q_params.params.cfc_del.cid_index = tx_index;
+ rc = bnx2x_queue_state_change(bp, &q_params);
+ if (rc)
+ return rc;
+ }
+ /* Stop the primary connection: */
+ /* ...halt the connection */
+ q_params.cmd = BNX2X_Q_CMD_HALT;
+ rc = bnx2x_queue_state_change(bp, &q_params);
+ if (rc)
+ return rc;
+
+ /* ...terminate the connection */
+ q_params.cmd = BNX2X_Q_CMD_TERMINATE;
+ memset(&q_params.params.terminate, 0,
+ sizeof(q_params.params.terminate));
+ q_params.params.terminate.cid_index = FIRST_TX_COS_INDEX;
+ rc = bnx2x_queue_state_change(bp, &q_params);
+ if (rc)
+ return rc;
+ /* ...delete cfc entry */
+ q_params.cmd = BNX2X_Q_CMD_CFC_DEL;
+ memset(&q_params.params.cfc_del, 0,
+ sizeof(q_params.params.cfc_del));
+ q_params.params.cfc_del.cid_index = FIRST_TX_COS_INDEX;
+ return bnx2x_queue_state_change(bp, &q_params);
}
@@ -6887,12 +7446,6 @@ static void bnx2x_reset_func(struct bnx2x *bp)
int port = BP_PORT(bp);
int func = BP_FUNC(bp);
int i;
- int pfunc_offset_fp = offsetof(struct hc_sb_data, p_func) +
- (CHIP_IS_E2(bp) ?
- offsetof(struct hc_status_block_data_e2, common) :
- offsetof(struct hc_status_block_data_e1x, common));
- int pfunc_offset_sp = offsetof(struct hc_sp_status_block_data, p_func);
- int pfid_offset = offsetof(struct pci_entity, pf_id);
/* Disable the function in the FW */
REG_WR8(bp, BAR_XSTRORM_INTMEM + XSTORM_FUNC_EN_OFFSET(func), 0);
@@ -6903,20 +7456,21 @@ static void bnx2x_reset_func(struct bnx2x *bp)
/* FP SBs */
for_each_eth_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
- REG_WR8(bp,
- BAR_CSTRORM_INTMEM +
- CSTORM_STATUS_BLOCK_DATA_OFFSET(fp->fw_sb_id)
- + pfunc_offset_fp + pfid_offset,
- HC_FUNCTION_DISABLED);
+ REG_WR8(bp, BAR_CSTRORM_INTMEM +
+ CSTORM_STATUS_BLOCK_DATA_STATE_OFFSET(fp->fw_sb_id),
+ SB_DISABLED);
}
+#ifdef BCM_CNIC
+ /* CNIC SB */
+ REG_WR8(bp, BAR_CSTRORM_INTMEM +
+ CSTORM_STATUS_BLOCK_DATA_STATE_OFFSET(bnx2x_cnic_fw_sb_id(bp)),
+ SB_DISABLED);
+#endif
/* SP SB */
- REG_WR8(bp,
- BAR_CSTRORM_INTMEM +
- CSTORM_SP_STATUS_BLOCK_DATA_OFFSET(func) +
- pfunc_offset_sp + pfid_offset,
- HC_FUNCTION_DISABLED);
-
+ REG_WR8(bp, BAR_CSTRORM_INTMEM +
+ CSTORM_SP_STATUS_BLOCK_DATA_STATE_OFFSET(func),
+ SB_DISABLED);
for (i = 0; i < XSTORM_SPQ_DATA_SIZE / 4; i++)
REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_DATA_OFFSET(func),
@@ -6950,7 +7504,7 @@ static void bnx2x_reset_func(struct bnx2x *bp)
/* Timers workaround bug for E2: if this is vnic-3,
* we need to set the entire ilt range for this timers.
*/
- if (CHIP_IS_E2(bp) && BP_VN(bp) == 3) {
+ if (!CHIP_IS_E1x(bp) && BP_VN(bp) == 3) {
struct ilt_client_info ilt_cli;
/* use dummy TM client */
memset(&ilt_cli, 0, sizeof(struct ilt_client_info));
@@ -6962,7 +7516,7 @@ static void bnx2x_reset_func(struct bnx2x *bp)
}
/* this assumes that reset_port() called before reset_func()*/
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
bnx2x_pf_disable(bp);
bp->dmae_ready = 0;
@@ -6973,6 +7527,9 @@ static void bnx2x_reset_port(struct bnx2x *bp)
int port = BP_PORT(bp);
u32 val;
+ /* Reset physical Link */
+ bnx2x__link_reset(bp);
+
REG_WR(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, 0);
/* Do not rcv packets to BRB */
@@ -6994,92 +7551,66 @@ static void bnx2x_reset_port(struct bnx2x *bp)
/* TODO: Close Doorbell port? */
}
-static void bnx2x_reset_chip(struct bnx2x *bp, u32 reset_code)
+static inline int bnx2x_reset_hw(struct bnx2x *bp, u32 load_code)
{
- DP(BNX2X_MSG_MCP, "function %d reset_code %x\n",
- BP_ABS_FUNC(bp), reset_code);
-
- switch (reset_code) {
- case FW_MSG_CODE_DRV_UNLOAD_COMMON:
- bnx2x_reset_port(bp);
- bnx2x_reset_func(bp);
- bnx2x_reset_common(bp);
- break;
-
- case FW_MSG_CODE_DRV_UNLOAD_PORT:
- bnx2x_reset_port(bp);
- bnx2x_reset_func(bp);
- break;
-
- case FW_MSG_CODE_DRV_UNLOAD_FUNCTION:
- bnx2x_reset_func(bp);
- break;
+ struct bnx2x_func_state_params func_params = {0};
- default:
- BNX2X_ERR("Unknown reset_code (0x%x) from MCP\n", reset_code);
- break;
- }
-}
+ /* Prepare parameters for function state transitions */
+ __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags);
-#ifdef BCM_CNIC
-static inline void bnx2x_del_fcoe_eth_macs(struct bnx2x *bp)
-{
- if (bp->flags & FCOE_MACS_SET) {
- if (!IS_MF_SD(bp))
- bnx2x_set_fip_eth_mac_addr(bp, 0);
+ func_params.f_obj = &bp->func_obj;
+ func_params.cmd = BNX2X_F_CMD_HW_RESET;
- bnx2x_set_all_enode_macs(bp, 0);
+ func_params.params.hw_init.load_phase = load_code;
- bp->flags &= ~FCOE_MACS_SET;
- }
+ return bnx2x_func_state_change(bp, &func_params);
}
-#endif
-void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode)
+static inline int bnx2x_func_stop(struct bnx2x *bp)
{
- int port = BP_PORT(bp);
- u32 reset_code = 0;
- int i, cnt, rc;
-
- /* Wait until tx fastpath tasks complete */
- for_each_tx_queue(bp, i) {
- struct bnx2x_fastpath *fp = &bp->fp[i];
+ struct bnx2x_func_state_params func_params = {0};
+ int rc;
- cnt = 1000;
- while (bnx2x_has_tx_work_unload(fp)) {
+ /* Prepare parameters for function state transitions */
+ __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags);
+ func_params.f_obj = &bp->func_obj;
+ func_params.cmd = BNX2X_F_CMD_STOP;
- if (!cnt) {
- BNX2X_ERR("timeout waiting for queue[%d]\n",
- i);
+ /*
+ * Try to stop the function the 'good way'. If fails (in case
+ * of a parity error during bnx2x_chip_cleanup()) and we are
+ * not in a debug mode, perform a state transaction in order to
+ * enable further HW_RESET transaction.
+ */
+ rc = bnx2x_func_state_change(bp, &func_params);
+ if (rc) {
#ifdef BNX2X_STOP_ON_ERROR
- bnx2x_panic();
- return -EBUSY;
+ return rc;
#else
- break;
+ BNX2X_ERR("FUNC_STOP ramrod failed. Running a dry "
+ "transaction\n");
+ __set_bit(RAMROD_DRV_CLR_ONLY, &func_params.ramrod_flags);
+ return bnx2x_func_state_change(bp, &func_params);
#endif
- }
- cnt--;
- msleep(1);
- }
}
- /* Give HW time to discard old tx messages */
- msleep(1);
-
- bnx2x_set_eth_mac(bp, 0);
-
- bnx2x_invalidate_uc_list(bp);
- if (CHIP_IS_E1(bp))
- bnx2x_invalidate_e1_mc_list(bp);
- else {
- bnx2x_invalidate_e1h_mc_list(bp);
- REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 0);
- }
+ return 0;
+}
-#ifdef BCM_CNIC
- bnx2x_del_fcoe_eth_macs(bp);
-#endif
+/**
+ * bnx2x_send_unload_req - request unload mode from the MCP.
+ *
+ * @bp: driver handle
+ * @unload_mode: requested function's unload mode
+ *
+ * Return unload mode returned by the MCP: COMMON, PORT or FUNC.
+ */
+u32 bnx2x_send_unload_req(struct bnx2x *bp, int unload_mode)
+{
+ u32 reset_code = 0;
+ int port = BP_PORT(bp);
+ /* Select the UNLOAD request mode */
if (unload_mode == UNLOAD_NORMAL)
reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS;
@@ -7090,9 +7621,12 @@ void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode)
u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
u8 *mac_addr = bp->dev->dev_addr;
u32 val;
+ u16 pmc;
+
/* The mac address is written to entries 1-4 to
- preserve entry 0 which is used by the PMF */
- u8 entry = (BP_E1HVN(bp) + 1)*8;
+ * preserve entry 0 which is used by the PMF
+ */
+ u8 entry = (BP_VN(bp) + 1)*8;
val = (mac_addr[0] << 8) | mac_addr[1];
EMAC_WR(bp, EMAC_REG_EMAC_MAC_MATCH + entry, val);
@@ -7101,60 +7635,226 @@ void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode)
(mac_addr[4] << 8) | mac_addr[5];
EMAC_WR(bp, EMAC_REG_EMAC_MAC_MATCH + entry + 4, val);
+ /* Enable the PME and clear the status */
+ pci_read_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, &pmc);
+ pmc |= PCI_PM_CTRL_PME_ENABLE | PCI_PM_CTRL_PME_STATUS;
+ pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, pmc);
+
reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_EN;
} else
reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS;
+ /* Send the request to the MCP */
+ if (!BP_NOMCP(bp))
+ reset_code = bnx2x_fw_command(bp, reset_code, 0);
+ else {
+ int path = BP_PATH(bp);
+
+ DP(NETIF_MSG_IFDOWN, "NO MCP - load counts[%d] "
+ "%d, %d, %d\n",
+ path, load_count[path][0], load_count[path][1],
+ load_count[path][2]);
+ load_count[path][0]--;
+ load_count[path][1 + port]--;
+ DP(NETIF_MSG_IFDOWN, "NO MCP - new load counts[%d] "
+ "%d, %d, %d\n",
+ path, load_count[path][0], load_count[path][1],
+ load_count[path][2]);
+ if (load_count[path][0] == 0)
+ reset_code = FW_MSG_CODE_DRV_UNLOAD_COMMON;
+ else if (load_count[path][1 + port] == 0)
+ reset_code = FW_MSG_CODE_DRV_UNLOAD_PORT;
+ else
+ reset_code = FW_MSG_CODE_DRV_UNLOAD_FUNCTION;
+ }
+
+ return reset_code;
+}
+
+/**
+ * bnx2x_send_unload_done - send UNLOAD_DONE command to the MCP.
+ *
+ * @bp: driver handle
+ */
+void bnx2x_send_unload_done(struct bnx2x *bp)
+{
+ /* Report UNLOAD_DONE to MCP */
+ if (!BP_NOMCP(bp))
+ bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_DONE, 0);
+}
+
+static inline int bnx2x_func_wait_started(struct bnx2x *bp)
+{
+ int tout = 50;
+ int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0;
+
+ if (!bp->port.pmf)
+ return 0;
+
+ /*
+ * (assumption: No Attention from MCP at this stage)
+ * PMF probably in the middle of TXdisable/enable transaction
+ * 1. Sync IRS for default SB
+ * 2. Sync SP queue - this guarantes us that attention handling started
+ * 3. Wait, that TXdisable/enable transaction completes
+ *
+ * 1+2 guranty that if DCBx attention was scheduled it already changed
+ * pending bit of transaction from STARTED-->TX_STOPPED, if we alredy
+ * received complettion for the transaction the state is TX_STOPPED.
+ * State will return to STARTED after completion of TX_STOPPED-->STARTED
+ * transaction.
+ */
+
+ /* make sure default SB ISR is done */
+ if (msix)
+ synchronize_irq(bp->msix_table[0].vector);
+ else
+ synchronize_irq(bp->pdev->irq);
+
+ flush_workqueue(bnx2x_wq);
+
+ while (bnx2x_func_get_state(bp, &bp->func_obj) !=
+ BNX2X_F_STATE_STARTED && tout--)
+ msleep(20);
+
+ if (bnx2x_func_get_state(bp, &bp->func_obj) !=
+ BNX2X_F_STATE_STARTED) {
+#ifdef BNX2X_STOP_ON_ERROR
+ return -EBUSY;
+#else
+ /*
+ * Failed to complete the transaction in a "good way"
+ * Force both transactions with CLR bit
+ */
+ struct bnx2x_func_state_params func_params = {0};
+
+ DP(BNX2X_MSG_SP, "Hmmm... unexpected function state! "
+ "Forcing STARTED-->TX_ST0PPED-->STARTED\n");
+
+ func_params.f_obj = &bp->func_obj;
+ __set_bit(RAMROD_DRV_CLR_ONLY,
+ &func_params.ramrod_flags);
+
+ /* STARTED-->TX_ST0PPED */
+ func_params.cmd = BNX2X_F_CMD_TX_STOP;
+ bnx2x_func_state_change(bp, &func_params);
+
+ /* TX_ST0PPED-->STARTED */
+ func_params.cmd = BNX2X_F_CMD_TX_START;
+ return bnx2x_func_state_change(bp, &func_params);
+#endif
+ }
+
+ return 0;
+}
+
+void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode)
+{
+ int port = BP_PORT(bp);
+ int i, rc = 0;
+ u8 cos;
+ struct bnx2x_mcast_ramrod_params rparam = {0};
+ u32 reset_code;
+
+ /* Wait until tx fastpath tasks complete */
+ for_each_tx_queue(bp, i) {
+ struct bnx2x_fastpath *fp = &bp->fp[i];
+
+ for_each_cos_in_tx_queue(fp, cos)
+ rc = bnx2x_clean_tx_queue(bp, &fp->txdata[cos]);
+#ifdef BNX2X_STOP_ON_ERROR
+ if (rc)
+ return;
+#endif
+ }
+
+ /* Give HW time to discard old tx messages */
+ usleep_range(1000, 1000);
+
+ /* Clean all ETH MACs */
+ rc = bnx2x_del_all_macs(bp, &bp->fp[0].mac_obj, BNX2X_ETH_MAC, false);
+ if (rc < 0)
+ BNX2X_ERR("Failed to delete all ETH macs: %d\n", rc);
+
+ /* Clean up UC list */
+ rc = bnx2x_del_all_macs(bp, &bp->fp[0].mac_obj, BNX2X_UC_LIST_MAC,
+ true);
+ if (rc < 0)
+ BNX2X_ERR("Failed to schedule DEL commands for UC MACs list: "
+ "%d\n", rc);
+
+ /* Disable LLH */
+ if (!CHIP_IS_E1(bp))
+ REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 0);
+
+ /* Set "drop all" (stop Rx).
+ * We need to take a netif_addr_lock() here in order to prevent
+ * a race between the completion code and this code.
+ */
+ netif_addr_lock_bh(bp->dev);
+ /* Schedule the rx_mode command */
+ if (test_bit(BNX2X_FILTER_RX_MODE_PENDING, &bp->sp_state))
+ set_bit(BNX2X_FILTER_RX_MODE_SCHED, &bp->sp_state);
+ else
+ bnx2x_set_storm_rx_mode(bp);
+
+ /* Cleanup multicast configuration */
+ rparam.mcast_obj = &bp->mcast_obj;
+ rc = bnx2x_config_mcast(bp, &rparam, BNX2X_MCAST_CMD_DEL);
+ if (rc < 0)
+ BNX2X_ERR("Failed to send DEL multicast command: %d\n", rc);
+
+ netif_addr_unlock_bh(bp->dev);
+
+
+
+ /*
+ * Send the UNLOAD_REQUEST to the MCP. This will return if
+ * this function should perform FUNC, PORT or COMMON HW
+ * reset.
+ */
+ reset_code = bnx2x_send_unload_req(bp, unload_mode);
+
+ /*
+ * (assumption: No Attention from MCP at this stage)
+ * PMF probably in the middle of TXdisable/enable transaction
+ */
+ rc = bnx2x_func_wait_started(bp);
+ if (rc) {
+ BNX2X_ERR("bnx2x_func_wait_started failed\n");
+#ifdef BNX2X_STOP_ON_ERROR
+ return;
+#endif
+ }
+
/* Close multi and leading connections
- Completions for ramrods are collected in a synchronous way */
+ * Completions for ramrods are collected in a synchronous way
+ */
for_each_queue(bp, i)
-
- if (bnx2x_stop_client(bp, i))
+ if (bnx2x_stop_queue(bp, i))
#ifdef BNX2X_STOP_ON_ERROR
return;
#else
goto unload_error;
#endif
+ /* If SP settings didn't get completed so far - something
+ * very wrong has happen.
+ */
+ if (!bnx2x_wait_sp_comp(bp, ~0x0UL))
+ BNX2X_ERR("Hmmm... Common slow path ramrods got stuck!\n");
+#ifndef BNX2X_STOP_ON_ERROR
+unload_error:
+#endif
rc = bnx2x_func_stop(bp);
if (rc) {
BNX2X_ERR("Function stop failed!\n");
#ifdef BNX2X_STOP_ON_ERROR
return;
-#else
- goto unload_error;
-#endif
- }
-#ifndef BNX2X_STOP_ON_ERROR
-unload_error:
#endif
- if (!BP_NOMCP(bp))
- reset_code = bnx2x_fw_command(bp, reset_code, 0);
- else {
- DP(NETIF_MSG_IFDOWN, "NO MCP - load counts[%d] "
- "%d, %d, %d\n", BP_PATH(bp),
- load_count[BP_PATH(bp)][0],
- load_count[BP_PATH(bp)][1],
- load_count[BP_PATH(bp)][2]);
- load_count[BP_PATH(bp)][0]--;
- load_count[BP_PATH(bp)][1 + port]--;
- DP(NETIF_MSG_IFDOWN, "NO MCP - new load counts[%d] "
- "%d, %d, %d\n", BP_PATH(bp),
- load_count[BP_PATH(bp)][0], load_count[BP_PATH(bp)][1],
- load_count[BP_PATH(bp)][2]);
- if (load_count[BP_PATH(bp)][0] == 0)
- reset_code = FW_MSG_CODE_DRV_UNLOAD_COMMON;
- else if (load_count[BP_PATH(bp)][1 + port] == 0)
- reset_code = FW_MSG_CODE_DRV_UNLOAD_PORT;
- else
- reset_code = FW_MSG_CODE_DRV_UNLOAD_FUNCTION;
}
- if ((reset_code == FW_MSG_CODE_DRV_UNLOAD_COMMON) ||
- (reset_code == FW_MSG_CODE_DRV_UNLOAD_PORT))
- bnx2x__link_reset(bp);
-
/* Disable HW interrupts, NAPI */
bnx2x_netif_stop(bp, 1);
@@ -7162,12 +7862,13 @@ unload_error:
bnx2x_free_irq(bp);
/* Reset the chip */
- bnx2x_reset_chip(bp, reset_code);
+ rc = bnx2x_reset_hw(bp, reset_code);
+ if (rc)
+ BNX2X_ERR("HW_RESET failed\n");
- /* Report UNLOAD_DONE to MCP */
- if (!BP_NOMCP(bp))
- bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_DONE, 0);
+ /* Report UNLOAD_DONE to MCP */
+ bnx2x_send_unload_done(bp);
}
void bnx2x_disable_close_the_gate(struct bnx2x *bp)
@@ -7184,7 +7885,7 @@ void bnx2x_disable_close_the_gate(struct bnx2x *bp)
val = REG_RD(bp, addr);
val &= ~(0x300);
REG_WR(bp, addr, val);
- } else if (CHIP_IS_E1H(bp)) {
+ } else {
val = REG_RD(bp, MISC_REG_AEU_GENERAL_MASK);
val &= ~(MISC_AEU_GENERAL_MASK_REG_AEU_PXP_CLOSE_MASK |
MISC_AEU_GENERAL_MASK_REG_AEU_NIG_CLOSE_MASK);
@@ -7195,24 +7896,37 @@ void bnx2x_disable_close_the_gate(struct bnx2x *bp)
/* Close gates #2, #3 and #4: */
static void bnx2x_set_234_gates(struct bnx2x *bp, bool close)
{
- u32 val, addr;
+ u32 val;
/* Gates #2 and #4a are closed/opened for "not E1" only */
if (!CHIP_IS_E1(bp)) {
/* #4 */
- val = REG_RD(bp, PXP_REG_HST_DISCARD_DOORBELLS);
- REG_WR(bp, PXP_REG_HST_DISCARD_DOORBELLS,
- close ? (val | 0x1) : (val & (~(u32)1)));
+ REG_WR(bp, PXP_REG_HST_DISCARD_DOORBELLS, !!close);
/* #2 */
- val = REG_RD(bp, PXP_REG_HST_DISCARD_INTERNAL_WRITES);
- REG_WR(bp, PXP_REG_HST_DISCARD_INTERNAL_WRITES,
- close ? (val | 0x1) : (val & (~(u32)1)));
+ REG_WR(bp, PXP_REG_HST_DISCARD_INTERNAL_WRITES, !!close);
}
/* #3 */
- addr = BP_PORT(bp) ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0;
- val = REG_RD(bp, addr);
- REG_WR(bp, addr, (!close) ? (val | 0x1) : (val & (~(u32)1)));
+ if (CHIP_IS_E1x(bp)) {
+ /* Prevent interrupts from HC on both ports */
+ val = REG_RD(bp, HC_REG_CONFIG_1);
+ REG_WR(bp, HC_REG_CONFIG_1,
+ (!close) ? (val | HC_CONFIG_1_REG_BLOCK_DISABLE_1) :
+ (val & ~(u32)HC_CONFIG_1_REG_BLOCK_DISABLE_1));
+
+ val = REG_RD(bp, HC_REG_CONFIG_0);
+ REG_WR(bp, HC_REG_CONFIG_0,
+ (!close) ? (val | HC_CONFIG_0_REG_BLOCK_DISABLE_0) :
+ (val & ~(u32)HC_CONFIG_0_REG_BLOCK_DISABLE_0));
+ } else {
+ /* Prevent incomming interrupts in IGU */
+ val = REG_RD(bp, IGU_REG_BLOCK_CONFIGURATION);
+
+ REG_WR(bp, IGU_REG_BLOCK_CONFIGURATION,
+ (!close) ?
+ (val | IGU_BLOCK_CONFIGURATION_REG_BLOCK_ENABLE) :
+ (val & ~(u32)IGU_BLOCK_CONFIGURATION_REG_BLOCK_ENABLE));
+ }
DP(NETIF_MSG_HW, "%s gates #2, #3 and #4\n",
close ? "closing" : "opening");
@@ -7330,7 +8044,6 @@ static void bnx2x_pxp_prep(struct bnx2x *bp)
if (!CHIP_IS_E1(bp)) {
REG_WR(bp, PXP2_REG_RD_START_INIT, 0);
REG_WR(bp, PXP2_REG_RQ_RBC_DONE, 0);
- REG_WR(bp, PXP2_REG_RQ_CFG_DONE, 0);
mmiowb();
}
}
@@ -7345,46 +8058,133 @@ static void bnx2x_pxp_prep(struct bnx2x *bp)
* - GRC
* - RBCN, RBCP
*/
-static void bnx2x_process_kill_chip_reset(struct bnx2x *bp)
+static void bnx2x_process_kill_chip_reset(struct bnx2x *bp, bool global)
{
u32 not_reset_mask1, reset_mask1, not_reset_mask2, reset_mask2;
+ u32 global_bits2, stay_reset2;
+
+ /*
+ * Bits that have to be set in reset_mask2 if we want to reset 'global'
+ * (per chip) blocks.
+ */
+ global_bits2 =
+ MISC_REGISTERS_RESET_REG_2_RST_MCP_N_RESET_CMN_CPU |
+ MISC_REGISTERS_RESET_REG_2_RST_MCP_N_RESET_CMN_CORE;
+ /* Don't reset the following blocks */
not_reset_mask1 =
MISC_REGISTERS_RESET_REG_1_RST_HC |
MISC_REGISTERS_RESET_REG_1_RST_PXPV |
MISC_REGISTERS_RESET_REG_1_RST_PXP;
not_reset_mask2 =
- MISC_REGISTERS_RESET_REG_2_RST_MDIO |
+ MISC_REGISTERS_RESET_REG_2_RST_PCI_MDIO |
MISC_REGISTERS_RESET_REG_2_RST_EMAC0_HARD_CORE |
MISC_REGISTERS_RESET_REG_2_RST_EMAC1_HARD_CORE |
MISC_REGISTERS_RESET_REG_2_RST_MISC_CORE |
MISC_REGISTERS_RESET_REG_2_RST_RBCN |
MISC_REGISTERS_RESET_REG_2_RST_GRC |
MISC_REGISTERS_RESET_REG_2_RST_MCP_N_RESET_REG_HARD_CORE |
- MISC_REGISTERS_RESET_REG_2_RST_MCP_N_HARD_CORE_RST_B;
+ MISC_REGISTERS_RESET_REG_2_RST_MCP_N_HARD_CORE_RST_B |
+ MISC_REGISTERS_RESET_REG_2_RST_ATC |
+ MISC_REGISTERS_RESET_REG_2_PGLC;
+ /*
+ * Keep the following blocks in reset:
+ * - all xxMACs are handled by the bnx2x_link code.
+ */
+ stay_reset2 =
+ MISC_REGISTERS_RESET_REG_2_RST_BMAC0 |
+ MISC_REGISTERS_RESET_REG_2_RST_BMAC1 |
+ MISC_REGISTERS_RESET_REG_2_RST_EMAC0 |
+ MISC_REGISTERS_RESET_REG_2_RST_EMAC1 |
+ MISC_REGISTERS_RESET_REG_2_UMAC0 |
+ MISC_REGISTERS_RESET_REG_2_UMAC1 |
+ MISC_REGISTERS_RESET_REG_2_XMAC |
+ MISC_REGISTERS_RESET_REG_2_XMAC_SOFT;
+
+ /* Full reset masks according to the chip */
reset_mask1 = 0xffffffff;
if (CHIP_IS_E1(bp))
reset_mask2 = 0xffff;
- else
+ else if (CHIP_IS_E1H(bp))
reset_mask2 = 0x1ffff;
+ else if (CHIP_IS_E2(bp))
+ reset_mask2 = 0xfffff;
+ else /* CHIP_IS_E3 */
+ reset_mask2 = 0x3ffffff;
- REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR,
- reset_mask1 & (~not_reset_mask1));
+ /* Don't reset global blocks unless we need to */
+ if (!global)
+ reset_mask2 &= ~global_bits2;
+
+ /*
+ * In case of attention in the QM, we need to reset PXP
+ * (MISC_REGISTERS_RESET_REG_2_RST_PXP_RQ_RD_WR) before QM
+ * because otherwise QM reset would release 'close the gates' shortly
+ * before resetting the PXP, then the PSWRQ would send a write
+ * request to PGLUE. Then when PXP is reset, PGLUE would try to
+ * read the payload data from PSWWR, but PSWWR would not
+ * respond. The write queue in PGLUE would stuck, dmae commands
+ * would not return. Therefore it's important to reset the second
+ * reset register (containing the
+ * MISC_REGISTERS_RESET_REG_2_RST_PXP_RQ_RD_WR bit) before the
+ * first one (containing the MISC_REGISTERS_RESET_REG_1_RST_QM
+ * bit).
+ */
REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
reset_mask2 & (~not_reset_mask2));
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR,
+ reset_mask1 & (~not_reset_mask1));
+
+ barrier();
+ mmiowb();
+
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET,
+ reset_mask2 & (~stay_reset2));
+
barrier();
mmiowb();
REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, reset_mask1);
- REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, reset_mask2);
mmiowb();
}
-static int bnx2x_process_kill(struct bnx2x *bp)
+/**
+ * bnx2x_er_poll_igu_vq - poll for pending writes bit.
+ * It should get cleared in no more than 1s.
+ *
+ * @bp: driver handle
+ *
+ * It should get cleared in no more than 1s. Returns 0 if
+ * pending writes bit gets cleared.
+ */
+static int bnx2x_er_poll_igu_vq(struct bnx2x *bp)
+{
+ u32 cnt = 1000;
+ u32 pend_bits = 0;
+
+ do {
+ pend_bits = REG_RD(bp, IGU_REG_PENDING_BITS_STATUS);
+
+ if (pend_bits == 0)
+ break;
+
+ usleep_range(1000, 1000);
+ } while (cnt-- > 0);
+
+ if (cnt <= 0) {
+ BNX2X_ERR("Still pending IGU requests pend_bits=%x!\n",
+ pend_bits);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int bnx2x_process_kill(struct bnx2x *bp, bool global)
{
int cnt = 1000;
u32 val = 0;
@@ -7403,7 +8203,7 @@ static int bnx2x_process_kill(struct bnx2x *bp)
((port_is_idle_1 & 0x1) == 0x1) &&
(pgl_exp_rom2 == 0xffffffff))
break;
- msleep(1);
+ usleep_range(1000, 1000);
} while (cnt-- > 0);
if (cnt <= 0) {
@@ -7423,6 +8223,11 @@ static int bnx2x_process_kill(struct bnx2x *bp)
/* Close gates #2, #3 and #4 */
bnx2x_set_234_gates(bp, true);
+ /* Poll for IGU VQs for 57712 and newer chips */
+ if (!CHIP_IS_E1x(bp) && bnx2x_er_poll_igu_vq(bp))
+ return -EAGAIN;
+
+
/* TBD: Indicate that "process kill" is in progress to MCP */
/* Clear "unprepared" bit */
@@ -7435,25 +8240,28 @@ static int bnx2x_process_kill(struct bnx2x *bp)
/* Wait for 1ms to empty GLUE and PCI-E core queues,
* PSWHST, GRC and PSWRD Tetris buffer.
*/
- msleep(1);
+ usleep_range(1000, 1000);
/* Prepare to chip reset: */
/* MCP */
- bnx2x_reset_mcp_prep(bp, &val);
+ if (global)
+ bnx2x_reset_mcp_prep(bp, &val);
/* PXP */
bnx2x_pxp_prep(bp);
barrier();
/* reset the chip */
- bnx2x_process_kill_chip_reset(bp);
+ bnx2x_process_kill_chip_reset(bp, global);
barrier();
/* Recover after reset: */
/* MCP */
- if (bnx2x_reset_mcp_comp(bp, val))
+ if (global && bnx2x_reset_mcp_comp(bp, val))
return -EAGAIN;
+ /* TBD: Add resetting the NO_MCP mode DB here */
+
/* PXP */
bnx2x_pxp_prep(bp);
@@ -7466,43 +8274,85 @@ static int bnx2x_process_kill(struct bnx2x *bp)
return 0;
}
-static int bnx2x_leader_reset(struct bnx2x *bp)
+int bnx2x_leader_reset(struct bnx2x *bp)
{
int rc = 0;
+ bool global = bnx2x_reset_is_global(bp);
+
/* Try to recover after the failure */
- if (bnx2x_process_kill(bp)) {
- printk(KERN_ERR "%s: Something bad had happen! Aii!\n",
- bp->dev->name);
+ if (bnx2x_process_kill(bp, global)) {
+ netdev_err(bp->dev, "Something bad had happen on engine %d! "
+ "Aii!\n", BP_PATH(bp));
rc = -EAGAIN;
goto exit_leader_reset;
}
- /* Clear "reset is in progress" bit and update the driver state */
+ /*
+ * Clear RESET_IN_PROGRES and RESET_GLOBAL bits and update the driver
+ * state.
+ */
bnx2x_set_reset_done(bp);
- bp->recovery_state = BNX2X_RECOVERY_DONE;
+ if (global)
+ bnx2x_clear_reset_global(bp);
exit_leader_reset:
bp->is_leader = 0;
- bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_RESERVED_08);
- smp_wmb();
+ bnx2x_release_leader_lock(bp);
+ smp_mb();
return rc;
}
-/* Assumption: runs under rtnl lock. This together with the fact
- * that it's called only from bnx2x_reset_task() ensure that it
+static inline void bnx2x_recovery_failed(struct bnx2x *bp)
+{
+ netdev_err(bp->dev, "Recovery has failed. Power cycle is needed.\n");
+
+ /* Disconnect this device */
+ netif_device_detach(bp->dev);
+
+ /*
+ * Block ifup for all function on this engine until "process kill"
+ * or power cycle.
+ */
+ bnx2x_set_reset_in_progress(bp);
+
+ /* Shut down the power */
+ bnx2x_set_power_state(bp, PCI_D3hot);
+
+ bp->recovery_state = BNX2X_RECOVERY_FAILED;
+
+ smp_mb();
+}
+
+/*
+ * Assumption: runs under rtnl lock. This together with the fact
+ * that it's called only from bnx2x_sp_rtnl() ensure that it
* will never be called when netif_running(bp->dev) is false.
*/
static void bnx2x_parity_recover(struct bnx2x *bp)
{
+ bool global = false;
+
DP(NETIF_MSG_HW, "Handling parity\n");
while (1) {
switch (bp->recovery_state) {
case BNX2X_RECOVERY_INIT:
DP(NETIF_MSG_HW, "State is BNX2X_RECOVERY_INIT\n");
+ bnx2x_chk_parity_attn(bp, &global, false);
+
/* Try to get a LEADER_LOCK HW lock */
- if (bnx2x_trylock_hw_lock(bp,
- HW_LOCK_RESOURCE_RESERVED_08))
+ if (bnx2x_trylock_leader_lock(bp)) {
+ bnx2x_set_reset_in_progress(bp);
+ /*
+ * Check if there is a global attention and if
+ * there was a global attention, set the global
+ * reset bit.
+ */
+
+ if (global)
+ bnx2x_set_reset_global(bp);
+
bp->is_leader = 1;
+ }
/* Stop the driver */
/* If interface has been removed - break */
@@ -7510,21 +8360,47 @@ static void bnx2x_parity_recover(struct bnx2x *bp)
return;
bp->recovery_state = BNX2X_RECOVERY_WAIT;
- /* Ensure "is_leader" and "recovery_state"
- * update values are seen on other CPUs
+
+ /*
+ * Reset MCP command sequence number and MCP mail box
+ * sequence as we are going to reset the MCP.
*/
- smp_wmb();
+ if (global) {
+ bp->fw_seq = 0;
+ bp->fw_drv_pulse_wr_seq = 0;
+ }
+
+ /* Ensure "is_leader", MCP command sequence and
+ * "recovery_state" update values are seen on other
+ * CPUs.
+ */
+ smp_mb();
break;
case BNX2X_RECOVERY_WAIT:
DP(NETIF_MSG_HW, "State is BNX2X_RECOVERY_WAIT\n");
if (bp->is_leader) {
- u32 load_counter = bnx2x_get_load_cnt(bp);
- if (load_counter) {
+ int other_engine = BP_PATH(bp) ? 0 : 1;
+ u32 other_load_counter =
+ bnx2x_get_load_cnt(bp, other_engine);
+ u32 load_counter =
+ bnx2x_get_load_cnt(bp, BP_PATH(bp));
+ global = bnx2x_reset_is_global(bp);
+
+ /*
+ * In case of a parity in a global block, let
+ * the first leader that performs a
+ * leader_reset() reset the global blocks in
+ * order to clear global attentions. Otherwise
+ * the the gates will remain closed for that
+ * engine.
+ */
+ if (load_counter ||
+ (global && other_load_counter)) {
/* Wait until all other functions get
* down.
*/
- schedule_delayed_work(&bp->reset_task,
+ schedule_delayed_work(&bp->sp_rtnl_task,
HZ/10);
return;
} else {
@@ -7533,37 +8409,27 @@ static void bnx2x_parity_recover(struct bnx2x *bp)
* normal. In any case it's an exit
* point for a leader.
*/
- if (bnx2x_leader_reset(bp) ||
- bnx2x_nic_load(bp, LOAD_NORMAL)) {
- printk(KERN_ERR"%s: Recovery "
- "has failed. Power cycle is "
- "needed.\n", bp->dev->name);
- /* Disconnect this device */
- netif_device_detach(bp->dev);
- /* Block ifup for all function
- * of this ASIC until
- * "process kill" or power
- * cycle.
- */
- bnx2x_set_reset_in_progress(bp);
- /* Shut down the power */
- bnx2x_set_power_state(bp,
- PCI_D3hot);
+ if (bnx2x_leader_reset(bp)) {
+ bnx2x_recovery_failed(bp);
return;
}
- return;
+ /* If we are here, means that the
+ * leader has succeeded and doesn't
+ * want to be a leader any more. Try
+ * to continue as a none-leader.
+ */
+ break;
}
} else { /* non-leader */
- if (!bnx2x_reset_is_done(bp)) {
+ if (!bnx2x_reset_is_done(bp, BP_PATH(bp))) {
/* Try to get a LEADER_LOCK HW lock as
* long as a former leader may have
* been unloaded by the user or
* released a leadership by another
* reason.
*/
- if (bnx2x_trylock_hw_lock(bp,
- HW_LOCK_RESOURCE_RESERVED_08)) {
+ if (bnx2x_trylock_leader_lock(bp)) {
/* I'm a leader now! Restart a
* switch case.
*/
@@ -7571,18 +8437,30 @@ static void bnx2x_parity_recover(struct bnx2x *bp)
break;
}
- schedule_delayed_work(&bp->reset_task,
+ schedule_delayed_work(&bp->sp_rtnl_task,
HZ/10);
return;
- } else { /* A leader has completed
- * the "process kill". It's an exit
- * point for a non-leader.
- */
- bnx2x_nic_load(bp, LOAD_NORMAL);
- bp->recovery_state =
- BNX2X_RECOVERY_DONE;
- smp_wmb();
+ } else {
+ /*
+ * If there was a global attention, wait
+ * for it to be cleared.
+ */
+ if (bnx2x_reset_is_global(bp)) {
+ schedule_delayed_work(
+ &bp->sp_rtnl_task,
+ HZ/10);
+ return;
+ }
+
+ if (bnx2x_nic_load(bp, LOAD_NORMAL))
+ bnx2x_recovery_failed(bp);
+ else {
+ bp->recovery_state =
+ BNX2X_RECOVERY_DONE;
+ smp_mb();
+ }
+
return;
}
}
@@ -7595,35 +8473,92 @@ static void bnx2x_parity_recover(struct bnx2x *bp)
/* bnx2x_nic_unload() flushes the bnx2x_wq, thus reset task is
* scheduled on a general queue in order to prevent a dead lock.
*/
-static void bnx2x_reset_task(struct work_struct *work)
+static void bnx2x_sp_rtnl_task(struct work_struct *work)
{
- struct bnx2x *bp = container_of(work, struct bnx2x, reset_task.work);
-
-#ifdef BNX2X_STOP_ON_ERROR
- BNX2X_ERR("reset task called but STOP_ON_ERROR defined"
- " so reset not done to allow debug dump,\n"
- KERN_ERR " you will need to reboot when done\n");
- return;
-#endif
+ struct bnx2x *bp = container_of(work, struct bnx2x, sp_rtnl_task.work);
rtnl_lock();
if (!netif_running(bp->dev))
- goto reset_task_exit;
+ goto sp_rtnl_exit;
+
+ /* if stop on error is defined no recovery flows should be executed */
+#ifdef BNX2X_STOP_ON_ERROR
+ BNX2X_ERR("recovery flow called but STOP_ON_ERROR defined "
+ "so reset not done to allow debug dump,\n"
+ "you will need to reboot when done\n");
+ goto sp_rtnl_not_reset;
+#endif
+
+ if (unlikely(bp->recovery_state != BNX2X_RECOVERY_DONE)) {
+ /*
+ * Clear all pending SP commands as we are going to reset the
+ * function anyway.
+ */
+ bp->sp_rtnl_state = 0;
+ smp_mb();
- if (unlikely(bp->recovery_state != BNX2X_RECOVERY_DONE))
bnx2x_parity_recover(bp);
- else {
+
+ goto sp_rtnl_exit;
+ }
+
+ if (test_and_clear_bit(BNX2X_SP_RTNL_TX_TIMEOUT, &bp->sp_rtnl_state)) {
+ /*
+ * Clear all pending SP commands as we are going to reset the
+ * function anyway.
+ */
+ bp->sp_rtnl_state = 0;
+ smp_mb();
+
bnx2x_nic_unload(bp, UNLOAD_NORMAL);
bnx2x_nic_load(bp, LOAD_NORMAL);
+
+ goto sp_rtnl_exit;
}
+#ifdef BNX2X_STOP_ON_ERROR
+sp_rtnl_not_reset:
+#endif
+ if (test_and_clear_bit(BNX2X_SP_RTNL_SETUP_TC, &bp->sp_rtnl_state))
+ bnx2x_setup_tc(bp->dev, bp->dcbx_port_params.ets.num_of_cos);
-reset_task_exit:
+sp_rtnl_exit:
rtnl_unlock();
}
/* end of nic load/unload */
+static void bnx2x_period_task(struct work_struct *work)
+{
+ struct bnx2x *bp = container_of(work, struct bnx2x, period_task.work);
+
+ if (!netif_running(bp->dev))
+ goto period_task_exit;
+
+ if (CHIP_REV_IS_SLOW(bp)) {
+ BNX2X_ERR("period task called on emulation, ignoring\n");
+ goto period_task_exit;
+ }
+
+ bnx2x_acquire_phy_lock(bp);
+ /*
+ * The barrier is needed to ensure the ordering between the writing to
+ * the bp->port.pmf in the bnx2x_nic_load() or bnx2x_pmf_update() and
+ * the reading here.
+ */
+ smp_mb();
+ if (bp->port.pmf) {
+ bnx2x_period_func(&bp->link_params, &bp->link_vars);
+
+ /* Re-queue task in 1 sec */
+ queue_delayed_work(bnx2x_wq, &bp->period_task, 1*HZ);
+ }
+
+ bnx2x_release_phy_lock(bp);
+period_task_exit:
+ return;
+}
+
/*
* Init service functions
*/
@@ -7672,17 +8607,19 @@ static void __devinit bnx2x_undi_unload(struct bnx2x *bp)
/* Check if there is any driver already loaded */
val = REG_RD(bp, MISC_REG_UNPREPARED);
if (val == 0x1) {
- /* Check if it is the UNDI driver
+
+ bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_RESET);
+ /*
+ * Check if it is the UNDI driver
* UNDI driver initializes CID offset for normal bell to 0x7
*/
- bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_UNDI);
val = REG_RD(bp, DORQ_REG_NORM_CID_OFST);
if (val == 0x7) {
u32 reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS;
/* save our pf_num */
int orig_pf_num = bp->pf_num;
- u32 swap_en;
- u32 swap_val;
+ int port;
+ u32 swap_en, swap_val, value;
/* clear the UNDI indication */
REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0);
@@ -7713,25 +8650,20 @@ static void __devinit bnx2x_undi_unload(struct bnx2x *bp)
bnx2x_fw_command(bp, reset_code, 0);
}
- /* now it's safe to release the lock */
- bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_UNDI);
-
bnx2x_undi_int_disable(bp);
+ port = BP_PORT(bp);
/* close input traffic and wait for it */
/* Do not rcv packets to BRB */
- REG_WR(bp,
- (BP_PORT(bp) ? NIG_REG_LLH1_BRB1_DRV_MASK :
- NIG_REG_LLH0_BRB1_DRV_MASK), 0x0);
+ REG_WR(bp, (port ? NIG_REG_LLH1_BRB1_DRV_MASK :
+ NIG_REG_LLH0_BRB1_DRV_MASK), 0x0);
/* Do not direct rcv packets that are not for MCP to
* the BRB */
- REG_WR(bp,
- (BP_PORT(bp) ? NIG_REG_LLH1_BRB1_NOT_MCP :
- NIG_REG_LLH0_BRB1_NOT_MCP), 0x0);
+ REG_WR(bp, (port ? NIG_REG_LLH1_BRB1_NOT_MCP :
+ NIG_REG_LLH0_BRB1_NOT_MCP), 0x0);
/* clear AEU */
- REG_WR(bp,
- (BP_PORT(bp) ? MISC_REG_AEU_MASK_ATTN_FUNC_1 :
- MISC_REG_AEU_MASK_ATTN_FUNC_0), 0);
+ REG_WR(bp, (port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 :
+ MISC_REG_AEU_MASK_ATTN_FUNC_0), 0);
msleep(10);
/* save NIG port swap info */
@@ -7741,9 +8673,17 @@ static void __devinit bnx2x_undi_unload(struct bnx2x *bp)
REG_WR(bp,
GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR,
0xd3ffffff);
+
+ value = 0x1400;
+ if (CHIP_IS_E3(bp)) {
+ value |= MISC_REGISTERS_RESET_REG_2_MSTAT0;
+ value |= MISC_REGISTERS_RESET_REG_2_MSTAT1;
+ }
+
REG_WR(bp,
GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
- 0x1403);
+ value);
+
/* take the NIG out of reset and restore swap values */
REG_WR(bp,
GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET,
@@ -7759,8 +8699,10 @@ static void __devinit bnx2x_undi_unload(struct bnx2x *bp)
bp->fw_seq =
(SHMEM_RD(bp, func_mb[bp->pf_num].drv_mb_header) &
DRV_MSG_SEQ_NUMBER_MASK);
- } else
- bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_UNDI);
+ }
+
+ /* now it's safe to release the lock */
+ bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_RESET);
}
}
@@ -7784,7 +8726,7 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp)
/* Set doorbell size */
bp->db_size = (1 << BNX2X_DB_SHIFT);
- if (CHIP_IS_E2(bp)) {
+ if (!CHIP_IS_E1x(bp)) {
val = REG_RD(bp, MISC_REG_PORT4MODE_EN_OVWR);
if ((val & 1) == 0)
val = REG_RD(bp, MISC_REG_PORT4MODE_EN);
@@ -7804,16 +8746,6 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp)
bp->pfid = bp->pf_num; /* 0..7 */
}
- /*
- * set base FW non-default (fast path) status block id, this value is
- * used to initialize the fw_sb_id saved on the fp/queue structure to
- * determine the id used by the FW.
- */
- if (CHIP_IS_E1x(bp))
- bp->base_fw_ndsb = BP_PORT(bp) * FP_SB_MAX_E1x;
- else /* E2 */
- bp->base_fw_ndsb = BP_PORT(bp) * FP_SB_MAX_E2;
-
bp->link_params.chip_id = bp->common.chip_id;
BNX2X_DEV_INFO("chip ID is 0x%x\n", id);
@@ -7825,13 +8757,15 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp)
}
val = REG_RD(bp, MCP_REG_MCPR_NVM_CFG4);
- bp->common.flash_size = (NVRAM_1MB_SIZE <<
+ bp->common.flash_size = (BNX2X_NVRAM_1MB_SIZE <<
(val & MCPR_NVM_CFG4_FLASH_SIZE));
BNX2X_DEV_INFO("flash_size 0x%x (%d)\n",
bp->common.flash_size, bp->common.flash_size);
bnx2x_init_shmem(bp);
+
+
bp->common.shmem2_base = REG_RD(bp, (BP_PATH(bp) ?
MISC_REG_GENERIC_CR_1 :
MISC_REG_GENERIC_CR_0));
@@ -7880,6 +8814,10 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp)
(val >= REQ_BC_VER_4_VRFY_SPECIFIC_PHY_OPT_MDL) ?
FEATURE_CONFIG_BC_SUPPORTS_DUAL_PHY_OPT_MDL_VRFY : 0;
+ bp->link_params.feature_config_flags |=
+ (val >= REQ_BC_VER_4_SFP_TX_DISABLE_SUPPORTED) ?
+ FEATURE_CONFIG_BC_SUPPORTS_SFP_TX_DISABLED : 0;
+
pci_read_config_word(bp->pdev, bp->pm_cap + PCI_PM_PMC, &pmc);
bp->flags |= (pmc & PCI_PM_CAP_PME_D3cold) ? 0 : NO_WOL_FLAG;
@@ -7901,17 +8839,14 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp)
static void __devinit bnx2x_get_igu_cam_info(struct bnx2x *bp)
{
int pfid = BP_FUNC(bp);
- int vn = BP_E1HVN(bp);
int igu_sb_id;
u32 val;
- u8 fid;
+ u8 fid, igu_sb_cnt = 0;
bp->igu_base_sb = 0xff;
- bp->igu_sb_cnt = 0;
if (CHIP_INT_MODE_IS_BC(bp)) {
- bp->igu_sb_cnt = min_t(u8, FP_SB_MAX_E1x,
- NUM_IGU_SB_REQUIRED(bp->l2_cid_count));
-
+ int vn = BP_VN(bp);
+ igu_sb_cnt = bp->igu_sb_cnt;
bp->igu_base_sb = (CHIP_MODE_IS_4_PORT(bp) ? pfid : vn) *
FP_SB_MAX_E1x;
@@ -7937,13 +8872,21 @@ static void __devinit bnx2x_get_igu_cam_info(struct bnx2x *bp)
else {
if (bp->igu_base_sb == 0xff)
bp->igu_base_sb = igu_sb_id;
- bp->igu_sb_cnt++;
+ igu_sb_cnt++;
}
}
}
- bp->igu_sb_cnt = min_t(u8, bp->igu_sb_cnt,
- NUM_IGU_SB_REQUIRED(bp->l2_cid_count));
- if (bp->igu_sb_cnt == 0)
+
+#ifdef CONFIG_PCI_MSI
+ /*
+ * It's expected that number of CAM entries for this functions is equal
+ * to the number evaluated based on the MSI-X table size. We want a
+ * harsh warning if these values are different!
+ */
+ WARN_ON(bp->igu_sb_cnt != igu_sb_cnt);
+#endif
+
+ if (igu_sb_cnt == 0)
BNX2X_ERR("CAM configuration error\n");
}
@@ -7991,24 +8934,25 @@ static void __devinit bnx2x_link_settings_supported(struct bnx2x *bp,
return;
}
- switch (switch_cfg) {
- case SWITCH_CFG_1G:
- bp->port.phy_addr = REG_RD(bp, NIG_REG_SERDES0_CTRL_PHY_ADDR +
- port*0x10);
- BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->port.phy_addr);
- break;
-
- case SWITCH_CFG_10G:
- bp->port.phy_addr = REG_RD(bp, NIG_REG_XGXS0_CTRL_PHY_ADDR +
- port*0x18);
- BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->port.phy_addr);
- break;
-
- default:
- BNX2X_ERR("BAD switch_cfg link_config 0x%x\n",
- bp->port.link_config[0]);
- return;
+ if (CHIP_IS_E3(bp))
+ bp->port.phy_addr = REG_RD(bp, MISC_REG_WC0_CTRL_PHY_ADDR);
+ else {
+ switch (switch_cfg) {
+ case SWITCH_CFG_1G:
+ bp->port.phy_addr = REG_RD(
+ bp, NIG_REG_SERDES0_CTRL_PHY_ADDR + port*0x10);
+ break;
+ case SWITCH_CFG_10G:
+ bp->port.phy_addr = REG_RD(
+ bp, NIG_REG_XGXS0_CTRL_PHY_ADDR + port*0x18);
+ break;
+ default:
+ BNX2X_ERR("BAD switch_cfg link_config 0x%x\n",
+ bp->port.link_config[0]);
+ return;
+ }
}
+ BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->port.phy_addr);
/* mask what we support according to speed_cap_mask per configuration */
for (idx = 0; idx < cfg_size; idx++) {
if (!(bp->link_params.speed_cap_mask[idx] &
@@ -8089,7 +9033,7 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
(ADVERTISED_10baseT_Full |
ADVERTISED_TP);
} else {
- BNX2X_ERROR("NVRAM config error. "
+ BNX2X_ERR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
@@ -8108,7 +9052,7 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
(ADVERTISED_10baseT_Half |
ADVERTISED_TP);
} else {
- BNX2X_ERROR("NVRAM config error. "
+ BNX2X_ERR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
@@ -8126,7 +9070,7 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
(ADVERTISED_100baseT_Full |
ADVERTISED_TP);
} else {
- BNX2X_ERROR("NVRAM config error. "
+ BNX2X_ERR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
@@ -8146,7 +9090,7 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
(ADVERTISED_100baseT_Half |
ADVERTISED_TP);
} else {
- BNX2X_ERROR("NVRAM config error. "
+ BNX2X_ERR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
@@ -8164,7 +9108,7 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
(ADVERTISED_1000baseT_Full |
ADVERTISED_TP);
} else {
- BNX2X_ERROR("NVRAM config error. "
+ BNX2X_ERR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
@@ -8182,7 +9126,7 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
(ADVERTISED_2500baseX_Full |
ADVERTISED_TP);
} else {
- BNX2X_ERROR("NVRAM config error. "
+ BNX2X_ERR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
@@ -8192,8 +9136,6 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
break;
case PORT_FEATURE_LINK_SPEED_10G_CX4:
- case PORT_FEATURE_LINK_SPEED_10G_KX4:
- case PORT_FEATURE_LINK_SPEED_10G_KR:
if (bp->port.supported[idx] &
SUPPORTED_10000baseT_Full) {
bp->link_params.req_line_speed[idx] =
@@ -8202,7 +9144,7 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
(ADVERTISED_10000baseT_Full |
ADVERTISED_FIBRE);
} else {
- BNX2X_ERROR("NVRAM config error. "
+ BNX2X_ERR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
@@ -8210,11 +9152,14 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
return;
}
break;
+ case PORT_FEATURE_LINK_SPEED_20G:
+ bp->link_params.req_line_speed[idx] = SPEED_20000;
+ break;
default:
- BNX2X_ERROR("NVRAM config error. "
- "BAD link speed link_config 0x%x\n",
- link_config);
+ BNX2X_ERR("NVRAM config error. "
+ "BAD link speed link_config 0x%x\n",
+ link_config);
bp->link_params.req_line_speed[idx] =
SPEED_AUTO_NEG;
bp->port.advertising[idx] =
@@ -8325,10 +9270,13 @@ static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp)
#ifdef BCM_CNIC
static void __devinit bnx2x_get_cnic_info(struct bnx2x *bp)
{
+ int port = BP_PORT(bp);
+ int func = BP_ABS_FUNC(bp);
+
u32 max_iscsi_conn = FW_ENCODE_32BIT_PATTERN ^ SHMEM_RD(bp,
- drv_lic_key[BP_PORT(bp)].max_iscsi_conn);
+ drv_lic_key[port].max_iscsi_conn);
u32 max_fcoe_conn = FW_ENCODE_32BIT_PATTERN ^ SHMEM_RD(bp,
- drv_lic_key[BP_PORT(bp)].max_fcoe_conn);
+ drv_lic_key[port].max_fcoe_conn);
/* Get the number of maximum allowed iSCSI and FCoE connections */
bp->cnic_eth_dev.max_iscsi_conn =
@@ -8339,11 +9287,59 @@ static void __devinit bnx2x_get_cnic_info(struct bnx2x *bp)
(max_fcoe_conn & BNX2X_MAX_FCOE_INIT_CONN_MASK) >>
BNX2X_MAX_FCOE_INIT_CONN_SHIFT;
+ /* Read the WWN: */
+ if (!IS_MF(bp)) {
+ /* Port info */
+ bp->cnic_eth_dev.fcoe_wwn_port_name_hi =
+ SHMEM_RD(bp,
+ dev_info.port_hw_config[port].
+ fcoe_wwn_port_name_upper);
+ bp->cnic_eth_dev.fcoe_wwn_port_name_lo =
+ SHMEM_RD(bp,
+ dev_info.port_hw_config[port].
+ fcoe_wwn_port_name_lower);
+
+ /* Node info */
+ bp->cnic_eth_dev.fcoe_wwn_node_name_hi =
+ SHMEM_RD(bp,
+ dev_info.port_hw_config[port].
+ fcoe_wwn_node_name_upper);
+ bp->cnic_eth_dev.fcoe_wwn_node_name_lo =
+ SHMEM_RD(bp,
+ dev_info.port_hw_config[port].
+ fcoe_wwn_node_name_lower);
+ } else if (!IS_MF_SD(bp)) {
+ u32 cfg = MF_CFG_RD(bp, func_ext_config[func].func_cfg);
+
+ /*
+ * Read the WWN info only if the FCoE feature is enabled for
+ * this function.
+ */
+ if (cfg & MACP_FUNC_CFG_FLAGS_FCOE_OFFLOAD) {
+ /* Port info */
+ bp->cnic_eth_dev.fcoe_wwn_port_name_hi =
+ MF_CFG_RD(bp, func_ext_config[func].
+ fcoe_wwn_port_name_upper);
+ bp->cnic_eth_dev.fcoe_wwn_port_name_lo =
+ MF_CFG_RD(bp, func_ext_config[func].
+ fcoe_wwn_port_name_lower);
+
+ /* Node info */
+ bp->cnic_eth_dev.fcoe_wwn_node_name_hi =
+ MF_CFG_RD(bp, func_ext_config[func].
+ fcoe_wwn_node_name_upper);
+ bp->cnic_eth_dev.fcoe_wwn_node_name_lo =
+ MF_CFG_RD(bp, func_ext_config[func].
+ fcoe_wwn_node_name_lower);
+ }
+ }
+
BNX2X_DEV_INFO("max_iscsi_conn 0x%x max_fcoe_conn 0x%x\n",
bp->cnic_eth_dev.max_iscsi_conn,
bp->cnic_eth_dev.max_fcoe_conn);
- /* If mamimum allowed number of connections is zero -
+ /*
+ * If maximum allowed number of connections is zero -
* disable the feature.
*/
if (!bp->cnic_eth_dev.max_iscsi_conn)
@@ -8364,6 +9360,9 @@ static void __devinit bnx2x_get_mac_hwinfo(struct bnx2x *bp)
u8 *fip_mac = bp->fip_mac;
#endif
+ /* Zero primary MAC configuration */
+ memset(bp->dev->dev_addr, 0, ETH_ALEN);
+
if (BP_NOMCP(bp)) {
BNX2X_ERROR("warning: random MAC workaround active\n");
random_ether_addr(bp->dev->dev_addr);
@@ -8385,9 +9384,9 @@ static void __devinit bnx2x_get_mac_hwinfo(struct bnx2x *bp)
iscsi_mac_addr_upper);
val = MF_CFG_RD(bp, func_ext_config[func].
iscsi_mac_addr_lower);
- BNX2X_DEV_INFO("Read iSCSI MAC: "
- "0x%x:0x%04x\n", val2, val);
bnx2x_set_mac_buf(iscsi_mac, val, val2);
+ BNX2X_DEV_INFO("Read iSCSI MAC: %pM\n",
+ iscsi_mac);
} else
bp->flags |= NO_ISCSI_OOO_FLAG | NO_ISCSI_FLAG;
@@ -8396,9 +9395,9 @@ static void __devinit bnx2x_get_mac_hwinfo(struct bnx2x *bp)
fcoe_mac_addr_upper);
val = MF_CFG_RD(bp, func_ext_config[func].
fcoe_mac_addr_lower);
- BNX2X_DEV_INFO("Read FCoE MAC to "
- "0x%x:0x%04x\n", val2, val);
bnx2x_set_mac_buf(fip_mac, val, val2);
+ BNX2X_DEV_INFO("Read FCoE L2 MAC to %pM\n",
+ fip_mac);
} else
bp->flags |= NO_FCOE_FLAG;
@@ -8416,6 +9415,12 @@ static void __devinit bnx2x_get_mac_hwinfo(struct bnx2x *bp)
val = SHMEM_RD(bp, dev_info.port_hw_config[port].
iscsi_mac_lower);
bnx2x_set_mac_buf(iscsi_mac, val, val2);
+
+ val2 = SHMEM_RD(bp, dev_info.port_hw_config[port].
+ fcoe_fip_mac_upper);
+ val = SHMEM_RD(bp, dev_info.port_hw_config[port].
+ fcoe_fip_mac_lower);
+ bnx2x_set_mac_buf(fip_mac, val, val2);
#endif
}
@@ -8423,13 +9428,9 @@ static void __devinit bnx2x_get_mac_hwinfo(struct bnx2x *bp)
memcpy(bp->dev->perm_addr, bp->dev->dev_addr, ETH_ALEN);
#ifdef BCM_CNIC
- /* Set the FCoE MAC in modes other then MF_SI */
- if (!CHIP_IS_E1x(bp)) {
- if (IS_MF_SD(bp))
- memcpy(fip_mac, bp->dev->dev_addr, ETH_ALEN);
- else if (!IS_MF(bp))
- memcpy(fip_mac, iscsi_mac, ETH_ALEN);
- }
+ /* Set the FCoE MAC in MF_SD mode */
+ if (!CHIP_IS_E1x(bp) && IS_MF_SD(bp))
+ memcpy(fip_mac, bp->dev->dev_addr, ETH_ALEN);
/* Disable iSCSI if MAC configuration is
* invalid.
@@ -8447,6 +9448,13 @@ static void __devinit bnx2x_get_mac_hwinfo(struct bnx2x *bp)
memset(bp->fip_mac, 0, ETH_ALEN);
}
#endif
+
+ if (!is_valid_ether_addr(bp->dev->dev_addr))
+ dev_err(&bp->pdev->dev,
+ "bad Ethernet MAC address configuration: "
+ "%pM, change it manually before bringing up "
+ "the appropriate network interface\n",
+ bp->dev->dev_addr);
}
static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp)
@@ -8458,27 +9466,71 @@ static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp)
bnx2x_get_common_hwinfo(bp);
+ /*
+ * initialize IGU parameters
+ */
if (CHIP_IS_E1x(bp)) {
bp->common.int_block = INT_BLOCK_HC;
bp->igu_dsb_id = DEF_SB_IGU_ID;
bp->igu_base_sb = 0;
- bp->igu_sb_cnt = min_t(u8, FP_SB_MAX_E1x,
- NUM_IGU_SB_REQUIRED(bp->l2_cid_count));
} else {
bp->common.int_block = INT_BLOCK_IGU;
+
+ /* do not allow device reset during IGU info preocessing */
+ bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_RESET);
+
val = REG_RD(bp, IGU_REG_BLOCK_CONFIGURATION);
+
if (val & IGU_BLOCK_CONFIGURATION_REG_BACKWARD_COMP_EN) {
- DP(NETIF_MSG_PROBE, "IGU Backward Compatible Mode\n");
+ int tout = 5000;
+
+ BNX2X_DEV_INFO("FORCING Normal Mode\n");
+
+ val &= ~(IGU_BLOCK_CONFIGURATION_REG_BACKWARD_COMP_EN);
+ REG_WR(bp, IGU_REG_BLOCK_CONFIGURATION, val);
+ REG_WR(bp, IGU_REG_RESET_MEMORIES, 0x7f);
+
+ while (tout && REG_RD(bp, IGU_REG_RESET_MEMORIES)) {
+ tout--;
+ usleep_range(1000, 1000);
+ }
+
+ if (REG_RD(bp, IGU_REG_RESET_MEMORIES)) {
+ dev_err(&bp->pdev->dev,
+ "FORCING Normal Mode failed!!!\n");
+ return -EPERM;
+ }
+ }
+
+ if (val & IGU_BLOCK_CONFIGURATION_REG_BACKWARD_COMP_EN) {
+ BNX2X_DEV_INFO("IGU Backward Compatible Mode\n");
bp->common.int_block |= INT_BLOCK_MODE_BW_COMP;
} else
- DP(NETIF_MSG_PROBE, "IGU Normal Mode\n");
+ BNX2X_DEV_INFO("IGU Normal Mode\n");
bnx2x_get_igu_cam_info(bp);
+ bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_RESET);
}
- DP(NETIF_MSG_PROBE, "igu_dsb_id %d igu_base_sb %d igu_sb_cnt %d\n",
- bp->igu_dsb_id, bp->igu_base_sb, bp->igu_sb_cnt);
+
+ /*
+ * set base FW non-default (fast path) status block id, this value is
+ * used to initialize the fw_sb_id saved on the fp/queue structure to
+ * determine the id used by the FW.
+ */
+ if (CHIP_IS_E1x(bp))
+ bp->base_fw_ndsb = BP_PORT(bp) * FP_SB_MAX_E1x + BP_L_ID(bp);
+ else /*
+ * 57712 - we currently use one FW SB per IGU SB (Rx and Tx of
+ * the same queue are indicated on the same IGU SB). So we prefer
+ * FW and IGU SBs to be the same value.
+ */
+ bp->base_fw_ndsb = bp->igu_base_sb;
+
+ BNX2X_DEV_INFO("igu_dsb_id %d igu_base_sb %d igu_sb_cnt %d\n"
+ "base_fw_ndsb %d\n", bp->igu_dsb_id, bp->igu_base_sb,
+ bp->igu_sb_cnt, bp->base_fw_ndsb);
/*
* Initialize MF configuration
@@ -8486,13 +9538,13 @@ static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp)
bp->mf_ov = 0;
bp->mf_mode = 0;
- vn = BP_E1HVN(bp);
+ vn = BP_VN(bp);
if (!CHIP_IS_E1(bp) && !BP_NOMCP(bp)) {
- DP(NETIF_MSG_PROBE,
- "shmem2base 0x%x, size %d, mfcfg offset %d\n",
- bp->common.shmem2_base, SHMEM2_RD(bp, size),
- (u32)offsetof(struct shmem2_region, mf_cfg_addr));
+ BNX2X_DEV_INFO("shmem2base 0x%x, size %d, mfcfg offset %d\n",
+ bp->common.shmem2_base, SHMEM2_RD(bp, size),
+ (u32)offsetof(struct shmem2_region, mf_cfg_addr));
+
if (SHMEM2_HAS(bp, mf_cfg_addr))
bp->common.mf_cfg_base = SHMEM2_RD(bp, mf_cfg_addr);
else
@@ -8523,8 +9575,8 @@ static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp)
bp->mf_config[vn] = MF_CFG_RD(bp,
func_mf_config[func].config);
} else
- DP(NETIF_MSG_PROBE, "illegal MAC "
- "address for SI\n");
+ BNX2X_DEV_INFO("illegal MAC address "
+ "for SI\n");
break;
case SHARED_FEAT_CFG_FORCE_SF_MODE_MF_ALLOWED:
/* get OV configuration */
@@ -8537,14 +9589,12 @@ static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp)
bp->mf_config[vn] = MF_CFG_RD(bp,
func_mf_config[func].config);
} else
- DP(NETIF_MSG_PROBE, "illegal OV for "
- "SD\n");
+ BNX2X_DEV_INFO("illegal OV for SD\n");
break;
default:
/* Unknown configuration: reset mf_config */
bp->mf_config[vn] = 0;
- DP(NETIF_MSG_PROBE, "Unknown MF mode 0x%x\n",
- val);
+ BNX2X_DEV_INFO("unkown MF mode 0x%x\n", val);
}
}
@@ -8557,13 +9607,16 @@ static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp)
FUNC_MF_CFG_E1HOV_TAG_MASK;
if (val != FUNC_MF_CFG_E1HOV_TAG_DEFAULT) {
bp->mf_ov = val;
- BNX2X_DEV_INFO("MF OV for func %d is %d"
- " (0x%04x)\n", func,
- bp->mf_ov, bp->mf_ov);
+ bp->path_has_ovlan = true;
+
+ BNX2X_DEV_INFO("MF OV for func %d is %d "
+ "(0x%04x)\n", func, bp->mf_ov,
+ bp->mf_ov);
} else {
- BNX2X_ERR("No valid MF OV for func %d,"
- " aborting\n", func);
- rc = -EPERM;
+ dev_err(&bp->pdev->dev,
+ "No valid MF OV for func %d, "
+ "aborting\n", func);
+ return -EPERM;
}
break;
case MULTI_FUNCTION_SI:
@@ -8572,36 +9625,38 @@ static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp)
break;
default:
if (vn) {
- BNX2X_ERR("VN %d in single function mode,"
- " aborting\n", vn);
- rc = -EPERM;
+ dev_err(&bp->pdev->dev,
+ "VN %d is in a single function mode, "
+ "aborting\n", vn);
+ return -EPERM;
}
break;
}
+ /* check if other port on the path needs ovlan:
+ * Since MF configuration is shared between ports
+ * Possible mixed modes are only
+ * {SF, SI} {SF, SD} {SD, SF} {SI, SF}
+ */
+ if (CHIP_MODE_IS_4_PORT(bp) &&
+ !bp->path_has_ovlan &&
+ !IS_MF(bp) &&
+ bp->common.mf_cfg_base != SHMEM_MF_CFG_ADDR_NONE) {
+ u8 other_port = !BP_PORT(bp);
+ u8 other_func = BP_PATH(bp) + 2*other_port;
+ val = MF_CFG_RD(bp,
+ func_mf_config[other_func].e1hov_tag);
+ if (val != FUNC_MF_CFG_E1HOV_TAG_DEFAULT)
+ bp->path_has_ovlan = true;
+ }
}
/* adjust igu_sb_cnt to MF for E1x */
if (CHIP_IS_E1x(bp) && IS_MF(bp))
bp->igu_sb_cnt /= E1HVN_MAX;
- /*
- * adjust E2 sb count: to be removed when FW will support
- * more then 16 L2 clients
- */
-#define MAX_L2_CLIENTS 16
- if (CHIP_IS_E2(bp))
- bp->igu_sb_cnt = min_t(u8, bp->igu_sb_cnt,
- MAX_L2_CLIENTS / (IS_MF(bp) ? 4 : 1));
-
- if (!BP_NOMCP(bp)) {
- bnx2x_get_port_hwinfo(bp);
-
- bp->fw_seq =
- (SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) &
- DRV_MSG_SEQ_NUMBER_MASK);
- BNX2X_DEV_INFO("fw_seq 0x%08x\n", bp->fw_seq);
- }
+ /* port info */
+ bnx2x_get_port_hwinfo(bp);
/* Get MAC addresses */
bnx2x_get_mac_hwinfo(bp);
@@ -8610,6 +9665,16 @@ static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp)
bnx2x_get_cnic_info(bp);
#endif
+ /* Get current FW pulse sequence */
+ if (!BP_NOMCP(bp)) {
+ int mb_idx = BP_FW_MB_IDX(bp);
+
+ bp->fw_drv_pulse_wr_seq =
+ (SHMEM_RD(bp, func_mb[mb_idx].drv_pulse_mb) &
+ DRV_PULSE_SEQ_MASK);
+ BNX2X_DEV_INFO("drv_pulse 0x%x\n", bp->fw_drv_pulse_wr_seq);
+ }
+
return rc;
}
@@ -8677,16 +9742,59 @@ out_not_found:
return;
}
+static void __devinit bnx2x_set_modes_bitmap(struct bnx2x *bp)
+{
+ u32 flags = 0;
+
+ if (CHIP_REV_IS_FPGA(bp))
+ SET_FLAGS(flags, MODE_FPGA);
+ else if (CHIP_REV_IS_EMUL(bp))
+ SET_FLAGS(flags, MODE_EMUL);
+ else
+ SET_FLAGS(flags, MODE_ASIC);
+
+ if (CHIP_MODE_IS_4_PORT(bp))
+ SET_FLAGS(flags, MODE_PORT4);
+ else
+ SET_FLAGS(flags, MODE_PORT2);
+
+ if (CHIP_IS_E2(bp))
+ SET_FLAGS(flags, MODE_E2);
+ else if (CHIP_IS_E3(bp)) {
+ SET_FLAGS(flags, MODE_E3);
+ if (CHIP_REV(bp) == CHIP_REV_Ax)
+ SET_FLAGS(flags, MODE_E3_A0);
+ else /*if (CHIP_REV(bp) == CHIP_REV_Bx)*/
+ SET_FLAGS(flags, MODE_E3_B0 | MODE_COS3);
+ }
+
+ if (IS_MF(bp)) {
+ SET_FLAGS(flags, MODE_MF);
+ switch (bp->mf_mode) {
+ case MULTI_FUNCTION_SD:
+ SET_FLAGS(flags, MODE_MF_SD);
+ break;
+ case MULTI_FUNCTION_SI:
+ SET_FLAGS(flags, MODE_MF_SI);
+ break;
+ }
+ } else
+ SET_FLAGS(flags, MODE_SF);
+
+#if defined(__LITTLE_ENDIAN)
+ SET_FLAGS(flags, MODE_LITTLE_ENDIAN);
+#else /*(__BIG_ENDIAN)*/
+ SET_FLAGS(flags, MODE_BIG_ENDIAN);
+#endif
+ INIT_MODE_FLAGS(bp) = flags;
+}
+
static int __devinit bnx2x_init_bp(struct bnx2x *bp)
{
int func;
int timer_interval;
int rc;
- /* Disable interrupt handling until HW is initialized */
- atomic_set(&bp->intr_sem, 1);
- smp_wmb(); /* Ensure that bp->intr_sem update is SMP-safe */
-
mutex_init(&bp->port.phy_mutex);
mutex_init(&bp->fw_mb_mutex);
spin_lock_init(&bp->stats_lock);
@@ -8695,12 +9803,17 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp)
#endif
INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task);
- INIT_DELAYED_WORK(&bp->reset_task, bnx2x_reset_task);
-
+ INIT_DELAYED_WORK(&bp->sp_rtnl_task, bnx2x_sp_rtnl_task);
+ INIT_DELAYED_WORK(&bp->period_task, bnx2x_period_task);
rc = bnx2x_get_hwinfo(bp);
+ if (rc)
+ return rc;
- if (!rc)
- rc = bnx2x_alloc_mem_bp(bp);
+ bnx2x_set_modes_bitmap(bp);
+
+ rc = bnx2x_alloc_mem_bp(bp);
+ if (rc)
+ return rc;
bnx2x_read_fwinfo(bp);
@@ -8710,6 +9823,14 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp)
if (!BP_NOMCP(bp))
bnx2x_undi_unload(bp);
+ /* init fw_seq after undi_unload! */
+ if (!BP_NOMCP(bp)) {
+ bp->fw_seq =
+ (SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) &
+ DRV_MSG_SEQ_NUMBER_MASK);
+ BNX2X_DEV_INFO("fw_seq 0x%08x\n", bp->fw_seq);
+ }
+
if (CHIP_REV_IS_FPGA(bp))
dev_err(&bp->pdev->dev, "FPGA detected\n");
@@ -8718,7 +9839,6 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp)
"must load devices in order!\n");
bp->multi_mode = multi_mode;
- bp->int_mode = int_mode;
/* Set TPA flags */
if (disable_tpa) {
@@ -8754,6 +9874,21 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp)
bnx2x_dcbx_set_state(bp, true, BNX2X_DCBX_ENABLED_ON_NEG_ON);
bnx2x_dcbx_init_params(bp);
+#ifdef BCM_CNIC
+ if (CHIP_IS_E1x(bp))
+ bp->cnic_base_cl_id = FP_SB_MAX_E1x;
+ else
+ bp->cnic_base_cl_id = FP_SB_MAX_E2;
+#endif
+
+ /* multiple tx priority */
+ if (CHIP_IS_E1x(bp))
+ bp->max_cos = BNX2X_MULTI_TX_COS_E1X;
+ if (CHIP_IS_E2(bp) || CHIP_IS_E3A0(bp))
+ bp->max_cos = BNX2X_MULTI_TX_COS_E2_E3A0;
+ if (CHIP_IS_E3B0(bp))
+ bp->max_cos = BNX2X_MULTI_TX_COS_E3B0;
+
return rc;
}
@@ -8762,49 +9897,70 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp)
* General service functions
****************************************************************************/
+/*
+ * net_device service functions
+ */
+
/* called with rtnl_lock */
static int bnx2x_open(struct net_device *dev)
{
struct bnx2x *bp = netdev_priv(dev);
+ bool global = false;
+ int other_engine = BP_PATH(bp) ? 0 : 1;
+ u32 other_load_counter, load_counter;
netif_carrier_off(dev);
bnx2x_set_power_state(bp, PCI_D0);
- if (!bnx2x_reset_is_done(bp)) {
+ other_load_counter = bnx2x_get_load_cnt(bp, other_engine);
+ load_counter = bnx2x_get_load_cnt(bp, BP_PATH(bp));
+
+ /*
+ * If parity had happen during the unload, then attentions
+ * and/or RECOVERY_IN_PROGRES may still be set. In this case we
+ * want the first function loaded on the current engine to
+ * complete the recovery.
+ */
+ if (!bnx2x_reset_is_done(bp, BP_PATH(bp)) ||
+ bnx2x_chk_parity_attn(bp, &global, true))
do {
- /* Reset MCP mail box sequence if there is on going
- * recovery
+ /*
+ * If there are attentions and they are in a global
+ * blocks, set the GLOBAL_RESET bit regardless whether
+ * it will be this function that will complete the
+ * recovery or not.
*/
- bp->fw_seq = 0;
+ if (global)
+ bnx2x_set_reset_global(bp);
- /* If it's the first function to load and reset done
- * is still not cleared it may mean that. We don't
- * check the attention state here because it may have
- * already been cleared by a "common" reset but we
- * shell proceed with "process kill" anyway.
+ /*
+ * Only the first function on the current engine should
+ * try to recover in open. In case of attentions in
+ * global blocks only the first in the chip should try
+ * to recover.
*/
- if ((bnx2x_get_load_cnt(bp) == 0) &&
- bnx2x_trylock_hw_lock(bp,
- HW_LOCK_RESOURCE_RESERVED_08) &&
- (!bnx2x_leader_reset(bp))) {
- DP(NETIF_MSG_HW, "Recovered in open\n");
+ if ((!load_counter &&
+ (!global || !other_load_counter)) &&
+ bnx2x_trylock_leader_lock(bp) &&
+ !bnx2x_leader_reset(bp)) {
+ netdev_info(bp->dev, "Recovered in open\n");
break;
}
+ /* recovery has failed... */
bnx2x_set_power_state(bp, PCI_D3hot);
+ bp->recovery_state = BNX2X_RECOVERY_FAILED;
- printk(KERN_ERR"%s: Recovery flow hasn't been properly"
+ netdev_err(bp->dev, "Recovery flow hasn't been properly"
" completed yet. Try again later. If u still see this"
" message after a few retries then power cycle is"
- " required.\n", bp->dev->name);
+ " required.\n");
return -EAGAIN;
} while (0);
- }
bp->recovery_state = BNX2X_RECOVERY_DONE;
-
return bnx2x_nic_load(bp, LOAD_OPEN);
}
@@ -8815,198 +9971,126 @@ static int bnx2x_close(struct net_device *dev)
/* Unload the driver, release IRQs */
bnx2x_nic_unload(bp, UNLOAD_CLOSE);
+
+ /* Power off */
bnx2x_set_power_state(bp, PCI_D3hot);
return 0;
}
-#define E1_MAX_UC_LIST 29
-#define E1H_MAX_UC_LIST 30
-#define E2_MAX_UC_LIST 14
-static inline u8 bnx2x_max_uc_list(struct bnx2x *bp)
+static inline int bnx2x_init_mcast_macs_list(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p)
{
- if (CHIP_IS_E1(bp))
- return E1_MAX_UC_LIST;
- else if (CHIP_IS_E1H(bp))
- return E1H_MAX_UC_LIST;
- else
- return E2_MAX_UC_LIST;
-}
+ int mc_count = netdev_mc_count(bp->dev);
+ struct bnx2x_mcast_list_elem *mc_mac =
+ kzalloc(sizeof(*mc_mac) * mc_count, GFP_ATOMIC);
+ struct netdev_hw_addr *ha;
+
+ if (!mc_mac)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&p->mcast_list);
+
+ netdev_for_each_mc_addr(ha, bp->dev) {
+ mc_mac->mac = bnx2x_mc_addr(ha);
+ list_add_tail(&mc_mac->link, &p->mcast_list);
+ mc_mac++;
+ }
+ p->mcast_list_len = mc_count;
-static inline u8 bnx2x_uc_list_cam_offset(struct bnx2x *bp)
+ return 0;
+}
+
+static inline void bnx2x_free_mcast_macs_list(
+ struct bnx2x_mcast_ramrod_params *p)
{
- if (CHIP_IS_E1(bp))
- /* CAM Entries for Port0:
- * 0 - prim ETH MAC
- * 1 - BCAST MAC
- * 2 - iSCSI L2 ring ETH MAC
- * 3-31 - UC MACs
- *
- * Port1 entries are allocated the same way starting from
- * entry 32.
- */
- return 3 + 32 * BP_PORT(bp);
- else if (CHIP_IS_E1H(bp)) {
- /* CAM Entries:
- * 0-7 - prim ETH MAC for each function
- * 8-15 - iSCSI L2 ring ETH MAC for each function
- * 16 till 255 UC MAC lists for each function
- *
- * Remark: There is no FCoE support for E1H, thus FCoE related
- * MACs are not considered.
- */
- return E1H_FUNC_MAX * (CAM_ISCSI_ETH_LINE + 1) +
- bnx2x_max_uc_list(bp) * BP_FUNC(bp);
- } else {
- /* CAM Entries (there is a separate CAM per engine):
- * 0-4 - prim ETH MAC for each function
- * 4-7 - iSCSI L2 ring ETH MAC for each function
- * 8-11 - FIP ucast L2 MAC for each function
- * 12-15 - ALL_ENODE_MACS mcast MAC for each function
- * 16 till 71 UC MAC lists for each function
- */
- u8 func_idx =
- (CHIP_MODE_IS_4_PORT(bp) ? BP_FUNC(bp) : BP_VN(bp));
+ struct bnx2x_mcast_list_elem *mc_mac =
+ list_first_entry(&p->mcast_list, struct bnx2x_mcast_list_elem,
+ link);
- return E2_FUNC_MAX * (CAM_MAX_PF_LINE + 1) +
- bnx2x_max_uc_list(bp) * func_idx;
- }
+ WARN_ON(!mc_mac);
+ kfree(mc_mac);
}
-/* set uc list, do not wait as wait implies sleep and
- * set_rx_mode can be invoked from non-sleepable context.
+/**
+ * bnx2x_set_uc_list - configure a new unicast MACs list.
+ *
+ * @bp: driver handle
*
- * Instead we use the same ramrod data buffer each time we need
- * to configure a list of addresses, and use the fact that the
- * list of MACs is changed in an incremental way and that the
- * function is called under the netif_addr_lock. A temporary
- * inconsistent CAM configuration (possible in case of very fast
- * sequence of add/del/add on the host side) will shortly be
- * restored by the handler of the last ramrod.
+ * We will use zero (0) as a MAC type for these MACs.
*/
-static int bnx2x_set_uc_list(struct bnx2x *bp)
+static inline int bnx2x_set_uc_list(struct bnx2x *bp)
{
- int i = 0, old;
+ int rc;
struct net_device *dev = bp->dev;
- u8 offset = bnx2x_uc_list_cam_offset(bp);
struct netdev_hw_addr *ha;
- struct mac_configuration_cmd *config_cmd = bnx2x_sp(bp, uc_mac_config);
- dma_addr_t config_cmd_map = bnx2x_sp_mapping(bp, uc_mac_config);
+ struct bnx2x_vlan_mac_obj *mac_obj = &bp->fp->mac_obj;
+ unsigned long ramrod_flags = 0;
- if (netdev_uc_count(dev) > bnx2x_max_uc_list(bp))
- return -EINVAL;
+ /* First schedule a cleanup up of old configuration */
+ rc = bnx2x_del_all_macs(bp, mac_obj, BNX2X_UC_LIST_MAC, false);
+ if (rc < 0) {
+ BNX2X_ERR("Failed to schedule DELETE operations: %d\n", rc);
+ return rc;
+ }
netdev_for_each_uc_addr(ha, dev) {
- /* copy mac */
- config_cmd->config_table[i].msb_mac_addr =
- swab16(*(u16 *)&bnx2x_uc_addr(ha)[0]);
- config_cmd->config_table[i].middle_mac_addr =
- swab16(*(u16 *)&bnx2x_uc_addr(ha)[2]);
- config_cmd->config_table[i].lsb_mac_addr =
- swab16(*(u16 *)&bnx2x_uc_addr(ha)[4]);
-
- config_cmd->config_table[i].vlan_id = 0;
- config_cmd->config_table[i].pf_id = BP_FUNC(bp);
- config_cmd->config_table[i].clients_bit_vector =
- cpu_to_le32(1 << BP_L_ID(bp));
-
- SET_FLAG(config_cmd->config_table[i].flags,
- MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
- T_ETH_MAC_COMMAND_SET);
-
- DP(NETIF_MSG_IFUP,
- "setting UCAST[%d] (%04x:%04x:%04x)\n", i,
- config_cmd->config_table[i].msb_mac_addr,
- config_cmd->config_table[i].middle_mac_addr,
- config_cmd->config_table[i].lsb_mac_addr);
-
- i++;
-
- /* Set uc MAC in NIG */
- bnx2x_set_mac_in_nig(bp, 1, bnx2x_uc_addr(ha),
- LLH_CAM_ETH_LINE + i);
- }
- old = config_cmd->hdr.length;
- if (old > i) {
- for (; i < old; i++) {
- if (CAM_IS_INVALID(config_cmd->
- config_table[i])) {
- /* already invalidated */
- break;
- }
- /* invalidate */
- SET_FLAG(config_cmd->config_table[i].flags,
- MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
- T_ETH_MAC_COMMAND_INVALIDATE);
+ rc = bnx2x_set_mac_one(bp, bnx2x_uc_addr(ha), mac_obj, true,
+ BNX2X_UC_LIST_MAC, &ramrod_flags);
+ if (rc < 0) {
+ BNX2X_ERR("Failed to schedule ADD operations: %d\n",
+ rc);
+ return rc;
}
}
- wmb();
-
- config_cmd->hdr.length = i;
- config_cmd->hdr.offset = offset;
- config_cmd->hdr.client_id = 0xff;
- /* Mark that this ramrod doesn't use bp->set_mac_pending for
- * synchronization.
- */
- config_cmd->hdr.echo = 0;
-
- mb();
-
- return bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_SET_MAC, 0,
- U64_HI(config_cmd_map), U64_LO(config_cmd_map), 1);
-
+ /* Execute the pending commands */
+ __set_bit(RAMROD_CONT, &ramrod_flags);
+ return bnx2x_set_mac_one(bp, NULL, mac_obj, false /* don't care */,
+ BNX2X_UC_LIST_MAC, &ramrod_flags);
}
-void bnx2x_invalidate_uc_list(struct bnx2x *bp)
+static inline int bnx2x_set_mc_list(struct bnx2x *bp)
{
- int i;
- struct mac_configuration_cmd *config_cmd = bnx2x_sp(bp, uc_mac_config);
- dma_addr_t config_cmd_map = bnx2x_sp_mapping(bp, uc_mac_config);
- int ramrod_flags = WAIT_RAMROD_COMMON;
- u8 offset = bnx2x_uc_list_cam_offset(bp);
- u8 max_list_size = bnx2x_max_uc_list(bp);
-
- for (i = 0; i < max_list_size; i++) {
- SET_FLAG(config_cmd->config_table[i].flags,
- MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
- T_ETH_MAC_COMMAND_INVALIDATE);
- bnx2x_set_mac_in_nig(bp, 0, NULL, LLH_CAM_ETH_LINE + 1 + i);
- }
-
- wmb();
+ struct net_device *dev = bp->dev;
+ struct bnx2x_mcast_ramrod_params rparam = {0};
+ int rc = 0;
- config_cmd->hdr.length = max_list_size;
- config_cmd->hdr.offset = offset;
- config_cmd->hdr.client_id = 0xff;
- /* We'll wait for a completion this time... */
- config_cmd->hdr.echo = 1;
+ rparam.mcast_obj = &bp->mcast_obj;
- bp->set_mac_pending = 1;
+ /* first, clear all configured multicast MACs */
+ rc = bnx2x_config_mcast(bp, &rparam, BNX2X_MCAST_CMD_DEL);
+ if (rc < 0) {
+ BNX2X_ERR("Failed to clear multicast "
+ "configuration: %d\n", rc);
+ return rc;
+ }
- mb();
+ /* then, configure a new MACs list */
+ if (netdev_mc_count(dev)) {
+ rc = bnx2x_init_mcast_macs_list(bp, &rparam);
+ if (rc) {
+ BNX2X_ERR("Failed to create multicast MACs "
+ "list: %d\n", rc);
+ return rc;
+ }
- bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_SET_MAC, 0,
- U64_HI(config_cmd_map), U64_LO(config_cmd_map), 1);
+ /* Now add the new MACs */
+ rc = bnx2x_config_mcast(bp, &rparam,
+ BNX2X_MCAST_CMD_ADD);
+ if (rc < 0)
+ BNX2X_ERR("Failed to set a new multicast "
+ "configuration: %d\n", rc);
- /* Wait for a completion */
- bnx2x_wait_ramrod(bp, 0, 0, &bp->set_mac_pending,
- ramrod_flags);
+ bnx2x_free_mcast_macs_list(&rparam);
+ }
+ return rc;
}
-static inline int bnx2x_set_mc_list(struct bnx2x *bp)
-{
- /* some multicasts */
- if (CHIP_IS_E1(bp)) {
- return bnx2x_set_e1_mc_list(bp);
- } else { /* E1H and newer */
- return bnx2x_set_e1h_mc_list(bp);
- }
-}
-/* called with netif_tx_lock from dev_mcast.c */
+/* If bp->state is OPEN, should be called with netif_addr_lock_bh() */
void bnx2x_set_rx_mode(struct net_device *dev)
{
struct bnx2x *bp = netdev_priv(dev);
@@ -9017,23 +10101,31 @@ void bnx2x_set_rx_mode(struct net_device *dev)
return;
}
- DP(NETIF_MSG_IFUP, "dev->flags = %x\n", dev->flags);
+ DP(NETIF_MSG_IFUP, "dev->flags = %x\n", bp->dev->flags);
if (dev->flags & IFF_PROMISC)
rx_mode = BNX2X_RX_MODE_PROMISC;
- else if (dev->flags & IFF_ALLMULTI)
+ else if ((dev->flags & IFF_ALLMULTI) ||
+ ((netdev_mc_count(dev) > BNX2X_MAX_MULTICAST) &&
+ CHIP_IS_E1(bp)))
rx_mode = BNX2X_RX_MODE_ALLMULTI;
else {
/* some multicasts */
- if (bnx2x_set_mc_list(bp))
+ if (bnx2x_set_mc_list(bp) < 0)
rx_mode = BNX2X_RX_MODE_ALLMULTI;
- /* some unicasts */
- if (bnx2x_set_uc_list(bp))
+ if (bnx2x_set_uc_list(bp) < 0)
rx_mode = BNX2X_RX_MODE_PROMISC;
}
bp->rx_mode = rx_mode;
+
+ /* Schedule the rx_mode command */
+ if (test_bit(BNX2X_FILTER_RX_MODE_PENDING, &bp->sp_state)) {
+ set_bit(BNX2X_FILTER_RX_MODE_SCHED, &bp->sp_state);
+ return;
+ }
+
bnx2x_set_storm_rx_mode(bp);
}
@@ -9122,10 +10214,35 @@ static const struct net_device_ops bnx2x_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = poll_bnx2x,
#endif
+ .ndo_setup_tc = bnx2x_setup_tc,
+
+#if defined(NETDEV_FCOE_WWNN) && defined(BCM_CNIC)
+ .ndo_fcoe_get_wwn = bnx2x_fcoe_get_wwn,
+#endif
};
+static inline int bnx2x_set_coherency_mask(struct bnx2x *bp)
+{
+ struct device *dev = &bp->pdev->dev;
+
+ if (dma_set_mask(dev, DMA_BIT_MASK(64)) == 0) {
+ bp->flags |= USING_DAC_FLAG;
+ if (dma_set_coherent_mask(dev, DMA_BIT_MASK(64)) != 0) {
+ dev_err(dev, "dma_set_coherent_mask failed, "
+ "aborting\n");
+ return -EIO;
+ }
+ } else if (dma_set_mask(dev, DMA_BIT_MASK(32)) != 0) {
+ dev_err(dev, "System does not support DMA, aborting\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int __devinit bnx2x_init_dev(struct pci_dev *pdev,
- struct net_device *dev)
+ struct net_device *dev,
+ unsigned long board_type)
{
struct bnx2x *bp;
int rc;
@@ -9179,29 +10296,15 @@ static int __devinit bnx2x_init_dev(struct pci_dev *pdev,
goto err_out_release;
}
- bp->pcie_cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
- if (bp->pcie_cap == 0) {
- dev_err(&bp->pdev->dev,
- "Cannot find PCI Express capability, aborting\n");
+ if (!pci_is_pcie(pdev)) {
+ dev_err(&bp->pdev->dev, "Not PCI Express, aborting\n");
rc = -EIO;
goto err_out_release;
}
- if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) == 0) {
- bp->flags |= USING_DAC_FLAG;
- if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)) != 0) {
- dev_err(&bp->pdev->dev, "dma_set_coherent_mask"
- " failed, aborting\n");
- rc = -EIO;
- goto err_out_release;
- }
-
- } else if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) != 0) {
- dev_err(&bp->pdev->dev,
- "System does not support DMA, aborting\n");
- rc = -EIO;
+ rc = bnx2x_set_coherency_mask(bp);
+ if (rc)
goto err_out_release;
- }
dev->mem_start = pci_resource_start(pdev, 0);
dev->base_addr = dev->mem_start;
@@ -9217,25 +10320,35 @@ static int __devinit bnx2x_init_dev(struct pci_dev *pdev,
goto err_out_release;
}
- bp->doorbells = ioremap_nocache(pci_resource_start(pdev, 2),
- min_t(u64, BNX2X_DB_SIZE(bp),
- pci_resource_len(pdev, 2)));
- if (!bp->doorbells) {
- dev_err(&bp->pdev->dev,
- "Cannot map doorbell space, aborting\n");
- rc = -ENOMEM;
- goto err_out_unmap;
- }
-
bnx2x_set_power_state(bp, PCI_D0);
/* clean indirect addresses */
pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS,
PCICFG_VENDOR_ID_OFFSET);
- REG_WR(bp, PXP2_REG_PGL_ADDR_88_F0 + BP_PORT(bp)*16, 0);
- REG_WR(bp, PXP2_REG_PGL_ADDR_8C_F0 + BP_PORT(bp)*16, 0);
- REG_WR(bp, PXP2_REG_PGL_ADDR_90_F0 + BP_PORT(bp)*16, 0);
- REG_WR(bp, PXP2_REG_PGL_ADDR_94_F0 + BP_PORT(bp)*16, 0);
+ /*
+ * Clean the following indirect addresses for all functions since it
+ * is not used by the driver.
+ */
+ REG_WR(bp, PXP2_REG_PGL_ADDR_88_F0, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_8C_F0, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_90_F0, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_94_F0, 0);
+
+ if (CHIP_IS_E1x(bp)) {
+ REG_WR(bp, PXP2_REG_PGL_ADDR_88_F1, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_8C_F1, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_90_F1, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_94_F1, 0);
+ }
+
+ /*
+ * Enable internal target-read (in case we are probed after PF FLR).
+ * Must be done prior to any BAR read access. Only for 57712 and up
+ */
+ if (board_type != BCM57710 &&
+ board_type != BCM57711 &&
+ board_type != BCM57711E)
+ REG_WR(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_TARGET_READ, 1);
/* Reset the load counter */
bnx2x_clear_load_cnt(bp);
@@ -9245,9 +10358,11 @@ static int __devinit bnx2x_init_dev(struct pci_dev *pdev,
dev->netdev_ops = &bnx2x_netdev_ops;
bnx2x_set_ethtool_ops(dev);
+ dev->priv_flags |= IFF_UNICAST_FLT;
+
dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
- NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 |
- NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_HW_VLAN_TX;
+ NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_LRO |
+ NETIF_F_RXCSUM | NETIF_F_RXHASH | NETIF_F_HW_VLAN_TX;
dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_HIGHDMA;
@@ -9273,16 +10388,6 @@ static int __devinit bnx2x_init_dev(struct pci_dev *pdev,
return 0;
-err_out_unmap:
- if (bp->regview) {
- iounmap(bp->regview);
- bp->regview = NULL;
- }
- if (bp->doorbells) {
- iounmap(bp->doorbells);
- bp->doorbells = NULL;
- }
-
err_out_release:
if (atomic_read(&pdev->enable_cnt) == 1)
pci_release_regions(pdev);
@@ -9443,33 +10548,38 @@ do { \
int bnx2x_init_firmware(struct bnx2x *bp)
{
- const char *fw_file_name;
struct bnx2x_fw_file_hdr *fw_hdr;
int rc;
- if (CHIP_IS_E1(bp))
- fw_file_name = FW_FILE_NAME_E1;
- else if (CHIP_IS_E1H(bp))
- fw_file_name = FW_FILE_NAME_E1H;
- else if (CHIP_IS_E2(bp))
- fw_file_name = FW_FILE_NAME_E2;
- else {
- BNX2X_ERR("Unsupported chip revision\n");
- return -EINVAL;
- }
- BNX2X_DEV_INFO("Loading %s\n", fw_file_name);
+ if (!bp->firmware) {
+ const char *fw_file_name;
- rc = request_firmware(&bp->firmware, fw_file_name, &bp->pdev->dev);
- if (rc) {
- BNX2X_ERR("Can't load firmware file %s\n", fw_file_name);
- goto request_firmware_exit;
- }
+ if (CHIP_IS_E1(bp))
+ fw_file_name = FW_FILE_NAME_E1;
+ else if (CHIP_IS_E1H(bp))
+ fw_file_name = FW_FILE_NAME_E1H;
+ else if (!CHIP_IS_E1x(bp))
+ fw_file_name = FW_FILE_NAME_E2;
+ else {
+ BNX2X_ERR("Unsupported chip revision\n");
+ return -EINVAL;
+ }
+ BNX2X_DEV_INFO("Loading %s\n", fw_file_name);
- rc = bnx2x_check_firmware(bp);
- if (rc) {
- BNX2X_ERR("Corrupt firmware file %s\n", fw_file_name);
- goto request_firmware_exit;
+ rc = request_firmware(&bp->firmware, fw_file_name,
+ &bp->pdev->dev);
+ if (rc) {
+ BNX2X_ERR("Can't load firmware file %s\n",
+ fw_file_name);
+ goto request_firmware_exit;
+ }
+
+ rc = bnx2x_check_firmware(bp);
+ if (rc) {
+ BNX2X_ERR("Corrupt firmware file %s\n", fw_file_name);
+ goto request_firmware_exit;
+ }
}
fw_hdr = (struct bnx2x_fw_file_hdr *)bp->firmware->data;
@@ -9519,9 +10629,48 @@ request_firmware_exit:
return rc;
}
-static inline int bnx2x_set_qm_cid_count(struct bnx2x *bp, int l2_cid_count)
+static void bnx2x_release_firmware(struct bnx2x *bp)
{
- int cid_count = L2_FP_COUNT(l2_cid_count);
+ kfree(bp->init_ops_offsets);
+ kfree(bp->init_ops);
+ kfree(bp->init_data);
+ release_firmware(bp->firmware);
+ bp->firmware = NULL;
+}
+
+
+static struct bnx2x_func_sp_drv_ops bnx2x_func_sp_drv = {
+ .init_hw_cmn_chip = bnx2x_init_hw_common_chip,
+ .init_hw_cmn = bnx2x_init_hw_common,
+ .init_hw_port = bnx2x_init_hw_port,
+ .init_hw_func = bnx2x_init_hw_func,
+
+ .reset_hw_cmn = bnx2x_reset_common,
+ .reset_hw_port = bnx2x_reset_port,
+ .reset_hw_func = bnx2x_reset_func,
+
+ .gunzip_init = bnx2x_gunzip_init,
+ .gunzip_end = bnx2x_gunzip_end,
+
+ .init_fw = bnx2x_init_firmware,
+ .release_fw = bnx2x_release_firmware,
+};
+
+void bnx2x__init_func_obj(struct bnx2x *bp)
+{
+ /* Prepare DMAE related driver resources */
+ bnx2x_setup_dmae(bp);
+
+ bnx2x_init_func_obj(bp, &bp->func_obj,
+ bnx2x_sp(bp, func_rdata),
+ bnx2x_sp_mapping(bp, func_rdata),
+ &bnx2x_func_sp_drv);
+}
+
+/* must be called after sriov-enable */
+static inline int bnx2x_set_qm_cid_count(struct bnx2x *bp)
+{
+ int cid_count = BNX2X_L2_CID_COUNT(bp);
#ifdef BCM_CNIC
cid_count += CNIC_CID_MAX;
@@ -9529,24 +10678,74 @@ static inline int bnx2x_set_qm_cid_count(struct bnx2x *bp, int l2_cid_count)
return roundup(cid_count, QM_CID_ROUND);
}
+/**
+ * bnx2x_get_num_none_def_sbs - return the number of none default SBs
+ *
+ * @dev: pci device
+ *
+ */
+static inline int bnx2x_get_num_non_def_sbs(struct pci_dev *pdev)
+{
+ int pos;
+ u16 control;
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
+
+ /*
+ * If MSI-X is not supported - return number of SBs needed to support
+ * one fast path queue: one FP queue + SB for CNIC
+ */
+ if (!pos)
+ return 1 + CNIC_PRESENT;
+
+ /*
+ * The value in the PCI configuration space is the index of the last
+ * entry, namely one less than the actual size of the table, which is
+ * exactly what we want to return from this function: number of all SBs
+ * without the default SB.
+ */
+ pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, &control);
+ return control & PCI_MSIX_FLAGS_QSIZE;
+}
+
static int __devinit bnx2x_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct net_device *dev = NULL;
struct bnx2x *bp;
int pcie_width, pcie_speed;
- int rc, cid_count;
+ int rc, max_non_def_sbs;
+ int rx_count, tx_count, rss_count;
+ /*
+ * An estimated maximum supported CoS number according to the chip
+ * version.
+ * We will try to roughly estimate the maximum number of CoSes this chip
+ * may support in order to minimize the memory allocated for Tx
+ * netdev_queue's. This number will be accurately calculated during the
+ * initialization of bp->max_cos based on the chip versions AND chip
+ * revision in the bnx2x_init_bp().
+ */
+ u8 max_cos_est = 0;
switch (ent->driver_data) {
case BCM57710:
case BCM57711:
case BCM57711E:
- cid_count = FP_SB_MAX_E1x;
+ max_cos_est = BNX2X_MULTI_TX_COS_E1X;
break;
case BCM57712:
- case BCM57712E:
- cid_count = FP_SB_MAX_E2;
+ case BCM57712_MF:
+ max_cos_est = BNX2X_MULTI_TX_COS_E2_E3A0;
+ break;
+
+ case BCM57800:
+ case BCM57800_MF:
+ case BCM57810:
+ case BCM57810_MF:
+ case BCM57840:
+ case BCM57840_MF:
+ max_cos_est = BNX2X_MULTI_TX_COS_E3B0;
break;
default:
@@ -9555,38 +10754,77 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev,
return -ENODEV;
}
- cid_count += NONE_ETH_CONTEXT_USE + CNIC_CONTEXT_USE;
+ max_non_def_sbs = bnx2x_get_num_non_def_sbs(pdev);
+
+ /* !!! FIXME !!!
+ * Do not allow the maximum SB count to grow above 16
+ * since Special CIDs starts from 16*BNX2X_MULTI_TX_COS=48.
+ * We will use the FP_SB_MAX_E1x macro for this matter.
+ */
+ max_non_def_sbs = min_t(int, FP_SB_MAX_E1x, max_non_def_sbs);
+
+ WARN_ON(!max_non_def_sbs);
+
+ /* Maximum number of RSS queues: one IGU SB goes to CNIC */
+ rss_count = max_non_def_sbs - CNIC_PRESENT;
+
+ /* Maximum number of netdev Rx queues: RSS + FCoE L2 */
+ rx_count = rss_count + FCOE_PRESENT;
+
+ /*
+ * Maximum number of netdev Tx queues:
+ * Maximum TSS queues * Maximum supported number of CoS + FCoE L2
+ */
+ tx_count = MAX_TXQS_PER_COS * max_cos_est + FCOE_PRESENT;
/* dev zeroed in init_etherdev */
- dev = alloc_etherdev_mq(sizeof(*bp), cid_count);
+ dev = alloc_etherdev_mqs(sizeof(*bp), tx_count, rx_count);
if (!dev) {
dev_err(&pdev->dev, "Cannot allocate net device\n");
return -ENOMEM;
}
bp = netdev_priv(dev);
- bp->msg_enable = debug;
- pci_set_drvdata(pdev, dev);
+ DP(NETIF_MSG_DRV, "Allocated netdev with %d tx and %d rx queues\n",
+ tx_count, rx_count);
- bp->l2_cid_count = cid_count;
+ bp->igu_sb_cnt = max_non_def_sbs;
+ bp->msg_enable = debug;
+ pci_set_drvdata(pdev, dev);
- rc = bnx2x_init_dev(pdev, dev);
+ rc = bnx2x_init_dev(pdev, dev, ent->driver_data);
if (rc < 0) {
free_netdev(dev);
return rc;
}
+ DP(NETIF_MSG_DRV, "max_non_def_sbs %d\n", max_non_def_sbs);
+
rc = bnx2x_init_bp(bp);
if (rc)
goto init_one_exit;
+ /*
+ * Map doorbels here as we need the real value of bp->max_cos which
+ * is initialized in bnx2x_init_bp().
+ */
+ bp->doorbells = ioremap_nocache(pci_resource_start(pdev, 2),
+ min_t(u64, BNX2X_DB_SIZE(bp),
+ pci_resource_len(pdev, 2)));
+ if (!bp->doorbells) {
+ dev_err(&bp->pdev->dev,
+ "Cannot map doorbell space, aborting\n");
+ rc = -ENOMEM;
+ goto init_one_exit;
+ }
+
/* calc qm_cid_count */
- bp->qm_cid_count = bnx2x_set_qm_cid_count(bp, cid_count);
+ bp->qm_cid_count = bnx2x_set_qm_cid_count(bp);
#ifdef BCM_CNIC
- /* disable FCOE L2 queue for E1x*/
- if (CHIP_IS_E1x(bp))
+ /* disable FCOE L2 queue for E1x and E3*/
+ if (CHIP_IS_E1x(bp) || CHIP_IS_E3(bp))
bp->flags |= NO_FCOE_FLAG;
#endif
@@ -9616,15 +10854,14 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev,
bnx2x_get_pcie_width_speed(bp, &pcie_width, &pcie_speed);
- netdev_info(dev, "%s (%c%d) PCI-E x%d %s found at mem %lx,"
- " IRQ %d, ", board_info[ent->driver_data].name,
- (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4),
- pcie_width,
- ((!CHIP_IS_E2(bp) && pcie_speed == 2) ||
- (CHIP_IS_E2(bp) && pcie_speed == 1)) ?
- "5GHz (Gen2)" : "2.5GHz",
- dev->base_addr, bp->pdev->irq);
- pr_cont("node addr %pM\n", dev->dev_addr);
+ netdev_info(dev, "%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n",
+ board_info[ent->driver_data].name,
+ (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4),
+ pcie_width,
+ ((!CHIP_IS_E2(bp) && pcie_speed == 2) ||
+ (CHIP_IS_E2(bp) && pcie_speed == 1)) ?
+ "5GHz (Gen2)" : "2.5GHz",
+ dev->base_addr, bp->pdev->irq, dev->dev_addr);
return 0;
@@ -9686,7 +10923,7 @@ static void __devexit bnx2x_remove_one(struct pci_dev *pdev)
bnx2x_set_power_state(bp, PCI_D3hot);
/* Make sure RESET task is not scheduled before continuing */
- cancel_delayed_work_sync(&bp->reset_task);
+ cancel_delayed_work_sync(&bp->sp_rtnl_task);
if (bp->regview)
iounmap(bp->regview);
@@ -9694,6 +10931,8 @@ static void __devexit bnx2x_remove_one(struct pci_dev *pdev)
if (bp->doorbells)
iounmap(bp->doorbells);
+ bnx2x_release_firmware(bp);
+
bnx2x_free_mem_bp(bp);
free_netdev(dev);
@@ -9713,12 +10952,17 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
bp->rx_mode = BNX2X_RX_MODE_NONE;
+#ifdef BCM_CNIC
+ bnx2x_cnic_notify(bp, CNIC_CTL_STOP_CMD);
+#endif
+ /* Stop Tx */
+ bnx2x_tx_disable(bp);
+
bnx2x_netif_stop(bp, 0);
- netif_carrier_off(bp->dev);
del_timer_sync(&bp->timer);
- bp->stats_state = STATS_STATE_DISABLED;
- DP(BNX2X_MSG_STATS, "stats_state - DISABLED\n");
+
+ bnx2x_stats_handle(bp, STATS_EVENT_STOP);
/* Release IRQs */
bnx2x_free_irq(bp);
@@ -9733,6 +10977,8 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
bp->state = BNX2X_STATE_CLOSED;
+ netif_carrier_off(bp->dev);
+
return 0;
}
@@ -9845,8 +11091,8 @@ static void bnx2x_io_resume(struct pci_dev *pdev)
struct bnx2x *bp = netdev_priv(dev);
if (bp->recovery_state != BNX2X_RECOVERY_DONE) {
- printk(KERN_ERR "Handling parity error recovery. "
- "Try again later\n");
+ netdev_err(bp->dev, "Handling parity error recovery. "
+ "Try again later\n");
return;
}
@@ -9905,10 +11151,33 @@ static void __exit bnx2x_cleanup(void)
destroy_workqueue(bnx2x_wq);
}
+void bnx2x_notify_link_changed(struct bnx2x *bp)
+{
+ REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_12 + BP_FUNC(bp)*sizeof(u32), 1);
+}
+
module_init(bnx2x_init);
module_exit(bnx2x_cleanup);
#ifdef BCM_CNIC
+/**
+ * bnx2x_set_iscsi_eth_mac_addr - set iSCSI MAC(s).
+ *
+ * @bp: driver handle
+ * @set: set or clear the CAM entry
+ *
+ * This function will wait until the ramdord completion returns.
+ * Return 0 if success, -ENODEV if ramrod doesn't return.
+ */
+static inline int bnx2x_set_iscsi_eth_mac_addr(struct bnx2x *bp)
+{
+ unsigned long ramrod_flags = 0;
+
+ __set_bit(RAMROD_COMP_WAIT, &ramrod_flags);
+ return bnx2x_set_mac_one(bp, bp->cnic_eth_dev.iscsi_mac,
+ &bp->iscsi_l2_mac_obj, true,
+ BNX2X_ISCSI_ETH_MAC, &ramrod_flags);
+}
/* count denotes the number of new completions we have seen */
static void bnx2x_cnic_sp_post(struct bnx2x *bp, int count)
@@ -9929,23 +11198,22 @@ static void bnx2x_cnic_sp_post(struct bnx2x *bp, int count)
u16 type = (le16_to_cpu(bp->cnic_kwq_cons->hdr.type)
& SPE_HDR_CONN_TYPE) >>
SPE_HDR_CONN_TYPE_SHIFT;
+ u8 cmd = (le32_to_cpu(bp->cnic_kwq_cons->hdr.conn_and_cmd_data)
+ >> SPE_HDR_CMD_ID_SHIFT) & 0xff;
/* Set validation for iSCSI L2 client before sending SETUP
* ramrod
*/
if (type == ETH_CONNECTION_TYPE) {
- u8 cmd = (le32_to_cpu(bp->cnic_kwq_cons->
- hdr.conn_and_cmd_data) >>
- SPE_HDR_CMD_ID_SHIFT) & 0xff;
-
if (cmd == RAMROD_CMD_ID_ETH_CLIENT_SETUP)
- bnx2x_set_ctx_validation(&bp->context.
- vcxt[BNX2X_ISCSI_ETH_CID].eth,
- HW_CID(bp, BNX2X_ISCSI_ETH_CID));
+ bnx2x_set_ctx_validation(bp, &bp->context.
+ vcxt[BNX2X_ISCSI_ETH_CID].eth,
+ BNX2X_ISCSI_ETH_CID);
}
- /* There may be not more than 8 L2 and not more than 8 L5 SPEs
- * We also check that the number of outstanding
+ /*
+ * There may be not more than 8 L2, not more than 8 L5 SPEs
+ * and in the air. We also check that number of outstanding
* COMMON ramrods is not more than the EQ and SPQ can
* accommodate.
*/
@@ -10071,18 +11339,61 @@ int bnx2x_cnic_notify(struct bnx2x *bp, int cmd)
return bnx2x_cnic_ctl_send(bp, &ctl);
}
-static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid)
+static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid, u8 err)
{
- struct cnic_ctl_info ctl;
+ struct cnic_ctl_info ctl = {0};
/* first we tell CNIC and only then we count this as a completion */
ctl.cmd = CNIC_CTL_COMPLETION_CMD;
ctl.data.comp.cid = cid;
+ ctl.data.comp.error = err;
bnx2x_cnic_ctl_send_bh(bp, &ctl);
bnx2x_cnic_sp_post(bp, 0);
}
+
+/* Called with netif_addr_lock_bh() taken.
+ * Sets an rx_mode config for an iSCSI ETH client.
+ * Doesn't block.
+ * Completion should be checked outside.
+ */
+static void bnx2x_set_iscsi_eth_rx_mode(struct bnx2x *bp, bool start)
+{
+ unsigned long accept_flags = 0, ramrod_flags = 0;
+ u8 cl_id = bnx2x_cnic_eth_cl_id(bp, BNX2X_ISCSI_ETH_CL_ID_IDX);
+ int sched_state = BNX2X_FILTER_ISCSI_ETH_STOP_SCHED;
+
+ if (start) {
+ /* Start accepting on iSCSI L2 ring. Accept all multicasts
+ * because it's the only way for UIO Queue to accept
+ * multicasts (in non-promiscuous mode only one Queue per
+ * function will receive multicast packets (leading in our
+ * case).
+ */
+ __set_bit(BNX2X_ACCEPT_UNICAST, &accept_flags);
+ __set_bit(BNX2X_ACCEPT_ALL_MULTICAST, &accept_flags);
+ __set_bit(BNX2X_ACCEPT_BROADCAST, &accept_flags);
+ __set_bit(BNX2X_ACCEPT_ANY_VLAN, &accept_flags);
+
+ /* Clear STOP_PENDING bit if START is requested */
+ clear_bit(BNX2X_FILTER_ISCSI_ETH_STOP_SCHED, &bp->sp_state);
+
+ sched_state = BNX2X_FILTER_ISCSI_ETH_START_SCHED;
+ } else
+ /* Clear START_PENDING bit if STOP is requested */
+ clear_bit(BNX2X_FILTER_ISCSI_ETH_START_SCHED, &bp->sp_state);
+
+ if (test_bit(BNX2X_FILTER_RX_MODE_PENDING, &bp->sp_state))
+ set_bit(sched_state, &bp->sp_state);
+ else {
+ __set_bit(RAMROD_RX, &ramrod_flags);
+ bnx2x_set_q_rx_mode(bp, cl_id, 0, accept_flags, 0,
+ ramrod_flags);
+ }
+}
+
+
static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl)
{
struct bnx2x *bp = netdev_priv(dev);
@@ -10106,45 +11417,65 @@ static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl)
/* rtnl_lock is held. */
case DRV_CTL_START_L2_CMD: {
- u32 cli = ctl->data.ring.client_id;
-
- /* Clear FCoE FIP and ALL ENODE MACs addresses first */
- bnx2x_del_fcoe_eth_macs(bp);
+ struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+ unsigned long sp_bits = 0;
+
+ /* Configure the iSCSI classification object */
+ bnx2x_init_mac_obj(bp, &bp->iscsi_l2_mac_obj,
+ cp->iscsi_l2_client_id,
+ cp->iscsi_l2_cid, BP_FUNC(bp),
+ bnx2x_sp(bp, mac_rdata),
+ bnx2x_sp_mapping(bp, mac_rdata),
+ BNX2X_FILTER_MAC_PENDING,
+ &bp->sp_state, BNX2X_OBJ_TYPE_RX,
+ &bp->macs_pool);
/* Set iSCSI MAC address */
- bnx2x_set_iscsi_eth_mac_addr(bp, 1);
+ rc = bnx2x_set_iscsi_eth_mac_addr(bp);
+ if (rc)
+ break;
mmiowb();
barrier();
- /* Start accepting on iSCSI L2 ring. Accept all multicasts
- * because it's the only way for UIO Client to accept
- * multicasts (in non-promiscuous mode only one Client per
- * function will receive multicast packets (leading in our
- * case).
- */
- bnx2x_rxq_set_mac_filters(bp, cli,
- BNX2X_ACCEPT_UNICAST |
- BNX2X_ACCEPT_BROADCAST |
- BNX2X_ACCEPT_ALL_MULTICAST);
- storm_memset_mac_filters(bp, &bp->mac_filters, BP_FUNC(bp));
+ /* Start accepting on iSCSI L2 ring */
+
+ netif_addr_lock_bh(dev);
+ bnx2x_set_iscsi_eth_rx_mode(bp, true);
+ netif_addr_unlock_bh(dev);
+
+ /* bits to wait on */
+ __set_bit(BNX2X_FILTER_RX_MODE_PENDING, &sp_bits);
+ __set_bit(BNX2X_FILTER_ISCSI_ETH_START_SCHED, &sp_bits);
+
+ if (!bnx2x_wait_sp_comp(bp, sp_bits))
+ BNX2X_ERR("rx_mode completion timed out!\n");
break;
}
/* rtnl_lock is held. */
case DRV_CTL_STOP_L2_CMD: {
- u32 cli = ctl->data.ring.client_id;
+ unsigned long sp_bits = 0;
/* Stop accepting on iSCSI L2 ring */
- bnx2x_rxq_set_mac_filters(bp, cli, BNX2X_ACCEPT_NONE);
- storm_memset_mac_filters(bp, &bp->mac_filters, BP_FUNC(bp));
+ netif_addr_lock_bh(dev);
+ bnx2x_set_iscsi_eth_rx_mode(bp, false);
+ netif_addr_unlock_bh(dev);
+
+ /* bits to wait on */
+ __set_bit(BNX2X_FILTER_RX_MODE_PENDING, &sp_bits);
+ __set_bit(BNX2X_FILTER_ISCSI_ETH_STOP_SCHED, &sp_bits);
+
+ if (!bnx2x_wait_sp_comp(bp, sp_bits))
+ BNX2X_ERR("rx_mode completion timed out!\n");
mmiowb();
barrier();
/* Unset iSCSI L2 MAC */
- bnx2x_set_iscsi_eth_mac_addr(bp, 0);
+ rc = bnx2x_del_all_macs(bp, &bp->iscsi_l2_mac_obj,
+ BNX2X_ISCSI_ETH_MAC, true);
break;
}
case DRV_CTL_RET_L2_SPQ_CREDIT_CMD: {
@@ -10156,11 +11487,6 @@ static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl)
break;
}
- case DRV_CTL_ISCSI_STOPPED_CMD: {
- bnx2x_dcbx_set_params(bp, BNX2X_DCBX_STATE_ISCSI_STOPPED);
- break;
- }
-
default:
BNX2X_ERR("unknown command %x\n", ctl->cmd);
rc = -EINVAL;
@@ -10181,13 +11507,13 @@ void bnx2x_setup_cnic_irq_info(struct bnx2x *bp)
cp->drv_state &= ~CNIC_DRV_STATE_USING_MSIX;
cp->irq_arr[0].irq_flags &= ~CNIC_IRQ_FL_MSIX;
}
- if (CHIP_IS_E2(bp))
+ if (!CHIP_IS_E1x(bp))
cp->irq_arr[0].status_blk = (void *)bp->cnic_sb.e2_sb;
else
cp->irq_arr[0].status_blk = (void *)bp->cnic_sb.e1x_sb;
- cp->irq_arr[0].status_blk_num = CNIC_SB_ID(bp);
- cp->irq_arr[0].status_blk_num2 = CNIC_IGU_SB_ID(bp);
+ cp->irq_arr[0].status_blk_num = bnx2x_cnic_fw_sb_id(bp);
+ cp->irq_arr[0].status_blk_num2 = bnx2x_cnic_igu_sb_id(bp);
cp->irq_arr[1].status_blk = bp->def_status_blk;
cp->irq_arr[1].status_blk_num = DEF_SB_ID;
cp->irq_arr[1].status_blk_num2 = DEF_SB_IGU_ID;
@@ -10204,9 +11530,6 @@ static int bnx2x_register_cnic(struct net_device *dev, struct cnic_ops *ops,
if (ops == NULL)
return -EINVAL;
- if (atomic_read(&bp->intr_sem) != 0)
- return -EBUSY;
-
bp->cnic_kwq = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!bp->cnic_kwq)
return -ENOMEM;
@@ -10221,7 +11544,7 @@ static int bnx2x_register_cnic(struct net_device *dev, struct cnic_ops *ops,
bp->cnic_data = data;
cp->num_irq = 0;
- cp->drv_state = CNIC_DRV_STATE_REGD;
+ cp->drv_state |= CNIC_DRV_STATE_REGD;
cp->iro_arr = bp->iro_arr;
bnx2x_setup_cnic_irq_info(bp);
@@ -10275,8 +11598,8 @@ struct cnic_eth_dev *bnx2x_cnic_probe(struct net_device *dev)
cp->drv_register_cnic = bnx2x_register_cnic;
cp->drv_unregister_cnic = bnx2x_unregister_cnic;
cp->fcoe_init_cid = BNX2X_FCOE_ETH_CID;
- cp->iscsi_l2_client_id = BNX2X_ISCSI_ETH_CL_ID +
- BP_E1HVN(bp) * NONE_ETH_CONTEXT_USE;
+ cp->iscsi_l2_client_id =
+ bnx2x_cnic_eth_cl_id(bp, BNX2X_ISCSI_ETH_CL_ID_IDX);
cp->iscsi_l2_cid = BNX2X_ISCSI_ETH_CID;
if (NO_ISCSI_OOO(bp))
diff --git a/drivers/net/bnx2x/bnx2x_reg.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h
index 86bba25d2d3f..fc7bd0f23c0b 100644
--- a/drivers/net/bnx2x/bnx2x_reg.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h
@@ -32,7 +32,11 @@
/* [R 1] ATC initalization done */
#define ATC_REG_ATC_INIT_DONE 0x1100bc
/* [RC 6] Interrupt register #0 read clear */
-#define ATC_REG_ATC_INT_STS_CLR 0x1101c0
+#define ATC_REG_ATC_INT_STS_CLR 0x1101c0
+/* [RW 5] Parity mask register #0 read/write */
+#define ATC_REG_ATC_PRTY_MASK 0x1101d8
+/* [RC 5] Parity register #0 read clear */
+#define ATC_REG_ATC_PRTY_STS_CLR 0x1101d0
/* [RW 19] Interrupt mask register #0 read/write */
#define BRB1_REG_BRB1_INT_MASK 0x60128
/* [R 19] Interrupt register #0 read */
@@ -54,16 +58,20 @@
/* [RW 10] The number of free blocks below which the full signal to class 0
* is asserted */
#define BRB1_REG_FULL_0_XOFF_THRESHOLD_0 0x601d0
-/* [RW 10] The number of free blocks above which the full signal to class 0
+#define BRB1_REG_FULL_0_XOFF_THRESHOLD_1 0x60230
+/* [RW 11] The number of free blocks above which the full signal to class 0
* is de-asserted */
#define BRB1_REG_FULL_0_XON_THRESHOLD_0 0x601d4
-/* [RW 10] The number of free blocks below which the full signal to class 1
+#define BRB1_REG_FULL_0_XON_THRESHOLD_1 0x60234
+/* [RW 11] The number of free blocks below which the full signal to class 1
* is asserted */
#define BRB1_REG_FULL_1_XOFF_THRESHOLD_0 0x601d8
-/* [RW 10] The number of free blocks above which the full signal to class 1
+#define BRB1_REG_FULL_1_XOFF_THRESHOLD_1 0x60238
+/* [RW 11] The number of free blocks above which the full signal to class 1
* is de-asserted */
#define BRB1_REG_FULL_1_XON_THRESHOLD_0 0x601dc
-/* [RW 10] The number of free blocks below which the full signal to the LB
+#define BRB1_REG_FULL_1_XON_THRESHOLD_1 0x6023c
+/* [RW 11] The number of free blocks below which the full signal to the LB
* port is asserted */
#define BRB1_REG_FULL_LB_XOFF_THRESHOLD 0x601e0
/* [RW 10] The number of free blocks above which the full signal to the LB
@@ -75,15 +83,49 @@
/* [RW 10] The number of free blocks below which the High_llfc signal to
interface #n is asserted. */
#define BRB1_REG_HIGH_LLFC_LOW_THRESHOLD_0 0x6013c
-/* [RW 23] LL RAM data. */
-#define BRB1_REG_LL_RAM 0x61000
+/* [RW 11] The number of blocks guarantied for the LB port */
+#define BRB1_REG_LB_GUARANTIED 0x601ec
+/* [RW 11] The hysteresis on the guarantied buffer space for the Lb port
+ * before signaling XON. */
+#define BRB1_REG_LB_GUARANTIED_HYST 0x60264
+/* [RW 24] LL RAM data. */
+#define BRB1_REG_LL_RAM 0x61000
/* [RW 10] The number of free blocks above which the Low_llfc signal to
interface #n is de-asserted. */
#define BRB1_REG_LOW_LLFC_HIGH_THRESHOLD_0 0x6016c
/* [RW 10] The number of free blocks below which the Low_llfc signal to
interface #n is asserted. */
#define BRB1_REG_LOW_LLFC_LOW_THRESHOLD_0 0x6015c
-/* [RW 10] The number of blocks guarantied for the MAC port */
+/* [RW 11] The number of blocks guarantied for class 0 in MAC 0. The
+ * register is applicable only when per_class_guaranty_mode is set. */
+#define BRB1_REG_MAC_0_CLASS_0_GUARANTIED 0x60244
+/* [RW 11] The hysteresis on the guarantied buffer space for class 0 in MAC
+ * 1 before signaling XON. The register is applicable only when
+ * per_class_guaranty_mode is set. */
+#define BRB1_REG_MAC_0_CLASS_0_GUARANTIED_HYST 0x60254
+/* [RW 11] The number of blocks guarantied for class 1 in MAC 0. The
+ * register is applicable only when per_class_guaranty_mode is set. */
+#define BRB1_REG_MAC_0_CLASS_1_GUARANTIED 0x60248
+/* [RW 11] The hysteresis on the guarantied buffer space for class 1in MAC 0
+ * before signaling XON. The register is applicable only when
+ * per_class_guaranty_mode is set. */
+#define BRB1_REG_MAC_0_CLASS_1_GUARANTIED_HYST 0x60258
+/* [RW 11] The number of blocks guarantied for class 0in MAC1.The register
+ * is applicable only when per_class_guaranty_mode is set. */
+#define BRB1_REG_MAC_1_CLASS_0_GUARANTIED 0x6024c
+/* [RW 11] The hysteresis on the guarantied buffer space for class 0 in MAC
+ * 1 before signaling XON. The register is applicable only when
+ * per_class_guaranty_mode is set. */
+#define BRB1_REG_MAC_1_CLASS_0_GUARANTIED_HYST 0x6025c
+/* [RW 11] The number of blocks guarantied for class 1 in MAC 1. The
+ * register is applicable only when per_class_guaranty_mode is set. */
+#define BRB1_REG_MAC_1_CLASS_1_GUARANTIED 0x60250
+/* [RW 11] The hysteresis on the guarantied buffer space for class 1 in MAC
+ * 1 before signaling XON. The register is applicable only when
+ * per_class_guaranty_mode is set. */
+#define BRB1_REG_MAC_1_CLASS_1_GUARANTIED_HYST 0x60260
+/* [RW 11] The number of blocks guarantied for the MAC port. The register is
+ * applicable only when per_class_guaranty_mode is reset. */
#define BRB1_REG_MAC_GUARANTIED_0 0x601e8
#define BRB1_REG_MAC_GUARANTIED_1 0x60240
/* [R 24] The number of full blocks. */
@@ -100,15 +142,19 @@
/* [RW 10] The number of free blocks below which the pause signal to class 0
* is asserted */
#define BRB1_REG_PAUSE_0_XOFF_THRESHOLD_0 0x601c0
-/* [RW 10] The number of free blocks above which the pause signal to class 0
+#define BRB1_REG_PAUSE_0_XOFF_THRESHOLD_1 0x60220
+/* [RW 11] The number of free blocks above which the pause signal to class 0
* is de-asserted */
#define BRB1_REG_PAUSE_0_XON_THRESHOLD_0 0x601c4
-/* [RW 10] The number of free blocks below which the pause signal to class 1
+#define BRB1_REG_PAUSE_0_XON_THRESHOLD_1 0x60224
+/* [RW 11] The number of free blocks below which the pause signal to class 1
* is asserted */
#define BRB1_REG_PAUSE_1_XOFF_THRESHOLD_0 0x601c8
-/* [RW 10] The number of free blocks above which the pause signal to class 1
+#define BRB1_REG_PAUSE_1_XOFF_THRESHOLD_1 0x60228
+/* [RW 11] The number of free blocks above which the pause signal to class 1
* is de-asserted */
#define BRB1_REG_PAUSE_1_XON_THRESHOLD_0 0x601cc
+#define BRB1_REG_PAUSE_1_XON_THRESHOLD_1 0x6022c
/* [RW 10] Write client 0: De-assert pause threshold. Not Functional */
#define BRB1_REG_PAUSE_HIGH_THRESHOLD_0 0x60078
#define BRB1_REG_PAUSE_HIGH_THRESHOLD_1 0x6007c
@@ -325,7 +371,7 @@
mechanism. The fields are: [5:0] - message length; [12:6] - message
pointer; 18:13] - next pointer. */
#define CCM_REG_XX_DESCR_TABLE 0xd0300
-#define CCM_REG_XX_DESCR_TABLE_SIZE 36
+#define CCM_REG_XX_DESCR_TABLE_SIZE 24
/* [R 7] Used to read the value of XX protection Free counter. */
#define CCM_REG_XX_FREE 0xd0184
/* [RW 6] Initial value for the credit counter; responsible for fulfilling
@@ -422,6 +468,7 @@
#define CFC_REG_NUM_LCIDS_ALLOC 0x104020
/* [R 9] Number of Arriving LCIDs in Link List Block */
#define CFC_REG_NUM_LCIDS_ARRIVING 0x104004
+#define CFC_REG_NUM_LCIDS_INSIDE_PF 0x104120
/* [R 9] Number of Leaving LCIDs in Link List Block */
#define CFC_REG_NUM_LCIDS_LEAVING 0x104018
#define CFC_REG_WEAK_ENABLE_PF 0x104124
@@ -783,6 +830,7 @@
/* [RW 3] The number of simultaneous outstanding requests to Context Fetch
Interface. */
#define DORQ_REG_OUTST_REQ 0x17003c
+#define DORQ_REG_PF_USAGE_CNT 0x1701d0
#define DORQ_REG_REGN 0x170038
/* [R 4] Current value of response A counter credit. Initial credit is
configured through write to ~dorq_registers_rsp_init_crd.rsp_init_crd
@@ -802,10 +850,12 @@
/* [RW 28] TCM Header when both ULP and TCP context is loaded. */
#define DORQ_REG_SHRT_CMHEAD 0x170054
#define HC_CONFIG_0_REG_ATTN_BIT_EN_0 (0x1<<4)
+#define HC_CONFIG_0_REG_BLOCK_DISABLE_0 (0x1<<0)
#define HC_CONFIG_0_REG_INT_LINE_EN_0 (0x1<<3)
#define HC_CONFIG_0_REG_MSI_ATTN_EN_0 (0x1<<7)
#define HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 (0x1<<2)
-#define HC_CONFIG_0_REG_SINGLE_ISR_EN_0 (0x1<<1)
+#define HC_CONFIG_0_REG_SINGLE_ISR_EN_0 (0x1<<1)
+#define HC_CONFIG_1_REG_BLOCK_DISABLE_1 (0x1<<0)
#define HC_REG_AGG_INT_0 0x108050
#define HC_REG_AGG_INT_1 0x108054
#define HC_REG_ATTN_BIT 0x108120
@@ -844,6 +894,7 @@
#define HC_REG_VQID_0 0x108008
#define HC_REG_VQID_1 0x10800c
#define IGU_BLOCK_CONFIGURATION_REG_BACKWARD_COMP_EN (0x1<<1)
+#define IGU_BLOCK_CONFIGURATION_REG_BLOCK_ENABLE (0x1<<0)
#define IGU_REG_ATTENTION_ACK_BITS 0x130108
/* [R 4] Debug: attn_fsm */
#define IGU_REG_ATTN_FSM 0x130054
@@ -933,6 +984,14 @@
* clear; 1 = set. Data valid only in addresses 0-4. all the rest are zero. */
#define IGU_REG_WRITE_DONE_PENDING 0x130480
#define MCP_A_REG_MCPR_SCRATCH 0x3a0000
+#define MCP_REG_MCPR_CPU_PROGRAM_COUNTER 0x8501c
+#define MCP_REG_MCPR_GP_INPUTS 0x800c0
+#define MCP_REG_MCPR_GP_OENABLE 0x800c8
+#define MCP_REG_MCPR_GP_OUTPUTS 0x800c4
+#define MCP_REG_MCPR_IMC_COMMAND 0x85900
+#define MCP_REG_MCPR_IMC_DATAREG0 0x85920
+#define MCP_REG_MCPR_IMC_SLAVE_CONTROL 0x85904
+#define MCP_REG_MCPR_CPU_PROGRAM_COUNTER 0x8501c
#define MCP_REG_MCPR_NVM_ACCESS_ENABLE 0x86424
#define MCP_REG_MCPR_NVM_ADDR 0x8640c
#define MCP_REG_MCPR_NVM_CFG4 0x8642c
@@ -1325,6 +1384,18 @@
Latched ump_tx_parity; [31] MCP Latched scpad_parity; */
#define MISC_REG_AEU_ENABLE4_PXP_0 0xa108
#define MISC_REG_AEU_ENABLE4_PXP_1 0xa1a8
+/* [RW 32] fifth 32b for enabling the output for function 0 output0. Mapped
+ * as follows: [0] PGLUE config_space; [1] PGLUE misc_flr; [2] PGLUE B RBC
+ * attention [3] PGLUE B RBC parity; [4] ATC attention; [5] ATC parity; [6]
+ * mstat0 attention; [7] mstat0 parity; [8] mstat1 attention; [9] mstat1
+ * parity; [31-10] Reserved; */
+#define MISC_REG_AEU_ENABLE5_FUNC_0_OUT_0 0xa688
+/* [RW 32] Fifth 32b for enabling the output for function 1 output0. Mapped
+ * as follows: [0] PGLUE config_space; [1] PGLUE misc_flr; [2] PGLUE B RBC
+ * attention [3] PGLUE B RBC parity; [4] ATC attention; [5] ATC parity; [6]
+ * mstat0 attention; [7] mstat0 parity; [8] mstat1 attention; [9] mstat1
+ * parity; [31-10] Reserved; */
+#define MISC_REG_AEU_ENABLE5_FUNC_1_OUT_0 0xa6b0
/* [RW 1] set/clr general attention 0; this will set/clr bit 94 in the aeu
128 bit vector */
#define MISC_REG_AEU_GENERAL_ATTN_0 0xa000
@@ -1429,11 +1500,37 @@
/* [RW 1] e1hmf for WOL. If clr WOL signal o the PXP will be send on bit 0
only. */
#define MISC_REG_E1HMF_MODE 0xa5f8
+/* [R 1] Status of four port mode path swap input pin. */
+#define MISC_REG_FOUR_PORT_PATH_SWAP 0xa75c
+/* [RW 2] 4 port path swap overwrite.[0] - Overwrite control; if it is 0 -
+ the path_swap output is equal to 4 port mode path swap input pin; if it
+ is 1 - the path_swap output is equal to bit[1] of this register; [1] -
+ Overwrite value. If bit[0] of this register is 1 this is the value that
+ receives the path_swap output. Reset on Hard reset. */
+#define MISC_REG_FOUR_PORT_PATH_SWAP_OVWR 0xa738
+/* [R 1] Status of 4 port mode port swap input pin. */
+#define MISC_REG_FOUR_PORT_PORT_SWAP 0xa754
+/* [RW 2] 4 port port swap overwrite.[0] - Overwrite control; if it is 0 -
+ the port_swap output is equal to 4 port mode port swap input pin; if it
+ is 1 - the port_swap output is equal to bit[1] of this register; [1] -
+ Overwrite value. If bit[0] of this register is 1 this is the value that
+ receives the port_swap output. Reset on Hard reset. */
+#define MISC_REG_FOUR_PORT_PORT_SWAP_OVWR 0xa734
/* [RW 32] Debug only: spare RW register reset by core reset */
#define MISC_REG_GENERIC_CR_0 0xa460
#define MISC_REG_GENERIC_CR_1 0xa464
/* [RW 32] Debug only: spare RW register reset by por reset */
#define MISC_REG_GENERIC_POR_1 0xa474
+/* [RW 32] Bit[0]: EPIO MODE SEL: Setting this bit to 1 will allow SW/FW to
+ use all of the 32 Extended GPIO pins. Without setting this bit; an EPIO
+ can not be configured as an output. Each output has its output enable in
+ the MCP register space; but this bit needs to be set to make use of that.
+ Bit[3:1] spare. Bit[4]: WCVTMON_PWRDN: Powerdown for Warpcore VTMON. When
+ set to 1 - Powerdown. Bit[5]: WCVTMON_RESETB: Reset for Warpcore VTMON.
+ When set to 0 - vTMON is in reset. Bit[6]: setting this bit will change
+ the i/o to an output and will drive the TimeSync output. Bit[31:7]:
+ spare. Global register. Reset by hard reset. */
+#define MISC_REG_GEN_PURP_HWG 0xa9a0
/* [RW 32] GPIO. [31-28] FLOAT port 0; [27-24] FLOAT port 0; When any of
these bits is written as a '1'; the corresponding SPIO bit will turn off
it's drivers and become an input. This is the reset state of all GPIO
@@ -1636,6 +1733,14 @@
in this register. address 0 - timer 1; address 1 - timer 2, ... address 7 -
timer 8 */
#define MISC_REG_SW_TIMER_VAL 0xa5c0
+/* [R 1] Status of two port mode path swap input pin. */
+#define MISC_REG_TWO_PORT_PATH_SWAP 0xa758
+/* [RW 2] 2 port swap overwrite.[0] - Overwrite control; if it is 0 - the
+ path_swap output is equal to 2 port mode path swap input pin; if it is 1
+ - the path_swap output is equal to bit[1] of this register; [1] -
+ Overwrite value. If bit[0] of this register is 1 this is the value that
+ receives the path_swap output. Reset on Hard reset. */
+#define MISC_REG_TWO_PORT_PATH_SWAP_OVWR 0xa72c
/* [RW 1] Set by the MCP to remember if one or more of the drivers is/are
loaded; 0-prepare; -unprepare */
#define MISC_REG_UNPREPARED 0xa424
@@ -1644,6 +1749,36 @@
#define NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_NO_VLAN (0x1<<4)
#define NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_UNCST (0x1<<2)
#define NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_VLAN (0x1<<3)
+/* [RW 5] MDIO PHY Address. The WC uses this address to determine whether or
+ * not it is the recipient of the message on the MDIO interface. The value
+ * is compared to the value on ctrl_md_devad. Drives output
+ * misc_xgxs0_phy_addr. Global register. */
+#define MISC_REG_WC0_CTRL_PHY_ADDR 0xa9cc
+/* [RW 2] XMAC Core port mode. Indicates the number of ports on the system
+ side. This should be less than or equal to phy_port_mode; if some of the
+ ports are not used. This enables reduction of frequency on the core side.
+ This is a strap input for the XMAC_MP core. 00 - Single Port Mode; 01 -
+ Dual Port Mode; 10 - Tri Port Mode; 11 - Quad Port Mode. This is a strap
+ input for the XMAC_MP core; and should be changed only while reset is
+ held low. Reset on Hard reset. */
+#define MISC_REG_XMAC_CORE_PORT_MODE 0xa964
+/* [RW 2] XMAC PHY port mode. Indicates the number of ports on the Warp
+ Core. This is a strap input for the XMAC_MP core. 00 - Single Port Mode;
+ 01 - Dual Port Mode; 1x - Quad Port Mode; This is a strap input for the
+ XMAC_MP core; and should be changed only while reset is held low. Reset
+ on Hard reset. */
+#define MISC_REG_XMAC_PHY_PORT_MODE 0xa960
+/* [RW 32] 1 [47] Packet Size = 64 Write to this register write bits 31:0.
+ * Reads from this register will clear bits 31:0. */
+#define MSTAT_REG_RX_STAT_GR64_LO 0x200
+/* [RW 32] 1 [00] Tx Good Packet Count Write to this register write bits
+ * 31:0. Reads from this register will clear bits 31:0. */
+#define MSTAT_REG_TX_STAT_GTXPOK_LO 0
+#define NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_BRCST (0x1<<0)
+#define NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_MLCST (0x1<<1)
+#define NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_NO_VLAN (0x1<<4)
+#define NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_UNCST (0x1<<2)
+#define NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_VLAN (0x1<<3)
#define NIG_LLH0_XCM_MASK_REG_LLH0_XCM_MASK_BCN (0x1<<0)
#define NIG_LLH1_XCM_MASK_REG_LLH1_XCM_MASK_BCN (0x1<<0)
#define NIG_MASK_INTERRUPT_PORT0_REG_MASK_EMAC0_MISC_MI_INT (0x1<<0)
@@ -1837,6 +1972,10 @@
#define NIG_REG_LLH1_FUNC_MEM 0x161c0
#define NIG_REG_LLH1_FUNC_MEM_ENABLE 0x16160
#define NIG_REG_LLH1_FUNC_MEM_SIZE 16
+/* [RW 1] When this bit is set; the LLH will classify the packet before
+ * sending it to the BRB or calculating WoL on it. This bit controls port 1
+ * only. The legacy llh_multi_function_mode bit controls port 0. */
+#define NIG_REG_LLH1_MF_MODE 0x18614
/* [RW 8] init credit counter for port1 in LLH */
#define NIG_REG_LLH1_XCM_INIT_CREDIT 0x10564
#define NIG_REG_LLH1_XCM_MASK 0x10134
@@ -1858,11 +1997,25 @@
/* [R 32] Interrupt register #0 read */
#define NIG_REG_NIG_INT_STS_0 0x103b0
#define NIG_REG_NIG_INT_STS_1 0x103c0
+/* [R 32] Legacy E1 and E1H location for parity error mask register. */
+#define NIG_REG_NIG_PRTY_MASK 0x103dc
+/* [RW 32] Parity mask register #0 read/write */
+#define NIG_REG_NIG_PRTY_MASK_0 0x183c8
+#define NIG_REG_NIG_PRTY_MASK_1 0x183d8
/* [R 32] Legacy E1 and E1H location for parity error status register. */
#define NIG_REG_NIG_PRTY_STS 0x103d0
/* [R 32] Parity register #0 read */
#define NIG_REG_NIG_PRTY_STS_0 0x183bc
#define NIG_REG_NIG_PRTY_STS_1 0x183cc
+/* [R 32] Legacy E1 and E1H location for parity error status clear register. */
+#define NIG_REG_NIG_PRTY_STS_CLR 0x103d4
+/* [RC 32] Parity register #0 read clear */
+#define NIG_REG_NIG_PRTY_STS_CLR_0 0x183c0
+#define NIG_REG_NIG_PRTY_STS_CLR_1 0x183d0
+#define MCPR_IMC_COMMAND_ENABLE (1L<<31)
+#define MCPR_IMC_COMMAND_IMC_STATUS_BITSHIFT 16
+#define MCPR_IMC_COMMAND_OPERATION_BITSHIFT 28
+#define MCPR_IMC_COMMAND_TRANSFER_ADDRESS_BITSHIFT 8
/* [RW 6] Bit-map indicating which L2 hdrs may appear after the basic
* Ethernet header. */
#define NIG_REG_P0_HDRS_AFTER_BASIC 0x18038
@@ -1872,6 +2025,12 @@
#define NIG_REG_P0_HWPFC_ENABLE 0x18078
#define NIG_REG_P0_LLH_FUNC_MEM2 0x18480
#define NIG_REG_P0_LLH_FUNC_MEM2_ENABLE 0x18440
+/* [RW 1] Input enable for RX MAC interface. */
+#define NIG_REG_P0_MAC_IN_EN 0x185ac
+/* [RW 1] Output enable for TX MAC interface */
+#define NIG_REG_P0_MAC_OUT_EN 0x185b0
+/* [RW 1] Output enable for TX PAUSE signal to the MAC. */
+#define NIG_REG_P0_MAC_PAUSE_OUT_EN 0x185b4
/* [RW 32] Eight 4-bit configurations for specifying which COS (0-15 for
* future expansion) each priorty is to be mapped to. Bits 3:0 specify the
* COS for priority 0. Bits 31:28 specify the COS for priority 7. The 3-bit
@@ -1888,11 +2047,52 @@
* than one bit may be set; allowing multiple priorities to be mapped to one
* COS. */
#define NIG_REG_P0_RX_COS1_PRIORITY_MASK 0x1805c
+/* [RW 16] Bit-map indicating which SAFC/PFC priorities to map to COS 2. A
+ * priority is mapped to COS 2 when the corresponding mask bit is 1. More
+ * than one bit may be set; allowing multiple priorities to be mapped to one
+ * COS. */
+#define NIG_REG_P0_RX_COS2_PRIORITY_MASK 0x186b0
+/* [RW 16] Bit-map indicating which SAFC/PFC priorities to map to COS 3. A
+ * priority is mapped to COS 3 when the corresponding mask bit is 1. More
+ * than one bit may be set; allowing multiple priorities to be mapped to one
+ * COS. */
+#define NIG_REG_P0_RX_COS3_PRIORITY_MASK 0x186b4
+/* [RW 16] Bit-map indicating which SAFC/PFC priorities to map to COS 4. A
+ * priority is mapped to COS 4 when the corresponding mask bit is 1. More
+ * than one bit may be set; allowing multiple priorities to be mapped to one
+ * COS. */
+#define NIG_REG_P0_RX_COS4_PRIORITY_MASK 0x186b8
+/* [RW 16] Bit-map indicating which SAFC/PFC priorities to map to COS 5. A
+ * priority is mapped to COS 5 when the corresponding mask bit is 1. More
+ * than one bit may be set; allowing multiple priorities to be mapped to one
+ * COS. */
+#define NIG_REG_P0_RX_COS5_PRIORITY_MASK 0x186bc
+/* [R 1] RX FIFO for receiving data from MAC is empty. */
/* [RW 15] Specify which of the credit registers the client is to be mapped
* to. Bits[2:0] are for client 0; bits [14:12] are for client 4. For
* clients that are not subject to WFQ credit blocking - their
* specifications here are not used. */
#define NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP 0x180f0
+/* [RW 32] Specify which of the credit registers the client is to be mapped
+ * to. This register specifies bits 31:0 of the 36-bit value. Bits[3:0] are
+ * for client 0; bits [35:32] are for client 8. For clients that are not
+ * subject to WFQ credit blocking - their specifications here are not used.
+ * This is a new register (with 2_) added in E3 B0 to accommodate the 9
+ * input clients to ETS arbiter. The reset default is set for management and
+ * debug to use credit registers 6, 7, and 8, respectively, and COSes 0-5 to
+ * use credit registers 0-5 respectively (0x543210876). Note that credit
+ * registers can not be shared between clients. */
+#define NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP2_LSB 0x18688
+/* [RW 4] Specify which of the credit registers the client is to be mapped
+ * to. This register specifies bits 35:32 of the 36-bit value. Bits[3:0] are
+ * for client 0; bits [35:32] are for client 8. For clients that are not
+ * subject to WFQ credit blocking - their specifications here are not used.
+ * This is a new register (with 2_) added in E3 B0 to accommodate the 9
+ * input clients to ETS arbiter. The reset default is set for management and
+ * debug to use credit registers 6, 7, and 8, respectively, and COSes 0-5 to
+ * use credit registers 0-5 respectively (0x543210876). Note that credit
+ * registers can not be shared between clients. */
+#define NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP2_MSB 0x1868c
/* [RW 5] Specify whether the client competes directly in the strict
* priority arbiter. The bits are mapped according to client ID (client IDs
* are defined in tx_arb_priority_client). Default value is set to enable
@@ -1907,10 +2107,24 @@
* reach. */
#define NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_0 0x1810c
#define NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_1 0x18110
+#define NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_2 0x18114
+#define NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_3 0x18118
+#define NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_4 0x1811c
+#define NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_5 0x186a0
+#define NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_6 0x186a4
+#define NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_7 0x186a8
+#define NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_8 0x186ac
/* [RW 32] Specify the weight (in bytes) to be added to credit register 0
* when it is time to increment. */
#define NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_0 0x180f8
#define NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_1 0x180fc
+#define NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_2 0x18100
+#define NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_3 0x18104
+#define NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_4 0x18108
+#define NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_5 0x18690
+#define NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_6 0x18694
+#define NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_7 0x18698
+#define NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_8 0x1869c
/* [RW 12] Specify the number of strict priority arbitration slots between
* two round-robin arbitration slots to avoid starvation. A value of 0 means
* no strict priority cycles - the strict priority with anti-starvation
@@ -1925,8 +2139,36 @@
* for management at priority 0; debug traffic at priorities 1 and 2; COS0
* traffic at priority 3; and COS1 traffic at priority 4. */
#define NIG_REG_P0_TX_ARB_PRIORITY_CLIENT 0x180e4
+/* [RW 6] Bit-map indicating which L2 hdrs may appear after the basic
+ * Ethernet header. */
+#define NIG_REG_P1_HDRS_AFTER_BASIC 0x1818c
#define NIG_REG_P1_LLH_FUNC_MEM2 0x184c0
#define NIG_REG_P1_LLH_FUNC_MEM2_ENABLE 0x18460
+/* [RW 32] Specify the client number to be assigned to each priority of the
+ * strict priority arbiter. This register specifies bits 31:0 of the 36-bit
+ * value. Priority 0 is the highest priority. Bits [3:0] are for priority 0
+ * client; bits [35-32] are for priority 8 client. The clients are assigned
+ * the following IDs: 0-management; 1-debug traffic from this port; 2-debug
+ * traffic from other port; 3-COS0 traffic; 4-COS1 traffic; 5-COS2 traffic;
+ * 6-COS3 traffic; 7-COS4 traffic; 8-COS5 traffic. The reset value[35:0] is
+ * set to 0x345678021. This is a new register (with 2_) added in E3 B0 to
+ * accommodate the 9 input clients to ETS arbiter. */
+#define NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_LSB 0x18680
+/* [RW 4] Specify the client number to be assigned to each priority of the
+ * strict priority arbiter. This register specifies bits 35:32 of the 36-bit
+ * value. Priority 0 is the highest priority. Bits [3:0] are for priority 0
+ * client; bits [35-32] are for priority 8 client. The clients are assigned
+ * the following IDs: 0-management; 1-debug traffic from this port; 2-debug
+ * traffic from other port; 3-COS0 traffic; 4-COS1 traffic; 5-COS2 traffic;
+ * 6-COS3 traffic; 7-COS4 traffic; 8-COS5 traffic. The reset value[35:0] is
+ * set to 0x345678021. This is a new register (with 2_) added in E3 B0 to
+ * accommodate the 9 input clients to ETS arbiter. */
+#define NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_MSB 0x18684
+#define NIG_REG_P1_MAC_IN_EN 0x185c0
+/* [RW 1] Output enable for TX MAC interface */
+#define NIG_REG_P1_MAC_OUT_EN 0x185c4
+/* [RW 1] Output enable for TX PAUSE signal to the MAC. */
+#define NIG_REG_P1_MAC_PAUSE_OUT_EN 0x185c8
/* [RW 32] Eight 4-bit configurations for specifying which COS (0-15 for
* future expansion) each priorty is to be mapped to. Bits 3:0 specify the
* COS for priority 0. Bits 31:28 specify the COS for priority 7. The 3-bit
@@ -1943,6 +2185,105 @@
* than one bit may be set; allowing multiple priorities to be mapped to one
* COS. */
#define NIG_REG_P1_RX_COS1_PRIORITY_MASK 0x181b0
+/* [RW 16] Bit-map indicating which SAFC/PFC priorities to map to COS 2. A
+ * priority is mapped to COS 2 when the corresponding mask bit is 1. More
+ * than one bit may be set; allowing multiple priorities to be mapped to one
+ * COS. */
+#define NIG_REG_P1_RX_COS2_PRIORITY_MASK 0x186f8
+/* [R 1] RX FIFO for receiving data from MAC is empty. */
+#define NIG_REG_P1_RX_MACFIFO_EMPTY 0x1858c
+/* [R 1] TLLH FIFO is empty. */
+#define NIG_REG_P1_TLLH_FIFO_EMPTY 0x18338
+/* [RW 32] Specify which of the credit registers the client is to be mapped
+ * to. This register specifies bits 31:0 of the 36-bit value. Bits[3:0] are
+ * for client 0; bits [35:32] are for client 8. For clients that are not
+ * subject to WFQ credit blocking - their specifications here are not used.
+ * This is a new register (with 2_) added in E3 B0 to accommodate the 9
+ * input clients to ETS arbiter. The reset default is set for management and
+ * debug to use credit registers 6, 7, and 8, respectively, and COSes 0-5 to
+ * use credit registers 0-5 respectively (0x543210876). Note that credit
+ * registers can not be shared between clients. Note also that there are
+ * only COS0-2 in port 1- there is a total of 6 clients in port 1. Only
+ * credit registers 0-5 are valid. This register should be configured
+ * appropriately before enabling WFQ. */
+#define NIG_REG_P1_TX_ARB_CLIENT_CREDIT_MAP2_LSB 0x186e8
+/* [RW 4] Specify which of the credit registers the client is to be mapped
+ * to. This register specifies bits 35:32 of the 36-bit value. Bits[3:0] are
+ * for client 0; bits [35:32] are for client 8. For clients that are not
+ * subject to WFQ credit blocking - their specifications here are not used.
+ * This is a new register (with 2_) added in E3 B0 to accommodate the 9
+ * input clients to ETS arbiter. The reset default is set for management and
+ * debug to use credit registers 6, 7, and 8, respectively, and COSes 0-5 to
+ * use credit registers 0-5 respectively (0x543210876). Note that credit
+ * registers can not be shared between clients. Note also that there are
+ * only COS0-2 in port 1- there is a total of 6 clients in port 1. Only
+ * credit registers 0-5 are valid. This register should be configured
+ * appropriately before enabling WFQ. */
+#define NIG_REG_P1_TX_ARB_CLIENT_CREDIT_MAP2_MSB 0x186ec
+/* [RW 9] Specify whether the client competes directly in the strict
+ * priority arbiter. The bits are mapped according to client ID (client IDs
+ * are defined in tx_arb_priority_client2): 0-management; 1-debug traffic
+ * from this port; 2-debug traffic from other port; 3-COS0 traffic; 4-COS1
+ * traffic; 5-COS2 traffic; 6-COS3 traffic; 7-COS4 traffic; 8-COS5 traffic.
+ * Default value is set to enable strict priorities for all clients. */
+#define NIG_REG_P1_TX_ARB_CLIENT_IS_STRICT 0x18234
+/* [RW 9] Specify whether the client is subject to WFQ credit blocking. The
+ * bits are mapped according to client ID (client IDs are defined in
+ * tx_arb_priority_client2): 0-management; 1-debug traffic from this port;
+ * 2-debug traffic from other port; 3-COS0 traffic; 4-COS1 traffic; 5-COS2
+ * traffic; 6-COS3 traffic; 7-COS4 traffic; 8-COS5 traffic. Default value is
+ * 0 for not using WFQ credit blocking. */
+#define NIG_REG_P1_TX_ARB_CLIENT_IS_SUBJECT2WFQ 0x18238
+#define NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_0 0x18258
+#define NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_1 0x1825c
+#define NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_2 0x18260
+#define NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_3 0x18264
+#define NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_4 0x18268
+#define NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_5 0x186f4
+/* [RW 32] Specify the weight (in bytes) to be added to credit register 0
+ * when it is time to increment. */
+#define NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_0 0x18244
+#define NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_1 0x18248
+#define NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_2 0x1824c
+#define NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_3 0x18250
+#define NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_4 0x18254
+#define NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_5 0x186f0
+/* [RW 12] Specify the number of strict priority arbitration slots between
+ two round-robin arbitration slots to avoid starvation. A value of 0 means
+ no strict priority cycles - the strict priority with anti-starvation
+ arbiter becomes a round-robin arbiter. */
+#define NIG_REG_P1_TX_ARB_NUM_STRICT_ARB_SLOTS 0x18240
+/* [RW 32] Specify the client number to be assigned to each priority of the
+ strict priority arbiter. This register specifies bits 31:0 of the 36-bit
+ value. Priority 0 is the highest priority. Bits [3:0] are for priority 0
+ client; bits [35-32] are for priority 8 client. The clients are assigned
+ the following IDs: 0-management; 1-debug traffic from this port; 2-debug
+ traffic from other port; 3-COS0 traffic; 4-COS1 traffic; 5-COS2 traffic;
+ 6-COS3 traffic; 7-COS4 traffic; 8-COS5 traffic. The reset value[35:0] is
+ set to 0x345678021. This is a new register (with 2_) added in E3 B0 to
+ accommodate the 9 input clients to ETS arbiter. Note that this register
+ is the same as the one for port 0, except that port 1 only has COS 0-2
+ traffic. There is no traffic for COS 3-5 of port 1. */
+#define NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_LSB 0x186e0
+/* [RW 4] Specify the client number to be assigned to each priority of the
+ strict priority arbiter. This register specifies bits 35:32 of the 36-bit
+ value. Priority 0 is the highest priority. Bits [3:0] are for priority 0
+ client; bits [35-32] are for priority 8 client. The clients are assigned
+ the following IDs: 0-management; 1-debug traffic from this port; 2-debug
+ traffic from other port; 3-COS0 traffic; 4-COS1 traffic; 5-COS2 traffic;
+ 6-COS3 traffic; 7-COS4 traffic; 8-COS5 traffic. The reset value[35:0] is
+ set to 0x345678021. This is a new register (with 2_) added in E3 B0 to
+ accommodate the 9 input clients to ETS arbiter. Note that this register
+ is the same as the one for port 0, except that port 1 only has COS 0-2
+ traffic. There is no traffic for COS 3-5 of port 1. */
+#define NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_MSB 0x186e4
+/* [R 1] TX FIFO for transmitting data to MAC is empty. */
+#define NIG_REG_P1_TX_MACFIFO_EMPTY 0x18594
+/* [R 1] FIFO empty status of the MCP TX FIFO used for storing MCP packets
+ forwarded to the host. */
+#define NIG_REG_P1_TX_MNG_HOST_FIFO_EMPTY 0x182b8
+/* [RW 32] Specify the upper bound that credit register 0 is allowed to
+ * reach. */
/* [RW 1] Pause enable for port0. This register may get 1 only when
~safc_enable.safc_enable = 0 and ppp_enable.ppp_enable =0 for the same
port */
@@ -2026,12 +2367,45 @@
#define NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_XGXS0_LINK_STATUS_SIZE 18
/* [RW 31] The upper bound of the weight of COS0 in the ETS command arbiter. */
#define PBF_REG_COS0_UPPER_BOUND 0x15c05c
+/* [RW 31] The upper bound of the weight of COS0 in the ETS command arbiter
+ * of port 0. */
+#define PBF_REG_COS0_UPPER_BOUND_P0 0x15c2cc
+/* [RW 31] The upper bound of the weight of COS0 in the ETS command arbiter
+ * of port 1. */
+#define PBF_REG_COS0_UPPER_BOUND_P1 0x15c2e4
/* [RW 31] The weight of COS0 in the ETS command arbiter. */
#define PBF_REG_COS0_WEIGHT 0x15c054
+/* [RW 31] The weight of COS0 in port 0 ETS command arbiter. */
+#define PBF_REG_COS0_WEIGHT_P0 0x15c2a8
+/* [RW 31] The weight of COS0 in port 1 ETS command arbiter. */
+#define PBF_REG_COS0_WEIGHT_P1 0x15c2c0
/* [RW 31] The upper bound of the weight of COS1 in the ETS command arbiter. */
#define PBF_REG_COS1_UPPER_BOUND 0x15c060
/* [RW 31] The weight of COS1 in the ETS command arbiter. */
#define PBF_REG_COS1_WEIGHT 0x15c058
+/* [RW 31] The weight of COS1 in port 0 ETS command arbiter. */
+#define PBF_REG_COS1_WEIGHT_P0 0x15c2ac
+/* [RW 31] The weight of COS1 in port 1 ETS command arbiter. */
+#define PBF_REG_COS1_WEIGHT_P1 0x15c2c4
+/* [RW 31] The weight of COS2 in port 0 ETS command arbiter. */
+#define PBF_REG_COS2_WEIGHT_P0 0x15c2b0
+/* [RW 31] The weight of COS2 in port 1 ETS command arbiter. */
+#define PBF_REG_COS2_WEIGHT_P1 0x15c2c8
+/* [RW 31] The weight of COS3 in port 0 ETS command arbiter. */
+#define PBF_REG_COS3_WEIGHT_P0 0x15c2b4
+/* [RW 31] The weight of COS4 in port 0 ETS command arbiter. */
+#define PBF_REG_COS4_WEIGHT_P0 0x15c2b8
+/* [RW 31] The weight of COS5 in port 0 ETS command arbiter. */
+#define PBF_REG_COS5_WEIGHT_P0 0x15c2bc
+/* [R 11] Current credit for the LB queue in the tx port buffers in 16 byte
+ * lines. */
+#define PBF_REG_CREDIT_LB_Q 0x140338
+/* [R 11] Current credit for queue 0 in the tx port buffers in 16 byte
+ * lines. */
+#define PBF_REG_CREDIT_Q0 0x14033c
+/* [R 11] Current credit for queue 1 in the tx port buffers in 16 byte
+ * lines. */
+#define PBF_REG_CREDIT_Q1 0x140340
/* [RW 1] Disable processing further tasks from port 0 (after ending the
current task in process). */
#define PBF_REG_DISABLE_NEW_TASK_PROC_P0 0x14005c
@@ -2042,6 +2416,52 @@
current task in process). */
#define PBF_REG_DISABLE_NEW_TASK_PROC_P4 0x14006c
#define PBF_REG_DISABLE_PF 0x1402e8
+/* [RW 18] For port 0: For each client that is subject to WFQ (the
+ * corresponding bit is 1); indicates to which of the credit registers this
+ * client is mapped. For clients which are not credit blocked; their mapping
+ * is dont care. */
+#define PBF_REG_ETS_ARB_CLIENT_CREDIT_MAP_P0 0x15c288
+/* [RW 9] For port 1: For each client that is subject to WFQ (the
+ * corresponding bit is 1); indicates to which of the credit registers this
+ * client is mapped. For clients which are not credit blocked; their mapping
+ * is dont care. */
+#define PBF_REG_ETS_ARB_CLIENT_CREDIT_MAP_P1 0x15c28c
+/* [RW 6] For port 0: Bit per client to indicate if the client competes in
+ * the strict priority arbiter directly (corresponding bit = 1); or first
+ * goes to the RR arbiter (corresponding bit = 0); and then competes in the
+ * lowest priority in the strict-priority arbiter. */
+#define PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P0 0x15c278
+/* [RW 3] For port 1: Bit per client to indicate if the client competes in
+ * the strict priority arbiter directly (corresponding bit = 1); or first
+ * goes to the RR arbiter (corresponding bit = 0); and then competes in the
+ * lowest priority in the strict-priority arbiter. */
+#define PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P1 0x15c27c
+/* [RW 6] For port 0: Bit per client to indicate if the client is subject to
+ * WFQ credit blocking (corresponding bit = 1). */
+#define PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P0 0x15c280
+/* [RW 3] For port 0: Bit per client to indicate if the client is subject to
+ * WFQ credit blocking (corresponding bit = 1). */
+#define PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P1 0x15c284
+/* [RW 16] For port 0: The number of strict priority arbitration slots
+ * between 2 RR arbitration slots. A value of 0 means no strict priority
+ * cycles; i.e. the strict-priority w/ anti-starvation arbiter is a RR
+ * arbiter. */
+#define PBF_REG_ETS_ARB_NUM_STRICT_ARB_SLOTS_P0 0x15c2a0
+/* [RW 16] For port 1: The number of strict priority arbitration slots
+ * between 2 RR arbitration slots. A value of 0 means no strict priority
+ * cycles; i.e. the strict-priority w/ anti-starvation arbiter is a RR
+ * arbiter. */
+#define PBF_REG_ETS_ARB_NUM_STRICT_ARB_SLOTS_P1 0x15c2a4
+/* [RW 18] For port 0: Indicates which client is connected to each priority
+ * in the strict-priority arbiter. Priority 0 is the highest priority, and
+ * priority 5 is the lowest; to which the RR output is connected to (this is
+ * not configurable). */
+#define PBF_REG_ETS_ARB_PRIORITY_CLIENT_P0 0x15c270
+/* [RW 9] For port 1: Indicates which client is connected to each priority
+ * in the strict-priority arbiter. Priority 0 is the highest priority, and
+ * priority 5 is the lowest; to which the RR output is connected to (this is
+ * not configurable). */
+#define PBF_REG_ETS_ARB_PRIORITY_CLIENT_P1 0x15c274
/* [RW 1] Indicates that ETS is performed between the COSes in the command
* arbiter. If reset strict priority w/ anti-starvation will be performed
* w/o WFQ. */
@@ -2049,14 +2469,25 @@
/* [RW 6] Bit-map indicating which L2 hdrs may appear after the basic
* Ethernet header. */
#define PBF_REG_HDRS_AFTER_BASIC 0x15c0a8
-/* [RW 1] Indicates which COS is conncted to the highest priority in the
- * command arbiter. */
+/* [RW 6] Bit-map indicating which L2 hdrs may appear after L2 tag 0 */
+#define PBF_REG_HDRS_AFTER_TAG_0 0x15c0b8
+/* [R 1] Removed for E3 B0 - Indicates which COS is conncted to the highest
+ * priority in the command arbiter. */
#define PBF_REG_HIGH_PRIORITY_COS_NUM 0x15c04c
#define PBF_REG_IF_ENABLE_REG 0x140044
/* [RW 1] Init bit. When set the initial credits are copied to the credit
registers (except the port credits). Should be set and then reset after
the configuration of the block has ended. */
#define PBF_REG_INIT 0x140000
+/* [RW 11] Initial credit for the LB queue in the tx port buffers in 16 byte
+ * lines. */
+#define PBF_REG_INIT_CRD_LB_Q 0x15c248
+/* [RW 11] Initial credit for queue 0 in the tx port buffers in 16 byte
+ * lines. */
+#define PBF_REG_INIT_CRD_Q0 0x15c230
+/* [RW 11] Initial credit for queue 1 in the tx port buffers in 16 byte
+ * lines. */
+#define PBF_REG_INIT_CRD_Q1 0x15c234
/* [RW 1] Init bit for port 0. When set the initial credit of port 0 is
copied to the credit register. Should be set and then reset after the
configuration of the port has ended. */
@@ -2069,6 +2500,15 @@
copied to the credit register. Should be set and then reset after the
configuration of the port has ended. */
#define PBF_REG_INIT_P4 0x14000c
+/* [R 32] Cyclic counter for the amount credits in 16 bytes lines added for
+ * the LB queue. Reset upon init. */
+#define PBF_REG_INTERNAL_CRD_FREED_CNT_LB_Q 0x140354
+/* [R 32] Cyclic counter for the amount credits in 16 bytes lines added for
+ * queue 0. Reset upon init. */
+#define PBF_REG_INTERNAL_CRD_FREED_CNT_Q0 0x140358
+/* [R 32] Cyclic counter for the amount credits in 16 bytes lines added for
+ * queue 1. Reset upon init. */
+#define PBF_REG_INTERNAL_CRD_FREED_CNT_Q1 0x14035c
/* [RW 1] Enable for mac interface 0. */
#define PBF_REG_MAC_IF0_ENABLE 0x140030
/* [RW 1] Enable for mac interface 1. */
@@ -2089,24 +2529,49 @@
/* [RW 11] Initial credit for port 0 in the tx port buffers in 16 byte
lines. */
#define PBF_REG_P0_INIT_CRD 0x1400d0
-/* [RW 1] Indication that pause is enabled for port 0. */
-#define PBF_REG_P0_PAUSE_ENABLE 0x140014
-/* [R 8] Number of tasks in port 0 task queue. */
+/* [R 32] Cyclic counter for the amount credits in 16 bytes lines added for
+ * port 0. Reset upon init. */
+#define PBF_REG_P0_INTERNAL_CRD_FREED_CNT 0x140308
+/* [R 1] Removed for E3 B0 - Indication that pause is enabled for port 0. */
+#define PBF_REG_P0_PAUSE_ENABLE 0x140014
+/* [R 8] Removed for E3 B0 - Number of tasks in port 0 task queue. */
#define PBF_REG_P0_TASK_CNT 0x140204
-/* [R 11] Current credit for port 1 in the tx port buffers in 16 byte lines. */
+/* [R 32] Removed for E3 B0 - Cyclic counter for number of 8 byte lines
+ * freed from the task queue of port 0. Reset upon init. */
+#define PBF_REG_P0_TQ_LINES_FREED_CNT 0x1402f0
+/* [R 12] Number of 8 bytes lines occupied in the task queue of port 0. */
+#define PBF_REG_P0_TQ_OCCUPANCY 0x1402fc
+/* [R 11] Removed for E3 B0 - Current credit for port 1 in the tx port
+ * buffers in 16 byte lines. */
#define PBF_REG_P1_CREDIT 0x140208
-/* [RW 11] Initial credit for port 1 in the tx port buffers in 16 byte
- lines. */
+/* [R 11] Removed for E3 B0 - Initial credit for port 0 in the tx port
+ * buffers in 16 byte lines. */
#define PBF_REG_P1_INIT_CRD 0x1400d4
-/* [R 8] Number of tasks in port 1 task queue. */
+/* [R 32] Cyclic counter for the amount credits in 16 bytes lines added for
+ * port 1. Reset upon init. */
+#define PBF_REG_P1_INTERNAL_CRD_FREED_CNT 0x14030c
+/* [R 8] Removed for E3 B0 - Number of tasks in port 1 task queue. */
#define PBF_REG_P1_TASK_CNT 0x14020c
+/* [R 32] Removed for E3 B0 - Cyclic counter for number of 8 byte lines
+ * freed from the task queue of port 1. Reset upon init. */
+#define PBF_REG_P1_TQ_LINES_FREED_CNT 0x1402f4
+/* [R 12] Number of 8 bytes lines occupied in the task queue of port 1. */
+#define PBF_REG_P1_TQ_OCCUPANCY 0x140300
/* [R 11] Current credit for port 4 in the tx port buffers in 16 byte lines. */
#define PBF_REG_P4_CREDIT 0x140210
/* [RW 11] Initial credit for port 4 in the tx port buffers in 16 byte
lines. */
#define PBF_REG_P4_INIT_CRD 0x1400e0
-/* [R 8] Number of tasks in port 4 task queue. */
+/* [R 32] Cyclic counter for the amount credits in 16 bytes lines added for
+ * port 4. Reset upon init. */
+#define PBF_REG_P4_INTERNAL_CRD_FREED_CNT 0x140310
+/* [R 8] Removed for E3 B0 - Number of tasks in port 4 task queue. */
#define PBF_REG_P4_TASK_CNT 0x140214
+/* [R 32] Removed for E3 B0 - Cyclic counter for number of 8 byte lines
+ * freed from the task queue of port 4. Reset upon init. */
+#define PBF_REG_P4_TQ_LINES_FREED_CNT 0x1402f8
+/* [R 12] Number of 8 bytes lines occupied in the task queue of port 4. */
+#define PBF_REG_P4_TQ_OCCUPANCY 0x140304
/* [RW 5] Interrupt mask register #0 read/write */
#define PBF_REG_PBF_INT_MASK 0x1401d4
/* [R 5] Interrupt register #0 read */
@@ -2115,6 +2580,27 @@
#define PBF_REG_PBF_PRTY_MASK 0x1401e4
/* [RC 20] Parity register #0 read clear */
#define PBF_REG_PBF_PRTY_STS_CLR 0x1401dc
+/* [RW 16] The Ethernet type value for L2 tag 0 */
+#define PBF_REG_TAG_ETHERTYPE_0 0x15c090
+/* [RW 4] The length of the info field for L2 tag 0. The length is between
+ * 2B and 14B; in 2B granularity */
+#define PBF_REG_TAG_LEN_0 0x15c09c
+/* [R 32] Cyclic counter for number of 8 byte lines freed from the LB task
+ * queue. Reset upon init. */
+#define PBF_REG_TQ_LINES_FREED_CNT_LB_Q 0x14038c
+/* [R 32] Cyclic counter for number of 8 byte lines freed from the task
+ * queue 0. Reset upon init. */
+#define PBF_REG_TQ_LINES_FREED_CNT_Q0 0x140390
+/* [R 32] Cyclic counter for number of 8 byte lines freed from task queue 1.
+ * Reset upon init. */
+#define PBF_REG_TQ_LINES_FREED_CNT_Q1 0x140394
+/* [R 13] Number of 8 bytes lines occupied in the task queue of the LB
+ * queue. */
+#define PBF_REG_TQ_OCCUPANCY_LB_Q 0x1403a8
+/* [R 13] Number of 8 bytes lines occupied in the task queue of queue 0. */
+#define PBF_REG_TQ_OCCUPANCY_Q0 0x1403ac
+/* [R 13] Number of 8 bytes lines occupied in the task queue of queue 1. */
+#define PBF_REG_TQ_OCCUPANCY_Q1 0x1403b0
#define PB_REG_CONTROL 0
/* [RW 2] Interrupt mask register #0 read/write */
#define PB_REG_PB_INT_MASK 0x28
@@ -2206,8 +2692,12 @@
#define PGLUE_B_REG_PGLUE_B_INT_STS 0x9298
/* [RC 9] Interrupt register #0 read clear */
#define PGLUE_B_REG_PGLUE_B_INT_STS_CLR 0x929c
+/* [RW 2] Parity mask register #0 read/write */
+#define PGLUE_B_REG_PGLUE_B_PRTY_MASK 0x92b4
/* [R 2] Parity register #0 read */
#define PGLUE_B_REG_PGLUE_B_PRTY_STS 0x92a8
+/* [RC 2] Parity register #0 read clear */
+#define PGLUE_B_REG_PGLUE_B_PRTY_STS_CLR 0x92ac
/* [R 13] Details of first request received with error. [2:0] - PFID. [3] -
* VF_VALID. [9:4] - VFID. [11:10] - Error Code - 0 - Indicates Completion
* Timeout of a User Tx non-posted request. 1 - unsupported request. 2 -
@@ -2444,10 +2934,24 @@
/* [RW 6] Bit-map indicating which L2 hdrs may appear after the basic
* Ethernet header. */
#define PRS_REG_HDRS_AFTER_BASIC 0x40238
+/* [RW 6] Bit-map indicating which L2 hdrs may appear after the basic
+ * Ethernet header for port 0 packets. */
+#define PRS_REG_HDRS_AFTER_BASIC_PORT_0 0x40270
+#define PRS_REG_HDRS_AFTER_BASIC_PORT_1 0x40290
+/* [R 6] Bit-map indicating which L2 hdrs may appear after L2 tag 0 */
+#define PRS_REG_HDRS_AFTER_TAG_0 0x40248
+/* [RW 6] Bit-map indicating which L2 hdrs may appear after L2 tag 0 for
+ * port 0 packets */
+#define PRS_REG_HDRS_AFTER_TAG_0_PORT_0 0x40280
+#define PRS_REG_HDRS_AFTER_TAG_0_PORT_1 0x402a0
/* [RW 4] The increment value to send in the CFC load request message */
#define PRS_REG_INC_VALUE 0x40048
/* [RW 6] Bit-map indicating which headers must appear in the packet */
#define PRS_REG_MUST_HAVE_HDRS 0x40254
+/* [RW 6] Bit-map indicating which headers must appear in the packet for
+ * port 0 packets */
+#define PRS_REG_MUST_HAVE_HDRS_PORT_0 0x4028c
+#define PRS_REG_MUST_HAVE_HDRS_PORT_1 0x402ac
#define PRS_REG_NIC_MODE 0x40138
/* [RW 8] The 8-bit event ID for cases where there is no match on the
connection. Used in packet start message to TCM. */
@@ -2496,6 +3000,11 @@
#define PRS_REG_SERIAL_NUM_STATUS_MSB 0x40158
/* [R 4] debug only: SRC current credit. Transaction based. */
#define PRS_REG_SRC_CURRENT_CREDIT 0x4016c
+/* [RW 16] The Ethernet type value for L2 tag 0 */
+#define PRS_REG_TAG_ETHERTYPE_0 0x401d4
+/* [RW 4] The length of the info field for L2 tag 0. The length is between
+ * 2B and 14B; in 2B granularity */
+#define PRS_REG_TAG_LEN_0 0x4022c
/* [R 8] debug only: TCM current credit. Cycle based. */
#define PRS_REG_TCM_CURRENT_CREDIT 0x40160
/* [R 8] debug only: TSDM current credit. Transaction based. */
@@ -2510,11 +3019,27 @@
/* [R 6] Debug only: Number of used entries in the data FIFO */
#define PXP2_REG_HST_DATA_FIFO_STATUS 0x12047c
/* [R 7] Debug only: Number of used entries in the header FIFO */
-#define PXP2_REG_HST_HEADER_FIFO_STATUS 0x120478
-#define PXP2_REG_PGL_ADDR_88_F0 0x120534
-#define PXP2_REG_PGL_ADDR_8C_F0 0x120538
-#define PXP2_REG_PGL_ADDR_90_F0 0x12053c
-#define PXP2_REG_PGL_ADDR_94_F0 0x120540
+#define PXP2_REG_HST_HEADER_FIFO_STATUS 0x120478
+#define PXP2_REG_PGL_ADDR_88_F0 0x120534
+/* [R 32] GRC address for configuration access to PCIE config address 0x88.
+ * any write to this PCIE address will cause a GRC write access to the
+ * address that's in t this register */
+#define PXP2_REG_PGL_ADDR_88_F1 0x120544
+#define PXP2_REG_PGL_ADDR_8C_F0 0x120538
+/* [R 32] GRC address for configuration access to PCIE config address 0x8c.
+ * any write to this PCIE address will cause a GRC write access to the
+ * address that's in t this register */
+#define PXP2_REG_PGL_ADDR_8C_F1 0x120548
+#define PXP2_REG_PGL_ADDR_90_F0 0x12053c
+/* [R 32] GRC address for configuration access to PCIE config address 0x90.
+ * any write to this PCIE address will cause a GRC write access to the
+ * address that's in t this register */
+#define PXP2_REG_PGL_ADDR_90_F1 0x12054c
+#define PXP2_REG_PGL_ADDR_94_F0 0x120540
+/* [R 32] GRC address for configuration access to PCIE config address 0x94.
+ * any write to this PCIE address will cause a GRC write access to the
+ * address that's in t this register */
+#define PXP2_REG_PGL_ADDR_94_F1 0x120550
#define PXP2_REG_PGL_CONTROL0 0x120490
#define PXP2_REG_PGL_CONTROL1 0x120514
#define PXP2_REG_PGL_DEBUG 0x120520
@@ -3080,6 +3605,7 @@
#define QM_REG_BYTECREDITAFULLTHR 0x168094
/* [RW 4] The initial credit for interface */
#define QM_REG_CMINITCRD_0 0x1680cc
+#define QM_REG_BYTECRDCMDQ_0 0x16e6e8
#define QM_REG_CMINITCRD_1 0x1680d0
#define QM_REG_CMINITCRD_2 0x1680d4
#define QM_REG_CMINITCRD_3 0x1680d8
@@ -3170,7 +3696,10 @@
/* [RW 2] The PCI attributes field used in the PCI request. */
#define QM_REG_PCIREQAT 0x168054
#define QM_REG_PF_EN 0x16e70c
-/* [R 16] The byte credit of port 0 */
+/* [R 24] The number of tasks stored in the QM for the PF. only even
+ * functions are valid in E2 (odd I registers will be hard wired to 0) */
+#define QM_REG_PF_USG_CNT_0 0x16e040
+/* [R 16] NOT USED */
#define QM_REG_PORT0BYTECRD 0x168300
/* [R 16] The byte credit of port 1 */
#define QM_REG_PORT1BYTECRD 0x168304
@@ -3725,7 +4254,7 @@
mechanism. The fields are: [5:0] - length of the message; 15:6] - message
pointer; 20:16] - next pointer. */
#define TCM_REG_XX_DESCR_TABLE 0x50280
-#define TCM_REG_XX_DESCR_TABLE_SIZE 32
+#define TCM_REG_XX_DESCR_TABLE_SIZE 29
/* [R 6] Use to read the value of XX protection Free counter. */
#define TCM_REG_XX_FREE 0x50178
/* [RW 6] Initial value for the credit counter; responsible for fulfilling
@@ -3782,6 +4311,8 @@
#define TM_REG_LIN0_LOGIC_ADDR 0x164240
/* [RW 18] Linear0 Max active cid (in banks of 32 entries). */
#define TM_REG_LIN0_MAX_ACTIVE_CID 0x164048
+/* [ST 16] Linear0 Number of scans counter. */
+#define TM_REG_LIN0_NUM_SCANS 0x1640a0
/* [WB 64] Linear0 phy address. */
#define TM_REG_LIN0_PHY_ADDR 0x164270
/* [RW 1] Linear0 physical address valid. */
@@ -3789,6 +4320,7 @@
#define TM_REG_LIN0_SCAN_ON 0x1640d0
/* [RW 24] Linear0 array scan timeout. */
#define TM_REG_LIN0_SCAN_TIME 0x16403c
+#define TM_REG_LIN0_VNIC_UC 0x164128
/* [RW 32] Linear1 logic address. */
#define TM_REG_LIN1_LOGIC_ADDR 0x164250
/* [WB 64] Linear1 phy address. */
@@ -4175,6 +4707,8 @@
#define UCM_REG_UCM_INT_MASK 0xe01d4
/* [R 11] Interrupt register #0 read */
#define UCM_REG_UCM_INT_STS 0xe01c8
+/* [RW 27] Parity mask register #0 read/write */
+#define UCM_REG_UCM_PRTY_MASK 0xe01e4
/* [R 27] Parity register #0 read */
#define UCM_REG_UCM_PRTY_STS 0xe01d8
/* [RC 27] Parity register #0 read clear */
@@ -4248,7 +4782,7 @@
mechanism. The fields are:[5:0] - message length; 14:6] - message
pointer; 19:15] - next pointer. */
#define UCM_REG_XX_DESCR_TABLE 0xe0280
-#define UCM_REG_XX_DESCR_TABLE_SIZE 32
+#define UCM_REG_XX_DESCR_TABLE_SIZE 27
/* [R 6] Use to read the XX protection Free counter. */
#define UCM_REG_XX_FREE 0xe016c
/* [RW 6] Initial value for the credit counter; responsible for fulfilling
@@ -4265,6 +4799,25 @@
The fields are: [4:0] - tail pointer; 10:5] - Link List size; 15:11] -
header pointer. */
#define UCM_REG_XX_TABLE 0xe0300
+#define UMAC_COMMAND_CONFIG_REG_IGNORE_TX_PAUSE (0x1<<28)
+#define UMAC_COMMAND_CONFIG_REG_LOOP_ENA (0x1<<15)
+#define UMAC_COMMAND_CONFIG_REG_NO_LGTH_CHECK (0x1<<24)
+#define UMAC_COMMAND_CONFIG_REG_PAD_EN (0x1<<5)
+#define UMAC_COMMAND_CONFIG_REG_PAUSE_IGNORE (0x1<<8)
+#define UMAC_COMMAND_CONFIG_REG_PROMIS_EN (0x1<<4)
+#define UMAC_COMMAND_CONFIG_REG_RX_ENA (0x1<<1)
+#define UMAC_COMMAND_CONFIG_REG_SW_RESET (0x1<<13)
+#define UMAC_COMMAND_CONFIG_REG_TX_ENA (0x1<<0)
+#define UMAC_REG_COMMAND_CONFIG 0x8
+/* [RW 32] Register Bit 0 refers to Bit 16 of the MAC address; Bit 1 refers
+ * to bit 17 of the MAC address etc. */
+#define UMAC_REG_MAC_ADDR0 0xc
+/* [RW 16] Register Bit 0 refers to Bit 0 of the MAC address; Register Bit 1
+ * refers to Bit 1 of the MAC address etc. Bits 16 to 31 are reserved. */
+#define UMAC_REG_MAC_ADDR1 0x10
+/* [RW 14] Defines a 14-Bit maximum frame length used by the MAC receive
+ * logic to check frames. */
+#define UMAC_REG_MAXFR 0x14
/* [RW 8] The event id for aggregated interrupt 0 */
#define USDM_REG_AGG_INT_EVENT_0 0xc4038
#define USDM_REG_AGG_INT_EVENT_1 0xc403c
@@ -4696,8 +5249,13 @@
#define XCM_REG_XCM_INT_MASK 0x202b4
/* [R 14] Interrupt register #0 read */
#define XCM_REG_XCM_INT_STS 0x202a8
+/* [RW 30] Parity mask register #0 read/write */
+#define XCM_REG_XCM_PRTY_MASK 0x202c4
/* [R 30] Parity register #0 read */
#define XCM_REG_XCM_PRTY_STS 0x202b8
+/* [RC 30] Parity register #0 read clear */
+#define XCM_REG_XCM_PRTY_STS_CLR 0x202bc
+
/* [RW 4] The size of AG context region 0 in REG-pairs. Designates the MS
REG-pair number (e.g. if region 0 is 6 REG-pairs; the value should be 5).
Is used to determine the number of the AG context REG-pairs written back;
@@ -4772,6 +5330,34 @@
#define XCM_REG_XX_MSG_NUM 0x20428
/* [RW 8] The Event ID; sent to the STORM in case of XX overflow. */
#define XCM_REG_XX_OVFL_EVNT_ID 0x20058
+#define XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_LOCAL_FAULT_STATUS (0x1<<0)
+#define XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_REMOTE_FAULT_STATUS (0x1<<1)
+#define XMAC_CTRL_REG_LINE_LOCAL_LPBK (0x1<<2)
+#define XMAC_CTRL_REG_RX_EN (0x1<<1)
+#define XMAC_CTRL_REG_SOFT_RESET (0x1<<6)
+#define XMAC_CTRL_REG_TX_EN (0x1<<0)
+#define XMAC_PAUSE_CTRL_REG_RX_PAUSE_EN (0x1<<18)
+#define XMAC_PAUSE_CTRL_REG_TX_PAUSE_EN (0x1<<17)
+#define XMAC_PFC_CTRL_HI_REG_PFC_REFRESH_EN (0x1<<0)
+#define XMAC_PFC_CTRL_HI_REG_PFC_STATS_EN (0x1<<3)
+#define XMAC_PFC_CTRL_HI_REG_RX_PFC_EN (0x1<<4)
+#define XMAC_PFC_CTRL_HI_REG_TX_PFC_EN (0x1<<5)
+#define XMAC_REG_CLEAR_RX_LSS_STATUS 0x60
+#define XMAC_REG_CTRL 0
+/* [RW 16] Upper 48 bits of ctrl_sa register. Used as the SA in PAUSE/PFC
+ * packets transmitted by the MAC */
+#define XMAC_REG_CTRL_SA_HI 0x2c
+/* [RW 32] Lower 48 bits of ctrl_sa register. Used as the SA in PAUSE/PFC
+ * packets transmitted by the MAC */
+#define XMAC_REG_CTRL_SA_LO 0x28
+#define XMAC_REG_PAUSE_CTRL 0x68
+#define XMAC_REG_PFC_CTRL 0x70
+#define XMAC_REG_PFC_CTRL_HI 0x74
+#define XMAC_REG_RX_LSS_STATUS 0x58
+/* [RW 14] Maximum packet size in receive direction; exclusive of preamble &
+ * CRC in strip mode */
+#define XMAC_REG_RX_MAX_SIZE 0x40
+#define XMAC_REG_TX_CTRL 0x20
/* [RW 16] Indirect access to the XX table of the XX protection mechanism.
The fields are:[4:0] - tail pointer; 9:5] - Link List size; 14:10] -
header pointer. */
@@ -4846,6 +5432,10 @@
#define XSDM_REG_NUM_OF_Q9_CMD 0x166268
/* [RW 13] The start address in the internal RAM for queue counters */
#define XSDM_REG_Q_COUNTER_START_ADDR 0x166010
+/* [W 17] Generate an operation after completion; bit-16 is
+ * AggVectIdx_valid; bits 15:8 are AggVectIdx; bits 7:5 are the TRIG and
+ * bits 4:0 are the T124Param[4:0] */
+#define XSDM_REG_OPERATION_GEN 0x1664c4
/* [R 1] pxp_ctrl rd_data fifo empty in sdm_dma_rsp block */
#define XSDM_REG_RSP_PXP_CTRL_RDATA_EMPTY 0x166548
/* [R 1] parser fifo empty in sdm_sync block */
@@ -5019,6 +5609,7 @@
#define BIGMAC_REGISTER_CNT_MAX_SIZE (0x05<<3)
#define BIGMAC_REGISTER_RX_CONTROL (0x21<<3)
#define BIGMAC_REGISTER_RX_LLFC_MSG_FLDS (0x46<<3)
+#define BIGMAC_REGISTER_RX_LSS_STATUS (0x43<<3)
#define BIGMAC_REGISTER_RX_MAX_SIZE (0x23<<3)
#define BIGMAC_REGISTER_RX_STAT_GR64 (0x26<<3)
#define BIGMAC_REGISTER_RX_STAT_GRIPJ (0x42<<3)
@@ -5034,6 +5625,7 @@
#define BIGMAC2_REGISTER_PFC_CONTROL (0x06<<3)
#define BIGMAC2_REGISTER_RX_CONTROL (0x3A<<3)
#define BIGMAC2_REGISTER_RX_LLFC_MSG_FLDS (0x62<<3)
+#define BIGMAC2_REGISTER_RX_LSS_STAT (0x3E<<3)
#define BIGMAC2_REGISTER_RX_MAX_SIZE (0x3C<<3)
#define BIGMAC2_REGISTER_RX_STAT_GR64 (0x40<<3)
#define BIGMAC2_REGISTER_RX_STAT_GRIPJ (0x5f<<3)
@@ -5052,14 +5644,17 @@
#define EMAC_LED_OVERRIDE (1L<<0)
#define EMAC_LED_TRAFFIC (1L<<6)
#define EMAC_MDIO_COMM_COMMAND_ADDRESS (0L<<26)
+#define EMAC_MDIO_COMM_COMMAND_READ_22 (2L<<26)
#define EMAC_MDIO_COMM_COMMAND_READ_45 (3L<<26)
+#define EMAC_MDIO_COMM_COMMAND_WRITE_22 (1L<<26)
#define EMAC_MDIO_COMM_COMMAND_WRITE_45 (1L<<26)
#define EMAC_MDIO_COMM_DATA (0xffffL<<0)
#define EMAC_MDIO_COMM_START_BUSY (1L<<29)
#define EMAC_MDIO_MODE_AUTO_POLL (1L<<4)
#define EMAC_MDIO_MODE_CLAUSE_45 (1L<<31)
-#define EMAC_MDIO_MODE_CLOCK_CNT (0x3fL<<16)
+#define EMAC_MDIO_MODE_CLOCK_CNT (0x3ffL<<16)
#define EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT 16
+#define EMAC_MDIO_STATUS_10MB (1L<<1)
#define EMAC_MODE_25G_MODE (1L<<5)
#define EMAC_MODE_HALF_DUPLEX (1L<<1)
#define EMAC_MODE_PORT_GMII (2L<<2)
@@ -5070,6 +5665,7 @@
#define EMAC_REG_EMAC_MAC_MATCH 0x10
#define EMAC_REG_EMAC_MDIO_COMM 0xac
#define EMAC_REG_EMAC_MDIO_MODE 0xb4
+#define EMAC_REG_EMAC_MDIO_STATUS 0xb0
#define EMAC_REG_EMAC_MODE 0x0
#define EMAC_REG_EMAC_RX_MODE 0xc8
#define EMAC_REG_EMAC_RX_MTU_SIZE 0x9c
@@ -5128,16 +5724,30 @@
#define MISC_REGISTERS_RESET_REG_1_RST_PXPV (0x1<<27)
#define MISC_REGISTERS_RESET_REG_1_SET 0x584
#define MISC_REGISTERS_RESET_REG_2_CLEAR 0x598
+#define MISC_REGISTERS_RESET_REG_2_MSTAT0 (0x1<<24)
+#define MISC_REGISTERS_RESET_REG_2_MSTAT1 (0x1<<25)
+#define MISC_REGISTERS_RESET_REG_2_PGLC (0x1<<19)
+#define MISC_REGISTERS_RESET_REG_2_RST_ATC (0x1<<17)
#define MISC_REGISTERS_RESET_REG_2_RST_BMAC0 (0x1<<0)
+#define MISC_REGISTERS_RESET_REG_2_RST_BMAC1 (0x1<<1)
+#define MISC_REGISTERS_RESET_REG_2_RST_EMAC0 (0x1<<2)
#define MISC_REGISTERS_RESET_REG_2_RST_EMAC0_HARD_CORE (0x1<<14)
+#define MISC_REGISTERS_RESET_REG_2_RST_EMAC1 (0x1<<3)
#define MISC_REGISTERS_RESET_REG_2_RST_EMAC1_HARD_CORE (0x1<<15)
#define MISC_REGISTERS_RESET_REG_2_RST_GRC (0x1<<4)
#define MISC_REGISTERS_RESET_REG_2_RST_MCP_N_HARD_CORE_RST_B (0x1<<6)
+#define MISC_REGISTERS_RESET_REG_2_RST_MCP_N_RESET_CMN_CORE (0x1<<8)
+#define MISC_REGISTERS_RESET_REG_2_RST_MCP_N_RESET_CMN_CPU (0x1<<7)
#define MISC_REGISTERS_RESET_REG_2_RST_MCP_N_RESET_REG_HARD_CORE (0x1<<5)
#define MISC_REGISTERS_RESET_REG_2_RST_MDIO (0x1<<13)
#define MISC_REGISTERS_RESET_REG_2_RST_MISC_CORE (0x1<<11)
+#define MISC_REGISTERS_RESET_REG_2_RST_PCI_MDIO (0x1<<13)
#define MISC_REGISTERS_RESET_REG_2_RST_RBCN (0x1<<9)
#define MISC_REGISTERS_RESET_REG_2_SET 0x594
+#define MISC_REGISTERS_RESET_REG_2_UMAC0 (0x1<<20)
+#define MISC_REGISTERS_RESET_REG_2_UMAC1 (0x1<<21)
+#define MISC_REGISTERS_RESET_REG_2_XMAC (0x1<<22)
+#define MISC_REGISTERS_RESET_REG_2_XMAC_SOFT (0x1<<23)
#define MISC_REGISTERS_RESET_REG_3_CLEAR 0x5a8
#define MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_SERDES0_IDDQ (0x1<<1)
#define MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_SERDES0_PWRDWN (0x1<<2)
@@ -5160,74 +5770,86 @@
#define MISC_REGISTERS_SPIO_OUTPUT_HIGH 1
#define MISC_REGISTERS_SPIO_OUTPUT_LOW 0
#define MISC_REGISTERS_SPIO_SET_POS 8
+#define HW_LOCK_DRV_FLAGS 10
#define HW_LOCK_MAX_RESOURCE_VALUE 31
#define HW_LOCK_RESOURCE_GPIO 1
#define HW_LOCK_RESOURCE_MDIO 0
-#define HW_LOCK_RESOURCE_PORT0_ATT_MASK 3
-#define HW_LOCK_RESOURCE_RESERVED_08 8
+#define HW_LOCK_RESOURCE_PORT0_ATT_MASK 3
+#define HW_LOCK_RESOURCE_RECOVERY_LEADER_0 8
+#define HW_LOCK_RESOURCE_RECOVERY_LEADER_1 9
#define HW_LOCK_RESOURCE_SPIO 2
-#define HW_LOCK_RESOURCE_UNDI 5
-#define PRS_FLAG_OVERETH_IPV4 1
-#define AEU_INPUTS_ATTN_BITS_ATC_HW_INTERRUPT (0x1<<4)
-#define AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR (0x1<<5)
-#define AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR (1<<18)
-#define AEU_INPUTS_ATTN_BITS_CCM_HW_INTERRUPT (1<<31)
-#define AEU_INPUTS_ATTN_BITS_CDU_HW_INTERRUPT (1<<9)
-#define AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR (1<<8)
-#define AEU_INPUTS_ATTN_BITS_CFC_HW_INTERRUPT (1<<7)
-#define AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR (1<<6)
-#define AEU_INPUTS_ATTN_BITS_CSDM_HW_INTERRUPT (1<<29)
-#define AEU_INPUTS_ATTN_BITS_CSDM_PARITY_ERROR (1<<28)
-#define AEU_INPUTS_ATTN_BITS_CSEMI_HW_INTERRUPT (1<<1)
-#define AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR (1<<0)
-#define AEU_INPUTS_ATTN_BITS_DEBUG_PARITY_ERROR (1<<18)
-#define AEU_INPUTS_ATTN_BITS_DMAE_HW_INTERRUPT (1<<11)
-#define AEU_INPUTS_ATTN_BITS_DOORBELLQ_HW_INTERRUPT (1<<13)
-#define AEU_INPUTS_ATTN_BITS_DOORBELLQ_PARITY_ERROR (1<<12)
-#define AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0 (1<<5)
-#define AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1 (1<<9)
-#define AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR (1<<12)
-#define AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY (1<<28)
-#define AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY (1<<31)
-#define AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY (1<<29)
-#define AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY (1<<30)
-#define AEU_INPUTS_ATTN_BITS_MISC_HW_INTERRUPT (1<<15)
-#define AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR (1<<14)
-#define AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR (1<<20)
-#define AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR (1<<0)
-#define AEU_INPUTS_ATTN_BITS_PBF_HW_INTERRUPT (1<<31)
-#define AEU_INPUTS_ATTN_BITS_PGLUE_HW_INTERRUPT (0x1<<2)
-#define AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR (0x1<<3)
-#define AEU_INPUTS_ATTN_BITS_PXP_HW_INTERRUPT (1<<3)
-#define AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR (1<<2)
-#define AEU_INPUTS_ATTN_BITS_PXPPCICLOCKCLIENT_HW_INTERRUPT (1<<5)
-#define AEU_INPUTS_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR (1<<4)
-#define AEU_INPUTS_ATTN_BITS_QM_HW_INTERRUPT (1<<3)
-#define AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR (1<<2)
-#define AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR (1<<22)
-#define AEU_INPUTS_ATTN_BITS_SPIO5 (1<<15)
-#define AEU_INPUTS_ATTN_BITS_TCM_HW_INTERRUPT (1<<27)
-#define AEU_INPUTS_ATTN_BITS_TIMERS_HW_INTERRUPT (1<<5)
-#define AEU_INPUTS_ATTN_BITS_TSDM_HW_INTERRUPT (1<<25)
-#define AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR (1<<24)
-#define AEU_INPUTS_ATTN_BITS_TSEMI_HW_INTERRUPT (1<<29)
-#define AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR (1<<28)
-#define AEU_INPUTS_ATTN_BITS_UCM_HW_INTERRUPT (1<<23)
-#define AEU_INPUTS_ATTN_BITS_UPB_HW_INTERRUPT (1<<27)
-#define AEU_INPUTS_ATTN_BITS_UPB_PARITY_ERROR (1<<26)
-#define AEU_INPUTS_ATTN_BITS_USDM_HW_INTERRUPT (1<<21)
-#define AEU_INPUTS_ATTN_BITS_USDM_PARITY_ERROR (1<<20)
-#define AEU_INPUTS_ATTN_BITS_USEMI_HW_INTERRUPT (1<<25)
-#define AEU_INPUTS_ATTN_BITS_USEMI_PARITY_ERROR (1<<24)
-#define AEU_INPUTS_ATTN_BITS_VAUX_PCI_CORE_PARITY_ERROR (1<<16)
-#define AEU_INPUTS_ATTN_BITS_XCM_HW_INTERRUPT (1<<9)
-#define AEU_INPUTS_ATTN_BITS_XSDM_HW_INTERRUPT (1<<7)
-#define AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR (1<<6)
-#define AEU_INPUTS_ATTN_BITS_XSEMI_HW_INTERRUPT (1<<11)
-#define AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR (1<<10)
+#define HW_LOCK_RESOURCE_RESET 5
+#define AEU_INPUTS_ATTN_BITS_ATC_HW_INTERRUPT (0x1<<4)
+#define AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR (0x1<<5)
+#define AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR (0x1<<18)
+#define AEU_INPUTS_ATTN_BITS_CCM_HW_INTERRUPT (0x1<<31)
+#define AEU_INPUTS_ATTN_BITS_CCM_PARITY_ERROR (0x1<<30)
+#define AEU_INPUTS_ATTN_BITS_CDU_HW_INTERRUPT (0x1<<9)
+#define AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR (0x1<<8)
+#define AEU_INPUTS_ATTN_BITS_CFC_HW_INTERRUPT (0x1<<7)
+#define AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR (0x1<<6)
+#define AEU_INPUTS_ATTN_BITS_CSDM_HW_INTERRUPT (0x1<<29)
+#define AEU_INPUTS_ATTN_BITS_CSDM_PARITY_ERROR (0x1<<28)
+#define AEU_INPUTS_ATTN_BITS_CSEMI_HW_INTERRUPT (0x1<<1)
+#define AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR (0x1<<0)
+#define AEU_INPUTS_ATTN_BITS_DEBUG_PARITY_ERROR (0x1<<18)
+#define AEU_INPUTS_ATTN_BITS_DMAE_HW_INTERRUPT (0x1<<11)
+#define AEU_INPUTS_ATTN_BITS_DMAE_PARITY_ERROR (0x1<<10)
+#define AEU_INPUTS_ATTN_BITS_DOORBELLQ_HW_INTERRUPT (0x1<<13)
+#define AEU_INPUTS_ATTN_BITS_DOORBELLQ_PARITY_ERROR (0x1<<12)
+#define AEU_INPUTS_ATTN_BITS_GPIO0_FUNCTION_0 (0x1<<2)
+#define AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR (0x1<<12)
+#define AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY (0x1<<28)
+#define AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY (0x1<<31)
+#define AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY (0x1<<29)
+#define AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY (0x1<<30)
+#define AEU_INPUTS_ATTN_BITS_MISC_HW_INTERRUPT (0x1<<15)
+#define AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR (0x1<<14)
+#define AEU_INPUTS_ATTN_BITS_NIG_PARITY_ERROR (0x1<<14)
+#define AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR (0x1<<20)
+#define AEU_INPUTS_ATTN_BITS_PBCLIENT_HW_INTERRUPT (0x1<<31)
+#define AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR (0x1<<30)
+#define AEU_INPUTS_ATTN_BITS_PBF_PARITY_ERROR (0x1<<0)
+#define AEU_INPUTS_ATTN_BITS_PGLUE_HW_INTERRUPT (0x1<<2)
+#define AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR (0x1<<3)
+#define AEU_INPUTS_ATTN_BITS_PXPPCICLOCKCLIENT_HW_INTERRUPT (0x1<<5)
+#define AEU_INPUTS_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR (0x1<<4)
+#define AEU_INPUTS_ATTN_BITS_PXP_HW_INTERRUPT (0x1<<3)
+#define AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR (0x1<<2)
+#define AEU_INPUTS_ATTN_BITS_QM_HW_INTERRUPT (0x1<<3)
+#define AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR (0x1<<2)
+#define AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR (0x1<<22)
+#define AEU_INPUTS_ATTN_BITS_SPIO5 (0x1<<15)
+#define AEU_INPUTS_ATTN_BITS_TCM_HW_INTERRUPT (0x1<<27)
+#define AEU_INPUTS_ATTN_BITS_TCM_PARITY_ERROR (0x1<<26)
+#define AEU_INPUTS_ATTN_BITS_TIMERS_HW_INTERRUPT (0x1<<5)
+#define AEU_INPUTS_ATTN_BITS_TIMERS_PARITY_ERROR (0x1<<4)
+#define AEU_INPUTS_ATTN_BITS_TSDM_HW_INTERRUPT (0x1<<25)
+#define AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR (0x1<<24)
+#define AEU_INPUTS_ATTN_BITS_TSEMI_HW_INTERRUPT (0x1<<29)
+#define AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR (0x1<<28)
+#define AEU_INPUTS_ATTN_BITS_UCM_HW_INTERRUPT (0x1<<23)
+#define AEU_INPUTS_ATTN_BITS_UCM_PARITY_ERROR (0x1<<22)
+#define AEU_INPUTS_ATTN_BITS_UPB_HW_INTERRUPT (0x1<<27)
+#define AEU_INPUTS_ATTN_BITS_UPB_PARITY_ERROR (0x1<<26)
+#define AEU_INPUTS_ATTN_BITS_USDM_HW_INTERRUPT (0x1<<21)
+#define AEU_INPUTS_ATTN_BITS_USDM_PARITY_ERROR (0x1<<20)
+#define AEU_INPUTS_ATTN_BITS_USEMI_HW_INTERRUPT (0x1<<25)
+#define AEU_INPUTS_ATTN_BITS_USEMI_PARITY_ERROR (0x1<<24)
+#define AEU_INPUTS_ATTN_BITS_VAUX_PCI_CORE_PARITY_ERROR (0x1<<16)
+#define AEU_INPUTS_ATTN_BITS_XCM_HW_INTERRUPT (0x1<<9)
+#define AEU_INPUTS_ATTN_BITS_XCM_PARITY_ERROR (0x1<<8)
+#define AEU_INPUTS_ATTN_BITS_XSDM_HW_INTERRUPT (0x1<<7)
+#define AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR (0x1<<6)
+#define AEU_INPUTS_ATTN_BITS_XSEMI_HW_INTERRUPT (0x1<<11)
+#define AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR (0x1<<10)
+
+#define AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0 (0x1<<5)
+#define AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1 (0x1<<9)
+
#define RESERVED_GENERAL_ATTENTION_BIT_0 0
-#define EVEREST_GEN_ATTN_IN_USE_MASK 0x3ffe0
+#define EVEREST_GEN_ATTN_IN_USE_MASK 0x7ffe0
#define EVEREST_LATCHED_ATTN_IN_USE_MASK 0xffe00000
#define RESERVED_GENERAL_ATTENTION_BIT_6 6
@@ -5317,7 +5939,13 @@
#define GRCBASE_HC 0x108000
#define GRCBASE_PXP2 0x120000
#define GRCBASE_PBF 0x140000
+#define GRCBASE_UMAC0 0x160000
+#define GRCBASE_UMAC1 0x160400
#define GRCBASE_XPB 0x161000
+#define GRCBASE_MSTAT0 0x162000
+#define GRCBASE_MSTAT1 0x162800
+#define GRCBASE_XMAC0 0x163000
+#define GRCBASE_XMAC1 0x163800
#define GRCBASE_TIMERS 0x164000
#define GRCBASE_XSDM 0x166000
#define GRCBASE_QM 0x168000
@@ -5883,6 +6511,10 @@
#define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_16G 0x0C00
#define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_1G_KX 0x0D00
#define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_KX4 0x0E00
+#define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_KR 0x0F00
+#define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_XFI 0x1B00
+#define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_20G_DXGXS 0x1E00
+#define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_SFI 0x1F00
#define MDIO_REG_BANK_10G_PARALLEL_DETECT 0x8130
@@ -6032,15 +6664,11 @@ Theotherbitsarereservedandshouldbezero*/
#define MDIO_PMA_REG_CTRL 0x0
#define MDIO_PMA_REG_STATUS 0x1
#define MDIO_PMA_REG_10G_CTRL2 0x7
+#define MDIO_PMA_REG_TX_DISABLE 0x0009
#define MDIO_PMA_REG_RX_SD 0xa
/*bcm*/
#define MDIO_PMA_REG_BCM_CTRL 0x0096
#define MDIO_PMA_REG_FEC_CTRL 0x00ab
-#define MDIO_PMA_REG_RX_ALARM_CTRL 0x9000
-#define MDIO_PMA_REG_LASI_CTRL 0x9002
-#define MDIO_PMA_REG_RX_ALARM 0x9003
-#define MDIO_PMA_REG_TX_ALARM 0x9004
-#define MDIO_PMA_REG_LASI_STATUS 0x9005
#define MDIO_PMA_REG_PHY_IDENTIFIER 0xc800
#define MDIO_PMA_REG_DIGITAL_CTRL 0xc808
#define MDIO_PMA_REG_DIGITAL_STATUS 0xc809
@@ -6201,6 +6829,172 @@ Theotherbitsarereservedandshouldbezero*/
#define MDIO_PMA_REG_84823_CTL_LED_CTL_1 0xa8e3
#define MDIO_PMA_REG_84823_LED3_STRETCH_EN 0x0080
+/* BCM84833 only */
+#define MDIO_84833_TOP_CFG_XGPHY_STRAP1 0x401a
+#define MDIO_84833_SUPER_ISOLATE 0x8000
+/* These are mailbox register set used by 84833. */
+#define MDIO_84833_TOP_CFG_SCRATCH_REG0 0x4005
+#define MDIO_84833_TOP_CFG_SCRATCH_REG1 0x4006
+#define MDIO_84833_TOP_CFG_SCRATCH_REG2 0x4007
+#define MDIO_84833_TOP_CFG_SCRATCH_REG3 0x4008
+#define MDIO_84833_TOP_CFG_SCRATCH_REG4 0x4009
+#define MDIO_84833_TOP_CFG_DATA3_REG 0x4011
+#define MDIO_84833_TOP_CFG_DATA4_REG 0x4012
+
+/* Mailbox command set used by 84833. */
+#define PHY84833_DIAG_CMD_PAIR_SWAP_CHANGE 0x2
+/* Mailbox status set used by 84833. */
+#define PHY84833_CMD_RECEIVED 0x0001
+#define PHY84833_CMD_IN_PROGRESS 0x0002
+#define PHY84833_CMD_COMPLETE_PASS 0x0004
+#define PHY84833_CMD_COMPLETE_ERROR 0x0008
+#define PHY84833_CMD_OPEN_FOR_CMDS 0x0010
+#define PHY84833_CMD_SYSTEM_BOOT 0x0020
+#define PHY84833_CMD_NOT_OPEN_FOR_CMDS 0x0040
+#define PHY84833_CMD_CLEAR_COMPLETE 0x0080
+#define PHY84833_CMD_OPEN_OVERRIDE 0xa5a5
+
+
+/* 84833 F/W Feature Commands */
+#define PHY84833_DIAG_CMD_GET_EEE_MODE 0x27
+#define PHY84833_DIAG_CMD_SET_EEE_MODE 0x28
+
+/* Warpcore clause 45 addressing */
+#define MDIO_WC_DEVAD 0x3
+#define MDIO_WC_REG_IEEE0BLK_MIICNTL 0x0
+#define MDIO_WC_REG_IEEE0BLK_AUTONEGNP 0x7
+#define MDIO_WC_REG_AN_IEEE1BLK_AN_ADVERTISEMENT0 0x10
+#define MDIO_WC_REG_AN_IEEE1BLK_AN_ADVERTISEMENT1 0x11
+#define MDIO_WC_REG_AN_IEEE1BLK_AN_ADVERTISEMENT2 0x12
+#define MDIO_WC_REG_AN_IEEE1BLK_AN_ADV2_FEC_ABILITY 0x4000
+#define MDIO_WC_REG_AN_IEEE1BLK_AN_ADV2_FEC_REQ 0x8000
+#define MDIO_WC_REG_PMD_IEEE9BLK_TENGBASE_KR_PMD_CONTROL_REGISTER_150 0x96
+#define MDIO_WC_REG_XGXSBLK0_XGXSCONTROL 0x8000
+#define MDIO_WC_REG_XGXSBLK0_MISCCONTROL1 0x800e
+#define MDIO_WC_REG_XGXSBLK1_DESKEW 0x8010
+#define MDIO_WC_REG_XGXSBLK1_LANECTRL0 0x8015
+#define MDIO_WC_REG_XGXSBLK1_LANECTRL1 0x8016
+#define MDIO_WC_REG_XGXSBLK1_LANECTRL2 0x8017
+#define MDIO_WC_REG_TX0_ANA_CTRL0 0x8061
+#define MDIO_WC_REG_TX1_ANA_CTRL0 0x8071
+#define MDIO_WC_REG_TX2_ANA_CTRL0 0x8081
+#define MDIO_WC_REG_TX3_ANA_CTRL0 0x8091
+#define MDIO_WC_REG_TX0_TX_DRIVER 0x8067
+#define MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET 0x04
+#define MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_MASK 0x00f0
+#define MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET 0x08
+#define MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_MASK 0x0f00
+#define MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET 0x0c
+#define MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_MASK 0x7000
+#define MDIO_WC_REG_TX1_TX_DRIVER 0x8077
+#define MDIO_WC_REG_TX2_TX_DRIVER 0x8087
+#define MDIO_WC_REG_TX3_TX_DRIVER 0x8097
+#define MDIO_WC_REG_RX0_ANARXCONTROL1G 0x80b9
+#define MDIO_WC_REG_RX2_ANARXCONTROL1G 0x80d9
+#define MDIO_WC_REG_RX0_PCI_CTRL 0x80ba
+#define MDIO_WC_REG_RX1_PCI_CTRL 0x80ca
+#define MDIO_WC_REG_RX2_PCI_CTRL 0x80da
+#define MDIO_WC_REG_RX3_PCI_CTRL 0x80ea
+#define MDIO_WC_REG_XGXSBLK2_UNICORE_MODE_10G 0x8104
+#define MDIO_WC_REG_XGXS_STATUS3 0x8129
+#define MDIO_WC_REG_PAR_DET_10G_STATUS 0x8130
+#define MDIO_WC_REG_PAR_DET_10G_CTRL 0x8131
+#define MDIO_WC_REG_XGXS_X2_CONTROL2 0x8141
+#define MDIO_WC_REG_XGXS_RX_LN_SWAP1 0x816B
+#define MDIO_WC_REG_XGXS_TX_LN_SWAP1 0x8169
+#define MDIO_WC_REG_GP2_STATUS_GP_2_0 0x81d0
+#define MDIO_WC_REG_GP2_STATUS_GP_2_1 0x81d1
+#define MDIO_WC_REG_GP2_STATUS_GP_2_2 0x81d2
+#define MDIO_WC_REG_GP2_STATUS_GP_2_3 0x81d3
+#define MDIO_WC_REG_GP2_STATUS_GP_2_4 0x81d4
+#define MDIO_WC_REG_UC_INFO_B0_DEAD_TRAP 0x81EE
+#define MDIO_WC_REG_UC_INFO_B1_VERSION 0x81F0
+#define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE 0x81F2
+#define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_LANE0_OFFSET 0x0
+#define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE_DEFAULT 0x0
+#define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE_SFP_OPT_LR 0x1
+#define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE_SFP_DAC 0x2
+#define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE_SFP_XLAUI 0x3
+#define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE_LONG_CH_6G 0x4
+#define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_LANE1_OFFSET 0x4
+#define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_LANE2_OFFSET 0x8
+#define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_LANE3_OFFSET 0xc
+#define MDIO_WC_REG_UC_INFO_B1_CRC 0x81FE
+#define MDIO_WC_REG_DSC_SMC 0x8213
+#define MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0 0x821e
+#define MDIO_WC_REG_TX_FIR_TAP 0x82e2
+#define MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_OFFSET 0x00
+#define MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_MASK 0x000f
+#define MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_OFFSET 0x04
+#define MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_MASK 0x03f0
+#define MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET 0x0a
+#define MDIO_WC_REG_TX_FIR_TAP_POST_TAP_MASK 0x7c00
+#define MDIO_WC_REG_TX_FIR_TAP_ENABLE 0x8000
+#define MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL 0x82e3
+#define MDIO_WC_REG_CL72_USERB0_CL72_OS_DEF_CTRL 0x82e6
+#define MDIO_WC_REG_CL72_USERB0_CL72_BR_DEF_CTRL 0x82e7
+#define MDIO_WC_REG_CL72_USERB0_CL72_2P5_DEF_CTRL 0x82e8
+#define MDIO_WC_REG_CL72_USERB0_CL72_MISC4_CONTROL 0x82ec
+#define MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1 0x8300
+#define MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2 0x8301
+#define MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3 0x8302
+#define MDIO_WC_REG_SERDESDIGITAL_STATUS1000X1 0x8304
+#define MDIO_WC_REG_SERDESDIGITAL_MISC1 0x8308
+#define MDIO_WC_REG_SERDESDIGITAL_MISC2 0x8309
+#define MDIO_WC_REG_DIGITAL3_UP1 0x8329
+#define MDIO_WC_REG_DIGITAL4_MISC3 0x833c
+#define MDIO_WC_REG_DIGITAL5_MISC6 0x8345
+#define MDIO_WC_REG_DIGITAL5_MISC7 0x8349
+#define MDIO_WC_REG_DIGITAL5_ACTUAL_SPEED 0x834e
+#define MDIO_WC_REG_DIGITAL6_MP5_NEXTPAGECTRL 0x8350
+#define MDIO_WC_REG_CL49_USERB0_CTRL 0x8368
+#define MDIO_WC_REG_TX66_CONTROL 0x83b0
+#define MDIO_WC_REG_RX66_CONTROL 0x83c0
+#define MDIO_WC_REG_RX66_SCW0 0x83c2
+#define MDIO_WC_REG_RX66_SCW1 0x83c3
+#define MDIO_WC_REG_RX66_SCW2 0x83c4
+#define MDIO_WC_REG_RX66_SCW3 0x83c5
+#define MDIO_WC_REG_RX66_SCW0_MASK 0x83c6
+#define MDIO_WC_REG_RX66_SCW1_MASK 0x83c7
+#define MDIO_WC_REG_RX66_SCW2_MASK 0x83c8
+#define MDIO_WC_REG_RX66_SCW3_MASK 0x83c9
+#define MDIO_WC_REG_FX100_CTRL1 0x8400
+#define MDIO_WC_REG_FX100_CTRL3 0x8402
+
+#define MDIO_WC_REG_MICROBLK_CMD 0xffc2
+#define MDIO_WC_REG_MICROBLK_DL_STATUS 0xffc5
+#define MDIO_WC_REG_MICROBLK_CMD3 0xffcc
+
+#define MDIO_WC_REG_AERBLK_AER 0xffde
+#define MDIO_WC_REG_COMBO_IEEE0_MIICTRL 0xffe0
+#define MDIO_WC_REG_COMBO_IEEE0_MIIISTAT 0xffe1
+
+#define MDIO_WC0_XGXS_BLK2_LANE_RESET 0x810A
+#define MDIO_WC0_XGXS_BLK2_LANE_RESET_RX_BITSHIFT 0
+#define MDIO_WC0_XGXS_BLK2_LANE_RESET_TX_BITSHIFT 4
+
+#define MDIO_WC0_XGXS_BLK6_XGXS_X2_CONTROL2 0x8141
+
+#define DIGITAL5_ACTUAL_SPEED_TX_MASK 0x003f
+
+/* 54618se */
+#define MDIO_REG_GPHY_PHYID_LSB 0x3
+#define MDIO_REG_GPHY_ID_54618SE 0x5cd5
+#define MDIO_REG_GPHY_CL45_ADDR_REG 0xd
+#define MDIO_REG_GPHY_CL45_DATA_REG 0xe
+#define MDIO_REG_GPHY_EEE_ADV 0x3c
+#define MDIO_REG_GPHY_EEE_1G (0x1 << 2)
+#define MDIO_REG_GPHY_EEE_100 (0x1 << 1)
+#define MDIO_REG_GPHY_EEE_RESOLVED 0x803e
+#define MDIO_REG_INTR_STATUS 0x1a
+#define MDIO_REG_INTR_MASK 0x1b
+#define MDIO_REG_INTR_MASK_LINK_STATUS (0x1 << 1)
+#define MDIO_REG_GPHY_SHADOW 0x1c
+#define MDIO_REG_GPHY_SHADOW_LED_SEL2 (0x0e << 10)
+#define MDIO_REG_GPHY_SHADOW_WR_ENA (0x1 << 15)
+#define MDIO_REG_GPHY_SHADOW_AUTO_DET_MED (0x1e << 10)
+#define MDIO_REG_GPHY_SHADOW_INVERT_FIB_SD (0x1 << 8)
+
#define IGU_FUNC_BASE 0x0400
#define IGU_ADDR_MSIX 0x0000
@@ -6217,11 +7011,6 @@ Theotherbitsarereservedandshouldbezero*/
#define IGU_ADDR_MSI_ADDR_HI 0x0212
#define IGU_ADDR_MSI_DATA 0x0213
-#define IGU_INT_ENABLE 0
-#define IGU_INT_DISABLE 1
-#define IGU_INT_NOP 2
-#define IGU_INT_NOP2 3
-
#define IGU_USE_REGISTER_ustorm_type_0_sb_cleanup 0
#define IGU_USE_REGISTER_ustorm_type_1_sb_cleanup 1
#define IGU_USE_REGISTER_cstorm_type_0_sb_cleanup 2
@@ -6292,15 +7081,6 @@ Theotherbitsarereservedandshouldbezero*/
#define IGU_BC_BASE_DSB_PROD 128
#define IGU_NORM_BASE_DSB_PROD 136
-#define IGU_CTRL_CMD_TYPE_WR\
- 1
-#define IGU_CTRL_CMD_TYPE_RD\
- 0
-
-#define IGU_SEG_ACCESS_NORM 0
-#define IGU_SEG_ACCESS_DEF 1
-#define IGU_SEG_ACCESS_ATTN 2
-
/* FID (if VF - [6] = 0; [5:0] = VF number; if PF - [6] = 1; \
[5:2] = 0; [1:0] = PF number) */
#define IGU_FID_ENCODE_IS_PF (0x1<<6)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
new file mode 100644
index 000000000000..14517691f8db
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
@@ -0,0 +1,5686 @@
+/* bnx2x_sp.c: Broadcom Everest network driver.
+ *
+ * Copyright 2011 Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2, available
+ * at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html (the "GPL").
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a
+ * license other than the GPL, without Broadcom's express prior written
+ * consent.
+ *
+ * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Written by: Vladislav Zolotarov
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/crc32.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32c.h>
+#include "bnx2x.h"
+#include "bnx2x_cmn.h"
+#include "bnx2x_sp.h"
+
+#define BNX2X_MAX_EMUL_MULTI 16
+
+/**** Exe Queue interfaces ****/
+
+/**
+ * bnx2x_exe_queue_init - init the Exe Queue object
+ *
+ * @o: poiter to the object
+ * @exe_len: length
+ * @owner: poiter to the owner
+ * @validate: validate function pointer
+ * @optimize: optimize function pointer
+ * @exec: execute function pointer
+ * @get: get function pointer
+ */
+static inline void bnx2x_exe_queue_init(struct bnx2x *bp,
+ struct bnx2x_exe_queue_obj *o,
+ int exe_len,
+ union bnx2x_qable_obj *owner,
+ exe_q_validate validate,
+ exe_q_optimize optimize,
+ exe_q_execute exec,
+ exe_q_get get)
+{
+ memset(o, 0, sizeof(*o));
+
+ INIT_LIST_HEAD(&o->exe_queue);
+ INIT_LIST_HEAD(&o->pending_comp);
+
+ spin_lock_init(&o->lock);
+
+ o->exe_chunk_len = exe_len;
+ o->owner = owner;
+
+ /* Owner specific callbacks */
+ o->validate = validate;
+ o->optimize = optimize;
+ o->execute = exec;
+ o->get = get;
+
+ DP(BNX2X_MSG_SP, "Setup the execution queue with the chunk "
+ "length of %d\n", exe_len);
+}
+
+static inline void bnx2x_exe_queue_free_elem(struct bnx2x *bp,
+ struct bnx2x_exeq_elem *elem)
+{
+ DP(BNX2X_MSG_SP, "Deleting an exe_queue element\n");
+ kfree(elem);
+}
+
+static inline int bnx2x_exe_queue_length(struct bnx2x_exe_queue_obj *o)
+{
+ struct bnx2x_exeq_elem *elem;
+ int cnt = 0;
+
+ spin_lock_bh(&o->lock);
+
+ list_for_each_entry(elem, &o->exe_queue, link)
+ cnt++;
+
+ spin_unlock_bh(&o->lock);
+
+ return cnt;
+}
+
+/**
+ * bnx2x_exe_queue_add - add a new element to the execution queue
+ *
+ * @bp: driver handle
+ * @o: queue
+ * @cmd: new command to add
+ * @restore: true - do not optimize the command
+ *
+ * If the element is optimized or is illegal, frees it.
+ */
+static inline int bnx2x_exe_queue_add(struct bnx2x *bp,
+ struct bnx2x_exe_queue_obj *o,
+ struct bnx2x_exeq_elem *elem,
+ bool restore)
+{
+ int rc;
+
+ spin_lock_bh(&o->lock);
+
+ if (!restore) {
+ /* Try to cancel this element queue */
+ rc = o->optimize(bp, o->owner, elem);
+ if (rc)
+ goto free_and_exit;
+
+ /* Check if this request is ok */
+ rc = o->validate(bp, o->owner, elem);
+ if (rc) {
+ BNX2X_ERR("Preamble failed: %d\n", rc);
+ goto free_and_exit;
+ }
+ }
+
+ /* If so, add it to the execution queue */
+ list_add_tail(&elem->link, &o->exe_queue);
+
+ spin_unlock_bh(&o->lock);
+
+ return 0;
+
+free_and_exit:
+ bnx2x_exe_queue_free_elem(bp, elem);
+
+ spin_unlock_bh(&o->lock);
+
+ return rc;
+
+}
+
+static inline void __bnx2x_exe_queue_reset_pending(
+ struct bnx2x *bp,
+ struct bnx2x_exe_queue_obj *o)
+{
+ struct bnx2x_exeq_elem *elem;
+
+ while (!list_empty(&o->pending_comp)) {
+ elem = list_first_entry(&o->pending_comp,
+ struct bnx2x_exeq_elem, link);
+
+ list_del(&elem->link);
+ bnx2x_exe_queue_free_elem(bp, elem);
+ }
+}
+
+static inline void bnx2x_exe_queue_reset_pending(struct bnx2x *bp,
+ struct bnx2x_exe_queue_obj *o)
+{
+
+ spin_lock_bh(&o->lock);
+
+ __bnx2x_exe_queue_reset_pending(bp, o);
+
+ spin_unlock_bh(&o->lock);
+
+}
+
+/**
+ * bnx2x_exe_queue_step - execute one execution chunk atomically
+ *
+ * @bp: driver handle
+ * @o: queue
+ * @ramrod_flags: flags
+ *
+ * (Atomicy is ensured using the exe_queue->lock).
+ */
+static inline int bnx2x_exe_queue_step(struct bnx2x *bp,
+ struct bnx2x_exe_queue_obj *o,
+ unsigned long *ramrod_flags)
+{
+ struct bnx2x_exeq_elem *elem, spacer;
+ int cur_len = 0, rc;
+
+ memset(&spacer, 0, sizeof(spacer));
+
+ spin_lock_bh(&o->lock);
+
+ /*
+ * Next step should not be performed until the current is finished,
+ * unless a DRV_CLEAR_ONLY bit is set. In this case we just want to
+ * properly clear object internals without sending any command to the FW
+ * which also implies there won't be any completion to clear the
+ * 'pending' list.
+ */
+ if (!list_empty(&o->pending_comp)) {
+ if (test_bit(RAMROD_DRV_CLR_ONLY, ramrod_flags)) {
+ DP(BNX2X_MSG_SP, "RAMROD_DRV_CLR_ONLY requested: "
+ "resetting pending_comp\n");
+ __bnx2x_exe_queue_reset_pending(bp, o);
+ } else {
+ spin_unlock_bh(&o->lock);
+ return 1;
+ }
+ }
+
+ /*
+ * Run through the pending commands list and create a next
+ * execution chunk.
+ */
+ while (!list_empty(&o->exe_queue)) {
+ elem = list_first_entry(&o->exe_queue, struct bnx2x_exeq_elem,
+ link);
+ WARN_ON(!elem->cmd_len);
+
+ if (cur_len + elem->cmd_len <= o->exe_chunk_len) {
+ cur_len += elem->cmd_len;
+ /*
+ * Prevent from both lists being empty when moving an
+ * element. This will allow the call of
+ * bnx2x_exe_queue_empty() without locking.
+ */
+ list_add_tail(&spacer.link, &o->pending_comp);
+ mb();
+ list_del(&elem->link);
+ list_add_tail(&elem->link, &o->pending_comp);
+ list_del(&spacer.link);
+ } else
+ break;
+ }
+
+ /* Sanity check */
+ if (!cur_len) {
+ spin_unlock_bh(&o->lock);
+ return 0;
+ }
+
+ rc = o->execute(bp, o->owner, &o->pending_comp, ramrod_flags);
+ if (rc < 0)
+ /*
+ * In case of an error return the commands back to the queue
+ * and reset the pending_comp.
+ */
+ list_splice_init(&o->pending_comp, &o->exe_queue);
+ else if (!rc)
+ /*
+ * If zero is returned, means there are no outstanding pending
+ * completions and we may dismiss the pending list.
+ */
+ __bnx2x_exe_queue_reset_pending(bp, o);
+
+ spin_unlock_bh(&o->lock);
+ return rc;
+}
+
+static inline bool bnx2x_exe_queue_empty(struct bnx2x_exe_queue_obj *o)
+{
+ bool empty = list_empty(&o->exe_queue);
+
+ /* Don't reorder!!! */
+ mb();
+
+ return empty && list_empty(&o->pending_comp);
+}
+
+static inline struct bnx2x_exeq_elem *bnx2x_exe_queue_alloc_elem(
+ struct bnx2x *bp)
+{
+ DP(BNX2X_MSG_SP, "Allocating a new exe_queue element\n");
+ return kzalloc(sizeof(struct bnx2x_exeq_elem), GFP_ATOMIC);
+}
+
+/************************ raw_obj functions ***********************************/
+static bool bnx2x_raw_check_pending(struct bnx2x_raw_obj *o)
+{
+ return !!test_bit(o->state, o->pstate);
+}
+
+static void bnx2x_raw_clear_pending(struct bnx2x_raw_obj *o)
+{
+ smp_mb__before_clear_bit();
+ clear_bit(o->state, o->pstate);
+ smp_mb__after_clear_bit();
+}
+
+static void bnx2x_raw_set_pending(struct bnx2x_raw_obj *o)
+{
+ smp_mb__before_clear_bit();
+ set_bit(o->state, o->pstate);
+ smp_mb__after_clear_bit();
+}
+
+/**
+ * bnx2x_state_wait - wait until the given bit(state) is cleared
+ *
+ * @bp: device handle
+ * @state: state which is to be cleared
+ * @state_p: state buffer
+ *
+ */
+static inline int bnx2x_state_wait(struct bnx2x *bp, int state,
+ unsigned long *pstate)
+{
+ /* can take a while if any port is running */
+ int cnt = 5000;
+
+
+ if (CHIP_REV_IS_EMUL(bp))
+ cnt *= 20;
+
+ DP(BNX2X_MSG_SP, "waiting for state to become %d\n", state);
+
+ might_sleep();
+ while (cnt--) {
+ if (!test_bit(state, pstate)) {
+#ifdef BNX2X_STOP_ON_ERROR
+ DP(BNX2X_MSG_SP, "exit (cnt %d)\n", 5000 - cnt);
+#endif
+ return 0;
+ }
+
+ usleep_range(1000, 1000);
+
+ if (bp->panic)
+ return -EIO;
+ }
+
+ /* timeout! */
+ BNX2X_ERR("timeout waiting for state %d\n", state);
+#ifdef BNX2X_STOP_ON_ERROR
+ bnx2x_panic();
+#endif
+
+ return -EBUSY;
+}
+
+static int bnx2x_raw_wait(struct bnx2x *bp, struct bnx2x_raw_obj *raw)
+{
+ return bnx2x_state_wait(bp, raw->state, raw->pstate);
+}
+
+/***************** Classification verbs: Set/Del MAC/VLAN/VLAN-MAC ************/
+/* credit handling callbacks */
+static bool bnx2x_get_cam_offset_mac(struct bnx2x_vlan_mac_obj *o, int *offset)
+{
+ struct bnx2x_credit_pool_obj *mp = o->macs_pool;
+
+ WARN_ON(!mp);
+
+ return mp->get_entry(mp, offset);
+}
+
+static bool bnx2x_get_credit_mac(struct bnx2x_vlan_mac_obj *o)
+{
+ struct bnx2x_credit_pool_obj *mp = o->macs_pool;
+
+ WARN_ON(!mp);
+
+ return mp->get(mp, 1);
+}
+
+static bool bnx2x_get_cam_offset_vlan(struct bnx2x_vlan_mac_obj *o, int *offset)
+{
+ struct bnx2x_credit_pool_obj *vp = o->vlans_pool;
+
+ WARN_ON(!vp);
+
+ return vp->get_entry(vp, offset);
+}
+
+static bool bnx2x_get_credit_vlan(struct bnx2x_vlan_mac_obj *o)
+{
+ struct bnx2x_credit_pool_obj *vp = o->vlans_pool;
+
+ WARN_ON(!vp);
+
+ return vp->get(vp, 1);
+}
+
+static bool bnx2x_get_credit_vlan_mac(struct bnx2x_vlan_mac_obj *o)
+{
+ struct bnx2x_credit_pool_obj *mp = o->macs_pool;
+ struct bnx2x_credit_pool_obj *vp = o->vlans_pool;
+
+ if (!mp->get(mp, 1))
+ return false;
+
+ if (!vp->get(vp, 1)) {
+ mp->put(mp, 1);
+ return false;
+ }
+
+ return true;
+}
+
+static bool bnx2x_put_cam_offset_mac(struct bnx2x_vlan_mac_obj *o, int offset)
+{
+ struct bnx2x_credit_pool_obj *mp = o->macs_pool;
+
+ return mp->put_entry(mp, offset);
+}
+
+static bool bnx2x_put_credit_mac(struct bnx2x_vlan_mac_obj *o)
+{
+ struct bnx2x_credit_pool_obj *mp = o->macs_pool;
+
+ return mp->put(mp, 1);
+}
+
+static bool bnx2x_put_cam_offset_vlan(struct bnx2x_vlan_mac_obj *o, int offset)
+{
+ struct bnx2x_credit_pool_obj *vp = o->vlans_pool;
+
+ return vp->put_entry(vp, offset);
+}
+
+static bool bnx2x_put_credit_vlan(struct bnx2x_vlan_mac_obj *o)
+{
+ struct bnx2x_credit_pool_obj *vp = o->vlans_pool;
+
+ return vp->put(vp, 1);
+}
+
+static bool bnx2x_put_credit_vlan_mac(struct bnx2x_vlan_mac_obj *o)
+{
+ struct bnx2x_credit_pool_obj *mp = o->macs_pool;
+ struct bnx2x_credit_pool_obj *vp = o->vlans_pool;
+
+ if (!mp->put(mp, 1))
+ return false;
+
+ if (!vp->put(vp, 1)) {
+ mp->get(mp, 1);
+ return false;
+ }
+
+ return true;
+}
+
+/* check_add() callbacks */
+static int bnx2x_check_mac_add(struct bnx2x_vlan_mac_obj *o,
+ union bnx2x_classification_ramrod_data *data)
+{
+ struct bnx2x_vlan_mac_registry_elem *pos;
+
+ if (!is_valid_ether_addr(data->mac.mac))
+ return -EINVAL;
+
+ /* Check if a requested MAC already exists */
+ list_for_each_entry(pos, &o->head, link)
+ if (!memcmp(data->mac.mac, pos->u.mac.mac, ETH_ALEN))
+ return -EEXIST;
+
+ return 0;
+}
+
+static int bnx2x_check_vlan_add(struct bnx2x_vlan_mac_obj *o,
+ union bnx2x_classification_ramrod_data *data)
+{
+ struct bnx2x_vlan_mac_registry_elem *pos;
+
+ list_for_each_entry(pos, &o->head, link)
+ if (data->vlan.vlan == pos->u.vlan.vlan)
+ return -EEXIST;
+
+ return 0;
+}
+
+static int bnx2x_check_vlan_mac_add(struct bnx2x_vlan_mac_obj *o,
+ union bnx2x_classification_ramrod_data *data)
+{
+ struct bnx2x_vlan_mac_registry_elem *pos;
+
+ list_for_each_entry(pos, &o->head, link)
+ if ((data->vlan_mac.vlan == pos->u.vlan_mac.vlan) &&
+ (!memcmp(data->vlan_mac.mac, pos->u.vlan_mac.mac,
+ ETH_ALEN)))
+ return -EEXIST;
+
+ return 0;
+}
+
+
+/* check_del() callbacks */
+static struct bnx2x_vlan_mac_registry_elem *
+ bnx2x_check_mac_del(struct bnx2x_vlan_mac_obj *o,
+ union bnx2x_classification_ramrod_data *data)
+{
+ struct bnx2x_vlan_mac_registry_elem *pos;
+
+ list_for_each_entry(pos, &o->head, link)
+ if (!memcmp(data->mac.mac, pos->u.mac.mac, ETH_ALEN))
+ return pos;
+
+ return NULL;
+}
+
+static struct bnx2x_vlan_mac_registry_elem *
+ bnx2x_check_vlan_del(struct bnx2x_vlan_mac_obj *o,
+ union bnx2x_classification_ramrod_data *data)
+{
+ struct bnx2x_vlan_mac_registry_elem *pos;
+
+ list_for_each_entry(pos, &o->head, link)
+ if (data->vlan.vlan == pos->u.vlan.vlan)
+ return pos;
+
+ return NULL;
+}
+
+static struct bnx2x_vlan_mac_registry_elem *
+ bnx2x_check_vlan_mac_del(struct bnx2x_vlan_mac_obj *o,
+ union bnx2x_classification_ramrod_data *data)
+{
+ struct bnx2x_vlan_mac_registry_elem *pos;
+
+ list_for_each_entry(pos, &o->head, link)
+ if ((data->vlan_mac.vlan == pos->u.vlan_mac.vlan) &&
+ (!memcmp(data->vlan_mac.mac, pos->u.vlan_mac.mac,
+ ETH_ALEN)))
+ return pos;
+
+ return NULL;
+}
+
+/* check_move() callback */
+static bool bnx2x_check_move(struct bnx2x_vlan_mac_obj *src_o,
+ struct bnx2x_vlan_mac_obj *dst_o,
+ union bnx2x_classification_ramrod_data *data)
+{
+ struct bnx2x_vlan_mac_registry_elem *pos;
+ int rc;
+
+ /* Check if we can delete the requested configuration from the first
+ * object.
+ */
+ pos = src_o->check_del(src_o, data);
+
+ /* check if configuration can be added */
+ rc = dst_o->check_add(dst_o, data);
+
+ /* If this classification can not be added (is already set)
+ * or can't be deleted - return an error.
+ */
+ if (rc || !pos)
+ return false;
+
+ return true;
+}
+
+static bool bnx2x_check_move_always_err(
+ struct bnx2x_vlan_mac_obj *src_o,
+ struct bnx2x_vlan_mac_obj *dst_o,
+ union bnx2x_classification_ramrod_data *data)
+{
+ return false;
+}
+
+
+static inline u8 bnx2x_vlan_mac_get_rx_tx_flag(struct bnx2x_vlan_mac_obj *o)
+{
+ struct bnx2x_raw_obj *raw = &o->raw;
+ u8 rx_tx_flag = 0;
+
+ if ((raw->obj_type == BNX2X_OBJ_TYPE_TX) ||
+ (raw->obj_type == BNX2X_OBJ_TYPE_RX_TX))
+ rx_tx_flag |= ETH_CLASSIFY_CMD_HEADER_TX_CMD;
+
+ if ((raw->obj_type == BNX2X_OBJ_TYPE_RX) ||
+ (raw->obj_type == BNX2X_OBJ_TYPE_RX_TX))
+ rx_tx_flag |= ETH_CLASSIFY_CMD_HEADER_RX_CMD;
+
+ return rx_tx_flag;
+}
+
+/* LLH CAM line allocations */
+enum {
+ LLH_CAM_ISCSI_ETH_LINE = 0,
+ LLH_CAM_ETH_LINE,
+ LLH_CAM_MAX_PF_LINE = NIG_REG_LLH1_FUNC_MEM_SIZE / 2
+};
+
+static inline void bnx2x_set_mac_in_nig(struct bnx2x *bp,
+ bool add, unsigned char *dev_addr, int index)
+{
+ u32 wb_data[2];
+ u32 reg_offset = BP_PORT(bp) ? NIG_REG_LLH1_FUNC_MEM :
+ NIG_REG_LLH0_FUNC_MEM;
+
+ if (!IS_MF_SI(bp) || index > LLH_CAM_MAX_PF_LINE)
+ return;
+
+ DP(BNX2X_MSG_SP, "Going to %s LLH configuration at entry %d\n",
+ (add ? "ADD" : "DELETE"), index);
+
+ if (add) {
+ /* LLH_FUNC_MEM is a u64 WB register */
+ reg_offset += 8*index;
+
+ wb_data[0] = ((dev_addr[2] << 24) | (dev_addr[3] << 16) |
+ (dev_addr[4] << 8) | dev_addr[5]);
+ wb_data[1] = ((dev_addr[0] << 8) | dev_addr[1]);
+
+ REG_WR_DMAE(bp, reg_offset, wb_data, 2);
+ }
+
+ REG_WR(bp, (BP_PORT(bp) ? NIG_REG_LLH1_FUNC_MEM_ENABLE :
+ NIG_REG_LLH0_FUNC_MEM_ENABLE) + 4*index, add);
+}
+
+/**
+ * bnx2x_vlan_mac_set_cmd_hdr_e2 - set a header in a single classify ramrod
+ *
+ * @bp: device handle
+ * @o: queue for which we want to configure this rule
+ * @add: if true the command is an ADD command, DEL otherwise
+ * @opcode: CLASSIFY_RULE_OPCODE_XXX
+ * @hdr: pointer to a header to setup
+ *
+ */
+static inline void bnx2x_vlan_mac_set_cmd_hdr_e2(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o, bool add, int opcode,
+ struct eth_classify_cmd_header *hdr)
+{
+ struct bnx2x_raw_obj *raw = &o->raw;
+
+ hdr->client_id = raw->cl_id;
+ hdr->func_id = raw->func_id;
+
+ /* Rx or/and Tx (internal switching) configuration ? */
+ hdr->cmd_general_data |=
+ bnx2x_vlan_mac_get_rx_tx_flag(o);
+
+ if (add)
+ hdr->cmd_general_data |= ETH_CLASSIFY_CMD_HEADER_IS_ADD;
+
+ hdr->cmd_general_data |=
+ (opcode << ETH_CLASSIFY_CMD_HEADER_OPCODE_SHIFT);
+}
+
+/**
+ * bnx2x_vlan_mac_set_rdata_hdr_e2 - set the classify ramrod data header
+ *
+ * @cid: connection id
+ * @type: BNX2X_FILTER_XXX_PENDING
+ * @hdr: poiter to header to setup
+ * @rule_cnt:
+ *
+ * currently we always configure one rule and echo field to contain a CID and an
+ * opcode type.
+ */
+static inline void bnx2x_vlan_mac_set_rdata_hdr_e2(u32 cid, int type,
+ struct eth_classify_header *hdr, int rule_cnt)
+{
+ hdr->echo = (cid & BNX2X_SWCID_MASK) | (type << BNX2X_SWCID_SHIFT);
+ hdr->rule_cnt = (u8)rule_cnt;
+}
+
+
+/* hw_config() callbacks */
+static void bnx2x_set_one_mac_e2(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o,
+ struct bnx2x_exeq_elem *elem, int rule_idx,
+ int cam_offset)
+{
+ struct bnx2x_raw_obj *raw = &o->raw;
+ struct eth_classify_rules_ramrod_data *data =
+ (struct eth_classify_rules_ramrod_data *)(raw->rdata);
+ int rule_cnt = rule_idx + 1, cmd = elem->cmd_data.vlan_mac.cmd;
+ union eth_classify_rule_cmd *rule_entry = &data->rules[rule_idx];
+ bool add = (cmd == BNX2X_VLAN_MAC_ADD) ? true : false;
+ unsigned long *vlan_mac_flags = &elem->cmd_data.vlan_mac.vlan_mac_flags;
+ u8 *mac = elem->cmd_data.vlan_mac.u.mac.mac;
+
+ /*
+ * Set LLH CAM entry: currently only iSCSI and ETH macs are
+ * relevant. In addition, current implementation is tuned for a
+ * single ETH MAC.
+ *
+ * When multiple unicast ETH MACs PF configuration in switch
+ * independent mode is required (NetQ, multiple netdev MACs,
+ * etc.), consider better utilisation of 8 per function MAC
+ * entries in the LLH register. There is also
+ * NIG_REG_P[01]_LLH_FUNC_MEM2 registers that complete the
+ * total number of CAM entries to 16.
+ *
+ * Currently we won't configure NIG for MACs other than a primary ETH
+ * MAC and iSCSI L2 MAC.
+ *
+ * If this MAC is moving from one Queue to another, no need to change
+ * NIG configuration.
+ */
+ if (cmd != BNX2X_VLAN_MAC_MOVE) {
+ if (test_bit(BNX2X_ISCSI_ETH_MAC, vlan_mac_flags))
+ bnx2x_set_mac_in_nig(bp, add, mac,
+ LLH_CAM_ISCSI_ETH_LINE);
+ else if (test_bit(BNX2X_ETH_MAC, vlan_mac_flags))
+ bnx2x_set_mac_in_nig(bp, add, mac, LLH_CAM_ETH_LINE);
+ }
+
+ /* Reset the ramrod data buffer for the first rule */
+ if (rule_idx == 0)
+ memset(data, 0, sizeof(*data));
+
+ /* Setup a command header */
+ bnx2x_vlan_mac_set_cmd_hdr_e2(bp, o, add, CLASSIFY_RULE_OPCODE_MAC,
+ &rule_entry->mac.header);
+
+ DP(BNX2X_MSG_SP, "About to %s MAC %pM for Queue %d\n",
+ add ? "add" : "delete", mac, raw->cl_id);
+
+ /* Set a MAC itself */
+ bnx2x_set_fw_mac_addr(&rule_entry->mac.mac_msb,
+ &rule_entry->mac.mac_mid,
+ &rule_entry->mac.mac_lsb, mac);
+
+ /* MOVE: Add a rule that will add this MAC to the target Queue */
+ if (cmd == BNX2X_VLAN_MAC_MOVE) {
+ rule_entry++;
+ rule_cnt++;
+
+ /* Setup ramrod data */
+ bnx2x_vlan_mac_set_cmd_hdr_e2(bp,
+ elem->cmd_data.vlan_mac.target_obj,
+ true, CLASSIFY_RULE_OPCODE_MAC,
+ &rule_entry->mac.header);
+
+ /* Set a MAC itself */
+ bnx2x_set_fw_mac_addr(&rule_entry->mac.mac_msb,
+ &rule_entry->mac.mac_mid,
+ &rule_entry->mac.mac_lsb, mac);
+ }
+
+ /* Set the ramrod data header */
+ /* TODO: take this to the higher level in order to prevent multiple
+ writing */
+ bnx2x_vlan_mac_set_rdata_hdr_e2(raw->cid, raw->state, &data->header,
+ rule_cnt);
+}
+
+/**
+ * bnx2x_vlan_mac_set_rdata_hdr_e1x - set a header in a single classify ramrod
+ *
+ * @bp: device handle
+ * @o: queue
+ * @type:
+ * @cam_offset: offset in cam memory
+ * @hdr: pointer to a header to setup
+ *
+ * E1/E1H
+ */
+static inline void bnx2x_vlan_mac_set_rdata_hdr_e1x(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o, int type, int cam_offset,
+ struct mac_configuration_hdr *hdr)
+{
+ struct bnx2x_raw_obj *r = &o->raw;
+
+ hdr->length = 1;
+ hdr->offset = (u8)cam_offset;
+ hdr->client_id = 0xff;
+ hdr->echo = ((r->cid & BNX2X_SWCID_MASK) | (type << BNX2X_SWCID_SHIFT));
+}
+
+static inline void bnx2x_vlan_mac_set_cfg_entry_e1x(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o, bool add, int opcode, u8 *mac,
+ u16 vlan_id, struct mac_configuration_entry *cfg_entry)
+{
+ struct bnx2x_raw_obj *r = &o->raw;
+ u32 cl_bit_vec = (1 << r->cl_id);
+
+ cfg_entry->clients_bit_vector = cpu_to_le32(cl_bit_vec);
+ cfg_entry->pf_id = r->func_id;
+ cfg_entry->vlan_id = cpu_to_le16(vlan_id);
+
+ if (add) {
+ SET_FLAG(cfg_entry->flags, MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
+ T_ETH_MAC_COMMAND_SET);
+ SET_FLAG(cfg_entry->flags,
+ MAC_CONFIGURATION_ENTRY_VLAN_FILTERING_MODE, opcode);
+
+ /* Set a MAC in a ramrod data */
+ bnx2x_set_fw_mac_addr(&cfg_entry->msb_mac_addr,
+ &cfg_entry->middle_mac_addr,
+ &cfg_entry->lsb_mac_addr, mac);
+ } else
+ SET_FLAG(cfg_entry->flags, MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
+ T_ETH_MAC_COMMAND_INVALIDATE);
+}
+
+static inline void bnx2x_vlan_mac_set_rdata_e1x(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o, int type, int cam_offset, bool add,
+ u8 *mac, u16 vlan_id, int opcode, struct mac_configuration_cmd *config)
+{
+ struct mac_configuration_entry *cfg_entry = &config->config_table[0];
+ struct bnx2x_raw_obj *raw = &o->raw;
+
+ bnx2x_vlan_mac_set_rdata_hdr_e1x(bp, o, type, cam_offset,
+ &config->hdr);
+ bnx2x_vlan_mac_set_cfg_entry_e1x(bp, o, add, opcode, mac, vlan_id,
+ cfg_entry);
+
+ DP(BNX2X_MSG_SP, "%s MAC %pM CLID %d CAM offset %d\n",
+ add ? "setting" : "clearing",
+ mac, raw->cl_id, cam_offset);
+}
+
+/**
+ * bnx2x_set_one_mac_e1x - fill a single MAC rule ramrod data
+ *
+ * @bp: device handle
+ * @o: bnx2x_vlan_mac_obj
+ * @elem: bnx2x_exeq_elem
+ * @rule_idx: rule_idx
+ * @cam_offset: cam_offset
+ */
+static void bnx2x_set_one_mac_e1x(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o,
+ struct bnx2x_exeq_elem *elem, int rule_idx,
+ int cam_offset)
+{
+ struct bnx2x_raw_obj *raw = &o->raw;
+ struct mac_configuration_cmd *config =
+ (struct mac_configuration_cmd *)(raw->rdata);
+ /*
+ * 57710 and 57711 do not support MOVE command,
+ * so it's either ADD or DEL
+ */
+ bool add = (elem->cmd_data.vlan_mac.cmd == BNX2X_VLAN_MAC_ADD) ?
+ true : false;
+
+ /* Reset the ramrod data buffer */
+ memset(config, 0, sizeof(*config));
+
+ bnx2x_vlan_mac_set_rdata_e1x(bp, o, BNX2X_FILTER_MAC_PENDING,
+ cam_offset, add,
+ elem->cmd_data.vlan_mac.u.mac.mac, 0,
+ ETH_VLAN_FILTER_ANY_VLAN, config);
+}
+
+static void bnx2x_set_one_vlan_e2(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o,
+ struct bnx2x_exeq_elem *elem, int rule_idx,
+ int cam_offset)
+{
+ struct bnx2x_raw_obj *raw = &o->raw;
+ struct eth_classify_rules_ramrod_data *data =
+ (struct eth_classify_rules_ramrod_data *)(raw->rdata);
+ int rule_cnt = rule_idx + 1;
+ union eth_classify_rule_cmd *rule_entry = &data->rules[rule_idx];
+ int cmd = elem->cmd_data.vlan_mac.cmd;
+ bool add = (cmd == BNX2X_VLAN_MAC_ADD) ? true : false;
+ u16 vlan = elem->cmd_data.vlan_mac.u.vlan.vlan;
+
+ /* Reset the ramrod data buffer for the first rule */
+ if (rule_idx == 0)
+ memset(data, 0, sizeof(*data));
+
+ /* Set a rule header */
+ bnx2x_vlan_mac_set_cmd_hdr_e2(bp, o, add, CLASSIFY_RULE_OPCODE_VLAN,
+ &rule_entry->vlan.header);
+
+ DP(BNX2X_MSG_SP, "About to %s VLAN %d\n", (add ? "add" : "delete"),
+ vlan);
+
+ /* Set a VLAN itself */
+ rule_entry->vlan.vlan = cpu_to_le16(vlan);
+
+ /* MOVE: Add a rule that will add this MAC to the target Queue */
+ if (cmd == BNX2X_VLAN_MAC_MOVE) {
+ rule_entry++;
+ rule_cnt++;
+
+ /* Setup ramrod data */
+ bnx2x_vlan_mac_set_cmd_hdr_e2(bp,
+ elem->cmd_data.vlan_mac.target_obj,
+ true, CLASSIFY_RULE_OPCODE_VLAN,
+ &rule_entry->vlan.header);
+
+ /* Set a VLAN itself */
+ rule_entry->vlan.vlan = cpu_to_le16(vlan);
+ }
+
+ /* Set the ramrod data header */
+ /* TODO: take this to the higher level in order to prevent multiple
+ writing */
+ bnx2x_vlan_mac_set_rdata_hdr_e2(raw->cid, raw->state, &data->header,
+ rule_cnt);
+}
+
+static void bnx2x_set_one_vlan_mac_e2(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o,
+ struct bnx2x_exeq_elem *elem,
+ int rule_idx, int cam_offset)
+{
+ struct bnx2x_raw_obj *raw = &o->raw;
+ struct eth_classify_rules_ramrod_data *data =
+ (struct eth_classify_rules_ramrod_data *)(raw->rdata);
+ int rule_cnt = rule_idx + 1;
+ union eth_classify_rule_cmd *rule_entry = &data->rules[rule_idx];
+ int cmd = elem->cmd_data.vlan_mac.cmd;
+ bool add = (cmd == BNX2X_VLAN_MAC_ADD) ? true : false;
+ u16 vlan = elem->cmd_data.vlan_mac.u.vlan_mac.vlan;
+ u8 *mac = elem->cmd_data.vlan_mac.u.vlan_mac.mac;
+
+
+ /* Reset the ramrod data buffer for the first rule */
+ if (rule_idx == 0)
+ memset(data, 0, sizeof(*data));
+
+ /* Set a rule header */
+ bnx2x_vlan_mac_set_cmd_hdr_e2(bp, o, add, CLASSIFY_RULE_OPCODE_PAIR,
+ &rule_entry->pair.header);
+
+ /* Set VLAN and MAC themselvs */
+ rule_entry->pair.vlan = cpu_to_le16(vlan);
+ bnx2x_set_fw_mac_addr(&rule_entry->pair.mac_msb,
+ &rule_entry->pair.mac_mid,
+ &rule_entry->pair.mac_lsb, mac);
+
+ /* MOVE: Add a rule that will add this MAC to the target Queue */
+ if (cmd == BNX2X_VLAN_MAC_MOVE) {
+ rule_entry++;
+ rule_cnt++;
+
+ /* Setup ramrod data */
+ bnx2x_vlan_mac_set_cmd_hdr_e2(bp,
+ elem->cmd_data.vlan_mac.target_obj,
+ true, CLASSIFY_RULE_OPCODE_PAIR,
+ &rule_entry->pair.header);
+
+ /* Set a VLAN itself */
+ rule_entry->pair.vlan = cpu_to_le16(vlan);
+ bnx2x_set_fw_mac_addr(&rule_entry->pair.mac_msb,
+ &rule_entry->pair.mac_mid,
+ &rule_entry->pair.mac_lsb, mac);
+ }
+
+ /* Set the ramrod data header */
+ /* TODO: take this to the higher level in order to prevent multiple
+ writing */
+ bnx2x_vlan_mac_set_rdata_hdr_e2(raw->cid, raw->state, &data->header,
+ rule_cnt);
+}
+
+/**
+ * bnx2x_set_one_vlan_mac_e1h -
+ *
+ * @bp: device handle
+ * @o: bnx2x_vlan_mac_obj
+ * @elem: bnx2x_exeq_elem
+ * @rule_idx: rule_idx
+ * @cam_offset: cam_offset
+ */
+static void bnx2x_set_one_vlan_mac_e1h(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o,
+ struct bnx2x_exeq_elem *elem,
+ int rule_idx, int cam_offset)
+{
+ struct bnx2x_raw_obj *raw = &o->raw;
+ struct mac_configuration_cmd *config =
+ (struct mac_configuration_cmd *)(raw->rdata);
+ /*
+ * 57710 and 57711 do not support MOVE command,
+ * so it's either ADD or DEL
+ */
+ bool add = (elem->cmd_data.vlan_mac.cmd == BNX2X_VLAN_MAC_ADD) ?
+ true : false;
+
+ /* Reset the ramrod data buffer */
+ memset(config, 0, sizeof(*config));
+
+ bnx2x_vlan_mac_set_rdata_e1x(bp, o, BNX2X_FILTER_VLAN_MAC_PENDING,
+ cam_offset, add,
+ elem->cmd_data.vlan_mac.u.vlan_mac.mac,
+ elem->cmd_data.vlan_mac.u.vlan_mac.vlan,
+ ETH_VLAN_FILTER_CLASSIFY, config);
+}
+
+#define list_next_entry(pos, member) \
+ list_entry((pos)->member.next, typeof(*(pos)), member)
+
+/**
+ * bnx2x_vlan_mac_restore - reconfigure next MAC/VLAN/VLAN-MAC element
+ *
+ * @bp: device handle
+ * @p: command parameters
+ * @ppos: pointer to the cooky
+ *
+ * reconfigure next MAC/VLAN/VLAN-MAC element from the
+ * previously configured elements list.
+ *
+ * from command parameters only RAMROD_COMP_WAIT bit in ramrod_flags is taken
+ * into an account
+ *
+ * pointer to the cooky - that should be given back in the next call to make
+ * function handle the next element. If *ppos is set to NULL it will restart the
+ * iterator. If returned *ppos == NULL this means that the last element has been
+ * handled.
+ *
+ */
+static int bnx2x_vlan_mac_restore(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_ramrod_params *p,
+ struct bnx2x_vlan_mac_registry_elem **ppos)
+{
+ struct bnx2x_vlan_mac_registry_elem *pos;
+ struct bnx2x_vlan_mac_obj *o = p->vlan_mac_obj;
+
+ /* If list is empty - there is nothing to do here */
+ if (list_empty(&o->head)) {
+ *ppos = NULL;
+ return 0;
+ }
+
+ /* make a step... */
+ if (*ppos == NULL)
+ *ppos = list_first_entry(&o->head,
+ struct bnx2x_vlan_mac_registry_elem,
+ link);
+ else
+ *ppos = list_next_entry(*ppos, link);
+
+ pos = *ppos;
+
+ /* If it's the last step - return NULL */
+ if (list_is_last(&pos->link, &o->head))
+ *ppos = NULL;
+
+ /* Prepare a 'user_req' */
+ memcpy(&p->user_req.u, &pos->u, sizeof(pos->u));
+
+ /* Set the command */
+ p->user_req.cmd = BNX2X_VLAN_MAC_ADD;
+
+ /* Set vlan_mac_flags */
+ p->user_req.vlan_mac_flags = pos->vlan_mac_flags;
+
+ /* Set a restore bit */
+ __set_bit(RAMROD_RESTORE, &p->ramrod_flags);
+
+ return bnx2x_config_vlan_mac(bp, p);
+}
+
+/*
+ * bnx2x_exeq_get_mac/bnx2x_exeq_get_vlan/bnx2x_exeq_get_vlan_mac return a
+ * pointer to an element with a specific criteria and NULL if such an element
+ * hasn't been found.
+ */
+static struct bnx2x_exeq_elem *bnx2x_exeq_get_mac(
+ struct bnx2x_exe_queue_obj *o,
+ struct bnx2x_exeq_elem *elem)
+{
+ struct bnx2x_exeq_elem *pos;
+ struct bnx2x_mac_ramrod_data *data = &elem->cmd_data.vlan_mac.u.mac;
+
+ /* Check pending for execution commands */
+ list_for_each_entry(pos, &o->exe_queue, link)
+ if (!memcmp(&pos->cmd_data.vlan_mac.u.mac, data,
+ sizeof(*data)) &&
+ (pos->cmd_data.vlan_mac.cmd == elem->cmd_data.vlan_mac.cmd))
+ return pos;
+
+ return NULL;
+}
+
+static struct bnx2x_exeq_elem *bnx2x_exeq_get_vlan(
+ struct bnx2x_exe_queue_obj *o,
+ struct bnx2x_exeq_elem *elem)
+{
+ struct bnx2x_exeq_elem *pos;
+ struct bnx2x_vlan_ramrod_data *data = &elem->cmd_data.vlan_mac.u.vlan;
+
+ /* Check pending for execution commands */
+ list_for_each_entry(pos, &o->exe_queue, link)
+ if (!memcmp(&pos->cmd_data.vlan_mac.u.vlan, data,
+ sizeof(*data)) &&
+ (pos->cmd_data.vlan_mac.cmd == elem->cmd_data.vlan_mac.cmd))
+ return pos;
+
+ return NULL;
+}
+
+static struct bnx2x_exeq_elem *bnx2x_exeq_get_vlan_mac(
+ struct bnx2x_exe_queue_obj *o,
+ struct bnx2x_exeq_elem *elem)
+{
+ struct bnx2x_exeq_elem *pos;
+ struct bnx2x_vlan_mac_ramrod_data *data =
+ &elem->cmd_data.vlan_mac.u.vlan_mac;
+
+ /* Check pending for execution commands */
+ list_for_each_entry(pos, &o->exe_queue, link)
+ if (!memcmp(&pos->cmd_data.vlan_mac.u.vlan_mac, data,
+ sizeof(*data)) &&
+ (pos->cmd_data.vlan_mac.cmd == elem->cmd_data.vlan_mac.cmd))
+ return pos;
+
+ return NULL;
+}
+
+/**
+ * bnx2x_validate_vlan_mac_add - check if an ADD command can be executed
+ *
+ * @bp: device handle
+ * @qo: bnx2x_qable_obj
+ * @elem: bnx2x_exeq_elem
+ *
+ * Checks that the requested configuration can be added. If yes and if
+ * requested, consume CAM credit.
+ *
+ * The 'validate' is run after the 'optimize'.
+ *
+ */
+static inline int bnx2x_validate_vlan_mac_add(struct bnx2x *bp,
+ union bnx2x_qable_obj *qo,
+ struct bnx2x_exeq_elem *elem)
+{
+ struct bnx2x_vlan_mac_obj *o = &qo->vlan_mac;
+ struct bnx2x_exe_queue_obj *exeq = &o->exe_queue;
+ int rc;
+
+ /* Check the registry */
+ rc = o->check_add(o, &elem->cmd_data.vlan_mac.u);
+ if (rc) {
+ DP(BNX2X_MSG_SP, "ADD command is not allowed considering "
+ "current registry state\n");
+ return rc;
+ }
+
+ /*
+ * Check if there is a pending ADD command for this
+ * MAC/VLAN/VLAN-MAC. Return an error if there is.
+ */
+ if (exeq->get(exeq, elem)) {
+ DP(BNX2X_MSG_SP, "There is a pending ADD command already\n");
+ return -EEXIST;
+ }
+
+ /*
+ * TODO: Check the pending MOVE from other objects where this
+ * object is a destination object.
+ */
+
+ /* Consume the credit if not requested not to */
+ if (!(test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT,
+ &elem->cmd_data.vlan_mac.vlan_mac_flags) ||
+ o->get_credit(o)))
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * bnx2x_validate_vlan_mac_del - check if the DEL command can be executed
+ *
+ * @bp: device handle
+ * @qo: quable object to check
+ * @elem: element that needs to be deleted
+ *
+ * Checks that the requested configuration can be deleted. If yes and if
+ * requested, returns a CAM credit.
+ *
+ * The 'validate' is run after the 'optimize'.
+ */
+static inline int bnx2x_validate_vlan_mac_del(struct bnx2x *bp,
+ union bnx2x_qable_obj *qo,
+ struct bnx2x_exeq_elem *elem)
+{
+ struct bnx2x_vlan_mac_obj *o = &qo->vlan_mac;
+ struct bnx2x_vlan_mac_registry_elem *pos;
+ struct bnx2x_exe_queue_obj *exeq = &o->exe_queue;
+ struct bnx2x_exeq_elem query_elem;
+
+ /* If this classification can not be deleted (doesn't exist)
+ * - return a BNX2X_EXIST.
+ */
+ pos = o->check_del(o, &elem->cmd_data.vlan_mac.u);
+ if (!pos) {
+ DP(BNX2X_MSG_SP, "DEL command is not allowed considering "
+ "current registry state\n");
+ return -EEXIST;
+ }
+
+ /*
+ * Check if there are pending DEL or MOVE commands for this
+ * MAC/VLAN/VLAN-MAC. Return an error if so.
+ */
+ memcpy(&query_elem, elem, sizeof(query_elem));
+
+ /* Check for MOVE commands */
+ query_elem.cmd_data.vlan_mac.cmd = BNX2X_VLAN_MAC_MOVE;
+ if (exeq->get(exeq, &query_elem)) {
+ BNX2X_ERR("There is a pending MOVE command already\n");
+ return -EINVAL;
+ }
+
+ /* Check for DEL commands */
+ if (exeq->get(exeq, elem)) {
+ DP(BNX2X_MSG_SP, "There is a pending DEL command already\n");
+ return -EEXIST;
+ }
+
+ /* Return the credit to the credit pool if not requested not to */
+ if (!(test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT,
+ &elem->cmd_data.vlan_mac.vlan_mac_flags) ||
+ o->put_credit(o))) {
+ BNX2X_ERR("Failed to return a credit\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * bnx2x_validate_vlan_mac_move - check if the MOVE command can be executed
+ *
+ * @bp: device handle
+ * @qo: quable object to check (source)
+ * @elem: element that needs to be moved
+ *
+ * Checks that the requested configuration can be moved. If yes and if
+ * requested, returns a CAM credit.
+ *
+ * The 'validate' is run after the 'optimize'.
+ */
+static inline int bnx2x_validate_vlan_mac_move(struct bnx2x *bp,
+ union bnx2x_qable_obj *qo,
+ struct bnx2x_exeq_elem *elem)
+{
+ struct bnx2x_vlan_mac_obj *src_o = &qo->vlan_mac;
+ struct bnx2x_vlan_mac_obj *dest_o = elem->cmd_data.vlan_mac.target_obj;
+ struct bnx2x_exeq_elem query_elem;
+ struct bnx2x_exe_queue_obj *src_exeq = &src_o->exe_queue;
+ struct bnx2x_exe_queue_obj *dest_exeq = &dest_o->exe_queue;
+
+ /*
+ * Check if we can perform this operation based on the current registry
+ * state.
+ */
+ if (!src_o->check_move(src_o, dest_o, &elem->cmd_data.vlan_mac.u)) {
+ DP(BNX2X_MSG_SP, "MOVE command is not allowed considering "
+ "current registry state\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Check if there is an already pending DEL or MOVE command for the
+ * source object or ADD command for a destination object. Return an
+ * error if so.
+ */
+ memcpy(&query_elem, elem, sizeof(query_elem));
+
+ /* Check DEL on source */
+ query_elem.cmd_data.vlan_mac.cmd = BNX2X_VLAN_MAC_DEL;
+ if (src_exeq->get(src_exeq, &query_elem)) {
+ BNX2X_ERR("There is a pending DEL command on the source "
+ "queue already\n");
+ return -EINVAL;
+ }
+
+ /* Check MOVE on source */
+ if (src_exeq->get(src_exeq, elem)) {
+ DP(BNX2X_MSG_SP, "There is a pending MOVE command already\n");
+ return -EEXIST;
+ }
+
+ /* Check ADD on destination */
+ query_elem.cmd_data.vlan_mac.cmd = BNX2X_VLAN_MAC_ADD;
+ if (dest_exeq->get(dest_exeq, &query_elem)) {
+ BNX2X_ERR("There is a pending ADD command on the "
+ "destination queue already\n");
+ return -EINVAL;
+ }
+
+ /* Consume the credit if not requested not to */
+ if (!(test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT_DEST,
+ &elem->cmd_data.vlan_mac.vlan_mac_flags) ||
+ dest_o->get_credit(dest_o)))
+ return -EINVAL;
+
+ if (!(test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT,
+ &elem->cmd_data.vlan_mac.vlan_mac_flags) ||
+ src_o->put_credit(src_o))) {
+ /* return the credit taken from dest... */
+ dest_o->put_credit(dest_o);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bnx2x_validate_vlan_mac(struct bnx2x *bp,
+ union bnx2x_qable_obj *qo,
+ struct bnx2x_exeq_elem *elem)
+{
+ switch (elem->cmd_data.vlan_mac.cmd) {
+ case BNX2X_VLAN_MAC_ADD:
+ return bnx2x_validate_vlan_mac_add(bp, qo, elem);
+ case BNX2X_VLAN_MAC_DEL:
+ return bnx2x_validate_vlan_mac_del(bp, qo, elem);
+ case BNX2X_VLAN_MAC_MOVE:
+ return bnx2x_validate_vlan_mac_move(bp, qo, elem);
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * bnx2x_wait_vlan_mac - passivly wait for 5 seconds until all work completes.
+ *
+ * @bp: device handle
+ * @o: bnx2x_vlan_mac_obj
+ *
+ */
+static int bnx2x_wait_vlan_mac(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o)
+{
+ int cnt = 5000, rc;
+ struct bnx2x_exe_queue_obj *exeq = &o->exe_queue;
+ struct bnx2x_raw_obj *raw = &o->raw;
+
+ while (cnt--) {
+ /* Wait for the current command to complete */
+ rc = raw->wait_comp(bp, raw);
+ if (rc)
+ return rc;
+
+ /* Wait until there are no pending commands */
+ if (!bnx2x_exe_queue_empty(exeq))
+ usleep_range(1000, 1000);
+ else
+ return 0;
+ }
+
+ return -EBUSY;
+}
+
+/**
+ * bnx2x_complete_vlan_mac - complete one VLAN-MAC ramrod
+ *
+ * @bp: device handle
+ * @o: bnx2x_vlan_mac_obj
+ * @cqe:
+ * @cont: if true schedule next execution chunk
+ *
+ */
+static int bnx2x_complete_vlan_mac(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o,
+ union event_ring_elem *cqe,
+ unsigned long *ramrod_flags)
+{
+ struct bnx2x_raw_obj *r = &o->raw;
+ int rc;
+
+ /* Reset pending list */
+ bnx2x_exe_queue_reset_pending(bp, &o->exe_queue);
+
+ /* Clear pending */
+ r->clear_pending(r);
+
+ /* If ramrod failed this is most likely a SW bug */
+ if (cqe->message.error)
+ return -EINVAL;
+
+ /* Run the next bulk of pending commands if requeted */
+ if (test_bit(RAMROD_CONT, ramrod_flags)) {
+ rc = bnx2x_exe_queue_step(bp, &o->exe_queue, ramrod_flags);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* If there is more work to do return PENDING */
+ if (!bnx2x_exe_queue_empty(&o->exe_queue))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * bnx2x_optimize_vlan_mac - optimize ADD and DEL commands.
+ *
+ * @bp: device handle
+ * @o: bnx2x_qable_obj
+ * @elem: bnx2x_exeq_elem
+ */
+static int bnx2x_optimize_vlan_mac(struct bnx2x *bp,
+ union bnx2x_qable_obj *qo,
+ struct bnx2x_exeq_elem *elem)
+{
+ struct bnx2x_exeq_elem query, *pos;
+ struct bnx2x_vlan_mac_obj *o = &qo->vlan_mac;
+ struct bnx2x_exe_queue_obj *exeq = &o->exe_queue;
+
+ memcpy(&query, elem, sizeof(query));
+
+ switch (elem->cmd_data.vlan_mac.cmd) {
+ case BNX2X_VLAN_MAC_ADD:
+ query.cmd_data.vlan_mac.cmd = BNX2X_VLAN_MAC_DEL;
+ break;
+ case BNX2X_VLAN_MAC_DEL:
+ query.cmd_data.vlan_mac.cmd = BNX2X_VLAN_MAC_ADD;
+ break;
+ default:
+ /* Don't handle anything other than ADD or DEL */
+ return 0;
+ }
+
+ /* If we found the appropriate element - delete it */
+ pos = exeq->get(exeq, &query);
+ if (pos) {
+
+ /* Return the credit of the optimized command */
+ if (!test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT,
+ &pos->cmd_data.vlan_mac.vlan_mac_flags)) {
+ if ((query.cmd_data.vlan_mac.cmd ==
+ BNX2X_VLAN_MAC_ADD) && !o->put_credit(o)) {
+ BNX2X_ERR("Failed to return the credit for the "
+ "optimized ADD command\n");
+ return -EINVAL;
+ } else if (!o->get_credit(o)) { /* VLAN_MAC_DEL */
+ BNX2X_ERR("Failed to recover the credit from "
+ "the optimized DEL command\n");
+ return -EINVAL;
+ }
+ }
+
+ DP(BNX2X_MSG_SP, "Optimizing %s command\n",
+ (elem->cmd_data.vlan_mac.cmd == BNX2X_VLAN_MAC_ADD) ?
+ "ADD" : "DEL");
+
+ list_del(&pos->link);
+ bnx2x_exe_queue_free_elem(bp, pos);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * bnx2x_vlan_mac_get_registry_elem - prepare a registry element
+ *
+ * @bp: device handle
+ * @o:
+ * @elem:
+ * @restore:
+ * @re:
+ *
+ * prepare a registry element according to the current command request.
+ */
+static inline int bnx2x_vlan_mac_get_registry_elem(
+ struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o,
+ struct bnx2x_exeq_elem *elem,
+ bool restore,
+ struct bnx2x_vlan_mac_registry_elem **re)
+{
+ int cmd = elem->cmd_data.vlan_mac.cmd;
+ struct bnx2x_vlan_mac_registry_elem *reg_elem;
+
+ /* Allocate a new registry element if needed. */
+ if (!restore &&
+ ((cmd == BNX2X_VLAN_MAC_ADD) || (cmd == BNX2X_VLAN_MAC_MOVE))) {
+ reg_elem = kzalloc(sizeof(*reg_elem), GFP_ATOMIC);
+ if (!reg_elem)
+ return -ENOMEM;
+
+ /* Get a new CAM offset */
+ if (!o->get_cam_offset(o, &reg_elem->cam_offset)) {
+ /*
+ * This shell never happen, because we have checked the
+ * CAM availiability in the 'validate'.
+ */
+ WARN_ON(1);
+ kfree(reg_elem);
+ return -EINVAL;
+ }
+
+ DP(BNX2X_MSG_SP, "Got cam offset %d\n", reg_elem->cam_offset);
+
+ /* Set a VLAN-MAC data */
+ memcpy(&reg_elem->u, &elem->cmd_data.vlan_mac.u,
+ sizeof(reg_elem->u));
+
+ /* Copy the flags (needed for DEL and RESTORE flows) */
+ reg_elem->vlan_mac_flags =
+ elem->cmd_data.vlan_mac.vlan_mac_flags;
+ } else /* DEL, RESTORE */
+ reg_elem = o->check_del(o, &elem->cmd_data.vlan_mac.u);
+
+ *re = reg_elem;
+ return 0;
+}
+
+/**
+ * bnx2x_execute_vlan_mac - execute vlan mac command
+ *
+ * @bp: device handle
+ * @qo:
+ * @exe_chunk:
+ * @ramrod_flags:
+ *
+ * go and send a ramrod!
+ */
+static int bnx2x_execute_vlan_mac(struct bnx2x *bp,
+ union bnx2x_qable_obj *qo,
+ struct list_head *exe_chunk,
+ unsigned long *ramrod_flags)
+{
+ struct bnx2x_exeq_elem *elem;
+ struct bnx2x_vlan_mac_obj *o = &qo->vlan_mac, *cam_obj;
+ struct bnx2x_raw_obj *r = &o->raw;
+ int rc, idx = 0;
+ bool restore = test_bit(RAMROD_RESTORE, ramrod_flags);
+ bool drv_only = test_bit(RAMROD_DRV_CLR_ONLY, ramrod_flags);
+ struct bnx2x_vlan_mac_registry_elem *reg_elem;
+ int cmd;
+
+ /*
+ * If DRIVER_ONLY execution is requested, cleanup a registry
+ * and exit. Otherwise send a ramrod to FW.
+ */
+ if (!drv_only) {
+ WARN_ON(r->check_pending(r));
+
+ /* Set pending */
+ r->set_pending(r);
+
+ /* Fill tha ramrod data */
+ list_for_each_entry(elem, exe_chunk, link) {
+ cmd = elem->cmd_data.vlan_mac.cmd;
+ /*
+ * We will add to the target object in MOVE command, so
+ * change the object for a CAM search.
+ */
+ if (cmd == BNX2X_VLAN_MAC_MOVE)
+ cam_obj = elem->cmd_data.vlan_mac.target_obj;
+ else
+ cam_obj = o;
+
+ rc = bnx2x_vlan_mac_get_registry_elem(bp, cam_obj,
+ elem, restore,
+ &reg_elem);
+ if (rc)
+ goto error_exit;
+
+ WARN_ON(!reg_elem);
+
+ /* Push a new entry into the registry */
+ if (!restore &&
+ ((cmd == BNX2X_VLAN_MAC_ADD) ||
+ (cmd == BNX2X_VLAN_MAC_MOVE)))
+ list_add(&reg_elem->link, &cam_obj->head);
+
+ /* Configure a single command in a ramrod data buffer */
+ o->set_one_rule(bp, o, elem, idx,
+ reg_elem->cam_offset);
+
+ /* MOVE command consumes 2 entries in the ramrod data */
+ if (cmd == BNX2X_VLAN_MAC_MOVE)
+ idx += 2;
+ else
+ idx++;
+ }
+
+ /*
+ * No need for an explicit memory barrier here as long we would
+ * need to ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read and we will have to put a full memory barrier there
+ * (inside bnx2x_sp_post()).
+ */
+
+ rc = bnx2x_sp_post(bp, o->ramrod_cmd, r->cid,
+ U64_HI(r->rdata_mapping),
+ U64_LO(r->rdata_mapping),
+ ETH_CONNECTION_TYPE);
+ if (rc)
+ goto error_exit;
+ }
+
+ /* Now, when we are done with the ramrod - clean up the registry */
+ list_for_each_entry(elem, exe_chunk, link) {
+ cmd = elem->cmd_data.vlan_mac.cmd;
+ if ((cmd == BNX2X_VLAN_MAC_DEL) ||
+ (cmd == BNX2X_VLAN_MAC_MOVE)) {
+ reg_elem = o->check_del(o, &elem->cmd_data.vlan_mac.u);
+
+ WARN_ON(!reg_elem);
+
+ o->put_cam_offset(o, reg_elem->cam_offset);
+ list_del(&reg_elem->link);
+ kfree(reg_elem);
+ }
+ }
+
+ if (!drv_only)
+ return 1;
+ else
+ return 0;
+
+error_exit:
+ r->clear_pending(r);
+
+ /* Cleanup a registry in case of a failure */
+ list_for_each_entry(elem, exe_chunk, link) {
+ cmd = elem->cmd_data.vlan_mac.cmd;
+
+ if (cmd == BNX2X_VLAN_MAC_MOVE)
+ cam_obj = elem->cmd_data.vlan_mac.target_obj;
+ else
+ cam_obj = o;
+
+ /* Delete all newly added above entries */
+ if (!restore &&
+ ((cmd == BNX2X_VLAN_MAC_ADD) ||
+ (cmd == BNX2X_VLAN_MAC_MOVE))) {
+ reg_elem = o->check_del(cam_obj,
+ &elem->cmd_data.vlan_mac.u);
+ if (reg_elem) {
+ list_del(&reg_elem->link);
+ kfree(reg_elem);
+ }
+ }
+ }
+
+ return rc;
+}
+
+static inline int bnx2x_vlan_mac_push_new_cmd(
+ struct bnx2x *bp,
+ struct bnx2x_vlan_mac_ramrod_params *p)
+{
+ struct bnx2x_exeq_elem *elem;
+ struct bnx2x_vlan_mac_obj *o = p->vlan_mac_obj;
+ bool restore = test_bit(RAMROD_RESTORE, &p->ramrod_flags);
+
+ /* Allocate the execution queue element */
+ elem = bnx2x_exe_queue_alloc_elem(bp);
+ if (!elem)
+ return -ENOMEM;
+
+ /* Set the command 'length' */
+ switch (p->user_req.cmd) {
+ case BNX2X_VLAN_MAC_MOVE:
+ elem->cmd_len = 2;
+ break;
+ default:
+ elem->cmd_len = 1;
+ }
+
+ /* Fill the object specific info */
+ memcpy(&elem->cmd_data.vlan_mac, &p->user_req, sizeof(p->user_req));
+
+ /* Try to add a new command to the pending list */
+ return bnx2x_exe_queue_add(bp, &o->exe_queue, elem, restore);
+}
+
+/**
+ * bnx2x_config_vlan_mac - configure VLAN/MAC/VLAN_MAC filtering rules.
+ *
+ * @bp: device handle
+ * @p:
+ *
+ */
+int bnx2x_config_vlan_mac(
+ struct bnx2x *bp,
+ struct bnx2x_vlan_mac_ramrod_params *p)
+{
+ int rc = 0;
+ struct bnx2x_vlan_mac_obj *o = p->vlan_mac_obj;
+ unsigned long *ramrod_flags = &p->ramrod_flags;
+ bool cont = test_bit(RAMROD_CONT, ramrod_flags);
+ struct bnx2x_raw_obj *raw = &o->raw;
+
+ /*
+ * Add new elements to the execution list for commands that require it.
+ */
+ if (!cont) {
+ rc = bnx2x_vlan_mac_push_new_cmd(bp, p);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * If nothing will be executed further in this iteration we want to
+ * return PENDING if there are pending commands
+ */
+ if (!bnx2x_exe_queue_empty(&o->exe_queue))
+ rc = 1;
+
+ if (test_bit(RAMROD_DRV_CLR_ONLY, ramrod_flags)) {
+ DP(BNX2X_MSG_SP, "RAMROD_DRV_CLR_ONLY requested: "
+ "clearing a pending bit.\n");
+ raw->clear_pending(raw);
+ }
+
+ /* Execute commands if required */
+ if (cont || test_bit(RAMROD_EXEC, ramrod_flags) ||
+ test_bit(RAMROD_COMP_WAIT, ramrod_flags)) {
+ rc = bnx2x_exe_queue_step(bp, &o->exe_queue, ramrod_flags);
+ if (rc < 0)
+ return rc;
+ }
+
+ /*
+ * RAMROD_COMP_WAIT is a superset of RAMROD_EXEC. If it was set
+ * then user want to wait until the last command is done.
+ */
+ if (test_bit(RAMROD_COMP_WAIT, &p->ramrod_flags)) {
+ /*
+ * Wait maximum for the current exe_queue length iterations plus
+ * one (for the current pending command).
+ */
+ int max_iterations = bnx2x_exe_queue_length(&o->exe_queue) + 1;
+
+ while (!bnx2x_exe_queue_empty(&o->exe_queue) &&
+ max_iterations--) {
+
+ /* Wait for the current command to complete */
+ rc = raw->wait_comp(bp, raw);
+ if (rc)
+ return rc;
+
+ /* Make a next step */
+ rc = bnx2x_exe_queue_step(bp, &o->exe_queue,
+ ramrod_flags);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+ }
+
+ return rc;
+}
+
+
+
+/**
+ * bnx2x_vlan_mac_del_all - delete elements with given vlan_mac_flags spec
+ *
+ * @bp: device handle
+ * @o:
+ * @vlan_mac_flags:
+ * @ramrod_flags: execution flags to be used for this deletion
+ *
+ * if the last operation has completed successfully and there are no
+ * moreelements left, positive value if the last operation has completed
+ * successfully and there are more previously configured elements, negative
+ * value is current operation has failed.
+ */
+static int bnx2x_vlan_mac_del_all(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o,
+ unsigned long *vlan_mac_flags,
+ unsigned long *ramrod_flags)
+{
+ struct bnx2x_vlan_mac_registry_elem *pos = NULL;
+ int rc = 0;
+ struct bnx2x_vlan_mac_ramrod_params p;
+ struct bnx2x_exe_queue_obj *exeq = &o->exe_queue;
+ struct bnx2x_exeq_elem *exeq_pos, *exeq_pos_n;
+
+ /* Clear pending commands first */
+
+ spin_lock_bh(&exeq->lock);
+
+ list_for_each_entry_safe(exeq_pos, exeq_pos_n, &exeq->exe_queue, link) {
+ if (exeq_pos->cmd_data.vlan_mac.vlan_mac_flags ==
+ *vlan_mac_flags)
+ list_del(&exeq_pos->link);
+ }
+
+ spin_unlock_bh(&exeq->lock);
+
+ /* Prepare a command request */
+ memset(&p, 0, sizeof(p));
+ p.vlan_mac_obj = o;
+ p.ramrod_flags = *ramrod_flags;
+ p.user_req.cmd = BNX2X_VLAN_MAC_DEL;
+
+ /*
+ * Add all but the last VLAN-MAC to the execution queue without actually
+ * execution anything.
+ */
+ __clear_bit(RAMROD_COMP_WAIT, &p.ramrod_flags);
+ __clear_bit(RAMROD_EXEC, &p.ramrod_flags);
+ __clear_bit(RAMROD_CONT, &p.ramrod_flags);
+
+ list_for_each_entry(pos, &o->head, link) {
+ if (pos->vlan_mac_flags == *vlan_mac_flags) {
+ p.user_req.vlan_mac_flags = pos->vlan_mac_flags;
+ memcpy(&p.user_req.u, &pos->u, sizeof(pos->u));
+ rc = bnx2x_config_vlan_mac(bp, &p);
+ if (rc < 0) {
+ BNX2X_ERR("Failed to add a new DEL command\n");
+ return rc;
+ }
+ }
+ }
+
+ p.ramrod_flags = *ramrod_flags;
+ __set_bit(RAMROD_CONT, &p.ramrod_flags);
+
+ return bnx2x_config_vlan_mac(bp, &p);
+}
+
+static inline void bnx2x_init_raw_obj(struct bnx2x_raw_obj *raw, u8 cl_id,
+ u32 cid, u8 func_id, void *rdata, dma_addr_t rdata_mapping, int state,
+ unsigned long *pstate, bnx2x_obj_type type)
+{
+ raw->func_id = func_id;
+ raw->cid = cid;
+ raw->cl_id = cl_id;
+ raw->rdata = rdata;
+ raw->rdata_mapping = rdata_mapping;
+ raw->state = state;
+ raw->pstate = pstate;
+ raw->obj_type = type;
+ raw->check_pending = bnx2x_raw_check_pending;
+ raw->clear_pending = bnx2x_raw_clear_pending;
+ raw->set_pending = bnx2x_raw_set_pending;
+ raw->wait_comp = bnx2x_raw_wait;
+}
+
+static inline void bnx2x_init_vlan_mac_common(struct bnx2x_vlan_mac_obj *o,
+ u8 cl_id, u32 cid, u8 func_id, void *rdata, dma_addr_t rdata_mapping,
+ int state, unsigned long *pstate, bnx2x_obj_type type,
+ struct bnx2x_credit_pool_obj *macs_pool,
+ struct bnx2x_credit_pool_obj *vlans_pool)
+{
+ INIT_LIST_HEAD(&o->head);
+
+ o->macs_pool = macs_pool;
+ o->vlans_pool = vlans_pool;
+
+ o->delete_all = bnx2x_vlan_mac_del_all;
+ o->restore = bnx2x_vlan_mac_restore;
+ o->complete = bnx2x_complete_vlan_mac;
+ o->wait = bnx2x_wait_vlan_mac;
+
+ bnx2x_init_raw_obj(&o->raw, cl_id, cid, func_id, rdata, rdata_mapping,
+ state, pstate, type);
+}
+
+
+void bnx2x_init_mac_obj(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *mac_obj,
+ u8 cl_id, u32 cid, u8 func_id, void *rdata,
+ dma_addr_t rdata_mapping, int state,
+ unsigned long *pstate, bnx2x_obj_type type,
+ struct bnx2x_credit_pool_obj *macs_pool)
+{
+ union bnx2x_qable_obj *qable_obj = (union bnx2x_qable_obj *)mac_obj;
+
+ bnx2x_init_vlan_mac_common(mac_obj, cl_id, cid, func_id, rdata,
+ rdata_mapping, state, pstate, type,
+ macs_pool, NULL);
+
+ /* CAM credit pool handling */
+ mac_obj->get_credit = bnx2x_get_credit_mac;
+ mac_obj->put_credit = bnx2x_put_credit_mac;
+ mac_obj->get_cam_offset = bnx2x_get_cam_offset_mac;
+ mac_obj->put_cam_offset = bnx2x_put_cam_offset_mac;
+
+ if (CHIP_IS_E1x(bp)) {
+ mac_obj->set_one_rule = bnx2x_set_one_mac_e1x;
+ mac_obj->check_del = bnx2x_check_mac_del;
+ mac_obj->check_add = bnx2x_check_mac_add;
+ mac_obj->check_move = bnx2x_check_move_always_err;
+ mac_obj->ramrod_cmd = RAMROD_CMD_ID_ETH_SET_MAC;
+
+ /* Exe Queue */
+ bnx2x_exe_queue_init(bp,
+ &mac_obj->exe_queue, 1, qable_obj,
+ bnx2x_validate_vlan_mac,
+ bnx2x_optimize_vlan_mac,
+ bnx2x_execute_vlan_mac,
+ bnx2x_exeq_get_mac);
+ } else {
+ mac_obj->set_one_rule = bnx2x_set_one_mac_e2;
+ mac_obj->check_del = bnx2x_check_mac_del;
+ mac_obj->check_add = bnx2x_check_mac_add;
+ mac_obj->check_move = bnx2x_check_move;
+ mac_obj->ramrod_cmd =
+ RAMROD_CMD_ID_ETH_CLASSIFICATION_RULES;
+
+ /* Exe Queue */
+ bnx2x_exe_queue_init(bp,
+ &mac_obj->exe_queue, CLASSIFY_RULES_COUNT,
+ qable_obj, bnx2x_validate_vlan_mac,
+ bnx2x_optimize_vlan_mac,
+ bnx2x_execute_vlan_mac,
+ bnx2x_exeq_get_mac);
+ }
+}
+
+void bnx2x_init_vlan_obj(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *vlan_obj,
+ u8 cl_id, u32 cid, u8 func_id, void *rdata,
+ dma_addr_t rdata_mapping, int state,
+ unsigned long *pstate, bnx2x_obj_type type,
+ struct bnx2x_credit_pool_obj *vlans_pool)
+{
+ union bnx2x_qable_obj *qable_obj = (union bnx2x_qable_obj *)vlan_obj;
+
+ bnx2x_init_vlan_mac_common(vlan_obj, cl_id, cid, func_id, rdata,
+ rdata_mapping, state, pstate, type, NULL,
+ vlans_pool);
+
+ vlan_obj->get_credit = bnx2x_get_credit_vlan;
+ vlan_obj->put_credit = bnx2x_put_credit_vlan;
+ vlan_obj->get_cam_offset = bnx2x_get_cam_offset_vlan;
+ vlan_obj->put_cam_offset = bnx2x_put_cam_offset_vlan;
+
+ if (CHIP_IS_E1x(bp)) {
+ BNX2X_ERR("Do not support chips others than E2 and newer\n");
+ BUG();
+ } else {
+ vlan_obj->set_one_rule = bnx2x_set_one_vlan_e2;
+ vlan_obj->check_del = bnx2x_check_vlan_del;
+ vlan_obj->check_add = bnx2x_check_vlan_add;
+ vlan_obj->check_move = bnx2x_check_move;
+ vlan_obj->ramrod_cmd =
+ RAMROD_CMD_ID_ETH_CLASSIFICATION_RULES;
+
+ /* Exe Queue */
+ bnx2x_exe_queue_init(bp,
+ &vlan_obj->exe_queue, CLASSIFY_RULES_COUNT,
+ qable_obj, bnx2x_validate_vlan_mac,
+ bnx2x_optimize_vlan_mac,
+ bnx2x_execute_vlan_mac,
+ bnx2x_exeq_get_vlan);
+ }
+}
+
+void bnx2x_init_vlan_mac_obj(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *vlan_mac_obj,
+ u8 cl_id, u32 cid, u8 func_id, void *rdata,
+ dma_addr_t rdata_mapping, int state,
+ unsigned long *pstate, bnx2x_obj_type type,
+ struct bnx2x_credit_pool_obj *macs_pool,
+ struct bnx2x_credit_pool_obj *vlans_pool)
+{
+ union bnx2x_qable_obj *qable_obj =
+ (union bnx2x_qable_obj *)vlan_mac_obj;
+
+ bnx2x_init_vlan_mac_common(vlan_mac_obj, cl_id, cid, func_id, rdata,
+ rdata_mapping, state, pstate, type,
+ macs_pool, vlans_pool);
+
+ /* CAM pool handling */
+ vlan_mac_obj->get_credit = bnx2x_get_credit_vlan_mac;
+ vlan_mac_obj->put_credit = bnx2x_put_credit_vlan_mac;
+ /*
+ * CAM offset is relevant for 57710 and 57711 chips only which have a
+ * single CAM for both MACs and VLAN-MAC pairs. So the offset
+ * will be taken from MACs' pool object only.
+ */
+ vlan_mac_obj->get_cam_offset = bnx2x_get_cam_offset_mac;
+ vlan_mac_obj->put_cam_offset = bnx2x_put_cam_offset_mac;
+
+ if (CHIP_IS_E1(bp)) {
+ BNX2X_ERR("Do not support chips others than E2\n");
+ BUG();
+ } else if (CHIP_IS_E1H(bp)) {
+ vlan_mac_obj->set_one_rule = bnx2x_set_one_vlan_mac_e1h;
+ vlan_mac_obj->check_del = bnx2x_check_vlan_mac_del;
+ vlan_mac_obj->check_add = bnx2x_check_vlan_mac_add;
+ vlan_mac_obj->check_move = bnx2x_check_move_always_err;
+ vlan_mac_obj->ramrod_cmd = RAMROD_CMD_ID_ETH_SET_MAC;
+
+ /* Exe Queue */
+ bnx2x_exe_queue_init(bp,
+ &vlan_mac_obj->exe_queue, 1, qable_obj,
+ bnx2x_validate_vlan_mac,
+ bnx2x_optimize_vlan_mac,
+ bnx2x_execute_vlan_mac,
+ bnx2x_exeq_get_vlan_mac);
+ } else {
+ vlan_mac_obj->set_one_rule = bnx2x_set_one_vlan_mac_e2;
+ vlan_mac_obj->check_del = bnx2x_check_vlan_mac_del;
+ vlan_mac_obj->check_add = bnx2x_check_vlan_mac_add;
+ vlan_mac_obj->check_move = bnx2x_check_move;
+ vlan_mac_obj->ramrod_cmd =
+ RAMROD_CMD_ID_ETH_CLASSIFICATION_RULES;
+
+ /* Exe Queue */
+ bnx2x_exe_queue_init(bp,
+ &vlan_mac_obj->exe_queue,
+ CLASSIFY_RULES_COUNT,
+ qable_obj, bnx2x_validate_vlan_mac,
+ bnx2x_optimize_vlan_mac,
+ bnx2x_execute_vlan_mac,
+ bnx2x_exeq_get_vlan_mac);
+ }
+
+}
+
+/* RX_MODE verbs: DROP_ALL/ACCEPT_ALL/ACCEPT_ALL_MULTI/ACCEPT_ALL_VLAN/NORMAL */
+static inline void __storm_memset_mac_filters(struct bnx2x *bp,
+ struct tstorm_eth_mac_filter_config *mac_filters,
+ u16 pf_id)
+{
+ size_t size = sizeof(struct tstorm_eth_mac_filter_config);
+
+ u32 addr = BAR_TSTRORM_INTMEM +
+ TSTORM_MAC_FILTER_CONFIG_OFFSET(pf_id);
+
+ __storm_memset_struct(bp, addr, size, (u32 *)mac_filters);
+}
+
+static int bnx2x_set_rx_mode_e1x(struct bnx2x *bp,
+ struct bnx2x_rx_mode_ramrod_params *p)
+{
+ /* update the bp MAC filter structure */
+ u32 mask = (1 << p->cl_id);
+
+ struct tstorm_eth_mac_filter_config *mac_filters =
+ (struct tstorm_eth_mac_filter_config *)p->rdata;
+
+ /* initial seeting is drop-all */
+ u8 drop_all_ucast = 1, drop_all_mcast = 1;
+ u8 accp_all_ucast = 0, accp_all_bcast = 0, accp_all_mcast = 0;
+ u8 unmatched_unicast = 0;
+
+ /* In e1x there we only take into account rx acceot flag since tx switching
+ * isn't enabled. */
+ if (test_bit(BNX2X_ACCEPT_UNICAST, &p->rx_accept_flags))
+ /* accept matched ucast */
+ drop_all_ucast = 0;
+
+ if (test_bit(BNX2X_ACCEPT_MULTICAST, &p->rx_accept_flags))
+ /* accept matched mcast */
+ drop_all_mcast = 0;
+
+ if (test_bit(BNX2X_ACCEPT_ALL_UNICAST, &p->rx_accept_flags)) {
+ /* accept all mcast */
+ drop_all_ucast = 0;
+ accp_all_ucast = 1;
+ }
+ if (test_bit(BNX2X_ACCEPT_ALL_MULTICAST, &p->rx_accept_flags)) {
+ /* accept all mcast */
+ drop_all_mcast = 0;
+ accp_all_mcast = 1;
+ }
+ if (test_bit(BNX2X_ACCEPT_BROADCAST, &p->rx_accept_flags))
+ /* accept (all) bcast */
+ accp_all_bcast = 1;
+ if (test_bit(BNX2X_ACCEPT_UNMATCHED, &p->rx_accept_flags))
+ /* accept unmatched unicasts */
+ unmatched_unicast = 1;
+
+ mac_filters->ucast_drop_all = drop_all_ucast ?
+ mac_filters->ucast_drop_all | mask :
+ mac_filters->ucast_drop_all & ~mask;
+
+ mac_filters->mcast_drop_all = drop_all_mcast ?
+ mac_filters->mcast_drop_all | mask :
+ mac_filters->mcast_drop_all & ~mask;
+
+ mac_filters->ucast_accept_all = accp_all_ucast ?
+ mac_filters->ucast_accept_all | mask :
+ mac_filters->ucast_accept_all & ~mask;
+
+ mac_filters->mcast_accept_all = accp_all_mcast ?
+ mac_filters->mcast_accept_all | mask :
+ mac_filters->mcast_accept_all & ~mask;
+
+ mac_filters->bcast_accept_all = accp_all_bcast ?
+ mac_filters->bcast_accept_all | mask :
+ mac_filters->bcast_accept_all & ~mask;
+
+ mac_filters->unmatched_unicast = unmatched_unicast ?
+ mac_filters->unmatched_unicast | mask :
+ mac_filters->unmatched_unicast & ~mask;
+
+ DP(BNX2X_MSG_SP, "drop_ucast 0x%x\ndrop_mcast 0x%x\n accp_ucast 0x%x\n"
+ "accp_mcast 0x%x\naccp_bcast 0x%x\n",
+ mac_filters->ucast_drop_all,
+ mac_filters->mcast_drop_all,
+ mac_filters->ucast_accept_all,
+ mac_filters->mcast_accept_all,
+ mac_filters->bcast_accept_all);
+
+ /* write the MAC filter structure*/
+ __storm_memset_mac_filters(bp, mac_filters, p->func_id);
+
+ /* The operation is completed */
+ clear_bit(p->state, p->pstate);
+ smp_mb__after_clear_bit();
+
+ return 0;
+}
+
+/* Setup ramrod data */
+static inline void bnx2x_rx_mode_set_rdata_hdr_e2(u32 cid,
+ struct eth_classify_header *hdr,
+ u8 rule_cnt)
+{
+ hdr->echo = cid;
+ hdr->rule_cnt = rule_cnt;
+}
+
+static inline void bnx2x_rx_mode_set_cmd_state_e2(struct bnx2x *bp,
+ unsigned long accept_flags,
+ struct eth_filter_rules_cmd *cmd,
+ bool clear_accept_all)
+{
+ u16 state;
+
+ /* start with 'drop-all' */
+ state = ETH_FILTER_RULES_CMD_UCAST_DROP_ALL |
+ ETH_FILTER_RULES_CMD_MCAST_DROP_ALL;
+
+ if (accept_flags) {
+ if (test_bit(BNX2X_ACCEPT_UNICAST, &accept_flags))
+ state &= ~ETH_FILTER_RULES_CMD_UCAST_DROP_ALL;
+
+ if (test_bit(BNX2X_ACCEPT_MULTICAST, &accept_flags))
+ state &= ~ETH_FILTER_RULES_CMD_MCAST_DROP_ALL;
+
+ if (test_bit(BNX2X_ACCEPT_ALL_UNICAST, &accept_flags)) {
+ state &= ~ETH_FILTER_RULES_CMD_UCAST_DROP_ALL;
+ state |= ETH_FILTER_RULES_CMD_UCAST_ACCEPT_ALL;
+ }
+
+ if (test_bit(BNX2X_ACCEPT_ALL_MULTICAST, &accept_flags)) {
+ state |= ETH_FILTER_RULES_CMD_MCAST_ACCEPT_ALL;
+ state &= ~ETH_FILTER_RULES_CMD_MCAST_DROP_ALL;
+ }
+ if (test_bit(BNX2X_ACCEPT_BROADCAST, &accept_flags))
+ state |= ETH_FILTER_RULES_CMD_BCAST_ACCEPT_ALL;
+
+ if (test_bit(BNX2X_ACCEPT_UNMATCHED, &accept_flags)) {
+ state &= ~ETH_FILTER_RULES_CMD_UCAST_DROP_ALL;
+ state |= ETH_FILTER_RULES_CMD_UCAST_ACCEPT_UNMATCHED;
+ }
+ if (test_bit(BNX2X_ACCEPT_ANY_VLAN, &accept_flags))
+ state |= ETH_FILTER_RULES_CMD_ACCEPT_ANY_VLAN;
+ }
+
+ /* Clear ACCEPT_ALL_XXX flags for FCoE L2 Queue */
+ if (clear_accept_all) {
+ state &= ~ETH_FILTER_RULES_CMD_MCAST_ACCEPT_ALL;
+ state &= ~ETH_FILTER_RULES_CMD_BCAST_ACCEPT_ALL;
+ state &= ~ETH_FILTER_RULES_CMD_UCAST_ACCEPT_ALL;
+ state &= ~ETH_FILTER_RULES_CMD_UCAST_ACCEPT_UNMATCHED;
+ }
+
+ cmd->state = cpu_to_le16(state);
+
+}
+
+static int bnx2x_set_rx_mode_e2(struct bnx2x *bp,
+ struct bnx2x_rx_mode_ramrod_params *p)
+{
+ struct eth_filter_rules_ramrod_data *data = p->rdata;
+ int rc;
+ u8 rule_idx = 0;
+
+ /* Reset the ramrod data buffer */
+ memset(data, 0, sizeof(*data));
+
+ /* Setup ramrod data */
+
+ /* Tx (internal switching) */
+ if (test_bit(RAMROD_TX, &p->ramrod_flags)) {
+ data->rules[rule_idx].client_id = p->cl_id;
+ data->rules[rule_idx].func_id = p->func_id;
+
+ data->rules[rule_idx].cmd_general_data =
+ ETH_FILTER_RULES_CMD_TX_CMD;
+
+ bnx2x_rx_mode_set_cmd_state_e2(bp, p->tx_accept_flags,
+ &(data->rules[rule_idx++]), false);
+ }
+
+ /* Rx */
+ if (test_bit(RAMROD_RX, &p->ramrod_flags)) {
+ data->rules[rule_idx].client_id = p->cl_id;
+ data->rules[rule_idx].func_id = p->func_id;
+
+ data->rules[rule_idx].cmd_general_data =
+ ETH_FILTER_RULES_CMD_RX_CMD;
+
+ bnx2x_rx_mode_set_cmd_state_e2(bp, p->rx_accept_flags,
+ &(data->rules[rule_idx++]), false);
+ }
+
+
+ /*
+ * If FCoE Queue configuration has been requested configure the Rx and
+ * internal switching modes for this queue in separate rules.
+ *
+ * FCoE queue shell never be set to ACCEPT_ALL packets of any sort:
+ * MCAST_ALL, UCAST_ALL, BCAST_ALL and UNMATCHED.
+ */
+ if (test_bit(BNX2X_RX_MODE_FCOE_ETH, &p->rx_mode_flags)) {
+ /* Tx (internal switching) */
+ if (test_bit(RAMROD_TX, &p->ramrod_flags)) {
+ data->rules[rule_idx].client_id = bnx2x_fcoe(bp, cl_id);
+ data->rules[rule_idx].func_id = p->func_id;
+
+ data->rules[rule_idx].cmd_general_data =
+ ETH_FILTER_RULES_CMD_TX_CMD;
+
+ bnx2x_rx_mode_set_cmd_state_e2(bp, p->tx_accept_flags,
+ &(data->rules[rule_idx++]),
+ true);
+ }
+
+ /* Rx */
+ if (test_bit(RAMROD_RX, &p->ramrod_flags)) {
+ data->rules[rule_idx].client_id = bnx2x_fcoe(bp, cl_id);
+ data->rules[rule_idx].func_id = p->func_id;
+
+ data->rules[rule_idx].cmd_general_data =
+ ETH_FILTER_RULES_CMD_RX_CMD;
+
+ bnx2x_rx_mode_set_cmd_state_e2(bp, p->rx_accept_flags,
+ &(data->rules[rule_idx++]),
+ true);
+ }
+ }
+
+ /*
+ * Set the ramrod header (most importantly - number of rules to
+ * configure).
+ */
+ bnx2x_rx_mode_set_rdata_hdr_e2(p->cid, &data->header, rule_idx);
+
+ DP(BNX2X_MSG_SP, "About to configure %d rules, rx_accept_flags 0x%lx, "
+ "tx_accept_flags 0x%lx\n",
+ data->header.rule_cnt, p->rx_accept_flags,
+ p->tx_accept_flags);
+
+ /*
+ * No need for an explicit memory barrier here as long we would
+ * need to ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read and we will have to put a full memory barrier there
+ * (inside bnx2x_sp_post()).
+ */
+
+ /* Send a ramrod */
+ rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_FILTER_RULES, p->cid,
+ U64_HI(p->rdata_mapping),
+ U64_LO(p->rdata_mapping),
+ ETH_CONNECTION_TYPE);
+ if (rc)
+ return rc;
+
+ /* Ramrod completion is pending */
+ return 1;
+}
+
+static int bnx2x_wait_rx_mode_comp_e2(struct bnx2x *bp,
+ struct bnx2x_rx_mode_ramrod_params *p)
+{
+ return bnx2x_state_wait(bp, p->state, p->pstate);
+}
+
+static int bnx2x_empty_rx_mode_wait(struct bnx2x *bp,
+ struct bnx2x_rx_mode_ramrod_params *p)
+{
+ /* Do nothing */
+ return 0;
+}
+
+int bnx2x_config_rx_mode(struct bnx2x *bp,
+ struct bnx2x_rx_mode_ramrod_params *p)
+{
+ int rc;
+
+ /* Configure the new classification in the chip */
+ rc = p->rx_mode_obj->config_rx_mode(bp, p);
+ if (rc < 0)
+ return rc;
+
+ /* Wait for a ramrod completion if was requested */
+ if (test_bit(RAMROD_COMP_WAIT, &p->ramrod_flags)) {
+ rc = p->rx_mode_obj->wait_comp(bp, p);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+void bnx2x_init_rx_mode_obj(struct bnx2x *bp,
+ struct bnx2x_rx_mode_obj *o)
+{
+ if (CHIP_IS_E1x(bp)) {
+ o->wait_comp = bnx2x_empty_rx_mode_wait;
+ o->config_rx_mode = bnx2x_set_rx_mode_e1x;
+ } else {
+ o->wait_comp = bnx2x_wait_rx_mode_comp_e2;
+ o->config_rx_mode = bnx2x_set_rx_mode_e2;
+ }
+}
+
+/********************* Multicast verbs: SET, CLEAR ****************************/
+static inline u8 bnx2x_mcast_bin_from_mac(u8 *mac)
+{
+ return (crc32c_le(0, mac, ETH_ALEN) >> 24) & 0xff;
+}
+
+struct bnx2x_mcast_mac_elem {
+ struct list_head link;
+ u8 mac[ETH_ALEN];
+ u8 pad[2]; /* For a natural alignment of the following buffer */
+};
+
+struct bnx2x_pending_mcast_cmd {
+ struct list_head link;
+ int type; /* BNX2X_MCAST_CMD_X */
+ union {
+ struct list_head macs_head;
+ u32 macs_num; /* Needed for DEL command */
+ int next_bin; /* Needed for RESTORE flow with aprox match */
+ } data;
+
+ bool done; /* set to true, when the command has been handled,
+ * practically used in 57712 handling only, where one pending
+ * command may be handled in a few operations. As long as for
+ * other chips every operation handling is completed in a
+ * single ramrod, there is no need to utilize this field.
+ */
+};
+
+static int bnx2x_mcast_wait(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o)
+{
+ if (bnx2x_state_wait(bp, o->sched_state, o->raw.pstate) ||
+ o->raw.wait_comp(bp, &o->raw))
+ return -EBUSY;
+
+ return 0;
+}
+
+static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o,
+ struct bnx2x_mcast_ramrod_params *p,
+ int cmd)
+{
+ int total_sz;
+ struct bnx2x_pending_mcast_cmd *new_cmd;
+ struct bnx2x_mcast_mac_elem *cur_mac = NULL;
+ struct bnx2x_mcast_list_elem *pos;
+ int macs_list_len = ((cmd == BNX2X_MCAST_CMD_ADD) ?
+ p->mcast_list_len : 0);
+
+ /* If the command is empty ("handle pending commands only"), break */
+ if (!p->mcast_list_len)
+ return 0;
+
+ total_sz = sizeof(*new_cmd) +
+ macs_list_len * sizeof(struct bnx2x_mcast_mac_elem);
+
+ /* Add mcast is called under spin_lock, thus calling with GFP_ATOMIC */
+ new_cmd = kzalloc(total_sz, GFP_ATOMIC);
+
+ if (!new_cmd)
+ return -ENOMEM;
+
+ DP(BNX2X_MSG_SP, "About to enqueue a new %d command. "
+ "macs_list_len=%d\n", cmd, macs_list_len);
+
+ INIT_LIST_HEAD(&new_cmd->data.macs_head);
+
+ new_cmd->type = cmd;
+ new_cmd->done = false;
+
+ switch (cmd) {
+ case BNX2X_MCAST_CMD_ADD:
+ cur_mac = (struct bnx2x_mcast_mac_elem *)
+ ((u8 *)new_cmd + sizeof(*new_cmd));
+
+ /* Push the MACs of the current command into the pendig command
+ * MACs list: FIFO
+ */
+ list_for_each_entry(pos, &p->mcast_list, link) {
+ memcpy(cur_mac->mac, pos->mac, ETH_ALEN);
+ list_add_tail(&cur_mac->link, &new_cmd->data.macs_head);
+ cur_mac++;
+ }
+
+ break;
+
+ case BNX2X_MCAST_CMD_DEL:
+ new_cmd->data.macs_num = p->mcast_list_len;
+ break;
+
+ case BNX2X_MCAST_CMD_RESTORE:
+ new_cmd->data.next_bin = 0;
+ break;
+
+ default:
+ BNX2X_ERR("Unknown command: %d\n", cmd);
+ return -EINVAL;
+ }
+
+ /* Push the new pending command to the tail of the pending list: FIFO */
+ list_add_tail(&new_cmd->link, &o->pending_cmds_head);
+
+ o->set_sched(o);
+
+ return 1;
+}
+
+/**
+ * bnx2x_mcast_get_next_bin - get the next set bin (index)
+ *
+ * @o:
+ * @last: index to start looking from (including)
+ *
+ * returns the next found (set) bin or a negative value if none is found.
+ */
+static inline int bnx2x_mcast_get_next_bin(struct bnx2x_mcast_obj *o, int last)
+{
+ int i, j, inner_start = last % BIT_VEC64_ELEM_SZ;
+
+ for (i = last / BIT_VEC64_ELEM_SZ; i < BNX2X_MCAST_VEC_SZ; i++) {
+ if (o->registry.aprox_match.vec[i])
+ for (j = inner_start; j < BIT_VEC64_ELEM_SZ; j++) {
+ int cur_bit = j + BIT_VEC64_ELEM_SZ * i;
+ if (BIT_VEC64_TEST_BIT(o->registry.aprox_match.
+ vec, cur_bit)) {
+ return cur_bit;
+ }
+ }
+ inner_start = 0;
+ }
+
+ /* None found */
+ return -1;
+}
+
+/**
+ * bnx2x_mcast_clear_first_bin - find the first set bin and clear it
+ *
+ * @o:
+ *
+ * returns the index of the found bin or -1 if none is found
+ */
+static inline int bnx2x_mcast_clear_first_bin(struct bnx2x_mcast_obj *o)
+{
+ int cur_bit = bnx2x_mcast_get_next_bin(o, 0);
+
+ if (cur_bit >= 0)
+ BIT_VEC64_CLEAR_BIT(o->registry.aprox_match.vec, cur_bit);
+
+ return cur_bit;
+}
+
+static inline u8 bnx2x_mcast_get_rx_tx_flag(struct bnx2x_mcast_obj *o)
+{
+ struct bnx2x_raw_obj *raw = &o->raw;
+ u8 rx_tx_flag = 0;
+
+ if ((raw->obj_type == BNX2X_OBJ_TYPE_TX) ||
+ (raw->obj_type == BNX2X_OBJ_TYPE_RX_TX))
+ rx_tx_flag |= ETH_MULTICAST_RULES_CMD_TX_CMD;
+
+ if ((raw->obj_type == BNX2X_OBJ_TYPE_RX) ||
+ (raw->obj_type == BNX2X_OBJ_TYPE_RX_TX))
+ rx_tx_flag |= ETH_MULTICAST_RULES_CMD_RX_CMD;
+
+ return rx_tx_flag;
+}
+
+static void bnx2x_mcast_set_one_rule_e2(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o, int idx,
+ union bnx2x_mcast_config_data *cfg_data,
+ int cmd)
+{
+ struct bnx2x_raw_obj *r = &o->raw;
+ struct eth_multicast_rules_ramrod_data *data =
+ (struct eth_multicast_rules_ramrod_data *)(r->rdata);
+ u8 func_id = r->func_id;
+ u8 rx_tx_add_flag = bnx2x_mcast_get_rx_tx_flag(o);
+ int bin;
+
+ if ((cmd == BNX2X_MCAST_CMD_ADD) || (cmd == BNX2X_MCAST_CMD_RESTORE))
+ rx_tx_add_flag |= ETH_MULTICAST_RULES_CMD_IS_ADD;
+
+ data->rules[idx].cmd_general_data |= rx_tx_add_flag;
+
+ /* Get a bin and update a bins' vector */
+ switch (cmd) {
+ case BNX2X_MCAST_CMD_ADD:
+ bin = bnx2x_mcast_bin_from_mac(cfg_data->mac);
+ BIT_VEC64_SET_BIT(o->registry.aprox_match.vec, bin);
+ break;
+
+ case BNX2X_MCAST_CMD_DEL:
+ /* If there were no more bins to clear
+ * (bnx2x_mcast_clear_first_bin() returns -1) then we would
+ * clear any (0xff) bin.
+ * See bnx2x_mcast_validate_e2() for explanation when it may
+ * happen.
+ */
+ bin = bnx2x_mcast_clear_first_bin(o);
+ break;
+
+ case BNX2X_MCAST_CMD_RESTORE:
+ bin = cfg_data->bin;
+ break;
+
+ default:
+ BNX2X_ERR("Unknown command: %d\n", cmd);
+ return;
+ }
+
+ DP(BNX2X_MSG_SP, "%s bin %d\n",
+ ((rx_tx_add_flag & ETH_MULTICAST_RULES_CMD_IS_ADD) ?
+ "Setting" : "Clearing"), bin);
+
+ data->rules[idx].bin_id = (u8)bin;
+ data->rules[idx].func_id = func_id;
+ data->rules[idx].engine_id = o->engine_id;
+}
+
+/**
+ * bnx2x_mcast_handle_restore_cmd_e2 - restore configuration from the registry
+ *
+ * @bp: device handle
+ * @o:
+ * @start_bin: index in the registry to start from (including)
+ * @rdata_idx: index in the ramrod data to start from
+ *
+ * returns last handled bin index or -1 if all bins have been handled
+ */
+static inline int bnx2x_mcast_handle_restore_cmd_e2(
+ struct bnx2x *bp, struct bnx2x_mcast_obj *o , int start_bin,
+ int *rdata_idx)
+{
+ int cur_bin, cnt = *rdata_idx;
+ union bnx2x_mcast_config_data cfg_data = {0};
+
+ /* go through the registry and configure the bins from it */
+ for (cur_bin = bnx2x_mcast_get_next_bin(o, start_bin); cur_bin >= 0;
+ cur_bin = bnx2x_mcast_get_next_bin(o, cur_bin + 1)) {
+
+ cfg_data.bin = (u8)cur_bin;
+ o->set_one_rule(bp, o, cnt, &cfg_data,
+ BNX2X_MCAST_CMD_RESTORE);
+
+ cnt++;
+
+ DP(BNX2X_MSG_SP, "About to configure a bin %d\n", cur_bin);
+
+ /* Break if we reached the maximum number
+ * of rules.
+ */
+ if (cnt >= o->max_cmd_len)
+ break;
+ }
+
+ *rdata_idx = cnt;
+
+ return cur_bin;
+}
+
+static inline void bnx2x_mcast_hdl_pending_add_e2(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o, struct bnx2x_pending_mcast_cmd *cmd_pos,
+ int *line_idx)
+{
+ struct bnx2x_mcast_mac_elem *pmac_pos, *pmac_pos_n;
+ int cnt = *line_idx;
+ union bnx2x_mcast_config_data cfg_data = {0};
+
+ list_for_each_entry_safe(pmac_pos, pmac_pos_n, &cmd_pos->data.macs_head,
+ link) {
+
+ cfg_data.mac = &pmac_pos->mac[0];
+ o->set_one_rule(bp, o, cnt, &cfg_data, cmd_pos->type);
+
+ cnt++;
+
+ DP(BNX2X_MSG_SP, "About to configure %pM mcast MAC\n",
+ pmac_pos->mac);
+
+ list_del(&pmac_pos->link);
+
+ /* Break if we reached the maximum number
+ * of rules.
+ */
+ if (cnt >= o->max_cmd_len)
+ break;
+ }
+
+ *line_idx = cnt;
+
+ /* if no more MACs to configure - we are done */
+ if (list_empty(&cmd_pos->data.macs_head))
+ cmd_pos->done = true;
+}
+
+static inline void bnx2x_mcast_hdl_pending_del_e2(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o, struct bnx2x_pending_mcast_cmd *cmd_pos,
+ int *line_idx)
+{
+ int cnt = *line_idx;
+
+ while (cmd_pos->data.macs_num) {
+ o->set_one_rule(bp, o, cnt, NULL, cmd_pos->type);
+
+ cnt++;
+
+ cmd_pos->data.macs_num--;
+
+ DP(BNX2X_MSG_SP, "Deleting MAC. %d left,cnt is %d\n",
+ cmd_pos->data.macs_num, cnt);
+
+ /* Break if we reached the maximum
+ * number of rules.
+ */
+ if (cnt >= o->max_cmd_len)
+ break;
+ }
+
+ *line_idx = cnt;
+
+ /* If we cleared all bins - we are done */
+ if (!cmd_pos->data.macs_num)
+ cmd_pos->done = true;
+}
+
+static inline void bnx2x_mcast_hdl_pending_restore_e2(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o, struct bnx2x_pending_mcast_cmd *cmd_pos,
+ int *line_idx)
+{
+ cmd_pos->data.next_bin = o->hdl_restore(bp, o, cmd_pos->data.next_bin,
+ line_idx);
+
+ if (cmd_pos->data.next_bin < 0)
+ /* If o->set_restore returned -1 we are done */
+ cmd_pos->done = true;
+ else
+ /* Start from the next bin next time */
+ cmd_pos->data.next_bin++;
+}
+
+static inline int bnx2x_mcast_handle_pending_cmds_e2(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p)
+{
+ struct bnx2x_pending_mcast_cmd *cmd_pos, *cmd_pos_n;
+ int cnt = 0;
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+
+ list_for_each_entry_safe(cmd_pos, cmd_pos_n, &o->pending_cmds_head,
+ link) {
+ switch (cmd_pos->type) {
+ case BNX2X_MCAST_CMD_ADD:
+ bnx2x_mcast_hdl_pending_add_e2(bp, o, cmd_pos, &cnt);
+ break;
+
+ case BNX2X_MCAST_CMD_DEL:
+ bnx2x_mcast_hdl_pending_del_e2(bp, o, cmd_pos, &cnt);
+ break;
+
+ case BNX2X_MCAST_CMD_RESTORE:
+ bnx2x_mcast_hdl_pending_restore_e2(bp, o, cmd_pos,
+ &cnt);
+ break;
+
+ default:
+ BNX2X_ERR("Unknown command: %d\n", cmd_pos->type);
+ return -EINVAL;
+ }
+
+ /* If the command has been completed - remove it from the list
+ * and free the memory
+ */
+ if (cmd_pos->done) {
+ list_del(&cmd_pos->link);
+ kfree(cmd_pos);
+ }
+
+ /* Break if we reached the maximum number of rules */
+ if (cnt >= o->max_cmd_len)
+ break;
+ }
+
+ return cnt;
+}
+
+static inline void bnx2x_mcast_hdl_add(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o, struct bnx2x_mcast_ramrod_params *p,
+ int *line_idx)
+{
+ struct bnx2x_mcast_list_elem *mlist_pos;
+ union bnx2x_mcast_config_data cfg_data = {0};
+ int cnt = *line_idx;
+
+ list_for_each_entry(mlist_pos, &p->mcast_list, link) {
+ cfg_data.mac = mlist_pos->mac;
+ o->set_one_rule(bp, o, cnt, &cfg_data, BNX2X_MCAST_CMD_ADD);
+
+ cnt++;
+
+ DP(BNX2X_MSG_SP, "About to configure %pM mcast MAC\n",
+ mlist_pos->mac);
+ }
+
+ *line_idx = cnt;
+}
+
+static inline void bnx2x_mcast_hdl_del(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o, struct bnx2x_mcast_ramrod_params *p,
+ int *line_idx)
+{
+ int cnt = *line_idx, i;
+
+ for (i = 0; i < p->mcast_list_len; i++) {
+ o->set_one_rule(bp, o, cnt, NULL, BNX2X_MCAST_CMD_DEL);
+
+ cnt++;
+
+ DP(BNX2X_MSG_SP, "Deleting MAC. %d left\n",
+ p->mcast_list_len - i - 1);
+ }
+
+ *line_idx = cnt;
+}
+
+/**
+ * bnx2x_mcast_handle_current_cmd -
+ *
+ * @bp: device handle
+ * @p:
+ * @cmd:
+ * @start_cnt: first line in the ramrod data that may be used
+ *
+ * This function is called iff there is enough place for the current command in
+ * the ramrod data.
+ * Returns number of lines filled in the ramrod data in total.
+ */
+static inline int bnx2x_mcast_handle_current_cmd(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p, int cmd,
+ int start_cnt)
+{
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+ int cnt = start_cnt;
+
+ DP(BNX2X_MSG_SP, "p->mcast_list_len=%d\n", p->mcast_list_len);
+
+ switch (cmd) {
+ case BNX2X_MCAST_CMD_ADD:
+ bnx2x_mcast_hdl_add(bp, o, p, &cnt);
+ break;
+
+ case BNX2X_MCAST_CMD_DEL:
+ bnx2x_mcast_hdl_del(bp, o, p, &cnt);
+ break;
+
+ case BNX2X_MCAST_CMD_RESTORE:
+ o->hdl_restore(bp, o, 0, &cnt);
+ break;
+
+ default:
+ BNX2X_ERR("Unknown command: %d\n", cmd);
+ return -EINVAL;
+ }
+
+ /* The current command has been handled */
+ p->mcast_list_len = 0;
+
+ return cnt;
+}
+
+static int bnx2x_mcast_validate_e2(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int cmd)
+{
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+ int reg_sz = o->get_registry_size(o);
+
+ switch (cmd) {
+ /* DEL command deletes all currently configured MACs */
+ case BNX2X_MCAST_CMD_DEL:
+ o->set_registry_size(o, 0);
+ /* Don't break */
+
+ /* RESTORE command will restore the entire multicast configuration */
+ case BNX2X_MCAST_CMD_RESTORE:
+ /* Here we set the approximate amount of work to do, which in
+ * fact may be only less as some MACs in postponed ADD
+ * command(s) scheduled before this command may fall into
+ * the same bin and the actual number of bins set in the
+ * registry would be less than we estimated here. See
+ * bnx2x_mcast_set_one_rule_e2() for further details.
+ */
+ p->mcast_list_len = reg_sz;
+ break;
+
+ case BNX2X_MCAST_CMD_ADD:
+ case BNX2X_MCAST_CMD_CONT:
+ /* Here we assume that all new MACs will fall into new bins.
+ * However we will correct the real registry size after we
+ * handle all pending commands.
+ */
+ o->set_registry_size(o, reg_sz + p->mcast_list_len);
+ break;
+
+ default:
+ BNX2X_ERR("Unknown command: %d\n", cmd);
+ return -EINVAL;
+
+ }
+
+ /* Increase the total number of MACs pending to be configured */
+ o->total_pending_num += p->mcast_list_len;
+
+ return 0;
+}
+
+static void bnx2x_mcast_revert_e2(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int old_num_bins)
+{
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+
+ o->set_registry_size(o, old_num_bins);
+ o->total_pending_num -= p->mcast_list_len;
+}
+
+/**
+ * bnx2x_mcast_set_rdata_hdr_e2 - sets a header values
+ *
+ * @bp: device handle
+ * @p:
+ * @len: number of rules to handle
+ */
+static inline void bnx2x_mcast_set_rdata_hdr_e2(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ u8 len)
+{
+ struct bnx2x_raw_obj *r = &p->mcast_obj->raw;
+ struct eth_multicast_rules_ramrod_data *data =
+ (struct eth_multicast_rules_ramrod_data *)(r->rdata);
+
+ data->header.echo = ((r->cid & BNX2X_SWCID_MASK) |
+ (BNX2X_FILTER_MCAST_PENDING << BNX2X_SWCID_SHIFT));
+ data->header.rule_cnt = len;
+}
+
+/**
+ * bnx2x_mcast_refresh_registry_e2 - recalculate the actual number of set bins
+ *
+ * @bp: device handle
+ * @o:
+ *
+ * Recalculate the actual number of set bins in the registry using Brian
+ * Kernighan's algorithm: it's execution complexity is as a number of set bins.
+ *
+ * returns 0 for the compliance with bnx2x_mcast_refresh_registry_e1().
+ */
+static inline int bnx2x_mcast_refresh_registry_e2(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o)
+{
+ int i, cnt = 0;
+ u64 elem;
+
+ for (i = 0; i < BNX2X_MCAST_VEC_SZ; i++) {
+ elem = o->registry.aprox_match.vec[i];
+ for (; elem; cnt++)
+ elem &= elem - 1;
+ }
+
+ o->set_registry_size(o, cnt);
+
+ return 0;
+}
+
+static int bnx2x_mcast_setup_e2(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int cmd)
+{
+ struct bnx2x_raw_obj *raw = &p->mcast_obj->raw;
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+ struct eth_multicast_rules_ramrod_data *data =
+ (struct eth_multicast_rules_ramrod_data *)(raw->rdata);
+ int cnt = 0, rc;
+
+ /* Reset the ramrod data buffer */
+ memset(data, 0, sizeof(*data));
+
+ cnt = bnx2x_mcast_handle_pending_cmds_e2(bp, p);
+
+ /* If there are no more pending commands - clear SCHEDULED state */
+ if (list_empty(&o->pending_cmds_head))
+ o->clear_sched(o);
+
+ /* The below may be true iff there was enough room in ramrod
+ * data for all pending commands and for the current
+ * command. Otherwise the current command would have been added
+ * to the pending commands and p->mcast_list_len would have been
+ * zeroed.
+ */
+ if (p->mcast_list_len > 0)
+ cnt = bnx2x_mcast_handle_current_cmd(bp, p, cmd, cnt);
+
+ /* We've pulled out some MACs - update the total number of
+ * outstanding.
+ */
+ o->total_pending_num -= cnt;
+
+ /* send a ramrod */
+ WARN_ON(o->total_pending_num < 0);
+ WARN_ON(cnt > o->max_cmd_len);
+
+ bnx2x_mcast_set_rdata_hdr_e2(bp, p, (u8)cnt);
+
+ /* Update a registry size if there are no more pending operations.
+ *
+ * We don't want to change the value of the registry size if there are
+ * pending operations because we want it to always be equal to the
+ * exact or the approximate number (see bnx2x_mcast_validate_e2()) of
+ * set bins after the last requested operation in order to properly
+ * evaluate the size of the next DEL/RESTORE operation.
+ *
+ * Note that we update the registry itself during command(s) handling
+ * - see bnx2x_mcast_set_one_rule_e2(). That's because for 57712 we
+ * aggregate multiple commands (ADD/DEL/RESTORE) into one ramrod but
+ * with a limited amount of update commands (per MAC/bin) and we don't
+ * know in this scope what the actual state of bins configuration is
+ * going to be after this ramrod.
+ */
+ if (!o->total_pending_num)
+ bnx2x_mcast_refresh_registry_e2(bp, o);
+
+ /*
+ * If CLEAR_ONLY was requested - don't send a ramrod and clear
+ * RAMROD_PENDING status immediately.
+ */
+ if (test_bit(RAMROD_DRV_CLR_ONLY, &p->ramrod_flags)) {
+ raw->clear_pending(raw);
+ return 0;
+ } else {
+ /*
+ * No need for an explicit memory barrier here as long we would
+ * need to ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read and we will have to put a full memory barrier there
+ * (inside bnx2x_sp_post()).
+ */
+
+ /* Send a ramrod */
+ rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_MULTICAST_RULES,
+ raw->cid, U64_HI(raw->rdata_mapping),
+ U64_LO(raw->rdata_mapping),
+ ETH_CONNECTION_TYPE);
+ if (rc)
+ return rc;
+
+ /* Ramrod completion is pending */
+ return 1;
+ }
+}
+
+static int bnx2x_mcast_validate_e1h(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int cmd)
+{
+ /* Mark, that there is a work to do */
+ if ((cmd == BNX2X_MCAST_CMD_DEL) || (cmd == BNX2X_MCAST_CMD_RESTORE))
+ p->mcast_list_len = 1;
+
+ return 0;
+}
+
+static void bnx2x_mcast_revert_e1h(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int old_num_bins)
+{
+ /* Do nothing */
+}
+
+#define BNX2X_57711_SET_MC_FILTER(filter, bit) \
+do { \
+ (filter)[(bit) >> 5] |= (1 << ((bit) & 0x1f)); \
+} while (0)
+
+static inline void bnx2x_mcast_hdl_add_e1h(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o,
+ struct bnx2x_mcast_ramrod_params *p,
+ u32 *mc_filter)
+{
+ struct bnx2x_mcast_list_elem *mlist_pos;
+ int bit;
+
+ list_for_each_entry(mlist_pos, &p->mcast_list, link) {
+ bit = bnx2x_mcast_bin_from_mac(mlist_pos->mac);
+ BNX2X_57711_SET_MC_FILTER(mc_filter, bit);
+
+ DP(BNX2X_MSG_SP, "About to configure %pM mcast MAC, bin %d\n",
+ mlist_pos->mac, bit);
+
+ /* bookkeeping... */
+ BIT_VEC64_SET_BIT(o->registry.aprox_match.vec,
+ bit);
+ }
+}
+
+static inline void bnx2x_mcast_hdl_restore_e1h(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o, struct bnx2x_mcast_ramrod_params *p,
+ u32 *mc_filter)
+{
+ int bit;
+
+ for (bit = bnx2x_mcast_get_next_bin(o, 0);
+ bit >= 0;
+ bit = bnx2x_mcast_get_next_bin(o, bit + 1)) {
+ BNX2X_57711_SET_MC_FILTER(mc_filter, bit);
+ DP(BNX2X_MSG_SP, "About to set bin %d\n", bit);
+ }
+}
+
+/* On 57711 we write the multicast MACs' aproximate match
+ * table by directly into the TSTORM's internal RAM. So we don't
+ * really need to handle any tricks to make it work.
+ */
+static int bnx2x_mcast_setup_e1h(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int cmd)
+{
+ int i;
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+ struct bnx2x_raw_obj *r = &o->raw;
+
+ /* If CLEAR_ONLY has been requested - clear the registry
+ * and clear a pending bit.
+ */
+ if (!test_bit(RAMROD_DRV_CLR_ONLY, &p->ramrod_flags)) {
+ u32 mc_filter[MC_HASH_SIZE] = {0};
+
+ /* Set the multicast filter bits before writing it into
+ * the internal memory.
+ */
+ switch (cmd) {
+ case BNX2X_MCAST_CMD_ADD:
+ bnx2x_mcast_hdl_add_e1h(bp, o, p, mc_filter);
+ break;
+
+ case BNX2X_MCAST_CMD_DEL:
+ DP(BNX2X_MSG_SP,
+ "Invalidating multicast MACs configuration\n");
+
+ /* clear the registry */
+ memset(o->registry.aprox_match.vec, 0,
+ sizeof(o->registry.aprox_match.vec));
+ break;
+
+ case BNX2X_MCAST_CMD_RESTORE:
+ bnx2x_mcast_hdl_restore_e1h(bp, o, p, mc_filter);
+ break;
+
+ default:
+ BNX2X_ERR("Unknown command: %d\n", cmd);
+ return -EINVAL;
+ }
+
+ /* Set the mcast filter in the internal memory */
+ for (i = 0; i < MC_HASH_SIZE; i++)
+ REG_WR(bp, MC_HASH_OFFSET(bp, i), mc_filter[i]);
+ } else
+ /* clear the registry */
+ memset(o->registry.aprox_match.vec, 0,
+ sizeof(o->registry.aprox_match.vec));
+
+ /* We are done */
+ r->clear_pending(r);
+
+ return 0;
+}
+
+static int bnx2x_mcast_validate_e1(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int cmd)
+{
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+ int reg_sz = o->get_registry_size(o);
+
+ switch (cmd) {
+ /* DEL command deletes all currently configured MACs */
+ case BNX2X_MCAST_CMD_DEL:
+ o->set_registry_size(o, 0);
+ /* Don't break */
+
+ /* RESTORE command will restore the entire multicast configuration */
+ case BNX2X_MCAST_CMD_RESTORE:
+ p->mcast_list_len = reg_sz;
+ DP(BNX2X_MSG_SP, "Command %d, p->mcast_list_len=%d\n",
+ cmd, p->mcast_list_len);
+ break;
+
+ case BNX2X_MCAST_CMD_ADD:
+ case BNX2X_MCAST_CMD_CONT:
+ /* Multicast MACs on 57710 are configured as unicast MACs and
+ * there is only a limited number of CAM entries for that
+ * matter.
+ */
+ if (p->mcast_list_len > o->max_cmd_len) {
+ BNX2X_ERR("Can't configure more than %d multicast MACs"
+ "on 57710\n", o->max_cmd_len);
+ return -EINVAL;
+ }
+ /* Every configured MAC should be cleared if DEL command is
+ * called. Only the last ADD command is relevant as long as
+ * every ADD commands overrides the previous configuration.
+ */
+ DP(BNX2X_MSG_SP, "p->mcast_list_len=%d\n", p->mcast_list_len);
+ if (p->mcast_list_len > 0)
+ o->set_registry_size(o, p->mcast_list_len);
+
+ break;
+
+ default:
+ BNX2X_ERR("Unknown command: %d\n", cmd);
+ return -EINVAL;
+
+ }
+
+ /* We want to ensure that commands are executed one by one for 57710.
+ * Therefore each none-empty command will consume o->max_cmd_len.
+ */
+ if (p->mcast_list_len)
+ o->total_pending_num += o->max_cmd_len;
+
+ return 0;
+}
+
+static void bnx2x_mcast_revert_e1(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int old_num_macs)
+{
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+
+ o->set_registry_size(o, old_num_macs);
+
+ /* If current command hasn't been handled yet and we are
+ * here means that it's meant to be dropped and we have to
+ * update the number of outstandling MACs accordingly.
+ */
+ if (p->mcast_list_len)
+ o->total_pending_num -= o->max_cmd_len;
+}
+
+static void bnx2x_mcast_set_one_rule_e1(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o, int idx,
+ union bnx2x_mcast_config_data *cfg_data,
+ int cmd)
+{
+ struct bnx2x_raw_obj *r = &o->raw;
+ struct mac_configuration_cmd *data =
+ (struct mac_configuration_cmd *)(r->rdata);
+
+ /* copy mac */
+ if ((cmd == BNX2X_MCAST_CMD_ADD) || (cmd == BNX2X_MCAST_CMD_RESTORE)) {
+ bnx2x_set_fw_mac_addr(&data->config_table[idx].msb_mac_addr,
+ &data->config_table[idx].middle_mac_addr,
+ &data->config_table[idx].lsb_mac_addr,
+ cfg_data->mac);
+
+ data->config_table[idx].vlan_id = 0;
+ data->config_table[idx].pf_id = r->func_id;
+ data->config_table[idx].clients_bit_vector =
+ cpu_to_le32(1 << r->cl_id);
+
+ SET_FLAG(data->config_table[idx].flags,
+ MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
+ T_ETH_MAC_COMMAND_SET);
+ }
+}
+
+/**
+ * bnx2x_mcast_set_rdata_hdr_e1 - set header values in mac_configuration_cmd
+ *
+ * @bp: device handle
+ * @p:
+ * @len: number of rules to handle
+ */
+static inline void bnx2x_mcast_set_rdata_hdr_e1(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ u8 len)
+{
+ struct bnx2x_raw_obj *r = &p->mcast_obj->raw;
+ struct mac_configuration_cmd *data =
+ (struct mac_configuration_cmd *)(r->rdata);
+
+ u8 offset = (CHIP_REV_IS_SLOW(bp) ?
+ BNX2X_MAX_EMUL_MULTI*(1 + r->func_id) :
+ BNX2X_MAX_MULTICAST*(1 + r->func_id));
+
+ data->hdr.offset = offset;
+ data->hdr.client_id = 0xff;
+ data->hdr.echo = ((r->cid & BNX2X_SWCID_MASK) |
+ (BNX2X_FILTER_MCAST_PENDING << BNX2X_SWCID_SHIFT));
+ data->hdr.length = len;
+}
+
+/**
+ * bnx2x_mcast_handle_restore_cmd_e1 - restore command for 57710
+ *
+ * @bp: device handle
+ * @o:
+ * @start_idx: index in the registry to start from
+ * @rdata_idx: index in the ramrod data to start from
+ *
+ * restore command for 57710 is like all other commands - always a stand alone
+ * command - start_idx and rdata_idx will always be 0. This function will always
+ * succeed.
+ * returns -1 to comply with 57712 variant.
+ */
+static inline int bnx2x_mcast_handle_restore_cmd_e1(
+ struct bnx2x *bp, struct bnx2x_mcast_obj *o , int start_idx,
+ int *rdata_idx)
+{
+ struct bnx2x_mcast_mac_elem *elem;
+ int i = 0;
+ union bnx2x_mcast_config_data cfg_data = {0};
+
+ /* go through the registry and configure the MACs from it. */
+ list_for_each_entry(elem, &o->registry.exact_match.macs, link) {
+ cfg_data.mac = &elem->mac[0];
+ o->set_one_rule(bp, o, i, &cfg_data, BNX2X_MCAST_CMD_RESTORE);
+
+ i++;
+
+ DP(BNX2X_MSG_SP, "About to configure %pM mcast MAC\n",
+ cfg_data.mac);
+ }
+
+ *rdata_idx = i;
+
+ return -1;
+}
+
+
+static inline int bnx2x_mcast_handle_pending_cmds_e1(
+ struct bnx2x *bp, struct bnx2x_mcast_ramrod_params *p)
+{
+ struct bnx2x_pending_mcast_cmd *cmd_pos;
+ struct bnx2x_mcast_mac_elem *pmac_pos;
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+ union bnx2x_mcast_config_data cfg_data = {0};
+ int cnt = 0;
+
+
+ /* If nothing to be done - return */
+ if (list_empty(&o->pending_cmds_head))
+ return 0;
+
+ /* Handle the first command */
+ cmd_pos = list_first_entry(&o->pending_cmds_head,
+ struct bnx2x_pending_mcast_cmd, link);
+
+ switch (cmd_pos->type) {
+ case BNX2X_MCAST_CMD_ADD:
+ list_for_each_entry(pmac_pos, &cmd_pos->data.macs_head, link) {
+ cfg_data.mac = &pmac_pos->mac[0];
+ o->set_one_rule(bp, o, cnt, &cfg_data, cmd_pos->type);
+
+ cnt++;
+
+ DP(BNX2X_MSG_SP, "About to configure %pM mcast MAC\n",
+ pmac_pos->mac);
+ }
+ break;
+
+ case BNX2X_MCAST_CMD_DEL:
+ cnt = cmd_pos->data.macs_num;
+ DP(BNX2X_MSG_SP, "About to delete %d multicast MACs\n", cnt);
+ break;
+
+ case BNX2X_MCAST_CMD_RESTORE:
+ o->hdl_restore(bp, o, 0, &cnt);
+ break;
+
+ default:
+ BNX2X_ERR("Unknown command: %d\n", cmd_pos->type);
+ return -EINVAL;
+ }
+
+ list_del(&cmd_pos->link);
+ kfree(cmd_pos);
+
+ return cnt;
+}
+
+/**
+ * bnx2x_get_fw_mac_addr - revert the bnx2x_set_fw_mac_addr().
+ *
+ * @fw_hi:
+ * @fw_mid:
+ * @fw_lo:
+ * @mac:
+ */
+static inline void bnx2x_get_fw_mac_addr(__le16 *fw_hi, __le16 *fw_mid,
+ __le16 *fw_lo, u8 *mac)
+{
+ mac[1] = ((u8 *)fw_hi)[0];
+ mac[0] = ((u8 *)fw_hi)[1];
+ mac[3] = ((u8 *)fw_mid)[0];
+ mac[2] = ((u8 *)fw_mid)[1];
+ mac[5] = ((u8 *)fw_lo)[0];
+ mac[4] = ((u8 *)fw_lo)[1];
+}
+
+/**
+ * bnx2x_mcast_refresh_registry_e1 -
+ *
+ * @bp: device handle
+ * @cnt:
+ *
+ * Check the ramrod data first entry flag to see if it's a DELETE or ADD command
+ * and update the registry correspondingly: if ADD - allocate a memory and add
+ * the entries to the registry (list), if DELETE - clear the registry and free
+ * the memory.
+ */
+static inline int bnx2x_mcast_refresh_registry_e1(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o)
+{
+ struct bnx2x_raw_obj *raw = &o->raw;
+ struct bnx2x_mcast_mac_elem *elem;
+ struct mac_configuration_cmd *data =
+ (struct mac_configuration_cmd *)(raw->rdata);
+
+ /* If first entry contains a SET bit - the command was ADD,
+ * otherwise - DEL_ALL
+ */
+ if (GET_FLAG(data->config_table[0].flags,
+ MAC_CONFIGURATION_ENTRY_ACTION_TYPE)) {
+ int i, len = data->hdr.length;
+
+ /* Break if it was a RESTORE command */
+ if (!list_empty(&o->registry.exact_match.macs))
+ return 0;
+
+ elem = kzalloc(sizeof(*elem)*len, GFP_ATOMIC);
+ if (!elem) {
+ BNX2X_ERR("Failed to allocate registry memory\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < len; i++, elem++) {
+ bnx2x_get_fw_mac_addr(
+ &data->config_table[i].msb_mac_addr,
+ &data->config_table[i].middle_mac_addr,
+ &data->config_table[i].lsb_mac_addr,
+ elem->mac);
+ DP(BNX2X_MSG_SP, "Adding registry entry for [%pM]\n",
+ elem->mac);
+ list_add_tail(&elem->link,
+ &o->registry.exact_match.macs);
+ }
+ } else {
+ elem = list_first_entry(&o->registry.exact_match.macs,
+ struct bnx2x_mcast_mac_elem, link);
+ DP(BNX2X_MSG_SP, "Deleting a registry\n");
+ kfree(elem);
+ INIT_LIST_HEAD(&o->registry.exact_match.macs);
+ }
+
+ return 0;
+}
+
+static int bnx2x_mcast_setup_e1(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int cmd)
+{
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+ struct bnx2x_raw_obj *raw = &o->raw;
+ struct mac_configuration_cmd *data =
+ (struct mac_configuration_cmd *)(raw->rdata);
+ int cnt = 0, i, rc;
+
+ /* Reset the ramrod data buffer */
+ memset(data, 0, sizeof(*data));
+
+ /* First set all entries as invalid */
+ for (i = 0; i < o->max_cmd_len ; i++)
+ SET_FLAG(data->config_table[i].flags,
+ MAC_CONFIGURATION_ENTRY_ACTION_TYPE,
+ T_ETH_MAC_COMMAND_INVALIDATE);
+
+ /* Handle pending commands first */
+ cnt = bnx2x_mcast_handle_pending_cmds_e1(bp, p);
+
+ /* If there are no more pending commands - clear SCHEDULED state */
+ if (list_empty(&o->pending_cmds_head))
+ o->clear_sched(o);
+
+ /* The below may be true iff there were no pending commands */
+ if (!cnt)
+ cnt = bnx2x_mcast_handle_current_cmd(bp, p, cmd, 0);
+
+ /* For 57710 every command has o->max_cmd_len length to ensure that
+ * commands are done one at a time.
+ */
+ o->total_pending_num -= o->max_cmd_len;
+
+ /* send a ramrod */
+
+ WARN_ON(cnt > o->max_cmd_len);
+
+ /* Set ramrod header (in particular, a number of entries to update) */
+ bnx2x_mcast_set_rdata_hdr_e1(bp, p, (u8)cnt);
+
+ /* update a registry: we need the registry contents to be always up
+ * to date in order to be able to execute a RESTORE opcode. Here
+ * we use the fact that for 57710 we sent one command at a time
+ * hence we may take the registry update out of the command handling
+ * and do it in a simpler way here.
+ */
+ rc = bnx2x_mcast_refresh_registry_e1(bp, o);
+ if (rc)
+ return rc;
+
+ /*
+ * If CLEAR_ONLY was requested - don't send a ramrod and clear
+ * RAMROD_PENDING status immediately.
+ */
+ if (test_bit(RAMROD_DRV_CLR_ONLY, &p->ramrod_flags)) {
+ raw->clear_pending(raw);
+ return 0;
+ } else {
+ /*
+ * No need for an explicit memory barrier here as long we would
+ * need to ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read and we will have to put a full memory barrier there
+ * (inside bnx2x_sp_post()).
+ */
+
+ /* Send a ramrod */
+ rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, raw->cid,
+ U64_HI(raw->rdata_mapping),
+ U64_LO(raw->rdata_mapping),
+ ETH_CONNECTION_TYPE);
+ if (rc)
+ return rc;
+
+ /* Ramrod completion is pending */
+ return 1;
+ }
+
+}
+
+static int bnx2x_mcast_get_registry_size_exact(struct bnx2x_mcast_obj *o)
+{
+ return o->registry.exact_match.num_macs_set;
+}
+
+static int bnx2x_mcast_get_registry_size_aprox(struct bnx2x_mcast_obj *o)
+{
+ return o->registry.aprox_match.num_bins_set;
+}
+
+static void bnx2x_mcast_set_registry_size_exact(struct bnx2x_mcast_obj *o,
+ int n)
+{
+ o->registry.exact_match.num_macs_set = n;
+}
+
+static void bnx2x_mcast_set_registry_size_aprox(struct bnx2x_mcast_obj *o,
+ int n)
+{
+ o->registry.aprox_match.num_bins_set = n;
+}
+
+int bnx2x_config_mcast(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int cmd)
+{
+ struct bnx2x_mcast_obj *o = p->mcast_obj;
+ struct bnx2x_raw_obj *r = &o->raw;
+ int rc = 0, old_reg_size;
+
+ /* This is needed to recover number of currently configured mcast macs
+ * in case of failure.
+ */
+ old_reg_size = o->get_registry_size(o);
+
+ /* Do some calculations and checks */
+ rc = o->validate(bp, p, cmd);
+ if (rc)
+ return rc;
+
+ /* Return if there is no work to do */
+ if ((!p->mcast_list_len) && (!o->check_sched(o)))
+ return 0;
+
+ DP(BNX2X_MSG_SP, "o->total_pending_num=%d p->mcast_list_len=%d "
+ "o->max_cmd_len=%d\n", o->total_pending_num,
+ p->mcast_list_len, o->max_cmd_len);
+
+ /* Enqueue the current command to the pending list if we can't complete
+ * it in the current iteration
+ */
+ if (r->check_pending(r) ||
+ ((o->max_cmd_len > 0) && (o->total_pending_num > o->max_cmd_len))) {
+ rc = o->enqueue_cmd(bp, p->mcast_obj, p, cmd);
+ if (rc < 0)
+ goto error_exit1;
+
+ /* As long as the current command is in a command list we
+ * don't need to handle it separately.
+ */
+ p->mcast_list_len = 0;
+ }
+
+ if (!r->check_pending(r)) {
+
+ /* Set 'pending' state */
+ r->set_pending(r);
+
+ /* Configure the new classification in the chip */
+ rc = o->config_mcast(bp, p, cmd);
+ if (rc < 0)
+ goto error_exit2;
+
+ /* Wait for a ramrod completion if was requested */
+ if (test_bit(RAMROD_COMP_WAIT, &p->ramrod_flags))
+ rc = o->wait_comp(bp, o);
+ }
+
+ return rc;
+
+error_exit2:
+ r->clear_pending(r);
+
+error_exit1:
+ o->revert(bp, p, old_reg_size);
+
+ return rc;
+}
+
+static void bnx2x_mcast_clear_sched(struct bnx2x_mcast_obj *o)
+{
+ smp_mb__before_clear_bit();
+ clear_bit(o->sched_state, o->raw.pstate);
+ smp_mb__after_clear_bit();
+}
+
+static void bnx2x_mcast_set_sched(struct bnx2x_mcast_obj *o)
+{
+ smp_mb__before_clear_bit();
+ set_bit(o->sched_state, o->raw.pstate);
+ smp_mb__after_clear_bit();
+}
+
+static bool bnx2x_mcast_check_sched(struct bnx2x_mcast_obj *o)
+{
+ return !!test_bit(o->sched_state, o->raw.pstate);
+}
+
+static bool bnx2x_mcast_check_pending(struct bnx2x_mcast_obj *o)
+{
+ return o->raw.check_pending(&o->raw) || o->check_sched(o);
+}
+
+void bnx2x_init_mcast_obj(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *mcast_obj,
+ u8 mcast_cl_id, u32 mcast_cid, u8 func_id,
+ u8 engine_id, void *rdata, dma_addr_t rdata_mapping,
+ int state, unsigned long *pstate, bnx2x_obj_type type)
+{
+ memset(mcast_obj, 0, sizeof(*mcast_obj));
+
+ bnx2x_init_raw_obj(&mcast_obj->raw, mcast_cl_id, mcast_cid, func_id,
+ rdata, rdata_mapping, state, pstate, type);
+
+ mcast_obj->engine_id = engine_id;
+
+ INIT_LIST_HEAD(&mcast_obj->pending_cmds_head);
+
+ mcast_obj->sched_state = BNX2X_FILTER_MCAST_SCHED;
+ mcast_obj->check_sched = bnx2x_mcast_check_sched;
+ mcast_obj->set_sched = bnx2x_mcast_set_sched;
+ mcast_obj->clear_sched = bnx2x_mcast_clear_sched;
+
+ if (CHIP_IS_E1(bp)) {
+ mcast_obj->config_mcast = bnx2x_mcast_setup_e1;
+ mcast_obj->enqueue_cmd = bnx2x_mcast_enqueue_cmd;
+ mcast_obj->hdl_restore =
+ bnx2x_mcast_handle_restore_cmd_e1;
+ mcast_obj->check_pending = bnx2x_mcast_check_pending;
+
+ if (CHIP_REV_IS_SLOW(bp))
+ mcast_obj->max_cmd_len = BNX2X_MAX_EMUL_MULTI;
+ else
+ mcast_obj->max_cmd_len = BNX2X_MAX_MULTICAST;
+
+ mcast_obj->wait_comp = bnx2x_mcast_wait;
+ mcast_obj->set_one_rule = bnx2x_mcast_set_one_rule_e1;
+ mcast_obj->validate = bnx2x_mcast_validate_e1;
+ mcast_obj->revert = bnx2x_mcast_revert_e1;
+ mcast_obj->get_registry_size =
+ bnx2x_mcast_get_registry_size_exact;
+ mcast_obj->set_registry_size =
+ bnx2x_mcast_set_registry_size_exact;
+
+ /* 57710 is the only chip that uses the exact match for mcast
+ * at the moment.
+ */
+ INIT_LIST_HEAD(&mcast_obj->registry.exact_match.macs);
+
+ } else if (CHIP_IS_E1H(bp)) {
+ mcast_obj->config_mcast = bnx2x_mcast_setup_e1h;
+ mcast_obj->enqueue_cmd = NULL;
+ mcast_obj->hdl_restore = NULL;
+ mcast_obj->check_pending = bnx2x_mcast_check_pending;
+
+ /* 57711 doesn't send a ramrod, so it has unlimited credit
+ * for one command.
+ */
+ mcast_obj->max_cmd_len = -1;
+ mcast_obj->wait_comp = bnx2x_mcast_wait;
+ mcast_obj->set_one_rule = NULL;
+ mcast_obj->validate = bnx2x_mcast_validate_e1h;
+ mcast_obj->revert = bnx2x_mcast_revert_e1h;
+ mcast_obj->get_registry_size =
+ bnx2x_mcast_get_registry_size_aprox;
+ mcast_obj->set_registry_size =
+ bnx2x_mcast_set_registry_size_aprox;
+ } else {
+ mcast_obj->config_mcast = bnx2x_mcast_setup_e2;
+ mcast_obj->enqueue_cmd = bnx2x_mcast_enqueue_cmd;
+ mcast_obj->hdl_restore =
+ bnx2x_mcast_handle_restore_cmd_e2;
+ mcast_obj->check_pending = bnx2x_mcast_check_pending;
+ /* TODO: There should be a proper HSI define for this number!!!
+ */
+ mcast_obj->max_cmd_len = 16;
+ mcast_obj->wait_comp = bnx2x_mcast_wait;
+ mcast_obj->set_one_rule = bnx2x_mcast_set_one_rule_e2;
+ mcast_obj->validate = bnx2x_mcast_validate_e2;
+ mcast_obj->revert = bnx2x_mcast_revert_e2;
+ mcast_obj->get_registry_size =
+ bnx2x_mcast_get_registry_size_aprox;
+ mcast_obj->set_registry_size =
+ bnx2x_mcast_set_registry_size_aprox;
+ }
+}
+
+/*************************** Credit handling **********************************/
+
+/**
+ * atomic_add_ifless - add if the result is less than a given value.
+ *
+ * @v: pointer of type atomic_t
+ * @a: the amount to add to v...
+ * @u: ...if (v + a) is less than u.
+ *
+ * returns true if (v + a) was less than u, and false otherwise.
+ *
+ */
+static inline bool __atomic_add_ifless(atomic_t *v, int a, int u)
+{
+ int c, old;
+
+ c = atomic_read(v);
+ for (;;) {
+ if (unlikely(c + a >= u))
+ return false;
+
+ old = atomic_cmpxchg((v), c, c + a);
+ if (likely(old == c))
+ break;
+ c = old;
+ }
+
+ return true;
+}
+
+/**
+ * atomic_dec_ifmoe - dec if the result is more or equal than a given value.
+ *
+ * @v: pointer of type atomic_t
+ * @a: the amount to dec from v...
+ * @u: ...if (v - a) is more or equal than u.
+ *
+ * returns true if (v - a) was more or equal than u, and false
+ * otherwise.
+ */
+static inline bool __atomic_dec_ifmoe(atomic_t *v, int a, int u)
+{
+ int c, old;
+
+ c = atomic_read(v);
+ for (;;) {
+ if (unlikely(c - a < u))
+ return false;
+
+ old = atomic_cmpxchg((v), c, c - a);
+ if (likely(old == c))
+ break;
+ c = old;
+ }
+
+ return true;
+}
+
+static bool bnx2x_credit_pool_get(struct bnx2x_credit_pool_obj *o, int cnt)
+{
+ bool rc;
+
+ smp_mb();
+ rc = __atomic_dec_ifmoe(&o->credit, cnt, 0);
+ smp_mb();
+
+ return rc;
+}
+
+static bool bnx2x_credit_pool_put(struct bnx2x_credit_pool_obj *o, int cnt)
+{
+ bool rc;
+
+ smp_mb();
+
+ /* Don't let to refill if credit + cnt > pool_sz */
+ rc = __atomic_add_ifless(&o->credit, cnt, o->pool_sz + 1);
+
+ smp_mb();
+
+ return rc;
+}
+
+static int bnx2x_credit_pool_check(struct bnx2x_credit_pool_obj *o)
+{
+ int cur_credit;
+
+ smp_mb();
+ cur_credit = atomic_read(&o->credit);
+
+ return cur_credit;
+}
+
+static bool bnx2x_credit_pool_always_true(struct bnx2x_credit_pool_obj *o,
+ int cnt)
+{
+ return true;
+}
+
+
+static bool bnx2x_credit_pool_get_entry(
+ struct bnx2x_credit_pool_obj *o,
+ int *offset)
+{
+ int idx, vec, i;
+
+ *offset = -1;
+
+ /* Find "internal cam-offset" then add to base for this object... */
+ for (vec = 0; vec < BNX2X_POOL_VEC_SIZE; vec++) {
+
+ /* Skip the current vector if there are no free entries in it */
+ if (!o->pool_mirror[vec])
+ continue;
+
+ /* If we've got here we are going to find a free entry */
+ for (idx = vec * BNX2X_POOL_VEC_SIZE, i = 0;
+ i < BIT_VEC64_ELEM_SZ; idx++, i++)
+
+ if (BIT_VEC64_TEST_BIT(o->pool_mirror, idx)) {
+ /* Got one!! */
+ BIT_VEC64_CLEAR_BIT(o->pool_mirror, idx);
+ *offset = o->base_pool_offset + idx;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool bnx2x_credit_pool_put_entry(
+ struct bnx2x_credit_pool_obj *o,
+ int offset)
+{
+ if (offset < o->base_pool_offset)
+ return false;
+
+ offset -= o->base_pool_offset;
+
+ if (offset >= o->pool_sz)
+ return false;
+
+ /* Return the entry to the pool */
+ BIT_VEC64_SET_BIT(o->pool_mirror, offset);
+
+ return true;
+}
+
+static bool bnx2x_credit_pool_put_entry_always_true(
+ struct bnx2x_credit_pool_obj *o,
+ int offset)
+{
+ return true;
+}
+
+static bool bnx2x_credit_pool_get_entry_always_true(
+ struct bnx2x_credit_pool_obj *o,
+ int *offset)
+{
+ *offset = -1;
+ return true;
+}
+/**
+ * bnx2x_init_credit_pool - initialize credit pool internals.
+ *
+ * @p:
+ * @base: Base entry in the CAM to use.
+ * @credit: pool size.
+ *
+ * If base is negative no CAM entries handling will be performed.
+ * If credit is negative pool operations will always succeed (unlimited pool).
+ *
+ */
+static inline void bnx2x_init_credit_pool(struct bnx2x_credit_pool_obj *p,
+ int base, int credit)
+{
+ /* Zero the object first */
+ memset(p, 0, sizeof(*p));
+
+ /* Set the table to all 1s */
+ memset(&p->pool_mirror, 0xff, sizeof(p->pool_mirror));
+
+ /* Init a pool as full */
+ atomic_set(&p->credit, credit);
+
+ /* The total poll size */
+ p->pool_sz = credit;
+
+ p->base_pool_offset = base;
+
+ /* Commit the change */
+ smp_mb();
+
+ p->check = bnx2x_credit_pool_check;
+
+ /* if pool credit is negative - disable the checks */
+ if (credit >= 0) {
+ p->put = bnx2x_credit_pool_put;
+ p->get = bnx2x_credit_pool_get;
+ p->put_entry = bnx2x_credit_pool_put_entry;
+ p->get_entry = bnx2x_credit_pool_get_entry;
+ } else {
+ p->put = bnx2x_credit_pool_always_true;
+ p->get = bnx2x_credit_pool_always_true;
+ p->put_entry = bnx2x_credit_pool_put_entry_always_true;
+ p->get_entry = bnx2x_credit_pool_get_entry_always_true;
+ }
+
+ /* If base is negative - disable entries handling */
+ if (base < 0) {
+ p->put_entry = bnx2x_credit_pool_put_entry_always_true;
+ p->get_entry = bnx2x_credit_pool_get_entry_always_true;
+ }
+}
+
+void bnx2x_init_mac_credit_pool(struct bnx2x *bp,
+ struct bnx2x_credit_pool_obj *p, u8 func_id,
+ u8 func_num)
+{
+/* TODO: this will be defined in consts as well... */
+#define BNX2X_CAM_SIZE_EMUL 5
+
+ int cam_sz;
+
+ if (CHIP_IS_E1(bp)) {
+ /* In E1, Multicast is saved in cam... */
+ if (!CHIP_REV_IS_SLOW(bp))
+ cam_sz = (MAX_MAC_CREDIT_E1 / 2) - BNX2X_MAX_MULTICAST;
+ else
+ cam_sz = BNX2X_CAM_SIZE_EMUL - BNX2X_MAX_EMUL_MULTI;
+
+ bnx2x_init_credit_pool(p, func_id * cam_sz, cam_sz);
+
+ } else if (CHIP_IS_E1H(bp)) {
+ /* CAM credit is equaly divided between all active functions
+ * on the PORT!.
+ */
+ if ((func_num > 0)) {
+ if (!CHIP_REV_IS_SLOW(bp))
+ cam_sz = (MAX_MAC_CREDIT_E1H / (2*func_num));
+ else
+ cam_sz = BNX2X_CAM_SIZE_EMUL;
+ bnx2x_init_credit_pool(p, func_id * cam_sz, cam_sz);
+ } else {
+ /* this should never happen! Block MAC operations. */
+ bnx2x_init_credit_pool(p, 0, 0);
+ }
+
+ } else {
+
+ /*
+ * CAM credit is equaly divided between all active functions
+ * on the PATH.
+ */
+ if ((func_num > 0)) {
+ if (!CHIP_REV_IS_SLOW(bp))
+ cam_sz = (MAX_MAC_CREDIT_E2 / func_num);
+ else
+ cam_sz = BNX2X_CAM_SIZE_EMUL;
+
+ /*
+ * No need for CAM entries handling for 57712 and
+ * newer.
+ */
+ bnx2x_init_credit_pool(p, -1, cam_sz);
+ } else {
+ /* this should never happen! Block MAC operations. */
+ bnx2x_init_credit_pool(p, 0, 0);
+ }
+
+ }
+}
+
+void bnx2x_init_vlan_credit_pool(struct bnx2x *bp,
+ struct bnx2x_credit_pool_obj *p,
+ u8 func_id,
+ u8 func_num)
+{
+ if (CHIP_IS_E1x(bp)) {
+ /*
+ * There is no VLAN credit in HW on 57710 and 57711 only
+ * MAC / MAC-VLAN can be set
+ */
+ bnx2x_init_credit_pool(p, 0, -1);
+ } else {
+ /*
+ * CAM credit is equaly divided between all active functions
+ * on the PATH.
+ */
+ if (func_num > 0) {
+ int credit = MAX_VLAN_CREDIT_E2 / func_num;
+ bnx2x_init_credit_pool(p, func_id * credit, credit);
+ } else
+ /* this should never happen! Block VLAN operations. */
+ bnx2x_init_credit_pool(p, 0, 0);
+ }
+}
+
+/****************** RSS Configuration ******************/
+/**
+ * bnx2x_debug_print_ind_table - prints the indirection table configuration.
+ *
+ * @bp: driver hanlde
+ * @p: pointer to rss configuration
+ *
+ * Prints it when NETIF_MSG_IFUP debug level is configured.
+ */
+static inline void bnx2x_debug_print_ind_table(struct bnx2x *bp,
+ struct bnx2x_config_rss_params *p)
+{
+ int i;
+
+ DP(BNX2X_MSG_SP, "Setting indirection table to:\n");
+ DP(BNX2X_MSG_SP, "0x0000: ");
+ for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) {
+ DP_CONT(BNX2X_MSG_SP, "0x%02x ", p->ind_table[i]);
+
+ /* Print 4 bytes in a line */
+ if ((i + 1 < T_ETH_INDIRECTION_TABLE_SIZE) &&
+ (((i + 1) & 0x3) == 0)) {
+ DP_CONT(BNX2X_MSG_SP, "\n");
+ DP(BNX2X_MSG_SP, "0x%04x: ", i + 1);
+ }
+ }
+
+ DP_CONT(BNX2X_MSG_SP, "\n");
+}
+
+/**
+ * bnx2x_setup_rss - configure RSS
+ *
+ * @bp: device handle
+ * @p: rss configuration
+ *
+ * sends on UPDATE ramrod for that matter.
+ */
+static int bnx2x_setup_rss(struct bnx2x *bp,
+ struct bnx2x_config_rss_params *p)
+{
+ struct bnx2x_rss_config_obj *o = p->rss_obj;
+ struct bnx2x_raw_obj *r = &o->raw;
+ struct eth_rss_update_ramrod_data *data =
+ (struct eth_rss_update_ramrod_data *)(r->rdata);
+ u8 rss_mode = 0;
+ int rc;
+
+ memset(data, 0, sizeof(*data));
+
+ DP(BNX2X_MSG_SP, "Configuring RSS\n");
+
+ /* Set an echo field */
+ data->echo = (r->cid & BNX2X_SWCID_MASK) |
+ (r->state << BNX2X_SWCID_SHIFT);
+
+ /* RSS mode */
+ if (test_bit(BNX2X_RSS_MODE_DISABLED, &p->rss_flags))
+ rss_mode = ETH_RSS_MODE_DISABLED;
+ else if (test_bit(BNX2X_RSS_MODE_REGULAR, &p->rss_flags))
+ rss_mode = ETH_RSS_MODE_REGULAR;
+ else if (test_bit(BNX2X_RSS_MODE_VLAN_PRI, &p->rss_flags))
+ rss_mode = ETH_RSS_MODE_VLAN_PRI;
+ else if (test_bit(BNX2X_RSS_MODE_E1HOV_PRI, &p->rss_flags))
+ rss_mode = ETH_RSS_MODE_E1HOV_PRI;
+ else if (test_bit(BNX2X_RSS_MODE_IP_DSCP, &p->rss_flags))
+ rss_mode = ETH_RSS_MODE_IP_DSCP;
+
+ data->rss_mode = rss_mode;
+
+ DP(BNX2X_MSG_SP, "rss_mode=%d\n", rss_mode);
+
+ /* RSS capabilities */
+ if (test_bit(BNX2X_RSS_IPV4, &p->rss_flags))
+ data->capabilities |=
+ ETH_RSS_UPDATE_RAMROD_DATA_IPV4_CAPABILITY;
+
+ if (test_bit(BNX2X_RSS_IPV4_TCP, &p->rss_flags))
+ data->capabilities |=
+ ETH_RSS_UPDATE_RAMROD_DATA_IPV4_TCP_CAPABILITY;
+
+ if (test_bit(BNX2X_RSS_IPV6, &p->rss_flags))
+ data->capabilities |=
+ ETH_RSS_UPDATE_RAMROD_DATA_IPV6_CAPABILITY;
+
+ if (test_bit(BNX2X_RSS_IPV6_TCP, &p->rss_flags))
+ data->capabilities |=
+ ETH_RSS_UPDATE_RAMROD_DATA_IPV6_TCP_CAPABILITY;
+
+ /* Hashing mask */
+ data->rss_result_mask = p->rss_result_mask;
+
+ /* RSS engine ID */
+ data->rss_engine_id = o->engine_id;
+
+ DP(BNX2X_MSG_SP, "rss_engine_id=%d\n", data->rss_engine_id);
+
+ /* Indirection table */
+ memcpy(data->indirection_table, p->ind_table,
+ T_ETH_INDIRECTION_TABLE_SIZE);
+
+ /* Remember the last configuration */
+ memcpy(o->ind_table, p->ind_table, T_ETH_INDIRECTION_TABLE_SIZE);
+
+ /* Print the indirection table */
+ if (netif_msg_ifup(bp))
+ bnx2x_debug_print_ind_table(bp, p);
+
+ /* RSS keys */
+ if (test_bit(BNX2X_RSS_SET_SRCH, &p->rss_flags)) {
+ memcpy(&data->rss_key[0], &p->rss_key[0],
+ sizeof(data->rss_key));
+ data->capabilities |= ETH_RSS_UPDATE_RAMROD_DATA_UPDATE_RSS_KEY;
+ }
+
+ /*
+ * No need for an explicit memory barrier here as long we would
+ * need to ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read and we will have to put a full memory barrier there
+ * (inside bnx2x_sp_post()).
+ */
+
+ /* Send a ramrod */
+ rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_RSS_UPDATE, r->cid,
+ U64_HI(r->rdata_mapping),
+ U64_LO(r->rdata_mapping),
+ ETH_CONNECTION_TYPE);
+
+ if (rc < 0)
+ return rc;
+
+ return 1;
+}
+
+void bnx2x_get_rss_ind_table(struct bnx2x_rss_config_obj *rss_obj,
+ u8 *ind_table)
+{
+ memcpy(ind_table, rss_obj->ind_table, sizeof(rss_obj->ind_table));
+}
+
+int bnx2x_config_rss(struct bnx2x *bp,
+ struct bnx2x_config_rss_params *p)
+{
+ int rc;
+ struct bnx2x_rss_config_obj *o = p->rss_obj;
+ struct bnx2x_raw_obj *r = &o->raw;
+
+ /* Do nothing if only driver cleanup was requested */
+ if (test_bit(RAMROD_DRV_CLR_ONLY, &p->ramrod_flags))
+ return 0;
+
+ r->set_pending(r);
+
+ rc = o->config_rss(bp, p);
+ if (rc < 0) {
+ r->clear_pending(r);
+ return rc;
+ }
+
+ if (test_bit(RAMROD_COMP_WAIT, &p->ramrod_flags))
+ rc = r->wait_comp(bp, r);
+
+ return rc;
+}
+
+
+void bnx2x_init_rss_config_obj(struct bnx2x *bp,
+ struct bnx2x_rss_config_obj *rss_obj,
+ u8 cl_id, u32 cid, u8 func_id, u8 engine_id,
+ void *rdata, dma_addr_t rdata_mapping,
+ int state, unsigned long *pstate,
+ bnx2x_obj_type type)
+{
+ bnx2x_init_raw_obj(&rss_obj->raw, cl_id, cid, func_id, rdata,
+ rdata_mapping, state, pstate, type);
+
+ rss_obj->engine_id = engine_id;
+ rss_obj->config_rss = bnx2x_setup_rss;
+}
+
+/********************** Queue state object ***********************************/
+
+/**
+ * bnx2x_queue_state_change - perform Queue state change transition
+ *
+ * @bp: device handle
+ * @params: parameters to perform the transition
+ *
+ * returns 0 in case of successfully completed transition, negative error
+ * code in case of failure, positive (EBUSY) value if there is a completion
+ * to that is still pending (possible only if RAMROD_COMP_WAIT is
+ * not set in params->ramrod_flags for asynchronous commands).
+ *
+ */
+int bnx2x_queue_state_change(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+ int rc, pending_bit;
+ unsigned long *pending = &o->pending;
+
+ /* Check that the requested transition is legal */
+ if (o->check_transition(bp, o, params))
+ return -EINVAL;
+
+ /* Set "pending" bit */
+ pending_bit = o->set_pending(o, params);
+
+ /* Don't send a command if only driver cleanup was requested */
+ if (test_bit(RAMROD_DRV_CLR_ONLY, &params->ramrod_flags))
+ o->complete_cmd(bp, o, pending_bit);
+ else {
+ /* Send a ramrod */
+ rc = o->send_cmd(bp, params);
+ if (rc) {
+ o->next_state = BNX2X_Q_STATE_MAX;
+ clear_bit(pending_bit, pending);
+ smp_mb__after_clear_bit();
+ return rc;
+ }
+
+ if (test_bit(RAMROD_COMP_WAIT, &params->ramrod_flags)) {
+ rc = o->wait_comp(bp, o, pending_bit);
+ if (rc)
+ return rc;
+
+ return 0;
+ }
+ }
+
+ return !!test_bit(pending_bit, pending);
+}
+
+
+static int bnx2x_queue_set_pending(struct bnx2x_queue_sp_obj *obj,
+ struct bnx2x_queue_state_params *params)
+{
+ enum bnx2x_queue_cmd cmd = params->cmd, bit;
+
+ /* ACTIVATE and DEACTIVATE commands are implemented on top of
+ * UPDATE command.
+ */
+ if ((cmd == BNX2X_Q_CMD_ACTIVATE) ||
+ (cmd == BNX2X_Q_CMD_DEACTIVATE))
+ bit = BNX2X_Q_CMD_UPDATE;
+ else
+ bit = cmd;
+
+ set_bit(bit, &obj->pending);
+ return bit;
+}
+
+static int bnx2x_queue_wait_comp(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *o,
+ enum bnx2x_queue_cmd cmd)
+{
+ return bnx2x_state_wait(bp, cmd, &o->pending);
+}
+
+/**
+ * bnx2x_queue_comp_cmd - complete the state change command.
+ *
+ * @bp: device handle
+ * @o:
+ * @cmd:
+ *
+ * Checks that the arrived completion is expected.
+ */
+static int bnx2x_queue_comp_cmd(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *o,
+ enum bnx2x_queue_cmd cmd)
+{
+ unsigned long cur_pending = o->pending;
+
+ if (!test_and_clear_bit(cmd, &cur_pending)) {
+ BNX2X_ERR("Bad MC reply %d for queue %d in state %d "
+ "pending 0x%lx, next_state %d\n", cmd,
+ o->cids[BNX2X_PRIMARY_CID_INDEX],
+ o->state, cur_pending, o->next_state);
+ return -EINVAL;
+ }
+
+ if (o->next_tx_only >= o->max_cos)
+ /* >= becuase tx only must always be smaller than cos since the
+ * primary connection suports COS 0
+ */
+ BNX2X_ERR("illegal value for next tx_only: %d. max cos was %d",
+ o->next_tx_only, o->max_cos);
+
+ DP(BNX2X_MSG_SP, "Completing command %d for queue %d, "
+ "setting state to %d\n", cmd,
+ o->cids[BNX2X_PRIMARY_CID_INDEX], o->next_state);
+
+ if (o->next_tx_only) /* print num tx-only if any exist */
+ DP(BNX2X_MSG_SP, "primary cid %d: num tx-only cons %d\n",
+ o->cids[BNX2X_PRIMARY_CID_INDEX], o->next_tx_only);
+
+ o->state = o->next_state;
+ o->num_tx_only = o->next_tx_only;
+ o->next_state = BNX2X_Q_STATE_MAX;
+
+ /* It's important that o->state and o->next_state are
+ * updated before o->pending.
+ */
+ wmb();
+
+ clear_bit(cmd, &o->pending);
+ smp_mb__after_clear_bit();
+
+ return 0;
+}
+
+static void bnx2x_q_fill_setup_data_e2(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *cmd_params,
+ struct client_init_ramrod_data *data)
+{
+ struct bnx2x_queue_setup_params *params = &cmd_params->params.setup;
+
+ /* Rx data */
+
+ /* IPv6 TPA supported for E2 and above only */
+ data->rx.tpa_en |= test_bit(BNX2X_Q_FLG_TPA_IPV6, &params->flags) *
+ CLIENT_INIT_RX_DATA_TPA_EN_IPV6;
+}
+
+static void bnx2x_q_fill_init_general_data(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *o,
+ struct bnx2x_general_setup_params *params,
+ struct client_init_general_data *gen_data,
+ unsigned long *flags)
+{
+ gen_data->client_id = o->cl_id;
+
+ if (test_bit(BNX2X_Q_FLG_STATS, flags)) {
+ gen_data->statistics_counter_id =
+ params->stat_id;
+ gen_data->statistics_en_flg = 1;
+ gen_data->statistics_zero_flg =
+ test_bit(BNX2X_Q_FLG_ZERO_STATS, flags);
+ } else
+ gen_data->statistics_counter_id =
+ DISABLE_STATISTIC_COUNTER_ID_VALUE;
+
+ gen_data->is_fcoe_flg = test_bit(BNX2X_Q_FLG_FCOE, flags);
+ gen_data->activate_flg = test_bit(BNX2X_Q_FLG_ACTIVE, flags);
+ gen_data->sp_client_id = params->spcl_id;
+ gen_data->mtu = cpu_to_le16(params->mtu);
+ gen_data->func_id = o->func_id;
+
+
+ gen_data->cos = params->cos;
+
+ gen_data->traffic_type =
+ test_bit(BNX2X_Q_FLG_FCOE, flags) ?
+ LLFC_TRAFFIC_TYPE_FCOE : LLFC_TRAFFIC_TYPE_NW;
+
+ DP(BNX2X_MSG_SP, "flags: active %d, cos %d, stats en %d\n",
+ gen_data->activate_flg, gen_data->cos, gen_data->statistics_en_flg);
+}
+
+static void bnx2x_q_fill_init_tx_data(struct bnx2x_queue_sp_obj *o,
+ struct bnx2x_txq_setup_params *params,
+ struct client_init_tx_data *tx_data,
+ unsigned long *flags)
+{
+ tx_data->enforce_security_flg =
+ test_bit(BNX2X_Q_FLG_TX_SEC, flags);
+ tx_data->default_vlan =
+ cpu_to_le16(params->default_vlan);
+ tx_data->default_vlan_flg =
+ test_bit(BNX2X_Q_FLG_DEF_VLAN, flags);
+ tx_data->tx_switching_flg =
+ test_bit(BNX2X_Q_FLG_TX_SWITCH, flags);
+ tx_data->anti_spoofing_flg =
+ test_bit(BNX2X_Q_FLG_ANTI_SPOOF, flags);
+ tx_data->tx_status_block_id = params->fw_sb_id;
+ tx_data->tx_sb_index_number = params->sb_cq_index;
+ tx_data->tss_leading_client_id = params->tss_leading_cl_id;
+
+ tx_data->tx_bd_page_base.lo =
+ cpu_to_le32(U64_LO(params->dscr_map));
+ tx_data->tx_bd_page_base.hi =
+ cpu_to_le32(U64_HI(params->dscr_map));
+
+ /* Don't configure any Tx switching mode during queue SETUP */
+ tx_data->state = 0;
+}
+
+static void bnx2x_q_fill_init_pause_data(struct bnx2x_queue_sp_obj *o,
+ struct rxq_pause_params *params,
+ struct client_init_rx_data *rx_data)
+{
+ /* flow control data */
+ rx_data->cqe_pause_thr_low = cpu_to_le16(params->rcq_th_lo);
+ rx_data->cqe_pause_thr_high = cpu_to_le16(params->rcq_th_hi);
+ rx_data->bd_pause_thr_low = cpu_to_le16(params->bd_th_lo);
+ rx_data->bd_pause_thr_high = cpu_to_le16(params->bd_th_hi);
+ rx_data->sge_pause_thr_low = cpu_to_le16(params->sge_th_lo);
+ rx_data->sge_pause_thr_high = cpu_to_le16(params->sge_th_hi);
+ rx_data->rx_cos_mask = cpu_to_le16(params->pri_map);
+}
+
+static void bnx2x_q_fill_init_rx_data(struct bnx2x_queue_sp_obj *o,
+ struct bnx2x_rxq_setup_params *params,
+ struct client_init_rx_data *rx_data,
+ unsigned long *flags)
+{
+ /* Rx data */
+ rx_data->tpa_en = test_bit(BNX2X_Q_FLG_TPA, flags) *
+ CLIENT_INIT_RX_DATA_TPA_EN_IPV4;
+ rx_data->vmqueue_mode_en_flg = 0;
+
+ rx_data->cache_line_alignment_log_size =
+ params->cache_line_log;
+ rx_data->enable_dynamic_hc =
+ test_bit(BNX2X_Q_FLG_DHC, flags);
+ rx_data->max_sges_for_packet = params->max_sges_pkt;
+ rx_data->client_qzone_id = params->cl_qzone_id;
+ rx_data->max_agg_size = cpu_to_le16(params->tpa_agg_sz);
+
+ /* Always start in DROP_ALL mode */
+ rx_data->state = cpu_to_le16(CLIENT_INIT_RX_DATA_UCAST_DROP_ALL |
+ CLIENT_INIT_RX_DATA_MCAST_DROP_ALL);
+
+ /* We don't set drop flags */
+ rx_data->drop_ip_cs_err_flg = 0;
+ rx_data->drop_tcp_cs_err_flg = 0;
+ rx_data->drop_ttl0_flg = 0;
+ rx_data->drop_udp_cs_err_flg = 0;
+ rx_data->inner_vlan_removal_enable_flg =
+ test_bit(BNX2X_Q_FLG_VLAN, flags);
+ rx_data->outer_vlan_removal_enable_flg =
+ test_bit(BNX2X_Q_FLG_OV, flags);
+ rx_data->status_block_id = params->fw_sb_id;
+ rx_data->rx_sb_index_number = params->sb_cq_index;
+ rx_data->max_tpa_queues = params->max_tpa_queues;
+ rx_data->max_bytes_on_bd = cpu_to_le16(params->buf_sz);
+ rx_data->sge_buff_size = cpu_to_le16(params->sge_buf_sz);
+ rx_data->bd_page_base.lo =
+ cpu_to_le32(U64_LO(params->dscr_map));
+ rx_data->bd_page_base.hi =
+ cpu_to_le32(U64_HI(params->dscr_map));
+ rx_data->sge_page_base.lo =
+ cpu_to_le32(U64_LO(params->sge_map));
+ rx_data->sge_page_base.hi =
+ cpu_to_le32(U64_HI(params->sge_map));
+ rx_data->cqe_page_base.lo =
+ cpu_to_le32(U64_LO(params->rcq_map));
+ rx_data->cqe_page_base.hi =
+ cpu_to_le32(U64_HI(params->rcq_map));
+ rx_data->is_leading_rss = test_bit(BNX2X_Q_FLG_LEADING_RSS, flags);
+
+ if (test_bit(BNX2X_Q_FLG_MCAST, flags)) {
+ rx_data->approx_mcast_engine_id = o->func_id;
+ rx_data->is_approx_mcast = 1;
+ }
+
+ rx_data->rss_engine_id = params->rss_engine_id;
+
+ /* silent vlan removal */
+ rx_data->silent_vlan_removal_flg =
+ test_bit(BNX2X_Q_FLG_SILENT_VLAN_REM, flags);
+ rx_data->silent_vlan_value =
+ cpu_to_le16(params->silent_removal_value);
+ rx_data->silent_vlan_mask =
+ cpu_to_le16(params->silent_removal_mask);
+
+}
+
+/* initialize the general, tx and rx parts of a queue object */
+static void bnx2x_q_fill_setup_data_cmn(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *cmd_params,
+ struct client_init_ramrod_data *data)
+{
+ bnx2x_q_fill_init_general_data(bp, cmd_params->q_obj,
+ &cmd_params->params.setup.gen_params,
+ &data->general,
+ &cmd_params->params.setup.flags);
+
+ bnx2x_q_fill_init_tx_data(cmd_params->q_obj,
+ &cmd_params->params.setup.txq_params,
+ &data->tx,
+ &cmd_params->params.setup.flags);
+
+ bnx2x_q_fill_init_rx_data(cmd_params->q_obj,
+ &cmd_params->params.setup.rxq_params,
+ &data->rx,
+ &cmd_params->params.setup.flags);
+
+ bnx2x_q_fill_init_pause_data(cmd_params->q_obj,
+ &cmd_params->params.setup.pause_params,
+ &data->rx);
+}
+
+/* initialize the general and tx parts of a tx-only queue object */
+static void bnx2x_q_fill_setup_tx_only(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *cmd_params,
+ struct tx_queue_init_ramrod_data *data)
+{
+ bnx2x_q_fill_init_general_data(bp, cmd_params->q_obj,
+ &cmd_params->params.tx_only.gen_params,
+ &data->general,
+ &cmd_params->params.tx_only.flags);
+
+ bnx2x_q_fill_init_tx_data(cmd_params->q_obj,
+ &cmd_params->params.tx_only.txq_params,
+ &data->tx,
+ &cmd_params->params.tx_only.flags);
+
+ DP(BNX2X_MSG_SP, "cid %d, tx bd page lo %x hi %x\n",cmd_params->q_obj->cids[0],
+ data->tx.tx_bd_page_base.lo, data->tx.tx_bd_page_base.hi);
+}
+
+/**
+ * bnx2x_q_init - init HW/FW queue
+ *
+ * @bp: device handle
+ * @params:
+ *
+ * HW/FW initial Queue configuration:
+ * - HC: Rx and Tx
+ * - CDU context validation
+ *
+ */
+static inline int bnx2x_q_init(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+ struct bnx2x_queue_init_params *init = &params->params.init;
+ u16 hc_usec;
+ u8 cos;
+
+ /* Tx HC configuration */
+ if (test_bit(BNX2X_Q_TYPE_HAS_TX, &o->type) &&
+ test_bit(BNX2X_Q_FLG_HC, &init->tx.flags)) {
+ hc_usec = init->tx.hc_rate ? 1000000 / init->tx.hc_rate : 0;
+
+ bnx2x_update_coalesce_sb_index(bp, init->tx.fw_sb_id,
+ init->tx.sb_cq_index,
+ !test_bit(BNX2X_Q_FLG_HC_EN, &init->tx.flags),
+ hc_usec);
+ }
+
+ /* Rx HC configuration */
+ if (test_bit(BNX2X_Q_TYPE_HAS_RX, &o->type) &&
+ test_bit(BNX2X_Q_FLG_HC, &init->rx.flags)) {
+ hc_usec = init->rx.hc_rate ? 1000000 / init->rx.hc_rate : 0;
+
+ bnx2x_update_coalesce_sb_index(bp, init->rx.fw_sb_id,
+ init->rx.sb_cq_index,
+ !test_bit(BNX2X_Q_FLG_HC_EN, &init->rx.flags),
+ hc_usec);
+ }
+
+ /* Set CDU context validation values */
+ for (cos = 0; cos < o->max_cos; cos++) {
+ DP(BNX2X_MSG_SP, "setting context validation. cid %d, cos %d\n",
+ o->cids[cos], cos);
+ DP(BNX2X_MSG_SP, "context pointer %p\n", init->cxts[cos]);
+ bnx2x_set_ctx_validation(bp, init->cxts[cos], o->cids[cos]);
+ }
+
+ /* As no ramrod is sent, complete the command immediately */
+ o->complete_cmd(bp, o, BNX2X_Q_CMD_INIT);
+
+ mmiowb();
+ smp_mb();
+
+ return 0;
+}
+
+static inline int bnx2x_q_send_setup_e1x(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+ struct client_init_ramrod_data *rdata =
+ (struct client_init_ramrod_data *)o->rdata;
+ dma_addr_t data_mapping = o->rdata_mapping;
+ int ramrod = RAMROD_CMD_ID_ETH_CLIENT_SETUP;
+
+ /* Clear the ramrod data */
+ memset(rdata, 0, sizeof(*rdata));
+
+ /* Fill the ramrod data */
+ bnx2x_q_fill_setup_data_cmn(bp, params, rdata);
+
+ /*
+ * No need for an explicit memory barrier here as long we would
+ * need to ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read and we will have to put a full memory barrier there
+ * (inside bnx2x_sp_post()).
+ */
+
+ return bnx2x_sp_post(bp, ramrod, o->cids[BNX2X_PRIMARY_CID_INDEX],
+ U64_HI(data_mapping),
+ U64_LO(data_mapping), ETH_CONNECTION_TYPE);
+}
+
+static inline int bnx2x_q_send_setup_e2(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+ struct client_init_ramrod_data *rdata =
+ (struct client_init_ramrod_data *)o->rdata;
+ dma_addr_t data_mapping = o->rdata_mapping;
+ int ramrod = RAMROD_CMD_ID_ETH_CLIENT_SETUP;
+
+ /* Clear the ramrod data */
+ memset(rdata, 0, sizeof(*rdata));
+
+ /* Fill the ramrod data */
+ bnx2x_q_fill_setup_data_cmn(bp, params, rdata);
+ bnx2x_q_fill_setup_data_e2(bp, params, rdata);
+
+ /*
+ * No need for an explicit memory barrier here as long we would
+ * need to ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read and we will have to put a full memory barrier there
+ * (inside bnx2x_sp_post()).
+ */
+
+ return bnx2x_sp_post(bp, ramrod, o->cids[BNX2X_PRIMARY_CID_INDEX],
+ U64_HI(data_mapping),
+ U64_LO(data_mapping), ETH_CONNECTION_TYPE);
+}
+
+static inline int bnx2x_q_send_setup_tx_only(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+ struct tx_queue_init_ramrod_data *rdata =
+ (struct tx_queue_init_ramrod_data *)o->rdata;
+ dma_addr_t data_mapping = o->rdata_mapping;
+ int ramrod = RAMROD_CMD_ID_ETH_TX_QUEUE_SETUP;
+ struct bnx2x_queue_setup_tx_only_params *tx_only_params =
+ &params->params.tx_only;
+ u8 cid_index = tx_only_params->cid_index;
+
+
+ if (cid_index >= o->max_cos) {
+ BNX2X_ERR("queue[%d]: cid_index (%d) is out of range\n",
+ o->cl_id, cid_index);
+ return -EINVAL;
+ }
+
+ DP(BNX2X_MSG_SP, "parameters received: cos: %d sp-id: %d\n",
+ tx_only_params->gen_params.cos,
+ tx_only_params->gen_params.spcl_id);
+
+ /* Clear the ramrod data */
+ memset(rdata, 0, sizeof(*rdata));
+
+ /* Fill the ramrod data */
+ bnx2x_q_fill_setup_tx_only(bp, params, rdata);
+
+ DP(BNX2X_MSG_SP, "sending tx-only ramrod: cid %d, client-id %d,"
+ "sp-client id %d, cos %d\n",
+ o->cids[cid_index],
+ rdata->general.client_id,
+ rdata->general.sp_client_id, rdata->general.cos);
+
+ /*
+ * No need for an explicit memory barrier here as long we would
+ * need to ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read and we will have to put a full memory barrier there
+ * (inside bnx2x_sp_post()).
+ */
+
+ return bnx2x_sp_post(bp, ramrod, o->cids[cid_index],
+ U64_HI(data_mapping),
+ U64_LO(data_mapping), ETH_CONNECTION_TYPE);
+}
+
+static void bnx2x_q_fill_update_data(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *obj,
+ struct bnx2x_queue_update_params *params,
+ struct client_update_ramrod_data *data)
+{
+ /* Client ID of the client to update */
+ data->client_id = obj->cl_id;
+
+ /* Function ID of the client to update */
+ data->func_id = obj->func_id;
+
+ /* Default VLAN value */
+ data->default_vlan = cpu_to_le16(params->def_vlan);
+
+ /* Inner VLAN stripping */
+ data->inner_vlan_removal_enable_flg =
+ test_bit(BNX2X_Q_UPDATE_IN_VLAN_REM, &params->update_flags);
+ data->inner_vlan_removal_change_flg =
+ test_bit(BNX2X_Q_UPDATE_IN_VLAN_REM_CHNG,
+ &params->update_flags);
+
+ /* Outer VLAN sripping */
+ data->outer_vlan_removal_enable_flg =
+ test_bit(BNX2X_Q_UPDATE_OUT_VLAN_REM, &params->update_flags);
+ data->outer_vlan_removal_change_flg =
+ test_bit(BNX2X_Q_UPDATE_OUT_VLAN_REM_CHNG,
+ &params->update_flags);
+
+ /* Drop packets that have source MAC that doesn't belong to this
+ * Queue.
+ */
+ data->anti_spoofing_enable_flg =
+ test_bit(BNX2X_Q_UPDATE_ANTI_SPOOF, &params->update_flags);
+ data->anti_spoofing_change_flg =
+ test_bit(BNX2X_Q_UPDATE_ANTI_SPOOF_CHNG, &params->update_flags);
+
+ /* Activate/Deactivate */
+ data->activate_flg =
+ test_bit(BNX2X_Q_UPDATE_ACTIVATE, &params->update_flags);
+ data->activate_change_flg =
+ test_bit(BNX2X_Q_UPDATE_ACTIVATE_CHNG, &params->update_flags);
+
+ /* Enable default VLAN */
+ data->default_vlan_enable_flg =
+ test_bit(BNX2X_Q_UPDATE_DEF_VLAN_EN, &params->update_flags);
+ data->default_vlan_change_flg =
+ test_bit(BNX2X_Q_UPDATE_DEF_VLAN_EN_CHNG,
+ &params->update_flags);
+
+ /* silent vlan removal */
+ data->silent_vlan_change_flg =
+ test_bit(BNX2X_Q_UPDATE_SILENT_VLAN_REM_CHNG,
+ &params->update_flags);
+ data->silent_vlan_removal_flg =
+ test_bit(BNX2X_Q_UPDATE_SILENT_VLAN_REM, &params->update_flags);
+ data->silent_vlan_value = cpu_to_le16(params->silent_removal_value);
+ data->silent_vlan_mask = cpu_to_le16(params->silent_removal_mask);
+}
+
+static inline int bnx2x_q_send_update(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+ struct client_update_ramrod_data *rdata =
+ (struct client_update_ramrod_data *)o->rdata;
+ dma_addr_t data_mapping = o->rdata_mapping;
+ struct bnx2x_queue_update_params *update_params =
+ &params->params.update;
+ u8 cid_index = update_params->cid_index;
+
+ if (cid_index >= o->max_cos) {
+ BNX2X_ERR("queue[%d]: cid_index (%d) is out of range\n",
+ o->cl_id, cid_index);
+ return -EINVAL;
+ }
+
+
+ /* Clear the ramrod data */
+ memset(rdata, 0, sizeof(*rdata));
+
+ /* Fill the ramrod data */
+ bnx2x_q_fill_update_data(bp, o, update_params, rdata);
+
+ /*
+ * No need for an explicit memory barrier here as long we would
+ * need to ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read and we will have to put a full memory barrier there
+ * (inside bnx2x_sp_post()).
+ */
+
+ return bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_CLIENT_UPDATE,
+ o->cids[cid_index], U64_HI(data_mapping),
+ U64_LO(data_mapping), ETH_CONNECTION_TYPE);
+}
+
+/**
+ * bnx2x_q_send_deactivate - send DEACTIVATE command
+ *
+ * @bp: device handle
+ * @params:
+ *
+ * implemented using the UPDATE command.
+ */
+static inline int bnx2x_q_send_deactivate(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_update_params *update = &params->params.update;
+
+ memset(update, 0, sizeof(*update));
+
+ __set_bit(BNX2X_Q_UPDATE_ACTIVATE_CHNG, &update->update_flags);
+
+ return bnx2x_q_send_update(bp, params);
+}
+
+/**
+ * bnx2x_q_send_activate - send ACTIVATE command
+ *
+ * @bp: device handle
+ * @params:
+ *
+ * implemented using the UPDATE command.
+ */
+static inline int bnx2x_q_send_activate(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_update_params *update = &params->params.update;
+
+ memset(update, 0, sizeof(*update));
+
+ __set_bit(BNX2X_Q_UPDATE_ACTIVATE, &update->update_flags);
+ __set_bit(BNX2X_Q_UPDATE_ACTIVATE_CHNG, &update->update_flags);
+
+ return bnx2x_q_send_update(bp, params);
+}
+
+static inline int bnx2x_q_send_update_tpa(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ /* TODO: Not implemented yet. */
+ return -1;
+}
+
+static inline int bnx2x_q_send_halt(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+
+ return bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_HALT,
+ o->cids[BNX2X_PRIMARY_CID_INDEX], 0, o->cl_id,
+ ETH_CONNECTION_TYPE);
+}
+
+static inline int bnx2x_q_send_cfc_del(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+ u8 cid_idx = params->params.cfc_del.cid_index;
+
+ if (cid_idx >= o->max_cos) {
+ BNX2X_ERR("queue[%d]: cid_index (%d) is out of range\n",
+ o->cl_id, cid_idx);
+ return -EINVAL;
+ }
+
+ return bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_CFC_DEL,
+ o->cids[cid_idx], 0, 0, NONE_CONNECTION_TYPE);
+}
+
+static inline int bnx2x_q_send_terminate(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+ u8 cid_index = params->params.terminate.cid_index;
+
+ if (cid_index >= o->max_cos) {
+ BNX2X_ERR("queue[%d]: cid_index (%d) is out of range\n",
+ o->cl_id, cid_index);
+ return -EINVAL;
+ }
+
+ return bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_TERMINATE,
+ o->cids[cid_index], 0, 0, ETH_CONNECTION_TYPE);
+}
+
+static inline int bnx2x_q_send_empty(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+
+ return bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_EMPTY,
+ o->cids[BNX2X_PRIMARY_CID_INDEX], 0, 0,
+ ETH_CONNECTION_TYPE);
+}
+
+static inline int bnx2x_queue_send_cmd_cmn(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ switch (params->cmd) {
+ case BNX2X_Q_CMD_INIT:
+ return bnx2x_q_init(bp, params);
+ case BNX2X_Q_CMD_SETUP_TX_ONLY:
+ return bnx2x_q_send_setup_tx_only(bp, params);
+ case BNX2X_Q_CMD_DEACTIVATE:
+ return bnx2x_q_send_deactivate(bp, params);
+ case BNX2X_Q_CMD_ACTIVATE:
+ return bnx2x_q_send_activate(bp, params);
+ case BNX2X_Q_CMD_UPDATE:
+ return bnx2x_q_send_update(bp, params);
+ case BNX2X_Q_CMD_UPDATE_TPA:
+ return bnx2x_q_send_update_tpa(bp, params);
+ case BNX2X_Q_CMD_HALT:
+ return bnx2x_q_send_halt(bp, params);
+ case BNX2X_Q_CMD_CFC_DEL:
+ return bnx2x_q_send_cfc_del(bp, params);
+ case BNX2X_Q_CMD_TERMINATE:
+ return bnx2x_q_send_terminate(bp, params);
+ case BNX2X_Q_CMD_EMPTY:
+ return bnx2x_q_send_empty(bp, params);
+ default:
+ BNX2X_ERR("Unknown command: %d\n", params->cmd);
+ return -EINVAL;
+ }
+}
+
+static int bnx2x_queue_send_cmd_e1x(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ switch (params->cmd) {
+ case BNX2X_Q_CMD_SETUP:
+ return bnx2x_q_send_setup_e1x(bp, params);
+ case BNX2X_Q_CMD_INIT:
+ case BNX2X_Q_CMD_SETUP_TX_ONLY:
+ case BNX2X_Q_CMD_DEACTIVATE:
+ case BNX2X_Q_CMD_ACTIVATE:
+ case BNX2X_Q_CMD_UPDATE:
+ case BNX2X_Q_CMD_UPDATE_TPA:
+ case BNX2X_Q_CMD_HALT:
+ case BNX2X_Q_CMD_CFC_DEL:
+ case BNX2X_Q_CMD_TERMINATE:
+ case BNX2X_Q_CMD_EMPTY:
+ return bnx2x_queue_send_cmd_cmn(bp, params);
+ default:
+ BNX2X_ERR("Unknown command: %d\n", params->cmd);
+ return -EINVAL;
+ }
+}
+
+static int bnx2x_queue_send_cmd_e2(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params)
+{
+ switch (params->cmd) {
+ case BNX2X_Q_CMD_SETUP:
+ return bnx2x_q_send_setup_e2(bp, params);
+ case BNX2X_Q_CMD_INIT:
+ case BNX2X_Q_CMD_SETUP_TX_ONLY:
+ case BNX2X_Q_CMD_DEACTIVATE:
+ case BNX2X_Q_CMD_ACTIVATE:
+ case BNX2X_Q_CMD_UPDATE:
+ case BNX2X_Q_CMD_UPDATE_TPA:
+ case BNX2X_Q_CMD_HALT:
+ case BNX2X_Q_CMD_CFC_DEL:
+ case BNX2X_Q_CMD_TERMINATE:
+ case BNX2X_Q_CMD_EMPTY:
+ return bnx2x_queue_send_cmd_cmn(bp, params);
+ default:
+ BNX2X_ERR("Unknown command: %d\n", params->cmd);
+ return -EINVAL;
+ }
+}
+
+/**
+ * bnx2x_queue_chk_transition - check state machine of a regular Queue
+ *
+ * @bp: device handle
+ * @o:
+ * @params:
+ *
+ * (not Forwarding)
+ * It both checks if the requested command is legal in a current
+ * state and, if it's legal, sets a `next_state' in the object
+ * that will be used in the completion flow to set the `state'
+ * of the object.
+ *
+ * returns 0 if a requested command is a legal transition,
+ * -EINVAL otherwise.
+ */
+static int bnx2x_queue_chk_transition(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *o,
+ struct bnx2x_queue_state_params *params)
+{
+ enum bnx2x_q_state state = o->state, next_state = BNX2X_Q_STATE_MAX;
+ enum bnx2x_queue_cmd cmd = params->cmd;
+ struct bnx2x_queue_update_params *update_params =
+ &params->params.update;
+ u8 next_tx_only = o->num_tx_only;
+
+ /*
+ * Forget all pending for completion commands if a driver only state
+ * transition has been requested.
+ */
+ if (test_bit(RAMROD_DRV_CLR_ONLY, &params->ramrod_flags)) {
+ o->pending = 0;
+ o->next_state = BNX2X_Q_STATE_MAX;
+ }
+
+ /*
+ * Don't allow a next state transition if we are in the middle of
+ * the previous one.
+ */
+ if (o->pending)
+ return -EBUSY;
+
+ switch (state) {
+ case BNX2X_Q_STATE_RESET:
+ if (cmd == BNX2X_Q_CMD_INIT)
+ next_state = BNX2X_Q_STATE_INITIALIZED;
+
+ break;
+ case BNX2X_Q_STATE_INITIALIZED:
+ if (cmd == BNX2X_Q_CMD_SETUP) {
+ if (test_bit(BNX2X_Q_FLG_ACTIVE,
+ &params->params.setup.flags))
+ next_state = BNX2X_Q_STATE_ACTIVE;
+ else
+ next_state = BNX2X_Q_STATE_INACTIVE;
+ }
+
+ break;
+ case BNX2X_Q_STATE_ACTIVE:
+ if (cmd == BNX2X_Q_CMD_DEACTIVATE)
+ next_state = BNX2X_Q_STATE_INACTIVE;
+
+ else if ((cmd == BNX2X_Q_CMD_EMPTY) ||
+ (cmd == BNX2X_Q_CMD_UPDATE_TPA))
+ next_state = BNX2X_Q_STATE_ACTIVE;
+
+ else if (cmd == BNX2X_Q_CMD_SETUP_TX_ONLY) {
+ next_state = BNX2X_Q_STATE_MULTI_COS;
+ next_tx_only = 1;
+ }
+
+ else if (cmd == BNX2X_Q_CMD_HALT)
+ next_state = BNX2X_Q_STATE_STOPPED;
+
+ else if (cmd == BNX2X_Q_CMD_UPDATE) {
+ /* If "active" state change is requested, update the
+ * state accordingly.
+ */
+ if (test_bit(BNX2X_Q_UPDATE_ACTIVATE_CHNG,
+ &update_params->update_flags) &&
+ !test_bit(BNX2X_Q_UPDATE_ACTIVATE,
+ &update_params->update_flags))
+ next_state = BNX2X_Q_STATE_INACTIVE;
+ else
+ next_state = BNX2X_Q_STATE_ACTIVE;
+ }
+
+ break;
+ case BNX2X_Q_STATE_MULTI_COS:
+ if (cmd == BNX2X_Q_CMD_TERMINATE)
+ next_state = BNX2X_Q_STATE_MCOS_TERMINATED;
+
+ else if (cmd == BNX2X_Q_CMD_SETUP_TX_ONLY) {
+ next_state = BNX2X_Q_STATE_MULTI_COS;
+ next_tx_only = o->num_tx_only + 1;
+ }
+
+ else if ((cmd == BNX2X_Q_CMD_EMPTY) ||
+ (cmd == BNX2X_Q_CMD_UPDATE_TPA))
+ next_state = BNX2X_Q_STATE_MULTI_COS;
+
+ else if (cmd == BNX2X_Q_CMD_UPDATE) {
+ /* If "active" state change is requested, update the
+ * state accordingly.
+ */
+ if (test_bit(BNX2X_Q_UPDATE_ACTIVATE_CHNG,
+ &update_params->update_flags) &&
+ !test_bit(BNX2X_Q_UPDATE_ACTIVATE,
+ &update_params->update_flags))
+ next_state = BNX2X_Q_STATE_INACTIVE;
+ else
+ next_state = BNX2X_Q_STATE_MULTI_COS;
+ }
+
+ break;
+ case BNX2X_Q_STATE_MCOS_TERMINATED:
+ if (cmd == BNX2X_Q_CMD_CFC_DEL) {
+ next_tx_only = o->num_tx_only - 1;
+ if (next_tx_only == 0)
+ next_state = BNX2X_Q_STATE_ACTIVE;
+ else
+ next_state = BNX2X_Q_STATE_MULTI_COS;
+ }
+
+ break;
+ case BNX2X_Q_STATE_INACTIVE:
+ if (cmd == BNX2X_Q_CMD_ACTIVATE)
+ next_state = BNX2X_Q_STATE_ACTIVE;
+
+ else if ((cmd == BNX2X_Q_CMD_EMPTY) ||
+ (cmd == BNX2X_Q_CMD_UPDATE_TPA))
+ next_state = BNX2X_Q_STATE_INACTIVE;
+
+ else if (cmd == BNX2X_Q_CMD_HALT)
+ next_state = BNX2X_Q_STATE_STOPPED;
+
+ else if (cmd == BNX2X_Q_CMD_UPDATE) {
+ /* If "active" state change is requested, update the
+ * state accordingly.
+ */
+ if (test_bit(BNX2X_Q_UPDATE_ACTIVATE_CHNG,
+ &update_params->update_flags) &&
+ test_bit(BNX2X_Q_UPDATE_ACTIVATE,
+ &update_params->update_flags)){
+ if (o->num_tx_only == 0)
+ next_state = BNX2X_Q_STATE_ACTIVE;
+ else /* tx only queues exist for this queue */
+ next_state = BNX2X_Q_STATE_MULTI_COS;
+ } else
+ next_state = BNX2X_Q_STATE_INACTIVE;
+ }
+
+ break;
+ case BNX2X_Q_STATE_STOPPED:
+ if (cmd == BNX2X_Q_CMD_TERMINATE)
+ next_state = BNX2X_Q_STATE_TERMINATED;
+
+ break;
+ case BNX2X_Q_STATE_TERMINATED:
+ if (cmd == BNX2X_Q_CMD_CFC_DEL)
+ next_state = BNX2X_Q_STATE_RESET;
+
+ break;
+ default:
+ BNX2X_ERR("Illegal state: %d\n", state);
+ }
+
+ /* Transition is assured */
+ if (next_state != BNX2X_Q_STATE_MAX) {
+ DP(BNX2X_MSG_SP, "Good state transition: %d(%d)->%d\n",
+ state, cmd, next_state);
+ o->next_state = next_state;
+ o->next_tx_only = next_tx_only;
+ return 0;
+ }
+
+ DP(BNX2X_MSG_SP, "Bad state transition request: %d %d\n", state, cmd);
+
+ return -EINVAL;
+}
+
+void bnx2x_init_queue_obj(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *obj,
+ u8 cl_id, u32 *cids, u8 cid_cnt, u8 func_id,
+ void *rdata,
+ dma_addr_t rdata_mapping, unsigned long type)
+{
+ memset(obj, 0, sizeof(*obj));
+
+ /* We support only BNX2X_MULTI_TX_COS Tx CoS at the moment */
+ BUG_ON(BNX2X_MULTI_TX_COS < cid_cnt);
+
+ memcpy(obj->cids, cids, sizeof(obj->cids[0]) * cid_cnt);
+ obj->max_cos = cid_cnt;
+ obj->cl_id = cl_id;
+ obj->func_id = func_id;
+ obj->rdata = rdata;
+ obj->rdata_mapping = rdata_mapping;
+ obj->type = type;
+ obj->next_state = BNX2X_Q_STATE_MAX;
+
+ if (CHIP_IS_E1x(bp))
+ obj->send_cmd = bnx2x_queue_send_cmd_e1x;
+ else
+ obj->send_cmd = bnx2x_queue_send_cmd_e2;
+
+ obj->check_transition = bnx2x_queue_chk_transition;
+
+ obj->complete_cmd = bnx2x_queue_comp_cmd;
+ obj->wait_comp = bnx2x_queue_wait_comp;
+ obj->set_pending = bnx2x_queue_set_pending;
+}
+
+void bnx2x_queue_set_cos_cid(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *obj,
+ u32 cid, u8 index)
+{
+ obj->cids[index] = cid;
+}
+
+/********************** Function state object *********************************/
+enum bnx2x_func_state bnx2x_func_get_state(struct bnx2x *bp,
+ struct bnx2x_func_sp_obj *o)
+{
+ /* in the middle of transaction - return INVALID state */
+ if (o->pending)
+ return BNX2X_F_STATE_MAX;
+
+ /*
+ * unsure the order of reading of o->pending and o->state
+ * o->pending should be read first
+ */
+ rmb();
+
+ return o->state;
+}
+
+static int bnx2x_func_wait_comp(struct bnx2x *bp,
+ struct bnx2x_func_sp_obj *o,
+ enum bnx2x_func_cmd cmd)
+{
+ return bnx2x_state_wait(bp, cmd, &o->pending);
+}
+
+/**
+ * bnx2x_func_state_change_comp - complete the state machine transition
+ *
+ * @bp: device handle
+ * @o:
+ * @cmd:
+ *
+ * Called on state change transition. Completes the state
+ * machine transition only - no HW interaction.
+ */
+static inline int bnx2x_func_state_change_comp(struct bnx2x *bp,
+ struct bnx2x_func_sp_obj *o,
+ enum bnx2x_func_cmd cmd)
+{
+ unsigned long cur_pending = o->pending;
+
+ if (!test_and_clear_bit(cmd, &cur_pending)) {
+ BNX2X_ERR("Bad MC reply %d for func %d in state %d "
+ "pending 0x%lx, next_state %d\n", cmd, BP_FUNC(bp),
+ o->state, cur_pending, o->next_state);
+ return -EINVAL;
+ }
+
+ DP(BNX2X_MSG_SP,
+ "Completing command %d for func %d, setting state to %d\n",
+ cmd, BP_FUNC(bp), o->next_state);
+
+ o->state = o->next_state;
+ o->next_state = BNX2X_F_STATE_MAX;
+
+ /* It's important that o->state and o->next_state are
+ * updated before o->pending.
+ */
+ wmb();
+
+ clear_bit(cmd, &o->pending);
+ smp_mb__after_clear_bit();
+
+ return 0;
+}
+
+/**
+ * bnx2x_func_comp_cmd - complete the state change command
+ *
+ * @bp: device handle
+ * @o:
+ * @cmd:
+ *
+ * Checks that the arrived completion is expected.
+ */
+static int bnx2x_func_comp_cmd(struct bnx2x *bp,
+ struct bnx2x_func_sp_obj *o,
+ enum bnx2x_func_cmd cmd)
+{
+ /* Complete the state machine part first, check if it's a
+ * legal completion.
+ */
+ int rc = bnx2x_func_state_change_comp(bp, o, cmd);
+ return rc;
+}
+
+/**
+ * bnx2x_func_chk_transition - perform function state machine transition
+ *
+ * @bp: device handle
+ * @o:
+ * @params:
+ *
+ * It both checks if the requested command is legal in a current
+ * state and, if it's legal, sets a `next_state' in the object
+ * that will be used in the completion flow to set the `state'
+ * of the object.
+ *
+ * returns 0 if a requested command is a legal transition,
+ * -EINVAL otherwise.
+ */
+static int bnx2x_func_chk_transition(struct bnx2x *bp,
+ struct bnx2x_func_sp_obj *o,
+ struct bnx2x_func_state_params *params)
+{
+ enum bnx2x_func_state state = o->state, next_state = BNX2X_F_STATE_MAX;
+ enum bnx2x_func_cmd cmd = params->cmd;
+
+ /*
+ * Forget all pending for completion commands if a driver only state
+ * transition has been requested.
+ */
+ if (test_bit(RAMROD_DRV_CLR_ONLY, &params->ramrod_flags)) {
+ o->pending = 0;
+ o->next_state = BNX2X_F_STATE_MAX;
+ }
+
+ /*
+ * Don't allow a next state transition if we are in the middle of
+ * the previous one.
+ */
+ if (o->pending)
+ return -EBUSY;
+
+ switch (state) {
+ case BNX2X_F_STATE_RESET:
+ if (cmd == BNX2X_F_CMD_HW_INIT)
+ next_state = BNX2X_F_STATE_INITIALIZED;
+
+ break;
+ case BNX2X_F_STATE_INITIALIZED:
+ if (cmd == BNX2X_F_CMD_START)
+ next_state = BNX2X_F_STATE_STARTED;
+
+ else if (cmd == BNX2X_F_CMD_HW_RESET)
+ next_state = BNX2X_F_STATE_RESET;
+
+ break;
+ case BNX2X_F_STATE_STARTED:
+ if (cmd == BNX2X_F_CMD_STOP)
+ next_state = BNX2X_F_STATE_INITIALIZED;
+ else if (cmd == BNX2X_F_CMD_TX_STOP)
+ next_state = BNX2X_F_STATE_TX_STOPPED;
+
+ break;
+ case BNX2X_F_STATE_TX_STOPPED:
+ if (cmd == BNX2X_F_CMD_TX_START)
+ next_state = BNX2X_F_STATE_STARTED;
+
+ break;
+ default:
+ BNX2X_ERR("Unknown state: %d\n", state);
+ }
+
+ /* Transition is assured */
+ if (next_state != BNX2X_F_STATE_MAX) {
+ DP(BNX2X_MSG_SP, "Good function state transition: %d(%d)->%d\n",
+ state, cmd, next_state);
+ o->next_state = next_state;
+ return 0;
+ }
+
+ DP(BNX2X_MSG_SP, "Bad function state transition request: %d %d\n",
+ state, cmd);
+
+ return -EINVAL;
+}
+
+/**
+ * bnx2x_func_init_func - performs HW init at function stage
+ *
+ * @bp: device handle
+ * @drv:
+ *
+ * Init HW when the current phase is
+ * FW_MSG_CODE_DRV_LOAD_FUNCTION: initialize only FUNCTION-only
+ * HW blocks.
+ */
+static inline int bnx2x_func_init_func(struct bnx2x *bp,
+ const struct bnx2x_func_sp_drv_ops *drv)
+{
+ return drv->init_hw_func(bp);
+}
+
+/**
+ * bnx2x_func_init_port - performs HW init at port stage
+ *
+ * @bp: device handle
+ * @drv:
+ *
+ * Init HW when the current phase is
+ * FW_MSG_CODE_DRV_LOAD_PORT: initialize PORT-only and
+ * FUNCTION-only HW blocks.
+ *
+ */
+static inline int bnx2x_func_init_port(struct bnx2x *bp,
+ const struct bnx2x_func_sp_drv_ops *drv)
+{
+ int rc = drv->init_hw_port(bp);
+ if (rc)
+ return rc;
+
+ return bnx2x_func_init_func(bp, drv);
+}
+
+/**
+ * bnx2x_func_init_cmn_chip - performs HW init at chip-common stage
+ *
+ * @bp: device handle
+ * @drv:
+ *
+ * Init HW when the current phase is
+ * FW_MSG_CODE_DRV_LOAD_COMMON_CHIP: initialize COMMON_CHIP,
+ * PORT-only and FUNCTION-only HW blocks.
+ */
+static inline int bnx2x_func_init_cmn_chip(struct bnx2x *bp,
+ const struct bnx2x_func_sp_drv_ops *drv)
+{
+ int rc = drv->init_hw_cmn_chip(bp);
+ if (rc)
+ return rc;
+
+ return bnx2x_func_init_port(bp, drv);
+}
+
+/**
+ * bnx2x_func_init_cmn - performs HW init at common stage
+ *
+ * @bp: device handle
+ * @drv:
+ *
+ * Init HW when the current phase is
+ * FW_MSG_CODE_DRV_LOAD_COMMON_CHIP: initialize COMMON,
+ * PORT-only and FUNCTION-only HW blocks.
+ */
+static inline int bnx2x_func_init_cmn(struct bnx2x *bp,
+ const struct bnx2x_func_sp_drv_ops *drv)
+{
+ int rc = drv->init_hw_cmn(bp);
+ if (rc)
+ return rc;
+
+ return bnx2x_func_init_port(bp, drv);
+}
+
+static int bnx2x_func_hw_init(struct bnx2x *bp,
+ struct bnx2x_func_state_params *params)
+{
+ u32 load_code = params->params.hw_init.load_phase;
+ struct bnx2x_func_sp_obj *o = params->f_obj;
+ const struct bnx2x_func_sp_drv_ops *drv = o->drv;
+ int rc = 0;
+
+ DP(BNX2X_MSG_SP, "function %d load_code %x\n",
+ BP_ABS_FUNC(bp), load_code);
+
+ /* Prepare buffers for unzipping the FW */
+ rc = drv->gunzip_init(bp);
+ if (rc)
+ return rc;
+
+ /* Prepare FW */
+ rc = drv->init_fw(bp);
+ if (rc) {
+ BNX2X_ERR("Error loading firmware\n");
+ goto init_err;
+ }
+
+ /* Handle the beginning of COMMON_XXX pases separatelly... */
+ switch (load_code) {
+ case FW_MSG_CODE_DRV_LOAD_COMMON_CHIP:
+ rc = bnx2x_func_init_cmn_chip(bp, drv);
+ if (rc)
+ goto init_err;
+
+ break;
+ case FW_MSG_CODE_DRV_LOAD_COMMON:
+ rc = bnx2x_func_init_cmn(bp, drv);
+ if (rc)
+ goto init_err;
+
+ break;
+ case FW_MSG_CODE_DRV_LOAD_PORT:
+ rc = bnx2x_func_init_port(bp, drv);
+ if (rc)
+ goto init_err;
+
+ break;
+ case FW_MSG_CODE_DRV_LOAD_FUNCTION:
+ rc = bnx2x_func_init_func(bp, drv);
+ if (rc)
+ goto init_err;
+
+ break;
+ default:
+ BNX2X_ERR("Unknown load_code (0x%x) from MCP\n", load_code);
+ rc = -EINVAL;
+ }
+
+init_err:
+ drv->gunzip_end(bp);
+
+ /* In case of success, complete the comand immediatelly: no ramrods
+ * have been sent.
+ */
+ if (!rc)
+ o->complete_cmd(bp, o, BNX2X_F_CMD_HW_INIT);
+
+ return rc;
+}
+
+/**
+ * bnx2x_func_reset_func - reset HW at function stage
+ *
+ * @bp: device handle
+ * @drv:
+ *
+ * Reset HW at FW_MSG_CODE_DRV_UNLOAD_FUNCTION stage: reset only
+ * FUNCTION-only HW blocks.
+ */
+static inline void bnx2x_func_reset_func(struct bnx2x *bp,
+ const struct bnx2x_func_sp_drv_ops *drv)
+{
+ drv->reset_hw_func(bp);
+}
+
+/**
+ * bnx2x_func_reset_port - reser HW at port stage
+ *
+ * @bp: device handle
+ * @drv:
+ *
+ * Reset HW at FW_MSG_CODE_DRV_UNLOAD_PORT stage: reset
+ * FUNCTION-only and PORT-only HW blocks.
+ *
+ * !!!IMPORTANT!!!
+ *
+ * It's important to call reset_port before reset_func() as the last thing
+ * reset_func does is pf_disable() thus disabling PGLUE_B, which
+ * makes impossible any DMAE transactions.
+ */
+static inline void bnx2x_func_reset_port(struct bnx2x *bp,
+ const struct bnx2x_func_sp_drv_ops *drv)
+{
+ drv->reset_hw_port(bp);
+ bnx2x_func_reset_func(bp, drv);
+}
+
+/**
+ * bnx2x_func_reset_cmn - reser HW at common stage
+ *
+ * @bp: device handle
+ * @drv:
+ *
+ * Reset HW at FW_MSG_CODE_DRV_UNLOAD_COMMON and
+ * FW_MSG_CODE_DRV_UNLOAD_COMMON_CHIP stages: reset COMMON,
+ * COMMON_CHIP, FUNCTION-only and PORT-only HW blocks.
+ */
+static inline void bnx2x_func_reset_cmn(struct bnx2x *bp,
+ const struct bnx2x_func_sp_drv_ops *drv)
+{
+ bnx2x_func_reset_port(bp, drv);
+ drv->reset_hw_cmn(bp);
+}
+
+
+static inline int bnx2x_func_hw_reset(struct bnx2x *bp,
+ struct bnx2x_func_state_params *params)
+{
+ u32 reset_phase = params->params.hw_reset.reset_phase;
+ struct bnx2x_func_sp_obj *o = params->f_obj;
+ const struct bnx2x_func_sp_drv_ops *drv = o->drv;
+
+ DP(BNX2X_MSG_SP, "function %d reset_phase %x\n", BP_ABS_FUNC(bp),
+ reset_phase);
+
+ switch (reset_phase) {
+ case FW_MSG_CODE_DRV_UNLOAD_COMMON:
+ bnx2x_func_reset_cmn(bp, drv);
+ break;
+ case FW_MSG_CODE_DRV_UNLOAD_PORT:
+ bnx2x_func_reset_port(bp, drv);
+ break;
+ case FW_MSG_CODE_DRV_UNLOAD_FUNCTION:
+ bnx2x_func_reset_func(bp, drv);
+ break;
+ default:
+ BNX2X_ERR("Unknown reset_phase (0x%x) from MCP\n",
+ reset_phase);
+ break;
+ }
+
+ /* Complete the comand immediatelly: no ramrods have been sent. */
+ o->complete_cmd(bp, o, BNX2X_F_CMD_HW_RESET);
+
+ return 0;
+}
+
+static inline int bnx2x_func_send_start(struct bnx2x *bp,
+ struct bnx2x_func_state_params *params)
+{
+ struct bnx2x_func_sp_obj *o = params->f_obj;
+ struct function_start_data *rdata =
+ (struct function_start_data *)o->rdata;
+ dma_addr_t data_mapping = o->rdata_mapping;
+ struct bnx2x_func_start_params *start_params = &params->params.start;
+
+ memset(rdata, 0, sizeof(*rdata));
+
+ /* Fill the ramrod data with provided parameters */
+ rdata->function_mode = cpu_to_le16(start_params->mf_mode);
+ rdata->sd_vlan_tag = start_params->sd_vlan_tag;
+ rdata->path_id = BP_PATH(bp);
+ rdata->network_cos_mode = start_params->network_cos_mode;
+
+ /*
+ * No need for an explicit memory barrier here as long we would
+ * need to ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read and we will have to put a full memory barrier there
+ * (inside bnx2x_sp_post()).
+ */
+
+ return bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_FUNCTION_START, 0,
+ U64_HI(data_mapping),
+ U64_LO(data_mapping), NONE_CONNECTION_TYPE);
+}
+
+static inline int bnx2x_func_send_stop(struct bnx2x *bp,
+ struct bnx2x_func_state_params *params)
+{
+ return bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_FUNCTION_STOP, 0, 0, 0,
+ NONE_CONNECTION_TYPE);
+}
+
+static inline int bnx2x_func_send_tx_stop(struct bnx2x *bp,
+ struct bnx2x_func_state_params *params)
+{
+ return bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STOP_TRAFFIC, 0, 0, 0,
+ NONE_CONNECTION_TYPE);
+}
+static inline int bnx2x_func_send_tx_start(struct bnx2x *bp,
+ struct bnx2x_func_state_params *params)
+{
+ struct bnx2x_func_sp_obj *o = params->f_obj;
+ struct flow_control_configuration *rdata =
+ (struct flow_control_configuration *)o->rdata;
+ dma_addr_t data_mapping = o->rdata_mapping;
+ struct bnx2x_func_tx_start_params *tx_start_params =
+ &params->params.tx_start;
+ int i;
+
+ memset(rdata, 0, sizeof(*rdata));
+
+ rdata->dcb_enabled = tx_start_params->dcb_enabled;
+ rdata->dcb_version = tx_start_params->dcb_version;
+ rdata->dont_add_pri_0_en = tx_start_params->dont_add_pri_0_en;
+
+ for (i = 0; i < ARRAY_SIZE(rdata->traffic_type_to_priority_cos); i++)
+ rdata->traffic_type_to_priority_cos[i] =
+ tx_start_params->traffic_type_to_priority_cos[i];
+
+ return bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_START_TRAFFIC, 0,
+ U64_HI(data_mapping),
+ U64_LO(data_mapping), NONE_CONNECTION_TYPE);
+}
+
+static int bnx2x_func_send_cmd(struct bnx2x *bp,
+ struct bnx2x_func_state_params *params)
+{
+ switch (params->cmd) {
+ case BNX2X_F_CMD_HW_INIT:
+ return bnx2x_func_hw_init(bp, params);
+ case BNX2X_F_CMD_START:
+ return bnx2x_func_send_start(bp, params);
+ case BNX2X_F_CMD_STOP:
+ return bnx2x_func_send_stop(bp, params);
+ case BNX2X_F_CMD_HW_RESET:
+ return bnx2x_func_hw_reset(bp, params);
+ case BNX2X_F_CMD_TX_STOP:
+ return bnx2x_func_send_tx_stop(bp, params);
+ case BNX2X_F_CMD_TX_START:
+ return bnx2x_func_send_tx_start(bp, params);
+ default:
+ BNX2X_ERR("Unknown command: %d\n", params->cmd);
+ return -EINVAL;
+ }
+}
+
+void bnx2x_init_func_obj(struct bnx2x *bp,
+ struct bnx2x_func_sp_obj *obj,
+ void *rdata, dma_addr_t rdata_mapping,
+ struct bnx2x_func_sp_drv_ops *drv_iface)
+{
+ memset(obj, 0, sizeof(*obj));
+
+ mutex_init(&obj->one_pending_mutex);
+
+ obj->rdata = rdata;
+ obj->rdata_mapping = rdata_mapping;
+
+ obj->send_cmd = bnx2x_func_send_cmd;
+ obj->check_transition = bnx2x_func_chk_transition;
+ obj->complete_cmd = bnx2x_func_comp_cmd;
+ obj->wait_comp = bnx2x_func_wait_comp;
+
+ obj->drv = drv_iface;
+}
+
+/**
+ * bnx2x_func_state_change - perform Function state change transition
+ *
+ * @bp: device handle
+ * @params: parameters to perform the transaction
+ *
+ * returns 0 in case of successfully completed transition,
+ * negative error code in case of failure, positive
+ * (EBUSY) value if there is a completion to that is
+ * still pending (possible only if RAMROD_COMP_WAIT is
+ * not set in params->ramrod_flags for asynchronous
+ * commands).
+ */
+int bnx2x_func_state_change(struct bnx2x *bp,
+ struct bnx2x_func_state_params *params)
+{
+ struct bnx2x_func_sp_obj *o = params->f_obj;
+ int rc;
+ enum bnx2x_func_cmd cmd = params->cmd;
+ unsigned long *pending = &o->pending;
+
+ mutex_lock(&o->one_pending_mutex);
+
+ /* Check that the requested transition is legal */
+ if (o->check_transition(bp, o, params)) {
+ mutex_unlock(&o->one_pending_mutex);
+ return -EINVAL;
+ }
+
+ /* Set "pending" bit */
+ set_bit(cmd, pending);
+
+ /* Don't send a command if only driver cleanup was requested */
+ if (test_bit(RAMROD_DRV_CLR_ONLY, &params->ramrod_flags)) {
+ bnx2x_func_state_change_comp(bp, o, cmd);
+ mutex_unlock(&o->one_pending_mutex);
+ } else {
+ /* Send a ramrod */
+ rc = o->send_cmd(bp, params);
+
+ mutex_unlock(&o->one_pending_mutex);
+
+ if (rc) {
+ o->next_state = BNX2X_F_STATE_MAX;
+ clear_bit(cmd, pending);
+ smp_mb__after_clear_bit();
+ return rc;
+ }
+
+ if (test_bit(RAMROD_COMP_WAIT, &params->ramrod_flags)) {
+ rc = o->wait_comp(bp, o, cmd);
+ if (rc)
+ return rc;
+
+ return 0;
+ }
+ }
+
+ return !!test_bit(cmd, pending);
+}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
new file mode 100644
index 000000000000..9a517c2e9f1b
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
@@ -0,0 +1,1297 @@
+/* bnx2x_sp.h: Broadcom Everest network driver.
+ *
+ * Copyright 2011 Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2, available
+ * at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html (the "GPL").
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a
+ * license other than the GPL, without Broadcom's express prior written
+ * consent.
+ *
+ * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Written by: Vladislav Zolotarov
+ *
+ */
+#ifndef BNX2X_SP_VERBS
+#define BNX2X_SP_VERBS
+
+struct bnx2x;
+struct eth_context;
+
+/* Bits representing general command's configuration */
+enum {
+ RAMROD_TX,
+ RAMROD_RX,
+ /* Wait until all pending commands complete */
+ RAMROD_COMP_WAIT,
+ /* Don't send a ramrod, only update a registry */
+ RAMROD_DRV_CLR_ONLY,
+ /* Configure HW according to the current object state */
+ RAMROD_RESTORE,
+ /* Execute the next command now */
+ RAMROD_EXEC,
+ /*
+ * Don't add a new command and continue execution of posponed
+ * commands. If not set a new command will be added to the
+ * pending commands list.
+ */
+ RAMROD_CONT,
+};
+
+typedef enum {
+ BNX2X_OBJ_TYPE_RX,
+ BNX2X_OBJ_TYPE_TX,
+ BNX2X_OBJ_TYPE_RX_TX,
+} bnx2x_obj_type;
+
+/* Filtering states */
+enum {
+ BNX2X_FILTER_MAC_PENDING,
+ BNX2X_FILTER_VLAN_PENDING,
+ BNX2X_FILTER_VLAN_MAC_PENDING,
+ BNX2X_FILTER_RX_MODE_PENDING,
+ BNX2X_FILTER_RX_MODE_SCHED,
+ BNX2X_FILTER_ISCSI_ETH_START_SCHED,
+ BNX2X_FILTER_ISCSI_ETH_STOP_SCHED,
+ BNX2X_FILTER_FCOE_ETH_START_SCHED,
+ BNX2X_FILTER_FCOE_ETH_STOP_SCHED,
+ BNX2X_FILTER_MCAST_PENDING,
+ BNX2X_FILTER_MCAST_SCHED,
+ BNX2X_FILTER_RSS_CONF_PENDING,
+};
+
+struct bnx2x_raw_obj {
+ u8 func_id;
+
+ /* Queue params */
+ u8 cl_id;
+ u32 cid;
+
+ /* Ramrod data buffer params */
+ void *rdata;
+ dma_addr_t rdata_mapping;
+
+ /* Ramrod state params */
+ int state; /* "ramrod is pending" state bit */
+ unsigned long *pstate; /* pointer to state buffer */
+
+ bnx2x_obj_type obj_type;
+
+ int (*wait_comp)(struct bnx2x *bp,
+ struct bnx2x_raw_obj *o);
+
+ bool (*check_pending)(struct bnx2x_raw_obj *o);
+ void (*clear_pending)(struct bnx2x_raw_obj *o);
+ void (*set_pending)(struct bnx2x_raw_obj *o);
+};
+
+/************************* VLAN-MAC commands related parameters ***************/
+struct bnx2x_mac_ramrod_data {
+ u8 mac[ETH_ALEN];
+};
+
+struct bnx2x_vlan_ramrod_data {
+ u16 vlan;
+};
+
+struct bnx2x_vlan_mac_ramrod_data {
+ u8 mac[ETH_ALEN];
+ u16 vlan;
+};
+
+union bnx2x_classification_ramrod_data {
+ struct bnx2x_mac_ramrod_data mac;
+ struct bnx2x_vlan_ramrod_data vlan;
+ struct bnx2x_vlan_mac_ramrod_data vlan_mac;
+};
+
+/* VLAN_MAC commands */
+enum bnx2x_vlan_mac_cmd {
+ BNX2X_VLAN_MAC_ADD,
+ BNX2X_VLAN_MAC_DEL,
+ BNX2X_VLAN_MAC_MOVE,
+};
+
+struct bnx2x_vlan_mac_data {
+ /* Requested command: BNX2X_VLAN_MAC_XX */
+ enum bnx2x_vlan_mac_cmd cmd;
+ /*
+ * used to contain the data related vlan_mac_flags bits from
+ * ramrod parameters.
+ */
+ unsigned long vlan_mac_flags;
+
+ /* Needed for MOVE command */
+ struct bnx2x_vlan_mac_obj *target_obj;
+
+ union bnx2x_classification_ramrod_data u;
+};
+
+/*************************** Exe Queue obj ************************************/
+union bnx2x_exe_queue_cmd_data {
+ struct bnx2x_vlan_mac_data vlan_mac;
+
+ struct {
+ /* TODO */
+ } mcast;
+};
+
+struct bnx2x_exeq_elem {
+ struct list_head link;
+
+ /* Length of this element in the exe_chunk. */
+ int cmd_len;
+
+ union bnx2x_exe_queue_cmd_data cmd_data;
+};
+
+union bnx2x_qable_obj;
+
+union bnx2x_exeq_comp_elem {
+ union event_ring_elem *elem;
+};
+
+struct bnx2x_exe_queue_obj;
+
+typedef int (*exe_q_validate)(struct bnx2x *bp,
+ union bnx2x_qable_obj *o,
+ struct bnx2x_exeq_elem *elem);
+
+/**
+ * @return positive is entry was optimized, 0 - if not, negative
+ * in case of an error.
+ */
+typedef int (*exe_q_optimize)(struct bnx2x *bp,
+ union bnx2x_qable_obj *o,
+ struct bnx2x_exeq_elem *elem);
+typedef int (*exe_q_execute)(struct bnx2x *bp,
+ union bnx2x_qable_obj *o,
+ struct list_head *exe_chunk,
+ unsigned long *ramrod_flags);
+typedef struct bnx2x_exeq_elem *
+ (*exe_q_get)(struct bnx2x_exe_queue_obj *o,
+ struct bnx2x_exeq_elem *elem);
+
+struct bnx2x_exe_queue_obj {
+ /*
+ * Commands pending for an execution.
+ */
+ struct list_head exe_queue;
+
+ /*
+ * Commands pending for an completion.
+ */
+ struct list_head pending_comp;
+
+ spinlock_t lock;
+
+ /* Maximum length of commands' list for one execution */
+ int exe_chunk_len;
+
+ union bnx2x_qable_obj *owner;
+
+ /****** Virtual functions ******/
+ /**
+ * Called before commands execution for commands that are really
+ * going to be executed (after 'optimize').
+ *
+ * Must run under exe_queue->lock
+ */
+ exe_q_validate validate;
+
+
+ /**
+ * This will try to cancel the current pending commands list
+ * considering the new command.
+ *
+ * Must run under exe_queue->lock
+ */
+ exe_q_optimize optimize;
+
+ /**
+ * Run the next commands chunk (owner specific).
+ */
+ exe_q_execute execute;
+
+ /**
+ * Return the exe_queue element containing the specific command
+ * if any. Otherwise return NULL.
+ */
+ exe_q_get get;
+};
+/***************** Classification verbs: Set/Del MAC/VLAN/VLAN-MAC ************/
+/*
+ * Element in the VLAN_MAC registry list having all currenty configured
+ * rules.
+ */
+struct bnx2x_vlan_mac_registry_elem {
+ struct list_head link;
+
+ /*
+ * Used to store the cam offset used for the mac/vlan/vlan-mac.
+ * Relevant for 57710 and 57711 only. VLANs and MACs share the
+ * same CAM for these chips.
+ */
+ int cam_offset;
+
+ /* Needed for DEL and RESTORE flows */
+ unsigned long vlan_mac_flags;
+
+ union bnx2x_classification_ramrod_data u;
+};
+
+/* Bits representing VLAN_MAC commands specific flags */
+enum {
+ BNX2X_UC_LIST_MAC,
+ BNX2X_ETH_MAC,
+ BNX2X_ISCSI_ETH_MAC,
+ BNX2X_NETQ_ETH_MAC,
+ BNX2X_DONT_CONSUME_CAM_CREDIT,
+ BNX2X_DONT_CONSUME_CAM_CREDIT_DEST,
+};
+
+struct bnx2x_vlan_mac_ramrod_params {
+ /* Object to run the command from */
+ struct bnx2x_vlan_mac_obj *vlan_mac_obj;
+
+ /* General command flags: COMP_WAIT, etc. */
+ unsigned long ramrod_flags;
+
+ /* Command specific configuration request */
+ struct bnx2x_vlan_mac_data user_req;
+};
+
+struct bnx2x_vlan_mac_obj {
+ struct bnx2x_raw_obj raw;
+
+ /* Bookkeeping list: will prevent the addition of already existing
+ * entries.
+ */
+ struct list_head head;
+
+ /* TODO: Add it's initialization in the init functions */
+ struct bnx2x_exe_queue_obj exe_queue;
+
+ /* MACs credit pool */
+ struct bnx2x_credit_pool_obj *macs_pool;
+
+ /* VLANs credit pool */
+ struct bnx2x_credit_pool_obj *vlans_pool;
+
+ /* RAMROD command to be used */
+ int ramrod_cmd;
+
+ /**
+ * Checks if ADD-ramrod with the given params may be performed.
+ *
+ * @return zero if the element may be added
+ */
+
+ int (*check_add)(struct bnx2x_vlan_mac_obj *o,
+ union bnx2x_classification_ramrod_data *data);
+
+ /**
+ * Checks if DEL-ramrod with the given params may be performed.
+ *
+ * @return true if the element may be deleted
+ */
+ struct bnx2x_vlan_mac_registry_elem *
+ (*check_del)(struct bnx2x_vlan_mac_obj *o,
+ union bnx2x_classification_ramrod_data *data);
+
+ /**
+ * Checks if DEL-ramrod with the given params may be performed.
+ *
+ * @return true if the element may be deleted
+ */
+ bool (*check_move)(struct bnx2x_vlan_mac_obj *src_o,
+ struct bnx2x_vlan_mac_obj *dst_o,
+ union bnx2x_classification_ramrod_data *data);
+
+ /**
+ * Update the relevant credit object(s) (consume/return
+ * correspondingly).
+ */
+ bool (*get_credit)(struct bnx2x_vlan_mac_obj *o);
+ bool (*put_credit)(struct bnx2x_vlan_mac_obj *o);
+ bool (*get_cam_offset)(struct bnx2x_vlan_mac_obj *o, int *offset);
+ bool (*put_cam_offset)(struct bnx2x_vlan_mac_obj *o, int offset);
+
+ /**
+ * Configures one rule in the ramrod data buffer.
+ */
+ void (*set_one_rule)(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o,
+ struct bnx2x_exeq_elem *elem, int rule_idx,
+ int cam_offset);
+
+ /**
+ * Delete all configured elements having the given
+ * vlan_mac_flags specification. Assumes no pending for
+ * execution commands. Will schedule all all currently
+ * configured MACs/VLANs/VLAN-MACs matching the vlan_mac_flags
+ * specification for deletion and will use the given
+ * ramrod_flags for the last DEL operation.
+ *
+ * @param bp
+ * @param o
+ * @param ramrod_flags RAMROD_XX flags
+ *
+ * @return 0 if the last operation has completed successfully
+ * and there are no more elements left, positive value
+ * if there are pending for completion commands,
+ * negative value in case of failure.
+ */
+ int (*delete_all)(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *o,
+ unsigned long *vlan_mac_flags,
+ unsigned long *ramrod_flags);
+
+ /**
+ * Reconfigures the next MAC/VLAN/VLAN-MAC element from the previously
+ * configured elements list.
+ *
+ * @param bp
+ * @param p Command parameters (RAMROD_COMP_WAIT bit in
+ * ramrod_flags is only taken into an account)
+ * @param ppos a pointer to the cooky that should be given back in the
+ * next call to make function handle the next element. If
+ * *ppos is set to NULL it will restart the iterator.
+ * If returned *ppos == NULL this means that the last
+ * element has been handled.
+ *
+ * @return int
+ */
+ int (*restore)(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_ramrod_params *p,
+ struct bnx2x_vlan_mac_registry_elem **ppos);
+
+ /**
+ * Should be called on a completion arival.
+ *
+ * @param bp
+ * @param o
+ * @param cqe Completion element we are handling
+ * @param ramrod_flags if RAMROD_CONT is set the next bulk of
+ * pending commands will be executed.
+ * RAMROD_DRV_CLR_ONLY and RAMROD_RESTORE
+ * may also be set if needed.
+ *
+ * @return 0 if there are neither pending nor waiting for
+ * completion commands. Positive value if there are
+ * pending for execution or for completion commands.
+ * Negative value in case of an error (including an
+ * error in the cqe).
+ */
+ int (*complete)(struct bnx2x *bp, struct bnx2x_vlan_mac_obj *o,
+ union event_ring_elem *cqe,
+ unsigned long *ramrod_flags);
+
+ /**
+ * Wait for completion of all commands. Don't schedule new ones,
+ * just wait. It assumes that the completion code will schedule
+ * for new commands.
+ */
+ int (*wait)(struct bnx2x *bp, struct bnx2x_vlan_mac_obj *o);
+};
+
+/** RX_MODE verbs:DROP_ALL/ACCEPT_ALL/ACCEPT_ALL_MULTI/ACCEPT_ALL_VLAN/NORMAL */
+
+/* RX_MODE ramrod spesial flags: set in rx_mode_flags field in
+ * a bnx2x_rx_mode_ramrod_params.
+ */
+enum {
+ BNX2X_RX_MODE_FCOE_ETH,
+ BNX2X_RX_MODE_ISCSI_ETH,
+};
+
+enum {
+ BNX2X_ACCEPT_UNICAST,
+ BNX2X_ACCEPT_MULTICAST,
+ BNX2X_ACCEPT_ALL_UNICAST,
+ BNX2X_ACCEPT_ALL_MULTICAST,
+ BNX2X_ACCEPT_BROADCAST,
+ BNX2X_ACCEPT_UNMATCHED,
+ BNX2X_ACCEPT_ANY_VLAN
+};
+
+struct bnx2x_rx_mode_ramrod_params {
+ struct bnx2x_rx_mode_obj *rx_mode_obj;
+ unsigned long *pstate;
+ int state;
+ u8 cl_id;
+ u32 cid;
+ u8 func_id;
+ unsigned long ramrod_flags;
+ unsigned long rx_mode_flags;
+
+ /*
+ * rdata is either a pointer to eth_filter_rules_ramrod_data(e2) or to
+ * a tstorm_eth_mac_filter_config (e1x).
+ */
+ void *rdata;
+ dma_addr_t rdata_mapping;
+
+ /* Rx mode settings */
+ unsigned long rx_accept_flags;
+
+ /* internal switching settings */
+ unsigned long tx_accept_flags;
+};
+
+struct bnx2x_rx_mode_obj {
+ int (*config_rx_mode)(struct bnx2x *bp,
+ struct bnx2x_rx_mode_ramrod_params *p);
+
+ int (*wait_comp)(struct bnx2x *bp,
+ struct bnx2x_rx_mode_ramrod_params *p);
+};
+
+/********************** Set multicast group ***********************************/
+
+struct bnx2x_mcast_list_elem {
+ struct list_head link;
+ u8 *mac;
+};
+
+union bnx2x_mcast_config_data {
+ u8 *mac;
+ u8 bin; /* used in a RESTORE flow */
+};
+
+struct bnx2x_mcast_ramrod_params {
+ struct bnx2x_mcast_obj *mcast_obj;
+
+ /* Relevant options are RAMROD_COMP_WAIT and RAMROD_DRV_CLR_ONLY */
+ unsigned long ramrod_flags;
+
+ struct list_head mcast_list; /* list of struct bnx2x_mcast_list_elem */
+ /** TODO:
+ * - rename it to macs_num.
+ * - Add a new command type for handling pending commands
+ * (remove "zero semantics").
+ *
+ * Length of mcast_list. If zero and ADD_CONT command - post
+ * pending commands.
+ */
+ int mcast_list_len;
+};
+
+enum {
+ BNX2X_MCAST_CMD_ADD,
+ BNX2X_MCAST_CMD_CONT,
+ BNX2X_MCAST_CMD_DEL,
+ BNX2X_MCAST_CMD_RESTORE,
+};
+
+struct bnx2x_mcast_obj {
+ struct bnx2x_raw_obj raw;
+
+ union {
+ struct {
+ #define BNX2X_MCAST_BINS_NUM 256
+ #define BNX2X_MCAST_VEC_SZ (BNX2X_MCAST_BINS_NUM / 64)
+ u64 vec[BNX2X_MCAST_VEC_SZ];
+
+ /** Number of BINs to clear. Should be updated
+ * immediately when a command arrives in order to
+ * properly create DEL commands.
+ */
+ int num_bins_set;
+ } aprox_match;
+
+ struct {
+ struct list_head macs;
+ int num_macs_set;
+ } exact_match;
+ } registry;
+
+ /* Pending commands */
+ struct list_head pending_cmds_head;
+
+ /* A state that is set in raw.pstate, when there are pending commands */
+ int sched_state;
+
+ /* Maximal number of mcast MACs configured in one command */
+ int max_cmd_len;
+
+ /* Total number of currently pending MACs to configure: both
+ * in the pending commands list and in the current command.
+ */
+ int total_pending_num;
+
+ u8 engine_id;
+
+ /**
+ * @param cmd command to execute (BNX2X_MCAST_CMD_X, see above)
+ */
+ int (*config_mcast)(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p, int cmd);
+
+ /**
+ * Fills the ramrod data during the RESTORE flow.
+ *
+ * @param bp
+ * @param o
+ * @param start_idx Registry index to start from
+ * @param rdata_idx Index in the ramrod data to start from
+ *
+ * @return -1 if we handled the whole registry or index of the last
+ * handled registry element.
+ */
+ int (*hdl_restore)(struct bnx2x *bp, struct bnx2x_mcast_obj *o,
+ int start_bin, int *rdata_idx);
+
+ int (*enqueue_cmd)(struct bnx2x *bp, struct bnx2x_mcast_obj *o,
+ struct bnx2x_mcast_ramrod_params *p, int cmd);
+
+ void (*set_one_rule)(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *o, int idx,
+ union bnx2x_mcast_config_data *cfg_data, int cmd);
+
+ /** Checks if there are more mcast MACs to be set or a previous
+ * command is still pending.
+ */
+ bool (*check_pending)(struct bnx2x_mcast_obj *o);
+
+ /**
+ * Set/Clear/Check SCHEDULED state of the object
+ */
+ void (*set_sched)(struct bnx2x_mcast_obj *o);
+ void (*clear_sched)(struct bnx2x_mcast_obj *o);
+ bool (*check_sched)(struct bnx2x_mcast_obj *o);
+
+ /* Wait until all pending commands complete */
+ int (*wait_comp)(struct bnx2x *bp, struct bnx2x_mcast_obj *o);
+
+ /**
+ * Handle the internal object counters needed for proper
+ * commands handling. Checks that the provided parameters are
+ * feasible.
+ */
+ int (*validate)(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p, int cmd);
+
+ /**
+ * Restore the values of internal counters in case of a failure.
+ */
+ void (*revert)(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p,
+ int old_num_bins);
+
+ int (*get_registry_size)(struct bnx2x_mcast_obj *o);
+ void (*set_registry_size)(struct bnx2x_mcast_obj *o, int n);
+};
+
+/*************************** Credit handling **********************************/
+struct bnx2x_credit_pool_obj {
+
+ /* Current amount of credit in the pool */
+ atomic_t credit;
+
+ /* Maximum allowed credit. put() will check against it. */
+ int pool_sz;
+
+ /*
+ * Allocate a pool table statically.
+ *
+ * Currently the mamimum allowed size is MAX_MAC_CREDIT_E2(272)
+ *
+ * The set bit in the table will mean that the entry is available.
+ */
+#define BNX2X_POOL_VEC_SIZE (MAX_MAC_CREDIT_E2 / 64)
+ u64 pool_mirror[BNX2X_POOL_VEC_SIZE];
+
+ /* Base pool offset (initialized differently */
+ int base_pool_offset;
+
+ /**
+ * Get the next free pool entry.
+ *
+ * @return true if there was a free entry in the pool
+ */
+ bool (*get_entry)(struct bnx2x_credit_pool_obj *o, int *entry);
+
+ /**
+ * Return the entry back to the pool.
+ *
+ * @return true if entry is legal and has been successfully
+ * returned to the pool.
+ */
+ bool (*put_entry)(struct bnx2x_credit_pool_obj *o, int entry);
+
+ /**
+ * Get the requested amount of credit from the pool.
+ *
+ * @param cnt Amount of requested credit
+ * @return true if the operation is successful
+ */
+ bool (*get)(struct bnx2x_credit_pool_obj *o, int cnt);
+
+ /**
+ * Returns the credit to the pool.
+ *
+ * @param cnt Amount of credit to return
+ * @return true if the operation is successful
+ */
+ bool (*put)(struct bnx2x_credit_pool_obj *o, int cnt);
+
+ /**
+ * Reads the current amount of credit.
+ */
+ int (*check)(struct bnx2x_credit_pool_obj *o);
+};
+
+/*************************** RSS configuration ********************************/
+enum {
+ /* RSS_MODE bits are mutually exclusive */
+ BNX2X_RSS_MODE_DISABLED,
+ BNX2X_RSS_MODE_REGULAR,
+ BNX2X_RSS_MODE_VLAN_PRI,
+ BNX2X_RSS_MODE_E1HOV_PRI,
+ BNX2X_RSS_MODE_IP_DSCP,
+
+ BNX2X_RSS_SET_SRCH, /* Setup searcher, E1x specific flag */
+
+ BNX2X_RSS_IPV4,
+ BNX2X_RSS_IPV4_TCP,
+ BNX2X_RSS_IPV6,
+ BNX2X_RSS_IPV6_TCP,
+};
+
+struct bnx2x_config_rss_params {
+ struct bnx2x_rss_config_obj *rss_obj;
+
+ /* may have RAMROD_COMP_WAIT set only */
+ unsigned long ramrod_flags;
+
+ /* BNX2X_RSS_X bits */
+ unsigned long rss_flags;
+
+ /* Number hash bits to take into an account */
+ u8 rss_result_mask;
+
+ /* Indirection table */
+ u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE];
+
+ /* RSS hash values */
+ u32 rss_key[10];
+
+ /* valid only iff BNX2X_RSS_UPDATE_TOE is set */
+ u16 toe_rss_bitmap;
+};
+
+struct bnx2x_rss_config_obj {
+ struct bnx2x_raw_obj raw;
+
+ /* RSS engine to use */
+ u8 engine_id;
+
+ /* Last configured indirection table */
+ u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE];
+
+ int (*config_rss)(struct bnx2x *bp,
+ struct bnx2x_config_rss_params *p);
+};
+
+/*********************** Queue state update ***********************************/
+
+/* UPDATE command options */
+enum {
+ BNX2X_Q_UPDATE_IN_VLAN_REM,
+ BNX2X_Q_UPDATE_IN_VLAN_REM_CHNG,
+ BNX2X_Q_UPDATE_OUT_VLAN_REM,
+ BNX2X_Q_UPDATE_OUT_VLAN_REM_CHNG,
+ BNX2X_Q_UPDATE_ANTI_SPOOF,
+ BNX2X_Q_UPDATE_ANTI_SPOOF_CHNG,
+ BNX2X_Q_UPDATE_ACTIVATE,
+ BNX2X_Q_UPDATE_ACTIVATE_CHNG,
+ BNX2X_Q_UPDATE_DEF_VLAN_EN,
+ BNX2X_Q_UPDATE_DEF_VLAN_EN_CHNG,
+ BNX2X_Q_UPDATE_SILENT_VLAN_REM_CHNG,
+ BNX2X_Q_UPDATE_SILENT_VLAN_REM
+};
+
+/* Allowed Queue states */
+enum bnx2x_q_state {
+ BNX2X_Q_STATE_RESET,
+ BNX2X_Q_STATE_INITIALIZED,
+ BNX2X_Q_STATE_ACTIVE,
+ BNX2X_Q_STATE_MULTI_COS,
+ BNX2X_Q_STATE_MCOS_TERMINATED,
+ BNX2X_Q_STATE_INACTIVE,
+ BNX2X_Q_STATE_STOPPED,
+ BNX2X_Q_STATE_TERMINATED,
+ BNX2X_Q_STATE_FLRED,
+ BNX2X_Q_STATE_MAX,
+};
+
+/* Allowed commands */
+enum bnx2x_queue_cmd {
+ BNX2X_Q_CMD_INIT,
+ BNX2X_Q_CMD_SETUP,
+ BNX2X_Q_CMD_SETUP_TX_ONLY,
+ BNX2X_Q_CMD_DEACTIVATE,
+ BNX2X_Q_CMD_ACTIVATE,
+ BNX2X_Q_CMD_UPDATE,
+ BNX2X_Q_CMD_UPDATE_TPA,
+ BNX2X_Q_CMD_HALT,
+ BNX2X_Q_CMD_CFC_DEL,
+ BNX2X_Q_CMD_TERMINATE,
+ BNX2X_Q_CMD_EMPTY,
+ BNX2X_Q_CMD_MAX,
+};
+
+/* queue SETUP + INIT flags */
+enum {
+ BNX2X_Q_FLG_TPA,
+ BNX2X_Q_FLG_TPA_IPV6,
+ BNX2X_Q_FLG_STATS,
+ BNX2X_Q_FLG_ZERO_STATS,
+ BNX2X_Q_FLG_ACTIVE,
+ BNX2X_Q_FLG_OV,
+ BNX2X_Q_FLG_VLAN,
+ BNX2X_Q_FLG_COS,
+ BNX2X_Q_FLG_HC,
+ BNX2X_Q_FLG_HC_EN,
+ BNX2X_Q_FLG_DHC,
+ BNX2X_Q_FLG_FCOE,
+ BNX2X_Q_FLG_LEADING_RSS,
+ BNX2X_Q_FLG_MCAST,
+ BNX2X_Q_FLG_DEF_VLAN,
+ BNX2X_Q_FLG_TX_SWITCH,
+ BNX2X_Q_FLG_TX_SEC,
+ BNX2X_Q_FLG_ANTI_SPOOF,
+ BNX2X_Q_FLG_SILENT_VLAN_REM
+};
+
+/* Queue type options: queue type may be a compination of below. */
+enum bnx2x_q_type {
+ /** TODO: Consider moving both these flags into the init()
+ * ramrod params.
+ */
+ BNX2X_Q_TYPE_HAS_RX,
+ BNX2X_Q_TYPE_HAS_TX,
+};
+
+#define BNX2X_PRIMARY_CID_INDEX 0
+#define BNX2X_MULTI_TX_COS_E1X 1
+#define BNX2X_MULTI_TX_COS_E2_E3A0 2
+#define BNX2X_MULTI_TX_COS_E3B0 3
+#define BNX2X_MULTI_TX_COS BNX2X_MULTI_TX_COS_E3B0
+
+
+struct bnx2x_queue_init_params {
+ struct {
+ unsigned long flags;
+ u16 hc_rate;
+ u8 fw_sb_id;
+ u8 sb_cq_index;
+ } tx;
+
+ struct {
+ unsigned long flags;
+ u16 hc_rate;
+ u8 fw_sb_id;
+ u8 sb_cq_index;
+ } rx;
+
+ /* CID context in the host memory */
+ struct eth_context *cxts[BNX2X_MULTI_TX_COS];
+
+ /* maximum number of cos supported by hardware */
+ u8 max_cos;
+};
+
+struct bnx2x_queue_terminate_params {
+ /* index within the tx_only cids of this queue object */
+ u8 cid_index;
+};
+
+struct bnx2x_queue_cfc_del_params {
+ /* index within the tx_only cids of this queue object */
+ u8 cid_index;
+};
+
+struct bnx2x_queue_update_params {
+ unsigned long update_flags; /* BNX2X_Q_UPDATE_XX bits */
+ u16 def_vlan;
+ u16 silent_removal_value;
+ u16 silent_removal_mask;
+/* index within the tx_only cids of this queue object */
+ u8 cid_index;
+};
+
+struct rxq_pause_params {
+ u16 bd_th_lo;
+ u16 bd_th_hi;
+ u16 rcq_th_lo;
+ u16 rcq_th_hi;
+ u16 sge_th_lo; /* valid iff BNX2X_Q_FLG_TPA */
+ u16 sge_th_hi; /* valid iff BNX2X_Q_FLG_TPA */
+ u16 pri_map;
+};
+
+/* general */
+struct bnx2x_general_setup_params {
+ /* valid iff BNX2X_Q_FLG_STATS */
+ u8 stat_id;
+
+ u8 spcl_id;
+ u16 mtu;
+ u8 cos;
+};
+
+struct bnx2x_rxq_setup_params {
+ /* dma */
+ dma_addr_t dscr_map;
+ dma_addr_t sge_map;
+ dma_addr_t rcq_map;
+ dma_addr_t rcq_np_map;
+
+ u16 drop_flags;
+ u16 buf_sz;
+ u8 fw_sb_id;
+ u8 cl_qzone_id;
+
+ /* valid iff BNX2X_Q_FLG_TPA */
+ u16 tpa_agg_sz;
+ u16 sge_buf_sz;
+ u8 max_sges_pkt;
+ u8 max_tpa_queues;
+ u8 rss_engine_id;
+
+ u8 cache_line_log;
+
+ u8 sb_cq_index;
+
+ /* valid iff BXN2X_Q_FLG_SILENT_VLAN_REM */
+ u16 silent_removal_value;
+ u16 silent_removal_mask;
+};
+
+struct bnx2x_txq_setup_params {
+ /* dma */
+ dma_addr_t dscr_map;
+
+ u8 fw_sb_id;
+ u8 sb_cq_index;
+ u8 cos; /* valid iff BNX2X_Q_FLG_COS */
+ u16 traffic_type;
+ /* equals to the leading rss client id, used for TX classification*/
+ u8 tss_leading_cl_id;
+
+ /* valid iff BNX2X_Q_FLG_DEF_VLAN */
+ u16 default_vlan;
+};
+
+struct bnx2x_queue_setup_params {
+ struct bnx2x_general_setup_params gen_params;
+ struct bnx2x_txq_setup_params txq_params;
+ struct bnx2x_rxq_setup_params rxq_params;
+ struct rxq_pause_params pause_params;
+ unsigned long flags;
+};
+
+struct bnx2x_queue_setup_tx_only_params {
+ struct bnx2x_general_setup_params gen_params;
+ struct bnx2x_txq_setup_params txq_params;
+ unsigned long flags;
+ /* index within the tx_only cids of this queue object */
+ u8 cid_index;
+};
+
+struct bnx2x_queue_state_params {
+ struct bnx2x_queue_sp_obj *q_obj;
+
+ /* Current command */
+ enum bnx2x_queue_cmd cmd;
+
+ /* may have RAMROD_COMP_WAIT set only */
+ unsigned long ramrod_flags;
+
+ /* Params according to the current command */
+ union {
+ struct bnx2x_queue_update_params update;
+ struct bnx2x_queue_setup_params setup;
+ struct bnx2x_queue_init_params init;
+ struct bnx2x_queue_setup_tx_only_params tx_only;
+ struct bnx2x_queue_terminate_params terminate;
+ struct bnx2x_queue_cfc_del_params cfc_del;
+ } params;
+};
+
+struct bnx2x_queue_sp_obj {
+ u32 cids[BNX2X_MULTI_TX_COS];
+ u8 cl_id;
+ u8 func_id;
+
+ /*
+ * number of traffic classes supported by queue.
+ * The primary connection of the queue suppotrs the first traffic
+ * class. Any further traffic class is suppoted by a tx-only
+ * connection.
+ *
+ * Therefore max_cos is also a number of valid entries in the cids
+ * array.
+ */
+ u8 max_cos;
+ u8 num_tx_only, next_tx_only;
+
+ enum bnx2x_q_state state, next_state;
+
+ /* bits from enum bnx2x_q_type */
+ unsigned long type;
+
+ /* BNX2X_Q_CMD_XX bits. This object implements "one
+ * pending" paradigm but for debug and tracing purposes it's
+ * more convinient to have different bits for different
+ * commands.
+ */
+ unsigned long pending;
+
+ /* Buffer to use as a ramrod data and its mapping */
+ void *rdata;
+ dma_addr_t rdata_mapping;
+
+ /**
+ * Performs one state change according to the given parameters.
+ *
+ * @return 0 in case of success and negative value otherwise.
+ */
+ int (*send_cmd)(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params);
+
+ /**
+ * Sets the pending bit according to the requested transition.
+ */
+ int (*set_pending)(struct bnx2x_queue_sp_obj *o,
+ struct bnx2x_queue_state_params *params);
+
+ /**
+ * Checks that the requested state transition is legal.
+ */
+ int (*check_transition)(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *o,
+ struct bnx2x_queue_state_params *params);
+
+ /**
+ * Completes the pending command.
+ */
+ int (*complete_cmd)(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *o,
+ enum bnx2x_queue_cmd);
+
+ int (*wait_comp)(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *o,
+ enum bnx2x_queue_cmd cmd);
+};
+
+/********************** Function state update *********************************/
+/* Allowed Function states */
+enum bnx2x_func_state {
+ BNX2X_F_STATE_RESET,
+ BNX2X_F_STATE_INITIALIZED,
+ BNX2X_F_STATE_STARTED,
+ BNX2X_F_STATE_TX_STOPPED,
+ BNX2X_F_STATE_MAX,
+};
+
+/* Allowed Function commands */
+enum bnx2x_func_cmd {
+ BNX2X_F_CMD_HW_INIT,
+ BNX2X_F_CMD_START,
+ BNX2X_F_CMD_STOP,
+ BNX2X_F_CMD_HW_RESET,
+ BNX2X_F_CMD_TX_STOP,
+ BNX2X_F_CMD_TX_START,
+ BNX2X_F_CMD_MAX,
+};
+
+struct bnx2x_func_hw_init_params {
+ /* A load phase returned by MCP.
+ *
+ * May be:
+ * FW_MSG_CODE_DRV_LOAD_COMMON_CHIP
+ * FW_MSG_CODE_DRV_LOAD_COMMON
+ * FW_MSG_CODE_DRV_LOAD_PORT
+ * FW_MSG_CODE_DRV_LOAD_FUNCTION
+ */
+ u32 load_phase;
+};
+
+struct bnx2x_func_hw_reset_params {
+ /* A load phase returned by MCP.
+ *
+ * May be:
+ * FW_MSG_CODE_DRV_LOAD_COMMON_CHIP
+ * FW_MSG_CODE_DRV_LOAD_COMMON
+ * FW_MSG_CODE_DRV_LOAD_PORT
+ * FW_MSG_CODE_DRV_LOAD_FUNCTION
+ */
+ u32 reset_phase;
+};
+
+struct bnx2x_func_start_params {
+ /* Multi Function mode:
+ * - Single Function
+ * - Switch Dependent
+ * - Switch Independent
+ */
+ u16 mf_mode;
+
+ /* Switch Dependent mode outer VLAN tag */
+ u16 sd_vlan_tag;
+
+ /* Function cos mode */
+ u8 network_cos_mode;
+};
+
+struct bnx2x_func_tx_start_params {
+ struct priority_cos traffic_type_to_priority_cos[MAX_TRAFFIC_TYPES];
+ u8 dcb_enabled;
+ u8 dcb_version;
+ u8 dont_add_pri_0_en;
+};
+
+struct bnx2x_func_state_params {
+ struct bnx2x_func_sp_obj *f_obj;
+
+ /* Current command */
+ enum bnx2x_func_cmd cmd;
+
+ /* may have RAMROD_COMP_WAIT set only */
+ unsigned long ramrod_flags;
+
+ /* Params according to the current command */
+ union {
+ struct bnx2x_func_hw_init_params hw_init;
+ struct bnx2x_func_hw_reset_params hw_reset;
+ struct bnx2x_func_start_params start;
+ struct bnx2x_func_tx_start_params tx_start;
+ } params;
+};
+
+struct bnx2x_func_sp_drv_ops {
+ /* Init tool + runtime initialization:
+ * - Common Chip
+ * - Common (per Path)
+ * - Port
+ * - Function phases
+ */
+ int (*init_hw_cmn_chip)(struct bnx2x *bp);
+ int (*init_hw_cmn)(struct bnx2x *bp);
+ int (*init_hw_port)(struct bnx2x *bp);
+ int (*init_hw_func)(struct bnx2x *bp);
+
+ /* Reset Function HW: Common, Port, Function phases. */
+ void (*reset_hw_cmn)(struct bnx2x *bp);
+ void (*reset_hw_port)(struct bnx2x *bp);
+ void (*reset_hw_func)(struct bnx2x *bp);
+
+ /* Init/Free GUNZIP resources */
+ int (*gunzip_init)(struct bnx2x *bp);
+ void (*gunzip_end)(struct bnx2x *bp);
+
+ /* Prepare/Release FW resources */
+ int (*init_fw)(struct bnx2x *bp);
+ void (*release_fw)(struct bnx2x *bp);
+};
+
+struct bnx2x_func_sp_obj {
+ enum bnx2x_func_state state, next_state;
+
+ /* BNX2X_FUNC_CMD_XX bits. This object implements "one
+ * pending" paradigm but for debug and tracing purposes it's
+ * more convinient to have different bits for different
+ * commands.
+ */
+ unsigned long pending;
+
+ /* Buffer to use as a ramrod data and its mapping */
+ void *rdata;
+ dma_addr_t rdata_mapping;
+
+ /* this mutex validates that when pending flag is taken, the next
+ * ramrod to be sent will be the one set the pending bit
+ */
+ struct mutex one_pending_mutex;
+
+ /* Driver interface */
+ struct bnx2x_func_sp_drv_ops *drv;
+
+ /**
+ * Performs one state change according to the given parameters.
+ *
+ * @return 0 in case of success and negative value otherwise.
+ */
+ int (*send_cmd)(struct bnx2x *bp,
+ struct bnx2x_func_state_params *params);
+
+ /**
+ * Checks that the requested state transition is legal.
+ */
+ int (*check_transition)(struct bnx2x *bp,
+ struct bnx2x_func_sp_obj *o,
+ struct bnx2x_func_state_params *params);
+
+ /**
+ * Completes the pending command.
+ */
+ int (*complete_cmd)(struct bnx2x *bp,
+ struct bnx2x_func_sp_obj *o,
+ enum bnx2x_func_cmd cmd);
+
+ int (*wait_comp)(struct bnx2x *bp, struct bnx2x_func_sp_obj *o,
+ enum bnx2x_func_cmd cmd);
+};
+
+/********************** Interfaces ********************************************/
+/* Queueable objects set */
+union bnx2x_qable_obj {
+ struct bnx2x_vlan_mac_obj vlan_mac;
+};
+/************** Function state update *********/
+void bnx2x_init_func_obj(struct bnx2x *bp,
+ struct bnx2x_func_sp_obj *obj,
+ void *rdata, dma_addr_t rdata_mapping,
+ struct bnx2x_func_sp_drv_ops *drv_iface);
+
+int bnx2x_func_state_change(struct bnx2x *bp,
+ struct bnx2x_func_state_params *params);
+
+enum bnx2x_func_state bnx2x_func_get_state(struct bnx2x *bp,
+ struct bnx2x_func_sp_obj *o);
+/******************* Queue State **************/
+void bnx2x_init_queue_obj(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *obj, u8 cl_id, u32 *cids,
+ u8 cid_cnt, u8 func_id, void *rdata,
+ dma_addr_t rdata_mapping, unsigned long type);
+
+int bnx2x_queue_state_change(struct bnx2x *bp,
+ struct bnx2x_queue_state_params *params);
+
+/********************* VLAN-MAC ****************/
+void bnx2x_init_mac_obj(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *mac_obj,
+ u8 cl_id, u32 cid, u8 func_id, void *rdata,
+ dma_addr_t rdata_mapping, int state,
+ unsigned long *pstate, bnx2x_obj_type type,
+ struct bnx2x_credit_pool_obj *macs_pool);
+
+void bnx2x_init_vlan_obj(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *vlan_obj,
+ u8 cl_id, u32 cid, u8 func_id, void *rdata,
+ dma_addr_t rdata_mapping, int state,
+ unsigned long *pstate, bnx2x_obj_type type,
+ struct bnx2x_credit_pool_obj *vlans_pool);
+
+void bnx2x_init_vlan_mac_obj(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *vlan_mac_obj,
+ u8 cl_id, u32 cid, u8 func_id, void *rdata,
+ dma_addr_t rdata_mapping, int state,
+ unsigned long *pstate, bnx2x_obj_type type,
+ struct bnx2x_credit_pool_obj *macs_pool,
+ struct bnx2x_credit_pool_obj *vlans_pool);
+
+int bnx2x_config_vlan_mac(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_ramrod_params *p);
+
+int bnx2x_vlan_mac_move(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_ramrod_params *p,
+ struct bnx2x_vlan_mac_obj *dest_o);
+
+/********************* RX MODE ****************/
+
+void bnx2x_init_rx_mode_obj(struct bnx2x *bp,
+ struct bnx2x_rx_mode_obj *o);
+
+/**
+ * Send and RX_MODE ramrod according to the provided parameters.
+ *
+ * @param bp
+ * @param p Command parameters
+ *
+ * @return 0 - if operation was successfull and there is no pending completions,
+ * positive number - if there are pending completions,
+ * negative - if there were errors
+ */
+int bnx2x_config_rx_mode(struct bnx2x *bp,
+ struct bnx2x_rx_mode_ramrod_params *p);
+
+/****************** MULTICASTS ****************/
+
+void bnx2x_init_mcast_obj(struct bnx2x *bp,
+ struct bnx2x_mcast_obj *mcast_obj,
+ u8 mcast_cl_id, u32 mcast_cid, u8 func_id,
+ u8 engine_id, void *rdata, dma_addr_t rdata_mapping,
+ int state, unsigned long *pstate,
+ bnx2x_obj_type type);
+
+/**
+ * Configure multicast MACs list. May configure a new list
+ * provided in p->mcast_list (BNX2X_MCAST_CMD_ADD), clean up
+ * (BNX2X_MCAST_CMD_DEL) or restore (BNX2X_MCAST_CMD_RESTORE) a current
+ * configuration, continue to execute the pending commands
+ * (BNX2X_MCAST_CMD_CONT).
+ *
+ * If previous command is still pending or if number of MACs to
+ * configure is more that maximum number of MACs in one command,
+ * the current command will be enqueued to the tail of the
+ * pending commands list.
+ *
+ * @param bp
+ * @param p
+ * @param command to execute: BNX2X_MCAST_CMD_X
+ *
+ * @return 0 is operation was sucessfull and there are no pending completions,
+ * negative if there were errors, positive if there are pending
+ * completions.
+ */
+int bnx2x_config_mcast(struct bnx2x *bp,
+ struct bnx2x_mcast_ramrod_params *p, int cmd);
+
+/****************** CREDIT POOL ****************/
+void bnx2x_init_mac_credit_pool(struct bnx2x *bp,
+ struct bnx2x_credit_pool_obj *p, u8 func_id,
+ u8 func_num);
+void bnx2x_init_vlan_credit_pool(struct bnx2x *bp,
+ struct bnx2x_credit_pool_obj *p, u8 func_id,
+ u8 func_num);
+
+
+/****************** RSS CONFIGURATION ****************/
+void bnx2x_init_rss_config_obj(struct bnx2x *bp,
+ struct bnx2x_rss_config_obj *rss_obj,
+ u8 cl_id, u32 cid, u8 func_id, u8 engine_id,
+ void *rdata, dma_addr_t rdata_mapping,
+ int state, unsigned long *pstate,
+ bnx2x_obj_type type);
+
+/**
+ * Updates RSS configuration according to provided parameters.
+ *
+ * @param bp
+ * @param p
+ *
+ * @return 0 in case of success
+ */
+int bnx2x_config_rss(struct bnx2x *bp,
+ struct bnx2x_config_rss_params *p);
+
+/**
+ * Return the current ind_table configuration.
+ *
+ * @param bp
+ * @param ind_table buffer to fill with the current indirection
+ * table content. Should be at least
+ * T_ETH_INDIRECTION_TABLE_SIZE bytes long.
+ */
+void bnx2x_get_rss_ind_table(struct bnx2x_rss_config_obj *rss_obj,
+ u8 *ind_table);
+
+#endif /* BNX2X_SP_VERBS */
diff --git a/drivers/net/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
index e535bfa08945..02ac6a771bf9 100644
--- a/drivers/net/bnx2x/bnx2x_stats.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
@@ -14,120 +14,14 @@
* Statistics and Link management by Yitchak Gertner
*
*/
-#include "bnx2x_cmn.h"
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "bnx2x_stats.h"
+#include "bnx2x_cmn.h"
-/* Statistics */
-/****************************************************************************
-* Macros
-****************************************************************************/
-
-/* sum[hi:lo] += add[hi:lo] */
-#define ADD_64(s_hi, a_hi, s_lo, a_lo) \
- do { \
- s_lo += a_lo; \
- s_hi += a_hi + ((s_lo < a_lo) ? 1 : 0); \
- } while (0)
-
-/* difference = minuend - subtrahend */
-#define DIFF_64(d_hi, m_hi, s_hi, d_lo, m_lo, s_lo) \
- do { \
- if (m_lo < s_lo) { \
- /* underflow */ \
- d_hi = m_hi - s_hi; \
- if (d_hi > 0) { \
- /* we can 'loan' 1 */ \
- d_hi--; \
- d_lo = m_lo + (UINT_MAX - s_lo) + 1; \
- } else { \
- /* m_hi <= s_hi */ \
- d_hi = 0; \
- d_lo = 0; \
- } \
- } else { \
- /* m_lo >= s_lo */ \
- if (m_hi < s_hi) { \
- d_hi = 0; \
- d_lo = 0; \
- } else { \
- /* m_hi >= s_hi */ \
- d_hi = m_hi - s_hi; \
- d_lo = m_lo - s_lo; \
- } \
- } \
- } while (0)
-
-#define UPDATE_STAT64(s, t) \
- do { \
- DIFF_64(diff.hi, new->s##_hi, pstats->mac_stx[0].t##_hi, \
- diff.lo, new->s##_lo, pstats->mac_stx[0].t##_lo); \
- pstats->mac_stx[0].t##_hi = new->s##_hi; \
- pstats->mac_stx[0].t##_lo = new->s##_lo; \
- ADD_64(pstats->mac_stx[1].t##_hi, diff.hi, \
- pstats->mac_stx[1].t##_lo, diff.lo); \
- } while (0)
-
-#define UPDATE_STAT64_NIG(s, t) \
- do { \
- DIFF_64(diff.hi, new->s##_hi, old->s##_hi, \
- diff.lo, new->s##_lo, old->s##_lo); \
- ADD_64(estats->t##_hi, diff.hi, \
- estats->t##_lo, diff.lo); \
- } while (0)
-
-/* sum[hi:lo] += add */
-#define ADD_EXTEND_64(s_hi, s_lo, a) \
- do { \
- s_lo += a; \
- s_hi += (s_lo < a) ? 1 : 0; \
- } while (0)
-
-#define UPDATE_EXTEND_STAT(s) \
- do { \
- ADD_EXTEND_64(pstats->mac_stx[1].s##_hi, \
- pstats->mac_stx[1].s##_lo, \
- new->s); \
- } while (0)
-
-#define UPDATE_EXTEND_TSTAT(s, t) \
- do { \
- diff = le32_to_cpu(tclient->s) - le32_to_cpu(old_tclient->s); \
- old_tclient->s = tclient->s; \
- ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \
- } while (0)
-
-#define UPDATE_EXTEND_USTAT(s, t) \
- do { \
- diff = le32_to_cpu(uclient->s) - le32_to_cpu(old_uclient->s); \
- old_uclient->s = uclient->s; \
- ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \
- } while (0)
-
-#define UPDATE_EXTEND_XSTAT(s, t) \
- do { \
- diff = le32_to_cpu(xclient->s) - le32_to_cpu(old_xclient->s); \
- old_xclient->s = xclient->s; \
- ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \
- } while (0)
-
-/* minuend -= subtrahend */
-#define SUB_64(m_hi, s_hi, m_lo, s_lo) \
- do { \
- DIFF_64(m_hi, m_hi, s_hi, m_lo, m_lo, s_lo); \
- } while (0)
-
-/* minuend[hi:lo] -= subtrahend */
-#define SUB_EXTEND_64(m_hi, m_lo, s) \
- do { \
- SUB_64(m_hi, 0, m_lo, s); \
- } while (0)
-
-#define SUB_EXTEND_USTAT(s, t) \
- do { \
- diff = le32_to_cpu(uclient->s) - le32_to_cpu(old_uclient->s); \
- SUB_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \
- } while (0)
+/* Statistics */
/*
* General service functions
@@ -149,12 +43,16 @@ static inline long bnx2x_hilo(u32 *hiref)
* Init service functions
*/
-
+/* Post the next statistics ramrod. Protect it with the spin in
+ * order to ensure the strict order between statistics ramrods
+ * (each ramrod has a sequence number passed in a
+ * bp->fw_stats_req->hdr.drv_stats_counter and ramrods must be
+ * sent in order).
+ */
static void bnx2x_storm_stats_post(struct bnx2x *bp)
{
if (!bp->stats_pending) {
- struct common_query_ramrod_data ramrod_data = {0};
- int i, rc;
+ int rc;
spin_lock_bh(&bp->stats_lock);
@@ -163,14 +61,19 @@ static void bnx2x_storm_stats_post(struct bnx2x *bp)
return;
}
- ramrod_data.drv_counter = bp->stats_counter++;
- ramrod_data.collect_port = bp->port.pmf ? 1 : 0;
- for_each_eth_queue(bp, i)
- ramrod_data.ctr_id_vector |= (1 << bp->fp[i].cl_id);
+ bp->fw_stats_req->hdr.drv_stats_counter =
+ cpu_to_le16(bp->stats_counter++);
+
+ DP(NETIF_MSG_TIMER, "Sending statistics ramrod %d\n",
+ bp->fw_stats_req->hdr.drv_stats_counter);
+
+
+ /* send FW stats ramrod */
rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STAT_QUERY, 0,
- ((u32 *)&ramrod_data)[1],
- ((u32 *)&ramrod_data)[0], 1);
+ U64_HI(bp->fw_stats_req_mapping),
+ U64_LO(bp->fw_stats_req_mapping),
+ NONE_CONNECTION_TYPE);
if (rc == 0)
bp->stats_pending = 1;
@@ -230,7 +133,7 @@ static int bnx2x_stats_comp(struct bnx2x *bp)
break;
}
cnt--;
- msleep(1);
+ usleep_range(1000, 1000);
}
return 1;
}
@@ -338,69 +241,8 @@ static void bnx2x_port_stats_init(struct bnx2x *bp)
opcode = bnx2x_dmae_opcode(bp, DMAE_SRC_GRC, DMAE_DST_PCI,
true, DMAE_COMP_GRC);
- if (bp->link_vars.mac_type == MAC_TYPE_BMAC) {
-
- mac_addr = (port ? NIG_REG_INGRESS_BMAC1_MEM :
- NIG_REG_INGRESS_BMAC0_MEM);
-
- /* BIGMAC_REGISTER_TX_STAT_GTPKT ..
- BIGMAC_REGISTER_TX_STAT_GTBYT */
- dmae = bnx2x_sp(bp, dmae[bp->executer_idx++]);
- dmae->opcode = opcode;
- if (CHIP_IS_E1x(bp)) {
- dmae->src_addr_lo = (mac_addr +
- BIGMAC_REGISTER_TX_STAT_GTPKT) >> 2;
- dmae->len = (8 + BIGMAC_REGISTER_TX_STAT_GTBYT -
- BIGMAC_REGISTER_TX_STAT_GTPKT) >> 2;
- } else {
- dmae->src_addr_lo = (mac_addr +
- BIGMAC2_REGISTER_TX_STAT_GTPOK) >> 2;
- dmae->len = (8 + BIGMAC2_REGISTER_TX_STAT_GTBYT -
- BIGMAC2_REGISTER_TX_STAT_GTPOK) >> 2;
- }
-
- dmae->src_addr_hi = 0;
- dmae->dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, mac_stats));
- dmae->dst_addr_hi = U64_HI(bnx2x_sp_mapping(bp, mac_stats));
- dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2;
- dmae->comp_addr_hi = 0;
- dmae->comp_val = 1;
-
- /* BIGMAC_REGISTER_RX_STAT_GR64 ..
- BIGMAC_REGISTER_RX_STAT_GRIPJ */
- dmae = bnx2x_sp(bp, dmae[bp->executer_idx++]);
- dmae->opcode = opcode;
- dmae->src_addr_hi = 0;
- if (CHIP_IS_E1x(bp)) {
- dmae->src_addr_lo = (mac_addr +
- BIGMAC_REGISTER_RX_STAT_GR64) >> 2;
- dmae->dst_addr_lo =
- U64_LO(bnx2x_sp_mapping(bp, mac_stats) +
- offsetof(struct bmac1_stats, rx_stat_gr64_lo));
- dmae->dst_addr_hi =
- U64_HI(bnx2x_sp_mapping(bp, mac_stats) +
- offsetof(struct bmac1_stats, rx_stat_gr64_lo));
- dmae->len = (8 + BIGMAC_REGISTER_RX_STAT_GRIPJ -
- BIGMAC_REGISTER_RX_STAT_GR64) >> 2;
- } else {
- dmae->src_addr_lo =
- (mac_addr + BIGMAC2_REGISTER_RX_STAT_GR64) >> 2;
- dmae->dst_addr_lo =
- U64_LO(bnx2x_sp_mapping(bp, mac_stats) +
- offsetof(struct bmac2_stats, rx_stat_gr64_lo));
- dmae->dst_addr_hi =
- U64_HI(bnx2x_sp_mapping(bp, mac_stats) +
- offsetof(struct bmac2_stats, rx_stat_gr64_lo));
- dmae->len = (8 + BIGMAC2_REGISTER_RX_STAT_GRIPJ -
- BIGMAC2_REGISTER_RX_STAT_GR64) >> 2;
- }
-
- dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2;
- dmae->comp_addr_hi = 0;
- dmae->comp_val = 1;
-
- } else if (bp->link_vars.mac_type == MAC_TYPE_EMAC) {
-
+ /* EMAC is special */
+ if (bp->link_vars.mac_type == MAC_TYPE_EMAC) {
mac_addr = (port ? GRCBASE_EMAC1 : GRCBASE_EMAC0);
/* EMAC_REG_EMAC_RX_STAT_AC (EMAC_REG_EMAC_RX_STAT_AC_COUNT)*/
@@ -445,46 +287,122 @@ static void bnx2x_port_stats_init(struct bnx2x *bp)
dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2;
dmae->comp_addr_hi = 0;
dmae->comp_val = 1;
+ } else {
+ u32 tx_src_addr_lo, rx_src_addr_lo;
+ u16 rx_len, tx_len;
+
+ /* configure the params according to MAC type */
+ switch (bp->link_vars.mac_type) {
+ case MAC_TYPE_BMAC:
+ mac_addr = (port ? NIG_REG_INGRESS_BMAC1_MEM :
+ NIG_REG_INGRESS_BMAC0_MEM);
+
+ /* BIGMAC_REGISTER_TX_STAT_GTPKT ..
+ BIGMAC_REGISTER_TX_STAT_GTBYT */
+ if (CHIP_IS_E1x(bp)) {
+ tx_src_addr_lo = (mac_addr +
+ BIGMAC_REGISTER_TX_STAT_GTPKT) >> 2;
+ tx_len = (8 + BIGMAC_REGISTER_TX_STAT_GTBYT -
+ BIGMAC_REGISTER_TX_STAT_GTPKT) >> 2;
+ rx_src_addr_lo = (mac_addr +
+ BIGMAC_REGISTER_RX_STAT_GR64) >> 2;
+ rx_len = (8 + BIGMAC_REGISTER_RX_STAT_GRIPJ -
+ BIGMAC_REGISTER_RX_STAT_GR64) >> 2;
+ } else {
+ tx_src_addr_lo = (mac_addr +
+ BIGMAC2_REGISTER_TX_STAT_GTPOK) >> 2;
+ tx_len = (8 + BIGMAC2_REGISTER_TX_STAT_GTBYT -
+ BIGMAC2_REGISTER_TX_STAT_GTPOK) >> 2;
+ rx_src_addr_lo = (mac_addr +
+ BIGMAC2_REGISTER_RX_STAT_GR64) >> 2;
+ rx_len = (8 + BIGMAC2_REGISTER_RX_STAT_GRIPJ -
+ BIGMAC2_REGISTER_RX_STAT_GR64) >> 2;
+ }
+ break;
+
+ case MAC_TYPE_UMAC: /* handled by MSTAT */
+ case MAC_TYPE_XMAC: /* handled by MSTAT */
+ default:
+ mac_addr = port ? GRCBASE_MSTAT1 : GRCBASE_MSTAT0;
+ tx_src_addr_lo = (mac_addr +
+ MSTAT_REG_TX_STAT_GTXPOK_LO) >> 2;
+ rx_src_addr_lo = (mac_addr +
+ MSTAT_REG_RX_STAT_GR64_LO) >> 2;
+ tx_len = sizeof(bp->slowpath->
+ mac_stats.mstat_stats.stats_tx) >> 2;
+ rx_len = sizeof(bp->slowpath->
+ mac_stats.mstat_stats.stats_rx) >> 2;
+ break;
+ }
+
+ /* TX stats */
+ dmae = bnx2x_sp(bp, dmae[bp->executer_idx++]);
+ dmae->opcode = opcode;
+ dmae->src_addr_lo = tx_src_addr_lo;
+ dmae->src_addr_hi = 0;
+ dmae->len = tx_len;
+ dmae->dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, mac_stats));
+ dmae->dst_addr_hi = U64_HI(bnx2x_sp_mapping(bp, mac_stats));
+ dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2;
+ dmae->comp_addr_hi = 0;
+ dmae->comp_val = 1;
+
+ /* RX stats */
+ dmae = bnx2x_sp(bp, dmae[bp->executer_idx++]);
+ dmae->opcode = opcode;
+ dmae->src_addr_hi = 0;
+ dmae->src_addr_lo = rx_src_addr_lo;
+ dmae->dst_addr_lo =
+ U64_LO(bnx2x_sp_mapping(bp, mac_stats) + (tx_len << 2));
+ dmae->dst_addr_hi =
+ U64_HI(bnx2x_sp_mapping(bp, mac_stats) + (tx_len << 2));
+ dmae->len = rx_len;
+ dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2;
+ dmae->comp_addr_hi = 0;
+ dmae->comp_val = 1;
}
/* NIG */
+ if (!CHIP_IS_E3(bp)) {
+ dmae = bnx2x_sp(bp, dmae[bp->executer_idx++]);
+ dmae->opcode = opcode;
+ dmae->src_addr_lo = (port ? NIG_REG_STAT1_EGRESS_MAC_PKT0 :
+ NIG_REG_STAT0_EGRESS_MAC_PKT0) >> 2;
+ dmae->src_addr_hi = 0;
+ dmae->dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, nig_stats) +
+ offsetof(struct nig_stats, egress_mac_pkt0_lo));
+ dmae->dst_addr_hi = U64_HI(bnx2x_sp_mapping(bp, nig_stats) +
+ offsetof(struct nig_stats, egress_mac_pkt0_lo));
+ dmae->len = (2*sizeof(u32)) >> 2;
+ dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2;
+ dmae->comp_addr_hi = 0;
+ dmae->comp_val = 1;
+
+ dmae = bnx2x_sp(bp, dmae[bp->executer_idx++]);
+ dmae->opcode = opcode;
+ dmae->src_addr_lo = (port ? NIG_REG_STAT1_EGRESS_MAC_PKT1 :
+ NIG_REG_STAT0_EGRESS_MAC_PKT1) >> 2;
+ dmae->src_addr_hi = 0;
+ dmae->dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, nig_stats) +
+ offsetof(struct nig_stats, egress_mac_pkt1_lo));
+ dmae->dst_addr_hi = U64_HI(bnx2x_sp_mapping(bp, nig_stats) +
+ offsetof(struct nig_stats, egress_mac_pkt1_lo));
+ dmae->len = (2*sizeof(u32)) >> 2;
+ dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2;
+ dmae->comp_addr_hi = 0;
+ dmae->comp_val = 1;
+ }
+
dmae = bnx2x_sp(bp, dmae[bp->executer_idx++]);
- dmae->opcode = opcode;
+ dmae->opcode = bnx2x_dmae_opcode(bp, DMAE_SRC_GRC, DMAE_DST_PCI,
+ true, DMAE_COMP_PCI);
dmae->src_addr_lo = (port ? NIG_REG_STAT1_BRB_DISCARD :
NIG_REG_STAT0_BRB_DISCARD) >> 2;
dmae->src_addr_hi = 0;
dmae->dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, nig_stats));
dmae->dst_addr_hi = U64_HI(bnx2x_sp_mapping(bp, nig_stats));
dmae->len = (sizeof(struct nig_stats) - 4*sizeof(u32)) >> 2;
- dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2;
- dmae->comp_addr_hi = 0;
- dmae->comp_val = 1;
- dmae = bnx2x_sp(bp, dmae[bp->executer_idx++]);
- dmae->opcode = opcode;
- dmae->src_addr_lo = (port ? NIG_REG_STAT1_EGRESS_MAC_PKT0 :
- NIG_REG_STAT0_EGRESS_MAC_PKT0) >> 2;
- dmae->src_addr_hi = 0;
- dmae->dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, nig_stats) +
- offsetof(struct nig_stats, egress_mac_pkt0_lo));
- dmae->dst_addr_hi = U64_HI(bnx2x_sp_mapping(bp, nig_stats) +
- offsetof(struct nig_stats, egress_mac_pkt0_lo));
- dmae->len = (2*sizeof(u32)) >> 2;
- dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2;
- dmae->comp_addr_hi = 0;
- dmae->comp_val = 1;
-
- dmae = bnx2x_sp(bp, dmae[bp->executer_idx++]);
- dmae->opcode = bnx2x_dmae_opcode(bp, DMAE_SRC_GRC, DMAE_DST_PCI,
- true, DMAE_COMP_PCI);
- dmae->src_addr_lo = (port ? NIG_REG_STAT1_EGRESS_MAC_PKT1 :
- NIG_REG_STAT0_EGRESS_MAC_PKT1) >> 2;
- dmae->src_addr_hi = 0;
- dmae->dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, nig_stats) +
- offsetof(struct nig_stats, egress_mac_pkt1_lo));
- dmae->dst_addr_hi = U64_HI(bnx2x_sp_mapping(bp, nig_stats) +
- offsetof(struct nig_stats, egress_mac_pkt1_lo));
- dmae->len = (2*sizeof(u32)) >> 2;
dmae->comp_addr_lo = U64_LO(bnx2x_sp_mapping(bp, stats_comp));
dmae->comp_addr_hi = U64_HI(bnx2x_sp_mapping(bp, stats_comp));
dmae->comp_val = DMAE_COMP_VAL;
@@ -566,7 +484,8 @@ static void bnx2x_bmac_stats_update(struct bnx2x *bp)
UPDATE_STAT64(rx_stat_grjbr, rx_stat_etherstatsjabbers);
UPDATE_STAT64(rx_stat_grxcf, rx_stat_maccontrolframesreceived);
UPDATE_STAT64(rx_stat_grxpf, rx_stat_xoffstateentered);
- UPDATE_STAT64(rx_stat_grxpf, rx_stat_bmac_xpf);
+ UPDATE_STAT64(rx_stat_grxpf, rx_stat_mac_xpf);
+
UPDATE_STAT64(tx_stat_gtxpf, tx_stat_outxoffsent);
UPDATE_STAT64(tx_stat_gtxpf, tx_stat_flowcontroldone);
UPDATE_STAT64(tx_stat_gt64, tx_stat_etherstatspkts64octets);
@@ -580,13 +499,13 @@ static void bnx2x_bmac_stats_update(struct bnx2x *bp)
tx_stat_etherstatspkts512octetsto1023octets);
UPDATE_STAT64(tx_stat_gt1518,
tx_stat_etherstatspkts1024octetsto1522octets);
- UPDATE_STAT64(tx_stat_gt2047, tx_stat_bmac_2047);
- UPDATE_STAT64(tx_stat_gt4095, tx_stat_bmac_4095);
- UPDATE_STAT64(tx_stat_gt9216, tx_stat_bmac_9216);
- UPDATE_STAT64(tx_stat_gt16383, tx_stat_bmac_16383);
+ UPDATE_STAT64(tx_stat_gt2047, tx_stat_mac_2047);
+ UPDATE_STAT64(tx_stat_gt4095, tx_stat_mac_4095);
+ UPDATE_STAT64(tx_stat_gt9216, tx_stat_mac_9216);
+ UPDATE_STAT64(tx_stat_gt16383, tx_stat_mac_16383);
UPDATE_STAT64(tx_stat_gterr,
tx_stat_dot3statsinternalmactransmiterrors);
- UPDATE_STAT64(tx_stat_gtufl, tx_stat_bmac_ufl);
+ UPDATE_STAT64(tx_stat_gtufl, tx_stat_mac_ufl);
} else {
struct bmac2_stats *new = bnx2x_sp(bp, mac_stats.bmac2_stats);
@@ -600,7 +519,7 @@ static void bnx2x_bmac_stats_update(struct bnx2x *bp)
UPDATE_STAT64(rx_stat_grjbr, rx_stat_etherstatsjabbers);
UPDATE_STAT64(rx_stat_grxcf, rx_stat_maccontrolframesreceived);
UPDATE_STAT64(rx_stat_grxpf, rx_stat_xoffstateentered);
- UPDATE_STAT64(rx_stat_grxpf, rx_stat_bmac_xpf);
+ UPDATE_STAT64(rx_stat_grxpf, rx_stat_mac_xpf);
UPDATE_STAT64(tx_stat_gtxpf, tx_stat_outxoffsent);
UPDATE_STAT64(tx_stat_gtxpf, tx_stat_flowcontroldone);
UPDATE_STAT64(tx_stat_gt64, tx_stat_etherstatspkts64octets);
@@ -614,19 +533,96 @@ static void bnx2x_bmac_stats_update(struct bnx2x *bp)
tx_stat_etherstatspkts512octetsto1023octets);
UPDATE_STAT64(tx_stat_gt1518,
tx_stat_etherstatspkts1024octetsto1522octets);
- UPDATE_STAT64(tx_stat_gt2047, tx_stat_bmac_2047);
- UPDATE_STAT64(tx_stat_gt4095, tx_stat_bmac_4095);
- UPDATE_STAT64(tx_stat_gt9216, tx_stat_bmac_9216);
- UPDATE_STAT64(tx_stat_gt16383, tx_stat_bmac_16383);
+ UPDATE_STAT64(tx_stat_gt2047, tx_stat_mac_2047);
+ UPDATE_STAT64(tx_stat_gt4095, tx_stat_mac_4095);
+ UPDATE_STAT64(tx_stat_gt9216, tx_stat_mac_9216);
+ UPDATE_STAT64(tx_stat_gt16383, tx_stat_mac_16383);
UPDATE_STAT64(tx_stat_gterr,
tx_stat_dot3statsinternalmactransmiterrors);
- UPDATE_STAT64(tx_stat_gtufl, tx_stat_bmac_ufl);
+ UPDATE_STAT64(tx_stat_gtufl, tx_stat_mac_ufl);
}
estats->pause_frames_received_hi =
- pstats->mac_stx[1].rx_stat_bmac_xpf_hi;
+ pstats->mac_stx[1].rx_stat_mac_xpf_hi;
estats->pause_frames_received_lo =
- pstats->mac_stx[1].rx_stat_bmac_xpf_lo;
+ pstats->mac_stx[1].rx_stat_mac_xpf_lo;
+
+ estats->pause_frames_sent_hi =
+ pstats->mac_stx[1].tx_stat_outxoffsent_hi;
+ estats->pause_frames_sent_lo =
+ pstats->mac_stx[1].tx_stat_outxoffsent_lo;
+}
+
+static void bnx2x_mstat_stats_update(struct bnx2x *bp)
+{
+ struct host_port_stats *pstats = bnx2x_sp(bp, port_stats);
+ struct bnx2x_eth_stats *estats = &bp->eth_stats;
+
+ struct mstat_stats *new = bnx2x_sp(bp, mac_stats.mstat_stats);
+
+ ADD_STAT64(stats_rx.rx_grerb, rx_stat_ifhcinbadoctets);
+ ADD_STAT64(stats_rx.rx_grfcs, rx_stat_dot3statsfcserrors);
+ ADD_STAT64(stats_rx.rx_grund, rx_stat_etherstatsundersizepkts);
+ ADD_STAT64(stats_rx.rx_grovr, rx_stat_dot3statsframestoolong);
+ ADD_STAT64(stats_rx.rx_grfrg, rx_stat_etherstatsfragments);
+ ADD_STAT64(stats_rx.rx_grxcf, rx_stat_maccontrolframesreceived);
+ ADD_STAT64(stats_rx.rx_grxpf, rx_stat_xoffstateentered);
+ ADD_STAT64(stats_rx.rx_grxpf, rx_stat_mac_xpf);
+ ADD_STAT64(stats_tx.tx_gtxpf, tx_stat_outxoffsent);
+ ADD_STAT64(stats_tx.tx_gtxpf, tx_stat_flowcontroldone);
+
+
+ ADD_STAT64(stats_tx.tx_gt64, tx_stat_etherstatspkts64octets);
+ ADD_STAT64(stats_tx.tx_gt127,
+ tx_stat_etherstatspkts65octetsto127octets);
+ ADD_STAT64(stats_tx.tx_gt255,
+ tx_stat_etherstatspkts128octetsto255octets);
+ ADD_STAT64(stats_tx.tx_gt511,
+ tx_stat_etherstatspkts256octetsto511octets);
+ ADD_STAT64(stats_tx.tx_gt1023,
+ tx_stat_etherstatspkts512octetsto1023octets);
+ ADD_STAT64(stats_tx.tx_gt1518,
+ tx_stat_etherstatspkts1024octetsto1522octets);
+ ADD_STAT64(stats_tx.tx_gt2047, tx_stat_mac_2047);
+
+ ADD_STAT64(stats_tx.tx_gt4095, tx_stat_mac_4095);
+ ADD_STAT64(stats_tx.tx_gt9216, tx_stat_mac_9216);
+ ADD_STAT64(stats_tx.tx_gt16383, tx_stat_mac_16383);
+
+ ADD_STAT64(stats_tx.tx_gterr,
+ tx_stat_dot3statsinternalmactransmiterrors);
+ ADD_STAT64(stats_tx.tx_gtufl, tx_stat_mac_ufl);
+
+ ADD_64(estats->etherstatspkts1024octetsto1522octets_hi,
+ new->stats_tx.tx_gt1518_hi,
+ estats->etherstatspkts1024octetsto1522octets_lo,
+ new->stats_tx.tx_gt1518_lo);
+
+ ADD_64(estats->etherstatspktsover1522octets_hi,
+ new->stats_tx.tx_gt2047_hi,
+ estats->etherstatspktsover1522octets_lo,
+ new->stats_tx.tx_gt2047_lo);
+
+ ADD_64(estats->etherstatspktsover1522octets_hi,
+ new->stats_tx.tx_gt4095_hi,
+ estats->etherstatspktsover1522octets_lo,
+ new->stats_tx.tx_gt4095_lo);
+
+ ADD_64(estats->etherstatspktsover1522octets_hi,
+ new->stats_tx.tx_gt9216_hi,
+ estats->etherstatspktsover1522octets_lo,
+ new->stats_tx.tx_gt9216_lo);
+
+
+ ADD_64(estats->etherstatspktsover1522octets_hi,
+ new->stats_tx.tx_gt16383_hi,
+ estats->etherstatspktsover1522octets_lo,
+ new->stats_tx.tx_gt16383_lo);
+
+ estats->pause_frames_received_hi =
+ pstats->mac_stx[1].rx_stat_mac_xpf_hi;
+ estats->pause_frames_received_lo =
+ pstats->mac_stx[1].rx_stat_mac_xpf_lo;
estats->pause_frames_sent_hi =
pstats->mac_stx[1].tx_stat_outxoffsent_hi;
@@ -702,15 +698,27 @@ static int bnx2x_hw_stats_update(struct bnx2x *bp)
u32 hi;
} diff;
- if (bp->link_vars.mac_type == MAC_TYPE_BMAC)
+ switch (bp->link_vars.mac_type) {
+ case MAC_TYPE_BMAC:
bnx2x_bmac_stats_update(bp);
+ break;
- else if (bp->link_vars.mac_type == MAC_TYPE_EMAC)
+ case MAC_TYPE_EMAC:
bnx2x_emac_stats_update(bp);
+ break;
- else { /* unreached */
- BNX2X_ERR("stats updated by DMAE but no MAC active\n");
+ case MAC_TYPE_UMAC:
+ case MAC_TYPE_XMAC:
+ bnx2x_mstat_stats_update(bp);
+ break;
+
+ case MAC_TYPE_NONE: /* unreached */
+ DP(BNX2X_MSG_STATS,
+ "stats updated by DMAE but no MAC active\n");
return -1;
+
+ default: /* unreached */
+ BNX2X_ERR("Unknown MAC type\n");
}
ADD_EXTEND_64(pstats->brb_drop_hi, pstats->brb_drop_lo,
@@ -718,9 +726,12 @@ static int bnx2x_hw_stats_update(struct bnx2x *bp)
ADD_EXTEND_64(estats->brb_truncate_hi, estats->brb_truncate_lo,
new->brb_truncate - old->brb_truncate);
- UPDATE_STAT64_NIG(egress_mac_pkt0,
+ if (!CHIP_IS_E3(bp)) {
+ UPDATE_STAT64_NIG(egress_mac_pkt0,
etherstatspkts1024octetsto1522octets);
- UPDATE_STAT64_NIG(egress_mac_pkt1, etherstatspktsover1522octets);
+ UPDATE_STAT64_NIG(egress_mac_pkt1,
+ etherstatspktsover1522octets);
+ }
memcpy(old, new, sizeof(struct nig_stats));
@@ -746,11 +757,13 @@ static int bnx2x_hw_stats_update(struct bnx2x *bp)
static int bnx2x_storm_stats_update(struct bnx2x *bp)
{
- struct eth_stats_query *stats = bnx2x_sp(bp, fw_stats);
struct tstorm_per_port_stats *tport =
- &stats->tstorm_common.port_statistics;
+ &bp->fw_stats_data->port.tstorm_port_statistics;
+ struct tstorm_per_pf_stats *tfunc =
+ &bp->fw_stats_data->pf.tstorm_pf_statistics;
struct host_func_stats *fstats = bnx2x_sp(bp, func_stats);
struct bnx2x_eth_stats *estats = &bp->eth_stats;
+ struct stats_counter *counters = &bp->fw_stats_data->storm_counters;
int i;
u16 cur_stats_counter;
@@ -761,6 +774,35 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp)
cur_stats_counter = bp->stats_counter - 1;
spin_unlock_bh(&bp->stats_lock);
+ /* are storm stats valid? */
+ if (le16_to_cpu(counters->xstats_counter) != cur_stats_counter) {
+ DP(BNX2X_MSG_STATS, "stats not updated by xstorm"
+ " xstorm counter (0x%x) != stats_counter (0x%x)\n",
+ le16_to_cpu(counters->xstats_counter), bp->stats_counter);
+ return -EAGAIN;
+ }
+
+ if (le16_to_cpu(counters->ustats_counter) != cur_stats_counter) {
+ DP(BNX2X_MSG_STATS, "stats not updated by ustorm"
+ " ustorm counter (0x%x) != stats_counter (0x%x)\n",
+ le16_to_cpu(counters->ustats_counter), bp->stats_counter);
+ return -EAGAIN;
+ }
+
+ if (le16_to_cpu(counters->cstats_counter) != cur_stats_counter) {
+ DP(BNX2X_MSG_STATS, "stats not updated by cstorm"
+ " cstorm counter (0x%x) != stats_counter (0x%x)\n",
+ le16_to_cpu(counters->cstats_counter), bp->stats_counter);
+ return -EAGAIN;
+ }
+
+ if (le16_to_cpu(counters->tstats_counter) != cur_stats_counter) {
+ DP(BNX2X_MSG_STATS, "stats not updated by tstorm"
+ " tstorm counter (0x%x) != stats_counter (0x%x)\n",
+ le16_to_cpu(counters->tstats_counter), bp->stats_counter);
+ return -EAGAIN;
+ }
+
memcpy(&(fstats->total_bytes_received_hi),
&(bnx2x_sp(bp, func_stats_base)->total_bytes_received_hi),
sizeof(struct host_func_stats) - 2*sizeof(u32));
@@ -770,94 +812,84 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp)
estats->etherstatsoverrsizepkts_lo = 0;
estats->no_buff_discard_hi = 0;
estats->no_buff_discard_lo = 0;
+ estats->total_tpa_aggregations_hi = 0;
+ estats->total_tpa_aggregations_lo = 0;
+ estats->total_tpa_aggregated_frames_hi = 0;
+ estats->total_tpa_aggregated_frames_lo = 0;
+ estats->total_tpa_bytes_hi = 0;
+ estats->total_tpa_bytes_lo = 0;
for_each_eth_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
- int cl_id = fp->cl_id;
- struct tstorm_per_client_stats *tclient =
- &stats->tstorm_common.client_statistics[cl_id];
- struct tstorm_per_client_stats *old_tclient = &fp->old_tclient;
- struct ustorm_per_client_stats *uclient =
- &stats->ustorm_common.client_statistics[cl_id];
- struct ustorm_per_client_stats *old_uclient = &fp->old_uclient;
- struct xstorm_per_client_stats *xclient =
- &stats->xstorm_common.client_statistics[cl_id];
- struct xstorm_per_client_stats *old_xclient = &fp->old_xclient;
+ struct tstorm_per_queue_stats *tclient =
+ &bp->fw_stats_data->queue_stats[i].
+ tstorm_queue_statistics;
+ struct tstorm_per_queue_stats *old_tclient = &fp->old_tclient;
+ struct ustorm_per_queue_stats *uclient =
+ &bp->fw_stats_data->queue_stats[i].
+ ustorm_queue_statistics;
+ struct ustorm_per_queue_stats *old_uclient = &fp->old_uclient;
+ struct xstorm_per_queue_stats *xclient =
+ &bp->fw_stats_data->queue_stats[i].
+ xstorm_queue_statistics;
+ struct xstorm_per_queue_stats *old_xclient = &fp->old_xclient;
struct bnx2x_eth_q_stats *qstats = &fp->eth_q_stats;
u32 diff;
- /* are storm stats valid? */
- if (le16_to_cpu(xclient->stats_counter) != cur_stats_counter) {
- DP(BNX2X_MSG_STATS, "[%d] stats not updated by xstorm"
- " xstorm counter (0x%x) != stats_counter (0x%x)\n",
- i, xclient->stats_counter, cur_stats_counter + 1);
- return -1;
- }
- if (le16_to_cpu(tclient->stats_counter) != cur_stats_counter) {
- DP(BNX2X_MSG_STATS, "[%d] stats not updated by tstorm"
- " tstorm counter (0x%x) != stats_counter (0x%x)\n",
- i, tclient->stats_counter, cur_stats_counter + 1);
- return -2;
- }
- if (le16_to_cpu(uclient->stats_counter) != cur_stats_counter) {
- DP(BNX2X_MSG_STATS, "[%d] stats not updated by ustorm"
- " ustorm counter (0x%x) != stats_counter (0x%x)\n",
- i, uclient->stats_counter, cur_stats_counter + 1);
- return -4;
- }
+ DP(BNX2X_MSG_STATS, "queue[%d]: ucast_sent 0x%x, "
+ "bcast_sent 0x%x mcast_sent 0x%x\n",
+ i, xclient->ucast_pkts_sent,
+ xclient->bcast_pkts_sent, xclient->mcast_pkts_sent);
+ DP(BNX2X_MSG_STATS, "---------------\n");
+
+ qstats->total_broadcast_bytes_received_hi =
+ le32_to_cpu(tclient->rcv_bcast_bytes.hi);
+ qstats->total_broadcast_bytes_received_lo =
+ le32_to_cpu(tclient->rcv_bcast_bytes.lo);
+
+ qstats->total_multicast_bytes_received_hi =
+ le32_to_cpu(tclient->rcv_mcast_bytes.hi);
+ qstats->total_multicast_bytes_received_lo =
+ le32_to_cpu(tclient->rcv_mcast_bytes.lo);
+
+ qstats->total_unicast_bytes_received_hi =
+ le32_to_cpu(tclient->rcv_ucast_bytes.hi);
+ qstats->total_unicast_bytes_received_lo =
+ le32_to_cpu(tclient->rcv_ucast_bytes.lo);
+
+ /*
+ * sum to total_bytes_received all
+ * unicast/multicast/broadcast
+ */
qstats->total_bytes_received_hi =
- le32_to_cpu(tclient->rcv_broadcast_bytes.hi);
+ qstats->total_broadcast_bytes_received_hi;
qstats->total_bytes_received_lo =
- le32_to_cpu(tclient->rcv_broadcast_bytes.lo);
+ qstats->total_broadcast_bytes_received_lo;
ADD_64(qstats->total_bytes_received_hi,
- le32_to_cpu(tclient->rcv_multicast_bytes.hi),
+ qstats->total_multicast_bytes_received_hi,
qstats->total_bytes_received_lo,
- le32_to_cpu(tclient->rcv_multicast_bytes.lo));
+ qstats->total_multicast_bytes_received_lo);
ADD_64(qstats->total_bytes_received_hi,
- le32_to_cpu(tclient->rcv_unicast_bytes.hi),
- qstats->total_bytes_received_lo,
- le32_to_cpu(tclient->rcv_unicast_bytes.lo));
-
- SUB_64(qstats->total_bytes_received_hi,
- le32_to_cpu(uclient->bcast_no_buff_bytes.hi),
- qstats->total_bytes_received_lo,
- le32_to_cpu(uclient->bcast_no_buff_bytes.lo));
-
- SUB_64(qstats->total_bytes_received_hi,
- le32_to_cpu(uclient->mcast_no_buff_bytes.hi),
+ qstats->total_unicast_bytes_received_hi,
qstats->total_bytes_received_lo,
- le32_to_cpu(uclient->mcast_no_buff_bytes.lo));
-
- SUB_64(qstats->total_bytes_received_hi,
- le32_to_cpu(uclient->ucast_no_buff_bytes.hi),
- qstats->total_bytes_received_lo,
- le32_to_cpu(uclient->ucast_no_buff_bytes.lo));
+ qstats->total_unicast_bytes_received_lo);
qstats->valid_bytes_received_hi =
qstats->total_bytes_received_hi;
qstats->valid_bytes_received_lo =
qstats->total_bytes_received_lo;
- qstats->error_bytes_received_hi =
- le32_to_cpu(tclient->rcv_error_bytes.hi);
- qstats->error_bytes_received_lo =
- le32_to_cpu(tclient->rcv_error_bytes.lo);
- ADD_64(qstats->total_bytes_received_hi,
- qstats->error_bytes_received_hi,
- qstats->total_bytes_received_lo,
- qstats->error_bytes_received_lo);
-
- UPDATE_EXTEND_TSTAT(rcv_unicast_pkts,
+ UPDATE_EXTEND_TSTAT(rcv_ucast_pkts,
total_unicast_packets_received);
- UPDATE_EXTEND_TSTAT(rcv_multicast_pkts,
+ UPDATE_EXTEND_TSTAT(rcv_mcast_pkts,
total_multicast_packets_received);
- UPDATE_EXTEND_TSTAT(rcv_broadcast_pkts,
+ UPDATE_EXTEND_TSTAT(rcv_bcast_pkts,
total_broadcast_packets_received);
- UPDATE_EXTEND_TSTAT(packets_too_big_discard,
+ UPDATE_EXTEND_TSTAT(pkts_too_big_discard,
etherstatsoverrsizepkts);
UPDATE_EXTEND_TSTAT(no_buff_discard, no_buff_discard);
@@ -871,30 +903,78 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp)
UPDATE_EXTEND_USTAT(mcast_no_buff_pkts, no_buff_discard);
UPDATE_EXTEND_USTAT(bcast_no_buff_pkts, no_buff_discard);
+ qstats->total_broadcast_bytes_transmitted_hi =
+ le32_to_cpu(xclient->bcast_bytes_sent.hi);
+ qstats->total_broadcast_bytes_transmitted_lo =
+ le32_to_cpu(xclient->bcast_bytes_sent.lo);
+
+ qstats->total_multicast_bytes_transmitted_hi =
+ le32_to_cpu(xclient->mcast_bytes_sent.hi);
+ qstats->total_multicast_bytes_transmitted_lo =
+ le32_to_cpu(xclient->mcast_bytes_sent.lo);
+
+ qstats->total_unicast_bytes_transmitted_hi =
+ le32_to_cpu(xclient->ucast_bytes_sent.hi);
+ qstats->total_unicast_bytes_transmitted_lo =
+ le32_to_cpu(xclient->ucast_bytes_sent.lo);
+ /*
+ * sum to total_bytes_transmitted all
+ * unicast/multicast/broadcast
+ */
qstats->total_bytes_transmitted_hi =
- le32_to_cpu(xclient->unicast_bytes_sent.hi);
+ qstats->total_unicast_bytes_transmitted_hi;
qstats->total_bytes_transmitted_lo =
- le32_to_cpu(xclient->unicast_bytes_sent.lo);
+ qstats->total_unicast_bytes_transmitted_lo;
ADD_64(qstats->total_bytes_transmitted_hi,
- le32_to_cpu(xclient->multicast_bytes_sent.hi),
+ qstats->total_broadcast_bytes_transmitted_hi,
qstats->total_bytes_transmitted_lo,
- le32_to_cpu(xclient->multicast_bytes_sent.lo));
+ qstats->total_broadcast_bytes_transmitted_lo);
ADD_64(qstats->total_bytes_transmitted_hi,
- le32_to_cpu(xclient->broadcast_bytes_sent.hi),
+ qstats->total_multicast_bytes_transmitted_hi,
qstats->total_bytes_transmitted_lo,
- le32_to_cpu(xclient->broadcast_bytes_sent.lo));
+ qstats->total_multicast_bytes_transmitted_lo);
- UPDATE_EXTEND_XSTAT(unicast_pkts_sent,
+ UPDATE_EXTEND_XSTAT(ucast_pkts_sent,
total_unicast_packets_transmitted);
- UPDATE_EXTEND_XSTAT(multicast_pkts_sent,
+ UPDATE_EXTEND_XSTAT(mcast_pkts_sent,
total_multicast_packets_transmitted);
- UPDATE_EXTEND_XSTAT(broadcast_pkts_sent,
+ UPDATE_EXTEND_XSTAT(bcast_pkts_sent,
total_broadcast_packets_transmitted);
- old_tclient->checksum_discard = tclient->checksum_discard;
- old_tclient->ttl0_discard = tclient->ttl0_discard;
+ UPDATE_EXTEND_TSTAT(checksum_discard,
+ total_packets_received_checksum_discarded);
+ UPDATE_EXTEND_TSTAT(ttl0_discard,
+ total_packets_received_ttl0_discarded);
+
+ UPDATE_EXTEND_XSTAT(error_drop_pkts,
+ total_transmitted_dropped_packets_error);
+
+ /* TPA aggregations completed */
+ UPDATE_EXTEND_USTAT(coalesced_events, total_tpa_aggregations);
+ /* Number of network frames aggregated by TPA */
+ UPDATE_EXTEND_USTAT(coalesced_pkts,
+ total_tpa_aggregated_frames);
+ /* Total number of bytes in completed TPA aggregations */
+ qstats->total_tpa_bytes_lo =
+ le32_to_cpu(uclient->coalesced_bytes.lo);
+ qstats->total_tpa_bytes_hi =
+ le32_to_cpu(uclient->coalesced_bytes.hi);
+
+ /* TPA stats per-function */
+ ADD_64(estats->total_tpa_aggregations_hi,
+ qstats->total_tpa_aggregations_hi,
+ estats->total_tpa_aggregations_lo,
+ qstats->total_tpa_aggregations_lo);
+ ADD_64(estats->total_tpa_aggregated_frames_hi,
+ qstats->total_tpa_aggregated_frames_hi,
+ estats->total_tpa_aggregated_frames_lo,
+ qstats->total_tpa_aggregated_frames_lo);
+ ADD_64(estats->total_tpa_bytes_hi,
+ qstats->total_tpa_bytes_hi,
+ estats->total_tpa_bytes_lo,
+ qstats->total_tpa_bytes_lo);
ADD_64(fstats->total_bytes_received_hi,
qstats->total_bytes_received_hi,
@@ -933,10 +1013,6 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp)
fstats->valid_bytes_received_lo,
qstats->valid_bytes_received_lo);
- ADD_64(estats->error_bytes_received_hi,
- qstats->error_bytes_received_hi,
- estats->error_bytes_received_lo,
- qstats->error_bytes_received_lo);
ADD_64(estats->etherstatsoverrsizepkts_hi,
qstats->etherstatsoverrsizepkts_hi,
estats->etherstatsoverrsizepkts_lo,
@@ -950,9 +1026,19 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp)
fstats->total_bytes_received_lo,
estats->rx_stat_ifhcinbadoctets_lo);
+ ADD_64(fstats->total_bytes_received_hi,
+ tfunc->rcv_error_bytes.hi,
+ fstats->total_bytes_received_lo,
+ tfunc->rcv_error_bytes.lo);
+
memcpy(estats, &(fstats->total_bytes_received_hi),
sizeof(struct host_func_stats) - 2*sizeof(u32));
+ ADD_64(estats->error_bytes_received_hi,
+ tfunc->rcv_error_bytes.hi,
+ estats->error_bytes_received_lo,
+ tfunc->rcv_error_bytes.lo);
+
ADD_64(estats->etherstatsoverrsizepkts_hi,
estats->rx_stat_dot3statsframestoolong_hi,
estats->etherstatsoverrsizepkts_lo,
@@ -965,8 +1051,8 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp)
if (bp->port.pmf) {
estats->mac_filter_discard =
le32_to_cpu(tport->mac_filter_discard);
- estats->xxoverflow_discard =
- le32_to_cpu(tport->xxoverflow_discard);
+ estats->mf_tag_discard =
+ le32_to_cpu(tport->mf_tag_discard);
estats->brb_truncate_discard =
le32_to_cpu(tport->brb_truncate_discard);
estats->mac_discard = le32_to_cpu(tport->mac_discard);
@@ -1023,7 +1109,7 @@ static void bnx2x_net_stats_update(struct bnx2x *bp)
nstats->rx_frame_errors =
bnx2x_hilo(&estats->rx_stat_dot3statsalignmenterrors_hi);
nstats->rx_fifo_errors = bnx2x_hilo(&estats->no_buff_discard_hi);
- nstats->rx_missed_errors = estats->xxoverflow_discard;
+ nstats->rx_missed_errors = 0;
nstats->rx_errors = nstats->rx_length_errors +
nstats->rx_over_errors +
@@ -1065,10 +1151,27 @@ static void bnx2x_drv_stats_update(struct bnx2x *bp)
}
}
+static bool bnx2x_edebug_stats_stopped(struct bnx2x *bp)
+{
+ u32 val;
+
+ if (SHMEM2_HAS(bp, edebug_driver_if[1])) {
+ val = SHMEM2_RD(bp, edebug_driver_if[1]);
+
+ if (val == EDEBUG_DRIVER_IF_OP_CODE_DISABLE_STAT)
+ return true;
+ }
+
+ return false;
+}
+
static void bnx2x_stats_update(struct bnx2x *bp)
{
u32 *stats_comp = bnx2x_sp(bp, stats_comp);
+ if (bnx2x_edebug_stats_stopped(bp))
+ return;
+
if (*stats_comp != DMAE_COMP_VAL)
return;
@@ -1086,42 +1189,50 @@ static void bnx2x_stats_update(struct bnx2x *bp)
if (netif_msg_timer(bp)) {
struct bnx2x_eth_stats *estats = &bp->eth_stats;
- int i;
+ int i, cos;
- printk(KERN_DEBUG "%s: brb drops %u brb truncate %u\n",
- bp->dev->name,
+ netdev_dbg(bp->dev, "brb drops %u brb truncate %u\n",
estats->brb_drop_lo, estats->brb_truncate_lo);
for_each_eth_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
struct bnx2x_eth_q_stats *qstats = &fp->eth_q_stats;
- printk(KERN_DEBUG "%s: rx usage(%4u) *rx_cons_sb(%u)"
- " rx pkt(%lu) rx calls(%lu %lu)\n",
- fp->name, (le16_to_cpu(*fp->rx_cons_sb) -
- fp->rx_comp_cons),
- le16_to_cpu(*fp->rx_cons_sb),
- bnx2x_hilo(&qstats->
- total_unicast_packets_received_hi),
- fp->rx_calls, fp->rx_pkt);
+ pr_debug("%s: rx usage(%4u) *rx_cons_sb(%u) rx pkt(%lu) rx calls(%lu %lu)\n",
+ fp->name, (le16_to_cpu(*fp->rx_cons_sb) -
+ fp->rx_comp_cons),
+ le16_to_cpu(*fp->rx_cons_sb),
+ bnx2x_hilo(&qstats->
+ total_unicast_packets_received_hi),
+ fp->rx_calls, fp->rx_pkt);
}
for_each_eth_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
+ struct bnx2x_fp_txdata *txdata;
struct bnx2x_eth_q_stats *qstats = &fp->eth_q_stats;
- struct netdev_queue *txq =
- netdev_get_tx_queue(bp->dev, i);
-
- printk(KERN_DEBUG "%s: tx avail(%4u) *tx_cons_sb(%u)"
- " tx pkt(%lu) tx calls (%lu)"
- " %s (Xoff events %u)\n",
- fp->name, bnx2x_tx_avail(fp),
- le16_to_cpu(*fp->tx_cons_sb),
- bnx2x_hilo(&qstats->
- total_unicast_packets_transmitted_hi),
- fp->tx_pkt,
- (netif_tx_queue_stopped(txq) ? "Xoff" : "Xon"),
- qstats->driver_xoff);
+ struct netdev_queue *txq;
+
+ pr_debug("%s: tx pkt(%lu) (Xoff events %u)",
+ fp->name,
+ bnx2x_hilo(
+ &qstats->total_unicast_packets_transmitted_hi),
+ qstats->driver_xoff);
+
+ for_each_cos_in_tx_queue(fp, cos) {
+ txdata = &fp->txdata[cos];
+ txq = netdev_get_tx_queue(bp->dev,
+ FP_COS_TO_TXQ(fp, cos));
+
+ pr_debug("%d: tx avail(%4u) *tx_cons_sb(%u) tx calls (%lu) %s\n",
+ cos,
+ bnx2x_tx_avail(bp, txdata),
+ le16_to_cpu(*txdata->tx_cons_sb),
+ txdata->tx_pkt,
+ (netif_tx_queue_stopped(txq) ?
+ "Xoff" : "Xon")
+ );
+ }
}
}
@@ -1149,6 +1260,7 @@ static void bnx2x_port_stats_stop(struct bnx2x *bp)
else
dmae->opcode = bnx2x_dmae_opcode_add_comp(
opcode, DMAE_COMP_PCI);
+
dmae->src_addr_lo = U64_LO(bnx2x_sp_mapping(bp, port_stats));
dmae->src_addr_hi = U64_HI(bnx2x_sp_mapping(bp, port_stats));
dmae->dst_addr_lo = bp->port.port_stx >> 2;
@@ -1235,13 +1347,9 @@ static const struct {
void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event)
{
enum bnx2x_stats_state state;
-
if (unlikely(bp->panic))
return;
-
bnx2x_stats_stm[bp->stats_state][event].action(bp);
-
- /* Protect a state change flow */
spin_lock_bh(&bp->stats_lock);
state = bp->stats_state;
bp->stats_state = bnx2x_stats_stm[state][event].next_state;
@@ -1284,7 +1392,7 @@ static void bnx2x_port_stats_base_init(struct bnx2x *bp)
static void bnx2x_func_stats_base_init(struct bnx2x *bp)
{
- int vn, vn_max = IS_MF(bp) ? E1HVN_MAX : E1VN_MAX;
+ int vn, vn_max = IS_MF(bp) ? BP_MAX_VN_NUM(bp) : E1VN_MAX;
u32 func_stx;
/* sanity */
@@ -1297,7 +1405,7 @@ static void bnx2x_func_stats_base_init(struct bnx2x *bp)
func_stx = bp->func_stx;
for (vn = VN_0; vn < vn_max; vn++) {
- int mb_idx = !CHIP_IS_E2(bp) ? 2*vn + BP_PORT(bp) : vn;
+ int mb_idx = BP_FW_MB_IDX_VN(bp, vn);
bp->func_stx = SHMEM_RD(bp, func_mb[mb_idx].fw_mb_param);
bnx2x_func_stats_init(bp);
@@ -1339,12 +1447,97 @@ static void bnx2x_func_stats_base_update(struct bnx2x *bp)
bnx2x_stats_comp(bp);
}
+/**
+ * This function will prepare the statistics ramrod data the way
+ * we will only have to increment the statistics counter and
+ * send the ramrod each time we have to.
+ *
+ * @param bp
+ */
+static inline void bnx2x_prep_fw_stats_req(struct bnx2x *bp)
+{
+ int i;
+ struct stats_query_header *stats_hdr = &bp->fw_stats_req->hdr;
+
+ dma_addr_t cur_data_offset;
+ struct stats_query_entry *cur_query_entry;
+
+ stats_hdr->cmd_num = bp->fw_stats_num;
+ stats_hdr->drv_stats_counter = 0;
+
+ /* storm_counters struct contains the counters of completed
+ * statistics requests per storm which are incremented by FW
+ * each time it completes hadning a statistics ramrod. We will
+ * check these counters in the timer handler and discard a
+ * (statistics) ramrod completion.
+ */
+ cur_data_offset = bp->fw_stats_data_mapping +
+ offsetof(struct bnx2x_fw_stats_data, storm_counters);
+
+ stats_hdr->stats_counters_addrs.hi =
+ cpu_to_le32(U64_HI(cur_data_offset));
+ stats_hdr->stats_counters_addrs.lo =
+ cpu_to_le32(U64_LO(cur_data_offset));
+
+ /* prepare to the first stats ramrod (will be completed with
+ * the counters equal to zero) - init counters to somethig different.
+ */
+ memset(&bp->fw_stats_data->storm_counters, 0xff,
+ sizeof(struct stats_counter));
+
+ /**** Port FW statistics data ****/
+ cur_data_offset = bp->fw_stats_data_mapping +
+ offsetof(struct bnx2x_fw_stats_data, port);
+
+ cur_query_entry = &bp->fw_stats_req->query[BNX2X_PORT_QUERY_IDX];
+
+ cur_query_entry->kind = STATS_TYPE_PORT;
+ /* For port query index is a DONT CARE */
+ cur_query_entry->index = BP_PORT(bp);
+ /* For port query funcID is a DONT CARE */
+ cur_query_entry->funcID = cpu_to_le16(BP_FUNC(bp));
+ cur_query_entry->address.hi = cpu_to_le32(U64_HI(cur_data_offset));
+ cur_query_entry->address.lo = cpu_to_le32(U64_LO(cur_data_offset));
+
+ /**** PF FW statistics data ****/
+ cur_data_offset = bp->fw_stats_data_mapping +
+ offsetof(struct bnx2x_fw_stats_data, pf);
+
+ cur_query_entry = &bp->fw_stats_req->query[BNX2X_PF_QUERY_IDX];
+
+ cur_query_entry->kind = STATS_TYPE_PF;
+ /* For PF query index is a DONT CARE */
+ cur_query_entry->index = BP_PORT(bp);
+ cur_query_entry->funcID = cpu_to_le16(BP_FUNC(bp));
+ cur_query_entry->address.hi = cpu_to_le32(U64_HI(cur_data_offset));
+ cur_query_entry->address.lo = cpu_to_le32(U64_LO(cur_data_offset));
+
+ /**** Clients' queries ****/
+ cur_data_offset = bp->fw_stats_data_mapping +
+ offsetof(struct bnx2x_fw_stats_data, queue_stats);
+
+ for_each_eth_queue(bp, i) {
+ cur_query_entry =
+ &bp->fw_stats_req->
+ query[BNX2X_FIRST_QUEUE_QUERY_IDX + i];
+
+ cur_query_entry->kind = STATS_TYPE_QUEUE;
+ cur_query_entry->index = bnx2x_stats_id(&bp->fp[i]);
+ cur_query_entry->funcID = cpu_to_le16(BP_FUNC(bp));
+ cur_query_entry->address.hi =
+ cpu_to_le32(U64_HI(cur_data_offset));
+ cur_query_entry->address.lo =
+ cpu_to_le32(U64_LO(cur_data_offset));
+
+ cur_data_offset += sizeof(struct per_queue_stats);
+ }
+}
+
void bnx2x_stats_init(struct bnx2x *bp)
{
- int port = BP_PORT(bp);
+ int /*abs*/port = BP_PORT(bp);
int mb_idx = BP_FW_MB_IDX(bp);
int i;
- struct eth_stats_query *stats = bnx2x_sp(bp, fw_stats);
bp->stats_pending = 0;
bp->executer_idx = 0;
@@ -1362,45 +1555,35 @@ void bnx2x_stats_init(struct bnx2x *bp)
DP(BNX2X_MSG_STATS, "port_stx 0x%x func_stx 0x%x\n",
bp->port.port_stx, bp->func_stx);
+ port = BP_PORT(bp);
/* port stats */
memset(&(bp->port.old_nig_stats), 0, sizeof(struct nig_stats));
bp->port.old_nig_stats.brb_discard =
REG_RD(bp, NIG_REG_STAT0_BRB_DISCARD + port*0x38);
bp->port.old_nig_stats.brb_truncate =
REG_RD(bp, NIG_REG_STAT0_BRB_TRUNCATE + port*0x38);
- REG_RD_DMAE(bp, NIG_REG_STAT0_EGRESS_MAC_PKT0 + port*0x50,
- &(bp->port.old_nig_stats.egress_mac_pkt0_lo), 2);
- REG_RD_DMAE(bp, NIG_REG_STAT0_EGRESS_MAC_PKT1 + port*0x50,
- &(bp->port.old_nig_stats.egress_mac_pkt1_lo), 2);
+ if (!CHIP_IS_E3(bp)) {
+ REG_RD_DMAE(bp, NIG_REG_STAT0_EGRESS_MAC_PKT0 + port*0x50,
+ &(bp->port.old_nig_stats.egress_mac_pkt0_lo), 2);
+ REG_RD_DMAE(bp, NIG_REG_STAT0_EGRESS_MAC_PKT1 + port*0x50,
+ &(bp->port.old_nig_stats.egress_mac_pkt1_lo), 2);
+ }
/* function stats */
for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
- memset(&fp->old_tclient, 0,
- sizeof(struct tstorm_per_client_stats));
- memset(&fp->old_uclient, 0,
- sizeof(struct ustorm_per_client_stats));
- memset(&fp->old_xclient, 0,
- sizeof(struct xstorm_per_client_stats));
- memset(&fp->eth_q_stats, 0, sizeof(struct bnx2x_eth_q_stats));
+ memset(&fp->old_tclient, 0, sizeof(fp->old_tclient));
+ memset(&fp->old_uclient, 0, sizeof(fp->old_uclient));
+ memset(&fp->old_xclient, 0, sizeof(fp->old_xclient));
+ memset(&fp->eth_q_stats, 0, sizeof(fp->eth_q_stats));
}
- /* FW stats are currently collected for ETH clients only */
- for_each_eth_queue(bp, i) {
- /* Set initial stats counter in the stats ramrod data to -1 */
- int cl_id = bp->fp[i].cl_id;
-
- stats->xstorm_common.client_statistics[cl_id].
- stats_counter = 0xffff;
- stats->ustorm_common.client_statistics[cl_id].
- stats_counter = 0xffff;
- stats->tstorm_common.client_statistics[cl_id].
- stats_counter = 0xffff;
- }
+ /* Prepare statistics ramrod data */
+ bnx2x_prep_fw_stats_req(bp);
- memset(&bp->dev->stats, 0, sizeof(struct net_device_stats));
- memset(&bp->eth_stats, 0, sizeof(struct bnx2x_eth_stats));
+ memset(&bp->dev->stats, 0, sizeof(bp->dev->stats));
+ memset(&bp->eth_stats, 0, sizeof(bp->eth_stats));
bp->stats_state = STATS_STATE_DISABLED;
diff --git a/drivers/net/bnx2x/bnx2x_stats.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h
index 45d14d8bc1aa..5d8ce2f6afef 100644
--- a/drivers/net/bnx2x/bnx2x_stats.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h
@@ -14,48 +14,11 @@
* Statistics and Link management by Yitchak Gertner
*
*/
-
#ifndef BNX2X_STATS_H
#define BNX2X_STATS_H
#include <linux/types.h>
-struct bnx2x_eth_q_stats {
- u32 total_bytes_received_hi;
- u32 total_bytes_received_lo;
- u32 total_bytes_transmitted_hi;
- u32 total_bytes_transmitted_lo;
- u32 total_unicast_packets_received_hi;
- u32 total_unicast_packets_received_lo;
- u32 total_multicast_packets_received_hi;
- u32 total_multicast_packets_received_lo;
- u32 total_broadcast_packets_received_hi;
- u32 total_broadcast_packets_received_lo;
- u32 total_unicast_packets_transmitted_hi;
- u32 total_unicast_packets_transmitted_lo;
- u32 total_multicast_packets_transmitted_hi;
- u32 total_multicast_packets_transmitted_lo;
- u32 total_broadcast_packets_transmitted_hi;
- u32 total_broadcast_packets_transmitted_lo;
- u32 valid_bytes_received_hi;
- u32 valid_bytes_received_lo;
-
- u32 error_bytes_received_hi;
- u32 error_bytes_received_lo;
- u32 etherstatsoverrsizepkts_hi;
- u32 etherstatsoverrsizepkts_lo;
- u32 no_buff_discard_hi;
- u32 no_buff_discard_lo;
-
- u32 driver_xoff;
- u32 rx_err_discard_pkt;
- u32 rx_skb_alloc_failed;
- u32 hw_csum_err;
-};
-
-#define Q_STATS_OFFSET32(stat_name) \
- (offsetof(struct bnx2x_eth_q_stats, stat_name) / 4)
-
struct nig_stats {
u32 brb_discard;
u32 brb_packet;
@@ -212,7 +175,7 @@ struct bnx2x_eth_stats {
u32 brb_truncate_lo;
u32 mac_filter_discard;
- u32 xxoverflow_discard;
+ u32 mf_tag_discard;
u32 brb_truncate_discard;
u32 mac_discard;
@@ -222,16 +185,197 @@ struct bnx2x_eth_stats {
u32 hw_csum_err;
u32 nig_timer_max;
+
+ /* TPA */
+ u32 total_tpa_aggregations_hi;
+ u32 total_tpa_aggregations_lo;
+ u32 total_tpa_aggregated_frames_hi;
+ u32 total_tpa_aggregated_frames_lo;
+ u32 total_tpa_bytes_hi;
+ u32 total_tpa_bytes_lo;
+};
+
+
+struct bnx2x_eth_q_stats {
+ u32 total_unicast_bytes_received_hi;
+ u32 total_unicast_bytes_received_lo;
+ u32 total_broadcast_bytes_received_hi;
+ u32 total_broadcast_bytes_received_lo;
+ u32 total_multicast_bytes_received_hi;
+ u32 total_multicast_bytes_received_lo;
+ u32 total_bytes_received_hi;
+ u32 total_bytes_received_lo;
+ u32 total_unicast_bytes_transmitted_hi;
+ u32 total_unicast_bytes_transmitted_lo;
+ u32 total_broadcast_bytes_transmitted_hi;
+ u32 total_broadcast_bytes_transmitted_lo;
+ u32 total_multicast_bytes_transmitted_hi;
+ u32 total_multicast_bytes_transmitted_lo;
+ u32 total_bytes_transmitted_hi;
+ u32 total_bytes_transmitted_lo;
+ u32 total_unicast_packets_received_hi;
+ u32 total_unicast_packets_received_lo;
+ u32 total_multicast_packets_received_hi;
+ u32 total_multicast_packets_received_lo;
+ u32 total_broadcast_packets_received_hi;
+ u32 total_broadcast_packets_received_lo;
+ u32 total_unicast_packets_transmitted_hi;
+ u32 total_unicast_packets_transmitted_lo;
+ u32 total_multicast_packets_transmitted_hi;
+ u32 total_multicast_packets_transmitted_lo;
+ u32 total_broadcast_packets_transmitted_hi;
+ u32 total_broadcast_packets_transmitted_lo;
+ u32 valid_bytes_received_hi;
+ u32 valid_bytes_received_lo;
+
+ u32 etherstatsoverrsizepkts_hi;
+ u32 etherstatsoverrsizepkts_lo;
+ u32 no_buff_discard_hi;
+ u32 no_buff_discard_lo;
+
+ u32 driver_xoff;
+ u32 rx_err_discard_pkt;
+ u32 rx_skb_alloc_failed;
+ u32 hw_csum_err;
+
+ u32 total_packets_received_checksum_discarded_hi;
+ u32 total_packets_received_checksum_discarded_lo;
+ u32 total_packets_received_ttl0_discarded_hi;
+ u32 total_packets_received_ttl0_discarded_lo;
+ u32 total_transmitted_dropped_packets_error_hi;
+ u32 total_transmitted_dropped_packets_error_lo;
+
+ /* TPA */
+ u32 total_tpa_aggregations_hi;
+ u32 total_tpa_aggregations_lo;
+ u32 total_tpa_aggregated_frames_hi;
+ u32 total_tpa_aggregated_frames_lo;
+ u32 total_tpa_bytes_hi;
+ u32 total_tpa_bytes_lo;
};
-#define STATS_OFFSET32(stat_name) \
- (offsetof(struct bnx2x_eth_stats, stat_name) / 4)
+/****************************************************************************
+* Macros
+****************************************************************************/
+
+/* sum[hi:lo] += add[hi:lo] */
+#define ADD_64(s_hi, a_hi, s_lo, a_lo) \
+ do { \
+ s_lo += a_lo; \
+ s_hi += a_hi + ((s_lo < a_lo) ? 1 : 0); \
+ } while (0)
+
+/* difference = minuend - subtrahend */
+#define DIFF_64(d_hi, m_hi, s_hi, d_lo, m_lo, s_lo) \
+ do { \
+ if (m_lo < s_lo) { \
+ /* underflow */ \
+ d_hi = m_hi - s_hi; \
+ if (d_hi > 0) { \
+ /* we can 'loan' 1 */ \
+ d_hi--; \
+ d_lo = m_lo + (UINT_MAX - s_lo) + 1; \
+ } else { \
+ /* m_hi <= s_hi */ \
+ d_hi = 0; \
+ d_lo = 0; \
+ } \
+ } else { \
+ /* m_lo >= s_lo */ \
+ if (m_hi < s_hi) { \
+ d_hi = 0; \
+ d_lo = 0; \
+ } else { \
+ /* m_hi >= s_hi */ \
+ d_hi = m_hi - s_hi; \
+ d_lo = m_lo - s_lo; \
+ } \
+ } \
+ } while (0)
+
+#define UPDATE_STAT64(s, t) \
+ do { \
+ DIFF_64(diff.hi, new->s##_hi, pstats->mac_stx[0].t##_hi, \
+ diff.lo, new->s##_lo, pstats->mac_stx[0].t##_lo); \
+ pstats->mac_stx[0].t##_hi = new->s##_hi; \
+ pstats->mac_stx[0].t##_lo = new->s##_lo; \
+ ADD_64(pstats->mac_stx[1].t##_hi, diff.hi, \
+ pstats->mac_stx[1].t##_lo, diff.lo); \
+ } while (0)
+
+#define UPDATE_STAT64_NIG(s, t) \
+ do { \
+ DIFF_64(diff.hi, new->s##_hi, old->s##_hi, \
+ diff.lo, new->s##_lo, old->s##_lo); \
+ ADD_64(estats->t##_hi, diff.hi, \
+ estats->t##_lo, diff.lo); \
+ } while (0)
+
+/* sum[hi:lo] += add */
+#define ADD_EXTEND_64(s_hi, s_lo, a) \
+ do { \
+ s_lo += a; \
+ s_hi += (s_lo < a) ? 1 : 0; \
+ } while (0)
+
+#define ADD_STAT64(diff, t) \
+ do { \
+ ADD_64(pstats->mac_stx[1].t##_hi, new->diff##_hi, \
+ pstats->mac_stx[1].t##_lo, new->diff##_lo); \
+ } while (0)
+
+#define UPDATE_EXTEND_STAT(s) \
+ do { \
+ ADD_EXTEND_64(pstats->mac_stx[1].s##_hi, \
+ pstats->mac_stx[1].s##_lo, \
+ new->s); \
+ } while (0)
+
+#define UPDATE_EXTEND_TSTAT(s, t) \
+ do { \
+ diff = le32_to_cpu(tclient->s) - le32_to_cpu(old_tclient->s); \
+ old_tclient->s = tclient->s; \
+ ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \
+ } while (0)
+
+#define UPDATE_EXTEND_USTAT(s, t) \
+ do { \
+ diff = le32_to_cpu(uclient->s) - le32_to_cpu(old_uclient->s); \
+ old_uclient->s = uclient->s; \
+ ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \
+ } while (0)
+
+#define UPDATE_EXTEND_XSTAT(s, t) \
+ do { \
+ diff = le32_to_cpu(xclient->s) - le32_to_cpu(old_xclient->s); \
+ old_xclient->s = xclient->s; \
+ ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \
+ } while (0)
+
+/* minuend -= subtrahend */
+#define SUB_64(m_hi, s_hi, m_lo, s_lo) \
+ do { \
+ DIFF_64(m_hi, m_hi, s_hi, m_lo, m_lo, s_lo); \
+ } while (0)
+
+/* minuend[hi:lo] -= subtrahend */
+#define SUB_EXTEND_64(m_hi, m_lo, s) \
+ do { \
+ SUB_64(m_hi, 0, m_lo, s); \
+ } while (0)
+
+#define SUB_EXTEND_USTAT(s, t) \
+ do { \
+ diff = le32_to_cpu(uclient->s) - le32_to_cpu(old_uclient->s); \
+ SUB_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \
+ } while (0)
+
-/* Forward declaration */
+/* forward */
struct bnx2x;
void bnx2x_stats_init(struct bnx2x *bp);
-extern const u32 dmae_reg_go_c[];
+void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event);
#endif /* BNX2X_STATS_H */
diff --git a/drivers/net/cnic.c b/drivers/net/ethernet/broadcom/cnic.c
index 11a92afdf982..6f10c6939834 100644
--- a/drivers/net/cnic.c
+++ b/drivers/net/ethernet/broadcom/cnic.c
@@ -1,6 +1,6 @@
/* cnic.c: Broadcom CNIC core network driver.
*
- * Copyright (c) 2006-2010 Broadcom Corporation
+ * Copyright (c) 2006-2011 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
#include <linux/ethtool.h>
#include <linux/if_vlan.h>
#include <linux/prefetch.h>
+#include <linux/random.h>
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
#define BCM_VLAN 1
#endif
@@ -44,8 +45,8 @@
#include "bnx2x/bnx2x_reg.h"
#include "bnx2x/bnx2x_fw_defs.h"
#include "bnx2x/bnx2x_hsi.h"
-#include "../scsi/bnx2i/57xx_iscsi_constants.h"
-#include "../scsi/bnx2i/57xx_iscsi_hsi.h"
+#include "../../../scsi/bnx2i/57xx_iscsi_constants.h"
+#include "../../../scsi/bnx2i/57xx_iscsi_hsi.h"
#include "cnic.h"
#include "cnic_defs.h"
@@ -327,7 +328,7 @@ static int cnic_send_nlmsg(struct cnic_local *cp, u32 type,
msleep(100);
retry++;
}
- return 0;
+ return rc;
}
static void cnic_cm_upcall(struct cnic_local *, struct cnic_sock *, u8);
@@ -605,11 +606,12 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
}
EXPORT_SYMBOL(cnic_unregister_driver);
-static int cnic_init_id_tbl(struct cnic_id_tbl *id_tbl, u32 size, u32 start_id)
+static int cnic_init_id_tbl(struct cnic_id_tbl *id_tbl, u32 size, u32 start_id,
+ u32 next)
{
id_tbl->start = start_id;
id_tbl->max = size;
- id_tbl->next = 0;
+ id_tbl->next = next;
spin_lock_init(&id_tbl->lock);
id_tbl->table = kzalloc(DIV_ROUND_UP(size, 32) * 4, GFP_KERNEL);
if (!id_tbl->table)
@@ -835,7 +837,6 @@ static void cnic_free_resc(struct cnic_dev *dev)
cp->ctx_blks = 0;
cnic_free_dma(dev, &cp->gbl_buf_info);
- cnic_free_dma(dev, &cp->conn_buf_info);
cnic_free_dma(dev, &cp->kwq_info);
cnic_free_dma(dev, &cp->kwq_16_data_info);
cnic_free_dma(dev, &cp->kcq2.dma);
@@ -899,24 +900,56 @@ static int cnic_alloc_context(struct cnic_dev *dev)
return 0;
}
-static int cnic_alloc_kcq(struct cnic_dev *dev, struct kcq_info *info)
+static u16 cnic_bnx2_next_idx(u16 idx)
{
- int err, i, is_bnx2 = 0;
+ return idx + 1;
+}
+
+static u16 cnic_bnx2_hw_idx(u16 idx)
+{
+ return idx;
+}
+
+static u16 cnic_bnx2x_next_idx(u16 idx)
+{
+ idx++;
+ if ((idx & MAX_KCQE_CNT) == MAX_KCQE_CNT)
+ idx++;
+
+ return idx;
+}
+
+static u16 cnic_bnx2x_hw_idx(u16 idx)
+{
+ if ((idx & MAX_KCQE_CNT) == MAX_KCQE_CNT)
+ idx++;
+ return idx;
+}
+
+static int cnic_alloc_kcq(struct cnic_dev *dev, struct kcq_info *info,
+ bool use_pg_tbl)
+{
+ int err, i, use_page_tbl = 0;
struct kcqe **kcq;
- if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags))
- is_bnx2 = 1;
+ if (use_pg_tbl)
+ use_page_tbl = 1;
- err = cnic_alloc_dma(dev, &info->dma, KCQ_PAGE_CNT, is_bnx2);
+ err = cnic_alloc_dma(dev, &info->dma, KCQ_PAGE_CNT, use_page_tbl);
if (err)
return err;
kcq = (struct kcqe **) info->dma.pg_arr;
info->kcq = kcq;
- if (is_bnx2)
+ info->next_idx = cnic_bnx2_next_idx;
+ info->hw_idx = cnic_bnx2_hw_idx;
+ if (use_pg_tbl)
return 0;
+ info->next_idx = cnic_bnx2x_next_idx;
+ info->hw_idx = cnic_bnx2x_hw_idx;
+
for (i = 0; i < KCQ_PAGE_CNT; i++) {
struct bnx2x_bd_chain_next *next =
(struct bnx2x_bd_chain_next *) &kcq[i][MAX_KCQE_CNT];
@@ -1059,7 +1092,7 @@ static int cnic_alloc_bnx2_resc(struct cnic_dev *dev)
goto error;
cp->kwq = (struct kwqe **) cp->kwq_info.pg_arr;
- ret = cnic_alloc_kcq(dev, &cp->kcq1);
+ ret = cnic_alloc_kcq(dev, &cp->kcq1, true);
if (ret)
goto error;
@@ -1139,25 +1172,17 @@ static int cnic_alloc_bnx2x_resc(struct cnic_dev *dev)
cp->iro_arr = ethdev->iro_arr;
- cp->max_cid_space = MAX_ISCSI_TBL_SZ + BNX2X_FCOE_NUM_CONNECTIONS;
+ cp->max_cid_space = MAX_ISCSI_TBL_SZ;
cp->iscsi_start_cid = start_cid;
cp->fcoe_start_cid = start_cid + MAX_ISCSI_TBL_SZ;
- if (BNX2X_CHIP_IS_E2(cp->chip_id)) {
- cp->max_cid_space += BNX2X_FCOE_NUM_CONNECTIONS;
+ if (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id)) {
+ cp->max_cid_space += dev->max_fcoe_conn;
cp->fcoe_init_cid = ethdev->fcoe_init_cid;
if (!cp->fcoe_init_cid)
cp->fcoe_init_cid = 0x10;
}
- if (start_cid < BNX2X_ISCSI_START_CID) {
- u32 delta = BNX2X_ISCSI_START_CID - start_cid;
-
- cp->iscsi_start_cid = BNX2X_ISCSI_START_CID;
- cp->fcoe_start_cid += delta;
- cp->max_cid_space += delta;
- }
-
cp->iscsi_tbl = kzalloc(sizeof(struct cnic_iscsi) * MAX_ISCSI_TBL_SZ,
GFP_KERNEL);
if (!cp->iscsi_tbl)
@@ -1195,22 +1220,16 @@ static int cnic_alloc_bnx2x_resc(struct cnic_dev *dev)
j++;
}
- ret = cnic_alloc_kcq(dev, &cp->kcq1);
+ ret = cnic_alloc_kcq(dev, &cp->kcq1, false);
if (ret)
goto error;
- if (BNX2X_CHIP_IS_E2(cp->chip_id)) {
- ret = cnic_alloc_kcq(dev, &cp->kcq2);
+ if (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id)) {
+ ret = cnic_alloc_kcq(dev, &cp->kcq2, true);
if (ret)
goto error;
}
- pages = PAGE_ALIGN(BNX2X_ISCSI_NUM_CONNECTIONS *
- BNX2X_ISCSI_CONN_BUF_SIZE) / PAGE_SIZE;
- ret = cnic_alloc_dma(dev, &cp->conn_buf_info, pages, 1);
- if (ret)
- goto error;
-
pages = PAGE_ALIGN(BNX2X_ISCSI_GLB_BUF_SIZE) / PAGE_SIZE;
ret = cnic_alloc_dma(dev, &cp->gbl_buf_info, pages, 0);
if (ret)
@@ -1577,6 +1596,7 @@ static int cnic_setup_bnx2x_ctx(struct cnic_dev *dev, struct kwqe *wqes[],
struct iscsi_context *ictx;
struct regpair context_addr;
int i, j, n = 2, n_max;
+ u8 port = CNIC_PORT(cp);
ctx->ctx_flags = 0;
if (!req2->num_additional_wqes)
@@ -1628,6 +1648,17 @@ static int cnic_setup_bnx2x_ctx(struct cnic_dev *dev, struct kwqe *wqes[],
XSTORM_ISCSI_CONTEXT_FLAGS_B_IMMEDIATE_DATA;
ictx->xstorm_st_context.iscsi.flags.flags |=
XSTORM_ISCSI_CONTEXT_FLAGS_B_INITIAL_R2T;
+ ictx->xstorm_st_context.common.ethernet.reserved_vlan_type =
+ ETH_P_8021Q;
+ if (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id) &&
+ cp->port_mode == CHIP_2_PORT_MODE) {
+
+ port = 0;
+ }
+ ictx->xstorm_st_context.common.flags =
+ 1 << XSTORM_COMMON_CONTEXT_SECTION_PHYSQ_INITIALIZED_SHIFT;
+ ictx->xstorm_st_context.common.flags =
+ port << XSTORM_COMMON_CONTEXT_SECTION_PBF_PORT_SHIFT;
ictx->tstorm_st_context.iscsi.hdr_bytes_2_fetch = ISCSI_HEADER_SIZE;
/* TSTORM requires the base address of RQ DB & not PTE */
@@ -1843,10 +1874,13 @@ static int cnic_bnx2x_destroy_ramrod(struct cnic_dev *dev, u32 l5_cid)
ret = cnic_submit_kwqe_16(dev, RAMROD_CMD_ID_COMMON_CFC_DEL,
hw_cid, NONE_CONNECTION_TYPE, &l5_data);
- if (ret == 0)
- wait_event(ctx->waitq, ctx->wait_cond);
+ if (ret == 0) {
+ wait_event_timeout(ctx->waitq, ctx->wait_cond, CNIC_RAMROD_TMO);
+ if (unlikely(test_bit(CTX_FL_CID_ERROR, &ctx->ctx_flags)))
+ return -EBUSY;
+ }
- return ret;
+ return 0;
}
static int cnic_bnx2x_iscsi_destroy(struct cnic_dev *dev, struct kwqe *kwqe)
@@ -1879,8 +1913,10 @@ static int cnic_bnx2x_iscsi_destroy(struct cnic_dev *dev, struct kwqe *kwqe)
skip_cfc_delete:
cnic_free_bnx2x_conn_resc(dev, l5_cid);
- atomic_dec(&cp->iscsi_conn);
- clear_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags);
+ if (!ret) {
+ atomic_dec(&cp->iscsi_conn);
+ clear_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags);
+ }
destroy_reply:
memset(&kcqe, 0, sizeof(kcqe));
@@ -1939,8 +1975,6 @@ static void cnic_init_storm_conn_bufs(struct cnic_dev *dev,
tstorm_buf->ka_interval = kwqe3->ka_interval;
tstorm_buf->ka_max_probe_count = kwqe3->ka_max_probe_count;
}
- tstorm_buf->rcv_buf = kwqe3->rcv_buf;
- tstorm_buf->snd_buf = kwqe3->snd_buf;
tstorm_buf->max_rt_time = 0xffffffff;
}
@@ -1969,15 +2003,14 @@ static void cnic_init_bnx2x_mac(struct cnic_dev *dev)
TSTORM_ISCSI_TCP_VARS_LSB_LOCAL_MAC_ADDR_OFFSET(pfid) + 1,
mac[4]);
CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
- TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(pfid), mac[3]);
+ TSTORM_ISCSI_TCP_VARS_MID_LOCAL_MAC_ADDR_OFFSET(pfid), mac[3]);
CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
- TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(pfid) + 1,
+ TSTORM_ISCSI_TCP_VARS_MID_LOCAL_MAC_ADDR_OFFSET(pfid) + 1,
mac[2]);
CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
- TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(pfid) + 2,
- mac[1]);
+ TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(pfid), mac[1]);
CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
- TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(pfid) + 3,
+ TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(pfid) + 1,
mac[0]);
}
@@ -2156,7 +2189,7 @@ static int cnic_bnx2x_fcoe_stat(struct cnic_dev *dev, struct kwqe *kwqe)
memset(fcoe_stat, 0, sizeof(*fcoe_stat));
memcpy(&fcoe_stat->stat_kwqe, req, sizeof(*req));
- ret = cnic_submit_kwqe_16(dev, FCOE_RAMROD_CMD_ID_STAT, cid,
+ ret = cnic_submit_kwqe_16(dev, FCOE_RAMROD_CMD_ID_STAT_FUNC, cid,
FCOE_CONNECTION_TYPE, &l5_data);
return ret;
}
@@ -2201,12 +2234,9 @@ static int cnic_bnx2x_fcoe_init1(struct cnic_dev *dev, struct kwqe *wqes[],
memcpy(&fcoe_init->init_kwqe1, req1, sizeof(*req1));
memcpy(&fcoe_init->init_kwqe2, req2, sizeof(*req2));
memcpy(&fcoe_init->init_kwqe3, req3, sizeof(*req3));
- fcoe_init->eq_addr.lo = cp->kcq2.dma.pg_map_arr[0] & 0xffffffff;
- fcoe_init->eq_addr.hi = (u64) cp->kcq2.dma.pg_map_arr[0] >> 32;
- fcoe_init->eq_next_page_addr.lo =
- cp->kcq2.dma.pg_map_arr[1] & 0xffffffff;
- fcoe_init->eq_next_page_addr.hi =
- (u64) cp->kcq2.dma.pg_map_arr[1] >> 32;
+ fcoe_init->eq_pbl_base.lo = cp->kcq2.dma.pgtbl_map & 0xffffffff;
+ fcoe_init->eq_pbl_base.hi = (u64) cp->kcq2.dma.pgtbl_map >> 32;
+ fcoe_init->eq_pbl_size = cp->kcq2.dma.num_pages;
fcoe_init->sb_num = cp->status_blk_num;
fcoe_init->eq_prod = MAX_KCQ_IDX;
@@ -2214,7 +2244,7 @@ static int cnic_bnx2x_fcoe_init1(struct cnic_dev *dev, struct kwqe *wqes[],
cp->kcq2.sw_prod_idx = 0;
cid = BNX2X_HW_CID(cp, cp->fcoe_init_cid);
- ret = cnic_submit_kwqe_16(dev, FCOE_RAMROD_CMD_ID_INIT, cid,
+ ret = cnic_submit_kwqe_16(dev, FCOE_RAMROD_CMD_ID_INIT_FUNC, cid,
FCOE_CONNECTION_TYPE, &l5_data);
*work = 3;
return ret;
@@ -2250,7 +2280,7 @@ static int cnic_bnx2x_fcoe_ofld1(struct cnic_dev *dev, struct kwqe *wqes[],
*work = 4;
l5_cid = req1->fcoe_conn_id;
- if (l5_cid >= BNX2X_FCOE_NUM_CONNECTIONS)
+ if (l5_cid >= dev->max_fcoe_conn)
goto err_reply;
l5_cid += BNX2X_FCOE_L5_CID_BASE;
@@ -2354,7 +2384,7 @@ static int cnic_bnx2x_fcoe_disable(struct cnic_dev *dev, struct kwqe *kwqe)
req = (struct fcoe_kwqe_conn_enable_disable *) kwqe;
cid = req->context_id;
l5_cid = req->conn_id;
- if (l5_cid >= BNX2X_FCOE_NUM_CONNECTIONS)
+ if (l5_cid >= dev->max_fcoe_conn)
return -EINVAL;
l5_cid += BNX2X_FCOE_L5_CID_BASE;
@@ -2388,7 +2418,7 @@ static int cnic_bnx2x_fcoe_destroy(struct cnic_dev *dev, struct kwqe *kwqe)
req = (struct fcoe_kwqe_conn_destroy *) kwqe;
cid = req->context_id;
l5_cid = req->conn_id;
- if (l5_cid >= BNX2X_FCOE_NUM_CONNECTIONS)
+ if (l5_cid >= dev->max_fcoe_conn)
return -EINVAL;
l5_cid += BNX2X_FCOE_L5_CID_BASE;
@@ -2398,17 +2428,20 @@ static int cnic_bnx2x_fcoe_destroy(struct cnic_dev *dev, struct kwqe *kwqe)
init_waitqueue_head(&ctx->waitq);
ctx->wait_cond = 0;
+ memset(&kcqe, 0, sizeof(kcqe));
+ kcqe.completion_status = FCOE_KCQE_COMPLETION_STATUS_ERROR;
memset(&l5_data, 0, sizeof(l5_data));
ret = cnic_submit_kwqe_16(dev, FCOE_RAMROD_CMD_ID_TERMINATE_CONN, cid,
FCOE_CONNECTION_TYPE, &l5_data);
if (ret == 0) {
- wait_event(ctx->waitq, ctx->wait_cond);
- set_bit(CTX_FL_DELETE_WAIT, &ctx->ctx_flags);
- queue_delayed_work(cnic_wq, &cp->delete_task,
- msecs_to_jiffies(2000));
+ wait_event_timeout(ctx->waitq, ctx->wait_cond, CNIC_RAMROD_TMO);
+ if (ctx->wait_cond)
+ kcqe.completion_status = 0;
}
- memset(&kcqe, 0, sizeof(kcqe));
+ set_bit(CTX_FL_DELETE_WAIT, &ctx->ctx_flags);
+ queue_delayed_work(cnic_wq, &cp->delete_task, msecs_to_jiffies(2000));
+
kcqe.op_code = FCOE_KCQE_OPCODE_DESTROY_CONN;
kcqe.fcoe_conn_id = req->conn_id;
kcqe.fcoe_conn_context_id = cid;
@@ -2418,6 +2451,30 @@ static int cnic_bnx2x_fcoe_destroy(struct cnic_dev *dev, struct kwqe *kwqe)
return ret;
}
+static void cnic_bnx2x_delete_wait(struct cnic_dev *dev, u32 start_cid)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ u32 i;
+
+ for (i = start_cid; i < cp->max_cid_space; i++) {
+ struct cnic_context *ctx = &cp->ctx_tbl[i];
+ int j;
+
+ while (test_bit(CTX_FL_DELETE_WAIT, &ctx->ctx_flags))
+ msleep(10);
+
+ for (j = 0; j < 5; j++) {
+ if (!test_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags))
+ break;
+ msleep(20);
+ }
+
+ if (test_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags))
+ netdev_warn(dev->netdev, "CID %x not deleted\n",
+ ctx->cid);
+ }
+}
+
static int cnic_bnx2x_fcoe_fw_destroy(struct cnic_dev *dev, struct kwqe *kwqe)
{
struct fcoe_kwqe_destroy *req;
@@ -2426,11 +2483,13 @@ static int cnic_bnx2x_fcoe_fw_destroy(struct cnic_dev *dev, struct kwqe *kwqe)
int ret;
u32 cid;
+ cnic_bnx2x_delete_wait(dev, MAX_ISCSI_TBL_SZ);
+
req = (struct fcoe_kwqe_destroy *) kwqe;
cid = BNX2X_HW_CID(cp, cp->fcoe_init_cid);
memset(&l5_data, 0, sizeof(l5_data));
- ret = cnic_submit_kwqe_16(dev, FCOE_RAMROD_CMD_ID_DESTROY, cid,
+ ret = cnic_submit_kwqe_16(dev, FCOE_RAMROD_CMD_ID_DESTROY_FUNC, cid,
FCOE_CONNECTION_TYPE, &l5_data);
return ret;
}
@@ -2511,7 +2570,7 @@ static int cnic_submit_bnx2x_fcoe_kwqes(struct cnic_dev *dev,
if (!test_bit(CNIC_F_CNIC_UP, &dev->flags))
return -EAGAIN; /* bnx2 is down */
- if (BNX2X_CHIP_NUM(cp->chip_id) == BNX2X_CHIP_NUM_57710)
+ if (!BNX2X_CHIP_IS_E2_PLUS(cp->chip_id))
return -EINVAL;
for (i = 0; i < num_wqes; ) {
@@ -2651,32 +2710,6 @@ end:
cnic_spq_completion(dev, DRV_CTL_RET_L5_SPQ_CREDIT_CMD, comp);
}
-static u16 cnic_bnx2_next_idx(u16 idx)
-{
- return idx + 1;
-}
-
-static u16 cnic_bnx2_hw_idx(u16 idx)
-{
- return idx;
-}
-
-static u16 cnic_bnx2x_next_idx(u16 idx)
-{
- idx++;
- if ((idx & MAX_KCQE_CNT) == MAX_KCQE_CNT)
- idx++;
-
- return idx;
-}
-
-static u16 cnic_bnx2x_hw_idx(u16 idx)
-{
- if ((idx & MAX_KCQE_CNT) == MAX_KCQE_CNT)
- idx++;
- return idx;
-}
-
static int cnic_get_kcqes(struct cnic_dev *dev, struct kcq_info *info)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -2687,12 +2720,12 @@ static int cnic_get_kcqes(struct cnic_dev *dev, struct kcq_info *info)
i = ri = last = info->sw_prod_idx;
ri &= MAX_KCQ_IDX;
hw_prod = *info->hw_prod_idx_ptr;
- hw_prod = cp->hw_idx(hw_prod);
+ hw_prod = info->hw_idx(hw_prod);
while ((i != hw_prod) && (kcqe_cnt < MAX_COMPLETED_KCQE)) {
kcqe = &info->kcq[KCQ_PG(ri)][KCQ_IDX(ri)];
cp->completed_kcq[kcqe_cnt++] = kcqe;
- i = cp->next_idx(i);
+ i = info->next_idx(i);
ri = i & MAX_KCQ_IDX;
if (likely(!(kcqe->kcqe_op_flag & KCQE_FLAGS_NEXT))) {
last_cnt = kcqe_cnt;
@@ -2778,13 +2811,10 @@ static u32 cnic_service_bnx2_queues(struct cnic_dev *dev)
/* Tell compiler that status_blk fields can change. */
barrier();
- if (status_idx != *cp->kcq1.status_idx_ptr) {
- status_idx = (u16) *cp->kcq1.status_idx_ptr;
- /* status block index must be read first */
- rmb();
- cp->kwq_con_idx = *cp->kwq_con_idx_ptr;
- } else
- break;
+ status_idx = (u16) *cp->kcq1.status_idx_ptr;
+ /* status block index must be read first */
+ rmb();
+ cp->kwq_con_idx = *cp->kwq_con_idx_ptr;
}
CNIC_WR16(dev, cp->kcq1.io_addr, cp->kcq1.sw_prod_idx);
@@ -2908,8 +2938,6 @@ static u32 cnic_service_bnx2x_kcq(struct cnic_dev *dev, struct kcq_info *info)
/* Tell compiler that sblk fields can change. */
barrier();
- if (last_status == *info->status_idx_ptr)
- break;
last_status = *info->status_idx_ptr;
/* status block index must be read before reading the KCQ */
@@ -2933,7 +2961,7 @@ static void cnic_service_bnx2x_bh(unsigned long data)
CNIC_WR16(dev, cp->kcq1.io_addr,
cp->kcq1.sw_prod_idx + MAX_KCQ_IDX);
- if (!BNX2X_CHIP_IS_E2(cp->chip_id)) {
+ if (!BNX2X_CHIP_IS_E2_PLUS(cp->chip_id)) {
cnic_ack_bnx2x_int(dev, cp->bnx2x_igu_sb_id, USTORM_ID,
status_idx, IGU_INT_ENABLE, 1);
break;
@@ -3052,13 +3080,21 @@ static int cnic_ctl(void *data, struct cnic_ctl_info *info)
break;
}
case CNIC_CTL_COMPLETION_CMD: {
- u32 cid = BNX2X_SW_CID(info->data.comp.cid);
+ struct cnic_ctl_completion *comp = &info->data.comp;
+ u32 cid = BNX2X_SW_CID(comp->cid);
u32 l5_cid;
struct cnic_local *cp = dev->cnic_priv;
if (cnic_get_l5_cid(cp, cid, &l5_cid) == 0) {
struct cnic_context *ctx = &cp->ctx_tbl[l5_cid];
+ if (unlikely(comp->error)) {
+ set_bit(CTX_FL_CID_ERROR, &ctx->ctx_flags);
+ netdev_err(dev->netdev,
+ "CID %x CFC delete comp error %x\n",
+ cid, comp->error);
+ }
+
ctx->wait_cond = 1;
wake_up(&ctx->waitq);
}
@@ -3772,7 +3808,13 @@ static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe)
break;
case L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED:
- cnic_cm_upcall(cp, csk, opcode);
+ /* after we already sent CLOSE_REQ */
+ if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags) &&
+ !test_bit(SK_F_OFFLD_COMPLETE, &csk->flags) &&
+ csk->state == L4_KCQE_OPCODE_VALUE_CLOSE_COMP)
+ cp->close_conn(csk, L4_KCQE_OPCODE_VALUE_RESET_COMP);
+ else
+ cnic_cm_upcall(cp, csk, opcode);
break;
}
csk_put(csk);
@@ -3803,14 +3845,17 @@ static void cnic_cm_free_mem(struct cnic_dev *dev)
static int cnic_cm_alloc_mem(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
+ u32 port_id;
cp->csk_tbl = kzalloc(sizeof(struct cnic_sock) * MAX_CM_SK_TBL_SZ,
GFP_KERNEL);
if (!cp->csk_tbl)
return -ENOMEM;
+ port_id = random32();
+ port_id %= CNIC_LOCAL_PORT_RANGE;
if (cnic_init_id_tbl(&cp->csk_port_tbl, CNIC_LOCAL_PORT_RANGE,
- CNIC_LOCAL_PORT_MIN)) {
+ CNIC_LOCAL_PORT_MIN, port_id)) {
cnic_cm_free_mem(dev);
return -ENOMEM;
}
@@ -3826,12 +3871,14 @@ static int cnic_ready_to_close(struct cnic_sock *csk, u32 opcode)
}
/* 1. If event opcode matches the expected event in csk->state
- * 2. If the expected event is CLOSE_COMP, we accept any event
+ * 2. If the expected event is CLOSE_COMP or RESET_COMP, we accept any
+ * event
* 3. If the expected event is 0, meaning the connection was never
* never established, we accept the opcode from cm_abort.
*/
if (opcode == csk->state || csk->state == 0 ||
- csk->state == L4_KCQE_OPCODE_VALUE_CLOSE_COMP) {
+ csk->state == L4_KCQE_OPCODE_VALUE_CLOSE_COMP ||
+ csk->state == L4_KCQE_OPCODE_VALUE_RESET_COMP) {
if (!test_and_set_bit(SK_F_CLOSING, &csk->flags)) {
if (csk->state == 0)
csk->state = opcode;
@@ -3865,7 +3912,7 @@ static int cnic_cm_init_bnx2_hw(struct cnic_dev *dev)
{
u32 seed;
- get_random_bytes(&seed, 4);
+ seed = random32();
cnic_ctx_wr(dev, 45, 0, seed);
return 0;
}
@@ -3912,7 +3959,6 @@ static void cnic_close_bnx2x_conn(struct cnic_sock *csk, u32 opcode)
static void cnic_cm_stop_bnx2x_hw(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
- int i;
if (!cp->ctx_tbl)
return;
@@ -3920,16 +3966,7 @@ static void cnic_cm_stop_bnx2x_hw(struct cnic_dev *dev)
if (!netif_running(dev->netdev))
return;
- for (i = 0; i < cp->max_cid_space; i++) {
- struct cnic_context *ctx = &cp->ctx_tbl[i];
-
- while (test_bit(CTX_FL_DELETE_WAIT, &ctx->ctx_flags))
- msleep(10);
-
- if (test_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags))
- netdev_warn(dev->netdev, "CID %x not deleted\n",
- ctx->cid);
- }
+ cnic_bnx2x_delete_wait(dev, 0);
cancel_delayed_work(&cp->delete_task);
flush_workqueue(cnic_wq);
@@ -3992,6 +4029,7 @@ static void cnic_delete_task(struct work_struct *work)
for (i = 0; i < cp->max_cid_space; i++) {
struct cnic_context *ctx = &cp->ctx_tbl[i];
+ int err;
if (!test_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags) ||
!test_bit(CTX_FL_DELETE_WAIT, &ctx->ctx_flags))
@@ -4005,13 +4043,15 @@ static void cnic_delete_task(struct work_struct *work)
if (!test_and_clear_bit(CTX_FL_DELETE_WAIT, &ctx->ctx_flags))
continue;
- cnic_bnx2x_destroy_ramrod(dev, i);
+ err = cnic_bnx2x_destroy_ramrod(dev, i);
cnic_free_bnx2x_conn_resc(dev, i);
- if (ctx->ulp_proto_id == CNIC_ULP_ISCSI)
- atomic_dec(&cp->iscsi_conn);
+ if (!err) {
+ if (ctx->ulp_proto_id == CNIC_ULP_ISCSI)
+ atomic_dec(&cp->iscsi_conn);
- clear_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags);
+ clear_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags);
+ }
}
if (need_resched)
@@ -4218,14 +4258,6 @@ static void cnic_enable_bnx2_int(struct cnic_dev *dev)
BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | cp->last_status_idx);
}
-static void cnic_get_bnx2_iscsi_info(struct cnic_dev *dev)
-{
- u32 max_conn;
-
- max_conn = cnic_reg_rd_ind(dev, BNX2_FW_MAX_ISCSI_CONN);
- dev->max_iscsi_conn = max_conn;
-}
-
static void cnic_disable_bnx2_int_sync(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -4291,7 +4323,7 @@ static void cnic_init_bnx2_tx_ring(struct cnic_dev *dev)
val = BNX2_L2CTX_CMD_TYPE_TYPE_L2 | (8 << 16);
cnic_ctx_wr(dev, cid_addr, offset1, val);
- txbd = (struct tx_bd *) udev->l2_ring;
+ txbd = udev->l2_ring;
buf_map = udev->l2_buf_map;
for (i = 0; i < MAX_TX_DESC_CNT; i++, txbd++) {
@@ -4350,7 +4382,7 @@ static void cnic_init_bnx2_rx_ring(struct cnic_dev *dev)
val = BNX2_L2CTX_L2_STATUSB_NUM(sb_id);
cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_HOST_BDIDX, val);
- rxbd = (struct rx_bd *) (udev->l2_ring + BCM_PAGE_SIZE);
+ rxbd = udev->l2_ring + BCM_PAGE_SIZE;
for (i = 0; i < MAX_RX_DESC_CNT; i++, rxbd++) {
dma_addr_t buf_map;
int n = (i % cp->l2_rx_ring_size) + 1;
@@ -4550,8 +4582,6 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev)
return err;
}
- cnic_get_bnx2_iscsi_info(dev);
-
return 0;
}
@@ -4617,7 +4647,7 @@ static void cnic_enable_bnx2x_int(struct cnic_dev *dev)
CSTORM_STATUS_BLOCK_DATA_OFFSET(sb_id) +
offsetof(struct hc_status_block_data_e1x, index_data) +
sizeof(struct hc_index_data)*HC_INDEX_ISCSI_EQ_CONS +
- offsetof(struct hc_index_data, timeout), 64 / 12);
+ offsetof(struct hc_index_data, timeout), 64 / 4);
cnic_storm_memset_hc_disable(dev, sb_id, HC_INDEX_ISCSI_EQ_CONS, 0);
}
@@ -4633,7 +4663,6 @@ static void cnic_init_bnx2x_tx_ring(struct cnic_dev *dev,
union eth_tx_bd_types *txbd = (union eth_tx_bd_types *) udev->l2_ring;
dma_addr_t buf_map, ring_map = udev->l2_ring_map;
struct host_sp_status_block *sb = cp->bnx2x_def_status_blk;
- int port = CNIC_PORT(cp);
int i;
u32 cli = cp->ethdev->iscsi_l2_client_id;
u32 val;
@@ -4674,10 +4703,9 @@ static void cnic_init_bnx2x_tx_ring(struct cnic_dev *dev,
/* reset xstorm per client statistics */
if (cli < MAX_STAT_COUNTER_ID) {
- val = BAR_XSTRORM_INTMEM +
- XSTORM_PER_COUNTER_ID_STATS_OFFSET(port, cli);
- for (i = 0; i < sizeof(struct xstorm_per_client_stats) / 4; i++)
- CNIC_WR(dev, val + i * 4, 0);
+ data->general.statistics_zero_flg = 1;
+ data->general.statistics_en_flg = 1;
+ data->general.statistics_counter_id = cli;
}
cp->tx_cons_ptr =
@@ -4695,7 +4723,6 @@ static void cnic_init_bnx2x_rx_ring(struct cnic_dev *dev,
(udev->l2_ring + (2 * BCM_PAGE_SIZE));
struct host_sp_status_block *sb = cp->bnx2x_def_status_blk;
int i;
- int port = CNIC_PORT(cp);
u32 cli = cp->ethdev->iscsi_l2_client_id;
int cl_qzone_id = BNX2X_CL_QZONE_ID(cp, cli);
u32 val;
@@ -4703,10 +4730,10 @@ static void cnic_init_bnx2x_rx_ring(struct cnic_dev *dev,
/* General data */
data->general.client_id = cli;
- data->general.statistics_en_flg = 1;
- data->general.statistics_counter_id = cli;
data->general.activate_flg = 1;
data->general.sp_client_id = cli;
+ data->general.mtu = cpu_to_le16(cp->l2_single_buf_size - 14);
+ data->general.func_id = cp->pfid;
for (i = 0; i < BNX2X_MAX_RX_DESC_CNT; i++, rxbd++) {
dma_addr_t buf_map;
@@ -4740,23 +4767,12 @@ static void cnic_init_bnx2x_rx_ring(struct cnic_dev *dev,
data->rx.status_block_id = BNX2X_DEF_SB_ID;
data->rx.cache_line_alignment_log_size = L1_CACHE_SHIFT;
- data->rx.bd_buff_size = cpu_to_le16(cp->l2_single_buf_size);
- data->rx.mtu = cpu_to_le16(cp->l2_single_buf_size - 14);
+ data->rx.max_bytes_on_bd = cpu_to_le16(cp->l2_single_buf_size);
data->rx.outer_vlan_removal_enable_flg = 1;
-
- /* reset tstorm and ustorm per client statistics */
- if (cli < MAX_STAT_COUNTER_ID) {
- val = BAR_TSTRORM_INTMEM +
- TSTORM_PER_COUNTER_ID_STATS_OFFSET(port, cli);
- for (i = 0; i < sizeof(struct tstorm_per_client_stats) / 4; i++)
- CNIC_WR(dev, val + i * 4, 0);
-
- val = BAR_USTRORM_INTMEM +
- USTORM_PER_COUNTER_ID_STATS_OFFSET(port, cli);
- for (i = 0; i < sizeof(struct ustorm_per_client_stats) / 4; i++)
- CNIC_WR(dev, val + i * 4, 0);
- }
+ data->rx.silent_vlan_removal_flg = 1;
+ data->rx.silent_vlan_value = 0;
+ data->rx.silent_vlan_mask = 0xffff;
cp->rx_cons_ptr =
&sb->sp_sb.index_values[HC_SP_INDEX_ETH_ISCSI_RX_CQ_CONS];
@@ -4772,7 +4788,7 @@ static void cnic_init_bnx2x_kcq(struct cnic_dev *dev)
CSTORM_ISCSI_EQ_PROD_OFFSET(pfid, 0);
cp->kcq1.sw_prod_idx = 0;
- if (BNX2X_CHIP_IS_E2(cp->chip_id)) {
+ if (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id)) {
struct host_hc_status_block_e2 *sb = cp->status_blk.gen;
cp->kcq1.hw_prod_idx_ptr =
@@ -4788,7 +4804,7 @@ static void cnic_init_bnx2x_kcq(struct cnic_dev *dev)
&sb->sb.running_index[SM_RX_ID];
}
- if (BNX2X_CHIP_IS_E2(cp->chip_id)) {
+ if (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id)) {
struct host_hc_status_block_e2 *sb = cp->status_blk.gen;
cp->kcq2.io_addr = BAR_USTRORM_INTMEM +
@@ -4805,10 +4821,12 @@ static int cnic_start_bnx2x_hw(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
struct cnic_eth_dev *ethdev = cp->ethdev;
- int func = CNIC_FUNC(cp), ret, i;
+ int func = CNIC_FUNC(cp), ret;
u32 pfid;
- if (BNX2X_CHIP_IS_E2(cp->chip_id)) {
+ cp->port_mode = CHIP_PORT_MODE_NONE;
+
+ if (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id)) {
u32 val = CNIC_RD(dev, MISC_REG_PORT4MODE_EN_OVWR);
if (!(val & 1))
@@ -4816,25 +4834,27 @@ static int cnic_start_bnx2x_hw(struct cnic_dev *dev)
else
val = (val >> 1) & 1;
- if (val)
+ if (val) {
+ cp->port_mode = CHIP_4_PORT_MODE;
cp->pfid = func >> 1;
- else
+ } else {
+ cp->port_mode = CHIP_2_PORT_MODE;
cp->pfid = func & 0x6;
+ }
} else {
cp->pfid = func;
}
pfid = cp->pfid;
ret = cnic_init_id_tbl(&cp->cid_tbl, MAX_ISCSI_TBL_SZ,
- cp->iscsi_start_cid);
+ cp->iscsi_start_cid, 0);
if (ret)
return -ENOMEM;
- if (BNX2X_CHIP_IS_E2(cp->chip_id)) {
- ret = cnic_init_id_tbl(&cp->fcoe_cid_tbl,
- BNX2X_FCOE_NUM_CONNECTIONS,
- cp->fcoe_start_cid);
+ if (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id)) {
+ ret = cnic_init_id_tbl(&cp->fcoe_cid_tbl, dev->max_fcoe_conn,
+ cp->fcoe_start_cid, 0);
if (ret)
return -ENOMEM;
@@ -4868,15 +4888,6 @@ static int cnic_start_bnx2x_hw(struct cnic_dev *dev)
CSTORM_ISCSI_EQ_SB_INDEX_OFFSET(pfid, 0),
HC_INDEX_ISCSI_EQ_CONS);
- for (i = 0; i < cp->conn_buf_info.num_pages; i++) {
- CNIC_WR(dev, BAR_TSTRORM_INTMEM +
- TSTORM_ISCSI_CONN_BUF_PBL_OFFSET(pfid, i),
- cp->conn_buf_info.pgtbl[2 * i]);
- CNIC_WR(dev, BAR_TSTRORM_INTMEM +
- TSTORM_ISCSI_CONN_BUF_PBL_OFFSET(pfid, i) + 4,
- cp->conn_buf_info.pgtbl[(2 * i) + 1]);
- }
-
CNIC_WR(dev, BAR_USTRORM_INTMEM +
USTORM_ISCSI_GLOBAL_BUF_PHYS_ADDR_OFFSET(pfid),
cp->gbl_buf_info.pg_map_arr[0] & 0xffffffff);
@@ -4915,7 +4926,7 @@ static void cnic_init_rings(struct cnic_dev *dev)
struct client_init_ramrod_data *data;
union l5cm_specific_data l5_data;
struct ustorm_eth_rx_producers rx_prods = {0};
- u32 off, i;
+ u32 off, i, *cid_ptr;
rx_prods.bd_prod = 0;
rx_prods.cqe_prod = BNX2X_MAX_RCQ_DESC_CNT;
@@ -4924,7 +4935,7 @@ static void cnic_init_rings(struct cnic_dev *dev)
cl_qzone_id = BNX2X_CL_QZONE_ID(cp, cli);
off = BAR_USTRORM_INTMEM +
- (BNX2X_CHIP_IS_E2(cp->chip_id) ?
+ (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id) ?
USTORM_RX_PRODS_E2_OFFSET(cl_qzone_id) :
USTORM_RX_PRODS_E1X_OFFSET(CNIC_PORT(cp), cli));
@@ -4934,6 +4945,7 @@ static void cnic_init_rings(struct cnic_dev *dev)
set_bit(CNIC_LCL_FL_L2_WAIT, &cp->cnic_local_flags);
data = udev->l2_buf;
+ cid_ptr = udev->l2_buf + 12;
memset(data, 0, sizeof(*data));
@@ -4958,12 +4970,15 @@ static void cnic_init_rings(struct cnic_dev *dev)
"iSCSI CLIENT_SETUP did not complete\n");
cnic_spq_completion(dev, DRV_CTL_RET_L2_SPQ_CREDIT_CMD, 1);
cnic_ring_ctl(dev, cid, cli, 1);
+ *cid_ptr = cid;
}
}
static void cnic_shutdown_rings(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_uio_dev *udev = cp->udev;
+ void *rx_ring;
if (!test_bit(CNIC_LCL_FL_RINGS_INITED, &cp->cnic_local_flags))
return;
@@ -4971,7 +4986,6 @@ static void cnic_shutdown_rings(struct cnic_dev *dev)
if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) {
cnic_shutdown_bnx2_rx_ring(dev);
} else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) {
- struct cnic_local *cp = dev->cnic_priv;
u32 cli = cp->ethdev->iscsi_l2_client_id;
u32 cid = cp->ethdev->iscsi_l2_cid;
union l5cm_specific_data l5_data;
@@ -5001,6 +5015,8 @@ static void cnic_shutdown_rings(struct cnic_dev *dev)
msleep(10);
}
clear_bit(CNIC_LCL_FL_RINGS_INITED, &cp->cnic_local_flags);
+ rx_ring = udev->l2_ring + BCM_PAGE_SIZE;
+ memset(rx_ring, 0, BCM_PAGE_SIZE);
}
static int cnic_register_netdev(struct cnic_dev *dev)
@@ -5217,6 +5233,8 @@ static struct cnic_dev *init_bnx2_cnic(struct net_device *dev)
cdev->pcidev = pdev;
cp->chip_id = ethdev->chip_id;
+ cdev->max_iscsi_conn = ethdev->max_iscsi_conn;
+
cp->cnic_ops = &cnic_bnx2_ops;
cp->start_hw = cnic_start_bnx2_hw;
cp->stop_hw = cnic_stop_bnx2_hw;
@@ -5228,8 +5246,6 @@ static struct cnic_dev *init_bnx2_cnic(struct net_device *dev)
cp->enable_int = cnic_enable_bnx2_int;
cp->disable_int_sync = cnic_disable_bnx2_int_sync;
cp->close_conn = cnic_close_bnx2_conn;
- cp->next_idx = cnic_bnx2_next_idx;
- cp->hw_idx = cnic_bnx2_hw_idx;
return cdev;
cnic_err:
@@ -5274,10 +5290,13 @@ static struct cnic_dev *init_bnx2x_cnic(struct net_device *dev)
if (!(ethdev->drv_state & CNIC_DRV_STATE_NO_ISCSI))
cdev->max_iscsi_conn = ethdev->max_iscsi_conn;
- if (BNX2X_CHIP_IS_E2(cp->chip_id) &&
+ if (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id) &&
!(ethdev->drv_state & CNIC_DRV_STATE_NO_FCOE))
cdev->max_fcoe_conn = ethdev->max_fcoe_conn;
+ if (cdev->max_fcoe_conn > BNX2X_FCOE_NUM_CONNECTIONS)
+ cdev->max_fcoe_conn = BNX2X_FCOE_NUM_CONNECTIONS;
+
memcpy(cdev->mac_addr, ethdev->iscsi_mac, 6);
cp->cnic_ops = &cnic_bnx2x_ops;
@@ -5290,13 +5309,11 @@ static struct cnic_dev *init_bnx2x_cnic(struct net_device *dev)
cp->stop_cm = cnic_cm_stop_bnx2x_hw;
cp->enable_int = cnic_enable_bnx2x_int;
cp->disable_int_sync = cnic_disable_bnx2x_int_sync;
- if (BNX2X_CHIP_IS_E2(cp->chip_id))
+ if (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id))
cp->ack_int = cnic_ack_bnx2x_e2_msix;
else
cp->ack_int = cnic_ack_bnx2x_msix;
cp->close_conn = cnic_close_bnx2x_conn;
- cp->next_idx = cnic_bnx2x_next_idx;
- cp->hw_idx = cnic_bnx2x_hw_idx;
return cdev;
}
@@ -5322,6 +5339,27 @@ static struct cnic_dev *is_cnic_dev(struct net_device *dev)
return cdev;
}
+static void cnic_rcv_netevent(struct cnic_local *cp, unsigned long event,
+ u16 vlan_id)
+{
+ int if_type;
+
+ rcu_read_lock();
+ for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+ struct cnic_ulp_ops *ulp_ops;
+ void *ctx;
+
+ ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+ if (!ulp_ops || !ulp_ops->indicate_netevent)
+ continue;
+
+ ctx = cp->ulp_handle[if_type];
+
+ ulp_ops->indicate_netevent(ctx, event, vlan_id);
+ }
+ rcu_read_unlock();
+}
+
/**
* netdev event handler
*/
@@ -5330,12 +5368,11 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
{
struct net_device *netdev = ptr;
struct cnic_dev *dev;
- int if_type;
int new_dev = 0;
dev = cnic_from_netdev(netdev);
- if (!dev && (event == NETDEV_REGISTER || event == NETDEV_UP)) {
+ if (!dev && (event == NETDEV_REGISTER || netif_running(netdev))) {
/* Check for the hot-plug device */
dev = is_cnic_dev(netdev);
if (dev) {
@@ -5351,7 +5388,7 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
else if (event == NETDEV_UNREGISTER)
cnic_ulp_exit(dev);
- if (event == NETDEV_UP) {
+ if (event == NETDEV_UP || (new_dev && netif_running(netdev))) {
if (cnic_register_netdev(dev) != 0) {
cnic_put(dev);
goto done;
@@ -5360,20 +5397,7 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
cnic_ulp_start(dev);
}
- rcu_read_lock();
- for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
- struct cnic_ulp_ops *ulp_ops;
- void *ctx;
-
- ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
- if (!ulp_ops || !ulp_ops->indicate_netevent)
- continue;
-
- ctx = cp->ulp_handle[if_type];
-
- ulp_ops->indicate_netevent(ctx, event);
- }
- rcu_read_unlock();
+ cnic_rcv_netevent(cp, event, 0);
if (event == NETDEV_GOING_DOWN) {
cnic_ulp_stop(dev);
@@ -5389,6 +5413,19 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
goto done;
}
cnic_put(dev);
+ } else {
+ struct net_device *realdev;
+ u16 vid;
+
+ vid = cnic_get_vlan(netdev, &realdev);
+ if (realdev) {
+ dev = cnic_from_netdev(realdev);
+ if (dev) {
+ vid |= VLAN_TAG_PRESENT;
+ cnic_rcv_netevent(dev->cnic_priv, event, vid);
+ cnic_put(dev);
+ }
+ }
}
done:
return NOTIFY_DONE;
diff --git a/drivers/net/cnic.h b/drivers/net/ethernet/broadcom/cnic.h
index 3367a6d3a774..30328097f516 100644
--- a/drivers/net/cnic.h
+++ b/drivers/net/ethernet/broadcom/cnic.h
@@ -1,6 +1,6 @@
/* cnic.h: Broadcom CNIC core network driver.
*
- * Copyright (c) 2006-2010 Broadcom Corporation
+ * Copyright (c) 2006-2011 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -68,11 +68,6 @@
#define BNX2_PG_CTX_MAP 0x1a0034
#define BNX2_ISCSI_CTX_MAP 0x1a0074
-struct cnic_redirect_entry {
- struct dst_entry *old_dst;
- struct dst_entry *new_dst;
-};
-
#define MAX_COMPLETED_KCQE 64
#define MAX_CNIC_L5_CONTEXT 256
@@ -171,6 +166,7 @@ struct cnic_context {
unsigned long ctx_flags;
#define CTX_FL_OFFLD_START 0
#define CTX_FL_DELETE_WAIT 1
+#define CTX_FL_CID_ERROR 2
u8 ulp_proto_id;
union {
struct cnic_iscsi *iscsi;
@@ -185,6 +181,9 @@ struct kcq_info {
u16 sw_prod_idx;
u16 *status_idx_ptr;
u32 io_addr;
+
+ u16 (*next_idx)(u16);
+ u16 (*hw_idx)(u16);
};
struct iro {
@@ -242,7 +241,7 @@ struct cnic_local {
u16 rx_cons;
u16 tx_cons;
- struct iro *iro_arr;
+ const struct iro *iro_arr;
#define IRO (((struct cnic_local *) dev->cnic_priv)->iro_arr)
struct cnic_dma kwq_info;
@@ -283,7 +282,6 @@ struct cnic_local {
struct cnic_sock *csk_tbl;
struct cnic_id_tbl csk_port_tbl;
- struct cnic_dma conn_buf_info;
struct cnic_dma gbl_buf_info;
struct cnic_iscsi *iscsi_tbl;
@@ -317,6 +315,11 @@ struct cnic_local {
u32 chip_id;
int func;
u32 pfid;
+ u8 port_mode;
+#define CHIP_4_PORT_MODE 0
+#define CHIP_2_PORT_MODE 1
+#define CHIP_PORT_MODE_NONE 2
+
u32 shmem_base;
struct cnic_ops *cnic_ops;
@@ -332,8 +335,6 @@ struct cnic_local {
void (*disable_int_sync)(struct cnic_dev *);
void (*ack_int)(struct cnic_dev *);
void (*close_conn)(struct cnic_sock *, u32 opcode);
- u16 (*next_idx)(u16);
- u16 (*hw_idx)(u16);
};
struct bnx2x_bd_chain_next {
@@ -368,12 +369,11 @@ struct bnx2x_bd_chain_next {
#define BNX2X_ISCSI_MAX_PENDING_R2TS 4
#define BNX2X_ISCSI_R2TQE_SIZE 8
#define BNX2X_ISCSI_HQ_BD_SIZE 64
-#define BNX2X_ISCSI_CONN_BUF_SIZE 64
#define BNX2X_ISCSI_GLB_BUF_SIZE 64
#define BNX2X_ISCSI_PBL_NOT_CACHED 0xff
#define BNX2X_ISCSI_PDU_HEADER_NOT_CACHED 0xff
-#define BNX2X_FCOE_NUM_CONNECTIONS 128
+#define BNX2X_FCOE_NUM_CONNECTIONS 1024
#define BNX2X_FCOE_L5_CID_BASE MAX_ISCSI_TBL_SZ
@@ -384,6 +384,9 @@ struct bnx2x_bd_chain_next {
#define BNX2X_CHIP_NUM_57712E 0x1663
#define BNX2X_CHIP_NUM_57713 0x1651
#define BNX2X_CHIP_NUM_57713E 0x1652
+#define BNX2X_CHIP_NUM_57800 0x168a
+#define BNX2X_CHIP_NUM_57810 0x168e
+#define BNX2X_CHIP_NUM_57840 0x168d
#define BNX2X_CHIP_NUM(x) (x >> 16)
#define BNX2X_CHIP_IS_57710(x) \
@@ -402,9 +405,19 @@ struct bnx2x_bd_chain_next {
(BNX2X_CHIP_NUM(x) == BNX2X_CHIP_NUM_57713)
#define BNX2X_CHIP_IS_57713E(x) \
(BNX2X_CHIP_NUM(x) == BNX2X_CHIP_NUM_57713E)
+#define BNX2X_CHIP_IS_57800(x) \
+ (BNX2X_CHIP_NUM(x) == BNX2X_CHIP_NUM_57800)
+#define BNX2X_CHIP_IS_57810(x) \
+ (BNX2X_CHIP_NUM(x) == BNX2X_CHIP_NUM_57810)
+#define BNX2X_CHIP_IS_57840(x) \
+ (BNX2X_CHIP_NUM(x) == BNX2X_CHIP_NUM_57840)
#define BNX2X_CHIP_IS_E2(x) \
(BNX2X_CHIP_IS_57712(x) || BNX2X_CHIP_IS_57712E(x) || \
BNX2X_CHIP_IS_57713(x) || BNX2X_CHIP_IS_57713E(x))
+#define BNX2X_CHIP_IS_E3(x) \
+ (BNX2X_CHIP_IS_57800(x) || BNX2X_CHIP_IS_57810(x) || \
+ BNX2X_CHIP_IS_57840(x))
+#define BNX2X_CHIP_IS_E2_PLUS(x) (BNX2X_CHIP_IS_E2(x) || BNX2X_CHIP_IS_E3(x))
#define IS_E1H_OFFSET BNX2X_CHIP_IS_E1H(cp->chip_id)
@@ -441,8 +454,8 @@ struct bnx2x_bd_chain_next {
#define CNIC_PORT(cp) ((cp)->pfid & 1)
#define CNIC_FUNC(cp) ((cp)->func)
-#define CNIC_PATH(cp) (!BNX2X_CHIP_IS_E2(cp->chip_id) ? 0 :\
- (CNIC_FUNC(cp) & 1))
+#define CNIC_PATH(cp) (!BNX2X_CHIP_IS_E2_PLUS(cp->chip_id) ? \
+ 0 : (CNIC_FUNC(cp) & 1))
#define CNIC_E1HVN(cp) ((cp)->pfid >> 1)
#define BNX2X_HW_CID(cp, x) ((CNIC_PORT(cp) << 23) | \
@@ -451,10 +464,17 @@ struct bnx2x_bd_chain_next {
#define BNX2X_SW_CID(x) (x & 0x1ffff)
#define BNX2X_CL_QZONE_ID(cp, cli) \
- (cli + (CNIC_PORT(cp) * (BNX2X_CHIP_IS_E2(cp->chip_id) ?\
- ETH_MAX_RX_CLIENTS_E2 : \
- ETH_MAX_RX_CLIENTS_E1H)))
+ (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id) ? cli : \
+ cli + (CNIC_PORT(cp) * ETH_MAX_RX_CLIENTS_E1H))
+
+#ifndef MAX_STAT_COUNTER_ID
+#define MAX_STAT_COUNTER_ID \
+ (BNX2X_CHIP_IS_E1H((cp)->chip_id) ? MAX_STAT_COUNTER_ID_E1H : \
+ ((BNX2X_CHIP_IS_E2_PLUS((cp)->chip_id)) ? MAX_STAT_COUNTER_ID_E2 :\
+ MAX_STAT_COUNTER_ID_E1))
+#endif
+
+#define CNIC_RAMROD_TMO (HZ / 4)
-#define TCP_TSTORM_OOO_DROP_AND_PROC_ACK (0<<4)
#endif
diff --git a/drivers/net/cnic_defs.h b/drivers/net/ethernet/broadcom/cnic_defs.h
index fdbc00415603..239de898f071 100644
--- a/drivers/net/cnic_defs.h
+++ b/drivers/net/ethernet/broadcom/cnic_defs.h
@@ -1,7 +1,7 @@
/* cnic.c: Broadcom CNIC core network driver.
*
- * Copyright (c) 2006-2010 Broadcom Corporation
+ * Copyright (c) 2006-2009 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -45,13 +45,13 @@
#define FCOE_KCQE_OPCODE_CQ_EVENT_NOTIFICATION (0x20)
#define FCOE_KCQE_OPCODE_FCOE_ERROR (0x21)
-#define FCOE_RAMROD_CMD_ID_INIT (FCOE_KCQE_OPCODE_INIT_FUNC)
-#define FCOE_RAMROD_CMD_ID_DESTROY (FCOE_KCQE_OPCODE_DESTROY_FUNC)
+#define FCOE_RAMROD_CMD_ID_INIT_FUNC (FCOE_KCQE_OPCODE_INIT_FUNC)
+#define FCOE_RAMROD_CMD_ID_DESTROY_FUNC (FCOE_KCQE_OPCODE_DESTROY_FUNC)
+#define FCOE_RAMROD_CMD_ID_STAT_FUNC (FCOE_KCQE_OPCODE_STAT_FUNC)
#define FCOE_RAMROD_CMD_ID_OFFLOAD_CONN (FCOE_KCQE_OPCODE_OFFLOAD_CONN)
#define FCOE_RAMROD_CMD_ID_ENABLE_CONN (FCOE_KCQE_OPCODE_ENABLE_CONN)
#define FCOE_RAMROD_CMD_ID_DISABLE_CONN (FCOE_KCQE_OPCODE_DISABLE_CONN)
#define FCOE_RAMROD_CMD_ID_DESTROY_CONN (FCOE_KCQE_OPCODE_DESTROY_CONN)
-#define FCOE_RAMROD_CMD_ID_STAT (FCOE_KCQE_OPCODE_STAT_FUNC)
#define FCOE_RAMROD_CMD_ID_TERMINATE_CONN (0x81)
#define FCOE_KWQE_OPCODE_INIT1 (0)
@@ -67,6 +67,7 @@
#define FCOE_KWQE_OPCODE_DESTROY (10)
#define FCOE_KWQE_OPCODE_STAT (11)
+#define FCOE_KCQE_COMPLETION_STATUS_ERROR (0x1)
#define FCOE_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE (0x3)
/* KCQ (kernel completion queue) response op codes */
@@ -641,20 +642,20 @@ struct cstorm_iscsi_ag_context {
#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_SE_CF_EN_SHIFT 12
#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_INV_CF_EN (0x1<<13)
#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_INV_CF_EN_SHIFT 13
-#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF (0x3<<14)
-#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF_SHIFT 14
+#define __CSTORM_ISCSI_AG_CONTEXT_AUX4_CF (0x3<<14)
+#define __CSTORM_ISCSI_AG_CONTEXT_AUX4_CF_SHIFT 14
#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED66 (0x3<<16)
#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED66_SHIFT 16
#define __CSTORM_ISCSI_AG_CONTEXT_FIN_RECEIVED_CF_EN (0x1<<18)
#define __CSTORM_ISCSI_AG_CONTEXT_FIN_RECEIVED_CF_EN_SHIFT 18
-#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION0_CF_EN (0x1<<19)
-#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION0_CF_EN_SHIFT 19
-#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION1_CF_EN (0x1<<20)
-#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION1_CF_EN_SHIFT 20
-#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION2_CF_EN (0x1<<21)
-#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION2_CF_EN_SHIFT 21
-#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF_EN (0x1<<22)
-#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF_EN_SHIFT 22
+#define __CSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN (0x1<<19)
+#define __CSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN_SHIFT 19
+#define __CSTORM_ISCSI_AG_CONTEXT_AUX2_CF_EN (0x1<<20)
+#define __CSTORM_ISCSI_AG_CONTEXT_AUX2_CF_EN_SHIFT 20
+#define __CSTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<21)
+#define __CSTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 21
+#define __CSTORM_ISCSI_AG_CONTEXT_AUX4_CF_EN (0x1<<22)
+#define __CSTORM_ISCSI_AG_CONTEXT_AUX4_CF_EN_SHIFT 22
#define __CSTORM_ISCSI_AG_CONTEXT_REL_SEQ_RULE (0x7<<23)
#define __CSTORM_ISCSI_AG_CONTEXT_REL_SEQ_RULE_SHIFT 23
#define CSTORM_ISCSI_AG_CONTEXT_HQ_PROD_RULE (0x3<<26)
@@ -694,573 +695,667 @@ struct cstorm_iscsi_ag_context {
#endif
#if defined(__BIG_ENDIAN)
u16 __reserved64;
- u16 __cq_u_prod0;
+ u16 cq_u_prod;
#elif defined(__LITTLE_ENDIAN)
- u16 __cq_u_prod0;
+ u16 cq_u_prod;
u16 __reserved64;
#endif
u32 __cq_u_prod1;
#if defined(__BIG_ENDIAN)
u16 __agg_vars3;
- u16 __cq_u_prod2;
+ u16 cq_u_pend;
#elif defined(__LITTLE_ENDIAN)
- u16 __cq_u_prod2;
+ u16 cq_u_pend;
u16 __agg_vars3;
#endif
#if defined(__BIG_ENDIAN)
u16 __aux2_th;
- u16 __cq_u_prod3;
+ u16 aux2_val;
#elif defined(__LITTLE_ENDIAN)
- u16 __cq_u_prod3;
+ u16 aux2_val;
u16 __aux2_th;
#endif
};
/*
- * Parameters initialized during offloaded according to FLOGI/PLOGI/PRLI and used in FCoE context section
+ * The fcoe extra aggregative context section of Tstorm
*/
-struct ustorm_fcoe_params {
-#if defined(__BIG_ENDIAN)
- u16 fcoe_conn_id;
- u16 flags;
-#define USTORM_FCOE_PARAMS_B_MUL_N_PORT_IDS (0x1<<0)
-#define USTORM_FCOE_PARAMS_B_MUL_N_PORT_IDS_SHIFT 0
-#define USTORM_FCOE_PARAMS_B_E_D_TOV_RES (0x1<<1)
-#define USTORM_FCOE_PARAMS_B_E_D_TOV_RES_SHIFT 1
-#define USTORM_FCOE_PARAMS_B_CONT_INCR_SEQ_CNT (0x1<<2)
-#define USTORM_FCOE_PARAMS_B_CONT_INCR_SEQ_CNT_SHIFT 2
-#define USTORM_FCOE_PARAMS_B_CONF_REQ (0x1<<3)
-#define USTORM_FCOE_PARAMS_B_CONF_REQ_SHIFT 3
-#define USTORM_FCOE_PARAMS_B_REC_VALID (0x1<<4)
-#define USTORM_FCOE_PARAMS_B_REC_VALID_SHIFT 4
-#define USTORM_FCOE_PARAMS_B_CQ_TOGGLE_BIT (0x1<<5)
-#define USTORM_FCOE_PARAMS_B_CQ_TOGGLE_BIT_SHIFT 5
-#define USTORM_FCOE_PARAMS_B_XFRQ_TOGGLE_BIT (0x1<<6)
-#define USTORM_FCOE_PARAMS_B_XFRQ_TOGGLE_BIT_SHIFT 6
-#define USTORM_FCOE_PARAMS_B_C2_VALID (0x1<<7)
-#define USTORM_FCOE_PARAMS_B_C2_VALID_SHIFT 7
-#define USTORM_FCOE_PARAMS_B_ACK_0 (0x1<<8)
-#define USTORM_FCOE_PARAMS_B_ACK_0_SHIFT 8
-#define USTORM_FCOE_PARAMS_RSRV0 (0x7F<<9)
-#define USTORM_FCOE_PARAMS_RSRV0_SHIFT 9
-#elif defined(__LITTLE_ENDIAN)
- u16 flags;
-#define USTORM_FCOE_PARAMS_B_MUL_N_PORT_IDS (0x1<<0)
-#define USTORM_FCOE_PARAMS_B_MUL_N_PORT_IDS_SHIFT 0
-#define USTORM_FCOE_PARAMS_B_E_D_TOV_RES (0x1<<1)
-#define USTORM_FCOE_PARAMS_B_E_D_TOV_RES_SHIFT 1
-#define USTORM_FCOE_PARAMS_B_CONT_INCR_SEQ_CNT (0x1<<2)
-#define USTORM_FCOE_PARAMS_B_CONT_INCR_SEQ_CNT_SHIFT 2
-#define USTORM_FCOE_PARAMS_B_CONF_REQ (0x1<<3)
-#define USTORM_FCOE_PARAMS_B_CONF_REQ_SHIFT 3
-#define USTORM_FCOE_PARAMS_B_REC_VALID (0x1<<4)
-#define USTORM_FCOE_PARAMS_B_REC_VALID_SHIFT 4
-#define USTORM_FCOE_PARAMS_B_CQ_TOGGLE_BIT (0x1<<5)
-#define USTORM_FCOE_PARAMS_B_CQ_TOGGLE_BIT_SHIFT 5
-#define USTORM_FCOE_PARAMS_B_XFRQ_TOGGLE_BIT (0x1<<6)
-#define USTORM_FCOE_PARAMS_B_XFRQ_TOGGLE_BIT_SHIFT 6
-#define USTORM_FCOE_PARAMS_B_C2_VALID (0x1<<7)
-#define USTORM_FCOE_PARAMS_B_C2_VALID_SHIFT 7
-#define USTORM_FCOE_PARAMS_B_ACK_0 (0x1<<8)
-#define USTORM_FCOE_PARAMS_B_ACK_0_SHIFT 8
-#define USTORM_FCOE_PARAMS_RSRV0 (0x7F<<9)
-#define USTORM_FCOE_PARAMS_RSRV0_SHIFT 9
- u16 fcoe_conn_id;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 hc_csdm_byte_en;
- u8 func_id;
- u8 port_id;
- u8 vnic_id;
-#elif defined(__LITTLE_ENDIAN)
- u8 vnic_id;
- u8 port_id;
- u8 func_id;
- u8 hc_csdm_byte_en;
-#endif
+struct tstorm_fcoe_extra_ag_context_section {
+ u32 __agg_val1;
#if defined(__BIG_ENDIAN)
- u16 rx_total_conc_seqs;
- u16 rx_max_fc_pay_len;
+ u8 __tcp_agg_vars2;
+ u8 __agg_val3;
+ u16 __agg_val2;
#elif defined(__LITTLE_ENDIAN)
- u16 rx_max_fc_pay_len;
- u16 rx_total_conc_seqs;
+ u16 __agg_val2;
+ u8 __agg_val3;
+ u8 __tcp_agg_vars2;
#endif
#if defined(__BIG_ENDIAN)
- u16 ox_id;
- u16 rx_max_conc_seqs;
+ u16 __agg_val5;
+ u8 __agg_val6;
+ u8 __tcp_agg_vars3;
#elif defined(__LITTLE_ENDIAN)
- u16 rx_max_conc_seqs;
- u16 ox_id;
+ u8 __tcp_agg_vars3;
+ u8 __agg_val6;
+ u16 __agg_val5;
#endif
+ u32 __lcq_prod;
+ u32 rtt_seq;
+ u32 rtt_time;
+ u32 __reserved66;
+ u32 wnd_right_edge;
+ u32 tcp_agg_vars1;
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_FIN_SENT_FLAG (0x1<<0)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_FIN_SENT_FLAG_SHIFT 0
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG (0x1<<1)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG_SHIFT 1
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_WND_UPD_CF (0x3<<2)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_WND_UPD_CF_SHIFT 2
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TIMEOUT_CF (0x3<<4)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TIMEOUT_CF_SHIFT 4
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_WND_UPD_CF_EN (0x1<<6)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_WND_UPD_CF_EN_SHIFT 6
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TIMEOUT_CF_EN (0x1<<7)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TIMEOUT_CF_EN_SHIFT 7
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN (0x1<<8)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN_SHIFT 8
+#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_LCQ_SND_EN (0x1<<9)
+#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_LCQ_SND_EN_SHIFT 9
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<10)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 10
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_FLAG (0x1<<11)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_FLAG_SHIFT 11
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_CF_EN (0x1<<12)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_CF_EN_SHIFT 12
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_CF_EN (0x1<<13)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_CF_EN_SHIFT 13
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_CF (0x3<<14)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_CF_SHIFT 14
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_CF (0x3<<16)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_CF_SHIFT 16
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TX_BLOCKED (0x1<<18)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TX_BLOCKED_SHIFT 18
+#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX10_CF_EN (0x1<<19)
+#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX10_CF_EN_SHIFT 19
+#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX11_CF_EN (0x1<<20)
+#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX11_CF_EN_SHIFT 20
+#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX12_CF_EN (0x1<<21)
+#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX12_CF_EN_SHIFT 21
+#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED1 (0x3<<22)
+#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED1_SHIFT 22
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ (0xF<<24)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ_SHIFT 24
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ (0xF<<28)
+#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ_SHIFT 28
+ u32 snd_max;
+ u32 __lcq_cons;
+ u32 __reserved2;
};
/*
- * FCoE 16-bits index structure
- */
-struct fcoe_idx16_fields {
- u16 fields;
-#define FCOE_IDX16_FIELDS_IDX (0x7FFF<<0)
-#define FCOE_IDX16_FIELDS_IDX_SHIFT 0
-#define FCOE_IDX16_FIELDS_MSB (0x1<<15)
-#define FCOE_IDX16_FIELDS_MSB_SHIFT 15
-};
-
-/*
- * FCoE 16-bits index union
- */
-union fcoe_idx16_field_union {
- struct fcoe_idx16_fields fields;
- u16 val;
-};
-
-/*
- * 4 regs size
+ * The fcoe aggregative context of Tstorm
*/
-struct fcoe_bd_ctx {
- u32 buf_addr_hi;
- u32 buf_addr_lo;
+struct tstorm_fcoe_ag_context {
#if defined(__BIG_ENDIAN)
- u16 rsrv0;
- u16 buf_len;
+ u16 ulp_credit;
+ u8 agg_vars1;
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF (0x3<<4)
+#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_SHIFT 4
+#define __TSTORM_FCOE_AG_CONTEXT_AUX3_FLAG (0x1<<6)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX3_FLAG_SHIFT 6
+#define __TSTORM_FCOE_AG_CONTEXT_AUX4_FLAG (0x1<<7)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX4_FLAG_SHIFT 7
+ u8 state;
#elif defined(__LITTLE_ENDIAN)
- u16 buf_len;
- u16 rsrv0;
+ u8 state;
+ u8 agg_vars1;
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF (0x3<<4)
+#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_SHIFT 4
+#define __TSTORM_FCOE_AG_CONTEXT_AUX3_FLAG (0x1<<6)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX3_FLAG_SHIFT 6
+#define __TSTORM_FCOE_AG_CONTEXT_AUX4_FLAG (0x1<<7)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX4_FLAG_SHIFT 7
+ u16 ulp_credit;
#endif
#if defined(__BIG_ENDIAN)
- u16 rsrv1;
- u16 flags;
+ u16 __agg_val4;
+ u16 agg_vars2;
+#define __TSTORM_FCOE_AG_CONTEXT_AUX5_FLAG (0x1<<0)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX5_FLAG_SHIFT 0
+#define __TSTORM_FCOE_AG_CONTEXT_AUX6_FLAG (0x1<<1)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX6_FLAG_SHIFT 1
+#define __TSTORM_FCOE_AG_CONTEXT_AUX4_CF (0x3<<2)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX4_CF_SHIFT 2
+#define __TSTORM_FCOE_AG_CONTEXT_AUX5_CF (0x3<<4)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX5_CF_SHIFT 4
+#define __TSTORM_FCOE_AG_CONTEXT_AUX6_CF (0x3<<6)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX6_CF_SHIFT 6
+#define __TSTORM_FCOE_AG_CONTEXT_AUX7_CF (0x3<<8)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX7_CF_SHIFT 8
+#define __TSTORM_FCOE_AG_CONTEXT_AUX7_FLAG (0x1<<10)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX7_FLAG_SHIFT 10
+#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_EN (0x1<<11)
+#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_EN_SHIFT 11
+#define TSTORM_FCOE_AG_CONTEXT_AUX4_CF_EN (0x1<<12)
+#define TSTORM_FCOE_AG_CONTEXT_AUX4_CF_EN_SHIFT 12
+#define TSTORM_FCOE_AG_CONTEXT_AUX5_CF_EN (0x1<<13)
+#define TSTORM_FCOE_AG_CONTEXT_AUX5_CF_EN_SHIFT 13
+#define TSTORM_FCOE_AG_CONTEXT_AUX6_CF_EN (0x1<<14)
+#define TSTORM_FCOE_AG_CONTEXT_AUX6_CF_EN_SHIFT 14
+#define TSTORM_FCOE_AG_CONTEXT_AUX7_CF_EN (0x1<<15)
+#define TSTORM_FCOE_AG_CONTEXT_AUX7_CF_EN_SHIFT 15
#elif defined(__LITTLE_ENDIAN)
- u16 flags;
- u16 rsrv1;
+ u16 agg_vars2;
+#define __TSTORM_FCOE_AG_CONTEXT_AUX5_FLAG (0x1<<0)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX5_FLAG_SHIFT 0
+#define __TSTORM_FCOE_AG_CONTEXT_AUX6_FLAG (0x1<<1)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX6_FLAG_SHIFT 1
+#define __TSTORM_FCOE_AG_CONTEXT_AUX4_CF (0x3<<2)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX4_CF_SHIFT 2
+#define __TSTORM_FCOE_AG_CONTEXT_AUX5_CF (0x3<<4)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX5_CF_SHIFT 4
+#define __TSTORM_FCOE_AG_CONTEXT_AUX6_CF (0x3<<6)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX6_CF_SHIFT 6
+#define __TSTORM_FCOE_AG_CONTEXT_AUX7_CF (0x3<<8)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX7_CF_SHIFT 8
+#define __TSTORM_FCOE_AG_CONTEXT_AUX7_FLAG (0x1<<10)
+#define __TSTORM_FCOE_AG_CONTEXT_AUX7_FLAG_SHIFT 10
+#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_EN (0x1<<11)
+#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_EN_SHIFT 11
+#define TSTORM_FCOE_AG_CONTEXT_AUX4_CF_EN (0x1<<12)
+#define TSTORM_FCOE_AG_CONTEXT_AUX4_CF_EN_SHIFT 12
+#define TSTORM_FCOE_AG_CONTEXT_AUX5_CF_EN (0x1<<13)
+#define TSTORM_FCOE_AG_CONTEXT_AUX5_CF_EN_SHIFT 13
+#define TSTORM_FCOE_AG_CONTEXT_AUX6_CF_EN (0x1<<14)
+#define TSTORM_FCOE_AG_CONTEXT_AUX6_CF_EN_SHIFT 14
+#define TSTORM_FCOE_AG_CONTEXT_AUX7_CF_EN (0x1<<15)
+#define TSTORM_FCOE_AG_CONTEXT_AUX7_CF_EN_SHIFT 15
+ u16 __agg_val4;
#endif
+ struct tstorm_fcoe_extra_ag_context_section __extra_section;
};
+
+
/*
- * Parameters required for placement according to SGL
+ * The tcp aggregative context section of Tstorm
*/
-struct ustorm_fcoe_data_place {
+struct tstorm_tcp_tcp_ag_context_section {
+ u32 __agg_val1;
#if defined(__BIG_ENDIAN)
- u16 cached_sge_off;
- u8 cached_num_sges;
- u8 cached_sge_idx;
+ u8 __tcp_agg_vars2;
+ u8 __agg_val3;
+ u16 __agg_val2;
#elif defined(__LITTLE_ENDIAN)
- u8 cached_sge_idx;
- u8 cached_num_sges;
- u16 cached_sge_off;
+ u16 __agg_val2;
+ u8 __agg_val3;
+ u8 __tcp_agg_vars2;
#endif
- struct fcoe_bd_ctx cached_sge[3];
-};
-
-struct fcoe_task_ctx_entry_txwr_rxrd {
#if defined(__BIG_ENDIAN)
- u16 verify_tx_seq;
- u8 init_flags;
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE (0x7<<0)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE (0x1<<3)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE_SHIFT 3
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE (0x1<<4)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE_SHIFT 4
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_SINGLE_SGE (0x1<<5)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_SINGLE_SGE_SHIFT 5
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV5 (0x3<<6)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV5_SHIFT 6
- u8 tx_flags;
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE (0xF<<0)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV4 (0xF<<4)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV4_SHIFT 4
+ u16 __agg_val5;
+ u8 __agg_val6;
+ u8 __tcp_agg_vars3;
#elif defined(__LITTLE_ENDIAN)
- u8 tx_flags;
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE (0xF<<0)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV4 (0xF<<4)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV4_SHIFT 4
- u8 init_flags;
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE (0x7<<0)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE (0x1<<3)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE_SHIFT 3
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE (0x1<<4)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE_SHIFT 4
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_SINGLE_SGE (0x1<<5)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_SINGLE_SGE_SHIFT 5
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV5 (0x3<<6)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV5_SHIFT 6
- u16 verify_tx_seq;
+ u8 __tcp_agg_vars3;
+ u8 __agg_val6;
+ u16 __agg_val5;
#endif
+ u32 snd_nxt;
+ u32 rtt_seq;
+ u32 rtt_time;
+ u32 __reserved66;
+ u32 wnd_right_edge;
+ u32 tcp_agg_vars1;
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_FIN_SENT_FLAG (0x1<<0)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_FIN_SENT_FLAG_SHIFT 0
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG (0x1<<1)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG_SHIFT 1
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF (0x3<<2)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_SHIFT 2
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF (0x3<<4)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_SHIFT 4
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_EN (0x1<<6)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_EN_SHIFT 6
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_EN (0x1<<7)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_EN_SHIFT 7
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN (0x1<<8)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN_SHIFT 8
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_SND_NXT_EN (0x1<<9)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_SND_NXT_EN_SHIFT 9
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<10)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 10
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_FLAG (0x1<<11)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_FLAG_SHIFT 11
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_EN (0x1<<12)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_EN_SHIFT 12
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_EN (0x1<<13)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_EN_SHIFT 13
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF (0x3<<14)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_SHIFT 14
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF (0x3<<16)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_SHIFT 16
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_BLOCKED (0x1<<18)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_BLOCKED_SHIFT 18
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN (0x1<<19)
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN_SHIFT 19
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_EN (0x1<<20)
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_EN_SHIFT 20
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_EN (0x1<<21)
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_EN_SHIFT 21
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED1 (0x3<<22)
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED1_SHIFT 22
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ (0xF<<24)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ_SHIFT 24
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ (0xF<<28)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ_SHIFT 28
+ u32 snd_max;
+ u32 snd_una;
+ u32 __reserved2;
};
-struct fcoe_fcp_cmd_payload {
- u32 opaque[8];
-};
-
-struct fcoe_fc_hdr {
-#if defined(__BIG_ENDIAN)
- u8 cs_ctl;
- u8 s_id[3];
-#elif defined(__LITTLE_ENDIAN)
- u8 s_id[3];
- u8 cs_ctl;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 r_ctl;
- u8 d_id[3];
-#elif defined(__LITTLE_ENDIAN)
- u8 d_id[3];
- u8 r_ctl;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 seq_id;
- u8 df_ctl;
- u16 seq_cnt;
-#elif defined(__LITTLE_ENDIAN)
- u16 seq_cnt;
- u8 df_ctl;
- u8 seq_id;
-#endif
+/*
+ * The iscsi aggregative context of Tstorm
+ */
+struct tstorm_iscsi_ag_context {
#if defined(__BIG_ENDIAN)
- u8 type;
- u8 f_ctl[3];
+ u16 ulp_credit;
+ u8 agg_vars1;
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF (0x3<<4)
+#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_SHIFT 4
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG (0x1<<6)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG_SHIFT 6
+#define __TSTORM_ISCSI_AG_CONTEXT_ACK_ON_FIN_SENT_FLAG (0x1<<7)
+#define __TSTORM_ISCSI_AG_CONTEXT_ACK_ON_FIN_SENT_FLAG_SHIFT 7
+ u8 state;
#elif defined(__LITTLE_ENDIAN)
- u8 f_ctl[3];
- u8 type;
+ u8 state;
+ u8 agg_vars1;
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF (0x3<<4)
+#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_SHIFT 4
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG (0x1<<6)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG_SHIFT 6
+#define __TSTORM_ISCSI_AG_CONTEXT_ACK_ON_FIN_SENT_FLAG (0x1<<7)
+#define __TSTORM_ISCSI_AG_CONTEXT_ACK_ON_FIN_SENT_FLAG_SHIFT 7
+ u16 ulp_credit;
#endif
- u32 parameters;
#if defined(__BIG_ENDIAN)
- u16 ox_id;
- u16 rx_id;
+ u16 __agg_val4;
+ u16 agg_vars2;
+#define __TSTORM_ISCSI_AG_CONTEXT_MSL_TIMER_SET_FLAG (0x1<<0)
+#define __TSTORM_ISCSI_AG_CONTEXT_MSL_TIMER_SET_FLAG_SHIFT 0
+#define __TSTORM_ISCSI_AG_CONTEXT_FIN_SENT_FIRST_FLAG (0x1<<1)
+#define __TSTORM_ISCSI_AG_CONTEXT_FIN_SENT_FIRST_FLAG_SHIFT 1
+#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF (0x3<<2)
+#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_SHIFT 2
+#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF (0x3<<4)
+#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_SHIFT 4
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF (0x3<<6)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_SHIFT 6
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF (0x3<<8)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_SHIFT 8
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG (0x1<<10)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG_SHIFT 10
+#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<11)
+#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 11
+#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_EN (0x1<<12)
+#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_EN_SHIFT 12
+#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_EN (0x1<<13)
+#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_EN_SHIFT 13
+#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN (0x1<<14)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN_SHIFT 14
+#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN (0x1<<15)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN_SHIFT 15
#elif defined(__LITTLE_ENDIAN)
- u16 rx_id;
- u16 ox_id;
+ u16 agg_vars2;
+#define __TSTORM_ISCSI_AG_CONTEXT_MSL_TIMER_SET_FLAG (0x1<<0)
+#define __TSTORM_ISCSI_AG_CONTEXT_MSL_TIMER_SET_FLAG_SHIFT 0
+#define __TSTORM_ISCSI_AG_CONTEXT_FIN_SENT_FIRST_FLAG (0x1<<1)
+#define __TSTORM_ISCSI_AG_CONTEXT_FIN_SENT_FIRST_FLAG_SHIFT 1
+#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF (0x3<<2)
+#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_SHIFT 2
+#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF (0x3<<4)
+#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_SHIFT 4
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF (0x3<<6)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_SHIFT 6
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF (0x3<<8)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_SHIFT 8
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG (0x1<<10)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG_SHIFT 10
+#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<11)
+#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 11
+#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_EN (0x1<<12)
+#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_EN_SHIFT 12
+#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_EN (0x1<<13)
+#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_EN_SHIFT 13
+#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN (0x1<<14)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN_SHIFT 14
+#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN (0x1<<15)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN_SHIFT 15
+ u16 __agg_val4;
#endif
+ struct tstorm_tcp_tcp_ag_context_section tcp;
};
-struct fcoe_fc_frame {
- struct fcoe_fc_hdr fc_hdr;
- u32 reserved0[2];
-};
-
-union fcoe_cmd_flow_info {
- struct fcoe_fcp_cmd_payload fcp_cmd_payload;
- struct fcoe_fc_frame mp_fc_frame;
-};
-
-struct fcoe_read_flow_info {
- struct fcoe_fc_hdr fc_data_in_hdr;
- u32 reserved[2];
-};
-
-struct fcoe_fcp_xfr_rdy_payload {
- u32 burst_len;
- u32 data_ro;
-};
-
-struct fcoe_write_flow_info {
- struct fcoe_fc_hdr fc_data_out_hdr;
- struct fcoe_fcp_xfr_rdy_payload fcp_xfr_payload;
-};
-
-struct fcoe_fcp_rsp_flags {
- u8 flags;
-#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID (0x1<<0)
-#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID_SHIFT 0
-#define FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID (0x1<<1)
-#define FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID_SHIFT 1
-#define FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER (0x1<<2)
-#define FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER_SHIFT 2
-#define FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER (0x1<<3)
-#define FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER_SHIFT 3
-#define FCOE_FCP_RSP_FLAGS_FCP_CONF_REQ (0x1<<4)
-#define FCOE_FCP_RSP_FLAGS_FCP_CONF_REQ_SHIFT 4
-#define FCOE_FCP_RSP_FLAGS_FCP_BIDI_FLAGS (0x7<<5)
-#define FCOE_FCP_RSP_FLAGS_FCP_BIDI_FLAGS_SHIFT 5
-};
-
-struct fcoe_fcp_rsp_payload {
- struct regpair reserved0;
- u32 fcp_resid;
-#if defined(__BIG_ENDIAN)
- u16 retry_delay_timer;
- struct fcoe_fcp_rsp_flags fcp_flags;
- u8 scsi_status_code;
-#elif defined(__LITTLE_ENDIAN)
- u8 scsi_status_code;
- struct fcoe_fcp_rsp_flags fcp_flags;
- u16 retry_delay_timer;
-#endif
- u32 fcp_rsp_len;
- u32 fcp_sns_len;
-};
-/*
- * Fixed size structure in order to plant it in Union structure
- */
-struct fcoe_fcp_rsp_union {
- struct fcoe_fcp_rsp_payload payload;
- struct regpair reserved0;
-};
/*
- * Fixed size structure in order to plant it in Union structure
+ * The fcoe aggregative context of Ustorm
*/
-struct fcoe_abts_rsp_union {
- u32 r_ctl;
- u32 abts_rsp_payload[7];
-};
-
-union fcoe_rsp_flow_info {
- struct fcoe_fcp_rsp_union fcp_rsp;
- struct fcoe_abts_rsp_union abts_rsp;
-};
-
-struct fcoe_cleanup_flow_info {
+struct ustorm_fcoe_ag_context {
#if defined(__BIG_ENDIAN)
- u16 reserved1;
- u16 task_id;
+ u8 __aux_counter_flags;
+ u8 agg_vars2;
+#define USTORM_FCOE_AG_CONTEXT_TX_CF (0x3<<0)
+#define USTORM_FCOE_AG_CONTEXT_TX_CF_SHIFT 0
+#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF (0x3<<2)
+#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_SHIFT 2
+#define USTORM_FCOE_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4)
+#define USTORM_FCOE_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4
+#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7)
+#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7
+ u8 agg_vars1;
+#define __USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define USTORM_FCOE_AG_CONTEXT_INV_CF (0x3<<4)
+#define USTORM_FCOE_AG_CONTEXT_INV_CF_SHIFT 4
+#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF (0x3<<6)
+#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_SHIFT 6
+ u8 state;
#elif defined(__LITTLE_ENDIAN)
- u16 task_id;
- u16 reserved1;
+ u8 state;
+ u8 agg_vars1;
+#define __USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define USTORM_FCOE_AG_CONTEXT_INV_CF (0x3<<4)
+#define USTORM_FCOE_AG_CONTEXT_INV_CF_SHIFT 4
+#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF (0x3<<6)
+#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_SHIFT 6
+ u8 agg_vars2;
+#define USTORM_FCOE_AG_CONTEXT_TX_CF (0x3<<0)
+#define USTORM_FCOE_AG_CONTEXT_TX_CF_SHIFT 0
+#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF (0x3<<2)
+#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_SHIFT 2
+#define USTORM_FCOE_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4)
+#define USTORM_FCOE_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4
+#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7)
+#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7
+ u8 __aux_counter_flags;
#endif
- u32 reserved2[7];
-};
-
-/*
- * 32 bytes used for general purposes
- */
-union fcoe_general_task_ctx {
- union fcoe_cmd_flow_info cmd_info;
- struct fcoe_read_flow_info read_info;
- struct fcoe_write_flow_info write_info;
- union fcoe_rsp_flow_info rsp_info;
- struct fcoe_cleanup_flow_info cleanup_info;
- u32 comp_info[8];
-};
-
-struct fcoe_s_stat_ctx {
- u8 flags;
-#define FCOE_S_STAT_CTX_ACTIVE (0x1<<0)
-#define FCOE_S_STAT_CTX_ACTIVE_SHIFT 0
-#define FCOE_S_STAT_CTX_ACK_ABORT_SEQ_COND (0x1<<1)
-#define FCOE_S_STAT_CTX_ACK_ABORT_SEQ_COND_SHIFT 1
-#define FCOE_S_STAT_CTX_ABTS_PERFORMED (0x1<<2)
-#define FCOE_S_STAT_CTX_ABTS_PERFORMED_SHIFT 2
-#define FCOE_S_STAT_CTX_SEQ_TIMEOUT (0x1<<3)
-#define FCOE_S_STAT_CTX_SEQ_TIMEOUT_SHIFT 3
-#define FCOE_S_STAT_CTX_P_RJT (0x1<<4)
-#define FCOE_S_STAT_CTX_P_RJT_SHIFT 4
-#define FCOE_S_STAT_CTX_ACK_EOFT (0x1<<5)
-#define FCOE_S_STAT_CTX_ACK_EOFT_SHIFT 5
-#define FCOE_S_STAT_CTX_RSRV1 (0x3<<6)
-#define FCOE_S_STAT_CTX_RSRV1_SHIFT 6
-};
-
-/*
- * Common section. Both TX and RX processing might write and read from it in different flows
- */
-struct fcoe_task_ctx_entry_tx_rx_cmn {
- u32 data_2_trns;
- union fcoe_general_task_ctx general;
-#if defined(__BIG_ENDIAN)
- u16 tx_low_seq_cnt;
- struct fcoe_s_stat_ctx tx_s_stat;
- u8 tx_seq_id;
-#elif defined(__LITTLE_ENDIAN)
- u8 tx_seq_id;
- struct fcoe_s_stat_ctx tx_s_stat;
- u16 tx_low_seq_cnt;
-#endif
- u32 common_flags;
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_CID (0xFFFFFF<<0)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_CID_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_VALID (0x1<<24)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_VALID_SHIFT 24
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_SEQ_INIT (0x1<<25)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_SEQ_INIT_SHIFT 25
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_PEND_XFER (0x1<<26)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_PEND_XFER_SHIFT 26
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_PEND_CONF (0x1<<27)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_PEND_CONF_SHIFT 27
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_EXP_FIRST_FRAME (0x1<<28)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_EXP_FIRST_FRAME_SHIFT 28
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_RSRV (0x7<<29)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_RSRV_SHIFT 29
-};
-
-struct fcoe_task_ctx_entry_rxwr_txrd {
-#if defined(__BIG_ENDIAN)
- u16 rx_id;
- u16 rx_flags;
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE (0xF<<0)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE (0x7<<4)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE_SHIFT 4
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_CONF_REQ (0x1<<7)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_CONF_REQ_SHIFT 7
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_MISS_FRAME (0x1<<8)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_MISS_FRAME_SHIFT 8
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RESERVED0 (0x7F<<9)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RESERVED0_SHIFT 9
-#elif defined(__LITTLE_ENDIAN)
- u16 rx_flags;
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE (0xF<<0)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE (0x7<<4)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE_SHIFT 4
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_CONF_REQ (0x1<<7)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_CONF_REQ_SHIFT 7
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_MISS_FRAME (0x1<<8)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_MISS_FRAME_SHIFT 8
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RESERVED0 (0x7F<<9)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RESERVED0_SHIFT 9
- u16 rx_id;
-#endif
-};
-
-struct fcoe_seq_ctx {
-#if defined(__BIG_ENDIAN)
- u16 low_seq_cnt;
- struct fcoe_s_stat_ctx s_stat;
- u8 seq_id;
+#if defined(__BIG_ENDIAN)
+ u8 cdu_usage;
+ u8 agg_misc2;
+ u16 pbf_tx_seq_ack;
#elif defined(__LITTLE_ENDIAN)
- u8 seq_id;
- struct fcoe_s_stat_ctx s_stat;
- u16 low_seq_cnt;
+ u16 pbf_tx_seq_ack;
+ u8 agg_misc2;
+ u8 cdu_usage;
#endif
+ u32 agg_misc4;
#if defined(__BIG_ENDIAN)
- u16 err_seq_cnt;
- u16 high_seq_cnt;
+ u8 agg_val3_th;
+ u8 agg_val3;
+ u16 agg_misc3;
#elif defined(__LITTLE_ENDIAN)
- u16 high_seq_cnt;
- u16 err_seq_cnt;
+ u16 agg_misc3;
+ u8 agg_val3;
+ u8 agg_val3_th;
#endif
- u32 low_exp_ro;
- u32 high_exp_ro;
-};
-
-struct fcoe_single_sge_ctx {
- struct regpair cur_buf_addr;
+ u32 expired_task_id;
+ u32 agg_misc4_th;
#if defined(__BIG_ENDIAN)
- u16 reserved0;
- u16 cur_buf_rem;
+ u16 cq_prod;
+ u16 cq_cons;
#elif defined(__LITTLE_ENDIAN)
- u16 cur_buf_rem;
- u16 reserved0;
+ u16 cq_cons;
+ u16 cq_prod;
#endif
-};
-
-struct fcoe_mul_sges_ctx {
- struct regpair cur_sge_addr;
#if defined(__BIG_ENDIAN)
- u8 sgl_size;
- u8 cur_sge_idx;
- u16 cur_sge_off;
+ u16 __reserved2;
+ u8 decision_rules;
+#define USTORM_FCOE_AG_CONTEXT_CQ_DEC_RULE (0x7<<0)
+#define USTORM_FCOE_AG_CONTEXT_CQ_DEC_RULE_SHIFT 0
+#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3)
+#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3
+#define USTORM_FCOE_AG_CONTEXT_CQ_ARM_N_FLAG (0x1<<6)
+#define USTORM_FCOE_AG_CONTEXT_CQ_ARM_N_FLAG_SHIFT 6
+#define __USTORM_FCOE_AG_CONTEXT_RESERVED1 (0x1<<7)
+#define __USTORM_FCOE_AG_CONTEXT_RESERVED1_SHIFT 7
+ u8 decision_rule_enable_bits;
+#define __USTORM_FCOE_AG_CONTEXT_RESERVED_INV_CF_EN (0x1<<0)
+#define __USTORM_FCOE_AG_CONTEXT_RESERVED_INV_CF_EN_SHIFT 0
+#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1)
+#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1
+#define USTORM_FCOE_AG_CONTEXT_TX_CF_EN (0x1<<2)
+#define USTORM_FCOE_AG_CONTEXT_TX_CF_EN_SHIFT 2
+#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_EN (0x1<<3)
+#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_EN_SHIFT 3
+#define __USTORM_FCOE_AG_CONTEXT_AUX1_CF_EN (0x1<<4)
+#define __USTORM_FCOE_AG_CONTEXT_AUX1_CF_EN_SHIFT 4
+#define __USTORM_FCOE_AG_CONTEXT_QUEUE0_CF_EN (0x1<<5)
+#define __USTORM_FCOE_AG_CONTEXT_QUEUE0_CF_EN_SHIFT 5
+#define __USTORM_FCOE_AG_CONTEXT_AUX3_CF_EN (0x1<<6)
+#define __USTORM_FCOE_AG_CONTEXT_AUX3_CF_EN_SHIFT 6
+#define __USTORM_FCOE_AG_CONTEXT_DQ_CF_EN (0x1<<7)
+#define __USTORM_FCOE_AG_CONTEXT_DQ_CF_EN_SHIFT 7
#elif defined(__LITTLE_ENDIAN)
- u16 cur_sge_off;
- u8 cur_sge_idx;
- u8 sgl_size;
+ u8 decision_rule_enable_bits;
+#define __USTORM_FCOE_AG_CONTEXT_RESERVED_INV_CF_EN (0x1<<0)
+#define __USTORM_FCOE_AG_CONTEXT_RESERVED_INV_CF_EN_SHIFT 0
+#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1)
+#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1
+#define USTORM_FCOE_AG_CONTEXT_TX_CF_EN (0x1<<2)
+#define USTORM_FCOE_AG_CONTEXT_TX_CF_EN_SHIFT 2
+#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_EN (0x1<<3)
+#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_EN_SHIFT 3
+#define __USTORM_FCOE_AG_CONTEXT_AUX1_CF_EN (0x1<<4)
+#define __USTORM_FCOE_AG_CONTEXT_AUX1_CF_EN_SHIFT 4
+#define __USTORM_FCOE_AG_CONTEXT_QUEUE0_CF_EN (0x1<<5)
+#define __USTORM_FCOE_AG_CONTEXT_QUEUE0_CF_EN_SHIFT 5
+#define __USTORM_FCOE_AG_CONTEXT_AUX3_CF_EN (0x1<<6)
+#define __USTORM_FCOE_AG_CONTEXT_AUX3_CF_EN_SHIFT 6
+#define __USTORM_FCOE_AG_CONTEXT_DQ_CF_EN (0x1<<7)
+#define __USTORM_FCOE_AG_CONTEXT_DQ_CF_EN_SHIFT 7
+ u8 decision_rules;
+#define USTORM_FCOE_AG_CONTEXT_CQ_DEC_RULE (0x7<<0)
+#define USTORM_FCOE_AG_CONTEXT_CQ_DEC_RULE_SHIFT 0
+#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3)
+#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3
+#define USTORM_FCOE_AG_CONTEXT_CQ_ARM_N_FLAG (0x1<<6)
+#define USTORM_FCOE_AG_CONTEXT_CQ_ARM_N_FLAG_SHIFT 6
+#define __USTORM_FCOE_AG_CONTEXT_RESERVED1 (0x1<<7)
+#define __USTORM_FCOE_AG_CONTEXT_RESERVED1_SHIFT 7
+ u16 __reserved2;
#endif
};
-union fcoe_sgl_ctx {
- struct fcoe_single_sge_ctx single_sge;
- struct fcoe_mul_sges_ctx mul_sges;
-};
-
-struct fcoe_task_ctx_entry_rx_only {
- struct fcoe_seq_ctx seq_ctx;
- struct fcoe_seq_ctx ooo_seq_ctx;
- u32 rsrv3;
- union fcoe_sgl_ctx sgl_ctx;
-};
-
-struct ustorm_fcoe_task_ctx_entry_rd {
- struct fcoe_task_ctx_entry_txwr_rxrd tx_wr_rx_rd;
- struct fcoe_task_ctx_entry_tx_rx_cmn cmn;
- struct fcoe_task_ctx_entry_rxwr_txrd rx_wr_tx_rd;
- struct fcoe_task_ctx_entry_rx_only rx_wr;
- u32 reserved;
-};
/*
- * Ustorm FCoE Storm Context
+ * The iscsi aggregative context of Ustorm
*/
-struct ustorm_fcoe_st_context {
- struct ustorm_fcoe_params fcoe_params;
- struct regpair task_addr;
- struct regpair cq_base_addr;
- struct regpair rq_pbl_base;
- struct regpair rq_cur_page_addr;
- struct regpair confq_pbl_base_addr;
- struct regpair conn_db_base;
- struct regpair xfrq_base_addr;
- struct regpair lcq_base_addr;
+struct ustorm_iscsi_ag_context {
#if defined(__BIG_ENDIAN)
- union fcoe_idx16_field_union rq_cons;
- union fcoe_idx16_field_union rq_prod;
+ u8 __aux_counter_flags;
+ u8 agg_vars2;
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF (0x3<<0)
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_SHIFT 0
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF (0x3<<2)
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_SHIFT 2
+#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4)
+#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7
+ u8 agg_vars1;
+#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF (0x3<<4)
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_SHIFT 4
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF (0x3<<6)
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_SHIFT 6
+ u8 state;
#elif defined(__LITTLE_ENDIAN)
- union fcoe_idx16_field_union rq_prod;
- union fcoe_idx16_field_union rq_cons;
+ u8 state;
+ u8 agg_vars1;
+#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF (0x3<<4)
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_SHIFT 4
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF (0x3<<6)
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_SHIFT 6
+ u8 agg_vars2;
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF (0x3<<0)
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_SHIFT 0
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF (0x3<<2)
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_SHIFT 2
+#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4)
+#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7
+ u8 __aux_counter_flags;
#endif
#if defined(__BIG_ENDIAN)
- u16 xfrq_prod;
- u16 cq_cons;
+ u8 cdu_usage;
+ u8 agg_misc2;
+ u16 __cq_local_comp_itt_val;
#elif defined(__LITTLE_ENDIAN)
- u16 cq_cons;
- u16 xfrq_prod;
+ u16 __cq_local_comp_itt_val;
+ u8 agg_misc2;
+ u8 cdu_usage;
#endif
+ u32 agg_misc4;
#if defined(__BIG_ENDIAN)
- u16 lcq_cons;
- u16 hc_cram_address;
+ u8 agg_val3_th;
+ u8 agg_val3;
+ u16 agg_misc3;
#elif defined(__LITTLE_ENDIAN)
- u16 hc_cram_address;
- u16 lcq_cons;
+ u16 agg_misc3;
+ u8 agg_val3;
+ u8 agg_val3_th;
#endif
+ u32 agg_val1;
+ u32 agg_misc4_th;
#if defined(__BIG_ENDIAN)
- u16 sq_xfrq_lcq_confq_size;
- u16 confq_prod;
+ u16 agg_val2_th;
+ u16 agg_val2;
#elif defined(__LITTLE_ENDIAN)
- u16 confq_prod;
- u16 sq_xfrq_lcq_confq_size;
+ u16 agg_val2;
+ u16 agg_val2_th;
#endif
#if defined(__BIG_ENDIAN)
- u8 hc_csdm_agg_int;
- u8 flags;
-#define USTORM_FCOE_ST_CONTEXT_MID_SEQ_PROC_FLAG (0x1<<0)
-#define USTORM_FCOE_ST_CONTEXT_MID_SEQ_PROC_FLAG_SHIFT 0
-#define USTORM_FCOE_ST_CONTEXT_CACHED_CONN_FLAG (0x1<<1)
-#define USTORM_FCOE_ST_CONTEXT_CACHED_CONN_FLAG_SHIFT 1
-#define USTORM_FCOE_ST_CONTEXT_CACHED_TCE_FLAG (0x1<<2)
-#define USTORM_FCOE_ST_CONTEXT_CACHED_TCE_FLAG_SHIFT 2
-#define USTORM_FCOE_ST_CONTEXT_RSRV1 (0x1F<<3)
-#define USTORM_FCOE_ST_CONTEXT_RSRV1_SHIFT 3
- u8 available_rqes;
- u8 sp_q_flush_cnt;
+ u16 __reserved2;
+ u8 decision_rules;
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE (0x7<<0)
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE_SHIFT 0
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3)
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG (0x1<<6)
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG_SHIFT 6
+#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1 (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1_SHIFT 7
+ u8 decision_rule_enable_bits;
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN (0x1<<0)
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN_SHIFT 0
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1)
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN (0x1<<2)
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN_SHIFT 2
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN (0x1<<3)
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN_SHIFT 3
+#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN (0x1<<4)
+#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN_SHIFT 4
+#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<5)
+#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 5
+#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<6)
+#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 6
+#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
#elif defined(__LITTLE_ENDIAN)
- u8 sp_q_flush_cnt;
- u8 available_rqes;
- u8 flags;
-#define USTORM_FCOE_ST_CONTEXT_MID_SEQ_PROC_FLAG (0x1<<0)
-#define USTORM_FCOE_ST_CONTEXT_MID_SEQ_PROC_FLAG_SHIFT 0
-#define USTORM_FCOE_ST_CONTEXT_CACHED_CONN_FLAG (0x1<<1)
-#define USTORM_FCOE_ST_CONTEXT_CACHED_CONN_FLAG_SHIFT 1
-#define USTORM_FCOE_ST_CONTEXT_CACHED_TCE_FLAG (0x1<<2)
-#define USTORM_FCOE_ST_CONTEXT_CACHED_TCE_FLAG_SHIFT 2
-#define USTORM_FCOE_ST_CONTEXT_RSRV1 (0x1F<<3)
-#define USTORM_FCOE_ST_CONTEXT_RSRV1_SHIFT 3
- u8 hc_csdm_agg_int;
+ u8 decision_rule_enable_bits;
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN (0x1<<0)
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN_SHIFT 0
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1)
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN (0x1<<2)
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN_SHIFT 2
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN (0x1<<3)
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN_SHIFT 3
+#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN (0x1<<4)
+#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN_SHIFT 4
+#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<5)
+#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 5
+#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<6)
+#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 6
+#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
+ u8 decision_rules;
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE (0x7<<0)
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE_SHIFT 0
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3)
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG (0x1<<6)
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG_SHIFT 6
+#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1 (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1_SHIFT 7
+ u16 __reserved2;
#endif
- struct ustorm_fcoe_data_place data_place;
- struct ustorm_fcoe_task_ctx_entry_rd tce;
};
-/*
- * The FCoE non-aggregative context of Tstorm
- */
-struct tstorm_fcoe_st_context {
- struct regpair reserved0;
- struct regpair reserved1;
-};
/*
* The fcoe aggregative context section of Xstorm
@@ -1272,8 +1367,8 @@ struct xstorm_fcoe_extra_ag_context_section {
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED51_SHIFT 0
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED (0x3<<2)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_SHIFT 2
-#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_PBF_TX_SEQ_ACK_CF (0x3<<4)
-#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_PBF_TX_SEQ_ACK_CF_SHIFT 4
+#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF (0x3<<4)
+#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_SHIFT 4
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_CLEAR_DA_TIMER_EN (0x1<<6)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_CLEAR_DA_TIMER_EN_SHIFT 6
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_DA_EXPIRATION_FLAG (0x1<<7)
@@ -1288,20 +1383,20 @@ struct xstorm_fcoe_extra_ag_context_section {
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED51_SHIFT 0
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED (0x3<<2)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_SHIFT 2
-#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_PBF_TX_SEQ_ACK_CF (0x3<<4)
-#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_PBF_TX_SEQ_ACK_CF_SHIFT 4
+#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF (0x3<<4)
+#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_SHIFT 4
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_CLEAR_DA_TIMER_EN (0x1<<6)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_CLEAR_DA_TIMER_EN_SHIFT 6
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_DA_EXPIRATION_FLAG (0x1<<7)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_DA_EXPIRATION_FLAG_SHIFT 7
#endif
- u32 __task_addr_lo;
- u32 __task_addr_hi;
+ u32 snd_nxt;
+ u32 tx_wnd;
u32 __reserved55;
- u32 __tx_prods;
+ u32 local_adv_wnd;
#if defined(__BIG_ENDIAN)
u8 __agg_val8_th;
- u8 __agg_val8;
+ u8 __tx_dest;
u16 tcp_agg_vars2;
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED57 (0x1<<0)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED57_SHIFT 0
@@ -1317,8 +1412,8 @@ struct xstorm_fcoe_extra_ag_context_section {
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED60_SHIFT 5
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_ACK_TO_FE_UPDATED_EN (0x1<<6)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_ACK_TO_FE_UPDATED_EN_SHIFT 6
-#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_PBF_TX_SEQ_ACK_CF_EN (0x1<<7)
-#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_PBF_TX_SEQ_ACK_CF_EN_SHIFT 7
+#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_EN (0x1<<7)
+#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_EN_SHIFT 7
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_TX_FIN_FLAG_EN (0x1<<8)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_TX_FIN_FLAG_EN_SHIFT 8
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<9)
@@ -1327,8 +1422,8 @@ struct xstorm_fcoe_extra_ag_context_section {
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_SET_RTO_CF_SHIFT 10
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF (0x3<<12)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF_SHIFT 12
-#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX8_CF (0x3<<14)
-#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX8_CF_SHIFT 14
+#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TX_DEST_UPDATED_CF (0x3<<14)
+#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TX_DEST_UPDATED_CF_SHIFT 14
#elif defined(__LITTLE_ENDIAN)
u16 tcp_agg_vars2;
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED57 (0x1<<0)
@@ -1345,8 +1440,8 @@ struct xstorm_fcoe_extra_ag_context_section {
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED60_SHIFT 5
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_ACK_TO_FE_UPDATED_EN (0x1<<6)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_ACK_TO_FE_UPDATED_EN_SHIFT 6
-#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_PBF_TX_SEQ_ACK_CF_EN (0x1<<7)
-#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_PBF_TX_SEQ_ACK_CF_EN_SHIFT 7
+#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_EN (0x1<<7)
+#define XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_EN_SHIFT 7
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_TX_FIN_FLAG_EN (0x1<<8)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED_TX_FIN_FLAG_EN_SHIFT 8
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<9)
@@ -1355,9 +1450,9 @@ struct xstorm_fcoe_extra_ag_context_section {
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_SET_RTO_CF_SHIFT 10
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF (0x3<<12)
#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF_SHIFT 12
-#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX8_CF (0x3<<14)
-#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX8_CF_SHIFT 14
- u8 __agg_val8;
+#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TX_DEST_UPDATED_CF (0x3<<14)
+#define __XSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TX_DEST_UPDATED_CF_SHIFT 14
+ u8 __tx_dest;
u8 __agg_val8_th;
#endif
u32 __sq_base_addr_lo;
@@ -1591,9 +1686,9 @@ struct xstorm_fcoe_ag_context {
#if defined(__BIG_ENDIAN)
u8 __reserved1;
u8 __agg_val6_th;
- u16 __confq_tx_prod;
+ u16 __agg_val9;
#elif defined(__LITTLE_ENDIAN)
- u16 __confq_tx_prod;
+ u16 __agg_val9;
u8 __agg_val6_th;
u8 __reserved1;
#endif
@@ -1605,16 +1700,16 @@ struct xstorm_fcoe_ag_context {
u16 confq_cons;
#endif
u32 agg_vars8;
-#define __XSTORM_FCOE_AG_CONTEXT_CACHE_WQE_IDX (0xFFFFFF<<0)
-#define __XSTORM_FCOE_AG_CONTEXT_CACHE_WQE_IDX_SHIFT 0
+#define XSTORM_FCOE_AG_CONTEXT_AGG_MISC2 (0xFFFFFF<<0)
+#define XSTORM_FCOE_AG_CONTEXT_AGG_MISC2_SHIFT 0
#define XSTORM_FCOE_AG_CONTEXT_AGG_MISC3 (0xFF<<24)
#define XSTORM_FCOE_AG_CONTEXT_AGG_MISC3_SHIFT 24
#if defined(__BIG_ENDIAN)
- u16 ox_id;
+ u16 agg_misc0;
u16 sq_prod;
#elif defined(__LITTLE_ENDIAN)
u16 sq_prod;
- u16 ox_id;
+ u16 agg_misc0;
#endif
#if defined(__BIG_ENDIAN)
u8 agg_val3;
@@ -1628,332 +1723,1685 @@ struct xstorm_fcoe_ag_context {
u8 agg_val3;
#endif
#if defined(__BIG_ENDIAN)
- u16 __pbf_tx_seq_ack;
+ u16 __agg_misc1;
u16 agg_limit1;
#elif defined(__LITTLE_ENDIAN)
u16 agg_limit1;
- u16 __pbf_tx_seq_ack;
+ u16 __agg_misc1;
#endif
u32 completion_seq;
u32 confq_pbl_base_lo;
u32 confq_pbl_base_hi;
};
+
+
/*
- * The fcoe extra aggregative context section of Tstorm
+ * The tcp aggregative context section of Xstorm
*/
-struct tstorm_fcoe_extra_ag_context_section {
- u32 __agg_val1;
+struct xstorm_tcp_tcp_ag_context_section {
#if defined(__BIG_ENDIAN)
- u8 __tcp_agg_vars2;
- u8 __agg_val3;
- u16 __agg_val2;
+ u8 tcp_agg_vars1;
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_DA_TIMER_CF (0x3<<0)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_DA_TIMER_CF_SHIFT 0
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED (0x3<<2)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_SHIFT 2
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF (0x3<<4)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_SHIFT 4
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CLEAR_DA_TIMER_EN (0x1<<6)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CLEAR_DA_TIMER_EN_SHIFT 6
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_EXPIRATION_FLAG (0x1<<7)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_EXPIRATION_FLAG_SHIFT 7
+ u8 __da_cnt;
+ u16 mss;
#elif defined(__LITTLE_ENDIAN)
- u16 __agg_val2;
- u8 __agg_val3;
- u8 __tcp_agg_vars2;
+ u16 mss;
+ u8 __da_cnt;
+ u8 tcp_agg_vars1;
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_DA_TIMER_CF (0x3<<0)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_DA_TIMER_CF_SHIFT 0
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED (0x3<<2)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_SHIFT 2
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF (0x3<<4)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_SHIFT 4
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CLEAR_DA_TIMER_EN (0x1<<6)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CLEAR_DA_TIMER_EN_SHIFT 6
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_EXPIRATION_FLAG (0x1<<7)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_EXPIRATION_FLAG_SHIFT 7
#endif
+ u32 snd_nxt;
+ u32 tx_wnd;
+ u32 snd_una;
+ u32 local_adv_wnd;
#if defined(__BIG_ENDIAN)
- u16 __agg_val5;
- u8 __agg_val6;
+ u8 __agg_val8_th;
+ u8 __tx_dest;
+ u16 tcp_agg_vars2;
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG (0x1<<0)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_SHIFT 0
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED (0x1<<1)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED_SHIFT 1
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE (0x1<<2)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE_SHIFT 2
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG (0x1<<3)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG_SHIFT 3
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG (0x1<<4)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG_SHIFT 4
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE (0x1<<5)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE_SHIFT 5
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN (0x1<<6)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN_SHIFT 6
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_EN (0x1<<7)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_EN_SHIFT 7
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN (0x1<<8)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN_SHIFT 8
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<9)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 9
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF (0x3<<10)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF_SHIFT 10
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF (0x3<<12)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF_SHIFT 12
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_DEST_UPDATED_CF (0x3<<14)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_DEST_UPDATED_CF_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 tcp_agg_vars2;
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG (0x1<<0)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_SHIFT 0
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED (0x1<<1)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED_SHIFT 1
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE (0x1<<2)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE_SHIFT 2
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG (0x1<<3)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG_SHIFT 3
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG (0x1<<4)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG_SHIFT 4
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE (0x1<<5)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE_SHIFT 5
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN (0x1<<6)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN_SHIFT 6
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_EN (0x1<<7)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SIDEBAND_SENT_CF_EN_SHIFT 7
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN (0x1<<8)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN_SHIFT 8
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<9)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 9
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF (0x3<<10)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF_SHIFT 10
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF (0x3<<12)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF_SHIFT 12
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_DEST_UPDATED_CF (0x3<<14)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_DEST_UPDATED_CF_SHIFT 14
+ u8 __tx_dest;
+ u8 __agg_val8_th;
+#endif
+ u32 ack_to_far_end;
+ u32 rto_timer;
+ u32 ka_timer;
+ u32 ts_to_echo;
+#if defined(__BIG_ENDIAN)
+ u16 __agg_val7_th;
+ u16 __agg_val7;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __agg_val7;
+ u16 __agg_val7_th;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 __tcp_agg_vars5;
+ u8 __tcp_agg_vars4;
u8 __tcp_agg_vars3;
+ u8 __force_pure_ack_cnt;
#elif defined(__LITTLE_ENDIAN)
+ u8 __force_pure_ack_cnt;
u8 __tcp_agg_vars3;
- u8 __agg_val6;
- u16 __agg_val5;
+ u8 __tcp_agg_vars4;
+ u8 __tcp_agg_vars5;
+#endif
+ u32 tcp_agg_vars6;
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_CF_EN (0x1<<0)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_CF_EN_SHIFT 0
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_DEST_UPDATED_CF_EN (0x1<<1)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_DEST_UPDATED_CF_EN_SHIFT 1
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_EN (0x1<<2)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_EN_SHIFT 2
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN (0x1<<3)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN_SHIFT 3
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX6_FLAG (0x1<<4)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX6_FLAG_SHIFT 4
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX7_FLAG (0x1<<5)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX7_FLAG_SHIFT 5
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX5_CF (0x3<<6)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX5_CF_SHIFT 6
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF (0x3<<8)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_SHIFT 8
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF (0x3<<10)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_SHIFT 10
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF (0x3<<12)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_SHIFT 12
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF (0x3<<14)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_SHIFT 14
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX13_CF (0x3<<16)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX13_CF_SHIFT 16
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX14_CF (0x3<<18)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX14_CF_SHIFT 18
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX15_CF (0x3<<20)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX15_CF_SHIFT 20
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX16_CF (0x3<<22)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX16_CF_SHIFT 22
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX17_CF (0x3<<24)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX17_CF_SHIFT 24
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ECE_FLAG (0x1<<26)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ECE_FLAG_SHIFT 26
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED71 (0x1<<27)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED71_SHIFT 27
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_FORCE_PURE_ACK_CNT_DIRTY (0x1<<28)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_FORCE_PURE_ACK_CNT_DIRTY_SHIFT 28
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TCP_AUTO_STOP_FLAG (0x1<<29)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TCP_AUTO_STOP_FLAG_SHIFT 29
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DO_TS_UPDATE_FLAG (0x1<<30)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DO_TS_UPDATE_FLAG_SHIFT 30
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CANCEL_RETRANSMIT_FLAG (0x1<<31)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CANCEL_RETRANSMIT_FLAG_SHIFT 31
+#if defined(__BIG_ENDIAN)
+ u16 __agg_misc6;
+ u16 __tcp_agg_vars7;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __tcp_agg_vars7;
+ u16 __agg_misc6;
+#endif
+ u32 __agg_val10;
+ u32 __agg_val10_th;
+#if defined(__BIG_ENDIAN)
+ u16 __reserved3;
+ u8 __reserved2;
+ u8 __da_only_cnt;
+#elif defined(__LITTLE_ENDIAN)
+ u8 __da_only_cnt;
+ u8 __reserved2;
+ u16 __reserved3;
#endif
- u32 __lcq_prod;
- u32 rtt_seq;
- u32 rtt_time;
- u32 __reserved66;
- u32 wnd_right_edge;
- u32 tcp_agg_vars1;
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_FIN_SENT_FLAG (0x1<<0)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_FIN_SENT_FLAG_SHIFT 0
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG (0x1<<1)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG_SHIFT 1
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_WND_UPD_CF (0x3<<2)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_WND_UPD_CF_SHIFT 2
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TIMEOUT_CF (0x3<<4)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TIMEOUT_CF_SHIFT 4
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_WND_UPD_CF_EN (0x1<<6)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_WND_UPD_CF_EN_SHIFT 6
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TIMEOUT_CF_EN (0x1<<7)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TIMEOUT_CF_EN_SHIFT 7
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN (0x1<<8)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN_SHIFT 8
-#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_LCQ_SND_EN (0x1<<9)
-#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_LCQ_SND_EN_SHIFT 9
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<10)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 10
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_FLAG (0x1<<11)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_FLAG_SHIFT 11
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_CF_EN (0x1<<12)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_CF_EN_SHIFT 12
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_CF_EN (0x1<<13)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_CF_EN_SHIFT 13
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_CF (0x3<<14)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX1_CF_SHIFT 14
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_CF (0x3<<16)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX2_CF_SHIFT 16
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TX_BLOCKED (0x1<<18)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_TX_BLOCKED_SHIFT 18
-#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX10_CF_EN (0x1<<19)
-#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX10_CF_EN_SHIFT 19
-#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX11_CF_EN (0x1<<20)
-#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX11_CF_EN_SHIFT 20
-#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX12_CF_EN (0x1<<21)
-#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_AUX12_CF_EN_SHIFT 21
-#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED1 (0x3<<22)
-#define __TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RESERVED1_SHIFT 22
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ (0xF<<24)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ_SHIFT 24
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ (0xF<<28)
-#define TSTORM_FCOE_EXTRA_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ_SHIFT 28
- u32 snd_max;
- u32 __lcq_cons;
- u32 __reserved2;
};
/*
- * The fcoe aggregative context of Tstorm
+ * The iscsi aggregative context of Xstorm
*/
-struct tstorm_fcoe_ag_context {
+struct xstorm_iscsi_ag_context {
#if defined(__BIG_ENDIAN)
- u16 ulp_credit;
+ u16 agg_val1;
u8 agg_vars1;
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
-#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF (0x3<<4)
-#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_SHIFT 4
-#define __TSTORM_FCOE_AG_CONTEXT_AUX3_FLAG (0x1<<6)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX3_FLAG_SHIFT 6
-#define __TSTORM_FCOE_AG_CONTEXT_AUX4_FLAG (0x1<<7)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX4_FLAG_SHIFT 7
+#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN (0x1<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN (0x1<<5)
+#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN_SHIFT 5
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG (0x1<<6)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_SHIFT 6
+#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN (0x1<<7)
+#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN_SHIFT 7
u8 state;
#elif defined(__LITTLE_ENDIAN)
u8 state;
u8 agg_vars1;
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
-#define TSTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
-#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF (0x3<<4)
-#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_SHIFT 4
-#define __TSTORM_FCOE_AG_CONTEXT_AUX3_FLAG (0x1<<6)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX3_FLAG_SHIFT 6
-#define __TSTORM_FCOE_AG_CONTEXT_AUX4_FLAG (0x1<<7)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX4_FLAG_SHIFT 7
- u16 ulp_credit;
+#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN (0x1<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN (0x1<<5)
+#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN_SHIFT 5
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG (0x1<<6)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_SHIFT 6
+#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN (0x1<<7)
+#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN_SHIFT 7
+ u16 agg_val1;
#endif
#if defined(__BIG_ENDIAN)
- u16 __agg_val4;
- u16 agg_vars2;
-#define __TSTORM_FCOE_AG_CONTEXT_AUX5_FLAG (0x1<<0)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX5_FLAG_SHIFT 0
-#define __TSTORM_FCOE_AG_CONTEXT_AUX6_FLAG (0x1<<1)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX6_FLAG_SHIFT 1
-#define __TSTORM_FCOE_AG_CONTEXT_AUX4_CF (0x3<<2)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX4_CF_SHIFT 2
-#define __TSTORM_FCOE_AG_CONTEXT_AUX5_CF (0x3<<4)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX5_CF_SHIFT 4
-#define __TSTORM_FCOE_AG_CONTEXT_AUX6_CF (0x3<<6)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX6_CF_SHIFT 6
-#define __TSTORM_FCOE_AG_CONTEXT_AUX7_CF (0x3<<8)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX7_CF_SHIFT 8
-#define __TSTORM_FCOE_AG_CONTEXT_AUX7_FLAG (0x1<<10)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX7_FLAG_SHIFT 10
-#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_EN (0x1<<11)
-#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_EN_SHIFT 11
-#define TSTORM_FCOE_AG_CONTEXT_AUX4_CF_EN (0x1<<12)
-#define TSTORM_FCOE_AG_CONTEXT_AUX4_CF_EN_SHIFT 12
-#define TSTORM_FCOE_AG_CONTEXT_AUX5_CF_EN (0x1<<13)
-#define TSTORM_FCOE_AG_CONTEXT_AUX5_CF_EN_SHIFT 13
-#define TSTORM_FCOE_AG_CONTEXT_AUX6_CF_EN (0x1<<14)
-#define TSTORM_FCOE_AG_CONTEXT_AUX6_CF_EN_SHIFT 14
-#define TSTORM_FCOE_AG_CONTEXT_AUX7_CF_EN (0x1<<15)
-#define TSTORM_FCOE_AG_CONTEXT_AUX7_CF_EN_SHIFT 15
+ u8 cdu_reserved;
+ u8 __agg_vars4;
+ u8 agg_vars3;
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2 (0x3F<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_RX_TS_EN_CF (0x3<<6)
+#define __XSTORM_ISCSI_AG_CONTEXT_RX_TS_EN_CF_SHIFT 6
+ u8 agg_vars2;
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF (0x3<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN (0x1<<2)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN_SHIFT 2
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG (0x1<<3)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG_SHIFT 3
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG (0x1<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1 (0x3<<5)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1_SHIFT 5
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
#elif defined(__LITTLE_ENDIAN)
- u16 agg_vars2;
-#define __TSTORM_FCOE_AG_CONTEXT_AUX5_FLAG (0x1<<0)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX5_FLAG_SHIFT 0
-#define __TSTORM_FCOE_AG_CONTEXT_AUX6_FLAG (0x1<<1)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX6_FLAG_SHIFT 1
-#define __TSTORM_FCOE_AG_CONTEXT_AUX4_CF (0x3<<2)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX4_CF_SHIFT 2
-#define __TSTORM_FCOE_AG_CONTEXT_AUX5_CF (0x3<<4)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX5_CF_SHIFT 4
-#define __TSTORM_FCOE_AG_CONTEXT_AUX6_CF (0x3<<6)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX6_CF_SHIFT 6
-#define __TSTORM_FCOE_AG_CONTEXT_AUX7_CF (0x3<<8)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX7_CF_SHIFT 8
-#define __TSTORM_FCOE_AG_CONTEXT_AUX7_FLAG (0x1<<10)
-#define __TSTORM_FCOE_AG_CONTEXT_AUX7_FLAG_SHIFT 10
-#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_EN (0x1<<11)
-#define __TSTORM_FCOE_AG_CONTEXT_QUEUE0_FLUSH_CF_EN_SHIFT 11
-#define TSTORM_FCOE_AG_CONTEXT_AUX4_CF_EN (0x1<<12)
-#define TSTORM_FCOE_AG_CONTEXT_AUX4_CF_EN_SHIFT 12
-#define TSTORM_FCOE_AG_CONTEXT_AUX5_CF_EN (0x1<<13)
-#define TSTORM_FCOE_AG_CONTEXT_AUX5_CF_EN_SHIFT 13
-#define TSTORM_FCOE_AG_CONTEXT_AUX6_CF_EN (0x1<<14)
-#define TSTORM_FCOE_AG_CONTEXT_AUX6_CF_EN_SHIFT 14
-#define TSTORM_FCOE_AG_CONTEXT_AUX7_CF_EN (0x1<<15)
-#define TSTORM_FCOE_AG_CONTEXT_AUX7_CF_EN_SHIFT 15
- u16 __agg_val4;
+ u8 agg_vars2;
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF (0x3<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN (0x1<<2)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN_SHIFT 2
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG (0x1<<3)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG_SHIFT 3
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG (0x1<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1 (0x3<<5)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1_SHIFT 5
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
+ u8 agg_vars3;
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2 (0x3F<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_RX_TS_EN_CF (0x3<<6)
+#define __XSTORM_ISCSI_AG_CONTEXT_RX_TS_EN_CF_SHIFT 6
+ u8 __agg_vars4;
+ u8 cdu_reserved;
#endif
- struct tstorm_fcoe_extra_ag_context_section __extra_section;
+ u32 more_to_send;
+#if defined(__BIG_ENDIAN)
+ u16 agg_vars5;
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5 (0x3<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0 (0x3F<<2)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0_SHIFT 2
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1 (0x3F<<8)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1_SHIFT 8
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2 (0x3<<14)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2_SHIFT 14
+ u16 sq_cons;
+#elif defined(__LITTLE_ENDIAN)
+ u16 sq_cons;
+ u16 agg_vars5;
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5 (0x3<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0 (0x3F<<2)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0_SHIFT 2
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1 (0x3F<<8)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1_SHIFT 8
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2 (0x3<<14)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2_SHIFT 14
+#endif
+ struct xstorm_tcp_tcp_ag_context_section tcp;
+#if defined(__BIG_ENDIAN)
+ u16 agg_vars7;
+#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE (0x7<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG (0x1<<3)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG_SHIFT 3
+#define __XSTORM_ISCSI_AG_CONTEXT_STORMS_SYNC_CF (0x3<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_STORMS_SYNC_CF_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3 (0x3<<6)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3_SHIFT 6
+#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF (0x3<<8)
+#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_SHIFT 8
+#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK (0x1<<10)
+#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK_SHIFT 10
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN (0x1<<11)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN_SHIFT 11
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG (0x1<<12)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG_SHIFT 12
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG (0x1<<13)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG_SHIFT 13
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG (0x1<<14)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG_SHIFT 14
+#define __XSTORM_ISCSI_AG_CONTEXT_RX_WND_SCL_EN (0x1<<15)
+#define __XSTORM_ISCSI_AG_CONTEXT_RX_WND_SCL_EN_SHIFT 15
+ u8 agg_val3_th;
+ u8 agg_vars6;
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6 (0x7<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7 (0x7<<3)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7_SHIFT 3
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4 (0x3<<6)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4_SHIFT 6
+#elif defined(__LITTLE_ENDIAN)
+ u8 agg_vars6;
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6 (0x7<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7 (0x7<<3)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7_SHIFT 3
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4 (0x3<<6)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4_SHIFT 6
+ u8 agg_val3_th;
+ u16 agg_vars7;
+#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE (0x7<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG (0x1<<3)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG_SHIFT 3
+#define __XSTORM_ISCSI_AG_CONTEXT_STORMS_SYNC_CF (0x3<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_STORMS_SYNC_CF_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3 (0x3<<6)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3_SHIFT 6
+#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF (0x3<<8)
+#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_SHIFT 8
+#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK (0x1<<10)
+#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK_SHIFT 10
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN (0x1<<11)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN_SHIFT 11
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG (0x1<<12)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG_SHIFT 12
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG (0x1<<13)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG_SHIFT 13
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG (0x1<<14)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG_SHIFT 14
+#define __XSTORM_ISCSI_AG_CONTEXT_RX_WND_SCL_EN (0x1<<15)
+#define __XSTORM_ISCSI_AG_CONTEXT_RX_WND_SCL_EN_SHIFT 15
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 __agg_val11_th;
+ u16 __gen_data;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __gen_data;
+ u16 __agg_val11_th;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 __reserved1;
+ u8 __agg_val6_th;
+ u16 __agg_val9;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __agg_val9;
+ u8 __agg_val6_th;
+ u8 __reserved1;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 hq_prod;
+ u16 hq_cons;
+#elif defined(__LITTLE_ENDIAN)
+ u16 hq_cons;
+ u16 hq_prod;
+#endif
+ u32 agg_vars8;
+#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC2 (0xFFFFFF<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC2_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC3 (0xFF<<24)
+#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC3_SHIFT 24
+#if defined(__BIG_ENDIAN)
+ u16 r2tq_prod;
+ u16 sq_prod;
+#elif defined(__LITTLE_ENDIAN)
+ u16 sq_prod;
+ u16 r2tq_prod;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 agg_val3;
+ u8 agg_val6;
+ u8 agg_val5_th;
+ u8 agg_val5;
+#elif defined(__LITTLE_ENDIAN)
+ u8 agg_val5;
+ u8 agg_val5_th;
+ u8 agg_val6;
+ u8 agg_val3;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 __agg_misc1;
+ u16 agg_limit1;
+#elif defined(__LITTLE_ENDIAN)
+ u16 agg_limit1;
+ u16 __agg_misc1;
+#endif
+ u32 hq_cons_tcp_seq;
+ u32 exp_stat_sn;
+ u32 rst_seq_num;
};
+
/*
- * The fcoe aggregative context of Ustorm
+ * The L5cm aggregative context of XStorm
*/
-struct ustorm_fcoe_ag_context {
+struct xstorm_l5cm_ag_context {
#if defined(__BIG_ENDIAN)
- u8 __aux_counter_flags;
- u8 agg_vars2;
-#define USTORM_FCOE_AG_CONTEXT_TX_CF (0x3<<0)
-#define USTORM_FCOE_AG_CONTEXT_TX_CF_SHIFT 0
-#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF (0x3<<2)
-#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_SHIFT 2
-#define USTORM_FCOE_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4)
-#define USTORM_FCOE_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4
-#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7)
-#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7
+ u16 agg_val1;
u8 agg_vars1;
-#define __USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
-#define __USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
-#define USTORM_FCOE_AG_CONTEXT_INV_CF (0x3<<4)
-#define USTORM_FCOE_AG_CONTEXT_INV_CF_SHIFT 4
-#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF (0x3<<6)
-#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_SHIFT 6
+#define __XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __XSTORM_L5CM_AG_CONTEXT_MORE_TO_SEND_EN (0x1<<4)
+#define __XSTORM_L5CM_AG_CONTEXT_MORE_TO_SEND_EN_SHIFT 4
+#define XSTORM_L5CM_AG_CONTEXT_NAGLE_EN (0x1<<5)
+#define XSTORM_L5CM_AG_CONTEXT_NAGLE_EN_SHIFT 5
+#define __XSTORM_L5CM_AG_CONTEXT_DQ_SPARE_FLAG (0x1<<6)
+#define __XSTORM_L5CM_AG_CONTEXT_DQ_SPARE_FLAG_SHIFT 6
+#define __XSTORM_L5CM_AG_CONTEXT_UNA_GT_NXT_EN (0x1<<7)
+#define __XSTORM_L5CM_AG_CONTEXT_UNA_GT_NXT_EN_SHIFT 7
u8 state;
#elif defined(__LITTLE_ENDIAN)
u8 state;
u8 agg_vars1;
-#define __USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
-#define __USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
-#define USTORM_FCOE_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
-#define USTORM_FCOE_AG_CONTEXT_INV_CF (0x3<<4)
-#define USTORM_FCOE_AG_CONTEXT_INV_CF_SHIFT 4
-#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF (0x3<<6)
-#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_SHIFT 6
- u8 agg_vars2;
-#define USTORM_FCOE_AG_CONTEXT_TX_CF (0x3<<0)
-#define USTORM_FCOE_AG_CONTEXT_TX_CF_SHIFT 0
-#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF (0x3<<2)
-#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_SHIFT 2
-#define USTORM_FCOE_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4)
-#define USTORM_FCOE_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4
-#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7)
-#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7
- u8 __aux_counter_flags;
+#define __XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define XSTORM_L5CM_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __XSTORM_L5CM_AG_CONTEXT_MORE_TO_SEND_EN (0x1<<4)
+#define __XSTORM_L5CM_AG_CONTEXT_MORE_TO_SEND_EN_SHIFT 4
+#define XSTORM_L5CM_AG_CONTEXT_NAGLE_EN (0x1<<5)
+#define XSTORM_L5CM_AG_CONTEXT_NAGLE_EN_SHIFT 5
+#define __XSTORM_L5CM_AG_CONTEXT_DQ_SPARE_FLAG (0x1<<6)
+#define __XSTORM_L5CM_AG_CONTEXT_DQ_SPARE_FLAG_SHIFT 6
+#define __XSTORM_L5CM_AG_CONTEXT_UNA_GT_NXT_EN (0x1<<7)
+#define __XSTORM_L5CM_AG_CONTEXT_UNA_GT_NXT_EN_SHIFT 7
+ u16 agg_val1;
#endif
#if defined(__BIG_ENDIAN)
- u8 cdu_usage;
- u8 agg_misc2;
- u16 pbf_tx_seq_ack;
+ u8 cdu_reserved;
+ u8 __agg_vars4;
+ u8 agg_vars3;
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM2 (0x3F<<0)
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM2_SHIFT 0
+#define __XSTORM_L5CM_AG_CONTEXT_RX_TS_EN_CF (0x3<<6)
+#define __XSTORM_L5CM_AG_CONTEXT_RX_TS_EN_CF_SHIFT 6
+ u8 agg_vars2;
+#define XSTORM_L5CM_AG_CONTEXT_AUX4_CF (0x3<<0)
+#define XSTORM_L5CM_AG_CONTEXT_AUX4_CF_SHIFT 0
+#define __XSTORM_L5CM_AG_CONTEXT_DQ_SPARE_FLAG_EN (0x1<<2)
+#define __XSTORM_L5CM_AG_CONTEXT_DQ_SPARE_FLAG_EN_SHIFT 2
+#define __XSTORM_L5CM_AG_CONTEXT_AUX8_FLAG (0x1<<3)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX8_FLAG_SHIFT 3
+#define __XSTORM_L5CM_AG_CONTEXT_AUX9_FLAG (0x1<<4)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX9_FLAG_SHIFT 4
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE1 (0x3<<5)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE1_SHIFT 5
+#define XSTORM_L5CM_AG_CONTEXT_AUX4_CF_EN (0x1<<7)
+#define XSTORM_L5CM_AG_CONTEXT_AUX4_CF_EN_SHIFT 7
#elif defined(__LITTLE_ENDIAN)
- u16 pbf_tx_seq_ack;
- u8 agg_misc2;
- u8 cdu_usage;
+ u8 agg_vars2;
+#define XSTORM_L5CM_AG_CONTEXT_AUX4_CF (0x3<<0)
+#define XSTORM_L5CM_AG_CONTEXT_AUX4_CF_SHIFT 0
+#define __XSTORM_L5CM_AG_CONTEXT_DQ_SPARE_FLAG_EN (0x1<<2)
+#define __XSTORM_L5CM_AG_CONTEXT_DQ_SPARE_FLAG_EN_SHIFT 2
+#define __XSTORM_L5CM_AG_CONTEXT_AUX8_FLAG (0x1<<3)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX8_FLAG_SHIFT 3
+#define __XSTORM_L5CM_AG_CONTEXT_AUX9_FLAG (0x1<<4)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX9_FLAG_SHIFT 4
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE1 (0x3<<5)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE1_SHIFT 5
+#define XSTORM_L5CM_AG_CONTEXT_AUX4_CF_EN (0x1<<7)
+#define XSTORM_L5CM_AG_CONTEXT_AUX4_CF_EN_SHIFT 7
+ u8 agg_vars3;
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM2 (0x3F<<0)
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM2_SHIFT 0
+#define __XSTORM_L5CM_AG_CONTEXT_RX_TS_EN_CF (0x3<<6)
+#define __XSTORM_L5CM_AG_CONTEXT_RX_TS_EN_CF_SHIFT 6
+ u8 __agg_vars4;
+ u8 cdu_reserved;
#endif
- u32 agg_misc4;
+ u32 more_to_send;
+#if defined(__BIG_ENDIAN)
+ u16 agg_vars5;
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE5 (0x3<<0)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE5_SHIFT 0
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM0 (0x3F<<2)
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM0_SHIFT 2
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM1 (0x3F<<8)
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM1_SHIFT 8
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE2 (0x3<<14)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE2_SHIFT 14
+ u16 agg_val4_th;
+#elif defined(__LITTLE_ENDIAN)
+ u16 agg_val4_th;
+ u16 agg_vars5;
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE5 (0x3<<0)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE5_SHIFT 0
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM0 (0x3F<<2)
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM0_SHIFT 2
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM1 (0x3F<<8)
+#define XSTORM_L5CM_AG_CONTEXT_PHYSICAL_QUEUE_NUM1_SHIFT 8
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE2 (0x3<<14)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE2_SHIFT 14
+#endif
+ struct xstorm_tcp_tcp_ag_context_section tcp;
#if defined(__BIG_ENDIAN)
+ u16 agg_vars7;
+#define __XSTORM_L5CM_AG_CONTEXT_AGG_VAL11_DECISION_RULE (0x7<<0)
+#define __XSTORM_L5CM_AG_CONTEXT_AGG_VAL11_DECISION_RULE_SHIFT 0
+#define __XSTORM_L5CM_AG_CONTEXT_AUX13_FLAG (0x1<<3)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX13_FLAG_SHIFT 3
+#define __XSTORM_L5CM_AG_CONTEXT_STORMS_SYNC_CF (0x3<<4)
+#define __XSTORM_L5CM_AG_CONTEXT_STORMS_SYNC_CF_SHIFT 4
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE3 (0x3<<6)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE3_SHIFT 6
+#define XSTORM_L5CM_AG_CONTEXT_AUX1_CF (0x3<<8)
+#define XSTORM_L5CM_AG_CONTEXT_AUX1_CF_SHIFT 8
+#define __XSTORM_L5CM_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK (0x1<<10)
+#define __XSTORM_L5CM_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK_SHIFT 10
+#define __XSTORM_L5CM_AG_CONTEXT_AUX1_CF_EN (0x1<<11)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX1_CF_EN_SHIFT 11
+#define __XSTORM_L5CM_AG_CONTEXT_AUX10_FLAG (0x1<<12)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX10_FLAG_SHIFT 12
+#define __XSTORM_L5CM_AG_CONTEXT_AUX11_FLAG (0x1<<13)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX11_FLAG_SHIFT 13
+#define __XSTORM_L5CM_AG_CONTEXT_AUX12_FLAG (0x1<<14)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX12_FLAG_SHIFT 14
+#define __XSTORM_L5CM_AG_CONTEXT_RX_WND_SCL_EN (0x1<<15)
+#define __XSTORM_L5CM_AG_CONTEXT_RX_WND_SCL_EN_SHIFT 15
u8 agg_val3_th;
+ u8 agg_vars6;
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE6 (0x7<<0)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE6_SHIFT 0
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE7 (0x7<<3)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE7_SHIFT 3
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE4 (0x3<<6)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE4_SHIFT 6
+#elif defined(__LITTLE_ENDIAN)
+ u8 agg_vars6;
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE6 (0x7<<0)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE6_SHIFT 0
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE7 (0x7<<3)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE7_SHIFT 3
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE4 (0x3<<6)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE4_SHIFT 6
+ u8 agg_val3_th;
+ u16 agg_vars7;
+#define __XSTORM_L5CM_AG_CONTEXT_AGG_VAL11_DECISION_RULE (0x7<<0)
+#define __XSTORM_L5CM_AG_CONTEXT_AGG_VAL11_DECISION_RULE_SHIFT 0
+#define __XSTORM_L5CM_AG_CONTEXT_AUX13_FLAG (0x1<<3)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX13_FLAG_SHIFT 3
+#define __XSTORM_L5CM_AG_CONTEXT_STORMS_SYNC_CF (0x3<<4)
+#define __XSTORM_L5CM_AG_CONTEXT_STORMS_SYNC_CF_SHIFT 4
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE3 (0x3<<6)
+#define XSTORM_L5CM_AG_CONTEXT_DECISION_RULE3_SHIFT 6
+#define XSTORM_L5CM_AG_CONTEXT_AUX1_CF (0x3<<8)
+#define XSTORM_L5CM_AG_CONTEXT_AUX1_CF_SHIFT 8
+#define __XSTORM_L5CM_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK (0x1<<10)
+#define __XSTORM_L5CM_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK_SHIFT 10
+#define __XSTORM_L5CM_AG_CONTEXT_AUX1_CF_EN (0x1<<11)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX1_CF_EN_SHIFT 11
+#define __XSTORM_L5CM_AG_CONTEXT_AUX10_FLAG (0x1<<12)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX10_FLAG_SHIFT 12
+#define __XSTORM_L5CM_AG_CONTEXT_AUX11_FLAG (0x1<<13)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX11_FLAG_SHIFT 13
+#define __XSTORM_L5CM_AG_CONTEXT_AUX12_FLAG (0x1<<14)
+#define __XSTORM_L5CM_AG_CONTEXT_AUX12_FLAG_SHIFT 14
+#define __XSTORM_L5CM_AG_CONTEXT_RX_WND_SCL_EN (0x1<<15)
+#define __XSTORM_L5CM_AG_CONTEXT_RX_WND_SCL_EN_SHIFT 15
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 __agg_val11_th;
+ u16 __gen_data;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __gen_data;
+ u16 __agg_val11_th;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 __reserved1;
+ u8 __agg_val6_th;
+ u16 __agg_val9;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __agg_val9;
+ u8 __agg_val6_th;
+ u8 __reserved1;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 agg_val2_th;
+ u16 agg_val2;
+#elif defined(__LITTLE_ENDIAN)
+ u16 agg_val2;
+ u16 agg_val2_th;
+#endif
+ u32 agg_vars8;
+#define XSTORM_L5CM_AG_CONTEXT_AGG_MISC2 (0xFFFFFF<<0)
+#define XSTORM_L5CM_AG_CONTEXT_AGG_MISC2_SHIFT 0
+#define XSTORM_L5CM_AG_CONTEXT_AGG_MISC3 (0xFF<<24)
+#define XSTORM_L5CM_AG_CONTEXT_AGG_MISC3_SHIFT 24
+#if defined(__BIG_ENDIAN)
+ u16 agg_misc0;
+ u16 agg_val4;
+#elif defined(__LITTLE_ENDIAN)
+ u16 agg_val4;
+ u16 agg_misc0;
+#endif
+#if defined(__BIG_ENDIAN)
u8 agg_val3;
- u16 agg_misc3;
+ u8 agg_val6;
+ u8 agg_val5_th;
+ u8 agg_val5;
#elif defined(__LITTLE_ENDIAN)
- u16 agg_misc3;
+ u8 agg_val5;
+ u8 agg_val5_th;
+ u8 agg_val6;
u8 agg_val3;
- u8 agg_val3_th;
#endif
- u32 expired_task_id;
- u32 agg_misc4_th;
#if defined(__BIG_ENDIAN)
- u16 cq_prod;
+ u16 __agg_misc1;
+ u16 agg_limit1;
+#elif defined(__LITTLE_ENDIAN)
+ u16 agg_limit1;
+ u16 __agg_misc1;
+#endif
+ u32 completion_seq;
+ u32 agg_misc4;
+ u32 rst_seq_num;
+};
+
+/*
+ * ABTS info $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_abts_info {
+ __le16 aborted_task_id;
+ __le16 reserved0;
+ __le32 reserved1;
+};
+
+
+/*
+ * Fixed size structure in order to plant it in Union structure
+ * $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_abts_rsp_union {
+ u8 r_ctl;
+ u8 rsrv[3];
+ __le32 abts_rsp_payload[7];
+};
+
+
+/*
+ * 4 regs size $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_bd_ctx {
+ __le32 buf_addr_hi;
+ __le32 buf_addr_lo;
+ __le16 buf_len;
+ __le16 rsrv0;
+ __le16 flags;
+ __le16 rsrv1;
+};
+
+
+/*
+ * FCoE cached sges context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_cached_sge_ctx {
+ struct regpair cur_buf_addr;
+ __le16 cur_buf_rem;
+ __le16 second_buf_rem;
+ struct regpair second_buf_addr;
+};
+
+
+/*
+ * Cleanup info $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_cleanup_info {
+ __le16 cleaned_task_id;
+ __le16 rolled_tx_seq_cnt;
+ __le32 rolled_tx_data_offset;
+};
+
+
+/*
+ * Fcp RSP flags $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fcp_rsp_flags {
+ u8 flags;
+#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID (0x1<<0)
+#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID_SHIFT 0
+#define FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID (0x1<<1)
+#define FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID_SHIFT 1
+#define FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER (0x1<<2)
+#define FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER_SHIFT 2
+#define FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER (0x1<<3)
+#define FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER_SHIFT 3
+#define FCOE_FCP_RSP_FLAGS_FCP_CONF_REQ (0x1<<4)
+#define FCOE_FCP_RSP_FLAGS_FCP_CONF_REQ_SHIFT 4
+#define FCOE_FCP_RSP_FLAGS_FCP_BIDI_FLAGS (0x7<<5)
+#define FCOE_FCP_RSP_FLAGS_FCP_BIDI_FLAGS_SHIFT 5
+};
+
+/*
+ * Fcp RSP payload $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fcp_rsp_payload {
+ struct regpair reserved0;
+ __le32 fcp_resid;
+ u8 scsi_status_code;
+ struct fcoe_fcp_rsp_flags fcp_flags;
+ __le16 retry_delay_timer;
+ __le32 fcp_rsp_len;
+ __le32 fcp_sns_len;
+};
+
+/*
+ * Fixed size structure in order to plant it in Union structure
+ * $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fcp_rsp_union {
+ struct fcoe_fcp_rsp_payload payload;
+ struct regpair reserved0;
+};
+
+/*
+ * FC header $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fc_hdr {
+ u8 s_id[3];
+ u8 cs_ctl;
+ u8 d_id[3];
+ u8 r_ctl;
+ __le16 seq_cnt;
+ u8 df_ctl;
+ u8 seq_id;
+ u8 f_ctl[3];
+ u8 type;
+ __le32 parameters;
+ __le16 rx_id;
+ __le16 ox_id;
+};
+
+/*
+ * FC header union $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_mp_rsp_union {
+ struct fcoe_fc_hdr fc_hdr;
+ __le32 mp_payload_len;
+ __le32 rsrv;
+};
+
+/*
+ * Completion information $$KEEP_ENDIANNESS$$
+ */
+union fcoe_comp_flow_info {
+ struct fcoe_fcp_rsp_union fcp_rsp;
+ struct fcoe_abts_rsp_union abts_rsp;
+ struct fcoe_mp_rsp_union mp_rsp;
+ __le32 opaque[8];
+};
+
+
+/*
+ * External ABTS info $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_ext_abts_info {
+ __le32 rsrv0[6];
+ struct fcoe_abts_info ctx;
+};
+
+
+/*
+ * External cleanup info $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_ext_cleanup_info {
+ __le32 rsrv0[6];
+ struct fcoe_cleanup_info ctx;
+};
+
+
+/*
+ * Fcoe FW Tx sequence context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fw_tx_seq_ctx {
+ __le32 data_offset;
+ __le16 seq_cnt;
+ __le16 rsrv0;
+};
+
+/*
+ * Fcoe external FW Tx sequence context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_ext_fw_tx_seq_ctx {
+ __le32 rsrv0[6];
+ struct fcoe_fw_tx_seq_ctx ctx;
+};
+
+
+/*
+ * FCoE multiple sges context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_mul_sges_ctx {
+ struct regpair cur_sge_addr;
+ __le16 cur_sge_off;
+ u8 cur_sge_idx;
+ u8 sgl_size;
+};
+
+/*
+ * FCoE external multiple sges context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_ext_mul_sges_ctx {
+ struct fcoe_mul_sges_ctx mul_sgl;
+ struct regpair rsrv0;
+};
+
+
+/*
+ * FCP CMD payload $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fcp_cmd_payload {
+ __le32 opaque[8];
+};
+
+
+
+
+
+/*
+ * Fcp xfr rdy payload $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fcp_xfr_rdy_payload {
+ __le32 burst_len;
+ __le32 data_ro;
+};
+
+
+/*
+ * FC frame $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fc_frame {
+ struct fcoe_fc_hdr fc_hdr;
+ __le32 reserved0[2];
+};
+
+
+
+
+/*
+ * FCoE KCQ CQE parameters $$KEEP_ENDIANNESS$$
+ */
+union fcoe_kcqe_params {
+ __le32 reserved0[4];
+};
+
+/*
+ * FCoE KCQ CQE $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kcqe {
+ __le32 fcoe_conn_id;
+ __le32 completion_status;
+ __le32 fcoe_conn_context_id;
+ union fcoe_kcqe_params params;
+ __le16 qe_self_seq;
+ u8 op_code;
+ u8 flags;
+#define FCOE_KCQE_RESERVED0 (0x7<<0)
+#define FCOE_KCQE_RESERVED0_SHIFT 0
+#define FCOE_KCQE_RAMROD_COMPLETION (0x1<<3)
+#define FCOE_KCQE_RAMROD_COMPLETION_SHIFT 3
+#define FCOE_KCQE_LAYER_CODE (0x7<<4)
+#define FCOE_KCQE_LAYER_CODE_SHIFT 4
+#define FCOE_KCQE_LINKED_WITH_NEXT (0x1<<7)
+#define FCOE_KCQE_LINKED_WITH_NEXT_SHIFT 7
+};
+
+
+
+/*
+ * FCoE KWQE header $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_header {
+ u8 op_code;
+ u8 flags;
+#define FCOE_KWQE_HEADER_RESERVED0 (0xF<<0)
+#define FCOE_KWQE_HEADER_RESERVED0_SHIFT 0
+#define FCOE_KWQE_HEADER_LAYER_CODE (0x7<<4)
+#define FCOE_KWQE_HEADER_LAYER_CODE_SHIFT 4
+#define FCOE_KWQE_HEADER_RESERVED1 (0x1<<7)
+#define FCOE_KWQE_HEADER_RESERVED1_SHIFT 7
+};
+
+/*
+ * FCoE firmware init request 1 $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_init1 {
+ __le16 num_tasks;
+ struct fcoe_kwqe_header hdr;
+ __le32 task_list_pbl_addr_lo;
+ __le32 task_list_pbl_addr_hi;
+ __le32 dummy_buffer_addr_lo;
+ __le32 dummy_buffer_addr_hi;
+ __le16 sq_num_wqes;
+ __le16 rq_num_wqes;
+ __le16 rq_buffer_log_size;
+ __le16 cq_num_wqes;
+ __le16 mtu;
+ u8 num_sessions_log;
+ u8 flags;
+#define FCOE_KWQE_INIT1_LOG_PAGE_SIZE (0xF<<0)
+#define FCOE_KWQE_INIT1_LOG_PAGE_SIZE_SHIFT 0
+#define FCOE_KWQE_INIT1_LOG_CACHED_PBES_PER_FUNC (0x7<<4)
+#define FCOE_KWQE_INIT1_LOG_CACHED_PBES_PER_FUNC_SHIFT 4
+#define FCOE_KWQE_INIT1_RESERVED1 (0x1<<7)
+#define FCOE_KWQE_INIT1_RESERVED1_SHIFT 7
+};
+
+/*
+ * FCoE firmware init request 2 $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_init2 {
+ u8 hsi_major_version;
+ u8 hsi_minor_version;
+ struct fcoe_kwqe_header hdr;
+ __le32 hash_tbl_pbl_addr_lo;
+ __le32 hash_tbl_pbl_addr_hi;
+ __le32 t2_hash_tbl_addr_lo;
+ __le32 t2_hash_tbl_addr_hi;
+ __le32 t2_ptr_hash_tbl_addr_lo;
+ __le32 t2_ptr_hash_tbl_addr_hi;
+ __le32 free_list_count;
+};
+
+/*
+ * FCoE firmware init request 3 $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_init3 {
+ __le16 reserved0;
+ struct fcoe_kwqe_header hdr;
+ __le32 error_bit_map_lo;
+ __le32 error_bit_map_hi;
+ u8 perf_config;
+ u8 reserved21[3];
+ __le32 reserved2[4];
+};
+
+/*
+ * FCoE connection offload request 1 $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_conn_offload1 {
+ __le16 fcoe_conn_id;
+ struct fcoe_kwqe_header hdr;
+ __le32 sq_addr_lo;
+ __le32 sq_addr_hi;
+ __le32 rq_pbl_addr_lo;
+ __le32 rq_pbl_addr_hi;
+ __le32 rq_first_pbe_addr_lo;
+ __le32 rq_first_pbe_addr_hi;
+ __le16 rq_prod;
+ __le16 reserved0;
+};
+
+/*
+ * FCoE connection offload request 2 $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_conn_offload2 {
+ __le16 tx_max_fc_pay_len;
+ struct fcoe_kwqe_header hdr;
+ __le32 cq_addr_lo;
+ __le32 cq_addr_hi;
+ __le32 xferq_addr_lo;
+ __le32 xferq_addr_hi;
+ __le32 conn_db_addr_lo;
+ __le32 conn_db_addr_hi;
+ __le32 reserved1;
+};
+
+/*
+ * FCoE connection offload request 3 $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_conn_offload3 {
+ __le16 vlan_tag;
+#define FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID (0xFFF<<0)
+#define FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID_SHIFT 0
+#define FCOE_KWQE_CONN_OFFLOAD3_CFI (0x1<<12)
+#define FCOE_KWQE_CONN_OFFLOAD3_CFI_SHIFT 12
+#define FCOE_KWQE_CONN_OFFLOAD3_PRIORITY (0x7<<13)
+#define FCOE_KWQE_CONN_OFFLOAD3_PRIORITY_SHIFT 13
+ struct fcoe_kwqe_header hdr;
+ u8 s_id[3];
+ u8 tx_max_conc_seqs_c3;
+ u8 d_id[3];
+ u8 flags;
+#define FCOE_KWQE_CONN_OFFLOAD3_B_MUL_N_PORT_IDS (0x1<<0)
+#define FCOE_KWQE_CONN_OFFLOAD3_B_MUL_N_PORT_IDS_SHIFT 0
+#define FCOE_KWQE_CONN_OFFLOAD3_B_E_D_TOV_RES (0x1<<1)
+#define FCOE_KWQE_CONN_OFFLOAD3_B_E_D_TOV_RES_SHIFT 1
+#define FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT (0x1<<2)
+#define FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT_SHIFT 2
+#define FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ (0x1<<3)
+#define FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ_SHIFT 3
+#define FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID (0x1<<4)
+#define FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID_SHIFT 4
+#define FCOE_KWQE_CONN_OFFLOAD3_B_C2_VALID (0x1<<5)
+#define FCOE_KWQE_CONN_OFFLOAD3_B_C2_VALID_SHIFT 5
+#define FCOE_KWQE_CONN_OFFLOAD3_B_ACK_0 (0x1<<6)
+#define FCOE_KWQE_CONN_OFFLOAD3_B_ACK_0_SHIFT 6
+#define FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG (0x1<<7)
+#define FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG_SHIFT 7
+ __le32 reserved;
+ __le32 confq_first_pbe_addr_lo;
+ __le32 confq_first_pbe_addr_hi;
+ __le16 tx_total_conc_seqs;
+ __le16 rx_max_fc_pay_len;
+ __le16 rx_total_conc_seqs;
+ u8 rx_max_conc_seqs_c3;
+ u8 rx_open_seqs_exch_c3;
+};
+
+/*
+ * FCoE connection offload request 4 $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_conn_offload4 {
+ u8 e_d_tov_timer_val;
+ u8 reserved2;
+ struct fcoe_kwqe_header hdr;
+ u8 src_mac_addr_lo[2];
+ u8 src_mac_addr_mid[2];
+ u8 src_mac_addr_hi[2];
+ u8 dst_mac_addr_hi[2];
+ u8 dst_mac_addr_lo[2];
+ u8 dst_mac_addr_mid[2];
+ __le32 lcq_addr_lo;
+ __le32 lcq_addr_hi;
+ __le32 confq_pbl_base_addr_lo;
+ __le32 confq_pbl_base_addr_hi;
+};
+
+/*
+ * FCoE connection enable request $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_conn_enable_disable {
+ __le16 reserved0;
+ struct fcoe_kwqe_header hdr;
+ u8 src_mac_addr_lo[2];
+ u8 src_mac_addr_mid[2];
+ u8 src_mac_addr_hi[2];
+ u16 vlan_tag;
+#define FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID (0xFFF<<0)
+#define FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT 0
+#define FCOE_KWQE_CONN_ENABLE_DISABLE_CFI (0x1<<12)
+#define FCOE_KWQE_CONN_ENABLE_DISABLE_CFI_SHIFT 12
+#define FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY (0x7<<13)
+#define FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT 13
+ u8 dst_mac_addr_lo[2];
+ u8 dst_mac_addr_mid[2];
+ u8 dst_mac_addr_hi[2];
+ __le16 reserved1;
+ u8 s_id[3];
+ u8 vlan_flag;
+ u8 d_id[3];
+ u8 reserved3;
+ __le32 context_id;
+ __le32 conn_id;
+ __le32 reserved4;
+};
+
+/*
+ * FCoE connection destroy request $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_conn_destroy {
+ __le16 reserved0;
+ struct fcoe_kwqe_header hdr;
+ __le32 context_id;
+ __le32 conn_id;
+ __le32 reserved1[5];
+};
+
+/*
+ * FCoe destroy request $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_destroy {
+ __le16 reserved0;
+ struct fcoe_kwqe_header hdr;
+ __le32 reserved1[7];
+};
+
+/*
+ * FCoe statistics request $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_kwqe_stat {
+ __le16 reserved0;
+ struct fcoe_kwqe_header hdr;
+ __le32 stat_params_addr_lo;
+ __le32 stat_params_addr_hi;
+ __le32 reserved1[5];
+};
+
+/*
+ * FCoE KWQ WQE $$KEEP_ENDIANNESS$$
+ */
+union fcoe_kwqe {
+ struct fcoe_kwqe_init1 init1;
+ struct fcoe_kwqe_init2 init2;
+ struct fcoe_kwqe_init3 init3;
+ struct fcoe_kwqe_conn_offload1 conn_offload1;
+ struct fcoe_kwqe_conn_offload2 conn_offload2;
+ struct fcoe_kwqe_conn_offload3 conn_offload3;
+ struct fcoe_kwqe_conn_offload4 conn_offload4;
+ struct fcoe_kwqe_conn_enable_disable conn_enable_disable;
+ struct fcoe_kwqe_conn_destroy conn_destroy;
+ struct fcoe_kwqe_destroy destroy;
+ struct fcoe_kwqe_stat statistics;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * TX SGL context $$KEEP_ENDIANNESS$$
+ */
+union fcoe_sgl_union_ctx {
+ struct fcoe_cached_sge_ctx cached_sge;
+ struct fcoe_ext_mul_sges_ctx sgl;
+ __le32 opaque[5];
+};
+
+/*
+ * Data-In/ELS/BLS information $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_read_flow_info {
+ union fcoe_sgl_union_ctx sgl_ctx;
+ __le32 rsrv0[3];
+};
+
+
+/*
+ * Fcoe stat context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_s_stat_ctx {
+ u8 flags;
+#define FCOE_S_STAT_CTX_ACTIVE (0x1<<0)
+#define FCOE_S_STAT_CTX_ACTIVE_SHIFT 0
+#define FCOE_S_STAT_CTX_ACK_ABORT_SEQ_COND (0x1<<1)
+#define FCOE_S_STAT_CTX_ACK_ABORT_SEQ_COND_SHIFT 1
+#define FCOE_S_STAT_CTX_ABTS_PERFORMED (0x1<<2)
+#define FCOE_S_STAT_CTX_ABTS_PERFORMED_SHIFT 2
+#define FCOE_S_STAT_CTX_SEQ_TIMEOUT (0x1<<3)
+#define FCOE_S_STAT_CTX_SEQ_TIMEOUT_SHIFT 3
+#define FCOE_S_STAT_CTX_P_RJT (0x1<<4)
+#define FCOE_S_STAT_CTX_P_RJT_SHIFT 4
+#define FCOE_S_STAT_CTX_ACK_EOFT (0x1<<5)
+#define FCOE_S_STAT_CTX_ACK_EOFT_SHIFT 5
+#define FCOE_S_STAT_CTX_RSRV1 (0x3<<6)
+#define FCOE_S_STAT_CTX_RSRV1_SHIFT 6
+};
+
+/*
+ * Fcoe rx seq context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_rx_seq_ctx {
+ u8 seq_id;
+ struct fcoe_s_stat_ctx s_stat;
+ __le16 seq_cnt;
+ __le32 low_exp_ro;
+ __le32 high_exp_ro;
+};
+
+
+/*
+ * Fcoe rx_wr union context $$KEEP_ENDIANNESS$$
+ */
+union fcoe_rx_wr_union_ctx {
+ struct fcoe_read_flow_info read_info;
+ union fcoe_comp_flow_info comp_info;
+ __le32 opaque[8];
+};
+
+
+
+/*
+ * FCoE SQ element $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_sqe {
+ __le16 wqe;
+#define FCOE_SQE_TASK_ID (0x7FFF<<0)
+#define FCOE_SQE_TASK_ID_SHIFT 0
+#define FCOE_SQE_TOGGLE_BIT (0x1<<15)
+#define FCOE_SQE_TOGGLE_BIT_SHIFT 15
+};
+
+
+
+/*
+ * 14 regs $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_tx_only {
+ union fcoe_sgl_union_ctx sgl_ctx;
+ __le32 rsrv0;
+};
+
+/*
+ * 32 bytes (8 regs) used for TX only purposes $$KEEP_ENDIANNESS$$
+ */
+union fcoe_tx_wr_rx_rd_union_ctx {
+ struct fcoe_fc_frame tx_frame;
+ struct fcoe_fcp_cmd_payload fcp_cmd;
+ struct fcoe_ext_cleanup_info cleanup;
+ struct fcoe_ext_abts_info abts;
+ struct fcoe_ext_fw_tx_seq_ctx tx_seq;
+ __le32 opaque[8];
+};
+
+/*
+ * tce_tx_wr_rx_rd_const $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_tx_wr_rx_rd_const {
+ u8 init_flags;
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE (0x7<<0)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE_SHIFT 0
+#define FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE (0x1<<3)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT 3
+#define FCOE_TCE_TX_WR_RX_RD_CONST_CLASS_TYPE (0x1<<4)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_CLASS_TYPE_SHIFT 4
+#define FCOE_TCE_TX_WR_RX_RD_CONST_CACHED_SGE (0x3<<5)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_CACHED_SGE_SHIFT 5
+#define FCOE_TCE_TX_WR_RX_RD_CONST_SUPPORT_REC_TOV (0x1<<7)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_SUPPORT_REC_TOV_SHIFT 7
+ u8 tx_flags;
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_VALID (0x1<<0)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_VALID_SHIFT 0
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_STATE (0xF<<1)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_STATE_SHIFT 1
+#define FCOE_TCE_TX_WR_RX_RD_CONST_RSRV1 (0x1<<5)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_RSRV1_SHIFT 5
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_SEQ_INIT (0x1<<6)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_SEQ_INIT_SHIFT 6
+#define FCOE_TCE_TX_WR_RX_RD_CONST_RSRV2 (0x1<<7)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_RSRV2_SHIFT 7
+ __le16 rsrv3;
+ __le32 verify_tx_seq;
+};
+
+/*
+ * tce_tx_wr_rx_rd $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_tx_wr_rx_rd {
+ union fcoe_tx_wr_rx_rd_union_ctx union_ctx;
+ struct fcoe_tce_tx_wr_rx_rd_const const_ctx;
+};
+
+/*
+ * tce_rx_wr_tx_rd_const $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_rx_wr_tx_rd_const {
+ __le32 data_2_trns;
+ __le32 init_flags;
+#define FCOE_TCE_RX_WR_TX_RD_CONST_CID (0xFFFFFF<<0)
+#define FCOE_TCE_RX_WR_TX_RD_CONST_CID_SHIFT 0
+#define FCOE_TCE_RX_WR_TX_RD_CONST_RSRV0 (0xFF<<24)
+#define FCOE_TCE_RX_WR_TX_RD_CONST_RSRV0_SHIFT 24
+};
+
+/*
+ * tce_rx_wr_tx_rd_var $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_rx_wr_tx_rd_var {
+ __le16 rx_flags;
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RSRV1 (0xF<<0)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RSRV1_SHIFT 0
+#define FCOE_TCE_RX_WR_TX_RD_VAR_NUM_RQ_WQE (0x7<<4)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_NUM_RQ_WQE_SHIFT 4
+#define FCOE_TCE_RX_WR_TX_RD_VAR_CONF_REQ (0x1<<7)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_CONF_REQ_SHIFT 7
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_STATE (0xF<<8)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_STATE_SHIFT 8
+#define FCOE_TCE_RX_WR_TX_RD_VAR_EXP_FIRST_FRAME (0x1<<12)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_EXP_FIRST_FRAME_SHIFT 12
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_SEQ_INIT (0x1<<13)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_SEQ_INIT_SHIFT 13
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RSRV2 (0x1<<14)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RSRV2_SHIFT 14
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_VALID (0x1<<15)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_VALID_SHIFT 15
+ __le16 rx_id;
+ struct fcoe_fcp_xfr_rdy_payload fcp_xfr_rdy;
+};
+
+/*
+ * tce_rx_wr_tx_rd $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_rx_wr_tx_rd {
+ struct fcoe_tce_rx_wr_tx_rd_const const_ctx;
+ struct fcoe_tce_rx_wr_tx_rd_var var_ctx;
+};
+
+/*
+ * tce_rx_only $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_rx_only {
+ struct fcoe_rx_seq_ctx rx_seq_ctx;
+ union fcoe_rx_wr_union_ctx union_ctx;
+};
+
+/*
+ * task_ctx_entry $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_task_ctx_entry {
+ struct fcoe_tce_tx_only txwr_only;
+ struct fcoe_tce_tx_wr_rx_rd txwr_rxrd;
+ struct fcoe_tce_rx_wr_tx_rd rxwr_txrd;
+ struct fcoe_tce_rx_only rxwr_only;
+};
+
+
+
+
+
+
+
+
+
+
+/*
+ * FCoE XFRQ element $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_xfrqe {
+ __le16 wqe;
+#define FCOE_XFRQE_TASK_ID (0x7FFF<<0)
+#define FCOE_XFRQE_TASK_ID_SHIFT 0
+#define FCOE_XFRQE_TOGGLE_BIT (0x1<<15)
+#define FCOE_XFRQE_TOGGLE_BIT_SHIFT 15
+};
+
+
+/*
+ * Cached SGEs $$KEEP_ENDIANNESS$$
+ */
+struct common_fcoe_sgl {
+ struct fcoe_bd_ctx sge[3];
+};
+
+
+/*
+ * FCoE SQ\XFRQ element
+ */
+struct fcoe_cached_wqe {
+ struct fcoe_sqe sqe;
+ struct fcoe_xfrqe xfrqe;
+};
+
+
+/*
+ * FCoE connection enable\disable params passed by driver to FW in FCoE enable
+ * ramrod $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_conn_enable_disable_ramrod_params {
+ struct fcoe_kwqe_conn_enable_disable enable_disable_kwqe;
+};
+
+
+/*
+ * FCoE connection offload params passed by driver to FW in FCoE offload ramrod
+ * $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_conn_offload_ramrod_params {
+ struct fcoe_kwqe_conn_offload1 offload_kwqe1;
+ struct fcoe_kwqe_conn_offload2 offload_kwqe2;
+ struct fcoe_kwqe_conn_offload3 offload_kwqe3;
+ struct fcoe_kwqe_conn_offload4 offload_kwqe4;
+};
+
+
+struct ustorm_fcoe_mng_ctx {
+#if defined(__BIG_ENDIAN)
+ u8 mid_seq_proc_flag;
+ u8 tce_in_cam_flag;
+ u8 tce_on_ior_flag;
+ u8 en_cached_tce_flag;
+#elif defined(__LITTLE_ENDIAN)
+ u8 en_cached_tce_flag;
+ u8 tce_on_ior_flag;
+ u8 tce_in_cam_flag;
+ u8 mid_seq_proc_flag;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 tce_cam_addr;
+ u8 cached_conn_flag;
+ u16 rsrv0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 rsrv0;
+ u8 cached_conn_flag;
+ u8 tce_cam_addr;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 dma_tce_ram_addr;
+ u16 tce_ram_addr;
+#elif defined(__LITTLE_ENDIAN)
+ u16 tce_ram_addr;
+ u16 dma_tce_ram_addr;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 ox_id;
+ u16 wr_done_seq;
+#elif defined(__LITTLE_ENDIAN)
+ u16 wr_done_seq;
+ u16 ox_id;
+#endif
+ struct regpair task_addr;
+};
+
+/*
+ * Parameters initialized during offloaded according to FLOGI/PLOGI/PRLI and
+ * used in FCoE context section
+ */
+struct ustorm_fcoe_params {
+#if defined(__BIG_ENDIAN)
+ u16 fcoe_conn_id;
+ u16 flags;
+#define USTORM_FCOE_PARAMS_B_MUL_N_PORT_IDS (0x1<<0)
+#define USTORM_FCOE_PARAMS_B_MUL_N_PORT_IDS_SHIFT 0
+#define USTORM_FCOE_PARAMS_B_E_D_TOV_RES (0x1<<1)
+#define USTORM_FCOE_PARAMS_B_E_D_TOV_RES_SHIFT 1
+#define USTORM_FCOE_PARAMS_B_CONT_INCR_SEQ_CNT (0x1<<2)
+#define USTORM_FCOE_PARAMS_B_CONT_INCR_SEQ_CNT_SHIFT 2
+#define USTORM_FCOE_PARAMS_B_CONF_REQ (0x1<<3)
+#define USTORM_FCOE_PARAMS_B_CONF_REQ_SHIFT 3
+#define USTORM_FCOE_PARAMS_B_REC_VALID (0x1<<4)
+#define USTORM_FCOE_PARAMS_B_REC_VALID_SHIFT 4
+#define USTORM_FCOE_PARAMS_B_CQ_TOGGLE_BIT (0x1<<5)
+#define USTORM_FCOE_PARAMS_B_CQ_TOGGLE_BIT_SHIFT 5
+#define USTORM_FCOE_PARAMS_B_XFRQ_TOGGLE_BIT (0x1<<6)
+#define USTORM_FCOE_PARAMS_B_XFRQ_TOGGLE_BIT_SHIFT 6
+#define USTORM_FCOE_PARAMS_RSRV0 (0x1FF<<7)
+#define USTORM_FCOE_PARAMS_RSRV0_SHIFT 7
+#elif defined(__LITTLE_ENDIAN)
+ u16 flags;
+#define USTORM_FCOE_PARAMS_B_MUL_N_PORT_IDS (0x1<<0)
+#define USTORM_FCOE_PARAMS_B_MUL_N_PORT_IDS_SHIFT 0
+#define USTORM_FCOE_PARAMS_B_E_D_TOV_RES (0x1<<1)
+#define USTORM_FCOE_PARAMS_B_E_D_TOV_RES_SHIFT 1
+#define USTORM_FCOE_PARAMS_B_CONT_INCR_SEQ_CNT (0x1<<2)
+#define USTORM_FCOE_PARAMS_B_CONT_INCR_SEQ_CNT_SHIFT 2
+#define USTORM_FCOE_PARAMS_B_CONF_REQ (0x1<<3)
+#define USTORM_FCOE_PARAMS_B_CONF_REQ_SHIFT 3
+#define USTORM_FCOE_PARAMS_B_REC_VALID (0x1<<4)
+#define USTORM_FCOE_PARAMS_B_REC_VALID_SHIFT 4
+#define USTORM_FCOE_PARAMS_B_CQ_TOGGLE_BIT (0x1<<5)
+#define USTORM_FCOE_PARAMS_B_CQ_TOGGLE_BIT_SHIFT 5
+#define USTORM_FCOE_PARAMS_B_XFRQ_TOGGLE_BIT (0x1<<6)
+#define USTORM_FCOE_PARAMS_B_XFRQ_TOGGLE_BIT_SHIFT 6
+#define USTORM_FCOE_PARAMS_RSRV0 (0x1FF<<7)
+#define USTORM_FCOE_PARAMS_RSRV0_SHIFT 7
+ u16 fcoe_conn_id;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 hc_csdm_byte_en;
+ u8 func_id;
+ u8 port_id;
+ u8 vnic_id;
+#elif defined(__LITTLE_ENDIAN)
+ u8 vnic_id;
+ u8 port_id;
+ u8 func_id;
+ u8 hc_csdm_byte_en;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 rx_total_conc_seqs;
+ u16 rx_max_fc_pay_len;
+#elif defined(__LITTLE_ENDIAN)
+ u16 rx_max_fc_pay_len;
+ u16 rx_total_conc_seqs;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 task_pbe_idx_off;
+ u8 task_in_page_log_size;
+ u16 rx_max_conc_seqs;
+#elif defined(__LITTLE_ENDIAN)
+ u16 rx_max_conc_seqs;
+ u8 task_in_page_log_size;
+ u8 task_pbe_idx_off;
+#endif
+};
+
+/*
+ * FCoE 16-bits index structure
+ */
+struct fcoe_idx16_fields {
+ u16 fields;
+#define FCOE_IDX16_FIELDS_IDX (0x7FFF<<0)
+#define FCOE_IDX16_FIELDS_IDX_SHIFT 0
+#define FCOE_IDX16_FIELDS_MSB (0x1<<15)
+#define FCOE_IDX16_FIELDS_MSB_SHIFT 15
+};
+
+/*
+ * FCoE 16-bits index union
+ */
+union fcoe_idx16_field_union {
+ struct fcoe_idx16_fields fields;
+ u16 val;
+};
+
+/*
+ * Parameters required for placement according to SGL
+ */
+struct ustorm_fcoe_data_place_mng {
+#if defined(__BIG_ENDIAN)
+ u16 sge_off;
+ u8 num_sges;
+ u8 sge_idx;
+#elif defined(__LITTLE_ENDIAN)
+ u8 sge_idx;
+ u8 num_sges;
+ u16 sge_off;
+#endif
+};
+
+/*
+ * Parameters required for placement according to SGL
+ */
+struct ustorm_fcoe_data_place {
+ struct ustorm_fcoe_data_place_mng cached_mng;
+ struct fcoe_bd_ctx cached_sge[2];
+};
+
+/*
+ * TX processing shall write and RX processing shall read from this section
+ */
+union fcoe_u_tce_tx_wr_rx_rd_union {
+ struct fcoe_abts_info abts;
+ struct fcoe_cleanup_info cleanup;
+ struct fcoe_fw_tx_seq_ctx tx_seq_ctx;
+ u32 opaque[2];
+};
+
+/*
+ * TX processing shall write and RX processing shall read from this section
+ */
+struct fcoe_u_tce_tx_wr_rx_rd {
+ union fcoe_u_tce_tx_wr_rx_rd_union union_ctx;
+ struct fcoe_tce_tx_wr_rx_rd_const const_ctx;
+};
+
+struct ustorm_fcoe_tce {
+ struct fcoe_u_tce_tx_wr_rx_rd txwr_rxrd;
+ struct fcoe_tce_rx_wr_tx_rd rxwr_txrd;
+ struct fcoe_tce_rx_only rxwr;
+};
+
+struct ustorm_fcoe_cache_ctx {
+ u32 rsrv0;
+ struct ustorm_fcoe_data_place data_place;
+ struct ustorm_fcoe_tce tce;
+};
+
+/*
+ * Ustorm FCoE Storm Context
+ */
+struct ustorm_fcoe_st_context {
+ struct ustorm_fcoe_mng_ctx mng_ctx;
+ struct ustorm_fcoe_params fcoe_params;
+ struct regpair cq_base_addr;
+ struct regpair rq_pbl_base;
+ struct regpair rq_cur_page_addr;
+ struct regpair confq_pbl_base_addr;
+ struct regpair conn_db_base;
+ struct regpair xfrq_base_addr;
+ struct regpair lcq_base_addr;
+#if defined(__BIG_ENDIAN)
+ union fcoe_idx16_field_union rq_cons;
+ union fcoe_idx16_field_union rq_prod;
+#elif defined(__LITTLE_ENDIAN)
+ union fcoe_idx16_field_union rq_prod;
+ union fcoe_idx16_field_union rq_cons;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 xfrq_prod;
u16 cq_cons;
#elif defined(__LITTLE_ENDIAN)
u16 cq_cons;
- u16 cq_prod;
+ u16 xfrq_prod;
#endif
#if defined(__BIG_ENDIAN)
- u16 __reserved2;
- u8 decision_rules;
-#define USTORM_FCOE_AG_CONTEXT_CQ_DEC_RULE (0x7<<0)
-#define USTORM_FCOE_AG_CONTEXT_CQ_DEC_RULE_SHIFT 0
-#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3)
-#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3
-#define USTORM_FCOE_AG_CONTEXT_CQ_ARM_N_FLAG (0x1<<6)
-#define USTORM_FCOE_AG_CONTEXT_CQ_ARM_N_FLAG_SHIFT 6
-#define __USTORM_FCOE_AG_CONTEXT_RESERVED1 (0x1<<7)
-#define __USTORM_FCOE_AG_CONTEXT_RESERVED1_SHIFT 7
- u8 decision_rule_enable_bits;
-#define __USTORM_FCOE_AG_CONTEXT_RESERVED_INV_CF_EN (0x1<<0)
-#define __USTORM_FCOE_AG_CONTEXT_RESERVED_INV_CF_EN_SHIFT 0
-#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1)
-#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1
-#define USTORM_FCOE_AG_CONTEXT_TX_CF_EN (0x1<<2)
-#define USTORM_FCOE_AG_CONTEXT_TX_CF_EN_SHIFT 2
-#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_EN (0x1<<3)
-#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_EN_SHIFT 3
-#define __USTORM_FCOE_AG_CONTEXT_AUX1_CF_EN (0x1<<4)
-#define __USTORM_FCOE_AG_CONTEXT_AUX1_CF_EN_SHIFT 4
-#define __USTORM_FCOE_AG_CONTEXT_QUEUE0_CF_EN (0x1<<5)
-#define __USTORM_FCOE_AG_CONTEXT_QUEUE0_CF_EN_SHIFT 5
-#define __USTORM_FCOE_AG_CONTEXT_AUX3_CF_EN (0x1<<6)
-#define __USTORM_FCOE_AG_CONTEXT_AUX3_CF_EN_SHIFT 6
-#define __USTORM_FCOE_AG_CONTEXT_DQ_CF_EN (0x1<<7)
-#define __USTORM_FCOE_AG_CONTEXT_DQ_CF_EN_SHIFT 7
+ u16 lcq_cons;
+ u16 hc_cram_address;
#elif defined(__LITTLE_ENDIAN)
- u8 decision_rule_enable_bits;
-#define __USTORM_FCOE_AG_CONTEXT_RESERVED_INV_CF_EN (0x1<<0)
-#define __USTORM_FCOE_AG_CONTEXT_RESERVED_INV_CF_EN_SHIFT 0
-#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1)
-#define USTORM_FCOE_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1
-#define USTORM_FCOE_AG_CONTEXT_TX_CF_EN (0x1<<2)
-#define USTORM_FCOE_AG_CONTEXT_TX_CF_EN_SHIFT 2
-#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_EN (0x1<<3)
-#define __USTORM_FCOE_AG_CONTEXT_TIMER_CF_EN_SHIFT 3
-#define __USTORM_FCOE_AG_CONTEXT_AUX1_CF_EN (0x1<<4)
-#define __USTORM_FCOE_AG_CONTEXT_AUX1_CF_EN_SHIFT 4
-#define __USTORM_FCOE_AG_CONTEXT_QUEUE0_CF_EN (0x1<<5)
-#define __USTORM_FCOE_AG_CONTEXT_QUEUE0_CF_EN_SHIFT 5
-#define __USTORM_FCOE_AG_CONTEXT_AUX3_CF_EN (0x1<<6)
-#define __USTORM_FCOE_AG_CONTEXT_AUX3_CF_EN_SHIFT 6
-#define __USTORM_FCOE_AG_CONTEXT_DQ_CF_EN (0x1<<7)
-#define __USTORM_FCOE_AG_CONTEXT_DQ_CF_EN_SHIFT 7
- u8 decision_rules;
-#define USTORM_FCOE_AG_CONTEXT_CQ_DEC_RULE (0x7<<0)
-#define USTORM_FCOE_AG_CONTEXT_CQ_DEC_RULE_SHIFT 0
-#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3)
-#define __USTORM_FCOE_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3
-#define USTORM_FCOE_AG_CONTEXT_CQ_ARM_N_FLAG (0x1<<6)
-#define USTORM_FCOE_AG_CONTEXT_CQ_ARM_N_FLAG_SHIFT 6
-#define __USTORM_FCOE_AG_CONTEXT_RESERVED1 (0x1<<7)
-#define __USTORM_FCOE_AG_CONTEXT_RESERVED1_SHIFT 7
- u16 __reserved2;
+ u16 hc_cram_address;
+ u16 lcq_cons;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 sq_xfrq_lcq_confq_size;
+ u16 confq_prod;
+#elif defined(__LITTLE_ENDIAN)
+ u16 confq_prod;
+ u16 sq_xfrq_lcq_confq_size;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 hc_csdm_agg_int;
+ u8 rsrv2;
+ u8 available_rqes;
+ u8 sp_q_flush_cnt;
+#elif defined(__LITTLE_ENDIAN)
+ u8 sp_q_flush_cnt;
+ u8 available_rqes;
+ u8 rsrv2;
+ u8 hc_csdm_agg_int;
#endif
+#if defined(__BIG_ENDIAN)
+ u16 num_pend_tasks;
+ u16 pbf_ack_ram_addr;
+#elif defined(__LITTLE_ENDIAN)
+ u16 pbf_ack_ram_addr;
+ u16 num_pend_tasks;
+#endif
+ struct ustorm_fcoe_cache_ctx cache_ctx;
+};
+
+/*
+ * The FCoE non-aggregative context of Tstorm
+ */
+struct tstorm_fcoe_st_context {
+ struct regpair reserved0;
+ struct regpair reserved1;
};
/*
@@ -2023,86 +3471,106 @@ struct xstorm_fcoe_context_flags {
#define XSTORM_FCOE_CONTEXT_FLAGS_B_PROC_Q_SHIFT 0
#define XSTORM_FCOE_CONTEXT_FLAGS_B_MID_SEQ (0x1<<2)
#define XSTORM_FCOE_CONTEXT_FLAGS_B_MID_SEQ_SHIFT 2
-#define XSTORM_FCOE_CONTEXT_FLAGS_B_EXCHANGE_CLEANUP_DEFFERED (0x1<<3)
-#define XSTORM_FCOE_CONTEXT_FLAGS_B_EXCHANGE_CLEANUP_DEFFERED_SHIFT 3
+#define XSTORM_FCOE_CONTEXT_FLAGS_B_BLOCK_SQ (0x1<<3)
+#define XSTORM_FCOE_CONTEXT_FLAGS_B_BLOCK_SQ_SHIFT 3
#define XSTORM_FCOE_CONTEXT_FLAGS_B_REC_SUPPORT (0x1<<4)
#define XSTORM_FCOE_CONTEXT_FLAGS_B_REC_SUPPORT_SHIFT 4
#define XSTORM_FCOE_CONTEXT_FLAGS_B_SQ_TOGGLE (0x1<<5)
#define XSTORM_FCOE_CONTEXT_FLAGS_B_SQ_TOGGLE_SHIFT 5
#define XSTORM_FCOE_CONTEXT_FLAGS_B_XFRQ_TOGGLE (0x1<<6)
#define XSTORM_FCOE_CONTEXT_FLAGS_B_XFRQ_TOGGLE_SHIFT 6
-#define XSTORM_FCOE_CONTEXT_FLAGS_B_ABTS_DEFFERED (0x1<<7)
-#define XSTORM_FCOE_CONTEXT_FLAGS_B_ABTS_DEFFERED_SHIFT 7
+#define XSTORM_FCOE_CONTEXT_FLAGS_B_VNTAG_VLAN (0x1<<7)
+#define XSTORM_FCOE_CONTEXT_FLAGS_B_VNTAG_VLAN_SHIFT 7
};
-/*
- * FCoE SQ element
- */
-struct fcoe_sqe {
- u16 wqe;
-#define FCOE_SQE_TASK_ID (0x7FFF<<0)
-#define FCOE_SQE_TASK_ID_SHIFT 0
-#define FCOE_SQE_TOGGLE_BIT (0x1<<15)
-#define FCOE_SQE_TOGGLE_BIT_SHIFT 15
-};
-
-/*
- * FCoE XFRQ element
- */
-struct fcoe_xfrqe {
- u16 wqe;
-#define FCOE_XFRQE_TASK_ID (0x7FFF<<0)
-#define FCOE_XFRQE_TASK_ID_SHIFT 0
-#define FCOE_XFRQE_TOGGLE_BIT (0x1<<15)
-#define FCOE_XFRQE_TOGGLE_BIT_SHIFT 15
+struct xstorm_fcoe_tce {
+ struct fcoe_tce_tx_only txwr;
+ struct fcoe_tce_tx_wr_rx_rd txwr_rxrd;
};
/*
- * FCoE SQ\XFRQ element
+ * FCP_DATA parameters required for transmission
*/
-struct fcoe_cached_wqe {
+struct xstorm_fcoe_fcp_data {
+ u32 io_rem;
#if defined(__BIG_ENDIAN)
- struct fcoe_xfrqe xfrqe;
- struct fcoe_sqe sqe;
+ u16 cached_sge_off;
+ u8 cached_num_sges;
+ u8 cached_sge_idx;
#elif defined(__LITTLE_ENDIAN)
- struct fcoe_sqe sqe;
- struct fcoe_xfrqe xfrqe;
+ u8 cached_sge_idx;
+ u8 cached_num_sges;
+ u16 cached_sge_off;
+#endif
+ u32 buf_addr_hi_0;
+ u32 buf_addr_lo_0;
+#if defined(__BIG_ENDIAN)
+ u16 num_of_pending_tasks;
+ u16 buf_len_0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 buf_len_0;
+ u16 num_of_pending_tasks;
+#endif
+ u32 buf_addr_hi_1;
+ u32 buf_addr_lo_1;
+#if defined(__BIG_ENDIAN)
+ u16 task_pbe_idx_off;
+ u16 buf_len_1;
+#elif defined(__LITTLE_ENDIAN)
+ u16 buf_len_1;
+ u16 task_pbe_idx_off;
+#endif
+ u32 buf_addr_hi_2;
+ u32 buf_addr_lo_2;
+#if defined(__BIG_ENDIAN)
+ u16 ox_id;
+ u16 buf_len_2;
+#elif defined(__LITTLE_ENDIAN)
+ u16 buf_len_2;
+ u16 ox_id;
#endif
};
-struct fcoe_task_ctx_entry_tx_only {
- union fcoe_sgl_ctx sgl_ctx;
+/*
+ * vlan configuration
+ */
+struct xstorm_fcoe_vlan_conf {
+ u8 vlan_conf;
+#define XSTORM_FCOE_VLAN_CONF_PRIORITY (0x7<<0)
+#define XSTORM_FCOE_VLAN_CONF_PRIORITY_SHIFT 0
+#define XSTORM_FCOE_VLAN_CONF_INNER_VLAN_FLAG (0x1<<3)
+#define XSTORM_FCOE_VLAN_CONF_INNER_VLAN_FLAG_SHIFT 3
+#define XSTORM_FCOE_VLAN_CONF_RESERVED (0xF<<4)
+#define XSTORM_FCOE_VLAN_CONF_RESERVED_SHIFT 4
};
-struct xstorm_fcoe_task_ctx_entry_rd {
- struct fcoe_task_ctx_entry_tx_only tx_wr;
- struct fcoe_task_ctx_entry_txwr_rxrd tx_wr_rx_rd;
- struct fcoe_task_ctx_entry_tx_rx_cmn cmn;
- struct fcoe_task_ctx_entry_rxwr_txrd rx_wr_tx_rd;
+/*
+ * FCoE 16-bits vlan structure
+ */
+struct fcoe_vlan_fields {
+ u16 fields;
+#define FCOE_VLAN_FIELDS_VID (0xFFF<<0)
+#define FCOE_VLAN_FIELDS_VID_SHIFT 0
+#define FCOE_VLAN_FIELDS_CLI (0x1<<12)
+#define FCOE_VLAN_FIELDS_CLI_SHIFT 12
+#define FCOE_VLAN_FIELDS_PRI (0x7<<13)
+#define FCOE_VLAN_FIELDS_PRI_SHIFT 13
};
/*
- * Cached SGEs
+ * FCoE 16-bits vlan union
*/
-struct common_fcoe_sgl {
- struct fcoe_bd_ctx sge[2];
+union fcoe_vlan_field_union {
+ struct fcoe_vlan_fields fields;
+ u16 val;
};
/*
- * FCP_DATA parameters required for transmission
+ * FCoE 16-bits vlan, vif union
*/
-struct xstorm_fcoe_fcp_data {
- u32 io_rem;
-#if defined(__BIG_ENDIAN)
- u16 cached_sge_off;
- u8 cached_num_sges;
- u8 cached_sge_idx;
-#elif defined(__LITTLE_ENDIAN)
- u8 cached_sge_idx;
- u8 cached_num_sges;
- u16 cached_sge_off;
-#endif
- struct common_fcoe_sgl cached_sgl;
+union fcoe_vlan_vif_field_union {
+ union fcoe_vlan_field_union vlan;
+ u16 vif;
};
/*
@@ -2110,18 +3578,18 @@ struct xstorm_fcoe_fcp_data {
*/
struct xstorm_fcoe_context_section {
#if defined(__BIG_ENDIAN)
- u8 vlan_flag;
+ u8 cs_ctl;
u8 s_id[3];
#elif defined(__LITTLE_ENDIAN)
u8 s_id[3];
- u8 vlan_flag;
+ u8 cs_ctl;
#endif
#if defined(__BIG_ENDIAN)
- u8 func_id;
+ u8 rctl;
u8 d_id[3];
#elif defined(__LITTLE_ENDIAN)
u8 d_id[3];
- u8 func_id;
+ u8 rctl;
#endif
#if defined(__BIG_ENDIAN)
u16 sq_xfrq_lcq_confq_size;
@@ -2133,56 +3601,84 @@ struct xstorm_fcoe_context_section {
u32 lcq_prod;
#if defined(__BIG_ENDIAN)
u8 port_id;
- u8 tx_max_conc_seqs_c3;
+ u8 func_id;
u8 seq_id;
struct xstorm_fcoe_context_flags tx_flags;
#elif defined(__LITTLE_ENDIAN)
struct xstorm_fcoe_context_flags tx_flags;
u8 seq_id;
- u8 tx_max_conc_seqs_c3;
+ u8 func_id;
u8 port_id;
#endif
#if defined(__BIG_ENDIAN)
- u16 verify_tx_seq;
+ u16 mtu;
u8 func_mode;
u8 vnic_id;
#elif defined(__LITTLE_ENDIAN)
u8 vnic_id;
u8 func_mode;
- u16 verify_tx_seq;
+ u16 mtu;
#endif
struct regpair confq_curr_page_addr;
struct fcoe_cached_wqe cached_wqe[8];
struct regpair lcq_base_addr;
- struct xstorm_fcoe_task_ctx_entry_rd tce;
+ struct xstorm_fcoe_tce tce;
struct xstorm_fcoe_fcp_data fcp_data;
#if defined(__BIG_ENDIAN)
+ u8 tx_max_conc_seqs_c3;
+ u8 vlan_flag;
+ u8 dcb_val;
+ u8 data_pb_cmd_size;
+#elif defined(__LITTLE_ENDIAN)
+ u8 data_pb_cmd_size;
+ u8 dcb_val;
+ u8 vlan_flag;
+ u8 tx_max_conc_seqs_c3;
+#endif
+#if defined(__BIG_ENDIAN)
u16 fcoe_tx_stat_params_ram_addr;
- u16 cmng_port_ram_addr;
+ u16 fcoe_tx_fc_seq_ram_addr;
#elif defined(__LITTLE_ENDIAN)
- u16 cmng_port_ram_addr;
+ u16 fcoe_tx_fc_seq_ram_addr;
u16 fcoe_tx_stat_params_ram_addr;
#endif
#if defined(__BIG_ENDIAN)
- u8 fcp_cmd_pb_cmd_size;
+ u8 fcp_cmd_line_credit;
u8 eth_hdr_size;
u16 pbf_addr;
#elif defined(__LITTLE_ENDIAN)
u16 pbf_addr;
u8 eth_hdr_size;
- u8 fcp_cmd_pb_cmd_size;
+ u8 fcp_cmd_line_credit;
#endif
#if defined(__BIG_ENDIAN)
- u8 reserved2[2];
+ union fcoe_vlan_vif_field_union multi_func_val;
+ u8 page_log_size;
+ struct xstorm_fcoe_vlan_conf orig_vlan_conf;
+#elif defined(__LITTLE_ENDIAN)
+ struct xstorm_fcoe_vlan_conf orig_vlan_conf;
+ u8 page_log_size;
+ union fcoe_vlan_vif_field_union multi_func_val;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 fcp_cmd_frame_size;
+ u16 pbf_addr_ff;
+#elif defined(__LITTLE_ENDIAN)
+ u16 pbf_addr_ff;
+ u16 fcp_cmd_frame_size;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 vlan_num;
u8 cos;
- u8 dcb_version;
+ u8 cache_xfrq_cons;
+ u8 cache_sq_cons;
#elif defined(__LITTLE_ENDIAN)
- u8 dcb_version;
+ u8 cache_sq_cons;
+ u8 cache_xfrq_cons;
u8 cos;
- u8 reserved2[2];
+ u8 vlan_num;
#endif
- u32 reserved3;
- struct regpair reserved4[2];
+ u32 verify_tx_seq;
};
/*
@@ -2207,6 +3703,181 @@ struct fcoe_context {
};
/*
+ * FCoE init params passed by driver to FW in FCoE init ramrod
+ * $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_init_ramrod_params {
+ struct fcoe_kwqe_init1 init_kwqe1;
+ struct fcoe_kwqe_init2 init_kwqe2;
+ struct fcoe_kwqe_init3 init_kwqe3;
+ struct regpair eq_pbl_base;
+ __le32 eq_pbl_size;
+ __le32 reserved2;
+ __le16 eq_prod;
+ __le16 sb_num;
+ u8 sb_id;
+ u8 reserved0;
+ __le16 reserved1;
+};
+
+/*
+ * FCoE statistics params buffer passed by driver to FW in FCoE statistics
+ * ramrod $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_stat_ramrod_params {
+ struct fcoe_kwqe_stat stat_kwqe;
+};
+
+/*
+ * CQ DB CQ producer and pending completion counter
+ */
+struct iscsi_cq_db_prod_pnd_cmpltn_cnt {
+#if defined(__BIG_ENDIAN)
+ u16 cntr;
+ u16 prod;
+#elif defined(__LITTLE_ENDIAN)
+ u16 prod;
+ u16 cntr;
+#endif
+};
+
+/*
+ * CQ DB pending completion ITT array
+ */
+struct iscsi_cq_db_prod_pnd_cmpltn_cnt_arr {
+ struct iscsi_cq_db_prod_pnd_cmpltn_cnt prod_pend_comp[8];
+};
+
+/*
+ * Cstorm CQ sequence to notify array, updated by driver
+ */
+struct iscsi_cq_db_sqn_2_notify_arr {
+ u16 sqn[8];
+};
+
+/*
+ * Cstorm iSCSI Storm Context
+ */
+struct cstorm_iscsi_st_context {
+ struct iscsi_cq_db_prod_pnd_cmpltn_cnt_arr cq_c_prod_pend_comp_ctr_arr;
+ struct iscsi_cq_db_sqn_2_notify_arr cq_c_prod_sqn_arr;
+ struct iscsi_cq_db_sqn_2_notify_arr cq_c_sqn_2_notify_arr;
+ struct regpair hq_pbl_base;
+ struct regpair hq_curr_pbe;
+ struct regpair task_pbl_base;
+ struct regpair cq_db_base;
+#if defined(__BIG_ENDIAN)
+ u16 hq_bd_itt;
+ u16 iscsi_conn_id;
+#elif defined(__LITTLE_ENDIAN)
+ u16 iscsi_conn_id;
+ u16 hq_bd_itt;
+#endif
+ u32 hq_bd_data_segment_len;
+ u32 hq_bd_buffer_offset;
+#if defined(__BIG_ENDIAN)
+ u8 rsrv;
+ u8 cq_proc_en_bit_map;
+ u8 cq_pend_comp_itt_valid_bit_map;
+ u8 hq_bd_opcode;
+#elif defined(__LITTLE_ENDIAN)
+ u8 hq_bd_opcode;
+ u8 cq_pend_comp_itt_valid_bit_map;
+ u8 cq_proc_en_bit_map;
+ u8 rsrv;
+#endif
+ u32 hq_tcp_seq;
+#if defined(__BIG_ENDIAN)
+ u16 flags;
+#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN (0x1<<0)
+#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN_SHIFT 0
+#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN (0x1<<1)
+#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN_SHIFT 1
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID (0x1<<2)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID_SHIFT 2
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG (0x1<<3)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG_SHIFT 3
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK (0x1<<4)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK_SHIFT 4
+#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV (0x7FF<<5)
+#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV_SHIFT 5
+ u16 hq_cons;
+#elif defined(__LITTLE_ENDIAN)
+ u16 hq_cons;
+ u16 flags;
+#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN (0x1<<0)
+#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN_SHIFT 0
+#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN (0x1<<1)
+#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN_SHIFT 1
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID (0x1<<2)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID_SHIFT 2
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG (0x1<<3)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG_SHIFT 3
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK (0x1<<4)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK_SHIFT 4
+#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV (0x7FF<<5)
+#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV_SHIFT 5
+#endif
+ struct regpair rsrv1;
+};
+
+
+/*
+ * SCSI read/write SQ WQE
+ */
+struct iscsi_cmd_pdu_hdr_little_endian {
+#if defined(__BIG_ENDIAN)
+ u8 opcode;
+ u8 op_attr;
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_ATTRIBUTES (0x7<<0)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_ATTRIBUTES_SHIFT 0
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_RSRV1 (0x3<<3)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_RSRV1_SHIFT 3
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_WRITE_FLAG (0x1<<5)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_WRITE_FLAG_SHIFT 5
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_READ_FLAG (0x1<<6)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_READ_FLAG_SHIFT 6
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_FINAL_FLAG (0x1<<7)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_FINAL_FLAG_SHIFT 7
+ u16 rsrv0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 rsrv0;
+ u8 op_attr;
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_ATTRIBUTES (0x7<<0)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_ATTRIBUTES_SHIFT 0
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_RSRV1 (0x3<<3)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_RSRV1_SHIFT 3
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_WRITE_FLAG (0x1<<5)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_WRITE_FLAG_SHIFT 5
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_READ_FLAG (0x1<<6)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_READ_FLAG_SHIFT 6
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_FINAL_FLAG (0x1<<7)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_FINAL_FLAG_SHIFT 7
+ u8 opcode;
+#endif
+ u32 data_fields;
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH (0xFFFFFF<<0)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH_SHIFT 0
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH (0xFF<<24)
+#define ISCSI_CMD_PDU_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH_SHIFT 24
+ struct regpair lun;
+ u32 itt;
+ u32 expected_data_transfer_length;
+ u32 cmd_sn;
+ u32 exp_stat_sn;
+ u32 scsi_command_block[4];
+};
+
+
+/*
+ * Buffer per connection, used in Tstorm
+ */
+struct iscsi_conn_buf {
+ struct regpair reserved[8];
+};
+
+
+/*
* iSCSI context region, used only in iSCSI
*/
struct ustorm_iscsi_rq_db {
@@ -2271,11 +3942,13 @@ struct ustorm_iscsi_placement_db {
u32 local_sge_1_address_hi;
u32 local_sge_1_address_lo;
#if defined(__BIG_ENDIAN)
- u16 reserved6;
+ u8 exp_padding_2b;
+ u8 nal_len_3b;
u16 local_sge_1_size;
#elif defined(__LITTLE_ENDIAN)
u16 local_sge_1_size;
- u16 reserved6;
+ u8 nal_len_3b;
+ u8 exp_padding_2b;
#endif
#if defined(__BIG_ENDIAN)
u8 sgl_size;
@@ -2300,12 +3973,8 @@ struct ustorm_iscsi_placement_db {
u32 nal;
#define USTORM_ISCSI_PLACEMENT_DB_REM_SGE_SIZE (0xFFFFFF<<0)
#define USTORM_ISCSI_PLACEMENT_DB_REM_SGE_SIZE_SHIFT 0
-#define USTORM_ISCSI_PLACEMENT_DB_EXP_PADDING_2B (0x3<<24)
-#define USTORM_ISCSI_PLACEMENT_DB_EXP_PADDING_2B_SHIFT 24
-#define USTORM_ISCSI_PLACEMENT_DB_EXP_DIGEST_3B (0x7<<26)
-#define USTORM_ISCSI_PLACEMENT_DB_EXP_DIGEST_3B_SHIFT 26
-#define USTORM_ISCSI_PLACEMENT_DB_NAL_LEN_3B (0x7<<29)
-#define USTORM_ISCSI_PLACEMENT_DB_NAL_LEN_3B_SHIFT 29
+#define USTORM_ISCSI_PLACEMENT_DB_EXP_DIGEST_3B (0xFF<<24)
+#define USTORM_ISCSI_PLACEMENT_DB_EXP_DIGEST_3B_SHIFT 24
};
/*
@@ -2509,7 +4178,13 @@ struct tstorm_tcp_st_context_section {
u16 vlan_id;
u16 lsb_mac_address;
#endif
- u32 msb_mac_address;
+#if defined(__BIG_ENDIAN)
+ u16 msb_mac_address;
+ u16 mid_mac_address;
+#elif defined(__LITTLE_ENDIAN)
+ u16 mid_mac_address;
+ u16 msb_mac_address;
+#endif
u32 rightmost_received_seq;
};
@@ -2534,13 +4209,7 @@ struct iscsi_term_vars {
* iSCSI context region, used only in iSCSI
*/
struct tstorm_iscsi_st_context_section {
-#if defined(__BIG_ENDIAN)
- u16 rem_tcp_data_len;
- u16 brb_offset;
-#elif defined(__LITTLE_ENDIAN)
- u16 brb_offset;
- u16 rem_tcp_data_len;
-#endif
+ u32 nalPayload;
u32 b2nh;
#if defined(__BIG_ENDIAN)
u16 rq_cons;
@@ -2555,8 +4224,10 @@ struct tstorm_iscsi_st_context_section {
#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_FULL_FEATURE_SHIFT 3
#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS (0x1<<4)
#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS_SHIFT 4
-#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV (0x7<<5)
-#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV_SHIFT 5
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_NALLEN (0x3<<5)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_NALLEN_SHIFT 5
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_RSRV0 (0x1<<7)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_RSRV0_SHIFT 7
u8 hdr_bytes_2_fetch;
#elif defined(__LITTLE_ENDIAN)
u8 hdr_bytes_2_fetch;
@@ -2571,18 +4242,20 @@ struct tstorm_iscsi_st_context_section {
#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_FULL_FEATURE_SHIFT 3
#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS (0x1<<4)
#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS_SHIFT 4
-#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV (0x7<<5)
-#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV_SHIFT 5
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_NALLEN (0x3<<5)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_NALLEN_SHIFT 5
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_RSRV0 (0x1<<7)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_RSRV0_SHIFT 7
u16 rq_cons;
#endif
struct regpair rq_db_phy_addr;
#if defined(__BIG_ENDIAN)
struct iscsi_term_vars term_vars;
- u8 scratchpad_idx;
+ u8 rsrv1;
u16 iscsi_conn_id;
#elif defined(__LITTLE_ENDIAN)
u16 iscsi_conn_id;
- u8 scratchpad_idx;
+ u8 rsrv1;
struct iscsi_term_vars term_vars;
#endif
u32 process_nxt;
@@ -2597,724 +4270,6 @@ struct tstorm_iscsi_st_context {
};
/*
- * The tcp aggregative context section of Xstorm
- */
-struct xstorm_tcp_tcp_ag_context_section {
-#if defined(__BIG_ENDIAN)
- u8 __tcp_agg_vars1;
- u8 __da_cnt;
- u16 mss;
-#elif defined(__LITTLE_ENDIAN)
- u16 mss;
- u8 __da_cnt;
- u8 __tcp_agg_vars1;
-#endif
- u32 snd_nxt;
- u32 tx_wnd;
- u32 snd_una;
- u32 local_adv_wnd;
-#if defined(__BIG_ENDIAN)
- u8 __agg_val8_th;
- u8 __agg_val8;
- u16 tcp_agg_vars2;
-#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG (0x1<<0)
-#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_SHIFT 0
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED (0x1<<1)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED_SHIFT 1
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE (0x1<<2)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE_SHIFT 2
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG (0x1<<3)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG_SHIFT 3
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG (0x1<<4)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG_SHIFT 4
-#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE (0x1<<5)
-#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE_SHIFT 5
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN (0x1<<6)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN_SHIFT 6
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN (0x1<<7)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN_SHIFT 7
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN (0x1<<8)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN_SHIFT 8
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<9)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 9
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF (0x3<<10)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF_SHIFT 10
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF (0x3<<12)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF_SHIFT 12
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF (0x3<<14)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_SHIFT 14
-#elif defined(__LITTLE_ENDIAN)
- u16 tcp_agg_vars2;
-#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG (0x1<<0)
-#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_SHIFT 0
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED (0x1<<1)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED_SHIFT 1
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE (0x1<<2)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE_SHIFT 2
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG (0x1<<3)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG_SHIFT 3
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG (0x1<<4)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG_SHIFT 4
-#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE (0x1<<5)
-#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE_SHIFT 5
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN (0x1<<6)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN_SHIFT 6
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN (0x1<<7)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN_SHIFT 7
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN (0x1<<8)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN_SHIFT 8
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<9)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 9
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF (0x3<<10)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF_SHIFT 10
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF (0x3<<12)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF_SHIFT 12
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF (0x3<<14)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_SHIFT 14
- u8 __agg_val8;
- u8 __agg_val8_th;
-#endif
- u32 ack_to_far_end;
- u32 rto_timer;
- u32 ka_timer;
- u32 ts_to_echo;
-#if defined(__BIG_ENDIAN)
- u16 __agg_val7_th;
- u16 __agg_val7;
-#elif defined(__LITTLE_ENDIAN)
- u16 __agg_val7;
- u16 __agg_val7_th;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 __tcp_agg_vars5;
- u8 __tcp_agg_vars4;
- u8 __tcp_agg_vars3;
- u8 __force_pure_ack_cnt;
-#elif defined(__LITTLE_ENDIAN)
- u8 __force_pure_ack_cnt;
- u8 __tcp_agg_vars3;
- u8 __tcp_agg_vars4;
- u8 __tcp_agg_vars5;
-#endif
- u32 tcp_agg_vars6;
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_CF_EN (0x1<<0)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_CF_EN_SHIFT 0
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_EN (0x1<<1)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_EN_SHIFT 1
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_EN (0x1<<2)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_EN_SHIFT 2
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN (0x1<<3)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN_SHIFT 3
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX6_FLAG (0x1<<4)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX6_FLAG_SHIFT 4
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX7_FLAG (0x1<<5)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX7_FLAG_SHIFT 5
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX5_CF (0x3<<6)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX5_CF_SHIFT 6
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF (0x3<<8)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_SHIFT 8
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF (0x3<<10)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_SHIFT 10
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF (0x3<<12)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_SHIFT 12
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF (0x3<<14)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_SHIFT 14
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX13_CF (0x3<<16)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX13_CF_SHIFT 16
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX14_CF (0x3<<18)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX14_CF_SHIFT 18
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX15_CF (0x3<<20)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX15_CF_SHIFT 20
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX16_CF (0x3<<22)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX16_CF_SHIFT 22
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX17_CF (0x3<<24)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX17_CF_SHIFT 24
-#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ECE_FLAG (0x1<<26)
-#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ECE_FLAG_SHIFT 26
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED71 (0x1<<27)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED71_SHIFT 27
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_FORCE_PURE_ACK_CNT_DIRTY (0x1<<28)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_FORCE_PURE_ACK_CNT_DIRTY_SHIFT 28
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TCP_AUTO_STOP_FLAG (0x1<<29)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TCP_AUTO_STOP_FLAG_SHIFT 29
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DO_TS_UPDATE_FLAG (0x1<<30)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DO_TS_UPDATE_FLAG_SHIFT 30
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CANCEL_RETRANSMIT_FLAG (0x1<<31)
-#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CANCEL_RETRANSMIT_FLAG_SHIFT 31
-#if defined(__BIG_ENDIAN)
- u16 __agg_misc6;
- u16 __tcp_agg_vars7;
-#elif defined(__LITTLE_ENDIAN)
- u16 __tcp_agg_vars7;
- u16 __agg_misc6;
-#endif
- u32 __agg_val10;
- u32 __agg_val10_th;
-#if defined(__BIG_ENDIAN)
- u16 __reserved3;
- u8 __reserved2;
- u8 __da_only_cnt;
-#elif defined(__LITTLE_ENDIAN)
- u8 __da_only_cnt;
- u8 __reserved2;
- u16 __reserved3;
-#endif
-};
-
-/*
- * The iscsi aggregative context of Xstorm
- */
-struct xstorm_iscsi_ag_context {
-#if defined(__BIG_ENDIAN)
- u16 agg_val1;
- u8 agg_vars1;
-#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
-#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
-#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN (0x1<<4)
-#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN_SHIFT 4
-#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN (0x1<<5)
-#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN_SHIFT 5
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG (0x1<<6)
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_SHIFT 6
-#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN (0x1<<7)
-#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN_SHIFT 7
- u8 state;
-#elif defined(__LITTLE_ENDIAN)
- u8 state;
- u8 agg_vars1;
-#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
-#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
-#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
-#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN (0x1<<4)
-#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN_SHIFT 4
-#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN (0x1<<5)
-#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN_SHIFT 5
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG (0x1<<6)
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_SHIFT 6
-#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN (0x1<<7)
-#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN_SHIFT 7
- u16 agg_val1;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 cdu_reserved;
- u8 __agg_vars4;
- u8 agg_vars3;
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2 (0x3F<<0)
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2_SHIFT 0
-#define __XSTORM_ISCSI_AG_CONTEXT_RX_TS_EN_CF (0x3<<6)
-#define __XSTORM_ISCSI_AG_CONTEXT_RX_TS_EN_CF_SHIFT 6
- u8 agg_vars2;
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF (0x3<<0)
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_SHIFT 0
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN (0x1<<2)
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN_SHIFT 2
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG (0x1<<3)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG_SHIFT 3
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG (0x1<<4)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG_SHIFT 4
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1 (0x3<<5)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1_SHIFT 5
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
-#elif defined(__LITTLE_ENDIAN)
- u8 agg_vars2;
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF (0x3<<0)
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_SHIFT 0
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN (0x1<<2)
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN_SHIFT 2
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG (0x1<<3)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG_SHIFT 3
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG (0x1<<4)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG_SHIFT 4
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1 (0x3<<5)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1_SHIFT 5
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
-#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
- u8 agg_vars3;
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2 (0x3F<<0)
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2_SHIFT 0
-#define __XSTORM_ISCSI_AG_CONTEXT_RX_TS_EN_CF (0x3<<6)
-#define __XSTORM_ISCSI_AG_CONTEXT_RX_TS_EN_CF_SHIFT 6
- u8 __agg_vars4;
- u8 cdu_reserved;
-#endif
- u32 more_to_send;
-#if defined(__BIG_ENDIAN)
- u16 agg_vars5;
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5 (0x3<<0)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5_SHIFT 0
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0 (0x3F<<2)
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0_SHIFT 2
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1 (0x3F<<8)
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1_SHIFT 8
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2 (0x3<<14)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2_SHIFT 14
- u16 sq_cons;
-#elif defined(__LITTLE_ENDIAN)
- u16 sq_cons;
- u16 agg_vars5;
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5 (0x3<<0)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5_SHIFT 0
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0 (0x3F<<2)
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0_SHIFT 2
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1 (0x3F<<8)
-#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1_SHIFT 8
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2 (0x3<<14)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2_SHIFT 14
-#endif
- struct xstorm_tcp_tcp_ag_context_section tcp;
-#if defined(__BIG_ENDIAN)
- u16 agg_vars7;
-#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE (0x7<<0)
-#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE_SHIFT 0
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG (0x1<<3)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG_SHIFT 3
-#define __XSTORM_ISCSI_AG_CONTEXT_STORMS_SYNC_CF (0x3<<4)
-#define __XSTORM_ISCSI_AG_CONTEXT_STORMS_SYNC_CF_SHIFT 4
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3 (0x3<<6)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3_SHIFT 6
-#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF (0x3<<8)
-#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_SHIFT 8
-#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK (0x1<<10)
-#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK_SHIFT 10
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN (0x1<<11)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN_SHIFT 11
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG (0x1<<12)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG_SHIFT 12
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG (0x1<<13)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG_SHIFT 13
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG (0x1<<14)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG_SHIFT 14
-#define __XSTORM_ISCSI_AG_CONTEXT_RX_WND_SCL_EN (0x1<<15)
-#define __XSTORM_ISCSI_AG_CONTEXT_RX_WND_SCL_EN_SHIFT 15
- u8 agg_val3_th;
- u8 agg_vars6;
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6 (0x7<<0)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6_SHIFT 0
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7 (0x7<<3)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7_SHIFT 3
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4 (0x3<<6)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4_SHIFT 6
-#elif defined(__LITTLE_ENDIAN)
- u8 agg_vars6;
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6 (0x7<<0)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6_SHIFT 0
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7 (0x7<<3)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7_SHIFT 3
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4 (0x3<<6)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4_SHIFT 6
- u8 agg_val3_th;
- u16 agg_vars7;
-#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE (0x7<<0)
-#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE_SHIFT 0
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG (0x1<<3)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG_SHIFT 3
-#define __XSTORM_ISCSI_AG_CONTEXT_STORMS_SYNC_CF (0x3<<4)
-#define __XSTORM_ISCSI_AG_CONTEXT_STORMS_SYNC_CF_SHIFT 4
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3 (0x3<<6)
-#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3_SHIFT 6
-#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF (0x3<<8)
-#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_SHIFT 8
-#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK (0x1<<10)
-#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK_SHIFT 10
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN (0x1<<11)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN_SHIFT 11
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG (0x1<<12)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG_SHIFT 12
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG (0x1<<13)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG_SHIFT 13
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG (0x1<<14)
-#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG_SHIFT 14
-#define __XSTORM_ISCSI_AG_CONTEXT_RX_WND_SCL_EN (0x1<<15)
-#define __XSTORM_ISCSI_AG_CONTEXT_RX_WND_SCL_EN_SHIFT 15
-#endif
-#if defined(__BIG_ENDIAN)
- u16 __agg_val11_th;
- u16 __gen_data;
-#elif defined(__LITTLE_ENDIAN)
- u16 __gen_data;
- u16 __agg_val11_th;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 __reserved1;
- u8 __agg_val6_th;
- u16 __agg_val9;
-#elif defined(__LITTLE_ENDIAN)
- u16 __agg_val9;
- u8 __agg_val6_th;
- u8 __reserved1;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 hq_prod;
- u16 hq_cons;
-#elif defined(__LITTLE_ENDIAN)
- u16 hq_cons;
- u16 hq_prod;
-#endif
- u32 agg_vars8;
-#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC2 (0xFFFFFF<<0)
-#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC2_SHIFT 0
-#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC3 (0xFF<<24)
-#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC3_SHIFT 24
-#if defined(__BIG_ENDIAN)
- u16 r2tq_prod;
- u16 sq_prod;
-#elif defined(__LITTLE_ENDIAN)
- u16 sq_prod;
- u16 r2tq_prod;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 agg_val3;
- u8 agg_val6;
- u8 agg_val5_th;
- u8 agg_val5;
-#elif defined(__LITTLE_ENDIAN)
- u8 agg_val5;
- u8 agg_val5_th;
- u8 agg_val6;
- u8 agg_val3;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 __agg_misc1;
- u16 agg_limit1;
-#elif defined(__LITTLE_ENDIAN)
- u16 agg_limit1;
- u16 __agg_misc1;
-#endif
- u32 hq_cons_tcp_seq;
- u32 exp_stat_sn;
- u32 rst_seq_num;
-};
-
-/*
- * The tcp aggregative context section of Tstorm
- */
-struct tstorm_tcp_tcp_ag_context_section {
- u32 __agg_val1;
-#if defined(__BIG_ENDIAN)
- u8 __tcp_agg_vars2;
- u8 __agg_val3;
- u16 __agg_val2;
-#elif defined(__LITTLE_ENDIAN)
- u16 __agg_val2;
- u8 __agg_val3;
- u8 __tcp_agg_vars2;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 __agg_val5;
- u8 __agg_val6;
- u8 __tcp_agg_vars3;
-#elif defined(__LITTLE_ENDIAN)
- u8 __tcp_agg_vars3;
- u8 __agg_val6;
- u16 __agg_val5;
-#endif
- u32 snd_nxt;
- u32 rtt_seq;
- u32 rtt_time;
- u32 __reserved66;
- u32 wnd_right_edge;
- u32 tcp_agg_vars1;
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_FIN_SENT_FLAG (0x1<<0)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_FIN_SENT_FLAG_SHIFT 0
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG (0x1<<1)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG_SHIFT 1
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF (0x3<<2)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_SHIFT 2
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF (0x3<<4)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_SHIFT 4
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_EN (0x1<<6)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_EN_SHIFT 6
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_EN (0x1<<7)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_EN_SHIFT 7
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN (0x1<<8)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN_SHIFT 8
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_SND_NXT_EN (0x1<<9)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_SND_NXT_EN_SHIFT 9
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<10)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 10
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_FLAG (0x1<<11)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_FLAG_SHIFT 11
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_EN (0x1<<12)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_EN_SHIFT 12
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_EN (0x1<<13)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_EN_SHIFT 13
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF (0x3<<14)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_SHIFT 14
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF (0x3<<16)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_SHIFT 16
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_BLOCKED (0x1<<18)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_BLOCKED_SHIFT 18
-#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN (0x1<<19)
-#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN_SHIFT 19
-#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_EN (0x1<<20)
-#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_EN_SHIFT 20
-#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_EN (0x1<<21)
-#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_EN_SHIFT 21
-#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED1 (0x3<<22)
-#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED1_SHIFT 22
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ (0xF<<24)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ_SHIFT 24
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ (0xF<<28)
-#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ_SHIFT 28
- u32 snd_max;
- u32 snd_una;
- u32 __reserved2;
-};
-
-/*
- * The iscsi aggregative context of Tstorm
- */
-struct tstorm_iscsi_ag_context {
-#if defined(__BIG_ENDIAN)
- u16 ulp_credit;
- u8 agg_vars1;
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
-#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF (0x3<<4)
-#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_SHIFT 4
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG (0x1<<6)
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG_SHIFT 6
-#define __TSTORM_ISCSI_AG_CONTEXT_ACK_ON_FIN_SENT_FLAG (0x1<<7)
-#define __TSTORM_ISCSI_AG_CONTEXT_ACK_ON_FIN_SENT_FLAG_SHIFT 7
- u8 state;
-#elif defined(__LITTLE_ENDIAN)
- u8 state;
- u8 agg_vars1;
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
-#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
-#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF (0x3<<4)
-#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_SHIFT 4
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG (0x1<<6)
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG_SHIFT 6
-#define __TSTORM_ISCSI_AG_CONTEXT_ACK_ON_FIN_SENT_FLAG (0x1<<7)
-#define __TSTORM_ISCSI_AG_CONTEXT_ACK_ON_FIN_SENT_FLAG_SHIFT 7
- u16 ulp_credit;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 __agg_val4;
- u16 agg_vars2;
-#define __TSTORM_ISCSI_AG_CONTEXT_MSL_TIMER_SET_FLAG (0x1<<0)
-#define __TSTORM_ISCSI_AG_CONTEXT_MSL_TIMER_SET_FLAG_SHIFT 0
-#define __TSTORM_ISCSI_AG_CONTEXT_FIN_SENT_FIRST_FLAG (0x1<<1)
-#define __TSTORM_ISCSI_AG_CONTEXT_FIN_SENT_FIRST_FLAG_SHIFT 1
-#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF (0x3<<2)
-#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_SHIFT 2
-#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF (0x3<<4)
-#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_SHIFT 4
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF (0x3<<6)
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_SHIFT 6
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF (0x3<<8)
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_SHIFT 8
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG (0x1<<10)
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG_SHIFT 10
-#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<11)
-#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 11
-#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_EN (0x1<<12)
-#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_EN_SHIFT 12
-#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_EN (0x1<<13)
-#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_EN_SHIFT 13
-#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN (0x1<<14)
-#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN_SHIFT 14
-#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN (0x1<<15)
-#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN_SHIFT 15
-#elif defined(__LITTLE_ENDIAN)
- u16 agg_vars2;
-#define __TSTORM_ISCSI_AG_CONTEXT_MSL_TIMER_SET_FLAG (0x1<<0)
-#define __TSTORM_ISCSI_AG_CONTEXT_MSL_TIMER_SET_FLAG_SHIFT 0
-#define __TSTORM_ISCSI_AG_CONTEXT_FIN_SENT_FIRST_FLAG (0x1<<1)
-#define __TSTORM_ISCSI_AG_CONTEXT_FIN_SENT_FIRST_FLAG_SHIFT 1
-#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF (0x3<<2)
-#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_SHIFT 2
-#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF (0x3<<4)
-#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_SHIFT 4
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF (0x3<<6)
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_SHIFT 6
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF (0x3<<8)
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_SHIFT 8
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG (0x1<<10)
-#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG_SHIFT 10
-#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<11)
-#define __TSTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 11
-#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_EN (0x1<<12)
-#define __TSTORM_ISCSI_AG_CONTEXT_RST_SENT_CF_EN_SHIFT 12
-#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_EN (0x1<<13)
-#define __TSTORM_ISCSI_AG_CONTEXT_WAKEUP_CALL_CF_EN_SHIFT 13
-#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN (0x1<<14)
-#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN_SHIFT 14
-#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN (0x1<<15)
-#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN_SHIFT 15
- u16 __agg_val4;
-#endif
- struct tstorm_tcp_tcp_ag_context_section tcp;
-};
-
-/*
- * The iscsi aggregative context of Ustorm
- */
-struct ustorm_iscsi_ag_context {
-#if defined(__BIG_ENDIAN)
- u8 __aux_counter_flags;
- u8 agg_vars2;
-#define USTORM_ISCSI_AG_CONTEXT_TX_CF (0x3<<0)
-#define USTORM_ISCSI_AG_CONTEXT_TX_CF_SHIFT 0
-#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF (0x3<<2)
-#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_SHIFT 2
-#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4)
-#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4
-#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7)
-#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7
- u8 agg_vars1;
-#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
-#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
-#define USTORM_ISCSI_AG_CONTEXT_INV_CF (0x3<<4)
-#define USTORM_ISCSI_AG_CONTEXT_INV_CF_SHIFT 4
-#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF (0x3<<6)
-#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_SHIFT 6
- u8 state;
-#elif defined(__LITTLE_ENDIAN)
- u8 state;
- u8 agg_vars1;
-#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
-#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
-#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
-#define USTORM_ISCSI_AG_CONTEXT_INV_CF (0x3<<4)
-#define USTORM_ISCSI_AG_CONTEXT_INV_CF_SHIFT 4
-#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF (0x3<<6)
-#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_SHIFT 6
- u8 agg_vars2;
-#define USTORM_ISCSI_AG_CONTEXT_TX_CF (0x3<<0)
-#define USTORM_ISCSI_AG_CONTEXT_TX_CF_SHIFT 0
-#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF (0x3<<2)
-#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_SHIFT 2
-#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4)
-#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4
-#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7)
-#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7
- u8 __aux_counter_flags;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 cdu_usage;
- u8 agg_misc2;
- u16 __cq_local_comp_itt_val;
-#elif defined(__LITTLE_ENDIAN)
- u16 __cq_local_comp_itt_val;
- u8 agg_misc2;
- u8 cdu_usage;
-#endif
- u32 agg_misc4;
-#if defined(__BIG_ENDIAN)
- u8 agg_val3_th;
- u8 agg_val3;
- u16 agg_misc3;
-#elif defined(__LITTLE_ENDIAN)
- u16 agg_misc3;
- u8 agg_val3;
- u8 agg_val3_th;
-#endif
- u32 agg_val1;
- u32 agg_misc4_th;
-#if defined(__BIG_ENDIAN)
- u16 agg_val2_th;
- u16 agg_val2;
-#elif defined(__LITTLE_ENDIAN)
- u16 agg_val2;
- u16 agg_val2_th;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 __reserved2;
- u8 decision_rules;
-#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE (0x7<<0)
-#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE_SHIFT 0
-#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3)
-#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3
-#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG (0x1<<6)
-#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG_SHIFT 6
-#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1 (0x1<<7)
-#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1_SHIFT 7
- u8 decision_rule_enable_bits;
-#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN (0x1<<0)
-#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN_SHIFT 0
-#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1)
-#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1
-#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN (0x1<<2)
-#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN_SHIFT 2
-#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN (0x1<<3)
-#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN_SHIFT 3
-#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN (0x1<<4)
-#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN_SHIFT 4
-#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<5)
-#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 5
-#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<6)
-#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 6
-#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
-#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
-#elif defined(__LITTLE_ENDIAN)
- u8 decision_rule_enable_bits;
-#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN (0x1<<0)
-#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN_SHIFT 0
-#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1)
-#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1
-#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN (0x1<<2)
-#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN_SHIFT 2
-#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN (0x1<<3)
-#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN_SHIFT 3
-#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN (0x1<<4)
-#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN_SHIFT 4
-#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<5)
-#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 5
-#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<6)
-#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 6
-#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
-#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
- u8 decision_rules;
-#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE (0x7<<0)
-#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE_SHIFT 0
-#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3)
-#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3
-#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG (0x1<<6)
-#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG_SHIFT 6
-#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1 (0x1<<7)
-#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1_SHIFT 7
- u16 __reserved2;
-#endif
-};
-
-/*
* Ethernet context section, shared in TOE, RDMA and ISCSI
*/
struct xstorm_eth_context_section {
@@ -3509,7 +4464,27 @@ struct xstorm_tcp_context_section {
u16 window_scaling_factor;
u16 pseudo_csum;
#endif
- u32 reserved2;
+#if defined(__BIG_ENDIAN)
+ u16 reserved2;
+ u8 statistics_counter_id;
+ u8 statistics_params;
+#define XSTORM_TCP_CONTEXT_SECTION_UPDATE_L2_STATSTICS (0x1<<0)
+#define XSTORM_TCP_CONTEXT_SECTION_UPDATE_L2_STATSTICS_SHIFT 0
+#define XSTORM_TCP_CONTEXT_SECTION_UPDATE_L4_STATSTICS (0x1<<1)
+#define XSTORM_TCP_CONTEXT_SECTION_UPDATE_L4_STATSTICS_SHIFT 1
+#define XSTORM_TCP_CONTEXT_SECTION_RESERVED (0x3F<<2)
+#define XSTORM_TCP_CONTEXT_SECTION_RESERVED_SHIFT 2
+#elif defined(__LITTLE_ENDIAN)
+ u8 statistics_params;
+#define XSTORM_TCP_CONTEXT_SECTION_UPDATE_L2_STATSTICS (0x1<<0)
+#define XSTORM_TCP_CONTEXT_SECTION_UPDATE_L2_STATSTICS_SHIFT 0
+#define XSTORM_TCP_CONTEXT_SECTION_UPDATE_L4_STATSTICS (0x1<<1)
+#define XSTORM_TCP_CONTEXT_SECTION_UPDATE_L4_STATSTICS_SHIFT 1
+#define XSTORM_TCP_CONTEXT_SECTION_RESERVED (0x3F<<2)
+#define XSTORM_TCP_CONTEXT_SECTION_RESERVED_SHIFT 2
+ u8 statistics_counter_id;
+ u16 reserved2;
+#endif
u32 ts_time_diff;
u32 __next_timer_expir;
};
@@ -3522,29 +4497,31 @@ struct xstorm_common_context_section {
union xstorm_ip_context_section_types ip_union;
struct xstorm_tcp_context_section tcp;
#if defined(__BIG_ENDIAN)
- u16 reserved;
- u8 statistics_params;
-#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS (0x1<<0)
-#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS_SHIFT 0
-#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS (0x1<<1)
-#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS_SHIFT 1
-#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID (0x1F<<2)
-#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID_SHIFT 2
-#define XSTORM_COMMON_CONTEXT_SECTION_DCB_EXISTS (0x1<<7)
-#define XSTORM_COMMON_CONTEXT_SECTION_DCB_EXISTS_SHIFT 7
+ u8 __dcb_val;
+ u8 flags;
+#define XSTORM_COMMON_CONTEXT_SECTION_PHYSQ_INITIALIZED (0x1<<0)
+#define XSTORM_COMMON_CONTEXT_SECTION_PHYSQ_INITIALIZED_SHIFT 0
+#define XSTORM_COMMON_CONTEXT_SECTION_PBF_PORT (0x7<<1)
+#define XSTORM_COMMON_CONTEXT_SECTION_PBF_PORT_SHIFT 1
+#define XSTORM_COMMON_CONTEXT_SECTION_VLAN_MODE (0x1<<4)
+#define XSTORM_COMMON_CONTEXT_SECTION_VLAN_MODE_SHIFT 4
+#define XSTORM_COMMON_CONTEXT_SECTION_ORIGINAL_PRIORITY (0x7<<5)
+#define XSTORM_COMMON_CONTEXT_SECTION_ORIGINAL_PRIORITY_SHIFT 5
+ u8 reserved;
u8 ip_version_1b;
#elif defined(__LITTLE_ENDIAN)
u8 ip_version_1b;
- u8 statistics_params;
-#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS (0x1<<0)
-#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS_SHIFT 0
-#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS (0x1<<1)
-#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS_SHIFT 1
-#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID (0x1F<<2)
-#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID_SHIFT 2
-#define XSTORM_COMMON_CONTEXT_SECTION_DCB_EXISTS (0x1<<7)
-#define XSTORM_COMMON_CONTEXT_SECTION_DCB_EXISTS_SHIFT 7
- u16 reserved;
+ u8 reserved;
+ u8 flags;
+#define XSTORM_COMMON_CONTEXT_SECTION_PHYSQ_INITIALIZED (0x1<<0)
+#define XSTORM_COMMON_CONTEXT_SECTION_PHYSQ_INITIALIZED_SHIFT 0
+#define XSTORM_COMMON_CONTEXT_SECTION_PBF_PORT (0x7<<1)
+#define XSTORM_COMMON_CONTEXT_SECTION_PBF_PORT_SHIFT 1
+#define XSTORM_COMMON_CONTEXT_SECTION_VLAN_MODE (0x1<<4)
+#define XSTORM_COMMON_CONTEXT_SECTION_VLAN_MODE_SHIFT 4
+#define XSTORM_COMMON_CONTEXT_SECTION_ORIGINAL_PRIORITY (0x7<<5)
+#define XSTORM_COMMON_CONTEXT_SECTION_ORIGINAL_PRIORITY_SHIFT 5
+ u8 __dcb_val;
#endif
};
@@ -3682,99 +4659,6 @@ struct xstorm_iscsi_st_context {
};
/*
- * CQ DB CQ producer and pending completion counter
- */
-struct iscsi_cq_db_prod_pnd_cmpltn_cnt {
-#if defined(__BIG_ENDIAN)
- u16 cntr;
- u16 prod;
-#elif defined(__LITTLE_ENDIAN)
- u16 prod;
- u16 cntr;
-#endif
-};
-
-/*
- * CQ DB pending completion ITT array
- */
-struct iscsi_cq_db_prod_pnd_cmpltn_cnt_arr {
- struct iscsi_cq_db_prod_pnd_cmpltn_cnt prod_pend_comp[8];
-};
-
-/*
- * Cstorm CQ sequence to notify array, updated by driver
- */
-struct iscsi_cq_db_sqn_2_notify_arr {
- u16 sqn[8];
-};
-
-/*
- * Cstorm iSCSI Storm Context
- */
-struct cstorm_iscsi_st_context {
- struct iscsi_cq_db_prod_pnd_cmpltn_cnt_arr cq_c_prod_pend_comp_ctr_arr;
- struct iscsi_cq_db_sqn_2_notify_arr cq_c_prod_sqn_arr;
- struct iscsi_cq_db_sqn_2_notify_arr cq_c_sqn_2_notify_arr;
- struct regpair hq_pbl_base;
- struct regpair hq_curr_pbe;
- struct regpair task_pbl_base;
- struct regpair cq_db_base;
-#if defined(__BIG_ENDIAN)
- u16 hq_bd_itt;
- u16 iscsi_conn_id;
-#elif defined(__LITTLE_ENDIAN)
- u16 iscsi_conn_id;
- u16 hq_bd_itt;
-#endif
- u32 hq_bd_data_segment_len;
- u32 hq_bd_buffer_offset;
-#if defined(__BIG_ENDIAN)
- u8 timer_entry_idx;
- u8 cq_proc_en_bit_map;
- u8 cq_pend_comp_itt_valid_bit_map;
- u8 hq_bd_opcode;
-#elif defined(__LITTLE_ENDIAN)
- u8 hq_bd_opcode;
- u8 cq_pend_comp_itt_valid_bit_map;
- u8 cq_proc_en_bit_map;
- u8 timer_entry_idx;
-#endif
- u32 hq_tcp_seq;
-#if defined(__BIG_ENDIAN)
- u16 flags;
-#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN (0x1<<0)
-#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN_SHIFT 0
-#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN (0x1<<1)
-#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN_SHIFT 1
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID (0x1<<2)
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID_SHIFT 2
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG (0x1<<3)
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG_SHIFT 3
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK (0x1<<4)
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK_SHIFT 4
-#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV (0x7FF<<5)
-#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV_SHIFT 5
- u16 hq_cons;
-#elif defined(__LITTLE_ENDIAN)
- u16 hq_cons;
- u16 flags;
-#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN (0x1<<0)
-#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN_SHIFT 0
-#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN (0x1<<1)
-#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN_SHIFT 1
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID (0x1<<2)
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID_SHIFT 2
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG (0x1<<3)
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG_SHIFT 3
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK (0x1<<4)
-#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK_SHIFT 4
-#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV (0x7FF<<5)
-#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV_SHIFT 5
-#endif
- struct regpair rsrv1;
-};
-
-/*
* Iscsi connection context
*/
struct iscsi_context {
@@ -3791,583 +4675,388 @@ struct iscsi_context {
struct cstorm_iscsi_st_context cstorm_st_context;
};
-/*
- * FCoE KCQ CQE parameters
- */
-union fcoe_kcqe_params {
- u32 reserved0[4];
-};
/*
- * FCoE KCQ CQE
+ * PDU header of an iSCSI DATA-OUT
*/
-struct fcoe_kcqe {
- u32 fcoe_conn_id;
- u32 completion_status;
- u32 fcoe_conn_context_id;
- union fcoe_kcqe_params params;
+struct iscsi_data_pdu_hdr_little_endian {
#if defined(__BIG_ENDIAN)
- u8 flags;
-#define FCOE_KCQE_RESERVED0 (0x7<<0)
-#define FCOE_KCQE_RESERVED0_SHIFT 0
-#define FCOE_KCQE_RAMROD_COMPLETION (0x1<<3)
-#define FCOE_KCQE_RAMROD_COMPLETION_SHIFT 3
-#define FCOE_KCQE_LAYER_CODE (0x7<<4)
-#define FCOE_KCQE_LAYER_CODE_SHIFT 4
-#define FCOE_KCQE_LINKED_WITH_NEXT (0x1<<7)
-#define FCOE_KCQE_LINKED_WITH_NEXT_SHIFT 7
- u8 op_code;
- u16 qe_self_seq;
+ u8 opcode;
+ u8 op_attr;
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_RSRV1 (0x7F<<0)
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_RSRV1_SHIFT 0
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_FINAL_FLAG (0x1<<7)
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_FINAL_FLAG_SHIFT 7
+ u16 rsrv0;
#elif defined(__LITTLE_ENDIAN)
- u16 qe_self_seq;
- u8 op_code;
- u8 flags;
-#define FCOE_KCQE_RESERVED0 (0x7<<0)
-#define FCOE_KCQE_RESERVED0_SHIFT 0
-#define FCOE_KCQE_RAMROD_COMPLETION (0x1<<3)
-#define FCOE_KCQE_RAMROD_COMPLETION_SHIFT 3
-#define FCOE_KCQE_LAYER_CODE (0x7<<4)
-#define FCOE_KCQE_LAYER_CODE_SHIFT 4
-#define FCOE_KCQE_LINKED_WITH_NEXT (0x1<<7)
-#define FCOE_KCQE_LINKED_WITH_NEXT_SHIFT 7
+ u16 rsrv0;
+ u8 op_attr;
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_RSRV1 (0x7F<<0)
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_RSRV1_SHIFT 0
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_FINAL_FLAG (0x1<<7)
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_FINAL_FLAG_SHIFT 7
+ u8 opcode;
#endif
+ u32 data_fields;
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH (0xFFFFFF<<0)
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH_SHIFT 0
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH (0xFF<<24)
+#define ISCSI_DATA_PDU_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH_SHIFT 24
+ struct regpair lun;
+ u32 itt;
+ u32 ttt;
+ u32 rsrv2;
+ u32 exp_stat_sn;
+ u32 rsrv3;
+ u32 data_sn;
+ u32 buffer_offset;
+ u32 rsrv4;
};
-/*
- * FCoE KWQE header
- */
-struct fcoe_kwqe_header {
-#if defined(__BIG_ENDIAN)
- u8 flags;
-#define FCOE_KWQE_HEADER_RESERVED0 (0xF<<0)
-#define FCOE_KWQE_HEADER_RESERVED0_SHIFT 0
-#define FCOE_KWQE_HEADER_LAYER_CODE (0x7<<4)
-#define FCOE_KWQE_HEADER_LAYER_CODE_SHIFT 4
-#define FCOE_KWQE_HEADER_RESERVED1 (0x1<<7)
-#define FCOE_KWQE_HEADER_RESERVED1_SHIFT 7
- u8 op_code;
-#elif defined(__LITTLE_ENDIAN)
- u8 op_code;
- u8 flags;
-#define FCOE_KWQE_HEADER_RESERVED0 (0xF<<0)
-#define FCOE_KWQE_HEADER_RESERVED0_SHIFT 0
-#define FCOE_KWQE_HEADER_LAYER_CODE (0x7<<4)
-#define FCOE_KWQE_HEADER_LAYER_CODE_SHIFT 4
-#define FCOE_KWQE_HEADER_RESERVED1 (0x1<<7)
-#define FCOE_KWQE_HEADER_RESERVED1_SHIFT 7
-#endif
-};
/*
- * FCoE firmware init request 1
+ * PDU header of an iSCSI login request
*/
-struct fcoe_kwqe_init1 {
+struct iscsi_login_req_hdr_little_endian {
#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 num_tasks;
-#elif defined(__LITTLE_ENDIAN)
- u16 num_tasks;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 task_list_pbl_addr_lo;
- u32 task_list_pbl_addr_hi;
- u32 dummy_buffer_addr_lo;
- u32 dummy_buffer_addr_hi;
-#if defined(__BIG_ENDIAN)
- u16 rq_num_wqes;
- u16 sq_num_wqes;
-#elif defined(__LITTLE_ENDIAN)
- u16 sq_num_wqes;
- u16 rq_num_wqes;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 cq_num_wqes;
- u16 rq_buffer_log_size;
-#elif defined(__LITTLE_ENDIAN)
- u16 rq_buffer_log_size;
- u16 cq_num_wqes;
+ u8 opcode;
+ u8 op_attr;
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_NSG (0x3<<0)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_NSG_SHIFT 0
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_CSG (0x3<<2)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_CSG_SHIFT 2
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_RSRV0 (0x3<<4)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_RSRV0_SHIFT 4
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_CONTINUE_FLG (0x1<<6)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_CONTINUE_FLG_SHIFT 6
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_TRANSIT (0x1<<7)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_TRANSIT_SHIFT 7
+ u8 version_max;
+ u8 version_min;
+#elif defined(__LITTLE_ENDIAN)
+ u8 version_min;
+ u8 version_max;
+ u8 op_attr;
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_NSG (0x3<<0)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_NSG_SHIFT 0
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_CSG (0x3<<2)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_CSG_SHIFT 2
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_RSRV0 (0x3<<4)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_RSRV0_SHIFT 4
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_CONTINUE_FLG (0x1<<6)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_CONTINUE_FLG_SHIFT 6
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_TRANSIT (0x1<<7)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_TRANSIT_SHIFT 7
+ u8 opcode;
#endif
+ u32 data_fields;
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH (0xFFFFFF<<0)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH_SHIFT 0
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH (0xFF<<24)
+#define ISCSI_LOGIN_REQ_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH_SHIFT 24
+ u32 isid_lo;
#if defined(__BIG_ENDIAN)
- u8 flags;
-#define FCOE_KWQE_INIT1_LOG_PAGE_SIZE (0xF<<0)
-#define FCOE_KWQE_INIT1_LOG_PAGE_SIZE_SHIFT 0
-#define FCOE_KWQE_INIT1_LOG_CACHED_PBES_PER_FUNC (0x7<<4)
-#define FCOE_KWQE_INIT1_LOG_CACHED_PBES_PER_FUNC_SHIFT 4
-#define FCOE_KWQE_INIT1_RESERVED1 (0x1<<7)
-#define FCOE_KWQE_INIT1_RESERVED1_SHIFT 7
- u8 num_sessions_log;
- u16 mtu;
+ u16 isid_hi;
+ u16 tsih;
#elif defined(__LITTLE_ENDIAN)
- u16 mtu;
- u8 num_sessions_log;
- u8 flags;
-#define FCOE_KWQE_INIT1_LOG_PAGE_SIZE (0xF<<0)
-#define FCOE_KWQE_INIT1_LOG_PAGE_SIZE_SHIFT 0
-#define FCOE_KWQE_INIT1_LOG_CACHED_PBES_PER_FUNC (0x7<<4)
-#define FCOE_KWQE_INIT1_LOG_CACHED_PBES_PER_FUNC_SHIFT 4
-#define FCOE_KWQE_INIT1_RESERVED1 (0x1<<7)
-#define FCOE_KWQE_INIT1_RESERVED1_SHIFT 7
+ u16 tsih;
+ u16 isid_hi;
#endif
-};
-
-/*
- * FCoE firmware init request 2
- */
-struct fcoe_kwqe_init2 {
+ u32 itt;
#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 reserved0;
+ u16 cid;
+ u16 rsrv1;
#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
- struct fcoe_kwqe_header hdr;
+ u16 rsrv1;
+ u16 cid;
#endif
- u32 hash_tbl_pbl_addr_lo;
- u32 hash_tbl_pbl_addr_hi;
- u32 t2_hash_tbl_addr_lo;
- u32 t2_hash_tbl_addr_hi;
- u32 t2_ptr_hash_tbl_addr_lo;
- u32 t2_ptr_hash_tbl_addr_hi;
- u32 free_list_count;
+ u32 cmd_sn;
+ u32 exp_stat_sn;
+ u32 rsrv2[4];
};
/*
- * FCoE firmware init request 3
+ * PDU header of an iSCSI logout request
*/
-struct fcoe_kwqe_init3 {
+struct iscsi_logout_req_hdr_little_endian {
#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 reserved0;
+ u8 opcode;
+ u8 op_attr;
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_REASON_CODE (0x7F<<0)
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_REASON_CODE_SHIFT 0
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_RSRV1_1 (0x1<<7)
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_RSRV1_1_SHIFT 7
+ u16 rsrv0;
#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
- struct fcoe_kwqe_header hdr;
+ u16 rsrv0;
+ u8 op_attr;
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_REASON_CODE (0x7F<<0)
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_REASON_CODE_SHIFT 0
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_RSRV1_1 (0x1<<7)
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_RSRV1_1_SHIFT 7
+ u8 opcode;
#endif
- u32 error_bit_map_lo;
- u32 error_bit_map_hi;
+ u32 data_fields;
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH (0xFFFFFF<<0)
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH_SHIFT 0
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH (0xFF<<24)
+#define ISCSI_LOGOUT_REQ_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH_SHIFT 24
+ u32 rsrv2[2];
+ u32 itt;
#if defined(__BIG_ENDIAN)
- u8 reserved21[3];
- u8 cached_session_enable;
+ u16 cid;
+ u16 rsrv1;
#elif defined(__LITTLE_ENDIAN)
- u8 cached_session_enable;
- u8 reserved21[3];
+ u16 rsrv1;
+ u16 cid;
#endif
- u32 reserved2[4];
+ u32 cmd_sn;
+ u32 exp_stat_sn;
+ u32 rsrv3[4];
};
/*
- * FCoE connection offload request 1
+ * PDU header of an iSCSI TMF request
*/
-struct fcoe_kwqe_conn_offload1 {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 fcoe_conn_id;
-#elif defined(__LITTLE_ENDIAN)
- u16 fcoe_conn_id;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 sq_addr_lo;
- u32 sq_addr_hi;
- u32 rq_pbl_addr_lo;
- u32 rq_pbl_addr_hi;
- u32 rq_first_pbe_addr_lo;
- u32 rq_first_pbe_addr_hi;
+struct iscsi_tmf_req_hdr_little_endian {
#if defined(__BIG_ENDIAN)
- u16 reserved0;
- u16 rq_prod;
+ u8 opcode;
+ u8 op_attr;
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_FUNCTION (0x7F<<0)
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_FUNCTION_SHIFT 0
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_RSRV1_1 (0x1<<7)
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_RSRV1_1_SHIFT 7
+ u16 rsrv0;
#elif defined(__LITTLE_ENDIAN)
- u16 rq_prod;
- u16 reserved0;
+ u16 rsrv0;
+ u8 op_attr;
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_FUNCTION (0x7F<<0)
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_FUNCTION_SHIFT 0
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_RSRV1_1 (0x1<<7)
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_RSRV1_1_SHIFT 7
+ u8 opcode;
#endif
+ u32 data_fields;
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH (0xFFFFFF<<0)
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH_SHIFT 0
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH (0xFF<<24)
+#define ISCSI_TMF_REQ_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH_SHIFT 24
+ struct regpair lun;
+ u32 itt;
+ u32 referenced_task_tag;
+ u32 cmd_sn;
+ u32 exp_stat_sn;
+ u32 ref_cmd_sn;
+ u32 exp_data_sn;
+ u32 rsrv2[2];
};
/*
- * FCoE connection offload request 2
+ * PDU header of an iSCSI Text request
*/
-struct fcoe_kwqe_conn_offload2 {
+struct iscsi_text_req_hdr_little_endian {
#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 tx_max_fc_pay_len;
+ u8 opcode;
+ u8 op_attr;
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_RSRV1 (0x3F<<0)
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_RSRV1_SHIFT 0
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_CONTINUE_FLG (0x1<<6)
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_CONTINUE_FLG_SHIFT 6
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_FINAL (0x1<<7)
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_FINAL_SHIFT 7
+ u16 rsrv0;
#elif defined(__LITTLE_ENDIAN)
- u16 tx_max_fc_pay_len;
- struct fcoe_kwqe_header hdr;
+ u16 rsrv0;
+ u8 op_attr;
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_RSRV1 (0x3F<<0)
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_RSRV1_SHIFT 0
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_CONTINUE_FLG (0x1<<6)
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_CONTINUE_FLG_SHIFT 6
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_FINAL (0x1<<7)
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_FINAL_SHIFT 7
+ u8 opcode;
#endif
- u32 cq_addr_lo;
- u32 cq_addr_hi;
- u32 xferq_addr_lo;
- u32 xferq_addr_hi;
- u32 conn_db_addr_lo;
- u32 conn_db_addr_hi;
- u32 reserved1;
+ u32 data_fields;
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH (0xFFFFFF<<0)
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH_SHIFT 0
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH (0xFF<<24)
+#define ISCSI_TEXT_REQ_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH_SHIFT 24
+ struct regpair lun;
+ u32 itt;
+ u32 ttt;
+ u32 cmd_sn;
+ u32 exp_stat_sn;
+ u32 rsrv3[4];
};
/*
- * FCoE connection offload request 3
+ * PDU header of an iSCSI Nop-Out
*/
-struct fcoe_kwqe_conn_offload3 {
+struct iscsi_nop_out_hdr_little_endian {
#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 vlan_tag;
-#define FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID (0xFFF<<0)
-#define FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID_SHIFT 0
-#define FCOE_KWQE_CONN_OFFLOAD3_CFI (0x1<<12)
-#define FCOE_KWQE_CONN_OFFLOAD3_CFI_SHIFT 12
-#define FCOE_KWQE_CONN_OFFLOAD3_PRIORITY (0x7<<13)
-#define FCOE_KWQE_CONN_OFFLOAD3_PRIORITY_SHIFT 13
-#elif defined(__LITTLE_ENDIAN)
- u16 vlan_tag;
-#define FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID (0xFFF<<0)
-#define FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID_SHIFT 0
-#define FCOE_KWQE_CONN_OFFLOAD3_CFI (0x1<<12)
-#define FCOE_KWQE_CONN_OFFLOAD3_CFI_SHIFT 12
-#define FCOE_KWQE_CONN_OFFLOAD3_PRIORITY (0x7<<13)
-#define FCOE_KWQE_CONN_OFFLOAD3_PRIORITY_SHIFT 13
- struct fcoe_kwqe_header hdr;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 tx_max_conc_seqs_c3;
- u8 s_id[3];
-#elif defined(__LITTLE_ENDIAN)
- u8 s_id[3];
- u8 tx_max_conc_seqs_c3;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 flags;
-#define FCOE_KWQE_CONN_OFFLOAD3_B_MUL_N_PORT_IDS (0x1<<0)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_MUL_N_PORT_IDS_SHIFT 0
-#define FCOE_KWQE_CONN_OFFLOAD3_B_E_D_TOV_RES (0x1<<1)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_E_D_TOV_RES_SHIFT 1
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT (0x1<<2)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT_SHIFT 2
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ (0x1<<3)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ_SHIFT 3
-#define FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID (0x1<<4)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID_SHIFT 4
-#define FCOE_KWQE_CONN_OFFLOAD3_B_C2_VALID (0x1<<5)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_C2_VALID_SHIFT 5
-#define FCOE_KWQE_CONN_OFFLOAD3_B_ACK_0 (0x1<<6)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_ACK_0_SHIFT 6
-#define FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG (0x1<<7)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG_SHIFT 7
- u8 d_id[3];
-#elif defined(__LITTLE_ENDIAN)
- u8 d_id[3];
- u8 flags;
-#define FCOE_KWQE_CONN_OFFLOAD3_B_MUL_N_PORT_IDS (0x1<<0)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_MUL_N_PORT_IDS_SHIFT 0
-#define FCOE_KWQE_CONN_OFFLOAD3_B_E_D_TOV_RES (0x1<<1)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_E_D_TOV_RES_SHIFT 1
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT (0x1<<2)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT_SHIFT 2
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ (0x1<<3)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ_SHIFT 3
-#define FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID (0x1<<4)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID_SHIFT 4
-#define FCOE_KWQE_CONN_OFFLOAD3_B_C2_VALID (0x1<<5)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_C2_VALID_SHIFT 5
-#define FCOE_KWQE_CONN_OFFLOAD3_B_ACK_0 (0x1<<6)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_ACK_0_SHIFT 6
-#define FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG (0x1<<7)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG_SHIFT 7
-#endif
- u32 reserved;
- u32 confq_first_pbe_addr_lo;
- u32 confq_first_pbe_addr_hi;
-#if defined(__BIG_ENDIAN)
- u16 rx_max_fc_pay_len;
- u16 tx_total_conc_seqs;
-#elif defined(__LITTLE_ENDIAN)
- u16 tx_total_conc_seqs;
- u16 rx_max_fc_pay_len;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 rx_open_seqs_exch_c3;
- u8 rx_max_conc_seqs_c3;
- u16 rx_total_conc_seqs;
+ u8 opcode;
+ u8 op_attr;
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_RSRV1 (0x7F<<0)
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_RSRV1_SHIFT 0
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_RSRV2_1 (0x1<<7)
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_RSRV2_1_SHIFT 7
+ u16 rsrv0;
#elif defined(__LITTLE_ENDIAN)
- u16 rx_total_conc_seqs;
- u8 rx_max_conc_seqs_c3;
- u8 rx_open_seqs_exch_c3;
+ u16 rsrv0;
+ u8 op_attr;
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_RSRV1 (0x7F<<0)
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_RSRV1_SHIFT 0
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_RSRV2_1 (0x1<<7)
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_RSRV2_1_SHIFT 7
+ u8 opcode;
#endif
+ u32 data_fields;
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH (0xFFFFFF<<0)
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_DATA_SEGMENT_LENGTH_SHIFT 0
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH (0xFF<<24)
+#define ISCSI_NOP_OUT_HDR_LITTLE_ENDIAN_TOTAL_AHS_LENGTH_SHIFT 24
+ struct regpair lun;
+ u32 itt;
+ u32 ttt;
+ u32 cmd_sn;
+ u32 exp_stat_sn;
+ u32 rsrv3[4];
};
/*
- * FCoE connection offload request 4
+ * iscsi pdu headers in little endian form.
*/
-struct fcoe_kwqe_conn_offload4 {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u8 reserved2;
- u8 e_d_tov_timer_val;
-#elif defined(__LITTLE_ENDIAN)
- u8 e_d_tov_timer_val;
- u8 reserved2;
- struct fcoe_kwqe_header hdr;
-#endif
- u8 src_mac_addr_lo32[4];
-#if defined(__BIG_ENDIAN)
- u8 dst_mac_addr_hi16[2];
- u8 src_mac_addr_hi16[2];
-#elif defined(__LITTLE_ENDIAN)
- u8 src_mac_addr_hi16[2];
- u8 dst_mac_addr_hi16[2];
-#endif
- u8 dst_mac_addr_lo32[4];
- u32 lcq_addr_lo;
- u32 lcq_addr_hi;
- u32 confq_pbl_base_addr_lo;
- u32 confq_pbl_base_addr_hi;
+union iscsi_pdu_headers_little_endian {
+ u32 fullHeaderSize[12];
+ struct iscsi_cmd_pdu_hdr_little_endian command_pdu_hdr;
+ struct iscsi_data_pdu_hdr_little_endian data_out_pdu_hdr;
+ struct iscsi_login_req_hdr_little_endian login_req_pdu_hdr;
+ struct iscsi_logout_req_hdr_little_endian logout_req_pdu_hdr;
+ struct iscsi_tmf_req_hdr_little_endian tmf_req_pdu_hdr;
+ struct iscsi_text_req_hdr_little_endian text_req_pdu_hdr;
+ struct iscsi_nop_out_hdr_little_endian nop_out_pdu_hdr;
};
-/*
- * FCoE connection enable request
- */
-struct fcoe_kwqe_conn_enable_disable {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 reserved0;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
- struct fcoe_kwqe_header hdr;
-#endif
- u8 src_mac_addr_lo32[4];
-#if defined(__BIG_ENDIAN)
- u16 vlan_tag;
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID (0xFFF<<0)
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT 0
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_CFI (0x1<<12)
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_CFI_SHIFT 12
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY (0x7<<13)
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT 13
- u8 src_mac_addr_hi16[2];
-#elif defined(__LITTLE_ENDIAN)
- u8 src_mac_addr_hi16[2];
- u16 vlan_tag;
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID (0xFFF<<0)
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT 0
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_CFI (0x1<<12)
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_CFI_SHIFT 12
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY (0x7<<13)
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT 13
-#endif
- u8 dst_mac_addr_lo32[4];
+struct iscsi_hq_bd {
+ union iscsi_pdu_headers_little_endian pdu_header;
#if defined(__BIG_ENDIAN)
u16 reserved1;
- u8 dst_mac_addr_hi16[2];
+ u16 lcl_cmp_flg;
#elif defined(__LITTLE_ENDIAN)
- u8 dst_mac_addr_hi16[2];
+ u16 lcl_cmp_flg;
u16 reserved1;
#endif
+ u32 sgl_base_lo;
+ u32 sgl_base_hi;
#if defined(__BIG_ENDIAN)
- u8 vlan_flag;
- u8 s_id[3];
-#elif defined(__LITTLE_ENDIAN)
- u8 s_id[3];
- u8 vlan_flag;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 reserved3;
- u8 d_id[3];
+ u8 sgl_size;
+ u8 sge_index;
+ u16 sge_offset;
#elif defined(__LITTLE_ENDIAN)
- u8 d_id[3];
- u8 reserved3;
+ u16 sge_offset;
+ u8 sge_index;
+ u8 sgl_size;
#endif
- u32 context_id;
- u32 conn_id;
- u32 reserved4;
};
-/*
- * FCoE connection destroy request
- */
-struct fcoe_kwqe_conn_destroy {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 reserved0;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 context_id;
- u32 conn_id;
- u32 reserved1[5];
-};
/*
- * FCoe destroy request
+ * CQE data for L2 OOO connection $$KEEP_ENDIANNESS$$
*/
-struct fcoe_kwqe_destroy {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 reserved0;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 reserved1[7];
+struct iscsi_l2_ooo_data {
+ __le32 iscsi_cid;
+ u8 drop_isle;
+ u8 drop_size;
+ u8 ooo_opcode;
+ u8 ooo_isle;
+ u8 reserved[8];
};
-/*
- * FCoe statistics request
- */
-struct fcoe_kwqe_stat {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 reserved0;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 stat_params_addr_lo;
- u32 stat_params_addr_hi;
- u32 reserved1[5];
-};
-/*
- * FCoE KWQ WQE
- */
-union fcoe_kwqe {
- struct fcoe_kwqe_init1 init1;
- struct fcoe_kwqe_init2 init2;
- struct fcoe_kwqe_init3 init3;
- struct fcoe_kwqe_conn_offload1 conn_offload1;
- struct fcoe_kwqe_conn_offload2 conn_offload2;
- struct fcoe_kwqe_conn_offload3 conn_offload3;
- struct fcoe_kwqe_conn_offload4 conn_offload4;
- struct fcoe_kwqe_conn_enable_disable conn_enable_disable;
- struct fcoe_kwqe_conn_destroy conn_destroy;
- struct fcoe_kwqe_destroy destroy;
- struct fcoe_kwqe_stat statistics;
-};
-struct fcoe_task_ctx_entry {
- struct fcoe_task_ctx_entry_tx_only tx_wr_only;
- struct fcoe_task_ctx_entry_txwr_rxrd tx_wr_rx_rd;
- struct fcoe_task_ctx_entry_tx_rx_cmn cmn;
- struct fcoe_task_ctx_entry_rxwr_txrd rx_wr_tx_rd;
- struct fcoe_task_ctx_entry_rx_only rx_wr_only;
- u32 reserved[4];
-};
-/*
- * FCoE connection enable\disable params passed by driver to FW in FCoE enable ramrod
- */
-struct fcoe_conn_enable_disable_ramrod_params {
- struct fcoe_kwqe_conn_enable_disable enable_disable_kwqe;
-};
-/*
- * FCoE connection offload params passed by driver to FW in FCoE offload ramrod
- */
-struct fcoe_conn_offload_ramrod_params {
- struct fcoe_kwqe_conn_offload1 offload_kwqe1;
- struct fcoe_kwqe_conn_offload2 offload_kwqe2;
- struct fcoe_kwqe_conn_offload3 offload_kwqe3;
- struct fcoe_kwqe_conn_offload4 offload_kwqe4;
+struct iscsi_task_context_entry_xuc_c_write_only {
+ u32 total_data_acked;
};
-/*
- * FCoE init params passed by driver to FW in FCoE init ramrod
- */
-struct fcoe_init_ramrod_params {
- struct fcoe_kwqe_init1 init_kwqe1;
- struct fcoe_kwqe_init2 init_kwqe2;
- struct fcoe_kwqe_init3 init_kwqe3;
- struct regpair eq_addr;
- struct regpair eq_next_page_addr;
+struct iscsi_task_context_r2t_table_entry {
+ u32 ttt;
+ u32 desired_data_len;
+};
+
+struct iscsi_task_context_entry_xuc_u_write_only {
+ u32 exp_r2t_sn;
+ struct iscsi_task_context_r2t_table_entry r2t_table[4];
#if defined(__BIG_ENDIAN)
- u16 sb_num;
- u16 eq_prod;
+ u16 data_in_count;
+ u8 cq_id;
+ u8 valid_1b;
#elif defined(__LITTLE_ENDIAN)
- u16 eq_prod;
- u16 sb_num;
+ u8 valid_1b;
+ u8 cq_id;
+ u16 data_in_count;
#endif
+};
+
+struct iscsi_task_context_entry_xuc {
+ struct iscsi_task_context_entry_xuc_c_write_only write_c;
+ u32 exp_data_transfer_len;
+ struct iscsi_task_context_entry_xuc_x_write_only write_x;
+ u32 lun_lo;
+ struct iscsi_task_context_entry_xuc_xu_write_both write_xu;
+ u32 lun_hi;
+ struct iscsi_task_context_entry_xuc_u_write_only write_u;
+};
+
+struct iscsi_task_context_entry_u {
+ u32 exp_r2t_buff_offset;
+ u32 rem_rcv_len;
+ u32 exp_data_sn;
+};
+
+struct iscsi_task_context_entry {
+ struct iscsi_task_context_entry_x tce_x;
#if defined(__BIG_ENDIAN)
- u16 reserved1;
- u8 reserved0;
- u8 sb_id;
+ u16 data_out_count;
+ u16 rsrv0;
#elif defined(__LITTLE_ENDIAN)
- u8 sb_id;
- u8 reserved0;
- u16 reserved1;
+ u16 rsrv0;
+ u16 data_out_count;
#endif
+ struct iscsi_task_context_entry_xuc tce_xuc;
+ struct iscsi_task_context_entry_u tce_u;
+ u32 rsrv1[7];
};
-/*
- * FCoE statistics params buffer passed by driver to FW in FCoE statistics ramrod
- */
-struct fcoe_stat_ramrod_params {
- struct fcoe_kwqe_stat stat_kwqe;
-};
-/*
- * FCoE 16-bits vlan structure
- */
-struct fcoe_vlan_fields {
- u16 fields;
-#define FCOE_VLAN_FIELDS_VID (0xFFF<<0)
-#define FCOE_VLAN_FIELDS_VID_SHIFT 0
-#define FCOE_VLAN_FIELDS_CLI (0x1<<12)
-#define FCOE_VLAN_FIELDS_CLI_SHIFT 12
-#define FCOE_VLAN_FIELDS_PRI (0x7<<13)
-#define FCOE_VLAN_FIELDS_PRI_SHIFT 13
-};
-/*
- * FCoE 16-bits vlan union
- */
-union fcoe_vlan_field_union {
- struct fcoe_vlan_fields fields;
- u16 val;
-};
-/*
- * Parameters used for Class 2 verifications
- */
-struct ustorm_fcoe_c2_params {
-#if defined(__BIG_ENDIAN)
- u16 e2e_credit;
- u16 con_seq;
-#elif defined(__LITTLE_ENDIAN)
- u16 con_seq;
- u16 e2e_credit;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 ackq_prod;
- u16 open_seq_per_exch;
-#elif defined(__LITTLE_ENDIAN)
- u16 open_seq_per_exch;
- u16 ackq_prod;
-#endif
- struct regpair ackq_pbl_base;
- struct regpair ackq_cur_seg;
-};
-/*
- * Parameters used for Class 2 verifications
- */
-struct xstorm_fcoe_c2_params {
-#if defined(__BIG_ENDIAN)
- u16 reserved0;
- u8 ackq_x_prod;
- u8 max_conc_seqs_c2;
-#elif defined(__LITTLE_ENDIAN)
- u8 max_conc_seqs_c2;
- u8 ackq_x_prod;
- u16 reserved0;
-#endif
- struct regpair ackq_pbl_base;
- struct regpair ackq_cur_seg;
+struct iscsi_task_context_entry_xuc_x_init_only {
+ struct regpair lun;
+ u32 exp_data_transfer_len;
};
-/*
- * Buffer per connection, used in Tstorm
- */
-struct iscsi_conn_buf {
- struct regpair reserved[8];
-};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
* ipv6 structure
@@ -4379,6 +5068,8 @@ struct ip_v6_addr {
u32 ip_addr_hi_hi;
};
+
+
/*
* l5cm- connection identification params
*/
@@ -4460,8 +5151,7 @@ struct l5cm_xstorm_conn_buffer {
* l5cm-tstorm connection buffer
*/
struct l5cm_tstorm_conn_buffer {
- u32 snd_buf;
- u32 rcv_buf;
+ u32 rsrv1[2];
#if defined(__BIG_ENDIAN)
u16 params;
#define L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE (0x1<<0)
@@ -4493,6 +5183,72 @@ struct l5cm_active_conn_buffer {
struct l5cm_tstorm_conn_buffer tstorm_conn_buffer;
};
+
+
+/*
+ * The l5cm opaque buffer passed in add new connection ramrod passive side
+ */
+struct l5cm_hash_input_string {
+ u32 __opaque1;
+#if defined(__BIG_ENDIAN)
+ u16 __opaque3;
+ u16 __opaque2;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __opaque2;
+ u16 __opaque3;
+#endif
+ struct ip_v6_addr __opaque4;
+ struct ip_v6_addr __opaque5;
+ u32 __opaque6;
+ u32 __opaque7[5];
+};
+
+
+/*
+ * syn cookie component
+ */
+struct l5cm_syn_cookie_comp {
+ u32 __opaque;
+};
+
+/*
+ * data related to listeners of a TCP port
+ */
+struct l5cm_port_listener_data {
+ u8 params;
+#define L5CM_PORT_LISTENER_DATA_ENABLE (0x1<<0)
+#define L5CM_PORT_LISTENER_DATA_ENABLE_SHIFT 0
+#define L5CM_PORT_LISTENER_DATA_IP_INDEX (0xF<<1)
+#define L5CM_PORT_LISTENER_DATA_IP_INDEX_SHIFT 1
+#define L5CM_PORT_LISTENER_DATA_NET_FILTER (0x1<<5)
+#define L5CM_PORT_LISTENER_DATA_NET_FILTER_SHIFT 5
+#define L5CM_PORT_LISTENER_DATA_DEFFERED_MODE (0x1<<6)
+#define L5CM_PORT_LISTENER_DATA_DEFFERED_MODE_SHIFT 6
+#define L5CM_PORT_LISTENER_DATA_MPA_MODE (0x1<<7)
+#define L5CM_PORT_LISTENER_DATA_MPA_MODE_SHIFT 7
+};
+
+/*
+ * Opaque structure passed from U to X when final ack arrives
+ */
+struct l5cm_opaque_buf {
+ u32 __opaque1;
+ u32 __opaque2;
+ u32 __opaque3;
+ u32 __opaque4;
+ struct l5cm_syn_cookie_comp __opaque5;
+#if defined(__BIG_ENDIAN)
+ u16 rsrv2;
+ u8 rsrv;
+ struct l5cm_port_listener_data __opaque6;
+#elif defined(__LITTLE_ENDIAN)
+ struct l5cm_port_listener_data __opaque6;
+ u8 rsrv;
+ u16 rsrv2;
+#endif
+};
+
+
/*
* l5cm slow path element
*/
@@ -4501,6 +5257,109 @@ struct l5cm_packet_size {
u32 rsrv;
};
+
+/*
+ * The final-ack union structure in PCS entry after final ack arrived
+ */
+struct l5cm_pcse_ack {
+ struct l5cm_xstorm_conn_buffer tx_socket_params;
+ struct l5cm_opaque_buf opaque_buf;
+ struct l5cm_tstorm_conn_buffer rx_socket_params;
+};
+
+
+/*
+ * The syn union structure in PCS entry after syn arrived
+ */
+struct l5cm_pcse_syn {
+ struct l5cm_opaque_buf opaque_buf;
+ u32 rsrv[12];
+};
+
+
+/*
+ * pcs entry data for passive connections
+ */
+struct l5cm_pcs_attributes {
+#if defined(__BIG_ENDIAN)
+ u16 pcs_id;
+ u8 status;
+ u8 flags;
+#define L5CM_PCS_ATTRIBUTES_NET_FILTER (0x1<<0)
+#define L5CM_PCS_ATTRIBUTES_NET_FILTER_SHIFT 0
+#define L5CM_PCS_ATTRIBUTES_CALCULATE_HASH (0x1<<1)
+#define L5CM_PCS_ATTRIBUTES_CALCULATE_HASH_SHIFT 1
+#define L5CM_PCS_ATTRIBUTES_COMPARE_HASH_RESULT (0x1<<2)
+#define L5CM_PCS_ATTRIBUTES_COMPARE_HASH_RESULT_SHIFT 2
+#define L5CM_PCS_ATTRIBUTES_QUERY_ULP_ACCEPT (0x1<<3)
+#define L5CM_PCS_ATTRIBUTES_QUERY_ULP_ACCEPT_SHIFT 3
+#define L5CM_PCS_ATTRIBUTES_FIND_DEST_MAC (0x1<<4)
+#define L5CM_PCS_ATTRIBUTES_FIND_DEST_MAC_SHIFT 4
+#define L5CM_PCS_ATTRIBUTES_L4_OFFLOAD (0x1<<5)
+#define L5CM_PCS_ATTRIBUTES_L4_OFFLOAD_SHIFT 5
+#define L5CM_PCS_ATTRIBUTES_FORWARD_PACKET (0x1<<6)
+#define L5CM_PCS_ATTRIBUTES_FORWARD_PACKET_SHIFT 6
+#define L5CM_PCS_ATTRIBUTES_RSRV (0x1<<7)
+#define L5CM_PCS_ATTRIBUTES_RSRV_SHIFT 7
+#elif defined(__LITTLE_ENDIAN)
+ u8 flags;
+#define L5CM_PCS_ATTRIBUTES_NET_FILTER (0x1<<0)
+#define L5CM_PCS_ATTRIBUTES_NET_FILTER_SHIFT 0
+#define L5CM_PCS_ATTRIBUTES_CALCULATE_HASH (0x1<<1)
+#define L5CM_PCS_ATTRIBUTES_CALCULATE_HASH_SHIFT 1
+#define L5CM_PCS_ATTRIBUTES_COMPARE_HASH_RESULT (0x1<<2)
+#define L5CM_PCS_ATTRIBUTES_COMPARE_HASH_RESULT_SHIFT 2
+#define L5CM_PCS_ATTRIBUTES_QUERY_ULP_ACCEPT (0x1<<3)
+#define L5CM_PCS_ATTRIBUTES_QUERY_ULP_ACCEPT_SHIFT 3
+#define L5CM_PCS_ATTRIBUTES_FIND_DEST_MAC (0x1<<4)
+#define L5CM_PCS_ATTRIBUTES_FIND_DEST_MAC_SHIFT 4
+#define L5CM_PCS_ATTRIBUTES_L4_OFFLOAD (0x1<<5)
+#define L5CM_PCS_ATTRIBUTES_L4_OFFLOAD_SHIFT 5
+#define L5CM_PCS_ATTRIBUTES_FORWARD_PACKET (0x1<<6)
+#define L5CM_PCS_ATTRIBUTES_FORWARD_PACKET_SHIFT 6
+#define L5CM_PCS_ATTRIBUTES_RSRV (0x1<<7)
+#define L5CM_PCS_ATTRIBUTES_RSRV_SHIFT 7
+ u8 status;
+ u16 pcs_id;
+#endif
+};
+
+
+union l5cm_seg_params {
+ struct l5cm_pcse_syn syn_seg_params;
+ struct l5cm_pcse_ack ack_seg_params;
+};
+
+/*
+ * pcs entry data for passive connections
+ */
+struct l5cm_pcs_hdr {
+ struct l5cm_hash_input_string hash_input_string;
+ struct l5cm_conn_addr_params conn_addr_buf;
+ u32 cid;
+ u32 hash_result;
+ union l5cm_seg_params seg_params;
+ struct l5cm_pcs_attributes att;
+#if defined(__BIG_ENDIAN)
+ u16 rsrv;
+ u16 rx_seg_size;
+#elif defined(__LITTLE_ENDIAN)
+ u16 rx_seg_size;
+ u16 rsrv;
+#endif
+};
+
+/*
+ * pcs entry for passive connections
+ */
+struct l5cm_pcs_entry {
+ struct l5cm_pcs_hdr hdr;
+ u8 rx_segment[1516];
+};
+
+
+
+
/*
* l5cm connection parameters
*/
@@ -4535,6 +5394,29 @@ struct l5cm_spe {
union l5cm_specific_data data;
};
+
+
+
+/*
+ * Termination variables
+ */
+struct l5cm_term_vars {
+ u8 BitMap;
+#define L5CM_TERM_VARS_TCP_STATE (0xF<<0)
+#define L5CM_TERM_VARS_TCP_STATE_SHIFT 0
+#define L5CM_TERM_VARS_FIN_RECEIVED_SBIT (0x1<<4)
+#define L5CM_TERM_VARS_FIN_RECEIVED_SBIT_SHIFT 4
+#define L5CM_TERM_VARS_ACK_ON_FIN_RECEIVED_SBIT (0x1<<5)
+#define L5CM_TERM_VARS_ACK_ON_FIN_RECEIVED_SBIT_SHIFT 5
+#define L5CM_TERM_VARS_TERM_ON_CHIP (0x1<<6)
+#define L5CM_TERM_VARS_TERM_ON_CHIP_SHIFT 6
+#define L5CM_TERM_VARS_RSRV (0x1<<7)
+#define L5CM_TERM_VARS_RSRV_SHIFT 7
+};
+
+
+
+
/*
* Tstorm Tcp flags
*/
@@ -4550,6 +5432,7 @@ struct tstorm_l5cm_tcp_flags {
#define TSTORM_L5CM_TCP_FLAGS_RSRV1_SHIFT 14
};
+
/*
* Xstorm Tcp flags
*/
@@ -4565,4 +5448,38 @@ struct xstorm_l5cm_tcp_flags {
#define XSTORM_L5CM_TCP_FLAGS_RSRV_SHIFT 3
};
-#endif /* CNIC_DEFS_H */
+
+
+/*
+ * Out-of-order states
+ */
+enum tcp_ooo_event {
+ TCP_EVENT_ADD_PEN = 0,
+ TCP_EVENT_ADD_NEW_ISLE = 1,
+ TCP_EVENT_ADD_ISLE_RIGHT = 2,
+ TCP_EVENT_ADD_ISLE_LEFT = 3,
+ TCP_EVENT_JOIN = 4,
+ TCP_EVENT_NOP = 5,
+ MAX_TCP_OOO_EVENT
+};
+
+
+/*
+ * OOO support modes
+ */
+enum tcp_tstorm_ooo {
+ TCP_TSTORM_OOO_DROP_AND_PROC_ACK = 0,
+ TCP_TSTORM_OOO_SEND_PURE_ACK = 1,
+ TCP_TSTORM_OOO_SUPPORTED = 2,
+ MAX_TCP_TSTORM_OOO
+};
+
+
+
+
+
+
+
+
+
+#endif /* __5710_HSI_CNIC_LE__ */
diff --git a/drivers/net/cnic_if.h b/drivers/net/ethernet/broadcom/cnic_if.h
index fdd8e46a9050..79443e0dbf96 100644
--- a/drivers/net/cnic_if.h
+++ b/drivers/net/ethernet/broadcom/cnic_if.h
@@ -12,8 +12,8 @@
#ifndef CNIC_IF_H
#define CNIC_IF_H
-#define CNIC_MODULE_VERSION "2.2.14"
-#define CNIC_MODULE_RELDATE "Mar 30, 2011"
+#define CNIC_MODULE_VERSION "2.5.7"
+#define CNIC_MODULE_RELDATE "July 20, 2011"
#define CNIC_ULP_RDMA 0
#define CNIC_ULP_ISCSI 1
@@ -99,6 +99,8 @@ struct kcqe {
struct cnic_ctl_completion {
u32 cid;
+ u8 opcode;
+ u8 error;
};
struct cnic_ctl_info {
@@ -169,7 +171,7 @@ struct cnic_eth_dev {
struct pci_dev *pdev;
void __iomem *io_base;
void __iomem *io_base2;
- void *iro_arr;
+ const void *iro_arr;
u32 ctx_tbl_offset;
u32 ctx_tbl_len;
@@ -179,6 +181,11 @@ struct cnic_eth_dev {
u32 max_fcoe_conn;
u32 max_rdma_conn;
u32 fcoe_init_cid;
+ u32 fcoe_wwn_port_name_hi;
+ u32 fcoe_wwn_port_name_lo;
+ u32 fcoe_wwn_node_name_hi;
+ u32 fcoe_wwn_node_name_lo;
+
u16 iscsi_l2_client_id;
u16 iscsi_l2_cid;
u8 iscsi_mac[ETH_ALEN];
@@ -311,7 +318,7 @@ struct cnic_ulp_ops {
void (*cnic_stop)(void *ulp_ctx);
void (*indicate_kcqes)(void *ulp_ctx, struct kcqe *cqes[],
u32 num_cqes);
- void (*indicate_netevent)(void *ulp_ctx, unsigned long event);
+ void (*indicate_netevent)(void *ulp_ctx, unsigned long event, u16 vid);
void (*cm_connect_complete)(struct cnic_sock *);
void (*cm_close_complete)(struct cnic_sock *);
void (*cm_abort_complete)(struct cnic_sock *);
diff --git a/drivers/net/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c
index 68d50429ddf3..0a1d7f279fc8 100644
--- a/drivers/net/sb1250-mac.c
+++ b/drivers/net/ethernet/broadcom/sb1250-mac.c
@@ -2176,7 +2176,7 @@ static const struct net_device_ops sbmac_netdev_ops = {
.ndo_open = sbmac_open,
.ndo_stop = sbmac_close,
.ndo_start_xmit = sbmac_start_tx,
- .ndo_set_multicast_list = sbmac_set_rx_mode,
+ .ndo_set_rx_mode = sbmac_set_rx_mode,
.ndo_tx_timeout = sbmac_tx_timeout,
.ndo_do_ioctl = sbmac_mii_ioctl,
.ndo_change_mtu = sb1250_change_mtu,
@@ -2597,7 +2597,7 @@ static int __devinit sbmac_probe(struct platform_device *pldev)
res = platform_get_resource(pldev, IORESOURCE_MEM, 0);
BUG_ON(!res);
- sbm_base = ioremap_nocache(res->start, res->end - res->start + 1);
+ sbm_base = ioremap_nocache(res->start, resource_size(res));
if (!sbm_base) {
printk(KERN_ERR "%s: unable to map device registers\n",
dev_name(&pldev->dev));
diff --git a/drivers/net/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index a1f9f9eef37d..bf4074167d6a 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/in.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
@@ -88,12 +89,15 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
#define DRV_MODULE_NAME "tg3"
#define TG3_MAJ_NUM 3
-#define TG3_MIN_NUM 119
+#define TG3_MIN_NUM 121
#define DRV_MODULE_VERSION \
__stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM)
-#define DRV_MODULE_RELDATE "May 18, 2011"
+#define DRV_MODULE_RELDATE "November 2, 2011"
+
+#define RESET_KIND_SHUTDOWN 0
+#define RESET_KIND_INIT 1
+#define RESET_KIND_SUSPEND 2
-#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
#define TG3_DEF_TX_MODE 0
#define TG3_DEF_MSG_ENABLE \
@@ -106,6 +110,8 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
NETIF_MSG_RX_ERR | \
NETIF_MSG_TX_ERR)
+#define TG3_GRC_LCLCTL_PWRSW_DELAY 100
+
/* length of time before we decide the hardware is borked,
* and dev->tx_timeout() should be called to fix the problem
*/
@@ -185,8 +191,15 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
#define TG3_RX_COPY_THRESH(tp) ((tp)->rx_copy_thresh)
#endif
+#if (NET_IP_ALIGN != 0)
+#define TG3_RX_OFFSET(tp) ((tp)->rx_offset)
+#else
+#define TG3_RX_OFFSET(tp) 0
+#endif
+
/* minimum number of free TX descriptors required to wake up TX process */
#define TG3_TX_WAKEUP_THRESH(tnapi) ((tnapi)->tx_pending / 4)
+#define TG3_TX_BD_DMA_MAX 4096
#define TG3_RAW_IP_ALIGN 2
@@ -387,12 +400,14 @@ static const struct {
static const struct {
const char string[ETH_GSTRING_LEN];
} ethtool_test_keys[] = {
- { "nvram test (online) " },
- { "link test (online) " },
- { "register test (offline)" },
- { "memory test (offline)" },
- { "loopback test (offline)" },
- { "interrupt test (offline)" },
+ { "nvram test (online) " },
+ { "link test (online) " },
+ { "register test (offline)" },
+ { "memory test (offline)" },
+ { "mac loopback test (offline)" },
+ { "phy loopback test (offline)" },
+ { "ext loopback test (offline)" },
+ { "interrupt test (offline)" },
};
#define TG3_NUM_TEST ARRAY_SIZE(ethtool_test_keys)
@@ -605,7 +620,7 @@ static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val)
static void tg3_ape_lock_init(struct tg3 *tp)
{
int i;
- u32 regbase;
+ u32 regbase, bit;
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761)
regbase = TG3_APE_LOCK_GRANT;
@@ -613,22 +628,44 @@ static void tg3_ape_lock_init(struct tg3 *tp)
regbase = TG3_APE_PER_LOCK_GRANT;
/* Make sure the driver hasn't any stale locks. */
- for (i = 0; i < 8; i++)
- tg3_ape_write32(tp, regbase + 4 * i, APE_LOCK_GRANT_DRIVER);
+ for (i = TG3_APE_LOCK_PHY0; i <= TG3_APE_LOCK_GPIO; i++) {
+ switch (i) {
+ case TG3_APE_LOCK_PHY0:
+ case TG3_APE_LOCK_PHY1:
+ case TG3_APE_LOCK_PHY2:
+ case TG3_APE_LOCK_PHY3:
+ bit = APE_LOCK_GRANT_DRIVER;
+ break;
+ default:
+ if (!tp->pci_fn)
+ bit = APE_LOCK_GRANT_DRIVER;
+ else
+ bit = 1 << tp->pci_fn;
+ }
+ tg3_ape_write32(tp, regbase + 4 * i, bit);
+ }
+
}
static int tg3_ape_lock(struct tg3 *tp, int locknum)
{
int i, off;
int ret = 0;
- u32 status, req, gnt;
+ u32 status, req, gnt, bit;
if (!tg3_flag(tp, ENABLE_APE))
return 0;
switch (locknum) {
+ case TG3_APE_LOCK_GPIO:
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761)
+ return 0;
case TG3_APE_LOCK_GRC:
case TG3_APE_LOCK_MEM:
+ if (!tp->pci_fn)
+ bit = APE_LOCK_REQ_DRIVER;
+ else
+ bit = 1 << tp->pci_fn;
break;
default:
return -EINVAL;
@@ -644,21 +681,19 @@ static int tg3_ape_lock(struct tg3 *tp, int locknum)
off = 4 * locknum;
- tg3_ape_write32(tp, req + off, APE_LOCK_REQ_DRIVER);
+ tg3_ape_write32(tp, req + off, bit);
/* Wait for up to 1 millisecond to acquire lock. */
for (i = 0; i < 100; i++) {
status = tg3_ape_read32(tp, gnt + off);
- if (status == APE_LOCK_GRANT_DRIVER)
+ if (status == bit)
break;
udelay(10);
}
- if (status != APE_LOCK_GRANT_DRIVER) {
+ if (status != bit) {
/* Revoke the lock request. */
- tg3_ape_write32(tp, gnt + off,
- APE_LOCK_GRANT_DRIVER);
-
+ tg3_ape_write32(tp, gnt + off, bit);
ret = -EBUSY;
}
@@ -667,14 +702,21 @@ static int tg3_ape_lock(struct tg3 *tp, int locknum)
static void tg3_ape_unlock(struct tg3 *tp, int locknum)
{
- u32 gnt;
+ u32 gnt, bit;
if (!tg3_flag(tp, ENABLE_APE))
return;
switch (locknum) {
+ case TG3_APE_LOCK_GPIO:
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761)
+ return;
case TG3_APE_LOCK_GRC:
case TG3_APE_LOCK_MEM:
+ if (!tp->pci_fn)
+ bit = APE_LOCK_GRANT_DRIVER;
+ else
+ bit = 1 << tp->pci_fn;
break;
default:
return;
@@ -685,7 +727,104 @@ static void tg3_ape_unlock(struct tg3 *tp, int locknum)
else
gnt = TG3_APE_PER_LOCK_GRANT;
- tg3_ape_write32(tp, gnt + 4 * locknum, APE_LOCK_GRANT_DRIVER);
+ tg3_ape_write32(tp, gnt + 4 * locknum, bit);
+}
+
+static void tg3_ape_send_event(struct tg3 *tp, u32 event)
+{
+ int i;
+ u32 apedata;
+
+ /* NCSI does not support APE events */
+ if (tg3_flag(tp, APE_HAS_NCSI))
+ return;
+
+ apedata = tg3_ape_read32(tp, TG3_APE_SEG_SIG);
+ if (apedata != APE_SEG_SIG_MAGIC)
+ return;
+
+ apedata = tg3_ape_read32(tp, TG3_APE_FW_STATUS);
+ if (!(apedata & APE_FW_STATUS_READY))
+ return;
+
+ /* Wait for up to 1 millisecond for APE to service previous event. */
+ for (i = 0; i < 10; i++) {
+ if (tg3_ape_lock(tp, TG3_APE_LOCK_MEM))
+ return;
+
+ apedata = tg3_ape_read32(tp, TG3_APE_EVENT_STATUS);
+
+ if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
+ tg3_ape_write32(tp, TG3_APE_EVENT_STATUS,
+ event | APE_EVENT_STATUS_EVENT_PENDING);
+
+ tg3_ape_unlock(tp, TG3_APE_LOCK_MEM);
+
+ if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
+ break;
+
+ udelay(100);
+ }
+
+ if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
+ tg3_ape_write32(tp, TG3_APE_EVENT, APE_EVENT_1);
+}
+
+static void tg3_ape_driver_state_change(struct tg3 *tp, int kind)
+{
+ u32 event;
+ u32 apedata;
+
+ if (!tg3_flag(tp, ENABLE_APE))
+ return;
+
+ switch (kind) {
+ case RESET_KIND_INIT:
+ tg3_ape_write32(tp, TG3_APE_HOST_SEG_SIG,
+ APE_HOST_SEG_SIG_MAGIC);
+ tg3_ape_write32(tp, TG3_APE_HOST_SEG_LEN,
+ APE_HOST_SEG_LEN_MAGIC);
+ apedata = tg3_ape_read32(tp, TG3_APE_HOST_INIT_COUNT);
+ tg3_ape_write32(tp, TG3_APE_HOST_INIT_COUNT, ++apedata);
+ tg3_ape_write32(tp, TG3_APE_HOST_DRIVER_ID,
+ APE_HOST_DRIVER_ID_MAGIC(TG3_MAJ_NUM, TG3_MIN_NUM));
+ tg3_ape_write32(tp, TG3_APE_HOST_BEHAVIOR,
+ APE_HOST_BEHAV_NO_PHYLOCK);
+ tg3_ape_write32(tp, TG3_APE_HOST_DRVR_STATE,
+ TG3_APE_HOST_DRVR_STATE_START);
+
+ event = APE_EVENT_STATUS_STATE_START;
+ break;
+ case RESET_KIND_SHUTDOWN:
+ /* With the interface we are currently using,
+ * APE does not track driver state. Wiping
+ * out the HOST SEGMENT SIGNATURE forces
+ * the APE to assume OS absent status.
+ */
+ tg3_ape_write32(tp, TG3_APE_HOST_SEG_SIG, 0x0);
+
+ if (device_may_wakeup(&tp->pdev->dev) &&
+ tg3_flag(tp, WOL_ENABLE)) {
+ tg3_ape_write32(tp, TG3_APE_HOST_WOL_SPEED,
+ TG3_APE_HOST_WOL_SPEED_AUTO);
+ apedata = TG3_APE_HOST_DRVR_STATE_WOL;
+ } else
+ apedata = TG3_APE_HOST_DRVR_STATE_UNLOAD;
+
+ tg3_ape_write32(tp, TG3_APE_HOST_DRVR_STATE, apedata);
+
+ event = APE_EVENT_STATUS_STATE_UNLOAD;
+ break;
+ case RESET_KIND_SUSPEND:
+ event = APE_EVENT_STATUS_STATE_SUSPEND;
+ break;
+ default:
+ return;
+ }
+
+ event |= APE_EVENT_STATUS_DRIVER_EVNT | APE_EVENT_STATUS_STATE_CHNGE;
+
+ tg3_ape_send_event(tp, event);
}
static void tg3_disable_ints(struct tg3 *tp)
@@ -860,7 +999,7 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val)
int ret;
if ((tp->phy_flags & TG3_PHYFLG_IS_FET) &&
- (reg == MII_TG3_CTRL || reg == MII_TG3_AUX_CTRL))
+ (reg == MII_CTRL1000 || reg == MII_TG3_AUX_CTRL))
return 0;
if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
@@ -1167,7 +1306,7 @@ static int tg3_mdio_init(struct tg3 *tp)
if (tg3_flag(tp, 5717_PLUS)) {
u32 is_serdes;
- tp->phy_addr = PCI_FUNC(tp->pdev->devfn) + 1;
+ tp->phy_addr = tp->pci_fn + 1;
if (tp->pci_chip_rev_id != CHIPREV_ID_5717_A0)
is_serdes = tr32(SG_DIG_STATUS) & SG_DIG_IS_SERDES;
@@ -1360,6 +1499,149 @@ static void tg3_ump_link_report(struct tg3 *tp)
tg3_generate_fw_event(tp);
}
+/* tp->lock is held. */
+static void tg3_stop_fw(struct tg3 *tp)
+{
+ if (tg3_flag(tp, ENABLE_ASF) && !tg3_flag(tp, ENABLE_APE)) {
+ /* Wait for RX cpu to ACK the previous event. */
+ tg3_wait_for_event_ack(tp);
+
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_PAUSE_FW);
+
+ tg3_generate_fw_event(tp);
+
+ /* Wait for RX cpu to ACK this event. */
+ tg3_wait_for_event_ack(tp);
+ }
+}
+
+/* tp->lock is held. */
+static void tg3_write_sig_pre_reset(struct tg3 *tp, int kind)
+{
+ tg3_write_mem(tp, NIC_SRAM_FIRMWARE_MBOX,
+ NIC_SRAM_FIRMWARE_MBOX_MAGIC1);
+
+ if (tg3_flag(tp, ASF_NEW_HANDSHAKE)) {
+ switch (kind) {
+ case RESET_KIND_INIT:
+ tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
+ DRV_STATE_START);
+ break;
+
+ case RESET_KIND_SHUTDOWN:
+ tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
+ DRV_STATE_UNLOAD);
+ break;
+
+ case RESET_KIND_SUSPEND:
+ tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
+ DRV_STATE_SUSPEND);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (kind == RESET_KIND_INIT ||
+ kind == RESET_KIND_SUSPEND)
+ tg3_ape_driver_state_change(tp, kind);
+}
+
+/* tp->lock is held. */
+static void tg3_write_sig_post_reset(struct tg3 *tp, int kind)
+{
+ if (tg3_flag(tp, ASF_NEW_HANDSHAKE)) {
+ switch (kind) {
+ case RESET_KIND_INIT:
+ tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
+ DRV_STATE_START_DONE);
+ break;
+
+ case RESET_KIND_SHUTDOWN:
+ tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
+ DRV_STATE_UNLOAD_DONE);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (kind == RESET_KIND_SHUTDOWN)
+ tg3_ape_driver_state_change(tp, kind);
+}
+
+/* tp->lock is held. */
+static void tg3_write_sig_legacy(struct tg3 *tp, int kind)
+{
+ if (tg3_flag(tp, ENABLE_ASF)) {
+ switch (kind) {
+ case RESET_KIND_INIT:
+ tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
+ DRV_STATE_START);
+ break;
+
+ case RESET_KIND_SHUTDOWN:
+ tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
+ DRV_STATE_UNLOAD);
+ break;
+
+ case RESET_KIND_SUSPEND:
+ tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
+ DRV_STATE_SUSPEND);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static int tg3_poll_fw(struct tg3 *tp)
+{
+ int i;
+ u32 val;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ /* Wait up to 20ms for init done. */
+ for (i = 0; i < 200; i++) {
+ if (tr32(VCPU_STATUS) & VCPU_STATUS_INIT_DONE)
+ return 0;
+ udelay(100);
+ }
+ return -ENODEV;
+ }
+
+ /* Wait for firmware initialization to complete. */
+ for (i = 0; i < 100000; i++) {
+ tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val);
+ if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1)
+ break;
+ udelay(10);
+ }
+
+ /* Chip might not be fitted with firmware. Some Sun onboard
+ * parts are configured like that. So don't signal the timeout
+ * of the above loop as an error, but do report the lack of
+ * running firmware once.
+ */
+ if (i >= 100000 && !tg3_flag(tp, NO_FWARE_REPORTED)) {
+ tg3_flag_set(tp, NO_FWARE_REPORTED);
+
+ netdev_info(tp->dev, "No firmware running\n");
+ }
+
+ if (tp->pci_chip_rev_id == CHIPREV_ID_57765_A0) {
+ /* The 57765 A0 needs a little more
+ * time to do some important work.
+ */
+ mdelay(10);
+ }
+
+ return 0;
+}
+
static void tg3_link_report(struct tg3 *tp)
{
if (!netif_carrier_ok(tp->dev)) {
@@ -1651,6 +1933,36 @@ static void tg3_phy_fini(struct tg3 *tp)
}
}
+static int tg3_phy_set_extloopbk(struct tg3 *tp)
+{
+ int err;
+ u32 val;
+
+ if (tp->phy_flags & TG3_PHYFLG_IS_FET)
+ return 0;
+
+ if ((tp->phy_id & TG3_PHY_ID_MASK) == TG3_PHY_ID_BCM5401) {
+ /* Cannot do read-modify-write on 5401 */
+ err = tg3_phy_auxctl_write(tp,
+ MII_TG3_AUXCTL_SHDWSEL_AUXCTL,
+ MII_TG3_AUXCTL_ACTL_EXTLOOPBK |
+ 0x4c20);
+ goto done;
+ }
+
+ err = tg3_phy_auxctl_read(tp,
+ MII_TG3_AUXCTL_SHDWSEL_AUXCTL, &val);
+ if (err)
+ return err;
+
+ val |= MII_TG3_AUXCTL_ACTL_EXTLOOPBK;
+ err = tg3_phy_auxctl_write(tp,
+ MII_TG3_AUXCTL_SHDWSEL_AUXCTL, val);
+
+done:
+ return err;
+}
+
static void tg3_phy_fet_toggle_apd(struct tg3 *tp, bool enable)
{
u32 phytest;
@@ -1830,6 +2142,12 @@ static void tg3_phy_eee_adjust(struct tg3 *tp, u32 current_link_up)
}
if (!tp->setlpicnt) {
+ if (current_link_up == 1 &&
+ !TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) {
+ tg3_phydsp_write(tp, MII_TG3_DSP_TAP26, 0x0000);
+ TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
+ }
+
val = tr32(TG3_CPMU_EEE_MODE);
tw32(TG3_CPMU_EEE_MODE, val & ~TG3_CPMU_EEEMD_LPI_ENABLE);
}
@@ -1844,7 +2162,9 @@ static void tg3_phy_eee_enable(struct tg3 *tp)
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) &&
!TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) {
- tg3_phydsp_write(tp, MII_TG3_DSP_TAP26, 0x0003);
+ val = MII_TG3_DSP_TAP26_ALNOKO |
+ MII_TG3_DSP_TAP26_RMRXSTO;
+ tg3_phydsp_write(tp, MII_TG3_DSP_TAP26, val);
TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
}
@@ -1980,15 +2300,14 @@ static int tg3_phy_reset_5703_4_5(struct tg3 *tp)
/* Set full-duplex, 1000 mbps. */
tg3_writephy(tp, MII_BMCR,
- BMCR_FULLDPLX | TG3_BMCR_SPEED1000);
+ BMCR_FULLDPLX | BMCR_SPEED1000);
/* Set to master mode. */
- if (tg3_readphy(tp, MII_TG3_CTRL, &phy9_orig))
+ if (tg3_readphy(tp, MII_CTRL1000, &phy9_orig))
continue;
- tg3_writephy(tp, MII_TG3_CTRL,
- (MII_TG3_CTRL_AS_MASTER |
- MII_TG3_CTRL_ENABLE_AS_MASTER));
+ tg3_writephy(tp, MII_CTRL1000,
+ CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER);
err = TG3_PHY_AUXCTL_SMDSP_ENABLE(tp);
if (err)
@@ -2013,7 +2332,7 @@ static int tg3_phy_reset_5703_4_5(struct tg3 *tp)
TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
- tg3_writephy(tp, MII_TG3_CTRL, phy9_orig);
+ tg3_writephy(tp, MII_CTRL1000, phy9_orig);
if (!tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32)) {
reg32 &= ~0x3000;
@@ -2165,21 +2484,214 @@ out:
return 0;
}
-static void tg3_frob_aux_power(struct tg3 *tp)
+#define TG3_GPIO_MSG_DRVR_PRES 0x00000001
+#define TG3_GPIO_MSG_NEED_VAUX 0x00000002
+#define TG3_GPIO_MSG_MASK (TG3_GPIO_MSG_DRVR_PRES | \
+ TG3_GPIO_MSG_NEED_VAUX)
+#define TG3_GPIO_MSG_ALL_DRVR_PRES_MASK \
+ ((TG3_GPIO_MSG_DRVR_PRES << 0) | \
+ (TG3_GPIO_MSG_DRVR_PRES << 4) | \
+ (TG3_GPIO_MSG_DRVR_PRES << 8) | \
+ (TG3_GPIO_MSG_DRVR_PRES << 12))
+
+#define TG3_GPIO_MSG_ALL_NEED_VAUX_MASK \
+ ((TG3_GPIO_MSG_NEED_VAUX << 0) | \
+ (TG3_GPIO_MSG_NEED_VAUX << 4) | \
+ (TG3_GPIO_MSG_NEED_VAUX << 8) | \
+ (TG3_GPIO_MSG_NEED_VAUX << 12))
+
+static inline u32 tg3_set_function_status(struct tg3 *tp, u32 newstat)
+{
+ u32 status, shift;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+ status = tg3_ape_read32(tp, TG3_APE_GPIO_MSG);
+ else
+ status = tr32(TG3_CPMU_DRV_STATUS);
+
+ shift = TG3_APE_GPIO_MSG_SHIFT + 4 * tp->pci_fn;
+ status &= ~(TG3_GPIO_MSG_MASK << shift);
+ status |= (newstat << shift);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+ tg3_ape_write32(tp, TG3_APE_GPIO_MSG, status);
+ else
+ tw32(TG3_CPMU_DRV_STATUS, status);
+
+ return status >> TG3_APE_GPIO_MSG_SHIFT;
+}
+
+static inline int tg3_pwrsrc_switch_to_vmain(struct tg3 *tp)
+{
+ if (!tg3_flag(tp, IS_NIC))
+ return 0;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) {
+ if (tg3_ape_lock(tp, TG3_APE_LOCK_GPIO))
+ return -EIO;
+
+ tg3_set_function_status(tp, TG3_GPIO_MSG_DRVR_PRES);
+
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+
+ tg3_ape_unlock(tp, TG3_APE_LOCK_GPIO);
+ } else {
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+ }
+
+ return 0;
+}
+
+static void tg3_pwrsrc_die_with_vmain(struct tg3 *tp)
+{
+ u32 grc_local_ctrl;
+
+ if (!tg3_flag(tp, IS_NIC) ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)
+ return;
+
+ grc_local_ctrl = tp->grc_local_ctrl | GRC_LCLCTRL_GPIO_OE1;
+
+ tw32_wait_f(GRC_LOCAL_CTRL,
+ grc_local_ctrl | GRC_LCLCTRL_GPIO_OUTPUT1,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+
+ tw32_wait_f(GRC_LOCAL_CTRL,
+ grc_local_ctrl,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+
+ tw32_wait_f(GRC_LOCAL_CTRL,
+ grc_local_ctrl | GRC_LCLCTRL_GPIO_OUTPUT1,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+}
+
+static void tg3_pwrsrc_switch_to_vaux(struct tg3 *tp)
+{
+ if (!tg3_flag(tp, IS_NIC))
+ return;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) {
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ (GRC_LCLCTRL_GPIO_OE0 |
+ GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OE2 |
+ GRC_LCLCTRL_GPIO_OUTPUT0 |
+ GRC_LCLCTRL_GPIO_OUTPUT1),
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+ } else if (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5761 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5761S) {
+ /* The 5761 non-e device swaps GPIO 0 and GPIO 2. */
+ u32 grc_local_ctrl = GRC_LCLCTRL_GPIO_OE0 |
+ GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OE2 |
+ GRC_LCLCTRL_GPIO_OUTPUT0 |
+ GRC_LCLCTRL_GPIO_OUTPUT1 |
+ tp->grc_local_ctrl;
+ tw32_wait_f(GRC_LOCAL_CTRL, grc_local_ctrl,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+
+ grc_local_ctrl |= GRC_LCLCTRL_GPIO_OUTPUT2;
+ tw32_wait_f(GRC_LOCAL_CTRL, grc_local_ctrl,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+
+ grc_local_ctrl &= ~GRC_LCLCTRL_GPIO_OUTPUT0;
+ tw32_wait_f(GRC_LOCAL_CTRL, grc_local_ctrl,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+ } else {
+ u32 no_gpio2;
+ u32 grc_local_ctrl = 0;
+
+ /* Workaround to prevent overdrawing Amps. */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) {
+ grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE3;
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ grc_local_ctrl,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+ }
+
+ /* On 5753 and variants, GPIO2 cannot be used. */
+ no_gpio2 = tp->nic_sram_data_cfg &
+ NIC_SRAM_DATA_CFG_NO_GPIO2;
+
+ grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE0 |
+ GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OE2 |
+ GRC_LCLCTRL_GPIO_OUTPUT1 |
+ GRC_LCLCTRL_GPIO_OUTPUT2;
+ if (no_gpio2) {
+ grc_local_ctrl &= ~(GRC_LCLCTRL_GPIO_OE2 |
+ GRC_LCLCTRL_GPIO_OUTPUT2);
+ }
+ tw32_wait_f(GRC_LOCAL_CTRL,
+ tp->grc_local_ctrl | grc_local_ctrl,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+
+ grc_local_ctrl |= GRC_LCLCTRL_GPIO_OUTPUT0;
+
+ tw32_wait_f(GRC_LOCAL_CTRL,
+ tp->grc_local_ctrl | grc_local_ctrl,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+
+ if (!no_gpio2) {
+ grc_local_ctrl &= ~GRC_LCLCTRL_GPIO_OUTPUT2;
+ tw32_wait_f(GRC_LOCAL_CTRL,
+ tp->grc_local_ctrl | grc_local_ctrl,
+ TG3_GRC_LCLCTL_PWRSW_DELAY);
+ }
+ }
+}
+
+static void tg3_frob_aux_power_5717(struct tg3 *tp, bool wol_enable)
+{
+ u32 msg = 0;
+
+ /* Serialize power state transitions */
+ if (tg3_ape_lock(tp, TG3_APE_LOCK_GPIO))
+ return;
+
+ if (tg3_flag(tp, ENABLE_ASF) || tg3_flag(tp, ENABLE_APE) || wol_enable)
+ msg = TG3_GPIO_MSG_NEED_VAUX;
+
+ msg = tg3_set_function_status(tp, msg);
+
+ if (msg & TG3_GPIO_MSG_ALL_DRVR_PRES_MASK)
+ goto done;
+
+ if (msg & TG3_GPIO_MSG_ALL_NEED_VAUX_MASK)
+ tg3_pwrsrc_switch_to_vaux(tp);
+ else
+ tg3_pwrsrc_die_with_vmain(tp);
+
+done:
+ tg3_ape_unlock(tp, TG3_APE_LOCK_GPIO);
+}
+
+static void tg3_frob_aux_power(struct tg3 *tp, bool include_wol)
{
bool need_vaux = false;
/* The GPIOs do something completely different on 57765. */
if (!tg3_flag(tp, IS_NIC) ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
return;
- if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) &&
- tp->pdev_peer != tp->pdev) {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) {
+ tg3_frob_aux_power_5717(tp, include_wol ?
+ tg3_flag(tp, WOL_ENABLE) != 0 : 0);
+ return;
+ }
+
+ if (tp->pdev_peer && tp->pdev_peer != tp->pdev) {
struct net_device *dev_peer;
dev_peer = pci_get_drvdata(tp->pdev_peer);
@@ -2191,95 +2703,20 @@ static void tg3_frob_aux_power(struct tg3 *tp)
if (tg3_flag(tp_peer, INIT_COMPLETE))
return;
- if (tg3_flag(tp_peer, WOL_ENABLE) ||
+ if ((include_wol && tg3_flag(tp_peer, WOL_ENABLE)) ||
tg3_flag(tp_peer, ENABLE_ASF))
need_vaux = true;
}
}
- if (tg3_flag(tp, WOL_ENABLE) || tg3_flag(tp, ENABLE_ASF))
+ if ((include_wol && tg3_flag(tp, WOL_ENABLE)) ||
+ tg3_flag(tp, ENABLE_ASF))
need_vaux = true;
- if (need_vaux) {
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) {
- tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- (GRC_LCLCTRL_GPIO_OE0 |
- GRC_LCLCTRL_GPIO_OE1 |
- GRC_LCLCTRL_GPIO_OE2 |
- GRC_LCLCTRL_GPIO_OUTPUT0 |
- GRC_LCLCTRL_GPIO_OUTPUT1),
- 100);
- } else if (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5761 ||
- tp->pdev->device == TG3PCI_DEVICE_TIGON3_5761S) {
- /* The 5761 non-e device swaps GPIO 0 and GPIO 2. */
- u32 grc_local_ctrl = GRC_LCLCTRL_GPIO_OE0 |
- GRC_LCLCTRL_GPIO_OE1 |
- GRC_LCLCTRL_GPIO_OE2 |
- GRC_LCLCTRL_GPIO_OUTPUT0 |
- GRC_LCLCTRL_GPIO_OUTPUT1 |
- tp->grc_local_ctrl;
- tw32_wait_f(GRC_LOCAL_CTRL, grc_local_ctrl, 100);
-
- grc_local_ctrl |= GRC_LCLCTRL_GPIO_OUTPUT2;
- tw32_wait_f(GRC_LOCAL_CTRL, grc_local_ctrl, 100);
-
- grc_local_ctrl &= ~GRC_LCLCTRL_GPIO_OUTPUT0;
- tw32_wait_f(GRC_LOCAL_CTRL, grc_local_ctrl, 100);
- } else {
- u32 no_gpio2;
- u32 grc_local_ctrl = 0;
-
- /* Workaround to prevent overdrawing Amps. */
- if (GET_ASIC_REV(tp->pci_chip_rev_id) ==
- ASIC_REV_5714) {
- grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE3;
- tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- grc_local_ctrl, 100);
- }
-
- /* On 5753 and variants, GPIO2 cannot be used. */
- no_gpio2 = tp->nic_sram_data_cfg &
- NIC_SRAM_DATA_CFG_NO_GPIO2;
-
- grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE0 |
- GRC_LCLCTRL_GPIO_OE1 |
- GRC_LCLCTRL_GPIO_OE2 |
- GRC_LCLCTRL_GPIO_OUTPUT1 |
- GRC_LCLCTRL_GPIO_OUTPUT2;
- if (no_gpio2) {
- grc_local_ctrl &= ~(GRC_LCLCTRL_GPIO_OE2 |
- GRC_LCLCTRL_GPIO_OUTPUT2);
- }
- tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- grc_local_ctrl, 100);
-
- grc_local_ctrl |= GRC_LCLCTRL_GPIO_OUTPUT0;
-
- tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- grc_local_ctrl, 100);
-
- if (!no_gpio2) {
- grc_local_ctrl &= ~GRC_LCLCTRL_GPIO_OUTPUT2;
- tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- grc_local_ctrl, 100);
- }
- }
- } else {
- if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) {
- tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- (GRC_LCLCTRL_GPIO_OE1 |
- GRC_LCLCTRL_GPIO_OUTPUT1), 100);
-
- tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- GRC_LCLCTRL_GPIO_OE1, 100);
-
- tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- (GRC_LCLCTRL_GPIO_OE1 |
- GRC_LCLCTRL_GPIO_OUTPUT1), 100);
- }
- }
+ if (need_vaux)
+ tg3_pwrsrc_switch_to_vaux(tp);
+ else
+ tg3_pwrsrc_die_with_vmain(tp);
}
static int tg3_5700_link_polarity(struct tg3 *tp, u32 speed)
@@ -2296,12 +2733,6 @@ static int tg3_5700_link_polarity(struct tg3 *tp, u32 speed)
}
static int tg3_setup_phy(struct tg3 *, int);
-
-#define RESET_KIND_SHUTDOWN 0
-#define RESET_KIND_INIT 1
-#define RESET_KIND_SUSPEND 2
-
-static void tg3_write_sig_post_reset(struct tg3 *, int);
static int tg3_halt_cpu(struct tg3 *, u32);
static void tg3_power_down_phy(struct tg3 *tp, bool do_low_power)
@@ -2570,6 +3001,228 @@ static int tg3_nvram_read_be32(struct tg3 *tp, u32 offset, __be32 *val)
return res;
}
+#define RX_CPU_SCRATCH_BASE 0x30000
+#define RX_CPU_SCRATCH_SIZE 0x04000
+#define TX_CPU_SCRATCH_BASE 0x34000
+#define TX_CPU_SCRATCH_SIZE 0x04000
+
+/* tp->lock is held. */
+static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
+{
+ int i;
+
+ BUG_ON(offset == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS));
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ u32 val = tr32(GRC_VCPU_EXT_CTRL);
+
+ tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_HALT_CPU);
+ return 0;
+ }
+ if (offset == RX_CPU_BASE) {
+ for (i = 0; i < 10000; i++) {
+ tw32(offset + CPU_STATE, 0xffffffff);
+ tw32(offset + CPU_MODE, CPU_MODE_HALT);
+ if (tr32(offset + CPU_MODE) & CPU_MODE_HALT)
+ break;
+ }
+
+ tw32(offset + CPU_STATE, 0xffffffff);
+ tw32_f(offset + CPU_MODE, CPU_MODE_HALT);
+ udelay(10);
+ } else {
+ for (i = 0; i < 10000; i++) {
+ tw32(offset + CPU_STATE, 0xffffffff);
+ tw32(offset + CPU_MODE, CPU_MODE_HALT);
+ if (tr32(offset + CPU_MODE) & CPU_MODE_HALT)
+ break;
+ }
+ }
+
+ if (i >= 10000) {
+ netdev_err(tp->dev, "%s timed out, %s CPU\n",
+ __func__, offset == RX_CPU_BASE ? "RX" : "TX");
+ return -ENODEV;
+ }
+
+ /* Clear firmware's nvram arbitration. */
+ if (tg3_flag(tp, NVRAM))
+ tw32(NVRAM_SWARB, SWARB_REQ_CLR0);
+ return 0;
+}
+
+struct fw_info {
+ unsigned int fw_base;
+ unsigned int fw_len;
+ const __be32 *fw_data;
+};
+
+/* tp->lock is held. */
+static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base,
+ u32 cpu_scratch_base, int cpu_scratch_size,
+ struct fw_info *info)
+{
+ int err, lock_err, i;
+ void (*write_op)(struct tg3 *, u32, u32);
+
+ if (cpu_base == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS)) {
+ netdev_err(tp->dev,
+ "%s: Trying to load TX cpu firmware which is 5705\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (tg3_flag(tp, 5705_PLUS))
+ write_op = tg3_write_mem;
+ else
+ write_op = tg3_write_indirect_reg32;
+
+ /* It is possible that bootcode is still loading at this point.
+ * Get the nvram lock first before halting the cpu.
+ */
+ lock_err = tg3_nvram_lock(tp);
+ err = tg3_halt_cpu(tp, cpu_base);
+ if (!lock_err)
+ tg3_nvram_unlock(tp);
+ if (err)
+ goto out;
+
+ for (i = 0; i < cpu_scratch_size; i += sizeof(u32))
+ write_op(tp, cpu_scratch_base + i, 0);
+ tw32(cpu_base + CPU_STATE, 0xffffffff);
+ tw32(cpu_base + CPU_MODE, tr32(cpu_base+CPU_MODE)|CPU_MODE_HALT);
+ for (i = 0; i < (info->fw_len / sizeof(u32)); i++)
+ write_op(tp, (cpu_scratch_base +
+ (info->fw_base & 0xffff) +
+ (i * sizeof(u32))),
+ be32_to_cpu(info->fw_data[i]));
+
+ err = 0;
+
+out:
+ return err;
+}
+
+/* tp->lock is held. */
+static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp)
+{
+ struct fw_info info;
+ const __be32 *fw_data;
+ int err, i;
+
+ fw_data = (void *)tp->fw->data;
+
+ /* Firmware blob starts with version numbers, followed by
+ start address and length. We are setting complete length.
+ length = end_address_of_bss - start_address_of_text.
+ Remainder is the blob to be loaded contiguously
+ from start address. */
+
+ info.fw_base = be32_to_cpu(fw_data[1]);
+ info.fw_len = tp->fw->size - 12;
+ info.fw_data = &fw_data[3];
+
+ err = tg3_load_firmware_cpu(tp, RX_CPU_BASE,
+ RX_CPU_SCRATCH_BASE, RX_CPU_SCRATCH_SIZE,
+ &info);
+ if (err)
+ return err;
+
+ err = tg3_load_firmware_cpu(tp, TX_CPU_BASE,
+ TX_CPU_SCRATCH_BASE, TX_CPU_SCRATCH_SIZE,
+ &info);
+ if (err)
+ return err;
+
+ /* Now startup only the RX cpu. */
+ tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
+ tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base);
+
+ for (i = 0; i < 5; i++) {
+ if (tr32(RX_CPU_BASE + CPU_PC) == info.fw_base)
+ break;
+ tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
+ tw32(RX_CPU_BASE + CPU_MODE, CPU_MODE_HALT);
+ tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base);
+ udelay(1000);
+ }
+ if (i >= 5) {
+ netdev_err(tp->dev, "%s fails to set RX CPU PC, is %08x "
+ "should be %08x\n", __func__,
+ tr32(RX_CPU_BASE + CPU_PC), info.fw_base);
+ return -ENODEV;
+ }
+ tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
+ tw32_f(RX_CPU_BASE + CPU_MODE, 0x00000000);
+
+ return 0;
+}
+
+/* tp->lock is held. */
+static int tg3_load_tso_firmware(struct tg3 *tp)
+{
+ struct fw_info info;
+ const __be32 *fw_data;
+ unsigned long cpu_base, cpu_scratch_base, cpu_scratch_size;
+ int err, i;
+
+ if (tg3_flag(tp, HW_TSO_1) ||
+ tg3_flag(tp, HW_TSO_2) ||
+ tg3_flag(tp, HW_TSO_3))
+ return 0;
+
+ fw_data = (void *)tp->fw->data;
+
+ /* Firmware blob starts with version numbers, followed by
+ start address and length. We are setting complete length.
+ length = end_address_of_bss - start_address_of_text.
+ Remainder is the blob to be loaded contiguously
+ from start address. */
+
+ info.fw_base = be32_to_cpu(fw_data[1]);
+ cpu_scratch_size = tp->fw_len;
+ info.fw_len = tp->fw->size - 12;
+ info.fw_data = &fw_data[3];
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
+ cpu_base = RX_CPU_BASE;
+ cpu_scratch_base = NIC_SRAM_MBUF_POOL_BASE5705;
+ } else {
+ cpu_base = TX_CPU_BASE;
+ cpu_scratch_base = TX_CPU_SCRATCH_BASE;
+ cpu_scratch_size = TX_CPU_SCRATCH_SIZE;
+ }
+
+ err = tg3_load_firmware_cpu(tp, cpu_base,
+ cpu_scratch_base, cpu_scratch_size,
+ &info);
+ if (err)
+ return err;
+
+ /* Now startup the cpu. */
+ tw32(cpu_base + CPU_STATE, 0xffffffff);
+ tw32_f(cpu_base + CPU_PC, info.fw_base);
+
+ for (i = 0; i < 5; i++) {
+ if (tr32(cpu_base + CPU_PC) == info.fw_base)
+ break;
+ tw32(cpu_base + CPU_STATE, 0xffffffff);
+ tw32(cpu_base + CPU_MODE, CPU_MODE_HALT);
+ tw32_f(cpu_base + CPU_PC, info.fw_base);
+ udelay(1000);
+ }
+ if (i >= 5) {
+ netdev_err(tp->dev,
+ "%s fails to set CPU PC, is %08x should be %08x\n",
+ __func__, tr32(cpu_base + CPU_PC), info.fw_base);
+ return -ENODEV;
+ }
+ tw32(cpu_base + CPU_STATE, 0xffffffff);
+ tw32_f(cpu_base + CPU_MODE, 0x00000000);
+ return 0;
+}
+
+
/* tp->lock is held. */
static void __tg3_set_mac_addr(struct tg3 *tp, int skip_mac_1)
{
@@ -2619,15 +3272,19 @@ static void tg3_enable_register_access(struct tg3 *tp)
static int tg3_power_up(struct tg3 *tp)
{
- tg3_enable_register_access(tp);
+ int err;
- pci_set_power_state(tp->pdev, PCI_D0);
+ tg3_enable_register_access(tp);
- /* Switch out of Vaux if it is a NIC */
- if (tg3_flag(tp, IS_NIC))
- tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl, 100);
+ err = pci_set_power_state(tp->pdev, PCI_D0);
+ if (!err) {
+ /* Switch out of Vaux if it is a NIC */
+ tg3_pwrsrc_switch_to_vmain(tp);
+ } else {
+ netdev_err(tp->dev, "Transition to D0 failed\n");
+ }
- return 0;
+ return err;
}
static int tg3_power_down_prepare(struct tg3 *tp)
@@ -2642,11 +3299,11 @@ static int tg3_power_down_prepare(struct tg3 *tp)
u16 lnkctl;
pci_read_config_word(tp->pdev,
- tp->pcie_cap + PCI_EXP_LNKCTL,
+ pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
&lnkctl);
lnkctl |= PCI_EXP_LNKCTL_CLKREQ_EN;
pci_write_config_word(tp->pdev,
- tp->pcie_cap + PCI_EXP_LNKCTL,
+ pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
lnkctl);
}
@@ -2852,7 +3509,7 @@ static int tg3_power_down_prepare(struct tg3 *tp)
if (!(device_should_wake) && !tg3_flag(tp, ENABLE_ASF))
tg3_power_down_phy(tp, do_low_power);
- tg3_frob_aux_power(tp);
+ tg3_frob_aux_power(tp, true);
/* Workaround for unstable PLL clock */
if ((GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_AX) ||
@@ -2957,16 +3614,15 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl)
new_adv = 0;
if (advertise & ADVERTISED_1000baseT_Half)
- new_adv |= MII_TG3_CTRL_ADV_1000_HALF;
+ new_adv |= ADVERTISE_1000HALF;
if (advertise & ADVERTISED_1000baseT_Full)
- new_adv |= MII_TG3_CTRL_ADV_1000_FULL;
+ new_adv |= ADVERTISE_1000FULL;
if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 ||
tp->pci_chip_rev_id == CHIPREV_ID_5701_B0)
- new_adv |= (MII_TG3_CTRL_AS_MASTER |
- MII_TG3_CTRL_ENABLE_AS_MASTER);
+ new_adv |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER;
- err = tg3_writephy(tp, MII_TG3_CTRL, new_adv);
+ err = tg3_writephy(tp, MII_CTRL1000, new_adv);
if (err)
goto done;
@@ -2980,20 +3636,6 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl)
if (!err) {
u32 err2;
- switch (GET_ASIC_REV(tp->pci_chip_rev_id)) {
- case ASIC_REV_5717:
- case ASIC_REV_57765:
- if (!tg3_phydsp_read(tp, MII_TG3_DSP_CH34TP2, &val))
- tg3_phydsp_write(tp, MII_TG3_DSP_CH34TP2, val |
- MII_TG3_DSP_CH34TP2_HIBW01);
- /* Fall through */
- case ASIC_REV_5719:
- val = MII_TG3_DSP_TAP26_ALNOKO |
- MII_TG3_DSP_TAP26_RMRXSTO |
- MII_TG3_DSP_TAP26_OPCSINPT;
- tg3_phydsp_write(tp, MII_TG3_DSP_TAP26, val);
- }
-
val = 0;
/* Advertise 100-BaseTX EEE ability */
if (advertise & ADVERTISED_100baseT_Full)
@@ -3002,6 +3644,25 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl)
if (advertise & ADVERTISED_1000baseT_Full)
val |= MDIO_AN_EEE_ADV_1000T;
err = tg3_phy_cl45_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val);
+ if (err)
+ val = 0;
+
+ switch (GET_ASIC_REV(tp->pci_chip_rev_id)) {
+ case ASIC_REV_5717:
+ case ASIC_REV_57765:
+ case ASIC_REV_5719:
+ /* If we advertised any eee advertisements above... */
+ if (val)
+ val = MII_TG3_DSP_TAP26_ALNOKO |
+ MII_TG3_DSP_TAP26_RMRXSTO |
+ MII_TG3_DSP_TAP26_OPCSINPT;
+ tg3_phydsp_write(tp, MII_TG3_DSP_TAP26, val);
+ /* Fall through */
+ case ASIC_REV_5720:
+ if (!tg3_phydsp_read(tp, MII_TG3_DSP_CH34TP2, &val))
+ tg3_phydsp_write(tp, MII_TG3_DSP_CH34TP2, val |
+ MII_TG3_DSP_CH34TP2_HIBW01);
+ }
err2 = TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
if (!err)
@@ -3075,7 +3736,7 @@ static void tg3_phy_copper_begin(struct tg3 *tp)
break;
case SPEED_1000:
- bmcr |= TG3_BMCR_SPEED1000;
+ bmcr |= BMCR_SPEED1000;
break;
}
@@ -3141,8 +3802,9 @@ static int tg3_copper_is_advertising_all(struct tg3 *tp, u32 mask)
if (tg3_readphy(tp, MII_ADVERTISE, &adv_reg))
return 0;
- if ((adv_reg & all_mask) != all_mask)
+ if ((adv_reg & ADVERTISE_ALL) != all_mask)
return 0;
+
if (!(tp->phy_flags & TG3_PHYFLG_10_100_ONLY)) {
u32 tg3_ctrl;
@@ -3152,12 +3814,14 @@ static int tg3_copper_is_advertising_all(struct tg3 *tp, u32 mask)
if (mask & ADVERTISED_1000baseT_Full)
all_mask |= ADVERTISE_1000FULL;
- if (tg3_readphy(tp, MII_TG3_CTRL, &tg3_ctrl))
+ if (tg3_readphy(tp, MII_CTRL1000, &tg3_ctrl))
return 0;
- if ((tg3_ctrl & all_mask) != all_mask)
+ tg3_ctrl &= (ADVERTISE_1000HALF | ADVERTISE_1000FULL);
+ if (tg3_ctrl != all_mask)
return 0;
}
+
return 1;
}
@@ -3449,7 +4113,7 @@ relink:
u16 oldlnkctl, newlnkctl;
pci_read_config_word(tp->pdev,
- tp->pcie_cap + PCI_EXP_LNKCTL,
+ pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
&oldlnkctl);
if (tp->link_config.active_speed == SPEED_100 ||
tp->link_config.active_speed == SPEED_10)
@@ -3458,8 +4122,8 @@ relink:
newlnkctl = oldlnkctl | PCI_EXP_LNKCTL_CLKREQ_EN;
if (newlnkctl != oldlnkctl)
pci_write_config_word(tp->pdev,
- tp->pcie_cap + PCI_EXP_LNKCTL,
- newlnkctl);
+ pci_pcie_cap(tp->pdev) +
+ PCI_EXP_LNKCTL, newlnkctl);
}
if (current_link_up != netif_carrier_ok(tp->dev)) {
@@ -4663,7 +5327,7 @@ static void tg3_tx(struct tg3_napi *tnapi)
txq = netdev_get_tx_queue(tp->dev, index);
while (sw_idx != hw_idx) {
- struct ring_info *ri = &tnapi->tx_buffers[sw_idx];
+ struct tg3_tx_ring_info *ri = &tnapi->tx_buffers[sw_idx];
struct sk_buff *skb = ri->skb;
int i, tx_bug = 0;
@@ -4679,6 +5343,12 @@ static void tg3_tx(struct tg3_napi *tnapi)
ri->skb = NULL;
+ while (ri->fragmented) {
+ ri->fragmented = false;
+ sw_idx = NEXT_TX(sw_idx);
+ ri = &tnapi->tx_buffers[sw_idx];
+ }
+
sw_idx = NEXT_TX(sw_idx);
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
@@ -4688,8 +5358,15 @@ static void tg3_tx(struct tg3_napi *tnapi)
pci_unmap_page(tp->pdev,
dma_unmap_addr(ri, mapping),
- skb_shinfo(skb)->frags[i].size,
+ skb_frag_size(&skb_shinfo(skb)->frags[i]),
PCI_DMA_TODEVICE);
+
+ while (ri->fragmented) {
+ ri->fragmented = false;
+ sw_idx = NEXT_TX(sw_idx);
+ ri = &tnapi->tx_buffers[sw_idx];
+ }
+
sw_idx = NEXT_TX(sw_idx);
}
@@ -4776,11 +5453,11 @@ static int tg3_alloc_rx_skb(struct tg3 *tp, struct tg3_rx_prodring_set *tpr,
* Callers depend upon this behavior and assume that
* we leave everything unchanged if we fail.
*/
- skb = netdev_alloc_skb(tp->dev, skb_size + tp->rx_offset);
+ skb = netdev_alloc_skb(tp->dev, skb_size + TG3_RX_OFFSET(tp));
if (skb == NULL)
return -ENOMEM;
- skb_reserve(skb, tp->rx_offset);
+ skb_reserve(skb, TG3_RX_OFFSET(tp));
mapping = pci_map_single(tp->pdev, skb->data, skb_size,
PCI_DMA_FROMDEVICE);
@@ -5252,6 +5929,18 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget)
return work_done;
}
+static inline void tg3_reset_task_schedule(struct tg3 *tp)
+{
+ if (!test_and_set_bit(TG3_FLAG_RESET_TASK_PENDING, tp->tg3_flags))
+ schedule_work(&tp->reset_task);
+}
+
+static inline void tg3_reset_task_cancel(struct tg3 *tp)
+{
+ cancel_work_sync(&tp->reset_task);
+ tg3_flag_clear(tp, RESET_TASK_PENDING);
+}
+
static int tg3_poll_msix(struct napi_struct *napi, int budget)
{
struct tg3_napi *tnapi = container_of(napi, struct tg3_napi, napi);
@@ -5292,7 +5981,7 @@ static int tg3_poll_msix(struct napi_struct *napi, int budget)
tx_recovery:
/* work_done is guaranteed to be less than budget. */
napi_complete(napi);
- schedule_work(&tp->reset_task);
+ tg3_reset_task_schedule(tp);
return work_done;
}
@@ -5327,7 +6016,7 @@ static void tg3_process_error(struct tg3 *tp)
tg3_dump_state(tp);
tg3_flag_set(tp, ERROR_PROCESSED);
- schedule_work(&tp->reset_task);
+ tg3_reset_task_schedule(tp);
}
static int tg3_poll(struct napi_struct *napi, int budget)
@@ -5374,7 +6063,7 @@ static int tg3_poll(struct napi_struct *napi, int budget)
tx_recovery:
/* work_done is guaranteed to be less than budget. */
napi_complete(napi);
- schedule_work(&tp->reset_task);
+ tg3_reset_task_schedule(tp);
return work_done;
}
@@ -5498,7 +6187,7 @@ static irqreturn_t tg3_msi(int irq, void *dev_id)
* NIC to stop sending us irqs, engaging "in-intr-handler"
* event coalescing.
*/
- tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001);
+ tw32_mailbox(tnapi->int_mbox, 0x00000001);
if (likely(!tg3_irq_sync(tp)))
napi_schedule(&tnapi->napi);
@@ -5663,11 +6352,11 @@ static void tg3_reset_task(struct work_struct *work)
{
struct tg3 *tp = container_of(work, struct tg3, reset_task);
int err;
- unsigned int restart_timer;
tg3_full_lock(tp, 0);
if (!netif_running(tp->dev)) {
+ tg3_flag_clear(tp, RESET_TASK_PENDING);
tg3_full_unlock(tp);
return;
}
@@ -5680,9 +6369,6 @@ static void tg3_reset_task(struct work_struct *work)
tg3_full_lock(tp, 1);
- restart_timer = tg3_flag(tp, RESTART_TIMER);
- tg3_flag_clear(tp, RESTART_TIMER);
-
if (tg3_flag(tp, TX_RECOVERY_PENDING)) {
tp->write32_tx_mbox = tg3_write32_tx_mbox;
tp->write32_rx_mbox = tg3_write_flush_reg32;
@@ -5697,14 +6383,13 @@ static void tg3_reset_task(struct work_struct *work)
tg3_netif_start(tp);
- if (restart_timer)
- mod_timer(&tp->timer, jiffies + 1);
-
out:
tg3_full_unlock(tp);
if (!err)
tg3_phy_start(tp);
+
+ tg3_flag_clear(tp, RESET_TASK_PENDING);
}
static void tg3_tx_timeout(struct net_device *dev)
@@ -5716,7 +6401,7 @@ static void tg3_tx_timeout(struct net_device *dev)
tg3_dump_state(tp);
}
- schedule_work(&tp->reset_task);
+ tg3_reset_task_schedule(tp);
}
/* Test for DMA buffers crossing any 4GB boundaries: 4G, 8G, etc */
@@ -5740,61 +6425,123 @@ static inline int tg3_40bit_overflow_test(struct tg3 *tp, dma_addr_t mapping,
#endif
}
-static void tg3_set_txd(struct tg3_napi *tnapi, int entry,
- dma_addr_t mapping, int len, u32 flags,
- u32 mss_and_is_end)
+static inline void tg3_tx_set_bd(struct tg3_tx_buffer_desc *txbd,
+ dma_addr_t mapping, u32 len, u32 flags,
+ u32 mss, u32 vlan)
+{
+ txbd->addr_hi = ((u64) mapping >> 32);
+ txbd->addr_lo = ((u64) mapping & 0xffffffff);
+ txbd->len_flags = (len << TXD_LEN_SHIFT) | (flags & 0x0000ffff);
+ txbd->vlan_tag = (mss << TXD_MSS_SHIFT) | (vlan << TXD_VLAN_TAG_SHIFT);
+}
+
+static bool tg3_tx_frag_set(struct tg3_napi *tnapi, u32 *entry, u32 *budget,
+ dma_addr_t map, u32 len, u32 flags,
+ u32 mss, u32 vlan)
{
- struct tg3_tx_buffer_desc *txd = &tnapi->tx_ring[entry];
- int is_end = (mss_and_is_end & 0x1);
- u32 mss = (mss_and_is_end >> 1);
- u32 vlan_tag = 0;
+ struct tg3 *tp = tnapi->tp;
+ bool hwbug = false;
+
+ if (tg3_flag(tp, SHORT_DMA_BUG) && len <= 8)
+ hwbug = 1;
+
+ if (tg3_4g_overflow_test(map, len))
+ hwbug = 1;
+
+ if (tg3_40bit_overflow_test(tp, map, len))
+ hwbug = 1;
+
+ if (tg3_flag(tp, 4K_FIFO_LIMIT)) {
+ u32 prvidx = *entry;
+ u32 tmp_flag = flags & ~TXD_FLAG_END;
+ while (len > TG3_TX_BD_DMA_MAX && *budget) {
+ u32 frag_len = TG3_TX_BD_DMA_MAX;
+ len -= TG3_TX_BD_DMA_MAX;
+
+ /* Avoid the 8byte DMA problem */
+ if (len <= 8) {
+ len += TG3_TX_BD_DMA_MAX / 2;
+ frag_len = TG3_TX_BD_DMA_MAX / 2;
+ }
+
+ tnapi->tx_buffers[*entry].fragmented = true;
+
+ tg3_tx_set_bd(&tnapi->tx_ring[*entry], map,
+ frag_len, tmp_flag, mss, vlan);
+ *budget -= 1;
+ prvidx = *entry;
+ *entry = NEXT_TX(*entry);
- if (is_end)
- flags |= TXD_FLAG_END;
- if (flags & TXD_FLAG_VLAN) {
- vlan_tag = flags >> 16;
- flags &= 0xffff;
+ map += frag_len;
+ }
+
+ if (len) {
+ if (*budget) {
+ tg3_tx_set_bd(&tnapi->tx_ring[*entry], map,
+ len, flags, mss, vlan);
+ *budget -= 1;
+ *entry = NEXT_TX(*entry);
+ } else {
+ hwbug = 1;
+ tnapi->tx_buffers[prvidx].fragmented = false;
+ }
+ }
+ } else {
+ tg3_tx_set_bd(&tnapi->tx_ring[*entry], map,
+ len, flags, mss, vlan);
+ *entry = NEXT_TX(*entry);
}
- vlan_tag |= (mss << TXD_MSS_SHIFT);
- txd->addr_hi = ((u64) mapping >> 32);
- txd->addr_lo = ((u64) mapping & 0xffffffff);
- txd->len_flags = (len << TXD_LEN_SHIFT) | flags;
- txd->vlan_tag = vlan_tag << TXD_VLAN_TAG_SHIFT;
+ return hwbug;
}
-static void tg3_skb_error_unmap(struct tg3_napi *tnapi,
- struct sk_buff *skb, int last)
+static void tg3_tx_skb_unmap(struct tg3_napi *tnapi, u32 entry, int last)
{
int i;
- u32 entry = tnapi->tx_prod;
- struct ring_info *txb = &tnapi->tx_buffers[entry];
+ struct sk_buff *skb;
+ struct tg3_tx_ring_info *txb = &tnapi->tx_buffers[entry];
+
+ skb = txb->skb;
+ txb->skb = NULL;
pci_unmap_single(tnapi->tp->pdev,
dma_unmap_addr(txb, mapping),
skb_headlen(skb),
PCI_DMA_TODEVICE);
- for (i = 0; i < last; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ while (txb->fragmented) {
+ txb->fragmented = false;
+ entry = NEXT_TX(entry);
+ txb = &tnapi->tx_buffers[entry];
+ }
+
+ for (i = 0; i <= last; i++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
entry = NEXT_TX(entry);
txb = &tnapi->tx_buffers[entry];
pci_unmap_page(tnapi->tp->pdev,
dma_unmap_addr(txb, mapping),
- frag->size, PCI_DMA_TODEVICE);
+ skb_frag_size(frag), PCI_DMA_TODEVICE);
+
+ while (txb->fragmented) {
+ txb->fragmented = false;
+ entry = NEXT_TX(entry);
+ txb = &tnapi->tx_buffers[entry];
+ }
}
}
/* Workaround 4GB and 40-bit hardware DMA bugs. */
static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi,
- struct sk_buff *skb,
- u32 base_flags, u32 mss)
+ struct sk_buff **pskb,
+ u32 *entry, u32 *budget,
+ u32 base_flags, u32 mss, u32 vlan)
{
struct tg3 *tp = tnapi->tp;
- struct sk_buff *new_skb;
+ struct sk_buff *new_skb, *skb = *pskb;
dma_addr_t new_addr = 0;
- u32 entry = tnapi->tx_prod;
int ret = 0;
if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701)
@@ -5815,30 +6562,29 @@ static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi,
PCI_DMA_TODEVICE);
/* Make sure the mapping succeeded */
if (pci_dma_mapping_error(tp->pdev, new_addr)) {
- ret = -1;
dev_kfree_skb(new_skb);
-
- /* Make sure new skb does not cross any 4G boundaries.
- * Drop the packet if it does.
- */
- } else if (tg3_flag(tp, 4G_DMA_BNDRY_BUG) &&
- tg3_4g_overflow_test(new_addr, new_skb->len)) {
- pci_unmap_single(tp->pdev, new_addr, new_skb->len,
- PCI_DMA_TODEVICE);
ret = -1;
- dev_kfree_skb(new_skb);
} else {
- tnapi->tx_buffers[entry].skb = new_skb;
- dma_unmap_addr_set(&tnapi->tx_buffers[entry],
+ u32 save_entry = *entry;
+
+ base_flags |= TXD_FLAG_END;
+
+ tnapi->tx_buffers[*entry].skb = new_skb;
+ dma_unmap_addr_set(&tnapi->tx_buffers[*entry],
mapping, new_addr);
- tg3_set_txd(tnapi, entry, new_addr, new_skb->len,
- base_flags, 1 | (mss << 1));
+ if (tg3_tx_frag_set(tnapi, entry, budget, new_addr,
+ new_skb->len, base_flags,
+ mss, vlan)) {
+ tg3_tx_skb_unmap(tnapi, save_entry, -1);
+ dev_kfree_skb(new_skb);
+ ret = -1;
+ }
}
}
dev_kfree_skb(skb);
-
+ *pskb = new_skb;
return ret;
}
@@ -5891,7 +6637,8 @@ tg3_tso_bug_end:
static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct tg3 *tp = netdev_priv(dev);
- u32 len, entry, base_flags, mss;
+ u32 len, entry, base_flags, mss, vlan = 0;
+ u32 budget;
int i = -1, would_hit_hwbug;
dma_addr_t mapping;
struct tg3_napi *tnapi;
@@ -5903,12 +6650,14 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (tg3_flag(tp, ENABLE_TSS))
tnapi++;
+ budget = tg3_tx_avail(tnapi);
+
/* We are running in BH disabled context with netif_tx_lock
* and TX reclaim runs via tp->napi.poll inside of a software
* interrupt. Furthermore, IRQ processing runs lockless so we have
* no IRQ context deadlocks to worry about either. Rejoice!
*/
- if (unlikely(tg3_tx_avail(tnapi) <= (skb_shinfo(skb)->nr_frags + 1))) {
+ if (unlikely(budget <= (skb_shinfo(skb)->nr_frags + 1))) {
if (!netif_tx_queue_stopped(txq)) {
netif_tx_stop_queue(txq);
@@ -5930,10 +6679,8 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
u32 tcp_opt_len, hdr_len;
if (skb_header_cloned(skb) &&
- pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
- dev_kfree_skb(skb);
- goto out_unlock;
- }
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ goto drop;
iph = ip_hdr(skb);
tcp_opt_len = tcp_optlen(skb);
@@ -5993,101 +6740,83 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
- if (vlan_tx_tag_present(skb))
- base_flags |= (TXD_FLAG_VLAN |
- (vlan_tx_tag_get(skb) << 16));
-
if (tg3_flag(tp, USE_JUMBO_BDFLAG) &&
!mss && skb->len > VLAN_ETH_FRAME_LEN)
base_flags |= TXD_FLAG_JMB_PKT;
+ if (vlan_tx_tag_present(skb)) {
+ base_flags |= TXD_FLAG_VLAN;
+ vlan = vlan_tx_tag_get(skb);
+ }
+
len = skb_headlen(skb);
mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE);
- if (pci_dma_mapping_error(tp->pdev, mapping)) {
- dev_kfree_skb(skb);
- goto out_unlock;
- }
+ if (pci_dma_mapping_error(tp->pdev, mapping))
+ goto drop;
+
tnapi->tx_buffers[entry].skb = skb;
dma_unmap_addr_set(&tnapi->tx_buffers[entry], mapping, mapping);
would_hit_hwbug = 0;
- if (tg3_flag(tp, SHORT_DMA_BUG) && len <= 8)
- would_hit_hwbug = 1;
-
- if (tg3_flag(tp, 4G_DMA_BNDRY_BUG) &&
- tg3_4g_overflow_test(mapping, len))
- would_hit_hwbug = 1;
-
- if (tg3_flag(tp, 40BIT_DMA_LIMIT_BUG) &&
- tg3_40bit_overflow_test(tp, mapping, len))
- would_hit_hwbug = 1;
-
if (tg3_flag(tp, 5701_DMA_BUG))
would_hit_hwbug = 1;
- tg3_set_txd(tnapi, entry, mapping, len, base_flags,
- (skb_shinfo(skb)->nr_frags == 0) | (mss << 1));
+ if (tg3_tx_frag_set(tnapi, &entry, &budget, mapping, len, base_flags |
+ ((skb_shinfo(skb)->nr_frags == 0) ? TXD_FLAG_END : 0),
+ mss, vlan)) {
+ would_hit_hwbug = 1;
+ /* Now loop through additional data fragments, and queue them. */
+ } else if (skb_shinfo(skb)->nr_frags > 0) {
+ u32 tmp_mss = mss;
- entry = NEXT_TX(entry);
+ if (!tg3_flag(tp, HW_TSO_1) &&
+ !tg3_flag(tp, HW_TSO_2) &&
+ !tg3_flag(tp, HW_TSO_3))
+ tmp_mss = 0;
- /* Now loop through additional data fragments, and queue them. */
- if (skb_shinfo(skb)->nr_frags > 0) {
last = skb_shinfo(skb)->nr_frags - 1;
for (i = 0; i <= last; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- len = frag->size;
- mapping = pci_map_page(tp->pdev,
- frag->page,
- frag->page_offset,
- len, PCI_DMA_TODEVICE);
+ len = skb_frag_size(frag);
+ mapping = skb_frag_dma_map(&tp->pdev->dev, frag, 0,
+ len, DMA_TO_DEVICE);
tnapi->tx_buffers[entry].skb = NULL;
dma_unmap_addr_set(&tnapi->tx_buffers[entry], mapping,
mapping);
- if (pci_dma_mapping_error(tp->pdev, mapping))
+ if (dma_mapping_error(&tp->pdev->dev, mapping))
goto dma_error;
- if (tg3_flag(tp, SHORT_DMA_BUG) &&
- len <= 8)
- would_hit_hwbug = 1;
-
- if (tg3_flag(tp, 4G_DMA_BNDRY_BUG) &&
- tg3_4g_overflow_test(mapping, len))
- would_hit_hwbug = 1;
-
- if (tg3_flag(tp, 40BIT_DMA_LIMIT_BUG) &&
- tg3_40bit_overflow_test(tp, mapping, len))
+ if (!budget ||
+ tg3_tx_frag_set(tnapi, &entry, &budget, mapping,
+ len, base_flags |
+ ((i == last) ? TXD_FLAG_END : 0),
+ tmp_mss, vlan)) {
would_hit_hwbug = 1;
-
- if (tg3_flag(tp, HW_TSO_1) ||
- tg3_flag(tp, HW_TSO_2) ||
- tg3_flag(tp, HW_TSO_3))
- tg3_set_txd(tnapi, entry, mapping, len,
- base_flags, (i == last)|(mss << 1));
- else
- tg3_set_txd(tnapi, entry, mapping, len,
- base_flags, (i == last));
-
- entry = NEXT_TX(entry);
+ break;
+ }
}
}
if (would_hit_hwbug) {
- tg3_skb_error_unmap(tnapi, skb, i);
+ tg3_tx_skb_unmap(tnapi, tnapi->tx_prod, i);
/* If the workaround fails due to memory/mapping
* failure, silently drop this packet.
*/
- if (tigon3_dma_hwbug_workaround(tnapi, skb, base_flags, mss))
- goto out_unlock;
-
- entry = NEXT_TX(tnapi->tx_prod);
+ entry = tnapi->tx_prod;
+ budget = tg3_tx_avail(tnapi);
+ if (tigon3_dma_hwbug_workaround(tnapi, &skb, &entry, &budget,
+ base_flags, mss, vlan))
+ goto drop_nofree;
}
+ skb_tx_timestamp(skb);
+
/* Packets are ready, update Tx producer idx local and on card. */
tw32_tx_mbox(tnapi->prodmbox, entry);
@@ -6105,18 +6834,140 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
netif_tx_wake_queue(txq);
}
-out_unlock:
mmiowb();
-
return NETDEV_TX_OK;
dma_error:
- tg3_skb_error_unmap(tnapi, skb, i);
- dev_kfree_skb(skb);
+ tg3_tx_skb_unmap(tnapi, tnapi->tx_prod, --i);
tnapi->tx_buffers[tnapi->tx_prod].skb = NULL;
+drop:
+ dev_kfree_skb(skb);
+drop_nofree:
+ tp->tx_dropped++;
return NETDEV_TX_OK;
}
+static void tg3_mac_loopback(struct tg3 *tp, bool enable)
+{
+ if (enable) {
+ tp->mac_mode &= ~(MAC_MODE_HALF_DUPLEX |
+ MAC_MODE_PORT_MODE_MASK);
+
+ tp->mac_mode |= MAC_MODE_PORT_INT_LPBACK;
+
+ if (!tg3_flag(tp, 5705_PLUS))
+ tp->mac_mode |= MAC_MODE_LINK_POLARITY;
+
+ if (tp->phy_flags & TG3_PHYFLG_10_100_ONLY)
+ tp->mac_mode |= MAC_MODE_PORT_MODE_MII;
+ else
+ tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
+ } else {
+ tp->mac_mode &= ~MAC_MODE_PORT_INT_LPBACK;
+
+ if (tg3_flag(tp, 5705_PLUS) ||
+ (tp->phy_flags & TG3_PHYFLG_PHY_SERDES) ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700)
+ tp->mac_mode &= ~MAC_MODE_LINK_POLARITY;
+ }
+
+ tw32(MAC_MODE, tp->mac_mode);
+ udelay(40);
+}
+
+static int tg3_phy_lpbk_set(struct tg3 *tp, u32 speed, bool extlpbk)
+{
+ u32 val, bmcr, mac_mode, ptest = 0;
+
+ tg3_phy_toggle_apd(tp, false);
+ tg3_phy_toggle_automdix(tp, 0);
+
+ if (extlpbk && tg3_phy_set_extloopbk(tp))
+ return -EIO;
+
+ bmcr = BMCR_FULLDPLX;
+ switch (speed) {
+ case SPEED_10:
+ break;
+ case SPEED_100:
+ bmcr |= BMCR_SPEED100;
+ break;
+ case SPEED_1000:
+ default:
+ if (tp->phy_flags & TG3_PHYFLG_IS_FET) {
+ speed = SPEED_100;
+ bmcr |= BMCR_SPEED100;
+ } else {
+ speed = SPEED_1000;
+ bmcr |= BMCR_SPEED1000;
+ }
+ }
+
+ if (extlpbk) {
+ if (!(tp->phy_flags & TG3_PHYFLG_IS_FET)) {
+ tg3_readphy(tp, MII_CTRL1000, &val);
+ val |= CTL1000_AS_MASTER |
+ CTL1000_ENABLE_MASTER;
+ tg3_writephy(tp, MII_CTRL1000, val);
+ } else {
+ ptest = MII_TG3_FET_PTEST_TRIM_SEL |
+ MII_TG3_FET_PTEST_TRIM_2;
+ tg3_writephy(tp, MII_TG3_FET_PTEST, ptest);
+ }
+ } else
+ bmcr |= BMCR_LOOPBACK;
+
+ tg3_writephy(tp, MII_BMCR, bmcr);
+
+ /* The write needs to be flushed for the FETs */
+ if (tp->phy_flags & TG3_PHYFLG_IS_FET)
+ tg3_readphy(tp, MII_BMCR, &bmcr);
+
+ udelay(40);
+
+ if ((tp->phy_flags & TG3_PHYFLG_IS_FET) &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) {
+ tg3_writephy(tp, MII_TG3_FET_PTEST, ptest |
+ MII_TG3_FET_PTEST_FRC_TX_LINK |
+ MII_TG3_FET_PTEST_FRC_TX_LOCK);
+
+ /* The write needs to be flushed for the AC131 */
+ tg3_readphy(tp, MII_TG3_FET_PTEST, &val);
+ }
+
+ /* Reset to prevent losing 1st rx packet intermittently */
+ if ((tp->phy_flags & TG3_PHYFLG_MII_SERDES) &&
+ tg3_flag(tp, 5780_CLASS)) {
+ tw32_f(MAC_RX_MODE, RX_MODE_RESET);
+ udelay(10);
+ tw32_f(MAC_RX_MODE, tp->rx_mode);
+ }
+
+ mac_mode = tp->mac_mode &
+ ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX);
+ if (speed == SPEED_1000)
+ mac_mode |= MAC_MODE_PORT_MODE_GMII;
+ else
+ mac_mode |= MAC_MODE_PORT_MODE_MII;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) {
+ u32 masked_phy_id = tp->phy_id & TG3_PHY_ID_MASK;
+
+ if (masked_phy_id == TG3_PHY_ID_BCM5401)
+ mac_mode &= ~MAC_MODE_LINK_POLARITY;
+ else if (masked_phy_id == TG3_PHY_ID_BCM5411)
+ mac_mode |= MAC_MODE_LINK_POLARITY;
+
+ tg3_writephy(tp, MII_TG3_EXT_CTRL,
+ MII_TG3_EXT_CTRL_LNK3_LED_MODE);
+ }
+
+ tw32(MAC_MODE, mac_mode);
+ udelay(40);
+
+ return 0;
+}
+
static void tg3_set_loopback(struct net_device *dev, u32 features)
{
struct tg3 *tp = netdev_priv(dev);
@@ -6125,16 +6976,8 @@ static void tg3_set_loopback(struct net_device *dev, u32 features)
if (tp->mac_mode & MAC_MODE_PORT_INT_LPBACK)
return;
- /*
- * Clear MAC_MODE_HALF_DUPLEX or you won't get packets back in
- * loopback mode if Half-Duplex mode was negotiated earlier.
- */
- tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX;
-
- /* Enable internal MAC loopback mode */
- tp->mac_mode |= MAC_MODE_PORT_INT_LPBACK;
spin_lock_bh(&tp->lock);
- tw32(MAC_MODE, tp->mac_mode);
+ tg3_mac_loopback(tp, true);
netif_carrier_on(tp->dev);
spin_unlock_bh(&tp->lock);
netdev_info(dev, "Internal MAC loopback mode enabled.\n");
@@ -6142,10 +6985,8 @@ static void tg3_set_loopback(struct net_device *dev, u32 features)
if (!(tp->mac_mode & MAC_MODE_PORT_INT_LPBACK))
return;
- /* Disable internal MAC loopback mode */
- tp->mac_mode &= ~MAC_MODE_PORT_INT_LPBACK;
spin_lock_bh(&tp->lock);
- tw32(MAC_MODE, tp->mac_mode);
+ tg3_mac_loopback(tp, false);
/* Force link status check */
tg3_setup_phy(tp, 1);
spin_unlock_bh(&tp->lock);
@@ -6444,35 +7285,14 @@ static void tg3_free_rings(struct tg3 *tp)
if (!tnapi->tx_buffers)
continue;
- for (i = 0; i < TG3_TX_RING_SIZE; ) {
- struct ring_info *txp;
- struct sk_buff *skb;
- unsigned int k;
-
- txp = &tnapi->tx_buffers[i];
- skb = txp->skb;
+ for (i = 0; i < TG3_TX_RING_SIZE; i++) {
+ struct sk_buff *skb = tnapi->tx_buffers[i].skb;
- if (skb == NULL) {
- i++;
+ if (!skb)
continue;
- }
-
- pci_unmap_single(tp->pdev,
- dma_unmap_addr(txp, mapping),
- skb_headlen(skb),
- PCI_DMA_TODEVICE);
- txp->skb = NULL;
- i++;
-
- for (k = 0; k < skb_shinfo(skb)->nr_frags; k++) {
- txp = &tnapi->tx_buffers[i & (TG3_TX_RING_SIZE - 1)];
- pci_unmap_page(tp->pdev,
- dma_unmap_addr(txp, mapping),
- skb_shinfo(skb)->frags[k].size,
- PCI_DMA_TODEVICE);
- i++;
- }
+ tg3_tx_skb_unmap(tnapi, i,
+ skb_shinfo(skb)->nr_frags - 1);
dev_kfree_skb_any(skb);
}
@@ -6604,9 +7424,9 @@ static int tg3_alloc_consistent(struct tg3 *tp)
*/
if ((!i && !tg3_flag(tp, ENABLE_TSS)) ||
(i && tg3_flag(tp, ENABLE_TSS))) {
- tnapi->tx_buffers = kzalloc(sizeof(struct ring_info) *
- TG3_TX_RING_SIZE,
- GFP_KERNEL);
+ tnapi->tx_buffers = kzalloc(
+ sizeof(struct tg3_tx_ring_info) *
+ TG3_TX_RING_SIZE, GFP_KERNEL);
if (!tnapi->tx_buffers)
goto err_out;
@@ -6777,230 +7597,6 @@ static int tg3_abort_hw(struct tg3 *tp, int silent)
return err;
}
-static void tg3_ape_send_event(struct tg3 *tp, u32 event)
-{
- int i;
- u32 apedata;
-
- /* NCSI does not support APE events */
- if (tg3_flag(tp, APE_HAS_NCSI))
- return;
-
- apedata = tg3_ape_read32(tp, TG3_APE_SEG_SIG);
- if (apedata != APE_SEG_SIG_MAGIC)
- return;
-
- apedata = tg3_ape_read32(tp, TG3_APE_FW_STATUS);
- if (!(apedata & APE_FW_STATUS_READY))
- return;
-
- /* Wait for up to 1 millisecond for APE to service previous event. */
- for (i = 0; i < 10; i++) {
- if (tg3_ape_lock(tp, TG3_APE_LOCK_MEM))
- return;
-
- apedata = tg3_ape_read32(tp, TG3_APE_EVENT_STATUS);
-
- if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
- tg3_ape_write32(tp, TG3_APE_EVENT_STATUS,
- event | APE_EVENT_STATUS_EVENT_PENDING);
-
- tg3_ape_unlock(tp, TG3_APE_LOCK_MEM);
-
- if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
- break;
-
- udelay(100);
- }
-
- if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
- tg3_ape_write32(tp, TG3_APE_EVENT, APE_EVENT_1);
-}
-
-static void tg3_ape_driver_state_change(struct tg3 *tp, int kind)
-{
- u32 event;
- u32 apedata;
-
- if (!tg3_flag(tp, ENABLE_APE))
- return;
-
- switch (kind) {
- case RESET_KIND_INIT:
- tg3_ape_write32(tp, TG3_APE_HOST_SEG_SIG,
- APE_HOST_SEG_SIG_MAGIC);
- tg3_ape_write32(tp, TG3_APE_HOST_SEG_LEN,
- APE_HOST_SEG_LEN_MAGIC);
- apedata = tg3_ape_read32(tp, TG3_APE_HOST_INIT_COUNT);
- tg3_ape_write32(tp, TG3_APE_HOST_INIT_COUNT, ++apedata);
- tg3_ape_write32(tp, TG3_APE_HOST_DRIVER_ID,
- APE_HOST_DRIVER_ID_MAGIC(TG3_MAJ_NUM, TG3_MIN_NUM));
- tg3_ape_write32(tp, TG3_APE_HOST_BEHAVIOR,
- APE_HOST_BEHAV_NO_PHYLOCK);
- tg3_ape_write32(tp, TG3_APE_HOST_DRVR_STATE,
- TG3_APE_HOST_DRVR_STATE_START);
-
- event = APE_EVENT_STATUS_STATE_START;
- break;
- case RESET_KIND_SHUTDOWN:
- /* With the interface we are currently using,
- * APE does not track driver state. Wiping
- * out the HOST SEGMENT SIGNATURE forces
- * the APE to assume OS absent status.
- */
- tg3_ape_write32(tp, TG3_APE_HOST_SEG_SIG, 0x0);
-
- if (device_may_wakeup(&tp->pdev->dev) &&
- tg3_flag(tp, WOL_ENABLE)) {
- tg3_ape_write32(tp, TG3_APE_HOST_WOL_SPEED,
- TG3_APE_HOST_WOL_SPEED_AUTO);
- apedata = TG3_APE_HOST_DRVR_STATE_WOL;
- } else
- apedata = TG3_APE_HOST_DRVR_STATE_UNLOAD;
-
- tg3_ape_write32(tp, TG3_APE_HOST_DRVR_STATE, apedata);
-
- event = APE_EVENT_STATUS_STATE_UNLOAD;
- break;
- case RESET_KIND_SUSPEND:
- event = APE_EVENT_STATUS_STATE_SUSPEND;
- break;
- default:
- return;
- }
-
- event |= APE_EVENT_STATUS_DRIVER_EVNT | APE_EVENT_STATUS_STATE_CHNGE;
-
- tg3_ape_send_event(tp, event);
-}
-
-/* tp->lock is held. */
-static void tg3_write_sig_pre_reset(struct tg3 *tp, int kind)
-{
- tg3_write_mem(tp, NIC_SRAM_FIRMWARE_MBOX,
- NIC_SRAM_FIRMWARE_MBOX_MAGIC1);
-
- if (tg3_flag(tp, ASF_NEW_HANDSHAKE)) {
- switch (kind) {
- case RESET_KIND_INIT:
- tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
- DRV_STATE_START);
- break;
-
- case RESET_KIND_SHUTDOWN:
- tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
- DRV_STATE_UNLOAD);
- break;
-
- case RESET_KIND_SUSPEND:
- tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
- DRV_STATE_SUSPEND);
- break;
-
- default:
- break;
- }
- }
-
- if (kind == RESET_KIND_INIT ||
- kind == RESET_KIND_SUSPEND)
- tg3_ape_driver_state_change(tp, kind);
-}
-
-/* tp->lock is held. */
-static void tg3_write_sig_post_reset(struct tg3 *tp, int kind)
-{
- if (tg3_flag(tp, ASF_NEW_HANDSHAKE)) {
- switch (kind) {
- case RESET_KIND_INIT:
- tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
- DRV_STATE_START_DONE);
- break;
-
- case RESET_KIND_SHUTDOWN:
- tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
- DRV_STATE_UNLOAD_DONE);
- break;
-
- default:
- break;
- }
- }
-
- if (kind == RESET_KIND_SHUTDOWN)
- tg3_ape_driver_state_change(tp, kind);
-}
-
-/* tp->lock is held. */
-static void tg3_write_sig_legacy(struct tg3 *tp, int kind)
-{
- if (tg3_flag(tp, ENABLE_ASF)) {
- switch (kind) {
- case RESET_KIND_INIT:
- tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
- DRV_STATE_START);
- break;
-
- case RESET_KIND_SHUTDOWN:
- tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
- DRV_STATE_UNLOAD);
- break;
-
- case RESET_KIND_SUSPEND:
- tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,
- DRV_STATE_SUSPEND);
- break;
-
- default:
- break;
- }
- }
-}
-
-static int tg3_poll_fw(struct tg3 *tp)
-{
- int i;
- u32 val;
-
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
- /* Wait up to 20ms for init done. */
- for (i = 0; i < 200; i++) {
- if (tr32(VCPU_STATUS) & VCPU_STATUS_INIT_DONE)
- return 0;
- udelay(100);
- }
- return -ENODEV;
- }
-
- /* Wait for firmware initialization to complete. */
- for (i = 0; i < 100000; i++) {
- tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val);
- if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1)
- break;
- udelay(10);
- }
-
- /* Chip might not be fitted with firmware. Some Sun onboard
- * parts are configured like that. So don't signal the timeout
- * of the above loop as an error, but do report the lack of
- * running firmware once.
- */
- if (i >= 100000 && !tg3_flag(tp, NO_FWARE_REPORTED)) {
- tg3_flag_set(tp, NO_FWARE_REPORTED);
-
- netdev_info(tp->dev, "No firmware running\n");
- }
-
- if (tp->pci_chip_rev_id == CHIPREV_ID_57765_A0) {
- /* The 57765 A0 needs a little more
- * time to do some important work.
- */
- mdelay(10);
- }
-
- return 0;
-}
-
/* Save PCI command register before chip reset */
static void tg3_save_pci_state(struct tg3 *tp)
{
@@ -7072,8 +7668,6 @@ static void tg3_restore_pci_state(struct tg3 *tp)
}
}
-static void tg3_stop_fw(struct tg3 *);
-
/* tp->lock is held. */
static int tg3_chip_reset(struct tg3 *tp)
{
@@ -7193,7 +7787,7 @@ static int tg3_chip_reset(struct tg3 *tp)
udelay(120);
- if (tg3_flag(tp, PCI_EXPRESS) && tp->pcie_cap) {
+ if (tg3_flag(tp, PCI_EXPRESS) && pci_pcie_cap(tp->pdev)) {
u16 val16;
if (tp->pci_chip_rev_id == CHIPREV_ID_5750_A0) {
@@ -7211,7 +7805,7 @@ static int tg3_chip_reset(struct tg3 *tp)
/* Clear the "no snoop" and "relaxed ordering" bits. */
pci_read_config_word(tp->pdev,
- tp->pcie_cap + PCI_EXP_DEVCTL,
+ pci_pcie_cap(tp->pdev) + PCI_EXP_DEVCTL,
&val16);
val16 &= ~(PCI_EXP_DEVCTL_RELAX_EN |
PCI_EXP_DEVCTL_NOSNOOP_EN);
@@ -7222,14 +7816,14 @@ static int tg3_chip_reset(struct tg3 *tp)
if (!tg3_flag(tp, CPMU_PRESENT))
val16 &= ~PCI_EXP_DEVCTL_PAYLOAD;
pci_write_config_word(tp->pdev,
- tp->pcie_cap + PCI_EXP_DEVCTL,
+ pci_pcie_cap(tp->pdev) + PCI_EXP_DEVCTL,
val16);
pcie_set_readrq(tp->pdev, tp->pcie_readrq);
/* Clear error status */
pci_write_config_word(tp->pdev,
- tp->pcie_cap + PCI_EXP_DEVSTA,
+ pci_pcie_cap(tp->pdev) + PCI_EXP_DEVSTA,
PCI_EXP_DEVSTA_CED |
PCI_EXP_DEVSTA_NFED |
PCI_EXP_DEVSTA_FED |
@@ -7267,16 +7861,11 @@ static int tg3_chip_reset(struct tg3 *tp)
tw32(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl);
}
- if (tg3_flag(tp, ENABLE_APE))
- tp->mac_mode = MAC_MODE_APE_TX_EN |
- MAC_MODE_APE_RX_EN |
- MAC_MODE_TDE_ENABLE;
-
if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES) {
- tp->mac_mode |= MAC_MODE_PORT_MODE_TBI;
+ tp->mac_mode = MAC_MODE_PORT_MODE_TBI;
val = tp->mac_mode;
} else if (tp->phy_flags & TG3_PHYFLG_MII_SERDES) {
- tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
+ tp->mac_mode = MAC_MODE_PORT_MODE_GMII;
val = tp->mac_mode;
} else
val = 0;
@@ -7326,22 +7915,6 @@ static int tg3_chip_reset(struct tg3 *tp)
}
/* tp->lock is held. */
-static void tg3_stop_fw(struct tg3 *tp)
-{
- if (tg3_flag(tp, ENABLE_ASF) && !tg3_flag(tp, ENABLE_APE)) {
- /* Wait for RX cpu to ACK the previous event. */
- tg3_wait_for_event_ack(tp);
-
- tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_PAUSE_FW);
-
- tg3_generate_fw_event(tp);
-
- /* Wait for RX cpu to ACK this event. */
- tg3_wait_for_event_ack(tp);
- }
-}
-
-/* tp->lock is held. */
static int tg3_halt(struct tg3 *tp, int kind, int silent)
{
int err;
@@ -7364,227 +7937,6 @@ static int tg3_halt(struct tg3 *tp, int kind, int silent)
return 0;
}
-#define RX_CPU_SCRATCH_BASE 0x30000
-#define RX_CPU_SCRATCH_SIZE 0x04000
-#define TX_CPU_SCRATCH_BASE 0x34000
-#define TX_CPU_SCRATCH_SIZE 0x04000
-
-/* tp->lock is held. */
-static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
-{
- int i;
-
- BUG_ON(offset == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS));
-
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
- u32 val = tr32(GRC_VCPU_EXT_CTRL);
-
- tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_HALT_CPU);
- return 0;
- }
- if (offset == RX_CPU_BASE) {
- for (i = 0; i < 10000; i++) {
- tw32(offset + CPU_STATE, 0xffffffff);
- tw32(offset + CPU_MODE, CPU_MODE_HALT);
- if (tr32(offset + CPU_MODE) & CPU_MODE_HALT)
- break;
- }
-
- tw32(offset + CPU_STATE, 0xffffffff);
- tw32_f(offset + CPU_MODE, CPU_MODE_HALT);
- udelay(10);
- } else {
- for (i = 0; i < 10000; i++) {
- tw32(offset + CPU_STATE, 0xffffffff);
- tw32(offset + CPU_MODE, CPU_MODE_HALT);
- if (tr32(offset + CPU_MODE) & CPU_MODE_HALT)
- break;
- }
- }
-
- if (i >= 10000) {
- netdev_err(tp->dev, "%s timed out, %s CPU\n",
- __func__, offset == RX_CPU_BASE ? "RX" : "TX");
- return -ENODEV;
- }
-
- /* Clear firmware's nvram arbitration. */
- if (tg3_flag(tp, NVRAM))
- tw32(NVRAM_SWARB, SWARB_REQ_CLR0);
- return 0;
-}
-
-struct fw_info {
- unsigned int fw_base;
- unsigned int fw_len;
- const __be32 *fw_data;
-};
-
-/* tp->lock is held. */
-static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base, u32 cpu_scratch_base,
- int cpu_scratch_size, struct fw_info *info)
-{
- int err, lock_err, i;
- void (*write_op)(struct tg3 *, u32, u32);
-
- if (cpu_base == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS)) {
- netdev_err(tp->dev,
- "%s: Trying to load TX cpu firmware which is 5705\n",
- __func__);
- return -EINVAL;
- }
-
- if (tg3_flag(tp, 5705_PLUS))
- write_op = tg3_write_mem;
- else
- write_op = tg3_write_indirect_reg32;
-
- /* It is possible that bootcode is still loading at this point.
- * Get the nvram lock first before halting the cpu.
- */
- lock_err = tg3_nvram_lock(tp);
- err = tg3_halt_cpu(tp, cpu_base);
- if (!lock_err)
- tg3_nvram_unlock(tp);
- if (err)
- goto out;
-
- for (i = 0; i < cpu_scratch_size; i += sizeof(u32))
- write_op(tp, cpu_scratch_base + i, 0);
- tw32(cpu_base + CPU_STATE, 0xffffffff);
- tw32(cpu_base + CPU_MODE, tr32(cpu_base+CPU_MODE)|CPU_MODE_HALT);
- for (i = 0; i < (info->fw_len / sizeof(u32)); i++)
- write_op(tp, (cpu_scratch_base +
- (info->fw_base & 0xffff) +
- (i * sizeof(u32))),
- be32_to_cpu(info->fw_data[i]));
-
- err = 0;
-
-out:
- return err;
-}
-
-/* tp->lock is held. */
-static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp)
-{
- struct fw_info info;
- const __be32 *fw_data;
- int err, i;
-
- fw_data = (void *)tp->fw->data;
-
- /* Firmware blob starts with version numbers, followed by
- start address and length. We are setting complete length.
- length = end_address_of_bss - start_address_of_text.
- Remainder is the blob to be loaded contiguously
- from start address. */
-
- info.fw_base = be32_to_cpu(fw_data[1]);
- info.fw_len = tp->fw->size - 12;
- info.fw_data = &fw_data[3];
-
- err = tg3_load_firmware_cpu(tp, RX_CPU_BASE,
- RX_CPU_SCRATCH_BASE, RX_CPU_SCRATCH_SIZE,
- &info);
- if (err)
- return err;
-
- err = tg3_load_firmware_cpu(tp, TX_CPU_BASE,
- TX_CPU_SCRATCH_BASE, TX_CPU_SCRATCH_SIZE,
- &info);
- if (err)
- return err;
-
- /* Now startup only the RX cpu. */
- tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
- tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base);
-
- for (i = 0; i < 5; i++) {
- if (tr32(RX_CPU_BASE + CPU_PC) == info.fw_base)
- break;
- tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
- tw32(RX_CPU_BASE + CPU_MODE, CPU_MODE_HALT);
- tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base);
- udelay(1000);
- }
- if (i >= 5) {
- netdev_err(tp->dev, "%s fails to set RX CPU PC, is %08x "
- "should be %08x\n", __func__,
- tr32(RX_CPU_BASE + CPU_PC), info.fw_base);
- return -ENODEV;
- }
- tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
- tw32_f(RX_CPU_BASE + CPU_MODE, 0x00000000);
-
- return 0;
-}
-
-/* tp->lock is held. */
-static int tg3_load_tso_firmware(struct tg3 *tp)
-{
- struct fw_info info;
- const __be32 *fw_data;
- unsigned long cpu_base, cpu_scratch_base, cpu_scratch_size;
- int err, i;
-
- if (tg3_flag(tp, HW_TSO_1) ||
- tg3_flag(tp, HW_TSO_2) ||
- tg3_flag(tp, HW_TSO_3))
- return 0;
-
- fw_data = (void *)tp->fw->data;
-
- /* Firmware blob starts with version numbers, followed by
- start address and length. We are setting complete length.
- length = end_address_of_bss - start_address_of_text.
- Remainder is the blob to be loaded contiguously
- from start address. */
-
- info.fw_base = be32_to_cpu(fw_data[1]);
- cpu_scratch_size = tp->fw_len;
- info.fw_len = tp->fw->size - 12;
- info.fw_data = &fw_data[3];
-
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
- cpu_base = RX_CPU_BASE;
- cpu_scratch_base = NIC_SRAM_MBUF_POOL_BASE5705;
- } else {
- cpu_base = TX_CPU_BASE;
- cpu_scratch_base = TX_CPU_SCRATCH_BASE;
- cpu_scratch_size = TX_CPU_SCRATCH_SIZE;
- }
-
- err = tg3_load_firmware_cpu(tp, cpu_base,
- cpu_scratch_base, cpu_scratch_size,
- &info);
- if (err)
- return err;
-
- /* Now startup the cpu. */
- tw32(cpu_base + CPU_STATE, 0xffffffff);
- tw32_f(cpu_base + CPU_PC, info.fw_base);
-
- for (i = 0; i < 5; i++) {
- if (tr32(cpu_base + CPU_PC) == info.fw_base)
- break;
- tw32(cpu_base + CPU_STATE, 0xffffffff);
- tw32(cpu_base + CPU_MODE, CPU_MODE_HALT);
- tw32_f(cpu_base + CPU_PC, info.fw_base);
- udelay(1000);
- }
- if (i >= 5) {
- netdev_err(tp->dev,
- "%s fails to set CPU PC, is %08x should be %08x\n",
- __func__, tr32(cpu_base + CPU_PC), info.fw_base);
- return -ENODEV;
- }
- tw32(cpu_base + CPU_STATE, 0xffffffff);
- tw32_f(cpu_base + CPU_MODE, 0x00000000);
- return 0;
-}
-
-
static int tg3_set_mac_addr(struct net_device *dev, void *p)
{
struct tg3 *tp = netdev_priv(dev);
@@ -7751,6 +8103,9 @@ static void tg3_rings_reset(struct tg3 *tp)
/* Disable interrupts */
tw32_mailbox_f(tp->napi[0].int_mbox, 1);
+ tp->napi[0].chk_msi_cnt = 0;
+ tp->napi[0].last_rx_cons = 0;
+ tp->napi[0].last_tx_cons = 0;
/* Zero mailbox registers. */
if (tg3_flag(tp, SUPPORT_MSIX)) {
@@ -7761,6 +8116,9 @@ static void tg3_rings_reset(struct tg3 *tp)
tw32_mailbox(tp->napi[i].prodmbox, 0);
tw32_rx_mbox(tp->napi[i].consmbox, 0);
tw32_mailbox_f(tp->napi[i].int_mbox, 1);
+ tp->napi[i].chk_msi_cnt = 0;
+ tp->napi[i].last_rx_cons = 0;
+ tp->napi[i].last_tx_cons = 0;
}
if (!tg3_flag(tp, ENABLE_TSS))
tw32_mailbox(tp->napi[0].prodmbox, 0);
@@ -8201,7 +8559,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
/* Program the jumbo buffer descriptor ring control
* blocks on those devices that have them.
*/
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5719_A0 ||
(tg3_flag(tp, JUMBO_CAPABLE) && !tg3_flag(tp, 5780_CLASS))) {
if (tg3_flag(tp, JUMBO_RING_ENABLE)) {
@@ -8408,12 +8766,11 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
udelay(10);
}
- if (tg3_flag(tp, ENABLE_APE))
- tp->mac_mode = MAC_MODE_APE_TX_EN | MAC_MODE_APE_RX_EN;
- else
- tp->mac_mode = 0;
tp->mac_mode |= MAC_MODE_TXSTAT_ENABLE | MAC_MODE_RXSTAT_ENABLE |
- MAC_MODE_TDE_ENABLE | MAC_MODE_RDE_ENABLE | MAC_MODE_FHDE_ENABLE;
+ MAC_MODE_TDE_ENABLE | MAC_MODE_RDE_ENABLE |
+ MAC_MODE_FHDE_ENABLE;
+ if (tg3_flag(tp, ENABLE_APE))
+ tp->mac_mode |= MAC_MODE_APE_TX_EN | MAC_MODE_APE_RX_EN;
if (!tg3_flag(tp, 5705_PLUS) &&
!(tp->phy_flags & TG3_PHYFLG_PHY_SERDES) &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700)
@@ -8455,6 +8812,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
if (tg3_flag(tp, USING_MSIX) && tp->irq_cnt > 1) {
val = tr32(MSGINT_MODE);
val |= MSGINT_MODE_MULTIVEC_EN | MSGINT_MODE_ENABLE;
+ if (!tg3_flag(tp, 1SHOT_MSI))
+ val |= MSGINT_MODE_ONE_SHOT_DISABLE;
tw32(MSGINT_MODE, val);
}
@@ -8565,15 +8924,24 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
udelay(100);
if (tg3_flag(tp, ENABLE_RSS)) {
+ int i = 0;
u32 reg = MAC_RSS_INDIR_TBL_0;
- u8 *ent = (u8 *)&val;
- /* Setup the indirection table */
- for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++) {
- int idx = i % sizeof(val);
+ if (tp->irq_cnt == 2) {
+ for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i += 8) {
+ tw32(reg, 0x0);
+ reg += 4;
+ }
+ } else {
+ u32 val;
- ent[idx] = i % (tp->irq_cnt - 1);
- if (idx == sizeof(val) - 1) {
+ while (i < TG3_RSS_INDIR_TBL_SIZE) {
+ val = i % (tp->irq_cnt - 1);
+ i++;
+ for (; i % 8; i++) {
+ val <<= 4;
+ val |= (i % (tp->irq_cnt - 1));
+ }
tw32(reg, val);
reg += 4;
}
@@ -8816,15 +9184,42 @@ static void tg3_periodic_fetch_stats(struct tg3 *tp)
TG3_STAT_ADD32(&sp->rx_errors, RCVLPC_IN_ERRORS_CNT);
}
+static void tg3_chk_missed_msi(struct tg3 *tp)
+{
+ u32 i;
+
+ for (i = 0; i < tp->irq_cnt; i++) {
+ struct tg3_napi *tnapi = &tp->napi[i];
+
+ if (tg3_has_work(tnapi)) {
+ if (tnapi->last_rx_cons == tnapi->rx_rcb_ptr &&
+ tnapi->last_tx_cons == tnapi->tx_cons) {
+ if (tnapi->chk_msi_cnt < 1) {
+ tnapi->chk_msi_cnt++;
+ return;
+ }
+ tg3_msi(0, tnapi);
+ }
+ }
+ tnapi->chk_msi_cnt = 0;
+ tnapi->last_rx_cons = tnapi->rx_rcb_ptr;
+ tnapi->last_tx_cons = tnapi->tx_cons;
+ }
+}
+
static void tg3_timer(unsigned long __opaque)
{
struct tg3 *tp = (struct tg3 *) __opaque;
- if (tp->irq_sync)
+ if (tp->irq_sync || tg3_flag(tp, RESET_TASK_PENDING))
goto restart_timer;
spin_lock(&tp->lock);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
+ tg3_chk_missed_msi(tp);
+
if (!tg3_flag(tp, TAGGED_STATUS)) {
/* All of this garbage is because when using non-tagged
* IRQ status the mailbox/status_block protocol the chip
@@ -8839,10 +9234,9 @@ static void tg3_timer(unsigned long __opaque)
}
if (!(tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) {
- tg3_flag_set(tp, RESTART_TIMER);
spin_unlock(&tp->lock);
- schedule_work(&tp->reset_task);
- return;
+ tg3_reset_task_schedule(tp);
+ goto restart_timer;
}
}
@@ -8988,7 +9382,7 @@ static int tg3_test_interrupt(struct tg3 *tp)
* Turn off MSI one shot mode. Otherwise this test has no
* observable way to know whether the interrupt was delivered.
*/
- if (tg3_flag(tp, 57765_PLUS) && tg3_flag(tp, USING_MSI)) {
+ if (tg3_flag(tp, 57765_PLUS)) {
val = tr32(MSGINT_MODE) | MSGINT_MODE_ONE_SHOT_DISABLE;
tw32(MSGINT_MODE, val);
}
@@ -9016,6 +9410,10 @@ static int tg3_test_interrupt(struct tg3 *tp)
break;
}
+ if (tg3_flag(tp, 57765_PLUS) &&
+ tnapi->hw_status->status_tag != tnapi->last_tag)
+ tw32_mailbox_f(tnapi->int_mbox, tnapi->last_tag << 24);
+
msleep(10);
}
@@ -9030,7 +9428,7 @@ static int tg3_test_interrupt(struct tg3 *tp)
if (intr_ok) {
/* Reenable MSI one shot mode. */
- if (tg3_flag(tp, 57765_PLUS) && tg3_flag(tp, USING_MSI)) {
+ if (tg3_flag(tp, 57765_PLUS) && tg3_flag(tp, 1SHOT_MSI)) {
val = tr32(MSGINT_MODE) & ~MSGINT_MODE_ONE_SHOT_DISABLE;
tw32(MSGINT_MODE, val);
}
@@ -9208,6 +9606,8 @@ static void tg3_ints_init(struct tg3 *tp)
u32 msi_mode = tr32(MSGINT_MODE);
if (tg3_flag(tp, USING_MSIX) && tp->irq_cnt > 1)
msi_mode |= MSGINT_MODE_MULTIVEC_EN;
+ if (!tg3_flag(tp, 1SHOT_MSI))
+ msi_mode |= MSGINT_MODE_ONE_SHOT_DISABLE;
tw32(MSGINT_MODE, msi_mode | MSGINT_MODE_ENABLE);
}
defcfg:
@@ -9284,15 +9684,14 @@ static int tg3_open(struct net_device *dev)
struct tg3_napi *tnapi = &tp->napi[i];
err = tg3_request_irq(tp, i);
if (err) {
- for (i--; i >= 0; i--)
+ for (i--; i >= 0; i--) {
+ tnapi = &tp->napi[i];
free_irq(tnapi->irq_vec, tnapi);
- break;
+ }
+ goto err_out2;
}
}
- if (err)
- goto err_out2;
-
tg3_full_lock(tp, 0);
err = tg3_init_hw(tp, 1);
@@ -9300,7 +9699,9 @@ static int tg3_open(struct net_device *dev)
tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
tg3_free_rings(tp);
} else {
- if (tg3_flag(tp, TAGGED_STATUS))
+ if (tg3_flag(tp, TAGGED_STATUS) &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57765)
tp->timer_offset = HZ;
else
tp->timer_offset = HZ / 10;
@@ -9376,6 +9777,8 @@ err_out2:
err_out1:
tg3_ints_fini(tp);
+ tg3_frob_aux_power(tp, false);
+ pci_set_power_state(tp->pdev, PCI_D3hot);
return err;
}
@@ -9389,7 +9792,7 @@ static int tg3_close(struct net_device *dev)
struct tg3 *tp = netdev_priv(dev);
tg3_napi_disable(tp);
- cancel_work_sync(&tp->reset_task);
+ tg3_reset_task_cancel(tp);
netif_tx_stop_all_queues(dev);
@@ -9613,6 +10016,7 @@ static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev,
get_stat64(&hw_stats->rx_discards);
stats->rx_dropped = tp->rx_dropped;
+ stats->tx_dropped = tp->tx_dropped;
return stats;
}
@@ -9902,6 +10306,18 @@ static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
}
cmd->advertising = tp->link_config.advertising;
+ if (tg3_flag(tp, PAUSE_AUTONEG)) {
+ if (tp->link_config.flowctrl & FLOW_CTRL_RX) {
+ if (tp->link_config.flowctrl & FLOW_CTRL_TX) {
+ cmd->advertising |= ADVERTISED_Pause;
+ } else {
+ cmd->advertising |= ADVERTISED_Pause |
+ ADVERTISED_Asym_Pause;
+ }
+ } else if (tp->link_config.flowctrl & FLOW_CTRL_TX) {
+ cmd->advertising |= ADVERTISED_Asym_Pause;
+ }
+ }
if (netif_running(dev)) {
ethtool_cmd_speed_set(cmd, tp->link_config.active_speed);
cmd->duplex = tp->link_config.active_duplex;
@@ -10106,7 +10522,6 @@ static void tg3_get_ringparam(struct net_device *dev, struct ethtool_ringparam *
struct tg3 *tp = netdev_priv(dev);
ering->rx_max_pending = tp->rx_std_ring_mask;
- ering->rx_mini_max_pending = 0;
if (tg3_flag(tp, JUMBO_RING_ENABLE))
ering->rx_jumbo_max_pending = tp->rx_jmb_ring_mask;
else
@@ -10115,7 +10530,6 @@ static void tg3_get_ringparam(struct net_device *dev, struct ethtool_ringparam *
ering->tx_max_pending = TG3_TX_RING_SIZE - 1;
ering->rx_pending = tp->rx_pending;
- ering->rx_mini_pending = 0;
if (tg3_flag(tp, JUMBO_RING_ENABLE))
ering->rx_jumbo_pending = tp->rx_jumbo_pending;
else
@@ -10358,7 +10772,7 @@ static void tg3_get_ethtool_stats(struct net_device *dev,
memcpy(tmp_stats, tg3_get_estats(tp), sizeof(tp->estats));
}
-static __be32 * tg3_vpd_readblock(struct tg3 *tp)
+static __be32 *tg3_vpd_readblock(struct tg3 *tp, u32 *vpdlen)
{
int i;
__be32 *buf;
@@ -10425,6 +10839,8 @@ static __be32 * tg3_vpd_readblock(struct tg3 *tp)
goto error;
}
+ *vpdlen = len;
+
return buf;
error:
@@ -10436,12 +10852,15 @@ error:
#define NVRAM_SELFBOOT_FORMAT1_0_SIZE 0x14
#define NVRAM_SELFBOOT_FORMAT1_2_SIZE 0x18
#define NVRAM_SELFBOOT_FORMAT1_3_SIZE 0x1c
+#define NVRAM_SELFBOOT_FORMAT1_4_SIZE 0x20
+#define NVRAM_SELFBOOT_FORMAT1_5_SIZE 0x24
+#define NVRAM_SELFBOOT_FORMAT1_6_SIZE 0x50
#define NVRAM_SELFBOOT_HW_SIZE 0x20
#define NVRAM_SELFBOOT_DATA_SIZE 0x1c
static int tg3_test_nvram(struct tg3 *tp)
{
- u32 csum, magic;
+ u32 csum, magic, len;
__be32 *buf;
int i, j, k, err = 0, size;
@@ -10466,8 +10885,17 @@ static int tg3_test_nvram(struct tg3 *tp)
case TG3_EEPROM_SB_REVISION_3:
size = NVRAM_SELFBOOT_FORMAT1_3_SIZE;
break;
+ case TG3_EEPROM_SB_REVISION_4:
+ size = NVRAM_SELFBOOT_FORMAT1_4_SIZE;
+ break;
+ case TG3_EEPROM_SB_REVISION_5:
+ size = NVRAM_SELFBOOT_FORMAT1_5_SIZE;
+ break;
+ case TG3_EEPROM_SB_REVISION_6:
+ size = NVRAM_SELFBOOT_FORMAT1_6_SIZE;
+ break;
default:
- return 0;
+ return -EIO;
}
} else
return 0;
@@ -10573,18 +11001,17 @@ static int tg3_test_nvram(struct tg3 *tp)
kfree(buf);
- buf = tg3_vpd_readblock(tp);
+ buf = tg3_vpd_readblock(tp, &len);
if (!buf)
return -ENOMEM;
- i = pci_vpd_find_tag((u8 *)buf, 0, TG3_NVM_VPD_LEN,
- PCI_VPD_LRDT_RO_DATA);
+ i = pci_vpd_find_tag((u8 *)buf, 0, len, PCI_VPD_LRDT_RO_DATA);
if (i > 0) {
j = pci_vpd_lrdt_size(&((u8 *)buf)[i]);
if (j < 0)
goto out;
- if (i + PCI_VPD_LRDT_TAG_SIZE + j > TG3_NVM_VPD_LEN)
+ if (i + PCI_VPD_LRDT_TAG_SIZE + j > len)
goto out;
i += PCI_VPD_LRDT_TAG_SIZE;
@@ -10945,10 +11372,6 @@ static int tg3_test_memory(struct tg3 *tp)
return err;
}
-#define TG3_MAC_LOOPBACK 0
-#define TG3_PHY_LOOPBACK 1
-#define TG3_TSO_LOOPBACK 2
-
#define TG3_TSO_MSS 500
#define TG3_TSO_IP_HDR_LEN 20
@@ -10972,10 +11395,11 @@ static const u8 tg3_tso_header[] = {
0x11, 0x11, 0x11, 0x11,
};
-static int tg3_run_loopback(struct tg3 *tp, u32 pktsz, int loopback_mode)
+static int tg3_run_loopback(struct tg3 *tp, u32 pktsz, bool tso_loopback)
{
- u32 mac_mode, rx_start_idx, rx_idx, tx_idx, opaque_key;
+ u32 rx_start_idx, rx_idx, tx_idx, opaque_key;
u32 base_flags = 0, mss = 0, desc_idx, coal_now, data_off, val;
+ u32 budget;
struct sk_buff *skb, *rx_skb;
u8 *tx_data;
dma_addr_t map;
@@ -10994,76 +11418,6 @@ static int tg3_run_loopback(struct tg3 *tp, u32 pktsz, int loopback_mode)
}
coal_now = tnapi->coal_now | rnapi->coal_now;
- if (loopback_mode == TG3_MAC_LOOPBACK) {
- /* HW errata - mac loopback fails in some cases on 5780.
- * Normal traffic and PHY loopback are not affected by
- * errata. Also, the MAC loopback test is deprecated for
- * all newer ASIC revisions.
- */
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780 ||
- tg3_flag(tp, CPMU_PRESENT))
- return 0;
-
- mac_mode = tp->mac_mode &
- ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX);
- mac_mode |= MAC_MODE_PORT_INT_LPBACK;
- if (!tg3_flag(tp, 5705_PLUS))
- mac_mode |= MAC_MODE_LINK_POLARITY;
- if (tp->phy_flags & TG3_PHYFLG_10_100_ONLY)
- mac_mode |= MAC_MODE_PORT_MODE_MII;
- else
- mac_mode |= MAC_MODE_PORT_MODE_GMII;
- tw32(MAC_MODE, mac_mode);
- } else {
- if (tp->phy_flags & TG3_PHYFLG_IS_FET) {
- tg3_phy_fet_toggle_apd(tp, false);
- val = BMCR_LOOPBACK | BMCR_FULLDPLX | BMCR_SPEED100;
- } else
- val = BMCR_LOOPBACK | BMCR_FULLDPLX | BMCR_SPEED1000;
-
- tg3_phy_toggle_automdix(tp, 0);
-
- tg3_writephy(tp, MII_BMCR, val);
- udelay(40);
-
- mac_mode = tp->mac_mode &
- ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX);
- if (tp->phy_flags & TG3_PHYFLG_IS_FET) {
- tg3_writephy(tp, MII_TG3_FET_PTEST,
- MII_TG3_FET_PTEST_FRC_TX_LINK |
- MII_TG3_FET_PTEST_FRC_TX_LOCK);
- /* The write needs to be flushed for the AC131 */
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
- tg3_readphy(tp, MII_TG3_FET_PTEST, &val);
- mac_mode |= MAC_MODE_PORT_MODE_MII;
- } else
- mac_mode |= MAC_MODE_PORT_MODE_GMII;
-
- /* reset to prevent losing 1st rx packet intermittently */
- if (tp->phy_flags & TG3_PHYFLG_MII_SERDES) {
- tw32_f(MAC_RX_MODE, RX_MODE_RESET);
- udelay(10);
- tw32_f(MAC_RX_MODE, tp->rx_mode);
- }
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) {
- u32 masked_phy_id = tp->phy_id & TG3_PHY_ID_MASK;
- if (masked_phy_id == TG3_PHY_ID_BCM5401)
- mac_mode &= ~MAC_MODE_LINK_POLARITY;
- else if (masked_phy_id == TG3_PHY_ID_BCM5411)
- mac_mode |= MAC_MODE_LINK_POLARITY;
- tg3_writephy(tp, MII_TG3_EXT_CTRL,
- MII_TG3_EXT_CTRL_LNK3_LED_MODE);
- }
- tw32(MAC_MODE, mac_mode);
-
- /* Wait for link */
- for (i = 0; i < 100; i++) {
- if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP)
- break;
- mdelay(1);
- }
- }
-
err = -EIO;
tx_len = pktsz;
@@ -11077,7 +11431,7 @@ static int tg3_run_loopback(struct tg3 *tp, u32 pktsz, int loopback_mode)
tw32(MAC_RX_MTU_SIZE, tx_len + ETH_FCS_LEN);
- if (loopback_mode == TG3_TSO_LOOPBACK) {
+ if (tso_loopback) {
struct iphdr *iph = (struct iphdr *)&tx_data[ETH_HLEN];
u32 hdr_len = TG3_TSO_IP_HDR_LEN + TG3_TSO_TCP_HDR_LEN +
@@ -11135,6 +11489,10 @@ static int tg3_run_loopback(struct tg3 *tp, u32 pktsz, int loopback_mode)
return -EIO;
}
+ val = tnapi->tx_prod;
+ tnapi->tx_buffers[val].skb = skb;
+ dma_unmap_addr_set(&tnapi->tx_buffers[val], mapping, map);
+
tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE |
rnapi->coal_now);
@@ -11142,8 +11500,13 @@ static int tg3_run_loopback(struct tg3 *tp, u32 pktsz, int loopback_mode)
rx_start_idx = rnapi->hw_status->idx[0].rx_producer;
- tg3_set_txd(tnapi, tnapi->tx_prod, map, tx_len,
- base_flags, (mss << 1) | 1);
+ budget = tg3_tx_avail(tnapi);
+ if (tg3_tx_frag_set(tnapi, &val, &budget, map, tx_len,
+ base_flags | TXD_FLAG_END, mss, 0)) {
+ tnapi->tx_buffers[val].skb = NULL;
+ dev_kfree_skb(skb);
+ return -EIO;
+ }
tnapi->tx_prod++;
@@ -11166,7 +11529,7 @@ static int tg3_run_loopback(struct tg3 *tp, u32 pktsz, int loopback_mode)
break;
}
- pci_unmap_single(tp->pdev, map, tx_len, PCI_DMA_TODEVICE);
+ tg3_tx_skb_unmap(tnapi, tnapi->tx_prod - 1, -1);
dev_kfree_skb(skb);
if (tx_idx != tnapi->tx_prod)
@@ -11188,7 +11551,7 @@ static int tg3_run_loopback(struct tg3 *tp, u32 pktsz, int loopback_mode)
rx_len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT)
- ETH_FCS_LEN;
- if (loopback_mode != TG3_TSO_LOOPBACK) {
+ if (!tso_loopback) {
if (rx_len != tx_len)
goto out;
@@ -11235,25 +11598,33 @@ out:
#define TG3_STD_LOOPBACK_FAILED 1
#define TG3_JMB_LOOPBACK_FAILED 2
#define TG3_TSO_LOOPBACK_FAILED 4
+#define TG3_LOOPBACK_FAILED \
+ (TG3_STD_LOOPBACK_FAILED | \
+ TG3_JMB_LOOPBACK_FAILED | \
+ TG3_TSO_LOOPBACK_FAILED)
-#define TG3_MAC_LOOPBACK_SHIFT 0
-#define TG3_PHY_LOOPBACK_SHIFT 4
-#define TG3_LOOPBACK_FAILED 0x00000077
-
-static int tg3_test_loopback(struct tg3 *tp)
+static int tg3_test_loopback(struct tg3 *tp, u64 *data, bool do_extlpbk)
{
- int err = 0;
- u32 eee_cap, cpmuctrl = 0;
-
- if (!netif_running(tp->dev))
- return TG3_LOOPBACK_FAILED;
+ int err = -EIO;
+ u32 eee_cap;
eee_cap = tp->phy_flags & TG3_PHYFLG_EEE_CAP;
tp->phy_flags &= ~TG3_PHYFLG_EEE_CAP;
+ if (!netif_running(tp->dev)) {
+ data[0] = TG3_LOOPBACK_FAILED;
+ data[1] = TG3_LOOPBACK_FAILED;
+ if (do_extlpbk)
+ data[2] = TG3_LOOPBACK_FAILED;
+ goto done;
+ }
+
err = tg3_reset_hw(tp, 1);
if (err) {
- err = TG3_LOOPBACK_FAILED;
+ data[0] = TG3_LOOPBACK_FAILED;
+ data[1] = TG3_LOOPBACK_FAILED;
+ if (do_extlpbk)
+ data[2] = TG3_LOOPBACK_FAILED;
goto done;
}
@@ -11266,68 +11637,72 @@ static int tg3_test_loopback(struct tg3 *tp)
tw32(i, 0x0);
}
- /* Turn off gphy autopowerdown. */
- if (tp->phy_flags & TG3_PHYFLG_ENABLE_APD)
- tg3_phy_toggle_apd(tp, false);
+ /* HW errata - mac loopback fails in some cases on 5780.
+ * Normal traffic and PHY loopback are not affected by
+ * errata. Also, the MAC loopback test is deprecated for
+ * all newer ASIC revisions.
+ */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5780 &&
+ !tg3_flag(tp, CPMU_PRESENT)) {
+ tg3_mac_loopback(tp, true);
+
+ if (tg3_run_loopback(tp, ETH_FRAME_LEN, false))
+ data[0] |= TG3_STD_LOOPBACK_FAILED;
+
+ if (tg3_flag(tp, JUMBO_RING_ENABLE) &&
+ tg3_run_loopback(tp, 9000 + ETH_HLEN, false))
+ data[0] |= TG3_JMB_LOOPBACK_FAILED;
- if (tg3_flag(tp, CPMU_PRESENT)) {
+ tg3_mac_loopback(tp, false);
+ }
+
+ if (!(tp->phy_flags & TG3_PHYFLG_PHY_SERDES) &&
+ !tg3_flag(tp, USE_PHYLIB)) {
int i;
- u32 status;
- tw32(TG3_CPMU_MUTEX_REQ, CPMU_MUTEX_REQ_DRIVER);
+ tg3_phy_lpbk_set(tp, 0, false);
- /* Wait for up to 40 microseconds to acquire lock. */
- for (i = 0; i < 4; i++) {
- status = tr32(TG3_CPMU_MUTEX_GNT);
- if (status == CPMU_MUTEX_GNT_DRIVER)
+ /* Wait for link */
+ for (i = 0; i < 100; i++) {
+ if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP)
break;
- udelay(10);
- }
-
- if (status != CPMU_MUTEX_GNT_DRIVER) {
- err = TG3_LOOPBACK_FAILED;
- goto done;
+ mdelay(1);
}
- /* Turn off link-based power management. */
- cpmuctrl = tr32(TG3_CPMU_CTRL);
- tw32(TG3_CPMU_CTRL,
- cpmuctrl & ~(CPMU_CTRL_LINK_SPEED_MODE |
- CPMU_CTRL_LINK_AWARE_MODE));
- }
-
- if (tg3_run_loopback(tp, ETH_FRAME_LEN, TG3_MAC_LOOPBACK))
- err |= TG3_STD_LOOPBACK_FAILED << TG3_MAC_LOOPBACK_SHIFT;
+ if (tg3_run_loopback(tp, ETH_FRAME_LEN, false))
+ data[1] |= TG3_STD_LOOPBACK_FAILED;
+ if (tg3_flag(tp, TSO_CAPABLE) &&
+ tg3_run_loopback(tp, ETH_FRAME_LEN, true))
+ data[1] |= TG3_TSO_LOOPBACK_FAILED;
+ if (tg3_flag(tp, JUMBO_RING_ENABLE) &&
+ tg3_run_loopback(tp, 9000 + ETH_HLEN, false))
+ data[1] |= TG3_JMB_LOOPBACK_FAILED;
- if (tg3_flag(tp, JUMBO_RING_ENABLE) &&
- tg3_run_loopback(tp, 9000 + ETH_HLEN, TG3_MAC_LOOPBACK))
- err |= TG3_JMB_LOOPBACK_FAILED << TG3_MAC_LOOPBACK_SHIFT;
+ if (do_extlpbk) {
+ tg3_phy_lpbk_set(tp, 0, true);
- if (tg3_flag(tp, CPMU_PRESENT)) {
- tw32(TG3_CPMU_CTRL, cpmuctrl);
+ /* All link indications report up, but the hardware
+ * isn't really ready for about 20 msec. Double it
+ * to be sure.
+ */
+ mdelay(40);
- /* Release the mutex */
- tw32(TG3_CPMU_MUTEX_GNT, CPMU_MUTEX_GNT_DRIVER);
- }
+ if (tg3_run_loopback(tp, ETH_FRAME_LEN, false))
+ data[2] |= TG3_STD_LOOPBACK_FAILED;
+ if (tg3_flag(tp, TSO_CAPABLE) &&
+ tg3_run_loopback(tp, ETH_FRAME_LEN, true))
+ data[2] |= TG3_TSO_LOOPBACK_FAILED;
+ if (tg3_flag(tp, JUMBO_RING_ENABLE) &&
+ tg3_run_loopback(tp, 9000 + ETH_HLEN, false))
+ data[2] |= TG3_JMB_LOOPBACK_FAILED;
+ }
- if (!(tp->phy_flags & TG3_PHYFLG_PHY_SERDES) &&
- !tg3_flag(tp, USE_PHYLIB)) {
- if (tg3_run_loopback(tp, ETH_FRAME_LEN, TG3_PHY_LOOPBACK))
- err |= TG3_STD_LOOPBACK_FAILED <<
- TG3_PHY_LOOPBACK_SHIFT;
- if (tg3_flag(tp, TSO_CAPABLE) &&
- tg3_run_loopback(tp, ETH_FRAME_LEN, TG3_TSO_LOOPBACK))
- err |= TG3_TSO_LOOPBACK_FAILED <<
- TG3_PHY_LOOPBACK_SHIFT;
- if (tg3_flag(tp, JUMBO_RING_ENABLE) &&
- tg3_run_loopback(tp, 9000 + ETH_HLEN, TG3_PHY_LOOPBACK))
- err |= TG3_JMB_LOOPBACK_FAILED <<
- TG3_PHY_LOOPBACK_SHIFT;
+ /* Re-enable gphy autopowerdown. */
+ if (tp->phy_flags & TG3_PHYFLG_ENABLE_APD)
+ tg3_phy_toggle_apd(tp, true);
}
- /* Re-enable gphy autopowerdown. */
- if (tp->phy_flags & TG3_PHYFLG_ENABLE_APD)
- tg3_phy_toggle_apd(tp, true);
+ err = (data[0] | data[1] | data[2]) ? -EIO : 0;
done:
tp->phy_flags |= eee_cap;
@@ -11339,9 +11714,14 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest,
u64 *data)
{
struct tg3 *tp = netdev_priv(dev);
+ bool doextlpbk = etest->flags & ETH_TEST_FL_EXTERNAL_LB;
- if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER)
- tg3_power_up(tp);
+ if ((tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) &&
+ tg3_power_up(tp)) {
+ etest->flags |= ETH_TEST_FL_FAILED;
+ memset(data, 1, sizeof(u64) * TG3_NUM_TEST);
+ return;
+ }
memset(data, 0, sizeof(u64) * TG3_NUM_TEST);
@@ -11349,7 +11729,7 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest,
etest->flags |= ETH_TEST_FL_FAILED;
data[0] = 1;
}
- if (tg3_test_link(tp) != 0) {
+ if (!doextlpbk && tg3_test_link(tp)) {
etest->flags |= ETH_TEST_FL_FAILED;
data[1] = 1;
}
@@ -11379,18 +11759,23 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest,
etest->flags |= ETH_TEST_FL_FAILED;
data[2] = 1;
}
+
if (tg3_test_memory(tp) != 0) {
etest->flags |= ETH_TEST_FL_FAILED;
data[3] = 1;
}
- if ((data[4] = tg3_test_loopback(tp)) != 0)
+
+ if (doextlpbk)
+ etest->flags |= ETH_TEST_FL_EXTERNAL_LB_DONE;
+
+ if (tg3_test_loopback(tp, &data[4], doextlpbk))
etest->flags |= ETH_TEST_FL_FAILED;
tg3_full_unlock(tp);
if (tg3_test_interrupt(tp) != 0) {
etest->flags |= ETH_TEST_FL_FAILED;
- data[5] = 1;
+ data[7] = 1;
}
tg3_full_lock(tp, 0);
@@ -12585,29 +12970,6 @@ static struct subsys_tbl_ent * __devinit tg3_lookup_by_subsys(struct tg3 *tp)
static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp)
{
u32 val;
- u16 pmcsr;
-
- /* On some early chips the SRAM cannot be accessed in D3hot state,
- * so need make sure we're in D0.
- */
- pci_read_config_word(tp->pdev, tp->pm_cap + PCI_PM_CTRL, &pmcsr);
- pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
- pci_write_config_word(tp->pdev, tp->pm_cap + PCI_PM_CTRL, pmcsr);
- msleep(1);
-
- /* Make sure register accesses (indirect or otherwise)
- * will function correctly.
- */
- pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
- tp->misc_host_ctrl);
-
- /* The memory arbiter has to be enabled in order for SRAM accesses
- * to succeed. Normally on powerup the tg3 chip firmware will make
- * sure it is enabled, but other entities such as system netboot
- * code might disable it.
- */
- val = tr32(MEMARB_MODE);
- tw32(MEMARB_MODE, val | MEMARB_MODE_ENABLE);
tp->phy_id = TG3_PHY_ID_INVALID;
tp->led_ctrl = LED_CTRL_MODE_PHY_1;
@@ -12947,7 +13309,9 @@ static int __devinit tg3_phy_probe(struct tg3 *tp)
}
if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES) &&
- ((tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720 ||
+ (tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 &&
tp->pci_chip_rev_id != CHIPREV_ID_5717_A0) ||
(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765 &&
tp->pci_chip_rev_id != CHIPREV_ID_57765_A0)))
@@ -12999,14 +13363,14 @@ static void __devinit tg3_read_vpd(struct tg3 *tp)
{
u8 *vpd_data;
unsigned int block_end, rosize, len;
+ u32 vpdlen;
int j, i = 0;
- vpd_data = (u8 *)tg3_vpd_readblock(tp);
+ vpd_data = (u8 *)tg3_vpd_readblock(tp, &vpdlen);
if (!vpd_data)
goto out_no_vpd;
- i = pci_vpd_find_tag(vpd_data, 0, TG3_NVM_VPD_LEN,
- PCI_VPD_LRDT_RO_DATA);
+ i = pci_vpd_find_tag(vpd_data, 0, vpdlen, PCI_VPD_LRDT_RO_DATA);
if (i < 0)
goto out_not_found;
@@ -13014,7 +13378,7 @@ static void __devinit tg3_read_vpd(struct tg3 *tp)
block_end = i + PCI_VPD_LRDT_TAG_SIZE + rosize;
i += PCI_VPD_LRDT_TAG_SIZE;
- if (block_end > TG3_NVM_VPD_LEN)
+ if (block_end > vpdlen)
goto out_not_found;
j = pci_vpd_find_info_keyword(vpd_data, i, rosize,
@@ -13039,7 +13403,7 @@ static void __devinit tg3_read_vpd(struct tg3 *tp)
goto partno;
memcpy(tp->fw_ver, &vpd_data[j], len);
- strncat(tp->fw_ver, " bc ", TG3_NVM_VPD_LEN - len - 1);
+ strncat(tp->fw_ver, " bc ", vpdlen - len - 1);
}
partno:
@@ -13052,7 +13416,7 @@ partno:
i += PCI_VPD_INFO_FLD_HDR_SIZE;
if (len > TG3_BPN_SIZE ||
- (len + i) > TG3_NVM_VPD_LEN)
+ (len + i) > vpdlen)
goto out_not_found;
memcpy(tp->board_part_number, &vpd_data[i], len);
@@ -13353,10 +13717,15 @@ static void __devinit tg3_read_fw_ver(struct tg3 *tp)
else
return;
- if (!tg3_flag(tp, ENABLE_ASF) || tg3_flag(tp, ENABLE_APE) || vpd_vers)
+ if (vpd_vers)
goto done;
- tg3_read_mgmtfw_ver(tp);
+ if (tg3_flag(tp, ENABLE_APE)) {
+ if (tg3_flag(tp, ENABLE_ASF))
+ tg3_read_dash_ver(tp);
+ } else if (tg3_flag(tp, ENABLE_ASF)) {
+ tg3_read_mgmtfw_ver(tp);
+ }
done:
tp->fw_ver[TG3_VER_SIZE - 1] = 0;
@@ -13400,14 +13769,17 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
pci_cmd &= ~PCI_COMMAND_INVALIDATE;
pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd);
- /* It is absolutely critical that TG3PCI_MISC_HOST_CTRL
- * has the register indirect write enable bit set before
- * we try to access any of the MMIO registers. It is also
- * critical that the PCI-X hw workaround situation is decided
- * before that as well.
+ /* Important! -- Make sure register accesses are byteswapped
+ * correctly. Also, for those chips that require it, make
+ * sure that indirect register accesses are enabled before
+ * the first operation.
*/
pci_read_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
&misc_ctrl_reg);
+ tp->misc_host_ctrl |= (misc_ctrl_reg &
+ MISC_HOST_CTRL_CHIPREV);
+ pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
+ tp->misc_host_ctrl);
tp->pci_chip_rev_id = (misc_ctrl_reg >>
MISC_HOST_CTRL_CHIPREV_SHIFT);
@@ -13563,16 +13935,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
} while (bridge);
}
- /* Initialize misc host control in PCI block. */
- tp->misc_host_ctrl |= (misc_ctrl_reg &
- MISC_HOST_CTRL_CHIPREV);
- pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
- tp->misc_host_ctrl);
-
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714)
tp->pdev_peer = tg3_find_peer(tp);
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
@@ -13606,7 +13970,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tg3_flag_set(tp, 5705_PLUS);
/* Determine TSO capabilities */
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5719_A0)
; /* Do nothing. HW bug. */
else if (tg3_flag(tp, 57765_PLUS))
tg3_flag_set(tp, HW_TSO_3);
@@ -13666,21 +14030,17 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
}
}
- /* All chips can get confused if TX buffers
- * straddle the 4GB address boundary.
- */
- tg3_flag_set(tp, 4G_DMA_BNDRY_BUG);
-
if (tg3_flag(tp, 5755_PLUS))
tg3_flag_set(tp, SHORT_DMA_BUG);
- else
- tg3_flag_set(tp, 40BIT_DMA_LIMIT_BUG);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+ tg3_flag_set(tp, 4K_FIFO_LIMIT);
if (tg3_flag(tp, 5717_PLUS))
tg3_flag_set(tp, LRG_PROD_RING_CAP);
if (tg3_flag(tp, 57765_PLUS) &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5719)
+ tp->pci_chip_rev_id != CHIPREV_ID_5719_A0)
tg3_flag_set(tp, USE_JUMBO_BDFLAG);
if (!tg3_flag(tp, 5705_PLUS) ||
@@ -13691,8 +14051,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE,
&pci_state_reg);
- tp->pcie_cap = pci_find_capability(tp->pdev, PCI_CAP_ID_EXP);
- if (tp->pcie_cap != 0) {
+ if (pci_is_pcie(tp->pdev)) {
u16 lnkctl;
tg3_flag_set(tp, PCI_EXPRESS);
@@ -13705,7 +14064,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
pcie_set_readrq(tp->pdev, tp->pcie_readrq);
pci_read_config_word(tp->pdev,
- tp->pcie_cap + PCI_EXP_LNKCTL,
+ pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
&lnkctl);
if (lnkctl & PCI_EXP_LNKCTL_CLKREQ_EN) {
if (GET_ASIC_REV(tp->pci_chip_rev_id) ==
@@ -13722,6 +14081,10 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tg3_flag_set(tp, L1PLLPD_EN);
}
} else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) {
+ /* BCM5785 devices are effectively PCIe devices, and should
+ * follow PCIe codepaths, but do not have a PCIe capabilities
+ * section.
+ */
tg3_flag_set(tp, PCI_EXPRESS);
} else if (!tg3_flag(tp, 5705_PLUS) ||
tg3_flag(tp, 5780_CLASS)) {
@@ -13757,6 +14120,9 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tp->pci_lat_timer);
}
+ /* Important! -- It is critical that the PCI-X hw workaround
+ * situation is decided before the first MMIO register access.
+ */
if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX) {
/* 5700 BX chips need to have their TX producer index
* mailboxes written twice to workaround a bug.
@@ -13863,6 +14229,40 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)))
tg3_flag_set(tp, SRAM_USE_CONFIG);
+ /* The memory arbiter has to be enabled in order for SRAM accesses
+ * to succeed. Normally on powerup the tg3 chip firmware will make
+ * sure it is enabled, but other entities such as system netboot
+ * code might disable it.
+ */
+ val = tr32(MEMARB_MODE);
+ tw32(MEMARB_MODE, val | MEMARB_MODE_ENABLE);
+
+ tp->pci_fn = PCI_FUNC(tp->pdev->devfn) & 3;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 ||
+ tg3_flag(tp, 5780_CLASS)) {
+ if (tg3_flag(tp, PCIX_MODE)) {
+ pci_read_config_dword(tp->pdev,
+ tp->pcix_cap + PCI_X_STATUS,
+ &val);
+ tp->pci_fn = val & 0x7;
+ }
+ } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) {
+ tg3_read_mem(tp, NIC_SRAM_CPMU_STATUS, &val);
+ if ((val & NIC_SRAM_CPMUSTAT_SIG_MSK) ==
+ NIC_SRAM_CPMUSTAT_SIG) {
+ tp->pci_fn = val & TG3_CPMU_STATUS_FMSK_5717;
+ tp->pci_fn = tp->pci_fn ? 1 : 0;
+ }
+ } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) {
+ tg3_read_mem(tp, NIC_SRAM_CPMU_STATUS, &val);
+ if ((val & NIC_SRAM_CPMUSTAT_SIG_MSK) ==
+ NIC_SRAM_CPMUSTAT_SIG) {
+ tp->pci_fn = (val & TG3_CPMU_STATUS_FMSK_5719) >>
+ TG3_CPMU_STATUS_FSHFT_5719;
+ }
+ }
+
/* Get eeprom hw config before calling tg3_set_power_state().
* In particular, the TG3_FLAG_IS_NIC flag must be
* determined before calling tg3_set_power_state() so that
@@ -13882,6 +14282,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
PCISTATE_ALLOW_APE_PSPACE_WR;
pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE,
pci_state_reg);
+
+ tg3_ape_lock_init(tp);
}
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
@@ -13891,8 +14293,9 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tg3_flag(tp, 57765_PLUS))
tg3_flag_set(tp, CPMU_PRESENT);
- /* Set up tp->grc_local_ctrl before calling tg3_power_up().
- * GPIO1 driven high will bring 5700's external PHY out of reset.
+ /* Set up tp->grc_local_ctrl before calling
+ * tg3_pwrsrc_switch_to_vmain(). GPIO1 driven high
+ * will bring 5700's external PHY out of reset.
* It is also used as eeprom write protect on LOMs.
*/
tp->grc_local_ctrl = GRC_LCLCTRL_INT_ON_ATTN | GRC_LCLCTRL_AUTO_SEEPROM;
@@ -13921,12 +14324,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GRC_LCLCTRL_GPIO_OUTPUT0;
}
- /* Force the chip into D0. */
- err = tg3_power_up(tp);
- if (err) {
- dev_err(&tp->pdev->dev, "Transition to D0 failed\n");
- return err;
- }
+ /* Switch out of Vaux if it is a NIC */
+ tg3_pwrsrc_switch_to_vmain(tp);
/* Derive initial jumbo mode from MTU assigned in
* ether_setup() via the alloc_etherdev() call
@@ -14084,7 +14483,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if (tg3_flag(tp, ENABLE_APE))
tp->mac_mode = MAC_MODE_APE_TX_EN | MAC_MODE_APE_RX_EN;
else
- tp->mac_mode = TG3_DEF_MAC_MODE;
+ tp->mac_mode = 0;
/* these are limited to 10/100 only */
if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 &&
@@ -14229,9 +14628,9 @@ static int __devinit tg3_get_device_address(struct tg3 *tp)
else
tg3_nvram_unlock(tp);
} else if (tg3_flag(tp, 5717_PLUS)) {
- if (PCI_FUNC(tp->pdev->devfn) & 1)
+ if (tp->pci_fn & 1)
mac_offset = 0xcc;
- if (PCI_FUNC(tp->pdev->devfn) > 1)
+ if (tp->pci_fn > 1)
mac_offset += 0x18c;
} else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
mac_offset = 0x10;
@@ -14893,7 +15292,7 @@ static const struct net_device_ops tg3_netdev_ops = {
.ndo_start_xmit = tg3_start_xmit,
.ndo_get_stats64 = tg3_get_stats64,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = tg3_set_rx_mode,
+ .ndo_set_rx_mode = tg3_set_rx_mode,
.ndo_set_mac_address = tg3_set_mac_addr,
.ndo_do_ioctl = tg3_ioctl,
.ndo_tx_timeout = tg3_tx_timeout,
@@ -14941,11 +15340,17 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
goto err_out_free_res;
}
+ err = pci_set_power_state(pdev, PCI_D0);
+ if (err) {
+ dev_err(&pdev->dev, "Transition to D0 failed, aborting\n");
+ goto err_out_free_res;
+ }
+
dev = alloc_etherdev_mq(sizeof(*tp), TG3_IRQ_MAX_VECS);
if (!dev) {
dev_err(&pdev->dev, "Etherdev alloc failed, aborting\n");
err = -ENOMEM;
- goto err_out_free_res;
+ goto err_out_power_down;
}
SET_NETDEV_DEV(dev, &pdev->dev);
@@ -14994,6 +15399,24 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
goto err_out_free_dev;
}
+ if (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5761 ||
+ tp->pdev->device == PCI_DEVICE_ID_TIGON3_5761E ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5761S ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5761SE ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5717 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5719 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5720) {
+ tg3_flag_set(tp, ENABLE_APE);
+ tp->aperegs = pci_ioremap_bar(pdev, BAR_2);
+ if (!tp->aperegs) {
+ dev_err(&pdev->dev,
+ "Cannot map APE registers, aborting\n");
+ err = -ENOMEM;
+ goto err_out_iounmap;
+ }
+ }
+
tp->rx_pending = TG3_DEF_RX_RING_PENDING;
tp->rx_jumbo_pending = TG3_DEF_RX_JUMBO_RING_PENDING;
@@ -15006,7 +15429,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
if (err) {
dev_err(&pdev->dev,
"Problem fetching invariants of chip, aborting\n");
- goto err_out_iounmap;
+ goto err_out_apeunmap;
}
/* The EPB bridge inside 5714, 5715, and 5780 and any
@@ -15035,7 +15458,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
if (err < 0) {
dev_err(&pdev->dev, "Unable to obtain 64 bit "
"DMA for consistent allocations\n");
- goto err_out_iounmap;
+ goto err_out_apeunmap;
}
}
}
@@ -15044,7 +15467,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
if (err) {
dev_err(&pdev->dev,
"No usable DMA configuration, aborting\n");
- goto err_out_iounmap;
+ goto err_out_apeunmap;
}
}
@@ -15109,22 +15532,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
if (err) {
dev_err(&pdev->dev,
"Could not obtain valid ethernet address, aborting\n");
- goto err_out_iounmap;
- }
-
- if (tg3_flag(tp, ENABLE_APE)) {
- tp->aperegs = pci_ioremap_bar(pdev, BAR_2);
- if (!tp->aperegs) {
- dev_err(&pdev->dev,
- "Cannot map APE registers, aborting\n");
- err = -ENOMEM;
- goto err_out_iounmap;
- }
-
- tg3_ape_lock_init(tp);
-
- if (tg3_flag(tp, ENABLE_ASF))
- tg3_read_dash_ver(tp);
+ goto err_out_apeunmap;
}
/*
@@ -15154,7 +15562,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
tnapi->tx_pending = TG3_DEF_TX_RING_PENDING;
tnapi->int_mbox = intmbx;
- if (i < 4)
+ if (i <= 4)
intmbx += 0x8;
else
intmbx += 0x4;
@@ -15192,6 +15600,11 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
pci_set_drvdata(pdev, dev);
+ if (tg3_flag(tp, 5717_PLUS)) {
+ /* Resume a low-power mode */
+ tg3_frob_aux_power(tp, false);
+ }
+
err = register_netdev(dev);
if (err) {
dev_err(&pdev->dev, "Cannot register net device, aborting\n");
@@ -15257,6 +15670,9 @@ err_out_iounmap:
err_out_free_dev:
free_netdev(dev);
+err_out_power_down:
+ pci_set_power_state(pdev, PCI_D3hot);
+
err_out_free_res:
pci_release_regions(pdev);
@@ -15276,9 +15692,9 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev)
if (tp->fw)
release_firmware(tp->fw);
- cancel_work_sync(&tp->reset_task);
+ tg3_reset_task_cancel(tp);
- if (!tg3_flag(tp, USE_PHYLIB)) {
+ if (tg3_flag(tp, USE_PHYLIB)) {
tg3_phy_fini(tp);
tg3_mdio_fini(tp);
}
@@ -15310,7 +15726,7 @@ static int tg3_suspend(struct device *device)
if (!netif_running(dev))
return 0;
- flush_work_sync(&tp->reset_task);
+ tg3_reset_task_cancel(tp);
tg3_phy_stop(tp);
tg3_netif_stop(tp);
@@ -15423,12 +15839,10 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev,
tg3_netif_stop(tp);
del_timer_sync(&tp->timer);
- tg3_flag_clear(tp, RESTART_TIMER);
/* Want to make sure that the reset task doesn't run */
- cancel_work_sync(&tp->reset_task);
+ tg3_reset_task_cancel(tp);
tg3_flag_clear(tp, TX_RECOVERY_PENDING);
- tg3_flag_clear(tp, RESTART_TIMER);
netif_device_detach(netdev);
@@ -15481,10 +15895,8 @@ static pci_ers_result_t tg3_io_slot_reset(struct pci_dev *pdev)
}
err = tg3_power_up(tp);
- if (err) {
- netdev_err(netdev, "Failed to restore register access.\n");
+ if (err)
goto done;
- }
rc = PCI_ERS_RESULT_RECOVERED;
diff --git a/drivers/net/tg3.h b/drivers/net/ethernet/broadcom/tg3.h
index 5b3d2f34da7a..94b4bd049a33 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/ethernet/broadcom/tg3.h
@@ -1065,6 +1065,8 @@
#define RCVLSC_STATUS_ERROR_ATTN 0x00000004
/* 0x3408 --> 0x3600 unused */
+#define TG3_CPMU_DRV_STATUS 0x0000344c
+
/* CPMU registers */
#define TG3_CPMU_CTRL 0x00003600
#define CPMU_CTRL_LINK_IDLE_MODE 0x00000200
@@ -1093,6 +1095,11 @@
#define TG3_CPMU_CLCK_ORIDE 0x00003624
#define CPMU_CLCK_ORIDE_MAC_ORIDE_EN 0x80000000
+#define TG3_CPMU_STATUS 0x0000362c
+#define TG3_CPMU_STATUS_FMSK_5717 0x20000000
+#define TG3_CPMU_STATUS_FMSK_5719 0xc0000000
+#define TG3_CPMU_STATUS_FSHFT_5719 30
+
#define TG3_CPMU_CLCK_STAT 0x00003630
#define CPMU_CLCK_STAT_MAC_CLCK_MASK 0x001f0000
#define CPMU_CLCK_STAT_MAC_CLCK_62_5 0x00000000
@@ -1118,10 +1125,10 @@
#define TG3_CPMU_EEEMD_EEE_ENABLE 0x00100000
#define TG3_CPMU_EEE_DBTMR1 0x000036b4
#define TG3_CPMU_DBTMR1_PCIEXIT_2047US 0x07ff0000
-#define TG3_CPMU_DBTMR1_LNKIDLE_2047US 0x000070ff
+#define TG3_CPMU_DBTMR1_LNKIDLE_2047US 0x000007ff
#define TG3_CPMU_EEE_DBTMR2 0x000036b8
#define TG3_CPMU_DBTMR2_APE_TX_2047US 0x07ff0000
-#define TG3_CPMU_DBTMR2_TXIDXEQ_2047US 0x000070ff
+#define TG3_CPMU_DBTMR2_TXIDXEQ_2047US 0x000007ff
#define TG3_CPMU_EEE_LNKIDL_CTRL 0x000036bc
#define TG3_CPMU_EEE_LNKIDL_PCIE_NL0 0x01000000
#define TG3_CPMU_EEE_LNKIDL_UART_IDL 0x00000004
@@ -2126,6 +2133,10 @@
#define NIC_SRAM_RGMII_EXT_IBND_RX_EN 0x00000008
#define NIC_SRAM_RGMII_EXT_IBND_TX_EN 0x00000010
+#define NIC_SRAM_CPMU_STATUS 0x00000e00
+#define NIC_SRAM_CPMUSTAT_SIG 0x0000362c
+#define NIC_SRAM_CPMUSTAT_SIG_MSK 0x0000ffff
+
#define NIC_SRAM_RX_MINI_BUFFER_DESC 0x00001000
#define NIC_SRAM_DMA_DESC_POOL_BASE 0x00002000
@@ -2152,14 +2163,6 @@
/*** Tigon3 specific PHY MII registers. ***/
-#define TG3_BMCR_SPEED1000 0x0040
-
-#define MII_TG3_CTRL 0x09 /* 1000-baseT control register */
-#define MII_TG3_CTRL_ADV_1000_HALF 0x0100
-#define MII_TG3_CTRL_ADV_1000_FULL 0x0200
-#define MII_TG3_CTRL_AS_MASTER 0x0800
-#define MII_TG3_CTRL_ENABLE_AS_MASTER 0x1000
-
#define MII_TG3_MMD_CTRL 0x0d /* MMD Access Control register */
#define MII_TG3_MMD_CTRL_DATA_NOINC 0x4000
#define MII_TG3_MMD_ADDRESS 0x0e /* MMD Address Data register */
@@ -2186,7 +2189,7 @@
#define MII_TG3_DSP_TAP26_OPCSINPT 0x0004
#define MII_TG3_DSP_AADJ1CH0 0x001f
#define MII_TG3_DSP_CH34TP2 0x4022
-#define MII_TG3_DSP_CH34TP2_HIBW01 0x017b
+#define MII_TG3_DSP_CH34TP2_HIBW01 0x01ff
#define MII_TG3_DSP_AADJ1CH3 0x601f
#define MII_TG3_DSP_AADJ1CH3_ADCCKADJ 0x0002
#define MII_TG3_DSP_EXP1_INT_STAT 0x0f01
@@ -2203,6 +2206,7 @@
#define MII_TG3_AUXCTL_ACTL_TX_6DB 0x0400
#define MII_TG3_AUXCTL_ACTL_SMDSP_ENA 0x0800
#define MII_TG3_AUXCTL_ACTL_EXTPKTLEN 0x4000
+#define MII_TG3_AUXCTL_ACTL_EXTLOOPBK 0x8000
#define MII_TG3_AUXCTL_SHDWSEL_PWRCTL 0x0002
#define MII_TG3_AUXCTL_PCTL_WOL_EN 0x0008
@@ -2268,6 +2272,8 @@
/* Fast Ethernet Tranceiver definitions */
#define MII_TG3_FET_PTEST 0x17
+#define MII_TG3_FET_PTEST_TRIM_SEL 0x0010
+#define MII_TG3_FET_PTEST_TRIM_2 0x0002
#define MII_TG3_FET_PTEST_FRC_TX_LINK 0x1000
#define MII_TG3_FET_PTEST_FRC_TX_LOCK 0x0800
@@ -2285,6 +2291,8 @@
/* APE registers. Accessible through BAR1 */
+#define TG3_APE_GPIO_MSG 0x0008
+#define TG3_APE_GPIO_MSG_SHIFT 4
#define TG3_APE_EVENT 0x000c
#define APE_EVENT_1 0x00000001
#define TG3_APE_LOCK_REQ 0x002c
@@ -2345,8 +2353,13 @@
#define APE_PER_LOCK_GRANT_DRIVER 0x00001000
/* APE convenience enumerations. */
-#define TG3_APE_LOCK_GRC 1
-#define TG3_APE_LOCK_MEM 4
+#define TG3_APE_LOCK_PHY0 0
+#define TG3_APE_LOCK_GRC 1
+#define TG3_APE_LOCK_PHY1 2
+#define TG3_APE_LOCK_PHY2 3
+#define TG3_APE_LOCK_MEM 4
+#define TG3_APE_LOCK_PHY3 5
+#define TG3_APE_LOCK_GPIO 7
#define TG3_EEPROM_SB_F1R2_MBA_OFF 0x10
@@ -2655,6 +2668,12 @@ struct ring_info {
DEFINE_DMA_UNMAP_ADDR(mapping);
};
+struct tg3_tx_ring_info {
+ struct sk_buff *skb;
+ DEFINE_DMA_UNMAP_ADDR(mapping);
+ bool fragmented;
+};
+
struct tg3_link_config {
/* Describes what we're trying to get. */
u32 advertising;
@@ -2800,6 +2819,7 @@ struct tg3_napi {
struct tg3 *tp;
struct tg3_hw_status *hw_status;
+ u32 chk_msi_cnt;
u32 last_tag;
u32 last_irq_tag;
u32 int_mbox;
@@ -2807,6 +2827,7 @@ struct tg3_napi {
u32 consmbox ____cacheline_aligned;
u32 rx_rcb_ptr;
+ u32 last_rx_cons;
u16 *rx_rcb_prod_idx;
struct tg3_rx_prodring_set prodring;
struct tg3_rx_buffer_desc *rx_rcb;
@@ -2814,9 +2835,10 @@ struct tg3_napi {
u32 tx_prod ____cacheline_aligned;
u32 tx_cons;
u32 tx_pending;
+ u32 last_tx_cons;
u32 prodmbox;
struct tg3_tx_buffer_desc *tx_ring;
- struct ring_info *tx_buffers;
+ struct tg3_tx_ring_info *tx_buffers;
dma_addr_t status_mapping;
dma_addr_t rx_rcb_mapping;
@@ -2857,12 +2879,11 @@ enum TG3_FLAGS {
TG3_FLAG_JUMBO_CAPABLE,
TG3_FLAG_CHIP_RESETTING,
TG3_FLAG_INIT_COMPLETE,
- TG3_FLAG_RESTART_TIMER,
TG3_FLAG_TSO_BUG,
TG3_FLAG_IS_5788,
TG3_FLAG_MAX_RXPEND_64,
TG3_FLAG_TSO_CAPABLE,
- TG3_FLAG_PCI_EXPRESS,
+ TG3_FLAG_PCI_EXPRESS, /* BCM5785 + pci_is_pcie() */
TG3_FLAG_ASF_NEW_HANDSHAKE,
TG3_FLAG_HW_AUTONEG,
TG3_FLAG_IS_NIC,
@@ -2893,14 +2914,14 @@ enum TG3_FLAGS {
TG3_FLAG_NO_NVRAM,
TG3_FLAG_ENABLE_RSS,
TG3_FLAG_ENABLE_TSS,
- TG3_FLAG_4G_DMA_BNDRY_BUG,
- TG3_FLAG_40BIT_DMA_LIMIT_BUG,
TG3_FLAG_SHORT_DMA_BUG,
TG3_FLAG_USE_JUMBO_BDFLAG,
TG3_FLAG_L1PLLPD_EN,
TG3_FLAG_57765_PLUS,
TG3_FLAG_APE_HAS_NCSI,
TG3_FLAG_5717_PLUS,
+ TG3_FLAG_4K_FIFO_LIMIT,
+ TG3_FLAG_RESET_TASK_PENDING,
/* Add new flags before this comment and TG3_FLAG_NUMBER_OF_FLAGS */
TG3_FLAG_NUMBER_OF_FLAGS, /* Last entry in enum TG3_FLAGS */
@@ -2982,6 +3003,7 @@ struct tg3 {
/* begin "everything else" cacheline(s) section */
unsigned long rx_dropped;
+ unsigned long tx_dropped;
struct rtnl_link_stats64 net_stats_prev;
struct tg3_ethtool_stats estats;
struct tg3_ethtool_stats estats_prev;
@@ -3027,12 +3049,10 @@ struct tg3 {
u8 pci_cacheline_sz;
u8 pci_lat_timer;
+ int pci_fn;
int pm_cap;
int msi_cap;
- union {
int pcix_cap;
- int pcie_cap;
- };
int pcie_readrq;
struct mii_bus *mdio_bus;
diff --git a/drivers/net/ethernet/brocade/Kconfig b/drivers/net/ethernet/brocade/Kconfig
new file mode 100644
index 000000000000..264155778857
--- /dev/null
+++ b/drivers/net/ethernet/brocade/Kconfig
@@ -0,0 +1,23 @@
+#
+# Brocade device configuration
+#
+
+config NET_VENDOR_BROCADE
+ bool "Brocade devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Brocade cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_BROCADE
+
+source "drivers/net/ethernet/brocade/bna/Kconfig"
+
+endif # NET_VENDOR_BROCADE
diff --git a/drivers/net/ethernet/brocade/Makefile b/drivers/net/ethernet/brocade/Makefile
new file mode 100644
index 000000000000..b58238d2df6a
--- /dev/null
+++ b/drivers/net/ethernet/brocade/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Brocade device drivers.
+#
+
+obj-$(CONFIG_BNA) += bna/
diff --git a/drivers/net/ethernet/brocade/bna/Kconfig b/drivers/net/ethernet/brocade/bna/Kconfig
new file mode 100644
index 000000000000..dc2eb526fbf7
--- /dev/null
+++ b/drivers/net/ethernet/brocade/bna/Kconfig
@@ -0,0 +1,17 @@
+#
+# Brocade network device configuration
+#
+
+config BNA
+ tristate "Brocade 1010/1020 10Gb Ethernet Driver support"
+ depends on PCI
+ ---help---
+ This driver supports Brocade 1010/1020 10Gb CEE capable Ethernet
+ cards.
+ To compile this driver as a module, choose M here: the module
+ will be called bna.
+
+ For general information and support, go to the Brocade support
+ website at:
+
+ <http://support.brocade.com>
diff --git a/drivers/net/bna/Makefile b/drivers/net/ethernet/brocade/bna/Makefile
index a5d604de7fea..74d3abca1960 100644
--- a/drivers/net/bna/Makefile
+++ b/drivers/net/ethernet/brocade/bna/Makefile
@@ -5,7 +5,8 @@
obj-$(CONFIG_BNA) += bna.o
-bna-objs := bnad.o bnad_ethtool.o bna_ctrl.o bna_txrx.o
-bna-objs += bfa_ioc.o bfa_ioc_ct.o bfa_cee.o cna_fwimg.o
+bna-objs := bnad.o bnad_ethtool.o bna_enet.o bna_tx_rx.o
+bna-objs += bfa_msgq.o bfa_ioc.o bfa_ioc_ct.o bfa_cee.o
+bna-objs += cna_fwimg.o
EXTRA_CFLAGS := -Idrivers/net/bna
diff --git a/drivers/net/bna/bfa_cee.c b/drivers/net/ethernet/brocade/bna/bfa_cee.c
index f7b789a3b217..8e627186507c 100644
--- a/drivers/net/bna/bfa_cee.c
+++ b/drivers/net/ethernet/brocade/bna/bfa_cee.c
@@ -16,15 +16,10 @@
* www.brocade.com
*/
-#include "bfa_defs_cna.h"
-#include "cna.h"
#include "bfa_cee.h"
#include "bfi_cna.h"
#include "bfa_ioc.h"
-#define bfa_ioc_portid(__ioc) ((__ioc)->port_id)
-#define bfa_lpuid(__arg) bfa_ioc_portid(&(__arg)->ioc)
-
static void bfa_cee_format_lldp_cfg(struct bfa_cee_lldp_cfg *lldp_cfg);
static void bfa_cee_format_cee_cfg(void *buffer);
@@ -223,44 +218,56 @@ bfa_cee_isr(void *cbarg, struct bfi_mbmsg *m)
}
/**
- * bfa_cee_hbfail()
+ * bfa_cee_notify()
*
* @brief CEE module heart-beat failure handler.
+ * @brief CEE module IOC event handler.
*
- * @param[in] Pointer to the CEE module data structure.
+ * @param[in] IOC event type
*
* @return void
*/
static void
-bfa_cee_hbfail(void *arg)
+bfa_cee_notify(void *arg, enum bfa_ioc_event event)
{
struct bfa_cee *cee;
cee = (struct bfa_cee *) arg;
- if (cee->get_attr_pending == true) {
- cee->get_attr_status = BFA_STATUS_FAILED;
- cee->get_attr_pending = false;
- if (cee->cbfn.get_attr_cbfn) {
- cee->cbfn.get_attr_cbfn(cee->cbfn.get_attr_cbarg,
- BFA_STATUS_FAILED);
+ switch (event) {
+ case BFA_IOC_E_DISABLED:
+ case BFA_IOC_E_FAILED:
+ if (cee->get_attr_pending == true) {
+ cee->get_attr_status = BFA_STATUS_FAILED;
+ cee->get_attr_pending = false;
+ if (cee->cbfn.get_attr_cbfn) {
+ cee->cbfn.get_attr_cbfn(
+ cee->cbfn.get_attr_cbarg,
+ BFA_STATUS_FAILED);
+ }
}
- }
- if (cee->get_stats_pending == true) {
- cee->get_stats_status = BFA_STATUS_FAILED;
- cee->get_stats_pending = false;
- if (cee->cbfn.get_stats_cbfn) {
- cee->cbfn.get_stats_cbfn(cee->cbfn.get_stats_cbarg,
- BFA_STATUS_FAILED);
+ if (cee->get_stats_pending == true) {
+ cee->get_stats_status = BFA_STATUS_FAILED;
+ cee->get_stats_pending = false;
+ if (cee->cbfn.get_stats_cbfn) {
+ cee->cbfn.get_stats_cbfn(
+ cee->cbfn.get_stats_cbarg,
+ BFA_STATUS_FAILED);
+ }
}
- }
- if (cee->reset_stats_pending == true) {
- cee->reset_stats_status = BFA_STATUS_FAILED;
- cee->reset_stats_pending = false;
- if (cee->cbfn.reset_stats_cbfn) {
- cee->cbfn.reset_stats_cbfn(cee->cbfn.reset_stats_cbarg,
- BFA_STATUS_FAILED);
+ if (cee->reset_stats_pending == true) {
+ cee->reset_stats_status = BFA_STATUS_FAILED;
+ cee->reset_stats_pending = false;
+ if (cee->cbfn.reset_stats_cbfn) {
+ cee->cbfn.reset_stats_cbfn(
+ cee->cbfn.reset_stats_cbarg,
+ BFA_STATUS_FAILED);
+ }
}
+ break;
+
+ default:
+ break;
}
}
@@ -286,6 +293,7 @@ bfa_nw_cee_attach(struct bfa_cee *cee, struct bfa_ioc *ioc,
cee->ioc = ioc;
bfa_nw_ioc_mbox_regisr(cee->ioc, BFI_MC_CEE, bfa_cee_isr, cee);
- bfa_ioc_hbfail_init(&cee->hbfail, bfa_cee_hbfail, cee);
- bfa_nw_ioc_hbfail_register(cee->ioc, &cee->hbfail);
+ bfa_q_qe_init(&cee->ioc_notify);
+ bfa_ioc_notify_init(&cee->ioc_notify, bfa_cee_notify, cee);
+ bfa_nw_ioc_notify_register(cee->ioc, &cee->ioc_notify);
}
diff --git a/drivers/net/bna/bfa_cee.h b/drivers/net/ethernet/brocade/bna/bfa_cee.h
index 20543d15b64f..58d54e98d595 100644
--- a/drivers/net/bna/bfa_cee.h
+++ b/drivers/net/ethernet/brocade/bna/bfa_cee.h
@@ -25,7 +25,6 @@
typedef void (*bfa_cee_get_attr_cbfn_t) (void *dev, enum bfa_status status);
typedef void (*bfa_cee_get_stats_cbfn_t) (void *dev, enum bfa_status status);
typedef void (*bfa_cee_reset_stats_cbfn_t) (void *dev, enum bfa_status status);
-typedef void (*bfa_cee_hbfail_cbfn_t) (void *dev, enum bfa_status status);
struct bfa_cee_cbfn {
bfa_cee_get_attr_cbfn_t get_attr_cbfn;
@@ -45,7 +44,7 @@ struct bfa_cee {
enum bfa_status get_stats_status;
enum bfa_status reset_stats_status;
struct bfa_cee_cbfn cbfn;
- struct bfa_ioc_hbfail_notify hbfail;
+ struct bfa_ioc_notify ioc_notify;
struct bfa_cee_attr *attr;
struct bfa_cee_stats *stats;
struct bfa_dma attr_dma;
diff --git a/drivers/net/bna/bfa_sm.h b/drivers/net/ethernet/brocade/bna/bfa_cs.h
index 46462c49b6f9..3da1a946ccdd 100644
--- a/drivers/net/bna/bfa_sm.h
+++ b/drivers/net/ethernet/brocade/bna/bfa_cs.h
@@ -11,20 +11,24 @@
* General Public License for more details.
*/
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
* All rights reserved
* www.brocade.com
*/
/**
- * @file bfasm.h State machine defines
+ * @file bfa_cs.h BFA common services
*/
-#ifndef __BFA_SM_H__
-#define __BFA_SM_H__
+#ifndef __BFA_CS_H__
+#define __BFA_CS_H__
#include "cna.h"
+/**
+ * @ BFA state machine interfaces
+ */
+
typedef void (*bfa_sm_t)(void *sm, int event);
/**
@@ -33,7 +37,7 @@ typedef void (*bfa_sm_t)(void *sm, int event);
* otype - object type, eg. struct bfa_ioc
* etype - object type, eg. enum ioc_event
*/
-#define bfa_sm_state_decl(oc, st, otype, etype) \
+#define bfa_sm_state_decl(oc, st, otype, etype) \
static void oc ## _sm_ ## st(otype * fsm, etype event)
#define bfa_sm_set_state(_sm, _state) ((_sm)->sm = (bfa_sm_t)(_state))
@@ -49,7 +53,7 @@ struct bfa_sm_table {
int state; /*!< state machine encoding */
char *name; /*!< state name for display */
};
-#define BFA_SM(_sm) ((bfa_sm_t)(_sm))
+#define BFA_SM(_sm) ((bfa_sm_t)(_sm))
/**
* State machine with entry actions.
@@ -62,18 +66,18 @@ typedef void (*bfa_fsm_t)(void *fsm, int event);
* otype - object type, eg. struct bfa_ioc
* etype - object type, eg. enum ioc_event
*/
-#define bfa_fsm_state_decl(oc, st, otype, etype) \
- static void oc ## _sm_ ## st(otype * fsm, etype event); \
+#define bfa_fsm_state_decl(oc, st, otype, etype) \
+ static void oc ## _sm_ ## st(otype * fsm, etype event); \
static void oc ## _sm_ ## st ## _entry(otype * fsm)
-#define bfa_fsm_set_state(_fsm, _state) do { \
- (_fsm)->fsm = (bfa_fsm_t)(_state); \
- _state ## _entry(_fsm); \
+#define bfa_fsm_set_state(_fsm, _state) do { \
+ (_fsm)->fsm = (bfa_fsm_t)(_state); \
+ _state ## _entry(_fsm); \
} while (0)
#define bfa_fsm_send_event(_fsm, _event) ((_fsm)->fsm((_fsm), (_event)))
#define bfa_fsm_get_state(_fsm) ((_fsm)->fsm)
-#define bfa_fsm_cmp_state(_fsm, _state) \
+#define bfa_fsm_cmp_state(_fsm, _state) \
((_fsm)->fsm == (bfa_fsm_t)(_state))
static inline int
@@ -85,4 +89,52 @@ bfa_sm_to_state(const struct bfa_sm_table *smt, bfa_sm_t sm)
i++;
return smt[i].state;
}
-#endif
+
+/**
+ * @ Generic wait counter.
+ */
+
+typedef void (*bfa_wc_resume_t) (void *cbarg);
+
+struct bfa_wc {
+ bfa_wc_resume_t wc_resume;
+ void *wc_cbarg;
+ int wc_count;
+};
+
+static inline void
+bfa_wc_up(struct bfa_wc *wc)
+{
+ wc->wc_count++;
+}
+
+static inline void
+bfa_wc_down(struct bfa_wc *wc)
+{
+ wc->wc_count--;
+ if (wc->wc_count == 0)
+ wc->wc_resume(wc->wc_cbarg);
+}
+
+/**
+ * Initialize a waiting counter.
+ */
+static inline void
+bfa_wc_init(struct bfa_wc *wc, bfa_wc_resume_t wc_resume, void *wc_cbarg)
+{
+ wc->wc_resume = wc_resume;
+ wc->wc_cbarg = wc_cbarg;
+ wc->wc_count = 0;
+ bfa_wc_up(wc);
+}
+
+/**
+ * Wait for counter to reach zero
+ */
+static inline void
+bfa_wc_wait(struct bfa_wc *wc)
+{
+ bfa_wc_down(wc);
+}
+
+#endif /* __BFA_CS_H__ */
diff --git a/drivers/net/bna/bfa_defs.h b/drivers/net/ethernet/brocade/bna/bfa_defs.h
index 2ea0dfe1cedc..2f12d68021d5 100644
--- a/drivers/net/bna/bfa_defs.h
+++ b/drivers/net/ethernet/brocade/bna/bfa_defs.h
@@ -80,7 +80,7 @@ struct bfa_adapter_attr {
enum {
BFA_IOC_DRIVER_LEN = 16,
- BFA_IOC_CHIP_REV_LEN = 8,
+ BFA_IOC_CHIP_REV_LEN = 8,
};
/**
@@ -124,6 +124,7 @@ enum bfa_ioc_state {
BFA_IOC_DISABLED = 10, /*!< IOC is disabled */
BFA_IOC_FWMISMATCH = 11, /*!< IOC f/w different from drivers */
BFA_IOC_ENABLING = 12, /*!< IOC is being enabled */
+ BFA_IOC_HWFAIL = 13, /*!< PCI mapping doesn't exist */
};
/**
@@ -153,6 +154,7 @@ struct bfa_ioc_drv_stats {
u32 enable_reqs;
u32 disable_replies;
u32 enable_replies;
+ u32 rsvd;
};
/**
@@ -174,12 +176,24 @@ enum bfa_ioc_type {
*/
struct bfa_ioc_attr {
enum bfa_ioc_type ioc_type;
- enum bfa_ioc_state state; /*!< IOC state */
+ enum bfa_ioc_state state; /*!< IOC state */
struct bfa_adapter_attr adapter_attr; /*!< HBA attributes */
struct bfa_ioc_driver_attr driver_attr; /*!< driver attr */
struct bfa_ioc_pci_attr pci_attr;
- u8 port_id; /*!< port number */
- u8 rsvd[7]; /*!< 64bit align */
+ u8 port_id; /*!< port number */
+ u8 port_mode; /*!< enum bfa_mode */
+ u8 cap_bm; /*!< capability */
+ u8 port_mode_cfg; /*!< enum bfa_mode */
+ u8 rsvd[4]; /*!< 64bit align */
+};
+
+/**
+ * Adapter capability mask definition
+ */
+enum {
+ BFA_CM_HBA = 0x01,
+ BFA_CM_CNA = 0x02,
+ BFA_CM_NIC = 0x04,
};
/**
@@ -227,8 +241,18 @@ struct bfa_mfg_block {
mac_t mfg_mac; /*!< mac address */
u8 num_mac; /*!< number of mac addresses */
u8 rsv2;
- u32 mfg_type; /*!< card type */
- u8 rsv3[108];
+ u32 card_type; /*!< card type */
+ char cap_nic; /*!< capability nic */
+ char cap_cna; /*!< capability cna */
+ char cap_hba; /*!< capability hba */
+ char cap_fc16g; /*!< capability fc 16g */
+ char cap_sriov; /*!< capability sriov */
+ char cap_mezz; /*!< capability mezz */
+ u8 rsv3;
+ u8 mfg_nports; /*!< number of ports */
+ char media[8]; /*!< xfi/xaui */
+ char initial_mode[8];/*!< initial mode: hba/cna/nic */
+ u8 rsv4[84];
u8 md5_chksum[BFA_MFG_CHKSUM_SIZE]; /*!< md5 checksum */
};
@@ -238,8 +262,35 @@ struct bfa_mfg_block {
* ---------------------- pci definitions ------------
*/
-#define bfa_asic_id_ct(devid) \
- ((devid) == PCI_DEVICE_ID_BROCADE_CT || \
- (devid) == PCI_DEVICE_ID_BROCADE_CT_FC)
+/*
+ * PCI device ID information
+ */
+enum {
+ BFA_PCI_DEVICE_ID_CT2 = 0x22,
+};
+
+#define bfa_asic_id_ct(device) \
+ ((device) == PCI_DEVICE_ID_BROCADE_CT || \
+ (device) == PCI_DEVICE_ID_BROCADE_CT_FC)
+#define bfa_asic_id_ct2(device) \
+ ((device) == BFA_PCI_DEVICE_ID_CT2)
+#define bfa_asic_id_ctc(device) \
+ (bfa_asic_id_ct(device) || bfa_asic_id_ct2(device))
+
+/**
+ * PCI sub-system device and vendor ID information
+ */
+enum {
+ BFA_PCI_FCOE_SSDEVICE_ID = 0x14,
+ BFA_PCI_CT2_SSID_FCoE = 0x22,
+ BFA_PCI_CT2_SSID_ETH = 0x23,
+ BFA_PCI_CT2_SSID_FC = 0x24,
+};
+
+enum bfa_mode {
+ BFA_MODE_HBA = 1,
+ BFA_MODE_CNA = 2,
+ BFA_MODE_NIC = 3
+};
#endif /* __BFA_DEFS_H__ */
diff --git a/drivers/net/bna/bfa_defs_cna.h b/drivers/net/ethernet/brocade/bna/bfa_defs_cna.h
index 7e0a9187bdd5..8ab33ee2c2bc 100644
--- a/drivers/net/bna/bfa_defs_cna.h
+++ b/drivers/net/ethernet/brocade/bna/bfa_defs_cna.h
@@ -15,7 +15,6 @@
* All rights reserved
* www.brocade.com
*/
-
#ifndef __BFA_DEFS_CNA_H__
#define __BFA_DEFS_CNA_H__
@@ -55,6 +54,9 @@ struct bfa_port_fc_stats {
u64 bad_os_count; /*!< Invalid ordered sets */
u64 err_enc_out; /*!< Encoding err nonframe_8b10b */
u64 err_enc; /*!< Encoding err frame_8b10b */
+ u64 bbsc_frames_lost; /*!< Credit Recovery-Frames Lost */
+ u64 bbsc_credits_lost; /*!< Credit Recovery-Credits Lost */
+ u64 bbsc_link_resets; /*!< Credit Recovery-Link Resets */
};
/**
@@ -100,6 +102,10 @@ struct bfa_port_eth_stats {
u64 rx_fcoe_zero_pause; /*!< Rx FCoE zero pause */
u64 tx_fcoe_pause; /*!< Tx FCoE pause */
u64 tx_fcoe_zero_pause; /*!< Tx FCoE zero pause */
+ u64 rx_iscsi_pause; /*!< Rx iSCSI pause */
+ u64 rx_iscsi_zero_pause; /*!< Rx iSCSI zero pause */
+ u64 tx_iscsi_pause; /*!< Tx iSCSI pause */
+ u64 tx_iscsi_zero_pause; /*!< Tx iSCSI zero pause */
};
/**
diff --git a/drivers/net/bna/bfa_defs_mfg_comm.h b/drivers/net/ethernet/brocade/bna/bfa_defs_mfg_comm.h
index fdd677618361..6681fe87c1e1 100644
--- a/drivers/net/bna/bfa_defs_mfg_comm.h
+++ b/drivers/net/ethernet/brocade/bna/bfa_defs_mfg_comm.h
@@ -18,12 +18,12 @@
#ifndef __BFA_DEFS_MFG_COMM_H__
#define __BFA_DEFS_MFG_COMM_H__
-#include "cna.h"
+#include "bfa_defs.h"
/**
* Manufacturing block version
*/
-#define BFA_MFG_VERSION 2
+#define BFA_MFG_VERSION 3
#define BFA_MFG_VERSION_UNINIT 0xFF
/**
@@ -60,20 +60,17 @@ enum {
BFA_MFG_TYPE_ASTRA = 807, /*!< Astra mezz card */
BFA_MFG_TYPE_LIGHTNING_P0 = 902, /*!< Lightning mezz card - old */
BFA_MFG_TYPE_LIGHTNING = 1741, /*!< Lightning mezz card */
+ BFA_MFG_TYPE_PROWLER_F = 1560, /*!< Prowler FC only cards */
+ BFA_MFG_TYPE_PROWLER_N = 1410, /*!< Prowler NIC only cards */
+ BFA_MFG_TYPE_PROWLER_C = 1710, /*!< Prowler CNA only cards */
+ BFA_MFG_TYPE_PROWLER_D = 1860, /*!< Prowler Dual cards */
+ BFA_MFG_TYPE_CHINOOK = 1867, /*!< Chinook cards */
BFA_MFG_TYPE_INVALID = 0, /*!< Invalid card type */
};
#pragma pack(1)
/**
- * Check if 1-port card
- */
-#define bfa_mfg_is_1port(type) (( \
- (type) == BFA_MFG_TYPE_FC8P1 || \
- (type) == BFA_MFG_TYPE_FC4P1 || \
- (type) == BFA_MFG_TYPE_CNA10P1))
-
-/**
* Check if Mezz card
*/
#define bfa_mfg_is_mezz(type) (( \
@@ -81,56 +78,8 @@ enum {
(type) == BFA_MFG_TYPE_WANCHESE || \
(type) == BFA_MFG_TYPE_ASTRA || \
(type) == BFA_MFG_TYPE_LIGHTNING_P0 || \
- (type) == BFA_MFG_TYPE_LIGHTNING))
-
-/**
- * Check if card type valid
- */
-#define bfa_mfg_is_card_type_valid(type) (( \
- (type) == BFA_MFG_TYPE_FC8P2 || \
- (type) == BFA_MFG_TYPE_FC8P1 || \
- (type) == BFA_MFG_TYPE_FC4P2 || \
- (type) == BFA_MFG_TYPE_FC4P1 || \
- (type) == BFA_MFG_TYPE_CNA10P2 || \
- (type) == BFA_MFG_TYPE_CNA10P1 || \
- bfa_mfg_is_mezz(type)))
-
-#define bfa_mfg_adapter_prop_init_flash(card_type, prop) \
-do { \
- switch ((card_type)) { \
- case BFA_MFG_TYPE_FC8P2: \
- case BFA_MFG_TYPE_JAYHAWK: \
- case BFA_MFG_TYPE_ASTRA: \
- (prop) = BFI_ADAPTER_SETP(NPORTS, 2) | \
- BFI_ADAPTER_SETP(SPEED, 8); \
- break; \
- case BFA_MFG_TYPE_FC8P1: \
- (prop) = BFI_ADAPTER_SETP(NPORTS, 1) | \
- BFI_ADAPTER_SETP(SPEED, 8); \
- break; \
- case BFA_MFG_TYPE_FC4P2: \
- (prop) = BFI_ADAPTER_SETP(NPORTS, 2) | \
- BFI_ADAPTER_SETP(SPEED, 4); \
- break; \
- case BFA_MFG_TYPE_FC4P1: \
- (prop) = BFI_ADAPTER_SETP(NPORTS, 1) | \
- BFI_ADAPTER_SETP(SPEED, 4); \
- break; \
- case BFA_MFG_TYPE_CNA10P2: \
- case BFA_MFG_TYPE_WANCHESE: \
- case BFA_MFG_TYPE_LIGHTNING_P0: \
- case BFA_MFG_TYPE_LIGHTNING: \
- (prop) = BFI_ADAPTER_SETP(NPORTS, 2); \
- (prop) |= BFI_ADAPTER_SETP(SPEED, 10); \
- break; \
- case BFA_MFG_TYPE_CNA10P1: \
- (prop) = BFI_ADAPTER_SETP(NPORTS, 1); \
- (prop) |= BFI_ADAPTER_SETP(SPEED, 10); \
- break; \
- default: \
- (prop) = BFI_ADAPTER_UNSUPP; \
- } \
-} while (0)
+ (type) == BFA_MFG_TYPE_LIGHTNING || \
+ (type) == BFA_MFG_TYPE_CHINOOK))
enum {
CB_GPIO_TTV = (1), /*!< TTV debug capable cards */
@@ -192,14 +141,14 @@ do { \
* VPD vendor tag
*/
enum {
- BFA_MFG_VPD_UNKNOWN = 0, /*!< vendor unknown */
- BFA_MFG_VPD_IBM = 1, /*!< vendor IBM */
- BFA_MFG_VPD_HP = 2, /*!< vendor HP */
- BFA_MFG_VPD_DELL = 3, /*!< vendor DELL */
- BFA_MFG_VPD_PCI_IBM = 0x08, /*!< PCI VPD IBM */
- BFA_MFG_VPD_PCI_HP = 0x10, /*!< PCI VPD HP */
- BFA_MFG_VPD_PCI_DELL = 0x20, /*!< PCI VPD DELL */
- BFA_MFG_VPD_PCI_BRCD = 0xf8, /*!< PCI VPD Brocade */
+ BFA_MFG_VPD_UNKNOWN = 0, /*!< vendor unknown */
+ BFA_MFG_VPD_IBM = 1, /*!< vendor IBM */
+ BFA_MFG_VPD_HP = 2, /*!< vendor HP */
+ BFA_MFG_VPD_DELL = 3, /*!< vendor DELL */
+ BFA_MFG_VPD_PCI_IBM = 0x08, /*!< PCI VPD IBM */
+ BFA_MFG_VPD_PCI_HP = 0x10, /*!< PCI VPD HP */
+ BFA_MFG_VPD_PCI_DELL = 0x20, /*!< PCI VPD DELL */
+ BFA_MFG_VPD_PCI_BRCD = 0xf8, /*!< PCI VPD Brocade */
};
/**
@@ -212,8 +161,8 @@ struct bfa_mfg_vpd {
u8 vpd_sig[3]; /*!< characters 'V', 'P', 'D' */
u8 chksum; /*!< u8 checksum */
u8 vendor; /*!< vendor */
- u8 len; /*!< vpd data length excluding header */
- u8 rsv;
+ u8 len; /*!< vpd data length excluding header */
+ u8 rsv;
u8 data[BFA_MFG_VPD_LEN]; /*!< vpd data */
};
diff --git a/drivers/net/bna/bfa_defs_status.h b/drivers/net/ethernet/brocade/bna/bfa_defs_status.h
index af951126375c..7c5fe6c2e80e 100644
--- a/drivers/net/bna/bfa_defs_status.h
+++ b/drivers/net/ethernet/brocade/bna/bfa_defs_status.h
@@ -25,95 +25,95 @@
* comments are supported
*/
enum bfa_status {
- BFA_STATUS_OK = 0,
- BFA_STATUS_FAILED = 1,
- BFA_STATUS_EINVAL = 2,
- BFA_STATUS_ENOMEM = 3,
- BFA_STATUS_ENOSYS = 4,
- BFA_STATUS_ETIMER = 5,
- BFA_STATUS_EPROTOCOL = 6,
- BFA_STATUS_ENOFCPORTS = 7,
- BFA_STATUS_NOFLASH = 8,
- BFA_STATUS_BADFLASH = 9,
- BFA_STATUS_SFP_UNSUPP = 10,
+ BFA_STATUS_OK = 0,
+ BFA_STATUS_FAILED = 1,
+ BFA_STATUS_EINVAL = 2,
+ BFA_STATUS_ENOMEM = 3,
+ BFA_STATUS_ENOSYS = 4,
+ BFA_STATUS_ETIMER = 5,
+ BFA_STATUS_EPROTOCOL = 6,
+ BFA_STATUS_ENOFCPORTS = 7,
+ BFA_STATUS_NOFLASH = 8,
+ BFA_STATUS_BADFLASH = 9,
+ BFA_STATUS_SFP_UNSUPP = 10,
BFA_STATUS_UNKNOWN_VFID = 11,
BFA_STATUS_DATACORRUPTED = 12,
- BFA_STATUS_DEVBUSY = 13,
- BFA_STATUS_ABORTED = 14,
- BFA_STATUS_NODEV = 15,
- BFA_STATUS_HDMA_FAILED = 16,
+ BFA_STATUS_DEVBUSY = 13,
+ BFA_STATUS_ABORTED = 14,
+ BFA_STATUS_NODEV = 15,
+ BFA_STATUS_HDMA_FAILED = 16,
BFA_STATUS_FLASH_BAD_LEN = 17,
BFA_STATUS_UNKNOWN_LWWN = 18,
BFA_STATUS_UNKNOWN_RWWN = 19,
- BFA_STATUS_FCPT_LS_RJT = 20,
+ BFA_STATUS_FCPT_LS_RJT = 20,
BFA_STATUS_VPORT_EXISTS = 21,
- BFA_STATUS_VPORT_MAX = 22,
+ BFA_STATUS_VPORT_MAX = 22,
BFA_STATUS_UNSUPP_SPEED = 23,
- BFA_STATUS_INVLD_DFSZ = 24,
- BFA_STATUS_CNFG_FAILED = 25,
- BFA_STATUS_CMD_NOTSUPP = 26,
- BFA_STATUS_NO_ADAPTER = 27,
- BFA_STATUS_LINKDOWN = 28,
- BFA_STATUS_FABRIC_RJT = 29,
+ BFA_STATUS_INVLD_DFSZ = 24,
+ BFA_STATUS_CNFG_FAILED = 25,
+ BFA_STATUS_CMD_NOTSUPP = 26,
+ BFA_STATUS_NO_ADAPTER = 27,
+ BFA_STATUS_LINKDOWN = 28,
+ BFA_STATUS_FABRIC_RJT = 29,
BFA_STATUS_UNKNOWN_VWWN = 30,
BFA_STATUS_NSLOGIN_FAILED = 31,
- BFA_STATUS_NO_RPORTS = 32,
+ BFA_STATUS_NO_RPORTS = 32,
BFA_STATUS_NSQUERY_FAILED = 33,
BFA_STATUS_PORT_OFFLINE = 34,
BFA_STATUS_RPORT_OFFLINE = 35,
BFA_STATUS_TGTOPEN_FAILED = 36,
- BFA_STATUS_BAD_LUNS = 37,
- BFA_STATUS_IO_FAILURE = 38,
- BFA_STATUS_NO_FABRIC = 39,
- BFA_STATUS_EBADF = 40,
- BFA_STATUS_EINTR = 41,
- BFA_STATUS_EIO = 42,
- BFA_STATUS_ENOTTY = 43,
- BFA_STATUS_ENXIO = 44,
- BFA_STATUS_EFOPEN = 45,
+ BFA_STATUS_BAD_LUNS = 37,
+ BFA_STATUS_IO_FAILURE = 38,
+ BFA_STATUS_NO_FABRIC = 39,
+ BFA_STATUS_EBADF = 40,
+ BFA_STATUS_EINTR = 41,
+ BFA_STATUS_EIO = 42,
+ BFA_STATUS_ENOTTY = 43,
+ BFA_STATUS_ENXIO = 44,
+ BFA_STATUS_EFOPEN = 45,
BFA_STATUS_VPORT_WWN_BP = 46,
BFA_STATUS_PORT_NOT_DISABLED = 47,
- BFA_STATUS_BADFRMHDR = 48,
- BFA_STATUS_BADFRMSZ = 49,
- BFA_STATUS_MISSINGFRM = 50,
- BFA_STATUS_LINKTIMEOUT = 51,
+ BFA_STATUS_BADFRMHDR = 48,
+ BFA_STATUS_BADFRMSZ = 49,
+ BFA_STATUS_MISSINGFRM = 50,
+ BFA_STATUS_LINKTIMEOUT = 51,
BFA_STATUS_NO_FCPIM_NEXUS = 52,
BFA_STATUS_CHECKSUM_FAIL = 53,
- BFA_STATUS_GZME_FAILED = 54,
+ BFA_STATUS_GZME_FAILED = 54,
BFA_STATUS_SCSISTART_REQD = 55,
- BFA_STATUS_IOC_FAILURE = 56,
- BFA_STATUS_INVALID_WWN = 57,
- BFA_STATUS_MISMATCH = 58,
- BFA_STATUS_IOC_ENABLED = 59,
+ BFA_STATUS_IOC_FAILURE = 56,
+ BFA_STATUS_INVALID_WWN = 57,
+ BFA_STATUS_MISMATCH = 58,
+ BFA_STATUS_IOC_ENABLED = 59,
BFA_STATUS_ADAPTER_ENABLED = 60,
- BFA_STATUS_IOC_NON_OP = 61,
+ BFA_STATUS_IOC_NON_OP = 61,
BFA_STATUS_ADDR_MAP_FAILURE = 62,
- BFA_STATUS_SAME_NAME = 63,
- BFA_STATUS_PENDING = 64,
- BFA_STATUS_8G_SPD = 65,
- BFA_STATUS_4G_SPD = 66,
+ BFA_STATUS_SAME_NAME = 63,
+ BFA_STATUS_PENDING = 64,
+ BFA_STATUS_8G_SPD = 65,
+ BFA_STATUS_4G_SPD = 66,
BFA_STATUS_AD_IS_ENABLE = 67,
- BFA_STATUS_EINVAL_TOV = 68,
+ BFA_STATUS_EINVAL_TOV = 68,
BFA_STATUS_EINVAL_QDEPTH = 69,
BFA_STATUS_VERSION_FAIL = 70,
- BFA_STATUS_DIAG_BUSY = 71,
- BFA_STATUS_BEACON_ON = 72,
- BFA_STATUS_BEACON_OFF = 73,
- BFA_STATUS_LBEACON_ON = 74,
- BFA_STATUS_LBEACON_OFF = 75,
+ BFA_STATUS_DIAG_BUSY = 71,
+ BFA_STATUS_BEACON_ON = 72,
+ BFA_STATUS_BEACON_OFF = 73,
+ BFA_STATUS_LBEACON_ON = 74,
+ BFA_STATUS_LBEACON_OFF = 75,
BFA_STATUS_PORT_NOT_INITED = 76,
BFA_STATUS_RPSC_ENABLED = 77,
- BFA_STATUS_ENOFSAVE = 78,
- BFA_STATUS_BAD_FILE = 79,
- BFA_STATUS_RLIM_EN = 80,
- BFA_STATUS_RLIM_DIS = 81,
- BFA_STATUS_IOC_DISABLED = 82,
- BFA_STATUS_ADAPTER_DISABLED = 83,
- BFA_STATUS_BIOS_DISABLED = 84,
- BFA_STATUS_AUTH_ENABLED = 85,
- BFA_STATUS_AUTH_DISABLED = 86,
- BFA_STATUS_ERROR_TRL_ENABLED = 87,
- BFA_STATUS_ERROR_QOS_ENABLED = 88,
+ BFA_STATUS_ENOFSAVE = 78,
+ BFA_STATUS_BAD_FILE = 79,
+ BFA_STATUS_RLIM_EN = 80,
+ BFA_STATUS_RLIM_DIS = 81,
+ BFA_STATUS_IOC_DISABLED = 82,
+ BFA_STATUS_ADAPTER_DISABLED = 83,
+ BFA_STATUS_BIOS_DISABLED = 84,
+ BFA_STATUS_AUTH_ENABLED = 85,
+ BFA_STATUS_AUTH_DISABLED = 86,
+ BFA_STATUS_ERROR_TRL_ENABLED = 87,
+ BFA_STATUS_ERROR_QOS_ENABLED = 88,
BFA_STATUS_NO_SFP_DEV = 89,
BFA_STATUS_MEMTEST_FAILED = 90,
BFA_STATUS_INVALID_DEVID = 91,
@@ -190,7 +190,7 @@ enum bfa_status {
BFA_STATUS_FLASH_CKFAIL = 162,
BFA_STATUS_TRUNK_UNSUPP = 163,
BFA_STATUS_TRUNK_ENABLED = 164,
- BFA_STATUS_TRUNK_DISABLED = 165,
+ BFA_STATUS_TRUNK_DISABLED = 165,
BFA_STATUS_TRUNK_ERROR_TRL_ENABLED = 166,
BFA_STATUS_BOOT_CODE_UPDATED = 167,
BFA_STATUS_BOOT_VERSION = 168,
@@ -198,8 +198,8 @@ enum bfa_status {
BFA_STATUS_INVALID_CARDTYPE = 170,
BFA_STATUS_NO_TOPOLOGY_FOR_CNA = 171,
BFA_STATUS_IM_VLAN_OVER_TEAM_DELETE_FAILED = 172,
- BFA_STATUS_ETHBOOT_ENABLED = 173,
- BFA_STATUS_ETHBOOT_DISABLED = 174,
+ BFA_STATUS_ETHBOOT_ENABLED = 173,
+ BFA_STATUS_ETHBOOT_DISABLED = 174,
BFA_STATUS_IOPROFILE_OFF = 175,
BFA_STATUS_NO_PORT_INSTANCE = 176,
BFA_STATUS_BOOT_CODE_TIMEDOUT = 177,
diff --git a/drivers/net/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c
index fcb9bb3169e0..b0307a00a109 100644
--- a/drivers/net/bna/bfa_ioc.c
+++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c
@@ -17,9 +17,7 @@
*/
#include "bfa_ioc.h"
-#include "cna.h"
-#include "bfi.h"
-#include "bfi_ctreg.h"
+#include "bfi_reg.h"
#include "bfa_defs.h"
/**
@@ -58,9 +56,11 @@ static bool bfa_nw_auto_recover = true;
/*
* forward declarations
*/
+static void bfa_ioc_hw_sem_init(struct bfa_ioc *ioc);
static void bfa_ioc_hw_sem_get(struct bfa_ioc *ioc);
static void bfa_ioc_hw_sem_get_cancel(struct bfa_ioc *ioc);
static void bfa_ioc_hwinit(struct bfa_ioc *ioc, bool force);
+static void bfa_ioc_poll_fwinit(struct bfa_ioc *ioc);
static void bfa_ioc_send_enable(struct bfa_ioc *ioc);
static void bfa_ioc_send_disable(struct bfa_ioc *ioc);
static void bfa_ioc_send_getattr(struct bfa_ioc *ioc);
@@ -68,16 +68,17 @@ static void bfa_ioc_hb_monitor(struct bfa_ioc *ioc);
static void bfa_ioc_hb_stop(struct bfa_ioc *ioc);
static void bfa_ioc_reset(struct bfa_ioc *ioc, bool force);
static void bfa_ioc_mbox_poll(struct bfa_ioc *ioc);
-static void bfa_ioc_mbox_hbfail(struct bfa_ioc *ioc);
+static void bfa_ioc_mbox_flush(struct bfa_ioc *ioc);
static void bfa_ioc_recover(struct bfa_ioc *ioc);
static void bfa_ioc_check_attr_wwns(struct bfa_ioc *ioc);
+static void bfa_ioc_event_notify(struct bfa_ioc *, enum bfa_ioc_event);
static void bfa_ioc_disable_comp(struct bfa_ioc *ioc);
static void bfa_ioc_lpu_stop(struct bfa_ioc *ioc);
static void bfa_ioc_fail_notify(struct bfa_ioc *ioc);
static void bfa_ioc_pf_enabled(struct bfa_ioc *ioc);
static void bfa_ioc_pf_disabled(struct bfa_ioc *ioc);
-static void bfa_ioc_pf_initfailed(struct bfa_ioc *ioc);
static void bfa_ioc_pf_failed(struct bfa_ioc *ioc);
+static void bfa_ioc_pf_hwfailed(struct bfa_ioc *ioc);
static void bfa_ioc_pf_fwmismatch(struct bfa_ioc *ioc);
static void bfa_ioc_boot(struct bfa_ioc *ioc, u32 boot_type,
u32 boot_param);
@@ -106,11 +107,11 @@ enum ioc_event {
IOC_E_ENABLED = 5, /*!< f/w enabled */
IOC_E_FWRSP_GETATTR = 6, /*!< IOC get attribute response */
IOC_E_DISABLED = 7, /*!< f/w disabled */
- IOC_E_INITFAILED = 8, /*!< failure notice by iocpf sm */
- IOC_E_PFAILED = 9, /*!< failure notice by iocpf sm */
- IOC_E_HBFAIL = 10, /*!< heartbeat failure */
- IOC_E_HWERROR = 11, /*!< hardware error interrupt */
- IOC_E_TIMEOUT = 12, /*!< timeout */
+ IOC_E_PFFAILED = 8, /*!< failure notice by iocpf sm */
+ IOC_E_HBFAIL = 9, /*!< heartbeat failure */
+ IOC_E_HWERROR = 10, /*!< hardware error interrupt */
+ IOC_E_TIMEOUT = 11, /*!< timeout */
+ IOC_E_HWFAILED = 12, /*!< PCI mapping failure notice */
};
bfa_fsm_state_decl(bfa_ioc, uninit, struct bfa_ioc, enum ioc_event);
@@ -122,6 +123,7 @@ bfa_fsm_state_decl(bfa_ioc, fail_retry, struct bfa_ioc, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, fail, struct bfa_ioc, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, disabling, struct bfa_ioc, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, disabled, struct bfa_ioc, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, hwfail, struct bfa_ioc, enum ioc_event);
static struct bfa_sm_table ioc_sm_table[] = {
{BFA_SM(bfa_ioc_sm_uninit), BFA_IOC_UNINIT},
@@ -133,12 +135,9 @@ static struct bfa_sm_table ioc_sm_table[] = {
{BFA_SM(bfa_ioc_sm_fail), BFA_IOC_FAIL},
{BFA_SM(bfa_ioc_sm_disabling), BFA_IOC_DISABLING},
{BFA_SM(bfa_ioc_sm_disabled), BFA_IOC_DISABLED},
+ {BFA_SM(bfa_ioc_sm_hwfail), BFA_IOC_HWFAIL},
};
-/**
- * IOCPF state machine definitions/declarations
- */
-
/*
* Forward declareations for iocpf state machine
*/
@@ -156,7 +155,7 @@ enum iocpf_event {
IOCPF_E_ENABLE = 1, /*!< IOCPF enable request */
IOCPF_E_DISABLE = 2, /*!< IOCPF disable request */
IOCPF_E_STOP = 3, /*!< stop on driver detach */
- IOCPF_E_FWREADY = 4, /*!< f/w initialization done */
+ IOCPF_E_FWREADY = 4, /*!< f/w initialization done */
IOCPF_E_FWRSP_ENABLE = 5, /*!< enable f/w response */
IOCPF_E_FWRSP_DISABLE = 6, /*!< disable f/w response */
IOCPF_E_FAIL = 7, /*!< failure notice by ioc sm */
@@ -164,6 +163,7 @@ enum iocpf_event {
IOCPF_E_GETATTRFAIL = 9, /*!< init fail notice by ioc sm */
IOCPF_E_SEMLOCKED = 10, /*!< h/w semaphore is locked */
IOCPF_E_TIMEOUT = 11, /*!< f/w response timeout */
+ IOCPF_E_SEM_ERROR = 12, /*!< h/w sem mapping error */
};
/**
@@ -239,7 +239,7 @@ bfa_ioc_sm_uninit(struct bfa_ioc *ioc, enum ioc_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -272,7 +272,7 @@ bfa_ioc_sm_reset(struct bfa_ioc *ioc, enum ioc_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -294,15 +294,20 @@ bfa_ioc_sm_enabling(struct bfa_ioc *ioc, enum ioc_event event)
bfa_fsm_set_state(ioc, bfa_ioc_sm_getattr);
break;
- case IOC_E_PFAILED:
+ case IOC_E_PFFAILED:
/* !!! fall through !!! */
case IOC_E_HWERROR:
ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
- bfa_fsm_set_state(ioc, bfa_ioc_sm_fail_retry);
- if (event != IOC_E_PFAILED)
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
+ if (event != IOC_E_PFFAILED)
bfa_iocpf_initfail(ioc);
break;
+ case IOC_E_HWFAILED:
+ ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_hwfail);
+ break;
+
case IOC_E_DISABLE:
bfa_fsm_set_state(ioc, bfa_ioc_sm_disabling);
break;
@@ -316,7 +321,7 @@ bfa_ioc_sm_enabling(struct bfa_ioc *ioc, enum ioc_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -341,17 +346,18 @@ bfa_ioc_sm_getattr(struct bfa_ioc *ioc, enum ioc_event event)
case IOC_E_FWRSP_GETATTR:
del_timer(&ioc->ioc_timer);
bfa_ioc_check_attr_wwns(ioc);
+ bfa_ioc_hb_monitor(ioc);
bfa_fsm_set_state(ioc, bfa_ioc_sm_op);
break;
- case IOC_E_PFAILED:
+ case IOC_E_PFFAILED:
case IOC_E_HWERROR:
del_timer(&ioc->ioc_timer);
/* fall through */
case IOC_E_TIMEOUT:
ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
- bfa_fsm_set_state(ioc, bfa_ioc_sm_fail_retry);
- if (event != IOC_E_PFAILED)
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
+ if (event != IOC_E_PFFAILED)
bfa_iocpf_getattrfail(ioc);
break;
@@ -364,7 +370,7 @@ bfa_ioc_sm_getattr(struct bfa_ioc *ioc, enum ioc_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -372,7 +378,7 @@ static void
bfa_ioc_sm_op_entry(struct bfa_ioc *ioc)
{
ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_OK);
- bfa_ioc_hb_monitor(ioc);
+ bfa_ioc_event_notify(ioc, BFA_IOC_E_ENABLED);
}
static void
@@ -387,23 +393,24 @@ bfa_ioc_sm_op(struct bfa_ioc *ioc, enum ioc_event event)
bfa_fsm_set_state(ioc, bfa_ioc_sm_disabling);
break;
- case IOC_E_PFAILED:
+ case IOC_E_PFFAILED:
case IOC_E_HWERROR:
bfa_ioc_hb_stop(ioc);
/* !!! fall through !!! */
case IOC_E_HBFAIL:
- bfa_ioc_fail_notify(ioc);
if (ioc->iocpf.auto_recover)
bfa_fsm_set_state(ioc, bfa_ioc_sm_fail_retry);
else
bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
- if (event != IOC_E_PFAILED)
+ bfa_ioc_fail_notify(ioc);
+
+ if (event != IOC_E_PFFAILED)
bfa_iocpf_fail(ioc);
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -414,7 +421,7 @@ bfa_ioc_sm_disabling_entry(struct bfa_ioc *ioc)
}
/**
- * IOC is being desabled
+ * IOC is being disabled
*/
static void
bfa_ioc_sm_disabling(struct bfa_ioc *ioc, enum ioc_event event)
@@ -433,13 +440,18 @@ bfa_ioc_sm_disabling(struct bfa_ioc *ioc, enum ioc_event event)
bfa_iocpf_fail(ioc);
break;
+ case IOC_E_HWFAILED:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_hwfail);
+ bfa_ioc_disable_comp(ioc);
+ break;
+
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
/**
- * IOC desable completion entry.
+ * IOC disable completion entry.
*/
static void
bfa_ioc_sm_disabled_entry(struct bfa_ioc *ioc)
@@ -465,7 +477,7 @@ bfa_ioc_sm_disabled(struct bfa_ioc *ioc, enum ioc_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -485,18 +497,20 @@ bfa_ioc_sm_fail_retry(struct bfa_ioc *ioc, enum ioc_event event)
bfa_fsm_set_state(ioc, bfa_ioc_sm_getattr);
break;
- case IOC_E_PFAILED:
+ case IOC_E_PFFAILED:
case IOC_E_HWERROR:
/**
* Initialization retry failed.
*/
ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
- if (event != IOC_E_PFAILED)
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
+ if (event != IOC_E_PFFAILED)
bfa_iocpf_initfail(ioc);
break;
- case IOC_E_INITFAILED:
- bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
+ case IOC_E_HWFAILED:
+ ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_hwfail);
break;
case IOC_E_ENABLE:
@@ -512,7 +526,7 @@ bfa_ioc_sm_fail_retry(struct bfa_ioc *ioc, enum ioc_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -546,7 +560,37 @@ bfa_ioc_sm_fail(struct bfa_ioc *ioc, enum ioc_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bfa_ioc_sm_hwfail_entry(struct bfa_ioc *ioc)
+{
+}
+
+/**
+ * IOC failure.
+ */
+static void
+bfa_ioc_sm_hwfail(struct bfa_ioc *ioc, enum ioc_event event)
+{
+ switch (event) {
+
+ case IOC_E_ENABLE:
+ ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+ break;
+
+ case IOC_E_DISABLE:
+ ioc->cbfn->disable_cbfn(ioc->bfa);
+ break;
+
+ case IOC_E_DETACH:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_uninit);
+ break;
+
+ default:
+ bfa_sm_fault(event);
}
}
@@ -560,7 +604,7 @@ bfa_ioc_sm_fail(struct bfa_ioc *ioc, enum ioc_event event)
static void
bfa_iocpf_sm_reset_entry(struct bfa_iocpf *iocpf)
{
- iocpf->retry_count = 0;
+ iocpf->fw_mismatch_notified = false;
iocpf->auto_recover = bfa_nw_auto_recover;
}
@@ -579,7 +623,7 @@ bfa_iocpf_sm_reset(struct bfa_iocpf *iocpf, enum iocpf_event event)
break;
default:
- bfa_sm_fault(iocpf->ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -589,6 +633,7 @@ bfa_iocpf_sm_reset(struct bfa_iocpf *iocpf, enum iocpf_event event)
static void
bfa_iocpf_sm_fwcheck_entry(struct bfa_iocpf *iocpf)
{
+ bfa_ioc_hw_sem_init(iocpf->ioc);
bfa_ioc_hw_sem_get(iocpf->ioc);
}
@@ -604,7 +649,6 @@ bfa_iocpf_sm_fwcheck(struct bfa_iocpf *iocpf, enum iocpf_event event)
case IOCPF_E_SEMLOCKED:
if (bfa_ioc_firmware_lock(ioc)) {
if (bfa_ioc_sync_start(ioc)) {
- iocpf->retry_count = 0;
bfa_ioc_sync_join(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_hwinit);
} else {
@@ -619,6 +663,11 @@ bfa_iocpf_sm_fwcheck(struct bfa_iocpf *iocpf, enum iocpf_event event)
}
break;
+ case IOCPF_E_SEM_ERROR:
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
+ bfa_ioc_pf_hwfailed(ioc);
+ break;
+
case IOCPF_E_DISABLE:
bfa_ioc_hw_sem_get_cancel(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_reset);
@@ -631,7 +680,7 @@ bfa_iocpf_sm_fwcheck(struct bfa_iocpf *iocpf, enum iocpf_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -642,10 +691,10 @@ static void
bfa_iocpf_sm_mismatch_entry(struct bfa_iocpf *iocpf)
{
/* Call only the first time sm enters fwmismatch state. */
- if (iocpf->retry_count == 0)
+ if (iocpf->fw_mismatch_notified == false)
bfa_ioc_pf_fwmismatch(iocpf->ioc);
- iocpf->retry_count++;
+ iocpf->fw_mismatch_notified = true;
mod_timer(&(iocpf->ioc)->iocpf_timer, jiffies +
msecs_to_jiffies(BFA_IOC_TOV));
}
@@ -675,7 +724,7 @@ bfa_iocpf_sm_mismatch(struct bfa_iocpf *iocpf, enum iocpf_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -708,22 +757,26 @@ bfa_iocpf_sm_semwait(struct bfa_iocpf *iocpf, enum iocpf_event event)
}
break;
+ case IOCPF_E_SEM_ERROR:
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
+ bfa_ioc_pf_hwfailed(ioc);
+ break;
+
case IOCPF_E_DISABLE:
bfa_ioc_hw_sem_get_cancel(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling_sync);
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
static void
bfa_iocpf_sm_hwinit_entry(struct bfa_iocpf *iocpf)
{
- mod_timer(&(iocpf->ioc)->iocpf_timer, jiffies +
- msecs_to_jiffies(BFA_IOC_TOV));
- bfa_ioc_reset(iocpf->ioc, 0);
+ iocpf->poll_time = 0;
+ bfa_ioc_reset(iocpf->ioc, false);
}
/**
@@ -737,19 +790,11 @@ bfa_iocpf_sm_hwinit(struct bfa_iocpf *iocpf, enum iocpf_event event)
switch (event) {
case IOCPF_E_FWREADY:
- del_timer(&ioc->iocpf_timer);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_enabling);
break;
- case IOCPF_E_INITFAIL:
- del_timer(&ioc->iocpf_timer);
- /*
- * !!! fall through !!!
- */
-
case IOCPF_E_TIMEOUT:
bfa_nw_ioc_hw_sem_release(ioc);
- if (event == IOCPF_E_TIMEOUT)
bfa_ioc_pf_failed(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_initfail_sync);
break;
@@ -762,7 +807,7 @@ bfa_iocpf_sm_hwinit(struct bfa_iocpf *iocpf, enum iocpf_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -771,6 +816,10 @@ bfa_iocpf_sm_enabling_entry(struct bfa_iocpf *iocpf)
{
mod_timer(&(iocpf->ioc)->iocpf_timer, jiffies +
msecs_to_jiffies(BFA_IOC_TOV));
+ /**
+ * Enable Interrupts before sending fw IOC ENABLE cmd.
+ */
+ iocpf->ioc->cbfn->reset_cbfn(iocpf->ioc->bfa);
bfa_ioc_send_enable(iocpf->ioc);
}
@@ -808,21 +857,11 @@ bfa_iocpf_sm_enabling(struct bfa_iocpf *iocpf, enum iocpf_event event)
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling);
break;
- case IOCPF_E_FWREADY:
- bfa_ioc_send_enable(ioc);
- break;
-
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
-static bool
-bfa_nw_ioc_is_operational(struct bfa_ioc *ioc)
-{
- return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_op);
-}
-
static void
bfa_iocpf_sm_ready_entry(struct bfa_iocpf *iocpf)
{
@@ -832,8 +871,6 @@ bfa_iocpf_sm_ready_entry(struct bfa_iocpf *iocpf)
static void
bfa_iocpf_sm_ready(struct bfa_iocpf *iocpf, enum iocpf_event event)
{
- struct bfa_ioc *ioc = iocpf->ioc;
-
switch (event) {
case IOCPF_E_DISABLE:
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling);
@@ -847,16 +884,8 @@ bfa_iocpf_sm_ready(struct bfa_iocpf *iocpf, enum iocpf_event event)
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail_sync);
break;
- case IOCPF_E_FWREADY:
- bfa_ioc_pf_failed(ioc);
- if (bfa_nw_ioc_is_operational(ioc))
- bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail_sync);
- else
- bfa_fsm_set_state(iocpf, bfa_iocpf_sm_initfail_sync);
- break;
-
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -878,7 +907,6 @@ bfa_iocpf_sm_disabling(struct bfa_iocpf *iocpf, enum iocpf_event event)
switch (event) {
case IOCPF_E_FWRSP_DISABLE:
- case IOCPF_E_FWREADY:
del_timer(&ioc->iocpf_timer);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling_sync);
break;
@@ -898,7 +926,7 @@ bfa_iocpf_sm_disabling(struct bfa_iocpf *iocpf, enum iocpf_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -923,11 +951,16 @@ bfa_iocpf_sm_disabling_sync(struct bfa_iocpf *iocpf, enum iocpf_event event)
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabled);
break;
+ case IOCPF_E_SEM_ERROR:
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
+ bfa_ioc_pf_hwfailed(ioc);
+ break;
+
case IOCPF_E_FAIL:
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -937,6 +970,7 @@ bfa_iocpf_sm_disabling_sync(struct bfa_iocpf *iocpf, enum iocpf_event event)
static void
bfa_iocpf_sm_disabled_entry(struct bfa_iocpf *iocpf)
{
+ bfa_ioc_mbox_flush(iocpf->ioc);
bfa_ioc_pf_disabled(iocpf->ioc);
}
@@ -947,7 +981,6 @@ bfa_iocpf_sm_disabled(struct bfa_iocpf *iocpf, enum iocpf_event event)
switch (event) {
case IOCPF_E_ENABLE:
- iocpf->retry_count = 0;
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_semwait);
break;
@@ -957,7 +990,7 @@ bfa_iocpf_sm_disabled(struct bfa_iocpf *iocpf, enum iocpf_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -978,20 +1011,15 @@ bfa_iocpf_sm_initfail_sync(struct bfa_iocpf *iocpf, enum iocpf_event event)
switch (event) {
case IOCPF_E_SEMLOCKED:
bfa_ioc_notify_fail(ioc);
- bfa_ioc_sync_ack(ioc);
- iocpf->retry_count++;
- if (iocpf->retry_count >= BFA_IOC_HWINIT_MAX) {
- bfa_ioc_sync_leave(ioc);
- bfa_nw_ioc_hw_sem_release(ioc);
- bfa_fsm_set_state(iocpf, bfa_iocpf_sm_initfail);
- } else {
- if (bfa_ioc_sync_complete(ioc))
- bfa_fsm_set_state(iocpf, bfa_iocpf_sm_hwinit);
- else {
- bfa_nw_ioc_hw_sem_release(ioc);
- bfa_fsm_set_state(iocpf, bfa_iocpf_sm_semwait);
- }
- }
+ bfa_ioc_sync_leave(ioc);
+ writel(BFI_IOC_FAIL, ioc->ioc_regs.ioc_fwstate);
+ bfa_nw_ioc_hw_sem_release(ioc);
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_initfail);
+ break;
+
+ case IOCPF_E_SEM_ERROR:
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
+ bfa_ioc_pf_hwfailed(ioc);
break;
case IOCPF_E_DISABLE:
@@ -1009,14 +1037,13 @@ bfa_iocpf_sm_initfail_sync(struct bfa_iocpf *iocpf, enum iocpf_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
static void
bfa_iocpf_sm_initfail_entry(struct bfa_iocpf *iocpf)
{
- bfa_ioc_pf_initfailed(iocpf->ioc);
}
/**
@@ -1038,7 +1065,7 @@ bfa_iocpf_sm_initfail(struct bfa_iocpf *iocpf, enum iocpf_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -1053,7 +1080,7 @@ bfa_iocpf_sm_fail_sync_entry(struct bfa_iocpf *iocpf)
/**
* Flush any queued up mailbox requests.
*/
- bfa_ioc_mbox_hbfail(iocpf->ioc);
+ bfa_ioc_mbox_flush(iocpf->ioc);
bfa_ioc_hw_sem_get(iocpf->ioc);
}
@@ -1067,11 +1094,11 @@ bfa_iocpf_sm_fail_sync(struct bfa_iocpf *iocpf, enum iocpf_event event)
switch (event) {
case IOCPF_E_SEMLOCKED:
- iocpf->retry_count = 0;
bfa_ioc_sync_ack(ioc);
bfa_ioc_notify_fail(ioc);
if (!iocpf->auto_recover) {
bfa_ioc_sync_leave(ioc);
+ writel(BFI_IOC_FAIL, ioc->ioc_regs.ioc_fwstate);
bfa_nw_ioc_hw_sem_release(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
} else {
@@ -1084,6 +1111,11 @@ bfa_iocpf_sm_fail_sync(struct bfa_iocpf *iocpf, enum iocpf_event event)
}
break;
+ case IOCPF_E_SEM_ERROR:
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
+ bfa_ioc_pf_hwfailed(ioc);
+ break;
+
case IOCPF_E_DISABLE:
bfa_ioc_hw_sem_get_cancel(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling_sync);
@@ -1093,7 +1125,7 @@ bfa_iocpf_sm_fail_sync(struct bfa_iocpf *iocpf, enum iocpf_event event)
break;
default:
- bfa_sm_fault(ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -1115,7 +1147,7 @@ bfa_iocpf_sm_fail(struct bfa_iocpf *iocpf, enum iocpf_event event)
break;
default:
- bfa_sm_fault(iocpf->ioc, event);
+ bfa_sm_fault(event);
}
}
@@ -1123,23 +1155,28 @@ bfa_iocpf_sm_fail(struct bfa_iocpf *iocpf, enum iocpf_event event)
* BFA IOC private functions
*/
+/**
+ * Notify common modules registered for notification.
+ */
static void
-bfa_ioc_disable_comp(struct bfa_ioc *ioc)
+bfa_ioc_event_notify(struct bfa_ioc *ioc, enum bfa_ioc_event event)
{
+ struct bfa_ioc_notify *notify;
struct list_head *qe;
- struct bfa_ioc_hbfail_notify *notify;
-
- ioc->cbfn->disable_cbfn(ioc->bfa);
- /**
- * Notify common modules registered for notification.
- */
- list_for_each(qe, &ioc->hb_notify_q) {
- notify = (struct bfa_ioc_hbfail_notify *) qe;
- notify->cbfn(notify->cbarg);
+ list_for_each(qe, &ioc->notify_q) {
+ notify = (struct bfa_ioc_notify *)qe;
+ notify->cbfn(notify->cbarg, event);
}
}
+static void
+bfa_ioc_disable_comp(struct bfa_ioc *ioc)
+{
+ ioc->cbfn->disable_cbfn(ioc->bfa);
+ bfa_ioc_event_notify(ioc, BFA_IOC_E_DISABLED);
+}
+
bool
bfa_nw_ioc_sem_get(void __iomem *sem_reg)
{
@@ -1149,26 +1186,49 @@ bfa_nw_ioc_sem_get(void __iomem *sem_reg)
r32 = readl(sem_reg);
- while (r32 && (cnt < BFA_SEM_SPINCNT)) {
+ while ((r32 & 1) && (cnt < BFA_SEM_SPINCNT)) {
cnt++;
udelay(2);
r32 = readl(sem_reg);
}
- if (r32 == 0)
+ if (!(r32 & 1))
return true;
- BUG_ON(!(cnt < BFA_SEM_SPINCNT));
return false;
}
void
bfa_nw_ioc_sem_release(void __iomem *sem_reg)
{
+ readl(sem_reg);
writel(1, sem_reg);
}
static void
+bfa_ioc_hw_sem_init(struct bfa_ioc *ioc)
+{
+ struct bfi_ioc_image_hdr fwhdr;
+ u32 fwstate = readl(ioc->ioc_regs.ioc_fwstate);
+
+ if (fwstate == BFI_IOC_UNINIT)
+ return;
+
+ bfa_nw_ioc_fwver_get(ioc, &fwhdr);
+
+ if (swab32(fwhdr.exec) == BFI_FWBOOT_TYPE_NORMAL)
+ return;
+
+ writel(BFI_IOC_UNINIT, ioc->ioc_regs.ioc_fwstate);
+
+ /*
+ * Try to lock and then unlock the semaphore.
+ */
+ readl(ioc->ioc_regs.ioc_sem_reg);
+ writel(1, ioc->ioc_regs.ioc_sem_reg);
+}
+
+static void
bfa_ioc_hw_sem_get(struct bfa_ioc *ioc)
{
u32 r32;
@@ -1178,7 +1238,11 @@ bfa_ioc_hw_sem_get(struct bfa_ioc *ioc)
* will return 1. Semaphore is released by writing 1 to the register
*/
r32 = readl(ioc->ioc_regs.ioc_sem_reg);
- if (r32 == 0) {
+ if (r32 == ~0) {
+ bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_SEM_ERROR);
+ return;
+ }
+ if (!(r32 & 1)) {
bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_SEMLOCKED);
return;
}
@@ -1299,7 +1363,7 @@ bfa_nw_ioc_fwver_cmp(struct bfa_ioc *ioc, struct bfi_ioc_image_hdr *fwhdr)
int i;
drv_fwhdr = (struct bfi_ioc_image_hdr *)
- bfa_cb_image_get_chunk(BFA_IOC_FWIMG_TYPE(ioc), 0);
+ bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), 0);
for (i = 0; i < BFI_IOC_MD5SUM_SZ; i++) {
if (fwhdr->md5sum[i] != drv_fwhdr->md5sum[i])
@@ -1320,12 +1384,12 @@ bfa_ioc_fwver_valid(struct bfa_ioc *ioc, u32 boot_env)
bfa_nw_ioc_fwver_get(ioc, &fwhdr);
drv_fwhdr = (struct bfi_ioc_image_hdr *)
- bfa_cb_image_get_chunk(BFA_IOC_FWIMG_TYPE(ioc), 0);
+ bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), 0);
if (fwhdr.signature != drv_fwhdr->signature)
return false;
- if (swab32(fwhdr.param) != boot_env)
+ if (swab32(fwhdr.bootenv) != boot_env)
return false;
return bfa_nw_ioc_fwver_cmp(ioc, &fwhdr);
@@ -1356,11 +1420,11 @@ bfa_ioc_hwinit(struct bfa_ioc *ioc, bool force)
ioc_fwstate = readl(ioc->ioc_regs.ioc_fwstate);
- boot_env = BFI_BOOT_LOADER_OS;
-
if (force)
ioc_fwstate = BFI_IOC_UNINIT;
+ boot_env = BFI_FWBOOT_ENV_OS;
+
/**
* check if firmware is valid
*/
@@ -1368,7 +1432,8 @@ bfa_ioc_hwinit(struct bfa_ioc *ioc, bool force)
false : bfa_ioc_fwver_valid(ioc, boot_env);
if (!fwvalid) {
- bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, boot_env);
+ bfa_ioc_boot(ioc, BFI_FWBOOT_TYPE_NORMAL, boot_env);
+ bfa_ioc_poll_fwinit(ioc);
return;
}
@@ -1377,7 +1442,7 @@ bfa_ioc_hwinit(struct bfa_ioc *ioc, bool force)
* just wait for an initialization completion interrupt.
*/
if (ioc_fwstate == BFI_IOC_INITING) {
- ioc->cbfn->reset_cbfn(ioc->bfa);
+ bfa_ioc_poll_fwinit(ioc);
return;
}
@@ -1391,7 +1456,6 @@ bfa_ioc_hwinit(struct bfa_ioc *ioc, bool force)
* be flushed. Otherwise MSI-X interrupts are not delivered.
*/
bfa_ioc_msgflush(ioc);
- ioc->cbfn->reset_cbfn(ioc->bfa);
bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_FWREADY);
return;
}
@@ -1399,7 +1463,8 @@ bfa_ioc_hwinit(struct bfa_ioc *ioc, bool force)
/**
* Initialize the h/w for any other states.
*/
- bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, boot_env);
+ bfa_ioc_boot(ioc, BFI_FWBOOT_TYPE_NORMAL, boot_env);
+ bfa_ioc_poll_fwinit(ioc);
}
void
@@ -1443,7 +1508,7 @@ bfa_ioc_send_enable(struct bfa_ioc *ioc)
bfi_h2i_set(enable_req.mh, BFI_MC_IOC, BFI_IOC_H2I_ENABLE_REQ,
bfa_ioc_portid(ioc));
- enable_req.ioc_class = ioc->ioc_mc;
+ enable_req.clscode = htons(ioc->clscode);
do_gettimeofday(&tv);
enable_req.tv_sec = ntohl(tv.tv_sec);
bfa_ioc_mbox_send(ioc, &enable_req, sizeof(struct bfi_ioc_ctrl_req));
@@ -1516,22 +1581,23 @@ bfa_ioc_download_fw(struct bfa_ioc *ioc, u32 boot_type,
u32 loff = 0;
u32 chunkno = 0;
u32 i;
+ u32 asicmode;
/**
* Initialize LMEM first before code download
*/
bfa_ioc_lmem_init(ioc);
- fwimg = bfa_cb_image_get_chunk(BFA_IOC_FWIMG_TYPE(ioc), chunkno);
+ fwimg = bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), chunkno);
pgnum = bfa_ioc_smem_pgnum(ioc, loff);
writel(pgnum, ioc->ioc_regs.host_page_num_fn);
- for (i = 0; i < bfa_cb_image_get_size(BFA_IOC_FWIMG_TYPE(ioc)); i++) {
+ for (i = 0; i < bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc)); i++) {
if (BFA_IOC_FLASH_CHUNK_NO(i) != chunkno) {
chunkno = BFA_IOC_FLASH_CHUNK_NO(i);
- fwimg = bfa_cb_image_get_chunk(BFA_IOC_FWIMG_TYPE(ioc),
+ fwimg = bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc),
BFA_IOC_FLASH_CHUNK_ADDR(chunkno));
}
@@ -1558,12 +1624,16 @@ bfa_ioc_download_fw(struct bfa_ioc *ioc, u32 boot_type,
ioc->ioc_regs.host_page_num_fn);
/*
- * Set boot type and boot param at the end.
+ * Set boot type, env and device mode at the end.
*/
+ asicmode = BFI_FWBOOT_DEVMODE(ioc->asic_gen, ioc->asic_mode,
+ ioc->port0_mode, ioc->port1_mode);
+ writel(asicmode, ((ioc->ioc_regs.smem_page_start)
+ + BFI_FWBOOT_DEVMODE_OFF));
writel(boot_type, ((ioc->ioc_regs.smem_page_start)
- + (BFI_BOOT_TYPE_OFF)));
+ + (BFI_FWBOOT_TYPE_OFF)));
writel(boot_env, ((ioc->ioc_regs.smem_page_start)
- + (BFI_BOOT_LOADER_OFF)));
+ + (BFI_FWBOOT_ENV_OFF)));
}
static void
@@ -1573,6 +1643,20 @@ bfa_ioc_reset(struct bfa_ioc *ioc, bool force)
}
/**
+ * BFA ioc enable reply by firmware
+ */
+static void
+bfa_ioc_enable_reply(struct bfa_ioc *ioc, enum bfa_mode port_mode,
+ u8 cap_bm)
+{
+ struct bfa_iocpf *iocpf = &ioc->iocpf;
+
+ ioc->port_mode = ioc->port_mode_cfg = port_mode;
+ ioc->ad_cap_bm = cap_bm;
+ bfa_fsm_send_event(iocpf, IOCPF_E_FWRSP_ENABLE);
+}
+
+/**
* @brief
* Update BFA configuration from firmware configuration.
*/
@@ -1612,7 +1696,9 @@ bfa_ioc_mbox_poll(struct bfa_ioc *ioc)
{
struct bfa_ioc_mbox_mod *mod = &ioc->mbox_mod;
struct bfa_mbox_cmd *cmd;
- u32 stat;
+ bfa_mbox_cmd_cbfn_t cbfn;
+ void *cbarg;
+ u32 stat;
/**
* If no command pending, do nothing
@@ -1632,13 +1718,23 @@ bfa_ioc_mbox_poll(struct bfa_ioc *ioc)
*/
bfa_q_deq(&mod->cmd_q, &cmd);
bfa_ioc_mbox_send(ioc, cmd->msg, sizeof(cmd->msg));
+
+ /**
+ * Give a callback to the client, indicating that the command is sent
+ */
+ if (cmd->cbfn) {
+ cbfn = cmd->cbfn;
+ cbarg = cmd->cbarg;
+ cmd->cbfn = NULL;
+ cbfn(cbarg);
+ }
}
/**
* Cleanup any pending requests.
*/
static void
-bfa_ioc_mbox_hbfail(struct bfa_ioc *ioc)
+bfa_ioc_mbox_flush(struct bfa_ioc *ioc)
{
struct bfa_ioc_mbox_mod *mod = &ioc->mbox_mod;
struct bfa_mbox_cmd *cmd;
@@ -1650,19 +1746,16 @@ bfa_ioc_mbox_hbfail(struct bfa_ioc *ioc)
static void
bfa_ioc_fail_notify(struct bfa_ioc *ioc)
{
- struct list_head *qe;
- struct bfa_ioc_hbfail_notify *notify;
-
/**
* Notify driver and common modules registered for notification.
*/
ioc->cbfn->hbfail_cbfn(ioc->bfa);
- list_for_each(qe, &ioc->hb_notify_q) {
- notify = (struct bfa_ioc_hbfail_notify *) qe;
- notify->cbfn(notify->cbarg);
- }
+ bfa_ioc_event_notify(ioc, BFA_IOC_E_FAILED);
}
+/**
+ * IOCPF to IOC interface
+ */
static void
bfa_ioc_pf_enabled(struct bfa_ioc *ioc)
{
@@ -1676,15 +1769,15 @@ bfa_ioc_pf_disabled(struct bfa_ioc *ioc)
}
static void
-bfa_ioc_pf_initfailed(struct bfa_ioc *ioc)
+bfa_ioc_pf_failed(struct bfa_ioc *ioc)
{
- bfa_fsm_send_event(ioc, IOC_E_INITFAILED);
+ bfa_fsm_send_event(ioc, IOC_E_PFFAILED);
}
static void
-bfa_ioc_pf_failed(struct bfa_ioc *ioc)
+bfa_ioc_pf_hwfailed(struct bfa_ioc *ioc)
{
- bfa_fsm_send_event(ioc, IOC_E_PFAILED);
+ bfa_fsm_send_event(ioc, IOC_E_HWFAILED);
}
static void
@@ -1723,10 +1816,9 @@ bfa_ioc_pll_init(struct bfa_ioc *ioc)
* as the entry vector.
*/
static void
-bfa_ioc_boot(struct bfa_ioc *ioc, u32 boot_type, u32 boot_env)
+bfa_ioc_boot(struct bfa_ioc *ioc, enum bfi_fwboot_type boot_type,
+ u32 boot_env)
{
- void __iomem *rb;
-
bfa_ioc_stats(ioc, ioc_boots);
if (bfa_ioc_pll_init(ioc) != BFA_STATUS_OK)
@@ -1735,22 +1827,16 @@ bfa_ioc_boot(struct bfa_ioc *ioc, u32 boot_type, u32 boot_env)
/**
* Initialize IOC state of all functions on a chip reset.
*/
- rb = ioc->pcidev.pci_bar_kva;
- if (boot_type == BFI_BOOT_TYPE_MEMTEST) {
- writel(BFI_IOC_MEMTEST, (rb + BFA_IOC0_STATE_REG));
- writel(BFI_IOC_MEMTEST, (rb + BFA_IOC1_STATE_REG));
+ if (boot_type == BFI_FWBOOT_TYPE_MEMTEST) {
+ writel(BFI_IOC_MEMTEST, ioc->ioc_regs.ioc_fwstate);
+ writel(BFI_IOC_MEMTEST, ioc->ioc_regs.alt_ioc_fwstate);
} else {
- writel(BFI_IOC_INITING, (rb + BFA_IOC0_STATE_REG));
- writel(BFI_IOC_INITING, (rb + BFA_IOC1_STATE_REG));
+ writel(BFI_IOC_INITING, ioc->ioc_regs.ioc_fwstate);
+ writel(BFI_IOC_INITING, ioc->ioc_regs.alt_ioc_fwstate);
}
bfa_ioc_msgflush(ioc);
bfa_ioc_download_fw(ioc, boot_type, boot_env);
-
- /**
- * Enable interrupts just before starting LPU
- */
- ioc->cbfn->reset_cbfn(ioc->bfa);
bfa_ioc_lpu_start(ioc);
}
@@ -1763,13 +1849,17 @@ bfa_nw_ioc_auto_recover(bool auto_recover)
bfa_nw_auto_recover = auto_recover;
}
-static void
+static bool
bfa_ioc_msgget(struct bfa_ioc *ioc, void *mbmsg)
{
u32 *msgp = mbmsg;
u32 r32;
int i;
+ r32 = readl(ioc->ioc_regs.lpu_mbox_cmd);
+ if ((r32 & 1) == 0)
+ return false;
+
/**
* read the MBOX msg
*/
@@ -1785,6 +1875,8 @@ bfa_ioc_msgget(struct bfa_ioc *ioc, void *mbmsg)
*/
writel(1, ioc->ioc_regs.lpu_mbox_cmd);
readl(ioc->ioc_regs.lpu_mbox_cmd);
+
+ return true;
}
static void
@@ -1801,12 +1893,10 @@ bfa_ioc_isr(struct bfa_ioc *ioc, struct bfi_mbmsg *m)
case BFI_IOC_I2H_HBEAT:
break;
- case BFI_IOC_I2H_READY_EVENT:
- bfa_fsm_send_event(iocpf, IOCPF_E_FWREADY);
- break;
-
case BFI_IOC_I2H_ENABLE_REPLY:
- bfa_fsm_send_event(iocpf, IOCPF_E_FWRSP_ENABLE);
+ bfa_ioc_enable_reply(ioc,
+ (enum bfa_mode)msg->fw_event.port_mode,
+ msg->fw_event.cap_bm);
break;
case BFI_IOC_I2H_DISABLE_REPLY:
@@ -1839,7 +1929,7 @@ bfa_nw_ioc_attach(struct bfa_ioc *ioc, void *bfa, struct bfa_ioc_cbfn *cbfn)
ioc->iocpf.ioc = ioc;
bfa_ioc_mbox_attach(ioc);
- INIT_LIST_HEAD(&ioc->hb_notify_q);
+ INIT_LIST_HEAD(&ioc->notify_q);
bfa_fsm_set_state(ioc, bfa_ioc_sm_uninit);
bfa_fsm_send_event(ioc, IOC_E_RESET);
@@ -1852,6 +1942,9 @@ void
bfa_nw_ioc_detach(struct bfa_ioc *ioc)
{
bfa_fsm_send_event(ioc, IOC_E_DETACH);
+
+ /* Done with detach, empty the notify_q. */
+ INIT_LIST_HEAD(&ioc->notify_q);
}
/**
@@ -1861,14 +1954,63 @@ bfa_nw_ioc_detach(struct bfa_ioc *ioc)
*/
void
bfa_nw_ioc_pci_init(struct bfa_ioc *ioc, struct bfa_pcidev *pcidev,
- enum bfi_mclass mc)
+ enum bfi_pcifn_class clscode)
{
- ioc->ioc_mc = mc;
+ ioc->clscode = clscode;
ioc->pcidev = *pcidev;
- ioc->ctdev = bfa_asic_id_ct(ioc->pcidev.device_id);
- ioc->cna = ioc->ctdev && !ioc->fcmode;
- bfa_nw_ioc_set_ct_hwif(ioc);
+ /**
+ * Initialize IOC and device personality
+ */
+ ioc->port0_mode = ioc->port1_mode = BFI_PORT_MODE_FC;
+ ioc->asic_mode = BFI_ASIC_MODE_FC;
+
+ switch (pcidev->device_id) {
+ case PCI_DEVICE_ID_BROCADE_CT:
+ ioc->asic_gen = BFI_ASIC_GEN_CT;
+ ioc->port0_mode = ioc->port1_mode = BFI_PORT_MODE_ETH;
+ ioc->asic_mode = BFI_ASIC_MODE_ETH;
+ ioc->port_mode = ioc->port_mode_cfg = BFA_MODE_CNA;
+ ioc->ad_cap_bm = BFA_CM_CNA;
+ break;
+
+ case BFA_PCI_DEVICE_ID_CT2:
+ ioc->asic_gen = BFI_ASIC_GEN_CT2;
+ if (clscode == BFI_PCIFN_CLASS_FC &&
+ pcidev->ssid == BFA_PCI_CT2_SSID_FC) {
+ ioc->asic_mode = BFI_ASIC_MODE_FC16;
+ ioc->fcmode = true;
+ ioc->port_mode = ioc->port_mode_cfg = BFA_MODE_HBA;
+ ioc->ad_cap_bm = BFA_CM_HBA;
+ } else {
+ ioc->port0_mode = ioc->port1_mode = BFI_PORT_MODE_ETH;
+ ioc->asic_mode = BFI_ASIC_MODE_ETH;
+ if (pcidev->ssid == BFA_PCI_CT2_SSID_FCoE) {
+ ioc->port_mode =
+ ioc->port_mode_cfg = BFA_MODE_CNA;
+ ioc->ad_cap_bm = BFA_CM_CNA;
+ } else {
+ ioc->port_mode =
+ ioc->port_mode_cfg = BFA_MODE_NIC;
+ ioc->ad_cap_bm = BFA_CM_NIC;
+ }
+ }
+ break;
+
+ default:
+ BUG_ON(1);
+ }
+
+ /**
+ * Set asic specific interfaces.
+ */
+ if (ioc->asic_gen == BFI_ASIC_GEN_CT)
+ bfa_nw_ioc_set_ct_hwif(ioc);
+ else {
+ WARN_ON(ioc->asic_gen != BFI_ASIC_GEN_CT2);
+ bfa_nw_ioc_set_ct2_hwif(ioc);
+ bfa_nw_ioc_ct2_poweron(ioc);
+ }
bfa_ioc_map_port(ioc);
bfa_ioc_reg_init(ioc);
@@ -1942,18 +2084,22 @@ bfa_nw_ioc_mbox_regisr(struct bfa_ioc *ioc, enum bfi_mclass mc,
* @param[in] ioc IOC instance
* @param[i] cmd Mailbox command
*/
-void
-bfa_nw_ioc_mbox_queue(struct bfa_ioc *ioc, struct bfa_mbox_cmd *cmd)
+bool
+bfa_nw_ioc_mbox_queue(struct bfa_ioc *ioc, struct bfa_mbox_cmd *cmd,
+ bfa_mbox_cmd_cbfn_t cbfn, void *cbarg)
{
struct bfa_ioc_mbox_mod *mod = &ioc->mbox_mod;
u32 stat;
+ cmd->cbfn = cbfn;
+ cmd->cbarg = cbarg;
+
/**
* If a previous command is pending, queue new command
*/
if (!list_empty(&mod->cmd_q)) {
list_add_tail(&cmd->qe, &mod->cmd_q);
- return;
+ return true;
}
/**
@@ -1962,13 +2108,15 @@ bfa_nw_ioc_mbox_queue(struct bfa_ioc *ioc, struct bfa_mbox_cmd *cmd)
stat = readl(ioc->ioc_regs.hfn_mbox_cmd);
if (stat) {
list_add_tail(&cmd->qe, &mod->cmd_q);
- return;
+ return true;
}
/**
* mailbox is free -- queue command to firmware
*/
bfa_ioc_mbox_send(ioc, cmd->msg, sizeof(cmd->msg));
+
+ return false;
}
/**
@@ -1981,38 +2129,57 @@ bfa_nw_ioc_mbox_isr(struct bfa_ioc *ioc)
struct bfi_mbmsg m;
int mc;
- bfa_ioc_msgget(ioc, &m);
+ if (bfa_ioc_msgget(ioc, &m)) {
+ /**
+ * Treat IOC message class as special.
+ */
+ mc = m.mh.msg_class;
+ if (mc == BFI_MC_IOC) {
+ bfa_ioc_isr(ioc, &m);
+ return;
+ }
- /**
- * Treat IOC message class as special.
- */
- mc = m.mh.msg_class;
- if (mc == BFI_MC_IOC) {
- bfa_ioc_isr(ioc, &m);
- return;
+ if ((mc >= BFI_MC_MAX) || (mod->mbhdlr[mc].cbfn == NULL))
+ return;
+
+ mod->mbhdlr[mc].cbfn(mod->mbhdlr[mc].cbarg, &m);
}
- if ((mc >= BFI_MC_MAX) || (mod->mbhdlr[mc].cbfn == NULL))
- return;
+ bfa_ioc_lpu_read_stat(ioc);
- mod->mbhdlr[mc].cbfn(mod->mbhdlr[mc].cbarg, &m);
+ /**
+ * Try to send pending mailbox commands
+ */
+ bfa_ioc_mbox_poll(ioc);
}
void
bfa_nw_ioc_error_isr(struct bfa_ioc *ioc)
{
+ bfa_ioc_stats(ioc, ioc_hbfails);
+ bfa_ioc_stats_hb_count(ioc, ioc->hb_count);
bfa_fsm_send_event(ioc, IOC_E_HWERROR);
}
/**
+ * return true if IOC is disabled
+ */
+bool
+bfa_nw_ioc_is_disabled(struct bfa_ioc *ioc)
+{
+ return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling) ||
+ bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled);
+}
+
+/**
* Add to IOC heartbeat failure notification queue. To be used by common
* modules such as cee, port, diag.
*/
void
-bfa_nw_ioc_hbfail_register(struct bfa_ioc *ioc,
- struct bfa_ioc_hbfail_notify *notify)
+bfa_nw_ioc_notify_register(struct bfa_ioc *ioc,
+ struct bfa_ioc_notify *notify)
{
- list_add_tail(&notify->qe, &ioc->hb_notify_q);
+ list_add_tail(&notify->qe, &ioc->notify_q);
}
#define BFA_MFG_NAME "Brocade"
@@ -2055,24 +2222,18 @@ bfa_ioc_get_adapter_attr(struct bfa_ioc *ioc,
ad_attr->asic_rev = ioc_attr->asic_rev;
bfa_ioc_get_pci_chip_rev(ioc, ad_attr->hw_ver);
-
- ad_attr->cna_capable = ioc->cna;
- ad_attr->trunk_capable = (ad_attr->nports > 1) && !ioc->cna;
}
static enum bfa_ioc_type
bfa_ioc_get_type(struct bfa_ioc *ioc)
{
- if (!ioc->ctdev || ioc->fcmode)
- return BFA_IOC_TYPE_FC;
- else if (ioc->ioc_mc == BFI_MC_IOCFC)
- return BFA_IOC_TYPE_FCoE;
- else if (ioc->ioc_mc == BFI_MC_LL)
- return BFA_IOC_TYPE_LL;
- else {
- BUG_ON(!(ioc->ioc_mc == BFI_MC_LL));
+ if (ioc->clscode == BFI_PCIFN_CLASS_ETH)
return BFA_IOC_TYPE_LL;
- }
+
+ BUG_ON(!(ioc->clscode == BFI_PCIFN_CLASS_FC));
+
+ return (ioc->attr->port_mode == BFI_PORT_MODE_FC)
+ ? BFA_IOC_TYPE_FC : BFA_IOC_TYPE_FCoE;
}
static void
@@ -2131,9 +2292,6 @@ bfa_ioc_get_adapter_model(struct bfa_ioc *ioc, char *model)
ioc_attr = ioc->attr;
- /**
- * model name
- */
snprintf(model, BFA_ADAPTER_MODEL_NAME_LEN, "%s-%u",
BFA_MFG_NAME, ioc_attr->card_type);
}
@@ -2184,6 +2342,10 @@ bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr)
ioc_attr->state = bfa_ioc_get_state(ioc);
ioc_attr->port_id = ioc->port_id;
+ ioc_attr->port_mode = ioc->port_mode;
+
+ ioc_attr->port_mode_cfg = ioc->port_mode_cfg;
+ ioc_attr->cap_bm = ioc->ad_cap_bm;
ioc_attr->ioc_type = bfa_ioc_get_type(ioc);
@@ -2217,6 +2379,7 @@ bfa_ioc_recover(struct bfa_ioc *ioc)
{
pr_crit("Heart Beat of IOC has failed\n");
bfa_ioc_stats(ioc, ioc_hbfails);
+ bfa_ioc_stats_hb_count(ioc, ioc->hb_count);
bfa_fsm_send_event(ioc, IOC_E_HBFAIL);
}
@@ -2272,8 +2435,14 @@ void
bfa_nw_iocpf_timeout(void *ioc_arg)
{
struct bfa_ioc *ioc = (struct bfa_ioc *) ioc_arg;
+ enum bfa_iocpf_state iocpf_st;
+
+ iocpf_st = bfa_sm_to_state(iocpf_sm_table, ioc->iocpf.fsm);
- bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_TIMEOUT);
+ if (iocpf_st == BFA_IOCPF_HWINIT)
+ bfa_ioc_poll_fwinit(ioc);
+ else
+ bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_TIMEOUT);
}
void
@@ -2283,3 +2452,22 @@ bfa_nw_iocpf_sem_timeout(void *ioc_arg)
bfa_ioc_hw_sem_get(ioc);
}
+
+static void
+bfa_ioc_poll_fwinit(struct bfa_ioc *ioc)
+{
+ u32 fwstate = readl(ioc->ioc_regs.ioc_fwstate);
+
+ if (fwstate == BFI_IOC_DISABLED) {
+ bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_FWREADY);
+ return;
+ }
+
+ if (ioc->iocpf.poll_time >= BFA_IOC_TOV) {
+ bfa_nw_iocpf_timeout(ioc);
+ } else {
+ ioc->iocpf.poll_time += BFA_IOC_POLL_TOV;
+ mod_timer(&ioc->iocpf_timer, jiffies +
+ msecs_to_jiffies(BFA_IOC_POLL_TOV));
+ }
+}
diff --git a/drivers/net/bna/bfa_ioc.h b/drivers/net/ethernet/brocade/bna/bfa_ioc.h
index bd48abee781f..ca158d1eaef3 100644
--- a/drivers/net/bna/bfa_ioc.h
+++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.h
@@ -19,14 +19,14 @@
#ifndef __BFA_IOC_H__
#define __BFA_IOC_H__
-#include "bfa_sm.h"
+#include "bfa_cs.h"
#include "bfi.h"
#include "cna.h"
#define BFA_IOC_TOV 3000 /* msecs */
#define BFA_IOC_HWSEM_TOV 500 /* msecs */
#define BFA_IOC_HB_TOV 500 /* msecs */
-#define BFA_IOC_HWINIT_MAX 5
+#define BFA_IOC_POLL_TOV 200 /* msecs */
/**
* PCI device information required by IOC
@@ -35,6 +35,7 @@ struct bfa_pcidev {
int pci_slot;
u8 pci_func;
u16 device_id;
+ u16 ssid;
void __iomem *pci_bar_kva;
};
@@ -72,6 +73,7 @@ struct bfa_ioc_regs {
void __iomem *hfn_mbox;
void __iomem *lpu_mbox_cmd;
void __iomem *lpu_mbox;
+ void __iomem *lpu_read_stat;
void __iomem *pss_ctl_reg;
void __iomem *pss_err_status_reg;
void __iomem *app_pll_fast_ctl_reg;
@@ -97,9 +99,12 @@ struct bfa_ioc_regs {
/**
* IOC Mailbox structures
*/
+typedef void (*bfa_mbox_cmd_cbfn_t)(void *cbarg);
struct bfa_mbox_cmd {
struct list_head qe;
- u32 msg[BFI_IOC_MSGSZ];
+ bfa_mbox_cmd_cbfn_t cbfn;
+ void *cbarg;
+ u32 msg[BFI_IOC_MSGSZ];
};
/**
@@ -130,18 +135,26 @@ struct bfa_ioc_cbfn {
};
/**
- * Heartbeat failure notification queue element.
+ * IOC event notification mechanism.
*/
-struct bfa_ioc_hbfail_notify {
+enum bfa_ioc_event {
+ BFA_IOC_E_ENABLED = 1,
+ BFA_IOC_E_DISABLED = 2,
+ BFA_IOC_E_FAILED = 3,
+};
+
+typedef void (*bfa_ioc_notify_cbfn_t)(void *, enum bfa_ioc_event);
+
+struct bfa_ioc_notify {
struct list_head qe;
- bfa_ioc_hbfail_cbfn_t cbfn;
+ bfa_ioc_notify_cbfn_t cbfn;
void *cbarg;
};
/**
- * Initialize a heartbeat failure notification structure
+ * Initialize a IOC event notification structure
*/
-#define bfa_ioc_hbfail_init(__notify, __cbfn, __cbarg) do { \
+#define bfa_ioc_notify_init(__notify, __cbfn, __cbarg) do { \
(__notify)->cbfn = (__cbfn); \
(__notify)->cbarg = (__cbarg); \
} while (0)
@@ -149,43 +162,50 @@ struct bfa_ioc_hbfail_notify {
struct bfa_iocpf {
bfa_fsm_t fsm;
struct bfa_ioc *ioc;
- u32 retry_count;
+ bool fw_mismatch_notified;
bool auto_recover;
+ u32 poll_time;
};
struct bfa_ioc {
bfa_fsm_t fsm;
- struct bfa *bfa;
- struct bfa_pcidev pcidev;
- struct timer_list ioc_timer;
- struct timer_list iocpf_timer;
- struct timer_list sem_timer;
+ struct bfa *bfa;
+ struct bfa_pcidev pcidev;
+ struct timer_list ioc_timer;
+ struct timer_list iocpf_timer;
+ struct timer_list sem_timer;
struct timer_list hb_timer;
u32 hb_count;
- struct list_head hb_notify_q;
+ struct list_head notify_q;
void *dbg_fwsave;
int dbg_fwsave_len;
bool dbg_fwsave_once;
- enum bfi_mclass ioc_mc;
- struct bfa_ioc_regs ioc_regs;
+ enum bfi_pcifn_class clscode;
+ struct bfa_ioc_regs ioc_regs;
struct bfa_ioc_drv_stats stats;
bool fcmode;
- bool ctdev;
- bool cna;
bool pllinit;
- bool stats_busy; /*!< outstanding stats */
+ bool stats_busy; /*!< outstanding stats */
u8 port_id;
struct bfa_dma attr_dma;
struct bfi_ioc_attr *attr;
struct bfa_ioc_cbfn *cbfn;
struct bfa_ioc_mbox_mod mbox_mod;
- struct bfa_ioc_hwif *ioc_hwif;
+ const struct bfa_ioc_hwif *ioc_hwif;
struct bfa_iocpf iocpf;
+ enum bfi_asic_gen asic_gen;
+ enum bfi_asic_mode asic_mode;
+ enum bfi_port_mode port0_mode;
+ enum bfi_port_mode port1_mode;
+ enum bfa_mode port_mode;
+ u8 ad_cap_bm; /*!< adapter cap bit mask */
+ u8 port_mode_cfg; /*!< config port mode */
};
struct bfa_ioc_hwif {
- enum bfa_status (*ioc_pll_init) (void __iomem *rb, bool fcmode);
+ enum bfa_status (*ioc_pll_init) (void __iomem *rb,
+ enum bfi_asic_mode m);
bool (*ioc_firmware_lock) (struct bfa_ioc *ioc);
void (*ioc_firmware_unlock) (struct bfa_ioc *ioc);
void (*ioc_reg_init) (struct bfa_ioc *ioc);
@@ -199,12 +219,14 @@ struct bfa_ioc_hwif {
void (*ioc_sync_leave) (struct bfa_ioc *ioc);
void (*ioc_sync_ack) (struct bfa_ioc *ioc);
bool (*ioc_sync_complete) (struct bfa_ioc *ioc);
+ bool (*ioc_lpu_read_stat) (struct bfa_ioc *ioc);
};
#define bfa_ioc_pcifn(__ioc) ((__ioc)->pcidev.pci_func)
#define bfa_ioc_devid(__ioc) ((__ioc)->pcidev.device_id)
#define bfa_ioc_bar0(__ioc) ((__ioc)->pcidev.pci_bar_kva)
#define bfa_ioc_portid(__ioc) ((__ioc)->port_id)
+#define bfa_ioc_asic_gen(__ioc) ((__ioc)->asic_gen)
#define bfa_ioc_fetch_stats(__ioc, __stats) \
(((__stats)->drv_stats) = (__ioc)->stats)
#define bfa_ioc_clr_stats(__ioc) \
@@ -217,13 +239,12 @@ struct bfa_ioc_hwif {
BFI_ADAPTER_GETP(NPORTS, (__ioc)->attr->adapter_prop)
#define bfa_ioc_stats(_ioc, _stats) ((_ioc)->stats._stats++)
+#define bfa_ioc_stats_hb_count(_ioc, _hb_count) \
+ ((_ioc)->stats.hb_count = (_hb_count))
#define BFA_IOC_FWIMG_MINSZ (16 * 1024)
-#define BFA_IOC_FWIMG_TYPE(__ioc) \
- (((__ioc)->ctdev) ? \
- (((__ioc)->fcmode) ? BFI_IMAGE_CT_FC : BFI_IMAGE_CT_CNA) : \
- BFI_IMAGE_CB_FC)
#define BFA_IOC_FW_SMEM_SIZE(__ioc) \
- (((__ioc)->ctdev) ? BFI_SMEM_CT_SIZE : BFI_SMEM_CB_SIZE)
+ ((bfa_ioc_asic_gen(__ioc) == BFI_ASIC_GEN_CB) \
+ ? BFI_SMEM_CB_SIZE : BFI_SMEM_CT_SIZE)
#define BFA_IOC_FLASH_CHUNK_NO(off) (off / BFI_FLASH_CHUNK_SZ_WORDS)
#define BFA_IOC_FLASH_OFFSET_IN_CHUNK(off) (off % BFI_FLASH_CHUNK_SZ_WORDS)
#define BFA_IOC_FLASH_CHUNK_ADDR(chunkno) (chunkno * BFI_FLASH_CHUNK_SZ_WORDS)
@@ -231,7 +252,9 @@ struct bfa_ioc_hwif {
/**
* IOC mailbox interface
*/
-void bfa_nw_ioc_mbox_queue(struct bfa_ioc *ioc, struct bfa_mbox_cmd *cmd);
+bool bfa_nw_ioc_mbox_queue(struct bfa_ioc *ioc,
+ struct bfa_mbox_cmd *cmd,
+ bfa_mbox_cmd_cbfn_t cbfn, void *cbarg);
void bfa_nw_ioc_mbox_isr(struct bfa_ioc *ioc);
void bfa_nw_ioc_mbox_regisr(struct bfa_ioc *ioc, enum bfi_mclass mc,
bfa_ioc_mbox_mcfunc_t cbfn, void *cbarg);
@@ -242,30 +265,40 @@ void bfa_nw_ioc_mbox_regisr(struct bfa_ioc *ioc, enum bfi_mclass mc,
#define bfa_ioc_pll_init_asic(__ioc) \
((__ioc)->ioc_hwif->ioc_pll_init((__ioc)->pcidev.pci_bar_kva, \
- (__ioc)->fcmode))
+ (__ioc)->asic_mode))
-#define bfa_ioc_isr_mode_set(__ioc, __msix) \
- ((__ioc)->ioc_hwif->ioc_isr_mode_set(__ioc, __msix))
+#define bfa_ioc_isr_mode_set(__ioc, __msix) do { \
+ if ((__ioc)->ioc_hwif->ioc_isr_mode_set) \
+ ((__ioc)->ioc_hwif->ioc_isr_mode_set(__ioc, __msix)); \
+} while (0)
#define bfa_ioc_ownership_reset(__ioc) \
((__ioc)->ioc_hwif->ioc_ownership_reset(__ioc))
+#define bfa_ioc_lpu_read_stat(__ioc) do { \
+ if ((__ioc)->ioc_hwif->ioc_lpu_read_stat) \
+ ((__ioc)->ioc_hwif->ioc_lpu_read_stat(__ioc)); \
+} while (0)
+
void bfa_nw_ioc_set_ct_hwif(struct bfa_ioc *ioc);
+void bfa_nw_ioc_set_ct2_hwif(struct bfa_ioc *ioc);
+void bfa_nw_ioc_ct2_poweron(struct bfa_ioc *ioc);
void bfa_nw_ioc_attach(struct bfa_ioc *ioc, void *bfa,
struct bfa_ioc_cbfn *cbfn);
void bfa_nw_ioc_auto_recover(bool auto_recover);
void bfa_nw_ioc_detach(struct bfa_ioc *ioc);
void bfa_nw_ioc_pci_init(struct bfa_ioc *ioc, struct bfa_pcidev *pcidev,
- enum bfi_mclass mc);
+ enum bfi_pcifn_class clscode);
u32 bfa_nw_ioc_meminfo(void);
void bfa_nw_ioc_mem_claim(struct bfa_ioc *ioc, u8 *dm_kva, u64 dm_pa);
void bfa_nw_ioc_enable(struct bfa_ioc *ioc);
void bfa_nw_ioc_disable(struct bfa_ioc *ioc);
void bfa_nw_ioc_error_isr(struct bfa_ioc *ioc);
+bool bfa_nw_ioc_is_disabled(struct bfa_ioc *ioc);
void bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr);
-void bfa_nw_ioc_hbfail_register(struct bfa_ioc *ioc,
- struct bfa_ioc_hbfail_notify *notify);
+void bfa_nw_ioc_notify_register(struct bfa_ioc *ioc,
+ struct bfa_ioc_notify *notify);
bool bfa_nw_ioc_sem_get(void __iomem *sem_reg);
void bfa_nw_ioc_sem_release(void __iomem *sem_reg);
void bfa_nw_ioc_hw_sem_release(struct bfa_ioc *ioc);
@@ -286,7 +319,7 @@ void bfa_nw_iocpf_sem_timeout(void *ioc);
/*
* F/W Image Size & Chunk
*/
-u32 *bfa_cb_image_get_chunk(int type, u32 off);
-u32 bfa_cb_image_get_size(int type);
+u32 *bfa_cb_image_get_chunk(enum bfi_asic_gen asic_gen, u32 off);
+u32 bfa_cb_image_get_size(enum bfi_asic_gen asic_gen);
#endif /* __BFA_IOC_H__ */
diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc_ct.c b/drivers/net/ethernet/brocade/bna/bfa_ioc_ct.c
new file mode 100644
index 000000000000..348479bbfa3a
--- /dev/null
+++ b/drivers/net/ethernet/brocade/bna/bfa_ioc_ct.c
@@ -0,0 +1,878 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+#include "bfa_ioc.h"
+#include "cna.h"
+#include "bfi.h"
+#include "bfi_reg.h"
+#include "bfa_defs.h"
+
+#define bfa_ioc_ct_sync_pos(__ioc) \
+ ((u32) (1 << bfa_ioc_pcifn(__ioc)))
+#define BFA_IOC_SYNC_REQD_SH 16
+#define bfa_ioc_ct_get_sync_ackd(__val) (__val & 0x0000ffff)
+#define bfa_ioc_ct_clear_sync_ackd(__val) (__val & 0xffff0000)
+#define bfa_ioc_ct_get_sync_reqd(__val) (__val >> BFA_IOC_SYNC_REQD_SH)
+#define bfa_ioc_ct_sync_reqd_pos(__ioc) \
+ (bfa_ioc_ct_sync_pos(__ioc) << BFA_IOC_SYNC_REQD_SH)
+
+/*
+ * forward declarations
+ */
+static bool bfa_ioc_ct_firmware_lock(struct bfa_ioc *ioc);
+static void bfa_ioc_ct_firmware_unlock(struct bfa_ioc *ioc);
+static void bfa_ioc_ct_reg_init(struct bfa_ioc *ioc);
+static void bfa_ioc_ct2_reg_init(struct bfa_ioc *ioc);
+static void bfa_ioc_ct_map_port(struct bfa_ioc *ioc);
+static void bfa_ioc_ct2_map_port(struct bfa_ioc *ioc);
+static void bfa_ioc_ct_isr_mode_set(struct bfa_ioc *ioc, bool msix);
+static void bfa_ioc_ct_notify_fail(struct bfa_ioc *ioc);
+static void bfa_ioc_ct_ownership_reset(struct bfa_ioc *ioc);
+static bool bfa_ioc_ct_sync_start(struct bfa_ioc *ioc);
+static void bfa_ioc_ct_sync_join(struct bfa_ioc *ioc);
+static void bfa_ioc_ct_sync_leave(struct bfa_ioc *ioc);
+static void bfa_ioc_ct_sync_ack(struct bfa_ioc *ioc);
+static bool bfa_ioc_ct_sync_complete(struct bfa_ioc *ioc);
+static enum bfa_status bfa_ioc_ct_pll_init(void __iomem *rb,
+ enum bfi_asic_mode asic_mode);
+static enum bfa_status bfa_ioc_ct2_pll_init(void __iomem *rb,
+ enum bfi_asic_mode asic_mode);
+static bool bfa_ioc_ct2_lpu_read_stat(struct bfa_ioc *ioc);
+
+static const struct bfa_ioc_hwif nw_hwif_ct = {
+ .ioc_pll_init = bfa_ioc_ct_pll_init,
+ .ioc_firmware_lock = bfa_ioc_ct_firmware_lock,
+ .ioc_firmware_unlock = bfa_ioc_ct_firmware_unlock,
+ .ioc_reg_init = bfa_ioc_ct_reg_init,
+ .ioc_map_port = bfa_ioc_ct_map_port,
+ .ioc_isr_mode_set = bfa_ioc_ct_isr_mode_set,
+ .ioc_notify_fail = bfa_ioc_ct_notify_fail,
+ .ioc_ownership_reset = bfa_ioc_ct_ownership_reset,
+ .ioc_sync_start = bfa_ioc_ct_sync_start,
+ .ioc_sync_join = bfa_ioc_ct_sync_join,
+ .ioc_sync_leave = bfa_ioc_ct_sync_leave,
+ .ioc_sync_ack = bfa_ioc_ct_sync_ack,
+ .ioc_sync_complete = bfa_ioc_ct_sync_complete,
+};
+
+static const struct bfa_ioc_hwif nw_hwif_ct2 = {
+ .ioc_pll_init = bfa_ioc_ct2_pll_init,
+ .ioc_firmware_lock = bfa_ioc_ct_firmware_lock,
+ .ioc_firmware_unlock = bfa_ioc_ct_firmware_unlock,
+ .ioc_reg_init = bfa_ioc_ct2_reg_init,
+ .ioc_map_port = bfa_ioc_ct2_map_port,
+ .ioc_lpu_read_stat = bfa_ioc_ct2_lpu_read_stat,
+ .ioc_isr_mode_set = NULL,
+ .ioc_notify_fail = bfa_ioc_ct_notify_fail,
+ .ioc_ownership_reset = bfa_ioc_ct_ownership_reset,
+ .ioc_sync_start = bfa_ioc_ct_sync_start,
+ .ioc_sync_join = bfa_ioc_ct_sync_join,
+ .ioc_sync_leave = bfa_ioc_ct_sync_leave,
+ .ioc_sync_ack = bfa_ioc_ct_sync_ack,
+ .ioc_sync_complete = bfa_ioc_ct_sync_complete,
+};
+
+/**
+ * Called from bfa_ioc_attach() to map asic specific calls.
+ */
+void
+bfa_nw_ioc_set_ct_hwif(struct bfa_ioc *ioc)
+{
+ ioc->ioc_hwif = &nw_hwif_ct;
+}
+
+void
+bfa_nw_ioc_set_ct2_hwif(struct bfa_ioc *ioc)
+{
+ ioc->ioc_hwif = &nw_hwif_ct2;
+}
+
+/**
+ * Return true if firmware of current driver matches the running firmware.
+ */
+static bool
+bfa_ioc_ct_firmware_lock(struct bfa_ioc *ioc)
+{
+ enum bfi_ioc_state ioc_fwstate;
+ u32 usecnt;
+ struct bfi_ioc_image_hdr fwhdr;
+
+ /**
+ * If bios boot (flash based) -- do not increment usage count
+ */
+ if (bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc)) <
+ BFA_IOC_FWIMG_MINSZ)
+ return true;
+
+ bfa_nw_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg);
+ usecnt = readl(ioc->ioc_regs.ioc_usage_reg);
+
+ /**
+ * If usage count is 0, always return TRUE.
+ */
+ if (usecnt == 0) {
+ writel(1, ioc->ioc_regs.ioc_usage_reg);
+ bfa_nw_ioc_sem_release(ioc->ioc_regs.ioc_usage_sem_reg);
+ writel(0, ioc->ioc_regs.ioc_fail_sync);
+ return true;
+ }
+
+ ioc_fwstate = readl(ioc->ioc_regs.ioc_fwstate);
+
+ /**
+ * Use count cannot be non-zero and chip in uninitialized state.
+ */
+ BUG_ON(!(ioc_fwstate != BFI_IOC_UNINIT));
+
+ /**
+ * Check if another driver with a different firmware is active
+ */
+ bfa_nw_ioc_fwver_get(ioc, &fwhdr);
+ if (!bfa_nw_ioc_fwver_cmp(ioc, &fwhdr)) {
+ bfa_nw_ioc_sem_release(ioc->ioc_regs.ioc_usage_sem_reg);
+ return false;
+ }
+
+ /**
+ * Same firmware version. Increment the reference count.
+ */
+ usecnt++;
+ writel(usecnt, ioc->ioc_regs.ioc_usage_reg);
+ bfa_nw_ioc_sem_release(ioc->ioc_regs.ioc_usage_sem_reg);
+ return true;
+}
+
+static void
+bfa_ioc_ct_firmware_unlock(struct bfa_ioc *ioc)
+{
+ u32 usecnt;
+
+ /**
+ * If bios boot (flash based) -- do not decrement usage count
+ */
+ if (bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc)) <
+ BFA_IOC_FWIMG_MINSZ)
+ return;
+
+ /**
+ * decrement usage count
+ */
+ bfa_nw_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg);
+ usecnt = readl(ioc->ioc_regs.ioc_usage_reg);
+ BUG_ON(!(usecnt > 0));
+
+ usecnt--;
+ writel(usecnt, ioc->ioc_regs.ioc_usage_reg);
+
+ bfa_nw_ioc_sem_release(ioc->ioc_regs.ioc_usage_sem_reg);
+}
+
+/**
+ * Notify other functions on HB failure.
+ */
+static void
+bfa_ioc_ct_notify_fail(struct bfa_ioc *ioc)
+{
+ writel(__FW_INIT_HALT_P, ioc->ioc_regs.ll_halt);
+ writel(__FW_INIT_HALT_P, ioc->ioc_regs.alt_ll_halt);
+ /* Wait for halt to take effect */
+ readl(ioc->ioc_regs.ll_halt);
+ readl(ioc->ioc_regs.alt_ll_halt);
+}
+
+/**
+ * Host to LPU mailbox message addresses
+ */
+static const struct {
+ u32 hfn_mbox;
+ u32 lpu_mbox;
+ u32 hfn_pgn;
+} ct_fnreg[] = {
+ { HOSTFN0_LPU_MBOX0_0, LPU_HOSTFN0_MBOX0_0, HOST_PAGE_NUM_FN0 },
+ { HOSTFN1_LPU_MBOX0_8, LPU_HOSTFN1_MBOX0_8, HOST_PAGE_NUM_FN1 },
+ { HOSTFN2_LPU_MBOX0_0, LPU_HOSTFN2_MBOX0_0, HOST_PAGE_NUM_FN2 },
+ { HOSTFN3_LPU_MBOX0_8, LPU_HOSTFN3_MBOX0_8, HOST_PAGE_NUM_FN3 }
+};
+
+/**
+ * Host <-> LPU mailbox command/status registers - port 0
+ */
+static const struct {
+ u32 hfn;
+ u32 lpu;
+} ct_p0reg[] = {
+ { HOSTFN0_LPU0_CMD_STAT, LPU0_HOSTFN0_CMD_STAT },
+ { HOSTFN1_LPU0_CMD_STAT, LPU0_HOSTFN1_CMD_STAT },
+ { HOSTFN2_LPU0_CMD_STAT, LPU0_HOSTFN2_CMD_STAT },
+ { HOSTFN3_LPU0_CMD_STAT, LPU0_HOSTFN3_CMD_STAT }
+};
+
+/**
+ * Host <-> LPU mailbox command/status registers - port 1
+ */
+static const struct {
+ u32 hfn;
+ u32 lpu;
+} ct_p1reg[] = {
+ { HOSTFN0_LPU1_CMD_STAT, LPU1_HOSTFN0_CMD_STAT },
+ { HOSTFN1_LPU1_CMD_STAT, LPU1_HOSTFN1_CMD_STAT },
+ { HOSTFN2_LPU1_CMD_STAT, LPU1_HOSTFN2_CMD_STAT },
+ { HOSTFN3_LPU1_CMD_STAT, LPU1_HOSTFN3_CMD_STAT }
+};
+
+static const struct {
+ u32 hfn_mbox;
+ u32 lpu_mbox;
+ u32 hfn_pgn;
+ u32 hfn;
+ u32 lpu;
+ u32 lpu_read;
+} ct2_reg[] = {
+ { CT2_HOSTFN_LPU0_MBOX0, CT2_LPU0_HOSTFN_MBOX0, CT2_HOSTFN_PAGE_NUM,
+ CT2_HOSTFN_LPU0_CMD_STAT, CT2_LPU0_HOSTFN_CMD_STAT,
+ CT2_HOSTFN_LPU0_READ_STAT},
+ { CT2_HOSTFN_LPU1_MBOX0, CT2_LPU1_HOSTFN_MBOX0, CT2_HOSTFN_PAGE_NUM,
+ CT2_HOSTFN_LPU1_CMD_STAT, CT2_LPU1_HOSTFN_CMD_STAT,
+ CT2_HOSTFN_LPU1_READ_STAT},
+};
+
+static void
+bfa_ioc_ct_reg_init(struct bfa_ioc *ioc)
+{
+ void __iomem *rb;
+ int pcifn = bfa_ioc_pcifn(ioc);
+
+ rb = bfa_ioc_bar0(ioc);
+
+ ioc->ioc_regs.hfn_mbox = rb + ct_fnreg[pcifn].hfn_mbox;
+ ioc->ioc_regs.lpu_mbox = rb + ct_fnreg[pcifn].lpu_mbox;
+ ioc->ioc_regs.host_page_num_fn = rb + ct_fnreg[pcifn].hfn_pgn;
+
+ if (ioc->port_id == 0) {
+ ioc->ioc_regs.heartbeat = rb + BFA_IOC0_HBEAT_REG;
+ ioc->ioc_regs.ioc_fwstate = rb + BFA_IOC0_STATE_REG;
+ ioc->ioc_regs.alt_ioc_fwstate = rb + BFA_IOC1_STATE_REG;
+ ioc->ioc_regs.hfn_mbox_cmd = rb + ct_p0reg[pcifn].hfn;
+ ioc->ioc_regs.lpu_mbox_cmd = rb + ct_p0reg[pcifn].lpu;
+ ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P0;
+ ioc->ioc_regs.alt_ll_halt = rb + FW_INIT_HALT_P1;
+ } else {
+ ioc->ioc_regs.heartbeat = rb + BFA_IOC1_HBEAT_REG;
+ ioc->ioc_regs.ioc_fwstate = rb + BFA_IOC1_STATE_REG;
+ ioc->ioc_regs.alt_ioc_fwstate = rb + BFA_IOC0_STATE_REG;
+ ioc->ioc_regs.hfn_mbox_cmd = rb + ct_p1reg[pcifn].hfn;
+ ioc->ioc_regs.lpu_mbox_cmd = rb + ct_p1reg[pcifn].lpu;
+ ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P1;
+ ioc->ioc_regs.alt_ll_halt = rb + FW_INIT_HALT_P0;
+ }
+
+ /*
+ * PSS control registers
+ */
+ ioc->ioc_regs.pss_ctl_reg = rb + PSS_CTL_REG;
+ ioc->ioc_regs.pss_err_status_reg = rb + PSS_ERR_STATUS_REG;
+ ioc->ioc_regs.app_pll_fast_ctl_reg = rb + APP_PLL_LCLK_CTL_REG;
+ ioc->ioc_regs.app_pll_slow_ctl_reg = rb + APP_PLL_SCLK_CTL_REG;
+
+ /*
+ * IOC semaphore registers and serialization
+ */
+ ioc->ioc_regs.ioc_sem_reg = rb + HOST_SEM0_REG;
+ ioc->ioc_regs.ioc_usage_sem_reg = rb + HOST_SEM1_REG;
+ ioc->ioc_regs.ioc_init_sem_reg = rb + HOST_SEM2_REG;
+ ioc->ioc_regs.ioc_usage_reg = rb + BFA_FW_USE_COUNT;
+ ioc->ioc_regs.ioc_fail_sync = rb + BFA_IOC_FAIL_SYNC;
+
+ /**
+ * sram memory access
+ */
+ ioc->ioc_regs.smem_page_start = rb + PSS_SMEM_PAGE_START;
+ ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CT;
+
+ /*
+ * err set reg : for notification of hb failure in fcmode
+ */
+ ioc->ioc_regs.err_set = (rb + ERR_SET_REG);
+}
+
+static void
+bfa_ioc_ct2_reg_init(struct bfa_ioc *ioc)
+{
+ void __iomem *rb;
+ int port = bfa_ioc_portid(ioc);
+
+ rb = bfa_ioc_bar0(ioc);
+
+ ioc->ioc_regs.hfn_mbox = rb + ct2_reg[port].hfn_mbox;
+ ioc->ioc_regs.lpu_mbox = rb + ct2_reg[port].lpu_mbox;
+ ioc->ioc_regs.host_page_num_fn = rb + ct2_reg[port].hfn_pgn;
+ ioc->ioc_regs.hfn_mbox_cmd = rb + ct2_reg[port].hfn;
+ ioc->ioc_regs.lpu_mbox_cmd = rb + ct2_reg[port].lpu;
+ ioc->ioc_regs.lpu_read_stat = rb + ct2_reg[port].lpu_read;
+
+ if (port == 0) {
+ ioc->ioc_regs.heartbeat = rb + CT2_BFA_IOC0_HBEAT_REG;
+ ioc->ioc_regs.ioc_fwstate = rb + CT2_BFA_IOC0_STATE_REG;
+ ioc->ioc_regs.alt_ioc_fwstate = rb + CT2_BFA_IOC1_STATE_REG;
+ ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P0;
+ ioc->ioc_regs.alt_ll_halt = rb + FW_INIT_HALT_P1;
+ } else {
+ ioc->ioc_regs.heartbeat = rb + CT2_BFA_IOC1_HBEAT_REG;
+ ioc->ioc_regs.ioc_fwstate = rb + CT2_BFA_IOC1_STATE_REG;
+ ioc->ioc_regs.alt_ioc_fwstate = rb + CT2_BFA_IOC0_STATE_REG;
+ ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P1;
+ ioc->ioc_regs.alt_ll_halt = rb + FW_INIT_HALT_P0;
+ }
+
+ /*
+ * PSS control registers
+ */
+ ioc->ioc_regs.pss_ctl_reg = rb + PSS_CTL_REG;
+ ioc->ioc_regs.pss_err_status_reg = rb + PSS_ERR_STATUS_REG;
+ ioc->ioc_regs.app_pll_fast_ctl_reg = rb + CT2_APP_PLL_LCLK_CTL_REG;
+ ioc->ioc_regs.app_pll_slow_ctl_reg = rb + CT2_APP_PLL_SCLK_CTL_REG;
+
+ /*
+ * IOC semaphore registers and serialization
+ */
+ ioc->ioc_regs.ioc_sem_reg = rb + CT2_HOST_SEM0_REG;
+ ioc->ioc_regs.ioc_usage_sem_reg = rb + CT2_HOST_SEM1_REG;
+ ioc->ioc_regs.ioc_init_sem_reg = rb + CT2_HOST_SEM2_REG;
+ ioc->ioc_regs.ioc_usage_reg = rb + CT2_BFA_FW_USE_COUNT;
+ ioc->ioc_regs.ioc_fail_sync = rb + CT2_BFA_IOC_FAIL_SYNC;
+
+ /**
+ * sram memory access
+ */
+ ioc->ioc_regs.smem_page_start = rb + PSS_SMEM_PAGE_START;
+ ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CT;
+
+ /*
+ * err set reg : for notification of hb failure in fcmode
+ */
+ ioc->ioc_regs.err_set = rb + ERR_SET_REG;
+}
+
+/**
+ * Initialize IOC to port mapping.
+ */
+
+#define FNC_PERS_FN_SHIFT(__fn) ((__fn) * 8)
+static void
+bfa_ioc_ct_map_port(struct bfa_ioc *ioc)
+{
+ void __iomem *rb = ioc->pcidev.pci_bar_kva;
+ u32 r32;
+
+ /**
+ * For catapult, base port id on personality register and IOC type
+ */
+ r32 = readl(rb + FNC_PERS_REG);
+ r32 >>= FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc));
+ ioc->port_id = (r32 & __F0_PORT_MAP_MK) >> __F0_PORT_MAP_SH;
+
+}
+
+static void
+bfa_ioc_ct2_map_port(struct bfa_ioc *ioc)
+{
+ void __iomem *rb = ioc->pcidev.pci_bar_kva;
+ u32 r32;
+
+ r32 = readl(rb + CT2_HOSTFN_PERSONALITY0);
+ ioc->port_id = ((r32 & __FC_LL_PORT_MAP__MK) >> __FC_LL_PORT_MAP__SH);
+}
+
+/**
+ * Set interrupt mode for a function: INTX or MSIX
+ */
+static void
+bfa_ioc_ct_isr_mode_set(struct bfa_ioc *ioc, bool msix)
+{
+ void __iomem *rb = ioc->pcidev.pci_bar_kva;
+ u32 r32, mode;
+
+ r32 = readl(rb + FNC_PERS_REG);
+
+ mode = (r32 >> FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc))) &
+ __F0_INTX_STATUS;
+
+ /**
+ * If already in desired mode, do not change anything
+ */
+ if ((!msix && mode) || (msix && !mode))
+ return;
+
+ if (msix)
+ mode = __F0_INTX_STATUS_MSIX;
+ else
+ mode = __F0_INTX_STATUS_INTA;
+
+ r32 &= ~(__F0_INTX_STATUS << FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc)));
+ r32 |= (mode << FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc)));
+
+ writel(r32, rb + FNC_PERS_REG);
+}
+
+static bool
+bfa_ioc_ct2_lpu_read_stat(struct bfa_ioc *ioc)
+{
+ u32 r32;
+
+ r32 = readl(ioc->ioc_regs.lpu_read_stat);
+ if (r32) {
+ writel(1, ioc->ioc_regs.lpu_read_stat);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * MSI-X resource allocation for 1860 with no asic block
+ */
+#define HOSTFN_MSIX_DEFAULT 64
+#define HOSTFN_MSIX_VT_INDEX_MBOX_ERR 0x30138
+#define HOSTFN_MSIX_VT_OFST_NUMVT 0x3013c
+#define __MSIX_VT_NUMVT__MK 0x003ff800
+#define __MSIX_VT_NUMVT__SH 11
+#define __MSIX_VT_NUMVT_(_v) ((_v) << __MSIX_VT_NUMVT__SH)
+#define __MSIX_VT_OFST_ 0x000007ff
+void
+bfa_nw_ioc_ct2_poweron(struct bfa_ioc *ioc)
+{
+ void __iomem *rb = ioc->pcidev.pci_bar_kva;
+ u32 r32;
+
+ r32 = readl(rb + HOSTFN_MSIX_VT_OFST_NUMVT);
+ if (r32 & __MSIX_VT_NUMVT__MK) {
+ writel(r32 & __MSIX_VT_OFST_,
+ rb + HOSTFN_MSIX_VT_INDEX_MBOX_ERR);
+ return;
+ }
+
+ writel(__MSIX_VT_NUMVT_(HOSTFN_MSIX_DEFAULT - 1) |
+ HOSTFN_MSIX_DEFAULT * bfa_ioc_pcifn(ioc),
+ rb + HOSTFN_MSIX_VT_OFST_NUMVT);
+ writel(HOSTFN_MSIX_DEFAULT * bfa_ioc_pcifn(ioc),
+ rb + HOSTFN_MSIX_VT_INDEX_MBOX_ERR);
+}
+
+/**
+ * Cleanup hw semaphore and usecnt registers
+ */
+static void
+bfa_ioc_ct_ownership_reset(struct bfa_ioc *ioc)
+{
+ bfa_nw_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg);
+ writel(0, ioc->ioc_regs.ioc_usage_reg);
+ bfa_nw_ioc_sem_release(ioc->ioc_regs.ioc_usage_sem_reg);
+
+ /*
+ * Read the hw sem reg to make sure that it is locked
+ * before we clear it. If it is not locked, writing 1
+ * will lock it instead of clearing it.
+ */
+ readl(ioc->ioc_regs.ioc_sem_reg);
+ bfa_nw_ioc_hw_sem_release(ioc);
+}
+
+/**
+ * Synchronized IOC failure processing routines
+ */
+static bool
+bfa_ioc_ct_sync_start(struct bfa_ioc *ioc)
+{
+ u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
+ u32 sync_reqd = bfa_ioc_ct_get_sync_reqd(r32);
+
+ /*
+ * Driver load time. If the sync required bit for this PCI fn
+ * is set, it is due to an unclean exit by the driver for this
+ * PCI fn in the previous incarnation. Whoever comes here first
+ * should clean it up, no matter which PCI fn.
+ */
+
+ if (sync_reqd & bfa_ioc_ct_sync_pos(ioc)) {
+ writel(0, ioc->ioc_regs.ioc_fail_sync);
+ writel(1, ioc->ioc_regs.ioc_usage_reg);
+ writel(BFI_IOC_UNINIT, ioc->ioc_regs.ioc_fwstate);
+ writel(BFI_IOC_UNINIT, ioc->ioc_regs.alt_ioc_fwstate);
+ return true;
+ }
+
+ return bfa_ioc_ct_sync_complete(ioc);
+}
+/**
+ * Synchronized IOC failure processing routines
+ */
+static void
+bfa_ioc_ct_sync_join(struct bfa_ioc *ioc)
+{
+ u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
+ u32 sync_pos = bfa_ioc_ct_sync_reqd_pos(ioc);
+
+ writel((r32 | sync_pos), ioc->ioc_regs.ioc_fail_sync);
+}
+
+static void
+bfa_ioc_ct_sync_leave(struct bfa_ioc *ioc)
+{
+ u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
+ u32 sync_msk = bfa_ioc_ct_sync_reqd_pos(ioc) |
+ bfa_ioc_ct_sync_pos(ioc);
+
+ writel((r32 & ~sync_msk), ioc->ioc_regs.ioc_fail_sync);
+}
+
+static void
+bfa_ioc_ct_sync_ack(struct bfa_ioc *ioc)
+{
+ u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
+
+ writel((r32 | bfa_ioc_ct_sync_pos(ioc)), ioc->ioc_regs.ioc_fail_sync);
+}
+
+static bool
+bfa_ioc_ct_sync_complete(struct bfa_ioc *ioc)
+{
+ u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
+ u32 sync_reqd = bfa_ioc_ct_get_sync_reqd(r32);
+ u32 sync_ackd = bfa_ioc_ct_get_sync_ackd(r32);
+ u32 tmp_ackd;
+
+ if (sync_ackd == 0)
+ return true;
+
+ /**
+ * The check below is to see whether any other PCI fn
+ * has reinitialized the ASIC (reset sync_ackd bits)
+ * and failed again while this IOC was waiting for hw
+ * semaphore (in bfa_iocpf_sm_semwait()).
+ */
+ tmp_ackd = sync_ackd;
+ if ((sync_reqd & bfa_ioc_ct_sync_pos(ioc)) &&
+ !(sync_ackd & bfa_ioc_ct_sync_pos(ioc)))
+ sync_ackd |= bfa_ioc_ct_sync_pos(ioc);
+
+ if (sync_reqd == sync_ackd) {
+ writel(bfa_ioc_ct_clear_sync_ackd(r32),
+ ioc->ioc_regs.ioc_fail_sync);
+ writel(BFI_IOC_FAIL, ioc->ioc_regs.ioc_fwstate);
+ writel(BFI_IOC_FAIL, ioc->ioc_regs.alt_ioc_fwstate);
+ return true;
+ }
+
+ /**
+ * If another PCI fn reinitialized and failed again while
+ * this IOC was waiting for hw sem, the sync_ackd bit for
+ * this IOC need to be set again to allow reinitialization.
+ */
+ if (tmp_ackd != sync_ackd)
+ writel((r32 | sync_ackd), ioc->ioc_regs.ioc_fail_sync);
+
+ return false;
+}
+
+static enum bfa_status
+bfa_ioc_ct_pll_init(void __iomem *rb, enum bfi_asic_mode asic_mode)
+{
+ u32 pll_sclk, pll_fclk, r32;
+ bool fcmode = (asic_mode == BFI_ASIC_MODE_FC);
+
+ pll_sclk = __APP_PLL_SCLK_LRESETN | __APP_PLL_SCLK_ENARST |
+ __APP_PLL_SCLK_RSEL200500 | __APP_PLL_SCLK_P0_1(3U) |
+ __APP_PLL_SCLK_JITLMT0_1(3U) |
+ __APP_PLL_SCLK_CNTLMT0_1(1U);
+ pll_fclk = __APP_PLL_LCLK_LRESETN | __APP_PLL_LCLK_ENARST |
+ __APP_PLL_LCLK_RSEL200500 | __APP_PLL_LCLK_P0_1(3U) |
+ __APP_PLL_LCLK_JITLMT0_1(3U) |
+ __APP_PLL_LCLK_CNTLMT0_1(1U);
+
+ if (fcmode) {
+ writel(0, (rb + OP_MODE));
+ writel(__APP_EMS_CMLCKSEL |
+ __APP_EMS_REFCKBUFEN2 |
+ __APP_EMS_CHANNEL_SEL,
+ (rb + ETH_MAC_SER_REG));
+ } else {
+ writel(__GLOBAL_FCOE_MODE, (rb + OP_MODE));
+ writel(__APP_EMS_REFCKBUFEN1,
+ (rb + ETH_MAC_SER_REG));
+ }
+ writel(BFI_IOC_UNINIT, (rb + BFA_IOC0_STATE_REG));
+ writel(BFI_IOC_UNINIT, (rb + BFA_IOC1_STATE_REG));
+ writel(0xffffffffU, (rb + HOSTFN0_INT_MSK));
+ writel(0xffffffffU, (rb + HOSTFN1_INT_MSK));
+ writel(0xffffffffU, (rb + HOSTFN0_INT_STATUS));
+ writel(0xffffffffU, (rb + HOSTFN1_INT_STATUS));
+ writel(0xffffffffU, (rb + HOSTFN0_INT_MSK));
+ writel(0xffffffffU, (rb + HOSTFN1_INT_MSK));
+ writel(pll_sclk |
+ __APP_PLL_SCLK_LOGIC_SOFT_RESET,
+ rb + APP_PLL_SCLK_CTL_REG);
+ writel(pll_fclk |
+ __APP_PLL_LCLK_LOGIC_SOFT_RESET,
+ rb + APP_PLL_LCLK_CTL_REG);
+ writel(pll_sclk |
+ __APP_PLL_SCLK_LOGIC_SOFT_RESET | __APP_PLL_SCLK_ENABLE,
+ rb + APP_PLL_SCLK_CTL_REG);
+ writel(pll_fclk |
+ __APP_PLL_LCLK_LOGIC_SOFT_RESET | __APP_PLL_LCLK_ENABLE,
+ rb + APP_PLL_LCLK_CTL_REG);
+ readl(rb + HOSTFN0_INT_MSK);
+ udelay(2000);
+ writel(0xffffffffU, (rb + HOSTFN0_INT_STATUS));
+ writel(0xffffffffU, (rb + HOSTFN1_INT_STATUS));
+ writel(pll_sclk |
+ __APP_PLL_SCLK_ENABLE,
+ rb + APP_PLL_SCLK_CTL_REG);
+ writel(pll_fclk |
+ __APP_PLL_LCLK_ENABLE,
+ rb + APP_PLL_LCLK_CTL_REG);
+
+ if (!fcmode) {
+ writel(__PMM_1T_RESET_P, (rb + PMM_1T_RESET_REG_P0));
+ writel(__PMM_1T_RESET_P, (rb + PMM_1T_RESET_REG_P1));
+ }
+ r32 = readl((rb + PSS_CTL_REG));
+ r32 &= ~__PSS_LMEM_RESET;
+ writel(r32, (rb + PSS_CTL_REG));
+ udelay(1000);
+ if (!fcmode) {
+ writel(0, (rb + PMM_1T_RESET_REG_P0));
+ writel(0, (rb + PMM_1T_RESET_REG_P1));
+ }
+
+ writel(__EDRAM_BISTR_START, (rb + MBIST_CTL_REG));
+ udelay(1000);
+ r32 = readl((rb + MBIST_STAT_REG));
+ writel(0, (rb + MBIST_CTL_REG));
+ return BFA_STATUS_OK;
+}
+
+static void
+bfa_ioc_ct2_sclk_init(void __iomem *rb)
+{
+ u32 r32;
+
+ /*
+ * put s_clk PLL and PLL FSM in reset
+ */
+ r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
+ r32 &= ~(__APP_PLL_SCLK_ENABLE | __APP_PLL_SCLK_LRESETN);
+ r32 |= (__APP_PLL_SCLK_ENARST | __APP_PLL_SCLK_BYPASS |
+ __APP_PLL_SCLK_LOGIC_SOFT_RESET);
+ writel(r32, (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+ /*
+ * Ignore mode and program for the max clock (which is FC16)
+ * Firmware/NFC will do the PLL init appropiately
+ */
+ r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
+ r32 &= ~(__APP_PLL_SCLK_REFCLK_SEL | __APP_PLL_SCLK_CLK_DIV2);
+ writel(r32, (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+ /*
+ * while doing PLL init dont clock gate ethernet subsystem
+ */
+ r32 = readl((rb + CT2_CHIP_MISC_PRG));
+ writel((r32 | __ETH_CLK_ENABLE_PORT0),
+ (rb + CT2_CHIP_MISC_PRG));
+
+ r32 = readl((rb + CT2_PCIE_MISC_REG));
+ writel((r32 | __ETH_CLK_ENABLE_PORT1),
+ (rb + CT2_PCIE_MISC_REG));
+
+ /*
+ * set sclk value
+ */
+ r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
+ r32 &= (__P_SCLK_PLL_LOCK | __APP_PLL_SCLK_REFCLK_SEL |
+ __APP_PLL_SCLK_CLK_DIV2);
+ writel(r32 | 0x1061731b, (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+ /*
+ * poll for s_clk lock or delay 1ms
+ */
+ udelay(1000);
+
+ /*
+ * Dont do clock gating for ethernet subsystem, firmware/NFC will
+ * do this appropriately
+ */
+}
+
+static void
+bfa_ioc_ct2_lclk_init(void __iomem *rb)
+{
+ u32 r32;
+
+ /*
+ * put l_clk PLL and PLL FSM in reset
+ */
+ r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
+ r32 &= ~(__APP_PLL_LCLK_ENABLE | __APP_PLL_LCLK_LRESETN);
+ r32 |= (__APP_PLL_LCLK_ENARST | __APP_PLL_LCLK_BYPASS |
+ __APP_PLL_LCLK_LOGIC_SOFT_RESET);
+ writel(r32, (rb + CT2_APP_PLL_LCLK_CTL_REG));
+
+ /*
+ * set LPU speed (set for FC16 which will work for other modes)
+ */
+ r32 = readl((rb + CT2_CHIP_MISC_PRG));
+ writel(r32, (rb + CT2_CHIP_MISC_PRG));
+
+ /*
+ * set LPU half speed (set for FC16 which will work for other modes)
+ */
+ r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
+ writel(r32, (rb + CT2_APP_PLL_LCLK_CTL_REG));
+
+ /*
+ * set lclk for mode (set for FC16)
+ */
+ r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
+ r32 &= (__P_LCLK_PLL_LOCK | __APP_LPUCLK_HALFSPEED);
+ r32 |= 0x20c1731b;
+ writel(r32, (rb + CT2_APP_PLL_LCLK_CTL_REG));
+
+ /*
+ * poll for s_clk lock or delay 1ms
+ */
+ udelay(1000);
+}
+
+static void
+bfa_ioc_ct2_mem_init(void __iomem *rb)
+{
+ u32 r32;
+
+ r32 = readl((rb + PSS_CTL_REG));
+ r32 &= ~__PSS_LMEM_RESET;
+ writel(r32, (rb + PSS_CTL_REG));
+ udelay(1000);
+
+ writel(__EDRAM_BISTR_START, (rb + CT2_MBIST_CTL_REG));
+ udelay(1000);
+ writel(0, (rb + CT2_MBIST_CTL_REG));
+}
+
+static void
+bfa_ioc_ct2_mac_reset(void __iomem *rb)
+{
+ volatile u32 r32;
+
+ bfa_ioc_ct2_sclk_init(rb);
+ bfa_ioc_ct2_lclk_init(rb);
+
+ /*
+ * release soft reset on s_clk & l_clk
+ */
+ r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
+ writel((r32 & ~__APP_PLL_SCLK_LOGIC_SOFT_RESET),
+ (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+ /*
+ * release soft reset on s_clk & l_clk
+ */
+ r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
+ writel((r32 & ~__APP_PLL_LCLK_LOGIC_SOFT_RESET),
+ (rb + CT2_APP_PLL_LCLK_CTL_REG));
+
+ /* put port0, port1 MAC & AHB in reset */
+ writel((__CSI_MAC_RESET | __CSI_MAC_AHB_RESET),
+ (rb + CT2_CSI_MAC_CONTROL_REG(0)));
+ writel((__CSI_MAC_RESET | __CSI_MAC_AHB_RESET),
+ (rb + CT2_CSI_MAC_CONTROL_REG(1)));
+}
+
+#define CT2_NFC_MAX_DELAY 1000
+static enum bfa_status
+bfa_ioc_ct2_pll_init(void __iomem *rb, enum bfi_asic_mode asic_mode)
+{
+ volatile u32 wgn, r32;
+ int i;
+
+ /*
+ * Initialize PLL if not already done by NFC
+ */
+ wgn = readl(rb + CT2_WGN_STATUS);
+ if (!(wgn & __GLBL_PF_VF_CFG_RDY)) {
+ writel(__HALT_NFC_CONTROLLER, (rb + CT2_NFC_CSR_SET_REG));
+ for (i = 0; i < CT2_NFC_MAX_DELAY; i++) {
+ r32 = readl(rb + CT2_NFC_CSR_SET_REG);
+ if (r32 & __NFC_CONTROLLER_HALTED)
+ break;
+ udelay(1000);
+ }
+ }
+
+ /*
+ * Mask the interrupts and clear any
+ * pending interrupts left by BIOS/EFI
+ */
+
+ writel(1, (rb + CT2_LPU0_HOSTFN_MBOX0_MSK));
+ writel(1, (rb + CT2_LPU1_HOSTFN_MBOX0_MSK));
+
+ r32 = readl((rb + CT2_LPU0_HOSTFN_CMD_STAT));
+ if (r32 == 1) {
+ writel(1, (rb + CT2_LPU0_HOSTFN_CMD_STAT));
+ readl((rb + CT2_LPU0_HOSTFN_CMD_STAT));
+ }
+ r32 = readl((rb + CT2_LPU1_HOSTFN_CMD_STAT));
+ if (r32 == 1) {
+ writel(1, (rb + CT2_LPU1_HOSTFN_CMD_STAT));
+ readl((rb + CT2_LPU1_HOSTFN_CMD_STAT));
+ }
+
+ bfa_ioc_ct2_mac_reset(rb);
+ bfa_ioc_ct2_sclk_init(rb);
+ bfa_ioc_ct2_lclk_init(rb);
+
+ /*
+ * release soft reset on s_clk & l_clk
+ */
+ r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
+ writel((r32 & ~__APP_PLL_SCLK_LOGIC_SOFT_RESET),
+ (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+ /*
+ * release soft reset on s_clk & l_clk
+ */
+ r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
+ writel(r32 & ~__APP_PLL_LCLK_LOGIC_SOFT_RESET,
+ (rb + CT2_APP_PLL_LCLK_CTL_REG));
+
+ /*
+ * Announce flash device presence, if flash was corrupted.
+ */
+ if (wgn == (__WGN_READY | __GLBL_PF_VF_CFG_RDY)) {
+ r32 = readl((rb + PSS_GPIO_OUT_REG));
+ writel((r32 & ~1), (rb + PSS_GPIO_OUT_REG));
+ r32 = readl((rb + PSS_GPIO_OE_REG));
+ writel((r32 | 1), (rb + PSS_GPIO_OE_REG));
+ }
+
+ bfa_ioc_ct2_mem_init(rb);
+
+ writel(BFI_IOC_UNINIT, (rb + CT2_BFA_IOC0_STATE_REG));
+ writel(BFI_IOC_UNINIT, (rb + CT2_BFA_IOC1_STATE_REG));
+ return BFA_STATUS_OK;
+}
diff --git a/drivers/net/ethernet/brocade/bna/bfa_msgq.c b/drivers/net/ethernet/brocade/bna/bfa_msgq.c
new file mode 100644
index 000000000000..dd36427f4752
--- /dev/null
+++ b/drivers/net/ethernet/brocade/bna/bfa_msgq.c
@@ -0,0 +1,669 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+/**
+ * @file bfa_msgq.c MSGQ module source file.
+ */
+
+#include "bfi.h"
+#include "bfa_msgq.h"
+#include "bfa_ioc.h"
+
+#define call_cmdq_ent_cbfn(_cmdq_ent, _status) \
+{ \
+ bfa_msgq_cmdcbfn_t cbfn; \
+ void *cbarg; \
+ cbfn = (_cmdq_ent)->cbfn; \
+ cbarg = (_cmdq_ent)->cbarg; \
+ (_cmdq_ent)->cbfn = NULL; \
+ (_cmdq_ent)->cbarg = NULL; \
+ if (cbfn) { \
+ cbfn(cbarg, (_status)); \
+ } \
+}
+
+static void bfa_msgq_cmdq_dbell(struct bfa_msgq_cmdq *cmdq);
+static void bfa_msgq_cmdq_copy_rsp(struct bfa_msgq_cmdq *cmdq);
+
+enum cmdq_event {
+ CMDQ_E_START = 1,
+ CMDQ_E_STOP = 2,
+ CMDQ_E_FAIL = 3,
+ CMDQ_E_POST = 4,
+ CMDQ_E_INIT_RESP = 5,
+ CMDQ_E_DB_READY = 6,
+};
+
+bfa_fsm_state_decl(cmdq, stopped, struct bfa_msgq_cmdq, enum cmdq_event);
+bfa_fsm_state_decl(cmdq, init_wait, struct bfa_msgq_cmdq, enum cmdq_event);
+bfa_fsm_state_decl(cmdq, ready, struct bfa_msgq_cmdq, enum cmdq_event);
+bfa_fsm_state_decl(cmdq, dbell_wait, struct bfa_msgq_cmdq,
+ enum cmdq_event);
+
+static void
+cmdq_sm_stopped_entry(struct bfa_msgq_cmdq *cmdq)
+{
+ struct bfa_msgq_cmd_entry *cmdq_ent;
+
+ cmdq->producer_index = 0;
+ cmdq->consumer_index = 0;
+ cmdq->flags = 0;
+ cmdq->token = 0;
+ cmdq->offset = 0;
+ cmdq->bytes_to_copy = 0;
+ while (!list_empty(&cmdq->pending_q)) {
+ bfa_q_deq(&cmdq->pending_q, &cmdq_ent);
+ bfa_q_qe_init(&cmdq_ent->qe);
+ call_cmdq_ent_cbfn(cmdq_ent, BFA_STATUS_FAILED);
+ }
+}
+
+static void
+cmdq_sm_stopped(struct bfa_msgq_cmdq *cmdq, enum cmdq_event event)
+{
+ switch (event) {
+ case CMDQ_E_START:
+ bfa_fsm_set_state(cmdq, cmdq_sm_init_wait);
+ break;
+
+ case CMDQ_E_STOP:
+ case CMDQ_E_FAIL:
+ /* No-op */
+ break;
+
+ case CMDQ_E_POST:
+ cmdq->flags |= BFA_MSGQ_CMDQ_F_DB_UPDATE;
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+cmdq_sm_init_wait_entry(struct bfa_msgq_cmdq *cmdq)
+{
+ bfa_wc_down(&cmdq->msgq->init_wc);
+}
+
+static void
+cmdq_sm_init_wait(struct bfa_msgq_cmdq *cmdq, enum cmdq_event event)
+{
+ switch (event) {
+ case CMDQ_E_STOP:
+ case CMDQ_E_FAIL:
+ bfa_fsm_set_state(cmdq, cmdq_sm_stopped);
+ break;
+
+ case CMDQ_E_POST:
+ cmdq->flags |= BFA_MSGQ_CMDQ_F_DB_UPDATE;
+ break;
+
+ case CMDQ_E_INIT_RESP:
+ if (cmdq->flags & BFA_MSGQ_CMDQ_F_DB_UPDATE) {
+ cmdq->flags &= ~BFA_MSGQ_CMDQ_F_DB_UPDATE;
+ bfa_fsm_set_state(cmdq, cmdq_sm_dbell_wait);
+ } else
+ bfa_fsm_set_state(cmdq, cmdq_sm_ready);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+cmdq_sm_ready_entry(struct bfa_msgq_cmdq *cmdq)
+{
+}
+
+static void
+cmdq_sm_ready(struct bfa_msgq_cmdq *cmdq, enum cmdq_event event)
+{
+ switch (event) {
+ case CMDQ_E_STOP:
+ case CMDQ_E_FAIL:
+ bfa_fsm_set_state(cmdq, cmdq_sm_stopped);
+ break;
+
+ case CMDQ_E_POST:
+ bfa_fsm_set_state(cmdq, cmdq_sm_dbell_wait);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+cmdq_sm_dbell_wait_entry(struct bfa_msgq_cmdq *cmdq)
+{
+ bfa_msgq_cmdq_dbell(cmdq);
+}
+
+static void
+cmdq_sm_dbell_wait(struct bfa_msgq_cmdq *cmdq, enum cmdq_event event)
+{
+ switch (event) {
+ case CMDQ_E_STOP:
+ case CMDQ_E_FAIL:
+ bfa_fsm_set_state(cmdq, cmdq_sm_stopped);
+ break;
+
+ case CMDQ_E_POST:
+ cmdq->flags |= BFA_MSGQ_CMDQ_F_DB_UPDATE;
+ break;
+
+ case CMDQ_E_DB_READY:
+ if (cmdq->flags & BFA_MSGQ_CMDQ_F_DB_UPDATE) {
+ cmdq->flags &= ~BFA_MSGQ_CMDQ_F_DB_UPDATE;
+ bfa_fsm_set_state(cmdq, cmdq_sm_dbell_wait);
+ } else
+ bfa_fsm_set_state(cmdq, cmdq_sm_ready);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bfa_msgq_cmdq_dbell_ready(void *arg)
+{
+ struct bfa_msgq_cmdq *cmdq = (struct bfa_msgq_cmdq *)arg;
+ bfa_fsm_send_event(cmdq, CMDQ_E_DB_READY);
+}
+
+static void
+bfa_msgq_cmdq_dbell(struct bfa_msgq_cmdq *cmdq)
+{
+ struct bfi_msgq_h2i_db *dbell =
+ (struct bfi_msgq_h2i_db *)(&cmdq->dbell_mb.msg[0]);
+
+ memset(dbell, 0, sizeof(struct bfi_msgq_h2i_db));
+ bfi_h2i_set(dbell->mh, BFI_MC_MSGQ, BFI_MSGQ_H2I_DOORBELL_PI, 0);
+ dbell->mh.mtag.i2htok = 0;
+ dbell->idx.cmdq_pi = htons(cmdq->producer_index);
+
+ if (!bfa_nw_ioc_mbox_queue(cmdq->msgq->ioc, &cmdq->dbell_mb,
+ bfa_msgq_cmdq_dbell_ready, cmdq)) {
+ bfa_msgq_cmdq_dbell_ready(cmdq);
+ }
+}
+
+static void
+__cmd_copy(struct bfa_msgq_cmdq *cmdq, struct bfa_msgq_cmd_entry *cmd)
+{
+ size_t len = cmd->msg_size;
+ int num_entries = 0;
+ size_t to_copy;
+ u8 *src, *dst;
+
+ src = (u8 *)cmd->msg_hdr;
+ dst = (u8 *)cmdq->addr.kva;
+ dst += (cmdq->producer_index * BFI_MSGQ_CMD_ENTRY_SIZE);
+
+ while (len) {
+ to_copy = (len < BFI_MSGQ_CMD_ENTRY_SIZE) ?
+ len : BFI_MSGQ_CMD_ENTRY_SIZE;
+ memcpy(dst, src, to_copy);
+ len -= to_copy;
+ src += BFI_MSGQ_CMD_ENTRY_SIZE;
+ BFA_MSGQ_INDX_ADD(cmdq->producer_index, 1, cmdq->depth);
+ dst = (u8 *)cmdq->addr.kva;
+ dst += (cmdq->producer_index * BFI_MSGQ_CMD_ENTRY_SIZE);
+ num_entries++;
+ }
+
+}
+
+static void
+bfa_msgq_cmdq_ci_update(struct bfa_msgq_cmdq *cmdq, struct bfi_mbmsg *mb)
+{
+ struct bfi_msgq_i2h_db *dbell = (struct bfi_msgq_i2h_db *)mb;
+ struct bfa_msgq_cmd_entry *cmd;
+ int posted = 0;
+
+ cmdq->consumer_index = ntohs(dbell->idx.cmdq_ci);
+
+ /* Walk through pending list to see if the command can be posted */
+ while (!list_empty(&cmdq->pending_q)) {
+ cmd =
+ (struct bfa_msgq_cmd_entry *)bfa_q_first(&cmdq->pending_q);
+ if (ntohs(cmd->msg_hdr->num_entries) <=
+ BFA_MSGQ_FREE_CNT(cmdq)) {
+ list_del(&cmd->qe);
+ __cmd_copy(cmdq, cmd);
+ posted = 1;
+ call_cmdq_ent_cbfn(cmd, BFA_STATUS_OK);
+ } else {
+ break;
+ }
+ }
+
+ if (posted)
+ bfa_fsm_send_event(cmdq, CMDQ_E_POST);
+}
+
+static void
+bfa_msgq_cmdq_copy_next(void *arg)
+{
+ struct bfa_msgq_cmdq *cmdq = (struct bfa_msgq_cmdq *)arg;
+
+ if (cmdq->bytes_to_copy)
+ bfa_msgq_cmdq_copy_rsp(cmdq);
+}
+
+static void
+bfa_msgq_cmdq_copy_req(struct bfa_msgq_cmdq *cmdq, struct bfi_mbmsg *mb)
+{
+ struct bfi_msgq_i2h_cmdq_copy_req *req =
+ (struct bfi_msgq_i2h_cmdq_copy_req *)mb;
+
+ cmdq->token = 0;
+ cmdq->offset = ntohs(req->offset);
+ cmdq->bytes_to_copy = ntohs(req->len);
+ bfa_msgq_cmdq_copy_rsp(cmdq);
+}
+
+static void
+bfa_msgq_cmdq_copy_rsp(struct bfa_msgq_cmdq *cmdq)
+{
+ struct bfi_msgq_h2i_cmdq_copy_rsp *rsp =
+ (struct bfi_msgq_h2i_cmdq_copy_rsp *)&cmdq->copy_mb.msg[0];
+ int copied;
+ u8 *addr = (u8 *)cmdq->addr.kva;
+
+ memset(rsp, 0, sizeof(struct bfi_msgq_h2i_cmdq_copy_rsp));
+ bfi_h2i_set(rsp->mh, BFI_MC_MSGQ, BFI_MSGQ_H2I_CMDQ_COPY_RSP, 0);
+ rsp->mh.mtag.i2htok = htons(cmdq->token);
+ copied = (cmdq->bytes_to_copy >= BFI_CMD_COPY_SZ) ? BFI_CMD_COPY_SZ :
+ cmdq->bytes_to_copy;
+ addr += cmdq->offset;
+ memcpy(rsp->data, addr, copied);
+
+ cmdq->token++;
+ cmdq->offset += copied;
+ cmdq->bytes_to_copy -= copied;
+
+ if (!bfa_nw_ioc_mbox_queue(cmdq->msgq->ioc, &cmdq->copy_mb,
+ bfa_msgq_cmdq_copy_next, cmdq)) {
+ bfa_msgq_cmdq_copy_next(cmdq);
+ }
+}
+
+static void
+bfa_msgq_cmdq_attach(struct bfa_msgq_cmdq *cmdq, struct bfa_msgq *msgq)
+{
+ cmdq->depth = BFA_MSGQ_CMDQ_NUM_ENTRY;
+ INIT_LIST_HEAD(&cmdq->pending_q);
+ cmdq->msgq = msgq;
+ bfa_fsm_set_state(cmdq, cmdq_sm_stopped);
+}
+
+static void bfa_msgq_rspq_dbell(struct bfa_msgq_rspq *rspq);
+
+enum rspq_event {
+ RSPQ_E_START = 1,
+ RSPQ_E_STOP = 2,
+ RSPQ_E_FAIL = 3,
+ RSPQ_E_RESP = 4,
+ RSPQ_E_INIT_RESP = 5,
+ RSPQ_E_DB_READY = 6,
+};
+
+bfa_fsm_state_decl(rspq, stopped, struct bfa_msgq_rspq, enum rspq_event);
+bfa_fsm_state_decl(rspq, init_wait, struct bfa_msgq_rspq,
+ enum rspq_event);
+bfa_fsm_state_decl(rspq, ready, struct bfa_msgq_rspq, enum rspq_event);
+bfa_fsm_state_decl(rspq, dbell_wait, struct bfa_msgq_rspq,
+ enum rspq_event);
+
+static void
+rspq_sm_stopped_entry(struct bfa_msgq_rspq *rspq)
+{
+ rspq->producer_index = 0;
+ rspq->consumer_index = 0;
+ rspq->flags = 0;
+}
+
+static void
+rspq_sm_stopped(struct bfa_msgq_rspq *rspq, enum rspq_event event)
+{
+ switch (event) {
+ case RSPQ_E_START:
+ bfa_fsm_set_state(rspq, rspq_sm_init_wait);
+ break;
+
+ case RSPQ_E_STOP:
+ case RSPQ_E_FAIL:
+ /* No-op */
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+rspq_sm_init_wait_entry(struct bfa_msgq_rspq *rspq)
+{
+ bfa_wc_down(&rspq->msgq->init_wc);
+}
+
+static void
+rspq_sm_init_wait(struct bfa_msgq_rspq *rspq, enum rspq_event event)
+{
+ switch (event) {
+ case RSPQ_E_FAIL:
+ case RSPQ_E_STOP:
+ bfa_fsm_set_state(rspq, rspq_sm_stopped);
+ break;
+
+ case RSPQ_E_INIT_RESP:
+ bfa_fsm_set_state(rspq, rspq_sm_ready);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+rspq_sm_ready_entry(struct bfa_msgq_rspq *rspq)
+{
+}
+
+static void
+rspq_sm_ready(struct bfa_msgq_rspq *rspq, enum rspq_event event)
+{
+ switch (event) {
+ case RSPQ_E_STOP:
+ case RSPQ_E_FAIL:
+ bfa_fsm_set_state(rspq, rspq_sm_stopped);
+ break;
+
+ case RSPQ_E_RESP:
+ bfa_fsm_set_state(rspq, rspq_sm_dbell_wait);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+rspq_sm_dbell_wait_entry(struct bfa_msgq_rspq *rspq)
+{
+ if (!bfa_nw_ioc_is_disabled(rspq->msgq->ioc))
+ bfa_msgq_rspq_dbell(rspq);
+}
+
+static void
+rspq_sm_dbell_wait(struct bfa_msgq_rspq *rspq, enum rspq_event event)
+{
+ switch (event) {
+ case RSPQ_E_STOP:
+ case RSPQ_E_FAIL:
+ bfa_fsm_set_state(rspq, rspq_sm_stopped);
+ break;
+
+ case RSPQ_E_RESP:
+ rspq->flags |= BFA_MSGQ_RSPQ_F_DB_UPDATE;
+ break;
+
+ case RSPQ_E_DB_READY:
+ if (rspq->flags & BFA_MSGQ_RSPQ_F_DB_UPDATE) {
+ rspq->flags &= ~BFA_MSGQ_RSPQ_F_DB_UPDATE;
+ bfa_fsm_set_state(rspq, rspq_sm_dbell_wait);
+ } else
+ bfa_fsm_set_state(rspq, rspq_sm_ready);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bfa_msgq_rspq_dbell_ready(void *arg)
+{
+ struct bfa_msgq_rspq *rspq = (struct bfa_msgq_rspq *)arg;
+ bfa_fsm_send_event(rspq, RSPQ_E_DB_READY);
+}
+
+static void
+bfa_msgq_rspq_dbell(struct bfa_msgq_rspq *rspq)
+{
+ struct bfi_msgq_h2i_db *dbell =
+ (struct bfi_msgq_h2i_db *)(&rspq->dbell_mb.msg[0]);
+
+ memset(dbell, 0, sizeof(struct bfi_msgq_h2i_db));
+ bfi_h2i_set(dbell->mh, BFI_MC_MSGQ, BFI_MSGQ_H2I_DOORBELL_CI, 0);
+ dbell->mh.mtag.i2htok = 0;
+ dbell->idx.rspq_ci = htons(rspq->consumer_index);
+
+ if (!bfa_nw_ioc_mbox_queue(rspq->msgq->ioc, &rspq->dbell_mb,
+ bfa_msgq_rspq_dbell_ready, rspq)) {
+ bfa_msgq_rspq_dbell_ready(rspq);
+ }
+}
+
+static void
+bfa_msgq_rspq_pi_update(struct bfa_msgq_rspq *rspq, struct bfi_mbmsg *mb)
+{
+ struct bfi_msgq_i2h_db *dbell = (struct bfi_msgq_i2h_db *)mb;
+ struct bfi_msgq_mhdr *msghdr;
+ int num_entries;
+ int mc;
+ u8 *rspq_qe;
+
+ rspq->producer_index = ntohs(dbell->idx.rspq_pi);
+
+ while (rspq->consumer_index != rspq->producer_index) {
+ rspq_qe = (u8 *)rspq->addr.kva;
+ rspq_qe += (rspq->consumer_index * BFI_MSGQ_RSP_ENTRY_SIZE);
+ msghdr = (struct bfi_msgq_mhdr *)rspq_qe;
+
+ mc = msghdr->msg_class;
+ num_entries = ntohs(msghdr->num_entries);
+
+ if ((mc >= BFI_MC_MAX) || (rspq->rsphdlr[mc].cbfn == NULL))
+ break;
+
+ (rspq->rsphdlr[mc].cbfn)(rspq->rsphdlr[mc].cbarg, msghdr);
+
+ BFA_MSGQ_INDX_ADD(rspq->consumer_index, num_entries,
+ rspq->depth);
+ }
+
+ bfa_fsm_send_event(rspq, RSPQ_E_RESP);
+}
+
+static void
+bfa_msgq_rspq_attach(struct bfa_msgq_rspq *rspq, struct bfa_msgq *msgq)
+{
+ rspq->depth = BFA_MSGQ_RSPQ_NUM_ENTRY;
+ rspq->msgq = msgq;
+ bfa_fsm_set_state(rspq, rspq_sm_stopped);
+}
+
+static void
+bfa_msgq_init_rsp(struct bfa_msgq *msgq,
+ struct bfi_mbmsg *mb)
+{
+ bfa_fsm_send_event(&msgq->cmdq, CMDQ_E_INIT_RESP);
+ bfa_fsm_send_event(&msgq->rspq, RSPQ_E_INIT_RESP);
+}
+
+static void
+bfa_msgq_init(void *arg)
+{
+ struct bfa_msgq *msgq = (struct bfa_msgq *)arg;
+ struct bfi_msgq_cfg_req *msgq_cfg =
+ (struct bfi_msgq_cfg_req *)&msgq->init_mb.msg[0];
+
+ memset(msgq_cfg, 0, sizeof(struct bfi_msgq_cfg_req));
+ bfi_h2i_set(msgq_cfg->mh, BFI_MC_MSGQ, BFI_MSGQ_H2I_INIT_REQ, 0);
+ msgq_cfg->mh.mtag.i2htok = 0;
+
+ bfa_dma_be_addr_set(msgq_cfg->cmdq.addr, msgq->cmdq.addr.pa);
+ msgq_cfg->cmdq.q_depth = htons(msgq->cmdq.depth);
+ bfa_dma_be_addr_set(msgq_cfg->rspq.addr, msgq->rspq.addr.pa);
+ msgq_cfg->rspq.q_depth = htons(msgq->rspq.depth);
+
+ bfa_nw_ioc_mbox_queue(msgq->ioc, &msgq->init_mb, NULL, NULL);
+}
+
+static void
+bfa_msgq_isr(void *cbarg, struct bfi_mbmsg *msg)
+{
+ struct bfa_msgq *msgq = (struct bfa_msgq *)cbarg;
+
+ switch (msg->mh.msg_id) {
+ case BFI_MSGQ_I2H_INIT_RSP:
+ bfa_msgq_init_rsp(msgq, msg);
+ break;
+
+ case BFI_MSGQ_I2H_DOORBELL_PI:
+ bfa_msgq_rspq_pi_update(&msgq->rspq, msg);
+ break;
+
+ case BFI_MSGQ_I2H_DOORBELL_CI:
+ bfa_msgq_cmdq_ci_update(&msgq->cmdq, msg);
+ break;
+
+ case BFI_MSGQ_I2H_CMDQ_COPY_REQ:
+ bfa_msgq_cmdq_copy_req(&msgq->cmdq, msg);
+ break;
+
+ default:
+ BUG_ON(1);
+ }
+}
+
+static void
+bfa_msgq_notify(void *cbarg, enum bfa_ioc_event event)
+{
+ struct bfa_msgq *msgq = (struct bfa_msgq *)cbarg;
+
+ switch (event) {
+ case BFA_IOC_E_ENABLED:
+ bfa_wc_init(&msgq->init_wc, bfa_msgq_init, msgq);
+ bfa_wc_up(&msgq->init_wc);
+ bfa_fsm_send_event(&msgq->cmdq, CMDQ_E_START);
+ bfa_wc_up(&msgq->init_wc);
+ bfa_fsm_send_event(&msgq->rspq, RSPQ_E_START);
+ bfa_wc_wait(&msgq->init_wc);
+ break;
+
+ case BFA_IOC_E_DISABLED:
+ bfa_fsm_send_event(&msgq->cmdq, CMDQ_E_STOP);
+ bfa_fsm_send_event(&msgq->rspq, RSPQ_E_STOP);
+ break;
+
+ case BFA_IOC_E_FAILED:
+ bfa_fsm_send_event(&msgq->cmdq, CMDQ_E_FAIL);
+ bfa_fsm_send_event(&msgq->rspq, RSPQ_E_FAIL);
+ break;
+
+ default:
+ break;
+ }
+}
+
+u32
+bfa_msgq_meminfo(void)
+{
+ return roundup(BFA_MSGQ_CMDQ_SIZE, BFA_DMA_ALIGN_SZ) +
+ roundup(BFA_MSGQ_RSPQ_SIZE, BFA_DMA_ALIGN_SZ);
+}
+
+void
+bfa_msgq_memclaim(struct bfa_msgq *msgq, u8 *kva, u64 pa)
+{
+ msgq->cmdq.addr.kva = kva;
+ msgq->cmdq.addr.pa = pa;
+
+ kva += roundup(BFA_MSGQ_CMDQ_SIZE, BFA_DMA_ALIGN_SZ);
+ pa += roundup(BFA_MSGQ_CMDQ_SIZE, BFA_DMA_ALIGN_SZ);
+
+ msgq->rspq.addr.kva = kva;
+ msgq->rspq.addr.pa = pa;
+}
+
+void
+bfa_msgq_attach(struct bfa_msgq *msgq, struct bfa_ioc *ioc)
+{
+ msgq->ioc = ioc;
+
+ bfa_msgq_cmdq_attach(&msgq->cmdq, msgq);
+ bfa_msgq_rspq_attach(&msgq->rspq, msgq);
+
+ bfa_nw_ioc_mbox_regisr(msgq->ioc, BFI_MC_MSGQ, bfa_msgq_isr, msgq);
+ bfa_q_qe_init(&msgq->ioc_notify);
+ bfa_ioc_notify_init(&msgq->ioc_notify, bfa_msgq_notify, msgq);
+ bfa_nw_ioc_notify_register(msgq->ioc, &msgq->ioc_notify);
+}
+
+void
+bfa_msgq_regisr(struct bfa_msgq *msgq, enum bfi_mclass mc,
+ bfa_msgq_mcfunc_t cbfn, void *cbarg)
+{
+ msgq->rspq.rsphdlr[mc].cbfn = cbfn;
+ msgq->rspq.rsphdlr[mc].cbarg = cbarg;
+}
+
+void
+bfa_msgq_cmd_post(struct bfa_msgq *msgq, struct bfa_msgq_cmd_entry *cmd)
+{
+ if (ntohs(cmd->msg_hdr->num_entries) <=
+ BFA_MSGQ_FREE_CNT(&msgq->cmdq)) {
+ __cmd_copy(&msgq->cmdq, cmd);
+ call_cmdq_ent_cbfn(cmd, BFA_STATUS_OK);
+ bfa_fsm_send_event(&msgq->cmdq, CMDQ_E_POST);
+ } else {
+ list_add_tail(&cmd->qe, &msgq->cmdq.pending_q);
+ }
+}
+
+void
+bfa_msgq_rsp_copy(struct bfa_msgq *msgq, u8 *buf, size_t buf_len)
+{
+ struct bfa_msgq_rspq *rspq = &msgq->rspq;
+ size_t len = buf_len;
+ size_t to_copy;
+ int ci;
+ u8 *src, *dst;
+
+ ci = rspq->consumer_index;
+ src = (u8 *)rspq->addr.kva;
+ src += (ci * BFI_MSGQ_RSP_ENTRY_SIZE);
+ dst = buf;
+
+ while (len) {
+ to_copy = (len < BFI_MSGQ_RSP_ENTRY_SIZE) ?
+ len : BFI_MSGQ_RSP_ENTRY_SIZE;
+ memcpy(dst, src, to_copy);
+ len -= to_copy;
+ dst += BFI_MSGQ_RSP_ENTRY_SIZE;
+ BFA_MSGQ_INDX_ADD(ci, 1, rspq->depth);
+ src = (u8 *)rspq->addr.kva;
+ src += (ci * BFI_MSGQ_RSP_ENTRY_SIZE);
+ }
+}
diff --git a/drivers/net/ethernet/brocade/bna/bfa_msgq.h b/drivers/net/ethernet/brocade/bna/bfa_msgq.h
new file mode 100644
index 000000000000..a6a565a366dc
--- /dev/null
+++ b/drivers/net/ethernet/brocade/bna/bfa_msgq.h
@@ -0,0 +1,130 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+#ifndef __BFA_MSGQ_H__
+#define __BFA_MSGQ_H__
+
+#include "bfa_defs.h"
+#include "bfi.h"
+#include "bfa_ioc.h"
+#include "bfa_cs.h"
+
+#define BFA_MSGQ_FREE_CNT(_q) \
+ (((_q)->consumer_index - (_q)->producer_index - 1) & ((_q)->depth - 1))
+
+#define BFA_MSGQ_INDX_ADD(_q_indx, _qe_num, _q_depth) \
+ ((_q_indx) = (((_q_indx) + (_qe_num)) & ((_q_depth) - 1)))
+
+#define BFA_MSGQ_CMDQ_NUM_ENTRY 128
+#define BFA_MSGQ_CMDQ_SIZE \
+ (BFI_MSGQ_CMD_ENTRY_SIZE * BFA_MSGQ_CMDQ_NUM_ENTRY)
+
+#define BFA_MSGQ_RSPQ_NUM_ENTRY 128
+#define BFA_MSGQ_RSPQ_SIZE \
+ (BFI_MSGQ_RSP_ENTRY_SIZE * BFA_MSGQ_RSPQ_NUM_ENTRY)
+
+#define bfa_msgq_cmd_set(_cmd, _cbfn, _cbarg, _msg_size, _msg_hdr) \
+do { \
+ (_cmd)->cbfn = (_cbfn); \
+ (_cmd)->cbarg = (_cbarg); \
+ (_cmd)->msg_size = (_msg_size); \
+ (_cmd)->msg_hdr = (_msg_hdr); \
+} while (0)
+
+struct bfa_msgq;
+
+typedef void (*bfa_msgq_cmdcbfn_t)(void *cbarg, enum bfa_status status);
+
+struct bfa_msgq_cmd_entry {
+ struct list_head qe;
+ bfa_msgq_cmdcbfn_t cbfn;
+ void *cbarg;
+ size_t msg_size;
+ struct bfi_msgq_mhdr *msg_hdr;
+};
+
+enum bfa_msgq_cmdq_flags {
+ BFA_MSGQ_CMDQ_F_DB_UPDATE = 1,
+};
+
+struct bfa_msgq_cmdq {
+ bfa_fsm_t fsm;
+ enum bfa_msgq_cmdq_flags flags;
+
+ u16 producer_index;
+ u16 consumer_index;
+ u16 depth; /* FW Q depth is 16 bits */
+ struct bfa_dma addr;
+ struct bfa_mbox_cmd dbell_mb;
+
+ u16 token;
+ int offset;
+ int bytes_to_copy;
+ struct bfa_mbox_cmd copy_mb;
+
+ struct list_head pending_q; /* pending command queue */
+
+ struct bfa_msgq *msgq;
+};
+
+enum bfa_msgq_rspq_flags {
+ BFA_MSGQ_RSPQ_F_DB_UPDATE = 1,
+};
+
+typedef void (*bfa_msgq_mcfunc_t)(void *cbarg, struct bfi_msgq_mhdr *mhdr);
+
+struct bfa_msgq_rspq {
+ bfa_fsm_t fsm;
+ enum bfa_msgq_rspq_flags flags;
+
+ u16 producer_index;
+ u16 consumer_index;
+ u16 depth; /* FW Q depth is 16 bits */
+ struct bfa_dma addr;
+ struct bfa_mbox_cmd dbell_mb;
+
+ int nmclass;
+ struct {
+ bfa_msgq_mcfunc_t cbfn;
+ void *cbarg;
+ } rsphdlr[BFI_MC_MAX];
+
+ struct bfa_msgq *msgq;
+};
+
+struct bfa_msgq {
+ struct bfa_msgq_cmdq cmdq;
+ struct bfa_msgq_rspq rspq;
+
+ struct bfa_wc init_wc;
+ struct bfa_mbox_cmd init_mb;
+
+ struct bfa_ioc_notify ioc_notify;
+ struct bfa_ioc *ioc;
+};
+
+u32 bfa_msgq_meminfo(void);
+void bfa_msgq_memclaim(struct bfa_msgq *msgq, u8 *kva, u64 pa);
+void bfa_msgq_attach(struct bfa_msgq *msgq, struct bfa_ioc *ioc);
+void bfa_msgq_regisr(struct bfa_msgq *msgq, enum bfi_mclass mc,
+ bfa_msgq_mcfunc_t cbfn, void *cbarg);
+void bfa_msgq_cmd_post(struct bfa_msgq *msgq,
+ struct bfa_msgq_cmd_entry *cmd);
+void bfa_msgq_rsp_copy(struct bfa_msgq *msgq, u8 *buf, size_t buf_len);
+
+#endif
diff --git a/drivers/net/bna/bfi.h b/drivers/net/ethernet/brocade/bna/bfi.h
index 6050379526f7..7a1393aabd43 100644
--- a/drivers/net/bna/bfi.h
+++ b/drivers/net/ethernet/brocade/bna/bfi.h
@@ -15,7 +15,6 @@
* All rights reserved
* www.brocade.com
*/
-
#ifndef __BFI_H__
#define __BFI_H__
@@ -28,12 +27,6 @@
*/
#define BFI_FLASH_CHUNK_SZ 256 /*!< Flash chunk size */
#define BFI_FLASH_CHUNK_SZ_WORDS (BFI_FLASH_CHUNK_SZ/sizeof(u32))
-enum {
- BFI_IMAGE_CB_FC,
- BFI_IMAGE_CT_FC,
- BFI_IMAGE_CT_CNA,
- BFI_IMAGE_MAX,
-};
/**
* Msg header common to all msgs
@@ -43,21 +36,25 @@ struct bfi_mhdr {
u8 msg_id; /*!< msg opcode with in the class */
union {
struct {
- u8 rsvd;
- u8 lpu_id; /*!< msg destination */
+ u8 qid;
+ u8 fn_lpu; /*!< msg destination */
} h2i;
u16 i2htok; /*!< token in msgs to host */
} mtag;
};
-#define bfi_h2i_set(_mh, _mc, _op, _lpuid) do { \
- (_mh).msg_class = (_mc); \
+#define bfi_fn_lpu(__fn, __lpu) ((__fn) << 1 | (__lpu))
+#define bfi_mhdr_2_fn(_mh) ((_mh)->mtag.h2i.fn_lpu >> 1)
+#define bfi_mhdr_2_qid(_mh) ((_mh)->mtag.h2i.qid)
+
+#define bfi_h2i_set(_mh, _mc, _op, _fn_lpu) do { \
+ (_mh).msg_class = (_mc); \
(_mh).msg_id = (_op); \
- (_mh).mtag.h2i.lpu_id = (_lpuid); \
+ (_mh).mtag.h2i.fn_lpu = (_fn_lpu); \
} while (0)
#define bfi_i2h_set(_mh, _mc, _op, _i2htok) do { \
- (_mh).msg_class = (_mc); \
+ (_mh).msg_class = (_mc); \
(_mh).msg_id = (_op); \
(_mh).mtag.i2htok = (_i2htok); \
} while (0)
@@ -66,7 +63,7 @@ struct bfi_mhdr {
* Message opcodes: 0-127 to firmware, 128-255 to host
*/
#define BFI_I2H_OPCODE_BASE 128
-#define BFA_I2HM(_x) ((_x) + BFI_I2H_OPCODE_BASE)
+#define BFA_I2HM(_x) ((_x) + BFI_I2H_OPCODE_BASE)
/**
****************************************************************************
@@ -76,20 +73,6 @@ struct bfi_mhdr {
****************************************************************************
*/
-#define BFI_SGE_INLINE 1
-#define BFI_SGE_INLINE_MAX (BFI_SGE_INLINE + 1)
-
-/**
- * SG Flags
- */
-enum {
- BFI_SGE_DATA = 0, /*!< data address, not last */
- BFI_SGE_DATA_CPL = 1, /*!< data addr, last in current page */
- BFI_SGE_DATA_LAST = 3, /*!< data address, last */
- BFI_SGE_LINK = 2, /*!< link address */
- BFI_SGE_PGDLEN = 2, /*!< cumulative data length for page */
-};
-
/**
* DMA addresses
*/
@@ -100,33 +83,6 @@ union bfi_addr_u {
} a32;
};
-/**
- * Scatter Gather Element
- */
-struct bfi_sge {
-#ifdef __BIGENDIAN
- u32 flags:2,
- rsvd:2,
- sg_len:28;
-#else
- u32 sg_len:28,
- rsvd:2,
- flags:2;
-#endif
- union bfi_addr_u sga;
-};
-
-/**
- * Scatter Gather Page
- */
-#define BFI_SGPG_DATA_SGES 7
-#define BFI_SGPG_SGES_MAX (BFI_SGPG_DATA_SGES + 1)
-#define BFI_SGPG_RSVD_WD_LEN 8
-struct bfi_sgpg {
- struct bfi_sge sges[BFI_SGPG_SGES_MAX];
- u32 rsvd[BFI_SGPG_RSVD_WD_LEN];
-};
-
/*
* Large Message structure - 128 Bytes size Msgs
*/
@@ -134,11 +90,6 @@ struct bfi_sgpg {
#define BFI_LMSG_PL_WSZ \
((BFI_LMSG_SZ - sizeof(struct bfi_mhdr)) / 4)
-struct bfi_msg {
- struct bfi_mhdr mhdr;
- u32 pl[BFI_LMSG_PL_WSZ];
-};
-
/**
* Mailbox message structure
*/
@@ -149,6 +100,14 @@ struct bfi_mbmsg {
};
/**
+ * Supported PCI function class codes (personality)
+ */
+enum bfi_pcifn_class {
+ BFI_PCIFN_CLASS_FC = 0x0c04,
+ BFI_PCIFN_CLASS_ETH = 0x0200,
+};
+
+/**
* Message Classes
*/
enum bfi_mclass {
@@ -176,24 +135,21 @@ enum bfi_mclass {
BFI_MC_SFP = 22, /*!< SFP module */
BFI_MC_MSGQ = 23, /*!< MSGQ */
BFI_MC_ENET = 24, /*!< ENET commands/responses */
- BFI_MC_MAX = 32
+ BFI_MC_PHY = 25, /*!< External PHY message class */
+ BFI_MC_NBOOT = 26, /*!< Network Boot */
+ BFI_MC_TIO_READ = 27, /*!< read IO (Target mode) */
+ BFI_MC_TIO_WRITE = 28, /*!< write IO (Target mode) */
+ BFI_MC_TIO_DATA_XFERED = 29, /*!< ds transferred (target mode) */
+ BFI_MC_TIO_IO = 30, /*!< IO (Target mode) */
+ BFI_MC_TIO = 31, /*!< IO (target mode) */
+ BFI_MC_MFG = 32, /*!< MFG/ASIC block commands */
+ BFI_MC_EDMA = 33, /*!< EDMA copy commands */
+ BFI_MC_MAX = 34
};
-#define BFI_IOC_MAX_CQS 4
-#define BFI_IOC_MAX_CQS_ASIC 8
#define BFI_IOC_MSGLEN_MAX 32 /* 32 bytes */
-#define BFI_BOOT_TYPE_OFF 8
-#define BFI_BOOT_LOADER_OFF 12
-
-#define BFI_BOOT_TYPE_NORMAL 0
-#define BFI_BOOT_TYPE_FLASH 1
-#define BFI_BOOT_TYPE_MEMTEST 2
-
-#define BFI_BOOT_LOADER_OS 0
-
-#define BFI_BOOT_MEMTEST_RES_ADDR 0x900
-#define BFI_BOOT_MEMTEST_RES_SIG 0xA0A1A2A3
+#define BFI_FWBOOT_ENV_OS 0
/**
*----------------------------------------------------------------------
@@ -201,6 +157,22 @@ enum bfi_mclass {
*----------------------------------------------------------------------
*/
+/**
+ * Different asic generations
+ */
+enum bfi_asic_gen {
+ BFI_ASIC_GEN_CB = 1,
+ BFI_ASIC_GEN_CT = 2,
+ BFI_ASIC_GEN_CT2 = 3,
+};
+
+enum bfi_asic_mode {
+ BFI_ASIC_MODE_FC = 1, /* FC upto 8G speed */
+ BFI_ASIC_MODE_FC16 = 2, /* FC upto 16G speed */
+ BFI_ASIC_MODE_ETH = 3, /* Ethernet ports */
+ BFI_ASIC_MODE_COMBO = 4, /* FC 16G and Ethernet 10G port */
+};
+
enum bfi_ioc_h2i_msgs {
BFI_IOC_H2I_ENABLE_REQ = 1,
BFI_IOC_H2I_DISABLE_REQ = 2,
@@ -211,10 +183,9 @@ enum bfi_ioc_h2i_msgs {
enum bfi_ioc_i2h_msgs {
BFI_IOC_I2H_ENABLE_REPLY = BFA_I2HM(1),
- BFI_IOC_I2H_DISABLE_REPLY = BFA_I2HM(2),
- BFI_IOC_I2H_GETATTR_REPLY = BFA_I2HM(3),
- BFI_IOC_I2H_READY_EVENT = BFA_I2HM(4),
- BFI_IOC_I2H_HBEAT = BFA_I2HM(5),
+ BFI_IOC_I2H_DISABLE_REPLY = BFA_I2HM(2),
+ BFI_IOC_I2H_GETATTR_REPLY = BFA_I2HM(3),
+ BFI_IOC_I2H_HBEAT = BFA_I2HM(4),
};
/**
@@ -229,7 +200,8 @@ struct bfi_ioc_attr {
u64 mfg_pwwn; /*!< Mfg port wwn */
u64 mfg_nwwn; /*!< Mfg node wwn */
mac_t mfg_mac; /*!< Mfg mac */
- u16 rsvd_a;
+ u8 port_mode; /* enum bfi_port_mode */
+ u8 rsvd_a;
u64 pwwn;
u64 nwwn;
mac_t mac; /*!< PBC or Mfg mac */
@@ -282,20 +254,34 @@ struct bfi_ioc_getattr_reply {
#define BFI_IOC_MD5SUM_SZ 4
struct bfi_ioc_image_hdr {
u32 signature; /*!< constant signature */
- u32 rsvd_a;
+ u8 asic_gen; /*!< asic generation */
+ u8 asic_mode;
+ u8 port0_mode; /*!< device mode for port 0 */
+ u8 port1_mode; /*!< device mode for port 1 */
u32 exec; /*!< exec vector */
- u32 param; /*!< parameters */
+ u32 bootenv; /*!< firmware boot env */
u32 rsvd_b[4];
u32 md5sum[BFI_IOC_MD5SUM_SZ];
};
-/**
- * BFI_IOC_I2H_READY_EVENT message
- */
-struct bfi_ioc_rdy_event {
- struct bfi_mhdr mh; /*!< common msg header */
- u8 init_status; /*!< init event status */
- u8 rsvd[3];
+#define BFI_FWBOOT_DEVMODE_OFF 4
+#define BFI_FWBOOT_TYPE_OFF 8
+#define BFI_FWBOOT_ENV_OFF 12
+#define BFI_FWBOOT_DEVMODE(__asic_gen, __asic_mode, __p0_mode, __p1_mode) \
+ (((u32)(__asic_gen)) << 24 | \
+ ((u32)(__asic_mode)) << 16 | \
+ ((u32)(__p0_mode)) << 8 | \
+ ((u32)(__p1_mode)))
+
+enum bfi_fwboot_type {
+ BFI_FWBOOT_TYPE_NORMAL = 0,
+ BFI_FWBOOT_TYPE_FLASH = 1,
+ BFI_FWBOOT_TYPE_MEMTEST = 2,
+};
+
+enum bfi_port_mode {
+ BFI_PORT_MODE_FC = 1,
+ BFI_PORT_MODE_ETH = 2,
};
struct bfi_ioc_hbeat {
@@ -354,8 +340,8 @@ enum {
*/
struct bfi_ioc_ctrl_req {
struct bfi_mhdr mh;
- u8 ioc_class;
- u8 rsvd[3];
+ u16 clscode;
+ u16 rsvd;
u32 tv_sec;
};
@@ -363,9 +349,11 @@ struct bfi_ioc_ctrl_req {
* BFI_IOC_I2H_ENABLE_REPLY & BFI_IOC_I2H_DISABLE_REPLY messages
*/
struct bfi_ioc_ctrl_reply {
- struct bfi_mhdr mh; /*!< Common msg header */
+ struct bfi_mhdr mh; /*!< Common msg header */
u8 status; /*!< enable/disable status */
- u8 rsvd[3];
+ u8 port_mode; /*!< enum bfa_mode */
+ u8 cap_bm; /*!< capability bit mask */
+ u8 rsvd;
};
#define BFI_IOC_MSGSZ 8
@@ -385,10 +373,109 @@ union bfi_ioc_h2i_msg_u {
*/
union bfi_ioc_i2h_msg_u {
struct bfi_mhdr mh;
- struct bfi_ioc_rdy_event rdy_event;
+ struct bfi_ioc_ctrl_reply fw_event;
u32 mboxmsg[BFI_IOC_MSGSZ];
};
+/**
+ *----------------------------------------------------------------------
+ * MSGQ
+ *----------------------------------------------------------------------
+ */
+
+enum bfi_msgq_h2i_msgs {
+ BFI_MSGQ_H2I_INIT_REQ = 1,
+ BFI_MSGQ_H2I_DOORBELL_PI = 2,
+ BFI_MSGQ_H2I_DOORBELL_CI = 3,
+ BFI_MSGQ_H2I_CMDQ_COPY_RSP = 4,
+};
+
+enum bfi_msgq_i2h_msgs {
+ BFI_MSGQ_I2H_INIT_RSP = BFA_I2HM(BFI_MSGQ_H2I_INIT_REQ),
+ BFI_MSGQ_I2H_DOORBELL_PI = BFA_I2HM(BFI_MSGQ_H2I_DOORBELL_PI),
+ BFI_MSGQ_I2H_DOORBELL_CI = BFA_I2HM(BFI_MSGQ_H2I_DOORBELL_CI),
+ BFI_MSGQ_I2H_CMDQ_COPY_REQ = BFA_I2HM(BFI_MSGQ_H2I_CMDQ_COPY_RSP),
+};
+
+/* Messages(commands/responsed/AENS will have the following header */
+struct bfi_msgq_mhdr {
+ u8 msg_class;
+ u8 msg_id;
+ u16 msg_token;
+ u16 num_entries;
+ u8 enet_id;
+ u8 rsvd[1];
+};
+
+#define bfi_msgq_mhdr_set(_mh, _mc, _mid, _tok, _enet_id) do { \
+ (_mh).msg_class = (_mc); \
+ (_mh).msg_id = (_mid); \
+ (_mh).msg_token = (_tok); \
+ (_mh).enet_id = (_enet_id); \
+} while (0)
+
+/*
+ * Mailbox for messaging interface
+ */
+#define BFI_MSGQ_CMD_ENTRY_SIZE (64) /* TBD */
+#define BFI_MSGQ_RSP_ENTRY_SIZE (64) /* TBD */
+
+#define bfi_msgq_num_cmd_entries(_size) \
+ (((_size) + BFI_MSGQ_CMD_ENTRY_SIZE - 1) / BFI_MSGQ_CMD_ENTRY_SIZE)
+
+struct bfi_msgq {
+ union bfi_addr_u addr;
+ u16 q_depth; /* Total num of entries in the queue */
+ u8 rsvd[2];
+};
+
+/* BFI_ENET_MSGQ_CFG_REQ TBD init or cfg? */
+struct bfi_msgq_cfg_req {
+ struct bfi_mhdr mh;
+ struct bfi_msgq cmdq;
+ struct bfi_msgq rspq;
+};
+
+/* BFI_ENET_MSGQ_CFG_RSP */
+struct bfi_msgq_cfg_rsp {
+ struct bfi_mhdr mh;
+ u8 cmd_status;
+ u8 rsvd[3];
+};
+
+/* BFI_MSGQ_H2I_DOORBELL */
+struct bfi_msgq_h2i_db {
+ struct bfi_mhdr mh;
+ union {
+ u16 cmdq_pi;
+ u16 rspq_ci;
+ } idx;
+};
+
+/* BFI_MSGQ_I2H_DOORBELL */
+struct bfi_msgq_i2h_db {
+ struct bfi_mhdr mh;
+ union {
+ u16 rspq_pi;
+ u16 cmdq_ci;
+ } idx;
+};
+
+#define BFI_CMD_COPY_SZ 28
+
+/* BFI_MSGQ_H2I_CMD_COPY_RSP */
+struct bfi_msgq_h2i_cmdq_copy_rsp {
+ struct bfi_mhdr mh;
+ u8 data[BFI_CMD_COPY_SZ];
+};
+
+/* BFI_MSGQ_I2H_CMD_COPY_REQ */
+struct bfi_msgq_i2h_cmdq_copy_req {
+ struct bfi_mhdr mh;
+ u16 offset;
+ u16 len;
+};
+
#pragma pack()
#endif /* __BFI_H__ */
diff --git a/drivers/net/bna/bfi_cna.h b/drivers/net/ethernet/brocade/bna/bfi_cna.h
index 4eecabea397b..4eecabea397b 100644
--- a/drivers/net/bna/bfi_cna.h
+++ b/drivers/net/ethernet/brocade/bna/bfi_cna.h
diff --git a/drivers/net/ethernet/brocade/bna/bfi_enet.h b/drivers/net/ethernet/brocade/bna/bfi_enet.h
new file mode 100644
index 000000000000..a90f1cf46b41
--- /dev/null
+++ b/drivers/net/ethernet/brocade/bna/bfi_enet.h
@@ -0,0 +1,901 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+/**
+ * @file bfi_enet.h BNA Hardware and Firmware Interface
+ */
+
+/**
+ * Skipping statistics collection to avoid clutter.
+ * Command is no longer needed:
+ * MTU
+ * TxQ Stop
+ * RxQ Stop
+ * RxF Enable/Disable
+ *
+ * HDS-off request is dynamic
+ * keep structures as multiple of 32-bit fields for alignment.
+ * All values must be written in big-endian.
+ */
+#ifndef __BFI_ENET_H__
+#define __BFI_ENET_H__
+
+#include "bfa_defs.h"
+#include "bfi.h"
+
+#pragma pack(1)
+
+#define BFI_ENET_CFG_MAX 32 /* Max resources per PF */
+
+#define BFI_ENET_TXQ_PRIO_MAX 8
+#define BFI_ENET_RX_QSET_MAX 16
+#define BFI_ENET_TXQ_WI_VECT_MAX 4
+
+#define BFI_ENET_VLAN_ID_MAX 4096
+#define BFI_ENET_VLAN_BLOCK_SIZE 512 /* in bits */
+#define BFI_ENET_VLAN_BLOCKS_MAX \
+ (BFI_ENET_VLAN_ID_MAX / BFI_ENET_VLAN_BLOCK_SIZE)
+#define BFI_ENET_VLAN_WORD_SIZE 32 /* in bits */
+#define BFI_ENET_VLAN_WORDS_MAX \
+ (BFI_ENET_VLAN_BLOCK_SIZE / BFI_ENET_VLAN_WORD_SIZE)
+
+#define BFI_ENET_RSS_RIT_MAX 64 /* entries */
+#define BFI_ENET_RSS_KEY_LEN 10 /* 32-bit words */
+
+union bfi_addr_be_u {
+ struct {
+ u32 addr_hi; /* Most Significant 32-bits */
+ u32 addr_lo; /* Least Significant 32-Bits */
+ } a32;
+};
+
+/**
+ * T X Q U E U E D E F I N E S
+ */
+/* TxQ Vector (a.k.a. Tx-Buffer Descriptor) */
+/* TxQ Entry Opcodes */
+#define BFI_ENET_TXQ_WI_SEND (0x402) /* Single Frame Transmission */
+#define BFI_ENET_TXQ_WI_SEND_LSO (0x403) /* Multi-Frame Transmission */
+#define BFI_ENET_TXQ_WI_EXTENSION (0x104) /* Extension WI */
+
+/* TxQ Entry Control Flags */
+#define BFI_ENET_TXQ_WI_CF_FCOE_CRC (1 << 8)
+#define BFI_ENET_TXQ_WI_CF_IPID_MODE (1 << 5)
+#define BFI_ENET_TXQ_WI_CF_INS_PRIO (1 << 4)
+#define BFI_ENET_TXQ_WI_CF_INS_VLAN (1 << 3)
+#define BFI_ENET_TXQ_WI_CF_UDP_CKSUM (1 << 2)
+#define BFI_ENET_TXQ_WI_CF_TCP_CKSUM (1 << 1)
+#define BFI_ENET_TXQ_WI_CF_IP_CKSUM (1 << 0)
+
+struct bfi_enet_txq_wi_base {
+ u8 reserved;
+ u8 num_vectors; /* number of vectors present */
+ u16 opcode;
+ /* BFI_ENET_TXQ_WI_SEND or BFI_ENET_TXQ_WI_SEND_LSO */
+ u16 flags; /* OR of all the flags */
+ u16 l4_hdr_size_n_offset;
+ u16 vlan_tag;
+ u16 lso_mss; /* Only 14 LSB are valid */
+ u32 frame_length; /* Only 24 LSB are valid */
+};
+
+struct bfi_enet_txq_wi_ext {
+ u16 reserved;
+ u16 opcode; /* BFI_ENET_TXQ_WI_EXTENSION */
+ u32 reserved2[3];
+};
+
+struct bfi_enet_txq_wi_vector { /* Tx Buffer Descriptor */
+ u16 reserved;
+ u16 length; /* Only 14 LSB are valid */
+ union bfi_addr_be_u addr;
+};
+
+/**
+ * TxQ Entry Structure
+ *
+ */
+struct bfi_enet_txq_entry {
+ union {
+ struct bfi_enet_txq_wi_base base;
+ struct bfi_enet_txq_wi_ext ext;
+ } wi;
+ struct bfi_enet_txq_wi_vector vector[BFI_ENET_TXQ_WI_VECT_MAX];
+};
+
+#define wi_hdr wi.base
+#define wi_ext_hdr wi.ext
+
+#define BFI_ENET_TXQ_WI_L4_HDR_N_OFFSET(_hdr_size, _offset) \
+ (((_hdr_size) << 10) | ((_offset) & 0x3FF))
+
+/**
+ * R X Q U E U E D E F I N E S
+ */
+struct bfi_enet_rxq_entry {
+ union bfi_addr_be_u rx_buffer;
+};
+
+/**
+ * R X C O M P L E T I O N Q U E U E D E F I N E S
+ */
+/* CQ Entry Flags */
+#define BFI_ENET_CQ_EF_MAC_ERROR (1 << 0)
+#define BFI_ENET_CQ_EF_FCS_ERROR (1 << 1)
+#define BFI_ENET_CQ_EF_TOO_LONG (1 << 2)
+#define BFI_ENET_CQ_EF_FC_CRC_OK (1 << 3)
+
+#define BFI_ENET_CQ_EF_RSVD1 (1 << 4)
+#define BFI_ENET_CQ_EF_L4_CKSUM_OK (1 << 5)
+#define BFI_ENET_CQ_EF_L3_CKSUM_OK (1 << 6)
+#define BFI_ENET_CQ_EF_HDS_HEADER (1 << 7)
+
+#define BFI_ENET_CQ_EF_UDP (1 << 8)
+#define BFI_ENET_CQ_EF_TCP (1 << 9)
+#define BFI_ENET_CQ_EF_IP_OPTIONS (1 << 10)
+#define BFI_ENET_CQ_EF_IPV6 (1 << 11)
+
+#define BFI_ENET_CQ_EF_IPV4 (1 << 12)
+#define BFI_ENET_CQ_EF_VLAN (1 << 13)
+#define BFI_ENET_CQ_EF_RSS (1 << 14)
+#define BFI_ENET_CQ_EF_RSVD2 (1 << 15)
+
+#define BFI_ENET_CQ_EF_MCAST_MATCH (1 << 16)
+#define BFI_ENET_CQ_EF_MCAST (1 << 17)
+#define BFI_ENET_CQ_EF_BCAST (1 << 18)
+#define BFI_ENET_CQ_EF_REMOTE (1 << 19)
+
+#define BFI_ENET_CQ_EF_LOCAL (1 << 20)
+
+/* CQ Entry Structure */
+struct bfi_enet_cq_entry {
+ u32 flags;
+ u16 vlan_tag;
+ u16 length;
+ u32 rss_hash;
+ u8 valid;
+ u8 reserved1;
+ u8 reserved2;
+ u8 rxq_id;
+};
+
+/**
+ * E N E T C O N T R O L P A T H C O M M A N D S
+ */
+struct bfi_enet_q {
+ union bfi_addr_u pg_tbl;
+ union bfi_addr_u first_entry;
+ u16 pages; /* # of pages */
+ u16 page_sz;
+};
+
+struct bfi_enet_txq {
+ struct bfi_enet_q q;
+ u8 priority;
+ u8 rsvd[3];
+};
+
+struct bfi_enet_rxq {
+ struct bfi_enet_q q;
+ u16 rx_buffer_size;
+ u16 rsvd;
+};
+
+struct bfi_enet_cq {
+ struct bfi_enet_q q;
+};
+
+struct bfi_enet_ib_cfg {
+ u8 int_pkt_dma;
+ u8 int_enabled;
+ u8 int_pkt_enabled;
+ u8 continuous_coalescing;
+ u8 msix;
+ u8 rsvd[3];
+ u32 coalescing_timeout;
+ u32 inter_pkt_timeout;
+ u8 inter_pkt_count;
+ u8 rsvd1[3];
+};
+
+struct bfi_enet_ib {
+ union bfi_addr_u index_addr;
+ union {
+ u16 msix_index;
+ u16 intx_bitmask;
+ } intr;
+ u16 rsvd;
+};
+
+/**
+ * ENET command messages
+ */
+enum bfi_enet_h2i_msgs {
+ /* Rx Commands */
+ BFI_ENET_H2I_RX_CFG_SET_REQ = 1,
+ BFI_ENET_H2I_RX_CFG_CLR_REQ = 2,
+
+ BFI_ENET_H2I_RIT_CFG_REQ = 3,
+ BFI_ENET_H2I_RSS_CFG_REQ = 4,
+ BFI_ENET_H2I_RSS_ENABLE_REQ = 5,
+ BFI_ENET_H2I_RX_PROMISCUOUS_REQ = 6,
+ BFI_ENET_H2I_RX_DEFAULT_REQ = 7,
+
+ BFI_ENET_H2I_MAC_UCAST_SET_REQ = 8,
+ BFI_ENET_H2I_MAC_UCAST_CLR_REQ = 9,
+ BFI_ENET_H2I_MAC_UCAST_ADD_REQ = 10,
+ BFI_ENET_H2I_MAC_UCAST_DEL_REQ = 11,
+
+ BFI_ENET_H2I_MAC_MCAST_ADD_REQ = 12,
+ BFI_ENET_H2I_MAC_MCAST_DEL_REQ = 13,
+ BFI_ENET_H2I_MAC_MCAST_FILTER_REQ = 14,
+
+ BFI_ENET_H2I_RX_VLAN_SET_REQ = 15,
+ BFI_ENET_H2I_RX_VLAN_STRIP_ENABLE_REQ = 16,
+
+ /* Tx Commands */
+ BFI_ENET_H2I_TX_CFG_SET_REQ = 17,
+ BFI_ENET_H2I_TX_CFG_CLR_REQ = 18,
+
+ /* Port Commands */
+ BFI_ENET_H2I_PORT_ADMIN_UP_REQ = 19,
+ BFI_ENET_H2I_SET_PAUSE_REQ = 20,
+ BFI_ENET_H2I_DIAG_LOOPBACK_REQ = 21,
+
+ /* Get Attributes Command */
+ BFI_ENET_H2I_GET_ATTR_REQ = 22,
+
+ /* Statistics Commands */
+ BFI_ENET_H2I_STATS_GET_REQ = 23,
+ BFI_ENET_H2I_STATS_CLR_REQ = 24,
+
+ BFI_ENET_H2I_WOL_MAGIC_REQ = 25,
+ BFI_ENET_H2I_WOL_FRAME_REQ = 26,
+
+ BFI_ENET_H2I_MAX = 27,
+};
+
+enum bfi_enet_i2h_msgs {
+ /* Rx Responses */
+ BFI_ENET_I2H_RX_CFG_SET_RSP =
+ BFA_I2HM(BFI_ENET_H2I_RX_CFG_SET_REQ),
+ BFI_ENET_I2H_RX_CFG_CLR_RSP =
+ BFA_I2HM(BFI_ENET_H2I_RX_CFG_CLR_REQ),
+
+ BFI_ENET_I2H_RIT_CFG_RSP =
+ BFA_I2HM(BFI_ENET_H2I_RIT_CFG_REQ),
+ BFI_ENET_I2H_RSS_CFG_RSP =
+ BFA_I2HM(BFI_ENET_H2I_RSS_CFG_REQ),
+ BFI_ENET_I2H_RSS_ENABLE_RSP =
+ BFA_I2HM(BFI_ENET_H2I_RSS_ENABLE_REQ),
+ BFI_ENET_I2H_RX_PROMISCUOUS_RSP =
+ BFA_I2HM(BFI_ENET_H2I_RX_PROMISCUOUS_REQ),
+ BFI_ENET_I2H_RX_DEFAULT_RSP =
+ BFA_I2HM(BFI_ENET_H2I_RX_DEFAULT_REQ),
+
+ BFI_ENET_I2H_MAC_UCAST_SET_RSP =
+ BFA_I2HM(BFI_ENET_H2I_MAC_UCAST_SET_REQ),
+ BFI_ENET_I2H_MAC_UCAST_CLR_RSP =
+ BFA_I2HM(BFI_ENET_H2I_MAC_UCAST_CLR_REQ),
+ BFI_ENET_I2H_MAC_UCAST_ADD_RSP =
+ BFA_I2HM(BFI_ENET_H2I_MAC_UCAST_ADD_REQ),
+ BFI_ENET_I2H_MAC_UCAST_DEL_RSP =
+ BFA_I2HM(BFI_ENET_H2I_MAC_UCAST_DEL_REQ),
+
+ BFI_ENET_I2H_MAC_MCAST_ADD_RSP =
+ BFA_I2HM(BFI_ENET_H2I_MAC_MCAST_ADD_REQ),
+ BFI_ENET_I2H_MAC_MCAST_DEL_RSP =
+ BFA_I2HM(BFI_ENET_H2I_MAC_MCAST_DEL_REQ),
+ BFI_ENET_I2H_MAC_MCAST_FILTER_RSP =
+ BFA_I2HM(BFI_ENET_H2I_MAC_MCAST_FILTER_REQ),
+
+ BFI_ENET_I2H_RX_VLAN_SET_RSP =
+ BFA_I2HM(BFI_ENET_H2I_RX_VLAN_SET_REQ),
+
+ BFI_ENET_I2H_RX_VLAN_STRIP_ENABLE_RSP =
+ BFA_I2HM(BFI_ENET_H2I_RX_VLAN_STRIP_ENABLE_REQ),
+
+ /* Tx Responses */
+ BFI_ENET_I2H_TX_CFG_SET_RSP =
+ BFA_I2HM(BFI_ENET_H2I_TX_CFG_SET_REQ),
+ BFI_ENET_I2H_TX_CFG_CLR_RSP =
+ BFA_I2HM(BFI_ENET_H2I_TX_CFG_CLR_REQ),
+
+ /* Port Responses */
+ BFI_ENET_I2H_PORT_ADMIN_RSP =
+ BFA_I2HM(BFI_ENET_H2I_PORT_ADMIN_UP_REQ),
+
+ BFI_ENET_I2H_SET_PAUSE_RSP =
+ BFA_I2HM(BFI_ENET_H2I_SET_PAUSE_REQ),
+ BFI_ENET_I2H_DIAG_LOOPBACK_RSP =
+ BFA_I2HM(BFI_ENET_H2I_DIAG_LOOPBACK_REQ),
+
+ /* Attributes Response */
+ BFI_ENET_I2H_GET_ATTR_RSP =
+ BFA_I2HM(BFI_ENET_H2I_GET_ATTR_REQ),
+
+ /* Statistics Responses */
+ BFI_ENET_I2H_STATS_GET_RSP =
+ BFA_I2HM(BFI_ENET_H2I_STATS_GET_REQ),
+ BFI_ENET_I2H_STATS_CLR_RSP =
+ BFA_I2HM(BFI_ENET_H2I_STATS_CLR_REQ),
+
+ BFI_ENET_I2H_WOL_MAGIC_RSP =
+ BFA_I2HM(BFI_ENET_H2I_WOL_MAGIC_REQ),
+ BFI_ENET_I2H_WOL_FRAME_RSP =
+ BFA_I2HM(BFI_ENET_H2I_WOL_FRAME_REQ),
+
+ /* AENs */
+ BFI_ENET_I2H_LINK_DOWN_AEN = BFA_I2HM(BFI_ENET_H2I_MAX),
+ BFI_ENET_I2H_LINK_UP_AEN = BFA_I2HM(BFI_ENET_H2I_MAX + 1),
+
+ BFI_ENET_I2H_PORT_ENABLE_AEN = BFA_I2HM(BFI_ENET_H2I_MAX + 2),
+ BFI_ENET_I2H_PORT_DISABLE_AEN = BFA_I2HM(BFI_ENET_H2I_MAX + 3),
+
+ BFI_ENET_I2H_BW_UPDATE_AEN = BFA_I2HM(BFI_ENET_H2I_MAX + 4),
+};
+
+/**
+ * The following error codes can be returned by the enet commands
+ */
+enum bfi_enet_err {
+ BFI_ENET_CMD_OK = 0,
+ BFI_ENET_CMD_FAIL = 1,
+ BFI_ENET_CMD_DUP_ENTRY = 2, /* !< Duplicate entry in CAM */
+ BFI_ENET_CMD_CAM_FULL = 3, /* !< CAM is full */
+ BFI_ENET_CMD_NOT_OWNER = 4, /* !< Not permitted, b'cos not owner */
+ BFI_ENET_CMD_NOT_EXEC = 5, /* !< Was not sent to f/w at all */
+ BFI_ENET_CMD_WAITING = 6, /* !< Waiting for completion */
+ BFI_ENET_CMD_PORT_DISABLED = 7, /* !< port in disabled state */
+};
+
+/**
+ * Generic Request
+ *
+ * bfi_enet_req is used by:
+ * BFI_ENET_H2I_RX_CFG_CLR_REQ
+ * BFI_ENET_H2I_TX_CFG_CLR_REQ
+ */
+struct bfi_enet_req {
+ struct bfi_msgq_mhdr mh;
+};
+
+/**
+ * Enable/Disable Request
+ *
+ * bfi_enet_enable_req is used by:
+ * BFI_ENET_H2I_RSS_ENABLE_REQ (enet_id must be zero)
+ * BFI_ENET_H2I_RX_PROMISCUOUS_REQ (enet_id must be zero)
+ * BFI_ENET_H2I_RX_DEFAULT_REQ (enet_id must be zero)
+ * BFI_ENET_H2I_RX_MAC_MCAST_FILTER_REQ
+ * BFI_ENET_H2I_PORT_ADMIN_UP_REQ (enet_id must be zero)
+ */
+struct bfi_enet_enable_req {
+ struct bfi_msgq_mhdr mh;
+ u8 enable; /* 1 = enable; 0 = disable */
+ u8 rsvd[3];
+};
+
+/**
+ * Generic Response
+ */
+struct bfi_enet_rsp {
+ struct bfi_msgq_mhdr mh;
+ u8 error; /*!< if error see cmd_offset */
+ u8 rsvd;
+ u16 cmd_offset; /*!< offset to invalid parameter */
+};
+
+/**
+ * GLOBAL CONFIGURATION
+ */
+
+/**
+ * bfi_enet_attr_req is used by:
+ * BFI_ENET_H2I_GET_ATTR_REQ
+ */
+struct bfi_enet_attr_req {
+ struct bfi_msgq_mhdr mh;
+};
+
+/**
+ * bfi_enet_attr_rsp is used by:
+ * BFI_ENET_I2H_GET_ATTR_RSP
+ */
+struct bfi_enet_attr_rsp {
+ struct bfi_msgq_mhdr mh;
+ u8 error; /*!< if error see cmd_offset */
+ u8 rsvd;
+ u16 cmd_offset; /*!< offset to invalid parameter */
+ u32 max_cfg;
+ u32 max_ucmac;
+ u32 rit_size;
+};
+
+/**
+ * Tx Configuration
+ *
+ * bfi_enet_tx_cfg is used by:
+ * BFI_ENET_H2I_TX_CFG_SET_REQ
+ */
+enum bfi_enet_tx_vlan_mode {
+ BFI_ENET_TX_VLAN_NOP = 0,
+ BFI_ENET_TX_VLAN_INS = 1,
+ BFI_ENET_TX_VLAN_WI = 2,
+};
+
+struct bfi_enet_tx_cfg {
+ u8 vlan_mode; /*!< processing mode */
+ u8 rsvd;
+ u16 vlan_id;
+ u8 admit_tagged_frame;
+ u8 apply_vlan_filter;
+ u8 add_to_vswitch;
+ u8 rsvd1[1];
+};
+
+struct bfi_enet_tx_cfg_req {
+ struct bfi_msgq_mhdr mh;
+ u8 num_queues; /* # of Tx Queues */
+ u8 rsvd[3];
+
+ struct {
+ struct bfi_enet_txq q;
+ struct bfi_enet_ib ib;
+ } q_cfg[BFI_ENET_TXQ_PRIO_MAX];
+
+ struct bfi_enet_ib_cfg ib_cfg;
+
+ struct bfi_enet_tx_cfg tx_cfg;
+};
+
+struct bfi_enet_tx_cfg_rsp {
+ struct bfi_msgq_mhdr mh;
+ u8 error;
+ u8 hw_id; /* For debugging */
+ u8 rsvd[2];
+ struct {
+ u32 q_dbell; /* PCI base address offset */
+ u32 i_dbell; /* PCI base address offset */
+ u8 hw_qid; /* For debugging */
+ u8 rsvd[3];
+ } q_handles[BFI_ENET_TXQ_PRIO_MAX];
+};
+
+/**
+ * Rx Configuration
+ *
+ * bfi_enet_rx_cfg is used by:
+ * BFI_ENET_H2I_RX_CFG_SET_REQ
+ */
+enum bfi_enet_rxq_type {
+ BFI_ENET_RXQ_SINGLE = 1,
+ BFI_ENET_RXQ_LARGE_SMALL = 2,
+ BFI_ENET_RXQ_HDS = 3,
+ BFI_ENET_RXQ_HDS_OPT_BASED = 4,
+};
+
+enum bfi_enet_hds_type {
+ BFI_ENET_HDS_FORCED = 0x01,
+ BFI_ENET_HDS_IPV6_UDP = 0x02,
+ BFI_ENET_HDS_IPV6_TCP = 0x04,
+ BFI_ENET_HDS_IPV4_TCP = 0x08,
+ BFI_ENET_HDS_IPV4_UDP = 0x10,
+};
+
+struct bfi_enet_rx_cfg {
+ u8 rxq_type;
+ u8 rsvd[3];
+
+ struct {
+ u8 max_header_size;
+ u8 force_offset;
+ u8 type;
+ u8 rsvd1;
+ } hds;
+
+ u8 multi_buffer;
+ u8 strip_vlan;
+ u8 drop_untagged;
+ u8 rsvd2;
+};
+
+/*
+ * Multicast frames are received on the ql of q-set index zero.
+ * On the completion queue. RxQ ID = even is for large/data buffer queues
+ * and RxQ ID = odd is for small/header buffer queues.
+ */
+struct bfi_enet_rx_cfg_req {
+ struct bfi_msgq_mhdr mh;
+ u8 num_queue_sets; /* # of Rx Queue Sets */
+ u8 rsvd[3];
+
+ struct {
+ struct bfi_enet_rxq ql; /* large/data/single buffers */
+ struct bfi_enet_rxq qs; /* small/header buffers */
+ struct bfi_enet_cq cq;
+ struct bfi_enet_ib ib;
+ } q_cfg[BFI_ENET_RX_QSET_MAX];
+
+ struct bfi_enet_ib_cfg ib_cfg;
+
+ struct bfi_enet_rx_cfg rx_cfg;
+};
+
+struct bfi_enet_rx_cfg_rsp {
+ struct bfi_msgq_mhdr mh;
+ u8 error;
+ u8 hw_id; /* For debugging */
+ u8 rsvd[2];
+ struct {
+ u32 ql_dbell; /* PCI base address offset */
+ u32 qs_dbell; /* PCI base address offset */
+ u32 i_dbell; /* PCI base address offset */
+ u8 hw_lqid; /* For debugging */
+ u8 hw_sqid; /* For debugging */
+ u8 hw_cqid; /* For debugging */
+ u8 rsvd;
+ } q_handles[BFI_ENET_RX_QSET_MAX];
+};
+
+/**
+ * RIT
+ *
+ * bfi_enet_rit_req is used by:
+ * BFI_ENET_H2I_RIT_CFG_REQ
+ */
+struct bfi_enet_rit_req {
+ struct bfi_msgq_mhdr mh;
+ u16 size; /* number of table-entries used */
+ u8 rsvd[2];
+ u8 table[BFI_ENET_RSS_RIT_MAX];
+};
+
+/**
+ * RSS
+ *
+ * bfi_enet_rss_cfg_req is used by:
+ * BFI_ENET_H2I_RSS_CFG_REQ
+ */
+enum bfi_enet_rss_type {
+ BFI_ENET_RSS_IPV6 = 0x01,
+ BFI_ENET_RSS_IPV6_TCP = 0x02,
+ BFI_ENET_RSS_IPV4 = 0x04,
+ BFI_ENET_RSS_IPV4_TCP = 0x08
+};
+
+struct bfi_enet_rss_cfg {
+ u8 type;
+ u8 mask;
+ u8 rsvd[2];
+ u32 key[BFI_ENET_RSS_KEY_LEN];
+};
+
+struct bfi_enet_rss_cfg_req {
+ struct bfi_msgq_mhdr mh;
+ struct bfi_enet_rss_cfg cfg;
+};
+
+/**
+ * MAC Unicast
+ *
+ * bfi_enet_rx_vlan_req is used by:
+ * BFI_ENET_H2I_MAC_UCAST_SET_REQ
+ * BFI_ENET_H2I_MAC_UCAST_CLR_REQ
+ * BFI_ENET_H2I_MAC_UCAST_ADD_REQ
+ * BFI_ENET_H2I_MAC_UCAST_DEL_REQ
+ */
+struct bfi_enet_ucast_req {
+ struct bfi_msgq_mhdr mh;
+ mac_t mac_addr;
+ u8 rsvd[2];
+};
+
+/**
+ * MAC Unicast + VLAN
+ */
+struct bfi_enet_mac_n_vlan_req {
+ struct bfi_msgq_mhdr mh;
+ u16 vlan_id;
+ mac_t mac_addr;
+};
+
+/**
+ * MAC Multicast
+ *
+ * bfi_enet_mac_mfilter_add_req is used by:
+ * BFI_ENET_H2I_MAC_MCAST_ADD_REQ
+ */
+struct bfi_enet_mcast_add_req {
+ struct bfi_msgq_mhdr mh;
+ mac_t mac_addr;
+ u8 rsvd[2];
+};
+
+/**
+ * bfi_enet_mac_mfilter_add_rsp is used by:
+ * BFI_ENET_I2H_MAC_MCAST_ADD_RSP
+ */
+struct bfi_enet_mcast_add_rsp {
+ struct bfi_msgq_mhdr mh;
+ u8 error;
+ u8 rsvd;
+ u16 cmd_offset;
+ u16 handle;
+ u8 rsvd1[2];
+};
+
+/**
+ * bfi_enet_mac_mfilter_del_req is used by:
+ * BFI_ENET_H2I_MAC_MCAST_DEL_REQ
+ */
+struct bfi_enet_mcast_del_req {
+ struct bfi_msgq_mhdr mh;
+ u16 handle;
+ u8 rsvd[2];
+};
+
+/**
+ * VLAN
+ *
+ * bfi_enet_rx_vlan_req is used by:
+ * BFI_ENET_H2I_RX_VLAN_SET_REQ
+ */
+struct bfi_enet_rx_vlan_req {
+ struct bfi_msgq_mhdr mh;
+ u8 block_idx;
+ u8 rsvd[3];
+ u32 bit_mask[BFI_ENET_VLAN_WORDS_MAX];
+};
+
+/**
+ * PAUSE
+ *
+ * bfi_enet_set_pause_req is used by:
+ * BFI_ENET_H2I_SET_PAUSE_REQ
+ */
+struct bfi_enet_set_pause_req {
+ struct bfi_msgq_mhdr mh;
+ u8 rsvd[2];
+ u8 tx_pause; /* 1 = enable; 0 = disable */
+ u8 rx_pause; /* 1 = enable; 0 = disable */
+};
+
+/**
+ * DIAGNOSTICS
+ *
+ * bfi_enet_diag_lb_req is used by:
+ * BFI_ENET_H2I_DIAG_LOOPBACK
+ */
+struct bfi_enet_diag_lb_req {
+ struct bfi_msgq_mhdr mh;
+ u8 rsvd[2];
+ u8 mode; /* cable or Serdes */
+ u8 enable; /* 1 = enable; 0 = disable */
+};
+
+/**
+ * enum for Loopback opmodes
+ */
+enum {
+ BFI_ENET_DIAG_LB_OPMODE_EXT = 0,
+ BFI_ENET_DIAG_LB_OPMODE_CBL = 1,
+};
+
+/**
+ * STATISTICS
+ *
+ * bfi_enet_stats_req is used by:
+ * BFI_ENET_H2I_STATS_GET_REQ
+ * BFI_ENET_I2H_STATS_CLR_REQ
+ */
+struct bfi_enet_stats_req {
+ struct bfi_msgq_mhdr mh;
+ u16 stats_mask;
+ u8 rsvd[2];
+ u32 rx_enet_mask;
+ u32 tx_enet_mask;
+ union bfi_addr_u host_buffer;
+};
+
+/**
+ * defines for "stats_mask" above.
+ */
+#define BFI_ENET_STATS_MAC (1 << 0) /* !< MAC Statistics */
+#define BFI_ENET_STATS_BPC (1 << 1) /* !< Pause Stats from BPC */
+#define BFI_ENET_STATS_RAD (1 << 2) /* !< Rx Admission Statistics */
+#define BFI_ENET_STATS_RX_FC (1 << 3) /* !< Rx FC Stats from RxA */
+#define BFI_ENET_STATS_TX_FC (1 << 4) /* !< Tx FC Stats from TxA */
+
+#define BFI_ENET_STATS_ALL 0x1f
+
+/* TxF Frame Statistics */
+struct bfi_enet_stats_txf {
+ u64 ucast_octets;
+ u64 ucast;
+ u64 ucast_vlan;
+
+ u64 mcast_octets;
+ u64 mcast;
+ u64 mcast_vlan;
+
+ u64 bcast_octets;
+ u64 bcast;
+ u64 bcast_vlan;
+
+ u64 errors;
+ u64 filter_vlan; /* frames filtered due to VLAN */
+ u64 filter_mac_sa; /* frames filtered due to SA check */
+};
+
+/* RxF Frame Statistics */
+struct bfi_enet_stats_rxf {
+ u64 ucast_octets;
+ u64 ucast;
+ u64 ucast_vlan;
+
+ u64 mcast_octets;
+ u64 mcast;
+ u64 mcast_vlan;
+
+ u64 bcast_octets;
+ u64 bcast;
+ u64 bcast_vlan;
+ u64 frame_drops;
+};
+
+/* FC Tx Frame Statistics */
+struct bfi_enet_stats_fc_tx {
+ u64 txf_ucast_octets;
+ u64 txf_ucast;
+ u64 txf_ucast_vlan;
+
+ u64 txf_mcast_octets;
+ u64 txf_mcast;
+ u64 txf_mcast_vlan;
+
+ u64 txf_bcast_octets;
+ u64 txf_bcast;
+ u64 txf_bcast_vlan;
+
+ u64 txf_parity_errors;
+ u64 txf_timeout;
+ u64 txf_fid_parity_errors;
+};
+
+/* FC Rx Frame Statistics */
+struct bfi_enet_stats_fc_rx {
+ u64 rxf_ucast_octets;
+ u64 rxf_ucast;
+ u64 rxf_ucast_vlan;
+
+ u64 rxf_mcast_octets;
+ u64 rxf_mcast;
+ u64 rxf_mcast_vlan;
+
+ u64 rxf_bcast_octets;
+ u64 rxf_bcast;
+ u64 rxf_bcast_vlan;
+};
+
+/* RAD Frame Statistics */
+struct bfi_enet_stats_rad {
+ u64 rx_frames;
+ u64 rx_octets;
+ u64 rx_vlan_frames;
+
+ u64 rx_ucast;
+ u64 rx_ucast_octets;
+ u64 rx_ucast_vlan;
+
+ u64 rx_mcast;
+ u64 rx_mcast_octets;
+ u64 rx_mcast_vlan;
+
+ u64 rx_bcast;
+ u64 rx_bcast_octets;
+ u64 rx_bcast_vlan;
+
+ u64 rx_drops;
+};
+
+/* BPC Tx Registers */
+struct bfi_enet_stats_bpc {
+ /* transmit stats */
+ u64 tx_pause[8];
+ u64 tx_zero_pause[8]; /*!< Pause cancellation */
+ /*!<Pause initiation rather than retention */
+ u64 tx_first_pause[8];
+
+ /* receive stats */
+ u64 rx_pause[8];
+ u64 rx_zero_pause[8]; /*!< Pause cancellation */
+ /*!<Pause initiation rather than retention */
+ u64 rx_first_pause[8];
+};
+
+/* MAC Rx Statistics */
+struct bfi_enet_stats_mac {
+ u64 frame_64; /* both rx and tx counter */
+ u64 frame_65_127; /* both rx and tx counter */
+ u64 frame_128_255; /* both rx and tx counter */
+ u64 frame_256_511; /* both rx and tx counter */
+ u64 frame_512_1023; /* both rx and tx counter */
+ u64 frame_1024_1518; /* both rx and tx counter */
+ u64 frame_1519_1522; /* both rx and tx counter */
+
+ /* receive stats */
+ u64 rx_bytes;
+ u64 rx_packets;
+ u64 rx_fcs_error;
+ u64 rx_multicast;
+ u64 rx_broadcast;
+ u64 rx_control_frames;
+ u64 rx_pause;
+ u64 rx_unknown_opcode;
+ u64 rx_alignment_error;
+ u64 rx_frame_length_error;
+ u64 rx_code_error;
+ u64 rx_carrier_sense_error;
+ u64 rx_undersize;
+ u64 rx_oversize;
+ u64 rx_fragments;
+ u64 rx_jabber;
+ u64 rx_drop;
+
+ /* transmit stats */
+ u64 tx_bytes;
+ u64 tx_packets;
+ u64 tx_multicast;
+ u64 tx_broadcast;
+ u64 tx_pause;
+ u64 tx_deferral;
+ u64 tx_excessive_deferral;
+ u64 tx_single_collision;
+ u64 tx_muliple_collision;
+ u64 tx_late_collision;
+ u64 tx_excessive_collision;
+ u64 tx_total_collision;
+ u64 tx_pause_honored;
+ u64 tx_drop;
+ u64 tx_jabber;
+ u64 tx_fcs_error;
+ u64 tx_control_frame;
+ u64 tx_oversize;
+ u64 tx_undersize;
+ u64 tx_fragments;
+};
+
+/**
+ * Complete statistics, DMAed from fw to host followed by
+ * BFI_ENET_I2H_STATS_GET_RSP
+ */
+struct bfi_enet_stats {
+ struct bfi_enet_stats_mac mac_stats;
+ struct bfi_enet_stats_bpc bpc_stats;
+ struct bfi_enet_stats_rad rad_stats;
+ struct bfi_enet_stats_rad rlb_stats;
+ struct bfi_enet_stats_fc_rx fc_rx_stats;
+ struct bfi_enet_stats_fc_tx fc_tx_stats;
+ struct bfi_enet_stats_rxf rxf_stats[BFI_ENET_CFG_MAX];
+ struct bfi_enet_stats_txf txf_stats[BFI_ENET_CFG_MAX];
+};
+
+#pragma pack()
+
+#endif /* __BFI_ENET_H__ */
diff --git a/drivers/net/ethernet/brocade/bna/bfi_reg.h b/drivers/net/ethernet/brocade/bna/bfi_reg.h
new file mode 100644
index 000000000000..efacff3ab51d
--- /dev/null
+++ b/drivers/net/ethernet/brocade/bna/bfi_reg.h
@@ -0,0 +1,452 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+/*
+ * bfi_reg.h ASIC register defines for all Brocade adapter ASICs
+ */
+
+#ifndef __BFI_REG_H__
+#define __BFI_REG_H__
+
+#define HOSTFN0_INT_STATUS 0x00014000 /* cb/ct */
+#define HOSTFN1_INT_STATUS 0x00014100 /* cb/ct */
+#define HOSTFN2_INT_STATUS 0x00014300 /* ct */
+#define HOSTFN3_INT_STATUS 0x00014400 /* ct */
+#define HOSTFN0_INT_MSK 0x00014004 /* cb/ct */
+#define HOSTFN1_INT_MSK 0x00014104 /* cb/ct */
+#define HOSTFN2_INT_MSK 0x00014304 /* ct */
+#define HOSTFN3_INT_MSK 0x00014404 /* ct */
+
+#define HOST_PAGE_NUM_FN0 0x00014008 /* cb/ct */
+#define HOST_PAGE_NUM_FN1 0x00014108 /* cb/ct */
+#define HOST_PAGE_NUM_FN2 0x00014308 /* ct */
+#define HOST_PAGE_NUM_FN3 0x00014408 /* ct */
+
+#define APP_PLL_LCLK_CTL_REG 0x00014204 /* cb/ct */
+#define __P_LCLK_PLL_LOCK 0x80000000
+#define __APP_PLL_LCLK_SRAM_USE_100MHZ 0x00100000
+#define __APP_PLL_LCLK_RESET_TIMER_MK 0x000e0000
+#define __APP_PLL_LCLK_RESET_TIMER_SH 17
+#define __APP_PLL_LCLK_RESET_TIMER(_v) ((_v) << __APP_PLL_LCLK_RESET_TIMER_SH)
+#define __APP_PLL_LCLK_LOGIC_SOFT_RESET 0x00010000
+#define __APP_PLL_LCLK_CNTLMT0_1_MK 0x0000c000
+#define __APP_PLL_LCLK_CNTLMT0_1_SH 14
+#define __APP_PLL_LCLK_CNTLMT0_1(_v) ((_v) << __APP_PLL_LCLK_CNTLMT0_1_SH)
+#define __APP_PLL_LCLK_JITLMT0_1_MK 0x00003000
+#define __APP_PLL_LCLK_JITLMT0_1_SH 12
+#define __APP_PLL_LCLK_JITLMT0_1(_v) ((_v) << __APP_PLL_LCLK_JITLMT0_1_SH)
+#define __APP_PLL_LCLK_HREF 0x00000800
+#define __APP_PLL_LCLK_HDIV 0x00000400
+#define __APP_PLL_LCLK_P0_1_MK 0x00000300
+#define __APP_PLL_LCLK_P0_1_SH 8
+#define __APP_PLL_LCLK_P0_1(_v) ((_v) << __APP_PLL_LCLK_P0_1_SH)
+#define __APP_PLL_LCLK_Z0_2_MK 0x000000e0
+#define __APP_PLL_LCLK_Z0_2_SH 5
+#define __APP_PLL_LCLK_Z0_2(_v) ((_v) << __APP_PLL_LCLK_Z0_2_SH)
+#define __APP_PLL_LCLK_RSEL200500 0x00000010
+#define __APP_PLL_LCLK_ENARST 0x00000008
+#define __APP_PLL_LCLK_BYPASS 0x00000004
+#define __APP_PLL_LCLK_LRESETN 0x00000002
+#define __APP_PLL_LCLK_ENABLE 0x00000001
+#define APP_PLL_SCLK_CTL_REG 0x00014208 /* cb/ct */
+#define __P_SCLK_PLL_LOCK 0x80000000
+#define __APP_PLL_SCLK_RESET_TIMER_MK 0x000e0000
+#define __APP_PLL_SCLK_RESET_TIMER_SH 17
+#define __APP_PLL_SCLK_RESET_TIMER(_v) ((_v) << __APP_PLL_SCLK_RESET_TIMER_SH)
+#define __APP_PLL_SCLK_LOGIC_SOFT_RESET 0x00010000
+#define __APP_PLL_SCLK_CNTLMT0_1_MK 0x0000c000
+#define __APP_PLL_SCLK_CNTLMT0_1_SH 14
+#define __APP_PLL_SCLK_CNTLMT0_1(_v) ((_v) << __APP_PLL_SCLK_CNTLMT0_1_SH)
+#define __APP_PLL_SCLK_JITLMT0_1_MK 0x00003000
+#define __APP_PLL_SCLK_JITLMT0_1_SH 12
+#define __APP_PLL_SCLK_JITLMT0_1(_v) ((_v) << __APP_PLL_SCLK_JITLMT0_1_SH)
+#define __APP_PLL_SCLK_HREF 0x00000800
+#define __APP_PLL_SCLK_HDIV 0x00000400
+#define __APP_PLL_SCLK_P0_1_MK 0x00000300
+#define __APP_PLL_SCLK_P0_1_SH 8
+#define __APP_PLL_SCLK_P0_1(_v) ((_v) << __APP_PLL_SCLK_P0_1_SH)
+#define __APP_PLL_SCLK_Z0_2_MK 0x000000e0
+#define __APP_PLL_SCLK_Z0_2_SH 5
+#define __APP_PLL_SCLK_Z0_2(_v) ((_v) << __APP_PLL_SCLK_Z0_2_SH)
+#define __APP_PLL_SCLK_RSEL200500 0x00000010
+#define __APP_PLL_SCLK_ENARST 0x00000008
+#define __APP_PLL_SCLK_BYPASS 0x00000004
+#define __APP_PLL_SCLK_LRESETN 0x00000002
+#define __APP_PLL_SCLK_ENABLE 0x00000001
+#define __ENABLE_MAC_AHB_1 0x00800000 /* ct */
+#define __ENABLE_MAC_AHB_0 0x00400000 /* ct */
+#define __ENABLE_MAC_1 0x00200000 /* ct */
+#define __ENABLE_MAC_0 0x00100000 /* ct */
+
+#define HOST_SEM0_REG 0x00014230 /* cb/ct */
+#define HOST_SEM1_REG 0x00014234 /* cb/ct */
+#define HOST_SEM2_REG 0x00014238 /* cb/ct */
+#define HOST_SEM3_REG 0x0001423c /* cb/ct */
+#define HOST_SEM4_REG 0x00014610 /* cb/ct */
+#define HOST_SEM5_REG 0x00014614 /* cb/ct */
+#define HOST_SEM6_REG 0x00014618 /* cb/ct */
+#define HOST_SEM7_REG 0x0001461c /* cb/ct */
+#define HOST_SEM0_INFO_REG 0x00014240 /* cb/ct */
+#define HOST_SEM1_INFO_REG 0x00014244 /* cb/ct */
+#define HOST_SEM2_INFO_REG 0x00014248 /* cb/ct */
+#define HOST_SEM3_INFO_REG 0x0001424c /* cb/ct */
+#define HOST_SEM4_INFO_REG 0x00014620 /* cb/ct */
+#define HOST_SEM5_INFO_REG 0x00014624 /* cb/ct */
+#define HOST_SEM6_INFO_REG 0x00014628 /* cb/ct */
+#define HOST_SEM7_INFO_REG 0x0001462c /* cb/ct */
+
+#define HOSTFN0_LPU0_CMD_STAT 0x00019000 /* cb/ct */
+#define HOSTFN0_LPU1_CMD_STAT 0x00019004 /* cb/ct */
+#define HOSTFN1_LPU0_CMD_STAT 0x00019010 /* cb/ct */
+#define HOSTFN1_LPU1_CMD_STAT 0x00019014 /* cb/ct */
+#define HOSTFN2_LPU0_CMD_STAT 0x00019150 /* ct */
+#define HOSTFN2_LPU1_CMD_STAT 0x00019154 /* ct */
+#define HOSTFN3_LPU0_CMD_STAT 0x00019160 /* ct */
+#define HOSTFN3_LPU1_CMD_STAT 0x00019164 /* ct */
+#define LPU0_HOSTFN0_CMD_STAT 0x00019008 /* cb/ct */
+#define LPU1_HOSTFN0_CMD_STAT 0x0001900c /* cb/ct */
+#define LPU0_HOSTFN1_CMD_STAT 0x00019018 /* cb/ct */
+#define LPU1_HOSTFN1_CMD_STAT 0x0001901c /* cb/ct */
+#define LPU0_HOSTFN2_CMD_STAT 0x00019158 /* ct */
+#define LPU1_HOSTFN2_CMD_STAT 0x0001915c /* ct */
+#define LPU0_HOSTFN3_CMD_STAT 0x00019168 /* ct */
+#define LPU1_HOSTFN3_CMD_STAT 0x0001916c /* ct */
+
+#define PSS_CTL_REG 0x00018800 /* cb/ct */
+#define __PSS_I2C_CLK_DIV_MK 0x007f0000
+#define __PSS_I2C_CLK_DIV_SH 16
+#define __PSS_I2C_CLK_DIV(_v) ((_v) << __PSS_I2C_CLK_DIV_SH)
+#define __PSS_LMEM_INIT_DONE 0x00001000
+#define __PSS_LMEM_RESET 0x00000200
+#define __PSS_LMEM_INIT_EN 0x00000100
+#define __PSS_LPU1_RESET 0x00000002
+#define __PSS_LPU0_RESET 0x00000001
+#define PSS_ERR_STATUS_REG 0x00018810 /* cb/ct */
+#define ERR_SET_REG 0x00018818 /* cb/ct */
+#define PSS_GPIO_OUT_REG 0x000188c0 /* cb/ct */
+#define __PSS_GPIO_OUT_REG 0x00000fff
+#define PSS_GPIO_OE_REG 0x000188c8 /* cb/ct */
+#define __PSS_GPIO_OE_REG 0x000000ff
+
+#define HOSTFN0_LPU_MBOX0_0 0x00019200 /* cb/ct */
+#define HOSTFN1_LPU_MBOX0_8 0x00019260 /* cb/ct */
+#define LPU_HOSTFN0_MBOX0_0 0x00019280 /* cb/ct */
+#define LPU_HOSTFN1_MBOX0_8 0x000192e0 /* cb/ct */
+#define HOSTFN2_LPU_MBOX0_0 0x00019400 /* ct */
+#define HOSTFN3_LPU_MBOX0_8 0x00019460 /* ct */
+#define LPU_HOSTFN2_MBOX0_0 0x00019480 /* ct */
+#define LPU_HOSTFN3_MBOX0_8 0x000194e0 /* ct */
+
+#define HOST_MSIX_ERR_INDEX_FN0 0x0001400c /* ct */
+#define HOST_MSIX_ERR_INDEX_FN1 0x0001410c /* ct */
+#define HOST_MSIX_ERR_INDEX_FN2 0x0001430c /* ct */
+#define HOST_MSIX_ERR_INDEX_FN3 0x0001440c /* ct */
+
+#define MBIST_CTL_REG 0x00014220 /* ct */
+#define __EDRAM_BISTR_START 0x00000004
+#define MBIST_STAT_REG 0x00014224 /* ct */
+#define ETH_MAC_SER_REG 0x00014288 /* ct */
+#define __APP_EMS_CKBUFAMPIN 0x00000020
+#define __APP_EMS_REFCLKSEL 0x00000010
+#define __APP_EMS_CMLCKSEL 0x00000008
+#define __APP_EMS_REFCKBUFEN2 0x00000004
+#define __APP_EMS_REFCKBUFEN1 0x00000002
+#define __APP_EMS_CHANNEL_SEL 0x00000001
+#define FNC_PERS_REG 0x00014604 /* ct */
+#define __F3_FUNCTION_ACTIVE 0x80000000
+#define __F3_FUNCTION_MODE 0x40000000
+#define __F3_PORT_MAP_MK 0x30000000
+#define __F3_PORT_MAP_SH 28
+#define __F3_PORT_MAP(_v) ((_v) << __F3_PORT_MAP_SH)
+#define __F3_VM_MODE 0x08000000
+#define __F3_INTX_STATUS_MK 0x07000000
+#define __F3_INTX_STATUS_SH 24
+#define __F3_INTX_STATUS(_v) ((_v) << __F3_INTX_STATUS_SH)
+#define __F2_FUNCTION_ACTIVE 0x00800000
+#define __F2_FUNCTION_MODE 0x00400000
+#define __F2_PORT_MAP_MK 0x00300000
+#define __F2_PORT_MAP_SH 20
+#define __F2_PORT_MAP(_v) ((_v) << __F2_PORT_MAP_SH)
+#define __F2_VM_MODE 0x00080000
+#define __F2_INTX_STATUS_MK 0x00070000
+#define __F2_INTX_STATUS_SH 16
+#define __F2_INTX_STATUS(_v) ((_v) << __F2_INTX_STATUS_SH)
+#define __F1_FUNCTION_ACTIVE 0x00008000
+#define __F1_FUNCTION_MODE 0x00004000
+#define __F1_PORT_MAP_MK 0x00003000
+#define __F1_PORT_MAP_SH 12
+#define __F1_PORT_MAP(_v) ((_v) << __F1_PORT_MAP_SH)
+#define __F1_VM_MODE 0x00000800
+#define __F1_INTX_STATUS_MK 0x00000700
+#define __F1_INTX_STATUS_SH 8
+#define __F1_INTX_STATUS(_v) ((_v) << __F1_INTX_STATUS_SH)
+#define __F0_FUNCTION_ACTIVE 0x00000080
+#define __F0_FUNCTION_MODE 0x00000040
+#define __F0_PORT_MAP_MK 0x00000030
+#define __F0_PORT_MAP_SH 4
+#define __F0_PORT_MAP(_v) ((_v) << __F0_PORT_MAP_SH)
+#define __F0_VM_MODE 0x00000008
+#define __F0_INTX_STATUS 0x00000007
+enum {
+ __F0_INTX_STATUS_MSIX = 0x0,
+ __F0_INTX_STATUS_INTA = 0x1,
+ __F0_INTX_STATUS_INTB = 0x2,
+ __F0_INTX_STATUS_INTC = 0x3,
+ __F0_INTX_STATUS_INTD = 0x4,
+};
+
+#define OP_MODE 0x0001460c
+#define __APP_ETH_CLK_LOWSPEED 0x00000004
+#define __GLOBAL_CORECLK_HALFSPEED 0x00000002
+#define __GLOBAL_FCOE_MODE 0x00000001
+#define FW_INIT_HALT_P0 0x000191ac
+#define __FW_INIT_HALT_P 0x00000001
+#define FW_INIT_HALT_P1 0x000191bc
+#define PMM_1T_RESET_REG_P0 0x0002381c
+#define __PMM_1T_RESET_P 0x00000001
+#define PMM_1T_RESET_REG_P1 0x00023c1c
+
+/**
+ * Brocade 1860 Adapter specific defines
+ */
+#define CT2_PCI_CPQ_BASE 0x00030000
+#define CT2_PCI_APP_BASE 0x00030100
+#define CT2_PCI_ETH_BASE 0x00030400
+
+/*
+ * APP block registers
+ */
+#define CT2_HOSTFN_INT_STATUS (CT2_PCI_APP_BASE + 0x00)
+#define CT2_HOSTFN_INTR_MASK (CT2_PCI_APP_BASE + 0x04)
+#define CT2_HOSTFN_PERSONALITY0 (CT2_PCI_APP_BASE + 0x08)
+#define __PME_STATUS_ 0x00200000
+#define __PF_VF_BAR_SIZE_MODE__MK 0x00180000
+#define __PF_VF_BAR_SIZE_MODE__SH 19
+#define __PF_VF_BAR_SIZE_MODE_(_v) ((_v) << __PF_VF_BAR_SIZE_MODE__SH)
+#define __FC_LL_PORT_MAP__MK 0x00060000
+#define __FC_LL_PORT_MAP__SH 17
+#define __FC_LL_PORT_MAP_(_v) ((_v) << __FC_LL_PORT_MAP__SH)
+#define __PF_VF_ACTIVE_ 0x00010000
+#define __PF_VF_CFG_RDY_ 0x00008000
+#define __PF_VF_ENABLE_ 0x00004000
+#define __PF_DRIVER_ACTIVE_ 0x00002000
+#define __PF_PME_SEND_ENABLE_ 0x00001000
+#define __PF_EXROM_OFFSET__MK 0x00000ff0
+#define __PF_EXROM_OFFSET__SH 4
+#define __PF_EXROM_OFFSET_(_v) ((_v) << __PF_EXROM_OFFSET__SH)
+#define __FC_LL_MODE_ 0x00000008
+#define __PF_INTX_PIN_ 0x00000007
+#define CT2_HOSTFN_PERSONALITY1 (CT2_PCI_APP_BASE + 0x0C)
+#define __PF_NUM_QUEUES1__MK 0xff000000
+#define __PF_NUM_QUEUES1__SH 24
+#define __PF_NUM_QUEUES1_(_v) ((_v) << __PF_NUM_QUEUES1__SH)
+#define __PF_VF_QUE_OFFSET1__MK 0x00ff0000
+#define __PF_VF_QUE_OFFSET1__SH 16
+#define __PF_VF_QUE_OFFSET1_(_v) ((_v) << __PF_VF_QUE_OFFSET1__SH)
+#define __PF_VF_NUM_QUEUES__MK 0x0000ff00
+#define __PF_VF_NUM_QUEUES__SH 8
+#define __PF_VF_NUM_QUEUES_(_v) ((_v) << __PF_VF_NUM_QUEUES__SH)
+#define __PF_VF_QUE_OFFSET_ 0x000000ff
+#define CT2_HOSTFN_PAGE_NUM (CT2_PCI_APP_BASE + 0x18)
+#define CT2_HOSTFN_MSIX_VT_INDEX_MBOX_ERR (CT2_PCI_APP_BASE + 0x38)
+
+/*
+ * Brocade 1860 adapter CPQ block registers
+ */
+#define CT2_HOSTFN_LPU0_MBOX0 (CT2_PCI_CPQ_BASE + 0x00)
+#define CT2_HOSTFN_LPU1_MBOX0 (CT2_PCI_CPQ_BASE + 0x20)
+#define CT2_LPU0_HOSTFN_MBOX0 (CT2_PCI_CPQ_BASE + 0x40)
+#define CT2_LPU1_HOSTFN_MBOX0 (CT2_PCI_CPQ_BASE + 0x60)
+#define CT2_HOSTFN_LPU0_CMD_STAT (CT2_PCI_CPQ_BASE + 0x80)
+#define CT2_HOSTFN_LPU1_CMD_STAT (CT2_PCI_CPQ_BASE + 0x84)
+#define CT2_LPU0_HOSTFN_CMD_STAT (CT2_PCI_CPQ_BASE + 0x88)
+#define CT2_LPU1_HOSTFN_CMD_STAT (CT2_PCI_CPQ_BASE + 0x8c)
+#define CT2_HOSTFN_LPU0_READ_STAT (CT2_PCI_CPQ_BASE + 0x90)
+#define CT2_HOSTFN_LPU1_READ_STAT (CT2_PCI_CPQ_BASE + 0x94)
+#define CT2_LPU0_HOSTFN_MBOX0_MSK (CT2_PCI_CPQ_BASE + 0x98)
+#define CT2_LPU1_HOSTFN_MBOX0_MSK (CT2_PCI_CPQ_BASE + 0x9C)
+#define CT2_HOST_SEM0_REG 0x000148f0
+#define CT2_HOST_SEM1_REG 0x000148f4
+#define CT2_HOST_SEM2_REG 0x000148f8
+#define CT2_HOST_SEM3_REG 0x000148fc
+#define CT2_HOST_SEM4_REG 0x00014900
+#define CT2_HOST_SEM5_REG 0x00014904
+#define CT2_HOST_SEM6_REG 0x00014908
+#define CT2_HOST_SEM7_REG 0x0001490c
+#define CT2_HOST_SEM0_INFO_REG 0x000148b0
+#define CT2_HOST_SEM1_INFO_REG 0x000148b4
+#define CT2_HOST_SEM2_INFO_REG 0x000148b8
+#define CT2_HOST_SEM3_INFO_REG 0x000148bc
+#define CT2_HOST_SEM4_INFO_REG 0x000148c0
+#define CT2_HOST_SEM5_INFO_REG 0x000148c4
+#define CT2_HOST_SEM6_INFO_REG 0x000148c8
+#define CT2_HOST_SEM7_INFO_REG 0x000148cc
+
+#define CT2_APP_PLL_LCLK_CTL_REG 0x00014808
+#define __APP_LPUCLK_HALFSPEED 0x40000000
+#define __APP_PLL_LCLK_LOAD 0x20000000
+#define __APP_PLL_LCLK_FBCNT_MK 0x1fe00000
+#define __APP_PLL_LCLK_FBCNT_SH 21
+#define __APP_PLL_LCLK_FBCNT(_v) ((_v) << __APP_PLL_SCLK_FBCNT_SH)
+enum {
+ __APP_PLL_LCLK_FBCNT_425_MHZ = 6,
+ __APP_PLL_LCLK_FBCNT_468_MHZ = 4,
+};
+#define __APP_PLL_LCLK_EXTFB 0x00000800
+#define __APP_PLL_LCLK_ENOUTS 0x00000400
+#define __APP_PLL_LCLK_RATE 0x00000010
+#define CT2_APP_PLL_SCLK_CTL_REG 0x0001480c
+#define __P_SCLK_PLL_LOCK 0x80000000
+#define __APP_PLL_SCLK_REFCLK_SEL 0x40000000
+#define __APP_PLL_SCLK_CLK_DIV2 0x20000000
+#define __APP_PLL_SCLK_LOAD 0x10000000
+#define __APP_PLL_SCLK_FBCNT_MK 0x0ff00000
+#define __APP_PLL_SCLK_FBCNT_SH 20
+#define __APP_PLL_SCLK_FBCNT(_v) ((_v) << __APP_PLL_SCLK_FBCNT_SH)
+enum {
+ __APP_PLL_SCLK_FBCNT_NORM = 6,
+ __APP_PLL_SCLK_FBCNT_10G_FC = 10,
+};
+#define __APP_PLL_SCLK_EXTFB 0x00000800
+#define __APP_PLL_SCLK_ENOUTS 0x00000400
+#define __APP_PLL_SCLK_RATE 0x00000010
+#define CT2_PCIE_MISC_REG 0x00014804
+#define __ETH_CLK_ENABLE_PORT1 0x00000010
+#define CT2_CHIP_MISC_PRG 0x000148a4
+#define __ETH_CLK_ENABLE_PORT0 0x00004000
+#define __APP_LPU_SPEED 0x00000002
+#define CT2_MBIST_STAT_REG 0x00014818
+#define CT2_MBIST_CTL_REG 0x0001481c
+#define CT2_PMM_1T_CONTROL_REG_P0 0x0002381c
+#define __PMM_1T_PNDB_P 0x00000002
+#define CT2_PMM_1T_CONTROL_REG_P1 0x00023c1c
+#define CT2_WGN_STATUS 0x00014990
+#define __A2T_AHB_LOAD 0x00000800
+#define __WGN_READY 0x00000400
+#define __GLBL_PF_VF_CFG_RDY 0x00000200
+#define CT2_NFC_CSR_SET_REG 0x00027424
+#define __HALT_NFC_CONTROLLER 0x00000002
+#define __NFC_CONTROLLER_HALTED 0x00001000
+
+#define CT2_CSI_MAC0_CONTROL_REG 0x000270d0
+#define __CSI_MAC_RESET 0x00000010
+#define __CSI_MAC_AHB_RESET 0x00000008
+#define CT2_CSI_MAC1_CONTROL_REG 0x000270d4
+#define CT2_CSI_MAC_CONTROL_REG(__n) \
+ (CT2_CSI_MAC0_CONTROL_REG + \
+ (__n) * (CT2_CSI_MAC1_CONTROL_REG - CT2_CSI_MAC0_CONTROL_REG))
+
+/*
+ * Name semaphore registers based on usage
+ */
+#define BFA_IOC0_HBEAT_REG HOST_SEM0_INFO_REG
+#define BFA_IOC0_STATE_REG HOST_SEM1_INFO_REG
+#define BFA_IOC1_HBEAT_REG HOST_SEM2_INFO_REG
+#define BFA_IOC1_STATE_REG HOST_SEM3_INFO_REG
+#define BFA_FW_USE_COUNT HOST_SEM4_INFO_REG
+#define BFA_IOC_FAIL_SYNC HOST_SEM5_INFO_REG
+
+/*
+ * CT2 semaphore register locations changed
+ */
+#define CT2_BFA_IOC0_HBEAT_REG CT2_HOST_SEM0_INFO_REG
+#define CT2_BFA_IOC0_STATE_REG CT2_HOST_SEM1_INFO_REG
+#define CT2_BFA_IOC1_HBEAT_REG CT2_HOST_SEM2_INFO_REG
+#define CT2_BFA_IOC1_STATE_REG CT2_HOST_SEM3_INFO_REG
+#define CT2_BFA_FW_USE_COUNT CT2_HOST_SEM4_INFO_REG
+#define CT2_BFA_IOC_FAIL_SYNC CT2_HOST_SEM5_INFO_REG
+
+#define CPE_Q_NUM(__fn, __q) (((__fn) << 2) + (__q))
+#define RME_Q_NUM(__fn, __q) (((__fn) << 2) + (__q))
+
+/*
+ * And corresponding host interrupt status bit field defines
+ */
+#define __HFN_INT_CPE_Q0 0x00000001U
+#define __HFN_INT_CPE_Q1 0x00000002U
+#define __HFN_INT_CPE_Q2 0x00000004U
+#define __HFN_INT_CPE_Q3 0x00000008U
+#define __HFN_INT_CPE_Q4 0x00000010U
+#define __HFN_INT_CPE_Q5 0x00000020U
+#define __HFN_INT_CPE_Q6 0x00000040U
+#define __HFN_INT_CPE_Q7 0x00000080U
+#define __HFN_INT_RME_Q0 0x00000100U
+#define __HFN_INT_RME_Q1 0x00000200U
+#define __HFN_INT_RME_Q2 0x00000400U
+#define __HFN_INT_RME_Q3 0x00000800U
+#define __HFN_INT_RME_Q4 0x00001000U
+#define __HFN_INT_RME_Q5 0x00002000U
+#define __HFN_INT_RME_Q6 0x00004000U
+#define __HFN_INT_RME_Q7 0x00008000U
+#define __HFN_INT_ERR_EMC 0x00010000U
+#define __HFN_INT_ERR_LPU0 0x00020000U
+#define __HFN_INT_ERR_LPU1 0x00040000U
+#define __HFN_INT_ERR_PSS 0x00080000U
+#define __HFN_INT_MBOX_LPU0 0x00100000U
+#define __HFN_INT_MBOX_LPU1 0x00200000U
+#define __HFN_INT_MBOX1_LPU0 0x00400000U
+#define __HFN_INT_MBOX1_LPU1 0x00800000U
+#define __HFN_INT_LL_HALT 0x01000000U
+#define __HFN_INT_CPE_MASK 0x000000ffU
+#define __HFN_INT_RME_MASK 0x0000ff00U
+#define __HFN_INT_ERR_MASK \
+ (__HFN_INT_ERR_EMC | __HFN_INT_ERR_LPU0 | __HFN_INT_ERR_LPU1 | \
+ __HFN_INT_ERR_PSS | __HFN_INT_LL_HALT)
+#define __HFN_INT_FN0_MASK \
+ (__HFN_INT_CPE_Q0 | __HFN_INT_CPE_Q1 | __HFN_INT_CPE_Q2 | \
+ __HFN_INT_CPE_Q3 | __HFN_INT_RME_Q0 | __HFN_INT_RME_Q1 | \
+ __HFN_INT_RME_Q2 | __HFN_INT_RME_Q3 | __HFN_INT_MBOX_LPU0)
+#define __HFN_INT_FN1_MASK \
+ (__HFN_INT_CPE_Q4 | __HFN_INT_CPE_Q5 | __HFN_INT_CPE_Q6 | \
+ __HFN_INT_CPE_Q7 | __HFN_INT_RME_Q4 | __HFN_INT_RME_Q5 | \
+ __HFN_INT_RME_Q6 | __HFN_INT_RME_Q7 | __HFN_INT_MBOX_LPU1)
+
+/*
+ * Host interrupt status defines for 1860
+ */
+#define __HFN_INT_MBOX_LPU0_CT2 0x00010000U
+#define __HFN_INT_MBOX_LPU1_CT2 0x00020000U
+#define __HFN_INT_ERR_PSS_CT2 0x00040000U
+#define __HFN_INT_ERR_LPU0_CT2 0x00080000U
+#define __HFN_INT_ERR_LPU1_CT2 0x00100000U
+#define __HFN_INT_CPQ_HALT_CT2 0x00200000U
+#define __HFN_INT_ERR_WGN_CT2 0x00400000U
+#define __HFN_INT_ERR_LEHRX_CT2 0x00800000U
+#define __HFN_INT_ERR_LEHTX_CT2 0x01000000U
+#define __HFN_INT_ERR_MASK_CT2 \
+ (__HFN_INT_ERR_PSS_CT2 | __HFN_INT_ERR_LPU0_CT2 | \
+ __HFN_INT_ERR_LPU1_CT2 | __HFN_INT_CPQ_HALT_CT2 | \
+ __HFN_INT_ERR_WGN_CT2 | __HFN_INT_ERR_LEHRX_CT2 | \
+ __HFN_INT_ERR_LEHTX_CT2)
+#define __HFN_INT_FN0_MASK_CT2 \
+ (__HFN_INT_CPE_Q0 | __HFN_INT_CPE_Q1 | __HFN_INT_CPE_Q2 | \
+ __HFN_INT_CPE_Q3 | __HFN_INT_RME_Q0 | __HFN_INT_RME_Q1 | \
+ __HFN_INT_RME_Q2 | __HFN_INT_RME_Q3 | __HFN_INT_MBOX_LPU0_CT2)
+#define __HFN_INT_FN1_MASK_CT2 \
+ (__HFN_INT_CPE_Q4 | __HFN_INT_CPE_Q5 | __HFN_INT_CPE_Q6 | \
+ __HFN_INT_CPE_Q7 | __HFN_INT_RME_Q4 | __HFN_INT_RME_Q5 | \
+ __HFN_INT_RME_Q6 | __HFN_INT_RME_Q7 | __HFN_INT_MBOX_LPU1_CT2)
+
+/*
+ * asic memory map.
+ */
+#define PSS_SMEM_PAGE_START 0x8000
+#define PSS_SMEM_PGNUM(_pg0, _ma) ((_pg0) + ((_ma) >> 15))
+#define PSS_SMEM_PGOFF(_ma) ((_ma) & 0x7fff)
+
+#endif /* __BFI_REG_H__ */
diff --git a/drivers/net/bna/bna.h b/drivers/net/ethernet/brocade/bna/bna.h
index a287f89b0289..4d7a5de08e12 100644
--- a/drivers/net/bna/bna.h
+++ b/drivers/net/ethernet/brocade/bna/bna.h
@@ -10,13 +10,17 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
#ifndef __BNA_H__
#define __BNA_H__
-#include "bfa_wc.h"
+#include "bfa_defs.h"
#include "bfa_ioc.h"
-#include "cna.h"
-#include "bfi_ll.h"
+#include "bfi_enet.h"
#include "bna_types.h"
extern const u32 bna_napi_dim_vector[][BNA_BIAS_T_MAX];
@@ -32,15 +36,7 @@ extern const u32 bna_napi_dim_vector[][BNA_BIAS_T_MAX];
/* Log string size */
#define BNA_MESSAGE_SIZE 256
-/* MBOX API for PORT, TX, RX */
-#define bna_mbox_qe_fill(_qe, _cmd, _cmd_len, _cbfn, _cbarg) \
-do { \
- memcpy(&((_qe)->cmd.msg[0]), (_cmd), (_cmd_len)); \
- (_qe)->cbfn = (_cbfn); \
- (_qe)->cbarg = (_cbarg); \
-} while (0)
-
-#define bna_is_small_rxq(rcb) ((rcb)->id == 1)
+#define bna_is_small_rxq(_id) ((_id) & 0x1)
#define BNA_MAC_IS_EQUAL(_mac1, _mac2) \
(!memcmp((_mac1), (_mac2), sizeof(mac_t)))
@@ -88,7 +84,7 @@ do { \
} while (0)
#define containing_rec(addr, type, field) \
- ((type *)((unsigned char *)(addr) - \
+ ((type *)((unsigned char *)(addr) - \
(unsigned char *)(&((type *)0)->field)))
#define BNA_TXQ_WI_NEEDED(_vectors) (((_vectors) + 3) >> 2)
@@ -101,8 +97,8 @@ do { \
{ \
unsigned int page_index; /* index within a page */ \
void *page_addr; \
- page_index = (_qe_idx) & (BNA_TXQ_PAGE_INDEX_MAX - 1); \
- (_qe_ptr_range) = (BNA_TXQ_PAGE_INDEX_MAX - page_index); \
+ page_index = (_qe_idx) & (BNA_TXQ_PAGE_INDEX_MAX - 1); \
+ (_qe_ptr_range) = (BNA_TXQ_PAGE_INDEX_MAX - page_index); \
page_addr = (_qpt_ptr)[((_qe_idx) >> BNA_TXQ_PAGE_INDEX_MAX_SHIFT)];\
(_qe_ptr) = &((struct bna_txq_entry *)(page_addr))[page_index]; \
}
@@ -166,43 +162,17 @@ do { \
(((_q_ptr)->q.producer_index + (_num)) & \
((_q_ptr)->q.q_depth - 1))
-#define BNA_Q_CI_ADD(_q_ptr, _num) \
+#define BNA_Q_CI_ADD(_q_ptr, _num) \
(_q_ptr)->q.consumer_index = \
- (((_q_ptr)->q.consumer_index + (_num)) \
+ (((_q_ptr)->q.consumer_index + (_num)) \
& ((_q_ptr)->q.q_depth - 1))
#define BNA_Q_FREE_COUNT(_q_ptr) \
(BNA_QE_FREE_CNT(&((_q_ptr)->q), (_q_ptr)->q.q_depth))
-#define BNA_Q_IN_USE_COUNT(_q_ptr) \
+#define BNA_Q_IN_USE_COUNT(_q_ptr) \
(BNA_QE_IN_USE_CNT(&(_q_ptr)->q, (_q_ptr)->q.q_depth))
-/* These macros build the data portion of the TxQ/RxQ doorbell */
-#define BNA_DOORBELL_Q_PRD_IDX(_pi) (0x80000000 | (_pi))
-#define BNA_DOORBELL_Q_STOP (0x40000000)
-
-/* These macros build the data portion of the IB doorbell */
-#define BNA_DOORBELL_IB_INT_ACK(_timeout, _events) \
- (0x80000000 | ((_timeout) << 16) | (_events))
-#define BNA_DOORBELL_IB_INT_DISABLE (0x40000000)
-
-/* Set the coalescing timer for the given ib */
-#define bna_ib_coalescing_timer_set(_i_dbell, _cls_timer) \
- ((_i_dbell)->doorbell_ack = BNA_DOORBELL_IB_INT_ACK((_cls_timer), 0));
-
-/* Acks 'events' # of events for a given ib */
-#define bna_ib_ack(_i_dbell, _events) \
- (writel(((_i_dbell)->doorbell_ack | (_events)), \
- (_i_dbell)->doorbell_addr));
-
-#define bna_txq_prod_indx_doorbell(_tcb) \
- (writel(BNA_DOORBELL_Q_PRD_IDX((_tcb)->producer_index), \
- (_tcb)->q_dbell));
-
-#define bna_rxq_prod_indx_doorbell(_rcb) \
- (writel(BNA_DOORBELL_Q_PRD_IDX((_rcb)->producer_index), \
- (_rcb)->q_dbell));
-
#define BNA_LARGE_PKT_SIZE 1000
#define BNA_UPDATE_PKT_CNT(_pkt, _len) \
@@ -214,38 +184,59 @@ do { \
} \
} while (0)
-#define call_rxf_stop_cbfn(rxf, status) \
+#define call_rxf_stop_cbfn(rxf) \
+do { \
if ((rxf)->stop_cbfn) { \
- (*(rxf)->stop_cbfn)((rxf)->stop_cbarg, (status)); \
+ void (*cbfn)(struct bna_rx *); \
+ struct bna_rx *cbarg; \
+ cbfn = (rxf)->stop_cbfn; \
+ cbarg = (rxf)->stop_cbarg; \
(rxf)->stop_cbfn = NULL; \
(rxf)->stop_cbarg = NULL; \
- }
+ cbfn(cbarg); \
+ } \
+} while (0)
-#define call_rxf_start_cbfn(rxf, status) \
+#define call_rxf_start_cbfn(rxf) \
+do { \
if ((rxf)->start_cbfn) { \
- (*(rxf)->start_cbfn)((rxf)->start_cbarg, (status)); \
+ void (*cbfn)(struct bna_rx *); \
+ struct bna_rx *cbarg; \
+ cbfn = (rxf)->start_cbfn; \
+ cbarg = (rxf)->start_cbarg; \
(rxf)->start_cbfn = NULL; \
(rxf)->start_cbarg = NULL; \
- }
+ cbfn(cbarg); \
+ } \
+} while (0)
-#define call_rxf_cam_fltr_cbfn(rxf, status) \
+#define call_rxf_cam_fltr_cbfn(rxf) \
+do { \
if ((rxf)->cam_fltr_cbfn) { \
- (*(rxf)->cam_fltr_cbfn)((rxf)->cam_fltr_cbarg, rxf->rx, \
- (status)); \
+ void (*cbfn)(struct bnad *, struct bna_rx *); \
+ struct bnad *cbarg; \
+ cbfn = (rxf)->cam_fltr_cbfn; \
+ cbarg = (rxf)->cam_fltr_cbarg; \
(rxf)->cam_fltr_cbfn = NULL; \
(rxf)->cam_fltr_cbarg = NULL; \
- }
+ cbfn(cbarg, rxf->rx); \
+ } \
+} while (0)
-#define call_rxf_pause_cbfn(rxf, status) \
+#define call_rxf_pause_cbfn(rxf) \
+do { \
if ((rxf)->oper_state_cbfn) { \
- (*(rxf)->oper_state_cbfn)((rxf)->oper_state_cbarg, rxf->rx,\
- (status)); \
- (rxf)->rxf_flags &= ~BNA_RXF_FL_OPERSTATE_CHANGED; \
+ void (*cbfn)(struct bnad *, struct bna_rx *); \
+ struct bnad *cbarg; \
+ cbfn = (rxf)->oper_state_cbfn; \
+ cbarg = (rxf)->oper_state_cbarg; \
(rxf)->oper_state_cbfn = NULL; \
(rxf)->oper_state_cbarg = NULL; \
- }
+ cbfn(cbarg, rxf->rx); \
+ } \
+} while (0)
-#define call_rxf_resume_cbfn(rxf, status) call_rxf_pause_cbfn(rxf, status)
+#define call_rxf_resume_cbfn(rxf) call_rxf_pause_cbfn(rxf)
#define is_xxx_enable(mode, bitmask, xxx) ((bitmask & xxx) && (mode & xxx))
@@ -331,6 +322,61 @@ do { \
} \
} while (0)
+#define bna_tx_rid_mask(_bna) ((_bna)->tx_mod.rid_mask)
+
+#define bna_rx_rid_mask(_bna) ((_bna)->rx_mod.rid_mask)
+
+#define bna_tx_from_rid(_bna, _rid, _tx) \
+do { \
+ struct bna_tx_mod *__tx_mod = &(_bna)->tx_mod; \
+ struct bna_tx *__tx; \
+ struct list_head *qe; \
+ _tx = NULL; \
+ list_for_each(qe, &__tx_mod->tx_active_q) { \
+ __tx = (struct bna_tx *)qe; \
+ if (__tx->rid == (_rid)) { \
+ (_tx) = __tx; \
+ break; \
+ } \
+ } \
+} while (0)
+
+#define bna_rx_from_rid(_bna, _rid, _rx) \
+do { \
+ struct bna_rx_mod *__rx_mod = &(_bna)->rx_mod; \
+ struct bna_rx *__rx; \
+ struct list_head *qe; \
+ _rx = NULL; \
+ list_for_each(qe, &__rx_mod->rx_active_q) { \
+ __rx = (struct bna_rx *)qe; \
+ if (__rx->rid == (_rid)) { \
+ (_rx) = __rx; \
+ break; \
+ } \
+ } \
+} while (0)
+
+/**
+ *
+ * Inline functions
+ *
+ */
+
+static inline struct bna_mac *bna_mac_find(struct list_head *q, u8 *addr)
+{
+ struct bna_mac *mac = NULL;
+ struct list_head *qe;
+ list_for_each(qe, q) {
+ if (BNA_MAC_IS_EQUAL(((struct bna_mac *)qe)->addr, addr)) {
+ mac = (struct bna_mac *)qe;
+ break;
+ }
+ }
+ return mac;
+}
+
+#define bna_attr(_bna) (&(_bna)->ioceth.attr)
+
/**
*
* Function prototypes
@@ -341,17 +387,20 @@ do { \
* BNA
*/
+/* FW response handlers */
+void bna_bfi_stats_clr_rsp(struct bna *bna, struct bfi_msgq_mhdr *msghdr);
+
/* APIs for BNAD */
void bna_res_req(struct bna_res_info *res_info);
+void bna_mod_res_req(struct bna *bna, struct bna_res_info *res_info);
void bna_init(struct bna *bna, struct bnad *bnad,
struct bfa_pcidev *pcidev,
struct bna_res_info *res_info);
+void bna_mod_init(struct bna *bna, struct bna_res_info *res_info);
void bna_uninit(struct bna *bna);
-void bna_stats_get(struct bna *bna);
-void bna_get_perm_mac(struct bna *bna, u8 *mac);
-
-/* APIs for Rx */
-int bna_rit_mod_can_satisfy(struct bna_rit_mod *rit_mod, int seg_size);
+int bna_num_txq_set(struct bna *bna, int num_txq);
+int bna_num_rxp_set(struct bna *bna, int num_rxp);
+void bna_hw_stats_get(struct bna *bna);
/* APIs for RxF */
struct bna_mac *bna_ucam_mod_mac_get(struct bna_ucam_mod *ucam_mod);
@@ -360,127 +409,78 @@ void bna_ucam_mod_mac_put(struct bna_ucam_mod *ucam_mod,
struct bna_mac *bna_mcam_mod_mac_get(struct bna_mcam_mod *mcam_mod);
void bna_mcam_mod_mac_put(struct bna_mcam_mod *mcam_mod,
struct bna_mac *mac);
-struct bna_rit_segment *
-bna_rit_mod_seg_get(struct bna_rit_mod *rit_mod, int seg_size);
-void bna_rit_mod_seg_put(struct bna_rit_mod *rit_mod,
- struct bna_rit_segment *seg);
-
-/**
- * DEVICE
- */
-
-/* APIs for BNAD */
-void bna_device_enable(struct bna_device *device);
-void bna_device_disable(struct bna_device *device,
- enum bna_cleanup_type type);
+struct bna_mcam_handle *bna_mcam_mod_handle_get(struct bna_mcam_mod *mod);
+void bna_mcam_mod_handle_put(struct bna_mcam_mod *mcam_mod,
+ struct bna_mcam_handle *handle);
/**
* MBOX
*/
-/* APIs for PORT, TX, RX */
-void bna_mbox_handler(struct bna *bna, u32 intr_status);
-void bna_mbox_send(struct bna *bna, struct bna_mbox_qe *mbox_qe);
-
-/**
- * PORT
- */
-
-/* API for RX */
-int bna_port_mtu_get(struct bna_port *port);
-void bna_llport_rx_started(struct bna_llport *llport);
-void bna_llport_rx_stopped(struct bna_llport *llport);
-
/* API for BNAD */
-void bna_port_enable(struct bna_port *port);
-void bna_port_disable(struct bna_port *port, enum bna_cleanup_type type,
- void (*cbfn)(void *, enum bna_cb_status));
-void bna_port_pause_config(struct bna_port *port,
- struct bna_pause_config *pause_config,
- void (*cbfn)(struct bnad *, enum bna_cb_status));
-void bna_port_mtu_set(struct bna_port *port, int mtu,
- void (*cbfn)(struct bnad *, enum bna_cb_status));
-void bna_port_mac_get(struct bna_port *port, mac_t *mac);
-
-/* Callbacks for TX, RX */
-void bna_port_cb_tx_stopped(struct bna_port *port,
- enum bna_cb_status status);
-void bna_port_cb_rx_stopped(struct bna_port *port,
- enum bna_cb_status status);
+void bna_mbox_handler(struct bna *bna, u32 intr_status);
/**
- * IB
+ * ETHPORT
*/
-/* APIs for BNA */
-void bna_ib_mod_init(struct bna_ib_mod *ib_mod, struct bna *bna,
- struct bna_res_info *res_info);
-void bna_ib_mod_uninit(struct bna_ib_mod *ib_mod);
+/* Callbacks for RX */
+void bna_ethport_cb_rx_started(struct bna_ethport *ethport);
+void bna_ethport_cb_rx_stopped(struct bna_ethport *ethport);
/**
* TX MODULE AND TX
*/
+/* FW response handelrs */
+void bna_bfi_tx_enet_start_rsp(struct bna_tx *tx,
+ struct bfi_msgq_mhdr *msghdr);
+void bna_bfi_tx_enet_stop_rsp(struct bna_tx *tx,
+ struct bfi_msgq_mhdr *msghdr);
+void bna_bfi_bw_update_aen(struct bna_tx_mod *tx_mod);
/* APIs for BNA */
void bna_tx_mod_init(struct bna_tx_mod *tx_mod, struct bna *bna,
struct bna_res_info *res_info);
void bna_tx_mod_uninit(struct bna_tx_mod *tx_mod);
-int bna_tx_state_get(struct bna_tx *tx);
-/* APIs for PORT */
+/* APIs for ENET */
void bna_tx_mod_start(struct bna_tx_mod *tx_mod, enum bna_tx_type type);
void bna_tx_mod_stop(struct bna_tx_mod *tx_mod, enum bna_tx_type type);
void bna_tx_mod_fail(struct bna_tx_mod *tx_mod);
-void bna_tx_mod_prio_changed(struct bna_tx_mod *tx_mod, int prio);
-void bna_tx_mod_cee_link_status(struct bna_tx_mod *tx_mod, int cee_link);
/* APIs for BNAD */
void bna_tx_res_req(int num_txq, int txq_depth,
struct bna_res_info *res_info);
struct bna_tx *bna_tx_create(struct bna *bna, struct bnad *bnad,
struct bna_tx_config *tx_cfg,
- struct bna_tx_event_cbfn *tx_cbfn,
+ const struct bna_tx_event_cbfn *tx_cbfn,
struct bna_res_info *res_info, void *priv);
void bna_tx_destroy(struct bna_tx *tx);
void bna_tx_enable(struct bna_tx *tx);
void bna_tx_disable(struct bna_tx *tx, enum bna_cleanup_type type,
- void (*cbfn)(void *, struct bna_tx *,
- enum bna_cb_status));
+ void (*cbfn)(void *, struct bna_tx *));
+void bna_tx_cleanup_complete(struct bna_tx *tx);
void bna_tx_coalescing_timeo_set(struct bna_tx *tx, int coalescing_timeo);
/**
* RX MODULE, RX, RXF
*/
-/* Internal APIs */
-void rxf_cb_cam_fltr_mbox_cmd(void *arg, int status);
-void rxf_cam_mbox_cmd(struct bna_rxf *rxf, u8 cmd,
- const struct bna_mac *mac_addr);
-void __rxf_vlan_filter_set(struct bna_rxf *rxf, enum bna_status status);
-void bna_rxf_adv_init(struct bna_rxf *rxf,
- struct bna_rx *rx,
- struct bna_rx_config *q_config);
-int rxf_process_packet_filter_ucast(struct bna_rxf *rxf);
-int rxf_process_packet_filter_promisc(struct bna_rxf *rxf);
-int rxf_process_packet_filter_default(struct bna_rxf *rxf);
-int rxf_process_packet_filter_allmulti(struct bna_rxf *rxf);
-int rxf_clear_packet_filter_ucast(struct bna_rxf *rxf);
-int rxf_clear_packet_filter_promisc(struct bna_rxf *rxf);
-int rxf_clear_packet_filter_default(struct bna_rxf *rxf);
-int rxf_clear_packet_filter_allmulti(struct bna_rxf *rxf);
-void rxf_reset_packet_filter_ucast(struct bna_rxf *rxf);
-void rxf_reset_packet_filter_promisc(struct bna_rxf *rxf);
-void rxf_reset_packet_filter_default(struct bna_rxf *rxf);
-void rxf_reset_packet_filter_allmulti(struct bna_rxf *rxf);
+/* FW response handlers */
+void bna_bfi_rx_enet_start_rsp(struct bna_rx *rx,
+ struct bfi_msgq_mhdr *msghdr);
+void bna_bfi_rx_enet_stop_rsp(struct bna_rx *rx,
+ struct bfi_msgq_mhdr *msghdr);
+void bna_bfi_rxf_cfg_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr);
+void bna_bfi_rxf_mcast_add_rsp(struct bna_rxf *rxf,
+ struct bfi_msgq_mhdr *msghdr);
/* APIs for BNA */
void bna_rx_mod_init(struct bna_rx_mod *rx_mod, struct bna *bna,
struct bna_res_info *res_info);
void bna_rx_mod_uninit(struct bna_rx_mod *rx_mod);
-int bna_rx_state_get(struct bna_rx *rx);
-int bna_rxf_state_get(struct bna_rxf *rxf);
-/* APIs for PORT */
+/* APIs for ENET */
void bna_rx_mod_start(struct bna_rx_mod *rx_mod, enum bna_rx_type type);
void bna_rx_mod_stop(struct bna_rx_mod *rx_mod, enum bna_rx_type type);
void bna_rx_mod_fail(struct bna_rx_mod *rx_mod);
@@ -490,59 +490,86 @@ void bna_rx_res_req(struct bna_rx_config *rx_config,
struct bna_res_info *res_info);
struct bna_rx *bna_rx_create(struct bna *bna, struct bnad *bnad,
struct bna_rx_config *rx_cfg,
- struct bna_rx_event_cbfn *rx_cbfn,
+ const struct bna_rx_event_cbfn *rx_cbfn,
struct bna_res_info *res_info, void *priv);
void bna_rx_destroy(struct bna_rx *rx);
void bna_rx_enable(struct bna_rx *rx);
void bna_rx_disable(struct bna_rx *rx, enum bna_cleanup_type type,
- void (*cbfn)(void *, struct bna_rx *,
- enum bna_cb_status));
+ void (*cbfn)(void *, struct bna_rx *));
+void bna_rx_cleanup_complete(struct bna_rx *rx);
void bna_rx_coalescing_timeo_set(struct bna_rx *rx, int coalescing_timeo);
void bna_rx_dim_reconfig(struct bna *bna, const u32 vector[][BNA_BIAS_T_MAX]);
void bna_rx_dim_update(struct bna_ccb *ccb);
enum bna_cb_status
bna_rx_ucast_set(struct bna_rx *rx, u8 *ucmac,
- void (*cbfn)(struct bnad *, struct bna_rx *,
- enum bna_cb_status));
+ void (*cbfn)(struct bnad *, struct bna_rx *));
+enum bna_cb_status
+bna_rx_ucast_add(struct bna_rx *rx, u8* ucmac,
+ void (*cbfn)(struct bnad *, struct bna_rx *));
+enum bna_cb_status
+bna_rx_ucast_del(struct bna_rx *rx, u8 *ucmac,
+ void (*cbfn)(struct bnad *, struct bna_rx *));
enum bna_cb_status
bna_rx_mcast_add(struct bna_rx *rx, u8 *mcmac,
- void (*cbfn)(struct bnad *, struct bna_rx *,
- enum bna_cb_status));
+ void (*cbfn)(struct bnad *, struct bna_rx *));
enum bna_cb_status
bna_rx_mcast_listset(struct bna_rx *rx, int count, u8 *mcmac,
- void (*cbfn)(struct bnad *, struct bna_rx *,
- enum bna_cb_status));
+ void (*cbfn)(struct bnad *, struct bna_rx *));
enum bna_cb_status
bna_rx_mode_set(struct bna_rx *rx, enum bna_rxmode rxmode,
enum bna_rxmode bitmask,
- void (*cbfn)(struct bnad *, struct bna_rx *,
- enum bna_cb_status));
+ void (*cbfn)(struct bnad *, struct bna_rx *));
void bna_rx_vlan_add(struct bna_rx *rx, int vlan_id);
void bna_rx_vlan_del(struct bna_rx *rx, int vlan_id);
void bna_rx_vlanfilter_enable(struct bna_rx *rx);
-void bna_rx_hds_enable(struct bna_rx *rx, struct bna_rxf_hds *hds_config,
- void (*cbfn)(struct bnad *, struct bna_rx *,
- enum bna_cb_status));
-void bna_rx_hds_disable(struct bna_rx *rx,
- void (*cbfn)(struct bnad *, struct bna_rx *,
- enum bna_cb_status));
+/**
+ * ENET
+ */
+
+/* API for RX */
+int bna_enet_mtu_get(struct bna_enet *enet);
+
+/* Callbacks for TX, RX */
+void bna_enet_cb_tx_stopped(struct bna_enet *enet);
+void bna_enet_cb_rx_stopped(struct bna_enet *enet);
+
+/* API for BNAD */
+void bna_enet_enable(struct bna_enet *enet);
+void bna_enet_disable(struct bna_enet *enet, enum bna_cleanup_type type,
+ void (*cbfn)(void *));
+void bna_enet_pause_config(struct bna_enet *enet,
+ struct bna_pause_config *pause_config,
+ void (*cbfn)(struct bnad *));
+void bna_enet_mtu_set(struct bna_enet *enet, int mtu,
+ void (*cbfn)(struct bnad *));
+void bna_enet_perm_mac_get(struct bna_enet *enet, mac_t *mac);
+
+/**
+ * IOCETH
+ */
+
+/* APIs for BNAD */
+void bna_ioceth_enable(struct bna_ioceth *ioceth);
+void bna_ioceth_disable(struct bna_ioceth *ioceth,
+ enum bna_cleanup_type type);
/**
* BNAD
*/
+/* Callbacks for ENET */
+void bnad_cb_ethport_link_status(struct bnad *bnad,
+ enum bna_link_status status);
+
+/* Callbacks for IOCETH */
+void bnad_cb_ioceth_ready(struct bnad *bnad);
+void bnad_cb_ioceth_failed(struct bnad *bnad);
+void bnad_cb_ioceth_disabled(struct bnad *bnad);
+void bnad_cb_mbox_intr_enable(struct bnad *bnad);
+void bnad_cb_mbox_intr_disable(struct bnad *bnad);
+
/* Callbacks for BNA */
void bnad_cb_stats_get(struct bnad *bnad, enum bna_cb_status status,
struct bna_stats *stats);
-/* Callbacks for DEVICE */
-void bnad_cb_device_enabled(struct bnad *bnad, enum bna_cb_status status);
-void bnad_cb_device_disabled(struct bnad *bnad, enum bna_cb_status status);
-void bnad_cb_device_enable_mbox_intr(struct bnad *bnad);
-void bnad_cb_device_disable_mbox_intr(struct bnad *bnad);
-
-/* Callbacks for port */
-void bnad_cb_port_link_status(struct bnad *bnad,
- enum bna_link_status status);
-
#endif /* __BNA_H__ */
diff --git a/drivers/net/ethernet/brocade/bna/bna_enet.c b/drivers/net/ethernet/brocade/bna/bna_enet.c
new file mode 100644
index 000000000000..26f5c5abfd1f
--- /dev/null
+++ b/drivers/net/ethernet/brocade/bna/bna_enet.c
@@ -0,0 +1,2144 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+#include "bna.h"
+
+static inline int
+ethport_can_be_up(struct bna_ethport *ethport)
+{
+ int ready = 0;
+ if (ethport->bna->enet.type == BNA_ENET_T_REGULAR)
+ ready = ((ethport->flags & BNA_ETHPORT_F_ADMIN_UP) &&
+ (ethport->flags & BNA_ETHPORT_F_RX_STARTED) &&
+ (ethport->flags & BNA_ETHPORT_F_PORT_ENABLED));
+ else
+ ready = ((ethport->flags & BNA_ETHPORT_F_ADMIN_UP) &&
+ (ethport->flags & BNA_ETHPORT_F_RX_STARTED) &&
+ !(ethport->flags & BNA_ETHPORT_F_PORT_ENABLED));
+ return ready;
+}
+
+#define ethport_is_up ethport_can_be_up
+
+enum bna_ethport_event {
+ ETHPORT_E_START = 1,
+ ETHPORT_E_STOP = 2,
+ ETHPORT_E_FAIL = 3,
+ ETHPORT_E_UP = 4,
+ ETHPORT_E_DOWN = 5,
+ ETHPORT_E_FWRESP_UP_OK = 6,
+ ETHPORT_E_FWRESP_DOWN = 7,
+ ETHPORT_E_FWRESP_UP_FAIL = 8,
+};
+
+enum bna_enet_event {
+ ENET_E_START = 1,
+ ENET_E_STOP = 2,
+ ENET_E_FAIL = 3,
+ ENET_E_PAUSE_CFG = 4,
+ ENET_E_MTU_CFG = 5,
+ ENET_E_FWRESP_PAUSE = 6,
+ ENET_E_CHLD_STOPPED = 7,
+};
+
+enum bna_ioceth_event {
+ IOCETH_E_ENABLE = 1,
+ IOCETH_E_DISABLE = 2,
+ IOCETH_E_IOC_RESET = 3,
+ IOCETH_E_IOC_FAILED = 4,
+ IOCETH_E_IOC_READY = 5,
+ IOCETH_E_ENET_ATTR_RESP = 6,
+ IOCETH_E_ENET_STOPPED = 7,
+ IOCETH_E_IOC_DISABLED = 8,
+};
+
+#define bna_stats_copy(_name, _type) \
+do { \
+ count = sizeof(struct bfi_enet_stats_ ## _type) / sizeof(u64); \
+ stats_src = (u64 *)&bna->stats.hw_stats_kva->_name ## _stats; \
+ stats_dst = (u64 *)&bna->stats.hw_stats._name ## _stats; \
+ for (i = 0; i < count; i++) \
+ stats_dst[i] = be64_to_cpu(stats_src[i]); \
+} while (0) \
+
+/*
+ * FW response handlers
+ */
+
+static void
+bna_bfi_ethport_enable_aen(struct bna_ethport *ethport,
+ struct bfi_msgq_mhdr *msghdr)
+{
+ ethport->flags |= BNA_ETHPORT_F_PORT_ENABLED;
+
+ if (ethport_can_be_up(ethport))
+ bfa_fsm_send_event(ethport, ETHPORT_E_UP);
+}
+
+static void
+bna_bfi_ethport_disable_aen(struct bna_ethport *ethport,
+ struct bfi_msgq_mhdr *msghdr)
+{
+ int ethport_up = ethport_is_up(ethport);
+
+ ethport->flags &= ~BNA_ETHPORT_F_PORT_ENABLED;
+
+ if (ethport_up)
+ bfa_fsm_send_event(ethport, ETHPORT_E_DOWN);
+}
+
+static void
+bna_bfi_ethport_admin_rsp(struct bna_ethport *ethport,
+ struct bfi_msgq_mhdr *msghdr)
+{
+ struct bfi_enet_enable_req *admin_req =
+ &ethport->bfi_enet_cmd.admin_req;
+ struct bfi_enet_rsp *rsp = (struct bfi_enet_rsp *)msghdr;
+
+ switch (admin_req->enable) {
+ case BNA_STATUS_T_ENABLED:
+ if (rsp->error == BFI_ENET_CMD_OK)
+ bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_UP_OK);
+ else {
+ ethport->flags &= ~BNA_ETHPORT_F_PORT_ENABLED;
+ bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_UP_FAIL);
+ }
+ break;
+
+ case BNA_STATUS_T_DISABLED:
+ bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_DOWN);
+ ethport->link_status = BNA_LINK_DOWN;
+ ethport->link_cbfn(ethport->bna->bnad, BNA_LINK_DOWN);
+ break;
+ }
+}
+
+static void
+bna_bfi_ethport_lpbk_rsp(struct bna_ethport *ethport,
+ struct bfi_msgq_mhdr *msghdr)
+{
+ struct bfi_enet_diag_lb_req *diag_lb_req =
+ &ethport->bfi_enet_cmd.lpbk_req;
+ struct bfi_enet_rsp *rsp = (struct bfi_enet_rsp *)msghdr;
+
+ switch (diag_lb_req->enable) {
+ case BNA_STATUS_T_ENABLED:
+ if (rsp->error == BFI_ENET_CMD_OK)
+ bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_UP_OK);
+ else {
+ ethport->flags &= ~BNA_ETHPORT_F_ADMIN_UP;
+ bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_UP_FAIL);
+ }
+ break;
+
+ case BNA_STATUS_T_DISABLED:
+ bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_DOWN);
+ break;
+ }
+}
+
+static void
+bna_bfi_pause_set_rsp(struct bna_enet *enet, struct bfi_msgq_mhdr *msghdr)
+{
+ bfa_fsm_send_event(enet, ENET_E_FWRESP_PAUSE);
+}
+
+static void
+bna_bfi_attr_get_rsp(struct bna_ioceth *ioceth,
+ struct bfi_msgq_mhdr *msghdr)
+{
+ struct bfi_enet_attr_rsp *rsp = (struct bfi_enet_attr_rsp *)msghdr;
+
+ /**
+ * Store only if not set earlier, since BNAD can override the HW
+ * attributes
+ */
+ if (!ioceth->attr.fw_query_complete) {
+ ioceth->attr.num_txq = ntohl(rsp->max_cfg);
+ ioceth->attr.num_rxp = ntohl(rsp->max_cfg);
+ ioceth->attr.num_ucmac = ntohl(rsp->max_ucmac);
+ ioceth->attr.num_mcmac = BFI_ENET_MAX_MCAM;
+ ioceth->attr.max_rit_size = ntohl(rsp->rit_size);
+ ioceth->attr.fw_query_complete = true;
+ }
+
+ bfa_fsm_send_event(ioceth, IOCETH_E_ENET_ATTR_RESP);
+}
+
+static void
+bna_bfi_stats_get_rsp(struct bna *bna, struct bfi_msgq_mhdr *msghdr)
+{
+ struct bfi_enet_stats_req *stats_req = &bna->stats_mod.stats_get;
+ u64 *stats_src;
+ u64 *stats_dst;
+ u32 tx_enet_mask = ntohl(stats_req->tx_enet_mask);
+ u32 rx_enet_mask = ntohl(stats_req->rx_enet_mask);
+ int count;
+ int i;
+
+ bna_stats_copy(mac, mac);
+ bna_stats_copy(bpc, bpc);
+ bna_stats_copy(rad, rad);
+ bna_stats_copy(rlb, rad);
+ bna_stats_copy(fc_rx, fc_rx);
+ bna_stats_copy(fc_tx, fc_tx);
+
+ stats_src = (u64 *)&(bna->stats.hw_stats_kva->rxf_stats[0]);
+
+ /* Copy Rxf stats to SW area, scatter them while copying */
+ for (i = 0; i < BFI_ENET_CFG_MAX; i++) {
+ stats_dst = (u64 *)&(bna->stats.hw_stats.rxf_stats[i]);
+ memset(stats_dst, 0, sizeof(struct bfi_enet_stats_rxf));
+ if (rx_enet_mask & ((u32)(1 << i))) {
+ int k;
+ count = sizeof(struct bfi_enet_stats_rxf) /
+ sizeof(u64);
+ for (k = 0; k < count; k++) {
+ stats_dst[k] = be64_to_cpu(*stats_src);
+ stats_src++;
+ }
+ }
+ }
+
+ /* Copy Txf stats to SW area, scatter them while copying */
+ for (i = 0; i < BFI_ENET_CFG_MAX; i++) {
+ stats_dst = (u64 *)&(bna->stats.hw_stats.txf_stats[i]);
+ memset(stats_dst, 0, sizeof(struct bfi_enet_stats_txf));
+ if (tx_enet_mask & ((u32)(1 << i))) {
+ int k;
+ count = sizeof(struct bfi_enet_stats_txf) /
+ sizeof(u64);
+ for (k = 0; k < count; k++) {
+ stats_dst[k] = be64_to_cpu(*stats_src);
+ stats_src++;
+ }
+ }
+ }
+
+ bna->stats_mod.stats_get_busy = false;
+ bnad_cb_stats_get(bna->bnad, BNA_CB_SUCCESS, &bna->stats);
+}
+
+static void
+bna_bfi_ethport_linkup_aen(struct bna_ethport *ethport,
+ struct bfi_msgq_mhdr *msghdr)
+{
+ ethport->link_status = BNA_LINK_UP;
+
+ /* Dispatch events */
+ ethport->link_cbfn(ethport->bna->bnad, ethport->link_status);
+}
+
+static void
+bna_bfi_ethport_linkdown_aen(struct bna_ethport *ethport,
+ struct bfi_msgq_mhdr *msghdr)
+{
+ ethport->link_status = BNA_LINK_DOWN;
+
+ /* Dispatch events */
+ ethport->link_cbfn(ethport->bna->bnad, BNA_LINK_DOWN);
+}
+
+static void
+bna_err_handler(struct bna *bna, u32 intr_status)
+{
+ if (BNA_IS_HALT_INTR(bna, intr_status))
+ bna_halt_clear(bna);
+
+ bfa_nw_ioc_error_isr(&bna->ioceth.ioc);
+}
+
+void
+bna_mbox_handler(struct bna *bna, u32 intr_status)
+{
+ if (BNA_IS_ERR_INTR(bna, intr_status)) {
+ bna_err_handler(bna, intr_status);
+ return;
+ }
+ if (BNA_IS_MBOX_INTR(bna, intr_status))
+ bfa_nw_ioc_mbox_isr(&bna->ioceth.ioc);
+}
+
+static void
+bna_msgq_rsp_handler(void *arg, struct bfi_msgq_mhdr *msghdr)
+{
+ struct bna *bna = (struct bna *)arg;
+ struct bna_tx *tx;
+ struct bna_rx *rx;
+
+ switch (msghdr->msg_id) {
+ case BFI_ENET_I2H_RX_CFG_SET_RSP:
+ bna_rx_from_rid(bna, msghdr->enet_id, rx);
+ if (rx)
+ bna_bfi_rx_enet_start_rsp(rx, msghdr);
+ break;
+
+ case BFI_ENET_I2H_RX_CFG_CLR_RSP:
+ bna_rx_from_rid(bna, msghdr->enet_id, rx);
+ if (rx)
+ bna_bfi_rx_enet_stop_rsp(rx, msghdr);
+ break;
+
+ case BFI_ENET_I2H_RIT_CFG_RSP:
+ case BFI_ENET_I2H_RSS_CFG_RSP:
+ case BFI_ENET_I2H_RSS_ENABLE_RSP:
+ case BFI_ENET_I2H_RX_PROMISCUOUS_RSP:
+ case BFI_ENET_I2H_RX_DEFAULT_RSP:
+ case BFI_ENET_I2H_MAC_UCAST_SET_RSP:
+ case BFI_ENET_I2H_MAC_UCAST_CLR_RSP:
+ case BFI_ENET_I2H_MAC_UCAST_ADD_RSP:
+ case BFI_ENET_I2H_MAC_UCAST_DEL_RSP:
+ case BFI_ENET_I2H_MAC_MCAST_DEL_RSP:
+ case BFI_ENET_I2H_MAC_MCAST_FILTER_RSP:
+ case BFI_ENET_I2H_RX_VLAN_SET_RSP:
+ case BFI_ENET_I2H_RX_VLAN_STRIP_ENABLE_RSP:
+ bna_rx_from_rid(bna, msghdr->enet_id, rx);
+ if (rx)
+ bna_bfi_rxf_cfg_rsp(&rx->rxf, msghdr);
+ break;
+
+ case BFI_ENET_I2H_MAC_MCAST_ADD_RSP:
+ bna_rx_from_rid(bna, msghdr->enet_id, rx);
+ if (rx)
+ bna_bfi_rxf_mcast_add_rsp(&rx->rxf, msghdr);
+ break;
+
+ case BFI_ENET_I2H_TX_CFG_SET_RSP:
+ bna_tx_from_rid(bna, msghdr->enet_id, tx);
+ if (tx)
+ bna_bfi_tx_enet_start_rsp(tx, msghdr);
+ break;
+
+ case BFI_ENET_I2H_TX_CFG_CLR_RSP:
+ bna_tx_from_rid(bna, msghdr->enet_id, tx);
+ if (tx)
+ bna_bfi_tx_enet_stop_rsp(tx, msghdr);
+ break;
+
+ case BFI_ENET_I2H_PORT_ADMIN_RSP:
+ bna_bfi_ethport_admin_rsp(&bna->ethport, msghdr);
+ break;
+
+ case BFI_ENET_I2H_DIAG_LOOPBACK_RSP:
+ bna_bfi_ethport_lpbk_rsp(&bna->ethport, msghdr);
+ break;
+
+ case BFI_ENET_I2H_SET_PAUSE_RSP:
+ bna_bfi_pause_set_rsp(&bna->enet, msghdr);
+ break;
+
+ case BFI_ENET_I2H_GET_ATTR_RSP:
+ bna_bfi_attr_get_rsp(&bna->ioceth, msghdr);
+ break;
+
+ case BFI_ENET_I2H_STATS_GET_RSP:
+ bna_bfi_stats_get_rsp(bna, msghdr);
+ break;
+
+ case BFI_ENET_I2H_STATS_CLR_RSP:
+ /* No-op */
+ break;
+
+ case BFI_ENET_I2H_LINK_UP_AEN:
+ bna_bfi_ethport_linkup_aen(&bna->ethport, msghdr);
+ break;
+
+ case BFI_ENET_I2H_LINK_DOWN_AEN:
+ bna_bfi_ethport_linkdown_aen(&bna->ethport, msghdr);
+ break;
+
+ case BFI_ENET_I2H_PORT_ENABLE_AEN:
+ bna_bfi_ethport_enable_aen(&bna->ethport, msghdr);
+ break;
+
+ case BFI_ENET_I2H_PORT_DISABLE_AEN:
+ bna_bfi_ethport_disable_aen(&bna->ethport, msghdr);
+ break;
+
+ case BFI_ENET_I2H_BW_UPDATE_AEN:
+ bna_bfi_bw_update_aen(&bna->tx_mod);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * ETHPORT
+ */
+#define call_ethport_stop_cbfn(_ethport) \
+do { \
+ if ((_ethport)->stop_cbfn) { \
+ void (*cbfn)(struct bna_enet *); \
+ cbfn = (_ethport)->stop_cbfn; \
+ (_ethport)->stop_cbfn = NULL; \
+ cbfn(&(_ethport)->bna->enet); \
+ } \
+} while (0)
+
+#define call_ethport_adminup_cbfn(ethport, status) \
+do { \
+ if ((ethport)->adminup_cbfn) { \
+ void (*cbfn)(struct bnad *, enum bna_cb_status); \
+ cbfn = (ethport)->adminup_cbfn; \
+ (ethport)->adminup_cbfn = NULL; \
+ cbfn((ethport)->bna->bnad, status); \
+ } \
+} while (0)
+
+static void
+bna_bfi_ethport_admin_up(struct bna_ethport *ethport)
+{
+ struct bfi_enet_enable_req *admin_up_req =
+ &ethport->bfi_enet_cmd.admin_req;
+
+ bfi_msgq_mhdr_set(admin_up_req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_PORT_ADMIN_UP_REQ, 0, 0);
+ admin_up_req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+ admin_up_req->enable = BNA_STATUS_T_ENABLED;
+
+ bfa_msgq_cmd_set(&ethport->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_enable_req), &admin_up_req->mh);
+ bfa_msgq_cmd_post(&ethport->bna->msgq, &ethport->msgq_cmd);
+}
+
+static void
+bna_bfi_ethport_admin_down(struct bna_ethport *ethport)
+{
+ struct bfi_enet_enable_req *admin_down_req =
+ &ethport->bfi_enet_cmd.admin_req;
+
+ bfi_msgq_mhdr_set(admin_down_req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_PORT_ADMIN_UP_REQ, 0, 0);
+ admin_down_req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+ admin_down_req->enable = BNA_STATUS_T_DISABLED;
+
+ bfa_msgq_cmd_set(&ethport->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_enable_req), &admin_down_req->mh);
+ bfa_msgq_cmd_post(&ethport->bna->msgq, &ethport->msgq_cmd);
+}
+
+static void
+bna_bfi_ethport_lpbk_up(struct bna_ethport *ethport)
+{
+ struct bfi_enet_diag_lb_req *lpbk_up_req =
+ &ethport->bfi_enet_cmd.lpbk_req;
+
+ bfi_msgq_mhdr_set(lpbk_up_req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_DIAG_LOOPBACK_REQ, 0, 0);
+ lpbk_up_req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_diag_lb_req)));
+ lpbk_up_req->mode = (ethport->bna->enet.type ==
+ BNA_ENET_T_LOOPBACK_INTERNAL) ?
+ BFI_ENET_DIAG_LB_OPMODE_EXT :
+ BFI_ENET_DIAG_LB_OPMODE_CBL;
+ lpbk_up_req->enable = BNA_STATUS_T_ENABLED;
+
+ bfa_msgq_cmd_set(&ethport->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_diag_lb_req), &lpbk_up_req->mh);
+ bfa_msgq_cmd_post(&ethport->bna->msgq, &ethport->msgq_cmd);
+}
+
+static void
+bna_bfi_ethport_lpbk_down(struct bna_ethport *ethport)
+{
+ struct bfi_enet_diag_lb_req *lpbk_down_req =
+ &ethport->bfi_enet_cmd.lpbk_req;
+
+ bfi_msgq_mhdr_set(lpbk_down_req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_DIAG_LOOPBACK_REQ, 0, 0);
+ lpbk_down_req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_diag_lb_req)));
+ lpbk_down_req->enable = BNA_STATUS_T_DISABLED;
+
+ bfa_msgq_cmd_set(&ethport->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_diag_lb_req), &lpbk_down_req->mh);
+ bfa_msgq_cmd_post(&ethport->bna->msgq, &ethport->msgq_cmd);
+}
+
+static void
+bna_bfi_ethport_up(struct bna_ethport *ethport)
+{
+ if (ethport->bna->enet.type == BNA_ENET_T_REGULAR)
+ bna_bfi_ethport_admin_up(ethport);
+ else
+ bna_bfi_ethport_lpbk_up(ethport);
+}
+
+static void
+bna_bfi_ethport_down(struct bna_ethport *ethport)
+{
+ if (ethport->bna->enet.type == BNA_ENET_T_REGULAR)
+ bna_bfi_ethport_admin_down(ethport);
+ else
+ bna_bfi_ethport_lpbk_down(ethport);
+}
+
+bfa_fsm_state_decl(bna_ethport, stopped, struct bna_ethport,
+ enum bna_ethport_event);
+bfa_fsm_state_decl(bna_ethport, down, struct bna_ethport,
+ enum bna_ethport_event);
+bfa_fsm_state_decl(bna_ethport, up_resp_wait, struct bna_ethport,
+ enum bna_ethport_event);
+bfa_fsm_state_decl(bna_ethport, down_resp_wait, struct bna_ethport,
+ enum bna_ethport_event);
+bfa_fsm_state_decl(bna_ethport, up, struct bna_ethport,
+ enum bna_ethport_event);
+bfa_fsm_state_decl(bna_ethport, last_resp_wait, struct bna_ethport,
+ enum bna_ethport_event);
+
+static void
+bna_ethport_sm_stopped_entry(struct bna_ethport *ethport)
+{
+ call_ethport_stop_cbfn(ethport);
+}
+
+static void
+bna_ethport_sm_stopped(struct bna_ethport *ethport,
+ enum bna_ethport_event event)
+{
+ switch (event) {
+ case ETHPORT_E_START:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_down);
+ break;
+
+ case ETHPORT_E_STOP:
+ call_ethport_stop_cbfn(ethport);
+ break;
+
+ case ETHPORT_E_FAIL:
+ /* No-op */
+ break;
+
+ case ETHPORT_E_DOWN:
+ /* This event is received due to Rx objects failing */
+ /* No-op */
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ethport_sm_down_entry(struct bna_ethport *ethport)
+{
+}
+
+static void
+bna_ethport_sm_down(struct bna_ethport *ethport,
+ enum bna_ethport_event event)
+{
+ switch (event) {
+ case ETHPORT_E_STOP:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+ break;
+
+ case ETHPORT_E_FAIL:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+ break;
+
+ case ETHPORT_E_UP:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_up_resp_wait);
+ bna_bfi_ethport_up(ethport);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ethport_sm_up_resp_wait_entry(struct bna_ethport *ethport)
+{
+}
+
+static void
+bna_ethport_sm_up_resp_wait(struct bna_ethport *ethport,
+ enum bna_ethport_event event)
+{
+ switch (event) {
+ case ETHPORT_E_STOP:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_last_resp_wait);
+ break;
+
+ case ETHPORT_E_FAIL:
+ call_ethport_adminup_cbfn(ethport, BNA_CB_FAIL);
+ bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+ break;
+
+ case ETHPORT_E_DOWN:
+ call_ethport_adminup_cbfn(ethport, BNA_CB_INTERRUPT);
+ bfa_fsm_set_state(ethport, bna_ethport_sm_down_resp_wait);
+ break;
+
+ case ETHPORT_E_FWRESP_UP_OK:
+ call_ethport_adminup_cbfn(ethport, BNA_CB_SUCCESS);
+ bfa_fsm_set_state(ethport, bna_ethport_sm_up);
+ break;
+
+ case ETHPORT_E_FWRESP_UP_FAIL:
+ call_ethport_adminup_cbfn(ethport, BNA_CB_FAIL);
+ bfa_fsm_set_state(ethport, bna_ethport_sm_down);
+ break;
+
+ case ETHPORT_E_FWRESP_DOWN:
+ /* down_resp_wait -> up_resp_wait transition on ETHPORT_E_UP */
+ bna_bfi_ethport_up(ethport);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ethport_sm_down_resp_wait_entry(struct bna_ethport *ethport)
+{
+ /**
+ * NOTE: Do not call bna_bfi_ethport_down() here. That will over step
+ * mbox due to up_resp_wait -> down_resp_wait transition on event
+ * ETHPORT_E_DOWN
+ */
+}
+
+static void
+bna_ethport_sm_down_resp_wait(struct bna_ethport *ethport,
+ enum bna_ethport_event event)
+{
+ switch (event) {
+ case ETHPORT_E_STOP:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_last_resp_wait);
+ break;
+
+ case ETHPORT_E_FAIL:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+ break;
+
+ case ETHPORT_E_UP:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_up_resp_wait);
+ break;
+
+ case ETHPORT_E_FWRESP_UP_OK:
+ /* up_resp_wait->down_resp_wait transition on ETHPORT_E_DOWN */
+ bna_bfi_ethport_down(ethport);
+ break;
+
+ case ETHPORT_E_FWRESP_UP_FAIL:
+ case ETHPORT_E_FWRESP_DOWN:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_down);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ethport_sm_up_entry(struct bna_ethport *ethport)
+{
+}
+
+static void
+bna_ethport_sm_up(struct bna_ethport *ethport,
+ enum bna_ethport_event event)
+{
+ switch (event) {
+ case ETHPORT_E_STOP:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_last_resp_wait);
+ bna_bfi_ethport_down(ethport);
+ break;
+
+ case ETHPORT_E_FAIL:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+ break;
+
+ case ETHPORT_E_DOWN:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_down_resp_wait);
+ bna_bfi_ethport_down(ethport);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ethport_sm_last_resp_wait_entry(struct bna_ethport *ethport)
+{
+}
+
+static void
+bna_ethport_sm_last_resp_wait(struct bna_ethport *ethport,
+ enum bna_ethport_event event)
+{
+ switch (event) {
+ case ETHPORT_E_FAIL:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+ break;
+
+ case ETHPORT_E_DOWN:
+ /**
+ * This event is received due to Rx objects stopping in
+ * parallel to ethport
+ */
+ /* No-op */
+ break;
+
+ case ETHPORT_E_FWRESP_UP_OK:
+ /* up_resp_wait->last_resp_wait transition on ETHPORT_T_STOP */
+ bna_bfi_ethport_down(ethport);
+ break;
+
+ case ETHPORT_E_FWRESP_UP_FAIL:
+ case ETHPORT_E_FWRESP_DOWN:
+ bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ethport_init(struct bna_ethport *ethport, struct bna *bna)
+{
+ ethport->flags |= (BNA_ETHPORT_F_ADMIN_UP | BNA_ETHPORT_F_PORT_ENABLED);
+ ethport->bna = bna;
+
+ ethport->link_status = BNA_LINK_DOWN;
+ ethport->link_cbfn = bnad_cb_ethport_link_status;
+
+ ethport->rx_started_count = 0;
+
+ ethport->stop_cbfn = NULL;
+ ethport->adminup_cbfn = NULL;
+
+ bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+}
+
+static void
+bna_ethport_uninit(struct bna_ethport *ethport)
+{
+ ethport->flags &= ~BNA_ETHPORT_F_ADMIN_UP;
+ ethport->flags &= ~BNA_ETHPORT_F_PORT_ENABLED;
+
+ ethport->bna = NULL;
+}
+
+static void
+bna_ethport_start(struct bna_ethport *ethport)
+{
+ bfa_fsm_send_event(ethport, ETHPORT_E_START);
+}
+
+static void
+bna_enet_cb_ethport_stopped(struct bna_enet *enet)
+{
+ bfa_wc_down(&enet->chld_stop_wc);
+}
+
+static void
+bna_ethport_stop(struct bna_ethport *ethport)
+{
+ ethport->stop_cbfn = bna_enet_cb_ethport_stopped;
+ bfa_fsm_send_event(ethport, ETHPORT_E_STOP);
+}
+
+static void
+bna_ethport_fail(struct bna_ethport *ethport)
+{
+ /* Reset the physical port status to enabled */
+ ethport->flags |= BNA_ETHPORT_F_PORT_ENABLED;
+
+ if (ethport->link_status != BNA_LINK_DOWN) {
+ ethport->link_status = BNA_LINK_DOWN;
+ ethport->link_cbfn(ethport->bna->bnad, BNA_LINK_DOWN);
+ }
+ bfa_fsm_send_event(ethport, ETHPORT_E_FAIL);
+}
+
+/* Should be called only when ethport is disabled */
+void
+bna_ethport_cb_rx_started(struct bna_ethport *ethport)
+{
+ ethport->rx_started_count++;
+
+ if (ethport->rx_started_count == 1) {
+ ethport->flags |= BNA_ETHPORT_F_RX_STARTED;
+
+ if (ethport_can_be_up(ethport))
+ bfa_fsm_send_event(ethport, ETHPORT_E_UP);
+ }
+}
+
+void
+bna_ethport_cb_rx_stopped(struct bna_ethport *ethport)
+{
+ int ethport_up = ethport_is_up(ethport);
+
+ ethport->rx_started_count--;
+
+ if (ethport->rx_started_count == 0) {
+ ethport->flags &= ~BNA_ETHPORT_F_RX_STARTED;
+
+ if (ethport_up)
+ bfa_fsm_send_event(ethport, ETHPORT_E_DOWN);
+ }
+}
+
+/**
+ * ENET
+ */
+#define bna_enet_chld_start(enet) \
+do { \
+ enum bna_tx_type tx_type = \
+ ((enet)->type == BNA_ENET_T_REGULAR) ? \
+ BNA_TX_T_REGULAR : BNA_TX_T_LOOPBACK; \
+ enum bna_rx_type rx_type = \
+ ((enet)->type == BNA_ENET_T_REGULAR) ? \
+ BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK; \
+ bna_ethport_start(&(enet)->bna->ethport); \
+ bna_tx_mod_start(&(enet)->bna->tx_mod, tx_type); \
+ bna_rx_mod_start(&(enet)->bna->rx_mod, rx_type); \
+} while (0)
+
+#define bna_enet_chld_stop(enet) \
+do { \
+ enum bna_tx_type tx_type = \
+ ((enet)->type == BNA_ENET_T_REGULAR) ? \
+ BNA_TX_T_REGULAR : BNA_TX_T_LOOPBACK; \
+ enum bna_rx_type rx_type = \
+ ((enet)->type == BNA_ENET_T_REGULAR) ? \
+ BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK; \
+ bfa_wc_init(&(enet)->chld_stop_wc, bna_enet_cb_chld_stopped, (enet));\
+ bfa_wc_up(&(enet)->chld_stop_wc); \
+ bna_ethport_stop(&(enet)->bna->ethport); \
+ bfa_wc_up(&(enet)->chld_stop_wc); \
+ bna_tx_mod_stop(&(enet)->bna->tx_mod, tx_type); \
+ bfa_wc_up(&(enet)->chld_stop_wc); \
+ bna_rx_mod_stop(&(enet)->bna->rx_mod, rx_type); \
+ bfa_wc_wait(&(enet)->chld_stop_wc); \
+} while (0)
+
+#define bna_enet_chld_fail(enet) \
+do { \
+ bna_ethport_fail(&(enet)->bna->ethport); \
+ bna_tx_mod_fail(&(enet)->bna->tx_mod); \
+ bna_rx_mod_fail(&(enet)->bna->rx_mod); \
+} while (0)
+
+#define bna_enet_rx_start(enet) \
+do { \
+ enum bna_rx_type rx_type = \
+ ((enet)->type == BNA_ENET_T_REGULAR) ? \
+ BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK; \
+ bna_rx_mod_start(&(enet)->bna->rx_mod, rx_type); \
+} while (0)
+
+#define bna_enet_rx_stop(enet) \
+do { \
+ enum bna_rx_type rx_type = \
+ ((enet)->type == BNA_ENET_T_REGULAR) ? \
+ BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK; \
+ bfa_wc_init(&(enet)->chld_stop_wc, bna_enet_cb_chld_stopped, (enet));\
+ bfa_wc_up(&(enet)->chld_stop_wc); \
+ bna_rx_mod_stop(&(enet)->bna->rx_mod, rx_type); \
+ bfa_wc_wait(&(enet)->chld_stop_wc); \
+} while (0)
+
+#define call_enet_stop_cbfn(enet) \
+do { \
+ if ((enet)->stop_cbfn) { \
+ void (*cbfn)(void *); \
+ void *cbarg; \
+ cbfn = (enet)->stop_cbfn; \
+ cbarg = (enet)->stop_cbarg; \
+ (enet)->stop_cbfn = NULL; \
+ (enet)->stop_cbarg = NULL; \
+ cbfn(cbarg); \
+ } \
+} while (0)
+
+#define call_enet_pause_cbfn(enet) \
+do { \
+ if ((enet)->pause_cbfn) { \
+ void (*cbfn)(struct bnad *); \
+ cbfn = (enet)->pause_cbfn; \
+ (enet)->pause_cbfn = NULL; \
+ cbfn((enet)->bna->bnad); \
+ } \
+} while (0)
+
+#define call_enet_mtu_cbfn(enet) \
+do { \
+ if ((enet)->mtu_cbfn) { \
+ void (*cbfn)(struct bnad *); \
+ cbfn = (enet)->mtu_cbfn; \
+ (enet)->mtu_cbfn = NULL; \
+ cbfn((enet)->bna->bnad); \
+ } \
+} while (0)
+
+static void bna_enet_cb_chld_stopped(void *arg);
+static void bna_bfi_pause_set(struct bna_enet *enet);
+
+bfa_fsm_state_decl(bna_enet, stopped, struct bna_enet,
+ enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, pause_init_wait, struct bna_enet,
+ enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, last_resp_wait, struct bna_enet,
+ enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, started, struct bna_enet,
+ enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, cfg_wait, struct bna_enet,
+ enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, cfg_stop_wait, struct bna_enet,
+ enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, chld_stop_wait, struct bna_enet,
+ enum bna_enet_event);
+
+static void
+bna_enet_sm_stopped_entry(struct bna_enet *enet)
+{
+ call_enet_pause_cbfn(enet);
+ call_enet_mtu_cbfn(enet);
+ call_enet_stop_cbfn(enet);
+}
+
+static void
+bna_enet_sm_stopped(struct bna_enet *enet, enum bna_enet_event event)
+{
+ switch (event) {
+ case ENET_E_START:
+ bfa_fsm_set_state(enet, bna_enet_sm_pause_init_wait);
+ break;
+
+ case ENET_E_STOP:
+ call_enet_stop_cbfn(enet);
+ break;
+
+ case ENET_E_FAIL:
+ /* No-op */
+ break;
+
+ case ENET_E_PAUSE_CFG:
+ call_enet_pause_cbfn(enet);
+ break;
+
+ case ENET_E_MTU_CFG:
+ call_enet_mtu_cbfn(enet);
+ break;
+
+ case ENET_E_CHLD_STOPPED:
+ /**
+ * This event is received due to Ethport, Tx and Rx objects
+ * failing
+ */
+ /* No-op */
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_enet_sm_pause_init_wait_entry(struct bna_enet *enet)
+{
+ bna_bfi_pause_set(enet);
+}
+
+static void
+bna_enet_sm_pause_init_wait(struct bna_enet *enet,
+ enum bna_enet_event event)
+{
+ switch (event) {
+ case ENET_E_STOP:
+ enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+ bfa_fsm_set_state(enet, bna_enet_sm_last_resp_wait);
+ break;
+
+ case ENET_E_FAIL:
+ enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+ bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+ break;
+
+ case ENET_E_PAUSE_CFG:
+ enet->flags |= BNA_ENET_F_PAUSE_CHANGED;
+ break;
+
+ case ENET_E_MTU_CFG:
+ /* No-op */
+ break;
+
+ case ENET_E_FWRESP_PAUSE:
+ if (enet->flags & BNA_ENET_F_PAUSE_CHANGED) {
+ enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+ bna_bfi_pause_set(enet);
+ } else {
+ bfa_fsm_set_state(enet, bna_enet_sm_started);
+ bna_enet_chld_start(enet);
+ }
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_enet_sm_last_resp_wait_entry(struct bna_enet *enet)
+{
+ enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+}
+
+static void
+bna_enet_sm_last_resp_wait(struct bna_enet *enet,
+ enum bna_enet_event event)
+{
+ switch (event) {
+ case ENET_E_FAIL:
+ case ENET_E_FWRESP_PAUSE:
+ bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_enet_sm_started_entry(struct bna_enet *enet)
+{
+ /**
+ * NOTE: Do not call bna_enet_chld_start() here, since it will be
+ * inadvertently called during cfg_wait->started transition as well
+ */
+ call_enet_pause_cbfn(enet);
+ call_enet_mtu_cbfn(enet);
+}
+
+static void
+bna_enet_sm_started(struct bna_enet *enet,
+ enum bna_enet_event event)
+{
+ switch (event) {
+ case ENET_E_STOP:
+ bfa_fsm_set_state(enet, bna_enet_sm_chld_stop_wait);
+ break;
+
+ case ENET_E_FAIL:
+ bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+ bna_enet_chld_fail(enet);
+ break;
+
+ case ENET_E_PAUSE_CFG:
+ bfa_fsm_set_state(enet, bna_enet_sm_cfg_wait);
+ bna_bfi_pause_set(enet);
+ break;
+
+ case ENET_E_MTU_CFG:
+ bfa_fsm_set_state(enet, bna_enet_sm_cfg_wait);
+ bna_enet_rx_stop(enet);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_enet_sm_cfg_wait_entry(struct bna_enet *enet)
+{
+}
+
+static void
+bna_enet_sm_cfg_wait(struct bna_enet *enet,
+ enum bna_enet_event event)
+{
+ switch (event) {
+ case ENET_E_STOP:
+ enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+ enet->flags &= ~BNA_ENET_F_MTU_CHANGED;
+ bfa_fsm_set_state(enet, bna_enet_sm_cfg_stop_wait);
+ break;
+
+ case ENET_E_FAIL:
+ enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+ enet->flags &= ~BNA_ENET_F_MTU_CHANGED;
+ bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+ bna_enet_chld_fail(enet);
+ break;
+
+ case ENET_E_PAUSE_CFG:
+ enet->flags |= BNA_ENET_F_PAUSE_CHANGED;
+ break;
+
+ case ENET_E_MTU_CFG:
+ enet->flags |= BNA_ENET_F_MTU_CHANGED;
+ break;
+
+ case ENET_E_CHLD_STOPPED:
+ bna_enet_rx_start(enet);
+ /* Fall through */
+ case ENET_E_FWRESP_PAUSE:
+ if (enet->flags & BNA_ENET_F_PAUSE_CHANGED) {
+ enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+ bna_bfi_pause_set(enet);
+ } else if (enet->flags & BNA_ENET_F_MTU_CHANGED) {
+ enet->flags &= ~BNA_ENET_F_MTU_CHANGED;
+ bna_enet_rx_stop(enet);
+ } else {
+ bfa_fsm_set_state(enet, bna_enet_sm_started);
+ }
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_enet_sm_cfg_stop_wait_entry(struct bna_enet *enet)
+{
+ enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+ enet->flags &= ~BNA_ENET_F_MTU_CHANGED;
+}
+
+static void
+bna_enet_sm_cfg_stop_wait(struct bna_enet *enet,
+ enum bna_enet_event event)
+{
+ switch (event) {
+ case ENET_E_FAIL:
+ bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+ bna_enet_chld_fail(enet);
+ break;
+
+ case ENET_E_FWRESP_PAUSE:
+ case ENET_E_CHLD_STOPPED:
+ bfa_fsm_set_state(enet, bna_enet_sm_chld_stop_wait);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_enet_sm_chld_stop_wait_entry(struct bna_enet *enet)
+{
+ bna_enet_chld_stop(enet);
+}
+
+static void
+bna_enet_sm_chld_stop_wait(struct bna_enet *enet,
+ enum bna_enet_event event)
+{
+ switch (event) {
+ case ENET_E_FAIL:
+ bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+ bna_enet_chld_fail(enet);
+ break;
+
+ case ENET_E_CHLD_STOPPED:
+ bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_bfi_pause_set(struct bna_enet *enet)
+{
+ struct bfi_enet_set_pause_req *pause_req = &enet->pause_req;
+
+ bfi_msgq_mhdr_set(pause_req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_SET_PAUSE_REQ, 0, 0);
+ pause_req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_set_pause_req)));
+ pause_req->tx_pause = enet->pause_config.tx_pause;
+ pause_req->rx_pause = enet->pause_config.rx_pause;
+
+ bfa_msgq_cmd_set(&enet->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_set_pause_req), &pause_req->mh);
+ bfa_msgq_cmd_post(&enet->bna->msgq, &enet->msgq_cmd);
+}
+
+static void
+bna_enet_cb_chld_stopped(void *arg)
+{
+ struct bna_enet *enet = (struct bna_enet *)arg;
+
+ bfa_fsm_send_event(enet, ENET_E_CHLD_STOPPED);
+}
+
+static void
+bna_enet_init(struct bna_enet *enet, struct bna *bna)
+{
+ enet->bna = bna;
+ enet->flags = 0;
+ enet->mtu = 0;
+ enet->type = BNA_ENET_T_REGULAR;
+
+ enet->stop_cbfn = NULL;
+ enet->stop_cbarg = NULL;
+
+ enet->pause_cbfn = NULL;
+
+ enet->mtu_cbfn = NULL;
+
+ bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+}
+
+static void
+bna_enet_uninit(struct bna_enet *enet)
+{
+ enet->flags = 0;
+
+ enet->bna = NULL;
+}
+
+static void
+bna_enet_start(struct bna_enet *enet)
+{
+ enet->flags |= BNA_ENET_F_IOCETH_READY;
+ if (enet->flags & BNA_ENET_F_ENABLED)
+ bfa_fsm_send_event(enet, ENET_E_START);
+}
+
+static void
+bna_ioceth_cb_enet_stopped(void *arg)
+{
+ struct bna_ioceth *ioceth = (struct bna_ioceth *)arg;
+
+ bfa_fsm_send_event(ioceth, IOCETH_E_ENET_STOPPED);
+}
+
+static void
+bna_enet_stop(struct bna_enet *enet)
+{
+ enet->stop_cbfn = bna_ioceth_cb_enet_stopped;
+ enet->stop_cbarg = &enet->bna->ioceth;
+
+ enet->flags &= ~BNA_ENET_F_IOCETH_READY;
+ bfa_fsm_send_event(enet, ENET_E_STOP);
+}
+
+static void
+bna_enet_fail(struct bna_enet *enet)
+{
+ enet->flags &= ~BNA_ENET_F_IOCETH_READY;
+ bfa_fsm_send_event(enet, ENET_E_FAIL);
+}
+
+void
+bna_enet_cb_tx_stopped(struct bna_enet *enet)
+{
+ bfa_wc_down(&enet->chld_stop_wc);
+}
+
+void
+bna_enet_cb_rx_stopped(struct bna_enet *enet)
+{
+ bfa_wc_down(&enet->chld_stop_wc);
+}
+
+int
+bna_enet_mtu_get(struct bna_enet *enet)
+{
+ return enet->mtu;
+}
+
+void
+bna_enet_enable(struct bna_enet *enet)
+{
+ if (enet->fsm != (bfa_sm_t)bna_enet_sm_stopped)
+ return;
+
+ enet->flags |= BNA_ENET_F_ENABLED;
+
+ if (enet->flags & BNA_ENET_F_IOCETH_READY)
+ bfa_fsm_send_event(enet, ENET_E_START);
+}
+
+void
+bna_enet_disable(struct bna_enet *enet, enum bna_cleanup_type type,
+ void (*cbfn)(void *))
+{
+ if (type == BNA_SOFT_CLEANUP) {
+ (*cbfn)(enet->bna->bnad);
+ return;
+ }
+
+ enet->stop_cbfn = cbfn;
+ enet->stop_cbarg = enet->bna->bnad;
+
+ enet->flags &= ~BNA_ENET_F_ENABLED;
+
+ bfa_fsm_send_event(enet, ENET_E_STOP);
+}
+
+void
+bna_enet_pause_config(struct bna_enet *enet,
+ struct bna_pause_config *pause_config,
+ void (*cbfn)(struct bnad *))
+{
+ enet->pause_config = *pause_config;
+
+ enet->pause_cbfn = cbfn;
+
+ bfa_fsm_send_event(enet, ENET_E_PAUSE_CFG);
+}
+
+void
+bna_enet_mtu_set(struct bna_enet *enet, int mtu,
+ void (*cbfn)(struct bnad *))
+{
+ enet->mtu = mtu;
+
+ enet->mtu_cbfn = cbfn;
+
+ bfa_fsm_send_event(enet, ENET_E_MTU_CFG);
+}
+
+void
+bna_enet_perm_mac_get(struct bna_enet *enet, mac_t *mac)
+{
+ *mac = bfa_nw_ioc_get_mac(&enet->bna->ioceth.ioc);
+}
+
+/**
+ * IOCETH
+ */
+#define enable_mbox_intr(_ioceth) \
+do { \
+ u32 intr_status; \
+ bna_intr_status_get((_ioceth)->bna, intr_status); \
+ bnad_cb_mbox_intr_enable((_ioceth)->bna->bnad); \
+ bna_mbox_intr_enable((_ioceth)->bna); \
+} while (0)
+
+#define disable_mbox_intr(_ioceth) \
+do { \
+ bna_mbox_intr_disable((_ioceth)->bna); \
+ bnad_cb_mbox_intr_disable((_ioceth)->bna->bnad); \
+} while (0)
+
+#define call_ioceth_stop_cbfn(_ioceth) \
+do { \
+ if ((_ioceth)->stop_cbfn) { \
+ void (*cbfn)(struct bnad *); \
+ struct bnad *cbarg; \
+ cbfn = (_ioceth)->stop_cbfn; \
+ cbarg = (_ioceth)->stop_cbarg; \
+ (_ioceth)->stop_cbfn = NULL; \
+ (_ioceth)->stop_cbarg = NULL; \
+ cbfn(cbarg); \
+ } \
+} while (0)
+
+#define bna_stats_mod_uninit(_stats_mod) \
+do { \
+} while (0)
+
+#define bna_stats_mod_start(_stats_mod) \
+do { \
+ (_stats_mod)->ioc_ready = true; \
+} while (0)
+
+#define bna_stats_mod_stop(_stats_mod) \
+do { \
+ (_stats_mod)->ioc_ready = false; \
+} while (0)
+
+#define bna_stats_mod_fail(_stats_mod) \
+do { \
+ (_stats_mod)->ioc_ready = false; \
+ (_stats_mod)->stats_get_busy = false; \
+ (_stats_mod)->stats_clr_busy = false; \
+} while (0)
+
+static void bna_bfi_attr_get(struct bna_ioceth *ioceth);
+
+bfa_fsm_state_decl(bna_ioceth, stopped, struct bna_ioceth,
+ enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, ioc_ready_wait, struct bna_ioceth,
+ enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, enet_attr_wait, struct bna_ioceth,
+ enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, ready, struct bna_ioceth,
+ enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, last_resp_wait, struct bna_ioceth,
+ enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, enet_stop_wait, struct bna_ioceth,
+ enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, ioc_disable_wait, struct bna_ioceth,
+ enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, failed, struct bna_ioceth,
+ enum bna_ioceth_event);
+
+static void
+bna_ioceth_sm_stopped_entry(struct bna_ioceth *ioceth)
+{
+ call_ioceth_stop_cbfn(ioceth);
+}
+
+static void
+bna_ioceth_sm_stopped(struct bna_ioceth *ioceth,
+ enum bna_ioceth_event event)
+{
+ switch (event) {
+ case IOCETH_E_ENABLE:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_ready_wait);
+ bfa_nw_ioc_enable(&ioceth->ioc);
+ break;
+
+ case IOCETH_E_DISABLE:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_stopped);
+ break;
+
+ case IOCETH_E_IOC_RESET:
+ enable_mbox_intr(ioceth);
+ break;
+
+ case IOCETH_E_IOC_FAILED:
+ disable_mbox_intr(ioceth);
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_failed);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ioceth_sm_ioc_ready_wait_entry(struct bna_ioceth *ioceth)
+{
+ /**
+ * Do not call bfa_nw_ioc_enable() here. It must be called in the
+ * previous state due to failed -> ioc_ready_wait transition.
+ */
+}
+
+static void
+bna_ioceth_sm_ioc_ready_wait(struct bna_ioceth *ioceth,
+ enum bna_ioceth_event event)
+{
+ switch (event) {
+ case IOCETH_E_DISABLE:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+ bfa_nw_ioc_disable(&ioceth->ioc);
+ break;
+
+ case IOCETH_E_IOC_RESET:
+ enable_mbox_intr(ioceth);
+ break;
+
+ case IOCETH_E_IOC_FAILED:
+ disable_mbox_intr(ioceth);
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_failed);
+ break;
+
+ case IOCETH_E_IOC_READY:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_enet_attr_wait);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ioceth_sm_enet_attr_wait_entry(struct bna_ioceth *ioceth)
+{
+ bna_bfi_attr_get(ioceth);
+}
+
+static void
+bna_ioceth_sm_enet_attr_wait(struct bna_ioceth *ioceth,
+ enum bna_ioceth_event event)
+{
+ switch (event) {
+ case IOCETH_E_DISABLE:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_last_resp_wait);
+ break;
+
+ case IOCETH_E_IOC_FAILED:
+ disable_mbox_intr(ioceth);
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_failed);
+ break;
+
+ case IOCETH_E_ENET_ATTR_RESP:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_ready);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ioceth_sm_ready_entry(struct bna_ioceth *ioceth)
+{
+ bna_enet_start(&ioceth->bna->enet);
+ bna_stats_mod_start(&ioceth->bna->stats_mod);
+ bnad_cb_ioceth_ready(ioceth->bna->bnad);
+}
+
+static void
+bna_ioceth_sm_ready(struct bna_ioceth *ioceth, enum bna_ioceth_event event)
+{
+ switch (event) {
+ case IOCETH_E_DISABLE:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_enet_stop_wait);
+ break;
+
+ case IOCETH_E_IOC_FAILED:
+ disable_mbox_intr(ioceth);
+ bna_enet_fail(&ioceth->bna->enet);
+ bna_stats_mod_fail(&ioceth->bna->stats_mod);
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_failed);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ioceth_sm_last_resp_wait_entry(struct bna_ioceth *ioceth)
+{
+}
+
+static void
+bna_ioceth_sm_last_resp_wait(struct bna_ioceth *ioceth,
+ enum bna_ioceth_event event)
+{
+ switch (event) {
+ case IOCETH_E_IOC_FAILED:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+ disable_mbox_intr(ioceth);
+ bfa_nw_ioc_disable(&ioceth->ioc);
+ break;
+
+ case IOCETH_E_ENET_ATTR_RESP:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+ bfa_nw_ioc_disable(&ioceth->ioc);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ioceth_sm_enet_stop_wait_entry(struct bna_ioceth *ioceth)
+{
+ bna_stats_mod_stop(&ioceth->bna->stats_mod);
+ bna_enet_stop(&ioceth->bna->enet);
+}
+
+static void
+bna_ioceth_sm_enet_stop_wait(struct bna_ioceth *ioceth,
+ enum bna_ioceth_event event)
+{
+ switch (event) {
+ case IOCETH_E_IOC_FAILED:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+ disable_mbox_intr(ioceth);
+ bna_enet_fail(&ioceth->bna->enet);
+ bna_stats_mod_fail(&ioceth->bna->stats_mod);
+ bfa_nw_ioc_disable(&ioceth->ioc);
+ break;
+
+ case IOCETH_E_ENET_STOPPED:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+ bfa_nw_ioc_disable(&ioceth->ioc);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ioceth_sm_ioc_disable_wait_entry(struct bna_ioceth *ioceth)
+{
+}
+
+static void
+bna_ioceth_sm_ioc_disable_wait(struct bna_ioceth *ioceth,
+ enum bna_ioceth_event event)
+{
+ switch (event) {
+ case IOCETH_E_IOC_DISABLED:
+ disable_mbox_intr(ioceth);
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_stopped);
+ break;
+
+ case IOCETH_E_ENET_STOPPED:
+ /* This event is received due to enet failing */
+ /* No-op */
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_ioceth_sm_failed_entry(struct bna_ioceth *ioceth)
+{
+ bnad_cb_ioceth_failed(ioceth->bna->bnad);
+}
+
+static void
+bna_ioceth_sm_failed(struct bna_ioceth *ioceth,
+ enum bna_ioceth_event event)
+{
+ switch (event) {
+ case IOCETH_E_DISABLE:
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+ bfa_nw_ioc_disable(&ioceth->ioc);
+ break;
+
+ case IOCETH_E_IOC_RESET:
+ enable_mbox_intr(ioceth);
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_ready_wait);
+ break;
+
+ case IOCETH_E_IOC_FAILED:
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_bfi_attr_get(struct bna_ioceth *ioceth)
+{
+ struct bfi_enet_attr_req *attr_req = &ioceth->attr_req;
+
+ bfi_msgq_mhdr_set(attr_req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_GET_ATTR_REQ, 0, 0);
+ attr_req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_attr_req)));
+ bfa_msgq_cmd_set(&ioceth->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_attr_req), &attr_req->mh);
+ bfa_msgq_cmd_post(&ioceth->bna->msgq, &ioceth->msgq_cmd);
+}
+
+/* IOC callback functions */
+
+static void
+bna_cb_ioceth_enable(void *arg, enum bfa_status error)
+{
+ struct bna_ioceth *ioceth = (struct bna_ioceth *)arg;
+
+ if (error)
+ bfa_fsm_send_event(ioceth, IOCETH_E_IOC_FAILED);
+ else
+ bfa_fsm_send_event(ioceth, IOCETH_E_IOC_READY);
+}
+
+static void
+bna_cb_ioceth_disable(void *arg)
+{
+ struct bna_ioceth *ioceth = (struct bna_ioceth *)arg;
+
+ bfa_fsm_send_event(ioceth, IOCETH_E_IOC_DISABLED);
+}
+
+static void
+bna_cb_ioceth_hbfail(void *arg)
+{
+ struct bna_ioceth *ioceth = (struct bna_ioceth *)arg;
+
+ bfa_fsm_send_event(ioceth, IOCETH_E_IOC_FAILED);
+}
+
+static void
+bna_cb_ioceth_reset(void *arg)
+{
+ struct bna_ioceth *ioceth = (struct bna_ioceth *)arg;
+
+ bfa_fsm_send_event(ioceth, IOCETH_E_IOC_RESET);
+}
+
+static struct bfa_ioc_cbfn bna_ioceth_cbfn = {
+ bna_cb_ioceth_enable,
+ bna_cb_ioceth_disable,
+ bna_cb_ioceth_hbfail,
+ bna_cb_ioceth_reset
+};
+
+static void bna_attr_init(struct bna_ioceth *ioceth)
+{
+ ioceth->attr.num_txq = BFI_ENET_DEF_TXQ;
+ ioceth->attr.num_rxp = BFI_ENET_DEF_RXP;
+ ioceth->attr.num_ucmac = BFI_ENET_DEF_UCAM;
+ ioceth->attr.num_mcmac = BFI_ENET_MAX_MCAM;
+ ioceth->attr.max_rit_size = BFI_ENET_DEF_RITSZ;
+ ioceth->attr.fw_query_complete = false;
+}
+
+static void
+bna_ioceth_init(struct bna_ioceth *ioceth, struct bna *bna,
+ struct bna_res_info *res_info)
+{
+ u64 dma;
+ u8 *kva;
+
+ ioceth->bna = bna;
+
+ /**
+ * Attach IOC and claim:
+ * 1. DMA memory for IOC attributes
+ * 2. Kernel memory for FW trace
+ */
+ bfa_nw_ioc_attach(&ioceth->ioc, ioceth, &bna_ioceth_cbfn);
+ bfa_nw_ioc_pci_init(&ioceth->ioc, &bna->pcidev, BFI_PCIFN_CLASS_ETH);
+
+ BNA_GET_DMA_ADDR(
+ &res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.mdl[0].dma, dma);
+ kva = res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.mdl[0].kva;
+ bfa_nw_ioc_mem_claim(&ioceth->ioc, kva, dma);
+
+ kva = res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mdl[0].kva;
+
+ /**
+ * Attach common modules (Diag, SFP, CEE, Port) and claim respective
+ * DMA memory.
+ */
+ BNA_GET_DMA_ADDR(
+ &res_info[BNA_RES_MEM_T_COM].res_u.mem_info.mdl[0].dma, dma);
+ kva = res_info[BNA_RES_MEM_T_COM].res_u.mem_info.mdl[0].kva;
+ bfa_nw_cee_attach(&bna->cee, &ioceth->ioc, bna);
+ bfa_nw_cee_mem_claim(&bna->cee, kva, dma);
+ kva += bfa_nw_cee_meminfo();
+ dma += bfa_nw_cee_meminfo();
+
+ bfa_msgq_attach(&bna->msgq, &ioceth->ioc);
+ bfa_msgq_memclaim(&bna->msgq, kva, dma);
+ bfa_msgq_regisr(&bna->msgq, BFI_MC_ENET, bna_msgq_rsp_handler, bna);
+ kva += bfa_msgq_meminfo();
+ dma += bfa_msgq_meminfo();
+
+ ioceth->stop_cbfn = NULL;
+ ioceth->stop_cbarg = NULL;
+
+ bna_attr_init(ioceth);
+
+ bfa_fsm_set_state(ioceth, bna_ioceth_sm_stopped);
+}
+
+static void
+bna_ioceth_uninit(struct bna_ioceth *ioceth)
+{
+ bfa_nw_ioc_detach(&ioceth->ioc);
+
+ ioceth->bna = NULL;
+}
+
+void
+bna_ioceth_enable(struct bna_ioceth *ioceth)
+{
+ if (ioceth->fsm == (bfa_fsm_t)bna_ioceth_sm_ready) {
+ bnad_cb_ioceth_ready(ioceth->bna->bnad);
+ return;
+ }
+
+ if (ioceth->fsm == (bfa_fsm_t)bna_ioceth_sm_stopped)
+ bfa_fsm_send_event(ioceth, IOCETH_E_ENABLE);
+}
+
+void
+bna_ioceth_disable(struct bna_ioceth *ioceth, enum bna_cleanup_type type)
+{
+ if (type == BNA_SOFT_CLEANUP) {
+ bnad_cb_ioceth_disabled(ioceth->bna->bnad);
+ return;
+ }
+
+ ioceth->stop_cbfn = bnad_cb_ioceth_disabled;
+ ioceth->stop_cbarg = ioceth->bna->bnad;
+
+ bfa_fsm_send_event(ioceth, IOCETH_E_DISABLE);
+}
+
+static void
+bna_ucam_mod_init(struct bna_ucam_mod *ucam_mod, struct bna *bna,
+ struct bna_res_info *res_info)
+{
+ int i;
+
+ ucam_mod->ucmac = (struct bna_mac *)
+ res_info[BNA_MOD_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.mdl[0].kva;
+
+ INIT_LIST_HEAD(&ucam_mod->free_q);
+ for (i = 0; i < bna->ioceth.attr.num_ucmac; i++) {
+ bfa_q_qe_init(&ucam_mod->ucmac[i].qe);
+ list_add_tail(&ucam_mod->ucmac[i].qe, &ucam_mod->free_q);
+ }
+
+ ucam_mod->bna = bna;
+}
+
+static void
+bna_ucam_mod_uninit(struct bna_ucam_mod *ucam_mod)
+{
+ struct list_head *qe;
+ int i = 0;
+
+ list_for_each(qe, &ucam_mod->free_q)
+ i++;
+
+ ucam_mod->bna = NULL;
+}
+
+static void
+bna_mcam_mod_init(struct bna_mcam_mod *mcam_mod, struct bna *bna,
+ struct bna_res_info *res_info)
+{
+ int i;
+
+ mcam_mod->mcmac = (struct bna_mac *)
+ res_info[BNA_MOD_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.mdl[0].kva;
+
+ INIT_LIST_HEAD(&mcam_mod->free_q);
+ for (i = 0; i < bna->ioceth.attr.num_mcmac; i++) {
+ bfa_q_qe_init(&mcam_mod->mcmac[i].qe);
+ list_add_tail(&mcam_mod->mcmac[i].qe, &mcam_mod->free_q);
+ }
+
+ mcam_mod->mchandle = (struct bna_mcam_handle *)
+ res_info[BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY].res_u.mem_info.mdl[0].kva;
+
+ INIT_LIST_HEAD(&mcam_mod->free_handle_q);
+ for (i = 0; i < bna->ioceth.attr.num_mcmac; i++) {
+ bfa_q_qe_init(&mcam_mod->mchandle[i].qe);
+ list_add_tail(&mcam_mod->mchandle[i].qe,
+ &mcam_mod->free_handle_q);
+ }
+
+ mcam_mod->bna = bna;
+}
+
+static void
+bna_mcam_mod_uninit(struct bna_mcam_mod *mcam_mod)
+{
+ struct list_head *qe;
+ int i;
+
+ i = 0;
+ list_for_each(qe, &mcam_mod->free_q) i++;
+
+ i = 0;
+ list_for_each(qe, &mcam_mod->free_handle_q) i++;
+
+ mcam_mod->bna = NULL;
+}
+
+static void
+bna_bfi_stats_get(struct bna *bna)
+{
+ struct bfi_enet_stats_req *stats_req = &bna->stats_mod.stats_get;
+
+ bna->stats_mod.stats_get_busy = true;
+
+ bfi_msgq_mhdr_set(stats_req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_STATS_GET_REQ, 0, 0);
+ stats_req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_stats_req)));
+ stats_req->stats_mask = htons(BFI_ENET_STATS_ALL);
+ stats_req->tx_enet_mask = htonl(bna->tx_mod.rid_mask);
+ stats_req->rx_enet_mask = htonl(bna->rx_mod.rid_mask);
+ stats_req->host_buffer.a32.addr_hi = bna->stats.hw_stats_dma.msb;
+ stats_req->host_buffer.a32.addr_lo = bna->stats.hw_stats_dma.lsb;
+
+ bfa_msgq_cmd_set(&bna->stats_mod.stats_get_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_stats_req), &stats_req->mh);
+ bfa_msgq_cmd_post(&bna->msgq, &bna->stats_mod.stats_get_cmd);
+}
+
+void
+bna_res_req(struct bna_res_info *res_info)
+{
+ /* DMA memory for COMMON_MODULE */
+ res_info[BNA_RES_MEM_T_COM].res_type = BNA_RES_T_MEM;
+ res_info[BNA_RES_MEM_T_COM].res_u.mem_info.mem_type = BNA_MEM_T_DMA;
+ res_info[BNA_RES_MEM_T_COM].res_u.mem_info.num = 1;
+ res_info[BNA_RES_MEM_T_COM].res_u.mem_info.len = ALIGN(
+ (bfa_nw_cee_meminfo() +
+ bfa_msgq_meminfo()), PAGE_SIZE);
+
+ /* DMA memory for retrieving IOC attributes */
+ res_info[BNA_RES_MEM_T_ATTR].res_type = BNA_RES_T_MEM;
+ res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.mem_type = BNA_MEM_T_DMA;
+ res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.num = 1;
+ res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.len =
+ ALIGN(bfa_nw_ioc_meminfo(), PAGE_SIZE);
+
+ /* Virtual memory for retreiving fw_trc */
+ res_info[BNA_RES_MEM_T_FWTRC].res_type = BNA_RES_T_MEM;
+ res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mem_type = BNA_MEM_T_KVA;
+ res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.num = 0;
+ res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.len = 0;
+
+ /* DMA memory for retreiving stats */
+ res_info[BNA_RES_MEM_T_STATS].res_type = BNA_RES_T_MEM;
+ res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mem_type = BNA_MEM_T_DMA;
+ res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.num = 1;
+ res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.len =
+ ALIGN(sizeof(struct bfi_enet_stats),
+ PAGE_SIZE);
+}
+
+void
+bna_mod_res_req(struct bna *bna, struct bna_res_info *res_info)
+{
+ struct bna_attr *attr = &bna->ioceth.attr;
+
+ /* Virtual memory for Tx objects - stored by Tx module */
+ res_info[BNA_MOD_RES_MEM_T_TX_ARRAY].res_type = BNA_RES_T_MEM;
+ res_info[BNA_MOD_RES_MEM_T_TX_ARRAY].res_u.mem_info.mem_type =
+ BNA_MEM_T_KVA;
+ res_info[BNA_MOD_RES_MEM_T_TX_ARRAY].res_u.mem_info.num = 1;
+ res_info[BNA_MOD_RES_MEM_T_TX_ARRAY].res_u.mem_info.len =
+ attr->num_txq * sizeof(struct bna_tx);
+
+ /* Virtual memory for TxQ - stored by Tx module */
+ res_info[BNA_MOD_RES_MEM_T_TXQ_ARRAY].res_type = BNA_RES_T_MEM;
+ res_info[BNA_MOD_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.mem_type =
+ BNA_MEM_T_KVA;
+ res_info[BNA_MOD_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.num = 1;
+ res_info[BNA_MOD_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.len =
+ attr->num_txq * sizeof(struct bna_txq);
+
+ /* Virtual memory for Rx objects - stored by Rx module */
+ res_info[BNA_MOD_RES_MEM_T_RX_ARRAY].res_type = BNA_RES_T_MEM;
+ res_info[BNA_MOD_RES_MEM_T_RX_ARRAY].res_u.mem_info.mem_type =
+ BNA_MEM_T_KVA;
+ res_info[BNA_MOD_RES_MEM_T_RX_ARRAY].res_u.mem_info.num = 1;
+ res_info[BNA_MOD_RES_MEM_T_RX_ARRAY].res_u.mem_info.len =
+ attr->num_rxp * sizeof(struct bna_rx);
+
+ /* Virtual memory for RxPath - stored by Rx module */
+ res_info[BNA_MOD_RES_MEM_T_RXP_ARRAY].res_type = BNA_RES_T_MEM;
+ res_info[BNA_MOD_RES_MEM_T_RXP_ARRAY].res_u.mem_info.mem_type =
+ BNA_MEM_T_KVA;
+ res_info[BNA_MOD_RES_MEM_T_RXP_ARRAY].res_u.mem_info.num = 1;
+ res_info[BNA_MOD_RES_MEM_T_RXP_ARRAY].res_u.mem_info.len =
+ attr->num_rxp * sizeof(struct bna_rxp);
+
+ /* Virtual memory for RxQ - stored by Rx module */
+ res_info[BNA_MOD_RES_MEM_T_RXQ_ARRAY].res_type = BNA_RES_T_MEM;
+ res_info[BNA_MOD_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.mem_type =
+ BNA_MEM_T_KVA;
+ res_info[BNA_MOD_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.num = 1;
+ res_info[BNA_MOD_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.len =
+ (attr->num_rxp * 2) * sizeof(struct bna_rxq);
+
+ /* Virtual memory for Unicast MAC address - stored by ucam module */
+ res_info[BNA_MOD_RES_MEM_T_UCMAC_ARRAY].res_type = BNA_RES_T_MEM;
+ res_info[BNA_MOD_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.mem_type =
+ BNA_MEM_T_KVA;
+ res_info[BNA_MOD_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.num = 1;
+ res_info[BNA_MOD_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.len =
+ attr->num_ucmac * sizeof(struct bna_mac);
+
+ /* Virtual memory for Multicast MAC address - stored by mcam module */
+ res_info[BNA_MOD_RES_MEM_T_MCMAC_ARRAY].res_type = BNA_RES_T_MEM;
+ res_info[BNA_MOD_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.mem_type =
+ BNA_MEM_T_KVA;
+ res_info[BNA_MOD_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.num = 1;
+ res_info[BNA_MOD_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.len =
+ attr->num_mcmac * sizeof(struct bna_mac);
+
+ /* Virtual memory for Multicast handle - stored by mcam module */
+ res_info[BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY].res_type = BNA_RES_T_MEM;
+ res_info[BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY].res_u.mem_info.mem_type =
+ BNA_MEM_T_KVA;
+ res_info[BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY].res_u.mem_info.num = 1;
+ res_info[BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY].res_u.mem_info.len =
+ attr->num_mcmac * sizeof(struct bna_mcam_handle);
+}
+
+void
+bna_init(struct bna *bna, struct bnad *bnad,
+ struct bfa_pcidev *pcidev, struct bna_res_info *res_info)
+{
+ bna->bnad = bnad;
+ bna->pcidev = *pcidev;
+
+ bna->stats.hw_stats_kva = (struct bfi_enet_stats *)
+ res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mdl[0].kva;
+ bna->stats.hw_stats_dma.msb =
+ res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mdl[0].dma.msb;
+ bna->stats.hw_stats_dma.lsb =
+ res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mdl[0].dma.lsb;
+
+ bna_reg_addr_init(bna, &bna->pcidev);
+
+ /* Also initializes diag, cee, sfp, phy_port, msgq */
+ bna_ioceth_init(&bna->ioceth, bna, res_info);
+
+ bna_enet_init(&bna->enet, bna);
+ bna_ethport_init(&bna->ethport, bna);
+}
+
+void
+bna_mod_init(struct bna *bna, struct bna_res_info *res_info)
+{
+ bna_tx_mod_init(&bna->tx_mod, bna, res_info);
+
+ bna_rx_mod_init(&bna->rx_mod, bna, res_info);
+
+ bna_ucam_mod_init(&bna->ucam_mod, bna, res_info);
+
+ bna_mcam_mod_init(&bna->mcam_mod, bna, res_info);
+
+ bna->default_mode_rid = BFI_INVALID_RID;
+ bna->promisc_rid = BFI_INVALID_RID;
+
+ bna->mod_flags |= BNA_MOD_F_INIT_DONE;
+}
+
+void
+bna_uninit(struct bna *bna)
+{
+ if (bna->mod_flags & BNA_MOD_F_INIT_DONE) {
+ bna_mcam_mod_uninit(&bna->mcam_mod);
+ bna_ucam_mod_uninit(&bna->ucam_mod);
+ bna_rx_mod_uninit(&bna->rx_mod);
+ bna_tx_mod_uninit(&bna->tx_mod);
+ bna->mod_flags &= ~BNA_MOD_F_INIT_DONE;
+ }
+
+ bna_stats_mod_uninit(&bna->stats_mod);
+ bna_ethport_uninit(&bna->ethport);
+ bna_enet_uninit(&bna->enet);
+
+ bna_ioceth_uninit(&bna->ioceth);
+
+ bna->bnad = NULL;
+}
+
+int
+bna_num_txq_set(struct bna *bna, int num_txq)
+{
+ if (bna->ioceth.attr.fw_query_complete &&
+ (num_txq <= bna->ioceth.attr.num_txq)) {
+ bna->ioceth.attr.num_txq = num_txq;
+ return BNA_CB_SUCCESS;
+ }
+
+ return BNA_CB_FAIL;
+}
+
+int
+bna_num_rxp_set(struct bna *bna, int num_rxp)
+{
+ if (bna->ioceth.attr.fw_query_complete &&
+ (num_rxp <= bna->ioceth.attr.num_rxp)) {
+ bna->ioceth.attr.num_rxp = num_rxp;
+ return BNA_CB_SUCCESS;
+ }
+
+ return BNA_CB_FAIL;
+}
+
+struct bna_mac *
+bna_ucam_mod_mac_get(struct bna_ucam_mod *ucam_mod)
+{
+ struct list_head *qe;
+
+ if (list_empty(&ucam_mod->free_q))
+ return NULL;
+
+ bfa_q_deq(&ucam_mod->free_q, &qe);
+
+ return (struct bna_mac *)qe;
+}
+
+void
+bna_ucam_mod_mac_put(struct bna_ucam_mod *ucam_mod, struct bna_mac *mac)
+{
+ list_add_tail(&mac->qe, &ucam_mod->free_q);
+}
+
+struct bna_mac *
+bna_mcam_mod_mac_get(struct bna_mcam_mod *mcam_mod)
+{
+ struct list_head *qe;
+
+ if (list_empty(&mcam_mod->free_q))
+ return NULL;
+
+ bfa_q_deq(&mcam_mod->free_q, &qe);
+
+ return (struct bna_mac *)qe;
+}
+
+void
+bna_mcam_mod_mac_put(struct bna_mcam_mod *mcam_mod, struct bna_mac *mac)
+{
+ list_add_tail(&mac->qe, &mcam_mod->free_q);
+}
+
+struct bna_mcam_handle *
+bna_mcam_mod_handle_get(struct bna_mcam_mod *mcam_mod)
+{
+ struct list_head *qe;
+
+ if (list_empty(&mcam_mod->free_handle_q))
+ return NULL;
+
+ bfa_q_deq(&mcam_mod->free_handle_q, &qe);
+
+ return (struct bna_mcam_handle *)qe;
+}
+
+void
+bna_mcam_mod_handle_put(struct bna_mcam_mod *mcam_mod,
+ struct bna_mcam_handle *handle)
+{
+ list_add_tail(&handle->qe, &mcam_mod->free_handle_q);
+}
+
+void
+bna_hw_stats_get(struct bna *bna)
+{
+ if (!bna->stats_mod.ioc_ready) {
+ bnad_cb_stats_get(bna->bnad, BNA_CB_FAIL, &bna->stats);
+ return;
+ }
+ if (bna->stats_mod.stats_get_busy) {
+ bnad_cb_stats_get(bna->bnad, BNA_CB_BUSY, &bna->stats);
+ return;
+ }
+
+ bna_bfi_stats_get(bna);
+}
diff --git a/drivers/net/ethernet/brocade/bna/bna_hw_defs.h b/drivers/net/ethernet/brocade/bna/bna_hw_defs.h
new file mode 100644
index 000000000000..4c6aab2a9534
--- /dev/null
+++ b/drivers/net/ethernet/brocade/bna/bna_hw_defs.h
@@ -0,0 +1,422 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+/**
+ * File for interrupt macros and functions
+ */
+
+#ifndef __BNA_HW_DEFS_H__
+#define __BNA_HW_DEFS_H__
+
+#include "bfi_reg.h"
+
+/**
+ *
+ * SW imposed limits
+ *
+ */
+#define BFI_ENET_DEF_TXQ 1
+#define BFI_ENET_DEF_RXP 1
+#define BFI_ENET_DEF_UCAM 1
+#define BFI_ENET_DEF_RITSZ 1
+
+#define BFI_ENET_MAX_MCAM 256
+
+#define BFI_INVALID_RID -1
+
+#define BFI_IBIDX_SIZE 4
+
+#define BFI_VLAN_WORD_SHIFT 5 /* 32 bits */
+#define BFI_VLAN_WORD_MASK 0x1F
+#define BFI_VLAN_BLOCK_SHIFT 9 /* 512 bits */
+#define BFI_VLAN_BMASK_ALL 0xFF
+
+#define BFI_COALESCING_TIMER_UNIT 5 /* 5us */
+#define BFI_MAX_COALESCING_TIMEO 0xFF /* in 5us units */
+#define BFI_MAX_INTERPKT_COUNT 0xFF
+#define BFI_MAX_INTERPKT_TIMEO 0xF /* in 0.5us units */
+#define BFI_TX_COALESCING_TIMEO 20 /* 20 * 5 = 100us */
+#define BFI_TX_INTERPKT_COUNT 32
+#define BFI_RX_COALESCING_TIMEO 12 /* 12 * 5 = 60us */
+#define BFI_RX_INTERPKT_COUNT 6 /* Pkt Cnt = 6 */
+#define BFI_RX_INTERPKT_TIMEO 3 /* 3 * 0.5 = 1.5us */
+
+#define BFI_TXQ_WI_SIZE 64 /* bytes */
+#define BFI_RXQ_WI_SIZE 8 /* bytes */
+#define BFI_CQ_WI_SIZE 16 /* bytes */
+#define BFI_TX_MAX_WRR_QUOTA 0xFFF
+
+#define BFI_TX_MAX_VECTORS_PER_WI 4
+#define BFI_TX_MAX_VECTORS_PER_PKT 0xFF
+#define BFI_TX_MAX_DATA_PER_VECTOR 0xFFFF
+#define BFI_TX_MAX_DATA_PER_PKT 0xFFFFFF
+
+/* Small Q buffer size */
+#define BFI_SMALL_RXBUF_SIZE 128
+
+#define BFI_TX_MAX_PRIO 8
+#define BFI_TX_PRIO_MAP_ALL 0xFF
+
+/*
+ *
+ * Register definitions and macros
+ *
+ */
+
+#define BNA_PCI_REG_CT_ADDRSZ (0x40000)
+
+#define ct_reg_addr_init(_bna, _pcidev) \
+{ \
+ struct bna_reg_offset reg_offset[] = \
+ {{HOSTFN0_INT_STATUS, HOSTFN0_INT_MSK}, \
+ {HOSTFN1_INT_STATUS, HOSTFN1_INT_MSK}, \
+ {HOSTFN2_INT_STATUS, HOSTFN2_INT_MSK}, \
+ {HOSTFN3_INT_STATUS, HOSTFN3_INT_MSK} }; \
+ \
+ (_bna)->regs.fn_int_status = (_pcidev)->pci_bar_kva + \
+ reg_offset[(_pcidev)->pci_func].fn_int_status;\
+ (_bna)->regs.fn_int_mask = (_pcidev)->pci_bar_kva + \
+ reg_offset[(_pcidev)->pci_func].fn_int_mask;\
+}
+
+#define ct_bit_defn_init(_bna, _pcidev) \
+{ \
+ (_bna)->bits.mbox_status_bits = (__HFN_INT_MBOX_LPU0 | \
+ __HFN_INT_MBOX_LPU1); \
+ (_bna)->bits.mbox_mask_bits = (__HFN_INT_MBOX_LPU0 | \
+ __HFN_INT_MBOX_LPU1); \
+ (_bna)->bits.error_status_bits = (__HFN_INT_ERR_MASK); \
+ (_bna)->bits.error_mask_bits = (__HFN_INT_ERR_MASK); \
+ (_bna)->bits.halt_status_bits = __HFN_INT_LL_HALT; \
+ (_bna)->bits.halt_mask_bits = __HFN_INT_LL_HALT; \
+}
+
+#define ct2_reg_addr_init(_bna, _pcidev) \
+{ \
+ (_bna)->regs.fn_int_status = (_pcidev)->pci_bar_kva + \
+ CT2_HOSTFN_INT_STATUS; \
+ (_bna)->regs.fn_int_mask = (_pcidev)->pci_bar_kva + \
+ CT2_HOSTFN_INTR_MASK; \
+}
+
+#define ct2_bit_defn_init(_bna, _pcidev) \
+{ \
+ (_bna)->bits.mbox_status_bits = (__HFN_INT_MBOX_LPU0_CT2 | \
+ __HFN_INT_MBOX_LPU1_CT2); \
+ (_bna)->bits.mbox_mask_bits = (__HFN_INT_MBOX_LPU0_CT2 | \
+ __HFN_INT_MBOX_LPU1_CT2); \
+ (_bna)->bits.error_status_bits = (__HFN_INT_ERR_MASK_CT2); \
+ (_bna)->bits.error_mask_bits = (__HFN_INT_ERR_MASK_CT2); \
+ (_bna)->bits.halt_status_bits = __HFN_INT_CPQ_HALT_CT2; \
+ (_bna)->bits.halt_mask_bits = __HFN_INT_CPQ_HALT_CT2; \
+}
+
+#define bna_reg_addr_init(_bna, _pcidev) \
+{ \
+ switch ((_pcidev)->device_id) { \
+ case PCI_DEVICE_ID_BROCADE_CT: \
+ ct_reg_addr_init((_bna), (_pcidev)); \
+ ct_bit_defn_init((_bna), (_pcidev)); \
+ break; \
+ case BFA_PCI_DEVICE_ID_CT2: \
+ ct2_reg_addr_init((_bna), (_pcidev)); \
+ ct2_bit_defn_init((_bna), (_pcidev)); \
+ break; \
+ } \
+}
+
+#define bna_port_id_get(_bna) ((_bna)->ioceth.ioc.port_id)
+/**
+ *
+ * Interrupt related bits, flags and macros
+ *
+ */
+
+#define IB_STATUS_BITS 0x0000ffff
+
+#define BNA_IS_MBOX_INTR(_bna, _intr_status) \
+ ((_intr_status) & (_bna)->bits.mbox_status_bits)
+
+#define BNA_IS_HALT_INTR(_bna, _intr_status) \
+ ((_intr_status) & (_bna)->bits.halt_status_bits)
+
+#define BNA_IS_ERR_INTR(_bna, _intr_status) \
+ ((_intr_status) & (_bna)->bits.error_status_bits)
+
+#define BNA_IS_MBOX_ERR_INTR(_bna, _intr_status) \
+ (BNA_IS_MBOX_INTR(_bna, _intr_status) | \
+ BNA_IS_ERR_INTR(_bna, _intr_status))
+
+#define BNA_IS_INTX_DATA_INTR(_intr_status) \
+ ((_intr_status) & IB_STATUS_BITS)
+
+#define bna_halt_clear(_bna) \
+do { \
+ u32 init_halt; \
+ init_halt = readl((_bna)->ioceth.ioc.ioc_regs.ll_halt); \
+ init_halt &= ~__FW_INIT_HALT_P; \
+ writel(init_halt, (_bna)->ioceth.ioc.ioc_regs.ll_halt); \
+ init_halt = readl((_bna)->ioceth.ioc.ioc_regs.ll_halt); \
+} while (0)
+
+#define bna_intx_disable(_bna, _cur_mask) \
+{ \
+ (_cur_mask) = readl((_bna)->regs.fn_int_mask); \
+ writel(0xffffffff, (_bna)->regs.fn_int_mask); \
+}
+
+#define bna_intx_enable(bna, new_mask) \
+ writel((new_mask), (bna)->regs.fn_int_mask)
+#define bna_mbox_intr_disable(bna) \
+do { \
+ u32 mask; \
+ mask = readl((bna)->regs.fn_int_mask); \
+ writel((mask | (bna)->bits.mbox_mask_bits | \
+ (bna)->bits.error_mask_bits), (bna)->regs.fn_int_mask); \
+ mask = readl((bna)->regs.fn_int_mask); \
+} while (0)
+
+#define bna_mbox_intr_enable(bna) \
+do { \
+ u32 mask; \
+ mask = readl((bna)->regs.fn_int_mask); \
+ writel((mask & ~((bna)->bits.mbox_mask_bits | \
+ (bna)->bits.error_mask_bits)), (bna)->regs.fn_int_mask);\
+ mask = readl((bna)->regs.fn_int_mask); \
+} while (0)
+
+#define bna_intr_status_get(_bna, _status) \
+{ \
+ (_status) = readl((_bna)->regs.fn_int_status); \
+ if (_status) { \
+ writel(((_status) & ~(_bna)->bits.mbox_status_bits), \
+ (_bna)->regs.fn_int_status); \
+ } \
+}
+
+/*
+ * MAX ACK EVENTS : No. of acks that can be accumulated in driver,
+ * before acking to h/w. The no. of bits is 16 in the doorbell register,
+ * however we keep this limited to 15 bits.
+ * This is because around the edge of 64K boundary (16 bits), one
+ * single poll can make the accumulated ACK counter cross the 64K boundary,
+ * causing problems, when we try to ack with a value greater than 64K.
+ * 15 bits (32K) should be large enough to accumulate, anyways, and the max.
+ * acked events to h/w can be (32K + max poll weight) (currently 64).
+ */
+#define BNA_IB_MAX_ACK_EVENTS (1 << 15)
+
+/* These macros build the data portion of the TxQ/RxQ doorbell */
+#define BNA_DOORBELL_Q_PRD_IDX(_pi) (0x80000000 | (_pi))
+#define BNA_DOORBELL_Q_STOP (0x40000000)
+
+/* These macros build the data portion of the IB doorbell */
+#define BNA_DOORBELL_IB_INT_ACK(_timeout, _events) \
+ (0x80000000 | ((_timeout) << 16) | (_events))
+#define BNA_DOORBELL_IB_INT_DISABLE (0x40000000)
+
+/* Set the coalescing timer for the given ib */
+#define bna_ib_coalescing_timer_set(_i_dbell, _cls_timer) \
+ ((_i_dbell)->doorbell_ack = BNA_DOORBELL_IB_INT_ACK((_cls_timer), 0));
+
+/* Acks 'events' # of events for a given ib while disabling interrupts */
+#define bna_ib_ack_disable_irq(_i_dbell, _events) \
+ (writel(BNA_DOORBELL_IB_INT_ACK(0, (_events)), \
+ (_i_dbell)->doorbell_addr));
+
+/* Acks 'events' # of events for a given ib */
+#define bna_ib_ack(_i_dbell, _events) \
+ (writel(((_i_dbell)->doorbell_ack | (_events)), \
+ (_i_dbell)->doorbell_addr));
+
+#define bna_ib_start(_bna, _ib, _is_regular) \
+{ \
+ u32 intx_mask; \
+ struct bna_ib *ib = _ib; \
+ if ((ib->intr_type == BNA_INTR_T_INTX)) { \
+ bna_intx_disable((_bna), intx_mask); \
+ intx_mask &= ~(ib->intr_vector); \
+ bna_intx_enable((_bna), intx_mask); \
+ } \
+ bna_ib_coalescing_timer_set(&ib->door_bell, \
+ ib->coalescing_timeo); \
+ if (_is_regular) \
+ bna_ib_ack(&ib->door_bell, 0); \
+}
+
+#define bna_ib_stop(_bna, _ib) \
+{ \
+ u32 intx_mask; \
+ struct bna_ib *ib = _ib; \
+ writel(BNA_DOORBELL_IB_INT_DISABLE, \
+ ib->door_bell.doorbell_addr); \
+ if (ib->intr_type == BNA_INTR_T_INTX) { \
+ bna_intx_disable((_bna), intx_mask); \
+ intx_mask |= ib->intr_vector; \
+ bna_intx_enable((_bna), intx_mask); \
+ } \
+}
+
+#define bna_txq_prod_indx_doorbell(_tcb) \
+ (writel(BNA_DOORBELL_Q_PRD_IDX((_tcb)->producer_index), \
+ (_tcb)->q_dbell));
+
+#define bna_rxq_prod_indx_doorbell(_rcb) \
+ (writel(BNA_DOORBELL_Q_PRD_IDX((_rcb)->producer_index), \
+ (_rcb)->q_dbell));
+
+/**
+ *
+ * TxQ, RxQ, CQ related bits, offsets, macros
+ *
+ */
+
+/* TxQ Entry Opcodes */
+#define BNA_TXQ_WI_SEND (0x402) /* Single Frame Transmission */
+#define BNA_TXQ_WI_SEND_LSO (0x403) /* Multi-Frame Transmission */
+#define BNA_TXQ_WI_EXTENSION (0x104) /* Extension WI */
+
+/* TxQ Entry Control Flags */
+#define BNA_TXQ_WI_CF_FCOE_CRC (1 << 8)
+#define BNA_TXQ_WI_CF_IPID_MODE (1 << 5)
+#define BNA_TXQ_WI_CF_INS_PRIO (1 << 4)
+#define BNA_TXQ_WI_CF_INS_VLAN (1 << 3)
+#define BNA_TXQ_WI_CF_UDP_CKSUM (1 << 2)
+#define BNA_TXQ_WI_CF_TCP_CKSUM (1 << 1)
+#define BNA_TXQ_WI_CF_IP_CKSUM (1 << 0)
+
+#define BNA_TXQ_WI_L4_HDR_N_OFFSET(_hdr_size, _offset) \
+ (((_hdr_size) << 10) | ((_offset) & 0x3FF))
+
+/*
+ * Completion Q defines
+ */
+/* CQ Entry Flags */
+#define BNA_CQ_EF_MAC_ERROR (1 << 0)
+#define BNA_CQ_EF_FCS_ERROR (1 << 1)
+#define BNA_CQ_EF_TOO_LONG (1 << 2)
+#define BNA_CQ_EF_FC_CRC_OK (1 << 3)
+
+#define BNA_CQ_EF_RSVD1 (1 << 4)
+#define BNA_CQ_EF_L4_CKSUM_OK (1 << 5)
+#define BNA_CQ_EF_L3_CKSUM_OK (1 << 6)
+#define BNA_CQ_EF_HDS_HEADER (1 << 7)
+
+#define BNA_CQ_EF_UDP (1 << 8)
+#define BNA_CQ_EF_TCP (1 << 9)
+#define BNA_CQ_EF_IP_OPTIONS (1 << 10)
+#define BNA_CQ_EF_IPV6 (1 << 11)
+
+#define BNA_CQ_EF_IPV4 (1 << 12)
+#define BNA_CQ_EF_VLAN (1 << 13)
+#define BNA_CQ_EF_RSS (1 << 14)
+#define BNA_CQ_EF_RSVD2 (1 << 15)
+
+#define BNA_CQ_EF_MCAST_MATCH (1 << 16)
+#define BNA_CQ_EF_MCAST (1 << 17)
+#define BNA_CQ_EF_BCAST (1 << 18)
+#define BNA_CQ_EF_REMOTE (1 << 19)
+
+#define BNA_CQ_EF_LOCAL (1 << 20)
+
+/**
+ *
+ * Data structures
+ *
+ */
+
+struct bna_reg_offset {
+ u32 fn_int_status;
+ u32 fn_int_mask;
+};
+
+struct bna_bit_defn {
+ u32 mbox_status_bits;
+ u32 mbox_mask_bits;
+ u32 error_status_bits;
+ u32 error_mask_bits;
+ u32 halt_status_bits;
+ u32 halt_mask_bits;
+};
+
+struct bna_reg {
+ void __iomem *fn_int_status;
+ void __iomem *fn_int_mask;
+};
+
+/* TxQ Vector (a.k.a. Tx-Buffer Descriptor) */
+struct bna_dma_addr {
+ u32 msb;
+ u32 lsb;
+};
+
+struct bna_txq_wi_vector {
+ u16 reserved;
+ u16 length; /* Only 14 LSB are valid */
+ struct bna_dma_addr host_addr; /* Tx-Buf DMA addr */
+};
+
+/**
+ * TxQ Entry Structure
+ *
+ * BEWARE: Load values into this structure with correct endianess.
+ */
+struct bna_txq_entry {
+ union {
+ struct {
+ u8 reserved;
+ u8 num_vectors; /* number of vectors present */
+ u16 opcode; /* Either */
+ /* BNA_TXQ_WI_SEND or */
+ /* BNA_TXQ_WI_SEND_LSO */
+ u16 flags; /* OR of all the flags */
+ u16 l4_hdr_size_n_offset;
+ u16 vlan_tag;
+ u16 lso_mss; /* Only 14 LSB are valid */
+ u32 frame_length; /* Only 24 LSB are valid */
+ } wi;
+
+ struct {
+ u16 reserved;
+ u16 opcode; /* Must be */
+ /* BNA_TXQ_WI_EXTENSION */
+ u32 reserved2[3]; /* Place holder for */
+ /* removed vector (12 bytes) */
+ } wi_ext;
+ } hdr;
+ struct bna_txq_wi_vector vector[4];
+};
+
+/* RxQ Entry Structure */
+struct bna_rxq_entry { /* Rx-Buffer */
+ struct bna_dma_addr host_addr; /* Rx-Buffer DMA address */
+};
+
+/* CQ Entry Structure */
+struct bna_cq_entry {
+ u32 flags;
+ u16 vlan_tag;
+ u16 length;
+ u32 rss_hash;
+ u8 valid;
+ u8 reserved1;
+ u8 reserved2;
+ u8 rxq_id;
+};
+
+#endif /* __BNA_HW_DEFS_H__ */
diff --git a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c
new file mode 100644
index 000000000000..276fcb589f4b
--- /dev/null
+++ b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c
@@ -0,0 +1,3798 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+#include "bna.h"
+#include "bfi.h"
+
+/**
+ * IB
+ */
+static void
+bna_ib_coalescing_timeo_set(struct bna_ib *ib, u8 coalescing_timeo)
+{
+ ib->coalescing_timeo = coalescing_timeo;
+ ib->door_bell.doorbell_ack = BNA_DOORBELL_IB_INT_ACK(
+ (u32)ib->coalescing_timeo, 0);
+}
+
+/**
+ * RXF
+ */
+
+#define bna_rxf_vlan_cfg_soft_reset(rxf) \
+do { \
+ (rxf)->vlan_pending_bitmask = (u8)BFI_VLAN_BMASK_ALL; \
+ (rxf)->vlan_strip_pending = true; \
+} while (0)
+
+#define bna_rxf_rss_cfg_soft_reset(rxf) \
+do { \
+ if ((rxf)->rss_status == BNA_STATUS_T_ENABLED) \
+ (rxf)->rss_pending = (BNA_RSS_F_RIT_PENDING | \
+ BNA_RSS_F_CFG_PENDING | \
+ BNA_RSS_F_STATUS_PENDING); \
+} while (0)
+
+static int bna_rxf_cfg_apply(struct bna_rxf *rxf);
+static void bna_rxf_cfg_reset(struct bna_rxf *rxf);
+static int bna_rxf_fltr_clear(struct bna_rxf *rxf);
+static int bna_rxf_ucast_cfg_apply(struct bna_rxf *rxf);
+static int bna_rxf_promisc_cfg_apply(struct bna_rxf *rxf);
+static int bna_rxf_allmulti_cfg_apply(struct bna_rxf *rxf);
+static int bna_rxf_vlan_strip_cfg_apply(struct bna_rxf *rxf);
+static int bna_rxf_ucast_cfg_reset(struct bna_rxf *rxf,
+ enum bna_cleanup_type cleanup);
+static int bna_rxf_promisc_cfg_reset(struct bna_rxf *rxf,
+ enum bna_cleanup_type cleanup);
+static int bna_rxf_allmulti_cfg_reset(struct bna_rxf *rxf,
+ enum bna_cleanup_type cleanup);
+
+bfa_fsm_state_decl(bna_rxf, stopped, struct bna_rxf,
+ enum bna_rxf_event);
+bfa_fsm_state_decl(bna_rxf, paused, struct bna_rxf,
+ enum bna_rxf_event);
+bfa_fsm_state_decl(bna_rxf, cfg_wait, struct bna_rxf,
+ enum bna_rxf_event);
+bfa_fsm_state_decl(bna_rxf, started, struct bna_rxf,
+ enum bna_rxf_event);
+bfa_fsm_state_decl(bna_rxf, fltr_clr_wait, struct bna_rxf,
+ enum bna_rxf_event);
+bfa_fsm_state_decl(bna_rxf, last_resp_wait, struct bna_rxf,
+ enum bna_rxf_event);
+
+static void
+bna_rxf_sm_stopped_entry(struct bna_rxf *rxf)
+{
+ call_rxf_stop_cbfn(rxf);
+}
+
+static void
+bna_rxf_sm_stopped(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+ switch (event) {
+ case RXF_E_START:
+ if (rxf->flags & BNA_RXF_F_PAUSED) {
+ bfa_fsm_set_state(rxf, bna_rxf_sm_paused);
+ call_rxf_start_cbfn(rxf);
+ } else
+ bfa_fsm_set_state(rxf, bna_rxf_sm_cfg_wait);
+ break;
+
+ case RXF_E_STOP:
+ call_rxf_stop_cbfn(rxf);
+ break;
+
+ case RXF_E_FAIL:
+ /* No-op */
+ break;
+
+ case RXF_E_CONFIG:
+ call_rxf_cam_fltr_cbfn(rxf);
+ break;
+
+ case RXF_E_PAUSE:
+ rxf->flags |= BNA_RXF_F_PAUSED;
+ call_rxf_pause_cbfn(rxf);
+ break;
+
+ case RXF_E_RESUME:
+ rxf->flags &= ~BNA_RXF_F_PAUSED;
+ call_rxf_resume_cbfn(rxf);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_rxf_sm_paused_entry(struct bna_rxf *rxf)
+{
+ call_rxf_pause_cbfn(rxf);
+}
+
+static void
+bna_rxf_sm_paused(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+ switch (event) {
+ case RXF_E_STOP:
+ case RXF_E_FAIL:
+ bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+ break;
+
+ case RXF_E_CONFIG:
+ call_rxf_cam_fltr_cbfn(rxf);
+ break;
+
+ case RXF_E_RESUME:
+ rxf->flags &= ~BNA_RXF_F_PAUSED;
+ bfa_fsm_set_state(rxf, bna_rxf_sm_cfg_wait);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_rxf_sm_cfg_wait_entry(struct bna_rxf *rxf)
+{
+ if (!bna_rxf_cfg_apply(rxf)) {
+ /* No more pending config updates */
+ bfa_fsm_set_state(rxf, bna_rxf_sm_started);
+ }
+}
+
+static void
+bna_rxf_sm_cfg_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+ switch (event) {
+ case RXF_E_STOP:
+ bfa_fsm_set_state(rxf, bna_rxf_sm_last_resp_wait);
+ break;
+
+ case RXF_E_FAIL:
+ bna_rxf_cfg_reset(rxf);
+ call_rxf_start_cbfn(rxf);
+ call_rxf_cam_fltr_cbfn(rxf);
+ call_rxf_resume_cbfn(rxf);
+ bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+ break;
+
+ case RXF_E_CONFIG:
+ /* No-op */
+ break;
+
+ case RXF_E_PAUSE:
+ rxf->flags |= BNA_RXF_F_PAUSED;
+ call_rxf_start_cbfn(rxf);
+ bfa_fsm_set_state(rxf, bna_rxf_sm_fltr_clr_wait);
+ break;
+
+ case RXF_E_FW_RESP:
+ if (!bna_rxf_cfg_apply(rxf)) {
+ /* No more pending config updates */
+ bfa_fsm_set_state(rxf, bna_rxf_sm_started);
+ }
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_rxf_sm_started_entry(struct bna_rxf *rxf)
+{
+ call_rxf_start_cbfn(rxf);
+ call_rxf_cam_fltr_cbfn(rxf);
+ call_rxf_resume_cbfn(rxf);
+}
+
+static void
+bna_rxf_sm_started(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+ switch (event) {
+ case RXF_E_STOP:
+ case RXF_E_FAIL:
+ bna_rxf_cfg_reset(rxf);
+ bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+ break;
+
+ case RXF_E_CONFIG:
+ bfa_fsm_set_state(rxf, bna_rxf_sm_cfg_wait);
+ break;
+
+ case RXF_E_PAUSE:
+ rxf->flags |= BNA_RXF_F_PAUSED;
+ if (!bna_rxf_fltr_clear(rxf))
+ bfa_fsm_set_state(rxf, bna_rxf_sm_paused);
+ else
+ bfa_fsm_set_state(rxf, bna_rxf_sm_fltr_clr_wait);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_rxf_sm_fltr_clr_wait_entry(struct bna_rxf *rxf)
+{
+}
+
+static void
+bna_rxf_sm_fltr_clr_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+ switch (event) {
+ case RXF_E_FAIL:
+ bna_rxf_cfg_reset(rxf);
+ call_rxf_pause_cbfn(rxf);
+ bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+ break;
+
+ case RXF_E_FW_RESP:
+ if (!bna_rxf_fltr_clear(rxf)) {
+ /* No more pending CAM entries to clear */
+ bfa_fsm_set_state(rxf, bna_rxf_sm_paused);
+ }
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_rxf_sm_last_resp_wait_entry(struct bna_rxf *rxf)
+{
+}
+
+static void
+bna_rxf_sm_last_resp_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+ switch (event) {
+ case RXF_E_FAIL:
+ case RXF_E_FW_RESP:
+ bna_rxf_cfg_reset(rxf);
+ bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_bfi_ucast_req(struct bna_rxf *rxf, struct bna_mac *mac,
+ enum bfi_enet_h2i_msgs req_type)
+{
+ struct bfi_enet_ucast_req *req = &rxf->bfi_enet_cmd.ucast_req;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET, req_type, 0, rxf->rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_ucast_req)));
+ memcpy(&req->mac_addr, &mac->addr, sizeof(mac_t));
+ bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_ucast_req), &req->mh);
+ bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_mcast_add_req(struct bna_rxf *rxf, struct bna_mac *mac)
+{
+ struct bfi_enet_mcast_add_req *req =
+ &rxf->bfi_enet_cmd.mcast_add_req;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET, BFI_ENET_H2I_MAC_MCAST_ADD_REQ,
+ 0, rxf->rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_mcast_add_req)));
+ memcpy(&req->mac_addr, &mac->addr, sizeof(mac_t));
+ bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_mcast_add_req), &req->mh);
+ bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_mcast_del_req(struct bna_rxf *rxf, u16 handle)
+{
+ struct bfi_enet_mcast_del_req *req =
+ &rxf->bfi_enet_cmd.mcast_del_req;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET, BFI_ENET_H2I_MAC_MCAST_DEL_REQ,
+ 0, rxf->rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_mcast_del_req)));
+ req->handle = htons(handle);
+ bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_mcast_del_req), &req->mh);
+ bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_mcast_filter_req(struct bna_rxf *rxf, enum bna_status status)
+{
+ struct bfi_enet_enable_req *req = &rxf->bfi_enet_cmd.req;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_MAC_MCAST_FILTER_REQ, 0, rxf->rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+ req->enable = status;
+ bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_enable_req), &req->mh);
+ bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_rx_promisc_req(struct bna_rxf *rxf, enum bna_status status)
+{
+ struct bfi_enet_enable_req *req = &rxf->bfi_enet_cmd.req;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_RX_PROMISCUOUS_REQ, 0, rxf->rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+ req->enable = status;
+ bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_enable_req), &req->mh);
+ bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_rx_vlan_filter_set(struct bna_rxf *rxf, u8 block_idx)
+{
+ struct bfi_enet_rx_vlan_req *req = &rxf->bfi_enet_cmd.vlan_req;
+ int i;
+ int j;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_RX_VLAN_SET_REQ, 0, rxf->rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_rx_vlan_req)));
+ req->block_idx = block_idx;
+ for (i = 0; i < (BFI_ENET_VLAN_BLOCK_SIZE / 32); i++) {
+ j = (block_idx * (BFI_ENET_VLAN_BLOCK_SIZE / 32)) + i;
+ if (rxf->vlan_filter_status == BNA_STATUS_T_ENABLED)
+ req->bit_mask[i] =
+ htonl(rxf->vlan_filter_table[j]);
+ else
+ req->bit_mask[i] = 0xFFFFFFFF;
+ }
+ bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_rx_vlan_req), &req->mh);
+ bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_vlan_strip_enable(struct bna_rxf *rxf)
+{
+ struct bfi_enet_enable_req *req = &rxf->bfi_enet_cmd.req;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_RX_VLAN_STRIP_ENABLE_REQ, 0, rxf->rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+ req->enable = rxf->vlan_strip_status;
+ bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_enable_req), &req->mh);
+ bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_rit_cfg(struct bna_rxf *rxf)
+{
+ struct bfi_enet_rit_req *req = &rxf->bfi_enet_cmd.rit_req;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_RIT_CFG_REQ, 0, rxf->rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_rit_req)));
+ req->size = htons(rxf->rit_size);
+ memcpy(&req->table[0], rxf->rit, rxf->rit_size);
+ bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_rit_req), &req->mh);
+ bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_rss_cfg(struct bna_rxf *rxf)
+{
+ struct bfi_enet_rss_cfg_req *req = &rxf->bfi_enet_cmd.rss_req;
+ int i;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_RSS_CFG_REQ, 0, rxf->rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_rss_cfg_req)));
+ req->cfg.type = rxf->rss_cfg.hash_type;
+ req->cfg.mask = rxf->rss_cfg.hash_mask;
+ for (i = 0; i < BFI_ENET_RSS_KEY_LEN; i++)
+ req->cfg.key[i] =
+ htonl(rxf->rss_cfg.toeplitz_hash_key[i]);
+ bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_rss_cfg_req), &req->mh);
+ bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_rss_enable(struct bna_rxf *rxf)
+{
+ struct bfi_enet_enable_req *req = &rxf->bfi_enet_cmd.req;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_RSS_ENABLE_REQ, 0, rxf->rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+ req->enable = rxf->rss_status;
+ bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_enable_req), &req->mh);
+ bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+/* This function gets the multicast MAC that has already been added to CAM */
+static struct bna_mac *
+bna_rxf_mcmac_get(struct bna_rxf *rxf, u8 *mac_addr)
+{
+ struct bna_mac *mac;
+ struct list_head *qe;
+
+ list_for_each(qe, &rxf->mcast_active_q) {
+ mac = (struct bna_mac *)qe;
+ if (BNA_MAC_IS_EQUAL(&mac->addr, mac_addr))
+ return mac;
+ }
+
+ list_for_each(qe, &rxf->mcast_pending_del_q) {
+ mac = (struct bna_mac *)qe;
+ if (BNA_MAC_IS_EQUAL(&mac->addr, mac_addr))
+ return mac;
+ }
+
+ return NULL;
+}
+
+static struct bna_mcam_handle *
+bna_rxf_mchandle_get(struct bna_rxf *rxf, int handle)
+{
+ struct bna_mcam_handle *mchandle;
+ struct list_head *qe;
+
+ list_for_each(qe, &rxf->mcast_handle_q) {
+ mchandle = (struct bna_mcam_handle *)qe;
+ if (mchandle->handle == handle)
+ return mchandle;
+ }
+
+ return NULL;
+}
+
+static void
+bna_rxf_mchandle_attach(struct bna_rxf *rxf, u8 *mac_addr, int handle)
+{
+ struct bna_mac *mcmac;
+ struct bna_mcam_handle *mchandle;
+
+ mcmac = bna_rxf_mcmac_get(rxf, mac_addr);
+ mchandle = bna_rxf_mchandle_get(rxf, handle);
+ if (mchandle == NULL) {
+ mchandle = bna_mcam_mod_handle_get(&rxf->rx->bna->mcam_mod);
+ mchandle->handle = handle;
+ mchandle->refcnt = 0;
+ list_add_tail(&mchandle->qe, &rxf->mcast_handle_q);
+ }
+ mchandle->refcnt++;
+ mcmac->handle = mchandle;
+}
+
+static int
+bna_rxf_mcast_del(struct bna_rxf *rxf, struct bna_mac *mac,
+ enum bna_cleanup_type cleanup)
+{
+ struct bna_mcam_handle *mchandle;
+ int ret = 0;
+
+ mchandle = mac->handle;
+ if (mchandle == NULL)
+ return ret;
+
+ mchandle->refcnt--;
+ if (mchandle->refcnt == 0) {
+ if (cleanup == BNA_HARD_CLEANUP) {
+ bna_bfi_mcast_del_req(rxf, mchandle->handle);
+ ret = 1;
+ }
+ list_del(&mchandle->qe);
+ bfa_q_qe_init(&mchandle->qe);
+ bna_mcam_mod_handle_put(&rxf->rx->bna->mcam_mod, mchandle);
+ }
+ mac->handle = NULL;
+
+ return ret;
+}
+
+static int
+bna_rxf_mcast_cfg_apply(struct bna_rxf *rxf)
+{
+ struct bna_mac *mac = NULL;
+ struct list_head *qe;
+ int ret;
+
+ /* Delete multicast entries previousely added */
+ while (!list_empty(&rxf->mcast_pending_del_q)) {
+ bfa_q_deq(&rxf->mcast_pending_del_q, &qe);
+ bfa_q_qe_init(qe);
+ mac = (struct bna_mac *)qe;
+ ret = bna_rxf_mcast_del(rxf, mac, BNA_HARD_CLEANUP);
+ bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+ if (ret)
+ return ret;
+ }
+
+ /* Add multicast entries */
+ if (!list_empty(&rxf->mcast_pending_add_q)) {
+ bfa_q_deq(&rxf->mcast_pending_add_q, &qe);
+ bfa_q_qe_init(qe);
+ mac = (struct bna_mac *)qe;
+ list_add_tail(&mac->qe, &rxf->mcast_active_q);
+ bna_bfi_mcast_add_req(rxf, mac);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+bna_rxf_vlan_cfg_apply(struct bna_rxf *rxf)
+{
+ u8 vlan_pending_bitmask;
+ int block_idx = 0;
+
+ if (rxf->vlan_pending_bitmask) {
+ vlan_pending_bitmask = rxf->vlan_pending_bitmask;
+ while (!(vlan_pending_bitmask & 0x1)) {
+ block_idx++;
+ vlan_pending_bitmask >>= 1;
+ }
+ rxf->vlan_pending_bitmask &= ~(1 << block_idx);
+ bna_bfi_rx_vlan_filter_set(rxf, block_idx);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+bna_rxf_mcast_cfg_reset(struct bna_rxf *rxf, enum bna_cleanup_type cleanup)
+{
+ struct list_head *qe;
+ struct bna_mac *mac;
+ int ret;
+
+ /* Throw away delete pending mcast entries */
+ while (!list_empty(&rxf->mcast_pending_del_q)) {
+ bfa_q_deq(&rxf->mcast_pending_del_q, &qe);
+ bfa_q_qe_init(qe);
+ mac = (struct bna_mac *)qe;
+ ret = bna_rxf_mcast_del(rxf, mac, cleanup);
+ bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+ if (ret)
+ return ret;
+ }
+
+ /* Move active mcast entries to pending_add_q */
+ while (!list_empty(&rxf->mcast_active_q)) {
+ bfa_q_deq(&rxf->mcast_active_q, &qe);
+ bfa_q_qe_init(qe);
+ list_add_tail(qe, &rxf->mcast_pending_add_q);
+ mac = (struct bna_mac *)qe;
+ if (bna_rxf_mcast_del(rxf, mac, cleanup))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+bna_rxf_rss_cfg_apply(struct bna_rxf *rxf)
+{
+ if (rxf->rss_pending) {
+ if (rxf->rss_pending & BNA_RSS_F_RIT_PENDING) {
+ rxf->rss_pending &= ~BNA_RSS_F_RIT_PENDING;
+ bna_bfi_rit_cfg(rxf);
+ return 1;
+ }
+
+ if (rxf->rss_pending & BNA_RSS_F_CFG_PENDING) {
+ rxf->rss_pending &= ~BNA_RSS_F_CFG_PENDING;
+ bna_bfi_rss_cfg(rxf);
+ return 1;
+ }
+
+ if (rxf->rss_pending & BNA_RSS_F_STATUS_PENDING) {
+ rxf->rss_pending &= ~BNA_RSS_F_STATUS_PENDING;
+ bna_bfi_rss_enable(rxf);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+bna_rxf_cfg_apply(struct bna_rxf *rxf)
+{
+ if (bna_rxf_ucast_cfg_apply(rxf))
+ return 1;
+
+ if (bna_rxf_mcast_cfg_apply(rxf))
+ return 1;
+
+ if (bna_rxf_promisc_cfg_apply(rxf))
+ return 1;
+
+ if (bna_rxf_allmulti_cfg_apply(rxf))
+ return 1;
+
+ if (bna_rxf_vlan_cfg_apply(rxf))
+ return 1;
+
+ if (bna_rxf_vlan_strip_cfg_apply(rxf))
+ return 1;
+
+ if (bna_rxf_rss_cfg_apply(rxf))
+ return 1;
+
+ return 0;
+}
+
+/* Only software reset */
+static int
+bna_rxf_fltr_clear(struct bna_rxf *rxf)
+{
+ if (bna_rxf_ucast_cfg_reset(rxf, BNA_HARD_CLEANUP))
+ return 1;
+
+ if (bna_rxf_mcast_cfg_reset(rxf, BNA_HARD_CLEANUP))
+ return 1;
+
+ if (bna_rxf_promisc_cfg_reset(rxf, BNA_HARD_CLEANUP))
+ return 1;
+
+ if (bna_rxf_allmulti_cfg_reset(rxf, BNA_HARD_CLEANUP))
+ return 1;
+
+ return 0;
+}
+
+static void
+bna_rxf_cfg_reset(struct bna_rxf *rxf)
+{
+ bna_rxf_ucast_cfg_reset(rxf, BNA_SOFT_CLEANUP);
+ bna_rxf_mcast_cfg_reset(rxf, BNA_SOFT_CLEANUP);
+ bna_rxf_promisc_cfg_reset(rxf, BNA_SOFT_CLEANUP);
+ bna_rxf_allmulti_cfg_reset(rxf, BNA_SOFT_CLEANUP);
+ bna_rxf_vlan_cfg_soft_reset(rxf);
+ bna_rxf_rss_cfg_soft_reset(rxf);
+}
+
+static void
+bna_rit_init(struct bna_rxf *rxf, int rit_size)
+{
+ struct bna_rx *rx = rxf->rx;
+ struct bna_rxp *rxp;
+ struct list_head *qe;
+ int offset = 0;
+
+ rxf->rit_size = rit_size;
+ list_for_each(qe, &rx->rxp_q) {
+ rxp = (struct bna_rxp *)qe;
+ rxf->rit[offset] = rxp->cq.ccb->id;
+ offset++;
+ }
+
+}
+
+void
+bna_bfi_rxf_cfg_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr)
+{
+ bfa_fsm_send_event(rxf, RXF_E_FW_RESP);
+}
+
+void
+bna_bfi_rxf_mcast_add_rsp(struct bna_rxf *rxf,
+ struct bfi_msgq_mhdr *msghdr)
+{
+ struct bfi_enet_mcast_add_req *req =
+ &rxf->bfi_enet_cmd.mcast_add_req;
+ struct bfi_enet_mcast_add_rsp *rsp =
+ (struct bfi_enet_mcast_add_rsp *)msghdr;
+
+ bna_rxf_mchandle_attach(rxf, (u8 *)&req->mac_addr,
+ ntohs(rsp->handle));
+ bfa_fsm_send_event(rxf, RXF_E_FW_RESP);
+}
+
+static void
+bna_rxf_init(struct bna_rxf *rxf,
+ struct bna_rx *rx,
+ struct bna_rx_config *q_config,
+ struct bna_res_info *res_info)
+{
+ rxf->rx = rx;
+
+ INIT_LIST_HEAD(&rxf->ucast_pending_add_q);
+ INIT_LIST_HEAD(&rxf->ucast_pending_del_q);
+ rxf->ucast_pending_set = 0;
+ rxf->ucast_active_set = 0;
+ INIT_LIST_HEAD(&rxf->ucast_active_q);
+ rxf->ucast_pending_mac = NULL;
+
+ INIT_LIST_HEAD(&rxf->mcast_pending_add_q);
+ INIT_LIST_HEAD(&rxf->mcast_pending_del_q);
+ INIT_LIST_HEAD(&rxf->mcast_active_q);
+ INIT_LIST_HEAD(&rxf->mcast_handle_q);
+
+ if (q_config->paused)
+ rxf->flags |= BNA_RXF_F_PAUSED;
+
+ rxf->rit = (u8 *)
+ res_info[BNA_RX_RES_MEM_T_RIT].res_u.mem_info.mdl[0].kva;
+ bna_rit_init(rxf, q_config->num_paths);
+
+ rxf->rss_status = q_config->rss_status;
+ if (rxf->rss_status == BNA_STATUS_T_ENABLED) {
+ rxf->rss_cfg = q_config->rss_config;
+ rxf->rss_pending |= BNA_RSS_F_CFG_PENDING;
+ rxf->rss_pending |= BNA_RSS_F_RIT_PENDING;
+ rxf->rss_pending |= BNA_RSS_F_STATUS_PENDING;
+ }
+
+ rxf->vlan_filter_status = BNA_STATUS_T_DISABLED;
+ memset(rxf->vlan_filter_table, 0,
+ (sizeof(u32) * (BFI_ENET_VLAN_ID_MAX / 32)));
+ rxf->vlan_filter_table[0] |= 1; /* for pure priority tagged frames */
+ rxf->vlan_pending_bitmask = (u8)BFI_VLAN_BMASK_ALL;
+
+ rxf->vlan_strip_status = q_config->vlan_strip_status;
+
+ bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+}
+
+static void
+bna_rxf_uninit(struct bna_rxf *rxf)
+{
+ struct bna_mac *mac;
+
+ rxf->ucast_pending_set = 0;
+ rxf->ucast_active_set = 0;
+
+ while (!list_empty(&rxf->ucast_pending_add_q)) {
+ bfa_q_deq(&rxf->ucast_pending_add_q, &mac);
+ bfa_q_qe_init(&mac->qe);
+ bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
+ }
+
+ if (rxf->ucast_pending_mac) {
+ bfa_q_qe_init(&rxf->ucast_pending_mac->qe);
+ bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod,
+ rxf->ucast_pending_mac);
+ rxf->ucast_pending_mac = NULL;
+ }
+
+ while (!list_empty(&rxf->mcast_pending_add_q)) {
+ bfa_q_deq(&rxf->mcast_pending_add_q, &mac);
+ bfa_q_qe_init(&mac->qe);
+ bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+ }
+
+ rxf->rxmode_pending = 0;
+ rxf->rxmode_pending_bitmask = 0;
+ if (rxf->rx->bna->promisc_rid == rxf->rx->rid)
+ rxf->rx->bna->promisc_rid = BFI_INVALID_RID;
+ if (rxf->rx->bna->default_mode_rid == rxf->rx->rid)
+ rxf->rx->bna->default_mode_rid = BFI_INVALID_RID;
+
+ rxf->rss_pending = 0;
+ rxf->vlan_strip_pending = false;
+
+ rxf->flags = 0;
+
+ rxf->rx = NULL;
+}
+
+static void
+bna_rx_cb_rxf_started(struct bna_rx *rx)
+{
+ bfa_fsm_send_event(rx, RX_E_RXF_STARTED);
+}
+
+static void
+bna_rxf_start(struct bna_rxf *rxf)
+{
+ rxf->start_cbfn = bna_rx_cb_rxf_started;
+ rxf->start_cbarg = rxf->rx;
+ bfa_fsm_send_event(rxf, RXF_E_START);
+}
+
+static void
+bna_rx_cb_rxf_stopped(struct bna_rx *rx)
+{
+ bfa_fsm_send_event(rx, RX_E_RXF_STOPPED);
+}
+
+static void
+bna_rxf_stop(struct bna_rxf *rxf)
+{
+ rxf->stop_cbfn = bna_rx_cb_rxf_stopped;
+ rxf->stop_cbarg = rxf->rx;
+ bfa_fsm_send_event(rxf, RXF_E_STOP);
+}
+
+static void
+bna_rxf_fail(struct bna_rxf *rxf)
+{
+ bfa_fsm_send_event(rxf, RXF_E_FAIL);
+}
+
+enum bna_cb_status
+bna_rx_ucast_set(struct bna_rx *rx, u8 *ucmac,
+ void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+ struct bna_rxf *rxf = &rx->rxf;
+
+ if (rxf->ucast_pending_mac == NULL) {
+ rxf->ucast_pending_mac =
+ bna_ucam_mod_mac_get(&rxf->rx->bna->ucam_mod);
+ if (rxf->ucast_pending_mac == NULL)
+ return BNA_CB_UCAST_CAM_FULL;
+ bfa_q_qe_init(&rxf->ucast_pending_mac->qe);
+ }
+
+ memcpy(rxf->ucast_pending_mac->addr, ucmac, ETH_ALEN);
+ rxf->ucast_pending_set = 1;
+ rxf->cam_fltr_cbfn = cbfn;
+ rxf->cam_fltr_cbarg = rx->bna->bnad;
+
+ bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+
+ return BNA_CB_SUCCESS;
+}
+
+enum bna_cb_status
+bna_rx_mcast_add(struct bna_rx *rx, u8 *addr,
+ void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+ struct bna_rxf *rxf = &rx->rxf;
+ struct bna_mac *mac;
+
+ /* Check if already added or pending addition */
+ if (bna_mac_find(&rxf->mcast_active_q, addr) ||
+ bna_mac_find(&rxf->mcast_pending_add_q, addr)) {
+ if (cbfn)
+ cbfn(rx->bna->bnad, rx);
+ return BNA_CB_SUCCESS;
+ }
+
+ mac = bna_mcam_mod_mac_get(&rxf->rx->bna->mcam_mod);
+ if (mac == NULL)
+ return BNA_CB_MCAST_LIST_FULL;
+ bfa_q_qe_init(&mac->qe);
+ memcpy(mac->addr, addr, ETH_ALEN);
+ list_add_tail(&mac->qe, &rxf->mcast_pending_add_q);
+
+ rxf->cam_fltr_cbfn = cbfn;
+ rxf->cam_fltr_cbarg = rx->bna->bnad;
+
+ bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+
+ return BNA_CB_SUCCESS;
+}
+
+enum bna_cb_status
+bna_rx_mcast_listset(struct bna_rx *rx, int count, u8 *mclist,
+ void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+ struct bna_rxf *rxf = &rx->rxf;
+ struct list_head list_head;
+ struct list_head *qe;
+ u8 *mcaddr;
+ struct bna_mac *mac;
+ int i;
+
+ /* Allocate nodes */
+ INIT_LIST_HEAD(&list_head);
+ for (i = 0, mcaddr = mclist; i < count; i++) {
+ mac = bna_mcam_mod_mac_get(&rxf->rx->bna->mcam_mod);
+ if (mac == NULL)
+ goto err_return;
+ bfa_q_qe_init(&mac->qe);
+ memcpy(mac->addr, mcaddr, ETH_ALEN);
+ list_add_tail(&mac->qe, &list_head);
+
+ mcaddr += ETH_ALEN;
+ }
+
+ /* Purge the pending_add_q */
+ while (!list_empty(&rxf->mcast_pending_add_q)) {
+ bfa_q_deq(&rxf->mcast_pending_add_q, &qe);
+ bfa_q_qe_init(qe);
+ mac = (struct bna_mac *)qe;
+ bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+ }
+
+ /* Schedule active_q entries for deletion */
+ while (!list_empty(&rxf->mcast_active_q)) {
+ bfa_q_deq(&rxf->mcast_active_q, &qe);
+ mac = (struct bna_mac *)qe;
+ bfa_q_qe_init(&mac->qe);
+ list_add_tail(&mac->qe, &rxf->mcast_pending_del_q);
+ }
+
+ /* Add the new entries */
+ while (!list_empty(&list_head)) {
+ bfa_q_deq(&list_head, &qe);
+ mac = (struct bna_mac *)qe;
+ bfa_q_qe_init(&mac->qe);
+ list_add_tail(&mac->qe, &rxf->mcast_pending_add_q);
+ }
+
+ rxf->cam_fltr_cbfn = cbfn;
+ rxf->cam_fltr_cbarg = rx->bna->bnad;
+ bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+
+ return BNA_CB_SUCCESS;
+
+err_return:
+ while (!list_empty(&list_head)) {
+ bfa_q_deq(&list_head, &qe);
+ mac = (struct bna_mac *)qe;
+ bfa_q_qe_init(&mac->qe);
+ bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+ }
+
+ return BNA_CB_MCAST_LIST_FULL;
+}
+
+void
+bna_rx_vlan_add(struct bna_rx *rx, int vlan_id)
+{
+ struct bna_rxf *rxf = &rx->rxf;
+ int index = (vlan_id >> BFI_VLAN_WORD_SHIFT);
+ int bit = (1 << (vlan_id & BFI_VLAN_WORD_MASK));
+ int group_id = (vlan_id >> BFI_VLAN_BLOCK_SHIFT);
+
+ rxf->vlan_filter_table[index] |= bit;
+ if (rxf->vlan_filter_status == BNA_STATUS_T_ENABLED) {
+ rxf->vlan_pending_bitmask |= (1 << group_id);
+ bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+ }
+}
+
+void
+bna_rx_vlan_del(struct bna_rx *rx, int vlan_id)
+{
+ struct bna_rxf *rxf = &rx->rxf;
+ int index = (vlan_id >> BFI_VLAN_WORD_SHIFT);
+ int bit = (1 << (vlan_id & BFI_VLAN_WORD_MASK));
+ int group_id = (vlan_id >> BFI_VLAN_BLOCK_SHIFT);
+
+ rxf->vlan_filter_table[index] &= ~bit;
+ if (rxf->vlan_filter_status == BNA_STATUS_T_ENABLED) {
+ rxf->vlan_pending_bitmask |= (1 << group_id);
+ bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+ }
+}
+
+static int
+bna_rxf_ucast_cfg_apply(struct bna_rxf *rxf)
+{
+ struct bna_mac *mac = NULL;
+ struct list_head *qe;
+
+ /* Delete MAC addresses previousely added */
+ if (!list_empty(&rxf->ucast_pending_del_q)) {
+ bfa_q_deq(&rxf->ucast_pending_del_q, &qe);
+ bfa_q_qe_init(qe);
+ mac = (struct bna_mac *)qe;
+ bna_bfi_ucast_req(rxf, mac, BFI_ENET_H2I_MAC_UCAST_DEL_REQ);
+ bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
+ return 1;
+ }
+
+ /* Set default unicast MAC */
+ if (rxf->ucast_pending_set) {
+ rxf->ucast_pending_set = 0;
+ memcpy(rxf->ucast_active_mac.addr,
+ rxf->ucast_pending_mac->addr, ETH_ALEN);
+ rxf->ucast_active_set = 1;
+ bna_bfi_ucast_req(rxf, &rxf->ucast_active_mac,
+ BFI_ENET_H2I_MAC_UCAST_SET_REQ);
+ return 1;
+ }
+
+ /* Add additional MAC entries */
+ if (!list_empty(&rxf->ucast_pending_add_q)) {
+ bfa_q_deq(&rxf->ucast_pending_add_q, &qe);
+ bfa_q_qe_init(qe);
+ mac = (struct bna_mac *)qe;
+ list_add_tail(&mac->qe, &rxf->ucast_active_q);
+ bna_bfi_ucast_req(rxf, mac, BFI_ENET_H2I_MAC_UCAST_ADD_REQ);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+bna_rxf_ucast_cfg_reset(struct bna_rxf *rxf, enum bna_cleanup_type cleanup)
+{
+ struct list_head *qe;
+ struct bna_mac *mac;
+
+ /* Throw away delete pending ucast entries */
+ while (!list_empty(&rxf->ucast_pending_del_q)) {
+ bfa_q_deq(&rxf->ucast_pending_del_q, &qe);
+ bfa_q_qe_init(qe);
+ mac = (struct bna_mac *)qe;
+ if (cleanup == BNA_SOFT_CLEANUP)
+ bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
+ else {
+ bna_bfi_ucast_req(rxf, mac,
+ BFI_ENET_H2I_MAC_UCAST_DEL_REQ);
+ bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
+ return 1;
+ }
+ }
+
+ /* Move active ucast entries to pending_add_q */
+ while (!list_empty(&rxf->ucast_active_q)) {
+ bfa_q_deq(&rxf->ucast_active_q, &qe);
+ bfa_q_qe_init(qe);
+ list_add_tail(qe, &rxf->ucast_pending_add_q);
+ if (cleanup == BNA_HARD_CLEANUP) {
+ mac = (struct bna_mac *)qe;
+ bna_bfi_ucast_req(rxf, mac,
+ BFI_ENET_H2I_MAC_UCAST_DEL_REQ);
+ return 1;
+ }
+ }
+
+ if (rxf->ucast_active_set) {
+ rxf->ucast_pending_set = 1;
+ rxf->ucast_active_set = 0;
+ if (cleanup == BNA_HARD_CLEANUP) {
+ bna_bfi_ucast_req(rxf, &rxf->ucast_active_mac,
+ BFI_ENET_H2I_MAC_UCAST_CLR_REQ);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+bna_rxf_promisc_cfg_apply(struct bna_rxf *rxf)
+{
+ struct bna *bna = rxf->rx->bna;
+
+ /* Enable/disable promiscuous mode */
+ if (is_promisc_enable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask)) {
+ /* move promisc configuration from pending -> active */
+ promisc_inactive(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ rxf->rxmode_active |= BNA_RXMODE_PROMISC;
+ bna_bfi_rx_promisc_req(rxf, BNA_STATUS_T_ENABLED);
+ return 1;
+ } else if (is_promisc_disable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask)) {
+ /* move promisc configuration from pending -> active */
+ promisc_inactive(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
+ bna->promisc_rid = BFI_INVALID_RID;
+ bna_bfi_rx_promisc_req(rxf, BNA_STATUS_T_DISABLED);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+bna_rxf_promisc_cfg_reset(struct bna_rxf *rxf, enum bna_cleanup_type cleanup)
+{
+ struct bna *bna = rxf->rx->bna;
+
+ /* Clear pending promisc mode disable */
+ if (is_promisc_disable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask)) {
+ promisc_inactive(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
+ bna->promisc_rid = BFI_INVALID_RID;
+ if (cleanup == BNA_HARD_CLEANUP) {
+ bna_bfi_rx_promisc_req(rxf, BNA_STATUS_T_DISABLED);
+ return 1;
+ }
+ }
+
+ /* Move promisc mode config from active -> pending */
+ if (rxf->rxmode_active & BNA_RXMODE_PROMISC) {
+ promisc_enable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
+ if (cleanup == BNA_HARD_CLEANUP) {
+ bna_bfi_rx_promisc_req(rxf, BNA_STATUS_T_DISABLED);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+bna_rxf_allmulti_cfg_apply(struct bna_rxf *rxf)
+{
+ /* Enable/disable allmulti mode */
+ if (is_allmulti_enable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask)) {
+ /* move allmulti configuration from pending -> active */
+ allmulti_inactive(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ rxf->rxmode_active |= BNA_RXMODE_ALLMULTI;
+ bna_bfi_mcast_filter_req(rxf, BNA_STATUS_T_DISABLED);
+ return 1;
+ } else if (is_allmulti_disable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask)) {
+ /* move allmulti configuration from pending -> active */
+ allmulti_inactive(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
+ bna_bfi_mcast_filter_req(rxf, BNA_STATUS_T_ENABLED);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+bna_rxf_allmulti_cfg_reset(struct bna_rxf *rxf, enum bna_cleanup_type cleanup)
+{
+ /* Clear pending allmulti mode disable */
+ if (is_allmulti_disable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask)) {
+ allmulti_inactive(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
+ if (cleanup == BNA_HARD_CLEANUP) {
+ bna_bfi_mcast_filter_req(rxf, BNA_STATUS_T_ENABLED);
+ return 1;
+ }
+ }
+
+ /* Move allmulti mode config from active -> pending */
+ if (rxf->rxmode_active & BNA_RXMODE_ALLMULTI) {
+ allmulti_enable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
+ if (cleanup == BNA_HARD_CLEANUP) {
+ bna_bfi_mcast_filter_req(rxf, BNA_STATUS_T_ENABLED);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+bna_rxf_promisc_enable(struct bna_rxf *rxf)
+{
+ struct bna *bna = rxf->rx->bna;
+ int ret = 0;
+
+ if (is_promisc_enable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask) ||
+ (rxf->rxmode_active & BNA_RXMODE_PROMISC)) {
+ /* Do nothing if pending enable or already enabled */
+ } else if (is_promisc_disable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask)) {
+ /* Turn off pending disable command */
+ promisc_inactive(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ } else {
+ /* Schedule enable */
+ promisc_enable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ bna->promisc_rid = rxf->rx->rid;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int
+bna_rxf_promisc_disable(struct bna_rxf *rxf)
+{
+ struct bna *bna = rxf->rx->bna;
+ int ret = 0;
+
+ if (is_promisc_disable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask) ||
+ (!(rxf->rxmode_active & BNA_RXMODE_PROMISC))) {
+ /* Do nothing if pending disable or already disabled */
+ } else if (is_promisc_enable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask)) {
+ /* Turn off pending enable command */
+ promisc_inactive(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ bna->promisc_rid = BFI_INVALID_RID;
+ } else if (rxf->rxmode_active & BNA_RXMODE_PROMISC) {
+ /* Schedule disable */
+ promisc_disable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int
+bna_rxf_allmulti_enable(struct bna_rxf *rxf)
+{
+ int ret = 0;
+
+ if (is_allmulti_enable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask) ||
+ (rxf->rxmode_active & BNA_RXMODE_ALLMULTI)) {
+ /* Do nothing if pending enable or already enabled */
+ } else if (is_allmulti_disable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask)) {
+ /* Turn off pending disable command */
+ allmulti_inactive(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ } else {
+ /* Schedule enable */
+ allmulti_enable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int
+bna_rxf_allmulti_disable(struct bna_rxf *rxf)
+{
+ int ret = 0;
+
+ if (is_allmulti_disable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask) ||
+ (!(rxf->rxmode_active & BNA_RXMODE_ALLMULTI))) {
+ /* Do nothing if pending disable or already disabled */
+ } else if (is_allmulti_enable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask)) {
+ /* Turn off pending enable command */
+ allmulti_inactive(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ } else if (rxf->rxmode_active & BNA_RXMODE_ALLMULTI) {
+ /* Schedule disable */
+ allmulti_disable(rxf->rxmode_pending,
+ rxf->rxmode_pending_bitmask);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int
+bna_rxf_vlan_strip_cfg_apply(struct bna_rxf *rxf)
+{
+ if (rxf->vlan_strip_pending) {
+ rxf->vlan_strip_pending = false;
+ bna_bfi_vlan_strip_enable(rxf);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * RX
+ */
+
+#define BNA_GET_RXQS(qcfg) (((qcfg)->rxp_type == BNA_RXP_SINGLE) ? \
+ (qcfg)->num_paths : ((qcfg)->num_paths * 2))
+
+#define SIZE_TO_PAGES(size) (((size) >> PAGE_SHIFT) + ((((size) &\
+ (PAGE_SIZE - 1)) + (PAGE_SIZE - 1)) >> PAGE_SHIFT))
+
+#define call_rx_stop_cbfn(rx) \
+do { \
+ if ((rx)->stop_cbfn) { \
+ void (*cbfn)(void *, struct bna_rx *); \
+ void *cbarg; \
+ cbfn = (rx)->stop_cbfn; \
+ cbarg = (rx)->stop_cbarg; \
+ (rx)->stop_cbfn = NULL; \
+ (rx)->stop_cbarg = NULL; \
+ cbfn(cbarg, rx); \
+ } \
+} while (0)
+
+#define call_rx_stall_cbfn(rx) \
+do { \
+ if ((rx)->rx_stall_cbfn) \
+ (rx)->rx_stall_cbfn((rx)->bna->bnad, (rx)); \
+} while (0)
+
+#define bfi_enet_datapath_q_init(bfi_q, bna_qpt) \
+do { \
+ struct bna_dma_addr cur_q_addr = \
+ *((struct bna_dma_addr *)((bna_qpt)->kv_qpt_ptr)); \
+ (bfi_q)->pg_tbl.a32.addr_lo = (bna_qpt)->hw_qpt_ptr.lsb; \
+ (bfi_q)->pg_tbl.a32.addr_hi = (bna_qpt)->hw_qpt_ptr.msb; \
+ (bfi_q)->first_entry.a32.addr_lo = cur_q_addr.lsb; \
+ (bfi_q)->first_entry.a32.addr_hi = cur_q_addr.msb; \
+ (bfi_q)->pages = htons((u16)(bna_qpt)->page_count); \
+ (bfi_q)->page_sz = htons((u16)(bna_qpt)->page_size);\
+} while (0)
+
+static void bna_bfi_rx_enet_start(struct bna_rx *rx);
+static void bna_rx_enet_stop(struct bna_rx *rx);
+static void bna_rx_mod_cb_rx_stopped(void *arg, struct bna_rx *rx);
+
+bfa_fsm_state_decl(bna_rx, stopped,
+ struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, start_wait,
+ struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, rxf_start_wait,
+ struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, started,
+ struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, rxf_stop_wait,
+ struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, stop_wait,
+ struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, cleanup_wait,
+ struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, failed,
+ struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, quiesce_wait,
+ struct bna_rx, enum bna_rx_event);
+
+static void bna_rx_sm_stopped_entry(struct bna_rx *rx)
+{
+ call_rx_stop_cbfn(rx);
+}
+
+static void bna_rx_sm_stopped(struct bna_rx *rx,
+ enum bna_rx_event event)
+{
+ switch (event) {
+ case RX_E_START:
+ bfa_fsm_set_state(rx, bna_rx_sm_start_wait);
+ break;
+
+ case RX_E_STOP:
+ call_rx_stop_cbfn(rx);
+ break;
+
+ case RX_E_FAIL:
+ /* no-op */
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ break;
+ }
+}
+
+static void bna_rx_sm_start_wait_entry(struct bna_rx *rx)
+{
+ bna_bfi_rx_enet_start(rx);
+}
+
+void
+bna_rx_sm_stop_wait_entry(struct bna_rx *rx)
+{
+}
+
+static void
+bna_rx_sm_stop_wait(struct bna_rx *rx, enum bna_rx_event event)
+{
+ switch (event) {
+ case RX_E_FAIL:
+ case RX_E_STOPPED:
+ bfa_fsm_set_state(rx, bna_rx_sm_cleanup_wait);
+ rx->rx_cleanup_cbfn(rx->bna->bnad, rx);
+ break;
+
+ case RX_E_STARTED:
+ bna_rx_enet_stop(rx);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ break;
+ }
+}
+
+static void bna_rx_sm_start_wait(struct bna_rx *rx,
+ enum bna_rx_event event)
+{
+ switch (event) {
+ case RX_E_STOP:
+ bfa_fsm_set_state(rx, bna_rx_sm_stop_wait);
+ break;
+
+ case RX_E_FAIL:
+ bfa_fsm_set_state(rx, bna_rx_sm_stopped);
+ break;
+
+ case RX_E_STARTED:
+ bfa_fsm_set_state(rx, bna_rx_sm_rxf_start_wait);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ break;
+ }
+}
+
+static void bna_rx_sm_rxf_start_wait_entry(struct bna_rx *rx)
+{
+ rx->rx_post_cbfn(rx->bna->bnad, rx);
+ bna_rxf_start(&rx->rxf);
+}
+
+void
+bna_rx_sm_rxf_stop_wait_entry(struct bna_rx *rx)
+{
+}
+
+static void
+bna_rx_sm_rxf_stop_wait(struct bna_rx *rx, enum bna_rx_event event)
+{
+ switch (event) {
+ case RX_E_FAIL:
+ bfa_fsm_set_state(rx, bna_rx_sm_cleanup_wait);
+ bna_rxf_fail(&rx->rxf);
+ call_rx_stall_cbfn(rx);
+ rx->rx_cleanup_cbfn(rx->bna->bnad, rx);
+ break;
+
+ case RX_E_RXF_STARTED:
+ bna_rxf_stop(&rx->rxf);
+ break;
+
+ case RX_E_RXF_STOPPED:
+ bfa_fsm_set_state(rx, bna_rx_sm_stop_wait);
+ call_rx_stall_cbfn(rx);
+ bna_rx_enet_stop(rx);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ break;
+ }
+
+}
+
+void
+bna_rx_sm_started_entry(struct bna_rx *rx)
+{
+ struct bna_rxp *rxp;
+ struct list_head *qe_rxp;
+ int is_regular = (rx->type == BNA_RX_T_REGULAR);
+
+ /* Start IB */
+ list_for_each(qe_rxp, &rx->rxp_q) {
+ rxp = (struct bna_rxp *)qe_rxp;
+ bna_ib_start(rx->bna, &rxp->cq.ib, is_regular);
+ }
+
+ bna_ethport_cb_rx_started(&rx->bna->ethport);
+}
+
+static void
+bna_rx_sm_started(struct bna_rx *rx, enum bna_rx_event event)
+{
+ switch (event) {
+ case RX_E_STOP:
+ bfa_fsm_set_state(rx, bna_rx_sm_rxf_stop_wait);
+ bna_ethport_cb_rx_stopped(&rx->bna->ethport);
+ bna_rxf_stop(&rx->rxf);
+ break;
+
+ case RX_E_FAIL:
+ bfa_fsm_set_state(rx, bna_rx_sm_failed);
+ bna_ethport_cb_rx_stopped(&rx->bna->ethport);
+ bna_rxf_fail(&rx->rxf);
+ call_rx_stall_cbfn(rx);
+ rx->rx_cleanup_cbfn(rx->bna->bnad, rx);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ break;
+ }
+}
+
+static void bna_rx_sm_rxf_start_wait(struct bna_rx *rx,
+ enum bna_rx_event event)
+{
+ switch (event) {
+ case RX_E_STOP:
+ bfa_fsm_set_state(rx, bna_rx_sm_rxf_stop_wait);
+ break;
+
+ case RX_E_FAIL:
+ bfa_fsm_set_state(rx, bna_rx_sm_failed);
+ bna_rxf_fail(&rx->rxf);
+ call_rx_stall_cbfn(rx);
+ rx->rx_cleanup_cbfn(rx->bna->bnad, rx);
+ break;
+
+ case RX_E_RXF_STARTED:
+ bfa_fsm_set_state(rx, bna_rx_sm_started);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ break;
+ }
+}
+
+void
+bna_rx_sm_cleanup_wait_entry(struct bna_rx *rx)
+{
+}
+
+void
+bna_rx_sm_cleanup_wait(struct bna_rx *rx, enum bna_rx_event event)
+{
+ switch (event) {
+ case RX_E_FAIL:
+ case RX_E_RXF_STOPPED:
+ /* No-op */
+ break;
+
+ case RX_E_CLEANUP_DONE:
+ bfa_fsm_set_state(rx, bna_rx_sm_stopped);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ break;
+ }
+}
+
+static void
+bna_rx_sm_failed_entry(struct bna_rx *rx)
+{
+}
+
+static void
+bna_rx_sm_failed(struct bna_rx *rx, enum bna_rx_event event)
+{
+ switch (event) {
+ case RX_E_START:
+ bfa_fsm_set_state(rx, bna_rx_sm_quiesce_wait);
+ break;
+
+ case RX_E_STOP:
+ bfa_fsm_set_state(rx, bna_rx_sm_cleanup_wait);
+ break;
+
+ case RX_E_FAIL:
+ case RX_E_RXF_STARTED:
+ case RX_E_RXF_STOPPED:
+ /* No-op */
+ break;
+
+ case RX_E_CLEANUP_DONE:
+ bfa_fsm_set_state(rx, bna_rx_sm_stopped);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ break;
+} }
+
+static void
+bna_rx_sm_quiesce_wait_entry(struct bna_rx *rx)
+{
+}
+
+static void
+bna_rx_sm_quiesce_wait(struct bna_rx *rx, enum bna_rx_event event)
+{
+ switch (event) {
+ case RX_E_STOP:
+ bfa_fsm_set_state(rx, bna_rx_sm_cleanup_wait);
+ break;
+
+ case RX_E_FAIL:
+ bfa_fsm_set_state(rx, bna_rx_sm_failed);
+ break;
+
+ case RX_E_CLEANUP_DONE:
+ bfa_fsm_set_state(rx, bna_rx_sm_start_wait);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ break;
+ }
+}
+
+static void
+bna_bfi_rx_enet_start(struct bna_rx *rx)
+{
+ struct bfi_enet_rx_cfg_req *cfg_req = &rx->bfi_enet_cmd.cfg_req;
+ struct bna_rxp *rxp = NULL;
+ struct bna_rxq *q0 = NULL, *q1 = NULL;
+ struct list_head *rxp_qe;
+ int i;
+
+ bfi_msgq_mhdr_set(cfg_req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_RX_CFG_SET_REQ, 0, rx->rid);
+ cfg_req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_rx_cfg_req)));
+
+ cfg_req->num_queue_sets = rx->num_paths;
+ for (i = 0, rxp_qe = bfa_q_first(&rx->rxp_q);
+ i < rx->num_paths;
+ i++, rxp_qe = bfa_q_next(rxp_qe)) {
+ rxp = (struct bna_rxp *)rxp_qe;
+
+ GET_RXQS(rxp, q0, q1);
+ switch (rxp->type) {
+ case BNA_RXP_SLR:
+ case BNA_RXP_HDS:
+ /* Small RxQ */
+ bfi_enet_datapath_q_init(&cfg_req->q_cfg[i].qs.q,
+ &q1->qpt);
+ cfg_req->q_cfg[i].qs.rx_buffer_size =
+ htons((u16)q1->buffer_size);
+ /* Fall through */
+
+ case BNA_RXP_SINGLE:
+ /* Large/Single RxQ */
+ bfi_enet_datapath_q_init(&cfg_req->q_cfg[i].ql.q,
+ &q0->qpt);
+ q0->buffer_size =
+ bna_enet_mtu_get(&rx->bna->enet);
+ cfg_req->q_cfg[i].ql.rx_buffer_size =
+ htons((u16)q0->buffer_size);
+ break;
+
+ default:
+ BUG_ON(1);
+ }
+
+ bfi_enet_datapath_q_init(&cfg_req->q_cfg[i].cq.q,
+ &rxp->cq.qpt);
+
+ cfg_req->q_cfg[i].ib.index_addr.a32.addr_lo =
+ rxp->cq.ib.ib_seg_host_addr.lsb;
+ cfg_req->q_cfg[i].ib.index_addr.a32.addr_hi =
+ rxp->cq.ib.ib_seg_host_addr.msb;
+ cfg_req->q_cfg[i].ib.intr.msix_index =
+ htons((u16)rxp->cq.ib.intr_vector);
+ }
+
+ cfg_req->ib_cfg.int_pkt_dma = BNA_STATUS_T_DISABLED;
+ cfg_req->ib_cfg.int_enabled = BNA_STATUS_T_ENABLED;
+ cfg_req->ib_cfg.int_pkt_enabled = BNA_STATUS_T_DISABLED;
+ cfg_req->ib_cfg.continuous_coalescing = BNA_STATUS_T_DISABLED;
+ cfg_req->ib_cfg.msix = (rxp->cq.ib.intr_type == BNA_INTR_T_MSIX)
+ ? BNA_STATUS_T_ENABLED :
+ BNA_STATUS_T_DISABLED;
+ cfg_req->ib_cfg.coalescing_timeout =
+ htonl((u32)rxp->cq.ib.coalescing_timeo);
+ cfg_req->ib_cfg.inter_pkt_timeout =
+ htonl((u32)rxp->cq.ib.interpkt_timeo);
+ cfg_req->ib_cfg.inter_pkt_count = (u8)rxp->cq.ib.interpkt_count;
+
+ switch (rxp->type) {
+ case BNA_RXP_SLR:
+ cfg_req->rx_cfg.rxq_type = BFI_ENET_RXQ_LARGE_SMALL;
+ break;
+
+ case BNA_RXP_HDS:
+ cfg_req->rx_cfg.rxq_type = BFI_ENET_RXQ_HDS;
+ cfg_req->rx_cfg.hds.type = rx->hds_cfg.hdr_type;
+ cfg_req->rx_cfg.hds.force_offset = rx->hds_cfg.forced_offset;
+ cfg_req->rx_cfg.hds.max_header_size = rx->hds_cfg.forced_offset;
+ break;
+
+ case BNA_RXP_SINGLE:
+ cfg_req->rx_cfg.rxq_type = BFI_ENET_RXQ_SINGLE;
+ break;
+
+ default:
+ BUG_ON(1);
+ }
+ cfg_req->rx_cfg.strip_vlan = rx->rxf.vlan_strip_status;
+
+ bfa_msgq_cmd_set(&rx->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_rx_cfg_req), &cfg_req->mh);
+ bfa_msgq_cmd_post(&rx->bna->msgq, &rx->msgq_cmd);
+}
+
+static void
+bna_bfi_rx_enet_stop(struct bna_rx *rx)
+{
+ struct bfi_enet_req *req = &rx->bfi_enet_cmd.req;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_RX_CFG_CLR_REQ, 0, rx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_req)));
+ bfa_msgq_cmd_set(&rx->msgq_cmd, NULL, NULL, sizeof(struct bfi_enet_req),
+ &req->mh);
+ bfa_msgq_cmd_post(&rx->bna->msgq, &rx->msgq_cmd);
+}
+
+static void
+bna_rx_enet_stop(struct bna_rx *rx)
+{
+ struct bna_rxp *rxp;
+ struct list_head *qe_rxp;
+
+ /* Stop IB */
+ list_for_each(qe_rxp, &rx->rxp_q) {
+ rxp = (struct bna_rxp *)qe_rxp;
+ bna_ib_stop(rx->bna, &rxp->cq.ib);
+ }
+
+ bna_bfi_rx_enet_stop(rx);
+}
+
+static int
+bna_rx_res_check(struct bna_rx_mod *rx_mod, struct bna_rx_config *rx_cfg)
+{
+ if ((rx_mod->rx_free_count == 0) ||
+ (rx_mod->rxp_free_count == 0) ||
+ (rx_mod->rxq_free_count == 0))
+ return 0;
+
+ if (rx_cfg->rxp_type == BNA_RXP_SINGLE) {
+ if ((rx_mod->rxp_free_count < rx_cfg->num_paths) ||
+ (rx_mod->rxq_free_count < rx_cfg->num_paths))
+ return 0;
+ } else {
+ if ((rx_mod->rxp_free_count < rx_cfg->num_paths) ||
+ (rx_mod->rxq_free_count < (2 * rx_cfg->num_paths)))
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct bna_rxq *
+bna_rxq_get(struct bna_rx_mod *rx_mod)
+{
+ struct bna_rxq *rxq = NULL;
+ struct list_head *qe = NULL;
+
+ bfa_q_deq(&rx_mod->rxq_free_q, &qe);
+ rx_mod->rxq_free_count--;
+ rxq = (struct bna_rxq *)qe;
+ bfa_q_qe_init(&rxq->qe);
+
+ return rxq;
+}
+
+static void
+bna_rxq_put(struct bna_rx_mod *rx_mod, struct bna_rxq *rxq)
+{
+ bfa_q_qe_init(&rxq->qe);
+ list_add_tail(&rxq->qe, &rx_mod->rxq_free_q);
+ rx_mod->rxq_free_count++;
+}
+
+static struct bna_rxp *
+bna_rxp_get(struct bna_rx_mod *rx_mod)
+{
+ struct list_head *qe = NULL;
+ struct bna_rxp *rxp = NULL;
+
+ bfa_q_deq(&rx_mod->rxp_free_q, &qe);
+ rx_mod->rxp_free_count--;
+ rxp = (struct bna_rxp *)qe;
+ bfa_q_qe_init(&rxp->qe);
+
+ return rxp;
+}
+
+static void
+bna_rxp_put(struct bna_rx_mod *rx_mod, struct bna_rxp *rxp)
+{
+ bfa_q_qe_init(&rxp->qe);
+ list_add_tail(&rxp->qe, &rx_mod->rxp_free_q);
+ rx_mod->rxp_free_count++;
+}
+
+static struct bna_rx *
+bna_rx_get(struct bna_rx_mod *rx_mod, enum bna_rx_type type)
+{
+ struct list_head *qe = NULL;
+ struct bna_rx *rx = NULL;
+
+ if (type == BNA_RX_T_REGULAR) {
+ bfa_q_deq(&rx_mod->rx_free_q, &qe);
+ } else
+ bfa_q_deq_tail(&rx_mod->rx_free_q, &qe);
+
+ rx_mod->rx_free_count--;
+ rx = (struct bna_rx *)qe;
+ bfa_q_qe_init(&rx->qe);
+ list_add_tail(&rx->qe, &rx_mod->rx_active_q);
+ rx->type = type;
+
+ return rx;
+}
+
+static void
+bna_rx_put(struct bna_rx_mod *rx_mod, struct bna_rx *rx)
+{
+ struct list_head *prev_qe = NULL;
+ struct list_head *qe;
+
+ bfa_q_qe_init(&rx->qe);
+
+ list_for_each(qe, &rx_mod->rx_free_q) {
+ if (((struct bna_rx *)qe)->rid < rx->rid)
+ prev_qe = qe;
+ else
+ break;
+ }
+
+ if (prev_qe == NULL) {
+ /* This is the first entry */
+ bfa_q_enq_head(&rx_mod->rx_free_q, &rx->qe);
+ } else if (bfa_q_next(prev_qe) == &rx_mod->rx_free_q) {
+ /* This is the last entry */
+ list_add_tail(&rx->qe, &rx_mod->rx_free_q);
+ } else {
+ /* Somewhere in the middle */
+ bfa_q_next(&rx->qe) = bfa_q_next(prev_qe);
+ bfa_q_prev(&rx->qe) = prev_qe;
+ bfa_q_next(prev_qe) = &rx->qe;
+ bfa_q_prev(bfa_q_next(&rx->qe)) = &rx->qe;
+ }
+
+ rx_mod->rx_free_count++;
+}
+
+static void
+bna_rxp_add_rxqs(struct bna_rxp *rxp, struct bna_rxq *q0,
+ struct bna_rxq *q1)
+{
+ switch (rxp->type) {
+ case BNA_RXP_SINGLE:
+ rxp->rxq.single.only = q0;
+ rxp->rxq.single.reserved = NULL;
+ break;
+ case BNA_RXP_SLR:
+ rxp->rxq.slr.large = q0;
+ rxp->rxq.slr.small = q1;
+ break;
+ case BNA_RXP_HDS:
+ rxp->rxq.hds.data = q0;
+ rxp->rxq.hds.hdr = q1;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+bna_rxq_qpt_setup(struct bna_rxq *rxq,
+ struct bna_rxp *rxp,
+ u32 page_count,
+ u32 page_size,
+ struct bna_mem_descr *qpt_mem,
+ struct bna_mem_descr *swqpt_mem,
+ struct bna_mem_descr *page_mem)
+{
+ int i;
+
+ rxq->qpt.hw_qpt_ptr.lsb = qpt_mem->dma.lsb;
+ rxq->qpt.hw_qpt_ptr.msb = qpt_mem->dma.msb;
+ rxq->qpt.kv_qpt_ptr = qpt_mem->kva;
+ rxq->qpt.page_count = page_count;
+ rxq->qpt.page_size = page_size;
+
+ rxq->rcb->sw_qpt = (void **) swqpt_mem->kva;
+
+ for (i = 0; i < rxq->qpt.page_count; i++) {
+ rxq->rcb->sw_qpt[i] = page_mem[i].kva;
+ ((struct bna_dma_addr *)rxq->qpt.kv_qpt_ptr)[i].lsb =
+ page_mem[i].dma.lsb;
+ ((struct bna_dma_addr *)rxq->qpt.kv_qpt_ptr)[i].msb =
+ page_mem[i].dma.msb;
+ }
+}
+
+static void
+bna_rxp_cqpt_setup(struct bna_rxp *rxp,
+ u32 page_count,
+ u32 page_size,
+ struct bna_mem_descr *qpt_mem,
+ struct bna_mem_descr *swqpt_mem,
+ struct bna_mem_descr *page_mem)
+{
+ int i;
+
+ rxp->cq.qpt.hw_qpt_ptr.lsb = qpt_mem->dma.lsb;
+ rxp->cq.qpt.hw_qpt_ptr.msb = qpt_mem->dma.msb;
+ rxp->cq.qpt.kv_qpt_ptr = qpt_mem->kva;
+ rxp->cq.qpt.page_count = page_count;
+ rxp->cq.qpt.page_size = page_size;
+
+ rxp->cq.ccb->sw_qpt = (void **) swqpt_mem->kva;
+
+ for (i = 0; i < rxp->cq.qpt.page_count; i++) {
+ rxp->cq.ccb->sw_qpt[i] = page_mem[i].kva;
+
+ ((struct bna_dma_addr *)rxp->cq.qpt.kv_qpt_ptr)[i].lsb =
+ page_mem[i].dma.lsb;
+ ((struct bna_dma_addr *)rxp->cq.qpt.kv_qpt_ptr)[i].msb =
+ page_mem[i].dma.msb;
+ }
+}
+
+static void
+bna_rx_mod_cb_rx_stopped(void *arg, struct bna_rx *rx)
+{
+ struct bna_rx_mod *rx_mod = (struct bna_rx_mod *)arg;
+
+ bfa_wc_down(&rx_mod->rx_stop_wc);
+}
+
+static void
+bna_rx_mod_cb_rx_stopped_all(void *arg)
+{
+ struct bna_rx_mod *rx_mod = (struct bna_rx_mod *)arg;
+
+ if (rx_mod->stop_cbfn)
+ rx_mod->stop_cbfn(&rx_mod->bna->enet);
+ rx_mod->stop_cbfn = NULL;
+}
+
+static void
+bna_rx_start(struct bna_rx *rx)
+{
+ rx->rx_flags |= BNA_RX_F_ENET_STARTED;
+ if (rx->rx_flags & BNA_RX_F_ENABLED)
+ bfa_fsm_send_event(rx, RX_E_START);
+}
+
+static void
+bna_rx_stop(struct bna_rx *rx)
+{
+ rx->rx_flags &= ~BNA_RX_F_ENET_STARTED;
+ if (rx->fsm == (bfa_fsm_t) bna_rx_sm_stopped)
+ bna_rx_mod_cb_rx_stopped(&rx->bna->rx_mod, rx);
+ else {
+ rx->stop_cbfn = bna_rx_mod_cb_rx_stopped;
+ rx->stop_cbarg = &rx->bna->rx_mod;
+ bfa_fsm_send_event(rx, RX_E_STOP);
+ }
+}
+
+static void
+bna_rx_fail(struct bna_rx *rx)
+{
+ /* Indicate Enet is not enabled, and failed */
+ rx->rx_flags &= ~BNA_RX_F_ENET_STARTED;
+ bfa_fsm_send_event(rx, RX_E_FAIL);
+}
+
+void
+bna_rx_mod_start(struct bna_rx_mod *rx_mod, enum bna_rx_type type)
+{
+ struct bna_rx *rx;
+ struct list_head *qe;
+
+ rx_mod->flags |= BNA_RX_MOD_F_ENET_STARTED;
+ if (type == BNA_RX_T_LOOPBACK)
+ rx_mod->flags |= BNA_RX_MOD_F_ENET_LOOPBACK;
+
+ list_for_each(qe, &rx_mod->rx_active_q) {
+ rx = (struct bna_rx *)qe;
+ if (rx->type == type)
+ bna_rx_start(rx);
+ }
+}
+
+void
+bna_rx_mod_stop(struct bna_rx_mod *rx_mod, enum bna_rx_type type)
+{
+ struct bna_rx *rx;
+ struct list_head *qe;
+
+ rx_mod->flags &= ~BNA_RX_MOD_F_ENET_STARTED;
+ rx_mod->flags &= ~BNA_RX_MOD_F_ENET_LOOPBACK;
+
+ rx_mod->stop_cbfn = bna_enet_cb_rx_stopped;
+
+ bfa_wc_init(&rx_mod->rx_stop_wc, bna_rx_mod_cb_rx_stopped_all, rx_mod);
+
+ list_for_each(qe, &rx_mod->rx_active_q) {
+ rx = (struct bna_rx *)qe;
+ if (rx->type == type) {
+ bfa_wc_up(&rx_mod->rx_stop_wc);
+ bna_rx_stop(rx);
+ }
+ }
+
+ bfa_wc_wait(&rx_mod->rx_stop_wc);
+}
+
+void
+bna_rx_mod_fail(struct bna_rx_mod *rx_mod)
+{
+ struct bna_rx *rx;
+ struct list_head *qe;
+
+ rx_mod->flags &= ~BNA_RX_MOD_F_ENET_STARTED;
+ rx_mod->flags &= ~BNA_RX_MOD_F_ENET_LOOPBACK;
+
+ list_for_each(qe, &rx_mod->rx_active_q) {
+ rx = (struct bna_rx *)qe;
+ bna_rx_fail(rx);
+ }
+}
+
+void bna_rx_mod_init(struct bna_rx_mod *rx_mod, struct bna *bna,
+ struct bna_res_info *res_info)
+{
+ int index;
+ struct bna_rx *rx_ptr;
+ struct bna_rxp *rxp_ptr;
+ struct bna_rxq *rxq_ptr;
+
+ rx_mod->bna = bna;
+ rx_mod->flags = 0;
+
+ rx_mod->rx = (struct bna_rx *)
+ res_info[BNA_MOD_RES_MEM_T_RX_ARRAY].res_u.mem_info.mdl[0].kva;
+ rx_mod->rxp = (struct bna_rxp *)
+ res_info[BNA_MOD_RES_MEM_T_RXP_ARRAY].res_u.mem_info.mdl[0].kva;
+ rx_mod->rxq = (struct bna_rxq *)
+ res_info[BNA_MOD_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.mdl[0].kva;
+
+ /* Initialize the queues */
+ INIT_LIST_HEAD(&rx_mod->rx_free_q);
+ rx_mod->rx_free_count = 0;
+ INIT_LIST_HEAD(&rx_mod->rxq_free_q);
+ rx_mod->rxq_free_count = 0;
+ INIT_LIST_HEAD(&rx_mod->rxp_free_q);
+ rx_mod->rxp_free_count = 0;
+ INIT_LIST_HEAD(&rx_mod->rx_active_q);
+
+ /* Build RX queues */
+ for (index = 0; index < bna->ioceth.attr.num_rxp; index++) {
+ rx_ptr = &rx_mod->rx[index];
+
+ bfa_q_qe_init(&rx_ptr->qe);
+ INIT_LIST_HEAD(&rx_ptr->rxp_q);
+ rx_ptr->bna = NULL;
+ rx_ptr->rid = index;
+ rx_ptr->stop_cbfn = NULL;
+ rx_ptr->stop_cbarg = NULL;
+
+ list_add_tail(&rx_ptr->qe, &rx_mod->rx_free_q);
+ rx_mod->rx_free_count++;
+ }
+
+ /* build RX-path queue */
+ for (index = 0; index < bna->ioceth.attr.num_rxp; index++) {
+ rxp_ptr = &rx_mod->rxp[index];
+ bfa_q_qe_init(&rxp_ptr->qe);
+ list_add_tail(&rxp_ptr->qe, &rx_mod->rxp_free_q);
+ rx_mod->rxp_free_count++;
+ }
+
+ /* build RXQ queue */
+ for (index = 0; index < (bna->ioceth.attr.num_rxp * 2); index++) {
+ rxq_ptr = &rx_mod->rxq[index];
+ bfa_q_qe_init(&rxq_ptr->qe);
+ list_add_tail(&rxq_ptr->qe, &rx_mod->rxq_free_q);
+ rx_mod->rxq_free_count++;
+ }
+}
+
+void
+bna_rx_mod_uninit(struct bna_rx_mod *rx_mod)
+{
+ struct list_head *qe;
+ int i;
+
+ i = 0;
+ list_for_each(qe, &rx_mod->rx_free_q)
+ i++;
+
+ i = 0;
+ list_for_each(qe, &rx_mod->rxp_free_q)
+ i++;
+
+ i = 0;
+ list_for_each(qe, &rx_mod->rxq_free_q)
+ i++;
+
+ rx_mod->bna = NULL;
+}
+
+void
+bna_bfi_rx_enet_start_rsp(struct bna_rx *rx, struct bfi_msgq_mhdr *msghdr)
+{
+ struct bfi_enet_rx_cfg_rsp *cfg_rsp = &rx->bfi_enet_cmd.cfg_rsp;
+ struct bna_rxp *rxp = NULL;
+ struct bna_rxq *q0 = NULL, *q1 = NULL;
+ struct list_head *rxp_qe;
+ int i;
+
+ bfa_msgq_rsp_copy(&rx->bna->msgq, (u8 *)cfg_rsp,
+ sizeof(struct bfi_enet_rx_cfg_rsp));
+
+ rx->hw_id = cfg_rsp->hw_id;
+
+ for (i = 0, rxp_qe = bfa_q_first(&rx->rxp_q);
+ i < rx->num_paths;
+ i++, rxp_qe = bfa_q_next(rxp_qe)) {
+ rxp = (struct bna_rxp *)rxp_qe;
+ GET_RXQS(rxp, q0, q1);
+
+ /* Setup doorbells */
+ rxp->cq.ccb->i_dbell->doorbell_addr =
+ rx->bna->pcidev.pci_bar_kva
+ + ntohl(cfg_rsp->q_handles[i].i_dbell);
+ rxp->hw_id = cfg_rsp->q_handles[i].hw_cqid;
+ q0->rcb->q_dbell =
+ rx->bna->pcidev.pci_bar_kva
+ + ntohl(cfg_rsp->q_handles[i].ql_dbell);
+ q0->hw_id = cfg_rsp->q_handles[i].hw_lqid;
+ if (q1) {
+ q1->rcb->q_dbell =
+ rx->bna->pcidev.pci_bar_kva
+ + ntohl(cfg_rsp->q_handles[i].qs_dbell);
+ q1->hw_id = cfg_rsp->q_handles[i].hw_sqid;
+ }
+
+ /* Initialize producer/consumer indexes */
+ (*rxp->cq.ccb->hw_producer_index) = 0;
+ rxp->cq.ccb->producer_index = 0;
+ q0->rcb->producer_index = q0->rcb->consumer_index = 0;
+ if (q1)
+ q1->rcb->producer_index = q1->rcb->consumer_index = 0;
+ }
+
+ bfa_fsm_send_event(rx, RX_E_STARTED);
+}
+
+void
+bna_bfi_rx_enet_stop_rsp(struct bna_rx *rx, struct bfi_msgq_mhdr *msghdr)
+{
+ bfa_fsm_send_event(rx, RX_E_STOPPED);
+}
+
+void
+bna_rx_res_req(struct bna_rx_config *q_cfg, struct bna_res_info *res_info)
+{
+ u32 cq_size, hq_size, dq_size;
+ u32 cpage_count, hpage_count, dpage_count;
+ struct bna_mem_info *mem_info;
+ u32 cq_depth;
+ u32 hq_depth;
+ u32 dq_depth;
+
+ dq_depth = q_cfg->q_depth;
+ hq_depth = ((q_cfg->rxp_type == BNA_RXP_SINGLE) ? 0 : q_cfg->q_depth);
+ cq_depth = dq_depth + hq_depth;
+
+ BNA_TO_POWER_OF_2_HIGH(cq_depth);
+ cq_size = cq_depth * BFI_CQ_WI_SIZE;
+ cq_size = ALIGN(cq_size, PAGE_SIZE);
+ cpage_count = SIZE_TO_PAGES(cq_size);
+
+ BNA_TO_POWER_OF_2_HIGH(dq_depth);
+ dq_size = dq_depth * BFI_RXQ_WI_SIZE;
+ dq_size = ALIGN(dq_size, PAGE_SIZE);
+ dpage_count = SIZE_TO_PAGES(dq_size);
+
+ if (BNA_RXP_SINGLE != q_cfg->rxp_type) {
+ BNA_TO_POWER_OF_2_HIGH(hq_depth);
+ hq_size = hq_depth * BFI_RXQ_WI_SIZE;
+ hq_size = ALIGN(hq_size, PAGE_SIZE);
+ hpage_count = SIZE_TO_PAGES(hq_size);
+ } else
+ hpage_count = 0;
+
+ res_info[BNA_RX_RES_MEM_T_CCB].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_CCB].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_KVA;
+ mem_info->len = sizeof(struct bna_ccb);
+ mem_info->num = q_cfg->num_paths;
+
+ res_info[BNA_RX_RES_MEM_T_RCB].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_RCB].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_KVA;
+ mem_info->len = sizeof(struct bna_rcb);
+ mem_info->num = BNA_GET_RXQS(q_cfg);
+
+ res_info[BNA_RX_RES_MEM_T_CQPT].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_CQPT].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_DMA;
+ mem_info->len = cpage_count * sizeof(struct bna_dma_addr);
+ mem_info->num = q_cfg->num_paths;
+
+ res_info[BNA_RX_RES_MEM_T_CSWQPT].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_CSWQPT].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_KVA;
+ mem_info->len = cpage_count * sizeof(void *);
+ mem_info->num = q_cfg->num_paths;
+
+ res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_DMA;
+ mem_info->len = PAGE_SIZE;
+ mem_info->num = cpage_count * q_cfg->num_paths;
+
+ res_info[BNA_RX_RES_MEM_T_DQPT].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_DQPT].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_DMA;
+ mem_info->len = dpage_count * sizeof(struct bna_dma_addr);
+ mem_info->num = q_cfg->num_paths;
+
+ res_info[BNA_RX_RES_MEM_T_DSWQPT].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_DSWQPT].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_KVA;
+ mem_info->len = dpage_count * sizeof(void *);
+ mem_info->num = q_cfg->num_paths;
+
+ res_info[BNA_RX_RES_MEM_T_DPAGE].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_DPAGE].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_DMA;
+ mem_info->len = PAGE_SIZE;
+ mem_info->num = dpage_count * q_cfg->num_paths;
+
+ res_info[BNA_RX_RES_MEM_T_HQPT].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_HQPT].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_DMA;
+ mem_info->len = hpage_count * sizeof(struct bna_dma_addr);
+ mem_info->num = (hpage_count ? q_cfg->num_paths : 0);
+
+ res_info[BNA_RX_RES_MEM_T_HSWQPT].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_HSWQPT].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_KVA;
+ mem_info->len = hpage_count * sizeof(void *);
+ mem_info->num = (hpage_count ? q_cfg->num_paths : 0);
+
+ res_info[BNA_RX_RES_MEM_T_HPAGE].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_HPAGE].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_DMA;
+ mem_info->len = (hpage_count ? PAGE_SIZE : 0);
+ mem_info->num = (hpage_count ? (hpage_count * q_cfg->num_paths) : 0);
+
+ res_info[BNA_RX_RES_MEM_T_IBIDX].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_IBIDX].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_DMA;
+ mem_info->len = BFI_IBIDX_SIZE;
+ mem_info->num = q_cfg->num_paths;
+
+ res_info[BNA_RX_RES_MEM_T_RIT].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_RX_RES_MEM_T_RIT].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_KVA;
+ mem_info->len = BFI_ENET_RSS_RIT_MAX;
+ mem_info->num = 1;
+
+ res_info[BNA_RX_RES_T_INTR].res_type = BNA_RES_T_INTR;
+ res_info[BNA_RX_RES_T_INTR].res_u.intr_info.intr_type = BNA_INTR_T_MSIX;
+ res_info[BNA_RX_RES_T_INTR].res_u.intr_info.num = q_cfg->num_paths;
+}
+
+struct bna_rx *
+bna_rx_create(struct bna *bna, struct bnad *bnad,
+ struct bna_rx_config *rx_cfg,
+ const struct bna_rx_event_cbfn *rx_cbfn,
+ struct bna_res_info *res_info,
+ void *priv)
+{
+ struct bna_rx_mod *rx_mod = &bna->rx_mod;
+ struct bna_rx *rx;
+ struct bna_rxp *rxp;
+ struct bna_rxq *q0;
+ struct bna_rxq *q1;
+ struct bna_intr_info *intr_info;
+ u32 page_count;
+ struct bna_mem_descr *ccb_mem;
+ struct bna_mem_descr *rcb_mem;
+ struct bna_mem_descr *unmapq_mem;
+ struct bna_mem_descr *cqpt_mem;
+ struct bna_mem_descr *cswqpt_mem;
+ struct bna_mem_descr *cpage_mem;
+ struct bna_mem_descr *hqpt_mem;
+ struct bna_mem_descr *dqpt_mem;
+ struct bna_mem_descr *hsqpt_mem;
+ struct bna_mem_descr *dsqpt_mem;
+ struct bna_mem_descr *hpage_mem;
+ struct bna_mem_descr *dpage_mem;
+ int i, cpage_idx = 0, dpage_idx = 0, hpage_idx = 0;
+ int dpage_count, hpage_count, rcb_idx;
+
+ if (!bna_rx_res_check(rx_mod, rx_cfg))
+ return NULL;
+
+ intr_info = &res_info[BNA_RX_RES_T_INTR].res_u.intr_info;
+ ccb_mem = &res_info[BNA_RX_RES_MEM_T_CCB].res_u.mem_info.mdl[0];
+ rcb_mem = &res_info[BNA_RX_RES_MEM_T_RCB].res_u.mem_info.mdl[0];
+ unmapq_mem = &res_info[BNA_RX_RES_MEM_T_UNMAPQ].res_u.mem_info.mdl[0];
+ cqpt_mem = &res_info[BNA_RX_RES_MEM_T_CQPT].res_u.mem_info.mdl[0];
+ cswqpt_mem = &res_info[BNA_RX_RES_MEM_T_CSWQPT].res_u.mem_info.mdl[0];
+ cpage_mem = &res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_u.mem_info.mdl[0];
+ hqpt_mem = &res_info[BNA_RX_RES_MEM_T_HQPT].res_u.mem_info.mdl[0];
+ dqpt_mem = &res_info[BNA_RX_RES_MEM_T_DQPT].res_u.mem_info.mdl[0];
+ hsqpt_mem = &res_info[BNA_RX_RES_MEM_T_HSWQPT].res_u.mem_info.mdl[0];
+ dsqpt_mem = &res_info[BNA_RX_RES_MEM_T_DSWQPT].res_u.mem_info.mdl[0];
+ hpage_mem = &res_info[BNA_RX_RES_MEM_T_HPAGE].res_u.mem_info.mdl[0];
+ dpage_mem = &res_info[BNA_RX_RES_MEM_T_DPAGE].res_u.mem_info.mdl[0];
+
+ page_count = res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_u.mem_info.num /
+ rx_cfg->num_paths;
+
+ dpage_count = res_info[BNA_RX_RES_MEM_T_DPAGE].res_u.mem_info.num /
+ rx_cfg->num_paths;
+
+ hpage_count = res_info[BNA_RX_RES_MEM_T_HPAGE].res_u.mem_info.num /
+ rx_cfg->num_paths;
+
+ rx = bna_rx_get(rx_mod, rx_cfg->rx_type);
+ rx->bna = bna;
+ rx->rx_flags = 0;
+ INIT_LIST_HEAD(&rx->rxp_q);
+ rx->stop_cbfn = NULL;
+ rx->stop_cbarg = NULL;
+ rx->priv = priv;
+
+ rx->rcb_setup_cbfn = rx_cbfn->rcb_setup_cbfn;
+ rx->rcb_destroy_cbfn = rx_cbfn->rcb_destroy_cbfn;
+ rx->ccb_setup_cbfn = rx_cbfn->ccb_setup_cbfn;
+ rx->ccb_destroy_cbfn = rx_cbfn->ccb_destroy_cbfn;
+ rx->rx_stall_cbfn = rx_cbfn->rx_stall_cbfn;
+ /* Following callbacks are mandatory */
+ rx->rx_cleanup_cbfn = rx_cbfn->rx_cleanup_cbfn;
+ rx->rx_post_cbfn = rx_cbfn->rx_post_cbfn;
+
+ if (rx->bna->rx_mod.flags & BNA_RX_MOD_F_ENET_STARTED) {
+ switch (rx->type) {
+ case BNA_RX_T_REGULAR:
+ if (!(rx->bna->rx_mod.flags &
+ BNA_RX_MOD_F_ENET_LOOPBACK))
+ rx->rx_flags |= BNA_RX_F_ENET_STARTED;
+ break;
+ case BNA_RX_T_LOOPBACK:
+ if (rx->bna->rx_mod.flags & BNA_RX_MOD_F_ENET_LOOPBACK)
+ rx->rx_flags |= BNA_RX_F_ENET_STARTED;
+ break;
+ }
+ }
+
+ rx->num_paths = rx_cfg->num_paths;
+ for (i = 0, rcb_idx = 0; i < rx->num_paths; i++) {
+ rxp = bna_rxp_get(rx_mod);
+ list_add_tail(&rxp->qe, &rx->rxp_q);
+ rxp->type = rx_cfg->rxp_type;
+ rxp->rx = rx;
+ rxp->cq.rx = rx;
+
+ q0 = bna_rxq_get(rx_mod);
+ if (BNA_RXP_SINGLE == rx_cfg->rxp_type)
+ q1 = NULL;
+ else
+ q1 = bna_rxq_get(rx_mod);
+
+ if (1 == intr_info->num)
+ rxp->vector = intr_info->idl[0].vector;
+ else
+ rxp->vector = intr_info->idl[i].vector;
+
+ /* Setup IB */
+
+ rxp->cq.ib.ib_seg_host_addr.lsb =
+ res_info[BNA_RX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].dma.lsb;
+ rxp->cq.ib.ib_seg_host_addr.msb =
+ res_info[BNA_RX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].dma.msb;
+ rxp->cq.ib.ib_seg_host_addr_kva =
+ res_info[BNA_RX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].kva;
+ rxp->cq.ib.intr_type = intr_info->intr_type;
+ if (intr_info->intr_type == BNA_INTR_T_MSIX)
+ rxp->cq.ib.intr_vector = rxp->vector;
+ else
+ rxp->cq.ib.intr_vector = (1 << rxp->vector);
+ rxp->cq.ib.coalescing_timeo = rx_cfg->coalescing_timeo;
+ rxp->cq.ib.interpkt_count = BFI_RX_INTERPKT_COUNT;
+ rxp->cq.ib.interpkt_timeo = BFI_RX_INTERPKT_TIMEO;
+
+ bna_rxp_add_rxqs(rxp, q0, q1);
+
+ /* Setup large Q */
+
+ q0->rx = rx;
+ q0->rxp = rxp;
+
+ q0->rcb = (struct bna_rcb *) rcb_mem[rcb_idx].kva;
+ q0->rcb->unmap_q = (void *)unmapq_mem[rcb_idx].kva;
+ rcb_idx++;
+ q0->rcb->q_depth = rx_cfg->q_depth;
+ q0->rcb->rxq = q0;
+ q0->rcb->bnad = bna->bnad;
+ q0->rcb->id = 0;
+ q0->rx_packets = q0->rx_bytes = 0;
+ q0->rx_packets_with_error = q0->rxbuf_alloc_failed = 0;
+
+ bna_rxq_qpt_setup(q0, rxp, dpage_count, PAGE_SIZE,
+ &dqpt_mem[i], &dsqpt_mem[i], &dpage_mem[dpage_idx]);
+ q0->rcb->page_idx = dpage_idx;
+ q0->rcb->page_count = dpage_count;
+ dpage_idx += dpage_count;
+
+ if (rx->rcb_setup_cbfn)
+ rx->rcb_setup_cbfn(bnad, q0->rcb);
+
+ /* Setup small Q */
+
+ if (q1) {
+ q1->rx = rx;
+ q1->rxp = rxp;
+
+ q1->rcb = (struct bna_rcb *) rcb_mem[rcb_idx].kva;
+ q1->rcb->unmap_q = (void *)unmapq_mem[rcb_idx].kva;
+ rcb_idx++;
+ q1->rcb->q_depth = rx_cfg->q_depth;
+ q1->rcb->rxq = q1;
+ q1->rcb->bnad = bna->bnad;
+ q1->rcb->id = 1;
+ q1->buffer_size = (rx_cfg->rxp_type == BNA_RXP_HDS) ?
+ rx_cfg->hds_config.forced_offset
+ : rx_cfg->small_buff_size;
+ q1->rx_packets = q1->rx_bytes = 0;
+ q1->rx_packets_with_error = q1->rxbuf_alloc_failed = 0;
+
+ bna_rxq_qpt_setup(q1, rxp, hpage_count, PAGE_SIZE,
+ &hqpt_mem[i], &hsqpt_mem[i],
+ &hpage_mem[hpage_idx]);
+ q1->rcb->page_idx = hpage_idx;
+ q1->rcb->page_count = hpage_count;
+ hpage_idx += hpage_count;
+
+ if (rx->rcb_setup_cbfn)
+ rx->rcb_setup_cbfn(bnad, q1->rcb);
+ }
+
+ /* Setup CQ */
+
+ rxp->cq.ccb = (struct bna_ccb *) ccb_mem[i].kva;
+ rxp->cq.ccb->q_depth = rx_cfg->q_depth +
+ ((rx_cfg->rxp_type == BNA_RXP_SINGLE) ?
+ 0 : rx_cfg->q_depth);
+ rxp->cq.ccb->cq = &rxp->cq;
+ rxp->cq.ccb->rcb[0] = q0->rcb;
+ q0->rcb->ccb = rxp->cq.ccb;
+ if (q1) {
+ rxp->cq.ccb->rcb[1] = q1->rcb;
+ q1->rcb->ccb = rxp->cq.ccb;
+ }
+ rxp->cq.ccb->hw_producer_index =
+ (u32 *)rxp->cq.ib.ib_seg_host_addr_kva;
+ rxp->cq.ccb->i_dbell = &rxp->cq.ib.door_bell;
+ rxp->cq.ccb->intr_type = rxp->cq.ib.intr_type;
+ rxp->cq.ccb->intr_vector = rxp->cq.ib.intr_vector;
+ rxp->cq.ccb->rx_coalescing_timeo =
+ rxp->cq.ib.coalescing_timeo;
+ rxp->cq.ccb->pkt_rate.small_pkt_cnt = 0;
+ rxp->cq.ccb->pkt_rate.large_pkt_cnt = 0;
+ rxp->cq.ccb->bnad = bna->bnad;
+ rxp->cq.ccb->id = i;
+
+ bna_rxp_cqpt_setup(rxp, page_count, PAGE_SIZE,
+ &cqpt_mem[i], &cswqpt_mem[i], &cpage_mem[cpage_idx]);
+ rxp->cq.ccb->page_idx = cpage_idx;
+ rxp->cq.ccb->page_count = page_count;
+ cpage_idx += page_count;
+
+ if (rx->ccb_setup_cbfn)
+ rx->ccb_setup_cbfn(bnad, rxp->cq.ccb);
+ }
+
+ rx->hds_cfg = rx_cfg->hds_config;
+
+ bna_rxf_init(&rx->rxf, rx, rx_cfg, res_info);
+
+ bfa_fsm_set_state(rx, bna_rx_sm_stopped);
+
+ rx_mod->rid_mask |= (1 << rx->rid);
+
+ return rx;
+}
+
+void
+bna_rx_destroy(struct bna_rx *rx)
+{
+ struct bna_rx_mod *rx_mod = &rx->bna->rx_mod;
+ struct bna_rxq *q0 = NULL;
+ struct bna_rxq *q1 = NULL;
+ struct bna_rxp *rxp;
+ struct list_head *qe;
+
+ bna_rxf_uninit(&rx->rxf);
+
+ while (!list_empty(&rx->rxp_q)) {
+ bfa_q_deq(&rx->rxp_q, &rxp);
+ GET_RXQS(rxp, q0, q1);
+ if (rx->rcb_destroy_cbfn)
+ rx->rcb_destroy_cbfn(rx->bna->bnad, q0->rcb);
+ q0->rcb = NULL;
+ q0->rxp = NULL;
+ q0->rx = NULL;
+ bna_rxq_put(rx_mod, q0);
+
+ if (q1) {
+ if (rx->rcb_destroy_cbfn)
+ rx->rcb_destroy_cbfn(rx->bna->bnad, q1->rcb);
+ q1->rcb = NULL;
+ q1->rxp = NULL;
+ q1->rx = NULL;
+ bna_rxq_put(rx_mod, q1);
+ }
+ rxp->rxq.slr.large = NULL;
+ rxp->rxq.slr.small = NULL;
+
+ if (rx->ccb_destroy_cbfn)
+ rx->ccb_destroy_cbfn(rx->bna->bnad, rxp->cq.ccb);
+ rxp->cq.ccb = NULL;
+ rxp->rx = NULL;
+ bna_rxp_put(rx_mod, rxp);
+ }
+
+ list_for_each(qe, &rx_mod->rx_active_q) {
+ if (qe == &rx->qe) {
+ list_del(&rx->qe);
+ bfa_q_qe_init(&rx->qe);
+ break;
+ }
+ }
+
+ rx_mod->rid_mask &= ~(1 << rx->rid);
+
+ rx->bna = NULL;
+ rx->priv = NULL;
+ bna_rx_put(rx_mod, rx);
+}
+
+void
+bna_rx_enable(struct bna_rx *rx)
+{
+ if (rx->fsm != (bfa_sm_t)bna_rx_sm_stopped)
+ return;
+
+ rx->rx_flags |= BNA_RX_F_ENABLED;
+ if (rx->rx_flags & BNA_RX_F_ENET_STARTED)
+ bfa_fsm_send_event(rx, RX_E_START);
+}
+
+void
+bna_rx_disable(struct bna_rx *rx, enum bna_cleanup_type type,
+ void (*cbfn)(void *, struct bna_rx *))
+{
+ if (type == BNA_SOFT_CLEANUP) {
+ /* h/w should not be accessed. Treat we're stopped */
+ (*cbfn)(rx->bna->bnad, rx);
+ } else {
+ rx->stop_cbfn = cbfn;
+ rx->stop_cbarg = rx->bna->bnad;
+
+ rx->rx_flags &= ~BNA_RX_F_ENABLED;
+
+ bfa_fsm_send_event(rx, RX_E_STOP);
+ }
+}
+
+void
+bna_rx_cleanup_complete(struct bna_rx *rx)
+{
+ bfa_fsm_send_event(rx, RX_E_CLEANUP_DONE);
+}
+
+enum bna_cb_status
+bna_rx_mode_set(struct bna_rx *rx, enum bna_rxmode new_mode,
+ enum bna_rxmode bitmask,
+ void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+ struct bna_rxf *rxf = &rx->rxf;
+ int need_hw_config = 0;
+
+ /* Error checks */
+
+ if (is_promisc_enable(new_mode, bitmask)) {
+ /* If promisc mode is already enabled elsewhere in the system */
+ if ((rx->bna->promisc_rid != BFI_INVALID_RID) &&
+ (rx->bna->promisc_rid != rxf->rx->rid))
+ goto err_return;
+
+ /* If default mode is already enabled in the system */
+ if (rx->bna->default_mode_rid != BFI_INVALID_RID)
+ goto err_return;
+
+ /* Trying to enable promiscuous and default mode together */
+ if (is_default_enable(new_mode, bitmask))
+ goto err_return;
+ }
+
+ if (is_default_enable(new_mode, bitmask)) {
+ /* If default mode is already enabled elsewhere in the system */
+ if ((rx->bna->default_mode_rid != BFI_INVALID_RID) &&
+ (rx->bna->default_mode_rid != rxf->rx->rid)) {
+ goto err_return;
+ }
+
+ /* If promiscuous mode is already enabled in the system */
+ if (rx->bna->promisc_rid != BFI_INVALID_RID)
+ goto err_return;
+ }
+
+ /* Process the commands */
+
+ if (is_promisc_enable(new_mode, bitmask)) {
+ if (bna_rxf_promisc_enable(rxf))
+ need_hw_config = 1;
+ } else if (is_promisc_disable(new_mode, bitmask)) {
+ if (bna_rxf_promisc_disable(rxf))
+ need_hw_config = 1;
+ }
+
+ if (is_allmulti_enable(new_mode, bitmask)) {
+ if (bna_rxf_allmulti_enable(rxf))
+ need_hw_config = 1;
+ } else if (is_allmulti_disable(new_mode, bitmask)) {
+ if (bna_rxf_allmulti_disable(rxf))
+ need_hw_config = 1;
+ }
+
+ /* Trigger h/w if needed */
+
+ if (need_hw_config) {
+ rxf->cam_fltr_cbfn = cbfn;
+ rxf->cam_fltr_cbarg = rx->bna->bnad;
+ bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+ } else if (cbfn)
+ (*cbfn)(rx->bna->bnad, rx);
+
+ return BNA_CB_SUCCESS;
+
+err_return:
+ return BNA_CB_FAIL;
+}
+
+void
+bna_rx_vlanfilter_enable(struct bna_rx *rx)
+{
+ struct bna_rxf *rxf = &rx->rxf;
+
+ if (rxf->vlan_filter_status == BNA_STATUS_T_DISABLED) {
+ rxf->vlan_filter_status = BNA_STATUS_T_ENABLED;
+ rxf->vlan_pending_bitmask = (u8)BFI_VLAN_BMASK_ALL;
+ bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+ }
+}
+
+void
+bna_rx_coalescing_timeo_set(struct bna_rx *rx, int coalescing_timeo)
+{
+ struct bna_rxp *rxp;
+ struct list_head *qe;
+
+ list_for_each(qe, &rx->rxp_q) {
+ rxp = (struct bna_rxp *)qe;
+ rxp->cq.ccb->rx_coalescing_timeo = coalescing_timeo;
+ bna_ib_coalescing_timeo_set(&rxp->cq.ib, coalescing_timeo);
+ }
+}
+
+void
+bna_rx_dim_reconfig(struct bna *bna, const u32 vector[][BNA_BIAS_T_MAX])
+{
+ int i, j;
+
+ for (i = 0; i < BNA_LOAD_T_MAX; i++)
+ for (j = 0; j < BNA_BIAS_T_MAX; j++)
+ bna->rx_mod.dim_vector[i][j] = vector[i][j];
+}
+
+void
+bna_rx_dim_update(struct bna_ccb *ccb)
+{
+ struct bna *bna = ccb->cq->rx->bna;
+ u32 load, bias;
+ u32 pkt_rt, small_rt, large_rt;
+ u8 coalescing_timeo;
+
+ if ((ccb->pkt_rate.small_pkt_cnt == 0) &&
+ (ccb->pkt_rate.large_pkt_cnt == 0))
+ return;
+
+ /* Arrive at preconfigured coalescing timeo value based on pkt rate */
+
+ small_rt = ccb->pkt_rate.small_pkt_cnt;
+ large_rt = ccb->pkt_rate.large_pkt_cnt;
+
+ pkt_rt = small_rt + large_rt;
+
+ if (pkt_rt < BNA_PKT_RATE_10K)
+ load = BNA_LOAD_T_LOW_4;
+ else if (pkt_rt < BNA_PKT_RATE_20K)
+ load = BNA_LOAD_T_LOW_3;
+ else if (pkt_rt < BNA_PKT_RATE_30K)
+ load = BNA_LOAD_T_LOW_2;
+ else if (pkt_rt < BNA_PKT_RATE_40K)
+ load = BNA_LOAD_T_LOW_1;
+ else if (pkt_rt < BNA_PKT_RATE_50K)
+ load = BNA_LOAD_T_HIGH_1;
+ else if (pkt_rt < BNA_PKT_RATE_60K)
+ load = BNA_LOAD_T_HIGH_2;
+ else if (pkt_rt < BNA_PKT_RATE_80K)
+ load = BNA_LOAD_T_HIGH_3;
+ else
+ load = BNA_LOAD_T_HIGH_4;
+
+ if (small_rt > (large_rt << 1))
+ bias = 0;
+ else
+ bias = 1;
+
+ ccb->pkt_rate.small_pkt_cnt = 0;
+ ccb->pkt_rate.large_pkt_cnt = 0;
+
+ coalescing_timeo = bna->rx_mod.dim_vector[load][bias];
+ ccb->rx_coalescing_timeo = coalescing_timeo;
+
+ /* Set it to IB */
+ bna_ib_coalescing_timeo_set(&ccb->cq->ib, coalescing_timeo);
+}
+
+const u32 bna_napi_dim_vector[BNA_LOAD_T_MAX][BNA_BIAS_T_MAX] = {
+ {12, 12},
+ {6, 10},
+ {5, 10},
+ {4, 8},
+ {3, 6},
+ {3, 6},
+ {2, 4},
+ {1, 2},
+};
+
+/**
+ * TX
+ */
+#define call_tx_stop_cbfn(tx) \
+do { \
+ if ((tx)->stop_cbfn) { \
+ void (*cbfn)(void *, struct bna_tx *); \
+ void *cbarg; \
+ cbfn = (tx)->stop_cbfn; \
+ cbarg = (tx)->stop_cbarg; \
+ (tx)->stop_cbfn = NULL; \
+ (tx)->stop_cbarg = NULL; \
+ cbfn(cbarg, (tx)); \
+ } \
+} while (0)
+
+#define call_tx_prio_change_cbfn(tx) \
+do { \
+ if ((tx)->prio_change_cbfn) { \
+ void (*cbfn)(struct bnad *, struct bna_tx *); \
+ cbfn = (tx)->prio_change_cbfn; \
+ (tx)->prio_change_cbfn = NULL; \
+ cbfn((tx)->bna->bnad, (tx)); \
+ } \
+} while (0)
+
+static void bna_tx_mod_cb_tx_stopped(void *tx_mod, struct bna_tx *tx);
+static void bna_bfi_tx_enet_start(struct bna_tx *tx);
+static void bna_tx_enet_stop(struct bna_tx *tx);
+
+enum bna_tx_event {
+ TX_E_START = 1,
+ TX_E_STOP = 2,
+ TX_E_FAIL = 3,
+ TX_E_STARTED = 4,
+ TX_E_STOPPED = 5,
+ TX_E_PRIO_CHANGE = 6,
+ TX_E_CLEANUP_DONE = 7,
+ TX_E_BW_UPDATE = 8,
+};
+
+bfa_fsm_state_decl(bna_tx, stopped, struct bna_tx, enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, start_wait, struct bna_tx, enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, started, struct bna_tx, enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, stop_wait, struct bna_tx, enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, cleanup_wait, struct bna_tx,
+ enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, prio_stop_wait, struct bna_tx,
+ enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, prio_cleanup_wait, struct bna_tx,
+ enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, failed, struct bna_tx, enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, quiesce_wait, struct bna_tx,
+ enum bna_tx_event);
+
+static void
+bna_tx_sm_stopped_entry(struct bna_tx *tx)
+{
+ call_tx_stop_cbfn(tx);
+}
+
+static void
+bna_tx_sm_stopped(struct bna_tx *tx, enum bna_tx_event event)
+{
+ switch (event) {
+ case TX_E_START:
+ bfa_fsm_set_state(tx, bna_tx_sm_start_wait);
+ break;
+
+ case TX_E_STOP:
+ call_tx_stop_cbfn(tx);
+ break;
+
+ case TX_E_FAIL:
+ /* No-op */
+ break;
+
+ case TX_E_PRIO_CHANGE:
+ call_tx_prio_change_cbfn(tx);
+ break;
+
+ case TX_E_BW_UPDATE:
+ /* No-op */
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_tx_sm_start_wait_entry(struct bna_tx *tx)
+{
+ bna_bfi_tx_enet_start(tx);
+}
+
+static void
+bna_tx_sm_start_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+ switch (event) {
+ case TX_E_STOP:
+ tx->flags &= ~(BNA_TX_F_PRIO_CHANGED | BNA_TX_F_BW_UPDATED);
+ bfa_fsm_set_state(tx, bna_tx_sm_stop_wait);
+ break;
+
+ case TX_E_FAIL:
+ tx->flags &= ~(BNA_TX_F_PRIO_CHANGED | BNA_TX_F_BW_UPDATED);
+ bfa_fsm_set_state(tx, bna_tx_sm_stopped);
+ break;
+
+ case TX_E_STARTED:
+ if (tx->flags & (BNA_TX_F_PRIO_CHANGED | BNA_TX_F_BW_UPDATED)) {
+ tx->flags &= ~(BNA_TX_F_PRIO_CHANGED |
+ BNA_TX_F_BW_UPDATED);
+ bfa_fsm_set_state(tx, bna_tx_sm_prio_stop_wait);
+ } else
+ bfa_fsm_set_state(tx, bna_tx_sm_started);
+ break;
+
+ case TX_E_PRIO_CHANGE:
+ tx->flags |= BNA_TX_F_PRIO_CHANGED;
+ break;
+
+ case TX_E_BW_UPDATE:
+ tx->flags |= BNA_TX_F_BW_UPDATED;
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_tx_sm_started_entry(struct bna_tx *tx)
+{
+ struct bna_txq *txq;
+ struct list_head *qe;
+ int is_regular = (tx->type == BNA_TX_T_REGULAR);
+
+ list_for_each(qe, &tx->txq_q) {
+ txq = (struct bna_txq *)qe;
+ txq->tcb->priority = txq->priority;
+ /* Start IB */
+ bna_ib_start(tx->bna, &txq->ib, is_regular);
+ }
+ tx->tx_resume_cbfn(tx->bna->bnad, tx);
+}
+
+static void
+bna_tx_sm_started(struct bna_tx *tx, enum bna_tx_event event)
+{
+ switch (event) {
+ case TX_E_STOP:
+ bfa_fsm_set_state(tx, bna_tx_sm_stop_wait);
+ tx->tx_stall_cbfn(tx->bna->bnad, tx);
+ bna_tx_enet_stop(tx);
+ break;
+
+ case TX_E_FAIL:
+ bfa_fsm_set_state(tx, bna_tx_sm_failed);
+ tx->tx_stall_cbfn(tx->bna->bnad, tx);
+ tx->tx_cleanup_cbfn(tx->bna->bnad, tx);
+ break;
+
+ case TX_E_PRIO_CHANGE:
+ case TX_E_BW_UPDATE:
+ bfa_fsm_set_state(tx, bna_tx_sm_prio_stop_wait);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_tx_sm_stop_wait_entry(struct bna_tx *tx)
+{
+}
+
+static void
+bna_tx_sm_stop_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+ switch (event) {
+ case TX_E_FAIL:
+ case TX_E_STOPPED:
+ bfa_fsm_set_state(tx, bna_tx_sm_cleanup_wait);
+ tx->tx_cleanup_cbfn(tx->bna->bnad, tx);
+ break;
+
+ case TX_E_STARTED:
+ /**
+ * We are here due to start_wait -> stop_wait transition on
+ * TX_E_STOP event
+ */
+ bna_tx_enet_stop(tx);
+ break;
+
+ case TX_E_PRIO_CHANGE:
+ case TX_E_BW_UPDATE:
+ /* No-op */
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_tx_sm_cleanup_wait_entry(struct bna_tx *tx)
+{
+}
+
+static void
+bna_tx_sm_cleanup_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+ switch (event) {
+ case TX_E_FAIL:
+ case TX_E_PRIO_CHANGE:
+ case TX_E_BW_UPDATE:
+ /* No-op */
+ break;
+
+ case TX_E_CLEANUP_DONE:
+ bfa_fsm_set_state(tx, bna_tx_sm_stopped);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_tx_sm_prio_stop_wait_entry(struct bna_tx *tx)
+{
+ tx->tx_stall_cbfn(tx->bna->bnad, tx);
+ bna_tx_enet_stop(tx);
+}
+
+static void
+bna_tx_sm_prio_stop_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+ switch (event) {
+ case TX_E_STOP:
+ bfa_fsm_set_state(tx, bna_tx_sm_stop_wait);
+ break;
+
+ case TX_E_FAIL:
+ bfa_fsm_set_state(tx, bna_tx_sm_failed);
+ call_tx_prio_change_cbfn(tx);
+ tx->tx_cleanup_cbfn(tx->bna->bnad, tx);
+ break;
+
+ case TX_E_STOPPED:
+ bfa_fsm_set_state(tx, bna_tx_sm_prio_cleanup_wait);
+ break;
+
+ case TX_E_PRIO_CHANGE:
+ case TX_E_BW_UPDATE:
+ /* No-op */
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_tx_sm_prio_cleanup_wait_entry(struct bna_tx *tx)
+{
+ call_tx_prio_change_cbfn(tx);
+ tx->tx_cleanup_cbfn(tx->bna->bnad, tx);
+}
+
+static void
+bna_tx_sm_prio_cleanup_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+ switch (event) {
+ case TX_E_STOP:
+ bfa_fsm_set_state(tx, bna_tx_sm_cleanup_wait);
+ break;
+
+ case TX_E_FAIL:
+ bfa_fsm_set_state(tx, bna_tx_sm_failed);
+ break;
+
+ case TX_E_PRIO_CHANGE:
+ case TX_E_BW_UPDATE:
+ /* No-op */
+ break;
+
+ case TX_E_CLEANUP_DONE:
+ bfa_fsm_set_state(tx, bna_tx_sm_start_wait);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_tx_sm_failed_entry(struct bna_tx *tx)
+{
+}
+
+static void
+bna_tx_sm_failed(struct bna_tx *tx, enum bna_tx_event event)
+{
+ switch (event) {
+ case TX_E_START:
+ bfa_fsm_set_state(tx, bna_tx_sm_quiesce_wait);
+ break;
+
+ case TX_E_STOP:
+ bfa_fsm_set_state(tx, bna_tx_sm_cleanup_wait);
+ break;
+
+ case TX_E_FAIL:
+ /* No-op */
+ break;
+
+ case TX_E_CLEANUP_DONE:
+ bfa_fsm_set_state(tx, bna_tx_sm_stopped);
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_tx_sm_quiesce_wait_entry(struct bna_tx *tx)
+{
+}
+
+static void
+bna_tx_sm_quiesce_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+ switch (event) {
+ case TX_E_STOP:
+ bfa_fsm_set_state(tx, bna_tx_sm_cleanup_wait);
+ break;
+
+ case TX_E_FAIL:
+ bfa_fsm_set_state(tx, bna_tx_sm_failed);
+ break;
+
+ case TX_E_CLEANUP_DONE:
+ bfa_fsm_set_state(tx, bna_tx_sm_start_wait);
+ break;
+
+ case TX_E_BW_UPDATE:
+ /* No-op */
+ break;
+
+ default:
+ bfa_sm_fault(event);
+ }
+}
+
+static void
+bna_bfi_tx_enet_start(struct bna_tx *tx)
+{
+ struct bfi_enet_tx_cfg_req *cfg_req = &tx->bfi_enet_cmd.cfg_req;
+ struct bna_txq *txq = NULL;
+ struct list_head *qe;
+ int i;
+
+ bfi_msgq_mhdr_set(cfg_req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_TX_CFG_SET_REQ, 0, tx->rid);
+ cfg_req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_tx_cfg_req)));
+
+ cfg_req->num_queues = tx->num_txq;
+ for (i = 0, qe = bfa_q_first(&tx->txq_q);
+ i < tx->num_txq;
+ i++, qe = bfa_q_next(qe)) {
+ txq = (struct bna_txq *)qe;
+
+ bfi_enet_datapath_q_init(&cfg_req->q_cfg[i].q.q, &txq->qpt);
+ cfg_req->q_cfg[i].q.priority = txq->priority;
+
+ cfg_req->q_cfg[i].ib.index_addr.a32.addr_lo =
+ txq->ib.ib_seg_host_addr.lsb;
+ cfg_req->q_cfg[i].ib.index_addr.a32.addr_hi =
+ txq->ib.ib_seg_host_addr.msb;
+ cfg_req->q_cfg[i].ib.intr.msix_index =
+ htons((u16)txq->ib.intr_vector);
+ }
+
+ cfg_req->ib_cfg.int_pkt_dma = BNA_STATUS_T_ENABLED;
+ cfg_req->ib_cfg.int_enabled = BNA_STATUS_T_ENABLED;
+ cfg_req->ib_cfg.int_pkt_enabled = BNA_STATUS_T_DISABLED;
+ cfg_req->ib_cfg.continuous_coalescing = BNA_STATUS_T_ENABLED;
+ cfg_req->ib_cfg.msix = (txq->ib.intr_type == BNA_INTR_T_MSIX)
+ ? BNA_STATUS_T_ENABLED : BNA_STATUS_T_DISABLED;
+ cfg_req->ib_cfg.coalescing_timeout =
+ htonl((u32)txq->ib.coalescing_timeo);
+ cfg_req->ib_cfg.inter_pkt_timeout =
+ htonl((u32)txq->ib.interpkt_timeo);
+ cfg_req->ib_cfg.inter_pkt_count = (u8)txq->ib.interpkt_count;
+
+ cfg_req->tx_cfg.vlan_mode = BFI_ENET_TX_VLAN_WI;
+ cfg_req->tx_cfg.vlan_id = htons((u16)tx->txf_vlan_id);
+ cfg_req->tx_cfg.admit_tagged_frame = BNA_STATUS_T_DISABLED;
+ cfg_req->tx_cfg.apply_vlan_filter = BNA_STATUS_T_DISABLED;
+
+ bfa_msgq_cmd_set(&tx->msgq_cmd, NULL, NULL,
+ sizeof(struct bfi_enet_tx_cfg_req), &cfg_req->mh);
+ bfa_msgq_cmd_post(&tx->bna->msgq, &tx->msgq_cmd);
+}
+
+static void
+bna_bfi_tx_enet_stop(struct bna_tx *tx)
+{
+ struct bfi_enet_req *req = &tx->bfi_enet_cmd.req;
+
+ bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+ BFI_ENET_H2I_TX_CFG_CLR_REQ, 0, tx->rid);
+ req->mh.num_entries = htons(
+ bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_req)));
+ bfa_msgq_cmd_set(&tx->msgq_cmd, NULL, NULL, sizeof(struct bfi_enet_req),
+ &req->mh);
+ bfa_msgq_cmd_post(&tx->bna->msgq, &tx->msgq_cmd);
+}
+
+static void
+bna_tx_enet_stop(struct bna_tx *tx)
+{
+ struct bna_txq *txq;
+ struct list_head *qe;
+
+ /* Stop IB */
+ list_for_each(qe, &tx->txq_q) {
+ txq = (struct bna_txq *)qe;
+ bna_ib_stop(tx->bna, &txq->ib);
+ }
+
+ bna_bfi_tx_enet_stop(tx);
+}
+
+static void
+bna_txq_qpt_setup(struct bna_txq *txq, int page_count, int page_size,
+ struct bna_mem_descr *qpt_mem,
+ struct bna_mem_descr *swqpt_mem,
+ struct bna_mem_descr *page_mem)
+{
+ int i;
+
+ txq->qpt.hw_qpt_ptr.lsb = qpt_mem->dma.lsb;
+ txq->qpt.hw_qpt_ptr.msb = qpt_mem->dma.msb;
+ txq->qpt.kv_qpt_ptr = qpt_mem->kva;
+ txq->qpt.page_count = page_count;
+ txq->qpt.page_size = page_size;
+
+ txq->tcb->sw_qpt = (void **) swqpt_mem->kva;
+
+ for (i = 0; i < page_count; i++) {
+ txq->tcb->sw_qpt[i] = page_mem[i].kva;
+
+ ((struct bna_dma_addr *)txq->qpt.kv_qpt_ptr)[i].lsb =
+ page_mem[i].dma.lsb;
+ ((struct bna_dma_addr *)txq->qpt.kv_qpt_ptr)[i].msb =
+ page_mem[i].dma.msb;
+ }
+}
+
+static struct bna_tx *
+bna_tx_get(struct bna_tx_mod *tx_mod, enum bna_tx_type type)
+{
+ struct list_head *qe = NULL;
+ struct bna_tx *tx = NULL;
+
+ if (list_empty(&tx_mod->tx_free_q))
+ return NULL;
+ if (type == BNA_TX_T_REGULAR) {
+ bfa_q_deq(&tx_mod->tx_free_q, &qe);
+ } else {
+ bfa_q_deq_tail(&tx_mod->tx_free_q, &qe);
+ }
+ tx = (struct bna_tx *)qe;
+ bfa_q_qe_init(&tx->qe);
+ tx->type = type;
+
+ return tx;
+}
+
+static void
+bna_tx_free(struct bna_tx *tx)
+{
+ struct bna_tx_mod *tx_mod = &tx->bna->tx_mod;
+ struct bna_txq *txq;
+ struct list_head *prev_qe;
+ struct list_head *qe;
+
+ while (!list_empty(&tx->txq_q)) {
+ bfa_q_deq(&tx->txq_q, &txq);
+ bfa_q_qe_init(&txq->qe);
+ txq->tcb = NULL;
+ txq->tx = NULL;
+ list_add_tail(&txq->qe, &tx_mod->txq_free_q);
+ }
+
+ list_for_each(qe, &tx_mod->tx_active_q) {
+ if (qe == &tx->qe) {
+ list_del(&tx->qe);
+ bfa_q_qe_init(&tx->qe);
+ break;
+ }
+ }
+
+ tx->bna = NULL;
+ tx->priv = NULL;
+
+ prev_qe = NULL;
+ list_for_each(qe, &tx_mod->tx_free_q) {
+ if (((struct bna_tx *)qe)->rid < tx->rid)
+ prev_qe = qe;
+ else {
+ break;
+ }
+ }
+
+ if (prev_qe == NULL) {
+ /* This is the first entry */
+ bfa_q_enq_head(&tx_mod->tx_free_q, &tx->qe);
+ } else if (bfa_q_next(prev_qe) == &tx_mod->tx_free_q) {
+ /* This is the last entry */
+ list_add_tail(&tx->qe, &tx_mod->tx_free_q);
+ } else {
+ /* Somewhere in the middle */
+ bfa_q_next(&tx->qe) = bfa_q_next(prev_qe);
+ bfa_q_prev(&tx->qe) = prev_qe;
+ bfa_q_next(prev_qe) = &tx->qe;
+ bfa_q_prev(bfa_q_next(&tx->qe)) = &tx->qe;
+ }
+}
+
+static void
+bna_tx_start(struct bna_tx *tx)
+{
+ tx->flags |= BNA_TX_F_ENET_STARTED;
+ if (tx->flags & BNA_TX_F_ENABLED)
+ bfa_fsm_send_event(tx, TX_E_START);
+}
+
+static void
+bna_tx_stop(struct bna_tx *tx)
+{
+ tx->stop_cbfn = bna_tx_mod_cb_tx_stopped;
+ tx->stop_cbarg = &tx->bna->tx_mod;
+
+ tx->flags &= ~BNA_TX_F_ENET_STARTED;
+ bfa_fsm_send_event(tx, TX_E_STOP);
+}
+
+static void
+bna_tx_fail(struct bna_tx *tx)
+{
+ tx->flags &= ~BNA_TX_F_ENET_STARTED;
+ bfa_fsm_send_event(tx, TX_E_FAIL);
+}
+
+void
+bna_bfi_tx_enet_start_rsp(struct bna_tx *tx, struct bfi_msgq_mhdr *msghdr)
+{
+ struct bfi_enet_tx_cfg_rsp *cfg_rsp = &tx->bfi_enet_cmd.cfg_rsp;
+ struct bna_txq *txq = NULL;
+ struct list_head *qe;
+ int i;
+
+ bfa_msgq_rsp_copy(&tx->bna->msgq, (u8 *)cfg_rsp,
+ sizeof(struct bfi_enet_tx_cfg_rsp));
+
+ tx->hw_id = cfg_rsp->hw_id;
+
+ for (i = 0, qe = bfa_q_first(&tx->txq_q);
+ i < tx->num_txq; i++, qe = bfa_q_next(qe)) {
+ txq = (struct bna_txq *)qe;
+
+ /* Setup doorbells */
+ txq->tcb->i_dbell->doorbell_addr =
+ tx->bna->pcidev.pci_bar_kva
+ + ntohl(cfg_rsp->q_handles[i].i_dbell);
+ txq->tcb->q_dbell =
+ tx->bna->pcidev.pci_bar_kva
+ + ntohl(cfg_rsp->q_handles[i].q_dbell);
+ txq->hw_id = cfg_rsp->q_handles[i].hw_qid;
+
+ /* Initialize producer/consumer indexes */
+ (*txq->tcb->hw_consumer_index) = 0;
+ txq->tcb->producer_index = txq->tcb->consumer_index = 0;
+ }
+
+ bfa_fsm_send_event(tx, TX_E_STARTED);
+}
+
+void
+bna_bfi_tx_enet_stop_rsp(struct bna_tx *tx, struct bfi_msgq_mhdr *msghdr)
+{
+ bfa_fsm_send_event(tx, TX_E_STOPPED);
+}
+
+void
+bna_bfi_bw_update_aen(struct bna_tx_mod *tx_mod)
+{
+ struct bna_tx *tx;
+ struct list_head *qe;
+
+ list_for_each(qe, &tx_mod->tx_active_q) {
+ tx = (struct bna_tx *)qe;
+ bfa_fsm_send_event(tx, TX_E_BW_UPDATE);
+ }
+}
+
+void
+bna_tx_res_req(int num_txq, int txq_depth, struct bna_res_info *res_info)
+{
+ u32 q_size;
+ u32 page_count;
+ struct bna_mem_info *mem_info;
+
+ res_info[BNA_TX_RES_MEM_T_TCB].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_TX_RES_MEM_T_TCB].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_KVA;
+ mem_info->len = sizeof(struct bna_tcb);
+ mem_info->num = num_txq;
+
+ q_size = txq_depth * BFI_TXQ_WI_SIZE;
+ q_size = ALIGN(q_size, PAGE_SIZE);
+ page_count = q_size >> PAGE_SHIFT;
+
+ res_info[BNA_TX_RES_MEM_T_QPT].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_TX_RES_MEM_T_QPT].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_DMA;
+ mem_info->len = page_count * sizeof(struct bna_dma_addr);
+ mem_info->num = num_txq;
+
+ res_info[BNA_TX_RES_MEM_T_SWQPT].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_TX_RES_MEM_T_SWQPT].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_KVA;
+ mem_info->len = page_count * sizeof(void *);
+ mem_info->num = num_txq;
+
+ res_info[BNA_TX_RES_MEM_T_PAGE].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_TX_RES_MEM_T_PAGE].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_DMA;
+ mem_info->len = PAGE_SIZE;
+ mem_info->num = num_txq * page_count;
+
+ res_info[BNA_TX_RES_MEM_T_IBIDX].res_type = BNA_RES_T_MEM;
+ mem_info = &res_info[BNA_TX_RES_MEM_T_IBIDX].res_u.mem_info;
+ mem_info->mem_type = BNA_MEM_T_DMA;
+ mem_info->len = BFI_IBIDX_SIZE;
+ mem_info->num = num_txq;
+
+ res_info[BNA_TX_RES_INTR_T_TXCMPL].res_type = BNA_RES_T_INTR;
+ res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info.intr_type =
+ BNA_INTR_T_MSIX;
+ res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info.num = num_txq;
+}
+
+struct bna_tx *
+bna_tx_create(struct bna *bna, struct bnad *bnad,
+ struct bna_tx_config *tx_cfg,
+ const struct bna_tx_event_cbfn *tx_cbfn,
+ struct bna_res_info *res_info, void *priv)
+{
+ struct bna_intr_info *intr_info;
+ struct bna_tx_mod *tx_mod = &bna->tx_mod;
+ struct bna_tx *tx;
+ struct bna_txq *txq;
+ struct list_head *qe;
+ int page_count;
+ int page_size;
+ int page_idx;
+ int i;
+
+ intr_info = &res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info;
+ page_count = (res_info[BNA_TX_RES_MEM_T_PAGE].res_u.mem_info.num) /
+ tx_cfg->num_txq;
+ page_size = res_info[BNA_TX_RES_MEM_T_PAGE].res_u.mem_info.len;
+
+ /**
+ * Get resources
+ */
+
+ if ((intr_info->num != 1) && (intr_info->num != tx_cfg->num_txq))
+ return NULL;
+
+ /* Tx */
+
+ tx = bna_tx_get(tx_mod, tx_cfg->tx_type);
+ if (!tx)
+ return NULL;
+ tx->bna = bna;
+ tx->priv = priv;
+
+ /* TxQs */
+
+ INIT_LIST_HEAD(&tx->txq_q);
+ for (i = 0; i < tx_cfg->num_txq; i++) {
+ if (list_empty(&tx_mod->txq_free_q))
+ goto err_return;
+
+ bfa_q_deq(&tx_mod->txq_free_q, &txq);
+ bfa_q_qe_init(&txq->qe);
+ list_add_tail(&txq->qe, &tx->txq_q);
+ txq->tx = tx;
+ }
+
+ /*
+ * Initialize
+ */
+
+ /* Tx */
+
+ tx->tcb_setup_cbfn = tx_cbfn->tcb_setup_cbfn;
+ tx->tcb_destroy_cbfn = tx_cbfn->tcb_destroy_cbfn;
+ /* Following callbacks are mandatory */
+ tx->tx_stall_cbfn = tx_cbfn->tx_stall_cbfn;
+ tx->tx_resume_cbfn = tx_cbfn->tx_resume_cbfn;
+ tx->tx_cleanup_cbfn = tx_cbfn->tx_cleanup_cbfn;
+
+ list_add_tail(&tx->qe, &tx_mod->tx_active_q);
+
+ tx->num_txq = tx_cfg->num_txq;
+
+ tx->flags = 0;
+ if (tx->bna->tx_mod.flags & BNA_TX_MOD_F_ENET_STARTED) {
+ switch (tx->type) {
+ case BNA_TX_T_REGULAR:
+ if (!(tx->bna->tx_mod.flags &
+ BNA_TX_MOD_F_ENET_LOOPBACK))
+ tx->flags |= BNA_TX_F_ENET_STARTED;
+ break;
+ case BNA_TX_T_LOOPBACK:
+ if (tx->bna->tx_mod.flags & BNA_TX_MOD_F_ENET_LOOPBACK)
+ tx->flags |= BNA_TX_F_ENET_STARTED;
+ break;
+ }
+ }
+
+ /* TxQ */
+
+ i = 0;
+ page_idx = 0;
+ list_for_each(qe, &tx->txq_q) {
+ txq = (struct bna_txq *)qe;
+ txq->tcb = (struct bna_tcb *)
+ res_info[BNA_TX_RES_MEM_T_TCB].res_u.mem_info.mdl[i].kva;
+ txq->tx_packets = 0;
+ txq->tx_bytes = 0;
+
+ /* IB */
+ txq->ib.ib_seg_host_addr.lsb =
+ res_info[BNA_TX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].dma.lsb;
+ txq->ib.ib_seg_host_addr.msb =
+ res_info[BNA_TX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].dma.msb;
+ txq->ib.ib_seg_host_addr_kva =
+ res_info[BNA_TX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].kva;
+ txq->ib.intr_type = intr_info->intr_type;
+ txq->ib.intr_vector = (intr_info->num == 1) ?
+ intr_info->idl[0].vector :
+ intr_info->idl[i].vector;
+ if (intr_info->intr_type == BNA_INTR_T_INTX)
+ txq->ib.intr_vector = (1 << txq->ib.intr_vector);
+ txq->ib.coalescing_timeo = tx_cfg->coalescing_timeo;
+ txq->ib.interpkt_timeo = 0; /* Not used */
+ txq->ib.interpkt_count = BFI_TX_INTERPKT_COUNT;
+
+ /* TCB */
+
+ txq->tcb->q_depth = tx_cfg->txq_depth;
+ txq->tcb->unmap_q = (void *)
+ res_info[BNA_TX_RES_MEM_T_UNMAPQ].res_u.mem_info.mdl[i].kva;
+ txq->tcb->hw_consumer_index =
+ (u32 *)txq->ib.ib_seg_host_addr_kva;
+ txq->tcb->i_dbell = &txq->ib.door_bell;
+ txq->tcb->intr_type = txq->ib.intr_type;
+ txq->tcb->intr_vector = txq->ib.intr_vector;
+ txq->tcb->txq = txq;
+ txq->tcb->bnad = bnad;
+ txq->tcb->id = i;
+
+ /* QPT, SWQPT, Pages */
+ bna_txq_qpt_setup(txq, page_count, page_size,
+ &res_info[BNA_TX_RES_MEM_T_QPT].res_u.mem_info.mdl[i],
+ &res_info[BNA_TX_RES_MEM_T_SWQPT].res_u.mem_info.mdl[i],
+ &res_info[BNA_TX_RES_MEM_T_PAGE].
+ res_u.mem_info.mdl[page_idx]);
+ txq->tcb->page_idx = page_idx;
+ txq->tcb->page_count = page_count;
+ page_idx += page_count;
+
+ /* Callback to bnad for setting up TCB */
+ if (tx->tcb_setup_cbfn)
+ (tx->tcb_setup_cbfn)(bna->bnad, txq->tcb);
+
+ if (tx_cfg->num_txq == BFI_TX_MAX_PRIO)
+ txq->priority = txq->tcb->id;
+ else
+ txq->priority = tx_mod->default_prio;
+
+ i++;
+ }
+
+ tx->txf_vlan_id = 0;
+
+ bfa_fsm_set_state(tx, bna_tx_sm_stopped);
+
+ tx_mod->rid_mask |= (1 << tx->rid);
+
+ return tx;
+
+err_return:
+ bna_tx_free(tx);
+ return NULL;
+}
+
+void
+bna_tx_destroy(struct bna_tx *tx)
+{
+ struct bna_txq *txq;
+ struct list_head *qe;
+
+ list_for_each(qe, &tx->txq_q) {
+ txq = (struct bna_txq *)qe;
+ if (tx->tcb_destroy_cbfn)
+ (tx->tcb_destroy_cbfn)(tx->bna->bnad, txq->tcb);
+ }
+
+ tx->bna->tx_mod.rid_mask &= ~(1 << tx->rid);
+ bna_tx_free(tx);
+}
+
+void
+bna_tx_enable(struct bna_tx *tx)
+{
+ if (tx->fsm != (bfa_sm_t)bna_tx_sm_stopped)
+ return;
+
+ tx->flags |= BNA_TX_F_ENABLED;
+
+ if (tx->flags & BNA_TX_F_ENET_STARTED)
+ bfa_fsm_send_event(tx, TX_E_START);
+}
+
+void
+bna_tx_disable(struct bna_tx *tx, enum bna_cleanup_type type,
+ void (*cbfn)(void *, struct bna_tx *))
+{
+ if (type == BNA_SOFT_CLEANUP) {
+ (*cbfn)(tx->bna->bnad, tx);
+ return;
+ }
+
+ tx->stop_cbfn = cbfn;
+ tx->stop_cbarg = tx->bna->bnad;
+
+ tx->flags &= ~BNA_TX_F_ENABLED;
+
+ bfa_fsm_send_event(tx, TX_E_STOP);
+}
+
+void
+bna_tx_cleanup_complete(struct bna_tx *tx)
+{
+ bfa_fsm_send_event(tx, TX_E_CLEANUP_DONE);
+}
+
+static void
+bna_tx_mod_cb_tx_stopped(void *arg, struct bna_tx *tx)
+{
+ struct bna_tx_mod *tx_mod = (struct bna_tx_mod *)arg;
+
+ bfa_wc_down(&tx_mod->tx_stop_wc);
+}
+
+static void
+bna_tx_mod_cb_tx_stopped_all(void *arg)
+{
+ struct bna_tx_mod *tx_mod = (struct bna_tx_mod *)arg;
+
+ if (tx_mod->stop_cbfn)
+ tx_mod->stop_cbfn(&tx_mod->bna->enet);
+ tx_mod->stop_cbfn = NULL;
+}
+
+void
+bna_tx_mod_init(struct bna_tx_mod *tx_mod, struct bna *bna,
+ struct bna_res_info *res_info)
+{
+ int i;
+
+ tx_mod->bna = bna;
+ tx_mod->flags = 0;
+
+ tx_mod->tx = (struct bna_tx *)
+ res_info[BNA_MOD_RES_MEM_T_TX_ARRAY].res_u.mem_info.mdl[0].kva;
+ tx_mod->txq = (struct bna_txq *)
+ res_info[BNA_MOD_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.mdl[0].kva;
+
+ INIT_LIST_HEAD(&tx_mod->tx_free_q);
+ INIT_LIST_HEAD(&tx_mod->tx_active_q);
+
+ INIT_LIST_HEAD(&tx_mod->txq_free_q);
+
+ for (i = 0; i < bna->ioceth.attr.num_txq; i++) {
+ tx_mod->tx[i].rid = i;
+ bfa_q_qe_init(&tx_mod->tx[i].qe);
+ list_add_tail(&tx_mod->tx[i].qe, &tx_mod->tx_free_q);
+ bfa_q_qe_init(&tx_mod->txq[i].qe);
+ list_add_tail(&tx_mod->txq[i].qe, &tx_mod->txq_free_q);
+ }
+
+ tx_mod->prio_map = BFI_TX_PRIO_MAP_ALL;
+ tx_mod->default_prio = 0;
+ tx_mod->iscsi_over_cee = BNA_STATUS_T_DISABLED;
+ tx_mod->iscsi_prio = -1;
+}
+
+void
+bna_tx_mod_uninit(struct bna_tx_mod *tx_mod)
+{
+ struct list_head *qe;
+ int i;
+
+ i = 0;
+ list_for_each(qe, &tx_mod->tx_free_q)
+ i++;
+
+ i = 0;
+ list_for_each(qe, &tx_mod->txq_free_q)
+ i++;
+
+ tx_mod->bna = NULL;
+}
+
+void
+bna_tx_mod_start(struct bna_tx_mod *tx_mod, enum bna_tx_type type)
+{
+ struct bna_tx *tx;
+ struct list_head *qe;
+
+ tx_mod->flags |= BNA_TX_MOD_F_ENET_STARTED;
+ if (type == BNA_TX_T_LOOPBACK)
+ tx_mod->flags |= BNA_TX_MOD_F_ENET_LOOPBACK;
+
+ list_for_each(qe, &tx_mod->tx_active_q) {
+ tx = (struct bna_tx *)qe;
+ if (tx->type == type)
+ bna_tx_start(tx);
+ }
+}
+
+void
+bna_tx_mod_stop(struct bna_tx_mod *tx_mod, enum bna_tx_type type)
+{
+ struct bna_tx *tx;
+ struct list_head *qe;
+
+ tx_mod->flags &= ~BNA_TX_MOD_F_ENET_STARTED;
+ tx_mod->flags &= ~BNA_TX_MOD_F_ENET_LOOPBACK;
+
+ tx_mod->stop_cbfn = bna_enet_cb_tx_stopped;
+
+ bfa_wc_init(&tx_mod->tx_stop_wc, bna_tx_mod_cb_tx_stopped_all, tx_mod);
+
+ list_for_each(qe, &tx_mod->tx_active_q) {
+ tx = (struct bna_tx *)qe;
+ if (tx->type == type) {
+ bfa_wc_up(&tx_mod->tx_stop_wc);
+ bna_tx_stop(tx);
+ }
+ }
+
+ bfa_wc_wait(&tx_mod->tx_stop_wc);
+}
+
+void
+bna_tx_mod_fail(struct bna_tx_mod *tx_mod)
+{
+ struct bna_tx *tx;
+ struct list_head *qe;
+
+ tx_mod->flags &= ~BNA_TX_MOD_F_ENET_STARTED;
+ tx_mod->flags &= ~BNA_TX_MOD_F_ENET_LOOPBACK;
+
+ list_for_each(qe, &tx_mod->tx_active_q) {
+ tx = (struct bna_tx *)qe;
+ bna_tx_fail(tx);
+ }
+}
+
+void
+bna_tx_coalescing_timeo_set(struct bna_tx *tx, int coalescing_timeo)
+{
+ struct bna_txq *txq;
+ struct list_head *qe;
+
+ list_for_each(qe, &tx->txq_q) {
+ txq = (struct bna_txq *)qe;
+ bna_ib_coalescing_timeo_set(&txq->ib, coalescing_timeo);
+ }
+}
diff --git a/drivers/net/bna/bna_types.h b/drivers/net/ethernet/brocade/bna/bna_types.h
index b9c134f7ad31..d090fbfb12fa 100644
--- a/drivers/net/bna/bna_types.h
+++ b/drivers/net/ethernet/brocade/bna/bna_types.h
@@ -19,8 +19,9 @@
#define __BNA_TYPES_H__
#include "cna.h"
-#include "bna_hw.h"
+#include "bna_hw_defs.h"
#include "bfa_cee.h"
+#include "bfa_msgq.h"
/**
*
@@ -28,13 +29,14 @@
*
*/
+struct bna_mcam_handle;
struct bna_txq;
struct bna_tx;
struct bna_rxq;
struct bna_cq;
struct bna_rx;
struct bna_rxf;
-struct bna_port;
+struct bna_enet;
struct bna;
struct bnad;
@@ -50,12 +52,12 @@ enum bna_status {
};
enum bna_cleanup_type {
- BNA_HARD_CLEANUP = 0,
- BNA_SOFT_CLEANUP = 1
+ BNA_HARD_CLEANUP = 0,
+ BNA_SOFT_CLEANUP = 1
};
enum bna_cb_status {
- BNA_CB_SUCCESS = 0,
+ BNA_CB_SUCCESS = 0,
BNA_CB_FAIL = 1,
BNA_CB_INTERRUPT = 2,
BNA_CB_BUSY = 3,
@@ -72,8 +74,8 @@ enum bna_res_type {
};
enum bna_mem_type {
- BNA_MEM_T_KVA = 1,
- BNA_MEM_T_DMA = 2
+ BNA_MEM_T_KVA = 1,
+ BNA_MEM_T_DMA = 2
};
enum bna_intr_type {
@@ -82,35 +84,33 @@ enum bna_intr_type {
};
enum bna_res_req_type {
- BNA_RES_MEM_T_COM = 0,
- BNA_RES_MEM_T_ATTR = 1,
- BNA_RES_MEM_T_FWTRC = 2,
- BNA_RES_MEM_T_STATS = 3,
- BNA_RES_MEM_T_SWSTATS = 4,
- BNA_RES_MEM_T_IBIDX = 5,
- BNA_RES_MEM_T_IB_ARRAY = 6,
- BNA_RES_MEM_T_INTR_ARRAY = 7,
- BNA_RES_MEM_T_IDXSEG_ARRAY = 8,
- BNA_RES_MEM_T_TX_ARRAY = 9,
- BNA_RES_MEM_T_TXQ_ARRAY = 10,
- BNA_RES_MEM_T_RX_ARRAY = 11,
- BNA_RES_MEM_T_RXP_ARRAY = 12,
- BNA_RES_MEM_T_RXQ_ARRAY = 13,
- BNA_RES_MEM_T_UCMAC_ARRAY = 14,
- BNA_RES_MEM_T_MCMAC_ARRAY = 15,
- BNA_RES_MEM_T_RIT_ENTRY = 16,
- BNA_RES_MEM_T_RIT_SEGMENT = 17,
- BNA_RES_INTR_T_MBOX = 18,
+ BNA_RES_MEM_T_COM = 0,
+ BNA_RES_MEM_T_ATTR = 1,
+ BNA_RES_MEM_T_FWTRC = 2,
+ BNA_RES_MEM_T_STATS = 3,
BNA_RES_T_MAX
};
+enum bna_mod_res_req_type {
+ BNA_MOD_RES_MEM_T_TX_ARRAY = 0,
+ BNA_MOD_RES_MEM_T_TXQ_ARRAY = 1,
+ BNA_MOD_RES_MEM_T_RX_ARRAY = 2,
+ BNA_MOD_RES_MEM_T_RXP_ARRAY = 3,
+ BNA_MOD_RES_MEM_T_RXQ_ARRAY = 4,
+ BNA_MOD_RES_MEM_T_UCMAC_ARRAY = 5,
+ BNA_MOD_RES_MEM_T_MCMAC_ARRAY = 6,
+ BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY = 7,
+ BNA_MOD_RES_T_MAX
+};
+
enum bna_tx_res_req_type {
BNA_TX_RES_MEM_T_TCB = 0,
BNA_TX_RES_MEM_T_UNMAPQ = 1,
- BNA_TX_RES_MEM_T_QPT = 2,
+ BNA_TX_RES_MEM_T_QPT = 2,
BNA_TX_RES_MEM_T_SWQPT = 3,
- BNA_TX_RES_MEM_T_PAGE = 4,
- BNA_TX_RES_INTR_T_TXCMPL = 5,
+ BNA_TX_RES_MEM_T_PAGE = 4,
+ BNA_TX_RES_MEM_T_IBIDX = 5,
+ BNA_TX_RES_INTR_T_TXCMPL = 6,
BNA_TX_RES_T_MAX,
};
@@ -127,13 +127,10 @@ enum bna_rx_mem_type {
BNA_RX_RES_MEM_T_DSWQPT = 9, /* RX s/w QPT */
BNA_RX_RES_MEM_T_DPAGE = 10, /* RX s/w QPT */
BNA_RX_RES_MEM_T_HPAGE = 11, /* RX s/w QPT */
- BNA_RX_RES_T_INTR = 12, /* Rx interrupts */
- BNA_RX_RES_T_MAX = 13
-};
-
-enum bna_mbox_state {
- BNA_MBOX_FREE = 0,
- BNA_MBOX_POSTED = 1
+ BNA_RX_RES_MEM_T_IBIDX = 12,
+ BNA_RX_RES_MEM_T_RIT = 13,
+ BNA_RX_RES_T_INTR = 14, /* Rx interrupts */
+ BNA_RX_RES_T_MAX = 15
};
enum bna_tx_type {
@@ -142,14 +139,15 @@ enum bna_tx_type {
};
enum bna_tx_flags {
- BNA_TX_F_PORT_STARTED = 1,
+ BNA_TX_F_ENET_STARTED = 1,
BNA_TX_F_ENABLED = 2,
- BNA_TX_F_PRIO_LOCK = 4,
+ BNA_TX_F_PRIO_CHANGED = 4,
+ BNA_TX_F_BW_UPDATED = 8,
};
enum bna_tx_mod_flags {
- BNA_TX_MOD_F_PORT_STARTED = 1,
- BNA_TX_MOD_F_PORT_LOOPBACK = 2,
+ BNA_TX_MOD_F_ENET_STARTED = 1,
+ BNA_TX_MOD_F_ENET_LOOPBACK = 2,
};
enum bna_rx_type {
@@ -158,106 +156,85 @@ enum bna_rx_type {
};
enum bna_rxp_type {
- BNA_RXP_SINGLE = 1,
- BNA_RXP_SLR = 2,
- BNA_RXP_HDS = 3
+ BNA_RXP_SINGLE = 1,
+ BNA_RXP_SLR = 2,
+ BNA_RXP_HDS = 3
};
enum bna_rxmode {
- BNA_RXMODE_PROMISC = 1,
- BNA_RXMODE_ALLMULTI = 2
+ BNA_RXMODE_PROMISC = 1,
+ BNA_RXMODE_DEFAULT = 2,
+ BNA_RXMODE_ALLMULTI = 4
};
enum bna_rx_event {
RX_E_START = 1,
RX_E_STOP = 2,
RX_E_FAIL = 3,
- RX_E_RXF_STARTED = 4,
- RX_E_RXF_STOPPED = 5,
- RX_E_RXQ_STOPPED = 6,
-};
-
-enum bna_rx_state {
- BNA_RX_STOPPED = 1,
- BNA_RX_RXF_START_WAIT = 2,
- BNA_RX_STARTED = 3,
- BNA_RX_RXF_STOP_WAIT = 4,
- BNA_RX_RXQ_STOP_WAIT = 5,
+ RX_E_STARTED = 4,
+ RX_E_STOPPED = 5,
+ RX_E_RXF_STARTED = 6,
+ RX_E_RXF_STOPPED = 7,
+ RX_E_CLEANUP_DONE = 8,
};
enum bna_rx_flags {
- BNA_RX_F_ENABLE = 0x01, /* bnad enabled rxf */
- BNA_RX_F_PORT_ENABLED = 0x02, /* Port object is enabled */
- BNA_RX_F_PORT_FAILED = 0x04, /* Port in failed state */
+ BNA_RX_F_ENET_STARTED = 1,
+ BNA_RX_F_ENABLED = 2,
};
enum bna_rx_mod_flags {
- BNA_RX_MOD_F_PORT_STARTED = 1,
- BNA_RX_MOD_F_PORT_LOOPBACK = 2,
-};
-
-enum bna_rxf_oper_state {
- BNA_RXF_OPER_STATE_RUNNING = 0x01, /* rxf operational */
- BNA_RXF_OPER_STATE_PAUSED = 0x02, /* rxf in PAUSED state */
+ BNA_RX_MOD_F_ENET_STARTED = 1,
+ BNA_RX_MOD_F_ENET_LOOPBACK = 2,
};
enum bna_rxf_flags {
- BNA_RXF_FL_STOP_PENDING = 0x01,
- BNA_RXF_FL_FAILED = 0x02,
- BNA_RXF_FL_RSS_CONFIG_PENDING = 0x04,
- BNA_RXF_FL_OPERSTATE_CHANGED = 0x08,
- BNA_RXF_FL_RXF_ENABLED = 0x10,
- BNA_RXF_FL_VLAN_CONFIG_PENDING = 0x20,
+ BNA_RXF_F_PAUSED = 1,
};
enum bna_rxf_event {
RXF_E_START = 1,
RXF_E_STOP = 2,
RXF_E_FAIL = 3,
- RXF_E_CAM_FLTR_MOD = 4,
- RXF_E_STARTED = 5,
- RXF_E_STOPPED = 6,
- RXF_E_CAM_FLTR_RESP = 7,
- RXF_E_PAUSE = 8,
- RXF_E_RESUME = 9,
- RXF_E_STAT_CLEARED = 10,
-};
-
-enum bna_rxf_state {
- BNA_RXF_STOPPED = 1,
- BNA_RXF_START_WAIT = 2,
- BNA_RXF_CAM_FLTR_MOD_WAIT = 3,
- BNA_RXF_STARTED = 4,
- BNA_RXF_CAM_FLTR_CLR_WAIT = 5,
- BNA_RXF_STOP_WAIT = 6,
- BNA_RXF_PAUSE_WAIT = 7,
- BNA_RXF_RESUME_WAIT = 8,
- BNA_RXF_STAT_CLR_WAIT = 9,
+ RXF_E_CONFIG = 4,
+ RXF_E_PAUSE = 5,
+ RXF_E_RESUME = 6,
+ RXF_E_FW_RESP = 7,
};
-enum bna_port_type {
- BNA_PORT_T_REGULAR = 0,
- BNA_PORT_T_LOOPBACK_INTERNAL = 1,
- BNA_PORT_T_LOOPBACK_EXTERNAL = 2,
+enum bna_enet_type {
+ BNA_ENET_T_REGULAR = 0,
+ BNA_ENET_T_LOOPBACK_INTERNAL = 1,
+ BNA_ENET_T_LOOPBACK_EXTERNAL = 2,
};
enum bna_link_status {
BNA_LINK_DOWN = 0,
BNA_LINK_UP = 1,
- BNA_CEE_UP = 2
+ BNA_CEE_UP = 2
};
-enum bna_llport_flags {
- BNA_LLPORT_F_ADMIN_UP = 1,
- BNA_LLPORT_F_PORT_ENABLED = 2,
- BNA_LLPORT_F_RX_STARTED = 4
+enum bna_ethport_flags {
+ BNA_ETHPORT_F_ADMIN_UP = 1,
+ BNA_ETHPORT_F_PORT_ENABLED = 2,
+ BNA_ETHPORT_F_RX_STARTED = 4,
};
-enum bna_port_flags {
- BNA_PORT_F_DEVICE_READY = 1,
- BNA_PORT_F_ENABLED = 2,
- BNA_PORT_F_PAUSE_CHANGED = 4,
- BNA_PORT_F_MTU_CHANGED = 8
+enum bna_enet_flags {
+ BNA_ENET_F_IOCETH_READY = 1,
+ BNA_ENET_F_ENABLED = 2,
+ BNA_ENET_F_PAUSE_CHANGED = 4,
+ BNA_ENET_F_MTU_CHANGED = 8
+};
+
+enum bna_rss_flags {
+ BNA_RSS_F_RIT_PENDING = 1,
+ BNA_RSS_F_CFG_PENDING = 2,
+ BNA_RSS_F_STATUS_PENDING = 4,
+};
+
+enum bna_mod_flags {
+ BNA_MOD_F_INIT_DONE = 1,
};
enum bna_pkt_rates {
@@ -289,10 +266,17 @@ enum bna_dim_bias_types {
BNA_BIAS_T_MAX = 2
};
+#define BNA_MAX_NAME_SIZE 64
+struct bna_ident {
+ int id;
+ char name[BNA_MAX_NAME_SIZE];
+};
+
struct bna_mac {
/* This should be the first one */
struct list_head qe;
u8 addr[ETH_ALEN];
+ struct bna_mcam_handle *handle;
};
struct bna_mem_descr {
@@ -304,7 +288,7 @@ struct bna_mem_descr {
struct bna_mem_info {
enum bna_mem_type mem_type;
u32 len;
- u32 num;
+ u32 num;
u32 align_sz; /* 0/1 = no alignment */
struct bna_mem_descr *mdl;
void *cookie; /* For bnad to unmap dma later */
@@ -338,23 +322,30 @@ struct bna_qpt {
u32 page_size;
};
+struct bna_attr {
+ bool fw_query_complete;
+ int num_txq;
+ int num_rxp;
+ int num_ucmac;
+ int num_mcmac;
+ int max_rit_size;
+};
+
/**
*
- * Device
+ * IOCEth
*
*/
-struct bna_device {
+struct bna_ioceth {
bfa_fsm_t fsm;
struct bfa_ioc ioc;
- enum bna_intr_type intr_type;
- int vector;
+ struct bna_attr attr;
+ struct bfa_msgq_cmd_entry msgq_cmd;
+ struct bfi_enet_attr_req attr_req;
- void (*ready_cbfn)(struct bnad *bnad, enum bna_cb_status status);
- struct bnad *ready_cbarg;
-
- void (*stop_cbfn)(struct bnad *bnad, enum bna_cb_status status);
+ void (*stop_cbfn)(struct bnad *bnad);
struct bnad *stop_cbarg;
struct bna *bna;
@@ -362,32 +353,7 @@ struct bna_device {
/**
*
- * Mail box
- *
- */
-
-struct bna_mbox_qe {
- /* This should be the first one */
- struct list_head qe;
-
- struct bfa_mbox_cmd cmd;
- u32 cmd_len;
- /* Callback for port, tx, rx, rxf */
- void (*cbfn)(void *arg, int status);
- void *cbarg;
-};
-
-struct bna_mbox_mod {
- enum bna_mbox_state state;
- struct list_head posted_q;
- u32 msg_pending;
- u32 msg_ctr;
- struct bna *bna;
-};
-
-/**
- *
- * Port
+ * Enet
*
*/
@@ -397,50 +363,58 @@ struct bna_pause_config {
enum bna_status rx_pause;
};
-struct bna_llport {
+struct bna_enet {
bfa_fsm_t fsm;
- enum bna_llport_flags flags;
+ enum bna_enet_flags flags;
- enum bna_port_type type;
+ enum bna_enet_type type;
- enum bna_link_status link_status;
+ struct bna_pause_config pause_config;
+ int mtu;
- int rx_started_count;
+ /* Callback for bna_enet_disable(), enet_stop() */
+ void (*stop_cbfn)(void *);
+ void *stop_cbarg;
+
+ /* Callback for bna_enet_pause_config() */
+ void (*pause_cbfn)(struct bnad *);
- void (*stop_cbfn)(struct bna_port *, enum bna_cb_status);
+ /* Callback for bna_enet_mtu_set() */
+ void (*mtu_cbfn)(struct bnad *);
- struct bna_mbox_qe mbox_qe;
+ struct bfa_wc chld_stop_wc;
+
+ struct bfa_msgq_cmd_entry msgq_cmd;
+ struct bfi_enet_set_pause_req pause_req;
struct bna *bna;
};
-struct bna_port {
- bfa_fsm_t fsm;
- enum bna_port_flags flags;
-
- enum bna_port_type type;
+/**
+ *
+ * Ethport
+ *
+ */
- struct bna_llport llport;
+struct bna_ethport {
+ bfa_fsm_t fsm;
+ enum bna_ethport_flags flags;
- struct bna_pause_config pause_config;
- u8 priority;
- int mtu;
+ enum bna_link_status link_status;
- /* Callback for bna_port_disable(), port_stop() */
- void (*stop_cbfn)(void *, enum bna_cb_status);
- void *stop_cbarg;
+ int rx_started_count;
- /* Callback for bna_port_pause_config() */
- void (*pause_cbfn)(struct bnad *, enum bna_cb_status);
+ void (*stop_cbfn)(struct bna_enet *);
- /* Callback for bna_port_mtu_set() */
- void (*mtu_cbfn)(struct bnad *, enum bna_cb_status);
+ void (*adminup_cbfn)(struct bnad *, enum bna_cb_status);
void (*link_cbfn)(struct bnad *, enum bna_link_status);
- struct bfa_wc chld_stop_wc;
-
- struct bna_mbox_qe mbox_qe;
+ struct bfa_msgq_cmd_entry msgq_cmd;
+ union {
+ struct bfi_enet_enable_req admin_req;
+ struct bfi_enet_diag_lb_req lpbk_req;
+ } bfi_enet_cmd;
struct bna *bna;
};
@@ -451,82 +425,26 @@ struct bna_port {
*
*/
-/* IB index segment structure */
-struct bna_ibidx_seg {
- /* This should be the first one */
- struct list_head qe;
-
- u8 ib_seg_size;
- u8 ib_idx_tbl_offset;
-};
-
-/* Interrupt structure */
-struct bna_intr {
- /* This should be the first one */
- struct list_head qe;
- int ref_count;
-
- enum bna_intr_type intr_type;
- int vector;
-
- struct bna_ib *ib;
-};
-
/* Doorbell structure */
struct bna_ib_dbell {
void *__iomem doorbell_addr;
u32 doorbell_ack;
};
-/* Interrupt timer configuration */
-struct bna_ib_config {
- u8 coalescing_timeo; /* Unit is 5usec. */
-
- int interpkt_count;
- int interpkt_timeo;
-
- enum ib_flags ctrl_flags;
-};
-
/* IB structure */
struct bna_ib {
- /* This should be the first one */
- struct list_head qe;
-
- int ib_id;
-
- int ref_count;
- int start_count;
-
struct bna_dma_addr ib_seg_host_addr;
void *ib_seg_host_addr_kva;
- u32 idx_mask; /* Size >= BNA_IBIDX_MAX_SEGSIZE */
-
- struct bna_ibidx_seg *idx_seg;
struct bna_ib_dbell door_bell;
- struct bna_intr *intr;
-
- struct bna_ib_config ib_config;
-
- struct bna *bna;
-};
-
-/* IB module - keeps track of IBs and interrupts */
-struct bna_ib_mod {
- struct bna_ib *ib; /* BFI_MAX_IB entries */
- struct bna_intr *intr; /* BFI_MAX_IB entries */
- struct bna_ibidx_seg *idx_seg; /* BNA_IBIDX_TOTAL_SEGS */
-
- struct list_head ib_free_q;
-
- struct list_head ibidx_seg_pool[BFI_IBIDX_TOTAL_POOLS];
+ enum bna_intr_type intr_type;
+ int intr_vector;
- struct list_head intr_free_q;
- struct list_head intr_active_q;
+ u8 coalescing_timeo; /* Unit is 5usec. */
- struct bna *bna;
+ int interpkt_count;
+ int interpkt_timeo;
};
/**
@@ -552,6 +470,7 @@ struct bna_tcb {
/* Control path */
struct bna_txq *txq;
struct bnad *bnad;
+ void *priv; /* BNAD's cookie */
enum bna_intr_type intr_type;
int intr_vector;
u8 priority; /* Current priority */
@@ -565,68 +484,66 @@ struct bna_txq {
/* This should be the first one */
struct list_head qe;
- int txq_id;
-
u8 priority;
struct bna_qpt qpt;
struct bna_tcb *tcb;
- struct bna_ib *ib;
- int ib_seg_offset;
+ struct bna_ib ib;
struct bna_tx *tx;
- u64 tx_packets;
- u64 tx_bytes;
-};
+ int hw_id;
-/* TxF structure (hardware Tx Function) */
-struct bna_txf {
- int txf_id;
- enum txf_flags ctrl_flags;
- u16 vlan;
+ u64 tx_packets;
+ u64 tx_bytes;
};
/* Tx object */
struct bna_tx {
/* This should be the first one */
struct list_head qe;
+ int rid;
+ int hw_id;
bfa_fsm_t fsm;
enum bna_tx_flags flags;
enum bna_tx_type type;
+ int num_txq;
struct list_head txq_q;
- struct bna_txf txf;
+ u16 txf_vlan_id;
/* Tx event handlers */
void (*tcb_setup_cbfn)(struct bnad *, struct bna_tcb *);
void (*tcb_destroy_cbfn)(struct bnad *, struct bna_tcb *);
- void (*tx_stall_cbfn)(struct bnad *, struct bna_tcb *);
- void (*tx_resume_cbfn)(struct bnad *, struct bna_tcb *);
- void (*tx_cleanup_cbfn)(struct bnad *, struct bna_tcb *);
+ void (*tx_stall_cbfn)(struct bnad *, struct bna_tx *);
+ void (*tx_resume_cbfn)(struct bnad *, struct bna_tx *);
+ void (*tx_cleanup_cbfn)(struct bnad *, struct bna_tx *);
/* callback for bna_tx_disable(), bna_tx_stop() */
- void (*stop_cbfn)(void *arg, struct bna_tx *tx,
- enum bna_cb_status status);
+ void (*stop_cbfn)(void *arg, struct bna_tx *tx);
void *stop_cbarg;
/* callback for bna_tx_prio_set() */
- void (*prio_change_cbfn)(struct bnad *bnad, struct bna_tx *tx,
- enum bna_cb_status status);
+ void (*prio_change_cbfn)(struct bnad *bnad, struct bna_tx *tx);
- struct bfa_wc txq_stop_wc;
-
- struct bna_mbox_qe mbox_qe;
+ struct bfa_msgq_cmd_entry msgq_cmd;
+ union {
+ struct bfi_enet_tx_cfg_req cfg_req;
+ struct bfi_enet_req req;
+ struct bfi_enet_tx_cfg_rsp cfg_rsp;
+ } bfi_enet_cmd;
struct bna *bna;
void *priv; /* bnad's cookie */
};
+/* Tx object configuration used during creation */
struct bna_tx_config {
int num_txq;
int txq_depth;
+ int coalescing_timeo;
enum bna_tx_type tx_type;
};
@@ -635,9 +552,9 @@ struct bna_tx_event_cbfn {
void (*tcb_setup_cbfn)(struct bnad *, struct bna_tcb *);
void (*tcb_destroy_cbfn)(struct bnad *, struct bna_tcb *);
/* Mandatory */
- void (*tx_stall_cbfn)(struct bnad *, struct bna_tcb *);
- void (*tx_resume_cbfn)(struct bnad *, struct bna_tcb *);
- void (*tx_cleanup_cbfn)(struct bnad *, struct bna_tcb *);
+ void (*tx_stall_cbfn)(struct bnad *, struct bna_tx *);
+ void (*tx_resume_cbfn)(struct bnad *, struct bna_tx *);
+ void (*tx_cleanup_cbfn)(struct bnad *, struct bna_tx *);
};
/* Tx module - keeps track of free, active tx objects */
@@ -651,57 +568,25 @@ struct bna_tx_mod {
struct list_head txq_free_q;
/* callback for bna_tx_mod_stop() */
- void (*stop_cbfn)(struct bna_port *port,
- enum bna_cb_status status);
+ void (*stop_cbfn)(struct bna_enet *enet);
struct bfa_wc tx_stop_wc;
enum bna_tx_mod_flags flags;
- int priority;
- int cee_link;
+ u8 prio_map;
+ int default_prio;
+ int iscsi_over_cee;
+ int iscsi_prio;
+ int prio_reconfigured;
- u32 txf_bmap[2];
+ u32 rid_mask;
struct bna *bna;
};
/**
*
- * Receive Indirection Table
- *
- */
-
-/* One row of RIT table */
-struct bna_rit_entry {
- u8 large_rxq_id; /* used for either large or data buffers */
- u8 small_rxq_id; /* used for either small or header buffers */
-};
-
-/* RIT segment */
-struct bna_rit_segment {
- struct list_head qe;
-
- u32 rit_offset;
- u32 rit_size;
- /**
- * max_rit_size: Varies per RIT segment depending on how RIT is
- * partitioned
- */
- u32 max_rit_size;
-
- struct bna_rit_entry *rit;
-};
-
-struct bna_rit_mod {
- struct bna_rit_entry *rit;
- struct bna_rit_segment *rit_segment;
-
- struct list_head rit_seg_pool[BFI_RIT_SEG_TOTAL_POOLS];
-};
-
-/**
- *
* Rx object
*
*/
@@ -719,8 +604,9 @@ struct bna_rcb {
int page_count;
/* Control path */
struct bna_rxq *rxq;
- struct bna_cq *cq;
+ struct bna_ccb *ccb;
struct bnad *bnad;
+ void *priv; /* BNAD's cookie */
unsigned long flags;
int id;
};
@@ -728,7 +614,6 @@ struct bna_rcb {
/* RxQ structure - QPT, configuration */
struct bna_rxq {
struct list_head qe;
- int rxq_id;
int buffer_size;
int q_depth;
@@ -739,10 +624,12 @@ struct bna_rxq {
struct bna_rxp *rxp;
struct bna_rx *rx;
- u64 rx_packets;
+ int hw_id;
+
+ u64 rx_packets;
u64 rx_bytes;
- u64 rx_packets_with_error;
- u64 rxbuf_alloc_failed;
+ u64 rx_packets_with_error;
+ u64 rxbuf_alloc_failed;
};
/* RxQ pair */
@@ -784,6 +671,7 @@ struct bna_ccb {
/* Control path */
struct bna_cq *cq;
struct bnad *bnad;
+ void *priv; /* BNAD's cookie */
enum bna_intr_type intr_type;
int intr_vector;
u8 rx_coalescing_timeo; /* For NAPI */
@@ -793,46 +681,43 @@ struct bna_ccb {
/* CQ QPT, configuration */
struct bna_cq {
- int cq_id;
-
struct bna_qpt qpt;
struct bna_ccb *ccb;
- struct bna_ib *ib;
- u8 ib_seg_offset;
+ struct bna_ib ib;
struct bna_rx *rx;
};
struct bna_rss_config {
- enum rss_hash_type hash_type;
+ enum bfi_enet_rss_type hash_type;
u8 hash_mask;
- u32 toeplitz_hash_key[BFI_RSS_HASH_KEY_LEN];
+ u32 toeplitz_hash_key[BFI_ENET_RSS_KEY_LEN];
};
struct bna_hds_config {
- enum hds_header_type hdr_type;
- int header_size;
+ enum bfi_enet_hds_type hdr_type;
+ int forced_offset;
};
-/* This structure is used during RX creation */
+/* Rx object configuration used during creation */
struct bna_rx_config {
enum bna_rx_type rx_type;
int num_paths;
enum bna_rxp_type rxp_type;
int paused;
int q_depth;
+ int coalescing_timeo;
/*
* Small/Large (or Header/Data) buffer size to be configured
* for SLR and HDS queue type. Large buffer size comes from
- * port->mtu.
+ * enet->mtu.
*/
int small_buff_size;
enum bna_status rss_status;
struct bna_rss_config rss_config;
- enum bna_status hds_status;
struct bna_hds_config hds_config;
enum bna_status vlan_strip_status;
@@ -851,79 +736,63 @@ struct bna_rxp {
/* MSI-x vector number for configuring RSS */
int vector;
-
- struct bna_mbox_qe mbox_qe;
-};
-
-/* HDS configuration structure */
-struct bna_rxf_hds {
- enum hds_header_type hdr_type;
- int header_size;
-};
-
-/* RSS configuration structure */
-struct bna_rxf_rss {
- enum rss_hash_type hash_type;
- u8 hash_mask;
- u32 toeplitz_hash_key[BFI_RSS_HASH_KEY_LEN];
+ int hw_id;
};
/* RxF structure (hardware Rx Function) */
struct bna_rxf {
bfa_fsm_t fsm;
- int rxf_id;
- enum rxf_flags ctrl_flags;
- u16 default_vlan_tag;
- enum bna_rxf_oper_state rxf_oper_state;
- enum bna_status hds_status;
- struct bna_rxf_hds hds_cfg;
- enum bna_status rss_status;
- struct bna_rxf_rss rss_cfg;
- struct bna_rit_segment *rit_segment;
- struct bna_rx *rx;
- u32 forced_offset;
- struct bna_mbox_qe mbox_qe;
- int mcast_rxq_id;
+ enum bna_rxf_flags flags;
+
+ struct bfa_msgq_cmd_entry msgq_cmd;
+ union {
+ struct bfi_enet_enable_req req;
+ struct bfi_enet_rss_cfg_req rss_req;
+ struct bfi_enet_rit_req rit_req;
+ struct bfi_enet_rx_vlan_req vlan_req;
+ struct bfi_enet_mcast_add_req mcast_add_req;
+ struct bfi_enet_mcast_del_req mcast_del_req;
+ struct bfi_enet_ucast_req ucast_req;
+ } bfi_enet_cmd;
/* callback for bna_rxf_start() */
- void (*start_cbfn) (struct bna_rx *rx, enum bna_cb_status status);
+ void (*start_cbfn) (struct bna_rx *rx);
struct bna_rx *start_cbarg;
/* callback for bna_rxf_stop() */
- void (*stop_cbfn) (struct bna_rx *rx, enum bna_cb_status status);
+ void (*stop_cbfn) (struct bna_rx *rx);
struct bna_rx *stop_cbarg;
- /* callback for bna_rxf_receive_enable() / bna_rxf_receive_disable() */
- void (*oper_state_cbfn) (struct bnad *bnad, struct bna_rx *rx,
- enum bna_cb_status status);
+ /* callback for bna_rx_receive_pause() / bna_rx_receive_resume() */
+ void (*oper_state_cbfn) (struct bnad *bnad, struct bna_rx *rx);
struct bnad *oper_state_cbarg;
/**
* callback for:
* bna_rxf_ucast_set()
* bna_rxf_{ucast/mcast}_add(),
- * bna_rxf_{ucast/mcast}_del(),
+ * bna_rxf_{ucast/mcast}_del(),
* bna_rxf_mode_set()
*/
- void (*cam_fltr_cbfn)(struct bnad *bnad, struct bna_rx *rx,
- enum bna_cb_status status);
+ void (*cam_fltr_cbfn)(struct bnad *bnad, struct bna_rx *rx);
struct bnad *cam_fltr_cbarg;
- enum bna_rxf_flags rxf_flags;
-
/* List of unicast addresses yet to be applied to h/w */
struct list_head ucast_pending_add_q;
struct list_head ucast_pending_del_q;
+ struct bna_mac *ucast_pending_mac;
int ucast_pending_set;
/* ucast addresses applied to the h/w */
struct list_head ucast_active_q;
- struct bna_mac *ucast_active_mac;
+ struct bna_mac ucast_active_mac;
+ int ucast_active_set;
/* List of multicast addresses yet to be applied to h/w */
struct list_head mcast_pending_add_q;
struct list_head mcast_pending_del_q;
/* multicast addresses applied to the h/w */
struct list_head mcast_active_q;
+ struct list_head mcast_handle_q;
/* Rx modes yet to be applied to h/w */
enum bna_rxmode rxmode_pending;
@@ -931,41 +800,59 @@ struct bna_rxf {
/* Rx modes applied to h/w */
enum bna_rxmode rxmode_active;
+ u8 vlan_pending_bitmask;
enum bna_status vlan_filter_status;
- u32 vlan_filter_table[(BFI_MAX_VLAN + 1) / 32];
+ u32 vlan_filter_table[(BFI_ENET_VLAN_ID_MAX) / 32];
+ bool vlan_strip_pending;
+ enum bna_status vlan_strip_status;
+
+ enum bna_rss_flags rss_pending;
+ enum bna_status rss_status;
+ struct bna_rss_config rss_cfg;
+ u8 *rit;
+ int rit_size;
+
+ struct bna_rx *rx;
};
/* Rx object */
struct bna_rx {
/* This should be the first one */
struct list_head qe;
+ int rid;
+ int hw_id;
bfa_fsm_t fsm;
enum bna_rx_type type;
- /* list-head for RX path objects */
+ int num_paths;
struct list_head rxp_q;
+ struct bna_hds_config hds_cfg;
+
struct bna_rxf rxf;
enum bna_rx_flags rx_flags;
- struct bna_mbox_qe mbox_qe;
-
- struct bfa_wc rxq_stop_wc;
+ struct bfa_msgq_cmd_entry msgq_cmd;
+ union {
+ struct bfi_enet_rx_cfg_req cfg_req;
+ struct bfi_enet_req req;
+ struct bfi_enet_rx_cfg_rsp cfg_rsp;
+ } bfi_enet_cmd;
/* Rx event handlers */
void (*rcb_setup_cbfn)(struct bnad *, struct bna_rcb *);
void (*rcb_destroy_cbfn)(struct bnad *, struct bna_rcb *);
void (*ccb_setup_cbfn)(struct bnad *, struct bna_ccb *);
void (*ccb_destroy_cbfn)(struct bnad *, struct bna_ccb *);
- void (*rx_cleanup_cbfn)(struct bnad *, struct bna_ccb *);
- void (*rx_post_cbfn)(struct bnad *, struct bna_rcb *);
+ void (*rx_stall_cbfn)(struct bnad *, struct bna_rx *);
+ void (*rx_cleanup_cbfn)(struct bnad *, struct bna_rx *);
+ void (*rx_post_cbfn)(struct bnad *, struct bna_rx *);
/* callback for bna_rx_disable(), bna_rx_stop() */
- void (*stop_cbfn)(void *arg, struct bna_rx *rx,
- enum bna_cb_status status);
+ void (*stop_cbfn)(void *arg, struct bna_rx *rx);
void *stop_cbarg;
struct bna *bna;
@@ -978,9 +865,10 @@ struct bna_rx_event_cbfn {
void (*rcb_destroy_cbfn)(struct bnad *, struct bna_rcb *);
void (*ccb_setup_cbfn)(struct bnad *, struct bna_ccb *);
void (*ccb_destroy_cbfn)(struct bnad *, struct bna_ccb *);
+ void (*rx_stall_cbfn)(struct bnad *, struct bna_rx *);
/* Mandatory */
- void (*rx_cleanup_cbfn)(struct bnad *, struct bna_ccb *);
- void (*rx_post_cbfn)(struct bnad *, struct bna_rcb *);
+ void (*rx_cleanup_cbfn)(struct bnad *, struct bna_rx *);
+ void (*rx_post_cbfn)(struct bnad *, struct bna_rx *);
};
/* Rx module - keeps track of free, active rx objects */
@@ -1003,12 +891,11 @@ struct bna_rx_mod {
enum bna_rx_mod_flags flags;
/* callback for bna_rx_mod_stop() */
- void (*stop_cbfn)(struct bna_port *port,
- enum bna_cb_status status);
+ void (*stop_cbfn)(struct bna_enet *enet);
struct bfa_wc rx_stop_wc;
u32 dim_vector[BNA_LOAD_T_MAX][BNA_BIAS_T_MAX];
- u32 rxf_bmap[2];
+ u32 rid_mask;
};
/**
@@ -1024,9 +911,18 @@ struct bna_ucam_mod {
struct bna *bna;
};
+struct bna_mcam_handle {
+ /* This should be the first one */
+ struct list_head qe;
+ int handle;
+ int refcnt;
+};
+
struct bna_mcam_mod {
struct bna_mac *mcmac; /* BFI_MAX_MCMAC entries */
+ struct bna_mcam_handle *mchandle; /* BFI_MAX_MCMAC entries */
struct list_head free_q;
+ struct list_head free_handle_q;
struct bna *bna;
};
@@ -1037,50 +933,20 @@ struct bna_mcam_mod {
*
*/
-struct bna_tx_stats {
- int tx_state;
- int tx_flags;
- int num_txqs;
- u32 txq_bmap[2];
- int txf_id;
-};
-
-struct bna_rx_stats {
- int rx_state;
- int rx_flags;
- int num_rxps;
- int num_rxqs;
- u32 rxq_bmap[2];
- u32 cq_bmap[2];
- int rxf_id;
- int rxf_state;
- int rxf_oper_state;
- int num_active_ucast;
- int num_active_mcast;
- int rxmode_active;
- int vlan_filter_status;
- u32 vlan_filter_table[(BFI_MAX_VLAN + 1) / 32];
- int rss_status;
- int hds_status;
-};
-
-struct bna_sw_stats {
- int device_state;
- int port_state;
- int port_flags;
- int llport_state;
- int priority;
- int num_active_tx;
- int num_active_rx;
- struct bna_tx_stats tx_stats[BFI_MAX_TXQ];
- struct bna_rx_stats rx_stats[BFI_MAX_RXQ];
+struct bna_stats {
+ struct bna_dma_addr hw_stats_dma;
+ struct bfi_enet_stats *hw_stats_kva;
+ struct bfi_enet_stats hw_stats;
};
-struct bna_stats {
- u32 txf_bmap[2];
- u32 rxf_bmap[2];
- struct bfi_ll_stats *hw_stats;
- struct bna_sw_stats *sw_stats;
+struct bna_stats_mod {
+ bool ioc_ready;
+ bool stats_get_busy;
+ bool stats_clr_busy;
+ struct bfa_msgq_cmd_entry stats_get_cmd;
+ struct bfa_msgq_cmd_entry stats_clr_cmd;
+ struct bfi_enet_stats_req stats_get;
+ struct bfi_enet_stats_req stats_clr;
};
/**
@@ -1090,38 +956,32 @@ struct bna_stats {
*/
struct bna {
+ struct bna_ident ident;
struct bfa_pcidev pcidev;
- int port_num;
-
- struct bna_chip_regs regs;
+ struct bna_reg regs;
+ struct bna_bit_defn bits;
- struct bna_dma_addr hw_stats_dma;
struct bna_stats stats;
- struct bna_device device;
+ struct bna_ioceth ioceth;
struct bfa_cee cee;
+ struct bfa_msgq msgq;
- struct bna_mbox_mod mbox_mod;
-
- struct bna_port port;
+ struct bna_ethport ethport;
+ struct bna_enet enet;
+ struct bna_stats_mod stats_mod;
struct bna_tx_mod tx_mod;
-
struct bna_rx_mod rx_mod;
-
- struct bna_ib_mod ib_mod;
-
struct bna_ucam_mod ucam_mod;
struct bna_mcam_mod mcam_mod;
- struct bna_rit_mod rit_mod;
+ enum bna_mod_flags mod_flags;
- int rxf_promisc_id;
-
- struct bna_mbox_qe mbox_qe;
+ int default_mode_rid;
+ int promisc_rid;
struct bnad *bnad;
};
-
#endif /* __BNA_TYPES_H__ */
diff --git a/drivers/net/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index 44e219c910da..7f3091e7eb42 100644
--- a/drivers/net/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -15,6 +15,7 @@
* All rights reserved
* www.brocade.com
*/
+#include <linux/bitops.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
@@ -24,6 +25,7 @@
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/prefetch.h>
+#include <linux/module.h>
#include "bnad.h"
#include "bna.h"
@@ -58,7 +60,7 @@ static const u8 bnad_bcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
#define BNAD_GET_MBOX_IRQ(_bnad) \
(((_bnad)->cfg_flags & BNAD_CF_MSIX) ? \
- ((_bnad)->msix_table[(_bnad)->msix_num - 1].vector) : \
+ ((_bnad)->msix_table[BNAD_MAILBOX_MSIX_INDEX].vector) : \
((_bnad)->pcidev->irq))
#define BNAD_FILL_UNMAPQ_MEM_REQ(_res_info, _num, _depth) \
@@ -101,6 +103,28 @@ bnad_cq_cmpl_init(struct bnad *bnad, struct bna_ccb *ccb)
}
}
+static u32
+bnad_pci_unmap_skb(struct device *pdev, struct bnad_skb_unmap *array,
+ u32 index, u32 depth, struct sk_buff *skb, u32 frag)
+{
+ int j;
+ array[index].skb = NULL;
+
+ dma_unmap_single(pdev, dma_unmap_addr(&array[index], dma_addr),
+ skb_headlen(skb), DMA_TO_DEVICE);
+ dma_unmap_addr_set(&array[index], dma_addr, 0);
+ BNA_QE_INDX_ADD(index, 1, depth);
+
+ for (j = 0; j < frag; j++) {
+ dma_unmap_page(pdev, dma_unmap_addr(&array[index], dma_addr),
+ skb_frag_size(&skb_shinfo(skb)->frags[j]), DMA_TO_DEVICE);
+ dma_unmap_addr_set(&array[index], dma_addr, 0);
+ BNA_QE_INDX_ADD(index, 1, depth);
+ }
+
+ return index;
+}
+
/*
* Frees all pending Tx Bufs
* At this point no activity is expected on the Q,
@@ -110,43 +134,24 @@ static void
bnad_free_all_txbufs(struct bnad *bnad,
struct bna_tcb *tcb)
{
- u32 unmap_cons;
+ u32 unmap_cons;
struct bnad_unmap_q *unmap_q = tcb->unmap_q;
struct bnad_skb_unmap *unmap_array;
- struct sk_buff *skb = NULL;
- int i;
+ struct sk_buff *skb = NULL;
+ int q;
unmap_array = unmap_q->unmap_array;
- unmap_cons = 0;
- while (unmap_cons < unmap_q->q_depth) {
- skb = unmap_array[unmap_cons].skb;
- if (!skb) {
- unmap_cons++;
+ for (q = 0; q < unmap_q->q_depth; q++) {
+ skb = unmap_array[q].skb;
+ if (!skb)
continue;
- }
- unmap_array[unmap_cons].skb = NULL;
- dma_unmap_single(&bnad->pcidev->dev,
- dma_unmap_addr(&unmap_array[unmap_cons],
- dma_addr), skb_headlen(skb),
- DMA_TO_DEVICE);
-
- dma_unmap_addr_set(&unmap_array[unmap_cons], dma_addr, 0);
- if (++unmap_cons >= unmap_q->q_depth)
- break;
+ unmap_cons = q;
+ unmap_cons = bnad_pci_unmap_skb(&bnad->pcidev->dev, unmap_array,
+ unmap_cons, unmap_q->q_depth, skb,
+ skb_shinfo(skb)->nr_frags);
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- dma_unmap_page(&bnad->pcidev->dev,
- dma_unmap_addr(&unmap_array[unmap_cons],
- dma_addr),
- skb_shinfo(skb)->frags[i].size,
- DMA_TO_DEVICE);
- dma_unmap_addr_set(&unmap_array[unmap_cons], dma_addr,
- 0);
- if (++unmap_cons >= unmap_q->q_depth)
- break;
- }
dev_kfree_skb_any(skb);
}
}
@@ -163,12 +168,11 @@ static u32
bnad_free_txbufs(struct bnad *bnad,
struct bna_tcb *tcb)
{
- u32 sent_packets = 0, sent_bytes = 0;
- u16 wis, unmap_cons, updated_hw_cons;
+ u32 unmap_cons, sent_packets = 0, sent_bytes = 0;
+ u16 wis, updated_hw_cons;
struct bnad_unmap_q *unmap_q = tcb->unmap_q;
struct bnad_skb_unmap *unmap_array;
- struct sk_buff *skb;
- int i;
+ struct sk_buff *skb;
/*
* Just return if TX is stopped. This check is useful
@@ -194,32 +198,14 @@ bnad_free_txbufs(struct bnad *bnad,
while (wis) {
skb = unmap_array[unmap_cons].skb;
- unmap_array[unmap_cons].skb = NULL;
-
sent_packets++;
sent_bytes += skb->len;
wis -= BNA_TXQ_WI_NEEDED(1 + skb_shinfo(skb)->nr_frags);
- dma_unmap_single(&bnad->pcidev->dev,
- dma_unmap_addr(&unmap_array[unmap_cons],
- dma_addr), skb_headlen(skb),
- DMA_TO_DEVICE);
- dma_unmap_addr_set(&unmap_array[unmap_cons], dma_addr, 0);
- BNA_QE_INDX_ADD(unmap_cons, 1, unmap_q->q_depth);
-
- prefetch(&unmap_array[unmap_cons + 1]);
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- prefetch(&unmap_array[unmap_cons + 1]);
-
- dma_unmap_page(&bnad->pcidev->dev,
- dma_unmap_addr(&unmap_array[unmap_cons],
- dma_addr),
- skb_shinfo(skb)->frags[i].size,
- DMA_TO_DEVICE);
- dma_unmap_addr_set(&unmap_array[unmap_cons], dma_addr,
- 0);
- BNA_QE_INDX_ADD(unmap_cons, 1, unmap_q->q_depth);
- }
+ unmap_cons = bnad_pci_unmap_skb(&bnad->pcidev->dev, unmap_array,
+ unmap_cons, unmap_q->q_depth, skb,
+ skb_shinfo(skb)->nr_frags);
+
dev_kfree_skb_any(skb);
}
@@ -245,7 +231,7 @@ bnad_tx_free_tasklet(unsigned long bnad_ptr)
{
struct bnad *bnad = (struct bnad *)bnad_ptr;
struct bna_tcb *tcb;
- u32 acked = 0;
+ u32 acked = 0;
int i, j;
for (i = 0; i < bnad->num_tx; i++) {
@@ -382,18 +368,16 @@ bnad_alloc_n_post_rxbufs(struct bnad *bnad, struct bna_rcb *rcb)
BNA_RXQ_QPGE_PTR_GET(unmap_prod, rcb->sw_qpt, rxent, wi_range);
while (to_alloc--) {
- if (!wi_range) {
+ if (!wi_range)
BNA_RXQ_QPGE_PTR_GET(unmap_prod, rcb->sw_qpt, rxent,
wi_range);
- }
- skb = alloc_skb(rcb->rxq->buffer_size + NET_IP_ALIGN,
- GFP_ATOMIC);
+ skb = netdev_alloc_skb_ip_align(bnad->netdev,
+ rcb->rxq->buffer_size);
if (unlikely(!skb)) {
BNAD_UPDATE_CTR(bnad, rxbuf_alloc_failed);
+ rcb->rxq->rxbuf_alloc_failed++;
goto finishing;
}
- skb->dev = bnad->netdev;
- skb_reserve(skb, NET_IP_ALIGN);
unmap_array[unmap_prod].skb = skb;
dma_addr = dma_map_single(&bnad->pcidev->dev, skb->data,
rcb->rxq->buffer_size,
@@ -413,7 +397,7 @@ finishing:
unmap_q->producer_index = unmap_prod;
rcb->producer_index = unmap_prod;
smp_mb();
- if (likely(test_bit(BNAD_RXQ_STARTED, &rcb->flags)))
+ if (likely(test_bit(BNAD_RXQ_POST_OK, &rcb->flags)))
bna_rxq_prod_indx_doorbell(rcb);
}
}
@@ -442,11 +426,15 @@ bnad_poll_cq(struct bnad *bnad, struct bna_ccb *ccb, int budget)
struct bnad_skb_unmap *unmap_array;
struct sk_buff *skb;
u32 flags, unmap_cons;
- u32 qid0 = ccb->rcb[0]->rxq->rxq_id;
struct bna_pkt_rate *pkt_rt = &ccb->pkt_rate;
+ struct bnad_rx_ctrl *rx_ctrl = (struct bnad_rx_ctrl *)(ccb->ctrl);
+
+ set_bit(BNAD_FP_IN_RX_PATH, &rx_ctrl->flags);
- if (!test_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags))
+ if (!test_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags)) {
+ clear_bit(BNAD_FP_IN_RX_PATH, &rx_ctrl->flags);
return 0;
+ }
prefetch(bnad->netdev);
BNA_CQ_QPGE_PTR_GET(ccb->producer_index, ccb->sw_qpt, cmpl,
@@ -456,10 +444,10 @@ bnad_poll_cq(struct bnad *bnad, struct bna_ccb *ccb, int budget)
packets++;
BNA_UPDATE_PKT_CNT(pkt_rt, ntohs(cmpl->length));
- if (qid0 == cmpl->rxq_id)
- rcb = ccb->rcb[0];
- else
+ if (bna_is_small_rxq(cmpl->rxq_id))
rcb = ccb->rcb[1];
+ else
+ rcb = ccb->rcb[0];
unmap_q = rcb->unmap_q;
unmap_array = unmap_q->unmap_array;
@@ -516,24 +504,13 @@ bnad_poll_cq(struct bnad *bnad, struct bna_ccb *ccb, int budget)
rcb->rxq->rx_bytes += skb->len;
skb->protocol = eth_type_trans(skb, bnad->netdev);
- if (bnad->vlan_grp && (flags & BNA_CQ_EF_VLAN)) {
- struct bnad_rx_ctrl *rx_ctrl =
- (struct bnad_rx_ctrl *)ccb->ctrl;
- if (skb->ip_summed == CHECKSUM_UNNECESSARY)
- vlan_gro_receive(&rx_ctrl->napi, bnad->vlan_grp,
- ntohs(cmpl->vlan_tag), skb);
- else
- vlan_hwaccel_receive_skb(skb,
- bnad->vlan_grp,
- ntohs(cmpl->vlan_tag));
-
- } else { /* Not VLAN tagged/stripped */
- struct bnad_rx_ctrl *rx_ctrl =
- (struct bnad_rx_ctrl *)ccb->ctrl;
- if (skb->ip_summed == CHECKSUM_UNNECESSARY)
- napi_gro_receive(&rx_ctrl->napi, skb);
- else
- netif_receive_skb(skb);
+ if (flags & BNA_CQ_EF_VLAN)
+ __vlan_hwaccel_put_tag(skb, ntohs(cmpl->vlan_tag));
+
+ if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+ napi_gro_receive(&rx_ctrl->napi, skb);
+ else {
+ netif_receive_skb(skb);
}
next:
@@ -543,39 +520,16 @@ next:
BNA_QE_INDX_ADD(ccb->producer_index, wis, ccb->q_depth);
- if (likely(ccb)) {
- if (likely(test_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags)))
- bna_ib_ack(ccb->i_dbell, packets);
- bnad_refill_rxq(bnad, ccb->rcb[0]);
- if (ccb->rcb[1])
- bnad_refill_rxq(bnad, ccb->rcb[1]);
- } else {
- if (likely(test_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags)))
- bna_ib_ack(ccb->i_dbell, 0);
- }
-
- return packets;
-}
-
-static void
-bnad_disable_rx_irq(struct bnad *bnad, struct bna_ccb *ccb)
-{
- if (unlikely(!test_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags)))
- return;
+ if (likely(test_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags)))
+ bna_ib_ack_disable_irq(ccb->i_dbell, packets);
- bna_ib_coalescing_timer_set(ccb->i_dbell, 0);
- bna_ib_ack(ccb->i_dbell, 0);
-}
+ bnad_refill_rxq(bnad, ccb->rcb[0]);
+ if (ccb->rcb[1])
+ bnad_refill_rxq(bnad, ccb->rcb[1]);
-static void
-bnad_enable_rx_irq(struct bnad *bnad, struct bna_ccb *ccb)
-{
- unsigned long flags;
+ clear_bit(BNAD_FP_IN_RX_PATH, &rx_ctrl->flags);
- /* Because of polling context */
- spin_lock_irqsave(&bnad->bna_lock, flags);
- bnad_enable_rx_irq_unsafe(ccb);
- spin_unlock_irqrestore(&bnad->bna_lock, flags);
+ return packets;
}
static void
@@ -585,10 +539,9 @@ bnad_netif_rx_schedule_poll(struct bnad *bnad, struct bna_ccb *ccb)
struct napi_struct *napi = &rx_ctrl->napi;
if (likely(napi_schedule_prep(napi))) {
- bnad_disable_rx_irq(bnad, ccb);
__napi_schedule(napi);
+ rx_ctrl->rx_schedule++;
}
- BNAD_UPDATE_CTR(bnad, netif_rx_schedule);
}
/* MSIX Rx Path Handler */
@@ -596,9 +549,11 @@ static irqreturn_t
bnad_msix_rx(int irq, void *data)
{
struct bna_ccb *ccb = (struct bna_ccb *)data;
- struct bnad *bnad = ccb->bnad;
- bnad_netif_rx_schedule_poll(bnad, ccb);
+ if (ccb) {
+ ((struct bnad_rx_ctrl *)(ccb->ctrl))->rx_intr_ctr++;
+ bnad_netif_rx_schedule_poll(ccb->bnad, ccb);
+ }
return IRQ_HANDLED;
}
@@ -613,14 +568,15 @@ bnad_msix_mbox_handler(int irq, void *data)
unsigned long flags;
struct bnad *bnad = (struct bnad *)data;
- if (unlikely(test_bit(BNAD_RF_MBOX_IRQ_DISABLED, &bnad->run_flags)))
- return IRQ_HANDLED;
-
spin_lock_irqsave(&bnad->bna_lock, flags);
+ if (unlikely(test_bit(BNAD_RF_MBOX_IRQ_DISABLED, &bnad->run_flags))) {
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
+ return IRQ_HANDLED;
+ }
bna_intr_status_get(&bnad->bna, intr_status);
- if (BNA_IS_MBOX_ERR_INTR(intr_status))
+ if (BNA_IS_MBOX_ERR_INTR(&bnad->bna, intr_status))
bna_mbox_handler(&bnad->bna, intr_status);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
@@ -637,18 +593,22 @@ bnad_isr(int irq, void *data)
struct bnad *bnad = (struct bnad *)data;
struct bnad_rx_info *rx_info;
struct bnad_rx_ctrl *rx_ctrl;
+ struct bna_tcb *tcb = NULL;
- if (unlikely(test_bit(BNAD_RF_MBOX_IRQ_DISABLED, &bnad->run_flags)))
+ spin_lock_irqsave(&bnad->bna_lock, flags);
+ if (unlikely(test_bit(BNAD_RF_MBOX_IRQ_DISABLED, &bnad->run_flags))) {
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
return IRQ_NONE;
+ }
bna_intr_status_get(&bnad->bna, intr_status);
- if (unlikely(!intr_status))
+ if (unlikely(!intr_status)) {
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
return IRQ_NONE;
+ }
- spin_lock_irqsave(&bnad->bna_lock, flags);
-
- if (BNA_IS_MBOX_ERR_INTR(intr_status))
+ if (BNA_IS_MBOX_ERR_INTR(&bnad->bna, intr_status))
bna_mbox_handler(&bnad->bna, intr_status);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
@@ -659,8 +619,11 @@ bnad_isr(int irq, void *data)
/* Process data interrupts */
/* Tx processing */
for (i = 0; i < bnad->num_tx; i++) {
- for (j = 0; j < bnad->num_txq_per_tx; j++)
- bnad_tx(bnad, bnad->tx_info[i].tcb[j]);
+ for (j = 0; j < bnad->num_txq_per_tx; j++) {
+ tcb = bnad->tx_info[i].tcb[j];
+ if (tcb && test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags))
+ bnad_tx(bnad, bnad->tx_info[i].tcb[j]);
+ }
}
/* Rx processing */
for (i = 0; i < bnad->num_rx; i++) {
@@ -715,43 +678,49 @@ bnad_set_netdev_perm_addr(struct bnad *bnad)
/* Callbacks */
void
-bnad_cb_device_enable_mbox_intr(struct bnad *bnad)
+bnad_cb_mbox_intr_enable(struct bnad *bnad)
{
bnad_enable_mbox_irq(bnad);
}
void
-bnad_cb_device_disable_mbox_intr(struct bnad *bnad)
+bnad_cb_mbox_intr_disable(struct bnad *bnad)
{
bnad_disable_mbox_irq(bnad);
}
void
-bnad_cb_device_enabled(struct bnad *bnad, enum bna_cb_status status)
+bnad_cb_ioceth_ready(struct bnad *bnad)
{
+ bnad->bnad_completions.ioc_comp_status = BNA_CB_SUCCESS;
complete(&bnad->bnad_completions.ioc_comp);
- bnad->bnad_completions.ioc_comp_status = status;
}
void
-bnad_cb_device_disabled(struct bnad *bnad, enum bna_cb_status status)
+bnad_cb_ioceth_failed(struct bnad *bnad)
{
+ bnad->bnad_completions.ioc_comp_status = BNA_CB_FAIL;
+ complete(&bnad->bnad_completions.ioc_comp);
+}
+
+void
+bnad_cb_ioceth_disabled(struct bnad *bnad)
+{
+ bnad->bnad_completions.ioc_comp_status = BNA_CB_SUCCESS;
complete(&bnad->bnad_completions.ioc_comp);
- bnad->bnad_completions.ioc_comp_status = status;
}
static void
-bnad_cb_port_disabled(void *arg, enum bna_cb_status status)
+bnad_cb_enet_disabled(void *arg)
{
struct bnad *bnad = (struct bnad *)arg;
- complete(&bnad->bnad_completions.port_comp);
-
netif_carrier_off(bnad->netdev);
+ complete(&bnad->bnad_completions.enet_comp);
}
void
-bnad_cb_port_link_status(struct bnad *bnad,
+bnad_cb_ethport_link_status(struct bnad *bnad,
enum bna_link_status link_status)
{
bool link_up = 0;
@@ -759,34 +728,60 @@ bnad_cb_port_link_status(struct bnad *bnad,
link_up = (link_status == BNA_LINK_UP) || (link_status == BNA_CEE_UP);
if (link_status == BNA_CEE_UP) {
+ if (!test_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags))
+ BNAD_UPDATE_CTR(bnad, cee_toggle);
set_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags);
- BNAD_UPDATE_CTR(bnad, cee_up);
- } else
+ } else {
+ if (test_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags))
+ BNAD_UPDATE_CTR(bnad, cee_toggle);
clear_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags);
+ }
if (link_up) {
if (!netif_carrier_ok(bnad->netdev)) {
- struct bna_tcb *tcb = bnad->tx_info[0].tcb[0];
- if (!tcb)
- return;
- pr_warn("bna: %s link up\n",
+ uint tx_id, tcb_id;
+ printk(KERN_WARNING "bna: %s link up\n",
bnad->netdev->name);
netif_carrier_on(bnad->netdev);
BNAD_UPDATE_CTR(bnad, link_toggle);
- if (test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags)) {
- /* Force an immediate Transmit Schedule */
- pr_info("bna: %s TX_STARTED\n",
- bnad->netdev->name);
- netif_wake_queue(bnad->netdev);
- BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
- } else {
- netif_stop_queue(bnad->netdev);
- BNAD_UPDATE_CTR(bnad, netif_queue_stop);
+ for (tx_id = 0; tx_id < bnad->num_tx; tx_id++) {
+ for (tcb_id = 0; tcb_id < bnad->num_txq_per_tx;
+ tcb_id++) {
+ struct bna_tcb *tcb =
+ bnad->tx_info[tx_id].tcb[tcb_id];
+ u32 txq_id;
+ if (!tcb)
+ continue;
+
+ txq_id = tcb->id;
+
+ if (test_bit(BNAD_TXQ_TX_STARTED,
+ &tcb->flags)) {
+ /*
+ * Force an immediate
+ * Transmit Schedule */
+ printk(KERN_INFO "bna: %s %d "
+ "TXQ_STARTED\n",
+ bnad->netdev->name,
+ txq_id);
+ netif_wake_subqueue(
+ bnad->netdev,
+ txq_id);
+ BNAD_UPDATE_CTR(bnad,
+ netif_queue_wakeup);
+ } else {
+ netif_stop_subqueue(
+ bnad->netdev,
+ txq_id);
+ BNAD_UPDATE_CTR(bnad,
+ netif_queue_stop);
+ }
+ }
}
}
} else {
if (netif_carrier_ok(bnad->netdev)) {
- pr_warn("bna: %s link down\n",
+ printk(KERN_WARNING "bna: %s link down\n",
bnad->netdev->name);
netif_carrier_off(bnad->netdev);
BNAD_UPDATE_CTR(bnad, link_toggle);
@@ -795,8 +790,7 @@ bnad_cb_port_link_status(struct bnad *bnad,
}
static void
-bnad_cb_tx_disabled(void *arg, struct bna_tx *tx,
- enum bna_cb_status status)
+bnad_cb_tx_disabled(void *arg, struct bna_tx *tx)
{
struct bnad *bnad = (struct bnad *)arg;
@@ -873,108 +867,188 @@ bnad_cb_ccb_destroy(struct bnad *bnad, struct bna_ccb *ccb)
}
static void
-bnad_cb_tx_stall(struct bnad *bnad, struct bna_tcb *tcb)
+bnad_cb_tx_stall(struct bnad *bnad, struct bna_tx *tx)
{
struct bnad_tx_info *tx_info =
- (struct bnad_tx_info *)tcb->txq->tx->priv;
-
- if (tx_info != &bnad->tx_info[0])
- return;
+ (struct bnad_tx_info *)tx->priv;
+ struct bna_tcb *tcb;
+ u32 txq_id;
+ int i;
- clear_bit(BNAD_TXQ_TX_STARTED, &tcb->flags);
- netif_stop_queue(bnad->netdev);
- pr_info("bna: %s TX_STOPPED\n", bnad->netdev->name);
+ for (i = 0; i < BNAD_MAX_TXQ_PER_TX; i++) {
+ tcb = tx_info->tcb[i];
+ if (!tcb)
+ continue;
+ txq_id = tcb->id;
+ clear_bit(BNAD_TXQ_TX_STARTED, &tcb->flags);
+ netif_stop_subqueue(bnad->netdev, txq_id);
+ printk(KERN_INFO "bna: %s %d TXQ_STOPPED\n",
+ bnad->netdev->name, txq_id);
+ }
}
static void
-bnad_cb_tx_resume(struct bnad *bnad, struct bna_tcb *tcb)
+bnad_cb_tx_resume(struct bnad *bnad, struct bna_tx *tx)
{
- struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+ struct bnad_tx_info *tx_info = (struct bnad_tx_info *)tx->priv;
+ struct bna_tcb *tcb;
+ struct bnad_unmap_q *unmap_q;
+ u32 txq_id;
+ int i;
- if (test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags))
- return;
+ for (i = 0; i < BNAD_MAX_TXQ_PER_TX; i++) {
+ tcb = tx_info->tcb[i];
+ if (!tcb)
+ continue;
+ txq_id = tcb->id;
- clear_bit(BNAD_RF_TX_SHUTDOWN_DELAYED, &bnad->run_flags);
+ unmap_q = tcb->unmap_q;
- while (test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags))
- cpu_relax();
+ if (test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags))
+ continue;
- bnad_free_all_txbufs(bnad, tcb);
+ while (test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags))
+ cpu_relax();
- unmap_q->producer_index = 0;
- unmap_q->consumer_index = 0;
+ bnad_free_all_txbufs(bnad, tcb);
- smp_mb__before_clear_bit();
- clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+ unmap_q->producer_index = 0;
+ unmap_q->consumer_index = 0;
+
+ smp_mb__before_clear_bit();
+ clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+
+ set_bit(BNAD_TXQ_TX_STARTED, &tcb->flags);
+
+ if (netif_carrier_ok(bnad->netdev)) {
+ printk(KERN_INFO "bna: %s %d TXQ_STARTED\n",
+ bnad->netdev->name, txq_id);
+ netif_wake_subqueue(bnad->netdev, txq_id);
+ BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+ }
+ }
/*
- * Workaround for first device enable failure & we
+ * Workaround for first ioceth enable failure & we
* get a 0 MAC address. We try to get the MAC address
* again here.
*/
if (is_zero_ether_addr(&bnad->perm_addr.mac[0])) {
- bna_port_mac_get(&bnad->bna.port, &bnad->perm_addr);
+ bna_enet_perm_mac_get(&bnad->bna.enet, &bnad->perm_addr);
bnad_set_netdev_perm_addr(bnad);
}
+}
- set_bit(BNAD_TXQ_TX_STARTED, &tcb->flags);
+static void
+bnad_cb_tx_cleanup(struct bnad *bnad, struct bna_tx *tx)
+{
+ struct bnad_tx_info *tx_info = (struct bnad_tx_info *)tx->priv;
+ struct bna_tcb *tcb;
+ int i;
- if (netif_carrier_ok(bnad->netdev)) {
- pr_info("bna: %s TX_STARTED\n", bnad->netdev->name);
- netif_wake_queue(bnad->netdev);
- BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+ for (i = 0; i < BNAD_MAX_TXQ_PER_TX; i++) {
+ tcb = tx_info->tcb[i];
+ if (!tcb)
+ continue;
}
+
+ mdelay(BNAD_TXRX_SYNC_MDELAY);
+ bna_tx_cleanup_complete(tx);
}
static void
-bnad_cb_tx_cleanup(struct bnad *bnad, struct bna_tcb *tcb)
+bnad_cb_rx_stall(struct bnad *bnad, struct bna_rx *rx)
{
- /* Delay only once for the whole Tx Path Shutdown */
- if (!test_and_set_bit(BNAD_RF_TX_SHUTDOWN_DELAYED, &bnad->run_flags))
- mdelay(BNAD_TXRX_SYNC_MDELAY);
+ struct bnad_rx_info *rx_info = (struct bnad_rx_info *)rx->priv;
+ struct bna_ccb *ccb;
+ struct bnad_rx_ctrl *rx_ctrl;
+ int i;
+
+ for (i = 0; i < BNAD_MAX_RXP_PER_RX; i++) {
+ rx_ctrl = &rx_info->rx_ctrl[i];
+ ccb = rx_ctrl->ccb;
+ if (!ccb)
+ continue;
+
+ clear_bit(BNAD_RXQ_POST_OK, &ccb->rcb[0]->flags);
+
+ if (ccb->rcb[1])
+ clear_bit(BNAD_RXQ_POST_OK, &ccb->rcb[1]->flags);
+ }
}
static void
-bnad_cb_rx_cleanup(struct bnad *bnad,
- struct bna_ccb *ccb)
+bnad_cb_rx_cleanup(struct bnad *bnad, struct bna_rx *rx)
{
- clear_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags);
+ struct bnad_rx_info *rx_info = (struct bnad_rx_info *)rx->priv;
+ struct bna_ccb *ccb;
+ struct bnad_rx_ctrl *rx_ctrl;
+ int i;
- if (ccb->rcb[1])
- clear_bit(BNAD_RXQ_STARTED, &ccb->rcb[1]->flags);
+ mdelay(BNAD_TXRX_SYNC_MDELAY);
+
+ for (i = 0; i < BNAD_MAX_RXP_PER_RX; i++) {
+ rx_ctrl = &rx_info->rx_ctrl[i];
+ ccb = rx_ctrl->ccb;
+ if (!ccb)
+ continue;
+
+ clear_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags);
+
+ if (ccb->rcb[1])
+ clear_bit(BNAD_RXQ_STARTED, &ccb->rcb[1]->flags);
+
+ while (test_bit(BNAD_FP_IN_RX_PATH, &rx_ctrl->flags))
+ cpu_relax();
+ }
- if (!test_and_set_bit(BNAD_RF_RX_SHUTDOWN_DELAYED, &bnad->run_flags))
- mdelay(BNAD_TXRX_SYNC_MDELAY);
+ bna_rx_cleanup_complete(rx);
}
static void
-bnad_cb_rx_post(struct bnad *bnad, struct bna_rcb *rcb)
+bnad_cb_rx_post(struct bnad *bnad, struct bna_rx *rx)
{
- struct bnad_unmap_q *unmap_q = rcb->unmap_q;
-
- clear_bit(BNAD_RF_RX_SHUTDOWN_DELAYED, &bnad->run_flags);
-
- if (rcb == rcb->cq->ccb->rcb[0])
- bnad_cq_cmpl_init(bnad, rcb->cq->ccb);
+ struct bnad_rx_info *rx_info = (struct bnad_rx_info *)rx->priv;
+ struct bna_ccb *ccb;
+ struct bna_rcb *rcb;
+ struct bnad_rx_ctrl *rx_ctrl;
+ struct bnad_unmap_q *unmap_q;
+ int i;
+ int j;
- bnad_free_all_rxbufs(bnad, rcb);
+ for (i = 0; i < BNAD_MAX_RXP_PER_RX; i++) {
+ rx_ctrl = &rx_info->rx_ctrl[i];
+ ccb = rx_ctrl->ccb;
+ if (!ccb)
+ continue;
- set_bit(BNAD_RXQ_STARTED, &rcb->flags);
+ bnad_cq_cmpl_init(bnad, ccb);
- /* Now allocate & post buffers for this RCB */
- /* !!Allocation in callback context */
- if (!test_and_set_bit(BNAD_RXQ_REFILL, &rcb->flags)) {
- if (BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth)
- >> BNAD_RXQ_REFILL_THRESHOLD_SHIFT)
- bnad_alloc_n_post_rxbufs(bnad, rcb);
- smp_mb__before_clear_bit();
- clear_bit(BNAD_RXQ_REFILL, &rcb->flags);
+ for (j = 0; j < BNAD_MAX_RXQ_PER_RXP; j++) {
+ rcb = ccb->rcb[j];
+ if (!rcb)
+ continue;
+ bnad_free_all_rxbufs(bnad, rcb);
+
+ set_bit(BNAD_RXQ_STARTED, &rcb->flags);
+ set_bit(BNAD_RXQ_POST_OK, &rcb->flags);
+ unmap_q = rcb->unmap_q;
+
+ /* Now allocate & post buffers for this RCB */
+ /* !!Allocation in callback context */
+ if (!test_and_set_bit(BNAD_RXQ_REFILL, &rcb->flags)) {
+ if (BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth)
+ >> BNAD_RXQ_REFILL_THRESHOLD_SHIFT)
+ bnad_alloc_n_post_rxbufs(bnad, rcb);
+ smp_mb__before_clear_bit();
+ clear_bit(BNAD_RXQ_REFILL, &rcb->flags);
+ }
+ }
}
}
static void
-bnad_cb_rx_disabled(void *arg, struct bna_rx *rx,
- enum bna_cb_status status)
+bnad_cb_rx_disabled(void *arg, struct bna_rx *rx)
{
struct bnad *bnad = (struct bnad *)arg;
@@ -982,10 +1056,9 @@ bnad_cb_rx_disabled(void *arg, struct bna_rx *rx,
}
static void
-bnad_cb_rx_mcast_add(struct bnad *bnad, struct bna_rx *rx,
- enum bna_cb_status status)
+bnad_cb_rx_mcast_add(struct bnad *bnad, struct bna_rx *rx)
{
- bnad->bnad_completions.mcast_comp_status = status;
+ bnad->bnad_completions.mcast_comp_status = BNA_CB_SUCCESS;
complete(&bnad->bnad_completions.mcast_comp);
}
@@ -1004,6 +1077,13 @@ bnad_cb_stats_get(struct bnad *bnad, enum bna_cb_status status,
jiffies + msecs_to_jiffies(BNAD_STATS_TIMER_FREQ));
}
+static void
+bnad_cb_enet_mtu_set(struct bnad *bnad)
+{
+ bnad->bnad_completions.mtu_comp_status = BNA_CB_SUCCESS;
+ complete(&bnad->bnad_completions.mtu_comp);
+}
+
/* Resource allocation, free functions */
static void
@@ -1082,23 +1162,17 @@ err_return:
/* Free IRQ for Mailbox */
static void
-bnad_mbox_irq_free(struct bnad *bnad,
- struct bna_intr_info *intr_info)
+bnad_mbox_irq_free(struct bnad *bnad)
{
int irq;
unsigned long flags;
- if (intr_info->idl == NULL)
- return;
-
spin_lock_irqsave(&bnad->bna_lock, flags);
bnad_disable_mbox_irq(bnad);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
irq = BNAD_GET_MBOX_IRQ(bnad);
free_irq(irq, bnad);
-
- kfree(intr_info->idl);
}
/*
@@ -1107,35 +1181,25 @@ bnad_mbox_irq_free(struct bnad *bnad,
* from bna
*/
static int
-bnad_mbox_irq_alloc(struct bnad *bnad,
- struct bna_intr_info *intr_info)
+bnad_mbox_irq_alloc(struct bnad *bnad)
{
- int err = 0;
- unsigned long irq_flags = 0, flags;
+ int err = 0;
+ unsigned long irq_flags, flags;
u32 irq;
- irq_handler_t irq_handler;
-
- /* Mbox should use only 1 vector */
-
- intr_info->idl = kzalloc(sizeof(*(intr_info->idl)), GFP_KERNEL);
- if (!intr_info->idl)
- return -ENOMEM;
+ irq_handler_t irq_handler;
spin_lock_irqsave(&bnad->bna_lock, flags);
if (bnad->cfg_flags & BNAD_CF_MSIX) {
irq_handler = (irq_handler_t)bnad_msix_mbox_handler;
- irq = bnad->msix_table[bnad->msix_num - 1].vector;
- intr_info->intr_type = BNA_INTR_T_MSIX;
- intr_info->idl[0].vector = bnad->msix_num - 1;
+ irq = bnad->msix_table[BNAD_MAILBOX_MSIX_INDEX].vector;
+ irq_flags = 0;
} else {
irq_handler = (irq_handler_t)bnad_isr;
irq = bnad->pcidev->irq;
irq_flags = IRQF_SHARED;
- intr_info->intr_type = BNA_INTR_T_INTX;
- /* intr_info->idl.vector = 0 ? */
}
+
spin_unlock_irqrestore(&bnad->bna_lock, flags);
- flags = irq_flags;
sprintf(bnad->mbox_irq_name, "%s", BNAD_NAME);
/*
@@ -1146,14 +1210,9 @@ bnad_mbox_irq_alloc(struct bnad *bnad,
BNAD_UPDATE_CTR(bnad, mbox_intr_disabled);
- err = request_irq(irq, irq_handler, flags,
+ err = request_irq(irq, irq_handler, irq_flags,
bnad->mbox_irq_name, bnad);
- if (err) {
- kfree(intr_info->idl);
- intr_info->idl = NULL;
- }
-
return err;
}
@@ -1167,7 +1226,7 @@ bnad_txrx_irq_free(struct bnad *bnad, struct bna_intr_info *intr_info)
/* Allocates Interrupt Descriptor List for MSIX/INT-X vectors */
static int
bnad_txrx_irq_alloc(struct bnad *bnad, enum bnad_intr_source src,
- uint txrx_id, struct bna_intr_info *intr_info)
+ u32 txrx_id, struct bna_intr_info *intr_info)
{
int i, vector_start = 0;
u32 cfg_flags;
@@ -1187,11 +1246,12 @@ bnad_txrx_irq_alloc(struct bnad *bnad, enum bnad_intr_source src,
switch (src) {
case BNAD_INTR_TX:
- vector_start = txrx_id;
+ vector_start = BNAD_MAILBOX_MSIX_VECTORS + txrx_id;
break;
case BNAD_INTR_RX:
- vector_start = bnad->num_tx * bnad->num_txq_per_tx +
+ vector_start = BNAD_MAILBOX_MSIX_VECTORS +
+ (bnad->num_tx * bnad->num_txq_per_tx) +
txrx_id;
break;
@@ -1212,11 +1272,11 @@ bnad_txrx_irq_alloc(struct bnad *bnad, enum bnad_intr_source src,
switch (src) {
case BNAD_INTR_TX:
- intr_info->idl[0].vector = 0x1; /* Bit mask : Tx IB */
+ intr_info->idl[0].vector = BNAD_INTX_TX_IB_BITMASK;
break;
case BNAD_INTR_RX:
- intr_info->idl[0].vector = 0x2; /* Bit mask : Rx IB */
+ intr_info->idl[0].vector = BNAD_INTX_RX_IB_BITMASK;
break;
}
}
@@ -1249,7 +1309,7 @@ bnad_tx_msix_unregister(struct bnad *bnad, struct bnad_tx_info *tx_info,
*/
static int
bnad_tx_msix_register(struct bnad *bnad, struct bnad_tx_info *tx_info,
- uint tx_id, int num_txqs)
+ u32 tx_id, int num_txqs)
{
int i;
int err;
@@ -1302,7 +1362,7 @@ bnad_rx_msix_unregister(struct bnad *bnad, struct bnad_rx_info *rx_info,
*/
static int
bnad_rx_msix_register(struct bnad *bnad, struct bnad_rx_info *rx_info,
- uint rx_id, int num_rxps)
+ u32 rx_id, int num_rxps)
{
int i;
int err;
@@ -1346,7 +1406,7 @@ bnad_tx_res_free(struct bnad *bnad, struct bna_res_info *res_info)
/* Allocates memory and interrupt resources for Tx object */
static int
bnad_tx_res_alloc(struct bnad *bnad, struct bna_res_info *res_info,
- uint tx_id)
+ u32 tx_id)
{
int i, err = 0;
@@ -1415,7 +1475,7 @@ bnad_ioc_timeout(unsigned long data)
unsigned long flags;
spin_lock_irqsave(&bnad->bna_lock, flags);
- bfa_nw_ioc_timeout((void *) &bnad->bna.device.ioc);
+ bfa_nw_ioc_timeout((void *) &bnad->bna.ioceth.ioc);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
}
@@ -1426,7 +1486,7 @@ bnad_ioc_hb_check(unsigned long data)
unsigned long flags;
spin_lock_irqsave(&bnad->bna_lock, flags);
- bfa_nw_ioc_hb_check((void *) &bnad->bna.device.ioc);
+ bfa_nw_ioc_hb_check((void *) &bnad->bna.ioceth.ioc);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
}
@@ -1437,7 +1497,7 @@ bnad_iocpf_timeout(unsigned long data)
unsigned long flags;
spin_lock_irqsave(&bnad->bna_lock, flags);
- bfa_nw_iocpf_timeout((void *) &bnad->bna.device.ioc);
+ bfa_nw_iocpf_timeout((void *) &bnad->bna.ioceth.ioc);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
}
@@ -1448,14 +1508,14 @@ bnad_iocpf_sem_timeout(unsigned long data)
unsigned long flags;
spin_lock_irqsave(&bnad->bna_lock, flags);
- bfa_nw_iocpf_sem_timeout((void *) &bnad->bna.device.ioc);
+ bfa_nw_iocpf_sem_timeout((void *) &bnad->bna.ioceth.ioc);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
}
/*
* All timer routines use bnad->bna_lock to protect against
* the following race, which may occur in case of no locking:
- * Time CPU m CPU n
+ * Time CPU m CPU n
* 0 1 = test_bit
* 1 clear_bit
* 2 del_timer_sync
@@ -1507,7 +1567,7 @@ bnad_stats_timeout(unsigned long data)
return;
spin_lock_irqsave(&bnad->bna_lock, flags);
- bna_stats_get(&bnad->bna);
+ bna_hw_stats_get(&bnad->bna);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
}
@@ -1585,32 +1645,32 @@ bnad_napi_poll_rx(struct napi_struct *napi, int budget)
{
struct bnad_rx_ctrl *rx_ctrl =
container_of(napi, struct bnad_rx_ctrl, napi);
- struct bna_ccb *ccb;
- struct bnad *bnad;
+ struct bnad *bnad = rx_ctrl->bnad;
int rcvd = 0;
- ccb = rx_ctrl->ccb;
-
- bnad = ccb->bnad;
+ rx_ctrl->rx_poll_ctr++;
if (!netif_carrier_ok(bnad->netdev))
goto poll_exit;
- rcvd = bnad_poll_cq(bnad, ccb, budget);
- if (rcvd == budget)
+ rcvd = bnad_poll_cq(bnad, rx_ctrl->ccb, budget);
+ if (rcvd >= budget)
return rcvd;
poll_exit:
- napi_complete((napi));
+ napi_complete(napi);
+
+ rx_ctrl->rx_complete++;
- BNAD_UPDATE_CTR(bnad, netif_rx_complete);
+ if (rx_ctrl->ccb)
+ bnad_enable_rx_irq_unsafe(rx_ctrl->ccb);
- bnad_enable_rx_irq(bnad, ccb);
return rcvd;
}
+#define BNAD_NAPI_POLL_QUOTA 64
static void
-bnad_napi_enable(struct bnad *bnad, u32 rx_id)
+bnad_napi_init(struct bnad *bnad, u32 rx_id)
{
struct bnad_rx_ctrl *rx_ctrl;
int i;
@@ -1618,9 +1678,20 @@ bnad_napi_enable(struct bnad *bnad, u32 rx_id)
/* Initialize & enable NAPI */
for (i = 0; i < bnad->num_rxp_per_rx; i++) {
rx_ctrl = &bnad->rx_info[rx_id].rx_ctrl[i];
-
netif_napi_add(bnad->netdev, &rx_ctrl->napi,
- bnad_napi_poll_rx, 64);
+ bnad_napi_poll_rx, BNAD_NAPI_POLL_QUOTA);
+ }
+}
+
+static void
+bnad_napi_enable(struct bnad *bnad, u32 rx_id)
+{
+ struct bnad_rx_ctrl *rx_ctrl;
+ int i;
+
+ /* Initialize & enable NAPI */
+ for (i = 0; i < bnad->num_rxp_per_rx; i++) {
+ rx_ctrl = &bnad->rx_info[rx_id].rx_ctrl[i];
napi_enable(&rx_ctrl->napi);
}
@@ -1640,7 +1711,7 @@ bnad_napi_disable(struct bnad *bnad, u32 rx_id)
/* Should be held with conf_lock held */
void
-bnad_cleanup_tx(struct bnad *bnad, uint tx_id)
+bnad_cleanup_tx(struct bnad *bnad, u32 tx_id)
{
struct bnad_tx_info *tx_info = &bnad->tx_info[tx_id];
struct bna_res_info *res_info = &bnad->tx_res_info[tx_id].res_info[0];
@@ -1659,21 +1730,22 @@ bnad_cleanup_tx(struct bnad *bnad, uint tx_id)
bnad_tx_msix_unregister(bnad, tx_info,
bnad->num_txq_per_tx);
+ if (0 == tx_id)
+ tasklet_kill(&bnad->tx_free_tasklet);
+
spin_lock_irqsave(&bnad->bna_lock, flags);
bna_tx_destroy(tx_info->tx);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
tx_info->tx = NULL;
-
- if (0 == tx_id)
- tasklet_kill(&bnad->tx_free_tasklet);
+ tx_info->tx_id = 0;
bnad_tx_res_free(bnad, res_info);
}
/* Should be held with conf_lock held */
int
-bnad_setup_tx(struct bnad *bnad, uint tx_id)
+bnad_setup_tx(struct bnad *bnad, u32 tx_id)
{
int err;
struct bnad_tx_info *tx_info = &bnad->tx_info[tx_id];
@@ -1681,21 +1753,24 @@ bnad_setup_tx(struct bnad *bnad, uint tx_id)
struct bna_intr_info *intr_info =
&res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info;
struct bna_tx_config *tx_config = &bnad->tx_config[tx_id];
- struct bna_tx_event_cbfn tx_cbfn;
+ static const struct bna_tx_event_cbfn tx_cbfn = {
+ .tcb_setup_cbfn = bnad_cb_tcb_setup,
+ .tcb_destroy_cbfn = bnad_cb_tcb_destroy,
+ .tx_stall_cbfn = bnad_cb_tx_stall,
+ .tx_resume_cbfn = bnad_cb_tx_resume,
+ .tx_cleanup_cbfn = bnad_cb_tx_cleanup,
+ };
+
struct bna_tx *tx;
unsigned long flags;
+ tx_info->tx_id = tx_id;
+
/* Initialize the Tx object configuration */
tx_config->num_txq = bnad->num_txq_per_tx;
tx_config->txq_depth = bnad->txq_depth;
tx_config->tx_type = BNA_TX_T_REGULAR;
-
- /* Initialize the tx event handlers */
- tx_cbfn.tcb_setup_cbfn = bnad_cb_tcb_setup;
- tx_cbfn.tcb_destroy_cbfn = bnad_cb_tcb_destroy;
- tx_cbfn.tx_stall_cbfn = bnad_cb_tx_stall;
- tx_cbfn.tx_resume_cbfn = bnad_cb_tx_resume;
- tx_cbfn.tx_cleanup_cbfn = bnad_cb_tx_cleanup;
+ tx_config->coalescing_timeo = bnad->tx_coalescing_timeo;
/* Get BNA's resource requirement for one tx object */
spin_lock_irqsave(&bnad->bna_lock, flags);
@@ -1749,14 +1824,15 @@ bnad_init_rx_config(struct bnad *bnad, struct bna_rx_config *rx_config)
{
rx_config->rx_type = BNA_RX_T_REGULAR;
rx_config->num_paths = bnad->num_rxp_per_rx;
+ rx_config->coalescing_timeo = bnad->rx_coalescing_timeo;
if (bnad->num_rxp_per_rx > 1) {
rx_config->rss_status = BNA_STATUS_T_ENABLED;
rx_config->rss_config.hash_type =
- (BFI_RSS_T_V4_TCP |
- BFI_RSS_T_V6_TCP |
- BFI_RSS_T_V4_IP |
- BFI_RSS_T_V6_IP);
+ (BFI_ENET_RSS_IPV6 |
+ BFI_ENET_RSS_IPV6_TCP |
+ BFI_ENET_RSS_IPV4 |
+ BFI_ENET_RSS_IPV4_TCP);
rx_config->rss_config.hash_mask =
bnad->num_rxp_per_rx - 1;
get_random_bytes(rx_config->rss_config.toeplitz_hash_key,
@@ -1774,31 +1850,41 @@ bnad_init_rx_config(struct bnad *bnad, struct bna_rx_config *rx_config)
rx_config->vlan_strip_status = BNA_STATUS_T_ENABLED;
}
+static void
+bnad_rx_ctrl_init(struct bnad *bnad, u32 rx_id)
+{
+ struct bnad_rx_info *rx_info = &bnad->rx_info[rx_id];
+ int i;
+
+ for (i = 0; i < bnad->num_rxp_per_rx; i++)
+ rx_info->rx_ctrl[i].bnad = bnad;
+}
+
/* Called with mutex_lock(&bnad->conf_mutex) held */
void
-bnad_cleanup_rx(struct bnad *bnad, uint rx_id)
+bnad_cleanup_rx(struct bnad *bnad, u32 rx_id)
{
struct bnad_rx_info *rx_info = &bnad->rx_info[rx_id];
struct bna_rx_config *rx_config = &bnad->rx_config[rx_id];
struct bna_res_info *res_info = &bnad->rx_res_info[rx_id].res_info[0];
unsigned long flags;
- int dim_timer_del = 0;
+ int to_del = 0;
if (!rx_info->rx)
return;
if (0 == rx_id) {
spin_lock_irqsave(&bnad->bna_lock, flags);
- dim_timer_del = bnad_dim_timer_running(bnad);
- if (dim_timer_del)
+ if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED &&
+ test_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags)) {
clear_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags);
+ to_del = 1;
+ }
spin_unlock_irqrestore(&bnad->bna_lock, flags);
- if (dim_timer_del)
+ if (to_del)
del_timer_sync(&bnad->dim_timer);
}
- bnad_napi_disable(bnad, rx_id);
-
init_completion(&bnad->bnad_completions.rx_comp);
spin_lock_irqsave(&bnad->bna_lock, flags);
bna_rx_disable(rx_info->rx, BNA_HARD_CLEANUP, bnad_cb_rx_disabled);
@@ -1808,18 +1894,21 @@ bnad_cleanup_rx(struct bnad *bnad, uint rx_id)
if (rx_info->rx_ctrl[0].ccb->intr_type == BNA_INTR_T_MSIX)
bnad_rx_msix_unregister(bnad, rx_info, rx_config->num_paths);
+ bnad_napi_disable(bnad, rx_id);
+
spin_lock_irqsave(&bnad->bna_lock, flags);
bna_rx_destroy(rx_info->rx);
- spin_unlock_irqrestore(&bnad->bna_lock, flags);
rx_info->rx = NULL;
+ rx_info->rx_id = 0;
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
bnad_rx_res_free(bnad, res_info);
}
/* Called with mutex_lock(&bnad->conf_mutex) held */
int
-bnad_setup_rx(struct bnad *bnad, uint rx_id)
+bnad_setup_rx(struct bnad *bnad, u32 rx_id)
{
int err;
struct bnad_rx_info *rx_info = &bnad->rx_info[rx_id];
@@ -1827,21 +1916,23 @@ bnad_setup_rx(struct bnad *bnad, uint rx_id)
struct bna_intr_info *intr_info =
&res_info[BNA_RX_RES_T_INTR].res_u.intr_info;
struct bna_rx_config *rx_config = &bnad->rx_config[rx_id];
- struct bna_rx_event_cbfn rx_cbfn;
+ static const struct bna_rx_event_cbfn rx_cbfn = {
+ .rcb_setup_cbfn = bnad_cb_rcb_setup,
+ .rcb_destroy_cbfn = bnad_cb_rcb_destroy,
+ .ccb_setup_cbfn = bnad_cb_ccb_setup,
+ .ccb_destroy_cbfn = bnad_cb_ccb_destroy,
+ .rx_stall_cbfn = bnad_cb_rx_stall,
+ .rx_cleanup_cbfn = bnad_cb_rx_cleanup,
+ .rx_post_cbfn = bnad_cb_rx_post,
+ };
struct bna_rx *rx;
unsigned long flags;
+ rx_info->rx_id = rx_id;
+
/* Initialize the Rx object configuration */
bnad_init_rx_config(bnad, rx_config);
- /* Initialize the Rx event handlers */
- rx_cbfn.rcb_setup_cbfn = bnad_cb_rcb_setup;
- rx_cbfn.rcb_destroy_cbfn = bnad_cb_rcb_destroy;
- rx_cbfn.ccb_setup_cbfn = bnad_cb_ccb_setup;
- rx_cbfn.ccb_destroy_cbfn = bnad_cb_ccb_destroy;
- rx_cbfn.rx_cleanup_cbfn = bnad_cb_rx_cleanup;
- rx_cbfn.rx_post_cbfn = bnad_cb_rx_post;
-
/* Get BNA's resource requirement for one Rx object */
spin_lock_irqsave(&bnad->bna_lock, flags);
bna_rx_res_req(rx_config, res_info);
@@ -1859,14 +1950,25 @@ bnad_setup_rx(struct bnad *bnad, uint rx_id)
if (err)
return err;
+ bnad_rx_ctrl_init(bnad, rx_id);
+
/* Ask BNA to create one Rx object, supplying required resources */
spin_lock_irqsave(&bnad->bna_lock, flags);
rx = bna_rx_create(&bnad->bna, bnad, rx_config, &rx_cbfn, res_info,
rx_info);
- spin_unlock_irqrestore(&bnad->bna_lock, flags);
- if (!rx)
+ if (!rx) {
+ err = -ENOMEM;
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
goto err_return;
+ }
rx_info->rx = rx;
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+ /*
+ * Init NAPI, so that state is set to NAPI_STATE_SCHED,
+ * so that IRQ handler cannot schedule NAPI at this point.
+ */
+ bnad_napi_init(bnad, rx_id);
/* Register ISR for the Rx object */
if (intr_info->intr_type == BNA_INTR_T_MSIX) {
@@ -1876,9 +1978,6 @@ bnad_setup_rx(struct bnad *bnad, uint rx_id)
goto err_return;
}
- /* Enable NAPI */
- bnad_napi_enable(bnad, rx_id);
-
spin_lock_irqsave(&bnad->bna_lock, flags);
if (0 == rx_id) {
/* Set up Dynamic Interrupt Moderation Vector */
@@ -1895,6 +1994,9 @@ bnad_setup_rx(struct bnad *bnad, uint rx_id)
bna_rx_enable(rx);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
+ /* Enable scheduling of NAPI */
+ bnad_napi_enable(bnad, rx_id);
+
return 0;
err_return:
@@ -1920,7 +2022,7 @@ void
bnad_rx_coalescing_timeo_set(struct bnad *bnad)
{
struct bnad_rx_info *rx_info;
- int i;
+ int i;
for (i = 0; i < bnad->num_rx; i++) {
rx_info = &bnad->rx_info[i];
@@ -1934,7 +2036,7 @@ bnad_rx_coalescing_timeo_set(struct bnad *bnad)
/*
* Called with bnad->bna_lock held
*/
-static int
+int
bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr)
{
int ret;
@@ -1954,7 +2056,7 @@ bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr)
}
/* Should be called with conf_lock held */
-static int
+int
bnad_enable_default_bcast(struct bnad *bnad)
{
struct bnad_rx_info *rx_info = &bnad->rx_info[0];
@@ -1979,23 +2081,16 @@ bnad_enable_default_bcast(struct bnad *bnad)
return 0;
}
-/* Called with bnad_conf_lock() held */
-static void
+/* Called with mutex_lock(&bnad->conf_mutex) held */
+void
bnad_restore_vlans(struct bnad *bnad, u32 rx_id)
{
- u16 vlan_id;
+ u16 vid;
unsigned long flags;
- if (!bnad->vlan_grp)
- return;
-
- BUG_ON(!(VLAN_N_VID == (BFI_MAX_VLAN + 1)));
-
- for (vlan_id = 0; vlan_id < VLAN_N_VID; vlan_id++) {
- if (!vlan_group_get_device(bnad->vlan_grp, vlan_id))
- continue;
+ for_each_set_bit(vid, bnad->active_vlans, VLAN_N_VID) {
spin_lock_irqsave(&bnad->bna_lock, flags);
- bna_rx_vlan_add(bnad->rx_info[rx_id].rx, vlan_id);
+ bna_rx_vlan_add(bnad->rx_info[rx_id].rx, vid);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
}
}
@@ -2044,11 +2139,11 @@ bnad_netdev_qstats_fill(struct bnad *bnad, struct rtnl_link_stats64 *stats)
void
bnad_netdev_hwstats_fill(struct bnad *bnad, struct rtnl_link_stats64 *stats)
{
- struct bfi_ll_stats_mac *mac_stats;
- u64 bmap;
+ struct bfi_enet_stats_mac *mac_stats;
+ u32 bmap;
int i;
- mac_stats = &bnad->stats.bna_stats->hw_stats->mac_stats;
+ mac_stats = &bnad->stats.bna_stats->hw_stats.mac_stats;
stats->rx_errors =
mac_stats->rx_fcs_error + mac_stats->rx_alignment_error +
mac_stats->rx_frame_length_error + mac_stats->rx_code_error +
@@ -2067,13 +2162,12 @@ bnad_netdev_hwstats_fill(struct bnad *bnad, struct rtnl_link_stats64 *stats)
stats->rx_crc_errors = mac_stats->rx_fcs_error;
stats->rx_frame_errors = mac_stats->rx_alignment_error;
/* recv'r fifo overrun */
- bmap = (u64)bnad->stats.bna_stats->rxf_bmap[0] |
- ((u64)bnad->stats.bna_stats->rxf_bmap[1] << 32);
- for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+ bmap = bna_rx_rid_mask(&bnad->bna);
+ for (i = 0; bmap; i++) {
if (bmap & 1) {
stats->rx_fifo_errors +=
bnad->stats.bna_stats->
- hw_stats->rxf_stats[i].frame_drops;
+ hw_stats.rxf_stats[i].frame_drops;
break;
}
bmap >>= 1;
@@ -2088,7 +2182,7 @@ bnad_mbox_irq_sync(struct bnad *bnad)
spin_lock_irqsave(&bnad->bna_lock, flags);
if (bnad->cfg_flags & BNAD_CF_MSIX)
- irq = bnad->msix_table[bnad->msix_num - 1].vector;
+ irq = bnad->msix_table[BNAD_MAILBOX_MSIX_INDEX].vector;
else
irq = bnad->pcidev->irq;
spin_unlock_irqrestore(&bnad->bna_lock, flags);
@@ -2102,9 +2196,6 @@ bnad_tso_prepare(struct bnad *bnad, struct sk_buff *skb)
{
int err;
- /* SKB_GSO_TCPV4 and SKB_GSO_TCPV6 is defined since 2.6.18. */
- BUG_ON(!(skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4 ||
- skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6));
if (skb_header_cloned(skb)) {
err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
if (err) {
@@ -2131,7 +2222,6 @@ bnad_tso_prepare(struct bnad *bnad, struct sk_buff *skb)
} else {
struct ipv6hdr *ipv6h = ipv6_hdr(skb);
- BUG_ON(!(skb->protocol == htons(ETH_P_IPV6)));
ipv6h->payload_len = 0;
tcp_hdr(skb)->check =
~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, 0,
@@ -2153,7 +2243,7 @@ bnad_q_num_init(struct bnad *bnad)
int rxps;
rxps = min((uint)num_online_cpus(),
- (uint)(BNAD_MAX_RXS * BNAD_MAX_RXPS_PER_RX));
+ (uint)(BNAD_MAX_RX * BNAD_MAX_RXP_PER_RX));
if (!(bnad->cfg_flags & BNAD_CF_MSIX))
rxps = 1; /* INTx */
@@ -2171,7 +2261,7 @@ bnad_q_num_init(struct bnad *bnad)
* Called with bnad->bna_lock held b'cos of cfg_flags access
*/
static void
-bnad_q_num_adjust(struct bnad *bnad, int msix_vectors)
+bnad_q_num_adjust(struct bnad *bnad, int msix_vectors, int temp)
{
bnad->num_txq_per_tx = 1;
if ((msix_vectors >= (bnad->num_tx * bnad->num_txq_per_tx) +
@@ -2184,76 +2274,72 @@ bnad_q_num_adjust(struct bnad *bnad, int msix_vectors)
bnad->num_rxp_per_rx = 1;
}
-/* Enable / disable device */
-static void
-bnad_device_disable(struct bnad *bnad)
+/* Enable / disable ioceth */
+static int
+bnad_ioceth_disable(struct bnad *bnad)
{
unsigned long flags;
-
- init_completion(&bnad->bnad_completions.ioc_comp);
+ int err = 0;
spin_lock_irqsave(&bnad->bna_lock, flags);
- bna_device_disable(&bnad->bna.device, BNA_HARD_CLEANUP);
+ init_completion(&bnad->bnad_completions.ioc_comp);
+ bna_ioceth_disable(&bnad->bna.ioceth, BNA_HARD_CLEANUP);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
- wait_for_completion(&bnad->bnad_completions.ioc_comp);
+ wait_for_completion_timeout(&bnad->bnad_completions.ioc_comp,
+ msecs_to_jiffies(BNAD_IOCETH_TIMEOUT));
+
+ err = bnad->bnad_completions.ioc_comp_status;
+ return err;
}
static int
-bnad_device_enable(struct bnad *bnad)
+bnad_ioceth_enable(struct bnad *bnad)
{
int err = 0;
unsigned long flags;
- init_completion(&bnad->bnad_completions.ioc_comp);
-
spin_lock_irqsave(&bnad->bna_lock, flags);
- bna_device_enable(&bnad->bna.device);
+ init_completion(&bnad->bnad_completions.ioc_comp);
+ bnad->bnad_completions.ioc_comp_status = BNA_CB_WAITING;
+ bna_ioceth_enable(&bnad->bna.ioceth);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
- wait_for_completion(&bnad->bnad_completions.ioc_comp);
+ wait_for_completion_timeout(&bnad->bnad_completions.ioc_comp,
+ msecs_to_jiffies(BNAD_IOCETH_TIMEOUT));
- if (bnad->bnad_completions.ioc_comp_status)
- err = bnad->bnad_completions.ioc_comp_status;
+ err = bnad->bnad_completions.ioc_comp_status;
return err;
}
/* Free BNA resources */
static void
-bnad_res_free(struct bnad *bnad)
+bnad_res_free(struct bnad *bnad, struct bna_res_info *res_info,
+ u32 res_val_max)
{
int i;
- struct bna_res_info *res_info = &bnad->res_info[0];
- for (i = 0; i < BNA_RES_T_MAX; i++) {
- if (res_info[i].res_type == BNA_RES_T_MEM)
- bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
- else
- bnad_mbox_irq_free(bnad, &res_info[i].res_u.intr_info);
- }
+ for (i = 0; i < res_val_max; i++)
+ bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
}
/* Allocates memory and interrupt resources for BNA */
static int
-bnad_res_alloc(struct bnad *bnad)
+bnad_res_alloc(struct bnad *bnad, struct bna_res_info *res_info,
+ u32 res_val_max)
{
int i, err;
- struct bna_res_info *res_info = &bnad->res_info[0];
- for (i = 0; i < BNA_RES_T_MAX; i++) {
- if (res_info[i].res_type == BNA_RES_T_MEM)
- err = bnad_mem_alloc(bnad, &res_info[i].res_u.mem_info);
- else
- err = bnad_mbox_irq_alloc(bnad,
- &res_info[i].res_u.intr_info);
+ for (i = 0; i < res_val_max; i++) {
+ err = bnad_mem_alloc(bnad, &res_info[i].res_u.mem_info);
if (err)
goto err_return;
}
return 0;
err_return:
- bnad_res_free(bnad);
+ bnad_res_free(bnad, res_info, res_val_max);
return err;
}
@@ -2286,17 +2372,21 @@ bnad_enable_msix(struct bnad *bnad)
ret = pci_enable_msix(bnad->pcidev, bnad->msix_table, bnad->msix_num);
if (ret > 0) {
/* Not enough MSI-X vectors. */
+ pr_warn("BNA: %d MSI-X vectors allocated < %d requested\n",
+ ret, bnad->msix_num);
spin_lock_irqsave(&bnad->bna_lock, flags);
/* ret = #of vectors that we got */
- bnad_q_num_adjust(bnad, ret);
+ bnad_q_num_adjust(bnad, (ret - BNAD_MAILBOX_MSIX_VECTORS) / 2,
+ (ret - BNAD_MAILBOX_MSIX_VECTORS) / 2);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
- bnad->msix_num = (bnad->num_tx * bnad->num_txq_per_tx)
- + (bnad->num_rx
- * bnad->num_rxp_per_rx) +
+ bnad->msix_num = BNAD_NUM_TXQ + BNAD_NUM_RXP +
BNAD_MAILBOX_MSIX_VECTORS;
+ if (bnad->msix_num > ret)
+ goto intx_mode;
+
/* Try once more with adjusted numbers */
/* If this fails, fall back to INTx */
ret = pci_enable_msix(bnad->pcidev, bnad->msix_table,
@@ -2306,9 +2396,13 @@ bnad_enable_msix(struct bnad *bnad)
} else if (ret < 0)
goto intx_mode;
+
+ pci_intx(bnad->pcidev, 0);
+
return;
intx_mode:
+ pr_warn("BNA: MSI-X enable failed - operating in INTx mode\n");
kfree(bnad->msix_table);
bnad->msix_table = NULL;
@@ -2364,12 +2458,12 @@ bnad_open(struct net_device *netdev)
pause_config.tx_pause = 0;
pause_config.rx_pause = 0;
- mtu = ETH_HLEN + bnad->netdev->mtu + ETH_FCS_LEN;
+ mtu = ETH_HLEN + VLAN_HLEN + bnad->netdev->mtu + ETH_FCS_LEN;
spin_lock_irqsave(&bnad->bna_lock, flags);
- bna_port_mtu_set(&bnad->bna.port, mtu, NULL);
- bna_port_pause_config(&bnad->bna.port, &pause_config, NULL);
- bna_port_enable(&bnad->bna.port);
+ bna_enet_mtu_set(&bnad->bna.enet, mtu, NULL);
+ bna_enet_pause_config(&bnad->bna.enet, &pause_config, NULL);
+ bna_enet_enable(&bnad->bna.enet);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
/* Enable broadcast */
@@ -2409,14 +2503,14 @@ bnad_stop(struct net_device *netdev)
/* Stop the stats timer */
bnad_stats_timer_stop(bnad);
- init_completion(&bnad->bnad_completions.port_comp);
+ init_completion(&bnad->bnad_completions.enet_comp);
spin_lock_irqsave(&bnad->bna_lock, flags);
- bna_port_disable(&bnad->bna.port, BNA_HARD_CLEANUP,
- bnad_cb_port_disabled);
+ bna_enet_disable(&bnad->bna.enet, BNA_HARD_CLEANUP,
+ bnad_cb_enet_disabled);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
- wait_for_completion(&bnad->bnad_completions.port_comp);
+ wait_for_completion(&bnad->bnad_completions.enet_comp);
bnad_cleanup_tx(bnad, 0);
bnad_cleanup_rx(bnad, 0);
@@ -2438,51 +2532,57 @@ static netdev_tx_t
bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct bnad *bnad = netdev_priv(netdev);
+ u32 txq_id = 0;
+ struct bna_tcb *tcb = bnad->tx_info[0].tcb[txq_id];
- u16 txq_prod, vlan_tag = 0;
- u32 unmap_prod, wis, wis_used, wi_range;
- u32 vectors, vect_id, i, acked;
- u32 tx_id;
- int err;
+ u16 txq_prod, vlan_tag = 0;
+ u32 unmap_prod, wis, wis_used, wi_range;
+ u32 vectors, vect_id, i, acked;
+ int err;
+ unsigned int len;
+ u32 gso_size;
- struct bnad_tx_info *tx_info;
- struct bna_tcb *tcb;
- struct bnad_unmap_q *unmap_q;
- dma_addr_t dma_addr;
+ struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+ dma_addr_t dma_addr;
struct bna_txq_entry *txqent;
- bna_txq_wi_ctrl_flag_t flags;
+ u16 flags;
- if (unlikely
- (skb->len <= ETH_HLEN || skb->len > BFI_TX_MAX_DATA_PER_PKT)) {
+ if (unlikely(skb->len <= ETH_HLEN)) {
dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_too_short);
+ return NETDEV_TX_OK;
+ }
+ if (unlikely(skb_headlen(skb) > BFI_TX_MAX_DATA_PER_VECTOR)) {
+ dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_headlen_too_long);
+ return NETDEV_TX_OK;
+ }
+ if (unlikely(skb_headlen(skb) == 0)) {
+ dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_headlen_zero);
return NETDEV_TX_OK;
}
-
- tx_id = 0;
-
- tx_info = &bnad->tx_info[tx_id];
- tcb = tx_info->tcb[tx_id];
- unmap_q = tcb->unmap_q;
/*
* Takes care of the Tx that is scheduled between clearing the flag
- * and the netif_stop_queue() call.
+ * and the netif_tx_stop_all_queues() call.
*/
if (unlikely(!test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags))) {
dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_stopping);
return NETDEV_TX_OK;
}
vectors = 1 + skb_shinfo(skb)->nr_frags;
- if (vectors > BFI_TX_MAX_VECTORS_PER_PKT) {
+ if (unlikely(vectors > BFI_TX_MAX_VECTORS_PER_PKT)) {
dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_max_vectors);
return NETDEV_TX_OK;
}
wis = BNA_TXQ_WI_NEEDED(vectors); /* 4 vectors per work item */
acked = 0;
- if (unlikely
- (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
- vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
+ if (unlikely(wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
+ vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
if ((u16) (*tcb->hw_consumer_index) !=
tcb->consumer_index &&
!test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags)) {
@@ -2514,18 +2614,12 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
}
unmap_prod = unmap_q->producer_index;
- wis_used = 1;
- vect_id = 0;
flags = 0;
txq_prod = tcb->producer_index;
BNA_TXQ_QPGE_PTR_GET(txq_prod, tcb->sw_qpt, txqent, wi_range);
- BUG_ON(!(wi_range <= tcb->q_depth));
txqent->hdr.wi.reserved = 0;
txqent->hdr.wi.num_vectors = vectors;
- txqent->hdr.wi.opcode =
- htons((skb_is_gso(skb) ? BNA_TXQ_WI_SEND_LSO :
- BNA_TXQ_WI_SEND));
if (vlan_tx_tag_present(skb)) {
vlan_tag = (u16) vlan_tx_tag_get(skb);
@@ -2540,62 +2634,93 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
txqent->hdr.wi.vlan_tag = htons(vlan_tag);
if (skb_is_gso(skb)) {
+ gso_size = skb_shinfo(skb)->gso_size;
+
+ if (unlikely(gso_size > netdev->mtu)) {
+ dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_mss_too_long);
+ return NETDEV_TX_OK;
+ }
+ if (unlikely((gso_size + skb_transport_offset(skb) +
+ tcp_hdrlen(skb)) >= skb->len)) {
+ txqent->hdr.wi.opcode =
+ __constant_htons(BNA_TXQ_WI_SEND);
+ txqent->hdr.wi.lso_mss = 0;
+ BNAD_UPDATE_CTR(bnad, tx_skb_tso_too_short);
+ } else {
+ txqent->hdr.wi.opcode =
+ __constant_htons(BNA_TXQ_WI_SEND_LSO);
+ txqent->hdr.wi.lso_mss = htons(gso_size);
+ }
+
err = bnad_tso_prepare(bnad, skb);
- if (err) {
+ if (unlikely(err)) {
dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_tso_prepare);
return NETDEV_TX_OK;
}
- txqent->hdr.wi.lso_mss = htons(skb_is_gso(skb));
flags |= (BNA_TXQ_WI_CF_IP_CKSUM | BNA_TXQ_WI_CF_TCP_CKSUM);
txqent->hdr.wi.l4_hdr_size_n_offset =
htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
(tcp_hdrlen(skb) >> 2,
skb_transport_offset(skb)));
- } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
- u8 proto = 0;
-
+ } else {
+ txqent->hdr.wi.opcode = __constant_htons(BNA_TXQ_WI_SEND);
txqent->hdr.wi.lso_mss = 0;
- if (skb->protocol == htons(ETH_P_IP))
- proto = ip_hdr(skb)->protocol;
- else if (skb->protocol == htons(ETH_P_IPV6)) {
- /* nexthdr may not be TCP immediately. */
- proto = ipv6_hdr(skb)->nexthdr;
+ if (unlikely(skb->len > (netdev->mtu + ETH_HLEN))) {
+ dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_non_tso_too_long);
+ return NETDEV_TX_OK;
}
- if (proto == IPPROTO_TCP) {
- flags |= BNA_TXQ_WI_CF_TCP_CKSUM;
- txqent->hdr.wi.l4_hdr_size_n_offset =
- htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
- (0, skb_transport_offset(skb)));
- BNAD_UPDATE_CTR(bnad, tcpcsum_offload);
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ u8 proto = 0;
- BUG_ON(!(skb_headlen(skb) >=
- skb_transport_offset(skb) + tcp_hdrlen(skb)));
-
- } else if (proto == IPPROTO_UDP) {
- flags |= BNA_TXQ_WI_CF_UDP_CKSUM;
- txqent->hdr.wi.l4_hdr_size_n_offset =
- htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
- (0, skb_transport_offset(skb)));
-
- BNAD_UPDATE_CTR(bnad, udpcsum_offload);
+ if (skb->protocol == __constant_htons(ETH_P_IP))
+ proto = ip_hdr(skb)->protocol;
+ else if (skb->protocol ==
+ __constant_htons(ETH_P_IPV6)) {
+ /* nexthdr may not be TCP immediately. */
+ proto = ipv6_hdr(skb)->nexthdr;
+ }
+ if (proto == IPPROTO_TCP) {
+ flags |= BNA_TXQ_WI_CF_TCP_CKSUM;
+ txqent->hdr.wi.l4_hdr_size_n_offset =
+ htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+ (0, skb_transport_offset(skb)));
+
+ BNAD_UPDATE_CTR(bnad, tcpcsum_offload);
+
+ if (unlikely(skb_headlen(skb) <
+ skb_transport_offset(skb) + tcp_hdrlen(skb))) {
+ dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_tcp_hdr);
+ return NETDEV_TX_OK;
+ }
- BUG_ON(!(skb_headlen(skb) >=
- skb_transport_offset(skb) +
- sizeof(struct udphdr)));
- } else {
- err = skb_checksum_help(skb);
- BNAD_UPDATE_CTR(bnad, csum_help);
- if (err) {
+ } else if (proto == IPPROTO_UDP) {
+ flags |= BNA_TXQ_WI_CF_UDP_CKSUM;
+ txqent->hdr.wi.l4_hdr_size_n_offset =
+ htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+ (0, skb_transport_offset(skb)));
+
+ BNAD_UPDATE_CTR(bnad, udpcsum_offload);
+ if (unlikely(skb_headlen(skb) <
+ skb_transport_offset(skb) +
+ sizeof(struct udphdr))) {
+ dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_udp_hdr);
+ return NETDEV_TX_OK;
+ }
+ } else {
dev_kfree_skb(skb);
- BNAD_UPDATE_CTR(bnad, csum_help_err);
+ BNAD_UPDATE_CTR(bnad, tx_skb_csum_err);
return NETDEV_TX_OK;
}
+ } else {
+ txqent->hdr.wi.l4_hdr_size_n_offset = 0;
}
- } else {
- txqent->hdr.wi.lso_mss = 0;
- txqent->hdr.wi.l4_hdr_size_n_offset = 0;
}
txqent->hdr.wi.flags = htons(flags);
@@ -2603,19 +2728,36 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
txqent->hdr.wi.frame_length = htonl(skb->len);
unmap_q->unmap_array[unmap_prod].skb = skb;
- BUG_ON(!(skb_headlen(skb) <= BFI_TX_MAX_DATA_PER_VECTOR));
- txqent->vector[vect_id].length = htons(skb_headlen(skb));
+ len = skb_headlen(skb);
+ txqent->vector[0].length = htons(len);
dma_addr = dma_map_single(&bnad->pcidev->dev, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
dma_unmap_addr_set(&unmap_q->unmap_array[unmap_prod], dma_addr,
dma_addr);
- BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr);
+ BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[0].host_addr);
BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+ vect_id = 0;
+ wis_used = 1;
+
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
- u32 size = frag->size;
+ const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
+ u16 size = skb_frag_size(frag);
+
+ if (unlikely(size == 0)) {
+ unmap_prod = unmap_q->producer_index;
+
+ unmap_prod = bnad_pci_unmap_skb(&bnad->pcidev->dev,
+ unmap_q->unmap_array,
+ unmap_prod, unmap_q->q_depth, skb,
+ i);
+ dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_frag_zero);
+ return NETDEV_TX_OK;
+ }
+
+ len += size;
if (++vect_id == BFI_TX_MAX_VECTORS_PER_WI) {
vect_id = 0;
@@ -2627,22 +2769,34 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
wis_used = 0;
BNA_TXQ_QPGE_PTR_GET(txq_prod, tcb->sw_qpt,
txqent, wi_range);
- BUG_ON(!(wi_range <= tcb->q_depth));
}
wis_used++;
- txqent->hdr.wi_ext.opcode = htons(BNA_TXQ_WI_EXTENSION);
+ txqent->hdr.wi_ext.opcode =
+ __constant_htons(BNA_TXQ_WI_EXTENSION);
}
BUG_ON(!(size <= BFI_TX_MAX_DATA_PER_VECTOR));
txqent->vector[vect_id].length = htons(size);
- dma_addr = dma_map_page(&bnad->pcidev->dev, frag->page,
- frag->page_offset, size, DMA_TO_DEVICE);
+ dma_addr = skb_frag_dma_map(&bnad->pcidev->dev, frag,
+ 0, size, DMA_TO_DEVICE);
dma_unmap_addr_set(&unmap_q->unmap_array[unmap_prod], dma_addr,
dma_addr);
BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr);
BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
}
+ if (unlikely(len != skb->len)) {
+ unmap_prod = unmap_q->producer_index;
+
+ unmap_prod = bnad_pci_unmap_skb(&bnad->pcidev->dev,
+ unmap_q->unmap_array, unmap_prod,
+ unmap_q->q_depth, skb,
+ skb_shinfo(skb)->nr_frags);
+ dev_kfree_skb(skb);
+ BNAD_UPDATE_CTR(bnad, tx_skb_len_mismatch);
+ return NETDEV_TX_OK;
+ }
+
unmap_q->producer_index = unmap_prod;
BNA_QE_INDX_ADD(txq_prod, wis_used, tcb->q_depth);
tcb->producer_index = txq_prod;
@@ -2653,6 +2807,7 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
return NETDEV_TX_OK;
bna_txq_prod_indx_doorbell(tcb);
+ smp_mb();
if ((u16) (*tcb->hw_consumer_index) != tcb->consumer_index)
tasklet_schedule(&bnad->tx_free_tasklet);
@@ -2680,7 +2835,7 @@ bnad_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
return stats;
}
-static void
+void
bnad_set_rx_mode(struct net_device *netdev)
{
struct bnad *bnad = netdev_priv(netdev);
@@ -2719,6 +2874,9 @@ bnad_set_rx_mode(struct net_device *netdev)
}
}
+ if (bnad->rx_info[0].rx == NULL)
+ goto unlock;
+
bna_rx_mode_set(bnad->rx_info[0].rx, new_mask, valid_mask, NULL);
if (!netdev_mc_empty(netdev)) {
@@ -2773,11 +2931,25 @@ bnad_set_mac_address(struct net_device *netdev, void *mac_addr)
}
static int
-bnad_change_mtu(struct net_device *netdev, int new_mtu)
+bnad_mtu_set(struct bnad *bnad, int mtu)
{
- int mtu, err = 0;
unsigned long flags;
+ init_completion(&bnad->bnad_completions.mtu_comp);
+
+ spin_lock_irqsave(&bnad->bna_lock, flags);
+ bna_enet_mtu_set(&bnad->bna.enet, mtu, bnad_cb_enet_mtu_set);
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+ wait_for_completion(&bnad->bnad_completions.mtu_comp);
+
+ return bnad->bnad_completions.mtu_comp_status;
+}
+
+static int
+bnad_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ int err, mtu = netdev->mtu;
struct bnad *bnad = netdev_priv(netdev);
if (new_mtu + ETH_HLEN < ETH_ZLEN || new_mtu > BNAD_JUMBO_MTU)
@@ -2787,28 +2959,16 @@ bnad_change_mtu(struct net_device *netdev, int new_mtu)
netdev->mtu = new_mtu;
- mtu = ETH_HLEN + new_mtu + ETH_FCS_LEN;
-
- spin_lock_irqsave(&bnad->bna_lock, flags);
- bna_port_mtu_set(&bnad->bna.port, mtu, NULL);
- spin_unlock_irqrestore(&bnad->bna_lock, flags);
+ mtu = ETH_HLEN + VLAN_HLEN + new_mtu + ETH_FCS_LEN;
+ err = bnad_mtu_set(bnad, mtu);
+ if (err)
+ err = -EBUSY;
mutex_unlock(&bnad->conf_mutex);
return err;
}
static void
-bnad_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *vlan_grp)
-{
- struct bnad *bnad = netdev_priv(netdev);
-
- mutex_lock(&bnad->conf_mutex);
- bnad->vlan_grp = vlan_grp;
- mutex_unlock(&bnad->conf_mutex);
-}
-
-static void
bnad_vlan_rx_add_vid(struct net_device *netdev,
unsigned short vid)
{
@@ -2822,6 +2982,7 @@ bnad_vlan_rx_add_vid(struct net_device *netdev,
spin_lock_irqsave(&bnad->bna_lock, flags);
bna_rx_vlan_add(bnad->rx_info[0].rx, vid);
+ set_bit(vid, bnad->active_vlans);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
mutex_unlock(&bnad->conf_mutex);
@@ -2840,6 +3001,7 @@ bnad_vlan_rx_kill_vid(struct net_device *netdev,
mutex_lock(&bnad->conf_mutex);
spin_lock_irqsave(&bnad->bna_lock, flags);
+ clear_bit(vid, bnad->active_vlans);
bna_rx_vlan_del(bnad->rx_info[0].rx, vid);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
@@ -2861,18 +3023,21 @@ bnad_netpoll(struct net_device *netdev)
bnad_isr(bnad->pcidev->irq, netdev);
bna_intx_enable(&bnad->bna, curr_mask);
} else {
+ /*
+ * Tx processing may happen in sending context, so no need
+ * to explicitly process completions here
+ */
+
+ /* Rx processing */
for (i = 0; i < bnad->num_rx; i++) {
rx_info = &bnad->rx_info[i];
if (!rx_info->rx)
continue;
for (j = 0; j < bnad->num_rxp_per_rx; j++) {
rx_ctrl = &rx_info->rx_ctrl[j];
- if (rx_ctrl->ccb) {
- bnad_disable_rx_irq(bnad,
- rx_ctrl->ccb);
+ if (rx_ctrl->ccb)
bnad_netif_rx_schedule_poll(bnad,
rx_ctrl->ccb);
- }
}
}
}
@@ -2885,11 +3050,9 @@ static const struct net_device_ops bnad_netdev_ops = {
.ndo_start_xmit = bnad_start_xmit,
.ndo_get_stats64 = bnad_get_stats64,
.ndo_set_rx_mode = bnad_set_rx_mode,
- .ndo_set_multicast_list = bnad_set_rx_mode,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = bnad_set_mac_address,
.ndo_change_mtu = bnad_change_mtu,
- .ndo_vlan_rx_register = bnad_vlan_rx_register,
.ndo_vlan_rx_add_vid = bnad_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = bnad_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -2991,7 +3154,7 @@ bnad_uninit(struct bnad *bnad)
/*
* Initialize locks
- a) Per device mutes used for serializing configuration
+ a) Per ioceth mutes used for serializing configuration
changes from OS interface
b) spin lock used to protect bna state machine
*/
@@ -3056,8 +3219,8 @@ static int __devinit
bnad_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *pcidev_id)
{
- bool using_dac = false;
- int err;
+ bool using_dac;
+ int err;
struct bnad *bnad;
struct bna *bna;
struct net_device *netdev;
@@ -3081,22 +3244,24 @@ bnad_pci_probe(struct pci_dev *pdev,
*/
netdev = alloc_etherdev(sizeof(struct bnad));
if (!netdev) {
- dev_err(&pdev->dev, "alloc_etherdev failed\n");
+ dev_err(&pdev->dev, "netdev allocation failed\n");
err = -ENOMEM;
return err;
}
bnad = netdev_priv(netdev);
+ bnad_lock_init(bnad);
+
+ mutex_lock(&bnad->conf_mutex);
/*
* PCI initialization
- * Output : using_dac = 1 for 64 bit DMA
+ * Output : using_dac = 1 for 64 bit DMA
* = 0 for 32 bit DMA
*/
err = bnad_pci_init(bnad, pdev, &using_dac);
if (err)
- goto free_netdev;
+ goto unlock_mutex;
- bnad_lock_init(bnad);
/*
* Initialize bnad structure
* Setup relation between pci_dev & netdev
@@ -3105,21 +3270,22 @@ bnad_pci_probe(struct pci_dev *pdev,
err = bnad_init(bnad, pdev, netdev);
if (err)
goto pci_uninit;
+
/* Initialize netdev structure, set up ethtool ops */
bnad_netdev_init(bnad, using_dac);
/* Set link to down state */
netif_carrier_off(netdev);
- bnad_enable_msix(bnad);
-
/* Get resource requirement form bna */
+ spin_lock_irqsave(&bnad->bna_lock, flags);
bna_res_req(&bnad->res_info[0]);
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
/* Allocate resources from bna */
- err = bnad_res_alloc(bnad);
+ err = bnad_res_alloc(bnad, &bnad->res_info[0], BNA_RES_T_MAX);
if (err)
- goto free_netdev;
+ goto drv_uninit;
bna = &bnad->bna;
@@ -3129,38 +3295,74 @@ bnad_pci_probe(struct pci_dev *pdev,
pcidev_info.device_id = bnad->pcidev->device;
pcidev_info.pci_bar_kva = bnad->bar0;
- mutex_lock(&bnad->conf_mutex);
-
spin_lock_irqsave(&bnad->bna_lock, flags);
bna_init(bna, bnad, &pcidev_info, &bnad->res_info[0]);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
bnad->stats.bna_stats = &bna->stats;
+ bnad_enable_msix(bnad);
+ err = bnad_mbox_irq_alloc(bnad);
+ if (err)
+ goto res_free;
+
+
/* Set up timers */
- setup_timer(&bnad->bna.device.ioc.ioc_timer, bnad_ioc_timeout,
+ setup_timer(&bnad->bna.ioceth.ioc.ioc_timer, bnad_ioc_timeout,
((unsigned long)bnad));
- setup_timer(&bnad->bna.device.ioc.hb_timer, bnad_ioc_hb_check,
+ setup_timer(&bnad->bna.ioceth.ioc.hb_timer, bnad_ioc_hb_check,
((unsigned long)bnad));
- setup_timer(&bnad->bna.device.ioc.iocpf_timer, bnad_iocpf_timeout,
+ setup_timer(&bnad->bna.ioceth.ioc.iocpf_timer, bnad_iocpf_timeout,
((unsigned long)bnad));
- setup_timer(&bnad->bna.device.ioc.sem_timer, bnad_iocpf_sem_timeout,
+ setup_timer(&bnad->bna.ioceth.ioc.sem_timer, bnad_iocpf_sem_timeout,
((unsigned long)bnad));
/* Now start the timer before calling IOC */
- mod_timer(&bnad->bna.device.ioc.iocpf_timer,
+ mod_timer(&bnad->bna.ioceth.ioc.iocpf_timer,
jiffies + msecs_to_jiffies(BNA_IOC_TIMER_FREQ));
/*
* Start the chip
- * Don't care even if err != 0, bna state machine will
- * deal with it
+ * If the call back comes with error, we bail out.
+ * This is a catastrophic error.
*/
- err = bnad_device_enable(bnad);
+ err = bnad_ioceth_enable(bnad);
+ if (err) {
+ pr_err("BNA: Initialization failed err=%d\n",
+ err);
+ goto probe_success;
+ }
+
+ spin_lock_irqsave(&bnad->bna_lock, flags);
+ if (bna_num_txq_set(bna, BNAD_NUM_TXQ + 1) ||
+ bna_num_rxp_set(bna, BNAD_NUM_RXP + 1)) {
+ bnad_q_num_adjust(bnad, bna_attr(bna)->num_txq - 1,
+ bna_attr(bna)->num_rxp - 1);
+ if (bna_num_txq_set(bna, BNAD_NUM_TXQ + 1) ||
+ bna_num_rxp_set(bna, BNAD_NUM_RXP + 1))
+ err = -EIO;
+ }
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
+ if (err)
+ goto disable_ioceth;
+
+ spin_lock_irqsave(&bnad->bna_lock, flags);
+ bna_mod_res_req(&bnad->bna, &bnad->mod_res_info[0]);
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+ err = bnad_res_alloc(bnad, &bnad->mod_res_info[0], BNA_MOD_RES_T_MAX);
+ if (err) {
+ err = -EIO;
+ goto disable_ioceth;
+ }
+
+ spin_lock_irqsave(&bnad->bna_lock, flags);
+ bna_mod_init(&bnad->bna, &bnad->mod_res_info[0]);
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
/* Get the burnt-in mac */
spin_lock_irqsave(&bnad->bna_lock, flags);
- bna_port_mac_get(&bna->port, &bnad->perm_addr);
+ bna_enet_perm_mac_get(&bna->enet, &bnad->perm_addr);
bnad_set_netdev_perm_addr(bnad);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
@@ -3170,29 +3372,38 @@ bnad_pci_probe(struct pci_dev *pdev,
err = register_netdev(netdev);
if (err) {
pr_err("BNA : Registering with netdev failed\n");
- goto disable_device;
+ goto probe_uninit;
}
+ set_bit(BNAD_RF_NETDEV_REGISTERED, &bnad->run_flags);
return 0;
-disable_device:
+probe_success:
+ mutex_unlock(&bnad->conf_mutex);
+ return 0;
+
+probe_uninit:
mutex_lock(&bnad->conf_mutex);
- bnad_device_disable(bnad);
- del_timer_sync(&bnad->bna.device.ioc.ioc_timer);
- del_timer_sync(&bnad->bna.device.ioc.sem_timer);
- del_timer_sync(&bnad->bna.device.ioc.hb_timer);
+ bnad_res_free(bnad, &bnad->mod_res_info[0], BNA_MOD_RES_T_MAX);
+disable_ioceth:
+ bnad_ioceth_disable(bnad);
+ del_timer_sync(&bnad->bna.ioceth.ioc.ioc_timer);
+ del_timer_sync(&bnad->bna.ioceth.ioc.sem_timer);
+ del_timer_sync(&bnad->bna.ioceth.ioc.hb_timer);
spin_lock_irqsave(&bnad->bna_lock, flags);
bna_uninit(bna);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
- mutex_unlock(&bnad->conf_mutex);
-
- bnad_res_free(bnad);
+ bnad_mbox_irq_free(bnad);
bnad_disable_msix(bnad);
+res_free:
+ bnad_res_free(bnad, &bnad->res_info[0], BNA_RES_T_MAX);
+drv_uninit:
+ bnad_uninit(bnad);
pci_uninit:
bnad_pci_uninit(pdev);
+unlock_mutex:
+ mutex_unlock(&bnad->conf_mutex);
bnad_lock_uninit(bnad);
- bnad_uninit(bnad);
-free_netdev:
free_netdev(netdev);
return err;
}
@@ -3212,33 +3423,43 @@ bnad_pci_remove(struct pci_dev *pdev)
bnad = netdev_priv(netdev);
bna = &bnad->bna;
- unregister_netdev(netdev);
+ if (test_and_clear_bit(BNAD_RF_NETDEV_REGISTERED, &bnad->run_flags))
+ unregister_netdev(netdev);
mutex_lock(&bnad->conf_mutex);
- bnad_device_disable(bnad);
- del_timer_sync(&bnad->bna.device.ioc.ioc_timer);
- del_timer_sync(&bnad->bna.device.ioc.sem_timer);
- del_timer_sync(&bnad->bna.device.ioc.hb_timer);
+ bnad_ioceth_disable(bnad);
+ del_timer_sync(&bnad->bna.ioceth.ioc.ioc_timer);
+ del_timer_sync(&bnad->bna.ioceth.ioc.sem_timer);
+ del_timer_sync(&bnad->bna.ioceth.ioc.hb_timer);
spin_lock_irqsave(&bnad->bna_lock, flags);
bna_uninit(bna);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
- mutex_unlock(&bnad->conf_mutex);
- bnad_res_free(bnad);
+ bnad_res_free(bnad, &bnad->mod_res_info[0], BNA_MOD_RES_T_MAX);
+ bnad_res_free(bnad, &bnad->res_info[0], BNA_RES_T_MAX);
+ bnad_mbox_irq_free(bnad);
bnad_disable_msix(bnad);
bnad_pci_uninit(pdev);
+ mutex_unlock(&bnad->conf_mutex);
bnad_lock_uninit(bnad);
bnad_uninit(bnad);
free_netdev(netdev);
}
-static const struct pci_device_id bnad_pci_id_table[] = {
+static DEFINE_PCI_DEVICE_TABLE(bnad_pci_id_table) = {
{
PCI_DEVICE(PCI_VENDOR_ID_BROCADE,
PCI_DEVICE_ID_BROCADE_CT),
.class = PCI_CLASS_NETWORK_ETHERNET << 8,
.class_mask = 0xffff00
- }, {0, }
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_BROCADE,
+ BFA_PCI_DEVICE_ID_CT2),
+ .class = PCI_CLASS_NETWORK_ETHERNET << 8,
+ .class_mask = 0xffff00
+ },
+ {0, },
};
MODULE_DEVICE_TABLE(pci, bnad_pci_id_table);
@@ -3255,7 +3476,8 @@ bnad_module_init(void)
{
int err;
- pr_info("Brocade 10G Ethernet driver\n");
+ pr_info("Brocade 10G Ethernet driver - version: %s\n",
+ BNAD_VERSION);
bfa_nw_ioc_auto_recover(bnad_ioc_auto_recover);
@@ -3286,3 +3508,4 @@ MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Brocade 10G PCIe Ethernet driver");
MODULE_VERSION(BNAD_VERSION);
MODULE_FIRMWARE(CNA_FW_FILE_CT);
+MODULE_FIRMWARE(CNA_FW_FILE_CT2);
diff --git a/drivers/net/bna/bnad.h b/drivers/net/ethernet/brocade/bna/bnad.h
index ccdabad0a40c..5487ca42d018 100644
--- a/drivers/net/bna/bnad.h
+++ b/drivers/net/ethernet/brocade/bna/bnad.h
@@ -24,6 +24,7 @@
#include <linux/etherdevice.h>
#include <linux/mutex.h>
#include <linux/firmware.h>
+#include <linux/if_vlan.h>
/* Fix for IA64 */
#include <asm/checksum.h>
@@ -37,12 +38,13 @@
#define BNAD_TXQ_DEPTH 2048
#define BNAD_RXQ_DEPTH 2048
-#define BNAD_MAX_TXS 1
+#define BNAD_MAX_TX 1
#define BNAD_MAX_TXQ_PER_TX 8 /* 8 priority queues */
#define BNAD_TXQ_NUM 1
-#define BNAD_MAX_RXS 1
-#define BNAD_MAX_RXPS_PER_RX 16
+#define BNAD_MAX_RX 1
+#define BNAD_MAX_RXP_PER_RX 16
+#define BNAD_MAX_RXQ_PER_RXP 2
/*
* Control structure pointed to ccb->ctrl, which
@@ -51,30 +53,43 @@
*/
struct bnad_rx_ctrl {
struct bna_ccb *ccb;
+ struct bnad *bnad;
unsigned long flags;
struct napi_struct napi;
+ u64 rx_intr_ctr;
+ u64 rx_poll_ctr;
+ u64 rx_schedule;
+ u64 rx_keep_poll;
+ u64 rx_complete;
};
#define BNAD_RXMODE_PROMISC_DEFAULT BNA_RXMODE_PROMISC
-#define BNAD_GET_TX_ID(_skb) (0)
-
/*
* GLOBAL #defines (CONSTANTS)
*/
#define BNAD_NAME "bna"
#define BNAD_NAME_LEN 64
-#define BNAD_VERSION "2.3.2.3"
+#define BNAD_VERSION "3.0.2.2"
+#define BNAD_MAILBOX_MSIX_INDEX 0
#define BNAD_MAILBOX_MSIX_VECTORS 1
+#define BNAD_INTX_TX_IB_BITMASK 0x1
+#define BNAD_INTX_RX_IB_BITMASK 0x2
+
+#define BNAD_STATS_TIMER_FREQ 1000 /* in msecs */
+#define BNAD_DIM_TIMER_FREQ 1000 /* in msecs */
-#define BNAD_STATS_TIMER_FREQ 1000 /* in msecs */
-#define BNAD_DIM_TIMER_FREQ 1000 /* in msecs */
+#define BNAD_IOCETH_TIMEOUT 10000
#define BNAD_MAX_Q_DEPTH 0x10000
#define BNAD_MIN_Q_DEPTH 0x200
+#define BNAD_MAX_RXQ_DEPTH (BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq)
+/* keeping MAX TX and RX Q depth equal */
+#define BNAD_MAX_TXQ_DEPTH BNAD_MAX_RXQ_DEPTH
+
#define BNAD_JUMBO_MTU 9000
#define BNAD_NETIF_WAKE_THRESHOLD 8
@@ -88,6 +103,11 @@ struct bnad_rx_ctrl {
/* Bit positions for rcb->flags */
#define BNAD_RXQ_REFILL 0
#define BNAD_RXQ_STARTED 1
+#define BNAD_RXQ_POST_OK 2
+
+/* Resource limits */
+#define BNAD_NUM_TXQ (bnad->num_tx * bnad->num_txq_per_tx)
+#define BNAD_NUM_RXP (bnad->num_rx * bnad->num_rxp_per_rx)
/*
* DATA STRUCTURES
@@ -101,17 +121,18 @@ enum bnad_intr_source {
enum bnad_link_state {
BNAD_LS_DOWN = 0,
- BNAD_LS_UP = 1
+ BNAD_LS_UP = 1
};
struct bnad_completion {
- struct completion ioc_comp;
- struct completion ucast_comp;
+ struct completion ioc_comp;
+ struct completion ucast_comp;
struct completion mcast_comp;
struct completion tx_comp;
struct completion rx_comp;
struct completion stats_comp;
- struct completion port_comp;
+ struct completion enet_comp;
+ struct completion mtu_comp;
u8 ioc_comp_status;
u8 ucast_comp_status;
@@ -120,11 +141,12 @@ struct bnad_completion {
u8 rx_comp_status;
u8 stats_comp_status;
u8 port_comp_status;
+ u8 mtu_comp_status;
};
/* Tx Rx Control Stats */
struct bnad_drv_stats {
- u64 netif_queue_stop;
+ u64 netif_queue_stop;
u64 netif_queue_wakeup;
u64 netif_queue_stopped;
u64 tso4;
@@ -133,15 +155,26 @@ struct bnad_drv_stats {
u64 tcpcsum_offload;
u64 udpcsum_offload;
u64 csum_help;
- u64 csum_help_err;
+ u64 tx_skb_too_short;
+ u64 tx_skb_stopping;
+ u64 tx_skb_max_vectors;
+ u64 tx_skb_mss_too_long;
+ u64 tx_skb_tso_too_short;
+ u64 tx_skb_tso_prepare;
+ u64 tx_skb_non_tso_too_long;
+ u64 tx_skb_tcp_hdr;
+ u64 tx_skb_udp_hdr;
+ u64 tx_skb_csum_err;
+ u64 tx_skb_headlen_too_long;
+ u64 tx_skb_headlen_zero;
+ u64 tx_skb_frag_zero;
+ u64 tx_skb_len_mismatch;
u64 hw_stats_updates;
- u64 netif_rx_schedule;
- u64 netif_rx_complete;
u64 netif_rx_dropped;
u64 link_toggle;
- u64 cee_up;
+ u64 cee_toggle;
u64 rxp_info_alloc_failed;
u64 mbox_intr_disabled;
@@ -170,12 +203,14 @@ struct bnad_rx_res_info {
struct bnad_tx_info {
struct bna_tx *tx; /* 1:1 between tx_info & tx */
struct bna_tcb *tcb[BNAD_MAX_TXQ_PER_TX];
+ u32 tx_id;
} ____cacheline_aligned;
struct bnad_rx_info {
struct bna_rx *rx; /* 1:1 between rx_info & rx */
- struct bnad_rx_ctrl rx_ctrl[BNAD_MAX_RXPS_PER_RX];
+ struct bnad_rx_ctrl rx_ctrl[BNAD_MAX_RXP_PER_RX];
+ u32 rx_id;
} ____cacheline_aligned;
/* Unmap queues for Tx / Rx cleanup */
@@ -187,7 +222,7 @@ struct bnad_skb_unmap {
struct bnad_unmap_q {
u32 producer_index;
u32 consumer_index;
- u32 q_depth;
+ u32 q_depth;
/* This should be the last one */
struct bnad_skb_unmap unmap_array[1];
};
@@ -201,22 +236,27 @@ struct bnad_unmap_q {
/* Defines for run_flags bit-mask */
/* Set, tested & cleared using xxx_bit() functions */
/* Values indicated bit positions */
-#define BNAD_RF_CEE_RUNNING 1
+#define BNAD_RF_CEE_RUNNING 0
+#define BNAD_RF_MTU_SET 1
#define BNAD_RF_MBOX_IRQ_DISABLED 2
-#define BNAD_RF_RX_STARTED 3
+#define BNAD_RF_NETDEV_REGISTERED 3
#define BNAD_RF_DIM_TIMER_RUNNING 4
#define BNAD_RF_STATS_TIMER_RUNNING 5
-#define BNAD_RF_TX_SHUTDOWN_DELAYED 6
-#define BNAD_RF_RX_SHUTDOWN_DELAYED 7
+#define BNAD_RF_TX_PRIO_SET 6
+
+
+/* Define for Fast Path flags */
+/* Defined as bit positions */
+#define BNAD_FP_IN_RX_PATH 0
struct bnad {
- struct net_device *netdev;
+ struct net_device *netdev;
/* Data path */
- struct bnad_tx_info tx_info[BNAD_MAX_TXS];
- struct bnad_rx_info rx_info[BNAD_MAX_RXS];
+ struct bnad_tx_info tx_info[BNAD_MAX_TX];
+ struct bnad_rx_info rx_info[BNAD_MAX_RX];
- struct vlan_group *vlan_grp;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
/*
* These q numbers are global only because
* they are used to calculate MSIx vectors.
@@ -234,8 +274,8 @@ struct bnad {
u8 tx_coalescing_timeo;
u8 rx_coalescing_timeo;
- struct bna_rx_config rx_config[BNAD_MAX_RXS];
- struct bna_tx_config tx_config[BNAD_MAX_TXS];
+ struct bna_rx_config rx_config[BNAD_MAX_RX];
+ struct bna_tx_config tx_config[BNAD_MAX_TX];
void __iomem *bar0; /* BAR0 address */
@@ -244,7 +284,7 @@ struct bnad {
u32 cfg_flags;
unsigned long run_flags;
- struct pci_dev *pcidev;
+ struct pci_dev *pcidev;
u64 mmio_start;
u64 mmio_len;
@@ -261,8 +301,9 @@ struct bnad {
/* Control path resources, memory & irq */
struct bna_res_info res_info[BNA_RES_T_MAX];
- struct bnad_tx_res_info tx_res_info[BNAD_MAX_TXS];
- struct bnad_rx_res_info rx_res_info[BNAD_MAX_RXS];
+ struct bna_res_info mod_res_info[BNA_MOD_RES_T_MAX];
+ struct bnad_tx_res_info tx_res_info[BNAD_MAX_TX];
+ struct bnad_rx_res_info rx_res_info[BNAD_MAX_RX];
struct bnad_completion bnad_completions;
@@ -277,7 +318,7 @@ struct bnad {
struct bnad_diag *diag;
char adapter_name[BNAD_NAME_LEN];
- char port_name[BNAD_NAME_LEN];
+ char port_name[BNAD_NAME_LEN];
char mbox_irq_name[BNAD_NAME_LEN];
};
@@ -285,23 +326,29 @@ struct bnad {
* EXTERN VARIABLES
*/
extern struct firmware *bfi_fw;
-extern u32 bnad_rxqs_per_cq;
+extern u32 bnad_rxqs_per_cq;
/*
* EXTERN PROTOTYPES
*/
extern u32 *cna_get_firmware_buf(struct pci_dev *pdev);
/* Netdev entry point prototypes */
+extern void bnad_set_rx_mode(struct net_device *netdev);
+extern struct net_device_stats *bnad_get_netdev_stats(
+ struct net_device *netdev);
+extern int bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr);
+extern int bnad_enable_default_bcast(struct bnad *bnad);
+extern void bnad_restore_vlans(struct bnad *bnad, u32 rx_id);
extern void bnad_set_ethtool_ops(struct net_device *netdev);
/* Configuration & setup */
extern void bnad_tx_coalescing_timeo_set(struct bnad *bnad);
extern void bnad_rx_coalescing_timeo_set(struct bnad *bnad);
-extern int bnad_setup_rx(struct bnad *bnad, uint rx_id);
-extern int bnad_setup_tx(struct bnad *bnad, uint tx_id);
-extern void bnad_cleanup_tx(struct bnad *bnad, uint tx_id);
-extern void bnad_cleanup_rx(struct bnad *bnad, uint rx_id);
+extern int bnad_setup_rx(struct bnad *bnad, u32 rx_id);
+extern int bnad_setup_tx(struct bnad *bnad, u32 tx_id);
+extern void bnad_cleanup_tx(struct bnad *bnad, u32 tx_id);
+extern void bnad_cleanup_rx(struct bnad *bnad, u32 rx_id);
/* Timer start/stop protos */
extern void bnad_dim_timer_start(struct bnad *bnad);
@@ -323,15 +370,11 @@ extern void bnad_netdev_hwstats_fill(struct bnad *bnad,
#define bnad_enable_rx_irq_unsafe(_ccb) \
{ \
- if (likely(test_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags))) {\
+ if (likely(test_bit(BNAD_RXQ_STARTED, &(_ccb)->rcb[0]->flags))) {\
bna_ib_coalescing_timer_set((_ccb)->i_dbell, \
(_ccb)->rx_coalescing_timeo); \
bna_ib_ack((_ccb)->i_dbell, 0); \
} \
}
-#define bnad_dim_timer_running(_bnad) \
- (((_bnad)->cfg_flags & BNAD_CF_DIM_ENABLED) && \
- (test_bit(BNAD_RF_DIM_TIMER_RUNNING, &((_bnad)->run_flags))))
-
#endif /* __BNAD_H__ */
diff --git a/drivers/net/bna/bnad_ethtool.c b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c
index 3330cd78da2c..fd3dcc1e9145 100644
--- a/drivers/net/bna/bnad_ethtool.c
+++ b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c
@@ -29,14 +29,14 @@
#define BNAD_NUM_TXF_COUNTERS 12
#define BNAD_NUM_RXF_COUNTERS 10
-#define BNAD_NUM_CQ_COUNTERS 3
+#define BNAD_NUM_CQ_COUNTERS (3 + 5)
#define BNAD_NUM_RXQ_COUNTERS 6
#define BNAD_NUM_TXQ_COUNTERS 5
#define BNAD_ETHTOOL_STATS_NUM \
(sizeof(struct rtnl_link_stats64) / sizeof(u64) + \
sizeof(struct bnad_drv_stats) / sizeof(u64) + \
- offsetof(struct bfi_ll_stats, rxf_stats[0]) / sizeof(u64))
+ offsetof(struct bfi_enet_stats, rxf_stats[0]) / sizeof(u64))
static char *bnad_net_stats_strings[BNAD_ETHTOOL_STATS_NUM] = {
"rx_packets",
@@ -75,14 +75,25 @@ static char *bnad_net_stats_strings[BNAD_ETHTOOL_STATS_NUM] = {
"tcpcsum_offload",
"udpcsum_offload",
"csum_help",
- "csum_help_err",
+ "tx_skb_too_short",
+ "tx_skb_stopping",
+ "tx_skb_max_vectors",
+ "tx_skb_mss_too_long",
+ "tx_skb_tso_too_short",
+ "tx_skb_tso_prepare",
+ "tx_skb_non_tso_too_long",
+ "tx_skb_tcp_hdr",
+ "tx_skb_udp_hdr",
+ "tx_skb_csum_err",
+ "tx_skb_headlen_too_long",
+ "tx_skb_headlen_zero",
+ "tx_skb_frag_zero",
+ "tx_skb_len_mismatch",
"hw_stats_updates",
- "netif_rx_schedule",
- "netif_rx_complete",
"netif_rx_dropped",
"link_toggle",
- "cee_up",
+ "cee_toggle",
"rxp_info_alloc_failed",
"mbox_intr_disabled",
@@ -201,6 +212,20 @@ static char *bnad_net_stats_strings[BNAD_ETHTOOL_STATS_NUM] = {
"rad_rx_bcast_vlan",
"rad_rx_drops",
+ "rlb_rad_rx_frames",
+ "rlb_rad_rx_octets",
+ "rlb_rad_rx_vlan_frames",
+ "rlb_rad_rx_ucast",
+ "rlb_rad_rx_ucast_octets",
+ "rlb_rad_rx_ucast_vlan",
+ "rlb_rad_rx_mcast",
+ "rlb_rad_rx_mcast_octets",
+ "rlb_rad_rx_mcast_vlan",
+ "rlb_rad_rx_bcast",
+ "rlb_rad_rx_bcast_octets",
+ "rlb_rad_rx_bcast_vlan",
+ "rlb_rad_rx_drops",
+
"fc_rx_ucast_octets",
"fc_rx_ucast",
"fc_rx_ucast_vlan",
@@ -277,7 +302,7 @@ bnad_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
ioc_attr = kzalloc(sizeof(*ioc_attr), GFP_KERNEL);
if (ioc_attr) {
spin_lock_irqsave(&bnad->bna_lock, flags);
- bfa_nw_ioc_get_attr(&bnad->bna.device.ioc, ioc_attr);
+ bfa_nw_ioc_get_attr(&bnad->bna.ioceth.ioc, ioc_attr);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
strncpy(drvinfo->fw_version, ioc_attr->adapter_attr.fw_ver,
@@ -288,323 +313,6 @@ bnad_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
strncpy(drvinfo->bus_info, pci_name(bnad->pcidev), ETHTOOL_BUSINFO_LEN);
}
-static int
-get_regs(struct bnad *bnad, u32 * regs)
-{
- int num = 0, i;
- u32 reg_addr;
- unsigned long flags;
-
-#define BNAD_GET_REG(addr) \
-do { \
- if (regs) \
- regs[num++] = readl(bnad->bar0 + (addr)); \
- else \
- num++; \
-} while (0)
-
- spin_lock_irqsave(&bnad->bna_lock, flags);
-
- /* DMA Block Internal Registers */
- BNAD_GET_REG(DMA_CTRL_REG0);
- BNAD_GET_REG(DMA_CTRL_REG1);
- BNAD_GET_REG(DMA_ERR_INT_STATUS);
- BNAD_GET_REG(DMA_ERR_INT_ENABLE);
- BNAD_GET_REG(DMA_ERR_INT_STATUS_SET);
-
- /* APP Block Register Address Offset from BAR0 */
- BNAD_GET_REG(HOSTFN0_INT_STATUS);
- BNAD_GET_REG(HOSTFN0_INT_MASK);
- BNAD_GET_REG(HOST_PAGE_NUM_FN0);
- BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN0);
- BNAD_GET_REG(FN0_PCIE_ERR_REG);
- BNAD_GET_REG(FN0_ERR_TYPE_STATUS_REG);
- BNAD_GET_REG(FN0_ERR_TYPE_MSK_STATUS_REG);
-
- BNAD_GET_REG(HOSTFN1_INT_STATUS);
- BNAD_GET_REG(HOSTFN1_INT_MASK);
- BNAD_GET_REG(HOST_PAGE_NUM_FN1);
- BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN1);
- BNAD_GET_REG(FN1_PCIE_ERR_REG);
- BNAD_GET_REG(FN1_ERR_TYPE_STATUS_REG);
- BNAD_GET_REG(FN1_ERR_TYPE_MSK_STATUS_REG);
-
- BNAD_GET_REG(PCIE_MISC_REG);
-
- BNAD_GET_REG(HOST_SEM0_INFO_REG);
- BNAD_GET_REG(HOST_SEM1_INFO_REG);
- BNAD_GET_REG(HOST_SEM2_INFO_REG);
- BNAD_GET_REG(HOST_SEM3_INFO_REG);
-
- BNAD_GET_REG(TEMPSENSE_CNTL_REG);
- BNAD_GET_REG(TEMPSENSE_STAT_REG);
-
- BNAD_GET_REG(APP_LOCAL_ERR_STAT);
- BNAD_GET_REG(APP_LOCAL_ERR_MSK);
-
- BNAD_GET_REG(PCIE_LNK_ERR_STAT);
- BNAD_GET_REG(PCIE_LNK_ERR_MSK);
-
- BNAD_GET_REG(FCOE_FIP_ETH_TYPE);
- BNAD_GET_REG(RESV_ETH_TYPE);
-
- BNAD_GET_REG(HOSTFN2_INT_STATUS);
- BNAD_GET_REG(HOSTFN2_INT_MASK);
- BNAD_GET_REG(HOST_PAGE_NUM_FN2);
- BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN2);
- BNAD_GET_REG(FN2_PCIE_ERR_REG);
- BNAD_GET_REG(FN2_ERR_TYPE_STATUS_REG);
- BNAD_GET_REG(FN2_ERR_TYPE_MSK_STATUS_REG);
-
- BNAD_GET_REG(HOSTFN3_INT_STATUS);
- BNAD_GET_REG(HOSTFN3_INT_MASK);
- BNAD_GET_REG(HOST_PAGE_NUM_FN3);
- BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN3);
- BNAD_GET_REG(FN3_PCIE_ERR_REG);
- BNAD_GET_REG(FN3_ERR_TYPE_STATUS_REG);
- BNAD_GET_REG(FN3_ERR_TYPE_MSK_STATUS_REG);
-
- /* Host Command Status Registers */
- reg_addr = HOST_CMDSTS0_CLR_REG;
- for (i = 0; i < 16; i++) {
- BNAD_GET_REG(reg_addr);
- BNAD_GET_REG(reg_addr + 4);
- BNAD_GET_REG(reg_addr + 8);
- reg_addr += 0x10;
- }
-
- /* Function ID register */
- BNAD_GET_REG(FNC_ID_REG);
-
- /* Function personality register */
- BNAD_GET_REG(FNC_PERS_REG);
-
- /* Operation mode register */
- BNAD_GET_REG(OP_MODE);
-
- /* LPU0 Registers */
- BNAD_GET_REG(LPU0_MBOX_CTL_REG);
- BNAD_GET_REG(LPU0_MBOX_CMD_REG);
- BNAD_GET_REG(LPU0_MBOX_LINK_0REG);
- BNAD_GET_REG(LPU1_MBOX_LINK_0REG);
- BNAD_GET_REG(LPU0_MBOX_STATUS_0REG);
- BNAD_GET_REG(LPU1_MBOX_STATUS_0REG);
- BNAD_GET_REG(LPU0_ERR_STATUS_REG);
- BNAD_GET_REG(LPU0_ERR_SET_REG);
-
- /* LPU1 Registers */
- BNAD_GET_REG(LPU1_MBOX_CTL_REG);
- BNAD_GET_REG(LPU1_MBOX_CMD_REG);
- BNAD_GET_REG(LPU0_MBOX_LINK_1REG);
- BNAD_GET_REG(LPU1_MBOX_LINK_1REG);
- BNAD_GET_REG(LPU0_MBOX_STATUS_1REG);
- BNAD_GET_REG(LPU1_MBOX_STATUS_1REG);
- BNAD_GET_REG(LPU1_ERR_STATUS_REG);
- BNAD_GET_REG(LPU1_ERR_SET_REG);
-
- /* PSS Registers */
- BNAD_GET_REG(PSS_CTL_REG);
- BNAD_GET_REG(PSS_ERR_STATUS_REG);
- BNAD_GET_REG(ERR_STATUS_SET);
- BNAD_GET_REG(PSS_RAM_ERR_STATUS_REG);
-
- /* Catapult CPQ Registers */
- BNAD_GET_REG(HOSTFN0_LPU0_MBOX0_CMD_STAT);
- BNAD_GET_REG(HOSTFN0_LPU1_MBOX0_CMD_STAT);
- BNAD_GET_REG(LPU0_HOSTFN0_MBOX0_CMD_STAT);
- BNAD_GET_REG(LPU1_HOSTFN0_MBOX0_CMD_STAT);
-
- BNAD_GET_REG(HOSTFN0_LPU0_MBOX1_CMD_STAT);
- BNAD_GET_REG(HOSTFN0_LPU1_MBOX1_CMD_STAT);
- BNAD_GET_REG(LPU0_HOSTFN0_MBOX1_CMD_STAT);
- BNAD_GET_REG(LPU1_HOSTFN0_MBOX1_CMD_STAT);
-
- BNAD_GET_REG(HOSTFN1_LPU0_MBOX0_CMD_STAT);
- BNAD_GET_REG(HOSTFN1_LPU1_MBOX0_CMD_STAT);
- BNAD_GET_REG(LPU0_HOSTFN1_MBOX0_CMD_STAT);
- BNAD_GET_REG(LPU1_HOSTFN1_MBOX0_CMD_STAT);
-
- BNAD_GET_REG(HOSTFN1_LPU0_MBOX1_CMD_STAT);
- BNAD_GET_REG(HOSTFN1_LPU1_MBOX1_CMD_STAT);
- BNAD_GET_REG(LPU0_HOSTFN1_MBOX1_CMD_STAT);
- BNAD_GET_REG(LPU1_HOSTFN1_MBOX1_CMD_STAT);
-
- BNAD_GET_REG(HOSTFN2_LPU0_MBOX0_CMD_STAT);
- BNAD_GET_REG(HOSTFN2_LPU1_MBOX0_CMD_STAT);
- BNAD_GET_REG(LPU0_HOSTFN2_MBOX0_CMD_STAT);
- BNAD_GET_REG(LPU1_HOSTFN2_MBOX0_CMD_STAT);
-
- BNAD_GET_REG(HOSTFN2_LPU0_MBOX1_CMD_STAT);
- BNAD_GET_REG(HOSTFN2_LPU1_MBOX1_CMD_STAT);
- BNAD_GET_REG(LPU0_HOSTFN2_MBOX1_CMD_STAT);
- BNAD_GET_REG(LPU1_HOSTFN2_MBOX1_CMD_STAT);
-
- BNAD_GET_REG(HOSTFN3_LPU0_MBOX0_CMD_STAT);
- BNAD_GET_REG(HOSTFN3_LPU1_MBOX0_CMD_STAT);
- BNAD_GET_REG(LPU0_HOSTFN3_MBOX0_CMD_STAT);
- BNAD_GET_REG(LPU1_HOSTFN3_MBOX0_CMD_STAT);
-
- BNAD_GET_REG(HOSTFN3_LPU0_MBOX1_CMD_STAT);
- BNAD_GET_REG(HOSTFN3_LPU1_MBOX1_CMD_STAT);
- BNAD_GET_REG(LPU0_HOSTFN3_MBOX1_CMD_STAT);
- BNAD_GET_REG(LPU1_HOSTFN3_MBOX1_CMD_STAT);
-
- /* Host Function Force Parity Error Registers */
- BNAD_GET_REG(HOSTFN0_LPU_FORCE_PERR);
- BNAD_GET_REG(HOSTFN1_LPU_FORCE_PERR);
- BNAD_GET_REG(HOSTFN2_LPU_FORCE_PERR);
- BNAD_GET_REG(HOSTFN3_LPU_FORCE_PERR);
-
- /* LL Port[0|1] Halt Mask Registers */
- BNAD_GET_REG(LL_HALT_MSK_P0);
- BNAD_GET_REG(LL_HALT_MSK_P1);
-
- /* LL Port[0|1] Error Mask Registers */
- BNAD_GET_REG(LL_ERR_MSK_P0);
- BNAD_GET_REG(LL_ERR_MSK_P1);
-
- /* EMC FLI Registers */
- BNAD_GET_REG(FLI_CMD_REG);
- BNAD_GET_REG(FLI_ADDR_REG);
- BNAD_GET_REG(FLI_CTL_REG);
- BNAD_GET_REG(FLI_WRDATA_REG);
- BNAD_GET_REG(FLI_RDDATA_REG);
- BNAD_GET_REG(FLI_DEV_STATUS_REG);
- BNAD_GET_REG(FLI_SIG_WD_REG);
-
- BNAD_GET_REG(FLI_DEV_VENDOR_REG);
- BNAD_GET_REG(FLI_ERR_STATUS_REG);
-
- /* RxAdm 0 Registers */
- BNAD_GET_REG(RAD0_CTL_REG);
- BNAD_GET_REG(RAD0_PE_PARM_REG);
- BNAD_GET_REG(RAD0_BCN_REG);
- BNAD_GET_REG(RAD0_DEFAULT_REG);
- BNAD_GET_REG(RAD0_PROMISC_REG);
- BNAD_GET_REG(RAD0_BCNQ_REG);
- BNAD_GET_REG(RAD0_DEFAULTQ_REG);
-
- BNAD_GET_REG(RAD0_ERR_STS);
- BNAD_GET_REG(RAD0_SET_ERR_STS);
- BNAD_GET_REG(RAD0_ERR_INT_EN);
- BNAD_GET_REG(RAD0_FIRST_ERR);
- BNAD_GET_REG(RAD0_FORCE_ERR);
-
- BNAD_GET_REG(RAD0_MAC_MAN_1H);
- BNAD_GET_REG(RAD0_MAC_MAN_1L);
- BNAD_GET_REG(RAD0_MAC_MAN_2H);
- BNAD_GET_REG(RAD0_MAC_MAN_2L);
- BNAD_GET_REG(RAD0_MAC_MAN_3H);
- BNAD_GET_REG(RAD0_MAC_MAN_3L);
- BNAD_GET_REG(RAD0_MAC_MAN_4H);
- BNAD_GET_REG(RAD0_MAC_MAN_4L);
-
- BNAD_GET_REG(RAD0_LAST4_IP);
-
- /* RxAdm 1 Registers */
- BNAD_GET_REG(RAD1_CTL_REG);
- BNAD_GET_REG(RAD1_PE_PARM_REG);
- BNAD_GET_REG(RAD1_BCN_REG);
- BNAD_GET_REG(RAD1_DEFAULT_REG);
- BNAD_GET_REG(RAD1_PROMISC_REG);
- BNAD_GET_REG(RAD1_BCNQ_REG);
- BNAD_GET_REG(RAD1_DEFAULTQ_REG);
-
- BNAD_GET_REG(RAD1_ERR_STS);
- BNAD_GET_REG(RAD1_SET_ERR_STS);
- BNAD_GET_REG(RAD1_ERR_INT_EN);
-
- /* TxA0 Registers */
- BNAD_GET_REG(TXA0_CTRL_REG);
- /* TxA0 TSO Sequence # Registers (RO) */
- for (i = 0; i < 8; i++) {
- BNAD_GET_REG(TXA0_TSO_TCP_SEQ_REG(i));
- BNAD_GET_REG(TXA0_TSO_IP_INFO_REG(i));
- }
-
- /* TxA1 Registers */
- BNAD_GET_REG(TXA1_CTRL_REG);
- /* TxA1 TSO Sequence # Registers (RO) */
- for (i = 0; i < 8; i++) {
- BNAD_GET_REG(TXA1_TSO_TCP_SEQ_REG(i));
- BNAD_GET_REG(TXA1_TSO_IP_INFO_REG(i));
- }
-
- /* RxA Registers */
- BNAD_GET_REG(RXA0_CTL_REG);
- BNAD_GET_REG(RXA1_CTL_REG);
-
- /* PLB0 Registers */
- BNAD_GET_REG(PLB0_ECM_TIMER_REG);
- BNAD_GET_REG(PLB0_RL_CTL);
- for (i = 0; i < 8; i++)
- BNAD_GET_REG(PLB0_RL_MAX_BC(i));
- BNAD_GET_REG(PLB0_RL_TU_PRIO);
- for (i = 0; i < 8; i++)
- BNAD_GET_REG(PLB0_RL_BYTE_CNT(i));
- BNAD_GET_REG(PLB0_RL_MIN_REG);
- BNAD_GET_REG(PLB0_RL_MAX_REG);
- BNAD_GET_REG(PLB0_EMS_ADD_REG);
-
- /* PLB1 Registers */
- BNAD_GET_REG(PLB1_ECM_TIMER_REG);
- BNAD_GET_REG(PLB1_RL_CTL);
- for (i = 0; i < 8; i++)
- BNAD_GET_REG(PLB1_RL_MAX_BC(i));
- BNAD_GET_REG(PLB1_RL_TU_PRIO);
- for (i = 0; i < 8; i++)
- BNAD_GET_REG(PLB1_RL_BYTE_CNT(i));
- BNAD_GET_REG(PLB1_RL_MIN_REG);
- BNAD_GET_REG(PLB1_RL_MAX_REG);
- BNAD_GET_REG(PLB1_EMS_ADD_REG);
-
- /* HQM Control Register */
- BNAD_GET_REG(HQM0_CTL_REG);
- BNAD_GET_REG(HQM0_RXQ_STOP_SEM);
- BNAD_GET_REG(HQM0_TXQ_STOP_SEM);
- BNAD_GET_REG(HQM1_CTL_REG);
- BNAD_GET_REG(HQM1_RXQ_STOP_SEM);
- BNAD_GET_REG(HQM1_TXQ_STOP_SEM);
-
- /* LUT Registers */
- BNAD_GET_REG(LUT0_ERR_STS);
- BNAD_GET_REG(LUT0_SET_ERR_STS);
- BNAD_GET_REG(LUT1_ERR_STS);
- BNAD_GET_REG(LUT1_SET_ERR_STS);
-
- /* TRC Registers */
- BNAD_GET_REG(TRC_CTL_REG);
- BNAD_GET_REG(TRC_MODS_REG);
- BNAD_GET_REG(TRC_TRGC_REG);
- BNAD_GET_REG(TRC_CNT1_REG);
- BNAD_GET_REG(TRC_CNT2_REG);
- BNAD_GET_REG(TRC_NXTS_REG);
- BNAD_GET_REG(TRC_DIRR_REG);
- for (i = 0; i < 10; i++)
- BNAD_GET_REG(TRC_TRGM_REG(i));
- for (i = 0; i < 10; i++)
- BNAD_GET_REG(TRC_NXTM_REG(i));
- for (i = 0; i < 10; i++)
- BNAD_GET_REG(TRC_STRM_REG(i));
-
- spin_unlock_irqrestore(&bnad->bna_lock, flags);
-#undef BNAD_GET_REG
- return num;
-}
-static int
-bnad_get_regs_len(struct net_device *netdev)
-{
- int ret = get_regs(netdev_priv(netdev), NULL) * sizeof(u32);
- return ret;
-}
-
-static void
-bnad_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *buf)
-{
- memset(buf, 0, bnad_get_regs_len(netdev));
- get_regs(netdev_priv(netdev), buf);
-}
-
static void
bnad_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wolinfo)
{
@@ -638,7 +346,7 @@ bnad_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
{
struct bnad *bnad = netdev_priv(netdev);
unsigned long flags;
- int dim_timer_del = 0;
+ int to_del = 0;
if (coalesce->rx_coalesce_usecs == 0 ||
coalesce->rx_coalesce_usecs >
@@ -665,14 +373,17 @@ bnad_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
} else {
if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED) {
bnad->cfg_flags &= ~BNAD_CF_DIM_ENABLED;
- dim_timer_del = bnad_dim_timer_running(bnad);
- if (dim_timer_del) {
+ if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED &&
+ test_bit(BNAD_RF_DIM_TIMER_RUNNING,
+ &bnad->run_flags)) {
clear_bit(BNAD_RF_DIM_TIMER_RUNNING,
&bnad->run_flags);
- spin_unlock_irqrestore(&bnad->bna_lock, flags);
- del_timer_sync(&bnad->dim_timer);
- spin_lock_irqsave(&bnad->bna_lock, flags);
+ to_del = 1;
}
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
+ if (to_del)
+ del_timer_sync(&bnad->dim_timer);
+ spin_lock_irqsave(&bnad->bna_lock, flags);
bnad_rx_coalescing_timeo_set(bnad);
}
}
@@ -707,14 +418,10 @@ bnad_get_ringparam(struct net_device *netdev,
{
struct bnad *bnad = netdev_priv(netdev);
- ringparam->rx_max_pending = BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq;
- ringparam->rx_mini_max_pending = 0;
- ringparam->rx_jumbo_max_pending = 0;
- ringparam->tx_max_pending = BNAD_MAX_Q_DEPTH;
+ ringparam->rx_max_pending = BNAD_MAX_RXQ_DEPTH;
+ ringparam->tx_max_pending = BNAD_MAX_TXQ_DEPTH;
ringparam->rx_pending = bnad->rxq_depth;
- ringparam->rx_mini_max_pending = 0;
- ringparam->rx_jumbo_max_pending = 0;
ringparam->tx_pending = bnad->txq_depth;
}
@@ -724,6 +431,7 @@ bnad_set_ringparam(struct net_device *netdev,
{
int i, current_err, err = 0;
struct bnad *bnad = netdev_priv(netdev);
+ unsigned long flags;
mutex_lock(&bnad->conf_mutex);
if (ringparam->rx_pending == bnad->rxq_depth &&
@@ -733,13 +441,13 @@ bnad_set_ringparam(struct net_device *netdev,
}
if (ringparam->rx_pending < BNAD_MIN_Q_DEPTH ||
- ringparam->rx_pending > BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq ||
+ ringparam->rx_pending > BNAD_MAX_RXQ_DEPTH ||
!BNA_POWER_OF_2(ringparam->rx_pending)) {
mutex_unlock(&bnad->conf_mutex);
return -EINVAL;
}
if (ringparam->tx_pending < BNAD_MIN_Q_DEPTH ||
- ringparam->tx_pending > BNAD_MAX_Q_DEPTH ||
+ ringparam->tx_pending > BNAD_MAX_TXQ_DEPTH ||
!BNA_POWER_OF_2(ringparam->tx_pending)) {
mutex_unlock(&bnad->conf_mutex);
return -EINVAL;
@@ -747,6 +455,11 @@ bnad_set_ringparam(struct net_device *netdev,
if (ringparam->rx_pending != bnad->rxq_depth) {
bnad->rxq_depth = ringparam->rx_pending;
+ if (!netif_running(netdev)) {
+ mutex_unlock(&bnad->conf_mutex);
+ return 0;
+ }
+
for (i = 0; i < bnad->num_rx; i++) {
if (!bnad->rx_info[i].rx)
continue;
@@ -755,9 +468,26 @@ bnad_set_ringparam(struct net_device *netdev,
if (current_err && !err)
err = current_err;
}
+
+ if (!err && bnad->rx_info[0].rx) {
+ /* restore rx configuration */
+ bnad_restore_vlans(bnad, 0);
+ bnad_enable_default_bcast(bnad);
+ spin_lock_irqsave(&bnad->bna_lock, flags);
+ bnad_mac_addr_set_locked(bnad, netdev->dev_addr);
+ spin_unlock_irqrestore(&bnad->bna_lock, flags);
+ bnad->cfg_flags &= ~(BNAD_CF_ALLMULTI |
+ BNAD_CF_PROMISC);
+ bnad_set_rx_mode(netdev);
+ }
}
if (ringparam->tx_pending != bnad->txq_depth) {
bnad->txq_depth = ringparam->tx_pending;
+ if (!netif_running(netdev)) {
+ mutex_unlock(&bnad->conf_mutex);
+ return 0;
+ }
+
for (i = 0; i < bnad->num_tx; i++) {
if (!bnad->tx_info[i].tx)
continue;
@@ -779,8 +509,8 @@ bnad_get_pauseparam(struct net_device *netdev,
struct bnad *bnad = netdev_priv(netdev);
pauseparam->autoneg = 0;
- pauseparam->rx_pause = bnad->bna.port.pause_config.rx_pause;
- pauseparam->tx_pause = bnad->bna.port.pause_config.tx_pause;
+ pauseparam->rx_pause = bnad->bna.enet.pause_config.rx_pause;
+ pauseparam->tx_pause = bnad->bna.enet.pause_config.tx_pause;
}
static int
@@ -795,12 +525,12 @@ bnad_set_pauseparam(struct net_device *netdev,
return -EINVAL;
mutex_lock(&bnad->conf_mutex);
- if (pauseparam->rx_pause != bnad->bna.port.pause_config.rx_pause ||
- pauseparam->tx_pause != bnad->bna.port.pause_config.tx_pause) {
+ if (pauseparam->rx_pause != bnad->bna.enet.pause_config.rx_pause ||
+ pauseparam->tx_pause != bnad->bna.enet.pause_config.tx_pause) {
pause_config.rx_pause = pauseparam->rx_pause;
pause_config.tx_pause = pauseparam->tx_pause;
spin_lock_irqsave(&bnad->bna_lock, flags);
- bna_port_pause_config(&bnad->bna.port, &pause_config, NULL);
+ bna_enet_pause_config(&bnad->bna.enet, &pause_config, NULL);
spin_unlock_irqrestore(&bnad->bna_lock, flags);
}
mutex_unlock(&bnad->conf_mutex);
@@ -812,7 +542,7 @@ bnad_get_strings(struct net_device *netdev, u32 stringset, u8 * string)
{
struct bnad *bnad = netdev_priv(netdev);
int i, j, q_num;
- u64 bmap;
+ u32 bmap;
mutex_lock(&bnad->conf_mutex);
@@ -825,9 +555,8 @@ bnad_get_strings(struct net_device *netdev, u32 stringset, u8 * string)
ETH_GSTRING_LEN);
string += ETH_GSTRING_LEN;
}
- bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
- ((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
- for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+ bmap = bna_tx_rid_mask(&bnad->bna);
+ for (i = 0; bmap; i++) {
if (bmap & 1) {
sprintf(string, "txf%d_ucast_octets", i);
string += ETH_GSTRING_LEN;
@@ -857,9 +586,8 @@ bnad_get_strings(struct net_device *netdev, u32 stringset, u8 * string)
bmap >>= 1;
}
- bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
- ((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
- for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+ bmap = bna_rx_rid_mask(&bnad->bna);
+ for (i = 0; bmap; i++) {
if (bmap & 1) {
sprintf(string, "rxf%d_ucast_octets", i);
string += ETH_GSTRING_LEN;
@@ -897,6 +625,16 @@ bnad_get_strings(struct net_device *netdev, u32 stringset, u8 * string)
sprintf(string, "cq%d_hw_producer_index",
q_num);
string += ETH_GSTRING_LEN;
+ sprintf(string, "cq%d_intr", q_num);
+ string += ETH_GSTRING_LEN;
+ sprintf(string, "cq%d_poll", q_num);
+ string += ETH_GSTRING_LEN;
+ sprintf(string, "cq%d_schedule", q_num);
+ string += ETH_GSTRING_LEN;
+ sprintf(string, "cq%d_keep_poll", q_num);
+ string += ETH_GSTRING_LEN;
+ sprintf(string, "cq%d_complete", q_num);
+ string += ETH_GSTRING_LEN;
q_num++;
}
}
@@ -979,19 +717,17 @@ static int
bnad_get_stats_count_locked(struct net_device *netdev)
{
struct bnad *bnad = netdev_priv(netdev);
- int i, j, count, rxf_active_num = 0, txf_active_num = 0;
- u64 bmap;
+ int i, j, count = 0, rxf_active_num = 0, txf_active_num = 0;
+ u32 bmap;
- bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
- ((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
- for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+ bmap = bna_tx_rid_mask(&bnad->bna);
+ for (i = 0; bmap; i++) {
if (bmap & 1)
txf_active_num++;
bmap >>= 1;
}
- bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
- ((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
- for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+ bmap = bna_rx_rid_mask(&bnad->bna);
+ for (i = 0; bmap; i++) {
if (bmap & 1)
rxf_active_num++;
bmap >>= 1;
@@ -1039,6 +775,17 @@ bnad_per_q_stats_fill(struct bnad *bnad, u64 *buf, int bi)
buf[bi++] = 0; /* ccb->consumer_index */
buf[bi++] = *(bnad->rx_info[i].rx_ctrl[j].
ccb->hw_producer_index);
+
+ buf[bi++] = bnad->rx_info[i].
+ rx_ctrl[j].rx_intr_ctr;
+ buf[bi++] = bnad->rx_info[i].
+ rx_ctrl[j].rx_poll_ctr;
+ buf[bi++] = bnad->rx_info[i].
+ rx_ctrl[j].rx_schedule;
+ buf[bi++] = bnad->rx_info[i].
+ rx_ctrl[j].rx_keep_poll;
+ buf[bi++] = bnad->rx_info[i].
+ rx_ctrl[j].rx_complete;
}
}
for (i = 0; i < bnad->num_rx; i++) {
@@ -1104,7 +851,7 @@ bnad_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats,
unsigned long flags;
struct rtnl_link_stats64 *net_stats64;
u64 *stats64;
- u64 bmap;
+ u32 bmap;
mutex_lock(&bnad->conf_mutex);
if (bnad_get_stats_count_locked(netdev) != stats->n_stats) {
@@ -1135,20 +882,20 @@ bnad_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats,
buf[bi++] = stats64[i];
/* Fill hardware stats excluding the rxf/txf into ethtool bufs */
- stats64 = (u64 *) bnad->stats.bna_stats->hw_stats;
+ stats64 = (u64 *) &bnad->stats.bna_stats->hw_stats;
for (i = 0;
- i < offsetof(struct bfi_ll_stats, rxf_stats[0]) / sizeof(u64);
+ i < offsetof(struct bfi_enet_stats, rxf_stats[0]) /
+ sizeof(u64);
i++)
buf[bi++] = stats64[i];
/* Fill txf stats into ethtool buffers */
- bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
- ((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
- for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+ bmap = bna_tx_rid_mask(&bnad->bna);
+ for (i = 0; bmap; i++) {
if (bmap & 1) {
stats64 = (u64 *)&bnad->stats.bna_stats->
- hw_stats->txf_stats[i];
- for (j = 0; j < sizeof(struct bfi_ll_stats_txf) /
+ hw_stats.txf_stats[i];
+ for (j = 0; j < sizeof(struct bfi_enet_stats_txf) /
sizeof(u64); j++)
buf[bi++] = stats64[j];
}
@@ -1156,13 +903,12 @@ bnad_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats,
}
/* Fill rxf stats into ethtool buffers */
- bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
- ((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
- for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+ bmap = bna_rx_rid_mask(&bnad->bna);
+ for (i = 0; bmap; i++) {
if (bmap & 1) {
stats64 = (u64 *)&bnad->stats.bna_stats->
- hw_stats->rxf_stats[i];
- for (j = 0; j < sizeof(struct bfi_ll_stats_rxf) /
+ hw_stats.rxf_stats[i];
+ for (j = 0; j < sizeof(struct bfi_enet_stats_rxf) /
sizeof(u64); j++)
buf[bi++] = stats64[j];
}
@@ -1192,8 +938,6 @@ static struct ethtool_ops bnad_ethtool_ops = {
.get_settings = bnad_get_settings,
.set_settings = bnad_set_settings,
.get_drvinfo = bnad_get_drvinfo,
- .get_regs_len = bnad_get_regs_len,
- .get_regs = bnad_get_regs,
.get_wol = bnad_get_wol,
.get_link = ethtool_op_get_link,
.get_coalesce = bnad_get_coalesce,
diff --git a/drivers/net/bna/cna.h b/drivers/net/ethernet/brocade/bna/cna.h
index bbd39dc65972..1b3e90dfbd9a 100644
--- a/drivers/net/bna/cna.h
+++ b/drivers/net/ethernet/brocade/bna/cna.h
@@ -19,29 +19,26 @@
#ifndef __CNA_H__
#define __CNA_H__
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
+#include <linux/if_vlan.h>
#include <linux/if_ether.h>
-#include <asm/page.h>
-#include <asm/io.h>
-#include <asm/string.h>
-#include <linux/list.h>
-
-#define bfa_sm_fault(__mod, __event) do { \
- pr_err("SM Assertion failure: %s: %d: event = %d", __FILE__, __LINE__, \
- __event); \
+#define bfa_sm_fault(__event) do { \
+ pr_err("SM Assertion failure: %s: %d: event = %d\n", \
+ __FILE__, __LINE__, __event); \
} while (0)
extern char bfa_version[];
-#define CNA_FW_FILE_CT "ctfw_cna.bin"
+#define CNA_FW_FILE_CT "ctfw.bin"
+#define CNA_FW_FILE_CT2 "ct2fw.bin"
#define FC_SYMNAME_MAX 256 /*!< max name server symbolic name size */
#pragma pack(1)
@@ -74,8 +71,37 @@ typedef struct mac { u8 mac[MAC_ADDRLEN]; } mac_t;
bfa_q_next(_q) = bfa_q_next(*((struct list_head **) _qe)); \
bfa_q_qe_init(*((struct list_head **) _qe)); \
} else { \
+ *((struct list_head **)(_qe)) = NULL; \
+ } \
+}
+
+/*
+ * bfa_q_deq_tail - dequeue an element from tail of the queue
+ */
+#define bfa_q_deq_tail(_q, _qe) { \
+ if (!list_empty(_q)) { \
+ *((struct list_head **) (_qe)) = bfa_q_prev(_q); \
+ bfa_q_next(bfa_q_prev(*((struct list_head **) _qe))) = \
+ (struct list_head *) (_q); \
+ bfa_q_prev(_q) = bfa_q_prev(*(struct list_head **) _qe);\
+ bfa_q_qe_init(*((struct list_head **) _qe)); \
+ } else { \
*((struct list_head **) (_qe)) = (struct list_head *) NULL; \
} \
}
+/*
+ * bfa_add_tail_head - enqueue an element at the head of queue
+ */
+#define bfa_q_enq_head(_q, _qe) { \
+ if (!(bfa_q_next(_qe) == NULL) && (bfa_q_prev(_qe) == NULL)) \
+ pr_err("Assertion failure: %s:%d: %d", \
+ __FILE__, __LINE__, \
+ (bfa_q_next(_qe) == NULL) && (bfa_q_prev(_qe) == NULL));\
+ bfa_q_next(_qe) = bfa_q_next(_q); \
+ bfa_q_prev(_qe) = (struct list_head *) (_q); \
+ bfa_q_prev(bfa_q_next(_q)) = (struct list_head *) (_qe); \
+ bfa_q_next(_q) = (struct list_head *) (_qe); \
+}
+
#endif /* __CNA_H__ */
diff --git a/drivers/net/bna/cna_fwimg.c b/drivers/net/ethernet/brocade/bna/cna_fwimg.c
index e8f4ecd9ebb5..725b9fff337f 100644
--- a/drivers/net/bna/cna_fwimg.c
+++ b/drivers/net/ethernet/brocade/bna/cna_fwimg.c
@@ -16,11 +16,12 @@
* www.brocade.com
*/
#include <linux/firmware.h>
+#include "bfi.h"
#include "cna.h"
const struct firmware *bfi_fw;
-static u32 *bfi_image_ct_cna;
-static u32 bfi_image_ct_cna_size;
+static u32 *bfi_image_ct_cna, *bfi_image_ct2_cna;
+static u32 bfi_image_ct_cna_size, bfi_image_ct2_cna_size;
static u32 *
cna_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
@@ -45,20 +46,47 @@ error:
u32 *
cna_get_firmware_buf(struct pci_dev *pdev)
{
- if (bfi_image_ct_cna_size == 0)
- cna_read_firmware(pdev, &bfi_image_ct_cna,
- &bfi_image_ct_cna_size, CNA_FW_FILE_CT);
- return bfi_image_ct_cna;
+ if (pdev->device == BFA_PCI_DEVICE_ID_CT2) {
+ if (bfi_image_ct2_cna_size == 0)
+ cna_read_firmware(pdev, &bfi_image_ct2_cna,
+ &bfi_image_ct2_cna_size, CNA_FW_FILE_CT2);
+ return bfi_image_ct2_cna;
+ } else if (bfa_asic_id_ct(pdev->device)) {
+ if (bfi_image_ct_cna_size == 0)
+ cna_read_firmware(pdev, &bfi_image_ct_cna,
+ &bfi_image_ct_cna_size, CNA_FW_FILE_CT);
+ return bfi_image_ct_cna;
+ }
+
+ return NULL;
}
u32 *
-bfa_cb_image_get_chunk(int type, u32 off)
+bfa_cb_image_get_chunk(enum bfi_asic_gen asic_gen, u32 off)
{
- return (u32 *)(bfi_image_ct_cna + off);
+ switch (asic_gen) {
+ case BFI_ASIC_GEN_CT:
+ return (u32 *)(bfi_image_ct_cna + off);
+ break;
+ case BFI_ASIC_GEN_CT2:
+ return (u32 *)(bfi_image_ct2_cna + off);
+ break;
+ default:
+ return NULL;
+ }
}
u32
-bfa_cb_image_get_size(int type)
+bfa_cb_image_get_size(enum bfi_asic_gen asic_gen)
{
- return bfi_image_ct_cna_size;
+ switch (asic_gen) {
+ case BFI_ASIC_GEN_CT:
+ return bfi_image_ct_cna_size;
+ break;
+ case BFI_ASIC_GEN_CT2:
+ return bfi_image_ct2_cna_size;
+ break;
+ default:
+ return 0;
+ }
}
diff --git a/drivers/net/ethernet/cadence/Kconfig b/drivers/net/ethernet/cadence/Kconfig
new file mode 100644
index 000000000000..b48378a41e49
--- /dev/null
+++ b/drivers/net/ethernet/cadence/Kconfig
@@ -0,0 +1,46 @@
+#
+# Atmel device configuration
+#
+
+config HAVE_NET_MACB
+ bool
+
+config NET_ATMEL
+ bool "Atmel devices"
+ default y
+ depends on HAVE_NET_MACB || (ARM && ARCH_AT91RM9200)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y.
+ Make sure you know the name of your card. Read the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ If unsure, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the remaining Atmel network card questions. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_ATMEL
+
+config ARM_AT91_ETHER
+ tristate "AT91RM9200 Ethernet support"
+ depends on ARM && ARCH_AT91RM9200
+ select NET_CORE
+ select MII
+ ---help---
+ If you wish to compile a kernel for the AT91RM9200 and enable
+ ethernet support, then you should always answer Y to this.
+
+config MACB
+ tristate "Atmel MACB support"
+ depends on HAVE_NET_MACB
+ select PHYLIB
+ ---help---
+ The Atmel MACB ethernet interface is found on many AT32 and AT91
+ parts. Say Y to include support for the MACB chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called macb.
+
+endif # NET_ATMEL
diff --git a/drivers/net/ethernet/cadence/Makefile b/drivers/net/ethernet/cadence/Makefile
new file mode 100644
index 000000000000..9068b8331ed1
--- /dev/null
+++ b/drivers/net/ethernet/cadence/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Atmel network device drivers.
+#
+
+obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
+obj-$(CONFIG_MACB) += macb.o
diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c
index e07b314ed8fd..56624d303487 100644
--- a/drivers/net/arm/at91_ether.c
+++ b/drivers/net/ethernet/cadence/at91_ether.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/mii.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -34,7 +35,7 @@
#include <asm/mach-types.h>
#include <mach/at91rm9200_emac.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#include <mach/board.h>
#include "at91_ether.h"
@@ -967,7 +968,7 @@ static const struct net_device_ops at91ether_netdev_ops = {
.ndo_stop = at91ether_close,
.ndo_start_xmit = at91ether_start_xmit,
.ndo_get_stats = at91ether_stats,
- .ndo_set_multicast_list = at91ether_set_multicast_list,
+ .ndo_set_rx_mode = at91ether_set_multicast_list,
.ndo_set_mac_address = set_mac_address,
.ndo_do_ioctl = at91ether_ioctl,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/arm/at91_ether.h b/drivers/net/ethernet/cadence/at91_ether.h
index 353f4dab62be..353f4dab62be 100644
--- a/drivers/net/arm/at91_ether.h
+++ b/drivers/net/ethernet/cadence/at91_ether.h
diff --git a/drivers/net/macb.c b/drivers/net/ethernet/cadence/macb.c
index 6c6a02869dfc..a437b46e5490 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -15,6 +15,7 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/dma-mapping.h>
@@ -321,6 +322,9 @@ static void macb_tx(struct macb *bp)
for (i = 0; i < TX_RING_SIZE; i++)
bp->tx_ring[i].ctrl = MACB_BIT(TX_USED);
+ /* Add wrap bit */
+ bp->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP);
+
/* free transmit buffer in upper layer*/
for (tail = bp->tx_tail; tail != head; tail = NEXT_TX(tail)) {
struct ring_info *rp = &bp->tx_skb[tail];
@@ -669,6 +673,8 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
entry = NEXT_TX(entry);
bp->tx_head = entry;
+ skb_tx_timestamp(skb);
+
macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
if (TX_BUFFS_AVAIL(bp) < 1)
@@ -1100,7 +1106,7 @@ static const struct net_device_ops macb_netdev_ops = {
.ndo_open = macb_open,
.ndo_stop = macb_close,
.ndo_start_xmit = macb_start_xmit,
- .ndo_set_multicast_list = macb_set_rx_mode,
+ .ndo_set_rx_mode = macb_set_rx_mode,
.ndo_get_stats = macb_get_stats,
.ndo_do_ioctl = macb_ioctl,
.ndo_validate_addr = eth_validate_addr,
@@ -1169,7 +1175,7 @@ static int __init macb_probe(struct platform_device *pdev)
clk_enable(bp->hclk);
#endif
- bp->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ bp->regs = ioremap(regs->start, resource_size(regs));
if (!bp->regs) {
dev_err(&pdev->dev, "failed to map registers, aborting.\n");
err = -ENOMEM;
diff --git a/drivers/net/macb.h b/drivers/net/ethernet/cadence/macb.h
index d3212f6db703..d3212f6db703 100644
--- a/drivers/net/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
diff --git a/drivers/net/ethernet/chelsio/Kconfig b/drivers/net/ethernet/chelsio/Kconfig
new file mode 100644
index 000000000000..2de50f95798f
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/Kconfig
@@ -0,0 +1,107 @@
+#
+# Chelsio device configuration
+#
+
+config NET_VENDOR_CHELSIO
+ bool "Chelsio devices"
+ default y
+ depends on PCI || INET
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Chelsio devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_CHELSIO
+
+config CHELSIO_T1
+ tristate "Chelsio 10Gb Ethernet support"
+ depends on PCI
+ select CRC32
+ select MDIO
+ ---help---
+ This driver supports Chelsio gigabit and 10-gigabit
+ Ethernet cards. More information about adapter features and
+ performance tuning is in <file:Documentation/networking/cxgb.txt>.
+
+ For general information about Chelsio and our products, visit
+ our website at <http://www.chelsio.com>.
+
+ For customer support, please visit our customer support page at
+ <http://www.chelsio.com/support.html>.
+
+ Please send feedback to <linux-bugs@chelsio.com>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called cxgb.
+
+config CHELSIO_T1_1G
+ bool "Chelsio gigabit Ethernet support"
+ depends on CHELSIO_T1
+ ---help---
+ Enables support for Chelsio's gigabit Ethernet PCI cards. If you
+ are using only 10G cards say 'N' here.
+
+config CHELSIO_T3
+ tristate "Chelsio Communications T3 10Gb Ethernet support"
+ depends on PCI && INET
+ select FW_LOADER
+ select MDIO
+ ---help---
+ This driver supports Chelsio T3-based gigabit and 10Gb Ethernet
+ adapters.
+
+ For general information about Chelsio and our products, visit
+ our website at <http://www.chelsio.com>.
+
+ For customer support, please visit our customer support page at
+ <http://www.chelsio.com/support.html>.
+
+ Please send feedback to <linux-bugs@chelsio.com>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called cxgb3.
+
+config CHELSIO_T4
+ tristate "Chelsio Communications T4 Ethernet support"
+ depends on PCI
+ select FW_LOADER
+ select MDIO
+ ---help---
+ This driver supports Chelsio T4-based gigabit and 10Gb Ethernet
+ adapters.
+
+ For general information about Chelsio and our products, visit
+ our website at <http://www.chelsio.com>.
+
+ For customer support, please visit our customer support page at
+ <http://www.chelsio.com/support.html>.
+
+ Please send feedback to <linux-bugs@chelsio.com>.
+
+ To compile this driver as a module choose M here; the module
+ will be called cxgb4.
+
+config CHELSIO_T4VF
+ tristate "Chelsio Communications T4 Virtual Function Ethernet support"
+ depends on PCI
+ ---help---
+ This driver supports Chelsio T4-based gigabit and 10Gb Ethernet
+ adapters with PCI-E SR-IOV Virtual Functions.
+
+ For general information about Chelsio and our products, visit
+ our website at <http://www.chelsio.com>.
+
+ For customer support, please visit our customer support page at
+ <http://www.chelsio.com/support.html>.
+
+ Please send feedback to <linux-bugs@chelsio.com>.
+
+ To compile this driver as a module choose M here; the module
+ will be called cxgb4vf.
+
+endif # NET_VENDOR_CHELSIO
diff --git a/drivers/net/ethernet/chelsio/Makefile b/drivers/net/ethernet/chelsio/Makefile
new file mode 100644
index 000000000000..390510b5e90f
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the Chelsio network device drivers.
+#
+
+obj-$(CONFIG_CHELSIO_T1) += cxgb/
+obj-$(CONFIG_CHELSIO_T3) += cxgb3/
+obj-$(CONFIG_CHELSIO_T4) += cxgb4/
+obj-$(CONFIG_CHELSIO_T4VF) += cxgb4vf/
diff --git a/drivers/net/chelsio/Makefile b/drivers/net/ethernet/chelsio/cxgb/Makefile
index 57a4b262fd3f..57a4b262fd3f 100644
--- a/drivers/net/chelsio/Makefile
+++ b/drivers/net/ethernet/chelsio/cxgb/Makefile
diff --git a/drivers/net/chelsio/common.h b/drivers/net/ethernet/chelsio/cxgb/common.h
index c26d863e1697..5ccbed1784d2 100644
--- a/drivers/net/chelsio/common.h
+++ b/drivers/net/ethernet/chelsio/cxgb/common.h
@@ -240,8 +240,6 @@ struct adapter {
struct work_struct ext_intr_handler_task;
struct adapter_params params;
- struct vlan_group *vlan_grp;
-
/* Terminator modules. */
struct sge *sge;
struct peespi *espi;
diff --git a/drivers/net/chelsio/cphy.h b/drivers/net/ethernet/chelsio/cxgb/cphy.h
index 1f095a9fc739..1f095a9fc739 100644
--- a/drivers/net/chelsio/cphy.h
+++ b/drivers/net/ethernet/chelsio/cxgb/cphy.h
diff --git a/drivers/net/chelsio/cpl5_cmd.h b/drivers/net/ethernet/chelsio/cxgb/cpl5_cmd.h
index e36d45b78cc7..e36d45b78cc7 100644
--- a/drivers/net/chelsio/cpl5_cmd.h
+++ b/drivers/net/ethernet/chelsio/cxgb/cpl5_cmd.h
diff --git a/drivers/net/chelsio/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c
index b422d83f5343..ca26d97171bd 100644
--- a/drivers/net/chelsio/cxgb2.c
+++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c
@@ -263,6 +263,8 @@ static int cxgb_open(struct net_device *dev)
if (!other_ports && adapter->params.stats_update_period)
schedule_mac_stats_update(adapter,
adapter->params.stats_update_period);
+
+ t1_vlan_mode(adapter, dev->features);
return 0;
}
@@ -710,12 +712,10 @@ static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
e->rx_max_pending = MAX_RX_BUFFERS;
- e->rx_mini_max_pending = 0;
e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS;
e->tx_max_pending = MAX_CMDQ_ENTRIES;
e->rx_pending = adapter->params.sge.freelQ_size[!jumbo_fl];
- e->rx_mini_pending = 0;
e->rx_jumbo_pending = adapter->params.sge.freelQ_size[jumbo_fl];
e->tx_pending = adapter->params.sge.cmdQ_size[0];
}
@@ -849,19 +849,30 @@ static int t1_set_mac_addr(struct net_device *dev, void *p)
return 0;
}
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
-static void t1_vlan_rx_register(struct net_device *dev,
- struct vlan_group *grp)
+static u32 t1_fix_features(struct net_device *dev, u32 features)
{
- struct adapter *adapter = dev->ml_priv;
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
- spin_lock_irq(&adapter->async_lock);
- adapter->vlan_grp = grp;
- t1_set_vlan_accel(adapter, grp != NULL);
- spin_unlock_irq(&adapter->async_lock);
+ return features;
}
-#endif
+static int t1_set_features(struct net_device *dev, u32 features)
+{
+ u32 changed = dev->features ^ features;
+ struct adapter *adapter = dev->ml_priv;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ t1_vlan_mode(adapter, features);
+
+ return 0;
+}
#ifdef CONFIG_NET_POLL_CONTROLLER
static void t1_netpoll(struct net_device *dev)
{
@@ -951,13 +962,12 @@ static const struct net_device_ops cxgb_netdev_ops = {
.ndo_start_xmit = t1_start_xmit,
.ndo_get_stats = t1_get_stats,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = t1_set_rxmode,
+ .ndo_set_rx_mode = t1_set_rxmode,
.ndo_do_ioctl = t1_ioctl,
.ndo_change_mtu = t1_change_mtu,
.ndo_set_mac_address = t1_set_mac_addr,
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
- .ndo_vlan_rx_register = t1_vlan_rx_register,
-#endif
+ .ndo_fix_features = t1_fix_features,
+ .ndo_set_features = t1_set_features,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = t1_netpoll,
#endif
@@ -1080,10 +1090,9 @@ static int __devinit init_one(struct pci_dev *pdev,
if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA;
if (vlan_tso_capable(adapter)) {
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
netdev->features |=
NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-#endif
+ netdev->hw_features |= NETIF_F_HW_VLAN_RX;
/* T204: disable TSO */
if (!(is_T2(adapter)) || bi->port_number != 4) {
diff --git a/drivers/net/chelsio/elmer0.h b/drivers/net/ethernet/chelsio/cxgb/elmer0.h
index eef655c827d9..eef655c827d9 100644
--- a/drivers/net/chelsio/elmer0.h
+++ b/drivers/net/ethernet/chelsio/cxgb/elmer0.h
diff --git a/drivers/net/chelsio/espi.c b/drivers/net/ethernet/chelsio/cxgb/espi.c
index 639ff1955739..639ff1955739 100644
--- a/drivers/net/chelsio/espi.c
+++ b/drivers/net/ethernet/chelsio/cxgb/espi.c
diff --git a/drivers/net/chelsio/espi.h b/drivers/net/ethernet/chelsio/cxgb/espi.h
index 5694aad4fbc0..5694aad4fbc0 100644
--- a/drivers/net/chelsio/espi.h
+++ b/drivers/net/ethernet/chelsio/cxgb/espi.h
diff --git a/drivers/net/chelsio/fpga_defs.h b/drivers/net/ethernet/chelsio/cxgb/fpga_defs.h
index ccdb2bc9ae98..ccdb2bc9ae98 100644
--- a/drivers/net/chelsio/fpga_defs.h
+++ b/drivers/net/ethernet/chelsio/cxgb/fpga_defs.h
diff --git a/drivers/net/chelsio/gmac.h b/drivers/net/ethernet/chelsio/cxgb/gmac.h
index d42337457cf7..d42337457cf7 100644
--- a/drivers/net/chelsio/gmac.h
+++ b/drivers/net/ethernet/chelsio/cxgb/gmac.h
diff --git a/drivers/net/chelsio/mv88e1xxx.c b/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c
index 71018a4fdf15..71018a4fdf15 100644
--- a/drivers/net/chelsio/mv88e1xxx.c
+++ b/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c
diff --git a/drivers/net/chelsio/mv88e1xxx.h b/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.h
index 967cc4286359..967cc4286359 100644
--- a/drivers/net/chelsio/mv88e1xxx.h
+++ b/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.h
diff --git a/drivers/net/chelsio/mv88x201x.c b/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c
index f7136b2fd1e5..f7136b2fd1e5 100644
--- a/drivers/net/chelsio/mv88x201x.c
+++ b/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c
diff --git a/drivers/net/chelsio/my3126.c b/drivers/net/ethernet/chelsio/cxgb/my3126.c
index a683fd3bb624..a683fd3bb624 100644
--- a/drivers/net/chelsio/my3126.c
+++ b/drivers/net/ethernet/chelsio/cxgb/my3126.c
diff --git a/drivers/net/chelsio/pm3393.c b/drivers/net/ethernet/chelsio/cxgb/pm3393.c
index 40c7b93ababc..40c7b93ababc 100644
--- a/drivers/net/chelsio/pm3393.c
+++ b/drivers/net/ethernet/chelsio/cxgb/pm3393.c
diff --git a/drivers/net/chelsio/regs.h b/drivers/net/ethernet/chelsio/cxgb/regs.h
index c80bf4d6d0a6..c80bf4d6d0a6 100644
--- a/drivers/net/chelsio/regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb/regs.h
diff --git a/drivers/net/chelsio/sge.c b/drivers/net/ethernet/chelsio/cxgb/sge.c
index 58380d240619..f9b602300040 100644
--- a/drivers/net/chelsio/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb/sge.c
@@ -742,13 +742,14 @@ static inline void setup_ring_params(struct adapter *adapter, u64 addr,
/*
* Enable/disable VLAN acceleration.
*/
-void t1_set_vlan_accel(struct adapter *adapter, int on_off)
+void t1_vlan_mode(struct adapter *adapter, u32 features)
{
struct sge *sge = adapter->sge;
- sge->sge_control &= ~F_VLAN_XTRACT;
- if (on_off)
+ if (features & NETIF_F_HW_VLAN_RX)
sge->sge_control |= F_VLAN_XTRACT;
+ else
+ sge->sge_control &= ~F_VLAN_XTRACT;
if (adapter->open_device_map) {
writel(sge->sge_control, adapter->regs + A_SG_CONTROL);
readl(adapter->regs + A_SG_CONTROL); /* flush */
@@ -1134,8 +1135,8 @@ static inline unsigned int compute_large_page_tx_descs(struct sk_buff *skb)
len -= SGE_TX_DESC_MAX_PLEN;
}
for (i = 0; nfrags--; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- len = frag->size;
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ len = skb_frag_size(frag);
while (len > SGE_TX_DESC_MAX_PLEN) {
count++;
len -= SGE_TX_DESC_MAX_PLEN;
@@ -1276,11 +1277,10 @@ static inline void write_tx_descs(struct adapter *adapter, struct sk_buff *skb,
ce = q->centries;
}
- mapping = pci_map_page(adapter->pdev, frag->page,
- frag->page_offset, frag->size,
- PCI_DMA_TODEVICE);
+ mapping = skb_frag_dma_map(&adapter->pdev->dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
desc_mapping = mapping;
- desc_len = frag->size;
+ desc_len = skb_frag_size(frag);
pidx = write_large_page_tx_descs(pidx, &e1, &ce, &gen,
&desc_mapping, &desc_len,
@@ -1290,7 +1290,7 @@ static inline void write_tx_descs(struct adapter *adapter, struct sk_buff *skb,
nfrags == 0);
ce->skb = NULL;
dma_unmap_addr_set(ce, dma_addr, mapping);
- dma_unmap_len_set(ce, dma_len, frag->size);
+ dma_unmap_len_set(ce, dma_len, skb_frag_size(frag));
}
ce->skb = skb;
wmb();
@@ -1397,12 +1397,11 @@ static void sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len)
} else
skb_checksum_none_assert(skb);
- if (unlikely(adapter->vlan_grp && p->vlan_valid)) {
+ if (p->vlan_valid) {
st->vlan_xtract++;
- vlan_hwaccel_receive_skb(skb, adapter->vlan_grp,
- ntohs(p->vlan));
- } else
- netif_receive_skb(skb);
+ __vlan_hwaccel_put_tag(skb, ntohs(p->vlan));
+ }
+ netif_receive_skb(skb);
}
/*
@@ -1875,13 +1874,11 @@ netdev_tx_t t1_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
cpl->iff = dev->if_port;
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
if (vlan_tx_tag_present(skb)) {
cpl->vlan_valid = 1;
cpl->vlan = htons(vlan_tx_tag_get(skb));
st->vlan_insert++;
} else
-#endif
cpl->vlan_valid = 0;
send:
diff --git a/drivers/net/chelsio/sge.h b/drivers/net/ethernet/chelsio/cxgb/sge.h
index 00cc37fc1f6f..e03980bcdd65 100644
--- a/drivers/net/chelsio/sge.h
+++ b/drivers/net/ethernet/chelsio/cxgb/sge.h
@@ -79,7 +79,7 @@ irqreturn_t t1_interrupt(int irq, void *cookie);
int t1_poll(struct napi_struct *, int);
netdev_tx_t t1_start_xmit(struct sk_buff *skb, struct net_device *dev);
-void t1_set_vlan_accel(struct adapter *adapter, int on_off);
+void t1_vlan_mode(struct adapter *adapter, u32 features);
void t1_sge_start(struct sge *);
void t1_sge_stop(struct sge *);
int t1_sge_intr_error_handler(struct sge *);
diff --git a/drivers/net/chelsio/subr.c b/drivers/net/ethernet/chelsio/cxgb/subr.c
index 8a43c7e19701..8a43c7e19701 100644
--- a/drivers/net/chelsio/subr.c
+++ b/drivers/net/ethernet/chelsio/cxgb/subr.c
diff --git a/drivers/net/chelsio/suni1x10gexp_regs.h b/drivers/net/ethernet/chelsio/cxgb/suni1x10gexp_regs.h
index d0f87d82566a..d0f87d82566a 100644
--- a/drivers/net/chelsio/suni1x10gexp_regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb/suni1x10gexp_regs.h
diff --git a/drivers/net/chelsio/tp.c b/drivers/net/ethernet/chelsio/cxgb/tp.c
index 8bed4a59e65f..8bed4a59e65f 100644
--- a/drivers/net/chelsio/tp.c
+++ b/drivers/net/ethernet/chelsio/cxgb/tp.c
diff --git a/drivers/net/chelsio/tp.h b/drivers/net/ethernet/chelsio/cxgb/tp.h
index dfd8ce25106a..dfd8ce25106a 100644
--- a/drivers/net/chelsio/tp.h
+++ b/drivers/net/ethernet/chelsio/cxgb/tp.h
diff --git a/drivers/net/chelsio/vsc7326.c b/drivers/net/ethernet/chelsio/cxgb/vsc7326.c
index b0cb388f5e12..b0cb388f5e12 100644
--- a/drivers/net/chelsio/vsc7326.c
+++ b/drivers/net/ethernet/chelsio/cxgb/vsc7326.c
diff --git a/drivers/net/chelsio/vsc7326_reg.h b/drivers/net/ethernet/chelsio/cxgb/vsc7326_reg.h
index 479edbcabe68..479edbcabe68 100644
--- a/drivers/net/chelsio/vsc7326_reg.h
+++ b/drivers/net/ethernet/chelsio/cxgb/vsc7326_reg.h
diff --git a/drivers/net/cxgb3/Makefile b/drivers/net/ethernet/chelsio/cxgb3/Makefile
index 29aff78c7820..29aff78c7820 100644
--- a/drivers/net/cxgb3/Makefile
+++ b/drivers/net/ethernet/chelsio/cxgb3/Makefile
diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/ethernet/chelsio/cxgb3/adapter.h
index 7300de5a1426..8b395b537330 100644
--- a/drivers/net/cxgb3/adapter.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/adapter.h
@@ -45,7 +45,6 @@
#include "t3cdev.h"
#include <asm/io.h>
-struct vlan_group;
struct adapter;
struct sge_qset;
struct port_info;
@@ -66,7 +65,6 @@ struct iscsi_config {
struct port_info {
struct adapter *adapter;
- struct vlan_group *vlan_grp;
struct sge_qset *qs;
u8 port_id;
u8 nqsets;
diff --git a/drivers/net/cxgb3/ael1002.c b/drivers/net/ethernet/chelsio/cxgb3/ael1002.c
index 2028da95afa1..2028da95afa1 100644
--- a/drivers/net/cxgb3/ael1002.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/ael1002.c
diff --git a/drivers/net/cxgb3/aq100x.c b/drivers/net/ethernet/chelsio/cxgb3/aq100x.c
index 341b7ef1508f..341b7ef1508f 100644
--- a/drivers/net/cxgb3/aq100x.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/aq100x.c
diff --git a/drivers/net/cxgb3/common.h b/drivers/net/ethernet/chelsio/cxgb3/common.h
index 056ee8c831f1..df01b6343241 100644
--- a/drivers/net/cxgb3/common.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/common.h
@@ -367,7 +367,6 @@ struct vpd_params {
struct pci_params {
unsigned int vpd_cap_addr;
- unsigned int pcie_cap_addr;
unsigned short speed;
unsigned char width;
unsigned char variant;
diff --git a/drivers/net/cxgb3/cxgb3_ctl_defs.h b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_ctl_defs.h
index 369fe711fd7f..369fe711fd7f 100644
--- a/drivers/net/cxgb3/cxgb3_ctl_defs.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_ctl_defs.h
diff --git a/drivers/net/cxgb3/cxgb3_defs.h b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_defs.h
index 920d918ed193..920d918ed193 100644
--- a/drivers/net/cxgb3/cxgb3_defs.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_defs.h
diff --git a/drivers/net/cxgb3/cxgb3_ioctl.h b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_ioctl.h
index b19e4376ba76..b19e4376ba76 100644
--- a/drivers/net/cxgb3/cxgb3_ioctl.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_ioctl.h
diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
index 9081ce037149..4d15c8f99c3b 100644
--- a/drivers/net/cxgb3/cxgb3_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
@@ -1898,7 +1898,6 @@ static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
const struct qset_params *q = &adapter->params.sge.qset[pi->first_qset];
e->rx_max_pending = MAX_RX_BUFFERS;
- e->rx_mini_max_pending = 0;
e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS;
e->tx_max_pending = MAX_TXQ_ENTRIES;
@@ -2532,25 +2531,51 @@ static void t3_synchronize_rx(struct adapter *adap, const struct port_info *p)
}
}
-static void vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
+static void cxgb_vlan_mode(struct net_device *dev, u32 features)
{
struct port_info *pi = netdev_priv(dev);
struct adapter *adapter = pi->adapter;
- pi->vlan_grp = grp;
- if (adapter->params.rev > 0)
- t3_set_vlan_accel(adapter, 1 << pi->port_id, grp != NULL);
- else {
+ if (adapter->params.rev > 0) {
+ t3_set_vlan_accel(adapter, 1 << pi->port_id,
+ features & NETIF_F_HW_VLAN_RX);
+ } else {
/* single control for all ports */
- unsigned int i, have_vlans = 0;
+ unsigned int i, have_vlans = features & NETIF_F_HW_VLAN_RX;
+
for_each_port(adapter, i)
- have_vlans |= adap2pinfo(adapter, i)->vlan_grp != NULL;
+ have_vlans |=
+ adapter->port[i]->features & NETIF_F_HW_VLAN_RX;
t3_set_vlan_accel(adapter, 1, have_vlans);
}
t3_synchronize_rx(adapter, pi);
}
+static u32 cxgb_fix_features(struct net_device *dev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int cxgb_set_features(struct net_device *dev, u32 features)
+{
+ u32 changed = dev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ cxgb_vlan_mode(dev, features);
+
+ return 0;
+}
+
#ifdef CONFIG_NET_POLL_CONTROLLER
static void cxgb_netpoll(struct net_device *dev)
{
@@ -3127,11 +3152,12 @@ static const struct net_device_ops cxgb_netdev_ops = {
.ndo_start_xmit = t3_eth_xmit,
.ndo_get_stats = cxgb_get_stats,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = cxgb_set_rxmode,
+ .ndo_set_rx_mode = cxgb_set_rxmode,
.ndo_do_ioctl = cxgb_ioctl,
.ndo_change_mtu = cxgb_change_mtu,
.ndo_set_mac_address = cxgb_set_mac_addr,
- .ndo_vlan_rx_register = vlan_rx_register,
+ .ndo_fix_features = cxgb_fix_features,
+ .ndo_set_features = cxgb_set_features,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = cxgb_netpoll,
#endif
@@ -3263,9 +3289,8 @@ static int __devinit init_one(struct pci_dev *pdev,
netdev->mem_start = mmio_start;
netdev->mem_end = mmio_start + mmio_len - 1;
netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
- NETIF_F_TSO | NETIF_F_RXCSUM;
- netdev->features |= netdev->hw_features |
- NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+ NETIF_F_TSO | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_RX;
+ netdev->features |= netdev->hw_features | NETIF_F_HW_VLAN_TX;
if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA;
@@ -3329,6 +3354,9 @@ static int __devinit init_one(struct pci_dev *pdev,
err = sysfs_create_group(&adapter->port[0]->dev.kobj,
&cxgb3_attr_group);
+ for_each_port(adapter, i)
+ cxgb_vlan_mode(adapter->port[i], adapter->port[i]->features);
+
print_port_info(adapter, ai);
return 0;
diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c
index 862804f32b6e..90ff1318cc05 100644
--- a/drivers/net/cxgb3/cxgb3_offload.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c
@@ -34,12 +34,13 @@
#include <linux/slab.h>
#include <net/neighbour.h>
#include <linux/notifier.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/proc_fs.h>
#include <linux/if_vlan.h>
#include <net/netevent.h>
#include <linux/highmem.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include "common.h"
#include "regs.h"
@@ -176,16 +177,13 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter,
int i;
for_each_port(adapter, i) {
- struct vlan_group *grp;
struct net_device *dev = adapter->port[i];
- const struct port_info *p = netdev_priv(dev);
if (!memcmp(dev->dev_addr, mac, ETH_ALEN)) {
if (vlan && vlan != VLAN_VID_MASK) {
- grp = p->vlan_grp;
- dev = NULL;
- if (grp)
- dev = vlan_group_get_device(grp, vlan);
+ rcu_read_lock();
+ dev = __vlan_find_dev_deep(dev, vlan);
+ rcu_read_unlock();
} else if (netif_is_bond_slave(dev)) {
while (dev->master)
dev = dev->master;
@@ -567,7 +565,7 @@ static void t3_process_tid_release_list(struct work_struct *work)
while (td->tid_release_list) {
struct t3c_tid_entry *p = td->tid_release_list;
- td->tid_release_list = (struct t3c_tid_entry *)p->ctx;
+ td->tid_release_list = p->ctx;
spin_unlock_bh(&td->tid_release_lock);
skb = alloc_skb(sizeof(struct cpl_tid_release),
@@ -971,7 +969,7 @@ static int nb_callback(struct notifier_block *self, unsigned long event,
case (NETEVENT_REDIRECT):{
struct netevent_redirect *nr = ctx;
cxgb_redirect(nr->old, nr->new);
- cxgb_neigh_update(nr->new->neighbour);
+ cxgb_neigh_update(dst_get_neighbour(nr->new));
break;
}
default:
@@ -1116,8 +1114,8 @@ static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new)
struct l2t_entry *e;
struct t3c_tid_entry *te;
- olddev = old->neighbour->dev;
- newdev = new->neighbour->dev;
+ olddev = dst_get_neighbour(old)->dev;
+ newdev = dst_get_neighbour(new)->dev;
if (!is_offloading(olddev))
return;
if (!is_offloading(newdev)) {
@@ -1134,7 +1132,7 @@ static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new)
}
/* Add new L2T entry */
- e = t3_l2t_get(tdev, new->neighbour, newdev);
+ e = t3_l2t_get(tdev, dst_get_neighbour(new), newdev);
if (!e) {
printk(KERN_ERR "%s: couldn't allocate new l2t entry!\n",
__func__);
@@ -1149,12 +1147,14 @@ static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new)
if (te && te->ctx && te->client && te->client->redirect) {
update_tcb = te->client->redirect(te->ctx, old, new, e);
if (update_tcb) {
+ rcu_read_lock();
l2t_hold(L2DATA(tdev), e);
+ rcu_read_unlock();
set_l2t_ix(tdev, tid, e);
}
}
}
- l2t_release(L2DATA(tdev), e);
+ l2t_release(tdev, e);
}
/*
@@ -1267,7 +1267,7 @@ int cxgb3_offload_activate(struct adapter *adapter)
goto out_free;
err = -ENOMEM;
- L2DATA(dev) = t3_init_l2t(l2t_capacity);
+ RCU_INIT_POINTER(dev->l2opt, t3_init_l2t(l2t_capacity));
if (!L2DATA(dev))
goto out_free;
@@ -1301,16 +1301,24 @@ int cxgb3_offload_activate(struct adapter *adapter)
out_free_l2t:
t3_free_l2t(L2DATA(dev));
- L2DATA(dev) = NULL;
+ rcu_assign_pointer(dev->l2opt, NULL);
out_free:
kfree(t);
return err;
}
+static void clean_l2_data(struct rcu_head *head)
+{
+ struct l2t_data *d = container_of(head, struct l2t_data, rcu_head);
+ t3_free_l2t(d);
+}
+
+
void cxgb3_offload_deactivate(struct adapter *adapter)
{
struct t3cdev *tdev = &adapter->tdev;
struct t3c_data *t = T3C_DATA(tdev);
+ struct l2t_data *d;
remove_adapter(adapter);
if (list_empty(&adapter_list))
@@ -1318,8 +1326,11 @@ void cxgb3_offload_deactivate(struct adapter *adapter)
free_tid_maps(&t->tid_maps);
T3C_DATA(tdev) = NULL;
- t3_free_l2t(L2DATA(tdev));
- L2DATA(tdev) = NULL;
+ rcu_read_lock();
+ d = L2DATA(tdev);
+ rcu_read_unlock();
+ rcu_assign_pointer(tdev->l2opt, NULL);
+ call_rcu(&d->rcu_head, clean_l2_data);
if (t->nofail_skb)
kfree_skb(t->nofail_skb);
kfree(t);
diff --git a/drivers/net/cxgb3/cxgb3_offload.h b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.h
index 929c298115ca..929c298115ca 100644
--- a/drivers/net/cxgb3/cxgb3_offload.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.h
diff --git a/drivers/net/cxgb3/firmware_exports.h b/drivers/net/ethernet/chelsio/cxgb3/firmware_exports.h
index 0d9b0e6dccff..0d9b0e6dccff 100644
--- a/drivers/net/cxgb3/firmware_exports.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/firmware_exports.h
diff --git a/drivers/net/cxgb3/l2t.c b/drivers/net/ethernet/chelsio/cxgb3/l2t.c
index f452c4003253..70fec8b1140f 100644
--- a/drivers/net/cxgb3/l2t.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/l2t.c
@@ -35,6 +35,7 @@
#include <linux/if_vlan.h>
#include <linux/jhash.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <net/neighbour.h>
#include "common.h"
#include "t3cdev.h"
@@ -300,14 +301,21 @@ static inline void reuse_entry(struct l2t_entry *e, struct neighbour *neigh)
struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct neighbour *neigh,
struct net_device *dev)
{
- struct l2t_entry *e;
- struct l2t_data *d = L2DATA(cdev);
+ struct l2t_entry *e = NULL;
+ struct l2t_data *d;
+ int hash;
u32 addr = *(u32 *) neigh->primary_key;
int ifidx = neigh->dev->ifindex;
- int hash = arp_hash(addr, ifidx, d);
struct port_info *p = netdev_priv(dev);
int smt_idx = p->port_id;
+ rcu_read_lock();
+ d = L2DATA(cdev);
+ if (!d)
+ goto done_rcu;
+
+ hash = arp_hash(addr, ifidx, d);
+
write_lock_bh(&d->lock);
for (e = d->l2tab[hash].first; e; e = e->next)
if (e->addr == addr && e->ifindex == ifidx &&
@@ -338,6 +346,8 @@ struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct neighbour *neigh,
}
done:
write_unlock_bh(&d->lock);
+done_rcu:
+ rcu_read_unlock();
return e;
}
diff --git a/drivers/net/cxgb3/l2t.h b/drivers/net/ethernet/chelsio/cxgb3/l2t.h
index fd3eb07e3f40..c5f54796e2cb 100644
--- a/drivers/net/cxgb3/l2t.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/l2t.h
@@ -34,7 +34,7 @@
#include <linux/spinlock.h>
#include "t3cdev.h"
-#include <asm/atomic.h>
+#include <linux/atomic.h>
enum {
L2T_STATE_VALID, /* entry is up to date */
@@ -76,6 +76,7 @@ struct l2t_data {
atomic_t nfree; /* number of free entries */
rwlock_t lock;
struct l2t_entry l2tab[0];
+ struct rcu_head rcu_head; /* to handle rcu cleanup */
};
typedef void (*arp_failure_handler_func)(struct t3cdev * dev,
@@ -99,7 +100,7 @@ static inline void set_arp_failure_handler(struct sk_buff *skb,
/*
* Getting to the L2 data from an offload device.
*/
-#define L2DATA(dev) ((dev)->l2opt)
+#define L2DATA(cdev) (rcu_dereference((cdev)->l2opt))
#define W_TCB_L2T_IX 0
#define S_TCB_L2T_IX 7
@@ -126,15 +127,22 @@ static inline int l2t_send(struct t3cdev *dev, struct sk_buff *skb,
return t3_l2t_send_slow(dev, skb, e);
}
-static inline void l2t_release(struct l2t_data *d, struct l2t_entry *e)
+static inline void l2t_release(struct t3cdev *t, struct l2t_entry *e)
{
- if (atomic_dec_and_test(&e->refcnt))
+ struct l2t_data *d;
+
+ rcu_read_lock();
+ d = L2DATA(t);
+
+ if (atomic_dec_and_test(&e->refcnt) && d)
t3_l2e_free(d, e);
+
+ rcu_read_unlock();
}
static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e)
{
- if (atomic_add_return(1, &e->refcnt) == 1) /* 0 -> 1 transition */
+ if (d && atomic_add_return(1, &e->refcnt) == 1) /* 0 -> 1 transition */
atomic_dec(&d->nfree);
}
diff --git a/drivers/net/cxgb3/mc5.c b/drivers/net/ethernet/chelsio/cxgb3/mc5.c
index e13b7fe9d082..e13b7fe9d082 100644
--- a/drivers/net/cxgb3/mc5.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/mc5.c
diff --git a/drivers/net/cxgb3/regs.h b/drivers/net/ethernet/chelsio/cxgb3/regs.h
index 6990f6c65221..6990f6c65221 100644
--- a/drivers/net/cxgb3/regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/regs.h
diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c
index 76bf5892b962..cfb60e1f51da 100644
--- a/drivers/net/cxgb3/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c
@@ -254,7 +254,7 @@ static inline void unmap_skb(struct sk_buff *skb, struct sge_txq *q,
while (frag_idx < nfrags && curflit < WR_FLITS) {
pci_unmap_page(pdev, be64_to_cpu(sgp->addr[j]),
- skb_shinfo(skb)->frags[frag_idx].size,
+ skb_frag_size(&skb_shinfo(skb)->frags[frag_idx]),
PCI_DMA_TODEVICE);
j ^= 1;
if (j == 0) {
@@ -977,11 +977,11 @@ static inline unsigned int make_sgl(const struct sk_buff *skb,
nfrags = skb_shinfo(skb)->nr_frags;
for (i = 0; i < nfrags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- mapping = pci_map_page(pdev, frag->page, frag->page_offset,
- frag->size, PCI_DMA_TODEVICE);
- sgp->len[j] = cpu_to_be32(frag->size);
+ mapping = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ sgp->len[j] = cpu_to_be32(skb_frag_size(frag));
sgp->addr[j] = cpu_to_be64(mapping);
j ^= 1;
if (j == 0)
@@ -1544,7 +1544,7 @@ static void deferred_unmap_destructor(struct sk_buff *skb)
si = skb_shinfo(skb);
for (i = 0; i < si->nr_frags; i++)
- pci_unmap_page(dui->pdev, *p++, si->frags[i].size,
+ pci_unmap_page(dui->pdev, *p++, skb_frag_size(&si->frags[i]),
PCI_DMA_TODEVICE);
}
@@ -2028,28 +2028,11 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
skb_checksum_none_assert(skb);
skb_record_rx_queue(skb, qs - &adap->sge.qs[pi->first_qset]);
- if (unlikely(p->vlan_valid)) {
- struct vlan_group *grp = pi->vlan_grp;
-
+ if (p->vlan_valid) {
qs->port_stats[SGE_PSTAT_VLANEX]++;
- if (likely(grp))
- if (lro)
- vlan_gro_receive(&qs->napi, grp,
- ntohs(p->vlan), skb);
- else {
- if (unlikely(pi->iscsic.flags)) {
- unsigned short vtag = ntohs(p->vlan) &
- VLAN_VID_MASK;
- skb->dev = vlan_group_get_device(grp,
- vtag);
- cxgb3_process_iscsi_prov_pack(pi, skb);
- }
- __vlan_hwaccel_rx(skb, grp, ntohs(p->vlan),
- rq->polling);
- }
- else
- dev_kfree_skb_any(skb);
- } else if (rq->polling) {
+ __vlan_hwaccel_put_tag(skb, ntohs(p->vlan));
+ }
+ if (rq->polling) {
if (lro)
napi_gro_receive(&qs->napi, skb);
else {
@@ -2133,9 +2116,9 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs,
len -= offset;
rx_frag += nr_frags;
- rx_frag->page = sd->pg_chunk.page;
+ __skb_frag_set_page(rx_frag, sd->pg_chunk.page);
rx_frag->page_offset = sd->pg_chunk.offset + offset;
- rx_frag->size = len;
+ skb_frag_size_set(rx_frag, len);
skb->len += len;
skb->data_len += len;
@@ -2147,14 +2130,8 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs,
skb_record_rx_queue(skb, qs - &adap->sge.qs[pi->first_qset]);
- if (unlikely(cpl->vlan_valid)) {
- struct vlan_group *grp = pi->vlan_grp;
-
- if (likely(grp != NULL)) {
- vlan_gro_frags(&qs->napi, grp, ntohs(cpl->vlan));
- return;
- }
- }
+ if (cpl->vlan_valid)
+ __vlan_hwaccel_put_tag(skb, ntohs(cpl->vlan));
napi_gro_frags(&qs->napi);
}
diff --git a/drivers/net/cxgb3/sge_defs.h b/drivers/net/ethernet/chelsio/cxgb3/sge_defs.h
index 29b6c800b238..29b6c800b238 100644
--- a/drivers/net/cxgb3/sge_defs.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/sge_defs.h
diff --git a/drivers/net/cxgb3/t3_cpl.h b/drivers/net/ethernet/chelsio/cxgb3/t3_cpl.h
index 852c399a8b0a..852c399a8b0a 100644
--- a/drivers/net/cxgb3/t3_cpl.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/t3_cpl.h
diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
index c688421da9c7..44ac2f40b644 100644
--- a/drivers/net/cxgb3/t3_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
@@ -3290,22 +3290,20 @@ static void config_pcie(struct adapter *adap)
unsigned int fst_trn_rx, fst_trn_tx, acklat, rpllmt;
pci_read_config_word(adap->pdev,
- adap->params.pci.pcie_cap_addr + PCI_EXP_DEVCTL,
+ adap->pdev->pcie_cap + PCI_EXP_DEVCTL,
&val);
pldsize = (val & PCI_EXP_DEVCTL_PAYLOAD) >> 5;
pci_read_config_word(adap->pdev, 0x2, &devid);
if (devid == 0x37) {
pci_write_config_word(adap->pdev,
- adap->params.pci.pcie_cap_addr +
- PCI_EXP_DEVCTL,
+ adap->pdev->pcie_cap + PCI_EXP_DEVCTL,
val & ~PCI_EXP_DEVCTL_READRQ &
~PCI_EXP_DEVCTL_PAYLOAD);
pldsize = 0;
}
- pci_read_config_word(adap->pdev,
- adap->params.pci.pcie_cap_addr + PCI_EXP_LNKCTL,
+ pci_read_config_word(adap->pdev, adap->pdev->pcie_cap + PCI_EXP_LNKCTL,
&val);
fst_trn_tx = G_NUMFSTTRNSEQ(t3_read_reg(adap, A_PCIE_PEX_CTRL0));
@@ -3429,12 +3427,11 @@ static void get_pci_mode(struct adapter *adapter, struct pci_params *p)
static unsigned short speed_map[] = { 33, 66, 100, 133 };
u32 pci_mode, pcie_cap;
- pcie_cap = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP);
+ pcie_cap = pci_pcie_cap(adapter->pdev);
if (pcie_cap) {
u16 val;
p->variant = PCI_VARIANT_PCIE;
- p->pcie_cap_addr = pcie_cap;
pci_read_config_word(adapter->pdev, pcie_cap + PCI_EXP_LNKSTA,
&val);
p->width = (val >> 4) & 0x3f;
diff --git a/drivers/net/cxgb3/t3cdev.h b/drivers/net/ethernet/chelsio/cxgb3/t3cdev.h
index be55e9ae74d1..705713b56636 100644
--- a/drivers/net/cxgb3/t3cdev.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/t3cdev.h
@@ -33,7 +33,7 @@
#define _T3CDEV_H_
#include <linux/list.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
diff --git a/drivers/net/cxgb3/version.h b/drivers/net/ethernet/chelsio/cxgb3/version.h
index 8bda06e366c8..8bda06e366c8 100644
--- a/drivers/net/cxgb3/version.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/version.h
diff --git a/drivers/net/cxgb3/vsc8211.c b/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c
index 4f9a1c2724f4..4f9a1c2724f4 100644
--- a/drivers/net/cxgb3/vsc8211.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c
diff --git a/drivers/net/cxgb3/xgmac.c b/drivers/net/ethernet/chelsio/cxgb3/xgmac.c
index 3af19a550372..3af19a550372 100644
--- a/drivers/net/cxgb3/xgmac.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/xgmac.c
diff --git a/drivers/net/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile
index 498667487f52..498667487f52 100644
--- a/drivers/net/cxgb4/Makefile
+++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile
diff --git a/drivers/net/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index bc9982a4c1f4..0fe18850c838 100644
--- a/drivers/net/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -276,7 +276,6 @@ enum {
};
struct adapter;
-struct vlan_group;
struct sge_rspq;
struct port_info {
@@ -327,7 +326,7 @@ struct sge_fl { /* SGE free-buffer queue state */
/* A packet gather list */
struct pkt_gl {
- skb_frag_t frags[MAX_SKB_FRAGS];
+ struct page_frag frags[MAX_SKB_FRAGS];
void *va; /* virtual address of first byte */
unsigned int nfrags; /* # of fragments */
unsigned int tot_len; /* total length of fragments */
diff --git a/drivers/net/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 7e3cfbe89e3b..4c8f42afa3c6 100644
--- a/drivers/net/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -41,6 +41,7 @@
#include <linux/err.h>
#include <linux/etherdevice.h>
#include <linux/firmware.h>
+#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/log2.h>
@@ -1901,7 +1902,7 @@ static int set_rss_table(struct net_device *dev,
}
static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
- void *rules)
+ u32 *rules)
{
const struct port_info *pi = netdev_priv(dev);
@@ -3639,6 +3640,8 @@ static int __devinit init_one(struct pci_dev *pdev,
netdev->features |= netdev->hw_features | highdma;
netdev->vlan_features = netdev->features & VLAN_FEAT;
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
netdev->netdev_ops = &cxgb4_netdev_ops;
SET_ETHTOOL_OPS(netdev, &cxgb_ethtool_ops);
}
@@ -3704,7 +3707,7 @@ static int __devinit init_one(struct pci_dev *pdev,
if (err) {
dev_warn(&pdev->dev, "only %d net devices registered\n", i);
err = 0;
- };
+ }
if (cxgb4_debugfs_root) {
adapter->debugfs_root = debugfs_create_dir(pci_name(pdev),
@@ -3712,6 +3715,9 @@ static int __devinit init_one(struct pci_dev *pdev,
setup_debugfs(adapter);
}
+ /* PCIe EEH recovery on powerpc platforms needs fundamental reset */
+ pdev->needs_freset = 1;
+
if (is_offload(adapter))
attach_ulds(adapter);
diff --git a/drivers/net/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
index 1b48c0170145..b1d39b8d141a 100644
--- a/drivers/net/cxgb4/cxgb4_uld.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
@@ -38,7 +38,7 @@
#include <linux/cache.h>
#include <linux/spinlock.h>
#include <linux/skbuff.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
/* CPL message priority levels */
enum {
diff --git a/drivers/net/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c
index a2d323c473f8..6ac77a62f361 100644
--- a/drivers/net/cxgb4/l2t.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c
@@ -37,6 +37,9 @@
#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/jhash.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <net/neighbour.h>
#include "cxgb4.h"
#include "l2t.h"
@@ -503,10 +506,6 @@ struct l2t_data *t4_init_l2t(void)
return d;
}
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-
static inline void *l2t_get_idx(struct seq_file *seq, loff_t pos)
{
struct l2t_entry *l2tab = seq->private;
diff --git a/drivers/net/cxgb4/l2t.h b/drivers/net/ethernet/chelsio/cxgb4/l2t.h
index 7bd8f42378ff..02b31d0c6410 100644
--- a/drivers/net/cxgb4/l2t.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.h
@@ -37,7 +37,7 @@
#include <linux/spinlock.h>
#include <linux/if_ether.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
struct adapter;
struct l2t_data;
diff --git a/drivers/net/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index 56adf448b9fe..140254c7cba9 100644
--- a/drivers/net/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -40,6 +40,7 @@
#include <linux/dma-mapping.h>
#include <linux/jiffies.h>
#include <linux/prefetch.h>
+#include <linux/export.h>
#include <net/ipv6.h>
#include <net/tcp.h>
#include "cxgb4.h"
@@ -215,8 +216,8 @@ static int map_skb(struct device *dev, const struct sk_buff *skb,
end = &si->frags[si->nr_frags];
for (fp = si->frags; fp < end; fp++) {
- *++addr = dma_map_page(dev, fp->page, fp->page_offset, fp->size,
- DMA_TO_DEVICE);
+ *++addr = skb_frag_dma_map(dev, fp, 0, skb_frag_size(fp),
+ DMA_TO_DEVICE);
if (dma_mapping_error(dev, *addr))
goto unwind;
}
@@ -224,7 +225,7 @@ static int map_skb(struct device *dev, const struct sk_buff *skb,
unwind:
while (fp-- > si->frags)
- dma_unmap_page(dev, *--addr, fp->size, DMA_TO_DEVICE);
+ dma_unmap_page(dev, *--addr, skb_frag_size(fp), DMA_TO_DEVICE);
dma_unmap_single(dev, addr[-1], skb_headlen(skb), DMA_TO_DEVICE);
out_err:
@@ -243,7 +244,7 @@ static void unmap_skb(struct device *dev, const struct sk_buff *skb,
si = skb_shinfo(skb);
end = &si->frags[si->nr_frags];
for (fp = si->frags; fp < end; fp++)
- dma_unmap_page(dev, *addr++, fp->size, DMA_TO_DEVICE);
+ dma_unmap_page(dev, *addr++, skb_frag_size(fp), DMA_TO_DEVICE);
}
/**
@@ -717,7 +718,7 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *q,
sgl->addr0 = cpu_to_be64(addr[0] + start);
nfrags++;
} else {
- sgl->len0 = htonl(si->frags[0].size);
+ sgl->len0 = htonl(skb_frag_size(&si->frags[0]));
sgl->addr0 = cpu_to_be64(addr[1]);
}
@@ -732,13 +733,13 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *q,
to = (u8 *)end > (u8 *)q->stat ? buf : sgl->sge;
for (i = (nfrags != si->nr_frags); nfrags >= 2; nfrags -= 2, to++) {
- to->len[0] = cpu_to_be32(si->frags[i].size);
- to->len[1] = cpu_to_be32(si->frags[++i].size);
+ to->len[0] = cpu_to_be32(skb_frag_size(&si->frags[i]));
+ to->len[1] = cpu_to_be32(skb_frag_size(&si->frags[++i]));
to->addr[0] = cpu_to_be64(addr[i]);
to->addr[1] = cpu_to_be64(addr[++i]);
}
if (nfrags) {
- to->len[0] = cpu_to_be32(si->frags[i].size);
+ to->len[0] = cpu_to_be32(skb_frag_size(&si->frags[i]));
to->len[1] = cpu_to_be32(0);
to->addr[0] = cpu_to_be64(addr[i + 1]);
}
@@ -1409,22 +1410,23 @@ int cxgb4_ofld_send(struct net_device *dev, struct sk_buff *skb)
}
EXPORT_SYMBOL(cxgb4_ofld_send);
-static inline void copy_frags(struct skb_shared_info *ssi,
+static inline void copy_frags(struct sk_buff *skb,
const struct pkt_gl *gl, unsigned int offset)
{
- unsigned int n;
+ int i;
/* usually there's just one frag */
- ssi->frags[0].page = gl->frags[0].page;
- ssi->frags[0].page_offset = gl->frags[0].page_offset + offset;
- ssi->frags[0].size = gl->frags[0].size - offset;
- ssi->nr_frags = gl->nfrags;
- n = gl->nfrags - 1;
- if (n)
- memcpy(&ssi->frags[1], &gl->frags[1], n * sizeof(skb_frag_t));
+ __skb_fill_page_desc(skb, 0, gl->frags[0].page,
+ gl->frags[0].offset + offset,
+ gl->frags[0].size - offset);
+ skb_shinfo(skb)->nr_frags = gl->nfrags;
+ for (i = 1; i < gl->nfrags; i++)
+ __skb_fill_page_desc(skb, i, gl->frags[i].page,
+ gl->frags[i].offset,
+ gl->frags[i].size);
/* get a reference to the last page, we don't own it */
- get_page(gl->frags[n].page);
+ get_page(gl->frags[gl->nfrags - 1].page);
}
/**
@@ -1459,7 +1461,7 @@ struct sk_buff *cxgb4_pktgl_to_skb(const struct pkt_gl *gl,
__skb_put(skb, pull_len);
skb_copy_to_linear_data(skb, gl->va, pull_len);
- copy_frags(skb_shinfo(skb), gl, pull_len);
+ copy_frags(skb, gl, pull_len);
skb->len = gl->tot_len;
skb->data_len = skb->len - pull_len;
skb->truesize += skb->data_len;
@@ -1478,7 +1480,7 @@ EXPORT_SYMBOL(cxgb4_pktgl_to_skb);
static void t4_pktgl_free(const struct pkt_gl *gl)
{
int n;
- const skb_frag_t *p;
+ const struct page_frag *p;
for (p = gl->frags, n = gl->nfrags - 1; n--; p++)
put_page(p->page);
@@ -1522,7 +1524,7 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
return;
}
- copy_frags(skb_shinfo(skb), gl, RX_PKT_PAD);
+ copy_frags(skb, gl, RX_PKT_PAD);
skb->len = gl->tot_len - RX_PKT_PAD;
skb->data_len = skb->len;
skb->truesize += skb->data_len;
@@ -1698,7 +1700,7 @@ static int process_responses(struct sge_rspq *q, int budget)
rmb();
rsp_type = RSPD_TYPE(rc->type_gen);
if (likely(rsp_type == RSP_TYPE_FLBUF)) {
- skb_frag_t *fp;
+ struct page_frag *fp;
struct pkt_gl si;
const struct rx_sw_desc *rsd;
u32 len = ntohl(rc->pldbuflen_qid), bufsz, frags;
@@ -1717,7 +1719,7 @@ static int process_responses(struct sge_rspq *q, int budget)
rsd = &rxq->fl.sdesc[rxq->fl.cidx];
bufsz = get_buf_size(rsd);
fp->page = rsd->page;
- fp->page_offset = q->offset;
+ fp->offset = q->offset;
fp->size = min(bufsz, len);
len -= fp->size;
if (!len)
@@ -1734,7 +1736,7 @@ static int process_responses(struct sge_rspq *q, int budget)
fp->size, DMA_FROM_DEVICE);
si.va = page_address(si.frags[0].page) +
- si.frags[0].page_offset;
+ si.frags[0].offset;
prefetch(si.va);
si.nfrags = frags + 1;
diff --git a/drivers/net/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index d1ec111aebd8..d1ec111aebd8 100644
--- a/drivers/net/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
diff --git a/drivers/net/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
index c26b455f37de..c26b455f37de 100644
--- a/drivers/net/cxgb4/t4_hw.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
diff --git a/drivers/net/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
index eb71b8250b91..eb71b8250b91 100644
--- a/drivers/net/cxgb4/t4_msg.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
diff --git a/drivers/net/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
index 0adc5bcec7c4..0adc5bcec7c4 100644
--- a/drivers/net/cxgb4/t4_regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
diff --git a/drivers/net/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
index edcfd7ec7802..edcfd7ec7802 100644
--- a/drivers/net/cxgb4/t4fw_api.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
diff --git a/drivers/net/cxgb4vf/Makefile b/drivers/net/ethernet/chelsio/cxgb4vf/Makefile
index d72ee26cb4c7..d72ee26cb4c7 100644
--- a/drivers/net/cxgb4vf/Makefile
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/Makefile
diff --git a/drivers/net/cxgb4vf/adapter.h b/drivers/net/ethernet/chelsio/cxgb4vf/adapter.h
index 4fd821aadc8a..611396c4b381 100644
--- a/drivers/net/cxgb4vf/adapter.h
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/adapter.h
@@ -40,6 +40,7 @@
#ifndef __CXGB4VF_ADAPTER_H__
#define __CXGB4VF_ADAPTER_H__
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/skbuff.h>
@@ -91,7 +92,6 @@ struct sge_rspq;
*/
struct port_info {
struct adapter *adapter; /* our adapter */
- struct vlan_group *vlan_grp; /* out VLAN group */
u16 viid; /* virtual interface ID */
s16 xact_addr_filt; /* index of our MAC address filter */
u16 rss_size; /* size of VI's RSS table slice */
@@ -144,7 +144,7 @@ struct sge_fl {
* An ingress packet gather list.
*/
struct pkt_gl {
- skb_frag_t frags[MAX_SKB_FRAGS];
+ struct page_frag frags[MAX_SKB_FRAGS];
void *va; /* virtual address of first byte */
unsigned int nfrags; /* # of fragments */
unsigned int tot_len; /* total length of fragments */
diff --git a/drivers/net/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
index e71c08e547e4..da9072bfca8b 100644
--- a/drivers/net/cxgb4vf/cxgb4vf_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
@@ -33,7 +33,6 @@
* SOFTWARE.
*/
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -210,18 +209,8 @@ void t4vf_os_link_changed(struct adapter *adapter, int pidx, int link_ok)
* ======================
*/
-/*
- * Record our new VLAN Group and enable/disable hardware VLAN Tag extraction
- * based on whether the specified VLAN Group pointer is NULL or not.
- */
-static void cxgb4vf_vlan_rx_register(struct net_device *dev,
- struct vlan_group *grp)
-{
- struct port_info *pi = netdev_priv(dev);
- pi->vlan_grp = grp;
- t4vf_set_rxmode(pi->adapter, pi->viid, -1, -1, -1, -1, grp != NULL, 0);
-}
+
/*
* Perform the MAC and PHY actions needed to enable a "port" (Virtual
@@ -234,9 +223,9 @@ static int link_start(struct net_device *dev)
/*
* We do not set address filters and promiscuity here, the stack does
- * that step explicitly.
+ * that step explicitly. Enable vlan accel.
*/
- ret = t4vf_set_rxmode(pi->adapter, pi->viid, dev->mtu, -1, -1, -1, -1,
+ ret = t4vf_set_rxmode(pi->adapter, pi->viid, dev->mtu, -1, -1, -1, 1,
true);
if (ret == 0) {
ret = t4vf_change_mac(pi->adapter, pi->viid,
@@ -1103,6 +1092,32 @@ static int cxgb4vf_change_mtu(struct net_device *dev, int new_mtu)
return ret;
}
+static u32 cxgb4vf_fix_features(struct net_device *dev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int cxgb4vf_set_features(struct net_device *dev, u32 features)
+{
+ struct port_info *pi = netdev_priv(dev);
+ u32 changed = dev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ t4vf_set_rxmode(pi->adapter, pi->viid, -1, -1, -1, -1,
+ features & NETIF_F_HW_VLAN_TX, 0);
+
+ return 0;
+}
+
/*
* Change the devices MAC address.
*/
@@ -2422,7 +2437,6 @@ static int __devinit enable_msix(struct adapter *adapter)
return err;
}
-#ifdef HAVE_NET_DEVICE_OPS
static const struct net_device_ops cxgb4vf_netdev_ops = {
.ndo_open = cxgb4vf_open,
.ndo_stop = cxgb4vf_stop,
@@ -2433,12 +2447,12 @@ static const struct net_device_ops cxgb4vf_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = cxgb4vf_do_ioctl,
.ndo_change_mtu = cxgb4vf_change_mtu,
- .ndo_vlan_rx_register = cxgb4vf_vlan_rx_register,
+ .ndo_fix_features = cxgb4vf_fix_features,
+ .ndo_set_features = cxgb4vf_set_features,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = cxgb4vf_poll_controller,
#endif
};
-#endif
/*
* "Probe" a device: initialize a device and construct all kernel and driver
@@ -2603,31 +2617,17 @@ static int __devinit cxgb4vf_pci_probe(struct pci_dev *pdev,
netdev->hw_features = NETIF_F_SG | TSO_FLAGS |
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
- NETIF_F_HW_VLAN_TX | NETIF_F_RXCSUM;
+ NETIF_F_HW_VLAN_RX | NETIF_F_RXCSUM;
netdev->vlan_features = NETIF_F_SG | TSO_FLAGS |
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_HIGHDMA;
- netdev->features = netdev->hw_features |
- NETIF_F_HW_VLAN_RX;
+ netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_TX;
if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA;
-#ifdef HAVE_NET_DEVICE_OPS
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
netdev->netdev_ops = &cxgb4vf_netdev_ops;
-#else
- netdev->vlan_rx_register = cxgb4vf_vlan_rx_register;
- netdev->open = cxgb4vf_open;
- netdev->stop = cxgb4vf_stop;
- netdev->hard_start_xmit = t4vf_eth_xmit;
- netdev->get_stats = cxgb4vf_get_stats;
- netdev->set_rx_mode = cxgb4vf_set_rxmode;
- netdev->do_ioctl = cxgb4vf_do_ioctl;
- netdev->change_mtu = cxgb4vf_change_mtu;
- netdev->set_mac_address = cxgb4vf_set_mac_addr;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = cxgb4vf_poll_controller;
-#endif
-#endif
SET_ETHTOOL_OPS(netdev, &cxgb4vf_ethtool_ops);
/*
diff --git a/drivers/net/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
index 5fd75fdaa631..8d5d55ad102d 100644
--- a/drivers/net/cxgb4vf/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
@@ -296,8 +296,8 @@ static int map_skb(struct device *dev, const struct sk_buff *skb,
si = skb_shinfo(skb);
end = &si->frags[si->nr_frags];
for (fp = si->frags; fp < end; fp++) {
- *++addr = dma_map_page(dev, fp->page, fp->page_offset, fp->size,
- DMA_TO_DEVICE);
+ *++addr = skb_frag_dma_map(dev, fp, 0, skb_frag_size(fp),
+ DMA_TO_DEVICE);
if (dma_mapping_error(dev, *addr))
goto unwind;
}
@@ -305,7 +305,7 @@ static int map_skb(struct device *dev, const struct sk_buff *skb,
unwind:
while (fp-- > si->frags)
- dma_unmap_page(dev, *--addr, fp->size, DMA_TO_DEVICE);
+ dma_unmap_page(dev, *--addr, skb_frag_size(fp), DMA_TO_DEVICE);
dma_unmap_single(dev, addr[-1], skb_headlen(skb), DMA_TO_DEVICE);
out_err:
@@ -899,7 +899,7 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *tq,
sgl->addr0 = cpu_to_be64(addr[0] + start);
nfrags++;
} else {
- sgl->len0 = htonl(si->frags[0].size);
+ sgl->len0 = htonl(skb_frag_size(&si->frags[0]));
sgl->addr0 = cpu_to_be64(addr[1]);
}
@@ -915,13 +915,13 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *tq,
to = (u8 *)end > (u8 *)tq->stat ? buf : sgl->sge;
for (i = (nfrags != si->nr_frags); nfrags >= 2; nfrags -= 2, to++) {
- to->len[0] = cpu_to_be32(si->frags[i].size);
- to->len[1] = cpu_to_be32(si->frags[++i].size);
+ to->len[0] = cpu_to_be32(skb_frag_size(&si->frags[i]));
+ to->len[1] = cpu_to_be32(skb_frag_size(&si->frags[++i]));
to->addr[0] = cpu_to_be64(addr[i]);
to->addr[1] = cpu_to_be64(addr[++i]);
}
if (nfrags) {
- to->len[0] = cpu_to_be32(si->frags[i].size);
+ to->len[0] = cpu_to_be32(skb_frag_size(&si->frags[i]));
to->len[1] = cpu_to_be32(0);
to->addr[0] = cpu_to_be64(addr[i + 1]);
}
@@ -1357,6 +1357,35 @@ out_free:
}
/**
+ * copy_frags - copy fragments from gather list into skb_shared_info
+ * @skb: destination skb
+ * @gl: source internal packet gather list
+ * @offset: packet start offset in first page
+ *
+ * Copy an internal packet gather list into a Linux skb_shared_info
+ * structure.
+ */
+static inline void copy_frags(struct sk_buff *skb,
+ const struct pkt_gl *gl,
+ unsigned int offset)
+{
+ int i;
+
+ /* usually there's just one frag */
+ __skb_fill_page_desc(skb, 0, gl->frags[0].page,
+ gl->frags[0].offset + offset,
+ gl->frags[0].size - offset);
+ skb_shinfo(skb)->nr_frags = gl->nfrags;
+ for (i = 1; i < gl->nfrags; i++)
+ __skb_fill_page_desc(skb, i, gl->frags[i].page,
+ gl->frags[i].offset,
+ gl->frags[i].size);
+
+ /* get a reference to the last page, we don't own it */
+ get_page(gl->frags[gl->nfrags - 1].page);
+}
+
+/**
* t4vf_pktgl_to_skb - build an sk_buff from a packet gather list
* @gl: the gather list
* @skb_len: size of sk_buff main body if it carries fragments
@@ -1369,7 +1398,6 @@ struct sk_buff *t4vf_pktgl_to_skb(const struct pkt_gl *gl,
unsigned int skb_len, unsigned int pull_len)
{
struct sk_buff *skb;
- struct skb_shared_info *ssi;
/*
* If the ingress packet is small enough, allocate an skb large enough
@@ -1396,21 +1424,10 @@ struct sk_buff *t4vf_pktgl_to_skb(const struct pkt_gl *gl,
__skb_put(skb, pull_len);
skb_copy_to_linear_data(skb, gl->va, pull_len);
- ssi = skb_shinfo(skb);
- ssi->frags[0].page = gl->frags[0].page;
- ssi->frags[0].page_offset = gl->frags[0].page_offset + pull_len;
- ssi->frags[0].size = gl->frags[0].size - pull_len;
- if (gl->nfrags > 1)
- memcpy(&ssi->frags[1], &gl->frags[1],
- (gl->nfrags-1) * sizeof(skb_frag_t));
- ssi->nr_frags = gl->nfrags;
-
+ copy_frags(skb, gl, pull_len);
skb->len = gl->tot_len;
skb->data_len = skb->len - pull_len;
skb->truesize += skb->data_len;
-
- /* Get a reference for the last page, we don't own it */
- get_page(gl->frags[gl->nfrags - 1].page);
}
out:
@@ -1434,35 +1451,6 @@ void t4vf_pktgl_free(const struct pkt_gl *gl)
}
/**
- * copy_frags - copy fragments from gather list into skb_shared_info
- * @si: destination skb shared info structure
- * @gl: source internal packet gather list
- * @offset: packet start offset in first page
- *
- * Copy an internal packet gather list into a Linux skb_shared_info
- * structure.
- */
-static inline void copy_frags(struct skb_shared_info *si,
- const struct pkt_gl *gl,
- unsigned int offset)
-{
- unsigned int n;
-
- /* usually there's just one frag */
- si->frags[0].page = gl->frags[0].page;
- si->frags[0].page_offset = gl->frags[0].page_offset + offset;
- si->frags[0].size = gl->frags[0].size - offset;
- si->nr_frags = gl->nfrags;
-
- n = gl->nfrags - 1;
- if (n)
- memcpy(&si->frags[1], &gl->frags[1], n * sizeof(skb_frag_t));
-
- /* get a reference to the last page, we don't own it */
- get_page(gl->frags[n].page);
-}
-
-/**
* do_gro - perform Generic Receive Offload ingress packet processing
* @rxq: ingress RX Ethernet Queue
* @gl: gather list for ingress packet
@@ -1484,27 +1472,17 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
return;
}
- copy_frags(skb_shinfo(skb), gl, PKTSHIFT);
+ copy_frags(skb, gl, PKTSHIFT);
skb->len = gl->tot_len - PKTSHIFT;
skb->data_len = skb->len;
skb->truesize += skb->data_len;
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(skb, rxq->rspq.idx);
- if (unlikely(pkt->vlan_ex)) {
- struct port_info *pi = netdev_priv(rxq->rspq.netdev);
- struct vlan_group *grp = pi->vlan_grp;
-
- rxq->stats.vlan_ex++;
- if (likely(grp)) {
- ret = vlan_gro_frags(&rxq->rspq.napi, grp,
- be16_to_cpu(pkt->vlan));
- goto stats;
- }
- }
+ if (pkt->vlan_ex)
+ __vlan_hwaccel_put_tag(skb, be16_to_cpu(pkt->vlan));
ret = napi_gro_frags(&rxq->rspq.napi);
-stats:
if (ret == GRO_HELD)
rxq->stats.lro_pkts++;
else if (ret == GRO_MERGED || ret == GRO_MERGED_FREE)
@@ -1525,7 +1503,6 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp,
const struct pkt_gl *gl)
{
struct sk_buff *skb;
- struct port_info *pi;
const struct cpl_rx_pkt *pkt = (void *)&rsp[1];
bool csum_ok = pkt->csum_calc && !pkt->err_vec;
struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq);
@@ -1553,7 +1530,6 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp,
__skb_pull(skb, PKTSHIFT);
skb->protocol = eth_type_trans(skb, rspq->netdev);
skb_record_rx_queue(skb, rspq->idx);
- pi = netdev_priv(skb->dev);
rxq->stats.pkts++;
if (csum_ok && (rspq->netdev->features & NETIF_F_RXCSUM) &&
@@ -1569,20 +1545,12 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp,
} else
skb_checksum_none_assert(skb);
- /*
- * Deliver the packet to the stack.
- */
- if (unlikely(pkt->vlan_ex)) {
- struct vlan_group *grp = pi->vlan_grp;
-
+ if (pkt->vlan_ex) {
rxq->stats.vlan_ex++;
- if (likely(grp))
- vlan_hwaccel_receive_skb(skb, grp,
- be16_to_cpu(pkt->vlan));
- else
- dev_kfree_skb_any(skb);
- } else
- netif_receive_skb(skb);
+ __vlan_hwaccel_put_tag(skb, be16_to_cpu(pkt->vlan));
+ }
+
+ netif_receive_skb(skb);
return 0;
}
@@ -1687,7 +1655,7 @@ int process_responses(struct sge_rspq *rspq, int budget)
rmb();
rsp_type = RSPD_TYPE(rc->type_gen);
if (likely(rsp_type == RSP_TYPE_FLBUF)) {
- skb_frag_t *fp;
+ struct page_frag *fp;
struct pkt_gl gl;
const struct rx_sw_desc *sdesc;
u32 bufsz, frag;
@@ -1721,7 +1689,7 @@ int process_responses(struct sge_rspq *rspq, int budget)
sdesc = &rxq->fl.sdesc[rxq->fl.cidx];
bufsz = get_buf_size(sdesc);
fp->page = sdesc->page;
- fp->page_offset = rspq->offset;
+ fp->offset = rspq->offset;
fp->size = min(bufsz, len);
len -= fp->size;
if (!len)
@@ -1739,7 +1707,7 @@ int process_responses(struct sge_rspq *rspq, int budget)
get_buf_addr(sdesc),
fp->size, DMA_FROM_DEVICE);
gl.va = (page_address(gl.frags[0].page) +
- gl.frags[0].page_offset);
+ gl.frags[0].offset);
prefetch(gl.va);
/*
diff --git a/drivers/net/cxgb4vf/t4vf_common.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h
index a65c80aed1f2..a65c80aed1f2 100644
--- a/drivers/net/cxgb4vf/t4vf_common.h
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h
diff --git a/drivers/net/cxgb4vf/t4vf_defs.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h
index c7b127d93767..c7b127d93767 100644
--- a/drivers/net/cxgb4vf/t4vf_defs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h
diff --git a/drivers/net/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
index 192db226ec7f..fe3fd3dad6f7 100644
--- a/drivers/net/cxgb4vf/t4vf_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
@@ -33,7 +33,6 @@
* SOFTWARE.
*/
-#include <linux/version.h>
#include <linux/pci.h>
#include "t4vf_common.h"
diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig
new file mode 100644
index 000000000000..1f8648f099c7
--- /dev/null
+++ b/drivers/net/ethernet/cirrus/Kconfig
@@ -0,0 +1,62 @@
+#
+# Cirrus network device configuration
+#
+
+config NET_VENDOR_CIRRUS
+ bool "Cirrus devices"
+ default y
+ depends on ISA || EISA || MACH_IXDP2351 || ARCH_IXDP2X01 \
+ || MACH_MX31ADS || MACH_QQ2440 || (ARM && ARCH_EP93XX) || MAC
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Cirrus cards. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_CIRRUS
+
+config CS89x0
+ tristate "CS89x0 support"
+ depends on (ISA || EISA || MACH_IXDP2351 \
+ || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440)
+ ---help---
+ Support for CS89x0 chipset based Ethernet cards. If you have a
+ network (Ethernet) card of this type, say Y and read the
+ Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto> as well as
+ <file:Documentation/networking/cs89x0.txt>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called cs89x0.
+
+config CS89x0_NONISA_IRQ
+ def_bool y
+ depends on CS89x0 != n
+ depends on MACH_IXDP2351 || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440
+
+config EP93XX_ETH
+ tristate "EP93xx Ethernet support"
+ depends on ARM && ARCH_EP93XX
+ select NET_CORE
+ select MII
+ help
+ This is a driver for the ethernet hardware included in EP93xx CPUs.
+ Say Y if you are building a kernel for EP93xx based devices.
+
+config MAC89x0
+ tristate "Macintosh CS89x0 based ethernet cards"
+ depends on MAC
+ ---help---
+ Support for CS89x0 chipset based Ethernet cards. If you have a
+ Nubus or LC-PDS network (Ethernet) card of this type, say Y and
+ read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. This module will
+ be called mac89x0.
+
+endif # NET_VENDOR_CIRRUS
diff --git a/drivers/net/ethernet/cirrus/Makefile b/drivers/net/ethernet/cirrus/Makefile
new file mode 100644
index 000000000000..ca245e2b5d98
--- /dev/null
+++ b/drivers/net/ethernet/cirrus/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Cirrus network device drivers.
+#
+
+obj-$(CONFIG_CS89x0) += cs89x0.o
+obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
+obj-$(CONFIG_MAC89x0) += mac89x0.o
diff --git a/drivers/net/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c
index 537a4b2e2020..f328da24c8fa 100644
--- a/drivers/net/cs89x0.c
+++ b/drivers/net/ethernet/cirrus/cs89x0.c
@@ -488,7 +488,7 @@ static const struct net_device_ops net_ops = {
.ndo_tx_timeout = net_timeout,
.ndo_start_xmit = net_send_packet,
.ndo_get_stats = net_get_stats,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_set_mac_address = set_mac_address,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = net_poll_controller,
diff --git a/drivers/net/cs89x0.h b/drivers/net/ethernet/cirrus/cs89x0.h
index 91423b70bb45..91423b70bb45 100644
--- a/drivers/net/cs89x0.h
+++ b/drivers/net/ethernet/cirrus/cs89x0.h
diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/ethernet/cirrus/ep93xx_eth.c
index 0b46b8ea0e80..4317af8d2f0a 100644
--- a/drivers/net/arm/ep93xx_eth.c
+++ b/drivers/net/ethernet/cirrus/ep93xx_eth.c
@@ -19,6 +19,7 @@
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
diff --git a/drivers/net/mac89x0.c b/drivers/net/ethernet/cirrus/mac89x0.c
index 669b317974a8..83781f316d1f 100644
--- a/drivers/net/mac89x0.c
+++ b/drivers/net/ethernet/cirrus/mac89x0.c
@@ -170,7 +170,7 @@ static const struct net_device_ops mac89x0_netdev_ops = {
.ndo_stop = net_close,
.ndo_start_xmit = net_send_packet,
.ndo_get_stats = net_get_stats,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_set_mac_address = set_mac_address,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/ethernet/cisco/Kconfig b/drivers/net/ethernet/cisco/Kconfig
new file mode 100644
index 000000000000..94606f7ee13a
--- /dev/null
+++ b/drivers/net/ethernet/cisco/Kconfig
@@ -0,0 +1,23 @@
+#
+# Cisco device configuration
+#
+
+config NET_VENDOR_CISCO
+ bool "Cisco devices"
+ default y
+ depends on PCI && INET
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Cisco cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_CISCO
+
+source "drivers/net/ethernet/cisco/enic/Kconfig"
+
+endif # NET_VENDOR_CISCO
diff --git a/drivers/net/ethernet/cisco/Makefile b/drivers/net/ethernet/cisco/Makefile
new file mode 100644
index 000000000000..6c7437bc4a92
--- /dev/null
+++ b/drivers/net/ethernet/cisco/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Cisco device drivers.
+#
+
+obj-$(CONFIG_ENIC) += enic/
diff --git a/drivers/net/ethernet/cisco/enic/Kconfig b/drivers/net/ethernet/cisco/enic/Kconfig
new file mode 100644
index 000000000000..9cc706a6cffd
--- /dev/null
+++ b/drivers/net/ethernet/cisco/enic/Kconfig
@@ -0,0 +1,9 @@
+#
+# Cisco device configuration
+#
+
+config ENIC
+ tristate "Cisco VIC Ethernet NIC Support"
+ depends on PCI && INET
+ ---help---
+ This enables the support for the Cisco VIC Ethernet card.
diff --git a/drivers/net/enic/Makefile b/drivers/net/ethernet/cisco/enic/Makefile
index 9d4974bba247..9d4974bba247 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/ethernet/cisco/enic/Makefile
diff --git a/drivers/net/enic/cq_desc.h b/drivers/net/ethernet/cisco/enic/cq_desc.h
index d6dd1b4edf6e..d6dd1b4edf6e 100644
--- a/drivers/net/enic/cq_desc.h
+++ b/drivers/net/ethernet/cisco/enic/cq_desc.h
diff --git a/drivers/net/enic/cq_enet_desc.h b/drivers/net/ethernet/cisco/enic/cq_enet_desc.h
index c2c0680a1146..c2c0680a1146 100644
--- a/drivers/net/enic/cq_enet_desc.h
+++ b/drivers/net/ethernet/cisco/enic/cq_enet_desc.h
diff --git a/drivers/net/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h
index 38b351c7b979..fe0c29acdbe6 100644
--- a/drivers/net/enic/enic.h
+++ b/drivers/net/ethernet/cisco/enic/enic.h
@@ -32,7 +32,7 @@
#define DRV_NAME "enic"
#define DRV_DESCRIPTION "Cisco VIC Ethernet NIC Driver"
-#define DRV_VERSION "2.1.1.13"
+#define DRV_VERSION "2.1.1.28"
#define DRV_COPYRIGHT "Copyright 2008-2011 Cisco Systems, Inc"
#define ENIC_BARS_MAX 6
@@ -49,6 +49,10 @@ struct enic_msix_entry {
void *devid;
};
+/* priv_flags */
+#define ENIC_SRIOV_ENABLED (1 << 0)
+
+/* enic port profile set flags */
#define ENIC_PORT_REQUEST_APPLIED (1 << 0)
#define ENIC_SET_REQUEST (1 << 1)
#define ENIC_SET_NAME (1 << 2)
@@ -74,6 +78,7 @@ struct enic {
struct vnic_dev *vdev;
struct timer_list notify_timer;
struct work_struct reset;
+ struct work_struct change_mtu_work;
struct msix_entry msix_entry[ENIC_INTR_MAX];
struct enic_msix_entry msix[ENIC_INTR_MAX];
u32 msg_enable;
@@ -82,18 +87,21 @@ struct enic {
u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN];
u8 uc_addr[ENIC_UNICAST_PERFECT_FILTERS][ETH_ALEN];
unsigned int flags;
+ unsigned int priv_flags;
unsigned int mc_count;
unsigned int uc_count;
u32 port_mtu;
u32 rx_coalesce_usecs;
u32 tx_coalesce_usecs;
- struct enic_port_profile pp;
+#ifdef CONFIG_PCI_IOV
+ u32 num_vfs;
+#endif
+ struct enic_port_profile *pp;
/* work queue cache line section */
____cacheline_aligned struct vnic_wq wq[ENIC_WQ_MAX];
spinlock_t wq_lock[ENIC_WQ_MAX];
unsigned int wq_count;
- struct vlan_group *vlan_group;
u16 loop_enable;
u16 loop_tag;
@@ -120,5 +128,8 @@ static inline struct device *enic_get_dev(struct enic *enic)
}
void enic_reset_addr_lists(struct enic *enic);
+int enic_sriov_enabled(struct enic *enic);
+int enic_is_valid_vf(struct enic *enic, int vf);
+int enic_is_dynamic(struct enic *enic);
#endif /* _ENIC_H_ */
diff --git a/drivers/net/enic/enic_dev.c b/drivers/net/ethernet/cisco/enic/enic_dev.c
index 90687b14e60f..fd6247b3c0ee 100644
--- a/drivers/net/enic/enic_dev.c
+++ b/drivers/net/ethernet/cisco/enic/enic_dev.c
@@ -166,6 +166,17 @@ int enic_dev_disable(struct enic *enic)
return err;
}
+int enic_dev_intr_coal_timer_info(struct enic *enic)
+{
+ int err;
+
+ spin_lock(&enic->devcmd_lock);
+ err = vnic_dev_intr_coal_timer_info(enic->vdev);
+ spin_unlock(&enic->devcmd_lock);
+
+ return err;
+}
+
int enic_vnic_dev_deinit(struct enic *enic)
{
int err;
diff --git a/drivers/net/enic/enic_dev.h b/drivers/net/ethernet/cisco/enic/enic_dev.h
index d5f681337626..1f83a4747ba0 100644
--- a/drivers/net/enic/enic_dev.h
+++ b/drivers/net/ethernet/cisco/enic/enic_dev.h
@@ -19,6 +19,25 @@
#ifndef _ENIC_DEV_H_
#define _ENIC_DEV_H_
+#include "vnic_dev.h"
+
+/*
+ * Calls the devcmd function given by argument vnicdevcmdfn.
+ * If vf argument is valid, it proxies the devcmd
+ */
+#define ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnicdevcmdfn, ...) \
+ do { \
+ spin_lock(&enic->devcmd_lock); \
+ if (enic_is_valid_vf(enic, vf)) { \
+ vnic_dev_cmd_proxy_by_index_start(enic->vdev, vf); \
+ err = vnicdevcmdfn(enic->vdev, ##__VA_ARGS__); \
+ vnic_dev_cmd_proxy_end(enic->vdev); \
+ } else { \
+ err = vnicdevcmdfn(enic->vdev, ##__VA_ARGS__); \
+ } \
+ spin_unlock(&enic->devcmd_lock); \
+ } while (0)
+
int enic_dev_fw_info(struct enic *enic, struct vnic_devcmd_fw_info **fw_info);
int enic_dev_stats_dump(struct enic *enic, struct vnic_stats **vstats);
int enic_dev_add_station_addr(struct enic *enic);
@@ -34,6 +53,7 @@ int enic_dev_hang_notify(struct enic *enic);
int enic_dev_set_ig_vlan_rewrite_mode(struct enic *enic);
int enic_dev_enable(struct enic *enic);
int enic_dev_disable(struct enic *enic);
+int enic_dev_intr_coal_timer_info(struct enic *enic);
int enic_vnic_dev_deinit(struct enic *enic);
int enic_dev_init_prov2(struct enic *enic, struct vic_provinfo *vp);
int enic_dev_deinit_done(struct enic *enic, int *status);
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index 2f433fbfca0c..c3786fda11db 100644
--- a/drivers/net/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -23,10 +23,12 @@
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/ethtool.h>
@@ -120,11 +122,25 @@ static const struct enic_stat enic_rx_stats[] = {
static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats);
static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats);
-static int enic_is_dynamic(struct enic *enic)
+int enic_is_dynamic(struct enic *enic)
{
return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_DYN;
}
+int enic_sriov_enabled(struct enic *enic)
+{
+ return (enic->priv_flags & ENIC_SRIOV_ENABLED) ? 1 : 0;
+}
+
+int enic_is_valid_vf(struct enic *enic, int vf)
+{
+#ifdef CONFIG_PCI_IOV
+ return vf >= 0 && vf < enic->num_vfs;
+#else
+ return 0;
+#endif
+}
+
static inline unsigned int enic_cq_rq(struct enic *enic, unsigned int rq)
{
return rq;
@@ -152,12 +168,12 @@ static inline unsigned int enic_legacy_notify_intr(void)
static inline unsigned int enic_msix_rq_intr(struct enic *enic, unsigned int rq)
{
- return rq;
+ return enic->cq[enic_cq_rq(enic, rq)].interrupt_offset;
}
static inline unsigned int enic_msix_wq_intr(struct enic *enic, unsigned int wq)
{
- return enic->rq_count + wq;
+ return enic->cq[enic_cq_wq(enic, wq)].interrupt_offset;
}
static inline unsigned int enic_msix_err_intr(struct enic *enic)
@@ -283,12 +299,10 @@ static int enic_set_coalesce(struct net_device *netdev,
u32 rx_coalesce_usecs;
unsigned int i, intr;
- tx_coalesce_usecs = min_t(u32,
- INTR_COALESCE_HW_TO_USEC(VNIC_INTR_TIMER_MAX),
- ecmd->tx_coalesce_usecs);
- rx_coalesce_usecs = min_t(u32,
- INTR_COALESCE_HW_TO_USEC(VNIC_INTR_TIMER_MAX),
- ecmd->rx_coalesce_usecs);
+ tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs,
+ vnic_dev_get_intr_coal_timer_max(enic->vdev));
+ rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs,
+ vnic_dev_get_intr_coal_timer_max(enic->vdev));
switch (vnic_dev_get_intr_mode(enic->vdev)) {
case VNIC_DEV_INTR_MODE_INTX:
@@ -297,26 +311,26 @@ static int enic_set_coalesce(struct net_device *netdev,
intr = enic_legacy_io_intr();
vnic_intr_coalescing_timer_set(&enic->intr[intr],
- INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs));
+ tx_coalesce_usecs);
break;
case VNIC_DEV_INTR_MODE_MSI:
if (tx_coalesce_usecs != rx_coalesce_usecs)
return -EINVAL;
vnic_intr_coalescing_timer_set(&enic->intr[0],
- INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs));
+ tx_coalesce_usecs);
break;
case VNIC_DEV_INTR_MODE_MSIX:
for (i = 0; i < enic->wq_count; i++) {
intr = enic_msix_wq_intr(enic, i);
vnic_intr_coalescing_timer_set(&enic->intr[intr],
- INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs));
+ tx_coalesce_usecs);
}
for (i = 0; i < enic->rq_count; i++) {
intr = enic_msix_rq_intr(enic, i);
vnic_intr_coalescing_timer_set(&enic->intr[intr],
- INTR_COALESCE_USEC_TO_HW(rx_coalesce_usecs));
+ rx_coalesce_usecs);
}
break;
@@ -423,11 +437,18 @@ static void enic_mtu_check(struct enic *enic)
if (mtu && mtu != enic->port_mtu) {
enic->port_mtu = mtu;
- if (mtu < netdev->mtu)
- netdev_warn(netdev,
- "interface MTU (%d) set higher "
- "than switch port MTU (%d)\n",
- netdev->mtu, mtu);
+ if (enic_is_dynamic(enic)) {
+ mtu = max_t(int, ENIC_MIN_MTU,
+ min_t(int, ENIC_MAX_MTU, mtu));
+ if (mtu != netdev->mtu)
+ schedule_work(&enic->change_mtu_work);
+ } else {
+ if (mtu < netdev->mtu)
+ netdev_warn(netdev,
+ "interface MTU (%d) set higher "
+ "than switch port MTU (%d)\n",
+ netdev->mtu, mtu);
+ }
}
}
@@ -578,16 +599,16 @@ static inline void enic_queue_wq_skb_cont(struct enic *enic,
struct vnic_wq *wq, struct sk_buff *skb,
unsigned int len_left, int loopback)
{
- skb_frag_t *frag;
+ const skb_frag_t *frag;
/* Queue additional data fragments */
for (frag = skb_shinfo(skb)->frags; len_left; frag++) {
- len_left -= frag->size;
+ len_left -= skb_frag_size(frag);
enic_queue_wq_desc_cont(wq, skb,
- pci_map_page(enic->pdev, frag->page,
- frag->page_offset, frag->size,
- PCI_DMA_TODEVICE),
- frag->size,
+ skb_frag_dma_map(&enic->pdev->dev,
+ frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE),
+ skb_frag_size(frag),
(len_left == 0), /* EOP? */
loopback);
}
@@ -696,16 +717,16 @@ static inline void enic_queue_wq_skb_tso(struct enic *enic,
* for additional data fragments
*/
for (frag = skb_shinfo(skb)->frags; len_left; frag++) {
- len_left -= frag->size;
- frag_len_left = frag->size;
- offset = frag->page_offset;
+ len_left -= skb_frag_size(frag);
+ frag_len_left = skb_frag_size(frag);
+ offset = 0;
while (frag_len_left) {
len = min(frag_len_left,
(unsigned int)WQ_ENET_MAX_DESC_LEN);
- dma_addr = pci_map_page(enic->pdev, frag->page,
- offset, len,
- PCI_DMA_TODEVICE);
+ dma_addr = skb_frag_dma_map(&enic->pdev->dev, frag,
+ offset, len,
+ DMA_TO_DEVICE);
enic_queue_wq_desc_cont(wq, skb,
dma_addr,
len,
@@ -793,10 +814,10 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
}
/* dev_base_lock rwlock held, nominally process context */
-static struct net_device_stats *enic_get_stats(struct net_device *netdev)
+static struct rtnl_link_stats64 *enic_get_stats(struct net_device *netdev,
+ struct rtnl_link_stats64 *net_stats)
{
struct enic *enic = netdev_priv(netdev);
- struct net_device_stats *net_stats = &netdev->stats;
struct vnic_stats *stats;
enic_dev_stats_dump(enic, &stats);
@@ -1023,14 +1044,6 @@ static void enic_set_rx_mode(struct net_device *netdev)
}
}
-/* rtnl lock is held */
-static void enic_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *vlan_group)
-{
- struct enic *enic = netdev_priv(netdev);
- enic->vlan_group = vlan_group;
-}
-
/* netif_tx_lock held, BHs disabled */
static void enic_tx_timeout(struct net_device *netdev)
{
@@ -1041,15 +1054,15 @@ static void enic_tx_timeout(struct net_device *netdev)
static int enic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
{
struct enic *enic = netdev_priv(netdev);
+ struct enic_port_profile *pp;
+ int err;
- if (vf != PORT_SELF_VF)
- return -EOPNOTSUPP;
+ ENIC_PP_BY_INDEX(enic, vf, pp, &err);
+ if (err)
+ return err;
- /* Ignore the vf argument for now. We can assume the request
- * is coming on a vf.
- */
if (is_valid_ether_addr(mac)) {
- memcpy(enic->pp.vf_mac, mac, ETH_ALEN);
+ memcpy(pp->vf_mac, mac, ETH_ALEN);
return 0;
} else
return -EINVAL;
@@ -1060,71 +1073,74 @@ static int enic_set_vf_port(struct net_device *netdev, int vf,
{
struct enic *enic = netdev_priv(netdev);
struct enic_port_profile prev_pp;
+ struct enic_port_profile *pp;
int err = 0, restore_pp = 1;
- /* don't support VFs, yet */
- if (vf != PORT_SELF_VF)
- return -EOPNOTSUPP;
+ ENIC_PP_BY_INDEX(enic, vf, pp, &err);
+ if (err)
+ return err;
if (!port[IFLA_PORT_REQUEST])
return -EOPNOTSUPP;
- memcpy(&prev_pp, &enic->pp, sizeof(enic->pp));
- memset(&enic->pp, 0, sizeof(enic->pp));
+ memcpy(&prev_pp, pp, sizeof(*enic->pp));
+ memset(pp, 0, sizeof(*enic->pp));
- enic->pp.set |= ENIC_SET_REQUEST;
- enic->pp.request = nla_get_u8(port[IFLA_PORT_REQUEST]);
+ pp->set |= ENIC_SET_REQUEST;
+ pp->request = nla_get_u8(port[IFLA_PORT_REQUEST]);
if (port[IFLA_PORT_PROFILE]) {
- enic->pp.set |= ENIC_SET_NAME;
- memcpy(enic->pp.name, nla_data(port[IFLA_PORT_PROFILE]),
+ pp->set |= ENIC_SET_NAME;
+ memcpy(pp->name, nla_data(port[IFLA_PORT_PROFILE]),
PORT_PROFILE_MAX);
}
if (port[IFLA_PORT_INSTANCE_UUID]) {
- enic->pp.set |= ENIC_SET_INSTANCE;
- memcpy(enic->pp.instance_uuid,
+ pp->set |= ENIC_SET_INSTANCE;
+ memcpy(pp->instance_uuid,
nla_data(port[IFLA_PORT_INSTANCE_UUID]), PORT_UUID_MAX);
}
if (port[IFLA_PORT_HOST_UUID]) {
- enic->pp.set |= ENIC_SET_HOST;
- memcpy(enic->pp.host_uuid,
+ pp->set |= ENIC_SET_HOST;
+ memcpy(pp->host_uuid,
nla_data(port[IFLA_PORT_HOST_UUID]), PORT_UUID_MAX);
}
/* Special case handling: mac came from IFLA_VF_MAC */
if (!is_zero_ether_addr(prev_pp.vf_mac))
- memcpy(enic->pp.mac_addr, prev_pp.vf_mac, ETH_ALEN);
+ memcpy(pp->mac_addr, prev_pp.vf_mac, ETH_ALEN);
- if (is_zero_ether_addr(netdev->dev_addr))
- random_ether_addr(netdev->dev_addr);
+ if (vf == PORT_SELF_VF && is_zero_ether_addr(netdev->dev_addr))
+ random_ether_addr(netdev->dev_addr);
- err = enic_process_set_pp_request(enic, &prev_pp, &restore_pp);
+ err = enic_process_set_pp_request(enic, vf, &prev_pp, &restore_pp);
if (err) {
if (restore_pp) {
/* Things are still the way they were: Implicit
* DISASSOCIATE failed
*/
- memcpy(&enic->pp, &prev_pp, sizeof(enic->pp));
+ memcpy(pp, &prev_pp, sizeof(*pp));
} else {
- memset(&enic->pp, 0, sizeof(enic->pp));
- memset(netdev->dev_addr, 0, ETH_ALEN);
+ memset(pp, 0, sizeof(*pp));
+ if (vf == PORT_SELF_VF)
+ memset(netdev->dev_addr, 0, ETH_ALEN);
}
} else {
/* Set flag to indicate that the port assoc/disassoc
* request has been sent out to fw
*/
- enic->pp.set |= ENIC_PORT_REQUEST_APPLIED;
+ pp->set |= ENIC_PORT_REQUEST_APPLIED;
/* If DISASSOCIATE, clean up all assigned/saved macaddresses */
- if (enic->pp.request == PORT_REQUEST_DISASSOCIATE) {
- memset(enic->pp.mac_addr, 0, ETH_ALEN);
- memset(netdev->dev_addr, 0, ETH_ALEN);
+ if (pp->request == PORT_REQUEST_DISASSOCIATE) {
+ memset(pp->mac_addr, 0, ETH_ALEN);
+ if (vf == PORT_SELF_VF)
+ memset(netdev->dev_addr, 0, ETH_ALEN);
}
}
- memset(enic->pp.vf_mac, 0, ETH_ALEN);
+ memset(pp->vf_mac, 0, ETH_ALEN);
return err;
}
@@ -1134,26 +1150,31 @@ static int enic_get_vf_port(struct net_device *netdev, int vf,
{
struct enic *enic = netdev_priv(netdev);
u16 response = PORT_PROFILE_RESPONSE_SUCCESS;
+ struct enic_port_profile *pp;
int err;
- if (!(enic->pp.set & ENIC_PORT_REQUEST_APPLIED))
+ ENIC_PP_BY_INDEX(enic, vf, pp, &err);
+ if (err)
+ return err;
+
+ if (!(pp->set & ENIC_PORT_REQUEST_APPLIED))
return -ENODATA;
- err = enic_process_get_pp_request(enic, enic->pp.request, &response);
+ err = enic_process_get_pp_request(enic, vf, pp->request, &response);
if (err)
return err;
- NLA_PUT_U16(skb, IFLA_PORT_REQUEST, enic->pp.request);
+ NLA_PUT_U16(skb, IFLA_PORT_REQUEST, pp->request);
NLA_PUT_U16(skb, IFLA_PORT_RESPONSE, response);
- if (enic->pp.set & ENIC_SET_NAME)
+ if (pp->set & ENIC_SET_NAME)
NLA_PUT(skb, IFLA_PORT_PROFILE, PORT_PROFILE_MAX,
- enic->pp.name);
- if (enic->pp.set & ENIC_SET_INSTANCE)
+ pp->name);
+ if (pp->set & ENIC_SET_INSTANCE)
NLA_PUT(skb, IFLA_PORT_INSTANCE_UUID, PORT_UUID_MAX,
- enic->pp.instance_uuid);
- if (enic->pp.set & ENIC_SET_HOST)
+ pp->instance_uuid);
+ if (pp->set & ENIC_SET_HOST)
NLA_PUT(skb, IFLA_PORT_HOST_UUID, PORT_UUID_MAX,
- enic->pp.host_uuid);
+ pp->host_uuid);
return 0;
@@ -1258,24 +1279,13 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
skb->dev = netdev;
- if (enic->vlan_group && vlan_stripped &&
- (vlan_tci & CQ_ENET_RQ_DESC_VLAN_TCI_VLAN_MASK)) {
-
- if (netdev->features & NETIF_F_GRO)
- vlan_gro_receive(&enic->napi[q_number],
- enic->vlan_group, vlan_tci, skb);
- else
- vlan_hwaccel_receive_skb(skb,
- enic->vlan_group, vlan_tci);
-
- } else {
-
- if (netdev->features & NETIF_F_GRO)
- napi_gro_receive(&enic->napi[q_number], skb);
- else
- netif_receive_skb(skb);
+ if (vlan_stripped)
+ __vlan_hwaccel_put_tag(skb, vlan_tci);
- }
+ if (netdev->features & NETIF_F_GRO)
+ napi_gro_receive(&enic->napi[q_number], skb);
+ else
+ netif_receive_skb(skb);
} else {
/* Buffer overflow
@@ -1560,7 +1570,7 @@ static void enic_notify_timer_start(struct enic *enic)
default:
/* Using intr for notification for INTx/MSI-X */
break;
- };
+ }
}
/* rtnl lock is held, process context */
@@ -1598,10 +1608,9 @@ static int enic_open(struct net_device *netdev)
for (i = 0; i < enic->rq_count; i++)
vnic_rq_enable(&enic->rq[i]);
- if (enic_is_dynamic(enic) && !is_zero_ether_addr(enic->pp.mac_addr))
- enic_dev_add_addr(enic, enic->pp.mac_addr);
- else
+ if (!enic_is_dynamic(enic))
enic_dev_add_station_addr(enic);
+
enic_set_rx_mode(netdev);
netif_wake_queue(netdev);
@@ -1649,9 +1658,8 @@ static int enic_stop(struct net_device *netdev)
netif_carrier_off(netdev);
netif_tx_disable(netdev);
- if (enic_is_dynamic(enic) && !is_zero_ether_addr(enic->pp.mac_addr))
- enic_dev_del_addr(enic, enic->pp.mac_addr);
- else
+
+ if (!enic_is_dynamic(enic))
enic_dev_del_station_addr(enic);
for (i = 0; i < enic->wq_count; i++) {
@@ -1688,6 +1696,9 @@ static int enic_change_mtu(struct net_device *netdev, int new_mtu)
if (new_mtu < ENIC_MIN_MTU || new_mtu > ENIC_MAX_MTU)
return -EINVAL;
+ if (enic_is_dynamic(enic))
+ return -EOPNOTSUPP;
+
if (running)
enic_stop(netdev);
@@ -1704,6 +1715,55 @@ static int enic_change_mtu(struct net_device *netdev, int new_mtu)
return 0;
}
+static void enic_change_mtu_work(struct work_struct *work)
+{
+ struct enic *enic = container_of(work, struct enic, change_mtu_work);
+ struct net_device *netdev = enic->netdev;
+ int new_mtu = vnic_dev_mtu(enic->vdev);
+ int err;
+ unsigned int i;
+
+ new_mtu = max_t(int, ENIC_MIN_MTU, min_t(int, ENIC_MAX_MTU, new_mtu));
+
+ rtnl_lock();
+
+ /* Stop RQ */
+ del_timer_sync(&enic->notify_timer);
+
+ for (i = 0; i < enic->rq_count; i++)
+ napi_disable(&enic->napi[i]);
+
+ vnic_intr_mask(&enic->intr[0]);
+ enic_synchronize_irqs(enic);
+ err = vnic_rq_disable(&enic->rq[0]);
+ if (err) {
+ netdev_err(netdev, "Unable to disable RQ.\n");
+ return;
+ }
+ vnic_rq_clean(&enic->rq[0], enic_free_rq_buf);
+ vnic_cq_clean(&enic->cq[0]);
+ vnic_intr_clean(&enic->intr[0]);
+
+ /* Fill RQ with new_mtu-sized buffers */
+ netdev->mtu = new_mtu;
+ vnic_rq_fill(&enic->rq[0], enic_rq_alloc_buf);
+ /* Need at least one buffer on ring to get going */
+ if (vnic_rq_desc_used(&enic->rq[0]) == 0) {
+ netdev_err(netdev, "Unable to alloc receive buffers.\n");
+ return;
+ }
+
+ /* Start RQ */
+ vnic_rq_enable(&enic->rq[0]);
+ napi_enable(&enic->napi[0]);
+ vnic_intr_unmask(&enic->intr[0]);
+ enic_notify_timer_start(enic);
+
+ rtnl_unlock();
+
+ netdev_info(netdev, "interface MTU set as %d\n", netdev->mtu);
+}
+
#ifdef CONFIG_NET_POLL_CONTROLLER
static void enic_poll_controller(struct net_device *netdev)
{
@@ -1718,8 +1778,12 @@ static void enic_poll_controller(struct net_device *netdev)
enic_isr_msix_rq(enic->msix_entry[intr].vector,
&enic->napi[i]);
}
- intr = enic_msix_wq_intr(enic, i);
- enic_isr_msix_wq(enic->msix_entry[intr].vector, enic);
+
+ for (i = 0; i < enic->wq_count; i++) {
+ intr = enic_msix_wq_intr(enic, i);
+ enic_isr_msix_wq(enic->msix_entry[intr].vector, enic);
+ }
+
break;
case VNIC_DEV_INTR_MODE_MSI:
enic_isr_msi(enic->pdev->irq, enic);
@@ -2057,13 +2121,11 @@ static const struct net_device_ops enic_netdev_dynamic_ops = {
.ndo_open = enic_open,
.ndo_stop = enic_stop,
.ndo_start_xmit = enic_hard_start_xmit,
- .ndo_get_stats = enic_get_stats,
+ .ndo_get_stats64 = enic_get_stats,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_rx_mode = enic_set_rx_mode,
- .ndo_set_multicast_list = enic_set_rx_mode,
.ndo_set_mac_address = enic_set_mac_address_dynamic,
.ndo_change_mtu = enic_change_mtu,
- .ndo_vlan_rx_register = enic_vlan_rx_register,
.ndo_vlan_rx_add_vid = enic_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = enic_vlan_rx_kill_vid,
.ndo_tx_timeout = enic_tx_timeout,
@@ -2079,16 +2141,17 @@ static const struct net_device_ops enic_netdev_ops = {
.ndo_open = enic_open,
.ndo_stop = enic_stop,
.ndo_start_xmit = enic_hard_start_xmit,
- .ndo_get_stats = enic_get_stats,
+ .ndo_get_stats64 = enic_get_stats,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = enic_set_mac_address,
.ndo_set_rx_mode = enic_set_rx_mode,
- .ndo_set_multicast_list = enic_set_rx_mode,
.ndo_change_mtu = enic_change_mtu,
- .ndo_vlan_rx_register = enic_vlan_rx_register,
.ndo_vlan_rx_add_vid = enic_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = enic_vlan_rx_kill_vid,
.ndo_tx_timeout = enic_tx_timeout,
+ .ndo_set_vf_port = enic_set_vf_port,
+ .ndo_get_vf_port = enic_get_vf_port,
+ .ndo_set_vf_mac = enic_set_vf_mac,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = enic_poll_controller,
#endif
@@ -2112,6 +2175,14 @@ static int enic_dev_init(struct enic *enic)
unsigned int i;
int err;
+ /* Get interrupt coalesce timer info */
+ err = enic_dev_intr_coal_timer_info(enic);
+ if (err) {
+ dev_warn(dev, "Using default conversion factor for "
+ "interrupt coalesce timer\n");
+ vnic_dev_intr_coal_timer_info_default(enic->vdev);
+ }
+
/* Get vNIC configuration
*/
@@ -2192,6 +2263,10 @@ static int __devinit enic_probe(struct pci_dev *pdev,
int using_dac = 0;
unsigned int i;
int err;
+ int num_pps = 1;
+#ifdef CONFIG_PCI_IOV
+ int pos = 0;
+#endif
/* Allocate net device structure and initialize. Private
* instance data is initialized to zero.
@@ -2283,13 +2358,41 @@ static int __devinit enic_probe(struct pci_dev *pdev,
goto err_out_iounmap;
}
+#ifdef CONFIG_PCI_IOV
+ /* Get number of subvnics */
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+ if (pos) {
+ pci_read_config_word(pdev, pos + PCI_SRIOV_TOTAL_VF,
+ (u16 *)&enic->num_vfs);
+ if (enic->num_vfs) {
+ err = pci_enable_sriov(pdev, enic->num_vfs);
+ if (err) {
+ dev_err(dev, "SRIOV enable failed, aborting."
+ " pci_enable_sriov() returned %d\n",
+ err);
+ goto err_out_vnic_unregister;
+ }
+ enic->priv_flags |= ENIC_SRIOV_ENABLED;
+ num_pps = enic->num_vfs;
+ }
+ }
+
+#endif
+ /* Allocate structure for port profiles */
+ enic->pp = kzalloc(num_pps * sizeof(*enic->pp), GFP_KERNEL);
+ if (!enic->pp) {
+ pr_err("port profile alloc failed, aborting\n");
+ err = -ENOMEM;
+ goto err_out_disable_sriov;
+ }
+
/* Issue device open to get device in known state
*/
err = enic_dev_open(enic);
if (err) {
dev_err(dev, "vNIC dev open failed, aborting\n");
- goto err_out_vnic_unregister;
+ goto err_out_free_pp;
}
/* Setup devcmd lock
@@ -2345,6 +2448,7 @@ static int __devinit enic_probe(struct pci_dev *pdev,
enic->notify_timer.data = (unsigned long)enic;
INIT_WORK(&enic->reset, enic_reset);
+ INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work);
for (i = 0; i < enic->wq_count; i++)
spin_lock_init(&enic->wq_lock[i]);
@@ -2355,6 +2459,12 @@ static int __devinit enic_probe(struct pci_dev *pdev,
enic->port_mtu = enic->config.mtu;
(void)enic_change_mtu(netdev, enic->port_mtu);
+#ifdef CONFIG_PCI_IOV
+ if (enic_is_dynamic(enic) && pdev->is_virtfn &&
+ is_zero_ether_addr(enic->mac_addr))
+ random_ether_addr(enic->mac_addr);
+#endif
+
err = enic_set_mac_addr(netdev, enic->mac_addr);
if (err) {
dev_err(dev, "Invalid MAC address, aborting\n");
@@ -2392,6 +2502,8 @@ static int __devinit enic_probe(struct pci_dev *pdev,
if (using_dac)
netdev->features |= NETIF_F_HIGHDMA;
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
err = register_netdev(netdev);
if (err) {
dev_err(dev, "Cannot register net device, aborting\n");
@@ -2404,8 +2516,17 @@ err_out_dev_deinit:
enic_dev_deinit(enic);
err_out_dev_close:
vnic_dev_close(enic->vdev);
+err_out_free_pp:
+ kfree(enic->pp);
+err_out_disable_sriov:
+#ifdef CONFIG_PCI_IOV
+ if (enic_sriov_enabled(enic)) {
+ pci_disable_sriov(pdev);
+ enic->priv_flags &= ~ENIC_SRIOV_ENABLED;
+ }
err_out_vnic_unregister:
vnic_dev_unregister(enic->vdev);
+#endif
err_out_iounmap:
enic_iounmap(enic);
err_out_release_regions:
@@ -2427,9 +2548,17 @@ static void __devexit enic_remove(struct pci_dev *pdev)
struct enic *enic = netdev_priv(netdev);
cancel_work_sync(&enic->reset);
+ cancel_work_sync(&enic->change_mtu_work);
unregister_netdev(netdev);
enic_dev_deinit(enic);
vnic_dev_close(enic->vdev);
+#ifdef CONFIG_PCI_IOV
+ if (enic_sriov_enabled(enic)) {
+ pci_disable_sriov(pdev);
+ enic->priv_flags &= ~ENIC_SRIOV_ENABLED;
+ }
+#endif
+ kfree(enic->pp);
vnic_dev_unregister(enic->vdev);
enic_iounmap(enic);
pci_release_regions(pdev);
diff --git a/drivers/net/enic/enic_pp.c b/drivers/net/ethernet/cisco/enic/enic_pp.c
index ffaa75dd1ded..22bf03a1829e 100644
--- a/drivers/net/enic/enic_pp.c
+++ b/drivers/net/ethernet/cisco/enic/enic_pp.c
@@ -29,10 +29,47 @@
#include "enic_res.h"
#include "enic.h"
#include "enic_dev.h"
+#include "enic_pp.h"
-static int enic_set_port_profile(struct enic *enic)
+/*
+ * Checks validity of vf index that came in
+ * port profile request
+ */
+int enic_is_valid_pp_vf(struct enic *enic, int vf, int *err)
+{
+ if (vf != PORT_SELF_VF) {
+#ifdef CONFIG_PCI_IOV
+ if (enic_sriov_enabled(enic)) {
+ if (vf < 0 || vf >= enic->num_vfs) {
+ *err = -EINVAL;
+ goto err_out;
+ }
+ } else {
+ *err = -EOPNOTSUPP;
+ goto err_out;
+ }
+#else
+ *err = -EOPNOTSUPP;
+ goto err_out;
+#endif
+ }
+
+ if (vf == PORT_SELF_VF && !enic_is_dynamic(enic)) {
+ *err = -EOPNOTSUPP;
+ goto err_out;
+ }
+
+ *err = 0;
+ return 1;
+
+err_out:
+ return 0;
+}
+
+static int enic_set_port_profile(struct enic *enic, int vf)
{
struct net_device *netdev = enic->netdev;
+ struct enic_port_profile *pp;
struct vic_provinfo *vp;
const u8 oui[3] = VIC_PROVINFO_CISCO_OUI;
const u16 os_type = htons(VIC_GENERIC_PROV_OS_TYPE_LINUX);
@@ -41,7 +78,11 @@ static int enic_set_port_profile(struct enic *enic)
u8 *client_mac;
int err;
- if (!(enic->pp.set & ENIC_SET_NAME) || !strlen(enic->pp.name))
+ ENIC_PP_BY_INDEX(enic, vf, pp, &err);
+ if (err)
+ return err;
+
+ if (!(pp->set & ENIC_SET_NAME) || !strlen(pp->name))
return -EINVAL;
vp = vic_provinfo_alloc(GFP_KERNEL, oui,
@@ -51,12 +92,18 @@ static int enic_set_port_profile(struct enic *enic)
VIC_PROVINFO_ADD_TLV(vp,
VIC_GENERIC_PROV_TLV_PORT_PROFILE_NAME_STR,
- strlen(enic->pp.name) + 1, enic->pp.name);
+ strlen(pp->name) + 1, pp->name);
- if (!is_zero_ether_addr(enic->pp.mac_addr))
- client_mac = enic->pp.mac_addr;
- else
+ if (!is_zero_ether_addr(pp->mac_addr)) {
+ client_mac = pp->mac_addr;
+ } else if (vf == PORT_SELF_VF) {
client_mac = netdev->dev_addr;
+ } else {
+ netdev_err(netdev, "Cannot find pp mac address "
+ "for VF %d\n", vf);
+ err = -EINVAL;
+ goto add_tlv_failure;
+ }
VIC_PROVINFO_ADD_TLV(vp,
VIC_GENERIC_PROV_TLV_CLIENT_MAC_ADDR,
@@ -67,15 +114,15 @@ static int enic_set_port_profile(struct enic *enic)
VIC_GENERIC_PROV_TLV_CLUSTER_PORT_UUID_STR,
sizeof(client_mac_str), client_mac_str);
- if (enic->pp.set & ENIC_SET_INSTANCE) {
- sprintf(uuid_str, "%pUB", enic->pp.instance_uuid);
+ if (pp->set & ENIC_SET_INSTANCE) {
+ sprintf(uuid_str, "%pUB", pp->instance_uuid);
VIC_PROVINFO_ADD_TLV(vp,
VIC_GENERIC_PROV_TLV_CLIENT_UUID_STR,
sizeof(uuid_str), uuid_str);
}
- if (enic->pp.set & ENIC_SET_HOST) {
- sprintf(uuid_str, "%pUB", enic->pp.host_uuid);
+ if (pp->set & ENIC_SET_HOST) {
+ sprintf(uuid_str, "%pUB", pp->host_uuid);
VIC_PROVINFO_ADD_TLV(vp,
VIC_GENERIC_PROV_TLV_HOST_UUID_STR,
sizeof(uuid_str), uuid_str);
@@ -85,7 +132,9 @@ static int enic_set_port_profile(struct enic *enic)
VIC_GENERIC_PROV_TLV_OS_TYPE,
sizeof(os_type), &os_type);
- err = enic_dev_status_to_errno(enic_dev_init_prov2(enic, vp));
+ ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_init_prov2, (u8 *)vp,
+ vic_provinfo_size(vp));
+ err = enic_dev_status_to_errno(err);
add_tlv_failure:
vic_provinfo_free(vp);
@@ -93,15 +142,16 @@ add_tlv_failure:
return err;
}
-static int enic_unset_port_profile(struct enic *enic)
+static int enic_unset_port_profile(struct enic *enic, int vf)
{
int err;
- err = enic_vnic_dev_deinit(enic);
+ ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_deinit);
if (err)
return enic_dev_status_to_errno(err);
- enic_reset_addr_lists(enic);
+ if (vf == PORT_SELF_VF)
+ enic_reset_addr_lists(enic);
return 0;
}
@@ -115,17 +165,18 @@ static int enic_are_pp_different(struct enic_port_profile *pp1,
!!memcmp(pp1->mac_addr, pp2->mac_addr, ETH_ALEN);
}
-static int enic_pp_preassociate(struct enic *enic,
+static int enic_pp_preassociate(struct enic *enic, int vf,
struct enic_port_profile *prev_pp, int *restore_pp);
-static int enic_pp_disassociate(struct enic *enic,
+static int enic_pp_disassociate(struct enic *enic, int vf,
struct enic_port_profile *prev_pp, int *restore_pp);
-static int enic_pp_preassociate_rr(struct enic *enic,
+static int enic_pp_preassociate_rr(struct enic *enic, int vf,
struct enic_port_profile *prev_pp, int *restore_pp);
-static int enic_pp_associate(struct enic *enic,
+static int enic_pp_associate(struct enic *enic, int vf,
struct enic_port_profile *prev_pp, int *restore_pp);
-static int (*enic_pp_handlers[])(struct enic *enic,
- struct enic_port_profile *prev_state, int *restore_pp) = {
+static int (*enic_pp_handlers[])(struct enic *enic, int vf,
+ struct enic_port_profile *prev_state,
+ int *restore_pp) = {
[PORT_REQUEST_PREASSOCIATE] = enic_pp_preassociate,
[PORT_REQUEST_PREASSOCIATE_RR] = enic_pp_preassociate_rr,
[PORT_REQUEST_ASSOCIATE] = enic_pp_associate,
@@ -135,28 +186,49 @@ static int (*enic_pp_handlers[])(struct enic *enic,
static const int enic_pp_handlers_count =
sizeof(enic_pp_handlers)/sizeof(*enic_pp_handlers);
-static int enic_pp_preassociate(struct enic *enic,
+static int enic_pp_preassociate(struct enic *enic, int vf,
struct enic_port_profile *prev_pp, int *restore_pp)
{
return -EOPNOTSUPP;
}
-static int enic_pp_disassociate(struct enic *enic,
+static int enic_pp_disassociate(struct enic *enic, int vf,
struct enic_port_profile *prev_pp, int *restore_pp)
{
- return enic_unset_port_profile(enic);
+ struct net_device *netdev = enic->netdev;
+ struct enic_port_profile *pp;
+ int err;
+
+ ENIC_PP_BY_INDEX(enic, vf, pp, &err);
+ if (err)
+ return err;
+
+ /* Deregister mac addresses */
+ if (!is_zero_ether_addr(pp->mac_addr))
+ ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_del_addr,
+ pp->mac_addr);
+ else if (!is_zero_ether_addr(netdev->dev_addr))
+ ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_del_addr,
+ netdev->dev_addr);
+
+ return enic_unset_port_profile(enic, vf);
}
-static int enic_pp_preassociate_rr(struct enic *enic,
+static int enic_pp_preassociate_rr(struct enic *enic, int vf,
struct enic_port_profile *prev_pp, int *restore_pp)
{
+ struct enic_port_profile *pp;
int err;
int active = 0;
- if (enic->pp.request != PORT_REQUEST_ASSOCIATE) {
+ ENIC_PP_BY_INDEX(enic, vf, pp, &err);
+ if (err)
+ return err;
+
+ if (pp->request != PORT_REQUEST_ASSOCIATE) {
/* If pre-associate is not part of an associate.
We always disassociate first */
- err = enic_pp_handlers[PORT_REQUEST_DISASSOCIATE](enic,
+ err = enic_pp_handlers[PORT_REQUEST_DISASSOCIATE](enic, vf,
prev_pp, restore_pp);
if (err)
return err;
@@ -166,29 +238,39 @@ static int enic_pp_preassociate_rr(struct enic *enic,
*restore_pp = 0;
- err = enic_set_port_profile(enic);
+ err = enic_set_port_profile(enic, vf);
if (err)
return err;
/* If pre-associate is not part of an associate. */
- if (enic->pp.request != PORT_REQUEST_ASSOCIATE)
- err = enic_dev_status_to_errno(enic_dev_enable2(enic, active));
+ if (pp->request != PORT_REQUEST_ASSOCIATE) {
+ /* Enable device as standby */
+ ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_enable2,
+ active);
+ err = enic_dev_status_to_errno(err);
+ }
return err;
}
-static int enic_pp_associate(struct enic *enic,
+static int enic_pp_associate(struct enic *enic, int vf,
struct enic_port_profile *prev_pp, int *restore_pp)
{
+ struct net_device *netdev = enic->netdev;
+ struct enic_port_profile *pp;
int err;
int active = 1;
+ ENIC_PP_BY_INDEX(enic, vf, pp, &err);
+ if (err)
+ return err;
+
/* Check if a pre-associate was called before */
if (prev_pp->request != PORT_REQUEST_PREASSOCIATE_RR ||
(prev_pp->request == PORT_REQUEST_PREASSOCIATE_RR &&
- enic_are_pp_different(prev_pp, &enic->pp))) {
+ enic_are_pp_different(prev_pp, pp))) {
err = enic_pp_handlers[PORT_REQUEST_DISASSOCIATE](
- enic, prev_pp, restore_pp);
+ enic, vf, prev_pp, restore_pp);
if (err)
return err;
@@ -196,28 +278,48 @@ static int enic_pp_associate(struct enic *enic,
}
err = enic_pp_handlers[PORT_REQUEST_PREASSOCIATE_RR](
- enic, prev_pp, restore_pp);
+ enic, vf, prev_pp, restore_pp);
if (err)
return err;
*restore_pp = 0;
- return enic_dev_status_to_errno(enic_dev_enable2(enic, active));
+ /* Enable device as active */
+ ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_enable2, active);
+ err = enic_dev_status_to_errno(err);
+ if (err)
+ return err;
+
+ /* Register mac address */
+ if (!is_zero_ether_addr(pp->mac_addr))
+ ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_add_addr,
+ pp->mac_addr);
+ else if (!is_zero_ether_addr(netdev->dev_addr))
+ ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_add_addr,
+ netdev->dev_addr);
+
+ return 0;
}
-int enic_process_set_pp_request(struct enic *enic,
+int enic_process_set_pp_request(struct enic *enic, int vf,
struct enic_port_profile *prev_pp, int *restore_pp)
{
- if (enic->pp.request < enic_pp_handlers_count
- && enic_pp_handlers[enic->pp.request])
- return enic_pp_handlers[enic->pp.request](enic,
- prev_pp, restore_pp);
- else
+ struct enic_port_profile *pp;
+ int err;
+
+ ENIC_PP_BY_INDEX(enic, vf, pp, &err);
+ if (err)
+ return err;
+
+ if (pp->request >= enic_pp_handlers_count
+ || !enic_pp_handlers[pp->request])
return -EOPNOTSUPP;
+
+ return enic_pp_handlers[pp->request](enic, vf, prev_pp, restore_pp);
}
-int enic_process_get_pp_request(struct enic *enic, int request,
- u16 *response)
+int enic_process_get_pp_request(struct enic *enic, int vf,
+ int request, u16 *response)
{
int err, status = ERR_SUCCESS;
@@ -225,11 +327,13 @@ int enic_process_get_pp_request(struct enic *enic, int request,
case PORT_REQUEST_PREASSOCIATE_RR:
case PORT_REQUEST_ASSOCIATE:
- err = enic_dev_enable2_done(enic, &status);
+ ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic,
+ vnic_dev_enable2_done, &status);
break;
case PORT_REQUEST_DISASSOCIATE:
- err = enic_dev_deinit_done(enic, &status);
+ ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic,
+ vnic_dev_deinit_done, &status);
break;
default:
diff --git a/drivers/net/enic/enic_pp.h b/drivers/net/ethernet/cisco/enic/enic_pp.h
index 699e365a944d..a09ff392c1c6 100644
--- a/drivers/net/enic/enic_pp.h
+++ b/drivers/net/ethernet/cisco/enic/enic_pp.h
@@ -19,9 +19,18 @@
#ifndef _ENIC_PP_H_
#define _ENIC_PP_H_
-int enic_process_set_pp_request(struct enic *enic,
+#define ENIC_PP_BY_INDEX(enic, vf, pp, err) \
+ do { \
+ if (enic_is_valid_pp_vf(enic, vf, err)) \
+ pp = (vf == PORT_SELF_VF) ? enic->pp : enic->pp + vf; \
+ else \
+ pp = NULL; \
+ } while (0)
+
+int enic_process_set_pp_request(struct enic *enic, int vf,
struct enic_port_profile *prev_pp, int *restore_pp);
-int enic_process_get_pp_request(struct enic *enic, int request,
- u16 *response);
+int enic_process_get_pp_request(struct enic *enic, int vf,
+ int request, u16 *response);
+int enic_is_valid_pp_vf(struct enic *enic, int vf, int *err);
#endif /* _ENIC_PP_H_ */
diff --git a/drivers/net/enic/enic_res.c b/drivers/net/ethernet/cisco/enic/enic_res.c
index 6e5c6356e7df..4a35367de790 100644
--- a/drivers/net/enic/enic_res.c
+++ b/drivers/net/ethernet/cisco/enic/enic_res.c
@@ -90,18 +90,30 @@ int enic_get_vnic_config(struct enic *enic)
max_t(u16, ENIC_MIN_MTU,
c->mtu));
- c->intr_timer_usec = min_t(u32,
- INTR_COALESCE_HW_TO_USEC(VNIC_INTR_TIMER_MAX),
- c->intr_timer_usec);
+ c->intr_timer_usec = min_t(u32, c->intr_timer_usec,
+ vnic_dev_get_intr_coal_timer_max(enic->vdev));
dev_info(enic_get_dev(enic),
"vNIC MAC addr %pM wq/rq %d/%d mtu %d\n",
enic->mac_addr, c->wq_desc_count, c->rq_desc_count, c->mtu);
- dev_info(enic_get_dev(enic), "vNIC csum tx/rx %d/%d "
- "tso %d intr timer %d usec rss %d\n",
- ENIC_SETTING(enic, TXCSUM), ENIC_SETTING(enic, RXCSUM),
- ENIC_SETTING(enic, TSO),
- c->intr_timer_usec, ENIC_SETTING(enic, RSS));
+
+ dev_info(enic_get_dev(enic), "vNIC csum tx/rx %s/%s "
+ "tso/lro %s/%s rss %s intr mode %s type %s timer %d usec "
+ "loopback tag 0x%04x\n",
+ ENIC_SETTING(enic, TXCSUM) ? "yes" : "no",
+ ENIC_SETTING(enic, RXCSUM) ? "yes" : "no",
+ ENIC_SETTING(enic, TSO) ? "yes" : "no",
+ ENIC_SETTING(enic, LRO) ? "yes" : "no",
+ ENIC_SETTING(enic, RSS) ? "yes" : "no",
+ c->intr_mode == VENET_INTR_MODE_INTX ? "INTx" :
+ c->intr_mode == VENET_INTR_MODE_MSI ? "MSI" :
+ c->intr_mode == VENET_INTR_MODE_ANY ? "any" :
+ "unknown",
+ c->intr_timer_type == VENET_INTR_TYPE_MIN ? "min" :
+ c->intr_timer_type == VENET_INTR_TYPE_IDLE ? "idle" :
+ "unknown",
+ c->intr_timer_usec,
+ c->loop_tag);
return 0;
}
@@ -290,7 +302,7 @@ void enic_init_vnic_resources(struct enic *enic)
for (i = 0; i < enic->intr_count; i++) {
vnic_intr_init(&enic->intr[i],
- INTR_COALESCE_USEC_TO_HW(enic->config.intr_timer_usec),
+ enic->config.intr_timer_usec,
enic->config.intr_timer_type,
mask_on_assertion);
}
diff --git a/drivers/net/enic/enic_res.h b/drivers/net/ethernet/cisco/enic/enic_res.h
index 25be2734c3fe..25be2734c3fe 100644
--- a/drivers/net/enic/enic_res.h
+++ b/drivers/net/ethernet/cisco/enic/enic_res.h
diff --git a/drivers/net/enic/rq_enet_desc.h b/drivers/net/ethernet/cisco/enic/rq_enet_desc.h
index e6dd30988d6f..e6dd30988d6f 100644
--- a/drivers/net/enic/rq_enet_desc.h
+++ b/drivers/net/ethernet/cisco/enic/rq_enet_desc.h
diff --git a/drivers/net/enic/vnic_cq.c b/drivers/net/ethernet/cisco/enic/vnic_cq.c
index b86d6ef8dad3..0daa1c7073cb 100644
--- a/drivers/net/enic/vnic_cq.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_cq.c
@@ -74,6 +74,8 @@ void vnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable,
iowrite32(cq_message_enable, &cq->ctrl->cq_message_enable);
iowrite32(interrupt_offset, &cq->ctrl->interrupt_offset);
writeq(cq_message_addr, &cq->ctrl->cq_message_addr);
+
+ cq->interrupt_offset = interrupt_offset;
}
void vnic_cq_clean(struct vnic_cq *cq)
diff --git a/drivers/net/enic/vnic_cq.h b/drivers/net/ethernet/cisco/enic/vnic_cq.h
index 552d3daf2508..579315cbe803 100644
--- a/drivers/net/enic/vnic_cq.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_cq.h
@@ -57,6 +57,7 @@ struct vnic_cq {
struct vnic_dev_ring ring;
unsigned int to_clean;
unsigned int last_color;
+ unsigned int interrupt_offset;
};
static inline unsigned int vnic_cq_service(struct vnic_cq *cq,
diff --git a/drivers/net/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c
index 68f24ae860ae..31e7f9bc2067 100644
--- a/drivers/net/enic/vnic_dev.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c
@@ -32,6 +32,7 @@
enum vnic_proxy_type {
PROXY_NONE,
PROXY_BY_BDF,
+ PROXY_BY_INDEX,
};
struct vnic_res {
@@ -40,6 +41,12 @@ struct vnic_res {
unsigned int count;
};
+struct vnic_intr_coal_timer_info {
+ u32 mul;
+ u32 div;
+ u32 max_usec;
+};
+
struct vnic_dev {
void *priv;
struct pci_dev *pdev;
@@ -58,6 +65,7 @@ struct vnic_dev {
enum vnic_proxy_type proxy;
u32 proxy_index;
u64 args[VNIC_DEVCMD_NARGS];
+ struct vnic_intr_coal_timer_info intr_coal_timer_info;
};
#define VNIC_MAX_RES_HDR_SIZE \
@@ -321,20 +329,21 @@ static int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
return -ETIMEDOUT;
}
-static int vnic_dev_cmd_proxy_by_bdf(struct vnic_dev *vdev,
- enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1, int wait)
+static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
+ enum vnic_devcmd_cmd proxy_cmd, enum vnic_devcmd_cmd cmd,
+ u64 *a0, u64 *a1, int wait)
{
u32 status;
int err;
memset(vdev->args, 0, sizeof(vdev->args));
- vdev->args[0] = vdev->proxy_index; /* bdf */
+ vdev->args[0] = vdev->proxy_index;
vdev->args[1] = cmd;
vdev->args[2] = *a0;
vdev->args[3] = *a1;
- err = _vnic_dev_cmd(vdev, CMD_PROXY_BY_BDF, wait);
+ err = _vnic_dev_cmd(vdev, proxy_cmd, wait);
if (err)
return err;
@@ -369,14 +378,30 @@ static int vnic_dev_cmd_no_proxy(struct vnic_dev *vdev,
return err;
}
+void vnic_dev_cmd_proxy_by_index_start(struct vnic_dev *vdev, u16 index)
+{
+ vdev->proxy = PROXY_BY_INDEX;
+ vdev->proxy_index = index;
+}
+
+void vnic_dev_cmd_proxy_end(struct vnic_dev *vdev)
+{
+ vdev->proxy = PROXY_NONE;
+ vdev->proxy_index = 0;
+}
+
int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
u64 *a0, u64 *a1, int wait)
{
memset(vdev->args, 0, sizeof(vdev->args));
switch (vdev->proxy) {
+ case PROXY_BY_INDEX:
+ return vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_INDEX, cmd,
+ a0, a1, wait);
case PROXY_BY_BDF:
- return vnic_dev_cmd_proxy_by_bdf(vdev, cmd, a0, a1, wait);
+ return vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_BDF, cmd,
+ a0, a1, wait);
case PROXY_NONE:
default:
return vnic_dev_cmd_no_proxy(vdev, cmd, a0, a1, wait);
@@ -794,6 +819,42 @@ int vnic_dev_deinit(struct vnic_dev *vdev)
return vnic_dev_cmd(vdev, CMD_DEINIT, &a0, &a1, wait);
}
+void vnic_dev_intr_coal_timer_info_default(struct vnic_dev *vdev)
+{
+ /* Default: hardware intr coal timer is in units of 1.5 usecs */
+ vdev->intr_coal_timer_info.mul = 2;
+ vdev->intr_coal_timer_info.div = 3;
+ vdev->intr_coal_timer_info.max_usec =
+ vnic_dev_intr_coal_timer_hw_to_usec(vdev, 0xffff);
+}
+
+int vnic_dev_intr_coal_timer_info(struct vnic_dev *vdev)
+{
+ int wait = 1000;
+ int err;
+
+ memset(vdev->args, 0, sizeof(vdev->args));
+
+ err = _vnic_dev_cmd(vdev, CMD_INTR_COAL_CONVERT, wait);
+
+ /* Use defaults when firmware doesn't support the devcmd at all or
+ * supports it for only specific hardware
+ */
+ if ((err == ERR_ECMDUNKNOWN) ||
+ (!err && !(vdev->args[0] && vdev->args[1] && vdev->args[2]))) {
+ pr_warning("Using default conversion factor for "
+ "interrupt coalesce timer\n");
+ vnic_dev_intr_coal_timer_info_default(vdev);
+ return 0;
+ }
+
+ vdev->intr_coal_timer_info.mul = (u32) vdev->args[0];
+ vdev->intr_coal_timer_info.div = (u32) vdev->args[1];
+ vdev->intr_coal_timer_info.max_usec = (u32) vdev->args[2];
+
+ return err;
+}
+
int vnic_dev_link_status(struct vnic_dev *vdev)
{
if (!vnic_dev_notify_ready(vdev))
@@ -838,6 +899,23 @@ enum vnic_dev_intr_mode vnic_dev_get_intr_mode(
return vdev->intr_mode;
}
+u32 vnic_dev_intr_coal_timer_usec_to_hw(struct vnic_dev *vdev, u32 usec)
+{
+ return (usec * vdev->intr_coal_timer_info.mul) /
+ vdev->intr_coal_timer_info.div;
+}
+
+u32 vnic_dev_intr_coal_timer_hw_to_usec(struct vnic_dev *vdev, u32 hw_cycles)
+{
+ return (hw_cycles * vdev->intr_coal_timer_info.div) /
+ vdev->intr_coal_timer_info.mul;
+}
+
+u32 vnic_dev_get_intr_coal_timer_max(struct vnic_dev *vdev)
+{
+ return vdev->intr_coal_timer_info.max_usec;
+}
+
void vnic_dev_unregister(struct vnic_dev *vdev)
{
if (vdev) {
diff --git a/drivers/net/enic/vnic_dev.h b/drivers/net/ethernet/cisco/enic/vnic_dev.h
index cf482a2c9dd9..6a138b625d13 100644
--- a/drivers/net/enic/vnic_dev.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_dev.h
@@ -85,6 +85,8 @@ void vnic_dev_free_desc_ring(struct vnic_dev *vdev,
struct vnic_dev_ring *ring);
int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
u64 *a0, u64 *a1, int wait);
+void vnic_dev_cmd_proxy_by_index_start(struct vnic_dev *vdev, u16 index);
+void vnic_dev_cmd_proxy_end(struct vnic_dev *vdev);
int vnic_dev_fw_info(struct vnic_dev *vdev,
struct vnic_devcmd_fw_info **fw_info);
int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size,
@@ -109,11 +111,16 @@ int vnic_dev_open(struct vnic_dev *vdev, int arg);
int vnic_dev_open_done(struct vnic_dev *vdev, int *done);
int vnic_dev_init(struct vnic_dev *vdev, int arg);
int vnic_dev_deinit(struct vnic_dev *vdev);
+void vnic_dev_intr_coal_timer_info_default(struct vnic_dev *vdev);
+int vnic_dev_intr_coal_timer_info(struct vnic_dev *vdev);
int vnic_dev_hang_reset(struct vnic_dev *vdev, int arg);
int vnic_dev_hang_reset_done(struct vnic_dev *vdev, int *done);
void vnic_dev_set_intr_mode(struct vnic_dev *vdev,
enum vnic_dev_intr_mode intr_mode);
enum vnic_dev_intr_mode vnic_dev_get_intr_mode(struct vnic_dev *vdev);
+u32 vnic_dev_intr_coal_timer_usec_to_hw(struct vnic_dev *vdev, u32 usec);
+u32 vnic_dev_intr_coal_timer_hw_to_usec(struct vnic_dev *vdev, u32 hw_cycles);
+u32 vnic_dev_get_intr_coal_timer_max(struct vnic_dev *vdev);
void vnic_dev_unregister(struct vnic_dev *vdev);
int vnic_dev_set_ig_vlan_rewrite_mode(struct vnic_dev *vdev,
u8 ig_vlan_rewrite_mode);
diff --git a/drivers/net/enic/vnic_devcmd.h b/drivers/net/ethernet/cisco/enic/vnic_devcmd.h
index c5569bfb47ac..8025e8808d61 100644
--- a/drivers/net/enic/vnic_devcmd.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_devcmd.h
@@ -318,6 +318,25 @@ enum vnic_devcmd_cmd {
* ERR_EINPROGRESS - command in a0 is still in progress
*/
CMD_STATUS = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 49),
+
+ /*
+ * Returns interrupt coalescing timer conversion factors.
+ * After calling this devcmd, ENIC driver can convert
+ * interrupt coalescing timer in usec into CPU cycles as follows:
+ *
+ * intr_timer_cycles = intr_timer_usec * multiplier / divisor
+ *
+ * Interrupt coalescing timer in usecs can be obtained from
+ * CPU cycles as follows:
+ *
+ * intr_timer_usec = intr_timer_cycles * divisor / multiplier
+ *
+ * in: none
+ * out: (u32)a0 = multiplier
+ * (u32)a1 = divisor
+ * (u32)a2 = maximum timer value in usec
+ */
+ CMD_INTR_COAL_CONVERT = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 50),
};
/* CMD_ENABLE2 flags */
diff --git a/drivers/net/enic/vnic_enet.h b/drivers/net/ethernet/cisco/enic/vnic_enet.h
index e8740e3704e4..609542848e02 100644
--- a/drivers/net/enic/vnic_enet.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_enet.h
@@ -20,10 +20,6 @@
#ifndef _VNIC_ENIC_H_
#define _VNIC_ENIC_H_
-/* Hardware intr coalesce timer is in units of 1.5us */
-#define INTR_COALESCE_USEC_TO_HW(usec) ((usec) * 2/3)
-#define INTR_COALESCE_HW_TO_USEC(usec) ((usec) * 3/2)
-
/* Device-specific region: enet configuration */
struct vnic_enet_config {
u32 flags;
@@ -51,4 +47,11 @@ struct vnic_enet_config {
#define VENETF_RSSHASH_TCPIPV6_EX 0x400 /* Hash on TCP + IPv6 ext. fields */
#define VENETF_LOOP 0x800 /* Loopback enabled */
+#define VENET_INTR_TYPE_MIN 0 /* Timer specs min interrupt spacing */
+#define VENET_INTR_TYPE_IDLE 1 /* Timer specs idle time before irq */
+
+#define VENET_INTR_MODE_ANY 0 /* Try MSI-X, then MSI, then INTx */
+#define VENET_INTR_MODE_MSI 1 /* Try MSI then INTx */
+#define VENET_INTR_MODE_INTX 2 /* Try INTx only */
+
#endif /* _VNIC_ENIC_H_ */
diff --git a/drivers/net/enic/vnic_intr.c b/drivers/net/ethernet/cisco/enic/vnic_intr.c
index 3873771d75cc..0ca107f7bc8c 100644
--- a/drivers/net/enic/vnic_intr.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_intr.c
@@ -46,7 +46,7 @@ int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
return 0;
}
-void vnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer,
+void vnic_intr_init(struct vnic_intr *intr, u32 coalescing_timer,
unsigned int coalescing_type, unsigned int mask_on_assertion)
{
vnic_intr_coalescing_timer_set(intr, coalescing_timer);
@@ -56,9 +56,10 @@ void vnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer,
}
void vnic_intr_coalescing_timer_set(struct vnic_intr *intr,
- unsigned int coalescing_timer)
+ u32 coalescing_timer)
{
- iowrite32(coalescing_timer, &intr->ctrl->coalescing_timer);
+ iowrite32(vnic_dev_intr_coal_timer_usec_to_hw(intr->vdev,
+ coalescing_timer), &intr->ctrl->coalescing_timer);
}
void vnic_intr_clean(struct vnic_intr *intr)
diff --git a/drivers/net/enic/vnic_intr.h b/drivers/net/ethernet/cisco/enic/vnic_intr.h
index 09dc0b73ff46..2b1636392294 100644
--- a/drivers/net/enic/vnic_intr.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_intr.h
@@ -24,8 +24,6 @@
#include "vnic_dev.h"
-#define VNIC_INTR_TIMER_MAX 0xffff
-
#define VNIC_INTR_TIMER_TYPE_ABS 0
#define VNIC_INTR_TIMER_TYPE_QUIET 1
@@ -104,10 +102,10 @@ static inline u32 vnic_intr_legacy_pba(u32 __iomem *legacy_pba)
void vnic_intr_free(struct vnic_intr *intr);
int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
unsigned int index);
-void vnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer,
+void vnic_intr_init(struct vnic_intr *intr, u32 coalescing_timer,
unsigned int coalescing_type, unsigned int mask_on_assertion);
void vnic_intr_coalescing_timer_set(struct vnic_intr *intr,
- unsigned int coalescing_timer);
+ u32 coalescing_timer);
void vnic_intr_clean(struct vnic_intr *intr);
#endif /* _VNIC_INTR_H_ */
diff --git a/drivers/net/enic/vnic_nic.h b/drivers/net/ethernet/cisco/enic/vnic_nic.h
index 995a50dd4c99..995a50dd4c99 100644
--- a/drivers/net/enic/vnic_nic.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_nic.h
diff --git a/drivers/net/enic/vnic_resource.h b/drivers/net/ethernet/cisco/enic/vnic_resource.h
index e0a73f1ca6f4..e0a73f1ca6f4 100644
--- a/drivers/net/enic/vnic_resource.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_resource.h
diff --git a/drivers/net/enic/vnic_rq.c b/drivers/net/ethernet/cisco/enic/vnic_rq.c
index 34105e0951a5..34105e0951a5 100644
--- a/drivers/net/enic/vnic_rq.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_rq.c
diff --git a/drivers/net/enic/vnic_rq.h b/drivers/net/ethernet/cisco/enic/vnic_rq.h
index 2056586f4d4b..2056586f4d4b 100644
--- a/drivers/net/enic/vnic_rq.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_rq.h
diff --git a/drivers/net/enic/vnic_rss.h b/drivers/net/ethernet/cisco/enic/vnic_rss.h
index fa421baf45b8..fa421baf45b8 100644
--- a/drivers/net/enic/vnic_rss.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_rss.h
diff --git a/drivers/net/enic/vnic_stats.h b/drivers/net/ethernet/cisco/enic/vnic_stats.h
index 77750ec93954..77750ec93954 100644
--- a/drivers/net/enic/vnic_stats.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_stats.h
diff --git a/drivers/net/enic/vnic_vic.c b/drivers/net/ethernet/cisco/enic/vnic_vic.c
index 24ef8cd40545..24ef8cd40545 100644
--- a/drivers/net/enic/vnic_vic.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_vic.c
diff --git a/drivers/net/enic/vnic_vic.h b/drivers/net/ethernet/cisco/enic/vnic_vic.h
index 9ef81f148351..9ef81f148351 100644
--- a/drivers/net/enic/vnic_vic.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_vic.h
diff --git a/drivers/net/enic/vnic_wq.c b/drivers/net/ethernet/cisco/enic/vnic_wq.c
index df61bd932ea6..df61bd932ea6 100644
--- a/drivers/net/enic/vnic_wq.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_wq.c
diff --git a/drivers/net/enic/vnic_wq.h b/drivers/net/ethernet/cisco/enic/vnic_wq.h
index 7dd937ac11c2..7dd937ac11c2 100644
--- a/drivers/net/enic/vnic_wq.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_wq.h
diff --git a/drivers/net/enic/wq_enet_desc.h b/drivers/net/ethernet/cisco/enic/wq_enet_desc.h
index c7021e3a631f..c7021e3a631f 100644
--- a/drivers/net/enic/wq_enet_desc.h
+++ b/drivers/net/ethernet/cisco/enic/wq_enet_desc.h
diff --git a/drivers/net/ethernet/davicom/Kconfig b/drivers/net/ethernet/davicom/Kconfig
new file mode 100644
index 000000000000..972b62b31837
--- /dev/null
+++ b/drivers/net/ethernet/davicom/Kconfig
@@ -0,0 +1,24 @@
+#
+# Davicom device configuration
+#
+
+config DM9000
+ tristate "DM9000 support"
+ depends on ARM || BLACKFIN || MIPS
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ Support for DM9000 chipset.
+
+ To compile this driver as a module, choose M here. The module
+ will be called dm9000.
+
+config DM9000_FORCE_SIMPLE_PHY_POLL
+ bool "Force simple NSR based PHY polling"
+ depends on DM9000
+ ---help---
+ This configuration forces the DM9000 to use the NSR's LinkStatus
+ bit to determine if the link is up or down instead of the more
+ costly MII PHY reads. Note, this will not work if the chip is
+ operating with an external PHY.
diff --git a/drivers/net/ethernet/davicom/Makefile b/drivers/net/ethernet/davicom/Makefile
new file mode 100644
index 000000000000..74b31f0ebe18
--- /dev/null
+++ b/drivers/net/ethernet/davicom/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Davicom device drivers.
+#
+
+obj-$(CONFIG_DM9000) += dm9000.o
diff --git a/drivers/net/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c
index ee597e676ee5..438f4580bf66 100644
--- a/drivers/net/dm9000.c
+++ b/drivers/net/ethernet/davicom/dm9000.c
@@ -24,6 +24,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/crc32.h>
@@ -55,6 +56,13 @@ static int watchdog = 5000;
module_param(watchdog, int, 0400);
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
+/*
+ * Debug messages level
+ */
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "dm9000 debug level (0-4)");
+
/* DM9000 register address locking.
*
* The DM9000 uses an address register to control where data written
@@ -102,7 +110,6 @@ typedef struct board_info {
unsigned int flags;
unsigned int in_suspend :1;
unsigned int wake_supported :1;
- int debug_level;
enum dm9000_type type;
@@ -137,8 +144,7 @@ typedef struct board_info {
/* debug code */
#define dm9000_dbg(db, lev, msg...) do { \
- if ((lev) < CONFIG_DM9000_DEBUGLEVEL && \
- (lev) < db->debug_level) { \
+ if ((lev) < debug) { \
dev_dbg(db->dev, msg); \
} \
} while (0)
@@ -534,21 +540,35 @@ static int dm9000_set_eeprom(struct net_device *dev,
board_info_t *dm = to_dm9000_board(dev);
int offset = ee->offset;
int len = ee->len;
- int i;
+ int done;
/* EEPROM access is aligned to two bytes */
- if ((len & 1) != 0 || (offset & 1) != 0)
- return -EINVAL;
-
if (dm->flags & DM9000_PLATF_NO_EEPROM)
return -ENOENT;
if (ee->magic != DM_EEPROM_MAGIC)
return -EINVAL;
- for (i = 0; i < len; i += 2)
- dm9000_write_eeprom(dm, (offset + i) / 2, data + i);
+ while (len > 0) {
+ if (len & 1 || offset & 1) {
+ int which = offset & 1;
+ u8 tmp[2];
+
+ dm9000_read_eeprom(dm, offset / 2, tmp);
+ tmp[which] = *data;
+ dm9000_write_eeprom(dm, offset / 2, tmp);
+
+ done = 1;
+ } else {
+ dm9000_write_eeprom(dm, offset / 2, data);
+ done = 2;
+ }
+
+ data += done;
+ offset += done;
+ len -= done;
+ }
return 0;
}
@@ -1324,7 +1344,7 @@ static const struct net_device_ops dm9000_netdev_ops = {
.ndo_stop = dm9000_stop,
.ndo_start_xmit = dm9000_start_xmit,
.ndo_tx_timeout = dm9000_timeout,
- .ndo_set_multicast_list = dm9000_hash_table,
+ .ndo_set_rx_mode = dm9000_hash_table,
.ndo_do_ioctl = dm9000_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_features = dm9000_set_features,
diff --git a/drivers/net/dm9000.h b/drivers/net/ethernet/davicom/dm9000.h
index 55688bd1a3ef..55688bd1a3ef 100644
--- a/drivers/net/dm9000.h
+++ b/drivers/net/ethernet/davicom/dm9000.h
diff --git a/drivers/net/ethernet/dec/Kconfig b/drivers/net/ethernet/dec/Kconfig
new file mode 100644
index 000000000000..37940279ded8
--- /dev/null
+++ b/drivers/net/ethernet/dec/Kconfig
@@ -0,0 +1,37 @@
+#
+# Digital Equipment Inc network device configuration
+#
+
+config NET_VENDOR_DEC
+ bool "Digital Equipment devices"
+ default y
+ depends on PCI || EISA || CARDBUS
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about DEC cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_DEC
+
+config EWRK3
+ tristate "EtherWORKS 3 (DE203, DE204, DE205) support"
+ depends on ISA
+ select CRC32
+ ---help---
+ This driver supports the DE203, DE204 and DE205 network (Ethernet)
+ cards. If this is for you, say Y and read
+ <file:Documentation/networking/ewrk3.txt> in the kernel source as
+ well as the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ewrk3.
+
+source "drivers/net/ethernet/dec/tulip/Kconfig"
+
+endif # NET_VENDOR_DEC
diff --git a/drivers/net/ethernet/dec/Makefile b/drivers/net/ethernet/dec/Makefile
new file mode 100644
index 000000000000..1b01ed8d42c8
--- /dev/null
+++ b/drivers/net/ethernet/dec/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Digital Equipment Inc. network device drivers.
+#
+
+obj-$(CONFIG_EWRK3) += ewrk3.o
+obj-$(CONFIG_NET_TULIP) += tulip/
diff --git a/drivers/net/ewrk3.c b/drivers/net/ethernet/dec/ewrk3.c
index b5f6173130f4..f9df5e4d0341 100644
--- a/drivers/net/ewrk3.c
+++ b/drivers/net/ethernet/dec/ewrk3.c
@@ -393,7 +393,7 @@ static const struct net_device_ops ewrk3_netdev_ops = {
.ndo_open = ewrk3_open,
.ndo_start_xmit = ewrk3_queue_pkt,
.ndo_stop = ewrk3_close,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_do_ioctl = ewrk3_ioctl,
.ndo_tx_timeout = ewrk3_timeout,
.ndo_change_mtu = eth_change_mtu,
@@ -1008,15 +1008,13 @@ static int ewrk3_rx(struct net_device *dev)
}
}
p = skb->data; /* Look at the dest addr */
- if (p[0] & 0x01) { /* Multicast/Broadcast */
- if ((*(s16 *) & p[0] == -1) && (*(s16 *) & p[2] == -1) && (*(s16 *) & p[4] == -1)) {
+ if (is_multicast_ether_addr(p)) {
+ if (is_broadcast_ether_addr(p)) {
lp->pktStats.broadcast++;
} else {
lp->pktStats.multicast++;
}
- } else if ((*(s16 *) & p[0] == *(s16 *) & dev->dev_addr[0]) &&
- (*(s16 *) & p[2] == *(s16 *) & dev->dev_addr[2]) &&
- (*(s16 *) & p[4] == *(s16 *) & dev->dev_addr[4])) {
+ } else if (compare_ether_addr(p, dev->dev_addr) == 0) {
lp->pktStats.unicast++;
}
lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
@@ -1171,7 +1169,7 @@ static void SetMulticastFilter(struct net_device *dev)
struct netdev_hw_addr *ha;
u_long iobase = dev->base_addr;
int i;
- char *addrs, bit, byte;
+ char bit, byte;
short __iomem *p = lp->mctbl;
u16 hashcode;
u32 crc;
@@ -1213,25 +1211,22 @@ static void SetMulticastFilter(struct net_device *dev)
/* Update table */
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
- if ((*addrs & 0x01) == 1) { /* multicast address? */
- crc = ether_crc_le(ETH_ALEN, addrs);
- hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */
+ crc = ether_crc_le(ETH_ALEN, ha->addr);
+ hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */
- byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
- bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+ byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
- if (lp->shmem_length == IO_ONLY) {
- u_char tmp;
+ if (lp->shmem_length == IO_ONLY) {
+ u_char tmp;
- outw(PAGE0_HTE + byte, EWRK3_PIR1);
- tmp = inb(EWRK3_DATA);
- tmp |= bit;
- outw(PAGE0_HTE + byte, EWRK3_PIR1);
- outb(tmp, EWRK3_DATA);
- } else {
- writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte);
- }
+ outw(PAGE0_HTE + byte, EWRK3_PIR1);
+ tmp = inb(EWRK3_DATA);
+ tmp |= bit;
+ outw(PAGE0_HTE + byte, EWRK3_PIR1);
+ outb(tmp, EWRK3_DATA);
+ } else {
+ writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte);
}
}
}
diff --git a/drivers/net/ewrk3.h b/drivers/net/ethernet/dec/ewrk3.h
index 8e0ee906567b..8e0ee906567b 100644
--- a/drivers/net/ewrk3.h
+++ b/drivers/net/ethernet/dec/ewrk3.h
diff --git a/drivers/net/tulip/21142.c b/drivers/net/ethernet/dec/tulip/21142.c
index 092c3faa882a..25b8deedbef8 100644
--- a/drivers/net/tulip/21142.c
+++ b/drivers/net/ethernet/dec/tulip/21142.c
@@ -7,9 +7,6 @@
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
- Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
- for more information on this driver.
-
DC21143 manual "21143 PCI/CardBus 10/100Mb/s Ethernet LAN Controller
Hardware Reference Manual" is currently available at :
http://developer.intel.com/design/network/manuals/278074.htm
diff --git a/drivers/net/tulip/Kconfig b/drivers/net/ethernet/dec/tulip/Kconfig
index 1f8d4a8d8ea4..1203be0436e2 100644
--- a/drivers/net/tulip/Kconfig
+++ b/drivers/net/ethernet/dec/tulip/Kconfig
@@ -2,10 +2,10 @@
# Tulip family network device configuration
#
-menuconfig NET_TULIP
- bool "\"Tulip\" family network device support"
- depends on PCI || EISA || CARDBUS
- help
+config NET_TULIP
+ bool "DEC - Tulip devices"
+ depends on (PCI || EISA || CARDBUS)
+ ---help---
This selects the "Tulip" family of EISA/PCI network cards.
if NET_TULIP
@@ -32,7 +32,7 @@ config DE2104X_DSL
depends on DE2104X
range 0 31
default 0
- help
+ ---help---
Setting this value allows to align ring buffer descriptors into their
own cache lines. Value of 4 corresponds to the typical 32 byte line
(the descriptor is 16 bytes). This is necessary on systems that lack
@@ -59,7 +59,7 @@ config TULIP
config TULIP_MWI
bool "New bus configuration (EXPERIMENTAL)"
depends on TULIP && EXPERIMENTAL
- help
+ ---help---
This configures your Tulip card specifically for the card and
system cache line size type you are using.
@@ -70,7 +70,7 @@ config TULIP_MWI
config TULIP_MMIO
bool "Use PCI shared mem for NIC registers"
depends on TULIP
- help
+ ---help---
Use PCI shared memory for the NIC registers, rather than going through
the Tulip's PIO (programmed I/O ports). Faster, but could produce
obscure bugs if your mainboard has memory controller timing issues.
@@ -79,7 +79,7 @@ config TULIP_MMIO
config TULIP_NAPI
bool "Use RX polling (NAPI)"
depends on TULIP
- help
+ ---help---
NAPI is a new driver API designed to reduce CPU and interrupt load
when the driver is receiving lots of packets from the card. It is
still somewhat experimental and thus not yet enabled by default.
@@ -107,7 +107,7 @@ config TULIP_DM910X
config DE4X5
tristate "Generic DECchip & DIGITAL EtherWORKS PCI/EISA"
- depends on PCI || EISA
+ depends on (PCI || EISA)
select CRC32
---help---
This is support for the DIGITAL series of PCI/EISA Ethernet cards.
@@ -125,8 +125,9 @@ config WINBOND_840
tristate "Winbond W89c840 Ethernet support"
depends on PCI
select CRC32
+ select NET_CORE
select MII
- help
+ ---help---
This driver is for the Winbond W89c840 chip. It also works with
the TX9882 chip on the Compex RL100-ATX board.
More specific information and updates are available from
diff --git a/drivers/net/tulip/Makefile b/drivers/net/ethernet/dec/tulip/Makefile
index 5e8be38b45bb..5e8be38b45bb 100644
--- a/drivers/net/tulip/Makefile
+++ b/drivers/net/ethernet/dec/tulip/Makefile
diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c
index e2f692351180..1427739d9a51 100644
--- a/drivers/net/tulip/de2104x.c
+++ b/drivers/net/ethernet/dec/tulip/de2104x.c
@@ -38,6 +38,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
@@ -1955,7 +1956,7 @@ bad_srom:
static const struct net_device_ops de_netdev_ops = {
.ndo_open = de_open,
.ndo_stop = de_close,
- .ndo_set_multicast_list = de_set_rx_mode,
+ .ndo_set_rx_mode = de_set_rx_mode,
.ndo_start_xmit = de_start_xmit,
.ndo_get_stats = de_get_stats,
.ndo_tx_timeout = de_tx_timeout,
diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c
index efaa1d69b720..871bcaa7068d 100644
--- a/drivers/net/tulip/de4x5.c
+++ b/drivers/net/ethernet/dec/tulip/de4x5.c
@@ -1084,7 +1084,7 @@ static const struct net_device_ops de4x5_netdev_ops = {
.ndo_stop = de4x5_close,
.ndo_start_xmit = de4x5_queue_pkt,
.ndo_get_stats = de4x5_get_stats,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_do_ioctl = de4x5_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address= eth_mac_addr,
@@ -1868,14 +1868,13 @@ de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len)
i = DE4X5_PKT_STAT_SZ;
}
}
- if (buf[0] & 0x01) { /* Multicast/Broadcast */
- if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) {
+ if (is_multicast_ether_addr(buf)) {
+ if (is_broadcast_ether_addr(buf)) {
lp->pktStats.broadcast++;
} else {
lp->pktStats.multicast++;
}
- } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) &&
- (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) {
+ } else if (compare_ether_addr(buf, dev->dev_addr) == 0) {
lp->pktStats.unicast++;
}
@@ -1964,9 +1963,7 @@ SetMulticastFilter(struct net_device *dev)
omr |= OMR_PM; /* Pass all multicasts */
} else if (lp->setup_f == HASH_PERF) { /* Hash Filtering */
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
- if ((*addrs & 0x01) == 1) { /* multicast address? */
- crc = ether_crc_le(ETH_ALEN, addrs);
+ crc = ether_crc_le(ETH_ALEN, ha->addr);
hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */
byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
@@ -1977,7 +1974,6 @@ SetMulticastFilter(struct net_device *dev)
byte -= 1;
}
lp->setup_frame[byte] |= bit;
- }
}
} else { /* Perfect filtering */
netdev_for_each_mc_addr(ha, dev) {
diff --git a/drivers/net/tulip/de4x5.h b/drivers/net/ethernet/dec/tulip/de4x5.h
index 9f2877438fb0..ec756eba397b 100644
--- a/drivers/net/tulip/de4x5.h
+++ b/drivers/net/ethernet/dec/tulip/de4x5.h
@@ -991,8 +991,6 @@
*/
#include <linux/sockios.h>
-#define DE4X5IOCTL SIOCDEVPRIVATE
-
struct de4x5_ioctl {
unsigned short cmd; /* Command to run */
unsigned short len; /* Length of the data buffer */
diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c
index 468512731966..17b11ee1745a 100644
--- a/drivers/net/tulip/dmfe.c
+++ b/drivers/net/ethernet/dec/tulip/dmfe.c
@@ -356,7 +356,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_open = dmfe_open,
.ndo_stop = dmfe_stop,
.ndo_start_xmit = dmfe_start_xmit,
- .ndo_set_multicast_list = dmfe_set_filter_mode,
+ .ndo_set_rx_mode = dmfe_set_filter_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
@@ -879,7 +879,6 @@ static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db)
txptr = db->tx_remove_ptr;
while(db->tx_packet_cnt) {
tdes0 = le32_to_cpu(txptr->tdes0);
- pr_debug("tdes0=%x\n", tdes0);
if (tdes0 & 0x80000000)
break;
@@ -889,7 +888,6 @@ static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db)
/* Transmit statistic counter */
if ( tdes0 != 0x7fffffff ) {
- pr_debug("tdes0=%x\n", tdes0);
dev->stats.collisions += (tdes0 >> 3) & 0xf;
dev->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff;
if (tdes0 & TDES0_ERR_MASK) {
@@ -986,7 +984,6 @@ static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db)
/* error summary bit check */
if (rdes0 & 0x8000) {
/* This is a error packet */
- pr_debug("rdes0: %x\n", rdes0);
dev->stats.rx_errors++;
if (rdes0 & 1)
dev->stats.rx_fifo_errors++;
@@ -1638,7 +1635,6 @@ static u8 dmfe_sense_speed(struct dmfe_board_info * db)
else /* DM9102/DM9102A */
phy_mode = phy_read(db->ioaddr,
db->phy_addr, 17, db->chip_id) & 0xf000;
- pr_debug("Phy_mode %x\n", phy_mode);
switch (phy_mode) {
case 0x1000: db->op_mode = DMFE_10MHF; break;
case 0x2000: db->op_mode = DMFE_10MFD; break;
diff --git a/drivers/net/tulip/eeprom.c b/drivers/net/ethernet/dec/tulip/eeprom.c
index fa5eee925f25..14d5b611783d 100644
--- a/drivers/net/tulip/eeprom.c
+++ b/drivers/net/ethernet/dec/tulip/eeprom.c
@@ -7,8 +7,6 @@
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
- Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
- for more information on this driver.
Please submit bug reports to http://bugzilla.kernel.org/.
*/
diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/ethernet/dec/tulip/interrupt.c
index 5350d753e0ff..4fb8c8c0a420 100644
--- a/drivers/net/tulip/interrupt.c
+++ b/drivers/net/ethernet/dec/tulip/interrupt.c
@@ -7,10 +7,7 @@
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
- Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
- for more information on this driver.
Please submit bugs to http://bugzilla.kernel.org/ .
-
*/
#include <linux/pci.h>
diff --git a/drivers/net/tulip/media.c b/drivers/net/ethernet/dec/tulip/media.c
index 4bd13922875d..beeb17b52ad4 100644
--- a/drivers/net/tulip/media.c
+++ b/drivers/net/ethernet/dec/tulip/media.c
@@ -7,9 +7,6 @@
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
- Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
- for more information on this driver.
-
Please submit bugs to http://bugzilla.kernel.org/ .
*/
diff --git a/drivers/net/tulip/pnic.c b/drivers/net/ethernet/dec/tulip/pnic.c
index aa4d9dad0395..9c16e4ad02a6 100644
--- a/drivers/net/tulip/pnic.c
+++ b/drivers/net/ethernet/dec/tulip/pnic.c
@@ -7,12 +7,10 @@
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
- Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
- for more information on this driver.
-
Please submit bugs to http://bugzilla.kernel.org/ .
*/
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include "tulip.h"
diff --git a/drivers/net/tulip/pnic2.c b/drivers/net/ethernet/dec/tulip/pnic2.c
index 93358ee4d830..04a7e477eaff 100644
--- a/drivers/net/tulip/pnic2.c
+++ b/drivers/net/ethernet/dec/tulip/pnic2.c
@@ -8,9 +8,6 @@
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
- Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
- for more information on this driver.
-
Please submit bugs to http://bugzilla.kernel.org/ .
*/
diff --git a/drivers/net/tulip/timer.c b/drivers/net/ethernet/dec/tulip/timer.c
index 2017faf2d0e6..19078d28ffb9 100644
--- a/drivers/net/tulip/timer.c
+++ b/drivers/net/ethernet/dec/tulip/timer.c
@@ -7,9 +7,6 @@
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
- Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
- for more information on this driver.
-
Please submit bugs to http://bugzilla.kernel.org/ .
*/
diff --git a/drivers/net/tulip/tulip.h b/drivers/net/ethernet/dec/tulip/tulip.h
index 9db528967da9..fb3887c18dc6 100644
--- a/drivers/net/tulip/tulip.h
+++ b/drivers/net/ethernet/dec/tulip/tulip.h
@@ -7,9 +7,6 @@
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
- Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
- for more information on this driver.
-
Please submit bugs to http://bugzilla.kernel.org/ .
*/
diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c
index 82f87647207e..9656dd0647d9 100644
--- a/drivers/net/tulip/tulip_core.c
+++ b/drivers/net/ethernet/dec/tulip/tulip_core.c
@@ -6,9 +6,6 @@
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
- Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
- for more information on this driver.
-
Please submit bugs to http://bugzilla.kernel.org/ .
*/
@@ -28,6 +25,7 @@
#include <linux/slab.h>
#include "tulip.h"
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/etherdevice.h>
#include <linux/delay.h>
#include <linux/mii.h>
@@ -1290,7 +1288,7 @@ static const struct net_device_ops tulip_netdev_ops = {
.ndo_stop = tulip_close,
.ndo_get_stats = tulip_get_stats,
.ndo_do_ioctl = private_ioctl,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c
index 9e63f406f72d..7a44a7a6adc8 100644
--- a/drivers/net/tulip/uli526x.c
+++ b/drivers/net/ethernet/dec/tulip/uli526x.c
@@ -259,7 +259,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_open = uli526x_open,
.ndo_stop = uli526x_stop,
.ndo_start_xmit = uli526x_start_xmit,
- .ndo_set_multicast_list = uli526x_set_filter_mode,
+ .ndo_set_rx_mode = uli526x_set_filter_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c
index 862eadf07191..4d01219ba22f 100644
--- a/drivers/net/tulip/winbond-840.c
+++ b/drivers/net/ethernet/dec/tulip/winbond-840.c
@@ -350,7 +350,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_stop = netdev_close,
.ndo_start_xmit = start_tx,
.ndo_get_stats = get_stats,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_do_ioctl = netdev_ioctl,
.ndo_tx_timeout = tx_timeout,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/ethernet/dec/tulip/xircom_cb.c
index 988b8eb24d37..988b8eb24d37 100644
--- a/drivers/net/tulip/xircom_cb.c
+++ b/drivers/net/ethernet/dec/tulip/xircom_cb.c
diff --git a/drivers/net/ethernet/dlink/Kconfig b/drivers/net/ethernet/dlink/Kconfig
new file mode 100644
index 000000000000..b5afe218c31b
--- /dev/null
+++ b/drivers/net/ethernet/dlink/Kconfig
@@ -0,0 +1,86 @@
+#
+# D-Link device configuration
+#
+
+config NET_VENDOR_DLINK
+ bool "D-Link devices"
+ default y
+ depends on PCI || PARPORT
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about D-Link devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_DLINK
+
+config DE600
+ tristate "D-Link DE600 pocket adapter support"
+ depends on PARPORT
+ ---help---
+ This is a network (Ethernet) device which attaches to your parallel
+ port. Read <file:Documentation/networking/DLINK.txt> as well as the
+ Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>, if you want to use
+ this. It is possible to have several devices share a single parallel
+ port and it is safe to compile the corresponding drivers into the
+ kernel.
+
+ To compile this driver as a module, choose M here: the module
+ will be called de600.
+
+config DE620
+ tristate "D-Link DE620 pocket adapter support"
+ depends on PARPORT
+ ---help---
+ This is a network (Ethernet) device which attaches to your parallel
+ port. Read <file:Documentation/networking/DLINK.txt> as well as the
+ Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>, if you want to use
+ this. It is possible to have several devices share a single parallel
+ port and it is safe to compile the corresponding drivers into the
+ kernel.
+
+ To compile this driver as a module, choose M here: the module
+ will be called de620.
+
+config DL2K
+ tristate "DL2000/TC902x-based Gigabit Ethernet support"
+ depends on PCI
+ select CRC32
+ ---help---
+ This driver supports DL2000/TC902x-based Gigabit ethernet cards,
+ which includes
+ D-Link DGE-550T Gigabit Ethernet Adapter.
+ D-Link DL2000-based Gigabit Ethernet Adapter.
+ Sundance/Tamarack TC902x Gigabit Ethernet Adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dl2k.
+
+config SUNDANCE
+ tristate "Sundance Alta support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This driver is for the Sundance "Alta" chip.
+ More specific information and updates are available from
+ <http://www.scyld.com/network/sundance.html>.
+
+config SUNDANCE_MMIO
+ bool "Use MMIO instead of PIO"
+ depends on SUNDANCE
+ ---help---
+ Enable memory-mapped I/O for interaction with Sundance NIC registers.
+ Do NOT enable this by default, PIO (enabled when MMIO is disabled)
+ is known to solve bugs on certain chips.
+
+ If unsure, say N.
+
+endif # NET_VENDOR_DLINK
diff --git a/drivers/net/ethernet/dlink/Makefile b/drivers/net/ethernet/dlink/Makefile
new file mode 100644
index 000000000000..c705eaa4f5b2
--- /dev/null
+++ b/drivers/net/ethernet/dlink/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the D-Link network device drivers.
+#
+
+obj-$(CONFIG_DE600) += de600.o
+obj-$(CONFIG_DE620) += de620.o
+obj-$(CONFIG_DL2K) += dl2k.o
+obj-$(CONFIG_SUNDANCE) += sundance.o
diff --git a/drivers/net/de600.c b/drivers/net/ethernet/dlink/de600.c
index 23a65398d011..23a65398d011 100644
--- a/drivers/net/de600.c
+++ b/drivers/net/ethernet/dlink/de600.c
diff --git a/drivers/net/de600.h b/drivers/net/ethernet/dlink/de600.h
index e80ecbabcf4e..e80ecbabcf4e 100644
--- a/drivers/net/de600.h
+++ b/drivers/net/ethernet/dlink/de600.h
diff --git a/drivers/net/de620.c b/drivers/net/ethernet/dlink/de620.c
index 1c51a7576119..3b934ab784d3 100644
--- a/drivers/net/de620.c
+++ b/drivers/net/ethernet/dlink/de620.c
@@ -767,7 +767,7 @@ static const struct net_device_ops de620_netdev_ops = {
.ndo_stop = de620_close,
.ndo_start_xmit = de620_start_xmit,
.ndo_tx_timeout = de620_timeout,
- .ndo_set_multicast_list = de620_set_multicast_list,
+ .ndo_set_rx_mode = de620_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/de620.h b/drivers/net/ethernet/dlink/de620.h
index e8d9a88f4cb5..e8d9a88f4cb5 100644
--- a/drivers/net/de620.h
+++ b/drivers/net/ethernet/dlink/de620.h
diff --git a/drivers/net/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c
index 23179dbcedd2..b2dc2c81a147 100644
--- a/drivers/net/dl2k.c
+++ b/drivers/net/ethernet/dlink/dl2k.c
@@ -92,7 +92,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_get_stats = get_stats,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
- .ndo_set_multicast_list = set_multicast,
+ .ndo_set_rx_mode = set_multicast,
.ndo_do_ioctl = rio_ioctl,
.ndo_tx_timeout = rio_tx_timeout,
.ndo_change_mtu = change_mtu,
@@ -221,13 +221,13 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
ring_space = pci_alloc_consistent (pdev, TX_TOTAL_SIZE, &ring_dma);
if (!ring_space)
goto err_out_iounmap;
- np->tx_ring = (struct netdev_desc *) ring_space;
+ np->tx_ring = ring_space;
np->tx_ring_dma = ring_dma;
ring_space = pci_alloc_consistent (pdev, RX_TOTAL_SIZE, &ring_dma);
if (!ring_space)
goto err_out_unmap_tx;
- np->rx_ring = (struct netdev_desc *) ring_space;
+ np->rx_ring = ring_space;
np->rx_ring_dma = ring_dma;
/* Parse eeprom data */
@@ -1428,7 +1428,7 @@ mii_wait_link (struct net_device *dev, int wait)
do {
bmsr = mii_read (dev, phy_addr, MII_BMSR);
- if (bmsr & MII_BMSR_LINK_STATUS)
+ if (bmsr & BMSR_LSTATUS)
return 0;
mdelay (1);
} while (--wait > 0);
@@ -1449,60 +1449,60 @@ mii_get_media (struct net_device *dev)
bmsr = mii_read (dev, phy_addr, MII_BMSR);
if (np->an_enable) {
- if (!(bmsr & MII_BMSR_AN_COMPLETE)) {
+ if (!(bmsr & BMSR_ANEGCOMPLETE)) {
/* Auto-Negotiation not completed */
return -1;
}
- negotiate = mii_read (dev, phy_addr, MII_ANAR) &
- mii_read (dev, phy_addr, MII_ANLPAR);
- mscr = mii_read (dev, phy_addr, MII_MSCR);
- mssr = mii_read (dev, phy_addr, MII_MSSR);
- if (mscr & MII_MSCR_1000BT_FD && mssr & MII_MSSR_LP_1000BT_FD) {
+ negotiate = mii_read (dev, phy_addr, MII_ADVERTISE) &
+ mii_read (dev, phy_addr, MII_LPA);
+ mscr = mii_read (dev, phy_addr, MII_CTRL1000);
+ mssr = mii_read (dev, phy_addr, MII_STAT1000);
+ if (mscr & ADVERTISE_1000FULL && mssr & LPA_1000FULL) {
np->speed = 1000;
np->full_duplex = 1;
printk (KERN_INFO "Auto 1000 Mbps, Full duplex\n");
- } else if (mscr & MII_MSCR_1000BT_HD && mssr & MII_MSSR_LP_1000BT_HD) {
+ } else if (mscr & ADVERTISE_1000HALF && mssr & LPA_1000HALF) {
np->speed = 1000;
np->full_duplex = 0;
printk (KERN_INFO "Auto 1000 Mbps, Half duplex\n");
- } else if (negotiate & MII_ANAR_100BX_FD) {
+ } else if (negotiate & ADVERTISE_100FULL) {
np->speed = 100;
np->full_duplex = 1;
printk (KERN_INFO "Auto 100 Mbps, Full duplex\n");
- } else if (negotiate & MII_ANAR_100BX_HD) {
+ } else if (negotiate & ADVERTISE_100HALF) {
np->speed = 100;
np->full_duplex = 0;
printk (KERN_INFO "Auto 100 Mbps, Half duplex\n");
- } else if (negotiate & MII_ANAR_10BT_FD) {
+ } else if (negotiate & ADVERTISE_10FULL) {
np->speed = 10;
np->full_duplex = 1;
printk (KERN_INFO "Auto 10 Mbps, Full duplex\n");
- } else if (negotiate & MII_ANAR_10BT_HD) {
+ } else if (negotiate & ADVERTISE_10HALF) {
np->speed = 10;
np->full_duplex = 0;
printk (KERN_INFO "Auto 10 Mbps, Half duplex\n");
}
- if (negotiate & MII_ANAR_PAUSE) {
+ if (negotiate & ADVERTISE_PAUSE_CAP) {
np->tx_flow &= 1;
np->rx_flow &= 1;
- } else if (negotiate & MII_ANAR_ASYMMETRIC) {
+ } else if (negotiate & ADVERTISE_PAUSE_ASYM) {
np->tx_flow = 0;
np->rx_flow &= 1;
}
/* else tx_flow, rx_flow = user select */
} else {
__u16 bmcr = mii_read (dev, phy_addr, MII_BMCR);
- switch (bmcr & (MII_BMCR_SPEED_100 | MII_BMCR_SPEED_1000)) {
- case MII_BMCR_SPEED_1000:
+ switch (bmcr & (BMCR_SPEED100 | BMCR_SPEED1000)) {
+ case BMCR_SPEED1000:
printk (KERN_INFO "Operating at 1000 Mbps, ");
break;
- case MII_BMCR_SPEED_100:
+ case BMCR_SPEED100:
printk (KERN_INFO "Operating at 100 Mbps, ");
break;
case 0:
printk (KERN_INFO "Operating at 10 Mbps, ");
}
- if (bmcr & MII_BMCR_DUPLEX_MODE) {
+ if (bmcr & BMCR_FULLDPLX) {
printk (KERN_CONT "Full duplex\n");
} else {
printk (KERN_CONT "Half duplex\n");
@@ -1536,24 +1536,22 @@ mii_set_media (struct net_device *dev)
if (np->an_enable) {
/* Advertise capabilities */
bmsr = mii_read (dev, phy_addr, MII_BMSR);
- anar = mii_read (dev, phy_addr, MII_ANAR) &
- ~MII_ANAR_100BX_FD &
- ~MII_ANAR_100BX_HD &
- ~MII_ANAR_100BT4 &
- ~MII_ANAR_10BT_FD &
- ~MII_ANAR_10BT_HD;
- if (bmsr & MII_BMSR_100BX_FD)
- anar |= MII_ANAR_100BX_FD;
- if (bmsr & MII_BMSR_100BX_HD)
- anar |= MII_ANAR_100BX_HD;
- if (bmsr & MII_BMSR_100BT4)
- anar |= MII_ANAR_100BT4;
- if (bmsr & MII_BMSR_10BT_FD)
- anar |= MII_ANAR_10BT_FD;
- if (bmsr & MII_BMSR_10BT_HD)
- anar |= MII_ANAR_10BT_HD;
- anar |= MII_ANAR_PAUSE | MII_ANAR_ASYMMETRIC;
- mii_write (dev, phy_addr, MII_ANAR, anar);
+ anar = mii_read (dev, phy_addr, MII_ADVERTISE) &
+ ~(ADVERTISE_100FULL | ADVERTISE_10FULL |
+ ADVERTISE_100HALF | ADVERTISE_10HALF |
+ ADVERTISE_100BASE4);
+ if (bmsr & BMSR_100FULL)
+ anar |= ADVERTISE_100FULL;
+ if (bmsr & BMSR_100HALF)
+ anar |= ADVERTISE_100HALF;
+ if (bmsr & BMSR_100BASE4)
+ anar |= ADVERTISE_100BASE4;
+ if (bmsr & BMSR_10FULL)
+ anar |= ADVERTISE_10FULL;
+ if (bmsr & BMSR_10HALF)
+ anar |= ADVERTISE_10HALF;
+ anar |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+ mii_write (dev, phy_addr, MII_ADVERTISE, anar);
/* Enable Auto crossover */
pscr = mii_read (dev, phy_addr, MII_PHY_SCR);
@@ -1561,8 +1559,8 @@ mii_set_media (struct net_device *dev)
mii_write (dev, phy_addr, MII_PHY_SCR, pscr);
/* Soft reset PHY */
- mii_write (dev, phy_addr, MII_BMCR, MII_BMCR_RESET);
- bmcr = MII_BMCR_AN_ENABLE | MII_BMCR_RESTART_AN | MII_BMCR_RESET;
+ mii_write (dev, phy_addr, MII_BMCR, BMCR_RESET);
+ bmcr = BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET;
mii_write (dev, phy_addr, MII_BMCR, bmcr);
mdelay(1);
} else {
@@ -1574,7 +1572,7 @@ mii_set_media (struct net_device *dev)
/* 2) PHY Reset */
bmcr = mii_read (dev, phy_addr, MII_BMCR);
- bmcr |= MII_BMCR_RESET;
+ bmcr |= BMCR_RESET;
mii_write (dev, phy_addr, MII_BMCR, bmcr);
/* 3) Power Down */
@@ -1583,25 +1581,25 @@ mii_set_media (struct net_device *dev)
mdelay (100); /* wait a certain time */
/* 4) Advertise nothing */
- mii_write (dev, phy_addr, MII_ANAR, 0);
+ mii_write (dev, phy_addr, MII_ADVERTISE, 0);
/* 5) Set media and Power Up */
- bmcr = MII_BMCR_POWER_DOWN;
+ bmcr = BMCR_PDOWN;
if (np->speed == 100) {
- bmcr |= MII_BMCR_SPEED_100;
+ bmcr |= BMCR_SPEED100;
printk (KERN_INFO "Manual 100 Mbps, ");
} else if (np->speed == 10) {
printk (KERN_INFO "Manual 10 Mbps, ");
}
if (np->full_duplex) {
- bmcr |= MII_BMCR_DUPLEX_MODE;
+ bmcr |= BMCR_FULLDPLX;
printk (KERN_CONT "Full duplex\n");
} else {
printk (KERN_CONT "Half duplex\n");
}
#if 0
/* Set 1000BaseT Master/Slave setting */
- mscr = mii_read (dev, phy_addr, MII_MSCR);
+ mscr = mii_read (dev, phy_addr, MII_CTRL1000);
mscr |= MII_MSCR_CFG_ENABLE;
mscr &= ~MII_MSCR_CFG_VALUE = 0;
#endif
@@ -1624,7 +1622,7 @@ mii_get_media_pcs (struct net_device *dev)
bmsr = mii_read (dev, phy_addr, PCS_BMSR);
if (np->an_enable) {
- if (!(bmsr & MII_BMSR_AN_COMPLETE)) {
+ if (!(bmsr & BMSR_ANEGCOMPLETE)) {
/* Auto-Negotiation not completed */
return -1;
}
@@ -1649,7 +1647,7 @@ mii_get_media_pcs (struct net_device *dev)
} else {
__u16 bmcr = mii_read (dev, phy_addr, PCS_BMCR);
printk (KERN_INFO "Operating at 1000 Mbps, ");
- if (bmcr & MII_BMCR_DUPLEX_MODE) {
+ if (bmcr & BMCR_FULLDPLX) {
printk (KERN_CONT "Full duplex\n");
} else {
printk (KERN_CONT "Half duplex\n");
@@ -1682,7 +1680,7 @@ mii_set_media_pcs (struct net_device *dev)
if (np->an_enable) {
/* Advertise capabilities */
esr = mii_read (dev, phy_addr, PCS_ESR);
- anar = mii_read (dev, phy_addr, MII_ANAR) &
+ anar = mii_read (dev, phy_addr, MII_ADVERTISE) &
~PCS_ANAR_HALF_DUPLEX &
~PCS_ANAR_FULL_DUPLEX;
if (esr & (MII_ESR_1000BT_HD | MII_ESR_1000BX_HD))
@@ -1690,22 +1688,21 @@ mii_set_media_pcs (struct net_device *dev)
if (esr & (MII_ESR_1000BT_FD | MII_ESR_1000BX_FD))
anar |= PCS_ANAR_FULL_DUPLEX;
anar |= PCS_ANAR_PAUSE | PCS_ANAR_ASYMMETRIC;
- mii_write (dev, phy_addr, MII_ANAR, anar);
+ mii_write (dev, phy_addr, MII_ADVERTISE, anar);
/* Soft reset PHY */
- mii_write (dev, phy_addr, MII_BMCR, MII_BMCR_RESET);
- bmcr = MII_BMCR_AN_ENABLE | MII_BMCR_RESTART_AN |
- MII_BMCR_RESET;
+ mii_write (dev, phy_addr, MII_BMCR, BMCR_RESET);
+ bmcr = BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET;
mii_write (dev, phy_addr, MII_BMCR, bmcr);
mdelay(1);
} else {
/* Force speed setting */
/* PHY Reset */
- bmcr = MII_BMCR_RESET;
+ bmcr = BMCR_RESET;
mii_write (dev, phy_addr, MII_BMCR, bmcr);
mdelay(10);
if (np->full_duplex) {
- bmcr = MII_BMCR_DUPLEX_MODE;
+ bmcr = BMCR_FULLDPLX;
printk (KERN_INFO "Manual full duplex\n");
} else {
bmcr = 0;
@@ -1715,7 +1712,7 @@ mii_set_media_pcs (struct net_device *dev)
mdelay(10);
/* Advertise nothing */
- mii_write (dev, phy_addr, MII_ANAR, 0);
+ mii_write (dev, phy_addr, MII_ADVERTISE, 0);
}
return 0;
}
diff --git a/drivers/net/dl2k.h b/drivers/net/ethernet/dlink/dl2k.h
index 7caab3d26a9e..ba0adcafa55a 100644
--- a/drivers/net/dl2k.h
+++ b/drivers/net/ethernet/dlink/dl2k.h
@@ -28,6 +28,7 @@
#include <linux/init.h>
#include <linux/crc32.h>
#include <linux/ethtool.h>
+#include <linux/mii.h>
#include <linux/bitops.h>
#include <asm/processor.h> /* Processor type for cache alignment. */
#include <asm/io.h>
@@ -271,20 +272,9 @@ enum RFS_bits {
#define MII_RESET_TIME_OUT 10000
/* MII register */
enum _mii_reg {
- MII_BMCR = 0,
- MII_BMSR = 1,
- MII_PHY_ID1 = 2,
- MII_PHY_ID2 = 3,
- MII_ANAR = 4,
- MII_ANLPAR = 5,
- MII_ANER = 6,
- MII_ANNPT = 7,
- MII_ANLPRNP = 8,
- MII_MSCR = 9,
- MII_MSSR = 10,
- MII_ESR = 15,
MII_PHY_SCR = 16,
};
+
/* PCS register */
enum _pcs_reg {
PCS_BMCR = 0,
@@ -297,102 +287,6 @@ enum _pcs_reg {
PCS_ESR = 15,
};
-/* Basic Mode Control Register */
-enum _mii_bmcr {
- MII_BMCR_RESET = 0x8000,
- MII_BMCR_LOOP_BACK = 0x4000,
- MII_BMCR_SPEED_LSB = 0x2000,
- MII_BMCR_AN_ENABLE = 0x1000,
- MII_BMCR_POWER_DOWN = 0x0800,
- MII_BMCR_ISOLATE = 0x0400,
- MII_BMCR_RESTART_AN = 0x0200,
- MII_BMCR_DUPLEX_MODE = 0x0100,
- MII_BMCR_COL_TEST = 0x0080,
- MII_BMCR_SPEED_MSB = 0x0040,
- MII_BMCR_SPEED_RESERVED = 0x003f,
- MII_BMCR_SPEED_10 = 0,
- MII_BMCR_SPEED_100 = MII_BMCR_SPEED_LSB,
- MII_BMCR_SPEED_1000 = MII_BMCR_SPEED_MSB,
-};
-
-/* Basic Mode Status Register */
-enum _mii_bmsr {
- MII_BMSR_100BT4 = 0x8000,
- MII_BMSR_100BX_FD = 0x4000,
- MII_BMSR_100BX_HD = 0x2000,
- MII_BMSR_10BT_FD = 0x1000,
- MII_BMSR_10BT_HD = 0x0800,
- MII_BMSR_100BT2_FD = 0x0400,
- MII_BMSR_100BT2_HD = 0x0200,
- MII_BMSR_EXT_STATUS = 0x0100,
- MII_BMSR_PREAMBLE_SUPP = 0x0040,
- MII_BMSR_AN_COMPLETE = 0x0020,
- MII_BMSR_REMOTE_FAULT = 0x0010,
- MII_BMSR_AN_ABILITY = 0x0008,
- MII_BMSR_LINK_STATUS = 0x0004,
- MII_BMSR_JABBER_DETECT = 0x0002,
- MII_BMSR_EXT_CAP = 0x0001,
-};
-
-/* ANAR */
-enum _mii_anar {
- MII_ANAR_NEXT_PAGE = 0x8000,
- MII_ANAR_REMOTE_FAULT = 0x4000,
- MII_ANAR_ASYMMETRIC = 0x0800,
- MII_ANAR_PAUSE = 0x0400,
- MII_ANAR_100BT4 = 0x0200,
- MII_ANAR_100BX_FD = 0x0100,
- MII_ANAR_100BX_HD = 0x0080,
- MII_ANAR_10BT_FD = 0x0020,
- MII_ANAR_10BT_HD = 0x0010,
- MII_ANAR_SELECTOR = 0x001f,
- MII_IEEE8023_CSMACD = 0x0001,
-};
-
-/* ANLPAR */
-enum _mii_anlpar {
- MII_ANLPAR_NEXT_PAGE = MII_ANAR_NEXT_PAGE,
- MII_ANLPAR_REMOTE_FAULT = MII_ANAR_REMOTE_FAULT,
- MII_ANLPAR_ASYMMETRIC = MII_ANAR_ASYMMETRIC,
- MII_ANLPAR_PAUSE = MII_ANAR_PAUSE,
- MII_ANLPAR_100BT4 = MII_ANAR_100BT4,
- MII_ANLPAR_100BX_FD = MII_ANAR_100BX_FD,
- MII_ANLPAR_100BX_HD = MII_ANAR_100BX_HD,
- MII_ANLPAR_10BT_FD = MII_ANAR_10BT_FD,
- MII_ANLPAR_10BT_HD = MII_ANAR_10BT_HD,
- MII_ANLPAR_SELECTOR = MII_ANAR_SELECTOR,
-};
-
-/* Auto-Negotiation Expansion Register */
-enum _mii_aner {
- MII_ANER_PAR_DETECT_FAULT = 0x0010,
- MII_ANER_LP_NEXTPAGABLE = 0x0008,
- MII_ANER_NETXTPAGABLE = 0x0004,
- MII_ANER_PAGE_RECEIVED = 0x0002,
- MII_ANER_LP_NEGOTIABLE = 0x0001,
-};
-
-/* MASTER-SLAVE Control Register */
-enum _mii_mscr {
- MII_MSCR_TEST_MODE = 0xe000,
- MII_MSCR_CFG_ENABLE = 0x1000,
- MII_MSCR_CFG_VALUE = 0x0800,
- MII_MSCR_PORT_VALUE = 0x0400,
- MII_MSCR_1000BT_FD = 0x0200,
- MII_MSCR_1000BT_HD = 0X0100,
-};
-
-/* MASTER-SLAVE Status Register */
-enum _mii_mssr {
- MII_MSSR_CFG_FAULT = 0x8000,
- MII_MSSR_CFG_RES = 0x4000,
- MII_MSSR_LOCAL_RCV_STATUS = 0x2000,
- MII_MSSR_REMOTE_RCVR = 0x1000,
- MII_MSSR_LP_1000BT_FD = 0x0800,
- MII_MSSR_LP_1000BT_HD = 0x0400,
- MII_MSSR_IDLE_ERR_COUNT = 0x00ff,
-};
-
/* IEEE Extened Status Register */
enum _mii_esr {
MII_ESR_1000BX_FD = 0x8000,
diff --git a/drivers/net/sundance.c b/drivers/net/ethernet/dlink/sundance.c
index 4793df843c24..dcd7f7a71ad4 100644
--- a/drivers/net/sundance.c
+++ b/drivers/net/ethernet/dlink/sundance.c
@@ -464,7 +464,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_stop = netdev_close,
.ndo_start_xmit = start_tx,
.ndo_get_stats = get_stats,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_do_ioctl = netdev_ioctl,
.ndo_tx_timeout = tx_timeout,
.ndo_change_mtu = change_mtu,
diff --git a/drivers/net/dnet.c b/drivers/net/ethernet/dnet.c
index 8318ea06cb6d..c1063d1540c2 100644
--- a/drivers/net/dnet.c
+++ b/drivers/net/ethernet/dnet.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/dma-mapping.h>
@@ -587,6 +588,8 @@ static netdev_tx_t dnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
dnet_writel(bp, irq_enable, INTR_ENB);
}
+ skb_tx_timestamp(skb);
+
/* free the buffer */
dev_kfree_skb(skb);
diff --git a/drivers/net/dnet.h b/drivers/net/ethernet/dnet.h
index 37f5b30fa78b..37f5b30fa78b 100644
--- a/drivers/net/dnet.h
+++ b/drivers/net/ethernet/dnet.h
diff --git a/drivers/net/ethernet/emulex/Kconfig b/drivers/net/ethernet/emulex/Kconfig
new file mode 100644
index 000000000000..7a28a6433944
--- /dev/null
+++ b/drivers/net/ethernet/emulex/Kconfig
@@ -0,0 +1,23 @@
+#
+# Emulex driver configuration
+#
+
+config NET_VENDOR_EMULEX
+ bool "Emulex devices"
+ default y
+ depends on PCI && INET
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Emulex cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_EMULEX
+
+source "drivers/net/ethernet/emulex/benet/Kconfig"
+
+endif # NET_VENDOR_EMULEX
diff --git a/drivers/net/ethernet/emulex/Makefile b/drivers/net/ethernet/emulex/Makefile
new file mode 100644
index 000000000000..ea8ec574d45a
--- /dev/null
+++ b/drivers/net/ethernet/emulex/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Emulex device drivers.
+#
+
+obj-$(CONFIG_BE2NET) += benet/
diff --git a/drivers/net/ethernet/emulex/benet/Kconfig b/drivers/net/ethernet/emulex/benet/Kconfig
new file mode 100644
index 000000000000..804db04a2bd0
--- /dev/null
+++ b/drivers/net/ethernet/emulex/benet/Kconfig
@@ -0,0 +1,6 @@
+config BE2NET
+ tristate "ServerEngines' 10Gbps NIC - BladeEngine"
+ depends on PCI && INET
+ ---help---
+ This driver implements the NIC functionality for ServerEngines'
+ 10Gbps network adapter - BladeEngine.
diff --git a/drivers/net/benet/Makefile b/drivers/net/ethernet/emulex/benet/Makefile
index a60cd8051135..a60cd8051135 100644
--- a/drivers/net/benet/Makefile
+++ b/drivers/net/ethernet/emulex/benet/Makefile
diff --git a/drivers/net/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h
index a7db870d1641..644e8fed8364 100644
--- a/drivers/net/benet/be.h
+++ b/drivers/net/ethernet/emulex/benet/be.h
@@ -20,7 +20,6 @@
#include <linux/pci.h>
#include <linux/etherdevice.h>
-#include <linux/version.h>
#include <linux/delay.h>
#include <net/tcp.h>
#include <net/ip.h>
@@ -30,6 +29,7 @@
#include <linux/interrupt.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/u64_stats_sync.h>
#include "be_hw.h"
@@ -87,6 +87,7 @@ static inline char *nic_name(struct pci_dev *pdev)
#define MAX_RSS_QS 4 /* BE limit is 4 queues/port */
#define MAX_RX_QS (MAX_RSS_QS + 1) /* RSS qs + 1 def Rx */
+#define MAX_TX_QS 8
#define BE_MAX_MSIX_VECTORS (MAX_RX_QS + 1)/* RX + TX */
#define BE_NAPI_WEIGHT 64
#define MAX_RX_POST BE_NAPI_WEIGHT /* Frags posted at a time */
@@ -136,6 +137,11 @@ static inline void *queue_tail_node(struct be_queue_info *q)
return q->dma_mem.va + q->tail * q->entry_size;
}
+static inline void *queue_index_node(struct be_queue_info *q, u16 index)
+{
+ return q->dma_mem.va + index * q->entry_size;
+}
+
static inline void queue_head_inc(struct be_queue_info *q)
{
index_inc(&q->head, q->len);
@@ -167,16 +173,15 @@ struct be_mcc_obj {
};
struct be_tx_stats {
- u32 be_tx_reqs; /* number of TX requests initiated */
- u32 be_tx_stops; /* number of times TX Q was stopped */
- u32 be_tx_wrbs; /* number of tx WRBs used */
- u32 be_tx_events; /* number of tx completion events */
- u32 be_tx_compl; /* number of tx completion entries processed */
- ulong be_tx_jiffies;
- u64 be_tx_bytes;
- u64 be_tx_bytes_prev;
- u64 be_tx_pkts;
- u32 be_tx_rate;
+ u64 tx_bytes;
+ u64 tx_pkts;
+ u64 tx_reqs;
+ u64 tx_wrbs;
+ u64 tx_compl;
+ ulong tx_jiffies;
+ u32 tx_stops;
+ struct u64_stats_sync sync;
+ struct u64_stats_sync sync_compl;
};
struct be_tx_obj {
@@ -184,6 +189,7 @@ struct be_tx_obj {
struct be_queue_info cq;
/* Remember the skbs that were transmitted */
struct sk_buff *sent_skb_list[TX_Q_LEN];
+ struct be_tx_stats stats;
};
/* Struct to remember the pages posted for rx frags */
@@ -195,21 +201,20 @@ struct be_rx_page_info {
};
struct be_rx_stats {
- u32 rx_post_fail;/* number of ethrx buffer alloc failures */
- u32 rx_polls; /* number of times NAPI called poll function */
- u32 rx_events; /* number of ucast rx completion events */
- u32 rx_compl; /* number of rx completion entries processed */
- ulong rx_jiffies;
u64 rx_bytes;
- u64 rx_bytes_prev;
u64 rx_pkts;
- u32 rx_rate;
+ u64 rx_pkts_prev;
+ ulong rx_jiffies;
+ u32 rx_drops_no_skbs; /* skb allocation errors */
+ u32 rx_drops_no_frags; /* HW has no fetched frags */
+ u32 rx_post_fail; /* page post alloc failures */
+ u32 rx_polls; /* NAPI calls */
+ u32 rx_events;
+ u32 rx_compl;
u32 rx_mcast_pkts;
- u32 rxcp_err; /* Num rx completion entries w/ err set. */
- ulong rx_fps_jiffies; /* jiffies at last FPS calc */
- u32 rx_frags;
- u32 prev_rx_frags;
- u32 rx_fps; /* Rx frags per second */
+ u32 rx_compl_err; /* completions with err set */
+ u32 rx_pps; /* pkts per second */
+ struct u64_stats_sync sync;
};
struct be_rx_compl_info {
@@ -217,7 +222,7 @@ struct be_rx_compl_info {
u16 vlan_tag;
u16 pkt_size;
u16 rxq_idx;
- u16 mac_id;
+ u16 port;
u8 vlanf;
u8 num_rcvd;
u8 err;
@@ -245,44 +250,41 @@ struct be_rx_obj {
};
struct be_drv_stats {
- u8 be_on_die_temperature;
- u64 be_tx_events;
- u64 eth_red_drops;
- u64 rx_drops_no_pbuf;
- u64 rx_drops_no_txpb;
- u64 rx_drops_no_erx_descr;
- u64 rx_drops_no_tpre_descr;
- u64 rx_drops_too_many_frags;
- u64 rx_drops_invalid_ring;
- u64 forwarded_packets;
- u64 rx_drops_mtu;
- u64 rx_crc_errors;
- u64 rx_alignment_symbol_errors;
- u64 rx_pause_frames;
- u64 rx_priority_pause_frames;
- u64 rx_control_frames;
- u64 rx_in_range_errors;
- u64 rx_out_range_errors;
- u64 rx_frame_too_long;
- u64 rx_address_match_errors;
- u64 rx_dropped_too_small;
- u64 rx_dropped_too_short;
- u64 rx_dropped_header_too_small;
- u64 rx_dropped_tcp_length;
- u64 rx_dropped_runt;
- u64 rx_ip_checksum_errs;
- u64 rx_tcp_checksum_errs;
- u64 rx_udp_checksum_errs;
- u64 rx_switched_unicast_packets;
- u64 rx_switched_multicast_packets;
- u64 rx_switched_broadcast_packets;
- u64 tx_pauseframes;
- u64 tx_priority_pauseframes;
- u64 tx_controlframes;
- u64 rxpp_fifo_overflow_drop;
- u64 rx_input_fifo_overflow_drop;
- u64 pmem_fifo_overflow_drop;
- u64 jabber_events;
+ u32 be_on_die_temperature;
+ u32 tx_events;
+ u32 eth_red_drops;
+ u32 rx_drops_no_pbuf;
+ u32 rx_drops_no_txpb;
+ u32 rx_drops_no_erx_descr;
+ u32 rx_drops_no_tpre_descr;
+ u32 rx_drops_too_many_frags;
+ u32 rx_drops_invalid_ring;
+ u32 forwarded_packets;
+ u32 rx_drops_mtu;
+ u32 rx_crc_errors;
+ u32 rx_alignment_symbol_errors;
+ u32 rx_pause_frames;
+ u32 rx_priority_pause_frames;
+ u32 rx_control_frames;
+ u32 rx_in_range_errors;
+ u32 rx_out_range_errors;
+ u32 rx_frame_too_long;
+ u32 rx_address_match_errors;
+ u32 rx_dropped_too_small;
+ u32 rx_dropped_too_short;
+ u32 rx_dropped_header_too_small;
+ u32 rx_dropped_tcp_length;
+ u32 rx_dropped_runt;
+ u32 rx_ip_checksum_errs;
+ u32 rx_tcp_checksum_errs;
+ u32 rx_udp_checksum_errs;
+ u32 tx_pauseframes;
+ u32 tx_priority_pauseframes;
+ u32 tx_controlframes;
+ u32 rxpp_fifo_overflow_drop;
+ u32 rx_input_fifo_overflow_drop;
+ u32 pmem_fifo_overflow_drop;
+ u32 jabber_events;
};
struct be_vf_cfg {
@@ -301,7 +303,6 @@ struct be_adapter {
u8 __iomem *csr;
u8 __iomem *db; /* Door Bell */
- u8 __iomem *pcicfg; /* PCI config space */
struct mutex mbox_lock; /* For serializing mbox cmds to BE card */
struct be_dma_mem mbox_mem;
@@ -319,8 +320,8 @@ struct be_adapter {
/* TX Rings */
struct be_eq_obj tx_eq;
- struct be_tx_obj tx_obj;
- struct be_tx_stats tx_stats;
+ struct be_tx_obj tx_obj[MAX_TX_QS];
+ u8 num_tx_qs;
u32 cache_line_break[8];
@@ -332,13 +333,12 @@ struct be_adapter {
u8 eq_next_idx;
struct be_drv_stats drv_stats;
- struct vlan_group *vlan_grp;
u16 vlans_added;
u16 max_vlans; /* Number of vlans supported */
u8 vlan_tag[VLAN_N_VID];
u8 vlan_prio_bmap; /* Available Priority BitMap */
u16 recommended_prio; /* Recommended Priority */
- struct be_dma_mem mc_cmd_mem;
+ struct be_dma_mem rx_filter; /* Cmd DMA mem for rx-filter */
struct be_dma_mem stats_cmd;
/* Work queue used to perform periodic tasks like getting statistics */
@@ -352,7 +352,6 @@ struct be_adapter {
u32 beacon_state; /* for set_phys_id */
bool eeh_err;
- bool link_up;
u32 port_num;
bool promiscuous;
bool wol;
@@ -385,13 +384,15 @@ struct be_adapter {
#define BE_GEN2 2
#define BE_GEN3 3
+#define ON 1
+#define OFF 0
#define lancer_chip(adapter) ((adapter->pdev->device == OC_DEVICE_ID3) || \
(adapter->pdev->device == OC_DEVICE_ID4))
extern const struct ethtool_ops be_ethtool_ops;
#define msix_enabled(adapter) (adapter->num_msix_vec > 0)
-#define tx_stats(adapter) (&adapter->tx_stats)
+#define tx_stats(txo) (&txo->stats)
#define rx_stats(rxo) (&rxo->stats)
#define BE_SET_NETDEV_OPS(netdev, ops) (netdev->netdev_ops = ops)
@@ -405,6 +406,10 @@ extern const struct ethtool_ops be_ethtool_ops;
for (i = 0, rxo = &adapter->rx_obj[i+1]; i < (adapter->num_rx_qs - 1);\
i++, rxo++)
+#define for_all_tx_queues(adapter, txo, i) \
+ for (i = 0, txo = &adapter->tx_obj[i]; i < adapter->num_tx_qs; \
+ i++, txo++)
+
#define PAGE_SHIFT_4K 12
#define PAGE_SIZE_4K (1 << PAGE_SHIFT_4K)
@@ -521,8 +526,7 @@ static inline bool be_multi_rxq(const struct be_adapter *adapter)
extern void be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm,
u16 num_popped);
-extern void be_link_status_update(struct be_adapter *adapter, bool link_up);
-extern void netdev_stats_update(struct be_adapter *adapter);
+extern void be_link_status_update(struct be_adapter *adapter, u32 link_status);
extern void be_parse_stats(struct be_adapter *adapter);
extern int be_load_fw(struct be_adapter *adapter, u8 *func);
#endif /* BE_H */
diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index 81654ae16c63..2c7b36673dfc 100644
--- a/drivers/net/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -19,7 +19,12 @@
#include "be_cmds.h"
/* Must be a power of 2 or else MODULO will BUG_ON */
-static int be_get_temp_freq = 32;
+static int be_get_temp_freq = 64;
+
+static inline void *embedded_payload(struct be_mcc_wrb *wrb)
+{
+ return wrb->payload.embedded_payload;
+}
static void be_mcc_notify(struct be_adapter *adapter)
{
@@ -82,38 +87,40 @@ static int be_mcc_compl_process(struct be_adapter *adapter,
if (((compl->tag0 == OPCODE_ETH_GET_STATISTICS) ||
(compl->tag0 == OPCODE_ETH_GET_PPORT_STATS)) &&
(compl->tag1 == CMD_SUBSYSTEM_ETH)) {
- if (adapter->generation == BE_GEN3) {
- if (lancer_chip(adapter)) {
- struct lancer_cmd_resp_pport_stats
- *resp = adapter->stats_cmd.va;
- be_dws_le_to_cpu(&resp->pport_stats,
- sizeof(resp->pport_stats));
- } else {
- struct be_cmd_resp_get_stats_v1 *resp =
- adapter->stats_cmd.va;
-
- be_dws_le_to_cpu(&resp->hw_stats,
- sizeof(resp->hw_stats));
- }
- } else {
- struct be_cmd_resp_get_stats_v0 *resp =
- adapter->stats_cmd.va;
-
- be_dws_le_to_cpu(&resp->hw_stats,
- sizeof(resp->hw_stats));
- }
be_parse_stats(adapter);
- netdev_stats_update(adapter);
adapter->stats_cmd_sent = false;
}
- } else if ((compl_status != MCC_STATUS_NOT_SUPPORTED) &&
- (compl->tag0 != OPCODE_COMMON_NTWK_MAC_QUERY)) {
- extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
- CQE_STATUS_EXTD_MASK;
- dev_warn(&adapter->pdev->dev,
- "Error in cmd completion - opcode %d, compl %d, extd %d\n",
- compl->tag0, compl_status, extd_status);
+ if (compl->tag0 ==
+ OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES) {
+ struct be_mcc_wrb *mcc_wrb =
+ queue_index_node(&adapter->mcc_obj.q,
+ compl->tag1);
+ struct be_cmd_resp_get_cntl_addnl_attribs *resp =
+ embedded_payload(mcc_wrb);
+ adapter->drv_stats.be_on_die_temperature =
+ resp->on_die_temperature;
+ }
+ } else {
+ if (compl->tag0 == OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES)
+ be_get_temp_freq = 0;
+
+ if (compl_status == MCC_STATUS_NOT_SUPPORTED ||
+ compl_status == MCC_STATUS_ILLEGAL_REQUEST)
+ goto done;
+
+ if (compl_status == MCC_STATUS_UNAUTHORIZED_REQUEST) {
+ dev_warn(&adapter->pdev->dev, "This domain(VM) is not "
+ "permitted to execute this cmd (opcode %d)\n",
+ compl->tag0);
+ } else {
+ extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
+ CQE_STATUS_EXTD_MASK;
+ dev_err(&adapter->pdev->dev, "Cmd (opcode %d) failed:"
+ "status %d, extd-status %d\n",
+ compl->tag0, compl_status, extd_status);
+ }
}
+done:
return compl_status;
}
@@ -121,8 +128,7 @@ static int be_mcc_compl_process(struct be_adapter *adapter,
static void be_async_link_state_process(struct be_adapter *adapter,
struct be_async_event_link_state *evt)
{
- be_link_status_update(adapter,
- evt->port_link_status == ASYNC_EVENT_LINK_UP);
+ be_link_status_update(adapter, evt->port_link_status);
}
/* Grp5 CoS Priority evt */
@@ -152,7 +158,7 @@ static void be_async_grp5_pvid_state_process(struct be_adapter *adapter,
struct be_async_event_grp5_pvid_state *evt)
{
if (evt->enabled)
- adapter->pvid = le16_to_cpu(evt->tag);
+ adapter->pvid = le16_to_cpu(evt->tag) & VLAN_VID_MASK;
else
adapter->pvid = 0;
}
@@ -312,8 +318,7 @@ static int be_mbox_db_ready_wait(struct be_adapter *adapter, void __iomem *db)
if (msecs > 4000) {
dev_err(&adapter->pdev->dev, "mbox poll timed out\n");
- if (!lancer_chip(adapter))
- be_detect_dump_ue(adapter);
+ be_detect_dump_ue(adapter);
return -1;
}
@@ -410,44 +415,45 @@ int be_cmd_POST(struct be_adapter *adapter)
} else {
return 0;
}
- } while (timeout < 40);
+ } while (timeout < 60);
dev_err(dev, "POST timeout; stage=0x%x\n", stage);
return -1;
}
-static inline void *embedded_payload(struct be_mcc_wrb *wrb)
-{
- return wrb->payload.embedded_payload;
-}
static inline struct be_sge *nonembedded_sgl(struct be_mcc_wrb *wrb)
{
return &wrb->payload.sgl[0];
}
-/* Don't touch the hdr after it's prepared */
-static void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
- bool embedded, u8 sge_cnt, u32 opcode)
-{
- if (embedded)
- wrb->embedded |= MCC_WRB_EMBEDDED_MASK;
- else
- wrb->embedded |= (sge_cnt & MCC_WRB_SGE_CNT_MASK) <<
- MCC_WRB_SGE_CNT_SHIFT;
- wrb->payload_length = payload_len;
- wrb->tag0 = opcode;
- be_dws_cpu_to_le(wrb, 8);
-}
/* Don't touch the hdr after it's prepared */
-static void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
- u8 subsystem, u8 opcode, int cmd_len)
+/* mem will be NULL for embedded commands */
+static void be_wrb_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
+ u8 subsystem, u8 opcode, int cmd_len,
+ struct be_mcc_wrb *wrb, struct be_dma_mem *mem)
{
+ struct be_sge *sge;
+
req_hdr->opcode = opcode;
req_hdr->subsystem = subsystem;
req_hdr->request_length = cpu_to_le32(cmd_len - sizeof(*req_hdr));
req_hdr->version = 0;
+
+ wrb->tag0 = opcode;
+ wrb->tag1 = subsystem;
+ wrb->payload_length = cmd_len;
+ if (mem) {
+ wrb->embedded |= (1 & MCC_WRB_SGE_CNT_MASK) <<
+ MCC_WRB_SGE_CNT_SHIFT;
+ sge = nonembedded_sgl(wrb);
+ sge->pa_hi = cpu_to_le32(upper_32_bits(mem->dma));
+ sge->pa_lo = cpu_to_le32(mem->dma & 0xFFFFFFFF);
+ sge->len = cpu_to_le32(mem->size);
+ } else
+ wrb->embedded |= MCC_WRB_EMBEDDED_MASK;
+ be_dws_cpu_to_le(wrb, 8);
}
static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages,
@@ -584,10 +590,8 @@ int be_cmd_eq_create(struct be_adapter *adapter,
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, OPCODE_COMMON_EQ_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_EQ_CREATE, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_EQ_CREATE, sizeof(*req), wrb, NULL);
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
@@ -613,7 +617,7 @@ int be_cmd_eq_create(struct be_adapter *adapter,
return status;
}
-/* Uses mbox */
+/* Use MCC */
int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
u8 type, bool permanent, u32 if_handle)
{
@@ -621,18 +625,17 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
struct be_cmd_req_mac_query *req;
int status;
- if (mutex_lock_interruptible(&adapter->mbox_lock))
- return -1;
+ spin_lock_bh(&adapter->mcc_lock);
- wrb = wrb_from_mbox(adapter);
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_MAC_QUERY);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_MAC_QUERY, sizeof(*req));
-
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_MAC_QUERY, sizeof(*req), wrb, NULL);
req->type = type;
if (permanent) {
req->permanent = 1;
@@ -641,13 +644,14 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
req->permanent = 0;
}
- status = be_mbox_notify_wait(adapter);
+ status = be_mcc_notify_wait(adapter);
if (!status) {
struct be_cmd_resp_mac_query *resp = embedded_payload(wrb);
memcpy(mac_addr, resp->mac.addr, ETH_ALEN);
}
- mutex_unlock(&adapter->mbox_lock);
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -668,11 +672,8 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_PMAC_ADD);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_PMAC_ADD, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_PMAC_ADD, sizeof(*req), wrb, NULL);
req->hdr.domain = domain;
req->if_id = cpu_to_le32(if_id);
@@ -686,6 +687,10 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
err:
spin_unlock_bh(&adapter->mcc_lock);
+
+ if (status == MCC_STATUS_UNAUTHORIZED_REQUEST)
+ status = -EPERM;
+
return status;
}
@@ -705,11 +710,8 @@ int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, u32 pmac_id, u32 dom)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_PMAC_DEL);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_PMAC_DEL, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_PMAC_DEL, sizeof(*req), wrb, NULL);
req->hdr.domain = dom;
req->if_id = cpu_to_le32(if_id);
@@ -740,11 +742,8 @@ int be_cmd_cq_create(struct be_adapter *adapter,
req = embedded_payload(wrb);
ctxt = &req->context;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_CQ_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_CQ_CREATE, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_CQ_CREATE, sizeof(*req), wrb, NULL);
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
if (lancer_chip(adapter)) {
@@ -799,12 +798,12 @@ static u32 be_encoded_q_len(int q_len)
return len_encoded;
}
-int be_cmd_mccq_create(struct be_adapter *adapter,
+int be_cmd_mccq_ext_create(struct be_adapter *adapter,
struct be_queue_info *mccq,
struct be_queue_info *cq)
{
struct be_mcc_wrb *wrb;
- struct be_cmd_req_mcc_create *req;
+ struct be_cmd_req_mcc_ext_create *req;
struct be_dma_mem *q_mem = &mccq->dma_mem;
void *ctxt;
int status;
@@ -816,11 +815,8 @@ int be_cmd_mccq_create(struct be_adapter *adapter,
req = embedded_payload(wrb);
ctxt = &req->context;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_MCC_CREATE_EXT);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MCC_CREATE_EXT, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MCC_CREATE_EXT, sizeof(*req), wrb, NULL);
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
if (lancer_chip(adapter)) {
@@ -859,6 +855,64 @@ int be_cmd_mccq_create(struct be_adapter *adapter,
return status;
}
+int be_cmd_mccq_org_create(struct be_adapter *adapter,
+ struct be_queue_info *mccq,
+ struct be_queue_info *cq)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_mcc_create *req;
+ struct be_dma_mem *q_mem = &mccq->dma_mem;
+ void *ctxt;
+ int status;
+
+ if (mutex_lock_interruptible(&adapter->mbox_lock))
+ return -1;
+
+ wrb = wrb_from_mbox(adapter);
+ req = embedded_payload(wrb);
+ ctxt = &req->context;
+
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MCC_CREATE, sizeof(*req), wrb, NULL);
+
+ req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
+
+ AMAP_SET_BITS(struct amap_mcc_context_be, valid, ctxt, 1);
+ AMAP_SET_BITS(struct amap_mcc_context_be, ring_size, ctxt,
+ be_encoded_q_len(mccq->len));
+ AMAP_SET_BITS(struct amap_mcc_context_be, cq_id, ctxt, cq->id);
+
+ be_dws_cpu_to_le(ctxt, sizeof(req->context));
+
+ be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
+
+ status = be_mbox_notify_wait(adapter);
+ if (!status) {
+ struct be_cmd_resp_mcc_create *resp = embedded_payload(wrb);
+ mccq->id = le16_to_cpu(resp->id);
+ mccq->created = true;
+ }
+
+ mutex_unlock(&adapter->mbox_lock);
+ return status;
+}
+
+int be_cmd_mccq_create(struct be_adapter *adapter,
+ struct be_queue_info *mccq,
+ struct be_queue_info *cq)
+{
+ int status;
+
+ status = be_cmd_mccq_ext_create(adapter, mccq, cq);
+ if (status && !lancer_chip(adapter)) {
+ dev_warn(&adapter->pdev->dev, "Upgrade to F/W ver 2.102.235.0 "
+ "or newer to avoid conflicting priorities between NIC "
+ "and FCoE traffic");
+ status = be_cmd_mccq_org_create(adapter, mccq, cq);
+ }
+ return status;
+}
+
int be_cmd_txq_create(struct be_adapter *adapter,
struct be_queue_info *txq,
struct be_queue_info *cq)
@@ -876,11 +930,8 @@ int be_cmd_txq_create(struct be_adapter *adapter,
req = embedded_payload(wrb);
ctxt = &req->context;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_ETH_TX_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_TX_CREATE,
- sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_TX_CREATE, sizeof(*req), wrb, NULL);
if (lancer_chip(adapter)) {
req->hdr.version = 1;
@@ -913,7 +964,7 @@ int be_cmd_txq_create(struct be_adapter *adapter,
return status;
}
-/* Uses mbox */
+/* Uses MCC */
int be_cmd_rxq_create(struct be_adapter *adapter,
struct be_queue_info *rxq, u16 cq_id, u16 frag_size,
u16 max_frame_size, u32 if_id, u32 rss, u8 *rss_id)
@@ -923,17 +974,17 @@ int be_cmd_rxq_create(struct be_adapter *adapter,
struct be_dma_mem *q_mem = &rxq->dma_mem;
int status;
- if (mutex_lock_interruptible(&adapter->mbox_lock))
- return -1;
+ spin_lock_bh(&adapter->mcc_lock);
- wrb = wrb_from_mbox(adapter);
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_ETH_RX_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_RX_CREATE,
- sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_RX_CREATE, sizeof(*req), wrb, NULL);
req->cq_id = cpu_to_le16(cq_id);
req->frag_size = fls(frag_size) - 1;
@@ -943,7 +994,7 @@ int be_cmd_rxq_create(struct be_adapter *adapter,
req->max_frame_size = cpu_to_le16(max_frame_size);
req->rss_queue = cpu_to_le32(rss);
- status = be_mbox_notify_wait(adapter);
+ status = be_mcc_notify_wait(adapter);
if (!status) {
struct be_cmd_resp_eth_rx_create *resp = embedded_payload(wrb);
rxq->id = le16_to_cpu(resp->id);
@@ -951,8 +1002,8 @@ int be_cmd_rxq_create(struct be_adapter *adapter,
*rss_id = resp->rss_id;
}
- mutex_unlock(&adapter->mbox_lock);
-
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -1001,61 +1052,90 @@ int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
BUG();
}
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, opcode);
-
- be_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req), wrb,
+ NULL);
req->id = cpu_to_le16(q->id);
status = be_mbox_notify_wait(adapter);
+ if (!status)
+ q->created = false;
mutex_unlock(&adapter->mbox_lock);
+ return status;
+}
+
+/* Uses MCC */
+int be_cmd_rxq_destroy(struct be_adapter *adapter, struct be_queue_info *q)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_q_destroy *req;
+ int status;
+
+ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ req = embedded_payload(wrb);
+
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_RX_DESTROY, sizeof(*req), wrb, NULL);
+ req->id = cpu_to_le16(q->id);
+
+ status = be_mcc_notify_wait(adapter);
+ if (!status)
+ q->created = false;
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
return status;
}
/* Create an rx filtering policy configuration on an i/f
- * Uses mbox
+ * Uses MCCQ
*/
int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags,
- u8 *mac, bool pmac_invalid, u32 *if_handle, u32 *pmac_id,
- u32 domain)
+ u8 *mac, u32 *if_handle, u32 *pmac_id, u32 domain)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_if_create *req;
int status;
- if (mutex_lock_interruptible(&adapter->mbox_lock))
- return -1;
+ spin_lock_bh(&adapter->mcc_lock);
- wrb = wrb_from_mbox(adapter);
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_INTERFACE_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_INTERFACE_CREATE, sizeof(*req));
-
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_INTERFACE_CREATE, sizeof(*req), wrb, NULL);
req->hdr.domain = domain;
req->capability_flags = cpu_to_le32(cap_flags);
req->enable_flags = cpu_to_le32(en_flags);
- req->pmac_invalid = pmac_invalid;
- if (!pmac_invalid)
+ if (mac)
memcpy(req->mac_addr, mac, ETH_ALEN);
+ else
+ req->pmac_invalid = true;
- status = be_mbox_notify_wait(adapter);
+ status = be_mcc_notify_wait(adapter);
if (!status) {
struct be_cmd_resp_if_create *resp = embedded_payload(wrb);
*if_handle = le32_to_cpu(resp->interface_id);
- if (!pmac_invalid)
+ if (mac)
*pmac_id = le32_to_cpu(resp->pmac_id);
}
- mutex_unlock(&adapter->mbox_lock);
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
return status;
}
-/* Uses mbox */
+/* Uses MCCQ */
int be_cmd_if_destroy(struct be_adapter *adapter, u32 interface_id, u32 domain)
{
struct be_mcc_wrb *wrb;
@@ -1065,25 +1145,26 @@ int be_cmd_if_destroy(struct be_adapter *adapter, u32 interface_id, u32 domain)
if (adapter->eeh_err)
return -EIO;
- if (mutex_lock_interruptible(&adapter->mbox_lock))
- return -1;
-
- wrb = wrb_from_mbox(adapter);
- req = embedded_payload(wrb);
+ if (!interface_id)
+ return 0;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_INTERFACE_DESTROY);
+ spin_lock_bh(&adapter->mcc_lock);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_INTERFACE_DESTROY, sizeof(*req));
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ req = embedded_payload(wrb);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_INTERFACE_DESTROY, sizeof(*req), wrb, NULL);
req->hdr.domain = domain;
req->interface_id = cpu_to_le32(interface_id);
- status = be_mbox_notify_wait(adapter);
-
- mutex_unlock(&adapter->mbox_lock);
-
+ status = be_mcc_notify_wait(adapter);
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -1095,7 +1176,6 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_hdr *hdr;
- struct be_sge *sge;
int status = 0;
if (MODULO(adapter->work_counter, be_get_temp_freq) == 0)
@@ -1109,22 +1189,13 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
goto err;
}
hdr = nonemb_cmd->va;
- sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, nonemb_cmd->size, false, 1,
- OPCODE_ETH_GET_STATISTICS);
-
- be_cmd_hdr_prepare(hdr, CMD_SUBSYSTEM_ETH,
- OPCODE_ETH_GET_STATISTICS, nonemb_cmd->size);
+ be_wrb_cmd_hdr_prepare(hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_GET_STATISTICS, nonemb_cmd->size, wrb, nonemb_cmd);
if (adapter->generation == BE_GEN3)
hdr->version = 1;
- wrb->tag1 = CMD_SUBSYSTEM_ETH;
- sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(nonemb_cmd->size);
-
be_mcc_notify(adapter);
adapter->stats_cmd_sent = true;
@@ -1140,7 +1211,6 @@ int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
struct be_mcc_wrb *wrb;
struct lancer_cmd_req_pport_stats *req;
- struct be_sge *sge;
int status = 0;
spin_lock_bh(&adapter->mcc_lock);
@@ -1151,23 +1221,14 @@ int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
goto err;
}
req = nonemb_cmd->va;
- sge = nonembedded_sgl(wrb);
-
- be_wrb_hdr_prepare(wrb, nonemb_cmd->size, false, 1,
- OPCODE_ETH_GET_PPORT_STATS);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
- OPCODE_ETH_GET_PPORT_STATS, nonemb_cmd->size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_GET_PPORT_STATS, nonemb_cmd->size, wrb,
+ nonemb_cmd);
req->cmd_params.params.pport_num = cpu_to_le16(adapter->port_num);
req->cmd_params.params.reset_stats = 0;
- wrb->tag1 = CMD_SUBSYSTEM_ETH;
- sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(nonemb_cmd->size);
-
be_mcc_notify(adapter);
adapter->stats_cmd_sent = true;
@@ -1177,8 +1238,8 @@ err:
}
/* Uses synchronous mcc */
-int be_cmd_link_status_query(struct be_adapter *adapter,
- bool *link_up, u8 *mac_speed, u16 *link_speed, u32 dom)
+int be_cmd_link_status_query(struct be_adapter *adapter, u8 *mac_speed,
+ u16 *link_speed, u32 dom)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_link_status *req;
@@ -1193,21 +1254,16 @@ int be_cmd_link_status_query(struct be_adapter *adapter,
}
req = embedded_payload(wrb);
- *link_up = false;
-
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_LINK_STATUS_QUERY);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_LINK_STATUS_QUERY, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_LINK_STATUS_QUERY, sizeof(*req), wrb, NULL);
status = be_mcc_notify_wait(adapter);
if (!status) {
struct be_cmd_resp_link_status *resp = embedded_payload(wrb);
if (resp->mac_speed != PHY_LINK_SPEED_ZERO) {
- *link_up = true;
*link_speed = le16_to_cpu(resp->link_speed);
- *mac_speed = resp->mac_speed;
+ if (mac_speed)
+ *mac_speed = resp->mac_speed;
}
}
@@ -1221,10 +1277,13 @@ int be_cmd_get_die_temperature(struct be_adapter *adapter)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_get_cntl_addnl_attribs *req;
+ u16 mccq_index;
int status;
spin_lock_bh(&adapter->mcc_lock);
+ mccq_index = adapter->mcc_obj.q.head;
+
wrb = wrb_from_mccq(adapter);
if (!wrb) {
status = -EBUSY;
@@ -1232,22 +1291,13 @@ int be_cmd_get_die_temperature(struct be_adapter *adapter)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES, sizeof(*req),
+ wrb, NULL);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES, sizeof(*req));
+ wrb->tag1 = mccq_index;
- status = be_mcc_notify_wait(adapter);
- if (!status) {
- struct be_cmd_resp_get_cntl_addnl_attribs *resp =
- embedded_payload(wrb);
- adapter->drv_stats.be_on_die_temperature =
- resp->on_die_temperature;
- }
- /* If IOCTL fails once, do not bother issuing it again */
- else
- be_get_temp_freq = 0;
+ be_mcc_notify(adapter);
err:
spin_unlock_bh(&adapter->mcc_lock);
@@ -1270,11 +1320,8 @@ int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_MANAGE_FAT);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MANAGE_FAT, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MANAGE_FAT, sizeof(*req), wrb, NULL);
req->fat_operation = cpu_to_le32(QUERY_FAT);
status = be_mcc_notify_wait(adapter);
if (!status) {
@@ -1293,7 +1340,6 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
struct be_dma_mem get_fat_cmd;
struct be_mcc_wrb *wrb;
struct be_cmd_req_get_fat *req;
- struct be_sge *sge;
u32 offset = 0, total_size, buf_size,
log_offset = sizeof(u32), payload_len;
int status;
@@ -1326,18 +1372,11 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
goto err;
}
req = get_fat_cmd.va;
- sge = nonembedded_sgl(wrb);
payload_len = sizeof(struct be_cmd_req_get_fat) + buf_size;
- be_wrb_hdr_prepare(wrb, payload_len, false, 1,
- OPCODE_COMMON_MANAGE_FAT);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MANAGE_FAT, payload_len);
-
- sge->pa_hi = cpu_to_le32(upper_32_bits(get_fat_cmd.dma));
- sge->pa_lo = cpu_to_le32(get_fat_cmd.dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(get_fat_cmd.size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MANAGE_FAT, payload_len, wrb,
+ &get_fat_cmd);
req->fat_operation = cpu_to_le32(RETRIEVE_FAT);
req->read_log_offset = cpu_to_le32(log_offset);
@@ -1349,7 +1388,7 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
struct be_cmd_resp_get_fat *resp = get_fat_cmd.va;
memcpy(buf + offset,
resp->data_buffer,
- resp->read_log_length);
+ le32_to_cpu(resp->read_log_length));
} else {
dev_err(&adapter->pdev->dev, "FAT Table Retrieve error\n");
goto err;
@@ -1364,32 +1403,35 @@ err:
spin_unlock_bh(&adapter->mcc_lock);
}
-/* Uses Mbox */
-int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver)
+/* Uses synchronous mcc */
+int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
+ char *fw_on_flash)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_get_fw_version *req;
int status;
- if (mutex_lock_interruptible(&adapter->mbox_lock))
- return -1;
-
- wrb = wrb_from_mbox(adapter);
- req = embedded_payload(wrb);
+ spin_lock_bh(&adapter->mcc_lock);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_GET_FW_VERSION);
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_FW_VERSION, sizeof(*req));
+ req = embedded_payload(wrb);
- status = be_mbox_notify_wait(adapter);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_FW_VERSION, sizeof(*req), wrb, NULL);
+ status = be_mcc_notify_wait(adapter);
if (!status) {
struct be_cmd_resp_get_fw_version *resp = embedded_payload(wrb);
- strncpy(fw_ver, resp->firmware_version_string, FW_VER_LEN);
+ strcpy(fw_ver, resp->firmware_version_string);
+ if (fw_on_flash)
+ strcpy(fw_on_flash, resp->fw_on_flash_version_string);
}
-
- mutex_unlock(&adapter->mbox_lock);
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -1411,11 +1453,8 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_MODIFY_EQ_DELAY);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req), wrb, NULL);
req->num_eq = cpu_to_le32(1);
req->delay[0].eq_id = cpu_to_le32(eq_id);
@@ -1446,11 +1485,8 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_VLAN_CONFIG);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req), wrb, NULL);
req->interface_id = if_id;
req->promiscuous = promiscuous;
@@ -1468,71 +1504,11 @@ err:
return status;
}
-/* Uses MCC for this command as it may be called in BH context
- * Uses synchronous mcc
- */
-int be_cmd_promiscuous_config(struct be_adapter *adapter, bool en)
-{
- struct be_mcc_wrb *wrb;
- struct be_cmd_req_rx_filter *req;
- struct be_dma_mem promiscous_cmd;
- struct be_sge *sge;
- int status;
-
- memset(&promiscous_cmd, 0, sizeof(struct be_dma_mem));
- promiscous_cmd.size = sizeof(struct be_cmd_req_rx_filter);
- promiscous_cmd.va = pci_alloc_consistent(adapter->pdev,
- promiscous_cmd.size, &promiscous_cmd.dma);
- if (!promiscous_cmd.va) {
- dev_err(&adapter->pdev->dev,
- "Memory allocation failure\n");
- return -ENOMEM;
- }
-
- spin_lock_bh(&adapter->mcc_lock);
-
- wrb = wrb_from_mccq(adapter);
- if (!wrb) {
- status = -EBUSY;
- goto err;
- }
-
- req = promiscous_cmd.va;
- sge = nonembedded_sgl(wrb);
-
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
- OPCODE_COMMON_NTWK_RX_FILTER);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_RX_FILTER, sizeof(*req));
-
- req->if_id = cpu_to_le32(adapter->if_handle);
- req->if_flags_mask = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS);
- if (en)
- req->if_flags = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS);
-
- sge->pa_hi = cpu_to_le32(upper_32_bits(promiscous_cmd.dma));
- sge->pa_lo = cpu_to_le32(promiscous_cmd.dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(promiscous_cmd.size);
-
- status = be_mcc_notify_wait(adapter);
-
-err:
- spin_unlock_bh(&adapter->mcc_lock);
- pci_free_consistent(adapter->pdev, promiscous_cmd.size,
- promiscous_cmd.va, promiscous_cmd.dma);
- return status;
-}
-
-/*
- * Uses MCC for this command as it may be called in BH context
- * (mc == NULL) => multicast promiscuous
- */
-int be_cmd_multicast_set(struct be_adapter *adapter, u32 if_id,
- struct net_device *netdev, struct be_dma_mem *mem)
+int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
{
struct be_mcc_wrb *wrb;
- struct be_cmd_req_mcast_mac_config *req = mem->va;
- struct be_sge *sge;
+ struct be_dma_mem *mem = &adapter->rx_filter;
+ struct be_cmd_req_rx_filter *req = mem->va;
int status;
spin_lock_bh(&adapter->mcc_lock);
@@ -1542,34 +1518,40 @@ int be_cmd_multicast_set(struct be_adapter *adapter, u32 if_id,
status = -EBUSY;
goto err;
}
- sge = nonembedded_sgl(wrb);
memset(req, 0, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_RX_FILTER, sizeof(*req),
+ wrb, mem);
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
- OPCODE_COMMON_NTWK_MULTICAST_SET);
- sge->pa_hi = cpu_to_le32(upper_32_bits(mem->dma));
- sge->pa_lo = cpu_to_le32(mem->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(mem->size);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_MULTICAST_SET, sizeof(*req));
-
- req->interface_id = if_id;
- if (netdev) {
- int i;
+ req->if_id = cpu_to_le32(adapter->if_handle);
+ if (flags & IFF_PROMISC) {
+ req->if_flags_mask = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS |
+ BE_IF_FLAGS_VLAN_PROMISCUOUS);
+ if (value == ON)
+ req->if_flags = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS |
+ BE_IF_FLAGS_VLAN_PROMISCUOUS);
+ } else if (flags & IFF_ALLMULTI) {
+ req->if_flags_mask = req->if_flags =
+ cpu_to_le32(BE_IF_FLAGS_MCAST_PROMISCUOUS);
+ } else {
struct netdev_hw_addr *ha;
+ int i = 0;
- req->num_mac = cpu_to_le16(netdev_mc_count(netdev));
+ req->if_flags_mask = req->if_flags =
+ cpu_to_le32(BE_IF_FLAGS_MULTICAST);
- i = 0;
- netdev_for_each_mc_addr(ha, netdev)
- memcpy(req->mac[i++].byte, ha->addr, ETH_ALEN);
- } else {
- req->promiscuous = 1;
+ /* Reset mcast promisc mode if already set by setting mask
+ * and not setting flags field
+ */
+ req->if_flags_mask |=
+ cpu_to_le32(BE_IF_FLAGS_MCAST_PROMISCUOUS);
+
+ req->mcast_num = cpu_to_le32(netdev_mc_count(adapter->netdev));
+ netdev_for_each_mc_addr(ha, adapter->netdev)
+ memcpy(req->mcast_mac[i++].byte, ha->addr, ETH_ALEN);
}
status = be_mcc_notify_wait(adapter);
-
err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
@@ -1591,11 +1573,8 @@ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_SET_FLOW_CONTROL);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_SET_FLOW_CONTROL, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SET_FLOW_CONTROL, sizeof(*req), wrb, NULL);
req->tx_flow_control = cpu_to_le16((u16)tx_fc);
req->rx_flow_control = cpu_to_le16((u16)rx_fc);
@@ -1623,11 +1602,8 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_GET_FLOW_CONTROL);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_FLOW_CONTROL, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_FLOW_CONTROL, sizeof(*req), wrb, NULL);
status = be_mcc_notify_wait(adapter);
if (!status) {
@@ -1656,11 +1632,8 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_QUERY_FIRMWARE_CONFIG);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req), wrb, NULL);
status = be_mbox_notify_wait(adapter);
if (!status) {
@@ -1687,11 +1660,8 @@ int be_cmd_reset_function(struct be_adapter *adapter)
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_FUNCTION_RESET);
-
- be_cmd_hdr_prepare(req, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_FUNCTION_RESET, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(req, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_FUNCTION_RESET, sizeof(*req), wrb, NULL);
status = be_mbox_notify_wait(adapter);
@@ -1713,11 +1683,8 @@ int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable, u16 table_size)
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_ETH_RSS_CONFIG);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
- OPCODE_ETH_RSS_CONFIG, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_RSS_CONFIG, sizeof(*req), wrb, NULL);
req->if_id = cpu_to_le32(adapter->if_handle);
req->enable_rss = cpu_to_le16(RSS_ENABLE_TCP_IPV4 | RSS_ENABLE_IPV4);
@@ -1749,11 +1716,8 @@ int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_ENABLE_DISABLE_BEACON);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_ENABLE_DISABLE_BEACON, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_ENABLE_DISABLE_BEACON, sizeof(*req), wrb, NULL);
req->port_num = port_num;
req->beacon_state = state;
@@ -1783,11 +1747,8 @@ int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num, u32 *state)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_GET_BEACON_STATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_BEACON_STATE, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_BEACON_STATE, sizeof(*req), wrb, NULL);
req->port_num = port_num;
@@ -1824,13 +1785,10 @@ int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(struct lancer_cmd_req_write_object),
- true, 1, OPCODE_COMMON_WRITE_OBJECT);
- wrb->tag1 = CMD_SUBSYSTEM_COMMON;
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_WRITE_OBJECT,
- sizeof(struct lancer_cmd_req_write_object));
+ sizeof(struct lancer_cmd_req_write_object), wrb,
+ NULL);
ctxt = &req->context;
AMAP_SET_BITS(struct amap_lancer_write_obj_context,
@@ -1883,7 +1841,6 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
{
struct be_mcc_wrb *wrb;
struct be_cmd_write_flashrom *req;
- struct be_sge *sge;
int status;
spin_lock_bh(&adapter->mcc_lock);
@@ -1895,17 +1852,9 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
goto err_unlock;
}
req = cmd->va;
- sge = nonembedded_sgl(wrb);
-
- be_wrb_hdr_prepare(wrb, cmd->size, false, 1,
- OPCODE_COMMON_WRITE_FLASHROM);
- wrb->tag1 = CMD_SUBSYSTEM_COMMON;
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_WRITE_FLASHROM, cmd->size);
- sge->pa_hi = cpu_to_le32(upper_32_bits(cmd->dma));
- sge->pa_lo = cpu_to_le32(cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(cmd->size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_WRITE_FLASHROM, cmd->size, wrb, cmd);
req->params.op_type = cpu_to_le32(flash_type);
req->params.op_code = cpu_to_le32(flash_opcode);
@@ -1915,7 +1864,7 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
spin_unlock_bh(&adapter->mcc_lock);
if (!wait_for_completion_timeout(&adapter->flash_compl,
- msecs_to_jiffies(12000)))
+ msecs_to_jiffies(40000)))
status = -1;
else
status = adapter->flash_status;
@@ -1943,11 +1892,8 @@ int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req)+4, true, 0,
- OPCODE_COMMON_READ_FLASHROM);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_READ_FLASHROM, sizeof(*req)+4);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_READ_FLASHROM, sizeof(*req)+4, wrb, NULL);
req->params.op_type = cpu_to_le32(IMG_TYPE_REDBOOT);
req->params.op_code = cpu_to_le32(FLASHROM_OPER_REPORT);
@@ -1968,7 +1914,6 @@ int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_acpi_wol_magic_config *req;
- struct be_sge *sge;
int status;
spin_lock_bh(&adapter->mcc_lock);
@@ -1979,19 +1924,12 @@ int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
goto err;
}
req = nonemb_cmd->va;
- sge = nonembedded_sgl(wrb);
-
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
- OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
- OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, sizeof(*req), wrb,
+ nonemb_cmd);
memcpy(req->magic_mac, mac, ETH_ALEN);
- sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(nonemb_cmd->size);
-
status = be_mcc_notify_wait(adapter);
err:
@@ -2016,12 +1954,9 @@ int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_LOWLEVEL_SET_LOOPBACK_MODE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
- OPCODE_LOWLEVEL_SET_LOOPBACK_MODE,
- sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
+ OPCODE_LOWLEVEL_SET_LOOPBACK_MODE, sizeof(*req), wrb,
+ NULL);
req->src_port = port_num;
req->dest_port = port_num;
@@ -2051,11 +1986,8 @@ int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_LOWLEVEL_LOOPBACK_TEST);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
- OPCODE_LOWLEVEL_LOOPBACK_TEST, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
+ OPCODE_LOWLEVEL_LOOPBACK_TEST, sizeof(*req), wrb, NULL);
req->hdr.timeout = cpu_to_le32(4);
req->pattern = cpu_to_le64(pattern);
@@ -2081,7 +2013,6 @@ int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_ddrdma_test *req;
- struct be_sge *sge;
int status;
int i, j = 0;
@@ -2093,15 +2024,8 @@ int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
goto err;
}
req = cmd->va;
- sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, cmd->size, false, 1,
- OPCODE_LOWLEVEL_HOST_DDR_DMA);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
- OPCODE_LOWLEVEL_HOST_DDR_DMA, cmd->size);
-
- sge->pa_hi = cpu_to_le32(upper_32_bits(cmd->dma));
- sge->pa_lo = cpu_to_le32(cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(cmd->size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
+ OPCODE_LOWLEVEL_HOST_DDR_DMA, cmd->size, wrb, cmd);
req->pattern = cpu_to_le64(pattern);
req->byte_count = cpu_to_le32(byte_cnt);
@@ -2146,15 +2070,9 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
req = nonemb_cmd->va;
sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
- OPCODE_COMMON_SEEPROM_READ);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_SEEPROM_READ, sizeof(*req));
-
- sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(nonemb_cmd->size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SEEPROM_READ, sizeof(*req), wrb,
+ nonemb_cmd);
status = be_mcc_notify_wait(adapter);
@@ -2163,11 +2081,12 @@ err:
return status;
}
-int be_cmd_get_phy_info(struct be_adapter *adapter, struct be_dma_mem *cmd)
+int be_cmd_get_phy_info(struct be_adapter *adapter,
+ struct be_phy_info *phy_info)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_get_phy_info *req;
- struct be_sge *sge;
+ struct be_dma_mem cmd;
int status;
spin_lock_bh(&adapter->mcc_lock);
@@ -2177,22 +2096,31 @@ int be_cmd_get_phy_info(struct be_adapter *adapter, struct be_dma_mem *cmd)
status = -EBUSY;
goto err;
}
+ cmd.size = sizeof(struct be_cmd_req_get_phy_info);
+ cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size,
+ &cmd.dma);
+ if (!cmd.va) {
+ dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
+ status = -ENOMEM;
+ goto err;
+ }
- req = cmd->va;
- sge = nonembedded_sgl(wrb);
-
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
- OPCODE_COMMON_GET_PHY_DETAILS);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_PHY_DETAILS,
- sizeof(*req));
+ req = cmd.va;
- sge->pa_hi = cpu_to_le32(upper_32_bits(cmd->dma));
- sge->pa_lo = cpu_to_le32(cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(cmd->size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_PHY_DETAILS, sizeof(*req),
+ wrb, &cmd);
status = be_mcc_notify_wait(adapter);
+ if (!status) {
+ struct be_phy_info *resp_phy_info =
+ cmd.va + sizeof(struct be_cmd_req_hdr);
+ phy_info->phy_type = le16_to_cpu(resp_phy_info->phy_type);
+ phy_info->interface_type =
+ le16_to_cpu(resp_phy_info->interface_type);
+ }
+ pci_free_consistent(adapter->pdev, cmd.size,
+ cmd.va, cmd.dma);
err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
@@ -2214,11 +2142,8 @@ int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain)
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_SET_QOS);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_SET_QOS, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SET_QOS, sizeof(*req), wrb, NULL);
req->hdr.domain = domain;
req->valid_bits = cpu_to_le32(BE_QOS_BITS_NIC);
@@ -2236,7 +2161,6 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
struct be_mcc_wrb *wrb;
struct be_cmd_req_cntl_attribs *req;
struct be_cmd_resp_cntl_attribs *resp;
- struct be_sge *sge;
int status;
int payload_len = max(sizeof(*req), sizeof(*resp));
struct mgmt_controller_attrib *attribs;
@@ -2261,20 +2185,14 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
goto err;
}
req = attribs_cmd.va;
- sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, payload_len, false, 1,
- OPCODE_COMMON_GET_CNTL_ATTRIBUTES);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_CNTL_ATTRIBUTES, payload_len);
- sge->pa_hi = cpu_to_le32(upper_32_bits(attribs_cmd.dma));
- sge->pa_lo = cpu_to_le32(attribs_cmd.dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(attribs_cmd.size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_CNTL_ATTRIBUTES, payload_len, wrb,
+ &attribs_cmd);
status = be_mbox_notify_wait(adapter);
if (!status) {
- attribs = (struct mgmt_controller_attrib *)( attribs_cmd.va +
- sizeof(struct be_cmd_resp_hdr));
+ attribs = attribs_cmd.va + sizeof(struct be_cmd_resp_hdr);
adapter->hba_port_num = attribs->hba_attribs.phy_port;
}
@@ -2286,7 +2204,7 @@ err:
}
/* Uses mbox */
-int be_cmd_check_native_mode(struct be_adapter *adapter)
+int be_cmd_req_native_mode(struct be_adapter *adapter)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_set_func_cap *req;
@@ -2303,11 +2221,8 @@ int be_cmd_check_native_mode(struct be_adapter *adapter)
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP, sizeof(*req), wrb, NULL);
req->valid_cap_flags = cpu_to_le32(CAPABILITY_SW_TIMESTAMPS |
CAPABILITY_BE3_NATIVE_ERX_API);
diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h
index 8148cc66cbe9..a35cd03fac4e 100644
--- a/drivers/net/benet/be_cmds.h
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.h
@@ -51,17 +51,12 @@ struct be_mcc_wrb {
/* Completion Status */
enum {
- MCC_STATUS_SUCCESS = 0x0,
-/* The client does not have sufficient privileges to execute the command */
- MCC_STATUS_INSUFFICIENT_PRIVILEGES = 0x1,
-/* A parameter in the command was invalid. */
- MCC_STATUS_INVALID_PARAMETER = 0x2,
-/* There are insufficient chip resources to execute the command */
- MCC_STATUS_INSUFFICIENT_RESOURCES = 0x3,
-/* The command is completing because the queue was getting flushed */
- MCC_STATUS_QUEUE_FLUSHING = 0x4,
-/* The command is completing with a DMA error */
- MCC_STATUS_DMA_FAILED = 0x5,
+ MCC_STATUS_SUCCESS = 0,
+ MCC_STATUS_FAILED = 1,
+ MCC_STATUS_ILLEGAL_REQUEST = 2,
+ MCC_STATUS_ILLEGAL_FIELD = 3,
+ MCC_STATUS_INSUFFICIENT_BUFFER = 4,
+ MCC_STATUS_UNAUTHORIZED_REQUEST = 5,
MCC_STATUS_NOT_SUPPORTED = 66
};
@@ -94,9 +89,10 @@ struct be_async_event_trailer {
};
enum {
- ASYNC_EVENT_LINK_DOWN = 0x0,
- ASYNC_EVENT_LINK_UP = 0x1
+ LINK_DOWN = 0x0,
+ LINK_UP = 0x1
};
+#define LINK_STATUS_MASK 0x1
/* When the event code of an async trailer is link-state, the mcc_compl
* must be interpreted as follows
@@ -434,6 +430,14 @@ struct be_cmd_req_mcc_create {
struct be_cmd_req_hdr hdr;
u16 num_pages;
u16 cq_id;
+ u8 context[sizeof(struct amap_mcc_context_be) / 8];
+ struct phys_addr pages[8];
+} __packed;
+
+struct be_cmd_req_mcc_ext_create {
+ struct be_cmd_req_hdr hdr;
+ u16 num_pages;
+ u16 cq_id;
u32 async_event_bitmap[1];
u8 context[sizeof(struct amap_mcc_context_be) / 8];
struct phys_addr pages[8];
@@ -690,8 +694,7 @@ struct be_cmd_resp_get_stats_v0 {
struct be_hw_stats_v0 hw_stats;
};
-#define make_64bit_val(hi_32, lo_32) (((u64)hi_32<<32) | lo_32)
-struct lancer_cmd_pport_stats {
+struct lancer_pport_stats {
u32 tx_packets_lo;
u32 tx_packets_hi;
u32 tx_unicast_packets_lo;
@@ -868,16 +871,16 @@ struct lancer_cmd_req_pport_stats {
struct be_cmd_req_hdr hdr;
union {
struct pport_stats_params params;
- u8 rsvd[sizeof(struct lancer_cmd_pport_stats)];
+ u8 rsvd[sizeof(struct lancer_pport_stats)];
} cmd_params;
};
struct lancer_cmd_resp_pport_stats {
struct be_cmd_resp_hdr hdr;
- struct lancer_cmd_pport_stats pport_stats;
+ struct lancer_pport_stats pport_stats;
};
-static inline struct lancer_cmd_pport_stats*
+static inline struct lancer_pport_stats*
pport_stats_from_cmd(struct be_adapter *adapter)
{
struct lancer_cmd_resp_pport_stats *cmd = adapter->stats_cmd.va;
@@ -907,21 +910,12 @@ struct be_cmd_req_vlan_config {
u16 normal_vlan[64];
} __packed;
-/******************** Multicast MAC Config *******************/
+/******************* RX FILTER ******************************/
#define BE_MAX_MC 64 /* set mcast promisc if > 64 */
struct macaddr {
u8 byte[ETH_ALEN];
};
-struct be_cmd_req_mcast_mac_config {
- struct be_cmd_req_hdr hdr;
- u16 num_mac;
- u8 promiscuous;
- u8 interface_id;
- struct macaddr mac[BE_MAX_MC];
-} __packed;
-
-/******************* RX FILTER ******************************/
struct be_cmd_req_rx_filter {
struct be_cmd_req_hdr hdr;
u32 global_flags_mask;
@@ -929,11 +923,10 @@ struct be_cmd_req_rx_filter {
u32 if_flags_mask;
u32 if_flags;
u32 if_id;
- u32 multicast_num;
- struct macaddr mac[BE_MAX_MC];
+ u32 mcast_num;
+ struct macaddr mcast_mac[BE_MAX_MC];
};
-
/******************** Link Status Query *******************/
struct be_cmd_req_link_status {
struct be_cmd_req_hdr hdr;
@@ -1053,6 +1046,12 @@ struct be_cmd_resp_modify_eq_delay {
/******************** Get FW Config *******************/
#define BE_FUNCTION_CAPS_RSS 0x2
+/* The HW can come up in either of the following multi-channel modes
+ * based on the skew/IPL.
+ */
+#define FLEX10_MODE 0x400
+#define VNIC_MODE 0x20000
+#define UMC_ENABLED 0x1000000
struct be_cmd_req_query_fw_cfg {
struct be_cmd_req_hdr hdr;
u32 rsvd[31];
@@ -1251,14 +1250,19 @@ struct be_cmd_req_get_phy_info {
struct be_cmd_req_hdr hdr;
u8 rsvd0[24];
};
-struct be_cmd_resp_get_phy_info {
- struct be_cmd_req_hdr hdr;
+
+struct be_phy_info {
u16 phy_type;
u16 interface_type;
u32 misc_params;
u32 future_use[4];
};
+struct be_cmd_resp_get_phy_info {
+ struct be_cmd_req_hdr hdr;
+ struct be_phy_info phy_info;
+};
+
/*********************** Set QOS ***********************/
#define BE_QOS_BITS_NIC 1
@@ -1380,8 +1384,7 @@ struct be_cmd_resp_get_stats_v1 {
struct be_hw_stats_v1 hw_stats;
};
-static inline void *
-hw_stats_from_cmd(struct be_adapter *adapter)
+static inline void *hw_stats_from_cmd(struct be_adapter *adapter)
{
if (adapter->generation == BE_GEN3) {
struct be_cmd_resp_get_stats_v1 *cmd = adapter->stats_cmd.va;
@@ -1394,34 +1397,6 @@ hw_stats_from_cmd(struct be_adapter *adapter)
}
}
-static inline void *be_port_rxf_stats_from_cmd(struct be_adapter *adapter)
-{
- if (adapter->generation == BE_GEN3) {
- struct be_hw_stats_v1 *hw_stats = hw_stats_from_cmd(adapter);
- struct be_rxf_stats_v1 *rxf_stats = &hw_stats->rxf;
-
- return &rxf_stats->port[adapter->port_num];
- } else {
- struct be_hw_stats_v0 *hw_stats = hw_stats_from_cmd(adapter);
- struct be_rxf_stats_v0 *rxf_stats = &hw_stats->rxf;
-
- return &rxf_stats->port[adapter->port_num];
- }
-}
-
-static inline void *be_rxf_stats_from_cmd(struct be_adapter *adapter)
-{
- if (adapter->generation == BE_GEN3) {
- struct be_hw_stats_v1 *hw_stats = hw_stats_from_cmd(adapter);
-
- return &hw_stats->rxf;
- } else {
- struct be_hw_stats_v0 *hw_stats = hw_stats_from_cmd(adapter);
-
- return &hw_stats->rxf;
- }
-}
-
static inline void *be_erx_stats_from_cmd(struct be_adapter *adapter)
{
if (adapter->generation == BE_GEN3) {
@@ -1435,19 +1410,6 @@ static inline void *be_erx_stats_from_cmd(struct be_adapter *adapter)
}
}
-static inline void *be_pmem_stats_from_cmd(struct be_adapter *adapter)
-{
- if (adapter->generation == BE_GEN3) {
- struct be_hw_stats_v1 *hw_stats = hw_stats_from_cmd(adapter);
-
- return &hw_stats->pmem;
- } else {
- struct be_hw_stats_v0 *hw_stats = hw_stats_from_cmd(adapter);
-
- return &hw_stats->pmem;
- }
-}
-
extern int be_pci_fnum_get(struct be_adapter *adapter);
extern int be_cmd_POST(struct be_adapter *adapter);
extern int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
@@ -1457,8 +1419,8 @@ extern int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
extern int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id,
u32 pmac_id, u32 domain);
extern int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags,
- u32 en_flags, u8 *mac, bool pmac_invalid,
- u32 *if_handle, u32 *pmac_id, u32 domain);
+ u32 en_flags, u8 *mac, u32 *if_handle, u32 *pmac_id,
+ u32 domain);
extern int be_cmd_if_destroy(struct be_adapter *adapter, u32 if_handle,
u32 domain);
extern int be_cmd_eq_create(struct be_adapter *adapter,
@@ -1479,22 +1441,23 @@ extern int be_cmd_rxq_create(struct be_adapter *adapter,
u32 rss, u8 *rss_id);
extern int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
int type);
+extern int be_cmd_rxq_destroy(struct be_adapter *adapter,
+ struct be_queue_info *q);
extern int be_cmd_link_status_query(struct be_adapter *adapter,
- bool *link_up, u8 *mac_speed, u16 *link_speed, u32 dom);
+ u8 *mac_speed, u16 *link_speed, u32 dom);
extern int be_cmd_reset(struct be_adapter *adapter);
extern int be_cmd_get_stats(struct be_adapter *adapter,
struct be_dma_mem *nonemb_cmd);
extern int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
struct be_dma_mem *nonemb_cmd);
-extern int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver);
+extern int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
+ char *fw_on_flash);
extern int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd);
extern int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id,
u16 *vtag_array, u32 num, bool untagged,
bool promiscuous);
-extern int be_cmd_promiscuous_config(struct be_adapter *adapter, bool en);
-extern int be_cmd_multicast_set(struct be_adapter *adapter, u32 if_id,
- struct net_device *netdev, struct be_dma_mem *mem);
+extern int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 status);
extern int be_cmd_set_flow_control(struct be_adapter *adapter,
u32 tx_fc, u32 rx_fc);
extern int be_cmd_get_flow_control(struct be_adapter *adapter,
@@ -1535,12 +1498,12 @@ extern int be_cmd_get_seeprom_data(struct be_adapter *adapter,
extern int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
u8 loopback_type, u8 enable);
extern int be_cmd_get_phy_info(struct be_adapter *adapter,
- struct be_dma_mem *cmd);
+ struct be_phy_info *phy_info);
extern int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain);
extern void be_detect_dump_ue(struct be_adapter *adapter);
extern int be_cmd_get_die_temperature(struct be_adapter *adapter);
extern int be_cmd_get_cntl_attributes(struct be_adapter *adapter);
-extern int be_cmd_check_native_mode(struct be_adapter *adapter);
+extern int be_cmd_req_native_mode(struct be_adapter *adapter);
extern int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size);
extern void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf);
diff --git a/drivers/net/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c
index facfe3ca5c40..bf8153ea4ed8 100644
--- a/drivers/net/benet/be_ethtool.c
+++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c
@@ -26,38 +26,18 @@ struct be_ethtool_stat {
int offset;
};
-enum {NETSTAT, DRVSTAT_TX, DRVSTAT_RX, ERXSTAT,
- DRVSTAT};
+enum {DRVSTAT_TX, DRVSTAT_RX, DRVSTAT};
#define FIELDINFO(_struct, field) FIELD_SIZEOF(_struct, field), \
offsetof(_struct, field)
-#define NETSTAT_INFO(field) #field, NETSTAT,\
- FIELDINFO(struct net_device_stats,\
- field)
#define DRVSTAT_TX_INFO(field) #field, DRVSTAT_TX,\
FIELDINFO(struct be_tx_stats, field)
#define DRVSTAT_RX_INFO(field) #field, DRVSTAT_RX,\
FIELDINFO(struct be_rx_stats, field)
-#define ERXSTAT_INFO(field) #field, ERXSTAT,\
- FIELDINFO(struct be_erx_stats_v1, field)
#define DRVSTAT_INFO(field) #field, DRVSTAT,\
- FIELDINFO(struct be_drv_stats, \
- field)
+ FIELDINFO(struct be_drv_stats, field)
static const struct be_ethtool_stat et_stats[] = {
- {NETSTAT_INFO(rx_packets)},
- {NETSTAT_INFO(tx_packets)},
- {NETSTAT_INFO(rx_bytes)},
- {NETSTAT_INFO(tx_bytes)},
- {NETSTAT_INFO(rx_errors)},
- {NETSTAT_INFO(tx_errors)},
- {NETSTAT_INFO(rx_dropped)},
- {NETSTAT_INFO(tx_dropped)},
- {DRVSTAT_TX_INFO(be_tx_rate)},
- {DRVSTAT_TX_INFO(be_tx_reqs)},
- {DRVSTAT_TX_INFO(be_tx_wrbs)},
- {DRVSTAT_TX_INFO(be_tx_stops)},
- {DRVSTAT_TX_INFO(be_tx_events)},
- {DRVSTAT_TX_INFO(be_tx_compl)},
+ {DRVSTAT_INFO(tx_events)},
{DRVSTAT_INFO(rx_crc_errors)},
{DRVSTAT_INFO(rx_alignment_symbol_errors)},
{DRVSTAT_INFO(rx_pause_frames)},
@@ -76,9 +56,6 @@ static const struct be_ethtool_stat et_stats[] = {
{DRVSTAT_INFO(rx_ip_checksum_errs)},
{DRVSTAT_INFO(rx_tcp_checksum_errs)},
{DRVSTAT_INFO(rx_udp_checksum_errs)},
- {DRVSTAT_INFO(rx_switched_unicast_packets)},
- {DRVSTAT_INFO(rx_switched_multicast_packets)},
- {DRVSTAT_INFO(rx_switched_broadcast_packets)},
{DRVSTAT_INFO(tx_pauseframes)},
{DRVSTAT_INFO(tx_controlframes)},
{DRVSTAT_INFO(rx_priority_pause_frames)},
@@ -97,20 +74,36 @@ static const struct be_ethtool_stat et_stats[] = {
};
#define ETHTOOL_STATS_NUM ARRAY_SIZE(et_stats)
-/* Stats related to multi RX queues */
+/* Stats related to multi RX queues: get_stats routine assumes bytes, pkts
+ * are first and second members respectively.
+ */
static const struct be_ethtool_stat et_rx_stats[] = {
- {DRVSTAT_RX_INFO(rx_bytes)},
- {DRVSTAT_RX_INFO(rx_pkts)},
- {DRVSTAT_RX_INFO(rx_rate)},
+ {DRVSTAT_RX_INFO(rx_bytes)},/* If moving this member see above note */
+ {DRVSTAT_RX_INFO(rx_pkts)}, /* If moving this member see above note */
{DRVSTAT_RX_INFO(rx_polls)},
{DRVSTAT_RX_INFO(rx_events)},
{DRVSTAT_RX_INFO(rx_compl)},
{DRVSTAT_RX_INFO(rx_mcast_pkts)},
{DRVSTAT_RX_INFO(rx_post_fail)},
- {ERXSTAT_INFO(rx_drops_no_fragments)}
+ {DRVSTAT_RX_INFO(rx_drops_no_skbs)},
+ {DRVSTAT_RX_INFO(rx_drops_no_frags)}
};
#define ETHTOOL_RXSTATS_NUM (ARRAY_SIZE(et_rx_stats))
+/* Stats related to multi TX queues: get_stats routine assumes compl is the
+ * first member
+ */
+static const struct be_ethtool_stat et_tx_stats[] = {
+ {DRVSTAT_TX_INFO(tx_compl)}, /* If moving this member see above note */
+ {DRVSTAT_TX_INFO(tx_bytes)},
+ {DRVSTAT_TX_INFO(tx_pkts)},
+ {DRVSTAT_TX_INFO(tx_reqs)},
+ {DRVSTAT_TX_INFO(tx_wrbs)},
+ {DRVSTAT_TX_INFO(tx_compl)},
+ {DRVSTAT_TX_INFO(tx_stops)}
+};
+#define ETHTOOL_TXSTATS_NUM (ARRAY_SIZE(et_tx_stats))
+
static const char et_self_tests[][ETH_GSTRING_LEN] = {
"MAC Loopback test",
"PHY Loopback test",
@@ -125,14 +118,24 @@ static const char et_self_tests[][ETH_GSTRING_LEN] = {
#define BE_ONE_PORT_EXT_LOOPBACK 0x2
#define BE_NO_LOOPBACK 0xff
-static void
-be_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
+static void be_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
{
struct be_adapter *adapter = netdev_priv(netdev);
+ char fw_on_flash[FW_VER_LEN];
+
+ memset(fw_on_flash, 0 , sizeof(fw_on_flash));
+ be_cmd_get_fw_ver(adapter, adapter->fw_ver, fw_on_flash);
strcpy(drvinfo->driver, DRV_NAME);
strcpy(drvinfo->version, DRV_VER);
strncpy(drvinfo->fw_version, adapter->fw_ver, FW_VER_LEN);
+ if (memcmp(adapter->fw_ver, fw_on_flash, FW_VER_LEN) != 0) {
+ strcat(drvinfo->fw_version, " [");
+ strcat(drvinfo->fw_version, fw_on_flash);
+ strcat(drvinfo->fw_version, "]");
+ }
+
strcpy(drvinfo->bus_info, pci_name(adapter->pdev));
drvinfo->testinfo_len = 0;
drvinfo->regdump_len = 0;
@@ -253,42 +256,50 @@ be_get_ethtool_stats(struct net_device *netdev,
{
struct be_adapter *adapter = netdev_priv(netdev);
struct be_rx_obj *rxo;
- void *p = NULL;
- int i, j;
+ struct be_tx_obj *txo;
+ void *p;
+ unsigned int i, j, base = 0, start;
for (i = 0; i < ETHTOOL_STATS_NUM; i++) {
- switch (et_stats[i].type) {
- case NETSTAT:
- p = &netdev->stats;
- break;
- case DRVSTAT_TX:
- p = &adapter->tx_stats;
- break;
- case DRVSTAT:
- p = &adapter->drv_stats;
- break;
- }
-
- p = (u8 *)p + et_stats[i].offset;
- data[i] = (et_stats[i].size == sizeof(u64)) ?
- *(u64 *)p: *(u32 *)p;
+ p = (u8 *)&adapter->drv_stats + et_stats[i].offset;
+ data[i] = *(u32 *)p;
}
+ base += ETHTOOL_STATS_NUM;
for_all_rx_queues(adapter, rxo, j) {
- for (i = 0; i < ETHTOOL_RXSTATS_NUM; i++) {
- switch (et_rx_stats[i].type) {
- case DRVSTAT_RX:
- p = (u8 *)&rxo->stats + et_rx_stats[i].offset;
- break;
- case ERXSTAT:
- p = (u32 *)be_erx_stats_from_cmd(adapter) +
- rxo->q.id;
- break;
- }
- data[ETHTOOL_STATS_NUM + j * ETHTOOL_RXSTATS_NUM + i] =
- (et_rx_stats[i].size == sizeof(u64)) ?
- *(u64 *)p: *(u32 *)p;
+ struct be_rx_stats *stats = rx_stats(rxo);
+
+ do {
+ start = u64_stats_fetch_begin_bh(&stats->sync);
+ data[base] = stats->rx_bytes;
+ data[base + 1] = stats->rx_pkts;
+ } while (u64_stats_fetch_retry_bh(&stats->sync, start));
+
+ for (i = 2; i < ETHTOOL_RXSTATS_NUM; i++) {
+ p = (u8 *)stats + et_rx_stats[i].offset;
+ data[base + i] = *(u32 *)p;
}
+ base += ETHTOOL_RXSTATS_NUM;
+ }
+
+ for_all_tx_queues(adapter, txo, j) {
+ struct be_tx_stats *stats = tx_stats(txo);
+
+ do {
+ start = u64_stats_fetch_begin_bh(&stats->sync_compl);
+ data[base] = stats->tx_compl;
+ } while (u64_stats_fetch_retry_bh(&stats->sync_compl, start));
+
+ do {
+ start = u64_stats_fetch_begin_bh(&stats->sync);
+ for (i = 1; i < ETHTOOL_TXSTATS_NUM; i++) {
+ p = (u8 *)stats + et_tx_stats[i].offset;
+ data[base + i] =
+ (et_tx_stats[i].size == sizeof(u64)) ?
+ *(u64 *)p : *(u32 *)p;
+ }
+ } while (u64_stats_fetch_retry_bh(&stats->sync, start));
+ base += ETHTOOL_TXSTATS_NUM;
}
}
@@ -312,6 +323,13 @@ be_get_stat_strings(struct net_device *netdev, uint32_t stringset,
data += ETH_GSTRING_LEN;
}
}
+ for (i = 0; i < adapter->num_tx_qs; i++) {
+ for (j = 0; j < ETHTOOL_TXSTATS_NUM; j++) {
+ sprintf(data, "txq%d: %s", i,
+ et_tx_stats[j].desc);
+ data += ETH_GSTRING_LEN;
+ }
+ }
break;
case ETH_SS_TEST:
for (i = 0; i < ETHTOOL_TESTS_NUM; i++) {
@@ -331,7 +349,8 @@ static int be_get_sset_count(struct net_device *netdev, int stringset)
return ETHTOOL_TESTS_NUM;
case ETH_SS_STATS:
return ETHTOOL_STATS_NUM +
- adapter->num_rx_qs * ETHTOOL_RXSTATS_NUM;
+ adapter->num_rx_qs * ETHTOOL_RXSTATS_NUM +
+ adapter->num_tx_qs * ETHTOOL_TXSTATS_NUM;
default:
return -EINVAL;
}
@@ -340,19 +359,15 @@ static int be_get_sset_count(struct net_device *netdev, int stringset)
static int be_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
{
struct be_adapter *adapter = netdev_priv(netdev);
- struct be_dma_mem phy_cmd;
- struct be_cmd_resp_get_phy_info *resp;
+ struct be_phy_info phy_info;
u8 mac_speed = 0;
u16 link_speed = 0;
- bool link_up = false;
int status;
- u16 intf_type;
if ((adapter->link_speed < 0) || (!(netdev->flags & IFF_UP))) {
- status = be_cmd_link_status_query(adapter, &link_up,
- &mac_speed, &link_speed, 0);
+ status = be_cmd_link_status_query(adapter, &mac_speed,
+ &link_speed, 0);
- be_link_status_update(adapter, link_up);
/* link_speed is in units of 10 Mbps */
if (link_speed) {
ethtool_cmd_speed_set(ecmd, link_speed*10);
@@ -376,20 +391,9 @@ static int be_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
}
}
- phy_cmd.size = sizeof(struct be_cmd_req_get_phy_info);
- phy_cmd.va = dma_alloc_coherent(&adapter->pdev->dev,
- phy_cmd.size, &phy_cmd.dma,
- GFP_KERNEL);
- if (!phy_cmd.va) {
- dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
- return -ENOMEM;
- }
- status = be_cmd_get_phy_info(adapter, &phy_cmd);
+ status = be_cmd_get_phy_info(adapter, &phy_info);
if (!status) {
- resp = (struct be_cmd_resp_get_phy_info *) phy_cmd.va;
- intf_type = le16_to_cpu(resp->interface_type);
-
- switch (intf_type) {
+ switch (phy_info.interface_type) {
case PHY_TYPE_XFP_10GB:
case PHY_TYPE_SFP_1GB:
case PHY_TYPE_SFP_PLUS_10GB:
@@ -400,7 +404,7 @@ static int be_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
break;
}
- switch (intf_type) {
+ switch (phy_info.interface_type) {
case PHY_TYPE_KR_10GB:
case PHY_TYPE_KX4_10GB:
ecmd->autoneg = AUTONEG_ENABLE;
@@ -418,8 +422,6 @@ static int be_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
adapter->port_type = ecmd->port;
adapter->transceiver = ecmd->transceiver;
adapter->autoneg = ecmd->autoneg;
- dma_free_coherent(&adapter->pdev->dev, phy_cmd.size, phy_cmd.va,
- phy_cmd.dma);
} else {
ethtool_cmd_speed_set(ecmd, adapter->link_speed);
ecmd->port = adapter->port_type;
@@ -457,10 +459,10 @@ be_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
struct be_adapter *adapter = netdev_priv(netdev);
ring->rx_max_pending = adapter->rx_obj[0].q.len;
- ring->tx_max_pending = adapter->tx_obj.q.len;
+ ring->tx_max_pending = adapter->tx_obj[0].q.len;
ring->rx_pending = atomic_read(&adapter->rx_obj[0].q.used);
- ring->tx_pending = atomic_read(&adapter->tx_obj.q.used);
+ ring->tx_pending = atomic_read(&adapter->tx_obj[0].q.used);
}
static void
@@ -608,7 +610,6 @@ static void
be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
{
struct be_adapter *adapter = netdev_priv(netdev);
- bool link_up;
u8 mac_speed = 0;
u16 qos_link_speed = 0;
@@ -634,7 +635,7 @@ be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
test->flags |= ETH_TEST_FL_FAILED;
}
- if (be_cmd_link_status_query(adapter, &link_up, &mac_speed,
+ if (be_cmd_link_status_query(adapter, &mac_speed,
&qos_link_speed, 0) != 0) {
test->flags |= ETH_TEST_FL_FAILED;
data[4] = -1;
@@ -690,7 +691,7 @@ be_read_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
status = be_cmd_get_seeprom_data(adapter, &eeprom_cmd);
if (!status) {
- resp = (struct be_cmd_resp_seeprom_read *) eeprom_cmd.va;
+ resp = eeprom_cmd.va;
memcpy(data, resp->seeprom_data + eeprom->offset, eeprom->len);
}
dma_free_coherent(&adapter->pdev->dev, eeprom_cmd.size, eeprom_cmd.va,
diff --git a/drivers/net/benet/be_hw.h b/drivers/net/ethernet/emulex/benet/be_hw.h
index 53d658afea2a..f2c89e3ccabd 100644
--- a/drivers/net/benet/be_hw.h
+++ b/drivers/net/ethernet/emulex/benet/be_hw.h
@@ -48,6 +48,8 @@
/* Lancer SLIPORT_CONTROL SLIPORT_STATUS registers */
#define SLIPORT_STATUS_OFFSET 0x404
#define SLIPORT_CONTROL_OFFSET 0x408
+#define SLIPORT_ERROR1_OFFSET 0x40C
+#define SLIPORT_ERROR2_OFFSET 0x410
#define SLIPORT_STATUS_ERR_MASK 0x80000000
#define SLIPORT_STATUS_RN_MASK 0x01000000
@@ -175,18 +177,24 @@
#define IMG_TYPE_FCOE_FW_ACTIVE 10
#define IMG_TYPE_FCOE_FW_BACKUP 11
#define IMG_TYPE_NCSI_FW 13
+#define IMG_TYPE_PHY_FW 99
+#define TN_8022 13
+#define ILLEGAL_IOCTL_REQ 2
+#define FLASHROM_OPER_PHY_FLASH 9
+#define FLASHROM_OPER_PHY_SAVE 10
#define FLASHROM_OPER_FLASH 1
#define FLASHROM_OPER_SAVE 2
#define FLASHROM_OPER_REPORT 4
-#define FLASH_IMAGE_MAX_SIZE_g2 (1310720) /* Max firmware image sz */
-#define FLASH_BIOS_IMAGE_MAX_SIZE_g2 (262144) /* Max OPTION ROM img sz */
-#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g2 (262144) /* Max Redboot image sz */
-#define FLASH_IMAGE_MAX_SIZE_g3 (2097152) /* Max fw image size */
-#define FLASH_BIOS_IMAGE_MAX_SIZE_g3 (524288) /* Max OPTION ROM img sz */
-#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g3 (1048576) /* Max Redboot image sz */
-#define FLASH_NCSI_IMAGE_MAX_SIZE_g3 (262144) /* Max NSCI image sz */
+#define FLASH_IMAGE_MAX_SIZE_g2 (1310720) /* Max firmware image size */
+#define FLASH_BIOS_IMAGE_MAX_SIZE_g2 (262144) /* Max OPTION ROM image sz */
+#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g2 (262144) /* Max Redboot image sz */
+#define FLASH_IMAGE_MAX_SIZE_g3 (2097152) /* Max firmware image size */
+#define FLASH_BIOS_IMAGE_MAX_SIZE_g3 (524288) /* Max OPTION ROM image sz */
+#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g3 (1048576) /* Max Redboot image sz */
+#define FLASH_NCSI_IMAGE_MAX_SIZE_g3 (262144)
+#define FLASH_PHY_FW_IMAGE_MAX_SIZE_g3 262144
#define FLASH_NCSI_MAGIC (0x16032009)
#define FLASH_NCSI_DISABLED (0)
@@ -213,6 +221,7 @@
#define FLASH_PXE_BIOS_START_g3 (13107200)
#define FLASH_FCoE_BIOS_START_g3 (13631488)
#define FLASH_REDBOOT_START_g3 (262144)
+#define FLASH_PHY_FW_START_g3 1310720
/************* Rx Packet Type Encoding **************/
#define BE_UNICAST_PACKET 0
diff --git a/drivers/net/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index a485f7fdaf37..bf266a00c774 100644
--- a/drivers/net/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -16,6 +16,7 @@
*/
#include <linux/prefetch.h>
+#include <linux/module.h>
#include "be.h"
#include "be_cmds.h"
#include <asm/div64.h>
@@ -33,10 +34,6 @@ module_param(num_vfs, uint, S_IRUGO);
MODULE_PARM_DESC(rx_frag_size, "Size of a fragment that holds rcvd data.");
MODULE_PARM_DESC(num_vfs, "Number of PCI VFs to initialize");
-static bool multi_rxq = true;
-module_param(multi_rxq, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(multi_rxq, "Multi Rx Queue support. Enabled by default");
-
static DEFINE_PCI_DEVICE_TABLE(be_dev_ids) = {
{ PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
{ PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID2) },
@@ -48,7 +45,7 @@ static DEFINE_PCI_DEVICE_TABLE(be_dev_ids) = {
};
MODULE_DEVICE_TABLE(pci, be_dev_ids);
/* UE Status Low CSR */
-static char *ue_status_low_desc[] = {
+static const char * const ue_status_low_desc[] = {
"CEV",
"CTX",
"DBUF",
@@ -83,7 +80,7 @@ static char *ue_status_low_desc[] = {
"MPU_INTPEND"
};
/* UE Status High CSR */
-static char *ue_status_hi_desc[] = {
+static const char * const ue_status_hi_desc[] = {
"LPCMEMHOST",
"MGMT_MAC",
"PCS0ONLINE",
@@ -107,7 +104,7 @@ static char *ue_status_hi_desc[] = {
"HOST7",
"HOST8",
"HOST9",
- "NETC"
+ "NETC",
"Unknown",
"Unknown",
"Unknown",
@@ -118,6 +115,13 @@ static char *ue_status_hi_desc[] = {
"Unknown"
};
+/* Is BE in a multi-channel mode */
+static inline bool be_is_mc(struct be_adapter *adapter) {
+ return (adapter->function_mode & FLEX10_MODE ||
+ adapter->function_mode & VNIC_MODE ||
+ adapter->function_mode & UMC_ENABLED);
+}
+
static void be_queue_free(struct be_adapter *adapter, struct be_queue_info *q)
{
struct be_dma_mem *mem = &q->dma_mem;
@@ -145,13 +149,15 @@ static int be_queue_alloc(struct be_adapter *adapter, struct be_queue_info *q,
static void be_intr_set(struct be_adapter *adapter, bool enable)
{
- u8 __iomem *addr = adapter->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET;
- u32 reg = ioread32(addr);
- u32 enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
+ u32 reg, enabled;
if (adapter->eeh_err)
return;
+ pci_read_config_dword(adapter->pdev, PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET,
+ &reg);
+ enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
+
if (!enabled && enable)
reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
else if (enabled && !enable)
@@ -159,7 +165,8 @@ static void be_intr_set(struct be_adapter *adapter, bool enable)
else
return;
- iowrite32(reg, addr);
+ pci_write_config_dword(adapter->pdev,
+ PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET, reg);
}
static void be_rxq_notify(struct be_adapter *adapter, u16 qid, u16 posted)
@@ -223,40 +230,42 @@ static int be_mac_addr_set(struct net_device *netdev, void *p)
struct be_adapter *adapter = netdev_priv(netdev);
struct sockaddr *addr = p;
int status = 0;
+ u8 current_mac[ETH_ALEN];
+ u32 pmac_id = adapter->pmac_id;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
- /* MAC addr configuration will be done in hardware for VFs
- * by their corresponding PFs. Just copy to netdev addr here
- */
- if (!be_physfn(adapter))
- goto netdev_addr;
-
- status = be_cmd_pmac_del(adapter, adapter->if_handle,
- adapter->pmac_id, 0);
+ status = be_cmd_mac_addr_query(adapter, current_mac,
+ MAC_ADDRESS_TYPE_NETWORK, false, adapter->if_handle);
if (status)
- return status;
+ goto err;
- status = be_cmd_pmac_add(adapter, (u8 *)addr->sa_data,
+ if (memcmp(addr->sa_data, current_mac, ETH_ALEN)) {
+ status = be_cmd_pmac_add(adapter, (u8 *)addr->sa_data,
adapter->if_handle, &adapter->pmac_id, 0);
-netdev_addr:
- if (!status)
- memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+ if (status)
+ goto err;
+ be_cmd_pmac_del(adapter, adapter->if_handle, pmac_id, 0);
+ }
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+ return 0;
+err:
+ dev_err(&adapter->pdev->dev, "MAC %pM set Failed\n", addr->sa_data);
return status;
}
static void populate_be2_stats(struct be_adapter *adapter)
{
-
- struct be_drv_stats *drvs = &adapter->drv_stats;
- struct be_pmem_stats *pmem_sts = be_pmem_stats_from_cmd(adapter);
+ struct be_hw_stats_v0 *hw_stats = hw_stats_from_cmd(adapter);
+ struct be_pmem_stats *pmem_sts = &hw_stats->pmem;
+ struct be_rxf_stats_v0 *rxf_stats = &hw_stats->rxf;
struct be_port_rxf_stats_v0 *port_stats =
- be_port_rxf_stats_from_cmd(adapter);
- struct be_rxf_stats_v0 *rxf_stats =
- be_rxf_stats_from_cmd(adapter);
+ &rxf_stats->port[adapter->port_num];
+ struct be_drv_stats *drvs = &adapter->drv_stats;
+ be_dws_le_to_cpu(hw_stats, sizeof(*hw_stats));
drvs->rx_pause_frames = port_stats->rx_pause_frames;
drvs->rx_crc_errors = port_stats->rx_crc_errors;
drvs->rx_control_frames = port_stats->rx_control_frames;
@@ -271,12 +280,10 @@ static void populate_be2_stats(struct be_adapter *adapter)
drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small;
drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short;
drvs->rx_out_range_errors = port_stats->rx_out_range_errors;
- drvs->rx_input_fifo_overflow_drop =
- port_stats->rx_input_fifo_overflow;
+ drvs->rx_input_fifo_overflow_drop = port_stats->rx_input_fifo_overflow;
drvs->rx_dropped_header_too_small =
port_stats->rx_dropped_header_too_small;
- drvs->rx_address_match_errors =
- port_stats->rx_address_match_errors;
+ drvs->rx_address_match_errors = port_stats->rx_address_match_errors;
drvs->rx_alignment_symbol_errors =
port_stats->rx_alignment_symbol_errors;
@@ -284,36 +291,30 @@ static void populate_be2_stats(struct be_adapter *adapter)
drvs->tx_controlframes = port_stats->tx_controlframes;
if (adapter->port_num)
- drvs->jabber_events =
- rxf_stats->port1_jabber_events;
+ drvs->jabber_events = rxf_stats->port1_jabber_events;
else
- drvs->jabber_events =
- rxf_stats->port0_jabber_events;
+ drvs->jabber_events = rxf_stats->port0_jabber_events;
drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf;
drvs->rx_drops_no_txpb = rxf_stats->rx_drops_no_txpb;
drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr;
drvs->rx_drops_invalid_ring = rxf_stats->rx_drops_invalid_ring;
drvs->forwarded_packets = rxf_stats->forwarded_packets;
drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu;
- drvs->rx_drops_no_tpre_descr =
- rxf_stats->rx_drops_no_tpre_descr;
- drvs->rx_drops_too_many_frags =
- rxf_stats->rx_drops_too_many_frags;
+ drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr;
+ drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags;
adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops;
}
static void populate_be3_stats(struct be_adapter *adapter)
{
- struct be_drv_stats *drvs = &adapter->drv_stats;
- struct be_pmem_stats *pmem_sts = be_pmem_stats_from_cmd(adapter);
-
- struct be_rxf_stats_v1 *rxf_stats =
- be_rxf_stats_from_cmd(adapter);
+ struct be_hw_stats_v1 *hw_stats = hw_stats_from_cmd(adapter);
+ struct be_pmem_stats *pmem_sts = &hw_stats->pmem;
+ struct be_rxf_stats_v1 *rxf_stats = &hw_stats->rxf;
struct be_port_rxf_stats_v1 *port_stats =
- be_port_rxf_stats_from_cmd(adapter);
+ &rxf_stats->port[adapter->port_num];
+ struct be_drv_stats *drvs = &adapter->drv_stats;
- drvs->rx_priority_pause_frames = 0;
- drvs->pmem_fifo_overflow_drop = 0;
+ be_dws_le_to_cpu(hw_stats, sizeof(*hw_stats));
drvs->rx_pause_frames = port_stats->rx_pause_frames;
drvs->rx_crc_errors = port_stats->rx_crc_errors;
drvs->rx_control_frames = port_stats->rx_control_frames;
@@ -331,12 +332,10 @@ static void populate_be3_stats(struct be_adapter *adapter)
port_stats->rx_dropped_header_too_small;
drvs->rx_input_fifo_overflow_drop =
port_stats->rx_input_fifo_overflow_drop;
- drvs->rx_address_match_errors =
- port_stats->rx_address_match_errors;
+ drvs->rx_address_match_errors = port_stats->rx_address_match_errors;
drvs->rx_alignment_symbol_errors =
port_stats->rx_alignment_symbol_errors;
- drvs->rxpp_fifo_overflow_drop =
- port_stats->rxpp_fifo_overflow_drop;
+ drvs->rxpp_fifo_overflow_drop = port_stats->rxpp_fifo_overflow_drop;
drvs->tx_pauseframes = port_stats->tx_pauseframes;
drvs->tx_controlframes = port_stats->tx_controlframes;
drvs->jabber_events = port_stats->jabber_events;
@@ -346,10 +345,8 @@ static void populate_be3_stats(struct be_adapter *adapter)
drvs->rx_drops_invalid_ring = rxf_stats->rx_drops_invalid_ring;
drvs->forwarded_packets = rxf_stats->forwarded_packets;
drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu;
- drvs->rx_drops_no_tpre_descr =
- rxf_stats->rx_drops_no_tpre_descr;
- drvs->rx_drops_too_many_frags =
- rxf_stats->rx_drops_too_many_frags;
+ drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr;
+ drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags;
adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops;
}
@@ -357,22 +354,15 @@ static void populate_lancer_stats(struct be_adapter *adapter)
{
struct be_drv_stats *drvs = &adapter->drv_stats;
- struct lancer_cmd_pport_stats *pport_stats = pport_stats_from_cmd
- (adapter);
- drvs->rx_priority_pause_frames = 0;
- drvs->pmem_fifo_overflow_drop = 0;
- drvs->rx_pause_frames =
- make_64bit_val(pport_stats->rx_pause_frames_lo,
- pport_stats->rx_pause_frames_hi);
- drvs->rx_crc_errors = make_64bit_val(pport_stats->rx_crc_errors_hi,
- pport_stats->rx_crc_errors_lo);
- drvs->rx_control_frames =
- make_64bit_val(pport_stats->rx_control_frames_hi,
- pport_stats->rx_control_frames_lo);
+ struct lancer_pport_stats *pport_stats =
+ pport_stats_from_cmd(adapter);
+
+ be_dws_le_to_cpu(pport_stats, sizeof(*pport_stats));
+ drvs->rx_pause_frames = pport_stats->rx_pause_frames_lo;
+ drvs->rx_crc_errors = pport_stats->rx_crc_errors_lo;
+ drvs->rx_control_frames = pport_stats->rx_control_frames_lo;
drvs->rx_in_range_errors = pport_stats->rx_in_range_errors;
- drvs->rx_frame_too_long =
- make_64bit_val(pport_stats->rx_internal_mac_errors_hi,
- pport_stats->rx_frames_too_long_lo);
+ drvs->rx_frame_too_long = pport_stats->rx_frames_too_long_lo;
drvs->rx_dropped_runt = pport_stats->rx_dropped_runt;
drvs->rx_ip_checksum_errs = pport_stats->rx_ip_checksum_errors;
drvs->rx_tcp_checksum_errs = pport_stats->rx_tcp_checksum_errors;
@@ -386,32 +376,36 @@ static void populate_lancer_stats(struct be_adapter *adapter)
pport_stats->rx_dropped_header_too_small;
drvs->rx_input_fifo_overflow_drop = pport_stats->rx_fifo_overflow;
drvs->rx_address_match_errors = pport_stats->rx_address_match_errors;
- drvs->rx_alignment_symbol_errors =
- make_64bit_val(pport_stats->rx_symbol_errors_hi,
- pport_stats->rx_symbol_errors_lo);
+ drvs->rx_alignment_symbol_errors = pport_stats->rx_symbol_errors_lo;
drvs->rxpp_fifo_overflow_drop = pport_stats->rx_fifo_overflow;
- drvs->tx_pauseframes = make_64bit_val(pport_stats->tx_pause_frames_hi,
- pport_stats->tx_pause_frames_lo);
- drvs->tx_controlframes =
- make_64bit_val(pport_stats->tx_control_frames_hi,
- pport_stats->tx_control_frames_lo);
+ drvs->tx_pauseframes = pport_stats->tx_pause_frames_lo;
+ drvs->tx_controlframes = pport_stats->tx_control_frames_lo;
drvs->jabber_events = pport_stats->rx_jabbers;
- drvs->rx_drops_no_pbuf = 0;
- drvs->rx_drops_no_txpb = 0;
- drvs->rx_drops_no_erx_descr = 0;
drvs->rx_drops_invalid_ring = pport_stats->rx_drops_invalid_queue;
- drvs->forwarded_packets = make_64bit_val(pport_stats->num_forwards_hi,
- pport_stats->num_forwards_lo);
- drvs->rx_drops_mtu = make_64bit_val(pport_stats->rx_drops_mtu_hi,
- pport_stats->rx_drops_mtu_lo);
- drvs->rx_drops_no_tpre_descr = 0;
+ drvs->forwarded_packets = pport_stats->num_forwards_lo;
+ drvs->rx_drops_mtu = pport_stats->rx_drops_mtu_lo;
drvs->rx_drops_too_many_frags =
- make_64bit_val(pport_stats->rx_drops_too_many_frags_hi,
- pport_stats->rx_drops_too_many_frags_lo);
+ pport_stats->rx_drops_too_many_frags_lo;
+}
+
+static void accumulate_16bit_val(u32 *acc, u16 val)
+{
+#define lo(x) (x & 0xFFFF)
+#define hi(x) (x & 0xFFFF0000)
+ bool wrapped = val < lo(*acc);
+ u32 newacc = hi(*acc) + val;
+
+ if (wrapped)
+ newacc += 65536;
+ ACCESS_ONCE(*acc) = newacc;
}
void be_parse_stats(struct be_adapter *adapter)
{
+ struct be_erx_stats_v1 *erx = be_erx_stats_from_cmd(adapter);
+ struct be_rx_obj *rxo;
+ int i;
+
if (adapter->generation == BE_GEN3) {
if (lancer_chip(adapter))
populate_lancer_stats(adapter);
@@ -420,41 +414,55 @@ void be_parse_stats(struct be_adapter *adapter)
} else {
populate_be2_stats(adapter);
}
+
+ /* as erx_v1 is longer than v0, ok to use v1 defn for v0 access */
+ for_all_rx_queues(adapter, rxo, i) {
+ /* below erx HW counter can actually wrap around after
+ * 65535. Driver accumulates a 32-bit value
+ */
+ accumulate_16bit_val(&rx_stats(rxo)->rx_drops_no_frags,
+ (u16)erx->rx_drops_no_fragments[rxo->q.id]);
+ }
}
-void netdev_stats_update(struct be_adapter *adapter)
+static struct rtnl_link_stats64 *be_get_stats64(struct net_device *netdev,
+ struct rtnl_link_stats64 *stats)
{
+ struct be_adapter *adapter = netdev_priv(netdev);
struct be_drv_stats *drvs = &adapter->drv_stats;
- struct net_device_stats *dev_stats = &adapter->netdev->stats;
struct be_rx_obj *rxo;
+ struct be_tx_obj *txo;
+ u64 pkts, bytes;
+ unsigned int start;
int i;
- memset(dev_stats, 0, sizeof(*dev_stats));
for_all_rx_queues(adapter, rxo, i) {
- dev_stats->rx_packets += rx_stats(rxo)->rx_pkts;
- dev_stats->rx_bytes += rx_stats(rxo)->rx_bytes;
- dev_stats->multicast += rx_stats(rxo)->rx_mcast_pkts;
- /* no space in linux buffers: best possible approximation */
- if (adapter->generation == BE_GEN3) {
- if (!(lancer_chip(adapter))) {
- struct be_erx_stats_v1 *erx_stats =
- be_erx_stats_from_cmd(adapter);
- dev_stats->rx_dropped +=
- erx_stats->rx_drops_no_fragments[rxo->q.id];
- }
- } else {
- struct be_erx_stats_v0 *erx_stats =
- be_erx_stats_from_cmd(adapter);
- dev_stats->rx_dropped +=
- erx_stats->rx_drops_no_fragments[rxo->q.id];
- }
+ const struct be_rx_stats *rx_stats = rx_stats(rxo);
+ do {
+ start = u64_stats_fetch_begin_bh(&rx_stats->sync);
+ pkts = rx_stats(rxo)->rx_pkts;
+ bytes = rx_stats(rxo)->rx_bytes;
+ } while (u64_stats_fetch_retry_bh(&rx_stats->sync, start));
+ stats->rx_packets += pkts;
+ stats->rx_bytes += bytes;
+ stats->multicast += rx_stats(rxo)->rx_mcast_pkts;
+ stats->rx_dropped += rx_stats(rxo)->rx_drops_no_skbs +
+ rx_stats(rxo)->rx_drops_no_frags;
+ }
+
+ for_all_tx_queues(adapter, txo, i) {
+ const struct be_tx_stats *tx_stats = tx_stats(txo);
+ do {
+ start = u64_stats_fetch_begin_bh(&tx_stats->sync);
+ pkts = tx_stats(txo)->tx_pkts;
+ bytes = tx_stats(txo)->tx_bytes;
+ } while (u64_stats_fetch_retry_bh(&tx_stats->sync, start));
+ stats->tx_packets += pkts;
+ stats->tx_bytes += bytes;
}
- dev_stats->tx_packets = tx_stats(adapter)->be_tx_pkts;
- dev_stats->tx_bytes = tx_stats(adapter)->be_tx_bytes;
-
/* bad pkts received */
- dev_stats->rx_errors = drvs->rx_crc_errors +
+ stats->rx_errors = drvs->rx_crc_errors +
drvs->rx_alignment_symbol_errors +
drvs->rx_in_range_errors +
drvs->rx_out_range_errors +
@@ -463,128 +471,54 @@ void netdev_stats_update(struct be_adapter *adapter)
drvs->rx_dropped_too_short +
drvs->rx_dropped_header_too_small +
drvs->rx_dropped_tcp_length +
- drvs->rx_dropped_runt +
- drvs->rx_tcp_checksum_errs +
- drvs->rx_ip_checksum_errs +
- drvs->rx_udp_checksum_errs;
+ drvs->rx_dropped_runt;
/* detailed rx errors */
- dev_stats->rx_length_errors = drvs->rx_in_range_errors +
+ stats->rx_length_errors = drvs->rx_in_range_errors +
drvs->rx_out_range_errors +
drvs->rx_frame_too_long;
- dev_stats->rx_crc_errors = drvs->rx_crc_errors;
+ stats->rx_crc_errors = drvs->rx_crc_errors;
/* frame alignment errors */
- dev_stats->rx_frame_errors = drvs->rx_alignment_symbol_errors;
+ stats->rx_frame_errors = drvs->rx_alignment_symbol_errors;
/* receiver fifo overrun */
/* drops_no_pbuf is no per i/f, it's per BE card */
- dev_stats->rx_fifo_errors = drvs->rxpp_fifo_overflow_drop +
+ stats->rx_fifo_errors = drvs->rxpp_fifo_overflow_drop +
drvs->rx_input_fifo_overflow_drop +
drvs->rx_drops_no_pbuf;
+ return stats;
}
-void be_link_status_update(struct be_adapter *adapter, bool link_up)
+void be_link_status_update(struct be_adapter *adapter, u32 link_status)
{
struct net_device *netdev = adapter->netdev;
- /* If link came up or went down */
- if (adapter->link_up != link_up) {
- adapter->link_speed = -1;
- if (link_up) {
- netif_carrier_on(netdev);
- printk(KERN_INFO "%s: Link up\n", netdev->name);
- } else {
- netif_carrier_off(netdev);
- printk(KERN_INFO "%s: Link down\n", netdev->name);
- }
- adapter->link_up = link_up;
- }
-}
-
-/* Update the EQ delay n BE based on the RX frags consumed / sec */
-static void be_rx_eqd_update(struct be_adapter *adapter, struct be_rx_obj *rxo)
-{
- struct be_eq_obj *rx_eq = &rxo->rx_eq;
- struct be_rx_stats *stats = &rxo->stats;
- ulong now = jiffies;
- u32 eqd;
-
- if (!rx_eq->enable_aic)
- return;
-
- /* Wrapped around */
- if (time_before(now, stats->rx_fps_jiffies)) {
- stats->rx_fps_jiffies = now;
- return;
- }
-
- /* Update once a second */
- if ((now - stats->rx_fps_jiffies) < HZ)
- return;
-
- stats->rx_fps = (stats->rx_frags - stats->prev_rx_frags) /
- ((now - stats->rx_fps_jiffies) / HZ);
-
- stats->rx_fps_jiffies = now;
- stats->prev_rx_frags = stats->rx_frags;
- eqd = stats->rx_fps / 110000;
- eqd = eqd << 3;
- if (eqd > rx_eq->max_eqd)
- eqd = rx_eq->max_eqd;
- if (eqd < rx_eq->min_eqd)
- eqd = rx_eq->min_eqd;
- if (eqd < 10)
- eqd = 0;
- if (eqd != rx_eq->cur_eqd)
- be_cmd_modify_eqd(adapter, rx_eq->q.id, eqd);
-
- rx_eq->cur_eqd = eqd;
-}
-
-static u32 be_calc_rate(u64 bytes, unsigned long ticks)
-{
- u64 rate = bytes;
-
- do_div(rate, ticks / HZ);
- rate <<= 3; /* bytes/sec -> bits/sec */
- do_div(rate, 1000000ul); /* MB/Sec */
-
- return rate;
-}
-
-static void be_tx_rate_update(struct be_adapter *adapter)
-{
- struct be_tx_stats *stats = tx_stats(adapter);
- ulong now = jiffies;
-
- /* Wrapped around? */
- if (time_before(now, stats->be_tx_jiffies)) {
- stats->be_tx_jiffies = now;
- return;
- }
-
- /* Update tx rate once in two seconds */
- if ((now - stats->be_tx_jiffies) > 2 * HZ) {
- stats->be_tx_rate = be_calc_rate(stats->be_tx_bytes
- - stats->be_tx_bytes_prev,
- now - stats->be_tx_jiffies);
- stats->be_tx_jiffies = now;
- stats->be_tx_bytes_prev = stats->be_tx_bytes;
+ /* when link status changes, link speed must be re-queried from card */
+ adapter->link_speed = -1;
+ if ((link_status & LINK_STATUS_MASK) == LINK_UP) {
+ netif_carrier_on(netdev);
+ dev_info(&adapter->pdev->dev, "%s: Link up\n", netdev->name);
+ } else {
+ netif_carrier_off(netdev);
+ dev_info(&adapter->pdev->dev, "%s: Link down\n", netdev->name);
}
}
-static void be_tx_stats_update(struct be_adapter *adapter,
+static void be_tx_stats_update(struct be_tx_obj *txo,
u32 wrb_cnt, u32 copied, u32 gso_segs, bool stopped)
{
- struct be_tx_stats *stats = tx_stats(adapter);
- stats->be_tx_reqs++;
- stats->be_tx_wrbs += wrb_cnt;
- stats->be_tx_bytes += copied;
- stats->be_tx_pkts += (gso_segs ? gso_segs : 1);
+ struct be_tx_stats *stats = tx_stats(txo);
+
+ u64_stats_update_begin(&stats->sync);
+ stats->tx_reqs++;
+ stats->tx_wrbs += wrb_cnt;
+ stats->tx_bytes += copied;
+ stats->tx_pkts += (gso_segs ? gso_segs : 1);
if (stopped)
- stats->be_tx_stops++;
+ stats->tx_stops++;
+ u64_stats_update_end(&stats->sync);
}
/* Determine number of WRB entries needed to xmit data in an skb */
@@ -648,7 +582,7 @@ static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr,
AMAP_SET_BITS(struct amap_eth_hdr_wrb, udpcs, hdr, 1);
}
- if (adapter->vlan_grp && vlan_tx_tag_present(skb)) {
+ if (vlan_tx_tag_present(skb)) {
AMAP_SET_BITS(struct amap_eth_hdr_wrb, vlan, hdr, 1);
vlan_tag = vlan_tx_tag_get(skb);
vlan_prio = (vlan_tag & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
@@ -682,14 +616,13 @@ static void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb,
}
}
-static int make_tx_wrbs(struct be_adapter *adapter,
+static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq,
struct sk_buff *skb, u32 wrb_cnt, bool dummy_wrb)
{
dma_addr_t busaddr;
int i, copied = 0;
struct device *dev = &adapter->pdev->dev;
struct sk_buff *first_skb = skb;
- struct be_queue_info *txq = &adapter->tx_obj.q;
struct be_eth_wrb *wrb;
struct be_eth_hdr_wrb *hdr;
bool map_single = false;
@@ -713,17 +646,17 @@ static int make_tx_wrbs(struct be_adapter *adapter,
}
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- struct skb_frag_struct *frag =
+ const struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[i];
- busaddr = dma_map_page(dev, frag->page, frag->page_offset,
- frag->size, DMA_TO_DEVICE);
+ busaddr = skb_frag_dma_map(dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
if (dma_mapping_error(dev, busaddr))
goto dma_err;
wrb = queue_head_node(txq);
- wrb_fill(wrb, busaddr, frag->size);
+ wrb_fill(wrb, busaddr, skb_frag_size(frag));
be_dws_cpu_to_le(wrb, sizeof(*wrb));
queue_head_inc(txq);
- copied += frag->size;
+ copied += skb_frag_size(frag);
}
if (dummy_wrb) {
@@ -753,19 +686,19 @@ static netdev_tx_t be_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct be_adapter *adapter = netdev_priv(netdev);
- struct be_tx_obj *tx_obj = &adapter->tx_obj;
- struct be_queue_info *txq = &tx_obj->q;
+ struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)];
+ struct be_queue_info *txq = &txo->q;
u32 wrb_cnt = 0, copied = 0;
u32 start = txq->head;
bool dummy_wrb, stopped = false;
wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb);
- copied = make_tx_wrbs(adapter, skb, wrb_cnt, dummy_wrb);
+ copied = make_tx_wrbs(adapter, txq, skb, wrb_cnt, dummy_wrb);
if (copied) {
/* record the sent skb in the sent_skb table */
- BUG_ON(tx_obj->sent_skb_list[start]);
- tx_obj->sent_skb_list[start] = skb;
+ BUG_ON(txo->sent_skb_list[start]);
+ txo->sent_skb_list[start] = skb;
/* Ensure txq has space for the next skb; Else stop the queue
* *BEFORE* ringing the tx doorbell, so that we serialze the
@@ -774,13 +707,13 @@ static netdev_tx_t be_xmit(struct sk_buff *skb,
atomic_add(wrb_cnt, &txq->used);
if ((BE_MAX_TX_FRAG_COUNT + atomic_read(&txq->used)) >=
txq->len) {
- netif_stop_queue(netdev);
+ netif_stop_subqueue(netdev, skb_get_queue_mapping(skb));
stopped = true;
}
be_txq_notify(adapter, txq->id, wrb_cnt);
- be_tx_stats_update(adapter, wrb_cnt, copied,
+ be_tx_stats_update(txo, wrb_cnt, copied,
skb_shinfo(skb)->gso_segs, stopped);
} else {
txq->head = start;
@@ -824,6 +757,10 @@ static int be_vid_config(struct be_adapter *adapter, bool vf, u32 vf_num)
status = be_cmd_vlan_config(adapter, if_handle, vtag, 1, 1, 0);
}
+ /* No need to further configure vids if in promiscuous mode */
+ if (adapter->promiscuous)
+ return 0;
+
if (adapter->vlans_added <= adapter->max_vlans) {
/* Construct VLAN Table to give to HW */
for (i = 0; i < VLAN_N_VID; i++) {
@@ -842,13 +779,6 @@ static int be_vid_config(struct be_adapter *adapter, bool vf, u32 vf_num)
return status;
}
-static void be_vlan_register(struct net_device *netdev, struct vlan_group *grp)
-{
- struct be_adapter *adapter = netdev_priv(netdev);
-
- adapter->vlan_grp = grp;
-}
-
static void be_vlan_add_vid(struct net_device *netdev, u16 vid)
{
struct be_adapter *adapter = netdev_priv(netdev);
@@ -867,7 +797,6 @@ static void be_vlan_rem_vid(struct net_device *netdev, u16 vid)
struct be_adapter *adapter = netdev_priv(netdev);
adapter->vlans_added--;
- vlan_group_set_device(adapter->vlan_grp, vid, NULL);
if (!be_physfn(adapter))
return;
@@ -877,12 +806,12 @@ static void be_vlan_rem_vid(struct net_device *netdev, u16 vid)
be_vid_config(adapter, false, 0);
}
-static void be_set_multicast_list(struct net_device *netdev)
+static void be_set_rx_mode(struct net_device *netdev)
{
struct be_adapter *adapter = netdev_priv(netdev);
if (netdev->flags & IFF_PROMISC) {
- be_cmd_promiscuous_config(adapter, true);
+ be_cmd_rx_filter(adapter, IFF_PROMISC, ON);
adapter->promiscuous = true;
goto done;
}
@@ -890,19 +819,20 @@ static void be_set_multicast_list(struct net_device *netdev)
/* BE was previously in promiscuous mode; disable it */
if (adapter->promiscuous) {
adapter->promiscuous = false;
- be_cmd_promiscuous_config(adapter, false);
+ be_cmd_rx_filter(adapter, IFF_PROMISC, OFF);
+
+ if (adapter->vlans_added)
+ be_vid_config(adapter, false, 0);
}
/* Enable multicast promisc if num configured exceeds what we support */
if (netdev->flags & IFF_ALLMULTI ||
- netdev_mc_count(netdev) > BE_MAX_MC) {
- be_cmd_multicast_set(adapter, adapter->if_handle, NULL,
- &adapter->mc_cmd_mem);
+ netdev_mc_count(netdev) > BE_MAX_MC) {
+ be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON);
goto done;
}
- be_cmd_multicast_set(adapter, adapter->if_handle, netdev,
- &adapter->mc_cmd_mem);
+ be_cmd_rx_filter(adapter, IFF_MULTICAST, ON);
done:
return;
}
@@ -1008,10 +938,17 @@ static int be_set_vf_tx_rate(struct net_device *netdev,
return status;
}
-static void be_rx_rate_update(struct be_rx_obj *rxo)
+static void be_rx_eqd_update(struct be_adapter *adapter, struct be_rx_obj *rxo)
{
- struct be_rx_stats *stats = &rxo->stats;
+ struct be_eq_obj *rx_eq = &rxo->rx_eq;
+ struct be_rx_stats *stats = rx_stats(rxo);
ulong now = jiffies;
+ ulong delta = now - stats->rx_jiffies;
+ u64 pkts;
+ unsigned int start, eqd;
+
+ if (!rx_eq->enable_aic)
+ return;
/* Wrapped around */
if (time_before(now, stats->rx_jiffies)) {
@@ -1019,29 +956,46 @@ static void be_rx_rate_update(struct be_rx_obj *rxo)
return;
}
- /* Update the rate once in two seconds */
- if ((now - stats->rx_jiffies) < 2 * HZ)
+ /* Update once a second */
+ if (delta < HZ)
return;
- stats->rx_rate = be_calc_rate(stats->rx_bytes - stats->rx_bytes_prev,
- now - stats->rx_jiffies);
+ do {
+ start = u64_stats_fetch_begin_bh(&stats->sync);
+ pkts = stats->rx_pkts;
+ } while (u64_stats_fetch_retry_bh(&stats->sync, start));
+
+ stats->rx_pps = (unsigned long)(pkts - stats->rx_pkts_prev) / (delta / HZ);
+ stats->rx_pkts_prev = pkts;
stats->rx_jiffies = now;
- stats->rx_bytes_prev = stats->rx_bytes;
+ eqd = stats->rx_pps / 110000;
+ eqd = eqd << 3;
+ if (eqd > rx_eq->max_eqd)
+ eqd = rx_eq->max_eqd;
+ if (eqd < rx_eq->min_eqd)
+ eqd = rx_eq->min_eqd;
+ if (eqd < 10)
+ eqd = 0;
+ if (eqd != rx_eq->cur_eqd) {
+ be_cmd_modify_eqd(adapter, rx_eq->q.id, eqd);
+ rx_eq->cur_eqd = eqd;
+ }
}
static void be_rx_stats_update(struct be_rx_obj *rxo,
struct be_rx_compl_info *rxcp)
{
- struct be_rx_stats *stats = &rxo->stats;
+ struct be_rx_stats *stats = rx_stats(rxo);
+ u64_stats_update_begin(&stats->sync);
stats->rx_compl++;
- stats->rx_frags += rxcp->num_rcvd;
stats->rx_bytes += rxcp->pkt_size;
stats->rx_pkts++;
if (rxcp->pkt_type == BE_MULTICAST_PACKET)
stats->rx_mcast_pkts++;
if (rxcp->err)
- stats->rxcp_err++;
+ stats->rx_compl_err++;
+ u64_stats_update_end(&stats->sync);
}
static inline bool csum_passed(struct be_rx_compl_info *rxcp)
@@ -1122,11 +1076,12 @@ static void skb_fill_rx_data(struct be_adapter *adapter, struct be_rx_obj *rxo,
skb->tail += curr_frag_len;
} else {
skb_shinfo(skb)->nr_frags = 1;
- skb_shinfo(skb)->frags[0].page = page_info->page;
+ skb_frag_set_page(skb, 0, page_info->page);
skb_shinfo(skb)->frags[0].page_offset =
page_info->page_offset + hdr_len;
- skb_shinfo(skb)->frags[0].size = curr_frag_len - hdr_len;
+ skb_frag_size_set(&skb_shinfo(skb)->frags[0], curr_frag_len - hdr_len);
skb->data_len = curr_frag_len - hdr_len;
+ skb->truesize += rx_frag_size;
skb->tail += hdr_len;
}
page_info->page = NULL;
@@ -1147,19 +1102,19 @@ static void skb_fill_rx_data(struct be_adapter *adapter, struct be_rx_obj *rxo,
if (page_info->page_offset == 0) {
/* Fresh page */
j++;
- skb_shinfo(skb)->frags[j].page = page_info->page;
+ skb_frag_set_page(skb, j, page_info->page);
skb_shinfo(skb)->frags[j].page_offset =
page_info->page_offset;
- skb_shinfo(skb)->frags[j].size = 0;
+ skb_frag_size_set(&skb_shinfo(skb)->frags[j], 0);
skb_shinfo(skb)->nr_frags++;
} else {
put_page(page_info->page);
}
- skb_shinfo(skb)->frags[j].size += curr_frag_len;
+ skb_frag_size_add(&skb_shinfo(skb)->frags[j], curr_frag_len);
skb->len += curr_frag_len;
skb->data_len += curr_frag_len;
-
+ skb->truesize += rx_frag_size;
remaining -= curr_frag_len;
index_inc(&rxcp->rxq_idx, rxq->len);
page_info->page = NULL;
@@ -1177,8 +1132,7 @@ static void be_rx_compl_process(struct be_adapter *adapter,
skb = netdev_alloc_skb_ip_align(netdev, BE_HDR_LEN);
if (unlikely(!skb)) {
- if (net_ratelimit())
- dev_warn(&adapter->pdev->dev, "skb alloc failed\n");
+ rx_stats(rxo)->rx_drops_no_skbs++;
be_rx_compl_discard(adapter, rxo, rxcp);
return;
}
@@ -1190,22 +1144,15 @@ static void be_rx_compl_process(struct be_adapter *adapter,
else
skb_checksum_none_assert(skb);
- skb->truesize = skb->len + sizeof(struct sk_buff);
skb->protocol = eth_type_trans(skb, netdev);
if (adapter->netdev->features & NETIF_F_RXHASH)
skb->rxhash = rxcp->rss_hash;
- if (unlikely(rxcp->vlanf)) {
- if (!adapter->vlan_grp || adapter->vlans_added == 0) {
- kfree_skb(skb);
- return;
- }
- vlan_hwaccel_receive_skb(skb, adapter->vlan_grp,
- rxcp->vlan_tag);
- } else {
- netif_receive_skb(skb);
- }
+ if (rxcp->vlanf)
+ __vlan_hwaccel_put_tag(skb, rxcp->vlan_tag);
+
+ netif_receive_skb(skb);
}
/* Process the RX completion indicated by rxcp when GRO is enabled */
@@ -1236,15 +1183,15 @@ static void be_rx_compl_process_gro(struct be_adapter *adapter,
if (i == 0 || page_info->page_offset == 0) {
/* First frag or Fresh page */
j++;
- skb_shinfo(skb)->frags[j].page = page_info->page;
+ skb_frag_set_page(skb, j, page_info->page);
skb_shinfo(skb)->frags[j].page_offset =
page_info->page_offset;
- skb_shinfo(skb)->frags[j].size = 0;
+ skb_frag_size_set(&skb_shinfo(skb)->frags[j], 0);
} else {
put_page(page_info->page);
}
- skb_shinfo(skb)->frags[j].size += curr_frag_len;
-
+ skb_frag_size_add(&skb_shinfo(skb)->frags[j], curr_frag_len);
+ skb->truesize += rx_frag_size;
remaining -= curr_frag_len;
index_inc(&rxcp->rxq_idx, rxq->len);
memset(page_info, 0, sizeof(*page_info));
@@ -1254,16 +1201,14 @@ static void be_rx_compl_process_gro(struct be_adapter *adapter,
skb_shinfo(skb)->nr_frags = j + 1;
skb->len = rxcp->pkt_size;
skb->data_len = rxcp->pkt_size;
- skb->truesize += rxcp->pkt_size;
skb->ip_summed = CHECKSUM_UNNECESSARY;
if (adapter->netdev->features & NETIF_F_RXHASH)
skb->rxhash = rxcp->rss_hash;
- if (likely(!rxcp->vlanf))
- napi_gro_frags(&eq_obj->napi);
- else
- vlan_gro_frags(&eq_obj->napi, adapter->vlan_grp,
- rxcp->vlan_tag);
+ if (rxcp->vlanf)
+ __vlan_hwaccel_put_tag(skb, rxcp->vlan_tag);
+
+ napi_gro_frags(&eq_obj->napi);
}
static void be_parse_rx_compl_v1(struct be_adapter *adapter,
@@ -1296,6 +1241,7 @@ static void be_parse_rx_compl_v1(struct be_adapter *adapter,
rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, vlan_tag,
compl);
}
+ rxcp->port = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, port, compl);
}
static void be_parse_rx_compl_v0(struct be_adapter *adapter,
@@ -1328,6 +1274,7 @@ static void be_parse_rx_compl_v0(struct be_adapter *adapter,
rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, vlan_tag,
compl);
}
+ rxcp->port = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, port, compl);
}
static struct be_rx_compl_info *be_rx_compl_get(struct be_rx_obj *rxo)
@@ -1352,14 +1299,13 @@ static struct be_rx_compl_info *be_rx_compl_get(struct be_rx_obj *rxo)
if (rxcp->vlanf) {
/* vlanf could be wrongly set in some cards.
* ignore if vtm is not set */
- if ((adapter->function_mode & 0x400) && !rxcp->vtm)
+ if ((adapter->function_mode & FLEX10_MODE) && !rxcp->vtm)
rxcp->vlanf = 0;
if (!lancer_chip(adapter))
rxcp->vlan_tag = swab16(rxcp->vlan_tag);
- if (((adapter->pvid & VLAN_VID_MASK) ==
- (rxcp->vlan_tag & VLAN_VID_MASK)) &&
+ if (adapter->pvid == (rxcp->vlan_tag & VLAN_VID_MASK) &&
!adapter->vlan_tag[rxcp->vlan_tag])
rxcp->vlanf = 0;
}
@@ -1400,7 +1346,7 @@ static void be_post_rx_frags(struct be_rx_obj *rxo, gfp_t gfp)
if (!pagep) {
pagep = be_alloc_pages(adapter->big_page_size, gfp);
if (unlikely(!pagep)) {
- rxo->stats.rx_post_fail++;
+ rx_stats(rxo)->rx_post_fail++;
break;
}
page_dmaaddr = dma_map_page(&adapter->pdev->dev, pagep,
@@ -1459,11 +1405,12 @@ static struct be_eth_tx_compl *be_tx_compl_get(struct be_queue_info *tx_cq)
return txcp;
}
-static u16 be_tx_compl_process(struct be_adapter *adapter, u16 last_index)
+static u16 be_tx_compl_process(struct be_adapter *adapter,
+ struct be_tx_obj *txo, u16 last_index)
{
- struct be_queue_info *txq = &adapter->tx_obj.q;
+ struct be_queue_info *txq = &txo->q;
struct be_eth_wrb *wrb;
- struct sk_buff **sent_skbs = adapter->tx_obj.sent_skb_list;
+ struct sk_buff **sent_skbs = txo->sent_skb_list;
struct sk_buff *sent_skb;
u16 cur_index, num_wrbs = 1; /* account for hdr wrb */
bool unmap_skb_hdr = true;
@@ -1504,7 +1451,8 @@ static inline struct be_eq_entry *event_get(struct be_eq_obj *eq_obj)
}
static int event_handle(struct be_adapter *adapter,
- struct be_eq_obj *eq_obj)
+ struct be_eq_obj *eq_obj,
+ bool rearm)
{
struct be_eq_entry *eqe;
u16 num = 0;
@@ -1517,7 +1465,10 @@ static int event_handle(struct be_adapter *adapter,
/* Deal with any spurious interrupts that come
* without events
*/
- be_eq_notify(adapter, eq_obj->q.id, true, true, num);
+ if (!num)
+ rearm = true;
+
+ be_eq_notify(adapter, eq_obj->q.id, rearm, true, num);
if (num)
napi_schedule(&eq_obj->napi);
@@ -1563,15 +1514,17 @@ static void be_rx_q_clean(struct be_adapter *adapter, struct be_rx_obj *rxo)
memset(page_info, 0, sizeof(*page_info));
}
BUG_ON(atomic_read(&rxq->used));
+ rxq->tail = rxq->head = 0;
}
-static void be_tx_compl_clean(struct be_adapter *adapter)
+static void be_tx_compl_clean(struct be_adapter *adapter,
+ struct be_tx_obj *txo)
{
- struct be_queue_info *tx_cq = &adapter->tx_obj.cq;
- struct be_queue_info *txq = &adapter->tx_obj.q;
+ struct be_queue_info *tx_cq = &txo->cq;
+ struct be_queue_info *txq = &txo->q;
struct be_eth_tx_compl *txcp;
u16 end_idx, cmpl = 0, timeo = 0, num_wrbs = 0;
- struct sk_buff **sent_skbs = adapter->tx_obj.sent_skb_list;
+ struct sk_buff **sent_skbs = txo->sent_skb_list;
struct sk_buff *sent_skb;
bool dummy_wrb;
@@ -1580,7 +1533,7 @@ static void be_tx_compl_clean(struct be_adapter *adapter)
while ((txcp = be_tx_compl_get(tx_cq))) {
end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl,
wrb_index, txcp);
- num_wrbs += be_tx_compl_process(adapter, end_idx);
+ num_wrbs += be_tx_compl_process(adapter, txo, end_idx);
cmpl++;
}
if (cmpl) {
@@ -1607,7 +1560,7 @@ static void be_tx_compl_clean(struct be_adapter *adapter)
index_adv(&end_idx,
wrb_cnt_for_skb(adapter, sent_skb, &dummy_wrb) - 1,
txq->len);
- num_wrbs = be_tx_compl_process(adapter, end_idx);
+ num_wrbs = be_tx_compl_process(adapter, txo, end_idx);
atomic_sub(num_wrbs, &txq->used);
}
}
@@ -1666,16 +1619,20 @@ err:
static void be_tx_queues_destroy(struct be_adapter *adapter)
{
struct be_queue_info *q;
+ struct be_tx_obj *txo;
+ u8 i;
- q = &adapter->tx_obj.q;
- if (q->created)
- be_cmd_q_destroy(adapter, q, QTYPE_TXQ);
- be_queue_free(adapter, q);
+ for_all_tx_queues(adapter, txo, i) {
+ q = &txo->q;
+ if (q->created)
+ be_cmd_q_destroy(adapter, q, QTYPE_TXQ);
+ be_queue_free(adapter, q);
- q = &adapter->tx_obj.cq;
- if (q->created)
- be_cmd_q_destroy(adapter, q, QTYPE_CQ);
- be_queue_free(adapter, q);
+ q = &txo->cq;
+ if (q->created)
+ be_cmd_q_destroy(adapter, q, QTYPE_CQ);
+ be_queue_free(adapter, q);
+ }
/* Clear any residual events */
be_eq_clean(adapter, &adapter->tx_eq);
@@ -1686,56 +1643,64 @@ static void be_tx_queues_destroy(struct be_adapter *adapter)
be_queue_free(adapter, q);
}
+static int be_num_txqs_want(struct be_adapter *adapter)
+{
+ if ((num_vfs && adapter->sriov_enabled) ||
+ be_is_mc(adapter) ||
+ lancer_chip(adapter) || !be_physfn(adapter) ||
+ adapter->generation == BE_GEN2)
+ return 1;
+ else
+ return MAX_TX_QS;
+}
+
+/* One TX event queue is shared by all TX compl qs */
static int be_tx_queues_create(struct be_adapter *adapter)
{
struct be_queue_info *eq, *q, *cq;
+ struct be_tx_obj *txo;
+ u8 i;
+
+ adapter->num_tx_qs = be_num_txqs_want(adapter);
+ if (adapter->num_tx_qs != MAX_TX_QS)
+ netif_set_real_num_tx_queues(adapter->netdev,
+ adapter->num_tx_qs);
adapter->tx_eq.max_eqd = 0;
adapter->tx_eq.min_eqd = 0;
adapter->tx_eq.cur_eqd = 96;
adapter->tx_eq.enable_aic = false;
- /* Alloc Tx Event queue */
+
eq = &adapter->tx_eq.q;
- if (be_queue_alloc(adapter, eq, EVNT_Q_LEN, sizeof(struct be_eq_entry)))
+ if (be_queue_alloc(adapter, eq, EVNT_Q_LEN,
+ sizeof(struct be_eq_entry)))
return -1;
- /* Ask BE to create Tx Event queue */
if (be_cmd_eq_create(adapter, eq, adapter->tx_eq.cur_eqd))
- goto tx_eq_free;
-
+ goto err;
adapter->tx_eq.eq_idx = adapter->eq_next_idx++;
-
- /* Alloc TX eth compl queue */
- cq = &adapter->tx_obj.cq;
- if (be_queue_alloc(adapter, cq, TX_CQ_LEN,
+ for_all_tx_queues(adapter, txo, i) {
+ cq = &txo->cq;
+ if (be_queue_alloc(adapter, cq, TX_CQ_LEN,
sizeof(struct be_eth_tx_compl)))
- goto tx_eq_destroy;
+ goto err;
- /* Ask BE to create Tx eth compl queue */
- if (be_cmd_cq_create(adapter, cq, eq, false, false, 3))
- goto tx_cq_free;
+ if (be_cmd_cq_create(adapter, cq, eq, false, false, 3))
+ goto err;
- /* Alloc TX eth queue */
- q = &adapter->tx_obj.q;
- if (be_queue_alloc(adapter, q, TX_Q_LEN, sizeof(struct be_eth_wrb)))
- goto tx_cq_destroy;
+ q = &txo->q;
+ if (be_queue_alloc(adapter, q, TX_Q_LEN,
+ sizeof(struct be_eth_wrb)))
+ goto err;
- /* Ask BE to create Tx eth queue */
- if (be_cmd_txq_create(adapter, q, cq))
- goto tx_q_free;
+ if (be_cmd_txq_create(adapter, q, cq))
+ goto err;
+ }
return 0;
-tx_q_free:
- be_queue_free(adapter, q);
-tx_cq_destroy:
- be_cmd_q_destroy(adapter, cq, QTYPE_CQ);
-tx_cq_free:
- be_queue_free(adapter, cq);
-tx_eq_destroy:
- be_cmd_q_destroy(adapter, eq, QTYPE_EQ);
-tx_eq_free:
- be_queue_free(adapter, eq);
+err:
+ be_tx_queues_destroy(adapter);
return -1;
}
@@ -1746,37 +1711,25 @@ static void be_rx_queues_destroy(struct be_adapter *adapter)
int i;
for_all_rx_queues(adapter, rxo, i) {
- q = &rxo->q;
- if (q->created) {
- be_cmd_q_destroy(adapter, q, QTYPE_RXQ);
- /* After the rxq is invalidated, wait for a grace time
- * of 1ms for all dma to end and the flush compl to
- * arrive
- */
- mdelay(1);
- be_rx_q_clean(adapter, rxo);
- }
- be_queue_free(adapter, q);
+ be_queue_free(adapter, &rxo->q);
q = &rxo->cq;
if (q->created)
be_cmd_q_destroy(adapter, q, QTYPE_CQ);
be_queue_free(adapter, q);
- /* Clear any residual events */
q = &rxo->rx_eq.q;
- if (q->created) {
- be_eq_clean(adapter, &rxo->rx_eq);
+ if (q->created)
be_cmd_q_destroy(adapter, q, QTYPE_EQ);
- }
be_queue_free(adapter, q);
}
}
static u32 be_num_rxqs_want(struct be_adapter *adapter)
{
- if (multi_rxq && (adapter->function_caps & BE_FUNCTION_CAPS_RSS) &&
- !adapter->sriov_enabled && !(adapter->function_mode & 0x400)) {
+ if ((adapter->function_caps & BE_FUNCTION_CAPS_RSS) &&
+ !adapter->sriov_enabled && be_physfn(adapter) &&
+ !be_is_mc(adapter)) {
return 1 + MAX_RSS_QS; /* one default non-RSS queue */
} else {
dev_warn(&adapter->pdev->dev,
@@ -1827,30 +1780,14 @@ static int be_rx_queues_create(struct be_adapter *adapter)
rc = be_cmd_cq_create(adapter, cq, eq, false, false, 3);
if (rc)
goto err;
- /* Rx Q */
+
+ /* Rx Q - will be created in be_open() */
q = &rxo->q;
rc = be_queue_alloc(adapter, q, RX_Q_LEN,
sizeof(struct be_eth_rx_d));
if (rc)
goto err;
- rc = be_cmd_rxq_create(adapter, q, cq->id, rx_frag_size,
- BE_MAX_JUMBO_FRAME_SIZE, adapter->if_handle,
- (i > 0) ? 1 : 0/* rss enable */, &rxo->rss_id);
- if (rc)
- goto err;
- }
-
- if (be_multi_rxq(adapter)) {
- u8 rsstable[MAX_RSS_QS];
-
- for_all_rss_queues(adapter, rxo, i)
- rsstable[i] = rxo->rss_id;
-
- rc = be_cmd_rss_config(adapter, rsstable,
- adapter->num_rx_qs - 1);
- if (rc)
- goto err;
}
return 0;
@@ -1876,10 +1813,10 @@ static irqreturn_t be_intx(int irq, void *dev)
if (lancer_chip(adapter)) {
if (event_peek(&adapter->tx_eq))
- tx = event_handle(adapter, &adapter->tx_eq);
+ tx = event_handle(adapter, &adapter->tx_eq, false);
for_all_rx_queues(adapter, rxo, i) {
if (event_peek(&rxo->rx_eq))
- rx |= event_handle(adapter, &rxo->rx_eq);
+ rx |= event_handle(adapter, &rxo->rx_eq, true);
}
if (!(tx || rx))
@@ -1892,11 +1829,11 @@ static irqreturn_t be_intx(int irq, void *dev)
return IRQ_NONE;
if ((1 << adapter->tx_eq.eq_idx & isr))
- event_handle(adapter, &adapter->tx_eq);
+ event_handle(adapter, &adapter->tx_eq, false);
for_all_rx_queues(adapter, rxo, i) {
if ((1 << rxo->rx_eq.eq_idx & isr))
- event_handle(adapter, &rxo->rx_eq);
+ event_handle(adapter, &rxo->rx_eq, true);
}
}
@@ -1908,7 +1845,7 @@ static irqreturn_t be_msix_rx(int irq, void *dev)
struct be_rx_obj *rxo = dev;
struct be_adapter *adapter = rxo->adapter;
- event_handle(adapter, &rxo->rx_eq);
+ event_handle(adapter, &rxo->rx_eq, true);
return IRQ_HANDLED;
}
@@ -1917,7 +1854,7 @@ static irqreturn_t be_msix_tx_mcc(int irq, void *dev)
{
struct be_adapter *adapter = dev;
- event_handle(adapter, &adapter->tx_eq);
+ event_handle(adapter, &adapter->tx_eq, false);
return IRQ_HANDLED;
}
@@ -1936,36 +1873,50 @@ static int be_poll_rx(struct napi_struct *napi, int budget)
struct be_rx_compl_info *rxcp;
u32 work_done;
- rxo->stats.rx_polls++;
+ rx_stats(rxo)->rx_polls++;
for (work_done = 0; work_done < budget; work_done++) {
rxcp = be_rx_compl_get(rxo);
if (!rxcp)
break;
- /* Ignore flush completions */
- if (rxcp->num_rcvd && rxcp->pkt_size) {
- if (do_gro(rxcp))
- be_rx_compl_process_gro(adapter, rxo, rxcp);
- else
- be_rx_compl_process(adapter, rxo, rxcp);
- } else if (rxcp->pkt_size == 0) {
+ /* Is it a flush compl that has no data */
+ if (unlikely(rxcp->num_rcvd == 0))
+ goto loop_continue;
+
+ /* Discard compl with partial DMA Lancer B0 */
+ if (unlikely(!rxcp->pkt_size)) {
+ be_rx_compl_discard(adapter, rxo, rxcp);
+ goto loop_continue;
+ }
+
+ /* On BE drop pkts that arrive due to imperfect filtering in
+ * promiscuous mode on some skews
+ */
+ if (unlikely(rxcp->port != adapter->port_num &&
+ !lancer_chip(adapter))) {
be_rx_compl_discard(adapter, rxo, rxcp);
+ goto loop_continue;
}
+ if (do_gro(rxcp))
+ be_rx_compl_process_gro(adapter, rxo, rxcp);
+ else
+ be_rx_compl_process(adapter, rxo, rxcp);
+loop_continue:
be_rx_stats_update(rxo, rxcp);
}
+ be_cq_notify(adapter, rx_cq->id, false, work_done);
+
/* Refill the queue */
- if (atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM)
+ if (work_done && atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM)
be_post_rx_frags(rxo, GFP_ATOMIC);
/* All consumed */
if (work_done < budget) {
napi_complete(napi);
- be_cq_notify(adapter, rx_cq->id, true, work_done);
- } else {
- /* More to be consumed; continue with interrupts disabled */
- be_cq_notify(adapter, rx_cq->id, false, work_done);
+ /* Arm CQ */
+ be_cq_notify(adapter, rx_cq->id, true, 0);
}
return work_done;
}
@@ -1978,86 +1929,111 @@ static int be_poll_tx_mcc(struct napi_struct *napi, int budget)
struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi);
struct be_adapter *adapter =
container_of(tx_eq, struct be_adapter, tx_eq);
- struct be_queue_info *txq = &adapter->tx_obj.q;
- struct be_queue_info *tx_cq = &adapter->tx_obj.cq;
+ struct be_tx_obj *txo;
struct be_eth_tx_compl *txcp;
- int tx_compl = 0, mcc_compl, status = 0;
- u16 end_idx, num_wrbs = 0;
+ int tx_compl, mcc_compl, status = 0;
+ u8 i;
+ u16 num_wrbs;
+
+ for_all_tx_queues(adapter, txo, i) {
+ tx_compl = 0;
+ num_wrbs = 0;
+ while ((txcp = be_tx_compl_get(&txo->cq))) {
+ num_wrbs += be_tx_compl_process(adapter, txo,
+ AMAP_GET_BITS(struct amap_eth_tx_compl,
+ wrb_index, txcp));
+ tx_compl++;
+ }
+ if (tx_compl) {
+ be_cq_notify(adapter, txo->cq.id, true, tx_compl);
+
+ atomic_sub(num_wrbs, &txo->q.used);
+
+ /* As Tx wrbs have been freed up, wake up netdev queue
+ * if it was stopped due to lack of tx wrbs. */
+ if (__netif_subqueue_stopped(adapter->netdev, i) &&
+ atomic_read(&txo->q.used) < txo->q.len / 2) {
+ netif_wake_subqueue(adapter->netdev, i);
+ }
- while ((txcp = be_tx_compl_get(tx_cq))) {
- end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl,
- wrb_index, txcp);
- num_wrbs += be_tx_compl_process(adapter, end_idx);
- tx_compl++;
+ u64_stats_update_begin(&tx_stats(txo)->sync_compl);
+ tx_stats(txo)->tx_compl += tx_compl;
+ u64_stats_update_end(&tx_stats(txo)->sync_compl);
+ }
}
mcc_compl = be_process_mcc(adapter, &status);
- napi_complete(napi);
-
if (mcc_compl) {
struct be_mcc_obj *mcc_obj = &adapter->mcc_obj;
be_cq_notify(adapter, mcc_obj->cq.id, true, mcc_compl);
}
- if (tx_compl) {
- be_cq_notify(adapter, adapter->tx_obj.cq.id, true, tx_compl);
-
- atomic_sub(num_wrbs, &txq->used);
-
- /* As Tx wrbs have been freed up, wake up netdev queue if
- * it was stopped due to lack of tx wrbs.
- */
- if (netif_queue_stopped(adapter->netdev) &&
- atomic_read(&txq->used) < txq->len / 2) {
- netif_wake_queue(adapter->netdev);
- }
-
- tx_stats(adapter)->be_tx_events++;
- tx_stats(adapter)->be_tx_compl += tx_compl;
- }
+ napi_complete(napi);
+ be_eq_notify(adapter, tx_eq->q.id, true, false, 0);
+ adapter->drv_stats.tx_events++;
return 1;
}
void be_detect_dump_ue(struct be_adapter *adapter)
{
- u32 ue_status_lo, ue_status_hi, ue_status_lo_mask, ue_status_hi_mask;
+ u32 ue_lo = 0, ue_hi = 0, ue_lo_mask = 0, ue_hi_mask = 0;
+ u32 sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0;
u32 i;
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_LOW, &ue_status_lo);
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_HIGH, &ue_status_hi);
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_LOW_MASK, &ue_status_lo_mask);
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_HI_MASK, &ue_status_hi_mask);
+ if (lancer_chip(adapter)) {
+ sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET);
+ if (sliport_status & SLIPORT_STATUS_ERR_MASK) {
+ sliport_err1 = ioread32(adapter->db +
+ SLIPORT_ERROR1_OFFSET);
+ sliport_err2 = ioread32(adapter->db +
+ SLIPORT_ERROR2_OFFSET);
+ }
+ } else {
+ pci_read_config_dword(adapter->pdev,
+ PCICFG_UE_STATUS_LOW, &ue_lo);
+ pci_read_config_dword(adapter->pdev,
+ PCICFG_UE_STATUS_HIGH, &ue_hi);
+ pci_read_config_dword(adapter->pdev,
+ PCICFG_UE_STATUS_LOW_MASK, &ue_lo_mask);
+ pci_read_config_dword(adapter->pdev,
+ PCICFG_UE_STATUS_HI_MASK, &ue_hi_mask);
- ue_status_lo = (ue_status_lo & (~ue_status_lo_mask));
- ue_status_hi = (ue_status_hi & (~ue_status_hi_mask));
+ ue_lo = (ue_lo & (~ue_lo_mask));
+ ue_hi = (ue_hi & (~ue_hi_mask));
+ }
- if (ue_status_lo || ue_status_hi) {
+ if (ue_lo || ue_hi ||
+ sliport_status & SLIPORT_STATUS_ERR_MASK) {
adapter->ue_detected = true;
adapter->eeh_err = true;
dev_err(&adapter->pdev->dev, "UE Detected!!\n");
}
- if (ue_status_lo) {
- for (i = 0; ue_status_lo; ue_status_lo >>= 1, i++) {
- if (ue_status_lo & 1)
+ if (ue_lo) {
+ for (i = 0; ue_lo; ue_lo >>= 1, i++) {
+ if (ue_lo & 1)
dev_err(&adapter->pdev->dev,
"UE: %s bit set\n", ue_status_low_desc[i]);
}
}
- if (ue_status_hi) {
- for (i = 0; ue_status_hi; ue_status_hi >>= 1, i++) {
- if (ue_status_hi & 1)
+ if (ue_hi) {
+ for (i = 0; ue_hi; ue_hi >>= 1, i++) {
+ if (ue_hi & 1)
dev_err(&adapter->pdev->dev,
"UE: %s bit set\n", ue_status_hi_desc[i]);
}
}
+ if (sliport_status & SLIPORT_STATUS_ERR_MASK) {
+ dev_err(&adapter->pdev->dev,
+ "sliport status 0x%x\n", sliport_status);
+ dev_err(&adapter->pdev->dev,
+ "sliport error1 0x%x\n", sliport_err1);
+ dev_err(&adapter->pdev->dev,
+ "sliport error2 0x%x\n", sliport_err2);
+ }
}
static void be_worker(struct work_struct *work)
@@ -2067,7 +2043,7 @@ static void be_worker(struct work_struct *work)
struct be_rx_obj *rxo;
int i;
- if (!adapter->ue_detected && !lancer_chip(adapter))
+ if (!adapter->ue_detected)
be_detect_dump_ue(adapter);
/* when interrupts are not yet enabled, just reap any pending
@@ -2092,10 +2068,8 @@ static void be_worker(struct work_struct *work)
else
be_cmd_get_stats(adapter, &adapter->stats_cmd);
}
- be_tx_rate_update(adapter);
for_all_rx_queues(adapter, rxo, i) {
- be_rx_rate_update(rxo);
be_rx_eqd_update(adapter, rxo);
if (rxo->rx_post_starved) {
@@ -2142,7 +2116,7 @@ done:
return;
}
-static void be_sriov_enable(struct be_adapter *adapter)
+static int be_sriov_enable(struct be_adapter *adapter)
{
be_check_sriov_fn_type(adapter);
#ifdef CONFIG_PCI_IOV
@@ -2164,8 +2138,17 @@ static void be_sriov_enable(struct be_adapter *adapter)
status = pci_enable_sriov(adapter->pdev, num_vfs);
adapter->sriov_enabled = status ? false : true;
+
+ if (adapter->sriov_enabled) {
+ adapter->vf_cfg = kcalloc(num_vfs,
+ sizeof(struct be_vf_cfg),
+ GFP_KERNEL);
+ if (!adapter->vf_cfg)
+ return -ENOMEM;
+ }
}
#endif
+ return 0;
}
static void be_sriov_disable(struct be_adapter *adapter)
@@ -2173,6 +2156,7 @@ static void be_sriov_disable(struct be_adapter *adapter)
#ifdef CONFIG_PCI_IOV
if (adapter->sriov_enabled) {
pci_disable_sriov(adapter->pdev);
+ kfree(adapter->vf_cfg);
adapter->sriov_enabled = false;
}
#endif
@@ -2290,18 +2274,41 @@ done:
adapter->isr_registered = false;
}
+static void be_rx_queues_clear(struct be_adapter *adapter)
+{
+ struct be_queue_info *q;
+ struct be_rx_obj *rxo;
+ int i;
+
+ for_all_rx_queues(adapter, rxo, i) {
+ q = &rxo->q;
+ if (q->created) {
+ be_cmd_rxq_destroy(adapter, q);
+ /* After the rxq is invalidated, wait for a grace time
+ * of 1ms for all dma to end and the flush compl to
+ * arrive
+ */
+ mdelay(1);
+ be_rx_q_clean(adapter, rxo);
+ }
+
+ /* Clear any residual events */
+ q = &rxo->rx_eq.q;
+ if (q->created)
+ be_eq_clean(adapter, &rxo->rx_eq);
+ }
+}
+
static int be_close(struct net_device *netdev)
{
struct be_adapter *adapter = netdev_priv(netdev);
struct be_rx_obj *rxo;
+ struct be_tx_obj *txo;
struct be_eq_obj *tx_eq = &adapter->tx_eq;
int vec, i;
be_async_mcc_disable(adapter);
- netif_carrier_off(netdev);
- adapter->link_up = false;
-
if (!lancer_chip(adapter))
be_intr_set(adapter, false);
@@ -2311,10 +2318,11 @@ static int be_close(struct net_device *netdev)
napi_disable(&tx_eq->napi);
if (lancer_chip(adapter)) {
- be_cq_notify(adapter, adapter->tx_obj.cq.id, false, 0);
be_cq_notify(adapter, adapter->mcc_obj.cq.id, false, 0);
for_all_rx_queues(adapter, rxo, i)
be_cq_notify(adapter, rxo->cq.id, false, 0);
+ for_all_tx_queues(adapter, txo, i)
+ be_cq_notify(adapter, txo->cq.id, false, 0);
}
if (msix_enabled(adapter)) {
@@ -2333,25 +2341,57 @@ static int be_close(struct net_device *netdev)
/* Wait for all pending tx completions to arrive so that
* all tx skbs are freed.
*/
- be_tx_compl_clean(adapter);
+ for_all_tx_queues(adapter, txo, i)
+ be_tx_compl_clean(adapter, txo);
+ be_rx_queues_clear(adapter);
return 0;
}
-static int be_open(struct net_device *netdev)
+static int be_rx_queues_setup(struct be_adapter *adapter)
{
- struct be_adapter *adapter = netdev_priv(netdev);
- struct be_eq_obj *tx_eq = &adapter->tx_eq;
struct be_rx_obj *rxo;
- bool link_up;
- int status, i;
- u8 mac_speed;
- u16 link_speed;
+ int rc, i;
+ u8 rsstable[MAX_RSS_QS];
+
+ for_all_rx_queues(adapter, rxo, i) {
+ rc = be_cmd_rxq_create(adapter, &rxo->q, rxo->cq.id,
+ rx_frag_size, BE_MAX_JUMBO_FRAME_SIZE,
+ adapter->if_handle,
+ (i > 0) ? 1 : 0/* rss enable */, &rxo->rss_id);
+ if (rc)
+ return rc;
+ }
+
+ if (be_multi_rxq(adapter)) {
+ for_all_rss_queues(adapter, rxo, i)
+ rsstable[i] = rxo->rss_id;
+
+ rc = be_cmd_rss_config(adapter, rsstable,
+ adapter->num_rx_qs - 1);
+ if (rc)
+ return rc;
+ }
+ /* First time posting */
for_all_rx_queues(adapter, rxo, i) {
be_post_rx_frags(rxo, GFP_KERNEL);
napi_enable(&rxo->rx_eq.napi);
}
+ return 0;
+}
+
+static int be_open(struct net_device *netdev)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+ struct be_eq_obj *tx_eq = &adapter->tx_eq;
+ struct be_rx_obj *rxo;
+ int status, i;
+
+ status = be_rx_queues_setup(adapter);
+ if (status)
+ goto err;
+
napi_enable(&tx_eq->napi);
be_irq_register(adapter);
@@ -2369,23 +2409,6 @@ static int be_open(struct net_device *netdev)
/* Now that interrupts are on we can process async mcc */
be_async_mcc_enable(adapter);
- status = be_cmd_link_status_query(adapter, &link_up, &mac_speed,
- &link_speed, 0);
- if (status)
- goto err;
- be_link_status_update(adapter, link_up);
-
- if (be_physfn(adapter)) {
- status = be_vid_config(adapter, false, 0);
- if (status)
- goto err;
-
- status = be_cmd_set_flow_control(adapter,
- adapter->tx_fc, adapter->rx_fc);
- if (status)
- goto err;
- }
-
return 0;
err:
be_close(adapter->netdev);
@@ -2439,7 +2462,7 @@ static int be_setup_wol(struct be_adapter *adapter, bool enable)
*/
static inline int be_vf_eth_addr_config(struct be_adapter *adapter)
{
- u32 vf = 0;
+ u32 vf;
int status = 0;
u8 mac[ETH_ALEN];
@@ -2461,7 +2484,7 @@ static inline int be_vf_eth_addr_config(struct be_adapter *adapter)
return status;
}
-static inline void be_vf_eth_addr_rem(struct be_adapter *adapter)
+static void be_vf_clear(struct be_adapter *adapter)
{
u32 vf;
@@ -2471,125 +2494,159 @@ static inline void be_vf_eth_addr_rem(struct be_adapter *adapter)
adapter->vf_cfg[vf].vf_if_handle,
adapter->vf_cfg[vf].vf_pmac_id, vf + 1);
}
+
+ for (vf = 0; vf < num_vfs; vf++)
+ if (adapter->vf_cfg[vf].vf_if_handle)
+ be_cmd_if_destroy(adapter,
+ adapter->vf_cfg[vf].vf_if_handle, vf + 1);
}
-static int be_setup(struct be_adapter *adapter)
+static int be_clear(struct be_adapter *adapter)
{
- struct net_device *netdev = adapter->netdev;
- u32 cap_flags, en_flags, vf = 0;
- int status;
- u8 mac[ETH_ALEN];
+ if (be_physfn(adapter) && adapter->sriov_enabled)
+ be_vf_clear(adapter);
- cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED |
- BE_IF_FLAGS_BROADCAST |
- BE_IF_FLAGS_MULTICAST;
+ be_cmd_if_destroy(adapter, adapter->if_handle, 0);
- if (be_physfn(adapter)) {
- cap_flags |= BE_IF_FLAGS_MCAST_PROMISCUOUS |
- BE_IF_FLAGS_PROMISCUOUS |
- BE_IF_FLAGS_PASS_L3L4_ERRORS;
- en_flags |= BE_IF_FLAGS_PASS_L3L4_ERRORS;
-
- if (adapter->function_caps & BE_FUNCTION_CAPS_RSS) {
- cap_flags |= BE_IF_FLAGS_RSS;
- en_flags |= BE_IF_FLAGS_RSS;
- }
- }
+ be_mcc_queues_destroy(adapter);
+ be_rx_queues_destroy(adapter);
+ be_tx_queues_destroy(adapter);
+ adapter->eq_next_idx = 0;
- status = be_cmd_if_create(adapter, cap_flags, en_flags,
- netdev->dev_addr, false/* pmac_invalid */,
- &adapter->if_handle, &adapter->pmac_id, 0);
- if (status != 0)
- goto do_none;
+ adapter->be3_native = false;
+ adapter->promiscuous = false;
- if (be_physfn(adapter)) {
- if (adapter->sriov_enabled) {
- while (vf < num_vfs) {
- cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED |
- BE_IF_FLAGS_BROADCAST;
- status = be_cmd_if_create(adapter, cap_flags,
- en_flags, mac, true,
+ /* tell fw we're done with firing cmds */
+ be_cmd_fw_clean(adapter);
+ return 0;
+}
+
+static int be_vf_setup(struct be_adapter *adapter)
+{
+ u32 cap_flags, en_flags, vf;
+ u16 lnk_speed;
+ int status;
+
+ cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST;
+ for (vf = 0; vf < num_vfs; vf++) {
+ status = be_cmd_if_create(adapter, cap_flags, en_flags, NULL,
&adapter->vf_cfg[vf].vf_if_handle,
NULL, vf+1);
- if (status) {
- dev_err(&adapter->pdev->dev,
- "Interface Create failed for VF %d\n",
- vf);
- goto if_destroy;
- }
- adapter->vf_cfg[vf].vf_pmac_id =
- BE_INVALID_PMAC_ID;
- vf++;
- }
- }
- } else {
- status = be_cmd_mac_addr_query(adapter, mac,
- MAC_ADDRESS_TYPE_NETWORK, false, adapter->if_handle);
- if (!status) {
- memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN);
- memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN);
- }
+ if (status)
+ goto err;
+ adapter->vf_cfg[vf].vf_pmac_id = BE_INVALID_PMAC_ID;
+ }
+
+ if (!lancer_chip(adapter)) {
+ status = be_vf_eth_addr_config(adapter);
+ if (status)
+ goto err;
}
+ for (vf = 0; vf < num_vfs; vf++) {
+ status = be_cmd_link_status_query(adapter, NULL, &lnk_speed,
+ vf + 1);
+ if (status)
+ goto err;
+ adapter->vf_cfg[vf].vf_tx_rate = lnk_speed * 10;
+ }
+ return 0;
+err:
+ return status;
+}
+
+static int be_setup(struct be_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ u32 cap_flags, en_flags;
+ u32 tx_fc, rx_fc;
+ int status;
+ u8 mac[ETH_ALEN];
+
+ /* Allow all priorities by default. A GRP5 evt may modify this */
+ adapter->vlan_prio_bmap = 0xff;
+ adapter->link_speed = -1;
+
+ be_cmd_req_native_mode(adapter);
+
status = be_tx_queues_create(adapter);
if (status != 0)
- goto if_destroy;
+ goto err;
status = be_rx_queues_create(adapter);
if (status != 0)
- goto tx_qs_destroy;
+ goto err;
status = be_mcc_queues_create(adapter);
if (status != 0)
- goto rx_qs_destroy;
+ goto err;
- adapter->link_speed = -1;
+ memset(mac, 0, ETH_ALEN);
+ status = be_cmd_mac_addr_query(adapter, mac, MAC_ADDRESS_TYPE_NETWORK,
+ true /*permanent */, 0);
+ if (status)
+ return status;
+ memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN);
+ memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN);
- return 0;
+ en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST |
+ BE_IF_FLAGS_MULTICAST | BE_IF_FLAGS_PASS_L3L4_ERRORS;
+ cap_flags = en_flags | BE_IF_FLAGS_MCAST_PROMISCUOUS |
+ BE_IF_FLAGS_PROMISCUOUS;
+ if (adapter->function_caps & BE_FUNCTION_CAPS_RSS) {
+ cap_flags |= BE_IF_FLAGS_RSS;
+ en_flags |= BE_IF_FLAGS_RSS;
+ }
+ status = be_cmd_if_create(adapter, cap_flags, en_flags,
+ netdev->dev_addr, &adapter->if_handle,
+ &adapter->pmac_id, 0);
+ if (status != 0)
+ goto err;
-rx_qs_destroy:
- be_rx_queues_destroy(adapter);
-tx_qs_destroy:
- be_tx_queues_destroy(adapter);
-if_destroy:
- if (be_physfn(adapter) && adapter->sriov_enabled)
- for (vf = 0; vf < num_vfs; vf++)
- if (adapter->vf_cfg[vf].vf_if_handle)
- be_cmd_if_destroy(adapter,
- adapter->vf_cfg[vf].vf_if_handle,
- vf + 1);
- be_cmd_if_destroy(adapter, adapter->if_handle, 0);
-do_none:
- return status;
-}
+ /* For BEx, the VF's permanent mac queried from card is incorrect.
+ * Query the mac configued by the PF using if_handle
+ */
+ if (!be_physfn(adapter) && !lancer_chip(adapter)) {
+ status = be_cmd_mac_addr_query(adapter, mac,
+ MAC_ADDRESS_TYPE_NETWORK, false, adapter->if_handle);
+ if (!status) {
+ memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN);
+ memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN);
+ }
+ }
-static int be_clear(struct be_adapter *adapter)
-{
- int vf;
+ be_cmd_get_fw_ver(adapter, adapter->fw_ver, NULL);
- if (be_physfn(adapter) && adapter->sriov_enabled)
- be_vf_eth_addr_rem(adapter);
+ status = be_vid_config(adapter, false, 0);
+ if (status)
+ goto err;
- be_mcc_queues_destroy(adapter);
- be_rx_queues_destroy(adapter);
- be_tx_queues_destroy(adapter);
- adapter->eq_next_idx = 0;
+ be_set_rx_mode(adapter->netdev);
- if (be_physfn(adapter) && adapter->sriov_enabled)
- for (vf = 0; vf < num_vfs; vf++)
- if (adapter->vf_cfg[vf].vf_if_handle)
- be_cmd_if_destroy(adapter,
- adapter->vf_cfg[vf].vf_if_handle,
- vf + 1);
+ status = be_cmd_get_flow_control(adapter, &tx_fc, &rx_fc);
+ if (status)
+ goto err;
+ if (rx_fc != adapter->rx_fc || tx_fc != adapter->tx_fc) {
+ status = be_cmd_set_flow_control(adapter, adapter->tx_fc,
+ adapter->rx_fc);
+ if (status)
+ goto err;
+ }
- be_cmd_if_destroy(adapter, adapter->if_handle, 0);
+ pcie_set_readrq(adapter->pdev, 4096);
+
+ if (be_physfn(adapter) && adapter->sriov_enabled) {
+ status = be_vf_setup(adapter);
+ if (status)
+ goto err;
+ }
- /* tell fw we're done with firing cmds */
- be_cmd_fw_clean(adapter);
return 0;
+err:
+ be_clear(adapter);
+ return status;
}
-
#define FW_FILE_HDR_SIGN "ServerEngines Corp. "
static bool be_flash_redboot(struct be_adapter *adapter,
const u8 *p, u32 img_start, int image_size,
@@ -2618,6 +2675,21 @@ static bool be_flash_redboot(struct be_adapter *adapter,
return true;
}
+static bool phy_flashing_required(struct be_adapter *adapter)
+{
+ int status = 0;
+ struct be_phy_info phy_info;
+
+ status = be_cmd_get_phy_info(adapter, &phy_info);
+ if (status)
+ return false;
+ if ((phy_info.phy_type == TN_8022) &&
+ (phy_info.interface_type == PHY_TYPE_BASET_10GB)) {
+ return true;
+ }
+ return false;
+}
+
static int be_flash_data(struct be_adapter *adapter,
const struct firmware *fw,
struct be_dma_mem *flash_cmd, int num_of_images)
@@ -2631,7 +2703,7 @@ static int be_flash_data(struct be_adapter *adapter,
const struct flash_comp *pflashcomp;
int num_comp;
- static const struct flash_comp gen3_flash_types[9] = {
+ static const struct flash_comp gen3_flash_types[10] = {
{ FLASH_iSCSI_PRIMARY_IMAGE_START_g3, IMG_TYPE_ISCSI_ACTIVE,
FLASH_IMAGE_MAX_SIZE_g3},
{ FLASH_REDBOOT_START_g3, IMG_TYPE_REDBOOT,
@@ -2649,7 +2721,9 @@ static int be_flash_data(struct be_adapter *adapter,
{ FLASH_FCoE_BACKUP_IMAGE_START_g3, IMG_TYPE_FCOE_FW_BACKUP,
FLASH_IMAGE_MAX_SIZE_g3},
{ FLASH_NCSI_START_g3, IMG_TYPE_NCSI_FW,
- FLASH_NCSI_IMAGE_MAX_SIZE_g3}
+ FLASH_NCSI_IMAGE_MAX_SIZE_g3},
+ { FLASH_PHY_FW_START_g3, IMG_TYPE_PHY_FW,
+ FLASH_PHY_FW_IMAGE_MAX_SIZE_g3}
};
static const struct flash_comp gen2_flash_types[8] = {
{ FLASH_iSCSI_PRIMARY_IMAGE_START_g2, IMG_TYPE_ISCSI_ACTIVE,
@@ -2683,6 +2757,10 @@ static int be_flash_data(struct be_adapter *adapter,
if ((pflashcomp[i].optype == IMG_TYPE_NCSI_FW) &&
memcmp(adapter->fw_ver, "3.102.148.0", 11) < 0)
continue;
+ if (pflashcomp[i].optype == IMG_TYPE_PHY_FW) {
+ if (!phy_flashing_required(adapter))
+ continue;
+ }
if ((pflashcomp[i].optype == IMG_TYPE_REDBOOT) &&
(!be_flash_redboot(adapter, fw->data,
pflashcomp[i].offset, pflashcomp[i].size, filehdr_size +
@@ -2691,25 +2769,35 @@ static int be_flash_data(struct be_adapter *adapter,
p = fw->data;
p += filehdr_size + pflashcomp[i].offset
+ (num_of_images * sizeof(struct image_hdr));
- if (p + pflashcomp[i].size > fw->data + fw->size)
- return -1;
- total_bytes = pflashcomp[i].size;
+ if (p + pflashcomp[i].size > fw->data + fw->size)
+ return -1;
+ total_bytes = pflashcomp[i].size;
while (total_bytes) {
if (total_bytes > 32*1024)
num_bytes = 32*1024;
else
num_bytes = total_bytes;
total_bytes -= num_bytes;
-
- if (!total_bytes)
- flash_op = FLASHROM_OPER_FLASH;
- else
- flash_op = FLASHROM_OPER_SAVE;
+ if (!total_bytes) {
+ if (pflashcomp[i].optype == IMG_TYPE_PHY_FW)
+ flash_op = FLASHROM_OPER_PHY_FLASH;
+ else
+ flash_op = FLASHROM_OPER_FLASH;
+ } else {
+ if (pflashcomp[i].optype == IMG_TYPE_PHY_FW)
+ flash_op = FLASHROM_OPER_PHY_SAVE;
+ else
+ flash_op = FLASHROM_OPER_SAVE;
+ }
memcpy(req->params.data_buf, p, num_bytes);
p += num_bytes;
status = be_cmd_write_flashrom(adapter, flash_cmd,
pflashcomp[i].optype, flash_op, num_bytes);
if (status) {
+ if ((status == ILLEGAL_IOCTL_REQ) &&
+ (pflashcomp[i].optype ==
+ IMG_TYPE_PHY_FW))
+ break;
dev_err(&adapter->pdev->dev,
"cmd to write to flash rom failed.\n");
return -1;
@@ -2897,11 +2985,11 @@ static struct net_device_ops be_netdev_ops = {
.ndo_open = be_open,
.ndo_stop = be_close,
.ndo_start_xmit = be_xmit,
- .ndo_set_rx_mode = be_set_multicast_list,
+ .ndo_set_rx_mode = be_set_rx_mode,
.ndo_set_mac_address = be_mac_addr_set,
.ndo_change_mtu = be_change_mtu,
+ .ndo_get_stats64 = be_get_stats64,
.ndo_validate_addr = eth_validate_addr,
- .ndo_vlan_rx_register = be_vlan_register,
.ndo_vlan_rx_add_vid = be_vlan_add_vid,
.ndo_vlan_rx_kill_vid = be_vlan_rem_vid,
.ndo_set_vf_mac = be_set_vf_mac,
@@ -2925,18 +3013,11 @@ static void be_netdev_init(struct net_device *netdev)
netdev->features |= netdev->hw_features |
NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER;
- netdev->vlan_features |= NETIF_F_SG | NETIF_F_TSO |
+ netdev->vlan_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
- if (lancer_chip(adapter))
- netdev->vlan_features |= NETIF_F_TSO6;
-
netdev->flags |= IFF_MULTICAST;
- /* Default settings for Rx and Tx flow control */
- adapter->rx_fc = true;
- adapter->tx_fc = true;
-
netif_set_gso_max_size(netdev, 65535);
BE_SET_NETDEV_OPS(netdev, &be_netdev_ops);
@@ -2957,14 +3038,12 @@ static void be_unmap_pci_bars(struct be_adapter *adapter)
iounmap(adapter->csr);
if (adapter->db)
iounmap(adapter->db);
- if (adapter->pcicfg && be_physfn(adapter))
- iounmap(adapter->pcicfg);
}
static int be_map_pci_bars(struct be_adapter *adapter)
{
u8 __iomem *addr;
- int pcicfg_reg, db_reg;
+ int db_reg;
if (lancer_chip(adapter)) {
addr = ioremap_nocache(pci_resource_start(adapter->pdev, 0),
@@ -2984,10 +3063,8 @@ static int be_map_pci_bars(struct be_adapter *adapter)
}
if (adapter->generation == BE_GEN2) {
- pcicfg_reg = 1;
db_reg = 4;
} else {
- pcicfg_reg = 0;
if (be_physfn(adapter))
db_reg = 4;
else
@@ -2999,16 +3076,6 @@ static int be_map_pci_bars(struct be_adapter *adapter)
goto pci_map_err;
adapter->db = addr;
- if (be_physfn(adapter)) {
- addr = ioremap_nocache(
- pci_resource_start(adapter->pdev, pcicfg_reg),
- pci_resource_len(adapter->pdev, pcicfg_reg));
- if (addr == NULL)
- goto pci_map_err;
- adapter->pcicfg = addr;
- } else
- adapter->pcicfg = adapter->db + SRIOV_VF_PCICFG_OFFSET;
-
return 0;
pci_map_err:
be_unmap_pci_bars(adapter);
@@ -3026,7 +3093,7 @@ static void be_ctrl_cleanup(struct be_adapter *adapter)
dma_free_coherent(&adapter->pdev->dev, mem->size, mem->va,
mem->dma);
- mem = &adapter->mc_cmd_mem;
+ mem = &adapter->rx_filter;
if (mem->va)
dma_free_coherent(&adapter->pdev->dev, mem->size, mem->va,
mem->dma);
@@ -3036,7 +3103,7 @@ static int be_ctrl_init(struct be_adapter *adapter)
{
struct be_dma_mem *mbox_mem_alloc = &adapter->mbox_mem_alloced;
struct be_dma_mem *mbox_mem_align = &adapter->mbox_mem;
- struct be_dma_mem *mc_cmd_mem = &adapter->mc_cmd_mem;
+ struct be_dma_mem *rx_filter = &adapter->rx_filter;
int status;
status = be_map_pci_bars(adapter);
@@ -3052,21 +3119,19 @@ static int be_ctrl_init(struct be_adapter *adapter)
status = -ENOMEM;
goto unmap_pci_bars;
}
-
mbox_mem_align->size = sizeof(struct be_mcc_mailbox);
mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16);
mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16);
memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox));
- mc_cmd_mem->size = sizeof(struct be_cmd_req_mcast_mac_config);
- mc_cmd_mem->va = dma_alloc_coherent(&adapter->pdev->dev,
- mc_cmd_mem->size, &mc_cmd_mem->dma,
- GFP_KERNEL);
- if (mc_cmd_mem->va == NULL) {
+ rx_filter->size = sizeof(struct be_cmd_req_rx_filter);
+ rx_filter->va = dma_alloc_coherent(&adapter->pdev->dev, rx_filter->size,
+ &rx_filter->dma, GFP_KERNEL);
+ if (rx_filter->va == NULL) {
status = -ENOMEM;
goto free_mbox;
}
- memset(mc_cmd_mem->va, 0, mc_cmd_mem->size);
+ memset(rx_filter->va, 0, rx_filter->size);
mutex_init(&adapter->mbox_lock);
spin_lock_init(&adapter->mcc_lock);
@@ -3133,7 +3198,6 @@ static void __devexit be_remove(struct pci_dev *pdev)
be_ctrl_cleanup(adapter);
- kfree(adapter->vf_cfg);
be_sriov_disable(adapter);
be_msix_disable(adapter);
@@ -3148,35 +3212,13 @@ static void __devexit be_remove(struct pci_dev *pdev)
static int be_get_config(struct be_adapter *adapter)
{
int status;
- u8 mac[ETH_ALEN];
-
- status = be_cmd_get_fw_ver(adapter, adapter->fw_ver);
- if (status)
- return status;
status = be_cmd_query_fw_cfg(adapter, &adapter->port_num,
&adapter->function_mode, &adapter->function_caps);
if (status)
return status;
- memset(mac, 0, ETH_ALEN);
-
- /* A default permanent address is given to each VF for Lancer*/
- if (be_physfn(adapter) || lancer_chip(adapter)) {
- status = be_cmd_mac_addr_query(adapter, mac,
- MAC_ADDRESS_TYPE_NETWORK, true /*permanent */, 0);
-
- if (status)
- return status;
-
- if (!is_valid_ether_addr(mac))
- return -EADDRNOTAVAIL;
-
- memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN);
- memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN);
- }
-
- if (adapter->function_mode & 0x400)
+ if (adapter->function_mode & FLEX10_MODE)
adapter->max_vlans = BE_NUM_VLANS_SUPPORTED/4;
else
adapter->max_vlans = BE_NUM_VLANS_SUPPORTED;
@@ -3185,7 +3227,6 @@ static int be_get_config(struct be_adapter *adapter)
if (status)
return status;
- be_cmd_check_native_mode(adapter);
return 0;
}
@@ -3288,7 +3329,7 @@ static int __devinit be_probe(struct pci_dev *pdev,
goto disable_dev;
pci_set_master(pdev);
- netdev = alloc_etherdev(sizeof(struct be_adapter));
+ netdev = alloc_etherdev_mq(sizeof(struct be_adapter), MAX_TX_QS);
if (netdev == NULL) {
status = -ENOMEM;
goto rel_reg;
@@ -3315,18 +3356,13 @@ static int __devinit be_probe(struct pci_dev *pdev,
}
}
- be_sriov_enable(adapter);
- if (adapter->sriov_enabled) {
- adapter->vf_cfg = kcalloc(num_vfs,
- sizeof(struct be_vf_cfg), GFP_KERNEL);
-
- if (!adapter->vf_cfg)
- goto free_netdev;
- }
+ status = be_sriov_enable(adapter);
+ if (status)
+ goto free_netdev;
status = be_ctrl_init(adapter);
if (status)
- goto free_vf_cfg;
+ goto disable_sriov;
if (lancer_chip(adapter)) {
status = lancer_test_and_set_rdy_state(adapter);
@@ -3360,9 +3396,16 @@ static int __devinit be_probe(struct pci_dev *pdev,
if (status)
goto stats_clean;
+ /* The INTR bit may be set in the card when probed by a kdump kernel
+ * after a crash.
+ */
+ if (!lancer_chip(adapter))
+ be_intr_set(adapter, false);
+
be_msix_enable(adapter);
INIT_DELAYED_WORK(&adapter->work, be_worker);
+ adapter->rx_fc = adapter->tx_fc = true;
status = be_setup(adapter);
if (status)
@@ -3372,35 +3415,12 @@ static int __devinit be_probe(struct pci_dev *pdev,
status = register_netdev(netdev);
if (status != 0)
goto unsetup;
- netif_carrier_off(netdev);
-
- if (be_physfn(adapter) && adapter->sriov_enabled) {
- u8 mac_speed;
- bool link_up;
- u16 vf, lnk_speed;
-
- if (!lancer_chip(adapter)) {
- status = be_vf_eth_addr_config(adapter);
- if (status)
- goto unreg_netdev;
- }
-
- for (vf = 0; vf < num_vfs; vf++) {
- status = be_cmd_link_status_query(adapter, &link_up,
- &mac_speed, &lnk_speed, vf + 1);
- if (!status)
- adapter->vf_cfg[vf].vf_tx_rate = lnk_speed * 10;
- else
- goto unreg_netdev;
- }
- }
dev_info(&pdev->dev, "%s port %d\n", nic_name(pdev), adapter->port_num);
+
schedule_delayed_work(&adapter->work, msecs_to_jiffies(100));
return 0;
-unreg_netdev:
- unregister_netdev(netdev);
unsetup:
be_clear(adapter);
msix_disable:
@@ -3409,10 +3429,9 @@ stats_clean:
be_stats_cleanup(adapter);
ctrl_clean:
be_ctrl_cleanup(adapter);
-free_vf_cfg:
- kfree(adapter->vf_cfg);
-free_netdev:
+disable_sriov:
be_sriov_disable(adapter);
+free_netdev:
free_netdev(netdev);
pci_set_drvdata(pdev, NULL);
rel_reg:
@@ -3439,7 +3458,6 @@ static int be_suspend(struct pci_dev *pdev, pm_message_t state)
be_close(netdev);
rtnl_unlock();
}
- be_cmd_get_flow_control(adapter, &adapter->tx_fc, &adapter->rx_fc);
be_clear(adapter);
be_msix_disable(adapter);
diff --git a/drivers/net/ethoc.c b/drivers/net/ethernet/ethoc.c
index a83dd312c3ac..251b635fe75a 100644
--- a/drivers/net/ethoc.c
+++ b/drivers/net/ethernet/ethoc.c
@@ -11,8 +11,10 @@
* Written by Thierry Reding <thierry.reding@avionic-design.de>
*/
+#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/crc32.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mii.h>
#include <linux/phy.h>
@@ -20,6 +22,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/module.h>
#include <net/ethoc.h>
static int buffer_size = 0x8000; /* 32 KBytes */
@@ -874,6 +877,7 @@ static netdev_tx_t ethoc_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
spin_unlock_irq(&priv->lock);
+ skb_tx_timestamp(skb);
out:
dev_kfree_skb(skb);
return NETDEV_TX_OK;
@@ -885,7 +889,7 @@ static const struct net_device_ops ethoc_netdev_ops = {
.ndo_do_ioctl = ethoc_ioctl,
.ndo_set_config = ethoc_config,
.ndo_set_mac_address = ethoc_set_mac_address,
- .ndo_set_multicast_list = ethoc_set_multicast_list,
+ .ndo_set_rx_mode = ethoc_set_multicast_list,
.ndo_change_mtu = ethoc_change_mtu,
.ndo_tx_timeout = ethoc_tx_timeout,
.ndo_start_xmit = ethoc_start_xmit,
@@ -965,7 +969,7 @@ static int __devinit ethoc_probe(struct platform_device *pdev)
priv = netdev_priv(netdev);
priv->netdev = netdev;
priv->dma_alloc = 0;
- priv->io_region_size = mmio->end - mmio->start + 1;
+ priv->io_region_size = resource_size(mmio);
priv->iobase = devm_ioremap_nocache(&pdev->dev, netdev->base_addr,
resource_size(mmio));
diff --git a/drivers/net/ethernet/faraday/Kconfig b/drivers/net/ethernet/faraday/Kconfig
new file mode 100644
index 000000000000..b8974b9e3b47
--- /dev/null
+++ b/drivers/net/ethernet/faraday/Kconfig
@@ -0,0 +1,40 @@
+#
+# Faraday device configuration
+#
+
+config NET_VENDOR_FARADAY
+ bool "Faraday devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Faraday cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_FARADAY
+
+config FTMAC100
+ tristate "Faraday FTMAC100 10/100 Ethernet support"
+ depends on ARM
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports the FTMAC100 10/100 Ethernet controller
+ from Faraday. It is used on Faraday A320, Andes AG101 and some
+ other ARM/NDS32 SoC's.
+
+config FTGMAC100
+ tristate "Faraday FTGMAC100 Gigabit Ethernet support"
+ depends on ARM
+ select PHYLIB
+ ---help---
+ This driver supports the FTGMAC100 Gigabit Ethernet controller
+ from Faraday. It is used on Faraday A369, Andes AG102 and some
+ other ARM/NDS32 SoC's.
+
+endif # NET_VENDOR_FARADAY
diff --git a/drivers/net/ethernet/faraday/Makefile b/drivers/net/ethernet/faraday/Makefile
new file mode 100644
index 000000000000..408b53980d53
--- /dev/null
+++ b/drivers/net/ethernet/faraday/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Faraday device drivers.
+#
+
+obj-$(CONFIG_FTGMAC100) += ftgmac100.o
+obj-$(CONFIG_FTMAC100) += ftmac100.o
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
new file mode 100644
index 000000000000..fb5579a3b19d
--- /dev/null
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -0,0 +1,1367 @@
+/*
+ * Faraday FTGMAC100 Gigabit Ethernet
+ *
+ * (C) Copyright 2009-2011 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <net/ip.h>
+
+#include "ftgmac100.h"
+
+#define DRV_NAME "ftgmac100"
+#define DRV_VERSION "0.7"
+
+#define RX_QUEUE_ENTRIES 256 /* must be power of 2 */
+#define TX_QUEUE_ENTRIES 512 /* must be power of 2 */
+
+#define MAX_PKT_SIZE 1518
+#define RX_BUF_SIZE PAGE_SIZE /* must be smaller than 0x3fff */
+
+/******************************************************************************
+ * private data
+ *****************************************************************************/
+struct ftgmac100_descs {
+ struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
+ struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES];
+};
+
+struct ftgmac100 {
+ struct resource *res;
+ void __iomem *base;
+ int irq;
+
+ struct ftgmac100_descs *descs;
+ dma_addr_t descs_dma_addr;
+
+ unsigned int rx_pointer;
+ unsigned int tx_clean_pointer;
+ unsigned int tx_pointer;
+ unsigned int tx_pending;
+
+ spinlock_t tx_lock;
+
+ struct net_device *netdev;
+ struct device *dev;
+ struct napi_struct napi;
+
+ struct mii_bus *mii_bus;
+ int phy_irq[PHY_MAX_ADDR];
+ struct phy_device *phydev;
+ int old_speed;
+};
+
+static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
+ struct ftgmac100_rxdes *rxdes, gfp_t gfp);
+
+/******************************************************************************
+ * internal functions (hardware register access)
+ *****************************************************************************/
+#define INT_MASK_ALL_ENABLED (FTGMAC100_INT_RPKT_LOST | \
+ FTGMAC100_INT_XPKT_ETH | \
+ FTGMAC100_INT_XPKT_LOST | \
+ FTGMAC100_INT_AHB_ERR | \
+ FTGMAC100_INT_PHYSTS_CHG | \
+ FTGMAC100_INT_RPKT_BUF | \
+ FTGMAC100_INT_NO_RXBUF)
+
+static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr)
+{
+ iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
+}
+
+static void ftgmac100_set_rx_buffer_size(struct ftgmac100 *priv,
+ unsigned int size)
+{
+ size = FTGMAC100_RBSR_SIZE(size);
+ iowrite32(size, priv->base + FTGMAC100_OFFSET_RBSR);
+}
+
+static void ftgmac100_set_normal_prio_tx_ring_base(struct ftgmac100 *priv,
+ dma_addr_t addr)
+{
+ iowrite32(addr, priv->base + FTGMAC100_OFFSET_NPTXR_BADR);
+}
+
+static void ftgmac100_txdma_normal_prio_start_polling(struct ftgmac100 *priv)
+{
+ iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD);
+}
+
+static int ftgmac100_reset_hw(struct ftgmac100 *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ int i;
+
+ /* NOTE: reset clears all registers */
+ iowrite32(FTGMAC100_MACCR_SW_RST, priv->base + FTGMAC100_OFFSET_MACCR);
+ for (i = 0; i < 5; i++) {
+ unsigned int maccr;
+
+ maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
+ if (!(maccr & FTGMAC100_MACCR_SW_RST))
+ return 0;
+
+ udelay(1000);
+ }
+
+ netdev_err(netdev, "software reset failed\n");
+ return -EIO;
+}
+
+static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac)
+{
+ unsigned int maddr = mac[0] << 8 | mac[1];
+ unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
+
+ iowrite32(maddr, priv->base + FTGMAC100_OFFSET_MAC_MADR);
+ iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
+}
+
+static void ftgmac100_init_hw(struct ftgmac100 *priv)
+{
+ /* setup ring buffer base registers */
+ ftgmac100_set_rx_ring_base(priv,
+ priv->descs_dma_addr +
+ offsetof(struct ftgmac100_descs, rxdes));
+ ftgmac100_set_normal_prio_tx_ring_base(priv,
+ priv->descs_dma_addr +
+ offsetof(struct ftgmac100_descs, txdes));
+
+ ftgmac100_set_rx_buffer_size(priv, RX_BUF_SIZE);
+
+ iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1), priv->base + FTGMAC100_OFFSET_APTC);
+
+ ftgmac100_set_mac(priv, priv->netdev->dev_addr);
+}
+
+#define MACCR_ENABLE_ALL (FTGMAC100_MACCR_TXDMA_EN | \
+ FTGMAC100_MACCR_RXDMA_EN | \
+ FTGMAC100_MACCR_TXMAC_EN | \
+ FTGMAC100_MACCR_RXMAC_EN | \
+ FTGMAC100_MACCR_FULLDUP | \
+ FTGMAC100_MACCR_CRC_APD | \
+ FTGMAC100_MACCR_RX_RUNT | \
+ FTGMAC100_MACCR_RX_BROADPKT)
+
+static void ftgmac100_start_hw(struct ftgmac100 *priv, int speed)
+{
+ int maccr = MACCR_ENABLE_ALL;
+
+ switch (speed) {
+ default:
+ case 10:
+ break;
+
+ case 100:
+ maccr |= FTGMAC100_MACCR_FAST_MODE;
+ break;
+
+ case 1000:
+ maccr |= FTGMAC100_MACCR_GIGA_MODE;
+ break;
+ }
+
+ iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
+}
+
+static void ftgmac100_stop_hw(struct ftgmac100 *priv)
+{
+ iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR);
+}
+
+/******************************************************************************
+ * internal functions (receive descriptor)
+ *****************************************************************************/
+static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS);
+}
+
+static bool ftgmac100_rxdes_last_segment(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_LRS);
+}
+
+static bool ftgmac100_rxdes_packet_ready(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY);
+}
+
+static void ftgmac100_rxdes_set_dma_own(struct ftgmac100_rxdes *rxdes)
+{
+ /* clear status bits */
+ rxdes->rxdes0 &= cpu_to_le32(FTGMAC100_RXDES0_EDORR);
+}
+
+static bool ftgmac100_rxdes_rx_error(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ERR);
+}
+
+static bool ftgmac100_rxdes_crc_error(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_CRC_ERR);
+}
+
+static bool ftgmac100_rxdes_frame_too_long(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FTL);
+}
+
+static bool ftgmac100_rxdes_runt(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RUNT);
+}
+
+static bool ftgmac100_rxdes_odd_nibble(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ODD_NB);
+}
+
+static unsigned int ftgmac100_rxdes_data_length(struct ftgmac100_rxdes *rxdes)
+{
+ return le32_to_cpu(rxdes->rxdes0) & FTGMAC100_RXDES0_VDBC;
+}
+
+static bool ftgmac100_rxdes_multicast(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_MULTICAST);
+}
+
+static void ftgmac100_rxdes_set_end_of_ring(struct ftgmac100_rxdes *rxdes)
+{
+ rxdes->rxdes0 |= cpu_to_le32(FTGMAC100_RXDES0_EDORR);
+}
+
+static void ftgmac100_rxdes_set_dma_addr(struct ftgmac100_rxdes *rxdes,
+ dma_addr_t addr)
+{
+ rxdes->rxdes3 = cpu_to_le32(addr);
+}
+
+static dma_addr_t ftgmac100_rxdes_get_dma_addr(struct ftgmac100_rxdes *rxdes)
+{
+ return le32_to_cpu(rxdes->rxdes3);
+}
+
+static bool ftgmac100_rxdes_is_tcp(struct ftgmac100_rxdes *rxdes)
+{
+ return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) ==
+ cpu_to_le32(FTGMAC100_RXDES1_PROT_TCPIP);
+}
+
+static bool ftgmac100_rxdes_is_udp(struct ftgmac100_rxdes *rxdes)
+{
+ return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) ==
+ cpu_to_le32(FTGMAC100_RXDES1_PROT_UDPIP);
+}
+
+static bool ftgmac100_rxdes_tcpcs_err(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR);
+}
+
+static bool ftgmac100_rxdes_udpcs_err(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_UDP_CHKSUM_ERR);
+}
+
+static bool ftgmac100_rxdes_ipcs_err(struct ftgmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_IP_CHKSUM_ERR);
+}
+
+/*
+ * rxdes2 is not used by hardware. We use it to keep track of page.
+ * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu().
+ */
+static void ftgmac100_rxdes_set_page(struct ftgmac100_rxdes *rxdes, struct page *page)
+{
+ rxdes->rxdes2 = (unsigned int)page;
+}
+
+static struct page *ftgmac100_rxdes_get_page(struct ftgmac100_rxdes *rxdes)
+{
+ return (struct page *)rxdes->rxdes2;
+}
+
+/******************************************************************************
+ * internal functions (receive)
+ *****************************************************************************/
+static int ftgmac100_next_rx_pointer(int pointer)
+{
+ return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
+}
+
+static void ftgmac100_rx_pointer_advance(struct ftgmac100 *priv)
+{
+ priv->rx_pointer = ftgmac100_next_rx_pointer(priv->rx_pointer);
+}
+
+static struct ftgmac100_rxdes *ftgmac100_current_rxdes(struct ftgmac100 *priv)
+{
+ return &priv->descs->rxdes[priv->rx_pointer];
+}
+
+static struct ftgmac100_rxdes *
+ftgmac100_rx_locate_first_segment(struct ftgmac100 *priv)
+{
+ struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv);
+
+ while (ftgmac100_rxdes_packet_ready(rxdes)) {
+ if (ftgmac100_rxdes_first_segment(rxdes))
+ return rxdes;
+
+ ftgmac100_rxdes_set_dma_own(rxdes);
+ ftgmac100_rx_pointer_advance(priv);
+ rxdes = ftgmac100_current_rxdes(priv);
+ }
+
+ return NULL;
+}
+
+static bool ftgmac100_rx_packet_error(struct ftgmac100 *priv,
+ struct ftgmac100_rxdes *rxdes)
+{
+ struct net_device *netdev = priv->netdev;
+ bool error = false;
+
+ if (unlikely(ftgmac100_rxdes_rx_error(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx err\n");
+
+ netdev->stats.rx_errors++;
+ error = true;
+ }
+
+ if (unlikely(ftgmac100_rxdes_crc_error(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx crc err\n");
+
+ netdev->stats.rx_crc_errors++;
+ error = true;
+ } else if (unlikely(ftgmac100_rxdes_ipcs_err(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx IP checksum err\n");
+
+ error = true;
+ }
+
+ if (unlikely(ftgmac100_rxdes_frame_too_long(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx frame too long\n");
+
+ netdev->stats.rx_length_errors++;
+ error = true;
+ } else if (unlikely(ftgmac100_rxdes_runt(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx runt\n");
+
+ netdev->stats.rx_length_errors++;
+ error = true;
+ } else if (unlikely(ftgmac100_rxdes_odd_nibble(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx odd nibble\n");
+
+ netdev->stats.rx_length_errors++;
+ error = true;
+ }
+
+ return error;
+}
+
+static void ftgmac100_rx_drop_packet(struct ftgmac100 *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv);
+ bool done = false;
+
+ if (net_ratelimit())
+ netdev_dbg(netdev, "drop packet %p\n", rxdes);
+
+ do {
+ if (ftgmac100_rxdes_last_segment(rxdes))
+ done = true;
+
+ ftgmac100_rxdes_set_dma_own(rxdes);
+ ftgmac100_rx_pointer_advance(priv);
+ rxdes = ftgmac100_current_rxdes(priv);
+ } while (!done && ftgmac100_rxdes_packet_ready(rxdes));
+
+ netdev->stats.rx_dropped++;
+}
+
+static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
+{
+ struct net_device *netdev = priv->netdev;
+ struct ftgmac100_rxdes *rxdes;
+ struct sk_buff *skb;
+ bool done = false;
+
+ rxdes = ftgmac100_rx_locate_first_segment(priv);
+ if (!rxdes)
+ return false;
+
+ if (unlikely(ftgmac100_rx_packet_error(priv, rxdes))) {
+ ftgmac100_rx_drop_packet(priv);
+ return true;
+ }
+
+ /* start processing */
+ skb = netdev_alloc_skb_ip_align(netdev, 128);
+ if (unlikely(!skb)) {
+ if (net_ratelimit())
+ netdev_err(netdev, "rx skb alloc failed\n");
+
+ ftgmac100_rx_drop_packet(priv);
+ return true;
+ }
+
+ if (unlikely(ftgmac100_rxdes_multicast(rxdes)))
+ netdev->stats.multicast++;
+
+ /*
+ * It seems that HW does checksum incorrectly with fragmented packets,
+ * so we are conservative here - if HW checksum error, let software do
+ * the checksum again.
+ */
+ if ((ftgmac100_rxdes_is_tcp(rxdes) && !ftgmac100_rxdes_tcpcs_err(rxdes)) ||
+ (ftgmac100_rxdes_is_udp(rxdes) && !ftgmac100_rxdes_udpcs_err(rxdes)))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ do {
+ dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes);
+ struct page *page = ftgmac100_rxdes_get_page(rxdes);
+ unsigned int size;
+
+ dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+
+ size = ftgmac100_rxdes_data_length(rxdes);
+ skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0, size);
+
+ skb->len += size;
+ skb->data_len += size;
+ skb->truesize += PAGE_SIZE;
+
+ if (ftgmac100_rxdes_last_segment(rxdes))
+ done = true;
+
+ ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);
+
+ ftgmac100_rx_pointer_advance(priv);
+ rxdes = ftgmac100_current_rxdes(priv);
+ } while (!done);
+
+ if (skb->len <= 64)
+ skb->truesize -= PAGE_SIZE;
+ __pskb_pull_tail(skb, min(skb->len, 64U));
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += skb->len;
+
+ /* push packet to protocol stack */
+ napi_gro_receive(&priv->napi, skb);
+
+ (*processed)++;
+ return true;
+}
+
+/******************************************************************************
+ * internal functions (transmit descriptor)
+ *****************************************************************************/
+static void ftgmac100_txdes_reset(struct ftgmac100_txdes *txdes)
+{
+ /* clear all except end of ring bit */
+ txdes->txdes0 &= cpu_to_le32(FTGMAC100_TXDES0_EDOTR);
+ txdes->txdes1 = 0;
+ txdes->txdes2 = 0;
+ txdes->txdes3 = 0;
+}
+
+static bool ftgmac100_txdes_owned_by_dma(struct ftgmac100_txdes *txdes)
+{
+ return txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN);
+}
+
+static void ftgmac100_txdes_set_dma_own(struct ftgmac100_txdes *txdes)
+{
+ /*
+ * Make sure dma own bit will not be set before any other
+ * descriptor fields.
+ */
+ wmb();
+ txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN);
+}
+
+static void ftgmac100_txdes_set_end_of_ring(struct ftgmac100_txdes *txdes)
+{
+ txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_EDOTR);
+}
+
+static void ftgmac100_txdes_set_first_segment(struct ftgmac100_txdes *txdes)
+{
+ txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_FTS);
+}
+
+static void ftgmac100_txdes_set_last_segment(struct ftgmac100_txdes *txdes)
+{
+ txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_LTS);
+}
+
+static void ftgmac100_txdes_set_buffer_size(struct ftgmac100_txdes *txdes,
+ unsigned int len)
+{
+ txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXBUF_SIZE(len));
+}
+
+static void ftgmac100_txdes_set_txint(struct ftgmac100_txdes *txdes)
+{
+ txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TXIC);
+}
+
+static void ftgmac100_txdes_set_tcpcs(struct ftgmac100_txdes *txdes)
+{
+ txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TCP_CHKSUM);
+}
+
+static void ftgmac100_txdes_set_udpcs(struct ftgmac100_txdes *txdes)
+{
+ txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_UDP_CHKSUM);
+}
+
+static void ftgmac100_txdes_set_ipcs(struct ftgmac100_txdes *txdes)
+{
+ txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_IP_CHKSUM);
+}
+
+static void ftgmac100_txdes_set_dma_addr(struct ftgmac100_txdes *txdes,
+ dma_addr_t addr)
+{
+ txdes->txdes3 = cpu_to_le32(addr);
+}
+
+static dma_addr_t ftgmac100_txdes_get_dma_addr(struct ftgmac100_txdes *txdes)
+{
+ return le32_to_cpu(txdes->txdes3);
+}
+
+/*
+ * txdes2 is not used by hardware. We use it to keep track of socket buffer.
+ * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu().
+ */
+static void ftgmac100_txdes_set_skb(struct ftgmac100_txdes *txdes,
+ struct sk_buff *skb)
+{
+ txdes->txdes2 = (unsigned int)skb;
+}
+
+static struct sk_buff *ftgmac100_txdes_get_skb(struct ftgmac100_txdes *txdes)
+{
+ return (struct sk_buff *)txdes->txdes2;
+}
+
+/******************************************************************************
+ * internal functions (transmit)
+ *****************************************************************************/
+static int ftgmac100_next_tx_pointer(int pointer)
+{
+ return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
+}
+
+static void ftgmac100_tx_pointer_advance(struct ftgmac100 *priv)
+{
+ priv->tx_pointer = ftgmac100_next_tx_pointer(priv->tx_pointer);
+}
+
+static void ftgmac100_tx_clean_pointer_advance(struct ftgmac100 *priv)
+{
+ priv->tx_clean_pointer = ftgmac100_next_tx_pointer(priv->tx_clean_pointer);
+}
+
+static struct ftgmac100_txdes *ftgmac100_current_txdes(struct ftgmac100 *priv)
+{
+ return &priv->descs->txdes[priv->tx_pointer];
+}
+
+static struct ftgmac100_txdes *
+ftgmac100_current_clean_txdes(struct ftgmac100 *priv)
+{
+ return &priv->descs->txdes[priv->tx_clean_pointer];
+}
+
+static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ struct ftgmac100_txdes *txdes;
+ struct sk_buff *skb;
+ dma_addr_t map;
+
+ if (priv->tx_pending == 0)
+ return false;
+
+ txdes = ftgmac100_current_clean_txdes(priv);
+
+ if (ftgmac100_txdes_owned_by_dma(txdes))
+ return false;
+
+ skb = ftgmac100_txdes_get_skb(txdes);
+ map = ftgmac100_txdes_get_dma_addr(txdes);
+
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes += skb->len;
+
+ dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
+
+ dev_kfree_skb(skb);
+
+ ftgmac100_txdes_reset(txdes);
+
+ ftgmac100_tx_clean_pointer_advance(priv);
+
+ spin_lock(&priv->tx_lock);
+ priv->tx_pending--;
+ spin_unlock(&priv->tx_lock);
+ netif_wake_queue(netdev);
+
+ return true;
+}
+
+static void ftgmac100_tx_complete(struct ftgmac100 *priv)
+{
+ while (ftgmac100_tx_complete_packet(priv))
+ ;
+}
+
+static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb,
+ dma_addr_t map)
+{
+ struct net_device *netdev = priv->netdev;
+ struct ftgmac100_txdes *txdes;
+ unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+
+ txdes = ftgmac100_current_txdes(priv);
+ ftgmac100_tx_pointer_advance(priv);
+
+ /* setup TX descriptor */
+ ftgmac100_txdes_set_skb(txdes, skb);
+ ftgmac100_txdes_set_dma_addr(txdes, map);
+ ftgmac100_txdes_set_buffer_size(txdes, len);
+
+ ftgmac100_txdes_set_first_segment(txdes);
+ ftgmac100_txdes_set_last_segment(txdes);
+ ftgmac100_txdes_set_txint(txdes);
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ __be16 protocol = skb->protocol;
+
+ if (protocol == cpu_to_be16(ETH_P_IP)) {
+ u8 ip_proto = ip_hdr(skb)->protocol;
+
+ ftgmac100_txdes_set_ipcs(txdes);
+ if (ip_proto == IPPROTO_TCP)
+ ftgmac100_txdes_set_tcpcs(txdes);
+ else if (ip_proto == IPPROTO_UDP)
+ ftgmac100_txdes_set_udpcs(txdes);
+ }
+ }
+
+ spin_lock(&priv->tx_lock);
+ priv->tx_pending++;
+ if (priv->tx_pending == TX_QUEUE_ENTRIES)
+ netif_stop_queue(netdev);
+
+ /* start transmit */
+ ftgmac100_txdes_set_dma_own(txdes);
+ spin_unlock(&priv->tx_lock);
+
+ ftgmac100_txdma_normal_prio_start_polling(priv);
+
+ return NETDEV_TX_OK;
+}
+
+/******************************************************************************
+ * internal functions (buffer)
+ *****************************************************************************/
+static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
+ struct ftgmac100_rxdes *rxdes, gfp_t gfp)
+{
+ struct net_device *netdev = priv->netdev;
+ struct page *page;
+ dma_addr_t map;
+
+ page = alloc_page(gfp);
+ if (!page) {
+ if (net_ratelimit())
+ netdev_err(netdev, "failed to allocate rx page\n");
+ return -ENOMEM;
+ }
+
+ map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(priv->dev, map))) {
+ if (net_ratelimit())
+ netdev_err(netdev, "failed to map rx page\n");
+ __free_page(page);
+ return -ENOMEM;
+ }
+
+ ftgmac100_rxdes_set_page(rxdes, page);
+ ftgmac100_rxdes_set_dma_addr(rxdes, map);
+ ftgmac100_rxdes_set_dma_own(rxdes);
+ return 0;
+}
+
+static void ftgmac100_free_buffers(struct ftgmac100 *priv)
+{
+ int i;
+
+ for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+ struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+ struct page *page = ftgmac100_rxdes_get_page(rxdes);
+ dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes);
+
+ if (!page)
+ continue;
+
+ dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+ __free_page(page);
+ }
+
+ for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
+ struct ftgmac100_txdes *txdes = &priv->descs->txdes[i];
+ struct sk_buff *skb = ftgmac100_txdes_get_skb(txdes);
+ dma_addr_t map = ftgmac100_txdes_get_dma_addr(txdes);
+
+ if (!skb)
+ continue;
+
+ dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
+ dev_kfree_skb(skb);
+ }
+
+ dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs),
+ priv->descs, priv->descs_dma_addr);
+}
+
+static int ftgmac100_alloc_buffers(struct ftgmac100 *priv)
+{
+ int i;
+
+ priv->descs = dma_alloc_coherent(priv->dev,
+ sizeof(struct ftgmac100_descs),
+ &priv->descs_dma_addr, GFP_KERNEL);
+ if (!priv->descs)
+ return -ENOMEM;
+
+ memset(priv->descs, 0, sizeof(struct ftgmac100_descs));
+
+ /* initialize RX ring */
+ ftgmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
+
+ for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+ struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+
+ if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL))
+ goto err;
+ }
+
+ /* initialize TX ring */
+ ftgmac100_txdes_set_end_of_ring(&priv->descs->txdes[TX_QUEUE_ENTRIES - 1]);
+ return 0;
+
+err:
+ ftgmac100_free_buffers(priv);
+ return -ENOMEM;
+}
+
+/******************************************************************************
+ * internal functions (mdio)
+ *****************************************************************************/
+static void ftgmac100_adjust_link(struct net_device *netdev)
+{
+ struct ftgmac100 *priv = netdev_priv(netdev);
+ struct phy_device *phydev = priv->phydev;
+ int ier;
+
+ if (phydev->speed == priv->old_speed)
+ return;
+
+ priv->old_speed = phydev->speed;
+
+ ier = ioread32(priv->base + FTGMAC100_OFFSET_IER);
+
+ /* disable all interrupts */
+ iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
+
+ netif_stop_queue(netdev);
+ ftgmac100_stop_hw(priv);
+
+ netif_start_queue(netdev);
+ ftgmac100_init_hw(priv);
+ ftgmac100_start_hw(priv, phydev->speed);
+
+ /* re-enable interrupts */
+ iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER);
+}
+
+static int ftgmac100_mii_probe(struct ftgmac100 *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ struct phy_device *phydev = NULL;
+ int i;
+
+ /* search for connect PHY device */
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ struct phy_device *tmp = priv->mii_bus->phy_map[i];
+
+ if (tmp) {
+ phydev = tmp;
+ break;
+ }
+ }
+
+ /* now we are supposed to have a proper phydev, to attach to... */
+ if (!phydev) {
+ netdev_info(netdev, "%s: no PHY found\n", netdev->name);
+ return -ENODEV;
+ }
+
+ phydev = phy_connect(netdev, dev_name(&phydev->dev),
+ &ftgmac100_adjust_link, 0,
+ PHY_INTERFACE_MODE_GMII);
+
+ if (IS_ERR(phydev)) {
+ netdev_err(netdev, "%s: Could not attach to PHY\n", netdev->name);
+ return PTR_ERR(phydev);
+ }
+
+ priv->phydev = phydev;
+ return 0;
+}
+
+/******************************************************************************
+ * struct mii_bus functions
+ *****************************************************************************/
+static int ftgmac100_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
+{
+ struct net_device *netdev = bus->priv;
+ struct ftgmac100 *priv = netdev_priv(netdev);
+ unsigned int phycr;
+ int i;
+
+ phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
+
+ /* preserve MDC cycle threshold */
+ phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK;
+
+ phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) |
+ FTGMAC100_PHYCR_REGAD(regnum) |
+ FTGMAC100_PHYCR_MIIRD;
+
+ iowrite32(phycr, priv->base + FTGMAC100_OFFSET_PHYCR);
+
+ for (i = 0; i < 10; i++) {
+ phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
+
+ if ((phycr & FTGMAC100_PHYCR_MIIRD) == 0) {
+ int data;
+
+ data = ioread32(priv->base + FTGMAC100_OFFSET_PHYDATA);
+ return FTGMAC100_PHYDATA_MIIRDATA(data);
+ }
+
+ udelay(100);
+ }
+
+ netdev_err(netdev, "mdio read timed out\n");
+ return -EIO;
+}
+
+static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr,
+ int regnum, u16 value)
+{
+ struct net_device *netdev = bus->priv;
+ struct ftgmac100 *priv = netdev_priv(netdev);
+ unsigned int phycr;
+ int data;
+ int i;
+
+ phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
+
+ /* preserve MDC cycle threshold */
+ phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK;
+
+ phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) |
+ FTGMAC100_PHYCR_REGAD(regnum) |
+ FTGMAC100_PHYCR_MIIWR;
+
+ data = FTGMAC100_PHYDATA_MIIWDATA(value);
+
+ iowrite32(data, priv->base + FTGMAC100_OFFSET_PHYDATA);
+ iowrite32(phycr, priv->base + FTGMAC100_OFFSET_PHYCR);
+
+ for (i = 0; i < 10; i++) {
+ phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
+
+ if ((phycr & FTGMAC100_PHYCR_MIIWR) == 0)
+ return 0;
+
+ udelay(100);
+ }
+
+ netdev_err(netdev, "mdio write timed out\n");
+ return -EIO;
+}
+
+static int ftgmac100_mdiobus_reset(struct mii_bus *bus)
+{
+ return 0;
+}
+
+/******************************************************************************
+ * struct ethtool_ops functions
+ *****************************************************************************/
+static void ftgmac100_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, dev_name(&netdev->dev));
+}
+
+static int ftgmac100_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *cmd)
+{
+ struct ftgmac100 *priv = netdev_priv(netdev);
+
+ return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int ftgmac100_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *cmd)
+{
+ struct ftgmac100 *priv = netdev_priv(netdev);
+
+ return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static const struct ethtool_ops ftgmac100_ethtool_ops = {
+ .set_settings = ftgmac100_set_settings,
+ .get_settings = ftgmac100_get_settings,
+ .get_drvinfo = ftgmac100_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+
+/******************************************************************************
+ * interrupt handler
+ *****************************************************************************/
+static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
+{
+ struct net_device *netdev = dev_id;
+ struct ftgmac100 *priv = netdev_priv(netdev);
+
+ if (likely(netif_running(netdev))) {
+ /* Disable interrupts for polling */
+ iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
+ napi_schedule(&priv->napi);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/******************************************************************************
+ * struct napi_struct functions
+ *****************************************************************************/
+static int ftgmac100_poll(struct napi_struct *napi, int budget)
+{
+ struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi);
+ struct net_device *netdev = priv->netdev;
+ unsigned int status;
+ bool completed = true;
+ int rx = 0;
+
+ status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
+ iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
+
+ if (status & (FTGMAC100_INT_RPKT_BUF | FTGMAC100_INT_NO_RXBUF)) {
+ /*
+ * FTGMAC100_INT_RPKT_BUF:
+ * RX DMA has received packets into RX buffer successfully
+ *
+ * FTGMAC100_INT_NO_RXBUF:
+ * RX buffer unavailable
+ */
+ bool retry;
+
+ do {
+ retry = ftgmac100_rx_packet(priv, &rx);
+ } while (retry && rx < budget);
+
+ if (retry && rx == budget)
+ completed = false;
+ }
+
+ if (status & (FTGMAC100_INT_XPKT_ETH | FTGMAC100_INT_XPKT_LOST)) {
+ /*
+ * FTGMAC100_INT_XPKT_ETH:
+ * packet transmitted to ethernet successfully
+ *
+ * FTGMAC100_INT_XPKT_LOST:
+ * packet transmitted to ethernet lost due to late
+ * collision or excessive collision
+ */
+ ftgmac100_tx_complete(priv);
+ }
+
+ if (status & (FTGMAC100_INT_NO_RXBUF | FTGMAC100_INT_RPKT_LOST |
+ FTGMAC100_INT_AHB_ERR | FTGMAC100_INT_PHYSTS_CHG)) {
+ if (net_ratelimit())
+ netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status,
+ status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "",
+ status & FTGMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "",
+ status & FTGMAC100_INT_AHB_ERR ? "AHB_ERR " : "",
+ status & FTGMAC100_INT_PHYSTS_CHG ? "PHYSTS_CHG" : "");
+
+ if (status & FTGMAC100_INT_NO_RXBUF) {
+ /* RX buffer unavailable */
+ netdev->stats.rx_over_errors++;
+ }
+
+ if (status & FTGMAC100_INT_RPKT_LOST) {
+ /* received packet lost due to RX FIFO full */
+ netdev->stats.rx_fifo_errors++;
+ }
+ }
+
+ if (completed) {
+ napi_complete(napi);
+
+ /* enable all interrupts */
+ iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+ }
+
+ return rx;
+}
+
+/******************************************************************************
+ * struct net_device_ops functions
+ *****************************************************************************/
+static int ftgmac100_open(struct net_device *netdev)
+{
+ struct ftgmac100 *priv = netdev_priv(netdev);
+ int err;
+
+ err = ftgmac100_alloc_buffers(priv);
+ if (err) {
+ netdev_err(netdev, "failed to allocate buffers\n");
+ goto err_alloc;
+ }
+
+ err = request_irq(priv->irq, ftgmac100_interrupt, 0, netdev->name, netdev);
+ if (err) {
+ netdev_err(netdev, "failed to request irq %d\n", priv->irq);
+ goto err_irq;
+ }
+
+ priv->rx_pointer = 0;
+ priv->tx_clean_pointer = 0;
+ priv->tx_pointer = 0;
+ priv->tx_pending = 0;
+
+ err = ftgmac100_reset_hw(priv);
+ if (err)
+ goto err_hw;
+
+ ftgmac100_init_hw(priv);
+ ftgmac100_start_hw(priv, 10);
+
+ phy_start(priv->phydev);
+
+ napi_enable(&priv->napi);
+ netif_start_queue(netdev);
+
+ /* enable all interrupts */
+ iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+ return 0;
+
+err_hw:
+ free_irq(priv->irq, netdev);
+err_irq:
+ ftgmac100_free_buffers(priv);
+err_alloc:
+ return err;
+}
+
+static int ftgmac100_stop(struct net_device *netdev)
+{
+ struct ftgmac100 *priv = netdev_priv(netdev);
+
+ /* disable all interrupts */
+ iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
+
+ netif_stop_queue(netdev);
+ napi_disable(&priv->napi);
+ phy_stop(priv->phydev);
+
+ ftgmac100_stop_hw(priv);
+ free_irq(priv->irq, netdev);
+ ftgmac100_free_buffers(priv);
+
+ return 0;
+}
+
+static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct ftgmac100 *priv = netdev_priv(netdev);
+ dma_addr_t map;
+
+ if (unlikely(skb->len > MAX_PKT_SIZE)) {
+ if (net_ratelimit())
+ netdev_dbg(netdev, "tx packet too big\n");
+
+ netdev->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(priv->dev, map))) {
+ /* drop packet */
+ if (net_ratelimit())
+ netdev_err(netdev, "map socket buffer failed\n");
+
+ netdev->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ return ftgmac100_xmit(priv, skb, map);
+}
+
+/* optional */
+static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ struct ftgmac100 *priv = netdev_priv(netdev);
+
+ return phy_mii_ioctl(priv->phydev, ifr, cmd);
+}
+
+static const struct net_device_ops ftgmac100_netdev_ops = {
+ .ndo_open = ftgmac100_open,
+ .ndo_stop = ftgmac100_stop,
+ .ndo_start_xmit = ftgmac100_hard_start_xmit,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = ftgmac100_do_ioctl,
+};
+
+/******************************************************************************
+ * struct platform_driver functions
+ *****************************************************************************/
+static int ftgmac100_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int irq;
+ struct net_device *netdev;
+ struct ftgmac100 *priv;
+ int err;
+ int i;
+
+ if (!pdev)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ /* setup net_device */
+ netdev = alloc_etherdev(sizeof(*priv));
+ if (!netdev) {
+ err = -ENOMEM;
+ goto err_alloc_etherdev;
+ }
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+
+ SET_ETHTOOL_OPS(netdev, &ftgmac100_ethtool_ops);
+ netdev->netdev_ops = &ftgmac100_netdev_ops;
+ netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;
+
+ platform_set_drvdata(pdev, netdev);
+
+ /* setup private data */
+ priv = netdev_priv(netdev);
+ priv->netdev = netdev;
+ priv->dev = &pdev->dev;
+
+ spin_lock_init(&priv->tx_lock);
+
+ /* initialize NAPI */
+ netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64);
+
+ /* map io memory */
+ priv->res = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+ if (!priv->res) {
+ dev_err(&pdev->dev, "Could not reserve memory region\n");
+ err = -ENOMEM;
+ goto err_req_mem;
+ }
+
+ priv->base = ioremap(res->start, resource_size(res));
+ if (!priv->base) {
+ dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
+ err = -EIO;
+ goto err_ioremap;
+ }
+
+ priv->irq = irq;
+
+ /* initialize mdio bus */
+ priv->mii_bus = mdiobus_alloc();
+ if (!priv->mii_bus) {
+ err = -EIO;
+ goto err_alloc_mdiobus;
+ }
+
+ priv->mii_bus->name = "ftgmac100_mdio";
+ snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii");
+
+ priv->mii_bus->priv = netdev;
+ priv->mii_bus->read = ftgmac100_mdiobus_read;
+ priv->mii_bus->write = ftgmac100_mdiobus_write;
+ priv->mii_bus->reset = ftgmac100_mdiobus_reset;
+ priv->mii_bus->irq = priv->phy_irq;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ priv->mii_bus->irq[i] = PHY_POLL;
+
+ err = mdiobus_register(priv->mii_bus);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
+ goto err_register_mdiobus;
+ }
+
+ err = ftgmac100_mii_probe(priv);
+ if (err) {
+ dev_err(&pdev->dev, "MII Probe failed!\n");
+ goto err_mii_probe;
+ }
+
+ /* register network device */
+ err = register_netdev(netdev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register netdev\n");
+ goto err_register_netdev;
+ }
+
+ netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base);
+
+ if (!is_valid_ether_addr(netdev->dev_addr)) {
+ random_ether_addr(netdev->dev_addr);
+ netdev_info(netdev, "generated random MAC address %pM\n",
+ netdev->dev_addr);
+ }
+
+ return 0;
+
+err_register_netdev:
+ phy_disconnect(priv->phydev);
+err_mii_probe:
+ mdiobus_unregister(priv->mii_bus);
+err_register_mdiobus:
+ mdiobus_free(priv->mii_bus);
+err_alloc_mdiobus:
+ iounmap(priv->base);
+err_ioremap:
+ release_resource(priv->res);
+err_req_mem:
+ netif_napi_del(&priv->napi);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(netdev);
+err_alloc_etherdev:
+ return err;
+}
+
+static int __exit ftgmac100_remove(struct platform_device *pdev)
+{
+ struct net_device *netdev;
+ struct ftgmac100 *priv;
+
+ netdev = platform_get_drvdata(pdev);
+ priv = netdev_priv(netdev);
+
+ unregister_netdev(netdev);
+
+ phy_disconnect(priv->phydev);
+ mdiobus_unregister(priv->mii_bus);
+ mdiobus_free(priv->mii_bus);
+
+ iounmap(priv->base);
+ release_resource(priv->res);
+
+ netif_napi_del(&priv->napi);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(netdev);
+ return 0;
+}
+
+static struct platform_driver ftgmac100_driver = {
+ .probe = ftgmac100_probe,
+ .remove = __exit_p(ftgmac100_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/******************************************************************************
+ * initialization / finalization
+ *****************************************************************************/
+static int __init ftgmac100_init(void)
+{
+ pr_info("Loading version " DRV_VERSION " ...\n");
+ return platform_driver_register(&ftgmac100_driver);
+}
+
+static void __exit ftgmac100_exit(void)
+{
+ platform_driver_unregister(&ftgmac100_driver);
+}
+
+module_init(ftgmac100_init);
+module_exit(ftgmac100_exit);
+
+MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
+MODULE_DESCRIPTION("FTGMAC100 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/faraday/ftgmac100.h
new file mode 100644
index 000000000000..13408d448b05
--- /dev/null
+++ b/drivers/net/ethernet/faraday/ftgmac100.h
@@ -0,0 +1,246 @@
+/*
+ * Faraday FTGMAC100 Gigabit Ethernet
+ *
+ * (C) Copyright 2009-2011 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __FTGMAC100_H
+#define __FTGMAC100_H
+
+#define FTGMAC100_OFFSET_ISR 0x00
+#define FTGMAC100_OFFSET_IER 0x04
+#define FTGMAC100_OFFSET_MAC_MADR 0x08
+#define FTGMAC100_OFFSET_MAC_LADR 0x0c
+#define FTGMAC100_OFFSET_MAHT0 0x10
+#define FTGMAC100_OFFSET_MAHT1 0x14
+#define FTGMAC100_OFFSET_NPTXPD 0x18
+#define FTGMAC100_OFFSET_RXPD 0x1c
+#define FTGMAC100_OFFSET_NPTXR_BADR 0x20
+#define FTGMAC100_OFFSET_RXR_BADR 0x24
+#define FTGMAC100_OFFSET_HPTXPD 0x28
+#define FTGMAC100_OFFSET_HPTXR_BADR 0x2c
+#define FTGMAC100_OFFSET_ITC 0x30
+#define FTGMAC100_OFFSET_APTC 0x34
+#define FTGMAC100_OFFSET_DBLAC 0x38
+#define FTGMAC100_OFFSET_DMAFIFOS 0x3c
+#define FTGMAC100_OFFSET_REVR 0x40
+#define FTGMAC100_OFFSET_FEAR 0x44
+#define FTGMAC100_OFFSET_TPAFCR 0x48
+#define FTGMAC100_OFFSET_RBSR 0x4c
+#define FTGMAC100_OFFSET_MACCR 0x50
+#define FTGMAC100_OFFSET_MACSR 0x54
+#define FTGMAC100_OFFSET_TM 0x58
+#define FTGMAC100_OFFSET_PHYCR 0x60
+#define FTGMAC100_OFFSET_PHYDATA 0x64
+#define FTGMAC100_OFFSET_FCR 0x68
+#define FTGMAC100_OFFSET_BPR 0x6c
+#define FTGMAC100_OFFSET_WOLCR 0x70
+#define FTGMAC100_OFFSET_WOLSR 0x74
+#define FTGMAC100_OFFSET_WFCRC 0x78
+#define FTGMAC100_OFFSET_WFBM1 0x80
+#define FTGMAC100_OFFSET_WFBM2 0x84
+#define FTGMAC100_OFFSET_WFBM3 0x88
+#define FTGMAC100_OFFSET_WFBM4 0x8c
+#define FTGMAC100_OFFSET_NPTXR_PTR 0x90
+#define FTGMAC100_OFFSET_HPTXR_PTR 0x94
+#define FTGMAC100_OFFSET_RXR_PTR 0x98
+#define FTGMAC100_OFFSET_TX 0xa0
+#define FTGMAC100_OFFSET_TX_MCOL_SCOL 0xa4
+#define FTGMAC100_OFFSET_TX_ECOL_FAIL 0xa8
+#define FTGMAC100_OFFSET_TX_LCOL_UND 0xac
+#define FTGMAC100_OFFSET_RX 0xb0
+#define FTGMAC100_OFFSET_RX_BC 0xb4
+#define FTGMAC100_OFFSET_RX_MC 0xb8
+#define FTGMAC100_OFFSET_RX_PF_AEP 0xbc
+#define FTGMAC100_OFFSET_RX_RUNT 0xc0
+#define FTGMAC100_OFFSET_RX_CRCER_FTL 0xc4
+#define FTGMAC100_OFFSET_RX_COL_LOST 0xc8
+
+/*
+ * Interrupt status register & interrupt enable register
+ */
+#define FTGMAC100_INT_RPKT_BUF (1 << 0)
+#define FTGMAC100_INT_RPKT_FIFO (1 << 1)
+#define FTGMAC100_INT_NO_RXBUF (1 << 2)
+#define FTGMAC100_INT_RPKT_LOST (1 << 3)
+#define FTGMAC100_INT_XPKT_ETH (1 << 4)
+#define FTGMAC100_INT_XPKT_FIFO (1 << 5)
+#define FTGMAC100_INT_NO_NPTXBUF (1 << 6)
+#define FTGMAC100_INT_XPKT_LOST (1 << 7)
+#define FTGMAC100_INT_AHB_ERR (1 << 8)
+#define FTGMAC100_INT_PHYSTS_CHG (1 << 9)
+#define FTGMAC100_INT_NO_HPTXBUF (1 << 10)
+
+/*
+ * Interrupt timer control register
+ */
+#define FTGMAC100_ITC_RXINT_CNT(x) (((x) & 0xf) << 0)
+#define FTGMAC100_ITC_RXINT_THR(x) (((x) & 0x7) << 4)
+#define FTGMAC100_ITC_RXINT_TIME_SEL (1 << 7)
+#define FTGMAC100_ITC_TXINT_CNT(x) (((x) & 0xf) << 8)
+#define FTGMAC100_ITC_TXINT_THR(x) (((x) & 0x7) << 12)
+#define FTGMAC100_ITC_TXINT_TIME_SEL (1 << 15)
+
+/*
+ * Automatic polling timer control register
+ */
+#define FTGMAC100_APTC_RXPOLL_CNT(x) (((x) & 0xf) << 0)
+#define FTGMAC100_APTC_RXPOLL_TIME_SEL (1 << 4)
+#define FTGMAC100_APTC_TXPOLL_CNT(x) (((x) & 0xf) << 8)
+#define FTGMAC100_APTC_TXPOLL_TIME_SEL (1 << 12)
+
+/*
+ * DMA burst length and arbitration control register
+ */
+#define FTGMAC100_DBLAC_RXFIFO_LTHR(x) (((x) & 0x7) << 0)
+#define FTGMAC100_DBLAC_RXFIFO_HTHR(x) (((x) & 0x7) << 3)
+#define FTGMAC100_DBLAC_RX_THR_EN (1 << 6)
+#define FTGMAC100_DBLAC_RXBURST_SIZE(x) (((x) & 0x3) << 8)
+#define FTGMAC100_DBLAC_TXBURST_SIZE(x) (((x) & 0x3) << 10)
+#define FTGMAC100_DBLAC_RXDES_SIZE(x) (((x) & 0xf) << 12)
+#define FTGMAC100_DBLAC_TXDES_SIZE(x) (((x) & 0xf) << 16)
+#define FTGMAC100_DBLAC_IFG_CNT(x) (((x) & 0x7) << 20)
+#define FTGMAC100_DBLAC_IFG_INC (1 << 23)
+
+/*
+ * DMA FIFO status register
+ */
+#define FTGMAC100_DMAFIFOS_RXDMA1_SM(dmafifos) ((dmafifos) & 0xf)
+#define FTGMAC100_DMAFIFOS_RXDMA2_SM(dmafifos) (((dmafifos) >> 4) & 0xf)
+#define FTGMAC100_DMAFIFOS_RXDMA3_SM(dmafifos) (((dmafifos) >> 8) & 0x7)
+#define FTGMAC100_DMAFIFOS_TXDMA1_SM(dmafifos) (((dmafifos) >> 12) & 0xf)
+#define FTGMAC100_DMAFIFOS_TXDMA2_SM(dmafifos) (((dmafifos) >> 16) & 0x3)
+#define FTGMAC100_DMAFIFOS_TXDMA3_SM(dmafifos) (((dmafifos) >> 18) & 0xf)
+#define FTGMAC100_DMAFIFOS_RXFIFO_EMPTY (1 << 26)
+#define FTGMAC100_DMAFIFOS_TXFIFO_EMPTY (1 << 27)
+#define FTGMAC100_DMAFIFOS_RXDMA_GRANT (1 << 28)
+#define FTGMAC100_DMAFIFOS_TXDMA_GRANT (1 << 29)
+#define FTGMAC100_DMAFIFOS_RXDMA_REQ (1 << 30)
+#define FTGMAC100_DMAFIFOS_TXDMA_REQ (1 << 31)
+
+/*
+ * Receive buffer size register
+ */
+#define FTGMAC100_RBSR_SIZE(x) ((x) & 0x3fff)
+
+/*
+ * MAC control register
+ */
+#define FTGMAC100_MACCR_TXDMA_EN (1 << 0)
+#define FTGMAC100_MACCR_RXDMA_EN (1 << 1)
+#define FTGMAC100_MACCR_TXMAC_EN (1 << 2)
+#define FTGMAC100_MACCR_RXMAC_EN (1 << 3)
+#define FTGMAC100_MACCR_RM_VLAN (1 << 4)
+#define FTGMAC100_MACCR_HPTXR_EN (1 << 5)
+#define FTGMAC100_MACCR_LOOP_EN (1 << 6)
+#define FTGMAC100_MACCR_ENRX_IN_HALFTX (1 << 7)
+#define FTGMAC100_MACCR_FULLDUP (1 << 8)
+#define FTGMAC100_MACCR_GIGA_MODE (1 << 9)
+#define FTGMAC100_MACCR_CRC_APD (1 << 10)
+#define FTGMAC100_MACCR_RX_RUNT (1 << 12)
+#define FTGMAC100_MACCR_JUMBO_LF (1 << 13)
+#define FTGMAC100_MACCR_RX_ALL (1 << 14)
+#define FTGMAC100_MACCR_HT_MULTI_EN (1 << 15)
+#define FTGMAC100_MACCR_RX_MULTIPKT (1 << 16)
+#define FTGMAC100_MACCR_RX_BROADPKT (1 << 17)
+#define FTGMAC100_MACCR_DISCARD_CRCERR (1 << 18)
+#define FTGMAC100_MACCR_FAST_MODE (1 << 19)
+#define FTGMAC100_MACCR_SW_RST (1 << 31)
+
+/*
+ * PHY control register
+ */
+#define FTGMAC100_PHYCR_MDC_CYCTHR_MASK 0x3f
+#define FTGMAC100_PHYCR_MDC_CYCTHR(x) ((x) & 0x3f)
+#define FTGMAC100_PHYCR_PHYAD(x) (((x) & 0x1f) << 16)
+#define FTGMAC100_PHYCR_REGAD(x) (((x) & 0x1f) << 21)
+#define FTGMAC100_PHYCR_MIIRD (1 << 26)
+#define FTGMAC100_PHYCR_MIIWR (1 << 27)
+
+/*
+ * PHY data register
+ */
+#define FTGMAC100_PHYDATA_MIIWDATA(x) ((x) & 0xffff)
+#define FTGMAC100_PHYDATA_MIIRDATA(phydata) (((phydata) >> 16) & 0xffff)
+
+/*
+ * Transmit descriptor, aligned to 16 bytes
+ */
+struct ftgmac100_txdes {
+ unsigned int txdes0;
+ unsigned int txdes1;
+ unsigned int txdes2; /* not used by HW */
+ unsigned int txdes3; /* TXBUF_BADR */
+} __attribute__ ((aligned(16)));
+
+#define FTGMAC100_TXDES0_TXBUF_SIZE(x) ((x) & 0x3fff)
+#define FTGMAC100_TXDES0_EDOTR (1 << 15)
+#define FTGMAC100_TXDES0_CRC_ERR (1 << 19)
+#define FTGMAC100_TXDES0_LTS (1 << 28)
+#define FTGMAC100_TXDES0_FTS (1 << 29)
+#define FTGMAC100_TXDES0_TXDMA_OWN (1 << 31)
+
+#define FTGMAC100_TXDES1_VLANTAG_CI(x) ((x) & 0xffff)
+#define FTGMAC100_TXDES1_INS_VLANTAG (1 << 16)
+#define FTGMAC100_TXDES1_TCP_CHKSUM (1 << 17)
+#define FTGMAC100_TXDES1_UDP_CHKSUM (1 << 18)
+#define FTGMAC100_TXDES1_IP_CHKSUM (1 << 19)
+#define FTGMAC100_TXDES1_LLC (1 << 22)
+#define FTGMAC100_TXDES1_TX2FIC (1 << 30)
+#define FTGMAC100_TXDES1_TXIC (1 << 31)
+
+/*
+ * Receive descriptor, aligned to 16 bytes
+ */
+struct ftgmac100_rxdes {
+ unsigned int rxdes0;
+ unsigned int rxdes1;
+ unsigned int rxdes2; /* not used by HW */
+ unsigned int rxdes3; /* RXBUF_BADR */
+} __attribute__ ((aligned(16)));
+
+#define FTGMAC100_RXDES0_VDBC 0x3fff
+#define FTGMAC100_RXDES0_EDORR (1 << 15)
+#define FTGMAC100_RXDES0_MULTICAST (1 << 16)
+#define FTGMAC100_RXDES0_BROADCAST (1 << 17)
+#define FTGMAC100_RXDES0_RX_ERR (1 << 18)
+#define FTGMAC100_RXDES0_CRC_ERR (1 << 19)
+#define FTGMAC100_RXDES0_FTL (1 << 20)
+#define FTGMAC100_RXDES0_RUNT (1 << 21)
+#define FTGMAC100_RXDES0_RX_ODD_NB (1 << 22)
+#define FTGMAC100_RXDES0_FIFO_FULL (1 << 23)
+#define FTGMAC100_RXDES0_PAUSE_OPCODE (1 << 24)
+#define FTGMAC100_RXDES0_PAUSE_FRAME (1 << 25)
+#define FTGMAC100_RXDES0_LRS (1 << 28)
+#define FTGMAC100_RXDES0_FRS (1 << 29)
+#define FTGMAC100_RXDES0_RXPKT_RDY (1 << 31)
+
+#define FTGMAC100_RXDES1_VLANTAG_CI 0xffff
+#define FTGMAC100_RXDES1_PROT_MASK (0x3 << 20)
+#define FTGMAC100_RXDES1_PROT_NONIP (0x0 << 20)
+#define FTGMAC100_RXDES1_PROT_IP (0x1 << 20)
+#define FTGMAC100_RXDES1_PROT_TCPIP (0x2 << 20)
+#define FTGMAC100_RXDES1_PROT_UDPIP (0x3 << 20)
+#define FTGMAC100_RXDES1_LLC (1 << 22)
+#define FTGMAC100_RXDES1_DF (1 << 23)
+#define FTGMAC100_RXDES1_VLANTAG_AVAIL (1 << 24)
+#define FTGMAC100_RXDES1_TCP_CHKSUM_ERR (1 << 25)
+#define FTGMAC100_RXDES1_UDP_CHKSUM_ERR (1 << 26)
+#define FTGMAC100_RXDES1_IP_CHKSUM_ERR (1 << 27)
+
+#endif /* __FTGMAC100_H */
diff --git a/drivers/net/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c
index 9bd7746cbfcf..a127cb2476c7 100644
--- a/drivers/net/ftmac100.c
+++ b/drivers/net/ethernet/faraday/ftmac100.c
@@ -439,7 +439,10 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
skb_fill_page_desc(skb, 0, page, 0, length);
skb->len += length;
skb->data_len += length;
- skb->truesize += length;
+
+ /* page might be freed in __pskb_pull_tail() */
+ if (length > 64)
+ skb->truesize += PAGE_SIZE;
__pskb_pull_tail(skb, min(length, 64));
ftmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);
diff --git a/drivers/net/ftmac100.h b/drivers/net/ethernet/faraday/ftmac100.h
index 46a0c47b1ee1..46a0c47b1ee1 100644
--- a/drivers/net/ftmac100.h
+++ b/drivers/net/ethernet/faraday/ftmac100.h
diff --git a/drivers/net/fealnx.c b/drivers/net/ethernet/fealnx.c
index dd54abe2f710..61d2bddec1fa 100644
--- a/drivers/net/fealnx.c
+++ b/drivers/net/ethernet/fealnx.c
@@ -469,7 +469,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_stop = netdev_close,
.ndo_start_xmit = start_tx,
.ndo_get_stats = get_stats,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_do_ioctl = mii_ioctl,
.ndo_tx_timeout = fealnx_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
@@ -566,7 +566,7 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev,
err = -ENOMEM;
goto err_out_free_dev;
}
- np->rx_ring = (struct fealnx_desc *)ring_space;
+ np->rx_ring = ring_space;
np->rx_ring_dma = ring_dma;
ring_space = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &ring_dma);
@@ -574,7 +574,7 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev,
err = -ENOMEM;
goto err_out_free_rx;
}
- np->tx_ring = (struct fealnx_desc *)ring_space;
+ np->tx_ring = ring_space;
np->tx_ring_dma = ring_dma;
/* find the connected MII xcvrs */
diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig
new file mode 100644
index 000000000000..c520cfd3b298
--- /dev/null
+++ b/drivers/net/ethernet/freescale/Kconfig
@@ -0,0 +1,87 @@
+#
+# Freescale device configuration
+#
+
+config NET_VENDOR_FREESCALE
+ bool "Freescale devices"
+ default y
+ depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \
+ M523x || M527x || M5272 || M528x || M520x || M532x || \
+ ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Freescale devices. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_VENDOR_FREESCALE
+
+config FEC
+ bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)"
+ depends on (M523x || M527x || M5272 || M528x || M520x || M532x || \
+ ARCH_MXC || ARCH_MXS)
+ select PHYLIB
+ ---help---
+ Say Y here if you want to use the built-in 10/100 Fast ethernet
+ controller on some Motorola ColdFire and Freescale i.MX processors.
+
+config FEC_MPC52xx
+ tristate "FEC MPC52xx driver"
+ depends on PPC_MPC52xx && PPC_BESTCOMM
+ select CRC32
+ select PHYLIB
+ select PPC_BESTCOMM_FEC
+ ---help---
+ This option enables support for the MPC5200's on-chip
+ Fast Ethernet Controller
+ If compiled as module, it will be called fec_mpc52xx.
+
+config FEC_MPC52xx_MDIO
+ bool "FEC MPC52xx MDIO bus driver"
+ depends on FEC_MPC52xx
+ default y
+ ---help---
+ The MPC5200's FEC can connect to the Ethernet either with
+ an external MII PHY chip or 10 Mbps 7-wire interface
+ (Motorola? industry standard).
+ If your board uses an external PHY connected to FEC, enable this.
+ If not sure, enable.
+ If compiled as module, it will be called fec_mpc52xx_phy.
+
+source "drivers/net/ethernet/freescale/fs_enet/Kconfig"
+
+config FSL_PQ_MDIO
+ tristate "Freescale PQ MDIO"
+ depends on FSL_SOC
+ select PHYLIB
+ ---help---
+ This driver supports the MDIO bus used by the gianfar and UCC drivers.
+
+config UCC_GETH
+ tristate "Freescale QE Gigabit Ethernet"
+ depends on QUICC_ENGINE
+ select FSL_PQ_MDIO
+ select PHYLIB
+ ---help---
+ This driver supports the Gigabit Ethernet mode of the QUICC Engine,
+ which is available on some Freescale SOCs.
+
+config UGETH_TX_ON_DEMAND
+ bool "Transmit on Demand support"
+ depends on UCC_GETH
+
+config GIANFAR
+ tristate "Gianfar Ethernet"
+ depends on FSL_SOC
+ select FSL_PQ_MDIO
+ select PHYLIB
+ select CRC32
+ ---help---
+ This driver supports the Gigabit TSEC on the MPC83xx, MPC85xx,
+ and MPC86xx family of chips, and the FEC on the 8540.
+
+endif # NET_VENDOR_FREESCALE
diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile
new file mode 100644
index 000000000000..1752488c9ee5
--- /dev/null
+++ b/drivers/net/ethernet/freescale/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the Freescale network device drivers.
+#
+
+obj-$(CONFIG_FEC) += fec.o
+obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
+ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
+ obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
+endif
+obj-$(CONFIG_FS_ENET) += fs_enet/
+obj-$(CONFIG_FSL_PQ_MDIO) += fsl_pq_mdio.o
+obj-$(CONFIG_GIANFAR) += gianfar_driver.o
+obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o
+gianfar_driver-objs := gianfar.o \
+ gianfar_ethtool.o \
+ gianfar_sysfs.o
+obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o
+ucc_geth_driver-objs := ucc_geth.o ucc_geth_ethtool.o
diff --git a/drivers/net/fec.c b/drivers/net/ethernet/freescale/fec.c
index 885d8baff7d5..1124ce0a1594 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/ethernet/freescale/fec.c
@@ -18,7 +18,7 @@
* Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
* Copyright (c) 2004-2006 Macq Electronique SA.
*
- * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
*/
#include <linux/module.h>
@@ -44,6 +44,10 @@
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/fec.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_net.h>
#include <asm/cacheflush.h>
@@ -66,17 +70,49 @@
#define FEC_QUIRK_ENET_MAC (1 << 0)
/* Controller needs driver to swap frame */
#define FEC_QUIRK_SWAP_FRAME (1 << 1)
+/* Controller uses gasket */
+#define FEC_QUIRK_USE_GASKET (1 << 2)
+/* Controller has GBIT support */
+#define FEC_QUIRK_HAS_GBIT (1 << 3)
static struct platform_device_id fec_devtype[] = {
{
+ /* keep it for coldfire */
.name = DRIVER_NAME,
.driver_data = 0,
}, {
+ .name = "imx25-fec",
+ .driver_data = FEC_QUIRK_USE_GASKET,
+ }, {
+ .name = "imx27-fec",
+ .driver_data = 0,
+ }, {
.name = "imx28-fec",
.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
- },
- { }
+ }, {
+ .name = "imx6q-fec",
+ .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, fec_devtype);
+
+enum imx_fec_type {
+ IMX25_FEC = 1, /* runs on i.mx25/50/53 */
+ IMX27_FEC, /* runs on i.mx27/35/51 */
+ IMX28_FEC,
+ IMX6Q_FEC,
+};
+
+static const struct of_device_id fec_dt_ids[] = {
+ { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
+ { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
+ { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
+ { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
+ { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, fec_dt_ids);
static unsigned char macaddr[ETH_ALEN];
module_param_array(macaddr, byte, NULL, 0);
@@ -141,6 +177,8 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
#define PKT_MINBUF_SIZE 64
#define PKT_MAXBLR_SIZE 1520
+/* This device has up to three irqs on some platforms */
+#define FEC_IRQ_NUM 3
/*
* The 5270/5271/5280/5282/532x RX control register also contains maximum frame
@@ -204,6 +242,7 @@ struct fec_enet_private {
int link;
int full_duplex;
struct completion mdio_done;
+ int irq[FEC_IRQ_NUM];
};
/* FEC MII MMFR bits definition */
@@ -324,6 +363,8 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
fep->cur_tx = bdp;
+ skb_tx_timestamp(skb);
+
spin_unlock_irqrestore(&fep->hw_lock, flags);
return NETDEV_TX_OK;
@@ -342,6 +383,7 @@ fec_restart(struct net_device *ndev, int duplex)
int i;
u32 temp_mac[2];
u32 rcntl = OPT_FRAME_SIZE | 0x04;
+ u32 ecntl = 0x2; /* ETHEREN */
/* Whack a reset. We should wait for this. */
writel(1, fep->hwp + FEC_ECNTRL);
@@ -411,21 +453,26 @@ fec_restart(struct net_device *ndev, int duplex)
/* Enable flow control and length check */
rcntl |= 0x40000000 | 0x00000020;
- /* MII or RMII */
- if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
+ /* RGMII, RMII or MII */
+ if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII)
+ rcntl |= (1 << 6);
+ else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
rcntl |= (1 << 8);
else
rcntl &= ~(1 << 8);
- /* 10M or 100M */
- if (fep->phy_dev && fep->phy_dev->speed == SPEED_100)
- rcntl &= ~(1 << 9);
- else
- rcntl |= (1 << 9);
-
+ /* 1G, 100M or 10M */
+ if (fep->phy_dev) {
+ if (fep->phy_dev->speed == SPEED_1000)
+ ecntl |= (1 << 5);
+ else if (fep->phy_dev->speed == SPEED_100)
+ rcntl &= ~(1 << 9);
+ else
+ rcntl |= (1 << 9);
+ }
} else {
#ifdef FEC_MIIGSK_ENR
- if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) {
+ if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
/* disable the gasket and wait */
writel(0, fep->hwp + FEC_MIIGSK_ENR);
while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
@@ -434,8 +481,11 @@ fec_restart(struct net_device *ndev, int duplex)
/*
* configure the gasket:
* RMII, 50 MHz, no loopback, no echo
+ * MII, 25 MHz, no loopback, no echo
*/
- writel(1, fep->hwp + FEC_MIIGSK_CFGR);
+ writel((fep->phy_interface == PHY_INTERFACE_MODE_RMII) ?
+ 1 : 0, fep->hwp + FEC_MIIGSK_CFGR);
+
/* re-enable the gasket */
writel(2, fep->hwp + FEC_MIIGSK_ENR);
@@ -444,8 +494,15 @@ fec_restart(struct net_device *ndev, int duplex)
}
writel(rcntl, fep->hwp + FEC_R_CNTRL);
+ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+ /* enable ENET endian swap */
+ ecntl |= (1 << 8);
+ /* enable ENET store and forward mode */
+ writel(1 << 8, fep->hwp + FEC_X_WMRK);
+ }
+
/* And last, enable the transmit and receive processing */
- writel(2, fep->hwp + FEC_ECNTRL);
+ writel(ecntl, fep->hwp + FEC_ECNTRL);
writel(0, fep->hwp + FEC_R_DES_ACTIVE);
/* Enable interrupts we wish to service */
@@ -456,6 +513,8 @@ static void
fec_stop(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
+ const struct platform_device_id *id_entry =
+ platform_get_device_id(fep->pdev);
/* We cannot expect a graceful transmit stop without link !!! */
if (fep->link) {
@@ -470,6 +529,10 @@ fec_stop(struct net_device *ndev)
udelay(10);
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+
+ /* We have to keep ENET enabled to have MII interrupt stay working */
+ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
+ writel(2, fep->hwp + FEC_ECNTRL);
}
@@ -650,7 +713,8 @@ fec_enet_rx(struct net_device *ndev)
skb_put(skb, pkt_len - 4); /* Make room */
skb_copy_to_linear_data(skb, data, pkt_len - 4);
skb->protocol = eth_type_trans(skb, ndev);
- netif_rx(skb);
+ if (!skb_defer_rx_timestamp(skb))
+ netif_rx(skb);
}
bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
@@ -731,8 +795,22 @@ static void __inline__ fec_get_mac(struct net_device *ndev)
*/
iap = macaddr;
+#ifdef CONFIG_OF
+ /*
+ * 2) from device tree data
+ */
+ if (!is_valid_ether_addr(iap)) {
+ struct device_node *np = fep->pdev->dev.of_node;
+ if (np) {
+ const char *mac = of_get_mac_address(np);
+ if (mac)
+ iap = (unsigned char *) mac;
+ }
+ }
+#endif
+
/*
- * 2) from flash or fuse (via platform data)
+ * 3) from flash or fuse (via platform data)
*/
if (!is_valid_ether_addr(iap)) {
#ifdef CONFIG_M5272
@@ -745,7 +823,7 @@ static void __inline__ fec_get_mac(struct net_device *ndev)
}
/*
- * 3) FEC mac registers set by bootloader
+ * 4) FEC mac registers set by bootloader
*/
if (!is_valid_ether_addr(iap)) {
*((unsigned long *) &tmpaddr[0]) =
@@ -869,6 +947,8 @@ static int fec_enet_mdio_reset(struct mii_bus *bus)
static int fec_enet_mii_probe(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
+ const struct platform_device_id *id_entry =
+ platform_get_device_id(fep->pdev);
struct phy_device *phy_dev = NULL;
char mdio_bus_id[MII_BUS_ID_SIZE];
char phy_name[MII_BUS_ID_SIZE + 3];
@@ -900,14 +980,18 @@ static int fec_enet_mii_probe(struct net_device *ndev)
snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id);
phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 0,
- PHY_INTERFACE_MODE_MII);
+ fep->phy_interface);
if (IS_ERR(phy_dev)) {
printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name);
return PTR_ERR(phy_dev);
}
/* mask with MAC supported features */
- phy_dev->supported &= PHY_BASIC_FEATURES;
+ if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT)
+ phy_dev->supported &= PHY_GBIT_FEATURES;
+ else
+ phy_dev->supported &= PHY_BASIC_FEATURES;
+
phy_dev->advertising = phy_dev->supported;
fep->phy_dev = phy_dev;
@@ -947,7 +1031,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
* mdio interface in board design, and need to be configured by
* fec0 mii_bus.
*/
- if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id) {
+ if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id > 0) {
/* fec1 uses fec0 mii_bus */
fep->mii_bus = fec0_mii_bus;
return 0;
@@ -957,8 +1041,16 @@ static int fec_enet_mii_init(struct platform_device *pdev)
/*
* Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
+ *
+ * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
+ * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28
+ * Reference Manual has an error on this, and gets fixed on i.MX6Q
+ * document.
*/
- fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000) << 1;
+ fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000);
+ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
+ fep->phy_speed--;
+ fep->phy_speed <<= 1;
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
fep->mii_bus = mdiobus_alloc();
@@ -1224,10 +1316,6 @@ static void set_multicast_list(struct net_device *ndev)
writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
netdev_for_each_mc_addr(ha, ndev) {
- /* Only support group multicast for now */
- if (!(ha->addr[0] & 1))
- continue;
-
/* calculate crc32 value of mac address */
crc = 0xffffffff;
@@ -1276,16 +1364,42 @@ fec_set_mac_address(struct net_device *ndev, void *p)
return 0;
}
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * fec_poll_controller: FEC Poll controller function
+ * @dev: The FEC network adapter
+ *
+ * Polled functionality used by netconsole and others in non interrupt mode
+ *
+ */
+void fec_poll_controller(struct net_device *dev)
+{
+ int i;
+ struct fec_enet_private *fep = netdev_priv(dev);
+
+ for (i = 0; i < FEC_IRQ_NUM; i++) {
+ if (fep->irq[i] > 0) {
+ disable_irq(fep->irq[i]);
+ fec_enet_interrupt(fep->irq[i], dev);
+ enable_irq(fep->irq[i]);
+ }
+ }
+}
+#endif
+
static const struct net_device_ops fec_netdev_ops = {
.ndo_open = fec_enet_open,
.ndo_stop = fec_enet_close,
.ndo_start_xmit = fec_enet_start_xmit,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_tx_timeout = fec_timeout,
.ndo_set_mac_address = fec_set_mac_address,
.ndo_do_ioctl = fec_enet_ioctl,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = fec_poll_controller,
+#endif
};
/*
@@ -1355,6 +1469,49 @@ static int fec_enet_init(struct net_device *ndev)
return 0;
}
+#ifdef CONFIG_OF
+static int __devinit fec_get_phy_mode_dt(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+
+ if (np)
+ return of_get_phy_mode(np);
+
+ return -ENODEV;
+}
+
+static void __devinit fec_reset_phy(struct platform_device *pdev)
+{
+ int err, phy_reset;
+ struct device_node *np = pdev->dev.of_node;
+
+ if (!np)
+ return;
+
+ phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
+ err = gpio_request_one(phy_reset, GPIOF_OUT_INIT_LOW, "phy-reset");
+ if (err) {
+ pr_debug("FEC: failed to get gpio phy-reset: %d\n", err);
+ return;
+ }
+ msleep(1);
+ gpio_set_value(phy_reset, 1);
+}
+#else /* CONFIG_OF */
+static inline int fec_get_phy_mode_dt(struct platform_device *pdev)
+{
+ return -ENODEV;
+}
+
+static inline void fec_reset_phy(struct platform_device *pdev)
+{
+ /*
+ * In case of platform probe, the reset has been done
+ * by machine code.
+ */
+}
+#endif /* CONFIG_OF */
+
static int __devinit
fec_probe(struct platform_device *pdev)
{
@@ -1363,6 +1520,11 @@ fec_probe(struct platform_device *pdev)
struct net_device *ndev;
int i, irq, ret = 0;
struct resource *r;
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(fec_dt_ids, &pdev->dev);
+ if (of_id)
+ pdev->id_entry = of_id->data;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
@@ -1394,12 +1556,20 @@ fec_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ndev);
- pdata = pdev->dev.platform_data;
- if (pdata)
- fep->phy_interface = pdata->phy;
+ ret = fec_get_phy_mode_dt(pdev);
+ if (ret < 0) {
+ pdata = pdev->dev.platform_data;
+ if (pdata)
+ fep->phy_interface = pdata->phy;
+ else
+ fep->phy_interface = PHY_INTERFACE_MODE_MII;
+ } else {
+ fep->phy_interface = ret;
+ }
+
+ fec_reset_phy(pdev);
- /* This device has up to three irqs on some platforms */
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < FEC_IRQ_NUM; i++) {
irq = platform_get_irq(pdev, i);
if (i && irq < 0)
break;
@@ -1444,7 +1614,7 @@ failed_init:
clk_disable(fep->clk);
clk_put(fep->clk);
failed_clk:
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < FEC_IRQ_NUM; i++) {
irq = platform_get_irq(pdev, i);
if (irq > 0)
free_irq(irq, ndev);
@@ -1531,6 +1701,7 @@ static struct platform_driver fec_driver = {
#ifdef CONFIG_PM
.pm = &fec_pm_ops,
#endif
+ .of_match_table = fec_dt_ids,
},
.id_table = fec_devtype,
.probe = fec_probe,
diff --git a/drivers/net/fec.h b/drivers/net/ethernet/freescale/fec.h
index 8b2c6d797e6d..8b2c6d797e6d 100644
--- a/drivers/net/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c
index 9f81b1ac130e..30745b56fe5d 100644
--- a/drivers/net/fec_mpc52xx.c
+++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c
@@ -14,6 +14,7 @@
*
*/
+#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -22,6 +23,7 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/crc32.h>
#include <linux/hardirq.h>
#include <linux/delay.h>
@@ -335,6 +337,7 @@ static int mpc52xx_fec_start_xmit(struct sk_buff *skb, struct net_device *dev)
bd->skb_pa = dma_map_single(dev->dev.parent, skb->data, skb->len,
DMA_TO_DEVICE);
+ skb_tx_timestamp(skb);
bcom_submit_next_buffer(priv->tx_dmatsk, skb);
spin_unlock_irqrestore(&priv->lock, flags);
@@ -434,7 +437,8 @@ static irqreturn_t mpc52xx_fec_rx_interrupt(int irq, void *dev_id)
length = status & BCOM_FEC_RX_BD_LEN_MASK;
skb_put(rskb, length - 4); /* length without CRC32 */
rskb->protocol = eth_type_trans(rskb, dev);
- netif_rx(rskb);
+ if (!skb_defer_rx_timestamp(skb))
+ netif_rx(rskb);
spin_lock(&priv->lock);
}
@@ -824,7 +828,7 @@ static const struct net_device_ops mpc52xx_fec_netdev_ops = {
.ndo_open = mpc52xx_fec_open,
.ndo_stop = mpc52xx_fec_close,
.ndo_start_xmit = mpc52xx_fec_start_xmit,
- .ndo_set_multicast_list = mpc52xx_fec_set_multicast_list,
+ .ndo_set_rx_mode = mpc52xx_fec_set_multicast_list,
.ndo_set_mac_address = mpc52xx_fec_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = mpc52xx_fec_ioctl,
@@ -867,10 +871,11 @@ static int __devinit mpc52xx_fec_probe(struct platform_device *op)
"Error while parsing device node resource\n" );
goto err_netdev;
}
- if ((mem.end - mem.start + 1) < sizeof(struct mpc52xx_fec)) {
+ if (resource_size(&mem) < sizeof(struct mpc52xx_fec)) {
printk(KERN_ERR DRIVER_NAME
- " - invalid resource size (%lx < %x), check mpc52xx_devices.c\n",
- (unsigned long)(mem.end - mem.start + 1), sizeof(struct mpc52xx_fec));
+ " - invalid resource size (%lx < %x), check mpc52xx_devices.c\n",
+ (unsigned long)resource_size(&mem),
+ sizeof(struct mpc52xx_fec));
rv = -EINVAL;
goto err_netdev;
}
diff --git a/drivers/net/fec_mpc52xx.h b/drivers/net/ethernet/freescale/fec_mpc52xx.h
index 41d2dffde55b..41d2dffde55b 100644
--- a/drivers/net/fec_mpc52xx.h
+++ b/drivers/net/ethernet/freescale/fec_mpc52xx.h
diff --git a/drivers/net/fec_mpc52xx_phy.c b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c
index 360a578c2bb7..360a578c2bb7 100644
--- a/drivers/net/fec_mpc52xx_phy.c
+++ b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c
diff --git a/drivers/net/fs_enet/Kconfig b/drivers/net/ethernet/freescale/fs_enet/Kconfig
index fc073b5a38c7..268414d9f2cb 100644
--- a/drivers/net/fs_enet/Kconfig
+++ b/drivers/net/ethernet/freescale/fs_enet/Kconfig
@@ -1,6 +1,7 @@
config FS_ENET
tristate "Freescale Ethernet Driver"
- depends on CPM1 || CPM2 || PPC_MPC512x
+ depends on NET_VENDOR_FREESCALE && (CPM1 || CPM2 || PPC_MPC512x)
+ select NET_CORE
select MII
select PHYLIB
diff --git a/drivers/net/fs_enet/Makefile b/drivers/net/ethernet/freescale/fs_enet/Makefile
index d4a305ee3455..d4a305ee3455 100644
--- a/drivers/net/fs_enet/Makefile
+++ b/drivers/net/ethernet/freescale/fs_enet/Makefile
diff --git a/drivers/net/fs_enet/fec.h b/drivers/net/ethernet/freescale/fs_enet/fec.h
index e980527e2b99..e980527e2b99 100644
--- a/drivers/net/fs_enet/fec.h
+++ b/drivers/net/ethernet/freescale/fs_enet/fec.h
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
index 21abb5c01a56..5bf5471f06ff 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
@@ -697,6 +697,8 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
sc |= BD_ENET_TX_PAD;
CBDS_SC(bdp, sc);
+ skb_tx_timestamp(skb);
+
(*fep->ops->tx_kickstart)(dev);
spin_unlock_irqrestore(&fep->tx_lock, flags);
@@ -986,7 +988,7 @@ static const struct net_device_ops fs_enet_netdev_ops = {
.ndo_get_stats = fs_enet_get_stats,
.ndo_start_xmit = fs_enet_start_xmit,
.ndo_tx_timeout = fs_timeout,
- .ndo_set_multicast_list = fs_set_multicast_list,
+ .ndo_set_rx_mode = fs_set_multicast_list,
.ndo_do_ioctl = fs_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/fs_enet/fs_enet.h b/drivers/net/ethernet/freescale/fs_enet/fs_enet.h
index 1ece4b1a689e..1ece4b1a689e 100644
--- a/drivers/net/fs_enet/fs_enet.h
+++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet.h
diff --git a/drivers/net/fs_enet/mac-fcc.c b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c
index 7583a9572bcc..7583a9572bcc 100644
--- a/drivers/net/fs_enet/mac-fcc.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c
diff --git a/drivers/net/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c
index b9fbc83d64a7..b9fbc83d64a7 100644
--- a/drivers/net/fs_enet/mac-fec.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c
diff --git a/drivers/net/fs_enet/mac-scc.c b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c
index 22a02a767069..22a02a767069 100644
--- a/drivers/net/fs_enet/mac-scc.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c
diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
index ad2975440719..b09270b5d0a5 100644
--- a/drivers/net/fs_enet/mii-bitbang.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
@@ -120,7 +120,7 @@ static int __devinit fs_mii_bitbang_init(struct mii_bus *bus,
if (ret)
return ret;
- if (res.end - res.start < 13)
+ if (resource_size(&res) <= 13)
return -ENODEV;
/* This should really encode the pin number as well, but all
@@ -139,7 +139,7 @@ static int __devinit fs_mii_bitbang_init(struct mii_bus *bus,
return -ENODEV;
mdc_pin = *data;
- bitbang->dir = ioremap(res.start, res.end - res.start + 1);
+ bitbang->dir = ioremap(res.start, resource_size(&res));
if (!bitbang->dir)
return -ENOMEM;
diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
index 6a2e150e75bb..e0e9d6c35d83 100644
--- a/drivers/net/fs_enet/mii-fec.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
@@ -136,7 +136,7 @@ static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev)
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", res.start);
- fec->fecp = ioremap(res.start, res.end - res.start + 1);
+ fec->fecp = ioremap(res.start, resource_size(&res));
if (!fec->fecp)
goto out_fec;
diff --git a/drivers/net/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
index 52f4e8ad48e7..52f4e8ad48e7 100644
--- a/drivers/net/fsl_pq_mdio.c
+++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
diff --git a/drivers/net/fsl_pq_mdio.h b/drivers/net/ethernet/freescale/fsl_pq_mdio.h
index bd17a2a0139b..bd17a2a0139b 100644
--- a/drivers/net/fsl_pq_mdio.h
+++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.h
diff --git a/drivers/net/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 2dfcc8047847..83199fd0d62b 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -62,6 +62,9 @@
* The driver then cleans up the buffer.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define DEBUG
+
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
@@ -137,8 +140,6 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue);
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
int amount_pull);
-static void gfar_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *grp);
void gfar_halt(struct net_device *dev);
static void gfar_halt_nodisable(struct net_device *dev);
void gfar_start(struct net_device *dev);
@@ -213,8 +214,7 @@ static int gfar_init_bds(struct net_device *ndev)
} else {
skb = gfar_new_skb(ndev);
if (!skb) {
- pr_err("%s: Can't allocate RX buffers\n",
- ndev->name);
+ netdev_err(ndev, "Can't allocate RX buffers\n");
goto err_rxalloc_fail;
}
rx_queue->rx_skbuff[j] = skb;
@@ -258,15 +258,14 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
sizeof(struct rxbd8) * priv->total_rx_ring_size,
&addr, GFP_KERNEL);
if (!vaddr) {
- if (netif_msg_ifup(priv))
- pr_err("%s: Could not allocate buffer descriptors!\n",
- ndev->name);
+ netif_err(priv, ifup, ndev,
+ "Could not allocate buffer descriptors!\n");
return -ENOMEM;
}
for (i = 0; i < priv->num_tx_queues; i++) {
tx_queue = priv->tx_queue[i];
- tx_queue->tx_bd_base = (struct txbd8 *) vaddr;
+ tx_queue->tx_bd_base = vaddr;
tx_queue->tx_bd_dma_base = addr;
tx_queue->dev = ndev;
/* enet DMA only understands physical addresses */
@@ -277,7 +276,7 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
/* Start the rx descriptor ring where the tx ring leaves off */
for (i = 0; i < priv->num_rx_queues; i++) {
rx_queue = priv->rx_queue[i];
- rx_queue->rx_bd_base = (struct rxbd8 *) vaddr;
+ rx_queue->rx_bd_base = vaddr;
rx_queue->rx_bd_dma_base = addr;
rx_queue->dev = ndev;
addr += sizeof (struct rxbd8) * rx_queue->rx_ring_size;
@@ -290,9 +289,8 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
tx_queue->tx_skbuff = kmalloc(sizeof(*tx_queue->tx_skbuff) *
tx_queue->tx_ring_size, GFP_KERNEL);
if (!tx_queue->tx_skbuff) {
- if (netif_msg_ifup(priv))
- pr_err("%s: Could not allocate tx_skbuff\n",
- ndev->name);
+ netif_err(priv, ifup, ndev,
+ "Could not allocate tx_skbuff\n");
goto cleanup;
}
@@ -306,9 +304,8 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
rx_queue->rx_ring_size, GFP_KERNEL);
if (!rx_queue->rx_skbuff) {
- if (netif_msg_ifup(priv))
- pr_err("%s: Could not allocate rx_skbuff\n",
- ndev->name);
+ netif_err(priv, ifup, ndev,
+ "Could not allocate rx_skbuff\n");
goto cleanup;
}
@@ -391,11 +388,8 @@ static void gfar_init_mac(struct net_device *ndev)
if (priv->hwts_rx_en)
rctrl |= RCTRL_PRSDEP_INIT | RCTRL_TS_ENABLE;
- /* keep vlan related bits if it's enabled */
- if (priv->vlgrp) {
+ if (ndev->features & NETIF_F_HW_VLAN_RX)
rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
- tctrl |= TCTRL_VLINS;
- }
/* Init rctrl based on our settings */
gfar_write(&regs->rctrl, rctrl);
@@ -464,11 +458,10 @@ static const struct net_device_ops gfar_netdev_ops = {
.ndo_stop = gfar_close,
.ndo_change_mtu = gfar_change_mtu,
.ndo_set_features = gfar_set_features,
- .ndo_set_multicast_list = gfar_set_multi,
+ .ndo_set_rx_mode = gfar_set_multi,
.ndo_tx_timeout = gfar_timeout,
.ndo_do_ioctl = gfar_ioctl,
.ndo_get_stats = gfar_get_stats,
- .ndo_vlan_rx_register = gfar_vlan_rx_register,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -508,10 +501,17 @@ void unlock_tx_qs(struct gfar_private *priv)
spin_unlock(&priv->tx_queue[i]->txlock);
}
+static bool gfar_is_vlan_on(struct gfar_private *priv)
+{
+ return (priv->ndev->features & NETIF_F_HW_VLAN_RX) ||
+ (priv->ndev->features & NETIF_F_HW_VLAN_TX);
+}
+
/* Returns 1 if incoming frames use an FCB */
static inline int gfar_uses_fcb(struct gfar_private *priv)
{
- return priv->vlgrp || (priv->ndev->features & NETIF_F_RXCSUM) ||
+ return gfar_is_vlan_on(priv) ||
+ (priv->ndev->features & NETIF_F_RXCSUM) ||
(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER);
}
@@ -625,9 +625,9 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
num_tx_qs = tx_queues ? *tx_queues : 1;
if (num_tx_qs > MAX_TX_QS) {
- printk(KERN_ERR "num_tx_qs(=%d) greater than MAX_TX_QS(=%d)\n",
- num_tx_qs, MAX_TX_QS);
- printk(KERN_ERR "Cannot do alloc_etherdev, aborting\n");
+ pr_err("num_tx_qs(=%d) greater than MAX_TX_QS(=%d)\n",
+ num_tx_qs, MAX_TX_QS);
+ pr_err("Cannot do alloc_etherdev, aborting\n");
return -EINVAL;
}
@@ -635,9 +635,9 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
num_rx_qs = rx_queues ? *rx_queues : 1;
if (num_rx_qs > MAX_RX_QS) {
- printk(KERN_ERR "num_rx_qs(=%d) greater than MAX_RX_QS(=%d)\n",
- num_tx_qs, MAX_TX_QS);
- printk(KERN_ERR "Cannot do alloc_etherdev, aborting\n");
+ pr_err("num_rx_qs(=%d) greater than MAX_RX_QS(=%d)\n",
+ num_rx_qs, MAX_RX_QS);
+ pr_err("Cannot do alloc_etherdev, aborting\n");
return -EINVAL;
}
@@ -655,6 +655,11 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
priv->num_rx_queues = num_rx_qs;
priv->num_grps = 0x0;
+ /* Init Rx queue filer rule set linked list*/
+ INIT_LIST_HEAD(&priv->rx_list.list);
+ priv->rx_list.count = 0;
+ mutex_init(&priv->rx_queue_access);
+
model = of_get_property(np, "model", NULL);
for (i = 0; i < MAXGROUPS; i++)
@@ -1034,10 +1039,10 @@ static int gfar_probe(struct platform_device *ofdev)
NETIF_F_RXCSUM | NETIF_F_HIGHDMA;
}
- priv->vlgrp = NULL;
-
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN)
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
+ dev->hw_features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+ }
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) {
priv->extended_hash = 1;
@@ -1148,9 +1153,8 @@ static int gfar_probe(struct platform_device *ofdev)
priv->rx_queue[i]->rxic = DEFAULT_RXIC;
}
- /* enable filer if using multiple RX queues*/
- if(priv->num_rx_queues > 1)
- priv->rx_filer_enable = 1;
+ /* always enable rx filer*/
+ priv->rx_filer_enable = 1;
/* Enable most messages by default */
priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
@@ -1160,8 +1164,7 @@ static int gfar_probe(struct platform_device *ofdev)
err = register_netdev(dev);
if (err) {
- printk(KERN_ERR "%s: Cannot register net device, aborting.\n",
- dev->name);
+ pr_err("%s: Cannot register net device, aborting\n", dev->name);
goto register_fail;
}
@@ -1212,17 +1215,17 @@ static int gfar_probe(struct platform_device *ofdev)
gfar_init_sysfs(dev);
/* Print out the device info */
- printk(KERN_INFO DEVICE_NAME "%pM\n", dev->name, dev->dev_addr);
+ netdev_info(dev, "mac: %pM\n", dev->dev_addr);
/* Even more device info helps when determining which kernel */
/* provided which set of benchmarks. */
- printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name);
+ netdev_info(dev, "Running with NAPI enabled\n");
for (i = 0; i < priv->num_rx_queues; i++)
- printk(KERN_INFO "%s: RX BD ring size for Q[%d]: %d\n",
- dev->name, i, priv->rx_queue[i]->rx_ring_size);
+ netdev_info(dev, "RX BD ring size for Q[%d]: %d\n",
+ i, priv->rx_queue[i]->rx_ring_size);
for(i = 0; i < priv->num_tx_queues; i++)
- printk(KERN_INFO "%s: TX BD ring size for Q[%d]: %d\n",
- dev->name, i, priv->tx_queue[i]->tx_ring_size);
+ netdev_info(dev, "TX BD ring size for Q[%d]: %d\n",
+ i, priv->tx_queue[i]->tx_ring_size);
return 0;
@@ -1855,34 +1858,30 @@ static int register_grp_irqs(struct gfar_priv_grp *grp)
* Transmit, and Receive */
if ((err = request_irq(grp->interruptError, gfar_error, 0,
grp->int_name_er,grp)) < 0) {
- if (netif_msg_intr(priv))
- printk(KERN_ERR "%s: Can't get IRQ %d\n",
- dev->name, grp->interruptError);
+ netif_err(priv, intr, dev, "Can't get IRQ %d\n",
+ grp->interruptError);
goto err_irq_fail;
}
if ((err = request_irq(grp->interruptTransmit, gfar_transmit,
0, grp->int_name_tx, grp)) < 0) {
- if (netif_msg_intr(priv))
- printk(KERN_ERR "%s: Can't get IRQ %d\n",
- dev->name, grp->interruptTransmit);
+ netif_err(priv, intr, dev, "Can't get IRQ %d\n",
+ grp->interruptTransmit);
goto tx_irq_fail;
}
if ((err = request_irq(grp->interruptReceive, gfar_receive, 0,
grp->int_name_rx, grp)) < 0) {
- if (netif_msg_intr(priv))
- printk(KERN_ERR "%s: Can't get IRQ %d\n",
- dev->name, grp->interruptReceive);
+ netif_err(priv, intr, dev, "Can't get IRQ %d\n",
+ grp->interruptReceive);
goto rx_irq_fail;
}
} else {
if ((err = request_irq(grp->interruptTransmit, gfar_interrupt, 0,
grp->int_name_tx, grp)) < 0) {
- if (netif_msg_intr(priv))
- printk(KERN_ERR "%s: Can't get IRQ %d\n",
- dev->name, grp->interruptTransmit);
+ netif_err(priv, intr, dev, "Can't get IRQ %d\n",
+ grp->interruptTransmit);
goto err_irq_fail;
}
}
@@ -2141,11 +2140,11 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (i == nr_frags - 1)
lstatus |= BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT);
- bufaddr = dma_map_page(&priv->ofdev->dev,
- skb_shinfo(skb)->frags[i].page,
- skb_shinfo(skb)->frags[i].page_offset,
- length,
- DMA_TO_DEVICE);
+ bufaddr = skb_frag_dma_map(&priv->ofdev->dev,
+ &skb_shinfo(skb)->frags[i],
+ 0,
+ length,
+ DMA_TO_DEVICE);
/* set the TxBD length and buffer pointer */
txbdp->bufPtr = bufaddr;
@@ -2289,10 +2288,25 @@ static int gfar_set_mac_address(struct net_device *dev)
return 0;
}
+/* Check if rx parser should be activated */
+void gfar_check_rx_parser_mode(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs;
+ u32 tempval;
+
+ regs = priv->gfargrp[0].regs;
+
+ tempval = gfar_read(&regs->rctrl);
+ /* If parse is no longer required, then disable parser */
+ if (tempval & RCTRL_REQ_PARSER)
+ tempval |= RCTRL_PRSDEP_INIT;
+ else
+ tempval &= ~RCTRL_PRSDEP_INIT;
+ gfar_write(&regs->rctrl, tempval);
+}
/* Enables and disables VLAN insertion/extraction */
-static void gfar_vlan_rx_register(struct net_device *dev,
- struct vlan_group *grp)
+void gfar_vlan_mode(struct net_device *dev, u32 features)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar __iomem *regs = NULL;
@@ -2303,34 +2317,30 @@ static void gfar_vlan_rx_register(struct net_device *dev,
local_irq_save(flags);
lock_rx_qs(priv);
- priv->vlgrp = grp;
-
- if (grp) {
+ if (features & NETIF_F_HW_VLAN_TX) {
/* Enable VLAN tag insertion */
tempval = gfar_read(&regs->tctrl);
tempval |= TCTRL_VLINS;
-
gfar_write(&regs->tctrl, tempval);
-
- /* Enable VLAN tag extraction */
- tempval = gfar_read(&regs->rctrl);
- tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT);
- gfar_write(&regs->rctrl, tempval);
} else {
/* Disable VLAN tag insertion */
tempval = gfar_read(&regs->tctrl);
tempval &= ~TCTRL_VLINS;
gfar_write(&regs->tctrl, tempval);
+ }
+ if (features & NETIF_F_HW_VLAN_RX) {
+ /* Enable VLAN tag extraction */
+ tempval = gfar_read(&regs->rctrl);
+ tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT);
+ gfar_write(&regs->rctrl, tempval);
+ } else {
/* Disable VLAN tag extraction */
tempval = gfar_read(&regs->rctrl);
tempval &= ~RCTRL_VLEX;
- /* If parse is no longer required, then disable parser */
- if (tempval & RCTRL_REQ_PARSER)
- tempval |= RCTRL_PRSDEP_INIT;
- else
- tempval &= ~RCTRL_PRSDEP_INIT;
gfar_write(&regs->rctrl, tempval);
+
+ gfar_check_rx_parser_mode(priv);
}
gfar_change_mtu(dev, dev->mtu);
@@ -2347,13 +2357,11 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
int oldsize = priv->rx_buffer_size;
int frame_size = new_mtu + ETH_HLEN;
- if (priv->vlgrp)
+ if (gfar_is_vlan_on(priv))
frame_size += VLAN_HLEN;
if ((frame_size < 64) || (frame_size > JUMBO_FRAME_SIZE)) {
- if (netif_msg_drv(priv))
- printk(KERN_ERR "%s: Invalid MTU setting\n",
- dev->name);
+ netif_err(priv, drv, dev, "Invalid MTU setting\n");
return -EINVAL;
}
@@ -2702,11 +2710,17 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
/* Tell the skb what kind of packet this is */
skb->protocol = eth_type_trans(skb, dev);
+ /*
+ * There's need to check for NETIF_F_HW_VLAN_RX here.
+ * Even if vlan rx accel is disabled, on some chips
+ * RXFCB_VLN is pseudo randomly set.
+ */
+ if (dev->features & NETIF_F_HW_VLAN_RX &&
+ fcb->flags & RXFCB_VLN)
+ __vlan_hwaccel_put_tag(skb, fcb->vlctl);
+
/* Send the packet up the stack */
- if (unlikely(priv->vlgrp && (fcb->flags & RXFCB_VLN)))
- ret = vlan_hwaccel_receive_skb(skb, priv->vlgrp, fcb->vlctl);
- else
- ret = netif_receive_skb(skb);
+ ret = netif_receive_skb(skb);
if (NET_RX_DROP == ret)
priv->extra_stats.kernel_dropped++;
@@ -2773,9 +2787,7 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
gfar_process_frame(dev, skb, amount_pull);
} else {
- if (netif_msg_rx_err(priv))
- printk(KERN_WARNING
- "%s: Missing skb!\n", dev->name);
+ netif_warn(priv, rx_err, dev, "Missing skb!\n");
rx_queue->stats.rx_dropped++;
priv->extra_stats.rx_skbmissing++;
}
@@ -2978,10 +2990,9 @@ static void adjust_link(struct net_device *dev)
ecntrl &= ~(ECNTRL_R100);
break;
default:
- if (netif_msg_link(priv))
- printk(KERN_WARNING
- "%s: Ack! Speed (%d) is not 10/100/1000!\n",
- dev->name, phydev->speed);
+ netif_warn(priv, link, dev,
+ "Ack! Speed (%d) is not 10/100/1000!\n",
+ phydev->speed);
break;
}
@@ -3186,8 +3197,8 @@ static irqreturn_t gfar_error(int irq, void *grp_id)
/* Hmm... */
if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv))
- printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n",
- dev->name, events, gfar_read(&regs->imask));
+ netdev_dbg(dev, "error interrupt (ievent=0x%08x imask=0x%08x)\n",
+ events, gfar_read(&regs->imask));
/* Update the error counters */
if (events & IEVENT_TXE) {
@@ -3200,9 +3211,8 @@ static irqreturn_t gfar_error(int irq, void *grp_id)
if (events & IEVENT_XFUN) {
unsigned long flags;
- if (netif_msg_tx_err(priv))
- printk(KERN_DEBUG "%s: TX FIFO underrun, "
- "packet dropped.\n", dev->name);
+ netif_dbg(priv, tx_err, dev,
+ "TX FIFO underrun, packet dropped\n");
dev->stats.tx_dropped++;
priv->extra_stats.tx_underrun++;
@@ -3215,8 +3225,7 @@ static irqreturn_t gfar_error(int irq, void *grp_id)
unlock_tx_qs(priv);
local_irq_restore(flags);
}
- if (netif_msg_tx_err(priv))
- printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);
+ netif_dbg(priv, tx_err, dev, "Transmit Error\n");
}
if (events & IEVENT_BSY) {
dev->stats.rx_errors++;
@@ -3224,29 +3233,25 @@ static irqreturn_t gfar_error(int irq, void *grp_id)
gfar_receive(irq, grp_id);
- if (netif_msg_rx_err(priv))
- printk(KERN_DEBUG "%s: busy error (rstat: %x)\n",
- dev->name, gfar_read(&regs->rstat));
+ netif_dbg(priv, rx_err, dev, "busy error (rstat: %x)\n",
+ gfar_read(&regs->rstat));
}
if (events & IEVENT_BABR) {
dev->stats.rx_errors++;
priv->extra_stats.rx_babr++;
- if (netif_msg_rx_err(priv))
- printk(KERN_DEBUG "%s: babbling RX error\n", dev->name);
+ netif_dbg(priv, rx_err, dev, "babbling RX error\n");
}
if (events & IEVENT_EBERR) {
priv->extra_stats.eberr++;
- if (netif_msg_rx_err(priv))
- printk(KERN_DEBUG "%s: bus error\n", dev->name);
+ netif_dbg(priv, rx_err, dev, "bus error\n");
}
- if ((events & IEVENT_RXC) && netif_msg_rx_status(priv))
- printk(KERN_DEBUG "%s: control frame\n", dev->name);
+ if (events & IEVENT_RXC)
+ netif_dbg(priv, rx_status, dev, "control frame\n");
if (events & IEVENT_BABT) {
priv->extra_stats.tx_babt++;
- if (netif_msg_tx_err(priv))
- printk(KERN_DEBUG "%s: babbling TX error\n", dev->name);
+ netif_dbg(priv, tx_err, dev, "babbling TX error\n");
}
return IRQ_HANDLED;
}
diff --git a/drivers/net/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h
index ba36dc7a3435..9aa43773e8e3 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/ethernet/freescale/gianfar.h
@@ -47,6 +47,16 @@
#include <linux/workqueue.h>
#include <linux/ethtool.h>
+struct ethtool_flow_spec_container {
+ struct ethtool_rx_flow_spec fs;
+ struct list_head list;
+};
+
+struct ethtool_rx_list {
+ struct list_head list;
+ unsigned int count;
+};
+
/* The maximum number of packets to be handled in one call of gfar_poll */
#define GFAR_DEV_WEIGHT 64
@@ -168,6 +178,7 @@ extern const char gfar_driver_version[];
#define MACCFG2_LENGTHCHECK 0x00000010
#define MACCFG2_MPEN 0x00000008
+#define ECNTRL_FIFM 0x00008000
#define ECNTRL_INIT_SETTINGS 0x00001000
#define ECNTRL_TBI_MODE 0x00000020
#define ECNTRL_REDUCED_MODE 0x00000010
@@ -271,10 +282,11 @@ extern const char gfar_driver_version[];
#define RCTRL_TUCSEN 0x00000100
#define RCTRL_PRSDEP_MASK 0x000000c0
#define RCTRL_PRSDEP_INIT 0x000000c0
+#define RCTRL_PRSFM 0x00000020
#define RCTRL_PROM 0x00000008
#define RCTRL_EMEN 0x00000002
#define RCTRL_REQ_PARSER (RCTRL_VLEX | RCTRL_IPCSEN | \
- RCTRL_TUCSEN)
+ RCTRL_TUCSEN | RCTRL_FILREN)
#define RCTRL_CHECKSUMMING (RCTRL_IPCSEN | RCTRL_TUCSEN | \
RCTRL_PRSDEP_INIT)
#define RCTRL_EXTHASH (RCTRL_GHTX)
@@ -397,6 +409,7 @@ extern const char gfar_driver_version[];
#define RQFCR_HASHTBL_2 0x00060000
#define RQFCR_HASHTBL_3 0x00080000
#define RQFCR_HASH 0x00010000
+#define RQFCR_QUEUE 0x0000FC00
#define RQFCR_CLE 0x00000200
#define RQFCR_RJE 0x00000100
#define RQFCR_AND 0x00000080
@@ -1064,8 +1077,9 @@ struct gfar_private {
struct sk_buff_head rx_recycle;
- struct vlan_group *vlgrp;
-
+ /* RX queue filer rule set*/
+ struct ethtool_rx_list rx_list;
+ struct mutex rx_queue_access;
/* Hash registers and their width */
u32 __iomem *hash_regs[16];
@@ -1142,6 +1156,16 @@ static inline void gfar_write_filer(struct gfar_private *priv,
gfar_write(&regs->rqfpr, fpr);
}
+static inline void gfar_read_filer(struct gfar_private *priv,
+ unsigned int far, unsigned int *fcr, unsigned int *fpr)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+
+ gfar_write(&regs->rqfar, far);
+ *fcr = gfar_read(&regs->rqfcr);
+ *fpr = gfar_read(&regs->rqfpr);
+}
+
extern void lock_rx_qs(struct gfar_private *priv);
extern void lock_tx_qs(struct gfar_private *priv);
extern void unlock_rx_qs(struct gfar_private *priv);
@@ -1156,7 +1180,37 @@ extern void gfar_configure_coalescing(struct gfar_private *priv,
unsigned long tx_mask, unsigned long rx_mask);
void gfar_init_sysfs(struct net_device *dev);
int gfar_set_features(struct net_device *dev, u32 features);
+extern void gfar_check_rx_parser_mode(struct gfar_private *priv);
+extern void gfar_vlan_mode(struct net_device *dev, u32 features);
extern const struct ethtool_ops gfar_ethtool_ops;
+#define MAX_FILER_CACHE_IDX (2*(MAX_FILER_IDX))
+
+#define RQFCR_PID_PRI_MASK 0xFFFFFFF8
+#define RQFCR_PID_L4P_MASK 0xFFFFFF00
+#define RQFCR_PID_VID_MASK 0xFFFFF000
+#define RQFCR_PID_PORT_MASK 0xFFFF0000
+#define RQFCR_PID_MAC_MASK 0xFF000000
+
+struct gfar_mask_entry {
+ unsigned int mask; /* The mask value which is valid form start to end */
+ unsigned int start;
+ unsigned int end;
+ unsigned int block; /* Same block values indicate depended entries */
+};
+
+/* Represents a receive filer table entry */
+struct gfar_filer_entry {
+ u32 ctrl;
+ u32 prop;
+};
+
+
+/* The 20 additional entries are a shadow for one extra element */
+struct filer_table {
+ u32 index;
+ struct gfar_filer_entry fe[MAX_FILER_CACHE_IDX + 20];
+};
+
#endif /* __GIANFAR_H */
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
new file mode 100644
index 000000000000..212736bab6bb
--- /dev/null
+++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
@@ -0,0 +1,1764 @@
+/*
+ * drivers/net/gianfar_ethtool.c
+ *
+ * Gianfar Ethernet Driver
+ * Ethtool support for Gianfar Enet
+ * Based on e1000 ethtool support
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
+ *
+ * Copyright 2003-2006, 2008-2009, 2011 Freescale Semiconductor, Inc.
+ *
+ * This software may be used and distributed according to
+ * the terms of the GNU Public License, Version 2, incorporated herein
+ * by reference.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/crc32.h>
+#include <asm/types.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/sort.h>
+#include <linux/if_vlan.h>
+
+#include "gianfar.h"
+
+extern void gfar_start(struct net_device *dev);
+extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
+
+#define GFAR_MAX_COAL_USECS 0xffff
+#define GFAR_MAX_COAL_FRAMES 0xff
+static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
+ u64 * buf);
+static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf);
+static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
+static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
+static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
+static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
+static void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo);
+
+static char stat_gstrings[][ETH_GSTRING_LEN] = {
+ "rx-dropped-by-kernel",
+ "rx-large-frame-errors",
+ "rx-short-frame-errors",
+ "rx-non-octet-errors",
+ "rx-crc-errors",
+ "rx-overrun-errors",
+ "rx-busy-errors",
+ "rx-babbling-errors",
+ "rx-truncated-frames",
+ "ethernet-bus-error",
+ "tx-babbling-errors",
+ "tx-underrun-errors",
+ "rx-skb-missing-errors",
+ "tx-timeout-errors",
+ "tx-rx-64-frames",
+ "tx-rx-65-127-frames",
+ "tx-rx-128-255-frames",
+ "tx-rx-256-511-frames",
+ "tx-rx-512-1023-frames",
+ "tx-rx-1024-1518-frames",
+ "tx-rx-1519-1522-good-vlan",
+ "rx-bytes",
+ "rx-packets",
+ "rx-fcs-errors",
+ "receive-multicast-packet",
+ "receive-broadcast-packet",
+ "rx-control-frame-packets",
+ "rx-pause-frame-packets",
+ "rx-unknown-op-code",
+ "rx-alignment-error",
+ "rx-frame-length-error",
+ "rx-code-error",
+ "rx-carrier-sense-error",
+ "rx-undersize-packets",
+ "rx-oversize-packets",
+ "rx-fragmented-frames",
+ "rx-jabber-frames",
+ "rx-dropped-frames",
+ "tx-byte-counter",
+ "tx-packets",
+ "tx-multicast-packets",
+ "tx-broadcast-packets",
+ "tx-pause-control-frames",
+ "tx-deferral-packets",
+ "tx-excessive-deferral-packets",
+ "tx-single-collision-packets",
+ "tx-multiple-collision-packets",
+ "tx-late-collision-packets",
+ "tx-excessive-collision-packets",
+ "tx-total-collision",
+ "reserved",
+ "tx-dropped-frames",
+ "tx-jabber-frames",
+ "tx-fcs-errors",
+ "tx-control-frames",
+ "tx-oversize-frames",
+ "tx-undersize-frames",
+ "tx-fragmented-frames",
+};
+
+/* Fill in a buffer with the strings which correspond to the
+ * stats */
+static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
+ memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN);
+ else
+ memcpy(buf, stat_gstrings,
+ GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN);
+}
+
+/* Fill in an array of 64-bit statistics from various sources.
+ * This array will be appended to the end of the ethtool_stats
+ * structure, and returned to user space
+ */
+static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
+{
+ int i;
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u64 *extra = (u64 *) & priv->extra_stats;
+
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
+ u32 __iomem *rmon = (u32 __iomem *) &regs->rmon;
+ struct gfar_stats *stats = (struct gfar_stats *) buf;
+
+ for (i = 0; i < GFAR_RMON_LEN; i++)
+ stats->rmon[i] = (u64) gfar_read(&rmon[i]);
+
+ for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++)
+ stats->extra[i] = extra[i];
+ } else
+ for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++)
+ buf[i] = extra[i];
+}
+
+static int gfar_sset_count(struct net_device *dev, int sset)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+
+ switch (sset) {
+ case ETH_SS_STATS:
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
+ return GFAR_STATS_LEN;
+ else
+ return GFAR_EXTRA_STATS_LEN;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/* Fills in the drvinfo structure with some basic info */
+static void gfar_gdrvinfo(struct net_device *dev, struct
+ ethtool_drvinfo *drvinfo)
+{
+ strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN);
+ drvinfo->regdump_len = 0;
+ drvinfo->eedump_len = 0;
+}
+
+
+static int gfar_ssettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+
+ if (NULL == phydev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phydev, cmd);
+}
+
+
+/* Return the current settings in the ethtool_cmd structure */
+static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar_priv_tx_q *tx_queue = NULL;
+
+ if (NULL == phydev)
+ return -ENODEV;
+ tx_queue = priv->tx_queue[0];
+ rx_queue = priv->rx_queue[0];
+
+ /* etsec-1.7 and older versions have only one txic
+ * and rxic regs although they support multiple queues */
+ cmd->maxtxpkt = get_icft_value(tx_queue->txic);
+ cmd->maxrxpkt = get_icft_value(rx_queue->rxic);
+
+ return phy_ethtool_gset(phydev, cmd);
+}
+
+/* Return the length of the register structure */
+static int gfar_reglen(struct net_device *dev)
+{
+ return sizeof (struct gfar);
+}
+
+/* Return a dump of the GFAR register space */
+static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
+{
+ int i;
+ struct gfar_private *priv = netdev_priv(dev);
+ u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp[0].regs;
+ u32 *buf = (u32 *) regbuf;
+
+ for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++)
+ buf[i] = gfar_read(&theregs[i]);
+}
+
+/* Convert microseconds to ethernet clock ticks, which changes
+ * depending on what speed the controller is running at */
+static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int usecs)
+{
+ unsigned int count;
+
+ /* The timer is different, depending on the interface speed */
+ switch (priv->phydev->speed) {
+ case SPEED_1000:
+ count = GFAR_GBIT_TIME;
+ break;
+ case SPEED_100:
+ count = GFAR_100_TIME;
+ break;
+ case SPEED_10:
+ default:
+ count = GFAR_10_TIME;
+ break;
+ }
+
+ /* Make sure we return a number greater than 0
+ * if usecs > 0 */
+ return (usecs * 1000 + count - 1) / count;
+}
+
+/* Convert ethernet clock ticks to microseconds */
+static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int ticks)
+{
+ unsigned int count;
+
+ /* The timer is different, depending on the interface speed */
+ switch (priv->phydev->speed) {
+ case SPEED_1000:
+ count = GFAR_GBIT_TIME;
+ break;
+ case SPEED_100:
+ count = GFAR_100_TIME;
+ break;
+ case SPEED_10:
+ default:
+ count = GFAR_10_TIME;
+ break;
+ }
+
+ /* Make sure we return a number greater than 0 */
+ /* if ticks is > 0 */
+ return (ticks * count) / 1000;
+}
+
+/* Get the coalescing parameters, and put them in the cvals
+ * structure. */
+static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ unsigned long rxtime;
+ unsigned long rxcount;
+ unsigned long txtime;
+ unsigned long txcount;
+
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
+ return -EOPNOTSUPP;
+
+ if (NULL == priv->phydev)
+ return -ENODEV;
+
+ rx_queue = priv->rx_queue[0];
+ tx_queue = priv->tx_queue[0];
+
+ rxtime = get_ictt_value(rx_queue->rxic);
+ rxcount = get_icft_value(rx_queue->rxic);
+ txtime = get_ictt_value(tx_queue->txic);
+ txcount = get_icft_value(tx_queue->txic);
+ cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, rxtime);
+ cvals->rx_max_coalesced_frames = rxcount;
+
+ cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, txtime);
+ cvals->tx_max_coalesced_frames = txcount;
+
+ cvals->use_adaptive_rx_coalesce = 0;
+ cvals->use_adaptive_tx_coalesce = 0;
+
+ cvals->pkt_rate_low = 0;
+ cvals->rx_coalesce_usecs_low = 0;
+ cvals->rx_max_coalesced_frames_low = 0;
+ cvals->tx_coalesce_usecs_low = 0;
+ cvals->tx_max_coalesced_frames_low = 0;
+
+ /* When the packet rate is below pkt_rate_high but above
+ * pkt_rate_low (both measured in packets per second) the
+ * normal {rx,tx}_* coalescing parameters are used.
+ */
+
+ /* When the packet rate is (measured in packets per second)
+ * is above pkt_rate_high, the {rx,tx}_*_high parameters are
+ * used.
+ */
+ cvals->pkt_rate_high = 0;
+ cvals->rx_coalesce_usecs_high = 0;
+ cvals->rx_max_coalesced_frames_high = 0;
+ cvals->tx_coalesce_usecs_high = 0;
+ cvals->tx_max_coalesced_frames_high = 0;
+
+ /* How often to do adaptive coalescing packet rate sampling,
+ * measured in seconds. Must not be zero.
+ */
+ cvals->rate_sample_interval = 0;
+
+ return 0;
+}
+
+/* Change the coalescing values.
+ * Both cvals->*_usecs and cvals->*_frames have to be > 0
+ * in order for coalescing to be active
+ */
+static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ int i = 0;
+
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
+ return -EOPNOTSUPP;
+
+ /* Set up rx coalescing */
+ /* As of now, we will enable/disable coalescing for all
+ * queues together in case of eTSEC2, this will be modified
+ * along with the ethtool interface */
+ if ((cvals->rx_coalesce_usecs == 0) ||
+ (cvals->rx_max_coalesced_frames == 0)) {
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i]->rxcoalescing = 0;
+ } else {
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i]->rxcoalescing = 1;
+ }
+
+ if (NULL == priv->phydev)
+ return -ENODEV;
+
+ /* Check the bounds of the values */
+ if (cvals->rx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
+ pr_info("Coalescing is limited to %d microseconds\n",
+ GFAR_MAX_COAL_USECS);
+ return -EINVAL;
+ }
+
+ if (cvals->rx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
+ pr_info("Coalescing is limited to %d frames\n",
+ GFAR_MAX_COAL_FRAMES);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rxic = mk_ic_value(
+ cvals->rx_max_coalesced_frames,
+ gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs));
+ }
+
+ /* Set up tx coalescing */
+ if ((cvals->tx_coalesce_usecs == 0) ||
+ (cvals->tx_max_coalesced_frames == 0)) {
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i]->txcoalescing = 0;
+ } else {
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i]->txcoalescing = 1;
+ }
+
+ /* Check the bounds of the values */
+ if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
+ pr_info("Coalescing is limited to %d microseconds\n",
+ GFAR_MAX_COAL_USECS);
+ return -EINVAL;
+ }
+
+ if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
+ pr_info("Coalescing is limited to %d frames\n",
+ GFAR_MAX_COAL_FRAMES);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i]->txic = mk_ic_value(
+ cvals->tx_max_coalesced_frames,
+ gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
+ }
+
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
+
+ return 0;
+}
+
+/* Fills in rvals with the current ring parameters. Currently,
+ * rx, rx_mini, and rx_jumbo rings are the same size, as mini and
+ * jumbo are ignored by the driver */
+static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+
+ tx_queue = priv->tx_queue[0];
+ rx_queue = priv->rx_queue[0];
+
+ rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
+ rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
+ rvals->rx_jumbo_max_pending = GFAR_RX_MAX_RING_SIZE;
+ rvals->tx_max_pending = GFAR_TX_MAX_RING_SIZE;
+
+ /* Values changeable by the user. The valid values are
+ * in the range 1 to the "*_max_pending" counterpart above.
+ */
+ rvals->rx_pending = rx_queue->rx_ring_size;
+ rvals->rx_mini_pending = rx_queue->rx_ring_size;
+ rvals->rx_jumbo_pending = rx_queue->rx_ring_size;
+ rvals->tx_pending = tx_queue->tx_ring_size;
+}
+
+/* Change the current ring parameters, stopping the controller if
+ * necessary so that we don't mess things up while we're in
+ * motion. We wait for the ring to be clean before reallocating
+ * the rings. */
+static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ int err = 0, i = 0;
+
+ if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
+ return -EINVAL;
+
+ if (!is_power_of_2(rvals->rx_pending)) {
+ netdev_err(dev, "Ring sizes must be a power of 2\n");
+ return -EINVAL;
+ }
+
+ if (rvals->tx_pending > GFAR_TX_MAX_RING_SIZE)
+ return -EINVAL;
+
+ if (!is_power_of_2(rvals->tx_pending)) {
+ netdev_err(dev, "Ring sizes must be a power of 2\n");
+ return -EINVAL;
+ }
+
+
+ if (dev->flags & IFF_UP) {
+ unsigned long flags;
+
+ /* Halt TX and RX, and process the frames which
+ * have already been received */
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
+
+ gfar_halt(dev);
+
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ gfar_clean_rx_ring(priv->rx_queue[i],
+ priv->rx_queue[i]->rx_ring_size);
+
+ /* Now we take down the rings to rebuild them */
+ stop_gfar(dev);
+ }
+
+ /* Change the size */
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rx_ring_size = rvals->rx_pending;
+ priv->tx_queue[i]->tx_ring_size = rvals->tx_pending;
+ priv->tx_queue[i]->num_txbdfree = priv->tx_queue[i]->tx_ring_size;
+ }
+
+ /* Rebuild the rings with the new size */
+ if (dev->flags & IFF_UP) {
+ err = startup_gfar(dev);
+ netif_tx_wake_all_queues(dev);
+ }
+ return err;
+}
+
+int gfar_set_features(struct net_device *dev, u32 features)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int err = 0, i = 0;
+ u32 changed = dev->features ^ features;
+
+ if (changed & (NETIF_F_HW_VLAN_TX|NETIF_F_HW_VLAN_RX))
+ gfar_vlan_mode(dev, features);
+
+ if (!(changed & NETIF_F_RXCSUM))
+ return 0;
+
+ if (dev->flags & IFF_UP) {
+ /* Halt TX and RX, and process the frames which
+ * have already been received */
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
+
+ gfar_halt(dev);
+
+ unlock_tx_qs(priv);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ gfar_clean_rx_ring(priv->rx_queue[i],
+ priv->rx_queue[i]->rx_ring_size);
+
+ /* Now we take down the rings to rebuild them */
+ stop_gfar(dev);
+
+ dev->features = features;
+
+ err = startup_gfar(dev);
+ netif_tx_wake_all_queues(dev);
+ }
+ return err;
+}
+
+static uint32_t gfar_get_msglevel(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ return priv->msg_enable;
+}
+
+static void gfar_set_msglevel(struct net_device *dev, uint32_t data)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ priv->msg_enable = data;
+}
+
+#ifdef CONFIG_PM
+static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) {
+ wol->supported = WAKE_MAGIC;
+ wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0;
+ } else {
+ wol->supported = wol->wolopts = 0;
+ }
+}
+
+static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
+ wol->wolopts != 0)
+ return -EINVAL;
+
+ if (wol->wolopts & ~WAKE_MAGIC)
+ return -EINVAL;
+
+ device_set_wakeup_enable(&dev->dev, wol->wolopts & WAKE_MAGIC);
+
+ spin_lock_irqsave(&priv->bflock, flags);
+ priv->wol_en = !!device_may_wakeup(&dev->dev);
+ spin_unlock_irqrestore(&priv->bflock, flags);
+
+ return 0;
+}
+#endif
+
+static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow)
+{
+ u32 fcr = 0x0, fpr = FPR_FILER_MASK;
+
+ if (ethflow & RXH_L2DA) {
+ fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH |
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+ priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+
+ fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH |
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+ priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_VLAN) {
+ fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_IP_SRC) {
+ fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & (RXH_IP_DST)) {
+ fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L3_PROTO) {
+ fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L4_B_0_1) {
+ fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L4_B_2_3) {
+ fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+}
+
+static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class)
+{
+ unsigned int last_rule_idx = priv->cur_filer_idx;
+ unsigned int cmp_rqfpr;
+ unsigned int *local_rqfpr;
+ unsigned int *local_rqfcr;
+ int i = 0x0, k = 0x0;
+ int j = MAX_FILER_IDX, l = 0x0;
+ int ret = 1;
+
+ local_rqfpr = kmalloc(sizeof(unsigned int) * (MAX_FILER_IDX + 1),
+ GFP_KERNEL);
+ local_rqfcr = kmalloc(sizeof(unsigned int) * (MAX_FILER_IDX + 1),
+ GFP_KERNEL);
+ if (!local_rqfpr || !local_rqfcr) {
+ pr_err("Out of memory\n");
+ ret = 0;
+ goto err;
+ }
+
+ switch (class) {
+ case TCP_V4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP;
+ break;
+ case UDP_V4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP;
+ break;
+ case TCP_V6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP;
+ break;
+ case UDP_V6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP;
+ break;
+ default:
+ pr_err("Right now this class is not supported\n");
+ ret = 0;
+ goto err;
+ }
+
+ for (i = 0; i < MAX_FILER_IDX + 1; i++) {
+ local_rqfpr[j] = priv->ftp_rqfpr[i];
+ local_rqfcr[j] = priv->ftp_rqfcr[i];
+ j--;
+ if ((priv->ftp_rqfcr[i] == (RQFCR_PID_PARSE |
+ RQFCR_CLE |RQFCR_AND)) &&
+ (priv->ftp_rqfpr[i] == cmp_rqfpr))
+ break;
+ }
+
+ if (i == MAX_FILER_IDX + 1) {
+ pr_err("No parse rule found, can't create hash rules\n");
+ ret = 0;
+ goto err;
+ }
+
+ /* If a match was found, then it begins the starting of a cluster rule
+ * if it was already programmed, we need to overwrite these rules
+ */
+ for (l = i+1; l < MAX_FILER_IDX; l++) {
+ if ((priv->ftp_rqfcr[l] & RQFCR_CLE) &&
+ !(priv->ftp_rqfcr[l] & RQFCR_AND)) {
+ priv->ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT |
+ RQFCR_HASHTBL_0 | RQFCR_PID_MASK;
+ priv->ftp_rqfpr[l] = FPR_FILER_MASK;
+ gfar_write_filer(priv, l, priv->ftp_rqfcr[l],
+ priv->ftp_rqfpr[l]);
+ break;
+ }
+
+ if (!(priv->ftp_rqfcr[l] & RQFCR_CLE) &&
+ (priv->ftp_rqfcr[l] & RQFCR_AND))
+ continue;
+ else {
+ local_rqfpr[j] = priv->ftp_rqfpr[l];
+ local_rqfcr[j] = priv->ftp_rqfcr[l];
+ j--;
+ }
+ }
+
+ priv->cur_filer_idx = l - 1;
+ last_rule_idx = l;
+
+ /* hash rules */
+ ethflow_to_filer_rules(priv, ethflow);
+
+ /* Write back the popped out rules again */
+ for (k = j+1; k < MAX_FILER_IDX; k++) {
+ priv->ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k];
+ priv->ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k];
+ gfar_write_filer(priv, priv->cur_filer_idx,
+ local_rqfcr[k], local_rqfpr[k]);
+ if (!priv->cur_filer_idx)
+ break;
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+err:
+ kfree(local_rqfcr);
+ kfree(local_rqfpr);
+ return ret;
+}
+
+static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd)
+{
+ /* write the filer rules here */
+ if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int gfar_check_filer_hardware(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = NULL;
+ u32 i;
+
+ regs = priv->gfargrp[0].regs;
+
+ /* Check if we are in FIFO mode */
+ i = gfar_read(&regs->ecntrl);
+ i &= ECNTRL_FIFM;
+ if (i == ECNTRL_FIFM) {
+ netdev_notice(priv->ndev, "Interface in FIFO mode\n");
+ i = gfar_read(&regs->rctrl);
+ i &= RCTRL_PRSDEP_MASK | RCTRL_PRSFM;
+ if (i == (RCTRL_PRSDEP_MASK | RCTRL_PRSFM)) {
+ netdev_info(priv->ndev,
+ "Receive Queue Filtering enabled\n");
+ } else {
+ netdev_warn(priv->ndev,
+ "Receive Queue Filtering disabled\n");
+ return -EOPNOTSUPP;
+ }
+ }
+ /* Or in standard mode */
+ else {
+ i = gfar_read(&regs->rctrl);
+ i &= RCTRL_PRSDEP_MASK;
+ if (i == RCTRL_PRSDEP_MASK) {
+ netdev_info(priv->ndev,
+ "Receive Queue Filtering enabled\n");
+ } else {
+ netdev_warn(priv->ndev,
+ "Receive Queue Filtering disabled\n");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ /* Sets the properties for arbitrary filer rule
+ * to the first 4 Layer 4 Bytes */
+ regs->rbifx = 0xC0C1C2C3;
+ return 0;
+}
+
+static int gfar_comp_asc(const void *a, const void *b)
+{
+ return memcmp(a, b, 4);
+}
+
+static int gfar_comp_desc(const void *a, const void *b)
+{
+ return -memcmp(a, b, 4);
+}
+
+static void gfar_swap(void *a, void *b, int size)
+{
+ u32 *_a = a;
+ u32 *_b = b;
+
+ swap(_a[0], _b[0]);
+ swap(_a[1], _b[1]);
+ swap(_a[2], _b[2]);
+ swap(_a[3], _b[3]);
+}
+
+/* Write a mask to filer cache */
+static void gfar_set_mask(u32 mask, struct filer_table *tab)
+{
+ tab->fe[tab->index].ctrl = RQFCR_AND | RQFCR_PID_MASK | RQFCR_CMP_EXACT;
+ tab->fe[tab->index].prop = mask;
+ tab->index++;
+}
+
+/* Sets parse bits (e.g. IP or TCP) */
+static void gfar_set_parse_bits(u32 value, u32 mask, struct filer_table *tab)
+{
+ gfar_set_mask(mask, tab);
+ tab->fe[tab->index].ctrl = RQFCR_CMP_EXACT | RQFCR_PID_PARSE
+ | RQFCR_AND;
+ tab->fe[tab->index].prop = value;
+ tab->index++;
+}
+
+static void gfar_set_general_attribute(u32 value, u32 mask, u32 flag,
+ struct filer_table *tab)
+{
+ gfar_set_mask(mask, tab);
+ tab->fe[tab->index].ctrl = RQFCR_CMP_EXACT | RQFCR_AND | flag;
+ tab->fe[tab->index].prop = value;
+ tab->index++;
+}
+
+/*
+ * For setting a tuple of value and mask of type flag
+ * Example:
+ * IP-Src = 10.0.0.0/255.0.0.0
+ * value: 0x0A000000 mask: FF000000 flag: RQFPR_IPV4
+ *
+ * Ethtool gives us a value=0 and mask=~0 for don't care a tuple
+ * For a don't care mask it gives us a 0
+ *
+ * The check if don't care and the mask adjustment if mask=0 is done for VLAN
+ * and MAC stuff on an upper level (due to missing information on this level).
+ * For these guys we can discard them if they are value=0 and mask=0.
+ *
+ * Further the all masks are one-padded for better hardware efficiency.
+ */
+static void gfar_set_attribute(u32 value, u32 mask, u32 flag,
+ struct filer_table *tab)
+{
+ switch (flag) {
+ /* 3bit */
+ case RQFCR_PID_PRI:
+ if (!(value | mask))
+ return;
+ mask |= RQFCR_PID_PRI_MASK;
+ break;
+ /* 8bit */
+ case RQFCR_PID_L4P:
+ case RQFCR_PID_TOS:
+ if (!~(mask | RQFCR_PID_L4P_MASK))
+ return;
+ if (!mask)
+ mask = ~0;
+ else
+ mask |= RQFCR_PID_L4P_MASK;
+ break;
+ /* 12bit */
+ case RQFCR_PID_VID:
+ if (!(value | mask))
+ return;
+ mask |= RQFCR_PID_VID_MASK;
+ break;
+ /* 16bit */
+ case RQFCR_PID_DPT:
+ case RQFCR_PID_SPT:
+ case RQFCR_PID_ETY:
+ if (!~(mask | RQFCR_PID_PORT_MASK))
+ return;
+ if (!mask)
+ mask = ~0;
+ else
+ mask |= RQFCR_PID_PORT_MASK;
+ break;
+ /* 24bit */
+ case RQFCR_PID_DAH:
+ case RQFCR_PID_DAL:
+ case RQFCR_PID_SAH:
+ case RQFCR_PID_SAL:
+ if (!(value | mask))
+ return;
+ mask |= RQFCR_PID_MAC_MASK;
+ break;
+ /* for all real 32bit masks */
+ default:
+ if (!~mask)
+ return;
+ if (!mask)
+ mask = ~0;
+ break;
+ }
+ gfar_set_general_attribute(value, mask, flag, tab);
+}
+
+/* Translates value and mask for UDP, TCP or SCTP */
+static void gfar_set_basic_ip(struct ethtool_tcpip4_spec *value,
+ struct ethtool_tcpip4_spec *mask, struct filer_table *tab)
+{
+ gfar_set_attribute(value->ip4src, mask->ip4src, RQFCR_PID_SIA, tab);
+ gfar_set_attribute(value->ip4dst, mask->ip4dst, RQFCR_PID_DIA, tab);
+ gfar_set_attribute(value->pdst, mask->pdst, RQFCR_PID_DPT, tab);
+ gfar_set_attribute(value->psrc, mask->psrc, RQFCR_PID_SPT, tab);
+ gfar_set_attribute(value->tos, mask->tos, RQFCR_PID_TOS, tab);
+}
+
+/* Translates value and mask for RAW-IP4 */
+static void gfar_set_user_ip(struct ethtool_usrip4_spec *value,
+ struct ethtool_usrip4_spec *mask, struct filer_table *tab)
+{
+ gfar_set_attribute(value->ip4src, mask->ip4src, RQFCR_PID_SIA, tab);
+ gfar_set_attribute(value->ip4dst, mask->ip4dst, RQFCR_PID_DIA, tab);
+ gfar_set_attribute(value->tos, mask->tos, RQFCR_PID_TOS, tab);
+ gfar_set_attribute(value->proto, mask->proto, RQFCR_PID_L4P, tab);
+ gfar_set_attribute(value->l4_4_bytes, mask->l4_4_bytes, RQFCR_PID_ARB,
+ tab);
+
+}
+
+/* Translates value and mask for ETHER spec */
+static void gfar_set_ether(struct ethhdr *value, struct ethhdr *mask,
+ struct filer_table *tab)
+{
+ u32 upper_temp_mask = 0;
+ u32 lower_temp_mask = 0;
+ /* Source address */
+ if (!is_broadcast_ether_addr(mask->h_source)) {
+
+ if (is_zero_ether_addr(mask->h_source)) {
+ upper_temp_mask = 0xFFFFFFFF;
+ lower_temp_mask = 0xFFFFFFFF;
+ } else {
+ upper_temp_mask = mask->h_source[0] << 16
+ | mask->h_source[1] << 8
+ | mask->h_source[2];
+ lower_temp_mask = mask->h_source[3] << 16
+ | mask->h_source[4] << 8
+ | mask->h_source[5];
+ }
+ /* Upper 24bit */
+ gfar_set_attribute(
+ value->h_source[0] << 16 | value->h_source[1]
+ << 8 | value->h_source[2],
+ upper_temp_mask, RQFCR_PID_SAH, tab);
+ /* And the same for the lower part */
+ gfar_set_attribute(
+ value->h_source[3] << 16 | value->h_source[4]
+ << 8 | value->h_source[5],
+ lower_temp_mask, RQFCR_PID_SAL, tab);
+ }
+ /* Destination address */
+ if (!is_broadcast_ether_addr(mask->h_dest)) {
+
+ /* Special for destination is limited broadcast */
+ if ((is_broadcast_ether_addr(value->h_dest)
+ && is_zero_ether_addr(mask->h_dest))) {
+ gfar_set_parse_bits(RQFPR_EBC, RQFPR_EBC, tab);
+ } else {
+
+ if (is_zero_ether_addr(mask->h_dest)) {
+ upper_temp_mask = 0xFFFFFFFF;
+ lower_temp_mask = 0xFFFFFFFF;
+ } else {
+ upper_temp_mask = mask->h_dest[0] << 16
+ | mask->h_dest[1] << 8
+ | mask->h_dest[2];
+ lower_temp_mask = mask->h_dest[3] << 16
+ | mask->h_dest[4] << 8
+ | mask->h_dest[5];
+ }
+
+ /* Upper 24bit */
+ gfar_set_attribute(
+ value->h_dest[0] << 16
+ | value->h_dest[1] << 8
+ | value->h_dest[2],
+ upper_temp_mask, RQFCR_PID_DAH, tab);
+ /* And the same for the lower part */
+ gfar_set_attribute(
+ value->h_dest[3] << 16
+ | value->h_dest[4] << 8
+ | value->h_dest[5],
+ lower_temp_mask, RQFCR_PID_DAL, tab);
+ }
+ }
+
+ gfar_set_attribute(value->h_proto, mask->h_proto, RQFCR_PID_ETY, tab);
+
+}
+
+/* Convert a rule to binary filter format of gianfar */
+static int gfar_convert_to_filer(struct ethtool_rx_flow_spec *rule,
+ struct filer_table *tab)
+{
+ u32 vlan = 0, vlan_mask = 0;
+ u32 id = 0, id_mask = 0;
+ u32 cfi = 0, cfi_mask = 0;
+ u32 prio = 0, prio_mask = 0;
+
+ u32 old_index = tab->index;
+
+ /* Check if vlan is wanted */
+ if ((rule->flow_type & FLOW_EXT) && (rule->m_ext.vlan_tci != 0xFFFF)) {
+ if (!rule->m_ext.vlan_tci)
+ rule->m_ext.vlan_tci = 0xFFFF;
+
+ vlan = RQFPR_VLN;
+ vlan_mask = RQFPR_VLN;
+
+ /* Separate the fields */
+ id = rule->h_ext.vlan_tci & VLAN_VID_MASK;
+ id_mask = rule->m_ext.vlan_tci & VLAN_VID_MASK;
+ cfi = rule->h_ext.vlan_tci & VLAN_CFI_MASK;
+ cfi_mask = rule->m_ext.vlan_tci & VLAN_CFI_MASK;
+ prio = (rule->h_ext.vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+ prio_mask = (rule->m_ext.vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+
+ if (cfi == VLAN_TAG_PRESENT && cfi_mask == VLAN_TAG_PRESENT) {
+ vlan |= RQFPR_CFI;
+ vlan_mask |= RQFPR_CFI;
+ } else if (cfi != VLAN_TAG_PRESENT && cfi_mask == VLAN_TAG_PRESENT) {
+ vlan_mask |= RQFPR_CFI;
+ }
+ }
+
+ switch (rule->flow_type & ~FLOW_EXT) {
+ case TCP_V4_FLOW:
+ gfar_set_parse_bits(RQFPR_IPV4 | RQFPR_TCP | vlan,
+ RQFPR_IPV4 | RQFPR_TCP | vlan_mask, tab);
+ gfar_set_basic_ip(&rule->h_u.tcp_ip4_spec,
+ &rule->m_u.tcp_ip4_spec, tab);
+ break;
+ case UDP_V4_FLOW:
+ gfar_set_parse_bits(RQFPR_IPV4 | RQFPR_UDP | vlan,
+ RQFPR_IPV4 | RQFPR_UDP | vlan_mask, tab);
+ gfar_set_basic_ip(&rule->h_u.udp_ip4_spec,
+ &rule->m_u.udp_ip4_spec, tab);
+ break;
+ case SCTP_V4_FLOW:
+ gfar_set_parse_bits(RQFPR_IPV4 | vlan, RQFPR_IPV4 | vlan_mask,
+ tab);
+ gfar_set_attribute(132, 0, RQFCR_PID_L4P, tab);
+ gfar_set_basic_ip((struct ethtool_tcpip4_spec *) &rule->h_u,
+ (struct ethtool_tcpip4_spec *) &rule->m_u, tab);
+ break;
+ case IP_USER_FLOW:
+ gfar_set_parse_bits(RQFPR_IPV4 | vlan, RQFPR_IPV4 | vlan_mask,
+ tab);
+ gfar_set_user_ip((struct ethtool_usrip4_spec *) &rule->h_u,
+ (struct ethtool_usrip4_spec *) &rule->m_u, tab);
+ break;
+ case ETHER_FLOW:
+ if (vlan)
+ gfar_set_parse_bits(vlan, vlan_mask, tab);
+ gfar_set_ether((struct ethhdr *) &rule->h_u,
+ (struct ethhdr *) &rule->m_u, tab);
+ break;
+ default:
+ return -1;
+ }
+
+ /* Set the vlan attributes in the end */
+ if (vlan) {
+ gfar_set_attribute(id, id_mask, RQFCR_PID_VID, tab);
+ gfar_set_attribute(prio, prio_mask, RQFCR_PID_PRI, tab);
+ }
+
+ /* If there has been nothing written till now, it must be a default */
+ if (tab->index == old_index) {
+ gfar_set_mask(0xFFFFFFFF, tab);
+ tab->fe[tab->index].ctrl = 0x20;
+ tab->fe[tab->index].prop = 0x0;
+ tab->index++;
+ }
+
+ /* Remove last AND */
+ tab->fe[tab->index - 1].ctrl &= (~RQFCR_AND);
+
+ /* Specify which queue to use or to drop */
+ if (rule->ring_cookie == RX_CLS_FLOW_DISC)
+ tab->fe[tab->index - 1].ctrl |= RQFCR_RJE;
+ else
+ tab->fe[tab->index - 1].ctrl |= (rule->ring_cookie << 10);
+
+ /* Only big enough entries can be clustered */
+ if (tab->index > (old_index + 2)) {
+ tab->fe[old_index + 1].ctrl |= RQFCR_CLE;
+ tab->fe[tab->index - 1].ctrl |= RQFCR_CLE;
+ }
+
+ /* In rare cases the cache can be full while there is free space in hw */
+ if (tab->index > MAX_FILER_CACHE_IDX - 1)
+ return -EBUSY;
+
+ return 0;
+}
+
+/* Copy size filer entries */
+static void gfar_copy_filer_entries(struct gfar_filer_entry dst[0],
+ struct gfar_filer_entry src[0], s32 size)
+{
+ while (size > 0) {
+ size--;
+ dst[size].ctrl = src[size].ctrl;
+ dst[size].prop = src[size].prop;
+ }
+}
+
+/* Delete the contents of the filer-table between start and end
+ * and collapse them */
+static int gfar_trim_filer_entries(u32 begin, u32 end, struct filer_table *tab)
+{
+ int length;
+ if (end > MAX_FILER_CACHE_IDX || end < begin)
+ return -EINVAL;
+
+ end++;
+ length = end - begin;
+
+ /* Copy */
+ while (end < tab->index) {
+ tab->fe[begin].ctrl = tab->fe[end].ctrl;
+ tab->fe[begin++].prop = tab->fe[end++].prop;
+
+ }
+ /* Fill up with don't cares */
+ while (begin < tab->index) {
+ tab->fe[begin].ctrl = 0x60;
+ tab->fe[begin].prop = 0xFFFFFFFF;
+ begin++;
+ }
+
+ tab->index -= length;
+ return 0;
+}
+
+/* Make space on the wanted location */
+static int gfar_expand_filer_entries(u32 begin, u32 length,
+ struct filer_table *tab)
+{
+ if (length == 0 || length + tab->index > MAX_FILER_CACHE_IDX || begin
+ > MAX_FILER_CACHE_IDX)
+ return -EINVAL;
+
+ gfar_copy_filer_entries(&(tab->fe[begin + length]), &(tab->fe[begin]),
+ tab->index - length + 1);
+
+ tab->index += length;
+ return 0;
+}
+
+static int gfar_get_next_cluster_start(int start, struct filer_table *tab)
+{
+ for (; (start < tab->index) && (start < MAX_FILER_CACHE_IDX - 1); start++) {
+ if ((tab->fe[start].ctrl & (RQFCR_AND | RQFCR_CLE))
+ == (RQFCR_AND | RQFCR_CLE))
+ return start;
+ }
+ return -1;
+}
+
+static int gfar_get_next_cluster_end(int start, struct filer_table *tab)
+{
+ for (; (start < tab->index) && (start < MAX_FILER_CACHE_IDX - 1); start++) {
+ if ((tab->fe[start].ctrl & (RQFCR_AND | RQFCR_CLE))
+ == (RQFCR_CLE))
+ return start;
+ }
+ return -1;
+}
+
+/*
+ * Uses hardwares clustering option to reduce
+ * the number of filer table entries
+ */
+static void gfar_cluster_filer(struct filer_table *tab)
+{
+ s32 i = -1, j, iend, jend;
+
+ while ((i = gfar_get_next_cluster_start(++i, tab)) != -1) {
+ j = i;
+ while ((j = gfar_get_next_cluster_start(++j, tab)) != -1) {
+ /*
+ * The cluster entries self and the previous one
+ * (a mask) must be identical!
+ */
+ if (tab->fe[i].ctrl != tab->fe[j].ctrl)
+ break;
+ if (tab->fe[i].prop != tab->fe[j].prop)
+ break;
+ if (tab->fe[i - 1].ctrl != tab->fe[j - 1].ctrl)
+ break;
+ if (tab->fe[i - 1].prop != tab->fe[j - 1].prop)
+ break;
+ iend = gfar_get_next_cluster_end(i, tab);
+ jend = gfar_get_next_cluster_end(j, tab);
+ if (jend == -1 || iend == -1)
+ break;
+ /*
+ * First we make some free space, where our cluster
+ * element should be. Then we copy it there and finally
+ * delete in from its old location.
+ */
+
+ if (gfar_expand_filer_entries(iend, (jend - j), tab)
+ == -EINVAL)
+ break;
+
+ gfar_copy_filer_entries(&(tab->fe[iend + 1]),
+ &(tab->fe[jend + 1]), jend - j);
+
+ if (gfar_trim_filer_entries(jend - 1,
+ jend + (jend - j), tab) == -EINVAL)
+ return;
+
+ /* Mask out cluster bit */
+ tab->fe[iend].ctrl &= ~(RQFCR_CLE);
+ }
+ }
+}
+
+/* Swaps the masked bits of a1<>a2 and b1<>b2 */
+static void gfar_swap_bits(struct gfar_filer_entry *a1,
+ struct gfar_filer_entry *a2, struct gfar_filer_entry *b1,
+ struct gfar_filer_entry *b2, u32 mask)
+{
+ u32 temp[4];
+ temp[0] = a1->ctrl & mask;
+ temp[1] = a2->ctrl & mask;
+ temp[2] = b1->ctrl & mask;
+ temp[3] = b2->ctrl & mask;
+
+ a1->ctrl &= ~mask;
+ a2->ctrl &= ~mask;
+ b1->ctrl &= ~mask;
+ b2->ctrl &= ~mask;
+
+ a1->ctrl |= temp[1];
+ a2->ctrl |= temp[0];
+ b1->ctrl |= temp[3];
+ b2->ctrl |= temp[2];
+}
+
+/*
+ * Generate a list consisting of masks values with their start and
+ * end of validity and block as indicator for parts belonging
+ * together (glued by ANDs) in mask_table
+ */
+static u32 gfar_generate_mask_table(struct gfar_mask_entry *mask_table,
+ struct filer_table *tab)
+{
+ u32 i, and_index = 0, block_index = 1;
+
+ for (i = 0; i < tab->index; i++) {
+
+ /* LSByte of control = 0 sets a mask */
+ if (!(tab->fe[i].ctrl & 0xF)) {
+ mask_table[and_index].mask = tab->fe[i].prop;
+ mask_table[and_index].start = i;
+ mask_table[and_index].block = block_index;
+ if (and_index >= 1)
+ mask_table[and_index - 1].end = i - 1;
+ and_index++;
+ }
+ /* cluster starts and ends will be separated because they should
+ * hold their position */
+ if (tab->fe[i].ctrl & RQFCR_CLE)
+ block_index++;
+ /* A not set AND indicates the end of a depended block */
+ if (!(tab->fe[i].ctrl & RQFCR_AND))
+ block_index++;
+
+ }
+
+ mask_table[and_index - 1].end = i - 1;
+
+ return and_index;
+}
+
+/*
+ * Sorts the entries of mask_table by the values of the masks.
+ * Important: The 0xFF80 flags of the first and last entry of a
+ * block must hold their position (which queue, CLusterEnable, ReJEct,
+ * AND)
+ */
+static void gfar_sort_mask_table(struct gfar_mask_entry *mask_table,
+ struct filer_table *temp_table, u32 and_index)
+{
+ /* Pointer to compare function (_asc or _desc) */
+ int (*gfar_comp)(const void *, const void *);
+
+ u32 i, size = 0, start = 0, prev = 1;
+ u32 old_first, old_last, new_first, new_last;
+
+ gfar_comp = &gfar_comp_desc;
+
+ for (i = 0; i < and_index; i++) {
+
+ if (prev != mask_table[i].block) {
+ old_first = mask_table[start].start + 1;
+ old_last = mask_table[i - 1].end;
+ sort(mask_table + start, size,
+ sizeof(struct gfar_mask_entry),
+ gfar_comp, &gfar_swap);
+
+ /* Toggle order for every block. This makes the
+ * thing more efficient! */
+ if (gfar_comp == gfar_comp_desc)
+ gfar_comp = &gfar_comp_asc;
+ else
+ gfar_comp = &gfar_comp_desc;
+
+ new_first = mask_table[start].start + 1;
+ new_last = mask_table[i - 1].end;
+
+ gfar_swap_bits(&temp_table->fe[new_first],
+ &temp_table->fe[old_first],
+ &temp_table->fe[new_last],
+ &temp_table->fe[old_last],
+ RQFCR_QUEUE | RQFCR_CLE |
+ RQFCR_RJE | RQFCR_AND
+ );
+
+ start = i;
+ size = 0;
+ }
+ size++;
+ prev = mask_table[i].block;
+ }
+
+}
+
+/*
+ * Reduces the number of masks needed in the filer table to save entries
+ * This is done by sorting the masks of a depended block. A depended block is
+ * identified by gluing ANDs or CLE. The sorting order toggles after every
+ * block. Of course entries in scope of a mask must change their location with
+ * it.
+ */
+static int gfar_optimize_filer_masks(struct filer_table *tab)
+{
+ struct filer_table *temp_table;
+ struct gfar_mask_entry *mask_table;
+
+ u32 and_index = 0, previous_mask = 0, i = 0, j = 0, size = 0;
+ s32 ret = 0;
+
+ /* We need a copy of the filer table because
+ * we want to change its order */
+ temp_table = kmalloc(sizeof(*temp_table), GFP_KERNEL);
+ if (temp_table == NULL)
+ return -ENOMEM;
+ memcpy(temp_table, tab, sizeof(*temp_table));
+
+ mask_table = kcalloc(MAX_FILER_CACHE_IDX / 2 + 1,
+ sizeof(struct gfar_mask_entry), GFP_KERNEL);
+
+ if (mask_table == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ and_index = gfar_generate_mask_table(mask_table, tab);
+
+ gfar_sort_mask_table(mask_table, temp_table, and_index);
+
+ /* Now we can copy the data from our duplicated filer table to
+ * the real one in the order the mask table says */
+ for (i = 0; i < and_index; i++) {
+ size = mask_table[i].end - mask_table[i].start + 1;
+ gfar_copy_filer_entries(&(tab->fe[j]),
+ &(temp_table->fe[mask_table[i].start]), size);
+ j += size;
+ }
+
+ /* And finally we just have to check for duplicated masks and drop the
+ * second ones */
+ for (i = 0; i < tab->index && i < MAX_FILER_CACHE_IDX; i++) {
+ if (tab->fe[i].ctrl == 0x80) {
+ previous_mask = i++;
+ break;
+ }
+ }
+ for (; i < tab->index && i < MAX_FILER_CACHE_IDX; i++) {
+ if (tab->fe[i].ctrl == 0x80) {
+ if (tab->fe[i].prop == tab->fe[previous_mask].prop) {
+ /* Two identical ones found!
+ * So drop the second one! */
+ gfar_trim_filer_entries(i, i, tab);
+ } else
+ /* Not identical! */
+ previous_mask = i;
+ }
+ }
+
+ kfree(mask_table);
+end: kfree(temp_table);
+ return ret;
+}
+
+/* Write the bit-pattern from software's buffer to hardware registers */
+static int gfar_write_filer_table(struct gfar_private *priv,
+ struct filer_table *tab)
+{
+ u32 i = 0;
+ if (tab->index > MAX_FILER_IDX - 1)
+ return -EBUSY;
+
+ /* Avoid inconsistent filer table to be processed */
+ lock_rx_qs(priv);
+
+ /* Fill regular entries */
+ for (; i < MAX_FILER_IDX - 1 && (tab->fe[i].ctrl | tab->fe[i].ctrl); i++)
+ gfar_write_filer(priv, i, tab->fe[i].ctrl, tab->fe[i].prop);
+ /* Fill the rest with fall-troughs */
+ for (; i < MAX_FILER_IDX - 1; i++)
+ gfar_write_filer(priv, i, 0x60, 0xFFFFFFFF);
+ /* Last entry must be default accept
+ * because that's what people expect */
+ gfar_write_filer(priv, i, 0x20, 0x0);
+
+ unlock_rx_qs(priv);
+
+ return 0;
+}
+
+static int gfar_check_capability(struct ethtool_rx_flow_spec *flow,
+ struct gfar_private *priv)
+{
+
+ if (flow->flow_type & FLOW_EXT) {
+ if (~flow->m_ext.data[0] || ~flow->m_ext.data[1])
+ netdev_warn(priv->ndev,
+ "User-specific data not supported!\n");
+ if (~flow->m_ext.vlan_etype)
+ netdev_warn(priv->ndev,
+ "VLAN-etype not supported!\n");
+ }
+ if (flow->flow_type == IP_USER_FLOW)
+ if (flow->h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4)
+ netdev_warn(priv->ndev,
+ "IP-Version differing from IPv4 not supported!\n");
+
+ return 0;
+}
+
+static int gfar_process_filer_changes(struct gfar_private *priv)
+{
+ struct ethtool_flow_spec_container *j;
+ struct filer_table *tab;
+ s32 i = 0;
+ s32 ret = 0;
+
+ /* So index is set to zero, too! */
+ tab = kzalloc(sizeof(*tab), GFP_KERNEL);
+ if (tab == NULL)
+ return -ENOMEM;
+
+ /* Now convert the existing filer data from flow_spec into
+ * filer tables binary format */
+ list_for_each_entry(j, &priv->rx_list.list, list) {
+ ret = gfar_convert_to_filer(&j->fs, tab);
+ if (ret == -EBUSY) {
+ netdev_err(priv->ndev, "Rule not added: No free space!\n");
+ goto end;
+ }
+ if (ret == -1) {
+ netdev_err(priv->ndev, "Rule not added: Unsupported Flow-type!\n");
+ goto end;
+ }
+ }
+
+ i = tab->index;
+
+ /* Optimizations to save entries */
+ gfar_cluster_filer(tab);
+ gfar_optimize_filer_masks(tab);
+
+ pr_debug("\n\tSummary:\n"
+ "\tData on hardware: %d\n"
+ "\tCompression rate: %d%%\n",
+ tab->index, 100 - (100 * tab->index) / i);
+
+ /* Write everything to hardware */
+ ret = gfar_write_filer_table(priv, tab);
+ if (ret == -EBUSY) {
+ netdev_err(priv->ndev, "Rule not added: No free space!\n");
+ goto end;
+ }
+
+end: kfree(tab);
+ return ret;
+}
+
+static void gfar_invert_masks(struct ethtool_rx_flow_spec *flow)
+{
+ u32 i = 0;
+
+ for (i = 0; i < sizeof(flow->m_u); i++)
+ flow->m_u.hdata[i] ^= 0xFF;
+
+ flow->m_ext.vlan_etype ^= 0xFFFF;
+ flow->m_ext.vlan_tci ^= 0xFFFF;
+ flow->m_ext.data[0] ^= ~0;
+ flow->m_ext.data[1] ^= ~0;
+}
+
+static int gfar_add_cls(struct gfar_private *priv,
+ struct ethtool_rx_flow_spec *flow)
+{
+ struct ethtool_flow_spec_container *temp, *comp;
+ int ret = 0;
+
+ temp = kmalloc(sizeof(*temp), GFP_KERNEL);
+ if (temp == NULL)
+ return -ENOMEM;
+ memcpy(&temp->fs, flow, sizeof(temp->fs));
+
+ gfar_invert_masks(&temp->fs);
+ ret = gfar_check_capability(&temp->fs, priv);
+ if (ret)
+ goto clean_mem;
+ /* Link in the new element at the right @location */
+ if (list_empty(&priv->rx_list.list)) {
+ ret = gfar_check_filer_hardware(priv);
+ if (ret != 0)
+ goto clean_mem;
+ list_add(&temp->list, &priv->rx_list.list);
+ goto process;
+ } else {
+
+ list_for_each_entry(comp, &priv->rx_list.list, list) {
+ if (comp->fs.location > flow->location) {
+ list_add_tail(&temp->list, &comp->list);
+ goto process;
+ }
+ if (comp->fs.location == flow->location) {
+ netdev_err(priv->ndev,
+ "Rule not added: ID %d not free!\n",
+ flow->location);
+ ret = -EBUSY;
+ goto clean_mem;
+ }
+ }
+ list_add_tail(&temp->list, &priv->rx_list.list);
+ }
+
+process:
+ ret = gfar_process_filer_changes(priv);
+ if (ret)
+ goto clean_list;
+ priv->rx_list.count++;
+ return ret;
+
+clean_list:
+ list_del(&temp->list);
+clean_mem:
+ kfree(temp);
+ return ret;
+}
+
+static int gfar_del_cls(struct gfar_private *priv, u32 loc)
+{
+ struct ethtool_flow_spec_container *comp;
+ u32 ret = -EINVAL;
+
+ if (list_empty(&priv->rx_list.list))
+ return ret;
+
+ list_for_each_entry(comp, &priv->rx_list.list, list) {
+ if (comp->fs.location == loc) {
+ list_del(&comp->list);
+ kfree(comp);
+ priv->rx_list.count--;
+ gfar_process_filer_changes(priv);
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+
+}
+
+static int gfar_get_cls(struct gfar_private *priv, struct ethtool_rxnfc *cmd)
+{
+ struct ethtool_flow_spec_container *comp;
+ u32 ret = -EINVAL;
+
+ list_for_each_entry(comp, &priv->rx_list.list, list) {
+ if (comp->fs.location == cmd->fs.location) {
+ memcpy(&cmd->fs, &comp->fs, sizeof(cmd->fs));
+ gfar_invert_masks(&cmd->fs);
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int gfar_get_cls_all(struct gfar_private *priv,
+ struct ethtool_rxnfc *cmd, u32 *rule_locs)
+{
+ struct ethtool_flow_spec_container *comp;
+ u32 i = 0;
+
+ list_for_each_entry(comp, &priv->rx_list.list, list) {
+ if (i == cmd->rule_cnt)
+ return -EMSGSIZE;
+ rule_locs[i] = comp->fs.location;
+ i++;
+ }
+
+ cmd->data = MAX_FILER_IDX;
+ cmd->rule_cnt = i;
+
+ return 0;
+}
+
+static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ int ret = 0;
+
+ mutex_lock(&priv->rx_queue_access);
+
+ switch (cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ ret = gfar_set_hash_opts(priv, cmd);
+ break;
+ case ETHTOOL_SRXCLSRLINS:
+ if (cmd->fs.ring_cookie != RX_CLS_FLOW_DISC &&
+ cmd->fs.ring_cookie >= priv->num_rx_queues) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = gfar_add_cls(priv, &cmd->fs);
+ break;
+ case ETHTOOL_SRXCLSRLDEL:
+ ret = gfar_del_cls(priv, cmd->fs.location);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&priv->rx_queue_access);
+
+ return ret;
+}
+
+static int gfar_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
+ u32 *rule_locs)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ int ret = 0;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXRINGS:
+ cmd->data = priv->num_rx_queues;
+ break;
+ case ETHTOOL_GRXCLSRLCNT:
+ cmd->rule_cnt = priv->rx_list.count;
+ break;
+ case ETHTOOL_GRXCLSRULE:
+ ret = gfar_get_cls(priv, cmd);
+ break;
+ case ETHTOOL_GRXCLSRLALL:
+ ret = gfar_get_cls_all(priv, cmd, rule_locs);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+const struct ethtool_ops gfar_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .set_settings = gfar_ssettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_coalesce = gfar_gcoalesce,
+ .set_coalesce = gfar_scoalesce,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings,
+ .get_sset_count = gfar_sset_count,
+ .get_ethtool_stats = gfar_fill_stats,
+ .get_msglevel = gfar_get_msglevel,
+ .set_msglevel = gfar_set_msglevel,
+#ifdef CONFIG_PM
+ .get_wol = gfar_get_wol,
+ .set_wol = gfar_set_wol,
+#endif
+ .set_rxnfc = gfar_set_nfc,
+ .get_rxnfc = gfar_get_nfc,
+};
diff --git a/drivers/net/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c
index d8e175382d1d..f67b8aebc89c 100644
--- a/drivers/net/gianfar_ptp.c
+++ b/drivers/net/ethernet/freescale/gianfar_ptp.c
@@ -193,14 +193,9 @@ static void set_alarm(struct etsects *etsects)
/* Caller must hold etsects->lock. */
static void set_fipers(struct etsects *etsects)
{
- u32 tmr_ctrl = gfar_read(&etsects->regs->tmr_ctrl);
-
- gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl & (~TE));
- gfar_write(&etsects->regs->tmr_prsc, etsects->tmr_prsc);
+ set_alarm(etsects);
gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
- set_alarm(etsects);
- gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|TE);
}
/*
@@ -491,7 +486,7 @@ static int gianfar_ptp_probe(struct platform_device *dev)
spin_lock_init(&etsects->lock);
etsects->regs = ioremap(etsects->rsrc->start,
- 1 + etsects->rsrc->end - etsects->rsrc->start);
+ resource_size(etsects->rsrc));
if (!etsects->regs) {
pr_err("ioremap ptp registers failed\n");
goto no_ioremap;
@@ -511,7 +506,7 @@ static int gianfar_ptp_probe(struct platform_device *dev)
gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
set_alarm(etsects);
- gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|FS|RTPE|TE);
+ gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|FS|RTPE|TE|FRD);
spin_unlock_irqrestore(&etsects->lock, flags);
diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/ethernet/freescale/gianfar_sysfs.c
index 64f4094ac7f1..64f4094ac7f1 100644
--- a/drivers/net/gianfar_sysfs.c
+++ b/drivers/net/ethernet/freescale/gianfar_sysfs.c
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index ef041057d9d3..b5dc0273a1d1 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -17,6 +17,7 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/stddef.h>
+#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -1761,10 +1762,12 @@ static int init_phy(struct net_device *dev)
if (priv->phy_interface == PHY_INTERFACE_MODE_SGMII)
uec_configure_serdes(dev);
- phydev->supported &= (ADVERTISED_10baseT_Half |
- ADVERTISED_10baseT_Full |
- ADVERTISED_100baseT_Half |
- ADVERTISED_100baseT_Full);
+ phydev->supported &= (SUPPORTED_MII |
+ SUPPORTED_Autoneg |
+ ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full);
if (priv->max_speed == SPEED_1000)
phydev->supported |= ADVERTISED_1000baseT_Full;
@@ -2030,11 +2033,6 @@ static void ucc_geth_set_multi(struct net_device *dev)
out_be32(&p_82xx_addr_filt->gaddr_l, 0x0);
netdev_for_each_mc_addr(ha, dev) {
- /* Only support group multicast for now.
- */
- if (!is_multicast_ether_addr(ha->addr))
- continue;
-
/* Ask CPM to run CRC and set bit in
* filter mask.
*/
@@ -3165,6 +3163,8 @@ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev)
ugeth->txBd[txQ] = bd;
+ skb_tx_timestamp(skb);
+
if (ugeth->p_scheduler) {
ugeth->cpucount[txQ]++;
/* Indicate to QE that there are more Tx bds ready for
@@ -3732,7 +3732,7 @@ static const struct net_device_ops ucc_geth_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = ucc_geth_set_mac_addr,
.ndo_change_mtu = eth_change_mtu,
- .ndo_set_multicast_list = ucc_geth_set_multi,
+ .ndo_set_rx_mode = ucc_geth_set_multi,
.ndo_tx_timeout = ucc_geth_timeout,
.ndo_do_ioctl = ucc_geth_ioctl,
#ifdef CONFIG_NET_POLL_CONTROLLER
diff --git a/drivers/net/ucc_geth.h b/drivers/net/ethernet/freescale/ucc_geth.h
index d12fcad145e9..d12fcad145e9 100644
--- a/drivers/net/ucc_geth.h
+++ b/drivers/net/ethernet/freescale/ucc_geth.h
diff --git a/drivers/net/ucc_geth_ethtool.c b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c
index a97257f91a3d..a97257f91a3d 100644
--- a/drivers/net/ucc_geth_ethtool.c
+++ b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c
diff --git a/drivers/net/ethernet/fujitsu/Kconfig b/drivers/net/ethernet/fujitsu/Kconfig
new file mode 100644
index 000000000000..dffee9d44fd5
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/Kconfig
@@ -0,0 +1,54 @@
+#
+# Fujitsu Network device configuration
+#
+
+config NET_VENDOR_FUJITSU
+ bool "Fujitsu devices"
+ default y
+ depends on ISA || PCMCIA || ((ISA || MCA_LEGACY) && EXPERIMENTAL)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ the questions about Fujitsu cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_FUJITSU
+
+config AT1700
+ tristate "AT1700/1720 support (EXPERIMENTAL)"
+ depends on (ISA || MCA_LEGACY) && EXPERIMENTAL
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called at1700.
+
+config PCMCIA_FMVJ18X
+ tristate "Fujitsu FMV-J18x PCMCIA support"
+ depends on PCMCIA
+ select CRC32
+ ---help---
+ Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible
+ PCMCIA (PC-card) Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called fmvj18x_cs. If unsure, say N.
+
+config ETH16I
+ tristate "ICL EtherTeam 16i/32 support"
+ depends on ISA
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called eth16i.
+
+endif # NET_VENDOR_FUJITSU
diff --git a/drivers/net/ethernet/fujitsu/Makefile b/drivers/net/ethernet/fujitsu/Makefile
new file mode 100644
index 000000000000..2730ae67d3aa
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Fujitsu network device drivers.
+#
+
+obj-$(CONFIG_AT1700) += at1700.o
+obj-$(CONFIG_ETH16I) += eth16i.o
+obj-$(CONFIG_PCMCIA_FMVJ18X) += fmvj18x_cs.o
diff --git a/drivers/net/at1700.c b/drivers/net/ethernet/fujitsu/at1700.c
index 65a78f965dd2..7c6c908bdf02 100644
--- a/drivers/net/at1700.c
+++ b/drivers/net/ethernet/fujitsu/at1700.c
@@ -253,7 +253,7 @@ static const struct net_device_ops at1700_netdev_ops = {
.ndo_open = net_open,
.ndo_stop = net_close,
.ndo_start_xmit = net_send_packet,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_tx_timeout = net_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/eth16i.c b/drivers/net/ethernet/fujitsu/eth16i.c
index 12d28e9d0cb7..b0e2313af3d1 100644
--- a/drivers/net/eth16i.c
+++ b/drivers/net/ethernet/fujitsu/eth16i.c
@@ -478,7 +478,7 @@ static const struct net_device_ops eth16i_netdev_ops = {
.ndo_open = eth16i_open,
.ndo_stop = eth16i_close,
.ndo_start_xmit = eth16i_tx,
- .ndo_set_multicast_list = eth16i_multicast,
+ .ndo_set_rx_mode = eth16i_multicast,
.ndo_tx_timeout = eth16i_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
index 723815e7a997..15416752c13e 100644
--- a/drivers/net/pcmcia/fmvj18x_cs.c
+++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
@@ -226,7 +226,7 @@ static const struct net_device_ops fjn_netdev_ops = {
.ndo_start_xmit = fjn_start_xmit,
.ndo_tx_timeout = fjn_tx_timeout,
.ndo_set_config = fjn_config,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/ethernet/hp/Kconfig b/drivers/net/ethernet/hp/Kconfig
new file mode 100644
index 000000000000..a0b8ece1e3bc
--- /dev/null
+++ b/drivers/net/ethernet/hp/Kconfig
@@ -0,0 +1,32 @@
+#
+# HP network device configuration
+#
+
+config NET_VENDOR_HP
+ bool "HP devices"
+ default y
+ depends on ISA || EISA || PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about HP cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_HP
+
+config HP100
+ tristate "HP 10/100VG PCLAN (ISA, EISA, PCI) support"
+ depends on (ISA || EISA || PCI)
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called hp100.
+
+endif # NET_VENDOR_HP
diff --git a/drivers/net/ethernet/hp/Makefile b/drivers/net/ethernet/hp/Makefile
new file mode 100644
index 000000000000..20b6918b52bd
--- /dev/null
+++ b/drivers/net/ethernet/hp/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HP network device drivers.
+#
+
+obj-$(CONFIG_HP100) += hp100.o
diff --git a/drivers/net/hp100.c b/drivers/net/ethernet/hp/hp100.c
index c3ecb118c1df..6a5ee0776b28 100644
--- a/drivers/net/hp100.c
+++ b/drivers/net/ethernet/hp/hp100.c
@@ -430,7 +430,7 @@ static const struct net_device_ops hp100_bm_netdev_ops = {
.ndo_stop = hp100_close,
.ndo_start_xmit = hp100_start_xmit_bm,
.ndo_get_stats = hp100_get_stats,
- .ndo_set_multicast_list = hp100_set_multicast_list,
+ .ndo_set_rx_mode = hp100_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
@@ -441,7 +441,7 @@ static const struct net_device_ops hp100_netdev_ops = {
.ndo_stop = hp100_close,
.ndo_start_xmit = hp100_start_xmit,
.ndo_get_stats = hp100_get_stats,
- .ndo_set_multicast_list = hp100_set_multicast_list,
+ .ndo_set_rx_mode = hp100_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
@@ -2103,20 +2103,18 @@ static void hp100_set_multicast_list(struct net_device *dev)
#endif
netdev_for_each_mc_addr(ha, dev) {
addrs = ha->addr;
- if ((*addrs & 0x01) == 0x01) { /* multicast address? */
#ifdef HP100_DEBUG
- printk("hp100: %s: multicast = %pM, ",
- dev->name, addrs);
+ printk("hp100: %s: multicast = %pM, ",
+ dev->name, addrs);
#endif
- for (i = idx = 0; i < 6; i++) {
- idx ^= *addrs++ & 0x3f;
- printk(":%02x:", idx);
- }
+ for (i = idx = 0; i < 6; i++) {
+ idx ^= *addrs++ & 0x3f;
+ printk(":%02x:", idx);
+ }
#ifdef HP100_DEBUG
- printk("idx = %i\n", idx);
+ printk("idx = %i\n", idx);
#endif
- lp->hash_bytes[idx >> 3] |= (1 << (idx & 7));
- }
+ lp->hash_bytes[idx >> 3] |= (1 << (idx & 7));
}
}
#else
diff --git a/drivers/net/hp100.h b/drivers/net/ethernet/hp/hp100.h
index b60e96fe38b4..b60e96fe38b4 100644
--- a/drivers/net/hp100.h
+++ b/drivers/net/ethernet/hp/hp100.h
diff --git a/drivers/net/3c505.c b/drivers/net/ethernet/i825xx/3c505.c
index 88d766ee0e1b..ba82a266051d 100644
--- a/drivers/net/3c505.c
+++ b/drivers/net/ethernet/i825xx/3c505.c
@@ -126,15 +126,13 @@
*
*********************************************************/
-#define filename __FILE__
-
#define timeout_msg "*** timeout at %s:%s (line %d) ***\n"
#define TIMEOUT_MSG(lineno) \
- pr_notice(timeout_msg, filename, __func__, (lineno))
+ pr_notice(timeout_msg, __FILE__, __func__, (lineno))
#define invalid_pcb_msg "*** invalid pcb length %d at %s:%s (line %d) ***\n"
#define INVALID_PCB_MSG(len) \
- pr_notice(invalid_pcb_msg, (len), filename, __func__, __LINE__)
+ pr_notice(invalid_pcb_msg, (len), __FILE__, __func__, __LINE__)
#define search_msg "%s: Looking for 3c505 adapter at address %#x..."
@@ -1363,7 +1361,7 @@ static const struct net_device_ops elp_netdev_ops = {
.ndo_get_stats = elp_get_stats,
.ndo_start_xmit = elp_start_xmit,
.ndo_tx_timeout = elp_timeout,
- .ndo_set_multicast_list = elp_set_mc_list,
+ .ndo_set_rx_mode = elp_set_mc_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/3c505.h b/drivers/net/ethernet/i825xx/3c505.h
index 04df2a9002b6..04df2a9002b6 100644
--- a/drivers/net/3c505.h
+++ b/drivers/net/ethernet/i825xx/3c505.h
diff --git a/drivers/net/3c507.c b/drivers/net/ethernet/i825xx/3c507.c
index 1e945551c144..1e945551c144 100644
--- a/drivers/net/3c507.c
+++ b/drivers/net/ethernet/i825xx/3c507.c
diff --git a/drivers/net/3c523.c b/drivers/net/ethernet/i825xx/3c523.c
index bc0d1a1c2e28..d70d3df4c985 100644
--- a/drivers/net/3c523.c
+++ b/drivers/net/ethernet/i825xx/3c523.c
@@ -409,7 +409,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_start_xmit = elmc_send_packet,
.ndo_tx_timeout = elmc_timeout,
#ifdef ELMC_MULTICAST
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
#endif
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/3c523.h b/drivers/net/ethernet/i825xx/3c523.h
index 6956441687b9..6956441687b9 100644
--- a/drivers/net/3c523.h
+++ b/drivers/net/ethernet/i825xx/3c523.h
diff --git a/drivers/net/3c527.c b/drivers/net/ethernet/i825xx/3c527.c
index d9d056d207f3..474b5e71a53a 100644
--- a/drivers/net/3c527.c
+++ b/drivers/net/ethernet/i825xx/3c527.c
@@ -292,7 +292,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_stop = mc32_close,
.ndo_start_xmit = mc32_send_packet,
.ndo_get_stats = mc32_get_stats,
- .ndo_set_multicast_list = mc32_set_multicast_list,
+ .ndo_set_rx_mode = mc32_set_multicast_list,
.ndo_tx_timeout = mc32_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/3c527.h b/drivers/net/ethernet/i825xx/3c527.h
index d693b8d15cde..d693b8d15cde 100644
--- a/drivers/net/3c527.h
+++ b/drivers/net/ethernet/i825xx/3c527.h
diff --git a/drivers/net/82596.c b/drivers/net/ethernet/i825xx/82596.c
index be1f1970c842..f2408a4d5d9c 100644
--- a/drivers/net/82596.c
+++ b/drivers/net/ethernet/i825xx/82596.c
@@ -1145,7 +1145,7 @@ static const struct net_device_ops i596_netdev_ops = {
.ndo_open = i596_open,
.ndo_stop = i596_close,
.ndo_start_xmit = i596_start_xmit,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_tx_timeout = i596_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/ethernet/i825xx/Kconfig b/drivers/net/ethernet/i825xx/Kconfig
new file mode 100644
index 000000000000..ca1ae985c6df
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/Kconfig
@@ -0,0 +1,183 @@
+#
+# Intel 82596/82593/82596 network device configuration
+#
+
+config NET_VENDOR_I825XX
+ bool "Intel (82586/82593/82596) devices"
+ default y
+ depends on NET_VENDOR_INTEL && (ISA || ISA_DMA_API || ARM || \
+ ARCH_ACORN || MCA || MCA_LEGACY || SNI_RM || SUN3 || \
+ GSC || BVME6000 || MVME16x || EXPERIMENTAL)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question does not directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about these devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_I825XX
+
+config ELPLUS
+ tristate "3c505 \"EtherLink Plus\" support"
+ depends on ISA && ISA_DMA_API
+ ---help---
+ Information about this network (Ethernet) card can be found in
+ <file:Documentation/networking/3c505.txt>. If you have a card of
+ this type, say Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c505.
+
+config EL16
+ tristate "3c507 \"EtherLink 16\" support (EXPERIMENTAL)"
+ depends on ISA && EXPERIMENTAL
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c507.
+
+config ELMC
+ tristate "3c523 \"EtherLink/MC\" support"
+ depends on MCA_LEGACY
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c523.
+
+config ELMC_II
+ tristate "3c527 \"EtherLink/MC 32\" support (EXPERIMENTAL)"
+ depends on MCA && MCA_LEGACY
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c527.
+
+config ARM_ETHER1
+ tristate "Acorn Ether1 support"
+ depends on ARM && ARCH_ACORN
+ ---help---
+ If you have an Acorn system with one of these (AKA25) network cards,
+ you should say Y to this option if you wish to use it with Linux.
+
+config APRICOT
+ tristate "Apricot Xen-II on board Ethernet"
+ depends on ISA
+ ---help---
+ If you have a network (Ethernet) controller of this type, say Y and
+ read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called apricot.
+
+config BVME6000_NET
+ tristate "BVME6000 Ethernet support"
+ depends on BVME6000
+ ---help---
+ This is the driver for the Ethernet interface on BVME4000 and
+ BVME6000 VME boards. Say Y here to include the driver for this chip
+ in your kernel.
+ To compile this driver as a module, choose M here.
+
+config EEXPRESS
+ tristate "EtherExpress 16 support"
+ depends on ISA
+ ---help---
+ If you have an EtherExpress16 network (Ethernet) card, say Y and
+ read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Note that the Intel
+ EtherExpress16 card used to be regarded as a very poor choice
+ because the driver was very unreliable. We now have a new driver
+ that should do better.
+
+ To compile this driver as a module, choose M here. The module
+ will be called eexpress.
+
+config EEXPRESS_PRO
+ tristate "EtherExpressPro support/EtherExpress 10 (i82595) support"
+ depends on ISA
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y. This
+ driver supports Intel i82595{FX,TX} based boards. Note however
+ that the EtherExpress PRO/100 Ethernet card has its own separate
+ driver. Please read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called eepro.
+
+config LASI_82596
+ tristate "Lasi ethernet"
+ depends on GSC
+ ---help---
+ Say Y here to support the builtin Intel 82596 ethernet controller
+ found in Hewlett-Packard PA-RISC machines with 10Mbit ethernet.
+
+config LP486E
+ tristate "LP486E on board Ethernet"
+ depends on ISA
+ ---help---
+ Say Y here to support the 82596-based on-board Ethernet controller
+ for the Panther motherboard, which is one of the two shipped in the
+ Intel Professional Workstation.
+
+config MVME16x_NET
+ tristate "MVME16x Ethernet support"
+ depends on MVME16x
+ ---help---
+ This is the driver for the Ethernet interface on the Motorola
+ MVME162, 166, 167, 172 and 177 boards. Say Y here to include the
+ driver for this chip in your kernel.
+ To compile this driver as a module, choose M here.
+
+config NI52
+ tristate "NI5210 support"
+ depends on ISA
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ni52.
+
+config SNI_82596
+ tristate "SNI RM ethernet"
+ depends on SNI_RM
+ ---help---
+ Say Y here to support the on-board Intel 82596 ethernet controller
+ built into SNI RM machines.
+
+config SUN3_82586
+ bool "Sun3 on-board Intel 82586 support"
+ depends on SUN3
+ ---help---
+ This driver enables support for the on-board Intel 82586 based
+ Ethernet adapter found on Sun 3/1xx and 3/2xx motherboards. Note
+ that this driver does not support 82586-based adapters on additional
+ VME boards.
+
+config ZNET
+ tristate "Zenith Z-Note support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && ISA_DMA_API
+ ---help---
+ The Zenith Z-Note notebook computer has a built-in network
+ (Ethernet) card, and this is the Linux driver for it. Note that the
+ IBM Thinkpad 300 is compatible with the Z-Note and is also supported
+ by this driver. Read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+endif # NET_VENDOR_I825XX
diff --git a/drivers/net/ethernet/i825xx/Makefile b/drivers/net/ethernet/i825xx/Makefile
new file mode 100644
index 000000000000..f68a3694968a
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the Intel 82586/82593/82596 chipset device drivers.
+#
+
+obj-$(CONFIG_ARM_ETHER1) += ether1.o
+obj-$(CONFIG_EEXPRESS) += eexpress.o
+obj-$(CONFIG_EEXPRESS_PRO) += eepro.o
+obj-$(CONFIG_ELPLUS) += 3c505.o
+obj-$(CONFIG_EL16) += 3c507.o
+obj-$(CONFIG_ELMC) += 3c523.o
+obj-$(CONFIG_ELMC_II) += 3c527.o
+obj-$(CONFIG_LP486E) += lp486e.o
+obj-$(CONFIG_NI52) += ni52.o
+obj-$(CONFIG_SUN3_82586) += sun3_82586.o
+obj-$(CONFIG_ZNET) += znet.o
+obj-$(CONFIG_APRICOT) += 82596.o
+obj-$(CONFIG_LASI_82596) += lasi_82596.o
+obj-$(CONFIG_SNI_82596) += sni_82596.o
+obj-$(CONFIG_MVME16x_NET) += 82596.o
+obj-$(CONFIG_BVME6000_NET) += 82596.o
diff --git a/drivers/net/eepro.c b/drivers/net/ethernet/i825xx/eepro.c
index dfeb006035df..067c46069a11 100644
--- a/drivers/net/eepro.c
+++ b/drivers/net/ethernet/i825xx/eepro.c
@@ -743,7 +743,7 @@ static const struct net_device_ops eepro_netdev_ops = {
.ndo_open = eepro_open,
.ndo_stop = eepro_close,
.ndo_start_xmit = eepro_send_packet,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_tx_timeout = eepro_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/eexpress.c b/drivers/net/ethernet/i825xx/eexpress.c
index 48ee51bb9e50..3a9580f3d4dd 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/ethernet/i825xx/eexpress.c
@@ -365,7 +365,7 @@ static int __init do_express_probe(struct net_device *dev)
dev->irq = mca_irqmap[(pos1>>4)&0x7];
/*
- * XXX: Transciever selection is done
+ * XXX: Transceiver selection is done
* differently on the MCA version.
* How to get it to select something
* other than external/AUI is currently
@@ -1047,7 +1047,7 @@ static const struct net_device_ops eexp_netdev_ops = {
.ndo_open = eexp_open,
.ndo_stop = eexp_close,
.ndo_start_xmit = eexp_xmit,
- .ndo_set_multicast_list = eexp_set_multicast,
+ .ndo_set_rx_mode = eexp_set_multicast,
.ndo_tx_timeout = eexp_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/eexpress.h b/drivers/net/ethernet/i825xx/eexpress.h
index dc9c6ea289e9..dc9c6ea289e9 100644
--- a/drivers/net/eexpress.h
+++ b/drivers/net/ethernet/i825xx/eexpress.h
diff --git a/drivers/net/arm/ether1.c b/drivers/net/ethernet/i825xx/ether1.c
index b00781c02d5d..42e90a97c7a5 100644
--- a/drivers/net/arm/ether1.c
+++ b/drivers/net/ethernet/i825xx/ether1.c
@@ -985,7 +985,7 @@ static const struct net_device_ops ether1_netdev_ops = {
.ndo_open = ether1_open,
.ndo_stop = ether1_close,
.ndo_start_xmit = ether1_sendpacket,
- .ndo_set_multicast_list = ether1_setmulticastlist,
+ .ndo_set_rx_mode = ether1_setmulticastlist,
.ndo_tx_timeout = ether1_timeout,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/arm/ether1.h b/drivers/net/ethernet/i825xx/ether1.h
index 3a5830ab3dc7..3a5830ab3dc7 100644
--- a/drivers/net/arm/ether1.h
+++ b/drivers/net/ethernet/i825xx/ether1.h
diff --git a/drivers/net/lasi_82596.c b/drivers/net/ethernet/i825xx/lasi_82596.c
index 6eba352c52e0..6eba352c52e0 100644
--- a/drivers/net/lasi_82596.c
+++ b/drivers/net/ethernet/i825xx/lasi_82596.c
diff --git a/drivers/net/lib82596.c b/drivers/net/ethernet/i825xx/lib82596.c
index 9e042894479b..3efbd8dbb63d 100644
--- a/drivers/net/lib82596.c
+++ b/drivers/net/ethernet/i825xx/lib82596.c
@@ -1038,7 +1038,7 @@ static const struct net_device_ops i596_netdev_ops = {
.ndo_open = i596_open,
.ndo_stop = i596_close,
.ndo_start_xmit = i596_start_xmit,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_tx_timeout = i596_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/lp486e.c b/drivers/net/ethernet/i825xx/lp486e.c
index 385a95311cd2..414044b3cb11 100644
--- a/drivers/net/lp486e.c
+++ b/drivers/net/ethernet/i825xx/lp486e.c
@@ -954,7 +954,7 @@ static const struct net_device_ops i596_netdev_ops = {
.ndo_open = i596_open,
.ndo_stop = i596_close,
.ndo_start_xmit = i596_start_xmit,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_tx_timeout = i596_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/ni52.c b/drivers/net/ethernet/i825xx/ni52.c
index d973fc6c6b88..c0893715ef47 100644
--- a/drivers/net/ni52.c
+++ b/drivers/net/ethernet/i825xx/ni52.c
@@ -445,7 +445,7 @@ static const struct net_device_ops ni52_netdev_ops = {
.ndo_get_stats = ni52_get_stats,
.ndo_tx_timeout = ni52_timeout,
.ndo_start_xmit = ni52_send_packet,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/ni52.h b/drivers/net/ethernet/i825xx/ni52.h
index 0a03b2883327..0a03b2883327 100644
--- a/drivers/net/ni52.h
+++ b/drivers/net/ethernet/i825xx/ni52.h
diff --git a/drivers/net/sni_82596.c b/drivers/net/ethernet/i825xx/sni_82596.c
index 6b2a88817473..6b2a88817473 100644
--- a/drivers/net/sni_82596.c
+++ b/drivers/net/ethernet/i825xx/sni_82596.c
diff --git a/drivers/net/sun3_82586.c b/drivers/net/ethernet/i825xx/sun3_82586.c
index b6ae53bada75..6ef5e11d1c84 100644
--- a/drivers/net/sun3_82586.c
+++ b/drivers/net/ethernet/i825xx/sun3_82586.c
@@ -333,7 +333,7 @@ static const struct net_device_ops sun3_82586_netdev_ops = {
.ndo_open = sun3_82586_open,
.ndo_stop = sun3_82586_close,
.ndo_start_xmit = sun3_82586_send_packet,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_tx_timeout = sun3_82586_timeout,
.ndo_get_stats = sun3_82586_get_stats,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/sun3_82586.h b/drivers/net/ethernet/i825xx/sun3_82586.h
index 93346f00486b..93346f00486b 100644
--- a/drivers/net/sun3_82586.h
+++ b/drivers/net/ethernet/i825xx/sun3_82586.h
diff --git a/drivers/net/znet.c b/drivers/net/ethernet/i825xx/znet.c
index ec2800ff8d42..962b4c421f3f 100644
--- a/drivers/net/znet.c
+++ b/drivers/net/ethernet/i825xx/znet.c
@@ -356,7 +356,7 @@ static const struct net_device_ops znet_netdev_ops = {
.ndo_open = znet_open,
.ndo_stop = znet_close,
.ndo_start_xmit = znet_send_packet,
- .ndo_set_multicast_list = znet_set_multicast_list,
+ .ndo_set_rx_mode = znet_set_multicast_list,
.ndo_tx_timeout = znet_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
@@ -731,7 +731,7 @@ static void znet_rx(struct net_device *dev)
cur_frame_end_offset -= ((count + 1)>>1) + 3;
if (cur_frame_end_offset < 0)
cur_frame_end_offset += RX_BUF_SIZE/2;
- };
+ }
/* Now step forward through the list. */
do {
diff --git a/drivers/net/ethernet/ibm/Kconfig b/drivers/net/ethernet/ibm/Kconfig
new file mode 100644
index 000000000000..9e16f3fa97b2
--- /dev/null
+++ b/drivers/net/ethernet/ibm/Kconfig
@@ -0,0 +1,48 @@
+#
+# IBM device configuration.
+#
+
+config NET_VENDOR_IBM
+ bool "IBM devices"
+ default y
+ depends on MCA || PPC_PSERIES || PPC_PSERIES || PPC_DCR || \
+ (IBMEBUS && INET && SPARSEMEM)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about IBM devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_IBM
+
+config IBMVETH
+ tristate "IBM LAN Virtual Ethernet support"
+ depends on PPC_PSERIES
+ ---help---
+ This driver supports virtual ethernet adapters on newer IBM iSeries
+ and pSeries systems.
+
+ To compile this driver as a module, choose M here. The module will
+ be called ibmveth.
+
+config ISERIES_VETH
+ tristate "iSeries Virtual Ethernet driver support"
+ depends on PPC_ISERIES
+
+source "drivers/net/ethernet/ibm/emac/Kconfig"
+
+config EHEA
+ tristate "eHEA Ethernet support"
+ depends on IBMEBUS && INET && SPARSEMEM
+ select INET_LRO
+ ---help---
+ This driver supports the IBM pSeries eHEA ethernet adapter.
+
+ To compile the driver as a module, choose M here. The module
+ will be called ehea.
+
+endif # NET_VENDOR_IBM
diff --git a/drivers/net/ethernet/ibm/Makefile b/drivers/net/ethernet/ibm/Makefile
new file mode 100644
index 000000000000..5a7d4e9ac803
--- /dev/null
+++ b/drivers/net/ethernet/ibm/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for th IBM network device drivers.
+#
+
+obj-$(CONFIG_IBMVETH) += ibmveth.o
+obj-$(CONFIG_ISERIES_VETH) += iseries_veth.o
+obj-$(CONFIG_IBM_EMAC) += emac/
+obj-$(CONFIG_EHEA) += ehea/
diff --git a/drivers/net/ehea/Makefile b/drivers/net/ethernet/ibm/ehea/Makefile
index 775d9969b5c2..775d9969b5c2 100644
--- a/drivers/net/ehea/Makefile
+++ b/drivers/net/ethernet/ibm/ehea/Makefile
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ethernet/ibm/ehea/ehea.h
index 6c7257bd73fc..410d6a1984ed 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ethernet/ibm/ehea/ehea.h
@@ -33,7 +33,6 @@
#include <linux/ethtool.h>
#include <linux/vmalloc.h>
#include <linux/if_vlan.h>
-#include <linux/inet_lro.h>
#include <asm/ibmebus.h>
#include <asm/abs_addr.h>
@@ -58,8 +57,6 @@
#define EHEA_MIN_ENTRIES_QP 127
#define EHEA_SMALL_QUEUES
-#define EHEA_NUM_TX_QP 1
-#define EHEA_LRO_MAX_AGGR 64
#ifdef EHEA_SMALL_QUEUES
#define EHEA_MAX_CQE_COUNT 1023
@@ -83,18 +80,16 @@
#define EHEA_SG_RQ3 0
#define EHEA_MAX_PACKET_SIZE 9022 /* for jumbo frames */
-#define EHEA_RQ2_PKT_SIZE 1522
+#define EHEA_RQ2_PKT_SIZE 2048
#define EHEA_L_PKT_SIZE 256 /* low latency */
-#define MAX_LRO_DESCRIPTORS 8
-
/* Send completion signaling */
/* Protection Domain Identifier */
#define EHEA_PD_ID 0xaabcdeff
#define EHEA_RQ2_THRESHOLD 1
-#define EHEA_RQ3_THRESHOLD 9 /* use RQ3 threshold of 1522 bytes */
+#define EHEA_RQ3_THRESHOLD 4 /* use RQ3 threshold of 2048 bytes */
#define EHEA_SPEED_10G 10000
#define EHEA_SPEED_1G 1000
@@ -363,7 +358,6 @@ struct ehea_port_res {
struct port_stats p_stats;
struct ehea_mr send_mr; /* send memory region */
struct ehea_mr recv_mr; /* receive memory region */
- spinlock_t xmit_lock;
struct ehea_port *port;
char int_recv_name[EHEA_IRQ_NAME_SIZE];
char int_send_name[EHEA_IRQ_NAME_SIZE];
@@ -376,8 +370,6 @@ struct ehea_port_res {
struct ehea_q_skb_arr rq3_skba;
struct ehea_q_skb_arr sq_skba;
int sq_skba_size;
- spinlock_t netif_queue;
- int queue_stopped;
int swqe_refill_th;
atomic_t swqe_avail;
int swqe_ll_count;
@@ -386,9 +378,6 @@ struct ehea_port_res {
u64 tx_bytes;
u64 rx_packets;
u64 rx_bytes;
- u32 poll_counter;
- struct net_lro_mgr lro_mgr;
- struct net_lro_desc lro_desc[MAX_LRO_DESCRIPTORS];
int sq_restart_flag;
};
@@ -453,19 +442,17 @@ struct ehea_bcmc_reg_array {
struct ehea_port {
struct ehea_adapter *adapter; /* adapter that owns this port */
struct net_device *netdev;
- struct net_device_stats stats;
+ struct rtnl_link_stats64 stats;
struct ehea_port_res port_res[EHEA_MAX_PORT_RES];
struct platform_device ofdev; /* Open Firmware Device */
struct ehea_mc_list *mc_list; /* Multicast MAC addresses */
- struct vlan_group *vgrp;
struct ehea_eq *qp_eq;
struct work_struct reset_task;
+ struct delayed_work stats_work;
struct mutex port_lock;
char int_aff_name[EHEA_IRQ_NAME_SIZE];
int allmulti; /* Indicates IFF_ALLMULTI state */
int promisc; /* Indicates IFF_PROMISC state */
- int num_tx_qps;
- int num_add_tx_qps;
int num_mcs;
int resets;
unsigned long flags;
@@ -475,7 +462,6 @@ struct ehea_port {
u32 msg_enable;
u32 sig_comp_iv;
u32 state;
- u32 lro_max_aggr;
u8 phy_link;
u8 full_duplex;
u8 autoneg;
diff --git a/drivers/net/ehea/ehea_ethtool.c b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c
index 7f642aef5e82..05b7359bde8d 100644
--- a/drivers/net/ehea/ehea_ethtool.c
+++ b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c
@@ -180,7 +180,7 @@ static void ehea_set_msglevel(struct net_device *dev, u32 value)
port->msg_enable = value;
}
-static char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
+static const char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
{"sig_comp_iv"},
{"swqe_refill_th"},
{"port resets"},
@@ -189,7 +189,6 @@ static char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
{"IP cksum errors"},
{"Frame cksum errors"},
{"num SQ stopped"},
- {"SQ stopped"},
{"PR0 free_swqes"},
{"PR1 free_swqes"},
{"PR2 free_swqes"},
@@ -198,9 +197,14 @@ static char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
{"PR5 free_swqes"},
{"PR6 free_swqes"},
{"PR7 free_swqes"},
- {"LRO aggregated"},
- {"LRO flushed"},
- {"LRO no_desc"},
+ {"PR8 free_swqes"},
+ {"PR9 free_swqes"},
+ {"PR10 free_swqes"},
+ {"PR11 free_swqes"},
+ {"PR12 free_swqes"},
+ {"PR13 free_swqes"},
+ {"PR14 free_swqes"},
+ {"PR15 free_swqes"},
};
static void ehea_get_strings(struct net_device *dev, u32 stringset, u8 *data)
@@ -255,25 +259,8 @@ static void ehea_get_ethtool_stats(struct net_device *dev,
tmp += port->port_res[k].p_stats.queue_stopped;
data[i++] = tmp;
- for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
- tmp |= port->port_res[k].queue_stopped;
- data[i++] = tmp;
-
- for (k = 0; k < 8; k++)
+ for (k = 0; k < 16; k++)
data[i++] = atomic_read(&port->port_res[k].swqe_avail);
-
- for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
- tmp |= port->port_res[k].lro_mgr.stats.aggregated;
- data[i++] = tmp;
-
- for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
- tmp |= port->port_res[k].lro_mgr.stats.flushed;
- data[i++] = tmp;
-
- for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
- tmp |= port->port_res[k].lro_mgr.stats.no_desc;
- data[i++] = tmp;
-
}
const struct ethtool_ops ehea_ethtool_ops = {
diff --git a/drivers/net/ehea/ehea_hw.h b/drivers/net/ethernet/ibm/ehea/ehea_hw.h
index 567981b4b2cc..1a2fe4dc3eb3 100644
--- a/drivers/net/ehea/ehea_hw.h
+++ b/drivers/net/ethernet/ibm/ehea/ehea_hw.h
@@ -210,36 +210,11 @@ static inline void epa_store_acc(struct h_epa epa, u32 offset, u64 value)
__raw_writeq(value, (void __iomem *)(epa.addr + offset));
}
-#define epa_store_eq(epa, offset, value)\
- epa_store(epa, EQTEMM_OFFSET(offset), value)
-#define epa_load_eq(epa, offset)\
- epa_load(epa, EQTEMM_OFFSET(offset))
-
#define epa_store_cq(epa, offset, value)\
epa_store(epa, CQTEMM_OFFSET(offset), value)
#define epa_load_cq(epa, offset)\
epa_load(epa, CQTEMM_OFFSET(offset))
-#define epa_store_qp(epa, offset, value)\
- epa_store(epa, QPTEMM_OFFSET(offset), value)
-#define epa_load_qp(epa, offset)\
- epa_load(epa, QPTEMM_OFFSET(offset))
-
-#define epa_store_qped(epa, offset, value)\
- epa_store(epa, QPEDMM_OFFSET(offset), value)
-#define epa_load_qped(epa, offset)\
- epa_load(epa, QPEDMM_OFFSET(offset))
-
-#define epa_store_mrmw(epa, offset, value)\
- epa_store(epa, MRMWMM_OFFSET(offset), value)
-#define epa_load_mrmw(epa, offset)\
- epa_load(epa, MRMWMM_OFFSET(offset))
-
-#define epa_store_base(epa, offset, value)\
- epa_store(epa, HCAGR_OFFSET(offset), value)
-#define epa_load_base(epa, offset)\
- epa_load(epa, HCAGR_OFFSET(offset))
-
static inline void ehea_update_sqa(struct ehea_qp *qp, u16 nr_wqes)
{
struct h_epa epa = qp->epas.kernel;
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c
index 3fd5a2400348..37b70f7052b6 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c
@@ -61,10 +61,7 @@ static int rq1_entries = EHEA_DEF_ENTRIES_RQ1;
static int rq2_entries = EHEA_DEF_ENTRIES_RQ2;
static int rq3_entries = EHEA_DEF_ENTRIES_RQ3;
static int sq_entries = EHEA_DEF_ENTRIES_SQ;
-static int use_mcs;
-static int use_lro;
-static int lro_max_aggr = EHEA_LRO_MAX_AGGR;
-static int num_tx_qps = EHEA_NUM_TX_QP;
+static int use_mcs = 1;
static int prop_carrier_state;
module_param(msg_level, int, 0);
@@ -74,11 +71,7 @@ module_param(rq3_entries, int, 0);
module_param(sq_entries, int, 0);
module_param(prop_carrier_state, int, 0);
module_param(use_mcs, int, 0);
-module_param(use_lro, int, 0);
-module_param(lro_max_aggr, int, 0);
-module_param(num_tx_qps, int, 0);
-MODULE_PARM_DESC(num_tx_qps, "Number of TX-QPS");
MODULE_PARM_DESC(msg_level, "msg_level");
MODULE_PARM_DESC(prop_carrier_state, "Propagate carrier state of physical "
"port to stack. 1:yes, 0:no. Default = 0 ");
@@ -94,12 +87,8 @@ MODULE_PARM_DESC(rq1_entries, "Number of entries for Receive Queue 1 "
MODULE_PARM_DESC(sq_entries, " Number of entries for the Send Queue "
"[2^x - 1], x = [6..14]. Default = "
__MODULE_STRING(EHEA_DEF_ENTRIES_SQ) ")");
-MODULE_PARM_DESC(use_mcs, " 0:NAPI, 1:Multiple receive queues, Default = 0 ");
-
-MODULE_PARM_DESC(lro_max_aggr, " LRO: Max packets to be aggregated. Default = "
- __MODULE_STRING(EHEA_LRO_MAX_AGGR));
-MODULE_PARM_DESC(use_lro, " Large Receive Offload, 1: enable, 0: disable, "
- "Default = 0");
+MODULE_PARM_DESC(use_mcs, " Multiple receive queues, 1: enable, 0: disable, "
+ "Default = 1");
static int port_name_cnt;
static LIST_HEAD(adapter_list);
@@ -173,7 +162,7 @@ static void ehea_update_firmware_handles(void)
continue;
num_ports++;
- num_portres += port->num_def_qps + port->num_add_tx_qps;
+ num_portres += port->num_def_qps;
}
}
@@ -199,9 +188,7 @@ static void ehea_update_firmware_handles(void)
(num_ports == 0))
continue;
- for (l = 0;
- l < port->num_def_qps + port->num_add_tx_qps;
- l++) {
+ for (l = 0; l < port->num_def_qps; l++) {
struct ehea_port_res *pr = &port->port_res[l];
arr[i].adh = adapter->handle;
@@ -327,20 +314,44 @@ out:
spin_unlock_irqrestore(&ehea_bcmc_regs.lock, flags);
}
-static struct net_device_stats *ehea_get_stats(struct net_device *dev)
+static struct rtnl_link_stats64 *ehea_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
{
struct ehea_port *port = netdev_priv(dev);
- struct net_device_stats *stats = &port->stats;
- struct hcp_ehea_port_cb2 *cb2;
- u64 hret, rx_packets, tx_packets, rx_bytes = 0, tx_bytes = 0;
+ u64 rx_packets = 0, tx_packets = 0, rx_bytes = 0, tx_bytes = 0;
int i;
- memset(stats, 0, sizeof(*stats));
+ for (i = 0; i < port->num_def_qps; i++) {
+ rx_packets += port->port_res[i].rx_packets;
+ rx_bytes += port->port_res[i].rx_bytes;
+ }
+
+ for (i = 0; i < port->num_def_qps; i++) {
+ tx_packets += port->port_res[i].tx_packets;
+ tx_bytes += port->port_res[i].tx_bytes;
+ }
+
+ stats->tx_packets = tx_packets;
+ stats->rx_bytes = rx_bytes;
+ stats->tx_bytes = tx_bytes;
+ stats->rx_packets = rx_packets;
+
+ return &port->stats;
+}
+
+static void ehea_update_stats(struct work_struct *work)
+{
+ struct ehea_port *port =
+ container_of(work, struct ehea_port, stats_work.work);
+ struct net_device *dev = port->netdev;
+ struct rtnl_link_stats64 *stats = &port->stats;
+ struct hcp_ehea_port_cb2 *cb2;
+ u64 hret;
cb2 = (void *)get_zeroed_page(GFP_KERNEL);
if (!cb2) {
- netdev_err(dev, "no mem for cb2\n");
- goto out;
+ netdev_err(dev, "No mem for cb2. Some interface statistics were not updated\n");
+ goto resched;
}
hret = ehea_h_query_ehea_port(port->adapter->handle,
@@ -354,29 +365,13 @@ static struct net_device_stats *ehea_get_stats(struct net_device *dev)
if (netif_msg_hw(port))
ehea_dump(cb2, sizeof(*cb2), "net_device_stats");
- rx_packets = 0;
- for (i = 0; i < port->num_def_qps; i++) {
- rx_packets += port->port_res[i].rx_packets;
- rx_bytes += port->port_res[i].rx_bytes;
- }
-
- tx_packets = 0;
- for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
- tx_packets += port->port_res[i].tx_packets;
- tx_bytes += port->port_res[i].tx_bytes;
- }
-
- stats->tx_packets = tx_packets;
stats->multicast = cb2->rxmcp;
stats->rx_errors = cb2->rxuerr;
- stats->rx_bytes = rx_bytes;
- stats->tx_bytes = tx_bytes;
- stats->rx_packets = rx_packets;
out_herr:
free_page((unsigned long)cb2);
-out:
- return stats;
+resched:
+ schedule_delayed_work(&port->stats_work, msecs_to_jiffies(1000));
}
static void ehea_refill_rq1(struct ehea_port_res *pr, int index, int nr_of_wqes)
@@ -543,7 +538,8 @@ static inline int ehea_check_cqe(struct ehea_cqe *cqe, int *rq_num)
}
static inline void ehea_fill_skb(struct net_device *dev,
- struct sk_buff *skb, struct ehea_cqe *cqe)
+ struct sk_buff *skb, struct ehea_cqe *cqe,
+ struct ehea_port_res *pr)
{
int length = cqe->num_bytes_transfered - 4; /*remove CRC */
@@ -557,6 +553,8 @@ static inline void ehea_fill_skb(struct net_device *dev,
skb->csum = csum_unfold(~cqe->inet_checksum_value);
} else
skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ skb_record_rx_queue(skb, pr - &pr->port->port_res[0]);
}
static inline struct sk_buff *get_skb_by_index(struct sk_buff **skb_array,
@@ -649,60 +647,6 @@ static int ehea_treat_poll_error(struct ehea_port_res *pr, int rq,
return 0;
}
-static int get_skb_hdr(struct sk_buff *skb, void **iphdr,
- void **tcph, u64 *hdr_flags, void *priv)
-{
- struct ehea_cqe *cqe = priv;
- unsigned int ip_len;
- struct iphdr *iph;
-
- /* non tcp/udp packets */
- if (!cqe->header_length)
- return -1;
-
- /* non tcp packet */
- skb_reset_network_header(skb);
- iph = ip_hdr(skb);
- if (iph->protocol != IPPROTO_TCP)
- return -1;
-
- ip_len = ip_hdrlen(skb);
- skb_set_transport_header(skb, ip_len);
- *tcph = tcp_hdr(skb);
-
- /* check if ip header and tcp header are complete */
- if (ntohs(iph->tot_len) < ip_len + tcp_hdrlen(skb))
- return -1;
-
- *hdr_flags = LRO_IPV4 | LRO_TCP;
- *iphdr = iph;
-
- return 0;
-}
-
-static void ehea_proc_skb(struct ehea_port_res *pr, struct ehea_cqe *cqe,
- struct sk_buff *skb)
-{
- int vlan_extracted = ((cqe->status & EHEA_CQE_VLAN_TAG_XTRACT) &&
- pr->port->vgrp);
-
- if (skb->dev->features & NETIF_F_LRO) {
- if (vlan_extracted)
- lro_vlan_hwaccel_receive_skb(&pr->lro_mgr, skb,
- pr->port->vgrp,
- cqe->vlan_tag,
- cqe);
- else
- lro_receive_skb(&pr->lro_mgr, skb, cqe);
- } else {
- if (vlan_extracted)
- vlan_hwaccel_receive_skb(skb, pr->port->vgrp,
- cqe->vlan_tag);
- else
- netif_receive_skb(skb);
- }
-}
-
static int ehea_proc_rwqes(struct net_device *dev,
struct ehea_port_res *pr,
int budget)
@@ -753,7 +697,7 @@ static int ehea_proc_rwqes(struct net_device *dev,
}
skb_copy_to_linear_data(skb, ((char *)cqe) + 64,
cqe->num_bytes_transfered - 4);
- ehea_fill_skb(dev, skb, cqe);
+ ehea_fill_skb(dev, skb, cqe, pr);
} else if (rq == 2) {
/* RQ2 */
skb = get_skb_by_index(skb_arr_rq2,
@@ -763,7 +707,7 @@ static int ehea_proc_rwqes(struct net_device *dev,
"rq2: skb=NULL\n");
break;
}
- ehea_fill_skb(dev, skb, cqe);
+ ehea_fill_skb(dev, skb, cqe, pr);
processed_rq2++;
} else {
/* RQ3 */
@@ -774,12 +718,16 @@ static int ehea_proc_rwqes(struct net_device *dev,
"rq3: skb=NULL\n");
break;
}
- ehea_fill_skb(dev, skb, cqe);
+ ehea_fill_skb(dev, skb, cqe, pr);
processed_rq3++;
}
processed_bytes += skb->len;
- ehea_proc_skb(pr, cqe, skb);
+
+ if (cqe->status & EHEA_CQE_VLAN_TAG_XTRACT)
+ __vlan_hwaccel_put_tag(skb, cqe->vlan_tag);
+
+ napi_gro_receive(&pr->napi, skb);
} else {
pr->p_stats.poll_receive_errors++;
port_reset = ehea_treat_poll_error(pr, rq, cqe,
@@ -790,8 +738,6 @@ static int ehea_proc_rwqes(struct net_device *dev,
}
cqe = ehea_poll_rq1(qp, &wqe_index);
}
- if (dev->features & NETIF_F_LRO)
- lro_flush_all(&pr->lro_mgr);
pr->rx_packets += processed;
pr->rx_bytes += processed_bytes;
@@ -809,7 +755,7 @@ static void reset_sq_restart_flag(struct ehea_port *port)
{
int i;
- for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+ for (i = 0; i < port->num_def_qps; i++) {
struct ehea_port_res *pr = &port->port_res[i];
pr->sq_restart_flag = 0;
}
@@ -822,7 +768,7 @@ static void check_sqs(struct ehea_port *port)
int swqe_index;
int i, k;
- for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+ for (i = 0; i < port->num_def_qps; i++) {
struct ehea_port_res *pr = &port->port_res[i];
int ret;
k = 0;
@@ -860,7 +806,8 @@ static struct ehea_cqe *ehea_proc_cqes(struct ehea_port_res *pr, int my_quota)
int cqe_counter = 0;
int swqe_av = 0;
int index;
- unsigned long flags;
+ struct netdev_queue *txq = netdev_get_tx_queue(pr->port->netdev,
+ pr - &pr->port->port_res[0]);
cqe = ehea_poll_cq(send_cq);
while (cqe && (quota > 0)) {
@@ -910,20 +857,20 @@ static struct ehea_cqe *ehea_proc_cqes(struct ehea_port_res *pr, int my_quota)
ehea_update_feca(send_cq, cqe_counter);
atomic_add(swqe_av, &pr->swqe_avail);
- spin_lock_irqsave(&pr->netif_queue, flags);
-
- if (pr->queue_stopped && (atomic_read(&pr->swqe_avail)
- >= pr->swqe_refill_th)) {
- netif_wake_queue(pr->port->netdev);
- pr->queue_stopped = 0;
+ if (unlikely(netif_tx_queue_stopped(txq) &&
+ (atomic_read(&pr->swqe_avail) >= pr->swqe_refill_th))) {
+ __netif_tx_lock(txq, smp_processor_id());
+ if (netif_tx_queue_stopped(txq) &&
+ (atomic_read(&pr->swqe_avail) >= pr->swqe_refill_th))
+ netif_tx_wake_queue(txq);
+ __netif_tx_unlock(txq);
}
- spin_unlock_irqrestore(&pr->netif_queue, flags);
+
wake_up(&pr->port->swqe_avail_wq);
return cqe;
}
-#define EHEA_NAPI_POLL_NUM_BEFORE_IRQ 16
#define EHEA_POLL_MAX_CQES 65535
static int ehea_poll(struct napi_struct *napi, int budget)
@@ -933,18 +880,13 @@ static int ehea_poll(struct napi_struct *napi, int budget)
struct net_device *dev = pr->port->netdev;
struct ehea_cqe *cqe;
struct ehea_cqe *cqe_skb = NULL;
- int force_irq, wqe_index;
+ int wqe_index;
int rx = 0;
- force_irq = (pr->poll_counter > EHEA_NAPI_POLL_NUM_BEFORE_IRQ);
cqe_skb = ehea_proc_cqes(pr, EHEA_POLL_MAX_CQES);
+ rx += ehea_proc_rwqes(dev, pr, budget - rx);
- if (!force_irq)
- rx += ehea_proc_rwqes(dev, pr, budget - rx);
-
- while ((rx != budget) || force_irq) {
- pr->poll_counter = 0;
- force_irq = 0;
+ while (rx != budget) {
napi_complete(napi);
ehea_reset_cq_ep(pr->recv_cq);
ehea_reset_cq_ep(pr->send_cq);
@@ -964,7 +906,6 @@ static int ehea_poll(struct napi_struct *napi, int budget)
rx += ehea_proc_rwqes(dev, pr, budget - rx);
}
- pr->poll_counter++;
return rx;
}
@@ -1116,13 +1057,6 @@ int ehea_sense_port_attr(struct ehea_port *port)
goto out_free;
}
- port->num_tx_qps = num_tx_qps;
-
- if (port->num_def_qps >= port->num_tx_qps)
- port->num_add_tx_qps = 0;
- else
- port->num_add_tx_qps = port->num_tx_qps - port->num_def_qps;
-
ret = 0;
out_free:
if (ret || netif_msg_probe(port))
@@ -1254,7 +1188,7 @@ static void ehea_parse_eqe(struct ehea_adapter *adapter, u64 eqe)
netif_info(port, link, dev,
"Logical port down\n");
netif_carrier_off(dev);
- netif_stop_queue(dev);
+ netif_tx_disable(dev);
}
if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PORT_UP, eqe)) {
@@ -1285,7 +1219,7 @@ static void ehea_parse_eqe(struct ehea_adapter *adapter, u64 eqe)
case EHEA_EC_PORT_MALFUNC:
netdev_info(dev, "Port malfunction\n");
netif_carrier_off(dev);
- netif_stop_queue(dev);
+ netif_tx_disable(dev);
break;
default:
netdev_err(dev, "unknown event code %x, eqe=0x%llX\n", ec, eqe);
@@ -1363,7 +1297,7 @@ static int ehea_reg_interrupts(struct net_device *dev)
port->qp_eq->attr.ist1);
- for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+ for (i = 0; i < port->num_def_qps; i++) {
pr = &port->port_res[i];
snprintf(pr->int_send_name, EHEA_IRQ_NAME_SIZE - 1,
"%s-queue%d", dev->name, i);
@@ -1406,7 +1340,7 @@ static void ehea_free_interrupts(struct net_device *dev)
/* send */
- for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+ for (i = 0; i < port->num_def_qps; i++) {
pr = &port->port_res[i];
ibmebus_free_irq(pr->eq->attr.ist1, pr);
netif_info(port, intr, dev,
@@ -1537,8 +1471,6 @@ static int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr,
pr->rx_packets = rx_packets;
pr->port = port;
- spin_lock_init(&pr->xmit_lock);
- spin_lock_init(&pr->netif_queue);
pr->eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
if (!pr->eq) {
@@ -1629,15 +1561,6 @@ static int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr,
netif_napi_add(pr->port->netdev, &pr->napi, ehea_poll, 64);
- pr->lro_mgr.max_aggr = pr->port->lro_max_aggr;
- pr->lro_mgr.max_desc = MAX_LRO_DESCRIPTORS;
- pr->lro_mgr.lro_arr = pr->lro_desc;
- pr->lro_mgr.get_skb_header = get_skb_hdr;
- pr->lro_mgr.features = LRO_F_NAPI | LRO_F_EXTRACT_VLAN_ID;
- pr->lro_mgr.dev = port->netdev;
- pr->lro_mgr.ip_summed = CHECKSUM_UNNECESSARY;
- pr->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY;
-
ret = 0;
goto out;
@@ -1694,96 +1617,35 @@ static int ehea_clean_portres(struct ehea_port *port, struct ehea_port_res *pr)
return ret;
}
-/*
- * The write_* functions store information in swqe which is used by
- * the hardware to calculate the ip/tcp/udp checksum
- */
-
-static inline void write_ip_start_end(struct ehea_swqe *swqe,
- const struct sk_buff *skb)
-{
- swqe->ip_start = skb_network_offset(skb);
- swqe->ip_end = (u8)(swqe->ip_start + ip_hdrlen(skb) - 1);
-}
-
-static inline void write_tcp_offset_end(struct ehea_swqe *swqe,
- const struct sk_buff *skb)
-{
- swqe->tcp_offset =
- (u8)(swqe->ip_end + 1 + offsetof(struct tcphdr, check));
-
- swqe->tcp_end = (u16)skb->len - 1;
-}
-
-static inline void write_udp_offset_end(struct ehea_swqe *swqe,
- const struct sk_buff *skb)
-{
- swqe->tcp_offset =
- (u8)(swqe->ip_end + 1 + offsetof(struct udphdr, check));
-
- swqe->tcp_end = (u16)skb->len - 1;
-}
-
-
-static void write_swqe2_TSO(struct sk_buff *skb,
- struct ehea_swqe *swqe, u32 lkey)
-{
- struct ehea_vsgentry *sg1entry = &swqe->u.immdata_desc.sg_entry;
- u8 *imm_data = &swqe->u.immdata_desc.immediate_data[0];
- int skb_data_size = skb_headlen(skb);
- int headersize;
-
- /* Packet is TCP with TSO enabled */
- swqe->tx_control |= EHEA_SWQE_TSO;
- swqe->mss = skb_shinfo(skb)->gso_size;
- /* copy only eth/ip/tcp headers to immediate data and
- * the rest of skb->data to sg1entry
- */
- headersize = ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb);
-
- skb_data_size = skb_headlen(skb);
-
- if (skb_data_size >= headersize) {
- /* copy immediate data */
- skb_copy_from_linear_data(skb, imm_data, headersize);
- swqe->immediate_data_length = headersize;
-
- if (skb_data_size > headersize) {
- /* set sg1entry data */
- sg1entry->l_key = lkey;
- sg1entry->len = skb_data_size - headersize;
- sg1entry->vaddr =
- ehea_map_vaddr(skb->data + headersize);
- swqe->descriptors++;
- }
- } else
- pr_err("cannot handle fragmented headers\n");
-}
-
-static void write_swqe2_nonTSO(struct sk_buff *skb,
- struct ehea_swqe *swqe, u32 lkey)
+static void write_swqe2_immediate(struct sk_buff *skb, struct ehea_swqe *swqe,
+ u32 lkey)
{
int skb_data_size = skb_headlen(skb);
u8 *imm_data = &swqe->u.immdata_desc.immediate_data[0];
struct ehea_vsgentry *sg1entry = &swqe->u.immdata_desc.sg_entry;
+ unsigned int immediate_len = SWQE2_MAX_IMM;
+
+ swqe->descriptors = 0;
- /* Packet is any nonTSO type
- *
- * Copy as much as possible skb->data to immediate data and
- * the rest to sg1entry
- */
- if (skb_data_size >= SWQE2_MAX_IMM) {
- /* copy immediate data */
- skb_copy_from_linear_data(skb, imm_data, SWQE2_MAX_IMM);
+ if (skb_is_gso(skb)) {
+ swqe->tx_control |= EHEA_SWQE_TSO;
+ swqe->mss = skb_shinfo(skb)->gso_size;
+ /*
+ * For TSO packets we only copy the headers into the
+ * immediate area.
+ */
+ immediate_len = ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb);
+ }
- swqe->immediate_data_length = SWQE2_MAX_IMM;
+ if (skb_is_gso(skb) || skb_data_size >= SWQE2_MAX_IMM) {
+ skb_copy_from_linear_data(skb, imm_data, immediate_len);
+ swqe->immediate_data_length = immediate_len;
- if (skb_data_size > SWQE2_MAX_IMM) {
- /* copy sg1entry data */
+ if (skb_data_size > immediate_len) {
sg1entry->l_key = lkey;
- sg1entry->len = skb_data_size - SWQE2_MAX_IMM;
+ sg1entry->len = skb_data_size - immediate_len;
sg1entry->vaddr =
- ehea_map_vaddr(skb->data + SWQE2_MAX_IMM);
+ ehea_map_vaddr(skb->data + immediate_len);
swqe->descriptors++;
}
} else {
@@ -1802,13 +1664,9 @@ static inline void write_swqe2_data(struct sk_buff *skb, struct net_device *dev,
nfrags = skb_shinfo(skb)->nr_frags;
sg1entry = &swqe->u.immdata_desc.sg_entry;
sg_list = (struct ehea_vsgentry *)&swqe->u.immdata_desc.sg_list;
- swqe->descriptors = 0;
sg1entry_contains_frag_data = 0;
- if ((dev->features & NETIF_F_TSO) && skb_shinfo(skb)->gso_size)
- write_swqe2_TSO(skb, swqe, lkey);
- else
- write_swqe2_nonTSO(skb, swqe, lkey);
+ write_swqe2_immediate(skb, swqe, lkey);
/* write descriptors */
if (nfrags > 0) {
@@ -1818,10 +1676,9 @@ static inline void write_swqe2_data(struct sk_buff *skb, struct net_device *dev,
/* copy sg1entry data */
sg1entry->l_key = lkey;
- sg1entry->len = frag->size;
+ sg1entry->len = skb_frag_size(frag);
sg1entry->vaddr =
- ehea_map_vaddr(page_address(frag->page)
- + frag->page_offset);
+ ehea_map_vaddr(skb_frag_address(frag));
swqe->descriptors++;
sg1entry_contains_frag_data = 1;
}
@@ -1832,10 +1689,8 @@ static inline void write_swqe2_data(struct sk_buff *skb, struct net_device *dev,
sgentry = &sg_list[i - sg1entry_contains_frag_data];
sgentry->l_key = lkey;
- sgentry->len = frag->size;
- sgentry->vaddr =
- ehea_map_vaddr(page_address(frag->page)
- + frag->page_offset);
+ sgentry->len = skb_frag_size(frag);
+ sgentry->vaddr = ehea_map_vaddr(skb_frag_address(frag));
swqe->descriptors++;
}
}
@@ -2126,41 +1981,44 @@ static int ehea_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
-static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
- struct ehea_swqe *swqe, u32 lkey)
+static void xmit_common(struct sk_buff *skb, struct ehea_swqe *swqe)
{
- if (skb->protocol == htons(ETH_P_IP)) {
- const struct iphdr *iph = ip_hdr(skb);
+ swqe->tx_control |= EHEA_SWQE_IMM_DATA_PRESENT | EHEA_SWQE_CRC;
- /* IPv4 */
- swqe->tx_control |= EHEA_SWQE_CRC
- | EHEA_SWQE_IP_CHECKSUM
- | EHEA_SWQE_TCP_CHECKSUM
- | EHEA_SWQE_IMM_DATA_PRESENT
- | EHEA_SWQE_DESCRIPTORS_PRESENT;
+ if (skb->protocol != htons(ETH_P_IP))
+ return;
- write_ip_start_end(swqe, skb);
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ swqe->tx_control |= EHEA_SWQE_IP_CHECKSUM;
- if (iph->protocol == IPPROTO_UDP) {
- if ((iph->frag_off & IP_MF) ||
- (iph->frag_off & IP_OFFSET))
- /* IP fragment, so don't change cs */
- swqe->tx_control &= ~EHEA_SWQE_TCP_CHECKSUM;
- else
- write_udp_offset_end(swqe, skb);
- } else if (iph->protocol == IPPROTO_TCP) {
- write_tcp_offset_end(swqe, skb);
- }
+ swqe->ip_start = skb_network_offset(skb);
+ swqe->ip_end = swqe->ip_start + ip_hdrlen(skb) - 1;
- /* icmp (big data) and ip segmentation packets (all other ip
- packets) do not require any special handling */
+ switch (ip_hdr(skb)->protocol) {
+ case IPPROTO_UDP:
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ swqe->tx_control |= EHEA_SWQE_TCP_CHECKSUM;
- } else {
- /* Other Ethernet Protocol */
- swqe->tx_control |= EHEA_SWQE_CRC
- | EHEA_SWQE_IMM_DATA_PRESENT
- | EHEA_SWQE_DESCRIPTORS_PRESENT;
+ swqe->tcp_offset = swqe->ip_end + 1 +
+ offsetof(struct udphdr, check);
+ break;
+
+ case IPPROTO_TCP:
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ swqe->tx_control |= EHEA_SWQE_TCP_CHECKSUM;
+
+ swqe->tcp_offset = swqe->ip_end + 1 +
+ offsetof(struct tcphdr, check);
+ break;
}
+}
+
+static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
+ struct ehea_swqe *swqe, u32 lkey)
+{
+ swqe->tx_control |= EHEA_SWQE_DESCRIPTORS_PRESENT;
+
+ xmit_common(skb, swqe);
write_swqe2_data(skb, dev, swqe, lkey);
}
@@ -2168,107 +2026,30 @@ static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
struct ehea_swqe *swqe)
{
- int nfrags = skb_shinfo(skb)->nr_frags;
u8 *imm_data = &swqe->u.immdata_nodesc.immediate_data[0];
- skb_frag_t *frag;
- int i;
- if (skb->protocol == htons(ETH_P_IP)) {
- const struct iphdr *iph = ip_hdr(skb);
+ xmit_common(skb, swqe);
- /* IPv4 */
- write_ip_start_end(swqe, skb);
-
- if (iph->protocol == IPPROTO_TCP) {
- swqe->tx_control |= EHEA_SWQE_CRC
- | EHEA_SWQE_IP_CHECKSUM
- | EHEA_SWQE_TCP_CHECKSUM
- | EHEA_SWQE_IMM_DATA_PRESENT;
-
- write_tcp_offset_end(swqe, skb);
-
- } else if (iph->protocol == IPPROTO_UDP) {
- if ((iph->frag_off & IP_MF) ||
- (iph->frag_off & IP_OFFSET))
- /* IP fragment, so don't change cs */
- swqe->tx_control |= EHEA_SWQE_CRC
- | EHEA_SWQE_IMM_DATA_PRESENT;
- else {
- swqe->tx_control |= EHEA_SWQE_CRC
- | EHEA_SWQE_IP_CHECKSUM
- | EHEA_SWQE_TCP_CHECKSUM
- | EHEA_SWQE_IMM_DATA_PRESENT;
-
- write_udp_offset_end(swqe, skb);
- }
- } else {
- /* icmp (big data) and
- ip segmentation packets (all other ip packets) */
- swqe->tx_control |= EHEA_SWQE_CRC
- | EHEA_SWQE_IP_CHECKSUM
- | EHEA_SWQE_IMM_DATA_PRESENT;
- }
- } else {
- /* Other Ethernet Protocol */
- swqe->tx_control |= EHEA_SWQE_CRC | EHEA_SWQE_IMM_DATA_PRESENT;
- }
- /* copy (immediate) data */
- if (nfrags == 0) {
- /* data is in a single piece */
+ if (!skb->data_len)
skb_copy_from_linear_data(skb, imm_data, skb->len);
- } else {
- /* first copy data from the skb->data buffer ... */
- skb_copy_from_linear_data(skb, imm_data,
- skb_headlen(skb));
- imm_data += skb_headlen(skb);
+ else
+ skb_copy_bits(skb, 0, imm_data, skb->len);
- /* ... then copy data from the fragments */
- for (i = 0; i < nfrags; i++) {
- frag = &skb_shinfo(skb)->frags[i];
- memcpy(imm_data,
- page_address(frag->page) + frag->page_offset,
- frag->size);
- imm_data += frag->size;
- }
- }
swqe->immediate_data_length = skb->len;
dev_kfree_skb(skb);
}
-static inline int ehea_hash_skb(struct sk_buff *skb, int num_qps)
-{
- struct tcphdr *tcp;
- u32 tmp;
-
- if ((skb->protocol == htons(ETH_P_IP)) &&
- (ip_hdr(skb)->protocol == IPPROTO_TCP)) {
- tcp = (struct tcphdr *)(skb_network_header(skb) +
- (ip_hdr(skb)->ihl * 4));
- tmp = (tcp->source + (tcp->dest << 16)) % 31;
- tmp += ip_hdr(skb)->daddr % 31;
- return tmp % num_qps;
- } else
- return 0;
-}
-
static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ehea_port *port = netdev_priv(dev);
struct ehea_swqe *swqe;
- unsigned long flags;
u32 lkey;
int swqe_index;
struct ehea_port_res *pr;
+ struct netdev_queue *txq;
- pr = &port->port_res[ehea_hash_skb(skb, port->num_tx_qps)];
-
- if (!spin_trylock(&pr->xmit_lock))
- return NETDEV_TX_BUSY;
-
- if (pr->queue_stopped) {
- spin_unlock(&pr->xmit_lock);
- return NETDEV_TX_BUSY;
- }
+ pr = &port->port_res[skb_get_queue_mapping(skb)];
+ txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
swqe = ehea_get_swqe(pr->qp, &swqe_index);
memset(swqe, 0, SWQE_HEADER_SIZE);
@@ -2318,53 +2099,20 @@ static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
ehea_dump(swqe, 512, "swqe");
if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
- netif_stop_queue(dev);
+ netif_tx_stop_queue(txq);
swqe->tx_control |= EHEA_SWQE_PURGE;
}
ehea_post_swqe(pr->qp, swqe);
if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
- spin_lock_irqsave(&pr->netif_queue, flags);
- if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
- pr->p_stats.queue_stopped++;
- netif_stop_queue(dev);
- pr->queue_stopped = 1;
- }
- spin_unlock_irqrestore(&pr->netif_queue, flags);
+ pr->p_stats.queue_stopped++;
+ netif_tx_stop_queue(txq);
}
- dev->trans_start = jiffies; /* NETIF_F_LLTX driver :( */
- spin_unlock(&pr->xmit_lock);
return NETDEV_TX_OK;
}
-static void ehea_vlan_rx_register(struct net_device *dev,
- struct vlan_group *grp)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct ehea_adapter *adapter = port->adapter;
- struct hcp_ehea_port_cb1 *cb1;
- u64 hret;
-
- port->vgrp = grp;
-
- cb1 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb1) {
- pr_err("no mem for cb1\n");
- goto out;
- }
-
- hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
- H_PORT_CB1, H_PORT_CB1_ALL, cb1);
- if (hret != H_SUCCESS)
- pr_err("modify_ehea_port failed\n");
-
- free_page((unsigned long)cb1);
-out:
- return;
-}
-
static void ehea_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
{
struct ehea_port *port = netdev_priv(dev);
@@ -2406,8 +2154,6 @@ static void ehea_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
int index;
u64 hret;
- vlan_group_set_device(port->vgrp, vid, NULL);
-
cb1 = (void *)get_zeroed_page(GFP_KERNEL);
if (!cb1) {
pr_err("no mem for cb1\n");
@@ -2507,8 +2253,7 @@ out:
return ret;
}
-static int ehea_port_res_setup(struct ehea_port *port, int def_qps,
- int add_tx_qps)
+static int ehea_port_res_setup(struct ehea_port *port, int def_qps)
{
int ret, i;
struct port_res_cfg pr_cfg, pr_cfg_small_rx;
@@ -2541,7 +2286,7 @@ static int ehea_port_res_setup(struct ehea_port *port, int def_qps,
if (ret)
goto out_clean_pr;
}
- for (i = def_qps; i < def_qps + add_tx_qps; i++) {
+ for (i = def_qps; i < def_qps; i++) {
ret = ehea_init_port_res(port, &port->port_res[i],
&pr_cfg_small_rx, i);
if (ret)
@@ -2564,7 +2309,7 @@ static int ehea_clean_all_portres(struct ehea_port *port)
int ret = 0;
int i;
- for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++)
+ for (i = 0; i < port->num_def_qps; i++)
ret |= ehea_clean_portres(port, &port->port_res[i]);
ret |= ehea_destroy_eq(port->qp_eq);
@@ -2596,8 +2341,7 @@ static int ehea_up(struct net_device *dev)
if (port->state == EHEA_PORT_UP)
return 0;
- ret = ehea_port_res_setup(port, port->num_def_qps,
- port->num_add_tx_qps);
+ ret = ehea_port_res_setup(port, port->num_def_qps);
if (ret) {
netdev_err(dev, "port_res_failed\n");
goto out;
@@ -2616,7 +2360,7 @@ static int ehea_up(struct net_device *dev)
goto out_clean_pr;
}
- for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+ for (i = 0; i < port->num_def_qps; i++) {
ret = ehea_activate_qp(port->adapter, port->port_res[i].qp);
if (ret) {
netdev_err(dev, "activate_qp failed\n");
@@ -2662,7 +2406,7 @@ static void port_napi_disable(struct ehea_port *port)
{
int i;
- for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++)
+ for (i = 0; i < port->num_def_qps; i++)
napi_disable(&port->port_res[i].napi);
}
@@ -2670,7 +2414,7 @@ static void port_napi_enable(struct ehea_port *port)
{
int i;
- for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++)
+ for (i = 0; i < port->num_def_qps; i++)
napi_enable(&port->port_res[i].napi);
}
@@ -2686,10 +2430,11 @@ static int ehea_open(struct net_device *dev)
ret = ehea_up(dev);
if (!ret) {
port_napi_enable(port);
- netif_start_queue(dev);
+ netif_tx_start_all_queues(dev);
}
mutex_unlock(&port->port_lock);
+ schedule_delayed_work(&port->stats_work, msecs_to_jiffies(1000));
return ret;
}
@@ -2729,8 +2474,9 @@ static int ehea_stop(struct net_device *dev)
set_bit(__EHEA_DISABLE_PORT_RESET, &port->flags);
cancel_work_sync(&port->reset_task);
+ cancel_delayed_work_sync(&port->stats_work);
mutex_lock(&port->port_lock);
- netif_stop_queue(dev);
+ netif_tx_stop_all_queues(dev);
port_napi_disable(port);
ret = ehea_down(dev);
mutex_unlock(&port->port_lock);
@@ -2756,7 +2502,7 @@ static void ehea_flush_sq(struct ehea_port *port)
{
int i;
- for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+ for (i = 0; i < port->num_def_qps; i++) {
struct ehea_port_res *pr = &port->port_res[i];
int swqe_max = pr->sq_skba_size - 2 - pr->swqe_ll_count;
int ret;
@@ -2790,7 +2536,7 @@ int ehea_stop_qps(struct net_device *dev)
goto out;
}
- for (i = 0; i < (port->num_def_qps + port->num_add_tx_qps); i++) {
+ for (i = 0; i < (port->num_def_qps); i++) {
struct ehea_port_res *pr = &port->port_res[i];
struct ehea_qp *qp = pr->qp;
@@ -2892,7 +2638,7 @@ int ehea_restart_qps(struct net_device *dev)
goto out;
}
- for (i = 0; i < (port->num_def_qps + port->num_add_tx_qps); i++) {
+ for (i = 0; i < (port->num_def_qps); i++) {
struct ehea_port_res *pr = &port->port_res[i];
struct ehea_qp *qp = pr->qp;
@@ -2954,7 +2700,7 @@ static void ehea_reset_port(struct work_struct *work)
mutex_lock(&dlpar_mem_lock);
port->resets++;
mutex_lock(&port->port_lock);
- netif_stop_queue(dev);
+ netif_tx_disable(dev);
port_napi_disable(port);
@@ -2970,7 +2716,7 @@ static void ehea_reset_port(struct work_struct *work)
port_napi_enable(port);
- netif_wake_queue(dev);
+ netif_tx_wake_all_queues(dev);
out:
mutex_unlock(&port->port_lock);
mutex_unlock(&dlpar_mem_lock);
@@ -2997,7 +2743,7 @@ static void ehea_rereg_mrs(void)
if (dev->flags & IFF_UP) {
mutex_lock(&port->port_lock);
- netif_stop_queue(dev);
+ netif_tx_disable(dev);
ehea_flush_sq(port);
ret = ehea_stop_qps(dev);
if (ret) {
@@ -3042,7 +2788,7 @@ static void ehea_rereg_mrs(void)
if (!ret) {
check_sqs(port);
port_napi_enable(port);
- netif_wake_queue(dev);
+ netif_tx_wake_all_queues(dev);
} else {
netdev_err(dev, "Unable to restart QPS\n");
}
@@ -3197,12 +2943,11 @@ static const struct net_device_ops ehea_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = ehea_netpoll,
#endif
- .ndo_get_stats = ehea_get_stats,
+ .ndo_get_stats64 = ehea_get_stats64,
.ndo_set_mac_address = ehea_set_mac_addr,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = ehea_set_multicast_list,
+ .ndo_set_rx_mode = ehea_set_multicast_list,
.ndo_change_mtu = ehea_change_mtu,
- .ndo_vlan_rx_register = ehea_vlan_rx_register,
.ndo_vlan_rx_add_vid = ehea_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = ehea_vlan_rx_kill_vid,
.ndo_tx_timeout = ehea_tx_watchdog,
@@ -3219,7 +2964,7 @@ struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter,
int jumbo;
/* allocate memory for the port structures */
- dev = alloc_etherdev(sizeof(struct ehea_port));
+ dev = alloc_etherdev_mq(sizeof(struct ehea_port), EHEA_MAX_PORT_RES);
if (!dev) {
pr_err("no mem for net_device\n");
@@ -3251,6 +2996,9 @@ struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter,
if (ret)
goto out_free_mc_list;
+ netif_set_real_num_rx_queues(dev, port->num_def_qps);
+ netif_set_real_num_tx_queues(dev, port->num_def_qps);
+
port_dev = ehea_register_port(port, dn);
if (!port_dev)
goto out_free_mc_list;
@@ -3263,30 +3011,29 @@ struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter,
dev->netdev_ops = &ehea_netdev_ops;
ehea_set_ethtool_ops(dev);
- dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO
+ dev->hw_features = NETIF_F_SG | NETIF_F_TSO
| NETIF_F_IP_CSUM | NETIF_F_HW_VLAN_TX | NETIF_F_LRO;
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO
| NETIF_F_HIGHDMA | NETIF_F_IP_CSUM | NETIF_F_HW_VLAN_TX
| NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER
- | NETIF_F_LLTX | NETIF_F_RXCSUM;
+ | NETIF_F_RXCSUM;
+ dev->vlan_features = NETIF_F_SG | NETIF_F_TSO | NETIF_F_HIGHDMA |
+ NETIF_F_IP_CSUM;
dev->watchdog_timeo = EHEA_WATCH_DOG_TIMEOUT;
- if (use_lro)
- dev->features |= NETIF_F_LRO;
-
INIT_WORK(&port->reset_task, ehea_reset_port);
+ INIT_DELAYED_WORK(&port->stats_work, ehea_update_stats);
init_waitqueue_head(&port->swqe_avail_wq);
init_waitqueue_head(&port->restart_wq);
+ memset(&port->stats, 0, sizeof(struct net_device_stats));
ret = register_netdev(dev);
if (ret) {
pr_err("register_netdev failed. ret=%d\n", ret);
goto out_unreg_port;
}
- port->lro_max_aggr = lro_max_aggr;
-
ret = ehea_get_jumboframe_status(port, &jumbo);
if (ret)
netdev_err(dev, "failed determining jumbo frame status\n");
@@ -3318,6 +3065,7 @@ static void ehea_shutdown_single_port(struct ehea_port *port)
struct ehea_adapter *adapter = port->adapter;
cancel_work_sync(&port->reset_task);
+ cancel_delayed_work_sync(&port->stats_work);
unregister_netdev(port->netdev);
ehea_unregister_port(port);
kfree(port->mc_list);
diff --git a/drivers/net/ehea/ehea_phyp.c b/drivers/net/ethernet/ibm/ehea/ehea_phyp.c
index 0506967b9044..0506967b9044 100644
--- a/drivers/net/ehea/ehea_phyp.c
+++ b/drivers/net/ethernet/ibm/ehea/ehea_phyp.c
diff --git a/drivers/net/ehea/ehea_phyp.h b/drivers/net/ethernet/ibm/ehea/ehea_phyp.h
index 2f8174c248bc..2f8174c248bc 100644
--- a/drivers/net/ehea/ehea_phyp.h
+++ b/drivers/net/ethernet/ibm/ehea/ehea_phyp.h
diff --git a/drivers/net/ehea/ehea_qmr.c b/drivers/net/ethernet/ibm/ehea/ehea_qmr.c
index cd44bb8017d9..95b9f4fa811e 100644
--- a/drivers/net/ehea/ehea_qmr.c
+++ b/drivers/net/ethernet/ibm/ehea/ehea_qmr.c
@@ -331,7 +331,7 @@ struct ehea_eqe *ehea_poll_eq(struct ehea_eq *eq)
unsigned long flags;
spin_lock_irqsave(&eq->spinlock, flags);
- eqe = (struct ehea_eqe *)hw_eqit_eq_get_inc_valid(&eq->hw_queue);
+ eqe = hw_eqit_eq_get_inc_valid(&eq->hw_queue);
spin_unlock_irqrestore(&eq->spinlock, flags);
return eqe;
diff --git a/drivers/net/ehea/ehea_qmr.h b/drivers/net/ethernet/ibm/ehea/ehea_qmr.h
index fddff8ec8cfd..337a47ecf4aa 100644
--- a/drivers/net/ehea/ehea_qmr.h
+++ b/drivers/net/ethernet/ibm/ehea/ehea_qmr.h
@@ -107,7 +107,7 @@ struct ehea_swqe {
u8 immediate_data_length;
u8 tcp_offset;
u8 reserved2;
- u16 tcp_end;
+ u16 reserved2b;
u8 wrap_tag;
u8 descriptors; /* number of valid descriptors in WQE */
u16 reserved3;
diff --git a/drivers/net/ibm_newemac/Kconfig b/drivers/net/ethernet/ibm/emac/Kconfig
index 78a1628c9892..3f44a30e0615 100644
--- a/drivers/net/ibm_newemac/Kconfig
+++ b/drivers/net/ethernet/ibm/emac/Kconfig
@@ -1,4 +1,4 @@
-config IBM_NEW_EMAC
+config IBM_EMAC
tristate "IBM EMAC Ethernet support"
depends on PPC_DCR
select CRC32
@@ -7,29 +7,29 @@ config IBM_NEW_EMAC
typically found on 4xx embedded PowerPC chips, but also on the
Axon southbridge for Cell.
-config IBM_NEW_EMAC_RXB
+config IBM_EMAC_RXB
int "Number of receive buffers"
- depends on IBM_NEW_EMAC
+ depends on IBM_EMAC
default "128"
-config IBM_NEW_EMAC_TXB
+config IBM_EMAC_TXB
int "Number of transmit buffers"
- depends on IBM_NEW_EMAC
+ depends on IBM_EMAC
default "64"
-config IBM_NEW_EMAC_POLL_WEIGHT
+config IBM_EMAC_POLL_WEIGHT
int "MAL NAPI polling weight"
- depends on IBM_NEW_EMAC
+ depends on IBM_EMAC
default "32"
-config IBM_NEW_EMAC_RX_COPY_THRESHOLD
+config IBM_EMAC_RX_COPY_THRESHOLD
int "RX skb copy threshold (bytes)"
- depends on IBM_NEW_EMAC
+ depends on IBM_EMAC
default "256"
-config IBM_NEW_EMAC_RX_SKB_HEADROOM
+config IBM_EMAC_RX_SKB_HEADROOM
int "Additional RX skb headroom (bytes)"
- depends on IBM_NEW_EMAC
+ depends on IBM_EMAC
default "0"
help
Additional receive skb headroom. Note, that driver
@@ -39,38 +39,38 @@ config IBM_NEW_EMAC_RX_SKB_HEADROOM
If unsure, set to 0.
-config IBM_NEW_EMAC_DEBUG
+config IBM_EMAC_DEBUG
bool "Debugging"
- depends on IBM_NEW_EMAC
+ depends on IBM_EMAC
default n
# The options below has to be select'ed by the respective
# processor types or platforms
-config IBM_NEW_EMAC_ZMII
+config IBM_EMAC_ZMII
bool
default n
-config IBM_NEW_EMAC_RGMII
+config IBM_EMAC_RGMII
bool
default n
-config IBM_NEW_EMAC_TAH
+config IBM_EMAC_TAH
bool
default n
-config IBM_NEW_EMAC_EMAC4
+config IBM_EMAC_EMAC4
bool
default n
-config IBM_NEW_EMAC_NO_FLOW_CTRL
+config IBM_EMAC_NO_FLOW_CTRL
bool
default n
-config IBM_NEW_EMAC_MAL_CLR_ICINTSTAT
+config IBM_EMAC_MAL_CLR_ICINTSTAT
bool
default n
-config IBM_NEW_EMAC_MAL_COMMON_ERR
+config IBM_EMAC_MAL_COMMON_ERR
bool
default n
diff --git a/drivers/net/ethernet/ibm/emac/Makefile b/drivers/net/ethernet/ibm/emac/Makefile
new file mode 100644
index 000000000000..eba21835d90d
--- /dev/null
+++ b/drivers/net/ethernet/ibm/emac/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the PowerPC 4xx on-chip ethernet driver
+#
+
+obj-$(CONFIG_IBM_EMAC) += ibm_emac.o
+
+ibm_emac-y := mal.o core.o phy.o
+ibm_emac-$(CONFIG_IBM_EMAC_ZMII) += zmii.o
+ibm_emac-$(CONFIG_IBM_EMAC_RGMII) += rgmii.o
+ibm_emac-$(CONFIG_IBM_EMAC_TAH) += tah.o
+ibm_emac-$(CONFIG_IBM_EMAC_DEBUG) += debug.o
diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index 079450fe5e96..ed79b2d3ad3e 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -39,6 +39,7 @@
#include <linux/bitops.h>
#include <linux/workqueue.h>
#include <linux/of.h>
+#include <linux/of_net.h>
#include <linux/slab.h>
#include <asm/processor.h>
@@ -89,7 +90,7 @@ MODULE_LICENSE("GPL");
/* If packet size is less than this number, we allocate small skb and copy packet
* contents into it instead of just sending original big skb up
*/
-#define EMAC_RX_COPY_THRESH CONFIG_IBM_NEW_EMAC_RX_COPY_THRESHOLD
+#define EMAC_RX_COPY_THRESH CONFIG_IBM_EMAC_RX_COPY_THRESHOLD
/* Since multiple EMACs share MDIO lines in various ways, we need
* to avoid re-using the same PHY ID in cases where the arch didn't
@@ -1452,13 +1453,13 @@ static int emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
/* skb fragments */
for (i = 0; i < nr_frags; ++i) {
struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
- len = frag->size;
+ len = skb_frag_size(frag);
if (unlikely(dev->tx_cnt + mal_tx_chunks(len) >= NUM_TX_BUFF))
goto undo_frame;
- pd = dma_map_page(&dev->ofdev->dev, frag->page, frag->page_offset, len,
- DMA_TO_DEVICE);
+ pd = skb_frag_dma_map(&dev->ofdev->dev, frag, 0, len,
+ DMA_TO_DEVICE);
slot = emac_xmit_split(dev, slot, pd, len, i == nr_frags - 1,
ctrl);
@@ -1617,7 +1618,7 @@ static void emac_parse_rx_error(struct emac_instance *dev, u16 ctrl)
static inline void emac_rx_csum(struct emac_instance *dev,
struct sk_buff *skb, u16 ctrl)
{
-#ifdef CONFIG_IBM_NEW_EMAC_TAH
+#ifdef CONFIG_IBM_EMAC_TAH
if (!ctrl && dev->tah_dev) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
++dev->stats.rx_packets_csum;
@@ -2506,18 +2507,6 @@ static int __devinit emac_init_config(struct emac_instance *dev)
{
struct device_node *np = dev->ofdev->dev.of_node;
const void *p;
- unsigned int plen;
- const char *pm, *phy_modes[] = {
- [PHY_MODE_NA] = "",
- [PHY_MODE_MII] = "mii",
- [PHY_MODE_RMII] = "rmii",
- [PHY_MODE_SMII] = "smii",
- [PHY_MODE_RGMII] = "rgmii",
- [PHY_MODE_TBI] = "tbi",
- [PHY_MODE_GMII] = "gmii",
- [PHY_MODE_RTBI] = "rtbi",
- [PHY_MODE_SGMII] = "sgmii",
- };
/* Read config from device-tree */
if (emac_read_uint_prop(np, "mal-device", &dev->mal_ph, 1))
@@ -2566,23 +2555,9 @@ static int __devinit emac_init_config(struct emac_instance *dev)
dev->mal_burst_size = 256;
/* PHY mode needs some decoding */
- dev->phy_mode = PHY_MODE_NA;
- pm = of_get_property(np, "phy-mode", &plen);
- if (pm != NULL) {
- int i;
- for (i = 0; i < ARRAY_SIZE(phy_modes); i++)
- if (!strcasecmp(pm, phy_modes[i])) {
- dev->phy_mode = i;
- break;
- }
- }
-
- /* Backward compat with non-final DT */
- if (dev->phy_mode == PHY_MODE_NA && pm != NULL && plen == 4) {
- u32 nmode = *(const u32 *)pm;
- if (nmode > PHY_MODE_NA && nmode <= PHY_MODE_SGMII)
- dev->phy_mode = nmode;
- }
+ dev->phy_mode = of_get_phy_mode(np);
+ if (dev->phy_mode < 0)
+ dev->phy_mode = PHY_MODE_NA;
/* Check EMAC version */
if (of_device_is_compatible(np, "ibm,emac4sync")) {
@@ -2602,7 +2577,7 @@ static int __devinit emac_init_config(struct emac_instance *dev)
of_device_is_compatible(np, "ibm,emac-440gr"))
dev->features |= EMAC_FTR_440EP_PHY_CLK_FIX;
if (of_device_is_compatible(np, "ibm,emac-405ez")) {
-#ifdef CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL
+#ifdef CONFIG_IBM_EMAC_NO_FLOW_CTRL
dev->features |= EMAC_FTR_NO_FLOW_CONTROL_40x;
#else
printk(KERN_ERR "%s: Flow control not disabled!\n",
@@ -2626,7 +2601,7 @@ static int __devinit emac_init_config(struct emac_instance *dev)
/* Enable TAH/ZMII/RGMII features as found */
if (dev->tah_ph != 0) {
-#ifdef CONFIG_IBM_NEW_EMAC_TAH
+#ifdef CONFIG_IBM_EMAC_TAH
dev->features |= EMAC_FTR_HAS_TAH;
#else
printk(KERN_ERR "%s: TAH support not enabled !\n",
@@ -2636,7 +2611,7 @@ static int __devinit emac_init_config(struct emac_instance *dev)
}
if (dev->zmii_ph != 0) {
-#ifdef CONFIG_IBM_NEW_EMAC_ZMII
+#ifdef CONFIG_IBM_EMAC_ZMII
dev->features |= EMAC_FTR_HAS_ZMII;
#else
printk(KERN_ERR "%s: ZMII support not enabled !\n",
@@ -2646,7 +2621,7 @@ static int __devinit emac_init_config(struct emac_instance *dev)
}
if (dev->rgmii_ph != 0) {
-#ifdef CONFIG_IBM_NEW_EMAC_RGMII
+#ifdef CONFIG_IBM_EMAC_RGMII
dev->features |= EMAC_FTR_HAS_RGMII;
#else
printk(KERN_ERR "%s: RGMII support not enabled !\n",
@@ -2686,7 +2661,7 @@ static const struct net_device_ops emac_netdev_ops = {
.ndo_open = emac_open,
.ndo_stop = emac_close,
.ndo_get_stats = emac_stats,
- .ndo_set_multicast_list = emac_set_multicast_list,
+ .ndo_set_rx_mode = emac_set_multicast_list,
.ndo_do_ioctl = emac_ioctl,
.ndo_tx_timeout = emac_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
@@ -2699,7 +2674,7 @@ static const struct net_device_ops emac_gige_netdev_ops = {
.ndo_open = emac_open,
.ndo_stop = emac_close,
.ndo_get_stats = emac_stats,
- .ndo_set_multicast_list = emac_set_multicast_list,
+ .ndo_set_rx_mode = emac_set_multicast_list,
.ndo_do_ioctl = emac_ioctl,
.ndo_tx_timeout = emac_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
@@ -2770,7 +2745,7 @@ static int __devinit emac_probe(struct platform_device *ofdev)
}
// TODO : request_mem_region
dev->emacp = ioremap(dev->rsrc_regs.start,
- dev->rsrc_regs.end - dev->rsrc_regs.start + 1);
+ resource_size(&dev->rsrc_regs));
if (dev->emacp == NULL) {
printk(KERN_ERR "%s: Can't map device registers!\n",
np->full_name);
diff --git a/drivers/net/ibm_newemac/core.h b/drivers/net/ethernet/ibm/emac/core.h
index 4fec0844d59d..fa3ec57935fa 100644
--- a/drivers/net/ibm_newemac/core.h
+++ b/drivers/net/ethernet/ibm/emac/core.h
@@ -47,8 +47,8 @@
#include "tah.h"
#include "debug.h"
-#define NUM_TX_BUFF CONFIG_IBM_NEW_EMAC_TXB
-#define NUM_RX_BUFF CONFIG_IBM_NEW_EMAC_RXB
+#define NUM_TX_BUFF CONFIG_IBM_EMAC_TXB
+#define NUM_RX_BUFF CONFIG_IBM_EMAC_RXB
/* Simple sanity check */
#if NUM_TX_BUFF > 256 || NUM_RX_BUFF > 256
@@ -72,7 +72,7 @@ static inline int emac_rx_size(int mtu)
#define EMAC_DMA_ALIGN(x) ALIGN((x), dma_get_cache_alignment())
#define EMAC_RX_SKB_HEADROOM \
- EMAC_DMA_ALIGN(CONFIG_IBM_NEW_EMAC_RX_SKB_HEADROOM)
+ EMAC_DMA_ALIGN(CONFIG_IBM_EMAC_RX_SKB_HEADROOM)
/* Size of RX skb for the given MTU */
static inline int emac_rx_skb_size(int mtu)
@@ -335,21 +335,21 @@ enum {
EMAC_FTRS_ALWAYS = 0,
EMAC_FTRS_POSSIBLE =
-#ifdef CONFIG_IBM_NEW_EMAC_EMAC4
+#ifdef CONFIG_IBM_EMAC_EMAC4
EMAC_FTR_EMAC4 | EMAC_FTR_EMAC4SYNC |
EMAC_FTR_HAS_NEW_STACR |
EMAC_FTR_STACR_OC_INVERT | EMAC_FTR_440GX_PHY_CLK_FIX |
#endif
-#ifdef CONFIG_IBM_NEW_EMAC_TAH
+#ifdef CONFIG_IBM_EMAC_TAH
EMAC_FTR_HAS_TAH |
#endif
-#ifdef CONFIG_IBM_NEW_EMAC_ZMII
+#ifdef CONFIG_IBM_EMAC_ZMII
EMAC_FTR_HAS_ZMII |
#endif
-#ifdef CONFIG_IBM_NEW_EMAC_RGMII
+#ifdef CONFIG_IBM_EMAC_RGMII
EMAC_FTR_HAS_RGMII |
#endif
-#ifdef CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL
+#ifdef CONFIG_IBM_EMAC_NO_FLOW_CTRL
EMAC_FTR_NO_FLOW_CONTROL_40x |
#endif
EMAC_FTR_460EX_PHY_CLK_FIX |
diff --git a/drivers/net/ibm_newemac/debug.c b/drivers/net/ethernet/ibm/emac/debug.c
index 8c6c1e2a8750..8c6c1e2a8750 100644
--- a/drivers/net/ibm_newemac/debug.c
+++ b/drivers/net/ethernet/ibm/emac/debug.c
diff --git a/drivers/net/ibm_newemac/debug.h b/drivers/net/ethernet/ibm/emac/debug.h
index e596c77ccdf7..90477fe69d0c 100644
--- a/drivers/net/ibm_newemac/debug.h
+++ b/drivers/net/ethernet/ibm/emac/debug.h
@@ -24,7 +24,7 @@
#include "core.h"
-#if defined(CONFIG_IBM_NEW_EMAC_DEBUG)
+#if defined(CONFIG_IBM_EMAC_DEBUG)
struct emac_instance;
struct mal_instance;
diff --git a/drivers/net/ibm_newemac/emac.h b/drivers/net/ethernet/ibm/emac/emac.h
index 8a61b597a169..1568278d759a 100644
--- a/drivers/net/ibm_newemac/emac.h
+++ b/drivers/net/ethernet/ibm/emac/emac.h
@@ -26,6 +26,7 @@
#define __IBM_NEWEMAC_H
#include <linux/types.h>
+#include <linux/phy.h>
/* EMAC registers Write Access rules */
struct emac_regs {
@@ -106,15 +107,15 @@ struct emac_regs {
/*
* PHY mode settings (EMAC <-> ZMII/RGMII bridge <-> PHY)
*/
-#define PHY_MODE_NA 0
-#define PHY_MODE_MII 1
-#define PHY_MODE_RMII 2
-#define PHY_MODE_SMII 3
-#define PHY_MODE_RGMII 4
-#define PHY_MODE_TBI 5
-#define PHY_MODE_GMII 6
-#define PHY_MODE_RTBI 7
-#define PHY_MODE_SGMII 8
+#define PHY_MODE_NA PHY_INTERFACE_MODE_NA
+#define PHY_MODE_MII PHY_INTERFACE_MODE_MII
+#define PHY_MODE_RMII PHY_INTERFACE_MODE_RMII
+#define PHY_MODE_SMII PHY_INTERFACE_MODE_SMII
+#define PHY_MODE_RGMII PHY_INTERFACE_MODE_RGMII
+#define PHY_MODE_TBI PHY_INTERFACE_MODE_TBI
+#define PHY_MODE_GMII PHY_INTERFACE_MODE_GMII
+#define PHY_MODE_RTBI PHY_INTERFACE_MODE_RTBI
+#define PHY_MODE_SGMII PHY_INTERFACE_MODE_SGMII
/* EMACx_MR0 */
#define EMAC_MR0_RXI 0x80000000
diff --git a/drivers/net/ibm_newemac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index d268f404b7b0..f3c50b97ec61 100644
--- a/drivers/net/ibm_newemac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -577,8 +577,8 @@ static int __devinit mal_probe(struct platform_device *ofdev)
}
if (of_device_is_compatible(ofdev->dev.of_node, "ibm,mcmal-405ez")) {
-#if defined(CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT) && \
- defined(CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR)
+#if defined(CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT) && \
+ defined(CONFIG_IBM_EMAC_MAL_COMMON_ERR)
mal->features |= (MAL_FTR_CLEAR_ICINTSTAT |
MAL_FTR_COMMON_ERR_INT);
#else
@@ -616,7 +616,7 @@ static int __devinit mal_probe(struct platform_device *ofdev)
init_dummy_netdev(&mal->dummy_dev);
netif_napi_add(&mal->dummy_dev, &mal->napi, mal_poll,
- CONFIG_IBM_NEW_EMAC_POLL_WEIGHT);
+ CONFIG_IBM_EMAC_POLL_WEIGHT);
/* Load power-on reset defaults */
mal_reset(mal);
diff --git a/drivers/net/ibm_newemac/mal.h b/drivers/net/ethernet/ibm/emac/mal.h
index 66084214bf45..d06f985bda32 100644
--- a/drivers/net/ibm_newemac/mal.h
+++ b/drivers/net/ethernet/ibm/emac/mal.h
@@ -245,10 +245,10 @@ enum {
MAL_FTRS_ALWAYS = 0,
MAL_FTRS_POSSIBLE =
-#ifdef CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT
+#ifdef CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT
MAL_FTR_CLEAR_ICINTSTAT |
#endif
-#ifdef CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR
+#ifdef CONFIG_IBM_EMAC_MAL_COMMON_ERR
MAL_FTR_COMMON_ERR_INT |
#endif
0,
diff --git a/drivers/net/ibm_newemac/phy.c b/drivers/net/ethernet/ibm/emac/phy.c
index ac9d964e59ec..ab4e5969fe65 100644
--- a/drivers/net/ibm_newemac/phy.c
+++ b/drivers/net/ethernet/ibm/emac/phy.c
@@ -28,12 +28,15 @@
#include "emac.h"
#include "phy.h"
-static inline int phy_read(struct mii_phy *phy, int reg)
+#define phy_read _phy_read
+#define phy_write _phy_write
+
+static inline int _phy_read(struct mii_phy *phy, int reg)
{
return phy->mdio_read(phy->dev, phy->address, reg);
}
-static inline void phy_write(struct mii_phy *phy, int reg, int val)
+static inline void _phy_write(struct mii_phy *phy, int reg, int val)
{
phy->mdio_write(phy->dev, phy->address, reg, val);
}
diff --git a/drivers/net/ibm_newemac/phy.h b/drivers/net/ethernet/ibm/emac/phy.h
index 5d2bf4cbe50b..5d2bf4cbe50b 100644
--- a/drivers/net/ibm_newemac/phy.h
+++ b/drivers/net/ethernet/ibm/emac/phy.h
diff --git a/drivers/net/ibm_newemac/rgmii.c b/drivers/net/ethernet/ibm/emac/rgmii.c
index 4fa53f3def64..4fa53f3def64 100644
--- a/drivers/net/ibm_newemac/rgmii.c
+++ b/drivers/net/ethernet/ibm/emac/rgmii.c
diff --git a/drivers/net/ibm_newemac/rgmii.h b/drivers/net/ethernet/ibm/emac/rgmii.h
index d69799049865..9296b6c5f920 100644
--- a/drivers/net/ibm_newemac/rgmii.h
+++ b/drivers/net/ethernet/ibm/emac/rgmii.h
@@ -54,7 +54,7 @@ struct rgmii_instance {
struct platform_device *ofdev;
};
-#ifdef CONFIG_IBM_NEW_EMAC_RGMII
+#ifdef CONFIG_IBM_EMAC_RGMII
extern int rgmii_init(void);
extern void rgmii_exit(void);
@@ -77,6 +77,6 @@ extern void *rgmii_dump_regs(struct platform_device *ofdev, void *buf);
# define rgmii_set_speed(x,y,z) do { } while(0)
# define rgmii_get_regs_len(x) 0
# define rgmii_dump_regs(x,buf) (buf)
-#endif /* !CONFIG_IBM_NEW_EMAC_RGMII */
+#endif /* !CONFIG_IBM_EMAC_RGMII */
#endif /* __IBM_NEWEMAC_RGMII_H */
diff --git a/drivers/net/ibm_newemac/tah.c b/drivers/net/ethernet/ibm/emac/tah.c
index 5f51bf7c9dc5..5f51bf7c9dc5 100644
--- a/drivers/net/ibm_newemac/tah.c
+++ b/drivers/net/ethernet/ibm/emac/tah.c
diff --git a/drivers/net/ibm_newemac/tah.h b/drivers/net/ethernet/ibm/emac/tah.h
index 61dbeca006d1..3437ab4964c7 100644
--- a/drivers/net/ibm_newemac/tah.h
+++ b/drivers/net/ethernet/ibm/emac/tah.h
@@ -70,7 +70,7 @@ struct tah_instance {
#define TAH_MR_DTFP 0x00100000
#define TAH_MR_DIG 0x00080000
-#ifdef CONFIG_IBM_NEW_EMAC_TAH
+#ifdef CONFIG_IBM_EMAC_TAH
extern int tah_init(void);
extern void tah_exit(void);
@@ -90,6 +90,6 @@ extern void *tah_dump_regs(struct platform_device *ofdev, void *buf);
# define tah_get_regs_len(x) 0
# define tah_dump_regs(x,buf) (buf)
-#endif /* !CONFIG_IBM_NEW_EMAC_TAH */
+#endif /* !CONFIG_IBM_EMAC_TAH */
#endif /* __IBM_NEWEMAC_TAH_H */
diff --git a/drivers/net/ibm_newemac/zmii.c b/drivers/net/ethernet/ibm/emac/zmii.c
index 97449e786d61..97449e786d61 100644
--- a/drivers/net/ibm_newemac/zmii.c
+++ b/drivers/net/ethernet/ibm/emac/zmii.c
diff --git a/drivers/net/ibm_newemac/zmii.h b/drivers/net/ethernet/ibm/emac/zmii.h
index 1333fa2b2781..ceaed823a83c 100644
--- a/drivers/net/ibm_newemac/zmii.h
+++ b/drivers/net/ethernet/ibm/emac/zmii.h
@@ -51,7 +51,7 @@ struct zmii_instance {
struct platform_device *ofdev;
};
-#ifdef CONFIG_IBM_NEW_EMAC_ZMII
+#ifdef CONFIG_IBM_EMAC_ZMII
extern int zmii_init(void);
extern void zmii_exit(void);
@@ -73,6 +73,6 @@ extern void *zmii_dump_regs(struct platform_device *ofdev, void *buf);
# define zmii_set_speed(x,y,z) do { } while(0)
# define zmii_get_regs_len(x) 0
# define zmii_dump_regs(x,buf) (buf)
-#endif /* !CONFIG_IBM_NEW_EMAC_ZMII */
+#endif /* !CONFIG_IBM_EMAC_ZMII */
#endif /* __IBM_NEWEMAC_ZMII_H */
diff --git a/drivers/net/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index b388d782c7c4..b1cd41b9c61c 100644
--- a/drivers/net/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -34,6 +34,7 @@
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/pm.h>
#include <linux/ethtool.h>
@@ -42,7 +43,7 @@
#include <linux/ipv6.h>
#include <linux/slab.h>
#include <asm/hvcall.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/vio.h>
#include <asm/iommu.h>
#include <asm/firmware.h>
@@ -394,7 +395,7 @@ static inline struct sk_buff *ibmveth_rxq_get_buffer(struct ibmveth_adapter *ada
}
/* recycle the current buffer on the rx queue */
-static void ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter)
+static int ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter)
{
u32 q_index = adapter->rx_queue.index;
u64 correlator = adapter->rx_queue.queue_addr[q_index].correlator;
@@ -402,6 +403,7 @@ static void ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter)
unsigned int index = correlator & 0xffffffffUL;
union ibmveth_buf_desc desc;
unsigned long lpar_rc;
+ int ret = 1;
BUG_ON(pool >= IBMVETH_NUM_BUFF_POOLS);
BUG_ON(index >= adapter->rx_buff_pool[pool].size);
@@ -409,7 +411,7 @@ static void ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter)
if (!adapter->rx_buff_pool[pool].active) {
ibmveth_rxq_harvest_buffer(adapter);
ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[pool]);
- return;
+ goto out;
}
desc.fields.flags_len = IBMVETH_BUF_VALID |
@@ -422,12 +424,16 @@ static void ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter)
netdev_dbg(adapter->netdev, "h_add_logical_lan_buffer failed "
"during recycle rc=%ld", lpar_rc);
ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator);
+ ret = 0;
}
if (++adapter->rx_queue.index == adapter->rx_queue.num_slots) {
adapter->rx_queue.index = 0;
adapter->rx_queue.toggle = !adapter->rx_queue.toggle;
}
+
+out:
+ return ret;
}
static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter)
@@ -630,8 +636,8 @@ static int ibmveth_open(struct net_device *netdev)
netdev_err(netdev, "unable to request irq 0x%x, rc %d\n",
netdev->irq, rc);
do {
- rc = h_free_logical_lan(adapter->vdev->unit_address);
- } while (H_IS_LONG_BUSY(rc) || (rc == H_BUSY));
+ lpar_rc = h_free_logical_lan(adapter->vdev->unit_address);
+ } while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
goto err_out;
}
@@ -751,7 +757,7 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data)
struct ibmveth_adapter *adapter = netdev_priv(dev);
unsigned long set_attr, clr_attr, ret_attr;
unsigned long set_attr6, clr_attr6;
- long ret, ret6;
+ long ret, ret4, ret6;
int rc1 = 0, rc2 = 0;
int restart = 0;
@@ -764,6 +770,8 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data)
set_attr = 0;
clr_attr = 0;
+ set_attr6 = 0;
+ clr_attr6 = 0;
if (data) {
set_attr = IBMVETH_ILLAN_IPV4_TCP_CSUM;
@@ -778,16 +786,20 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data)
if (ret == H_SUCCESS && !(ret_attr & IBMVETH_ILLAN_ACTIVE_TRUNK) &&
!(ret_attr & IBMVETH_ILLAN_TRUNK_PRI_MASK) &&
(ret_attr & IBMVETH_ILLAN_PADDED_PKT_CSUM)) {
- ret = h_illan_attributes(adapter->vdev->unit_address, clr_attr,
+ ret4 = h_illan_attributes(adapter->vdev->unit_address, clr_attr,
set_attr, &ret_attr);
- if (ret != H_SUCCESS) {
+ if (ret4 != H_SUCCESS) {
netdev_err(dev, "unable to change IPv4 checksum "
"offload settings. %d rc=%ld\n",
- data, ret);
+ data, ret4);
+
+ h_illan_attributes(adapter->vdev->unit_address,
+ set_attr, clr_attr, &ret_attr);
+
+ if (data == 1)
+ dev->features &= ~NETIF_F_IP_CSUM;
- ret = h_illan_attributes(adapter->vdev->unit_address,
- set_attr, clr_attr, &ret_attr);
} else {
adapter->fw_ipv4_csum_support = data;
}
@@ -798,15 +810,18 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data)
if (ret6 != H_SUCCESS) {
netdev_err(dev, "unable to change IPv6 checksum "
"offload settings. %d rc=%ld\n",
- data, ret);
+ data, ret6);
+
+ h_illan_attributes(adapter->vdev->unit_address,
+ set_attr6, clr_attr6, &ret_attr);
+
+ if (data == 1)
+ dev->features &= ~NETIF_F_IPV6_CSUM;
- ret = h_illan_attributes(adapter->vdev->unit_address,
- set_attr6, clr_attr6,
- &ret_attr);
} else
adapter->fw_ipv6_csum_support = data;
- if (ret != H_SUCCESS || ret6 != H_SUCCESS)
+ if (ret4 == H_SUCCESS || ret6 == H_SUCCESS)
adapter->rx_csum = data;
else
rc1 = -EIO;
@@ -924,6 +939,7 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
union ibmveth_buf_desc descs[6];
int last, i;
int force_bounce = 0;
+ dma_addr_t dma_addr;
/*
* veth handles a maximum of 6 segments including the header, so
@@ -988,27 +1004,25 @@ retry_bounce:
}
/* Map the header */
- descs[0].fields.address = dma_map_single(&adapter->vdev->dev, skb->data,
- skb_headlen(skb),
- DMA_TO_DEVICE);
- if (dma_mapping_error(&adapter->vdev->dev, descs[0].fields.address))
+ dma_addr = dma_map_single(&adapter->vdev->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ if (dma_mapping_error(&adapter->vdev->dev, dma_addr))
goto map_failed;
descs[0].fields.flags_len = desc_flags | skb_headlen(skb);
+ descs[0].fields.address = dma_addr;
/* Map the frags */
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- unsigned long dma_addr;
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- dma_addr = dma_map_page(&adapter->vdev->dev, frag->page,
- frag->page_offset, frag->size,
- DMA_TO_DEVICE);
+ dma_addr = skb_frag_dma_map(&adapter->vdev->dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
if (dma_mapping_error(&adapter->vdev->dev, dma_addr))
goto map_failed_frags;
- descs[i+1].fields.flags_len = desc_flags | frag->size;
+ descs[i+1].fields.flags_len = desc_flags | skb_frag_size(frag);
descs[i+1].fields.address = dma_addr;
}
@@ -1020,7 +1034,12 @@ retry_bounce:
netdev->stats.tx_bytes += skb->len;
}
- for (i = 0; i < skb_shinfo(skb)->nr_frags + 1; i++)
+ dma_unmap_single(&adapter->vdev->dev,
+ descs[0].fields.address,
+ descs[0].fields.flags_len & IBMVETH_BUF_LEN_MASK,
+ DMA_TO_DEVICE);
+
+ for (i = 1; i < skb_shinfo(skb)->nr_frags + 1; i++)
dma_unmap_page(&adapter->vdev->dev, descs[i].fields.address,
descs[i].fields.flags_len & IBMVETH_BUF_LEN_MASK,
DMA_TO_DEVICE);
@@ -1083,8 +1102,9 @@ restart_poll:
if (rx_flush)
ibmveth_flush_buffer(skb->data,
length + offset);
+ if (!ibmveth_rxq_recycle_buffer(adapter))
+ kfree_skb(skb);
skb = new_skb;
- ibmveth_rxq_recycle_buffer(adapter);
} else {
ibmveth_rxq_harvest_buffer(adapter);
skb_reserve(skb, offset);
@@ -1298,7 +1318,7 @@ static const struct net_device_ops ibmveth_netdev_ops = {
.ndo_open = ibmveth_open,
.ndo_stop = ibmveth_close,
.ndo_start_xmit = ibmveth_start_xmit,
- .ndo_set_multicast_list = ibmveth_set_multicast_list,
+ .ndo_set_rx_mode = ibmveth_set_multicast_list,
.ndo_do_ioctl = ibmveth_ioctl,
.ndo_change_mtu = ibmveth_change_mtu,
.ndo_fix_features = ibmveth_fix_features,
diff --git a/drivers/net/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h
index 43a794fab9ff..43a794fab9ff 100644
--- a/drivers/net/ibmveth.h
+++ b/drivers/net/ethernet/ibm/ibmveth.h
diff --git a/drivers/net/iseries_veth.c b/drivers/net/ethernet/ibm/iseries_veth.c
index 9ece1fd9889d..4326681df382 100644
--- a/drivers/net/iseries_veth.c
+++ b/drivers/net/ethernet/ibm/iseries_veth.c
@@ -538,7 +538,7 @@ static void veth_handle_ack(struct veth_lpevent *event)
default:
veth_error("Unknown ack type %d from LPAR %d.\n",
event->base_event.xSubtype, rlp);
- };
+ }
}
static void veth_handle_int(struct veth_lpevent *event)
@@ -584,7 +584,7 @@ static void veth_handle_int(struct veth_lpevent *event)
default:
veth_error("Unknown interrupt type %d from LPAR %d.\n",
event->base_event.xSubtype, rlp);
- };
+ }
}
static void veth_handle_event(struct HvLpEvent *event)
@@ -964,11 +964,9 @@ static void veth_set_multicast_list(struct net_device *dev)
u8 *addr = ha->addr;
u64 xaddr = 0;
- if (addr[0] & 0x01) {/* multicast address? */
- memcpy(&xaddr, addr, ETH_ALEN);
- port->mcast_addr[port->num_mcast] = xaddr;
- port->num_mcast++;
- }
+ memcpy(&xaddr, addr, ETH_ALEN);
+ port->mcast_addr[port->num_mcast] = xaddr;
+ port->num_mcast++;
}
}
@@ -1011,7 +1009,7 @@ static const struct net_device_ops veth_netdev_ops = {
.ndo_stop = veth_close,
.ndo_start_xmit = veth_start_xmit,
.ndo_change_mtu = veth_change_mtu,
- .ndo_set_multicast_list = veth_set_multicast_list,
+ .ndo_set_rx_mode = veth_set_multicast_list,
.ndo_set_mac_address = NULL,
.ndo_validate_addr = eth_validate_addr,
};
@@ -1184,7 +1182,7 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct veth_port *port = netdev_priv(dev);
HvLpIndexMap lpmask;
- if (! (frame[0] & 0x01)) {
+ if (is_unicast_ether_addr(frame)) {
/* unicast packet */
HvLpIndex rlp = frame[5];
diff --git a/drivers/net/ethernet/icplus/Kconfig b/drivers/net/ethernet/icplus/Kconfig
new file mode 100644
index 000000000000..3aff81d7989f
--- /dev/null
+++ b/drivers/net/ethernet/icplus/Kconfig
@@ -0,0 +1,14 @@
+#
+# IC Plus device configuration
+#
+
+config IP1000
+ tristate "IP1000 Gigabit Ethernet support"
+ depends on PCI && EXPERIMENTAL
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports IP1000 gigabit Ethernet cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ipg. This is recommended.
diff --git a/drivers/net/ethernet/icplus/Makefile b/drivers/net/ethernet/icplus/Makefile
new file mode 100644
index 000000000000..5bc87c1f36aa
--- /dev/null
+++ b/drivers/net/ethernet/icplus/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the IC Plus device drivers
+#
+
+obj-$(CONFIG_IP1000) += ipg.o
diff --git a/drivers/net/ipg.c b/drivers/net/ethernet/icplus/ipg.c
index 58cd3202b48c..8fd80a00b898 100644
--- a/drivers/net/ipg.c
+++ b/drivers/net/ethernet/icplus/ipg.c
@@ -20,8 +20,12 @@
* http://www.icplus.com.tw
* jesse@icplus.com.tw
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/crc32.h>
#include <linux/ethtool.h>
+#include <linux/interrupt.h>
#include <linux/gfp.h>
#include <linux/mii.h>
#include <linux/mutex.h>
@@ -66,7 +70,7 @@ MODULE_LICENSE("GPL");
* Variable record -- index by leading revision/length
* Revision/Length(=N*4), Address1, Data1, Address2, Data2,...,AddressN,DataN
*/
-static unsigned short DefaultPhyParam[] = {
+static const unsigned short DefaultPhyParam[] = {
/* 11/12/03 IP1000A v1-3 rev=0x40 */
/*--------------------------------------------------------------------------
(0x4000|(15*4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 22, 0x85bd, 24, 0xfff2,
@@ -84,7 +88,7 @@ static unsigned short DefaultPhyParam[] = {
0x0000
};
-static const char *ipg_brand_name[] = {
+static const char * const ipg_brand_name[] = {
"IC PLUS IP1000 1000/100/10 based NIC",
"Sundance Technology ST2021 based NIC",
"Tamarack Microelectronics TC9020/9021 based NIC",
@@ -117,23 +121,23 @@ static void ipg_dump_rfdlist(struct net_device *dev)
IPG_DEBUG_MSG("_dump_rfdlist\n");
- printk(KERN_INFO "rx_current = %2.2x\n", sp->rx_current);
- printk(KERN_INFO "rx_dirty = %2.2x\n", sp->rx_dirty);
- printk(KERN_INFO "RFDList start address = %16.16lx\n",
- (unsigned long) sp->rxd_map);
- printk(KERN_INFO "RFDListPtr register = %8.8x%8.8x\n",
- ipg_r32(IPG_RFDLISTPTR1), ipg_r32(IPG_RFDLISTPTR0));
+ netdev_info(dev, "rx_current = %02x\n", sp->rx_current);
+ netdev_info(dev, "rx_dirty = %02x\n", sp->rx_dirty);
+ netdev_info(dev, "RFDList start address = %016lx\n",
+ (unsigned long)sp->rxd_map);
+ netdev_info(dev, "RFDListPtr register = %08x%08x\n",
+ ipg_r32(IPG_RFDLISTPTR1), ipg_r32(IPG_RFDLISTPTR0));
for (i = 0; i < IPG_RFDLIST_LENGTH; i++) {
offset = (u32) &sp->rxd[i].next_desc - (u32) sp->rxd;
- printk(KERN_INFO "%2.2x %4.4x RFDNextPtr = %16.16lx\n", i,
- offset, (unsigned long) sp->rxd[i].next_desc);
+ netdev_info(dev, "%02x %04x RFDNextPtr = %016lx\n",
+ i, offset, (unsigned long)sp->rxd[i].next_desc);
offset = (u32) &sp->rxd[i].rfs - (u32) sp->rxd;
- printk(KERN_INFO "%2.2x %4.4x RFS = %16.16lx\n", i,
- offset, (unsigned long) sp->rxd[i].rfs);
+ netdev_info(dev, "%02x %04x RFS = %016lx\n",
+ i, offset, (unsigned long)sp->rxd[i].rfs);
offset = (u32) &sp->rxd[i].frag_info - (u32) sp->rxd;
- printk(KERN_INFO "%2.2x %4.4x frag_info = %16.16lx\n", i,
- offset, (unsigned long) sp->rxd[i].frag_info);
+ netdev_info(dev, "%02x %04x frag_info = %016lx\n",
+ i, offset, (unsigned long)sp->rxd[i].frag_info);
}
}
@@ -146,24 +150,24 @@ static void ipg_dump_tfdlist(struct net_device *dev)
IPG_DEBUG_MSG("_dump_tfdlist\n");
- printk(KERN_INFO "tx_current = %2.2x\n", sp->tx_current);
- printk(KERN_INFO "tx_dirty = %2.2x\n", sp->tx_dirty);
- printk(KERN_INFO "TFDList start address = %16.16lx\n",
- (unsigned long) sp->txd_map);
- printk(KERN_INFO "TFDListPtr register = %8.8x%8.8x\n",
- ipg_r32(IPG_TFDLISTPTR1), ipg_r32(IPG_TFDLISTPTR0));
+ netdev_info(dev, "tx_current = %02x\n", sp->tx_current);
+ netdev_info(dev, "tx_dirty = %02x\n", sp->tx_dirty);
+ netdev_info(dev, "TFDList start address = %016lx\n",
+ (unsigned long) sp->txd_map);
+ netdev_info(dev, "TFDListPtr register = %08x%08x\n",
+ ipg_r32(IPG_TFDLISTPTR1), ipg_r32(IPG_TFDLISTPTR0));
for (i = 0; i < IPG_TFDLIST_LENGTH; i++) {
offset = (u32) &sp->txd[i].next_desc - (u32) sp->txd;
- printk(KERN_INFO "%2.2x %4.4x TFDNextPtr = %16.16lx\n", i,
- offset, (unsigned long) sp->txd[i].next_desc);
+ netdev_info(dev, "%02x %04x TFDNextPtr = %016lx\n",
+ i, offset, (unsigned long)sp->txd[i].next_desc);
offset = (u32) &sp->txd[i].tfc - (u32) sp->txd;
- printk(KERN_INFO "%2.2x %4.4x TFC = %16.16lx\n", i,
- offset, (unsigned long) sp->txd[i].tfc);
+ netdev_info(dev, "%02x %04x TFC = %016lx\n",
+ i, offset, (unsigned long) sp->txd[i].tfc);
offset = (u32) &sp->txd[i].frag_info - (u32) sp->txd;
- printk(KERN_INFO "%2.2x %4.4x frag_info = %16.16lx\n", i,
- offset, (unsigned long) sp->txd[i].frag_info);
+ netdev_info(dev, "%02x %04x frag_info = %016lx\n",
+ i, offset, (unsigned long) sp->txd[i].frag_info);
}
}
#endif
@@ -479,6 +483,10 @@ static int ipg_config_autoneg(struct net_device *dev)
u32 mac_ctrl_val;
u32 asicctrl;
u8 phyctrl;
+ const char *speed;
+ const char *duplex;
+ const char *tx_desc;
+ const char *rx_desc;
IPG_DEBUG_MSG("_config_autoneg\n");
@@ -498,27 +506,27 @@ static int ipg_config_autoneg(struct net_device *dev)
*/
sp->tenmbpsmode = 0;
- printk(KERN_INFO "%s: Link speed = ", dev->name);
-
/* Determine actual speed of operation. */
switch (phyctrl & IPG_PC_LINK_SPEED) {
case IPG_PC_LINK_SPEED_10MBPS:
- printk("10Mbps.\n");
- printk(KERN_INFO "%s: 10Mbps operational mode enabled.\n",
- dev->name);
+ speed = "10Mbps";
sp->tenmbpsmode = 1;
break;
case IPG_PC_LINK_SPEED_100MBPS:
- printk("100Mbps.\n");
+ speed = "100Mbps";
break;
case IPG_PC_LINK_SPEED_1000MBPS:
- printk("1000Mbps.\n");
+ speed = "1000Mbps";
break;
default:
- printk("undefined!\n");
+ speed = "undefined!";
return 0;
}
+ netdev_info(dev, "Link speed = %s\n", speed);
+ if (sp->tenmbpsmode == 1)
+ netdev_info(dev, "10Mbps operational mode enabled\n");
+
if (phyctrl & IPG_PC_DUPLEX_STATUS) {
fullduplex = 1;
txflowcontrol = 1;
@@ -527,38 +535,41 @@ static int ipg_config_autoneg(struct net_device *dev)
/* Configure full duplex, and flow control. */
if (fullduplex == 1) {
+
/* Configure IPG for full duplex operation. */
- printk(KERN_INFO "%s: setting full duplex, ", dev->name);
+
+ duplex = "full";
mac_ctrl_val |= IPG_MC_DUPLEX_SELECT_FD;
if (txflowcontrol == 1) {
- printk("TX flow control");
+ tx_desc = "";
mac_ctrl_val |= IPG_MC_TX_FLOW_CONTROL_ENABLE;
} else {
- printk("no TX flow control");
+ tx_desc = "no ";
mac_ctrl_val &= ~IPG_MC_TX_FLOW_CONTROL_ENABLE;
}
if (rxflowcontrol == 1) {
- printk(", RX flow control.");
+ rx_desc = "";
mac_ctrl_val |= IPG_MC_RX_FLOW_CONTROL_ENABLE;
} else {
- printk(", no RX flow control.");
+ rx_desc = "no ";
mac_ctrl_val &= ~IPG_MC_RX_FLOW_CONTROL_ENABLE;
}
-
- printk("\n");
} else {
- /* Configure IPG for half duplex operation. */
- printk(KERN_INFO "%s: setting half duplex, "
- "no TX flow control, no RX flow control.\n", dev->name);
-
- mac_ctrl_val &= ~IPG_MC_DUPLEX_SELECT_FD &
- ~IPG_MC_TX_FLOW_CONTROL_ENABLE &
- ~IPG_MC_RX_FLOW_CONTROL_ENABLE;
+ duplex = "half";
+ tx_desc = "no ";
+ rx_desc = "no ";
+ mac_ctrl_val &= (~IPG_MC_DUPLEX_SELECT_FD &
+ ~IPG_MC_TX_FLOW_CONTROL_ENABLE &
+ ~IPG_MC_RX_FLOW_CONTROL_ENABLE);
}
+
+ netdev_info(dev, "setting %s duplex, %sTX, %sRX flow control\n",
+ duplex, tx_desc, rx_desc);
ipg_w32(mac_ctrl_val, MAC_CTRL);
+
return 0;
}
@@ -783,14 +794,13 @@ static int init_rfdlist(struct net_device *dev)
* A receive buffer was not ready, break the
* RFD list here.
*/
- IPG_DEBUG_MSG("Cannot allocate Rx buffer.\n");
+ IPG_DEBUG_MSG("Cannot allocate Rx buffer\n");
/* Just in case we cannot allocate a single RFD.
* Should not occur.
*/
if (i == 0) {
- printk(KERN_ERR "%s: No memory available"
- " for RFD list.\n", dev->name);
+ netdev_err(dev, "No memory available for RFD list\n");
return -ENOMEM;
}
}
@@ -837,7 +847,7 @@ static void init_tfdlist(struct net_device *dev)
sp->tx_dirty = 0;
/* Write the location of the TFDList to the IPG. */
- IPG_DDEBUG_MSG("Starting TFDListPtr = %8.8x\n",
+ IPG_DDEBUG_MSG("Starting TFDListPtr = %08x\n",
(u32) sp->txd_map);
ipg_w32((u32) sp->txd_map, TFD_LIST_PTR_0);
ipg_w32(0x00000000, TFD_LIST_PTR_1);
@@ -863,7 +873,7 @@ static void ipg_nic_txfree(struct net_device *dev)
struct sk_buff *skb = sp->tx_buff[dirty];
struct ipg_tx *txfd = sp->txd + dirty;
- IPG_DEBUG_MSG("TFC = %16.16lx\n", (unsigned long) txfd->tfc);
+ IPG_DEBUG_MSG("TFC = %016lx\n", (unsigned long) txfd->tfc);
/* Look at each TFD's TFC field beginning
* at the last freed TFD up to the current TFD.
@@ -905,10 +915,8 @@ static void ipg_tx_timeout(struct net_device *dev)
spin_lock_irq(&sp->lock);
/* Re-configure after DMA reset. */
- if (ipg_io_config(dev) < 0) {
- printk(KERN_INFO "%s: Error during re-configuration.\n",
- dev->name);
- }
+ if (ipg_io_config(dev) < 0)
+ netdev_info(dev, "Error during re-configuration\n");
init_tfdlist(dev);
@@ -937,7 +945,7 @@ static void ipg_nic_txcleanup(struct net_device *dev)
*/
u32 txstatusdword = ipg_r32(TX_STATUS);
- IPG_DEBUG_MSG("TxStatus = %8.8x\n", txstatusdword);
+ IPG_DEBUG_MSG("TxStatus = %08x\n", txstatusdword);
/* Check for Transmit errors. Error bits only valid if
* TX_COMPLETE bit in the TXSTATUS register is a 1.
@@ -952,20 +960,20 @@ static void ipg_nic_txcleanup(struct net_device *dev)
/* Transmit error, increment stat counters. */
if (txstatusdword & IPG_TS_TX_ERROR) {
- IPG_DEBUG_MSG("Transmit error.\n");
+ IPG_DEBUG_MSG("Transmit error\n");
sp->stats.tx_errors++;
}
/* Late collision, re-enable transmitter. */
if (txstatusdword & IPG_TS_LATE_COLLISION) {
- IPG_DEBUG_MSG("Late collision on transmit.\n");
+ IPG_DEBUG_MSG("Late collision on transmit\n");
ipg_w32((ipg_r32(MAC_CTRL) | IPG_MC_TX_ENABLE) &
IPG_MC_RSVD_MASK, MAC_CTRL);
}
/* Maximum collisions, re-enable transmitter. */
if (txstatusdword & IPG_TS_TX_MAX_COLL) {
- IPG_DEBUG_MSG("Maximum collisions on transmit.\n");
+ IPG_DEBUG_MSG("Maximum collisions on transmit\n");
ipg_w32((ipg_r32(MAC_CTRL) | IPG_MC_TX_ENABLE) &
IPG_MC_RSVD_MASK, MAC_CTRL);
}
@@ -974,16 +982,14 @@ static void ipg_nic_txcleanup(struct net_device *dev)
* transmitter.
*/
if (txstatusdword & IPG_TS_TX_UNDERRUN) {
- IPG_DEBUG_MSG("Transmitter underrun.\n");
+ IPG_DEBUG_MSG("Transmitter underrun\n");
sp->stats.tx_fifo_errors++;
ipg_reset(dev, IPG_AC_TX_RESET | IPG_AC_DMA |
IPG_AC_NETWORK | IPG_AC_FIFO);
/* Re-configure after DMA reset. */
if (ipg_io_config(dev) < 0) {
- printk(KERN_INFO
- "%s: Error during re-configuration.\n",
- dev->name);
+ netdev_info(dev, "Error during re-configuration\n");
}
init_tfdlist(dev);
@@ -1062,7 +1068,7 @@ static int ipg_nic_rxrestore(struct net_device *dev)
* Linux system).
*/
if (ipg_get_rxbuff(dev, entry) < 0) {
- IPG_DEBUG_MSG("Cannot allocate new Rx buffer.\n");
+ IPG_DEBUG_MSG("Cannot allocate new Rx buffer\n");
break;
}
@@ -1133,7 +1139,7 @@ static int ipg_nic_rx_check_error(struct net_device *dev)
(IPG_RFS_RXFIFOOVERRUN | IPG_RFS_RXRUNTFRAME |
IPG_RFS_RXALIGNMENTERROR | IPG_RFS_RXFCSERROR |
IPG_RFS_RXOVERSIZEDFRAME | IPG_RFS_RXLENGTHERROR))) {
- IPG_DEBUG_MSG("Rx error, RFS = %16.16lx\n",
+ IPG_DEBUG_MSG("Rx error, RFS = %016lx\n",
(unsigned long) rxfd->rfs);
/* Increment general receive error statistic. */
@@ -1141,13 +1147,13 @@ static int ipg_nic_rx_check_error(struct net_device *dev)
/* Increment detailed receive error statistics. */
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFIFOOVERRUN) {
- IPG_DEBUG_MSG("RX FIFO overrun occurred.\n");
+ IPG_DEBUG_MSG("RX FIFO overrun occurred\n");
sp->stats.rx_fifo_errors++;
}
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXRUNTFRAME) {
- IPG_DEBUG_MSG("RX runt occurred.\n");
+ IPG_DEBUG_MSG("RX runt occurred\n");
sp->stats.rx_length_errors++;
}
@@ -1156,7 +1162,7 @@ static int ipg_nic_rx_check_error(struct net_device *dev)
*/
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXALIGNMENTERROR) {
- IPG_DEBUG_MSG("RX alignment error occurred.\n");
+ IPG_DEBUG_MSG("RX alignment error occurred\n");
sp->stats.rx_frame_errors++;
}
@@ -1403,7 +1409,7 @@ static int ipg_nic_rx(struct net_device *dev)
*/
if (framelen > sp->rxfrag_size) {
IPG_DEBUG_MSG
- ("RFS FrameLen > allocated fragment size.\n");
+ ("RFS FrameLen > allocated fragment size\n");
framelen = sp->rxfrag_size;
}
@@ -1413,7 +1419,7 @@ static int ipg_nic_rx(struct net_device *dev)
IPG_RFS_RXALIGNMENTERROR | IPG_RFS_RXFCSERROR |
IPG_RFS_RXOVERSIZEDFRAME | IPG_RFS_RXLENGTHERROR)))) {
- IPG_DEBUG_MSG("Rx error, RFS = %16.16lx\n",
+ IPG_DEBUG_MSG("Rx error, RFS = %016lx\n",
(unsigned long int) rxfd->rfs);
/* Increment general receive error statistic. */
@@ -1421,12 +1427,12 @@ static int ipg_nic_rx(struct net_device *dev)
/* Increment detailed receive error statistics. */
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFIFOOVERRUN) {
- IPG_DEBUG_MSG("RX FIFO overrun occurred.\n");
+ IPG_DEBUG_MSG("RX FIFO overrun occurred\n");
sp->stats.rx_fifo_errors++;
}
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXRUNTFRAME) {
- IPG_DEBUG_MSG("RX runt occurred.\n");
+ IPG_DEBUG_MSG("RX runt occurred\n");
sp->stats.rx_length_errors++;
}
@@ -1436,7 +1442,7 @@ static int ipg_nic_rx(struct net_device *dev)
*/
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXALIGNMENTERROR) {
- IPG_DEBUG_MSG("RX alignment error occurred.\n");
+ IPG_DEBUG_MSG("RX alignment error occurred\n");
sp->stats.rx_frame_errors++;
}
@@ -1507,7 +1513,7 @@ static int ipg_nic_rx(struct net_device *dev)
rxfd = sp->rxd + entry;
- IPG_DEBUG_MSG("Frame requires multiple RFDs.\n");
+ IPG_DEBUG_MSG("Frame requires multiple RFDs\n");
/* An unexpected event, additional code needed to handle
* properly. So for the time being, just disregard the
@@ -1556,8 +1562,7 @@ static void ipg_reset_after_host_error(struct work_struct *work)
init_tfdlist(dev);
if (ipg_io_config(dev) < 0) {
- printk(KERN_INFO "%s: Cannot recover from PCI error.\n",
- dev->name);
+ netdev_info(dev, "Cannot recover from PCI error\n");
schedule_delayed_work(&sp->task, HZ);
}
}
@@ -1585,7 +1590,7 @@ static irqreturn_t ipg_interrupt_handler(int irq, void *dev_inst)
*/
status = ipg_r16(INT_STATUS_ACK);
- IPG_DEBUG_MSG("IntStatusAck = %4.4x\n", status);
+ IPG_DEBUG_MSG("IntStatusAck = %04x\n", status);
/* Shared IRQ of remove event. */
if (!(status & IPG_IS_RSVD_MASK))
@@ -1598,7 +1603,7 @@ static irqreturn_t ipg_interrupt_handler(int irq, void *dev_inst)
/* If RFDListEnd interrupt, restore all used RFDs. */
if (status & IPG_IS_RFD_LIST_END) {
- IPG_DEBUG_MSG("RFDListEnd Interrupt.\n");
+ IPG_DEBUG_MSG("RFDListEnd Interrupt\n");
/* The RFD list end indicates an RFD was encountered
* with a 0 NextPtr, or with an RFDDone bit set to 1
@@ -1660,21 +1665,20 @@ static irqreturn_t ipg_interrupt_handler(int irq, void *dev_inst)
/* If LinkEvent interrupt, resolve autonegotiation. */
if (status & IPG_IS_LINK_EVENT) {
if (ipg_config_autoneg(dev) < 0)
- printk(KERN_INFO "%s: Auto-negotiation error.\n",
- dev->name);
+ netdev_info(dev, "Auto-negotiation error\n");
}
/* If MACCtrlFrame interrupt, do nothing. */
if (status & IPG_IS_MAC_CTRL_FRAME)
- IPG_DEBUG_MSG("MACCtrlFrame interrupt.\n");
+ IPG_DEBUG_MSG("MACCtrlFrame interrupt\n");
/* If RxComplete interrupt, do nothing. */
if (status & IPG_IS_RX_COMPLETE)
- IPG_DEBUG_MSG("RxComplete interrupt.\n");
+ IPG_DEBUG_MSG("RxComplete interrupt\n");
/* If RxEarly interrupt, do nothing. */
if (status & IPG_IS_RX_EARLY)
- IPG_DEBUG_MSG("RxEarly interrupt.\n");
+ IPG_DEBUG_MSG("RxEarly interrupt\n");
out_enable:
/* Re-enable IPG interrupts. */
@@ -1748,8 +1752,7 @@ static int ipg_nic_open(struct net_device *dev)
rc = request_irq(pdev->irq, ipg_interrupt_handler, IRQF_SHARED,
dev->name, dev);
if (rc < 0) {
- printk(KERN_INFO "%s: Error when requesting interrupt.\n",
- dev->name);
+ netdev_info(dev, "Error when requesting interrupt\n");
goto out;
}
@@ -1769,8 +1772,7 @@ static int ipg_nic_open(struct net_device *dev)
rc = init_rfdlist(dev);
if (rc < 0) {
- printk(KERN_INFO "%s: Error during configuration.\n",
- dev->name);
+ netdev_info(dev, "Error during configuration\n");
goto err_free_tx_2;
}
@@ -1778,14 +1780,13 @@ static int ipg_nic_open(struct net_device *dev)
rc = ipg_io_config(dev);
if (rc < 0) {
- printk(KERN_INFO "%s: Error during configuration.\n",
- dev->name);
+ netdev_info(dev, "Error during configuration\n");
goto err_release_tfdlist_3;
}
/* Resolve autonegotiation. */
if (ipg_config_autoneg(dev) < 0)
- printk(KERN_INFO "%s: Auto-negotiation error.\n", dev->name);
+ netdev_info(dev, "Auto-negotiation error\n");
/* initialize JUMBO Frame control variable */
sp->jumbo.found_start = 0;
@@ -1960,7 +1961,7 @@ static void ipg_set_phy_default_param(unsigned char rev,
{
unsigned short length;
unsigned char revision;
- unsigned short *phy_param;
+ const unsigned short *phy_param;
unsigned short address, value;
phy_param = &DefaultPhyParam[0];
@@ -2200,7 +2201,7 @@ static const struct net_device_ops ipg_netdev_ops = {
.ndo_stop = ipg_nic_stop,
.ndo_start_xmit = ipg_nic_hard_start_xmit,
.ndo_get_stats = ipg_nic_get_stats,
- .ndo_set_multicast_list = ipg_nic_set_multicast_list,
+ .ndo_set_rx_mode = ipg_nic_set_multicast_list,
.ndo_do_ioctl = ipg_ioctl,
.ndo_tx_timeout = ipg_tx_timeout,
.ndo_change_mtu = ipg_nic_change_mtu,
@@ -2221,7 +2222,7 @@ static int __devinit ipg_probe(struct pci_dev *pdev,
if (rc < 0)
goto out;
- printk(KERN_INFO "%s: %s\n", pci_name(pdev), ipg_brand_name[i]);
+ pr_info("%s: %s\n", pci_name(pdev), ipg_brand_name[i]);
pci_set_master(pdev);
@@ -2229,8 +2230,7 @@ static int __devinit ipg_probe(struct pci_dev *pdev,
if (rc < 0) {
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc < 0) {
- printk(KERN_ERR "%s: DMA config failed.\n",
- pci_name(pdev));
+ pr_err("%s: DMA config failed\n", pci_name(pdev));
goto err_disable_0;
}
}
@@ -2240,7 +2240,7 @@ static int __devinit ipg_probe(struct pci_dev *pdev,
*/
dev = alloc_etherdev(sizeof(struct ipg_nic_private));
if (!dev) {
- printk(KERN_ERR "%s: alloc_etherdev failed\n", pci_name(pdev));
+ pr_err("%s: alloc_etherdev failed\n", pci_name(pdev));
rc = -ENOMEM;
goto err_disable_0;
}
@@ -2266,7 +2266,7 @@ static int __devinit ipg_probe(struct pci_dev *pdev,
ioaddr = pci_iomap(pdev, 1, pci_resource_len(pdev, 1));
if (!ioaddr) {
- printk(KERN_ERR "%s cannot map MMIO\n", pci_name(pdev));
+ pr_err("%s: cannot map MMIO\n", pci_name(pdev));
rc = -EIO;
goto err_release_regions_2;
}
@@ -2288,7 +2288,7 @@ static int __devinit ipg_probe(struct pci_dev *pdev,
if (rc < 0)
goto err_unmap_3;
- printk(KERN_INFO "Ethernet device registered as: %s\n", dev->name);
+ netdev_info(dev, "Ethernet device registered\n");
out:
return rc;
diff --git a/drivers/net/ipg.h b/drivers/net/ethernet/icplus/ipg.h
index 6ce027355fcf..6ce027355fcf 100644
--- a/drivers/net/ipg.h
+++ b/drivers/net/ethernet/icplus/ipg.h
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
new file mode 100644
index 000000000000..76213162fbe3
--- /dev/null
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -0,0 +1,226 @@
+#
+# Intel network device configuration
+#
+
+config NET_VENDOR_INTEL
+ bool "Intel devices"
+ default y
+ depends on PCI || PCI_MSI || ISA || ISA_DMA_API || ARM || \
+ ARCH_ACORN || MCA || MCA_LEGACY || SNI_RM || SUN3 || \
+ GSC || BVME6000 || MVME16x || ARCH_ENP2611 || \
+ (ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR) || \
+ EXPERIMENTAL
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Intel cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_INTEL
+
+config E100
+ tristate "Intel(R) PRO/100+ support"
+ depends on PCI
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports Intel(R) PRO/100 family of adapters.
+ To verify that your adapter is supported, find the board ID number
+ on the adapter. Look for a label that has a barcode and a number
+ in the format 123456-001 (six digits hyphen three digits).
+
+ Use the above information and the Adapter & Driver ID Guide at:
+
+ <http://support.intel.com/support/network/adapter/pro100/21397.htm>
+
+ to identify the adapter.
+
+ For the latest Intel PRO/100 network driver for Linux, see:
+
+ <http://www.intel.com/p/en_US/support/highlights/network/pro100plus>
+
+ More specific information on configuring the driver is in
+ <file:Documentation/networking/e100.txt>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called e100.
+
+config E1000
+ tristate "Intel(R) PRO/1000 Gigabit Ethernet support"
+ depends on PCI
+ ---help---
+ This driver supports Intel(R) PRO/1000 gigabit ethernet family of
+ adapters. For more information on how to identify your adapter, go
+ to the Adapter & Driver ID Guide at:
+
+ <http://support.intel.com/support/network/adapter/pro100/21397.htm>
+
+ For general information and support, go to the Intel support
+ website at:
+
+ <http://support.intel.com>
+
+ More specific information on configuring the driver is in
+ <file:Documentation/networking/e1000.txt>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called e1000.
+
+config E1000E
+ tristate "Intel(R) PRO/1000 PCI-Express Gigabit Ethernet support"
+ depends on PCI && (!SPARC32 || BROKEN)
+ select CRC32
+ ---help---
+ This driver supports the PCI-Express Intel(R) PRO/1000 gigabit
+ ethernet family of adapters. For PCI or PCI-X e1000 adapters,
+ use the regular e1000 driver For more information on how to
+ identify your adapter, go to the Adapter & Driver ID Guide at:
+
+ <http://support.intel.com/support/network/adapter/pro100/21397.htm>
+
+ For general information and support, go to the Intel support
+ website at:
+
+ <http://support.intel.com>
+
+ To compile this driver as a module, choose M here. The module
+ will be called e1000e.
+
+config IGB
+ tristate "Intel(R) 82575/82576 PCI-Express Gigabit Ethernet support"
+ depends on PCI
+ ---help---
+ This driver supports Intel(R) 82575/82576 gigabit ethernet family of
+ adapters. For more information on how to identify your adapter, go
+ to the Adapter & Driver ID Guide at:
+
+ <http://support.intel.com/support/network/adapter/pro100/21397.htm>
+
+ For general information and support, go to the Intel support
+ website at:
+
+ <http://support.intel.com>
+
+ More specific information on configuring the driver is in
+ <file:Documentation/networking/e1000.txt>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called igb.
+
+config IGB_DCA
+ bool "Direct Cache Access (DCA) Support"
+ default y
+ depends on IGB && DCA && !(IGB=y && DCA=m)
+ ---help---
+ Say Y here if you want to use Direct Cache Access (DCA) in the
+ driver. DCA is a method for warming the CPU cache before data
+ is used, with the intent of lessening the impact of cache misses.
+
+config IGBVF
+ tristate "Intel(R) 82576 Virtual Function Ethernet support"
+ depends on PCI
+ ---help---
+ This driver supports Intel(R) 82576 virtual functions. For more
+ information on how to identify your adapter, go to the Adapter &
+ Driver ID Guide at:
+
+ <http://support.intel.com/support/network/adapter/pro100/21397.htm>
+
+ For general information and support, go to the Intel support
+ website at:
+
+ <http://support.intel.com>
+
+ More specific information on configuring the driver is in
+ <file:Documentation/networking/e1000.txt>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called igbvf.
+
+config IXGB
+ tristate "Intel(R) PRO/10GbE support"
+ depends on PCI
+ ---help---
+ This driver supports Intel(R) PRO/10GbE family of adapters for
+ PCI-X type cards. For PCI-E type cards, use the "ixgbe" driver
+ instead. For more information on how to identify your adapter, go
+ to the Adapter & Driver ID Guide at:
+
+ <http://support.intel.com/support/network/adapter/pro100/21397.htm>
+
+ For general information and support, go to the Intel support
+ website at:
+
+ <http://support.intel.com>
+
+ More specific information on configuring the driver is in
+ <file:Documentation/networking/ixgb.txt>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ixgb.
+
+config IXGBE
+ tristate "Intel(R) 10GbE PCI Express adapters support"
+ depends on PCI && INET
+ select MDIO
+ ---help---
+ This driver supports Intel(R) 10GbE PCI Express family of
+ adapters. For more information on how to identify your adapter, go
+ to the Adapter & Driver ID Guide at:
+
+ <http://support.intel.com/support/network/adapter/pro100/21397.htm>
+
+ For general information and support, go to the Intel support
+ website at:
+
+ <http://support.intel.com>
+
+ To compile this driver as a module, choose M here. The module
+ will be called ixgbe.
+
+config IXGBE_DCA
+ bool "Direct Cache Access (DCA) Support"
+ default y
+ depends on IXGBE && DCA && !(IXGBE=y && DCA=m)
+ ---help---
+ Say Y here if you want to use Direct Cache Access (DCA) in the
+ driver. DCA is a method for warming the CPU cache before data
+ is used, with the intent of lessening the impact of cache misses.
+
+config IXGBE_DCB
+ bool "Data Center Bridging (DCB) Support"
+ default n
+ depends on IXGBE && DCB
+ ---help---
+ Say Y here if you want to use Data Center Bridging (DCB) in the
+ driver.
+
+ If unsure, say N.
+
+config IXGBEVF
+ tristate "Intel(R) 82599 Virtual Function Ethernet support"
+ depends on PCI_MSI
+ ---help---
+ This driver supports Intel(R) 82599 virtual functions. For more
+ information on how to identify your adapter, go to the Adapter &
+ Driver ID Guide at:
+
+ <http://support.intel.com/support/network/sb/CS-008441.htm>
+
+ For general information and support, go to the Intel support
+ website at:
+
+ <http://support.intel.com>
+
+ More specific information on configuring the driver is in
+ <file:Documentation/networking/ixgbevf.txt>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ixgbevf. MSI-X interrupt support is required
+ for this driver to work correctly.
+
+endif # NET_VENDOR_INTEL
diff --git a/drivers/net/ethernet/intel/Makefile b/drivers/net/ethernet/intel/Makefile
new file mode 100644
index 000000000000..c8210e688669
--- /dev/null
+++ b/drivers/net/ethernet/intel/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the Intel network device drivers.
+#
+
+obj-$(CONFIG_E100) += e100.o
+obj-$(CONFIG_E1000) += e1000/
+obj-$(CONFIG_E1000E) += e1000e/
+obj-$(CONFIG_IGB) += igb/
+obj-$(CONFIG_IGBVF) += igbvf/
+obj-$(CONFIG_IXGBE) += ixgbe/
+obj-$(CONFIG_IXGBEVF) += ixgbevf/
+obj-$(CONFIG_IXGB) += ixgb/
diff --git a/drivers/net/e100.c b/drivers/net/ethernet/intel/e100.c
index e336c7937f05..5a2fdf7a00c8 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/ethernet/intel/e100.c
@@ -149,6 +149,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/hardirq.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -2500,12 +2502,8 @@ static void e100_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = rfds->max;
ring->tx_max_pending = cbs->max;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = rfds->count;
ring->tx_pending = cbs->count;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
static int e100_set_ringparam(struct net_device *netdev,
@@ -2736,7 +2734,7 @@ static const struct net_device_ops e100_netdev_ops = {
.ndo_stop = e100_close,
.ndo_start_xmit = e100_xmit_frame,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = e100_set_multicast_list,
+ .ndo_set_rx_mode = e100_set_multicast_list,
.ndo_set_mac_address = e100_set_mac_address,
.ndo_change_mtu = e100_change_mtu,
.ndo_do_ioctl = e100_do_ioctl,
@@ -2812,6 +2810,10 @@ static int __devinit e100_probe(struct pci_dev *pdev,
e100_get_defaults(nic);
+ /* D100 MAC doesn't allow rx of vlan packets with normal MTU */
+ if (nic->mac < mac_82558_D101_A4)
+ netdev->features |= NETIF_F_VLAN_CHALLENGED;
+
/* locks must be initialized before calling hw_reset */
spin_lock_init(&nic->cb_lock);
spin_lock_init(&nic->cmd_lock);
diff --git a/drivers/net/e1000/Makefile b/drivers/net/ethernet/intel/e1000/Makefile
index 4a6ab1522451..4a6ab1522451 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/ethernet/intel/e1000/Makefile
diff --git a/drivers/net/e1000/e1000.h b/drivers/net/ethernet/intel/e1000/e1000.h
index 8676899120c3..1e1596990b5c 100644
--- a/drivers/net/e1000/e1000.h
+++ b/drivers/net/ethernet/intel/e1000/e1000.h
@@ -150,6 +150,8 @@ struct e1000_buffer {
unsigned long time_stamp;
u16 length;
u16 next_to_watch;
+ unsigned int segs;
+ unsigned int bytecount;
u16 mapped_as_page;
};
@@ -212,10 +214,7 @@ struct e1000_rx_ring {
/* board specific private data structure */
struct e1000_adapter {
- struct timer_list tx_fifo_stall_timer;
- struct timer_list watchdog_timer;
- struct timer_list phy_info_timer;
- struct vlan_group *vlgrp;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
u16 mng_vlan_id;
u32 bd_number;
u32 rx_buffer_len;
@@ -235,7 +234,6 @@ struct e1000_adapter {
u16 tx_itr;
u16 rx_itr;
- struct work_struct reset_task;
u8 fc_autoneg;
/* TX */
@@ -308,8 +306,12 @@ struct e1000_adapter {
bool discarding;
- struct work_struct fifo_stall_task;
- struct work_struct phy_info_task;
+ struct work_struct reset_task;
+ struct delayed_work watchdog_task;
+ struct delayed_work fifo_stall_task;
+ struct delayed_work phy_info_task;
+
+ struct mutex mutex;
};
enum e1000_state_t {
diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
index ec0fa426cce2..2b223ac99c42 100644
--- a/drivers/net/e1000/e1000_ethtool.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
@@ -290,69 +290,6 @@ static int e1000_set_pauseparam(struct net_device *netdev,
return retval;
}
-static u32 e1000_get_rx_csum(struct net_device *netdev)
-{
- struct e1000_adapter *adapter = netdev_priv(netdev);
- return adapter->rx_csum;
-}
-
-static int e1000_set_rx_csum(struct net_device *netdev, u32 data)
-{
- struct e1000_adapter *adapter = netdev_priv(netdev);
- adapter->rx_csum = data;
-
- if (netif_running(netdev))
- e1000_reinit_locked(adapter);
- else
- e1000_reset(adapter);
- return 0;
-}
-
-static u32 e1000_get_tx_csum(struct net_device *netdev)
-{
- return (netdev->features & NETIF_F_HW_CSUM) != 0;
-}
-
-static int e1000_set_tx_csum(struct net_device *netdev, u32 data)
-{
- struct e1000_adapter *adapter = netdev_priv(netdev);
- struct e1000_hw *hw = &adapter->hw;
-
- if (hw->mac_type < e1000_82543) {
- if (!data)
- return -EINVAL;
- return 0;
- }
-
- if (data)
- netdev->features |= NETIF_F_HW_CSUM;
- else
- netdev->features &= ~NETIF_F_HW_CSUM;
-
- return 0;
-}
-
-static int e1000_set_tso(struct net_device *netdev, u32 data)
-{
- struct e1000_adapter *adapter = netdev_priv(netdev);
- struct e1000_hw *hw = &adapter->hw;
-
- if ((hw->mac_type < e1000_82544) ||
- (hw->mac_type == e1000_82547))
- return data ? -EINVAL : 0;
-
- if (data)
- netdev->features |= NETIF_F_TSO;
- else
- netdev->features &= ~NETIF_F_TSO;
-
- netdev->features &= ~NETIF_F_TSO6;
-
- e_info(probe, "TSO is %s\n", data ? "Enabled" : "Disabled");
- adapter->tso_force = true;
- return 0;
-}
-
static u32 e1000_get_msglevel(struct net_device *netdev)
{
struct e1000_adapter *adapter = netdev_priv(netdev);
@@ -603,12 +540,8 @@ static void e1000_get_ringparam(struct net_device *netdev,
E1000_MAX_82544_RXD;
ring->tx_max_pending = (mac_type < e1000_82544) ? E1000_MAX_TXD :
E1000_MAX_82544_TXD;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = rxdr->count;
ring->tx_pending = txdr->count;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
static int e1000_set_ringparam(struct net_device *netdev,
@@ -901,6 +834,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
/* Disable all the interrupts */
ew32(IMC, 0xFFFFFFFF);
+ E1000_WRITE_FLUSH();
msleep(10);
/* Test each interrupt */
@@ -919,6 +853,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
adapter->test_icr = 0;
ew32(IMC, mask);
ew32(ICS, mask);
+ E1000_WRITE_FLUSH();
msleep(10);
if (adapter->test_icr & mask) {
@@ -936,6 +871,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
adapter->test_icr = 0;
ew32(IMS, mask);
ew32(ICS, mask);
+ E1000_WRITE_FLUSH();
msleep(10);
if (!(adapter->test_icr & mask)) {
@@ -953,6 +889,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
adapter->test_icr = 0;
ew32(IMC, ~mask & 0x00007FFF);
ew32(ICS, ~mask & 0x00007FFF);
+ E1000_WRITE_FLUSH();
msleep(10);
if (adapter->test_icr) {
@@ -964,6 +901,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
/* Disable all the interrupts */
ew32(IMC, 0xFFFFFFFF);
+ E1000_WRITE_FLUSH();
msleep(10);
/* Unhook test interrupt handler */
@@ -1457,6 +1395,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
if (unlikely(++k == txdr->count)) k = 0;
}
ew32(TDT, k);
+ E1000_WRITE_FLUSH();
msleep(200);
time = jiffies; /* set the start time for the receive */
good_cnt = 0;
@@ -1905,12 +1844,6 @@ static const struct ethtool_ops e1000_ethtool_ops = {
.set_ringparam = e1000_set_ringparam,
.get_pauseparam = e1000_get_pauseparam,
.set_pauseparam = e1000_set_pauseparam,
- .get_rx_csum = e1000_get_rx_csum,
- .set_rx_csum = e1000_set_rx_csum,
- .get_tx_csum = e1000_get_tx_csum,
- .set_tx_csum = e1000_set_tx_csum,
- .set_sg = ethtool_op_set_sg,
- .set_tso = e1000_set_tso,
.self_test = e1000_diag_test,
.get_strings = e1000_get_strings,
.set_phys_id = e1000_set_phys_id,
diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c
index 7501d977d992..36ee76bf4cba 100644
--- a/drivers/net/e1000/e1000_hw.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c
@@ -446,6 +446,7 @@ s32 e1000_reset_hw(struct e1000_hw *hw)
/* Must reset the PHY before resetting the MAC */
if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
ew32(CTRL, (ctrl | E1000_CTRL_PHY_RST));
+ E1000_WRITE_FLUSH();
msleep(5);
}
@@ -3080,7 +3081,6 @@ s32 e1000_phy_hw_reset(struct e1000_hw *hw)
{
u32 ctrl, ctrl_ext;
u32 led_ctrl;
- s32 ret_val;
e_dbg("e1000_phy_hw_reset");
@@ -3126,11 +3126,7 @@ s32 e1000_phy_hw_reset(struct e1000_hw *hw)
}
/* Wait for FW to finish PHY configuration. */
- ret_val = e1000_get_phy_cfg_done(hw);
- if (ret_val != E1000_SUCCESS)
- return ret_val;
-
- return ret_val;
+ return e1000_get_phy_cfg_done(hw);
}
/**
@@ -3757,6 +3753,7 @@ static s32 e1000_acquire_eeprom(struct e1000_hw *hw)
/* Clear SK and CS */
eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
ew32(EECD, eecd);
+ E1000_WRITE_FLUSH();
udelay(1);
}
@@ -3829,6 +3826,7 @@ static void e1000_release_eeprom(struct e1000_hw *hw)
eecd &= ~E1000_EECD_SK; /* Lower SCK */
ew32(EECD, eecd);
+ E1000_WRITE_FLUSH();
udelay(hw->eeprom.delay_usec);
} else if (hw->eeprom.type == e1000_eeprom_microwire) {
@@ -4028,6 +4026,12 @@ s32 e1000_validate_eeprom_checksum(struct e1000_hw *hw)
checksum += eeprom_data;
}
+#ifdef CONFIG_PARISC
+ /* This is a signature and not a checksum on HP c8000 */
+ if ((hw->subsystem_vendor_id == 0x103C) && (eeprom_data == 0x16d6))
+ return E1000_SUCCESS;
+
+#endif
if (checksum == (u16) EEPROM_SUM)
return E1000_SUCCESS;
else {
@@ -5381,7 +5385,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
if (ret_val)
return ret_val;
- mdelay(20);
+ msleep(20);
ret_val = e1000_write_phy_reg(hw, 0x0000,
IGP01E1000_IEEE_FORCE_GIGA);
@@ -5409,7 +5413,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
if (ret_val)
return ret_val;
- mdelay(20);
+ msleep(20);
/* Now enable the transmitter */
ret_val =
@@ -5436,7 +5440,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
if (ret_val)
return ret_val;
- mdelay(20);
+ msleep(20);
ret_val = e1000_write_phy_reg(hw, 0x0000,
IGP01E1000_IEEE_FORCE_GIGA);
@@ -5453,7 +5457,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
if (ret_val)
return ret_val;
- mdelay(20);
+ msleep(20);
/* Now enable the transmitter */
ret_val =
@@ -5746,26 +5750,26 @@ static s32 e1000_polarity_reversal_workaround(struct e1000_hw *hw)
if ((mii_status_reg & ~MII_SR_LINK_STATUS) == 0)
break;
- mdelay(100);
+ msleep(100);
}
/* Recommended delay time after link has been lost */
- mdelay(1000);
+ msleep(1000);
/* Now we will re-enable th transmitter on the PHY */
ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019);
if (ret_val)
return ret_val;
- mdelay(50);
+ msleep(50);
ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFF0);
if (ret_val)
return ret_val;
- mdelay(50);
+ msleep(50);
ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFF00);
if (ret_val)
return ret_val;
- mdelay(50);
+ msleep(50);
ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x0000);
if (ret_val)
return ret_val;
@@ -5790,7 +5794,7 @@ static s32 e1000_polarity_reversal_workaround(struct e1000_hw *hw)
if (mii_status_reg & MII_SR_LINK_STATUS)
break;
- mdelay(100);
+ msleep(100);
}
return E1000_SUCCESS;
}
@@ -5821,6 +5825,6 @@ static s32 e1000_get_auto_rd_done(struct e1000_hw *hw)
static s32 e1000_get_phy_cfg_done(struct e1000_hw *hw)
{
e_dbg("e1000_get_phy_cfg_done");
- mdelay(10);
+ msleep(10);
return E1000_SUCCESS;
}
diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/ethernet/intel/e1000/e1000_hw.h
index 5c9a8403668b..5c9a8403668b 100644
--- a/drivers/net/e1000/e1000_hw.h
+++ b/drivers/net/ethernet/intel/e1000/e1000_hw.h
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c
index 76e8af00d86d..cf480b554622 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_main.c
@@ -30,6 +30,8 @@
#include <net/ip6_checksum.h>
#include <linux/io.h>
#include <linux/prefetch.h>
+#include <linux/bitops.h>
+#include <linux/if_vlan.h>
/* Intel Media SOC GbE MDIO physical base address */
static unsigned long ce4100_gbe_mdio_base_phy;
@@ -129,10 +131,8 @@ static void e1000_clean_tx_ring(struct e1000_adapter *adapter,
static void e1000_clean_rx_ring(struct e1000_adapter *adapter,
struct e1000_rx_ring *rx_ring);
static void e1000_set_rx_mode(struct net_device *netdev);
-static void e1000_update_phy_info(unsigned long data);
static void e1000_update_phy_info_task(struct work_struct *work);
-static void e1000_watchdog(unsigned long data);
-static void e1000_82547_tx_fifo_stall(unsigned long data);
+static void e1000_watchdog(struct work_struct *work);
static void e1000_82547_tx_fifo_stall_task(struct work_struct *work);
static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
struct net_device *netdev);
@@ -166,7 +166,8 @@ static void e1000_smartspeed(struct e1000_adapter *adapter);
static int e1000_82547_fifo_workaround(struct e1000_adapter *adapter,
struct sk_buff *skb);
-static void e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp);
+static bool e1000_vlan_used(struct e1000_adapter *adapter);
+static void e1000_vlan_mode(struct net_device *netdev, u32 features);
static void e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid);
static void e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid);
static void e1000_restore_vlan(struct e1000_adapter *adapter);
@@ -330,21 +331,24 @@ static void e1000_update_mng_vlan(struct e1000_adapter *adapter)
struct net_device *netdev = adapter->netdev;
u16 vid = hw->mng_cookie.vlan_id;
u16 old_vid = adapter->mng_vlan_id;
- if (adapter->vlgrp) {
- if (!vlan_group_get_device(adapter->vlgrp, vid)) {
- if (hw->mng_cookie.status &
- E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) {
- e1000_vlan_rx_add_vid(netdev, vid);
- adapter->mng_vlan_id = vid;
- } else
- adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
- if ((old_vid != (u16)E1000_MNG_VLAN_NONE) &&
- (vid != old_vid) &&
- !vlan_group_get_device(adapter->vlgrp, old_vid))
- e1000_vlan_rx_kill_vid(netdev, old_vid);
- } else
+ if (!e1000_vlan_used(adapter))
+ return;
+
+ if (!test_bit(vid, adapter->active_vlans)) {
+ if (hw->mng_cookie.status &
+ E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) {
+ e1000_vlan_rx_add_vid(netdev, vid);
adapter->mng_vlan_id = vid;
+ } else {
+ adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
+ }
+ if ((old_vid != (u16)E1000_MNG_VLAN_NONE) &&
+ (vid != old_vid) &&
+ !test_bit(old_vid, adapter->active_vlans))
+ e1000_vlan_rx_kill_vid(netdev, old_vid);
+ } else {
+ adapter->mng_vlan_id = vid;
}
}
@@ -481,12 +485,21 @@ static void e1000_power_down_phy(struct e1000_adapter *adapter)
e1000_read_phy_reg(hw, PHY_CTRL, &mii_reg);
mii_reg |= MII_CR_POWER_DOWN;
e1000_write_phy_reg(hw, PHY_CTRL, mii_reg);
- mdelay(1);
+ msleep(1);
}
out:
return;
}
+static void e1000_down_and_stop(struct e1000_adapter *adapter)
+{
+ set_bit(__E1000_DOWN, &adapter->flags);
+ cancel_work_sync(&adapter->reset_task);
+ cancel_delayed_work_sync(&adapter->watchdog_task);
+ cancel_delayed_work_sync(&adapter->phy_info_task);
+ cancel_delayed_work_sync(&adapter->fifo_stall_task);
+}
+
void e1000_down(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
@@ -516,13 +529,9 @@ void e1000_down(struct e1000_adapter *adapter)
/*
* Setting DOWN must be after irq_disable to prevent
* a screaming interrupt. Setting DOWN also prevents
- * timers and tasks from rescheduling.
+ * tasks from rescheduling.
*/
- set_bit(__E1000_DOWN, &adapter->flags);
-
- del_timer_sync(&adapter->tx_fifo_stall_timer);
- del_timer_sync(&adapter->watchdog_timer);
- del_timer_sync(&adapter->phy_info_timer);
+ e1000_down_and_stop(adapter);
adapter->link_speed = 0;
adapter->link_duplex = 0;
@@ -537,10 +546,10 @@ static void e1000_reinit_safe(struct e1000_adapter *adapter)
{
while (test_and_set_bit(__E1000_RESETTING, &adapter->flags))
msleep(1);
- rtnl_lock();
+ mutex_lock(&adapter->mutex);
e1000_down(adapter);
e1000_up(adapter);
- rtnl_unlock();
+ mutex_unlock(&adapter->mutex);
clear_bit(__E1000_RESETTING, &adapter->flags);
}
@@ -797,6 +806,41 @@ static int e1000_is_need_ioport(struct pci_dev *pdev)
}
}
+static u32 e1000_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int e1000_set_features(struct net_device *netdev, u32 features)
+{
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+ u32 changed = features ^ netdev->features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ e1000_vlan_mode(netdev, features);
+
+ if (!(changed & NETIF_F_RXCSUM))
+ return 0;
+
+ adapter->rx_csum = !!(features & NETIF_F_RXCSUM);
+
+ if (netif_running(netdev))
+ e1000_reinit_locked(adapter);
+ else
+ e1000_reset(adapter);
+
+ return 0;
+}
+
static const struct net_device_ops e1000_netdev_ops = {
.ndo_open = e1000_open,
.ndo_stop = e1000_close,
@@ -804,17 +848,17 @@ static const struct net_device_ops e1000_netdev_ops = {
.ndo_get_stats = e1000_get_stats,
.ndo_set_rx_mode = e1000_set_rx_mode,
.ndo_set_mac_address = e1000_set_mac,
- .ndo_tx_timeout = e1000_tx_timeout,
+ .ndo_tx_timeout = e1000_tx_timeout,
.ndo_change_mtu = e1000_change_mtu,
.ndo_do_ioctl = e1000_ioctl,
.ndo_validate_addr = eth_validate_addr,
-
- .ndo_vlan_rx_register = e1000_vlan_rx_register,
.ndo_vlan_rx_add_vid = e1000_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = e1000_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = e1000_netpoll,
#endif
+ .ndo_fix_features = e1000_fix_features,
+ .ndo_set_features = e1000_set_features,
};
/**
@@ -1016,16 +1060,19 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
}
if (hw->mac_type >= e1000_82543) {
- netdev->features = NETIF_F_SG |
+ netdev->hw_features = NETIF_F_SG |
NETIF_F_HW_CSUM |
- NETIF_F_HW_VLAN_TX |
- NETIF_F_HW_VLAN_RX |
+ NETIF_F_HW_VLAN_RX;
+ netdev->features = NETIF_F_HW_VLAN_TX |
NETIF_F_HW_VLAN_FILTER;
}
if ((hw->mac_type >= e1000_82544) &&
(hw->mac_type != e1000_82547))
- netdev->features |= NETIF_F_TSO;
+ netdev->hw_features |= NETIF_F_TSO;
+
+ netdev->features |= netdev->hw_features;
+ netdev->hw_features |= NETIF_F_RXCSUM;
if (pci_using_dac) {
netdev->features |= NETIF_F_HIGHDMA;
@@ -1036,6 +1083,8 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
netdev->vlan_features |= NETIF_F_HW_CSUM;
netdev->vlan_features |= NETIF_F_SG;
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
adapter->en_mng_pt = e1000_enable_mng_pass_thru(hw);
/* initialize eeprom parameters */
@@ -1074,21 +1123,12 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
if (!is_valid_ether_addr(netdev->perm_addr))
e_err(probe, "Invalid MAC Address\n");
- init_timer(&adapter->tx_fifo_stall_timer);
- adapter->tx_fifo_stall_timer.function = e1000_82547_tx_fifo_stall;
- adapter->tx_fifo_stall_timer.data = (unsigned long)adapter;
-
- init_timer(&adapter->watchdog_timer);
- adapter->watchdog_timer.function = e1000_watchdog;
- adapter->watchdog_timer.data = (unsigned long) adapter;
- init_timer(&adapter->phy_info_timer);
- adapter->phy_info_timer.function = e1000_update_phy_info;
- adapter->phy_info_timer.data = (unsigned long)adapter;
-
- INIT_WORK(&adapter->fifo_stall_task, e1000_82547_tx_fifo_stall_task);
+ INIT_DELAYED_WORK(&adapter->watchdog_task, e1000_watchdog);
+ INIT_DELAYED_WORK(&adapter->fifo_stall_task,
+ e1000_82547_tx_fifo_stall_task);
+ INIT_DELAYED_WORK(&adapter->phy_info_task, e1000_update_phy_info_task);
INIT_WORK(&adapter->reset_task, e1000_reset_task);
- INIT_WORK(&adapter->phy_info_task, e1000_update_phy_info_task);
e1000_check_options(adapter);
@@ -1175,6 +1215,8 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
if (err)
goto err_register;
+ e1000_vlan_mode(netdev, netdev->features);
+
/* print bus type/speed/width info */
e_info(probe, "(PCI%s:%dMHz:%d-bit) %pM\n",
((hw->bus_type == e1000_bus_type_pcix) ? "-X" : ""),
@@ -1231,13 +1273,7 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
- set_bit(__E1000_DOWN, &adapter->flags);
- del_timer_sync(&adapter->tx_fifo_stall_timer);
- del_timer_sync(&adapter->watchdog_timer);
- del_timer_sync(&adapter->phy_info_timer);
-
- cancel_work_sync(&adapter->reset_task);
-
+ e1000_down_and_stop(adapter);
e1000_release_manageability(adapter);
unregister_netdev(netdev);
@@ -1281,6 +1317,7 @@ static int __devinit e1000_sw_init(struct e1000_adapter *adapter)
e1000_irq_disable(adapter);
spin_lock_init(&adapter->stats_lock);
+ mutex_init(&adapter->mutex);
set_bit(__E1000_DOWN, &adapter->flags);
@@ -1321,7 +1358,7 @@ static int __devinit e1000_alloc_queues(struct e1000_adapter *adapter)
* 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,
+ * handler is registered with the OS, the watchdog task is started,
* and the stack is notified that the interface is ready.
**/
@@ -1419,8 +1456,7 @@ static int e1000_close(struct net_device *netdev)
* the same ID is registered on the host OS (let 8021q kill it) */
if ((hw->mng_cookie.status &
E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) &&
- !(adapter->vlgrp &&
- vlan_group_get_device(adapter->vlgrp, adapter->mng_vlan_id))) {
+ !test_bit(adapter->mng_vlan_id, adapter->active_vlans)) {
e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id);
}
@@ -1767,8 +1803,8 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
rctl &= ~(3 << E1000_RCTL_MO_SHIFT);
- rctl |= E1000_RCTL_EN | E1000_RCTL_BAM |
- E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
+ rctl |= E1000_RCTL_BAM | E1000_RCTL_LBM_NO |
+ E1000_RCTL_RDMTS_HALF |
(hw->mc_filter_type << E1000_RCTL_MO_SHIFT);
if (hw->tbi_compatibility_on == 1)
@@ -1870,7 +1906,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
}
/* Enable Receives */
- ew32(RCTL, rctl);
+ ew32(RCTL, rctl | E1000_RCTL_EN);
}
/**
@@ -2211,7 +2247,7 @@ static void e1000_set_rx_mode(struct net_device *netdev)
else
rctl &= ~E1000_RCTL_MPE;
/* Enable VLAN filter if there is a VLAN */
- if (adapter->vlgrp)
+ if (e1000_vlan_used(adapter))
rctl |= E1000_RCTL_VFE;
}
@@ -2284,35 +2320,23 @@ static void e1000_set_rx_mode(struct net_device *netdev)
kfree(mcarray);
}
-/* Need to wait a few seconds after link up to get diagnostic information from
- * the phy */
-
-static void e1000_update_phy_info(unsigned long data)
-{
- struct e1000_adapter *adapter = (struct e1000_adapter *)data;
- schedule_work(&adapter->phy_info_task);
-}
-
+/**
+ * e1000_update_phy_info_task - get phy info
+ * @work: work struct contained inside adapter struct
+ *
+ * Need to wait a few seconds after link up to get diagnostic information from
+ * the phy
+ */
static void e1000_update_phy_info_task(struct work_struct *work)
{
struct e1000_adapter *adapter = container_of(work,
- struct e1000_adapter,
- phy_info_task);
- struct e1000_hw *hw = &adapter->hw;
-
- rtnl_lock();
- e1000_phy_get_info(hw, &adapter->phy_info);
- rtnl_unlock();
-}
-
-/**
- * e1000_82547_tx_fifo_stall - Timer Call-back
- * @data: pointer to adapter cast into an unsigned long
- **/
-static void e1000_82547_tx_fifo_stall(unsigned long data)
-{
- struct e1000_adapter *adapter = (struct e1000_adapter *)data;
- schedule_work(&adapter->fifo_stall_task);
+ struct e1000_adapter,
+ phy_info_task.work);
+ if (test_bit(__E1000_DOWN, &adapter->flags))
+ return;
+ mutex_lock(&adapter->mutex);
+ e1000_phy_get_info(&adapter->hw, &adapter->phy_info);
+ mutex_unlock(&adapter->mutex);
}
/**
@@ -2322,13 +2346,15 @@ static void e1000_82547_tx_fifo_stall(unsigned long data)
static void e1000_82547_tx_fifo_stall_task(struct work_struct *work)
{
struct e1000_adapter *adapter = container_of(work,
- struct e1000_adapter,
- fifo_stall_task);
+ struct e1000_adapter,
+ fifo_stall_task.work);
struct e1000_hw *hw = &adapter->hw;
struct net_device *netdev = adapter->netdev;
u32 tctl;
- rtnl_lock();
+ if (test_bit(__E1000_DOWN, &adapter->flags))
+ return;
+ mutex_lock(&adapter->mutex);
if (atomic_read(&adapter->tx_fifo_stall)) {
if ((er32(TDT) == er32(TDH)) &&
(er32(TDFT) == er32(TDFH)) &&
@@ -2346,10 +2372,10 @@ static void e1000_82547_tx_fifo_stall_task(struct work_struct *work)
atomic_set(&adapter->tx_fifo_stall, 0);
netif_wake_queue(netdev);
} else if (!test_bit(__E1000_DOWN, &adapter->flags)) {
- mod_timer(&adapter->tx_fifo_stall_timer, jiffies + 1);
+ schedule_delayed_work(&adapter->fifo_stall_task, 1);
}
}
- rtnl_unlock();
+ mutex_unlock(&adapter->mutex);
}
bool e1000_has_link(struct e1000_adapter *adapter)
@@ -2357,13 +2383,16 @@ bool e1000_has_link(struct e1000_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
bool link_active = false;
- /* get_link_status is set on LSC (link status) interrupt or
- * rx sequence error interrupt. get_link_status will stay
- * false until the e1000_check_for_link establishes link
- * for copper adapters ONLY
+ /* get_link_status is set on LSC (link status) interrupt or rx
+ * sequence error interrupt (except on intel ce4100).
+ * get_link_status will stay false until the
+ * e1000_check_for_link establishes link for copper adapters
+ * ONLY
*/
switch (hw->media_type) {
case e1000_media_type_copper:
+ if (hw->mac_type == e1000_ce4100)
+ hw->get_link_status = 1;
if (hw->get_link_status) {
e1000_check_for_link(hw);
link_active = !hw->get_link_status;
@@ -2387,17 +2416,23 @@ bool e1000_has_link(struct e1000_adapter *adapter)
}
/**
- * e1000_watchdog - Timer Call-back
- * @data: pointer to adapter cast into an unsigned long
+ * e1000_watchdog - work function
+ * @work: work struct contained inside adapter struct
**/
-static void e1000_watchdog(unsigned long data)
+static void e1000_watchdog(struct work_struct *work)
{
- struct e1000_adapter *adapter = (struct e1000_adapter *)data;
+ struct e1000_adapter *adapter = container_of(work,
+ struct e1000_adapter,
+ watchdog_task.work);
struct e1000_hw *hw = &adapter->hw;
struct net_device *netdev = adapter->netdev;
struct e1000_tx_ring *txdr = adapter->tx_ring;
u32 link, tctl;
+ if (test_bit(__E1000_DOWN, &adapter->flags))
+ return;
+
+ mutex_lock(&adapter->mutex);
link = e1000_has_link(adapter);
if ((netif_carrier_ok(netdev)) && link)
goto link_up;
@@ -2443,8 +2478,8 @@ static void e1000_watchdog(unsigned long data)
netif_carrier_on(netdev);
if (!test_bit(__E1000_DOWN, &adapter->flags))
- mod_timer(&adapter->phy_info_timer,
- round_jiffies(jiffies + 2 * HZ));
+ schedule_delayed_work(&adapter->phy_info_task,
+ 2 * HZ);
adapter->smartspeed = 0;
}
} else {
@@ -2456,8 +2491,8 @@ static void e1000_watchdog(unsigned long data)
netif_carrier_off(netdev);
if (!test_bit(__E1000_DOWN, &adapter->flags))
- mod_timer(&adapter->phy_info_timer,
- round_jiffies(jiffies + 2 * HZ));
+ schedule_delayed_work(&adapter->phy_info_task,
+ 2 * HZ);
}
e1000_smartspeed(adapter);
@@ -2486,8 +2521,8 @@ link_up:
* (Do the reset outside of interrupt context). */
adapter->tx_timeout_count++;
schedule_work(&adapter->reset_task);
- /* return immediately since reset is imminent */
- return;
+ /* exit immediately since reset is imminent */
+ goto unlock;
}
}
@@ -2513,10 +2548,12 @@ link_up:
/* Force detection of hung controller every watchdog period */
adapter->detect_tx_hung = true;
- /* Reset the timer */
+ /* Reschedule the task */
if (!test_bit(__E1000_DOWN, &adapter->flags))
- mod_timer(&adapter->watchdog_timer,
- round_jiffies(jiffies + 2 * HZ));
+ schedule_delayed_work(&adapter->watchdog_task, 2 * HZ);
+
+unlock:
+ mutex_unlock(&adapter->mutex);
}
enum latency_range {
@@ -2798,7 +2835,7 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
struct e1000_buffer *buffer_info;
unsigned int len = skb_headlen(skb);
unsigned int offset = 0, size, count = 0, i;
- unsigned int f;
+ unsigned int f, bytecount, segs;
i = tx_ring->next_to_use;
@@ -2857,13 +2894,14 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
}
for (f = 0; f < nr_frags; f++) {
- struct skb_frag_struct *frag;
+ const struct skb_frag_struct *frag;
frag = &skb_shinfo(skb)->frags[f];
- len = frag->size;
- offset = frag->page_offset;
+ len = skb_frag_size(frag);
+ offset = 0;
while (len) {
+ unsigned long bufend;
i++;
if (unlikely(i == tx_ring->count))
i = 0;
@@ -2877,18 +2915,19 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
/* Workaround for potential 82544 hang in PCI-X.
* Avoid terminating buffers within evenly-aligned
* dwords. */
+ bufend = (unsigned long)
+ page_to_phys(skb_frag_page(frag));
+ bufend += offset + size - 1;
if (unlikely(adapter->pcix_82544 &&
- !((unsigned long)(page_to_phys(frag->page) + offset
- + size - 1) & 4) &&
- size > 4))
+ !(bufend & 4) &&
+ size > 4))
size -= 4;
buffer_info->length = size;
buffer_info->time_stamp = jiffies;
buffer_info->mapped_as_page = true;
- buffer_info->dma = dma_map_page(&pdev->dev, frag->page,
- offset, size,
- DMA_TO_DEVICE);
+ buffer_info->dma = skb_frag_dma_map(&pdev->dev, frag,
+ offset, size, DMA_TO_DEVICE);
if (dma_mapping_error(&pdev->dev, buffer_info->dma))
goto dma_error;
buffer_info->next_to_watch = i;
@@ -2899,7 +2938,13 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
}
}
+ segs = skb_shinfo(skb)->gso_segs ?: 1;
+ /* multiply data chunks by size of headers */
+ bytecount = ((segs - 1) * skb_headlen(skb)) + skb->len;
+
tx_ring->buffer_info[i].skb = skb;
+ tx_ring->buffer_info[i].segs = segs;
+ tx_ring->buffer_info[i].bytecount = bytecount;
tx_ring->buffer_info[first].next_to_watch = i;
return count;
@@ -3138,7 +3183,7 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
nr_frags = skb_shinfo(skb)->nr_frags;
for (f = 0; f < nr_frags; f++)
- count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size,
+ count += TXD_USE_COUNT(skb_frag_size(&skb_shinfo(skb)->frags[f]),
max_txd_pwr);
if (adapter->pcix_82544)
count += nr_frags;
@@ -3148,17 +3193,15 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
if (unlikely(e1000_maybe_stop_tx(netdev, tx_ring, count + 2)))
return NETDEV_TX_BUSY;
- if (unlikely(hw->mac_type == e1000_82547)) {
- if (unlikely(e1000_82547_fifo_workaround(adapter, skb))) {
- netif_stop_queue(netdev);
- if (!test_bit(__E1000_DOWN, &adapter->flags))
- mod_timer(&adapter->tx_fifo_stall_timer,
- jiffies + 1);
- return NETDEV_TX_BUSY;
- }
+ if (unlikely((hw->mac_type == e1000_82547) &&
+ (e1000_82547_fifo_workaround(adapter, skb)))) {
+ netif_stop_queue(netdev);
+ if (!test_bit(__E1000_DOWN, &adapter->flags))
+ schedule_delayed_work(&adapter->fifo_stall_task, 1);
+ return NETDEV_TX_BUSY;
}
- if (unlikely(vlan_tx_tag_present(skb))) {
+ if (vlan_tx_tag_present(skb)) {
tx_flags |= E1000_TX_FLAGS_VLAN;
tx_flags |= (vlan_tx_tag_get(skb) << E1000_TX_FLAGS_VLAN_SHIFT);
}
@@ -3217,6 +3260,8 @@ static void e1000_reset_task(struct work_struct *work)
struct e1000_adapter *adapter =
container_of(work, struct e1000_adapter, reset_task);
+ if (test_bit(__E1000_DOWN, &adapter->flags))
+ return;
e1000_reinit_safe(adapter);
}
@@ -3225,7 +3270,7 @@ static void e1000_reset_task(struct work_struct *work)
* @netdev: network interface device structure
*
* Returns the address of the device statistics structure.
- * The statistics are actually updated from the timer callback.
+ * The statistics are actually updated from the watchdog.
**/
static struct net_device_stats *e1000_get_stats(struct net_device *netdev)
@@ -3493,7 +3538,7 @@ static irqreturn_t e1000_intr(int irq, void *data)
hw->get_link_status = 1;
/* guard against interrupt when we're going down */
if (!test_bit(__E1000_DOWN, &adapter->flags))
- mod_timer(&adapter->watchdog_timer, jiffies + 1);
+ schedule_delayed_work(&adapter->watchdog_task, 1);
}
/* disable interrupts, without the synchronize_irq bit */
@@ -3573,14 +3618,8 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter,
cleaned = (i == eop);
if (cleaned) {
- struct sk_buff *skb = buffer_info->skb;
- unsigned int segs, bytecount;
- segs = skb_shinfo(skb)->gso_segs ?: 1;
- /* multiply data chunks by size of headers */
- bytecount = ((segs - 1) * skb_headlen(skb)) +
- skb->len;
- total_tx_packets += segs;
- total_tx_bytes += bytecount;
+ total_tx_packets += buffer_info->segs;
+ total_tx_bytes += buffer_info->bytecount;
}
e1000_unmap_and_free_tx_resource(adapter, buffer_info);
tx_desc->upper.data = 0;
@@ -3698,7 +3737,7 @@ static void e1000_consume_page(struct e1000_buffer *bi, struct sk_buff *skb,
bi->page = NULL;
skb->len += length;
skb->data_len += length;
- skb->truesize += length;
+ skb->truesize += PAGE_SIZE;
}
/**
@@ -3713,12 +3752,12 @@ static void e1000_receive_skb(struct e1000_adapter *adapter, u8 status,
{
skb->protocol = eth_type_trans(skb, adapter->netdev);
- if ((unlikely(adapter->vlgrp && (status & E1000_RXD_STAT_VP))))
- vlan_gro_receive(&adapter->napi, adapter->vlgrp,
- le16_to_cpu(vlan) & E1000_RXD_SPC_VLAN_MASK,
- skb);
- else
- napi_gro_receive(&adapter->napi, skb);
+ if (status & E1000_RXD_STAT_VP) {
+ u16 vid = le16_to_cpu(vlan) & E1000_RXD_SPC_VLAN_MASK;
+
+ __vlan_hwaccel_put_tag(skb, vid);
+ }
+ napi_gro_receive(&adapter->napi, skb);
}
/**
@@ -4501,46 +4540,61 @@ void e1000_io_write(struct e1000_hw *hw, unsigned long port, u32 value)
outl(value, port);
}
-static void e1000_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *grp)
+static bool e1000_vlan_used(struct e1000_adapter *adapter)
+{
+ u16 vid;
+
+ for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
+ return true;
+ return false;
+}
+
+static void e1000_vlan_filter_on_off(struct e1000_adapter *adapter,
+ bool filter_on)
{
- struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
- u32 ctrl, rctl;
+ u32 rctl;
if (!test_bit(__E1000_DOWN, &adapter->flags))
e1000_irq_disable(adapter);
- adapter->vlgrp = grp;
-
- if (grp) {
- /* enable VLAN tag insert/strip */
- ctrl = er32(CTRL);
- ctrl |= E1000_CTRL_VME;
- ew32(CTRL, ctrl);
+ if (filter_on) {
/* enable VLAN receive filtering */
rctl = er32(RCTL);
rctl &= ~E1000_RCTL_CFIEN;
- if (!(netdev->flags & IFF_PROMISC))
+ if (!(adapter->netdev->flags & IFF_PROMISC))
rctl |= E1000_RCTL_VFE;
ew32(RCTL, rctl);
e1000_update_mng_vlan(adapter);
} else {
- /* disable VLAN tag insert/strip */
- ctrl = er32(CTRL);
- ctrl &= ~E1000_CTRL_VME;
- ew32(CTRL, ctrl);
-
/* disable VLAN receive filtering */
rctl = er32(RCTL);
rctl &= ~E1000_RCTL_VFE;
ew32(RCTL, rctl);
+ }
- if (adapter->mng_vlan_id != (u16)E1000_MNG_VLAN_NONE) {
- e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id);
- adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
- }
+ if (!test_bit(__E1000_DOWN, &adapter->flags))
+ e1000_irq_enable(adapter);
+}
+
+static void e1000_vlan_mode(struct net_device *netdev, u32 features)
+{
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+ struct e1000_hw *hw = &adapter->hw;
+ u32 ctrl;
+
+ if (!test_bit(__E1000_DOWN, &adapter->flags))
+ e1000_irq_disable(adapter);
+
+ ctrl = er32(CTRL);
+ if (features & NETIF_F_HW_VLAN_RX) {
+ /* enable VLAN tag insert/strip */
+ ctrl |= E1000_CTRL_VME;
+ } else {
+ /* disable VLAN tag insert/strip */
+ ctrl &= ~E1000_CTRL_VME;
}
+ ew32(CTRL, ctrl);
if (!test_bit(__E1000_DOWN, &adapter->flags))
e1000_irq_enable(adapter);
@@ -4556,11 +4610,17 @@ static void e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) &&
(vid == adapter->mng_vlan_id))
return;
+
+ if (!e1000_vlan_used(adapter))
+ e1000_vlan_filter_on_off(adapter, true);
+
/* add VID to filter table */
index = (vid >> 5) & 0x7F;
vfta = E1000_READ_REG_ARRAY(hw, VFTA, index);
vfta |= (1 << (vid & 0x1F));
e1000_write_vfta(hw, index, vfta);
+
+ set_bit(vid, adapter->active_vlans);
}
static void e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
@@ -4571,7 +4631,6 @@ static void e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
if (!test_bit(__E1000_DOWN, &adapter->flags))
e1000_irq_disable(adapter);
- vlan_group_set_device(adapter->vlgrp, vid, NULL);
if (!test_bit(__E1000_DOWN, &adapter->flags))
e1000_irq_enable(adapter);
@@ -4580,20 +4639,23 @@ static void e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
vfta = E1000_READ_REG_ARRAY(hw, VFTA, index);
vfta &= ~(1 << (vid & 0x1F));
e1000_write_vfta(hw, index, vfta);
+
+ clear_bit(vid, adapter->active_vlans);
+
+ if (!e1000_vlan_used(adapter))
+ e1000_vlan_filter_on_off(adapter, false);
}
static void e1000_restore_vlan(struct e1000_adapter *adapter)
{
- e1000_vlan_rx_register(adapter->netdev, adapter->vlgrp);
+ u16 vid;
- if (adapter->vlgrp) {
- u16 vid;
- for (vid = 0; vid < VLAN_N_VID; vid++) {
- if (!vlan_group_get_device(adapter->vlgrp, vid))
- continue;
- e1000_vlan_rx_add_vid(adapter->netdev, vid);
- }
- }
+ if (!e1000_vlan_used(adapter))
+ return;
+
+ e1000_vlan_filter_on_off(adapter, true);
+ for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
+ e1000_vlan_rx_add_vid(adapter->netdev, vid);
}
int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx)
@@ -4654,6 +4716,8 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake)
netif_device_detach(netdev);
+ mutex_lock(&adapter->mutex);
+
if (netif_running(netdev)) {
WARN_ON(test_bit(__E1000_RESETTING, &adapter->flags));
e1000_down(adapter);
@@ -4661,8 +4725,10 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake)
#ifdef CONFIG_PM
retval = pci_save_state(pdev);
- if (retval)
+ if (retval) {
+ mutex_unlock(&adapter->mutex);
return retval;
+ }
#endif
status = er32(STATUS);
@@ -4717,6 +4783,8 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake)
if (netif_running(netdev))
e1000_free_irq(adapter);
+ mutex_unlock(&adapter->mutex);
+
pci_disable_device(pdev);
return 0;
diff --git a/drivers/net/e1000/e1000_osdep.h b/drivers/net/ethernet/intel/e1000/e1000_osdep.h
index 33e7c45a4fe4..33e7c45a4fe4 100644
--- a/drivers/net/e1000/e1000_osdep.h
+++ b/drivers/net/ethernet/intel/e1000/e1000_osdep.h
diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/ethernet/intel/e1000/e1000_param.c
index 1301eba8b57a..1301eba8b57a 100644
--- a/drivers/net/e1000/e1000_param.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_param.c
diff --git a/drivers/net/e1000e/es2lan.c b/drivers/net/ethernet/intel/e1000e/80003es2lan.c
index f4bbeb22f51f..e1159e54334a 100644
--- a/drivers/net/e1000e/es2lan.c
+++ b/drivers/net/ethernet/intel/e1000e/80003es2lan.c
@@ -836,6 +836,7 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
struct e1000_mac_info *mac = &hw->mac;
u32 reg_data;
s32 ret_val;
+ u16 kum_reg_data;
u16 i;
e1000_initialize_hw_bits_80003es2lan(hw);
@@ -861,6 +862,13 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
/* Setup link and flow control */
ret_val = e1000e_setup_link(hw);
+ /* Disable IBIST slave mode (far-end loopback) */
+ e1000_read_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
+ &kum_reg_data);
+ kum_reg_data |= E1000_KMRNCTRLSTA_IBIST_DISABLE;
+ e1000_write_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
+ kum_reg_data);
+
/* Set the transmit descriptor write-back policy */
reg_data = er32(TXDCTL(0));
reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
@@ -1305,6 +1313,7 @@ static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
ew32(KMRNCTRLSTA, kmrnctrlsta);
+ e1e_flush();
udelay(2);
@@ -1339,6 +1348,7 @@ static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | data;
ew32(KMRNCTRLSTA, kmrnctrlsta);
+ e1e_flush();
udelay(2);
@@ -1431,7 +1441,7 @@ static void e1000_clear_hw_cntrs_80003es2lan(struct e1000_hw *hw)
er32(ICRXDMTC);
}
-static struct e1000_mac_operations es2_mac_ops = {
+static const struct e1000_mac_operations es2_mac_ops = {
.read_mac_addr = e1000_read_mac_addr_80003es2lan,
.id_led_init = e1000e_id_led_init,
.blink_led = e1000e_blink_led_generic,
@@ -1454,7 +1464,7 @@ static struct e1000_mac_operations es2_mac_ops = {
.setup_led = e1000e_setup_led_generic,
};
-static struct e1000_phy_operations es2_phy_ops = {
+static const struct e1000_phy_operations es2_phy_ops = {
.acquire = e1000_acquire_phy_80003es2lan,
.check_polarity = e1000_check_polarity_m88,
.check_reset_block = e1000e_check_reset_block_generic,
@@ -1472,7 +1482,7 @@ static struct e1000_phy_operations es2_phy_ops = {
.cfg_on_link_up = e1000_cfg_on_link_up_80003es2lan,
};
-static struct e1000_nvm_operations es2_nvm_ops = {
+static const struct e1000_nvm_operations es2_nvm_ops = {
.acquire = e1000_acquire_nvm_80003es2lan,
.read = e1000e_read_nvm_eerd,
.release = e1000_release_nvm_80003es2lan,
@@ -1482,13 +1492,12 @@ static struct e1000_nvm_operations es2_nvm_ops = {
.write = e1000_write_nvm_80003es2lan,
};
-struct e1000_info e1000_es2_info = {
+const struct e1000_info e1000_es2_info = {
.mac = e1000_80003es2lan,
.flags = FLAG_HAS_HW_VLAN_FILTER
| FLAG_HAS_JUMBO_FRAMES
| FLAG_HAS_WOL
| FLAG_APME_IN_CTRL3
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_CTRLEXT_ON_LOAD
| FLAG_RX_NEEDS_RESTART /* errata */
| FLAG_TARC_SET_BIT_ZERO /* errata */
diff --git a/drivers/net/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c
index 8295f2192439..a3e65fd26e09 100644
--- a/drivers/net/e1000e/82571.c
+++ b/drivers/net/ethernet/intel/e1000e/82571.c
@@ -431,8 +431,6 @@ static s32 e1000_get_variants_82571(struct e1000_adapter *adapter)
adapter->flags &= ~FLAG_HAS_WOL;
break;
case e1000_82573:
- case e1000_82574:
- case e1000_82583:
if (pdev->device == E1000_DEV_ID_82573L) {
adapter->flags |= FLAG_HAS_JUMBO_FRAMES;
adapter->max_hw_frame_size = DEFAULT_JUMBO;
@@ -1929,7 +1927,7 @@ static void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw)
er32(ICRXDMTC);
}
-static struct e1000_mac_operations e82571_mac_ops = {
+static const struct e1000_mac_operations e82571_mac_ops = {
/* .check_mng_mode: mac type dependent */
/* .check_for_link: media type dependent */
.id_led_init = e1000e_id_led_init,
@@ -1951,7 +1949,7 @@ static struct e1000_mac_operations e82571_mac_ops = {
.read_mac_addr = e1000_read_mac_addr_82571,
};
-static struct e1000_phy_operations e82_phy_ops_igp = {
+static const struct e1000_phy_operations e82_phy_ops_igp = {
.acquire = e1000_get_hw_semaphore_82571,
.check_polarity = e1000_check_polarity_igp,
.check_reset_block = e1000e_check_reset_block_generic,
@@ -1969,7 +1967,7 @@ static struct e1000_phy_operations e82_phy_ops_igp = {
.cfg_on_link_up = NULL,
};
-static struct e1000_phy_operations e82_phy_ops_m88 = {
+static const struct e1000_phy_operations e82_phy_ops_m88 = {
.acquire = e1000_get_hw_semaphore_82571,
.check_polarity = e1000_check_polarity_m88,
.check_reset_block = e1000e_check_reset_block_generic,
@@ -1987,7 +1985,7 @@ static struct e1000_phy_operations e82_phy_ops_m88 = {
.cfg_on_link_up = NULL,
};
-static struct e1000_phy_operations e82_phy_ops_bm = {
+static const struct e1000_phy_operations e82_phy_ops_bm = {
.acquire = e1000_get_hw_semaphore_82571,
.check_polarity = e1000_check_polarity_m88,
.check_reset_block = e1000e_check_reset_block_generic,
@@ -2005,7 +2003,7 @@ static struct e1000_phy_operations e82_phy_ops_bm = {
.cfg_on_link_up = NULL,
};
-static struct e1000_nvm_operations e82571_nvm_ops = {
+static const struct e1000_nvm_operations e82571_nvm_ops = {
.acquire = e1000_acquire_nvm_82571,
.read = e1000e_read_nvm_eerd,
.release = e1000_release_nvm_82571,
@@ -2015,13 +2013,12 @@ static struct e1000_nvm_operations e82571_nvm_ops = {
.write = e1000_write_nvm_82571,
};
-struct e1000_info e1000_82571_info = {
+const struct e1000_info e1000_82571_info = {
.mac = e1000_82571,
.flags = FLAG_HAS_HW_VLAN_FILTER
| FLAG_HAS_JUMBO_FRAMES
| FLAG_HAS_WOL
| FLAG_APME_IN_CTRL3
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_CTRLEXT_ON_LOAD
| FLAG_HAS_SMART_POWER_DOWN
| FLAG_RESET_OVERWRITES_LAA /* errata */
@@ -2037,13 +2034,12 @@ struct e1000_info e1000_82571_info = {
.nvm_ops = &e82571_nvm_ops,
};
-struct e1000_info e1000_82572_info = {
+const struct e1000_info e1000_82572_info = {
.mac = e1000_82572,
.flags = FLAG_HAS_HW_VLAN_FILTER
| FLAG_HAS_JUMBO_FRAMES
| FLAG_HAS_WOL
| FLAG_APME_IN_CTRL3
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_CTRLEXT_ON_LOAD
| FLAG_TARC_SPEED_MODE_BIT, /* errata */
.flags2 = FLAG2_DISABLE_ASPM_L1 /* errata 13 */
@@ -2056,12 +2052,11 @@ struct e1000_info e1000_82572_info = {
.nvm_ops = &e82571_nvm_ops,
};
-struct e1000_info e1000_82573_info = {
+const struct e1000_info e1000_82573_info = {
.mac = e1000_82573,
.flags = FLAG_HAS_HW_VLAN_FILTER
| FLAG_HAS_WOL
| FLAG_APME_IN_CTRL3
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_SMART_POWER_DOWN
| FLAG_HAS_AMT
| FLAG_HAS_SWSM_ON_LOAD,
@@ -2075,19 +2070,19 @@ struct e1000_info e1000_82573_info = {
.nvm_ops = &e82571_nvm_ops,
};
-struct e1000_info e1000_82574_info = {
+const struct e1000_info e1000_82574_info = {
.mac = e1000_82574,
.flags = FLAG_HAS_HW_VLAN_FILTER
| FLAG_HAS_MSIX
| FLAG_HAS_JUMBO_FRAMES
| FLAG_HAS_WOL
| FLAG_APME_IN_CTRL3
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_SMART_POWER_DOWN
| FLAG_HAS_AMT
| FLAG_HAS_CTRLEXT_ON_LOAD,
.flags2 = FLAG2_CHECK_PHY_HANG
- | FLAG2_DISABLE_ASPM_L0S,
+ | FLAG2_DISABLE_ASPM_L0S
+ | FLAG2_NO_DISABLE_RX,
.pba = 32,
.max_hw_frame_size = DEFAULT_JUMBO,
.get_variants = e1000_get_variants_82571,
@@ -2096,18 +2091,19 @@ struct e1000_info e1000_82574_info = {
.nvm_ops = &e82571_nvm_ops,
};
-struct e1000_info e1000_82583_info = {
+const struct e1000_info e1000_82583_info = {
.mac = e1000_82583,
.flags = FLAG_HAS_HW_VLAN_FILTER
| FLAG_HAS_WOL
| FLAG_APME_IN_CTRL3
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_SMART_POWER_DOWN
| FLAG_HAS_AMT
+ | FLAG_HAS_JUMBO_FRAMES
| FLAG_HAS_CTRLEXT_ON_LOAD,
- .flags2 = FLAG2_DISABLE_ASPM_L0S,
+ .flags2 = FLAG2_DISABLE_ASPM_L0S
+ | FLAG2_NO_DISABLE_RX,
.pba = 32,
- .max_hw_frame_size = ETH_FRAME_LEN + ETH_FCS_LEN,
+ .max_hw_frame_size = DEFAULT_JUMBO,
.get_variants = e1000_get_variants_82571,
.mac_ops = &e82571_mac_ops,
.phy_ops = &e82_phy_ops_bm,
diff --git a/drivers/net/e1000e/Makefile b/drivers/net/ethernet/intel/e1000e/Makefile
index 28519acacd2d..948c05db5d68 100644
--- a/drivers/net/e1000e/Makefile
+++ b/drivers/net/ethernet/intel/e1000e/Makefile
@@ -32,6 +32,6 @@
obj-$(CONFIG_E1000E) += e1000e.o
-e1000e-objs := 82571.o ich8lan.o es2lan.o \
+e1000e-objs := 82571.o ich8lan.o 80003es2lan.o \
lib.o phy.o param.o ethtool.o netdev.o
diff --git a/drivers/net/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h
index c516a7440bec..c516a7440bec 100644
--- a/drivers/net/e1000e/defines.h
+++ b/drivers/net/ethernet/intel/e1000e/defines.h
diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index 9549879e66a0..9fe18d1d53d8 100644
--- a/drivers/net/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -104,6 +104,7 @@ struct e1000_info;
(((reg) & ~MAX_PHY_REG_ADDRESS) << (PHY_UPPER_SHIFT - PHY_PAGE_SHIFT)))
/* PHY Wakeup Registers and defines */
+#define BM_PORT_GEN_CFG PHY_REG(BM_PORT_CTRL_PAGE, 17)
#define BM_RCTL PHY_REG(BM_WUC_PAGE, 0)
#define BM_WUC PHY_REG(BM_WUC_PAGE, 1)
#define BM_WUFC PHY_REG(BM_WUC_PAGE, 2)
@@ -122,20 +123,21 @@ struct e1000_info;
#define BM_RCTL_PMCF 0x0040 /* Pass MAC Control Frames */
#define BM_RCTL_RFCE 0x0080 /* Rx Flow Control Enable */
-#define HV_SCC_UPPER PHY_REG(778, 16) /* Single Collision Count */
-#define HV_SCC_LOWER PHY_REG(778, 17)
-#define HV_ECOL_UPPER PHY_REG(778, 18) /* Excessive Collision Count */
-#define HV_ECOL_LOWER PHY_REG(778, 19)
-#define HV_MCC_UPPER PHY_REG(778, 20) /* Multiple Collision Count */
-#define HV_MCC_LOWER PHY_REG(778, 21)
-#define HV_LATECOL_UPPER PHY_REG(778, 23) /* Late Collision Count */
-#define HV_LATECOL_LOWER PHY_REG(778, 24)
-#define HV_COLC_UPPER PHY_REG(778, 25) /* Collision Count */
-#define HV_COLC_LOWER PHY_REG(778, 26)
-#define HV_DC_UPPER PHY_REG(778, 27) /* Defer Count */
-#define HV_DC_LOWER PHY_REG(778, 28)
-#define HV_TNCRS_UPPER PHY_REG(778, 29) /* Transmit with no CRS */
-#define HV_TNCRS_LOWER PHY_REG(778, 30)
+#define HV_STATS_PAGE 778
+#define HV_SCC_UPPER PHY_REG(HV_STATS_PAGE, 16) /* Single Collision Count */
+#define HV_SCC_LOWER PHY_REG(HV_STATS_PAGE, 17)
+#define HV_ECOL_UPPER PHY_REG(HV_STATS_PAGE, 18) /* Excessive Coll. Count */
+#define HV_ECOL_LOWER PHY_REG(HV_STATS_PAGE, 19)
+#define HV_MCC_UPPER PHY_REG(HV_STATS_PAGE, 20) /* Multiple Coll. Count */
+#define HV_MCC_LOWER PHY_REG(HV_STATS_PAGE, 21)
+#define HV_LATECOL_UPPER PHY_REG(HV_STATS_PAGE, 23) /* Late Collision Count */
+#define HV_LATECOL_LOWER PHY_REG(HV_STATS_PAGE, 24)
+#define HV_COLC_UPPER PHY_REG(HV_STATS_PAGE, 25) /* Collision Count */
+#define HV_COLC_LOWER PHY_REG(HV_STATS_PAGE, 26)
+#define HV_DC_UPPER PHY_REG(HV_STATS_PAGE, 27) /* Defer Count */
+#define HV_DC_LOWER PHY_REG(HV_STATS_PAGE, 28)
+#define HV_TNCRS_UPPER PHY_REG(HV_STATS_PAGE, 29) /* Transmit with no CRS */
+#define HV_TNCRS_LOWER PHY_REG(HV_STATS_PAGE, 30)
#define E1000_FCRTV_PCH 0x05F40 /* PCH Flow Control Refresh Timer Value */
@@ -153,6 +155,9 @@ struct e1000_info;
#define HV_M_STATUS_SPEED_1000 0x0200
#define HV_M_STATUS_LINK_UP 0x0040
+#define E1000_ICH_FWSM_PCIM2PCI 0x01000000 /* ME PCIm-to-PCI active */
+#define E1000_ICH_FWSM_PCIM2PCI_COUNT 2000
+
/* Time to wait before putting the device into D3 if there's no link (in ms). */
#define LINK_TIMEOUT 100
@@ -197,11 +202,6 @@ enum e1000_boards {
board_pch2lan,
};
-struct e1000_queue_stats {
- u64 packets;
- u64 bytes;
-};
-
struct e1000_ps_page {
struct page *page;
u64 dma; /* must be u64 - written to hw */
@@ -255,8 +255,6 @@ struct e1000_ring {
int set_itr;
struct sk_buff *rx_skb_top;
-
- struct e1000_queue_stats stats;
};
/* PHY register snapshot values */
@@ -339,7 +337,7 @@ struct e1000_adapter {
int *work_done, int work_to_do)
____cacheline_aligned_in_smp;
void (*alloc_rx_buf) (struct e1000_adapter *adapter,
- int cleaned_count);
+ int cleaned_count, gfp_t gfp);
struct e1000_ring *rx_ring;
u32 rx_int_delay;
@@ -408,9 +406,9 @@ struct e1000_info {
u32 pba;
u32 max_hw_frame_size;
s32 (*get_variants)(struct e1000_adapter *);
- struct e1000_mac_operations *mac_ops;
- struct e1000_phy_operations *phy_ops;
- struct e1000_nvm_operations *nvm_ops;
+ const struct e1000_mac_operations *mac_ops;
+ const struct e1000_phy_operations *phy_ops;
+ const struct e1000_nvm_operations *nvm_ops;
};
/* hardware capability, feature, and workaround flags */
@@ -442,12 +440,11 @@ struct e1000_info {
#define FLAG_LSC_GIG_SPEED_DROP (1 << 25)
#define FLAG_SMART_POWER_DOWN (1 << 26)
#define FLAG_MSI_ENABLED (1 << 27)
-#define FLAG_RX_CSUM_ENABLED (1 << 28)
+/* reserved (1 << 28) */
#define FLAG_TSO_FORCE (1 << 29)
#define FLAG_RX_RESTART_NOW (1 << 30)
#define FLAG_MSI_TEST_FAILED (1 << 31)
-/* CRC Stripping defines */
#define FLAG2_CRC_STRIPPING (1 << 0)
#define FLAG2_HAS_PHY_WAKEUP (1 << 1)
#define FLAG2_IS_DISCARDING (1 << 2)
@@ -458,17 +455,21 @@ struct e1000_info {
#define FLAG2_DISABLE_ASPM_L0S (1 << 7)
#define FLAG2_DISABLE_AIM (1 << 8)
#define FLAG2_CHECK_PHY_HANG (1 << 9)
+#define FLAG2_NO_DISABLE_RX (1 << 10)
+#define FLAG2_PCIM2PCI_ARBITER_WA (1 << 11)
#define E1000_RX_DESC_PS(R, i) \
(&(((union e1000_rx_desc_packet_split *)((R).desc))[i]))
+#define E1000_RX_DESC_EXT(R, i) \
+ (&(((union e1000_rx_desc_extended *)((R).desc))[i]))
#define E1000_GET_DESC(R, i, type) (&(((struct type *)((R).desc))[i]))
-#define E1000_RX_DESC(R, i) E1000_GET_DESC(R, i, e1000_rx_desc)
#define E1000_TX_DESC(R, i) E1000_GET_DESC(R, i, e1000_tx_desc)
#define E1000_CONTEXT_DESC(R, i) E1000_GET_DESC(R, i, e1000_context_desc)
enum e1000_state_t {
__E1000_TESTING,
__E1000_RESETTING,
+ __E1000_ACCESS_SHARED_RESOURCE,
__E1000_DOWN
};
@@ -506,17 +507,17 @@ extern unsigned int copybreak;
extern char *e1000e_get_hw_dev_name(struct e1000_hw *hw);
-extern struct e1000_info e1000_82571_info;
-extern struct e1000_info e1000_82572_info;
-extern struct e1000_info e1000_82573_info;
-extern struct e1000_info e1000_82574_info;
-extern struct e1000_info e1000_82583_info;
-extern struct e1000_info e1000_ich8_info;
-extern struct e1000_info e1000_ich9_info;
-extern struct e1000_info e1000_ich10_info;
-extern struct e1000_info e1000_pch_info;
-extern struct e1000_info e1000_pch2_info;
-extern struct e1000_info e1000_es2_info;
+extern const struct e1000_info e1000_82571_info;
+extern const struct e1000_info e1000_82572_info;
+extern const struct e1000_info e1000_82573_info;
+extern const struct e1000_info e1000_82574_info;
+extern const struct e1000_info e1000_82583_info;
+extern const struct e1000_info e1000_ich8_info;
+extern const struct e1000_info e1000_ich9_info;
+extern const struct e1000_info e1000_ich10_info;
+extern const struct e1000_info e1000_pch_info;
+extern const struct e1000_info e1000_pch2_info;
+extern const struct e1000_info e1000_es2_info;
extern s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num,
u32 pba_num_size);
@@ -533,7 +534,8 @@ extern void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
bool state);
extern void e1000e_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw);
extern void e1000e_gig_downshift_workaround_ich8lan(struct e1000_hw *hw);
-extern void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw);
+extern void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw);
+extern void e1000_resume_workarounds_pchlan(struct e1000_hw *hw);
extern s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable);
extern s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable);
extern void e1000_copy_rx_addrs_to_phy_ich8lan(struct e1000_hw *hw);
@@ -584,6 +586,7 @@ extern s32 e1000e_check_reset_block_generic(struct e1000_hw *hw);
extern s32 e1000e_phy_force_speed_duplex_igp(struct e1000_hw *hw);
extern s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw);
extern s32 e1000e_get_phy_info_igp(struct e1000_hw *hw);
+extern s32 e1000_set_page_igp(struct e1000_hw *hw, u16 page);
extern s32 e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data);
extern s32 e1000e_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset,
u16 *data);
@@ -604,6 +607,10 @@ extern enum e1000_phy_type e1000e_get_phy_type_from_id(u32 phy_id);
extern s32 e1000e_determine_phy_address(struct e1000_hw *hw);
extern s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data);
extern s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data);
+extern s32 e1000_enable_phy_wakeup_reg_access_bm(struct e1000_hw *hw,
+ u16 *phy_reg);
+extern s32 e1000_disable_phy_wakeup_reg_access_bm(struct e1000_hw *hw,
+ u16 *phy_reg);
extern s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data);
extern s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data);
extern void e1000e_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl);
@@ -624,9 +631,13 @@ extern s32 e1000e_check_downshift(struct e1000_hw *hw);
extern s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data);
extern s32 e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset,
u16 *data);
+extern s32 e1000_read_phy_reg_page_hv(struct e1000_hw *hw, u32 offset,
+ u16 *data);
extern s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data);
extern s32 e1000_write_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset,
u16 data);
+extern s32 e1000_write_phy_reg_page_hv(struct e1000_hw *hw, u32 offset,
+ u16 data);
extern s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw);
extern s32 e1000_copper_link_setup_82577(struct e1000_hw *hw);
extern s32 e1000_check_polarity_82577(struct e1000_hw *hw);
diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c
index 859d0d3af6c9..69c9d2199140 100644
--- a/drivers/net/e1000e/ethtool.c
+++ b/drivers/net/ethernet/intel/e1000e/ethtool.c
@@ -29,6 +29,7 @@
/* ethtool support for e1000 */
#include <linux/netdevice.h>
+#include <linux/interrupt.h>
#include <linux/ethtool.h>
#include <linux/pci.h>
#include <linux/slab.h>
@@ -366,59 +367,6 @@ out:
return retval;
}
-static u32 e1000_get_rx_csum(struct net_device *netdev)
-{
- struct e1000_adapter *adapter = netdev_priv(netdev);
- return adapter->flags & FLAG_RX_CSUM_ENABLED;
-}
-
-static int e1000_set_rx_csum(struct net_device *netdev, u32 data)
-{
- struct e1000_adapter *adapter = netdev_priv(netdev);
-
- if (data)
- adapter->flags |= FLAG_RX_CSUM_ENABLED;
- else
- adapter->flags &= ~FLAG_RX_CSUM_ENABLED;
-
- if (netif_running(netdev))
- e1000e_reinit_locked(adapter);
- else
- e1000e_reset(adapter);
- return 0;
-}
-
-static u32 e1000_get_tx_csum(struct net_device *netdev)
-{
- return (netdev->features & NETIF_F_HW_CSUM) != 0;
-}
-
-static int e1000_set_tx_csum(struct net_device *netdev, u32 data)
-{
- if (data)
- netdev->features |= NETIF_F_HW_CSUM;
- else
- netdev->features &= ~NETIF_F_HW_CSUM;
-
- return 0;
-}
-
-static int e1000_set_tso(struct net_device *netdev, u32 data)
-{
- struct e1000_adapter *adapter = netdev_priv(netdev);
-
- if (data) {
- netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
- } else {
- netdev->features &= ~NETIF_F_TSO;
- netdev->features &= ~NETIF_F_TSO6;
- }
-
- adapter->flags |= FLAG_TSO_FORCE;
- return 0;
-}
-
static u32 e1000_get_msglevel(struct net_device *netdev)
{
struct e1000_adapter *adapter = netdev_priv(netdev);
@@ -664,12 +612,8 @@ static void e1000_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = E1000_MAX_RXD;
ring->tx_max_pending = E1000_MAX_TXD;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = rx_ring->count;
ring->tx_pending = tx_ring->count;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
static int e1000_set_ringparam(struct net_device *netdev,
@@ -963,6 +907,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
/* Disable all the interrupts */
ew32(IMC, 0xFFFFFFFF);
+ e1e_flush();
usleep_range(10000, 20000);
/* Test each interrupt */
@@ -995,6 +940,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
adapter->test_icr = 0;
ew32(IMC, mask);
ew32(ICS, mask);
+ e1e_flush();
usleep_range(10000, 20000);
if (adapter->test_icr & mask) {
@@ -1013,6 +959,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
adapter->test_icr = 0;
ew32(IMS, mask);
ew32(ICS, mask);
+ e1e_flush();
usleep_range(10000, 20000);
if (!(adapter->test_icr & mask)) {
@@ -1031,6 +978,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
adapter->test_icr = 0;
ew32(IMC, ~mask & 0x00007FFF);
ew32(ICS, ~mask & 0x00007FFF);
+ e1e_flush();
usleep_range(10000, 20000);
if (adapter->test_icr) {
@@ -1042,6 +990,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
/* Disable all the interrupts */
ew32(IMC, 0xFFFFFFFF);
+ e1e_flush();
usleep_range(10000, 20000);
/* Unhook test interrupt handler */
@@ -1189,7 +1138,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
goto err_nomem;
}
- rx_ring->size = rx_ring->count * sizeof(struct e1000_rx_desc);
+ rx_ring->size = rx_ring->count * sizeof(union e1000_rx_desc_extended);
rx_ring->desc = dma_alloc_coherent(&pdev->dev, rx_ring->size,
&rx_ring->dma, GFP_KERNEL);
if (!rx_ring->desc) {
@@ -1200,7 +1149,8 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
rx_ring->next_to_clean = 0;
rctl = er32(RCTL);
- ew32(RCTL, rctl & ~E1000_RCTL_EN);
+ if (!(adapter->flags2 & FLAG2_NO_DISABLE_RX))
+ ew32(RCTL, rctl & ~E1000_RCTL_EN);
ew32(RDBAL, ((u64) rx_ring->dma & 0xFFFFFFFF));
ew32(RDBAH, ((u64) rx_ring->dma >> 32));
ew32(RDLEN, rx_ring->size);
@@ -1214,7 +1164,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
ew32(RCTL, rctl);
for (i = 0; i < rx_ring->count; i++) {
- struct e1000_rx_desc *rx_desc = E1000_RX_DESC(*rx_ring, i);
+ union e1000_rx_desc_extended *rx_desc;
struct sk_buff *skb;
skb = alloc_skb(2048 + NET_IP_ALIGN, GFP_KERNEL);
@@ -1232,8 +1182,9 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
ret_val = 8;
goto err_nomem;
}
- rx_desc->buffer_addr =
- cpu_to_le64(rx_ring->buffer_info[i].dma);
+ rx_desc = E1000_RX_DESC_EXT(*rx_ring, i);
+ rx_desc->read.buffer_addr =
+ cpu_to_le64(rx_ring->buffer_info[i].dma);
memset(skb->data, 0x00, skb->len);
}
@@ -1275,6 +1226,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter)
E1000_CTRL_FD); /* Force Duplex to FULL */
ew32(CTRL, ctrl_reg);
+ e1e_flush();
udelay(500);
return 0;
@@ -1417,6 +1369,7 @@ static int e1000_set_82571_fiber_loopback(struct e1000_adapter *adapter)
*/
#define E1000_SERDES_LB_ON 0x410
ew32(SCTL, E1000_SERDES_LB_ON);
+ e1e_flush();
usleep_range(10000, 20000);
return 0;
@@ -1512,6 +1465,7 @@ static void e1000_loopback_cleanup(struct e1000_adapter *adapter)
hw->phy.media_type == e1000_media_type_internal_serdes) {
#define E1000_SERDES_LB_OFF 0x400
ew32(SCTL, E1000_SERDES_LB_OFF);
+ e1e_flush();
usleep_range(10000, 20000);
break;
}
@@ -1591,6 +1545,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
k = 0;
}
ew32(TDT, k);
+ e1e_flush();
msleep(200);
time = jiffies; /* set the start time for the receive */
good_cnt = 0;
@@ -2002,31 +1957,6 @@ static void e1000_get_strings(struct net_device *netdev, u32 stringset,
}
}
-static int e1000e_set_flags(struct net_device *netdev, u32 data)
-{
- struct e1000_adapter *adapter = netdev_priv(netdev);
- bool need_reset = false;
- int rc;
-
- need_reset = (data & ETH_FLAG_RXVLAN) !=
- (netdev->features & NETIF_F_HW_VLAN_RX);
-
- rc = ethtool_op_set_flags(netdev, data, ETH_FLAG_RXVLAN |
- ETH_FLAG_TXVLAN);
-
- if (rc)
- return rc;
-
- if (need_reset) {
- if (netif_running(netdev))
- e1000e_reinit_locked(adapter);
- else
- e1000e_reset(adapter);
- }
-
- return 0;
-}
-
static const struct ethtool_ops e1000_ethtool_ops = {
.get_settings = e1000_get_settings,
.set_settings = e1000_set_settings,
@@ -2046,14 +1976,6 @@ static const struct ethtool_ops e1000_ethtool_ops = {
.set_ringparam = e1000_set_ringparam,
.get_pauseparam = e1000_get_pauseparam,
.set_pauseparam = e1000_set_pauseparam,
- .get_rx_csum = e1000_get_rx_csum,
- .set_rx_csum = e1000_set_rx_csum,
- .get_tx_csum = e1000_get_tx_csum,
- .set_tx_csum = e1000_set_tx_csum,
- .get_sg = ethtool_op_get_sg,
- .set_sg = ethtool_op_set_sg,
- .get_tso = ethtool_op_get_tso,
- .set_tso = e1000_set_tso,
.self_test = e1000_diag_test,
.get_strings = e1000_get_strings,
.set_phys_id = e1000_set_phys_id,
@@ -2061,8 +1983,6 @@ static const struct ethtool_ops e1000_ethtool_ops = {
.get_sset_count = e1000e_get_sset_count,
.get_coalesce = e1000_get_coalesce,
.set_coalesce = e1000_set_coalesce,
- .get_flags = ethtool_op_get_flags,
- .set_flags = e1000e_set_flags,
};
void e1000e_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h
index 6c2fa8327f5c..29670397079b 100644
--- a/drivers/net/e1000e/hw.h
+++ b/drivers/net/ethernet/intel/e1000e/hw.h
@@ -246,6 +246,7 @@ enum e1e_registers {
#define BM_WUC_ENABLE_REG 17
#define BM_WUC_ENABLE_BIT (1 << 2)
#define BM_WUC_HOST_WU_BIT (1 << 4)
+#define BM_WUC_ME_WU_BIT (1 << 5)
#define BM_WUC PHY_REG(BM_WUC_PAGE, 1)
#define BM_WUFC PHY_REG(BM_WUC_PAGE, 2)
@@ -312,6 +313,7 @@ enum e1e_registers {
#define E1000_KMRNCTRLSTA_DIAG_OFFSET 0x3 /* Kumeran Diagnostic */
#define E1000_KMRNCTRLSTA_TIMEOUTS 0x4 /* Kumeran Timeouts */
#define E1000_KMRNCTRLSTA_INBAND_PARAM 0x9 /* Kumeran InBand Parameters */
+#define E1000_KMRNCTRLSTA_IBIST_DISABLE 0x0200 /* Kumeran IBIST Disable */
#define E1000_KMRNCTRLSTA_DIAG_NELPBK 0x1000 /* Nearend Loopback mode */
#define E1000_KMRNCTRLSTA_K1_CONFIG 0x7
#define E1000_KMRNCTRLSTA_K1_ENABLE 0x0002
@@ -777,7 +779,21 @@ struct e1000_mac_operations {
s32 (*read_mac_addr)(struct e1000_hw *);
};
-/* Function pointers for the PHY. */
+/*
+ * When to use various PHY register access functions:
+ *
+ * Func Caller
+ * Function Does Does When to use
+ * ~~~~~~~~~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * X_reg L,P,A n/a for simple PHY reg accesses
+ * X_reg_locked P,A L for multiple accesses of different regs
+ * on different pages
+ * X_reg_page A L,P for multiple accesses of different regs
+ * on the same page
+ *
+ * Where X=[read|write], L=locking, P=sets page, A=register access
+ *
+ */
struct e1000_phy_operations {
s32 (*acquire)(struct e1000_hw *);
s32 (*cfg_on_link_up)(struct e1000_hw *);
@@ -788,14 +804,17 @@ struct e1000_phy_operations {
s32 (*get_cfg_done)(struct e1000_hw *hw);
s32 (*get_cable_length)(struct e1000_hw *);
s32 (*get_info)(struct e1000_hw *);
+ s32 (*set_page)(struct e1000_hw *, u16);
s32 (*read_reg)(struct e1000_hw *, u32, u16 *);
s32 (*read_reg_locked)(struct e1000_hw *, u32, u16 *);
+ s32 (*read_reg_page)(struct e1000_hw *, u32, u16 *);
void (*release)(struct e1000_hw *);
s32 (*reset)(struct e1000_hw *);
s32 (*set_d0_lplu_state)(struct e1000_hw *, bool);
s32 (*set_d3_lplu_state)(struct e1000_hw *, bool);
s32 (*write_reg)(struct e1000_hw *, u32, u16);
s32 (*write_reg_locked)(struct e1000_hw *, u32, u16);
+ s32 (*write_reg_page)(struct e1000_hw *, u32, u16);
void (*power_up)(struct e1000_hw *);
void (*power_down)(struct e1000_hw *);
};
diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index 3369d1f6a39c..e2a80a283fd3 100644
--- a/drivers/net/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -137,8 +137,9 @@
#define HV_PM_CTRL PHY_REG(770, 17)
/* PHY Low Power Idle Control */
-#define I82579_LPI_CTRL PHY_REG(772, 20)
-#define I82579_LPI_CTRL_ENABLE_MASK 0x6000
+#define I82579_LPI_CTRL PHY_REG(772, 20)
+#define I82579_LPI_CTRL_ENABLE_MASK 0x6000
+#define I82579_LPI_CTRL_FORCE_PLL_LOCK_COUNT 0x80
/* EMI Registers */
#define I82579_EMI_ADDR 0x10
@@ -163,6 +164,11 @@
#define HV_KMRN_MODE_CTRL PHY_REG(769, 16)
#define HV_KMRN_MDIO_SLOW 0x0400
+/* KMRN FIFO Control and Status */
+#define HV_KMRN_FIFO_CTRLSTA PHY_REG(770, 16)
+#define HV_KMRN_FIFO_CTRLSTA_PREAMBLE_MASK 0x7000
+#define HV_KMRN_FIFO_CTRLSTA_PREAMBLE_SHIFT 12
+
/* ICH GbE Flash Hardware Sequencing Flash Status Register bit breakdown */
/* Offset 04h HSFSTS */
union ich8_hws_flash_status {
@@ -275,6 +281,20 @@ static inline void __ew32flash(struct e1000_hw *hw, unsigned long reg, u32 val)
#define ew16flash(reg,val) __ew16flash(hw, (reg), (val))
#define ew32flash(reg,val) __ew32flash(hw, (reg), (val))
+static void e1000_toggle_lanphypc_value_ich8lan(struct e1000_hw *hw)
+{
+ u32 ctrl;
+
+ ctrl = er32(CTRL);
+ ctrl |= E1000_CTRL_LANPHYPC_OVERRIDE;
+ ctrl &= ~E1000_CTRL_LANPHYPC_VALUE;
+ ew32(CTRL, ctrl);
+ e1e_flush();
+ udelay(10);
+ ctrl &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
+ ew32(CTRL, ctrl);
+}
+
/**
* e1000_init_phy_params_pchlan - Initialize PHY function pointers
* @hw: pointer to the HW structure
@@ -284,18 +304,21 @@ static inline void __ew32flash(struct e1000_hw *hw, unsigned long reg, u32 val)
static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
- u32 ctrl, fwsm;
+ u32 fwsm;
s32 ret_val = 0;
phy->addr = 1;
phy->reset_delay_us = 100;
+ phy->ops.set_page = e1000_set_page_igp;
phy->ops.read_reg = e1000_read_phy_reg_hv;
phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked;
+ phy->ops.read_reg_page = e1000_read_phy_reg_page_hv;
phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan;
phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan;
phy->ops.write_reg = e1000_write_phy_reg_hv;
phy->ops.write_reg_locked = e1000_write_phy_reg_hv_locked;
+ phy->ops.write_reg_page = e1000_write_phy_reg_page_hv;
phy->ops.power_up = e1000_power_up_phy_copper;
phy->ops.power_down = e1000_power_down_phy_copper_ich8lan;
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
@@ -308,13 +331,7 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw)
*/
fwsm = er32(FWSM);
if (!(fwsm & E1000_ICH_FWSM_FW_VALID) && !e1000_check_reset_block(hw)) {
- ctrl = er32(CTRL);
- ctrl |= E1000_CTRL_LANPHYPC_OVERRIDE;
- ctrl &= ~E1000_CTRL_LANPHYPC_VALUE;
- ew32(CTRL, ctrl);
- udelay(10);
- ctrl &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
- ew32(CTRL, ctrl);
+ e1000_toggle_lanphypc_value_ich8lan(hw);
msleep(50);
/*
@@ -646,6 +663,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
struct e1000_mac_info *mac = &hw->mac;
s32 ret_val;
bool link;
+ u16 phy_reg;
/*
* We only want to go out to the PHY registers to see if Auto-Neg
@@ -678,16 +696,35 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
mac->get_link_status = false;
- if (hw->phy.type == e1000_phy_82578) {
- ret_val = e1000_link_stall_workaround_hv(hw);
- if (ret_val)
- goto out;
- }
-
- if (hw->mac.type == e1000_pch2lan) {
+ switch (hw->mac.type) {
+ case e1000_pch2lan:
ret_val = e1000_k1_workaround_lv(hw);
if (ret_val)
goto out;
+ /* fall-thru */
+ case e1000_pchlan:
+ if (hw->phy.type == e1000_phy_82578) {
+ ret_val = e1000_link_stall_workaround_hv(hw);
+ if (ret_val)
+ goto out;
+ }
+
+ /*
+ * Workaround for PCHx parts in half-duplex:
+ * Set the number of preambles removed from the packet
+ * when it is passed from the PHY to the MAC to prevent
+ * the MAC from misinterpreting the packet type.
+ */
+ e1e_rphy(hw, HV_KMRN_FIFO_CTRLSTA, &phy_reg);
+ phy_reg &= ~HV_KMRN_FIFO_CTRLSTA_PREAMBLE_MASK;
+
+ if ((er32(STATUS) & E1000_STATUS_FD) != E1000_STATUS_FD)
+ phy_reg |= (1 << HV_KMRN_FIFO_CTRLSTA_PREAMBLE_SHIFT);
+
+ e1e_wphy(hw, HV_KMRN_FIFO_CTRLSTA, phy_reg);
+ break;
+ default:
+ break;
}
/*
@@ -774,9 +811,14 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter)
}
if ((adapter->hw.mac.type == e1000_ich8lan) &&
- (adapter->hw.phy.type == e1000_phy_igp_3))
+ (adapter->hw.phy.type != e1000_phy_ife))
adapter->flags |= FLAG_LSC_GIG_SPEED_DROP;
+ /* Enable workaround for 82579 w/ ME enabled */
+ if ((adapter->hw.mac.type == e1000_pch2lan) &&
+ (er32(FWSM) & E1000_ICH_FWSM_FW_VALID))
+ adapter->flags2 |= FLAG2_PCIM2PCI_ARBITER_WA;
+
/* Disable EEE by default until IEEE802.3az spec is finalized */
if (adapter->flags2 & FLAG2_HAS_EEE)
adapter->hw.dev_spec.ich8lan.eee_disable = true;
@@ -810,8 +852,6 @@ static void e1000_release_nvm_ich8lan(struct e1000_hw *hw)
mutex_unlock(&nvm_mutex);
}
-static DEFINE_MUTEX(swflag_mutex);
-
/**
* e1000_acquire_swflag_ich8lan - Acquire software control flag
* @hw: pointer to the HW structure
@@ -824,7 +864,11 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
u32 extcnf_ctrl, timeout = PHY_CFG_TIMEOUT;
s32 ret_val = 0;
- mutex_lock(&swflag_mutex);
+ if (test_and_set_bit(__E1000_ACCESS_SHARED_RESOURCE,
+ &hw->adapter->state)) {
+ e_dbg("contention for Phy access\n");
+ return -E1000_ERR_PHY;
+ }
while (timeout) {
extcnf_ctrl = er32(EXTCNF_CTRL);
@@ -836,7 +880,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
}
if (!timeout) {
- e_dbg("SW/FW/HW has locked the resource for too long.\n");
+ e_dbg("SW has already locked the resource.\n");
ret_val = -E1000_ERR_CONFIG;
goto out;
}
@@ -856,7 +900,9 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
}
if (!timeout) {
- e_dbg("Failed to acquire the semaphore.\n");
+ e_dbg("Failed to acquire the semaphore, FW or HW has it: "
+ "FWSM=0x%8.8x EXTCNF_CTRL=0x%8.8x)\n",
+ er32(FWSM), extcnf_ctrl);
extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
ew32(EXTCNF_CTRL, extcnf_ctrl);
ret_val = -E1000_ERR_CONFIG;
@@ -865,7 +911,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
out:
if (ret_val)
- mutex_unlock(&swflag_mutex);
+ clear_bit(__E1000_ACCESS_SHARED_RESOURCE, &hw->adapter->state);
return ret_val;
}
@@ -882,10 +928,15 @@ static void e1000_release_swflag_ich8lan(struct e1000_hw *hw)
u32 extcnf_ctrl;
extcnf_ctrl = er32(EXTCNF_CTRL);
- extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
- ew32(EXTCNF_CTRL, extcnf_ctrl);
- mutex_unlock(&swflag_mutex);
+ if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG) {
+ extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
+ ew32(EXTCNF_CTRL, extcnf_ctrl);
+ } else {
+ e_dbg("Semaphore unexpectedly released by sw/fw/hw\n");
+ }
+
+ clear_bit(__E1000_ACCESS_SHARED_RESOURCE, &hw->adapter->state);
}
/**
@@ -1215,9 +1266,11 @@ s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable)
ew32(CTRL, reg);
ew32(CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_SPD_BYPS);
+ e1e_flush();
udelay(20);
ew32(CTRL, ctrl_reg);
ew32(CTRL_EXT, ctrl_ext);
+ e1e_flush();
udelay(20);
out:
@@ -1270,16 +1323,20 @@ static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state)
if (mac_reg & E1000_PHY_CTRL_D0A_LPLU)
oem_reg |= HV_OEM_BITS_LPLU;
+
+ /* Set Restart auto-neg to activate the bits */
+ if (!e1000_check_reset_block(hw))
+ oem_reg |= HV_OEM_BITS_RESTART_AN;
} else {
- if (mac_reg & E1000_PHY_CTRL_NOND0A_GBE_DISABLE)
+ if (mac_reg & (E1000_PHY_CTRL_GBE_DISABLE |
+ E1000_PHY_CTRL_NOND0A_GBE_DISABLE))
oem_reg |= HV_OEM_BITS_GBE_DIS;
- if (mac_reg & E1000_PHY_CTRL_NOND0A_LPLU)
+ if (mac_reg & (E1000_PHY_CTRL_D0A_LPLU |
+ E1000_PHY_CTRL_NOND0A_LPLU))
oem_reg |= HV_OEM_BITS_LPLU;
}
- /* Restart auto-neg to activate the bits */
- if (!e1000_check_reset_block(hw))
- oem_reg |= HV_OEM_BITS_RESTART_AN;
+
ret_val = hw->phy.ops.write_reg_locked(hw, HV_OEM_BITS, oem_reg);
out:
@@ -1337,7 +1394,7 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
return ret_val;
/* Preamble tuning for SSC */
- ret_val = e1e_wphy(hw, PHY_REG(770, 16), 0xA204);
+ ret_val = e1e_wphy(hw, HV_KMRN_FIFO_CTRLSTA, 0xA204);
if (ret_val)
return ret_val;
}
@@ -1376,14 +1433,11 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
goto out;
- ret_val = hw->phy.ops.read_reg_locked(hw,
- PHY_REG(BM_PORT_CTRL_PAGE, 17),
- &phy_data);
+ ret_val = hw->phy.ops.read_reg_locked(hw, BM_PORT_GEN_CFG, &phy_data);
if (ret_val)
goto release;
- ret_val = hw->phy.ops.write_reg_locked(hw,
- PHY_REG(BM_PORT_CTRL_PAGE, 17),
- phy_data & 0x00FF);
+ ret_val = hw->phy.ops.write_reg_locked(hw, BM_PORT_GEN_CFG,
+ phy_data & 0x00FF);
release:
hw->phy.ops.release(hw);
out:
@@ -1397,17 +1451,36 @@ out:
void e1000_copy_rx_addrs_to_phy_ich8lan(struct e1000_hw *hw)
{
u32 mac_reg;
- u16 i;
+ u16 i, phy_reg = 0;
+ s32 ret_val;
+
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return;
+ ret_val = e1000_enable_phy_wakeup_reg_access_bm(hw, &phy_reg);
+ if (ret_val)
+ goto release;
/* Copy both RAL/H (rar_entry_count) and SHRAL/H (+4) to PHY */
for (i = 0; i < (hw->mac.rar_entry_count + 4); i++) {
mac_reg = er32(RAL(i));
- e1e_wphy(hw, BM_RAR_L(i), (u16)(mac_reg & 0xFFFF));
- e1e_wphy(hw, BM_RAR_M(i), (u16)((mac_reg >> 16) & 0xFFFF));
+ hw->phy.ops.write_reg_page(hw, BM_RAR_L(i),
+ (u16)(mac_reg & 0xFFFF));
+ hw->phy.ops.write_reg_page(hw, BM_RAR_M(i),
+ (u16)((mac_reg >> 16) & 0xFFFF));
+
mac_reg = er32(RAH(i));
- e1e_wphy(hw, BM_RAR_H(i), (u16)(mac_reg & 0xFFFF));
- e1e_wphy(hw, BM_RAR_CTRL(i), (u16)((mac_reg >> 16) & 0x8000));
+ hw->phy.ops.write_reg_page(hw, BM_RAR_H(i),
+ (u16)(mac_reg & 0xFFFF));
+ hw->phy.ops.write_reg_page(hw, BM_RAR_CTRL(i),
+ (u16)((mac_reg & E1000_RAH_AV)
+ >> 16));
}
+
+ e1000_disable_phy_wakeup_reg_access_bm(hw, &phy_reg);
+
+release:
+ hw->phy.ops.release(hw);
}
/**
@@ -1509,7 +1582,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
ret_val = e1e_wphy(hw, PHY_REG(776, 20), data);
if (ret_val)
goto out;
- ret_val = e1e_wphy(hw, PHY_REG(776, 23), 0xFE00);
+ ret_val = e1e_wphy(hw, PHY_REG(776, 23), 0xF100);
if (ret_val)
goto out;
e1e_rphy(hw, HV_PM_CTRL, &data);
@@ -1611,6 +1684,7 @@ static s32 e1000_k1_workaround_lv(struct e1000_hw *hw)
s32 ret_val = 0;
u16 status_reg = 0;
u32 mac_reg;
+ u16 phy_reg;
if (hw->mac.type != e1000_pch2lan)
goto out;
@@ -1625,12 +1699,19 @@ static s32 e1000_k1_workaround_lv(struct e1000_hw *hw)
mac_reg = er32(FEXTNVM4);
mac_reg &= ~E1000_FEXTNVM4_BEACON_DURATION_MASK;
- if (status_reg & HV_M_STATUS_SPEED_1000)
+ ret_val = e1e_rphy(hw, I82579_LPI_CTRL, &phy_reg);
+ if (ret_val)
+ goto out;
+
+ if (status_reg & HV_M_STATUS_SPEED_1000) {
mac_reg |= E1000_FEXTNVM4_BEACON_DURATION_8USEC;
- else
+ phy_reg &= ~I82579_LPI_CTRL_FORCE_PLL_LOCK_COUNT;
+ } else {
mac_reg |= E1000_FEXTNVM4_BEACON_DURATION_16USEC;
-
+ phy_reg |= I82579_LPI_CTRL_FORCE_PLL_LOCK_COUNT;
+ }
ew32(FEXTNVM4, mac_reg);
+ ret_val = e1e_wphy(hw, I82579_LPI_CTRL, phy_reg);
}
out:
@@ -1726,9 +1807,12 @@ static s32 e1000_post_phy_reset_ich8lan(struct e1000_hw *hw)
break;
}
- /* Dummy read to clear the phy wakeup bit after lcd reset */
- if (hw->mac.type >= e1000_pchlan)
- e1e_rphy(hw, BM_WUC, &reg);
+ /* Clear the host wakeup bit after lcd reset */
+ if (hw->mac.type >= e1000_pchlan) {
+ e1e_rphy(hw, BM_PORT_GEN_CFG, &reg);
+ reg &= ~BM_WUC_HOST_WU_BIT;
+ e1e_wphy(hw, BM_PORT_GEN_CFG, reg);
+ }
/* Configure the LCD with the extended configuration region in NVM */
ret_val = e1000_sw_lcd_config_ich8lan(hw);
@@ -2100,8 +2184,7 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
ret_val = 0;
for (i = 0; i < words; i++) {
- if ((dev_spec->shadow_ram) &&
- (dev_spec->shadow_ram[offset+i].modified)) {
+ if (dev_spec->shadow_ram[offset+i].modified) {
data[i] = dev_spec->shadow_ram[offset+i].value;
} else {
ret_val = e1000_read_flash_word_ich8lan(hw,
@@ -3056,10 +3139,11 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
ret_val = e1000_acquire_swflag_ich8lan(hw);
e_dbg("Issuing a global reset to ich8lan\n");
ew32(CTRL, (ctrl | E1000_CTRL_RST));
+ /* cannot issue a flush here because it hangs the hardware */
msleep(20);
if (!ret_val)
- e1000_release_swflag_ich8lan(hw);
+ clear_bit(__E1000_ACCESS_SHARED_RESOURCE, &hw->adapter->state);
if (ctrl & E1000_CTRL_PHY_RST) {
ret_val = hw->phy.ops.get_cfg_done(hw);
@@ -3127,11 +3211,13 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
/*
* The 82578 Rx buffer will stall if wakeup is enabled in host and
- * the ME. Reading the BM_WUC register will clear the host wakeup bit.
+ * the ME. Disable wakeup by clearing the host wakeup bit.
* Reset the phy after disabling host wakeup to reset the Rx buffer.
*/
if (hw->phy.type == e1000_phy_82578) {
- e1e_rphy(hw, BM_WUC, &i);
+ e1e_rphy(hw, BM_PORT_GEN_CFG, &i);
+ i &= ~BM_WUC_HOST_WU_BIT;
+ e1e_wphy(hw, BM_PORT_GEN_CFG, i);
ret_val = e1000_phy_hw_reset_ich8lan(hw);
if (ret_val)
return ret_val;
@@ -3560,15 +3646,14 @@ void e1000e_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw)
* LPLU, Gig disable, MDIC PHY reset):
* 1) Set Kumeran Near-end loopback
* 2) Clear Kumeran Near-end loopback
- * Should only be called for ICH8[m] devices with IGP_3 Phy.
+ * Should only be called for ICH8[m] devices with any 1G Phy.
**/
void e1000e_gig_downshift_workaround_ich8lan(struct e1000_hw *hw)
{
s32 ret_val;
u16 reg_data;
- if ((hw->mac.type != e1000_ich8lan) ||
- (hw->phy.type != e1000_phy_igp_3))
+ if ((hw->mac.type != e1000_ich8lan) || (hw->phy.type == e1000_phy_ife))
return;
ret_val = e1000e_read_kmrn_reg(hw, E1000_KMRNCTRLSTA_DIAG_OFFSET,
@@ -3586,17 +3671,16 @@ void e1000e_gig_downshift_workaround_ich8lan(struct e1000_hw *hw)
}
/**
- * e1000e_disable_gig_wol_ich8lan - disable gig during WoL
+ * e1000_suspend_workarounds_ich8lan - workarounds needed during S0->Sx
* @hw: pointer to the HW structure
*
* During S0 to Sx transition, it is possible the link remains at gig
* instead of negotiating to a lower speed. Before going to Sx, set
* 'LPLU Enabled' and 'Gig Disable' to force link speed negotiation
- * to a lower speed.
- *
- * Should only be called for applicable parts.
+ * to a lower speed. For PCH and newer parts, the OEM bits PHY register
+ * (LED, GbE disable and LPLU configurations) also needs to be written.
**/
-void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw)
+void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw)
{
u32 phy_ctrl;
s32 ret_val;
@@ -3605,8 +3689,12 @@ void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw)
phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU | E1000_PHY_CTRL_GBE_DISABLE;
ew32(PHY_CTRL, phy_ctrl);
+ if (hw->mac.type == e1000_ich8lan)
+ e1000e_gig_downshift_workaround_ich8lan(hw);
+
if (hw->mac.type >= e1000_pchlan) {
e1000_oem_bits_config_ich8lan(hw, false);
+ e1000_phy_hw_reset_ich8lan(hw);
ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return;
@@ -3616,6 +3704,60 @@ void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw)
}
/**
+ * e1000_resume_workarounds_pchlan - workarounds needed during Sx->S0
+ * @hw: pointer to the HW structure
+ *
+ * During Sx to S0 transitions on non-managed devices or managed devices
+ * on which PHY resets are not blocked, if the PHY registers cannot be
+ * accessed properly by the s/w toggle the LANPHYPC value to power cycle
+ * the PHY.
+ **/
+void e1000_resume_workarounds_pchlan(struct e1000_hw *hw)
+{
+ u32 fwsm;
+
+ if (hw->mac.type != e1000_pch2lan)
+ return;
+
+ fwsm = er32(FWSM);
+ if (!(fwsm & E1000_ICH_FWSM_FW_VALID) || !e1000_check_reset_block(hw)) {
+ u16 phy_id1, phy_id2;
+ s32 ret_val;
+
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val) {
+ e_dbg("Failed to acquire PHY semaphore in resume\n");
+ return;
+ }
+
+ /* Test access to the PHY registers by reading the ID regs */
+ ret_val = hw->phy.ops.read_reg_locked(hw, PHY_ID1, &phy_id1);
+ if (ret_val)
+ goto release;
+ ret_val = hw->phy.ops.read_reg_locked(hw, PHY_ID2, &phy_id2);
+ if (ret_val)
+ goto release;
+
+ if (hw->phy.id == ((u32)(phy_id1 << 16) |
+ (u32)(phy_id2 & PHY_REVISION_MASK)))
+ goto release;
+
+ e1000_toggle_lanphypc_value_ich8lan(hw);
+
+ hw->phy.ops.release(hw);
+ msleep(50);
+ e1000_phy_hw_reset(hw);
+ msleep(50);
+ return;
+ }
+
+release:
+ hw->phy.ops.release(hw);
+
+ return;
+}
+
+/**
* e1000_cleanup_led_ich8lan - Restore the default LED operation
* @hw: pointer to the HW structure
*
@@ -3832,6 +3974,7 @@ static void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw)
static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw)
{
u16 phy_data;
+ s32 ret_val;
e1000e_clear_hw_cntrs_base(hw);
@@ -3853,24 +3996,33 @@ static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw)
if ((hw->phy.type == e1000_phy_82578) ||
(hw->phy.type == e1000_phy_82579) ||
(hw->phy.type == e1000_phy_82577)) {
- e1e_rphy(hw, HV_SCC_UPPER, &phy_data);
- e1e_rphy(hw, HV_SCC_LOWER, &phy_data);
- e1e_rphy(hw, HV_ECOL_UPPER, &phy_data);
- e1e_rphy(hw, HV_ECOL_LOWER, &phy_data);
- e1e_rphy(hw, HV_MCC_UPPER, &phy_data);
- e1e_rphy(hw, HV_MCC_LOWER, &phy_data);
- e1e_rphy(hw, HV_LATECOL_UPPER, &phy_data);
- e1e_rphy(hw, HV_LATECOL_LOWER, &phy_data);
- e1e_rphy(hw, HV_COLC_UPPER, &phy_data);
- e1e_rphy(hw, HV_COLC_LOWER, &phy_data);
- e1e_rphy(hw, HV_DC_UPPER, &phy_data);
- e1e_rphy(hw, HV_DC_LOWER, &phy_data);
- e1e_rphy(hw, HV_TNCRS_UPPER, &phy_data);
- e1e_rphy(hw, HV_TNCRS_LOWER, &phy_data);
- }
-}
-
-static struct e1000_mac_operations ich8_mac_ops = {
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return;
+ ret_val = hw->phy.ops.set_page(hw,
+ HV_STATS_PAGE << IGP_PAGE_SHIFT);
+ if (ret_val)
+ goto release;
+ hw->phy.ops.read_reg_page(hw, HV_SCC_UPPER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_SCC_LOWER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_ECOL_UPPER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_ECOL_LOWER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_MCC_UPPER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_MCC_LOWER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_LATECOL_UPPER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_LATECOL_LOWER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_COLC_UPPER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_COLC_LOWER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_DC_UPPER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_DC_LOWER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_TNCRS_UPPER, &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_TNCRS_LOWER, &phy_data);
+release:
+ hw->phy.ops.release(hw);
+ }
+}
+
+static const struct e1000_mac_operations ich8_mac_ops = {
.id_led_init = e1000e_id_led_init,
/* check_mng_mode dependent on mac type */
.check_for_link = e1000_check_for_copper_link_ich8lan,
@@ -3889,7 +4041,7 @@ static struct e1000_mac_operations ich8_mac_ops = {
/* id_led_init dependent on mac type */
};
-static struct e1000_phy_operations ich8_phy_ops = {
+static const struct e1000_phy_operations ich8_phy_ops = {
.acquire = e1000_acquire_swflag_ich8lan,
.check_reset_block = e1000_check_reset_block_ich8lan,
.commit = NULL,
@@ -3903,7 +4055,7 @@ static struct e1000_phy_operations ich8_phy_ops = {
.write_reg = e1000e_write_phy_reg_igp,
};
-static struct e1000_nvm_operations ich8_nvm_ops = {
+static const struct e1000_nvm_operations ich8_nvm_ops = {
.acquire = e1000_acquire_nvm_ich8lan,
.read = e1000_read_nvm_ich8lan,
.release = e1000_release_nvm_ich8lan,
@@ -3913,11 +4065,10 @@ static struct e1000_nvm_operations ich8_nvm_ops = {
.write = e1000_write_nvm_ich8lan,
};
-struct e1000_info e1000_ich8_info = {
+const struct e1000_info e1000_ich8_info = {
.mac = e1000_ich8lan,
.flags = FLAG_HAS_WOL
| FLAG_IS_ICH
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_CTRLEXT_ON_LOAD
| FLAG_HAS_AMT
| FLAG_HAS_FLASH
@@ -3930,12 +4081,11 @@ struct e1000_info e1000_ich8_info = {
.nvm_ops = &ich8_nvm_ops,
};
-struct e1000_info e1000_ich9_info = {
+const struct e1000_info e1000_ich9_info = {
.mac = e1000_ich9lan,
.flags = FLAG_HAS_JUMBO_FRAMES
| FLAG_IS_ICH
| FLAG_HAS_WOL
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_CTRLEXT_ON_LOAD
| FLAG_HAS_AMT
| FLAG_HAS_ERT
@@ -3949,12 +4099,11 @@ struct e1000_info e1000_ich9_info = {
.nvm_ops = &ich8_nvm_ops,
};
-struct e1000_info e1000_ich10_info = {
+const struct e1000_info e1000_ich10_info = {
.mac = e1000_ich10lan,
.flags = FLAG_HAS_JUMBO_FRAMES
| FLAG_IS_ICH
| FLAG_HAS_WOL
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_CTRLEXT_ON_LOAD
| FLAG_HAS_AMT
| FLAG_HAS_ERT
@@ -3968,11 +4117,10 @@ struct e1000_info e1000_ich10_info = {
.nvm_ops = &ich8_nvm_ops,
};
-struct e1000_info e1000_pch_info = {
+const struct e1000_info e1000_pch_info = {
.mac = e1000_pchlan,
.flags = FLAG_IS_ICH
| FLAG_HAS_WOL
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_CTRLEXT_ON_LOAD
| FLAG_HAS_AMT
| FLAG_HAS_FLASH
@@ -3988,11 +4136,10 @@ struct e1000_info e1000_pch_info = {
.nvm_ops = &ich8_nvm_ops,
};
-struct e1000_info e1000_pch2_info = {
+const struct e1000_info e1000_pch2_info = {
.mac = e1000_pch2lan,
.flags = FLAG_IS_ICH
| FLAG_HAS_WOL
- | FLAG_RX_CSUM_ENABLED
| FLAG_HAS_CTRLEXT_ON_LOAD
| FLAG_HAS_AMT
| FLAG_HAS_FLASH
diff --git a/drivers/net/e1000e/lib.c b/drivers/net/ethernet/intel/e1000e/lib.c
index dd8ab05b5590..0893ab107adf 100644
--- a/drivers/net/e1000e/lib.c
+++ b/drivers/net/ethernet/intel/e1000e/lib.c
@@ -56,7 +56,7 @@ s32 e1000e_get_bus_info_pcie(struct e1000_hw *hw)
struct e1000_adapter *adapter = hw->adapter;
u16 pcie_link_status, cap_offset;
- cap_offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP);
+ cap_offset = adapter->pdev->pcie_cap;
if (!cap_offset) {
bus->width = e1000_bus_width_unknown;
} else {
@@ -190,7 +190,8 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw)
/* Check for LOM (vs. NIC) or one of two valid mezzanine cards */
if (!((nvm_data & NVM_COMPAT_LOM) ||
(hw->adapter->pdev->device == E1000_DEV_ID_82571EB_SERDES_DUAL) ||
- (hw->adapter->pdev->device == E1000_DEV_ID_82571EB_SERDES_QUAD)))
+ (hw->adapter->pdev->device == E1000_DEV_ID_82571EB_SERDES_QUAD) ||
+ (hw->adapter->pdev->device == E1000_DEV_ID_82571EB_SERDES)))
goto out;
ret_val = e1000_read_nvm(hw, NVM_ALT_MAC_ADDR_PTR, 1,
@@ -200,10 +201,10 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw)
goto out;
}
- if (nvm_alt_mac_addr_offset == 0xFFFF) {
+ if ((nvm_alt_mac_addr_offset == 0xFFFF) ||
+ (nvm_alt_mac_addr_offset == 0x0000))
/* There is no Alternate MAC Address */
goto out;
- }
if (hw->bus.func == E1000_FUNC_1)
nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN1;
@@ -220,7 +221,7 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw)
}
/* if multicast bit is set, the alternate address will not be used */
- if (alt_mac_addr[0] & 0x01) {
+ if (is_multicast_ether_addr(alt_mac_addr)) {
e_dbg("Ignoring Alternate Mac Address with MC bit set\n");
goto out;
}
@@ -1986,6 +1987,7 @@ static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw)
/* Clear SK and CS */
eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
ew32(EECD, eecd);
+ e1e_flush();
udelay(1);
/*
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 3310c3d477d7..a855db1ad249 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -36,6 +36,7 @@
#include <linux/pagemap.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
+#include <linux/interrupt.h>
#include <linux/tcp.h>
#include <linux/ipv6.h>
#include <linux/slab.h>
@@ -46,16 +47,16 @@
#include <linux/if_vlan.h>
#include <linux/cpu.h>
#include <linux/smp.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
#include <linux/aer.h>
#include <linux/prefetch.h>
#include "e1000.h"
-#define DRV_EXTRAVERSION "-k2"
+#define DRV_EXTRAVERSION "-k"
-#define DRV_VERSION "1.3.10" DRV_EXTRAVERSION
+#define DRV_VERSION "1.5.1" DRV_EXTRAVERSION
char e1000e_driver_name[] = "e1000e";
const char e1000e_driver_version[] = DRV_VERSION;
@@ -191,7 +192,7 @@ static void e1000e_dump(struct e1000_adapter *adapter)
struct e1000_buffer *buffer_info;
struct e1000_ring *rx_ring = adapter->rx_ring;
union e1000_rx_desc_packet_split *rx_desc_ps;
- struct e1000_rx_desc *rx_desc;
+ union e1000_rx_desc_extended *rx_desc;
struct my_u1 {
u64 a;
u64 b;
@@ -398,41 +399,70 @@ rx_ring_summary:
break;
default:
case 0:
- /* Legacy Receive Descriptor Format
+ /* Extended Receive Descriptor (Read) Format
*
- * +-----------------------------------------------------+
- * | Buffer Address [63:0] |
- * +-----------------------------------------------------+
- * | VLAN Tag | Errors | Status 0 | Packet csum | Length |
- * +-----------------------------------------------------+
- * 63 48 47 40 39 32 31 16 15 0
+ * +-----------------------------------------------------+
+ * 0 | Buffer Address [63:0] |
+ * +-----------------------------------------------------+
+ * 8 | Reserved |
+ * +-----------------------------------------------------+
*/
- printk(KERN_INFO "Rl[desc] [address 63:0 ] "
- "[vl er S cks ln] [bi->dma ] [bi->skb] "
- "<-- Legacy format\n");
- for (i = 0; rx_ring->desc && (i < rx_ring->count); i++) {
- rx_desc = E1000_RX_DESC(*rx_ring, i);
+ printk(KERN_INFO "R [desc] [buf addr 63:0 ] "
+ "[reserved 63:0 ] [bi->dma ] "
+ "[bi->skb] <-- Ext (Read) format\n");
+ /* Extended Receive Descriptor (Write-Back) Format
+ *
+ * 63 48 47 32 31 24 23 4 3 0
+ * +------------------------------------------------------+
+ * | RSS Hash | | | |
+ * 0 +-------------------+ Rsvd | Reserved | MRQ RSS |
+ * | Packet | IP | | | Type |
+ * | Checksum | Ident | | | |
+ * +------------------------------------------------------+
+ * 8 | VLAN Tag | Length | Extended Error | Extended Status |
+ * +------------------------------------------------------+
+ * 63 48 47 32 31 20 19 0
+ */
+ printk(KERN_INFO "RWB[desc] [cs ipid mrq] "
+ "[vt ln xe xs] "
+ "[bi->skb] <-- Ext (Write-Back) format\n");
+
+ for (i = 0; i < rx_ring->count; i++) {
buffer_info = &rx_ring->buffer_info[i];
- u0 = (struct my_u0 *)rx_desc;
- printk(KERN_INFO "Rl[0x%03X] %016llX %016llX "
- "%016llX %p", i,
- (unsigned long long)le64_to_cpu(u0->a),
- (unsigned long long)le64_to_cpu(u0->b),
- (unsigned long long)buffer_info->dma,
- buffer_info->skb);
+ rx_desc = E1000_RX_DESC_EXT(*rx_ring, i);
+ u1 = (struct my_u1 *)rx_desc;
+ staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
+ if (staterr & E1000_RXD_STAT_DD) {
+ /* Descriptor Done */
+ printk(KERN_INFO "RWB[0x%03X] %016llX "
+ "%016llX ---------------- %p", i,
+ (unsigned long long)le64_to_cpu(u1->a),
+ (unsigned long long)le64_to_cpu(u1->b),
+ buffer_info->skb);
+ } else {
+ printk(KERN_INFO "R [0x%03X] %016llX "
+ "%016llX %016llX %p", i,
+ (unsigned long long)le64_to_cpu(u1->a),
+ (unsigned long long)le64_to_cpu(u1->b),
+ (unsigned long long)buffer_info->dma,
+ buffer_info->skb);
+
+ if (netif_msg_pktdata(adapter))
+ print_hex_dump(KERN_INFO, "",
+ DUMP_PREFIX_ADDRESS, 16,
+ 1,
+ phys_to_virt
+ (buffer_info->dma),
+ adapter->rx_buffer_len,
+ true);
+ }
+
if (i == rx_ring->next_to_use)
printk(KERN_CONT " NTU\n");
else if (i == rx_ring->next_to_clean)
printk(KERN_CONT " NTC\n");
else
printk(KERN_CONT "\n");
-
- if (netif_msg_pktdata(adapter))
- print_hex_dump(KERN_INFO, "",
- DUMP_PREFIX_ADDRESS,
- 16, 1,
- phys_to_virt(buffer_info->dma),
- adapter->rx_buffer_len, true);
}
}
@@ -518,16 +548,73 @@ static void e1000_rx_checksum(struct e1000_adapter *adapter, u32 status_err,
}
/**
- * e1000_alloc_rx_buffers - Replace used receive buffers; legacy & extended
+ * e1000e_update_tail_wa - helper function for e1000e_update_[rt]dt_wa()
+ * @hw: pointer to the HW structure
+ * @tail: address of tail descriptor register
+ * @i: value to write to tail descriptor register
+ *
+ * When updating the tail register, the ME could be accessing Host CSR
+ * registers at the same time. Normally, this is handled in h/w by an
+ * arbiter but on some parts there is a bug that acknowledges Host accesses
+ * later than it should which could result in the descriptor register to
+ * have an incorrect value. Workaround this by checking the FWSM register
+ * which has bit 24 set while ME is accessing Host CSR registers, wait
+ * if it is set and try again a number of times.
+ **/
+static inline s32 e1000e_update_tail_wa(struct e1000_hw *hw, u8 __iomem * tail,
+ unsigned int i)
+{
+ unsigned int j = 0;
+
+ while ((j++ < E1000_ICH_FWSM_PCIM2PCI_COUNT) &&
+ (er32(FWSM) & E1000_ICH_FWSM_PCIM2PCI))
+ udelay(50);
+
+ writel(i, tail);
+
+ if ((j == E1000_ICH_FWSM_PCIM2PCI_COUNT) && (i != readl(tail)))
+ return E1000_ERR_SWFW_SYNC;
+
+ return 0;
+}
+
+static void e1000e_update_rdt_wa(struct e1000_adapter *adapter, unsigned int i)
+{
+ u8 __iomem *tail = (adapter->hw.hw_addr + adapter->rx_ring->tail);
+ struct e1000_hw *hw = &adapter->hw;
+
+ if (e1000e_update_tail_wa(hw, tail, i)) {
+ u32 rctl = er32(RCTL);
+ ew32(RCTL, rctl & ~E1000_RCTL_EN);
+ e_err("ME firmware caused invalid RDT - resetting\n");
+ schedule_work(&adapter->reset_task);
+ }
+}
+
+static void e1000e_update_tdt_wa(struct e1000_adapter *adapter, unsigned int i)
+{
+ u8 __iomem *tail = (adapter->hw.hw_addr + adapter->tx_ring->tail);
+ struct e1000_hw *hw = &adapter->hw;
+
+ if (e1000e_update_tail_wa(hw, tail, i)) {
+ u32 tctl = er32(TCTL);
+ ew32(TCTL, tctl & ~E1000_TCTL_EN);
+ e_err("ME firmware caused invalid TDT - resetting\n");
+ schedule_work(&adapter->reset_task);
+ }
+}
+
+/**
+ * e1000_alloc_rx_buffers - Replace used receive buffers
* @adapter: address of board private structure
**/
static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter,
- int cleaned_count)
+ int cleaned_count, gfp_t gfp)
{
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
struct e1000_ring *rx_ring = adapter->rx_ring;
- struct e1000_rx_desc *rx_desc;
+ union e1000_rx_desc_extended *rx_desc;
struct e1000_buffer *buffer_info;
struct sk_buff *skb;
unsigned int i;
@@ -543,7 +630,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter,
goto map_skb;
}
- skb = netdev_alloc_skb_ip_align(netdev, bufsz);
+ skb = __netdev_alloc_skb_ip_align(netdev, bufsz, gfp);
if (!skb) {
/* Better luck next round */
adapter->alloc_rx_buff_failed++;
@@ -561,8 +648,8 @@ map_skb:
break;
}
- rx_desc = E1000_RX_DESC(*rx_ring, i);
- rx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
+ rx_desc = E1000_RX_DESC_EXT(*rx_ring, i);
+ rx_desc->read.buffer_addr = cpu_to_le64(buffer_info->dma);
if (unlikely(!(i & (E1000_RX_BUFFER_WRITE - 1)))) {
/*
@@ -572,7 +659,10 @@ map_skb:
* such as IA-64).
*/
wmb();
- writel(i, adapter->hw.hw_addr + rx_ring->tail);
+ if (adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
+ e1000e_update_rdt_wa(adapter, i);
+ else
+ writel(i, adapter->hw.hw_addr + rx_ring->tail);
}
i++;
if (i == rx_ring->count)
@@ -588,7 +678,7 @@ map_skb:
* @adapter: address of board private structure
**/
static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter,
- int cleaned_count)
+ int cleaned_count, gfp_t gfp)
{
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
@@ -614,7 +704,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter,
continue;
}
if (!ps_page->page) {
- ps_page->page = alloc_page(GFP_ATOMIC);
+ ps_page->page = alloc_page(gfp);
if (!ps_page->page) {
adapter->alloc_rx_buff_failed++;
goto no_buffers;
@@ -640,8 +730,9 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter,
cpu_to_le64(ps_page->dma);
}
- skb = netdev_alloc_skb_ip_align(netdev,
- adapter->rx_ps_bsize0);
+ skb = __netdev_alloc_skb_ip_align(netdev,
+ adapter->rx_ps_bsize0,
+ gfp);
if (!skb) {
adapter->alloc_rx_buff_failed++;
@@ -671,7 +762,11 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter,
* such as IA-64).
*/
wmb();
- writel(i << 1, adapter->hw.hw_addr + rx_ring->tail);
+ if (adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
+ e1000e_update_rdt_wa(adapter, i << 1);
+ else
+ writel(i << 1,
+ adapter->hw.hw_addr + rx_ring->tail);
}
i++;
@@ -691,11 +786,11 @@ no_buffers:
**/
static void e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter,
- int cleaned_count)
+ int cleaned_count, gfp_t gfp)
{
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
- struct e1000_rx_desc *rx_desc;
+ union e1000_rx_desc_extended *rx_desc;
struct e1000_ring *rx_ring = adapter->rx_ring;
struct e1000_buffer *buffer_info;
struct sk_buff *skb;
@@ -712,7 +807,7 @@ static void e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter,
goto check_page;
}
- skb = netdev_alloc_skb_ip_align(netdev, bufsz);
+ skb = __netdev_alloc_skb_ip_align(netdev, bufsz, gfp);
if (unlikely(!skb)) {
/* Better luck next round */
adapter->alloc_rx_buff_failed++;
@@ -723,7 +818,7 @@ static void e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter,
check_page:
/* allocate a new page if necessary */
if (!buffer_info->page) {
- buffer_info->page = alloc_page(GFP_ATOMIC);
+ buffer_info->page = alloc_page(gfp);
if (unlikely(!buffer_info->page)) {
adapter->alloc_rx_buff_failed++;
break;
@@ -736,8 +831,8 @@ check_page:
PAGE_SIZE,
DMA_FROM_DEVICE);
- rx_desc = E1000_RX_DESC(*rx_ring, i);
- rx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
+ rx_desc = E1000_RX_DESC_EXT(*rx_ring, i);
+ rx_desc->read.buffer_addr = cpu_to_le64(buffer_info->dma);
if (unlikely(++i == rx_ring->count))
i = 0;
@@ -754,7 +849,10 @@ check_page:
* applicable for weak-ordered memory model archs,
* such as IA-64). */
wmb();
- writel(i, adapter->hw.hw_addr + rx_ring->tail);
+ if (adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
+ e1000e_update_rdt_wa(adapter, i);
+ else
+ writel(i, adapter->hw.hw_addr + rx_ring->tail);
}
}
@@ -772,28 +870,27 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
struct pci_dev *pdev = adapter->pdev;
struct e1000_hw *hw = &adapter->hw;
struct e1000_ring *rx_ring = adapter->rx_ring;
- struct e1000_rx_desc *rx_desc, *next_rxd;
+ union e1000_rx_desc_extended *rx_desc, *next_rxd;
struct e1000_buffer *buffer_info, *next_buffer;
- u32 length;
+ u32 length, staterr;
unsigned int i;
int cleaned_count = 0;
bool cleaned = 0;
unsigned int total_rx_bytes = 0, total_rx_packets = 0;
i = rx_ring->next_to_clean;
- rx_desc = E1000_RX_DESC(*rx_ring, i);
+ rx_desc = E1000_RX_DESC_EXT(*rx_ring, i);
+ staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
buffer_info = &rx_ring->buffer_info[i];
- while (rx_desc->status & E1000_RXD_STAT_DD) {
+ while (staterr & E1000_RXD_STAT_DD) {
struct sk_buff *skb;
- u8 status;
if (*work_done >= work_to_do)
break;
(*work_done)++;
rmb(); /* read descriptor and rx_buffer_info after status DD */
- status = rx_desc->status;
skb = buffer_info->skb;
buffer_info->skb = NULL;
@@ -802,7 +899,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
i++;
if (i == rx_ring->count)
i = 0;
- next_rxd = E1000_RX_DESC(*rx_ring, i);
+ next_rxd = E1000_RX_DESC_EXT(*rx_ring, i);
prefetch(next_rxd);
next_buffer = &rx_ring->buffer_info[i];
@@ -815,7 +912,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
DMA_FROM_DEVICE);
buffer_info->dma = 0;
- length = le16_to_cpu(rx_desc->length);
+ length = le16_to_cpu(rx_desc->wb.upper.length);
/*
* !EOP means multiple descriptors were used to store a single
@@ -824,7 +921,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
* next frame that _does_ have the EOP bit set, as it is by
* definition only a frame fragment
*/
- if (unlikely(!(status & E1000_RXD_STAT_EOP)))
+ if (unlikely(!(staterr & E1000_RXD_STAT_EOP)))
adapter->flags2 |= FLAG2_IS_DISCARDING;
if (adapter->flags2 & FLAG2_IS_DISCARDING) {
@@ -832,12 +929,12 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
e_dbg("Receive packet consumed multiple buffers\n");
/* recycle */
buffer_info->skb = skb;
- if (status & E1000_RXD_STAT_EOP)
+ if (staterr & E1000_RXD_STAT_EOP)
adapter->flags2 &= ~FLAG2_IS_DISCARDING;
goto next_desc;
}
- if (rx_desc->errors & E1000_RXD_ERR_FRAME_ERR_MASK) {
+ if (staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK) {
/* recycle */
buffer_info->skb = skb;
goto next_desc;
@@ -875,31 +972,34 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
skb_put(skb, length);
/* Receive Checksum Offload */
- e1000_rx_checksum(adapter,
- (u32)(status) |
- ((u32)(rx_desc->errors) << 24),
- le16_to_cpu(rx_desc->csum), skb);
+ e1000_rx_checksum(adapter, staterr,
+ le16_to_cpu(rx_desc->wb.lower.hi_dword.
+ csum_ip.csum), skb);
- e1000_receive_skb(adapter, netdev, skb,status,rx_desc->special);
+ e1000_receive_skb(adapter, netdev, skb, staterr,
+ rx_desc->wb.upper.vlan);
next_desc:
- rx_desc->status = 0;
+ rx_desc->wb.upper.status_error &= cpu_to_le32(~0xFF);
/* return some buffers to hardware, one at a time is too slow */
if (cleaned_count >= E1000_RX_BUFFER_WRITE) {
- adapter->alloc_rx_buf(adapter, cleaned_count);
+ adapter->alloc_rx_buf(adapter, cleaned_count,
+ GFP_ATOMIC);
cleaned_count = 0;
}
/* use prefetched values */
rx_desc = next_rxd;
buffer_info = next_buffer;
+
+ staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
}
rx_ring->next_to_clean = i;
cleaned_count = e1000_desc_unused(rx_ring);
if (cleaned_count)
- adapter->alloc_rx_buf(adapter, cleaned_count);
+ adapter->alloc_rx_buf(adapter, cleaned_count, GFP_ATOMIC);
adapter->total_rx_bytes += total_rx_bytes;
adapter->total_rx_packets += total_rx_packets;
@@ -1200,7 +1300,7 @@ static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter,
ps_page->page = NULL;
skb->len += length;
skb->data_len += length;
- skb->truesize += length;
+ skb->truesize += PAGE_SIZE;
}
/* strip the ethernet crc, problem is we're using pages now so
@@ -1229,7 +1329,8 @@ next_desc:
/* return some buffers to hardware, one at a time is too slow */
if (cleaned_count >= E1000_RX_BUFFER_WRITE) {
- adapter->alloc_rx_buf(adapter, cleaned_count);
+ adapter->alloc_rx_buf(adapter, cleaned_count,
+ GFP_ATOMIC);
cleaned_count = 0;
}
@@ -1243,7 +1344,7 @@ next_desc:
cleaned_count = e1000_desc_unused(rx_ring);
if (cleaned_count)
- adapter->alloc_rx_buf(adapter, cleaned_count);
+ adapter->alloc_rx_buf(adapter, cleaned_count, GFP_ATOMIC);
adapter->total_rx_bytes += total_rx_bytes;
adapter->total_rx_packets += total_rx_packets;
@@ -1259,7 +1360,7 @@ static void e1000_consume_page(struct e1000_buffer *bi, struct sk_buff *skb,
bi->page = NULL;
skb->len += length;
skb->data_len += length;
- skb->truesize += length;
+ skb->truesize += PAGE_SIZE;
}
/**
@@ -1276,35 +1377,34 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
struct e1000_ring *rx_ring = adapter->rx_ring;
- struct e1000_rx_desc *rx_desc, *next_rxd;
+ union e1000_rx_desc_extended *rx_desc, *next_rxd;
struct e1000_buffer *buffer_info, *next_buffer;
- u32 length;
+ u32 length, staterr;
unsigned int i;
int cleaned_count = 0;
bool cleaned = false;
unsigned int total_rx_bytes=0, total_rx_packets=0;
i = rx_ring->next_to_clean;
- rx_desc = E1000_RX_DESC(*rx_ring, i);
+ rx_desc = E1000_RX_DESC_EXT(*rx_ring, i);
+ staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
buffer_info = &rx_ring->buffer_info[i];
- while (rx_desc->status & E1000_RXD_STAT_DD) {
+ while (staterr & E1000_RXD_STAT_DD) {
struct sk_buff *skb;
- u8 status;
if (*work_done >= work_to_do)
break;
(*work_done)++;
rmb(); /* read descriptor and rx_buffer_info after status DD */
- status = rx_desc->status;
skb = buffer_info->skb;
buffer_info->skb = NULL;
++i;
if (i == rx_ring->count)
i = 0;
- next_rxd = E1000_RX_DESC(*rx_ring, i);
+ next_rxd = E1000_RX_DESC_EXT(*rx_ring, i);
prefetch(next_rxd);
next_buffer = &rx_ring->buffer_info[i];
@@ -1315,23 +1415,22 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
DMA_FROM_DEVICE);
buffer_info->dma = 0;
- length = le16_to_cpu(rx_desc->length);
+ length = le16_to_cpu(rx_desc->wb.upper.length);
/* errors is only valid for DD + EOP descriptors */
- if (unlikely((status & E1000_RXD_STAT_EOP) &&
- (rx_desc->errors & E1000_RXD_ERR_FRAME_ERR_MASK))) {
- /* recycle both page and skb */
- buffer_info->skb = skb;
- /* an error means any chain goes out the window
- * too */
- if (rx_ring->rx_skb_top)
- dev_kfree_skb_irq(rx_ring->rx_skb_top);
- rx_ring->rx_skb_top = NULL;
- goto next_desc;
+ if (unlikely((staterr & E1000_RXD_STAT_EOP) &&
+ (staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK))) {
+ /* recycle both page and skb */
+ buffer_info->skb = skb;
+ /* an error means any chain goes out the window too */
+ if (rx_ring->rx_skb_top)
+ dev_kfree_skb_irq(rx_ring->rx_skb_top);
+ rx_ring->rx_skb_top = NULL;
+ goto next_desc;
}
#define rxtop (rx_ring->rx_skb_top)
- if (!(status & E1000_RXD_STAT_EOP)) {
+ if (!(staterr & E1000_RXD_STAT_EOP)) {
/* this descriptor is only the beginning (or middle) */
if (!rxtop) {
/* this is the beginning of a chain */
@@ -1386,10 +1485,9 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
}
/* Receive Checksum Offload XXX recompute due to CRC strip? */
- e1000_rx_checksum(adapter,
- (u32)(status) |
- ((u32)(rx_desc->errors) << 24),
- le16_to_cpu(rx_desc->csum), skb);
+ e1000_rx_checksum(adapter, staterr,
+ le16_to_cpu(rx_desc->wb.lower.hi_dword.
+ csum_ip.csum), skb);
/* probably a little skewed due to removing CRC */
total_rx_bytes += skb->len;
@@ -1402,27 +1500,30 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
goto next_desc;
}
- e1000_receive_skb(adapter, netdev, skb, status,
- rx_desc->special);
+ e1000_receive_skb(adapter, netdev, skb, staterr,
+ rx_desc->wb.upper.vlan);
next_desc:
- rx_desc->status = 0;
+ rx_desc->wb.upper.status_error &= cpu_to_le32(~0xFF);
/* return some buffers to hardware, one at a time is too slow */
if (unlikely(cleaned_count >= E1000_RX_BUFFER_WRITE)) {
- adapter->alloc_rx_buf(adapter, cleaned_count);
+ adapter->alloc_rx_buf(adapter, cleaned_count,
+ GFP_ATOMIC);
cleaned_count = 0;
}
/* use prefetched values */
rx_desc = next_rxd;
buffer_info = next_buffer;
+
+ staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
}
rx_ring->next_to_clean = i;
cleaned_count = e1000_desc_unused(rx_ring);
if (cleaned_count)
- adapter->alloc_rx_buf(adapter, cleaned_count);
+ adapter->alloc_rx_buf(adapter, cleaned_count, GFP_ATOMIC);
adapter->total_rx_bytes += total_rx_bytes;
adapter->total_rx_packets += total_rx_packets;
@@ -2815,6 +2916,10 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
break;
}
+ /* Enable Extended Status in all Receive Descriptors */
+ rfctl = er32(RFCTL);
+ rfctl |= E1000_RFCTL_EXTEN;
+
/*
* 82571 and greater support packet-split where the protocol
* header is placed in skb->data and the packet data is
@@ -2840,9 +2945,6 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
if (adapter->rx_ps_pages) {
u32 psrctl = 0;
- /* Configure extra packet-split registers */
- rfctl = er32(RFCTL);
- rfctl |= E1000_RFCTL_EXTEN;
/*
* disable packet split support for IPv6 extension headers,
* because some malformed IPv6 headers can hang the Rx
@@ -2850,8 +2952,6 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
rfctl |= (E1000_RFCTL_IPV6_EX_DIS |
E1000_RFCTL_NEW_IPV6_EXT_DIS);
- ew32(RFCTL, rfctl);
-
/* Enable Packet split descriptors */
rctl |= E1000_RCTL_DTYP_PS;
@@ -2874,6 +2974,7 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
ew32(PSRCTL, psrctl);
}
+ ew32(RFCTL, rfctl);
ew32(RCTL, rctl);
/* just started the receive unit, no need to restart */
adapter->flags &= ~FLAG_RX_RESTART_NOW;
@@ -2899,18 +3000,19 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
adapter->clean_rx = e1000_clean_rx_irq_ps;
adapter->alloc_rx_buf = e1000_alloc_rx_buffers_ps;
} else if (adapter->netdev->mtu > ETH_FRAME_LEN + ETH_FCS_LEN) {
- rdlen = rx_ring->count * sizeof(struct e1000_rx_desc);
+ rdlen = rx_ring->count * sizeof(union e1000_rx_desc_extended);
adapter->clean_rx = e1000_clean_jumbo_rx_irq;
adapter->alloc_rx_buf = e1000_alloc_jumbo_rx_buffers;
} else {
- rdlen = rx_ring->count * sizeof(struct e1000_rx_desc);
+ rdlen = rx_ring->count * sizeof(union e1000_rx_desc_extended);
adapter->clean_rx = e1000_clean_rx_irq;
adapter->alloc_rx_buf = e1000_alloc_rx_buffers;
}
/* disable receives while setting up the descriptors */
rctl = er32(RCTL);
- ew32(RCTL, rctl & ~E1000_RCTL_EN);
+ if (!(adapter->flags2 & FLAG2_NO_DISABLE_RX))
+ ew32(RCTL, rctl & ~E1000_RCTL_EN);
e1e_flush();
usleep_range(10000, 20000);
@@ -2967,7 +3069,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
/* Enable Receive Checksum Offload for TCP and UDP */
rxcsum = er32(RXCSUM);
- if (adapter->flags & FLAG_RX_CSUM_ENABLED) {
+ if (adapter->netdev->features & NETIF_F_RXCSUM) {
rxcsum |= E1000_RXCSUM_TUOFL;
/*
@@ -3104,7 +3206,8 @@ static void e1000_configure(struct e1000_adapter *adapter)
e1000_configure_tx(adapter);
e1000_setup_rctl(adapter);
e1000_configure_rx(adapter);
- adapter->alloc_rx_buf(adapter, e1000_desc_unused(adapter->rx_ring));
+ adapter->alloc_rx_buf(adapter, e1000_desc_unused(adapter->rx_ring),
+ GFP_KERNEL);
}
/**
@@ -3346,7 +3449,7 @@ int e1000e_up(struct e1000_adapter *adapter)
e1000_configure_msix(adapter);
e1000_irq_enable(adapter);
- netif_wake_queue(adapter->netdev);
+ netif_start_queue(adapter->netdev);
/* fire a link change interrupt to start the watchdog */
if (adapter->msix_entries)
@@ -3388,7 +3491,8 @@ void e1000e_down(struct e1000_adapter *adapter)
/* disable receives in the hardware */
rctl = er32(RCTL);
- ew32(RCTL, rctl & ~E1000_RCTL_EN);
+ if (!(adapter->flags2 & FLAG2_NO_DISABLE_RX))
+ ew32(RCTL, rctl & ~E1000_RCTL_EN);
/* flush and sleep below */
netif_stop_queue(netdev);
@@ -3397,6 +3501,7 @@ void e1000e_down(struct e1000_adapter *adapter)
tctl = er32(TCTL);
tctl &= ~E1000_TCTL_EN;
ew32(TCTL, tctl);
+
/* flush both disables and wait for them to finish */
e1e_flush();
usleep_range(10000, 20000);
@@ -3413,17 +3518,16 @@ void e1000e_down(struct e1000_adapter *adapter)
e1000e_update_stats(adapter);
spin_unlock(&adapter->stats64_lock);
+ e1000e_flush_descriptors(adapter);
+ e1000_clean_tx_ring(adapter);
+ e1000_clean_rx_ring(adapter);
+
adapter->link_speed = 0;
adapter->link_duplex = 0;
if (!pci_channel_offline(adapter->pdev))
e1000e_reset(adapter);
- e1000e_flush_descriptors(adapter);
-
- e1000_clean_tx_ring(adapter);
- e1000_clean_rx_ring(adapter);
-
/*
* TODO: for power management, we could drop the link and
* pci_disable_device here.
@@ -3833,6 +3937,8 @@ static void e1000_update_phy_info(unsigned long data)
/**
* e1000e_update_phy_stats - Update the PHY statistics counters
* @adapter: board private structure
+ *
+ * Read/clear the upper 16-bit PHY registers and read/accumulate lower
**/
static void e1000e_update_phy_stats(struct e1000_adapter *adapter)
{
@@ -3844,89 +3950,61 @@ static void e1000e_update_phy_stats(struct e1000_adapter *adapter)
if (ret_val)
return;
- hw->phy.addr = 1;
-
-#define HV_PHY_STATS_PAGE 778
/*
* A page set is expensive so check if already on desired page.
* If not, set to the page with the PHY status registers.
*/
+ hw->phy.addr = 1;
ret_val = e1000e_read_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
&phy_data);
if (ret_val)
goto release;
- if (phy_data != (HV_PHY_STATS_PAGE << IGP_PAGE_SHIFT)) {
- ret_val = e1000e_write_phy_reg_mdic(hw,
- IGP01E1000_PHY_PAGE_SELECT,
- (HV_PHY_STATS_PAGE <<
- IGP_PAGE_SHIFT));
+ if (phy_data != (HV_STATS_PAGE << IGP_PAGE_SHIFT)) {
+ ret_val = hw->phy.ops.set_page(hw,
+ HV_STATS_PAGE << IGP_PAGE_SHIFT);
if (ret_val)
goto release;
}
- /* Read/clear the upper 16-bit registers and read/accumulate lower */
-
/* Single Collision Count */
- e1000e_read_phy_reg_mdic(hw, HV_SCC_UPPER & MAX_PHY_REG_ADDRESS,
- &phy_data);
- ret_val = e1000e_read_phy_reg_mdic(hw,
- HV_SCC_LOWER & MAX_PHY_REG_ADDRESS,
- &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_SCC_UPPER, &phy_data);
+ ret_val = hw->phy.ops.read_reg_page(hw, HV_SCC_LOWER, &phy_data);
if (!ret_val)
adapter->stats.scc += phy_data;
/* Excessive Collision Count */
- e1000e_read_phy_reg_mdic(hw, HV_ECOL_UPPER & MAX_PHY_REG_ADDRESS,
- &phy_data);
- ret_val = e1000e_read_phy_reg_mdic(hw,
- HV_ECOL_LOWER & MAX_PHY_REG_ADDRESS,
- &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_ECOL_UPPER, &phy_data);
+ ret_val = hw->phy.ops.read_reg_page(hw, HV_ECOL_LOWER, &phy_data);
if (!ret_val)
adapter->stats.ecol += phy_data;
/* Multiple Collision Count */
- e1000e_read_phy_reg_mdic(hw, HV_MCC_UPPER & MAX_PHY_REG_ADDRESS,
- &phy_data);
- ret_val = e1000e_read_phy_reg_mdic(hw,
- HV_MCC_LOWER & MAX_PHY_REG_ADDRESS,
- &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_MCC_UPPER, &phy_data);
+ ret_val = hw->phy.ops.read_reg_page(hw, HV_MCC_LOWER, &phy_data);
if (!ret_val)
adapter->stats.mcc += phy_data;
/* Late Collision Count */
- e1000e_read_phy_reg_mdic(hw, HV_LATECOL_UPPER & MAX_PHY_REG_ADDRESS,
- &phy_data);
- ret_val = e1000e_read_phy_reg_mdic(hw,
- HV_LATECOL_LOWER &
- MAX_PHY_REG_ADDRESS,
- &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_LATECOL_UPPER, &phy_data);
+ ret_val = hw->phy.ops.read_reg_page(hw, HV_LATECOL_LOWER, &phy_data);
if (!ret_val)
adapter->stats.latecol += phy_data;
/* Collision Count - also used for adaptive IFS */
- e1000e_read_phy_reg_mdic(hw, HV_COLC_UPPER & MAX_PHY_REG_ADDRESS,
- &phy_data);
- ret_val = e1000e_read_phy_reg_mdic(hw,
- HV_COLC_LOWER & MAX_PHY_REG_ADDRESS,
- &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_COLC_UPPER, &phy_data);
+ ret_val = hw->phy.ops.read_reg_page(hw, HV_COLC_LOWER, &phy_data);
if (!ret_val)
hw->mac.collision_delta = phy_data;
/* Defer Count */
- e1000e_read_phy_reg_mdic(hw, HV_DC_UPPER & MAX_PHY_REG_ADDRESS,
- &phy_data);
- ret_val = e1000e_read_phy_reg_mdic(hw,
- HV_DC_LOWER & MAX_PHY_REG_ADDRESS,
- &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_DC_UPPER, &phy_data);
+ ret_val = hw->phy.ops.read_reg_page(hw, HV_DC_LOWER, &phy_data);
if (!ret_val)
adapter->stats.dc += phy_data;
/* Transmit with no CRS */
- e1000e_read_phy_reg_mdic(hw, HV_TNCRS_UPPER & MAX_PHY_REG_ADDRESS,
- &phy_data);
- ret_val = e1000e_read_phy_reg_mdic(hw,
- HV_TNCRS_LOWER & MAX_PHY_REG_ADDRESS,
- &phy_data);
+ hw->phy.ops.read_reg_page(hw, HV_TNCRS_UPPER, &phy_data);
+ ret_val = hw->phy.ops.read_reg_page(hw, HV_TNCRS_LOWER, &phy_data);
if (!ret_val)
adapter->stats.tncrs += phy_data;
@@ -4595,11 +4673,11 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
}
for (f = 0; f < nr_frags; f++) {
- struct skb_frag_struct *frag;
+ const struct skb_frag_struct *frag;
frag = &skb_shinfo(skb)->frags[f];
- len = frag->size;
- offset = frag->page_offset;
+ len = skb_frag_size(frag);
+ offset = 0;
while (len) {
i++;
@@ -4612,9 +4690,8 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
buffer_info->length = size;
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- buffer_info->dma = dma_map_page(&pdev->dev, frag->page,
- offset, size,
- DMA_TO_DEVICE);
+ buffer_info->dma = skb_frag_dma_map(&pdev->dev, frag,
+ offset, size, DMA_TO_DEVICE);
buffer_info->mapped_as_page = true;
if (dma_mapping_error(&pdev->dev, buffer_info->dma))
goto dma_error;
@@ -4707,7 +4784,12 @@ static void e1000_tx_queue(struct e1000_adapter *adapter,
wmb();
tx_ring->next_to_use = i;
- writel(i, adapter->hw.hw_addr + tx_ring->tail);
+
+ if (adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
+ e1000e_update_tdt_wa(adapter, i);
+ else
+ writel(i, adapter->hw.hw_addr + tx_ring->tail);
+
/*
* we need this if more than one processor can write to our tail
* at a time, it synchronizes IO on IA64/Altix systems
@@ -4861,7 +4943,7 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
nr_frags = skb_shinfo(skb)->nr_frags;
for (f = 0; f < nr_frags; f++)
- count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size,
+ count += TXD_USE_COUNT(skb_frag_size(&skb_shinfo(skb)->frags[f]),
max_txd_pwr);
if (adapter->hw.mac.tx_pkt_filtering)
@@ -5154,21 +5236,34 @@ static int e1000_init_phy_wakeup(struct e1000_adapter *adapter, u32 wufc)
{
struct e1000_hw *hw = &adapter->hw;
u32 i, mac_reg;
- u16 phy_reg;
+ u16 phy_reg, wuc_enable;
int retval = 0;
/* copy MAC RARs to PHY RARs */
e1000_copy_rx_addrs_to_phy_ich8lan(hw);
- /* copy MAC MTA to PHY MTA */
+ retval = hw->phy.ops.acquire(hw);
+ if (retval) {
+ e_err("Could not acquire PHY\n");
+ return retval;
+ }
+
+ /* Enable access to wakeup registers on and set page to BM_WUC_PAGE */
+ retval = e1000_enable_phy_wakeup_reg_access_bm(hw, &wuc_enable);
+ if (retval)
+ goto out;
+
+ /* copy MAC MTA to PHY MTA - only needed for pchlan */
for (i = 0; i < adapter->hw.mac.mta_reg_count; i++) {
mac_reg = E1000_READ_REG_ARRAY(hw, E1000_MTA, i);
- e1e_wphy(hw, BM_MTA(i), (u16)(mac_reg & 0xFFFF));
- e1e_wphy(hw, BM_MTA(i) + 1, (u16)((mac_reg >> 16) & 0xFFFF));
+ hw->phy.ops.write_reg_page(hw, BM_MTA(i),
+ (u16)(mac_reg & 0xFFFF));
+ hw->phy.ops.write_reg_page(hw, BM_MTA(i) + 1,
+ (u16)((mac_reg >> 16) & 0xFFFF));
}
/* configure PHY Rx Control register */
- e1e_rphy(&adapter->hw, BM_RCTL, &phy_reg);
+ hw->phy.ops.read_reg_page(&adapter->hw, BM_RCTL, &phy_reg);
mac_reg = er32(RCTL);
if (mac_reg & E1000_RCTL_UPE)
phy_reg |= BM_RCTL_UPE;
@@ -5185,31 +5280,19 @@ static int e1000_init_phy_wakeup(struct e1000_adapter *adapter, u32 wufc)
mac_reg = er32(CTRL);
if (mac_reg & E1000_CTRL_RFCE)
phy_reg |= BM_RCTL_RFCE;
- e1e_wphy(&adapter->hw, BM_RCTL, phy_reg);
+ hw->phy.ops.write_reg_page(&adapter->hw, BM_RCTL, phy_reg);
/* enable PHY wakeup in MAC register */
ew32(WUFC, wufc);
ew32(WUC, E1000_WUC_PHY_WAKE | E1000_WUC_PME_EN);
/* configure and enable PHY wakeup in PHY registers */
- e1e_wphy(&adapter->hw, BM_WUFC, wufc);
- e1e_wphy(&adapter->hw, BM_WUC, E1000_WUC_PME_EN);
+ hw->phy.ops.write_reg_page(&adapter->hw, BM_WUFC, wufc);
+ hw->phy.ops.write_reg_page(&adapter->hw, BM_WUC, E1000_WUC_PME_EN);
/* activate PHY wakeup */
- retval = hw->phy.ops.acquire(hw);
- if (retval) {
- e_err("Could not acquire PHY\n");
- return retval;
- }
- e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
- (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT));
- retval = e1000e_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, &phy_reg);
- if (retval) {
- e_err("Could not read PHY page 769\n");
- goto out;
- }
- phy_reg |= BM_WUC_ENABLE_BIT | BM_WUC_HOST_WU_BIT;
- retval = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg);
+ wuc_enable |= BM_WUC_ENABLE_BIT | BM_WUC_HOST_WU_BIT;
+ retval = e1000_disable_phy_wakeup_reg_access_bm(hw, &wuc_enable);
if (retval)
e_err("Could not set PHY Host Wakeup bit\n");
out:
@@ -5277,7 +5360,7 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake,
}
if (adapter->flags & FLAG_IS_ICH)
- e1000e_disable_gig_wol_ich8lan(&adapter->hw);
+ e1000_suspend_workarounds_ich8lan(&adapter->hw);
/* Allow time for pending master requests to run */
e1000e_disable_pcie_master(&adapter->hw);
@@ -5343,7 +5426,7 @@ static void e1000_complete_shutdown(struct pci_dev *pdev, bool sleep,
*/
if (adapter->flags & FLAG_IS_QUAD_PORT) {
struct pci_dev *us_dev = pdev->bus->self;
- int pos = pci_find_capability(us_dev, PCI_CAP_ID_EXP);
+ int pos = pci_pcie_cap(us_dev);
u16 devctl;
pci_read_config_word(us_dev, pos + PCI_EXP_DEVCTL, &devctl);
@@ -5428,6 +5511,9 @@ static int __e1000_resume(struct pci_dev *pdev)
return err;
}
+ if (hw->mac.type == e1000_pch2lan)
+ e1000_resume_workarounds_pchlan(&adapter->hw);
+
e1000e_power_up_phy(adapter);
/* report the system wakeup cause from S3/S4 */
@@ -5773,12 +5859,32 @@ static void e1000_eeprom_checks(struct e1000_adapter *adapter)
}
}
+static int e1000_set_features(struct net_device *netdev, u32 features)
+{
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+ u32 changed = features ^ netdev->features;
+
+ if (changed & (NETIF_F_TSO | NETIF_F_TSO6))
+ adapter->flags |= FLAG_TSO_FORCE;
+
+ if (!(changed & (NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX |
+ NETIF_F_RXCSUM)))
+ return 0;
+
+ if (netif_running(netdev))
+ e1000e_reinit_locked(adapter);
+ else
+ e1000e_reset(adapter);
+
+ return 0;
+}
+
static const struct net_device_ops e1000e_netdev_ops = {
.ndo_open = e1000_open,
.ndo_stop = e1000_close,
.ndo_start_xmit = e1000_xmit_frame,
.ndo_get_stats64 = e1000e_get_stats64,
- .ndo_set_multicast_list = e1000_set_multi,
+ .ndo_set_rx_mode = e1000_set_multi,
.ndo_set_mac_address = e1000_set_mac,
.ndo_change_mtu = e1000_change_mtu,
.ndo_do_ioctl = e1000_ioctl,
@@ -5790,6 +5896,7 @@ static const struct net_device_ops e1000e_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = e1000_netpoll,
#endif
+ .ndo_set_features = e1000_set_features,
};
/**
@@ -5949,21 +6056,25 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
if (e1000_check_reset_block(&adapter->hw))
e_info("PHY reset is blocked due to SOL/IDER session.\n");
- netdev->features = NETIF_F_SG |
- NETIF_F_HW_CSUM |
- NETIF_F_HW_VLAN_TX |
- NETIF_F_HW_VLAN_RX;
+ /* Set initial default active device features */
+ netdev->features = (NETIF_F_SG |
+ NETIF_F_HW_VLAN_RX |
+ NETIF_F_HW_VLAN_TX |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_RXCSUM |
+ NETIF_F_HW_CSUM);
+
+ /* Set user-changeable features (subset of all device features) */
+ netdev->hw_features = netdev->features;
if (adapter->flags & FLAG_HAS_HW_VLAN_FILTER)
netdev->features |= NETIF_F_HW_VLAN_FILTER;
- netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
-
- netdev->vlan_features |= NETIF_F_TSO;
- netdev->vlan_features |= NETIF_F_TSO6;
- netdev->vlan_features |= NETIF_F_HW_CSUM;
- netdev->vlan_features |= NETIF_F_SG;
+ netdev->vlan_features |= (NETIF_F_SG |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_HW_CSUM);
if (pci_using_dac) {
netdev->features |= NETIF_F_HIGHDMA;
diff --git a/drivers/net/e1000e/param.c b/drivers/net/ethernet/intel/e1000e/param.c
index 4dd9b63273f6..20e93b08e7f3 100644
--- a/drivers/net/e1000e/param.c
+++ b/drivers/net/ethernet/intel/e1000e/param.c
@@ -27,6 +27,7 @@
*******************************************************************************/
#include <linux/netdevice.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include "e1000.h"
diff --git a/drivers/net/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c
index 484774c13c21..8666476cb9be 100644
--- a/drivers/net/e1000e/phy.c
+++ b/drivers/net/ethernet/intel/e1000e/phy.c
@@ -36,7 +36,7 @@ static s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active);
static s32 e1000_wait_autoneg(struct e1000_hw *hw);
static u32 e1000_get_phy_addr_for_bm_page(u32 page, u32 reg);
static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
- u16 *data, bool read);
+ u16 *data, bool read, bool page_set);
static u32 e1000_get_phy_addr_for_hv_page(u32 page);
static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
u16 *data, bool read);
@@ -348,6 +348,24 @@ s32 e1000e_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data)
}
/**
+ * e1000_set_page_igp - Set page as on IGP-like PHY(s)
+ * @hw: pointer to the HW structure
+ * @page: page to set (shifted left when necessary)
+ *
+ * Sets PHY page required for PHY register access. Assumes semaphore is
+ * already acquired. Note, this function sets phy.addr to 1 so the caller
+ * must set it appropriately (if necessary) after this function returns.
+ **/
+s32 e1000_set_page_igp(struct e1000_hw *hw, u16 page)
+{
+ e_dbg("Setting page 0x%x\n", page);
+
+ hw->phy.addr = 1;
+
+ return e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, page);
+}
+
+/**
* __e1000e_read_phy_reg_igp - Read igp PHY register
* @hw: pointer to the HW structure
* @offset: register offset to be read
@@ -519,6 +537,7 @@ static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data,
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
ew32(KMRNCTRLSTA, kmrnctrlsta);
+ e1e_flush();
udelay(2);
@@ -591,6 +610,7 @@ static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data,
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | data;
ew32(KMRNCTRLSTA, kmrnctrlsta);
+ e1e_flush();
udelay(2);
@@ -2418,7 +2438,7 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data,
- false);
+ false, false);
goto out;
}
@@ -2477,7 +2497,7 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data,
- true);
+ true, false);
goto out;
}
@@ -2535,7 +2555,7 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data)
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data,
- true);
+ true, false);
goto out;
}
@@ -2579,7 +2599,7 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data)
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data,
- false);
+ false, false);
goto out;
}
@@ -2603,104 +2623,163 @@ out:
}
/**
- * e1000_access_phy_wakeup_reg_bm - Read BM PHY wakeup register
+ * e1000_enable_phy_wakeup_reg_access_bm - enable access to BM wakeup registers
* @hw: pointer to the HW structure
- * @offset: register offset to be read or written
- * @data: pointer to the data to read or write
- * @read: determines if operation is read or write
- *
- * Acquires semaphore, if necessary, then reads the PHY register at offset
- * and storing the retrieved information in data. Release any acquired
- * semaphores before exiting. Note that procedure to read the wakeup
- * registers are different. It works as such:
- * 1) Set page 769, register 17, bit 2 = 1
- * 2) Set page to 800 for host (801 if we were manageability)
- * 3) Write the address using the address opcode (0x11)
- * 4) Read or write the data using the data opcode (0x12)
- * 5) Restore 769_17.2 to its original value
+ * @phy_reg: pointer to store original contents of BM_WUC_ENABLE_REG
*
- * Assumes semaphore already acquired.
+ * Assumes semaphore already acquired and phy_reg points to a valid memory
+ * address to store contents of the BM_WUC_ENABLE_REG register.
**/
-static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
- u16 *data, bool read)
+s32 e1000_enable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg)
{
s32 ret_val;
- u16 reg = BM_PHY_REG_NUM(offset);
- u16 phy_reg = 0;
-
- /* Gig must be disabled for MDIO accesses to page 800 */
- if ((hw->mac.type == e1000_pchlan) &&
- (!(er32(PHY_CTRL) & E1000_PHY_CTRL_GBE_DISABLE)))
- e_dbg("Attempting to access page 800 while gig enabled.\n");
+ u16 temp;
- /* All operations in this function are phy address 1 */
+ /* All page select, port ctrl and wakeup registers use phy address 1 */
hw->phy.addr = 1;
- /* Set page 769 */
- e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
- (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT));
+ /* Select Port Control Registers page */
+ ret_val = e1000_set_page_igp(hw, (BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT));
+ if (ret_val) {
+ e_dbg("Could not set Port Control page\n");
+ goto out;
+ }
- ret_val = e1000e_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, &phy_reg);
+ ret_val = e1000e_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg);
if (ret_val) {
- e_dbg("Could not read PHY page 769\n");
+ e_dbg("Could not read PHY register %d.%d\n",
+ BM_PORT_CTRL_PAGE, BM_WUC_ENABLE_REG);
goto out;
}
- /* First clear bit 4 to avoid a power state change */
- phy_reg &= ~(BM_WUC_HOST_WU_BIT);
- ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg);
+ /*
+ * Enable both PHY wakeup mode and Wakeup register page writes.
+ * Prevent a power state change by disabling ME and Host PHY wakeup.
+ */
+ temp = *phy_reg;
+ temp |= BM_WUC_ENABLE_BIT;
+ temp &= ~(BM_WUC_ME_WU_BIT | BM_WUC_HOST_WU_BIT);
+
+ ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, temp);
if (ret_val) {
- e_dbg("Could not clear PHY page 769 bit 4\n");
+ e_dbg("Could not write PHY register %d.%d\n",
+ BM_PORT_CTRL_PAGE, BM_WUC_ENABLE_REG);
goto out;
}
- /* Write bit 2 = 1, and clear bit 4 to 769_17 */
- ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG,
- phy_reg | BM_WUC_ENABLE_BIT);
+ /* Select Host Wakeup Registers page */
+ ret_val = e1000_set_page_igp(hw, (BM_WUC_PAGE << IGP_PAGE_SHIFT));
+
+ /* caller now able to write registers on the Wakeup registers page */
+out:
+ return ret_val;
+}
+
+/**
+ * e1000_disable_phy_wakeup_reg_access_bm - disable access to BM wakeup regs
+ * @hw: pointer to the HW structure
+ * @phy_reg: pointer to original contents of BM_WUC_ENABLE_REG
+ *
+ * Restore BM_WUC_ENABLE_REG to its original value.
+ *
+ * Assumes semaphore already acquired and *phy_reg is the contents of the
+ * BM_WUC_ENABLE_REG before register(s) on BM_WUC_PAGE were accessed by
+ * caller.
+ **/
+s32 e1000_disable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg)
+{
+ s32 ret_val = 0;
+
+ /* Select Port Control Registers page */
+ ret_val = e1000_set_page_igp(hw, (BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT));
if (ret_val) {
- e_dbg("Could not write PHY page 769 bit 2\n");
+ e_dbg("Could not set Port Control page\n");
goto out;
}
- /* Select page 800 */
- ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
- (BM_WUC_PAGE << IGP_PAGE_SHIFT));
+ /* Restore 769.17 to its original value */
+ ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, *phy_reg);
+ if (ret_val)
+ e_dbg("Could not restore PHY register %d.%d\n",
+ BM_PORT_CTRL_PAGE, BM_WUC_ENABLE_REG);
+out:
+ return ret_val;
+}
+
+/**
+ * e1000_access_phy_wakeup_reg_bm - Read/write BM PHY wakeup register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read or written
+ * @data: pointer to the data to read or write
+ * @read: determines if operation is read or write
+ * @page_set: BM_WUC_PAGE already set and access enabled
+ *
+ * Read the PHY register at offset and store the retrieved information in
+ * data, or write data to PHY register at offset. Note the procedure to
+ * access the PHY wakeup registers is different than reading the other PHY
+ * registers. It works as such:
+ * 1) Set 769.17.2 (page 769, register 17, bit 2) = 1
+ * 2) Set page to 800 for host (801 if we were manageability)
+ * 3) Write the address using the address opcode (0x11)
+ * 4) Read or write the data using the data opcode (0x12)
+ * 5) Restore 769.17.2 to its original value
+ *
+ * Steps 1 and 2 are done by e1000_enable_phy_wakeup_reg_access_bm() and
+ * step 5 is done by e1000_disable_phy_wakeup_reg_access_bm().
+ *
+ * Assumes semaphore is already acquired. When page_set==true, assumes
+ * the PHY page is set to BM_WUC_PAGE (i.e. a function in the call stack
+ * is responsible for calls to e1000_[enable|disable]_phy_wakeup_reg_bm()).
+ **/
+static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
+ u16 *data, bool read, bool page_set)
+{
+ s32 ret_val;
+ u16 reg = BM_PHY_REG_NUM(offset);
+ u16 page = BM_PHY_REG_PAGE(offset);
+ u16 phy_reg = 0;
+
+ /* Gig must be disabled for MDIO accesses to Host Wakeup reg page */
+ if ((hw->mac.type == e1000_pchlan) &&
+ (!(er32(PHY_CTRL) & E1000_PHY_CTRL_GBE_DISABLE)))
+ e_dbg("Attempting to access page %d while gig enabled.\n",
+ page);
- /* Write the page 800 offset value using opcode 0x11 */
+ if (!page_set) {
+ /* Enable access to PHY wakeup registers */
+ ret_val = e1000_enable_phy_wakeup_reg_access_bm(hw, &phy_reg);
+ if (ret_val) {
+ e_dbg("Could not enable PHY wakeup reg access\n");
+ goto out;
+ }
+ }
+
+ e_dbg("Accessing PHY page %d reg 0x%x\n", page, reg);
+
+ /* Write the Wakeup register page offset value using opcode 0x11 */
ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ADDRESS_OPCODE, reg);
if (ret_val) {
- e_dbg("Could not write address opcode to page 800\n");
+ e_dbg("Could not write address opcode to page %d\n", page);
goto out;
}
if (read) {
- /* Read the page 800 value using opcode 0x12 */
+ /* Read the Wakeup register page value using opcode 0x12 */
ret_val = e1000e_read_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE,
data);
} else {
- /* Write the page 800 value using opcode 0x12 */
+ /* Write the Wakeup register page value using opcode 0x12 */
ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE,
*data);
}
if (ret_val) {
- e_dbg("Could not access data value from page 800\n");
+ e_dbg("Could not access PHY reg %d.%d\n", page, reg);
goto out;
}
- /*
- * Restore 769_17.2 to its original value
- * Set page 769
- */
- e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
- (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT));
-
- /* Clear 769_17.2 */
- ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg);
- if (ret_val) {
- e_dbg("Could not clear PHY page 769 bit 2\n");
- goto out;
- }
+ if (!page_set)
+ ret_val = e1000_disable_phy_wakeup_reg_access_bm(hw, &phy_reg);
out:
return ret_val;
@@ -2792,11 +2871,12 @@ static s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active)
* semaphore before exiting.
**/
static s32 __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
- bool locked)
+ bool locked, bool page_set)
{
s32 ret_val;
u16 page = BM_PHY_REG_PAGE(offset);
u16 reg = BM_PHY_REG_NUM(offset);
+ u32 phy_addr = hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
if (!locked) {
ret_val = hw->phy.ops.acquire(hw);
@@ -2806,8 +2886,8 @@ static s32 __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
- ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset,
- data, true);
+ ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data,
+ true, page_set);
goto out;
}
@@ -2817,26 +2897,25 @@ static s32 __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
goto out;
}
- hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
+ if (!page_set) {
+ if (page == HV_INTC_FC_PAGE_START)
+ page = 0;
- if (page == HV_INTC_FC_PAGE_START)
- page = 0;
+ if (reg > MAX_PHY_MULTI_PAGE_REG) {
+ /* Page is shifted left, PHY expects (page x 32) */
+ ret_val = e1000_set_page_igp(hw,
+ (page << IGP_PAGE_SHIFT));
- if (reg > MAX_PHY_MULTI_PAGE_REG) {
- u32 phy_addr = hw->phy.addr;
+ hw->phy.addr = phy_addr;
- hw->phy.addr = 1;
-
- /* Page is shifted left, PHY expects (page x 32) */
- ret_val = e1000e_write_phy_reg_mdic(hw,
- IGP01E1000_PHY_PAGE_SELECT,
- (page << IGP_PAGE_SHIFT));
- hw->phy.addr = phy_addr;
-
- if (ret_val)
- goto out;
+ if (ret_val)
+ goto out;
+ }
}
+ e_dbg("reading PHY page %d (or 0x%x shifted) reg 0x%x\n", page,
+ page << IGP_PAGE_SHIFT, reg);
+
ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
data);
out:
@@ -2858,7 +2937,7 @@ out:
**/
s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
{
- return __e1000_read_phy_reg_hv(hw, offset, data, false);
+ return __e1000_read_phy_reg_hv(hw, offset, data, false, false);
}
/**
@@ -2872,7 +2951,21 @@ s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
**/
s32 e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 *data)
{
- return __e1000_read_phy_reg_hv(hw, offset, data, true);
+ return __e1000_read_phy_reg_hv(hw, offset, data, true, false);
+}
+
+/**
+ * e1000_read_phy_reg_page_hv - Read HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Reads the PHY register at offset and stores the retrieved information
+ * in data. Assumes semaphore already acquired and page already set.
+ **/
+s32 e1000_read_phy_reg_page_hv(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_phy_reg_hv(hw, offset, data, true, true);
}
/**
@@ -2886,11 +2979,12 @@ s32 e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 *data)
* at the offset. Release any acquired semaphores before exiting.
**/
static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
- bool locked)
+ bool locked, bool page_set)
{
s32 ret_val;
u16 page = BM_PHY_REG_PAGE(offset);
u16 reg = BM_PHY_REG_NUM(offset);
+ u32 phy_addr = hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
if (!locked) {
ret_val = hw->phy.ops.acquire(hw);
@@ -2900,8 +2994,8 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
- ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset,
- &data, false);
+ ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data,
+ false, page_set);
goto out;
}
@@ -2911,42 +3005,41 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
goto out;
}
- hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
-
- if (page == HV_INTC_FC_PAGE_START)
- page = 0;
-
- /*
- * Workaround MDIO accesses being disabled after entering IEEE Power
- * Down (whenever bit 11 of the PHY Control register is set)
- */
- if ((hw->phy.type == e1000_phy_82578) &&
- (hw->phy.revision >= 1) &&
- (hw->phy.addr == 2) &&
- ((MAX_PHY_REG_ADDRESS & reg) == 0) &&
- (data & (1 << 11))) {
- u16 data2 = 0x7EFF;
- ret_val = e1000_access_phy_debug_regs_hv(hw, (1 << 6) | 0x3,
- &data2, false);
- if (ret_val)
- goto out;
- }
+ if (!page_set) {
+ if (page == HV_INTC_FC_PAGE_START)
+ page = 0;
- if (reg > MAX_PHY_MULTI_PAGE_REG) {
- u32 phy_addr = hw->phy.addr;
+ /*
+ * Workaround MDIO accesses being disabled after entering IEEE
+ * Power Down (when bit 11 of the PHY Control register is set)
+ */
+ if ((hw->phy.type == e1000_phy_82578) &&
+ (hw->phy.revision >= 1) &&
+ (hw->phy.addr == 2) &&
+ ((MAX_PHY_REG_ADDRESS & reg) == 0) && (data & (1 << 11))) {
+ u16 data2 = 0x7EFF;
+ ret_val = e1000_access_phy_debug_regs_hv(hw,
+ (1 << 6) | 0x3,
+ &data2, false);
+ if (ret_val)
+ goto out;
+ }
- hw->phy.addr = 1;
+ if (reg > MAX_PHY_MULTI_PAGE_REG) {
+ /* Page is shifted left, PHY expects (page x 32) */
+ ret_val = e1000_set_page_igp(hw,
+ (page << IGP_PAGE_SHIFT));
- /* Page is shifted left, PHY expects (page x 32) */
- ret_val = e1000e_write_phy_reg_mdic(hw,
- IGP01E1000_PHY_PAGE_SELECT,
- (page << IGP_PAGE_SHIFT));
- hw->phy.addr = phy_addr;
+ hw->phy.addr = phy_addr;
- if (ret_val)
- goto out;
+ if (ret_val)
+ goto out;
+ }
}
+ e_dbg("writing PHY page %d (or 0x%x shifted) reg 0x%x\n", page,
+ page << IGP_PAGE_SHIFT, reg);
+
ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
data);
@@ -2968,7 +3061,7 @@ out:
**/
s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
{
- return __e1000_write_phy_reg_hv(hw, offset, data, false);
+ return __e1000_write_phy_reg_hv(hw, offset, data, false, false);
}
/**
@@ -2982,7 +3075,21 @@ s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
**/
s32 e1000_write_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 data)
{
- return __e1000_write_phy_reg_hv(hw, offset, data, true);
+ return __e1000_write_phy_reg_hv(hw, offset, data, true, false);
+}
+
+/**
+ * e1000_write_phy_reg_page_hv - Write HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Writes the data to PHY register at the offset. Assumes semaphore
+ * already acquired and page already set.
+ **/
+s32 e1000_write_phy_reg_page_hv(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_phy_reg_hv(hw, offset, data, true, true);
}
/**
@@ -3004,11 +3111,12 @@ static u32 e1000_get_phy_addr_for_hv_page(u32 page)
* @hw: pointer to the HW structure
* @offset: register offset to be read or written
* @data: pointer to the data to be read or written
- * @read: determines if operation is read or written
+ * @read: determines if operation is read or write
*
* Reads the PHY register at offset and stores the retreived information
* in data. Assumes semaphore already acquired. Note that the procedure
- * to read these regs uses the address port and data port to read/write.
+ * to access these regs uses the address port and data port to read/write.
+ * These accesses done with PHY address 2 and without using pages.
**/
static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
u16 *data, bool read)
@@ -3028,7 +3136,7 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
/* masking with 0x3F to remove the page from offset */
ret_val = e1000e_write_phy_reg_mdic(hw, addr_reg, (u16)offset & 0x3F);
if (ret_val) {
- e_dbg("Could not write PHY the HV address register\n");
+ e_dbg("Could not write the Address Offset port register\n");
goto out;
}
@@ -3039,7 +3147,7 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
ret_val = e1000e_write_phy_reg_mdic(hw, data_reg, *data);
if (ret_val) {
- e_dbg("Could not read data value from HV data register\n");
+ e_dbg("Could not access the Data port register\n");
goto out;
}
diff --git a/drivers/net/igb/Makefile b/drivers/net/ethernet/intel/igb/Makefile
index 8372cb9a8c1a..c6e4621b6262 100644
--- a/drivers/net/igb/Makefile
+++ b/drivers/net/ethernet/intel/igb/Makefile
@@ -1,7 +1,7 @@
################################################################################
#
# Intel 82575 PCI-Express Ethernet Linux driver
-# Copyright(c) 1999 - 2009 Intel Corporation.
+# Copyright(c) 1999 - 2011 Intel Corporation.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
diff --git a/drivers/net/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
index 0f563c8c5ffc..7881fb95a25b 100644
--- a/drivers/net/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -66,10 +66,6 @@ static s32 igb_set_pcie_completion_timeout(struct e1000_hw *hw);
static s32 igb_reset_mdicnfg_82580(struct e1000_hw *hw);
static s32 igb_validate_nvm_checksum_82580(struct e1000_hw *hw);
static s32 igb_update_nvm_checksum_82580(struct e1000_hw *hw);
-static s32 igb_update_nvm_checksum_with_offset(struct e1000_hw *hw,
- u16 offset);
-static s32 igb_validate_nvm_checksum_with_offset(struct e1000_hw *hw,
- u16 offset);
static s32 igb_validate_nvm_checksum_i350(struct e1000_hw *hw);
static s32 igb_update_nvm_checksum_i350(struct e1000_hw *hw);
static const u16 e1000_82580_rxpbs_table[] =
@@ -1055,7 +1051,10 @@ static s32 igb_init_hw_82575(struct e1000_hw *hw)
/* Disabling VLAN filtering */
hw_dbg("Initializing the IEEE VLAN\n");
- igb_clear_vfta(hw);
+ if (hw->mac.type == e1000_i350)
+ igb_clear_vfta_i350(hw);
+ else
+ igb_clear_vfta(hw);
/* Setup the receive address */
igb_init_rx_addrs(hw, rar_count);
@@ -1156,10 +1155,13 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
{
u32 ctrl_ext, ctrl_reg, reg;
bool pcs_autoneg;
+ s32 ret_val = E1000_SUCCESS;
+ u16 data;
if ((hw->phy.media_type != e1000_media_type_internal_serdes) &&
!igb_sgmii_active_82575(hw))
- return 0;
+ return ret_val;
+
/*
* On the 82575, SerDes loopback mode persists until it is
@@ -1203,6 +1205,18 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
/* disable PCS autoneg and support parallel detect only */
pcs_autoneg = false;
default:
+ if (hw->mac.type == e1000_82575 ||
+ hw->mac.type == e1000_82576) {
+ ret_val = hw->nvm.ops.read(hw, NVM_COMPAT, 1, &data);
+ if (ret_val) {
+ printk(KERN_DEBUG "NVM Read Error\n\n");
+ return ret_val;
+ }
+
+ if (data & E1000_EEPROM_PCS_AUTONEG_DISABLE_BIT)
+ pcs_autoneg = false;
+ }
+
/*
* non-SGMII modes only supports a speed of 1000/Full for the
* link so it is best to just force the MAC and let the pcs
@@ -1250,7 +1264,7 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
if (!igb_sgmii_active_82575(hw))
igb_force_mac_fc(hw);
- return 0;
+ return ret_val;
}
/**
@@ -1569,14 +1583,31 @@ void igb_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf)
**/
void igb_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable)
{
- u32 dtxswc = rd32(E1000_DTXSWC);
+ u32 dtxswc;
+
+ switch (hw->mac.type) {
+ case e1000_82576:
+ dtxswc = rd32(E1000_DTXSWC);
+ if (enable)
+ dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN;
+ else
+ dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN;
+ wr32(E1000_DTXSWC, dtxswc);
+ break;
+ case e1000_i350:
+ dtxswc = rd32(E1000_TXSWC);
+ if (enable)
+ dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN;
+ else
+ dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN;
+ wr32(E1000_TXSWC, dtxswc);
+ break;
+ default:
+ /* Currently no other hardware supports loopback */
+ break;
+ }
- if (enable)
- dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN;
- else
- dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN;
- wr32(E1000_DTXSWC, dtxswc);
}
/**
@@ -1735,6 +1766,7 @@ static s32 igb_reset_hw_82580(struct e1000_hw *hw)
ctrl |= E1000_CTRL_RST;
wr32(E1000_CTRL, ctrl);
+ wrfl();
/* Add delay to insure DEV_RST has time to complete */
if (global_device_reset)
@@ -1804,7 +1836,8 @@ u16 igb_rxpbs_adjust_82580(u32 data)
* Calculates the EEPROM checksum by reading/adding each word of the EEPROM
* and then verifies that the sum of the EEPROM is equal to 0xBABA.
**/
-s32 igb_validate_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset)
+static s32 igb_validate_nvm_checksum_with_offset(struct e1000_hw *hw,
+ u16 offset)
{
s32 ret_val = 0;
u16 checksum = 0;
@@ -1839,7 +1872,7 @@ out:
* up to the checksum. Then calculates the EEPROM checksum and writes the
* value to the EEPROM.
**/
-s32 igb_update_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset)
+static s32 igb_update_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset)
{
s32 ret_val;
u16 checksum = 0;
diff --git a/drivers/net/igb/e1000_82575.h b/drivers/net/ethernet/intel/igb/e1000_82575.h
index dd6df3498998..08a757eb6608 100644
--- a/drivers/net/igb/e1000_82575.h
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -130,7 +130,9 @@ union e1000_adv_tx_desc {
#define E1000_ADVTXD_MAC_TSTAMP 0x00080000 /* IEEE1588 Timestamp packet */
#define E1000_ADVTXD_DTYP_CTXT 0x00200000 /* Advanced Context Descriptor */
#define E1000_ADVTXD_DTYP_DATA 0x00300000 /* Advanced Data Descriptor */
+#define E1000_ADVTXD_DCMD_EOP 0x01000000 /* End of Packet */
#define E1000_ADVTXD_DCMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */
+#define E1000_ADVTXD_DCMD_RS 0x08000000 /* Report Status */
#define E1000_ADVTXD_DCMD_DEXT 0x20000000 /* Descriptor extension (1=Adv) */
#define E1000_ADVTXD_DCMD_VLE 0x40000000 /* VLAN pkt enable */
#define E1000_ADVTXD_DCMD_TSE 0x80000000 /* TCP Seg enable */
@@ -243,6 +245,8 @@ struct e1000_adv_tx_context_desc {
#define E1000_DTXCTL_MDP_EN 0x0020
#define E1000_DTXCTL_SPOOF_INT 0x0040
+#define E1000_EEPROM_PCS_AUTONEG_DISABLE_BIT (1 << 14)
+
#define ALL_QUEUES 0xFFFF
/* RX packet buffer size defines */
diff --git a/drivers/net/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h
index 6b80d40110ca..f5fc5725ea94 100644
--- a/drivers/net/igb/e1000_defines.h
+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -85,6 +85,7 @@
#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */
#define E1000_RXD_STAT_TS 0x10000 /* Pkt was time stamped */
+#define E1000_RXDEXT_STATERR_LB 0x00040000
#define E1000_RXDEXT_STATERR_CE 0x01000000
#define E1000_RXDEXT_STATERR_SE 0x02000000
#define E1000_RXDEXT_STATERR_SEQ 0x04000000
@@ -409,6 +410,9 @@
#define E1000_ICS_DRSTA E1000_ICR_DRSTA /* Device Reset Aserted */
/* Extended Interrupt Cause Set */
+/* E1000_EITR_CNT_IGNR is only for 82576 and newer */
+#define E1000_EITR_CNT_IGNR 0x80000000 /* Don't reset counters on write */
+
/* Transmit Descriptor Control */
/* Enable the counting of descriptors still to be processed. */
@@ -437,6 +441,7 @@
#define E1000_RAH_POOL_1 0x00040000
/* Error Codes */
+#define E1000_SUCCESS 0
#define E1000_ERR_NVM 1
#define E1000_ERR_PHY 2
#define E1000_ERR_CONFIG 3
@@ -511,6 +516,16 @@
#define E1000_GCR_CMPL_TMOUT_RESEND 0x00010000
#define E1000_GCR_CAP_VER2 0x00040000
+/* mPHY Address Control and Data Registers */
+#define E1000_MPHY_ADDR_CTL 0x0024 /* mPHY Address Control Register */
+#define E1000_MPHY_ADDR_CTL_OFFSET_MASK 0xFFFF0000
+#define E1000_MPHY_DATA 0x0E10 /* mPHY Data Register */
+
+/* mPHY PCS CLK Register */
+#define E1000_MPHY_PCS_CLK_REG_OFFSET 0x0004 /* mPHY PCS CLK AFE CSR Offset */
+/* mPHY Near End Digital Loopback Override Bit */
+#define E1000_MPHY_PCS_CLK_REG_DIGINELBEN 0x10
+
/* PHY Control Register */
#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
@@ -587,8 +602,8 @@
#define E1000_NVM_POLL_READ 0 /* Flag for polling for read complete */
/* NVM Word Offsets */
-#define NVM_ID_LED_SETTINGS 0x0004
-/* For SERDES output amplitude adjustment. */
+#define NVM_COMPAT 0x0003
+#define NVM_ID_LED_SETTINGS 0x0004 /* SERDES output amplitude */
#define NVM_INIT_CONTROL2_REG 0x000F
#define NVM_INIT_CONTROL3_PORT_B 0x0014
#define NVM_INIT_CONTROL3_PORT_A 0x0024
diff --git a/drivers/net/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h
index 27153e8d7b16..4519a1367170 100644
--- a/drivers/net/igb/e1000_hw.h
+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
diff --git a/drivers/net/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c
index ce8255fc3c52..73aac082c44d 100644
--- a/drivers/net/igb/e1000_mac.c
+++ b/drivers/net/ethernet/intel/igb/e1000_mac.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -29,6 +29,7 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
#include "e1000_mac.h"
@@ -116,6 +117,50 @@ static void igb_write_vfta(struct e1000_hw *hw, u32 offset, u32 value)
wrfl();
}
+/* Due to a hw errata, if the host tries to configure the VFTA register
+ * while performing queries from the BMC or DMA, then the VFTA in some
+ * cases won't be written.
+ */
+
+/**
+ * igb_clear_vfta_i350 - Clear VLAN filter table
+ * @hw: pointer to the HW structure
+ *
+ * Clears the register array which contains the VLAN filter table by
+ * setting all the values to 0.
+ **/
+void igb_clear_vfta_i350(struct e1000_hw *hw)
+{
+ u32 offset;
+ int i;
+
+ for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) {
+ for (i = 0; i < 10; i++)
+ array_wr32(E1000_VFTA, offset, 0);
+
+ wrfl();
+ }
+}
+
+/**
+ * igb_write_vfta_i350 - Write value to VLAN filter table
+ * @hw: pointer to the HW structure
+ * @offset: register offset in VLAN filter table
+ * @value: register value written to VLAN filter table
+ *
+ * Writes value at the given offset in the register array which stores
+ * the VLAN filter table.
+ **/
+void igb_write_vfta_i350(struct e1000_hw *hw, u32 offset, u32 value)
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ array_wr32(E1000_VFTA, offset, value);
+
+ wrfl();
+}
+
/**
* igb_init_rx_addrs - Initialize receive address's
* @hw: pointer to the HW structure
@@ -154,9 +199,12 @@ s32 igb_vfta_set(struct e1000_hw *hw, u32 vid, bool add)
{
u32 index = (vid >> E1000_VFTA_ENTRY_SHIFT) & E1000_VFTA_ENTRY_MASK;
u32 mask = 1 << (vid & E1000_VFTA_ENTRY_BIT_SHIFT_MASK);
- u32 vfta = array_rd32(E1000_VFTA, index);
+ u32 vfta;
+ struct igb_adapter *adapter = hw->back;
s32 ret_val = 0;
+ vfta = adapter->shadow_vfta[index];
+
/* bit was set/cleared before we started */
if ((!!(vfta & mask)) == add) {
ret_val = -E1000_ERR_CONFIG;
@@ -166,8 +214,11 @@ s32 igb_vfta_set(struct e1000_hw *hw, u32 vid, bool add)
else
vfta &= ~mask;
}
-
- igb_write_vfta(hw, index, vfta);
+ if (hw->mac.type == e1000_i350)
+ igb_write_vfta_i350(hw, index, vfta);
+ else
+ igb_write_vfta(hw, index, vfta);
+ adapter->shadow_vfta[index] = vfta;
return ret_val;
}
@@ -190,6 +241,13 @@ s32 igb_check_alt_mac_addr(struct e1000_hw *hw)
u16 offset, nvm_alt_mac_addr_offset, nvm_data;
u8 alt_mac_addr[ETH_ALEN];
+ /*
+ * Alternate MAC address is handled by the option ROM for 82580
+ * and newer. SW support not required.
+ */
+ if (hw->mac.type >= e1000_82580)
+ goto out;
+
ret_val = hw->nvm.ops.read(hw, NVM_ALT_MAC_ADDR_PTR, 1,
&nvm_alt_mac_addr_offset);
if (ret_val) {
@@ -197,13 +255,18 @@ s32 igb_check_alt_mac_addr(struct e1000_hw *hw)
goto out;
}
- if (nvm_alt_mac_addr_offset == 0xFFFF) {
+ if ((nvm_alt_mac_addr_offset == 0xFFFF) ||
+ (nvm_alt_mac_addr_offset == 0x0000))
/* There is no Alternate MAC Address */
goto out;
- }
if (hw->bus.func == E1000_FUNC_1)
nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN1;
+ if (hw->bus.func == E1000_FUNC_2)
+ nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN2;
+
+ if (hw->bus.func == E1000_FUNC_3)
+ nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN3;
for (i = 0; i < ETH_ALEN; i += 2) {
offset = nvm_alt_mac_addr_offset + (i >> 1);
ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
@@ -217,7 +280,7 @@ s32 igb_check_alt_mac_addr(struct e1000_hw *hw)
}
/* if multicast bit is set, the alternate address will not be used */
- if (alt_mac_addr[0] & 0x01) {
+ if (is_multicast_ether_addr(alt_mac_addr)) {
hw_dbg("Ignoring Alternate Mac Address with MC bit set\n");
goto out;
}
diff --git a/drivers/net/igb/e1000_mac.h b/drivers/net/ethernet/intel/igb/e1000_mac.h
index 601be99711c2..e45996b4ea34 100644
--- a/drivers/net/igb/e1000_mac.h
+++ b/drivers/net/ethernet/intel/igb/e1000_mac.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -60,6 +60,7 @@ s32 igb_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg,
void igb_clear_hw_cntrs_base(struct e1000_hw *hw);
void igb_clear_vfta(struct e1000_hw *hw);
+void igb_clear_vfta_i350(struct e1000_hw *hw);
s32 igb_vfta_set(struct e1000_hw *hw, u32 vid, bool add);
void igb_config_collision_dist(struct e1000_hw *hw);
void igb_init_rx_addrs(struct e1000_hw *hw, u16 rar_count);
diff --git a/drivers/net/igb/e1000_mbx.c b/drivers/net/ethernet/intel/igb/e1000_mbx.c
index 78d48c7fa859..469d95eaa154 100644
--- a/drivers/net/igb/e1000_mbx.c
+++ b/drivers/net/ethernet/intel/igb/e1000_mbx.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -34,7 +34,7 @@
* @size: Length of buffer
* @mbx_id: id of mailbox to read
*
- * returns SUCCESS if it successfuly read message from buffer
+ * returns SUCCESS if it successfully read message from buffer
**/
s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id)
{
diff --git a/drivers/net/igb/e1000_mbx.h b/drivers/net/ethernet/intel/igb/e1000_mbx.h
index bb112fb6c3a1..eddb0f83dcea 100644
--- a/drivers/net/igb/e1000_mbx.h
+++ b/drivers/net/ethernet/intel/igb/e1000_mbx.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
diff --git a/drivers/net/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c
index 75bf36a4baee..40407124e722 100644
--- a/drivers/net/igb/e1000_nvm.c
+++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -285,6 +285,7 @@ static s32 igb_ready_nvm_eeprom(struct e1000_hw *hw)
/* Clear SK and CS */
eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
wr32(E1000_EECD, eecd);
+ wrfl();
udelay(1);
timeout = NVM_MAX_RETRY_SPI;
diff --git a/drivers/net/igb/e1000_nvm.h b/drivers/net/ethernet/intel/igb/e1000_nvm.h
index 7f43564c4bcc..a2a7ca9fa733 100644
--- a/drivers/net/igb/e1000_nvm.h
+++ b/drivers/net/ethernet/intel/igb/e1000_nvm.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007 Intel Corporation.
+ Copyright(c) 2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
diff --git a/drivers/net/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c
index d639706eb3f6..b17d7c20f817 100644
--- a/drivers/net/igb/e1000_phy.c
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -306,6 +306,12 @@ s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data)
u32 i, i2ccmd = 0;
u16 phy_data_swapped;
+ /* Prevent overwritting SFP I2C EEPROM which is at A0 address.*/
+ if ((hw->phy.addr == 0) || (hw->phy.addr > 7)) {
+ hw_dbg("PHY I2C Address %d is out of range.\n",
+ hw->phy.addr);
+ return -E1000_ERR_CONFIG;
+ }
/* Swap the data bytes for the I2C interface */
phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00);
@@ -1681,7 +1687,7 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw)
if (ret_val)
goto out;
- is_cm = !(phy_data & I347AT4_PCDC_CABLE_LENGTH_UNIT);
+ is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT);
/* Populate the phy structure with cable length in meters */
phy->min_cable_length = phy_data / (is_cm ? 100 : 1);
diff --git a/drivers/net/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h
index 2cc117705a31..8510797b9d81 100644
--- a/drivers/net/igb/e1000_phy.h
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
diff --git a/drivers/net/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h
index 958ca3bda482..0a860bc1198e 100644
--- a/drivers/net/igb/e1000_regs.h
+++ b/drivers/net/ethernet/intel/igb/e1000_regs.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -318,6 +318,7 @@
#define E1000_RPLOLR 0x05AF0 /* Replication Offload - RW */
#define E1000_UTA 0x0A000 /* Unicast Table Array - RW */
#define E1000_IOVTCL 0x05BBC /* IOV Control Register */
+#define E1000_TXSWC 0x05ACC /* Tx Switch Control */
/* These act per VF so an array friendly macro is used */
#define E1000_P2VMAILBOX(_n) (0x00C00 + (4 * (_n)))
#define E1000_VMBMEM(_n) (0x00800 + (64 * (_n)))
diff --git a/drivers/net/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index f4fa4b1751cf..c69feebf2653 100644
--- a/drivers/net/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -37,14 +37,20 @@
#include <linux/clocksource.h>
#include <linux/timecompare.h>
#include <linux/net_tstamp.h>
+#include <linux/bitops.h>
+#include <linux/if_vlan.h>
struct igb_adapter;
-/* ((1000000000ns / (6000ints/s * 1024ns)) << 2 = 648 */
-#define IGB_START_ITR 648
+/* Interrupt defines */
+#define IGB_START_ITR 648 /* ~6000 ints/sec */
+#define IGB_4K_ITR 980
+#define IGB_20K_ITR 196
+#define IGB_70K_ITR 56
/* TX/RX descriptor defines */
#define IGB_DEFAULT_TXD 256
+#define IGB_DEFAULT_TX_WORK 128
#define IGB_MIN_TXD 80
#define IGB_MAX_TXD 4096
@@ -61,12 +67,13 @@ struct igb_adapter;
/* Transmit and receive queues */
#define IGB_MAX_RX_QUEUES (adapter->vfs_allocated_count ? 2 : \
(hw->mac.type > e1000_82575 ? 8 : 4))
-#define IGB_ABS_MAX_TX_QUEUES 8
-#define IGB_MAX_TX_QUEUES IGB_MAX_RX_QUEUES
+#define IGB_MAX_TX_QUEUES 16
#define IGB_MAX_VF_MC_ENTRIES 30
#define IGB_MAX_VF_FUNCTIONS 8
#define IGB_MAX_VFTA_ENTRIES 128
+#define IGB_82576_VF_DEV_ID 0x10CA
+#define IGB_I350_VF_DEV_ID 0x1520
struct vf_data_storage {
unsigned char vf_mac_addresses[ETH_ALEN];
@@ -78,6 +85,7 @@ struct vf_data_storage {
u16 pf_vlan; /* When set, guest VLAN config not allowed. */
u16 pf_qos;
u16 tx_rate;
+ struct pci_dev *vfdev;
};
#define IGB_VF_FLAG_CTS 0x00000001 /* VF is clear to send data */
@@ -98,23 +106,20 @@ struct vf_data_storage {
*/
#define IGB_RX_PTHRESH 8
#define IGB_RX_HTHRESH 8
-#define IGB_RX_WTHRESH 1
#define IGB_TX_PTHRESH 8
#define IGB_TX_HTHRESH 1
+#define IGB_RX_WTHRESH ((hw->mac.type == e1000_82576 && \
+ adapter->msix_entries) ? 1 : 4)
#define IGB_TX_WTHRESH ((hw->mac.type == e1000_82576 && \
- adapter->msix_entries) ? 1 : 16)
+ adapter->msix_entries) ? 1 : 16)
/* this is the size past which hardware will drop packets when setting LPE=0 */
#define MAXIMUM_ETHERNET_VLAN_SIZE 1522
/* Supported Rx Buffer Sizes */
-#define IGB_RXBUFFER_64 64 /* Used for packet split */
-#define IGB_RXBUFFER_128 128 /* Used for packet split */
-#define IGB_RXBUFFER_1024 1024
-#define IGB_RXBUFFER_2048 2048
+#define IGB_RXBUFFER_512 512
#define IGB_RXBUFFER_16384 16384
-
-#define MAX_STD_JUMBO_FRAME_SIZE 9234
+#define IGB_RX_HDR_LEN IGB_RXBUFFER_512
/* How many Tx Descriptors do we need to call netif_wake_queue ? */
#define IGB_TX_QUEUE_WAKE 16
@@ -131,29 +136,34 @@ struct vf_data_storage {
#define IGB_MNG_VLAN_NONE -1
+#define IGB_TX_FLAGS_CSUM 0x00000001
+#define IGB_TX_FLAGS_VLAN 0x00000002
+#define IGB_TX_FLAGS_TSO 0x00000004
+#define IGB_TX_FLAGS_IPV4 0x00000008
+#define IGB_TX_FLAGS_TSTAMP 0x00000010
+#define IGB_TX_FLAGS_VLAN_MASK 0xffff0000
+#define IGB_TX_FLAGS_VLAN_SHIFT 16
+
/* wrapper around a pointer to a socket buffer,
* so a DMA handle can be stored along with the buffer */
-struct igb_buffer {
+struct igb_tx_buffer {
+ union e1000_adv_tx_desc *next_to_watch;
+ unsigned long time_stamp;
struct sk_buff *skb;
+ unsigned int bytecount;
+ u16 gso_segs;
+ __be16 protocol;
dma_addr_t dma;
- union {
- /* TX */
- struct {
- unsigned long time_stamp;
- u16 length;
- u16 next_to_watch;
- unsigned int bytecount;
- u16 gso_segs;
- u8 tx_flags;
- u8 mapped_as_page;
- };
- /* RX */
- struct {
- struct page *page;
- dma_addr_t page_dma;
- u16 page_offset;
- };
- };
+ u32 length;
+ u32 tx_flags;
+};
+
+struct igb_rx_buffer {
+ struct sk_buff *skb;
+ dma_addr_t dma;
+ struct page *page;
+ dma_addr_t page_dma;
+ u32 page_offset;
};
struct igb_tx_queue_stats {
@@ -171,14 +181,24 @@ struct igb_rx_queue_stats {
u64 alloc_failed;
};
+struct igb_ring_container {
+ struct igb_ring *ring; /* pointer to linked list of rings */
+ unsigned int total_bytes; /* total bytes processed this int */
+ unsigned int total_packets; /* total packets processed this int */
+ u16 work_limit; /* total work allowed per interrupt */
+ u8 count; /* total number of rings in vector */
+ u8 itr; /* current ITR setting for ring */
+};
+
struct igb_q_vector {
- struct igb_adapter *adapter; /* backlink */
- struct igb_ring *rx_ring;
- struct igb_ring *tx_ring;
- struct napi_struct napi;
+ struct igb_adapter *adapter; /* backlink */
+ int cpu; /* CPU for DCA */
+ u32 eims_value; /* EIMS mask value */
- u32 eims_value;
- u16 cpu;
+ struct igb_ring_container rx, tx;
+
+ struct napi_struct napi;
+ int numa_node;
u16 itr_val;
u8 set_itr;
@@ -188,25 +208,25 @@ struct igb_q_vector {
};
struct igb_ring {
- struct igb_q_vector *q_vector; /* backlink to q_vector */
- struct net_device *netdev; /* back pointer to net_device */
- struct device *dev; /* device pointer for dma mapping */
- dma_addr_t dma; /* phys address of the ring */
- void *desc; /* descriptor ring memory */
- unsigned int size; /* length of desc. ring in bytes */
- u16 count; /* number of desc. in the ring */
- u16 next_to_use;
- u16 next_to_clean;
- u8 queue_index;
- u8 reg_idx;
- void __iomem *head;
- void __iomem *tail;
- struct igb_buffer *buffer_info; /* array of buffer info structs */
+ struct igb_q_vector *q_vector; /* backlink to q_vector */
+ struct net_device *netdev; /* back pointer to net_device */
+ struct device *dev; /* device pointer for dma mapping */
+ union { /* array of buffer info structs */
+ struct igb_tx_buffer *tx_buffer_info;
+ struct igb_rx_buffer *rx_buffer_info;
+ };
+ void *desc; /* descriptor ring memory */
+ unsigned long flags; /* ring specific flags */
+ void __iomem *tail; /* pointer to ring tail register */
- unsigned int total_bytes;
- unsigned int total_packets;
+ u16 count; /* number of desc. in the ring */
+ u8 queue_index; /* logical index of the ring*/
+ u8 reg_idx; /* physical index of the ring */
+ u32 size; /* length of desc. ring in bytes */
- u32 flags;
+ /* everything past this point are written often */
+ u16 next_to_clean ____cacheline_aligned_in_smp;
+ u16 next_to_use;
union {
/* TX */
@@ -214,30 +234,40 @@ struct igb_ring {
struct igb_tx_queue_stats tx_stats;
struct u64_stats_sync tx_syncp;
struct u64_stats_sync tx_syncp2;
- bool detect_tx_hung;
};
/* RX */
struct {
struct igb_rx_queue_stats rx_stats;
struct u64_stats_sync rx_syncp;
- u32 rx_buffer_len;
};
};
+ /* Items past this point are only used during ring alloc / free */
+ dma_addr_t dma; /* phys address of the ring */
+ int numa_node; /* node to alloc ring memory on */
};
-#define IGB_RING_FLAG_RX_CSUM 0x00000001 /* RX CSUM enabled */
-#define IGB_RING_FLAG_RX_SCTP_CSUM 0x00000002 /* SCTP CSUM offload enabled */
+enum e1000_ring_flags_t {
+ IGB_RING_FLAG_RX_SCTP_CSUM,
+ IGB_RING_FLAG_RX_LB_VLAN_BSWAP,
+ IGB_RING_FLAG_TX_CTX_IDX,
+ IGB_RING_FLAG_TX_DETECT_HANG
+};
-#define IGB_RING_FLAG_TX_CTX_IDX 0x00000001 /* HW requires context index */
+#define IGB_TXD_DCMD (E1000_ADVTXD_DCMD_EOP | E1000_ADVTXD_DCMD_RS)
-#define IGB_ADVTXD_DCMD (E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS)
+#define IGB_RX_DESC(R, i) \
+ (&(((union e1000_adv_rx_desc *)((R)->desc))[i]))
+#define IGB_TX_DESC(R, i) \
+ (&(((union e1000_adv_tx_desc *)((R)->desc))[i]))
+#define IGB_TX_CTXTDESC(R, i) \
+ (&(((struct e1000_adv_tx_context_desc *)((R)->desc))[i]))
-#define E1000_RX_DESC_ADV(R, i) \
- (&(((union e1000_adv_rx_desc *)((R).desc))[i]))
-#define E1000_TX_DESC_ADV(R, i) \
- (&(((union e1000_adv_tx_desc *)((R).desc))[i]))
-#define E1000_TX_CTXTDESC_ADV(R, i) \
- (&(((struct e1000_adv_tx_context_desc *)((R).desc))[i]))
+/* igb_test_staterr - tests bits within Rx descriptor status and error fields */
+static inline __le32 igb_test_staterr(union e1000_adv_rx_desc *rx_desc,
+ const u32 stat_err_bits)
+{
+ return rx_desc->wb.upper.status_error & cpu_to_le32(stat_err_bits);
+}
/* igb_desc_unused - calculate if we have unused descriptors */
static inline int igb_desc_unused(struct igb_ring *ring)
@@ -250,15 +280,15 @@ static inline int igb_desc_unused(struct igb_ring *ring)
/* board specific private data structure */
struct igb_adapter {
- struct timer_list watchdog_timer;
- struct timer_list phy_info_timer;
- struct vlan_group *vlgrp;
- u16 mng_vlan_id;
- u32 bd_number;
- u32 wol;
- u32 en_mng_pt;
- u16 link_speed;
- u16 link_duplex;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+
+ struct net_device *netdev;
+
+ unsigned long state;
+ unsigned int flags;
+
+ unsigned int num_q_vectors;
+ struct msix_entry *msix_entries;
/* Interrupt Throttle Rate */
u32 rx_itr_setting;
@@ -266,27 +296,37 @@ struct igb_adapter {
u16 tx_itr;
u16 rx_itr;
- struct work_struct reset_task;
- struct work_struct watchdog_task;
- bool fc_autoneg;
- u8 tx_timeout_factor;
- struct timer_list blink_timer;
- unsigned long led_status;
-
/* TX */
- struct igb_ring *tx_ring[16];
+ u16 tx_work_limit;
u32 tx_timeout_count;
+ int num_tx_queues;
+ struct igb_ring *tx_ring[16];
/* RX */
- struct igb_ring *rx_ring[16];
- int num_tx_queues;
int num_rx_queues;
+ struct igb_ring *rx_ring[16];
u32 max_frame_size;
u32 min_frame_size;
+ struct timer_list watchdog_timer;
+ struct timer_list phy_info_timer;
+
+ u16 mng_vlan_id;
+ u32 bd_number;
+ u32 wol;
+ u32 en_mng_pt;
+ u16 link_speed;
+ u16 link_duplex;
+
+ struct work_struct reset_task;
+ struct work_struct watchdog_task;
+ bool fc_autoneg;
+ u8 tx_timeout_factor;
+ struct timer_list blink_timer;
+ unsigned long led_status;
+
/* OS defined structs */
- struct net_device *netdev;
struct pci_dev *pdev;
struct cyclecounter cycles;
struct timecounter clock;
@@ -308,18 +348,13 @@ struct igb_adapter {
int msg_enable;
- unsigned int num_q_vectors;
struct igb_q_vector *q_vector[MAX_Q_VECTORS];
- struct msix_entry *msix_entries;
u32 eims_enable_mask;
u32 eims_other;
/* to not mess up cache alignment, always add to the bottom */
- unsigned long state;
- unsigned int flags;
u32 eeprom_wol;
- struct igb_ring *multi_tx_table[IGB_ABS_MAX_TX_QUEUES];
u16 tx_ring_count;
u16 rx_ring_count;
unsigned int vfs_allocated_count;
@@ -327,6 +362,8 @@ struct igb_adapter {
int vf_rate_link_speed;
u32 rss_queues;
u32 wvbr;
+ int node;
+ u32 *shadow_vfta;
};
#define IGB_FLAG_HAS_MSI (1 << 0)
@@ -369,10 +406,10 @@ extern void igb_configure_tx_ring(struct igb_adapter *, struct igb_ring *);
extern void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *);
extern void igb_setup_tctl(struct igb_adapter *);
extern void igb_setup_rctl(struct igb_adapter *);
-extern netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *, struct igb_ring *);
+extern netdev_tx_t igb_xmit_frame_ring(struct sk_buff *, struct igb_ring *);
extern void igb_unmap_and_free_tx_resource(struct igb_ring *,
- struct igb_buffer *);
-extern void igb_alloc_rx_buffers_adv(struct igb_ring *, int);
+ struct igb_tx_buffer *);
+extern void igb_alloc_rx_buffers(struct igb_ring *, u16);
extern void igb_update_stats(struct igb_adapter *, struct rtnl_link_stats64 *);
extern bool igb_has_link(struct igb_adapter *adapter);
extern void igb_set_ethtool_ops(struct net_device *);
diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index fdc895e5a3f8..43873eba2f63 100644
--- a/drivers/net/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -318,65 +318,6 @@ static int igb_set_pauseparam(struct net_device *netdev,
return retval;
}
-static u32 igb_get_rx_csum(struct net_device *netdev)
-{
- struct igb_adapter *adapter = netdev_priv(netdev);
- return !!(adapter->rx_ring[0]->flags & IGB_RING_FLAG_RX_CSUM);
-}
-
-static int igb_set_rx_csum(struct net_device *netdev, u32 data)
-{
- struct igb_adapter *adapter = netdev_priv(netdev);
- int i;
-
- for (i = 0; i < adapter->num_rx_queues; i++) {
- if (data)
- adapter->rx_ring[i]->flags |= IGB_RING_FLAG_RX_CSUM;
- else
- adapter->rx_ring[i]->flags &= ~IGB_RING_FLAG_RX_CSUM;
- }
-
- return 0;
-}
-
-static u32 igb_get_tx_csum(struct net_device *netdev)
-{
- return (netdev->features & NETIF_F_IP_CSUM) != 0;
-}
-
-static int igb_set_tx_csum(struct net_device *netdev, u32 data)
-{
- struct igb_adapter *adapter = netdev_priv(netdev);
-
- if (data) {
- netdev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
- if (adapter->hw.mac.type >= e1000_82576)
- netdev->features |= NETIF_F_SCTP_CSUM;
- } else {
- netdev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
- NETIF_F_SCTP_CSUM);
- }
-
- return 0;
-}
-
-static int igb_set_tso(struct net_device *netdev, u32 data)
-{
- struct igb_adapter *adapter = netdev_priv(netdev);
-
- if (data) {
- netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
- } else {
- netdev->features &= ~NETIF_F_TSO;
- netdev->features &= ~NETIF_F_TSO6;
- }
-
- dev_info(&adapter->pdev->dev, "TSO is %s\n",
- data ? "Enabled" : "Disabled");
- return 0;
-}
-
static u32 igb_get_msglevel(struct net_device *netdev)
{
struct igb_adapter *adapter = netdev_priv(netdev);
@@ -764,12 +705,8 @@ static void igb_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = IGB_MAX_RXD;
ring->tx_max_pending = IGB_MAX_TXD;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = adapter->rx_ring_count;
ring->tx_pending = adapter->tx_ring_count;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
static int igb_set_ringparam(struct net_device *netdev,
@@ -1284,6 +1221,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
/* Disable all the interrupts */
wr32(E1000_IMC, ~0);
+ wrfl();
msleep(10);
/* Define all writable bits for ICS */
@@ -1327,6 +1265,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
wr32(E1000_IMC, mask);
wr32(E1000_ICS, mask);
+ wrfl();
msleep(10);
if (adapter->test_icr & mask) {
@@ -1348,6 +1287,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
wr32(E1000_IMS, mask);
wr32(E1000_ICS, mask);
+ wrfl();
msleep(10);
if (!(adapter->test_icr & mask)) {
@@ -1369,6 +1309,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
wr32(E1000_IMC, ~mask);
wr32(E1000_ICS, ~mask);
+ wrfl();
msleep(10);
if (adapter->test_icr & mask) {
@@ -1380,6 +1321,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
/* Disable all the interrupts */
wr32(E1000_IMC, ~0);
+ wrfl();
msleep(10);
/* Unhook test interrupt handler */
@@ -1422,7 +1364,6 @@ static int igb_setup_desc_rings(struct igb_adapter *adapter)
rx_ring->count = IGB_DEFAULT_RXD;
rx_ring->dev = &adapter->pdev->dev;
rx_ring->netdev = adapter->netdev;
- rx_ring->rx_buffer_len = IGB_RXBUFFER_2048;
rx_ring->reg_idx = adapter->vfs_allocated_count;
if (igb_setup_rx_resources(rx_ring)) {
@@ -1437,7 +1378,7 @@ static int igb_setup_desc_rings(struct igb_adapter *adapter)
igb_setup_rctl(adapter);
igb_configure_rx_ring(adapter, rx_ring);
- igb_alloc_rx_buffers_adv(rx_ring, igb_desc_unused(rx_ring));
+ igb_alloc_rx_buffers(rx_ring, igb_desc_unused(rx_ring));
return 0;
@@ -1520,6 +1461,22 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter)
/* use CTRL_EXT to identify link type as SGMII can appear as copper */
if (reg & E1000_CTRL_EXT_LINK_MODE_MASK) {
+ if ((hw->device_id == E1000_DEV_ID_DH89XXCC_SGMII) ||
+ (hw->device_id == E1000_DEV_ID_DH89XXCC_SERDES) ||
+ (hw->device_id == E1000_DEV_ID_DH89XXCC_BACKPLANE) ||
+ (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP)) {
+
+ /* Enable DH89xxCC MPHY for near end loopback */
+ reg = rd32(E1000_MPHY_ADDR_CTL);
+ reg = (reg & E1000_MPHY_ADDR_CTL_OFFSET_MASK) |
+ E1000_MPHY_PCS_CLK_REG_OFFSET;
+ wr32(E1000_MPHY_ADDR_CTL, reg);
+
+ reg = rd32(E1000_MPHY_DATA);
+ reg |= E1000_MPHY_PCS_CLK_REG_DIGINELBEN;
+ wr32(E1000_MPHY_DATA, reg);
+ }
+
reg = rd32(E1000_RCTL);
reg |= E1000_RCTL_LBM_TCVR;
wr32(E1000_RCTL, reg);
@@ -1561,6 +1518,23 @@ static void igb_loopback_cleanup(struct igb_adapter *adapter)
u32 rctl;
u16 phy_reg;
+ if ((hw->device_id == E1000_DEV_ID_DH89XXCC_SGMII) ||
+ (hw->device_id == E1000_DEV_ID_DH89XXCC_SERDES) ||
+ (hw->device_id == E1000_DEV_ID_DH89XXCC_BACKPLANE) ||
+ (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP)) {
+ u32 reg;
+
+ /* Disable near end loopback on DH89xxCC */
+ reg = rd32(E1000_MPHY_ADDR_CTL);
+ reg = (reg & E1000_MPHY_ADDR_CTL_OFFSET_MASK) |
+ E1000_MPHY_PCS_CLK_REG_OFFSET;
+ wr32(E1000_MPHY_ADDR_CTL, reg);
+
+ reg = rd32(E1000_MPHY_DATA);
+ reg &= ~E1000_MPHY_PCS_CLK_REG_DIGINELBEN;
+ wr32(E1000_MPHY_DATA, reg);
+ }
+
rctl = rd32(E1000_RCTL);
rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC);
wr32(E1000_RCTL, rctl);
@@ -1601,34 +1575,33 @@ static int igb_clean_test_rings(struct igb_ring *rx_ring,
unsigned int size)
{
union e1000_adv_rx_desc *rx_desc;
- struct igb_buffer *buffer_info;
- int rx_ntc, tx_ntc, count = 0;
- u32 staterr;
+ struct igb_rx_buffer *rx_buffer_info;
+ struct igb_tx_buffer *tx_buffer_info;
+ u16 rx_ntc, tx_ntc, count = 0;
/* initialize next to clean and descriptor values */
rx_ntc = rx_ring->next_to_clean;
tx_ntc = tx_ring->next_to_clean;
- rx_desc = E1000_RX_DESC_ADV(*rx_ring, rx_ntc);
- staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
+ rx_desc = IGB_RX_DESC(rx_ring, rx_ntc);
- while (staterr & E1000_RXD_STAT_DD) {
+ while (igb_test_staterr(rx_desc, E1000_RXD_STAT_DD)) {
/* check rx buffer */
- buffer_info = &rx_ring->buffer_info[rx_ntc];
+ rx_buffer_info = &rx_ring->rx_buffer_info[rx_ntc];
/* unmap rx buffer, will be remapped by alloc_rx_buffers */
dma_unmap_single(rx_ring->dev,
- buffer_info->dma,
- rx_ring->rx_buffer_len,
+ rx_buffer_info->dma,
+ IGB_RX_HDR_LEN,
DMA_FROM_DEVICE);
- buffer_info->dma = 0;
+ rx_buffer_info->dma = 0;
/* verify contents of skb */
- if (!igb_check_lbtest_frame(buffer_info->skb, size))
+ if (!igb_check_lbtest_frame(rx_buffer_info->skb, size))
count++;
/* unmap buffer on tx side */
- buffer_info = &tx_ring->buffer_info[tx_ntc];
- igb_unmap_and_free_tx_resource(tx_ring, buffer_info);
+ tx_buffer_info = &tx_ring->tx_buffer_info[tx_ntc];
+ igb_unmap_and_free_tx_resource(tx_ring, tx_buffer_info);
/* increment rx/tx next to clean counters */
rx_ntc++;
@@ -1639,12 +1612,11 @@ static int igb_clean_test_rings(struct igb_ring *rx_ring,
tx_ntc = 0;
/* fetch next descriptor */
- rx_desc = E1000_RX_DESC_ADV(*rx_ring, rx_ntc);
- staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
+ rx_desc = IGB_RX_DESC(rx_ring, rx_ntc);
}
/* re-map buffers to ring, store next to clean values */
- igb_alloc_rx_buffers_adv(rx_ring, count);
+ igb_alloc_rx_buffers(rx_ring, count);
rx_ring->next_to_clean = rx_ntc;
tx_ring->next_to_clean = tx_ntc;
@@ -1655,8 +1627,9 @@ static int igb_run_loopback_test(struct igb_adapter *adapter)
{
struct igb_ring *tx_ring = &adapter->test_tx_ring;
struct igb_ring *rx_ring = &adapter->test_rx_ring;
- int i, j, lc, good_cnt, ret_val = 0;
- unsigned int size = 1024;
+ u16 i, j, lc, good_cnt;
+ int ret_val = 0;
+ unsigned int size = IGB_RX_HDR_LEN;
netdev_tx_t tx_ret_val;
struct sk_buff *skb;
@@ -1687,7 +1660,7 @@ static int igb_run_loopback_test(struct igb_adapter *adapter)
/* place 64 packets on the transmit queue*/
for (i = 0; i < 64; i++) {
skb_get(skb);
- tx_ret_val = igb_xmit_frame_ring_adv(skb, tx_ring);
+ tx_ret_val = igb_xmit_frame_ring(skb, tx_ring);
if (tx_ret_val == NETDEV_TX_OK)
good_cnt++;
}
@@ -2033,7 +2006,8 @@ static int igb_set_coalesce(struct net_device *netdev,
for (i = 0; i < adapter->num_q_vectors; i++) {
struct igb_q_vector *q_vector = adapter->q_vector[i];
- if (q_vector->rx_ring)
+ q_vector->tx.work_limit = adapter->tx_work_limit;
+ if (q_vector->rx.ring)
q_vector->itr_val = adapter->rx_itr_setting;
else
q_vector->itr_val = adapter->tx_itr_setting;
@@ -2207,14 +2181,6 @@ static const struct ethtool_ops igb_ethtool_ops = {
.set_ringparam = igb_set_ringparam,
.get_pauseparam = igb_get_pauseparam,
.set_pauseparam = igb_set_pauseparam,
- .get_rx_csum = igb_get_rx_csum,
- .set_rx_csum = igb_set_rx_csum,
- .get_tx_csum = igb_get_tx_csum,
- .set_tx_csum = igb_set_tx_csum,
- .get_sg = ethtool_op_get_sg,
- .set_sg = ethtool_op_set_sg,
- .get_tso = ethtool_op_get_tso,
- .set_tso = igb_set_tso,
.self_test = igb_diag_test,
.get_strings = igb_get_strings,
.set_phys_id = igb_set_phys_id,
diff --git a/drivers/net/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 2c28621eb30b..ced544499f1b 100644
--- a/drivers/net/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2009 Intel Corporation.
+ Copyright(c) 2007-2011 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -28,6 +28,7 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
+#include <linux/bitops.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
#include <linux/netdevice.h>
@@ -38,11 +39,15 @@
#include <linux/net_tstamp.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
+#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/pci.h>
#include <linux/pci-aspm.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/sctp.h>
#include <linux/if_ether.h>
#include <linux/aer.h>
#include <linux/prefetch.h>
@@ -52,11 +57,10 @@
#include "igb.h"
#define MAJ 3
-#define MIN 0
-#define BUILD 6
-#define KFIX 2
+#define MIN 2
+#define BUILD 10
#define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) "." \
-__stringify(BUILD) "-k" __stringify(KFIX)
+__stringify(BUILD) "-k"
char igb_driver_name[] = "igb";
char igb_driver_version[] = DRV_VERSION;
static const char igb_driver_string[] =
@@ -121,7 +125,7 @@ static void igb_set_rx_mode(struct net_device *);
static void igb_update_phy_info(unsigned long);
static void igb_watchdog(unsigned long);
static void igb_watchdog_task(struct work_struct *);
-static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb, struct net_device *);
+static netdev_tx_t igb_xmit_frame(struct sk_buff *skb, struct net_device *);
static struct rtnl_link_stats64 *igb_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats);
static int igb_change_mtu(struct net_device *, int);
@@ -135,13 +139,13 @@ static irqreturn_t igb_msix_ring(int irq, void *);
static void igb_update_dca(struct igb_q_vector *);
static void igb_setup_dca(struct igb_adapter *);
#endif /* CONFIG_IGB_DCA */
-static bool igb_clean_tx_irq(struct igb_q_vector *);
static int igb_poll(struct napi_struct *, int);
-static bool igb_clean_rx_irq_adv(struct igb_q_vector *, int *, int);
+static bool igb_clean_tx_irq(struct igb_q_vector *);
+static bool igb_clean_rx_irq(struct igb_q_vector *, int);
static int igb_ioctl(struct net_device *, struct ifreq *, int cmd);
static void igb_tx_timeout(struct net_device *);
static void igb_reset_task(struct work_struct *);
-static void igb_vlan_rx_register(struct net_device *, struct vlan_group *);
+static void igb_vlan_mode(struct net_device *netdev, u32 features);
static void igb_vlan_rx_add_vid(struct net_device *, u16);
static void igb_vlan_rx_kill_vid(struct net_device *, u16);
static void igb_restore_vlan(struct igb_adapter *);
@@ -159,6 +163,12 @@ static int igb_ndo_get_vf_config(struct net_device *netdev, int vf,
struct ifla_vf_info *ivi);
static void igb_check_vf_rate_limit(struct igb_adapter *);
+#ifdef CONFIG_PCI_IOV
+static int igb_vf_configure(struct igb_adapter *adapter, int vf);
+static int igb_find_enabled_vfs(struct igb_adapter *adapter);
+static int igb_check_vf_assignment(struct igb_adapter *adapter);
+#endif
+
#ifdef CONFIG_PM
static int igb_suspend(struct pci_dev *, pm_message_t);
static int igb_resume(struct pci_dev *);
@@ -194,6 +204,7 @@ static struct pci_error_handlers igb_err_handler = {
.resume = igb_io_resume,
};
+static void igb_init_dmac(struct igb_adapter *adapter, u32 pba);
static struct pci_driver igb_driver = {
.name = igb_driver_name,
@@ -334,15 +345,13 @@ static void igb_dump(struct igb_adapter *adapter)
struct net_device *netdev = adapter->netdev;
struct e1000_hw *hw = &adapter->hw;
struct igb_reg_info *reginfo;
- int n = 0;
struct igb_ring *tx_ring;
union e1000_adv_tx_desc *tx_desc;
struct my_u0 { u64 a; u64 b; } *u0;
- struct igb_buffer *buffer_info;
struct igb_ring *rx_ring;
union e1000_adv_rx_desc *rx_desc;
u32 staterr;
- int i = 0;
+ u16 i, n;
if (!netif_msg_hw(adapter))
return;
@@ -375,9 +384,10 @@ static void igb_dump(struct igb_adapter *adapter)
printk(KERN_INFO "Queue [NTU] [NTC] [bi(ntc)->dma ]"
" leng ntw timestamp\n");
for (n = 0; n < adapter->num_tx_queues; n++) {
+ struct igb_tx_buffer *buffer_info;
tx_ring = adapter->tx_ring[n];
- buffer_info = &tx_ring->buffer_info[tx_ring->next_to_clean];
- printk(KERN_INFO " %5d %5X %5X %016llX %04X %3X %016llX\n",
+ buffer_info = &tx_ring->tx_buffer_info[tx_ring->next_to_clean];
+ printk(KERN_INFO " %5d %5X %5X %016llX %04X %p %016llX\n",
n, tx_ring->next_to_use, tx_ring->next_to_clean,
(u64)buffer_info->dma,
buffer_info->length,
@@ -412,11 +422,12 @@ static void igb_dump(struct igb_adapter *adapter)
"leng ntw timestamp bi->skb\n");
for (i = 0; tx_ring->desc && (i < tx_ring->count); i++) {
- tx_desc = E1000_TX_DESC_ADV(*tx_ring, i);
- buffer_info = &tx_ring->buffer_info[i];
+ struct igb_tx_buffer *buffer_info;
+ tx_desc = IGB_TX_DESC(tx_ring, i);
+ buffer_info = &tx_ring->tx_buffer_info[i];
u0 = (struct my_u0 *)tx_desc;
printk(KERN_INFO "T [0x%03X] %016llX %016llX %016llX"
- " %04X %3X %016llX %p", i,
+ " %04X %p %016llX %p", i,
le64_to_cpu(u0->a),
le64_to_cpu(u0->b),
(u64)buffer_info->dma,
@@ -492,8 +503,9 @@ rx_ring_summary:
"<-- Adv Rx Write-Back format\n");
for (i = 0; i < rx_ring->count; i++) {
- buffer_info = &rx_ring->buffer_info[i];
- rx_desc = E1000_RX_DESC_ADV(*rx_ring, i);
+ struct igb_rx_buffer *buffer_info;
+ buffer_info = &rx_ring->rx_buffer_info[i];
+ rx_desc = IGB_RX_DESC(rx_ring, i);
u0 = (struct my_u0 *)rx_desc;
staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
if (staterr & E1000_RXD_STAT_DD) {
@@ -516,16 +528,14 @@ rx_ring_summary:
DUMP_PREFIX_ADDRESS,
16, 1,
phys_to_virt(buffer_info->dma),
- rx_ring->rx_buffer_len, true);
- if (rx_ring->rx_buffer_len
- < IGB_RXBUFFER_1024)
- print_hex_dump(KERN_INFO, "",
- DUMP_PREFIX_ADDRESS,
- 16, 1,
- phys_to_virt(
- buffer_info->page_dma +
- buffer_info->page_offset),
- PAGE_SIZE/2, true);
+ IGB_RX_HDR_LEN, true);
+ print_hex_dump(KERN_INFO, "",
+ DUMP_PREFIX_ADDRESS,
+ 16, 1,
+ phys_to_virt(
+ buffer_info->page_dma +
+ buffer_info->page_offset),
+ PAGE_SIZE/2, true);
}
}
@@ -560,7 +570,7 @@ static cycle_t igb_read_clock(const struct cyclecounter *tc)
* the lowest register is SYSTIMR instead of SYSTIML. However we never
* adjusted TIMINCA so SYSTIMR will just read as all 0s so ignore it.
*/
- if (hw->mac.type == e1000_82580) {
+ if (hw->mac.type >= e1000_82580) {
stamp = rd32(E1000_SYSTIMR) >> 8;
shift = IGB_82580_TSYNC_SHIFT;
}
@@ -683,61 +693,116 @@ static int igb_alloc_queues(struct igb_adapter *adapter)
{
struct igb_ring *ring;
int i;
+ int orig_node = adapter->node;
for (i = 0; i < adapter->num_tx_queues; i++) {
- ring = kzalloc(sizeof(struct igb_ring), GFP_KERNEL);
+ if (orig_node == -1) {
+ int cur_node = next_online_node(adapter->node);
+ if (cur_node == MAX_NUMNODES)
+ cur_node = first_online_node;
+ adapter->node = cur_node;
+ }
+ ring = kzalloc_node(sizeof(struct igb_ring), GFP_KERNEL,
+ adapter->node);
+ if (!ring)
+ ring = kzalloc(sizeof(struct igb_ring), GFP_KERNEL);
if (!ring)
goto err;
ring->count = adapter->tx_ring_count;
ring->queue_index = i;
ring->dev = &adapter->pdev->dev;
ring->netdev = adapter->netdev;
+ ring->numa_node = adapter->node;
/* For 82575, context index must be unique per ring. */
if (adapter->hw.mac.type == e1000_82575)
- ring->flags = IGB_RING_FLAG_TX_CTX_IDX;
+ set_bit(IGB_RING_FLAG_TX_CTX_IDX, &ring->flags);
adapter->tx_ring[i] = ring;
}
+ /* Restore the adapter's original node */
+ adapter->node = orig_node;
for (i = 0; i < adapter->num_rx_queues; i++) {
- ring = kzalloc(sizeof(struct igb_ring), GFP_KERNEL);
+ if (orig_node == -1) {
+ int cur_node = next_online_node(adapter->node);
+ if (cur_node == MAX_NUMNODES)
+ cur_node = first_online_node;
+ adapter->node = cur_node;
+ }
+ ring = kzalloc_node(sizeof(struct igb_ring), GFP_KERNEL,
+ adapter->node);
+ if (!ring)
+ ring = kzalloc(sizeof(struct igb_ring), GFP_KERNEL);
if (!ring)
goto err;
ring->count = adapter->rx_ring_count;
ring->queue_index = i;
ring->dev = &adapter->pdev->dev;
ring->netdev = adapter->netdev;
- ring->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE;
- ring->flags = IGB_RING_FLAG_RX_CSUM; /* enable rx checksum */
+ ring->numa_node = adapter->node;
/* set flag indicating ring supports SCTP checksum offload */
if (adapter->hw.mac.type >= e1000_82576)
- ring->flags |= IGB_RING_FLAG_RX_SCTP_CSUM;
+ set_bit(IGB_RING_FLAG_RX_SCTP_CSUM, &ring->flags);
+
+ /* On i350, loopback VLAN packets have the tag byte-swapped. */
+ if (adapter->hw.mac.type == e1000_i350)
+ set_bit(IGB_RING_FLAG_RX_LB_VLAN_BSWAP, &ring->flags);
+
adapter->rx_ring[i] = ring;
}
+ /* Restore the adapter's original node */
+ adapter->node = orig_node;
igb_cache_ring_register(adapter);
return 0;
err:
+ /* Restore the adapter's original node */
+ adapter->node = orig_node;
igb_free_queues(adapter);
return -ENOMEM;
}
+/**
+ * igb_write_ivar - configure ivar for given MSI-X vector
+ * @hw: pointer to the HW structure
+ * @msix_vector: vector number we are allocating to a given ring
+ * @index: row index of IVAR register to write within IVAR table
+ * @offset: column offset of in IVAR, should be multiple of 8
+ *
+ * This function is intended to handle the writing of the IVAR register
+ * for adapters 82576 and newer. The IVAR table consists of 2 columns,
+ * each containing an cause allocation for an Rx and Tx ring, and a
+ * variable number of rows depending on the number of queues supported.
+ **/
+static void igb_write_ivar(struct e1000_hw *hw, int msix_vector,
+ int index, int offset)
+{
+ u32 ivar = array_rd32(E1000_IVAR0, index);
+
+ /* clear any bits that are currently set */
+ ivar &= ~((u32)0xFF << offset);
+
+ /* write vector and valid bit */
+ ivar |= (msix_vector | E1000_IVAR_VALID) << offset;
+
+ array_wr32(E1000_IVAR0, index, ivar);
+}
+
#define IGB_N0_QUEUE -1
static void igb_assign_vector(struct igb_q_vector *q_vector, int msix_vector)
{
- u32 msixbm = 0;
struct igb_adapter *adapter = q_vector->adapter;
struct e1000_hw *hw = &adapter->hw;
- u32 ivar, index;
int rx_queue = IGB_N0_QUEUE;
int tx_queue = IGB_N0_QUEUE;
+ u32 msixbm = 0;
- if (q_vector->rx_ring)
- rx_queue = q_vector->rx_ring->reg_idx;
- if (q_vector->tx_ring)
- tx_queue = q_vector->tx_ring->reg_idx;
+ if (q_vector->rx.ring)
+ rx_queue = q_vector->rx.ring->reg_idx;
+ if (q_vector->tx.ring)
+ tx_queue = q_vector->tx.ring->reg_idx;
switch (hw->mac.type) {
case e1000_82575:
@@ -755,72 +820,39 @@ static void igb_assign_vector(struct igb_q_vector *q_vector, int msix_vector)
q_vector->eims_value = msixbm;
break;
case e1000_82576:
- /* 82576 uses a table-based method for assigning vectors.
- Each queue has a single entry in the table to which we write
- a vector number along with a "valid" bit. Sadly, the layout
- of the table is somewhat counterintuitive. */
- if (rx_queue > IGB_N0_QUEUE) {
- index = (rx_queue & 0x7);
- ivar = array_rd32(E1000_IVAR0, index);
- if (rx_queue < 8) {
- /* vector goes into low byte of register */
- ivar = ivar & 0xFFFFFF00;
- ivar |= msix_vector | E1000_IVAR_VALID;
- } else {
- /* vector goes into third byte of register */
- ivar = ivar & 0xFF00FFFF;
- ivar |= (msix_vector | E1000_IVAR_VALID) << 16;
- }
- array_wr32(E1000_IVAR0, index, ivar);
- }
- if (tx_queue > IGB_N0_QUEUE) {
- index = (tx_queue & 0x7);
- ivar = array_rd32(E1000_IVAR0, index);
- if (tx_queue < 8) {
- /* vector goes into second byte of register */
- ivar = ivar & 0xFFFF00FF;
- ivar |= (msix_vector | E1000_IVAR_VALID) << 8;
- } else {
- /* vector goes into high byte of register */
- ivar = ivar & 0x00FFFFFF;
- ivar |= (msix_vector | E1000_IVAR_VALID) << 24;
- }
- array_wr32(E1000_IVAR0, index, ivar);
- }
+ /*
+ * 82576 uses a table that essentially consists of 2 columns
+ * with 8 rows. The ordering is column-major so we use the
+ * lower 3 bits as the row index, and the 4th bit as the
+ * column offset.
+ */
+ if (rx_queue > IGB_N0_QUEUE)
+ igb_write_ivar(hw, msix_vector,
+ rx_queue & 0x7,
+ (rx_queue & 0x8) << 1);
+ if (tx_queue > IGB_N0_QUEUE)
+ igb_write_ivar(hw, msix_vector,
+ tx_queue & 0x7,
+ ((tx_queue & 0x8) << 1) + 8);
q_vector->eims_value = 1 << msix_vector;
break;
case e1000_82580:
case e1000_i350:
- /* 82580 uses the same table-based approach as 82576 but has fewer
- entries as a result we carry over for queues greater than 4. */
- if (rx_queue > IGB_N0_QUEUE) {
- index = (rx_queue >> 1);
- ivar = array_rd32(E1000_IVAR0, index);
- if (rx_queue & 0x1) {
- /* vector goes into third byte of register */
- ivar = ivar & 0xFF00FFFF;
- ivar |= (msix_vector | E1000_IVAR_VALID) << 16;
- } else {
- /* vector goes into low byte of register */
- ivar = ivar & 0xFFFFFF00;
- ivar |= msix_vector | E1000_IVAR_VALID;
- }
- array_wr32(E1000_IVAR0, index, ivar);
- }
- if (tx_queue > IGB_N0_QUEUE) {
- index = (tx_queue >> 1);
- ivar = array_rd32(E1000_IVAR0, index);
- if (tx_queue & 0x1) {
- /* vector goes into high byte of register */
- ivar = ivar & 0x00FFFFFF;
- ivar |= (msix_vector | E1000_IVAR_VALID) << 24;
- } else {
- /* vector goes into second byte of register */
- ivar = ivar & 0xFFFF00FF;
- ivar |= (msix_vector | E1000_IVAR_VALID) << 8;
- }
- array_wr32(E1000_IVAR0, index, ivar);
- }
+ /*
+ * On 82580 and newer adapters the scheme is similar to 82576
+ * however instead of ordering column-major we have things
+ * ordered row-major. So we traverse the table by using
+ * bit 0 as the column offset, and the remaining bits as the
+ * row index.
+ */
+ if (rx_queue > IGB_N0_QUEUE)
+ igb_write_ivar(hw, msix_vector,
+ rx_queue >> 1,
+ (rx_queue & 0x1) << 4);
+ if (tx_queue > IGB_N0_QUEUE)
+ igb_write_ivar(hw, msix_vector,
+ tx_queue >> 1,
+ ((tx_queue & 0x1) << 4) + 8);
q_vector->eims_value = 1 << msix_vector;
break;
default:
@@ -920,15 +952,15 @@ static int igb_request_msix(struct igb_adapter *adapter)
q_vector->itr_register = hw->hw_addr + E1000_EITR(vector);
- if (q_vector->rx_ring && q_vector->tx_ring)
+ if (q_vector->rx.ring && q_vector->tx.ring)
sprintf(q_vector->name, "%s-TxRx-%u", netdev->name,
- q_vector->rx_ring->queue_index);
- else if (q_vector->tx_ring)
+ q_vector->rx.ring->queue_index);
+ else if (q_vector->tx.ring)
sprintf(q_vector->name, "%s-tx-%u", netdev->name,
- q_vector->tx_ring->queue_index);
- else if (q_vector->rx_ring)
+ q_vector->tx.ring->queue_index);
+ else if (q_vector->rx.ring)
sprintf(q_vector->name, "%s-rx-%u", netdev->name,
- q_vector->rx_ring->queue_index);
+ q_vector->rx.ring->queue_index);
else
sprintf(q_vector->name, "%s-unused", netdev->name);
@@ -1052,6 +1084,7 @@ msi_only:
kfree(adapter->vf_data);
adapter->vf_data = NULL;
wr32(E1000_IOVCTL, E1000_IOVCTL_REUSE_VFQ);
+ wrfl();
msleep(100);
dev_info(&adapter->pdev->dev, "IOV Disabled\n");
}
@@ -1083,9 +1116,24 @@ static int igb_alloc_q_vectors(struct igb_adapter *adapter)
struct igb_q_vector *q_vector;
struct e1000_hw *hw = &adapter->hw;
int v_idx;
+ int orig_node = adapter->node;
for (v_idx = 0; v_idx < adapter->num_q_vectors; v_idx++) {
- q_vector = kzalloc(sizeof(struct igb_q_vector), GFP_KERNEL);
+ if ((adapter->num_q_vectors == (adapter->num_rx_queues +
+ adapter->num_tx_queues)) &&
+ (adapter->num_rx_queues == v_idx))
+ adapter->node = orig_node;
+ if (orig_node == -1) {
+ int cur_node = next_online_node(adapter->node);
+ if (cur_node == MAX_NUMNODES)
+ cur_node = first_online_node;
+ adapter->node = cur_node;
+ }
+ q_vector = kzalloc_node(sizeof(struct igb_q_vector), GFP_KERNEL,
+ adapter->node);
+ if (!q_vector)
+ q_vector = kzalloc(sizeof(struct igb_q_vector),
+ GFP_KERNEL);
if (!q_vector)
goto err_out;
q_vector->adapter = adapter;
@@ -1094,9 +1142,14 @@ static int igb_alloc_q_vectors(struct igb_adapter *adapter)
netif_napi_add(adapter->netdev, &q_vector->napi, igb_poll, 64);
adapter->q_vector[v_idx] = q_vector;
}
+ /* Restore the adapter's original node */
+ adapter->node = orig_node;
+
return 0;
err_out:
+ /* Restore the adapter's original node */
+ adapter->node = orig_node;
igb_free_q_vectors(adapter);
return -ENOMEM;
}
@@ -1106,8 +1159,9 @@ static void igb_map_rx_ring_to_vector(struct igb_adapter *adapter,
{
struct igb_q_vector *q_vector = adapter->q_vector[v_idx];
- q_vector->rx_ring = adapter->rx_ring[ring_idx];
- q_vector->rx_ring->q_vector = q_vector;
+ q_vector->rx.ring = adapter->rx_ring[ring_idx];
+ q_vector->rx.ring->q_vector = q_vector;
+ q_vector->rx.count++;
q_vector->itr_val = adapter->rx_itr_setting;
if (q_vector->itr_val && q_vector->itr_val <= 3)
q_vector->itr_val = IGB_START_ITR;
@@ -1118,9 +1172,11 @@ static void igb_map_tx_ring_to_vector(struct igb_adapter *adapter,
{
struct igb_q_vector *q_vector = adapter->q_vector[v_idx];
- q_vector->tx_ring = adapter->tx_ring[ring_idx];
- q_vector->tx_ring->q_vector = q_vector;
+ q_vector->tx.ring = adapter->tx_ring[ring_idx];
+ q_vector->tx.ring->q_vector = q_vector;
+ q_vector->tx.count++;
q_vector->itr_val = adapter->tx_itr_setting;
+ q_vector->tx.work_limit = adapter->tx_work_limit;
if (q_vector->itr_val && q_vector->itr_val <= 3)
q_vector->itr_val = IGB_START_ITR;
}
@@ -1218,7 +1274,7 @@ static int igb_request_irq(struct igb_adapter *adapter)
goto request_done;
/* fall back to MSI */
igb_clear_interrupt_scheme(adapter);
- if (!pci_enable_msi(adapter->pdev))
+ if (!pci_enable_msi(pdev))
adapter->flags |= IGB_FLAG_HAS_MSI;
igb_free_all_tx_resources(adapter);
igb_free_all_rx_resources(adapter);
@@ -1240,12 +1296,12 @@ static int igb_request_irq(struct igb_adapter *adapter)
}
igb_setup_all_tx_resources(adapter);
igb_setup_all_rx_resources(adapter);
- } else {
- igb_assign_vector(adapter->q_vector[0], 0);
}
+ igb_assign_vector(adapter->q_vector[0], 0);
+
if (adapter->flags & IGB_FLAG_HAS_MSI) {
- err = request_irq(adapter->pdev->irq, igb_intr_msi, 0,
+ err = request_irq(pdev->irq, igb_intr_msi, 0,
netdev->name, adapter);
if (!err)
goto request_done;
@@ -1255,11 +1311,11 @@ static int igb_request_irq(struct igb_adapter *adapter)
adapter->flags &= ~IGB_FLAG_HAS_MSI;
}
- err = request_irq(adapter->pdev->irq, igb_intr, IRQF_SHARED,
+ err = request_irq(pdev->irq, igb_intr, IRQF_SHARED,
netdev->name, adapter);
if (err)
- dev_err(&adapter->pdev->dev, "Error %d getting interrupt\n",
+ dev_err(&pdev->dev, "Error %d getting interrupt\n",
err);
request_done:
@@ -1273,11 +1329,9 @@ static void igb_free_irq(struct igb_adapter *adapter)
free_irq(adapter->msix_entries[vector++].vector, adapter);
- for (i = 0; i < adapter->num_q_vectors; i++) {
- struct igb_q_vector *q_vector = adapter->q_vector[i];
+ for (i = 0; i < adapter->num_q_vectors; i++)
free_irq(adapter->msix_entries[vector++].vector,
- q_vector);
- }
+ adapter->q_vector[i]);
} else {
free_irq(adapter->pdev->irq, adapter);
}
@@ -1325,7 +1379,7 @@ static void igb_irq_enable(struct igb_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
if (adapter->msix_entries) {
- u32 ims = E1000_IMS_LSC | E1000_IMS_DOUTSYNC;
+ u32 ims = E1000_IMS_LSC | E1000_IMS_DOUTSYNC | E1000_IMS_DRSTA;
u32 regval = rd32(E1000_EIAC);
wr32(E1000_EIAC, regval | adapter->eims_enable_mask);
regval = rd32(E1000_EIAM);
@@ -1335,9 +1389,6 @@ static void igb_irq_enable(struct igb_adapter *adapter)
wr32(E1000_MBVFIMR, 0xFF);
ims |= E1000_IMS_VMMB;
}
- if (adapter->hw.mac.type == e1000_82580)
- ims |= E1000_IMS_DRSTA;
-
wr32(E1000_IMS, ims);
} else {
wr32(E1000_IMS, IMS_ENABLE_MASK |
@@ -1363,7 +1414,7 @@ static void igb_update_mng_vlan(struct igb_adapter *adapter)
if ((old_vid != (u16)IGB_MNG_VLAN_NONE) &&
(vid != old_vid) &&
- !vlan_group_get_device(adapter->vlgrp, old_vid)) {
+ !test_bit(old_vid, adapter->active_vlans)) {
/* remove VID from filter table */
igb_vfta_set(hw, old_vid, false);
}
@@ -1437,7 +1488,7 @@ static void igb_configure(struct igb_adapter *adapter)
* next_to_use != next_to_clean */
for (i = 0; i < adapter->num_rx_queues; i++) {
struct igb_ring *ring = adapter->rx_ring[i];
- igb_alloc_rx_buffers_adv(ring, igb_desc_unused(ring));
+ igb_alloc_rx_buffers(ring, igb_desc_unused(ring));
}
}
@@ -1479,10 +1530,9 @@ int igb_up(struct igb_adapter *adapter)
clear_bit(__IGB_DOWN, &adapter->state);
- for (i = 0; i < adapter->num_q_vectors; i++) {
- struct igb_q_vector *q_vector = adapter->q_vector[i];
- napi_enable(&q_vector->napi);
- }
+ for (i = 0; i < adapter->num_q_vectors; i++)
+ napi_enable(&(adapter->q_vector[i]->napi));
+
if (adapter->msix_entries)
igb_configure_msix(adapter);
else
@@ -1534,10 +1584,8 @@ void igb_down(struct igb_adapter *adapter)
wrfl();
msleep(10);
- for (i = 0; i < adapter->num_q_vectors; i++) {
- struct igb_q_vector *q_vector = adapter->q_vector[i];
- napi_disable(&q_vector->napi);
- }
+ for (i = 0; i < adapter->num_q_vectors; i++)
+ napi_disable(&(adapter->q_vector[i]->napi));
igb_irq_disable(adapter);
@@ -1681,63 +1729,8 @@ void igb_reset(struct igb_adapter *adapter)
if (hw->mac.ops.init_hw(hw))
dev_err(&pdev->dev, "Hardware Error\n");
- if (hw->mac.type > e1000_82580) {
- if (adapter->flags & IGB_FLAG_DMAC) {
- u32 reg;
-
- /*
- * DMA Coalescing high water mark needs to be higher
- * than * the * Rx threshold. The Rx threshold is
- * currently * pba - 6, so we * should use a high water
- * mark of pba * - 4. */
- hwm = (pba - 4) << 10;
-
- reg = (((pba-6) << E1000_DMACR_DMACTHR_SHIFT)
- & E1000_DMACR_DMACTHR_MASK);
-
- /* transition to L0x or L1 if available..*/
- reg |= (E1000_DMACR_DMAC_EN | E1000_DMACR_DMAC_LX_MASK);
-
- /* watchdog timer= +-1000 usec in 32usec intervals */
- reg |= (1000 >> 5);
- wr32(E1000_DMACR, reg);
-
- /* no lower threshold to disable coalescing(smart fifb)
- * -UTRESH=0*/
- wr32(E1000_DMCRTRH, 0);
- /* set hwm to PBA - 2 * max frame size */
- wr32(E1000_FCRTC, hwm);
-
- /*
- * This sets the time to wait before requesting tran-
- * sition to * low power state to number of usecs needed
- * to receive 1 512 * byte frame at gigabit line rate
- */
- reg = rd32(E1000_DMCTLX);
- reg |= IGB_DMCTLX_DCFLUSH_DIS;
-
- /* Delay 255 usec before entering Lx state. */
- reg |= 0xFF;
- wr32(E1000_DMCTLX, reg);
-
- /* free space in Tx packet buffer to wake from DMAC */
- wr32(E1000_DMCTXTH,
- (IGB_MIN_TXPBSIZE -
- (IGB_TX_BUF_4096 + adapter->max_frame_size))
- >> 6);
-
- /* make low power state decision controlled by DMAC */
- reg = rd32(E1000_PCIEMISC);
- reg |= E1000_PCIEMISC_LX_DECISION;
- wr32(E1000_PCIEMISC, reg);
- } /* end if IGB_FLAG_DMAC set */
- }
- if (hw->mac.type == e1000_82580) {
- u32 reg = rd32(E1000_PCIEMISC);
- wr32(E1000_PCIEMISC,
- reg & ~E1000_PCIEMISC_LX_DECISION);
- }
+ igb_init_dmac(adapter, pba);
if (!netif_running(adapter->netdev))
igb_power_down_link(adapter);
@@ -1749,19 +1742,41 @@ void igb_reset(struct igb_adapter *adapter)
igb_get_phy_info(hw);
}
+static u32 igb_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int igb_set_features(struct net_device *netdev, u32 features)
+{
+ u32 changed = netdev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ igb_vlan_mode(netdev, features);
+
+ return 0;
+}
+
static const struct net_device_ops igb_netdev_ops = {
.ndo_open = igb_open,
.ndo_stop = igb_close,
- .ndo_start_xmit = igb_xmit_frame_adv,
+ .ndo_start_xmit = igb_xmit_frame,
.ndo_get_stats64 = igb_get_stats64,
.ndo_set_rx_mode = igb_set_rx_mode,
- .ndo_set_multicast_list = igb_set_rx_mode,
.ndo_set_mac_address = igb_set_mac,
.ndo_change_mtu = igb_change_mtu,
.ndo_do_ioctl = igb_ioctl,
.ndo_tx_timeout = igb_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
- .ndo_vlan_rx_register = igb_vlan_rx_register,
.ndo_vlan_rx_add_vid = igb_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = igb_vlan_rx_kill_vid,
.ndo_set_vf_mac = igb_ndo_set_vf_mac,
@@ -1771,6 +1786,8 @@ static const struct net_device_ops igb_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = igb_netpoll,
#endif
+ .ndo_fix_features = igb_fix_features,
+ .ndo_set_features = igb_set_features,
};
/**
@@ -1843,7 +1860,7 @@ static int __devinit igb_probe(struct pci_dev *pdev,
err = -ENOMEM;
netdev = alloc_etherdev_mq(sizeof(struct igb_adapter),
- IGB_ABS_MAX_TX_QUEUES);
+ IGB_MAX_TX_QUEUES);
if (!netdev)
goto err_alloc_etherdev;
@@ -1910,30 +1927,44 @@ static int __devinit igb_probe(struct pci_dev *pdev,
dev_info(&pdev->dev,
"PHY reset is blocked due to SOL/IDER session.\n");
- netdev->features = NETIF_F_SG |
- NETIF_F_IP_CSUM |
- NETIF_F_HW_VLAN_TX |
- NETIF_F_HW_VLAN_RX |
- NETIF_F_HW_VLAN_FILTER;
-
- netdev->features |= NETIF_F_IPV6_CSUM;
- netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
- netdev->features |= NETIF_F_GRO;
-
- netdev->vlan_features |= NETIF_F_TSO;
- netdev->vlan_features |= NETIF_F_TSO6;
- netdev->vlan_features |= NETIF_F_IP_CSUM;
- netdev->vlan_features |= NETIF_F_IPV6_CSUM;
- netdev->vlan_features |= NETIF_F_SG;
+ /*
+ * features is initialized to 0 in allocation, it might have bits
+ * set by igb_sw_init so we should use an or instead of an
+ * assignment.
+ */
+ netdev->features |= NETIF_F_SG |
+ NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_RXHASH |
+ NETIF_F_RXCSUM |
+ NETIF_F_HW_VLAN_RX |
+ NETIF_F_HW_VLAN_TX;
+
+ /* copy netdev features into list of user selectable features */
+ netdev->hw_features |= netdev->features;
+
+ /* set this bit last since it cannot be part of hw_features */
+ netdev->features |= NETIF_F_HW_VLAN_FILTER;
+
+ netdev->vlan_features |= NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM |
+ NETIF_F_SG;
if (pci_using_dac) {
netdev->features |= NETIF_F_HIGHDMA;
netdev->vlan_features |= NETIF_F_HIGHDMA;
}
- if (hw->mac.type >= e1000_82576)
+ if (hw->mac.type >= e1000_82576) {
+ netdev->hw_features |= NETIF_F_SCTP_CSUM;
netdev->features |= NETIF_F_SCTP_CSUM;
+ }
+
+ netdev->priv_flags |= IFF_UNICAST_FLT;
adapter->en_mng_pt = igb_enable_mng_pass_thru(hw);
@@ -1985,7 +2016,7 @@ static int __devinit igb_probe(struct pci_dev *pdev,
if (hw->bus.func == 0)
hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
- else if (hw->mac.type == e1000_82580)
+ else if (hw->mac.type >= e1000_82580)
hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A +
NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1,
&eeprom_data);
@@ -2153,12 +2184,17 @@ static void __devexit igb_remove(struct pci_dev *pdev)
/* reclaim resources allocated to VFs */
if (adapter->vf_data) {
/* disable iov and allow time for transactions to clear */
- pci_disable_sriov(pdev);
- msleep(500);
+ if (!igb_check_vf_assignment(adapter)) {
+ pci_disable_sriov(pdev);
+ msleep(500);
+ } else {
+ dev_info(&pdev->dev, "VF(s) assigned to guests!\n");
+ }
kfree(adapter->vf_data);
adapter->vf_data = NULL;
wr32(E1000_IOVCTL, E1000_IOVCTL_REUSE_VFQ);
+ wrfl();
msleep(100);
dev_info(&pdev->dev, "IOV Disabled\n");
}
@@ -2170,6 +2206,7 @@ static void __devexit igb_remove(struct pci_dev *pdev)
pci_release_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM));
+ kfree(adapter->shadow_vfta);
free_netdev(netdev);
pci_disable_pcie_error_reporting(pdev);
@@ -2190,42 +2227,49 @@ static void __devinit igb_probe_vfs(struct igb_adapter * adapter)
{
#ifdef CONFIG_PCI_IOV
struct pci_dev *pdev = adapter->pdev;
+ int old_vfs = igb_find_enabled_vfs(adapter);
+ int i;
- if (adapter->vfs_allocated_count) {
- adapter->vf_data = kcalloc(adapter->vfs_allocated_count,
- sizeof(struct vf_data_storage),
- GFP_KERNEL);
- /* if allocation failed then we do not support SR-IOV */
- if (!adapter->vf_data) {
- adapter->vfs_allocated_count = 0;
- dev_err(&pdev->dev, "Unable to allocate memory for VF "
- "Data Storage\n");
- }
+ if (old_vfs) {
+ dev_info(&pdev->dev, "%d pre-allocated VFs found - override "
+ "max_vfs setting of %d\n", old_vfs, max_vfs);
+ adapter->vfs_allocated_count = old_vfs;
}
- if (pci_enable_sriov(pdev, adapter->vfs_allocated_count)) {
- kfree(adapter->vf_data);
- adapter->vf_data = NULL;
-#endif /* CONFIG_PCI_IOV */
+ if (!adapter->vfs_allocated_count)
+ return;
+
+ adapter->vf_data = kcalloc(adapter->vfs_allocated_count,
+ sizeof(struct vf_data_storage), GFP_KERNEL);
+ /* if allocation failed then we do not support SR-IOV */
+ if (!adapter->vf_data) {
adapter->vfs_allocated_count = 0;
-#ifdef CONFIG_PCI_IOV
- } else {
- unsigned char mac_addr[ETH_ALEN];
- int i;
- dev_info(&pdev->dev, "%d vfs allocated\n",
- adapter->vfs_allocated_count);
- for (i = 0; i < adapter->vfs_allocated_count; i++) {
- random_ether_addr(mac_addr);
- igb_set_vf_mac(adapter, i, mac_addr);
- }
- /* DMA Coalescing is not supported in IOV mode. */
- if (adapter->flags & IGB_FLAG_DMAC)
- adapter->flags &= ~IGB_FLAG_DMAC;
+ dev_err(&pdev->dev, "Unable to allocate memory for VF "
+ "Data Storage\n");
+ goto out;
}
+
+ if (!old_vfs) {
+ if (pci_enable_sriov(pdev, adapter->vfs_allocated_count))
+ goto err_out;
+ }
+ dev_info(&pdev->dev, "%d VFs allocated\n",
+ adapter->vfs_allocated_count);
+ for (i = 0; i < adapter->vfs_allocated_count; i++)
+ igb_vf_configure(adapter, i);
+
+ /* DMA Coalescing is not supported in IOV mode. */
+ adapter->flags &= ~IGB_FLAG_DMAC;
+ goto out;
+err_out:
+ kfree(adapter->vf_data);
+ adapter->vf_data = NULL;
+ adapter->vfs_allocated_count = 0;
+out:
+ return;
#endif /* CONFIG_PCI_IOV */
}
-
/**
* igb_init_hw_timer - Initialize hardware timer used with IEEE 1588 timestamp
* @adapter: board private structure to initialize
@@ -2348,14 +2392,23 @@ static int __devinit igb_sw_init(struct igb_adapter *adapter)
pci_read_config_word(pdev, PCI_COMMAND, &hw->bus.pci_cmd_word);
+ /* set default ring sizes */
adapter->tx_ring_count = IGB_DEFAULT_TXD;
adapter->rx_ring_count = IGB_DEFAULT_RXD;
+
+ /* set default ITR values */
adapter->rx_itr_setting = IGB_DEFAULT_ITR;
adapter->tx_itr_setting = IGB_DEFAULT_ITR;
- adapter->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
+ /* set default work limits */
+ adapter->tx_work_limit = IGB_DEFAULT_TX_WORK;
+
+ adapter->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN +
+ VLAN_HLEN;
adapter->min_frame_size = ETH_ZLEN + ETH_FCS_LEN;
+ adapter->node = -1;
+
spin_lock_init(&adapter->stats64_lock);
#ifdef CONFIG_PCI_IOV
switch (hw->mac.type) {
@@ -2386,6 +2439,11 @@ static int __devinit igb_sw_init(struct igb_adapter *adapter)
((adapter->rss_queues > 1) && (adapter->vfs_allocated_count > 6)))
adapter->flags |= IGB_FLAG_QUEUE_PAIRS;
+ /* Setup and initialize a copy of the hw vlan table array */
+ adapter->shadow_vfta = kzalloc(sizeof(u32) *
+ E1000_VLAN_FILTER_TBL_SIZE,
+ GFP_ATOMIC);
+
/* This call may decrease the number of queues */
if (igb_init_interrupt_scheme(adapter)) {
dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
@@ -2454,10 +2512,8 @@ static int igb_open(struct net_device *netdev)
/* From here on the code is the same as igb_up() */
clear_bit(__IGB_DOWN, &adapter->state);
- for (i = 0; i < adapter->num_q_vectors; i++) {
- struct igb_q_vector *q_vector = adapter->q_vector[i];
- napi_enable(&q_vector->napi);
- }
+ for (i = 0; i < adapter->num_q_vectors; i++)
+ napi_enable(&(adapter->q_vector[i]->napi));
/* Clear any pending interrupts. */
rd32(E1000_ICR);
@@ -2526,31 +2582,42 @@ static int igb_close(struct net_device *netdev)
int igb_setup_tx_resources(struct igb_ring *tx_ring)
{
struct device *dev = tx_ring->dev;
+ int orig_node = dev_to_node(dev);
int size;
- size = sizeof(struct igb_buffer) * tx_ring->count;
- tx_ring->buffer_info = vzalloc(size);
- if (!tx_ring->buffer_info)
+ size = sizeof(struct igb_tx_buffer) * tx_ring->count;
+ tx_ring->tx_buffer_info = vzalloc_node(size, tx_ring->numa_node);
+ if (!tx_ring->tx_buffer_info)
+ tx_ring->tx_buffer_info = vzalloc(size);
+ if (!tx_ring->tx_buffer_info)
goto err;
/* round up to nearest 4K */
tx_ring->size = tx_ring->count * sizeof(union e1000_adv_tx_desc);
tx_ring->size = ALIGN(tx_ring->size, 4096);
+ set_dev_node(dev, tx_ring->numa_node);
tx_ring->desc = dma_alloc_coherent(dev,
tx_ring->size,
&tx_ring->dma,
GFP_KERNEL);
+ set_dev_node(dev, orig_node);
+ if (!tx_ring->desc)
+ tx_ring->desc = dma_alloc_coherent(dev,
+ tx_ring->size,
+ &tx_ring->dma,
+ GFP_KERNEL);
if (!tx_ring->desc)
goto err;
tx_ring->next_to_use = 0;
tx_ring->next_to_clean = 0;
+
return 0;
err:
- vfree(tx_ring->buffer_info);
+ vfree(tx_ring->tx_buffer_info);
dev_err(dev,
"Unable to allocate memory for the transmit descriptor ring\n");
return -ENOMEM;
@@ -2579,10 +2646,6 @@ static int igb_setup_all_tx_resources(struct igb_adapter *adapter)
}
}
- for (i = 0; i < IGB_ABS_MAX_TX_QUEUES; i++) {
- int r_idx = i % adapter->num_tx_queues;
- adapter->multi_tx_table[i] = adapter->tx_ring[r_idx];
- }
return err;
}
@@ -2623,14 +2686,12 @@ void igb_configure_tx_ring(struct igb_adapter *adapter,
struct igb_ring *ring)
{
struct e1000_hw *hw = &adapter->hw;
- u32 txdctl;
+ u32 txdctl = 0;
u64 tdba = ring->dma;
int reg_idx = ring->reg_idx;
/* disable the queue */
- txdctl = rd32(E1000_TXDCTL(reg_idx));
- wr32(E1000_TXDCTL(reg_idx),
- txdctl & ~E1000_TXDCTL_QUEUE_ENABLE);
+ wr32(E1000_TXDCTL(reg_idx), 0);
wrfl();
mdelay(10);
@@ -2640,9 +2701,8 @@ void igb_configure_tx_ring(struct igb_adapter *adapter,
tdba & 0x00000000ffffffffULL);
wr32(E1000_TDBAH(reg_idx), tdba >> 32);
- ring->head = hw->hw_addr + E1000_TDH(reg_idx);
ring->tail = hw->hw_addr + E1000_TDT(reg_idx);
- writel(0, ring->head);
+ wr32(E1000_TDH(reg_idx), 0);
writel(0, ring->tail);
txdctl |= IGB_TX_PTHRESH;
@@ -2676,11 +2736,14 @@ static void igb_configure_tx(struct igb_adapter *adapter)
int igb_setup_rx_resources(struct igb_ring *rx_ring)
{
struct device *dev = rx_ring->dev;
+ int orig_node = dev_to_node(dev);
int size, desc_len;
- size = sizeof(struct igb_buffer) * rx_ring->count;
- rx_ring->buffer_info = vzalloc(size);
- if (!rx_ring->buffer_info)
+ size = sizeof(struct igb_rx_buffer) * rx_ring->count;
+ rx_ring->rx_buffer_info = vzalloc_node(size, rx_ring->numa_node);
+ if (!rx_ring->rx_buffer_info)
+ rx_ring->rx_buffer_info = vzalloc(size);
+ if (!rx_ring->rx_buffer_info)
goto err;
desc_len = sizeof(union e1000_adv_rx_desc);
@@ -2689,10 +2752,17 @@ int igb_setup_rx_resources(struct igb_ring *rx_ring)
rx_ring->size = rx_ring->count * desc_len;
rx_ring->size = ALIGN(rx_ring->size, 4096);
+ set_dev_node(dev, rx_ring->numa_node);
rx_ring->desc = dma_alloc_coherent(dev,
rx_ring->size,
&rx_ring->dma,
GFP_KERNEL);
+ set_dev_node(dev, orig_node);
+ if (!rx_ring->desc)
+ rx_ring->desc = dma_alloc_coherent(dev,
+ rx_ring->size,
+ &rx_ring->dma,
+ GFP_KERNEL);
if (!rx_ring->desc)
goto err;
@@ -2703,8 +2773,8 @@ int igb_setup_rx_resources(struct igb_ring *rx_ring)
return 0;
err:
- vfree(rx_ring->buffer_info);
- rx_ring->buffer_info = NULL;
+ vfree(rx_ring->rx_buffer_info);
+ rx_ring->rx_buffer_info = NULL;
dev_err(dev, "Unable to allocate memory for the receive descriptor"
" ring\n");
return -ENOMEM;
@@ -2925,13 +2995,15 @@ static void igb_rlpml_set(struct igb_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
u16 pf_id = adapter->vfs_allocated_count;
- if (adapter->vlgrp)
- max_frame_size += VLAN_TAG_SIZE;
-
- /* if vfs are enabled we set RLPML to the largest possible request
- * size and set the VMOLR RLPML to the size we need */
if (pf_id) {
igb_set_vf_rlpml(adapter, max_frame_size, pf_id);
+ /*
+ * If we're in VMDQ or SR-IOV mode, then set global RLPML
+ * to our max jumbo frame size, in case we need to enable
+ * jumbo frames on one of the rings later.
+ * This will not pass over-length frames into the default
+ * queue because it's gated by the VMOLR.RLPML.
+ */
max_frame_size = MAX_JUMBO_FRAME_SIZE;
}
@@ -2986,12 +3058,10 @@ void igb_configure_rx_ring(struct igb_adapter *adapter,
struct e1000_hw *hw = &adapter->hw;
u64 rdba = ring->dma;
int reg_idx = ring->reg_idx;
- u32 srrctl, rxdctl;
+ u32 srrctl = 0, rxdctl = 0;
/* disable the queue */
- rxdctl = rd32(E1000_RXDCTL(reg_idx));
- wr32(E1000_RXDCTL(reg_idx),
- rxdctl & ~E1000_RXDCTL_QUEUE_ENABLE);
+ wr32(E1000_RXDCTL(reg_idx), 0);
/* Set DMA base address registers */
wr32(E1000_RDBAL(reg_idx),
@@ -3001,29 +3071,19 @@ void igb_configure_rx_ring(struct igb_adapter *adapter,
ring->count * sizeof(union e1000_adv_rx_desc));
/* initialize head and tail */
- ring->head = hw->hw_addr + E1000_RDH(reg_idx);
ring->tail = hw->hw_addr + E1000_RDT(reg_idx);
- writel(0, ring->head);
+ wr32(E1000_RDH(reg_idx), 0);
writel(0, ring->tail);
/* set descriptor configuration */
- if (ring->rx_buffer_len < IGB_RXBUFFER_1024) {
- srrctl = ALIGN(ring->rx_buffer_len, 64) <<
- E1000_SRRCTL_BSIZEHDRSIZE_SHIFT;
+ srrctl = IGB_RX_HDR_LEN << E1000_SRRCTL_BSIZEHDRSIZE_SHIFT;
#if (PAGE_SIZE / 2) > IGB_RXBUFFER_16384
- srrctl |= IGB_RXBUFFER_16384 >>
- E1000_SRRCTL_BSIZEPKT_SHIFT;
+ srrctl |= IGB_RXBUFFER_16384 >> E1000_SRRCTL_BSIZEPKT_SHIFT;
#else
- srrctl |= (PAGE_SIZE / 2) >>
- E1000_SRRCTL_BSIZEPKT_SHIFT;
+ srrctl |= (PAGE_SIZE / 2) >> E1000_SRRCTL_BSIZEPKT_SHIFT;
#endif
- srrctl |= E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS;
- } else {
- srrctl = ALIGN(ring->rx_buffer_len, 1024) >>
- E1000_SRRCTL_BSIZEPKT_SHIFT;
- srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF;
- }
- if (hw->mac.type == e1000_82580)
+ srrctl |= E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS;
+ if (hw->mac.type >= e1000_82580)
srrctl |= E1000_SRRCTL_TIMESTAMP;
/* Only set Drop Enable if we are supporting multiple queues */
if (adapter->vfs_allocated_count || adapter->num_rx_queues > 1)
@@ -3034,13 +3094,12 @@ void igb_configure_rx_ring(struct igb_adapter *adapter,
/* set filtering for VMDQ pools */
igb_set_vmolr(adapter, reg_idx & 0x7, true);
- /* enable receive descriptor fetching */
- rxdctl = rd32(E1000_RXDCTL(reg_idx));
- rxdctl |= E1000_RXDCTL_QUEUE_ENABLE;
- rxdctl &= 0xFFF00000;
rxdctl |= IGB_RX_PTHRESH;
rxdctl |= IGB_RX_HTHRESH << 8;
rxdctl |= IGB_RX_WTHRESH << 16;
+
+ /* enable receive descriptor fetching */
+ rxdctl |= E1000_RXDCTL_QUEUE_ENABLE;
wr32(E1000_RXDCTL(reg_idx), rxdctl);
}
@@ -3077,8 +3136,8 @@ void igb_free_tx_resources(struct igb_ring *tx_ring)
{
igb_clean_tx_ring(tx_ring);
- vfree(tx_ring->buffer_info);
- tx_ring->buffer_info = NULL;
+ vfree(tx_ring->tx_buffer_info);
+ tx_ring->tx_buffer_info = NULL;
/* if not set, then don't free */
if (!tx_ring->desc)
@@ -3104,30 +3163,26 @@ static void igb_free_all_tx_resources(struct igb_adapter *adapter)
igb_free_tx_resources(adapter->tx_ring[i]);
}
-void igb_unmap_and_free_tx_resource(struct igb_ring *tx_ring,
- struct igb_buffer *buffer_info)
+void igb_unmap_and_free_tx_resource(struct igb_ring *ring,
+ struct igb_tx_buffer *tx_buffer)
{
- if (buffer_info->dma) {
- if (buffer_info->mapped_as_page)
- dma_unmap_page(tx_ring->dev,
- buffer_info->dma,
- buffer_info->length,
- DMA_TO_DEVICE);
- else
- dma_unmap_single(tx_ring->dev,
- buffer_info->dma,
- buffer_info->length,
- DMA_TO_DEVICE);
- buffer_info->dma = 0;
- }
- if (buffer_info->skb) {
- dev_kfree_skb_any(buffer_info->skb);
- buffer_info->skb = NULL;
+ if (tx_buffer->skb) {
+ dev_kfree_skb_any(tx_buffer->skb);
+ if (tx_buffer->dma)
+ dma_unmap_single(ring->dev,
+ tx_buffer->dma,
+ tx_buffer->length,
+ DMA_TO_DEVICE);
+ } else if (tx_buffer->dma) {
+ dma_unmap_page(ring->dev,
+ tx_buffer->dma,
+ tx_buffer->length,
+ DMA_TO_DEVICE);
}
- buffer_info->time_stamp = 0;
- buffer_info->length = 0;
- buffer_info->next_to_watch = 0;
- buffer_info->mapped_as_page = false;
+ tx_buffer->next_to_watch = NULL;
+ tx_buffer->skb = NULL;
+ tx_buffer->dma = 0;
+ /* buffer_info must be completely set up in the transmit path */
}
/**
@@ -3136,21 +3191,21 @@ void igb_unmap_and_free_tx_resource(struct igb_ring *tx_ring,
**/
static void igb_clean_tx_ring(struct igb_ring *tx_ring)
{
- struct igb_buffer *buffer_info;
+ struct igb_tx_buffer *buffer_info;
unsigned long size;
- unsigned int i;
+ u16 i;
- if (!tx_ring->buffer_info)
+ if (!tx_ring->tx_buffer_info)
return;
/* Free all the Tx ring sk_buffs */
for (i = 0; i < tx_ring->count; i++) {
- buffer_info = &tx_ring->buffer_info[i];
+ buffer_info = &tx_ring->tx_buffer_info[i];
igb_unmap_and_free_tx_resource(tx_ring, buffer_info);
}
- size = sizeof(struct igb_buffer) * tx_ring->count;
- memset(tx_ring->buffer_info, 0, size);
+ size = sizeof(struct igb_tx_buffer) * tx_ring->count;
+ memset(tx_ring->tx_buffer_info, 0, size);
/* Zero out the descriptor ring */
memset(tx_ring->desc, 0, tx_ring->size);
@@ -3181,8 +3236,8 @@ void igb_free_rx_resources(struct igb_ring *rx_ring)
{
igb_clean_rx_ring(rx_ring);
- vfree(rx_ring->buffer_info);
- rx_ring->buffer_info = NULL;
+ vfree(rx_ring->rx_buffer_info);
+ rx_ring->rx_buffer_info = NULL;
/* if not set, then don't free */
if (!rx_ring->desc)
@@ -3214,20 +3269,19 @@ static void igb_free_all_rx_resources(struct igb_adapter *adapter)
**/
static void igb_clean_rx_ring(struct igb_ring *rx_ring)
{
- struct igb_buffer *buffer_info;
unsigned long size;
- unsigned int i;
+ u16 i;
- if (!rx_ring->buffer_info)
+ if (!rx_ring->rx_buffer_info)
return;
/* Free all the Rx ring sk_buffs */
for (i = 0; i < rx_ring->count; i++) {
- buffer_info = &rx_ring->buffer_info[i];
+ struct igb_rx_buffer *buffer_info = &rx_ring->rx_buffer_info[i];
if (buffer_info->dma) {
dma_unmap_single(rx_ring->dev,
buffer_info->dma,
- rx_ring->rx_buffer_len,
+ IGB_RX_HDR_LEN,
DMA_FROM_DEVICE);
buffer_info->dma = 0;
}
@@ -3250,8 +3304,8 @@ static void igb_clean_rx_ring(struct igb_ring *rx_ring)
}
}
- size = sizeof(struct igb_buffer) * rx_ring->count;
- memset(rx_ring->buffer_info, 0, size);
+ size = sizeof(struct igb_rx_buffer) * rx_ring->count;
+ memset(rx_ring->rx_buffer_info, 0, size);
/* Zero out the descriptor ring */
memset(rx_ring->desc, 0, rx_ring->size);
@@ -3673,16 +3727,14 @@ static void igb_watchdog_task(struct work_struct *work)
}
/* Force detection of hung controller every watchdog period */
- tx_ring->detect_tx_hung = true;
+ set_bit(IGB_RING_FLAG_TX_DETECT_HANG, &tx_ring->flags);
}
/* Cause software interrupt to ensure rx ring is cleaned */
if (adapter->msix_entries) {
u32 eics = 0;
- for (i = 0; i < adapter->num_q_vectors; i++) {
- struct igb_q_vector *q_vector = adapter->q_vector[i];
- eics |= q_vector->eims_value;
- }
+ for (i = 0; i < adapter->num_q_vectors; i++)
+ eics |= adapter->q_vector[i]->eims_value;
wr32(E1000_EICS, eics);
} else {
wr32(E1000_ICS, E1000_ICS_RXDMT0);
@@ -3724,33 +3776,24 @@ static void igb_update_ring_itr(struct igb_q_vector *q_vector)
int new_val = q_vector->itr_val;
int avg_wire_size = 0;
struct igb_adapter *adapter = q_vector->adapter;
- struct igb_ring *ring;
unsigned int packets;
/* For non-gigabit speeds, just fix the interrupt rate at 4000
* ints/sec - ITR timer value of 120 ticks.
*/
if (adapter->link_speed != SPEED_1000) {
- new_val = 976;
+ new_val = IGB_4K_ITR;
goto set_itr_val;
}
- ring = q_vector->rx_ring;
- if (ring) {
- packets = ACCESS_ONCE(ring->total_packets);
-
- if (packets)
- avg_wire_size = ring->total_bytes / packets;
- }
+ packets = q_vector->rx.total_packets;
+ if (packets)
+ avg_wire_size = q_vector->rx.total_bytes / packets;
- ring = q_vector->tx_ring;
- if (ring) {
- packets = ACCESS_ONCE(ring->total_packets);
-
- if (packets)
- avg_wire_size = max_t(u32, avg_wire_size,
- ring->total_bytes / packets);
- }
+ packets = q_vector->tx.total_packets;
+ if (packets)
+ avg_wire_size = max_t(u32, avg_wire_size,
+ q_vector->tx.total_bytes / packets);
/* if avg_wire_size isn't set no work was done */
if (!avg_wire_size)
@@ -3768,9 +3811,11 @@ static void igb_update_ring_itr(struct igb_q_vector *q_vector)
else
new_val = avg_wire_size / 2;
- /* when in itr mode 3 do not exceed 20K ints/sec */
- if (adapter->rx_itr_setting == 3 && new_val < 196)
- new_val = 196;
+ /* conservative mode (itr 3) eliminates the lowest_latency setting */
+ if (new_val < IGB_20K_ITR &&
+ ((q_vector->rx.ring && adapter->rx_itr_setting == 3) ||
+ (!q_vector->rx.ring && adapter->tx_itr_setting == 3)))
+ new_val = IGB_20K_ITR;
set_itr_val:
if (new_val != q_vector->itr_val) {
@@ -3778,14 +3823,10 @@ set_itr_val:
q_vector->set_itr = 1;
}
clear_counts:
- if (q_vector->rx_ring) {
- q_vector->rx_ring->total_bytes = 0;
- q_vector->rx_ring->total_packets = 0;
- }
- if (q_vector->tx_ring) {
- q_vector->tx_ring->total_bytes = 0;
- q_vector->tx_ring->total_packets = 0;
- }
+ q_vector->rx.total_bytes = 0;
+ q_vector->rx.total_packets = 0;
+ q_vector->tx.total_bytes = 0;
+ q_vector->tx.total_packets = 0;
}
/**
@@ -3801,106 +3842,102 @@ clear_counts:
* parameter (see igb_param.c)
* NOTE: These calculations are only valid when operating in a single-
* queue environment.
- * @adapter: pointer to adapter
- * @itr_setting: current q_vector->itr_val
- * @packets: the number of packets during this measurement interval
- * @bytes: the number of bytes during this measurement interval
+ * @q_vector: pointer to q_vector
+ * @ring_container: ring info to update the itr for
**/
-static unsigned int igb_update_itr(struct igb_adapter *adapter, u16 itr_setting,
- int packets, int bytes)
+static void igb_update_itr(struct igb_q_vector *q_vector,
+ struct igb_ring_container *ring_container)
{
- unsigned int retval = itr_setting;
+ unsigned int packets = ring_container->total_packets;
+ unsigned int bytes = ring_container->total_bytes;
+ u8 itrval = ring_container->itr;
+ /* no packets, exit with status unchanged */
if (packets == 0)
- goto update_itr_done;
+ return;
- switch (itr_setting) {
+ switch (itrval) {
case lowest_latency:
/* handle TSO and jumbo frames */
if (bytes/packets > 8000)
- retval = bulk_latency;
+ itrval = bulk_latency;
else if ((packets < 5) && (bytes > 512))
- retval = low_latency;
+ itrval = low_latency;
break;
case low_latency: /* 50 usec aka 20000 ints/s */
if (bytes > 10000) {
/* this if handles the TSO accounting */
if (bytes/packets > 8000) {
- retval = bulk_latency;
+ itrval = bulk_latency;
} else if ((packets < 10) || ((bytes/packets) > 1200)) {
- retval = bulk_latency;
+ itrval = bulk_latency;
} else if ((packets > 35)) {
- retval = lowest_latency;
+ itrval = lowest_latency;
}
} else if (bytes/packets > 2000) {
- retval = bulk_latency;
+ itrval = bulk_latency;
} else if (packets <= 2 && bytes < 512) {
- retval = lowest_latency;
+ itrval = lowest_latency;
}
break;
case bulk_latency: /* 250 usec aka 4000 ints/s */
if (bytes > 25000) {
if (packets > 35)
- retval = low_latency;
+ itrval = low_latency;
} else if (bytes < 1500) {
- retval = low_latency;
+ itrval = low_latency;
}
break;
}
-update_itr_done:
- return retval;
+ /* clear work counters since we have the values we need */
+ ring_container->total_bytes = 0;
+ ring_container->total_packets = 0;
+
+ /* write updated itr to ring container */
+ ring_container->itr = itrval;
}
-static void igb_set_itr(struct igb_adapter *adapter)
+static void igb_set_itr(struct igb_q_vector *q_vector)
{
- struct igb_q_vector *q_vector = adapter->q_vector[0];
- u16 current_itr;
+ struct igb_adapter *adapter = q_vector->adapter;
u32 new_itr = q_vector->itr_val;
+ u8 current_itr = 0;
/* for non-gigabit speeds, just fix the interrupt rate at 4000 */
if (adapter->link_speed != SPEED_1000) {
current_itr = 0;
- new_itr = 4000;
+ new_itr = IGB_4K_ITR;
goto set_itr_now;
}
- adapter->rx_itr = igb_update_itr(adapter,
- adapter->rx_itr,
- q_vector->rx_ring->total_packets,
- q_vector->rx_ring->total_bytes);
+ igb_update_itr(q_vector, &q_vector->tx);
+ igb_update_itr(q_vector, &q_vector->rx);
- adapter->tx_itr = igb_update_itr(adapter,
- adapter->tx_itr,
- q_vector->tx_ring->total_packets,
- q_vector->tx_ring->total_bytes);
- current_itr = max(adapter->rx_itr, adapter->tx_itr);
+ current_itr = max(q_vector->rx.itr, q_vector->tx.itr);
/* conservative mode (itr 3) eliminates the lowest_latency setting */
- if (adapter->rx_itr_setting == 3 && current_itr == lowest_latency)
+ if (current_itr == lowest_latency &&
+ ((q_vector->rx.ring && adapter->rx_itr_setting == 3) ||
+ (!q_vector->rx.ring && adapter->tx_itr_setting == 3)))
current_itr = low_latency;
switch (current_itr) {
/* counts and packets in update_itr are dependent on these numbers */
case lowest_latency:
- new_itr = 56; /* aka 70,000 ints/sec */
+ new_itr = IGB_70K_ITR; /* 70,000 ints/sec */
break;
case low_latency:
- new_itr = 196; /* aka 20,000 ints/sec */
+ new_itr = IGB_20K_ITR; /* 20,000 ints/sec */
break;
case bulk_latency:
- new_itr = 980; /* aka 4,000 ints/sec */
+ new_itr = IGB_4K_ITR; /* 4,000 ints/sec */
break;
default:
break;
}
set_itr_now:
- q_vector->rx_ring->total_bytes = 0;
- q_vector->rx_ring->total_packets = 0;
- q_vector->tx_ring->total_bytes = 0;
- q_vector->tx_ring->total_packets = 0;
-
if (new_itr != q_vector->itr_val) {
/* this attempts to bias the interrupt rate towards Bulk
* by adding intermediate steps when interrupt rate is
@@ -3908,7 +3945,7 @@ set_itr_now:
new_itr = new_itr > q_vector->itr_val ?
max((new_itr * q_vector->itr_val) /
(new_itr + (q_vector->itr_val >> 2)),
- new_itr) :
+ new_itr) :
new_itr;
/* Don't write the value here; it resets the adapter's
* internal timer, and causes us to delay far longer than
@@ -3921,35 +3958,51 @@ set_itr_now:
}
}
-#define IGB_TX_FLAGS_CSUM 0x00000001
-#define IGB_TX_FLAGS_VLAN 0x00000002
-#define IGB_TX_FLAGS_TSO 0x00000004
-#define IGB_TX_FLAGS_IPV4 0x00000008
-#define IGB_TX_FLAGS_TSTAMP 0x00000010
-#define IGB_TX_FLAGS_VLAN_MASK 0xffff0000
-#define IGB_TX_FLAGS_VLAN_SHIFT 16
-
-static inline int igb_tso_adv(struct igb_ring *tx_ring,
- struct sk_buff *skb, u32 tx_flags, u8 *hdr_len)
+void igb_tx_ctxtdesc(struct igb_ring *tx_ring, u32 vlan_macip_lens,
+ u32 type_tucmd, u32 mss_l4len_idx)
{
struct e1000_adv_tx_context_desc *context_desc;
- unsigned int i;
- int err;
- struct igb_buffer *buffer_info;
- u32 info = 0, tu_cmd = 0;
- u32 mss_l4len_idx;
- u8 l4len;
+ u16 i = tx_ring->next_to_use;
+
+ context_desc = IGB_TX_CTXTDESC(tx_ring, i);
+
+ i++;
+ tx_ring->next_to_use = (i < tx_ring->count) ? i : 0;
+
+ /* set bits to identify this as an advanced context descriptor */
+ type_tucmd |= E1000_TXD_CMD_DEXT | E1000_ADVTXD_DTYP_CTXT;
+
+ /* For 82575, context index must be unique per ring. */
+ if (test_bit(IGB_RING_FLAG_TX_CTX_IDX, &tx_ring->flags))
+ mss_l4len_idx |= tx_ring->reg_idx << 4;
+
+ context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens);
+ context_desc->seqnum_seed = 0;
+ context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd);
+ context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx);
+}
+
+static int igb_tso(struct igb_ring *tx_ring,
+ struct igb_tx_buffer *first,
+ u8 *hdr_len)
+{
+ struct sk_buff *skb = first->skb;
+ u32 vlan_macip_lens, type_tucmd;
+ u32 mss_l4len_idx, l4len;
+
+ if (!skb_is_gso(skb))
+ return 0;
if (skb_header_cloned(skb)) {
- err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ int err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
if (err)
return err;
}
- l4len = tcp_hdrlen(skb);
- *hdr_len += l4len;
+ /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */
+ type_tucmd = E1000_ADVTXD_TUCMD_L4T_TCP;
- if (skb->protocol == htons(ETH_P_IP)) {
+ if (first->protocol == __constant_htons(ETH_P_IP)) {
struct iphdr *iph = ip_hdr(skb);
iph->tot_len = 0;
iph->check = 0;
@@ -3957,290 +4010,289 @@ static inline int igb_tso_adv(struct igb_ring *tx_ring,
iph->daddr, 0,
IPPROTO_TCP,
0);
+ type_tucmd |= E1000_ADVTXD_TUCMD_IPV4;
+ first->tx_flags |= IGB_TX_FLAGS_TSO |
+ IGB_TX_FLAGS_CSUM |
+ IGB_TX_FLAGS_IPV4;
} else if (skb_is_gso_v6(skb)) {
ipv6_hdr(skb)->payload_len = 0;
tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr,
0, IPPROTO_TCP, 0);
+ first->tx_flags |= IGB_TX_FLAGS_TSO |
+ IGB_TX_FLAGS_CSUM;
}
- i = tx_ring->next_to_use;
-
- buffer_info = &tx_ring->buffer_info[i];
- context_desc = E1000_TX_CTXTDESC_ADV(*tx_ring, i);
- /* VLAN MACLEN IPLEN */
- if (tx_flags & IGB_TX_FLAGS_VLAN)
- info |= (tx_flags & IGB_TX_FLAGS_VLAN_MASK);
- info |= (skb_network_offset(skb) << E1000_ADVTXD_MACLEN_SHIFT);
- *hdr_len += skb_network_offset(skb);
- info |= skb_network_header_len(skb);
- *hdr_len += skb_network_header_len(skb);
- context_desc->vlan_macip_lens = cpu_to_le32(info);
-
- /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */
- tu_cmd |= (E1000_TXD_CMD_DEXT | E1000_ADVTXD_DTYP_CTXT);
-
- if (skb->protocol == htons(ETH_P_IP))
- tu_cmd |= E1000_ADVTXD_TUCMD_IPV4;
- tu_cmd |= E1000_ADVTXD_TUCMD_L4T_TCP;
+ /* compute header lengths */
+ l4len = tcp_hdrlen(skb);
+ *hdr_len = skb_transport_offset(skb) + l4len;
- context_desc->type_tucmd_mlhl = cpu_to_le32(tu_cmd);
+ /* update gso size and bytecount with header size */
+ first->gso_segs = skb_shinfo(skb)->gso_segs;
+ first->bytecount += (first->gso_segs - 1) * *hdr_len;
/* MSS L4LEN IDX */
- mss_l4len_idx = (skb_shinfo(skb)->gso_size << E1000_ADVTXD_MSS_SHIFT);
- mss_l4len_idx |= (l4len << E1000_ADVTXD_L4LEN_SHIFT);
-
- /* For 82575, context index must be unique per ring. */
- if (tx_ring->flags & IGB_RING_FLAG_TX_CTX_IDX)
- mss_l4len_idx |= tx_ring->reg_idx << 4;
-
- context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx);
- context_desc->seqnum_seed = 0;
+ mss_l4len_idx = l4len << E1000_ADVTXD_L4LEN_SHIFT;
+ mss_l4len_idx |= skb_shinfo(skb)->gso_size << E1000_ADVTXD_MSS_SHIFT;
- buffer_info->time_stamp = jiffies;
- buffer_info->next_to_watch = i;
- buffer_info->dma = 0;
- i++;
- if (i == tx_ring->count)
- i = 0;
+ /* VLAN MACLEN IPLEN */
+ vlan_macip_lens = skb_network_header_len(skb);
+ vlan_macip_lens |= skb_network_offset(skb) << E1000_ADVTXD_MACLEN_SHIFT;
+ vlan_macip_lens |= first->tx_flags & IGB_TX_FLAGS_VLAN_MASK;
- tx_ring->next_to_use = i;
+ igb_tx_ctxtdesc(tx_ring, vlan_macip_lens, type_tucmd, mss_l4len_idx);
- return true;
+ return 1;
}
-static inline bool igb_tx_csum_adv(struct igb_ring *tx_ring,
- struct sk_buff *skb, u32 tx_flags)
+static void igb_tx_csum(struct igb_ring *tx_ring, struct igb_tx_buffer *first)
{
- struct e1000_adv_tx_context_desc *context_desc;
- struct device *dev = tx_ring->dev;
- struct igb_buffer *buffer_info;
- u32 info = 0, tu_cmd = 0;
- unsigned int i;
+ struct sk_buff *skb = first->skb;
+ u32 vlan_macip_lens = 0;
+ u32 mss_l4len_idx = 0;
+ u32 type_tucmd = 0;
- if ((skb->ip_summed == CHECKSUM_PARTIAL) ||
- (tx_flags & IGB_TX_FLAGS_VLAN)) {
- i = tx_ring->next_to_use;
- buffer_info = &tx_ring->buffer_info[i];
- context_desc = E1000_TX_CTXTDESC_ADV(*tx_ring, i);
+ if (skb->ip_summed != CHECKSUM_PARTIAL) {
+ if (!(first->tx_flags & IGB_TX_FLAGS_VLAN))
+ return;
+ } else {
+ u8 l4_hdr = 0;
+ switch (first->protocol) {
+ case __constant_htons(ETH_P_IP):
+ vlan_macip_lens |= skb_network_header_len(skb);
+ type_tucmd |= E1000_ADVTXD_TUCMD_IPV4;
+ l4_hdr = ip_hdr(skb)->protocol;
+ break;
+ case __constant_htons(ETH_P_IPV6):
+ vlan_macip_lens |= skb_network_header_len(skb);
+ l4_hdr = ipv6_hdr(skb)->nexthdr;
+ break;
+ default:
+ if (unlikely(net_ratelimit())) {
+ dev_warn(tx_ring->dev,
+ "partial checksum but proto=%x!\n",
+ first->protocol);
+ }
+ break;
+ }
- if (tx_flags & IGB_TX_FLAGS_VLAN)
- info |= (tx_flags & IGB_TX_FLAGS_VLAN_MASK);
+ switch (l4_hdr) {
+ case IPPROTO_TCP:
+ type_tucmd |= E1000_ADVTXD_TUCMD_L4T_TCP;
+ mss_l4len_idx = tcp_hdrlen(skb) <<
+ E1000_ADVTXD_L4LEN_SHIFT;
+ break;
+ case IPPROTO_SCTP:
+ type_tucmd |= E1000_ADVTXD_TUCMD_L4T_SCTP;
+ mss_l4len_idx = sizeof(struct sctphdr) <<
+ E1000_ADVTXD_L4LEN_SHIFT;
+ break;
+ case IPPROTO_UDP:
+ mss_l4len_idx = sizeof(struct udphdr) <<
+ E1000_ADVTXD_L4LEN_SHIFT;
+ break;
+ default:
+ if (unlikely(net_ratelimit())) {
+ dev_warn(tx_ring->dev,
+ "partial checksum but l4 proto=%x!\n",
+ l4_hdr);
+ }
+ break;
+ }
- info |= (skb_network_offset(skb) << E1000_ADVTXD_MACLEN_SHIFT);
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- info |= skb_network_header_len(skb);
+ /* update TX checksum flag */
+ first->tx_flags |= IGB_TX_FLAGS_CSUM;
+ }
- context_desc->vlan_macip_lens = cpu_to_le32(info);
+ vlan_macip_lens |= skb_network_offset(skb) << E1000_ADVTXD_MACLEN_SHIFT;
+ vlan_macip_lens |= first->tx_flags & IGB_TX_FLAGS_VLAN_MASK;
- tu_cmd |= (E1000_TXD_CMD_DEXT | E1000_ADVTXD_DTYP_CTXT);
+ igb_tx_ctxtdesc(tx_ring, vlan_macip_lens, type_tucmd, mss_l4len_idx);
+}
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- __be16 protocol;
+static __le32 igb_tx_cmd_type(u32 tx_flags)
+{
+ /* set type for advanced descriptor with frame checksum insertion */
+ __le32 cmd_type = cpu_to_le32(E1000_ADVTXD_DTYP_DATA |
+ E1000_ADVTXD_DCMD_IFCS |
+ E1000_ADVTXD_DCMD_DEXT);
- if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) {
- const struct vlan_ethhdr *vhdr =
- (const struct vlan_ethhdr*)skb->data;
+ /* set HW vlan bit if vlan is present */
+ if (tx_flags & IGB_TX_FLAGS_VLAN)
+ cmd_type |= cpu_to_le32(E1000_ADVTXD_DCMD_VLE);
- protocol = vhdr->h_vlan_encapsulated_proto;
- } else {
- protocol = skb->protocol;
- }
+ /* set timestamp bit if present */
+ if (tx_flags & IGB_TX_FLAGS_TSTAMP)
+ cmd_type |= cpu_to_le32(E1000_ADVTXD_MAC_TSTAMP);
- switch (protocol) {
- case cpu_to_be16(ETH_P_IP):
- tu_cmd |= E1000_ADVTXD_TUCMD_IPV4;
- if (ip_hdr(skb)->protocol == IPPROTO_TCP)
- tu_cmd |= E1000_ADVTXD_TUCMD_L4T_TCP;
- else if (ip_hdr(skb)->protocol == IPPROTO_SCTP)
- tu_cmd |= E1000_ADVTXD_TUCMD_L4T_SCTP;
- break;
- case cpu_to_be16(ETH_P_IPV6):
- /* XXX what about other V6 headers?? */
- if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP)
- tu_cmd |= E1000_ADVTXD_TUCMD_L4T_TCP;
- else if (ipv6_hdr(skb)->nexthdr == IPPROTO_SCTP)
- tu_cmd |= E1000_ADVTXD_TUCMD_L4T_SCTP;
- break;
- default:
- if (unlikely(net_ratelimit()))
- dev_warn(dev,
- "partial checksum but proto=%x!\n",
- skb->protocol);
- break;
- }
- }
+ /* set segmentation bits for TSO */
+ if (tx_flags & IGB_TX_FLAGS_TSO)
+ cmd_type |= cpu_to_le32(E1000_ADVTXD_DCMD_TSE);
- context_desc->type_tucmd_mlhl = cpu_to_le32(tu_cmd);
- context_desc->seqnum_seed = 0;
- if (tx_ring->flags & IGB_RING_FLAG_TX_CTX_IDX)
- context_desc->mss_l4len_idx =
- cpu_to_le32(tx_ring->reg_idx << 4);
+ return cmd_type;
+}
- buffer_info->time_stamp = jiffies;
- buffer_info->next_to_watch = i;
- buffer_info->dma = 0;
+static void igb_tx_olinfo_status(struct igb_ring *tx_ring,
+ union e1000_adv_tx_desc *tx_desc,
+ u32 tx_flags, unsigned int paylen)
+{
+ u32 olinfo_status = paylen << E1000_ADVTXD_PAYLEN_SHIFT;
- i++;
- if (i == tx_ring->count)
- i = 0;
- tx_ring->next_to_use = i;
+ /* 82575 requires a unique index per ring if any offload is enabled */
+ if ((tx_flags & (IGB_TX_FLAGS_CSUM | IGB_TX_FLAGS_VLAN)) &&
+ test_bit(IGB_RING_FLAG_TX_CTX_IDX, &tx_ring->flags))
+ olinfo_status |= tx_ring->reg_idx << 4;
- return true;
+ /* insert L4 checksum */
+ if (tx_flags & IGB_TX_FLAGS_CSUM) {
+ olinfo_status |= E1000_TXD_POPTS_TXSM << 8;
+
+ /* insert IPv4 checksum */
+ if (tx_flags & IGB_TX_FLAGS_IPV4)
+ olinfo_status |= E1000_TXD_POPTS_IXSM << 8;
}
- return false;
+
+ tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
}
-#define IGB_MAX_TXD_PWR 16
+/*
+ * The largest size we can write to the descriptor is 65535. In order to
+ * maintain a power of two alignment we have to limit ourselves to 32K.
+ */
+#define IGB_MAX_TXD_PWR 15
#define IGB_MAX_DATA_PER_TXD (1<<IGB_MAX_TXD_PWR)
-static inline int igb_tx_map_adv(struct igb_ring *tx_ring, struct sk_buff *skb,
- unsigned int first)
+static void igb_tx_map(struct igb_ring *tx_ring,
+ struct igb_tx_buffer *first,
+ const u8 hdr_len)
{
- struct igb_buffer *buffer_info;
- struct device *dev = tx_ring->dev;
- unsigned int hlen = skb_headlen(skb);
- unsigned int count = 0, i;
- unsigned int f;
- u16 gso_segs = skb_shinfo(skb)->gso_segs ?: 1;
-
- i = tx_ring->next_to_use;
-
- buffer_info = &tx_ring->buffer_info[i];
- BUG_ON(hlen >= IGB_MAX_DATA_PER_TXD);
- buffer_info->length = hlen;
- /* set time_stamp *before* dma to help avoid a possible race */
- buffer_info->time_stamp = jiffies;
- buffer_info->next_to_watch = i;
- buffer_info->dma = dma_map_single(dev, skb->data, hlen,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, buffer_info->dma))
+ struct sk_buff *skb = first->skb;
+ struct igb_tx_buffer *tx_buffer_info;
+ union e1000_adv_tx_desc *tx_desc;
+ dma_addr_t dma;
+ struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[0];
+ unsigned int data_len = skb->data_len;
+ unsigned int size = skb_headlen(skb);
+ unsigned int paylen = skb->len - hdr_len;
+ __le32 cmd_type;
+ u32 tx_flags = first->tx_flags;
+ u16 i = tx_ring->next_to_use;
+
+ tx_desc = IGB_TX_DESC(tx_ring, i);
+
+ igb_tx_olinfo_status(tx_ring, tx_desc, tx_flags, paylen);
+ cmd_type = igb_tx_cmd_type(tx_flags);
+
+ dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE);
+ if (dma_mapping_error(tx_ring->dev, dma))
goto dma_error;
- for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) {
- struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[f];
- unsigned int len = frag->size;
-
- count++;
- i++;
- if (i == tx_ring->count)
- i = 0;
-
- buffer_info = &tx_ring->buffer_info[i];
- BUG_ON(len >= IGB_MAX_DATA_PER_TXD);
- buffer_info->length = len;
- buffer_info->time_stamp = jiffies;
- buffer_info->next_to_watch = i;
- buffer_info->mapped_as_page = true;
- buffer_info->dma = dma_map_page(dev,
- frag->page,
- frag->page_offset,
- len,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, buffer_info->dma))
- goto dma_error;
-
- }
-
- tx_ring->buffer_info[i].skb = skb;
- tx_ring->buffer_info[i].tx_flags = skb_shinfo(skb)->tx_flags;
- /* multiply data chunks by size of headers */
- tx_ring->buffer_info[i].bytecount = ((gso_segs - 1) * hlen) + skb->len;
- tx_ring->buffer_info[i].gso_segs = gso_segs;
- tx_ring->buffer_info[first].next_to_watch = i;
-
- return ++count;
+ /* record length, and DMA address */
+ first->length = size;
+ first->dma = dma;
+ tx_desc->read.buffer_addr = cpu_to_le64(dma);
-dma_error:
- dev_err(dev, "TX DMA map failed\n");
+ for (;;) {
+ while (unlikely(size > IGB_MAX_DATA_PER_TXD)) {
+ tx_desc->read.cmd_type_len =
+ cmd_type | cpu_to_le32(IGB_MAX_DATA_PER_TXD);
- /* clear timestamp and dma mappings for failed buffer_info mapping */
- buffer_info->dma = 0;
- buffer_info->time_stamp = 0;
- buffer_info->length = 0;
- buffer_info->next_to_watch = 0;
- buffer_info->mapped_as_page = false;
+ i++;
+ tx_desc++;
+ if (i == tx_ring->count) {
+ tx_desc = IGB_TX_DESC(tx_ring, 0);
+ i = 0;
+ }
- /* clear timestamp and dma mappings for remaining portion of packet */
- while (count--) {
- if (i == 0)
- i = tx_ring->count;
- i--;
- buffer_info = &tx_ring->buffer_info[i];
- igb_unmap_and_free_tx_resource(tx_ring, buffer_info);
- }
+ dma += IGB_MAX_DATA_PER_TXD;
+ size -= IGB_MAX_DATA_PER_TXD;
- return 0;
-}
+ tx_desc->read.olinfo_status = 0;
+ tx_desc->read.buffer_addr = cpu_to_le64(dma);
+ }
-static inline void igb_tx_queue_adv(struct igb_ring *tx_ring,
- u32 tx_flags, int count, u32 paylen,
- u8 hdr_len)
-{
- union e1000_adv_tx_desc *tx_desc;
- struct igb_buffer *buffer_info;
- u32 olinfo_status = 0, cmd_type_len;
- unsigned int i = tx_ring->next_to_use;
+ if (likely(!data_len))
+ break;
- cmd_type_len = (E1000_ADVTXD_DTYP_DATA | E1000_ADVTXD_DCMD_IFCS |
- E1000_ADVTXD_DCMD_DEXT);
+ tx_desc->read.cmd_type_len = cmd_type | cpu_to_le32(size);
- if (tx_flags & IGB_TX_FLAGS_VLAN)
- cmd_type_len |= E1000_ADVTXD_DCMD_VLE;
+ i++;
+ tx_desc++;
+ if (i == tx_ring->count) {
+ tx_desc = IGB_TX_DESC(tx_ring, 0);
+ i = 0;
+ }
- if (tx_flags & IGB_TX_FLAGS_TSTAMP)
- cmd_type_len |= E1000_ADVTXD_MAC_TSTAMP;
+ size = skb_frag_size(frag);
+ data_len -= size;
- if (tx_flags & IGB_TX_FLAGS_TSO) {
- cmd_type_len |= E1000_ADVTXD_DCMD_TSE;
+ dma = skb_frag_dma_map(tx_ring->dev, frag, 0,
+ size, DMA_TO_DEVICE);
+ if (dma_mapping_error(tx_ring->dev, dma))
+ goto dma_error;
- /* insert tcp checksum */
- olinfo_status |= E1000_TXD_POPTS_TXSM << 8;
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ tx_buffer_info->length = size;
+ tx_buffer_info->dma = dma;
- /* insert ip checksum */
- if (tx_flags & IGB_TX_FLAGS_IPV4)
- olinfo_status |= E1000_TXD_POPTS_IXSM << 8;
+ tx_desc->read.olinfo_status = 0;
+ tx_desc->read.buffer_addr = cpu_to_le64(dma);
- } else if (tx_flags & IGB_TX_FLAGS_CSUM) {
- olinfo_status |= E1000_TXD_POPTS_TXSM << 8;
+ frag++;
}
- if ((tx_ring->flags & IGB_RING_FLAG_TX_CTX_IDX) &&
- (tx_flags & (IGB_TX_FLAGS_CSUM |
- IGB_TX_FLAGS_TSO |
- IGB_TX_FLAGS_VLAN)))
- olinfo_status |= tx_ring->reg_idx << 4;
+ /* write last descriptor with RS and EOP bits */
+ cmd_type |= cpu_to_le32(size) | cpu_to_le32(IGB_TXD_DCMD);
+ tx_desc->read.cmd_type_len = cmd_type;
- olinfo_status |= ((paylen - hdr_len) << E1000_ADVTXD_PAYLEN_SHIFT);
-
- do {
- buffer_info = &tx_ring->buffer_info[i];
- tx_desc = E1000_TX_DESC_ADV(*tx_ring, i);
- tx_desc->read.buffer_addr = cpu_to_le64(buffer_info->dma);
- tx_desc->read.cmd_type_len =
- cpu_to_le32(cmd_type_len | buffer_info->length);
- tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
- count--;
- i++;
- if (i == tx_ring->count)
- i = 0;
- } while (count > 0);
+ /* set the timestamp */
+ first->time_stamp = jiffies;
- tx_desc->read.cmd_type_len |= cpu_to_le32(IGB_ADVTXD_DCMD);
- /* Force memory writes to complete before letting h/w
- * know there are new descriptors to fetch. (Only
- * applicable for weak-ordered memory model archs,
- * such as IA-64). */
+ /*
+ * Force memory writes to complete before letting h/w know there
+ * are new descriptors to fetch. (Only applicable for weak-ordered
+ * memory model archs, such as IA-64).
+ *
+ * We also need this memory barrier to make certain all of the
+ * status bits have been updated before next_to_watch is written.
+ */
wmb();
+ /* set next_to_watch value indicating a packet is present */
+ first->next_to_watch = tx_desc;
+
+ i++;
+ if (i == tx_ring->count)
+ i = 0;
+
tx_ring->next_to_use = i;
+
writel(i, tx_ring->tail);
+
/* we need this if more than one processor can write to our tail
* at a time, it syncronizes IO on IA64/Altix systems */
mmiowb();
+
+ return;
+
+dma_error:
+ dev_err(tx_ring->dev, "TX DMA map failed\n");
+
+ /* clear dma mappings for failed tx_buffer_info map */
+ for (;;) {
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ igb_unmap_and_free_tx_resource(tx_ring, tx_buffer_info);
+ if (tx_buffer_info == first)
+ break;
+ if (i == 0)
+ i = tx_ring->count;
+ i--;
+ }
+
+ tx_ring->next_to_use = i;
}
-static int __igb_maybe_stop_tx(struct igb_ring *tx_ring, int size)
+static int __igb_maybe_stop_tx(struct igb_ring *tx_ring, const u16 size)
{
struct net_device *netdev = tx_ring->netdev;
@@ -4266,19 +4318,20 @@ static int __igb_maybe_stop_tx(struct igb_ring *tx_ring, int size)
return 0;
}
-static inline int igb_maybe_stop_tx(struct igb_ring *tx_ring, int size)
+static inline int igb_maybe_stop_tx(struct igb_ring *tx_ring, const u16 size)
{
if (igb_desc_unused(tx_ring) >= size)
return 0;
return __igb_maybe_stop_tx(tx_ring, size);
}
-netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb,
- struct igb_ring *tx_ring)
+netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
+ struct igb_ring *tx_ring)
{
- int tso = 0, count;
+ struct igb_tx_buffer *first;
+ int tso;
u32 tx_flags = 0;
- u16 first;
+ __be16 protocol = vlan_get_protocol(skb);
u8 hdr_len = 0;
/* need: 1 descriptor per page,
@@ -4291,6 +4344,12 @@ netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb,
return NETDEV_TX_BUSY;
}
+ /* record the location of the first descriptor for this packet */
+ first = &tx_ring->tx_buffer_info[tx_ring->next_to_use];
+ first->skb = skb;
+ first->bytecount = skb->len;
+ first->gso_segs = 1;
+
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
tx_flags |= IGB_TX_FLAGS_TSTAMP;
@@ -4301,51 +4360,44 @@ netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb,
tx_flags |= (vlan_tx_tag_get(skb) << IGB_TX_FLAGS_VLAN_SHIFT);
}
- if (skb->protocol == htons(ETH_P_IP))
- tx_flags |= IGB_TX_FLAGS_IPV4;
+ /* record initial flags and protocol */
+ first->tx_flags = tx_flags;
+ first->protocol = protocol;
- first = tx_ring->next_to_use;
- if (skb_is_gso(skb)) {
- tso = igb_tso_adv(tx_ring, skb, tx_flags, &hdr_len);
+ tso = igb_tso(tx_ring, first, &hdr_len);
+ if (tso < 0)
+ goto out_drop;
+ else if (!tso)
+ igb_tx_csum(tx_ring, first);
- if (tso < 0) {
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
- }
-
- if (tso)
- tx_flags |= IGB_TX_FLAGS_TSO;
- else if (igb_tx_csum_adv(tx_ring, skb, tx_flags) &&
- (skb->ip_summed == CHECKSUM_PARTIAL))
- tx_flags |= IGB_TX_FLAGS_CSUM;
-
- /*
- * count reflects descriptors mapped, if 0 or less then mapping error
- * has occurred and we need to rewind the descriptor queue
- */
- count = igb_tx_map_adv(tx_ring, skb, first);
- if (!count) {
- dev_kfree_skb_any(skb);
- tx_ring->buffer_info[first].time_stamp = 0;
- tx_ring->next_to_use = first;
- return NETDEV_TX_OK;
- }
-
- igb_tx_queue_adv(tx_ring, tx_flags, count, skb->len, hdr_len);
+ igb_tx_map(tx_ring, first, hdr_len);
/* Make sure there is space in the ring for the next send. */
igb_maybe_stop_tx(tx_ring, MAX_SKB_FRAGS + 4);
return NETDEV_TX_OK;
+
+out_drop:
+ igb_unmap_and_free_tx_resource(tx_ring, first);
+
+ return NETDEV_TX_OK;
+}
+
+static inline struct igb_ring *igb_tx_queue_mapping(struct igb_adapter *adapter,
+ struct sk_buff *skb)
+{
+ unsigned int r_idx = skb->queue_mapping;
+
+ if (r_idx >= adapter->num_tx_queues)
+ r_idx = r_idx % adapter->num_tx_queues;
+
+ return adapter->tx_ring[r_idx];
}
-static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb,
- struct net_device *netdev)
+static netdev_tx_t igb_xmit_frame(struct sk_buff *skb,
+ struct net_device *netdev)
{
struct igb_adapter *adapter = netdev_priv(netdev);
- struct igb_ring *tx_ring;
- int r_idx = 0;
if (test_bit(__IGB_DOWN, &adapter->state)) {
dev_kfree_skb_any(skb);
@@ -4357,14 +4409,17 @@ static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb,
return NETDEV_TX_OK;
}
- r_idx = skb->queue_mapping & (IGB_ABS_MAX_TX_QUEUES - 1);
- tx_ring = adapter->multi_tx_table[r_idx];
+ /*
+ * The minimum packet size with TCTL.PSP set is 17 so pad the skb
+ * in order to meet this minimum size requirement.
+ */
+ if (skb->len < 17) {
+ if (skb_padto(skb, 17))
+ return NETDEV_TX_OK;
+ skb->len = 17;
+ }
- /* This goes back to the question of how to logically map a tx queue
- * to a flow. Right now, performance is impacted slightly negatively
- * if using multiple tx queues. If the stack breaks away from a
- * single qdisc implementation, we can look at this again. */
- return igb_xmit_frame_ring_adv(skb, tx_ring);
+ return igb_xmit_frame_ring(skb, igb_tx_queue_mapping(adapter, skb));
}
/**
@@ -4379,7 +4434,7 @@ static void igb_tx_timeout(struct net_device *netdev)
/* Do the reset outside of interrupt context */
adapter->tx_timeout_count++;
- if (hw->mac.type == e1000_82580)
+ if (hw->mac.type >= e1000_82580)
hw->dev_spec._82575.global_device_reset = true;
schedule_work(&adapter->reset_task);
@@ -4427,14 +4482,14 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct pci_dev *pdev = adapter->pdev;
- int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
- u32 rx_buffer_len, i;
+ int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
if ((new_mtu < 68) || (max_frame > MAX_JUMBO_FRAME_SIZE)) {
dev_err(&pdev->dev, "Invalid MTU setting\n");
return -EINVAL;
}
+#define MAX_STD_JUMBO_FRAME_SIZE 9238
if (max_frame > MAX_STD_JUMBO_FRAME_SIZE) {
dev_err(&pdev->dev, "MTU > 9216 not supported.\n");
return -EINVAL;
@@ -4446,30 +4501,6 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu)
/* igb_down has a dependency on max_frame_size */
adapter->max_frame_size = max_frame;
- /* NOTE: netdev_alloc_skb reserves 16 bytes, and typically NET_IP_ALIGN
- * means we reserve 2 more, this pushes us to allocate from the next
- * larger slab size.
- * i.e. RXBUFFER_2048 --> size-4096 slab
- */
-
- if (adapter->hw.mac.type == e1000_82580)
- max_frame += IGB_TS_HDR_LEN;
-
- if (max_frame <= IGB_RXBUFFER_1024)
- rx_buffer_len = IGB_RXBUFFER_1024;
- else if (max_frame <= MAXIMUM_ETHERNET_VLAN_SIZE)
- rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE;
- else
- rx_buffer_len = IGB_RXBUFFER_128;
-
- if ((max_frame == ETH_FRAME_LEN + ETH_FCS_LEN + IGB_TS_HDR_LEN) ||
- (max_frame == MAXIMUM_ETHERNET_VLAN_SIZE + IGB_TS_HDR_LEN))
- rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE + IGB_TS_HDR_LEN;
-
- if ((adapter->hw.mac.type == e1000_82580) &&
- (rx_buffer_len == IGB_RXBUFFER_128))
- rx_buffer_len += IGB_RXBUFFER_64;
-
if (netif_running(netdev))
igb_down(adapter);
@@ -4477,9 +4508,6 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu)
netdev->mtu, new_mtu);
netdev->mtu = new_mtu;
- for (i = 0; i < adapter->num_rx_queues; i++)
- adapter->rx_ring[i]->rx_buffer_len = rx_buffer_len;
-
if (netif_running(netdev))
igb_up(adapter);
else
@@ -4712,12 +4740,6 @@ static irqreturn_t igb_msix_other(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1);
}
- if (adapter->vfs_allocated_count)
- wr32(E1000_IMS, E1000_IMS_LSC |
- E1000_IMS_VMMB |
- E1000_IMS_DOUTSYNC);
- else
- wr32(E1000_IMS, E1000_IMS_LSC | E1000_IMS_DOUTSYNC);
wr32(E1000_EIMS, adapter->eims_other);
return IRQ_HANDLED;
@@ -4737,7 +4759,7 @@ static void igb_write_itr(struct igb_q_vector *q_vector)
if (adapter->hw.mac.type == e1000_82575)
itr_val |= itr_val << 16;
else
- itr_val |= 0x8000000;
+ itr_val |= E1000_EITR_CNT_IGNR;
writel(itr_val, q_vector->itr_register);
q_vector->set_itr = 0;
@@ -4765,8 +4787,8 @@ static void igb_update_dca(struct igb_q_vector *q_vector)
if (q_vector->cpu == cpu)
goto out_no_update;
- if (q_vector->tx_ring) {
- int q = q_vector->tx_ring->reg_idx;
+ if (q_vector->tx.ring) {
+ int q = q_vector->tx.ring->reg_idx;
u32 dca_txctrl = rd32(E1000_DCA_TXCTRL(q));
if (hw->mac.type == e1000_82575) {
dca_txctrl &= ~E1000_DCA_TXCTRL_CPUID_MASK;
@@ -4779,8 +4801,8 @@ static void igb_update_dca(struct igb_q_vector *q_vector)
dca_txctrl |= E1000_DCA_TXCTRL_DESC_DCA_EN;
wr32(E1000_DCA_TXCTRL(q), dca_txctrl);
}
- if (q_vector->rx_ring) {
- int q = q_vector->rx_ring->reg_idx;
+ if (q_vector->rx.ring) {
+ int q = q_vector->rx.ring->reg_idx;
u32 dca_rxctrl = rd32(E1000_DCA_RXCTRL(q));
if (hw->mac.type == e1000_82575) {
dca_rxctrl &= ~E1000_DCA_RXCTRL_CPUID_MASK;
@@ -4864,6 +4886,109 @@ static int igb_notify_dca(struct notifier_block *nb, unsigned long event,
}
#endif /* CONFIG_IGB_DCA */
+#ifdef CONFIG_PCI_IOV
+static int igb_vf_configure(struct igb_adapter *adapter, int vf)
+{
+ unsigned char mac_addr[ETH_ALEN];
+ struct pci_dev *pdev = adapter->pdev;
+ struct e1000_hw *hw = &adapter->hw;
+ struct pci_dev *pvfdev;
+ unsigned int device_id;
+ u16 thisvf_devfn;
+
+ random_ether_addr(mac_addr);
+ igb_set_vf_mac(adapter, vf, mac_addr);
+
+ switch (adapter->hw.mac.type) {
+ case e1000_82576:
+ device_id = IGB_82576_VF_DEV_ID;
+ /* VF Stride for 82576 is 2 */
+ thisvf_devfn = (pdev->devfn + 0x80 + (vf << 1)) |
+ (pdev->devfn & 1);
+ break;
+ case e1000_i350:
+ device_id = IGB_I350_VF_DEV_ID;
+ /* VF Stride for I350 is 4 */
+ thisvf_devfn = (pdev->devfn + 0x80 + (vf << 2)) |
+ (pdev->devfn & 3);
+ break;
+ default:
+ device_id = 0;
+ thisvf_devfn = 0;
+ break;
+ }
+
+ pvfdev = pci_get_device(hw->vendor_id, device_id, NULL);
+ while (pvfdev) {
+ if (pvfdev->devfn == thisvf_devfn)
+ break;
+ pvfdev = pci_get_device(hw->vendor_id,
+ device_id, pvfdev);
+ }
+
+ if (pvfdev)
+ adapter->vf_data[vf].vfdev = pvfdev;
+ else
+ dev_err(&pdev->dev,
+ "Couldn't find pci dev ptr for VF %4.4x\n",
+ thisvf_devfn);
+ return pvfdev != NULL;
+}
+
+static int igb_find_enabled_vfs(struct igb_adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ struct pci_dev *pdev = adapter->pdev;
+ struct pci_dev *pvfdev;
+ u16 vf_devfn = 0;
+ u16 vf_stride;
+ unsigned int device_id;
+ int vfs_found = 0;
+
+ switch (adapter->hw.mac.type) {
+ case e1000_82576:
+ device_id = IGB_82576_VF_DEV_ID;
+ /* VF Stride for 82576 is 2 */
+ vf_stride = 2;
+ break;
+ case e1000_i350:
+ device_id = IGB_I350_VF_DEV_ID;
+ /* VF Stride for I350 is 4 */
+ vf_stride = 4;
+ break;
+ default:
+ device_id = 0;
+ vf_stride = 0;
+ break;
+ }
+
+ vf_devfn = pdev->devfn + 0x80;
+ pvfdev = pci_get_device(hw->vendor_id, device_id, NULL);
+ while (pvfdev) {
+ if (pvfdev->devfn == vf_devfn)
+ vfs_found++;
+ vf_devfn += vf_stride;
+ pvfdev = pci_get_device(hw->vendor_id,
+ device_id, pvfdev);
+ }
+
+ return vfs_found;
+}
+
+static int igb_check_vf_assignment(struct igb_adapter *adapter)
+{
+ int i;
+ for (i = 0; i < adapter->vfs_allocated_count; i++) {
+ if (adapter->vf_data[i].vfdev) {
+ if (adapter->vf_data[i].vfdev->dev_flags &
+ PCI_DEV_FLAGS_ASSIGNED)
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
static void igb_ping_all_vfs(struct igb_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
@@ -5061,7 +5186,6 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf)
}
adapter->vf_data[vf].vlans_enabled++;
- return 0;
}
} else {
if (i < E1000_VLVF_ARRAY_SIZE) {
@@ -5424,16 +5548,14 @@ static irqreturn_t igb_intr(int irq, void *data)
/* Interrupt Auto-Mask...upon reading ICR, interrupts are masked. No
* need for the IMC write */
u32 icr = rd32(E1000_ICR);
- if (!icr)
- return IRQ_NONE; /* Not our interrupt */
-
- igb_write_itr(q_vector);
/* IMS will not auto-mask if INT_ASSERTED is not set, and if it is
* not set, then the adapter didn't send an interrupt */
if (!(icr & E1000_ICR_INT_ASSERTED))
return IRQ_NONE;
+ igb_write_itr(q_vector);
+
if (icr & E1000_ICR_DRSTA)
schedule_work(&adapter->reset_task);
@@ -5454,15 +5576,15 @@ static irqreturn_t igb_intr(int irq, void *data)
return IRQ_HANDLED;
}
-static inline void igb_ring_irq_enable(struct igb_q_vector *q_vector)
+void igb_ring_irq_enable(struct igb_q_vector *q_vector)
{
struct igb_adapter *adapter = q_vector->adapter;
struct e1000_hw *hw = &adapter->hw;
- if ((q_vector->rx_ring && (adapter->rx_itr_setting & 3)) ||
- (!q_vector->rx_ring && (adapter->tx_itr_setting & 3))) {
- if (!adapter->msix_entries)
- igb_set_itr(adapter);
+ if ((q_vector->rx.ring && (adapter->rx_itr_setting & 3)) ||
+ (!q_vector->rx.ring && (adapter->tx_itr_setting & 3))) {
+ if ((adapter->num_q_vectors == 1) && !adapter->vf_data)
+ igb_set_itr(q_vector);
else
igb_update_ring_itr(q_vector);
}
@@ -5485,28 +5607,27 @@ static int igb_poll(struct napi_struct *napi, int budget)
struct igb_q_vector *q_vector = container_of(napi,
struct igb_q_vector,
napi);
- int tx_clean_complete = 1, work_done = 0;
+ bool clean_complete = true;
#ifdef CONFIG_IGB_DCA
if (q_vector->adapter->flags & IGB_FLAG_DCA_ENABLED)
igb_update_dca(q_vector);
#endif
- if (q_vector->tx_ring)
- tx_clean_complete = igb_clean_tx_irq(q_vector);
+ if (q_vector->tx.ring)
+ clean_complete = igb_clean_tx_irq(q_vector);
- if (q_vector->rx_ring)
- igb_clean_rx_irq_adv(q_vector, &work_done, budget);
+ if (q_vector->rx.ring)
+ clean_complete &= igb_clean_rx_irq(q_vector, budget);
- if (!tx_clean_complete)
- work_done = budget;
+ /* If all work not completed, return budget and keep polling */
+ if (!clean_complete)
+ return budget;
/* If not enough Rx work done, exit the polling mode */
- if (work_done < budget) {
- napi_complete(napi);
- igb_ring_irq_enable(q_vector);
- }
+ napi_complete(napi);
+ igb_ring_irq_enable(q_vector);
- return work_done;
+ return 0;
}
/**
@@ -5528,7 +5649,7 @@ static void igb_systim_to_hwtstamp(struct igb_adapter *adapter,
* The 82580 starts with 1ns at bit 0 in RX/TXSTMPL, shift this up to
* 24 to match clock shift we setup earlier.
*/
- if (adapter->hw.mac.type == e1000_82580)
+ if (adapter->hw.mac.type >= e1000_82580)
regval <<= IGB_82580_TSYNC_SHIFT;
ns = timecounter_cyc2time(&adapter->clock, regval);
@@ -5541,13 +5662,14 @@ static void igb_systim_to_hwtstamp(struct igb_adapter *adapter,
/**
* igb_tx_hwtstamp - utility function which checks for TX time stamp
* @q_vector: pointer to q_vector containing needed info
- * @buffer: pointer to igb_buffer structure
+ * @buffer: pointer to igb_tx_buffer structure
*
* If we were asked to do hardware stamping and such a time stamp is
* available, then it must have been for this skb here because we only
* allow only one such packet into the queue.
*/
-static void igb_tx_hwtstamp(struct igb_q_vector *q_vector, struct igb_buffer *buffer_info)
+static void igb_tx_hwtstamp(struct igb_q_vector *q_vector,
+ struct igb_tx_buffer *buffer_info)
{
struct igb_adapter *adapter = q_vector->adapter;
struct e1000_hw *hw = &adapter->hw;
@@ -5555,7 +5677,7 @@ static void igb_tx_hwtstamp(struct igb_q_vector *q_vector, struct igb_buffer *bu
u64 regval;
/* if skb does not support hw timestamp or TX stamp not valid exit */
- if (likely(!(buffer_info->tx_flags & SKBTX_HW_TSTAMP)) ||
+ if (likely(!(buffer_info->tx_flags & IGB_TX_FLAGS_TSTAMP)) ||
!(rd32(E1000_TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID))
return;
@@ -5574,70 +5696,109 @@ static void igb_tx_hwtstamp(struct igb_q_vector *q_vector, struct igb_buffer *bu
static bool igb_clean_tx_irq(struct igb_q_vector *q_vector)
{
struct igb_adapter *adapter = q_vector->adapter;
- struct igb_ring *tx_ring = q_vector->tx_ring;
- struct net_device *netdev = tx_ring->netdev;
- struct e1000_hw *hw = &adapter->hw;
- struct igb_buffer *buffer_info;
+ struct igb_ring *tx_ring = q_vector->tx.ring;
+ struct igb_tx_buffer *tx_buffer;
union e1000_adv_tx_desc *tx_desc, *eop_desc;
unsigned int total_bytes = 0, total_packets = 0;
- unsigned int i, eop, count = 0;
- bool cleaned = false;
-
- i = tx_ring->next_to_clean;
- eop = tx_ring->buffer_info[i].next_to_watch;
- eop_desc = E1000_TX_DESC_ADV(*tx_ring, eop);
-
- while ((eop_desc->wb.status & cpu_to_le32(E1000_TXD_STAT_DD)) &&
- (count < tx_ring->count)) {
- rmb(); /* read buffer_info after eop_desc status */
- for (cleaned = false; !cleaned; count++) {
- tx_desc = E1000_TX_DESC_ADV(*tx_ring, i);
- buffer_info = &tx_ring->buffer_info[i];
- cleaned = (i == eop);
-
- if (buffer_info->skb) {
- total_bytes += buffer_info->bytecount;
- /* gso_segs is currently only valid for tcp */
- total_packets += buffer_info->gso_segs;
- igb_tx_hwtstamp(q_vector, buffer_info);
- }
+ unsigned int budget = q_vector->tx.work_limit;
+ unsigned int i = tx_ring->next_to_clean;
+
+ if (test_bit(__IGB_DOWN, &adapter->state))
+ return true;
+
+ tx_buffer = &tx_ring->tx_buffer_info[i];
+ tx_desc = IGB_TX_DESC(tx_ring, i);
+ i -= tx_ring->count;
+
+ for (; budget; budget--) {
+ eop_desc = tx_buffer->next_to_watch;
+
+ /* prevent any other reads prior to eop_desc */
+ rmb();
+
+ /* if next_to_watch is not set then there is no work pending */
+ if (!eop_desc)
+ break;
- igb_unmap_and_free_tx_resource(tx_ring, buffer_info);
- tx_desc->wb.status = 0;
+ /* if DD is not set pending work has not been completed */
+ if (!(eop_desc->wb.status & cpu_to_le32(E1000_TXD_STAT_DD)))
+ break;
+
+ /* clear next_to_watch to prevent false hangs */
+ tx_buffer->next_to_watch = NULL;
+
+ /* update the statistics for this packet */
+ total_bytes += tx_buffer->bytecount;
+ total_packets += tx_buffer->gso_segs;
+
+ /* retrieve hardware timestamp */
+ igb_tx_hwtstamp(q_vector, tx_buffer);
+
+ /* free the skb */
+ dev_kfree_skb_any(tx_buffer->skb);
+ tx_buffer->skb = NULL;
+ /* unmap skb header data */
+ dma_unmap_single(tx_ring->dev,
+ tx_buffer->dma,
+ tx_buffer->length,
+ DMA_TO_DEVICE);
+
+ /* clear last DMA location and unmap remaining buffers */
+ while (tx_desc != eop_desc) {
+ tx_buffer->dma = 0;
+
+ tx_buffer++;
+ tx_desc++;
i++;
- if (i == tx_ring->count)
- i = 0;
+ if (unlikely(!i)) {
+ i -= tx_ring->count;
+ tx_buffer = tx_ring->tx_buffer_info;
+ tx_desc = IGB_TX_DESC(tx_ring, 0);
+ }
+
+ /* unmap any remaining paged data */
+ if (tx_buffer->dma) {
+ dma_unmap_page(tx_ring->dev,
+ tx_buffer->dma,
+ tx_buffer->length,
+ DMA_TO_DEVICE);
+ }
+ }
+
+ /* clear last DMA location */
+ tx_buffer->dma = 0;
+
+ /* move us one more past the eop_desc for start of next pkt */
+ tx_buffer++;
+ tx_desc++;
+ i++;
+ if (unlikely(!i)) {
+ i -= tx_ring->count;
+ tx_buffer = tx_ring->tx_buffer_info;
+ tx_desc = IGB_TX_DESC(tx_ring, 0);
}
- eop = tx_ring->buffer_info[i].next_to_watch;
- eop_desc = E1000_TX_DESC_ADV(*tx_ring, eop);
}
+ i += tx_ring->count;
tx_ring->next_to_clean = i;
+ u64_stats_update_begin(&tx_ring->tx_syncp);
+ tx_ring->tx_stats.bytes += total_bytes;
+ tx_ring->tx_stats.packets += total_packets;
+ u64_stats_update_end(&tx_ring->tx_syncp);
+ q_vector->tx.total_bytes += total_bytes;
+ q_vector->tx.total_packets += total_packets;
- if (unlikely(count &&
- netif_carrier_ok(netdev) &&
- igb_desc_unused(tx_ring) >= IGB_TX_QUEUE_WAKE)) {
- /* Make sure that anybody stopping the queue after this
- * sees the new next_to_clean.
- */
- smp_mb();
- if (__netif_subqueue_stopped(netdev, tx_ring->queue_index) &&
- !(test_bit(__IGB_DOWN, &adapter->state))) {
- netif_wake_subqueue(netdev, tx_ring->queue_index);
+ if (test_bit(IGB_RING_FLAG_TX_DETECT_HANG, &tx_ring->flags)) {
+ struct e1000_hw *hw = &adapter->hw;
- u64_stats_update_begin(&tx_ring->tx_syncp);
- tx_ring->tx_stats.restart_queue++;
- u64_stats_update_end(&tx_ring->tx_syncp);
- }
- }
+ eop_desc = tx_buffer->next_to_watch;
- if (tx_ring->detect_tx_hung) {
/* Detect a transmit hang in hardware, this serializes the
* check with the clearing of time_stamp and movement of i */
- tx_ring->detect_tx_hung = false;
- if (tx_ring->buffer_info[i].time_stamp &&
- time_after(jiffies, tx_ring->buffer_info[i].time_stamp +
+ clear_bit(IGB_RING_FLAG_TX_DETECT_HANG, &tx_ring->flags);
+ if (eop_desc &&
+ time_after(jiffies, tx_buffer->time_stamp +
(adapter->tx_timeout_factor * HZ)) &&
!(rd32(E1000_STATUS) & E1000_STATUS_TXOFF)) {
@@ -5651,69 +5812,73 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector)
" next_to_clean <%x>\n"
"buffer_info[next_to_clean]\n"
" time_stamp <%lx>\n"
- " next_to_watch <%x>\n"
+ " next_to_watch <%p>\n"
" jiffies <%lx>\n"
" desc.status <%x>\n",
tx_ring->queue_index,
- readl(tx_ring->head),
+ rd32(E1000_TDH(tx_ring->reg_idx)),
readl(tx_ring->tail),
tx_ring->next_to_use,
tx_ring->next_to_clean,
- tx_ring->buffer_info[eop].time_stamp,
- eop,
+ tx_buffer->time_stamp,
+ eop_desc,
jiffies,
eop_desc->wb.status);
- netif_stop_subqueue(netdev, tx_ring->queue_index);
+ netif_stop_subqueue(tx_ring->netdev,
+ tx_ring->queue_index);
+
+ /* we are about to reset, no point in enabling stuff */
+ return true;
}
}
- tx_ring->total_bytes += total_bytes;
- tx_ring->total_packets += total_packets;
- u64_stats_update_begin(&tx_ring->tx_syncp);
- tx_ring->tx_stats.bytes += total_bytes;
- tx_ring->tx_stats.packets += total_packets;
- u64_stats_update_end(&tx_ring->tx_syncp);
- return count < tx_ring->count;
-}
-/**
- * igb_receive_skb - helper function to handle rx indications
- * @q_vector: structure containing interrupt and ring information
- * @skb: packet to send up
- * @vlan_tag: vlan tag for packet
- **/
-static void igb_receive_skb(struct igb_q_vector *q_vector,
- struct sk_buff *skb,
- u16 vlan_tag)
-{
- struct igb_adapter *adapter = q_vector->adapter;
+ if (unlikely(total_packets &&
+ netif_carrier_ok(tx_ring->netdev) &&
+ igb_desc_unused(tx_ring) >= IGB_TX_QUEUE_WAKE)) {
+ /* Make sure that anybody stopping the queue after this
+ * sees the new next_to_clean.
+ */
+ smp_mb();
+ if (__netif_subqueue_stopped(tx_ring->netdev,
+ tx_ring->queue_index) &&
+ !(test_bit(__IGB_DOWN, &adapter->state))) {
+ netif_wake_subqueue(tx_ring->netdev,
+ tx_ring->queue_index);
- if (vlan_tag && adapter->vlgrp)
- vlan_gro_receive(&q_vector->napi, adapter->vlgrp,
- vlan_tag, skb);
- else
- napi_gro_receive(&q_vector->napi, skb);
+ u64_stats_update_begin(&tx_ring->tx_syncp);
+ tx_ring->tx_stats.restart_queue++;
+ u64_stats_update_end(&tx_ring->tx_syncp);
+ }
+ }
+
+ return !!budget;
}
-static inline void igb_rx_checksum_adv(struct igb_ring *ring,
- u32 status_err, struct sk_buff *skb)
+static inline void igb_rx_checksum(struct igb_ring *ring,
+ union e1000_adv_rx_desc *rx_desc,
+ struct sk_buff *skb)
{
skb_checksum_none_assert(skb);
- /* Ignore Checksum bit is set or checksum is disabled through ethtool */
- if (!(ring->flags & IGB_RING_FLAG_RX_CSUM) ||
- (status_err & E1000_RXD_STAT_IXSM))
+ /* Ignore Checksum bit is set */
+ if (igb_test_staterr(rx_desc, E1000_RXD_STAT_IXSM))
+ return;
+
+ /* Rx checksum disabled via ethtool */
+ if (!(ring->netdev->features & NETIF_F_RXCSUM))
return;
/* TCP/UDP checksum error bit is set */
- if (status_err &
- (E1000_RXDEXT_STATERR_TCPE | E1000_RXDEXT_STATERR_IPE)) {
+ if (igb_test_staterr(rx_desc,
+ E1000_RXDEXT_STATERR_TCPE |
+ E1000_RXDEXT_STATERR_IPE)) {
/*
* work around errata with sctp packets where the TCPE aka
* L4E bit is set incorrectly on 64 byte (60 byte w/o crc)
* packets, (aka let the stack check the crc32c)
*/
- if ((skb->len == 60) &&
- (ring->flags & IGB_RING_FLAG_RX_SCTP_CSUM)) {
+ if (!((skb->len == 60) &&
+ test_bit(IGB_RING_FLAG_RX_SCTP_CSUM, &ring->flags))) {
u64_stats_update_begin(&ring->rx_syncp);
ring->rx_stats.csum_err++;
u64_stats_update_end(&ring->rx_syncp);
@@ -5722,19 +5887,34 @@ static inline void igb_rx_checksum_adv(struct igb_ring *ring,
return;
}
/* It must be a TCP or UDP packet with a valid checksum */
- if (status_err & (E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS))
+ if (igb_test_staterr(rx_desc, E1000_RXD_STAT_TCPCS |
+ E1000_RXD_STAT_UDPCS))
skb->ip_summed = CHECKSUM_UNNECESSARY;
- dev_dbg(ring->dev, "cksum success: bits %08X\n", status_err);
+ dev_dbg(ring->dev, "cksum success: bits %08X\n",
+ le32_to_cpu(rx_desc->wb.upper.status_error));
+}
+
+static inline void igb_rx_hash(struct igb_ring *ring,
+ union e1000_adv_rx_desc *rx_desc,
+ struct sk_buff *skb)
+{
+ if (ring->netdev->features & NETIF_F_RXHASH)
+ skb->rxhash = le32_to_cpu(rx_desc->wb.lower.hi_dword.rss);
}
-static void igb_rx_hwtstamp(struct igb_q_vector *q_vector, u32 staterr,
- struct sk_buff *skb)
+static void igb_rx_hwtstamp(struct igb_q_vector *q_vector,
+ union e1000_adv_rx_desc *rx_desc,
+ struct sk_buff *skb)
{
struct igb_adapter *adapter = q_vector->adapter;
struct e1000_hw *hw = &adapter->hw;
u64 regval;
+ if (!igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP |
+ E1000_RXDADV_STAT_TS))
+ return;
+
/*
* If this bit is set, then the RX registers contain the time stamp. No
* other packet will be time stamped until we read these registers, so
@@ -5746,7 +5926,7 @@ static void igb_rx_hwtstamp(struct igb_q_vector *q_vector, u32 staterr,
* If nothing went wrong, then it should have a shared tx_flags that we
* can turn into a skb_shared_hwtstamps.
*/
- if (staterr & E1000_RXDADV_STAT_TSIP) {
+ if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) {
u32 *stamp = (u32 *)skb->data;
regval = le32_to_cpu(*(stamp + 2));
regval |= (u64)le32_to_cpu(*(stamp + 3)) << 32;
@@ -5761,8 +5941,24 @@ static void igb_rx_hwtstamp(struct igb_q_vector *q_vector, u32 staterr,
igb_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval);
}
-static inline u16 igb_get_hlen(struct igb_ring *rx_ring,
- union e1000_adv_rx_desc *rx_desc)
+
+static void igb_rx_vlan(struct igb_ring *ring,
+ union e1000_adv_rx_desc *rx_desc,
+ struct sk_buff *skb)
+{
+ if (igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) {
+ u16 vid;
+ if (igb_test_staterr(rx_desc, E1000_RXDEXT_STATERR_LB) &&
+ test_bit(IGB_RING_FLAG_RX_LB_VLAN_BSWAP, &ring->flags))
+ vid = be16_to_cpu(rx_desc->wb.upper.vlan);
+ else
+ vid = le16_to_cpu(rx_desc->wb.upper.vlan);
+
+ __vlan_hwaccel_put_tag(skb, vid);
+ }
+}
+
+static inline u16 igb_get_hlen(union e1000_adv_rx_desc *rx_desc)
{
/* HW will not DMA in data larger than the given buffer, even if it
* parses the (NFS, of course) header to be larger. In that case, it
@@ -5770,246 +5966,244 @@ static inline u16 igb_get_hlen(struct igb_ring *rx_ring,
*/
u16 hlen = (le16_to_cpu(rx_desc->wb.lower.lo_dword.hdr_info) &
E1000_RXDADV_HDRBUFLEN_MASK) >> E1000_RXDADV_HDRBUFLEN_SHIFT;
- if (hlen > rx_ring->rx_buffer_len)
- hlen = rx_ring->rx_buffer_len;
+ if (hlen > IGB_RX_HDR_LEN)
+ hlen = IGB_RX_HDR_LEN;
return hlen;
}
-static bool igb_clean_rx_irq_adv(struct igb_q_vector *q_vector,
- int *work_done, int budget)
+static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget)
{
- struct igb_ring *rx_ring = q_vector->rx_ring;
- struct net_device *netdev = rx_ring->netdev;
- struct device *dev = rx_ring->dev;
- union e1000_adv_rx_desc *rx_desc , *next_rxd;
- struct igb_buffer *buffer_info , *next_buffer;
- struct sk_buff *skb;
- bool cleaned = false;
- int cleaned_count = 0;
- int current_node = numa_node_id();
+ struct igb_ring *rx_ring = q_vector->rx.ring;
+ union e1000_adv_rx_desc *rx_desc;
+ const int current_node = numa_node_id();
unsigned int total_bytes = 0, total_packets = 0;
- unsigned int i;
- u32 staterr;
- u16 length;
- u16 vlan_tag;
+ u16 cleaned_count = igb_desc_unused(rx_ring);
+ u16 i = rx_ring->next_to_clean;
- i = rx_ring->next_to_clean;
- buffer_info = &rx_ring->buffer_info[i];
- rx_desc = E1000_RX_DESC_ADV(*rx_ring, i);
- staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
+ rx_desc = IGB_RX_DESC(rx_ring, i);
- while (staterr & E1000_RXD_STAT_DD) {
- if (*work_done >= budget)
- break;
- (*work_done)++;
- rmb(); /* read descriptor and rx_buffer_info after status DD */
+ while (igb_test_staterr(rx_desc, E1000_RXD_STAT_DD)) {
+ struct igb_rx_buffer *buffer_info = &rx_ring->rx_buffer_info[i];
+ struct sk_buff *skb = buffer_info->skb;
+ union e1000_adv_rx_desc *next_rxd;
- skb = buffer_info->skb;
- prefetch(skb->data - NET_IP_ALIGN);
buffer_info->skb = NULL;
+ prefetch(skb->data);
i++;
if (i == rx_ring->count)
i = 0;
- next_rxd = E1000_RX_DESC_ADV(*rx_ring, i);
+ next_rxd = IGB_RX_DESC(rx_ring, i);
prefetch(next_rxd);
- next_buffer = &rx_ring->buffer_info[i];
- length = le16_to_cpu(rx_desc->wb.upper.length);
- cleaned = true;
- cleaned_count++;
+ /*
+ * This memory barrier is needed to keep us from reading
+ * any other fields out of the rx_desc until we know the
+ * RXD_STAT_DD bit is set
+ */
+ rmb();
- if (buffer_info->dma) {
- dma_unmap_single(dev, buffer_info->dma,
- rx_ring->rx_buffer_len,
+ if (!skb_is_nonlinear(skb)) {
+ __skb_put(skb, igb_get_hlen(rx_desc));
+ dma_unmap_single(rx_ring->dev, buffer_info->dma,
+ IGB_RX_HDR_LEN,
DMA_FROM_DEVICE);
buffer_info->dma = 0;
- if (rx_ring->rx_buffer_len >= IGB_RXBUFFER_1024) {
- skb_put(skb, length);
- goto send_up;
- }
- skb_put(skb, igb_get_hlen(rx_ring, rx_desc));
}
- if (length) {
- dma_unmap_page(dev, buffer_info->page_dma,
- PAGE_SIZE / 2, DMA_FROM_DEVICE);
- buffer_info->page_dma = 0;
+ if (rx_desc->wb.upper.length) {
+ u16 length = le16_to_cpu(rx_desc->wb.upper.length);
skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags,
buffer_info->page,
buffer_info->page_offset,
length);
+ skb->len += length;
+ skb->data_len += length;
+ skb->truesize += PAGE_SIZE / 2;
+
if ((page_count(buffer_info->page) != 1) ||
(page_to_nid(buffer_info->page) != current_node))
buffer_info->page = NULL;
else
get_page(buffer_info->page);
- skb->len += length;
- skb->data_len += length;
- skb->truesize += length;
+ dma_unmap_page(rx_ring->dev, buffer_info->page_dma,
+ PAGE_SIZE / 2, DMA_FROM_DEVICE);
+ buffer_info->page_dma = 0;
}
- if (!(staterr & E1000_RXD_STAT_EOP)) {
+ if (!igb_test_staterr(rx_desc, E1000_RXD_STAT_EOP)) {
+ struct igb_rx_buffer *next_buffer;
+ next_buffer = &rx_ring->rx_buffer_info[i];
buffer_info->skb = next_buffer->skb;
buffer_info->dma = next_buffer->dma;
next_buffer->skb = skb;
next_buffer->dma = 0;
goto next_desc;
}
-send_up:
- if (staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK) {
- dev_kfree_skb_irq(skb);
+
+ if (igb_test_staterr(rx_desc,
+ E1000_RXDEXT_ERR_FRAME_ERR_MASK)) {
+ dev_kfree_skb_any(skb);
goto next_desc;
}
- if (staterr & (E1000_RXDADV_STAT_TSIP | E1000_RXDADV_STAT_TS))
- igb_rx_hwtstamp(q_vector, staterr, skb);
+ igb_rx_hwtstamp(q_vector, rx_desc, skb);
+ igb_rx_hash(rx_ring, rx_desc, skb);
+ igb_rx_checksum(rx_ring, rx_desc, skb);
+ igb_rx_vlan(rx_ring, rx_desc, skb);
+
total_bytes += skb->len;
total_packets++;
- igb_rx_checksum_adv(rx_ring, staterr, skb);
-
- skb->protocol = eth_type_trans(skb, netdev);
- skb_record_rx_queue(skb, rx_ring->queue_index);
-
- vlan_tag = ((staterr & E1000_RXD_STAT_VP) ?
- le16_to_cpu(rx_desc->wb.upper.vlan) : 0);
+ skb->protocol = eth_type_trans(skb, rx_ring->netdev);
- igb_receive_skb(q_vector, skb, vlan_tag);
+ napi_gro_receive(&q_vector->napi, skb);
+ budget--;
next_desc:
- rx_desc->wb.upper.status_error = 0;
+ if (!budget)
+ break;
+ cleaned_count++;
/* return some buffers to hardware, one at a time is too slow */
if (cleaned_count >= IGB_RX_BUFFER_WRITE) {
- igb_alloc_rx_buffers_adv(rx_ring, cleaned_count);
+ igb_alloc_rx_buffers(rx_ring, cleaned_count);
cleaned_count = 0;
}
/* use prefetched values */
rx_desc = next_rxd;
- buffer_info = next_buffer;
- staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
}
rx_ring->next_to_clean = i;
- cleaned_count = igb_desc_unused(rx_ring);
-
- if (cleaned_count)
- igb_alloc_rx_buffers_adv(rx_ring, cleaned_count);
-
- rx_ring->total_packets += total_packets;
- rx_ring->total_bytes += total_bytes;
u64_stats_update_begin(&rx_ring->rx_syncp);
rx_ring->rx_stats.packets += total_packets;
rx_ring->rx_stats.bytes += total_bytes;
u64_stats_update_end(&rx_ring->rx_syncp);
- return cleaned;
+ q_vector->rx.total_packets += total_packets;
+ q_vector->rx.total_bytes += total_bytes;
+
+ if (cleaned_count)
+ igb_alloc_rx_buffers(rx_ring, cleaned_count);
+
+ return !!budget;
+}
+
+static bool igb_alloc_mapped_skb(struct igb_ring *rx_ring,
+ struct igb_rx_buffer *bi)
+{
+ struct sk_buff *skb = bi->skb;
+ dma_addr_t dma = bi->dma;
+
+ if (dma)
+ return true;
+
+ if (likely(!skb)) {
+ skb = netdev_alloc_skb_ip_align(rx_ring->netdev,
+ IGB_RX_HDR_LEN);
+ bi->skb = skb;
+ if (!skb) {
+ rx_ring->rx_stats.alloc_failed++;
+ return false;
+ }
+
+ /* initialize skb for ring */
+ skb_record_rx_queue(skb, rx_ring->queue_index);
+ }
+
+ dma = dma_map_single(rx_ring->dev, skb->data,
+ IGB_RX_HDR_LEN, DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(rx_ring->dev, dma)) {
+ rx_ring->rx_stats.alloc_failed++;
+ return false;
+ }
+
+ bi->dma = dma;
+ return true;
+}
+
+static bool igb_alloc_mapped_page(struct igb_ring *rx_ring,
+ struct igb_rx_buffer *bi)
+{
+ struct page *page = bi->page;
+ dma_addr_t page_dma = bi->page_dma;
+ unsigned int page_offset = bi->page_offset ^ (PAGE_SIZE / 2);
+
+ if (page_dma)
+ return true;
+
+ if (!page) {
+ page = netdev_alloc_page(rx_ring->netdev);
+ bi->page = page;
+ if (unlikely(!page)) {
+ rx_ring->rx_stats.alloc_failed++;
+ return false;
+ }
+ }
+
+ page_dma = dma_map_page(rx_ring->dev, page,
+ page_offset, PAGE_SIZE / 2,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(rx_ring->dev, page_dma)) {
+ rx_ring->rx_stats.alloc_failed++;
+ return false;
+ }
+
+ bi->page_dma = page_dma;
+ bi->page_offset = page_offset;
+ return true;
}
/**
- * igb_alloc_rx_buffers_adv - Replace used receive buffers; packet split
+ * igb_alloc_rx_buffers - Replace used receive buffers; packet split
* @adapter: address of board private structure
**/
-void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring, int cleaned_count)
+void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count)
{
- struct net_device *netdev = rx_ring->netdev;
union e1000_adv_rx_desc *rx_desc;
- struct igb_buffer *buffer_info;
- struct sk_buff *skb;
- unsigned int i;
- int bufsz;
+ struct igb_rx_buffer *bi;
+ u16 i = rx_ring->next_to_use;
- i = rx_ring->next_to_use;
- buffer_info = &rx_ring->buffer_info[i];
-
- bufsz = rx_ring->rx_buffer_len;
+ rx_desc = IGB_RX_DESC(rx_ring, i);
+ bi = &rx_ring->rx_buffer_info[i];
+ i -= rx_ring->count;
while (cleaned_count--) {
- rx_desc = E1000_RX_DESC_ADV(*rx_ring, i);
-
- if ((bufsz < IGB_RXBUFFER_1024) && !buffer_info->page_dma) {
- if (!buffer_info->page) {
- buffer_info->page = netdev_alloc_page(netdev);
- if (unlikely(!buffer_info->page)) {
- u64_stats_update_begin(&rx_ring->rx_syncp);
- rx_ring->rx_stats.alloc_failed++;
- u64_stats_update_end(&rx_ring->rx_syncp);
- goto no_buffers;
- }
- buffer_info->page_offset = 0;
- } else {
- buffer_info->page_offset ^= PAGE_SIZE / 2;
- }
- buffer_info->page_dma =
- dma_map_page(rx_ring->dev, buffer_info->page,
- buffer_info->page_offset,
- PAGE_SIZE / 2,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(rx_ring->dev,
- buffer_info->page_dma)) {
- buffer_info->page_dma = 0;
- u64_stats_update_begin(&rx_ring->rx_syncp);
- rx_ring->rx_stats.alloc_failed++;
- u64_stats_update_end(&rx_ring->rx_syncp);
- goto no_buffers;
- }
- }
+ if (!igb_alloc_mapped_skb(rx_ring, bi))
+ break;
- skb = buffer_info->skb;
- if (!skb) {
- skb = netdev_alloc_skb_ip_align(netdev, bufsz);
- if (unlikely(!skb)) {
- u64_stats_update_begin(&rx_ring->rx_syncp);
- rx_ring->rx_stats.alloc_failed++;
- u64_stats_update_end(&rx_ring->rx_syncp);
- goto no_buffers;
- }
+ /* Refresh the desc even if buffer_addrs didn't change
+ * because each write-back erases this info. */
+ rx_desc->read.hdr_addr = cpu_to_le64(bi->dma);
- buffer_info->skb = skb;
- }
- if (!buffer_info->dma) {
- buffer_info->dma = dma_map_single(rx_ring->dev,
- skb->data,
- bufsz,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(rx_ring->dev,
- buffer_info->dma)) {
- buffer_info->dma = 0;
- u64_stats_update_begin(&rx_ring->rx_syncp);
- rx_ring->rx_stats.alloc_failed++;
- u64_stats_update_end(&rx_ring->rx_syncp);
- goto no_buffers;
- }
- }
- /* Refresh the desc even if buffer_addrs didn't change because
- * each write-back erases this info. */
- if (bufsz < IGB_RXBUFFER_1024) {
- rx_desc->read.pkt_addr =
- cpu_to_le64(buffer_info->page_dma);
- rx_desc->read.hdr_addr = cpu_to_le64(buffer_info->dma);
- } else {
- rx_desc->read.pkt_addr = cpu_to_le64(buffer_info->dma);
- rx_desc->read.hdr_addr = 0;
- }
+ if (!igb_alloc_mapped_page(rx_ring, bi))
+ break;
+ rx_desc->read.pkt_addr = cpu_to_le64(bi->page_dma);
+
+ rx_desc++;
+ bi++;
i++;
- if (i == rx_ring->count)
- i = 0;
- buffer_info = &rx_ring->buffer_info[i];
+ if (unlikely(!i)) {
+ rx_desc = IGB_RX_DESC(rx_ring, 0);
+ bi = rx_ring->rx_buffer_info;
+ i -= rx_ring->count;
+ }
+
+ /* clear the hdr_addr for the next_to_use descriptor */
+ rx_desc->read.hdr_addr = 0;
}
-no_buffers:
+ i += rx_ring->count;
+
if (rx_ring->next_to_use != i) {
rx_ring->next_to_use = i;
- if (i == 0)
- i = (rx_ring->count - 1);
- else
- i--;
/* Force memory writes to complete before letting h/w
* know there are new descriptors to fetch. (Only
@@ -6146,6 +6340,7 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev,
tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_EVENT_V2;
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
is_l2 = true;
+ is_l4 = true;
break;
default:
return -ERANGE;
@@ -6162,7 +6357,7 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev,
* timestamped, so enable timestamping in all packets as
* long as one rx filter was configured.
*/
- if ((hw->mac.type == e1000_82580) && tsync_rx_ctl) {
+ if ((hw->mac.type >= e1000_82580) && tsync_rx_ctl) {
tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL;
}
@@ -6249,7 +6444,7 @@ s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
struct igb_adapter *adapter = hw->back;
u16 cap_offset;
- cap_offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP);
+ cap_offset = adapter->pdev->pcie_cap;
if (!cap_offset)
return -E1000_ERR_CONFIG;
@@ -6263,7 +6458,7 @@ s32 igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
struct igb_adapter *adapter = hw->back;
u16 cap_offset;
- cap_offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP);
+ cap_offset = adapter->pdev->pcie_cap;
if (!cap_offset)
return -E1000_ERR_CONFIG;
@@ -6272,17 +6467,14 @@ s32 igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
return 0;
}
-static void igb_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *grp)
+static void igb_vlan_mode(struct net_device *netdev, u32 features)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
u32 ctrl, rctl;
+ bool enable = !!(features & NETIF_F_HW_VLAN_RX);
- igb_irq_disable(adapter);
- adapter->vlgrp = grp;
-
- if (grp) {
+ if (enable) {
/* enable VLAN tag insert/strip */
ctrl = rd32(E1000_CTRL);
ctrl |= E1000_CTRL_VME;
@@ -6300,9 +6492,6 @@ static void igb_vlan_rx_register(struct net_device *netdev,
}
igb_rlpml_set(adapter);
-
- if (!test_bit(__IGB_DOWN, &adapter->state))
- igb_irq_enable(adapter);
}
static void igb_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
@@ -6316,6 +6505,8 @@ static void igb_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
/* add the filter since PF can receive vlans w/o entry in vlvf */
igb_vfta_set(hw, vid, true);
+
+ set_bit(vid, adapter->active_vlans);
}
static void igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
@@ -6325,32 +6516,24 @@ static void igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
int pf_id = adapter->vfs_allocated_count;
s32 err;
- igb_irq_disable(adapter);
- vlan_group_set_device(adapter->vlgrp, vid, NULL);
-
- if (!test_bit(__IGB_DOWN, &adapter->state))
- igb_irq_enable(adapter);
-
/* remove vlan from VLVF table array */
err = igb_vlvf_set(adapter, vid, false, pf_id);
/* if vid was not present in VLVF just remove it from table */
if (err)
igb_vfta_set(hw, vid, false);
+
+ clear_bit(vid, adapter->active_vlans);
}
static void igb_restore_vlan(struct igb_adapter *adapter)
{
- igb_vlan_rx_register(adapter->netdev, adapter->vlgrp);
+ u16 vid;
- if (adapter->vlgrp) {
- u16 vid;
- for (vid = 0; vid < VLAN_N_VID; vid++) {
- if (!vlan_group_get_device(adapter->vlgrp, vid))
- continue;
- igb_vlan_rx_add_vid(adapter->netdev, vid);
- }
- }
+ igb_vlan_mode(adapter->netdev, adapter->netdev->features);
+
+ for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
+ igb_vlan_rx_add_vid(adapter->netdev, vid);
}
int igb_set_spd_dplx(struct igb_adapter *adapter, u32 spd, u8 dplx)
@@ -6560,18 +6743,15 @@ static void igb_netpoll(struct net_device *netdev)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
+ struct igb_q_vector *q_vector;
int i;
- if (!adapter->msix_entries) {
- struct igb_q_vector *q_vector = adapter->q_vector[0];
- igb_irq_disable(adapter);
- napi_schedule(&q_vector->napi);
- return;
- }
-
for (i = 0; i < adapter->num_q_vectors; i++) {
- struct igb_q_vector *q_vector = adapter->q_vector[i];
- wr32(E1000_EIMC, q_vector->eims_value);
+ q_vector = adapter->q_vector[i];
+ if (adapter->msix_entries)
+ wr32(E1000_EIMC, q_vector->eims_value);
+ else
+ igb_irq_disable(adapter);
napi_schedule(&q_vector->napi);
}
}
@@ -6870,4 +7050,70 @@ static void igb_vmm_control(struct igb_adapter *adapter)
}
}
+static void igb_init_dmac(struct igb_adapter *adapter, u32 pba)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 dmac_thr;
+ u16 hwm;
+
+ if (hw->mac.type > e1000_82580) {
+ if (adapter->flags & IGB_FLAG_DMAC) {
+ u32 reg;
+
+ /* force threshold to 0. */
+ wr32(E1000_DMCTXTH, 0);
+
+ /*
+ * DMA Coalescing high water mark needs to be higher
+ * than the RX threshold. set hwm to PBA - 2 * max
+ * frame size
+ */
+ hwm = pba - (2 * adapter->max_frame_size);
+ reg = rd32(E1000_DMACR);
+ reg &= ~E1000_DMACR_DMACTHR_MASK;
+ dmac_thr = pba - 4;
+
+ reg |= ((dmac_thr << E1000_DMACR_DMACTHR_SHIFT)
+ & E1000_DMACR_DMACTHR_MASK);
+
+ /* transition to L0x or L1 if available..*/
+ reg |= (E1000_DMACR_DMAC_EN | E1000_DMACR_DMAC_LX_MASK);
+
+ /* watchdog timer= +-1000 usec in 32usec intervals */
+ reg |= (1000 >> 5);
+ wr32(E1000_DMACR, reg);
+
+ /*
+ * no lower threshold to disable
+ * coalescing(smart fifb)-UTRESH=0
+ */
+ wr32(E1000_DMCRTRH, 0);
+ wr32(E1000_FCRTC, hwm);
+
+ reg = (IGB_DMCTLX_DCFLUSH_DIS | 0x4);
+
+ wr32(E1000_DMCTLX, reg);
+
+ /*
+ * free space in tx packet buffer to wake from
+ * DMA coal
+ */
+ wr32(E1000_DMCTXTH, (IGB_MIN_TXPBSIZE -
+ (IGB_TX_BUF_4096 + adapter->max_frame_size)) >> 6);
+
+ /*
+ * make low power state decision controlled
+ * by DMA coal
+ */
+ reg = rd32(E1000_PCIEMISC);
+ reg &= ~E1000_PCIEMISC_LX_DECISION;
+ wr32(E1000_PCIEMISC, reg);
+ } /* endif adapter->dmac is not disabled */
+ } else if (hw->mac.type == e1000_82580) {
+ u32 reg = rd32(E1000_PCIEMISC);
+ wr32(E1000_PCIEMISC, reg & ~E1000_PCIEMISC_LX_DECISION);
+ wr32(E1000_DMACR, 0);
+ }
+}
+
/* igb_main.c */
diff --git a/drivers/net/igbvf/Makefile b/drivers/net/ethernet/intel/igbvf/Makefile
index 0fa3db3dd8b6..0fa3db3dd8b6 100644
--- a/drivers/net/igbvf/Makefile
+++ b/drivers/net/ethernet/intel/igbvf/Makefile
diff --git a/drivers/net/igbvf/defines.h b/drivers/net/ethernet/intel/igbvf/defines.h
index 79f2604673fe..79f2604673fe 100644
--- a/drivers/net/igbvf/defines.h
+++ b/drivers/net/ethernet/intel/igbvf/defines.h
diff --git a/drivers/net/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c
index b0b14d63dfbf..2c25858cc0ff 100644
--- a/drivers/net/igbvf/ethtool.c
+++ b/drivers/net/ethernet/intel/igbvf/ethtool.c
@@ -128,55 +128,6 @@ static int igbvf_set_pauseparam(struct net_device *netdev,
return -EOPNOTSUPP;
}
-static u32 igbvf_get_rx_csum(struct net_device *netdev)
-{
- struct igbvf_adapter *adapter = netdev_priv(netdev);
- return !(adapter->flags & IGBVF_FLAG_RX_CSUM_DISABLED);
-}
-
-static int igbvf_set_rx_csum(struct net_device *netdev, u32 data)
-{
- struct igbvf_adapter *adapter = netdev_priv(netdev);
-
- if (data)
- adapter->flags &= ~IGBVF_FLAG_RX_CSUM_DISABLED;
- else
- adapter->flags |= IGBVF_FLAG_RX_CSUM_DISABLED;
-
- return 0;
-}
-
-static u32 igbvf_get_tx_csum(struct net_device *netdev)
-{
- return (netdev->features & NETIF_F_IP_CSUM) != 0;
-}
-
-static int igbvf_set_tx_csum(struct net_device *netdev, u32 data)
-{
- if (data)
- netdev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
- else
- netdev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
- return 0;
-}
-
-static int igbvf_set_tso(struct net_device *netdev, u32 data)
-{
- struct igbvf_adapter *adapter = netdev_priv(netdev);
-
- if (data) {
- netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
- } else {
- netdev->features &= ~NETIF_F_TSO;
- netdev->features &= ~NETIF_F_TSO6;
- }
-
- dev_info(&adapter->pdev->dev, "TSO is %s\n",
- data ? "Enabled" : "Disabled");
- return 0;
-}
-
static u32 igbvf_get_msglevel(struct net_device *netdev)
{
struct igbvf_adapter *adapter = netdev_priv(netdev);
@@ -259,12 +210,8 @@ static void igbvf_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = IGBVF_MAX_RXD;
ring->tx_max_pending = IGBVF_MAX_TXD;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = rx_ring->count;
ring->tx_pending = tx_ring->count;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
static int igbvf_set_ringparam(struct net_device *netdev,
@@ -511,14 +458,6 @@ static const struct ethtool_ops igbvf_ethtool_ops = {
.set_ringparam = igbvf_set_ringparam,
.get_pauseparam = igbvf_get_pauseparam,
.set_pauseparam = igbvf_set_pauseparam,
- .get_rx_csum = igbvf_get_rx_csum,
- .set_rx_csum = igbvf_set_rx_csum,
- .get_tx_csum = igbvf_get_tx_csum,
- .set_tx_csum = igbvf_set_tx_csum,
- .get_sg = ethtool_op_get_sg,
- .set_sg = ethtool_op_set_sg,
- .get_tso = ethtool_op_get_tso,
- .set_tso = igbvf_set_tso,
.self_test = igbvf_diag_test,
.get_sset_count = igbvf_get_sset_count,
.get_strings = igbvf_get_strings,
diff --git a/drivers/net/igbvf/igbvf.h b/drivers/net/ethernet/intel/igbvf/igbvf.h
index d5dad5d607d6..fd4a7b780fdd 100644
--- a/drivers/net/igbvf/igbvf.h
+++ b/drivers/net/ethernet/intel/igbvf/igbvf.h
@@ -34,7 +34,7 @@
#include <linux/timer.h>
#include <linux/io.h>
#include <linux/netdevice.h>
-
+#include <linux/if_vlan.h>
#include "vf.h"
@@ -173,7 +173,7 @@ struct igbvf_adapter {
const struct igbvf_info *ei;
- struct vlan_group *vlgrp;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
u32 bd_number;
u32 rx_buffer_len;
u32 polling_interval;
diff --git a/drivers/net/igbvf/mbx.c b/drivers/net/ethernet/intel/igbvf/mbx.c
index 3d6f4cc3998a..048aae248d06 100644
--- a/drivers/net/igbvf/mbx.c
+++ b/drivers/net/ethernet/intel/igbvf/mbx.c
@@ -288,7 +288,7 @@ out_no_write:
* @msg: The message buffer
* @size: Length of buffer
*
- * returns SUCCESS if it successfuly read message from buffer
+ * returns SUCCESS if it successfully read message from buffer
**/
static s32 e1000_read_mbx_vf(struct e1000_hw *hw, u32 *msg, u16 size)
{
diff --git a/drivers/net/igbvf/mbx.h b/drivers/net/ethernet/intel/igbvf/mbx.h
index c2883c45d477..c2883c45d477 100644
--- a/drivers/net/igbvf/mbx.h
+++ b/drivers/net/ethernet/intel/igbvf/mbx.h
diff --git a/drivers/net/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c
index 1c77fb3bf4ae..cca78124be31 100644
--- a/drivers/net/igbvf/netdev.c
+++ b/drivers/net/ethernet/intel/igbvf/netdev.c
@@ -45,13 +45,13 @@
#include "igbvf.h"
-#define DRV_VERSION "1.0.8-k0"
+#define DRV_VERSION "2.0.1-k"
char igbvf_driver_name[] = "igbvf";
const char igbvf_driver_version[] = DRV_VERSION;
static const char igbvf_driver_string[] =
- "Intel(R) Virtual Function Network Driver";
+ "Intel(R) Gigabit Virtual Function Network Driver";
static const char igbvf_copyright[] =
- "Copyright (c) 2009 - 2010 Intel Corporation.";
+ "Copyright (c) 2009 - 2011 Intel Corporation.";
static int igbvf_poll(struct napi_struct *napi, int budget);
static void igbvf_reset(struct igbvf_adapter *);
@@ -100,12 +100,12 @@ static void igbvf_receive_skb(struct igbvf_adapter *adapter,
struct sk_buff *skb,
u32 status, u16 vlan)
{
- if (adapter->vlgrp && (status & E1000_RXD_STAT_VP))
- vlan_hwaccel_receive_skb(skb, adapter->vlgrp,
- le16_to_cpu(vlan) &
- E1000_RXD_SPC_VLAN_MASK);
- else
- netif_receive_skb(skb);
+ if (status & E1000_RXD_STAT_VP) {
+ u16 vid = le16_to_cpu(vlan) & E1000_RXD_SPC_VLAN_MASK;
+ if (test_bit(vid, adapter->active_vlans))
+ __vlan_hwaccel_put_tag(skb, vid);
+ }
+ netif_receive_skb(skb);
}
static inline void igbvf_rx_checksum_adv(struct igbvf_adapter *adapter,
@@ -312,7 +312,7 @@ static bool igbvf_clean_rx_irq(struct igbvf_adapter *adapter,
skb->len += length;
skb->data_len += length;
- skb->truesize += length;
+ skb->truesize += PAGE_SIZE / 2;
}
send_up:
i++;
@@ -1167,12 +1167,10 @@ static int igbvf_poll(struct napi_struct *napi, int budget)
*/
static void igbvf_set_rlpml(struct igbvf_adapter *adapter)
{
- int max_frame_size = adapter->max_frame_size;
+ int max_frame_size;
struct e1000_hw *hw = &adapter->hw;
- if (adapter->vlgrp)
- max_frame_size += VLAN_TAG_SIZE;
-
+ max_frame_size = adapter->max_frame_size + VLAN_TAG_SIZE;
e1000_rlpml_set_vf(hw, max_frame_size);
}
@@ -1183,6 +1181,8 @@ static void igbvf_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
if (hw->mac.ops.set_vfta(hw, vid, true))
dev_err(&adapter->pdev->dev, "Failed to add vlan id %d\n", vid);
+ else
+ set_bit(vid, adapter->active_vlans);
}
static void igbvf_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
@@ -1191,7 +1191,6 @@ static void igbvf_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
struct e1000_hw *hw = &adapter->hw;
igbvf_irq_disable(adapter);
- vlan_group_set_device(adapter->vlgrp, vid, NULL);
if (!test_bit(__IGBVF_DOWN, &adapter->state))
igbvf_irq_enable(adapter);
@@ -1199,30 +1198,16 @@ static void igbvf_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
if (hw->mac.ops.set_vfta(hw, vid, false))
dev_err(&adapter->pdev->dev,
"Failed to remove vlan id %d\n", vid);
-}
-
-static void igbvf_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *grp)
-{
- struct igbvf_adapter *adapter = netdev_priv(netdev);
-
- adapter->vlgrp = grp;
+ else
+ clear_bit(vid, adapter->active_vlans);
}
static void igbvf_restore_vlan(struct igbvf_adapter *adapter)
{
u16 vid;
- if (!adapter->vlgrp)
- return;
-
- for (vid = 0; vid < VLAN_N_VID; vid++) {
- if (!vlan_group_get_device(adapter->vlgrp, vid))
- continue;
+ for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
igbvf_vlan_rx_add_vid(adapter->netdev, vid);
- }
-
- igbvf_set_rlpml(adapter);
}
/**
@@ -1241,6 +1226,7 @@ static void igbvf_configure_tx(struct igbvf_adapter *adapter)
/* disable transmits */
txdctl = er32(TXDCTL(0));
ew32(TXDCTL(0), txdctl & ~E1000_TXDCTL_QUEUE_ENABLE);
+ e1e_flush();
msleep(10);
/* Setup the HW Tx Head and Tail descriptor pointers */
@@ -1321,6 +1307,7 @@ static void igbvf_configure_rx(struct igbvf_adapter *adapter)
/* disable receives */
rxdctl = er32(RXDCTL(0));
ew32(RXDCTL(0), rxdctl & ~E1000_RXDCTL_QUEUE_ENABLE);
+ e1e_flush();
msleep(10);
rdlen = rx_ring->count * sizeof(union e1000_adv_rx_desc);
@@ -2058,7 +2045,7 @@ static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter,
for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) {
- struct skb_frag_struct *frag;
+ const struct skb_frag_struct *frag;
count++;
i++;
@@ -2066,7 +2053,7 @@ static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter,
i = 0;
frag = &skb_shinfo(skb)->frags[f];
- len = frag->size;
+ len = skb_frag_size(frag);
buffer_info = &tx_ring->buffer_info[i];
BUG_ON(len >= IGBVF_MAX_DATA_PER_TXD);
@@ -2074,10 +2061,7 @@ static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter,
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
buffer_info->mapped_as_page = true;
- buffer_info->dma = dma_map_page(&pdev->dev,
- frag->page,
- frag->page_offset,
- len,
+ buffer_info->dma = skb_frag_dma_map(&pdev->dev, frag, 0, len,
DMA_TO_DEVICE);
if (dma_mapping_error(&pdev->dev, buffer_info->dma))
goto dma_error;
@@ -2203,7 +2187,7 @@ static netdev_tx_t igbvf_xmit_frame_ring_adv(struct sk_buff *skb,
return NETDEV_TX_BUSY;
}
- if (adapter->vlgrp && vlan_tx_tag_present(skb)) {
+ if (vlan_tx_tag_present(skb)) {
tx_flags |= IGBVF_TX_FLAGS_VLAN;
tx_flags |= (vlan_tx_tag_get(skb) << IGBVF_TX_FLAGS_VLAN_SHIFT);
}
@@ -2541,9 +2525,23 @@ static void igbvf_print_device_info(struct igbvf_adapter *adapter)
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
- dev_info(&pdev->dev, "Intel(R) 82576 Virtual Function\n");
+ if (hw->mac.type == e1000_vfadapt_i350)
+ dev_info(&pdev->dev, "Intel(R) I350 Virtual Function\n");
+ else
+ dev_info(&pdev->dev, "Intel(R) 82576 Virtual Function\n");
dev_info(&pdev->dev, "Address: %pM\n", netdev->dev_addr);
- dev_info(&pdev->dev, "MAC: %d\n", hw->mac.type);
+}
+
+static int igbvf_set_features(struct net_device *netdev, u32 features)
+{
+ struct igbvf_adapter *adapter = netdev_priv(netdev);
+
+ if (features & NETIF_F_RXCSUM)
+ adapter->flags &= ~IGBVF_FLAG_RX_CSUM_DISABLED;
+ else
+ adapter->flags |= IGBVF_FLAG_RX_CSUM_DISABLED;
+
+ return 0;
}
static const struct net_device_ops igbvf_netdev_ops = {
@@ -2551,17 +2549,17 @@ static const struct net_device_ops igbvf_netdev_ops = {
.ndo_stop = igbvf_close,
.ndo_start_xmit = igbvf_xmit_frame,
.ndo_get_stats = igbvf_get_stats,
- .ndo_set_multicast_list = igbvf_set_multi,
+ .ndo_set_rx_mode = igbvf_set_multi,
.ndo_set_mac_address = igbvf_set_mac,
.ndo_change_mtu = igbvf_change_mtu,
.ndo_do_ioctl = igbvf_ioctl,
.ndo_tx_timeout = igbvf_tx_timeout,
- .ndo_vlan_rx_register = igbvf_vlan_rx_register,
.ndo_vlan_rx_add_vid = igbvf_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = igbvf_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = igbvf_netpoll,
#endif
+ .ndo_set_features = igbvf_set_features,
};
/**
@@ -2669,16 +2667,18 @@ static int __devinit igbvf_probe(struct pci_dev *pdev,
adapter->bd_number = cards_found++;
- netdev->features = NETIF_F_SG |
+ netdev->hw_features = NETIF_F_SG |
NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_RXCSUM;
+
+ netdev->features = netdev->hw_features |
NETIF_F_HW_VLAN_TX |
NETIF_F_HW_VLAN_RX |
NETIF_F_HW_VLAN_FILTER;
- netdev->features |= NETIF_F_IPV6_CSUM;
- netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
-
if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA;
@@ -2866,7 +2866,7 @@ module_exit(igbvf_exit_module);
MODULE_AUTHOR("Intel Corporation, <e1000-devel@lists.sourceforge.net>");
-MODULE_DESCRIPTION("Intel(R) 82576 Virtual Function Network Driver");
+MODULE_DESCRIPTION("Intel(R) Gigabit Virtual Function Network Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/net/igbvf/regs.h b/drivers/net/ethernet/intel/igbvf/regs.h
index 77e18d3d6b15..77e18d3d6b15 100644
--- a/drivers/net/igbvf/regs.h
+++ b/drivers/net/ethernet/intel/igbvf/regs.h
diff --git a/drivers/net/igbvf/vf.c b/drivers/net/ethernet/intel/igbvf/vf.c
index af3822f9ea9a..af3822f9ea9a 100644
--- a/drivers/net/igbvf/vf.c
+++ b/drivers/net/ethernet/intel/igbvf/vf.c
diff --git a/drivers/net/igbvf/vf.h b/drivers/net/ethernet/intel/igbvf/vf.h
index d7ed58fcd9bb..d7ed58fcd9bb 100644
--- a/drivers/net/igbvf/vf.h
+++ b/drivers/net/ethernet/intel/igbvf/vf.h
diff --git a/drivers/net/ixgb/Makefile b/drivers/net/ethernet/intel/ixgb/Makefile
index 0b20c5e62ffe..0b20c5e62ffe 100644
--- a/drivers/net/ixgb/Makefile
+++ b/drivers/net/ethernet/intel/ixgb/Makefile
diff --git a/drivers/net/ixgb/ixgb.h b/drivers/net/ethernet/intel/ixgb/ixgb.h
index 49e8408f05fc..cb23448fe2fa 100644
--- a/drivers/net/ixgb/ixgb.h
+++ b/drivers/net/ethernet/intel/ixgb/ixgb.h
@@ -204,6 +204,8 @@ extern void ixgb_set_ethtool_ops(struct net_device *netdev);
extern char ixgb_driver_name[];
extern const char ixgb_driver_version[];
+extern void ixgb_set_speed_duplex(struct net_device *netdev);
+
extern int ixgb_up(struct ixgb_adapter *adapter);
extern void ixgb_down(struct ixgb_adapter *adapter, bool kill_watchdog);
extern void ixgb_reset(struct ixgb_adapter *adapter);
diff --git a/drivers/net/ixgb/ixgb_ee.c b/drivers/net/ethernet/intel/ixgb/ixgb_ee.c
index c982ab9f9005..2ed925f38811 100644
--- a/drivers/net/ixgb/ixgb_ee.c
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_ee.c
@@ -57,6 +57,7 @@ ixgb_raise_clock(struct ixgb_hw *hw,
*/
*eecd_reg = *eecd_reg | IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, *eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
}
@@ -75,6 +76,7 @@ ixgb_lower_clock(struct ixgb_hw *hw,
*/
*eecd_reg = *eecd_reg & ~IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, *eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
}
@@ -112,6 +114,7 @@ ixgb_shift_out_bits(struct ixgb_hw *hw,
eecd_reg |= IXGB_EECD_DI;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
@@ -206,21 +209,25 @@ ixgb_standby_eeprom(struct ixgb_hw *hw)
/* Deselect EEPROM */
eecd_reg &= ~(IXGB_EECD_CS | IXGB_EECD_SK);
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
/* Clock high */
eecd_reg |= IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
/* Select EEPROM */
eecd_reg |= IXGB_EECD_CS;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
/* Clock low */
eecd_reg &= ~IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
}
@@ -239,11 +246,13 @@ ixgb_clock_eeprom(struct ixgb_hw *hw)
/* Rising edge of clock */
eecd_reg |= IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
/* Falling edge of clock */
eecd_reg &= ~IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
}
@@ -550,7 +559,7 @@ ixgb_get_ee_mac_addr(struct ixgb_hw *hw,
ENTER();
if (ixgb_check_and_get_eeprom_data(hw) == true) {
- for (i = 0; i < IXGB_ETH_LENGTH_OF_ADDRESS; i++) {
+ for (i = 0; i < ETH_ALEN; i++) {
mac_addr[i] = ee_map->mac_addr[i];
}
pr_debug("eeprom mac address = %pM\n", mac_addr);
diff --git a/drivers/net/ixgb/ixgb_ee.h b/drivers/net/ethernet/intel/ixgb/ixgb_ee.h
index 7ea12652f471..5680f64314b8 100644
--- a/drivers/net/ixgb/ixgb_ee.h
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_ee.h
@@ -31,8 +31,6 @@
#define IXGB_EEPROM_SIZE 64 /* Size in words */
-#define IXGB_ETH_LENGTH_OF_ADDRESS 6
-
/* EEPROM Commands */
#define EEPROM_READ_OPCODE 0x6 /* EEPROM read opcode */
#define EEPROM_WRITE_OPCODE 0x5 /* EEPROM write opcode */
@@ -75,7 +73,7 @@
/* EEPROM structure */
struct ixgb_ee_map_type {
- u8 mac_addr[IXGB_ETH_LENGTH_OF_ADDRESS];
+ u8 mac_addr[ETH_ALEN];
__le16 compatibility;
__le16 reserved1[4];
__le32 pba_number;
diff --git a/drivers/net/ixgb/ixgb_ethtool.c b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c
index 6da890b9534c..9dfce7dff79b 100644
--- a/drivers/net/ixgb/ixgb_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c
@@ -115,7 +115,7 @@ ixgb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
return 0;
}
-static void ixgb_set_speed_duplex(struct net_device *netdev)
+void ixgb_set_speed_duplex(struct net_device *netdev)
{
struct ixgb_adapter *adapter = netdev_priv(netdev);
/* be optimistic about our link, since we were up before */
@@ -195,57 +195,6 @@ ixgb_set_pauseparam(struct net_device *netdev,
}
static u32
-ixgb_get_rx_csum(struct net_device *netdev)
-{
- struct ixgb_adapter *adapter = netdev_priv(netdev);
-
- return adapter->rx_csum;
-}
-
-static int
-ixgb_set_rx_csum(struct net_device *netdev, u32 data)
-{
- struct ixgb_adapter *adapter = netdev_priv(netdev);
-
- adapter->rx_csum = data;
-
- if (netif_running(netdev)) {
- ixgb_down(adapter, true);
- ixgb_up(adapter);
- ixgb_set_speed_duplex(netdev);
- } else
- ixgb_reset(adapter);
- return 0;
-}
-
-static u32
-ixgb_get_tx_csum(struct net_device *netdev)
-{
- return (netdev->features & NETIF_F_HW_CSUM) != 0;
-}
-
-static int
-ixgb_set_tx_csum(struct net_device *netdev, u32 data)
-{
- if (data)
- netdev->features |= NETIF_F_HW_CSUM;
- else
- netdev->features &= ~NETIF_F_HW_CSUM;
-
- return 0;
-}
-
-static int
-ixgb_set_tso(struct net_device *netdev, u32 data)
-{
- if (data)
- netdev->features |= NETIF_F_TSO;
- else
- netdev->features &= ~NETIF_F_TSO;
- return 0;
-}
-
-static u32
ixgb_get_msglevel(struct net_device *netdev)
{
struct ixgb_adapter *adapter = netdev_priv(netdev);
@@ -543,12 +492,8 @@ ixgb_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = MAX_RXD;
ring->tx_max_pending = MAX_TXD;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = rxdr->count;
ring->tx_pending = txdr->count;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
static int
@@ -685,43 +630,6 @@ ixgb_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
}
}
-static int ixgb_set_flags(struct net_device *netdev, u32 data)
-{
- struct ixgb_adapter *adapter = netdev_priv(netdev);
- bool need_reset;
- int rc;
-
- /*
- * Tx VLAN insertion does not work per HW design when Rx stripping is
- * disabled. Disable txvlan when rxvlan is turned off, and enable
- * rxvlan when txvlan is turned on.
- */
- if (!(data & ETH_FLAG_RXVLAN) &&
- (netdev->features & NETIF_F_HW_VLAN_TX))
- data &= ~ETH_FLAG_TXVLAN;
- else if (data & ETH_FLAG_TXVLAN)
- data |= ETH_FLAG_RXVLAN;
-
- need_reset = (data & ETH_FLAG_RXVLAN) !=
- (netdev->features & NETIF_F_HW_VLAN_RX);
-
- rc = ethtool_op_set_flags(netdev, data, ETH_FLAG_RXVLAN |
- ETH_FLAG_TXVLAN);
- if (rc)
- return rc;
-
- if (need_reset) {
- if (netif_running(netdev)) {
- ixgb_down(adapter, true);
- ixgb_up(adapter);
- ixgb_set_speed_duplex(netdev);
- } else
- ixgb_reset(adapter);
- }
-
- return 0;
-}
-
static const struct ethtool_ops ixgb_ethtool_ops = {
.get_settings = ixgb_get_settings,
.set_settings = ixgb_set_settings,
@@ -736,20 +644,12 @@ static const struct ethtool_ops ixgb_ethtool_ops = {
.set_ringparam = ixgb_set_ringparam,
.get_pauseparam = ixgb_get_pauseparam,
.set_pauseparam = ixgb_set_pauseparam,
- .get_rx_csum = ixgb_get_rx_csum,
- .set_rx_csum = ixgb_set_rx_csum,
- .get_tx_csum = ixgb_get_tx_csum,
- .set_tx_csum = ixgb_set_tx_csum,
- .set_sg = ethtool_op_set_sg,
.get_msglevel = ixgb_get_msglevel,
.set_msglevel = ixgb_set_msglevel,
- .set_tso = ixgb_set_tso,
.get_strings = ixgb_get_strings,
.set_phys_id = ixgb_set_phys_id,
.get_sset_count = ixgb_get_sset_count,
.get_ethtool_stats = ixgb_get_ethtool_stats,
- .get_flags = ethtool_op_get_flags,
- .set_flags = ixgb_set_flags,
};
void ixgb_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ixgb/ixgb_hw.c b/drivers/net/ethernet/intel/ixgb/ixgb_hw.c
index 6cb2e42ff4c1..99b69adb4a0f 100644
--- a/drivers/net/ixgb/ixgb_hw.c
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_hw.c
@@ -149,6 +149,7 @@ ixgb_adapter_stop(struct ixgb_hw *hw)
*/
IXGB_WRITE_REG(hw, RCTL, IXGB_READ_REG(hw, RCTL) & ~IXGB_RCTL_RXEN);
IXGB_WRITE_REG(hw, TCTL, IXGB_READ_REG(hw, TCTL) & ~IXGB_TCTL_TXEN);
+ IXGB_WRITE_FLUSH(hw);
msleep(IXGB_DELAY_BEFORE_RESET);
/* Issue a global reset to the MAC. This will reset the chip's
@@ -477,7 +478,7 @@ ixgb_mc_addr_list_update(struct ixgb_hw *hw,
ixgb_mta_set(hw, hash_value);
}
- mca += IXGB_ETH_LENGTH_OF_ADDRESS + pad;
+ mca += ETH_ALEN + pad;
}
pr_debug("MC Update Complete\n");
@@ -1220,6 +1221,7 @@ ixgb_optics_reset_bcm(struct ixgb_hw *hw)
ctrl &= ~IXGB_CTRL0_SDP2;
ctrl |= IXGB_CTRL0_SDP3;
IXGB_WRITE_REG(hw, CTRL0, ctrl);
+ IXGB_WRITE_FLUSH(hw);
/* SerDes needs extra delay */
msleep(IXGB_SUN_PHY_RESET_DELAY);
diff --git a/drivers/net/ixgb/ixgb_hw.h b/drivers/net/ethernet/intel/ixgb/ixgb_hw.h
index 873d32b89fba..2a99a35c33aa 100644
--- a/drivers/net/ixgb/ixgb_hw.h
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_hw.h
@@ -97,8 +97,6 @@ typedef enum {
ixgb_bus_width_64
} ixgb_bus_width;
-#define IXGB_ETH_LENGTH_OF_ADDRESS 6
-
#define IXGB_EEPROM_SIZE 64 /* Size in words */
#define SPEED_10000 10000
@@ -674,7 +672,7 @@ struct ixgb_hw {
u32 max_frame_size; /* Maximum frame size supported */
u32 mc_filter_type; /* Multicast filter hash type */
u32 num_mc_addrs; /* Number of current Multicast addrs */
- u8 curr_mac_addr[IXGB_ETH_LENGTH_OF_ADDRESS]; /* Individual address currently programmed in MAC */
+ u8 curr_mac_addr[ETH_ALEN]; /* Individual address currently programmed in MAC */
u32 num_tx_desc; /* Number of Transmit descriptors */
u32 num_rx_desc; /* Number of Receive descriptors */
u32 rx_buffer_size; /* Size of Receive buffer */
diff --git a/drivers/net/ixgb/ixgb_ids.h b/drivers/net/ethernet/intel/ixgb/ixgb_ids.h
index 2a58847f46e8..2a58847f46e8 100644
--- a/drivers/net/ixgb/ixgb_ids.h
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_ids.h
diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c
index 6a130eb51cfa..e21148f8b160 100644
--- a/drivers/net/ixgb/ixgb_main.c
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c
@@ -325,12 +325,47 @@ ixgb_reset(struct ixgb_adapter *adapter)
}
}
+static u32
+ixgb_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Tx VLAN insertion does not work per HW design when Rx stripping is
+ * disabled.
+ */
+ if (!(features & NETIF_F_HW_VLAN_RX))
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int
+ixgb_set_features(struct net_device *netdev, u32 features)
+{
+ struct ixgb_adapter *adapter = netdev_priv(netdev);
+ u32 changed = features ^ netdev->features;
+
+ if (!(changed & (NETIF_F_RXCSUM|NETIF_F_HW_VLAN_RX)))
+ return 0;
+
+ adapter->rx_csum = !!(features & NETIF_F_RXCSUM);
+
+ if (netif_running(netdev)) {
+ ixgb_down(adapter, true);
+ ixgb_up(adapter);
+ ixgb_set_speed_duplex(netdev);
+ } else
+ ixgb_reset(adapter);
+
+ return 0;
+}
+
+
static const struct net_device_ops ixgb_netdev_ops = {
.ndo_open = ixgb_open,
.ndo_stop = ixgb_close,
.ndo_start_xmit = ixgb_xmit_frame,
.ndo_get_stats = ixgb_get_stats,
- .ndo_set_multicast_list = ixgb_set_multi,
+ .ndo_set_rx_mode = ixgb_set_multi,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = ixgb_set_mac,
.ndo_change_mtu = ixgb_change_mtu,
@@ -340,6 +375,8 @@ static const struct net_device_ops ixgb_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = ixgb_netpoll,
#endif
+ .ndo_fix_features = ixgb_fix_features,
+ .ndo_set_features = ixgb_set_features,
};
/**
@@ -439,12 +476,14 @@ ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
goto err_sw_init;
- netdev->features = NETIF_F_SG |
+ netdev->hw_features = NETIF_F_SG |
+ NETIF_F_TSO |
NETIF_F_HW_CSUM |
NETIF_F_HW_VLAN_TX |
- NETIF_F_HW_VLAN_RX |
+ NETIF_F_HW_VLAN_RX;
+ netdev->features = netdev->hw_features |
NETIF_F_HW_VLAN_FILTER;
- netdev->features |= NETIF_F_TSO;
+ netdev->hw_features |= NETIF_F_RXCSUM;
if (pci_using_dac) {
netdev->features |= NETIF_F_HIGHDMA;
@@ -1068,7 +1107,6 @@ ixgb_set_multi(struct net_device *netdev)
struct ixgb_hw *hw = &adapter->hw;
struct netdev_hw_addr *ha;
u32 rctl;
- int i;
/* Check for Promiscuous and All Multicast modes */
@@ -1095,19 +1133,27 @@ ixgb_set_multi(struct net_device *netdev)
rctl |= IXGB_RCTL_MPE;
IXGB_WRITE_REG(hw, RCTL, rctl);
} else {
- u8 mta[IXGB_MAX_NUM_MULTICAST_ADDRESSES *
- IXGB_ETH_LENGTH_OF_ADDRESS];
+ u8 *mta = kmalloc(IXGB_MAX_NUM_MULTICAST_ADDRESSES *
+ ETH_ALEN, GFP_ATOMIC);
+ u8 *addr;
+ if (!mta) {
+ pr_err("allocation of multicast memory failed\n");
+ goto alloc_failed;
+ }
IXGB_WRITE_REG(hw, RCTL, rctl);
- i = 0;
- netdev_for_each_mc_addr(ha, netdev)
- memcpy(&mta[i++ * IXGB_ETH_LENGTH_OF_ADDRESS],
- ha->addr, IXGB_ETH_LENGTH_OF_ADDRESS);
+ addr = mta;
+ netdev_for_each_mc_addr(ha, netdev) {
+ memcpy(addr, ha->addr, ETH_ALEN);
+ addr += ETH_ALEN;
+ }
ixgb_mc_addr_list_update(hw, mta, netdev_mc_count(netdev), 0);
+ kfree(mta);
}
+alloc_failed:
if (netdev->features & NETIF_F_HW_VLAN_RX)
ixgb_vlan_strip_enable(adapter);
else
@@ -1337,11 +1383,11 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb,
}
for (f = 0; f < nr_frags; f++) {
- struct skb_frag_struct *frag;
+ const struct skb_frag_struct *frag;
frag = &skb_shinfo(skb)->frags[f];
- len = frag->size;
- offset = frag->page_offset;
+ len = skb_frag_size(frag);
+ offset = 0;
while (len) {
i++;
@@ -1361,8 +1407,8 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb,
buffer_info->time_stamp = jiffies;
buffer_info->mapped_as_page = true;
buffer_info->dma =
- dma_map_page(&pdev->dev, frag->page,
- offset, size, DMA_TO_DEVICE);
+ skb_frag_dma_map(&pdev->dev, frag, offset, size,
+ DMA_TO_DEVICE);
if (dma_mapping_error(&pdev->dev, buffer_info->dma))
goto dma_error;
buffer_info->next_to_watch = 0;
diff --git a/drivers/net/ixgb/ixgb_osdep.h b/drivers/net/ethernet/intel/ixgb/ixgb_osdep.h
index e361185920ef..8fc905192231 100644
--- a/drivers/net/ixgb/ixgb_osdep.h
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_osdep.h
@@ -38,6 +38,7 @@
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
+#include <linux/if_ether.h>
#undef ASSERT
#define ASSERT(x) BUG_ON(!(x))
diff --git a/drivers/net/ixgb/ixgb_param.c b/drivers/net/ethernet/intel/ixgb/ixgb_param.c
index dd7fbeb1f7d1..07d83ab46e21 100644
--- a/drivers/net/ixgb/ixgb_param.c
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_param.c
@@ -267,7 +267,7 @@ ixgb_check_options(struct ixgb_adapter *adapter)
}
{ /* Transmit Descriptor Count */
- const struct ixgb_option opt = {
+ static const struct ixgb_option opt = {
.type = range_option,
.name = "Transmit Descriptors",
.err = "using default of " __MODULE_STRING(DEFAULT_TXD),
@@ -286,7 +286,7 @@ ixgb_check_options(struct ixgb_adapter *adapter)
tx_ring->count = ALIGN(tx_ring->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE);
}
{ /* Receive Descriptor Count */
- const struct ixgb_option opt = {
+ static const struct ixgb_option opt = {
.type = range_option,
.name = "Receive Descriptors",
.err = "using default of " __MODULE_STRING(DEFAULT_RXD),
@@ -305,7 +305,7 @@ ixgb_check_options(struct ixgb_adapter *adapter)
rx_ring->count = ALIGN(rx_ring->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE);
}
{ /* Receive Checksum Offload Enable */
- const struct ixgb_option opt = {
+ static const struct ixgb_option opt = {
.type = enable_option,
.name = "Receive Checksum Offload",
.err = "defaulting to Enabled",
@@ -348,7 +348,7 @@ ixgb_check_options(struct ixgb_adapter *adapter)
}
}
{ /* Receive Flow Control High Threshold */
- const struct ixgb_option opt = {
+ static const struct ixgb_option opt = {
.type = range_option,
.name = "Rx Flow Control High Threshold",
.err = "using default of " __MODULE_STRING(DEFAULT_FCRTH),
@@ -367,7 +367,7 @@ ixgb_check_options(struct ixgb_adapter *adapter)
pr_info("Ignoring RxFCHighThresh when no RxFC\n");
}
{ /* Receive Flow Control Low Threshold */
- const struct ixgb_option opt = {
+ static const struct ixgb_option opt = {
.type = range_option,
.name = "Rx Flow Control Low Threshold",
.err = "using default of " __MODULE_STRING(DEFAULT_FCRTL),
@@ -386,7 +386,7 @@ ixgb_check_options(struct ixgb_adapter *adapter)
pr_info("Ignoring RxFCLowThresh when no RxFC\n");
}
{ /* Flow Control Pause Time Request*/
- const struct ixgb_option opt = {
+ static const struct ixgb_option opt = {
.type = range_option,
.name = "Flow Control Pause Time Request",
.err = "using default of "__MODULE_STRING(DEFAULT_FCPAUSE),
@@ -416,7 +416,7 @@ ixgb_check_options(struct ixgb_adapter *adapter)
}
}
{ /* Receive Interrupt Delay */
- const struct ixgb_option opt = {
+ static const struct ixgb_option opt = {
.type = range_option,
.name = "Receive Interrupt Delay",
.err = "using default of " __MODULE_STRING(DEFAULT_RDTR),
@@ -433,7 +433,7 @@ ixgb_check_options(struct ixgb_adapter *adapter)
}
}
{ /* Transmit Interrupt Delay */
- const struct ixgb_option opt = {
+ static const struct ixgb_option opt = {
.type = range_option,
.name = "Transmit Interrupt Delay",
.err = "using default of " __MODULE_STRING(DEFAULT_TIDV),
@@ -451,7 +451,7 @@ ixgb_check_options(struct ixgb_adapter *adapter)
}
{ /* Transmit Interrupt Delay Enable */
- const struct ixgb_option opt = {
+ static const struct ixgb_option opt = {
.type = enable_option,
.name = "Tx Interrupt Delay Enable",
.err = "defaulting to Enabled",
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ethernet/intel/ixgbe/Makefile
index 7d7387fbdecd..7d7387fbdecd 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ethernet/intel/ixgbe/Makefile
diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index e467b20ed1f0..a8368d5cf686 100644
--- a/drivers/net/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -53,6 +53,7 @@
/* TX/RX descriptor defines */
#define IXGBE_DEFAULT_TXD 512
+#define IXGBE_DEFAULT_TX_WORK 256
#define IXGBE_MAX_TXD 4096
#define IXGBE_MIN_TXD 64
@@ -71,10 +72,13 @@
/* Supported Rx Buffer Sizes */
#define IXGBE_RXBUFFER_512 512 /* Used for packet split */
-#define IXGBE_RXBUFFER_2048 2048
-#define IXGBE_RXBUFFER_4096 4096
-#define IXGBE_RXBUFFER_8192 8192
-#define IXGBE_MAX_RXBUFFER 16384 /* largest size for a single descriptor */
+#define IXGBE_RXBUFFER_2K 2048
+#define IXGBE_RXBUFFER_3K 3072
+#define IXGBE_RXBUFFER_4K 4096
+#define IXGBE_RXBUFFER_7K 7168
+#define IXGBE_RXBUFFER_8K 8192
+#define IXGBE_RXBUFFER_15K 15360
+#define IXGBE_MAX_RXBUFFER 16384 /* largest size for a single descriptor */
/*
* NOTE: netdev_alloc_skb reserves up to 64 bytes, NET_IP_ALIGN mans we
@@ -91,13 +95,17 @@
#define IXGBE_RX_BUFFER_WRITE 16 /* Must be power of 2 */
#define IXGBE_TX_FLAGS_CSUM (u32)(1)
-#define IXGBE_TX_FLAGS_VLAN (u32)(1 << 1)
-#define IXGBE_TX_FLAGS_TSO (u32)(1 << 2)
-#define IXGBE_TX_FLAGS_IPV4 (u32)(1 << 3)
-#define IXGBE_TX_FLAGS_FCOE (u32)(1 << 4)
-#define IXGBE_TX_FLAGS_FSO (u32)(1 << 5)
+#define IXGBE_TX_FLAGS_HW_VLAN (u32)(1 << 1)
+#define IXGBE_TX_FLAGS_SW_VLAN (u32)(1 << 2)
+#define IXGBE_TX_FLAGS_TSO (u32)(1 << 3)
+#define IXGBE_TX_FLAGS_IPV4 (u32)(1 << 4)
+#define IXGBE_TX_FLAGS_FCOE (u32)(1 << 5)
+#define IXGBE_TX_FLAGS_FSO (u32)(1 << 6)
+#define IXGBE_TX_FLAGS_TXSW (u32)(1 << 7)
+#define IXGBE_TX_FLAGS_MAPPED_AS_PAGE (u32)(1 << 8)
#define IXGBE_TX_FLAGS_VLAN_MASK 0xffff0000
-#define IXGBE_TX_FLAGS_VLAN_PRIO_MASK 0x0000e000
+#define IXGBE_TX_FLAGS_VLAN_PRIO_MASK 0xe0000000
+#define IXGBE_TX_FLAGS_VLAN_PRIO_SHIFT 29
#define IXGBE_TX_FLAGS_VLAN_SHIFT 16
#define IXGBE_MAX_RSC_INT_RATE 162760
@@ -108,6 +116,8 @@
#define MAX_EMULATION_MAC_ADDRS 16
#define IXGBE_MAX_PF_MACVLANS 15
#define VMDQ_P(p) ((p) + adapter->num_vfs)
+#define IXGBE_82599_VF_DEVICE_ID 0x10ED
+#define IXGBE_X540_VF_DEVICE_ID 0x1515
struct vf_data_storage {
unsigned char vf_mac_addresses[ETH_ALEN];
@@ -120,6 +130,9 @@ struct vf_data_storage {
u16 pf_vlan; /* When set, guest VLAN config not allowed. */
u16 pf_qos;
u16 tx_rate;
+ u16 vlan_count;
+ u8 spoofchk_enabled;
+ struct pci_dev *vfdev;
};
struct vf_macvlans {
@@ -131,17 +144,24 @@ struct vf_macvlans {
u8 vf_macvlan[ETH_ALEN];
};
+#define IXGBE_MAX_TXD_PWR 14
+#define IXGBE_MAX_DATA_PER_TXD (1 << IXGBE_MAX_TXD_PWR)
+
+/* Tx Descriptors needed, worst case */
+#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), IXGBE_MAX_DATA_PER_TXD)
+#define DESC_NEEDED ((MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE)) + 4)
+
/* wrapper around a pointer to a socket buffer,
* so a DMA handle can be stored along with the buffer */
struct ixgbe_tx_buffer {
- struct sk_buff *skb;
- dma_addr_t dma;
+ union ixgbe_adv_tx_desc *next_to_watch;
unsigned long time_stamp;
- u16 length;
- u16 next_to_watch;
- unsigned int bytecount;
+ dma_addr_t dma;
+ u32 length;
+ u32 tx_flags;
+ struct sk_buff *skb;
+ u32 bytecount;
u16 gso_segs;
- u8 mapped_as_page;
};
struct ixgbe_rx_buffer {
@@ -199,6 +219,7 @@ enum ixbge_ring_state_t {
#define clear_ring_rsc_enabled(ring) \
clear_bit(__IXGBE_RX_RSC_ENABLED, &(ring)->state)
struct ixgbe_ring {
+ struct ixgbe_ring *next; /* pointer to next ring in q_vector */
void *desc; /* descriptor ring memory */
struct device *dev; /* device for DMA mapping */
struct net_device *netdev; /* netdev ring belongs to */
@@ -207,12 +228,10 @@ struct ixgbe_ring {
struct ixgbe_rx_buffer *rx_buffer_info;
};
unsigned long state;
- u8 atr_sample_rate;
- u8 atr_count;
+ u8 __iomem *tail;
+
u16 count; /* amount of descriptors */
u16 rx_buf_len;
- u16 next_to_use;
- u16 next_to_clean;
u8 queue_index; /* needed for multiqueue queue management */
u8 reg_idx; /* holds the special value that gets
@@ -220,15 +239,13 @@ struct ixgbe_ring {
* associated with this ring, which is
* different for DCB and RSS modes
*/
- u8 dcb_tc;
-
- u16 work_limit; /* max work per interrupt */
-
- u8 __iomem *tail;
+ u8 atr_sample_rate;
+ u8 atr_count;
- unsigned int total_bytes;
- unsigned int total_packets;
+ u16 next_to_use;
+ u16 next_to_clean;
+ u8 dcb_tc;
struct ixgbe_queue_stats stats;
struct u64_stats_sync syncp;
union {
@@ -244,7 +261,6 @@ struct ixgbe_ring {
enum ixgbe_ring_f_enum {
RING_F_NONE = 0,
- RING_F_DCB,
RING_F_VMDQ, /* SR-IOV uses the same ring feature */
RING_F_RSS,
RING_F_FDIR,
@@ -255,7 +271,6 @@ enum ixgbe_ring_f_enum {
RING_F_ARRAY_SIZE /* must be last in enum set */
};
-#define IXGBE_MAX_DCB_INDICES 64
#define IXGBE_MAX_RSS_INDICES 16
#define IXGBE_MAX_VMDQ_INDICES 64
#define IXGBE_MAX_FDIR_INDICES 64
@@ -272,6 +287,14 @@ struct ixgbe_ring_feature {
int mask;
} ____cacheline_internodealigned_in_smp;
+struct ixgbe_ring_container {
+ struct ixgbe_ring *ring; /* pointer to linked list of rings */
+ unsigned int total_bytes; /* total bytes processed this int */
+ unsigned int total_packets; /* total packets processed this int */
+ u16 work_limit; /* total work allowed per interrupt */
+ u8 count; /* total number of rings in vector */
+ u8 itr; /* current ITR setting for ring */
+};
#define MAX_RX_PACKET_BUFFERS ((adapter->flags & IXGBE_FLAG_DCB_ENABLED) \
? 8 : 1)
@@ -282,35 +305,37 @@ struct ixgbe_ring_feature {
*/
struct ixgbe_q_vector {
struct ixgbe_adapter *adapter;
- unsigned int v_idx; /* index of q_vector within array, also used for
- * finding the bit in EICR and friends that
- * represents the vector for this ring */
#ifdef CONFIG_IXGBE_DCA
int cpu; /* CPU for DCA */
#endif
+ u16 v_idx; /* index of q_vector within array, also used for
+ * finding the bit in EICR and friends that
+ * represents the vector for this ring */
+ u16 itr; /* Interrupt throttle rate written to EITR */
+ struct ixgbe_ring_container rx, tx;
+
struct napi_struct napi;
- DECLARE_BITMAP(rxr_idx, MAX_RX_QUEUES); /* Rx ring indices */
- DECLARE_BITMAP(txr_idx, MAX_TX_QUEUES); /* Tx ring indices */
- u8 rxr_count; /* Rx ring count assigned to this vector */
- u8 txr_count; /* Tx ring count assigned to this vector */
- u8 tx_itr;
- u8 rx_itr;
- u32 eitr;
cpumask_var_t affinity_mask;
char name[IFNAMSIZ + 9];
};
-/* Helper macros to switch between ints/sec and what the register uses.
- * And yes, it's the same math going both ways. The lowest value
- * supported by all of the ixgbe hardware is 8.
+/*
+ * microsecond values for various ITR rates shifted by 2 to fit itr register
+ * with the first 3 bits reserved 0
*/
-#define EITR_INTS_PER_SEC_TO_REG(_eitr) \
- ((_eitr) ? (1000000000 / ((_eitr) * 256)) : 8)
-#define EITR_REG_TO_INTS_PER_SEC EITR_INTS_PER_SEC_TO_REG
+#define IXGBE_MIN_RSC_ITR 24
+#define IXGBE_100K_ITR 40
+#define IXGBE_20K_ITR 200
+#define IXGBE_10K_ITR 400
+#define IXGBE_8K_ITR 500
-#define IXGBE_DESC_UNUSED(R) \
- ((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
- (R)->next_to_clean - (R)->next_to_use - 1)
+static inline u16 ixgbe_desc_unused(struct ixgbe_ring *ring)
+{
+ u16 ntc = ring->next_to_clean;
+ u16 ntu = ring->next_to_use;
+
+ return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1;
+}
#define IXGBE_RX_DESC_ADV(R, i) \
(&(((union ixgbe_adv_rx_desc *)((R)->desc))[i]))
@@ -404,6 +429,9 @@ struct ixgbe_adapter {
u16 eitr_low;
u16 eitr_high;
+ /* Work limits */
+ u16 tx_work_limit;
+
/* TX */
struct ixgbe_ring *tx_ring[MAX_TX_QUEUES] ____cacheline_aligned_in_smp;
int num_tx_queues;
@@ -469,12 +497,13 @@ struct ixgbe_adapter {
u64 rsc_total_count;
u64 rsc_total_flush;
u32 wol;
- u16 eeprom_version;
+ u16 eeprom_verh;
+ u16 eeprom_verl;
+ u16 eeprom_cap;
int node;
u32 led_reg;
u32 interrupt_event;
- char lsc_int_name[IFNAMSIZ + 9];
/* SR-IOV */
DECLARE_BITMAP(active_vfs, IXGBE_MAX_VF_FUNCTIONS);
@@ -483,7 +512,19 @@ struct ixgbe_adapter {
int vf_rate_link_speed;
struct vf_macvlans vf_mvs;
struct vf_macvlans *mv_list;
- bool antispoofing_enabled;
+
+ struct hlist_head fdir_filter_list;
+ union ixgbe_atr_input fdir_mask;
+ int fdir_filter_count;
+ u32 timer_event_accumulator;
+ u32 vferr_refcount;
+};
+
+struct ixgbe_fdir_filter {
+ struct hlist_node fdir_node;
+ union ixgbe_atr_input filter;
+ u16 sw_idx;
+ u16 action;
};
enum ixbge_state_t {
@@ -520,7 +561,7 @@ extern int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *src_dcb_cfg,
extern char ixgbe_driver_name[];
extern const char ixgbe_driver_version[];
-extern int ixgbe_up(struct ixgbe_adapter *adapter);
+extern void ixgbe_up(struct ixgbe_adapter *adapter);
extern void ixgbe_down(struct ixgbe_adapter *adapter);
extern void ixgbe_reinit_locked(struct ixgbe_adapter *adapter);
extern void ixgbe_reset(struct ixgbe_adapter *adapter);
@@ -545,31 +586,35 @@ extern void ixgbe_alloc_rx_buffers(struct ixgbe_ring *, u16);
extern void ixgbe_write_eitr(struct ixgbe_q_vector *);
extern int ethtool_ioctl(struct ifreq *ifr);
extern s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw);
-extern s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 pballoc);
-extern s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 pballoc);
+extern s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl);
+extern s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 fdirctrl);
extern s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
union ixgbe_atr_hash_dword input,
union ixgbe_atr_hash_dword common,
u8 queue);
-extern s32 ixgbe_fdir_add_perfect_filter_82599(struct ixgbe_hw *hw,
- union ixgbe_atr_input *input,
- struct ixgbe_atr_input_masks *input_masks,
- u16 soft_id, u8 queue);
-extern void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter,
- struct ixgbe_ring *ring);
-extern void ixgbe_clear_rscctl(struct ixgbe_adapter *adapter,
- struct ixgbe_ring *ring);
+extern s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw,
+ union ixgbe_atr_input *input_mask);
+extern s32 ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw,
+ union ixgbe_atr_input *input,
+ u16 soft_id, u8 queue);
+extern s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw,
+ union ixgbe_atr_input *input,
+ u16 soft_id);
+extern void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
+ union ixgbe_atr_input *mask);
extern void ixgbe_set_rx_mode(struct net_device *netdev);
extern int ixgbe_setup_tc(struct net_device *dev, u8 tc);
+extern void ixgbe_tx_ctxtdesc(struct ixgbe_ring *, u32, u32, u32, u32);
+extern void ixgbe_do_reset(struct net_device *netdev);
#ifdef IXGBE_FCOE
extern void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter);
-extern int ixgbe_fso(struct ixgbe_adapter *adapter,
- struct ixgbe_ring *tx_ring, struct sk_buff *skb,
+extern int ixgbe_fso(struct ixgbe_ring *tx_ring, struct sk_buff *skb,
u32 tx_flags, u8 *hdr_len);
extern void ixgbe_cleanup_fcoe(struct ixgbe_adapter *adapter);
extern int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
- union ixgbe_adv_rx_desc *rx_desc,
- struct sk_buff *skb);
+ union ixgbe_adv_rx_desc *rx_desc,
+ struct sk_buff *skb,
+ u32 staterr);
extern int ixgbe_fcoe_ddp_get(struct net_device *netdev, u16 xid,
struct scatterlist *sgl, unsigned int sgc);
extern int ixgbe_fcoe_ddp_target(struct net_device *netdev, u16 xid,
diff --git a/drivers/net/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
index 8179e5060a18..ef2afefb0cd4 100644
--- a/drivers/net/ixgbe/ixgbe_82598.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
@@ -307,7 +307,6 @@ static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw)
switch (hw->phy.type) {
case ixgbe_phy_cu_unknown:
case ixgbe_phy_tn:
- case ixgbe_phy_aq:
media_type = ixgbe_media_type_copper;
goto out;
default:
@@ -358,7 +357,6 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw, s32 packetbuf_num)
u32 fctrl_reg;
u32 rmcs_reg;
u32 reg;
- u32 rx_pba_size;
u32 link_speed = 0;
bool link_up;
@@ -461,16 +459,13 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw, s32 packetbuf_num)
/* Set up and enable Rx high/low water mark thresholds, enable XON. */
if (hw->fc.current_mode & ixgbe_fc_tx_pause) {
- rx_pba_size = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(packetbuf_num));
- rx_pba_size >>= IXGBE_RXPBSIZE_SHIFT;
-
- reg = (rx_pba_size - hw->fc.low_water) << 6;
+ reg = hw->fc.low_water << 6;
if (hw->fc.send_xon)
reg |= IXGBE_FCRTL_XONE;
IXGBE_WRITE_REG(hw, IXGBE_FCRTL(packetbuf_num), reg);
- reg = (rx_pba_size - hw->fc.high_water) << 6;
+ reg = hw->fc.high_water[packetbuf_num] << 6;
reg |= IXGBE_FCRTH_FCEN;
IXGBE_WRITE_REG(hw, IXGBE_FCRTH(packetbuf_num), reg);
@@ -654,11 +649,6 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
(ixgbe_validate_link_ready(hw) != 0))
*link_up = false;
- /* if link is down, zero out the current_mode */
- if (*link_up == false) {
- hw->fc.current_mode = ixgbe_fc_none;
- hw->fc.fc_was_autonegged = false;
- }
out:
return 0;
}
@@ -759,7 +749,9 @@ static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw)
u8 analog_val;
/* Call adapter stop to disable tx/rx and clear interrupts */
- hw->mac.ops.stop_adapter(hw);
+ status = hw->mac.ops.stop_adapter(hw);
+ if (status != 0)
+ goto reset_hw_out;
/*
* Power up the Atlas Tx lanes if they are currently powered down.
@@ -802,26 +794,19 @@ static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw)
phy_status = hw->phy.ops.init(hw);
if (phy_status == IXGBE_ERR_SFP_NOT_SUPPORTED)
goto reset_hw_out;
- else if (phy_status == IXGBE_ERR_SFP_NOT_PRESENT)
- goto no_phy_reset;
+ if (phy_status == IXGBE_ERR_SFP_NOT_PRESENT)
+ goto mac_reset_top;
hw->phy.ops.reset(hw);
}
-no_phy_reset:
- /*
- * Prevent the PCI-E bus from from hanging by disabling PCI-E master
- * access and verify no pending requests before reset
- */
- ixgbe_disable_pcie_master(hw);
-
mac_reset_top:
/*
* Issue global reset to the MAC. This needs to be a SW reset.
* If link reset is used, it might reset the MAC when mng is using it
*/
- ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
- IXGBE_WRITE_REG(hw, IXGBE_CTRL, (ctrl | IXGBE_CTRL_RST));
+ ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL) | IXGBE_CTRL_RST;
+ IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
/* Poll for reset bit to self-clear indicating reset is complete */
@@ -836,21 +821,18 @@ mac_reset_top:
hw_dbg(hw, "Reset polling failed to complete.\n");
}
+ msleep(50);
+
/*
* Double resets are required for recovery from certain error
* conditions. Between resets, it is necessary to stall to allow time
- * for any pending HW events to complete. We use 1usec since that is
- * what is needed for ixgbe_disable_pcie_master(). The second reset
- * then clears out any effects of those events.
+ * for any pending HW events to complete.
*/
if (hw->mac.flags & IXGBE_FLAGS_DOUBLE_RESET_REQUIRED) {
hw->mac.flags &= ~IXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
- udelay(1);
goto mac_reset_top;
}
- msleep(50);
-
gheccr = IXGBE_READ_REG(hw, IXGBE_GHECCR);
gheccr &= ~((1 << 21) | (1 << 18) | (1 << 9) | (1 << 6));
IXGBE_WRITE_REG(hw, IXGBE_GHECCR, gheccr);
@@ -1129,7 +1111,6 @@ static u32 ixgbe_get_supported_physical_layer_82598(struct ixgbe_hw *hw)
* physical layer because 10GBase-T PHYs use LMS = KX4/KX */
switch (hw->phy.type) {
case ixgbe_phy_tn:
- case ixgbe_phy_aq:
case ixgbe_phy_cu_unknown:
hw->phy.ops.read_reg(hw, MDIO_PMA_EXTABLE,
MDIO_MMD_PMAPMD, &ext_ability);
@@ -1242,6 +1223,47 @@ static void ixgbe_set_lan_id_multi_port_pcie_82598(struct ixgbe_hw *hw)
}
}
+/**
+ * ixgbe_set_rxpba_82598 - Configure packet buffers
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure packet buffers.
+ */
+static void ixgbe_set_rxpba_82598(struct ixgbe_hw *hw, int num_pb, u32 headroom,
+ int strategy)
+{
+ u32 rxpktsize = IXGBE_RXPBSIZE_64KB;
+ u8 i = 0;
+
+ if (!num_pb)
+ return;
+
+ /* Setup Rx packet buffer sizes */
+ switch (strategy) {
+ case PBA_STRATEGY_WEIGHTED:
+ /* Setup the first four at 80KB */
+ rxpktsize = IXGBE_RXPBSIZE_80KB;
+ for (; i < 4; i++)
+ IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), rxpktsize);
+ /* Setup the last four at 48KB...don't re-init i */
+ rxpktsize = IXGBE_RXPBSIZE_48KB;
+ /* Fall Through */
+ case PBA_STRATEGY_EQUAL:
+ default:
+ /* Divide the remaining Rx packet buffer evenly among the TCs */
+ for (; i < IXGBE_MAX_PACKET_BUFFERS; i++)
+ IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), rxpktsize);
+ break;
+ }
+
+ /* Setup Tx packet buffer sizes */
+ for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++)
+ IXGBE_WRITE_REG(hw, IXGBE_TXPBSIZE(i), IXGBE_TXPBSIZE_40KB);
+
+ return;
+}
+
static struct ixgbe_mac_operations mac_ops_82598 = {
.init_hw = &ixgbe_init_hw_generic,
.reset_hw = &ixgbe_reset_hw_82598,
@@ -1257,6 +1279,7 @@ static struct ixgbe_mac_operations mac_ops_82598 = {
.read_analog_reg8 = &ixgbe_read_analog_reg8_82598,
.write_analog_reg8 = &ixgbe_write_analog_reg8_82598,
.setup_link = &ixgbe_setup_mac_link_82598,
+ .set_rxpba = &ixgbe_set_rxpba_82598,
.check_link = &ixgbe_check_mac_link_82598,
.get_link_capabilities = &ixgbe_get_link_capabilities_82598,
.led_on = &ixgbe_led_on_generic,
@@ -1274,6 +1297,7 @@ static struct ixgbe_mac_operations mac_ops_82598 = {
.clear_vfta = &ixgbe_clear_vfta_82598,
.set_vfta = &ixgbe_set_vfta_82598,
.fc_enable = &ixgbe_fc_enable_82598,
+ .set_fw_drv_ver = NULL,
.acquire_swfw_sync = &ixgbe_acquire_swfw_sync,
.release_swfw_sync = &ixgbe_release_swfw_sync,
};
@@ -1281,6 +1305,8 @@ static struct ixgbe_mac_operations mac_ops_82598 = {
static struct ixgbe_eeprom_operations eeprom_ops_82598 = {
.init_params = &ixgbe_init_eeprom_params_generic,
.read = &ixgbe_read_eerd_generic,
+ .write = &ixgbe_write_eeprom_generic,
+ .write_buffer = &ixgbe_write_eeprom_buffer_bit_bang_generic,
.read_buffer = &ixgbe_read_eerd_buffer_generic,
.calc_checksum = &ixgbe_calc_eeprom_checksum_generic,
.validate_checksum = &ixgbe_validate_eeprom_checksum_generic,
diff --git a/drivers/net/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
index 8ee661245af3..4ae26a748da0 100644
--- a/drivers/net/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
@@ -213,13 +213,10 @@ static s32 ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw)
switch (hw->phy.type) {
case ixgbe_phy_tn:
phy->ops.check_link = &ixgbe_check_phy_link_tnx;
+ phy->ops.setup_link = &ixgbe_setup_phy_link_tnx;
phy->ops.get_firmware_version =
&ixgbe_get_phy_firmware_version_tnx;
break;
- case ixgbe_phy_aq:
- phy->ops.get_firmware_version =
- &ixgbe_get_phy_firmware_version_generic;
- break;
default:
break;
}
@@ -339,7 +336,6 @@ static enum ixgbe_media_type ixgbe_get_media_type_82599(struct ixgbe_hw *hw)
switch (hw->phy.type) {
case ixgbe_phy_cu_unknown:
case ixgbe_phy_tn:
- case ixgbe_phy_aq:
media_type = ixgbe_media_type_copper;
goto out;
default:
@@ -360,6 +356,7 @@ static enum ixgbe_media_type ixgbe_get_media_type_82599(struct ixgbe_hw *hw)
case IXGBE_DEV_ID_82599_SFP_FCOE:
case IXGBE_DEV_ID_82599_SFP_EM:
case IXGBE_DEV_ID_82599_SFP_SF2:
+ case IXGBE_DEV_ID_82599EN_SFP:
media_type = ixgbe_media_type_fiber;
break;
case IXGBE_DEV_ID_82599_CX4:
@@ -903,14 +900,18 @@ static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw,
**/
static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw)
{
- s32 status = 0;
- u32 ctrl;
- u32 i;
- u32 autoc;
- u32 autoc2;
+ ixgbe_link_speed link_speed;
+ s32 status;
+ u32 ctrl, i, autoc, autoc2;
+ bool link_up = false;
/* Call adapter stop to disable tx/rx and clear interrupts */
- hw->mac.ops.stop_adapter(hw);
+ status = hw->mac.ops.stop_adapter(hw);
+ if (status != 0)
+ goto reset_hw_out;
+
+ /* flush pending Tx transactions */
+ ixgbe_clear_tx_pending(hw);
/* PHY ops must be identified and initialized prior to reset */
@@ -933,48 +934,49 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw)
if (hw->phy.reset_disable == false && hw->phy.ops.reset != NULL)
hw->phy.ops.reset(hw);
- /*
- * Prevent the PCI-E bus from from hanging by disabling PCI-E master
- * access and verify no pending requests before reset
- */
- ixgbe_disable_pcie_master(hw);
-
mac_reset_top:
/*
- * Issue global reset to the MAC. This needs to be a SW reset.
- * If link reset is used, it might reset the MAC when mng is using it
+ * Issue global reset to the MAC. Needs to be SW reset if link is up.
+ * If link reset is used when link is up, it might reset the PHY when
+ * mng is using it. If link is down or the flag to force full link
+ * reset is set, then perform link reset.
*/
- ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
- IXGBE_WRITE_REG(hw, IXGBE_CTRL, (ctrl | IXGBE_CTRL_RST));
+ ctrl = IXGBE_CTRL_LNK_RST;
+ if (!hw->force_full_reset) {
+ hw->mac.ops.check_link(hw, &link_speed, &link_up, false);
+ if (link_up)
+ ctrl = IXGBE_CTRL_RST;
+ }
+
+ ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL);
+ IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
/* Poll for reset bit to self-clear indicating reset is complete */
for (i = 0; i < 10; i++) {
udelay(1);
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
- if (!(ctrl & IXGBE_CTRL_RST))
+ if (!(ctrl & IXGBE_CTRL_RST_MASK))
break;
}
- if (ctrl & IXGBE_CTRL_RST) {
+
+ if (ctrl & IXGBE_CTRL_RST_MASK) {
status = IXGBE_ERR_RESET_FAILED;
hw_dbg(hw, "Reset polling failed to complete.\n");
}
+ msleep(50);
+
/*
* Double resets are required for recovery from certain error
* conditions. Between resets, it is necessary to stall to allow time
- * for any pending HW events to complete. We use 1usec since that is
- * what is needed for ixgbe_disable_pcie_master(). The second reset
- * then clears out any effects of those events.
+ * for any pending HW events to complete.
*/
if (hw->mac.flags & IXGBE_FLAGS_DOUBLE_RESET_REQUIRED) {
hw->mac.flags &= ~IXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
- udelay(1);
goto mac_reset_top;
}
- msleep(50);
-
/*
* Store the original AUTOC/AUTOC2 values if they have not been
* stored off yet. Otherwise restore the stored original
@@ -1107,62 +1109,14 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw)
}
/**
- * ixgbe_init_fdir_signature_82599 - Initialize Flow Director signature filters
+ * ixgbe_fdir_enable_82599 - Initialize Flow Director control registers
* @hw: pointer to hardware structure
- * @pballoc: which mode to allocate filters with
+ * @fdirctrl: value to write to flow director control register
**/
-s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 pballoc)
+static void ixgbe_fdir_enable_82599(struct ixgbe_hw *hw, u32 fdirctrl)
{
- u32 fdirctrl = 0;
- u32 pbsize;
int i;
- /*
- * Before enabling Flow Director, the Rx Packet Buffer size
- * must be reduced. The new value is the current size minus
- * flow director memory usage size.
- */
- pbsize = (1 << (IXGBE_FDIR_PBALLOC_SIZE_SHIFT + pballoc));
- IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(0),
- (IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(0)) - pbsize));
-
- /*
- * The defaults in the HW for RX PB 1-7 are not zero and so should be
- * initialized to zero for non DCB mode otherwise actual total RX PB
- * would be bigger than programmed and filter space would run into
- * the PB 0 region.
- */
- for (i = 1; i < 8; i++)
- IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), 0);
-
- /* Send interrupt when 64 filters are left */
- fdirctrl |= 4 << IXGBE_FDIRCTRL_FULL_THRESH_SHIFT;
-
- /* Set the maximum length per hash bucket to 0xA filters */
- fdirctrl |= 0xA << IXGBE_FDIRCTRL_MAX_LENGTH_SHIFT;
-
- switch (pballoc) {
- case IXGBE_FDIR_PBALLOC_64K:
- /* 8k - 1 signature filters */
- fdirctrl |= IXGBE_FDIRCTRL_PBALLOC_64K;
- break;
- case IXGBE_FDIR_PBALLOC_128K:
- /* 16k - 1 signature filters */
- fdirctrl |= IXGBE_FDIRCTRL_PBALLOC_128K;
- break;
- case IXGBE_FDIR_PBALLOC_256K:
- /* 32k - 1 signature filters */
- fdirctrl |= IXGBE_FDIRCTRL_PBALLOC_256K;
- break;
- default:
- /* bad value */
- return IXGBE_ERR_CONFIG;
- };
-
- /* Move the flexible bytes to use the ethertype - shift 6 words */
- fdirctrl |= (0x6 << IXGBE_FDIRCTRL_FLEX_SHIFT);
-
-
/* Prime the keys for hashing */
IXGBE_WRITE_REG(hw, IXGBE_FDIRHKEY, IXGBE_ATR_BUCKET_HASH_KEY);
IXGBE_WRITE_REG(hw, IXGBE_FDIRSKEY, IXGBE_ATR_SIGNATURE_HASH_KEY);
@@ -1188,196 +1142,63 @@ s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 pballoc)
break;
usleep_range(1000, 2000);
}
- if (i >= IXGBE_FDIR_INIT_DONE_POLL)
- hw_dbg(hw, "Flow Director Signature poll time exceeded!\n");
- return 0;
+ if (i >= IXGBE_FDIR_INIT_DONE_POLL)
+ hw_dbg(hw, "Flow Director poll time exceeded!\n");
}
/**
- * ixgbe_init_fdir_perfect_82599 - Initialize Flow Director perfect filters
+ * ixgbe_init_fdir_signature_82599 - Initialize Flow Director signature filters
* @hw: pointer to hardware structure
- * @pballoc: which mode to allocate filters with
+ * @fdirctrl: value to write to flow director control register, initially
+ * contains just the value of the Rx packet buffer allocation
**/
-s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 pballoc)
+s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl)
{
- u32 fdirctrl = 0;
- u32 pbsize;
- int i;
-
- /*
- * Before enabling Flow Director, the Rx Packet Buffer size
- * must be reduced. The new value is the current size minus
- * flow director memory usage size.
- */
- pbsize = (1 << (IXGBE_FDIR_PBALLOC_SIZE_SHIFT + pballoc));
- IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(0),
- (IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(0)) - pbsize));
-
- /*
- * The defaults in the HW for RX PB 1-7 are not zero and so should be
- * initialized to zero for non DCB mode otherwise actual total RX PB
- * would be bigger than programmed and filter space would run into
- * the PB 0 region.
- */
- for (i = 1; i < 8; i++)
- IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), 0);
-
- /* Send interrupt when 64 filters are left */
- fdirctrl |= 4 << IXGBE_FDIRCTRL_FULL_THRESH_SHIFT;
-
- /* Initialize the drop queue to Rx queue 127 */
- fdirctrl |= (127 << IXGBE_FDIRCTRL_DROP_Q_SHIFT);
-
- switch (pballoc) {
- case IXGBE_FDIR_PBALLOC_64K:
- /* 2k - 1 perfect filters */
- fdirctrl |= IXGBE_FDIRCTRL_PBALLOC_64K;
- break;
- case IXGBE_FDIR_PBALLOC_128K:
- /* 4k - 1 perfect filters */
- fdirctrl |= IXGBE_FDIRCTRL_PBALLOC_128K;
- break;
- case IXGBE_FDIR_PBALLOC_256K:
- /* 8k - 1 perfect filters */
- fdirctrl |= IXGBE_FDIRCTRL_PBALLOC_256K;
- break;
- default:
- /* bad value */
- return IXGBE_ERR_CONFIG;
- };
-
- /* Turn perfect match filtering on */
- fdirctrl |= IXGBE_FDIRCTRL_PERFECT_MATCH;
- fdirctrl |= IXGBE_FDIRCTRL_REPORT_STATUS;
-
- /* Move the flexible bytes to use the ethertype - shift 6 words */
- fdirctrl |= (0x6 << IXGBE_FDIRCTRL_FLEX_SHIFT);
-
- /* Prime the keys for hashing */
- IXGBE_WRITE_REG(hw, IXGBE_FDIRHKEY, IXGBE_ATR_BUCKET_HASH_KEY);
- IXGBE_WRITE_REG(hw, IXGBE_FDIRSKEY, IXGBE_ATR_SIGNATURE_HASH_KEY);
-
/*
- * Poll init-done after we write the register. Estimated times:
- * 10G: PBALLOC = 11b, timing is 60us
- * 1G: PBALLOC = 11b, timing is 600us
- * 100M: PBALLOC = 11b, timing is 6ms
- *
- * Multiple these timings by 4 if under full Rx load
- *
- * So we'll poll for IXGBE_FDIR_INIT_DONE_POLL times, sleeping for
- * 1 msec per poll time. If we're at line rate and drop to 100M, then
- * this might not finish in our poll time, but we can live with that
- * for now.
+ * Continue setup of fdirctrl register bits:
+ * Move the flexible bytes to use the ethertype - shift 6 words
+ * Set the maximum length per hash bucket to 0xA filters
+ * Send interrupt when 64 filters are left
*/
+ fdirctrl |= (0x6 << IXGBE_FDIRCTRL_FLEX_SHIFT) |
+ (0xA << IXGBE_FDIRCTRL_MAX_LENGTH_SHIFT) |
+ (4 << IXGBE_FDIRCTRL_FULL_THRESH_SHIFT);
- /* Set the maximum length per hash bucket to 0xA filters */
- fdirctrl |= (0xA << IXGBE_FDIRCTRL_MAX_LENGTH_SHIFT);
-
- IXGBE_WRITE_REG(hw, IXGBE_FDIRCTRL, fdirctrl);
- IXGBE_WRITE_FLUSH(hw);
- for (i = 0; i < IXGBE_FDIR_INIT_DONE_POLL; i++) {
- if (IXGBE_READ_REG(hw, IXGBE_FDIRCTRL) &
- IXGBE_FDIRCTRL_INIT_DONE)
- break;
- usleep_range(1000, 2000);
- }
- if (i >= IXGBE_FDIR_INIT_DONE_POLL)
- hw_dbg(hw, "Flow Director Perfect poll time exceeded!\n");
+ /* write hashes and fdirctrl register, poll for completion */
+ ixgbe_fdir_enable_82599(hw, fdirctrl);
return 0;
}
-
/**
- * ixgbe_atr_compute_hash_82599 - Compute the hashes for SW ATR
- * @stream: input bitstream to compute the hash on
- * @key: 32-bit hash key
+ * ixgbe_init_fdir_perfect_82599 - Initialize Flow Director perfect filters
+ * @hw: pointer to hardware structure
+ * @fdirctrl: value to write to flow director control register, initially
+ * contains just the value of the Rx packet buffer allocation
**/
-static u32 ixgbe_atr_compute_hash_82599(union ixgbe_atr_input *atr_input,
- u32 key)
+s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 fdirctrl)
{
/*
- * The algorithm is as follows:
- * Hash[15:0] = Sum { S[n] x K[n+16] }, n = 0...350
- * where Sum {A[n]}, n = 0...n is bitwise XOR of A[0], A[1]...A[n]
- * and A[n] x B[n] is bitwise AND between same length strings
- *
- * K[n] is 16 bits, defined as:
- * for n modulo 32 >= 15, K[n] = K[n % 32 : (n % 32) - 15]
- * for n modulo 32 < 15, K[n] =
- * K[(n % 32:0) | (31:31 - (14 - (n % 32)))]
- *
- * S[n] is 16 bits, defined as:
- * for n >= 15, S[n] = S[n:n - 15]
- * for n < 15, S[n] = S[(n:0) | (350:350 - (14 - n))]
- *
- * To simplify for programming, the algorithm is implemented
- * in software this way:
- *
- * key[31:0], hi_hash_dword[31:0], lo_hash_dword[31:0], hash[15:0]
- *
- * for (i = 0; i < 352; i+=32)
- * hi_hash_dword[31:0] ^= Stream[(i+31):i];
- *
- * lo_hash_dword[15:0] ^= Stream[15:0];
- * lo_hash_dword[15:0] ^= hi_hash_dword[31:16];
- * lo_hash_dword[31:16] ^= hi_hash_dword[15:0];
- *
- * hi_hash_dword[31:0] ^= Stream[351:320];
- *
- * if(key[0])
- * hash[15:0] ^= Stream[15:0];
- *
- * for (i = 0; i < 16; i++) {
- * if (key[i])
- * hash[15:0] ^= lo_hash_dword[(i+15):i];
- * if (key[i + 16])
- * hash[15:0] ^= hi_hash_dword[(i+15):i];
- * }
- *
- */
- __be32 common_hash_dword = 0;
- u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
- u32 hash_result = 0;
- u8 i;
-
- /* record the flow_vm_vlan bits as they are a key part to the hash */
- flow_vm_vlan = ntohl(atr_input->dword_stream[0]);
-
- /* generate common hash dword */
- for (i = 10; i; i -= 2)
- common_hash_dword ^= atr_input->dword_stream[i] ^
- atr_input->dword_stream[i - 1];
-
- hi_hash_dword = ntohl(common_hash_dword);
-
- /* low dword is word swapped version of common */
- lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);
-
- /* apply flow ID/VM pool/VLAN ID bits to hash words */
- hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16);
-
- /* Process bits 0 and 16 */
- if (key & 0x0001) hash_result ^= lo_hash_dword;
- if (key & 0x00010000) hash_result ^= hi_hash_dword;
-
- /*
- * apply flow ID/VM pool/VLAN ID bits to lo hash dword, we had to
- * delay this because bit 0 of the stream should not be processed
- * so we do not add the vlan until after bit 0 was processed
+ * Continue setup of fdirctrl register bits:
+ * Turn perfect match filtering on
+ * Report hash in RSS field of Rx wb descriptor
+ * Initialize the drop queue
+ * Move the flexible bytes to use the ethertype - shift 6 words
+ * Set the maximum length per hash bucket to 0xA filters
+ * Send interrupt when 64 (0x4 * 16) filters are left
*/
- lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16);
+ fdirctrl |= IXGBE_FDIRCTRL_PERFECT_MATCH |
+ IXGBE_FDIRCTRL_REPORT_STATUS |
+ (IXGBE_FDIR_DROP_QUEUE << IXGBE_FDIRCTRL_DROP_Q_SHIFT) |
+ (0x6 << IXGBE_FDIRCTRL_FLEX_SHIFT) |
+ (0xA << IXGBE_FDIRCTRL_MAX_LENGTH_SHIFT) |
+ (4 << IXGBE_FDIRCTRL_FULL_THRESH_SHIFT);
+ /* write hashes and fdirctrl register, poll for completion */
+ ixgbe_fdir_enable_82599(hw, fdirctrl);
- /* process the remaining 30 bits in the key 2 bits at a time */
- for (i = 15; i; i-- ) {
- if (key & (0x0001 << i)) hash_result ^= lo_hash_dword >> i;
- if (key & (0x00010000 << i)) hash_result ^= hi_hash_dword >> i;
- }
-
- return hash_result & IXGBE_ATR_HASH_MASK;
+ return 0;
}
/*
@@ -1514,7 +1335,6 @@ s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
*/
fdirhashcmd = (u64)fdircmd << 32;
fdirhashcmd |= ixgbe_atr_compute_sig_hash_82599(input, common);
-
IXGBE_WRITE_REG64(hw, IXGBE_FDIRHASH, fdirhashcmd);
hw_dbg(hw, "Tx Queue=%x hash=%x\n", queue, (u32)fdirhashcmd);
@@ -1522,6 +1342,101 @@ s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
return 0;
}
+#define IXGBE_COMPUTE_BKT_HASH_ITERATION(_n) \
+do { \
+ u32 n = (_n); \
+ if (IXGBE_ATR_BUCKET_HASH_KEY & (0x01 << n)) \
+ bucket_hash ^= lo_hash_dword >> n; \
+ if (IXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \
+ bucket_hash ^= hi_hash_dword >> n; \
+} while (0);
+
+/**
+ * ixgbe_atr_compute_perfect_hash_82599 - Compute the perfect filter hash
+ * @atr_input: input bitstream to compute the hash on
+ * @input_mask: mask for the input bitstream
+ *
+ * This function serves two main purposes. First it applys the input_mask
+ * to the atr_input resulting in a cleaned up atr_input data stream.
+ * Secondly it computes the hash and stores it in the bkt_hash field at
+ * the end of the input byte stream. This way it will be available for
+ * future use without needing to recompute the hash.
+ **/
+void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
+ union ixgbe_atr_input *input_mask)
+{
+
+ u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
+ u32 bucket_hash = 0;
+
+ /* Apply masks to input data */
+ input->dword_stream[0] &= input_mask->dword_stream[0];
+ input->dword_stream[1] &= input_mask->dword_stream[1];
+ input->dword_stream[2] &= input_mask->dword_stream[2];
+ input->dword_stream[3] &= input_mask->dword_stream[3];
+ input->dword_stream[4] &= input_mask->dword_stream[4];
+ input->dword_stream[5] &= input_mask->dword_stream[5];
+ input->dword_stream[6] &= input_mask->dword_stream[6];
+ input->dword_stream[7] &= input_mask->dword_stream[7];
+ input->dword_stream[8] &= input_mask->dword_stream[8];
+ input->dword_stream[9] &= input_mask->dword_stream[9];
+ input->dword_stream[10] &= input_mask->dword_stream[10];
+
+ /* record the flow_vm_vlan bits as they are a key part to the hash */
+ flow_vm_vlan = ntohl(input->dword_stream[0]);
+
+ /* generate common hash dword */
+ hi_hash_dword = ntohl(input->dword_stream[1] ^
+ input->dword_stream[2] ^
+ input->dword_stream[3] ^
+ input->dword_stream[4] ^
+ input->dword_stream[5] ^
+ input->dword_stream[6] ^
+ input->dword_stream[7] ^
+ input->dword_stream[8] ^
+ input->dword_stream[9] ^
+ input->dword_stream[10]);
+
+ /* low dword is word swapped version of common */
+ lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);
+
+ /* apply flow ID/VM pool/VLAN ID bits to hash words */
+ hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16);
+
+ /* Process bits 0 and 16 */
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(0);
+
+ /*
+ * apply flow ID/VM pool/VLAN ID bits to lo hash dword, we had to
+ * delay this because bit 0 of the stream should not be processed
+ * so we do not add the vlan until after bit 0 was processed
+ */
+ lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16);
+
+ /* Process remaining 30 bit of the key */
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(1);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(2);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(3);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(4);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(5);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(6);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(7);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(8);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(9);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(10);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(11);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(12);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(13);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(14);
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(15);
+
+ /*
+ * Limit hash to 13 bits since max bucket count is 8K.
+ * Store result at the end of the input stream.
+ */
+ input->formatted.bkt_hash = bucket_hash & 0x1FFF;
+}
+
/**
* ixgbe_get_fdirtcpm_82599 - generate a tcp port from atr_input_masks
* @input_mask: mask to be bit swapped
@@ -1531,11 +1446,11 @@ s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
* generate a correctly swapped value we need to bit swap the mask and that
* is what is accomplished by this function.
**/
-static u32 ixgbe_get_fdirtcpm_82599(struct ixgbe_atr_input_masks *input_masks)
+static u32 ixgbe_get_fdirtcpm_82599(union ixgbe_atr_input *input_mask)
{
- u32 mask = ntohs(input_masks->dst_port_mask);
+ u32 mask = ntohs(input_mask->formatted.dst_port);
mask <<= IXGBE_FDIRTCPM_DPORTM_SHIFT;
- mask |= ntohs(input_masks->src_port_mask);
+ mask |= ntohs(input_mask->formatted.src_port);
mask = ((mask & 0x55555555) << 1) | ((mask & 0xAAAAAAAA) >> 1);
mask = ((mask & 0x33333333) << 2) | ((mask & 0xCCCCCCCC) >> 2);
mask = ((mask & 0x0F0F0F0F) << 4) | ((mask & 0xF0F0F0F0) >> 4);
@@ -1557,52 +1472,14 @@ static u32 ixgbe_get_fdirtcpm_82599(struct ixgbe_atr_input_masks *input_masks)
IXGBE_WRITE_REG((a), (reg), IXGBE_STORE_AS_BE32(ntohl(value)))
#define IXGBE_STORE_AS_BE16(_value) \
- (((u16)(_value) >> 8) | ((u16)(_value) << 8))
+ ntohs(((u16)(_value) >> 8) | ((u16)(_value) << 8))
-/**
- * ixgbe_fdir_add_perfect_filter_82599 - Adds a perfect filter
- * @hw: pointer to hardware structure
- * @input: input bitstream
- * @input_masks: bitwise masks for relevant fields
- * @soft_id: software index into the silicon hash tables for filter storage
- * @queue: queue index to direct traffic to
- *
- * Note that the caller to this function must lock before calling, since the
- * hardware writes must be protected from one another.
- **/
-s32 ixgbe_fdir_add_perfect_filter_82599(struct ixgbe_hw *hw,
- union ixgbe_atr_input *input,
- struct ixgbe_atr_input_masks *input_masks,
- u16 soft_id, u8 queue)
+s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw,
+ union ixgbe_atr_input *input_mask)
{
- u32 fdirhash;
- u32 fdircmd;
- u32 fdirport, fdirtcpm;
- u32 fdirvlan;
- /* start with VLAN, flex bytes, VM pool, and IPv6 destination masked */
- u32 fdirm = IXGBE_FDIRM_VLANID | IXGBE_FDIRM_VLANP | IXGBE_FDIRM_FLEX |
- IXGBE_FDIRM_POOL | IXGBE_FDIRM_DIPv6;
-
- /*
- * Check flow_type formatting, and bail out before we touch the hardware
- * if there's a configuration issue
- */
- switch (input->formatted.flow_type) {
- case IXGBE_ATR_FLOW_TYPE_IPV4:
- /* use the L4 protocol mask for raw IPv4/IPv6 traffic */
- fdirm |= IXGBE_FDIRM_L4P;
- case IXGBE_ATR_FLOW_TYPE_SCTPV4:
- if (input_masks->dst_port_mask || input_masks->src_port_mask) {
- hw_dbg(hw, " Error on src/dst port mask\n");
- return IXGBE_ERR_CONFIG;
- }
- case IXGBE_ATR_FLOW_TYPE_TCPV4:
- case IXGBE_ATR_FLOW_TYPE_UDPV4:
- break;
- default:
- hw_dbg(hw, " Error on flow type input\n");
- return IXGBE_ERR_CONFIG;
- }
+ /* mask IPv6 since it is currently not supported */
+ u32 fdirm = IXGBE_FDIRM_DIPv6;
+ u32 fdirtcpm;
/*
* Program the relevant mask registers. If src/dst_port or src/dst_addr
@@ -1614,41 +1491,71 @@ s32 ixgbe_fdir_add_perfect_filter_82599(struct ixgbe_hw *hw,
* point in time.
*/
- /* Program FDIRM */
- switch (ntohs(input_masks->vlan_id_mask) & 0xEFFF) {
- case 0xEFFF:
- /* Unmask VLAN ID - bit 0 and fall through to unmask prio */
- fdirm &= ~IXGBE_FDIRM_VLANID;
- case 0xE000:
- /* Unmask VLAN prio - bit 1 */
- fdirm &= ~IXGBE_FDIRM_VLANP;
+ /* verify bucket hash is cleared on hash generation */
+ if (input_mask->formatted.bkt_hash)
+ hw_dbg(hw, " bucket hash should always be 0 in mask\n");
+
+ /* Program FDIRM and verify partial masks */
+ switch (input_mask->formatted.vm_pool & 0x7F) {
+ case 0x0:
+ fdirm |= IXGBE_FDIRM_POOL;
+ case 0x7F:
break;
- case 0x0FFF:
- /* Unmask VLAN ID - bit 0 */
- fdirm &= ~IXGBE_FDIRM_VLANID;
+ default:
+ hw_dbg(hw, " Error on vm pool mask\n");
+ return IXGBE_ERR_CONFIG;
+ }
+
+ switch (input_mask->formatted.flow_type & IXGBE_ATR_L4TYPE_MASK) {
+ case 0x0:
+ fdirm |= IXGBE_FDIRM_L4P;
+ if (input_mask->formatted.dst_port ||
+ input_mask->formatted.src_port) {
+ hw_dbg(hw, " Error on src/dst port mask\n");
+ return IXGBE_ERR_CONFIG;
+ }
+ case IXGBE_ATR_L4TYPE_MASK:
break;
+ default:
+ hw_dbg(hw, " Error on flow type mask\n");
+ return IXGBE_ERR_CONFIG;
+ }
+
+ switch (ntohs(input_mask->formatted.vlan_id) & 0xEFFF) {
case 0x0000:
- /* do nothing, vlans already masked */
+ /* mask VLAN ID, fall through to mask VLAN priority */
+ fdirm |= IXGBE_FDIRM_VLANID;
+ case 0x0FFF:
+ /* mask VLAN priority */
+ fdirm |= IXGBE_FDIRM_VLANP;
+ break;
+ case 0xE000:
+ /* mask VLAN ID only, fall through */
+ fdirm |= IXGBE_FDIRM_VLANID;
+ case 0xEFFF:
+ /* no VLAN fields masked */
break;
default:
hw_dbg(hw, " Error on VLAN mask\n");
return IXGBE_ERR_CONFIG;
}
- if (input_masks->flex_mask & 0xFFFF) {
- if ((input_masks->flex_mask & 0xFFFF) != 0xFFFF) {
- hw_dbg(hw, " Error on flexible byte mask\n");
- return IXGBE_ERR_CONFIG;
- }
- /* Unmask Flex Bytes - bit 4 */
- fdirm &= ~IXGBE_FDIRM_FLEX;
+ switch (input_mask->formatted.flex_bytes & 0xFFFF) {
+ case 0x0000:
+ /* Mask Flex Bytes, fall through */
+ fdirm |= IXGBE_FDIRM_FLEX;
+ case 0xFFFF:
+ break;
+ default:
+ hw_dbg(hw, " Error on flexible byte mask\n");
+ return IXGBE_ERR_CONFIG;
}
/* Now mask VM pool and destination IPv6 - bits 5 and 2 */
IXGBE_WRITE_REG(hw, IXGBE_FDIRM, fdirm);
/* store the TCP/UDP port masks, bit reversed from port layout */
- fdirtcpm = ixgbe_get_fdirtcpm_82599(input_masks);
+ fdirtcpm = ixgbe_get_fdirtcpm_82599(input_mask);
/* write both the same so that UDP and TCP use the same mask */
IXGBE_WRITE_REG(hw, IXGBE_FDIRTCPM, ~fdirtcpm);
@@ -1656,24 +1563,32 @@ s32 ixgbe_fdir_add_perfect_filter_82599(struct ixgbe_hw *hw,
/* store source and destination IP masks (big-enian) */
IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRSIP4M,
- ~input_masks->src_ip_mask[0]);
+ ~input_mask->formatted.src_ip[0]);
IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRDIP4M,
- ~input_masks->dst_ip_mask[0]);
+ ~input_mask->formatted.dst_ip[0]);
- /* Apply masks to input data */
- input->formatted.vlan_id &= input_masks->vlan_id_mask;
- input->formatted.flex_bytes &= input_masks->flex_mask;
- input->formatted.src_port &= input_masks->src_port_mask;
- input->formatted.dst_port &= input_masks->dst_port_mask;
- input->formatted.src_ip[0] &= input_masks->src_ip_mask[0];
- input->formatted.dst_ip[0] &= input_masks->dst_ip_mask[0];
+ return 0;
+}
- /* record vlan (little-endian) and flex_bytes(big-endian) */
- fdirvlan =
- IXGBE_STORE_AS_BE16(ntohs(input->formatted.flex_bytes));
- fdirvlan <<= IXGBE_FDIRVLAN_FLEX_SHIFT;
- fdirvlan |= ntohs(input->formatted.vlan_id);
- IXGBE_WRITE_REG(hw, IXGBE_FDIRVLAN, fdirvlan);
+s32 ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw,
+ union ixgbe_atr_input *input,
+ u16 soft_id, u8 queue)
+{
+ u32 fdirport, fdirvlan, fdirhash, fdircmd;
+
+ /* currently IPv6 is not supported, must be programmed with 0 */
+ IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRSIPv6(0),
+ input->formatted.src_ip[0]);
+ IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRSIPv6(1),
+ input->formatted.src_ip[1]);
+ IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRSIPv6(2),
+ input->formatted.src_ip[2]);
+
+ /* record the source address (big-endian) */
+ IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRIPSA, input->formatted.src_ip[0]);
+
+ /* record the first 32 bits of the destination address (big-endian) */
+ IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRIPDA, input->formatted.dst_ip[0]);
/* record source and destination port (little-endian)*/
fdirport = ntohs(input->formatted.dst_port);
@@ -1681,29 +1596,80 @@ s32 ixgbe_fdir_add_perfect_filter_82599(struct ixgbe_hw *hw,
fdirport |= ntohs(input->formatted.src_port);
IXGBE_WRITE_REG(hw, IXGBE_FDIRPORT, fdirport);
- /* record the first 32 bits of the destination address (big-endian) */
- IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRIPDA, input->formatted.dst_ip[0]);
+ /* record vlan (little-endian) and flex_bytes(big-endian) */
+ fdirvlan = IXGBE_STORE_AS_BE16(input->formatted.flex_bytes);
+ fdirvlan <<= IXGBE_FDIRVLAN_FLEX_SHIFT;
+ fdirvlan |= ntohs(input->formatted.vlan_id);
+ IXGBE_WRITE_REG(hw, IXGBE_FDIRVLAN, fdirvlan);
- /* record the source address (big-endian) */
- IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRIPSA, input->formatted.src_ip[0]);
+ /* configure FDIRHASH register */
+ fdirhash = input->formatted.bkt_hash;
+ fdirhash |= soft_id << IXGBE_FDIRHASH_SIG_SW_INDEX_SHIFT;
+ IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash);
+
+ /*
+ * flush all previous writes to make certain registers are
+ * programmed prior to issuing the command
+ */
+ IXGBE_WRITE_FLUSH(hw);
/* configure FDIRCMD register */
fdircmd = IXGBE_FDIRCMD_CMD_ADD_FLOW | IXGBE_FDIRCMD_FILTER_UPDATE |
IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN;
+ if (queue == IXGBE_FDIR_DROP_QUEUE)
+ fdircmd |= IXGBE_FDIRCMD_DROP;
fdircmd |= input->formatted.flow_type << IXGBE_FDIRCMD_FLOW_TYPE_SHIFT;
fdircmd |= (u32)queue << IXGBE_FDIRCMD_RX_QUEUE_SHIFT;
+ fdircmd |= (u32)input->formatted.vm_pool << IXGBE_FDIRCMD_VT_POOL_SHIFT;
- /* we only want the bucket hash so drop the upper 16 bits */
- fdirhash = ixgbe_atr_compute_hash_82599(input,
- IXGBE_ATR_BUCKET_HASH_KEY);
- fdirhash |= soft_id << IXGBE_FDIRHASH_SIG_SW_INDEX_SHIFT;
-
- IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash);
IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD, fdircmd);
return 0;
}
+s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw,
+ union ixgbe_atr_input *input,
+ u16 soft_id)
+{
+ u32 fdirhash;
+ u32 fdircmd = 0;
+ u32 retry_count;
+ s32 err = 0;
+
+ /* configure FDIRHASH register */
+ fdirhash = input->formatted.bkt_hash;
+ fdirhash |= soft_id << IXGBE_FDIRHASH_SIG_SW_INDEX_SHIFT;
+ IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash);
+
+ /* flush hash to HW */
+ IXGBE_WRITE_FLUSH(hw);
+
+ /* Query if filter is present */
+ IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD, IXGBE_FDIRCMD_CMD_QUERY_REM_FILT);
+
+ for (retry_count = 10; retry_count; retry_count--) {
+ /* allow 10us for query to process */
+ udelay(10);
+ /* verify query completed successfully */
+ fdircmd = IXGBE_READ_REG(hw, IXGBE_FDIRCMD);
+ if (!(fdircmd & IXGBE_FDIRCMD_CMD_MASK))
+ break;
+ }
+
+ if (!retry_count)
+ err = IXGBE_ERR_FDIR_REINIT_FAILED;
+
+ /* if filter exists in hardware then remove it */
+ if (fdircmd & IXGBE_FDIRCMD_FILTER_VALID) {
+ IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash);
+ IXGBE_WRITE_FLUSH(hw);
+ IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD,
+ IXGBE_FDIRCMD_CMD_REMOVE_FLOW);
+ }
+
+ return err;
+}
+
/**
* ixgbe_read_analog_reg8_82599 - Reads 8 bit Omer analog register
* @hw: pointer to hardware structure
@@ -1834,7 +1800,6 @@ static u32 ixgbe_get_supported_physical_layer_82599(struct ixgbe_hw *hw)
switch (hw->phy.type) {
case ixgbe_phy_tn:
- case ixgbe_phy_aq:
case ixgbe_phy_cu_unknown:
hw->phy.ops.read_reg(hw, MDIO_PMA_EXTABLE, MDIO_MMD_PMAPMD,
&ext_ability);
@@ -2146,6 +2111,7 @@ static struct ixgbe_mac_operations mac_ops_82599 = {
.read_analog_reg8 = &ixgbe_read_analog_reg8_82599,
.write_analog_reg8 = &ixgbe_write_analog_reg8_82599,
.setup_link = &ixgbe_setup_mac_link_82599,
+ .set_rxpba = &ixgbe_set_rxpba_generic,
.check_link = &ixgbe_check_mac_link_generic,
.get_link_capabilities = &ixgbe_get_link_capabilities_82599,
.led_on = &ixgbe_led_on_generic,
@@ -2163,6 +2129,7 @@ static struct ixgbe_mac_operations mac_ops_82599 = {
.clear_vfta = &ixgbe_clear_vfta_generic,
.set_vfta = &ixgbe_set_vfta_generic,
.fc_enable = &ixgbe_fc_enable_generic,
+ .set_fw_drv_ver = &ixgbe_set_fw_drv_ver_generic,
.init_uta_tables = &ixgbe_init_uta_tables_generic,
.setup_sfp = &ixgbe_setup_sfp_modules_82599,
.set_mac_anti_spoofing = &ixgbe_set_mac_anti_spoofing,
diff --git a/drivers/net/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
index b894b42a741c..f1365fef4ed2 100644
--- a/drivers/net/ixgbe/ixgbe_common.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
@@ -61,6 +61,7 @@ static s32 ixgbe_write_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset,
u16 words, u16 *data);
static s32 ixgbe_detect_eeprom_page_size_generic(struct ixgbe_hw *hw,
u16 offset);
+static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw);
/**
* ixgbe_start_hw_generic - Prepare hardware for Tx/Rx
@@ -225,8 +226,9 @@ s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw)
IXGBE_READ_REG(hw, IXGBE_GORCH);
IXGBE_READ_REG(hw, IXGBE_GOTCL);
IXGBE_READ_REG(hw, IXGBE_GOTCH);
- for (i = 0; i < 8; i++)
- IXGBE_READ_REG(hw, IXGBE_RNBC(i));
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ for (i = 0; i < 8; i++)
+ IXGBE_READ_REG(hw, IXGBE_RNBC(i));
IXGBE_READ_REG(hw, IXGBE_RUC);
IXGBE_READ_REG(hw, IXGBE_RFC);
IXGBE_READ_REG(hw, IXGBE_ROC);
@@ -495,7 +497,6 @@ void ixgbe_set_lan_id_multi_port_pcie(struct ixgbe_hw *hw)
**/
s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw)
{
- u32 number_of_queues;
u32 reg_val;
u16 i;
@@ -506,35 +507,35 @@ s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw)
hw->adapter_stopped = true;
/* Disable the receive unit */
- reg_val = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
- reg_val &= ~(IXGBE_RXCTRL_RXEN);
- IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, reg_val);
- IXGBE_WRITE_FLUSH(hw);
- usleep_range(2000, 4000);
+ IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, 0);
- /* Clear interrupt mask to stop from interrupts being generated */
+ /* Clear interrupt mask to stop interrupts from being generated */
IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_IRQ_CLEAR_MASK);
- /* Clear any pending interrupts */
+ /* Clear any pending interrupts, flush previous writes */
IXGBE_READ_REG(hw, IXGBE_EICR);
/* Disable the transmit unit. Each queue must be disabled. */
- number_of_queues = hw->mac.max_tx_queues;
- for (i = 0; i < number_of_queues; i++) {
- reg_val = IXGBE_READ_REG(hw, IXGBE_TXDCTL(i));
- if (reg_val & IXGBE_TXDCTL_ENABLE) {
- reg_val &= ~IXGBE_TXDCTL_ENABLE;
- IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(i), reg_val);
- }
+ for (i = 0; i < hw->mac.max_tx_queues; i++)
+ IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(i), IXGBE_TXDCTL_SWFLSH);
+
+ /* Disable the receive unit by stopping each queue */
+ for (i = 0; i < hw->mac.max_rx_queues; i++) {
+ reg_val = IXGBE_READ_REG(hw, IXGBE_RXDCTL(i));
+ reg_val &= ~IXGBE_RXDCTL_ENABLE;
+ reg_val |= IXGBE_RXDCTL_SWFLSH;
+ IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(i), reg_val);
}
+ /* flush all queues disables */
+ IXGBE_WRITE_FLUSH(hw);
+ usleep_range(1000, 2000);
+
/*
* Prevent the PCI-E bus from from hanging by disabling PCI-E master
* access and verify no pending requests
*/
- ixgbe_disable_pcie_master(hw);
-
- return 0;
+ return ixgbe_disable_pcie_master(hw);
}
/**
@@ -1292,7 +1293,7 @@ static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw)
udelay(5);
ixgbe_standby_eeprom(hw);
- };
+ }
/*
* On some parts, SPI write time could vary from 0-20mSec on 3.3V
@@ -1374,7 +1375,7 @@ static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data,
* EEPROM
*/
mask = mask >> 1;
- };
+ }
/* We leave the "DI" bit set to "0" when we leave this routine. */
eec &= ~IXGBE_EEC_DI;
@@ -1931,7 +1932,6 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num)
s32 ret_val = 0;
u32 mflcn_reg, fccfg_reg;
u32 reg;
- u32 rx_pba_size;
u32 fcrtl, fcrth;
#ifdef CONFIG_DCB
@@ -2011,11 +2011,8 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num)
IXGBE_WRITE_REG(hw, IXGBE_MFLCN, mflcn_reg);
IXGBE_WRITE_REG(hw, IXGBE_FCCFG, fccfg_reg);
- rx_pba_size = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(packetbuf_num));
- rx_pba_size >>= IXGBE_RXPBSIZE_SHIFT;
-
- fcrth = (rx_pba_size - hw->fc.high_water) << 10;
- fcrtl = (rx_pba_size - hw->fc.low_water) << 10;
+ fcrth = hw->fc.high_water[packetbuf_num] << 10;
+ fcrtl = hw->fc.low_water << 10;
if (hw->fc.current_mode & ixgbe_fc_tx_pause) {
fcrth |= IXGBE_FCRTH_FCEN;
@@ -2121,8 +2118,8 @@ static s32 ixgbe_fc_autoneg_fiber(struct ixgbe_hw *hw)
*/
linkstat = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA);
- if (((linkstat & IXGBE_PCS1GLSTA_AN_COMPLETE) == 0) ||
- ((linkstat & IXGBE_PCS1GLSTA_AN_TIMED_OUT) == 1)) {
+ if ((!!(linkstat & IXGBE_PCS1GLSTA_AN_COMPLETE) == 0) ||
+ (!!(linkstat & IXGBE_PCS1GLSTA_AN_TIMED_OUT) == 1)) {
ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED;
goto out;
}
@@ -2292,7 +2289,9 @@ static s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num)
* Validate the water mark configuration. Zero water marks are invalid
* because it causes the controller to just blast out fc packets.
*/
- if (!hw->fc.low_water || !hw->fc.high_water || !hw->fc.pause_time) {
+ if (!hw->fc.low_water ||
+ !hw->fc.high_water[packetbuf_num] ||
+ !hw->fc.pause_time) {
hw_dbg(hw, "Invalid water mark configuration\n");
ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
goto out;
@@ -2457,75 +2456,57 @@ out:
* bit hasn't caused the master requests to be disabled, else 0
* is returned signifying master requests disabled.
**/
-s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
+static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
{
struct ixgbe_adapter *adapter = hw->back;
- u32 i;
- u32 reg_val;
- u32 number_of_queues;
s32 status = 0;
- u16 dev_status = 0;
+ u32 i;
+ u16 value;
- /* Just jump out if bus mastering is already disabled */
+ /* Always set this bit to ensure any future transactions are blocked */
+ IXGBE_WRITE_REG(hw, IXGBE_CTRL, IXGBE_CTRL_GIO_DIS);
+
+ /* Exit if master requests are blocked */
if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO))
goto out;
- /* Disable the receive unit by stopping each queue */
- number_of_queues = hw->mac.max_rx_queues;
- for (i = 0; i < number_of_queues; i++) {
- reg_val = IXGBE_READ_REG(hw, IXGBE_RXDCTL(i));
- if (reg_val & IXGBE_RXDCTL_ENABLE) {
- reg_val &= ~IXGBE_RXDCTL_ENABLE;
- IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(i), reg_val);
- }
- }
-
- reg_val = IXGBE_READ_REG(hw, IXGBE_CTRL);
- reg_val |= IXGBE_CTRL_GIO_DIS;
- IXGBE_WRITE_REG(hw, IXGBE_CTRL, reg_val);
-
+ /* Poll for master request bit to clear */
for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) {
- if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO))
- goto check_device_status;
udelay(100);
+ if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO))
+ goto out;
}
+ /*
+ * Two consecutive resets are required via CTRL.RST per datasheet
+ * 5.2.5.3.2 Master Disable. We set a flag to inform the reset routine
+ * of this need. The first reset prevents new master requests from
+ * being issued by our device. We then must wait 1usec or more for any
+ * remaining completions from the PCIe bus to trickle in, and then reset
+ * again to clear out any effects they may have had on our device.
+ */
hw_dbg(hw, "GIO Master Disable bit didn't clear - requesting resets\n");
- status = IXGBE_ERR_MASTER_REQUESTS_PENDING;
+ hw->mac.flags |= IXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
/*
* Before proceeding, make sure that the PCIe block does not have
* transactions pending.
*/
-check_device_status:
for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) {
- pci_read_config_word(adapter->pdev, IXGBE_PCI_DEVICE_STATUS,
- &dev_status);
- if (!(dev_status & IXGBE_PCI_DEVICE_STATUS_TRANSACTION_PENDING))
- break;
udelay(100);
+ pci_read_config_word(adapter->pdev, IXGBE_PCI_DEVICE_STATUS,
+ &value);
+ if (!(value & IXGBE_PCI_DEVICE_STATUS_TRANSACTION_PENDING))
+ goto out;
}
- if (i == IXGBE_PCI_MASTER_DISABLE_TIMEOUT)
- hw_dbg(hw, "PCIe transaction pending bit also did not clear.\n");
- else
- goto out;
-
- /*
- * Two consecutive resets are required via CTRL.RST per datasheet
- * 5.2.5.3.2 Master Disable. We set a flag to inform the reset routine
- * of this need. The first reset prevents new master requests from
- * being issued by our device. We then must wait 1usec for any
- * remaining completions from the PCIe bus to trickle in, and then reset
- * again to clear out any effects they may have had on our device.
- */
- hw->mac.flags |= IXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
+ hw_dbg(hw, "PCIe transaction pending bit also did not clear.\n");
+ status = IXGBE_ERR_MASTER_REQUESTS_PENDING;
out:
return status;
}
-
/**
* ixgbe_acquire_swfw_sync - Acquire SWFW semaphore
* @hw: pointer to hardware structure
@@ -2632,6 +2613,7 @@ s32 ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index)
autoc_reg |= IXGBE_AUTOC_AN_RESTART;
autoc_reg |= IXGBE_AUTOC_FLU;
IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc_reg);
+ IXGBE_WRITE_FLUSH(hw);
usleep_range(10000, 20000);
}
@@ -3113,12 +3095,6 @@ s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
else
*speed = IXGBE_LINK_SPEED_UNKNOWN;
- /* if link is down, zero out the current_mode */
- if (*link_up == false) {
- hw->fc.current_mode = ixgbe_fc_none;
- hw->fc.fc_was_autonegged = false;
- }
-
return 0;
}
@@ -3267,3 +3243,286 @@ s32 ixgbe_get_device_caps_generic(struct ixgbe_hw *hw, u16 *device_caps)
return 0;
}
+
+/**
+ * ixgbe_set_rxpba_generic - Initialize RX packet buffer
+ * @hw: pointer to hardware structure
+ * @num_pb: number of packet buffers to allocate
+ * @headroom: reserve n KB of headroom
+ * @strategy: packet buffer allocation strategy
+ **/
+void ixgbe_set_rxpba_generic(struct ixgbe_hw *hw,
+ int num_pb,
+ u32 headroom,
+ int strategy)
+{
+ u32 pbsize = hw->mac.rx_pb_size;
+ int i = 0;
+ u32 rxpktsize, txpktsize, txpbthresh;
+
+ /* Reserve headroom */
+ pbsize -= headroom;
+
+ if (!num_pb)
+ num_pb = 1;
+
+ /* Divide remaining packet buffer space amongst the number
+ * of packet buffers requested using supplied strategy.
+ */
+ switch (strategy) {
+ case (PBA_STRATEGY_WEIGHTED):
+ /* pba_80_48 strategy weight first half of packet buffer with
+ * 5/8 of the packet buffer space.
+ */
+ rxpktsize = ((pbsize * 5 * 2) / (num_pb * 8));
+ pbsize -= rxpktsize * (num_pb / 2);
+ rxpktsize <<= IXGBE_RXPBSIZE_SHIFT;
+ for (; i < (num_pb / 2); i++)
+ IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), rxpktsize);
+ /* Fall through to configure remaining packet buffers */
+ case (PBA_STRATEGY_EQUAL):
+ /* Divide the remaining Rx packet buffer evenly among the TCs */
+ rxpktsize = (pbsize / (num_pb - i)) << IXGBE_RXPBSIZE_SHIFT;
+ for (; i < num_pb; i++)
+ IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), rxpktsize);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Setup Tx packet buffer and threshold equally for all TCs
+ * TXPBTHRESH register is set in K so divide by 1024 and subtract
+ * 10 since the largest packet we support is just over 9K.
+ */
+ txpktsize = IXGBE_TXPBSIZE_MAX / num_pb;
+ txpbthresh = (txpktsize / 1024) - IXGBE_TXPKT_SIZE_MAX;
+ for (i = 0; i < num_pb; i++) {
+ IXGBE_WRITE_REG(hw, IXGBE_TXPBSIZE(i), txpktsize);
+ IXGBE_WRITE_REG(hw, IXGBE_TXPBTHRESH(i), txpbthresh);
+ }
+
+ /* Clear unused TCs, if any, to zero buffer size*/
+ for (; i < IXGBE_MAX_PB; i++) {
+ IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_TXPBSIZE(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_TXPBTHRESH(i), 0);
+ }
+}
+
+/**
+ * ixgbe_calculate_checksum - Calculate checksum for buffer
+ * @buffer: pointer to EEPROM
+ * @length: size of EEPROM to calculate a checksum for
+ * Calculates the checksum for some buffer on a specified length. The
+ * checksum calculated is returned.
+ **/
+static u8 ixgbe_calculate_checksum(u8 *buffer, u32 length)
+{
+ u32 i;
+ u8 sum = 0;
+
+ if (!buffer)
+ return 0;
+
+ for (i = 0; i < length; i++)
+ sum += buffer[i];
+
+ return (u8) (0 - sum);
+}
+
+/**
+ * ixgbe_host_interface_command - Issue command to manageability block
+ * @hw: pointer to the HW structure
+ * @buffer: contains the command to write and where the return status will
+ * be placed
+ * @lenght: lenght of buffer, must be multiple of 4 bytes
+ *
+ * Communicates with the manageability block. On success return 0
+ * else return IXGBE_ERR_HOST_INTERFACE_COMMAND.
+ **/
+static s32 ixgbe_host_interface_command(struct ixgbe_hw *hw, u32 *buffer,
+ u32 length)
+{
+ u32 hicr, i, bi;
+ u32 hdr_size = sizeof(struct ixgbe_hic_hdr);
+ u8 buf_len, dword_len;
+
+ s32 ret_val = 0;
+
+ if (length == 0 || length & 0x3 ||
+ length > IXGBE_HI_MAX_BLOCK_BYTE_LENGTH) {
+ hw_dbg(hw, "Buffer length failure.\n");
+ ret_val = IXGBE_ERR_HOST_INTERFACE_COMMAND;
+ goto out;
+ }
+
+ /* Check that the host interface is enabled. */
+ hicr = IXGBE_READ_REG(hw, IXGBE_HICR);
+ if ((hicr & IXGBE_HICR_EN) == 0) {
+ hw_dbg(hw, "IXGBE_HOST_EN bit disabled.\n");
+ ret_val = IXGBE_ERR_HOST_INTERFACE_COMMAND;
+ goto out;
+ }
+
+ /* Calculate length in DWORDs */
+ dword_len = length >> 2;
+
+ /*
+ * The device driver writes the relevant command block
+ * into the ram area.
+ */
+ for (i = 0; i < dword_len; i++)
+ IXGBE_WRITE_REG_ARRAY(hw, IXGBE_FLEX_MNG,
+ i, cpu_to_le32(buffer[i]));
+
+ /* Setting this bit tells the ARC that a new command is pending. */
+ IXGBE_WRITE_REG(hw, IXGBE_HICR, hicr | IXGBE_HICR_C);
+
+ for (i = 0; i < IXGBE_HI_COMMAND_TIMEOUT; i++) {
+ hicr = IXGBE_READ_REG(hw, IXGBE_HICR);
+ if (!(hicr & IXGBE_HICR_C))
+ break;
+ usleep_range(1000, 2000);
+ }
+
+ /* Check command successful completion. */
+ if (i == IXGBE_HI_COMMAND_TIMEOUT ||
+ (!(IXGBE_READ_REG(hw, IXGBE_HICR) & IXGBE_HICR_SV))) {
+ hw_dbg(hw, "Command has failed with no status valid.\n");
+ ret_val = IXGBE_ERR_HOST_INTERFACE_COMMAND;
+ goto out;
+ }
+
+ /* Calculate length in DWORDs */
+ dword_len = hdr_size >> 2;
+
+ /* first pull in the header so we know the buffer length */
+ for (bi = 0; bi < dword_len; bi++) {
+ buffer[bi] = IXGBE_READ_REG_ARRAY(hw, IXGBE_FLEX_MNG, bi);
+ le32_to_cpus(&buffer[bi]);
+ }
+
+ /* If there is any thing in data position pull it in */
+ buf_len = ((struct ixgbe_hic_hdr *)buffer)->buf_len;
+ if (buf_len == 0)
+ goto out;
+
+ if (length < (buf_len + hdr_size)) {
+ hw_dbg(hw, "Buffer not large enough for reply message.\n");
+ ret_val = IXGBE_ERR_HOST_INTERFACE_COMMAND;
+ goto out;
+ }
+
+ /* Calculate length in DWORDs, add 3 for odd lengths */
+ dword_len = (buf_len + 3) >> 2;
+
+ /* Pull in the rest of the buffer (bi is where we left off)*/
+ for (; bi <= dword_len; bi++) {
+ buffer[bi] = IXGBE_READ_REG_ARRAY(hw, IXGBE_FLEX_MNG, bi);
+ le32_to_cpus(&buffer[bi]);
+ }
+
+out:
+ return ret_val;
+}
+
+/**
+ * ixgbe_set_fw_drv_ver_generic - Sends driver version to firmware
+ * @hw: pointer to the HW structure
+ * @maj: driver version major number
+ * @min: driver version minor number
+ * @build: driver version build number
+ * @sub: driver version sub build number
+ *
+ * Sends driver version number to firmware through the manageability
+ * block. On success return 0
+ * else returns IXGBE_ERR_SWFW_SYNC when encountering an error acquiring
+ * semaphore or IXGBE_ERR_HOST_INTERFACE_COMMAND when command fails.
+ **/
+s32 ixgbe_set_fw_drv_ver_generic(struct ixgbe_hw *hw, u8 maj, u8 min,
+ u8 build, u8 sub)
+{
+ struct ixgbe_hic_drv_info fw_cmd;
+ int i;
+ s32 ret_val = 0;
+
+ if (hw->mac.ops.acquire_swfw_sync(hw, IXGBE_GSSR_SW_MNG_SM) != 0) {
+ ret_val = IXGBE_ERR_SWFW_SYNC;
+ goto out;
+ }
+
+ fw_cmd.hdr.cmd = FW_CEM_CMD_DRIVER_INFO;
+ fw_cmd.hdr.buf_len = FW_CEM_CMD_DRIVER_INFO_LEN;
+ fw_cmd.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
+ fw_cmd.port_num = (u8)hw->bus.func;
+ fw_cmd.ver_maj = maj;
+ fw_cmd.ver_min = min;
+ fw_cmd.ver_build = build;
+ fw_cmd.ver_sub = sub;
+ fw_cmd.hdr.checksum = 0;
+ fw_cmd.hdr.checksum = ixgbe_calculate_checksum((u8 *)&fw_cmd,
+ (FW_CEM_HDR_LEN + fw_cmd.hdr.buf_len));
+ fw_cmd.pad = 0;
+ fw_cmd.pad2 = 0;
+
+ for (i = 0; i <= FW_CEM_MAX_RETRIES; i++) {
+ ret_val = ixgbe_host_interface_command(hw, (u32 *)&fw_cmd,
+ sizeof(fw_cmd));
+ if (ret_val != 0)
+ continue;
+
+ if (fw_cmd.hdr.cmd_or_resp.ret_status ==
+ FW_CEM_RESP_STATUS_SUCCESS)
+ ret_val = 0;
+ else
+ ret_val = IXGBE_ERR_HOST_INTERFACE_COMMAND;
+
+ break;
+ }
+
+ hw->mac.ops.release_swfw_sync(hw, IXGBE_GSSR_SW_MNG_SM);
+out:
+ return ret_val;
+}
+
+/**
+ * ixgbe_clear_tx_pending - Clear pending TX work from the PCIe fifo
+ * @hw: pointer to the hardware structure
+ *
+ * The 82599 and x540 MACs can experience issues if TX work is still pending
+ * when a reset occurs. This function prevents this by flushing the PCIe
+ * buffers on the system.
+ **/
+void ixgbe_clear_tx_pending(struct ixgbe_hw *hw)
+{
+ u32 gcr_ext, hlreg0;
+
+ /*
+ * If double reset is not requested then all transactions should
+ * already be clear and as such there is no work to do
+ */
+ if (!(hw->mac.flags & IXGBE_FLAGS_DOUBLE_RESET_REQUIRED))
+ return;
+
+ /*
+ * Set loopback enable to prevent any transmits from being sent
+ * should the link come up. This assumes that the RXCTRL.RXEN bit
+ * has already been cleared.
+ */
+ hlreg0 = IXGBE_READ_REG(hw, IXGBE_HLREG0);
+ IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0 | IXGBE_HLREG0_LPBK);
+
+ /* initiate cleaning flow for buffers in the PCIe transaction layer */
+ gcr_ext = IXGBE_READ_REG(hw, IXGBE_GCR_EXT);
+ IXGBE_WRITE_REG(hw, IXGBE_GCR_EXT,
+ gcr_ext | IXGBE_GCR_EXT_BUFFERS_CLEAR);
+
+ /* Flush all writes and allow 20usec for all transactions to clear */
+ IXGBE_WRITE_FLUSH(hw);
+ udelay(20);
+
+ /* restore previous register values */
+ IXGBE_WRITE_REG(hw, IXGBE_GCR_EXT, gcr_ext);
+ IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0);
+}
diff --git a/drivers/net/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
index 46be83cfb500..863f9c1f145b 100644
--- a/drivers/net/ixgbe/ixgbe_common.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
@@ -81,7 +81,6 @@ s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw);
s32 ixgbe_validate_mac_addr(u8 *mac_addr);
s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask);
void ixgbe_release_swfw_sync(struct ixgbe_hw *hw, u16 mask);
-s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw);
s32 ixgbe_get_san_mac_addr_generic(struct ixgbe_hw *hw, u8 *san_mac_addr);
s32 ixgbe_set_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq);
s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq);
@@ -99,6 +98,12 @@ s32 ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index);
void ixgbe_set_mac_anti_spoofing(struct ixgbe_hw *hw, bool enable, int pf);
void ixgbe_set_vlan_anti_spoofing(struct ixgbe_hw *hw, bool enable, int vf);
s32 ixgbe_get_device_caps_generic(struct ixgbe_hw *hw, u16 *device_caps);
+s32 ixgbe_set_fw_drv_ver_generic(struct ixgbe_hw *hw, u8 maj, u8 min,
+ u8 build, u8 ver);
+void ixgbe_clear_tx_pending(struct ixgbe_hw *hw);
+
+void ixgbe_set_rxpba_generic(struct ixgbe_hw *hw, int num_pb,
+ u32 headroom, int strategy);
#define IXGBE_WRITE_REG(a, reg, value) writel((value), ((a)->hw_addr + (reg)))
diff --git a/drivers/net/ixgbe/ixgbe_dcb.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
index 686a17aadef3..318caf4bf623 100644
--- a/drivers/net/ixgbe/ixgbe_dcb.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
@@ -40,7 +40,8 @@
* hardware. The IEEE 802.1Qaz specification do not use bandwidth
* groups so this is much simplified from the CEE case.
*/
-s32 ixgbe_ieee_credits(__u8 *bw, __u16 *refill, __u16 *max, int max_frame)
+static s32 ixgbe_ieee_credits(__u8 *bw, __u16 *refill,
+ __u16 *max, int max_frame)
{
int min_percent = 100;
int min_credit, multiplier;
@@ -183,7 +184,7 @@ void ixgbe_dcb_unpack_pfc(struct ixgbe_dcb_config *cfg, u8 *pfc_en)
*pfc_en = 0;
for (i = 0; i < MAX_TRAFFIC_CLASS; i++)
- *pfc_en |= (cfg->tc_config[i].dcb_pfc & 0xF) << i;
+ *pfc_en |= !!(cfg->tc_config[i].dcb_pfc & 0xF) << i;
}
void ixgbe_dcb_unpack_refill(struct ixgbe_dcb_config *cfg, int direction,
@@ -230,6 +231,18 @@ void ixgbe_dcb_unpack_prio(struct ixgbe_dcb_config *cfg, int direction,
}
}
+void ixgbe_dcb_unpack_map(struct ixgbe_dcb_config *cfg, int direction, u8 *map)
+{
+ int i, up;
+ unsigned long bitmap;
+
+ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+ bitmap = cfg->tc_config[i].path[direction].up_to_tc_bitmap;
+ for_each_set_bit(up, &bitmap, MAX_USER_PRIORITY)
+ map[up] = i;
+ }
+}
+
/**
* ixgbe_dcb_hw_config - Config and enable DCB
* @hw: pointer to hardware structure
@@ -244,10 +257,9 @@ s32 ixgbe_dcb_hw_config(struct ixgbe_hw *hw,
u8 pfc_en;
u8 ptype[MAX_TRAFFIC_CLASS];
u8 bwgid[MAX_TRAFFIC_CLASS];
+ u8 prio_tc[MAX_TRAFFIC_CLASS];
u16 refill[MAX_TRAFFIC_CLASS];
u16 max[MAX_TRAFFIC_CLASS];
- /* CEE does not define a priority to tc mapping so map 1:1 */
- u8 prio_tc[MAX_TRAFFIC_CLASS] = {0, 1, 2, 3, 4, 5, 6, 7};
/* Unpack CEE standard containers */
ixgbe_dcb_unpack_pfc(dcb_config, &pfc_en);
@@ -255,18 +267,17 @@ s32 ixgbe_dcb_hw_config(struct ixgbe_hw *hw,
ixgbe_dcb_unpack_max(dcb_config, max);
ixgbe_dcb_unpack_bwgid(dcb_config, DCB_TX_CONFIG, bwgid);
ixgbe_dcb_unpack_prio(dcb_config, DCB_TX_CONFIG, ptype);
+ ixgbe_dcb_unpack_map(dcb_config, DCB_TX_CONFIG, prio_tc);
switch (hw->mac.type) {
case ixgbe_mac_82598EB:
- ret = ixgbe_dcb_hw_config_82598(hw, dcb_config->rx_pba_cfg,
- pfc_en, refill, max, bwgid,
- ptype);
+ ret = ixgbe_dcb_hw_config_82598(hw, pfc_en, refill, max,
+ bwgid, ptype);
break;
case ixgbe_mac_82599EB:
case ixgbe_mac_X540:
- ret = ixgbe_dcb_hw_config_82599(hw, dcb_config->rx_pba_cfg,
- pfc_en, refill, max, bwgid,
- ptype, prio_tc);
+ ret = ixgbe_dcb_hw_config_82599(hw, pfc_en, refill, max,
+ bwgid, ptype, prio_tc);
break;
default:
break;
@@ -275,7 +286,7 @@ s32 ixgbe_dcb_hw_config(struct ixgbe_hw *hw,
}
/* Helper routines to abstract HW specifics from DCB netlink ops */
-s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en)
+s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc)
{
int ret = -EINVAL;
@@ -285,7 +296,7 @@ s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en)
break;
case ixgbe_mac_82599EB:
case ixgbe_mac_X540:
- ret = ixgbe_dcb_config_pfc_82599(hw, pfc_en);
+ ret = ixgbe_dcb_config_pfc_82599(hw, pfc_en, prio_tc);
break;
default:
break;
@@ -293,6 +304,39 @@ s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en)
return ret;
}
+s32 ixgbe_dcb_hw_ets(struct ixgbe_hw *hw, struct ieee_ets *ets, int max_frame)
+{
+ __u16 refill[IEEE_8021QAZ_MAX_TCS], max[IEEE_8021QAZ_MAX_TCS];
+ __u8 prio_type[IEEE_8021QAZ_MAX_TCS];
+ int i;
+
+ /* naively give each TC a bwg to map onto CEE hardware */
+ __u8 bwg_id[IEEE_8021QAZ_MAX_TCS] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+ /* Map TSA onto CEE prio type */
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
+ switch (ets->tc_tsa[i]) {
+ case IEEE_8021QAZ_TSA_STRICT:
+ prio_type[i] = 2;
+ break;
+ case IEEE_8021QAZ_TSA_ETS:
+ prio_type[i] = 0;
+ break;
+ default:
+ /* Hardware only supports priority strict or
+ * ETS transmission selection algorithms if
+ * we receive some other value from dcbnl
+ * throw an error
+ */
+ return -EINVAL;
+ }
+ }
+
+ ixgbe_ieee_credits(ets->tc_tx_bw, refill, max, max_frame);
+ return ixgbe_dcb_hw_ets_config(hw, refill, max,
+ bwg_id, prio_type, ets->prio_tc);
+}
+
s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw,
u16 *refill, u16 *max, u8 *bwg_id,
u8 *prio_type, u8 *prio_tc)
diff --git a/drivers/net/ixgbe/ixgbe_dcb.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h
index 944838fc7b59..e162775064da 100644
--- a/drivers/net/ixgbe/ixgbe_dcb.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h
@@ -29,13 +29,13 @@
#ifndef _DCB_CONFIG_H_
#define _DCB_CONFIG_H_
+#include <linux/dcbnl.h>
#include "ixgbe_type.h"
/* DCB data structures */
#define IXGBE_MAX_PACKET_BUFFERS 8
#define MAX_USER_PRIORITY 8
-#define MAX_TRAFFIC_CLASS 8
#define MAX_BW_GROUP 8
#define BW_PERCENT 100
@@ -123,11 +123,6 @@ struct tc_configuration {
u8 tc; /* Traffic class (TC) */
};
-enum dcb_rx_pba_cfg {
- pba_equal, /* PBA[0-7] each use 64KB FIFO */
- pba_80_48 /* PBA[0-3] each use 80KB, PBA[4-7] each use 48KB */
-};
-
struct dcb_num_tcs {
u8 pg_tcs;
u8 pfc_tcs;
@@ -140,8 +135,6 @@ struct ixgbe_dcb_config {
u8 bw_percentage[2][MAX_BW_GROUP]; /* One each for Tx/Rx */
bool pfc_mode_enable;
- enum dcb_rx_pba_cfg rx_pba_cfg;
-
u32 dcb_cfg_version; /* Not used...OS-specific? */
u32 link_speed; /* For bandwidth allocation validation purpose */
};
@@ -152,16 +145,17 @@ void ixgbe_dcb_unpack_refill(struct ixgbe_dcb_config *, int, u16 *);
void ixgbe_dcb_unpack_max(struct ixgbe_dcb_config *, u16 *);
void ixgbe_dcb_unpack_bwgid(struct ixgbe_dcb_config *, int, u8 *);
void ixgbe_dcb_unpack_prio(struct ixgbe_dcb_config *, int, u8 *);
+void ixgbe_dcb_unpack_map(struct ixgbe_dcb_config *, int, u8 *);
/* DCB credits calculation */
-s32 ixgbe_ieee_credits(__u8 *bw, __u16 *refill, __u16 *max, int max_frame);
s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *,
struct ixgbe_dcb_config *, int, u8);
/* DCB hw initialization */
+s32 ixgbe_dcb_hw_ets(struct ixgbe_hw *hw, struct ieee_ets *ets, int max);
s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, u16 *refill, u16 *max,
u8 *bwg_id, u8 *prio_type, u8 *tc_prio);
-s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en);
+s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en, u8 *tc_prio);
s32 ixgbe_dcb_hw_config(struct ixgbe_hw *, struct ixgbe_dcb_config *);
/* DCB definitions for credit calculation */
diff --git a/drivers/net/ixgbe/ixgbe_dcb_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c
index 771d01a60d06..fcd0e479721f 100644
--- a/drivers/net/ixgbe/ixgbe_dcb_82598.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c
@@ -32,45 +32,6 @@
#include "ixgbe_dcb_82598.h"
/**
- * ixgbe_dcb_config_packet_buffers_82598 - Configure packet buffers
- * @hw: pointer to hardware structure
- * @dcb_config: pointer to ixgbe_dcb_config structure
- *
- * Configure packet buffers for DCB mode.
- */
-static s32 ixgbe_dcb_config_packet_buffers_82598(struct ixgbe_hw *hw, u8 rx_pba)
-{
- s32 ret_val = 0;
- u32 value = IXGBE_RXPBSIZE_64KB;
- u8 i = 0;
-
- /* Setup Rx packet buffer sizes */
- switch (rx_pba) {
- case pba_80_48:
- /* Setup the first four at 80KB */
- value = IXGBE_RXPBSIZE_80KB;
- for (; i < 4; i++)
- IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), value);
- /* Setup the last four at 48KB...don't re-init i */
- value = IXGBE_RXPBSIZE_48KB;
- /* Fall Through */
- case pba_equal:
- default:
- for (; i < IXGBE_MAX_PACKET_BUFFERS; i++)
- IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), value);
-
- /* Setup Tx packet buffer sizes */
- for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) {
- IXGBE_WRITE_REG(hw, IXGBE_TXPBSIZE(i),
- IXGBE_TXPBSIZE_40KB);
- }
- break;
- }
-
- return ret_val;
-}
-
-/**
* ixgbe_dcb_config_rx_arbiter_82598 - Config Rx data arbiter
* @hw: pointer to hardware structure
* @dcb_config: pointer to ixgbe_dcb_config structure
@@ -230,7 +191,7 @@ s32 ixgbe_dcb_config_tx_data_arbiter_82598(struct ixgbe_hw *hw,
*/
s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw, u8 pfc_en)
{
- u32 reg, rx_pba_size;
+ u32 reg;
u8 i;
if (pfc_en) {
@@ -261,9 +222,8 @@ s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw, u8 pfc_en)
*/
for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
int enabled = pfc_en & (1 << i);
- rx_pba_size = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i));
- rx_pba_size >>= IXGBE_RXPBSIZE_SHIFT;
- reg = (rx_pba_size - hw->fc.low_water) << 10;
+
+ reg = hw->fc.low_water << 10;
if (enabled == pfc_enabled_tx ||
enabled == pfc_enabled_full)
@@ -271,7 +231,7 @@ s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw, u8 pfc_en)
IXGBE_WRITE_REG(hw, IXGBE_FCRTL(i), reg);
- reg = (rx_pba_size - hw->fc.high_water) << 10;
+ reg = hw->fc.high_water[i] << 10;
if (enabled == pfc_enabled_tx ||
enabled == pfc_enabled_full)
reg |= IXGBE_FCRTH_FCEN;
@@ -321,11 +281,9 @@ static s32 ixgbe_dcb_config_tc_stats_82598(struct ixgbe_hw *hw)
*
* Configure dcb settings and enable dcb mode.
*/
-s32 ixgbe_dcb_hw_config_82598(struct ixgbe_hw *hw,
- u8 rx_pba, u8 pfc_en, u16 *refill,
+s32 ixgbe_dcb_hw_config_82598(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill,
u16 *max, u8 *bwg_id, u8 *prio_type)
{
- ixgbe_dcb_config_packet_buffers_82598(hw, rx_pba);
ixgbe_dcb_config_rx_arbiter_82598(hw, refill, max, prio_type);
ixgbe_dcb_config_tx_desc_arbiter_82598(hw, refill, max,
bwg_id, prio_type);
diff --git a/drivers/net/ixgbe/ixgbe_dcb_82598.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h
index 1e9750c2b46b..2f318935561a 100644
--- a/drivers/net/ixgbe/ixgbe_dcb_82598.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h
@@ -91,8 +91,7 @@ s32 ixgbe_dcb_config_tx_data_arbiter_82598(struct ixgbe_hw *hw,
u8 *bwg_id,
u8 *prio_type);
-s32 ixgbe_dcb_hw_config_82598(struct ixgbe_hw *hw,
- u8 rx_pba, u8 pfc_en, u16 *refill,
+s32 ixgbe_dcb_hw_config_82598(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill,
u16 *max, u8 *bwg_id, u8 *prio_type);
#endif /* _DCB_82598_CONFIG_H */
diff --git a/drivers/net/ixgbe/ixgbe_dcb_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
index d50cf78c234d..32cd97bc794d 100644
--- a/drivers/net/ixgbe/ixgbe_dcb_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
@@ -31,63 +31,6 @@
#include "ixgbe_dcb_82599.h"
/**
- * ixgbe_dcb_config_packet_buffers_82599 - Configure DCB packet buffers
- * @hw: pointer to hardware structure
- * @rx_pba: method to distribute packet buffer
- *
- * Configure packet buffers for DCB mode.
- */
-static s32 ixgbe_dcb_config_packet_buffers_82599(struct ixgbe_hw *hw, u8 rx_pba)
-{
- int num_tcs = IXGBE_MAX_PACKET_BUFFERS;
- u32 rx_pb_size = hw->mac.rx_pb_size << IXGBE_RXPBSIZE_SHIFT;
- u32 rxpktsize;
- u32 txpktsize;
- u32 txpbthresh;
- u8 i = 0;
-
- /*
- * This really means configure the first half of the TCs
- * (Traffic Classes) to use 5/8 of the Rx packet buffer
- * space. To determine the size of the buffer for each TC,
- * we are multiplying the average size by 5/4 and applying
- * it to half of the traffic classes.
- */
- if (rx_pba == pba_80_48) {
- rxpktsize = (rx_pb_size * 5) / (num_tcs * 4);
- rx_pb_size -= rxpktsize * (num_tcs / 2);
- for (; i < (num_tcs / 2); i++)
- IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), rxpktsize);
- }
-
- /* Divide the remaining Rx packet buffer evenly among the TCs */
- rxpktsize = rx_pb_size / (num_tcs - i);
- for (; i < num_tcs; i++)
- IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), rxpktsize);
-
- /*
- * Setup Tx packet buffer and threshold equally for all TCs
- * TXPBTHRESH register is set in K so divide by 1024 and subtract
- * 10 since the largest packet we support is just over 9K.
- */
- txpktsize = IXGBE_TXPBSIZE_MAX / num_tcs;
- txpbthresh = (txpktsize / 1024) - IXGBE_TXPKT_SIZE_MAX;
- for (i = 0; i < num_tcs; i++) {
- IXGBE_WRITE_REG(hw, IXGBE_TXPBSIZE(i), txpktsize);
- IXGBE_WRITE_REG(hw, IXGBE_TXPBTHRESH(i), txpbthresh);
- }
-
- /* Clear unused TCs, if any, to zero buffer size*/
- for (; i < MAX_TRAFFIC_CLASS; i++) {
- IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), 0);
- IXGBE_WRITE_REG(hw, IXGBE_TXPBSIZE(i), 0);
- IXGBE_WRITE_REG(hw, IXGBE_TXPBTHRESH(i), 0);
- }
-
- return 0;
-}
-
-/**
* ixgbe_dcb_config_rx_arbiter_82599 - Config Rx Data arbiter
* @hw: pointer to hardware structure
* @refill: refill credits index by traffic class
@@ -116,9 +59,9 @@ s32 ixgbe_dcb_config_rx_arbiter_82599(struct ixgbe_hw *hw,
reg = IXGBE_RTRPCS_RRM | IXGBE_RTRPCS_RAC | IXGBE_RTRPCS_ARBDIS;
IXGBE_WRITE_REG(hw, IXGBE_RTRPCS, reg);
- /* Map all traffic classes to their UP, 1 to 1 */
+ /* Map all traffic classes to their UP */
reg = 0;
- for (i = 0; i < MAX_TRAFFIC_CLASS; i++)
+ for (i = 0; i < MAX_USER_PRIORITY; i++)
reg |= (prio_tc[i] << (i * IXGBE_RTRUP2TC_UP_SHIFT));
IXGBE_WRITE_REG(hw, IXGBE_RTRUP2TC, reg);
@@ -226,9 +169,9 @@ s32 ixgbe_dcb_config_tx_data_arbiter_82599(struct ixgbe_hw *hw,
IXGBE_RTTPCS_ARBDIS;
IXGBE_WRITE_REG(hw, IXGBE_RTTPCS, reg);
- /* Map all traffic classes to their UP, 1 to 1 */
+ /* Map all traffic classes to their UP */
reg = 0;
- for (i = 0; i < MAX_TRAFFIC_CLASS; i++)
+ for (i = 0; i < MAX_USER_PRIORITY; i++)
reg |= (prio_tc[i] << (i * IXGBE_RTTUP2TC_UP_SHIFT));
IXGBE_WRITE_REG(hw, IXGBE_RTTUP2TC, reg);
@@ -262,26 +205,44 @@ s32 ixgbe_dcb_config_tx_data_arbiter_82599(struct ixgbe_hw *hw,
* ixgbe_dcb_config_pfc_82599 - Configure priority flow control
* @hw: pointer to hardware structure
* @pfc_en: enabled pfc bitmask
+ * @prio_tc: priority to tc assignments indexed by priority
*
* Configure Priority Flow Control (PFC) for each traffic class.
*/
-s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en)
+s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc)
{
- u32 i, reg, rx_pba_size;
+ u32 i, j, reg;
+ u8 max_tc = 0;
+
+ for (i = 0; i < MAX_USER_PRIORITY; i++)
+ if (prio_tc[i] > max_tc)
+ max_tc = prio_tc[i];
/* Configure PFC Tx thresholds per TC */
for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
- int enabled = pfc_en & (1 << i);
- rx_pba_size = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i));
- rx_pba_size >>= IXGBE_RXPBSIZE_SHIFT;
+ int enabled = 0;
+
+ if (i > max_tc) {
+ reg = 0;
+ IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), reg);
+ IXGBE_WRITE_REG(hw, IXGBE_FCRTH_82599(i), reg);
+ continue;
+ }
- reg = (rx_pba_size - hw->fc.low_water) << 10;
+ for (j = 0; j < MAX_USER_PRIORITY; j++) {
+ if ((prio_tc[j] == i) && (pfc_en & (1 << j))) {
+ enabled = 1;
+ break;
+ }
+ }
+
+ reg = hw->fc.low_water << 10;
if (enabled)
reg |= IXGBE_FCRTL_XONE;
IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), reg);
- reg = (rx_pba_size - hw->fc.high_water) << 10;
+ reg = hw->fc.high_water[i] << 10;
if (enabled)
reg |= IXGBE_FCRTH_FCEN;
IXGBE_WRITE_REG(hw, IXGBE_FCRTH_82599(i), reg);
@@ -309,12 +270,24 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en)
reg &= ~IXGBE_MFLCN_RFCE;
reg |= IXGBE_MFLCN_RPFCE | IXGBE_MFLCN_DPF;
- if (hw->mac.type == ixgbe_mac_X540)
+ if (hw->mac.type == ixgbe_mac_X540) {
+ reg &= ~IXGBE_MFLCN_RPFCE_MASK;
reg |= pfc_en << IXGBE_MFLCN_RPFCE_SHIFT;
+ }
IXGBE_WRITE_REG(hw, IXGBE_MFLCN, reg);
} else {
+ /* X540 devices have a RX bit that should be cleared
+ * if PFC is disabled on all TCs but PFC features is
+ * enabled.
+ */
+ if (hw->mac.type == ixgbe_mac_X540) {
+ reg = IXGBE_READ_REG(hw, IXGBE_MFLCN);
+ reg &= ~IXGBE_MFLCN_RPFCE_MASK;
+ IXGBE_WRITE_REG(hw, IXGBE_MFLCN, reg);
+ }
+
for (i = 0; i < MAX_TRAFFIC_CLASS; i++)
hw->mac.ops.fc_enable(hw, i);
}
@@ -376,65 +349,8 @@ static s32 ixgbe_dcb_config_tc_stats_82599(struct ixgbe_hw *hw)
}
/**
- * ixgbe_dcb_config_82599 - Configure general DCB parameters
- * @hw: pointer to hardware structure
- *
- * Configure general DCB parameters.
- */
-static s32 ixgbe_dcb_config_82599(struct ixgbe_hw *hw)
-{
- u32 reg;
- u32 q;
-
- /* Disable the Tx desc arbiter so that MTQC can be changed */
- reg = IXGBE_READ_REG(hw, IXGBE_RTTDCS);
- reg |= IXGBE_RTTDCS_ARBDIS;
- IXGBE_WRITE_REG(hw, IXGBE_RTTDCS, reg);
-
- /* Enable DCB for Rx with 8 TCs */
- reg = IXGBE_READ_REG(hw, IXGBE_MRQC);
- switch (reg & IXGBE_MRQC_MRQE_MASK) {
- case 0:
- case IXGBE_MRQC_RT4TCEN:
- /* RSS disabled cases */
- reg = (reg & ~IXGBE_MRQC_MRQE_MASK) | IXGBE_MRQC_RT8TCEN;
- break;
- case IXGBE_MRQC_RSSEN:
- case IXGBE_MRQC_RTRSS4TCEN:
- /* RSS enabled cases */
- reg = (reg & ~IXGBE_MRQC_MRQE_MASK) | IXGBE_MRQC_RTRSS8TCEN;
- break;
- default:
- /* Unsupported value, assume stale data, overwrite no RSS */
- reg = (reg & ~IXGBE_MRQC_MRQE_MASK) | IXGBE_MRQC_RT8TCEN;
- }
- IXGBE_WRITE_REG(hw, IXGBE_MRQC, reg);
-
- /* Enable DCB for Tx with 8 TCs */
- reg = IXGBE_MTQC_RT_ENA | IXGBE_MTQC_8TC_8TQ;
- IXGBE_WRITE_REG(hw, IXGBE_MTQC, reg);
-
- /* Disable drop for all queues */
- for (q = 0; q < 128; q++)
- IXGBE_WRITE_REG(hw, IXGBE_QDE, q << IXGBE_QDE_IDX_SHIFT);
-
- /* Enable the Tx desc arbiter */
- reg = IXGBE_READ_REG(hw, IXGBE_RTTDCS);
- reg &= ~IXGBE_RTTDCS_ARBDIS;
- IXGBE_WRITE_REG(hw, IXGBE_RTTDCS, reg);
-
- /* Enable Security TX Buffer IFG for DCB */
- reg = IXGBE_READ_REG(hw, IXGBE_SECTXMINIFG);
- reg |= IXGBE_SECTX_DCB;
- IXGBE_WRITE_REG(hw, IXGBE_SECTXMINIFG, reg);
-
- return 0;
-}
-
-/**
* ixgbe_dcb_hw_config_82599 - Configure and enable DCB
* @hw: pointer to hardware structure
- * @rx_pba: method to distribute packet buffer
* @refill: refill credits index by traffic class
* @max: max credits index by traffic class
* @bwg_id: bandwidth grouping indexed by traffic class
@@ -443,19 +359,16 @@ static s32 ixgbe_dcb_config_82599(struct ixgbe_hw *hw)
*
* Configure dcb settings and enable dcb mode.
*/
-s32 ixgbe_dcb_hw_config_82599(struct ixgbe_hw *hw,
- u8 rx_pba, u8 pfc_en, u16 *refill,
+s32 ixgbe_dcb_hw_config_82599(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill,
u16 *max, u8 *bwg_id, u8 *prio_type, u8 *prio_tc)
{
- ixgbe_dcb_config_packet_buffers_82599(hw, rx_pba);
- ixgbe_dcb_config_82599(hw);
ixgbe_dcb_config_rx_arbiter_82599(hw, refill, max, bwg_id,
prio_type, prio_tc);
ixgbe_dcb_config_tx_desc_arbiter_82599(hw, refill, max,
bwg_id, prio_type);
ixgbe_dcb_config_tx_data_arbiter_82599(hw, refill, max,
bwg_id, prio_type, prio_tc);
- ixgbe_dcb_config_pfc_82599(hw, pfc_en);
+ ixgbe_dcb_config_pfc_82599(hw, pfc_en, prio_tc);
ixgbe_dcb_config_tc_stats_82599(hw);
return 0;
diff --git a/drivers/net/ixgbe/ixgbe_dcb_82599.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
index 2de71a503153..a59d5dc59d04 100644
--- a/drivers/net/ixgbe/ixgbe_dcb_82599.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
@@ -86,17 +86,6 @@
#define IXGBE_RTTPCS_ARBD_SHIFT 22
#define IXGBE_RTTPCS_ARBD_DCB 0x4 /* Arbitration delay in DCB mode */
-#define IXGBE_TXPBSIZE_20KB 0x00005000 /* 20KB Packet Buffer */
-#define IXGBE_TXPBSIZE_40KB 0x0000A000 /* 40KB Packet Buffer */
-#define IXGBE_RXPBSIZE_48KB 0x0000C000 /* 48KB Packet Buffer */
-#define IXGBE_RXPBSIZE_64KB 0x00010000 /* 64KB Packet Buffer */
-#define IXGBE_RXPBSIZE_80KB 0x00014000 /* 80KB Packet Buffer */
-#define IXGBE_RXPBSIZE_128KB 0x00020000 /* 128KB Packet Buffer */
-#define IXGBE_TXPBSIZE_MAX 0x00028000 /* 160KB Packet Buffer*/
-
-#define IXGBE_TXPBTHRESH_DCB 0xA /* THRESH value for DCB mode */
-#define IXGBE_TXPKT_SIZE_MAX 0xA /* Max Tx Packet size */
-
/* SECTXMINIFG DCB */
#define IXGBE_SECTX_DCB 0x00001F00 /* DCB TX Buffer IFG */
@@ -104,7 +93,7 @@
/* DCB hardware-specific driver APIs */
/* DCB PFC functions */
-s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en);
+s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc);
/* DCB hw initialization */
s32 ixgbe_dcb_config_rx_arbiter_82599(struct ixgbe_hw *hw,
@@ -127,8 +116,7 @@ s32 ixgbe_dcb_config_tx_data_arbiter_82599(struct ixgbe_hw *hw,
u8 *prio_type,
u8 *prio_tc);
-s32 ixgbe_dcb_hw_config_82599(struct ixgbe_hw *hw,
- u8 rx_pba, u8 pfc_en, u16 *refill,
+s32 ixgbe_dcb_hw_config_82599(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill,
u16 *max, u8 *bwg_id, u8 *prio_type,
u8 *prio_tc);
diff --git a/drivers/net/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
index 5e7ed225851a..33b93ffb87cb 100644
--- a/drivers/net/ixgbe/ixgbe_dcb_nl.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
@@ -114,70 +114,19 @@ static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state)
u8 err = 0;
struct ixgbe_adapter *adapter = netdev_priv(netdev);
- if (state > 0) {
- /* Turn on DCB */
- if (adapter->flags & IXGBE_FLAG_DCB_ENABLED)
- goto out;
-
- if (!(adapter->flags & IXGBE_FLAG_MSIX_ENABLED)) {
- e_err(drv, "Enable failed, needs MSI-X\n");
- err = 1;
- goto out;
- }
-
- if (netif_running(netdev))
- netdev->netdev_ops->ndo_stop(netdev);
- ixgbe_clear_interrupt_scheme(adapter);
-
- switch (adapter->hw.mac.type) {
- case ixgbe_mac_82598EB:
- adapter->last_lfc_mode = adapter->hw.fc.current_mode;
- adapter->hw.fc.requested_mode = ixgbe_fc_none;
- break;
- case ixgbe_mac_82599EB:
- case ixgbe_mac_X540:
- adapter->flags &= ~IXGBE_FLAG_FDIR_HASH_CAPABLE;
- adapter->flags &= ~IXGBE_FLAG_FDIR_PERFECT_CAPABLE;
- break;
- default:
- break;
- }
+ /* Fail command if not in CEE mode */
+ if (!(adapter->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
+ return 1;
- adapter->flags |= IXGBE_FLAG_DCB_ENABLED;
- if (!netdev_get_num_tc(netdev))
- ixgbe_setup_tc(netdev, MAX_TRAFFIC_CLASS);
+ /* verify there is something to do, if not then exit */
+ if (!!state != !(adapter->flags & IXGBE_FLAG_DCB_ENABLED))
+ return err;
- ixgbe_init_interrupt_scheme(adapter);
- if (netif_running(netdev))
- netdev->netdev_ops->ndo_open(netdev);
- } else {
- /* Turn off DCB */
- if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
- if (netif_running(netdev))
- netdev->netdev_ops->ndo_stop(netdev);
- ixgbe_clear_interrupt_scheme(adapter);
+ if (state > 0)
+ err = ixgbe_setup_tc(netdev, adapter->dcb_cfg.num_tcs.pg_tcs);
+ else
+ err = ixgbe_setup_tc(netdev, 0);
- adapter->hw.fc.requested_mode = adapter->last_lfc_mode;
- adapter->temp_dcb_cfg.pfc_mode_enable = false;
- adapter->dcb_cfg.pfc_mode_enable = false;
- adapter->flags &= ~IXGBE_FLAG_DCB_ENABLED;
- switch (adapter->hw.mac.type) {
- case ixgbe_mac_82599EB:
- case ixgbe_mac_X540:
- adapter->flags |= IXGBE_FLAG_FDIR_HASH_CAPABLE;
- break;
- default:
- break;
- }
-
- ixgbe_setup_tc(netdev, 0);
-
- ixgbe_init_interrupt_scheme(adapter);
- if (netif_running(netdev))
- netdev->netdev_ops->ndo_open(netdev);
- }
- }
-out:
return err;
}
@@ -209,6 +158,10 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc,
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ /* Abort a bad configuration */
+ if (ffs(up_map) > adapter->dcb_cfg.num_tcs.pg_tcs)
+ return;
+
if (prio != DCB_ATTR_VALUE_UNDEFINED)
adapter->temp_dcb_cfg.tc_config[tc].path[0].prio_type = prio;
if (bwg_id != DCB_ATTR_VALUE_UNDEFINED)
@@ -229,6 +182,10 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc,
(adapter->temp_dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap !=
adapter->dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap))
adapter->dcb_set_bitmap |= BIT_PG_TX;
+
+ if (adapter->temp_dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap !=
+ adapter->dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap)
+ adapter->dcb_set_bitmap |= BIT_PFC;
}
static void ixgbe_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
@@ -249,6 +206,10 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc,
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ /* Abort bad configurations */
+ if (ffs(up_map) > adapter->dcb_cfg.num_tcs.pg_tcs)
+ return;
+
if (prio != DCB_ATTR_VALUE_UNDEFINED)
adapter->temp_dcb_cfg.tc_config[tc].path[1].prio_type = prio;
if (bwg_id != DCB_ATTR_VALUE_UNDEFINED)
@@ -269,6 +230,10 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc,
(adapter->temp_dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap !=
adapter->dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap))
adapter->dcb_set_bitmap |= BIT_PG_RX;
+
+ if (adapter->temp_dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap !=
+ adapter->dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap)
+ adapter->dcb_set_bitmap |= BIT_PFC;
}
static void ixgbe_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
@@ -347,26 +312,34 @@ static void ixgbe_dcbnl_get_pfc_cfg(struct net_device *netdev, int priority,
static u8 ixgbe_dcbnl_set_all(struct net_device *netdev)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int ret, i;
+#ifdef IXGBE_FCOE
struct dcb_app app = {
.selector = DCB_APP_IDTYPE_ETHTYPE,
.protocol = ETH_P_FCOE,
};
- u8 up = dcb_getapp(netdev, &app);
- int ret;
+ u8 up;
+
+ /* In IEEE mode, use the IEEE Ethertype selector value */
+ if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) {
+ app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE;
+ up = dcb_ieee_getapp_mask(netdev, &app);
+ } else {
+ up = dcb_getapp(netdev, &app);
+ }
+#endif
+
+ /* Fail command if not in CEE mode */
+ if (!(adapter->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
+ return 1;
ret = ixgbe_copy_dcb_cfg(&adapter->temp_dcb_cfg, &adapter->dcb_cfg,
MAX_TRAFFIC_CLASS);
if (ret)
return DCB_NO_HW_CHG;
- /* In IEEE mode app data must be parsed into DCBX format for
- * hardware routines.
- */
- if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)
- up = (1 << up);
-
#ifdef IXGBE_FCOE
- if (up && (up != (1 << adapter->fcoe.up)))
+ if (up && !(up & (1 << adapter->fcoe.up)))
adapter->dcb_set_bitmap |= BIT_APP_UPCHG;
/*
@@ -378,7 +351,7 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev)
while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state))
usleep_range(1000, 2000);
- ixgbe_fcoe_setapp(adapter, up);
+ adapter->fcoe.up = ffs(up) - 1;
if (netif_running(netdev))
netdev->netdev_ops->ndo_stop(netdev);
@@ -421,21 +394,14 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev)
}
#endif
- if (adapter->dcb_set_bitmap & BIT_PFC) {
- u8 pfc_en;
- ixgbe_dcb_unpack_pfc(&adapter->dcb_cfg, &pfc_en);
- ixgbe_dcb_hw_pfc_config(&adapter->hw, pfc_en);
- ret = DCB_HW_CHG;
- }
-
if (adapter->dcb_set_bitmap & (BIT_PG_TX|BIT_PG_RX)) {
u16 refill[MAX_TRAFFIC_CLASS], max[MAX_TRAFFIC_CLASS];
u8 bwg_id[MAX_TRAFFIC_CLASS], prio_type[MAX_TRAFFIC_CLASS];
/* Priority to TC mapping in CEE case default to 1:1 */
- u8 prio_tc[MAX_TRAFFIC_CLASS] = {0, 1, 2, 3, 4, 5, 6, 7};
+ u8 prio_tc[MAX_USER_PRIORITY];
int max_frame = adapter->netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
-#ifdef CONFIG_FCOE
+#ifdef IXGBE_FCOE
if (adapter->netdev->features & NETIF_F_FCOE_MTU)
max_frame = max(max_frame, IXGBE_FCOE_JUMBO_FRAME_SIZE);
#endif
@@ -452,9 +418,25 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev)
DCB_TX_CONFIG, bwg_id);
ixgbe_dcb_unpack_prio(&adapter->dcb_cfg,
DCB_TX_CONFIG, prio_type);
+ ixgbe_dcb_unpack_map(&adapter->dcb_cfg,
+ DCB_TX_CONFIG, prio_tc);
ixgbe_dcb_hw_ets_config(&adapter->hw, refill, max,
bwg_id, prio_type, prio_tc);
+
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
+ netdev_set_prio_tc_map(netdev, i, prio_tc[i]);
+ }
+
+ if (adapter->dcb_set_bitmap & BIT_PFC) {
+ u8 pfc_en;
+ u8 prio_tc[MAX_USER_PRIORITY];
+
+ ixgbe_dcb_unpack_map(&adapter->dcb_cfg,
+ DCB_TX_CONFIG, prio_tc);
+ ixgbe_dcb_unpack_pfc(&adapter->dcb_cfg, &pfc_en);
+ ixgbe_dcb_hw_pfc_config(&adapter->hw, pfc_en, prio_tc);
+ ret = DCB_HW_CHG;
}
if (adapter->dcb_cfg.pfc_mode_enable)
@@ -511,10 +493,10 @@ static u8 ixgbe_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num)
if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
switch (tcid) {
case DCB_NUMTCS_ATTR_PG:
- *num = MAX_TRAFFIC_CLASS;
+ *num = adapter->dcb_cfg.num_tcs.pg_tcs;
break;
case DCB_NUMTCS_ATTR_PFC:
- *num = MAX_TRAFFIC_CLASS;
+ *num = adapter->dcb_cfg.num_tcs.pfc_tcs;
break;
default:
rval = -EINVAL;
@@ -579,11 +561,12 @@ static int ixgbe_dcbnl_ieee_getets(struct net_device *dev,
struct ixgbe_adapter *adapter = netdev_priv(dev);
struct ieee_ets *my_ets = adapter->ixgbe_ieee_ets;
+ ets->ets_cap = adapter->dcb_cfg.num_tcs.pg_tcs;
+
/* No IEEE PFC settings available */
if (!my_ets)
- return -EINVAL;
+ return 0;
- ets->ets_cap = MAX_TRAFFIC_CLASS;
ets->cbs = my_ets->cbs;
memcpy(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw));
memcpy(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw));
@@ -596,13 +579,9 @@ static int ixgbe_dcbnl_ieee_setets(struct net_device *dev,
struct ieee_ets *ets)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
- __u16 refill[IEEE_8021QAZ_MAX_TCS], max[IEEE_8021QAZ_MAX_TCS];
- __u8 prio_type[IEEE_8021QAZ_MAX_TCS];
int max_frame = dev->mtu + ETH_HLEN + ETH_FCS_LEN;
- int i, err;
- __u64 *p = (__u64 *) ets->prio_tc;
- /* naively give each TC a bwg to map onto CEE hardware */
- __u8 bwg_id[IEEE_8021QAZ_MAX_TCS] = {0, 1, 2, 3, 4, 5, 6, 7};
+ int i;
+ __u8 max_tc = 0;
if (!(adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
return -EINVAL;
@@ -616,34 +595,24 @@ static int ixgbe_dcbnl_ieee_setets(struct net_device *dev,
memcpy(adapter->ixgbe_ieee_ets, ets, sizeof(*adapter->ixgbe_ieee_ets));
- /* Map TSA onto CEE prio type */
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
- switch (ets->tc_tsa[i]) {
- case IEEE_8021QAZ_TSA_STRICT:
- prio_type[i] = 2;
- break;
- case IEEE_8021QAZ_TSA_ETS:
- prio_type[i] = 0;
- break;
- default:
- /* Hardware only supports priority strict or
- * ETS transmission selection algorithms if
- * we receive some other value from dcbnl
- * throw an error
- */
- return -EINVAL;
- }
+ if (ets->prio_tc[i] > max_tc)
+ max_tc = ets->prio_tc[i];
}
- if (*p)
- ixgbe_dcbnl_set_state(dev, 1);
- else
- ixgbe_dcbnl_set_state(dev, 0);
+ if (max_tc)
+ max_tc++;
- ixgbe_ieee_credits(ets->tc_tx_bw, refill, max, max_frame);
- err = ixgbe_dcb_hw_ets_config(&adapter->hw, refill, max,
- bwg_id, prio_type, ets->prio_tc);
- return err;
+ if (max_tc > adapter->dcb_cfg.num_tcs.pg_tcs)
+ return -EINVAL;
+
+ if (max_tc != netdev_get_num_tc(dev))
+ ixgbe_setup_tc(dev, max_tc);
+
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
+ netdev_set_prio_tc_map(dev, i, ets->prio_tc[i]);
+
+ return ixgbe_dcb_hw_ets(&adapter->hw, ets, max_frame);
}
static int ixgbe_dcbnl_ieee_getpfc(struct net_device *dev,
@@ -653,11 +622,12 @@ static int ixgbe_dcbnl_ieee_getpfc(struct net_device *dev,
struct ieee_pfc *my_pfc = adapter->ixgbe_ieee_pfc;
int i;
+ pfc->pfc_cap = adapter->dcb_cfg.num_tcs.pfc_tcs;
+
/* No IEEE PFC settings available */
if (!my_pfc)
- return -EINVAL;
+ return 0;
- pfc->pfc_cap = MAX_TRAFFIC_CLASS;
pfc->pfc_en = my_pfc->pfc_en;
pfc->mbc = my_pfc->mbc;
pfc->delay = my_pfc->delay;
@@ -674,7 +644,7 @@ static int ixgbe_dcbnl_ieee_setpfc(struct net_device *dev,
struct ieee_pfc *pfc)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
- int err;
+ u8 *prio_tc;
if (!(adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
return -EINVAL;
@@ -686,29 +656,80 @@ static int ixgbe_dcbnl_ieee_setpfc(struct net_device *dev,
return -ENOMEM;
}
+ prio_tc = adapter->ixgbe_ieee_ets->prio_tc;
memcpy(adapter->ixgbe_ieee_pfc, pfc, sizeof(*adapter->ixgbe_ieee_pfc));
- err = ixgbe_dcb_hw_pfc_config(&adapter->hw, pfc->pfc_en);
- return err;
+ return ixgbe_dcb_hw_pfc_config(&adapter->hw, pfc->pfc_en, prio_tc);
}
+#ifdef IXGBE_FCOE
+static void ixgbe_dcbnl_devreset(struct net_device *dev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(dev);
+
+ if (netif_running(dev))
+ dev->netdev_ops->ndo_stop(dev);
+
+ ixgbe_clear_interrupt_scheme(adapter);
+ ixgbe_init_interrupt_scheme(adapter);
+
+ if (netif_running(dev))
+ dev->netdev_ops->ndo_open(dev);
+}
+#endif
+
static int ixgbe_dcbnl_ieee_setapp(struct net_device *dev,
struct dcb_app *app)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
+ int err = -EINVAL;
if (!(adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
- return -EINVAL;
+ return err;
- dcb_setapp(dev, app);
+ err = dcb_ieee_setapp(dev, app);
#ifdef IXGBE_FCOE
- if (app->selector == 1 && app->protocol == ETH_P_FCOE &&
- adapter->fcoe.tc == app->priority)
- ixgbe_dcbnl_set_all(dev);
+ if (!err && app->selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
+ app->protocol == ETH_P_FCOE) {
+ u8 app_mask = dcb_ieee_getapp_mask(dev, app);
+
+ if (app_mask & (1 << adapter->fcoe.up))
+ return err;
+
+ adapter->fcoe.up = app->priority;
+ ixgbe_dcbnl_devreset(dev);
+ }
#endif
return 0;
}
+static int ixgbe_dcbnl_ieee_delapp(struct net_device *dev,
+ struct dcb_app *app)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(dev);
+ int err;
+
+ if (!(adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
+ return -EINVAL;
+
+ err = dcb_ieee_delapp(dev, app);
+
+#ifdef IXGBE_FCOE
+ if (!err && app->selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
+ app->protocol == ETH_P_FCOE) {
+ u8 app_mask = dcb_ieee_getapp_mask(dev, app);
+
+ if (app_mask & (1 << adapter->fcoe.up))
+ return err;
+
+ adapter->fcoe.up = app_mask ?
+ ffs(app_mask) - 1 : IXGBE_FCOE_DEFTC;
+ ixgbe_dcbnl_devreset(dev);
+ }
+#endif
+ return err;
+}
+
static u8 ixgbe_dcbnl_getdcbx(struct net_device *dev)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
@@ -748,7 +769,7 @@ static u8 ixgbe_dcbnl_setdcbx(struct net_device *dev, u8 mode)
*/
ixgbe_dcbnl_ieee_setets(dev, &ets);
ixgbe_dcbnl_ieee_setpfc(dev, &pfc);
- ixgbe_dcbnl_set_state(dev, 0);
+ ixgbe_setup_tc(dev, 0);
}
return 0;
@@ -760,6 +781,7 @@ const struct dcbnl_rtnl_ops dcbnl_ops = {
.ieee_getpfc = ixgbe_dcbnl_ieee_getpfc,
.ieee_setpfc = ixgbe_dcbnl_ieee_setpfc,
.ieee_setapp = ixgbe_dcbnl_ieee_setapp,
+ .ieee_delapp = ixgbe_dcbnl_ieee_delapp,
.getstate = ixgbe_dcbnl_get_state,
.setstate = ixgbe_dcbnl_set_state,
.getpermhwaddr = ixgbe_dcbnl_get_perm_hw_addr,
diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index cb1555bc8548..70d58c3849b0 100644
--- a/drivers/net/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -27,6 +27,7 @@
/* ethtool support for ixgbe */
+#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -112,6 +113,8 @@ static struct ixgbe_stats ixgbe_gstrings_stats[] = {
{"rx_fcoe_dropped", IXGBE_STAT(stats.fcoerpdc)},
{"rx_fcoe_packets", IXGBE_STAT(stats.fcoeprc)},
{"rx_fcoe_dwords", IXGBE_STAT(stats.fcoedwrc)},
+ {"fcoe_noddp", IXGBE_STAT(stats.fcoe_noddp)},
+ {"fcoe_noddp_ext_buff", IXGBE_STAT(stats.fcoe_noddp_ext_buff)},
{"tx_fcoe_packets", IXGBE_STAT(stats.fcoeptc)},
{"tx_fcoe_dwords", IXGBE_STAT(stats.fcoedwtc)},
#endif /* IXGBE_FCOE */
@@ -323,12 +326,16 @@ static int ixgbe_set_settings(struct net_device *netdev,
if ((hw->phy.media_type == ixgbe_media_type_copper) ||
(hw->phy.multispeed_fiber)) {
- /* 10000/copper and 1000/copper must autoneg
- * this function does not support any duplex forcing, but can
- * limit the advertising of the adapter to only 10000 or 1000 */
+ /*
+ * this function does not support duplex forcing, but can
+ * limit the advertising of the adapter to the specified speed
+ */
if (ecmd->autoneg == AUTONEG_DISABLE)
return -EINVAL;
+ if (ecmd->advertising & ~ecmd->supported)
+ return -EINVAL;
+
old = hw->phy.autoneg_advertised;
advertised = 0;
if (ecmd->advertising & ADVERTISED_10000baseT_Full)
@@ -367,13 +374,7 @@ static void ixgbe_get_pauseparam(struct net_device *netdev,
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
- /*
- * Flow Control Autoneg isn't on if
- * - we didn't ask for it OR
- * - it failed, we know this by tx & rx being off
- */
- if (hw->fc.disable_fc_autoneg ||
- (hw->fc.current_mode == ixgbe_fc_none))
+ if (hw->fc.disable_fc_autoneg)
pause->autoneg = 0;
else
pause->autoneg = 1;
@@ -441,62 +442,6 @@ static int ixgbe_set_pauseparam(struct net_device *netdev,
return 0;
}
-static u32 ixgbe_get_rx_csum(struct net_device *netdev)
-{
- struct ixgbe_adapter *adapter = netdev_priv(netdev);
- return adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED;
-}
-
-static int ixgbe_set_rx_csum(struct net_device *netdev, u32 data)
-{
- struct ixgbe_adapter *adapter = netdev_priv(netdev);
- if (data)
- adapter->flags |= IXGBE_FLAG_RX_CSUM_ENABLED;
- else
- adapter->flags &= ~IXGBE_FLAG_RX_CSUM_ENABLED;
-
- return 0;
-}
-
-static u32 ixgbe_get_tx_csum(struct net_device *netdev)
-{
- return (netdev->features & NETIF_F_IP_CSUM) != 0;
-}
-
-static int ixgbe_set_tx_csum(struct net_device *netdev, u32 data)
-{
- struct ixgbe_adapter *adapter = netdev_priv(netdev);
- u32 feature_list;
-
- feature_list = (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
- switch (adapter->hw.mac.type) {
- case ixgbe_mac_82599EB:
- case ixgbe_mac_X540:
- feature_list |= NETIF_F_SCTP_CSUM;
- break;
- default:
- break;
- }
- if (data)
- netdev->features |= feature_list;
- else
- netdev->features &= ~feature_list;
-
- return 0;
-}
-
-static int ixgbe_set_tso(struct net_device *netdev, u32 data)
-{
- if (data) {
- netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
- } else {
- netdev->features &= ~NETIF_F_TSO;
- netdev->features &= ~NETIF_F_TSO6;
- }
- return 0;
-}
-
static u32 ixgbe_get_msglevel(struct net_device *netdev)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
@@ -511,7 +456,7 @@ static void ixgbe_set_msglevel(struct net_device *netdev, u32 data)
static int ixgbe_get_regs_len(struct net_device *netdev)
{
-#define IXGBE_REGS_LEN 1128
+#define IXGBE_REGS_LEN 1129
return IXGBE_REGS_LEN * sizeof(u32);
}
@@ -580,6 +525,7 @@ static void ixgbe_get_regs(struct net_device *netdev,
regs_buff[43 + i] = IXGBE_READ_REG(hw, IXGBE_FCRTH(i));
break;
case ixgbe_mac_82599EB:
+ case ixgbe_mac_X540:
regs_buff[35 + i] = IXGBE_READ_REG(hw, IXGBE_FCRTL_82599(i));
regs_buff[43 + i] = IXGBE_READ_REG(hw, IXGBE_FCRTH_82599(i));
break;
@@ -821,6 +767,9 @@ static void ixgbe_get_regs(struct net_device *netdev,
regs_buff[1125] = IXGBE_READ_REG(hw, IXGBE_PCIEECCCTL);
regs_buff[1126] = IXGBE_READ_REG(hw, IXGBE_PBTXECC);
regs_buff[1127] = IXGBE_READ_REG(hw, IXGBE_PBRXECC);
+
+ /* 82599 X540 specific registers */
+ regs_buff[1128] = IXGBE_READ_REG(hw, IXGBE_MFLCN);
}
static int ixgbe_get_eeprom_len(struct net_device *netdev)
@@ -865,26 +814,97 @@ static int ixgbe_get_eeprom(struct net_device *netdev,
return ret_val;
}
+static int ixgbe_set_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_hw *hw = &adapter->hw;
+ u16 *eeprom_buff;
+ void *ptr;
+ int max_len, first_word, last_word, ret_val = 0;
+ u16 i;
+
+ if (eeprom->len == 0)
+ return -EINVAL;
+
+ if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
+ return -EINVAL;
+
+ max_len = hw->eeprom.word_size * 2;
+
+ first_word = eeprom->offset >> 1;
+ last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+ eeprom_buff = kmalloc(max_len, GFP_KERNEL);
+ if (!eeprom_buff)
+ return -ENOMEM;
+
+ ptr = eeprom_buff;
+
+ if (eeprom->offset & 1) {
+ /*
+ * need read/modify/write of first changed EEPROM word
+ * only the second byte of the word is being modified
+ */
+ ret_val = hw->eeprom.ops.read(hw, first_word, &eeprom_buff[0]);
+ if (ret_val)
+ goto err;
+
+ ptr++;
+ }
+ if ((eeprom->offset + eeprom->len) & 1) {
+ /*
+ * need read/modify/write of last changed EEPROM word
+ * only the first byte of the word is being modified
+ */
+ ret_val = hw->eeprom.ops.read(hw, last_word,
+ &eeprom_buff[last_word - first_word]);
+ if (ret_val)
+ goto err;
+ }
+
+ /* Device's eeprom is always little-endian, word addressable */
+ for (i = 0; i < last_word - first_word + 1; i++)
+ le16_to_cpus(&eeprom_buff[i]);
+
+ memcpy(ptr, bytes, eeprom->len);
+
+ for (i = 0; i < last_word - first_word + 1; i++)
+ cpu_to_le16s(&eeprom_buff[i]);
+
+ ret_val = hw->eeprom.ops.write_buffer(hw, first_word,
+ last_word - first_word + 1,
+ eeprom_buff);
+
+ /* Update the checksum */
+ if (ret_val == 0)
+ hw->eeprom.ops.update_checksum(hw);
+
+err:
+ kfree(eeprom_buff);
+ return ret_val;
+}
+
static void ixgbe_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *drvinfo)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
char firmware_version[32];
+ u32 nvm_track_id;
strncpy(drvinfo->driver, ixgbe_driver_name,
sizeof(drvinfo->driver) - 1);
strncpy(drvinfo->version, ixgbe_driver_version,
sizeof(drvinfo->version) - 1);
- snprintf(firmware_version, sizeof(firmware_version), "%d.%d-%d",
- (adapter->eeprom_version & 0xF000) >> 12,
- (adapter->eeprom_version & 0x0FF0) >> 4,
- adapter->eeprom_version & 0x000F);
+ nvm_track_id = (adapter->eeprom_verh << 16) |
+ adapter->eeprom_verl;
+ snprintf(firmware_version, sizeof(firmware_version), "0x%08x",
+ nvm_track_id);
strncpy(drvinfo->fw_version, firmware_version,
- sizeof(drvinfo->fw_version));
+ sizeof(drvinfo->fw_version) - 1);
strncpy(drvinfo->bus_info, pci_name(adapter->pdev),
- sizeof(drvinfo->bus_info));
+ sizeof(drvinfo->bus_info) - 1);
drvinfo->n_stats = IXGBE_STATS_LEN;
drvinfo->testinfo_len = IXGBE_TEST_LEN;
drvinfo->regdump_len = ixgbe_get_regs_len(netdev);
@@ -899,12 +919,8 @@ static void ixgbe_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = IXGBE_MAX_RXD;
ring->tx_max_pending = IXGBE_MAX_TXD;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = rx_ring->count;
ring->tx_pending = tx_ring->count;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
static int ixgbe_set_ringparam(struct net_device *netdev,
@@ -1433,6 +1449,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
/* Disable all the interrupts */
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, 0xFFFFFFFF);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
usleep_range(10000, 20000);
/* Test each interrupt */
@@ -1453,6 +1470,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
~mask & 0x00007FFF);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
~mask & 0x00007FFF);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
usleep_range(10000, 20000);
if (adapter->test_icr & mask) {
@@ -1470,6 +1488,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
adapter->test_icr = 0;
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, mask);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, mask);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
usleep_range(10000, 20000);
if (!(adapter->test_icr &mask)) {
@@ -1490,6 +1509,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
~mask & 0x00007FFF);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
~mask & 0x00007FFF);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
usleep_range(10000, 20000);
if (adapter->test_icr) {
@@ -1501,6 +1521,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
/* Disable all the interrupts */
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, 0xFFFFFFFF);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
usleep_range(10000, 20000);
/* Unhook test interrupt handler */
@@ -1585,7 +1606,7 @@ static int ixgbe_setup_desc_rings(struct ixgbe_adapter *adapter)
rx_ring->dev = &adapter->pdev->dev;
rx_ring->netdev = adapter->netdev;
rx_ring->reg_idx = adapter->rx_ring[0]->reg_idx;
- rx_ring->rx_buf_len = IXGBE_RXBUFFER_2048;
+ rx_ring->rx_buf_len = IXGBE_RXBUFFER_2K;
rx_ring->numa_node = adapter->node;
err = ixgbe_setup_rx_resources(rx_ring);
@@ -1616,26 +1637,26 @@ static int ixgbe_setup_loopback_test(struct ixgbe_adapter *adapter)
/* X540 needs to set the MACC.FLU bit to force link up */
if (adapter->hw.mac.type == ixgbe_mac_X540) {
- reg_data = IXGBE_READ_REG(&adapter->hw, IXGBE_MACC);
+ reg_data = IXGBE_READ_REG(hw, IXGBE_MACC);
reg_data |= IXGBE_MACC_FLU;
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_MACC, reg_data);
+ IXGBE_WRITE_REG(hw, IXGBE_MACC, reg_data);
}
/* right now we only support MAC loopback in the driver */
- reg_data = IXGBE_READ_REG(&adapter->hw, IXGBE_HLREG0);
+ reg_data = IXGBE_READ_REG(hw, IXGBE_HLREG0);
/* Setup MAC loopback */
reg_data |= IXGBE_HLREG0_LPBK;
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_HLREG0, reg_data);
+ IXGBE_WRITE_REG(hw, IXGBE_HLREG0, reg_data);
- reg_data = IXGBE_READ_REG(&adapter->hw, IXGBE_FCTRL);
+ reg_data = IXGBE_READ_REG(hw, IXGBE_FCTRL);
reg_data |= IXGBE_FCTRL_BAM | IXGBE_FCTRL_SBP | IXGBE_FCTRL_MPE;
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCTRL, reg_data);
+ IXGBE_WRITE_REG(hw, IXGBE_FCTRL, reg_data);
- reg_data = IXGBE_READ_REG(&adapter->hw, IXGBE_AUTOC);
+ reg_data = IXGBE_READ_REG(hw, IXGBE_AUTOC);
reg_data &= ~IXGBE_AUTOC_LMS_MASK;
reg_data |= IXGBE_AUTOC_LMS_10G_LINK_NO_AN | IXGBE_AUTOC_FLU;
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_AUTOC, reg_data);
- IXGBE_WRITE_FLUSH(&adapter->hw);
+ IXGBE_WRITE_REG(hw, IXGBE_AUTOC, reg_data);
+ IXGBE_WRITE_FLUSH(hw);
usleep_range(10000, 20000);
/* Disable Atlas Tx lanes; re-enabled in reset path */
@@ -1933,6 +1954,7 @@ static int ixgbe_wol_exclusion(struct ixgbe_adapter *adapter,
{
struct ixgbe_hw *hw = &adapter->hw;
int retval = 1;
+ u16 wol_cap = adapter->eeprom_cap & IXGBE_DEVICE_CAPS_WOL_MASK;
/* WOL not supported except for the following */
switch(hw->device_id) {
@@ -1956,6 +1978,18 @@ static int ixgbe_wol_exclusion(struct ixgbe_adapter *adapter,
case IXGBE_DEV_ID_82599_KX4:
retval = 0;
break;
+ case IXGBE_DEV_ID_X540T:
+ /* check eeprom to see if enabled wol */
+ if ((wol_cap == IXGBE_DEVICE_CAPS_WOL_PORT0_1) ||
+ ((wol_cap == IXGBE_DEVICE_CAPS_WOL_PORT0) &&
+ (hw->bus.func == 0))) {
+ retval = 0;
+ break;
+ }
+
+ /* All others not supported */
+ wol->supported = 0;
+ break;
default:
wol->supported = 0;
}
@@ -2055,42 +2089,23 @@ static int ixgbe_get_coalesce(struct net_device *netdev,
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
- ec->tx_max_coalesced_frames_irq = adapter->tx_ring[0]->work_limit;
+ ec->tx_max_coalesced_frames_irq = adapter->tx_work_limit;
/* only valid if in constant ITR mode */
- switch (adapter->rx_itr_setting) {
- case 0:
- /* throttling disabled */
- ec->rx_coalesce_usecs = 0;
- break;
- case 1:
- /* dynamic ITR mode */
- ec->rx_coalesce_usecs = 1;
- break;
- default:
- /* fixed interrupt rate mode */
- ec->rx_coalesce_usecs = 1000000/adapter->rx_eitr_param;
- break;
- }
+ if (adapter->rx_itr_setting <= 1)
+ ec->rx_coalesce_usecs = adapter->rx_itr_setting;
+ else
+ ec->rx_coalesce_usecs = adapter->rx_itr_setting >> 2;
/* if in mixed tx/rx queues per vector mode, report only rx settings */
- if (adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count)
+ if (adapter->q_vector[0]->tx.count && adapter->q_vector[0]->rx.count)
return 0;
/* only valid if in constant ITR mode */
- switch (adapter->tx_itr_setting) {
- case 0:
- /* throttling disabled */
- ec->tx_coalesce_usecs = 0;
- break;
- case 1:
- /* dynamic ITR mode */
- ec->tx_coalesce_usecs = 1;
- break;
- default:
- ec->tx_coalesce_usecs = 1000000/adapter->tx_eitr_param;
- break;
- }
+ if (adapter->tx_itr_setting <= 1)
+ ec->tx_coalesce_usecs = adapter->tx_itr_setting;
+ else
+ ec->tx_coalesce_usecs = adapter->tx_itr_setting >> 2;
return 0;
}
@@ -2109,10 +2124,9 @@ static bool ixgbe_update_rsc(struct ixgbe_adapter *adapter,
/* if interrupt rate is too high then disable RSC */
if (ec->rx_coalesce_usecs != 1 &&
- ec->rx_coalesce_usecs <= 1000000/IXGBE_MAX_RSC_INT_RATE) {
+ ec->rx_coalesce_usecs <= (IXGBE_MIN_RSC_ITR >> 2)) {
if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) {
- e_info(probe, "rx-usecs set too low, "
- "disabling RSC\n");
+ e_info(probe, "rx-usecs set too low, disabling RSC\n");
adapter->flags2 &= ~IXGBE_FLAG2_RSC_ENABLED;
return true;
}
@@ -2120,8 +2134,7 @@ static bool ixgbe_update_rsc(struct ixgbe_adapter *adapter,
/* check the feature flag value and enable RSC if necessary */
if ((netdev->features & NETIF_F_LRO) &&
!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)) {
- e_info(probe, "rx-usecs set to %d, "
- "re-enabling RSC\n",
+ e_info(probe, "rx-usecs set to %d, re-enabling RSC\n",
ec->rx_coalesce_usecs);
adapter->flags2 |= IXGBE_FLAG2_RSC_ENABLED;
return true;
@@ -2136,95 +2149,59 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_q_vector *q_vector;
int i;
+ int num_vectors;
+ u16 tx_itr_param, rx_itr_param;
bool need_reset = false;
/* don't accept tx specific changes if we've got mixed RxTx vectors */
- if (adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count
- && ec->tx_coalesce_usecs)
+ if (adapter->q_vector[0]->tx.count && adapter->q_vector[0]->rx.count
+ && ec->tx_coalesce_usecs)
return -EINVAL;
if (ec->tx_max_coalesced_frames_irq)
- adapter->tx_ring[0]->work_limit = ec->tx_max_coalesced_frames_irq;
-
- if (ec->rx_coalesce_usecs > 1) {
- /* check the limits */
- if ((1000000/ec->rx_coalesce_usecs > IXGBE_MAX_INT_RATE) ||
- (1000000/ec->rx_coalesce_usecs < IXGBE_MIN_INT_RATE))
- return -EINVAL;
+ adapter->tx_work_limit = ec->tx_max_coalesced_frames_irq;
- /* check the old value and enable RSC if necessary */
- need_reset = ixgbe_update_rsc(adapter, ec);
-
- /* store the value in ints/second */
- adapter->rx_eitr_param = 1000000/ec->rx_coalesce_usecs;
+ if ((ec->rx_coalesce_usecs > (IXGBE_MAX_EITR >> 2)) ||
+ (ec->tx_coalesce_usecs > (IXGBE_MAX_EITR >> 2)))
+ return -EINVAL;
- /* static value of interrupt rate */
- adapter->rx_itr_setting = adapter->rx_eitr_param;
- /* clear the lower bit as its used for dynamic state */
- adapter->rx_itr_setting &= ~1;
- } else if (ec->rx_coalesce_usecs == 1) {
- /* check the old value and enable RSC if necessary */
- need_reset = ixgbe_update_rsc(adapter, ec);
+ /* check the old value and enable RSC if necessary */
+ need_reset = ixgbe_update_rsc(adapter, ec);
- /* 1 means dynamic mode */
- adapter->rx_eitr_param = 20000;
- adapter->rx_itr_setting = 1;
- } else {
- /* check the old value and enable RSC if necessary */
- need_reset = ixgbe_update_rsc(adapter, ec);
- /*
- * any other value means disable eitr, which is best
- * served by setting the interrupt rate very high
- */
- adapter->rx_eitr_param = IXGBE_MAX_INT_RATE;
- adapter->rx_itr_setting = 0;
- }
-
- if (ec->tx_coalesce_usecs > 1) {
- /*
- * don't have to worry about max_int as above because
- * tx vectors don't do hardware RSC (an rx function)
- */
- /* check the limits */
- if ((1000000/ec->tx_coalesce_usecs > IXGBE_MAX_INT_RATE) ||
- (1000000/ec->tx_coalesce_usecs < IXGBE_MIN_INT_RATE))
- return -EINVAL;
+ if (ec->rx_coalesce_usecs > 1)
+ adapter->rx_itr_setting = ec->rx_coalesce_usecs << 2;
+ else
+ adapter->rx_itr_setting = ec->rx_coalesce_usecs;
- /* store the value in ints/second */
- adapter->tx_eitr_param = 1000000/ec->tx_coalesce_usecs;
+ if (adapter->rx_itr_setting == 1)
+ rx_itr_param = IXGBE_20K_ITR;
+ else
+ rx_itr_param = adapter->rx_itr_setting;
- /* static value of interrupt rate */
- adapter->tx_itr_setting = adapter->tx_eitr_param;
+ if (ec->tx_coalesce_usecs > 1)
+ adapter->tx_itr_setting = ec->tx_coalesce_usecs << 2;
+ else
+ adapter->tx_itr_setting = ec->tx_coalesce_usecs;
- /* clear the lower bit as its used for dynamic state */
- adapter->tx_itr_setting &= ~1;
- } else if (ec->tx_coalesce_usecs == 1) {
- /* 1 means dynamic mode */
- adapter->tx_eitr_param = 10000;
- adapter->tx_itr_setting = 1;
- } else {
- adapter->tx_eitr_param = IXGBE_MAX_INT_RATE;
- adapter->tx_itr_setting = 0;
- }
+ if (adapter->tx_itr_setting == 1)
+ tx_itr_param = IXGBE_10K_ITR;
+ else
+ tx_itr_param = adapter->tx_itr_setting;
- /* MSI/MSIx Interrupt Mode */
- if (adapter->flags &
- (IXGBE_FLAG_MSIX_ENABLED | IXGBE_FLAG_MSI_ENABLED)) {
- int num_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
- for (i = 0; i < num_vectors; i++) {
- q_vector = adapter->q_vector[i];
- if (q_vector->txr_count && !q_vector->rxr_count)
- /* tx only */
- q_vector->eitr = adapter->tx_eitr_param;
- else
- /* rx only or mixed */
- q_vector->eitr = adapter->rx_eitr_param;
- ixgbe_write_eitr(q_vector);
- }
- /* Legacy Interrupt Mode */
- } else {
- q_vector = adapter->q_vector[0];
- q_vector->eitr = adapter->rx_eitr_param;
+ if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED)
+ num_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ else
+ num_vectors = 1;
+
+ for (i = 0; i < num_vectors; i++) {
+ q_vector = adapter->q_vector[i];
+ q_vector->tx.work_limit = adapter->tx_work_limit;
+ if (q_vector->tx.count && !q_vector->rx.count)
+ /* tx only */
+ q_vector->itr = tx_itr_param;
+ else
+ /* rx only or mixed */
+ q_vector->itr = rx_itr_param;
ixgbe_write_eitr(q_vector);
}
@@ -2233,241 +2210,377 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
* correctly w.r.t stopping tx, and changing TXDCTL.WTHRESH settings
* also locks in RSC enable/disable which requires reset
*/
- if (need_reset) {
- if (netif_running(netdev))
- ixgbe_reinit_locked(adapter);
- else
- ixgbe_reset(adapter);
- }
+ if (need_reset)
+ ixgbe_do_reset(netdev);
return 0;
}
-static int ixgbe_set_flags(struct net_device *netdev, u32 data)
+static int ixgbe_get_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
+ struct ethtool_rxnfc *cmd)
{
- struct ixgbe_adapter *adapter = netdev_priv(netdev);
- bool need_reset = false;
- int rc;
+ union ixgbe_atr_input *mask = &adapter->fdir_mask;
+ struct ethtool_rx_flow_spec *fsp =
+ (struct ethtool_rx_flow_spec *)&cmd->fs;
+ struct hlist_node *node, *node2;
+ struct ixgbe_fdir_filter *rule = NULL;
+
+ /* report total rule count */
+ cmd->data = (1024 << adapter->fdir_pballoc) - 2;
+
+ hlist_for_each_entry_safe(rule, node, node2,
+ &adapter->fdir_filter_list, fdir_node) {
+ if (fsp->location <= rule->sw_idx)
+ break;
+ }
-#ifdef CONFIG_IXGBE_DCB
- if ((adapter->flags & IXGBE_FLAG_DCB_ENABLED) &&
- !(data & ETH_FLAG_RXVLAN))
+ if (!rule || fsp->location != rule->sw_idx)
return -EINVAL;
-#endif
- need_reset = (data & ETH_FLAG_RXVLAN) !=
- (netdev->features & NETIF_F_HW_VLAN_RX);
+ /* fill out the flow spec entry */
- if ((data & ETH_FLAG_RXHASH) &&
- !(adapter->flags & IXGBE_FLAG_RSS_ENABLED))
- return -EOPNOTSUPP;
-
- rc = ethtool_op_set_flags(netdev, data, ETH_FLAG_LRO | ETH_FLAG_NTUPLE |
- ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN |
- ETH_FLAG_RXHASH);
- if (rc)
- return rc;
-
- /* if state changes we need to update adapter->flags and reset */
- if ((adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE) &&
- (!!(data & ETH_FLAG_LRO) !=
- !!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED))) {
- if ((data & ETH_FLAG_LRO) &&
- (!adapter->rx_itr_setting ||
- (adapter->rx_itr_setting > IXGBE_MAX_RSC_INT_RATE))) {
- e_info(probe, "rx-usecs set too low, "
- "not enabling RSC.\n");
- } else {
- adapter->flags2 ^= IXGBE_FLAG2_RSC_ENABLED;
- switch (adapter->hw.mac.type) {
- case ixgbe_mac_82599EB:
- need_reset = true;
- break;
- case ixgbe_mac_X540: {
- int i;
- for (i = 0; i < adapter->num_rx_queues; i++) {
- struct ixgbe_ring *ring =
- adapter->rx_ring[i];
- if (adapter->flags2 &
- IXGBE_FLAG2_RSC_ENABLED) {
- ixgbe_configure_rscctl(adapter,
- ring);
- } else {
- ixgbe_clear_rscctl(adapter,
- ring);
- }
- }
- }
- break;
- default:
- break;
- }
- }
+ /* set flow type field */
+ switch (rule->filter.formatted.flow_type) {
+ case IXGBE_ATR_FLOW_TYPE_TCPV4:
+ fsp->flow_type = TCP_V4_FLOW;
+ break;
+ case IXGBE_ATR_FLOW_TYPE_UDPV4:
+ fsp->flow_type = UDP_V4_FLOW;
+ break;
+ case IXGBE_ATR_FLOW_TYPE_SCTPV4:
+ fsp->flow_type = SCTP_V4_FLOW;
+ break;
+ case IXGBE_ATR_FLOW_TYPE_IPV4:
+ fsp->flow_type = IP_USER_FLOW;
+ fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
+ fsp->h_u.usr_ip4_spec.proto = 0;
+ fsp->m_u.usr_ip4_spec.proto = 0;
+ break;
+ default:
+ return -EINVAL;
}
- /*
- * Check if Flow Director n-tuple support was enabled or disabled. If
- * the state changed, we need to reset.
- */
- if ((adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE) &&
- (!(data & ETH_FLAG_NTUPLE))) {
- /* turn off Flow Director perfect, set hash and reset */
- adapter->flags &= ~IXGBE_FLAG_FDIR_PERFECT_CAPABLE;
- adapter->flags |= IXGBE_FLAG_FDIR_HASH_CAPABLE;
- need_reset = true;
- } else if ((!(adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE)) &&
- (data & ETH_FLAG_NTUPLE)) {
- /* turn off Flow Director hash, enable perfect and reset */
- adapter->flags &= ~IXGBE_FLAG_FDIR_HASH_CAPABLE;
- adapter->flags |= IXGBE_FLAG_FDIR_PERFECT_CAPABLE;
- need_reset = true;
- } else {
- /* no state change */
- }
+ fsp->h_u.tcp_ip4_spec.psrc = rule->filter.formatted.src_port;
+ fsp->m_u.tcp_ip4_spec.psrc = mask->formatted.src_port;
+ fsp->h_u.tcp_ip4_spec.pdst = rule->filter.formatted.dst_port;
+ fsp->m_u.tcp_ip4_spec.pdst = mask->formatted.dst_port;
+ fsp->h_u.tcp_ip4_spec.ip4src = rule->filter.formatted.src_ip[0];
+ fsp->m_u.tcp_ip4_spec.ip4src = mask->formatted.src_ip[0];
+ fsp->h_u.tcp_ip4_spec.ip4dst = rule->filter.formatted.dst_ip[0];
+ fsp->m_u.tcp_ip4_spec.ip4dst = mask->formatted.dst_ip[0];
+ fsp->h_ext.vlan_tci = rule->filter.formatted.vlan_id;
+ fsp->m_ext.vlan_tci = mask->formatted.vlan_id;
+ fsp->h_ext.vlan_etype = rule->filter.formatted.flex_bytes;
+ fsp->m_ext.vlan_etype = mask->formatted.flex_bytes;
+ fsp->h_ext.data[1] = htonl(rule->filter.formatted.vm_pool);
+ fsp->m_ext.data[1] = htonl(mask->formatted.vm_pool);
+ fsp->flow_type |= FLOW_EXT;
+
+ /* record action */
+ if (rule->action == IXGBE_FDIR_DROP_QUEUE)
+ fsp->ring_cookie = RX_CLS_FLOW_DISC;
+ else
+ fsp->ring_cookie = rule->action;
- if (need_reset) {
- if (netif_running(netdev))
- ixgbe_reinit_locked(adapter);
- else
- ixgbe_reset(adapter);
+ return 0;
+}
+
+static int ixgbe_get_ethtool_fdir_all(struct ixgbe_adapter *adapter,
+ struct ethtool_rxnfc *cmd,
+ u32 *rule_locs)
+{
+ struct hlist_node *node, *node2;
+ struct ixgbe_fdir_filter *rule;
+ int cnt = 0;
+
+ /* report total rule count */
+ cmd->data = (1024 << adapter->fdir_pballoc) - 2;
+
+ hlist_for_each_entry_safe(rule, node, node2,
+ &adapter->fdir_filter_list, fdir_node) {
+ if (cnt == cmd->rule_cnt)
+ return -EMSGSIZE;
+ rule_locs[cnt] = rule->sw_idx;
+ cnt++;
}
+ cmd->rule_cnt = cnt;
+
return 0;
}
-static int ixgbe_set_rx_ntuple(struct net_device *dev,
- struct ethtool_rx_ntuple *cmd)
+static int ixgbe_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
+ u32 *rule_locs)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
- struct ethtool_rx_ntuple_flow_spec *fs = &cmd->fs;
- union ixgbe_atr_input input_struct;
- struct ixgbe_atr_input_masks input_masks;
- int target_queue;
- int err;
+ int ret = -EOPNOTSUPP;
- if (adapter->hw.mac.type == ixgbe_mac_82598EB)
- return -EOPNOTSUPP;
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXRINGS:
+ cmd->data = adapter->num_rx_queues;
+ ret = 0;
+ break;
+ case ETHTOOL_GRXCLSRLCNT:
+ cmd->rule_cnt = adapter->fdir_filter_count;
+ ret = 0;
+ break;
+ case ETHTOOL_GRXCLSRULE:
+ ret = ixgbe_get_ethtool_fdir_entry(adapter, cmd);
+ break;
+ case ETHTOOL_GRXCLSRLALL:
+ ret = ixgbe_get_ethtool_fdir_all(adapter, cmd, rule_locs);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int ixgbe_update_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
+ struct ixgbe_fdir_filter *input,
+ u16 sw_idx)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct hlist_node *node, *node2, *parent;
+ struct ixgbe_fdir_filter *rule;
+ int err = -EINVAL;
+
+ parent = NULL;
+ rule = NULL;
+
+ hlist_for_each_entry_safe(rule, node, node2,
+ &adapter->fdir_filter_list, fdir_node) {
+ /* hash found, or no matching entry */
+ if (rule->sw_idx >= sw_idx)
+ break;
+ parent = node;
+ }
+
+ /* if there is an old rule occupying our place remove it */
+ if (rule && (rule->sw_idx == sw_idx)) {
+ if (!input || (rule->filter.formatted.bkt_hash !=
+ input->filter.formatted.bkt_hash)) {
+ err = ixgbe_fdir_erase_perfect_filter_82599(hw,
+ &rule->filter,
+ sw_idx);
+ }
+
+ hlist_del(&rule->fdir_node);
+ kfree(rule);
+ adapter->fdir_filter_count--;
+ }
/*
- * Don't allow programming if the action is a queue greater than
- * the number of online Tx queues.
+ * If no input this was a delete, err should be 0 if a rule was
+ * successfully found and removed from the list else -EINVAL
*/
- if ((fs->action >= adapter->num_tx_queues) ||
- (fs->action < ETHTOOL_RXNTUPLE_ACTION_DROP))
- return -EINVAL;
+ if (!input)
+ return err;
- memset(&input_struct, 0, sizeof(union ixgbe_atr_input));
- memset(&input_masks, 0, sizeof(struct ixgbe_atr_input_masks));
+ /* initialize node and set software index */
+ INIT_HLIST_NODE(&input->fdir_node);
- /* record flow type */
- switch (fs->flow_type) {
- case IPV4_FLOW:
- input_struct.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_IPV4;
- break;
+ /* add filter to the list */
+ if (parent)
+ hlist_add_after(parent, &input->fdir_node);
+ else
+ hlist_add_head(&input->fdir_node,
+ &adapter->fdir_filter_list);
+
+ /* update counts */
+ adapter->fdir_filter_count++;
+
+ return 0;
+}
+
+static int ixgbe_flowspec_to_flow_type(struct ethtool_rx_flow_spec *fsp,
+ u8 *flow_type)
+{
+ switch (fsp->flow_type & ~FLOW_EXT) {
case TCP_V4_FLOW:
- input_struct.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4;
+ *flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4;
break;
case UDP_V4_FLOW:
- input_struct.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_UDPV4;
+ *flow_type = IXGBE_ATR_FLOW_TYPE_UDPV4;
break;
case SCTP_V4_FLOW:
- input_struct.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_SCTPV4;
+ *flow_type = IXGBE_ATR_FLOW_TYPE_SCTPV4;
break;
- default:
- return -1;
- }
-
- /* copy vlan tag minus the CFI bit */
- if ((fs->vlan_tag & 0xEFFF) || (~fs->vlan_tag_mask & 0xEFFF)) {
- input_struct.formatted.vlan_id = htons(fs->vlan_tag & 0xEFFF);
- if (!fs->vlan_tag_mask) {
- input_masks.vlan_id_mask = htons(0xEFFF);
- } else {
- switch (~fs->vlan_tag_mask & 0xEFFF) {
- /* all of these are valid vlan-mask values */
- case 0xEFFF:
- case 0xE000:
- case 0x0FFF:
- case 0x0000:
- input_masks.vlan_id_mask =
- htons(~fs->vlan_tag_mask);
+ case IP_USER_FLOW:
+ switch (fsp->h_u.usr_ip4_spec.proto) {
+ case IPPROTO_TCP:
+ *flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4;
+ break;
+ case IPPROTO_UDP:
+ *flow_type = IXGBE_ATR_FLOW_TYPE_UDPV4;
+ break;
+ case IPPROTO_SCTP:
+ *flow_type = IXGBE_ATR_FLOW_TYPE_SCTPV4;
+ break;
+ case 0:
+ if (!fsp->m_u.usr_ip4_spec.proto) {
+ *flow_type = IXGBE_ATR_FLOW_TYPE_IPV4;
break;
- /* exit with error if vlan-mask is invalid */
- default:
- e_err(drv, "Partial VLAN ID or "
- "priority mask in vlan-mask is not "
- "supported by hardware\n");
- return -1;
}
+ default:
+ return 0;
}
+ break;
+ default:
+ return 0;
}
- /* make sure we only use the first 2 bytes of user data */
- if ((fs->data & 0xFFFF) || (~fs->data_mask & 0xFFFF)) {
- input_struct.formatted.flex_bytes = htons(fs->data & 0xFFFF);
- if (!(fs->data_mask & 0xFFFF)) {
- input_masks.flex_mask = 0xFFFF;
- } else if (~fs->data_mask & 0xFFFF) {
- e_err(drv, "Partial user-def-mask is not "
- "supported by hardware\n");
- return -1;
- }
- }
+ return 1;
+}
+
+static int ixgbe_add_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
+ struct ethtool_rxnfc *cmd)
+{
+ struct ethtool_rx_flow_spec *fsp =
+ (struct ethtool_rx_flow_spec *)&cmd->fs;
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct ixgbe_fdir_filter *input;
+ union ixgbe_atr_input mask;
+ int err;
+
+ if (!(adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE))
+ return -EOPNOTSUPP;
/*
- * Copy input into formatted structures
- *
- * These assignments are based on the following logic
- * If neither input or mask are set assume value is masked out.
- * If input is set, but mask is not mask should default to accept all.
- * If input is not set, but mask is set then mask likely results in 0.
- * If input is set and mask is set then assign both.
+ * Don't allow programming if the action is a queue greater than
+ * the number of online Rx queues.
*/
- if (fs->h_u.tcp_ip4_spec.ip4src || ~fs->m_u.tcp_ip4_spec.ip4src) {
- input_struct.formatted.src_ip[0] = fs->h_u.tcp_ip4_spec.ip4src;
- if (!fs->m_u.tcp_ip4_spec.ip4src)
- input_masks.src_ip_mask[0] = 0xFFFFFFFF;
- else
- input_masks.src_ip_mask[0] =
- ~fs->m_u.tcp_ip4_spec.ip4src;
- }
- if (fs->h_u.tcp_ip4_spec.ip4dst || ~fs->m_u.tcp_ip4_spec.ip4dst) {
- input_struct.formatted.dst_ip[0] = fs->h_u.tcp_ip4_spec.ip4dst;
- if (!fs->m_u.tcp_ip4_spec.ip4dst)
- input_masks.dst_ip_mask[0] = 0xFFFFFFFF;
- else
- input_masks.dst_ip_mask[0] =
- ~fs->m_u.tcp_ip4_spec.ip4dst;
+ if ((fsp->ring_cookie != RX_CLS_FLOW_DISC) &&
+ (fsp->ring_cookie >= adapter->num_rx_queues))
+ return -EINVAL;
+
+ /* Don't allow indexes to exist outside of available space */
+ if (fsp->location >= ((1024 << adapter->fdir_pballoc) - 2)) {
+ e_err(drv, "Location out of range\n");
+ return -EINVAL;
}
- if (fs->h_u.tcp_ip4_spec.psrc || ~fs->m_u.tcp_ip4_spec.psrc) {
- input_struct.formatted.src_port = fs->h_u.tcp_ip4_spec.psrc;
- if (!fs->m_u.tcp_ip4_spec.psrc)
- input_masks.src_port_mask = 0xFFFF;
- else
- input_masks.src_port_mask = ~fs->m_u.tcp_ip4_spec.psrc;
+
+ input = kzalloc(sizeof(*input), GFP_ATOMIC);
+ if (!input)
+ return -ENOMEM;
+
+ memset(&mask, 0, sizeof(union ixgbe_atr_input));
+
+ /* set SW index */
+ input->sw_idx = fsp->location;
+
+ /* record flow type */
+ if (!ixgbe_flowspec_to_flow_type(fsp,
+ &input->filter.formatted.flow_type)) {
+ e_err(drv, "Unrecognized flow type\n");
+ goto err_out;
}
- if (fs->h_u.tcp_ip4_spec.pdst || ~fs->m_u.tcp_ip4_spec.pdst) {
- input_struct.formatted.dst_port = fs->h_u.tcp_ip4_spec.pdst;
- if (!fs->m_u.tcp_ip4_spec.pdst)
- input_masks.dst_port_mask = 0xFFFF;
- else
- input_masks.dst_port_mask = ~fs->m_u.tcp_ip4_spec.pdst;
+
+ mask.formatted.flow_type = IXGBE_ATR_L4TYPE_IPV6_MASK |
+ IXGBE_ATR_L4TYPE_MASK;
+
+ if (input->filter.formatted.flow_type == IXGBE_ATR_FLOW_TYPE_IPV4)
+ mask.formatted.flow_type &= IXGBE_ATR_L4TYPE_IPV6_MASK;
+
+ /* Copy input into formatted structures */
+ input->filter.formatted.src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
+ mask.formatted.src_ip[0] = fsp->m_u.tcp_ip4_spec.ip4src;
+ input->filter.formatted.dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
+ mask.formatted.dst_ip[0] = fsp->m_u.tcp_ip4_spec.ip4dst;
+ input->filter.formatted.src_port = fsp->h_u.tcp_ip4_spec.psrc;
+ mask.formatted.src_port = fsp->m_u.tcp_ip4_spec.psrc;
+ input->filter.formatted.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
+ mask.formatted.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
+
+ if (fsp->flow_type & FLOW_EXT) {
+ input->filter.formatted.vm_pool =
+ (unsigned char)ntohl(fsp->h_ext.data[1]);
+ mask.formatted.vm_pool =
+ (unsigned char)ntohl(fsp->m_ext.data[1]);
+ input->filter.formatted.vlan_id = fsp->h_ext.vlan_tci;
+ mask.formatted.vlan_id = fsp->m_ext.vlan_tci;
+ input->filter.formatted.flex_bytes =
+ fsp->h_ext.vlan_etype;
+ mask.formatted.flex_bytes = fsp->m_ext.vlan_etype;
}
/* determine if we need to drop or route the packet */
- if (fs->action == ETHTOOL_RXNTUPLE_ACTION_DROP)
- target_queue = MAX_RX_QUEUES - 1;
+ if (fsp->ring_cookie == RX_CLS_FLOW_DISC)
+ input->action = IXGBE_FDIR_DROP_QUEUE;
else
- target_queue = fs->action;
+ input->action = fsp->ring_cookie;
spin_lock(&adapter->fdir_perfect_lock);
- err = ixgbe_fdir_add_perfect_filter_82599(&adapter->hw,
- &input_struct,
- &input_masks, 0,
- target_queue);
+
+ if (hlist_empty(&adapter->fdir_filter_list)) {
+ /* save mask and program input mask into HW */
+ memcpy(&adapter->fdir_mask, &mask, sizeof(mask));
+ err = ixgbe_fdir_set_input_mask_82599(hw, &mask);
+ if (err) {
+ e_err(drv, "Error writing mask\n");
+ goto err_out_w_lock;
+ }
+ } else if (memcmp(&adapter->fdir_mask, &mask, sizeof(mask))) {
+ e_err(drv, "Only one mask supported per port\n");
+ goto err_out_w_lock;
+ }
+
+ /* apply mask and compute/store hash */
+ ixgbe_atr_compute_perfect_hash_82599(&input->filter, &mask);
+
+ /* program filters to filter memory */
+ err = ixgbe_fdir_write_perfect_filter_82599(hw,
+ &input->filter, input->sw_idx,
+ (input->action == IXGBE_FDIR_DROP_QUEUE) ?
+ IXGBE_FDIR_DROP_QUEUE :
+ adapter->rx_ring[input->action]->reg_idx);
+ if (err)
+ goto err_out_w_lock;
+
+ ixgbe_update_ethtool_fdir_entry(adapter, input, input->sw_idx);
+
+ spin_unlock(&adapter->fdir_perfect_lock);
+
+ return err;
+err_out_w_lock:
spin_unlock(&adapter->fdir_perfect_lock);
+err_out:
+ kfree(input);
+ return -EINVAL;
+}
+
+static int ixgbe_del_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
+ struct ethtool_rxnfc *cmd)
+{
+ struct ethtool_rx_flow_spec *fsp =
+ (struct ethtool_rx_flow_spec *)&cmd->fs;
+ int err;
+
+ spin_lock(&adapter->fdir_perfect_lock);
+ err = ixgbe_update_ethtool_fdir_entry(adapter, NULL, fsp->location);
+ spin_unlock(&adapter->fdir_perfect_lock);
+
+ return err;
+}
+
+static int ixgbe_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(dev);
+ int ret = -EOPNOTSUPP;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_SRXCLSRLINS:
+ ret = ixgbe_add_ethtool_fdir_entry(adapter, cmd);
+ break;
+ case ETHTOOL_SRXCLSRLDEL:
+ ret = ixgbe_del_ethtool_fdir_entry(adapter, cmd);
+ break;
+ default:
+ break;
+ }
- return err ? -1 : 0;
+ return ret;
}
static const struct ethtool_ops ixgbe_ethtool_ops = {
@@ -2482,20 +2595,13 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_eeprom_len = ixgbe_get_eeprom_len,
.get_eeprom = ixgbe_get_eeprom,
+ .set_eeprom = ixgbe_set_eeprom,
.get_ringparam = ixgbe_get_ringparam,
.set_ringparam = ixgbe_set_ringparam,
.get_pauseparam = ixgbe_get_pauseparam,
.set_pauseparam = ixgbe_set_pauseparam,
- .get_rx_csum = ixgbe_get_rx_csum,
- .set_rx_csum = ixgbe_set_rx_csum,
- .get_tx_csum = ixgbe_get_tx_csum,
- .set_tx_csum = ixgbe_set_tx_csum,
- .get_sg = ethtool_op_get_sg,
- .set_sg = ethtool_op_set_sg,
.get_msglevel = ixgbe_get_msglevel,
.set_msglevel = ixgbe_set_msglevel,
- .get_tso = ethtool_op_get_tso,
- .set_tso = ixgbe_set_tso,
.self_test = ixgbe_diag_test,
.get_strings = ixgbe_get_strings,
.set_phys_id = ixgbe_set_phys_id,
@@ -2503,9 +2609,8 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
.get_ethtool_stats = ixgbe_get_ethtool_stats,
.get_coalesce = ixgbe_get_coalesce,
.set_coalesce = ixgbe_set_coalesce,
- .get_flags = ethtool_op_get_flags,
- .set_flags = ixgbe_set_flags,
- .set_rx_ntuple = ixgbe_set_rx_ntuple,
+ .get_rxnfc = ixgbe_get_rxnfc,
+ .set_rxnfc = ixgbe_set_rxnfc,
};
void ixgbe_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
index 05920726e824..df3b1be69d83 100644
--- a/drivers/net/ixgbe/ixgbe_fcoe.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
@@ -26,9 +26,6 @@
*******************************************************************************/
#include "ixgbe.h"
-#ifdef CONFIG_IXGBE_DCB
-#include "ixgbe_dcb_82599.h"
-#endif /* CONFIG_IXGBE_DCB */
#include <linux/if_ether.h>
#include <linux/gfp.h>
#include <linux/if_vlan.h>
@@ -40,25 +37,6 @@
#include <scsi/libfcoe.h>
/**
- * ixgbe_rx_is_fcoe - check the rx desc for incoming pkt type
- * @rx_desc: advanced rx descriptor
- *
- * Returns : true if it is FCoE pkt
- */
-static inline bool ixgbe_rx_is_fcoe(union ixgbe_adv_rx_desc *rx_desc)
-{
- u16 p;
-
- p = le16_to_cpu(rx_desc->wb.lower.lo_dword.hs_rss.pkt_info);
- if (p & IXGBE_RXDADV_PKTTYPE_ETQF) {
- p &= IXGBE_RXDADV_PKTTYPE_ETQF_MASK;
- p >>= IXGBE_RXDADV_PKTTYPE_ETQF_SHIFT;
- return p == IXGBE_ETQF_FILTER_FCOE;
- }
- return false;
-}
-
-/**
* ixgbe_fcoe_clear_ddp - clear the given ddp context
* @ddp - ptr to the ixgbe_fcoe_ddp
*
@@ -128,14 +106,17 @@ int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid)
if (ddp->sgl)
pci_unmap_sg(adapter->pdev, ddp->sgl, ddp->sgc,
DMA_FROM_DEVICE);
- pci_pool_free(fcoe->pool, ddp->udl, ddp->udp);
+ if (ddp->pool) {
+ pci_pool_free(ddp->pool, ddp->udl, ddp->udp);
+ ddp->pool = NULL;
+ }
+
ixgbe_fcoe_clear_ddp(ddp);
out_ddp_put:
return len;
}
-
/**
* ixgbe_fcoe_ddp_setup - called to set up ddp context
* @netdev: the corresponding net_device
@@ -163,6 +144,8 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
unsigned int thislen = 0;
u32 fcbuff, fcdmarw, fcfltrw, fcrxctl;
dma_addr_t addr = 0;
+ struct pci_pool *pool;
+ unsigned int cpu;
if (!netdev || !sgl)
return 0;
@@ -199,12 +182,15 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
return 0;
}
- /* alloc the udl from our ddp pool */
- ddp->udl = pci_pool_alloc(fcoe->pool, GFP_ATOMIC, &ddp->udp);
+ /* alloc the udl from per cpu ddp pool */
+ cpu = get_cpu();
+ pool = *per_cpu_ptr(fcoe->pool, cpu);
+ ddp->udl = pci_pool_alloc(pool, GFP_ATOMIC, &ddp->udp);
if (!ddp->udl) {
e_err(drv, "failed allocated ddp context\n");
goto out_noddp_unmap;
}
+ ddp->pool = pool;
ddp->sgl = sgl;
ddp->sgc = sgc;
@@ -215,9 +201,7 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
while (len) {
/* max number of buffers allowed in one DDP context */
if (j >= IXGBE_BUFFCNT_MAX) {
- e_err(drv, "xid=%x:%d,%d,%d:addr=%llx "
- "not enough descriptors\n",
- xid, i, j, dmacount, (u64)addr);
+ *per_cpu_ptr(fcoe->pcpu_noddp, cpu) += 1;
goto out_noddp_free;
}
@@ -257,10 +241,7 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
*/
if (lastsize == bufflen) {
if (j >= IXGBE_BUFFCNT_MAX) {
- e_err(drv, "xid=%x:%d,%d,%d:addr=%llx "
- "not enough user buffers. We need an extra "
- "buffer because lastsize is bufflen.\n",
- xid, i, j, dmacount, (u64)addr);
+ *per_cpu_ptr(fcoe->pcpu_noddp_ext_buff, cpu) += 1;
goto out_noddp_free;
}
@@ -268,6 +249,7 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
j++;
lastsize = 1;
}
+ put_cpu();
fcbuff = (IXGBE_FCBUFF_4KB << IXGBE_FCBUFF_BUFFSIZE_SHIFT);
fcbuff |= ((j & 0xff) << IXGBE_FCBUFF_BUFFCNT_SHIFT);
@@ -311,11 +293,12 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
return 1;
out_noddp_free:
- pci_pool_free(fcoe->pool, ddp->udl, ddp->udp);
+ pci_pool_free(pool, ddp->udl, ddp->udp);
ixgbe_fcoe_clear_ddp(ddp);
out_noddp_unmap:
pci_unmap_sg(adapter->pdev, sgl, sgc, DMA_FROM_DEVICE);
+ put_cpu();
return 0;
}
@@ -374,23 +357,20 @@ int ixgbe_fcoe_ddp_target(struct net_device *netdev, u16 xid,
*/
int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
union ixgbe_adv_rx_desc *rx_desc,
- struct sk_buff *skb)
+ struct sk_buff *skb,
+ u32 staterr)
{
u16 xid;
u32 fctl;
- u32 sterr, fceofe, fcerr, fcstat;
+ u32 fceofe, fcerr, fcstat;
int rc = -EINVAL;
struct ixgbe_fcoe *fcoe;
struct ixgbe_fcoe_ddp *ddp;
struct fc_frame_header *fh;
struct fcoe_crc_eof *crc;
- if (!ixgbe_rx_is_fcoe(rx_desc))
- goto ddp_out;
-
- sterr = le32_to_cpu(rx_desc->wb.upper.status_error);
- fcerr = (sterr & IXGBE_RXDADV_ERR_FCERR);
- fceofe = (sterr & IXGBE_RXDADV_ERR_FCEOFE);
+ fcerr = (staterr & IXGBE_RXDADV_ERR_FCERR);
+ fceofe = (staterr & IXGBE_RXDADV_ERR_FCEOFE);
if (fcerr == IXGBE_FCERR_BADCRC)
skb_checksum_none_assert(skb);
else
@@ -419,7 +399,7 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
if (fcerr | fceofe)
goto ddp_out;
- fcstat = (sterr & IXGBE_RXDADV_STAT_FCSTAT);
+ fcstat = (staterr & IXGBE_RXDADV_STAT_FCSTAT);
if (fcstat) {
/* update length of DDPed data */
ddp->len = le32_to_cpu(rx_desc->wb.lower.hi_dword.rss);
@@ -455,7 +435,6 @@ ddp_out:
/**
* ixgbe_fso - ixgbe FCoE Sequence Offload (FSO)
- * @adapter: ixgbe adapter
* @tx_ring: tx desc ring
* @skb: associated skb
* @tx_flags: tx flags
@@ -465,24 +444,18 @@ ddp_out:
*
* Returns : 0 indicates no FSO, > 0 for FSO, < 0 for error
*/
-int ixgbe_fso(struct ixgbe_adapter *adapter,
- struct ixgbe_ring *tx_ring, struct sk_buff *skb,
+int ixgbe_fso(struct ixgbe_ring *tx_ring, struct sk_buff *skb,
u32 tx_flags, u8 *hdr_len)
{
- u8 sof, eof;
+ struct fc_frame_header *fh;
u32 vlan_macip_lens;
- u32 fcoe_sof_eof;
- u32 type_tucmd;
+ u32 fcoe_sof_eof = 0;
u32 mss_l4len_idx;
- int mss = 0;
- unsigned int i;
- struct ixgbe_tx_buffer *tx_buffer_info;
- struct ixgbe_adv_tx_context_desc *context_desc;
- struct fc_frame_header *fh;
+ u8 sof, eof;
if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_type != SKB_GSO_FCOE)) {
- e_err(drv, "Wrong gso type %d:expecting SKB_GSO_FCOE\n",
- skb_shinfo(skb)->gso_type);
+ dev_err(tx_ring->dev, "Wrong gso type %d:expecting SKB_GSO_FCOE\n",
+ skb_shinfo(skb)->gso_type);
return -EINVAL;
}
@@ -492,23 +465,22 @@ int ixgbe_fso(struct ixgbe_adapter *adapter,
sizeof(struct fcoe_hdr));
/* sets up SOF and ORIS */
- fcoe_sof_eof = 0;
sof = ((struct fcoe_hdr *)skb_network_header(skb))->fcoe_sof;
switch (sof) {
case FC_SOF_I2:
- fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_ORIS;
+ fcoe_sof_eof = IXGBE_ADVTXD_FCOEF_ORIS;
break;
case FC_SOF_I3:
- fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_SOF;
- fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_ORIS;
+ fcoe_sof_eof = IXGBE_ADVTXD_FCOEF_SOF |
+ IXGBE_ADVTXD_FCOEF_ORIS;
break;
case FC_SOF_N2:
break;
case FC_SOF_N3:
- fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_SOF;
+ fcoe_sof_eof = IXGBE_ADVTXD_FCOEF_SOF;
break;
default:
- e_warn(drv, "unknown sof = 0x%x\n", sof);
+ dev_warn(tx_ring->dev, "unknown sof = 0x%x\n", sof);
return -EINVAL;
}
@@ -521,12 +493,11 @@ int ixgbe_fso(struct ixgbe_adapter *adapter,
break;
case FC_EOF_T:
/* lso needs ORIE */
- if (skb_is_gso(skb)) {
- fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_EOF_N;
- fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_ORIE;
- } else {
+ if (skb_is_gso(skb))
+ fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_EOF_N |
+ IXGBE_ADVTXD_FCOEF_ORIE;
+ else
fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_EOF_T;
- }
break;
case FC_EOF_NI:
fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_EOF_NI;
@@ -535,7 +506,7 @@ int ixgbe_fso(struct ixgbe_adapter *adapter,
fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_EOF_A;
break;
default:
- e_warn(drv, "unknown eof = 0x%x\n", eof);
+ dev_warn(tx_ring->dev, "unknown eof = 0x%x\n", eof);
return -EINVAL;
}
@@ -544,47 +515,72 @@ int ixgbe_fso(struct ixgbe_adapter *adapter,
if (fh->fh_f_ctl[2] & FC_FC_REL_OFF)
fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_PARINC;
- /* hdr_len includes fc_hdr if FCoE lso is enabled */
+ /* include trailer in headlen as it is replicated per frame */
*hdr_len = sizeof(struct fcoe_crc_eof);
+
+ /* hdr_len includes fc_hdr if FCoE LSO is enabled */
if (skb_is_gso(skb))
*hdr_len += (skb_transport_offset(skb) +
sizeof(struct fc_frame_header));
- /* vlan_macip_lens: HEADLEN, MACLEN, VLAN tag */
- vlan_macip_lens = (skb_transport_offset(skb) +
- sizeof(struct fc_frame_header));
- vlan_macip_lens |= ((skb_transport_offset(skb) - 4)
- << IXGBE_ADVTXD_MACLEN_SHIFT);
- vlan_macip_lens |= (tx_flags & IXGBE_TX_FLAGS_VLAN_MASK);
-
- /* type_tycmd and mss: set TUCMD.FCoE to enable offload */
- type_tucmd = IXGBE_TXD_CMD_DEXT | IXGBE_ADVTXD_DTYP_CTXT |
- IXGBE_ADVTXT_TUCMD_FCOE;
- if (skb_is_gso(skb))
- mss = skb_shinfo(skb)->gso_size;
+
/* mss_l4len_id: use 1 for FSO as TSO, no need for L4LEN */
- mss_l4len_idx = (mss << IXGBE_ADVTXD_MSS_SHIFT) |
- (1 << IXGBE_ADVTXD_IDX_SHIFT);
+ mss_l4len_idx = skb_shinfo(skb)->gso_size << IXGBE_ADVTXD_MSS_SHIFT;
+ mss_l4len_idx |= 1 << IXGBE_ADVTXD_IDX_SHIFT;
+
+ /* vlan_macip_lens: HEADLEN, MACLEN, VLAN tag */
+ vlan_macip_lens = skb_transport_offset(skb) +
+ sizeof(struct fc_frame_header);
+ vlan_macip_lens |= (skb_transport_offset(skb) - 4)
+ << IXGBE_ADVTXD_MACLEN_SHIFT;
+ vlan_macip_lens |= tx_flags & IXGBE_TX_FLAGS_VLAN_MASK;
/* write context desc */
- i = tx_ring->next_to_use;
- context_desc = IXGBE_TX_CTXTDESC_ADV(tx_ring, i);
- context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens);
- context_desc->seqnum_seed = cpu_to_le32(fcoe_sof_eof);
- context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd);
- context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx);
-
- tx_buffer_info = &tx_ring->tx_buffer_info[i];
- tx_buffer_info->time_stamp = jiffies;
- tx_buffer_info->next_to_watch = i;
-
- i++;
- if (i == tx_ring->count)
- i = 0;
- tx_ring->next_to_use = i;
+ ixgbe_tx_ctxtdesc(tx_ring, vlan_macip_lens, fcoe_sof_eof,
+ IXGBE_ADVTXT_TUCMD_FCOE, mss_l4len_idx);
return skb_is_gso(skb);
}
+static void ixgbe_fcoe_ddp_pools_free(struct ixgbe_fcoe *fcoe)
+{
+ unsigned int cpu;
+ struct pci_pool **pool;
+
+ for_each_possible_cpu(cpu) {
+ pool = per_cpu_ptr(fcoe->pool, cpu);
+ if (*pool)
+ pci_pool_destroy(*pool);
+ }
+ free_percpu(fcoe->pool);
+ fcoe->pool = NULL;
+}
+
+static void ixgbe_fcoe_ddp_pools_alloc(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_fcoe *fcoe = &adapter->fcoe;
+ unsigned int cpu;
+ struct pci_pool **pool;
+ char pool_name[32];
+
+ fcoe->pool = alloc_percpu(struct pci_pool *);
+ if (!fcoe->pool)
+ return;
+
+ /* allocate pci pool for each cpu */
+ for_each_possible_cpu(cpu) {
+ snprintf(pool_name, 32, "ixgbe_fcoe_ddp_%d", cpu);
+ pool = per_cpu_ptr(fcoe->pool, cpu);
+ *pool = pci_pool_create(pool_name,
+ adapter->pdev, IXGBE_FCPTR_MAX,
+ IXGBE_FCPTR_ALIGN, PAGE_SIZE);
+ if (!*pool) {
+ e_err(drv, "failed to alloc DDP pool on cpu:%d\n", cpu);
+ ixgbe_fcoe_ddp_pools_free(fcoe);
+ return;
+ }
+ }
+}
+
/**
* ixgbe_configure_fcoe - configures registers for fcoe at start
* @adapter: ptr to ixgbe adapter
@@ -599,27 +595,22 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
struct ixgbe_hw *hw = &adapter->hw;
struct ixgbe_fcoe *fcoe = &adapter->fcoe;
struct ixgbe_ring_feature *f = &adapter->ring_feature[RING_F_FCOE];
-#ifdef CONFIG_IXGBE_DCB
- u8 tc;
- u32 up2tc;
-#endif
+ unsigned int cpu;
- /* create the pool for ddp if not created yet */
if (!fcoe->pool) {
- /* allocate ddp pool */
- fcoe->pool = pci_pool_create("ixgbe_fcoe_ddp",
- adapter->pdev, IXGBE_FCPTR_MAX,
- IXGBE_FCPTR_ALIGN, PAGE_SIZE);
- if (!fcoe->pool)
- e_err(drv, "failed to allocated FCoE DDP pool\n");
-
spin_lock_init(&fcoe->lock);
+ ixgbe_fcoe_ddp_pools_alloc(adapter);
+ if (!fcoe->pool) {
+ e_err(drv, "failed to alloc percpu fcoe DDP pools\n");
+ return;
+ }
+
/* Extra buffer to be shared by all DDPs for HW work around */
fcoe->extra_ddp_buffer = kmalloc(IXGBE_FCBUFF_MIN, GFP_ATOMIC);
if (fcoe->extra_ddp_buffer == NULL) {
e_err(drv, "failed to allocated extra DDP buffer\n");
- goto out_extra_ddp_buffer_alloc;
+ goto out_ddp_pools;
}
fcoe->extra_ddp_buffer_dma =
@@ -630,7 +621,25 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
if (dma_mapping_error(&adapter->pdev->dev,
fcoe->extra_ddp_buffer_dma)) {
e_err(drv, "failed to map extra DDP buffer\n");
- goto out_extra_ddp_buffer_dma;
+ goto out_extra_ddp_buffer;
+ }
+
+ /* Alloc per cpu mem to count the ddp alloc failure number */
+ fcoe->pcpu_noddp = alloc_percpu(u64);
+ if (!fcoe->pcpu_noddp) {
+ e_err(drv, "failed to alloc noddp counter\n");
+ goto out_pcpu_noddp_alloc_fail;
+ }
+
+ fcoe->pcpu_noddp_ext_buff = alloc_percpu(u64);
+ if (!fcoe->pcpu_noddp_ext_buff) {
+ e_err(drv, "failed to alloc noddp extra buff cnt\n");
+ goto out_pcpu_noddp_extra_buff_alloc_fail;
+ }
+
+ for_each_possible_cpu(cpu) {
+ *per_cpu_ptr(fcoe->pcpu_noddp, cpu) = 0;
+ *per_cpu_ptr(fcoe->pcpu_noddp_ext_buff, cpu) = 0;
}
}
@@ -666,29 +675,20 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
IXGBE_ETQS_QUEUE_EN |
(fcoe_q << IXGBE_ETQS_RX_QUEUE_SHIFT));
- IXGBE_WRITE_REG(hw, IXGBE_FCRXCTRL,
- IXGBE_FCRXCTRL_FCOELLI |
- IXGBE_FCRXCTRL_FCCRCBO |
+ IXGBE_WRITE_REG(hw, IXGBE_FCRXCTRL, IXGBE_FCRXCTRL_FCCRCBO |
(FC_FCOE_VER << IXGBE_FCRXCTRL_FCOEVER_SHIFT));
-#ifdef CONFIG_IXGBE_DCB
- up2tc = IXGBE_READ_REG(&adapter->hw, IXGBE_RTTUP2TC);
- for (i = 0; i < MAX_USER_PRIORITY; i++) {
- tc = (u8)(up2tc >> (i * IXGBE_RTTUP2TC_UP_SHIFT));
- tc &= (MAX_TRAFFIC_CLASS - 1);
- if (fcoe->tc == tc) {
- fcoe->up = i;
- break;
- }
- }
-#endif
-
return;
-
-out_extra_ddp_buffer_dma:
+out_pcpu_noddp_extra_buff_alloc_fail:
+ free_percpu(fcoe->pcpu_noddp);
+out_pcpu_noddp_alloc_fail:
+ dma_unmap_single(&adapter->pdev->dev,
+ fcoe->extra_ddp_buffer_dma,
+ IXGBE_FCBUFF_MIN,
+ DMA_FROM_DEVICE);
+out_extra_ddp_buffer:
kfree(fcoe->extra_ddp_buffer);
-out_extra_ddp_buffer_alloc:
- pci_pool_destroy(fcoe->pool);
- fcoe->pool = NULL;
+out_ddp_pools:
+ ixgbe_fcoe_ddp_pools_free(fcoe);
}
/**
@@ -704,18 +704,19 @@ void ixgbe_cleanup_fcoe(struct ixgbe_adapter *adapter)
int i;
struct ixgbe_fcoe *fcoe = &adapter->fcoe;
- /* release ddp resource */
- if (fcoe->pool) {
- for (i = 0; i < IXGBE_FCOE_DDP_MAX; i++)
- ixgbe_fcoe_ddp_put(adapter->netdev, i);
- dma_unmap_single(&adapter->pdev->dev,
- fcoe->extra_ddp_buffer_dma,
- IXGBE_FCBUFF_MIN,
- DMA_FROM_DEVICE);
- kfree(fcoe->extra_ddp_buffer);
- pci_pool_destroy(fcoe->pool);
- fcoe->pool = NULL;
- }
+ if (!fcoe->pool)
+ return;
+
+ for (i = 0; i < IXGBE_FCOE_DDP_MAX; i++)
+ ixgbe_fcoe_ddp_put(adapter->netdev, i);
+ dma_unmap_single(&adapter->pdev->dev,
+ fcoe->extra_ddp_buffer_dma,
+ IXGBE_FCBUFF_MIN,
+ DMA_FROM_DEVICE);
+ free_percpu(fcoe->pcpu_noddp);
+ free_percpu(fcoe->pcpu_noddp_ext_buff);
+ kfree(fcoe->extra_ddp_buffer);
+ ixgbe_fcoe_ddp_pools_free(fcoe);
}
/**
@@ -811,41 +812,6 @@ out_disable:
return rc;
}
-#ifdef CONFIG_IXGBE_DCB
-/**
- * ixgbe_fcoe_setapp - sets the user priority bitmap for FCoE
- * @adapter : ixgbe adapter
- * @up : 802.1p user priority bitmap
- *
- * Finds out the traffic class from the input user priority
- * bitmap for FCoE.
- *
- * Returns : 0 on success otherwise returns 1 on error
- */
-u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up)
-{
- int i;
- u32 up2tc;
-
- /* valid user priority bitmap must not be 0 */
- if (up) {
- /* from user priority to the corresponding traffic class */
- up2tc = IXGBE_READ_REG(&adapter->hw, IXGBE_RTTUP2TC);
- for (i = 0; i < MAX_USER_PRIORITY; i++) {
- if (up & (1 << i)) {
- up2tc >>= (i * IXGBE_RTTUP2TC_UP_SHIFT);
- up2tc &= (MAX_TRAFFIC_CLASS - 1);
- adapter->fcoe.tc = (u8)up2tc;
- adapter->fcoe.up = i;
- return 0;
- }
- }
- }
-
- return 1;
-}
-#endif /* CONFIG_IXGBE_DCB */
-
/**
* ixgbe_fcoe_get_wwn - get world wide name for the node or the port
* @netdev : ixgbe adapter
diff --git a/drivers/net/ixgbe/ixgbe_fcoe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
index 5a650a4ace66..261fd62dda18 100644
--- a/drivers/net/ixgbe/ixgbe_fcoe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
@@ -62,20 +62,22 @@ struct ixgbe_fcoe_ddp {
struct scatterlist *sgl;
dma_addr_t udp;
u64 *udl;
+ struct pci_pool *pool;
};
struct ixgbe_fcoe {
-#ifdef CONFIG_IXGBE_DCB
- u8 tc;
- u8 up;
-#endif
- unsigned long mode;
+ struct pci_pool **pool;
atomic_t refcnt;
spinlock_t lock;
- struct pci_pool *pool;
struct ixgbe_fcoe_ddp ddp[IXGBE_FCOE_DDP_MAX];
unsigned char *extra_ddp_buffer;
dma_addr_t extra_ddp_buffer_dma;
+ unsigned long mode;
+ u64 __percpu *pcpu_noddp;
+ u64 __percpu *pcpu_noddp_ext_buff;
+#ifdef CONFIG_IXGBE_DCB
+ u8 up;
+#endif
};
#endif /* _IXGBE_FCOE_H */
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 08e8e25c159d..8ef92d1a6aa1 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -32,14 +32,17 @@
#include <linux/vmalloc.h>
#include <linux/string.h>
#include <linux/in.h>
+#include <linux/interrupt.h>
#include <linux/ip.h>
#include <linux/tcp.h>
+#include <linux/sctp.h>
#include <linux/pkt_sched.h>
#include <linux/ipv6.h>
#include <linux/slab.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
#include <linux/ethtool.h>
+#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/prefetch.h>
#include <scsi/fc/fc_fcoe.h>
@@ -53,11 +56,10 @@ char ixgbe_driver_name[] = "ixgbe";
static const char ixgbe_driver_string[] =
"Intel(R) 10 Gigabit PCI Express Network Driver";
#define MAJ 3
-#define MIN 3
-#define BUILD 8
-#define KFIX 2
+#define MIN 6
+#define BUILD 7
#define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) "." \
- __stringify(BUILD) "-k" __stringify(KFIX)
+ __stringify(BUILD) "-k"
const char ixgbe_driver_version[] = DRV_VERSION;
static const char ixgbe_copyright[] =
"Copyright (c) 1999-2011 Intel Corporation.";
@@ -77,59 +79,33 @@ static const struct ixgbe_info *ixgbe_info_tbl[] = {
* Class, Class Mask, private data (not used) }
*/
static DEFINE_PCI_DEVICE_TABLE(ixgbe_pci_tbl) = {
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_DUAL_PORT),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_SINGLE_PORT),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT2),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_CX4),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_CX4_DUAL_PORT),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_DA_DUAL_PORT),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_XF_LR),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_SFP_LOM),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_BX),
- board_82598 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_XAUI_LOM),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KR),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_EM),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4_MEZZ),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_CX4),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_BACKPLANE_FCOE),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_FCOE),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_T3_LOM),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_COMBO_BACKPLANE),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540T),
- board_X540 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_SF2),
- board_82599 },
- {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_LS),
- board_82599 },
-
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_DUAL_PORT), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_SINGLE_PORT), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT2), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_CX4), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_CX4_DUAL_PORT), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_DA_DUAL_PORT), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_XF_LR), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_SFP_LOM), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_BX), board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_XAUI_LOM), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KR), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_EM), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4_MEZZ), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_CX4), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_BACKPLANE_FCOE), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_FCOE), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_T3_LOM), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_COMBO_BACKPLANE), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540T), board_X540 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_SF2), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_LS), board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599EN_SFP), board_82599 },
/* required last entry */
{0, }
};
@@ -159,41 +135,6 @@ MODULE_VERSION(DRV_VERSION);
#define DEFAULT_DEBUG_LEVEL_SHIFT 3
-static inline void ixgbe_disable_sriov(struct ixgbe_adapter *adapter)
-{
- struct ixgbe_hw *hw = &adapter->hw;
- u32 gcr;
- u32 gpie;
- u32 vmdctl;
-
-#ifdef CONFIG_PCI_IOV
- /* disable iov and allow time for transactions to clear */
- pci_disable_sriov(adapter->pdev);
-#endif
-
- /* turn off device IOV mode */
- gcr = IXGBE_READ_REG(hw, IXGBE_GCR_EXT);
- gcr &= ~(IXGBE_GCR_EXT_SRIOV);
- IXGBE_WRITE_REG(hw, IXGBE_GCR_EXT, gcr);
- gpie = IXGBE_READ_REG(hw, IXGBE_GPIE);
- gpie &= ~IXGBE_GPIE_VTMODE_MASK;
- IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie);
-
- /* set default pool back to 0 */
- vmdctl = IXGBE_READ_REG(hw, IXGBE_VT_CTL);
- vmdctl &= ~IXGBE_VT_CTL_POOL_MASK;
- IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vmdctl);
-
- /* take a breather then clean up driver data */
- msleep(100);
-
- kfree(adapter->vfinfo);
- adapter->vfinfo = NULL;
-
- adapter->num_vfs = 0;
- adapter->flags &= ~IXGBE_FLAG_SRIOV_ENABLED;
-}
-
static void ixgbe_service_event_schedule(struct ixgbe_adapter *adapter)
{
if (!test_bit(__IXGBE_DOWN, &adapter->state) &&
@@ -382,7 +323,7 @@ static void ixgbe_dump(struct ixgbe_adapter *adapter)
tx_ring = adapter->tx_ring[n];
tx_buffer_info =
&tx_ring->tx_buffer_info[tx_ring->next_to_clean];
- pr_info(" %5d %5X %5X %016llX %04X %3X %016llX\n",
+ pr_info(" %5d %5X %5X %016llX %04X %p %016llX\n",
n, tx_ring->next_to_use, tx_ring->next_to_clean,
(u64)tx_buffer_info->dma,
tx_buffer_info->length,
@@ -421,7 +362,7 @@ static void ixgbe_dump(struct ixgbe_adapter *adapter)
tx_buffer_info = &tx_ring->tx_buffer_info[i];
u0 = (struct my_u0 *)tx_desc;
pr_info("T [0x%03X] %016llX %016llX %016llX"
- " %04X %3X %016llX %p", i,
+ " %04X %p %016llX %p", i,
le64_to_cpu(u0->a),
le64_to_cpu(u0->b),
(u64)tx_buffer_info->dma,
@@ -523,7 +464,7 @@ rx_ring_summary:
rx_ring->rx_buf_len, true);
if (rx_ring->rx_buf_len
- < IXGBE_RXBUFFER_2048)
+ < IXGBE_RXBUFFER_2K)
print_hex_dump(KERN_INFO, "",
DUMP_PREFIX_ADDRESS, 16, 1,
phys_to_virt(
@@ -640,84 +581,32 @@ static inline void ixgbe_irq_rearm_queues(struct ixgbe_adapter *adapter,
}
}
-void ixgbe_unmap_and_free_tx_resource(struct ixgbe_ring *tx_ring,
- struct ixgbe_tx_buffer *tx_buffer_info)
+static inline void ixgbe_unmap_tx_resource(struct ixgbe_ring *ring,
+ struct ixgbe_tx_buffer *tx_buffer)
{
- if (tx_buffer_info->dma) {
- if (tx_buffer_info->mapped_as_page)
- dma_unmap_page(tx_ring->dev,
- tx_buffer_info->dma,
- tx_buffer_info->length,
- DMA_TO_DEVICE);
+ if (tx_buffer->dma) {
+ if (tx_buffer->tx_flags & IXGBE_TX_FLAGS_MAPPED_AS_PAGE)
+ dma_unmap_page(ring->dev,
+ tx_buffer->dma,
+ tx_buffer->length,
+ DMA_TO_DEVICE);
else
- dma_unmap_single(tx_ring->dev,
- tx_buffer_info->dma,
- tx_buffer_info->length,
- DMA_TO_DEVICE);
- tx_buffer_info->dma = 0;
- }
- if (tx_buffer_info->skb) {
- dev_kfree_skb_any(tx_buffer_info->skb);
- tx_buffer_info->skb = NULL;
+ dma_unmap_single(ring->dev,
+ tx_buffer->dma,
+ tx_buffer->length,
+ DMA_TO_DEVICE);
}
- tx_buffer_info->time_stamp = 0;
- /* tx_buffer_info must be completely set up in the transmit path */
+ tx_buffer->dma = 0;
}
-/**
- * ixgbe_dcb_txq_to_tc - convert a reg index to a traffic class
- * @adapter: driver private struct
- * @index: reg idx of queue to query (0-127)
- *
- * Helper function to determine the traffic index for a particular
- * register index.
- *
- * Returns : a tc index for use in range 0-7, or 0-3
- */
-static u8 ixgbe_dcb_txq_to_tc(struct ixgbe_adapter *adapter, u8 reg_idx)
+void ixgbe_unmap_and_free_tx_resource(struct ixgbe_ring *tx_ring,
+ struct ixgbe_tx_buffer *tx_buffer_info)
{
- int tc = -1;
- int dcb_i = netdev_get_num_tc(adapter->netdev);
-
- /* if DCB is not enabled the queues have no TC */
- if (!(adapter->flags & IXGBE_FLAG_DCB_ENABLED))
- return tc;
-
- /* check valid range */
- if (reg_idx >= adapter->hw.mac.max_tx_queues)
- return tc;
-
- switch (adapter->hw.mac.type) {
- case ixgbe_mac_82598EB:
- tc = reg_idx >> 2;
- break;
- default:
- if (dcb_i != 4 && dcb_i != 8)
- break;
-
- /* if VMDq is enabled the lowest order bits determine TC */
- if (adapter->flags & (IXGBE_FLAG_SRIOV_ENABLED |
- IXGBE_FLAG_VMDQ_ENABLED)) {
- tc = reg_idx & (dcb_i - 1);
- break;
- }
-
- /*
- * Convert the reg_idx into the correct TC. This bitmask
- * targets the last full 32 ring traffic class and assigns
- * it a value of 1. From there the rest of the rings are
- * based on shifting the mask further up to include the
- * reg_idx / 16 and then reg_idx / 8. It assumes dcB_i
- * will only ever be 8 or 4 and that reg_idx will never
- * be greater then 128. The code without the power of 2
- * optimizations would be:
- * (((reg_idx % 32) + 32) * dcb_i) >> (9 - reg_idx / 32)
- */
- tc = ((reg_idx & 0X1F) + 0x20) * dcb_i;
- tc >>= 9 - (reg_idx >> 5);
- }
-
- return tc;
+ ixgbe_unmap_tx_resource(tx_ring, tx_buffer_info);
+ if (tx_buffer_info->skb)
+ dev_kfree_skb_any(tx_buffer_info->skb);
+ tx_buffer_info->skb = NULL;
+ /* tx_buffer_info must be completely set up in the transmit path */
}
static void ixgbe_update_xoff_received(struct ixgbe_adapter *adapter)
@@ -765,7 +654,7 @@ static void ixgbe_update_xoff_received(struct ixgbe_adapter *adapter)
/* disarm tx queues that have received xoff frames */
for (i = 0; i < adapter->num_tx_queues; i++) {
struct ixgbe_ring *tx_ring = adapter->tx_ring[i];
- u32 tc = ixgbe_dcb_txq_to_tc(adapter, tx_ring->reg_idx);
+ u8 tc = tx_ring->dcb_tc;
if (xoff[tc])
clear_bit(__IXGBE_HANG_CHECK_ARMED, &tx_ring->state);
@@ -827,15 +716,6 @@ static inline bool ixgbe_check_tx_hang(struct ixgbe_ring *tx_ring)
return ret;
}
-#define IXGBE_MAX_TXD_PWR 14
-#define IXGBE_MAX_DATA_PER_TXD (1 << IXGBE_MAX_TXD_PWR)
-
-/* Tx Descriptors needed, worst case */
-#define TXD_USE_COUNT(S) (((S) >> IXGBE_MAX_TXD_PWR) + \
- (((S) & (IXGBE_MAX_DATA_PER_TXD - 1)) ? 1 : 0))
-#define DESC_NEEDED (TXD_USE_COUNT(IXGBE_MAX_DATA_PER_TXD) /* skb->data */ + \
- MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE) + 1) /* for context */
-
/**
* ixgbe_tx_timeout_reset - initiate reset due to Tx timeout
* @adapter: driver private struct
@@ -859,56 +739,72 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector,
struct ixgbe_ring *tx_ring)
{
struct ixgbe_adapter *adapter = q_vector->adapter;
- union ixgbe_adv_tx_desc *tx_desc, *eop_desc;
- struct ixgbe_tx_buffer *tx_buffer_info;
+ struct ixgbe_tx_buffer *tx_buffer;
+ union ixgbe_adv_tx_desc *tx_desc;
unsigned int total_bytes = 0, total_packets = 0;
- u16 i, eop, count = 0;
+ unsigned int budget = q_vector->tx.work_limit;
+ u16 i = tx_ring->next_to_clean;
- i = tx_ring->next_to_clean;
- eop = tx_ring->tx_buffer_info[i].next_to_watch;
- eop_desc = IXGBE_TX_DESC_ADV(tx_ring, eop);
+ tx_buffer = &tx_ring->tx_buffer_info[i];
+ tx_desc = IXGBE_TX_DESC_ADV(tx_ring, i);
- while ((eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD)) &&
- (count < tx_ring->work_limit)) {
- bool cleaned = false;
- rmb(); /* read buffer_info after eop_desc */
- for ( ; !cleaned; count++) {
- tx_desc = IXGBE_TX_DESC_ADV(tx_ring, i);
- tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ for (; budget; budget--) {
+ union ixgbe_adv_tx_desc *eop_desc = tx_buffer->next_to_watch;
+
+ /* if next_to_watch is not set then there is no work pending */
+ if (!eop_desc)
+ break;
+
+ /* if DD is not set pending work has not been completed */
+ if (!(eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD)))
+ break;
+
+ /* count the packet as being completed */
+ tx_ring->tx_stats.completed++;
+ /* clear next_to_watch to prevent false hangs */
+ tx_buffer->next_to_watch = NULL;
+
+ /* prevent any other reads prior to eop_desc being verified */
+ rmb();
+
+ do {
+ ixgbe_unmap_tx_resource(tx_ring, tx_buffer);
tx_desc->wb.status = 0;
- cleaned = (i == eop);
+ if (likely(tx_desc == eop_desc)) {
+ eop_desc = NULL;
+ dev_kfree_skb_any(tx_buffer->skb);
+ tx_buffer->skb = NULL;
+ total_bytes += tx_buffer->bytecount;
+ total_packets += tx_buffer->gso_segs;
+ }
+
+ tx_buffer++;
+ tx_desc++;
i++;
- if (i == tx_ring->count)
+ if (unlikely(i == tx_ring->count)) {
i = 0;
- if (cleaned && tx_buffer_info->skb) {
- total_bytes += tx_buffer_info->bytecount;
- total_packets += tx_buffer_info->gso_segs;
+ tx_buffer = tx_ring->tx_buffer_info;
+ tx_desc = IXGBE_TX_DESC_ADV(tx_ring, 0);
}
- ixgbe_unmap_and_free_tx_resource(tx_ring,
- tx_buffer_info);
- }
-
- tx_ring->tx_stats.completed++;
- eop = tx_ring->tx_buffer_info[i].next_to_watch;
- eop_desc = IXGBE_TX_DESC_ADV(tx_ring, eop);
+ } while (eop_desc);
}
tx_ring->next_to_clean = i;
- tx_ring->total_bytes += total_bytes;
- tx_ring->total_packets += total_packets;
u64_stats_update_begin(&tx_ring->syncp);
- tx_ring->stats.packets += total_packets;
tx_ring->stats.bytes += total_bytes;
+ tx_ring->stats.packets += total_packets;
u64_stats_update_end(&tx_ring->syncp);
+ q_vector->tx.total_bytes += total_bytes;
+ q_vector->tx.total_packets += total_packets;
if (check_for_tx_hang(tx_ring) && ixgbe_check_tx_hang(tx_ring)) {
/* schedule immediate reset if we believe we hung */
struct ixgbe_hw *hw = &adapter->hw;
- tx_desc = IXGBE_TX_DESC_ADV(tx_ring, eop);
+ tx_desc = IXGBE_TX_DESC_ADV(tx_ring, i);
e_err(drv, "Detected Tx Unit Hang\n"
" Tx Queue <%d>\n"
" TDH, TDT <%x>, <%x>\n"
@@ -920,8 +816,8 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector,
tx_ring->queue_index,
IXGBE_READ_REG(hw, IXGBE_TDH(tx_ring->reg_idx)),
IXGBE_READ_REG(hw, IXGBE_TDT(tx_ring->reg_idx)),
- tx_ring->next_to_use, eop,
- tx_ring->tx_buffer_info[eop].time_stamp, jiffies);
+ tx_ring->next_to_use, i,
+ tx_ring->tx_buffer_info[i].time_stamp, jiffies);
netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
@@ -937,8 +833,8 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector,
}
#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)
- if (unlikely(count && netif_carrier_ok(tx_ring->netdev) &&
- (IXGBE_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) {
+ if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) &&
+ (ixgbe_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) {
/* Make sure that anybody stopping the queue after this
* sees the new next_to_clean.
*/
@@ -950,7 +846,7 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector,
}
}
- return count < tx_ring->work_limit;
+ return !!budget;
}
#ifdef CONFIG_IXGBE_DCA
@@ -966,12 +862,12 @@ static void ixgbe_update_rx_dca(struct ixgbe_adapter *adapter,
switch (hw->mac.type) {
case ixgbe_mac_82598EB:
rxctrl &= ~IXGBE_DCA_RXCTRL_CPUID_MASK;
- rxctrl |= dca3_get_tag(&adapter->pdev->dev, cpu);
+ rxctrl |= dca3_get_tag(rx_ring->dev, cpu);
break;
case ixgbe_mac_82599EB:
case ixgbe_mac_X540:
rxctrl &= ~IXGBE_DCA_RXCTRL_CPUID_MASK_82599;
- rxctrl |= (dca3_get_tag(&adapter->pdev->dev, cpu) <<
+ rxctrl |= (dca3_get_tag(rx_ring->dev, cpu) <<
IXGBE_DCA_RXCTRL_CPUID_SHIFT_82599);
break;
default:
@@ -995,7 +891,7 @@ static void ixgbe_update_tx_dca(struct ixgbe_adapter *adapter,
case ixgbe_mac_82598EB:
txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL(reg_idx));
txctrl &= ~IXGBE_DCA_TXCTRL_CPUID_MASK;
- txctrl |= dca3_get_tag(&adapter->pdev->dev, cpu);
+ txctrl |= dca3_get_tag(tx_ring->dev, cpu);
txctrl |= IXGBE_DCA_TXCTRL_DESC_DCA_EN;
IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL(reg_idx), txctrl);
break;
@@ -1003,7 +899,7 @@ static void ixgbe_update_tx_dca(struct ixgbe_adapter *adapter,
case ixgbe_mac_X540:
txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(reg_idx));
txctrl &= ~IXGBE_DCA_TXCTRL_CPUID_MASK_82599;
- txctrl |= (dca3_get_tag(&adapter->pdev->dev, cpu) <<
+ txctrl |= (dca3_get_tag(tx_ring->dev, cpu) <<
IXGBE_DCA_TXCTRL_CPUID_SHIFT_82599);
txctrl |= IXGBE_DCA_TXCTRL_DESC_DCA_EN;
IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL_82599(reg_idx), txctrl);
@@ -1016,26 +912,17 @@ static void ixgbe_update_tx_dca(struct ixgbe_adapter *adapter,
static void ixgbe_update_dca(struct ixgbe_q_vector *q_vector)
{
struct ixgbe_adapter *adapter = q_vector->adapter;
+ struct ixgbe_ring *ring;
int cpu = get_cpu();
- long r_idx;
- int i;
if (q_vector->cpu == cpu)
goto out_no_update;
- r_idx = find_first_bit(q_vector->txr_idx, adapter->num_tx_queues);
- for (i = 0; i < q_vector->txr_count; i++) {
- ixgbe_update_tx_dca(adapter, adapter->tx_ring[r_idx], cpu);
- r_idx = find_next_bit(q_vector->txr_idx, adapter->num_tx_queues,
- r_idx + 1);
- }
+ for (ring = q_vector->tx.ring; ring != NULL; ring = ring->next)
+ ixgbe_update_tx_dca(adapter, ring, cpu);
- r_idx = find_first_bit(q_vector->rxr_idx, adapter->num_rx_queues);
- for (i = 0; i < q_vector->rxr_count; i++) {
- ixgbe_update_rx_dca(adapter, adapter->rx_ring[r_idx], cpu);
- r_idx = find_next_bit(q_vector->rxr_idx, adapter->num_rx_queues,
- r_idx + 1);
- }
+ for (ring = q_vector->rx.ring; ring != NULL; ring = ring->next)
+ ixgbe_update_rx_dca(adapter, ring, cpu);
q_vector->cpu = cpu;
out_no_update:
@@ -1069,7 +956,7 @@ static int __ixgbe_notify_dca(struct device *dev, void *data)
struct ixgbe_adapter *adapter = dev_get_drvdata(dev);
unsigned long event = *(unsigned long *)data;
- if (!(adapter->flags & IXGBE_FLAG_DCA_ENABLED))
+ if (!(adapter->flags & IXGBE_FLAG_DCA_CAPABLE))
return 0;
switch (event) {
@@ -1103,6 +990,24 @@ static inline void ixgbe_rx_hash(union ixgbe_adv_rx_desc *rx_desc,
}
/**
+ * ixgbe_rx_is_fcoe - check the rx desc for incoming pkt type
+ * @adapter: address of board private structure
+ * @rx_desc: advanced rx descriptor
+ *
+ * Returns : true if it is FCoE pkt
+ */
+static inline bool ixgbe_rx_is_fcoe(struct ixgbe_adapter *adapter,
+ union ixgbe_adv_rx_desc *rx_desc)
+{
+ __le16 pkt_info = rx_desc->wb.lower.lo_dword.hs_rss.pkt_info;
+
+ return (adapter->flags & IXGBE_FLAG_FCOE_ENABLED) &&
+ ((pkt_info & cpu_to_le16(IXGBE_RXDADV_PKTTYPE_ETQF_MASK)) ==
+ (cpu_to_le16(IXGBE_ETQF_FILTER_FCOE <<
+ IXGBE_RXDADV_PKTTYPE_ETQF_SHIFT)));
+}
+
+/**
* ixgbe_receive_skb - Send a completed packet up the stack
* @adapter: board private structure
* @skb: packet to send up
@@ -1134,14 +1039,14 @@ static void ixgbe_receive_skb(struct ixgbe_q_vector *q_vector,
* @adapter: address of board private structure
* @status_err: hardware indication of status of receive
* @skb: skb currently being received and modified
+ * @status_err: status error value of last descriptor in packet
**/
static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter,
union ixgbe_adv_rx_desc *rx_desc,
- struct sk_buff *skb)
+ struct sk_buff *skb,
+ u32 status_err)
{
- u32 status_err = le32_to_cpu(rx_desc->wb.upper.status_error);
-
- skb_checksum_none_assert(skb);
+ skb->ip_summed = CHECKSUM_NONE;
/* Rx csum disabled */
if (!(adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED))
@@ -1330,9 +1235,9 @@ static inline bool ixgbe_get_rsc_state(union ixgbe_adv_rx_desc *rx_desc)
IXGBE_RXDADV_RSCCNT_MASK);
}
-static void ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
+static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
struct ixgbe_ring *rx_ring,
- int *work_done, int work_to_do)
+ int budget)
{
struct ixgbe_adapter *adapter = q_vector->adapter;
union ixgbe_adv_rx_desc *rx_desc, *next_rxd;
@@ -1366,8 +1271,8 @@ static void ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
if (ring_is_rsc_enabled(rx_ring))
pkt_is_rsc = ixgbe_get_rsc_state(rx_desc);
- /* if this is a skb from previous receive DMA will be 0 */
- if (rx_buffer_info->dma) {
+ /* linear means we are building an skb from multiple pages */
+ if (!skb_is_nonlinear(skb)) {
u16 hlen;
if (pkt_is_rsc &&
!(staterr & IXGBE_RXD_STAT_EOP) &&
@@ -1421,7 +1326,7 @@ static void ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
skb->len += upper_len;
skb->data_len += upper_len;
- skb->truesize += upper_len;
+ skb->truesize += PAGE_SIZE / 2;
}
i++;
@@ -1485,14 +1390,12 @@ static void ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
}
/* ERR_MASK will only have valid bits if EOP set */
- if (staterr & IXGBE_RXDADV_ERR_FRAME_ERR_MASK) {
- /* trim packet back to size 0 and recycle it */
- __pskb_trim(skb, 0);
- rx_buffer_info->skb = skb;
+ if (unlikely(staterr & IXGBE_RXDADV_ERR_FRAME_ERR_MASK)) {
+ dev_kfree_skb_any(skb);
goto next_desc;
}
- ixgbe_rx_checksum(adapter, rx_desc, skb);
+ ixgbe_rx_checksum(adapter, rx_desc, skb, staterr);
if (adapter->netdev->features & NETIF_F_RXHASH)
ixgbe_rx_hash(rx_desc, skb);
@@ -1503,19 +1406,22 @@ static void ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
skb->protocol = eth_type_trans(skb, rx_ring->netdev);
#ifdef IXGBE_FCOE
/* if ddp, not passing to ULD unless for FCP_RSP or error */
- if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED) {
- ddp_bytes = ixgbe_fcoe_ddp(adapter, rx_desc, skb);
- if (!ddp_bytes)
+ if (ixgbe_rx_is_fcoe(adapter, rx_desc)) {
+ ddp_bytes = ixgbe_fcoe_ddp(adapter, rx_desc, skb,
+ staterr);
+ if (!ddp_bytes) {
+ dev_kfree_skb_any(skb);
goto next_desc;
+ }
}
#endif /* IXGBE_FCOE */
ixgbe_receive_skb(q_vector, skb, staterr, rx_ring, rx_desc);
+ budget--;
next_desc:
rx_desc->wb.upper.status_error = 0;
- (*work_done)++;
- if (*work_done >= work_to_do)
+ if (!budget)
break;
/* return some buffers to hardware, one at a time is too slow */
@@ -1530,7 +1436,7 @@ next_desc:
}
rx_ring->next_to_clean = i;
- cleaned_count = IXGBE_DESC_UNUSED(rx_ring);
+ cleaned_count = ixgbe_desc_unused(rx_ring);
if (cleaned_count)
ixgbe_alloc_rx_buffers(rx_ring, cleaned_count);
@@ -1550,15 +1456,16 @@ next_desc:
}
#endif /* IXGBE_FCOE */
- rx_ring->total_packets += total_rx_packets;
- rx_ring->total_bytes += total_rx_bytes;
u64_stats_update_begin(&rx_ring->syncp);
rx_ring->stats.packets += total_rx_packets;
rx_ring->stats.bytes += total_rx_bytes;
u64_stats_update_end(&rx_ring->syncp);
+ q_vector->rx.total_packets += total_rx_packets;
+ q_vector->rx.total_bytes += total_rx_bytes;
+
+ return !!budget;
}
-static int ixgbe_clean_rxonly(struct napi_struct *, int);
/**
* ixgbe_configure_msix - Configure MSI-X hardware
* @adapter: board private structure
@@ -1569,62 +1476,46 @@ static int ixgbe_clean_rxonly(struct napi_struct *, int);
static void ixgbe_configure_msix(struct ixgbe_adapter *adapter)
{
struct ixgbe_q_vector *q_vector;
- int i, q_vectors, v_idx, r_idx;
+ int q_vectors, v_idx;
u32 mask;
q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ /* Populate MSIX to EITR Select */
+ if (adapter->num_vfs > 32) {
+ u32 eitrsel = (1 << (adapter->num_vfs - 32)) - 1;
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EITRSEL, eitrsel);
+ }
+
/*
* Populate the IVAR table and set the ITR values to the
* corresponding register.
*/
for (v_idx = 0; v_idx < q_vectors; v_idx++) {
+ struct ixgbe_ring *ring;
q_vector = adapter->q_vector[v_idx];
- /* XXX for_each_set_bit(...) */
- r_idx = find_first_bit(q_vector->rxr_idx,
- adapter->num_rx_queues);
-
- for (i = 0; i < q_vector->rxr_count; i++) {
- u8 reg_idx = adapter->rx_ring[r_idx]->reg_idx;
- ixgbe_set_ivar(adapter, 0, reg_idx, v_idx);
- r_idx = find_next_bit(q_vector->rxr_idx,
- adapter->num_rx_queues,
- r_idx + 1);
- }
- r_idx = find_first_bit(q_vector->txr_idx,
- adapter->num_tx_queues);
-
- for (i = 0; i < q_vector->txr_count; i++) {
- u8 reg_idx = adapter->tx_ring[r_idx]->reg_idx;
- ixgbe_set_ivar(adapter, 1, reg_idx, v_idx);
- r_idx = find_next_bit(q_vector->txr_idx,
- adapter->num_tx_queues,
- r_idx + 1);
- }
- if (q_vector->txr_count && !q_vector->rxr_count)
- /* tx only */
- q_vector->eitr = adapter->tx_eitr_param;
- else if (q_vector->rxr_count)
- /* rx or mixed */
- q_vector->eitr = adapter->rx_eitr_param;
+ for (ring = q_vector->rx.ring; ring != NULL; ring = ring->next)
+ ixgbe_set_ivar(adapter, 0, ring->reg_idx, v_idx);
- ixgbe_write_eitr(q_vector);
- /* If Flow Director is enabled, set interrupt affinity */
- if ((adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) ||
- (adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE)) {
- /*
- * Allocate the affinity_hint cpumask, assign the mask
- * for this vector, and set our affinity_hint for
- * this irq.
- */
- if (!alloc_cpumask_var(&q_vector->affinity_mask,
- GFP_KERNEL))
- return;
- cpumask_set_cpu(v_idx, q_vector->affinity_mask);
- irq_set_affinity_hint(adapter->msix_entries[v_idx].vector,
- q_vector->affinity_mask);
+ for (ring = q_vector->tx.ring; ring != NULL; ring = ring->next)
+ ixgbe_set_ivar(adapter, 1, ring->reg_idx, v_idx);
+
+ if (q_vector->tx.ring && !q_vector->rx.ring) {
+ /* tx only vector */
+ if (adapter->tx_itr_setting == 1)
+ q_vector->itr = IXGBE_10K_ITR;
+ else
+ q_vector->itr = adapter->tx_itr_setting;
+ } else {
+ /* rx or rx/tx vector */
+ if (adapter->rx_itr_setting == 1)
+ q_vector->itr = IXGBE_20K_ITR;
+ else
+ q_vector->itr = adapter->rx_itr_setting;
}
+
+ ixgbe_write_eitr(q_vector);
}
switch (adapter->hw.mac.type) {
@@ -1636,7 +1527,6 @@ static void ixgbe_configure_msix(struct ixgbe_adapter *adapter)
case ixgbe_mac_X540:
ixgbe_set_ivar(adapter, -1, 1, v_idx);
break;
-
default:
break;
}
@@ -1644,12 +1534,10 @@ static void ixgbe_configure_msix(struct ixgbe_adapter *adapter)
/* set up to autoclear timer, and the vectors */
mask = IXGBE_EIMS_ENABLE_MASK;
- if (adapter->num_vfs)
- mask &= ~(IXGBE_EIMS_OTHER |
- IXGBE_EIMS_MAILBOX |
- IXGBE_EIMS_LSC);
- else
- mask &= ~(IXGBE_EIMS_OTHER | IXGBE_EIMS_LSC);
+ mask &= ~(IXGBE_EIMS_OTHER |
+ IXGBE_EIMS_MAILBOX |
+ IXGBE_EIMS_LSC);
+
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIAC, mask);
}
@@ -1662,11 +1550,8 @@ enum latency_range {
/**
* ixgbe_update_itr - update the dynamic ITR value based on statistics
- * @adapter: pointer to adapter
- * @eitr: eitr setting (ints per sec) to give last timeslice
- * @itr_setting: current throttle rate in ints/second
- * @packets: the number of packets during this measurement interval
- * @bytes: the number of bytes during this measurement interval
+ * @q_vector: structure containing interrupt and ring information
+ * @ring_container: structure containing ring performance data
*
* Stores a new ITR value based on packets and byte
* counts during the last interrupt. The advantage of per interrupt
@@ -1678,17 +1563,18 @@ enum latency_range {
* this functionality is controlled by the InterruptThrottleRate module
* parameter (see ixgbe_param.c)
**/
-static u8 ixgbe_update_itr(struct ixgbe_adapter *adapter,
- u32 eitr, u8 itr_setting,
- int packets, int bytes)
+static void ixgbe_update_itr(struct ixgbe_q_vector *q_vector,
+ struct ixgbe_ring_container *ring_container)
{
- unsigned int retval = itr_setting;
- u32 timepassed_us;
u64 bytes_perint;
+ struct ixgbe_adapter *adapter = q_vector->adapter;
+ int bytes = ring_container->total_bytes;
+ int packets = ring_container->total_packets;
+ u32 timepassed_us;
+ u8 itr_setting = ring_container->itr;
if (packets == 0)
- goto update_itr_done;
-
+ return;
/* simple throttlerate management
* 0-20MB/s lowest (100000 ints/s)
@@ -1696,28 +1582,32 @@ static u8 ixgbe_update_itr(struct ixgbe_adapter *adapter,
* 100-1249MB/s bulk (8000 ints/s)
*/
/* what was last interrupt timeslice? */
- timepassed_us = 1000000/eitr;
+ timepassed_us = q_vector->itr >> 2;
bytes_perint = bytes / timepassed_us; /* bytes/usec */
switch (itr_setting) {
case lowest_latency:
if (bytes_perint > adapter->eitr_low)
- retval = low_latency;
+ itr_setting = low_latency;
break;
case low_latency:
if (bytes_perint > adapter->eitr_high)
- retval = bulk_latency;
+ itr_setting = bulk_latency;
else if (bytes_perint <= adapter->eitr_low)
- retval = lowest_latency;
+ itr_setting = lowest_latency;
break;
case bulk_latency:
if (bytes_perint <= adapter->eitr_high)
- retval = low_latency;
+ itr_setting = low_latency;
break;
}
-update_itr_done:
- return retval;
+ /* clear work counters since we have the values we need */
+ ring_container->total_bytes = 0;
+ ring_container->total_packets = 0;
+
+ /* write updated itr to ring container */
+ ring_container->itr = itr_setting;
}
/**
@@ -1733,7 +1623,7 @@ void ixgbe_write_eitr(struct ixgbe_q_vector *q_vector)
struct ixgbe_adapter *adapter = q_vector->adapter;
struct ixgbe_hw *hw = &adapter->hw;
int v_idx = q_vector->v_idx;
- u32 itr_reg = EITR_INTS_PER_SEC_TO_REG(q_vector->eitr);
+ u32 itr_reg = q_vector->itr;
switch (adapter->hw.mac.type) {
case ixgbe_mac_82598EB:
@@ -1743,15 +1633,6 @@ void ixgbe_write_eitr(struct ixgbe_q_vector *q_vector)
case ixgbe_mac_82599EB:
case ixgbe_mac_X540:
/*
- * 82599 and X540 can support a value of zero, so allow it for
- * max interrupt rate, but there is an errata where it can
- * not be zero with RSC
- */
- if (itr_reg == 8 &&
- !(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED))
- itr_reg = 0;
-
- /*
* set the WDIS bit to not clear the timer bits and cause an
* immediate assertion of the interrupt
*/
@@ -1763,65 +1644,38 @@ void ixgbe_write_eitr(struct ixgbe_q_vector *q_vector)
IXGBE_WRITE_REG(hw, IXGBE_EITR(v_idx), itr_reg);
}
-static void ixgbe_set_itr_msix(struct ixgbe_q_vector *q_vector)
+static void ixgbe_set_itr(struct ixgbe_q_vector *q_vector)
{
- struct ixgbe_adapter *adapter = q_vector->adapter;
- int i, r_idx;
- u32 new_itr;
- u8 current_itr, ret_itr;
-
- r_idx = find_first_bit(q_vector->txr_idx, adapter->num_tx_queues);
- for (i = 0; i < q_vector->txr_count; i++) {
- struct ixgbe_ring *tx_ring = adapter->tx_ring[r_idx];
- ret_itr = ixgbe_update_itr(adapter, q_vector->eitr,
- q_vector->tx_itr,
- tx_ring->total_packets,
- tx_ring->total_bytes);
- /* if the result for this queue would decrease interrupt
- * rate for this vector then use that result */
- q_vector->tx_itr = ((q_vector->tx_itr > ret_itr) ?
- q_vector->tx_itr - 1 : ret_itr);
- r_idx = find_next_bit(q_vector->txr_idx, adapter->num_tx_queues,
- r_idx + 1);
- }
-
- r_idx = find_first_bit(q_vector->rxr_idx, adapter->num_rx_queues);
- for (i = 0; i < q_vector->rxr_count; i++) {
- struct ixgbe_ring *rx_ring = adapter->rx_ring[r_idx];
- ret_itr = ixgbe_update_itr(adapter, q_vector->eitr,
- q_vector->rx_itr,
- rx_ring->total_packets,
- rx_ring->total_bytes);
- /* if the result for this queue would decrease interrupt
- * rate for this vector then use that result */
- q_vector->rx_itr = ((q_vector->rx_itr > ret_itr) ?
- q_vector->rx_itr - 1 : ret_itr);
- r_idx = find_next_bit(q_vector->rxr_idx, adapter->num_rx_queues,
- r_idx + 1);
- }
-
- current_itr = max(q_vector->rx_itr, q_vector->tx_itr);
+ u32 new_itr = q_vector->itr;
+ u8 current_itr;
+
+ ixgbe_update_itr(q_vector, &q_vector->tx);
+ ixgbe_update_itr(q_vector, &q_vector->rx);
+
+ current_itr = max(q_vector->rx.itr, q_vector->tx.itr);
switch (current_itr) {
/* counts and packets in update_itr are dependent on these numbers */
case lowest_latency:
- new_itr = 100000;
+ new_itr = IXGBE_100K_ITR;
break;
case low_latency:
- new_itr = 20000; /* aka hwitr = ~200 */
+ new_itr = IXGBE_20K_ITR;
break;
case bulk_latency:
+ new_itr = IXGBE_8K_ITR;
+ break;
default:
- new_itr = 8000;
break;
}
- if (new_itr != q_vector->eitr) {
+ if (new_itr != q_vector->itr) {
/* do an exponential smoothing */
- new_itr = ((q_vector->eitr * 9) + new_itr)/10;
+ new_itr = (10 * new_itr * q_vector->itr) /
+ ((9 * new_itr) + q_vector->itr);
- /* save the algorithm value here, not the smoothed one */
- q_vector->eitr = new_itr;
+ /* save the algorithm value here */
+ q_vector->itr = new_itr & IXGBE_MAX_EITR;
ixgbe_write_eitr(q_vector);
}
@@ -1898,6 +1752,39 @@ static void ixgbe_check_fan_failure(struct ixgbe_adapter *adapter, u32 eicr)
}
}
+static void ixgbe_check_overtemp_event(struct ixgbe_adapter *adapter, u32 eicr)
+{
+ if (!(adapter->flags2 & IXGBE_FLAG2_TEMP_SENSOR_CAPABLE))
+ return;
+
+ switch (adapter->hw.mac.type) {
+ case ixgbe_mac_82599EB:
+ /*
+ * Need to check link state so complete overtemp check
+ * on service task
+ */
+ if (((eicr & IXGBE_EICR_GPI_SDP0) || (eicr & IXGBE_EICR_LSC)) &&
+ (!test_bit(__IXGBE_DOWN, &adapter->state))) {
+ adapter->interrupt_event = eicr;
+ adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_EVENT;
+ ixgbe_service_event_schedule(adapter);
+ return;
+ }
+ return;
+ case ixgbe_mac_X540:
+ if (!(eicr & IXGBE_EICR_TS))
+ return;
+ break;
+ default:
+ return;
+ }
+
+ e_crit(drv,
+ "Network adapter has been stopped because it has over heated. "
+ "Restart the computer. If the problem persists, "
+ "power off the system and replace the adapter\n");
+}
+
static void ixgbe_check_sfp_event(struct ixgbe_adapter *adapter, u32 eicr)
{
struct ixgbe_hw *hw = &adapter->hw;
@@ -1935,73 +1822,6 @@ static void ixgbe_check_lsc(struct ixgbe_adapter *adapter)
}
}
-static irqreturn_t ixgbe_msix_lsc(int irq, void *data)
-{
- struct net_device *netdev = data;
- struct ixgbe_adapter *adapter = netdev_priv(netdev);
- struct ixgbe_hw *hw = &adapter->hw;
- u32 eicr;
-
- /*
- * Workaround for Silicon errata. Use clear-by-write instead
- * of clear-by-read. Reading with EICS will return the
- * interrupt causes without clearing, which later be done
- * with the write to EICR.
- */
- eicr = IXGBE_READ_REG(hw, IXGBE_EICS);
- IXGBE_WRITE_REG(hw, IXGBE_EICR, eicr);
-
- if (eicr & IXGBE_EICR_LSC)
- ixgbe_check_lsc(adapter);
-
- if (eicr & IXGBE_EICR_MAILBOX)
- ixgbe_msg_task(adapter);
-
- switch (hw->mac.type) {
- case ixgbe_mac_82599EB:
- case ixgbe_mac_X540:
- /* Handle Flow Director Full threshold interrupt */
- if (eicr & IXGBE_EICR_FLOW_DIR) {
- int reinit_count = 0;
- int i;
- for (i = 0; i < adapter->num_tx_queues; i++) {
- struct ixgbe_ring *ring = adapter->tx_ring[i];
- if (test_and_clear_bit(__IXGBE_TX_FDIR_INIT_DONE,
- &ring->state))
- reinit_count++;
- }
- if (reinit_count) {
- /* no more flow director interrupts until after init */
- IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_FLOW_DIR);
- eicr &= ~IXGBE_EICR_FLOW_DIR;
- adapter->flags2 |= IXGBE_FLAG2_FDIR_REQUIRES_REINIT;
- ixgbe_service_event_schedule(adapter);
- }
- }
- ixgbe_check_sfp_event(adapter, eicr);
- if ((adapter->flags2 & IXGBE_FLAG2_TEMP_SENSOR_CAPABLE) &&
- ((eicr & IXGBE_EICR_GPI_SDP0) || (eicr & IXGBE_EICR_LSC))) {
- if (!test_bit(__IXGBE_DOWN, &adapter->state)) {
- adapter->interrupt_event = eicr;
- adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_EVENT;
- ixgbe_service_event_schedule(adapter);
- }
- }
- break;
- default:
- break;
- }
-
- ixgbe_check_fan_failure(adapter, eicr);
-
- /* re-enable the original interrupt state, no lsc, no queues */
- if (!test_bit(__IXGBE_DOWN, &adapter->state))
- IXGBE_WRITE_REG(hw, IXGBE_EIMS, eicr &
- ~(IXGBE_EIMS_LSC | IXGBE_EIMS_RTX_QUEUE));
-
- return IRQ_HANDLED;
-}
-
static inline void ixgbe_irq_enable_queues(struct ixgbe_adapter *adapter,
u64 qmask)
{
@@ -2054,240 +1874,124 @@ static inline void ixgbe_irq_disable_queues(struct ixgbe_adapter *adapter,
/* skip the flush */
}
-static irqreturn_t ixgbe_msix_clean_tx(int irq, void *data)
-{
- struct ixgbe_q_vector *q_vector = data;
- struct ixgbe_adapter *adapter = q_vector->adapter;
- struct ixgbe_ring *tx_ring;
- int i, r_idx;
-
- if (!q_vector->txr_count)
- return IRQ_HANDLED;
-
- r_idx = find_first_bit(q_vector->txr_idx, adapter->num_tx_queues);
- for (i = 0; i < q_vector->txr_count; i++) {
- tx_ring = adapter->tx_ring[r_idx];
- tx_ring->total_bytes = 0;
- tx_ring->total_packets = 0;
- r_idx = find_next_bit(q_vector->txr_idx, adapter->num_tx_queues,
- r_idx + 1);
- }
-
- /* EIAM disabled interrupts (on this vector) for us */
- napi_schedule(&q_vector->napi);
-
- return IRQ_HANDLED;
-}
-
/**
- * ixgbe_msix_clean_rx - single unshared vector rx clean (all queues)
- * @irq: unused
- * @data: pointer to our q_vector struct for this interrupt vector
+ * ixgbe_irq_enable - Enable default interrupt generation settings
+ * @adapter: board private structure
**/
-static irqreturn_t ixgbe_msix_clean_rx(int irq, void *data)
-{
- struct ixgbe_q_vector *q_vector = data;
- struct ixgbe_adapter *adapter = q_vector->adapter;
- struct ixgbe_ring *rx_ring;
- int r_idx;
- int i;
-
-#ifdef CONFIG_IXGBE_DCA
- if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
- ixgbe_update_dca(q_vector);
-#endif
-
- r_idx = find_first_bit(q_vector->rxr_idx, adapter->num_rx_queues);
- for (i = 0; i < q_vector->rxr_count; i++) {
- rx_ring = adapter->rx_ring[r_idx];
- rx_ring->total_bytes = 0;
- rx_ring->total_packets = 0;
- r_idx = find_next_bit(q_vector->rxr_idx, adapter->num_rx_queues,
- r_idx + 1);
- }
-
- if (!q_vector->rxr_count)
- return IRQ_HANDLED;
-
- /* EIAM disabled interrupts (on this vector) for us */
- napi_schedule(&q_vector->napi);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t ixgbe_msix_clean_many(int irq, void *data)
+static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter, bool queues,
+ bool flush)
{
- struct ixgbe_q_vector *q_vector = data;
- struct ixgbe_adapter *adapter = q_vector->adapter;
- struct ixgbe_ring *ring;
- int r_idx;
- int i;
-
- if (!q_vector->txr_count && !q_vector->rxr_count)
- return IRQ_HANDLED;
+ u32 mask = (IXGBE_EIMS_ENABLE_MASK & ~IXGBE_EIMS_RTX_QUEUE);
- r_idx = find_first_bit(q_vector->txr_idx, adapter->num_tx_queues);
- for (i = 0; i < q_vector->txr_count; i++) {
- ring = adapter->tx_ring[r_idx];
- ring->total_bytes = 0;
- ring->total_packets = 0;
- r_idx = find_next_bit(q_vector->txr_idx, adapter->num_tx_queues,
- r_idx + 1);
- }
+ /* don't reenable LSC while waiting for link */
+ if (adapter->flags & IXGBE_FLAG_NEED_LINK_UPDATE)
+ mask &= ~IXGBE_EIMS_LSC;
- r_idx = find_first_bit(q_vector->rxr_idx, adapter->num_rx_queues);
- for (i = 0; i < q_vector->rxr_count; i++) {
- ring = adapter->rx_ring[r_idx];
- ring->total_bytes = 0;
- ring->total_packets = 0;
- r_idx = find_next_bit(q_vector->rxr_idx, adapter->num_rx_queues,
- r_idx + 1);
+ if (adapter->flags2 & IXGBE_FLAG2_TEMP_SENSOR_CAPABLE)
+ switch (adapter->hw.mac.type) {
+ case ixgbe_mac_82599EB:
+ mask |= IXGBE_EIMS_GPI_SDP0;
+ break;
+ case ixgbe_mac_X540:
+ mask |= IXGBE_EIMS_TS;
+ break;
+ default:
+ break;
+ }
+ if (adapter->flags & IXGBE_FLAG_FAN_FAIL_CAPABLE)
+ mask |= IXGBE_EIMS_GPI_SDP1;
+ switch (adapter->hw.mac.type) {
+ case ixgbe_mac_82599EB:
+ mask |= IXGBE_EIMS_GPI_SDP1;
+ mask |= IXGBE_EIMS_GPI_SDP2;
+ case ixgbe_mac_X540:
+ mask |= IXGBE_EIMS_ECC;
+ mask |= IXGBE_EIMS_MAILBOX;
+ break;
+ default:
+ break;
}
+ if ((adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) &&
+ !(adapter->flags2 & IXGBE_FLAG2_FDIR_REQUIRES_REINIT))
+ mask |= IXGBE_EIMS_FLOW_DIR;
- /* EIAM disabled interrupts (on this vector) for us */
- napi_schedule(&q_vector->napi);
-
- return IRQ_HANDLED;
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, mask);
+ if (queues)
+ ixgbe_irq_enable_queues(adapter, ~0);
+ if (flush)
+ IXGBE_WRITE_FLUSH(&adapter->hw);
}
-/**
- * ixgbe_clean_rxonly - msix (aka one shot) rx clean routine
- * @napi: napi struct with our devices info in it
- * @budget: amount of work driver is allowed to do this pass, in packets
- *
- * This function is optimized for cleaning one queue only on a single
- * q_vector!!!
- **/
-static int ixgbe_clean_rxonly(struct napi_struct *napi, int budget)
+static irqreturn_t ixgbe_msix_other(int irq, void *data)
{
- struct ixgbe_q_vector *q_vector =
- container_of(napi, struct ixgbe_q_vector, napi);
- struct ixgbe_adapter *adapter = q_vector->adapter;
- struct ixgbe_ring *rx_ring = NULL;
- int work_done = 0;
- long r_idx;
+ struct ixgbe_adapter *adapter = data;
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 eicr;
-#ifdef CONFIG_IXGBE_DCA
- if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
- ixgbe_update_dca(q_vector);
-#endif
+ /*
+ * Workaround for Silicon errata. Use clear-by-write instead
+ * of clear-by-read. Reading with EICS will return the
+ * interrupt causes without clearing, which later be done
+ * with the write to EICR.
+ */
+ eicr = IXGBE_READ_REG(hw, IXGBE_EICS);
+ IXGBE_WRITE_REG(hw, IXGBE_EICR, eicr);
- r_idx = find_first_bit(q_vector->rxr_idx, adapter->num_rx_queues);
- rx_ring = adapter->rx_ring[r_idx];
+ if (eicr & IXGBE_EICR_LSC)
+ ixgbe_check_lsc(adapter);
- ixgbe_clean_rx_irq(q_vector, rx_ring, &work_done, budget);
+ if (eicr & IXGBE_EICR_MAILBOX)
+ ixgbe_msg_task(adapter);
- /* If all Rx work done, exit the polling mode */
- if (work_done < budget) {
- napi_complete(napi);
- if (adapter->rx_itr_setting & 1)
- ixgbe_set_itr_msix(q_vector);
- if (!test_bit(__IXGBE_DOWN, &adapter->state))
- ixgbe_irq_enable_queues(adapter,
- ((u64)1 << q_vector->v_idx));
+ switch (hw->mac.type) {
+ case ixgbe_mac_82599EB:
+ case ixgbe_mac_X540:
+ if (eicr & IXGBE_EICR_ECC)
+ e_info(link, "Received unrecoverable ECC Err, please "
+ "reboot\n");
+ /* Handle Flow Director Full threshold interrupt */
+ if (eicr & IXGBE_EICR_FLOW_DIR) {
+ int reinit_count = 0;
+ int i;
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ struct ixgbe_ring *ring = adapter->tx_ring[i];
+ if (test_and_clear_bit(__IXGBE_TX_FDIR_INIT_DONE,
+ &ring->state))
+ reinit_count++;
+ }
+ if (reinit_count) {
+ /* no more flow director interrupts until after init */
+ IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_FLOW_DIR);
+ adapter->flags2 |= IXGBE_FLAG2_FDIR_REQUIRES_REINIT;
+ ixgbe_service_event_schedule(adapter);
+ }
+ }
+ ixgbe_check_sfp_event(adapter, eicr);
+ ixgbe_check_overtemp_event(adapter, eicr);
+ break;
+ default:
+ break;
}
- return work_done;
-}
-
-/**
- * ixgbe_clean_rxtx_many - msix (aka one shot) rx clean routine
- * @napi: napi struct with our devices info in it
- * @budget: amount of work driver is allowed to do this pass, in packets
- *
- * This function will clean more than one rx queue associated with a
- * q_vector.
- **/
-static int ixgbe_clean_rxtx_many(struct napi_struct *napi, int budget)
-{
- struct ixgbe_q_vector *q_vector =
- container_of(napi, struct ixgbe_q_vector, napi);
- struct ixgbe_adapter *adapter = q_vector->adapter;
- struct ixgbe_ring *ring = NULL;
- int work_done = 0, i;
- long r_idx;
- bool tx_clean_complete = true;
-
-#ifdef CONFIG_IXGBE_DCA
- if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
- ixgbe_update_dca(q_vector);
-#endif
-
- r_idx = find_first_bit(q_vector->txr_idx, adapter->num_tx_queues);
- for (i = 0; i < q_vector->txr_count; i++) {
- ring = adapter->tx_ring[r_idx];
- tx_clean_complete &= ixgbe_clean_tx_irq(q_vector, ring);
- r_idx = find_next_bit(q_vector->txr_idx, adapter->num_tx_queues,
- r_idx + 1);
- }
+ ixgbe_check_fan_failure(adapter, eicr);
- /* attempt to distribute budget to each queue fairly, but don't allow
- * the budget to go below 1 because we'll exit polling */
- budget /= (q_vector->rxr_count ?: 1);
- budget = max(budget, 1);
- r_idx = find_first_bit(q_vector->rxr_idx, adapter->num_rx_queues);
- for (i = 0; i < q_vector->rxr_count; i++) {
- ring = adapter->rx_ring[r_idx];
- ixgbe_clean_rx_irq(q_vector, ring, &work_done, budget);
- r_idx = find_next_bit(q_vector->rxr_idx, adapter->num_rx_queues,
- r_idx + 1);
- }
-
- r_idx = find_first_bit(q_vector->rxr_idx, adapter->num_rx_queues);
- ring = adapter->rx_ring[r_idx];
- /* If all Rx work done, exit the polling mode */
- if (work_done < budget) {
- napi_complete(napi);
- if (adapter->rx_itr_setting & 1)
- ixgbe_set_itr_msix(q_vector);
- if (!test_bit(__IXGBE_DOWN, &adapter->state))
- ixgbe_irq_enable_queues(adapter,
- ((u64)1 << q_vector->v_idx));
- return 0;
- }
+ /* re-enable the original interrupt state, no lsc, no queues */
+ if (!test_bit(__IXGBE_DOWN, &adapter->state))
+ ixgbe_irq_enable(adapter, false, false);
- return work_done;
+ return IRQ_HANDLED;
}
-/**
- * ixgbe_clean_txonly - msix (aka one shot) tx clean routine
- * @napi: napi struct with our devices info in it
- * @budget: amount of work driver is allowed to do this pass, in packets
- *
- * This function is optimized for cleaning one queue only on a single
- * q_vector!!!
- **/
-static int ixgbe_clean_txonly(struct napi_struct *napi, int budget)
+static irqreturn_t ixgbe_msix_clean_rings(int irq, void *data)
{
- struct ixgbe_q_vector *q_vector =
- container_of(napi, struct ixgbe_q_vector, napi);
- struct ixgbe_adapter *adapter = q_vector->adapter;
- struct ixgbe_ring *tx_ring = NULL;
- int work_done = 0;
- long r_idx;
-
-#ifdef CONFIG_IXGBE_DCA
- if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
- ixgbe_update_dca(q_vector);
-#endif
+ struct ixgbe_q_vector *q_vector = data;
- r_idx = find_first_bit(q_vector->txr_idx, adapter->num_tx_queues);
- tx_ring = adapter->tx_ring[r_idx];
+ /* EIAM disabled interrupts (on this vector) for us */
- if (!ixgbe_clean_tx_irq(q_vector, tx_ring))
- work_done = budget;
+ if (q_vector->rx.ring || q_vector->tx.ring)
+ napi_schedule(&q_vector->napi);
- /* If all Tx work done, exit the polling mode */
- if (work_done < budget) {
- napi_complete(napi);
- if (adapter->tx_itr_setting & 1)
- ixgbe_set_itr_msix(q_vector);
- if (!test_bit(__IXGBE_DOWN, &adapter->state))
- ixgbe_irq_enable_queues(adapter,
- ((u64)1 << q_vector->v_idx));
- }
-
- return work_done;
+ return IRQ_HANDLED;
}
static inline void map_vector_to_rxq(struct ixgbe_adapter *a, int v_idx,
@@ -2296,9 +2000,10 @@ static inline void map_vector_to_rxq(struct ixgbe_adapter *a, int v_idx,
struct ixgbe_q_vector *q_vector = a->q_vector[v_idx];
struct ixgbe_ring *rx_ring = a->rx_ring[r_idx];
- set_bit(r_idx, q_vector->rxr_idx);
- q_vector->rxr_count++;
rx_ring->q_vector = q_vector;
+ rx_ring->next = q_vector->rx.ring;
+ q_vector->rx.ring = rx_ring;
+ q_vector->rx.count++;
}
static inline void map_vector_to_txq(struct ixgbe_adapter *a, int v_idx,
@@ -2307,9 +2012,11 @@ static inline void map_vector_to_txq(struct ixgbe_adapter *a, int v_idx,
struct ixgbe_q_vector *q_vector = a->q_vector[v_idx];
struct ixgbe_ring *tx_ring = a->tx_ring[t_idx];
- set_bit(t_idx, q_vector->txr_idx);
- q_vector->txr_count++;
tx_ring->q_vector = q_vector;
+ tx_ring->next = q_vector->tx.ring;
+ q_vector->tx.ring = tx_ring;
+ q_vector->tx.count++;
+ q_vector->tx.work_limit = a->tx_work_limit;
}
/**
@@ -2322,59 +2029,41 @@ static inline void map_vector_to_txq(struct ixgbe_adapter *a, int v_idx,
* group the rings as "efficiently" as possible. You would add new
* mapping configurations in here.
**/
-static int ixgbe_map_rings_to_vectors(struct ixgbe_adapter *adapter)
+static void ixgbe_map_rings_to_vectors(struct ixgbe_adapter *adapter)
{
- int q_vectors;
+ int q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ int rxr_remaining = adapter->num_rx_queues, rxr_idx = 0;
+ int txr_remaining = adapter->num_tx_queues, txr_idx = 0;
int v_start = 0;
- int rxr_idx = 0, txr_idx = 0;
- int rxr_remaining = adapter->num_rx_queues;
- int txr_remaining = adapter->num_tx_queues;
- int i, j;
- int rqpv, tqpv;
- int err = 0;
- /* No mapping required if MSI-X is disabled. */
+ /* only one q_vector if MSI-X is disabled. */
if (!(adapter->flags & IXGBE_FLAG_MSIX_ENABLED))
- goto out;
-
- q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ q_vectors = 1;
/*
- * The ideal configuration...
- * We have enough vectors to map one per queue.
+ * If we don't have enough vectors for a 1-to-1 mapping, we'll have to
+ * group them so there are multiple queues per vector.
+ *
+ * Re-adjusting *qpv takes care of the remainder.
*/
- if (q_vectors == adapter->num_rx_queues + adapter->num_tx_queues) {
- for (; rxr_idx < rxr_remaining; v_start++, rxr_idx++)
+ for (; v_start < q_vectors && rxr_remaining; v_start++) {
+ int rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors - v_start);
+ for (; rqpv; rqpv--, rxr_idx++, rxr_remaining--)
map_vector_to_rxq(adapter, v_start, rxr_idx);
-
- for (; txr_idx < txr_remaining; v_start++, txr_idx++)
- map_vector_to_txq(adapter, v_start, txr_idx);
-
- goto out;
}
/*
- * If we don't have enough vectors for a 1-to-1
- * mapping, we'll have to group them so there are
- * multiple queues per vector.
+ * If there are not enough q_vectors for each ring to have it's own
+ * vector then we must pair up Rx/Tx on a each vector
*/
- /* Re-adjusting *qpv takes care of the remainder. */
- for (i = v_start; i < q_vectors; i++) {
- rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors - i);
- for (j = 0; j < rqpv; j++) {
- map_vector_to_rxq(adapter, i, rxr_idx);
- rxr_idx++;
- rxr_remaining--;
- }
- tqpv = DIV_ROUND_UP(txr_remaining, q_vectors - i);
- for (j = 0; j < tqpv; j++) {
- map_vector_to_txq(adapter, i, txr_idx);
- txr_idx++;
- txr_remaining--;
- }
+ if ((v_start + txr_remaining) > q_vectors)
+ v_start = 0;
+
+ for (; v_start < q_vectors && txr_remaining; v_start++) {
+ int tqpv = DIV_ROUND_UP(txr_remaining, q_vectors - v_start);
+ for (; tqpv; tqpv--, txr_idx++, txr_remaining--)
+ map_vector_to_txq(adapter, v_start, txr_idx);
}
-out:
- return err;
}
/**
@@ -2387,53 +2076,45 @@ out:
static int ixgbe_request_msix_irqs(struct ixgbe_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
- irqreturn_t (*handler)(int, void *);
- int i, vector, q_vectors, err;
+ int q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ int vector, err;
int ri = 0, ti = 0;
- /* Decrement for Other and TCP Timer vectors */
- q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
-
- err = ixgbe_map_rings_to_vectors(adapter);
- if (err)
- return err;
-
-#define SET_HANDLER(_v) (((_v)->rxr_count && (_v)->txr_count) \
- ? &ixgbe_msix_clean_many : \
- (_v)->rxr_count ? &ixgbe_msix_clean_rx : \
- (_v)->txr_count ? &ixgbe_msix_clean_tx : \
- NULL)
for (vector = 0; vector < q_vectors; vector++) {
struct ixgbe_q_vector *q_vector = adapter->q_vector[vector];
- handler = SET_HANDLER(q_vector);
+ struct msix_entry *entry = &adapter->msix_entries[vector];
- if (handler == &ixgbe_msix_clean_rx) {
+ if (q_vector->tx.ring && q_vector->rx.ring) {
snprintf(q_vector->name, sizeof(q_vector->name) - 1,
- "%s-%s-%d", netdev->name, "rx", ri++);
- } else if (handler == &ixgbe_msix_clean_tx) {
+ "%s-%s-%d", netdev->name, "TxRx", ri++);
+ ti++;
+ } else if (q_vector->rx.ring) {
snprintf(q_vector->name, sizeof(q_vector->name) - 1,
- "%s-%s-%d", netdev->name, "tx", ti++);
- } else if (handler == &ixgbe_msix_clean_many) {
+ "%s-%s-%d", netdev->name, "rx", ri++);
+ } else if (q_vector->tx.ring) {
snprintf(q_vector->name, sizeof(q_vector->name) - 1,
- "%s-%s-%d", netdev->name, "TxRx", ri++);
- ti++;
+ "%s-%s-%d", netdev->name, "tx", ti++);
} else {
/* skip this unused q_vector */
continue;
}
- err = request_irq(adapter->msix_entries[vector].vector,
- handler, 0, q_vector->name,
- q_vector);
+ err = request_irq(entry->vector, &ixgbe_msix_clean_rings, 0,
+ q_vector->name, q_vector);
if (err) {
e_err(probe, "request_irq failed for MSIX interrupt "
"Error: %d\n", err);
goto free_queue_irqs;
}
+ /* If Flow Director is enabled, set interrupt affinity */
+ if (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) {
+ /* assign the mask for this irq */
+ irq_set_affinity_hint(entry->vector,
+ q_vector->affinity_mask);
+ }
}
- sprintf(adapter->lsc_int_name, "%s:lsc", netdev->name);
err = request_irq(adapter->msix_entries[vector].vector,
- ixgbe_msix_lsc, 0, adapter->lsc_int_name, netdev);
+ ixgbe_msix_other, 0, netdev->name, adapter);
if (err) {
e_err(probe, "request_irq for msix_lsc failed: %d\n", err);
goto free_queue_irqs;
@@ -2442,9 +2123,13 @@ static int ixgbe_request_msix_irqs(struct ixgbe_adapter *adapter)
return 0;
free_queue_irqs:
- for (i = vector - 1; i >= 0; i--)
- free_irq(adapter->msix_entries[--vector].vector,
- adapter->q_vector[i]);
+ while (vector) {
+ vector--;
+ irq_set_affinity_hint(adapter->msix_entries[vector].vector,
+ NULL);
+ free_irq(adapter->msix_entries[vector].vector,
+ adapter->q_vector[vector]);
+ }
adapter->flags &= ~IXGBE_FLAG_MSIX_ENABLED;
pci_disable_msix(adapter->pdev);
kfree(adapter->msix_entries);
@@ -2452,93 +2137,6 @@ free_queue_irqs:
return err;
}
-static void ixgbe_set_itr(struct ixgbe_adapter *adapter)
-{
- struct ixgbe_q_vector *q_vector = adapter->q_vector[0];
- struct ixgbe_ring *rx_ring = adapter->rx_ring[0];
- struct ixgbe_ring *tx_ring = adapter->tx_ring[0];
- u32 new_itr = q_vector->eitr;
- u8 current_itr;
-
- q_vector->tx_itr = ixgbe_update_itr(adapter, new_itr,
- q_vector->tx_itr,
- tx_ring->total_packets,
- tx_ring->total_bytes);
- q_vector->rx_itr = ixgbe_update_itr(adapter, new_itr,
- q_vector->rx_itr,
- rx_ring->total_packets,
- rx_ring->total_bytes);
-
- current_itr = max(q_vector->rx_itr, q_vector->tx_itr);
-
- switch (current_itr) {
- /* counts and packets in update_itr are dependent on these numbers */
- case lowest_latency:
- new_itr = 100000;
- break;
- case low_latency:
- new_itr = 20000; /* aka hwitr = ~200 */
- break;
- case bulk_latency:
- new_itr = 8000;
- break;
- default:
- break;
- }
-
- if (new_itr != q_vector->eitr) {
- /* do an exponential smoothing */
- new_itr = ((q_vector->eitr * 9) + new_itr)/10;
-
- /* save the algorithm value here */
- q_vector->eitr = new_itr;
-
- ixgbe_write_eitr(q_vector);
- }
-}
-
-/**
- * ixgbe_irq_enable - Enable default interrupt generation settings
- * @adapter: board private structure
- **/
-static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter, bool queues,
- bool flush)
-{
- u32 mask;
-
- mask = (IXGBE_EIMS_ENABLE_MASK & ~IXGBE_EIMS_RTX_QUEUE);
- if (adapter->flags2 & IXGBE_FLAG2_TEMP_SENSOR_CAPABLE)
- mask |= IXGBE_EIMS_GPI_SDP0;
- if (adapter->flags & IXGBE_FLAG_FAN_FAIL_CAPABLE)
- mask |= IXGBE_EIMS_GPI_SDP1;
- switch (adapter->hw.mac.type) {
- case ixgbe_mac_82599EB:
- case ixgbe_mac_X540:
- mask |= IXGBE_EIMS_ECC;
- mask |= IXGBE_EIMS_GPI_SDP1;
- mask |= IXGBE_EIMS_GPI_SDP2;
- if (adapter->num_vfs)
- mask |= IXGBE_EIMS_MAILBOX;
- break;
- default:
- break;
- }
- if (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE ||
- adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE)
- mask |= IXGBE_EIMS_FLOW_DIR;
-
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, mask);
- if (queues)
- ixgbe_irq_enable_queues(adapter, ~0);
- if (flush)
- IXGBE_WRITE_FLUSH(&adapter->hw);
-
- if (adapter->num_vfs > 32) {
- u32 eitrsel = (1 << (adapter->num_vfs - 32)) - 1;
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_EITRSEL, eitrsel);
- }
-}
-
/**
* ixgbe_intr - legacy mode Interrupt Handler
* @irq: interrupt number
@@ -2546,8 +2144,7 @@ static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter, bool queues,
**/
static irqreturn_t ixgbe_intr(int irq, void *data)
{
- struct net_device *netdev = data;
- struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_adapter *adapter = data;
struct ixgbe_hw *hw = &adapter->hw;
struct ixgbe_q_vector *q_vector = adapter->q_vector[0];
u32 eicr;
@@ -2580,14 +2177,12 @@ static irqreturn_t ixgbe_intr(int irq, void *data)
switch (hw->mac.type) {
case ixgbe_mac_82599EB:
ixgbe_check_sfp_event(adapter, eicr);
- if ((adapter->flags2 & IXGBE_FLAG2_TEMP_SENSOR_CAPABLE) &&
- ((eicr & IXGBE_EICR_GPI_SDP0) || (eicr & IXGBE_EICR_LSC))) {
- if (!test_bit(__IXGBE_DOWN, &adapter->state)) {
- adapter->interrupt_event = eicr;
- adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_EVENT;
- ixgbe_service_event_schedule(adapter);
- }
- }
+ /* Fall through */
+ case ixgbe_mac_X540:
+ if (eicr & IXGBE_EICR_ECC)
+ e_info(link, "Received unrecoverable ECC err, please "
+ "reboot\n");
+ ixgbe_check_overtemp_event(adapter, eicr);
break;
default:
break;
@@ -2596,10 +2191,6 @@ static irqreturn_t ixgbe_intr(int irq, void *data)
ixgbe_check_fan_failure(adapter, eicr);
if (napi_schedule_prep(&(q_vector->napi))) {
- adapter->tx_ring[0]->total_packets = 0;
- adapter->tx_ring[0]->total_bytes = 0;
- adapter->rx_ring[0]->total_packets = 0;
- adapter->rx_ring[0]->total_bytes = 0;
/* would disable interrupts here but EIAM disabled it */
__napi_schedule(&(q_vector->napi));
}
@@ -2617,14 +2208,26 @@ static irqreturn_t ixgbe_intr(int irq, void *data)
static inline void ixgbe_reset_q_vectors(struct ixgbe_adapter *adapter)
{
- int i, q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ int q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ int i;
+
+ /* legacy and MSI only use one vector */
+ if (!(adapter->flags & IXGBE_FLAG_MSIX_ENABLED))
+ q_vectors = 1;
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ adapter->rx_ring[i]->q_vector = NULL;
+ adapter->rx_ring[i]->next = NULL;
+ }
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ adapter->tx_ring[i]->q_vector = NULL;
+ adapter->tx_ring[i]->next = NULL;
+ }
for (i = 0; i < q_vectors; i++) {
struct ixgbe_q_vector *q_vector = adapter->q_vector[i];
- bitmap_zero(q_vector->rxr_idx, MAX_RX_QUEUES);
- bitmap_zero(q_vector->txr_idx, MAX_TX_QUEUES);
- q_vector->rxr_count = 0;
- q_vector->txr_count = 0;
+ memset(&q_vector->rx, 0, sizeof(struct ixgbe_ring_container));
+ memset(&q_vector->tx, 0, sizeof(struct ixgbe_ring_container));
}
}
@@ -2640,49 +2243,57 @@ static int ixgbe_request_irq(struct ixgbe_adapter *adapter)
struct net_device *netdev = adapter->netdev;
int err;
- if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
+ /* map all of the rings to the q_vectors */
+ ixgbe_map_rings_to_vectors(adapter);
+
+ if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED)
err = ixgbe_request_msix_irqs(adapter);
- } else if (adapter->flags & IXGBE_FLAG_MSI_ENABLED) {
+ else if (adapter->flags & IXGBE_FLAG_MSI_ENABLED)
err = request_irq(adapter->pdev->irq, ixgbe_intr, 0,
- netdev->name, netdev);
- } else {
+ netdev->name, adapter);
+ else
err = request_irq(adapter->pdev->irq, ixgbe_intr, IRQF_SHARED,
- netdev->name, netdev);
- }
+ netdev->name, adapter);
- if (err)
+ if (err) {
e_err(probe, "request_irq failed, Error %d\n", err);
+ /* place q_vectors and rings back into a known good state */
+ ixgbe_reset_q_vectors(adapter);
+ }
+
return err;
}
static void ixgbe_free_irq(struct ixgbe_adapter *adapter)
{
- struct net_device *netdev = adapter->netdev;
-
if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
int i, q_vectors;
q_vectors = adapter->num_msix_vectors;
-
i = q_vectors - 1;
- free_irq(adapter->msix_entries[i].vector, netdev);
-
+ free_irq(adapter->msix_entries[i].vector, adapter);
i--;
+
for (; i >= 0; i--) {
/* free only the irqs that were actually requested */
- if (!adapter->q_vector[i]->rxr_count &&
- !adapter->q_vector[i]->txr_count)
+ if (!adapter->q_vector[i]->rx.ring &&
+ !adapter->q_vector[i]->tx.ring)
continue;
+ /* clear the affinity_mask in the IRQ descriptor */
+ irq_set_affinity_hint(adapter->msix_entries[i].vector,
+ NULL);
+
free_irq(adapter->msix_entries[i].vector,
adapter->q_vector[i]);
}
-
- ixgbe_reset_q_vectors(adapter);
} else {
- free_irq(adapter->pdev->irq, netdev);
+ free_irq(adapter->pdev->irq, adapter);
}
+
+ /* clear q_vector state information */
+ ixgbe_reset_q_vectors(adapter);
}
/**
@@ -2700,8 +2311,6 @@ static inline void ixgbe_irq_disable(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, 0xFFFF0000);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC_EX(0), ~0);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC_EX(1), ~0);
- if (adapter->num_vfs > 32)
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_EITRSEL, 0);
break;
default:
break;
@@ -2722,17 +2331,19 @@ static inline void ixgbe_irq_disable(struct ixgbe_adapter *adapter)
**/
static void ixgbe_configure_msi_and_legacy(struct ixgbe_adapter *adapter)
{
- struct ixgbe_hw *hw = &adapter->hw;
+ struct ixgbe_q_vector *q_vector = adapter->q_vector[0];
- IXGBE_WRITE_REG(hw, IXGBE_EITR(0),
- EITR_INTS_PER_SEC_TO_REG(adapter->rx_eitr_param));
+ /* rx/tx vector */
+ if (adapter->rx_itr_setting == 1)
+ q_vector->itr = IXGBE_20K_ITR;
+ else
+ q_vector->itr = adapter->rx_itr_setting;
+
+ ixgbe_write_eitr(q_vector);
ixgbe_set_ivar(adapter, 0, 0, 0);
ixgbe_set_ivar(adapter, 1, 0, 0);
- map_vector_to_rxq(adapter, 0, 0);
- map_vector_to_txq(adapter, 0, 0);
-
e_info(hw, "Legacy interrupt IVAR setup done\n");
}
@@ -2749,13 +2360,11 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter,
struct ixgbe_hw *hw = &adapter->hw;
u64 tdba = ring->dma;
int wait_loop = 10;
- u32 txdctl;
+ u32 txdctl = IXGBE_TXDCTL_ENABLE;
u8 reg_idx = ring->reg_idx;
/* disable queue to avoid issues while updating state */
- txdctl = IXGBE_READ_REG(hw, IXGBE_TXDCTL(reg_idx));
- IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx),
- txdctl & ~IXGBE_TXDCTL_ENABLE);
+ IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), 0);
IXGBE_WRITE_FLUSH(hw);
IXGBE_WRITE_REG(hw, IXGBE_TDBAL(reg_idx),
@@ -2767,18 +2376,22 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter,
IXGBE_WRITE_REG(hw, IXGBE_TDT(reg_idx), 0);
ring->tail = hw->hw_addr + IXGBE_TDT(reg_idx);
- /* configure fetching thresholds */
- if (adapter->rx_itr_setting == 0) {
- /* cannot set wthresh when itr==0 */
- txdctl &= ~0x007F0000;
- } else {
- /* enable WTHRESH=8 descriptors, to encourage burst writeback */
- txdctl |= (8 << 16);
- }
- if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
- /* PThresh workaround for Tx hang with DFP enabled. */
- txdctl |= 32;
- }
+ /*
+ * set WTHRESH to encourage burst writeback, it should not be set
+ * higher than 1 when ITR is 0 as it could cause false TX hangs
+ *
+ * In order to avoid issues WTHRESH + PTHRESH should always be equal
+ * to or less than the number of on chip descriptors, which is
+ * currently 40.
+ */
+ if (!adapter->tx_itr_setting || !adapter->rx_itr_setting)
+ txdctl |= (1 << 16); /* WTHRESH = 1 */
+ else
+ txdctl |= (8 << 16); /* WTHRESH = 8 */
+
+ /* PTHRESH=32 is needed to avoid a Tx hang with DFP enabled. */
+ txdctl |= (1 << 8) | /* HTHRESH = 1 */
+ 32; /* PTHRESH = 32 */
/* reinitialize flowdirector state */
if ((adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) &&
@@ -2793,7 +2406,6 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter,
clear_bit(__IXGBE_HANG_CHECK_ARMED, &ring->state);
/* enable queue */
- txdctl |= IXGBE_TXDCTL_ENABLE;
IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl);
/* TXDCTL.EN will return 0 on 82598 if link is down, so skip it */
@@ -2814,7 +2426,8 @@ static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
u32 rttdcs;
- u32 mask;
+ u32 reg;
+ u8 tcs = netdev_get_num_tc(adapter->netdev);
if (hw->mac.type == ixgbe_mac_82598EB)
return;
@@ -2825,22 +2438,27 @@ static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(hw, IXGBE_RTTDCS, rttdcs);
/* set transmit pool layout */
- mask = (IXGBE_FLAG_SRIOV_ENABLED | IXGBE_FLAG_DCB_ENABLED);
- switch (adapter->flags & mask) {
-
+ switch (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) {
case (IXGBE_FLAG_SRIOV_ENABLED):
IXGBE_WRITE_REG(hw, IXGBE_MTQC,
(IXGBE_MTQC_VT_ENA | IXGBE_MTQC_64VF));
break;
+ default:
+ if (!tcs)
+ reg = IXGBE_MTQC_64Q_1PB;
+ else if (tcs <= 4)
+ reg = IXGBE_MTQC_RT_ENA | IXGBE_MTQC_4TC_4TQ;
+ else
+ reg = IXGBE_MTQC_RT_ENA | IXGBE_MTQC_8TC_8TQ;
- case (IXGBE_FLAG_DCB_ENABLED):
- /* We enable 8 traffic classes, DCB only */
- IXGBE_WRITE_REG(hw, IXGBE_MTQC,
- (IXGBE_MTQC_RT_ENA | IXGBE_MTQC_8TC_8TQ));
- break;
+ IXGBE_WRITE_REG(hw, IXGBE_MTQC, reg);
- default:
- IXGBE_WRITE_REG(hw, IXGBE_MTQC, IXGBE_MTQC_64Q_1PB);
+ /* Enable Security TX Buffer IFG for multiple pb */
+ if (tcs) {
+ reg = IXGBE_READ_REG(hw, IXGBE_SECTXMINIFG);
+ reg |= IXGBE_SECTX_DCB;
+ IXGBE_WRITE_REG(hw, IXGBE_SECTXMINIFG, reg);
+ }
break;
}
@@ -2931,7 +2549,11 @@ static void ixgbe_setup_mrqc(struct ixgbe_adapter *adapter)
u32 mrqc = 0, reta = 0;
u32 rxcsum;
int i, j;
- int mask;
+ u8 tcs = netdev_get_num_tc(adapter->netdev);
+ int maxq = adapter->ring_feature[RING_F_RSS].indices;
+
+ if (tcs)
+ maxq = min(maxq, adapter->num_tx_queues / tcs);
/* Fill out hash function seeds */
for (i = 0; i < 10; i++)
@@ -2939,7 +2561,7 @@ static void ixgbe_setup_mrqc(struct ixgbe_adapter *adapter)
/* Fill out redirection table */
for (i = 0, j = 0; i < 128; i++, j++) {
- if (j == adapter->ring_feature[RING_F_RSS].indices)
+ if (j == maxq)
j = 0;
/* reta = 4-byte sliding window of
* 0x00..(indices-1)(indices-1)00..etc. */
@@ -2953,33 +2575,28 @@ static void ixgbe_setup_mrqc(struct ixgbe_adapter *adapter)
rxcsum |= IXGBE_RXCSUM_PCSD;
IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum);
- if (adapter->hw.mac.type == ixgbe_mac_82598EB)
- mask = adapter->flags & IXGBE_FLAG_RSS_ENABLED;
- else
- mask = adapter->flags & (IXGBE_FLAG_RSS_ENABLED
-#ifdef CONFIG_IXGBE_DCB
- | IXGBE_FLAG_DCB_ENABLED
-#endif
- | IXGBE_FLAG_SRIOV_ENABLED
- );
-
- switch (mask) {
-#ifdef CONFIG_IXGBE_DCB
- case (IXGBE_FLAG_DCB_ENABLED | IXGBE_FLAG_RSS_ENABLED):
- mrqc = IXGBE_MRQC_RTRSS8TCEN;
- break;
- case (IXGBE_FLAG_DCB_ENABLED):
- mrqc = IXGBE_MRQC_RT8TCEN;
- break;
-#endif /* CONFIG_IXGBE_DCB */
- case (IXGBE_FLAG_RSS_ENABLED):
+ if (adapter->hw.mac.type == ixgbe_mac_82598EB &&
+ (adapter->flags & IXGBE_FLAG_RSS_ENABLED)) {
mrqc = IXGBE_MRQC_RSSEN;
- break;
- case (IXGBE_FLAG_SRIOV_ENABLED):
- mrqc = IXGBE_MRQC_VMDQEN;
- break;
- default:
- break;
+ } else {
+ int mask = adapter->flags & (IXGBE_FLAG_RSS_ENABLED
+ | IXGBE_FLAG_SRIOV_ENABLED);
+
+ switch (mask) {
+ case (IXGBE_FLAG_RSS_ENABLED):
+ if (!tcs)
+ mrqc = IXGBE_MRQC_RSSEN;
+ else if (tcs <= 4)
+ mrqc = IXGBE_MRQC_RTRSS4TCEN;
+ else
+ mrqc = IXGBE_MRQC_RTRSS8TCEN;
+ break;
+ case (IXGBE_FLAG_SRIOV_ENABLED):
+ mrqc = IXGBE_MRQC_VMDQEN;
+ break;
+ default:
+ break;
+ }
}
/* Perform hash on these packet types */
@@ -2992,28 +2609,11 @@ static void ixgbe_setup_mrqc(struct ixgbe_adapter *adapter)
}
/**
- * ixgbe_clear_rscctl - disable RSC for the indicated ring
- * @adapter: address of board private structure
- * @ring: structure containing ring specific data
- **/
-void ixgbe_clear_rscctl(struct ixgbe_adapter *adapter,
- struct ixgbe_ring *ring)
-{
- struct ixgbe_hw *hw = &adapter->hw;
- u32 rscctrl;
- u8 reg_idx = ring->reg_idx;
-
- rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(reg_idx));
- rscctrl &= ~IXGBE_RSCCTL_RSCEN;
- IXGBE_WRITE_REG(hw, IXGBE_RSCCTL(reg_idx), rscctrl);
-}
-
-/**
* ixgbe_configure_rscctl - enable RSC for the indicated ring
* @adapter: address of board private structure
* @index: index of ring to set
**/
-void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter,
+static void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter,
struct ixgbe_ring *ring)
{
struct ixgbe_hw *hw = &adapter->hw;
@@ -3043,9 +2643,9 @@ void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter,
rscctrl |= IXGBE_RSCCTL_MAXDESC_1;
#endif
} else {
- if (rx_buf_len < IXGBE_RXBUFFER_4096)
+ if (rx_buf_len < IXGBE_RXBUFFER_4K)
rscctrl |= IXGBE_RSCCTL_MAXDESC_16;
- else if (rx_buf_len < IXGBE_RXBUFFER_8192)
+ else if (rx_buf_len < IXGBE_RXBUFFER_8K)
rscctrl |= IXGBE_RSCCTL_MAXDESC_8;
else
rscctrl |= IXGBE_RSCCTL_MAXDESC_4;
@@ -3183,7 +2783,7 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter,
IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl);
ixgbe_rx_desc_queue_enable(adapter, ring);
- ixgbe_alloc_rx_buffers(ring, IXGBE_DESC_UNUSED(ring));
+ ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring));
}
static void ixgbe_setup_psrtype(struct ixgbe_adapter *adapter)
@@ -3216,6 +2816,7 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter)
u32 vt_reg_bits;
u32 reg_offset, vf_shift;
u32 vmdctl;
+ int i;
if (!(adapter->flags & IXGBE_FLAG_SRIOV_ENABLED))
return;
@@ -3251,9 +2852,13 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN);
/* Enable MAC Anti-Spoofing */
hw->mac.ops.set_mac_anti_spoofing(hw,
- (adapter->antispoofing_enabled =
- (adapter->num_vfs != 0)),
+ (adapter->num_vfs != 0),
adapter->num_vfs);
+ /* For VFs that have spoof checking turned off */
+ for (i = 0; i < adapter->num_vfs; i++) {
+ if (!adapter->vfinfo[i].spoofchk_enabled)
+ ixgbe_ndo_set_vf_spoofchk(adapter->netdev, i, false);
+ }
}
static void ixgbe_set_rx_buffer_len(struct ixgbe_adapter *adapter)
@@ -3278,17 +2883,6 @@ static void ixgbe_set_rx_buffer_len(struct ixgbe_adapter *adapter)
if (hw->mac.type == ixgbe_mac_82599EB)
adapter->flags &= ~IXGBE_FLAG_RX_PS_ENABLED;
- /* Set the RX buffer length according to the mode */
- if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
- rx_buf_len = IXGBE_RX_HDR_SIZE;
- } else {
- if (!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) &&
- (netdev->mtu <= ETH_DATA_LEN))
- rx_buf_len = MAXIMUM_ETHERNET_VLAN_SIZE;
- else
- rx_buf_len = ALIGN(max_frame + VLAN_HLEN, 1024);
- }
-
#ifdef IXGBE_FCOE
/* adjust max frame to be able to do baby jumbo for FCoE */
if ((adapter->flags & IXGBE_FLAG_FCOE_ENABLED) &&
@@ -3304,6 +2898,30 @@ static void ixgbe_set_rx_buffer_len(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(hw, IXGBE_MHADD, mhadd);
}
+ /* MHADD will allow an extra 4 bytes past for vlan tagged frames */
+ max_frame += VLAN_HLEN;
+
+ /* Set the RX buffer length according to the mode */
+ if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+ rx_buf_len = IXGBE_RX_HDR_SIZE;
+ } else {
+ if (!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) &&
+ (netdev->mtu <= ETH_DATA_LEN))
+ rx_buf_len = MAXIMUM_ETHERNET_VLAN_SIZE;
+ /*
+ * Make best use of allocation by using all but 1K of a
+ * power of 2 allocation that will be used for skb->head.
+ */
+ else if (max_frame <= IXGBE_RXBUFFER_3K)
+ rx_buf_len = IXGBE_RXBUFFER_3K;
+ else if (max_frame <= IXGBE_RXBUFFER_7K)
+ rx_buf_len = IXGBE_RXBUFFER_7K;
+ else if (max_frame <= IXGBE_RXBUFFER_15K)
+ rx_buf_len = IXGBE_RXBUFFER_15K;
+ else
+ rx_buf_len = IXGBE_MAX_RXBUFFER;
+ }
+
hlreg0 = IXGBE_READ_REG(hw, IXGBE_HLREG0);
/* set jumbo enable since MHADD.MFS is keeping size locked at max_frame */
hlreg0 |= IXGBE_HLREG0_JUMBOEN;
@@ -3677,19 +3295,8 @@ static void ixgbe_napi_enable_all(struct ixgbe_adapter *adapter)
q_vectors = 1;
for (q_idx = 0; q_idx < q_vectors; q_idx++) {
- struct napi_struct *napi;
q_vector = adapter->q_vector[q_idx];
- napi = &q_vector->napi;
- if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
- if (!q_vector->rxr_count || !q_vector->txr_count) {
- if (q_vector->txr_count == 1)
- napi->poll = &ixgbe_clean_txonly;
- else if (q_vector->rxr_count == 1)
- napi->poll = &ixgbe_clean_rxonly;
- }
- }
-
- napi_enable(napi);
+ napi_enable(&q_vector->napi);
}
}
@@ -3738,26 +3345,25 @@ static void ixgbe_configure_dcb(struct ixgbe_adapter *adapter)
hw->mac.ops.set_vfta(&adapter->hw, 0, 0, true);
- /* reconfigure the hardware */
- if (adapter->dcbx_cap & (DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_CEE)) {
-#ifdef CONFIG_FCOE
- if (adapter->netdev->features & NETIF_F_FCOE_MTU)
- max_frame = max(max_frame, IXGBE_FCOE_JUMBO_FRAME_SIZE);
+#ifdef IXGBE_FCOE
+ if (adapter->netdev->features & NETIF_F_FCOE_MTU)
+ max_frame = max(max_frame, IXGBE_FCOE_JUMBO_FRAME_SIZE);
#endif
+
+ /* reconfigure the hardware */
+ if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_CEE) {
ixgbe_dcb_calculate_tc_credits(hw, &adapter->dcb_cfg, max_frame,
DCB_TX_CONFIG);
ixgbe_dcb_calculate_tc_credits(hw, &adapter->dcb_cfg, max_frame,
DCB_RX_CONFIG);
ixgbe_dcb_hw_config(hw, &adapter->dcb_cfg);
- } else {
- struct net_device *dev = adapter->netdev;
-
- if (adapter->ixgbe_ieee_ets)
- dev->dcbnl_ops->ieee_setets(dev,
- adapter->ixgbe_ieee_ets);
- if (adapter->ixgbe_ieee_pfc)
- dev->dcbnl_ops->ieee_setpfc(dev,
- adapter->ixgbe_ieee_pfc);
+ } else if (adapter->ixgbe_ieee_ets && adapter->ixgbe_ieee_pfc) {
+ ixgbe_dcb_hw_ets(&adapter->hw,
+ adapter->ixgbe_ieee_ets,
+ max_frame);
+ ixgbe_dcb_hw_pfc_config(&adapter->hw,
+ adapter->ixgbe_ieee_pfc->pfc_en,
+ adapter->ixgbe_ieee_ets->prio_tc);
}
/* Enable RSS Hash per TC */
@@ -3777,19 +3383,176 @@ static void ixgbe_configure_dcb(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(hw, IXGBE_RQTC, reg);
}
}
+#endif
+
+/* Additional bittime to account for IXGBE framing */
+#define IXGBE_ETH_FRAMING 20
+
+/*
+ * ixgbe_hpbthresh - calculate high water mark for flow control
+ *
+ * @adapter: board private structure to calculate for
+ * @pb - packet buffer to calculate
+ */
+static int ixgbe_hpbthresh(struct ixgbe_adapter *adapter, int pb)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct net_device *dev = adapter->netdev;
+ int link, tc, kb, marker;
+ u32 dv_id, rx_pba;
+
+ /* Calculate max LAN frame size */
+ tc = link = dev->mtu + ETH_HLEN + ETH_FCS_LEN + IXGBE_ETH_FRAMING;
+
+#ifdef IXGBE_FCOE
+ /* FCoE traffic class uses FCOE jumbo frames */
+ if (dev->features & NETIF_F_FCOE_MTU) {
+ int fcoe_pb = 0;
+
+#ifdef CONFIG_IXGBE_DCB
+ fcoe_pb = netdev_get_prio_tc_map(dev, adapter->fcoe.up);
#endif
-static void ixgbe_configure(struct ixgbe_adapter *adapter)
+ if (fcoe_pb == pb && tc < IXGBE_FCOE_JUMBO_FRAME_SIZE)
+ tc = IXGBE_FCOE_JUMBO_FRAME_SIZE;
+ }
+#endif
+
+ /* Calculate delay value for device */
+ switch (hw->mac.type) {
+ case ixgbe_mac_X540:
+ dv_id = IXGBE_DV_X540(link, tc);
+ break;
+ default:
+ dv_id = IXGBE_DV(link, tc);
+ break;
+ }
+
+ /* Loopback switch introduces additional latency */
+ if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
+ dv_id += IXGBE_B2BT(tc);
+
+ /* Delay value is calculated in bit times convert to KB */
+ kb = IXGBE_BT2KB(dv_id);
+ rx_pba = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(pb)) >> 10;
+
+ marker = rx_pba - kb;
+
+ /* It is possible that the packet buffer is not large enough
+ * to provide required headroom. In this case throw an error
+ * to user and a do the best we can.
+ */
+ if (marker < 0) {
+ e_warn(drv, "Packet Buffer(%i) can not provide enough"
+ "headroom to support flow control."
+ "Decrease MTU or number of traffic classes\n", pb);
+ marker = tc + 1;
+ }
+
+ return marker;
+}
+
+/*
+ * ixgbe_lpbthresh - calculate low water mark for for flow control
+ *
+ * @adapter: board private structure to calculate for
+ * @pb - packet buffer to calculate
+ */
+static int ixgbe_lpbthresh(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct net_device *dev = adapter->netdev;
+ int tc;
+ u32 dv_id;
+
+ /* Calculate max LAN frame size */
+ tc = dev->mtu + ETH_HLEN + ETH_FCS_LEN;
+
+ /* Calculate delay value for device */
+ switch (hw->mac.type) {
+ case ixgbe_mac_X540:
+ dv_id = IXGBE_LOW_DV_X540(tc);
+ break;
+ default:
+ dv_id = IXGBE_LOW_DV(tc);
+ break;
+ }
+
+ /* Delay value is calculated in bit times convert to KB */
+ return IXGBE_BT2KB(dv_id);
+}
+
+/*
+ * ixgbe_pbthresh_setup - calculate and setup high low water marks
+ */
+static void ixgbe_pbthresh_setup(struct ixgbe_adapter *adapter)
{
- struct net_device *netdev = adapter->netdev;
struct ixgbe_hw *hw = &adapter->hw;
+ int num_tc = netdev_get_num_tc(adapter->netdev);
int i;
+ if (!num_tc)
+ num_tc = 1;
+
+ hw->fc.low_water = ixgbe_lpbthresh(adapter);
+
+ for (i = 0; i < num_tc; i++) {
+ hw->fc.high_water[i] = ixgbe_hpbthresh(adapter, i);
+
+ /* Low water marks must not be larger than high water marks */
+ if (hw->fc.low_water > hw->fc.high_water[i])
+ hw->fc.low_water = 0;
+ }
+}
+
+static void ixgbe_configure_pb(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ int hdrm;
+ u8 tc = netdev_get_num_tc(adapter->netdev);
+
+ if (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE ||
+ adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE)
+ hdrm = 32 << adapter->fdir_pballoc;
+ else
+ hdrm = 0;
+
+ hw->mac.ops.set_rxpba(hw, tc, hdrm, PBA_STRATEGY_EQUAL);
+ ixgbe_pbthresh_setup(adapter);
+}
+
+static void ixgbe_fdir_filter_restore(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct hlist_node *node, *node2;
+ struct ixgbe_fdir_filter *filter;
+
+ spin_lock(&adapter->fdir_perfect_lock);
+
+ if (!hlist_empty(&adapter->fdir_filter_list))
+ ixgbe_fdir_set_input_mask_82599(hw, &adapter->fdir_mask);
+
+ hlist_for_each_entry_safe(filter, node, node2,
+ &adapter->fdir_filter_list, fdir_node) {
+ ixgbe_fdir_write_perfect_filter_82599(hw,
+ &filter->filter,
+ filter->sw_idx,
+ (filter->action == IXGBE_FDIR_DROP_QUEUE) ?
+ IXGBE_FDIR_DROP_QUEUE :
+ adapter->rx_ring[filter->action]->reg_idx);
+ }
+
+ spin_unlock(&adapter->fdir_perfect_lock);
+}
+
+static void ixgbe_configure(struct ixgbe_adapter *adapter)
+{
+ ixgbe_configure_pb(adapter);
#ifdef CONFIG_IXGBE_DCB
ixgbe_configure_dcb(adapter);
#endif
- ixgbe_set_rx_mode(netdev);
+ ixgbe_set_rx_mode(adapter->netdev);
ixgbe_restore_vlan(adapter);
#ifdef IXGBE_FCOE
@@ -3798,13 +3561,14 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter)
#endif /* IXGBE_FCOE */
if (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) {
- for (i = 0; i < adapter->num_tx_queues; i++)
- adapter->tx_ring[i]->atr_sample_rate =
- adapter->atr_sample_rate;
- ixgbe_init_fdir_signature_82599(hw, adapter->fdir_pballoc);
+ ixgbe_init_fdir_signature_82599(&adapter->hw,
+ adapter->fdir_pballoc);
} else if (adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE) {
- ixgbe_init_fdir_perfect_82599(hw, adapter->fdir_pballoc);
+ ixgbe_init_fdir_perfect_82599(&adapter->hw,
+ adapter->fdir_pballoc);
+ ixgbe_fdir_filter_restore(adapter);
}
+
ixgbe_configure_virtualization(adapter);
ixgbe_configure_tx(adapter);
@@ -3823,6 +3587,9 @@ static inline bool ixgbe_is_sfp(struct ixgbe_hw *hw)
case ixgbe_phy_sfp_active_unknown:
case ixgbe_phy_sfp_ftl_active:
return true;
+ case ixgbe_phy_nl:
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ return true;
default:
return false;
}
@@ -3915,6 +3682,20 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter)
gpie |= IXGBE_GPIE_VTMODE_64;
}
+ /* Enable Thermal over heat sensor interrupt */
+ if (adapter->flags2 & IXGBE_FLAG2_TEMP_SENSOR_CAPABLE) {
+ switch (adapter->hw.mac.type) {
+ case ixgbe_mac_82599EB:
+ gpie |= IXGBE_SDP0_GPIEN;
+ break;
+ case ixgbe_mac_X540:
+ gpie |= IXGBE_EIMS_TS;
+ break;
+ default:
+ break;
+ }
+ }
+
/* Enable fan failure interrupt */
if (adapter->flags & IXGBE_FLAG_FAN_FAIL_CAPABLE)
gpie |= IXGBE_SDP1_GPIEN;
@@ -3927,7 +3708,7 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie);
}
-static int ixgbe_up_complete(struct ixgbe_adapter *adapter)
+static void ixgbe_up_complete(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
int err;
@@ -3986,8 +3767,6 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter)
ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT);
ctrl_ext |= IXGBE_CTRL_EXT_PFRSTD;
IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext);
-
- return 0;
}
void ixgbe_reinit_locked(struct ixgbe_adapter *adapter)
@@ -4011,12 +3790,12 @@ void ixgbe_reinit_locked(struct ixgbe_adapter *adapter)
clear_bit(__IXGBE_RESETTING, &adapter->state);
}
-int ixgbe_up(struct ixgbe_adapter *adapter)
+void ixgbe_up(struct ixgbe_adapter *adapter)
{
/* hardware has been reset, we need to reload some things */
ixgbe_configure(adapter);
- return ixgbe_up_complete(adapter);
+ ixgbe_up_complete(adapter);
}
void ixgbe_reset(struct ixgbe_adapter *adapter)
@@ -4180,13 +3959,29 @@ static void ixgbe_clean_all_tx_rings(struct ixgbe_adapter *adapter)
ixgbe_clean_tx_ring(adapter->tx_ring[i]);
}
+static void ixgbe_fdir_filter_exit(struct ixgbe_adapter *adapter)
+{
+ struct hlist_node *node, *node2;
+ struct ixgbe_fdir_filter *filter;
+
+ spin_lock(&adapter->fdir_perfect_lock);
+
+ hlist_for_each_entry_safe(filter, node, node2,
+ &adapter->fdir_filter_list, fdir_node) {
+ hlist_del(&filter->fdir_node);
+ kfree(filter);
+ }
+ adapter->fdir_filter_count = 0;
+
+ spin_unlock(&adapter->fdir_perfect_lock);
+}
+
void ixgbe_down(struct ixgbe_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
struct ixgbe_hw *hw = &adapter->hw;
u32 rxctrl;
int i;
- int num_q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
/* signal that we are down to the interrupt handler */
set_bit(__IXGBE_DOWN, &adapter->state);
@@ -4218,26 +4013,19 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
del_timer_sync(&adapter->service_timer);
- /* disable receive for all VFs and wait one second */
if (adapter->num_vfs) {
- /* ping all the active vfs to let them know we are going down */
- ixgbe_ping_all_vfs(adapter);
-
- /* Disable all VFTE/VFRE TX/RX */
- ixgbe_disable_tx_rx(adapter);
+ /* Clear EITR Select mapping */
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EITRSEL, 0);
/* Mark all the VFs as inactive */
for (i = 0 ; i < adapter->num_vfs; i++)
adapter->vfinfo[i].clear_to_send = 0;
- }
- /* Cleanup the affinity_hint CPU mask memory and callback */
- for (i = 0; i < num_q_vectors; i++) {
- struct ixgbe_q_vector *q_vector = adapter->q_vector[i];
- /* clear the affinity_mask in the IRQ descriptor */
- irq_set_affinity_hint(adapter->msix_entries[i]. vector, NULL);
- /* release the CPU mask memory */
- free_cpumask_var(q_vector->affinity_mask);
+ /* ping all the active vfs to let them know we are going down */
+ ixgbe_ping_all_vfs(adapter);
+
+ /* Disable all VFTE/VFRE TX/RX */
+ ixgbe_disable_tx_rx(adapter);
}
/* disable transmits in the hardware now that interrupts are off */
@@ -4289,28 +4077,41 @@ static int ixgbe_poll(struct napi_struct *napi, int budget)
struct ixgbe_q_vector *q_vector =
container_of(napi, struct ixgbe_q_vector, napi);
struct ixgbe_adapter *adapter = q_vector->adapter;
- int tx_clean_complete, work_done = 0;
+ struct ixgbe_ring *ring;
+ int per_ring_budget;
+ bool clean_complete = true;
#ifdef CONFIG_IXGBE_DCA
if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
ixgbe_update_dca(q_vector);
#endif
- tx_clean_complete = ixgbe_clean_tx_irq(q_vector, adapter->tx_ring[0]);
- ixgbe_clean_rx_irq(q_vector, adapter->rx_ring[0], &work_done, budget);
+ for (ring = q_vector->tx.ring; ring != NULL; ring = ring->next)
+ clean_complete &= !!ixgbe_clean_tx_irq(q_vector, ring);
- if (!tx_clean_complete)
- work_done = budget;
+ /* attempt to distribute budget to each queue fairly, but don't allow
+ * the budget to go below 1 because we'll exit polling */
+ if (q_vector->rx.count > 1)
+ per_ring_budget = max(budget/q_vector->rx.count, 1);
+ else
+ per_ring_budget = budget;
- /* If budget not fully consumed, exit the polling mode */
- if (work_done < budget) {
- napi_complete(napi);
- if (adapter->rx_itr_setting & 1)
- ixgbe_set_itr(adapter);
- if (!test_bit(__IXGBE_DOWN, &adapter->state))
- ixgbe_irq_enable_queues(adapter, IXGBE_EIMS_RTX_QUEUE);
- }
- return work_done;
+ for (ring = q_vector->rx.ring; ring != NULL; ring = ring->next)
+ clean_complete &= ixgbe_clean_rx_irq(q_vector, ring,
+ per_ring_budget);
+
+ /* If all work not completed, return budget and keep polling */
+ if (!clean_complete)
+ return budget;
+
+ /* all work done, exit the polling mode */
+ napi_complete(napi);
+ if (adapter->rx_itr_setting & 1)
+ ixgbe_set_itr(q_vector);
+ if (!test_bit(__IXGBE_DOWN, &adapter->state))
+ ixgbe_irq_enable_queues(adapter, ((u64)1 << q_vector->v_idx));
+
+ return 0;
}
/**
@@ -4369,15 +4170,13 @@ static inline bool ixgbe_set_fdir_queues(struct ixgbe_adapter *adapter)
f_fdir->mask = 0;
/* Flow Director must have RSS enabled */
- if (adapter->flags & IXGBE_FLAG_RSS_ENABLED &&
- ((adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE ||
- (adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE)))) {
+ if ((adapter->flags & IXGBE_FLAG_RSS_ENABLED) &&
+ (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE)) {
adapter->num_tx_queues = f_fdir->indices;
adapter->num_rx_queues = f_fdir->indices;
ret = true;
} else {
adapter->flags &= ~IXGBE_FLAG_FDIR_HASH_CAPABLE;
- adapter->flags &= ~IXGBE_FLAG_FDIR_PERFECT_CAPABLE;
}
return ret;
}
@@ -4400,69 +4199,71 @@ static inline bool ixgbe_set_fcoe_queues(struct ixgbe_adapter *adapter)
if (!(adapter->flags & IXGBE_FLAG_FCOE_ENABLED))
return false;
- if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
-#ifdef CONFIG_IXGBE_DCB
- int tc;
- struct net_device *dev = adapter->netdev;
-
- tc = netdev_get_prio_tc_map(dev, adapter->fcoe.up);
- f->indices = dev->tc_to_txq[tc].count;
- f->mask = dev->tc_to_txq[tc].offset;
-#endif
- } else {
- f->indices = min((int)num_online_cpus(), f->indices);
+ f->indices = min((int)num_online_cpus(), f->indices);
- adapter->num_rx_queues = 1;
- adapter->num_tx_queues = 1;
+ adapter->num_rx_queues = 1;
+ adapter->num_tx_queues = 1;
- if (adapter->flags & IXGBE_FLAG_RSS_ENABLED) {
- e_info(probe, "FCoE enabled with RSS\n");
- if ((adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) ||
- (adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE))
- ixgbe_set_fdir_queues(adapter);
- else
- ixgbe_set_rss_queues(adapter);
- }
- /* adding FCoE rx rings to the end */
- f->mask = adapter->num_rx_queues;
- adapter->num_rx_queues += f->indices;
- adapter->num_tx_queues += f->indices;
+ if (adapter->flags & IXGBE_FLAG_RSS_ENABLED) {
+ e_info(probe, "FCoE enabled with RSS\n");
+ if (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE)
+ ixgbe_set_fdir_queues(adapter);
+ else
+ ixgbe_set_rss_queues(adapter);
}
+ /* adding FCoE rx rings to the end */
+ f->mask = adapter->num_rx_queues;
+ adapter->num_rx_queues += f->indices;
+ adapter->num_tx_queues += f->indices;
+
return true;
}
#endif /* IXGBE_FCOE */
+/* Artificial max queue cap per traffic class in DCB mode */
+#define DCB_QUEUE_CAP 8
+
#ifdef CONFIG_IXGBE_DCB
static inline bool ixgbe_set_dcb_queues(struct ixgbe_adapter *adapter)
{
- bool ret = false;
- struct ixgbe_ring_feature *f = &adapter->ring_feature[RING_F_DCB];
- int i, q;
+ int per_tc_q, q, i, offset = 0;
+ struct net_device *dev = adapter->netdev;
+ int tcs = netdev_get_num_tc(dev);
+
+ if (!tcs)
+ return false;
- if (!(adapter->flags & IXGBE_FLAG_DCB_ENABLED))
- return ret;
+ /* Map queue offset and counts onto allocated tx queues */
+ per_tc_q = min(dev->num_tx_queues / tcs, (unsigned int)DCB_QUEUE_CAP);
+ q = min((int)num_online_cpus(), per_tc_q);
- f->indices = 0;
- for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
- q = min((int)num_online_cpus(), MAX_TRAFFIC_CLASS);
- f->indices += q;
+ for (i = 0; i < tcs; i++) {
+ netdev_set_tc_queue(dev, i, q, offset);
+ offset += q;
}
- f->mask = 0x7 << 3;
- adapter->num_rx_queues = f->indices;
- adapter->num_tx_queues = f->indices;
- ret = true;
+ adapter->num_tx_queues = q * tcs;
+ adapter->num_rx_queues = q * tcs;
#ifdef IXGBE_FCOE
- /* FCoE enabled queues require special configuration done through
- * configure_fcoe() and others. Here we map FCoE indices onto the
- * DCB queue pairs allowing FCoE to own configuration later.
+ /* FCoE enabled queues require special configuration indexed
+ * by feature specific indices and mask. Here we map FCoE
+ * indices onto the DCB queue pairs allowing FCoE to own
+ * configuration later.
*/
- ixgbe_set_fcoe_queues(adapter);
+ if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED) {
+ int tc;
+ struct ixgbe_ring_feature *f =
+ &adapter->ring_feature[RING_F_FCOE];
+
+ tc = netdev_get_prio_tc_map(dev, adapter->fcoe.up);
+ f->indices = dev->tc_to_txq[tc].count;
+ f->mask = dev->tc_to_txq[tc].offset;
+ }
#endif
- return ret;
+ return true;
}
#endif
@@ -4616,12 +4417,12 @@ static void ixgbe_get_first_reg_idx(struct ixgbe_adapter *adapter, u8 tc,
switch (hw->mac.type) {
case ixgbe_mac_82598EB:
- *tx = tc << 3;
- *rx = tc << 2;
+ *tx = tc << 2;
+ *rx = tc << 3;
break;
case ixgbe_mac_82599EB:
case ixgbe_mac_X540:
- if (num_tcs == 8) {
+ if (num_tcs > 4) {
if (tc < 3) {
*tx = tc << 5;
*rx = tc << 4;
@@ -4632,7 +4433,7 @@ static void ixgbe_get_first_reg_idx(struct ixgbe_adapter *adapter, u8 tc,
*tx = ((tc + 8) << 3);
*rx = tc << 4;
}
- } else if (num_tcs == 4) {
+ } else {
*rx = tc << 5;
switch (tc) {
case 0:
@@ -4657,55 +4458,6 @@ static void ixgbe_get_first_reg_idx(struct ixgbe_adapter *adapter, u8 tc,
}
}
-#define IXGBE_MAX_Q_PER_TC (IXGBE_MAX_DCB_INDICES / MAX_TRAFFIC_CLASS)
-
-/* ixgbe_setup_tc - routine to configure net_device for multiple traffic
- * classes.
- *
- * @netdev: net device to configure
- * @tc: number of traffic classes to enable
- */
-int ixgbe_setup_tc(struct net_device *dev, u8 tc)
-{
- int i;
- unsigned int q, offset = 0;
-
- if (!tc) {
- netdev_reset_tc(dev);
- } else {
- struct ixgbe_adapter *adapter = netdev_priv(dev);
-
- /* Hardware supports up to 8 traffic classes */
- if (tc > MAX_TRAFFIC_CLASS || netdev_set_num_tc(dev, tc))
- return -EINVAL;
-
- /* Partition Tx queues evenly amongst traffic classes */
- for (i = 0; i < tc; i++) {
- q = min((int)num_online_cpus(), IXGBE_MAX_Q_PER_TC);
- netdev_set_prio_tc_map(dev, i, i);
- netdev_set_tc_queue(dev, i, q, offset);
- offset += q;
- }
-
- /* This enables multiple traffic class support in the hardware
- * which defaults to strict priority transmission by default.
- * If traffic classes are already enabled perhaps through DCB
- * code path then existing configuration will be used.
- */
- if (!(adapter->flags & IXGBE_FLAG_DCB_ENABLED) &&
- dev->dcbnl_ops && dev->dcbnl_ops->setdcbx) {
- struct ieee_ets ets = {
- .prio_tc = {0, 1, 2, 3, 4, 5, 6, 7},
- };
- u8 mode = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
-
- dev->dcbnl_ops->setdcbx(dev, mode);
- dev->dcbnl_ops->ieee_setets(dev, &ets);
- }
- }
- return 0;
-}
-
/**
* ixgbe_cache_ring_dcb - Descriptor ring to register mapping for DCB
* @adapter: board private structure to initialize
@@ -4719,7 +4471,7 @@ static inline bool ixgbe_cache_ring_dcb(struct ixgbe_adapter *adapter)
int i, j, k;
u8 num_tcs = netdev_get_num_tc(dev);
- if (!(adapter->flags & IXGBE_FLAG_DCB_ENABLED))
+ if (!num_tcs)
return false;
for (i = 0, k = 0; i < num_tcs; i++) {
@@ -4751,9 +4503,8 @@ static inline bool ixgbe_cache_ring_fdir(struct ixgbe_adapter *adapter)
int i;
bool ret = false;
- if (adapter->flags & IXGBE_FLAG_RSS_ENABLED &&
- ((adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) ||
- (adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE))) {
+ if ((adapter->flags & IXGBE_FLAG_RSS_ENABLED) &&
+ (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE)) {
for (i = 0; i < adapter->num_rx_queues; i++)
adapter->rx_ring[i]->reg_idx = i;
for (i = 0; i < adapter->num_tx_queues; i++)
@@ -4782,8 +4533,7 @@ static inline bool ixgbe_cache_ring_fcoe(struct ixgbe_adapter *adapter)
return false;
if (adapter->flags & IXGBE_FLAG_RSS_ENABLED) {
- if ((adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) ||
- (adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE))
+ if (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE)
ixgbe_cache_ring_fdir(adapter);
else
ixgbe_cache_ring_rss(adapter);
@@ -4963,14 +4713,12 @@ static int ixgbe_set_interrupt_capability(struct ixgbe_adapter *adapter)
adapter->flags &= ~IXGBE_FLAG_DCB_ENABLED;
adapter->flags &= ~IXGBE_FLAG_RSS_ENABLED;
- if (adapter->flags & (IXGBE_FLAG_FDIR_HASH_CAPABLE |
- IXGBE_FLAG_FDIR_PERFECT_CAPABLE)) {
+ if (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) {
e_err(probe,
- "Flow Director is not supported while multiple "
+ "ATR is not supported while multiple "
"queues are disabled. Disabling Flow Director\n");
}
adapter->flags &= ~IXGBE_FLAG_FDIR_HASH_CAPABLE;
- adapter->flags &= ~IXGBE_FLAG_FDIR_PERFECT_CAPABLE;
adapter->atr_sample_rate = 0;
if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
ixgbe_disable_sriov(adapter);
@@ -5003,19 +4751,15 @@ out:
**/
static int ixgbe_alloc_q_vectors(struct ixgbe_adapter *adapter)
{
- int q_idx, num_q_vectors;
+ int v_idx, num_q_vectors;
struct ixgbe_q_vector *q_vector;
- int (*poll)(struct napi_struct *, int);
- if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
+ if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED)
num_q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
- poll = &ixgbe_clean_rxtx_many;
- } else {
+ else
num_q_vectors = 1;
- poll = &ixgbe_poll;
- }
- for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
+ for (v_idx = 0; v_idx < num_q_vectors; v_idx++) {
q_vector = kzalloc_node(sizeof(struct ixgbe_q_vector),
GFP_KERNEL, adapter->node);
if (!q_vector)
@@ -5023,25 +4767,29 @@ static int ixgbe_alloc_q_vectors(struct ixgbe_adapter *adapter)
GFP_KERNEL);
if (!q_vector)
goto err_out;
+
q_vector->adapter = adapter;
- if (q_vector->txr_count && !q_vector->rxr_count)
- q_vector->eitr = adapter->tx_eitr_param;
- else
- q_vector->eitr = adapter->rx_eitr_param;
- q_vector->v_idx = q_idx;
- netif_napi_add(adapter->netdev, &q_vector->napi, (*poll), 64);
- adapter->q_vector[q_idx] = q_vector;
+ q_vector->v_idx = v_idx;
+
+ /* Allocate the affinity_hint cpumask, configure the mask */
+ if (!alloc_cpumask_var(&q_vector->affinity_mask, GFP_KERNEL))
+ goto err_out;
+ cpumask_set_cpu(v_idx, q_vector->affinity_mask);
+ netif_napi_add(adapter->netdev, &q_vector->napi,
+ ixgbe_poll, 64);
+ adapter->q_vector[v_idx] = q_vector;
}
return 0;
err_out:
- while (q_idx) {
- q_idx--;
- q_vector = adapter->q_vector[q_idx];
+ while (v_idx) {
+ v_idx--;
+ q_vector = adapter->q_vector[v_idx];
netif_napi_del(&q_vector->napi);
+ free_cpumask_var(q_vector->affinity_mask);
kfree(q_vector);
- adapter->q_vector[q_idx] = NULL;
+ adapter->q_vector[v_idx] = NULL;
}
return -ENOMEM;
}
@@ -5056,17 +4804,18 @@ err_out:
**/
static void ixgbe_free_q_vectors(struct ixgbe_adapter *adapter)
{
- int q_idx, num_q_vectors;
+ int v_idx, num_q_vectors;
if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED)
num_q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
else
num_q_vectors = 1;
- for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
- struct ixgbe_q_vector *q_vector = adapter->q_vector[q_idx];
- adapter->q_vector[q_idx] = NULL;
+ for (v_idx = 0; v_idx < num_q_vectors; v_idx++) {
+ struct ixgbe_q_vector *q_vector = adapter->q_vector[v_idx];
+ adapter->q_vector[v_idx] = NULL;
netif_napi_del(&q_vector->napi);
+ free_cpumask_var(q_vector->affinity_mask);
kfree(q_vector);
}
}
@@ -5181,13 +4930,11 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
struct pci_dev *pdev = adapter->pdev;
- struct net_device *dev = adapter->netdev;
unsigned int rss;
#ifdef CONFIG_IXGBE_DCB
int j;
struct tc_configuration *tc;
#endif
- int max_frame = dev->mtu + ETH_HLEN + ETH_FCS_LEN;
/* PCI config space info */
@@ -5201,35 +4948,32 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
rss = min(IXGBE_MAX_RSS_INDICES, (int)num_online_cpus());
adapter->ring_feature[RING_F_RSS].indices = rss;
adapter->flags |= IXGBE_FLAG_RSS_ENABLED;
- adapter->ring_feature[RING_F_DCB].indices = IXGBE_MAX_DCB_INDICES;
switch (hw->mac.type) {
case ixgbe_mac_82598EB:
if (hw->device_id == IXGBE_DEV_ID_82598AT)
adapter->flags |= IXGBE_FLAG_FAN_FAIL_CAPABLE;
adapter->max_msix_q_vectors = MAX_MSIX_Q_VECTORS_82598;
break;
- case ixgbe_mac_82599EB:
case ixgbe_mac_X540:
+ adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_CAPABLE;
+ case ixgbe_mac_82599EB:
adapter->max_msix_q_vectors = MAX_MSIX_Q_VECTORS_82599;
adapter->flags2 |= IXGBE_FLAG2_RSC_CAPABLE;
adapter->flags2 |= IXGBE_FLAG2_RSC_ENABLED;
if (hw->device_id == IXGBE_DEV_ID_82599_T3_LOM)
adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_CAPABLE;
- /* n-tuple support exists, always init our spinlock */
- spin_lock_init(&adapter->fdir_perfect_lock);
/* Flow Director hash filters enabled */
adapter->flags |= IXGBE_FLAG_FDIR_HASH_CAPABLE;
adapter->atr_sample_rate = 20;
adapter->ring_feature[RING_F_FDIR].indices =
IXGBE_MAX_FDIR_INDICES;
- adapter->fdir_pballoc = 0;
+ adapter->fdir_pballoc = IXGBE_FDIR_PBALLOC_64K;
#ifdef IXGBE_FCOE
adapter->flags |= IXGBE_FLAG_FCOE_CAPABLE;
adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED;
adapter->ring_feature[RING_F_FCOE].indices = 0;
#ifdef CONFIG_IXGBE_DCB
/* Default traffic class to use for FCoE */
- adapter->fcoe.tc = IXGBE_FCOE_DEFTC;
adapter->fcoe.up = IXGBE_FCOE_DEFTC;
#endif
#endif /* IXGBE_FCOE */
@@ -5238,7 +4982,21 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
break;
}
+ /* n-tuple support exists, always init our spinlock */
+ spin_lock_init(&adapter->fdir_perfect_lock);
+
#ifdef CONFIG_IXGBE_DCB
+ switch (hw->mac.type) {
+ case ixgbe_mac_X540:
+ adapter->dcb_cfg.num_tcs.pg_tcs = X540_TRAFFIC_CLASS;
+ adapter->dcb_cfg.num_tcs.pfc_tcs = X540_TRAFFIC_CLASS;
+ break;
+ default:
+ adapter->dcb_cfg.num_tcs.pg_tcs = MAX_TRAFFIC_CLASS;
+ adapter->dcb_cfg.num_tcs.pfc_tcs = MAX_TRAFFIC_CLASS;
+ break;
+ }
+
/* Configure DCB traffic classes */
for (j = 0; j < MAX_TRAFFIC_CLASS; j++) {
tc = &adapter->dcb_cfg.tc_config[j];
@@ -5248,9 +5006,14 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
tc->path[DCB_RX_CONFIG].bwg_percent = 12 + (j & 1);
tc->dcb_pfc = pfc_disabled;
}
+
+ /* Initialize default user to priority mapping, UPx->TC0 */
+ tc = &adapter->dcb_cfg.tc_config[0];
+ tc->path[DCB_TX_CONFIG].up_to_tc_bitmap = 0xFF;
+ tc->path[DCB_RX_CONFIG].up_to_tc_bitmap = 0xFF;
+
adapter->dcb_cfg.bw_percentage[DCB_TX_CONFIG][0] = 100;
adapter->dcb_cfg.bw_percentage[DCB_RX_CONFIG][0] = 100;
- adapter->dcb_cfg.rx_pba_cfg = pba_equal;
adapter->dcb_cfg.pfc_mode_enable = false;
adapter->dcb_set_bitmap = 0x00;
adapter->dcbx_cap = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_CEE;
@@ -5265,17 +5028,14 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
#ifdef CONFIG_DCB
adapter->last_lfc_mode = hw->fc.current_mode;
#endif
- hw->fc.high_water = FC_HIGH_WATER(max_frame);
- hw->fc.low_water = FC_LOW_WATER(max_frame);
+ ixgbe_pbthresh_setup(adapter);
hw->fc.pause_time = IXGBE_DEFAULT_FCPAUSE;
hw->fc.send_xon = true;
hw->fc.disable_fc_autoneg = false;
/* enable itr by default in dynamic mode */
adapter->rx_itr_setting = 1;
- adapter->rx_eitr_param = 20000;
adapter->tx_itr_setting = 1;
- adapter->tx_eitr_param = 10000;
/* set defaults for eitr in MegaBytes */
adapter->eitr_low = 10;
@@ -5285,6 +5045,9 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
adapter->tx_ring_count = IXGBE_DEFAULT_TXD;
adapter->rx_ring_count = IXGBE_DEFAULT_RXD;
+ /* set default work limits */
+ adapter->tx_work_limit = IXGBE_DEFAULT_TX_WORK;
+
/* initialize eeprom parameters */
if (ixgbe_init_eeprom_params_generic(hw)) {
e_dev_err("EEPROM initialization failed\n");
@@ -5331,7 +5094,6 @@ int ixgbe_setup_tx_resources(struct ixgbe_ring *tx_ring)
tx_ring->next_to_use = 0;
tx_ring->next_to_clean = 0;
- tx_ring->work_limit = tx_ring->count;
return 0;
err:
@@ -5533,9 +5295,6 @@ static int ixgbe_change_mtu(struct net_device *netdev, int new_mtu)
/* must set new MTU before calling down or up */
netdev->mtu = new_mtu;
- hw->fc.high_water = FC_HIGH_WATER(max_frame);
- hw->fc.low_water = FC_LOW_WATER(max_frame);
-
if (netif_running(netdev))
ixgbe_reinit_locked(adapter);
@@ -5581,17 +5340,10 @@ static int ixgbe_open(struct net_device *netdev)
if (err)
goto err_req_irq;
- err = ixgbe_up_complete(adapter);
- if (err)
- goto err_up;
-
- netif_tx_start_all_queues(netdev);
+ ixgbe_up_complete(adapter);
return 0;
-err_up:
- ixgbe_release_hw_control(adapter);
- ixgbe_free_irq(adapter);
err_req_irq:
err_setup_rx:
ixgbe_free_all_rx_resources(adapter);
@@ -5620,6 +5372,8 @@ static int ixgbe_close(struct net_device *netdev)
ixgbe_down(adapter);
ixgbe_free_irq(adapter);
+ ixgbe_fdir_filter_exit(adapter);
+
ixgbe_free_all_tx_resources(adapter);
ixgbe_free_all_rx_resources(adapter);
@@ -5794,6 +5548,11 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter)
u64 non_eop_descs = 0, restart_queue = 0, tx_busy = 0;
u64 alloc_rx_page_failed = 0, alloc_rx_buff_failed = 0;
u64 bytes = 0, packets = 0;
+#ifdef IXGBE_FCOE
+ struct ixgbe_fcoe *fcoe = &adapter->fcoe;
+ unsigned int cpu;
+ u64 fcoe_noddp_counts_sum = 0, fcoe_noddp_ext_buff_counts_sum = 0;
+#endif /* IXGBE_FCOE */
if (test_bit(__IXGBE_DOWN, &adapter->state) ||
test_bit(__IXGBE_RESETTING, &adapter->state))
@@ -5843,20 +5602,21 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter)
netdev->stats.tx_packets = packets;
hwstats->crcerrs += IXGBE_READ_REG(hw, IXGBE_CRCERRS);
+
+ /* 8 register reads */
for (i = 0; i < 8; i++) {
/* for packet buffers not used, the register should read 0 */
mpc = IXGBE_READ_REG(hw, IXGBE_MPC(i));
missed_rx += mpc;
hwstats->mpc[i] += mpc;
total_mpc += hwstats->mpc[i];
- if (hw->mac.type == ixgbe_mac_82598EB)
- hwstats->rnbc[i] += IXGBE_READ_REG(hw, IXGBE_RNBC(i));
- hwstats->qptc[i] += IXGBE_READ_REG(hw, IXGBE_QPTC(i));
- hwstats->qbtc[i] += IXGBE_READ_REG(hw, IXGBE_QBTC(i));
- hwstats->qprc[i] += IXGBE_READ_REG(hw, IXGBE_QPRC(i));
- hwstats->qbrc[i] += IXGBE_READ_REG(hw, IXGBE_QBRC(i));
+ hwstats->pxontxc[i] += IXGBE_READ_REG(hw, IXGBE_PXONTXC(i));
+ hwstats->pxofftxc[i] += IXGBE_READ_REG(hw, IXGBE_PXOFFTXC(i));
switch (hw->mac.type) {
case ixgbe_mac_82598EB:
+ hwstats->rnbc[i] += IXGBE_READ_REG(hw, IXGBE_RNBC(i));
+ hwstats->qbtc[i] += IXGBE_READ_REG(hw, IXGBE_QBTC(i));
+ hwstats->qbrc[i] += IXGBE_READ_REG(hw, IXGBE_QBRC(i));
hwstats->pxonrxc[i] +=
IXGBE_READ_REG(hw, IXGBE_PXONRXC(i));
break;
@@ -5868,9 +5628,21 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter)
default:
break;
}
- hwstats->pxontxc[i] += IXGBE_READ_REG(hw, IXGBE_PXONTXC(i));
- hwstats->pxofftxc[i] += IXGBE_READ_REG(hw, IXGBE_PXOFFTXC(i));
}
+
+ /*16 register reads */
+ for (i = 0; i < 16; i++) {
+ hwstats->qptc[i] += IXGBE_READ_REG(hw, IXGBE_QPTC(i));
+ hwstats->qprc[i] += IXGBE_READ_REG(hw, IXGBE_QPRC(i));
+ if ((hw->mac.type == ixgbe_mac_82599EB) ||
+ (hw->mac.type == ixgbe_mac_X540)) {
+ hwstats->qbtc[i] += IXGBE_READ_REG(hw, IXGBE_QBTC_L(i));
+ IXGBE_READ_REG(hw, IXGBE_QBTC_H(i)); /* to clear */
+ hwstats->qbrc[i] += IXGBE_READ_REG(hw, IXGBE_QBRC_L(i));
+ IXGBE_READ_REG(hw, IXGBE_QBRC_H(i)); /* to clear */
+ }
+ }
+
hwstats->gprc += IXGBE_READ_REG(hw, IXGBE_GPRC);
/* work around hardware counting issue */
hwstats->gprc -= missed_rx;
@@ -5908,6 +5680,18 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter)
hwstats->fcoeptc += IXGBE_READ_REG(hw, IXGBE_FCOEPTC);
hwstats->fcoedwrc += IXGBE_READ_REG(hw, IXGBE_FCOEDWRC);
hwstats->fcoedwtc += IXGBE_READ_REG(hw, IXGBE_FCOEDWTC);
+ /* Add up per cpu counters for total ddp aloc fail */
+ if (fcoe->pcpu_noddp && fcoe->pcpu_noddp_ext_buff) {
+ for_each_possible_cpu(cpu) {
+ fcoe_noddp_counts_sum +=
+ *per_cpu_ptr(fcoe->pcpu_noddp, cpu);
+ fcoe_noddp_ext_buff_counts_sum +=
+ *per_cpu_ptr(fcoe->
+ pcpu_noddp_ext_buff, cpu);
+ }
+ }
+ hwstats->fcoe_noddp = fcoe_noddp_counts_sum;
+ hwstats->fcoe_noddp_ext_buff = fcoe_noddp_ext_buff_counts_sum;
#endif /* IXGBE_FCOE */
break;
default:
@@ -5930,7 +5714,6 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter)
hwstats->lxontxc += lxon;
lxoff = IXGBE_READ_REG(hw, IXGBE_LXOFFTXC);
hwstats->lxofftxc += lxoff;
- hwstats->ruc += IXGBE_READ_REG(hw, IXGBE_RUC);
hwstats->gptc += IXGBE_READ_REG(hw, IXGBE_GPTC);
hwstats->mptc += IXGBE_READ_REG(hw, IXGBE_MPTC);
/*
@@ -6038,7 +5821,7 @@ static void ixgbe_check_hang_subtask(struct ixgbe_adapter *adapter)
/* get one bit for every active tx/rx interrupt vector */
for (i = 0; i < adapter->num_msix_vectors - NON_Q_VECTORS; i++) {
struct ixgbe_q_vector *qv = adapter->q_vector[i];
- if (qv->rxr_count || qv->txr_count)
+ if (qv->rx.ring || qv->tx.ring)
eics |= ((u64)1 << i);
}
}
@@ -6143,9 +5926,7 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter)
(flow_tx ? "TX" : "None"))));
netif_carrier_on(netdev);
-#ifdef HAVE_IPLINK_VF_CONFIG
ixgbe_check_vf_rate_limit(adapter);
-#endif /* HAVE_IPLINK_VF_CONFIG */
}
/**
@@ -6229,7 +6010,8 @@ static void ixgbe_spoof_check(struct ixgbe_adapter *adapter)
static void ixgbe_watchdog_subtask(struct ixgbe_adapter *adapter)
{
/* if interface is down do nothing */
- if (test_bit(__IXGBE_DOWN, &adapter->state))
+ if (test_bit(__IXGBE_DOWN, &adapter->state) ||
+ test_bit(__IXGBE_RESETTING, &adapter->state))
return;
ixgbe_watchdog_update_link(adapter);
@@ -6334,7 +6116,6 @@ static void ixgbe_sfp_link_config_subtask(struct ixgbe_adapter *adapter)
autoneg = hw->phy.autoneg_advertised;
if ((!autoneg) && (hw->mac.ops.get_link_capabilities))
hw->mac.ops.get_link_capabilities(hw, &autoneg, &negotiation);
- hw->mac.autotry_restart = false;
if (hw->mac.ops.setup_link)
hw->mac.ops.setup_link(hw, autoneg, negotiation, true);
@@ -6343,6 +6124,51 @@ static void ixgbe_sfp_link_config_subtask(struct ixgbe_adapter *adapter)
clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state);
}
+#ifdef CONFIG_PCI_IOV
+static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter)
+{
+ int vf;
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+ u32 gpc;
+ u32 ciaa, ciad;
+
+ gpc = IXGBE_READ_REG(hw, IXGBE_TXDGPC);
+ if (gpc) /* If incrementing then no need for the check below */
+ return;
+ /*
+ * Check to see if a bad DMA write target from an errant or
+ * malicious VF has caused a PCIe error. If so then we can
+ * issue a VFLR to the offending VF(s) and then resume without
+ * requesting a full slot reset.
+ */
+
+ for (vf = 0; vf < adapter->num_vfs; vf++) {
+ ciaa = (vf << 16) | 0x80000000;
+ /* 32 bit read so align, we really want status at offset 6 */
+ ciaa |= PCI_COMMAND;
+ IXGBE_WRITE_REG(hw, IXGBE_CIAA_82599, ciaa);
+ ciad = IXGBE_READ_REG(hw, IXGBE_CIAD_82599);
+ ciaa &= 0x7FFFFFFF;
+ /* disable debug mode asap after reading data */
+ IXGBE_WRITE_REG(hw, IXGBE_CIAA_82599, ciaa);
+ /* Get the upper 16 bits which will be the PCI status reg */
+ ciad >>= 16;
+ if (ciad & PCI_STATUS_REC_MASTER_ABORT) {
+ netdev_err(netdev, "VF %d Hung DMA\n", vf);
+ /* Issue VFLR */
+ ciaa = (vf << 16) | 0x80000000;
+ ciaa |= 0xA8;
+ IXGBE_WRITE_REG(hw, IXGBE_CIAA_82599, ciaa);
+ ciad = 0x00008000; /* VFLR */
+ IXGBE_WRITE_REG(hw, IXGBE_CIAD_82599, ciad);
+ ciaa &= 0x7FFFFFFF;
+ IXGBE_WRITE_REG(hw, IXGBE_CIAA_82599, ciaa);
+ }
+ }
+}
+
+#endif
/**
* ixgbe_service_timer - Timer Call-back
* @data: pointer to adapter cast into an unsigned long
@@ -6351,17 +6177,49 @@ static void ixgbe_service_timer(unsigned long data)
{
struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data;
unsigned long next_event_offset;
+ bool ready = true;
+
+#ifdef CONFIG_PCI_IOV
+ ready = false;
+
+ /*
+ * don't bother with SR-IOV VF DMA hang check if there are
+ * no VFs or the link is down
+ */
+ if (!adapter->num_vfs ||
+ (adapter->flags & IXGBE_FLAG_NEED_LINK_UPDATE)) {
+ ready = true;
+ goto normal_timer_service;
+ }
+
+ /* If we have VFs allocated then we must check for DMA hangs */
+ ixgbe_check_for_bad_vf(adapter);
+ next_event_offset = HZ / 50;
+ adapter->timer_event_accumulator++;
+ if (adapter->timer_event_accumulator >= 100) {
+ ready = true;
+ adapter->timer_event_accumulator = 0;
+ }
+
+ goto schedule_event;
+
+normal_timer_service:
+#endif
/* poll faster when waiting for link */
if (adapter->flags & IXGBE_FLAG_NEED_LINK_UPDATE)
next_event_offset = HZ / 10;
else
next_event_offset = HZ * 2;
+#ifdef CONFIG_PCI_IOV
+schedule_event:
+#endif
/* Reset the timer */
mod_timer(&adapter->service_timer, next_event_offset + jiffies);
- ixgbe_service_event_schedule(adapter);
+ if (ready)
+ ixgbe_service_event_schedule(adapter);
}
static void ixgbe_reset_subtask(struct ixgbe_adapter *adapter)
@@ -6404,264 +6262,309 @@ static void ixgbe_service_task(struct work_struct *work)
ixgbe_service_event_complete(adapter);
}
-static int ixgbe_tso(struct ixgbe_adapter *adapter,
- struct ixgbe_ring *tx_ring, struct sk_buff *skb,
- u32 tx_flags, u8 *hdr_len, __be16 protocol)
+void ixgbe_tx_ctxtdesc(struct ixgbe_ring *tx_ring, u32 vlan_macip_lens,
+ u32 fcoe_sof_eof, u32 type_tucmd, u32 mss_l4len_idx)
{
struct ixgbe_adv_tx_context_desc *context_desc;
- unsigned int i;
- int err;
- struct ixgbe_tx_buffer *tx_buffer_info;
- u32 vlan_macip_lens = 0, type_tucmd_mlhl;
- u32 mss_l4len_idx, l4len;
+ u16 i = tx_ring->next_to_use;
- if (skb_is_gso(skb)) {
- if (skb_header_cloned(skb)) {
- err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
- if (err)
- return err;
- }
- l4len = tcp_hdrlen(skb);
- *hdr_len += l4len;
-
- if (protocol == htons(ETH_P_IP)) {
- struct iphdr *iph = ip_hdr(skb);
- iph->tot_len = 0;
- iph->check = 0;
- tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
- iph->daddr, 0,
- IPPROTO_TCP,
- 0);
- } else if (skb_is_gso_v6(skb)) {
- ipv6_hdr(skb)->payload_len = 0;
- tcp_hdr(skb)->check =
- ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
- &ipv6_hdr(skb)->daddr,
- 0, IPPROTO_TCP, 0);
- }
+ context_desc = IXGBE_TX_CTXTDESC_ADV(tx_ring, i);
- i = tx_ring->next_to_use;
+ i++;
+ tx_ring->next_to_use = (i < tx_ring->count) ? i : 0;
- tx_buffer_info = &tx_ring->tx_buffer_info[i];
- context_desc = IXGBE_TX_CTXTDESC_ADV(tx_ring, i);
-
- /* VLAN MACLEN IPLEN */
- if (tx_flags & IXGBE_TX_FLAGS_VLAN)
- vlan_macip_lens |=
- (tx_flags & IXGBE_TX_FLAGS_VLAN_MASK);
- vlan_macip_lens |= ((skb_network_offset(skb)) <<
- IXGBE_ADVTXD_MACLEN_SHIFT);
- *hdr_len += skb_network_offset(skb);
- vlan_macip_lens |=
- (skb_transport_header(skb) - skb_network_header(skb));
- *hdr_len +=
- (skb_transport_header(skb) - skb_network_header(skb));
- context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens);
- context_desc->seqnum_seed = 0;
-
- /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */
- type_tucmd_mlhl = (IXGBE_TXD_CMD_DEXT |
- IXGBE_ADVTXD_DTYP_CTXT);
-
- if (protocol == htons(ETH_P_IP))
- type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4;
- type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP;
- context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd_mlhl);
-
- /* MSS L4LEN IDX */
- mss_l4len_idx =
- (skb_shinfo(skb)->gso_size << IXGBE_ADVTXD_MSS_SHIFT);
- mss_l4len_idx |= (l4len << IXGBE_ADVTXD_L4LEN_SHIFT);
- /* use index 1 for TSO */
- mss_l4len_idx |= (1 << IXGBE_ADVTXD_IDX_SHIFT);
- context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx);
-
- tx_buffer_info->time_stamp = jiffies;
- tx_buffer_info->next_to_watch = i;
+ /* set bits to identify this as an advanced context descriptor */
+ type_tucmd |= IXGBE_TXD_CMD_DEXT | IXGBE_ADVTXD_DTYP_CTXT;
- i++;
- if (i == tx_ring->count)
- i = 0;
- tx_ring->next_to_use = i;
+ context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens);
+ context_desc->seqnum_seed = cpu_to_le32(fcoe_sof_eof);
+ context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd);
+ context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx);
+}
- return true;
+static int ixgbe_tso(struct ixgbe_ring *tx_ring, struct sk_buff *skb,
+ u32 tx_flags, __be16 protocol, u8 *hdr_len)
+{
+ int err;
+ u32 vlan_macip_lens, type_tucmd;
+ u32 mss_l4len_idx, l4len;
+
+ if (!skb_is_gso(skb))
+ return 0;
+
+ if (skb_header_cloned(skb)) {
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (err)
+ return err;
}
- return false;
-}
-static u32 ixgbe_psum(struct ixgbe_adapter *adapter, struct sk_buff *skb,
- __be16 protocol)
+ /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */
+ type_tucmd = IXGBE_ADVTXD_TUCMD_L4T_TCP;
+
+ if (protocol == __constant_htons(ETH_P_IP)) {
+ struct iphdr *iph = ip_hdr(skb);
+ iph->tot_len = 0;
+ iph->check = 0;
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, 0,
+ IPPROTO_TCP,
+ 0);
+ type_tucmd |= IXGBE_ADVTXD_TUCMD_IPV4;
+ } else if (skb_is_gso_v6(skb)) {
+ ipv6_hdr(skb)->payload_len = 0;
+ tcp_hdr(skb)->check =
+ ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
+ }
+
+ l4len = tcp_hdrlen(skb);
+ *hdr_len = skb_transport_offset(skb) + l4len;
+
+ /* mss_l4len_id: use 1 as index for TSO */
+ mss_l4len_idx = l4len << IXGBE_ADVTXD_L4LEN_SHIFT;
+ mss_l4len_idx |= skb_shinfo(skb)->gso_size << IXGBE_ADVTXD_MSS_SHIFT;
+ mss_l4len_idx |= 1 << IXGBE_ADVTXD_IDX_SHIFT;
+
+ /* vlan_macip_lens: HEADLEN, MACLEN, VLAN tag */
+ vlan_macip_lens = skb_network_header_len(skb);
+ vlan_macip_lens |= skb_network_offset(skb) << IXGBE_ADVTXD_MACLEN_SHIFT;
+ vlan_macip_lens |= tx_flags & IXGBE_TX_FLAGS_VLAN_MASK;
+
+ ixgbe_tx_ctxtdesc(tx_ring, vlan_macip_lens, 0, type_tucmd,
+ mss_l4len_idx);
+
+ return 1;
+}
+
+static bool ixgbe_tx_csum(struct ixgbe_ring *tx_ring,
+ struct sk_buff *skb, u32 tx_flags,
+ __be16 protocol)
{
- u32 rtn = 0;
+ u32 vlan_macip_lens = 0;
+ u32 mss_l4len_idx = 0;
+ u32 type_tucmd = 0;
- switch (protocol) {
- case cpu_to_be16(ETH_P_IP):
- rtn |= IXGBE_ADVTXD_TUCMD_IPV4;
- switch (ip_hdr(skb)->protocol) {
- case IPPROTO_TCP:
- rtn |= IXGBE_ADVTXD_TUCMD_L4T_TCP;
+ if (skb->ip_summed != CHECKSUM_PARTIAL) {
+ if (!(tx_flags & IXGBE_TX_FLAGS_HW_VLAN) &&
+ !(tx_flags & IXGBE_TX_FLAGS_TXSW))
+ return false;
+ } else {
+ u8 l4_hdr = 0;
+ switch (protocol) {
+ case __constant_htons(ETH_P_IP):
+ vlan_macip_lens |= skb_network_header_len(skb);
+ type_tucmd |= IXGBE_ADVTXD_TUCMD_IPV4;
+ l4_hdr = ip_hdr(skb)->protocol;
break;
- case IPPROTO_SCTP:
- rtn |= IXGBE_ADVTXD_TUCMD_L4T_SCTP;
+ case __constant_htons(ETH_P_IPV6):
+ vlan_macip_lens |= skb_network_header_len(skb);
+ l4_hdr = ipv6_hdr(skb)->nexthdr;
+ break;
+ default:
+ if (unlikely(net_ratelimit())) {
+ dev_warn(tx_ring->dev,
+ "partial checksum but proto=%x!\n",
+ skb->protocol);
+ }
break;
}
- break;
- case cpu_to_be16(ETH_P_IPV6):
- /* XXX what about other V6 headers?? */
- switch (ipv6_hdr(skb)->nexthdr) {
+
+ switch (l4_hdr) {
case IPPROTO_TCP:
- rtn |= IXGBE_ADVTXD_TUCMD_L4T_TCP;
+ type_tucmd |= IXGBE_ADVTXD_TUCMD_L4T_TCP;
+ mss_l4len_idx = tcp_hdrlen(skb) <<
+ IXGBE_ADVTXD_L4LEN_SHIFT;
break;
case IPPROTO_SCTP:
- rtn |= IXGBE_ADVTXD_TUCMD_L4T_SCTP;
+ type_tucmd |= IXGBE_ADVTXD_TUCMD_L4T_SCTP;
+ mss_l4len_idx = sizeof(struct sctphdr) <<
+ IXGBE_ADVTXD_L4LEN_SHIFT;
+ break;
+ case IPPROTO_UDP:
+ mss_l4len_idx = sizeof(struct udphdr) <<
+ IXGBE_ADVTXD_L4LEN_SHIFT;
+ break;
+ default:
+ if (unlikely(net_ratelimit())) {
+ dev_warn(tx_ring->dev,
+ "partial checksum but l4 proto=%x!\n",
+ skb->protocol);
+ }
break;
}
- break;
- default:
- if (unlikely(net_ratelimit()))
- e_warn(probe, "partial checksum but proto=%x!\n",
- protocol);
- break;
}
- return rtn;
+ vlan_macip_lens |= skb_network_offset(skb) << IXGBE_ADVTXD_MACLEN_SHIFT;
+ vlan_macip_lens |= tx_flags & IXGBE_TX_FLAGS_VLAN_MASK;
+
+ ixgbe_tx_ctxtdesc(tx_ring, vlan_macip_lens, 0,
+ type_tucmd, mss_l4len_idx);
+
+ return (skb->ip_summed == CHECKSUM_PARTIAL);
}
-static bool ixgbe_tx_csum(struct ixgbe_adapter *adapter,
- struct ixgbe_ring *tx_ring,
- struct sk_buff *skb, u32 tx_flags,
- __be16 protocol)
+static __le32 ixgbe_tx_cmd_type(u32 tx_flags)
{
- struct ixgbe_adv_tx_context_desc *context_desc;
- unsigned int i;
- struct ixgbe_tx_buffer *tx_buffer_info;
- u32 vlan_macip_lens = 0, type_tucmd_mlhl = 0;
+ /* set type for advanced descriptor with frame checksum insertion */
+ __le32 cmd_type = cpu_to_le32(IXGBE_ADVTXD_DTYP_DATA |
+ IXGBE_ADVTXD_DCMD_IFCS |
+ IXGBE_ADVTXD_DCMD_DEXT);
- if (skb->ip_summed == CHECKSUM_PARTIAL ||
- (tx_flags & IXGBE_TX_FLAGS_VLAN)) {
- i = tx_ring->next_to_use;
- tx_buffer_info = &tx_ring->tx_buffer_info[i];
- context_desc = IXGBE_TX_CTXTDESC_ADV(tx_ring, i);
+ /* set HW vlan bit if vlan is present */
+ if (tx_flags & IXGBE_TX_FLAGS_HW_VLAN)
+ cmd_type |= cpu_to_le32(IXGBE_ADVTXD_DCMD_VLE);
- if (tx_flags & IXGBE_TX_FLAGS_VLAN)
- vlan_macip_lens |=
- (tx_flags & IXGBE_TX_FLAGS_VLAN_MASK);
- vlan_macip_lens |= (skb_network_offset(skb) <<
- IXGBE_ADVTXD_MACLEN_SHIFT);
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- vlan_macip_lens |= (skb_transport_header(skb) -
- skb_network_header(skb));
-
- context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens);
- context_desc->seqnum_seed = 0;
+ /* set segmentation enable bits for TSO/FSO */
+#ifdef IXGBE_FCOE
+ if ((tx_flags & IXGBE_TX_FLAGS_TSO) || (tx_flags & IXGBE_TX_FLAGS_FSO))
+#else
+ if (tx_flags & IXGBE_TX_FLAGS_TSO)
+#endif
+ cmd_type |= cpu_to_le32(IXGBE_ADVTXD_DCMD_TSE);
- type_tucmd_mlhl |= (IXGBE_TXD_CMD_DEXT |
- IXGBE_ADVTXD_DTYP_CTXT);
+ return cmd_type;
+}
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- type_tucmd_mlhl |= ixgbe_psum(adapter, skb, protocol);
+static __le32 ixgbe_tx_olinfo_status(u32 tx_flags, unsigned int paylen)
+{
+ __le32 olinfo_status =
+ cpu_to_le32(paylen << IXGBE_ADVTXD_PAYLEN_SHIFT);
- context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd_mlhl);
- /* use index zero for tx checksum offload */
- context_desc->mss_l4len_idx = 0;
+ if (tx_flags & IXGBE_TX_FLAGS_TSO) {
+ olinfo_status |= cpu_to_le32(IXGBE_ADVTXD_POPTS_TXSM |
+ (1 << IXGBE_ADVTXD_IDX_SHIFT));
+ /* enble IPv4 checksum for TSO */
+ if (tx_flags & IXGBE_TX_FLAGS_IPV4)
+ olinfo_status |= cpu_to_le32(IXGBE_ADVTXD_POPTS_IXSM);
+ }
- tx_buffer_info->time_stamp = jiffies;
- tx_buffer_info->next_to_watch = i;
+ /* enable L4 checksum for TSO and TX checksum offload */
+ if (tx_flags & IXGBE_TX_FLAGS_CSUM)
+ olinfo_status |= cpu_to_le32(IXGBE_ADVTXD_POPTS_TXSM);
- i++;
- if (i == tx_ring->count)
- i = 0;
- tx_ring->next_to_use = i;
+#ifdef IXGBE_FCOE
+ /* use index 1 context for FCOE/FSO */
+ if (tx_flags & IXGBE_TX_FLAGS_FCOE)
+ olinfo_status |= cpu_to_le32(IXGBE_ADVTXD_CC |
+ (1 << IXGBE_ADVTXD_IDX_SHIFT));
- return true;
- }
+#endif
+ /*
+ * Check Context must be set if Tx switch is enabled, which it
+ * always is for case where virtual functions are running
+ */
+ if (tx_flags & IXGBE_TX_FLAGS_TXSW)
+ olinfo_status |= cpu_to_le32(IXGBE_ADVTXD_CC);
- return false;
+ return olinfo_status;
}
-static int ixgbe_tx_map(struct ixgbe_adapter *adapter,
- struct ixgbe_ring *tx_ring,
- struct sk_buff *skb, u32 tx_flags,
- unsigned int first, const u8 hdr_len)
+#define IXGBE_TXD_CMD (IXGBE_TXD_CMD_EOP | \
+ IXGBE_TXD_CMD_RS)
+
+static void ixgbe_tx_map(struct ixgbe_ring *tx_ring,
+ struct sk_buff *skb,
+ struct ixgbe_tx_buffer *first,
+ u32 tx_flags,
+ const u8 hdr_len)
{
struct device *dev = tx_ring->dev;
struct ixgbe_tx_buffer *tx_buffer_info;
- unsigned int len;
- unsigned int total = skb->len;
- unsigned int offset = 0, size, count = 0, i;
- unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
- unsigned int f;
- unsigned int bytecount = skb->len;
- u16 gso_segs = 1;
+ union ixgbe_adv_tx_desc *tx_desc;
+ dma_addr_t dma;
+ __le32 cmd_type, olinfo_status;
+ struct skb_frag_struct *frag;
+ unsigned int f = 0;
+ unsigned int data_len = skb->data_len;
+ unsigned int size = skb_headlen(skb);
+ u32 offset = 0;
+ u32 paylen = skb->len - hdr_len;
+ u16 i = tx_ring->next_to_use;
+ u16 gso_segs;
+
+#ifdef IXGBE_FCOE
+ if (tx_flags & IXGBE_TX_FLAGS_FCOE) {
+ if (data_len >= sizeof(struct fcoe_crc_eof)) {
+ data_len -= sizeof(struct fcoe_crc_eof);
+ } else {
+ size -= sizeof(struct fcoe_crc_eof) - data_len;
+ data_len = 0;
+ }
+ }
- i = tx_ring->next_to_use;
+#endif
+ dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, dma))
+ goto dma_error;
- if (tx_flags & IXGBE_TX_FLAGS_FCOE)
- /* excluding fcoe_crc_eof for FCoE */
- total -= sizeof(struct fcoe_crc_eof);
+ cmd_type = ixgbe_tx_cmd_type(tx_flags);
+ olinfo_status = ixgbe_tx_olinfo_status(tx_flags, paylen);
- len = min(skb_headlen(skb), total);
- while (len) {
- tx_buffer_info = &tx_ring->tx_buffer_info[i];
- size = min(len, (uint)IXGBE_MAX_DATA_PER_TXD);
-
- tx_buffer_info->length = size;
- tx_buffer_info->mapped_as_page = false;
- tx_buffer_info->dma = dma_map_single(dev,
- skb->data + offset,
- size, DMA_TO_DEVICE);
- if (dma_mapping_error(dev, tx_buffer_info->dma))
- goto dma_error;
- tx_buffer_info->time_stamp = jiffies;
- tx_buffer_info->next_to_watch = i;
+ tx_desc = IXGBE_TX_DESC_ADV(tx_ring, i);
+
+ for (;;) {
+ while (size > IXGBE_MAX_DATA_PER_TXD) {
+ tx_desc->read.buffer_addr = cpu_to_le64(dma + offset);
+ tx_desc->read.cmd_type_len =
+ cmd_type | cpu_to_le32(IXGBE_MAX_DATA_PER_TXD);
+ tx_desc->read.olinfo_status = olinfo_status;
- len -= size;
- total -= size;
- offset += size;
- count++;
+ offset += IXGBE_MAX_DATA_PER_TXD;
+ size -= IXGBE_MAX_DATA_PER_TXD;
- if (len) {
+ tx_desc++;
i++;
- if (i == tx_ring->count)
+ if (i == tx_ring->count) {
+ tx_desc = IXGBE_TX_DESC_ADV(tx_ring, 0);
i = 0;
+ }
}
- }
- for (f = 0; f < nr_frags; f++) {
- struct skb_frag_struct *frag;
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ tx_buffer_info->length = offset + size;
+ tx_buffer_info->tx_flags = tx_flags;
+ tx_buffer_info->dma = dma;
+
+ tx_desc->read.buffer_addr = cpu_to_le64(dma + offset);
+ tx_desc->read.cmd_type_len = cmd_type | cpu_to_le32(size);
+ tx_desc->read.olinfo_status = olinfo_status;
+
+ if (!data_len)
+ break;
frag = &skb_shinfo(skb)->frags[f];
- len = min((unsigned int)frag->size, total);
- offset = frag->page_offset;
+#ifdef IXGBE_FCOE
+ size = min_t(unsigned int, data_len, skb_frag_size(frag));
+#else
+ size = skb_frag_size(frag);
+#endif
+ data_len -= size;
+ f++;
- while (len) {
- i++;
- if (i == tx_ring->count)
- i = 0;
+ offset = 0;
+ tx_flags |= IXGBE_TX_FLAGS_MAPPED_AS_PAGE;
- tx_buffer_info = &tx_ring->tx_buffer_info[i];
- size = min(len, (uint)IXGBE_MAX_DATA_PER_TXD);
-
- tx_buffer_info->length = size;
- tx_buffer_info->dma = dma_map_page(dev,
- frag->page,
- offset, size,
- DMA_TO_DEVICE);
- tx_buffer_info->mapped_as_page = true;
- if (dma_mapping_error(dev, tx_buffer_info->dma))
- goto dma_error;
- tx_buffer_info->time_stamp = jiffies;
- tx_buffer_info->next_to_watch = i;
-
- len -= size;
- total -= size;
- offset += size;
- count++;
+ dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, dma))
+ goto dma_error;
+
+ tx_desc++;
+ i++;
+ if (i == tx_ring->count) {
+ tx_desc = IXGBE_TX_DESC_ADV(tx_ring, 0);
+ i = 0;
}
- if (total == 0)
- break;
}
+ tx_desc->read.cmd_type_len |= cpu_to_le32(IXGBE_TXD_CMD);
+
+ i++;
+ if (i == tx_ring->count)
+ i = 0;
+
+ tx_ring->next_to_use = i;
+
if (tx_flags & IXGBE_TX_FLAGS_TSO)
gso_segs = skb_shinfo(skb)->gso_segs;
#ifdef IXGBE_FCOE
@@ -6670,93 +6573,16 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter,
gso_segs = DIV_ROUND_UP(skb->len - hdr_len,
skb_shinfo(skb)->gso_size);
#endif /* IXGBE_FCOE */
- bytecount += (gso_segs - 1) * hdr_len;
+ else
+ gso_segs = 1;
/* multiply data chunks by size of headers */
- tx_ring->tx_buffer_info[i].bytecount = bytecount;
- tx_ring->tx_buffer_info[i].gso_segs = gso_segs;
- tx_ring->tx_buffer_info[i].skb = skb;
- tx_ring->tx_buffer_info[first].next_to_watch = i;
-
- return count;
-
-dma_error:
- e_dev_err("TX DMA map failed\n");
-
- /* clear timestamp and dma mappings for failed tx_buffer_info map */
- tx_buffer_info->dma = 0;
- tx_buffer_info->time_stamp = 0;
- tx_buffer_info->next_to_watch = 0;
- if (count)
- count--;
+ tx_buffer_info->bytecount = paylen + (gso_segs * hdr_len);
+ tx_buffer_info->gso_segs = gso_segs;
+ tx_buffer_info->skb = skb;
- /* clear timestamp and dma mappings for remaining portion of packet */
- while (count--) {
- if (i == 0)
- i += tx_ring->count;
- i--;
- tx_buffer_info = &tx_ring->tx_buffer_info[i];
- ixgbe_unmap_and_free_tx_resource(tx_ring, tx_buffer_info);
- }
-
- return 0;
-}
-
-static void ixgbe_tx_queue(struct ixgbe_ring *tx_ring,
- int tx_flags, int count, u32 paylen, u8 hdr_len)
-{
- union ixgbe_adv_tx_desc *tx_desc = NULL;
- struct ixgbe_tx_buffer *tx_buffer_info;
- u32 olinfo_status = 0, cmd_type_len = 0;
- unsigned int i;
- u32 txd_cmd = IXGBE_TXD_CMD_EOP | IXGBE_TXD_CMD_RS | IXGBE_TXD_CMD_IFCS;
-
- cmd_type_len |= IXGBE_ADVTXD_DTYP_DATA;
-
- cmd_type_len |= IXGBE_ADVTXD_DCMD_IFCS | IXGBE_ADVTXD_DCMD_DEXT;
-
- if (tx_flags & IXGBE_TX_FLAGS_VLAN)
- cmd_type_len |= IXGBE_ADVTXD_DCMD_VLE;
-
- if (tx_flags & IXGBE_TX_FLAGS_TSO) {
- cmd_type_len |= IXGBE_ADVTXD_DCMD_TSE;
-
- olinfo_status |= IXGBE_TXD_POPTS_TXSM <<
- IXGBE_ADVTXD_POPTS_SHIFT;
-
- /* use index 1 context for tso */
- olinfo_status |= (1 << IXGBE_ADVTXD_IDX_SHIFT);
- if (tx_flags & IXGBE_TX_FLAGS_IPV4)
- olinfo_status |= IXGBE_TXD_POPTS_IXSM <<
- IXGBE_ADVTXD_POPTS_SHIFT;
-
- } else if (tx_flags & IXGBE_TX_FLAGS_CSUM)
- olinfo_status |= IXGBE_TXD_POPTS_TXSM <<
- IXGBE_ADVTXD_POPTS_SHIFT;
-
- if (tx_flags & IXGBE_TX_FLAGS_FCOE) {
- olinfo_status |= IXGBE_ADVTXD_CC;
- olinfo_status |= (1 << IXGBE_ADVTXD_IDX_SHIFT);
- if (tx_flags & IXGBE_TX_FLAGS_FSO)
- cmd_type_len |= IXGBE_ADVTXD_DCMD_TSE;
- }
-
- olinfo_status |= ((paylen - hdr_len) << IXGBE_ADVTXD_PAYLEN_SHIFT);
-
- i = tx_ring->next_to_use;
- while (count--) {
- tx_buffer_info = &tx_ring->tx_buffer_info[i];
- tx_desc = IXGBE_TX_DESC_ADV(tx_ring, i);
- tx_desc->read.buffer_addr = cpu_to_le64(tx_buffer_info->dma);
- tx_desc->read.cmd_type_len =
- cpu_to_le32(cmd_type_len | tx_buffer_info->length);
- tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
- i++;
- if (i == tx_ring->count)
- i = 0;
- }
-
- tx_desc->read.cmd_type_len |= cpu_to_le32(txd_cmd);
+ /* set the timestamp */
+ first->time_stamp = jiffies;
/*
* Force memory writes to complete before letting h/w
@@ -6766,8 +6592,30 @@ static void ixgbe_tx_queue(struct ixgbe_ring *tx_ring,
*/
wmb();
- tx_ring->next_to_use = i;
+ /* set next_to_watch value indicating a packet is present */
+ first->next_to_watch = tx_desc;
+
+ /* notify HW of packet */
writel(i, tx_ring->tail);
+
+ return;
+dma_error:
+ dev_err(dev, "TX DMA map failed\n");
+
+ /* clear dma mappings for failed tx_buffer_info map */
+ for (;;) {
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ ixgbe_unmap_tx_resource(tx_ring, tx_buffer_info);
+ if (tx_buffer_info == first)
+ break;
+ if (i == 0)
+ i = tx_ring->count;
+ i--;
+ }
+
+ dev_kfree_skb_any(skb);
+
+ tx_ring->next_to_use = i;
}
static void ixgbe_atr(struct ixgbe_ring *ring, struct sk_buff *skb,
@@ -6806,8 +6654,8 @@ static void ixgbe_atr(struct ixgbe_ring *ring, struct sk_buff *skb,
th = tcp_hdr(skb);
- /* skip this packet since the socket is closing */
- if (th->fin)
+ /* skip this packet since it is invalid or the socket is closing */
+ if (!th || th->fin)
return;
/* sample on all syn packets or once every atr sample count */
@@ -6832,7 +6680,7 @@ static void ixgbe_atr(struct ixgbe_ring *ring, struct sk_buff *skb,
* since src port and flex bytes occupy the same word XOR them together
* and write the value to source port portion of compressed dword
*/
- if (vlan_id)
+ if (tx_flags & (IXGBE_TX_FLAGS_SW_VLAN | IXGBE_TX_FLAGS_HW_VLAN))
common.port.src ^= th->dest ^ __constant_htons(ETH_P_8021Q);
else
common.port.src ^= th->dest ^ protocol;
@@ -6858,7 +6706,7 @@ static void ixgbe_atr(struct ixgbe_ring *ring, struct sk_buff *skb,
input, common, ring->queue_index);
}
-static int __ixgbe_maybe_stop_tx(struct ixgbe_ring *tx_ring, int size)
+static int __ixgbe_maybe_stop_tx(struct ixgbe_ring *tx_ring, u16 size)
{
netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
/* Herbert's original patch had:
@@ -6868,7 +6716,7 @@ static int __ixgbe_maybe_stop_tx(struct ixgbe_ring *tx_ring, int size)
/* We need to check again in a case another CPU has just
* made room available. */
- if (likely(IXGBE_DESC_UNUSED(tx_ring) < size))
+ if (likely(ixgbe_desc_unused(tx_ring) < size))
return -EBUSY;
/* A reprieve! - use start_queue because it doesn't call schedule */
@@ -6877,9 +6725,9 @@ static int __ixgbe_maybe_stop_tx(struct ixgbe_ring *tx_ring, int size)
return 0;
}
-static int ixgbe_maybe_stop_tx(struct ixgbe_ring *tx_ring, int size)
+static inline int ixgbe_maybe_stop_tx(struct ixgbe_ring *tx_ring, u16 size)
{
- if (likely(IXGBE_DESC_UNUSED(tx_ring) >= size))
+ if (likely(ixgbe_desc_unused(tx_ring) >= size))
return 0;
return __ixgbe_maybe_stop_tx(tx_ring, size);
}
@@ -6887,11 +6735,10 @@ static int ixgbe_maybe_stop_tx(struct ixgbe_ring *tx_ring, int size)
static u16 ixgbe_select_queue(struct net_device *dev, struct sk_buff *skb)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
- int txq = smp_processor_id();
+ int txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) :
+ smp_processor_id();
#ifdef IXGBE_FCOE
- __be16 protocol;
-
- protocol = vlan_get_protocol(skb);
+ __be16 protocol = vlan_get_protocol(skb);
if (((protocol == htons(ETH_P_FCOE)) ||
(protocol == htons(ETH_P_FIP))) &&
@@ -6915,99 +6762,122 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
struct ixgbe_adapter *adapter,
struct ixgbe_ring *tx_ring)
{
- unsigned int first;
- unsigned int tx_flags = 0;
- u8 hdr_len = 0;
+ struct ixgbe_tx_buffer *first;
int tso;
- int count = 0;
- unsigned int f;
- __be16 protocol;
+ u32 tx_flags = 0;
+#if PAGE_SIZE > IXGBE_MAX_DATA_PER_TXD
+ unsigned short f;
+#endif
+ u16 count = TXD_USE_COUNT(skb_headlen(skb));
+ __be16 protocol = skb->protocol;
+ u8 hdr_len = 0;
+
+ /*
+ * need: 1 descriptor per page * PAGE_SIZE/IXGBE_MAX_DATA_PER_TXD,
+ * + 1 desc for skb_head_len/IXGBE_MAX_DATA_PER_TXD,
+ * + 2 desc gap to keep tail from touching head,
+ * + 1 desc for context descriptor,
+ * otherwise try next time
+ */
+#if PAGE_SIZE > IXGBE_MAX_DATA_PER_TXD
+ for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
+ count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
+#else
+ count += skb_shinfo(skb)->nr_frags;
+#endif
+ if (ixgbe_maybe_stop_tx(tx_ring, count + 3)) {
+ tx_ring->tx_stats.tx_busy++;
+ return NETDEV_TX_BUSY;
+ }
- protocol = vlan_get_protocol(skb);
+#ifdef CONFIG_PCI_IOV
+ if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
+ tx_flags |= IXGBE_TX_FLAGS_TXSW;
+#endif
+ /* if we have a HW VLAN tag being added default to the HW one */
if (vlan_tx_tag_present(skb)) {
- tx_flags |= vlan_tx_tag_get(skb);
- if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
- tx_flags &= ~IXGBE_TX_FLAGS_VLAN_PRIO_MASK;
- tx_flags |= tx_ring->dcb_tc << 13;
+ tx_flags |= vlan_tx_tag_get(skb) << IXGBE_TX_FLAGS_VLAN_SHIFT;
+ tx_flags |= IXGBE_TX_FLAGS_HW_VLAN;
+ /* else if it is a SW VLAN check the next protocol and store the tag */
+ } else if (protocol == __constant_htons(ETH_P_8021Q)) {
+ struct vlan_hdr *vhdr, _vhdr;
+ vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), &_vhdr);
+ if (!vhdr)
+ goto out_drop;
+
+ protocol = vhdr->h_vlan_encapsulated_proto;
+ tx_flags |= ntohs(vhdr->h_vlan_TCI) << IXGBE_TX_FLAGS_VLAN_SHIFT;
+ tx_flags |= IXGBE_TX_FLAGS_SW_VLAN;
+ }
+
+ /* DCB maps skb priorities 0-7 onto 3 bit PCP of VLAN tag. */
+ if ((adapter->flags & IXGBE_FLAG_DCB_ENABLED) &&
+ ((tx_flags & (IXGBE_TX_FLAGS_HW_VLAN | IXGBE_TX_FLAGS_SW_VLAN)) ||
+ (skb->priority != TC_PRIO_CONTROL))) {
+ tx_flags &= ~IXGBE_TX_FLAGS_VLAN_PRIO_MASK;
+ tx_flags |= (skb->priority & 0x7) <<
+ IXGBE_TX_FLAGS_VLAN_PRIO_SHIFT;
+ if (tx_flags & IXGBE_TX_FLAGS_SW_VLAN) {
+ struct vlan_ethhdr *vhdr;
+ if (skb_header_cloned(skb) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ goto out_drop;
+ vhdr = (struct vlan_ethhdr *)skb->data;
+ vhdr->h_vlan_TCI = htons(tx_flags >>
+ IXGBE_TX_FLAGS_VLAN_SHIFT);
+ } else {
+ tx_flags |= IXGBE_TX_FLAGS_HW_VLAN;
}
- tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT;
- tx_flags |= IXGBE_TX_FLAGS_VLAN;
- } else if (adapter->flags & IXGBE_FLAG_DCB_ENABLED &&
- skb->priority != TC_PRIO_CONTROL) {
- tx_flags |= tx_ring->dcb_tc << 13;
- tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT;
- tx_flags |= IXGBE_TX_FLAGS_VLAN;
}
-#ifdef IXGBE_FCOE
- /* for FCoE with DCB, we force the priority to what
- * was specified by the switch */
- if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED &&
- (protocol == htons(ETH_P_FCOE)))
- tx_flags |= IXGBE_TX_FLAGS_FCOE;
-#endif
+ /* record the location of the first descriptor for this packet */
+ first = &tx_ring->tx_buffer_info[tx_ring->next_to_use];
- /* four things can cause us to need a context descriptor */
- if (skb_is_gso(skb) ||
- (skb->ip_summed == CHECKSUM_PARTIAL) ||
- (tx_flags & IXGBE_TX_FLAGS_VLAN) ||
- (tx_flags & IXGBE_TX_FLAGS_FCOE))
- count++;
-
- count += TXD_USE_COUNT(skb_headlen(skb));
- for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
- count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
+#ifdef IXGBE_FCOE
+ /* setup tx offload for FCoE */
+ if ((protocol == __constant_htons(ETH_P_FCOE)) &&
+ (adapter->flags & IXGBE_FLAG_FCOE_ENABLED)) {
+ tso = ixgbe_fso(tx_ring, skb, tx_flags, &hdr_len);
+ if (tso < 0)
+ goto out_drop;
+ else if (tso)
+ tx_flags |= IXGBE_TX_FLAGS_FSO |
+ IXGBE_TX_FLAGS_FCOE;
+ else
+ tx_flags |= IXGBE_TX_FLAGS_FCOE;
- if (ixgbe_maybe_stop_tx(tx_ring, count)) {
- tx_ring->tx_stats.tx_busy++;
- return NETDEV_TX_BUSY;
+ goto xmit_fcoe;
}
- first = tx_ring->next_to_use;
- if (tx_flags & IXGBE_TX_FLAGS_FCOE) {
-#ifdef IXGBE_FCOE
- /* setup tx offload for FCoE */
- tso = ixgbe_fso(adapter, tx_ring, skb, tx_flags, &hdr_len);
- if (tso < 0) {
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
- if (tso)
- tx_flags |= IXGBE_TX_FLAGS_FSO;
#endif /* IXGBE_FCOE */
- } else {
- if (protocol == htons(ETH_P_IP))
- tx_flags |= IXGBE_TX_FLAGS_IPV4;
- tso = ixgbe_tso(adapter, tx_ring, skb, tx_flags, &hdr_len,
- protocol);
- if (tso < 0) {
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
+ /* setup IPv4/IPv6 offloads */
+ if (protocol == __constant_htons(ETH_P_IP))
+ tx_flags |= IXGBE_TX_FLAGS_IPV4;
+
+ tso = ixgbe_tso(tx_ring, skb, tx_flags, protocol, &hdr_len);
+ if (tso < 0)
+ goto out_drop;
+ else if (tso)
+ tx_flags |= IXGBE_TX_FLAGS_TSO;
+ else if (ixgbe_tx_csum(tx_ring, skb, tx_flags, protocol))
+ tx_flags |= IXGBE_TX_FLAGS_CSUM;
+
+ /* add the ATR filter if ATR is on */
+ if (test_bit(__IXGBE_TX_FDIR_INIT_DONE, &tx_ring->state))
+ ixgbe_atr(tx_ring, skb, tx_flags, protocol);
- if (tso)
- tx_flags |= IXGBE_TX_FLAGS_TSO;
- else if (ixgbe_tx_csum(adapter, tx_ring, skb, tx_flags,
- protocol) &&
- (skb->ip_summed == CHECKSUM_PARTIAL))
- tx_flags |= IXGBE_TX_FLAGS_CSUM;
- }
+#ifdef IXGBE_FCOE
+xmit_fcoe:
+#endif /* IXGBE_FCOE */
+ ixgbe_tx_map(tx_ring, skb, first, tx_flags, hdr_len);
- count = ixgbe_tx_map(adapter, tx_ring, skb, tx_flags, first, hdr_len);
- if (count) {
- /* add the ATR filter if ATR is on */
- if (test_bit(__IXGBE_TX_FDIR_INIT_DONE, &tx_ring->state))
- ixgbe_atr(tx_ring, skb, tx_flags, protocol);
- ixgbe_tx_queue(tx_ring, tx_flags, count, skb->len, hdr_len);
- ixgbe_maybe_stop_tx(tx_ring, DESC_NEEDED);
+ ixgbe_maybe_stop_tx(tx_ring, DESC_NEEDED);
- } else {
- dev_kfree_skb_any(skb);
- tx_ring->tx_buffer_info[first].time_stamp = 0;
- tx_ring->next_to_use = first;
- }
+ return NETDEV_TX_OK;
+out_drop:
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -7141,7 +7011,7 @@ static void ixgbe_netpoll(struct net_device *netdev)
int num_q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
for (i = 0; i < num_q_vectors; i++) {
struct ixgbe_q_vector *q_vector = adapter->q_vector[i];
- ixgbe_msix_clean_many(0, q_vector);
+ ixgbe_msix_clean_rings(0, q_vector);
}
} else {
ixgbe_intr(adapter->pdev->irq, netdev);
@@ -7198,6 +7068,193 @@ static struct rtnl_link_stats64 *ixgbe_get_stats64(struct net_device *netdev,
return stats;
}
+/* ixgbe_validate_rtr - verify 802.1Qp to Rx packet buffer mapping is valid.
+ * #adapter: pointer to ixgbe_adapter
+ * @tc: number of traffic classes currently enabled
+ *
+ * Configure a valid 802.1Qp to Rx packet buffer mapping ie confirm
+ * 802.1Q priority maps to a packet buffer that exists.
+ */
+static void ixgbe_validate_rtr(struct ixgbe_adapter *adapter, u8 tc)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 reg, rsave;
+ int i;
+
+ /* 82598 have a static priority to TC mapping that can not
+ * be changed so no validation is needed.
+ */
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ return;
+
+ reg = IXGBE_READ_REG(hw, IXGBE_RTRUP2TC);
+ rsave = reg;
+
+ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+ u8 up2tc = reg >> (i * IXGBE_RTRUP2TC_UP_SHIFT);
+
+ /* If up2tc is out of bounds default to zero */
+ if (up2tc > tc)
+ reg &= ~(0x7 << IXGBE_RTRUP2TC_UP_SHIFT);
+ }
+
+ if (reg != rsave)
+ IXGBE_WRITE_REG(hw, IXGBE_RTRUP2TC, reg);
+
+ return;
+}
+
+
+/* ixgbe_setup_tc - routine to configure net_device for multiple traffic
+ * classes.
+ *
+ * @netdev: net device to configure
+ * @tc: number of traffic classes to enable
+ */
+int ixgbe_setup_tc(struct net_device *dev, u8 tc)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(dev);
+ struct ixgbe_hw *hw = &adapter->hw;
+
+ /* Multiple traffic classes requires multiple queues */
+ if (!(adapter->flags & IXGBE_FLAG_MSIX_ENABLED)) {
+ e_err(drv, "Enable failed, needs MSI-X\n");
+ return -EINVAL;
+ }
+
+ /* Hardware supports up to 8 traffic classes */
+ if (tc > adapter->dcb_cfg.num_tcs.pg_tcs ||
+ (hw->mac.type == ixgbe_mac_82598EB && tc < MAX_TRAFFIC_CLASS))
+ return -EINVAL;
+
+ /* Hardware has to reinitialize queues and interrupts to
+ * match packet buffer alignment. Unfortunantly, the
+ * hardware is not flexible enough to do this dynamically.
+ */
+ if (netif_running(dev))
+ ixgbe_close(dev);
+ ixgbe_clear_interrupt_scheme(adapter);
+
+ if (tc) {
+ netdev_set_num_tc(dev, tc);
+ adapter->last_lfc_mode = adapter->hw.fc.current_mode;
+
+ adapter->flags |= IXGBE_FLAG_DCB_ENABLED;
+ adapter->flags &= ~IXGBE_FLAG_FDIR_HASH_CAPABLE;
+
+ if (adapter->hw.mac.type == ixgbe_mac_82598EB)
+ adapter->hw.fc.requested_mode = ixgbe_fc_none;
+ } else {
+ netdev_reset_tc(dev);
+
+ adapter->hw.fc.requested_mode = adapter->last_lfc_mode;
+
+ adapter->flags &= ~IXGBE_FLAG_DCB_ENABLED;
+ adapter->flags |= IXGBE_FLAG_FDIR_HASH_CAPABLE;
+
+ adapter->temp_dcb_cfg.pfc_mode_enable = false;
+ adapter->dcb_cfg.pfc_mode_enable = false;
+ }
+
+ ixgbe_init_interrupt_scheme(adapter);
+ ixgbe_validate_rtr(adapter, tc);
+ if (netif_running(dev))
+ ixgbe_open(dev);
+
+ return 0;
+}
+
+void ixgbe_do_reset(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ if (netif_running(netdev))
+ ixgbe_reinit_locked(adapter);
+ else
+ ixgbe_reset(adapter);
+}
+
+static u32 ixgbe_fix_features(struct net_device *netdev, u32 data)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+#ifdef CONFIG_DCB
+ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED)
+ data &= ~NETIF_F_HW_VLAN_RX;
+#endif
+
+ /* return error if RXHASH is being enabled when RSS is not supported */
+ if (!(adapter->flags & IXGBE_FLAG_RSS_ENABLED))
+ data &= ~NETIF_F_RXHASH;
+
+ /* If Rx checksum is disabled, then RSC/LRO should also be disabled */
+ if (!(data & NETIF_F_RXCSUM))
+ data &= ~NETIF_F_LRO;
+
+ /* Turn off LRO if not RSC capable or invalid ITR settings */
+ if (!(adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE)) {
+ data &= ~NETIF_F_LRO;
+ } else if (!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) &&
+ (adapter->rx_itr_setting != 1 &&
+ adapter->rx_itr_setting > IXGBE_MAX_RSC_INT_RATE)) {
+ data &= ~NETIF_F_LRO;
+ e_info(probe, "rx-usecs set too low, not enabling RSC\n");
+ }
+
+ return data;
+}
+
+static int ixgbe_set_features(struct net_device *netdev, u32 data)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ bool need_reset = false;
+
+ /* If Rx checksum is disabled, then RSC/LRO should also be disabled */
+ if (!(data & NETIF_F_RXCSUM))
+ adapter->flags &= ~IXGBE_FLAG_RX_CSUM_ENABLED;
+ else
+ adapter->flags |= IXGBE_FLAG_RX_CSUM_ENABLED;
+
+ /* Make sure RSC matches LRO, reset if change */
+ if (!!(data & NETIF_F_LRO) !=
+ !!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)) {
+ adapter->flags2 ^= IXGBE_FLAG2_RSC_ENABLED;
+ switch (adapter->hw.mac.type) {
+ case ixgbe_mac_X540:
+ case ixgbe_mac_82599EB:
+ need_reset = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Check if Flow Director n-tuple support was enabled or disabled. If
+ * the state changed, we need to reset.
+ */
+ if (!(adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE)) {
+ /* turn off ATR, enable perfect filters and reset */
+ if (data & NETIF_F_NTUPLE) {
+ adapter->flags &= ~IXGBE_FLAG_FDIR_HASH_CAPABLE;
+ adapter->flags |= IXGBE_FLAG_FDIR_PERFECT_CAPABLE;
+ need_reset = true;
+ }
+ } else if (!(data & NETIF_F_NTUPLE)) {
+ /* turn off Flow Director, set ATR and reset */
+ adapter->flags &= ~IXGBE_FLAG_FDIR_PERFECT_CAPABLE;
+ if ((adapter->flags & IXGBE_FLAG_RSS_ENABLED) &&
+ !(adapter->flags & IXGBE_FLAG_DCB_ENABLED))
+ adapter->flags |= IXGBE_FLAG_FDIR_HASH_CAPABLE;
+ need_reset = true;
+ }
+
+ if (need_reset)
+ ixgbe_do_reset(netdev);
+
+ return 0;
+
+}
static const struct net_device_ops ixgbe_netdev_ops = {
.ndo_open = ixgbe_open,
@@ -7205,7 +7262,6 @@ static const struct net_device_ops ixgbe_netdev_ops = {
.ndo_start_xmit = ixgbe_xmit_frame,
.ndo_select_queue = ixgbe_select_queue,
.ndo_set_rx_mode = ixgbe_set_rx_mode,
- .ndo_set_multicast_list = ixgbe_set_rx_mode,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = ixgbe_set_mac,
.ndo_change_mtu = ixgbe_change_mtu,
@@ -7216,11 +7272,10 @@ static const struct net_device_ops ixgbe_netdev_ops = {
.ndo_set_vf_mac = ixgbe_ndo_set_vf_mac,
.ndo_set_vf_vlan = ixgbe_ndo_set_vf_vlan,
.ndo_set_vf_tx_rate = ixgbe_ndo_set_vf_bw,
+ .ndo_set_vf_spoofchk = ixgbe_ndo_set_vf_spoofchk,
.ndo_get_vf_config = ixgbe_ndo_get_vf_config,
.ndo_get_stats64 = ixgbe_get_stats64,
-#ifdef CONFIG_IXGBE_DCB
.ndo_setup_tc = ixgbe_setup_tc,
-#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = ixgbe_netpoll,
#endif
@@ -7232,6 +7287,8 @@ static const struct net_device_ops ixgbe_netdev_ops = {
.ndo_fcoe_disable = ixgbe_fcoe_disable,
.ndo_fcoe_get_wwn = ixgbe_fcoe_get_wwn,
#endif /* IXGBE_FCOE */
+ .ndo_set_features = ixgbe_set_features,
+ .ndo_fix_features = ixgbe_fix_features,
};
static void __devinit ixgbe_probe_vf(struct ixgbe_adapter *adapter,
@@ -7239,11 +7296,8 @@ static void __devinit ixgbe_probe_vf(struct ixgbe_adapter *adapter,
{
#ifdef CONFIG_PCI_IOV
struct ixgbe_hw *hw = &adapter->hw;
- int err;
- int num_vf_macvlans, i;
- struct vf_macvlans *mv_list;
- if (hw->mac.type == ixgbe_mac_82598EB || !max_vfs)
+ if (hw->mac.type == ixgbe_mac_82598EB)
return;
/* The 82599 supports up to 64 VFs per physical function
@@ -7252,60 +7306,7 @@ static void __devinit ixgbe_probe_vf(struct ixgbe_adapter *adapter,
* physical function
*/
adapter->num_vfs = (max_vfs > 63) ? 63 : max_vfs;
- adapter->flags |= IXGBE_FLAG_SRIOV_ENABLED;
- err = pci_enable_sriov(adapter->pdev, adapter->num_vfs);
- if (err) {
- e_err(probe, "Failed to enable PCI sriov: %d\n", err);
- goto err_novfs;
- }
-
- num_vf_macvlans = hw->mac.num_rar_entries -
- (IXGBE_MAX_PF_MACVLANS + 1 + adapter->num_vfs);
-
- adapter->mv_list = mv_list = kcalloc(num_vf_macvlans,
- sizeof(struct vf_macvlans),
- GFP_KERNEL);
- if (mv_list) {
- /* Initialize list of VF macvlans */
- INIT_LIST_HEAD(&adapter->vf_mvs.l);
- for (i = 0; i < num_vf_macvlans; i++) {
- mv_list->vf = -1;
- mv_list->free = true;
- mv_list->rar_entry = hw->mac.num_rar_entries -
- (i + adapter->num_vfs + 1);
- list_add(&mv_list->l, &adapter->vf_mvs.l);
- mv_list++;
- }
- }
-
- /* If call to enable VFs succeeded then allocate memory
- * for per VF control structures.
- */
- adapter->vfinfo =
- kcalloc(adapter->num_vfs,
- sizeof(struct vf_data_storage), GFP_KERNEL);
- if (adapter->vfinfo) {
- /* Now that we're sure SR-IOV is enabled
- * and memory allocated set up the mailbox parameters
- */
- ixgbe_init_mbx_params_pf(hw);
- memcpy(&hw->mbx.ops, ii->mbx_ops,
- sizeof(hw->mbx.ops));
-
- /* Disable RSC when in SR-IOV mode */
- adapter->flags2 &= ~(IXGBE_FLAG2_RSC_CAPABLE |
- IXGBE_FLAG2_RSC_ENABLED);
- return;
- }
-
- /* Oh oh */
- e_err(probe, "Unable to allocate memory for VF Data Storage - "
- "SRIOV disabled\n");
- pci_disable_sriov(adapter->pdev);
-
-err_novfs:
- adapter->flags &= ~IXGBE_FLAG_SRIOV_ENABLED;
- adapter->num_vfs = 0;
+ ixgbe_enable_sriov(adapter, ii);
#endif /* CONFIG_PCI_IOV */
}
@@ -7335,6 +7336,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
u16 device_caps;
#endif
u32 eec;
+ u16 wol_cap;
/* Catch broken hardware that put the wrong VF device ID in
* the PCIe SR-IOV capability.
@@ -7379,14 +7381,16 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
pci_set_master(pdev);
pci_save_state(pdev);
+#ifdef CONFIG_IXGBE_DCB
+ indices *= MAX_TRAFFIC_CLASS;
+#endif
+
if (ii->mac == ixgbe_mac_82598EB)
indices = min_t(unsigned int, indices, IXGBE_MAX_RSS_INDICES);
else
indices = min_t(unsigned int, indices, IXGBE_MAX_FDIR_INDICES);
-#if defined(CONFIG_DCB)
- indices = max_t(unsigned int, indices, IXGBE_MAX_DCB_INDICES);
-#elif defined(IXGBE_FCOE)
+#ifdef IXGBE_FCOE
indices += min_t(unsigned int, num_possible_cpus(),
IXGBE_MAX_FCOE_INDICES);
#endif
@@ -7497,20 +7501,23 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
netdev->features = NETIF_F_SG |
NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM |
NETIF_F_HW_VLAN_TX |
NETIF_F_HW_VLAN_RX |
- NETIF_F_HW_VLAN_FILTER;
+ NETIF_F_HW_VLAN_FILTER |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_RXHASH |
+ NETIF_F_RXCSUM;
- netdev->features |= NETIF_F_IPV6_CSUM;
- netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
- netdev->features |= NETIF_F_GRO;
- netdev->features |= NETIF_F_RXHASH;
+ netdev->hw_features = netdev->features;
switch (adapter->hw.mac.type) {
case ixgbe_mac_82599EB:
case ixgbe_mac_X540:
netdev->features |= NETIF_F_SCTP_CSUM;
+ netdev->hw_features |= NETIF_F_SCTP_CSUM |
+ NETIF_F_NTUPLE;
break;
default:
break;
@@ -7522,6 +7529,8 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
netdev->vlan_features |= NETIF_F_IPV6_CSUM;
netdev->vlan_features |= NETIF_F_SG;
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
adapter->flags &= ~(IXGBE_FLAG_RSS_ENABLED |
IXGBE_FLAG_DCB_ENABLED);
@@ -7549,6 +7558,8 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
netdev->vlan_features |= NETIF_F_HIGHDMA;
}
+ if (adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE)
+ netdev->hw_features |= NETIF_F_LRO;
if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)
netdev->features |= NETIF_F_LRO;
@@ -7568,13 +7579,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
goto err_eeprom;
}
- /* power down the optics for multispeed fiber and 82599 SFP+ fiber */
- if (hw->mac.ops.disable_tx_laser &&
- ((hw->phy.multispeed_fiber) ||
- ((hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) &&
- (hw->mac.type == ixgbe_mac_82599EB))))
- hw->mac.ops.disable_tx_laser(hw);
-
setup_timer(&adapter->service_timer, &ixgbe_service_timer,
(unsigned long) adapter);
@@ -7585,32 +7589,44 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
if (err)
goto err_sw_init;
- if (!(adapter->flags & IXGBE_FLAG_RSS_ENABLED))
+ if (!(adapter->flags & IXGBE_FLAG_RSS_ENABLED)) {
+ netdev->hw_features &= ~NETIF_F_RXHASH;
netdev->features &= ~NETIF_F_RXHASH;
+ }
+ /* WOL not supported for all but the following */
+ adapter->wol = 0;
switch (pdev->device) {
case IXGBE_DEV_ID_82599_SFP:
/* Only this subdevice supports WOL */
if (pdev->subsystem_device == IXGBE_SUBDEV_ID_82599_SFP)
- adapter->wol = (IXGBE_WUFC_MAG | IXGBE_WUFC_EX |
- IXGBE_WUFC_MC | IXGBE_WUFC_BC);
+ adapter->wol = IXGBE_WUFC_MAG;
break;
case IXGBE_DEV_ID_82599_COMBO_BACKPLANE:
/* All except this subdevice support WOL */
if (pdev->subsystem_device != IXGBE_SUBDEV_ID_82599_KX4_KR_MEZZ)
- adapter->wol = (IXGBE_WUFC_MAG | IXGBE_WUFC_EX |
- IXGBE_WUFC_MC | IXGBE_WUFC_BC);
+ adapter->wol = IXGBE_WUFC_MAG;
break;
case IXGBE_DEV_ID_82599_KX4:
- adapter->wol = (IXGBE_WUFC_MAG | IXGBE_WUFC_EX |
- IXGBE_WUFC_MC | IXGBE_WUFC_BC);
+ adapter->wol = IXGBE_WUFC_MAG;
break;
- default:
- adapter->wol = 0;
+ case IXGBE_DEV_ID_X540T:
+ /* Check eeprom to see if it is enabled */
+ hw->eeprom.ops.read(hw, 0x2c, &adapter->eeprom_cap);
+ wol_cap = adapter->eeprom_cap & IXGBE_DEVICE_CAPS_WOL_MASK;
+
+ if ((wol_cap == IXGBE_DEVICE_CAPS_WOL_PORT0_1) ||
+ ((wol_cap == IXGBE_DEVICE_CAPS_WOL_PORT0) &&
+ (hw->bus.func == 0)))
+ adapter->wol = IXGBE_WUFC_MAG;
break;
}
device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
+ /* save off EEPROM version number */
+ hw->eeprom.ops.read(hw, 0x2e, &adapter->eeprom_verh);
+ hw->eeprom.ops.read(hw, 0x2d, &adapter->eeprom_verl);
+
/* pick up the PCI bus settings for reporting later */
hw->mac.ops.get_bus_info(hw);
@@ -7643,9 +7659,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
"is required.\n");
}
- /* save off EEPROM version number */
- hw->eeprom.ops.read(hw, 0x29, &adapter->eeprom_version);
-
/* reset the hardware with the new settings */
err = hw->mac.ops.start_hw(hw);
@@ -7663,6 +7676,13 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
if (err)
goto err_register;
+ /* power down the optics for multispeed fiber and 82599 SFP+ fiber */
+ if (hw->mac.ops.disable_tx_laser &&
+ ((hw->phy.multispeed_fiber) ||
+ ((hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) &&
+ (hw->mac.type == ixgbe_mac_82599EB))))
+ hw->mac.ops.disable_tx_laser(hw);
+
/* carrier off reporting is important to ethtool even BEFORE open */
netif_carrier_off(netdev);
@@ -7678,6 +7698,13 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
ixgbe_vf_configuration(pdev, (i | 0x10000000));
}
+ /* firmware requires driver version to be 0xFFFFFFFF
+ * since os does not support feature
+ */
+ if (hw->mac.ops.set_fw_drv_ver)
+ hw->mac.ops.set_fw_drv_ver(hw, 0xFF, 0xFF, 0xFF,
+ 0xFF);
+
/* add san mac addr to netdev */
ixgbe_add_sanmac_netdev(netdev);
@@ -7742,8 +7769,13 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev)
if (netdev->reg_state == NETREG_REGISTERED)
unregister_netdev(netdev);
- if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
- ixgbe_disable_sriov(adapter);
+ if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) {
+ if (!(ixgbe_check_vf_assignment(adapter)))
+ ixgbe_disable_sriov(adapter);
+ else
+ e_dev_warn("Unloading driver while VFs are assigned "
+ "- VFs will not be deallocated\n");
+ }
ixgbe_clear_interrupt_scheme(adapter);
@@ -7776,6 +7808,91 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
struct net_device *netdev = adapter->netdev;
+#ifdef CONFIG_PCI_IOV
+ struct pci_dev *bdev, *vfdev;
+ u32 dw0, dw1, dw2, dw3;
+ int vf, pos;
+ u16 req_id, pf_func;
+
+ if (adapter->hw.mac.type == ixgbe_mac_82598EB ||
+ adapter->num_vfs == 0)
+ goto skip_bad_vf_detection;
+
+ bdev = pdev->bus->self;
+ while (bdev && (bdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT))
+ bdev = bdev->bus->self;
+
+ if (!bdev)
+ goto skip_bad_vf_detection;
+
+ pos = pci_find_ext_capability(bdev, PCI_EXT_CAP_ID_ERR);
+ if (!pos)
+ goto skip_bad_vf_detection;
+
+ pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG, &dw0);
+ pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 4, &dw1);
+ pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 8, &dw2);
+ pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 12, &dw3);
+
+ req_id = dw1 >> 16;
+ /* On the 82599 if bit 7 of the requestor ID is set then it's a VF */
+ if (!(req_id & 0x0080))
+ goto skip_bad_vf_detection;
+
+ pf_func = req_id & 0x01;
+ if ((pf_func & 1) == (pdev->devfn & 1)) {
+ unsigned int device_id;
+
+ vf = (req_id & 0x7F) >> 1;
+ e_dev_err("VF %d has caused a PCIe error\n", vf);
+ e_dev_err("TLP: dw0: %8.8x\tdw1: %8.8x\tdw2: "
+ "%8.8x\tdw3: %8.8x\n",
+ dw0, dw1, dw2, dw3);
+ switch (adapter->hw.mac.type) {
+ case ixgbe_mac_82599EB:
+ device_id = IXGBE_82599_VF_DEVICE_ID;
+ break;
+ case ixgbe_mac_X540:
+ device_id = IXGBE_X540_VF_DEVICE_ID;
+ break;
+ default:
+ device_id = 0;
+ break;
+ }
+
+ /* Find the pci device of the offending VF */
+ vfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, device_id, NULL);
+ while (vfdev) {
+ if (vfdev->devfn == (req_id & 0xFF))
+ break;
+ vfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID,
+ device_id, vfdev);
+ }
+ /*
+ * There's a slim chance the VF could have been hot plugged,
+ * so if it is no longer present we don't need to issue the
+ * VFLR. Just clean up the AER in that case.
+ */
+ if (vfdev) {
+ e_dev_err("Issuing VFLR to VF %d\n", vf);
+ pci_write_config_dword(vfdev, 0xA8, 0x00008000);
+ }
+
+ pci_cleanup_aer_uncorrect_error_status(pdev);
+ }
+
+ /*
+ * Even though the error may have occurred on the other port
+ * we still need to increment the vf error reference count for
+ * both ports because the I/O resume function will be called
+ * for both of them.
+ */
+ adapter->vferr_refcount++;
+
+ return PCI_ERS_RESULT_RECOVERED;
+
+skip_bad_vf_detection:
+#endif /* CONFIG_PCI_IOV */
netif_device_detach(netdev);
if (state == pci_channel_io_perm_failure)
@@ -7838,13 +7955,17 @@ static void ixgbe_io_resume(struct pci_dev *pdev)
struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
struct net_device *netdev = adapter->netdev;
- if (netif_running(netdev)) {
- if (ixgbe_up(adapter)) {
- e_info(probe, "ixgbe_up failed after reset\n");
- return;
- }
+#ifdef CONFIG_PCI_IOV
+ if (adapter->vferr_refcount) {
+ e_info(drv, "Resuming after VF err\n");
+ adapter->vferr_refcount--;
+ return;
}
+#endif
+ if (netif_running(netdev))
+ ixgbe_up(adapter);
+
netif_device_attach(netdev);
}
diff --git a/drivers/net/ixgbe/ixgbe_mbx.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c
index 1ff0eefcfd0a..3f725d48336d 100644
--- a/drivers/net/ixgbe/ixgbe_mbx.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c
@@ -38,7 +38,7 @@
* @size: Length of buffer
* @mbx_id: id of mailbox to read
*
- * returns SUCCESS if it successfuly read message from buffer
+ * returns SUCCESS if it successfully read message from buffer
**/
s32 ixgbe_read_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 mbx_id)
{
diff --git a/drivers/net/ixgbe/ixgbe_mbx.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
index b239bdac38da..b239bdac38da 100644
--- a/drivers/net/ixgbe/ixgbe_mbx.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
diff --git a/drivers/net/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
index 735f686c3b36..9a56fd74e673 100644
--- a/drivers/net/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
@@ -39,7 +39,7 @@ static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data);
static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw);
static s32 ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data);
static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data);
-static s32 ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl);
+static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl);
static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl);
static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data);
static bool ixgbe_get_i2c_data(u32 *i2cctl);
@@ -1205,7 +1205,7 @@ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
* @data: value read
*
* Performs byte read operation to SFP module's EEPROM over I2C interface at
- * a specified deivce address.
+ * a specified device address.
**/
s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
u8 dev_addr, u8 *data)
@@ -1215,6 +1215,7 @@ s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
u32 retry = 0;
u16 swfw_mask = 0;
bool nack = 1;
+ *data = 0;
if (IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1)
swfw_mask = IXGBE_GSSR_PHY1_SM;
@@ -1419,19 +1420,15 @@ static void ixgbe_i2c_stop(struct ixgbe_hw *hw)
**/
static s32 ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data)
{
- s32 status = 0;
s32 i;
bool bit = 0;
for (i = 7; i >= 0; i--) {
- status = ixgbe_clock_in_i2c_bit(hw, &bit);
+ ixgbe_clock_in_i2c_bit(hw, &bit);
*data |= bit << i;
-
- if (status != 0)
- break;
}
- return status;
+ return 0;
}
/**
@@ -1472,16 +1469,14 @@ static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data)
**/
static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw)
{
- s32 status;
+ s32 status = 0;
u32 i = 0;
u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL);
u32 timeout = 10;
bool ack = 1;
- status = ixgbe_raise_i2c_clk(hw, &i2cctl);
+ ixgbe_raise_i2c_clk(hw, &i2cctl);
- if (status != 0)
- goto out;
/* Minimum high period of clock is 4us */
udelay(IXGBE_I2C_T_HIGH);
@@ -1507,7 +1502,6 @@ static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw)
/* Minimum low period of clock is 4.7 us */
udelay(IXGBE_I2C_T_LOW);
-out:
return status;
}
@@ -1520,10 +1514,9 @@ out:
**/
static s32 ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data)
{
- s32 status;
u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL);
- status = ixgbe_raise_i2c_clk(hw, &i2cctl);
+ ixgbe_raise_i2c_clk(hw, &i2cctl);
/* Minimum high period of clock is 4us */
udelay(IXGBE_I2C_T_HIGH);
@@ -1536,7 +1529,7 @@ static s32 ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data)
/* Minimum low period of clock is 4.7 us */
udelay(IXGBE_I2C_T_LOW);
- return status;
+ return 0;
}
/**
@@ -1553,7 +1546,7 @@ static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data)
status = ixgbe_set_i2c_data(hw, &i2cctl, data);
if (status == 0) {
- status = ixgbe_raise_i2c_clk(hw, &i2cctl);
+ ixgbe_raise_i2c_clk(hw, &i2cctl);
/* Minimum high period of clock is 4us */
udelay(IXGBE_I2C_T_HIGH);
@@ -1578,18 +1571,15 @@ static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data)
*
* Raises the I2C clock line '0'->'1'
**/
-static s32 ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
+static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
{
- s32 status = 0;
-
*i2cctl |= IXGBE_I2C_CLK_OUT;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl);
+ IXGBE_WRITE_FLUSH(hw);
/* SCL rise time (1000ns) */
udelay(IXGBE_I2C_T_RISE);
-
- return status;
}
/**
@@ -1605,6 +1595,7 @@ static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
*i2cctl &= ~IXGBE_I2C_CLK_OUT;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl);
+ IXGBE_WRITE_FLUSH(hw);
/* SCL fall time (300ns) */
udelay(IXGBE_I2C_T_FALL);
@@ -1628,6 +1619,7 @@ static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data)
*i2cctl &= ~IXGBE_I2C_DATA_OUT;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl);
+ IXGBE_WRITE_FLUSH(hw);
/* Data rise/fall (1000ns/300ns) and set-up time (250ns) */
udelay(IXGBE_I2C_T_RISE + IXGBE_I2C_T_FALL + IXGBE_I2C_T_SU_DATA);
diff --git a/drivers/net/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
index 197bdd13106a..197bdd13106a 100644
--- a/drivers/net/ixgbe/ixgbe_phy.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
diff --git a/drivers/net/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
index ac99b0458fe2..00fcd39ad666 100644
--- a/drivers/net/ixgbe/ixgbe_sriov.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
@@ -40,9 +40,176 @@
#endif
#include "ixgbe.h"
-
+#include "ixgbe_type.h"
#include "ixgbe_sriov.h"
+#ifdef CONFIG_PCI_IOV
+static int ixgbe_find_enabled_vfs(struct ixgbe_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ struct pci_dev *pvfdev;
+ u16 vf_devfn = 0;
+ int device_id;
+ int vfs_found = 0;
+
+ switch (adapter->hw.mac.type) {
+ case ixgbe_mac_82599EB:
+ device_id = IXGBE_DEV_ID_82599_VF;
+ break;
+ case ixgbe_mac_X540:
+ device_id = IXGBE_DEV_ID_X540_VF;
+ break;
+ default:
+ device_id = 0;
+ break;
+ }
+
+ vf_devfn = pdev->devfn + 0x80;
+ pvfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, device_id, NULL);
+ while (pvfdev) {
+ if (pvfdev->devfn == vf_devfn)
+ vfs_found++;
+ vf_devfn += 2;
+ pvfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID,
+ device_id, pvfdev);
+ }
+
+ return vfs_found;
+}
+
+void ixgbe_enable_sriov(struct ixgbe_adapter *adapter,
+ const struct ixgbe_info *ii)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ int err = 0;
+ int num_vf_macvlans, i;
+ struct vf_macvlans *mv_list;
+ int pre_existing_vfs = 0;
+
+ pre_existing_vfs = ixgbe_find_enabled_vfs(adapter);
+ if (!pre_existing_vfs && !adapter->num_vfs)
+ return;
+
+ /* If there are pre-existing VFs then we have to force
+ * use of that many because they were not deleted the last
+ * time someone removed the PF driver. That would have
+ * been because they were allocated to guest VMs and can't
+ * be removed. Go ahead and just re-enable the old amount.
+ * If the user wants to change the number of VFs they can
+ * use ethtool while making sure no VFs are allocated to
+ * guest VMs... i.e. the right way.
+ */
+ if (pre_existing_vfs) {
+ adapter->num_vfs = pre_existing_vfs;
+ dev_warn(&adapter->pdev->dev, "Virtual Functions already "
+ "enabled for this device - Please reload all "
+ "VF drivers to avoid spoofed packet errors\n");
+ } else {
+ err = pci_enable_sriov(adapter->pdev, adapter->num_vfs);
+ }
+ if (err) {
+ e_err(probe, "Failed to enable PCI sriov: %d\n", err);
+ goto err_novfs;
+ }
+ adapter->flags |= IXGBE_FLAG_SRIOV_ENABLED;
+
+ e_info(probe, "SR-IOV enabled with %d VFs\n", adapter->num_vfs);
+
+ num_vf_macvlans = hw->mac.num_rar_entries -
+ (IXGBE_MAX_PF_MACVLANS + 1 + adapter->num_vfs);
+
+ adapter->mv_list = mv_list = kcalloc(num_vf_macvlans,
+ sizeof(struct vf_macvlans),
+ GFP_KERNEL);
+ if (mv_list) {
+ /* Initialize list of VF macvlans */
+ INIT_LIST_HEAD(&adapter->vf_mvs.l);
+ for (i = 0; i < num_vf_macvlans; i++) {
+ mv_list->vf = -1;
+ mv_list->free = true;
+ mv_list->rar_entry = hw->mac.num_rar_entries -
+ (i + adapter->num_vfs + 1);
+ list_add(&mv_list->l, &adapter->vf_mvs.l);
+ mv_list++;
+ }
+ }
+
+ /* If call to enable VFs succeeded then allocate memory
+ * for per VF control structures.
+ */
+ adapter->vfinfo =
+ kcalloc(adapter->num_vfs,
+ sizeof(struct vf_data_storage), GFP_KERNEL);
+ if (adapter->vfinfo) {
+ /* Now that we're sure SR-IOV is enabled
+ * and memory allocated set up the mailbox parameters
+ */
+ ixgbe_init_mbx_params_pf(hw);
+ memcpy(&hw->mbx.ops, ii->mbx_ops,
+ sizeof(hw->mbx.ops));
+
+ /* Disable RSC when in SR-IOV mode */
+ adapter->flags2 &= ~(IXGBE_FLAG2_RSC_CAPABLE |
+ IXGBE_FLAG2_RSC_ENABLED);
+ for (i = 0; i < adapter->num_vfs; i++)
+ adapter->vfinfo[i].spoofchk_enabled = true;
+ return;
+ }
+
+ /* Oh oh */
+ e_err(probe, "Unable to allocate memory for VF Data Storage - "
+ "SRIOV disabled\n");
+ pci_disable_sriov(adapter->pdev);
+
+err_novfs:
+ adapter->flags &= ~IXGBE_FLAG_SRIOV_ENABLED;
+ adapter->num_vfs = 0;
+}
+#endif /* #ifdef CONFIG_PCI_IOV */
+
+void ixgbe_disable_sriov(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 gcr;
+ u32 gpie;
+ u32 vmdctl;
+ int i;
+
+#ifdef CONFIG_PCI_IOV
+ /* disable iov and allow time for transactions to clear */
+ pci_disable_sriov(adapter->pdev);
+#endif
+
+ /* turn off device IOV mode */
+ gcr = IXGBE_READ_REG(hw, IXGBE_GCR_EXT);
+ gcr &= ~(IXGBE_GCR_EXT_SRIOV);
+ IXGBE_WRITE_REG(hw, IXGBE_GCR_EXT, gcr);
+ gpie = IXGBE_READ_REG(hw, IXGBE_GPIE);
+ gpie &= ~IXGBE_GPIE_VTMODE_MASK;
+ IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie);
+
+ /* set default pool back to 0 */
+ vmdctl = IXGBE_READ_REG(hw, IXGBE_VT_CTL);
+ vmdctl &= ~IXGBE_VT_CTL_POOL_MASK;
+ IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vmdctl);
+ IXGBE_WRITE_FLUSH(hw);
+
+ /* take a breather then clean up driver data */
+ msleep(100);
+
+ /* Release reference to VF devices */
+ for (i = 0; i < adapter->num_vfs; i++) {
+ if (adapter->vfinfo[i].vfdev)
+ pci_dev_put(adapter->vfinfo[i].vfdev);
+ }
+ kfree(adapter->vfinfo);
+ kfree(adapter->mv_list);
+ adapter->vfinfo = NULL;
+
+ adapter->num_vfs = 0;
+ adapter->flags &= ~IXGBE_FLAG_SRIOV_ENABLED;
+}
+
static int ixgbe_set_vf_multicasts(struct ixgbe_adapter *adapter,
int entries, u16 *hash_list, u32 vf)
{
@@ -273,11 +440,28 @@ static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter,
return 0;
}
+int ixgbe_check_vf_assignment(struct ixgbe_adapter *adapter)
+{
+#ifdef CONFIG_PCI_IOV
+ int i;
+ for (i = 0; i < adapter->num_vfs; i++) {
+ if (adapter->vfinfo[i].vfdev->dev_flags &
+ PCI_DEV_FLAGS_ASSIGNED)
+ return true;
+ }
+#endif
+ return false;
+}
+
int ixgbe_vf_configuration(struct pci_dev *pdev, unsigned int event_mask)
{
unsigned char vf_mac_addr[6];
struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
unsigned int vfn = (event_mask & 0x3f);
+ struct pci_dev *pvfdev;
+ unsigned int device_id;
+ u16 thisvf_devfn = (pdev->devfn + 0x80 + (vfn << 1)) |
+ (pdev->devfn & 1);
bool enable = ((event_mask & 0x10000000U) != 0);
@@ -290,6 +474,31 @@ int ixgbe_vf_configuration(struct pci_dev *pdev, unsigned int event_mask)
* for it later.
*/
memcpy(adapter->vfinfo[vfn].vf_mac_addresses, vf_mac_addr, 6);
+
+ switch (adapter->hw.mac.type) {
+ case ixgbe_mac_82599EB:
+ device_id = IXGBE_DEV_ID_82599_VF;
+ break;
+ case ixgbe_mac_X540:
+ device_id = IXGBE_DEV_ID_X540_VF;
+ break;
+ default:
+ device_id = 0;
+ break;
+ }
+
+ pvfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, device_id, NULL);
+ while (pvfdev) {
+ if (pvfdev->devfn == thisvf_devfn)
+ break;
+ pvfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID,
+ device_id, pvfdev);
+ }
+ if (pvfdev)
+ adapter->vfinfo[vfn].vfdev = pvfdev;
+ else
+ e_err(drv, "Couldn't find pci dev ptr for VF %4.4x\n",
+ thisvf_devfn);
}
return 0;
@@ -415,7 +624,13 @@ static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf)
vf);
retval = -1;
} else {
+ if (add)
+ adapter->vfinfo[vf].vlan_count++;
+ else if (adapter->vfinfo[vf].vlan_count)
+ adapter->vfinfo[vf].vlan_count--;
retval = ixgbe_set_vf_vlan(adapter, add, vid, vf);
+ if (!retval && adapter->vfinfo[vf].spoofchk_enabled)
+ hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf);
}
break;
case IXGBE_VF_SET_MACVLAN:
@@ -427,12 +642,8 @@ static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf)
* greater than 0 will indicate the VF is setting a
* macvlan MAC filter.
*/
- if (index > 0 && adapter->antispoofing_enabled) {
- hw->mac.ops.set_mac_anti_spoofing(hw, false,
- adapter->num_vfs);
- hw->mac.ops.set_vlan_anti_spoofing(hw, false, vf);
- adapter->antispoofing_enabled = false;
- }
+ if (index > 0 && adapter->vfinfo[vf].spoofchk_enabled)
+ ixgbe_ndo_set_vf_spoofchk(adapter->netdev, vf, false);
retval = ixgbe_set_vf_macvlan(adapter, vf, index,
(unsigned char *)(&msgbuf[1]));
break;
@@ -543,8 +754,9 @@ int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos)
goto out;
ixgbe_set_vmvir(adapter, vlan | (qos << VLAN_PRIO_SHIFT), vf);
ixgbe_set_vmolr(hw, vf, false);
- if (adapter->antispoofing_enabled)
+ if (adapter->vfinfo[vf].spoofchk_enabled)
hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf);
+ adapter->vfinfo[vf].vlan_count++;
adapter->vfinfo[vf].pf_vlan = vlan;
adapter->vfinfo[vf].pf_qos = qos;
dev_info(&adapter->pdev->dev,
@@ -563,6 +775,8 @@ int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos)
ixgbe_set_vmvir(adapter, vlan, vf);
ixgbe_set_vmolr(hw, vf, true);
hw->mac.ops.set_vlan_anti_spoofing(hw, false, vf);
+ if (adapter->vfinfo[vf].vlan_count)
+ adapter->vfinfo[vf].vlan_count--;
adapter->vfinfo[vf].pf_vlan = 0;
adapter->vfinfo[vf].pf_qos = 0;
}
@@ -605,6 +819,22 @@ static void ixgbe_set_vf_rate_limit(struct ixgbe_hw *hw, int vf, int tx_rate,
}
IXGBE_WRITE_REG(hw, IXGBE_RTTDQSEL, 2*vf); /* vf Y uses queue 2*Y */
+ /*
+ * Set global transmit compensation time to the MMW_SIZE in RTTBCNRM
+ * register. Typically MMW_SIZE=0x014 if 9728-byte jumbo is supported
+ * and 0x004 otherwise.
+ */
+ switch (hw->mac.type) {
+ case ixgbe_mac_82599EB:
+ IXGBE_WRITE_REG(hw, IXGBE_RTTBCNRM, 0x4);
+ break;
+ case ixgbe_mac_X540:
+ IXGBE_WRITE_REG(hw, IXGBE_RTTBCNRM, 0x14);
+ break;
+ default:
+ break;
+ }
+
IXGBE_WRITE_REG(hw, IXGBE_RTTBCNRC, bcnrc_val);
}
@@ -656,6 +886,32 @@ int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
return 0;
}
+int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int vf_target_reg = vf >> 3;
+ int vf_target_shift = vf % 8;
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 regval;
+
+ adapter->vfinfo[vf].spoofchk_enabled = setting;
+
+ regval = IXGBE_READ_REG(hw, IXGBE_PFVFSPOOF(vf_target_reg));
+ regval &= ~(1 << vf_target_shift);
+ regval |= (setting << vf_target_shift);
+ IXGBE_WRITE_REG(hw, IXGBE_PFVFSPOOF(vf_target_reg), regval);
+
+ if (adapter->vfinfo[vf].vlan_count) {
+ vf_target_shift += IXGBE_SPOOF_VLANAS_SHIFT;
+ regval = IXGBE_READ_REG(hw, IXGBE_PFVFSPOOF(vf_target_reg));
+ regval &= ~(1 << vf_target_shift);
+ regval |= (setting << vf_target_shift);
+ IXGBE_WRITE_REG(hw, IXGBE_PFVFSPOOF(vf_target_reg), regval);
+ }
+
+ return 0;
+}
+
int ixgbe_ndo_get_vf_config(struct net_device *netdev,
int vf, struct ifla_vf_info *ivi)
{
@@ -667,5 +923,6 @@ int ixgbe_ndo_get_vf_config(struct net_device *netdev,
ivi->tx_rate = adapter->vfinfo[vf].tx_rate;
ivi->vlan = adapter->vfinfo[vf].pf_vlan;
ivi->qos = adapter->vfinfo[vf].pf_qos;
+ ivi->spoofchk = adapter->vfinfo[vf].spoofchk_enabled;
return 0;
}
diff --git a/drivers/net/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
index 34175564bb78..df04f1a3857c 100644
--- a/drivers/net/ixgbe/ixgbe_sriov.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
@@ -38,9 +38,17 @@ int ixgbe_ndo_set_vf_mac(struct net_device *netdev, int queue, u8 *mac);
int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int queue, u16 vlan,
u8 qos);
int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate);
+int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting);
int ixgbe_ndo_get_vf_config(struct net_device *netdev,
int vf, struct ifla_vf_info *ivi);
void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter);
+void ixgbe_disable_sriov(struct ixgbe_adapter *adapter);
+int ixgbe_check_vf_assignment(struct ixgbe_adapter *adapter);
+#ifdef CONFIG_PCI_IOV
+void ixgbe_enable_sriov(struct ixgbe_adapter *adapter,
+ const struct ixgbe_info *ii);
+#endif
+
#endif /* _IXGBE_SRIOV_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
index fa43f2507f43..6c5cca808bd7 100644
--- a/drivers/net/ixgbe/ixgbe_type.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
@@ -59,12 +59,17 @@
#define IXGBE_SUBDEV_ID_82599_SFP 0x11A9
#define IXGBE_DEV_ID_82599_SFP_EM 0x1507
#define IXGBE_DEV_ID_82599_SFP_SF2 0x154D
+#define IXGBE_DEV_ID_82599EN_SFP 0x1557
#define IXGBE_DEV_ID_82599_XAUI_LOM 0x10FC
#define IXGBE_DEV_ID_82599_COMBO_BACKPLANE 0x10F8
#define IXGBE_SUBDEV_ID_82599_KX4_KR_MEZZ 0x000C
#define IXGBE_DEV_ID_82599_LS 0x154F
#define IXGBE_DEV_ID_X540T 0x1528
+/* VF Device IDs */
+#define IXGBE_DEV_ID_82599_VF 0x10ED
+#define IXGBE_DEV_ID_X540_VF 0x1515
+
/* General Registers */
#define IXGBE_CTRL 0x00000
#define IXGBE_STATUS 0x00008
@@ -400,6 +405,8 @@
#define IXGBE_WUPL_LENGTH_MASK 0xFFFF
/* DCB registers */
+#define MAX_TRAFFIC_CLASS 8
+#define X540_TRAFFIC_CLASS 4
#define IXGBE_RMCS 0x03D00
#define IXGBE_DPMCS 0x07F40
#define IXGBE_PDPMCS 0x0CD00
@@ -534,7 +541,7 @@
#define IXGBE_RTTBCNRC_RF_INT_SHIFT 14
#define IXGBE_RTTBCNRC_RF_INT_MASK \
(IXGBE_RTTBCNRC_RF_DEC_MASK << IXGBE_RTTBCNRC_RF_INT_SHIFT)
-
+#define IXGBE_RTTBCNRM 0x04980
/* FCoE DMA Context Registers */
#define IXGBE_FCPTRL 0x02410 /* FC User Desc. PTR Low */
@@ -707,6 +714,13 @@
#define IXGBE_HFDR 0x15FE8
#define IXGBE_FLEX_MNG 0x15800 /* 0x15800 - 0x15EFC */
+#define IXGBE_HICR_EN 0x01 /* Enable bit - RO */
+/* Driver sets this bit when done to put command in RAM */
+#define IXGBE_HICR_C 0x02
+#define IXGBE_HICR_SV 0x04 /* Status Validity */
+#define IXGBE_HICR_FW_RESET_ENABLE 0x40
+#define IXGBE_HICR_FW_RESET 0x80
+
/* PCI-E registers */
#define IXGBE_GCR 0x11000
#define IXGBE_GTV 0x11004
@@ -759,6 +773,7 @@
#define IXGBE_GCR_CAP_VER2 0x00040000
#define IXGBE_GCR_EXT_MSIX_EN 0x80000000
+#define IXGBE_GCR_EXT_BUFFERS_CLEAR 0x40000000
#define IXGBE_GCR_EXT_VT_MODE_16 0x00000001
#define IXGBE_GCR_EXT_VT_MODE_32 0x00000002
#define IXGBE_GCR_EXT_VT_MODE_64 0x00000003
@@ -975,6 +990,7 @@
#define IXGBE_CTRL_GIO_DIS 0x00000004 /* Global IO Master Disable bit */
#define IXGBE_CTRL_LNK_RST 0x00000008 /* Link Reset. Resets everything. */
#define IXGBE_CTRL_RST 0x04000000 /* Reset (SW) */
+#define IXGBE_CTRL_RST_MASK (IXGBE_CTRL_LNK_RST | IXGBE_CTRL_RST)
/* FACTPS */
#define IXGBE_FACTPS_LFS 0x40000000 /* LAN Function Select */
@@ -1118,6 +1134,27 @@
#define IXGBE_GPIE_VTMODE_32 0x00008000 /* 32 VFs 4 queues per VF */
#define IXGBE_GPIE_VTMODE_64 0x0000C000 /* 64 VFs 2 queues per VF */
+/* Packet Buffer Initialization */
+#define IXGBE_TXPBSIZE_20KB 0x00005000 /* 20KB Packet Buffer */
+#define IXGBE_TXPBSIZE_40KB 0x0000A000 /* 40KB Packet Buffer */
+#define IXGBE_RXPBSIZE_48KB 0x0000C000 /* 48KB Packet Buffer */
+#define IXGBE_RXPBSIZE_64KB 0x00010000 /* 64KB Packet Buffer */
+#define IXGBE_RXPBSIZE_80KB 0x00014000 /* 80KB Packet Buffer */
+#define IXGBE_RXPBSIZE_128KB 0x00020000 /* 128KB Packet Buffer */
+#define IXGBE_RXPBSIZE_MAX 0x00080000 /* 512KB Packet Buffer*/
+#define IXGBE_TXPBSIZE_MAX 0x00028000 /* 160KB Packet Buffer*/
+
+#define IXGBE_TXPKT_SIZE_MAX 0xA /* Max Tx Packet size */
+#define IXGBE_MAX_PB 8
+
+/* Packet buffer allocation strategies */
+enum {
+ PBA_STRATEGY_EQUAL = 0, /* Distribute PB space equally */
+#define PBA_STRATEGY_EQUAL PBA_STRATEGY_EQUAL
+ PBA_STRATEGY_WEIGHTED = 1, /* Weight front half of TCs */
+#define PBA_STRATEGY_WEIGHTED PBA_STRATEGY_WEIGHTED
+};
+
/* Transmit Flow Control status */
#define IXGBE_TFCS_TXOFF 0x00000001
#define IXGBE_TFCS_TXOFF0 0x00000100
@@ -1245,6 +1282,7 @@
#define IXGBE_EICR_LSC 0x00100000 /* Link Status Change */
#define IXGBE_EICR_LINKSEC 0x00200000 /* PN Threshold */
#define IXGBE_EICR_MNG 0x00400000 /* Manageability Event Interrupt */
+#define IXGBE_EICR_TS 0x00800000 /* Thermal Sensor Event */
#define IXGBE_EICR_GPI_SDP0 0x01000000 /* Gen Purpose Interrupt on SDP0 */
#define IXGBE_EICR_GPI_SDP1 0x02000000 /* Gen Purpose Interrupt on SDP1 */
#define IXGBE_EICR_GPI_SDP2 0x04000000 /* Gen Purpose Interrupt on SDP2 */
@@ -1279,6 +1317,7 @@
#define IXGBE_EIMS_MAILBOX IXGBE_EICR_MAILBOX /* VF to PF Mailbox Int */
#define IXGBE_EIMS_LSC IXGBE_EICR_LSC /* Link Status Change */
#define IXGBE_EIMS_MNG IXGBE_EICR_MNG /* MNG Event Interrupt */
+#define IXGBE_EIMS_TS IXGBE_EICR_TS /* Thermel Sensor Event */
#define IXGBE_EIMS_GPI_SDP0 IXGBE_EICR_GPI_SDP0 /* SDP0 Gen Purpose Int */
#define IXGBE_EIMS_GPI_SDP1 IXGBE_EICR_GPI_SDP1 /* SDP1 Gen Purpose Int */
#define IXGBE_EIMS_GPI_SDP2 IXGBE_EICR_GPI_SDP2 /* SDP2 Gen Purpose Int */
@@ -1720,6 +1759,10 @@
#define IXGBE_ALT_SAN_MAC_ADDR_CAPS_SANMAC 0x0 /* Alt. SAN MAC exists */
#define IXGBE_ALT_SAN_MAC_ADDR_CAPS_ALTWWN 0x1 /* Alt. WWN base exists */
+#define IXGBE_DEVICE_CAPS_WOL_PORT0_1 0x4 /* WoL supported on ports 0 & 1 */
+#define IXGBE_DEVICE_CAPS_WOL_PORT0 0x8 /* WoL supported on port 0 */
+#define IXGBE_DEVICE_CAPS_WOL_MASK 0xC /* Mask for WoL capabilities */
+
/* PCI Bus Info */
#define IXGBE_PCI_DEVICE_STATUS 0xAA
#define IXGBE_PCI_DEVICE_STATUS_TRANSACTION_PENDING 0x0020
@@ -1789,6 +1832,7 @@
#define IXGBE_RXCTRL_RXEN 0x00000001 /* Enable Receiver */
#define IXGBE_RXCTRL_DMBYPS 0x00000002 /* Descriptor Monitor Bypass */
#define IXGBE_RXDCTL_ENABLE 0x02000000 /* Enable specific Rx Queue */
+#define IXGBE_RXDCTL_SWFLSH 0x04000000 /* Rx Desc. write-back flushing */
#define IXGBE_RXDCTL_RLPMLMASK 0x00003FFF /* Only supported on the X540 */
#define IXGBE_RXDCTL_RLPML_EN 0x00008000
#define IXGBE_RXDCTL_VME 0x40000000 /* VLAN mode enable */
@@ -1806,6 +1850,7 @@
#define IXGBE_MFLCN_DPF 0x00000002 /* Discard Pause Frame */
#define IXGBE_MFLCN_RPFCE 0x00000004 /* Receive Priority FC Enable */
#define IXGBE_MFLCN_RFCE 0x00000008 /* Receive FC Enable */
+#define IXGBE_MFLCN_RPFCE_MASK 0x00000FF0 /* Receive FC Mask */
#define IXGBE_MFLCN_RPFCE_SHIFT 4
@@ -1860,6 +1905,7 @@
#define IXGBE_MTQC_32VF 0x8 /* 4 TX Queues per pool w/32VF's */
#define IXGBE_MTQC_64VF 0x4 /* 2 TX Queues per pool w/64VF's */
#define IXGBE_MTQC_8TC_8TQ 0xC /* 8 TC if RT_ENA or 8 TQ if VT_ENA */
+#define IXGBE_MTQC_4TC_4TQ 0x8 /* 4 TC if RT_ENA or 4 TQ if VT_ENA */
/* Receive Descriptor bit definitions */
#define IXGBE_RXD_STAT_DD 0x01 /* Descriptor Done */
@@ -2027,9 +2073,10 @@
#define IXGBE_VFLREC(_i) (0x00700 + (_i * 4))
enum ixgbe_fdir_pballoc_type {
- IXGBE_FDIR_PBALLOC_64K = 0,
- IXGBE_FDIR_PBALLOC_128K,
- IXGBE_FDIR_PBALLOC_256K,
+ IXGBE_FDIR_PBALLOC_NONE = 0,
+ IXGBE_FDIR_PBALLOC_64K = 1,
+ IXGBE_FDIR_PBALLOC_128K = 2,
+ IXGBE_FDIR_PBALLOC_256K = 3,
};
#define IXGBE_FDIR_PBALLOC_SIZE_SHIFT 16
@@ -2083,7 +2130,7 @@ enum ixgbe_fdir_pballoc_type {
#define IXGBE_FDIRCMD_CMD_ADD_FLOW 0x00000001
#define IXGBE_FDIRCMD_CMD_REMOVE_FLOW 0x00000002
#define IXGBE_FDIRCMD_CMD_QUERY_REM_FILT 0x00000003
-#define IXGBE_FDIRCMD_CMD_QUERY_REM_HASH 0x00000007
+#define IXGBE_FDIRCMD_FILTER_VALID 0x00000004
#define IXGBE_FDIRCMD_FILTER_UPDATE 0x00000008
#define IXGBE_FDIRCMD_IPv6DMATCH 0x00000010
#define IXGBE_FDIRCMD_L4TYPE_UDP 0x00000020
@@ -2102,6 +2149,44 @@ enum ixgbe_fdir_pballoc_type {
#define IXGBE_FDIR_INIT_DONE_POLL 10
#define IXGBE_FDIRCMD_CMD_POLL 10
+#define IXGBE_FDIR_DROP_QUEUE 127
+
+/* Manageablility Host Interface defines */
+#define IXGBE_HI_MAX_BLOCK_BYTE_LENGTH 1792 /* Num of bytes in range */
+#define IXGBE_HI_MAX_BLOCK_DWORD_LENGTH 448 /* Num of dwords in range */
+#define IXGBE_HI_COMMAND_TIMEOUT 500 /* Process HI command limit */
+
+/* CEM Support */
+#define FW_CEM_HDR_LEN 0x4
+#define FW_CEM_CMD_DRIVER_INFO 0xDD
+#define FW_CEM_CMD_DRIVER_INFO_LEN 0x5
+#define FW_CEM_CMD_RESERVED 0x0
+#define FW_CEM_UNUSED_VER 0x0
+#define FW_CEM_MAX_RETRIES 3
+#define FW_CEM_RESP_STATUS_SUCCESS 0x1
+
+/* Host Interface Command Structures */
+struct ixgbe_hic_hdr {
+ u8 cmd;
+ u8 buf_len;
+ union {
+ u8 cmd_resv;
+ u8 ret_status;
+ } cmd_or_resp;
+ u8 checksum;
+};
+
+struct ixgbe_hic_drv_info {
+ struct ixgbe_hic_hdr hdr;
+ u8 port_num;
+ u8 ver_sub;
+ u8 ver_build;
+ u8 ver_min;
+ u8 ver_maj;
+ u8 pad; /* end spacing to ensure length is mult. of dword */
+ u16 pad2; /* end spacing to ensure length is mult. of dword2 */
+};
+
/* Transmit Descriptor - Advanced */
union ixgbe_adv_tx_desc {
struct {
@@ -2243,13 +2328,60 @@ typedef u32 ixgbe_physical_layer;
#define IXGBE_PHYSICAL_LAYER_10GBASE_XAUI 0x1000
#define IXGBE_PHYSICAL_LAYER_SFP_ACTIVE_DA 0x2000
-/* Flow Control Macros */
-#define PAUSE_RTT 8
-#define PAUSE_MTU(MTU) ((MTU + 1024 - 1) / 1024)
+/* Flow Control Data Sheet defined values
+ * Calculation and defines taken from 802.1bb Annex O
+ */
+
+/* BitTimes (BT) conversion */
+#define IXGBE_BT2KB(BT) ((BT + 1023) / (8 * 1024))
+#define IXGBE_B2BT(BT) (BT * 8)
+
+/* Calculate Delay to respond to PFC */
+#define IXGBE_PFC_D 672
+
+/* Calculate Cable Delay */
+#define IXGBE_CABLE_DC 5556 /* Delay Copper */
+#define IXGBE_CABLE_DO 5000 /* Delay Optical */
-#define FC_HIGH_WATER(MTU) ((((PAUSE_RTT + PAUSE_MTU(MTU)) * 144) + 99) / 100 +\
- PAUSE_MTU(MTU))
-#define FC_LOW_WATER(MTU) (2 * (2 * PAUSE_MTU(MTU) + PAUSE_RTT))
+/* Calculate Interface Delay X540 */
+#define IXGBE_PHY_DC 25600 /* Delay 10G BASET */
+#define IXGBE_MAC_DC 8192 /* Delay Copper XAUI interface */
+#define IXGBE_XAUI_DC (2 * 2048) /* Delay Copper Phy */
+
+#define IXGBE_ID_X540 (IXGBE_MAC_DC + IXGBE_XAUI_DC + IXGBE_PHY_DC)
+
+/* Calculate Interface Delay 82598, 82599 */
+#define IXGBE_PHY_D 12800
+#define IXGBE_MAC_D 4096
+#define IXGBE_XAUI_D (2 * 1024)
+
+#define IXGBE_ID (IXGBE_MAC_D + IXGBE_XAUI_D + IXGBE_PHY_D)
+
+/* Calculate Delay incurred from higher layer */
+#define IXGBE_HD 6144
+
+/* Calculate PCI Bus delay for low thresholds */
+#define IXGBE_PCI_DELAY 10000
+
+/* Calculate X540 delay value in bit times */
+#define IXGBE_FILL_RATE (36 / 25)
+
+#define IXGBE_DV_X540(LINK, TC) (IXGBE_FILL_RATE * \
+ (IXGBE_B2BT(LINK) + IXGBE_PFC_D + \
+ (2 * IXGBE_CABLE_DC) + \
+ (2 * IXGBE_ID_X540) + \
+ IXGBE_HD + IXGBE_B2BT(TC)))
+
+/* Calculate 82599, 82598 delay value in bit times */
+#define IXGBE_DV(LINK, TC) (IXGBE_FILL_RATE * \
+ (IXGBE_B2BT(LINK) + IXGBE_PFC_D + \
+ (2 * IXGBE_CABLE_DC) + (2 * IXGBE_ID) + \
+ IXGBE_HD + IXGBE_B2BT(TC)))
+
+/* Calculate low threshold delay values */
+#define IXGBE_LOW_DV_X540(TC) (2 * IXGBE_B2BT(TC) + \
+ (IXGBE_FILL_RATE * IXGBE_PCI_DELAY))
+#define IXGBE_LOW_DV(TC) (2 * IXGBE_LOW_DV_X540(TC))
/* Software ATR hash keys */
#define IXGBE_ATR_BUCKET_HASH_KEY 0x3DAD14E2
@@ -2286,7 +2418,7 @@ union ixgbe_atr_input {
* src_port - 2 bytes
* dst_port - 2 bytes
* flex_bytes - 2 bytes
- * rsvd0 - 2 bytes - space reserved must be 0.
+ * bkt_hash - 2 bytes
*/
struct {
u8 vm_pool;
@@ -2297,7 +2429,7 @@ union ixgbe_atr_input {
__be16 src_port;
__be16 dst_port;
__be16 flex_bytes;
- __be16 rsvd0;
+ __be16 bkt_hash;
} formatted;
__be32 dword_stream[11];
};
@@ -2318,16 +2450,6 @@ union ixgbe_atr_hash_dword {
__be32 dword;
};
-struct ixgbe_atr_input_masks {
- __be16 rsvd0;
- __be16 vlan_id_mask;
- __be32 dst_ip_mask[4];
- __be32 src_ip_mask[4];
- __be16 src_port_mask;
- __be16 dst_port_mask;
- __be16 flex_mask;
-};
-
enum ixgbe_eeprom_type {
ixgbe_eeprom_uninitialized = 0,
ixgbe_eeprom_spi,
@@ -2478,7 +2600,7 @@ struct ixgbe_bus_info {
/* Flow control parameters */
struct ixgbe_fc_info {
- u32 high_water; /* Flow Control High-water */
+ u32 high_water[MAX_TRAFFIC_CLASS]; /* Flow Control High-water */
u32 low_water; /* Flow Control Low-water */
u16 pause_time; /* Flow Control Pause timer */
bool send_xon; /* Flow control send XON */
@@ -2560,6 +2682,8 @@ struct ixgbe_hw_stats {
u64 fcoeptc;
u64 fcoedwrc;
u64 fcoedwtc;
+ u64 fcoe_noddp;
+ u64 fcoe_noddp_ext_buff;
u64 b2ospc;
u64 b2ogprc;
u64 o2bgptc;
@@ -2615,6 +2739,9 @@ struct ixgbe_mac_operations {
s32 (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *,
bool *);
+ /* Packet Buffer Manipulation */
+ void (*set_rxpba)(struct ixgbe_hw *, int, u32, int);
+
/* LED */
s32 (*led_on)(struct ixgbe_hw *, u32);
s32 (*led_off)(struct ixgbe_hw *, u32);
@@ -2638,6 +2765,9 @@ struct ixgbe_mac_operations {
/* Flow Control */
s32 (*fc_enable)(struct ixgbe_hw *, s32);
+
+ /* Manageability interface */
+ s32 (*set_fw_drv_ver)(struct ixgbe_hw *, u8, u8, u8, u8);
};
struct ixgbe_phy_operations {
@@ -2807,6 +2937,7 @@ struct ixgbe_info {
#define IXGBE_ERR_SFP_SETUP_NOT_COMPLETE -30
#define IXGBE_ERR_PBA_SECTION -31
#define IXGBE_ERR_INVALID_ARGUMENT -32
+#define IXGBE_ERR_HOST_INTERFACE_COMMAND -33
#define IXGBE_NOT_IMPLEMENTED 0x7FFFFFFF
#endif /* _IXGBE_TYPE_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
index 4ed687be2fe3..e5101e91b6b5 100644
--- a/drivers/net/ixgbe/ixgbe_x540.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
@@ -93,78 +93,47 @@ static s32 ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw,
**/
static s32 ixgbe_reset_hw_X540(struct ixgbe_hw *hw)
{
- ixgbe_link_speed link_speed;
- s32 status = 0;
- u32 ctrl;
- u32 ctrl_ext;
- u32 reset_bit;
- u32 i;
- u32 autoc;
- u32 autoc2;
- bool link_up = false;
+ s32 status;
+ u32 ctrl, i;
/* Call adapter stop to disable tx/rx and clear interrupts */
- hw->mac.ops.stop_adapter(hw);
+ status = hw->mac.ops.stop_adapter(hw);
+ if (status != 0)
+ goto reset_hw_out;
- /*
- * Prevent the PCI-E bus from from hanging by disabling PCI-E master
- * access and verify no pending requests before reset
- */
- ixgbe_disable_pcie_master(hw);
+ /* flush pending Tx transactions */
+ ixgbe_clear_tx_pending(hw);
mac_reset_top:
- /*
- * Issue global reset to the MAC. Needs to be SW reset if link is up.
- * If link reset is used when link is up, it might reset the PHY when
- * mng is using it. If link is down or the flag to force full link
- * reset is set, then perform link reset.
- */
- if (hw->force_full_reset) {
- reset_bit = IXGBE_CTRL_LNK_RST;
- } else {
- hw->mac.ops.check_link(hw, &link_speed, &link_up, false);
- if (!link_up)
- reset_bit = IXGBE_CTRL_LNK_RST;
- else
- reset_bit = IXGBE_CTRL_RST;
- }
-
- ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
- IXGBE_WRITE_REG(hw, IXGBE_CTRL, (ctrl | reset_bit));
+ ctrl = IXGBE_CTRL_RST;
+ ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL);
+ IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
/* Poll for reset bit to self-clear indicating reset is complete */
for (i = 0; i < 10; i++) {
udelay(1);
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
- if (!(ctrl & reset_bit))
+ if (!(ctrl & IXGBE_CTRL_RST_MASK))
break;
}
- if (ctrl & reset_bit) {
+
+ if (ctrl & IXGBE_CTRL_RST_MASK) {
status = IXGBE_ERR_RESET_FAILED;
hw_dbg(hw, "Reset polling failed to complete.\n");
}
+ msleep(100);
/*
* Double resets are required for recovery from certain error
* conditions. Between resets, it is necessary to stall to allow time
- * for any pending HW events to complete. We use 1usec since that is
- * what is needed for ixgbe_disable_pcie_master(). The second reset
- * then clears out any effects of those events.
+ * for any pending HW events to complete.
*/
if (hw->mac.flags & IXGBE_FLAGS_DOUBLE_RESET_REQUIRED) {
hw->mac.flags &= ~IXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
- udelay(1);
goto mac_reset_top;
}
- /* Clear PF Reset Done bit so PF/VF Mail Ops can work */
- ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT);
- ctrl_ext |= IXGBE_CTRL_EXT_PFRSTD;
- IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext);
-
- msleep(50);
-
/* Set the Rx packet buffer size. */
IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(0), 384 << IXGBE_RXPBSIZE_SHIFT);
@@ -172,31 +141,6 @@ mac_reset_top:
hw->mac.ops.get_mac_addr(hw, hw->mac.perm_addr);
/*
- * Store the original AUTOC/AUTOC2 values if they have not been
- * stored off yet. Otherwise restore the stored original
- * values since the reset operation sets back to defaults.
- */
- autoc = IXGBE_READ_REG(hw, IXGBE_AUTOC);
- autoc2 = IXGBE_READ_REG(hw, IXGBE_AUTOC2);
- if (hw->mac.orig_link_settings_stored == false) {
- hw->mac.orig_autoc = autoc;
- hw->mac.orig_autoc2 = autoc2;
- hw->mac.orig_link_settings_stored = true;
- } else {
- if (autoc != hw->mac.orig_autoc)
- IXGBE_WRITE_REG(hw, IXGBE_AUTOC, (hw->mac.orig_autoc |
- IXGBE_AUTOC_AN_RESTART));
-
- if ((autoc2 & IXGBE_AUTOC2_UPPER_MASK) !=
- (hw->mac.orig_autoc2 & IXGBE_AUTOC2_UPPER_MASK)) {
- autoc2 &= ~IXGBE_AUTOC2_UPPER_MASK;
- autoc2 |= (hw->mac.orig_autoc2 &
- IXGBE_AUTOC2_UPPER_MASK);
- IXGBE_WRITE_REG(hw, IXGBE_AUTOC2, autoc2);
- }
- }
-
- /*
* Store MAC address from RAR0, clear receive address registers, and
* clear the multicast table. Also reset num_rar_entries to 128,
* since we modify this value when programming the SAN MAC address.
@@ -204,9 +148,6 @@ mac_reset_top:
hw->mac.num_rar_entries = IXGBE_X540_MAX_TX_QUEUES;
hw->mac.ops.init_rx_addrs(hw);
- /* Store the permanent mac address */
- hw->mac.ops.get_mac_addr(hw, hw->mac.perm_addr);
-
/* Store the permanent SAN mac address */
hw->mac.ops.get_san_mac_addr(hw, hw->mac.san_addr);
@@ -223,6 +164,7 @@ mac_reset_top:
hw->mac.ops.get_wwn_prefix(hw, &hw->mac.wwnn_prefix,
&hw->mac.wwpn_prefix);
+reset_hw_out:
return status;
}
@@ -876,6 +818,7 @@ static struct ixgbe_mac_operations mac_ops_X540 = {
.read_analog_reg8 = NULL,
.write_analog_reg8 = NULL,
.setup_link = &ixgbe_setup_mac_link_X540,
+ .set_rxpba = &ixgbe_set_rxpba_generic,
.check_link = &ixgbe_check_mac_link_generic,
.get_link_capabilities = &ixgbe_get_copper_link_capabilities_generic,
.led_on = &ixgbe_led_on_generic,
@@ -893,6 +836,7 @@ static struct ixgbe_mac_operations mac_ops_X540 = {
.clear_vfta = &ixgbe_clear_vfta_generic,
.set_vfta = &ixgbe_set_vfta_generic,
.fc_enable = &ixgbe_fc_enable_generic,
+ .set_fw_drv_ver = &ixgbe_set_fw_drv_ver_generic,
.init_uta_tables = &ixgbe_init_uta_tables_generic,
.setup_sfp = NULL,
.set_mac_anti_spoofing = &ixgbe_set_mac_anti_spoofing,
@@ -926,6 +870,7 @@ static struct ixgbe_phy_operations phy_ops_X540 = {
.read_i2c_eeprom = &ixgbe_read_i2c_eeprom_generic,
.write_i2c_eeprom = &ixgbe_write_i2c_eeprom_generic,
.check_overtemp = &ixgbe_tn_check_overtemp,
+ .get_firmware_version = &ixgbe_get_phy_firmware_version_generic,
};
struct ixgbe_info ixgbe_X540_info = {
diff --git a/drivers/net/ixgbevf/Makefile b/drivers/net/ethernet/intel/ixgbevf/Makefile
index 1f35d229e71a..1f35d229e71a 100644
--- a/drivers/net/ixgbevf/Makefile
+++ b/drivers/net/ethernet/intel/ixgbevf/Makefile
diff --git a/drivers/net/ixgbevf/defines.h b/drivers/net/ethernet/intel/ixgbevf/defines.h
index 78abb6f1a866..78abb6f1a866 100644
--- a/drivers/net/ixgbevf/defines.h
+++ b/drivers/net/ethernet/intel/ixgbevf/defines.h
diff --git a/drivers/net/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
index deee3754b1f7..e29ba4506b74 100644
--- a/drivers/net/ixgbevf/ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
@@ -117,44 +117,6 @@ static int ixgbevf_get_settings(struct net_device *netdev,
return 0;
}
-static u32 ixgbevf_get_rx_csum(struct net_device *netdev)
-{
- struct ixgbevf_adapter *adapter = netdev_priv(netdev);
- return adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED;
-}
-
-static int ixgbevf_set_rx_csum(struct net_device *netdev, u32 data)
-{
- struct ixgbevf_adapter *adapter = netdev_priv(netdev);
- if (data)
- adapter->flags |= IXGBE_FLAG_RX_CSUM_ENABLED;
- else
- adapter->flags &= ~IXGBE_FLAG_RX_CSUM_ENABLED;
-
- if (netif_running(netdev)) {
- if (!adapter->dev_closed)
- ixgbevf_reinit_locked(adapter);
- } else {
- ixgbevf_reset(adapter);
- }
-
- return 0;
-}
-
-static int ixgbevf_set_tso(struct net_device *netdev, u32 data)
-{
- if (data) {
- netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
- } else {
- netif_tx_stop_all_queues(netdev);
- netdev->features &= ~NETIF_F_TSO;
- netdev->features &= ~NETIF_F_TSO6;
- netif_tx_start_all_queues(netdev);
- }
- return 0;
-}
-
static u32 ixgbevf_get_msglevel(struct net_device *netdev)
{
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
@@ -319,12 +281,8 @@ static void ixgbevf_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = IXGBEVF_MAX_RXD;
ring->tx_max_pending = IXGBEVF_MAX_TXD;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = rx_ring->count;
ring->tx_pending = tx_ring->count;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
static int ixgbevf_set_ringparam(struct net_device *netdev,
@@ -720,16 +678,8 @@ static struct ethtool_ops ixgbevf_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_ringparam = ixgbevf_get_ringparam,
.set_ringparam = ixgbevf_set_ringparam,
- .get_rx_csum = ixgbevf_get_rx_csum,
- .set_rx_csum = ixgbevf_set_rx_csum,
- .get_tx_csum = ethtool_op_get_tx_csum,
- .set_tx_csum = ethtool_op_set_tx_ipv6_csum,
- .get_sg = ethtool_op_get_sg,
- .set_sg = ethtool_op_set_sg,
.get_msglevel = ixgbevf_get_msglevel,
.set_msglevel = ixgbevf_set_msglevel,
- .get_tso = ethtool_op_get_tso,
- .set_tso = ixgbevf_set_tso,
.self_test = ixgbevf_diag_test,
.get_sset_count = ixgbevf_get_sset_count,
.get_strings = ixgbevf_get_strings,
diff --git a/drivers/net/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
index b703f60be3b7..e6c9d1a927a9 100644
--- a/drivers/net/ixgbevf/ixgbevf.h
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
@@ -29,9 +29,12 @@
#define _IXGBEVF_H_
#include <linux/types.h>
+#include <linux/bitops.h>
#include <linux/timer.h>
#include <linux/io.h>
#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/u64_stats_sync.h>
#include "vf.h"
@@ -69,12 +72,13 @@ struct ixgbevf_ring {
struct ixgbevf_rx_buffer *rx_buffer_info;
};
+ u64 total_bytes;
+ u64 total_packets;
+ struct u64_stats_sync syncp;
+
u16 head;
u16 tail;
- unsigned int total_bytes;
- unsigned int total_packets;
-
u16 reg_idx; /* holds the special value that gets the hardware register
* offset associated with this ring, which is different
* for DCB and RSS modes */
@@ -185,9 +189,7 @@ struct ixgbevf_q_vector {
/* board specific private data structure */
struct ixgbevf_adapter {
struct timer_list watchdog_timer;
-#ifdef NETIF_F_HW_VLAN_TX
- struct vlan_group *vlgrp;
-#endif
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
u16 bd_number;
struct work_struct reset_task;
struct ixgbevf_q_vector *q_vector[MAX_MSIX_Q_VECTORS];
@@ -279,7 +281,7 @@ enum ixgbevf_boards {
extern struct ixgbevf_info ixgbevf_82599_vf_info;
extern struct ixgbevf_info ixgbevf_X540_vf_info;
-extern struct ixgbe_mac_operations ixgbevf_mbx_ops;
+extern struct ixgbe_mbx_operations ixgbevf_mbx_ops;
/* needed by ethtool.c */
extern char ixgbevf_driver_name[];
diff --git a/drivers/net/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 28d3cb21d376..4c8e19951d57 100644
--- a/drivers/net/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -30,6 +30,7 @@
Copyright (c)2006 - 2007 Myricom, Inc. for some LRO specific code
******************************************************************************/
#include <linux/types.h>
+#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
@@ -43,6 +44,7 @@
#include <net/checksum.h>
#include <net/ip6_checksum.h>
#include <linux/ethtool.h>
+#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/prefetch.h>
@@ -52,7 +54,7 @@ char ixgbevf_driver_name[] = "ixgbevf";
static const char ixgbevf_driver_string[] =
"Intel(R) 10 Gigabit PCI Express Virtual Function Network Driver";
-#define DRV_VERSION "2.0.0-k2"
+#define DRV_VERSION "2.2.0-k"
const char ixgbevf_driver_version[] = DRV_VERSION;
static char ixgbevf_copyright[] =
"Copyright (c) 2009 - 2010 Intel Corporation.";
@@ -201,6 +203,9 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_adapter *adapter,
(count < tx_ring->work_limit)) {
bool cleaned = false;
rmb(); /* read buffer_info after eop_desc */
+ /* eop could change between read and DD-check */
+ if (unlikely(eop != tx_ring->tx_buffer_info[i].next_to_watch))
+ goto cont_loop;
for ( ; !cleaned; count++) {
struct sk_buff *skb;
tx_desc = IXGBE_TX_DESC_ADV(*tx_ring, i);
@@ -230,6 +235,7 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_adapter *adapter,
i = 0;
}
+cont_loop:
eop = tx_ring->tx_buffer_info[i].next_to_watch;
eop_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop);
}
@@ -264,11 +270,10 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_adapter *adapter,
IXGBE_WRITE_REG(hw, IXGBE_VTEICS, tx_ring->v_idx);
}
+ u64_stats_update_begin(&tx_ring->syncp);
tx_ring->total_bytes += total_bytes;
tx_ring->total_packets += total_packets;
-
- netdev->stats.tx_bytes += total_bytes;
- netdev->stats.tx_packets += total_packets;
+ u64_stats_update_end(&tx_ring->syncp);
return count < tx_ring->work_limit;
}
@@ -290,19 +295,13 @@ static void ixgbevf_receive_skb(struct ixgbevf_q_vector *q_vector,
bool is_vlan = (status & IXGBE_RXD_STAT_VP);
u16 tag = le16_to_cpu(rx_desc->wb.upper.vlan);
- if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) {
- if (adapter->vlgrp && is_vlan)
- vlan_gro_receive(&q_vector->napi,
- adapter->vlgrp,
- tag, skb);
- else
+ if (is_vlan && test_bit(tag, adapter->active_vlans))
+ __vlan_hwaccel_put_tag(skb, tag);
+
+ if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL))
napi_gro_receive(&q_vector->napi, skb);
- } else {
- if (adapter->vlgrp && is_vlan)
- vlan_hwaccel_rx(skb, adapter->vlgrp, tag);
- else
+ else
netif_rx(skb);
- }
}
/**
@@ -595,10 +594,10 @@ next_desc:
if (cleaned_count)
ixgbevf_alloc_rx_buffers(adapter, rx_ring, cleaned_count);
+ u64_stats_update_begin(&rx_ring->syncp);
rx_ring->total_packets += total_rx_packets;
rx_ring->total_bytes += total_rx_bytes;
- adapter->netdev->stats.rx_bytes += total_rx_bytes;
- adapter->netdev->stats.rx_packets += total_rx_packets;
+ u64_stats_update_end(&rx_ring->syncp);
return cleaned;
}
@@ -1401,24 +1400,6 @@ static void ixgbevf_configure_rx(struct ixgbevf_adapter *adapter)
}
}
-static void ixgbevf_vlan_rx_register(struct net_device *netdev,
- struct vlan_group *grp)
-{
- struct ixgbevf_adapter *adapter = netdev_priv(netdev);
- struct ixgbe_hw *hw = &adapter->hw;
- int i, j;
- u32 ctrl;
-
- adapter->vlgrp = grp;
-
- for (i = 0; i < adapter->num_rx_queues; i++) {
- j = adapter->rx_ring[i].reg_idx;
- ctrl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(j));
- ctrl |= IXGBE_RXDCTL_VME;
- IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(j), ctrl);
- }
-}
-
static void ixgbevf_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
{
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
@@ -1427,6 +1408,7 @@ static void ixgbevf_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
/* add VID to filter table */
if (hw->mac.ops.set_vfta)
hw->mac.ops.set_vfta(hw, vid, 0, true);
+ set_bit(vid, adapter->active_vlans);
}
static void ixgbevf_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
@@ -1434,31 +1416,18 @@ static void ixgbevf_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
- if (!test_bit(__IXGBEVF_DOWN, &adapter->state))
- ixgbevf_irq_disable(adapter);
-
- vlan_group_set_device(adapter->vlgrp, vid, NULL);
-
- if (!test_bit(__IXGBEVF_DOWN, &adapter->state))
- ixgbevf_irq_enable(adapter, true, true);
-
/* remove VID from filter table */
if (hw->mac.ops.set_vfta)
hw->mac.ops.set_vfta(hw, vid, 0, false);
+ clear_bit(vid, adapter->active_vlans);
}
static void ixgbevf_restore_vlan(struct ixgbevf_adapter *adapter)
{
- ixgbevf_vlan_rx_register(adapter->netdev, adapter->vlgrp);
+ u16 vid;
- if (adapter->vlgrp) {
- u16 vid;
- for (vid = 0; vid < VLAN_N_VID; vid++) {
- if (!vlan_group_get_device(adapter->vlgrp, vid))
- continue;
- ixgbevf_vlan_rx_add_vid(adapter->netdev, vid);
- }
- }
+ for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
+ ixgbevf_vlan_rx_add_vid(adapter->netdev, vid);
}
static int ixgbevf_write_uc_addr_list(struct net_device *netdev)
@@ -1648,7 +1617,7 @@ static int ixgbevf_up_complete(struct ixgbevf_adapter *adapter)
for (i = 0; i < num_rx_rings; i++) {
j = adapter->rx_ring[i].reg_idx;
rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(j));
- rxdctl |= IXGBE_RXDCTL_ENABLE;
+ rxdctl |= IXGBE_RXDCTL_ENABLE | IXGBE_RXDCTL_VME;
if (hw->mac.type == ixgbe_mac_X540_vf) {
rxdctl &= ~IXGBE_RXDCTL_RLPMLMASK;
rxdctl |= ((netdev->mtu + ETH_HLEN + ETH_FCS_LEN) |
@@ -2288,10 +2257,6 @@ void ixgbevf_update_stats(struct ixgbevf_adapter *adapter)
adapter->stats.vfgotc);
UPDATE_VF_COUNTER_32bit(IXGBE_VFMPRC, adapter->stats.last_vfmprc,
adapter->stats.vfmprc);
-
- /* Fill out the OS statistics structure */
- adapter->netdev->stats.multicast = adapter->stats.vfmprc -
- adapter->stats.base_vfmprc;
}
/**
@@ -2947,22 +2912,20 @@ static int ixgbevf_tx_map(struct ixgbevf_adapter *adapter,
}
for (f = 0; f < nr_frags; f++) {
- struct skb_frag_struct *frag;
+ const struct skb_frag_struct *frag;
frag = &skb_shinfo(skb)->frags[f];
- len = min((unsigned int)frag->size, total);
- offset = frag->page_offset;
+ len = min((unsigned int)skb_frag_size(frag), total);
+ offset = 0;
while (len) {
tx_buffer_info = &tx_ring->tx_buffer_info[i];
size = min(len, (unsigned int)IXGBE_MAX_DATA_PER_TXD);
tx_buffer_info->length = size;
- tx_buffer_info->dma = dma_map_page(&adapter->pdev->dev,
- frag->page,
- offset,
- size,
- DMA_TO_DEVICE);
+ tx_buffer_info->dma =
+ skb_frag_dma_map(&adapter->pdev->dev, frag,
+ offset, size, DMA_TO_DEVICE);
tx_buffer_info->mapped_as_page = true;
if (dma_mapping_error(&pdev->dev, tx_buffer_info->dma))
goto dma_error;
@@ -3133,7 +3096,7 @@ static int ixgbevf_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
count += TXD_USE_COUNT(skb_headlen(skb));
for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
- count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
+ count += TXD_USE_COUNT(skb_frag_size(&skb_shinfo(skb)->frags[f]));
if (ixgbevf_maybe_stop_tx(netdev, tx_ring, count)) {
adapter->tx_busy++;
@@ -3248,19 +3211,69 @@ static void ixgbevf_shutdown(struct pci_dev *pdev)
pci_disable_device(pdev);
}
+static struct rtnl_link_stats64 *ixgbevf_get_stats(struct net_device *netdev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+ unsigned int start;
+ u64 bytes, packets;
+ const struct ixgbevf_ring *ring;
+ int i;
+
+ ixgbevf_update_stats(adapter);
+
+ stats->multicast = adapter->stats.vfmprc - adapter->stats.base_vfmprc;
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ ring = &adapter->rx_ring[i];
+ do {
+ start = u64_stats_fetch_begin_bh(&ring->syncp);
+ bytes = ring->total_bytes;
+ packets = ring->total_packets;
+ } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
+ stats->rx_bytes += bytes;
+ stats->rx_packets += packets;
+ }
+
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ ring = &adapter->tx_ring[i];
+ do {
+ start = u64_stats_fetch_begin_bh(&ring->syncp);
+ bytes = ring->total_bytes;
+ packets = ring->total_packets;
+ } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
+ stats->tx_bytes += bytes;
+ stats->tx_packets += packets;
+ }
+
+ return stats;
+}
+
+static int ixgbevf_set_features(struct net_device *netdev, u32 features)
+{
+ struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+
+ if (features & NETIF_F_RXCSUM)
+ adapter->flags |= IXGBE_FLAG_RX_CSUM_ENABLED;
+ else
+ adapter->flags &= ~IXGBE_FLAG_RX_CSUM_ENABLED;
+
+ return 0;
+}
+
static const struct net_device_ops ixgbe_netdev_ops = {
- .ndo_open = &ixgbevf_open,
- .ndo_stop = &ixgbevf_close,
- .ndo_start_xmit = &ixgbevf_xmit_frame,
- .ndo_set_rx_mode = &ixgbevf_set_rx_mode,
- .ndo_set_multicast_list = &ixgbevf_set_rx_mode,
+ .ndo_open = ixgbevf_open,
+ .ndo_stop = ixgbevf_close,
+ .ndo_start_xmit = ixgbevf_xmit_frame,
+ .ndo_set_rx_mode = ixgbevf_set_rx_mode,
+ .ndo_get_stats64 = ixgbevf_get_stats,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_mac_address = &ixgbevf_set_mac,
- .ndo_change_mtu = &ixgbevf_change_mtu,
- .ndo_tx_timeout = &ixgbevf_tx_timeout,
- .ndo_vlan_rx_register = &ixgbevf_vlan_rx_register,
- .ndo_vlan_rx_add_vid = &ixgbevf_vlan_rx_add_vid,
- .ndo_vlan_rx_kill_vid = &ixgbevf_vlan_rx_kill_vid,
+ .ndo_set_mac_address = ixgbevf_set_mac,
+ .ndo_change_mtu = ixgbevf_change_mtu,
+ .ndo_tx_timeout = ixgbevf_tx_timeout,
+ .ndo_vlan_rx_add_vid = ixgbevf_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = ixgbevf_vlan_rx_kill_vid,
+ .ndo_set_features = ixgbevf_set_features,
};
static void ixgbevf_assign_netdev_ops(struct net_device *dev)
@@ -3364,7 +3377,7 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev,
hw->mac.type = ii->mac;
memcpy(&hw->mbx.ops, &ixgbevf_mbx_ops,
- sizeof(struct ixgbe_mac_operations));
+ sizeof(struct ixgbe_mbx_operations));
adapter->flags &= ~IXGBE_FLAG_RX_PS_CAPABLE;
adapter->flags &= ~IXGBE_FLAG_RX_PS_ENABLED;
@@ -3373,16 +3386,18 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev,
/* setup the private structure */
err = ixgbevf_sw_init(adapter);
- netdev->features = NETIF_F_SG |
+ netdev->hw_features = NETIF_F_SG |
NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_RXCSUM;
+
+ netdev->features = netdev->hw_features |
NETIF_F_HW_VLAN_TX |
NETIF_F_HW_VLAN_RX |
NETIF_F_HW_VLAN_FILTER;
- netdev->features |= NETIF_F_IPV6_CSUM;
- netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
- netdev->features |= NETIF_F_GRO;
netdev->vlan_features |= NETIF_F_TSO;
netdev->vlan_features |= NETIF_F_TSO6;
netdev->vlan_features |= NETIF_F_IP_CSUM;
@@ -3392,6 +3407,8 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev,
if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA;
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
/* The HW MAC address was set and/or determined in sw_init */
memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len);
memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len);
diff --git a/drivers/net/ixgbevf/mbx.c b/drivers/net/ethernet/intel/ixgbevf/mbx.c
index 7a8833125770..930fa83f2568 100644
--- a/drivers/net/ixgbevf/mbx.c
+++ b/drivers/net/ethernet/intel/ixgbevf/mbx.c
@@ -276,7 +276,7 @@ out_no_write:
* @msg: The message buffer
* @size: Length of buffer
*
- * returns 0 if it successfuly read message from buffer
+ * returns 0 if it successfully read message from buffer
**/
static s32 ixgbevf_read_mbx_vf(struct ixgbe_hw *hw, u32 *msg, u16 size)
{
diff --git a/drivers/net/ixgbevf/mbx.h b/drivers/net/ethernet/intel/ixgbevf/mbx.h
index ea393eb03f3a..ea393eb03f3a 100644
--- a/drivers/net/ixgbevf/mbx.h
+++ b/drivers/net/ethernet/intel/ixgbevf/mbx.h
diff --git a/drivers/net/ixgbevf/regs.h b/drivers/net/ethernet/intel/ixgbevf/regs.h
index 189200eeca26..189200eeca26 100644
--- a/drivers/net/ixgbevf/regs.h
+++ b/drivers/net/ethernet/intel/ixgbevf/regs.h
diff --git a/drivers/net/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c
index aa3682e8c473..aa3682e8c473 100644
--- a/drivers/net/ixgbevf/vf.c
+++ b/drivers/net/ethernet/intel/ixgbevf/vf.c
diff --git a/drivers/net/ixgbevf/vf.h b/drivers/net/ethernet/intel/ixgbevf/vf.h
index 10306b492ee6..10306b492ee6 100644
--- a/drivers/net/ixgbevf/vf.h
+++ b/drivers/net/ethernet/intel/ixgbevf/vf.h
diff --git a/drivers/net/jme.c b/drivers/net/ethernet/jme.c
index b5b174a8c149..7becff1f387d 100644
--- a/drivers/net/jme.c
+++ b/drivers/net/ethernet/jme.c
@@ -271,9 +271,7 @@ jme_reset_mac_processor(struct jme_adapter *jme)
static inline void
jme_clear_pm(struct jme_adapter *jme)
{
- jwrite32(jme, JME_PMCS, 0xFFFF0000 | jme->reg_pmcs);
- pci_set_power_state(jme->pdev, PCI_D0);
- device_set_wakeup_enable(&jme->pdev->dev, false);
+ jwrite32(jme, JME_PMCS, PMCS_STMASK | jme->reg_pmcs);
}
static int
@@ -753,20 +751,28 @@ jme_make_new_rx_buf(struct jme_adapter *jme, int i)
struct jme_ring *rxring = &(jme->rxring[0]);
struct jme_buffer_info *rxbi = rxring->bufinf + i;
struct sk_buff *skb;
+ dma_addr_t mapping;
skb = netdev_alloc_skb(jme->dev,
jme->dev->mtu + RX_EXTRA_LEN);
if (unlikely(!skb))
return -ENOMEM;
+ mapping = pci_map_page(jme->pdev, virt_to_page(skb->data),
+ offset_in_page(skb->data), skb_tailroom(skb),
+ PCI_DMA_FROMDEVICE);
+ if (unlikely(pci_dma_mapping_error(jme->pdev, mapping))) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ if (likely(rxbi->mapping))
+ pci_unmap_page(jme->pdev, rxbi->mapping,
+ rxbi->len, PCI_DMA_FROMDEVICE);
+
rxbi->skb = skb;
rxbi->len = skb_tailroom(skb);
- rxbi->mapping = pci_map_page(jme->pdev,
- virt_to_page(skb->data),
- offset_in_page(skb->data),
- rxbi->len,
- PCI_DMA_FROMDEVICE);
-
+ rxbi->mapping = mapping;
return 0;
}
@@ -1050,16 +1056,12 @@ jme_alloc_and_feed_skb(struct jme_adapter *jme, int idx)
skb_checksum_none_assert(skb);
if (rxdesc->descwb.flags & cpu_to_le16(RXWBFLAG_TAGON)) {
- if (jme->vlgrp) {
- jme->jme_vlan_rx(skb, jme->vlgrp,
- le16_to_cpu(rxdesc->descwb.vlan));
- NET_STAT(jme).rx_bytes += 4;
- } else {
- dev_kfree_skb(skb);
- }
- } else {
- jme->jme_rx(skb);
+ u16 vid = le16_to_cpu(rxdesc->descwb.vlan);
+
+ __vlan_hwaccel_put_tag(skb, vid);
+ NET_STAT(jme).rx_bytes += 4;
}
+ jme->jme_rx(skb);
if ((rxdesc->descwb.flags & cpu_to_le16(RXWBFLAG_DEST)) ==
cpu_to_le16(RXWBFLAG_DEST_MUL))
@@ -1817,11 +1819,9 @@ jme_powersave_phy(struct jme_adapter *jme)
{
if (jme->reg_pmcs) {
jme_set_100m_half(jme);
-
if (jme->reg_pmcs & (PMCS_LFEN | PMCS_LREN))
jme_wait_link(jme);
-
- jwrite32(jme, JME_PMCS, jme->reg_pmcs);
+ jme_clear_pm(jme);
} else {
jme_phy_off(jme);
}
@@ -1920,7 +1920,7 @@ jme_map_tx_skb(struct jme_adapter *jme, struct sk_buff *skb, int idx)
u8 hidma = jme->dev->features & NETIF_F_HIGHDMA;
int i, nr_frags = skb_shinfo(skb)->nr_frags;
int mask = jme->tx_ring_mask;
- struct skb_frag_struct *frag;
+ const struct skb_frag_struct *frag;
u32 len;
for (i = 0 ; i < nr_frags ; ++i) {
@@ -1928,8 +1928,9 @@ jme_map_tx_skb(struct jme_adapter *jme, struct sk_buff *skb, int idx)
ctxdesc = txdesc + ((idx + i + 2) & (mask));
ctxbi = txbi + ((idx + i + 2) & (mask));
- jme_fill_tx_map(jme->pdev, ctxdesc, ctxbi, frag->page,
- frag->page_offset, frag->size, hidma);
+ jme_fill_tx_map(jme->pdev, ctxdesc, ctxbi,
+ skb_frag_page(frag),
+ frag->page_offset, skb_frag_size(frag), hidma);
}
len = skb_is_nonlinear(skb) ? skb_headlen(skb) : skb->len;
@@ -2286,16 +2287,6 @@ static inline void jme_resume_rx(struct jme_adapter *jme)
}
static void
-jme_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
-{
- struct jme_adapter *jme = netdev_priv(netdev);
-
- jme_pause_rx(jme);
- jme->vlgrp = grp;
- jme_resume_rx(jme);
-}
-
-static void
jme_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
@@ -2405,7 +2396,6 @@ jme_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd)
test_bit(JME_FLAG_POLL, &jme->flags)) {
clear_bit(JME_FLAG_POLL, &jme->flags);
jme->jme_rx = netif_rx;
- jme->jme_vlan_rx = vlan_hwaccel_rx;
dpi->cur = PCC_P1;
dpi->attempt = PCC_P1;
dpi->cnt = 0;
@@ -2415,7 +2405,6 @@ jme_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd)
!(test_bit(JME_FLAG_POLL, &jme->flags))) {
set_bit(JME_FLAG_POLL, &jme->flags);
jme->jme_rx = netif_receive_skb;
- jme->jme_vlan_rx = vlan_hwaccel_receive_skb;
jme_interrupt_mode(jme);
}
@@ -2529,8 +2518,7 @@ jme_set_wol(struct net_device *netdev,
jme->reg_pmcs |= PMCS_MFEN;
jwrite32(jme, JME_PMCS, jme->reg_pmcs);
-
- device_set_wakeup_enable(&jme->pdev->dev, jme->reg_pmcs);
+ device_set_wakeup_enable(&jme->pdev->dev, !!(jme->reg_pmcs));
return 0;
}
@@ -2852,10 +2840,9 @@ static const struct net_device_ops jme_netdev_ops = {
.ndo_do_ioctl = jme_ioctl,
.ndo_start_xmit = jme_start_xmit,
.ndo_set_mac_address = jme_set_macaddr,
- .ndo_set_multicast_list = jme_set_multi,
+ .ndo_set_rx_mode = jme_set_multi,
.ndo_change_mtu = jme_change_mtu,
.ndo_tx_timeout = jme_tx_timeout,
- .ndo_vlan_rx_register = jme_vlan_rx_register,
.ndo_fix_features = jme_fix_features,
.ndo_set_features = jme_set_features,
};
@@ -2938,7 +2925,6 @@ jme_init_one(struct pci_dev *pdev,
jme->pdev = pdev;
jme->dev = netdev;
jme->jme_rx = netif_rx;
- jme->jme_vlan_rx = vlan_hwaccel_rx;
jme->old_mtu = netdev->mtu = 1500;
jme->phylink = 0;
jme->tx_ring_size = 1 << 10;
@@ -3058,6 +3044,9 @@ jme_init_one(struct pci_dev *pdev,
jme->mii_if.mdio_write = jme_mdio_write;
jme_clear_pm(jme);
+ pci_set_power_state(jme->pdev, PCI_D0);
+ device_set_wakeup_enable(&pdev->dev, true);
+
jme_set_phyfifo_5level(jme);
jme->pcirev = pdev->revision;
if (!jme->fpgaver)
@@ -3135,13 +3124,17 @@ jme_shutdown(struct pci_dev *pdev)
pci_pme_active(pdev, true);
}
-#ifdef CONFIG_PM
-static int jme_suspend(struct device *dev)
+#ifdef CONFIG_PM_SLEEP
+static int
+jme_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct net_device *netdev = pci_get_drvdata(pdev);
struct jme_adapter *jme = netdev_priv(netdev);
+ if (!netif_running(netdev))
+ return 0;
+
atomic_dec(&jme->link_changing);
netif_device_detach(netdev);
@@ -3175,14 +3168,17 @@ static int jme_suspend(struct device *dev)
return 0;
}
-static int jme_resume(struct device *dev)
+static int
+jme_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct net_device *netdev = pci_get_drvdata(pdev);
struct jme_adapter *jme = netdev_priv(netdev);
- jwrite32(jme, JME_PMCS, 0xFFFF0000 | jme->reg_pmcs);
+ if (!netif_running(netdev))
+ return 0;
+ jme_clear_pm(jme);
jme_phy_on(jme);
if (test_bit(JME_FLAG_SSET, &jme->flags))
jme_set_settings(netdev, &jme->old_ecmd);
diff --git a/drivers/net/jme.h b/drivers/net/ethernet/jme.h
index e9aaeca96abc..02ea27c1dcb5 100644
--- a/drivers/net/jme.h
+++ b/drivers/net/ethernet/jme.h
@@ -24,6 +24,7 @@
#ifndef __JME_H_INCLUDED__
#define __JME_H_INCLUDED__
+#include <linux/interrupt.h>
#define DRV_NAME "jme"
#define DRV_VERSION "1.0.8"
@@ -101,7 +102,6 @@ enum jme_spi_op_bits {
};
#define HALF_US 500 /* 500 ns */
-#define JMESPIIOCTL SIOCDEVPRIVATE
#define PCI_PRIV_PE1 0xE4
@@ -450,7 +450,6 @@ struct jme_adapter {
u32 msg_enable;
struct ethtool_cmd old_ecmd;
unsigned int old_mtu;
- struct vlan_group *vlgrp;
struct dynpcc_info dpi;
atomic_t intr_sem;
atomic_t link_changing;
@@ -458,9 +457,6 @@ struct jme_adapter {
atomic_t rx_cleaning;
atomic_t rx_empty;
int (*jme_rx)(struct sk_buff *skb);
- int (*jme_vlan_rx)(struct sk_buff *skb,
- struct vlan_group *grp,
- unsigned short vlan_tag);
DECLARE_NAPI_STRUCT
DECLARE_NET_DEVICE_STATS
};
@@ -851,6 +847,7 @@ enum jme_ghc_txmac_clk {
* Power management control and status register
*/
enum jme_pmcs_bit_masks {
+ PMCS_STMASK = 0xFFFF0000,
PMCS_WF7DET = 0x80000000,
PMCS_WF6DET = 0x40000000,
PMCS_WF5DET = 0x20000000,
@@ -862,6 +859,7 @@ enum jme_pmcs_bit_masks {
PMCS_LFDET = 0x00040000,
PMCS_LRDET = 0x00020000,
PMCS_MFDET = 0x00010000,
+ PMCS_ENMASK = 0x0000FFFF,
PMCS_WF7EN = 0x00008000,
PMCS_WF6EN = 0x00004000,
PMCS_WF5EN = 0x00002000,
diff --git a/drivers/net/korina.c b/drivers/net/ethernet/korina.c
index c7a9bef4dfb0..d8430f487b84 100644
--- a/drivers/net/korina.c
+++ b/drivers/net/ethernet/korina.c
@@ -58,7 +58,6 @@
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/pgtable.h>
-#include <asm/segment.h>
#include <asm/io.h>
#include <asm/dma.h>
@@ -504,12 +503,7 @@ static void korina_multicast_list(struct net_device *dev)
hash_table[i] = 0;
netdev_for_each_mc_addr(ha, dev) {
- char *addrs = ha->addr;
-
- if (!(*addrs & 1))
- continue;
-
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc >>= 26;
hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf));
}
@@ -1094,7 +1088,7 @@ static const struct net_device_ops korina_netdev_ops = {
.ndo_open = korina_open,
.ndo_stop = korina_close,
.ndo_start_xmit = korina_send_packet,
- .ndo_set_multicast_list = korina_multicast_list,
+ .ndo_set_rx_mode = korina_multicast_list,
.ndo_tx_timeout = korina_tx_timeout,
.ndo_do_ioctl = korina_ioctl,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c
index 45f252b7da30..0b3567ab8121 100644
--- a/drivers/net/lantiq_etop.c
+++ b/drivers/net/ethernet/lantiq_etop.c
@@ -34,6 +34,8 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
#include <asm/checksum.h>
@@ -687,7 +689,7 @@ static const struct net_device_ops ltq_eth_netdev_ops = {
.ndo_do_ioctl = ltq_etop_ioctl,
.ndo_set_mac_address = ltq_etop_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = ltq_etop_set_multicast_list,
+ .ndo_set_rx_mode = ltq_etop_set_multicast_list,
.ndo_select_queue = ltq_etop_select_queue,
.ndo_init = ltq_etop_init,
.ndo_tx_timeout = ltq_etop_tx_timeout,
diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
new file mode 100644
index 000000000000..0029934748bc
--- /dev/null
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -0,0 +1,111 @@
+#
+# Marvell device configuration
+#
+
+config NET_VENDOR_MARVELL
+ bool "Marvell devices"
+ default y
+ depends on PCI || CPU_PXA168 || MV64X60 || PPC32 || PLAT_ORION || INET
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Marvell devices. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_VENDOR_MARVELL
+
+config MV643XX_ETH
+ tristate "Marvell Discovery (643XX) and Orion ethernet support"
+ depends on (MV64X60 || PPC32 || PLAT_ORION) && INET
+ select INET_LRO
+ select PHYLIB
+ ---help---
+ This driver supports the gigabit ethernet MACs in the
+ Marvell Discovery PPC/MIPS chipset family (MV643XX) and
+ in the Marvell Orion ARM SoC family.
+
+ Some boards that use the Discovery chipset are the Momenco
+ Ocelot C and Jaguar ATX and Pegasos II.
+
+config PXA168_ETH
+ tristate "Marvell pxa168 ethernet support"
+ depends on CPU_PXA168
+ select PHYLIB
+ ---help---
+ This driver supports the pxa168 Ethernet ports.
+
+ To compile this driver as a module, choose M here. The module
+ will be called pxa168_eth.
+
+config SKGE
+ tristate "Marvell Yukon Gigabit Ethernet support"
+ depends on PCI
+ select CRC32
+ ---help---
+ This driver support the Marvell Yukon or SysKonnect SK-98xx/SK-95xx
+ and related Gigabit Ethernet adapters. It is a new smaller driver
+ with better performance and more complete ethtool support.
+
+ It does not support the link failover and network management
+ features that "portable" vendor supplied sk98lin driver does.
+
+ This driver supports adapters based on the original Yukon chipset:
+ Marvell 88E8001, Belkin F5D5005, CNet GigaCard, DLink DGE-530T,
+ Linksys EG1032/EG1064, 3Com 3C940/3C940B, SysKonnect SK-9871/9872.
+
+ It does not support the newer Yukon2 chipset: a separate driver,
+ sky2, is provided for these adapters.
+
+ To compile this driver as a module, choose M here: the module
+ will be called skge. This is recommended.
+
+config SKGE_DEBUG
+ bool "Debugging interface"
+ depends on SKGE && DEBUG_FS
+ ---help---
+ This option adds the ability to dump driver state for debugging.
+ The file /sys/kernel/debug/skge/ethX displays the state of the internal
+ transmit and receive rings.
+
+ If unsure, say N.
+
+config SKGE_GENESIS
+ bool "Support for older SysKonnect Genesis boards"
+ depends on SKGE
+ ---help---
+ This enables support for the older and uncommon SysKonnect Genesis
+ chips, which support MII via an external transceiver, instead of
+ an internal one. Disabling this option will save some memory
+ by making code smaller. If unsure say Y.
+
+config SKY2
+ tristate "Marvell Yukon 2 support"
+ depends on PCI
+ select CRC32
+ ---help---
+ This driver supports Gigabit Ethernet adapters based on the
+ Marvell Yukon 2 chipset:
+ Marvell 88E8021/88E8022/88E8035/88E8036/88E8038/88E8050/88E8052/
+ 88E8053/88E8055/88E8061/88E8062, SysKonnect SK-9E21D/SK-9S21
+
+ There is companion driver for the older Marvell Yukon and
+ SysKonnect Genesis based adapters: skge.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sky2. This is recommended.
+
+config SKY2_DEBUG
+ bool "Debugging interface"
+ depends on SKY2 && DEBUG_FS
+ ---help---
+ This option adds the ability to dump driver state for debugging.
+ The file /sys/kernel/debug/sky2/ethX displays the state of the internal
+ transmit and receive rings.
+
+ If unsure, say N.
+
+endif # NET_VENDOR_MARVELL
diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile
new file mode 100644
index 000000000000..57e3234a37ba
--- /dev/null
+++ b/drivers/net/ethernet/marvell/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the Marvell device drivers.
+#
+
+obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
+obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
+obj-$(CONFIG_SKGE) += skge.o
+obj-$(CONFIG_SKY2) += sky2.o
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
index a5d9b1c310b3..194a03113802 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
@@ -713,8 +713,9 @@ static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
int frag;
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
- skb_frag_t *fragp = &skb_shinfo(skb)->frags[frag];
- if (fragp->size <= 8 && fragp->page_offset & 7)
+ const skb_frag_t *fragp = &skb_shinfo(skb)->frags[frag];
+
+ if (skb_frag_size(fragp) <= 8 && fragp->page_offset & 7)
return 1;
}
@@ -751,11 +752,11 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
}
desc->l4i_chk = 0;
- desc->byte_cnt = this_frag->size;
- desc->buf_ptr = dma_map_page(mp->dev->dev.parent,
- this_frag->page,
- this_frag->page_offset,
- this_frag->size, DMA_TO_DEVICE);
+ desc->byte_cnt = skb_frag_size(this_frag);
+ desc->buf_ptr = skb_frag_dma_map(mp->dev->dev.parent,
+ this_frag, 0,
+ skb_frag_size(this_frag),
+ DMA_TO_DEVICE);
}
}
@@ -840,6 +841,8 @@ no_csum:
__skb_queue_tail(&txq->tx_skb, skb);
+ skb_tx_timestamp(skb);
+
/* ensure all other descriptors are written before first cmd_sts */
wmb();
desc->cmd_sts = cmd_sts;
@@ -859,7 +862,7 @@ no_csum:
static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
- int queue;
+ int length, queue;
struct tx_queue *txq;
struct netdev_queue *nq;
@@ -881,10 +884,12 @@ static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
+ length = skb->len;
+
if (!txq_submit_skb(txq, skb)) {
int entries_left;
- txq->tx_bytes += skb->len;
+ txq->tx_bytes += length;
txq->tx_packets++;
entries_left = txq->tx_ring_size - txq->tx_desc_count;
@@ -1543,13 +1548,9 @@ mv643xx_eth_get_ringparam(struct net_device *dev, struct ethtool_ringparam *er)
er->rx_max_pending = 4096;
er->tx_max_pending = 4096;
- er->rx_mini_max_pending = 0;
- er->rx_jumbo_max_pending = 0;
er->rx_pending = mp->rx_ring_size;
er->tx_pending = mp->tx_ring_size;
- er->rx_mini_pending = 0;
- er->rx_jumbo_pending = 0;
}
static int
@@ -2593,7 +2594,7 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
if (msp == NULL)
goto out;
- msp->base = ioremap(res->start, res->end - res->start + 1);
+ msp->base = ioremap(res->start, resource_size(res));
if (msp->base == NULL)
goto out_free;
@@ -2919,6 +2920,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM;
+ dev->priv_flags |= IFF_UNICAST_FLT;
+
SET_NETDEV_DEV(dev, &pdev->dev);
if (mp->shared->win_protect)
diff --git a/drivers/net/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c
index 5f597ca592bb..d17d0624c5e6 100644
--- a/drivers/net/pxa168_eth.c
+++ b/drivers/net/ethernet/marvell/pxa168_eth.c
@@ -40,6 +40,7 @@
#include <linux/clk.h>
#include <linux/phy.h>
#include <linux/io.h>
+#include <linux/interrupt.h>
#include <linux/types.h>
#include <asm/pgtable.h>
#include <asm/system.h>
@@ -502,7 +503,7 @@ static int add_del_hash_entry(struct pxa168_eth_private *pep,
* Pick the appropriate table, start scanning for free/reusable
* entries at the index obtained by hashing the specified MAC address
*/
- start = (struct addr_table_entry *)(pep->htpr);
+ start = pep->htpr;
entry = start + hash_function(mac_addr);
for (i = 0; i < HOP_NUMBER; i++) {
if (!(le32_to_cpu(entry->lo) & HASH_ENTRY_VALID)) {
@@ -1267,6 +1268,9 @@ static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
pep->tx_skb[tx_index] = skb;
desc->byte_cnt = length;
desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
+
+ skb_tx_timestamp(skb);
+
wmb();
desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
TX_ZERO_PADDING | TX_LAST_DESC | TX_EN_INT;
@@ -1502,7 +1506,7 @@ static int pxa168_eth_probe(struct platform_device *pdev)
err = -ENODEV;
goto err_netdev;
}
- pep->base = ioremap(res->start, res->end - res->start + 1);
+ pep->base = ioremap(res->start, resource_size(res));
if (pep->base == NULL) {
err = -ENOMEM;
goto err_netdev;
diff --git a/drivers/net/skge.c b/drivers/net/ethernet/marvell/skge.c
index f4be5c78ebfd..c7b60839ac99 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/ethernet/marvell/skge.c
@@ -50,7 +50,7 @@
#include "skge.h"
#define DRV_NAME "skge"
-#define DRV_VERSION "1.13"
+#define DRV_VERSION "1.14"
#define DEFAULT_TX_RING_SIZE 128
#define DEFAULT_RX_RING_SIZE 512
@@ -83,17 +83,20 @@ module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
static DEFINE_PCI_DEVICE_TABLE(skge_id_table) = {
- { PCI_DEVICE(PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C940) },
- { PCI_DEVICE(PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C940B) },
- { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_GE) },
- { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_YU) },
- { PCI_DEVICE(PCI_VENDOR_ID_DLINK, PCI_DEVICE_ID_DLINK_DGE510T) },
- { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b01) }, /* DGE-530T */
- { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4320) },
- { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5005) }, /* Belkin */
- { PCI_DEVICE(PCI_VENDOR_ID_CNET, PCI_DEVICE_ID_CNET_GIGACARD) },
- { PCI_DEVICE(PCI_VENDOR_ID_LINKSYS, PCI_DEVICE_ID_LINKSYS_EG1064) },
- { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0015 },
+ { PCI_DEVICE(PCI_VENDOR_ID_3COM, 0x1700) }, /* 3Com 3C940 */
+ { PCI_DEVICE(PCI_VENDOR_ID_3COM, 0x80EB) }, /* 3Com 3C940B */
+#ifdef CONFIG_SKGE_GENESIS
+ { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x4300) }, /* SK-9xx */
+#endif
+ { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x4320) }, /* SK-98xx V2.0 */
+ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b01) }, /* D-Link DGE-530T (rev.B) */
+ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4c00) }, /* D-Link DGE-530T */
+ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4302) }, /* D-Link DGE-530T Rev C1 */
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4320) }, /* Marvell Yukon 88E8001/8003/8010 */
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5005) }, /* Belkin */
+ { PCI_DEVICE(PCI_VENDOR_ID_CNET, 0x434E) }, /* CNet PowerG-2000 */
+ { PCI_DEVICE(PCI_VENDOR_ID_LINKSYS, 0x1064) }, /* Linksys EG1064 v2 */
+ { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0015 }, /* Linksys EG1032 v2 */
{ 0 }
};
MODULE_DEVICE_TABLE(pci, skge_id_table);
@@ -110,6 +113,7 @@ static void yukon_init(struct skge_hw *hw, int port);
static void genesis_mac_init(struct skge_hw *hw, int port);
static void genesis_link_up(struct skge_port *skge);
static void skge_set_multicast(struct net_device *dev);
+static irqreturn_t skge_intr(int irq, void *dev_id);
/* Avoid conditionals by using array */
static const int txqaddr[] = { Q_XA1, Q_XA2 };
@@ -119,6 +123,15 @@ static const u32 txirqmask[] = { IS_XA1_F, IS_XA2_F };
static const u32 napimask[] = { IS_R1_F|IS_XA1_F, IS_R2_F|IS_XA2_F };
static const u32 portmask[] = { IS_PORT_1, IS_PORT_2 };
+static inline bool is_genesis(const struct skge_hw *hw)
+{
+#ifdef CONFIG_SKGE_GENESIS
+ return hw->chip_id == CHIP_ID_GENESIS;
+#else
+ return false;
+#endif
+}
+
static int skge_get_regs_len(struct net_device *dev)
{
return 0x4000;
@@ -146,7 +159,7 @@ static void skge_get_regs(struct net_device *dev, struct ethtool_regs *regs,
/* Wake on Lan only supported on Yukon chips with rev 1 or above */
static u32 wol_supported(const struct skge_hw *hw)
{
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
return 0;
if (hw->chip_id == CHIP_ID_YUKON && hw->chip_rev == 0)
@@ -270,7 +283,7 @@ static u32 skge_supported_modes(const struct skge_hw *hw)
SUPPORTED_Autoneg |
SUPPORTED_TP);
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
supported &= ~(SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half |
@@ -433,7 +446,7 @@ static void skge_get_ethtool_stats(struct net_device *dev,
{
struct skge_port *skge = netdev_priv(dev);
- if (skge->hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(skge->hw))
genesis_get_stats(skge, data);
else
yukon_get_stats(skge, data);
@@ -448,7 +461,7 @@ static struct net_device_stats *skge_get_stats(struct net_device *dev)
struct skge_port *skge = netdev_priv(dev);
u64 data[ARRAY_SIZE(skge_stats)];
- if (skge->hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(skge->hw))
genesis_get_stats(skge, data);
else
yukon_get_stats(skge, data);
@@ -484,13 +497,9 @@ static void skge_get_ring_param(struct net_device *dev,
p->rx_max_pending = MAX_RX_RING_SIZE;
p->tx_max_pending = MAX_TX_RING_SIZE;
- p->rx_mini_max_pending = 0;
- p->rx_jumbo_max_pending = 0;
p->rx_pending = skge->rx_ring.count;
p->tx_pending = skge->tx_ring.count;
- p->rx_mini_pending = 0;
- p->rx_jumbo_pending = 0;
}
static int skge_set_ring_param(struct net_device *dev,
@@ -589,7 +598,7 @@ static int skge_set_pauseparam(struct net_device *dev,
/* Chip internal frequency for clock calculations */
static inline u32 hwkhz(const struct skge_hw *hw)
{
- return (hw->chip_id == CHIP_ID_GENESIS) ? 53125 : 78125;
+ return is_genesis(hw) ? 53125 : 78125;
}
/* Chip HZ to microseconds */
@@ -674,7 +683,7 @@ static void skge_led(struct skge_port *skge, enum led_mode mode)
int port = skge->port;
spin_lock_bh(&hw->phy_lock);
- if (hw->chip_id == CHIP_ID_GENESIS) {
+ if (is_genesis(hw)) {
switch (mode) {
case LED_MODE_OFF:
if (hw->phy_type == SK_PHY_BCOM)
@@ -1053,7 +1062,6 @@ static void skge_link_down(struct skge_port *skge)
netif_info(skge, link, skge->netdev, "Link is down\n");
}
-
static void xm_link_down(struct skge_hw *hw, int port)
{
struct net_device *dev = hw->dev[port];
@@ -1172,7 +1180,6 @@ static void genesis_reset(struct skge_hw *hw, int port)
xm_write32(hw, port, XM_MODE, reg | XM_MD_FRF);
}
-
/* Convert mode to MII values */
static const u16 phy_pause_map[] = {
[FLOW_MODE_NONE] = 0,
@@ -2405,7 +2412,7 @@ static void skge_phy_reset(struct skge_port *skge)
netif_carrier_off(skge->netdev);
spin_lock_bh(&hw->phy_lock);
- if (hw->chip_id == CHIP_ID_GENESIS) {
+ if (is_genesis(hw)) {
genesis_reset(hw, port);
genesis_mac_init(hw, port);
} else {
@@ -2436,7 +2443,8 @@ static int skge_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCGMIIREG: {
u16 val = 0;
spin_lock_bh(&hw->phy_lock);
- if (hw->chip_id == CHIP_ID_GENESIS)
+
+ if (is_genesis(hw))
err = __xm_phy_read(hw, skge->port, data->reg_num & 0x1f, &val);
else
err = __gm_phy_read(hw, skge->port, data->reg_num & 0x1f, &val);
@@ -2447,7 +2455,7 @@ static int skge_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCSMIIREG:
spin_lock_bh(&hw->phy_lock);
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
err = xm_phy_write(hw, skge->port, data->reg_num & 0x1f,
data->val_in);
else
@@ -2557,9 +2565,19 @@ static int skge_up(struct net_device *dev)
if (err)
goto free_rx_ring;
+ if (hw->ports == 1) {
+ err = request_irq(hw->pdev->irq, skge_intr, IRQF_SHARED,
+ dev->name, hw);
+ if (err) {
+ netdev_err(dev, "Unable to allocate interrupt %d error: %d\n",
+ hw->pdev->irq, err);
+ goto free_tx_ring;
+ }
+ }
+
/* Initialize MAC */
spin_lock_bh(&hw->phy_lock);
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
genesis_mac_init(hw, port);
else
yukon_mac_init(hw, port);
@@ -2584,11 +2602,14 @@ static int skge_up(struct net_device *dev)
spin_lock_irq(&hw->hw_lock);
hw->intr_mask |= portmask[port];
skge_write32(hw, B0_IMSK, hw->intr_mask);
+ skge_read32(hw, B0_IMSK);
spin_unlock_irq(&hw->hw_lock);
napi_enable(&skge->napi);
return 0;
+ free_tx_ring:
+ kfree(skge->tx_ring.start);
free_rx_ring:
skge_rx_clean(skge);
kfree(skge->rx_ring.start);
@@ -2621,7 +2642,7 @@ static int skge_down(struct net_device *dev)
netif_tx_disable(dev);
- if (hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC)
+ if (is_genesis(hw) && hw->phy_type == SK_PHY_XMAC)
del_timer_sync(&skge->link_timer);
napi_disable(&skge->napi);
@@ -2629,11 +2650,15 @@ static int skge_down(struct net_device *dev)
spin_lock_irq(&hw->hw_lock);
hw->intr_mask &= ~portmask[port];
- skge_write32(hw, B0_IMSK, hw->intr_mask);
+ skge_write32(hw, B0_IMSK, (hw->ports == 1) ? 0 : hw->intr_mask);
+ skge_read32(hw, B0_IMSK);
spin_unlock_irq(&hw->hw_lock);
+ if (hw->ports == 1)
+ free_irq(hw->pdev->irq, hw);
+
skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
genesis_stop(skge);
else
yukon_stop(skge);
@@ -2661,7 +2686,7 @@ static int skge_down(struct net_device *dev)
skge_rx_stop(hw, port);
- if (hw->chip_id == CHIP_ID_GENESIS) {
+ if (is_genesis(hw)) {
skge_write8(hw, SK_REG(port, TX_MFF_CTRL2), MFF_RST_SET);
skge_write8(hw, SK_REG(port, RX_MFF_CTRL2), MFF_RST_SET);
} else {
@@ -2745,10 +2770,10 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb,
control |= BMU_STFWD;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- map = pci_map_page(hw->pdev, frag->page, frag->page_offset,
- frag->size, PCI_DMA_TODEVICE);
+ map = skb_frag_dma_map(&hw->pdev->dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
e = e->next;
e->skb = skb;
@@ -2758,9 +2783,9 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb,
tf->dma_lo = map;
tf->dma_hi = (u64) map >> 32;
dma_unmap_addr_set(e, mapaddr, map);
- dma_unmap_len_set(e, maplen, frag->size);
+ dma_unmap_len_set(e, maplen, skb_frag_size(frag));
- tf->control = BMU_OWN | BMU_SW | control | frag->size;
+ tf->control = BMU_OWN | BMU_SW | control | skb_frag_size(frag);
}
tf->control |= BMU_EOF | BMU_IRQ_EOF;
}
@@ -2957,7 +2982,7 @@ static void yukon_set_multicast(struct net_device *dev)
static inline u16 phy_length(const struct skge_hw *hw, u32 status)
{
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
return status >> XMR_FS_LEN_SHIFT;
else
return status >> GMR_FS_LEN_SHIFT;
@@ -2965,7 +2990,7 @@ static inline u16 phy_length(const struct skge_hw *hw, u32 status)
static inline int bad_phy_status(const struct skge_hw *hw, u32 status)
{
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
return (status & (XMR_FS_ERR | XMR_FS_2L_VLAN)) != 0;
else
return (status & GMR_FS_ANY_ERR) ||
@@ -2975,9 +3000,8 @@ static inline int bad_phy_status(const struct skge_hw *hw, u32 status)
static void skge_set_multicast(struct net_device *dev)
{
struct skge_port *skge = netdev_priv(dev);
- struct skge_hw *hw = skge->hw;
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(skge->hw))
genesis_set_multicast(dev);
else
yukon_set_multicast(dev);
@@ -3057,7 +3081,7 @@ error:
"rx err, slot %td control 0x%x status 0x%x\n",
e - skge->rx_ring.start, control, status);
- if (skge->hw->chip_id == CHIP_ID_GENESIS) {
+ if (is_genesis(skge->hw)) {
if (status & (XMR_FS_RUNT|XMR_FS_LNG_ERR))
dev->stats.rx_length_errors++;
if (status & XMR_FS_FRA_ERR)
@@ -3171,7 +3195,7 @@ static void skge_mac_parity(struct skge_hw *hw, int port)
++dev->stats.tx_heartbeat_errors;
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
skge_write16(hw, SK_REG(port, TX_MFF_CTRL1),
MFF_CLR_PERR);
else
@@ -3183,7 +3207,7 @@ static void skge_mac_parity(struct skge_hw *hw, int port)
static void skge_mac_intr(struct skge_hw *hw, int port)
{
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
genesis_mac_intr(hw, port);
else
yukon_mac_intr(hw, port);
@@ -3195,7 +3219,7 @@ static void skge_error_irq(struct skge_hw *hw)
struct pci_dev *pdev = hw->pdev;
u32 hwstatus = skge_read32(hw, B0_HWE_ISRC);
- if (hw->chip_id == CHIP_ID_GENESIS) {
+ if (is_genesis(hw)) {
/* clear xmac errors */
if (hwstatus & (IS_NO_STAT_M1|IS_NO_TIST_M1))
skge_write16(hw, RX_MFF_CTRL1, MFF_CLR_INSTAT);
@@ -3278,7 +3302,7 @@ static void skge_extirq(unsigned long arg)
struct skge_port *skge = netdev_priv(dev);
spin_lock(&hw->phy_lock);
- if (hw->chip_id != CHIP_ID_GENESIS)
+ if (!is_genesis(hw))
yukon_phy_intr(skge);
else if (hw->phy_type == SK_PHY_BCOM)
bcom_phy_intr(skge);
@@ -3397,7 +3421,7 @@ static int skge_set_mac_address(struct net_device *dev, void *p)
memcpy_toio(hw->regs + B2_MAC_1 + port*8, dev->dev_addr, ETH_ALEN);
memcpy_toio(hw->regs + B2_MAC_2 + port*8, dev->dev_addr, ETH_ALEN);
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
xm_outaddr(hw, port, XM_SA, dev->dev_addr);
else {
gma_set_addr(hw, port, GM_SRC_ADDR_1L, dev->dev_addr);
@@ -3473,6 +3497,7 @@ static int skge_reset(struct skge_hw *hw)
switch (hw->chip_id) {
case CHIP_ID_GENESIS:
+#ifdef CONFIG_SKGE_GENESIS
switch (hw->phy_type) {
case SK_PHY_XMAC:
hw->phy_addr = PHY_ADDR_XMAC;
@@ -3486,6 +3511,10 @@ static int skge_reset(struct skge_hw *hw)
return -EOPNOTSUPP;
}
break;
+#else
+ dev_err(&hw->pdev->dev, "Genesis chip detected but not configured\n");
+ return -EOPNOTSUPP;
+#endif
case CHIP_ID_YUKON:
case CHIP_ID_YUKON_LITE:
@@ -3508,7 +3537,7 @@ static int skge_reset(struct skge_hw *hw)
/* read the adapters RAM size */
t8 = skge_read8(hw, B2_E_0);
- if (hw->chip_id == CHIP_ID_GENESIS) {
+ if (is_genesis(hw)) {
if (t8 == 3) {
/* special case: 4 x 64k x 36, offset = 0x80000 */
hw->ram_size = 0x100000;
@@ -3523,10 +3552,10 @@ static int skge_reset(struct skge_hw *hw)
hw->intr_mask = IS_HW_ERR;
/* Use PHY IRQ for all but fiber based Genesis board */
- if (!(hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC))
+ if (!(is_genesis(hw) && hw->phy_type == SK_PHY_XMAC))
hw->intr_mask |= IS_EXT_REG;
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
genesis_init(hw);
else {
/* switch power to VCC (WA for VAUX problem) */
@@ -3588,10 +3617,11 @@ static int skge_reset(struct skge_hw *hw)
skge_write32(hw, B2_IRQM_INI, skge_usecs2clk(hw, 100));
skge_write32(hw, B2_IRQM_CTRL, TIM_START);
- skge_write32(hw, B0_IMSK, hw->intr_mask);
+ /* Leave irq disabled until first port is brought up. */
+ skge_write32(hw, B0_IMSK, 0);
for (i = 0; i < hw->ports; i++) {
- if (hw->chip_id == CHIP_ID_GENESIS)
+ if (is_genesis(hw))
genesis_reset(hw, i);
else
yukon_reset(hw, i);
@@ -3747,7 +3777,7 @@ static const struct net_device_ops skge_netdev_ops = {
.ndo_tx_timeout = skge_tx_timeout,
.ndo_change_mtu = skge_change_mtu,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = skge_set_multicast,
+ .ndo_set_rx_mode = skge_set_multicast,
.ndo_set_mac_address = skge_set_mac_address,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = skge_netpoll,
@@ -3802,9 +3832,9 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
skge->port = port;
/* Only used for Genesis XMAC */
- setup_timer(&skge->link_timer, xm_link_timer, (unsigned long) skge);
-
- if (hw->chip_id != CHIP_ID_GENESIS) {
+ if (is_genesis(hw))
+ setup_timer(&skge->link_timer, xm_link_timer, (unsigned long) skge);
+ else {
dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
NETIF_F_RXCSUM;
dev->features |= dev->hw_features;
@@ -3915,31 +3945,39 @@ static int __devinit skge_probe(struct pci_dev *pdev,
goto err_out_free_netdev;
}
- err = request_irq(pdev->irq, skge_intr, IRQF_SHARED, hw->irq_name, hw);
- if (err) {
- dev_err(&pdev->dev, "%s: cannot assign irq %d\n",
- dev->name, pdev->irq);
- goto err_out_unregister;
- }
skge_show_addr(dev);
if (hw->ports > 1) {
dev1 = skge_devinit(hw, 1, using_dac);
- if (dev1 && register_netdev(dev1) == 0)
- skge_show_addr(dev1);
- else {
- /* Failure to register second port need not be fatal */
- dev_warn(&pdev->dev, "register of second port failed\n");
- hw->dev[1] = NULL;
- hw->ports = 1;
- if (dev1)
- free_netdev(dev1);
+ if (!dev1) {
+ err = -ENOMEM;
+ goto err_out_unregister;
+ }
+
+ err = register_netdev(dev1);
+ if (err) {
+ dev_err(&pdev->dev, "cannot register second net device\n");
+ goto err_out_free_dev1;
}
+
+ err = request_irq(pdev->irq, skge_intr, IRQF_SHARED,
+ hw->irq_name, hw);
+ if (err) {
+ dev_err(&pdev->dev, "cannot assign irq %d\n",
+ pdev->irq);
+ goto err_out_unregister_dev1;
+ }
+
+ skge_show_addr(dev1);
}
pci_set_drvdata(pdev, hw);
return 0;
+err_out_unregister_dev1:
+ unregister_netdev(dev1);
+err_out_free_dev1:
+ free_netdev(dev1);
err_out_unregister:
unregister_netdev(dev);
err_out_free_netdev:
@@ -3977,14 +4015,19 @@ static void __devexit skge_remove(struct pci_dev *pdev)
spin_lock_irq(&hw->hw_lock);
hw->intr_mask = 0;
- skge_write32(hw, B0_IMSK, 0);
- skge_read32(hw, B0_IMSK);
+
+ if (hw->ports > 1) {
+ skge_write32(hw, B0_IMSK, 0);
+ skge_read32(hw, B0_IMSK);
+ free_irq(pdev->irq, hw);
+ }
spin_unlock_irq(&hw->hw_lock);
skge_write16(hw, B0_LED, LED_STAT_OFF);
skge_write8(hw, B0_CTST, CS_RST_SET);
- free_irq(pdev->irq, hw);
+ if (hw->ports > 1)
+ free_irq(pdev->irq, hw);
pci_release_regions(pdev);
pci_disable_device(pdev);
if (dev1)
diff --git a/drivers/net/skge.h b/drivers/net/ethernet/marvell/skge.h
index 598bf7a1a55e..a2eb34115844 100644
--- a/drivers/net/skge.h
+++ b/drivers/net/ethernet/marvell/skge.h
@@ -3,6 +3,7 @@
*/
#ifndef _SKGE_H
#define _SKGE_H
+#include <linux/interrupt.h>
/* PCI config registers */
#define PCI_DEV_REG1 0x40
diff --git a/drivers/net/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 3ee41da130c2..7803efa46eb2 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -32,6 +32,7 @@
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/pci.h>
+#include <linux/interrupt.h>
#include <linux/ip.h>
#include <linux/slab.h>
#include <net/ip.h>
@@ -49,7 +50,7 @@
#include "sky2.h"
#define DRV_NAME "sky2"
-#define DRV_VERSION "1.28"
+#define DRV_VERSION "1.30"
/*
* The Yukon II chipset takes 64 bit command blocks (called list elements)
@@ -67,7 +68,7 @@
#define MAX_SKB_TX_LE (2 + (sizeof(dma_addr_t)/sizeof(u32))*(MAX_SKB_FRAGS+1))
#define TX_MIN_PENDING (MAX_SKB_TX_LE+1)
#define TX_MAX_PENDING 1024
-#define TX_DEF_PENDING 127
+#define TX_DEF_PENDING 63
#define TX_WATCHDOG (5 * HZ)
#define NAPI_WEIGHT 64
@@ -147,6 +148,7 @@ static const unsigned rxqaddr[] = { Q_R1, Q_R2 };
static const u32 portirq_msk[] = { Y2_IS_PORT_1, Y2_IS_PORT_2 };
static void sky2_set_multicast(struct net_device *dev);
+static irqreturn_t sky2_intr(int irq, void *dev_id);
/* Access to PHY via serial interconnect */
static int gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val)
@@ -625,6 +627,63 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
if (ledover)
gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);
+ } else if (hw->chip_id == CHIP_ID_YUKON_PRM &&
+ (sky2_read8(hw, B2_MAC_CFG) & 0xf) == 0x7) {
+ int i;
+ /* This a phy register setup workaround copied from vendor driver. */
+ static const struct {
+ u16 reg, val;
+ } eee_afe[] = {
+ { 0x156, 0x58ce },
+ { 0x153, 0x99eb },
+ { 0x141, 0x8064 },
+ /* { 0x155, 0x130b },*/
+ { 0x000, 0x0000 },
+ { 0x151, 0x8433 },
+ { 0x14b, 0x8c44 },
+ { 0x14c, 0x0f90 },
+ { 0x14f, 0x39aa },
+ /* { 0x154, 0x2f39 },*/
+ { 0x14d, 0xba33 },
+ { 0x144, 0x0048 },
+ { 0x152, 0x2010 },
+ /* { 0x158, 0x1223 },*/
+ { 0x140, 0x4444 },
+ { 0x154, 0x2f3b },
+ { 0x158, 0xb203 },
+ { 0x157, 0x2029 },
+ };
+
+ /* Start Workaround for OptimaEEE Rev.Z0 */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00fb);
+
+ gm_phy_write(hw, port, 1, 0x4099);
+ gm_phy_write(hw, port, 3, 0x1120);
+ gm_phy_write(hw, port, 11, 0x113c);
+ gm_phy_write(hw, port, 14, 0x8100);
+ gm_phy_write(hw, port, 15, 0x112a);
+ gm_phy_write(hw, port, 17, 0x1008);
+
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00fc);
+ gm_phy_write(hw, port, 1, 0x20b0);
+
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00ff);
+
+ for (i = 0; i < ARRAY_SIZE(eee_afe); i++) {
+ /* apply AFE settings */
+ gm_phy_write(hw, port, 17, eee_afe[i].val);
+ gm_phy_write(hw, port, 16, eee_afe[i].reg | 1u<<13);
+ }
+
+ /* End Workaround for OptimaEEE */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+
+ /* Enable 10Base-Te (EEE) */
+ if (hw->chip_id >= CHIP_ID_YUKON_PRM) {
+ reg = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
+ gm_phy_write(hw, port, PHY_MARV_EXT_CTRL,
+ reg | PHY_M_10B_TE_ENABLE);
+ }
}
/* Enable phy interrupt on auto-negotiation complete (or link up) */
@@ -713,6 +772,20 @@ static void sky2_phy_power_down(struct sky2_hw *hw, unsigned port)
sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
}
+/* configure IPG according to used link speed */
+static void sky2_set_ipg(struct sky2_port *sky2)
+{
+ u16 reg;
+
+ reg = gma_read16(sky2->hw, sky2->port, GM_SERIAL_MODE);
+ reg &= ~GM_SMOD_IPG_MSK;
+ if (sky2->speed > SPEED_100)
+ reg |= IPG_DATA_VAL(IPG_DATA_DEF_1000);
+ else
+ reg |= IPG_DATA_VAL(IPG_DATA_DEF_10_100);
+ gma_write16(sky2->hw, sky2->port, GM_SERIAL_MODE, reg);
+}
+
/* Enable Rx/Tx */
static void sky2_enable_rx_tx(struct sky2_port *sky2)
{
@@ -796,6 +869,7 @@ static void sky2_wol_init(struct sky2_port *sky2)
/* block receiver */
sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
+ sky2_read32(hw, B0_CTST);
}
static void sky2_set_tx_stfwd(struct sky2_hw *hw, unsigned port)
@@ -881,7 +955,7 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
/* serial mode register */
reg = DATA_BLIND_VAL(DATA_BLIND_DEF) |
- GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
+ GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF_1000);
if (hw->dev[port]->mtu > ETH_DATA_LEN)
reg |= GM_SMOD_JUMBO_ENA;
@@ -1141,14 +1215,13 @@ static int sky2_rx_map_skb(struct pci_dev *pdev, struct rx_ring_info *re,
dma_unmap_len_set(re, data_size, size);
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- re->frag_addr[i] = pci_map_page(pdev, frag->page,
- frag->page_offset,
- frag->size,
- PCI_DMA_FROMDEVICE);
+ re->frag_addr[i] = skb_frag_dma_map(&pdev->dev, frag, 0,
+ skb_frag_size(frag),
+ DMA_FROM_DEVICE);
- if (pci_dma_mapping_error(pdev, re->frag_addr[i]))
+ if (dma_mapping_error(&pdev->dev, re->frag_addr[i]))
goto map_page_error;
}
return 0;
@@ -1156,7 +1229,7 @@ static int sky2_rx_map_skb(struct pci_dev *pdev, struct rx_ring_info *re,
map_page_error:
while (--i >= 0) {
pci_unmap_page(pdev, re->frag_addr[i],
- skb_shinfo(skb)->frags[i].size,
+ skb_frag_size(&skb_shinfo(skb)->frags[i]),
PCI_DMA_FROMDEVICE);
}
@@ -1180,7 +1253,7 @@ static void sky2_rx_unmap_skb(struct pci_dev *pdev, struct rx_ring_info *re)
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
pci_unmap_page(pdev, re->frag_addr[i],
- skb_shinfo(skb)->frags[i].size,
+ skb_frag_size(&skb_shinfo(skb)->frags[i]),
PCI_DMA_FROMDEVICE);
}
@@ -1202,6 +1275,14 @@ static void rx_set_checksum(struct sky2_port *sky2)
? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
}
+/*
+ * Fixed initial key as seed to RSS.
+ */
+static const uint32_t rss_init_key[10] = {
+ 0x7c3351da, 0x51c5cf4e, 0x44adbdd1, 0xe8d38d18, 0x48897c43,
+ 0xb1d60e7e, 0x6a3dd760, 0x01a2e453, 0x16f46f13, 0x1a0e7b30
+};
+
/* Enable/disable receive hash calculation (RSS) */
static void rx_set_rss(struct net_device *dev, u32 features)
{
@@ -1217,12 +1298,9 @@ static void rx_set_rss(struct net_device *dev, u32 features)
/* Program RSS initial values */
if (features & NETIF_F_RXHASH) {
- u32 key[nkeys];
-
- get_random_bytes(key, nkeys * sizeof(u32));
for (i = 0; i < nkeys; i++)
sky2_write32(hw, SK_REG(sky2->port, RSS_KEY + i * 4),
- key[i]);
+ rss_init_key[i]);
/* Need to turn on (undocumented) flag to make hashing work */
sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T),
@@ -1361,13 +1439,14 @@ static inline unsigned sky2_rx_pad(const struct sky2_hw *hw)
* Allocate an skb for receiving. If the MTU is large enough
* make the skb non-linear with a fragment list of pages.
*/
-static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2)
+static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2, gfp_t gfp)
{
struct sk_buff *skb;
int i;
- skb = netdev_alloc_skb(sky2->netdev,
- sky2->rx_data_size + sky2_rx_pad(sky2->hw));
+ skb = __netdev_alloc_skb(sky2->netdev,
+ sky2->rx_data_size + sky2_rx_pad(sky2->hw),
+ gfp);
if (!skb)
goto nomem;
@@ -1385,7 +1464,7 @@ static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2)
skb_reserve(skb, NET_IP_ALIGN);
for (i = 0; i < sky2->rx_nfrags; i++) {
- struct page *page = alloc_page(GFP_ATOMIC);
+ struct page *page = alloc_page(gfp);
if (!page)
goto free_partial;
@@ -1415,7 +1494,7 @@ static int sky2_alloc_rx_skbs(struct sky2_port *sky2)
for (i = 0; i < sky2->rx_pending; i++) {
struct rx_ring_info *re = sky2->rx_ring + i;
- re->skb = sky2_rx_alloc(sky2);
+ re->skb = sky2_rx_alloc(sky2, GFP_KERNEL);
if (!re->skb)
return -ENOMEM;
@@ -1448,7 +1527,7 @@ static void sky2_rx_start(struct sky2_port *sky2)
sky2_qset(hw, rxq);
/* On PCI express lowering the watermark gives better performance */
- if (pci_find_capability(hw->pdev, PCI_CAP_ID_EXP))
+ if (pci_is_pcie(hw->pdev))
sky2_write32(hw, Q_ADDR(rxq, Q_WM), BMU_WM_PEX);
/* These chips have no ram buffer?
@@ -1632,8 +1711,31 @@ static void sky2_hw_up(struct sky2_port *sky2)
sky2_rx_start(sky2);
}
+/* Setup device IRQ and enable napi to process */
+static int sky2_setup_irq(struct sky2_hw *hw, const char *name)
+{
+ struct pci_dev *pdev = hw->pdev;
+ int err;
+
+ err = request_irq(pdev->irq, sky2_intr,
+ (hw->flags & SKY2_HW_USE_MSI) ? 0 : IRQF_SHARED,
+ name, hw);
+ if (err)
+ dev_err(&pdev->dev, "cannot assign irq %d\n", pdev->irq);
+ else {
+ hw->flags |= SKY2_HW_IRQ_SETUP;
+
+ napi_enable(&hw->napi);
+ sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
+ sky2_read32(hw, B0_IMSK);
+ }
+
+ return err;
+}
+
+
/* Bring up network interface. */
-static int sky2_up(struct net_device *dev)
+static int sky2_open(struct net_device *dev)
{
struct sky2_port *sky2 = netdev_priv(dev);
struct sky2_hw *hw = sky2->hw;
@@ -1647,8 +1749,17 @@ static int sky2_up(struct net_device *dev)
if (err)
goto err_out;
+ /* With single port, IRQ is setup when device is brought up */
+ if (hw->ports == 1 && (err = sky2_setup_irq(hw, dev->name)))
+ goto err_out;
+
sky2_hw_up(sky2);
+ if (hw->chip_id == CHIP_ID_YUKON_OPT ||
+ hw->chip_id == CHIP_ID_YUKON_PRM ||
+ hw->chip_id == CHIP_ID_YUKON_OP_2)
+ imask |= Y2_IS_PHY_QLNK; /* enable PHY Quick Link */
+
/* Enable interrupts from phy/mac for port */
imask = sky2_read32(hw, B0_IMSK);
imask |= portirq_msk[port];
@@ -1826,10 +1937,10 @@ static netdev_tx_t sky2_xmit_frame(struct sk_buff *skb,
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- mapping = pci_map_page(hw->pdev, frag->page, frag->page_offset,
- frag->size, PCI_DMA_TODEVICE);
+ mapping = skb_frag_dma_map(&hw->pdev->dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
- if (pci_dma_mapping_error(hw->pdev, mapping))
+ if (dma_mapping_error(&hw->pdev->dev, mapping))
goto mapping_unwind;
upper = upper_32_bits(mapping);
@@ -1843,11 +1954,11 @@ static netdev_tx_t sky2_xmit_frame(struct sk_buff *skb,
re = sky2->tx_ring + slot;
re->flags = TX_MAP_PAGE;
dma_unmap_addr_set(re, mapaddr, mapping);
- dma_unmap_len_set(re, maplen, frag->size);
+ dma_unmap_len_set(re, maplen, skb_frag_size(frag));
le = get_tx_le(sky2, &slot);
le->addr = cpu_to_le32(lower_32_bits(mapping));
- le->length = cpu_to_le16(frag->size);
+ le->length = cpu_to_le16(skb_frag_size(frag));
le->ctrl = ctrl;
le->opcode = OP_BUFFER | HW_OWNER;
}
@@ -1942,6 +2053,8 @@ static void sky2_tx_reset(struct sky2_hw *hw, unsigned port)
sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET);
sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
+
+ sky2_read32(hw, B0_CTST);
}
static void sky2_hw_down(struct sky2_port *sky2)
@@ -1973,7 +2086,7 @@ static void sky2_hw_down(struct sky2_port *sky2)
sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
- /* Force any delayed status interrrupt and NAPI */
+ /* Force any delayed status interrupt and NAPI */
sky2_write32(hw, STAT_LEV_TIMER_CNT, 0);
sky2_write32(hw, STAT_TX_TIMER_CNT, 0);
sky2_write32(hw, STAT_ISR_TIMER_CNT, 0);
@@ -1992,7 +2105,7 @@ static void sky2_hw_down(struct sky2_port *sky2)
}
/* Network shutdown */
-static int sky2_down(struct net_device *dev)
+static int sky2_close(struct net_device *dev)
{
struct sky2_port *sky2 = netdev_priv(dev);
struct sky2_hw *hw = sky2->hw;
@@ -2003,13 +2116,25 @@ static int sky2_down(struct net_device *dev)
netif_info(sky2, ifdown, dev, "disabling interface\n");
- /* Disable port IRQ */
- sky2_write32(hw, B0_IMSK,
- sky2_read32(hw, B0_IMSK) & ~portirq_msk[sky2->port]);
- sky2_read32(hw, B0_IMSK);
+ if (hw->ports == 1) {
+ sky2_write32(hw, B0_IMSK, 0);
+ sky2_read32(hw, B0_IMSK);
- synchronize_irq(hw->pdev->irq);
- napi_synchronize(&hw->napi);
+ napi_disable(&hw->napi);
+ free_irq(hw->pdev->irq, hw);
+ hw->flags &= ~SKY2_HW_IRQ_SETUP;
+ } else {
+ u32 imask;
+
+ /* Disable port IRQ */
+ imask = sky2_read32(hw, B0_IMSK);
+ imask &= ~portirq_msk[sky2->port];
+ sky2_write32(hw, B0_IMSK, imask);
+ sky2_read32(hw, B0_IMSK);
+
+ synchronize_irq(hw->pdev->irq);
+ napi_synchronize(&hw->napi);
+ }
sky2_hw_down(sky2);
@@ -2051,6 +2176,8 @@ static void sky2_link_up(struct sky2_port *sky2)
[FC_BOTH] = "both",
};
+ sky2_set_ipg(sky2);
+
sky2_enable_rx_tx(sky2);
gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
@@ -2288,8 +2415,11 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
dev->mtu = new_mtu;
netdev_update_features(dev);
- mode = DATA_BLIND_VAL(DATA_BLIND_DEF) |
- GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
+ mode = DATA_BLIND_VAL(DATA_BLIND_DEF) | GM_SMOD_VLAN_ENA;
+ if (sky2->speed > SPEED_100)
+ mode |= IPG_DATA_VAL(IPG_DATA_DEF_1000);
+ else
+ mode |= IPG_DATA_VAL(IPG_DATA_DEF_10_100);
if (dev->mtu > ETH_DATA_LEN)
mode |= GM_SMOD_JUMBO_ENA;
@@ -2360,14 +2490,14 @@ static void skb_put_frags(struct sk_buff *skb, unsigned int hdr_space,
if (length == 0) {
/* don't need this page */
- __free_page(frag->page);
+ __skb_frag_unref(frag);
--skb_shinfo(skb)->nr_frags;
} else {
size = min(length, (unsigned) PAGE_SIZE);
- frag->size = size;
+ skb_frag_size_set(frag, size);
skb->data_len += size;
- skb->truesize += size;
+ skb->truesize += PAGE_SIZE;
skb->len += size;
length -= size;
}
@@ -2383,7 +2513,7 @@ static struct sk_buff *receive_new(struct sky2_port *sky2,
struct rx_ring_info nre;
unsigned hdr_space = sky2->rx_data_size;
- nre.skb = sky2_rx_alloc(sky2);
+ nre.skb = sky2_rx_alloc(sky2, GFP_ATOMIC);
if (unlikely(!nre.skb))
goto nobuf;
@@ -2479,7 +2609,7 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last)
if (netif_running(dev)) {
sky2_tx_complete(sky2, last);
- /* Wake unless it's detached, and called e.g. from sky2_down() */
+ /* Wake unless it's detached, and called e.g. from sky2_close() */
if (tx_avail(sky2) > MAX_SKB_TX_LE + 4)
netif_wake_queue(dev);
}
@@ -2938,6 +3068,8 @@ static u32 sky2_mhz(const struct sky2_hw *hw)
case CHIP_ID_YUKON_SUPR:
case CHIP_ID_YUKON_UL_2:
case CHIP_ID_YUKON_OPT:
+ case CHIP_ID_YUKON_PRM:
+ case CHIP_ID_YUKON_OP_2:
return 125;
case CHIP_ID_YUKON_FE:
@@ -2994,7 +3126,8 @@ static int __devinit sky2_init(struct sky2_hw *hw)
hw->flags = SKY2_HW_GIGABIT
| SKY2_HW_NEWER_PHY
| SKY2_HW_NEW_LE
- | SKY2_HW_ADV_POWER_CTL;
+ | SKY2_HW_ADV_POWER_CTL
+ | SKY2_HW_RSS_CHKSUM;
/* New transmit checksum */
if (hw->chip_rev != CHIP_REV_YU_EX_B0)
@@ -3022,7 +3155,7 @@ static int __devinit sky2_init(struct sky2_hw *hw)
/* The workaround for status conflicts VLAN tag detection. */
if (hw->chip_rev == CHIP_REV_YU_FE2_A0)
- hw->flags |= SKY2_HW_VLAN_BROKEN;
+ hw->flags |= SKY2_HW_VLAN_BROKEN | SKY2_HW_RSS_CHKSUM;
break;
case CHIP_ID_YUKON_SUPR:
@@ -3031,6 +3164,9 @@ static int __devinit sky2_init(struct sky2_hw *hw)
| SKY2_HW_NEW_LE
| SKY2_HW_AUTO_TX_SUM
| SKY2_HW_ADV_POWER_CTL;
+
+ if (hw->chip_rev == CHIP_REV_YU_SU_A0)
+ hw->flags |= SKY2_HW_RSS_CHKSUM;
break;
case CHIP_ID_YUKON_UL_2:
@@ -3039,6 +3175,8 @@ static int __devinit sky2_init(struct sky2_hw *hw)
break;
case CHIP_ID_YUKON_OPT:
+ case CHIP_ID_YUKON_PRM:
+ case CHIP_ID_YUKON_OP_2:
hw->flags = SKY2_HW_GIGABIT
| SKY2_HW_NEW_LE
| SKY2_HW_ADV_POWER_CTL;
@@ -3071,7 +3209,7 @@ static void sky2_reset(struct sky2_hw *hw)
{
struct pci_dev *pdev = hw->pdev;
u16 status;
- int i, cap;
+ int i;
u32 hwe_mask = Y2_HWE_ALL_MASK;
/* disable ASF */
@@ -3107,8 +3245,7 @@ static void sky2_reset(struct sky2_hw *hw)
sky2_write8(hw, B0_CTST, CS_MRST_CLR);
- cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
- if (cap) {
+ if (pci_is_pcie(pdev)) {
sky2_write32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS,
0xfffffffful);
@@ -3139,42 +3276,39 @@ static void sky2_reset(struct sky2_hw *hw)
sky2_pci_write32(hw, PCI_DEV_REG3, P_CLK_MACSEC_DIS);
}
- if (hw->chip_id == CHIP_ID_YUKON_OPT) {
+ if (hw->chip_id == CHIP_ID_YUKON_OPT ||
+ hw->chip_id == CHIP_ID_YUKON_PRM ||
+ hw->chip_id == CHIP_ID_YUKON_OP_2) {
u16 reg;
- u32 msk;
- if (hw->chip_rev == 0) {
+ if (hw->chip_id == CHIP_ID_YUKON_OPT && hw->chip_rev == 0) {
/* disable PCI-E PHY power down (set PHY reg 0x80, bit 7 */
sky2_write32(hw, Y2_PEX_PHY_DATA, (0x80UL << 16) | (1 << 7));
/* set PHY Link Detect Timer to 1.1 second (11x 100ms) */
reg = 10;
+
+ /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
+ sky2_write32(hw, Y2_PEX_PHY_DATA, PEX_DB_ACCESS | (0x08UL << 16));
} else {
/* set PHY Link Detect Timer to 0.4 second (4x 100ms) */
reg = 3;
}
reg <<= PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE;
+ reg |= PSM_CONFIG_REG4_RST_PHY_LINK_DETECT;
/* reset PHY Link Detect */
sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
- sky2_pci_write16(hw, PSM_CONFIG_REG4,
- reg | PSM_CONFIG_REG4_RST_PHY_LINK_DETECT);
sky2_pci_write16(hw, PSM_CONFIG_REG4, reg);
-
- /* enable PHY Quick Link */
- msk = sky2_read32(hw, B0_IMSK);
- msk |= Y2_IS_PHY_QLNK;
- sky2_write32(hw, B0_IMSK, msk);
-
/* check if PSMv2 was running before */
reg = sky2_pci_read16(hw, PSM_CONFIG_REG3);
- if (reg & PCI_EXP_LNKCTL_ASPMC) {
- cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ if (reg & PCI_EXP_LNKCTL_ASPMC)
/* restore the PCIe Link Control register */
- sky2_pci_write16(hw, cap + PCI_EXP_LNKCTL, reg);
- }
+ sky2_pci_write16(hw, pdev->pcie_cap + PCI_EXP_LNKCTL,
+ reg);
+
sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
/* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
@@ -3265,7 +3399,7 @@ static void sky2_detach(struct net_device *dev)
netif_tx_lock(dev);
netif_device_detach(dev); /* stop txq */
netif_tx_unlock(dev);
- sky2_down(dev);
+ sky2_close(dev);
}
}
@@ -3275,7 +3409,7 @@ static int sky2_reattach(struct net_device *dev)
int err = 0;
if (netif_running(dev)) {
- err = sky2_up(dev);
+ err = sky2_open(dev);
if (err) {
netdev_info(dev, "could not restart %d\n", err);
dev_close(dev);
@@ -3292,10 +3426,13 @@ static void sky2_all_down(struct sky2_hw *hw)
{
int i;
- sky2_read32(hw, B0_IMSK);
- sky2_write32(hw, B0_IMSK, 0);
- synchronize_irq(hw->pdev->irq);
- napi_disable(&hw->napi);
+ if (hw->flags & SKY2_HW_IRQ_SETUP) {
+ sky2_read32(hw, B0_IMSK);
+ sky2_write32(hw, B0_IMSK, 0);
+
+ synchronize_irq(hw->pdev->irq);
+ napi_disable(&hw->napi);
+ }
for (i = 0; i < hw->ports; i++) {
struct net_device *dev = hw->dev[i];
@@ -3328,11 +3465,12 @@ static void sky2_all_up(struct sky2_hw *hw)
netif_wake_queue(dev);
}
- sky2_write32(hw, B0_IMSK, imask);
- sky2_read32(hw, B0_IMSK);
-
- sky2_read32(hw, B0_Y2_SP_LISR);
- napi_enable(&hw->napi);
+ if (hw->flags & SKY2_HW_IRQ_SETUP) {
+ sky2_write32(hw, B0_IMSK, imask);
+ sky2_read32(hw, B0_IMSK);
+ sky2_read32(hw, B0_Y2_SP_LISR);
+ napi_enable(&hw->napi);
+ }
}
static void sky2_restart(struct work_struct *work)
@@ -3953,19 +4091,25 @@ static int sky2_set_coalesce(struct net_device *dev,
return 0;
}
+/*
+ * Hardware is limited to min of 128 and max of 2048 for ring size
+ * and rounded up to next power of two
+ * to avoid division in modulus calclation
+ */
+static unsigned long roundup_ring_size(unsigned long pending)
+{
+ return max(128ul, roundup_pow_of_two(pending+1));
+}
+
static void sky2_get_ringparam(struct net_device *dev,
struct ethtool_ringparam *ering)
{
struct sky2_port *sky2 = netdev_priv(dev);
ering->rx_max_pending = RX_MAX_PENDING;
- ering->rx_mini_max_pending = 0;
- ering->rx_jumbo_max_pending = 0;
ering->tx_max_pending = TX_MAX_PENDING;
ering->rx_pending = sky2->rx_pending;
- ering->rx_mini_pending = 0;
- ering->rx_jumbo_pending = 0;
ering->tx_pending = sky2->tx_pending;
}
@@ -3984,7 +4128,7 @@ static int sky2_set_ringparam(struct net_device *dev,
sky2->rx_pending = ering->rx_pending;
sky2->tx_pending = ering->tx_pending;
- sky2->tx_ring_size = roundup_pow_of_two(sky2->tx_pending+1);
+ sky2->tx_ring_size = roundup_ring_size(sky2->tx_pending);
return sky2_reattach(dev);
}
@@ -4175,8 +4319,18 @@ static u32 sky2_fix_features(struct net_device *dev, u32 features)
/* In order to do Jumbo packets on these chips, need to turn off the
* transmit store/forward. Therefore checksum offload won't work.
*/
- if (dev->mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_EC_U)
+ if (dev->mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_EC_U) {
+ netdev_info(dev, "checksum offload not possible with jumbo frames\n");
features &= ~(NETIF_F_TSO|NETIF_F_SG|NETIF_F_ALL_CSUM);
+ }
+
+ /* Some hardware requires receive checksum for RSS to work. */
+ if ( (features & NETIF_F_RXHASH) &&
+ !(features & NETIF_F_RXCSUM) &&
+ (sky2->hw->flags & SKY2_HW_RSS_CHKSUM)) {
+ netdev_info(dev, "receive hashing forces receive checksum\n");
+ features |= NETIF_F_RXCSUM;
+ }
return features;
}
@@ -4432,7 +4586,7 @@ static int sky2_device_event(struct notifier_block *unused,
struct net_device *dev = ptr;
struct sky2_port *sky2 = netdev_priv(dev);
- if (dev->netdev_ops->ndo_open != sky2_up || !sky2_debug)
+ if (dev->netdev_ops->ndo_open != sky2_open || !sky2_debug)
return NOTIFY_DONE;
switch (event) {
@@ -4497,13 +4651,13 @@ static __exit void sky2_debug_cleanup(void)
not allowing netpoll on second port */
static const struct net_device_ops sky2_netdev_ops[2] = {
{
- .ndo_open = sky2_up,
- .ndo_stop = sky2_down,
+ .ndo_open = sky2_open,
+ .ndo_stop = sky2_close,
.ndo_start_xmit = sky2_xmit_frame,
.ndo_do_ioctl = sky2_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = sky2_set_mac_address,
- .ndo_set_multicast_list = sky2_set_multicast,
+ .ndo_set_rx_mode = sky2_set_multicast,
.ndo_change_mtu = sky2_change_mtu,
.ndo_fix_features = sky2_fix_features,
.ndo_set_features = sky2_set_features,
@@ -4514,13 +4668,13 @@ static const struct net_device_ops sky2_netdev_ops[2] = {
#endif
},
{
- .ndo_open = sky2_up,
- .ndo_stop = sky2_down,
+ .ndo_open = sky2_open,
+ .ndo_stop = sky2_close,
.ndo_start_xmit = sky2_xmit_frame,
.ndo_do_ioctl = sky2_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = sky2_set_mac_address,
- .ndo_set_multicast_list = sky2_set_multicast,
+ .ndo_set_rx_mode = sky2_set_multicast,
.ndo_change_mtu = sky2_change_mtu,
.ndo_fix_features = sky2_fix_features,
.ndo_set_features = sky2_set_features,
@@ -4568,7 +4722,7 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
spin_lock_init(&sky2->phy_lock);
sky2->tx_pending = TX_DEF_PENDING;
- sky2->tx_ring_size = roundup_pow_of_two(TX_DEF_PENDING+1);
+ sky2->tx_ring_size = roundup_ring_size(TX_DEF_PENDING);
sky2->rx_pending = RX_DEF_PENDING;
hw->dev[port] = dev;
@@ -4676,9 +4830,11 @@ static const char *sky2_name(u8 chipid, char *buf, int sz)
"UL 2", /* 0xba */
"Unknown", /* 0xbb */
"Optima", /* 0xbc */
+ "Optima Prime", /* 0xbd */
+ "Optima 2", /* 0xbe */
};
- if (chipid >= CHIP_ID_YUKON_XL && chipid <= CHIP_ID_YUKON_OPT)
+ if (chipid >= CHIP_ID_YUKON_XL && chipid <= CHIP_ID_YUKON_OP_2)
strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz);
else
snprintf(buf, sz, "(chip %#x)", chipid);
@@ -4688,7 +4844,7 @@ static const char *sky2_name(u8 chipid, char *buf, int sz)
static int __devinit sky2_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- struct net_device *dev;
+ struct net_device *dev, *dev1;
struct sky2_hw *hw;
int err, using_dac = 0, wol_default;
u32 reg;
@@ -4814,33 +4970,26 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
netif_napi_add(dev, &hw->napi, sky2_poll, NAPI_WEIGHT);
- err = request_irq(pdev->irq, sky2_intr,
- (hw->flags & SKY2_HW_USE_MSI) ? 0 : IRQF_SHARED,
- hw->irq_name, hw);
- if (err) {
- dev_err(&pdev->dev, "cannot assign irq %d\n", pdev->irq);
- goto err_out_unregister;
- }
- sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
- napi_enable(&hw->napi);
-
sky2_show_addr(dev);
if (hw->ports > 1) {
- struct net_device *dev1;
-
- err = -ENOMEM;
dev1 = sky2_init_netdev(hw, 1, using_dac, wol_default);
- if (dev1 && (err = register_netdev(dev1)) == 0)
- sky2_show_addr(dev1);
- else {
- dev_warn(&pdev->dev,
- "register of second port failed (%d)\n", err);
- hw->dev[1] = NULL;
- hw->ports = 1;
- if (dev1)
- free_netdev(dev1);
+ if (!dev1) {
+ err = -ENOMEM;
+ goto err_out_unregister;
+ }
+
+ err = register_netdev(dev1);
+ if (err) {
+ dev_err(&pdev->dev, "cannot register second net device\n");
+ goto err_out_free_dev1;
}
+
+ err = sky2_setup_irq(hw, hw->irq_name);
+ if (err)
+ goto err_out_unregister_dev1;
+
+ sky2_show_addr(dev1);
}
setup_timer(&hw->watchdog_timer, sky2_watchdog, (unsigned long) hw);
@@ -4851,6 +5000,10 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
return 0;
+err_out_unregister_dev1:
+ unregister_netdev(dev1);
+err_out_free_dev1:
+ free_netdev(dev1);
err_out_unregister:
if (hw->flags & SKY2_HW_USE_MSI)
pci_disable_msi(pdev);
@@ -4890,13 +5043,18 @@ static void __devexit sky2_remove(struct pci_dev *pdev)
unregister_netdev(hw->dev[i]);
sky2_write32(hw, B0_IMSK, 0);
+ sky2_read32(hw, B0_IMSK);
sky2_power_aux(hw);
sky2_write8(hw, B0_CTST, CS_RST_SET);
sky2_read8(hw, B0_CTST);
- free_irq(pdev->irq, hw);
+ if (hw->ports > 1) {
+ napi_disable(&hw->napi);
+ free_irq(pdev->irq, hw);
+ }
+
if (hw->flags & SKY2_HW_USE_MSI)
pci_disable_msi(pdev);
pci_free_consistent(pdev, hw->st_size * sizeof(struct sky2_status_le),
diff --git a/drivers/net/sky2.h b/drivers/net/ethernet/marvell/sky2.h
index 318c9ae7bf91..ff6f58bf822a 100644
--- a/drivers/net/sky2.h
+++ b/drivers/net/ethernet/marvell/sky2.h
@@ -412,7 +412,7 @@ enum {
Y2_IS_HW_ERR = 1<<31, /* Interrupt HW Error */
Y2_IS_STAT_BMU = 1<<30, /* Status BMU Interrupt */
Y2_IS_ASF = 1<<29, /* ASF subsystem Interrupt */
-
+ Y2_IS_CPU_TO = 1<<28, /* CPU Timeout */
Y2_IS_POLL_CHK = 1<<27, /* Check IRQ from polling unit */
Y2_IS_TWSI_RDY = 1<<26, /* IRQ on end of TWSI Tx */
Y2_IS_IRQ_SW = 1<<25, /* SW forced IRQ */
@@ -547,6 +547,8 @@ enum {
CHIP_ID_YUKON_SUPR = 0xb9, /* YUKON-2 Supreme */
CHIP_ID_YUKON_UL_2 = 0xba, /* YUKON-2 Ultra 2 */
CHIP_ID_YUKON_OPT = 0xbc, /* YUKON-2 Optima */
+ CHIP_ID_YUKON_PRM = 0xbd, /* YUKON-2 Optima Prime */
+ CHIP_ID_YUKON_OP_2 = 0xbe, /* YUKON-2 Optima 2 */
};
enum yukon_xl_rev {
@@ -1420,8 +1422,10 @@ enum {
PHY_M_EC_FIB_AN_ENA = 1<<3, /* Fiber Auto-Neg. Enable (88E1011S only) */
PHY_M_EC_DTE_D_ENA = 1<<2, /* DTE Detect Enable (88E1111 only) */
PHY_M_EC_TX_TIM_CT = 1<<1, /* RGMII Tx Timing Control */
- PHY_M_EC_TRANS_DIS = 1<<0, /* Transmitter Disable (88E1111 only) */};
+ PHY_M_EC_TRANS_DIS = 1<<0, /* Transmitter Disable (88E1111 only) */
+ PHY_M_10B_TE_ENABLE = 1<<7, /* 10Base-Te Enable (88E8079 and above) */
+};
#define PHY_M_EC_M_DSC(x) ((u16)(x)<<10 & PHY_M_EC_M_DSC_MSK)
/* 00=1x; 01=2x; 10=3x; 11=4x */
#define PHY_M_EC_S_DSC(x) ((u16)(x)<<8 & PHY_M_EC_S_DSC_MSK)
@@ -1807,10 +1811,11 @@ enum {
};
#define DATA_BLIND_VAL(x) (((x)<<11) & GM_SMOD_DATABL_MSK)
-#define DATA_BLIND_DEF 0x04
-
#define IPG_DATA_VAL(x) (x & GM_SMOD_IPG_MSK)
-#define IPG_DATA_DEF 0x1e
+
+#define DATA_BLIND_DEF 0x04
+#define IPG_DATA_DEF_1000 0x1e
+#define IPG_DATA_DEF_10_100 0x18
/* GM_SMI_CTRL 16 bit r/w SMI Control Register */
enum {
@@ -2281,6 +2286,8 @@ struct sky2_hw {
#define SKY2_HW_ADV_POWER_CTL 0x00000080 /* additional PHY power regs */
#define SKY2_HW_RSS_BROKEN 0x00000100
#define SKY2_HW_VLAN_BROKEN 0x00000200
+#define SKY2_HW_RSS_CHKSUM 0x00000400 /* RSS requires chksum */
+#define SKY2_HW_IRQ_SETUP 0x00000800
u8 chip_id;
u8 chip_rev;
diff --git a/drivers/net/ethernet/mellanox/Kconfig b/drivers/net/ethernet/mellanox/Kconfig
new file mode 100644
index 000000000000..d8099a7903d3
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/Kconfig
@@ -0,0 +1,23 @@
+#
+# Mellanox driver configuration
+#
+
+config NET_VENDOR_MELLANOX
+ bool "Mellanox devices"
+ default y
+ depends on PCI && INET
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Mellanox cards. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_MELLANOX
+
+source "drivers/net/ethernet/mellanox/mlx4/Kconfig"
+
+endif # NET_VENDOR_MELLANOX
diff --git a/drivers/net/ethernet/mellanox/Makefile b/drivers/net/ethernet/mellanox/Makefile
new file mode 100644
index 000000000000..37afb9683372
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Mellanox device drivers.
+#
+
+obj-$(CONFIG_MLX4_CORE) += mlx4/
diff --git a/drivers/net/ethernet/mellanox/mlx4/Kconfig b/drivers/net/ethernet/mellanox/mlx4/Kconfig
new file mode 100644
index 000000000000..1bb93531f1ba
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx4/Kconfig
@@ -0,0 +1,27 @@
+#
+# Mellanox driver configuration
+#
+
+config MLX4_EN
+ tristate "Mellanox Technologies 10Gbit Ethernet support"
+ depends on PCI && INET
+ select MLX4_CORE
+ select INET_LRO
+ ---help---
+ This driver supports Mellanox Technologies ConnectX Ethernet
+ devices.
+
+config MLX4_CORE
+ tristate
+ depends on PCI
+ default n
+
+config MLX4_DEBUG
+ bool "Verbose debugging output" if (MLX4_CORE && EXPERT)
+ depends on MLX4_CORE
+ default y
+ ---help---
+ This option causes debugging code to be compiled into the
+ mlx4_core driver. The output can be turned on via the
+ debug_level module parameter (which can also be set after
+ the driver is loaded through sysfs).
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/ethernet/mellanox/mlx4/Makefile
index d1aa45a15854..d1aa45a15854 100644
--- a/drivers/net/mlx4/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx4/Makefile
diff --git a/drivers/net/mlx4/alloc.c b/drivers/net/ethernet/mellanox/mlx4/alloc.c
index 116cae334dad..8be20e7ea3d1 100644
--- a/drivers/net/mlx4/alloc.c
+++ b/drivers/net/ethernet/mellanox/mlx4/alloc.c
@@ -34,6 +34,7 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/mm.h>
+#include <linux/export.h>
#include <linux/bitmap.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
diff --git a/drivers/net/mlx4/catas.c b/drivers/net/ethernet/mellanox/mlx4/catas.c
index 32f947154c33..45aea9c3ae2c 100644
--- a/drivers/net/mlx4/catas.c
+++ b/drivers/net/ethernet/mellanox/mlx4/catas.c
@@ -32,6 +32,7 @@
*/
#include <linux/workqueue.h>
+#include <linux/module.h>
#include "mlx4.h"
diff --git a/drivers/net/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c
index 23cee7b6af91..78f5a1a0b8c8 100644
--- a/drivers/net/mlx4/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c
@@ -34,6 +34,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <linux/pci.h>
#include <linux/errno.h>
diff --git a/drivers/net/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c
index bd8ef9f2fa71..499a5168892a 100644
--- a/drivers/net/mlx4/cq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/cq.c
@@ -35,6 +35,7 @@
*/
#include <linux/hardirq.h>
+#include <linux/export.h>
#include <linux/gfp.h>
#include <linux/mlx4/cmd.h>
diff --git a/drivers/net/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
index ec4b6d047fe0..227997d775e8 100644
--- a/drivers/net/mlx4/en_cq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
@@ -74,7 +74,8 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv,
return err;
}
-int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
+int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
+ int cq_idx)
{
struct mlx4_en_dev *mdev = priv->mdev;
int err = 0;
@@ -90,13 +91,15 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
if (cq->is_tx == RX) {
if (mdev->dev->caps.comp_pool) {
if (!cq->vector) {
- sprintf(name , "%s-rx-%d", priv->dev->name, cq->ring);
+ sprintf(name, "%s-%d", priv->dev->name,
+ cq->ring);
+ /* Set IRQ for specific name (per ring) */
if (mlx4_assign_eq(mdev->dev, name, &cq->vector)) {
- cq->vector = (cq->ring + 1 + priv->port) %
- mdev->dev->caps.num_comp_vectors;
+ cq->vector = (cq->ring + 1 + priv->port)
+ % mdev->dev->caps.num_comp_vectors;
mlx4_warn(mdev, "Failed Assigning an EQ to "
- "%s_rx-%d ,Falling back to legacy EQ's\n",
- priv->dev->name, cq->ring);
+ "%s ,Falling back to legacy EQ's\n",
+ name);
}
}
} else {
@@ -104,10 +107,13 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
mdev->dev->caps.num_comp_vectors;
}
} else {
- if (!cq->vector || !mdev->dev->caps.comp_pool) {
- /*Fallback to legacy pool in case of error*/
- cq->vector = 0;
- }
+ /* For TX we use the same irq per
+ ring we assigned for the RX */
+ struct mlx4_en_cq *rx_cq;
+
+ cq_idx = cq_idx % priv->rx_ring_num;
+ rx_cq = &priv->rx_cq[cq_idx];
+ cq->vector = rx_cq->vector;
}
if (!cq->is_tx)
@@ -133,14 +139,13 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
return 0;
}
-void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
- bool reserve_vectors)
+void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
{
struct mlx4_en_dev *mdev = priv->mdev;
mlx4_en_unmap_buffer(&cq->wqres.buf);
mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size);
- if (priv->mdev->dev->caps.comp_pool && cq->vector && !reserve_vectors)
+ if (priv->mdev->dev->caps.comp_pool && cq->vector)
mlx4_release_eq(priv->mdev->dev, cq->vector);
cq->buf_size = 0;
cq->buf = NULL;
diff --git a/drivers/net/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index 2e858e4dcf4d..74e2a2a8a02b 100644
--- a/drivers/net/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -104,7 +104,7 @@ static void mlx4_en_get_wol(struct net_device *netdev,
int err = 0;
u64 config = 0;
- if (!priv->mdev->dev->caps.wol) {
+ if (!(priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_WOL)) {
wol->supported = 0;
wol->wolopts = 0;
return;
@@ -134,7 +134,7 @@ static int mlx4_en_set_wol(struct net_device *netdev,
u64 config = 0;
int err = 0;
- if (!priv->mdev->dev->caps.wol)
+ if (!(priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_WOL))
return -EOPNOTSUPP;
if (wol->supported & ~WAKE_MAGIC)
@@ -170,7 +170,8 @@ static int mlx4_en_get_sset_count(struct net_device *dev, int sset)
return NUM_ALL_STATS +
(priv->tx_ring_num + priv->rx_ring_num) * 2;
case ETH_SS_TEST:
- return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.loopback_support) * 2;
+ return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags
+ & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2;
default:
return -EOPNOTSUPP;
}
@@ -220,7 +221,7 @@ static void mlx4_en_get_strings(struct net_device *dev,
case ETH_SS_TEST:
for (i = 0; i < MLX4_EN_NUM_SELF_TEST - 2; i++)
strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]);
- if (priv->mdev->dev->caps.loopback_support)
+ if (priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UC_LOOPBACK)
for (; i < MLX4_EN_NUM_SELF_TEST; i++)
strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]);
break;
@@ -341,13 +342,13 @@ static int mlx4_en_set_coalesce(struct net_device *dev,
priv->rx_usecs_high = coal->rx_coalesce_usecs_high;
priv->sample_interval = coal->rate_sample_interval;
priv->adaptive_rx_coal = coal->use_adaptive_rx_coalesce;
- priv->last_moder_time = MLX4_EN_AUTO_CONF;
if (priv->adaptive_rx_coal)
return 0;
for (i = 0; i < priv->rx_ring_num; i++) {
priv->rx_cq[i].moder_cnt = priv->rx_frames;
priv->rx_cq[i].moder_time = priv->rx_usecs;
+ priv->last_moder_time[i] = MLX4_EN_AUTO_CONF;
err = mlx4_en_set_cq_moder(priv, &priv->rx_cq[i]);
if (err)
return err;
@@ -393,6 +394,7 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
u32 rx_size, tx_size;
int port_up = 0;
int err = 0;
+ int i;
if (param->rx_jumbo_pending || param->rx_mini_pending)
return -EINVAL;
@@ -415,7 +417,7 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
mlx4_en_stop_port(dev);
}
- mlx4_en_free_resources(priv, true);
+ mlx4_en_free_resources(priv);
priv->prof->tx_ring_size = tx_size;
priv->prof->rx_ring_size = rx_size;
@@ -431,6 +433,15 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
en_err(priv, "Failed starting port\n");
}
+ for (i = 0; i < priv->rx_ring_num; i++) {
+ priv->rx_cq[i].moder_cnt = priv->rx_frames;
+ priv->rx_cq[i].moder_time = priv->rx_usecs;
+ priv->last_moder_time[i] = MLX4_EN_AUTO_CONF;
+ err = mlx4_en_set_cq_moder(priv, &priv->rx_cq[i]);
+ if (err)
+ goto out;
+ }
+
out:
mutex_unlock(&mdev->state_lock);
return err;
diff --git a/drivers/net/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c
index 9276b1b25586..a06096fcc0b8 100644
--- a/drivers/net/mlx4/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c
@@ -106,7 +106,8 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
params->tcp_rss = tcp_rss;
params->udp_rss = udp_rss;
- if (params->udp_rss && !mdev->dev->caps.udp_rss) {
+ if (params->udp_rss && !(mdev->dev->caps.flags
+ & MLX4_DEV_CAP_FLAG_UDP_RSS)) {
mlx4_warn(mdev, "UDP RSS is not supported on this device.\n");
params->udp_rss = 0;
}
@@ -175,6 +176,7 @@ static void mlx4_en_remove(struct mlx4_dev *dev, void *endev_ptr)
flush_workqueue(mdev->workqueue);
destroy_workqueue(mdev->workqueue);
mlx4_mr_free(dev, &mdev->mr);
+ iounmap(mdev->uar_map);
mlx4_uar_free(dev, &mdev->priv_uar);
mlx4_pd_free(dev, mdev->priv_pdn);
kfree(mdev);
@@ -222,7 +224,7 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
MLX4_PERM_LOCAL_WRITE | MLX4_PERM_LOCAL_READ,
0, 0, &mdev->mr)) {
mlx4_err(mdev, "Failed allocating memory region\n");
- goto err_uar;
+ goto err_map;
}
if (mlx4_mr_enable(mdev->dev, &mdev->mr)) {
mlx4_err(mdev, "Failed enabling memory region\n");
@@ -281,6 +283,9 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
err_mr:
mlx4_mr_free(dev, &mdev->mr);
+err_map:
+ if (!mdev->uar_map)
+ iounmap(mdev->uar_map);
err_uar:
mlx4_uar_free(dev, &mdev->priv_uar);
err_pd:
diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 61850adae6f7..78d776bc355c 100644
--- a/drivers/net/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -45,25 +45,6 @@
#include "mlx4_en.h"
#include "en_port.h"
-
-static void mlx4_en_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
-{
- struct mlx4_en_priv *priv = netdev_priv(dev);
- struct mlx4_en_dev *mdev = priv->mdev;
- int err;
-
- en_dbg(HW, priv, "Registering VLAN group:%p\n", grp);
- priv->vlgrp = grp;
-
- mutex_lock(&mdev->state_lock);
- if (mdev->device_up && priv->port_up) {
- err = mlx4_SET_VLAN_FLTR(mdev->dev, priv->port, grp);
- if (err)
- en_err(priv, "Failed configuring VLAN filter\n");
- }
- mutex_unlock(&mdev->state_lock);
-}
-
static void mlx4_en_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
@@ -71,16 +52,14 @@ static void mlx4_en_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
int err;
int idx;
- if (!priv->vlgrp)
- return;
+ en_dbg(HW, priv, "adding VLAN:%d\n", vid);
- en_dbg(HW, priv, "adding VLAN:%d (vlgrp entry:%p)\n",
- vid, vlan_group_get_device(priv->vlgrp, vid));
+ set_bit(vid, priv->active_vlans);
/* Add VID to port VLAN filter */
mutex_lock(&mdev->state_lock);
if (mdev->device_up && priv->port_up) {
- err = mlx4_SET_VLAN_FLTR(mdev->dev, priv->port, priv->vlgrp);
+ err = mlx4_SET_VLAN_FLTR(mdev->dev, priv);
if (err)
en_err(priv, "Failed configuring VLAN filter\n");
}
@@ -97,12 +76,9 @@ static void mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
int err;
int idx;
- if (!priv->vlgrp)
- return;
+ en_dbg(HW, priv, "Killing VID:%d\n", vid);
- en_dbg(HW, priv, "Killing VID:%d (vlgrp:%p vlgrp entry:%p)\n",
- vid, priv->vlgrp, vlan_group_get_device(priv->vlgrp, vid));
- vlan_group_set_device(priv->vlgrp, vid, NULL);
+ clear_bit(vid, priv->active_vlans);
/* Remove VID from port VLAN filter */
mutex_lock(&mdev->state_lock);
@@ -112,7 +88,7 @@ static void mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
en_err(priv, "could not find vid %d in cache\n", vid);
if (mdev->device_up && priv->port_up) {
- err = mlx4_SET_VLAN_FLTR(mdev->dev, priv->port, priv->vlgrp);
+ err = mlx4_SET_VLAN_FLTR(mdev->dev, priv);
if (err)
en_err(priv, "Failed configuring VLAN filter\n");
}
@@ -239,7 +215,8 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
priv->flags |= MLX4_EN_FLAG_PROMISC;
/* Enable promiscouos mode */
- if (!mdev->dev->caps.vep_uc_steering)
+ if (!(mdev->dev->caps.flags &
+ MLX4_DEV_CAP_FLAG_VEP_UC_STEER))
err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port,
priv->base_qpn, 1);
else
@@ -265,12 +242,10 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
priv->flags |= MLX4_EN_FLAG_MC_PROMISC;
}
- if (priv->vlgrp) {
- /* Disable port VLAN filter */
- err = mlx4_SET_VLAN_FLTR(mdev->dev, priv->port, NULL);
- if (err)
- en_err(priv, "Failed disabling VLAN filter\n");
- }
+ /* Disable port VLAN filter */
+ err = mlx4_SET_VLAN_FLTR(mdev->dev, priv);
+ if (err)
+ en_err(priv, "Failed disabling VLAN filter\n");
}
goto out;
}
@@ -285,7 +260,7 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
priv->flags &= ~MLX4_EN_FLAG_PROMISC;
/* Disable promiscouos mode */
- if (!mdev->dev->caps.vep_uc_steering)
+ if (!(mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER))
err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port,
priv->base_qpn, 0);
else
@@ -304,7 +279,7 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
}
/* Enable port VLAN filter */
- err = mlx4_SET_VLAN_FLTR(mdev->dev, priv->port, priv->vlgrp);
+ err = mlx4_SET_VLAN_FLTR(mdev->dev, priv);
if (err)
en_err(priv, "Failed enabling VLAN filter\n");
}
@@ -440,6 +415,9 @@ static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv)
cq = &priv->rx_cq[i];
cq->moder_cnt = priv->rx_frames;
cq->moder_time = priv->rx_usecs;
+ priv->last_moder_time[i] = MLX4_EN_AUTO_CONF;
+ priv->last_moder_packets[i] = 0;
+ priv->last_moder_bytes[i] = 0;
}
for (i = 0; i < priv->tx_ring_num; i++) {
@@ -455,11 +433,8 @@ static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv)
priv->rx_usecs_high = MLX4_EN_RX_COAL_TIME_HIGH;
priv->sample_interval = MLX4_EN_SAMPLE_INTERVAL;
priv->adaptive_rx_coal = 1;
- priv->last_moder_time = MLX4_EN_AUTO_CONF;
priv->last_moder_jiffies = 0;
- priv->last_moder_packets = 0;
priv->last_moder_tx_packets = 0;
- priv->last_moder_bytes = 0;
}
static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv)
@@ -471,43 +446,30 @@ static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv)
unsigned long avg_pkt_size;
unsigned long rx_packets;
unsigned long rx_bytes;
- unsigned long tx_packets;
- unsigned long tx_pkt_diff;
unsigned long rx_pkt_diff;
int moder_time;
- int i, err;
+ int ring, err;
if (!priv->adaptive_rx_coal || period < priv->sample_interval * HZ)
return;
- spin_lock_bh(&priv->stats_lock);
- rx_packets = priv->stats.rx_packets;
- rx_bytes = priv->stats.rx_bytes;
- tx_packets = priv->stats.tx_packets;
- spin_unlock_bh(&priv->stats_lock);
-
- if (!priv->last_moder_jiffies || !period)
- goto out;
-
- tx_pkt_diff = ((unsigned long) (tx_packets -
- priv->last_moder_tx_packets));
- rx_pkt_diff = ((unsigned long) (rx_packets -
- priv->last_moder_packets));
- packets = max(tx_pkt_diff, rx_pkt_diff);
- rate = packets * HZ / period;
- avg_pkt_size = packets ? ((unsigned long) (rx_bytes -
- priv->last_moder_bytes)) / packets : 0;
-
- /* Apply auto-moderation only when packet rate exceeds a rate that
- * it matters */
- if (rate > MLX4_EN_RX_RATE_THRESH && avg_pkt_size > MLX4_EN_AVG_PKT_SMALL) {
- /* If tx and rx packet rates are not balanced, assume that
- * traffic is mainly BW bound and apply maximum moderation.
- * Otherwise, moderate according to packet rate */
- if (2 * tx_pkt_diff > 3 * rx_pkt_diff ||
- 2 * rx_pkt_diff > 3 * tx_pkt_diff) {
- moder_time = priv->rx_usecs_high;
- } else {
+ for (ring = 0; ring < priv->rx_ring_num; ring++) {
+ spin_lock_bh(&priv->stats_lock);
+ rx_packets = priv->rx_ring[ring].packets;
+ rx_bytes = priv->rx_ring[ring].bytes;
+ spin_unlock_bh(&priv->stats_lock);
+
+ rx_pkt_diff = ((unsigned long) (rx_packets -
+ priv->last_moder_packets[ring]));
+ packets = rx_pkt_diff;
+ rate = packets * HZ / period;
+ avg_pkt_size = packets ? ((unsigned long) (rx_bytes -
+ priv->last_moder_bytes[ring])) / packets : 0;
+
+ /* Apply auto-moderation only when packet rate
+ * exceeds a rate that it matters */
+ if (rate > (MLX4_EN_RX_RATE_THRESH / priv->rx_ring_num) &&
+ avg_pkt_size > MLX4_EN_AVG_PKT_SMALL) {
if (rate < priv->pkt_rate_low)
moder_time = priv->rx_usecs_low;
else if (rate > priv->pkt_rate_high)
@@ -517,36 +479,23 @@ static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv)
(priv->rx_usecs_high - priv->rx_usecs_low) /
(priv->pkt_rate_high - priv->pkt_rate_low) +
priv->rx_usecs_low;
+ } else {
+ moder_time = priv->rx_usecs_low;
}
- } else {
- moder_time = priv->rx_usecs_low;
- }
-
- en_dbg(INTR, priv, "tx rate:%lu rx_rate:%lu\n",
- tx_pkt_diff * HZ / period, rx_pkt_diff * HZ / period);
- en_dbg(INTR, priv, "Rx moder_time changed from:%d to %d period:%lu "
- "[jiff] packets:%lu avg_pkt_size:%lu rate:%lu [p/s])\n",
- priv->last_moder_time, moder_time, period, packets,
- avg_pkt_size, rate);
-
- if (moder_time != priv->last_moder_time) {
- priv->last_moder_time = moder_time;
- for (i = 0; i < priv->rx_ring_num; i++) {
- cq = &priv->rx_cq[i];
+ if (moder_time != priv->last_moder_time[ring]) {
+ priv->last_moder_time[ring] = moder_time;
+ cq = &priv->rx_cq[ring];
cq->moder_time = moder_time;
err = mlx4_en_set_cq_moder(priv, cq);
- if (err) {
- en_err(priv, "Failed modifying moderation for cq:%d\n", i);
- break;
- }
+ if (err)
+ en_err(priv, "Failed modifying moderation "
+ "for cq:%d\n", ring);
}
+ priv->last_moder_packets[ring] = rx_packets;
+ priv->last_moder_bytes[ring] = rx_bytes;
}
-out:
- priv->last_moder_packets = rx_packets;
- priv->last_moder_tx_packets = tx_packets;
- priv->last_moder_bytes = rx_bytes;
priv->last_moder_jiffies = jiffies;
}
@@ -612,7 +561,6 @@ int mlx4_en_start_port(struct net_device *dev)
int i;
int j;
u8 mc_list[16] = {0};
- char name[32];
if (priv->port_up) {
en_dbg(DRV, priv, "start port called while port already up\n");
@@ -633,7 +581,7 @@ int mlx4_en_start_port(struct net_device *dev)
for (i = 0; i < priv->rx_ring_num; i++) {
cq = &priv->rx_cq[i];
- err = mlx4_en_activate_cq(priv, cq);
+ err = mlx4_en_activate_cq(priv, cq, i);
if (err) {
en_err(priv, "Failed activating Rx CQ\n");
goto cq_err;
@@ -667,20 +615,11 @@ int mlx4_en_start_port(struct net_device *dev)
goto mac_err;
}
- if (mdev->dev->caps.comp_pool && !priv->tx_vector) {
- sprintf(name , "%s-tx", priv->dev->name);
- if (mlx4_assign_eq(mdev->dev , name, &priv->tx_vector)) {
- mlx4_warn(mdev, "Failed Assigning an EQ to "
- "%s_tx ,Falling back to legacy "
- "EQ's\n", priv->dev->name);
- }
- }
/* Configure tx cq's and rings */
for (i = 0; i < priv->tx_ring_num; i++) {
/* Configure cq */
cq = &priv->tx_cq[i];
- cq->vector = priv->tx_vector;
- err = mlx4_en_activate_cq(priv, cq);
+ err = mlx4_en_activate_cq(priv, cq, i);
if (err) {
en_err(priv, "Failed allocating Tx CQ\n");
goto tx_err;
@@ -911,7 +850,7 @@ static int mlx4_en_close(struct net_device *dev)
return 0;
}
-void mlx4_en_free_resources(struct mlx4_en_priv *priv, bool reserve_vectors)
+void mlx4_en_free_resources(struct mlx4_en_priv *priv)
{
int i;
@@ -919,14 +858,14 @@ void mlx4_en_free_resources(struct mlx4_en_priv *priv, bool reserve_vectors)
if (priv->tx_ring[i].tx_info)
mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]);
if (priv->tx_cq[i].buf)
- mlx4_en_destroy_cq(priv, &priv->tx_cq[i], reserve_vectors);
+ mlx4_en_destroy_cq(priv, &priv->tx_cq[i]);
}
for (i = 0; i < priv->rx_ring_num; i++) {
if (priv->rx_ring[i].rx_info)
mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i]);
if (priv->rx_cq[i].buf)
- mlx4_en_destroy_cq(priv, &priv->rx_cq[i], reserve_vectors);
+ mlx4_en_destroy_cq(priv, &priv->rx_cq[i]);
}
}
@@ -996,7 +935,7 @@ void mlx4_en_destroy_netdev(struct net_device *dev)
mdev->pndev[priv->port] = NULL;
mutex_unlock(&mdev->state_lock);
- mlx4_en_free_resources(priv, false);
+ mlx4_en_free_resources(priv);
free_netdev(dev);
}
@@ -1041,12 +980,11 @@ static const struct net_device_ops mlx4_netdev_ops = {
.ndo_start_xmit = mlx4_en_xmit,
.ndo_select_queue = mlx4_en_select_queue,
.ndo_get_stats = mlx4_en_get_stats,
- .ndo_set_multicast_list = mlx4_en_set_multicast,
+ .ndo_set_rx_mode = mlx4_en_set_multicast,
.ndo_set_mac_address = mlx4_en_set_mac,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = mlx4_en_change_mtu,
.ndo_tx_timeout = mlx4_en_tx_timeout,
- .ndo_vlan_rx_register = mlx4_en_vlan_rx_register,
.ndo_vlan_rx_add_vid = mlx4_en_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = mlx4_en_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -1146,7 +1084,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
dev->vlan_features = dev->hw_features;
- dev->hw_features |= NETIF_F_RXCSUM;
+ dev->hw_features |= NETIF_F_RXCSUM | NETIF_F_RXHASH;
dev->features = dev->hw_features | NETIF_F_HIGHDMA |
NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
NETIF_F_HW_VLAN_FILTER;
@@ -1159,6 +1097,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
en_err(priv, "Netdev registration failed for port %d\n", port);
goto out;
}
+ priv->registered = 1;
en_warn(priv, "Using %d TX rings\n", prof->tx_ring_num);
en_warn(priv, "Using %d RX rings\n", prof->rx_ring_num);
@@ -1180,7 +1119,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
en_err(priv, "Failed Initializing port\n");
goto out;
}
- priv->registered = 1;
mlx4_en_set_default_moderation(priv);
queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY);
return 0;
diff --git a/drivers/net/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c
index f2a4f5dd313d..03c84cd78cde 100644
--- a/drivers/net/mlx4/en_port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c
@@ -48,7 +48,7 @@ int mlx4_SET_MCAST_FLTR(struct mlx4_dev *dev, u8 port,
MLX4_CMD_SET_MCAST_FLTR, MLX4_CMD_TIME_CLASS_B);
}
-int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, u8 port, struct vlan_group *grp)
+int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, struct mlx4_en_priv *priv)
{
struct mlx4_cmd_mailbox *mailbox;
struct mlx4_set_vlan_fltr_mbox *filter;
@@ -63,20 +63,15 @@ int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, u8 port, struct vlan_group *grp)
return PTR_ERR(mailbox);
filter = mailbox->buf;
- if (grp) {
- memset(filter, 0, sizeof *filter);
- for (i = VLAN_FLTR_SIZE - 1; i >= 0; i--) {
- entry = 0;
- for (j = 0; j < 32; j++)
- if (vlan_group_get_device(grp, index++))
- entry |= 1 << j;
- filter->entry[i] = cpu_to_be32(entry);
- }
- } else {
- /* When no vlans are configured we block all vlans */
- memset(filter, 0, sizeof(*filter));
+ memset(filter, 0, sizeof(*filter));
+ for (i = VLAN_FLTR_SIZE - 1; i >= 0; i--) {
+ entry = 0;
+ for (j = 0; j < 32; j++)
+ if (test_bit(index++, priv->active_vlans))
+ entry |= 1 << j;
+ filter->entry[i] = cpu_to_be32(entry);
}
- err = mlx4_cmd(dev, mailbox->dma, port, 0, MLX4_CMD_SET_VLAN_FLTR,
+ err = mlx4_cmd(dev, mailbox->dma, priv->port, 0, MLX4_CMD_SET_VLAN_FLTR,
MLX4_CMD_TIME_CLASS_B);
mlx4_free_cmd_mailbox(dev, mailbox);
return err;
@@ -119,9 +114,11 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn,
struct mlx4_set_port_rqp_calc_context *context;
int err;
u32 in_mod;
- u32 m_promisc = (dev->caps.vep_mc_steering) ? MCAST_DIRECT : MCAST_DEFAULT;
+ u32 m_promisc = (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) ?
+ MCAST_DIRECT : MCAST_DEFAULT;
- if (dev->caps.vep_mc_steering && dev->caps.vep_uc_steering)
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER &&
+ dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)
return 0;
mailbox = mlx4_alloc_cmd_mailbox(dev);
@@ -131,7 +128,7 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn,
memset(context, 0, sizeof *context);
context->base_qpn = cpu_to_be32(base_qpn);
- context->n_mac = 0x7;
+ context->n_mac = dev->caps.log_num_macs;
context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_SHIFT |
base_qpn);
context->mcast = cpu_to_be32(m_promisc << SET_PORT_MC_PROMISC_SHIFT |
@@ -170,11 +167,21 @@ int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port)
/* This command is always accessed from Ethtool context
* already synchronized, no need in locking */
state->link_state = !!(qport_context->link_up & MLX4_EN_LINK_UP_MASK);
- if ((qport_context->link_speed & MLX4_EN_SPEED_MASK) ==
- MLX4_EN_1G_SPEED)
+ switch (qport_context->link_speed & MLX4_EN_SPEED_MASK) {
+ case MLX4_EN_1G_SPEED:
state->link_speed = 1000;
- else
+ break;
+ case MLX4_EN_10G_SPEED_XAUI:
+ case MLX4_EN_10G_SPEED_XFI:
state->link_speed = 10000;
+ break;
+ case MLX4_EN_40G_SPEED:
+ state->link_speed = 40000;
+ break;
+ default:
+ state->link_speed = -1;
+ break;
+ }
state->transciver = qport_context->transceiver;
out:
@@ -207,15 +214,21 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset)
stats->rx_packets = 0;
stats->rx_bytes = 0;
+ priv->port_stats.rx_chksum_good = 0;
+ priv->port_stats.rx_chksum_none = 0;
for (i = 0; i < priv->rx_ring_num; i++) {
stats->rx_packets += priv->rx_ring[i].packets;
stats->rx_bytes += priv->rx_ring[i].bytes;
+ priv->port_stats.rx_chksum_good += priv->rx_ring[i].csum_ok;
+ priv->port_stats.rx_chksum_none += priv->rx_ring[i].csum_none;
}
stats->tx_packets = 0;
stats->tx_bytes = 0;
+ priv->port_stats.tx_chksum_offload = 0;
for (i = 0; i < priv->tx_ring_num; i++) {
stats->tx_packets += priv->tx_ring[i].packets;
stats->tx_bytes += priv->tx_ring[i].bytes;
+ priv->port_stats.tx_chksum_offload += priv->tx_ring[i].tx_csum;
}
stats->rx_errors = be64_to_cpu(mlx4_en_stats->PCS) +
diff --git a/drivers/net/mlx4/en_port.h b/drivers/net/ethernet/mellanox/mlx4/en_port.h
index e3d73e41c567..19eb244f5165 100644
--- a/drivers/net/mlx4/en_port.h
+++ b/drivers/net/ethernet/mellanox/mlx4/en_port.h
@@ -94,6 +94,14 @@ enum {
MLX4_MCAST_ENABLE = 2,
};
+enum {
+ MLX4_EN_1G_SPEED = 0x02,
+ MLX4_EN_10G_SPEED_XFI = 0x01,
+ MLX4_EN_10G_SPEED_XAUI = 0x00,
+ MLX4_EN_40G_SPEED = 0x40,
+ MLX4_EN_OTHER_SPEED = 0x0f,
+};
+
struct mlx4_en_query_port_context {
u8 link_up;
#define MLX4_EN_LINK_UP_MASK 0x80
@@ -101,8 +109,7 @@ struct mlx4_en_query_port_context {
__be16 mtu;
u8 reserved2;
u8 link_speed;
-#define MLX4_EN_SPEED_MASK 0x3
-#define MLX4_EN_1G_SPEED 0x2
+#define MLX4_EN_SPEED_MASK 0x43
u16 reserved3[5];
__be64 mac;
u8 transceiver;
diff --git a/drivers/net/mlx4/en_resources.c b/drivers/net/ethernet/mellanox/mlx4/en_resources.c
index 0dfb4ec8a9dd..0dfb4ec8a9dd 100644
--- a/drivers/net/mlx4/en_resources.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_resources.c
diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index 277215fb9d72..c2df6c358603 100644
--- a/drivers/net/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -44,7 +44,7 @@
static int mlx4_en_alloc_frag(struct mlx4_en_priv *priv,
struct mlx4_en_rx_desc *rx_desc,
- struct skb_frag_struct *skb_frags,
+ struct page_frag *skb_frags,
struct mlx4_en_rx_alloc *ring_alloc,
int i)
{
@@ -61,7 +61,7 @@ static int mlx4_en_alloc_frag(struct mlx4_en_priv *priv,
return -ENOMEM;
skb_frags[i].page = page_alloc->page;
- skb_frags[i].page_offset = page_alloc->offset;
+ skb_frags[i].offset = page_alloc->offset;
page_alloc->page = page;
page_alloc->offset = frag_info->frag_align;
} else {
@@ -69,11 +69,11 @@ static int mlx4_en_alloc_frag(struct mlx4_en_priv *priv,
get_page(page);
skb_frags[i].page = page;
- skb_frags[i].page_offset = page_alloc->offset;
+ skb_frags[i].offset = page_alloc->offset;
page_alloc->offset += frag_info->frag_stride;
}
dma = pci_map_single(mdev->pdev, page_address(skb_frags[i].page) +
- skb_frags[i].page_offset, frag_info->frag_size,
+ skb_frags[i].offset, frag_info->frag_size,
PCI_DMA_FROMDEVICE);
rx_desc->data[i].addr = cpu_to_be64(dma);
return 0;
@@ -135,7 +135,7 @@ static void mlx4_en_init_rx_desc(struct mlx4_en_priv *priv,
/* Set size and memtype fields */
for (i = 0; i < priv->num_frags; i++) {
- skb_frags[i].size = priv->frag_info[i].frag_size;
+ skb_frag_size_set(&skb_frags[i], priv->frag_info[i].frag_size);
rx_desc->data[i].byte_count =
cpu_to_be32(priv->frag_info[i].frag_size);
rx_desc->data[i].lkey = cpu_to_be32(priv->mdev->mr.key);
@@ -157,8 +157,8 @@ static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv,
struct mlx4_en_rx_ring *ring, int index)
{
struct mlx4_en_rx_desc *rx_desc = ring->buf + (index * ring->stride);
- struct skb_frag_struct *skb_frags = ring->rx_info +
- (index << priv->log_rx_info);
+ struct page_frag *skb_frags = ring->rx_info +
+ (index << priv->log_rx_info);
int i;
for (i = 0; i < priv->num_frags; i++)
@@ -183,7 +183,7 @@ static void mlx4_en_free_rx_desc(struct mlx4_en_priv *priv,
int index)
{
struct mlx4_en_dev *mdev = priv->mdev;
- struct skb_frag_struct *skb_frags;
+ struct page_frag *skb_frags;
struct mlx4_en_rx_desc *rx_desc = ring->buf + (index << ring->log_stride);
dma_addr_t dma;
int nr;
@@ -403,11 +403,12 @@ void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv,
/* Unmap a completed descriptor and free unused pages */
static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv,
struct mlx4_en_rx_desc *rx_desc,
- struct skb_frag_struct *skb_frags,
- struct skb_frag_struct *skb_frags_rx,
+ struct page_frag *skb_frags,
+ struct sk_buff *skb,
struct mlx4_en_rx_alloc *page_alloc,
int length)
{
+ struct skb_frag_struct *skb_frags_rx = skb_shinfo(skb)->frags;
struct mlx4_en_dev *mdev = priv->mdev;
struct mlx4_en_frag_info *frag_info;
int nr;
@@ -420,9 +421,10 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv,
break;
/* Save page reference in skb */
- skb_frags_rx[nr].page = skb_frags[nr].page;
- skb_frags_rx[nr].size = skb_frags[nr].size;
- skb_frags_rx[nr].page_offset = skb_frags[nr].page_offset;
+ __skb_frag_set_page(&skb_frags_rx[nr], skb_frags[nr].page);
+ skb_frag_size_set(&skb_frags_rx[nr], skb_frags[nr].size);
+ skb_frags_rx[nr].page_offset = skb_frags[nr].offset;
+ skb->truesize += frag_info->frag_stride;
dma = be64_to_cpu(rx_desc->data[nr].addr);
/* Allocate a replacement page */
@@ -430,13 +432,13 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv,
goto fail;
/* Unmap buffer */
- pci_unmap_single(mdev->pdev, dma, skb_frags_rx[nr].size,
+ pci_unmap_single(mdev->pdev, dma, skb_frag_size(&skb_frags_rx[nr]),
PCI_DMA_FROMDEVICE);
}
/* Adjust size of last fragment to match actual length */
if (nr > 0)
- skb_frags_rx[nr - 1].size = length -
- priv->frag_info[nr - 1].frag_prefix_size;
+ skb_frag_size_set(&skb_frags_rx[nr - 1],
+ length - priv->frag_info[nr - 1].frag_prefix_size);
return nr;
fail:
@@ -444,7 +446,7 @@ fail:
* the descriptor) of this packet; remaining fragments are reused... */
while (nr > 0) {
nr--;
- put_page(skb_frags_rx[nr].page);
+ __skb_frag_unref(&skb_frags_rx[nr]);
}
return 0;
}
@@ -452,7 +454,7 @@ fail:
static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv,
struct mlx4_en_rx_desc *rx_desc,
- struct skb_frag_struct *skb_frags,
+ struct page_frag *skb_frags,
struct mlx4_en_rx_alloc *page_alloc,
unsigned int length)
{
@@ -470,11 +472,10 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv,
skb->dev = priv->dev;
skb_reserve(skb, NET_IP_ALIGN);
skb->len = length;
- skb->truesize = length + sizeof(struct sk_buff);
/* Get pointer to first fragment so we could copy the headers into the
* (linear part of the) skb */
- va = page_address(skb_frags[0].page) + skb_frags[0].page_offset;
+ va = page_address(skb_frags[0].page) + skb_frags[0].offset;
if (length <= SMALL_PACKET_SIZE) {
/* We are copying all relevant data to the skb - temporarily
@@ -490,8 +491,7 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv,
/* Move relevant fragments to skb */
used_frags = mlx4_en_complete_rx_desc(priv, rx_desc, skb_frags,
- skb_shinfo(skb)->frags,
- page_alloc, length);
+ skb, page_alloc, length);
if (unlikely(!used_frags)) {
kfree_skb(skb);
return NULL;
@@ -506,7 +506,7 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv,
skb_shinfo(skb)->frags[0].page_offset += HEADER_COPY_SIZE;
/* Adjust size of first fragment */
- skb_shinfo(skb)->frags[0].size -= HEADER_COPY_SIZE;
+ skb_frag_size_sub(&skb_shinfo(skb)->frags[0], HEADER_COPY_SIZE);
skb->data_len = length - HEADER_COPY_SIZE;
}
return skb;
@@ -533,7 +533,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_cqe *cqe;
struct mlx4_en_rx_ring *ring = &priv->rx_ring[cq->ring];
- struct skb_frag_struct *skb_frags;
+ struct page_frag *skb_frags;
struct mlx4_en_rx_desc *rx_desc;
struct sk_buff *skb;
int index;
@@ -581,13 +581,14 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
* Packet is OK - process it.
*/
length = be32_to_cpu(cqe->byte_cnt);
+ length -= ring->fcs_del;
ring->bytes += length;
ring->packets++;
if (likely(dev->features & NETIF_F_RXCSUM)) {
if ((cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPOK)) &&
(cqe->checksum == cpu_to_be16(0xffff))) {
- priv->port_stats.rx_chksum_good++;
+ ring->csum_ok++;
/* This packet is eligible for LRO if it is:
* - DIX Ethernet (type interpretation)
* - TCP/IP (v4)
@@ -600,7 +601,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
nr = mlx4_en_complete_rx_desc(
priv, rx_desc,
- skb_frags, skb_shinfo(gro_skb)->frags,
+ skb_frags, gro_skb,
ring->page_alloc, length);
if (!nr)
goto next;
@@ -608,14 +609,20 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
skb_shinfo(gro_skb)->nr_frags = nr;
gro_skb->len = length;
gro_skb->data_len = length;
- gro_skb->truesize += length;
gro_skb->ip_summed = CHECKSUM_UNNECESSARY;
- if (priv->vlgrp && (cqe->vlan_my_qpn &
- cpu_to_be32(MLX4_CQE_VLAN_PRESENT_MASK)))
- vlan_gro_frags(&cq->napi, priv->vlgrp, be16_to_cpu(cqe->sl_vid));
- else
- napi_gro_frags(&cq->napi);
+ if (cqe->vlan_my_qpn &
+ cpu_to_be32(MLX4_CQE_VLAN_PRESENT_MASK)) {
+ u16 vid = be16_to_cpu(cqe->sl_vid);
+
+ __vlan_hwaccel_put_tag(gro_skb, vid);
+ }
+
+ if (dev->features & NETIF_F_RXHASH)
+ gro_skb->rxhash = be32_to_cpu(cqe->immed_rss_invalid);
+
+ skb_record_rx_queue(gro_skb, cq->ring);
+ napi_gro_frags(&cq->napi);
goto next;
}
@@ -624,11 +631,11 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
ip_summed = CHECKSUM_UNNECESSARY;
} else {
ip_summed = CHECKSUM_NONE;
- priv->port_stats.rx_chksum_none++;
+ ring->csum_none++;
}
} else {
ip_summed = CHECKSUM_NONE;
- priv->port_stats.rx_chksum_none++;
+ ring->csum_none++;
}
skb = mlx4_en_rx_skb(priv, rx_desc, skb_frags,
@@ -647,13 +654,15 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
skb->protocol = eth_type_trans(skb, dev);
skb_record_rx_queue(skb, cq->ring);
+ if (dev->features & NETIF_F_RXHASH)
+ skb->rxhash = be32_to_cpu(cqe->immed_rss_invalid);
+
+ if (be32_to_cpu(cqe->vlan_my_qpn) &
+ MLX4_CQE_VLAN_PRESENT_MASK)
+ __vlan_hwaccel_put_tag(skb, be16_to_cpu(cqe->sl_vid));
+
/* Push it up the stack */
- if (priv->vlgrp && (be32_to_cpu(cqe->vlan_my_qpn) &
- MLX4_CQE_VLAN_PRESENT_MASK)) {
- vlan_hwaccel_receive_skb(skb, priv->vlgrp,
- be16_to_cpu(cqe->sl_vid));
- } else
- netif_receive_skb(skb);
+ netif_receive_skb(skb);
next:
++cq->mcq.cons_index;
@@ -804,6 +813,13 @@ static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv, int qpn,
qpn, ring->cqn, context);
context->db_rec_addr = cpu_to_be64(ring->wqres.db.dma);
+ /* Cancel FCS removal if FW allows */
+ if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP) {
+ context->param3 |= cpu_to_be32(1 << 29);
+ ring->fcs_del = ETH_FCS_LEN;
+ } else
+ ring->fcs_del = 0;
+
err = mlx4_qp_to_ready(mdev->dev, &ring->wqres.mtt, context, qp, state);
if (err) {
mlx4_qp_remove(mdev->dev, qp);
@@ -827,6 +843,9 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv)
int i, qpn;
int err = 0;
int good_qps = 0;
+ static const u32 rsskey[10] = { 0xD181C62C, 0xF7F4DB5B, 0x1983A2FC,
+ 0x943E1ADB, 0xD9389E6B, 0xD1039C2C, 0xA74499AD,
+ 0x593D56D9, 0xF3253C06, 0x2ADC1FFC};
en_dbg(DRV, priv, "Configuring rss steering\n");
err = mlx4_qp_reserve_range(mdev->dev, priv->rx_ring_num,
@@ -859,11 +878,14 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv)
priv->rx_ring[0].cqn, &context);
ptr = ((void *) &context) + 0x3c;
- rss_context = (struct mlx4_en_rss_context *) ptr;
+ rss_context = ptr;
rss_context->base_qpn = cpu_to_be32(ilog2(priv->rx_ring_num) << 24 |
(rss_map->base_qpn));
rss_context->default_qpn = cpu_to_be32(rss_map->base_qpn);
rss_context->flags = rss_mask;
+ rss_context->hash_fn = 1;
+ for (i = 0; i < 10; i++)
+ rss_context->rss_key[i] = rsskey[i];
if (priv->mdev->profile.udp_rss)
rss_context->base_qpn_udp = rss_context->default_qpn;
diff --git a/drivers/net/mlx4/en_selftest.c b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c
index 191a8dcd8a93..9fdbcecd499d 100644
--- a/drivers/net/mlx4/en_selftest.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c
@@ -159,7 +159,8 @@ retry_tx:
goto retry_tx;
}
- if (priv->mdev->dev->caps.loopback_support){
+ if (priv->mdev->dev->caps.flags &
+ MLX4_DEV_CAP_FLAG_UC_LOOPBACK) {
buf[3] = mlx4_en_test_registers(priv);
buf[4] = mlx4_en_test_loopback(priv);
}
diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index b229acf1855f..d901b4267537 100644
--- a/drivers/net/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -39,6 +39,7 @@
#include <linux/if_vlan.h>
#include <linux/vmalloc.h>
#include <linux/tcp.h>
+#include <linux/moduleparam.h>
#include "mlx4_en.h"
@@ -172,7 +173,7 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
memset(ring->buf, 0, ring->buf_size);
ring->qp_state = MLX4_QP_STATE_RST;
- ring->doorbell_qpn = swab32(ring->qp.qpn << 8);
+ ring->doorbell_qpn = ring->qp.qpn << 8;
mlx4_en_fill_qp_context(priv, ring->size, ring->stride, 1, 0, ring->qpn,
ring->cqn, &ring->context);
@@ -226,7 +227,7 @@ static u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv,
frag = &skb_shinfo(skb)->frags[i];
pci_unmap_page(mdev->pdev,
(dma_addr_t) be64_to_cpu(data[i].addr),
- frag->size, PCI_DMA_TODEVICE);
+ skb_frag_size(frag), PCI_DMA_TODEVICE);
}
}
/* Stamp the freed descriptor */
@@ -238,8 +239,7 @@ static u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv,
} else {
if (!tx_info->inl) {
if ((void *) data >= end) {
- data = (struct mlx4_wqe_data_seg *)
- (ring->buf + ((void *) data - end));
+ data = ring->buf + ((void *)data - end);
}
if (tx_info->linear) {
@@ -253,11 +253,11 @@ static u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv,
for (i = 0; i < frags; i++) {
/* Check for wraparound before unmapping */
if ((void *) data >= end)
- data = (struct mlx4_wqe_data_seg *) ring->buf;
+ data = ring->buf;
frag = &skb_shinfo(skb)->frags[i];
pci_unmap_page(mdev->pdev,
(dma_addr_t) be64_to_cpu(data->addr),
- frag->size, PCI_DMA_TODEVICE);
+ skb_frag_size(frag), PCI_DMA_TODEVICE);
++data;
}
}
@@ -461,26 +461,13 @@ static inline void mlx4_en_xmit_poll(struct mlx4_en_priv *priv, int tx_ind)
}
}
-static void *get_frag_ptr(struct sk_buff *skb)
-{
- struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[0];
- struct page *page = frag->page;
- void *ptr;
-
- ptr = page_address(page);
- if (unlikely(!ptr))
- return NULL;
-
- return ptr + frag->page_offset;
-}
-
static int is_inline(struct sk_buff *skb, void **pfrag)
{
void *ptr;
if (inline_thold && !skb_is_gso(skb) && skb->len <= inline_thold) {
if (skb_shinfo(skb)->nr_frags == 1) {
- ptr = get_frag_ptr(skb);
+ ptr = skb_frag_address_safe(&skb_shinfo(skb)->frags[0]);
if (unlikely(!ptr))
return 0;
@@ -551,7 +538,7 @@ static void build_inline_wqe(struct mlx4_en_tx_desc *tx_desc, struct sk_buff *sk
skb_copy_from_linear_data(skb, inl + 1, skb_headlen(skb));
if (skb_shinfo(skb)->nr_frags)
memcpy(((void *)(inl + 1)) + skb_headlen(skb), fragptr,
- skb_shinfo(skb)->frags[0].size);
+ skb_frag_size(&skb_shinfo(skb)->frags[0]));
} else {
inl->byte_count = cpu_to_be32(1 << 31 | spc);
@@ -571,7 +558,7 @@ static void build_inline_wqe(struct mlx4_en_tx_desc *tx_desc, struct sk_buff *sk
skb_headlen(skb) - spc);
if (skb_shinfo(skb)->nr_frags)
memcpy(((void *)(inl + 1)) + skb_headlen(skb) - spc,
- fragptr, skb_shinfo(skb)->frags[0].size);
+ fragptr, skb_frag_size(&skb_shinfo(skb)->frags[0]));
}
wmb();
@@ -696,7 +683,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
tx_desc->ctrl.srcrb_flags |= cpu_to_be32(MLX4_WQE_CTRL_IP_CSUM |
MLX4_WQE_CTRL_TCP_UDP_CSUM);
- priv->port_stats.tx_chksum_offload++;
+ ring->tx_csum++;
}
if (unlikely(priv->validate_loopback)) {
@@ -757,12 +744,13 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
/* Map fragments */
for (i = skb_shinfo(skb)->nr_frags - 1; i >= 0; i--) {
frag = &skb_shinfo(skb)->frags[i];
- dma = pci_map_page(mdev->dev->pdev, frag->page, frag->page_offset,
- frag->size, PCI_DMA_TODEVICE);
+ dma = skb_frag_dma_map(&mdev->dev->pdev->dev, frag,
+ 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
data->addr = cpu_to_be64(dma);
data->lkey = cpu_to_be32(mdev->mr.key);
wmb();
- data->byte_count = cpu_to_be32(frag->size);
+ data->byte_count = cpu_to_be32(skb_frag_size(frag));
--data;
}
@@ -792,7 +780,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
skb_orphan(skb);
if (ring->bf_enabled && desc_size <= MAX_BF && !bounce && !vlan_tag) {
- *(u32 *) (&tx_desc->ctrl.vlan_tag) |= ring->doorbell_qpn;
+ *(__be32 *) (&tx_desc->ctrl.vlan_tag) |= cpu_to_be32(ring->doorbell_qpn);
op_own |= htonl((bf_index & 0xffff) << 8);
/* Ensure new descirptor hits memory
* before setting ownership of this descriptor to HW */
@@ -813,7 +801,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
wmb();
tx_desc->ctrl.owner_opcode = op_own;
wmb();
- writel(ring->doorbell_qpn, ring->bf.uar->map + MLX4_SEND_DOORBELL);
+ iowrite32be(ring->doorbell_qpn, ring->bf.uar->map + MLX4_SEND_DOORBELL);
}
/* Poll CQ here */
diff --git a/drivers/net/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c
index 1ad1f6029af8..24ee96775996 100644
--- a/drivers/net/mlx4/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/eq.c
@@ -33,6 +33,7 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
@@ -484,7 +485,7 @@ static void mlx4_free_eq(struct mlx4_dev *dev,
mlx4_mtt_cleanup(dev, &eq->mtt);
for (i = 0; i < npages; ++i)
- pci_free_consistent(dev->pdev, PAGE_SIZE,
+ dma_free_coherent(&dev->pdev->dev, PAGE_SIZE,
eq->page_list[i].buf,
eq->page_list[i].map);
diff --git a/drivers/net/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c
index 67a209ba939d..435ca6e49734 100644
--- a/drivers/net/mlx4/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.c
@@ -33,6 +33,7 @@
*/
#include <linux/mlx4/cmd.h>
+#include <linux/module.h>
#include <linux/cache.h>
#include "fw.h"
@@ -75,7 +76,7 @@ MODULE_PARM_DESC(enable_qos, "Enable Quality of Service support in the HCA (defa
} \
} while (0)
-static void dump_dev_cap_flags(struct mlx4_dev *dev, u32 flags)
+static void dump_dev_cap_flags(struct mlx4_dev *dev, u64 flags)
{
static const char *fname[] = {
[ 0] = "RC transport",
@@ -99,13 +100,20 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u32 flags)
[21] = "UD multicast support",
[24] = "Demand paging support",
[25] = "Router support",
- [30] = "IBoE support"
+ [30] = "IBoE support",
+ [32] = "Unicast loopback support",
+ [34] = "FCS header control",
+ [38] = "Wake On LAN support",
+ [40] = "UDP RSS support",
+ [41] = "Unicast VEP steering support",
+ [42] = "Multicast VEP steering support",
+ [48] = "Counters support",
};
int i;
mlx4_dbg(dev, "DEV_CAP flags:\n");
for (i = 0; i < ARRAY_SIZE(fname); ++i)
- if (fname[i] && (flags & (1 << i)))
+ if (fname[i] && (flags & (1LL << i)))
mlx4_dbg(dev, " %s\n", fname[i]);
}
@@ -142,7 +150,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
struct mlx4_cmd_mailbox *mailbox;
u32 *outbox;
u8 field;
- u32 field32;
+ u32 field32, flags, ext_flags;
u16 size;
u16 stat_rate;
int err;
@@ -180,8 +188,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
#define QUERY_DEV_CAP_MAX_GID_OFFSET 0x3b
#define QUERY_DEV_CAP_RATE_SUPPORT_OFFSET 0x3c
#define QUERY_DEV_CAP_MAX_PKEY_OFFSET 0x3f
-#define QUERY_DEV_CAP_UDP_RSS_OFFSET 0x42
-#define QUERY_DEV_CAP_ETH_UC_LOOPBACK_OFFSET 0x43
+#define QUERY_DEV_CAP_EXT_FLAGS_OFFSET 0x40
#define QUERY_DEV_CAP_FLAGS_OFFSET 0x44
#define QUERY_DEV_CAP_RSVD_UAR_OFFSET 0x48
#define QUERY_DEV_CAP_UAR_SZ_OFFSET 0x49
@@ -199,6 +206,9 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
#define QUERY_DEV_CAP_MAX_MCG_OFFSET 0x63
#define QUERY_DEV_CAP_RSVD_PD_OFFSET 0x64
#define QUERY_DEV_CAP_MAX_PD_OFFSET 0x65
+#define QUERY_DEV_CAP_RSVD_XRC_OFFSET 0x66
+#define QUERY_DEV_CAP_MAX_XRC_OFFSET 0x67
+#define QUERY_DEV_CAP_MAX_COUNTERS_OFFSET 0x68
#define QUERY_DEV_CAP_RDMARC_ENTRY_SZ_OFFSET 0x80
#define QUERY_DEV_CAP_QPC_ENTRY_SZ_OFFSET 0x82
#define QUERY_DEV_CAP_AUX_ENTRY_SZ_OFFSET 0x84
@@ -272,14 +282,9 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
dev_cap->max_msg_sz = 1 << (field & 0x1f);
MLX4_GET(stat_rate, outbox, QUERY_DEV_CAP_RATE_SUPPORT_OFFSET);
dev_cap->stat_rate_support = stat_rate;
- MLX4_GET(field, outbox, QUERY_DEV_CAP_UDP_RSS_OFFSET);
- dev_cap->udp_rss = field & 0x1;
- dev_cap->vep_uc_steering = field & 0x2;
- dev_cap->vep_mc_steering = field & 0x4;
- MLX4_GET(field, outbox, QUERY_DEV_CAP_ETH_UC_LOOPBACK_OFFSET);
- dev_cap->loopback_support = field & 0x1;
- dev_cap->wol = field & 0x40;
- MLX4_GET(dev_cap->flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET);
+ MLX4_GET(ext_flags, outbox, QUERY_DEV_CAP_EXT_FLAGS_OFFSET);
+ MLX4_GET(flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET);
+ dev_cap->flags = flags | (u64)ext_flags << 32;
MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET);
dev_cap->reserved_uars = field >> 4;
MLX4_GET(field, outbox, QUERY_DEV_CAP_UAR_SZ_OFFSET);
@@ -317,6 +322,10 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
dev_cap->reserved_pds = field >> 4;
MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_PD_OFFSET);
dev_cap->max_pds = 1 << (field & 0x3f);
+ MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_XRC_OFFSET);
+ dev_cap->reserved_xrcds = field >> 4;
+ MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_PD_OFFSET);
+ dev_cap->max_xrcds = 1 << (field & 0x1f);
MLX4_GET(size, outbox, QUERY_DEV_CAP_RDMARC_ENTRY_SZ_OFFSET);
dev_cap->rdmarc_entry_sz = size;
@@ -356,6 +365,9 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
QUERY_DEV_CAP_RSVD_LKEY_OFFSET);
MLX4_GET(dev_cap->max_icm_sz, outbox,
QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET);
+ if (dev_cap->flags & MLX4_DEV_CAP_FLAG_COUNTERS)
+ MLX4_GET(dev_cap->max_counters, outbox,
+ QUERY_DEV_CAP_MAX_COUNTERS_OFFSET);
if (dev->flags & MLX4_FLAG_OLD_PORT_CMDS) {
for (i = 1; i <= dev_cap->num_ports; ++i) {
@@ -449,6 +461,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
mlx4_dbg(dev, "Max RQ desc size: %d, max RQ S/G: %d\n",
dev_cap->max_rq_desc_sz, dev_cap->max_rq_sg);
mlx4_dbg(dev, "Max GSO size: %d\n", dev_cap->max_gso_sz);
+ mlx4_dbg(dev, "Max counters: %d\n", dev_cap->max_counters);
dump_dev_cap_flags(dev, dev_cap->flags);
@@ -781,6 +794,10 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param)
if (enable_qos)
*(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 2);
+ /* enable counters */
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_COUNTERS)
+ *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 4);
+
/* QPC/EEC/CQC/EQC/RDMARC attributes */
MLX4_PUT(inbox, param->qpc_base, INIT_HCA_QPC_BASE_OFFSET);
@@ -801,7 +818,7 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param)
MLX4_PUT(inbox, param->mc_base, INIT_HCA_MC_BASE_OFFSET);
MLX4_PUT(inbox, param->log_mc_entry_sz, INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET);
MLX4_PUT(inbox, param->log_mc_hash_sz, INIT_HCA_LOG_MC_HASH_SZ_OFFSET);
- if (dev->caps.vep_mc_steering)
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)
MLX4_PUT(inbox, (u8) (1 << 3), INIT_HCA_UC_STEERING_OFFSET);
MLX4_PUT(inbox, param->log_mc_table_sz, INIT_HCA_LOG_MC_TABLE_SZ_OFFSET);
diff --git a/drivers/net/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h
index 88003ebc6185..bf5ec2286528 100644
--- a/drivers/net/mlx4/fw.h
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.h
@@ -78,12 +78,7 @@ struct mlx4_dev_cap {
u16 wavelength[MLX4_MAX_PORTS + 1];
u64 trans_code[MLX4_MAX_PORTS + 1];
u16 stat_rate_support;
- int udp_rss;
- int loopback_support;
- int vep_uc_steering;
- int vep_mc_steering;
- int wol;
- u32 flags;
+ u64 flags;
int reserved_uars;
int uar_size;
int min_page_sz;
@@ -98,6 +93,8 @@ struct mlx4_dev_cap {
int max_mcgs;
int reserved_pds;
int max_pds;
+ int reserved_xrcds;
+ int max_xrcds;
int qpc_entry_sz;
int rdmarc_entry_sz;
int altc_entry_sz;
@@ -116,6 +113,7 @@ struct mlx4_dev_cap {
u8 supported_port_types[MLX4_MAX_PORTS + 1];
u8 log_max_macs[MLX4_MAX_PORTS + 1];
u8 log_max_vlans[MLX4_MAX_PORTS + 1];
+ u32 max_counters;
};
struct mlx4_adapter {
diff --git a/drivers/net/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c
index 02393fdf44c1..02393fdf44c1 100644
--- a/drivers/net/mlx4/icm.c
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.c
diff --git a/drivers/net/mlx4/icm.h b/drivers/net/ethernet/mellanox/mlx4/icm.h
index b10c07a1dc1a..b10c07a1dc1a 100644
--- a/drivers/net/mlx4/icm.h
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.h
diff --git a/drivers/net/mlx4/intf.c b/drivers/net/ethernet/mellanox/mlx4/intf.c
index 73c94fcdfddf..ca6feb55bd94 100644
--- a/drivers/net/mlx4/intf.c
+++ b/drivers/net/ethernet/mellanox/mlx4/intf.c
@@ -32,6 +32,7 @@
*/
#include <linux/slab.h>
+#include <linux/export.h>
#include "mlx4.h"
diff --git a/drivers/net/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 3814fc9b1145..94bbc85a532d 100644
--- a/drivers/net/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -96,6 +96,8 @@ MODULE_PARM_DESC(log_num_mac, "Log2 max number of MACs per ETH port (1-7)");
static int log_num_vlan;
module_param_named(log_num_vlan, log_num_vlan, int, 0444);
MODULE_PARM_DESC(log_num_vlan, "Log2 max number of VLANs per ETH port (0-7)");
+/* Log2 max number of VLANs per ETH port (0-7) */
+#define MLX4_LOG_NUM_VLANS 7
static int use_prio;
module_param_named(use_prio, use_prio, bool, 0444);
@@ -143,6 +145,7 @@ static void mlx4_set_port_mask(struct mlx4_dev *dev)
if (dev->caps.port_type[i] == MLX4_PORT_TYPE_IB)
dev->caps.port_mask |= 1 << (i - 1);
}
+
static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
{
int err;
@@ -219,6 +222,10 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
dev->caps.reserved_mrws = dev_cap->reserved_mrws;
dev->caps.reserved_uars = dev_cap->reserved_uars;
dev->caps.reserved_pds = dev_cap->reserved_pds;
+ dev->caps.reserved_xrcds = (dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC) ?
+ dev_cap->reserved_xrcds : 0;
+ dev->caps.max_xrcds = (dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC) ?
+ dev_cap->max_xrcds : 0;
dev->caps.mtt_entry_sz = dev->caps.mtts_per_seg * dev_cap->mtt_entry_sz;
dev->caps.max_msg_sz = dev_cap->max_msg_sz;
dev->caps.page_size_cap = ~(u32) (dev_cap->min_page_sz - 1);
@@ -226,15 +233,10 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
dev->caps.bmme_flags = dev_cap->bmme_flags;
dev->caps.reserved_lkey = dev_cap->reserved_lkey;
dev->caps.stat_rate_support = dev_cap->stat_rate_support;
- dev->caps.udp_rss = dev_cap->udp_rss;
- dev->caps.loopback_support = dev_cap->loopback_support;
- dev->caps.vep_uc_steering = dev_cap->vep_uc_steering;
- dev->caps.vep_mc_steering = dev_cap->vep_mc_steering;
- dev->caps.wol = dev_cap->wol;
dev->caps.max_gso_sz = dev_cap->max_gso_sz;
dev->caps.log_num_macs = log_num_mac;
- dev->caps.log_num_vlans = log_num_vlan;
+ dev->caps.log_num_vlans = MLX4_LOG_NUM_VLANS;
dev->caps.log_num_prios = use_prio ? 3 : 0;
for (i = 1; i <= dev->caps.num_ports; ++i) {
@@ -262,6 +264,8 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
mlx4_set_port_mask(dev);
+ dev->caps.max_counters = 1 << ilog2(dev_cap->max_counters);
+
dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FW] = dev_cap->reserved_qps;
dev->caps.reserved_qps_cnt[MLX4_QP_REGION_ETH_ADDR] =
dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FC_ADDR] =
@@ -839,6 +843,45 @@ err_stop_fw:
return err;
}
+static int mlx4_init_counters_table(struct mlx4_dev *dev)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+ int nent;
+
+ if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_COUNTERS))
+ return -ENOENT;
+
+ nent = dev->caps.max_counters;
+ return mlx4_bitmap_init(&priv->counters_bitmap, nent, nent - 1, 0, 0);
+}
+
+static void mlx4_cleanup_counters_table(struct mlx4_dev *dev)
+{
+ mlx4_bitmap_cleanup(&mlx4_priv(dev)->counters_bitmap);
+}
+
+int mlx4_counter_alloc(struct mlx4_dev *dev, u32 *idx)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_COUNTERS))
+ return -ENOENT;
+
+ *idx = mlx4_bitmap_alloc(&priv->counters_bitmap);
+ if (*idx == -1)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mlx4_counter_alloc);
+
+void mlx4_counter_free(struct mlx4_dev *dev, u32 idx)
+{
+ mlx4_bitmap_free(&mlx4_priv(dev)->counters_bitmap, idx);
+ return;
+}
+EXPORT_SYMBOL_GPL(mlx4_counter_free);
+
static int mlx4_setup_hca(struct mlx4_dev *dev)
{
struct mlx4_priv *priv = mlx4_priv(dev);
@@ -875,11 +918,18 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
goto err_kar_unmap;
}
+ err = mlx4_init_xrcd_table(dev);
+ if (err) {
+ mlx4_err(dev, "Failed to initialize "
+ "reliable connection domain table, aborting.\n");
+ goto err_pd_table_free;
+ }
+
err = mlx4_init_mr_table(dev);
if (err) {
mlx4_err(dev, "Failed to initialize "
"memory region table, aborting.\n");
- goto err_pd_table_free;
+ goto err_xrcd_table_free;
}
err = mlx4_init_eq_table(dev);
@@ -943,6 +993,12 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
goto err_qp_table_free;
}
+ err = mlx4_init_counters_table(dev);
+ if (err && err != -ENOENT) {
+ mlx4_err(dev, "Failed to initialize counters table, aborting.\n");
+ goto err_counters_table_free;
+ }
+
for (port = 1; port <= dev->caps.num_ports; port++) {
enum mlx4_port_type port_type = 0;
mlx4_SENSE_PORT(dev, port, &port_type);
@@ -955,6 +1011,13 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
"ib capabilities (%d). Continuing with "
"caps = 0\n", port, err);
dev->caps.ib_port_def_cap[port] = ib_port_default_caps;
+
+ err = mlx4_check_ext_port_caps(dev, port);
+ if (err)
+ mlx4_warn(dev, "failed to get port %d extended "
+ "port capabilities support info (%d)."
+ " Assuming not supported\n", port, err);
+
err = mlx4_SET_PORT(dev, port);
if (err) {
mlx4_err(dev, "Failed to set port %d, aborting\n",
@@ -969,6 +1032,9 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
err_mcg_table_free:
mlx4_cleanup_mcg_table(dev);
+err_counters_table_free:
+ mlx4_cleanup_counters_table(dev);
+
err_qp_table_free:
mlx4_cleanup_qp_table(dev);
@@ -987,6 +1053,9 @@ err_eq_table_free:
err_mr_table_free:
mlx4_cleanup_mr_table(dev);
+err_xrcd_table_free:
+ mlx4_cleanup_xrcd_table(dev);
+
err_pd_table_free:
mlx4_cleanup_pd_table(dev);
@@ -1071,6 +1140,8 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port)
info->port = port;
mlx4_init_mac_table(dev, &info->mac_table);
mlx4_init_vlan_table(dev, &info->vlan_table);
+ info->base_qpn = dev->caps.reserved_qps_base[MLX4_QP_REGION_ETH_ADDR] +
+ (port - 1) * (1 << log_num_mac);
sprintf(info->dev_name, "mlx4_port%d", port);
info->port_attr.attr.name = info->dev_name;
@@ -1230,11 +1301,11 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
INIT_LIST_HEAD(&priv->pgdir_list);
mutex_init(&priv->pgdir_mutex);
- pci_read_config_byte(pdev, PCI_REVISION_ID, &dev->rev_id);
-
INIT_LIST_HEAD(&priv->bf_list);
mutex_init(&priv->bf_mutex);
+ dev->rev_id = pdev->revision;
+
/*
* Now reset the HCA before we touch the PCI capabilities or
* attempt a firmware command, since a boot ROM may have left
@@ -1299,6 +1370,7 @@ err_port:
for (--port; port >= 1; --port)
mlx4_cleanup_port_info(&priv->port[port]);
+ mlx4_cleanup_counters_table(dev);
mlx4_cleanup_mcg_table(dev);
mlx4_cleanup_qp_table(dev);
mlx4_cleanup_srq_table(dev);
@@ -1306,6 +1378,7 @@ err_port:
mlx4_cmd_use_polling(dev);
mlx4_cleanup_eq_table(dev);
mlx4_cleanup_mr_table(dev);
+ mlx4_cleanup_xrcd_table(dev);
mlx4_cleanup_pd_table(dev);
mlx4_cleanup_uar_table(dev);
@@ -1359,6 +1432,7 @@ static void mlx4_remove_one(struct pci_dev *pdev)
mlx4_CLOSE_PORT(dev, p);
}
+ mlx4_cleanup_counters_table(dev);
mlx4_cleanup_mcg_table(dev);
mlx4_cleanup_qp_table(dev);
mlx4_cleanup_srq_table(dev);
@@ -1366,6 +1440,7 @@ static void mlx4_remove_one(struct pci_dev *pdev)
mlx4_cmd_use_polling(dev);
mlx4_cleanup_eq_table(dev);
mlx4_cleanup_mr_table(dev);
+ mlx4_cleanup_xrcd_table(dev);
mlx4_cleanup_pd_table(dev);
iounmap(priv->kar);
@@ -1439,10 +1514,9 @@ static int __init mlx4_verify_params(void)
return -1;
}
- if ((log_num_vlan < 0) || (log_num_vlan > 7)) {
- pr_warning("mlx4_core: bad num_vlan: %d\n", log_num_vlan);
- return -1;
- }
+ if (log_num_vlan != 0)
+ pr_warning("mlx4_core: log_num_vlan - obsolete module param, using %d\n",
+ MLX4_LOG_NUM_VLANS);
if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 7)) {
pr_warning("mlx4_core: bad log_mtts_per_seg: %d\n", log_mtts_per_seg);
diff --git a/drivers/net/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c
index e63c37d6a115..978688c31046 100644
--- a/drivers/net/mlx4/mcg.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c
@@ -35,6 +35,7 @@
#include <linux/etherdevice.h>
#include <linux/mlx4/cmd.h>
+#include <linux/export.h>
#include "mlx4.h"
@@ -559,7 +560,8 @@ static int find_entry(struct mlx4_dev *dev, u8 port,
struct mlx4_mgm *mgm = mgm_mailbox->buf;
u8 *mgid;
int err;
- u8 op_mod = (prot == MLX4_PROT_ETH) ? !!(dev->caps.vep_mc_steering) : 0;
+ u8 op_mod = (prot == MLX4_PROT_ETH) ?
+ !!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) : 0;
mailbox = mlx4_alloc_cmd_mailbox(dev);
if (IS_ERR(mailbox))
@@ -834,7 +836,8 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
steer = (is_valid_ether_addr(&gid[10])) ? MLX4_UC_STEER : MLX4_MC_STEER;
- if (prot == MLX4_PROT_ETH && !dev->caps.vep_mc_steering)
+ if (prot == MLX4_PROT_ETH &&
+ !(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER))
return 0;
if (prot == MLX4_PROT_ETH)
@@ -853,7 +856,8 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
steer = (is_valid_ether_addr(&gid[10])) ? MLX4_UC_STEER : MLX4_MC_STEER;
- if (prot == MLX4_PROT_ETH && !dev->caps.vep_mc_steering)
+ if (prot == MLX4_PROT_ETH &&
+ !(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER))
return 0;
if (prot == MLX4_PROT_ETH) {
@@ -867,7 +871,7 @@ EXPORT_SYMBOL_GPL(mlx4_multicast_detach);
int mlx4_multicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port)
{
- if (!dev->caps.vep_mc_steering)
+ if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER))
return 0;
@@ -877,7 +881,7 @@ EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_add);
int mlx4_multicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port)
{
- if (!dev->caps.vep_mc_steering)
+ if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER))
return 0;
@@ -887,7 +891,7 @@ EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_remove);
int mlx4_unicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port)
{
- if (!dev->caps.vep_mc_steering)
+ if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER))
return 0;
@@ -897,7 +901,7 @@ EXPORT_SYMBOL_GPL(mlx4_unicast_promisc_add);
int mlx4_unicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port)
{
- if (!dev->caps.vep_mc_steering)
+ if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER))
return 0;
return remove_promisc_qp(dev, 0, port, MLX4_UC_STEER, qpn);
diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
index dd7d745fbab4..5dfa68ffc11c 100644
--- a/drivers/net/mlx4/mlx4.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
@@ -48,8 +48,8 @@
#include <linux/mlx4/doorbell.h>
#define DRV_NAME "mlx4_core"
-#define DRV_VERSION "0.01"
-#define DRV_RELDATE "May 1, 2007"
+#define DRV_VERSION "1.0"
+#define DRV_RELDATE "July 14, 2011"
enum {
MLX4_HCR_BASE = 0x80680,
@@ -335,6 +335,7 @@ struct mlx4_priv {
struct mlx4_cmd cmd;
struct mlx4_bitmap pd_bitmap;
+ struct mlx4_bitmap xrcd_bitmap;
struct mlx4_uar_table uar_table;
struct mlx4_mr_table mr_table;
struct mlx4_cq_table cq_table;
@@ -342,6 +343,7 @@ struct mlx4_priv {
struct mlx4_srq_table srq_table;
struct mlx4_qp_table qp_table;
struct mlx4_mcg_table mcg_table;
+ struct mlx4_bitmap counters_bitmap;
struct mlx4_catas_err catas_err;
@@ -383,6 +385,7 @@ int mlx4_alloc_eq_table(struct mlx4_dev *dev);
void mlx4_free_eq_table(struct mlx4_dev *dev);
int mlx4_init_pd_table(struct mlx4_dev *dev);
+int mlx4_init_xrcd_table(struct mlx4_dev *dev);
int mlx4_init_uar_table(struct mlx4_dev *dev);
int mlx4_init_mr_table(struct mlx4_dev *dev);
int mlx4_init_eq_table(struct mlx4_dev *dev);
@@ -392,6 +395,7 @@ int mlx4_init_srq_table(struct mlx4_dev *dev);
int mlx4_init_mcg_table(struct mlx4_dev *dev);
void mlx4_cleanup_pd_table(struct mlx4_dev *dev);
+void mlx4_cleanup_xrcd_table(struct mlx4_dev *dev);
void mlx4_cleanup_uar_table(struct mlx4_dev *dev);
void mlx4_cleanup_mr_table(struct mlx4_dev *dev);
void mlx4_cleanup_eq_table(struct mlx4_dev *dev);
@@ -449,6 +453,7 @@ void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table);
int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port);
int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps);
+int mlx4_check_ext_port_caps(struct mlx4_dev *dev, u8 port);
int mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
enum mlx4_protocol prot, enum mlx4_steer_type steer);
diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index 0b5150df0585..207b5add3ca8 100644
--- a/drivers/net/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -34,10 +34,12 @@
#ifndef _MLX4_EN_H_
#define _MLX4_EN_H_
+#include <linux/bitops.h>
#include <linux/compiler.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
#include <linux/mlx4/device.h>
#include <linux/mlx4/qp.h>
@@ -49,8 +51,8 @@
#include "en_port.h"
#define DRV_NAME "mlx4_en"
-#define DRV_VERSION "1.5.4.1"
-#define DRV_RELDATE "March 2011"
+#define DRV_VERSION "1.5.4.2"
+#define DRV_RELDATE "October 2011"
#define MLX4_EN_MSG_LEVEL (NETIF_MSG_LINK | NETIF_MSG_IFDOWN)
@@ -247,6 +249,7 @@ struct mlx4_en_tx_ring {
struct mlx4_srq dummy;
unsigned long bytes;
unsigned long packets;
+ unsigned long tx_csum;
spinlock_t comp_lock;
struct mlx4_bf bf;
bool bf_enabled;
@@ -269,10 +272,13 @@ struct mlx4_en_rx_ring {
u32 prod;
u32 cons;
u32 buf_size;
+ u8 fcs_del;
void *buf;
void *rx_info;
unsigned long bytes;
unsigned long packets;
+ unsigned long csum_ok;
+ unsigned long csum_none;
};
@@ -418,17 +424,17 @@ struct mlx4_en_priv {
struct mlx4_en_dev *mdev;
struct mlx4_en_port_profile *prof;
struct net_device *dev;
- struct vlan_group *vlgrp;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
struct net_device_stats stats;
struct net_device_stats ret_stats;
struct mlx4_en_port_state port_state;
spinlock_t stats_lock;
- unsigned long last_moder_packets;
+ unsigned long last_moder_packets[MAX_RX_RINGS];
unsigned long last_moder_tx_packets;
- unsigned long last_moder_bytes;
+ unsigned long last_moder_bytes[MAX_RX_RINGS];
unsigned long last_moder_jiffies;
- int last_moder_time;
+ int last_moder_time[MAX_RX_RINGS];
u16 rx_usecs;
u16 rx_frames;
u16 tx_usecs;
@@ -468,7 +474,6 @@ struct mlx4_en_priv {
u16 log_rx_info;
struct mlx4_en_tx_ring tx_ring[MAX_TX_RINGS];
- int tx_vector;
struct mlx4_en_rx_ring rx_ring[MAX_RX_RINGS];
struct mlx4_en_cq tx_cq[MAX_TX_RINGS];
struct mlx4_en_cq rx_cq[MAX_RX_RINGS];
@@ -501,14 +506,14 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
int mlx4_en_start_port(struct net_device *dev);
void mlx4_en_stop_port(struct net_device *dev);
-void mlx4_en_free_resources(struct mlx4_en_priv *priv, bool reserve_vectors);
+void mlx4_en_free_resources(struct mlx4_en_priv *priv);
int mlx4_en_alloc_resources(struct mlx4_en_priv *priv);
int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
int entries, int ring, enum cq_type mode);
-void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
- bool reserve_vectors);
-int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq);
+void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq);
+int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
+ int cq_idx);
void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq);
int mlx4_en_set_cq_moder(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq);
int mlx4_en_arm_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq);
@@ -553,7 +558,7 @@ int mlx4_en_free_tx_buf(struct net_device *dev, struct mlx4_en_tx_ring *ring);
void mlx4_en_rx_irq(struct mlx4_cq *mcq);
int mlx4_SET_MCAST_FLTR(struct mlx4_dev *dev, u8 port, u64 mac, u64 clear, u8 mode);
-int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, u8 port, struct vlan_group *grp);
+int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, struct mlx4_en_priv *priv);
int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu,
u8 pptx, u8 pfctx, u8 pprx, u8 pfcrx);
int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn,
@@ -577,8 +582,9 @@ extern const struct ethtool_ops mlx4_en_ethtool_ops;
* printk / logging functions
*/
+__printf(3, 4)
int en_print(const char *level, const struct mlx4_en_priv *priv,
- const char *format, ...) __attribute__ ((format (printf, 3, 4)));
+ const char *format, ...);
#define en_dbg(mlevel, priv, format, arg...) \
do { \
diff --git a/drivers/net/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c
index 9c188bdd7f4f..efa3e77355e4 100644
--- a/drivers/net/mlx4/mr.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mr.c
@@ -33,6 +33,7 @@
*/
#include <linux/errno.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/mlx4/cmd.h>
@@ -139,7 +140,7 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order)
buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *),
GFP_KERNEL);
- buddy->num_free = kzalloc((buddy->max_order + 1) * sizeof (int *),
+ buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free,
GFP_KERNEL);
if (!buddy->bits || !buddy->num_free)
goto err_out;
diff --git a/drivers/net/mlx4/pd.c b/drivers/net/ethernet/mellanox/mlx4/pd.c
index 1286b886dcea..260ed259ce9b 100644
--- a/drivers/net/mlx4/pd.c
+++ b/drivers/net/ethernet/mellanox/mlx4/pd.c
@@ -32,6 +32,7 @@
*/
#include <linux/errno.h>
+#include <linux/export.h>
#include <linux/io-mapping.h>
#include <asm/page.h>
@@ -61,6 +62,24 @@ void mlx4_pd_free(struct mlx4_dev *dev, u32 pdn)
}
EXPORT_SYMBOL_GPL(mlx4_pd_free);
+int mlx4_xrcd_alloc(struct mlx4_dev *dev, u32 *xrcdn)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ *xrcdn = mlx4_bitmap_alloc(&priv->xrcd_bitmap);
+ if (*xrcdn == -1)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mlx4_xrcd_alloc);
+
+void mlx4_xrcd_free(struct mlx4_dev *dev, u32 xrcdn)
+{
+ mlx4_bitmap_free(&mlx4_priv(dev)->xrcd_bitmap, xrcdn);
+}
+EXPORT_SYMBOL_GPL(mlx4_xrcd_free);
+
int mlx4_init_pd_table(struct mlx4_dev *dev)
{
struct mlx4_priv *priv = mlx4_priv(dev);
@@ -74,6 +93,18 @@ void mlx4_cleanup_pd_table(struct mlx4_dev *dev)
mlx4_bitmap_cleanup(&mlx4_priv(dev)->pd_bitmap);
}
+int mlx4_init_xrcd_table(struct mlx4_dev *dev)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ return mlx4_bitmap_init(&priv->xrcd_bitmap, (1 << 16),
+ (1 << 16) - 1, dev->caps.reserved_xrcds + 1, 0);
+}
+
+void mlx4_cleanup_xrcd_table(struct mlx4_dev *dev)
+{
+ mlx4_bitmap_cleanup(&mlx4_priv(dev)->xrcd_bitmap);
+}
int mlx4_uar_alloc(struct mlx4_dev *dev, struct mlx4_uar *uar)
{
diff --git a/drivers/net/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c
index 8856659fb43c..d942aea4927b 100644
--- a/drivers/net/mlx4/port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/port.c
@@ -32,6 +32,7 @@
#include <linux/errno.h>
#include <linux/if_ether.h>
+#include <linux/export.h>
#include <linux/mlx4/cmd.h>
@@ -65,7 +66,7 @@ void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table)
table->entries[i] = 0;
table->refs[i] = 0;
}
- table->max = 1 << dev->caps.log_num_vlans;
+ table->max = (1 << dev->caps.log_num_vlans) - MLX4_VLAN_REGULAR;
table->total = 0;
}
@@ -146,24 +147,28 @@ int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn, u8 wrap)
int i, err = 0;
int free = -1;
- if (dev->caps.vep_uc_steering) {
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER) {
err = mlx4_uc_steer_add(dev, port, mac, qpn, 1);
- if (!err) {
- entry = kmalloc(sizeof *entry, GFP_KERNEL);
- if (!entry) {
- mlx4_uc_steer_release(dev, port, mac, *qpn, 1);
- return -ENOMEM;
- }
- entry->mac = mac;
- err = radix_tree_insert(&info->mac_tree, *qpn, entry);
- if (err) {
- mlx4_uc_steer_release(dev, port, mac, *qpn, 1);
- return err;
- }
- } else
+ if (err)
return err;
+
+ entry = kmalloc(sizeof *entry, GFP_KERNEL);
+ if (!entry) {
+ mlx4_uc_steer_release(dev, port, mac, *qpn, 1);
+ return -ENOMEM;
+ }
+
+ entry->mac = mac;
+ err = radix_tree_insert(&info->mac_tree, *qpn, entry);
+ if (err) {
+ kfree(entry);
+ mlx4_uc_steer_release(dev, port, mac, *qpn, 1);
+ return err;
+ }
}
+
mlx4_dbg(dev, "Registering MAC: 0x%llx\n", (unsigned long long) mac);
+
mutex_lock(&table->mutex);
for (i = 0; i < MLX4_MAX_MAC_NUM - 1; i++) {
if (free < 0 && !table->refs[i]) {
@@ -203,7 +208,7 @@ int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn, u8 wrap)
goto out;
}
- if (!dev->caps.vep_uc_steering)
+ if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER))
*qpn = info->base_qpn + free;
++table->total;
out:
@@ -243,7 +248,7 @@ void mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, int qpn)
int index = qpn - info->base_qpn;
struct mlx4_mac_entry *entry;
- if (dev->caps.vep_uc_steering) {
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER) {
entry = radix_tree_lookup(&info->mac_tree, qpn);
if (entry) {
mlx4_uc_steer_release(dev, port, entry->mac, qpn, 1);
@@ -258,9 +263,12 @@ void mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, int qpn)
if (validate_index(dev, table, index))
goto out;
- table->entries[index] = 0;
- mlx4_set_port_mac_table(dev, port, table->entries);
- --table->total;
+ /* Check whether this address has reference count */
+ if (!(--table->refs[index])) {
+ table->entries[index] = 0;
+ mlx4_set_port_mac_table(dev, port, table->entries);
+ --table->total;
+ }
out:
mutex_unlock(&table->mutex);
}
@@ -274,7 +282,7 @@ int mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac, u8 wra
struct mlx4_mac_entry *entry;
int err;
- if (dev->caps.vep_uc_steering) {
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER) {
entry = radix_tree_lookup(&info->mac_tree, qpn);
if (!entry)
return -EINVAL;
@@ -351,6 +359,13 @@ int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index)
int free = -1;
mutex_lock(&table->mutex);
+
+ if (table->total == table->max) {
+ /* No free vlan entries */
+ err = -ENOSPC;
+ goto out;
+ }
+
for (i = MLX4_VLAN_REGULAR; i < MLX4_MAX_VLAN_NUM; i++) {
if (free < 0 && (table->refs[i] == 0)) {
free = i;
@@ -372,12 +387,6 @@ int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index)
goto out;
}
- if (table->total == table->max) {
- /* No free vlan entries */
- err = -ENOSPC;
- goto out;
- }
-
/* Register new MAC */
table->refs[free] = 1;
table->entries[free] = cpu_to_be32(vlan | MLX4_VLAN_VALID);
@@ -461,6 +470,48 @@ int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps)
return err;
}
+int mlx4_check_ext_port_caps(struct mlx4_dev *dev, u8 port)
+{
+ struct mlx4_cmd_mailbox *inmailbox, *outmailbox;
+ u8 *inbuf, *outbuf;
+ int err, packet_error;
+
+ inmailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(inmailbox))
+ return PTR_ERR(inmailbox);
+
+ outmailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(outmailbox)) {
+ mlx4_free_cmd_mailbox(dev, inmailbox);
+ return PTR_ERR(outmailbox);
+ }
+
+ inbuf = inmailbox->buf;
+ outbuf = outmailbox->buf;
+ memset(inbuf, 0, 256);
+ memset(outbuf, 0, 256);
+ inbuf[0] = 1;
+ inbuf[1] = 1;
+ inbuf[2] = 1;
+ inbuf[3] = 1;
+
+ *(__be16 *) (&inbuf[16]) = MLX4_ATTR_EXTENDED_PORT_INFO;
+ *(__be32 *) (&inbuf[20]) = cpu_to_be32(port);
+
+ err = mlx4_cmd_box(dev, inmailbox->dma, outmailbox->dma, port, 3,
+ MLX4_CMD_MAD_IFC, MLX4_CMD_TIME_CLASS_C);
+
+ packet_error = be16_to_cpu(*(__be16 *) (outbuf + 4));
+
+ dev->caps.ext_port_cap[port] = (!err && !packet_error) ?
+ MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO
+ : 0;
+
+ mlx4_free_cmd_mailbox(dev, inmailbox);
+ mlx4_free_cmd_mailbox(dev, outmailbox);
+ return err;
+}
+
int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port)
{
struct mlx4_cmd_mailbox *mailbox;
diff --git a/drivers/net/mlx4/profile.c b/drivers/net/ethernet/mellanox/mlx4/profile.c
index b967647d0c76..b967647d0c76 100644
--- a/drivers/net/mlx4/profile.c
+++ b/drivers/net/ethernet/mellanox/mlx4/profile.c
diff --git a/drivers/net/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c
index ec9350e5f21a..15f870cb2590 100644
--- a/drivers/net/mlx4/qp.c
+++ b/drivers/net/ethernet/mellanox/mlx4/qp.c
@@ -34,6 +34,7 @@
*/
#include <linux/gfp.h>
+#include <linux/export.h>
#include <linux/mlx4/cmd.h>
#include <linux/mlx4/qp.h>
@@ -280,6 +281,9 @@ int mlx4_init_qp_table(struct mlx4_dev *dev)
* We reserve 2 extra QPs per port for the special QPs. The
* block of special QPs must be aligned to a multiple of 8, so
* round up.
+ *
+ * We also reserve the MSB of the 24-bit QP number to indicate
+ * that a QP is an XRC QP.
*/
dev->caps.sqp_start =
ALIGN(dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FW], 8);
diff --git a/drivers/net/mlx4/reset.c b/drivers/net/ethernet/mellanox/mlx4/reset.c
index e5741dab3825..11e7c1cb99bf 100644
--- a/drivers/net/mlx4/reset.c
+++ b/drivers/net/ethernet/mellanox/mlx4/reset.c
@@ -77,7 +77,7 @@ int mlx4_reset(struct mlx4_dev *dev)
goto out;
}
- pcie_cap = pci_find_capability(dev->pdev, PCI_CAP_ID_EXP);
+ pcie_cap = pci_pcie_cap(dev->pdev);
for (i = 0; i < 64; ++i) {
if (i == 22 || i == 23)
diff --git a/drivers/net/mlx4/sense.c b/drivers/net/ethernet/mellanox/mlx4/sense.c
index e2337a7411d9..e2337a7411d9 100644
--- a/drivers/net/mlx4/sense.c
+++ b/drivers/net/ethernet/mellanox/mlx4/sense.c
diff --git a/drivers/net/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c
index 3b07b80a0456..9cbf3fce0145 100644
--- a/drivers/net/mlx4/srq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/srq.c
@@ -32,6 +32,7 @@
*/
#include <linux/mlx4/cmd.h>
+#include <linux/export.h>
#include <linux/gfp.h>
#include "mlx4.h"
@@ -40,20 +41,20 @@
struct mlx4_srq_context {
__be32 state_logsize_srqn;
u8 logstride;
- u8 reserved1[3];
- u8 pg_offset;
- u8 reserved2[3];
- u32 reserved3;
+ u8 reserved1;
+ __be16 xrcd;
+ __be32 pg_offset_cqn;
+ u32 reserved2;
u8 log_page_size;
- u8 reserved4[2];
+ u8 reserved3[2];
u8 mtt_base_addr_h;
__be32 mtt_base_addr_l;
__be32 pd;
__be16 limit_watermark;
__be16 wqe_cnt;
- u16 reserved5;
+ u16 reserved4;
__be16 wqe_counter;
- u32 reserved6;
+ u32 reserved5;
__be64 db_rec_addr;
};
@@ -109,8 +110,8 @@ static int mlx4_QUERY_SRQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox
MLX4_CMD_TIME_CLASS_A);
}
-int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, struct mlx4_mtt *mtt,
- u64 db_rec, struct mlx4_srq *srq)
+int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, u32 cqn, u16 xrcd,
+ struct mlx4_mtt *mtt, u64 db_rec, struct mlx4_srq *srq)
{
struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table;
struct mlx4_cmd_mailbox *mailbox;
@@ -148,6 +149,8 @@ int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, struct mlx4_mtt *mtt,
srq_context->state_logsize_srqn = cpu_to_be32((ilog2(srq->max) << 24) |
srq->srqn);
srq_context->logstride = srq->wqe_shift - 4;
+ srq_context->xrcd = cpu_to_be16(xrcd);
+ srq_context->pg_offset_cqn = cpu_to_be32(cqn & 0xffffff);
srq_context->log_page_size = mtt->page_shift - MLX4_ICM_PAGE_SHIFT;
mtt_addr = mlx4_mtt_addr(dev, mtt);
diff --git a/drivers/net/ethernet/micrel/Kconfig b/drivers/net/ethernet/micrel/Kconfig
new file mode 100644
index 000000000000..d10c2e15f4ed
--- /dev/null
+++ b/drivers/net/ethernet/micrel/Kconfig
@@ -0,0 +1,69 @@
+#
+# Micrel device configuration
+#
+
+config NET_VENDOR_MICREL
+ bool "Micrel devices"
+ default y
+ depends on (HAS_IOMEM && DMA_ENGINE) || SPI || PCI || HAS_IOMEM || \
+ (ARM && ARCH_KS8695)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Micrel devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_MICREL
+
+config ARM_KS8695_ETHER
+ tristate "KS8695 Ethernet support"
+ depends on ARM && ARCH_KS8695
+ select NET_CORE
+ select MII
+ ---help---
+ If you wish to compile a kernel for the KS8695 and want to
+ use the internal ethernet then you should answer Y to this.
+
+config KS8842
+ tristate "Micrel KSZ8841/42 with generic bus interface"
+ depends on HAS_IOMEM && DMA_ENGINE
+ ---help---
+ This platform driver is for KSZ8841(1-port) / KS8842(2-port)
+ ethernet switch chip (managed, VLAN, QoS) from Micrel or
+ Timberdale(FPGA).
+
+config KS8851
+ tristate "Micrel KS8851 SPI"
+ depends on SPI
+ select NET_CORE
+ select MII
+ select CRC32
+ ---help---
+ SPI driver for Micrel KS8851 SPI attached network chip.
+
+config KS8851_MLL
+ tristate "Micrel KS8851 MLL"
+ depends on HAS_IOMEM
+ select NET_CORE
+ select MII
+ ---help---
+ This platform driver is for Micrel KS8851 Address/data bus
+ multiplexed network chip.
+
+config KSZ884X_PCI
+ tristate "Micrel KSZ8841/2 PCI"
+ depends on PCI
+ select NET_CORE
+ select MII
+ select CRC32
+ ---help---
+ This PCI driver is for Micrel KSZ8841/KSZ8842 PCI Ethernet chip.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ksz884x.
+
+endif # NET_VENDOR_MICREL
diff --git a/drivers/net/ethernet/micrel/Makefile b/drivers/net/ethernet/micrel/Makefile
new file mode 100644
index 000000000000..c83e4bc50c73
--- /dev/null
+++ b/drivers/net/ethernet/micrel/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the Micrel network device drivers.
+#
+
+obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
+obj-$(CONFIG_KS8842) += ks8842.o
+obj-$(CONFIG_KS8851) += ks8851.o
+obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o
+obj-$(CONFIG_KSZ884X_PCI) += ksz884x.o
diff --git a/drivers/net/arm/ks8695net.c b/drivers/net/ethernet/micrel/ks8695net.c
index a7b0caa18179..ab81c0dc96e2 100644
--- a/drivers/net/arm/ks8695net.c
+++ b/drivers/net/ethernet/micrel/ks8695net.c
@@ -16,11 +16,13 @@
* Vincent Sanders <vince@simtec.co.uk>
*/
+#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/crc32.h>
@@ -412,7 +414,7 @@ ks8695_tx_irq(int irq, void *dev_id)
* Interrupt Status Register (Offset 0xF208)
* Bit29: WAN MAC Receive Status
* Bit16: LAN MAC Receive Status
- * So, this Rx interrrupt enable/status bit number is equal
+ * So, this Rx interrupt enable/status bit number is equal
* as Rx IRQ number.
*/
static inline u32 ks8695_get_rx_enable_bit(struct ks8695_priv *ksp)
@@ -1331,7 +1333,7 @@ static const struct net_device_ops ks8695_netdev_ops = {
.ndo_tx_timeout = ks8695_timeout,
.ndo_set_mac_address = ks8695_set_mac,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = ks8695_set_multicast,
+ .ndo_set_rx_mode = ks8695_set_multicast,
};
/**
diff --git a/drivers/net/arm/ks8695net.h b/drivers/net/ethernet/micrel/ks8695net.h
index 80eff6ea5163..80eff6ea5163 100644
--- a/drivers/net/arm/ks8695net.h
+++ b/drivers/net/ethernet/micrel/ks8695net.h
diff --git a/drivers/net/ks8842.c b/drivers/net/ethernet/micrel/ks8842.c
index fc12ac0d9f2e..4a6ae057e3b1 100644
--- a/drivers/net/ks8842.c
+++ b/drivers/net/ethernet/micrel/ks8842.c
@@ -23,6 +23,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
diff --git a/drivers/net/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c
index bcd9ba68c9f2..f56743a28fc0 100644
--- a/drivers/net/ks8851.c
+++ b/drivers/net/ethernet/micrel/ks8851.c
@@ -13,6 +13,7 @@
#define DEBUG
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
diff --git a/drivers/net/ks8851.h b/drivers/net/ethernet/micrel/ks8851.h
index 537fb06e5932..537fb06e5932 100644
--- a/drivers/net/ks8851.h
+++ b/drivers/net/ethernet/micrel/ks8851.h
diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c
index 61631cace913..d19c849059d8 100644
--- a/drivers/net/ks8851_mll.c
+++ b/drivers/net/ethernet/micrel/ks8851_mll.c
@@ -23,6 +23,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
@@ -34,6 +35,7 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <asm/io.h>
#define DRV_NAME "ks8851_mll"
@@ -1188,8 +1190,6 @@ static void ks_set_rx_mode(struct net_device *netdev)
int i = 0;
netdev_for_each_mc_addr(ha, netdev) {
- if (!(*ha->addr & 1))
- continue;
if (i >= MAX_MCAST_LST)
break;
memcpy(ks->mcast_lst[i++], ha->addr, ETH_ALEN);
diff --git a/drivers/net/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c
index 41ea5920c158..7ece990381c8 100644
--- a/drivers/net/ksz884x.c
+++ b/drivers/net/ethernet/micrel/ksz884x.c
@@ -17,6 +17,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ioport.h>
@@ -4699,12 +4700,11 @@ static void send_packet(struct sk_buff *skb, struct net_device *dev)
++hw->tx_int_cnt;
dma_buf = DMA_BUFFER(desc);
- dma_buf->len = this_frag->size;
+ dma_buf->len = skb_frag_size(this_frag);
dma_buf->dma = pci_map_single(
hw_priv->pdev,
- page_address(this_frag->page) +
- this_frag->page_offset,
+ skb_frag_address(this_frag),
dma_buf->len,
PCI_DMA_TODEVICE);
set_tx_buf(desc, dma_buf->dma);
@@ -5784,8 +5784,6 @@ static void netdev_set_rx_mode(struct net_device *dev)
}
netdev_for_each_mc_addr(ha, dev) {
- if (!(*ha->addr & 1))
- continue;
if (i >= MAX_MULTICAST_LIST)
break;
memcpy(hw->multi_list[i++], ha->addr, MAC_ADDR_LEN);
diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig
new file mode 100644
index 000000000000..8163fd0f453f
--- /dev/null
+++ b/drivers/net/ethernet/microchip/Kconfig
@@ -0,0 +1,38 @@
+#
+# Microchip network device configuration
+#
+
+config NET_VENDOR_MICROCHIP
+ bool "Microchip devices"
+ default y
+ depends on SPI && EXPERIMENTAL
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Microchip cards. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_MICROCHIP
+
+config ENC28J60
+ tristate "ENC28J60 support"
+ depends on SPI && EXPERIMENTAL
+ select CRC32
+ ---help---
+ Support for the Microchip EN28J60 ethernet chip.
+
+ To compile this driver as a module, choose M here. The module will be
+ called enc28j60.
+
+config ENC28J60_WRITEVERIFY
+ bool "Enable write verify"
+ depends on ENC28J60
+ ---help---
+ Enable the verify after the buffer write useful for debugging purpose.
+ If unsure, say N.
+
+endif # NET_VENDOR_MICROCHIP
diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile
new file mode 100644
index 000000000000..573d4292b9ea
--- /dev/null
+++ b/drivers/net/ethernet/microchip/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Microchip network device drivers.
+#
+
+obj-$(CONFIG_ENC28J60) += enc28j60.o
diff --git a/drivers/net/enc28j60.c b/drivers/net/ethernet/microchip/enc28j60.c
index 2837ce209cd7..50055e0282ed 100644
--- a/drivers/net/enc28j60.c
+++ b/drivers/net/ethernet/microchip/enc28j60.c
@@ -1534,7 +1534,7 @@ static const struct net_device_ops enc28j60_netdev_ops = {
.ndo_open = enc28j60_net_open,
.ndo_stop = enc28j60_net_close,
.ndo_start_xmit = enc28j60_send_packet,
- .ndo_set_multicast_list = enc28j60_set_multicast_list,
+ .ndo_set_rx_mode = enc28j60_set_multicast_list,
.ndo_set_mac_address = enc28j60_set_mac_address,
.ndo_tx_timeout = enc28j60_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/enc28j60_hw.h b/drivers/net/ethernet/microchip/enc28j60_hw.h
index 25b41de49f0e..25b41de49f0e 100644
--- a/drivers/net/enc28j60_hw.h
+++ b/drivers/net/ethernet/microchip/enc28j60_hw.h
diff --git a/drivers/net/mipsnet.c b/drivers/net/ethernet/mipsnet.c
index 869f0ea43a5b..d05b0c9e1e9c 100644
--- a/drivers/net/mipsnet.c
+++ b/drivers/net/ethernet/mipsnet.c
@@ -5,6 +5,7 @@
*/
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -241,7 +242,7 @@ static const struct net_device_ops mipsnet_netdev_ops = {
.ndo_open = mipsnet_open,
.ndo_stop = mipsnet_close,
.ndo_start_xmit = mipsnet_xmit,
- .ndo_set_multicast_list = mipsnet_set_mclist,
+ .ndo_set_rx_mode = mipsnet_set_mclist,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/ethernet/myricom/Kconfig b/drivers/net/ethernet/myricom/Kconfig
new file mode 100644
index 000000000000..540f0c6fc160
--- /dev/null
+++ b/drivers/net/ethernet/myricom/Kconfig
@@ -0,0 +1,47 @@
+#
+# Myricom device configuration
+#
+
+config NET_VENDOR_MYRI
+ bool "Myricom devices"
+ default y
+ depends on PCI && INET
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say
+ Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Myricom cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_MYRI
+
+config MYRI10GE
+ tristate "Myricom Myri-10G Ethernet support"
+ depends on PCI && INET
+ select FW_LOADER
+ select CRC32
+ select INET_LRO
+ ---help---
+ This driver supports Myricom Myri-10G Dual Protocol interface in
+ Ethernet mode. If the eeprom on your board is not recent enough,
+ you will need a newer firmware image.
+ You may get this image or more information, at:
+
+ <http://www.myri.com/scs/download-Myri10GE.html>
+
+ To compile this driver as a module, choose M here. The module
+ will be called myri10ge.
+
+config MYRI10GE_DCA
+ bool "Direct Cache Access (DCA) Support"
+ default y
+ depends on MYRI10GE && DCA && !(MYRI10GE=y && DCA=m)
+ ---help---
+ Say Y here if you want to use Direct Cache Access (DCA) in the
+ driver. DCA is a method for warming the CPU cache before data
+ is used, with the intent of lessening the impact of cache misses.
+
+endif # NET_VENDOR_MYRI
diff --git a/drivers/net/ethernet/myricom/Makefile b/drivers/net/ethernet/myricom/Makefile
new file mode 100644
index 000000000000..296c0a10056b
--- /dev/null
+++ b/drivers/net/ethernet/myricom/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Myricom network device drivers.
+#
+
+obj-$(CONFIG_MYRI10GE) += myri10ge/
diff --git a/drivers/net/myri10ge/Makefile b/drivers/net/ethernet/myricom/myri10ge/Makefile
index 5df891647aee..5df891647aee 100644
--- a/drivers/net/myri10ge/Makefile
+++ b/drivers/net/ethernet/myricom/myri10ge/Makefile
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
index bf84849600ce..0778edcf7b9a 100644
--- a/drivers/net/myri10ge/myri10ge.c
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
@@ -1,7 +1,7 @@
/*************************************************************************
* myri10ge.c: Myricom Myri-10G Ethernet driver.
*
- * Copyright (C) 2005 - 2009 Myricom, Inc.
+ * Copyright (C) 2005 - 2011 Myricom, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -79,7 +79,7 @@
#include "myri10ge_mcp.h"
#include "myri10ge_mcp_gen_header.h"
-#define MYRI10GE_VERSION_STR "1.5.2-1.459"
+#define MYRI10GE_VERSION_STR "1.5.3-1.534"
MODULE_DESCRIPTION("Myricom 10G driver (10GbE)");
MODULE_AUTHOR("Maintainer: help@myri.com");
@@ -193,6 +193,7 @@ struct myri10ge_slice_state {
int watchdog_tx_done;
int watchdog_tx_req;
int watchdog_rx_done;
+ int stuck;
#ifdef CONFIG_MYRI10GE_DCA
int cached_dca_tag;
int cpu;
@@ -210,7 +211,6 @@ struct myri10ge_priv {
int big_bytes;
int max_intr_slots;
struct net_device *dev;
- spinlock_t stats_lock;
u8 __iomem *sram;
int sram_size;
unsigned long board_span;
@@ -377,7 +377,8 @@ static inline void put_be32(__be32 val, __be32 __iomem * p)
__raw_writel((__force __u32) val, (__force void __iomem *)p);
}
-static struct net_device_stats *myri10ge_get_stats(struct net_device *dev);
+static struct rtnl_link_stats64 *myri10ge_get_stats(struct net_device *dev,
+ struct rtnl_link_stats64 *stats);
static void set_fw_name(struct myri10ge_priv *mgp, char *name, bool allocated)
{
@@ -1013,7 +1014,7 @@ static int myri10ge_reset(struct myri10ge_priv *mgp)
cmd.data2 = i;
status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_DMA,
&cmd, 0);
- };
+ }
status |=
myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd, 0);
@@ -1080,11 +1081,14 @@ static int myri10ge_toggle_relaxed(struct pci_dev *pdev, int on)
int ret, cap, err;
u16 ctl;
- cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ cap = pci_pcie_cap(pdev);
if (!cap)
return 0;
err = pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl);
+ if (err)
+ return 0;
+
ret = (ctl & PCI_EXP_DEVCTL_RELAX_EN) >> 4;
if (ret != on) {
ctl &= ~PCI_EXP_DEVCTL_RELAX_EN;
@@ -1139,20 +1143,19 @@ static void myri10ge_setup_dca(struct myri10ge_priv *mgp)
mgp->ss[i].cpu = -1;
mgp->ss[i].cached_dca_tag = -1;
myri10ge_update_dca(&mgp->ss[i]);
- }
+ }
}
static void myri10ge_teardown_dca(struct myri10ge_priv *mgp)
{
struct pci_dev *pdev = mgp->pdev;
- int err;
if (!mgp->dca_enabled)
return;
mgp->dca_enabled = 0;
if (mgp->relaxed_order)
myri10ge_toggle_relaxed(pdev, 1);
- err = dca_remove_requester(&pdev->dev);
+ dca_remove_requester(&pdev->dev);
}
static int myri10ge_notify_dca_device(struct device *dev, void *data)
@@ -1207,13 +1210,12 @@ myri10ge_rx_skb_build(struct sk_buff *skb, u8 * va,
struct skb_frag_struct *skb_frags;
skb->len = skb->data_len = len;
- skb->truesize = len + sizeof(struct sk_buff);
/* attach the page(s) */
skb_frags = skb_shinfo(skb)->frags;
while (len > 0) {
memcpy(skb_frags, rx_frags, sizeof(*skb_frags));
- len -= rx_frags->size;
+ len -= skb_frag_size(rx_frags);
skb_frags++;
rx_frags++;
skb_shinfo(skb)->nr_frags++;
@@ -1225,7 +1227,7 @@ myri10ge_rx_skb_build(struct sk_buff *skb, u8 * va,
* manually */
skb_copy_to_linear_data(skb, va, hlen);
skb_shinfo(skb)->frags[0].page_offset += hlen;
- skb_shinfo(skb)->frags[0].size -= hlen;
+ skb_frag_size_sub(&skb_shinfo(skb)->frags[0], hlen);
skb->data_len -= hlen;
skb->tail += hlen;
skb_pull(skb, MXGEFW_PAD);
@@ -1313,7 +1315,7 @@ myri10ge_unmap_rx_page(struct pci_dev *pdev,
static inline int
myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum,
- int lro_enabled)
+ bool lro_enabled)
{
struct myri10ge_priv *mgp = ss->mgp;
struct sk_buff *skb;
@@ -1339,12 +1341,12 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum,
/* Fill skb_frag_struct(s) with data from our receive */
for (i = 0, remainder = len; remainder > 0; i++) {
myri10ge_unmap_rx_page(pdev, &rx->info[idx], bytes);
- rx_frags[i].page = rx->info[idx].page;
+ __skb_frag_set_page(&rx_frags[i], rx->info[idx].page);
rx_frags[i].page_offset = rx->info[idx].page_offset;
if (remainder < MYRI10GE_ALLOC_SIZE)
- rx_frags[i].size = remainder;
+ skb_frag_size_set(&rx_frags[i], remainder);
else
- rx_frags[i].size = MYRI10GE_ALLOC_SIZE;
+ skb_frag_size_set(&rx_frags[i], MYRI10GE_ALLOC_SIZE);
rx->cnt++;
idx = rx->cnt & rx->mask;
remainder -= MYRI10GE_ALLOC_SIZE;
@@ -1352,7 +1354,7 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum,
if (lro_enabled) {
rx_frags[0].page_offset += MXGEFW_PAD;
- rx_frags[0].size -= MXGEFW_PAD;
+ skb_frag_size_sub(&rx_frags[0], MXGEFW_PAD);
len -= MXGEFW_PAD;
lro_receive_frags(&ss->rx_done.lro_mgr, rx_frags,
/* opaque, will come back in get_frag_header */
@@ -1372,16 +1374,18 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum,
ss->stats.rx_dropped++;
do {
i--;
- put_page(rx_frags[i].page);
+ __skb_frag_unref(&rx_frags[i]);
} while (i != 0);
return 0;
}
/* Attach the pages to the skb, and trim off any padding */
myri10ge_rx_skb_build(skb, va, rx_frags, len, hlen);
- if (skb_shinfo(skb)->frags[0].size <= 0) {
- put_page(skb_shinfo(skb)->frags[0].page);
+ if (skb_frag_size(&skb_shinfo(skb)->frags[0]) <= 0) {
+ skb_frag_unref(skb, 0);
skb_shinfo(skb)->nr_frags = 0;
+ } else {
+ skb->truesize += bytes * skb_shinfo(skb)->nr_frags;
}
skb->protocol = eth_type_trans(skb, dev);
skb_record_rx_queue(skb, ss - &mgp->ss[0]);
@@ -1461,7 +1465,8 @@ myri10ge_tx_done(struct myri10ge_slice_state *ss, int mcp_index)
/* start the queue if we've stopped it */
if (netif_tx_queue_stopped(dev_queue) &&
- tx->req - tx->done < (tx->mask >> 1)) {
+ tx->req - tx->done < (tx->mask >> 1) &&
+ ss->mgp->running == MYRI10GE_ETH_RUNNING) {
tx->wake_queue++;
netif_tx_wake_queue(dev_queue);
}
@@ -1472,11 +1477,9 @@ myri10ge_clean_rx_done(struct myri10ge_slice_state *ss, int budget)
{
struct myri10ge_rx_done *rx_done = &ss->rx_done;
struct myri10ge_priv *mgp = ss->mgp;
-
unsigned long rx_bytes = 0;
unsigned long rx_packets = 0;
unsigned long rx_ok;
-
int idx = rx_done->idx;
int cnt = rx_done->cnt;
int work_done = 0;
@@ -1529,16 +1532,14 @@ static inline void myri10ge_check_statblock(struct myri10ge_priv *mgp)
mgp->link_state = link_up;
if (mgp->link_state == MXGEFW_LINK_UP) {
- if (netif_msg_link(mgp))
- netdev_info(mgp->dev, "link up\n");
+ netif_info(mgp, link, mgp->dev, "link up\n");
netif_carrier_on(mgp->dev);
mgp->link_changes++;
} else {
- if (netif_msg_link(mgp))
- netdev_info(mgp->dev, "link %s\n",
- link_up == MXGEFW_LINK_MYRINET ?
+ netif_info(mgp, link, mgp->dev, "link %s\n",
+ (link_up == MXGEFW_LINK_MYRINET ?
"mismatch (Myrinet detected)" :
- "down");
+ "down"));
netif_carrier_off(mgp->dev);
mgp->link_changes++;
}
@@ -1619,7 +1620,7 @@ static irqreturn_t myri10ge_intr(int irq, void *arg)
if (send_done_count != tx->pkt_done)
myri10ge_tx_done(ss, (int)send_done_count);
if (unlikely(i > myri10ge_max_irq_loops)) {
- netdev_err(mgp->dev, "irq stuck?\n");
+ netdev_warn(mgp->dev, "irq stuck?\n");
stats->valid = 0;
schedule_work(&mgp->watchdog_work);
}
@@ -1783,9 +1784,8 @@ static const char myri10ge_gstrings_slice_stats[][ETH_GSTRING_LEN] = {
"----------- slice ---------",
"tx_pkt_start", "tx_pkt_done", "tx_req", "tx_done",
"rx_small_cnt", "rx_big_cnt",
- "wake_queue", "stop_queue", "tx_linearized", "LRO aggregated",
- "LRO flushed",
- "LRO avg aggr", "LRO no_desc"
+ "wake_queue", "stop_queue", "tx_linearized",
+ "LRO aggregated", "LRO flushed", "LRO avg aggr", "LRO no_desc",
};
#define MYRI10GE_NET_STATS_LEN 21
@@ -1831,13 +1831,15 @@ myri10ge_get_ethtool_stats(struct net_device *netdev,
{
struct myri10ge_priv *mgp = netdev_priv(netdev);
struct myri10ge_slice_state *ss;
+ struct rtnl_link_stats64 link_stats;
int slice;
int i;
/* force stats update */
- (void)myri10ge_get_stats(netdev);
+ memset(&link_stats, 0, sizeof(link_stats));
+ (void)myri10ge_get_stats(netdev, &link_stats);
for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++)
- data[i] = ((unsigned long *)&netdev->stats)[i];
+ data[i] = ((u64 *)&link_stats)[i];
data[i++] = (unsigned int)mgp->tx_boundary;
data[i++] = (unsigned int)mgp->wc_enabled;
@@ -1907,6 +1909,60 @@ static u32 myri10ge_get_msglevel(struct net_device *netdev)
return mgp->msg_enable;
}
+/*
+ * Use a low-level command to change the LED behavior. Rather than
+ * blinking (which is the normal case), when identify is used, the
+ * yellow LED turns solid.
+ */
+static int myri10ge_led(struct myri10ge_priv *mgp, int on)
+{
+ struct mcp_gen_header *hdr;
+ struct device *dev = &mgp->pdev->dev;
+ size_t hdr_off, pattern_off, hdr_len;
+ u32 pattern = 0xfffffffe;
+
+ /* find running firmware header */
+ hdr_off = swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET));
+ if ((hdr_off & 3) || hdr_off + sizeof(*hdr) > mgp->sram_size) {
+ dev_err(dev, "Running firmware has bad header offset (%d)\n",
+ (int)hdr_off);
+ return -EIO;
+ }
+ hdr_len = swab32(readl(mgp->sram + hdr_off +
+ offsetof(struct mcp_gen_header, header_length)));
+ pattern_off = hdr_off + offsetof(struct mcp_gen_header, led_pattern);
+ if (pattern_off >= (hdr_len + hdr_off)) {
+ dev_info(dev, "Firmware does not support LED identification\n");
+ return -EINVAL;
+ }
+ if (!on)
+ pattern = swab32(readl(mgp->sram + pattern_off + 4));
+ writel(htonl(pattern), mgp->sram + pattern_off);
+ return 0;
+}
+
+static int
+myri10ge_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+ int rc;
+
+ switch (state) {
+ case ETHTOOL_ID_ACTIVE:
+ rc = myri10ge_led(mgp, 1);
+ break;
+
+ case ETHTOOL_ID_INACTIVE:
+ rc = myri10ge_led(mgp, 0);
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
static const struct ethtool_ops myri10ge_ethtool_ops = {
.get_settings = myri10ge_get_settings,
.get_drvinfo = myri10ge_get_drvinfo,
@@ -1921,6 +1977,7 @@ static const struct ethtool_ops myri10ge_ethtool_ops = {
.get_ethtool_stats = myri10ge_get_ethtool_stats,
.set_msglevel = myri10ge_set_msglevel,
.get_msglevel = myri10ge_get_msglevel,
+ .set_phys_id = myri10ge_phys_id,
};
static int myri10ge_allocate_rings(struct myri10ge_slice_state *ss)
@@ -2000,8 +2057,12 @@ static int myri10ge_allocate_rings(struct myri10ge_slice_state *ss)
ss->rx_big.page_offset = MYRI10GE_ALLOC_SIZE;
ss->rx_small.watchdog_needed = 0;
ss->rx_big.watchdog_needed = 0;
- myri10ge_alloc_rx_pages(mgp, &ss->rx_small,
- mgp->small_bytes + MXGEFW_PAD, 0);
+ if (mgp->small_bytes == 0) {
+ ss->rx_small.fill_cnt = ss->rx_small.mask + 1;
+ } else {
+ myri10ge_alloc_rx_pages(mgp, &ss->rx_small,
+ mgp->small_bytes + MXGEFW_PAD, 0);
+ }
if (ss->rx_small.fill_cnt < ss->rx_small.mask + 1) {
netdev_err(dev, "slice-%d: alloced only %d small bufs\n",
@@ -2027,6 +2088,8 @@ abort_with_rx_big_ring:
}
abort_with_rx_small_ring:
+ if (mgp->small_bytes == 0)
+ ss->rx_small.fill_cnt = ss->rx_small.cnt;
for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) {
int idx = i & ss->rx_small.mask;
myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_small.info[idx],
@@ -2077,6 +2140,8 @@ static void myri10ge_free_rings(struct myri10ge_slice_state *ss)
put_page(ss->rx_big.info[idx].page);
}
+ if (mgp->small_bytes == 0)
+ ss->rx_small.fill_cnt = ss->rx_small.cnt;
for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) {
idx = i & ss->rx_small.mask;
if (i == ss->rx_small.fill_cnt - 1)
@@ -2220,7 +2285,7 @@ myri10ge_get_frag_header(struct skb_frag_struct *frag, void **mac_hdr,
struct ethhdr *eh;
struct vlan_ethhdr *veh;
struct iphdr *iph;
- u8 *va = page_address(frag->page) + frag->page_offset;
+ u8 *va = skb_frag_address(frag);
unsigned long ll_hlen;
/* passed opaque through lro_receive_frags() */
__wsum csum = (__force __wsum) (unsigned long)priv;
@@ -2255,7 +2320,7 @@ myri10ge_get_frag_header(struct skb_frag_struct *frag, void **mac_hdr,
*ip_hdr = iph;
if (iph->protocol != IPPROTO_TCP)
return -1;
- if (iph->frag_off & htons(IP_MF | IP_OFFSET))
+ if (ip_is_fragment(iph))
return -1;
*hdr_flags |= LRO_TCP;
*tcpudp_hdr = (u8 *) (*ip_hdr) + (iph->ihl << 2);
@@ -2414,7 +2479,7 @@ static int myri10ge_open(struct net_device *dev)
mgp->small_bytes = VLAN_ETH_FRAME_LEN;
/* Override the small buffer size? */
- if (myri10ge_small_bytes > 0)
+ if (myri10ge_small_bytes >= 0)
mgp->small_bytes = myri10ge_small_bytes;
/* Firmware needs the big buff size as a power of 2. Lie and
@@ -2862,9 +2927,9 @@ again:
idx = (count + tx->req) & tx->mask;
frag = &skb_shinfo(skb)->frags[frag_idx];
frag_idx++;
- len = frag->size;
- bus = pci_map_page(mgp->pdev, frag->page, frag->page_offset,
- len, PCI_DMA_TODEVICE);
+ len = skb_frag_size(frag);
+ bus = skb_frag_dma_map(&mgp->pdev->dev, frag, 0, len,
+ DMA_TO_DEVICE);
dma_unmap_addr_set(&tx->info[idx], bus, bus);
dma_unmap_len_set(&tx->info[idx], len, len);
}
@@ -2976,15 +3041,13 @@ drop:
return NETDEV_TX_OK;
}
-static struct net_device_stats *myri10ge_get_stats(struct net_device *dev)
+static struct rtnl_link_stats64 *myri10ge_get_stats(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
{
- struct myri10ge_priv *mgp = netdev_priv(dev);
- struct myri10ge_slice_netstats *slice_stats;
- struct net_device_stats *stats = &dev->stats;
+ const struct myri10ge_priv *mgp = netdev_priv(dev);
+ const struct myri10ge_slice_netstats *slice_stats;
int i;
- spin_lock(&mgp->stats_lock);
- memset(stats, 0, sizeof(*stats));
for (i = 0; i < mgp->num_slices; i++) {
slice_stats = &mgp->ss[i].stats;
stats->rx_packets += slice_stats->rx_packets;
@@ -2994,7 +3057,6 @@ static struct net_device_stats *myri10ge_get_stats(struct net_device *dev)
stats->rx_dropped += slice_stats->rx_dropped;
stats->tx_dropped += slice_stats->tx_dropped;
}
- spin_unlock(&mgp->stats_lock);
return stats;
}
@@ -3127,7 +3189,7 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
{
struct pci_dev *bridge = mgp->pdev->bus->self;
struct device *dev = &mgp->pdev->dev;
- unsigned cap;
+ int cap;
unsigned err_cap;
u16 val;
u8 ext_type;
@@ -3137,7 +3199,7 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
return;
/* check that the bridge is a root port */
- cap = pci_find_capability(bridge, PCI_CAP_ID_EXP);
+ cap = pci_pcie_cap(bridge);
pci_read_config_word(bridge, cap + PCI_CAP_FLAGS, &val);
ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4;
if (ext_type != PCI_EXP_TYPE_ROOT_PORT) {
@@ -3155,8 +3217,7 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
" to force ECRC\n");
return;
}
- cap =
- pci_find_capability(bridge, PCI_CAP_ID_EXP);
+ cap = pci_pcie_cap(bridge);
pci_read_config_word(bridge,
cap + PCI_CAP_FLAGS, &val);
ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4;
@@ -3266,7 +3327,6 @@ abort:
/* fall back to using the unaligned firmware */
mgp->tx_boundary = 2048;
set_fw_name(mgp, myri10ge_fw_unaligned, false);
-
}
static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
@@ -3277,7 +3337,7 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
int link_width, exp_cap;
u16 lnk;
- exp_cap = pci_find_capability(mgp->pdev, PCI_CAP_ID_EXP);
+ exp_cap = pci_pcie_cap(mgp->pdev);
pci_read_config_word(mgp->pdev, exp_cap + PCI_EXP_LNKSTA, &lnk);
link_width = (lnk >> 4) & 0x3f;
@@ -3327,6 +3387,26 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
mgp->fw_name);
}
+static void myri10ge_mask_surprise_down(struct pci_dev *pdev)
+{
+ struct pci_dev *bridge = pdev->bus->self;
+ int cap;
+ u32 mask;
+
+ if (bridge == NULL)
+ return;
+
+ cap = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ERR);
+ if (cap) {
+ /* a sram parity error can cause a surprise link
+ * down; since we expect and can recover from sram
+ * parity errors, mask surprise link down events */
+ pci_read_config_dword(bridge, cap + PCI_ERR_UNCOR_MASK, &mask);
+ mask |= 0x20;
+ pci_write_config_dword(bridge, cap + PCI_ERR_UNCOR_MASK, mask);
+ }
+}
+
#ifdef CONFIG_PM
static int myri10ge_suspend(struct pci_dev *pdev, pm_message_t state)
{
@@ -3422,6 +3502,42 @@ static u32 myri10ge_read_reboot(struct myri10ge_priv *mgp)
return reboot;
}
+static void
+myri10ge_check_slice(struct myri10ge_slice_state *ss, int *reset_needed,
+ int *busy_slice_cnt, u32 rx_pause_cnt)
+{
+ struct myri10ge_priv *mgp = ss->mgp;
+ int slice = ss - mgp->ss;
+
+ if (ss->tx.req != ss->tx.done &&
+ ss->tx.done == ss->watchdog_tx_done &&
+ ss->watchdog_tx_req != ss->watchdog_tx_done) {
+ /* nic seems like it might be stuck.. */
+ if (rx_pause_cnt != mgp->watchdog_pause) {
+ if (net_ratelimit())
+ netdev_warn(mgp->dev, "slice %d: TX paused, "
+ "check link partner\n", slice);
+ } else {
+ netdev_warn(mgp->dev,
+ "slice %d: TX stuck %d %d %d %d %d %d\n",
+ slice, ss->tx.queue_active, ss->tx.req,
+ ss->tx.done, ss->tx.pkt_start,
+ ss->tx.pkt_done,
+ (int)ntohl(mgp->ss[slice].fw_stats->
+ send_done_count));
+ *reset_needed = 1;
+ ss->stuck = 1;
+ }
+ }
+ if (ss->watchdog_tx_done != ss->tx.done ||
+ ss->watchdog_rx_done != ss->rx_done.cnt) {
+ *busy_slice_cnt += 1;
+ }
+ ss->watchdog_tx_done = ss->tx.done;
+ ss->watchdog_tx_req = ss->tx.req;
+ ss->watchdog_rx_done = ss->rx_done.cnt;
+}
+
/*
* This watchdog is used to check whether the board has suffered
* from a parity error and needs to be recovered.
@@ -3430,10 +3546,12 @@ static void myri10ge_watchdog(struct work_struct *work)
{
struct myri10ge_priv *mgp =
container_of(work, struct myri10ge_priv, watchdog_work);
- struct myri10ge_tx_buf *tx;
- u32 reboot;
+ struct myri10ge_slice_state *ss;
+ u32 reboot, rx_pause_cnt;
int status, rebooted;
int i;
+ int reset_needed = 0;
+ int busy_slice_cnt = 0;
u16 cmd, vendor;
mgp->watchdog_resets++;
@@ -3445,8 +3563,7 @@ static void myri10ge_watchdog(struct work_struct *work)
* For now, just report it */
reboot = myri10ge_read_reboot(mgp);
netdev_err(mgp->dev, "NIC rebooted (0x%x),%s resetting\n",
- reboot,
- myri10ge_reset_recover ? "" : " not");
+ reboot, myri10ge_reset_recover ? "" : " not");
if (myri10ge_reset_recover == 0)
return;
rtnl_lock();
@@ -3478,23 +3595,24 @@ static void myri10ge_watchdog(struct work_struct *work)
return;
}
}
- /* Perhaps it is a software error. Try to reset */
-
- netdev_err(mgp->dev, "device timeout, resetting\n");
+ /* Perhaps it is a software error. See if stuck slice
+ * has recovered, reset if not */
+ rx_pause_cnt = ntohl(mgp->ss[0].fw_stats->dropped_pause);
for (i = 0; i < mgp->num_slices; i++) {
- tx = &mgp->ss[i].tx;
- netdev_err(mgp->dev, "(%d): %d %d %d %d %d %d\n",
- i, tx->queue_active, tx->req,
- tx->done, tx->pkt_start, tx->pkt_done,
- (int)ntohl(mgp->ss[i].fw_stats->
- send_done_count));
- msleep(2000);
- netdev_info(mgp->dev, "(%d): %d %d %d %d %d %d\n",
- i, tx->queue_active, tx->req,
- tx->done, tx->pkt_start, tx->pkt_done,
- (int)ntohl(mgp->ss[i].fw_stats->
- send_done_count));
+ ss = mgp->ss;
+ if (ss->stuck) {
+ myri10ge_check_slice(ss, &reset_needed,
+ &busy_slice_cnt,
+ rx_pause_cnt);
+ ss->stuck = 0;
+ }
}
+ if (!reset_needed) {
+ netdev_dbg(mgp->dev, "not resetting\n");
+ return;
+ }
+
+ netdev_err(mgp->dev, "device timeout, resetting\n");
}
if (!rebooted) {
@@ -3547,27 +3665,8 @@ static void myri10ge_watchdog_timer(unsigned long arg)
myri10ge_fill_thresh)
ss->rx_big.watchdog_needed = 0;
}
-
- if (ss->tx.req != ss->tx.done &&
- ss->tx.done == ss->watchdog_tx_done &&
- ss->watchdog_tx_req != ss->watchdog_tx_done) {
- /* nic seems like it might be stuck.. */
- if (rx_pause_cnt != mgp->watchdog_pause) {
- if (net_ratelimit())
- netdev_err(mgp->dev, "slice %d: TX paused, check link partner\n",
- i);
- } else {
- netdev_warn(mgp->dev, "slice %d stuck:", i);
- reset_needed = 1;
- }
- }
- if (ss->watchdog_tx_done != ss->tx.done ||
- ss->watchdog_rx_done != ss->rx_done.cnt) {
- busy_slice_cnt++;
- }
- ss->watchdog_tx_done = ss->tx.done;
- ss->watchdog_tx_req = ss->tx.req;
- ss->watchdog_rx_done = ss->rx_done.cnt;
+ myri10ge_check_slice(ss, &reset_needed, &busy_slice_cnt,
+ rx_pause_cnt);
}
/* if we've sent or received no traffic, poll the NIC to
* ensure it is still there. Otherwise, we risk not noticing
@@ -3613,8 +3712,8 @@ static void myri10ge_free_slices(struct myri10ge_priv *mgp)
dma_free_coherent(&pdev->dev, bytes,
ss->fw_stats, ss->fw_stats_bus);
ss->fw_stats = NULL;
- netif_napi_del(&ss->napi);
}
+ netif_napi_del(&ss->napi);
}
kfree(mgp->ss);
mgp->ss = NULL;
@@ -3790,11 +3889,11 @@ static const struct net_device_ops myri10ge_netdev_ops = {
.ndo_open = myri10ge_open,
.ndo_stop = myri10ge_close,
.ndo_start_xmit = myri10ge_xmit,
- .ndo_get_stats = myri10ge_get_stats,
+ .ndo_get_stats64 = myri10ge_get_stats,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = myri10ge_change_mtu,
.ndo_fix_features = myri10ge_fix_features,
- .ndo_set_multicast_list = myri10ge_set_multicast_list,
+ .ndo_set_rx_mode = myri10ge_set_multicast_list,
.ndo_set_mac_address = myri10ge_set_mac_address,
};
@@ -3845,6 +3944,7 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto abort_with_enabled;
}
+ myri10ge_mask_surprise_down(pdev);
pci_set_master(pdev);
dac_enabled = 1;
status = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
@@ -3964,7 +4064,6 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
setup_timer(&mgp->watchdog_timer, myri10ge_watchdog_timer,
(unsigned long)mgp);
- spin_lock_init(&mgp->stats_lock);
SET_ETHTOOL_OPS(netdev, &myri10ge_ethtool_ops);
INIT_WORK(&mgp->watchdog_work, myri10ge_watchdog);
status = register_netdev(netdev);
diff --git a/drivers/net/myri10ge/myri10ge_mcp.h b/drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp.h
index 11be150e4d67..11be150e4d67 100644
--- a/drivers/net/myri10ge/myri10ge_mcp.h
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp.h
diff --git a/drivers/net/myri10ge/myri10ge_mcp_gen_header.h b/drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp_gen_header.h
index 62a1cbab603f..7ec4b864a550 100644
--- a/drivers/net/myri10ge/myri10ge_mcp_gen_header.h
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp_gen_header.h
@@ -45,6 +45,8 @@ struct mcp_gen_header {
unsigned bss_addr; /* start of bss */
unsigned features;
unsigned ee_hdr_addr;
+ unsigned led_pattern;
+ unsigned led_pattern_dflt;
/* 8 */
};
diff --git a/drivers/net/ethernet/natsemi/Kconfig b/drivers/net/ethernet/natsemi/Kconfig
new file mode 100644
index 000000000000..eb836f770f50
--- /dev/null
+++ b/drivers/net/ethernet/natsemi/Kconfig
@@ -0,0 +1,86 @@
+#
+# National Semi-conductor device configuration
+#
+
+config NET_VENDOR_NATSEMI
+ bool "National Semi-conductor devices"
+ default y
+ depends on AMIGA_PCMCIA || ARM || EISA || EXPERIMENTAL || H8300 || \
+ ISA || M32R || MAC || MACH_JAZZ || MACH_TX49XX || MCA || \
+ MCA_LEGACY || MIPS || PCI || PCMCIA || SUPERH || \
+ XTENSA_PLATFORM_XT2000 || ZORRO
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about National Semi-conductor devices. If you say Y,
+ you will be asked for your specific card in the following questions.
+
+if NET_VENDOR_NATSEMI
+
+config IBMLANA
+ tristate "IBM LAN Adapter/A support"
+ depends on MCA
+ ---help---
+ This is a Micro Channel Ethernet adapter. You need to set
+ CONFIG_MCA to use this driver. It is both available as an in-kernel
+ driver and as a module.
+
+ To compile this driver as a module, choose M here. The only
+ currently supported card is the IBM LAN Adapter/A for Ethernet. It
+ will both support 16K and 32K memory windows, however a 32K window
+ gives a better security against packet losses. Usage of multiple
+ boards with this driver should be possible, but has not been tested
+ up to now due to lack of hardware.
+
+config MACSONIC
+ tristate "Macintosh SONIC based ethernet (onboard, NuBus, LC, CS)"
+ depends on MAC
+ ---help---
+ Support for NatSemi SONIC based Ethernet devices. This includes
+ the onboard Ethernet in many Quadras as well as some LC-PDS,
+ a few Nubus and all known Comm Slot Ethernet cards. If you have
+ one of these say Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. This module will
+ be called macsonic.
+
+config MIPS_JAZZ_SONIC
+ tristate "MIPS JAZZ onboard SONIC Ethernet support"
+ depends on MACH_JAZZ
+ ---help---
+ This is the driver for the onboard card of MIPS Magnum 4000,
+ Acer PICA, Olivetti M700-10 and a few other identical OEM systems.
+
+config NATSEMI
+ tristate "National Semiconductor DP8381x series PCI Ethernet support"
+ depends on PCI
+ select CRC32
+ ---help---
+ This driver is for the National Semiconductor DP83810 series,
+ which is used in cards from PureData, NetGear, Linksys
+ and others, including the 83815 chip.
+ More specific information and updates are available from
+ <http://www.scyld.com/network/natsemi.html>.
+
+config NS83820
+ tristate "National Semiconductor DP83820 support"
+ depends on PCI
+ ---help---
+ This is a driver for the National Semiconductor DP83820 series
+ of gigabit ethernet MACs. Cards using this chipset include
+ the D-Link DGE-500T, PureData's PDP8023Z-TG, SMC's SMC9462TX,
+ SOHO-GA2000T, SOHO-GA2500T. The driver supports the use of
+ zero copy.
+
+config XTENSA_XT2000_SONIC
+ tristate "Xtensa XT2000 onboard SONIC Ethernet support"
+ depends on XTENSA_PLATFORM_XT2000
+ ---help---
+ This is the driver for the onboard card of the Xtensa XT2000 board.
+
+endif # NET_VENDOR_NATSEMI
diff --git a/drivers/net/ethernet/natsemi/Makefile b/drivers/net/ethernet/natsemi/Makefile
new file mode 100644
index 000000000000..9aa5dea52b3e
--- /dev/null
+++ b/drivers/net/ethernet/natsemi/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the National Semi-conductor Sonic devices.
+#
+
+obj-$(CONFIG_IBMLANA) += ibmlana.o
+obj-$(CONFIG_MACSONIC) += macsonic.o
+obj-$(CONFIG_MIPS_JAZZ_SONIC) += jazzsonic.o
+obj-$(CONFIG_NATSEMI) += natsemi.o
+obj-$(CONFIG_NS83820) += ns83820.o
+obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
diff --git a/drivers/net/ibmlana.c b/drivers/net/ethernet/natsemi/ibmlana.c
index a7d6cad32953..999407f7ebdf 100644
--- a/drivers/net/ibmlana.c
+++ b/drivers/net/ethernet/natsemi/ibmlana.c
@@ -910,7 +910,7 @@ static const struct net_device_ops ibmlana_netdev_ops = {
.ndo_open = ibmlana_open,
.ndo_stop = ibmlana_close,
.ndo_start_xmit = ibmlana_tx,
- .ndo_set_multicast_list = ibmlana_set_multicast_list,
+ .ndo_set_rx_mode = ibmlana_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/ibmlana.h b/drivers/net/ethernet/natsemi/ibmlana.h
index accd5efc9c8a..accd5efc9c8a 100644
--- a/drivers/net/ibmlana.h
+++ b/drivers/net/ethernet/natsemi/ibmlana.h
diff --git a/drivers/net/jazzsonic.c b/drivers/net/ethernet/natsemi/jazzsonic.c
index 949c1f933644..fc7c6a932ad9 100644
--- a/drivers/net/jazzsonic.c
+++ b/drivers/net/ethernet/natsemi/jazzsonic.c
@@ -111,7 +111,7 @@ static const struct net_device_ops sonic_netdev_ops = {
.ndo_stop = jazzsonic_close,
.ndo_start_xmit = sonic_send_packet,
.ndo_get_stats = sonic_get_stats,
- .ndo_set_multicast_list = sonic_multicast_list,
+ .ndo_set_rx_mode = sonic_multicast_list,
.ndo_tx_timeout = sonic_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/macsonic.c b/drivers/net/ethernet/natsemi/macsonic.c
index c93679ee6994..a2eacbfb4252 100644
--- a/drivers/net/macsonic.c
+++ b/drivers/net/ethernet/natsemi/macsonic.c
@@ -190,7 +190,7 @@ static const struct net_device_ops macsonic_netdev_ops = {
.ndo_open = macsonic_open,
.ndo_stop = macsonic_close,
.ndo_start_xmit = sonic_send_packet,
- .ndo_set_multicast_list = sonic_multicast_list,
+ .ndo_set_rx_mode = sonic_multicast_list,
.ndo_tx_timeout = sonic_tx_timeout,
.ndo_get_stats = sonic_get_stats,
.ndo_validate_addr = eth_validate_addr,
@@ -313,22 +313,13 @@ static void __devinit mac_onboard_sonic_ethernet_addr(struct net_device *dev)
static int __devinit mac_onboard_sonic_probe(struct net_device *dev)
{
- /* Bwahahaha */
- static int once_is_more_than_enough;
struct sonic_local* lp = netdev_priv(dev);
int sr;
int commslot = 0;
- if (once_is_more_than_enough)
- return -ENODEV;
- once_is_more_than_enough = 1;
-
if (!MACH_IS_MAC)
return -ENODEV;
- if (macintosh_config->ether_type != MAC_ETHER_SONIC)
- return -ENODEV;
-
printk(KERN_INFO "Checking for internal Macintosh ethernet (SONIC).. ");
/* Bogus probing, on the models which may or may not have
diff --git a/drivers/net/natsemi.c b/drivers/net/ethernet/natsemi/natsemi.c
index 8f8b65af9ed5..6ca047aab793 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/ethernet/natsemi/natsemi.c
@@ -140,7 +140,7 @@ MODULE_LICENSE("GPL");
module_param(mtu, int, 0);
module_param(debug, int, 0);
module_param(rx_copybreak, int, 0);
-module_param(dspcfg_workaround, int, 1);
+module_param(dspcfg_workaround, int, 0);
module_param_array(options, int, NULL, 0);
module_param_array(full_duplex, int, NULL, 0);
MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)");
@@ -783,7 +783,7 @@ static const struct net_device_ops natsemi_netdev_ops = {
.ndo_stop = netdev_close,
.ndo_start_xmit = start_tx,
.ndo_get_stats = get_stats,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_change_mtu = natsemi_change_mtu,
.ndo_do_ioctl = netdev_ioctl,
.ndo_tx_timeout = ns_tx_timeout,
@@ -1382,7 +1382,7 @@ static int find_mii(struct net_device *dev)
/* WCSR bits [0:4] [9:10] */
#define WCSR_RESET_SAVE 0x61f
/* RFCR bits [20] [22] [27:31] */
-#define RFCR_RESET_SAVE 0xf8500000;
+#define RFCR_RESET_SAVE 0xf8500000
static void natsemi_reset(struct net_device *dev)
{
@@ -2028,8 +2028,8 @@ static void drain_rx(struct net_device *dev)
np->rx_ring[i].cmd_status = 0;
np->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
if (np->rx_skbuff[i]) {
- pci_unmap_single(np->pci_dev,
- np->rx_dma[i], buflen,
+ pci_unmap_single(np->pci_dev, np->rx_dma[i],
+ buflen + NATSEMI_PADDING,
PCI_DMA_FROMDEVICE);
dev_kfree_skb(np->rx_skbuff[i]);
}
@@ -2920,7 +2920,7 @@ static int netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
/*
* If we're ignoring the PHY then autoneg and the internal
- * transciever are really not going to work so don't let the
+ * transceiver are really not going to work so don't let the
* user select them.
*/
if (np->ignore_phy && (ecmd->autoneg == AUTONEG_ENABLE ||
diff --git a/drivers/net/ns83820.c b/drivers/net/ethernet/natsemi/ns83820.c
index 3e4040f2f3cb..2b8f64ddfb55 100644
--- a/drivers/net/ns83820.c
+++ b/drivers/net/ethernet/natsemi/ns83820.c
@@ -106,6 +106,7 @@
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/ip.h> /* for iph */
#include <linux/in.h> /* for IPPROTO_... */
#include <linux/compiler.h>
@@ -429,10 +430,6 @@ struct ns83820 {
struct pci_dev *pci_dev;
struct net_device *ndev;
-#ifdef NS83820_VLAN_ACCEL_SUPPORT
- struct vlan_group *vlgrp;
-#endif
-
struct rx_info rx_info;
struct tasklet_struct rx_tasklet;
@@ -493,22 +490,6 @@ static inline void kick_rx(struct net_device *ndev)
#define start_tx_okay(dev) \
(((NR_TX_DESC-2 + dev->tx_done_idx - dev->tx_free_idx) % NR_TX_DESC) > MIN_TX_DESC_FREE)
-
-#ifdef NS83820_VLAN_ACCEL_SUPPORT
-static void ns83820_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp)
-{
- struct ns83820 *dev = PRIV(ndev);
-
- spin_lock_irq(&dev->misc_lock);
- spin_lock(&dev->tx_lock);
-
- dev->vlgrp = grp;
-
- spin_unlock(&dev->tx_lock);
- spin_unlock_irq(&dev->misc_lock);
-}
-#endif
-
/* Packet Receiver
*
* The hardware supports linked lists of receive descriptors for
@@ -929,14 +910,12 @@ static void rx_irq(struct net_device *ndev)
#ifdef NS83820_VLAN_ACCEL_SUPPORT
if(extsts & EXTSTS_VPKT) {
unsigned short tag;
+
tag = ntohs(extsts & EXTSTS_VTG_MASK);
- rx_rc = vlan_hwaccel_rx(skb,dev->vlgrp,tag);
- } else {
- rx_rc = netif_rx(skb);
+ __vlan_hwaccel_put_tag(skb, tag);
}
-#else
- rx_rc = netif_rx(skb);
#endif
+ rx_rc = netif_rx(skb);
if (NET_RX_DROP == rx_rc) {
netdev_mangle_me_harder_failed:
ndev->stats.rx_dropped++;
@@ -1181,13 +1160,12 @@ again:
if (!nr_frags)
break;
- buf = pci_map_page(dev->pci_dev, frag->page,
- frag->page_offset,
- frag->size, PCI_DMA_TODEVICE);
+ buf = skb_frag_dma_map(&dev->pci_dev->dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
dprintk("frag: buf=%08Lx page=%08lx offset=%08lx\n",
(long long)buf, (long) page_to_pfn(frag->page),
frag->page_offset);
- len = frag->size;
+ len = skb_frag_size(frag);
frag++;
nr_frags--;
}
@@ -1958,13 +1936,10 @@ static const struct net_device_ops netdev_ops = {
.ndo_start_xmit = ns83820_hard_start_xmit,
.ndo_get_stats = ns83820_get_stats,
.ndo_change_mtu = ns83820_change_mtu,
- .ndo_set_multicast_list = ns83820_set_multicast,
+ .ndo_set_rx_mode = ns83820_set_multicast,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_mac_address = eth_mac_addr,
.ndo_tx_timeout = ns83820_tx_timeout,
-#ifdef NS83820_VLAN_ACCEL_SUPPORT
- .ndo_vlan_rx_register = ns83820_vlan_rx_register,
-#endif
};
static int __devinit ns83820_init_one(struct pci_dev *pci_dev,
diff --git a/drivers/net/sonic.c b/drivers/net/ethernet/natsemi/sonic.c
index 26e25d7f5829..26e25d7f5829 100644
--- a/drivers/net/sonic.c
+++ b/drivers/net/ethernet/natsemi/sonic.c
diff --git a/drivers/net/sonic.h b/drivers/net/ethernet/natsemi/sonic.h
index 07091dd27e5d..07091dd27e5d 100644
--- a/drivers/net/sonic.h
+++ b/drivers/net/ethernet/natsemi/sonic.h
diff --git a/drivers/net/xtsonic.c b/drivers/net/ethernet/natsemi/xtsonic.c
index 9f12026d98e7..ccf61b9da8d1 100644
--- a/drivers/net/xtsonic.c
+++ b/drivers/net/ethernet/natsemi/xtsonic.c
@@ -122,7 +122,7 @@ static const struct net_device_ops xtsonic_netdev_ops = {
.ndo_stop = xtsonic_close,
.ndo_start_xmit = sonic_send_packet,
.ndo_get_stats = sonic_get_stats,
- .ndo_set_multicast_list = sonic_multicast_list,
+ .ndo_set_rx_mode = sonic_multicast_list,
.ndo_tx_timeout = sonic_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/ethernet/neterion/Kconfig b/drivers/net/ethernet/neterion/Kconfig
new file mode 100644
index 000000000000..ff26b54bd3fb
--- /dev/null
+++ b/drivers/net/ethernet/neterion/Kconfig
@@ -0,0 +1,55 @@
+#
+# Exar device configuration
+#
+
+config NET_VENDOR_EXAR
+ bool "Exar devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say
+ Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Exar cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_EXAR
+
+config S2IO
+ tristate "Exar Xframe 10Gb Ethernet Adapter"
+ depends on PCI
+ ---help---
+ This driver supports Exar Corp's Xframe Series 10Gb Ethernet Adapters.
+
+ More specific information on configuring the driver is in
+ <file:Documentation/networking/s2io.txt>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called s2io.
+
+config VXGE
+ tristate "Exar X3100 Series 10GbE PCIe Server Adapter"
+ depends on PCI && INET
+ ---help---
+ This driver supports Exar Corp's X3100 Series 10 GbE PCIe
+ I/O Virtualized Server Adapter.
+
+ More specific information on configuring the driver is in
+ <file:Documentation/networking/vxge.txt>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called vxge.
+
+config VXGE_DEBUG_TRACE_ALL
+ bool "Enabling All Debug trace statements in driver"
+ default n
+ depends on VXGE
+ ---help---
+ Say Y here if you want to enabling all the debug trace statements in
+ the vxge driver. By default only few debug trace statements are
+ enabled.
+
+endif # NET_VENDOR_EXAR
diff --git a/drivers/net/ethernet/neterion/Makefile b/drivers/net/ethernet/neterion/Makefile
new file mode 100644
index 000000000000..70c8058a601a
--- /dev/null
+++ b/drivers/net/ethernet/neterion/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Exar network device drivers.
+#
+
+obj-$(CONFIG_S2IO) += s2io.o
+obj-$(CONFIG_VXGE) += vxge/
diff --git a/drivers/net/s2io-regs.h b/drivers/net/ethernet/neterion/s2io-regs.h
index 3688325c11f5..3688325c11f5 100644
--- a/drivers/net/s2io-regs.h
+++ b/drivers/net/ethernet/neterion/s2io-regs.h
diff --git a/drivers/net/s2io.c b/drivers/net/ethernet/neterion/s2io.c
index df0d2c8ecc09..c27fb3dda9f4 100644
--- a/drivers/net/s2io.c
+++ b/drivers/net/ethernet/neterion/s2io.c
@@ -356,56 +356,6 @@ static void do_s2io_copy_mac_addr(struct s2io_nic *sp, int offset, u64 mac_addr)
sp->def_mac_addr[offset].mac_addr[0] = (u8) (mac_addr >> 40);
}
-/* Add the vlan */
-static void s2io_vlan_rx_register(struct net_device *dev,
- struct vlan_group *grp)
-{
- int i;
- struct s2io_nic *nic = netdev_priv(dev);
- unsigned long flags[MAX_TX_FIFOS];
- struct config_param *config = &nic->config;
- struct mac_info *mac_control = &nic->mac_control;
-
- for (i = 0; i < config->tx_fifo_num; i++) {
- struct fifo_info *fifo = &mac_control->fifos[i];
-
- spin_lock_irqsave(&fifo->tx_lock, flags[i]);
- }
-
- nic->vlgrp = grp;
-
- for (i = config->tx_fifo_num - 1; i >= 0; i--) {
- struct fifo_info *fifo = &mac_control->fifos[i];
-
- spin_unlock_irqrestore(&fifo->tx_lock, flags[i]);
- }
-}
-
-/* Unregister the vlan */
-static void s2io_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
-{
- int i;
- struct s2io_nic *nic = netdev_priv(dev);
- unsigned long flags[MAX_TX_FIFOS];
- struct config_param *config = &nic->config;
- struct mac_info *mac_control = &nic->mac_control;
-
- for (i = 0; i < config->tx_fifo_num; i++) {
- struct fifo_info *fifo = &mac_control->fifos[i];
-
- spin_lock_irqsave(&fifo->tx_lock, flags[i]);
- }
-
- if (nic->vlgrp)
- vlan_group_set_device(nic->vlgrp, vid, NULL);
-
- for (i = config->tx_fifo_num - 1; i >= 0; i--) {
- struct fifo_info *fifo = &mac_control->fifos[i];
-
- spin_unlock_irqrestore(&fifo->tx_lock, flags[i]);
- }
-}
-
/*
* Constants to be programmed into the Xena's registers, to configure
* the XAUI.
@@ -841,7 +791,7 @@ static int init_shared_mem(struct s2io_nic *nic)
tmp_p_addr = ring->rx_blocks[j].block_dma_addr;
tmp_p_addr_next = ring->rx_blocks[next].block_dma_addr;
- pre_rxd_blk = (struct RxD_block *)tmp_v_addr;
+ pre_rxd_blk = tmp_v_addr;
pre_rxd_blk->reserved_2_pNext_RxD_block =
(unsigned long)tmp_v_addr_next;
pre_rxd_blk->pNext_RxD_Blk_physical =
@@ -918,7 +868,7 @@ static int init_shared_mem(struct s2io_nic *nic)
mac_control->stats_mem_sz = size;
tmp_v_addr = mac_control->stats_mem;
- mac_control->stats_info = (struct stat_block *)tmp_v_addr;
+ mac_control->stats_info = tmp_v_addr;
memset(tmp_v_addr, 0, size);
DBG_PRINT(INIT_DBG, "%s: Ring Mem PHY: 0x%llx\n",
dev_name(&nic->pdev->dev), (unsigned long long)tmp_p_addr);
@@ -2400,12 +2350,12 @@ static struct sk_buff *s2io_txdl_getskb(struct fifo_info *fifo_data,
if (frg_cnt) {
txds++;
for (j = 0; j < frg_cnt; j++, txds++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[j];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[j];
if (!txds->Buffer_Pointer)
break;
pci_unmap_page(nic->pdev,
(dma_addr_t)txds->Buffer_Pointer,
- frag->size, PCI_DMA_TODEVICE);
+ skb_frag_size(frag), PCI_DMA_TODEVICE);
}
}
memset(txdlp, 0, (sizeof(struct TxD) * fifo_data->max_txds));
@@ -2439,7 +2389,7 @@ static void free_tx_buffers(struct s2io_nic *nic)
spin_lock_irqsave(&fifo->tx_lock, flags);
for (j = 0; j < tx_cfg->fifo_len; j++) {
- txdp = (struct TxD *)fifo->list_info[j].list_virt_addr;
+ txdp = fifo->list_info[j].list_virt_addr;
skb = s2io_txdl_getskb(&mac_control->fifos[i], txdp, j);
if (skb) {
swstats->mem_freed += skb->truesize;
@@ -3075,8 +3025,7 @@ static void tx_intr_handler(struct fifo_info *fifo_data)
get_info = fifo_data->tx_curr_get_info;
memcpy(&put_info, &fifo_data->tx_curr_put_info, sizeof(put_info));
- txdlp = (struct TxD *)
- fifo_data->list_info[get_info.offset].list_virt_addr;
+ txdlp = fifo_data->list_info[get_info.offset].list_virt_addr;
while ((!(txdlp->Control_1 & TXD_LIST_OWN_XENA)) &&
(get_info.offset != put_info.offset) &&
(txdlp->Host_Control)) {
@@ -3129,8 +3078,7 @@ static void tx_intr_handler(struct fifo_info *fifo_data)
get_info.offset++;
if (get_info.offset == get_info.fifo_len + 1)
get_info.offset = 0;
- txdlp = (struct TxD *)
- fifo_data->list_info[get_info.offset].list_virt_addr;
+ txdlp = fifo_data->list_info[get_info.offset].list_virt_addr;
fifo_data->tx_curr_get_info.offset = get_info.offset;
}
@@ -4111,7 +4059,7 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev)
struct tcphdr *th;
ip = ip_hdr(skb);
- if ((ip->frag_off & htons(IP_OFFSET|IP_MF)) == 0) {
+ if (!ip_is_fragment(ip)) {
th = (struct tcphdr *)(((unsigned char *)ip) +
ip->ihl*4);
@@ -4163,7 +4111,7 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev)
put_off = (u16)fifo->tx_curr_put_info.offset;
get_off = (u16)fifo->tx_curr_get_info.offset;
- txdp = (struct TxD *)fifo->list_info[put_off].list_virt_addr;
+ txdp = fifo->list_info[put_off].list_virt_addr;
queue_len = fifo->tx_curr_put_info.fifo_len + 1;
/* Avoid "put" pointer going beyond "get" pointer */
@@ -4237,16 +4185,16 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev)
frg_cnt = skb_shinfo(skb)->nr_frags;
/* For fragmented SKB. */
for (i = 0; i < frg_cnt; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
/* A '0' length fragment will be ignored */
- if (!frag->size)
+ if (!skb_frag_size(frag))
continue;
txdp++;
- txdp->Buffer_Pointer = (u64)pci_map_page(sp->pdev, frag->page,
- frag->page_offset,
- frag->size,
- PCI_DMA_TODEVICE);
- txdp->Control_1 = TXD_BUFFER0_SIZE(frag->size);
+ txdp->Buffer_Pointer = (u64)skb_frag_dma_map(&sp->pdev->dev,
+ frag, 0,
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ txdp->Control_1 = TXD_BUFFER0_SIZE(skb_frag_size(frag));
if (offload_type == SKB_GSO_UDP)
txdp->Control_1 |= TXD_UFO_EN;
}
@@ -5574,14 +5522,12 @@ static void s2io_ethtool_gringparam(struct net_device *dev,
ering->rx_jumbo_max_pending = MAX_RX_DESC_2;
}
- ering->rx_mini_max_pending = 0;
ering->tx_max_pending = MAX_TX_DESC;
for (i = 0; i < sp->config.rx_ring_num; i++)
rx_desc_count += sp->config.rx_cfg[i].num_rxd;
ering->rx_pending = rx_desc_count;
ering->rx_jumbo_pending = rx_desc_count;
- ering->rx_mini_pending = 0;
for (i = 0; i < sp->config.tx_fifo_num; i++)
tx_desc_count += sp->config.tx_cfg[i].fifo_len;
@@ -7734,13 +7680,11 @@ static const struct net_device_ops s2io_netdev_ops = {
.ndo_get_stats = s2io_get_stats,
.ndo_start_xmit = s2io_xmit,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = s2io_set_multicast,
+ .ndo_set_rx_mode = s2io_set_multicast,
.ndo_do_ioctl = s2io_ioctl,
.ndo_set_mac_address = s2io_set_mac_addr,
.ndo_change_mtu = s2io_change_mtu,
.ndo_set_features = s2io_set_features,
- .ndo_vlan_rx_register = s2io_vlan_rx_register,
- .ndo_vlan_rx_kill_vid = s2io_vlan_rx_kill_vid,
.ndo_tx_timeout = s2io_tx_watchdog,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = s2io_netpoll,
@@ -7972,9 +7916,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
/* Initializing the BAR1 address as the start of the FIFO pointer. */
for (j = 0; j < MAX_TX_FIFOS; j++) {
- mac_control->tx_FIFO_start[j] =
- (struct TxFIFO_element __iomem *)
- (sp->bar1 + (j * 0x00020000));
+ mac_control->tx_FIFO_start[j] = sp->bar1 + (j * 0x00020000);
}
/* Driver entry points */
@@ -8621,18 +8563,12 @@ static void queue_rx_frame(struct sk_buff *skb, u16 vlan_tag)
struct s2io_nic *sp = netdev_priv(dev);
skb->protocol = eth_type_trans(skb, dev);
- if (sp->vlgrp && vlan_tag && (sp->vlan_strip_flag)) {
- /* Queueing the vlan frame to the upper layer */
- if (sp->config.napi)
- vlan_hwaccel_receive_skb(skb, sp->vlgrp, vlan_tag);
- else
- vlan_hwaccel_rx(skb, sp->vlgrp, vlan_tag);
- } else {
- if (sp->config.napi)
- netif_receive_skb(skb);
- else
- netif_rx(skb);
- }
+ if (vlan_tag && sp->vlan_strip_flag)
+ __vlan_hwaccel_put_tag(skb, vlan_tag);
+ if (sp->config.napi)
+ netif_receive_skb(skb);
+ else
+ netif_rx(skb);
}
static void lro_append_pkt(struct s2io_nic *sp, struct lro *lro,
diff --git a/drivers/net/s2io.h b/drivers/net/ethernet/neterion/s2io.h
index 800b3a44e653..d5596926a1ef 100644
--- a/drivers/net/s2io.h
+++ b/drivers/net/ethernet/neterion/s2io.h
@@ -939,7 +939,6 @@ struct s2io_nic {
int task_flag;
unsigned long long start_time;
- struct vlan_group *vlgrp;
int vlan_strip_flag;
#define MSIX_FLG 0xA5
int num_entries;
@@ -968,8 +967,8 @@ struct s2io_nic {
u8 serial_num[VPD_STRING_LEN];
};
-#define RESET_ERROR 1;
-#define CMD_ERROR 2;
+#define RESET_ERROR 1
+#define CMD_ERROR 2
/* OS related system calls */
#ifndef readq
diff --git a/drivers/net/vxge/Makefile b/drivers/net/ethernet/neterion/vxge/Makefile
index b625e2c503f5..b625e2c503f5 100644
--- a/drivers/net/vxge/Makefile
+++ b/drivers/net/ethernet/neterion/vxge/Makefile
diff --git a/drivers/net/vxge/vxge-config.c b/drivers/net/ethernet/neterion/vxge/vxge-config.c
index 32763b2dd73f..98e2c10ae08b 100644
--- a/drivers/net/vxge/vxge-config.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-config.c
@@ -582,7 +582,7 @@ __vxge_hw_device_toc_get(void __iomem *bar0)
goto exit;
val64 = readq(&legacy_reg->toc_first_pointer);
- toc = (struct vxge_hw_toc_reg __iomem *)(bar0+val64);
+ toc = bar0 + val64;
exit:
return toc;
}
@@ -600,7 +600,7 @@ __vxge_hw_device_reg_addr_get(struct __vxge_hw_device *hldev)
u32 i;
enum vxge_hw_status status = VXGE_HW_OK;
- hldev->legacy_reg = (struct vxge_hw_legacy_reg __iomem *)hldev->bar0;
+ hldev->legacy_reg = hldev->bar0;
hldev->toc_reg = __vxge_hw_device_toc_get(hldev->bar0);
if (hldev->toc_reg == NULL) {
@@ -609,39 +609,31 @@ __vxge_hw_device_reg_addr_get(struct __vxge_hw_device *hldev)
}
val64 = readq(&hldev->toc_reg->toc_common_pointer);
- hldev->common_reg =
- (struct vxge_hw_common_reg __iomem *)(hldev->bar0 + val64);
+ hldev->common_reg = hldev->bar0 + val64;
val64 = readq(&hldev->toc_reg->toc_mrpcim_pointer);
- hldev->mrpcim_reg =
- (struct vxge_hw_mrpcim_reg __iomem *)(hldev->bar0 + val64);
+ hldev->mrpcim_reg = hldev->bar0 + val64;
for (i = 0; i < VXGE_HW_TITAN_SRPCIM_REG_SPACES; i++) {
val64 = readq(&hldev->toc_reg->toc_srpcim_pointer[i]);
- hldev->srpcim_reg[i] =
- (struct vxge_hw_srpcim_reg __iomem *)
- (hldev->bar0 + val64);
+ hldev->srpcim_reg[i] = hldev->bar0 + val64;
}
for (i = 0; i < VXGE_HW_TITAN_VPMGMT_REG_SPACES; i++) {
val64 = readq(&hldev->toc_reg->toc_vpmgmt_pointer[i]);
- hldev->vpmgmt_reg[i] =
- (struct vxge_hw_vpmgmt_reg __iomem *)(hldev->bar0 + val64);
+ hldev->vpmgmt_reg[i] = hldev->bar0 + val64;
}
for (i = 0; i < VXGE_HW_TITAN_VPATH_REG_SPACES; i++) {
val64 = readq(&hldev->toc_reg->toc_vpath_pointer[i]);
- hldev->vpath_reg[i] =
- (struct vxge_hw_vpath_reg __iomem *)
- (hldev->bar0 + val64);
+ hldev->vpath_reg[i] = hldev->bar0 + val64;
}
val64 = readq(&hldev->toc_reg->toc_kdfc);
switch (VXGE_HW_TOC_GET_KDFC_INITIAL_BIR(val64)) {
case 0:
- hldev->kdfc = (u8 __iomem *)(hldev->bar0 +
- VXGE_HW_TOC_GET_KDFC_INITIAL_OFFSET(val64));
+ hldev->kdfc = hldev->bar0 + VXGE_HW_TOC_GET_KDFC_INITIAL_OFFSET(val64) ;
break;
default:
break;
@@ -761,12 +753,11 @@ static void __vxge_hw_device_host_info_get(struct __vxge_hw_device *hldev)
static enum vxge_hw_status
__vxge_hw_verify_pci_e_info(struct __vxge_hw_device *hldev)
{
- int exp_cap;
+ struct pci_dev *dev = hldev->pdev;
u16 lnk;
/* Get the negotiated link width and speed from PCI config space */
- exp_cap = pci_find_capability(hldev->pdev, PCI_CAP_ID_EXP);
- pci_read_config_word(hldev->pdev, exp_cap + PCI_EXP_LNKSTA, &lnk);
+ pci_read_config_word(dev, dev->pcie_cap + PCI_EXP_LNKSTA, &lnk);
if ((lnk & PCI_EXP_LNKSTA_CLS) != 1)
return VXGE_HW_ERR_INVALID_PCI_INFO;
@@ -1024,7 +1015,7 @@ vxge_hw_device_hw_info_get(void __iomem *bar0,
}
val64 = readq(&toc->toc_common_pointer);
- common_reg = (struct vxge_hw_common_reg __iomem *)(bar0 + val64);
+ common_reg = bar0 + val64;
status = __vxge_hw_device_vpath_reset_in_prog_check(
(u64 __iomem *)&common_reg->vpath_rst_in_prog);
@@ -1044,8 +1035,7 @@ vxge_hw_device_hw_info_get(void __iomem *bar0,
val64 = readq(&toc->toc_vpmgmt_pointer[i]);
- vpmgmt_reg = (struct vxge_hw_vpmgmt_reg __iomem *)
- (bar0 + val64);
+ vpmgmt_reg = bar0 + val64;
hw_info->func_id = __vxge_hw_vpath_func_id_get(vpmgmt_reg);
if (__vxge_hw_device_access_rights_get(hw_info->host_type,
@@ -1054,8 +1044,7 @@ vxge_hw_device_hw_info_get(void __iomem *bar0,
val64 = readq(&toc->toc_mrpcim_pointer);
- mrpcim_reg = (struct vxge_hw_mrpcim_reg __iomem *)
- (bar0 + val64);
+ mrpcim_reg = bar0 + val64;
writeq(0, &mrpcim_reg->xgmac_gen_fw_memo_mask);
wmb();
@@ -1064,8 +1053,7 @@ vxge_hw_device_hw_info_get(void __iomem *bar0,
val64 = readq(&toc->toc_vpath_pointer[i]);
spin_lock_init(&vpath.lock);
- vpath.vp_reg = (struct vxge_hw_vpath_reg __iomem *)
- (bar0 + val64);
+ vpath.vp_reg = bar0 + val64;
vpath.vp_open = VXGE_HW_VP_NOT_OPEN;
status = __vxge_hw_vpath_pci_func_mode_get(&vpath, hw_info);
@@ -1088,8 +1076,7 @@ vxge_hw_device_hw_info_get(void __iomem *bar0,
continue;
val64 = readq(&toc->toc_vpath_pointer[i]);
- vpath.vp_reg = (struct vxge_hw_vpath_reg __iomem *)
- (bar0 + val64);
+ vpath.vp_reg = bar0 + val64;
vpath.vp_open = VXGE_HW_VP_NOT_OPEN;
status = __vxge_hw_vpath_addr_get(&vpath,
@@ -1355,9 +1342,7 @@ vxge_hw_device_initialize(
hldev->bar0 = attr->bar0;
hldev->pdev = attr->pdev;
- hldev->uld_callbacks.link_up = attr->uld_callbacks.link_up;
- hldev->uld_callbacks.link_down = attr->uld_callbacks.link_down;
- hldev->uld_callbacks.crit_err = attr->uld_callbacks.crit_err;
+ hldev->uld_callbacks = attr->uld_callbacks;
__vxge_hw_device_pci_e_init(hldev);
@@ -1994,13 +1979,11 @@ exit:
u16 vxge_hw_device_link_width_get(struct __vxge_hw_device *hldev)
{
- int link_width, exp_cap;
+ struct pci_dev *dev = hldev->pdev;
u16 lnk;
- exp_cap = pci_find_capability(hldev->pdev, PCI_CAP_ID_EXP);
- pci_read_config_word(hldev->pdev, exp_cap + PCI_EXP_LNKSTA, &lnk);
- link_width = (lnk & VXGE_HW_PCI_EXP_LNKCAP_LNK_WIDTH) >> 4;
- return link_width;
+ pci_read_config_word(dev, dev->pcie_cap + PCI_EXP_LNKSTA, &lnk);
+ return (lnk & VXGE_HW_PCI_EXP_LNKCAP_LNK_WIDTH) >> 4;
}
/*
@@ -2140,8 +2123,7 @@ __vxge_hw_ring_mempool_item_alloc(struct vxge_hw_mempool *mempoolh,
memblock_index, item,
&memblock_item_idx);
- rxdp = (struct vxge_hw_ring_rxd_1 *)
- ring->channel.reserve_arr[reserve_index];
+ rxdp = ring->channel.reserve_arr[reserve_index];
uld_priv = ((u8 *)rxdblock_priv + ring->rxd_priv_size * i);
@@ -2649,7 +2631,7 @@ __vxge_hw_mempool_create(struct __vxge_hw_device *devh,
u32 items_priv_size,
u32 items_initial,
u32 items_max,
- struct vxge_hw_mempool_cbs *mp_callback,
+ const struct vxge_hw_mempool_cbs *mp_callback,
void *userdata)
{
enum vxge_hw_status status = VXGE_HW_OK;
@@ -2833,7 +2815,9 @@ __vxge_hw_ring_create(struct __vxge_hw_vpath_handle *vp,
struct vxge_hw_ring_config *config;
struct __vxge_hw_device *hldev;
u32 vp_id;
- struct vxge_hw_mempool_cbs ring_mp_callback;
+ static const struct vxge_hw_mempool_cbs ring_mp_callback = {
+ .item_func_alloc = __vxge_hw_ring_mempool_item_alloc,
+ };
if ((vp == NULL) || (attr == NULL)) {
status = VXGE_HW_FAIL;
@@ -2888,7 +2872,6 @@ __vxge_hw_ring_create(struct __vxge_hw_vpath_handle *vp,
/* calculate actual RxD block private size */
ring->rxdblock_priv_size = ring->rxd_priv_size * ring->rxds_per_block;
- ring_mp_callback.item_func_alloc = __vxge_hw_ring_mempool_item_alloc;
ring->mempool = __vxge_hw_mempool_create(hldev,
VXGE_HW_BLOCK_SIZE,
VXGE_HW_BLOCK_SIZE,
@@ -4880,8 +4863,7 @@ vxge_hw_vpath_open(struct __vxge_hw_device *hldev,
goto vpath_open_exit8;
}
- vpath->hw_stats = (struct vxge_hw_vpath_stats_hw_info *)vpath->
- stats_block->memblock;
+ vpath->hw_stats = vpath->stats_block->memblock;
memset(vpath->hw_stats, 0,
sizeof(struct vxge_hw_vpath_stats_hw_info));
diff --git a/drivers/net/vxge/vxge-config.h b/drivers/net/ethernet/neterion/vxge/vxge-config.h
index 359b9b9f8041..5046a64f0fe8 100644
--- a/drivers/net/vxge/vxge-config.h
+++ b/drivers/net/ethernet/neterion/vxge/vxge-config.h
@@ -13,8 +13,10 @@
******************************************************************************/
#ifndef VXGE_CONFIG_H
#define VXGE_CONFIG_H
+#include <linux/hardirq.h>
#include <linux/list.h>
#include <linux/slab.h>
+#include <asm/io.h>
#ifndef VXGE_CACHE_LINE_SIZE
#define VXGE_CACHE_LINE_SIZE 128
@@ -738,7 +740,7 @@ struct __vxge_hw_device {
struct vxge_hw_device_config config;
enum vxge_hw_device_link_state link_state;
- struct vxge_hw_uld_cbs uld_callbacks;
+ const struct vxge_hw_uld_cbs *uld_callbacks;
u32 host_type;
u32 func_id;
@@ -838,7 +840,7 @@ struct vxge_hw_device_hw_info {
struct vxge_hw_device_attr {
void __iomem *bar0;
struct pci_dev *pdev;
- struct vxge_hw_uld_cbs uld_callbacks;
+ const struct vxge_hw_uld_cbs *uld_callbacks;
};
#define VXGE_HW_DEVICE_LINK_STATE_SET(hldev, ls) (hldev->link_state = ls)
diff --git a/drivers/net/vxge/vxge-ethtool.c b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c
index 92dd72d3f9de..92dd72d3f9de 100644
--- a/drivers/net/vxge/vxge-ethtool.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c
diff --git a/drivers/net/vxge/vxge-ethtool.h b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.h
index 6cf3044d7f43..6cf3044d7f43 100644
--- a/drivers/net/vxge/vxge-ethtool.h
+++ b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.h
diff --git a/drivers/net/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c
index 8ab870a2ad02..a83197d757c1 100644
--- a/drivers/net/vxge/vxge-main.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c
@@ -43,7 +43,9 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/bitops.h>
#include <linux/if_vlan.h>
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/tcp.h>
@@ -53,6 +55,7 @@
#include <linux/firmware.h>
#include <linux/net_tstamp.h>
#include <linux/prefetch.h>
+#include <linux/module.h>
#include "vxge-main.h"
#include "vxge-reg.h"
@@ -295,23 +298,22 @@ vxge_rx_complete(struct vxge_ring *ring, struct sk_buff *skb, u16 vlan,
skb_record_rx_queue(skb, ring->driver_id);
skb->protocol = eth_type_trans(skb, ring->ndev);
+ u64_stats_update_begin(&ring->stats.syncp);
ring->stats.rx_frms++;
ring->stats.rx_bytes += pkt_length;
if (skb->pkt_type == PACKET_MULTICAST)
ring->stats.rx_mcast++;
+ u64_stats_update_end(&ring->stats.syncp);
vxge_debug_rx(VXGE_TRACE,
"%s: %s:%d skb protocol = %d",
ring->ndev->name, __func__, __LINE__, skb->protocol);
- if (ring->vlgrp && ext_info->vlan &&
- (ring->vlan_tag_strip ==
- VXGE_HW_VPATH_RPA_STRIP_VLAN_TAG_ENABLE))
- vlan_gro_receive(ring->napi_p, ring->vlgrp,
- ext_info->vlan, skb);
- else
- napi_gro_receive(ring->napi_p, skb);
+ if (ext_info->vlan &&
+ ring->vlan_tag_strip == VXGE_HW_VPATH_RPA_STRIP_VLAN_TAG_ENABLE)
+ __vlan_hwaccel_put_tag(skb, ext_info->vlan);
+ napi_gro_receive(ring->napi_p, skb);
vxge_debug_entryexit(VXGE_TRACE,
"%s: %s:%d Exiting...", ring->ndev->name, __func__, __LINE__);
@@ -584,15 +586,17 @@ vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr,
for (j = 0; j < frg_cnt; j++) {
pci_unmap_page(fifo->pdev,
txd_priv->dma_buffers[i++],
- frag->size, PCI_DMA_TODEVICE);
+ skb_frag_size(frag), PCI_DMA_TODEVICE);
frag += 1;
}
vxge_hw_fifo_txdl_free(fifo_hw, dtr);
/* Updating the statistics block */
+ u64_stats_update_begin(&fifo->stats.syncp);
fifo->stats.tx_frms++;
fifo->stats.tx_bytes += skb->len;
+ u64_stats_update_end(&fifo->stats.syncp);
*done_skb++ = skb;
@@ -628,7 +632,7 @@ static u32 vxge_get_vpath_no(struct vxgedev *vdev, struct sk_buff *skb)
ip = ip_hdr(skb);
- if ((ip->frag_off & htons(IP_OFFSET|IP_MF)) == 0) {
+ if (!ip_is_fragment(ip)) {
th = (struct tcphdr *)(((unsigned char *)ip) +
ip->ihl*4);
@@ -679,8 +683,7 @@ static int vxge_mac_list_add(struct vxge_vpath *vpath, struct macInfo *mac)
new_mac_entry->state = mac->state;
vpath->mac_addr_cnt++;
- /* Is this a multicast address */
- if (0x01 & mac->macaddr[0])
+ if (is_multicast_ether_addr(mac->macaddr))
vpath->mcast_addr_cnt++;
return TRUE;
@@ -694,7 +697,7 @@ vxge_add_mac_addr(struct vxgedev *vdev, struct macInfo *mac)
struct vxge_vpath *vpath;
enum vxge_hw_vpath_mac_addr_add_mode duplicate_mode;
- if (0x01 & mac->macaddr[0]) /* multicast address */
+ if (is_multicast_ether_addr(mac->macaddr))
duplicate_mode = VXGE_HW_VPATH_MAC_ADDR_ADD_DUPLICATE;
else
duplicate_mode = VXGE_HW_VPATH_MAC_ADDR_REPLACE_DUPLICATE;
@@ -918,14 +921,14 @@ vxge_xmit(struct sk_buff *skb, struct net_device *dev)
frag = &skb_shinfo(skb)->frags[0];
for (i = 0; i < frg_cnt; i++) {
/* ignore 0 length fragment */
- if (!frag->size)
+ if (!skb_frag_size(frag))
continue;
- dma_pointer = (u64) pci_map_page(fifo->pdev, frag->page,
- frag->page_offset, frag->size,
- PCI_DMA_TODEVICE);
+ dma_pointer = (u64)skb_frag_dma_map(&fifo->pdev->dev, frag,
+ 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
- if (unlikely(pci_dma_mapping_error(fifo->pdev, dma_pointer)))
+ if (unlikely(dma_mapping_error(&fifo->pdev->dev, dma_pointer)))
goto _exit2;
vxge_debug_tx(VXGE_TRACE,
"%s: %s:%d frag = %d dma_pointer = 0x%llx",
@@ -934,7 +937,7 @@ vxge_xmit(struct sk_buff *skb, struct net_device *dev)
txdl_priv->dma_buffers[j] = dma_pointer;
vxge_hw_fifo_txdl_buffer_set(fifo_hw, dtr, j++, dma_pointer,
- frag->size);
+ skb_frag_size(frag));
frag += 1;
}
@@ -977,7 +980,7 @@ _exit1:
for (; j < i; j++) {
pci_unmap_page(fifo->pdev, txdl_priv->dma_buffers[j],
- frag->size, PCI_DMA_TODEVICE);
+ skb_frag_size(frag), PCI_DMA_TODEVICE);
frag += 1;
}
@@ -1048,7 +1051,7 @@ vxge_tx_term(void *dtrh, enum vxge_hw_txdl_state state, void *userdata)
for (j = 0; j < frg_cnt; j++) {
pci_unmap_page(fifo->pdev, txd_priv->dma_buffers[i++],
- frag->size, PCI_DMA_TODEVICE);
+ skb_frag_size(frag), PCI_DMA_TODEVICE);
frag += 1;
}
@@ -1073,8 +1076,7 @@ static int vxge_mac_list_del(struct vxge_vpath *vpath, struct macInfo *mac)
kfree((struct vxge_mac_addrs *)entry);
vpath->mac_addr_cnt--;
- /* Is this a multicast address */
- if (0x01 & mac->macaddr[0])
+ if (is_multicast_ether_addr(mac->macaddr))
vpath->mcast_addr_cnt--;
return TRUE;
}
@@ -1196,8 +1198,7 @@ static void vxge_set_multicast(struct net_device *dev)
mac_address = (u8 *)&mac_entry->macaddr;
memcpy(mac_info.macaddr, mac_address, ETH_ALEN);
- /* Is this a multicast address */
- if (0x01 & mac_info.macaddr[0]) {
+ if (is_multicast_ether_addr(mac_info.macaddr)) {
for (vpath_idx = 0; vpath_idx <
vdev->no_of_vpath;
vpath_idx++) {
@@ -1239,8 +1240,7 @@ _set_all_mcast:
mac_address = (u8 *)&mac_entry->macaddr;
memcpy(mac_info.macaddr, mac_address, ETH_ALEN);
- /* Is this a multicast address */
- if (0x01 & mac_info.macaddr[0])
+ if (is_multicast_ether_addr(mac_info.macaddr))
break;
}
@@ -1488,15 +1488,11 @@ vxge_restore_vpath_vid_table(struct vxge_vpath *vpath)
struct vxgedev *vdev = vpath->vdev;
u16 vid;
- if (vdev->vlgrp && vpath->is_open) {
+ if (!vpath->is_open)
+ return status;
- for (vid = 0; vid < VLAN_N_VID; vid++) {
- if (!vlan_group_get_device(vdev->vlgrp, vid))
- continue;
- /* Add these vlan to the vid table */
- status = vxge_hw_vpath_vid_add(vpath->handle, vid);
- }
- }
+ for_each_set_bit(vid, vdev->active_vlans, VLAN_N_VID)
+ status = vxge_hw_vpath_vid_add(vpath->handle, vid);
return status;
}
@@ -2629,11 +2625,16 @@ static void vxge_poll_vp_lockup(unsigned long data)
struct vxge_vpath *vpath;
struct vxge_ring *ring;
int i;
+ unsigned long rx_frms;
for (i = 0; i < vdev->no_of_vpath; i++) {
ring = &vdev->vpaths[i].ring;
+
+ /* Truncated to machine word size number of frames */
+ rx_frms = ACCESS_ONCE(ring->stats.rx_frms);
+
/* Did this vpath received any packets */
- if (ring->stats.prev_rx_frms == ring->stats.rx_frms) {
+ if (ring->stats.prev_rx_frms == rx_frms) {
status = vxge_hw_vpath_check_leak(ring->handle);
/* Did it received any packets last time */
@@ -2653,7 +2654,7 @@ static void vxge_poll_vp_lockup(unsigned long data)
}
}
}
- ring->stats.prev_rx_frms = ring->stats.rx_frms;
+ ring->stats.prev_rx_frms = rx_frms;
ring->last_status = status;
}
@@ -3124,14 +3125,36 @@ vxge_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *net_stats)
/* net_stats already zeroed by caller */
for (k = 0; k < vdev->no_of_vpath; k++) {
- net_stats->rx_packets += vdev->vpaths[k].ring.stats.rx_frms;
- net_stats->rx_bytes += vdev->vpaths[k].ring.stats.rx_bytes;
- net_stats->rx_errors += vdev->vpaths[k].ring.stats.rx_errors;
- net_stats->multicast += vdev->vpaths[k].ring.stats.rx_mcast;
- net_stats->rx_dropped += vdev->vpaths[k].ring.stats.rx_dropped;
- net_stats->tx_packets += vdev->vpaths[k].fifo.stats.tx_frms;
- net_stats->tx_bytes += vdev->vpaths[k].fifo.stats.tx_bytes;
- net_stats->tx_errors += vdev->vpaths[k].fifo.stats.tx_errors;
+ struct vxge_ring_stats *rxstats = &vdev->vpaths[k].ring.stats;
+ struct vxge_fifo_stats *txstats = &vdev->vpaths[k].fifo.stats;
+ unsigned int start;
+ u64 packets, bytes, multicast;
+
+ do {
+ start = u64_stats_fetch_begin(&rxstats->syncp);
+
+ packets = rxstats->rx_frms;
+ multicast = rxstats->rx_mcast;
+ bytes = rxstats->rx_bytes;
+ } while (u64_stats_fetch_retry(&rxstats->syncp, start));
+
+ net_stats->rx_packets += packets;
+ net_stats->rx_bytes += bytes;
+ net_stats->multicast += multicast;
+
+ net_stats->rx_errors += rxstats->rx_errors;
+ net_stats->rx_dropped += rxstats->rx_dropped;
+
+ do {
+ start = u64_stats_fetch_begin(&txstats->syncp);
+
+ packets = txstats->tx_frms;
+ bytes = txstats->tx_bytes;
+ } while (u64_stats_fetch_retry(&txstats->syncp, start));
+
+ net_stats->tx_packets += packets;
+ net_stats->tx_bytes += bytes;
+ net_stats->tx_errors += txstats->tx_errors;
}
return net_stats;
@@ -3275,60 +3298,6 @@ static void vxge_tx_watchdog(struct net_device *dev)
}
/**
- * vxge_vlan_rx_register
- * @dev: net device pointer.
- * @grp: vlan group
- *
- * Vlan group registration
- */
-static void
-vxge_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
-{
- struct vxgedev *vdev;
- struct vxge_vpath *vpath;
- int vp;
- u64 vid;
- enum vxge_hw_status status;
- int i;
-
- vxge_debug_entryexit(VXGE_TRACE, "%s:%d", __func__, __LINE__);
-
- vdev = netdev_priv(dev);
-
- vpath = &vdev->vpaths[0];
- if ((NULL == grp) && (vpath->is_open)) {
- /* Get the first vlan */
- status = vxge_hw_vpath_vid_get(vpath->handle, &vid);
-
- while (status == VXGE_HW_OK) {
-
- /* Delete this vlan from the vid table */
- for (vp = 0; vp < vdev->no_of_vpath; vp++) {
- vpath = &vdev->vpaths[vp];
- if (!vpath->is_open)
- continue;
-
- vxge_hw_vpath_vid_delete(vpath->handle, vid);
- }
-
- /* Get the next vlan to be deleted */
- vpath = &vdev->vpaths[0];
- status = vxge_hw_vpath_vid_get(vpath->handle, &vid);
- }
- }
-
- vdev->vlgrp = grp;
-
- for (i = 0; i < vdev->no_of_vpath; i++) {
- if (vdev->vpaths[i].is_configured)
- vdev->vpaths[i].ring.vlgrp = grp;
- }
-
- vxge_debug_entryexit(VXGE_TRACE,
- "%s:%d Exiting...", __func__, __LINE__);
-}
-
-/**
* vxge_vlan_rx_add_vid
* @dev: net device pointer.
* @vid: vid
@@ -3338,12 +3307,10 @@ vxge_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
static void
vxge_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
{
- struct vxgedev *vdev;
+ struct vxgedev *vdev = netdev_priv(dev);
struct vxge_vpath *vpath;
int vp_id;
- vdev = netdev_priv(dev);
-
/* Add these vlan to the vid table */
for (vp_id = 0; vp_id < vdev->no_of_vpath; vp_id++) {
vpath = &vdev->vpaths[vp_id];
@@ -3351,6 +3318,7 @@ vxge_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
continue;
vxge_hw_vpath_vid_add(vpath->handle, vid);
}
+ set_bit(vid, vdev->active_vlans);
}
/**
@@ -3363,16 +3331,12 @@ vxge_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
static void
vxge_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
{
- struct vxgedev *vdev;
+ struct vxgedev *vdev = netdev_priv(dev);
struct vxge_vpath *vpath;
int vp_id;
vxge_debug_entryexit(VXGE_TRACE, "%s:%d", __func__, __LINE__);
- vdev = netdev_priv(dev);
-
- vlan_group_set_device(vdev->vlgrp, vid, NULL);
-
/* Delete this vlan from the vid table */
for (vp_id = 0; vp_id < vdev->no_of_vpath; vp_id++) {
vpath = &vdev->vpaths[vp_id];
@@ -3382,6 +3346,7 @@ vxge_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
}
vxge_debug_entryexit(VXGE_TRACE,
"%s:%d Exiting...", __func__, __LINE__);
+ clear_bit(vid, vdev->active_vlans);
}
static const struct net_device_ops vxge_netdev_ops = {
@@ -3390,13 +3355,12 @@ static const struct net_device_ops vxge_netdev_ops = {
.ndo_get_stats64 = vxge_get_stats64,
.ndo_start_xmit = vxge_xmit,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = vxge_set_multicast,
+ .ndo_set_rx_mode = vxge_set_multicast,
.ndo_do_ioctl = vxge_ioctl,
.ndo_set_mac_address = vxge_set_mac_addr,
.ndo_change_mtu = vxge_change_mtu,
.ndo_fix_features = vxge_fix_features,
.ndo_set_features = vxge_set_features,
- .ndo_vlan_rx_register = vxge_vlan_rx_register,
.ndo_vlan_rx_kill_vid = vxge_vlan_rx_kill_vid,
.ndo_vlan_rx_add_vid = vxge_vlan_rx_add_vid,
.ndo_tx_timeout = vxge_tx_watchdog,
@@ -4321,6 +4285,12 @@ static int __devinit is_sriov_initialized(struct pci_dev *pdev)
return 0;
}
+static const struct vxge_hw_uld_cbs vxge_callbacks = {
+ .link_up = vxge_callback_link_up,
+ .link_down = vxge_callback_link_down,
+ .crit_err = vxge_callback_crit_err,
+};
+
/**
* vxge_probe
* @pdev : structure containing the PCI related information of the device.
@@ -4531,9 +4501,7 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre)
}
/* Setting driver callbacks */
- attr.uld_callbacks.link_up = vxge_callback_link_up;
- attr.uld_callbacks.link_down = vxge_callback_link_down;
- attr.uld_callbacks.crit_err = vxge_callback_crit_err;
+ attr.uld_callbacks = &vxge_callbacks;
status = vxge_hw_device_initialize(&hldev, &attr, device_config);
if (status != VXGE_HW_OK) {
diff --git a/drivers/net/vxge/vxge-main.h b/drivers/net/ethernet/neterion/vxge/vxge-main.h
index ed120aba443d..f52a42d1dbb7 100644
--- a/drivers/net/vxge/vxge-main.h
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.h
@@ -18,6 +18,8 @@
#include "vxge-config.h"
#include "vxge-version.h"
#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/if_vlan.h>
#define VXGE_DRIVER_NAME "vxge"
#define VXGE_DRIVER_VENDOR "Neterion, Inc"
@@ -201,30 +203,14 @@ struct vxge_msix_entry {
/* Software Statistics */
struct vxge_sw_stats {
- /* Network Stats (interface stats) */
-
- /* Tx */
- u64 tx_frms;
- u64 tx_errors;
- u64 tx_bytes;
- u64 txd_not_free;
- u64 txd_out_of_desc;
/* Virtual Path */
- u64 vpaths_open;
- u64 vpath_open_fail;
-
- /* Rx */
- u64 rx_frms;
- u64 rx_errors;
- u64 rx_bytes;
- u64 rx_mcast;
+ unsigned long vpaths_open;
+ unsigned long vpath_open_fail;
/* Misc. */
- u64 link_up;
- u64 link_down;
- u64 pci_map_fail;
- u64 skb_alloc_fail;
+ unsigned long link_up;
+ unsigned long link_down;
};
struct vxge_mac_addrs {
@@ -237,12 +223,14 @@ struct vxge_mac_addrs {
struct vxgedev;
struct vxge_fifo_stats {
+ struct u64_stats_sync syncp;
u64 tx_frms;
- u64 tx_errors;
u64 tx_bytes;
- u64 txd_not_free;
- u64 txd_out_of_desc;
- u64 pci_map_fail;
+
+ unsigned long tx_errors;
+ unsigned long txd_not_free;
+ unsigned long txd_out_of_desc;
+ unsigned long pci_map_fail;
};
struct vxge_fifo {
@@ -264,14 +252,16 @@ struct vxge_fifo {
} ____cacheline_aligned;
struct vxge_ring_stats {
- u64 prev_rx_frms;
+ struct u64_stats_sync syncp;
u64 rx_frms;
- u64 rx_errors;
- u64 rx_dropped;
- u64 rx_bytes;
u64 rx_mcast;
- u64 pci_map_fail;
- u64 skb_alloc_fail;
+ u64 rx_bytes;
+
+ unsigned long rx_errors;
+ unsigned long rx_dropped;
+ unsigned long prev_rx_frms;
+ unsigned long pci_map_fail;
+ unsigned long skb_alloc_fail;
};
struct vxge_ring {
@@ -299,7 +289,6 @@ struct vxge_ring {
#define VXGE_MAX_MAC_ADDR_COUNT 30
int vlan_tag_strip;
- struct vlan_group *vlgrp;
u32 rx_vector_no;
enum vxge_hw_status last_status;
@@ -344,7 +333,7 @@ struct vxgedev {
struct net_device *ndev;
struct pci_dev *pdev;
struct __vxge_hw_device *devh;
- struct vlan_group *vlgrp;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
int vlan_tag_strip;
struct vxge_config config;
unsigned long state;
diff --git a/drivers/net/vxge/vxge-reg.h b/drivers/net/ethernet/neterion/vxge/vxge-reg.h
index 3e658b175947..3e658b175947 100644
--- a/drivers/net/vxge/vxge-reg.h
+++ b/drivers/net/ethernet/neterion/vxge/vxge-reg.h
diff --git a/drivers/net/vxge/vxge-traffic.c b/drivers/net/ethernet/neterion/vxge/vxge-traffic.c
index f93517055162..5954fa264da1 100644
--- a/drivers/net/vxge/vxge-traffic.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-traffic.c
@@ -532,8 +532,8 @@ __vxge_hw_device_handle_error(struct __vxge_hw_device *hldev, u32 vp_id,
}
/* notify driver */
- if (hldev->uld_callbacks.crit_err)
- hldev->uld_callbacks.crit_err(
+ if (hldev->uld_callbacks->crit_err)
+ hldev->uld_callbacks->crit_err(
(struct __vxge_hw_device *)hldev,
type, vp_id);
out:
@@ -560,8 +560,8 @@ __vxge_hw_device_handle_link_down_ind(struct __vxge_hw_device *hldev)
hldev->link_state = VXGE_HW_LINK_DOWN;
/* notify driver */
- if (hldev->uld_callbacks.link_down)
- hldev->uld_callbacks.link_down(hldev);
+ if (hldev->uld_callbacks->link_down)
+ hldev->uld_callbacks->link_down(hldev);
exit:
return VXGE_HW_OK;
}
@@ -585,8 +585,8 @@ __vxge_hw_device_handle_link_up_ind(struct __vxge_hw_device *hldev)
hldev->link_state = VXGE_HW_LINK_UP;
/* notify driver */
- if (hldev->uld_callbacks.link_up)
- hldev->uld_callbacks.link_up(hldev);
+ if (hldev->uld_callbacks->link_up)
+ hldev->uld_callbacks->link_up(hldev);
exit:
return VXGE_HW_OK;
}
@@ -1309,7 +1309,7 @@ enum vxge_hw_status vxge_hw_ring_rxd_next_completed(
vxge_hw_channel_dtr_try_complete(channel, rxdh);
- rxdp = (struct vxge_hw_ring_rxd_1 *)*rxdh;
+ rxdp = *rxdh;
if (rxdp == NULL) {
status = VXGE_HW_INF_NO_MORE_COMPLETED_DESCRIPTORS;
goto exit;
@@ -1565,7 +1565,7 @@ void vxge_hw_fifo_txdl_post(struct __vxge_hw_fifo *fifo, void *txdlh)
channel = &fifo->channel;
txdl_priv = __vxge_hw_fifo_txdl_priv(fifo, txdlh);
- txdp_first = (struct vxge_hw_fifo_txd *)txdlh;
+ txdp_first = txdlh;
txdp_last = (struct vxge_hw_fifo_txd *)txdlh + (txdl_priv->frags - 1);
txdp_last->control_0 |=
@@ -1631,7 +1631,7 @@ enum vxge_hw_status vxge_hw_fifo_txdl_next_completed(
vxge_hw_channel_dtr_try_complete(channel, txdlh);
- txdp = (struct vxge_hw_fifo_txd *)*txdlh;
+ txdp = *txdlh;
if (txdp == NULL) {
status = VXGE_HW_INF_NO_MORE_COMPLETED_DESCRIPTORS;
goto exit;
diff --git a/drivers/net/vxge/vxge-traffic.h b/drivers/net/ethernet/neterion/vxge/vxge-traffic.h
index 4a518a3b131c..4a518a3b131c 100644
--- a/drivers/net/vxge/vxge-traffic.h
+++ b/drivers/net/ethernet/neterion/vxge/vxge-traffic.h
diff --git a/drivers/net/vxge/vxge-version.h b/drivers/net/ethernet/neterion/vxge/vxge-version.h
index b9efa28bab3e..b9efa28bab3e 100644
--- a/drivers/net/vxge/vxge-version.h
+++ b/drivers/net/ethernet/neterion/vxge/vxge-version.h
diff --git a/drivers/net/netx-eth.c b/drivers/net/ethernet/netx-eth.c
index 2e4b42175f3f..8d288af16fc9 100644
--- a/drivers/net/netx-eth.c
+++ b/drivers/net/ethernet/netx-eth.c
@@ -18,6 +18,7 @@
*/
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
@@ -305,7 +306,7 @@ static const struct net_device_ops netx_eth_netdev_ops = {
.ndo_stop = netx_eth_close,
.ndo_start_xmit = netx_eth_hard_start_xmit,
.ndo_tx_timeout = netx_eth_timeout,
- .ndo_set_multicast_list = netx_eth_set_multicast_list,
+ .ndo_set_rx_mode = netx_eth_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/ethernet/nuvoton/Kconfig b/drivers/net/ethernet/nuvoton/Kconfig
new file mode 100644
index 000000000000..334c17183095
--- /dev/null
+++ b/drivers/net/ethernet/nuvoton/Kconfig
@@ -0,0 +1,31 @@
+#
+# Nuvoton network device configuration
+#
+
+config NET_VENDOR_NUVOTON
+ bool "Nuvoton devices"
+ default y
+ depends on ARM && ARCH_W90X900
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Nuvoton cards. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_NUVOTON
+
+config W90P910_ETH
+ tristate "Nuvoton w90p910 Ethernet support"
+ depends on ARM && ARCH_W90X900
+ select PHYLIB
+ select NET_CORE
+ select MII
+ ---help---
+ Say Y here if you want to use built-in Ethernet ports
+ on w90p910 processor.
+
+endif # NET_VENDOR_NUVOTON
diff --git a/drivers/net/ethernet/nuvoton/Makefile b/drivers/net/ethernet/nuvoton/Makefile
new file mode 100644
index 000000000000..171aa044bd3b
--- /dev/null
+++ b/drivers/net/ethernet/nuvoton/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Nuvoton network device drivers.
+#
+
+obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o
diff --git a/drivers/net/arm/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c
index bfea499a3513..f1bfb8f8fcf0 100644
--- a/drivers/net/arm/w90p910_ether.c
+++ b/drivers/net/ethernet/nuvoton/w90p910_ether.c
@@ -919,7 +919,7 @@ static const struct net_device_ops w90p910_ether_netdev_ops = {
.ndo_stop = w90p910_ether_close,
.ndo_start_xmit = w90p910_ether_start_xmit,
.ndo_get_stats = w90p910_ether_stats,
- .ndo_set_multicast_list = w90p910_ether_set_multicast_list,
+ .ndo_set_rx_mode = w90p910_ether_set_multicast_list,
.ndo_set_mac_address = w90p910_set_mac_address,
.ndo_do_ioctl = w90p910_ether_ioctl,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/ethernet/nvidia/Kconfig b/drivers/net/ethernet/nvidia/Kconfig
new file mode 100644
index 000000000000..ace19e7f6d13
--- /dev/null
+++ b/drivers/net/ethernet/nvidia/Kconfig
@@ -0,0 +1,32 @@
+#
+# NVIDIA network device configuration
+#
+
+config NET_VENDOR_NVIDIA
+ bool "NVIDIA devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about NVIDIA cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_NVIDIA
+
+config FORCEDETH
+ tristate "nForce Ethernet support"
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) controller of this type, say Y and
+ read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called forcedeth.
+
+endif # NET_VENDOR_NVIDIA
diff --git a/drivers/net/ethernet/nvidia/Makefile b/drivers/net/ethernet/nvidia/Makefile
new file mode 100644
index 000000000000..e079ae5771d5
--- /dev/null
+++ b/drivers/net/ethernet/nvidia/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the NVIDIA network device drivers.
+#
+
+obj-$(CONFIG_FORCEDETH) += forcedeth.o
diff --git a/drivers/net/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
index 537b6957bb79..1c61d36e6570 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/ethernet/nvidia/forcedeth.c
@@ -609,7 +609,7 @@ struct nv_ethtool_str {
};
static const struct nv_ethtool_str nv_estats_str[] = {
- { "tx_bytes" },
+ { "tx_bytes" }, /* includes Ethernet FCS CRC */
{ "tx_zero_rexmt" },
{ "tx_one_rexmt" },
{ "tx_many_rexmt" },
@@ -637,7 +637,7 @@ static const struct nv_ethtool_str nv_estats_str[] = {
/* version 2 stats */
{ "tx_deferral" },
{ "tx_packets" },
- { "rx_bytes" },
+ { "rx_bytes" }, /* includes Ethernet FCS CRC */
{ "tx_pause" },
{ "rx_pause" },
{ "rx_drop_frame" },
@@ -649,7 +649,7 @@ static const struct nv_ethtool_str nv_estats_str[] = {
};
struct nv_ethtool_stats {
- u64 tx_bytes;
+ u64 tx_bytes; /* should be ifconfig->tx_bytes + 4*tx_packets */
u64 tx_zero_rexmt;
u64 tx_one_rexmt;
u64 tx_many_rexmt;
@@ -670,14 +670,14 @@ struct nv_ethtool_stats {
u64 rx_unicast;
u64 rx_multicast;
u64 rx_broadcast;
- u64 rx_packets;
+ u64 rx_packets; /* should be ifconfig->rx_packets */
u64 rx_errors_total;
u64 tx_errors_total;
/* version 2 stats */
u64 tx_deferral;
- u64 tx_packets;
- u64 rx_bytes;
+ u64 tx_packets; /* should be ifconfig->tx_packets */
+ u64 rx_bytes; /* should be ifconfig->rx_bytes + 4*rx_packets */
u64 tx_pause;
u64 rx_pause;
u64 rx_drop_frame;
@@ -820,9 +820,6 @@ struct fe_priv {
struct nv_skb_map *tx_end_flip;
int tx_stop;
- /* vlan fields */
- struct vlan_group *vlangrp;
-
/* msi/msi-x fields */
u32 msi_flags;
struct msix_entry msi_x_entry[NV_MSI_X_MAX_VECTORS];
@@ -1685,6 +1682,7 @@ static void nv_get_hw_stats(struct net_device *dev)
np->estats.tx_pause += readl(base + NvRegTxPause);
np->estats.rx_pause += readl(base + NvRegRxPause);
np->estats.rx_drop_frame += readl(base + NvRegRxDropFrame);
+ np->estats.rx_errors_total += np->estats.rx_drop_frame;
}
if (np->driver_data & DEV_HAS_STATISTICS_V3) {
@@ -1708,12 +1706,22 @@ static struct net_device_stats *nv_get_stats(struct net_device *dev)
if (np->driver_data & (DEV_HAS_STATISTICS_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_STATISTICS_V3)) {
nv_get_hw_stats(dev);
+ /*
+ * Note: because HW stats are not always available and
+ * for consistency reasons, the following ifconfig
+ * stats are managed by software: rx_bytes, tx_bytes,
+ * rx_packets and tx_packets. The related hardware
+ * stats reported by ethtool should be equivalent to
+ * these ifconfig stats, with 4 additional bytes per
+ * packet (Ethernet FCS CRC).
+ */
+
/* copy to net_device stats */
- dev->stats.tx_bytes = np->estats.tx_bytes;
dev->stats.tx_fifo_errors = np->estats.tx_fifo_errors;
dev->stats.tx_carrier_errors = np->estats.tx_carrier_errors;
dev->stats.rx_crc_errors = np->estats.rx_crc_errors;
dev->stats.rx_over_errors = np->estats.rx_over_errors;
+ dev->stats.rx_fifo_errors = np->estats.rx_drop_frame;
dev->stats.rx_errors = np->estats.rx_errors_total;
dev->stats.tx_errors = np->estats.tx_errors_total;
}
@@ -2102,8 +2110,10 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* add fragments to entries count */
for (i = 0; i < fragments; i++) {
- entries += (skb_shinfo(skb)->frags[i].size >> NV_TX2_TSO_MAX_SHIFT) +
- ((skb_shinfo(skb)->frags[i].size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
+ u32 frag_size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
+
+ entries += (frag_size >> NV_TX2_TSO_MAX_SHIFT) +
+ ((frag_size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
}
spin_lock_irqsave(&np->lock, flags);
@@ -2141,28 +2151,31 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* setup the fragments */
for (i = 0; i < fragments; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- u32 size = frag->size;
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ u32 frag_size = skb_frag_size(frag);
offset = 0;
do {
prev_tx = put_tx;
prev_tx_ctx = np->put_tx_ctx;
- bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size;
- np->put_tx_ctx->dma = pci_map_page(np->pci_dev, frag->page, frag->page_offset+offset, bcnt,
- PCI_DMA_TODEVICE);
+ bcnt = (frag_size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : frag_size;
+ np->put_tx_ctx->dma = skb_frag_dma_map(
+ &np->pci_dev->dev,
+ frag, offset,
+ bcnt,
+ DMA_TO_DEVICE);
np->put_tx_ctx->dma_len = bcnt;
np->put_tx_ctx->dma_single = 0;
put_tx->buf = cpu_to_le32(np->put_tx_ctx->dma);
put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags);
offset += bcnt;
- size -= bcnt;
+ frag_size -= bcnt;
if (unlikely(put_tx++ == np->last_tx.orig))
put_tx = np->first_tx.orig;
if (unlikely(np->put_tx_ctx++ == np->last_tx_ctx))
np->put_tx_ctx = np->first_tx_ctx;
- } while (size);
+ } while (frag_size);
}
/* set last fragment flag */
@@ -2211,8 +2224,10 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
/* add fragments to entries count */
for (i = 0; i < fragments; i++) {
- entries += (skb_shinfo(skb)->frags[i].size >> NV_TX2_TSO_MAX_SHIFT) +
- ((skb_shinfo(skb)->frags[i].size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
+ u32 frag_size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
+
+ entries += (frag_size >> NV_TX2_TSO_MAX_SHIFT) +
+ ((frag_size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
}
spin_lock_irqsave(&np->lock, flags);
@@ -2253,15 +2268,18 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
/* setup the fragments */
for (i = 0; i < fragments; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- u32 size = frag->size;
+ u32 frag_size = skb_frag_size(frag);
offset = 0;
do {
prev_tx = put_tx;
prev_tx_ctx = np->put_tx_ctx;
- bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size;
- np->put_tx_ctx->dma = pci_map_page(np->pci_dev, frag->page, frag->page_offset+offset, bcnt,
- PCI_DMA_TODEVICE);
+ bcnt = (frag_size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : frag_size;
+ np->put_tx_ctx->dma = skb_frag_dma_map(
+ &np->pci_dev->dev,
+ frag, offset,
+ bcnt,
+ DMA_TO_DEVICE);
np->put_tx_ctx->dma_len = bcnt;
np->put_tx_ctx->dma_single = 0;
put_tx->bufhigh = cpu_to_le32(dma_high(np->put_tx_ctx->dma));
@@ -2269,12 +2287,12 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags);
offset += bcnt;
- size -= bcnt;
+ frag_size -= bcnt;
if (unlikely(put_tx++ == np->last_tx.ex))
put_tx = np->first_tx.ex;
if (unlikely(np->put_tx_ctx++ == np->last_tx_ctx))
np->put_tx_ctx = np->first_tx_ctx;
- } while (size);
+ } while (frag_size);
}
/* set last fragment flag */
@@ -2367,13 +2385,8 @@ static int nv_tx_done(struct net_device *dev, int limit)
if (np->desc_ver == DESC_VER_1) {
if (flags & NV_TX_LASTPACKET) {
if (flags & NV_TX_ERROR) {
- if (flags & NV_TX_UNDERFLOW)
- dev->stats.tx_fifo_errors++;
- if (flags & NV_TX_CARRIERLOST)
- dev->stats.tx_carrier_errors++;
if ((flags & NV_TX_RETRYERROR) && !(flags & NV_TX_RETRYCOUNT_MASK))
nv_legacybackoff_reseed(dev);
- dev->stats.tx_errors++;
} else {
dev->stats.tx_packets++;
dev->stats.tx_bytes += np->get_tx_ctx->skb->len;
@@ -2385,13 +2398,8 @@ static int nv_tx_done(struct net_device *dev, int limit)
} else {
if (flags & NV_TX2_LASTPACKET) {
if (flags & NV_TX2_ERROR) {
- if (flags & NV_TX2_UNDERFLOW)
- dev->stats.tx_fifo_errors++;
- if (flags & NV_TX2_CARRIERLOST)
- dev->stats.tx_carrier_errors++;
if ((flags & NV_TX2_RETRYERROR) && !(flags & NV_TX2_RETRYCOUNT_MASK))
nv_legacybackoff_reseed(dev);
- dev->stats.tx_errors++;
} else {
dev->stats.tx_packets++;
dev->stats.tx_bytes += np->get_tx_ctx->skb->len;
@@ -2427,15 +2435,16 @@ static int nv_tx_done_optimized(struct net_device *dev, int limit)
nv_unmap_txskb(np, np->get_tx_ctx);
if (flags & NV_TX2_LASTPACKET) {
- if (!(flags & NV_TX2_ERROR))
- dev->stats.tx_packets++;
- else {
+ if (flags & NV_TX2_ERROR) {
if ((flags & NV_TX2_RETRYERROR) && !(flags & NV_TX2_RETRYCOUNT_MASK)) {
if (np->driver_data & DEV_HAS_GEAR_MODE)
nv_gear_backoff_reseed(dev);
else
nv_legacybackoff_reseed(dev);
}
+ } else {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += np->get_tx_ctx->skb->len;
}
dev_kfree_skb_any(np->get_tx_ctx->skb);
@@ -2629,7 +2638,6 @@ static int nv_rx_process(struct net_device *dev, int limit)
if ((flags & NV_RX_ERROR_MASK) == NV_RX_ERROR4) {
len = nv_getlen(dev, skb->data, len);
if (len < 0) {
- dev->stats.rx_errors++;
dev_kfree_skb(skb);
goto next_pkt;
}
@@ -2643,11 +2651,6 @@ static int nv_rx_process(struct net_device *dev, int limit)
else {
if (flags & NV_RX_MISSEDFRAME)
dev->stats.rx_missed_errors++;
- if (flags & NV_RX_CRCERR)
- dev->stats.rx_crc_errors++;
- if (flags & NV_RX_OVERFLOW)
- dev->stats.rx_over_errors++;
- dev->stats.rx_errors++;
dev_kfree_skb(skb);
goto next_pkt;
}
@@ -2663,7 +2666,6 @@ static int nv_rx_process(struct net_device *dev, int limit)
if ((flags & NV_RX2_ERROR_MASK) == NV_RX2_ERROR4) {
len = nv_getlen(dev, skb->data, len);
if (len < 0) {
- dev->stats.rx_errors++;
dev_kfree_skb(skb);
goto next_pkt;
}
@@ -2675,11 +2677,6 @@ static int nv_rx_process(struct net_device *dev, int limit)
}
/* the rest are hard errors */
else {
- if (flags & NV_RX2_CRCERR)
- dev->stats.rx_crc_errors++;
- if (flags & NV_RX2_OVERFLOW)
- dev->stats.rx_over_errors++;
- dev->stats.rx_errors++;
dev_kfree_skb(skb);
goto next_pkt;
}
@@ -2766,18 +2763,20 @@ static int nv_rx_process_optimized(struct net_device *dev, int limit)
skb->protocol = eth_type_trans(skb, dev);
prefetch(skb->data);
- if (likely(!np->vlangrp)) {
- napi_gro_receive(&np->napi, skb);
- } else {
- vlanflags = le32_to_cpu(np->get_rx.ex->buflow);
- if (vlanflags & NV_RX3_VLAN_TAG_PRESENT) {
- vlan_gro_receive(&np->napi, np->vlangrp,
- vlanflags & NV_RX3_VLAN_TAG_MASK, skb);
- } else {
- napi_gro_receive(&np->napi, skb);
- }
- }
+ vlanflags = le32_to_cpu(np->get_rx.ex->buflow);
+ /*
+ * There's need to check for NETIF_F_HW_VLAN_RX here.
+ * Even if vlan rx accel is disabled,
+ * NV_RX3_VLAN_TAG_PRESENT is pseudo randomly set.
+ */
+ if (dev->features & NETIF_F_HW_VLAN_RX &&
+ vlanflags & NV_RX3_VLAN_TAG_PRESENT) {
+ u16 vid = vlanflags & NV_RX3_VLAN_TAG_MASK;
+
+ __vlan_hwaccel_put_tag(skb, vid);
+ }
+ napi_gro_receive(&np->napi, skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
} else {
@@ -2952,11 +2951,11 @@ static void nv_set_multicast(struct net_device *dev)
struct netdev_hw_addr *ha;
netdev_for_each_mc_addr(ha, dev) {
- unsigned char *addr = ha->addr;
+ unsigned char *hw_addr = ha->addr;
u32 a, b;
- a = le32_to_cpu(*(__le32 *) addr);
- b = le16_to_cpu(*(__le16 *) (&addr[4]));
+ a = le32_to_cpu(*(__le32 *) hw_addr);
+ b = le16_to_cpu(*(__le16 *) (&hw_addr[4]));
alwaysOn[0] &= a;
alwaysOff[0] &= ~a;
alwaysOn[1] &= b;
@@ -3388,7 +3387,8 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data)
for (i = 0;; i++) {
events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_TX_ALL;
- writel(NVREG_IRQ_TX_ALL, base + NvRegMSIXIrqStatus);
+ writel(events, base + NvRegMSIXIrqStatus);
+ netdev_dbg(dev, "tx irq events: %08x\n", events);
if (!(events & np->irqmask))
break;
@@ -3499,7 +3499,8 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
for (i = 0;; i++) {
events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_RX_ALL;
- writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus);
+ writel(events, base + NvRegMSIXIrqStatus);
+ netdev_dbg(dev, "rx irq events: %08x\n", events);
if (!(events & np->irqmask))
break;
@@ -3543,7 +3544,8 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data)
for (i = 0;; i++) {
events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_OTHER;
- writel(NVREG_IRQ_OTHER, base + NvRegMSIXIrqStatus);
+ writel(events, base + NvRegMSIXIrqStatus);
+ netdev_dbg(dev, "irq events: %08x\n", events);
if (!(events & np->irqmask))
break;
@@ -3607,10 +3609,10 @@ static irqreturn_t nv_nic_irq_test(int foo, void *data)
if (!(np->msi_flags & NV_MSI_X_ENABLED)) {
events = readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK;
- writel(NVREG_IRQ_TIMER, base + NvRegIrqStatus);
+ writel(events & NVREG_IRQ_TIMER, base + NvRegIrqStatus);
} else {
events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQSTAT_MASK;
- writel(NVREG_IRQ_TIMER, base + NvRegMSIXIrqStatus);
+ writel(events & NVREG_IRQ_TIMER, base + NvRegMSIXIrqStatus);
}
pci_push(base);
if (!(events & NVREG_IRQ_TIMER))
@@ -4274,13 +4276,9 @@ static void nv_get_ringparam(struct net_device *dev, struct ethtool_ringparam* r
struct fe_priv *np = netdev_priv(dev);
ring->rx_max_pending = (np->desc_ver == DESC_VER_1) ? RING_MAX_DESC_VER_1 : RING_MAX_DESC_VER_2_3;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->tx_max_pending = (np->desc_ver == DESC_VER_1) ? RING_MAX_DESC_VER_1 : RING_MAX_DESC_VER_2_3;
ring->rx_pending = np->rx_ring_size;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
ring->tx_pending = np->tx_ring_size;
}
@@ -4484,6 +4482,27 @@ static u32 nv_fix_features(struct net_device *dev, u32 features)
return features;
}
+static void nv_vlan_mode(struct net_device *dev, u32 features)
+{
+ struct fe_priv *np = get_nvpriv(dev);
+
+ spin_lock_irq(&np->lock);
+
+ if (features & NETIF_F_HW_VLAN_RX)
+ np->txrxctl_bits |= NVREG_TXRXCTL_VLANSTRIP;
+ else
+ np->txrxctl_bits &= ~NVREG_TXRXCTL_VLANSTRIP;
+
+ if (features & NETIF_F_HW_VLAN_TX)
+ np->txrxctl_bits |= NVREG_TXRXCTL_VLANINS;
+ else
+ np->txrxctl_bits &= ~NVREG_TXRXCTL_VLANINS;
+
+ writel(np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
+
+ spin_unlock_irq(&np->lock);
+}
+
static int nv_set_features(struct net_device *dev, u32 features)
{
struct fe_priv *np = netdev_priv(dev);
@@ -4504,6 +4523,9 @@ static int nv_set_features(struct net_device *dev, u32 features)
spin_unlock_irq(&np->lock);
}
+ if (changed & (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX))
+ nv_vlan_mode(dev, features);
+
return 0;
}
@@ -4536,7 +4558,7 @@ static void nv_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *e
struct fe_priv *np = netdev_priv(dev);
/* update stats */
- nv_do_stats_poll((unsigned long)dev);
+ nv_get_hw_stats(dev);
memcpy(buffer, &np->estats, nv_get_sset_count(dev, ETH_SS_STATS)*sizeof(u64));
}
@@ -4879,29 +4901,6 @@ static const struct ethtool_ops ops = {
.self_test = nv_self_test,
};
-static void nv_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
-{
- struct fe_priv *np = get_nvpriv(dev);
-
- spin_lock_irq(&np->lock);
-
- /* save vlan group */
- np->vlangrp = grp;
-
- if (grp) {
- /* enable vlan on MAC */
- np->txrxctl_bits |= NVREG_TXRXCTL_VLANSTRIP | NVREG_TXRXCTL_VLANINS;
- } else {
- /* disable vlan on MAC */
- np->txrxctl_bits &= ~NVREG_TXRXCTL_VLANSTRIP;
- np->txrxctl_bits &= ~NVREG_TXRXCTL_VLANINS;
- }
-
- writel(np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
-
- spin_unlock_irq(&np->lock);
-}
-
/* The mgmt unit and driver use a semaphore to access the phy during init */
static int nv_mgmt_acquire_sema(struct net_device *dev)
{
@@ -5207,8 +5206,7 @@ static const struct net_device_ops nv_netdev_ops = {
.ndo_set_features = nv_set_features,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = nv_set_mac_address,
- .ndo_set_multicast_list = nv_set_multicast,
- .ndo_vlan_rx_register = nv_vlan_rx_register,
+ .ndo_set_rx_mode = nv_set_multicast,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = nv_poll_controller,
#endif
@@ -5225,8 +5223,7 @@ static const struct net_device_ops nv_netdev_ops_optimized = {
.ndo_set_features = nv_set_features,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = nv_set_mac_address,
- .ndo_set_multicast_list = nv_set_multicast,
- .ndo_vlan_rx_register = nv_vlan_rx_register,
+ .ndo_set_rx_mode = nv_set_multicast,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = nv_poll_controller,
#endif
@@ -5339,15 +5336,16 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
np->txrxctl_bits |= NVREG_TXRXCTL_RXCHECK;
dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_SG |
NETIF_F_TSO | NETIF_F_RXCSUM;
- dev->features |= dev->hw_features;
}
np->vlanctl_bits = 0;
if (id->driver_data & DEV_HAS_VLAN) {
np->vlanctl_bits = NVREG_VLANCONTROL_ENABLE;
- dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
+ dev->hw_features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
}
+ dev->features |= dev->hw_features;
+
np->pause_flags = NV_PAUSEFRAME_RX_CAPABLE | NV_PAUSEFRAME_RX_REQ | NV_PAUSEFRAME_AUTONEG;
if ((id->driver_data & DEV_HAS_PAUSEFRAME_TX_V1) ||
(id->driver_data & DEV_HAS_PAUSEFRAME_TX_V2) ||
@@ -5615,6 +5613,9 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
goto out_error;
}
+ if (id->driver_data & DEV_HAS_VLAN)
+ nv_vlan_mode(dev, dev->features);
+
netif_carrier_off(dev);
dev_info(&pci_dev->dev, "ifname %s, PHY OUI 0x%x @ %d, addr %pM\n",
diff --git a/drivers/net/octeon/Kconfig b/drivers/net/ethernet/octeon/Kconfig
index 1e56bbf3f5c0..3de52ffd2872 100644
--- a/drivers/net/octeon/Kconfig
+++ b/drivers/net/ethernet/octeon/Kconfig
@@ -1,10 +1,14 @@
+#
+# Cavium network device configuration
+#
+
config OCTEON_MGMT_ETHERNET
tristate "Octeon Management port ethernet driver (CN5XXX, CN6XXX)"
depends on CPU_CAVIUM_OCTEON
select PHYLIB
select MDIO_OCTEON
default y
- help
+ ---help---
This option enables the ethernet driver for the management
port on Cavium Networks' Octeon CN57XX, CN56XX, CN55XX,
CN54XX, CN52XX, and CN6XXX chips.
diff --git a/drivers/net/ethernet/octeon/Makefile b/drivers/net/ethernet/octeon/Makefile
new file mode 100644
index 000000000000..efa41c1d91c5
--- /dev/null
+++ b/drivers/net/ethernet/octeon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Cavium network device drivers.
+#
+
+obj-$(CONFIG_OCTEON_MGMT_ETHERNET) += octeon_mgmt.o
diff --git a/drivers/net/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c
index b264f0f45605..212f43b308a3 100644
--- a/drivers/net/octeon/octeon_mgmt.c
+++ b/drivers/net/ethernet/octeon/octeon_mgmt.c
@@ -9,9 +9,12 @@
#include <linux/capability.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/slab.h>
#include <linux/phy.h>
@@ -1058,7 +1061,6 @@ static const struct net_device_ops octeon_mgmt_ops = {
.ndo_stop = octeon_mgmt_stop,
.ndo_start_xmit = octeon_mgmt_xmit,
.ndo_set_rx_mode = octeon_mgmt_set_rx_filtering,
- .ndo_set_multicast_list = octeon_mgmt_set_rx_filtering,
.ndo_set_mac_address = octeon_mgmt_set_mac_address,
.ndo_do_ioctl = octeon_mgmt_ioctl,
.ndo_change_mtu = octeon_mgmt_change_mtu,
@@ -1101,6 +1103,8 @@ static int __devinit octeon_mgmt_probe(struct platform_device *pdev)
tasklet_init(&p->tx_clean_tasklet,
octeon_mgmt_clean_tx_tasklet, (unsigned long)p);
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
netdev->netdev_ops = &octeon_mgmt_ops;
netdev->ethtool_ops = &octeon_mgmt_ethtool_ops;
diff --git a/drivers/net/ethernet/oki-semi/Kconfig b/drivers/net/ethernet/oki-semi/Kconfig
new file mode 100644
index 000000000000..ecd45f9ea9d9
--- /dev/null
+++ b/drivers/net/ethernet/oki-semi/Kconfig
@@ -0,0 +1,23 @@
+#
+# OKI Semiconductor device configuration
+#
+
+config NET_VENDOR_OKI
+ bool "OKI Semiconductor devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about OKI Semiconductor cards. If you say Y, you will
+ be asked for your specific card in the following questions.
+
+if NET_VENDOR_OKI
+
+source "drivers/net/ethernet/oki-semi/pch_gbe/Kconfig"
+
+endif # NET_VENDOR_OKI
diff --git a/drivers/net/ethernet/oki-semi/Makefile b/drivers/net/ethernet/oki-semi/Makefile
new file mode 100644
index 000000000000..b6780c877c19
--- /dev/null
+++ b/drivers/net/ethernet/oki-semi/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the OKI Semiconductor device drivers.
+#
+
+obj-$(CONFIG_PCH_GBE) += pch_gbe/
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig b/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig
new file mode 100644
index 000000000000..00bc4fc968c7
--- /dev/null
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig
@@ -0,0 +1,22 @@
+#
+# OKI Semiconductor device configuration
+#
+
+config PCH_GBE
+ tristate "OKI SEMICONDUCTOR IOH(ML7223/ML7831) GbE"
+ depends on PCI
+ select NET_CORE
+ select MII
+ ---help---
+ This is a gigabit ethernet driver for EG20T PCH.
+ EG20T PCH is the platform controller hub that is used in Intel's
+ general embedded platform. EG20T PCH has Gigabit Ethernet interface.
+ Using this interface, it is able to access system devices connected
+ to Gigabit Ethernet. This driver enables Gigabit Ethernet function.
+
+ This driver also can be used for OKI SEMICONDUCTOR IOH(Input/
+ Output Hub), ML7223/ML7831.
+ ML7223 IOH is for MP(Media Phone) use. ML7831 IOH is for general
+ purpose use.
+ ML7223/ML7831 is companion chip for Intel Atom E6xx series.
+ ML7223/ML7831 is completely compatible for Intel EG20T PCH.
diff --git a/drivers/net/pch_gbe/Makefile b/drivers/net/ethernet/oki-semi/pch_gbe/Makefile
index 31288d4ad248..31288d4ad248 100644
--- a/drivers/net/pch_gbe/Makefile
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/Makefile
diff --git a/drivers/net/pch_gbe/pch_gbe.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h
index 59fac77d0dbb..a09a07197eb5 100644
--- a/drivers/net/pch_gbe/pch_gbe.h
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h
@@ -127,8 +127,8 @@ struct pch_gbe_regs {
/* Reset */
#define PCH_GBE_ALL_RST 0x80000000 /* All reset */
-#define PCH_GBE_TX_RST 0x40000000 /* TX MAC, TX FIFO, TX DMA reset */
-#define PCH_GBE_RX_RST 0x04000000 /* RX MAC, RX FIFO, RX DMA reset */
+#define PCH_GBE_TX_RST 0x00008000 /* TX MAC, TX FIFO, TX DMA reset */
+#define PCH_GBE_RX_RST 0x00004000 /* RX MAC, RX FIFO, RX DMA reset */
/* TCP/IP Accelerator Control */
#define PCH_GBE_EX_LIST_EN 0x00000008 /* External List Enable */
@@ -276,6 +276,9 @@ struct pch_gbe_regs {
#define PCH_GBE_RX_DMA_EN 0x00000002 /* Enables Receive DMA */
#define PCH_GBE_TX_DMA_EN 0x00000001 /* Enables Transmission DMA */
+/* RX DMA STATUS */
+#define PCH_GBE_IDLE_CHECK 0xFFFFFFFE
+
/* Wake On LAN Status */
#define PCH_GBE_WLS_BR 0x00000008 /* Broadcas Address */
#define PCH_GBE_WLS_MLT 0x00000004 /* Multicast Address */
@@ -471,6 +474,7 @@ struct pch_gbe_tx_desc {
struct pch_gbe_buffer {
struct sk_buff *skb;
dma_addr_t dma;
+ unsigned char *rx_buffer;
unsigned long time_stamp;
u16 length;
bool mapped;
@@ -511,6 +515,9 @@ struct pch_gbe_tx_ring {
struct pch_gbe_rx_ring {
struct pch_gbe_rx_desc *desc;
dma_addr_t dma;
+ unsigned char *rx_buff_pool;
+ dma_addr_t rx_buff_pool_logic;
+ unsigned int rx_buff_pool_size;
unsigned int size;
unsigned int count;
unsigned int next_to_use;
@@ -622,6 +629,7 @@ struct pch_gbe_adapter {
unsigned long rx_buffer_len;
unsigned long tx_queue_len;
bool have_msi;
+ bool rx_stop_flag;
};
extern const char pch_driver_version[];
diff --git a/drivers/net/pch_gbe/pch_gbe_api.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c
index e48f084ad226..e48f084ad226 100644
--- a/drivers/net/pch_gbe/pch_gbe_api.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c
diff --git a/drivers/net/pch_gbe/pch_gbe_api.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.h
index 94aaac5b057b..94aaac5b057b 100644
--- a/drivers/net/pch_gbe/pch_gbe_api.h
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.h
diff --git a/drivers/net/pch_gbe/pch_gbe_ethtool.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c
index ea2d8e41887a..8c8027176bef 100644
--- a/drivers/net/pch_gbe/pch_gbe_ethtool.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c
@@ -273,12 +273,8 @@ static void pch_gbe_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = PCH_GBE_MAX_RXD;
ring->tx_max_pending = PCH_GBE_MAX_TXD;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = rxdr->count;
ring->tx_pending = txdr->count;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
/**
diff --git a/drivers/net/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
index eac3c5ca9731..48406ca382f1 100644
--- a/drivers/net/pch_gbe/pch_gbe_main.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
@@ -20,7 +20,7 @@
#include "pch_gbe.h"
#include "pch_gbe_api.h"
-#include <linux/prefetch.h>
+#include <linux/module.h>
#define DRV_VERSION "1.00"
const char pch_driver_version[] = DRV_VERSION;
@@ -34,11 +34,15 @@ const char pch_driver_version[] = DRV_VERSION;
#define PCH_GBE_WATCHDOG_PERIOD (1 * HZ) /* watchdog time */
#define PCH_GBE_COPYBREAK_DEFAULT 256
#define PCH_GBE_PCI_BAR 1
+#define PCH_GBE_RESERVE_MEMORY 0x200000 /* 2MB */
/* Macros for ML7223 */
#define PCI_VENDOR_ID_ROHM 0x10db
#define PCI_DEVICE_ID_ROHM_ML7223_GBE 0x8013
+/* Macros for ML7831 */
+#define PCI_DEVICE_ID_ROHM_ML7831_GBE 0x8802
+
#define PCH_GBE_TX_WEIGHT 64
#define PCH_GBE_RX_WEIGHT 64
#define PCH_GBE_RX_BUFFER_WRITE 16
@@ -52,6 +56,7 @@ const char pch_driver_version[] = DRV_VERSION;
)
/* Ethertype field values */
+#define PCH_GBE_MAX_RX_BUFFER_SIZE 0x2880
#define PCH_GBE_MAX_JUMBO_FRAME_SIZE 10318
#define PCH_GBE_FRAME_SIZE_2048 2048
#define PCH_GBE_FRAME_SIZE_4096 4096
@@ -83,10 +88,12 @@ const char pch_driver_version[] = DRV_VERSION;
#define PCH_GBE_INT_ENABLE_MASK ( \
PCH_GBE_INT_RX_DMA_CMPLT | \
PCH_GBE_INT_RX_DSC_EMP | \
+ PCH_GBE_INT_RX_FIFO_ERR | \
PCH_GBE_INT_WOL_DET | \
PCH_GBE_INT_TX_CMPLT \
)
+#define PCH_GBE_INT_DISABLE_ALL 0
static unsigned int copybreak __read_mostly = PCH_GBE_COPYBREAK_DEFAULT;
@@ -138,6 +145,27 @@ static void pch_gbe_wait_clr_bit(void *reg, u32 bit)
if (!tmp)
pr_err("Error: busy bit is not cleared\n");
}
+
+/**
+ * pch_gbe_wait_clr_bit_irq - Wait to clear a bit for interrupt context
+ * @reg: Pointer of register
+ * @busy: Busy bit
+ */
+static int pch_gbe_wait_clr_bit_irq(void *reg, u32 bit)
+{
+ u32 tmp;
+ int ret = -1;
+ /* wait busy */
+ tmp = 20;
+ while ((ioread32(reg) & bit) && --tmp)
+ udelay(5);
+ if (!tmp)
+ pr_err("Error: busy bit is not cleared\n");
+ else
+ ret = 0;
+ return ret;
+}
+
/**
* pch_gbe_mac_mar_set - Set MAC address register
* @hw: Pointer to the HW structure
@@ -189,6 +217,17 @@ static void pch_gbe_mac_reset_hw(struct pch_gbe_hw *hw)
return;
}
+static void pch_gbe_mac_reset_rx(struct pch_gbe_hw *hw)
+{
+ /* Read the MAC address. and store to the private data */
+ pch_gbe_mac_read_mac_addr(hw);
+ iowrite32(PCH_GBE_RX_RST, &hw->reg->RESET);
+ pch_gbe_wait_clr_bit_irq(&hw->reg->RESET, PCH_GBE_RX_RST);
+ /* Setup the MAC address */
+ pch_gbe_mac_mar_set(hw, hw->mac.addr, 0);
+ return;
+}
+
/**
* pch_gbe_mac_init_rx_addrs - Initialize receive address's
* @hw: Pointer to the HW structure
@@ -660,7 +699,6 @@ static void pch_gbe_configure_tx(struct pch_gbe_adapter *adapter)
*/
static void pch_gbe_setup_rctl(struct pch_gbe_adapter *adapter)
{
- struct net_device *netdev = adapter->netdev;
struct pch_gbe_hw *hw = &adapter->hw;
u32 rx_mode, tcpip;
@@ -671,13 +709,8 @@ static void pch_gbe_setup_rctl(struct pch_gbe_adapter *adapter)
tcpip = ioread32(&hw->reg->TCPIP_ACC);
- if (netdev->features & NETIF_F_RXCSUM) {
- tcpip &= ~PCH_GBE_RX_TCPIPACC_OFF;
- tcpip |= PCH_GBE_RX_TCPIPACC_EN;
- } else {
- tcpip |= PCH_GBE_RX_TCPIPACC_OFF;
- tcpip &= ~PCH_GBE_RX_TCPIPACC_EN;
- }
+ tcpip |= PCH_GBE_RX_TCPIPACC_OFF;
+ tcpip &= ~PCH_GBE_RX_TCPIPACC_EN;
iowrite32(tcpip, &hw->reg->TCPIP_ACC);
return;
}
@@ -717,13 +750,6 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter)
iowrite32(rdba, &hw->reg->RX_DSC_BASE);
iowrite32(rdlen, &hw->reg->RX_DSC_SIZE);
iowrite32((rdba + rdlen), &hw->reg->RX_DSC_SW_P);
-
- /* Enables Receive DMA */
- rxdma = ioread32(&hw->reg->DMA_CTRL);
- rxdma |= PCH_GBE_RX_DMA_EN;
- iowrite32(rxdma, &hw->reg->DMA_CTRL);
- /* Enables Receive */
- iowrite32(PCH_GBE_MRE_MAC_RX_EN, &hw->reg->MAC_RX_EN);
}
/**
@@ -1097,6 +1123,48 @@ void pch_gbe_update_stats(struct pch_gbe_adapter *adapter)
spin_unlock_irqrestore(&adapter->stats_lock, flags);
}
+static void pch_gbe_stop_receive(struct pch_gbe_adapter *adapter)
+{
+ struct pch_gbe_hw *hw = &adapter->hw;
+ u32 rxdma;
+ u16 value;
+ int ret;
+
+ /* Disable Receive DMA */
+ rxdma = ioread32(&hw->reg->DMA_CTRL);
+ rxdma &= ~PCH_GBE_RX_DMA_EN;
+ iowrite32(rxdma, &hw->reg->DMA_CTRL);
+ /* Wait Rx DMA BUS is IDLE */
+ ret = pch_gbe_wait_clr_bit_irq(&hw->reg->RX_DMA_ST, PCH_GBE_IDLE_CHECK);
+ if (ret) {
+ /* Disable Bus master */
+ pci_read_config_word(adapter->pdev, PCI_COMMAND, &value);
+ value &= ~PCI_COMMAND_MASTER;
+ pci_write_config_word(adapter->pdev, PCI_COMMAND, value);
+ /* Stop Receive */
+ pch_gbe_mac_reset_rx(hw);
+ /* Enable Bus master */
+ value |= PCI_COMMAND_MASTER;
+ pci_write_config_word(adapter->pdev, PCI_COMMAND, value);
+ } else {
+ /* Stop Receive */
+ pch_gbe_mac_reset_rx(hw);
+ }
+}
+
+static void pch_gbe_start_receive(struct pch_gbe_hw *hw)
+{
+ u32 rxdma;
+
+ /* Enables Receive DMA */
+ rxdma = ioread32(&hw->reg->DMA_CTRL);
+ rxdma |= PCH_GBE_RX_DMA_EN;
+ iowrite32(rxdma, &hw->reg->DMA_CTRL);
+ /* Enables Receive */
+ iowrite32(PCH_GBE_MRE_MAC_RX_EN, &hw->reg->MAC_RX_EN);
+ return;
+}
+
/**
* pch_gbe_intr - Interrupt Handler
* @irq: Interrupt number
@@ -1123,7 +1191,17 @@ static irqreturn_t pch_gbe_intr(int irq, void *data)
if (int_st & PCH_GBE_INT_RX_FRAME_ERR)
adapter->stats.intr_rx_frame_err_count++;
if (int_st & PCH_GBE_INT_RX_FIFO_ERR)
- adapter->stats.intr_rx_fifo_err_count++;
+ if (!adapter->rx_stop_flag) {
+ adapter->stats.intr_rx_fifo_err_count++;
+ pr_debug("Rx fifo over run\n");
+ adapter->rx_stop_flag = true;
+ int_en = ioread32(&hw->reg->INT_EN);
+ iowrite32((int_en & ~PCH_GBE_INT_RX_FIFO_ERR),
+ &hw->reg->INT_EN);
+ pch_gbe_stop_receive(adapter);
+ int_st |= ioread32(&hw->reg->INT_ST);
+ int_st = int_st & ioread32(&hw->reg->INT_EN);
+ }
if (int_st & PCH_GBE_INT_RX_DMA_ERR)
adapter->stats.intr_rx_dma_err_count++;
if (int_st & PCH_GBE_INT_TX_FIFO_ERR)
@@ -1135,21 +1213,18 @@ static irqreturn_t pch_gbe_intr(int irq, void *data)
/* When Rx descriptor is empty */
if ((int_st & PCH_GBE_INT_RX_DSC_EMP)) {
adapter->stats.intr_rx_dsc_empty_count++;
- pr_err("Rx descriptor is empty\n");
+ pr_debug("Rx descriptor is empty\n");
int_en = ioread32(&hw->reg->INT_EN);
iowrite32((int_en & ~PCH_GBE_INT_RX_DSC_EMP), &hw->reg->INT_EN);
if (hw->mac.tx_fc_enable) {
/* Set Pause packet */
pch_gbe_mac_set_pause_packet(hw);
}
- if ((int_en & (PCH_GBE_INT_RX_DMA_CMPLT | PCH_GBE_INT_TX_CMPLT))
- == 0) {
- return IRQ_HANDLED;
- }
}
/* When request status is Receive interruption */
- if ((int_st & (PCH_GBE_INT_RX_DMA_CMPLT | PCH_GBE_INT_TX_CMPLT))) {
+ if ((int_st & (PCH_GBE_INT_RX_DMA_CMPLT | PCH_GBE_INT_TX_CMPLT)) ||
+ (adapter->rx_stop_flag == true)) {
if (likely(napi_schedule_prep(&adapter->napi))) {
/* Enable only Rx Descriptor empty */
atomic_inc(&adapter->irq_sem);
@@ -1185,29 +1260,23 @@ pch_gbe_alloc_rx_buffers(struct pch_gbe_adapter *adapter,
unsigned int i;
unsigned int bufsz;
- bufsz = adapter->rx_buffer_len + PCH_GBE_DMA_ALIGN;
+ bufsz = adapter->rx_buffer_len + NET_IP_ALIGN;
i = rx_ring->next_to_use;
while ((cleaned_count--)) {
buffer_info = &rx_ring->buffer_info[i];
- skb = buffer_info->skb;
- if (skb) {
- skb_trim(skb, 0);
- } else {
- skb = netdev_alloc_skb(netdev, bufsz);
- if (unlikely(!skb)) {
- /* Better luck next round */
- adapter->stats.rx_alloc_buff_failed++;
- break;
- }
- /* 64byte align */
- skb_reserve(skb, PCH_GBE_DMA_ALIGN);
-
- buffer_info->skb = skb;
- buffer_info->length = adapter->rx_buffer_len;
+ skb = netdev_alloc_skb(netdev, bufsz);
+ if (unlikely(!skb)) {
+ /* Better luck next round */
+ adapter->stats.rx_alloc_buff_failed++;
+ break;
}
+ /* align */
+ skb_reserve(skb, NET_IP_ALIGN);
+ buffer_info->skb = skb;
+
buffer_info->dma = dma_map_single(&pdev->dev,
- skb->data,
+ buffer_info->rx_buffer,
buffer_info->length,
DMA_FROM_DEVICE);
if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) {
@@ -1240,6 +1309,36 @@ pch_gbe_alloc_rx_buffers(struct pch_gbe_adapter *adapter,
return;
}
+static int
+pch_gbe_alloc_rx_buffers_pool(struct pch_gbe_adapter *adapter,
+ struct pch_gbe_rx_ring *rx_ring, int cleaned_count)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ struct pch_gbe_buffer *buffer_info;
+ unsigned int i;
+ unsigned int bufsz;
+ unsigned int size;
+
+ bufsz = adapter->rx_buffer_len;
+
+ size = rx_ring->count * bufsz + PCH_GBE_RESERVE_MEMORY;
+ rx_ring->rx_buff_pool = dma_alloc_coherent(&pdev->dev, size,
+ &rx_ring->rx_buff_pool_logic,
+ GFP_KERNEL);
+ if (!rx_ring->rx_buff_pool) {
+ pr_err("Unable to allocate memory for the receive poll buffer\n");
+ return -ENOMEM;
+ }
+ memset(rx_ring->rx_buff_pool, 0, size);
+ rx_ring->rx_buff_pool_size = size;
+ for (i = 0; i < rx_ring->count; i++) {
+ buffer_info = &rx_ring->buffer_info[i];
+ buffer_info->rx_buffer = rx_ring->rx_buff_pool + bufsz * i;
+ buffer_info->length = bufsz;
+ }
+ return 0;
+}
+
/**
* pch_gbe_alloc_tx_buffers - Allocate transmit buffers
* @adapter: Board private structure
@@ -1285,7 +1384,7 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
struct sk_buff *skb;
unsigned int i;
unsigned int cleaned_count = 0;
- bool cleaned = false;
+ bool cleaned = true;
pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean);
@@ -1296,7 +1395,6 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
while ((tx_desc->gbec_status & DSC_INIT16) == 0x0000) {
pr_debug("gbec_status:0x%04x\n", tx_desc->gbec_status);
- cleaned = true;
buffer_info = &tx_ring->buffer_info[i];
skb = buffer_info->skb;
@@ -1339,8 +1437,10 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
tx_desc = PCH_GBE_TX_DESC(*tx_ring, i);
/* weight of a sort for tx, to avoid endless transmit cleanup */
- if (cleaned_count++ == PCH_GBE_TX_WEIGHT)
+ if (cleaned_count++ == PCH_GBE_TX_WEIGHT) {
+ cleaned = false;
break;
+ }
}
pr_debug("called pch_gbe_unmap_and_free_tx_resource() %d count\n",
cleaned_count);
@@ -1380,7 +1480,7 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter,
unsigned int i;
unsigned int cleaned_count = 0;
bool cleaned = false;
- struct sk_buff *skb, *new_skb;
+ struct sk_buff *skb;
u8 dma_status;
u16 gbec_status;
u32 tcp_ip_status;
@@ -1401,13 +1501,12 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter,
rx_desc->gbec_status = DSC_INIT16;
buffer_info = &rx_ring->buffer_info[i];
skb = buffer_info->skb;
+ buffer_info->skb = NULL;
/* unmap dma */
dma_unmap_single(&pdev->dev, buffer_info->dma,
buffer_info->length, DMA_FROM_DEVICE);
buffer_info->mapped = false;
- /* Prefetch the packet */
- prefetch(skb->data);
pr_debug("RxDecNo = 0x%04x Status[DMA:0x%02x GBE:0x%04x "
"TCP:0x%08x] BufInf = 0x%p\n",
@@ -1427,70 +1526,16 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter,
pr_err("Receive CRC Error\n");
} else {
/* get receive length */
- /* length convert[-3] */
- length = (rx_desc->rx_words_eob) - 3;
-
- /* Decide the data conversion method */
- if (!(netdev->features & NETIF_F_RXCSUM)) {
- /* [Header:14][payload] */
- if (NET_IP_ALIGN) {
- /* Because alignment differs,
- * the new_skb is newly allocated,
- * and data is copied to new_skb.*/
- new_skb = netdev_alloc_skb(netdev,
- length + NET_IP_ALIGN);
- if (!new_skb) {
- /* dorrop error */
- pr_err("New skb allocation "
- "Error\n");
- goto dorrop;
- }
- skb_reserve(new_skb, NET_IP_ALIGN);
- memcpy(new_skb->data, skb->data,
- length);
- skb = new_skb;
- } else {
- /* DMA buffer is used as SKB as it is.*/
- buffer_info->skb = NULL;
- }
- } else {
- /* [Header:14][padding:2][payload] */
- /* The length includes padding length */
- length = length - PCH_GBE_DMA_PADDING;
- if ((length < copybreak) ||
- (NET_IP_ALIGN != PCH_GBE_DMA_PADDING)) {
- /* Because alignment differs,
- * the new_skb is newly allocated,
- * and data is copied to new_skb.
- * Padding data is deleted
- * at the time of a copy.*/
- new_skb = netdev_alloc_skb(netdev,
- length + NET_IP_ALIGN);
- if (!new_skb) {
- /* dorrop error */
- pr_err("New skb allocation "
- "Error\n");
- goto dorrop;
- }
- skb_reserve(new_skb, NET_IP_ALIGN);
- memcpy(new_skb->data, skb->data,
- ETH_HLEN);
- memcpy(&new_skb->data[ETH_HLEN],
- &skb->data[ETH_HLEN +
- PCH_GBE_DMA_PADDING],
- length - ETH_HLEN);
- skb = new_skb;
- } else {
- /* Padding data is deleted
- * by moving header data.*/
- memmove(&skb->data[PCH_GBE_DMA_PADDING],
- &skb->data[0], ETH_HLEN);
- skb_reserve(skb, NET_IP_ALIGN);
- buffer_info->skb = NULL;
- }
- }
- /* The length includes FCS length */
- length = length - ETH_FCS_LEN;
+ /* length convert[-3], length includes FCS length */
+ length = (rx_desc->rx_words_eob) - 3 - ETH_FCS_LEN;
+ if (rx_desc->rx_words_eob & 0x02)
+ length = length - 4;
+ /*
+ * buffer_info->rx_buffer: [Header:14][payload]
+ * skb->data: [Reserve:2][Header:14][payload]
+ */
+ memcpy(skb->data, buffer_info->rx_buffer, length);
+
/* update status of driver */
adapter->stats.rx_bytes += length;
adapter->stats.rx_packets++;
@@ -1509,7 +1554,6 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter,
pr_debug("Receive skb->ip_summed: %d length: %d\n",
skb->ip_summed, length);
}
-dorrop:
/* return some buffers to hardware, one at a time is too slow */
if (unlikely(cleaned_count >= PCH_GBE_RX_BUFFER_WRITE)) {
pch_gbe_alloc_rx_buffers(adapter, rx_ring,
@@ -1714,9 +1758,15 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter)
pr_err("Error: can't bring device up\n");
return err;
}
+ err = pch_gbe_alloc_rx_buffers_pool(adapter, rx_ring, rx_ring->count);
+ if (err) {
+ pr_err("Error: can't bring device up\n");
+ return err;
+ }
pch_gbe_alloc_tx_buffers(adapter, tx_ring);
pch_gbe_alloc_rx_buffers(adapter, rx_ring, rx_ring->count);
adapter->tx_queue_len = netdev->tx_queue_len;
+ pch_gbe_start_receive(&adapter->hw);
mod_timer(&adapter->watchdog_timer, jiffies);
@@ -1734,6 +1784,7 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter)
void pch_gbe_down(struct pch_gbe_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
+ struct pch_gbe_rx_ring *rx_ring = adapter->rx_ring;
/* signal that we're down so the interrupt handler does not
* reschedule our watchdog timer */
@@ -1752,6 +1803,12 @@ void pch_gbe_down(struct pch_gbe_adapter *adapter)
pch_gbe_reset(adapter);
pch_gbe_clean_tx_ring(adapter, adapter->tx_ring);
pch_gbe_clean_rx_ring(adapter, adapter->rx_ring);
+
+ pci_free_consistent(adapter->pdev, rx_ring->rx_buff_pool_size,
+ rx_ring->rx_buff_pool, rx_ring->rx_buff_pool_logic);
+ rx_ring->rx_buff_pool_logic = 0;
+ rx_ring->rx_buff_pool_size = 0;
+ rx_ring->rx_buff_pool = NULL;
}
/**
@@ -2004,6 +2061,8 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu)
{
struct pch_gbe_adapter *adapter = netdev_priv(netdev);
int max_frame;
+ unsigned long old_rx_buffer_len = adapter->rx_buffer_len;
+ int err;
max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) ||
@@ -2018,14 +2077,24 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu)
else if (max_frame <= PCH_GBE_FRAME_SIZE_8192)
adapter->rx_buffer_len = PCH_GBE_FRAME_SIZE_8192;
else
- adapter->rx_buffer_len = PCH_GBE_MAX_JUMBO_FRAME_SIZE;
- netdev->mtu = new_mtu;
- adapter->hw.mac.max_frame_size = max_frame;
+ adapter->rx_buffer_len = PCH_GBE_MAX_RX_BUFFER_SIZE;
- if (netif_running(netdev))
- pch_gbe_reinit_locked(adapter);
- else
+ if (netif_running(netdev)) {
+ pch_gbe_down(adapter);
+ err = pch_gbe_up(adapter);
+ if (err) {
+ adapter->rx_buffer_len = old_rx_buffer_len;
+ pch_gbe_up(adapter);
+ return -ENOMEM;
+ } else {
+ netdev->mtu = new_mtu;
+ adapter->hw.mac.max_frame_size = max_frame;
+ }
+ } else {
pch_gbe_reset(adapter);
+ netdev->mtu = new_mtu;
+ adapter->hw.mac.max_frame_size = max_frame;
+ }
pr_debug("max_frame : %d rx_buffer_len : %d mtu : %d max_frame_size : %d\n",
max_frame, (u32) adapter->rx_buffer_len, netdev->mtu,
@@ -2099,33 +2168,39 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget)
{
struct pch_gbe_adapter *adapter =
container_of(napi, struct pch_gbe_adapter, napi);
- struct net_device *netdev = adapter->netdev;
int work_done = 0;
bool poll_end_flag = false;
bool cleaned = false;
+ u32 int_en;
pr_debug("budget : %d\n", budget);
- /* Keep link state information with original netdev */
- if (!netif_carrier_ok(netdev)) {
- poll_end_flag = true;
- } else {
- cleaned = pch_gbe_clean_tx(adapter, adapter->tx_ring);
- pch_gbe_clean_rx(adapter, adapter->rx_ring, &work_done, budget);
+ pch_gbe_clean_rx(adapter, adapter->rx_ring, &work_done, budget);
+ cleaned = pch_gbe_clean_tx(adapter, adapter->tx_ring);
- if (cleaned)
- work_done = budget;
- /* If no Tx and not enough Rx work done,
- * exit the polling mode
- */
- if ((work_done < budget) || !netif_running(netdev))
- poll_end_flag = true;
- }
+ if (!cleaned)
+ work_done = budget;
+ /* If no Tx and not enough Rx work done,
+ * exit the polling mode
+ */
+ if (work_done < budget)
+ poll_end_flag = true;
if (poll_end_flag) {
napi_complete(napi);
+ if (adapter->rx_stop_flag) {
+ adapter->rx_stop_flag = false;
+ pch_gbe_start_receive(&adapter->hw);
+ }
pch_gbe_irq_enable(adapter);
- }
+ } else
+ if (adapter->rx_stop_flag) {
+ adapter->rx_stop_flag = false;
+ pch_gbe_start_receive(&adapter->hw);
+ int_en = ioread32(&adapter->hw.reg->INT_EN);
+ iowrite32((int_en | PCH_GBE_INT_RX_FIFO_ERR),
+ &adapter->hw.reg->INT_EN);
+ }
pr_debug("poll_end_flag : %d work_done : %d budget : %d\n",
poll_end_flag, work_done, budget);
@@ -2158,7 +2233,7 @@ static const struct net_device_ops pch_gbe_netdev_ops = {
.ndo_change_mtu = pch_gbe_change_mtu,
.ndo_set_features = pch_gbe_set_features,
.ndo_do_ioctl = pch_gbe_ioctl,
- .ndo_set_multicast_list = &pch_gbe_set_multi,
+ .ndo_set_rx_mode = pch_gbe_set_multi,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = pch_gbe_netpoll,
#endif
@@ -2452,6 +2527,13 @@ static DEFINE_PCI_DEVICE_TABLE(pch_gbe_pcidev_id) = {
.class = (PCI_CLASS_NETWORK_ETHERNET << 8),
.class_mask = (0xFFFF00)
},
+ {.vendor = PCI_VENDOR_ID_ROHM,
+ .device = PCI_DEVICE_ID_ROHM_ML7831_GBE,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = (PCI_CLASS_NETWORK_ETHERNET << 8),
+ .class_mask = (0xFFFF00)
+ },
/* required last entry */
{0}
};
diff --git a/drivers/net/pch_gbe/pch_gbe_param.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c
index 5b5d90a47e29..9cb5f912e489 100644
--- a/drivers/net/pch_gbe/pch_gbe_param.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c
@@ -19,6 +19,7 @@
*/
#include "pch_gbe.h"
+#include <linux/module.h> /* for __MODULE_STRING */
#define OPTION_UNSET -1
#define OPTION_DISABLED 0
diff --git a/drivers/net/pch_gbe/pch_gbe_phy.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c
index 28bb9603d736..28bb9603d736 100644
--- a/drivers/net/pch_gbe/pch_gbe_phy.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c
diff --git a/drivers/net/pch_gbe/pch_gbe_phy.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.h
index 03264dc7b5ec..03264dc7b5ec 100644
--- a/drivers/net/pch_gbe/pch_gbe_phy.h
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.h
diff --git a/drivers/net/ethernet/packetengines/Kconfig b/drivers/net/ethernet/packetengines/Kconfig
new file mode 100644
index 000000000000..b97132d9dff0
--- /dev/null
+++ b/drivers/net/ethernet/packetengines/Kconfig
@@ -0,0 +1,47 @@
+#
+# Packet engine device configuration
+#
+
+config NET_PACKET_ENGINE
+ bool "Packet Engine devices"
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about packet engine devices. If you say Y, you will
+ be asked for your specific card in the following questions.
+
+if NET_PACKET_ENGINE
+
+config HAMACHI
+ tristate "Packet Engines Hamachi GNIC-II support"
+ depends on PCI
+ select NET_CORE
+ select MII
+ ---help---
+ If you have a Gigabit Ethernet card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module will be
+ called hamachi.
+
+config YELLOWFIN
+ tristate "Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ select CRC32
+ ---help---
+ Say Y here if you have a Packet Engines G-NIC PCI Gigabit Ethernet
+ adapter or the SYM53C885 Ethernet controller. The Gigabit adapter is
+ used by the Beowulf Linux cluster project. See
+ <http://cesdis.gsfc.nasa.gov/linux/drivers/yellowfin.html> for more
+ information about this driver in particular and Beowulf in general.
+
+ To compile this driver as a module, choose M here: the module
+ will be called yellowfin. This is recommended.
+
+endif # NET_PACKET_ENGINE
diff --git a/drivers/net/ethernet/packetengines/Makefile b/drivers/net/ethernet/packetengines/Makefile
new file mode 100644
index 000000000000..995ccd077d0c
--- /dev/null
+++ b/drivers/net/ethernet/packetengines/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Packet Engine network device drivers.
+#
+
+obj-$(CONFIG_HAMACHI) += hamachi.o
+obj-$(CONFIG_YELLOWFIN) += yellowfin.o
diff --git a/drivers/net/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c
index a09041aa8509..3458df3780b8 100644
--- a/drivers/net/hamachi.c
+++ b/drivers/net/ethernet/packetengines/hamachi.c
@@ -567,7 +567,7 @@ static const struct net_device_ops hamachi_netdev_ops = {
.ndo_stop = hamachi_close,
.ndo_start_xmit = hamachi_start_xmit,
.ndo_get_stats = hamachi_get_stats,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
@@ -648,13 +648,13 @@ static int __devinit hamachi_init_one (struct pci_dev *pdev,
ring_space = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &ring_dma);
if (!ring_space)
goto err_out_cleardev;
- hmp->tx_ring = (struct hamachi_desc *)ring_space;
+ hmp->tx_ring = ring_space;
hmp->tx_ring_dma = ring_dma;
ring_space = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &ring_dma);
if (!ring_space)
goto err_out_unmap_tx;
- hmp->rx_ring = (struct hamachi_desc *)ring_space;
+ hmp->rx_ring = ring_space;
hmp->rx_ring_dma = ring_dma;
/* Check for options being passed in */
diff --git a/drivers/net/yellowfin.c b/drivers/net/ethernet/packetengines/yellowfin.c
index ec47e22fa186..db44e9af03c3 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/ethernet/packetengines/yellowfin.c
@@ -359,7 +359,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_open = yellowfin_open,
.ndo_stop = yellowfin_close,
.ndo_start_xmit = yellowfin_start_xmit,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
@@ -442,19 +442,19 @@ static int __devinit yellowfin_init_one(struct pci_dev *pdev,
ring_space = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &ring_dma);
if (!ring_space)
goto err_out_cleardev;
- np->tx_ring = (struct yellowfin_desc *)ring_space;
+ np->tx_ring = ring_space;
np->tx_ring_dma = ring_dma;
ring_space = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &ring_dma);
if (!ring_space)
goto err_out_unmap_tx;
- np->rx_ring = (struct yellowfin_desc *)ring_space;
+ np->rx_ring = ring_space;
np->rx_ring_dma = ring_dma;
ring_space = pci_alloc_consistent(pdev, STATUS_TOTAL_SIZE, &ring_dma);
if (!ring_space)
goto err_out_unmap_rx;
- np->tx_status = (struct tx_status_words *)ring_space;
+ np->tx_status = ring_space;
np->tx_status_dma = ring_dma;
if (dev->mem_start)
diff --git a/drivers/net/ethernet/pasemi/Kconfig b/drivers/net/ethernet/pasemi/Kconfig
new file mode 100644
index 000000000000..01e6c329d78c
--- /dev/null
+++ b/drivers/net/ethernet/pasemi/Kconfig
@@ -0,0 +1,30 @@
+#
+# PA Semi network device configuration
+#
+
+config NET_VENDOR_PASEMI
+ bool "PA Semi devices"
+ default y
+ depends on PPC_PASEMI && PCI && INET
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about PA Semi cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_PASEMI
+
+config PASEMI_MAC
+ tristate "PA Semi 1/10Gbit MAC"
+ depends on PPC_PASEMI && PCI && INET
+ select PHYLIB
+ select INET_LRO
+ ---help---
+ This driver supports the on-chip 1/10Gbit Ethernet controller on
+ PA Semi's PWRficient line of chips.
+
+endif # NET_VENDOR_PASEMI
diff --git a/drivers/net/ethernet/pasemi/Makefile b/drivers/net/ethernet/pasemi/Makefile
new file mode 100644
index 000000000000..05db5434bafc
--- /dev/null
+++ b/drivers/net/ethernet/pasemi/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the A Semi network device drivers.
+#
+
+obj-$(CONFIG_PASEMI_MAC) += pasemi_mac.o pasemi_mac_ethtool.o
diff --git a/drivers/net/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c
index 9ec112ca62e4..49b549ff2c78 100644
--- a/drivers/net/pasemi_mac.c
+++ b/drivers/net/ethernet/pasemi/pasemi_mac.c
@@ -300,9 +300,9 @@ static int pasemi_mac_unmap_tx_skb(struct pasemi_mac *mac,
pci_unmap_single(pdev, dmas[0], skb_headlen(skb), PCI_DMA_TODEVICE);
for (f = 0; f < nfrags; f++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[f];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[f];
- pci_unmap_page(pdev, dmas[f+1], frag->size, PCI_DMA_TODEVICE);
+ pci_unmap_page(pdev, dmas[f+1], skb_frag_size(frag), PCI_DMA_TODEVICE);
}
dev_kfree_skb_irq(skb);
@@ -1505,11 +1505,10 @@ static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev)
for (i = 0; i < nfrags; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- map[i+1] = pci_map_page(mac->dma_pdev, frag->page,
- frag->page_offset, frag->size,
- PCI_DMA_TODEVICE);
- map_size[i+1] = frag->size;
- if (pci_dma_mapping_error(mac->dma_pdev, map[i+1])) {
+ map[i + 1] = skb_frag_dma_map(&mac->dma_pdev->dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
+ map_size[i+1] = skb_frag_size(frag);
+ if (dma_mapping_error(&mac->dma_pdev->dev, map[i + 1])) {
nfrags = i;
goto out_err_nolock;
}
@@ -1719,7 +1718,7 @@ static const struct net_device_ops pasemi_netdev_ops = {
.ndo_open = pasemi_mac_open,
.ndo_stop = pasemi_mac_close,
.ndo_start_xmit = pasemi_mac_start_tx,
- .ndo_set_multicast_list = pasemi_mac_set_rx_mode,
+ .ndo_set_rx_mode = pasemi_mac_set_rx_mode,
.ndo_set_mac_address = pasemi_mac_set_mac_addr,
.ndo_change_mtu = pasemi_mac_change_mtu,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/pasemi_mac.h b/drivers/net/ethernet/pasemi/pasemi_mac.h
index e2f4efa8ad46..e2f4efa8ad46 100644
--- a/drivers/net/pasemi_mac.h
+++ b/drivers/net/ethernet/pasemi/pasemi_mac.h
diff --git a/drivers/net/pasemi_mac_ethtool.c b/drivers/net/ethernet/pasemi/pasemi_mac_ethtool.c
index 4825959a0efe..4825959a0efe 100644
--- a/drivers/net/pasemi_mac_ethtool.c
+++ b/drivers/net/ethernet/pasemi/pasemi_mac_ethtool.c
diff --git a/drivers/net/ethernet/qlogic/Kconfig b/drivers/net/ethernet/qlogic/Kconfig
new file mode 100644
index 000000000000..a8669adecc97
--- /dev/null
+++ b/drivers/net/ethernet/qlogic/Kconfig
@@ -0,0 +1,54 @@
+#
+# QLogic network device configuration
+#
+
+config NET_VENDOR_QLOGIC
+ bool "QLogic devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about QLogic cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_QLOGIC
+
+config QLA3XXX
+ tristate "QLogic QLA3XXX Network Driver Support"
+ depends on PCI
+ ---help---
+ This driver supports QLogic ISP3XXX gigabit Ethernet cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called qla3xxx.
+
+config QLCNIC
+ tristate "QLOGIC QLCNIC 1/10Gb Converged Ethernet NIC Support"
+ depends on PCI
+ select FW_LOADER
+ ---help---
+ This driver supports QLogic QLE8240 and QLE8242 Converged Ethernet
+ devices.
+
+config QLGE
+ tristate "QLogic QLGE 10Gb Ethernet Driver Support"
+ depends on PCI
+ ---help---
+ This driver supports QLogic ISP8XXX 10Gb Ethernet cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called qlge.
+
+config NETXEN_NIC
+ tristate "NetXen Multi port (1/10) Gigabit Ethernet NIC"
+ depends on PCI
+ select FW_LOADER
+ ---help---
+ This enables the support for NetXen's Gigabit Ethernet card.
+
+endif # NET_VENDOR_QLOGIC
diff --git a/drivers/net/ethernet/qlogic/Makefile b/drivers/net/ethernet/qlogic/Makefile
new file mode 100644
index 000000000000..b2a283d9ae60
--- /dev/null
+++ b/drivers/net/ethernet/qlogic/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the QLogic network device drivers.
+#
+
+obj-$(CONFIG_QLA3XXX) += qla3xxx.o
+obj-$(CONFIG_QLCNIC) += qlcnic/
+obj-$(CONFIG_QLGE) += qlge/
+obj-$(CONFIG_NETXEN_NIC) += netxen/
diff --git a/drivers/net/netxen/Makefile b/drivers/net/ethernet/qlogic/netxen/Makefile
index 861a0590b1f4..861a0590b1f4 100644
--- a/drivers/net/netxen/Makefile
+++ b/drivers/net/ethernet/qlogic/netxen/Makefile
diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h
index 77220687b92a..a876dffd7101 100644
--- a/drivers/net/netxen/netxen_nic.h
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h
@@ -53,8 +53,8 @@
#define _NETXEN_NIC_LINUX_MAJOR 4
#define _NETXEN_NIC_LINUX_MINOR 0
-#define _NETXEN_NIC_LINUX_SUBVERSION 75
-#define NETXEN_NIC_LINUX_VERSIONID "4.0.75"
+#define _NETXEN_NIC_LINUX_SUBVERSION 77
+#define NETXEN_NIC_LINUX_VERSIONID "4.0.77"
#define NETXEN_VERSION_CODE(a, b, c) (((a) << 24) + ((b) << 16) + (c))
#define _major(v) (((v) >> 24) & 0xff)
@@ -940,6 +940,11 @@ typedef struct nx_mac_list_s {
uint8_t mac_addr[ETH_ALEN+2];
} nx_mac_list_t;
+struct nx_vlan_ip_list {
+ struct list_head list;
+ u32 ip_addr;
+};
+
/*
* Interrupt coalescing defaults. The defaults are for 1500 MTU. It is
* adjusted based on configured MTU.
@@ -1165,6 +1170,7 @@ struct netxen_adapter {
struct net_device *netdev;
struct pci_dev *pdev;
struct list_head mac_list;
+ struct list_head vlan_ip_list;
spinlock_t tx_clean_lock;
@@ -1302,6 +1308,7 @@ int netxen_nic_wol_supported(struct netxen_adapter *adapter);
int netxen_init_dummy_dma(struct netxen_adapter *adapter);
void netxen_free_dummy_dma(struct netxen_adapter *adapter);
+int netxen_check_flash_fw_compatibility(struct netxen_adapter *adapter);
int netxen_phantom_init(struct netxen_adapter *adapter, int pegtune_val);
int netxen_load_firmware(struct netxen_adapter *adapter);
int netxen_need_fw_reset(struct netxen_adapter *adapter);
diff --git a/drivers/net/netxen/netxen_nic_ctx.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c
index f16966afa64e..a925392abd6f 100644
--- a/drivers/net/netxen/netxen_nic_ctx.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c
@@ -163,7 +163,7 @@ nx_fw_cmd_create_rx_ctx(struct netxen_adapter *adapter)
rq_size, &hostrq_phys_addr);
if (addr == NULL)
return -ENOMEM;
- prq = (nx_hostrq_rx_ctx_t *)addr;
+ prq = addr;
addr = pci_alloc_consistent(adapter->pdev,
rsp_size, &cardrsp_phys_addr);
@@ -171,7 +171,7 @@ nx_fw_cmd_create_rx_ctx(struct netxen_adapter *adapter)
err = -ENOMEM;
goto out_free_rq;
}
- prsp = (nx_cardrsp_rx_ctx_t *)addr;
+ prsp = addr;
prq->host_rsp_dma_addr = cpu_to_le64(cardrsp_phys_addr);
@@ -318,10 +318,10 @@ nx_fw_cmd_create_tx_ctx(struct netxen_adapter *adapter)
}
memset(rq_addr, 0, rq_size);
- prq = (nx_hostrq_tx_ctx_t *)rq_addr;
+ prq = rq_addr;
memset(rsp_addr, 0, rsp_size);
- prsp = (nx_cardrsp_tx_ctx_t *)rsp_addr;
+ prsp = rsp_addr;
prq->host_rsp_dma_addr = cpu_to_le64(rsp_phys_addr);
@@ -629,7 +629,7 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter)
}
memset(addr, 0, sizeof(struct netxen_ring_ctx));
- recv_ctx->hwctx = (struct netxen_ring_ctx *)addr;
+ recv_ctx->hwctx = addr;
recv_ctx->hwctx->ctx_id = cpu_to_le32(port);
recv_ctx->hwctx->cmd_consumer_offset =
cpu_to_le64(recv_ctx->phys_addr +
@@ -648,7 +648,7 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter)
goto err_out_free;
}
- tx_ring->desc_head = (struct cmd_desc_type0 *)addr;
+ tx_ring->desc_head = addr;
for (ring = 0; ring < adapter->max_rds_rings; ring++) {
rds_ring = &recv_ctx->rds_rings[ring];
@@ -662,7 +662,7 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter)
err = -ENOMEM;
goto err_out_free;
}
- rds_ring->desc_head = (struct rcv_desc *)addr;
+ rds_ring->desc_head = addr;
if (NX_IS_REVISION_P2(adapter->ahw.revision_id))
rds_ring->crb_rcv_producer =
@@ -683,7 +683,7 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter)
err = -ENOMEM;
goto err_out_free;
}
- sds_ring->desc_head = (struct status_desc *)addr;
+ sds_ring->desc_head = addr;
if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
sds_ring->crb_sts_consumer =
diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c
index b34fb74d07e3..e09ea83b8c47 100644
--- a/drivers/net/netxen/netxen_nic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c
@@ -413,9 +413,6 @@ netxen_nic_get_ringparam(struct net_device *dev,
}
ring->tx_max_pending = MAX_CMD_DESCRIPTORS;
-
- ring->rx_mini_max_pending = 0;
- ring->rx_mini_pending = 0;
}
static u32
diff --git a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h
index dc1967c1f312..dc1967c1f312 100644
--- a/drivers/net/netxen/netxen_nic_hdr.h
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h
diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c
index 5cef718fe35f..3f89e57cae50 100644
--- a/drivers/net/netxen/netxen_nic_hw.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c
@@ -809,6 +809,9 @@ int netxen_config_hw_lro(struct netxen_adapter *adapter, int enable)
u64 word;
int rv = 0;
+ if (!test_bit(__NX_FW_ATTACHED, &adapter->state))
+ return 0;
+
memset(&req, 0, sizeof(nx_nic_req_t));
req.qhdr = cpu_to_le64(NX_HOST_REQUEST << 23);
@@ -959,6 +962,9 @@ int netxen_send_lro_cleanup(struct netxen_adapter *adapter)
u64 word;
int rv;
+ if (!test_bit(__NX_FW_ATTACHED, &adapter->state))
+ return 0;
+
memset(&req, 0, sizeof(nx_nic_req_t));
req.qhdr = cpu_to_le64(NX_HOST_REQUEST << 23);
diff --git a/drivers/net/netxen/netxen_nic_hw.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.h
index e2c5b6f2df03..e2c5b6f2df03 100644
--- a/drivers/net/netxen/netxen_nic_hw.h
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.h
diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
index 7f999671c7b2..a8259cc19a63 100644
--- a/drivers/net/netxen/netxen_nic_init.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
@@ -26,6 +26,7 @@
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/if_vlan.h>
#include "netxen_nic.h"
#include "netxen_nic_hw.h"
@@ -964,6 +965,35 @@ netxen_need_fw_reset(struct netxen_adapter *adapter)
return 0;
}
+#define NETXEN_MIN_P3_FW_SUPP NETXEN_VERSION_CODE(4, 0, 505)
+
+int
+netxen_check_flash_fw_compatibility(struct netxen_adapter *adapter)
+{
+ u32 flash_fw_ver, min_fw_ver;
+
+ if (NX_IS_REVISION_P2(adapter->ahw.revision_id))
+ return 0;
+
+ if (netxen_rom_fast_read(adapter,
+ NX_FW_VERSION_OFFSET, (int *)&flash_fw_ver)) {
+ dev_err(&adapter->pdev->dev, "Unable to read flash fw"
+ "version\n");
+ return -EIO;
+ }
+
+ flash_fw_ver = NETXEN_DECODE_VERSION(flash_fw_ver);
+ min_fw_ver = NETXEN_MIN_P3_FW_SUPP;
+ if (flash_fw_ver >= min_fw_ver)
+ return 0;
+
+ dev_info(&adapter->pdev->dev, "Flash fw[%d.%d.%d] is < min fw supported"
+ "[4.0.505]. Please update firmware on flash\n",
+ _major(flash_fw_ver), _minor(flash_fw_ver),
+ _build(flash_fw_ver));
+ return -EINVAL;
+}
+
static char *fw_name[] = {
NX_P2_MN_ROMIMAGE_NAME,
NX_P3_CT_ROMIMAGE_NAME,
@@ -1071,10 +1101,12 @@ static int
netxen_validate_firmware(struct netxen_adapter *adapter)
{
__le32 val;
- u32 ver, min_ver, bios;
+ __le32 flash_fw_ver;
+ u32 file_fw_ver, min_ver, bios;
struct pci_dev *pdev = adapter->pdev;
const struct firmware *fw = adapter->fw;
u8 fw_type = adapter->fw_type;
+ u32 crbinit_fix_fw;
if (fw_type == NX_UNIFIED_ROMIMAGE) {
if (netxen_nic_validate_unified_romimage(adapter))
@@ -1091,16 +1123,18 @@ netxen_validate_firmware(struct netxen_adapter *adapter)
val = nx_get_fw_version(adapter);
if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
- min_ver = NETXEN_VERSION_CODE(4, 0, 216);
+ min_ver = NETXEN_MIN_P3_FW_SUPP;
else
min_ver = NETXEN_VERSION_CODE(3, 4, 216);
- ver = NETXEN_DECODE_VERSION(val);
+ file_fw_ver = NETXEN_DECODE_VERSION(val);
- if ((_major(ver) > _NETXEN_NIC_LINUX_MAJOR) || (ver < min_ver)) {
+ if ((_major(file_fw_ver) > _NETXEN_NIC_LINUX_MAJOR) ||
+ (file_fw_ver < min_ver)) {
dev_err(&pdev->dev,
"%s: firmware version %d.%d.%d unsupported\n",
- fw_name[fw_type], _major(ver), _minor(ver), _build(ver));
+ fw_name[fw_type], _major(file_fw_ver), _minor(file_fw_ver),
+ _build(file_fw_ver));
return -EINVAL;
}
@@ -1112,17 +1146,34 @@ netxen_validate_firmware(struct netxen_adapter *adapter)
return -EINVAL;
}
- /* check if flashed firmware is newer */
if (netxen_rom_fast_read(adapter,
- NX_FW_VERSION_OFFSET, (int *)&val))
+ NX_FW_VERSION_OFFSET, (int *)&flash_fw_ver)) {
+ dev_err(&pdev->dev, "Unable to read flash fw version\n");
return -EIO;
- val = NETXEN_DECODE_VERSION(val);
- if (val > ver) {
- dev_info(&pdev->dev, "%s: firmware is older than flash\n",
- fw_name[fw_type]);
+ }
+ flash_fw_ver = NETXEN_DECODE_VERSION(flash_fw_ver);
+
+ /* New fw from file is not allowed, if fw on flash is < 4.0.554 */
+ crbinit_fix_fw = NETXEN_VERSION_CODE(4, 0, 554);
+ if (file_fw_ver >= crbinit_fix_fw && flash_fw_ver < crbinit_fix_fw &&
+ NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
+ dev_err(&pdev->dev, "Incompatibility detected between driver "
+ "and firmware version on flash. This configuration "
+ "is not recommended. Please update the firmware on "
+ "flash immediately\n");
return -EINVAL;
}
+ /* check if flashed firmware is newer only for no-mn and P2 case*/
+ if (!netxen_p3_has_mn(adapter) ||
+ NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
+ if (flash_fw_ver > file_fw_ver) {
+ dev_info(&pdev->dev, "%s: firmware is older than flash\n",
+ fw_name[fw_type]);
+ return -EINVAL;
+ }
+ }
+
NXWR32(adapter, NETXEN_CAM_RAM(0x1fc), NETXEN_BDINFO_MAGIC);
return 0;
}
@@ -1279,7 +1330,7 @@ void netxen_free_dummy_dma(struct netxen_adapter *adapter)
if (--i == 0)
break;
- };
+ }
}
if (i) {
@@ -1569,6 +1620,7 @@ netxen_process_lro(struct netxen_adapter *adapter,
int index;
u16 lro_length, length, data_offset;
u32 seq_number;
+ u8 vhdr_len = 0;
if (unlikely(ring > adapter->max_rds_rings))
return NULL;
@@ -1602,8 +1654,10 @@ netxen_process_lro(struct netxen_adapter *adapter,
skb_pull(skb, l2_hdr_offset);
skb->protocol = eth_type_trans(skb, netdev);
- iph = (struct iphdr *)skb->data;
- th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
+ if (skb->protocol == htons(ETH_P_8021Q))
+ vhdr_len = VLAN_HLEN;
+ iph = (struct iphdr *)(skb->data + vhdr_len);
+ th = (struct tcphdr *)((skb->data + vhdr_len) + (iph->ihl << 2));
length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
iph->tot_len = htons(length);
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
index c0788a31ff0f..8cf3173ba488 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
@@ -91,8 +91,10 @@ static irqreturn_t netxen_intr(int irq, void *data);
static irqreturn_t netxen_msi_intr(int irq, void *data);
static irqreturn_t netxen_msix_intr(int irq, void *data);
-static void netxen_config_indev_addr(struct net_device *dev, unsigned long);
-static struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev);
+static void netxen_free_vlan_ip_list(struct netxen_adapter *);
+static void netxen_restore_indev_addr(struct net_device *dev, unsigned long);
+static struct rtnl_link_stats64 *netxen_nic_get_stats(struct net_device *dev,
+ struct rtnl_link_stats64 *stats);
static int netxen_nic_set_mac(struct net_device *netdev, void *p);
/* PCI Device ID Table */
@@ -398,6 +400,63 @@ static void netxen_set_port_mode(struct netxen_adapter *adapter)
}
}
+#define PCI_CAP_ID_GEN 0x10
+
+static void netxen_pcie_strap_init(struct netxen_adapter *adapter)
+{
+ u32 pdevfuncsave;
+ u32 c8c9value = 0;
+ u32 chicken = 0;
+ u32 control = 0;
+ int i, pos;
+ struct pci_dev *pdev;
+
+ pdev = adapter->pdev;
+
+ chicken = NXRD32(adapter, NETXEN_PCIE_REG(PCIE_CHICKEN3));
+ /* clear chicken3.25:24 */
+ chicken &= 0xFCFFFFFF;
+ /*
+ * if gen1 and B0, set F1020 - if gen 2, do nothing
+ * if gen2 set to F1000
+ */
+ pos = pci_find_capability(pdev, PCI_CAP_ID_GEN);
+ if (pos == 0xC0) {
+ pci_read_config_dword(pdev, pos + 0x10, &control);
+ if ((control & 0x000F0000) != 0x00020000) {
+ /* set chicken3.24 if gen1 */
+ chicken |= 0x01000000;
+ }
+ dev_info(&adapter->pdev->dev, "Gen2 strapping detected\n");
+ c8c9value = 0xF1000;
+ } else {
+ /* set chicken3.24 if gen1 */
+ chicken |= 0x01000000;
+ dev_info(&adapter->pdev->dev, "Gen1 strapping detected\n");
+ if (adapter->ahw.revision_id == NX_P3_B0)
+ c8c9value = 0xF1020;
+ else
+ c8c9value = 0;
+ }
+
+ NXWR32(adapter, NETXEN_PCIE_REG(PCIE_CHICKEN3), chicken);
+
+ if (!c8c9value)
+ return;
+
+ pdevfuncsave = pdev->devfn;
+ if (pdevfuncsave & 0x07)
+ return;
+
+ for (i = 0; i < 8; i++) {
+ pci_read_config_dword(pdev, pos + 8, &control);
+ pci_read_config_dword(pdev, pos + 8, &control);
+ pci_write_config_dword(pdev, pos + 8, c8c9value);
+ pdev->devfn++;
+ }
+ pdev->devfn = pdevfuncsave;
+}
+
static void netxen_set_msix_bit(struct pci_dev *pdev, int enable)
{
u32 control;
@@ -520,9 +579,9 @@ static const struct net_device_ops netxen_netdev_ops = {
.ndo_open = netxen_nic_open,
.ndo_stop = netxen_nic_close,
.ndo_start_xmit = netxen_nic_xmit_frame,
- .ndo_get_stats = netxen_nic_get_stats,
+ .ndo_get_stats64 = netxen_nic_get_stats,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = netxen_set_multicast_list,
+ .ndo_set_rx_mode = netxen_set_multicast_list,
.ndo_set_mac_address = netxen_nic_set_mac,
.ndo_change_mtu = netxen_nic_change_mtu,
.ndo_tx_timeout = netxen_tx_timeout,
@@ -865,7 +924,7 @@ netxen_start_firmware(struct netxen_adapter *adapter)
if (err < 0)
goto err_out;
if (err == 0)
- goto wait_init;
+ goto pcie_strap_init;
if (first_boot != 0x55555555) {
NXWR32(adapter, CRB_CMDPEG_STATE, 0);
@@ -908,6 +967,10 @@ netxen_start_firmware(struct netxen_adapter *adapter)
| (_NETXEN_NIC_LINUX_SUBVERSION);
NXWR32(adapter, CRB_DRIVER_VERSION, val);
+pcie_strap_init:
+ if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
+ netxen_pcie_strap_init(adapter);
+
wait_init:
/* Handshake with the card before we register the devices. */
err = netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
@@ -1358,6 +1421,7 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
spin_lock_init(&adapter->tx_clean_lock);
INIT_LIST_HEAD(&adapter->mac_list);
+ INIT_LIST_HEAD(&adapter->vlan_ip_list);
err = netxen_setup_pci_map(adapter);
if (err)
@@ -1387,6 +1451,10 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
break;
}
+ err = netxen_check_flash_fw_compatibility(adapter);
+ if (err)
+ goto err_out_iounmap;
+
if (adapter->portnum == 0) {
val = NXRD32(adapter, NX_CRB_DEV_REF_COUNT);
if (val != 0xffffffff && val != 0) {
@@ -1476,6 +1544,7 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev)
cancel_work_sync(&adapter->tx_timeout_task);
+ netxen_free_vlan_ip_list(adapter);
netxen_nic_detach(adapter);
nx_decr_dev_ref_cnt(adapter);
@@ -1558,7 +1627,7 @@ static int netxen_nic_attach_func(struct pci_dev *pdev)
if (err)
goto err_out_detach;
- netxen_config_indev_addr(netdev, NETDEV_UP);
+ netxen_restore_indev_addr(netdev, NETDEV_UP);
}
netif_device_attach(netdev);
@@ -1836,13 +1905,13 @@ netxen_map_tx_skb(struct pci_dev *pdev,
frag = &skb_shinfo(skb)->frags[i];
nf = &pbuf->frag_array[i+1];
- map = pci_map_page(pdev, frag->page, frag->page_offset,
- frag->size, PCI_DMA_TODEVICE);
- if (pci_dma_mapping_error(pdev, map))
+ map = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&pdev->dev, map))
goto unwind;
nf->dma = map;
- nf->length = frag->size;
+ nf->length = skb_frag_size(frag);
}
return 0;
@@ -1893,7 +1962,7 @@ netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
for (i = 0; i < (frag_count - NETXEN_MAX_FRAGS_PER_TX); i++) {
frag = &skb_shinfo(skb)->frags[i];
- delta += frag->size;
+ delta += skb_frag_size(frag);
}
if (!__pskb_pull_tail(skb, delta))
@@ -2110,10 +2179,10 @@ request_reset:
clear_bit(__NX_RESETTING, &adapter->state);
}
-static struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev)
+static struct rtnl_link_stats64 *netxen_nic_get_stats(struct net_device *netdev,
+ struct rtnl_link_stats64 *stats)
{
struct netxen_adapter *adapter = netdev_priv(netdev);
- struct net_device_stats *stats = &netdev->stats;
stats->rx_packets = adapter->stats.rx_pkts + adapter->stats.lro_pkts;
stats->tx_packets = adapter->stats.xmitfinished;
@@ -2369,7 +2438,7 @@ netxen_attach_work(struct work_struct *work)
goto done;
}
- netxen_config_indev_addr(netdev, NETDEV_UP);
+ netxen_restore_indev_addr(netdev, NETDEV_UP);
}
netif_device_attach(netdev);
@@ -2843,10 +2912,70 @@ netxen_destip_supported(struct netxen_adapter *adapter)
}
static void
-netxen_config_indev_addr(struct net_device *dev, unsigned long event)
+netxen_free_vlan_ip_list(struct netxen_adapter *adapter)
+{
+ struct nx_vlan_ip_list *cur;
+ struct list_head *head = &adapter->vlan_ip_list;
+
+ while (!list_empty(head)) {
+ cur = list_entry(head->next, struct nx_vlan_ip_list, list);
+ netxen_config_ipaddr(adapter, cur->ip_addr, NX_IP_DOWN);
+ list_del(&cur->list);
+ kfree(cur);
+ }
+
+}
+static void
+netxen_list_config_vlan_ip(struct netxen_adapter *adapter,
+ struct in_ifaddr *ifa, unsigned long event)
+{
+ struct net_device *dev;
+ struct nx_vlan_ip_list *cur, *tmp_cur;
+ struct list_head *head;
+
+ dev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL;
+
+ if (dev == NULL)
+ return;
+
+ if (!is_vlan_dev(dev))
+ return;
+
+ switch (event) {
+ case NX_IP_UP:
+ list_for_each(head, &adapter->vlan_ip_list) {
+ cur = list_entry(head, struct nx_vlan_ip_list, list);
+
+ if (cur->ip_addr == ifa->ifa_address)
+ return;
+ }
+
+ cur = kzalloc(sizeof(struct nx_vlan_ip_list), GFP_ATOMIC);
+ if (cur == NULL) {
+ printk(KERN_ERR "%s: failed to add vlan ip to list\n",
+ adapter->netdev->name);
+ return;
+ }
+
+ cur->ip_addr = ifa->ifa_address;
+ list_add_tail(&cur->list, &adapter->vlan_ip_list);
+ break;
+ case NX_IP_DOWN:
+ list_for_each_entry_safe(cur, tmp_cur,
+ &adapter->vlan_ip_list, list) {
+ if (cur->ip_addr == ifa->ifa_address) {
+ list_del(&cur->list);
+ kfree(cur);
+ break;
+ }
+ }
+ }
+}
+static void
+netxen_config_indev_addr(struct netxen_adapter *adapter,
+ struct net_device *dev, unsigned long event)
{
struct in_device *indev;
- struct netxen_adapter *adapter = netdev_priv(dev);
if (!netxen_destip_supported(adapter))
return;
@@ -2860,10 +2989,12 @@ netxen_config_indev_addr(struct net_device *dev, unsigned long event)
case NETDEV_UP:
netxen_config_ipaddr(adapter,
ifa->ifa_address, NX_IP_UP);
+ netxen_list_config_vlan_ip(adapter, ifa, NX_IP_UP);
break;
case NETDEV_DOWN:
netxen_config_ipaddr(adapter,
ifa->ifa_address, NX_IP_DOWN);
+ netxen_list_config_vlan_ip(adapter, ifa, NX_IP_DOWN);
break;
default:
break;
@@ -2873,11 +3004,28 @@ netxen_config_indev_addr(struct net_device *dev, unsigned long event)
in_dev_put(indev);
}
+static void
+netxen_restore_indev_addr(struct net_device *netdev, unsigned long event)
+
+{
+ struct netxen_adapter *adapter = netdev_priv(netdev);
+ struct nx_vlan_ip_list *pos, *tmp_pos;
+ unsigned long ip_event;
+
+ ip_event = (event == NETDEV_UP) ? NX_IP_UP : NX_IP_DOWN;
+ netxen_config_indev_addr(adapter, netdev, event);
+
+ list_for_each_entry_safe(pos, tmp_pos, &adapter->vlan_ip_list, list) {
+ netxen_config_ipaddr(adapter, pos->ip_addr, ip_event);
+ }
+}
+
static int netxen_netdev_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct netxen_adapter *adapter;
struct net_device *dev = (struct net_device *)ptr;
+ struct net_device *orig_dev = dev;
recheck:
if (dev == NULL)
@@ -2899,7 +3047,7 @@ recheck:
if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC)
goto done;
- netxen_config_indev_addr(dev, event);
+ netxen_config_indev_addr(adapter, orig_dev, event);
done:
return NOTIFY_DONE;
}
@@ -2916,7 +3064,7 @@ netxen_inetaddr_event(struct notifier_block *this,
dev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL;
recheck:
- if (dev == NULL || !netif_running(dev))
+ if (dev == NULL)
goto done;
if (dev->priv_flags & IFF_802_1Q_VLAN) {
@@ -2938,9 +3086,11 @@ recheck:
switch (event) {
case NETDEV_UP:
netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_UP);
+ netxen_list_config_vlan_ip(adapter, ifa, NX_IP_UP);
break;
case NETDEV_DOWN:
netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_DOWN);
+ netxen_list_config_vlan_ip(adapter, ifa, NX_IP_DOWN);
break;
default:
break;
@@ -2959,7 +3109,10 @@ static struct notifier_block netxen_inetaddr_cb = {
};
#else
static void
-netxen_config_indev_addr(struct net_device *dev, unsigned long event)
+netxen_restore_indev_addr(struct net_device *dev, unsigned long event)
+{ }
+static void
+netxen_free_vlan_ip_list(struct netxen_adapter *adapter)
{ }
#endif
diff --git a/drivers/net/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c
index 771bb614ccc9..a4bdff438a5e 100644
--- a/drivers/net/qla3xxx.c
+++ b/drivers/net/ethernet/qlogic/qla3xxx.c
@@ -1650,7 +1650,7 @@ static int ql_mii_setup(struct ql3_adapter *qdev)
SUPPORTED_1000baseT_Half | \
SUPPORTED_1000baseT_Full | \
SUPPORTED_Autoneg | \
- SUPPORTED_TP); \
+ SUPPORTED_TP) \
static u32 ql_supported_modes(struct ql3_adapter *qdev)
{
@@ -2388,11 +2388,10 @@ static int ql_send_map(struct ql3_adapter *qdev,
seg++;
}
- map = pci_map_page(qdev->pdev, frag->page,
- frag->page_offset, frag->size,
- PCI_DMA_TODEVICE);
+ map = skb_frag_dma_map(&qdev->pdev->dev, frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
- err = pci_dma_mapping_error(qdev->pdev, map);
+ err = dma_mapping_error(&qdev->pdev->dev, map);
if (err) {
netdev_err(qdev->ndev,
"PCI mapping frags failed with error: %d\n",
@@ -2402,9 +2401,9 @@ static int ql_send_map(struct ql3_adapter *qdev,
oal_entry->dma_lo = cpu_to_le32(LS_64BITS(map));
oal_entry->dma_hi = cpu_to_le32(MS_64BITS(map));
- oal_entry->len = cpu_to_le32(frag->size);
+ oal_entry->len = cpu_to_le32(skb_frag_size(frag));
dma_unmap_addr_set(&tx_cb->map[seg], mapaddr, map);
- dma_unmap_len_set(&tx_cb->map[seg], maplen, frag->size);
+ dma_unmap_len_set(&tx_cb->map[seg], maplen, skb_frag_size(frag));
}
/* Terminate the last segment. */
oal_entry->len |= cpu_to_le32(OAL_LAST_ENTRY);
@@ -2873,7 +2872,7 @@ static int ql_alloc_mem_resources(struct ql3_adapter *qdev)
PAGE_SIZE, &qdev->shadow_reg_phy_addr);
if (qdev->shadow_reg_virt_addr != NULL) {
- qdev->preq_consumer_index = (u16 *) qdev->shadow_reg_virt_addr;
+ qdev->preq_consumer_index = qdev->shadow_reg_virt_addr;
qdev->req_consumer_index_phy_addr_high =
MS_64BITS(qdev->shadow_reg_phy_addr);
qdev->req_consumer_index_phy_addr_low =
@@ -3114,8 +3113,7 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev)
qdev->small_buf_release_cnt = 8;
qdev->lrg_buf_q_producer_index = qdev->num_lbufq_entries - 1;
qdev->lrg_buf_release_cnt = 8;
- qdev->lrg_buf_next_free =
- (struct bufq_addr_element *)qdev->lrg_buf_q_virt_addr;
+ qdev->lrg_buf_next_free = qdev->lrg_buf_q_virt_addr;
qdev->small_buf_index = 0;
qdev->lrg_buf_index = 0;
qdev->lrg_buf_free_count = 0;
@@ -3763,7 +3761,6 @@ static const struct net_device_ops ql3xxx_netdev_ops = {
.ndo_open = ql3xxx_open,
.ndo_start_xmit = ql3xxx_send,
.ndo_stop = ql3xxx_close,
- .ndo_set_multicast_list = NULL, /* not allowed on NIC side */
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = ql3xxx_set_mac_address,
diff --git a/drivers/net/qla3xxx.h b/drivers/net/ethernet/qlogic/qla3xxx.h
index 73e234366a82..73e234366a82 100644
--- a/drivers/net/qla3xxx.h
+++ b/drivers/net/ethernet/qlogic/qla3xxx.h
diff --git a/drivers/net/qlcnic/Makefile b/drivers/net/ethernet/qlogic/qlcnic/Makefile
index ddba83ef3f44..ddba83ef3f44 100644
--- a/drivers/net/qlcnic/Makefile
+++ b/drivers/net/ethernet/qlogic/qlcnic/Makefile
diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 480ef5cb6ef9..7ed53dbb8646 100644
--- a/drivers/net/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -36,8 +36,8 @@
#define _QLCNIC_LINUX_MAJOR 5
#define _QLCNIC_LINUX_MINOR 0
-#define _QLCNIC_LINUX_SUBVERSION 18
-#define QLCNIC_LINUX_VERSIONID "5.0.18"
+#define _QLCNIC_LINUX_SUBVERSION 25
+#define QLCNIC_LINUX_VERSIONID "5.0.25"
#define QLCNIC_DRV_IDC_VER 0x01
#define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\
(_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
@@ -73,6 +73,7 @@
(sizeof(struct cmd_desc_type0) * tx_ring->num_desc)
#define QLCNIC_P3P_A0 0x50
+#define QLCNIC_P3P_C0 0x58
#define QLCNIC_IS_REVISION_P3P(REVISION) (REVISION >= QLCNIC_P3P_A0)
@@ -291,7 +292,8 @@ struct uni_data_desc{
/* Flash Defines and Structures */
#define QLCNIC_FLT_LOCATION 0x3F1000
-#define QLCNIC_FW_IMAGE_REGION 0x74
+#define QLCNIC_B0_FW_IMAGE_REGION 0x74
+#define QLCNIC_C0_FW_IMAGE_REGION 0x97
#define QLCNIC_BOOTLD_REGION 0X72
struct qlcnic_flt_header {
u16 version;
@@ -429,6 +431,7 @@ struct qlcnic_dump_template_hdr {
struct qlcnic_fw_dump {
u8 clr; /* flag to indicate if dump is cleared */
+ u8 enable; /* enable/disable dump */
u32 size; /* total size of the dump */
void *data; /* dump data area */
struct qlcnic_dump_template_hdr *tmpl_hdr;
@@ -450,9 +453,12 @@ struct qlcnic_hardware_context {
u8 revision_id;
u8 pci_func;
u8 linkup;
+ u8 loopback_state;
u16 port_type;
u16 board_type;
+ u8 beacon_state;
+
struct qlcnic_nic_intr_coalesce coal;
struct qlcnic_fw_dump fw_dump;
};
@@ -577,6 +583,7 @@ struct qlcnic_recv_context {
#define QLCNIC_CDRP_CMD_DESTROY_RX_CTX 0x00000008
#define QLCNIC_CDRP_CMD_CREATE_TX_CTX 0x00000009
#define QLCNIC_CDRP_CMD_DESTROY_TX_CTX 0x0000000a
+#define QLCNIC_CDRP_CMD_INTRPT_TEST 0x00000011
#define QLCNIC_CDRP_CMD_SET_MTU 0x00000012
#define QLCNIC_CDRP_CMD_READ_PHY 0x00000013
#define QLCNIC_CDRP_CMD_WRITE_PHY 0x00000014
@@ -779,6 +786,14 @@ struct qlcnic_mac_list_s {
#define QLCNIC_IP_UP 2
#define QLCNIC_IP_DOWN 3
+#define QLCNIC_ILB_MODE 0x1
+#define QLCNIC_ELB_MODE 0x2
+
+#define QLCNIC_LINKEVENT 0x1
+#define QLCNIC_LB_RESPONSE 0x2
+#define QLCNIC_IS_LB_CONFIGURED(VAL) \
+ (VAL == (QLCNIC_LINKEVENT | QLCNIC_LB_RESPONSE))
+
/*
* Driver --> Firmware
*/
@@ -788,13 +803,17 @@ struct qlcnic_mac_list_s {
#define QLCNIC_H2C_OPCODE_LRO_REQUEST 0x7
#define QLCNIC_H2C_OPCODE_SET_MAC_RECEIVE_MODE 0xc
#define QLCNIC_H2C_OPCODE_CONFIG_IPADDR 0x12
+
#define QLCNIC_H2C_OPCODE_GET_LINKEVENT 0x15
#define QLCNIC_H2C_OPCODE_CONFIG_BRIDGING 0x17
#define QLCNIC_H2C_OPCODE_CONFIG_HW_LRO 0x18
+#define QLCNIC_H2C_OPCODE_CONFIG_LOOPBACK 0x13
+
/*
* Firmware --> Driver
*/
+#define QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK 0x8f
#define QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE 141
#define VPORT_MISS_MODE_DROP 0 /* drop all unmatched */
@@ -808,6 +827,7 @@ struct qlcnic_mac_list_s {
#define QLCNIC_FW_CAPABILITY_BDG BIT_8
#define QLCNIC_FW_CAPABILITY_FVLANTX BIT_9
#define QLCNIC_FW_CAPABILITY_HW_LRO BIT_10
+#define QLCNIC_FW_CAPABILITY_MULTI_LOOPBACK BIT_27
/* module types */
#define LINKEVENT_MODULE_NOT_PRESENT 1
@@ -895,11 +915,12 @@ struct qlcnic_ipaddr {
#define QLCNIC_MAC_OVERRIDE_DISABLED 0x400
#define QLCNIC_PROMISC_DISABLED 0x800
#define QLCNIC_NEED_FLR 0x1000
+#define QLCNIC_FW_RESET_OWNER 0x2000
+#define QLCNIC_FW_HANG 0x4000
#define QLCNIC_IS_MSI_FAMILY(adapter) \
((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED))
#define QLCNIC_DEF_NUM_STS_DESC_RINGS 4
-#define QLCNIC_MIN_NUM_RSS_RINGS 2
#define QLCNIC_MSIX_TBL_SPACE 8192
#define QLCNIC_PCI_REG_MSIX_TBL 0x44
#define QLCNIC_MSIX_TBL_PGSIZE 4096
@@ -913,6 +934,7 @@ struct qlcnic_ipaddr {
#define __QLCNIC_START_FW 4
#define __QLCNIC_AER 5
#define __QLCNIC_DIAG_RES_ALLOC 6
+#define __QLCNIC_LED_ENABLE 7
#define QLCNIC_INTERRUPT_TEST 1
#define QLCNIC_LOOPBACK_TEST 2
@@ -922,6 +944,12 @@ struct qlcnic_ipaddr {
#define QLCNIC_READD_AGE 20
#define QLCNIC_LB_MAX_FILTERS 64
+/* QLCNIC Driver Error Code */
+#define QLCNIC_FW_NOT_RESPOND 51
+#define QLCNIC_TEST_IN_PROGRESS 52
+#define QLCNIC_UNDEFINED_ERROR 53
+#define QLCNIC_LB_CABLE_NOT_CONN 54
+
struct qlcnic_filter {
struct hlist_node fnode;
u8 faddr[ETH_ALEN];
@@ -993,7 +1021,7 @@ struct qlcnic_adapter {
u8 max_mac_filters;
u8 dev_state;
u8 diag_test;
- u8 diag_cnt;
+ char diag_cnt;
u8 reset_ack_timeo;
u8 dev_init_timeo;
u16 msg_enable;
@@ -1001,6 +1029,7 @@ struct qlcnic_adapter {
u8 mac_addr[ETH_ALEN];
u64 dev_rst_time;
+ u8 mac_learn;
unsigned long vlans[BITS_TO_LONGS(VLAN_N_VID)];
struct qlcnic_npar_info *npars;
@@ -1219,8 +1248,7 @@ struct __ctrl {
struct __cache {
__le32 addr;
- u8 stride;
- u8 rsvd;
+ __le16 stride;
__le16 init_tag_val;
__le32 size;
__le32 no_ops;
@@ -1318,9 +1346,12 @@ enum op_codes {
#define QLCNIC_DUMP_SKIP BIT_7
#define QLCNIC_DUMP_MASK_MIN 3
-#define QLCNIC_DUMP_MASK_DEF 0x0f
+#define QLCNIC_DUMP_MASK_DEF 0x1f
#define QLCNIC_DUMP_MASK_MAX 0xff
#define QLCNIC_FORCE_FW_DUMP_KEY 0xdeadfeed
+#define QLCNIC_ENABLE_FW_DUMP 0xaddfeed
+#define QLCNIC_DISABLE_FW_DUMP 0xbadfeed
+#define QLCNIC_FORCE_FW_RESET 0xdeaddead
struct qlcnic_dump_operations {
enum op_codes opcode;
@@ -1328,6 +1359,18 @@ struct qlcnic_dump_operations {
struct qlcnic_dump_entry *, u32 *);
};
+struct _cdrp_cmd {
+ u32 cmd;
+ u32 arg1;
+ u32 arg2;
+ u32 arg3;
+};
+
+struct qlcnic_cmd_args {
+ struct _cdrp_cmd req;
+ struct _cdrp_cmd rsp;
+};
+
int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter);
int qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config);
@@ -1370,6 +1413,9 @@ void qlcnic_pcie_sem_unlock(struct qlcnic_adapter *, int);
#define crb_win_unlock(a) \
qlcnic_pcie_sem_unlock((a), 7)
+#define __QLCNIC_MAX_LED_RATE 0xf
+#define __QLCNIC_MAX_LED_STATE 0x2
+
int qlcnic_get_board_info(struct qlcnic_adapter *adapter);
int qlcnic_wol_supported(struct qlcnic_adapter *adapter);
int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate);
@@ -1428,17 +1474,23 @@ int qlcnic_send_lro_cleanup(struct qlcnic_adapter *adapter);
void qlcnic_update_cmd_producer(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring);
void qlcnic_fetch_mac(struct qlcnic_adapter *, u32, u32, u8, u8 *);
+void qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring);
+void qlcnic_clear_lb_mode(struct qlcnic_adapter *adapter);
+int qlcnic_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode);
+
+/* Functions from qlcnic_ethtool.c */
+int qlcnic_check_loopback_buff(unsigned char *data, u8 mac[]);
/* Functions from qlcnic_main.c */
int qlcnic_reset_context(struct qlcnic_adapter *);
-u32 qlcnic_issue_cmd(struct qlcnic_adapter *adapter,
- u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd);
+void qlcnic_issue_cmd(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args *);
void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings);
int qlcnic_diag_alloc_res(struct net_device *netdev, int test);
netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
int qlcnic_validate_max_rss(struct net_device *netdev, u8 max_hw, u8 val);
int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data);
void qlcnic_dev_request_reset(struct qlcnic_adapter *);
+void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter);
/* Management functions */
int qlcnic_get_mac_address(struct qlcnic_adapter *, u8*);
@@ -1489,6 +1541,8 @@ static const struct qlcnic_brdinfo qlcnic_boards[] = {
"NC523SFP 10Gb 2-port Server Adapter"},
{0x1077, 0x8020, 0x103c, 0x3346,
"CN1000Q Dual Port Converged Network Adapter"},
+ {0x1077, 0x8020, 0x1077, 0x210,
+ "QME8242-k 10GbE Dual Port Mezzanine Card"},
{0x1077, 0x8020, 0x0, 0x0, "cLOM8214 1/10GbE Controller"},
};
diff --git a/drivers/net/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
index bab041a5c758..569a837d2ac4 100644
--- a/drivers/net/qlcnic/qlcnic_ctx.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
@@ -26,42 +26,54 @@ qlcnic_poll_rsp(struct qlcnic_adapter *adapter)
return rsp;
}
-u32
-qlcnic_issue_cmd(struct qlcnic_adapter *adapter,
- u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd)
+void
+qlcnic_issue_cmd(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args *cmd)
{
u32 rsp;
u32 signature;
- u32 rcode = QLCNIC_RCODE_SUCCESS;
struct pci_dev *pdev = adapter->pdev;
+ struct qlcnic_hardware_context *ahw = adapter->ahw;
- signature = QLCNIC_CDRP_SIGNATURE_MAKE(pci_fn, version);
+ signature = QLCNIC_CDRP_SIGNATURE_MAKE(ahw->pci_func,
+ adapter->fw_hal_version);
/* Acquire semaphore before accessing CRB */
- if (qlcnic_api_lock(adapter))
- return QLCNIC_RCODE_TIMEOUT;
+ if (qlcnic_api_lock(adapter)) {
+ cmd->rsp.cmd = QLCNIC_RCODE_TIMEOUT;
+ return;
+ }
QLCWR32(adapter, QLCNIC_SIGN_CRB_OFFSET, signature);
- QLCWR32(adapter, QLCNIC_ARG1_CRB_OFFSET, arg1);
- QLCWR32(adapter, QLCNIC_ARG2_CRB_OFFSET, arg2);
- QLCWR32(adapter, QLCNIC_ARG3_CRB_OFFSET, arg3);
- QLCWR32(adapter, QLCNIC_CDRP_CRB_OFFSET, QLCNIC_CDRP_FORM_CMD(cmd));
+ QLCWR32(adapter, QLCNIC_ARG1_CRB_OFFSET, cmd->req.arg1);
+ QLCWR32(adapter, QLCNIC_ARG2_CRB_OFFSET, cmd->req.arg2);
+ QLCWR32(adapter, QLCNIC_ARG3_CRB_OFFSET, cmd->req.arg3);
+ QLCWR32(adapter, QLCNIC_CDRP_CRB_OFFSET,
+ QLCNIC_CDRP_FORM_CMD(cmd->req.cmd));
rsp = qlcnic_poll_rsp(adapter);
if (rsp == QLCNIC_CDRP_RSP_TIMEOUT) {
dev_err(&pdev->dev, "card response timeout.\n");
- rcode = QLCNIC_RCODE_TIMEOUT;
+ cmd->rsp.cmd = QLCNIC_RCODE_TIMEOUT;
} else if (rsp == QLCNIC_CDRP_RSP_FAIL) {
- rcode = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET);
+ cmd->rsp.cmd = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET);
dev_err(&pdev->dev, "failed card response code:0x%x\n",
- rcode);
+ cmd->rsp.cmd);
+ } else if (rsp == QLCNIC_CDRP_RSP_OK) {
+ cmd->rsp.cmd = QLCNIC_RCODE_SUCCESS;
+ if (cmd->rsp.arg2)
+ cmd->rsp.arg2 = QLCRD32(adapter,
+ QLCNIC_ARG2_CRB_OFFSET);
+ if (cmd->rsp.arg3)
+ cmd->rsp.arg3 = QLCRD32(adapter,
+ QLCNIC_ARG3_CRB_OFFSET);
}
+ if (cmd->rsp.arg1)
+ cmd->rsp.arg1 = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET);
/* Release semaphore */
qlcnic_api_unlock(adapter);
- return rcode;
}
static uint32_t qlcnic_temp_checksum(uint32_t *temp_buffer, u16 temp_size)
@@ -81,27 +93,24 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
u16 temp_size;
void *tmp_addr;
u32 version, csum, *template, *tmp_buf;
+ struct qlcnic_cmd_args cmd;
struct qlcnic_hardware_context *ahw;
struct qlcnic_dump_template_hdr *tmpl_hdr, *tmp_tmpl;
dma_addr_t tmp_addr_t = 0;
ahw = adapter->ahw;
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- 0,
- 0,
- 0,
- QLCNIC_CDRP_CMD_TEMP_SIZE);
- if (err != QLCNIC_RCODE_SUCCESS) {
- err = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET);
- dev_err(&adapter->pdev->dev,
- "Failed to get template size %d\n", err);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_TEMP_SIZE;
+ memset(&cmd.rsp, 1, sizeof(struct _cdrp_cmd));
+ qlcnic_issue_cmd(adapter, &cmd);
+ if (cmd.rsp.cmd != QLCNIC_RCODE_SUCCESS) {
+ dev_info(&adapter->pdev->dev,
+ "Can't get template size %d\n", cmd.rsp.cmd);
err = -EIO;
return err;
}
- version = QLCRD32(adapter, QLCNIC_ARG3_CRB_OFFSET);
- temp_size = QLCRD32(adapter, QLCNIC_ARG2_CRB_OFFSET);
+ temp_size = cmd.rsp.arg2;
+ version = cmd.rsp.arg3;
if (!temp_size)
return -EIO;
@@ -112,21 +121,21 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
"Can't get memory for FW dump template\n");
return -ENOMEM;
}
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- LSD(tmp_addr_t),
- MSD(tmp_addr_t),
- temp_size,
- QLCNIC_CDRP_CMD_GET_TEMP_HDR);
-
+ memset(&cmd.rsp, 0, sizeof(struct _cdrp_cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_GET_TEMP_HDR;
+ cmd.req.arg1 = LSD(tmp_addr_t);
+ cmd.req.arg2 = MSD(tmp_addr_t);
+ cmd.req.arg3 = temp_size;
+ qlcnic_issue_cmd(adapter, &cmd);
+
+ err = cmd.rsp.cmd;
if (err != QLCNIC_RCODE_SUCCESS) {
dev_err(&adapter->pdev->dev,
"Failed to get mini dump template header %d\n", err);
err = -EIO;
goto error;
}
- tmp_tmpl = (struct qlcnic_dump_template_hdr *) tmp_addr;
+ tmp_tmpl = tmp_addr;
csum = qlcnic_temp_checksum((uint32_t *) tmp_addr, temp_size);
if (csum) {
dev_err(&adapter->pdev->dev,
@@ -139,17 +148,14 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
err = -EIO;
goto error;
}
- tmp_buf = (u32 *) tmp_addr;
+ tmp_buf = tmp_addr;
template = (u32 *) ahw->fw_dump.tmpl_hdr;
for (i = 0; i < temp_size/sizeof(u32); i++)
*template++ = __le32_to_cpu(*tmp_buf++);
tmpl_hdr = ahw->fw_dump.tmpl_hdr;
- if (tmpl_hdr->cap_mask > QLCNIC_DUMP_MASK_DEF &&
- tmpl_hdr->cap_mask <= QLCNIC_DUMP_MASK_MAX)
- tmpl_hdr->drv_cap_mask = tmpl_hdr->cap_mask;
- else
- tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF;
+ tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF;
+ ahw->fw_dump.enable = 1;
error:
dma_free_coherent(&adapter->pdev->dev, temp_size, tmp_addr, tmp_addr_t);
return err;
@@ -158,17 +164,17 @@ error:
int
qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu)
{
+ struct qlcnic_cmd_args cmd;
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_SET_MTU;
+ cmd.req.arg1 = recv_ctx->context_id;
+ cmd.req.arg2 = mtu;
+ cmd.req.arg3 = 0;
if (recv_ctx->state == QLCNIC_HOST_CTX_STATE_ACTIVE) {
- if (qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- recv_ctx->context_id,
- mtu,
- 0,
- QLCNIC_CDRP_CMD_SET_MTU)) {
-
+ qlcnic_issue_cmd(adapter, &cmd);
+ if (cmd.rsp.cmd) {
dev_err(&adapter->pdev->dev, "Failed to set mtu\n");
return -EIO;
}
@@ -189,6 +195,7 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
struct qlcnic_cardrsp_sds_ring *prsp_sds;
struct qlcnic_host_rds_ring *rds_ring;
struct qlcnic_host_sds_ring *sds_ring;
+ struct qlcnic_cmd_args cmd;
dma_addr_t hostrq_phys_addr, cardrsp_phys_addr;
u64 phys_addr;
@@ -214,7 +221,7 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
&hostrq_phys_addr, GFP_KERNEL);
if (addr == NULL)
return -ENOMEM;
- prq = (struct qlcnic_hostrq_rx_ctx *)addr;
+ prq = addr;
addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size,
&cardrsp_phys_addr, GFP_KERNEL);
@@ -222,7 +229,7 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
err = -ENOMEM;
goto out_free_rq;
}
- prsp = (struct qlcnic_cardrsp_rx_ctx *)addr;
+ prsp = addr;
prq->host_rsp_dma_addr = cpu_to_le64(cardrsp_phys_addr);
@@ -277,13 +284,13 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
}
phys_addr = hostrq_phys_addr;
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- (u32)(phys_addr >> 32),
- (u32)(phys_addr & 0xffffffff),
- rq_size,
- QLCNIC_CDRP_CMD_CREATE_RX_CTX);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.arg1 = (u32) (phys_addr >> 32);
+ cmd.req.arg2 = (u32) (phys_addr & 0xffffffff);
+ cmd.req.arg3 = rq_size;
+ cmd.req.cmd = QLCNIC_CDRP_CMD_CREATE_RX_CTX;
+ qlcnic_issue_cmd(adapter, &cmd);
+ err = cmd.rsp.cmd;
if (err) {
dev_err(&adapter->pdev->dev,
"Failed to create rx ctx in firmware%d\n", err);
@@ -329,19 +336,18 @@ out_free_rq:
static void
qlcnic_fw_cmd_destroy_rx_ctx(struct qlcnic_adapter *adapter)
{
+ struct qlcnic_cmd_args cmd;
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
- if (qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- recv_ctx->context_id,
- QLCNIC_DESTROY_CTX_RESET,
- 0,
- QLCNIC_CDRP_CMD_DESTROY_RX_CTX)) {
-
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.arg1 = recv_ctx->context_id;
+ cmd.req.arg2 = QLCNIC_DESTROY_CTX_RESET;
+ cmd.req.arg3 = 0;
+ cmd.req.cmd = QLCNIC_CDRP_CMD_DESTROY_RX_CTX;
+ qlcnic_issue_cmd(adapter, &cmd);
+ if (cmd.rsp.cmd)
dev_err(&adapter->pdev->dev,
"Failed to destroy rx ctx in firmware\n");
- }
recv_ctx->state = QLCNIC_HOST_CTX_STATE_FREED;
}
@@ -355,6 +361,7 @@ qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter)
void *rq_addr, *rsp_addr;
size_t rq_size, rsp_size;
u32 temp;
+ struct qlcnic_cmd_args cmd;
int err;
u64 phys_addr;
dma_addr_t rq_phys_addr, rsp_phys_addr;
@@ -380,10 +387,10 @@ qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter)
}
memset(rq_addr, 0, rq_size);
- prq = (struct qlcnic_hostrq_tx_ctx *)rq_addr;
+ prq = rq_addr;
memset(rsp_addr, 0, rsp_size);
- prsp = (struct qlcnic_cardrsp_tx_ctx *)rsp_addr;
+ prsp = rsp_addr;
prq->host_rsp_dma_addr = cpu_to_le64(rsp_phys_addr);
@@ -404,13 +411,13 @@ qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter)
prq_cds->ring_size = cpu_to_le32(tx_ring->num_desc);
phys_addr = rq_phys_addr;
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- (u32)(phys_addr >> 32),
- ((u32)phys_addr & 0xffffffff),
- rq_size,
- QLCNIC_CDRP_CMD_CREATE_TX_CTX);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.arg1 = (u32)(phys_addr >> 32);
+ cmd.req.arg2 = ((u32)phys_addr & 0xffffffff);
+ cmd.req.arg3 = rq_size;
+ cmd.req.cmd = QLCNIC_CDRP_CMD_CREATE_TX_CTX;
+ qlcnic_issue_cmd(adapter, &cmd);
+ err = cmd.rsp.cmd;
if (err == QLCNIC_RCODE_SUCCESS) {
temp = le32_to_cpu(prsp->cds_ring.host_producer_crb);
@@ -436,29 +443,30 @@ out_free_rq:
static void
qlcnic_fw_cmd_destroy_tx_ctx(struct qlcnic_adapter *adapter)
{
- if (qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- adapter->tx_context_id,
- QLCNIC_DESTROY_CTX_RESET,
- 0,
- QLCNIC_CDRP_CMD_DESTROY_TX_CTX)) {
-
+ struct qlcnic_cmd_args cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.arg1 = adapter->tx_context_id;
+ cmd.req.arg2 = QLCNIC_DESTROY_CTX_RESET;
+ cmd.req.arg3 = 0;
+ cmd.req.cmd = QLCNIC_CDRP_CMD_DESTROY_TX_CTX;
+ qlcnic_issue_cmd(adapter, &cmd);
+ if (cmd.rsp.cmd)
dev_err(&adapter->pdev->dev,
"Failed to destroy tx ctx in firmware\n");
- }
}
int
qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config)
{
- return qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- config,
- 0,
- 0,
- QLCNIC_CDRP_CMD_CONFIG_PORT);
+ struct qlcnic_cmd_args cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.arg1 = config;
+ cmd.req.cmd = QLCNIC_CDRP_CMD_CONFIG_PORT;
+ qlcnic_issue_cmd(adapter, &cmd);
+
+ return cmd.rsp.cmd;
}
int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
@@ -493,7 +501,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
goto err_out_free;
}
- tx_ring->desc_head = (struct cmd_desc_type0 *)addr;
+ tx_ring->desc_head = addr;
for (ring = 0; ring < adapter->max_rds_rings; ring++) {
rds_ring = &recv_ctx->rds_rings[ring];
@@ -506,7 +514,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
err = -ENOMEM;
goto err_out_free;
}
- rds_ring->desc_head = (struct rcv_desc *)addr;
+ rds_ring->desc_head = addr;
}
@@ -522,7 +530,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
err = -ENOMEM;
goto err_out_free;
}
- sds_ring->desc_head = (struct status_desc *)addr;
+ sds_ring->desc_head = addr;
}
return 0;
@@ -623,20 +631,17 @@ void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter)
int qlcnic_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac)
{
int err;
- u32 arg1;
+ struct qlcnic_cmd_args cmd;
- arg1 = adapter->ahw->pci_func | BIT_8;
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- arg1,
- 0,
- 0,
- QLCNIC_CDRP_CMD_MAC_ADDRESS);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.arg1 = adapter->ahw->pci_func | BIT_8;
+ cmd.req.cmd = QLCNIC_CDRP_CMD_MAC_ADDRESS;
+ cmd.rsp.arg1 = cmd.rsp.arg2 = 1;
+ qlcnic_issue_cmd(adapter, &cmd);
+ err = cmd.rsp.cmd;
if (err == QLCNIC_RCODE_SUCCESS)
- qlcnic_fetch_mac(adapter, QLCNIC_ARG1_CRB_OFFSET,
- QLCNIC_ARG2_CRB_OFFSET, 0, mac);
+ qlcnic_fetch_mac(adapter, cmd.rsp.arg1, cmd.rsp.arg2, 0, mac);
else {
dev_err(&adapter->pdev->dev,
"Failed to get mac address%d\n", err);
@@ -654,6 +659,7 @@ int qlcnic_get_nic_info(struct qlcnic_adapter *adapter,
dma_addr_t nic_dma_t;
struct qlcnic_info *nic_info;
void *nic_info_addr;
+ struct qlcnic_cmd_args cmd;
size_t nic_size = sizeof(struct qlcnic_info);
nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size,
@@ -662,14 +668,14 @@ int qlcnic_get_nic_info(struct qlcnic_adapter *adapter,
return -ENOMEM;
memset(nic_info_addr, 0, nic_size);
- nic_info = (struct qlcnic_info *) nic_info_addr;
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- MSD(nic_dma_t),
- LSD(nic_dma_t),
- (func_id << 16 | nic_size),
- QLCNIC_CDRP_CMD_GET_NIC_INFO);
+ nic_info = nic_info_addr;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_GET_NIC_INFO;
+ cmd.req.arg1 = MSD(nic_dma_t);
+ cmd.req.arg2 = LSD(nic_dma_t);
+ cmd.req.arg3 = (func_id << 16 | nic_size);
+ qlcnic_issue_cmd(adapter, &cmd);
+ err = cmd.rsp.cmd;
if (err == QLCNIC_RCODE_SUCCESS) {
npar_info->pci_func = le16_to_cpu(nic_info->pci_func);
@@ -708,6 +714,7 @@ int qlcnic_set_nic_info(struct qlcnic_adapter *adapter, struct qlcnic_info *nic)
int err = -EIO;
dma_addr_t nic_dma_t;
void *nic_info_addr;
+ struct qlcnic_cmd_args cmd;
struct qlcnic_info *nic_info;
size_t nic_size = sizeof(struct qlcnic_info);
@@ -720,7 +727,7 @@ int qlcnic_set_nic_info(struct qlcnic_adapter *adapter, struct qlcnic_info *nic)
return -ENOMEM;
memset(nic_info_addr, 0, nic_size);
- nic_info = (struct qlcnic_info *)nic_info_addr;
+ nic_info = nic_info_addr;
nic_info->pci_func = cpu_to_le16(nic->pci_func);
nic_info->op_mode = cpu_to_le16(nic->op_mode);
@@ -733,13 +740,13 @@ int qlcnic_set_nic_info(struct qlcnic_adapter *adapter, struct qlcnic_info *nic)
nic_info->min_tx_bw = cpu_to_le16(nic->min_tx_bw);
nic_info->max_tx_bw = cpu_to_le16(nic->max_tx_bw);
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- MSD(nic_dma_t),
- LSD(nic_dma_t),
- ((nic->pci_func << 16) | nic_size),
- QLCNIC_CDRP_CMD_SET_NIC_INFO);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_SET_NIC_INFO;
+ cmd.req.arg1 = MSD(nic_dma_t);
+ cmd.req.arg2 = LSD(nic_dma_t);
+ cmd.req.arg3 = ((nic->pci_func << 16) | nic_size);
+ qlcnic_issue_cmd(adapter, &cmd);
+ err = cmd.rsp.cmd;
if (err != QLCNIC_RCODE_SUCCESS) {
dev_err(&adapter->pdev->dev,
@@ -757,6 +764,7 @@ int qlcnic_get_pci_info(struct qlcnic_adapter *adapter,
struct qlcnic_pci_info *pci_info)
{
int err = 0, i;
+ struct qlcnic_cmd_args cmd;
dma_addr_t pci_info_dma_t;
struct qlcnic_pci_info *npar;
void *pci_info_addr;
@@ -769,14 +777,14 @@ int qlcnic_get_pci_info(struct qlcnic_adapter *adapter,
return -ENOMEM;
memset(pci_info_addr, 0, pci_size);
- npar = (struct qlcnic_pci_info *) pci_info_addr;
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- MSD(pci_info_dma_t),
- LSD(pci_info_dma_t),
- pci_size,
- QLCNIC_CDRP_CMD_GET_PCI_INFO);
+ npar = pci_info_addr;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_GET_PCI_INFO;
+ cmd.req.arg1 = MSD(pci_info_dma_t);
+ cmd.req.arg2 = LSD(pci_info_dma_t);
+ cmd.req.arg3 = pci_size;
+ qlcnic_issue_cmd(adapter, &cmd);
+ err = cmd.rsp.cmd;
if (err == QLCNIC_RCODE_SUCCESS) {
for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++, npar++, pci_info++) {
@@ -808,6 +816,7 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
{
int err = -EIO;
u32 arg1;
+ struct qlcnic_cmd_args cmd;
if (adapter->op_mode != QLCNIC_MGMT_FUNC ||
!(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE))
@@ -816,13 +825,11 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
arg1 = id | (enable_mirroring ? BIT_4 : 0);
arg1 |= pci_func << 8;
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- arg1,
- 0,
- 0,
- QLCNIC_CDRP_CMD_SET_PORTMIRRORING);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_SET_PORTMIRRORING;
+ cmd.req.arg1 = arg1;
+ qlcnic_issue_cmd(adapter, &cmd);
+ err = cmd.rsp.cmd;
if (err != QLCNIC_RCODE_SUCCESS) {
dev_err(&adapter->pdev->dev,
@@ -845,6 +852,7 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
dma_addr_t stats_dma_t;
void *stats_addr;
u32 arg1;
+ struct qlcnic_cmd_args cmd;
int err;
if (esw_stats == NULL)
@@ -868,16 +876,16 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
arg1 = func | QLCNIC_STATS_VERSION << 8 | QLCNIC_STATS_PORT << 12;
arg1 |= rx_tx << 15 | stats_size << 16;
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- arg1,
- MSD(stats_dma_t),
- LSD(stats_dma_t),
- QLCNIC_CDRP_CMD_GET_ESWITCH_STATS);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_GET_ESWITCH_STATS;
+ cmd.req.arg1 = arg1;
+ cmd.req.arg2 = MSD(stats_dma_t);
+ cmd.req.arg3 = LSD(stats_dma_t);
+ qlcnic_issue_cmd(adapter, &cmd);
+ err = cmd.rsp.cmd;
if (!err) {
- stats = (struct __qlcnic_esw_statistics *)stats_addr;
+ stats = stats_addr;
esw_stats->context_id = le16_to_cpu(stats->context_id);
esw_stats->version = le16_to_cpu(stats->version);
esw_stats->size = le16_to_cpu(stats->size);
@@ -955,6 +963,7 @@ int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw,
{
u32 arg1;
+ struct qlcnic_cmd_args cmd;
if (adapter->op_mode != QLCNIC_MGMT_FUNC)
return -EIO;
@@ -975,13 +984,11 @@ int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw,
arg1 = port | QLCNIC_STATS_VERSION << 8 | func_esw << 12;
arg1 |= BIT_14 | rx_tx << 15;
- return qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- arg1,
- 0,
- 0,
- QLCNIC_CDRP_CMD_GET_ESWITCH_STATS);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_GET_ESWITCH_STATS;
+ cmd.req.arg1 = arg1;
+ qlcnic_issue_cmd(adapter, &cmd);
+ return cmd.rsp.cmd;
err_ret:
dev_err(&adapter->pdev->dev, "Invalid argument func_esw=%d port=%d"
@@ -994,19 +1001,19 @@ __qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
u32 *arg1, u32 *arg2)
{
int err = -EIO;
+ struct qlcnic_cmd_args cmd;
u8 pci_func;
pci_func = (*arg1 >> 8);
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- *arg1,
- 0,
- 0,
- QLCNIC_CDRP_CMD_GET_ESWITCH_PORT_CONFIG);
+
+ cmd.req.cmd = QLCNIC_CDRP_CMD_GET_ESWITCH_PORT_CONFIG;
+ cmd.req.arg1 = *arg1;
+ cmd.rsp.arg1 = cmd.rsp.arg2 = 1;
+ qlcnic_issue_cmd(adapter, &cmd);
+ *arg1 = cmd.rsp.arg1;
+ *arg2 = cmd.rsp.arg2;
+ err = cmd.rsp.cmd;
if (err == QLCNIC_RCODE_SUCCESS) {
- *arg1 = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET);
- *arg2 = QLCRD32(adapter, QLCNIC_ARG2_CRB_OFFSET);
dev_info(&adapter->pdev->dev,
"eSwitch port config for pci func %d\n", pci_func);
} else {
@@ -1028,6 +1035,7 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
{
int err = -EIO;
u32 arg1, arg2 = 0;
+ struct qlcnic_cmd_args cmd;
u8 pci_func;
if (adapter->op_mode != QLCNIC_MGMT_FUNC)
@@ -1074,14 +1082,13 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
return err;
}
- err = qlcnic_issue_cmd(adapter,
- adapter->ahw->pci_func,
- adapter->fw_hal_version,
- arg1,
- arg2,
- 0,
- QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH;
+ cmd.req.arg1 = arg1;
+ cmd.req.arg2 = arg2;
+ qlcnic_issue_cmd(adapter, &cmd);
+ err = cmd.rsp.cmd;
if (err != QLCNIC_RCODE_SUCCESS) {
dev_err(&adapter->pdev->dev,
"Failed to configure eswitch pci func %d\n", pci_func);
diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index 9efc690a289f..8aa1c6e8667b 100644
--- a/drivers/net/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -84,7 +84,9 @@ static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = {
static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = {
"Register_Test_on_offline",
"Link_Test_on_offline",
- "Interrupt_Test_offline"
+ "Interrupt_Test_offline",
+ "Internal_Loopback_offline",
+ "External_Loopback_offline"
};
#define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test)
@@ -416,9 +418,6 @@ qlcnic_get_ringparam(struct net_device *dev,
ring->rx_max_pending = adapter->max_rxd;
ring->rx_jumbo_max_pending = adapter->max_jumbo_rxd;
ring->tx_max_pending = MAX_CMD_DESCRIPTORS;
-
- ring->rx_mini_max_pending = 0;
- ring->rx_mini_pending = 0;
}
static u32
@@ -657,6 +656,7 @@ static int qlcnic_irq_test(struct net_device *netdev)
struct qlcnic_adapter *adapter = netdev_priv(netdev);
int max_sds_rings = adapter->max_sds_rings;
int ret;
+ struct qlcnic_cmd_args cmd;
if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
return -EIO;
@@ -666,9 +666,12 @@ static int qlcnic_irq_test(struct net_device *netdev)
goto clear_it;
adapter->diag_cnt = 0;
- ret = qlcnic_issue_cmd(adapter, adapter->ahw->pci_func,
- adapter->fw_hal_version, adapter->ahw->pci_func,
- 0, 0, 0x00000011);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.req.cmd = QLCNIC_CDRP_CMD_INTRPT_TEST;
+ cmd.req.arg1 = adapter->ahw->pci_func;
+ qlcnic_issue_cmd(adapter, &cmd);
+ ret = cmd.rsp.cmd;
+
if (ret)
goto done;
@@ -685,6 +688,134 @@ clear_it:
return ret;
}
+#define QLCNIC_ILB_PKT_SIZE 64
+#define QLCNIC_NUM_ILB_PKT 16
+#define QLCNIC_ILB_MAX_RCV_LOOP 10
+
+static void qlcnic_create_loopback_buff(unsigned char *data, u8 mac[])
+{
+ unsigned char random_data[] = {0xa8, 0x06, 0x45, 0x00};
+
+ memset(data, 0x4e, QLCNIC_ILB_PKT_SIZE);
+
+ memcpy(data, mac, ETH_ALEN);
+ memcpy(data + ETH_ALEN, mac, ETH_ALEN);
+
+ memcpy(data + 2 * ETH_ALEN, random_data, sizeof(random_data));
+}
+
+int qlcnic_check_loopback_buff(unsigned char *data, u8 mac[])
+{
+ unsigned char buff[QLCNIC_ILB_PKT_SIZE];
+ qlcnic_create_loopback_buff(buff, mac);
+ return memcmp(data, buff, QLCNIC_ILB_PKT_SIZE);
+}
+
+static int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode)
+{
+ struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+ struct qlcnic_host_sds_ring *sds_ring = &recv_ctx->sds_rings[0];
+ struct sk_buff *skb;
+ int i, loop, cnt = 0;
+
+ for (i = 0; i < QLCNIC_NUM_ILB_PKT; i++) {
+ skb = dev_alloc_skb(QLCNIC_ILB_PKT_SIZE);
+ qlcnic_create_loopback_buff(skb->data, adapter->mac_addr);
+ skb_put(skb, QLCNIC_ILB_PKT_SIZE);
+
+ adapter->diag_cnt = 0;
+ qlcnic_xmit_frame(skb, adapter->netdev);
+
+ loop = 0;
+ do {
+ msleep(1);
+ qlcnic_process_rcv_ring_diag(sds_ring);
+ if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP)
+ break;
+ } while (!adapter->diag_cnt);
+
+ dev_kfree_skb_any(skb);
+
+ if (!adapter->diag_cnt)
+ QLCDB(adapter, DRV,
+ "LB Test: packet #%d was not received\n", i + 1);
+ else
+ cnt++;
+ }
+ if (cnt != i) {
+ dev_warn(&adapter->pdev->dev, "LB Test failed\n");
+ if (mode != QLCNIC_ILB_MODE) {
+ dev_warn(&adapter->pdev->dev,
+ "WARNING: Please make sure external"
+ "loopback connector is plugged in\n");
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static int qlcnic_loopback_test(struct net_device *netdev, u8 mode)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ int max_sds_rings = adapter->max_sds_rings;
+ struct qlcnic_host_sds_ring *sds_ring;
+ int loop = 0;
+ int ret;
+
+ if (!(adapter->capabilities & QLCNIC_FW_CAPABILITY_MULTI_LOOPBACK)) {
+ netdev_info(netdev, "Firmware is not loopback test capable\n");
+ return -EOPNOTSUPP;
+ }
+
+ QLCDB(adapter, DRV, "%s loopback test in progress\n",
+ mode == QLCNIC_ILB_MODE ? "internal" : "external");
+ if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) {
+ netdev_warn(netdev, "Loopback test not supported for non "
+ "privilege function\n");
+ return 0;
+ }
+
+ if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
+ return -EBUSY;
+
+ ret = qlcnic_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST);
+ if (ret)
+ goto clear_it;
+
+ sds_ring = &adapter->recv_ctx->sds_rings[0];
+
+ ret = qlcnic_set_lb_mode(adapter, mode);
+ if (ret)
+ goto free_res;
+
+ adapter->diag_cnt = 0;
+ do {
+ msleep(500);
+ qlcnic_process_rcv_ring_diag(sds_ring);
+ if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
+ netdev_info(netdev, "firmware didnt respond to loopback"
+ " configure request\n");
+ ret = -QLCNIC_FW_NOT_RESPOND;
+ goto free_res;
+ } else if (adapter->diag_cnt) {
+ ret = adapter->diag_cnt;
+ goto free_res;
+ }
+ } while (!QLCNIC_IS_LB_CONFIGURED(adapter->ahw->loopback_state));
+
+ ret = qlcnic_do_lb_test(adapter, mode);
+
+ qlcnic_clear_lb_mode(adapter);
+
+ free_res:
+ qlcnic_diag_free_res(netdev, max_sds_rings);
+
+ clear_it:
+ adapter->max_sds_rings = max_sds_rings;
+ clear_bit(__QLCNIC_RESETTING, &adapter->state);
+ return ret;
+}
+
static void
qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test,
u64 *data)
@@ -704,7 +835,15 @@ qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test,
if (data[2])
eth_test->flags |= ETH_TEST_FL_FAILED;
-
+ data[3] = qlcnic_loopback_test(dev, QLCNIC_ILB_MODE);
+ if (data[3])
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+ if (eth_test->flags & ETH_TEST_FL_EXTERNAL_LB) {
+ data[4] = qlcnic_loopback_test(dev, QLCNIC_ELB_MODE);
+ if (data[4])
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+ eth_test->flags |= ETH_TEST_FL_EXTERNAL_LB_DONE;
+ }
}
}
@@ -796,28 +935,49 @@ static int qlcnic_set_led(struct net_device *dev,
{
struct qlcnic_adapter *adapter = netdev_priv(dev);
int max_sds_rings = adapter->max_sds_rings;
+ int err = -EIO, active = 1;
+
+ if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) {
+ netdev_warn(dev, "LED test not supported for non "
+ "privilege function\n");
+ return -EOPNOTSUPP;
+ }
switch (state) {
case ETHTOOL_ID_ACTIVE:
- if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
- if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
- return -EIO;
+ if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state))
+ return -EBUSY;
- if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST)) {
- clear_bit(__QLCNIC_RESETTING, &adapter->state);
- return -EIO;
- }
+ if (test_bit(__QLCNIC_RESETTING, &adapter->state))
+ break;
+
+ if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
+ if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST))
+ break;
set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state);
}
- if (adapter->nic_ops->config_led(adapter, 1, 0xf) == 0)
- return 0;
+ if (adapter->nic_ops->config_led(adapter, 1, 0xf) == 0) {
+ err = 0;
+ break;
+ }
dev_err(&adapter->pdev->dev,
"Failed to set LED blink state.\n");
break;
case ETHTOOL_ID_INACTIVE:
+ active = 0;
+
+ if (test_bit(__QLCNIC_RESETTING, &adapter->state))
+ break;
+
+ if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
+ if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST))
+ break;
+ set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state);
+ }
+
if (adapter->nic_ops->config_led(adapter, 0, 0xf))
dev_err(&adapter->pdev->dev,
"Failed to reset LED blink state.\n");
@@ -828,12 +988,13 @@ static int qlcnic_set_led(struct net_device *dev,
return -EINVAL;
}
- if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) {
+ if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state))
qlcnic_diag_free_res(dev, max_sds_rings);
- clear_bit(__QLCNIC_RESETTING, &adapter->state);
- }
- return -EIO;
+ if (!active || err)
+ clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
+
+ return err;
}
static void
@@ -971,7 +1132,10 @@ qlcnic_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump)
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
- dump->len = fw_dump->tmpl_hdr->size + fw_dump->size;
+ if (fw_dump->clr)
+ dump->len = fw_dump->tmpl_hdr->size + fw_dump->size;
+ else
+ dump->len = 0;
dump->flag = fw_dump->tmpl_hdr->drv_cap_mask;
dump->version = adapter->fw_version;
return 0;
@@ -986,8 +1150,6 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
- if (qlcnic_api_lock(adapter))
- return -EIO;
if (!fw_dump->clr) {
netdev_info(netdev, "Dump not available\n");
qlcnic_api_unlock(adapter);
@@ -996,7 +1158,7 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
/* Copy template header first */
copy_sz = fw_dump->tmpl_hdr->size;
hdr_ptr = (u32 *) fw_dump->tmpl_hdr;
- data = (u32 *) buffer;
+ data = buffer;
for (i = 0; i < copy_sz/sizeof(u32); i++)
*data++ = cpu_to_le32(*hdr_ptr++);
@@ -1009,7 +1171,6 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
vfree(fw_dump->data);
fw_dump->data = NULL;
fw_dump->clr = 0;
- qlcnic_api_unlock(adapter);
return 0;
}
@@ -1021,10 +1182,38 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val)
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
- if (val->flag == QLCNIC_FORCE_FW_DUMP_KEY) {
+ switch (val->flag) {
+ case QLCNIC_FORCE_FW_DUMP_KEY:
+ if (!fw_dump->enable) {
+ netdev_info(netdev, "FW dump not enabled\n");
+ return ret;
+ }
+ if (fw_dump->clr) {
+ dev_info(&adapter->pdev->dev,
+ "Previous dump not cleared, not forcing dump\n");
+ return ret;
+ }
netdev_info(netdev, "Forcing a FW dump\n");
qlcnic_dev_request_reset(adapter);
- } else {
+ break;
+ case QLCNIC_DISABLE_FW_DUMP:
+ if (fw_dump->enable) {
+ netdev_info(netdev, "Disabling FW dump\n");
+ fw_dump->enable = 0;
+ }
+ break;
+ case QLCNIC_ENABLE_FW_DUMP:
+ if (!fw_dump->enable && fw_dump->tmpl_hdr) {
+ netdev_info(netdev, "Enabling FW dump\n");
+ fw_dump->enable = 1;
+ }
+ break;
+ case QLCNIC_FORCE_FW_RESET:
+ netdev_info(netdev, "Forcing a FW reset\n");
+ qlcnic_dev_request_reset(adapter);
+ adapter->flags &= ~QLCNIC_FW_RESET_OWNER;
+ break;
+ default:
if (val->flag > QLCNIC_DUMP_MASK_MAX ||
val->flag < QLCNIC_DUMP_MASK_MIN) {
netdev_info(netdev,
@@ -1032,10 +1221,7 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val)
ret = -EINVAL;
goto out;
}
- if (qlcnic_api_lock(adapter))
- return -EIO;
fw_dump->tmpl_hdr->drv_cap_mask = val->flag & 0xff;
- qlcnic_api_unlock(adapter);
netdev_info(netdev, "Driver mask changed to: 0x%x\n",
fw_dump->tmpl_hdr->drv_cap_mask);
}
diff --git a/drivers/net/qlcnic/qlcnic_hdr.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
index d14506f764e0..a52819303d1b 100644
--- a/drivers/net/qlcnic/qlcnic_hdr.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
@@ -407,7 +407,9 @@ enum {
#define QLCNIC_CRB_SRE QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_SRE)
#define QLCNIC_CRB_ROMUSB \
QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_ROMUSB)
+#define QLCNIC_CRB_EPG QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_EG)
#define QLCNIC_CRB_I2Q QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_I2Q)
+#define QLCNIC_CRB_TIMER QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_TIMR)
#define QLCNIC_CRB_I2C0 QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_I2C0)
#define QLCNIC_CRB_SMB QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_SMB)
#define QLCNIC_CRB_MAX QLCNIC_PCI_CRB_WINDOW(64)
@@ -609,6 +611,7 @@ enum {
QLCNIC_TEMP_PANIC /* Fatal error, hardware has shut down. */
};
+
/* Lock IDs for PHY lock */
#define PHY_LOCK_DRIVER 0x44524956
@@ -723,7 +726,8 @@ enum {
#define QLCNIC_RCODE_DRIVER_CAN_RELOAD BIT_30
#define QLCNIC_RCODE_FATAL_ERROR BIT_31
#define QLCNIC_FWERROR_PEGNUM(code) ((code) & 0xff)
-#define QLCNIC_FWERROR_CODE(code) ((code >> 8) & 0xfffff)
+#define QLCNIC_FWERROR_CODE(code) ((code >> 8) & 0x1fffff)
+#define QLCNIC_FWERROR_FAN_FAILURE 0x16
#define FW_POLL_DELAY (1 * HZ)
#define FW_FAIL_THRESH 2
diff --git a/drivers/net/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
index a5d9fbf9d816..bcb81e47543a 100644
--- a/drivers/net/qlcnic/qlcnic_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
@@ -446,6 +446,13 @@ void qlcnic_set_multi(struct net_device *netdev)
}
send_fw_cmd:
+ if (mode == VPORT_MISS_MODE_ACCEPT_ALL) {
+ qlcnic_alloc_lb_filters_mem(adapter);
+ adapter->mac_learn = 1;
+ } else {
+ adapter->mac_learn = 0;
+ }
+
qlcnic_nic_set_promisc(adapter, mode);
}
@@ -533,6 +540,56 @@ void qlcnic_delete_lb_filters(struct qlcnic_adapter *adapter)
}
}
+int qlcnic_set_fw_loopback(struct qlcnic_adapter *adapter, u8 flag)
+{
+ struct qlcnic_nic_req req;
+ int rv;
+
+ memset(&req, 0, sizeof(struct qlcnic_nic_req));
+
+ req.qhdr = cpu_to_le64(QLCNIC_HOST_REQUEST << 23);
+ req.req_hdr = cpu_to_le64(QLCNIC_H2C_OPCODE_CONFIG_LOOPBACK |
+ ((u64) adapter->portnum << 16) | ((u64) 0x1 << 32));
+
+ req.words[0] = cpu_to_le64(flag);
+
+ rv = qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);
+ if (rv != 0)
+ dev_err(&adapter->pdev->dev, "%sting loopback mode failed\n",
+ flag ? "Set" : "Reset");
+ return rv;
+}
+
+int qlcnic_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
+{
+ if (qlcnic_set_fw_loopback(adapter, mode))
+ return -EIO;
+
+ if (qlcnic_nic_set_promisc(adapter, VPORT_MISS_MODE_ACCEPT_ALL)) {
+ qlcnic_set_fw_loopback(adapter, 0);
+ return -EIO;
+ }
+
+ msleep(1000);
+ return 0;
+}
+
+void qlcnic_clear_lb_mode(struct qlcnic_adapter *adapter)
+{
+ int mode = VPORT_MISS_MODE_DROP;
+ struct net_device *netdev = adapter->netdev;
+
+ qlcnic_set_fw_loopback(adapter, 0);
+
+ if (netdev->flags & IFF_PROMISC)
+ mode = VPORT_MISS_MODE_ACCEPT_ALL;
+ else if (netdev->flags & IFF_ALLMULTI)
+ mode = VPORT_MISS_MODE_ACCEPT_MULTI;
+
+ qlcnic_nic_set_promisc(adapter, mode);
+ msleep(1000);
+}
+
/*
* Send the interrupt coalescing parameter set by ethtool to the card.
*/
@@ -1509,18 +1566,26 @@ qlcnic_dump_l2_cache(struct qlcnic_adapter *adapter,
for (i = 0; i < l2->no_ops; i++) {
QLCNIC_WR_DUMP_REG(l2->addr, base, val);
- do {
+ if (LSW(l2->ctrl_val))
QLCNIC_WR_DUMP_REG(l2->ctrl_addr, base,
LSW(l2->ctrl_val));
+ if (!poll_mask)
+ goto skip_poll;
+ do {
QLCNIC_RD_DUMP_REG(l2->ctrl_addr, base, &data);
if (!(data & poll_mask))
break;
msleep(1);
time_out++;
} while (time_out <= poll_to);
- if (time_out > poll_to)
- return -EINVAL;
+ if (time_out > poll_to) {
+ dev_err(&adapter->pdev->dev,
+ "Timeout exceeded in %s, aborting dump\n",
+ __func__);
+ return -EINVAL;
+ }
+skip_poll:
addr = l2->read_addr;
cnt = l2->read_addr_num;
while (cnt) {
@@ -1673,8 +1738,7 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
tmpl_hdr->sys_info[1] = adapter->fw_version;
for (i = 0; i < no_entries; i++) {
- entry = (struct qlcnic_dump_entry *) ((void *) tmpl_hdr +
- entry_offset);
+ entry = (void *)tmpl_hdr + entry_offset;
if (!(entry->hdr.mask & tmpl_hdr->drv_cap_mask)) {
entry->hdr.flags |= QLCNIC_DUMP_SKIP;
entry_offset += entry->hdr.offset;
@@ -1709,8 +1773,8 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
goto error;
} else {
fw_dump->clr = 1;
- snprintf(mesg, sizeof(mesg), "FW dump for device: %d\n",
- adapter->pdev->devfn);
+ snprintf(mesg, sizeof(mesg), "FW_DUMP=%s",
+ adapter->netdev->name);
dev_info(&adapter->pdev->dev, "Dump data, %d bytes captured\n",
fw_dump->size);
/* Send a udev event to notify availability of FW dump */
diff --git a/drivers/net/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
index 5b8bbcf904d5..38669583840c 100644
--- a/drivers/net/qlcnic/qlcnic_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
@@ -422,9 +422,53 @@ int qlcnic_pinit_from_rom(struct qlcnic_adapter *adapter)
QLCWR32(adapter, CRB_CMDPEG_STATE, 0);
QLCWR32(adapter, CRB_RCVPEG_STATE, 0);
- qlcnic_rom_lock(adapter);
- QLCWR32(adapter, QLCNIC_ROMUSB_GLB_SW_RESET, 0xfeffffff);
+ /* Halt all the indiviual PEGs and other blocks */
+ /* disable all I2Q */
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x10, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x14, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x18, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x1c, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x20, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x24, 0x0);
+
+ /* disable all niu interrupts */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0x40, 0xff);
+ /* disable xge rx/tx */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0x70000, 0x00);
+ /* disable xg1 rx/tx */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0x80000, 0x00);
+ /* disable sideband mac */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0x90000, 0x00);
+ /* disable ap0 mac */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0xa0000, 0x00);
+ /* disable ap1 mac */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0xb0000, 0x00);
+
+ /* halt sre */
+ val = QLCRD32(adapter, QLCNIC_CRB_SRE + 0x1000);
+ QLCWR32(adapter, QLCNIC_CRB_SRE + 0x1000, val & (~(0x1)));
+
+ /* halt epg */
+ QLCWR32(adapter, QLCNIC_CRB_EPG + 0x1300, 0x1);
+
+ /* halt timers */
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x0, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x8, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x10, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x18, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x100, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x200, 0x0);
+ /* halt pegs */
+ QLCWR32(adapter, QLCNIC_CRB_PEG_NET_0 + 0x3c, 1);
+ QLCWR32(adapter, QLCNIC_CRB_PEG_NET_1 + 0x3c, 1);
+ QLCWR32(adapter, QLCNIC_CRB_PEG_NET_2 + 0x3c, 1);
+ QLCWR32(adapter, QLCNIC_CRB_PEG_NET_3 + 0x3c, 1);
+ QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0x3c, 1);
+ msleep(20);
+
qlcnic_rom_unlock(adapter);
+ /* big hammer don't reset CAM block on reset */
+ QLCWR32(adapter, QLCNIC_ROMUSB_GLB_SW_RESET, 0xfeffffff);
/* Init HW CRB block */
if (qlcnic_rom_fast_read(adapter, 0, &n) != 0 || (n != 0xcafecafe) ||
@@ -522,8 +566,10 @@ int qlcnic_pinit_from_rom(struct qlcnic_adapter *adapter)
QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0x8, 0);
QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0xc, 0);
msleep(1);
+
QLCWR32(adapter, QLCNIC_PEG_HALT_STATUS1, 0);
QLCWR32(adapter, QLCNIC_PEG_HALT_STATUS2, 0);
+
return 0;
}
@@ -686,7 +732,13 @@ qlcnic_check_flash_fw_ver(struct qlcnic_adapter *adapter)
u32 ver = -1, min_ver;
int ret;
- ret = qlcnic_get_flt_entry(adapter, QLCNIC_FW_IMAGE_REGION, &fw_entry);
+ if (adapter->ahw->revision_id == QLCNIC_P3P_C0)
+ ret = qlcnic_get_flt_entry(adapter, QLCNIC_C0_FW_IMAGE_REGION,
+ &fw_entry);
+ else
+ ret = qlcnic_get_flt_entry(adapter, QLCNIC_B0_FW_IMAGE_REGION,
+ &fw_entry);
+
if (!ret)
/* 0-4:-signature, 4-8:-fw version */
qlcnic_rom_fast_read(adapter, fw_entry.start_addr + 4,
@@ -1056,7 +1108,8 @@ qlcnic_check_fw_hearbeat(struct qlcnic_adapter *adapter)
int
qlcnic_need_fw_reset(struct qlcnic_adapter *adapter)
{
- if (qlcnic_check_fw_hearbeat(adapter)) {
+ if ((adapter->flags & QLCNIC_FW_HANG) ||
+ qlcnic_check_fw_hearbeat(adapter)) {
qlcnic_rom_lock_recovery(adapter);
return 1;
}
@@ -1281,6 +1334,7 @@ qlcnic_handle_linkevent(struct qlcnic_adapter *adapter,
u16 cable_len;
u16 link_speed;
u8 link_status, module, duplex, autoneg;
+ u8 lb_status = 0;
struct net_device *netdev = adapter->netdev;
adapter->has_link_events = 1;
@@ -1292,6 +1346,7 @@ qlcnic_handle_linkevent(struct qlcnic_adapter *adapter,
link_status = msg->body[2] & 0xff;
duplex = (msg->body[2] >> 16) & 0xff;
autoneg = (msg->body[2] >> 24) & 0xff;
+ lb_status = (msg->body[2] >> 32) & 0x3;
module = (msg->body[2] >> 8) & 0xff;
if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE)
@@ -1301,6 +1356,10 @@ qlcnic_handle_linkevent(struct qlcnic_adapter *adapter,
dev_info(&netdev->dev, "unsupported cable length %d\n",
cable_len);
+ if (!link_status && (lb_status == QLCNIC_ILB_MODE ||
+ lb_status == QLCNIC_ELB_MODE))
+ adapter->ahw->loopback_state |= QLCNIC_LINKEVENT;
+
qlcnic_advert_link_change(adapter, link_status);
if (duplex == LINKEVENT_FULL_DUPLEX)
@@ -1319,7 +1378,9 @@ qlcnic_handle_fw_message(int desc_cnt, int index,
{
struct qlcnic_fw_msg msg;
struct status_desc *desc;
- int i = 0, opcode;
+ struct qlcnic_adapter *adapter;
+ struct device *dev;
+ int i = 0, opcode, ret;
while (desc_cnt > 0 && i < 8) {
desc = &sds_ring->desc_head[index];
@@ -1330,10 +1391,34 @@ qlcnic_handle_fw_message(int desc_cnt, int index,
desc_cnt--;
}
+ adapter = sds_ring->adapter;
+ dev = &adapter->pdev->dev;
opcode = qlcnic_get_nic_msg_opcode(msg.body[0]);
+
switch (opcode) {
case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE:
- qlcnic_handle_linkevent(sds_ring->adapter, &msg);
+ qlcnic_handle_linkevent(adapter, &msg);
+ break;
+ case QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK:
+ ret = (u32)(msg.body[1]);
+ switch (ret) {
+ case 0:
+ adapter->ahw->loopback_state |= QLCNIC_LB_RESPONSE;
+ break;
+ case 1:
+ dev_info(dev, "loopback already in progress\n");
+ adapter->diag_cnt = -QLCNIC_TEST_IN_PROGRESS;
+ break;
+ case 2:
+ dev_info(dev, "loopback cable is not connected\n");
+ adapter->diag_cnt = -QLCNIC_LB_CABLE_NOT_CONN;
+ break;
+ default:
+ dev_info(dev, "loopback configure request failed,"
+ " ret %x\n", ret);
+ adapter->diag_cnt = -QLCNIC_UNDEFINED_ERROR;
+ break;
+ }
break;
default:
break;
@@ -1746,6 +1831,103 @@ qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter,
spin_unlock(&rds_ring->lock);
}
+static void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter)
+{
+ int i;
+ unsigned char *data = skb->data;
+
+ printk(KERN_INFO "\n");
+ for (i = 0; i < skb->len; i++) {
+ QLCDB(adapter, DRV, "%02x ", data[i]);
+ if ((i & 0x0f) == 8)
+ printk(KERN_INFO "\n");
+ }
+}
+
+void qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter,
+ struct qlcnic_host_sds_ring *sds_ring,
+ int ring, u64 sts_data0)
+{
+ struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+ struct sk_buff *skb;
+ struct qlcnic_host_rds_ring *rds_ring;
+ int index, length, cksum, pkt_offset;
+
+ if (unlikely(ring >= adapter->max_rds_rings))
+ return;
+
+ rds_ring = &recv_ctx->rds_rings[ring];
+
+ index = qlcnic_get_sts_refhandle(sts_data0);
+ length = qlcnic_get_sts_totallength(sts_data0);
+ if (unlikely(index >= rds_ring->num_desc))
+ return;
+
+ cksum = qlcnic_get_sts_status(sts_data0);
+ pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0);
+
+ skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
+ if (!skb)
+ return;
+
+ if (length > rds_ring->skb_size)
+ skb_put(skb, rds_ring->skb_size);
+ else
+ skb_put(skb, length);
+
+ if (pkt_offset)
+ skb_pull(skb, pkt_offset);
+
+ if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr))
+ adapter->diag_cnt++;
+ else
+ dump_skb(skb, adapter);
+
+ dev_kfree_skb_any(skb);
+ adapter->stats.rx_pkts++;
+ adapter->stats.rxbytes += length;
+
+ return;
+}
+
+void
+qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring)
+{
+ struct qlcnic_adapter *adapter = sds_ring->adapter;
+ struct status_desc *desc;
+ u64 sts_data0;
+ int ring, opcode, desc_cnt;
+
+ u32 consumer = sds_ring->consumer;
+
+ desc = &sds_ring->desc_head[consumer];
+ sts_data0 = le64_to_cpu(desc->status_desc_data[0]);
+
+ if (!(sts_data0 & STATUS_OWNER_HOST))
+ return;
+
+ desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0);
+ opcode = qlcnic_get_sts_opcode(sts_data0);
+ switch (opcode) {
+ case QLCNIC_RESPONSE_DESC:
+ qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring);
+ break;
+ default:
+ ring = qlcnic_get_sts_type(sts_data0);
+ qlcnic_process_rcv_diag(adapter, sds_ring, ring, sts_data0);
+ break;
+ }
+
+ for (; desc_cnt > 0; desc_cnt--) {
+ desc = &sds_ring->desc_head[consumer];
+ desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM);
+ consumer = get_next_index(consumer, sds_ring->num_desc);
+ }
+
+ sds_ring->consumer = consumer;
+ writel(consumer, sds_ring->crb_sts_consumer);
+}
+
void
qlcnic_fetch_mac(struct qlcnic_adapter *adapter, u32 off1, u32 off2,
u8 alt_mac, u8 *mac)
@@ -1753,8 +1935,8 @@ qlcnic_fetch_mac(struct qlcnic_adapter *adapter, u32 off1, u32 off2,
u32 mac_low, mac_high;
int i;
- mac_low = QLCRD32(adapter, off1);
- mac_high = QLCRD32(adapter, off2);
+ mac_low = off1;
+ mac_high = off2;
if (alt_mac) {
mac_low |= (mac_low >> 16) | (mac_high << 16);
diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 0f6af5c61a7c..0bd163828e33 100644
--- a/drivers/net/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -90,7 +90,6 @@ static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev);
static void qlcnic_restore_indev_addr(struct net_device *dev, unsigned long);
static int qlcnic_start_firmware(struct qlcnic_adapter *);
-static void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter);
static void qlcnic_free_lb_filters_mem(struct qlcnic_adapter *adapter);
static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *);
static int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32);
@@ -326,7 +325,7 @@ static const struct net_device_ops qlcnic_netdev_ops = {
.ndo_start_xmit = qlcnic_xmit_frame,
.ndo_get_stats = qlcnic_get_stats,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = qlcnic_set_multi,
+ .ndo_set_rx_mode = qlcnic_set_multi,
.ndo_set_mac_address = qlcnic_set_mac,
.ndo_change_mtu = qlcnic_change_mtu,
.ndo_fix_features = qlcnic_fix_features,
@@ -418,10 +417,8 @@ qlcnic_setup_intr(struct qlcnic_adapter *adapter)
int num_msix;
if (adapter->msix_supported) {
- num_msix = (num_online_cpus() >=
- QLCNIC_DEF_NUM_STS_DESC_RINGS) ?
- QLCNIC_DEF_NUM_STS_DESC_RINGS :
- QLCNIC_MIN_NUM_RSS_RINGS;
+ num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(),
+ QLCNIC_DEF_NUM_STS_DESC_RINGS));
} else
num_msix = 1;
@@ -646,8 +643,11 @@ static void get_brd_name(struct qlcnic_adapter *adapter, char *name)
static void
qlcnic_check_options(struct qlcnic_adapter *adapter)
{
- u32 fw_major, fw_minor, fw_build;
+ u32 fw_major, fw_minor, fw_build, prev_fw_version;
struct pci_dev *pdev = adapter->pdev;
+ struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
+
+ prev_fw_version = adapter->fw_version;
fw_major = QLCRD32(adapter, QLCNIC_FW_VERSION_MAJOR);
fw_minor = QLCRD32(adapter, QLCNIC_FW_VERSION_MINOR);
@@ -655,6 +655,17 @@ qlcnic_check_options(struct qlcnic_adapter *adapter)
adapter->fw_version = QLCNIC_VERSION_CODE(fw_major, fw_minor, fw_build);
+ if (adapter->op_mode != QLCNIC_NON_PRIV_FUNC) {
+ if (fw_dump->tmpl_hdr == NULL ||
+ adapter->fw_version > prev_fw_version) {
+ if (fw_dump->tmpl_hdr)
+ vfree(fw_dump->tmpl_hdr);
+ if (!qlcnic_fw_cmd_get_minidump_temp(adapter))
+ dev_info(&pdev->dev,
+ "Supports FW dump capability\n");
+ }
+ }
+
dev_info(&pdev->dev, "firmware v%d.%d.%d\n",
fw_major, fw_minor, fw_build);
if (adapter->ahw->port_type == QLCNIC_XGBE) {
@@ -1393,6 +1404,12 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
qlcnic_enable_int(sds_ring);
}
}
+
+ if (adapter->diag_test == QLCNIC_LOOPBACK_TEST) {
+ adapter->ahw->loopback_state = 0;
+ qlcnic_linkevent_request(adapter, 1);
+ }
+
set_bit(__QLCNIC_DEV_UP, &adapter->state);
return 0;
@@ -1487,8 +1504,6 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter,
netdev->irq = adapter->msix_entries[0].vector;
- netif_carrier_off(netdev);
-
err = register_netdev(netdev);
if (err) {
dev_err(&pdev->dev, "failed to register net device\n");
@@ -1576,6 +1591,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
adapter->dev_rst_time = jiffies;
revision_id = pdev->revision;
adapter->ahw->revision_id = revision_id;
+ adapter->mac_learn = qlcnic_mac_learn;
rwlock_init(&adapter->ahw->crb_lock);
mutex_init(&adapter->ahw->mem_lock);
@@ -1590,10 +1606,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* This will be reset for mezz cards */
adapter->portnum = adapter->ahw->pci_func;
- /* Get FW dump template and store it */
- if (adapter->op_mode != QLCNIC_NON_PRIV_FUNC)
- qlcnic_fw_cmd_get_minidump_temp(adapter);
-
err = qlcnic_get_board_info(adapter);
if (err) {
dev_err(&pdev->dev, "Error getting board config info.\n");
@@ -1650,7 +1662,9 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
break;
}
- qlcnic_alloc_lb_filters_mem(adapter);
+ if (adapter->mac_learn)
+ qlcnic_alloc_lb_filters_mem(adapter);
+
qlcnic_create_diag_entries(adapter);
return 0;
@@ -1816,6 +1830,8 @@ static int qlcnic_open(struct net_device *netdev)
struct qlcnic_adapter *adapter = netdev_priv(netdev);
int err;
+ netif_carrier_off(netdev);
+
err = qlcnic_attach(adapter);
if (err)
return err;
@@ -1844,13 +1860,12 @@ static int qlcnic_close(struct net_device *netdev)
return 0;
}
-static void
-qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter)
+void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter)
{
void *head;
int i;
- if (!qlcnic_mac_learn)
+ if (adapter->fhash.fmax && adapter->fhash.fhead)
return;
spin_lock_init(&adapter->mac_learn_lock);
@@ -1861,7 +1876,7 @@ qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter)
return;
adapter->fhash.fmax = QLCNIC_LB_MAX_FILTERS;
- adapter->fhash.fhead = (struct hlist_head *)head;
+ adapter->fhash.fhead = head;
for (i = 0; i < adapter->fhash.fmax; i++)
INIT_HLIST_HEAD(&adapter->fhash.fhead[i]);
@@ -2120,13 +2135,13 @@ qlcnic_map_tx_skb(struct pci_dev *pdev,
frag = &skb_shinfo(skb)->frags[i];
nf = &pbuf->frag_array[i+1];
- map = pci_map_page(pdev, frag->page, frag->page_offset,
- frag->size, PCI_DMA_TODEVICE);
- if (pci_dma_mapping_error(pdev, map))
+ map = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&pdev->dev, map))
goto unwind;
nf->dma = map;
- nf->length = frag->size;
+ nf->length = skb_frag_size(frag);
}
return 0;
@@ -2206,7 +2221,7 @@ qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) {
for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++)
- delta += skb_shinfo(skb)->frags[i].size;
+ delta += skb_frag_size(&skb_shinfo(skb)->frags[i]);
if (!__pskb_pull_tail(skb, delta))
goto drop_packet;
@@ -2280,14 +2295,14 @@ qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb)))
goto unwind_buff;
- if (qlcnic_mac_learn)
+ if (adapter->mac_learn)
qlcnic_send_filter(adapter, tx_ring, first_desc, skb);
- qlcnic_update_cmd_producer(adapter, tx_ring);
-
adapter->stats.txbytes += skb->len;
adapter->stats.xmitcalled++;
+ qlcnic_update_cmd_producer(adapter, tx_ring);
+
return NETDEV_TX_OK;
unwind_buff:
@@ -2675,6 +2690,7 @@ qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed)
qlcnic_api_unlock(adapter);
err:
adapter->fw_fail_cnt = 0;
+ adapter->flags &= ~QLCNIC_FW_HANG;
clear_bit(__QLCNIC_START_FW, &adapter->state);
clear_bit(__QLCNIC_RESETTING, &adapter->state);
}
@@ -2683,11 +2699,16 @@ err:
static int
qlcnic_check_drv_state(struct qlcnic_adapter *adapter)
{
- int act, state;
+ int act, state, active_mask;
state = QLCRD32(adapter, QLCNIC_CRB_DRV_STATE);
act = QLCRD32(adapter, QLCNIC_CRB_DRV_ACTIVE);
+ if (adapter->flags & QLCNIC_FW_RESET_OWNER) {
+ active_mask = (~(1 << (adapter->ahw->pci_func * 4)));
+ act = act & active_mask;
+ }
+
if (((state & 0x11111111) == (act & 0x11111111)) ||
((act & 0x11111111) == ((state >> 1) & 0x11111111)))
return 0;
@@ -2800,6 +2821,7 @@ qlcnic_fwinit_work(struct work_struct *work)
struct qlcnic_adapter *adapter = container_of(work,
struct qlcnic_adapter, fw_work.work);
u32 dev_state = 0xf;
+ u32 val;
if (qlcnic_api_lock(adapter))
goto err_ret;
@@ -2818,8 +2840,15 @@ qlcnic_fwinit_work(struct work_struct *work)
goto wait_npar;
}
+ if (dev_state == QLCNIC_DEV_INITIALIZING ||
+ dev_state == QLCNIC_DEV_READY) {
+ dev_info(&adapter->pdev->dev, "Detected state change from "
+ "DEV_NEED_RESET, skipping ack check\n");
+ goto skip_ack_check;
+ }
+
if (adapter->fw_wait_cnt++ > adapter->reset_ack_timeo) {
- dev_err(&adapter->pdev->dev, "Reset:Failed to get ack %d sec\n",
+ dev_info(&adapter->pdev->dev, "Reset:Failed to get ack %d sec\n",
adapter->reset_ack_timeo);
goto skip_ack_check;
}
@@ -2834,12 +2863,23 @@ skip_ack_check:
set_bit(__QLCNIC_START_FW, &adapter->state);
QLCDB(adapter, DRV, "Restarting fw\n");
qlcnic_idc_debug_info(adapter, 0);
- QLCDB(adapter, DRV, "Take FW dump\n");
- qlcnic_dump_fw(adapter);
+ val = QLCRD32(adapter, QLCNIC_CRB_DRV_STATE);
+ QLC_DEV_SET_RST_RDY(val, adapter->portnum);
+ QLCWR32(adapter, QLCNIC_CRB_DRV_STATE, val);
}
qlcnic_api_unlock(adapter);
+ rtnl_lock();
+ if (adapter->ahw->fw_dump.enable &&
+ (adapter->flags & QLCNIC_FW_RESET_OWNER)) {
+ QLCDB(adapter, DRV, "Take FW dump\n");
+ qlcnic_dump_fw(adapter);
+ adapter->flags |= QLCNIC_FW_HANG;
+ }
+ rtnl_unlock();
+
+ adapter->flags &= ~QLCNIC_FW_RESET_OWNER;
if (!adapter->nic_ops->start_firmware(adapter)) {
qlcnic_schedule_work(adapter, qlcnic_attach_work, 0);
adapter->fw_wait_cnt = 0;
@@ -2895,14 +2935,37 @@ qlcnic_detach_work(struct work_struct *work)
status = QLCRD32(adapter, QLCNIC_PEG_HALT_STATUS1);
- if (status & QLCNIC_RCODE_FATAL_ERROR)
- goto err_ret;
+ if (status & QLCNIC_RCODE_FATAL_ERROR) {
+ dev_err(&adapter->pdev->dev,
+ "Detaching the device: peg halt status1=0x%x\n",
+ status);
+
+ if (QLCNIC_FWERROR_CODE(status) == QLCNIC_FWERROR_FAN_FAILURE) {
+ dev_err(&adapter->pdev->dev,
+ "On board active cooling fan failed. "
+ "Device has been halted.\n");
+ dev_err(&adapter->pdev->dev,
+ "Replace the adapter.\n");
+ }
- if (adapter->temp == QLCNIC_TEMP_PANIC)
goto err_ret;
+ }
- if (qlcnic_set_drv_state(adapter, adapter->dev_state))
+ if (adapter->temp == QLCNIC_TEMP_PANIC) {
+ dev_err(&adapter->pdev->dev, "Detaching the device: temp=%d\n",
+ adapter->temp);
goto err_ret;
+ }
+
+ /* Dont ack if this instance is the reset owner */
+ if (!(adapter->flags & QLCNIC_FW_RESET_OWNER)) {
+ if (qlcnic_set_drv_state(adapter, adapter->dev_state)) {
+ dev_err(&adapter->pdev->dev,
+ "Failed to set driver state,"
+ "detaching the device.\n");
+ goto err_ret;
+ }
+ }
adapter->fw_wait_cnt = 0;
@@ -2911,8 +2974,6 @@ qlcnic_detach_work(struct work_struct *work)
return;
err_ret:
- dev_err(&adapter->pdev->dev, "detach failed; status=%d temp=%d\n",
- status, adapter->temp);
netif_device_attach(netdev);
qlcnic_clr_all_drv_state(adapter, 1);
}
@@ -2947,6 +3008,7 @@ qlcnic_dev_request_reset(struct qlcnic_adapter *adapter)
if (state == QLCNIC_DEV_READY) {
QLCWR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_NEED_RESET);
+ adapter->flags |= QLCNIC_FW_RESET_OWNER;
QLCDB(adapter, DRV, "NEED_RESET state set\n");
qlcnic_idc_debug_info(adapter, 0);
}
@@ -3020,6 +3082,7 @@ attach:
done:
netif_device_attach(netdev);
adapter->fw_fail_cnt = 0;
+ adapter->flags &= ~QLCNIC_FW_HANG;
clear_bit(__QLCNIC_RESETTING, &adapter->state);
if (!qlcnic_clr_drv_state(adapter))
@@ -3031,7 +3094,7 @@ static int
qlcnic_check_health(struct qlcnic_adapter *adapter)
{
u32 state = 0, heartbeat;
- struct net_device *netdev = adapter->netdev;
+ u32 peg_status;
if (qlcnic_check_temp(adapter))
goto detach;
@@ -3064,13 +3127,31 @@ qlcnic_check_health(struct qlcnic_adapter *adapter)
if (++adapter->fw_fail_cnt < FW_FAIL_THRESH)
return 0;
+ adapter->flags |= QLCNIC_FW_HANG;
+
qlcnic_dev_request_reset(adapter);
if (auto_fw_reset)
clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state);
- dev_info(&netdev->dev, "firmware hang detected\n");
-
+ dev_err(&adapter->pdev->dev, "firmware hang detected\n");
+ dev_err(&adapter->pdev->dev, "Dumping hw/fw registers\n"
+ "PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2: 0x%x,\n"
+ "PEG_NET_0_PC: 0x%x, PEG_NET_1_PC: 0x%x,\n"
+ "PEG_NET_2_PC: 0x%x, PEG_NET_3_PC: 0x%x,\n"
+ "PEG_NET_4_PC: 0x%x\n",
+ QLCRD32(adapter, QLCNIC_PEG_HALT_STATUS1),
+ QLCRD32(adapter, QLCNIC_PEG_HALT_STATUS2),
+ QLCRD32(adapter, QLCNIC_CRB_PEG_NET_0 + 0x3c),
+ QLCRD32(adapter, QLCNIC_CRB_PEG_NET_1 + 0x3c),
+ QLCRD32(adapter, QLCNIC_CRB_PEG_NET_2 + 0x3c),
+ QLCRD32(adapter, QLCNIC_CRB_PEG_NET_3 + 0x3c),
+ QLCRD32(adapter, QLCNIC_CRB_PEG_NET_4 + 0x3c));
+ peg_status = QLCRD32(adapter, QLCNIC_PEG_HALT_STATUS1);
+ if (LSW(MSB(peg_status)) == 0x67)
+ dev_err(&adapter->pdev->dev,
+ "Firmware aborted with error code 0x00006700. "
+ "Device is being reset.\n");
detach:
adapter->dev_state = (state == QLCNIC_DEV_NEED_QUISCENT) ? state :
QLCNIC_DEV_NEED_RESET;
@@ -3397,6 +3478,107 @@ int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data)
}
static int
+qlcnic_validate_beacon(struct qlcnic_adapter *adapter, u16 beacon, u8 *state,
+ u8 *rate)
+{
+ *rate = LSB(beacon);
+ *state = MSB(beacon);
+
+ QLCDB(adapter, DRV, "rate %x state %x\n", *rate, *state);
+
+ if (!*state) {
+ *rate = __QLCNIC_MAX_LED_RATE;
+ return 0;
+ } else if (*state > __QLCNIC_MAX_LED_STATE)
+ return -EINVAL;
+
+ if ((!*rate) || (*rate > __QLCNIC_MAX_LED_RATE))
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t
+qlcnic_store_beacon(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+ int max_sds_rings = adapter->max_sds_rings;
+ u16 beacon;
+ u8 b_state, b_rate;
+ int err;
+
+ if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) {
+ dev_warn(dev, "LED test not supported for non "
+ "privilege function\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (len != sizeof(u16))
+ return QL_STATUS_INVALID_PARAM;
+
+ memcpy(&beacon, buf, sizeof(u16));
+ err = qlcnic_validate_beacon(adapter, beacon, &b_state, &b_rate);
+ if (err)
+ return err;
+
+ if (adapter->ahw->beacon_state == b_state)
+ return len;
+
+ rtnl_lock();
+
+ if (!adapter->ahw->beacon_state)
+ if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) {
+ rtnl_unlock();
+ return -EBUSY;
+ }
+
+ if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
+ err = -EIO;
+ goto out;
+ }
+
+ if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
+ err = qlcnic_diag_alloc_res(adapter->netdev, QLCNIC_LED_TEST);
+ if (err)
+ goto out;
+ set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state);
+ }
+
+ err = qlcnic_config_led(adapter, b_state, b_rate);
+
+ if (!err) {
+ err = len;
+ adapter->ahw->beacon_state = b_state;
+ }
+
+ if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state))
+ qlcnic_diag_free_res(adapter->netdev, max_sds_rings);
+
+ out:
+ if (!adapter->ahw->beacon_state)
+ clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
+ rtnl_unlock();
+
+ return err;
+}
+
+static ssize_t
+qlcnic_show_beacon(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", adapter->ahw->beacon_state);
+}
+
+static struct device_attribute dev_attr_beacon = {
+ .attr = {.name = "beacon", .mode = (S_IRUGO | S_IWUSR)},
+ .show = qlcnic_show_beacon,
+ .store = qlcnic_store_beacon,
+};
+
+static int
qlcnic_sysfs_validate_crb(struct qlcnic_adapter *adapter,
loff_t offset, size_t size)
{
@@ -4093,6 +4275,8 @@ qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
return;
if (device_create_file(dev, &dev_attr_diag_mode))
dev_info(dev, "failed to create diag_mode sysfs entry\n");
+ if (device_create_file(dev, &dev_attr_beacon))
+ dev_info(dev, "failed to create beacon sysfs entry");
if (device_create_bin_file(dev, &bin_attr_crb))
dev_info(dev, "failed to create crb sysfs entry\n");
if (device_create_bin_file(dev, &bin_attr_mem))
@@ -4123,6 +4307,7 @@ qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter)
if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC)
return;
device_remove_file(dev, &dev_attr_diag_mode);
+ device_remove_file(dev, &dev_attr_beacon);
device_remove_bin_file(dev, &bin_attr_crb);
device_remove_bin_file(dev, &bin_attr_mem);
device_remove_bin_file(dev, &bin_attr_pci_config);
@@ -4178,7 +4363,7 @@ qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event)
qlcnic_config_indev_addr(adapter, netdev, event);
for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) {
- dev = vlan_find_dev(netdev, vid);
+ dev = __vlan_find_dev_deep(netdev, vid);
if (!dev)
continue;
qlcnic_config_indev_addr(adapter, dev, event);
diff --git a/drivers/net/qlge/Makefile b/drivers/net/ethernet/qlogic/qlge/Makefile
index 8a197658d76f..8a197658d76f 100644
--- a/drivers/net/qlge/Makefile
+++ b/drivers/net/ethernet/qlogic/qlge/Makefile
diff --git a/drivers/net/qlge/qlge.h b/drivers/net/ethernet/qlogic/qlge/qlge.h
index ca306fd5f588..8731f79c9efc 100644
--- a/drivers/net/qlge/qlge.h
+++ b/drivers/net/ethernet/qlogic/qlge/qlge.h
@@ -7,9 +7,11 @@
#ifndef _QLGE_H_
#define _QLGE_H_
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
+#include <linux/if_vlan.h>
/*
* General definitions...
@@ -2051,7 +2053,7 @@ struct ql_adapter {
struct nic_stats nic_stats;
- struct vlan_group *vlgrp;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
/* PCI Configuration information for this device */
struct pci_dev *pdev;
diff --git a/drivers/net/qlge/qlge_dbg.c b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c
index fca804f36d61..fca804f36d61 100644
--- a/drivers/net/qlge/qlge_dbg.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c
diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c
index 19b00fa0eaf0..9b67bfea035f 100644
--- a/drivers/net/qlge/qlge_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c
@@ -650,8 +650,6 @@ static int ql_set_pauseparam(struct net_device *netdev,
return -EINVAL;
status = ql_mb_set_port_cfg(qdev);
- if (status)
- return status;
return status;
}
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
index 6b4ff970972b..c92afcd912e2 100644
--- a/drivers/net/qlge/qlge_main.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
@@ -7,6 +7,7 @@
*/
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/list.h>
@@ -33,8 +34,8 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
-#include <linux/skbuff.h>
#include <linux/if_vlan.h>
+#include <linux/skbuff.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
@@ -415,7 +416,7 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type,
(qdev->
func << CAM_OUT_FUNC_SHIFT) |
(0 << CAM_OUT_CQ_ID_SHIFT));
- if (qdev->vlgrp)
+ if (qdev->ndev->features & NETIF_F_HW_VLAN_RX)
cam_output |= CAM_OUT_RV;
/* route to NIC core */
ql_write32(qdev, MAC_ADDR_DATA, cam_output);
@@ -1430,12 +1431,10 @@ static int ql_map_send(struct ql_adapter *qdev,
map_idx++;
}
- map =
- pci_map_page(qdev->pdev, frag->page,
- frag->page_offset, frag->size,
- PCI_DMA_TODEVICE);
+ map = skb_frag_dma_map(&qdev->pdev->dev, frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
- err = pci_dma_mapping_error(qdev->pdev, map);
+ err = dma_mapping_error(&qdev->pdev->dev, map);
if (err) {
netif_err(qdev, tx_queued, qdev->ndev,
"PCI mapping frags failed with error: %d.\n",
@@ -1444,10 +1443,10 @@ static int ql_map_send(struct ql_adapter *qdev,
}
tbd->addr = cpu_to_le64(map);
- tbd->len = cpu_to_le32(frag->size);
+ tbd->len = cpu_to_le32(skb_frag_size(frag));
dma_unmap_addr_set(&tx_ring_desc->map[map_idx], mapaddr, map);
dma_unmap_len_set(&tx_ring_desc->map[map_idx], maplen,
- frag->size);
+ skb_frag_size(frag));
}
/* Save the number of segments we've mapped. */
@@ -1476,8 +1475,6 @@ static void ql_process_mac_rx_gro_page(struct ql_adapter *qdev,
{
struct sk_buff *skb;
struct bq_desc *lbq_desc = ql_get_curr_lchunk(qdev, rx_ring);
- struct skb_frag_struct *rx_frag;
- int nr_frags;
struct napi_struct *napi = &rx_ring->napi;
napi->dev = qdev->ndev;
@@ -1491,12 +1488,10 @@ static void ql_process_mac_rx_gro_page(struct ql_adapter *qdev,
return;
}
prefetch(lbq_desc->p.pg_chunk.va);
- rx_frag = skb_shinfo(skb)->frags;
- nr_frags = skb_shinfo(skb)->nr_frags;
- rx_frag += nr_frags;
- rx_frag->page = lbq_desc->p.pg_chunk.page;
- rx_frag->page_offset = lbq_desc->p.pg_chunk.offset;
- rx_frag->size = length;
+ __skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags,
+ lbq_desc->p.pg_chunk.page,
+ lbq_desc->p.pg_chunk.offset,
+ length);
skb->len += length;
skb->data_len += length;
@@ -1507,10 +1502,9 @@ static void ql_process_mac_rx_gro_page(struct ql_adapter *qdev,
rx_ring->rx_bytes += length;
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(skb, rx_ring->cq_id);
- if (qdev->vlgrp && (vlan_id != 0xffff))
- vlan_gro_frags(&rx_ring->napi, qdev->vlgrp, vlan_id);
- else
- napi_gro_frags(napi);
+ if (vlan_id != 0xffff)
+ __vlan_hwaccel_put_tag(skb, vlan_id);
+ napi_gro_frags(napi);
}
/* Process an inbound completion from an rx ring. */
@@ -1594,17 +1588,12 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev,
}
skb_record_rx_queue(skb, rx_ring->cq_id);
- if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
- if (qdev->vlgrp && (vlan_id != 0xffff))
- vlan_gro_receive(napi, qdev->vlgrp, vlan_id, skb);
- else
- napi_gro_receive(napi, skb);
- } else {
- if (qdev->vlgrp && (vlan_id != 0xffff))
- vlan_hwaccel_receive_skb(skb, qdev->vlgrp, vlan_id);
- else
- netif_receive_skb(skb);
- }
+ if (vlan_id != 0xffff)
+ __vlan_hwaccel_put_tag(skb, vlan_id);
+ if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+ napi_gro_receive(napi, skb);
+ else
+ netif_receive_skb(skb);
return;
err_out:
dev_kfree_skb_any(skb);
@@ -1707,18 +1696,12 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev,
}
skb_record_rx_queue(skb, rx_ring->cq_id);
- if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
- if (qdev->vlgrp && (vlan_id != 0xffff))
- vlan_gro_receive(&rx_ring->napi, qdev->vlgrp,
- vlan_id, skb);
- else
- napi_gro_receive(&rx_ring->napi, skb);
- } else {
- if (qdev->vlgrp && (vlan_id != 0xffff))
- vlan_hwaccel_receive_skb(skb, qdev->vlgrp, vlan_id);
- else
- netif_receive_skb(skb);
- }
+ if (vlan_id != 0xffff)
+ __vlan_hwaccel_put_tag(skb, vlan_id);
+ if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+ napi_gro_receive(&rx_ring->napi, skb);
+ else
+ netif_receive_skb(skb);
}
static void ql_realign_skb(struct sk_buff *skb, int len)
@@ -2028,22 +2011,12 @@ static void ql_process_mac_split_rx_intr(struct ql_adapter *qdev,
rx_ring->rx_packets++;
rx_ring->rx_bytes += skb->len;
skb_record_rx_queue(skb, rx_ring->cq_id);
- if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
- if (qdev->vlgrp &&
- (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) &&
- (vlan_id != 0))
- vlan_gro_receive(&rx_ring->napi, qdev->vlgrp,
- vlan_id, skb);
- else
- napi_gro_receive(&rx_ring->napi, skb);
- } else {
- if (qdev->vlgrp &&
- (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) &&
- (vlan_id != 0))
- vlan_hwaccel_receive_skb(skb, qdev->vlgrp, vlan_id);
- else
- netif_receive_skb(skb);
- }
+ if ((ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) && (vlan_id != 0))
+ __vlan_hwaccel_put_tag(skb, vlan_id);
+ if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+ napi_gro_receive(&rx_ring->napi, skb);
+ else
+ netif_receive_skb(skb);
}
/* Process an inbound completion from an rx ring. */
@@ -2334,71 +2307,111 @@ static int ql_napi_poll_msix(struct napi_struct *napi, int budget)
return work_done;
}
-static void qlge_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp)
+static void qlge_vlan_mode(struct net_device *ndev, u32 features)
{
struct ql_adapter *qdev = netdev_priv(ndev);
- qdev->vlgrp = grp;
- if (grp) {
- netif_printk(qdev, ifup, KERN_DEBUG, qdev->ndev,
+ if (features & NETIF_F_HW_VLAN_RX) {
+ netif_printk(qdev, ifup, KERN_DEBUG, ndev,
"Turning on VLAN in NIC_RCV_CFG.\n");
ql_write32(qdev, NIC_RCV_CFG, NIC_RCV_CFG_VLAN_MASK |
- NIC_RCV_CFG_VLAN_MATCH_AND_NON);
+ NIC_RCV_CFG_VLAN_MATCH_AND_NON);
} else {
- netif_printk(qdev, ifup, KERN_DEBUG, qdev->ndev,
+ netif_printk(qdev, ifup, KERN_DEBUG, ndev,
"Turning off VLAN in NIC_RCV_CFG.\n");
ql_write32(qdev, NIC_RCV_CFG, NIC_RCV_CFG_VLAN_MASK);
}
}
-static void qlge_vlan_rx_add_vid(struct net_device *ndev, u16 vid)
+static u32 qlge_fix_features(struct net_device *ndev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int qlge_set_features(struct net_device *ndev, u32 features)
+{
+ u32 changed = ndev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ qlge_vlan_mode(ndev, features);
+
+ return 0;
+}
+
+static void __qlge_vlan_rx_add_vid(struct ql_adapter *qdev, u16 vid)
{
- struct ql_adapter *qdev = netdev_priv(ndev);
u32 enable_bit = MAC_ADDR_E;
- int status;
- status = ql_sem_spinlock(qdev, SEM_MAC_ADDR_MASK);
- if (status)
- return;
if (ql_set_mac_addr_reg
(qdev, (u8 *) &enable_bit, MAC_ADDR_TYPE_VLAN, vid)) {
netif_err(qdev, ifup, qdev->ndev,
"Failed to init vlan address.\n");
}
- ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK);
}
-static void qlge_vlan_rx_kill_vid(struct net_device *ndev, u16 vid)
+static void qlge_vlan_rx_add_vid(struct net_device *ndev, u16 vid)
{
struct ql_adapter *qdev = netdev_priv(ndev);
- u32 enable_bit = 0;
int status;
status = ql_sem_spinlock(qdev, SEM_MAC_ADDR_MASK);
if (status)
return;
+ __qlge_vlan_rx_add_vid(qdev, vid);
+ set_bit(vid, qdev->active_vlans);
+
+ ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK);
+}
+
+static void __qlge_vlan_rx_kill_vid(struct ql_adapter *qdev, u16 vid)
+{
+ u32 enable_bit = 0;
+
if (ql_set_mac_addr_reg
(qdev, (u8 *) &enable_bit, MAC_ADDR_TYPE_VLAN, vid)) {
netif_err(qdev, ifup, qdev->ndev,
"Failed to clear vlan address.\n");
}
- ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK);
+}
+static void qlge_vlan_rx_kill_vid(struct net_device *ndev, u16 vid)
+{
+ struct ql_adapter *qdev = netdev_priv(ndev);
+ int status;
+
+ status = ql_sem_spinlock(qdev, SEM_MAC_ADDR_MASK);
+ if (status)
+ return;
+
+ __qlge_vlan_rx_kill_vid(qdev, vid);
+ clear_bit(vid, qdev->active_vlans);
+
+ ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK);
}
static void qlge_restore_vlan(struct ql_adapter *qdev)
{
- qlge_vlan_rx_register(qdev->ndev, qdev->vlgrp);
+ int status;
+ u16 vid;
- if (qdev->vlgrp) {
- u16 vid;
- for (vid = 0; vid < VLAN_N_VID; vid++) {
- if (!vlan_group_get_device(qdev->vlgrp, vid))
- continue;
- qlge_vlan_rx_add_vid(qdev->ndev, vid);
- }
- }
+ status = ql_sem_spinlock(qdev, SEM_MAC_ADDR_MASK);
+ if (status)
+ return;
+
+ for_each_set_bit(vid, qdev->active_vlans, VLAN_N_VID)
+ __qlge_vlan_rx_add_vid(qdev, vid);
+
+ ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK);
}
/* MSI-X Multiple Vector Interrupt Handler for inbound completions. */
@@ -3096,7 +3109,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
if (rx_ring->lbq_len) {
cqicb->flags |= FLAGS_LL; /* Load lbq values */
tmp = (u64)rx_ring->lbq_base_dma;
- base_indirect_ptr = (__le64 *) rx_ring->lbq_base_indirect;
+ base_indirect_ptr = rx_ring->lbq_base_indirect;
page_entries = 0;
do {
*base_indirect_ptr = cpu_to_le64(tmp);
@@ -3120,7 +3133,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
if (rx_ring->sbq_len) {
cqicb->flags |= FLAGS_LS; /* Load sbq values */
tmp = (u64)rx_ring->sbq_base_dma;
- base_indirect_ptr = (__le64 *) rx_ring->sbq_base_indirect;
+ base_indirect_ptr = rx_ring->sbq_base_indirect;
page_entries = 0;
do {
*base_indirect_ptr = cpu_to_le64(tmp);
@@ -4657,11 +4670,12 @@ static const struct net_device_ops qlge_netdev_ops = {
.ndo_start_xmit = qlge_send,
.ndo_change_mtu = qlge_change_mtu,
.ndo_get_stats = qlge_get_stats,
- .ndo_set_multicast_list = qlge_set_multicast_list,
+ .ndo_set_rx_mode = qlge_set_multicast_list,
.ndo_set_mac_address = qlge_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
.ndo_tx_timeout = qlge_tx_timeout,
- .ndo_vlan_rx_register = qlge_vlan_rx_register,
+ .ndo_fix_features = qlge_fix_features,
+ .ndo_set_features = qlge_set_features,
.ndo_vlan_rx_add_vid = qlge_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = qlge_vlan_rx_kill_vid,
};
diff --git a/drivers/net/qlge/qlge_mpi.c b/drivers/net/ethernet/qlogic/qlge/qlge_mpi.c
index ff2bf8a4e247..ff2bf8a4e247 100644
--- a/drivers/net/qlge/qlge_mpi.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_mpi.c
diff --git a/drivers/net/ethernet/racal/Kconfig b/drivers/net/ethernet/racal/Kconfig
new file mode 100644
index 000000000000..01969e0a9c68
--- /dev/null
+++ b/drivers/net/ethernet/racal/Kconfig
@@ -0,0 +1,33 @@
+#
+# Racal-Interlan device configuration
+#
+
+config NET_VENDOR_RACAL
+ bool "Racal-Interlan (Micom) NI devices"
+ default y
+ depends on ISA
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, such
+ as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about NI cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_RACAL
+
+config NI5010
+ tristate "NI5010 support (EXPERIMENTAL)"
+ depends on ISA && EXPERIMENTAL && BROKEN_ON_SMP
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Note that this is still
+ experimental code.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ni5010.
+
+endif # NET_VENDOR_RACAL
diff --git a/drivers/net/ethernet/racal/Makefile b/drivers/net/ethernet/racal/Makefile
new file mode 100644
index 000000000000..1e210ca1d78b
--- /dev/null
+++ b/drivers/net/ethernet/racal/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Racal-Interlan network device drivers.
+#
+
+obj-$(CONFIG_NI5010) += ni5010.o
diff --git a/drivers/net/ni5010.c b/drivers/net/ethernet/racal/ni5010.c
index 4d3f2e2b28bd..072810da9a37 100644
--- a/drivers/net/ni5010.c
+++ b/drivers/net/ethernet/racal/ni5010.c
@@ -192,7 +192,7 @@ static const struct net_device_ops ni5010_netdev_ops = {
.ndo_open = ni5010_open,
.ndo_stop = ni5010_close,
.ndo_start_xmit = ni5010_send_packet,
- .ndo_set_multicast_list = ni5010_set_multicast_list,
+ .ndo_set_rx_mode = ni5010_set_multicast_list,
.ndo_tx_timeout = ni5010_timeout,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/ni5010.h b/drivers/net/ethernet/racal/ni5010.h
index e10e717fcd76..e10e717fcd76 100644
--- a/drivers/net/ni5010.h
+++ b/drivers/net/ethernet/racal/ni5010.h
diff --git a/drivers/net/ethernet/rdc/Kconfig b/drivers/net/ethernet/rdc/Kconfig
new file mode 100644
index 000000000000..c8ba4b3494c1
--- /dev/null
+++ b/drivers/net/ethernet/rdc/Kconfig
@@ -0,0 +1,35 @@
+#
+# RDC network device configuration
+#
+
+config NET_VENDOR_RDC
+ bool "RDC devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about RDC cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_RDC
+
+config R6040
+ tristate "RDC R6040 Fast Ethernet Adapter support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ select PHYLIB
+ ---help---
+ This is a driver for the R6040 Fast Ethernet MACs found in the
+ the RDC R-321x System-on-chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called r6040. This is recommended.
+
+endif # NET_VENDOR_RDC
diff --git a/drivers/net/ethernet/rdc/Makefile b/drivers/net/ethernet/rdc/Makefile
new file mode 100644
index 000000000000..8d51fd2d07fc
--- /dev/null
+++ b/drivers/net/ethernet/rdc/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the RDC network device drivers.
+#
+
+obj-$(CONFIG_R6040) += r6040.o
diff --git a/drivers/net/r6040.c b/drivers/net/ethernet/rdc/r6040.c
index 200a363c3bf5..4bf68cfef390 100644
--- a/drivers/net/r6040.c
+++ b/drivers/net/ethernet/rdc/r6040.c
@@ -49,8 +49,8 @@
#include <asm/processor.h>
#define DRV_NAME "r6040"
-#define DRV_VERSION "0.27"
-#define DRV_RELDATE "23Feb2011"
+#define DRV_VERSION "0.28"
+#define DRV_RELDATE "07Oct2011"
/* PHY CHIP Address */
#define PHY1_ADDR 1 /* For MAC1 */
@@ -470,6 +470,8 @@ static void r6040_down(struct net_device *dev)
iowrite16(adrp[0], ioaddr + MID_0L);
iowrite16(adrp[1], ioaddr + MID_0M);
iowrite16(adrp[2], ioaddr + MID_0H);
+
+ phy_stop(lp->phydev);
}
static int r6040_close(struct net_device *dev)
@@ -677,9 +679,11 @@ static irqreturn_t r6040_interrupt(int irq, void *dev_id)
if (status & RX_FIFO_FULL)
dev->stats.rx_fifo_errors++;
- /* Mask off RX interrupt */
- misr &= ~RX_INTS;
- napi_schedule(&lp->napi);
+ if (likely(napi_schedule_prep(&lp->napi))) {
+ /* Mask off RX interrupt */
+ misr &= ~RX_INTS;
+ __napi_schedule(&lp->napi);
+ }
}
/* TX interrupt request */
@@ -725,6 +729,8 @@ static int r6040_up(struct net_device *dev)
/* Initialize all MAC registers */
r6040_init_mac_regs(dev);
+ phy_start(lp->phydev);
+
return 0;
}
@@ -836,6 +842,9 @@ static netdev_tx_t r6040_start_xmit(struct sk_buff *skb,
descptr->buf = cpu_to_le32(pci_map_single(lp->pdev,
skb->data, skb->len, PCI_DMA_TODEVICE));
descptr->status = DSC_OWNER_MAC;
+
+ skb_tx_timestamp(skb);
+
/* Trigger the MAC to check the TX descriptor */
iowrite16(0x01, ioaddr + MTPR);
lp->tx_insert_ptr = descptr->vndescp;
@@ -931,7 +940,7 @@ static void r6040_multicast_list(struct net_device *dev)
iowrite16(lp->mcr0, ioaddr + MCR0);
/* Fill the MAC hash tables with their values */
- if (lp->mcr0 && MCR0_HASH_EN) {
+ if (lp->mcr0 & MCR0_HASH_EN) {
iowrite16(hash_table[0], ioaddr + MAR0);
iowrite16(hash_table[1], ioaddr + MAR1);
iowrite16(hash_table[2], ioaddr + MAR2);
@@ -977,7 +986,7 @@ static const struct net_device_ops r6040_netdev_ops = {
.ndo_stop = r6040_close,
.ndo_start_xmit = r6040_start_xmit,
.ndo_get_stats = r6040_get_stats,
- .ndo_set_multicast_list = r6040_multicast_list,
+ .ndo_set_rx_mode = r6040_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c
index 10c45051caea..ee5da9293ce0 100644
--- a/drivers/net/8139cp.c
+++ b/drivers/net/ethernet/realtek/8139cp.c
@@ -60,6 +60,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
@@ -77,17 +78,6 @@
#include <asm/irq.h>
#include <asm/uaccess.h>
-/* VLAN tagging feature enable/disable */
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
-#define CP_VLAN_TAG_USED 1
-#define CP_VLAN_TX_TAG(tx_desc,vlan_tag_value) \
- do { (tx_desc)->opts2 = cpu_to_le32(vlan_tag_value); } while (0)
-#else
-#define CP_VLAN_TAG_USED 0
-#define CP_VLAN_TX_TAG(tx_desc,vlan_tag_value) \
- do { (tx_desc)->opts2 = 0; } while (0)
-#endif
-
/* These identify the driver base version and may not be removed. */
static char version[] =
DRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n";
@@ -355,9 +345,6 @@ struct cp_private {
unsigned rx_buf_sz;
unsigned wol_enabled : 1; /* Is Wake-on-LAN enabled? */
-#if CP_VLAN_TAG_USED
- struct vlan_group *vlgrp;
-#endif
dma_addr_t ring_dma;
struct mii_if_info mii_if;
@@ -422,24 +409,6 @@ static struct {
};
-#if CP_VLAN_TAG_USED
-static void cp_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
-{
- struct cp_private *cp = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&cp->lock, flags);
- cp->vlgrp = grp;
- if (grp)
- cp->cpcmd |= RxVlanOn;
- else
- cp->cpcmd &= ~RxVlanOn;
-
- cpw16(CpCmd, cp->cpcmd);
- spin_unlock_irqrestore(&cp->lock, flags);
-}
-#endif /* CP_VLAN_TAG_USED */
-
static inline void cp_set_rxbufsize (struct cp_private *cp)
{
unsigned int mtu = cp->dev->mtu;
@@ -454,18 +423,17 @@ static inline void cp_set_rxbufsize (struct cp_private *cp)
static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb,
struct cp_desc *desc)
{
+ u32 opts2 = le32_to_cpu(desc->opts2);
+
skb->protocol = eth_type_trans (skb, cp->dev);
cp->dev->stats.rx_packets++;
cp->dev->stats.rx_bytes += skb->len;
-#if CP_VLAN_TAG_USED
- if (cp->vlgrp && (desc->opts2 & cpu_to_le32(RxVlanTagged))) {
- vlan_hwaccel_receive_skb(skb, cp->vlgrp,
- swab16(le32_to_cpu(desc->opts2) & 0xffff));
- } else
-#endif
- netif_receive_skb(skb);
+ if (opts2 & RxVlanTagged)
+ __vlan_hwaccel_put_tag(skb, swab16(opts2 & 0xffff));
+
+ napi_gro_receive(&cp->napi, skb);
}
static void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail,
@@ -729,6 +697,12 @@ static void cp_tx (struct cp_private *cp)
netif_wake_queue(cp->dev);
}
+static inline u32 cp_tx_vlan_tag(struct sk_buff *skb)
+{
+ return vlan_tx_tag_present(skb) ?
+ TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00;
+}
+
static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
struct net_device *dev)
{
@@ -736,9 +710,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
unsigned entry;
u32 eor, flags;
unsigned long intr_flags;
-#if CP_VLAN_TAG_USED
- u32 vlan_tag = 0;
-#endif
+ __le32 opts2;
int mss = 0;
spin_lock_irqsave(&cp->lock, intr_flags);
@@ -751,15 +723,12 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
return NETDEV_TX_BUSY;
}
-#if CP_VLAN_TAG_USED
- if (vlan_tx_tag_present(skb))
- vlan_tag = TxVlanTag | swab16(vlan_tx_tag_get(skb));
-#endif
-
entry = cp->tx_head;
eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
mss = skb_shinfo(skb)->gso_size;
+ opts2 = cpu_to_le32(cp_tx_vlan_tag(skb));
+
if (skb_shinfo(skb)->nr_frags == 0) {
struct cp_desc *txd = &cp->tx_ring[entry];
u32 len;
@@ -767,7 +736,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
len = skb->len;
mapping = dma_map_single(&cp->pdev->dev, skb->data, len, PCI_DMA_TODEVICE);
- CP_VLAN_TX_TAG(txd, vlan_tag);
+ txd->opts2 = opts2;
txd->addr = cpu_to_le64(mapping);
wmb();
@@ -808,15 +777,14 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
entry = NEXT_TX(entry);
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
- skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
+ const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
u32 len;
u32 ctrl;
dma_addr_t mapping;
- len = this_frag->size;
+ len = skb_frag_size(this_frag);
mapping = dma_map_single(&cp->pdev->dev,
- ((void *) page_address(this_frag->page) +
- this_frag->page_offset),
+ skb_frag_address(this_frag),
len, PCI_DMA_TODEVICE);
eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
@@ -838,7 +806,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
ctrl |= LastFrag;
txd = &cp->tx_ring[entry];
- CP_VLAN_TX_TAG(txd, vlan_tag);
+ txd->opts2 = opts2;
txd->addr = cpu_to_le64(mapping);
wmb();
@@ -850,7 +818,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
}
txd = &cp->tx_ring[first_entry];
- CP_VLAN_TX_TAG(txd, vlan_tag);
+ txd->opts2 = opts2;
txd->addr = cpu_to_le64(first_mapping);
wmb();
@@ -1356,6 +1324,15 @@ static void cp_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info
strcpy (info->bus_info, pci_name(cp->pdev));
}
+static void cp_get_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ring)
+{
+ ring->rx_max_pending = CP_RX_RING_SIZE;
+ ring->tx_max_pending = CP_TX_RING_SIZE;
+ ring->rx_pending = CP_RX_RING_SIZE;
+ ring->tx_pending = CP_TX_RING_SIZE;
+}
+
static int cp_get_regs_len(struct net_device *dev)
{
return CP_REGS_SIZE;
@@ -1430,6 +1407,11 @@ static int cp_set_features(struct net_device *dev, u32 features)
else
cp->cpcmd &= ~RxChkSum;
+ if (features & NETIF_F_HW_VLAN_RX)
+ cp->cpcmd |= RxVlanOn;
+ else
+ cp->cpcmd &= ~RxVlanOn;
+
cpw16_f(CpCmd, cp->cpcmd);
spin_unlock_irqrestore(&cp->lock, flags);
@@ -1552,6 +1534,7 @@ static const struct ethtool_ops cp_ethtool_ops = {
.get_eeprom_len = cp_get_eeprom_len,
.get_eeprom = cp_get_eeprom,
.set_eeprom = cp_set_eeprom,
+ .get_ringparam = cp_get_ringparam,
};
static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
@@ -1811,15 +1794,12 @@ static const struct net_device_ops cp_netdev_ops = {
.ndo_stop = cp_close,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = cp_set_mac_address,
- .ndo_set_multicast_list = cp_set_rx_mode,
+ .ndo_set_rx_mode = cp_set_rx_mode,
.ndo_get_stats = cp_get_stats,
.ndo_do_ioctl = cp_ioctl,
.ndo_start_xmit = cp_start_xmit,
.ndo_tx_timeout = cp_tx_timeout,
.ndo_set_features = cp_set_features,
-#if CP_VLAN_TAG_USED
- .ndo_vlan_rx_register = cp_vlan_rx_register,
-#endif
#ifdef BROKEN
.ndo_change_mtu = cp_change_mtu,
#endif
@@ -1948,15 +1928,16 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
dev->ethtool_ops = &cp_ethtool_ops;
dev->watchdog_timeo = TX_TIMEOUT;
-#if CP_VLAN_TAG_USED
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-#endif
if (pci_using_dac)
dev->features |= NETIF_F_HIGHDMA;
/* disabled by default until verified */
- dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
+ dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
+ NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+ dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
+ NETIF_F_HIGHDMA;
dev->irq = pdev->irq;
diff --git a/drivers/net/8139too.c b/drivers/net/ethernet/realtek/8139too.c
index e3bad8247fd1..4d6b254fc6c1 100644
--- a/drivers/net/8139too.c
+++ b/drivers/net/ethernet/realtek/8139too.c
@@ -100,6 +100,7 @@
#include <linux/compiler.h>
#include <linux/pci.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
@@ -915,7 +916,7 @@ static const struct net_device_ops rtl8139_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = rtl8139_set_mac_address,
.ndo_start_xmit = rtl8139_start_xmit,
- .ndo_set_multicast_list = rtl8139_set_rx_mode,
+ .ndo_set_rx_mode = rtl8139_set_rx_mode,
.ndo_do_ioctl = netdev_ioctl,
.ndo_tx_timeout = rtl8139_tx_timeout,
#ifdef CONFIG_NET_POLL_CONTROLLER
diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig
new file mode 100644
index 000000000000..0578859a3c73
--- /dev/null
+++ b/drivers/net/ethernet/realtek/Kconfig
@@ -0,0 +1,118 @@
+#
+# Realtek device configuration
+#
+
+config NET_VENDOR_REALTEK
+ bool "Realtek devices"
+ default y
+ depends on PCI || (PARPORT && X86)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Realtek devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_REALTEK
+
+config ATP
+ tristate "AT-LAN-TEC/RealTek pocket adapter support"
+ depends on PARPORT && X86
+ select CRC32
+ ---help---
+ This is a network (Ethernet) device which attaches to your parallel
+ port. Read <file:drivers/net/atp.c> as well as the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>, if you
+ want to use this. If you intend to use this driver, you should have
+ said N to the "Parallel printer support", because the two drivers
+ don't like each other.
+
+ To compile this driver as a module, choose M here: the module
+ will be called atp.
+
+config 8139CP
+ tristate "RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This is a driver for the Fast Ethernet PCI network cards based on
+ the RTL8139C+ chips. If you have one of those, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called 8139cp. This is recommended.
+
+config 8139TOO
+ tristate "RealTek RTL-8129/8130/8139 PCI Fast Ethernet Adapter support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This is a driver for the Fast Ethernet PCI network cards based on
+ the RTL 8129/8130/8139 chips. If you have one of those, say Y and
+ read the Ethernet-HOWTO <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called 8139too. This is recommended.
+
+config 8139TOO_PIO
+ bool "Use PIO instead of MMIO"
+ default y
+ depends on 8139TOO
+ ---help---
+ This instructs the driver to use programmed I/O ports (PIO) instead
+ of PCI shared memory (MMIO). This can possibly solve some problems
+ in case your mainboard has memory consistency issues. If unsure,
+ say N.
+
+config 8139TOO_TUNE_TWISTER
+ bool "Support for uncommon RTL-8139 rev. K (automatic channel equalization)"
+ depends on 8139TOO
+ ---help---
+ This implements a function which might come in handy in case you
+ are using low quality on long cabling. It is required for RealTek
+ RTL-8139 revision K boards, and totally unused otherwise. It tries
+ to match the transceiver to the cable characteristics. This is
+ experimental since hardly documented by the manufacturer.
+ If unsure, say Y.
+
+config 8139TOO_8129
+ bool "Support for older RTL-8129/8130 boards"
+ depends on 8139TOO
+ ---help---
+ This enables support for the older and uncommon RTL-8129 and
+ RTL-8130 chips, which support MII via an external transceiver,
+ instead of an internal one. Disabling this option will save some
+ memory by making the code size smaller. If unsure, say Y.
+
+config 8139_OLD_RX_RESET
+ bool "Use older RX-reset method"
+ depends on 8139TOO
+ ---help---
+ The 8139too driver was recently updated to contain a more rapid
+ reset sequence, in the face of severe receive errors. This "new"
+ RX-reset method should be adequate for all boards. But if you
+ experience problems, you can enable this option to restore the
+ old RX-reset behavior. If unsure, say N.
+
+config R8169
+ tristate "Realtek 8169 gigabit ethernet support"
+ depends on PCI
+ select FW_LOADER
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ Say Y here if you have a Realtek 8169 PCI Gigabit Ethernet adapter.
+
+ To compile this driver as a module, choose M here: the module
+ will be called r8169. This is recommended.
+
+endif # NET_VENDOR_REALTEK
diff --git a/drivers/net/ethernet/realtek/Makefile b/drivers/net/ethernet/realtek/Makefile
new file mode 100644
index 000000000000..71b1da30ecb5
--- /dev/null
+++ b/drivers/net/ethernet/realtek/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the Realtek network device drivers.
+#
+
+obj-$(CONFIG_8139CP) += 8139cp.o
+obj-$(CONFIG_8139TOO) += 8139too.o
+obj-$(CONFIG_ATP) += atp.o
+obj-$(CONFIG_R8169) += r8169.o
diff --git a/drivers/net/atp.c b/drivers/net/ethernet/realtek/atp.c
index f3459798b0e9..e3f57fdbf0ea 100644
--- a/drivers/net/atp.c
+++ b/drivers/net/ethernet/realtek/atp.c
@@ -245,7 +245,7 @@ static const struct net_device_ops atp_netdev_ops = {
.ndo_open = net_open,
.ndo_stop = net_close,
.ndo_start_xmit = atp_send_packet,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_tx_timeout = tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/atp.h b/drivers/net/ethernet/realtek/atp.h
index 0edc642c2c2f..0edc642c2c2f 100644
--- a/drivers/net/atp.h
+++ b/drivers/net/ethernet/realtek/atp.h
diff --git a/drivers/net/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 5990621fb5cd..6f06aa10f0d7 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -22,6 +22,7 @@
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/firmware.h>
@@ -40,6 +41,9 @@
#define FIRMWARE_8168D_2 "rtl_nic/rtl8168d-2.fw"
#define FIRMWARE_8168E_1 "rtl_nic/rtl8168e-1.fw"
#define FIRMWARE_8168E_2 "rtl_nic/rtl8168e-2.fw"
+#define FIRMWARE_8168E_3 "rtl_nic/rtl8168e-3.fw"
+#define FIRMWARE_8168F_1 "rtl_nic/rtl8168f-1.fw"
+#define FIRMWARE_8168F_2 "rtl_nic/rtl8168f-2.fw"
#define FIRMWARE_8105E_1 "rtl_nic/rtl8105e-1.fw"
#ifdef RTL8169_DEBUG
@@ -69,8 +73,6 @@ static const int multicast_filter_limit = 32;
#define MAC_ADDR_LEN 6
#define MAX_READ_REQUEST_SHIFT 12
-#define RX_FIFO_THRESH 7 /* 7 means NO threshold, Rx buffer level before first PCI xfer. */
-#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
#define SafeMtu 0x1c20 /* ... actually life sucks beyond ~7k */
#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
@@ -132,6 +134,9 @@ enum mac_version {
RTL_GIGA_MAC_VER_31,
RTL_GIGA_MAC_VER_32,
RTL_GIGA_MAC_VER_33,
+ RTL_GIGA_MAC_VER_34,
+ RTL_GIGA_MAC_VER_35,
+ RTL_GIGA_MAC_VER_36,
RTL_GIGA_MAC_NONE = 0xff,
};
@@ -140,82 +145,110 @@ enum rtl_tx_desc_version {
RTL_TD_1 = 1,
};
-#define _R(NAME,TD,FW) \
- { .name = NAME, .txd_version = TD, .fw_name = FW }
+#define JUMBO_1K ETH_DATA_LEN
+#define JUMBO_4K (4*1024 - ETH_HLEN - 2)
+#define JUMBO_6K (6*1024 - ETH_HLEN - 2)
+#define JUMBO_7K (7*1024 - ETH_HLEN - 2)
+#define JUMBO_9K (9*1024 - ETH_HLEN - 2)
+
+#define _R(NAME,TD,FW,SZ,B) { \
+ .name = NAME, \
+ .txd_version = TD, \
+ .fw_name = FW, \
+ .jumbo_max = SZ, \
+ .jumbo_tx_csum = B \
+}
static const struct {
const char *name;
enum rtl_tx_desc_version txd_version;
const char *fw_name;
+ u16 jumbo_max;
+ bool jumbo_tx_csum;
} rtl_chip_infos[] = {
/* PCI devices. */
[RTL_GIGA_MAC_VER_01] =
- _R("RTL8169", RTL_TD_0, NULL),
+ _R("RTL8169", RTL_TD_0, NULL, JUMBO_7K, true),
[RTL_GIGA_MAC_VER_02] =
- _R("RTL8169s", RTL_TD_0, NULL),
+ _R("RTL8169s", RTL_TD_0, NULL, JUMBO_7K, true),
[RTL_GIGA_MAC_VER_03] =
- _R("RTL8110s", RTL_TD_0, NULL),
+ _R("RTL8110s", RTL_TD_0, NULL, JUMBO_7K, true),
[RTL_GIGA_MAC_VER_04] =
- _R("RTL8169sb/8110sb", RTL_TD_0, NULL),
+ _R("RTL8169sb/8110sb", RTL_TD_0, NULL, JUMBO_7K, true),
[RTL_GIGA_MAC_VER_05] =
- _R("RTL8169sc/8110sc", RTL_TD_0, NULL),
+ _R("RTL8169sc/8110sc", RTL_TD_0, NULL, JUMBO_7K, true),
[RTL_GIGA_MAC_VER_06] =
- _R("RTL8169sc/8110sc", RTL_TD_0, NULL),
+ _R("RTL8169sc/8110sc", RTL_TD_0, NULL, JUMBO_7K, true),
/* PCI-E devices. */
[RTL_GIGA_MAC_VER_07] =
- _R("RTL8102e", RTL_TD_1, NULL),
+ _R("RTL8102e", RTL_TD_1, NULL, JUMBO_1K, true),
[RTL_GIGA_MAC_VER_08] =
- _R("RTL8102e", RTL_TD_1, NULL),
+ _R("RTL8102e", RTL_TD_1, NULL, JUMBO_1K, true),
[RTL_GIGA_MAC_VER_09] =
- _R("RTL8102e", RTL_TD_1, NULL),
+ _R("RTL8102e", RTL_TD_1, NULL, JUMBO_1K, true),
[RTL_GIGA_MAC_VER_10] =
- _R("RTL8101e", RTL_TD_0, NULL),
+ _R("RTL8101e", RTL_TD_0, NULL, JUMBO_1K, true),
[RTL_GIGA_MAC_VER_11] =
- _R("RTL8168b/8111b", RTL_TD_0, NULL),
+ _R("RTL8168b/8111b", RTL_TD_0, NULL, JUMBO_4K, false),
[RTL_GIGA_MAC_VER_12] =
- _R("RTL8168b/8111b", RTL_TD_0, NULL),
+ _R("RTL8168b/8111b", RTL_TD_0, NULL, JUMBO_4K, false),
[RTL_GIGA_MAC_VER_13] =
- _R("RTL8101e", RTL_TD_0, NULL),
+ _R("RTL8101e", RTL_TD_0, NULL, JUMBO_1K, true),
[RTL_GIGA_MAC_VER_14] =
- _R("RTL8100e", RTL_TD_0, NULL),
+ _R("RTL8100e", RTL_TD_0, NULL, JUMBO_1K, true),
[RTL_GIGA_MAC_VER_15] =
- _R("RTL8100e", RTL_TD_0, NULL),
+ _R("RTL8100e", RTL_TD_0, NULL, JUMBO_1K, true),
[RTL_GIGA_MAC_VER_16] =
- _R("RTL8101e", RTL_TD_0, NULL),
+ _R("RTL8101e", RTL_TD_0, NULL, JUMBO_1K, true),
[RTL_GIGA_MAC_VER_17] =
- _R("RTL8168b/8111b", RTL_TD_0, NULL),
+ _R("RTL8168b/8111b", RTL_TD_1, NULL, JUMBO_4K, false),
[RTL_GIGA_MAC_VER_18] =
- _R("RTL8168cp/8111cp", RTL_TD_1, NULL),
+ _R("RTL8168cp/8111cp", RTL_TD_1, NULL, JUMBO_6K, false),
[RTL_GIGA_MAC_VER_19] =
- _R("RTL8168c/8111c", RTL_TD_1, NULL),
+ _R("RTL8168c/8111c", RTL_TD_1, NULL, JUMBO_6K, false),
[RTL_GIGA_MAC_VER_20] =
- _R("RTL8168c/8111c", RTL_TD_1, NULL),
+ _R("RTL8168c/8111c", RTL_TD_1, NULL, JUMBO_6K, false),
[RTL_GIGA_MAC_VER_21] =
- _R("RTL8168c/8111c", RTL_TD_1, NULL),
+ _R("RTL8168c/8111c", RTL_TD_1, NULL, JUMBO_6K, false),
[RTL_GIGA_MAC_VER_22] =
- _R("RTL8168c/8111c", RTL_TD_1, NULL),
+ _R("RTL8168c/8111c", RTL_TD_1, NULL, JUMBO_6K, false),
[RTL_GIGA_MAC_VER_23] =
- _R("RTL8168cp/8111cp", RTL_TD_1, NULL),
+ _R("RTL8168cp/8111cp", RTL_TD_1, NULL, JUMBO_6K, false),
[RTL_GIGA_MAC_VER_24] =
- _R("RTL8168cp/8111cp", RTL_TD_1, NULL),
+ _R("RTL8168cp/8111cp", RTL_TD_1, NULL, JUMBO_6K, false),
[RTL_GIGA_MAC_VER_25] =
- _R("RTL8168d/8111d", RTL_TD_1, FIRMWARE_8168D_1),
+ _R("RTL8168d/8111d", RTL_TD_1, FIRMWARE_8168D_1,
+ JUMBO_9K, false),
[RTL_GIGA_MAC_VER_26] =
- _R("RTL8168d/8111d", RTL_TD_1, FIRMWARE_8168D_2),
+ _R("RTL8168d/8111d", RTL_TD_1, FIRMWARE_8168D_2,
+ JUMBO_9K, false),
[RTL_GIGA_MAC_VER_27] =
- _R("RTL8168dp/8111dp", RTL_TD_1, NULL),
+ _R("RTL8168dp/8111dp", RTL_TD_1, NULL, JUMBO_9K, false),
[RTL_GIGA_MAC_VER_28] =
- _R("RTL8168dp/8111dp", RTL_TD_1, NULL),
+ _R("RTL8168dp/8111dp", RTL_TD_1, NULL, JUMBO_9K, false),
[RTL_GIGA_MAC_VER_29] =
- _R("RTL8105e", RTL_TD_1, FIRMWARE_8105E_1),
+ _R("RTL8105e", RTL_TD_1, FIRMWARE_8105E_1,
+ JUMBO_1K, true),
[RTL_GIGA_MAC_VER_30] =
- _R("RTL8105e", RTL_TD_1, FIRMWARE_8105E_1),
+ _R("RTL8105e", RTL_TD_1, FIRMWARE_8105E_1,
+ JUMBO_1K, true),
[RTL_GIGA_MAC_VER_31] =
- _R("RTL8168dp/8111dp", RTL_TD_1, NULL),
+ _R("RTL8168dp/8111dp", RTL_TD_1, NULL, JUMBO_9K, false),
[RTL_GIGA_MAC_VER_32] =
- _R("RTL8168e/8111e", RTL_TD_1, FIRMWARE_8168E_1),
+ _R("RTL8168e/8111e", RTL_TD_1, FIRMWARE_8168E_1,
+ JUMBO_9K, false),
[RTL_GIGA_MAC_VER_33] =
- _R("RTL8168e/8111e", RTL_TD_1, FIRMWARE_8168E_2)
+ _R("RTL8168e/8111e", RTL_TD_1, FIRMWARE_8168E_2,
+ JUMBO_9K, false),
+ [RTL_GIGA_MAC_VER_34] =
+ _R("RTL8168evl/8111evl",RTL_TD_1, FIRMWARE_8168E_3,
+ JUMBO_9K, false),
+ [RTL_GIGA_MAC_VER_35] =
+ _R("RTL8168f/8111f", RTL_TD_1, FIRMWARE_8168F_1,
+ JUMBO_9K, false),
+ [RTL_GIGA_MAC_VER_36] =
+ _R("RTL8168f/8111f", RTL_TD_1, FIRMWARE_8168F_2,
+ JUMBO_9K, false),
};
#undef _R
@@ -236,6 +269,7 @@ static DEFINE_PCI_DEVICE_TABLE(rtl8169_pci_tbl) = {
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), 0, 0, RTL_CFG_1 },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), 0, 0, RTL_CFG_0 },
{ PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4300), 0, 0, RTL_CFG_0 },
+ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4302), 0, 0, RTL_CFG_0 },
{ PCI_DEVICE(PCI_VENDOR_ID_AT, 0xc107), 0, 0, RTL_CFG_0 },
{ PCI_DEVICE(0x16ec, 0x0116), 0, 0, RTL_CFG_0 },
{ PCI_VENDOR_ID_LINKSYS, 0x1032,
@@ -269,10 +303,20 @@ enum rtl_registers {
TxPoll = 0x38,
IntrMask = 0x3c,
IntrStatus = 0x3e,
+
TxConfig = 0x40,
- RxConfig = 0x44,
+#define TXCFG_AUTO_FIFO (1 << 7) /* 8111e-vl */
+#define TXCFG_EMPTY (1 << 11) /* 8111e-vl */
-#define RTL_RX_CONFIG_MASK 0xff7e1880u
+ RxConfig = 0x44,
+#define RX128_INT_EN (1 << 15) /* 8111c and later */
+#define RX_MULTI_EN (1 << 14) /* 8111c only */
+#define RXCFG_FIFO_SHIFT 13
+ /* No threshold before first PCI xfer */
+#define RX_FIFO_THRESH (7 << RXCFG_FIFO_SHIFT)
+#define RXCFG_DMA_SHIFT 8
+ /* Unlimited maximum PCI burst. */
+#define RX_DMA_BURST (7 << RXCFG_DMA_SHIFT)
RxMissed = 0x4c,
Cfg9346 = 0x50,
@@ -297,6 +341,7 @@ enum rtl_registers {
MaxTxPacketSize = 0xec, /* 8101/8168. Unit of 128 bytes. */
#define TxPacketMax (8064 >> 7)
+#define EarlySize 0x27
FuncEvent = 0xf0,
FuncEventMask = 0xf4,
@@ -326,12 +371,13 @@ enum rtl8168_8101_registers {
#define EPHYAR_REG_SHIFT 16
#define EPHYAR_DATA_MASK 0xffff
DLLPR = 0xd0,
-#define PM_SWITCH (1 << 6)
+#define PFM_EN (1 << 6)
DBG_REG = 0xd1,
#define FIX_NAK_1 (1 << 4)
#define FIX_NAK_2 (1 << 3)
TWSI = 0xd2,
MCU = 0xd3,
+#define NOW_IS_OOB (1 << 7)
#define EN_NDP (1 << 3)
#define EN_OOB_RESET (1 << 2)
EFUSEAR = 0xdc,
@@ -344,18 +390,22 @@ enum rtl8168_8101_registers {
};
enum rtl8168_registers {
+ LED_FREQ = 0x1a,
+ EEE_LED = 0x1b,
ERIDR = 0x70,
ERIAR = 0x74,
#define ERIAR_FLAG 0x80000000
#define ERIAR_WRITE_CMD 0x80000000
#define ERIAR_READ_CMD 0x00000000
#define ERIAR_ADDR_BYTE_ALIGN 4
-#define ERIAR_EXGMAC 0
-#define ERIAR_MSIX 1
-#define ERIAR_ASF 2
#define ERIAR_TYPE_SHIFT 16
-#define ERIAR_BYTEEN 0x0f
-#define ERIAR_BYTEEN_SHIFT 12
+#define ERIAR_EXGMAC (0x00 << ERIAR_TYPE_SHIFT)
+#define ERIAR_MSIX (0x01 << ERIAR_TYPE_SHIFT)
+#define ERIAR_ASF (0x02 << ERIAR_TYPE_SHIFT)
+#define ERIAR_MASK_SHIFT 12
+#define ERIAR_MASK_0001 (0x1 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_0011 (0x3 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_1111 (0xf << ERIAR_MASK_SHIFT)
EPHY_RXER_NUM = 0x7c,
OCPDR = 0xb0, /* OCP GPHY access */
#define OCPDR_WRITE_CMD 0x80000000
@@ -370,6 +420,7 @@ enum rtl8168_registers {
RDSAR1 = 0xd0, /* 8168c only. Undocumented on 8168dp */
MISC = 0xf0, /* 8168e only. */
#define TXPLA_RST (1 << 29)
+#define PWM_EN (1 << 22)
};
enum rtl_register_content {
@@ -387,6 +438,7 @@ enum rtl_register_content {
RxOK = 0x0001,
/* RxStatusDesc */
+ RxBOVF = (1 << 24),
RxFOVF = (1 << 23),
RxRWT = (1 << 22),
RxRES = (1 << 21),
@@ -394,6 +446,7 @@ enum rtl_register_content {
RxCRC = (1 << 19),
/* ChipCmdBits */
+ StopReq = 0x80,
CmdReset = 0x10,
CmdRxEnb = 0x08,
CmdTxEnb = 0x04,
@@ -415,10 +468,7 @@ enum rtl_register_content {
AcceptMulticast = 0x04,
AcceptMyPhys = 0x02,
AcceptAllPhys = 0x01,
-
- /* RxConfigBits */
- RxCfgFIFOShift = 13,
- RxCfgDMAShift = 8,
+#define RX_CONFIG_ACCEPT_MASK 0x3f
/* TxConfigBits */
TxInterFrameGapShift = 24,
@@ -441,8 +491,12 @@ enum rtl_register_content {
/* Config3 register p.25 */
MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */
LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */
+ Jumbo_En0 = (1 << 2), /* 8168 only. Reserved in the 8168b */
Beacon_en = (1 << 0), /* 8168 only. Reserved in the 8168b */
+ /* Config4 register */
+ Jumbo_En1 = (1 << 1), /* 8168 only. Reserved in the 8168b */
+
/* Config5 register p.27 */
BWF = (1 << 6), /* Accept Broadcast wakeup frame */
MWF = (1 << 5), /* Accept Multicast wakeup frame */
@@ -651,6 +705,11 @@ struct rtl8169_private {
void (*up)(struct rtl8169_private *);
} pll_power_ops;
+ struct jumbo_ops {
+ void (*enable)(struct rtl8169_private *);
+ void (*disable)(struct rtl8169_private *);
+ } jumbo_ops;
+
int (*set_speed)(struct net_device *, u8 aneg, u16 sp, u8 dpx, u32 adv);
int (*get_settings)(struct net_device *, struct ethtool_cmd *);
void (*phy_reset_enable)(struct rtl8169_private *tp);
@@ -658,16 +717,27 @@ struct rtl8169_private {
unsigned int (*phy_reset_pending)(struct rtl8169_private *tp);
unsigned int (*link_ok)(void __iomem *);
int (*do_ioctl)(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd);
- int pcie_cap;
struct delayed_work task;
unsigned features;
struct mii_if_info mii;
struct rtl8169_counters counters;
u32 saved_wolopts;
+ u32 opts1_mask;
+
+ struct rtl_fw {
+ const struct firmware *fw;
- const struct firmware *fw;
-#define RTL_FIRMWARE_UNKNOWN ERR_PTR(-EAGAIN);
+#define RTL_VER_SIZE 32
+
+ char version[RTL_VER_SIZE];
+
+ struct rtl_fw_phy_action {
+ __le32 *code;
+ size_t size;
+ } phy_action;
+ } *rtl_fw;
+#define RTL_FIRMWARE_UNKNOWN ERR_PTR(-EAGAIN)
};
MODULE_AUTHOR("Realtek and the Linux r8169 crew <netdev@vger.kernel.org>");
@@ -682,7 +752,10 @@ MODULE_FIRMWARE(FIRMWARE_8168D_1);
MODULE_FIRMWARE(FIRMWARE_8168D_2);
MODULE_FIRMWARE(FIRMWARE_8168E_1);
MODULE_FIRMWARE(FIRMWARE_8168E_2);
+MODULE_FIRMWARE(FIRMWARE_8168E_3);
MODULE_FIRMWARE(FIRMWARE_8105E_1);
+MODULE_FIRMWARE(FIRMWARE_8168F_1);
+MODULE_FIRMWARE(FIRMWARE_8168F_2);
static int rtl8169_open(struct net_device *dev);
static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
@@ -701,8 +774,18 @@ static void rtl8169_down(struct net_device *dev);
static void rtl8169_rx_clear(struct rtl8169_private *tp);
static int rtl8169_poll(struct napi_struct *napi, int budget);
-static const unsigned int rtl8169_rx_config =
- (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift);
+static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force)
+{
+ int cap = pci_pcie_cap(pdev);
+
+ if (cap) {
+ u16 ctl;
+
+ pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl);
+ ctl = (ctl & ~PCI_EXP_DEVCTL_READRQ) | force;
+ pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl);
+ }
+}
static u32 ocp_read(struct rtl8169_private *tp, u8 mask, u16 reg)
{
@@ -1024,6 +1107,64 @@ static u32 rtl_csi_read(void __iomem *ioaddr, int addr)
return value;
}
+static
+void rtl_eri_write(void __iomem *ioaddr, int addr, u32 mask, u32 val, int type)
+{
+ unsigned int i;
+
+ BUG_ON((addr & 3) || (mask == 0));
+ RTL_W32(ERIDR, val);
+ RTL_W32(ERIAR, ERIAR_WRITE_CMD | type | mask | addr);
+
+ for (i = 0; i < 100; i++) {
+ if (!(RTL_R32(ERIAR) & ERIAR_FLAG))
+ break;
+ udelay(100);
+ }
+}
+
+static u32 rtl_eri_read(void __iomem *ioaddr, int addr, int type)
+{
+ u32 value = ~0x00;
+ unsigned int i;
+
+ RTL_W32(ERIAR, ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr);
+
+ for (i = 0; i < 100; i++) {
+ if (RTL_R32(ERIAR) & ERIAR_FLAG) {
+ value = RTL_R32(ERIDR);
+ break;
+ }
+ udelay(100);
+ }
+
+ return value;
+}
+
+static void
+rtl_w1w0_eri(void __iomem *ioaddr, int addr, u32 mask, u32 p, u32 m, int type)
+{
+ u32 val;
+
+ val = rtl_eri_read(ioaddr, addr, type);
+ rtl_eri_write(ioaddr, addr, mask, (val & ~m) | p, type);
+}
+
+struct exgmac_reg {
+ u16 addr;
+ u16 mask;
+ u32 val;
+};
+
+static void rtl_write_exgmac_batch(void __iomem *ioaddr,
+ const struct exgmac_reg *r, int len)
+{
+ while (len-- > 0) {
+ rtl_eri_write(ioaddr, r->addr, r->mask, r->val, ERIAR_EXGMAC);
+ r++;
+ }
+}
+
static u8 rtl8168d_efuse_read(void __iomem *ioaddr, int reg_addr)
{
u8 value = 0xff;
@@ -1049,13 +1190,6 @@ static void rtl8169_irq_mask_and_ack(void __iomem *ioaddr)
RTL_W16(IntrStatus, 0xffff);
}
-static void rtl8169_asic_down(void __iomem *ioaddr)
-{
- RTL_W8(ChipCmd, 0x00);
- rtl8169_irq_mask_and_ack(ioaddr);
- RTL_R16(CPlusCmd);
-}
-
static unsigned int rtl8169_tbi_reset_pending(struct rtl8169_private *tp)
{
void __iomem *ioaddr = tp->mmio_addr;
@@ -1093,6 +1227,52 @@ static void rtl8169_xmii_reset_enable(struct rtl8169_private *tp)
rtl_writephy(tp, MII_BMCR, val & 0xffff);
}
+static void rtl_link_chg_patch(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ struct net_device *dev = tp->dev;
+
+ if (!netif_running(dev))
+ return;
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_34) {
+ if (RTL_R8(PHYstatus) & _1000bpsF) {
+ rtl_eri_write(ioaddr, 0x1bc, ERIAR_MASK_1111,
+ 0x00000011, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0x1dc, ERIAR_MASK_1111,
+ 0x00000005, ERIAR_EXGMAC);
+ } else if (RTL_R8(PHYstatus) & _100bps) {
+ rtl_eri_write(ioaddr, 0x1bc, ERIAR_MASK_1111,
+ 0x0000001f, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0x1dc, ERIAR_MASK_1111,
+ 0x00000005, ERIAR_EXGMAC);
+ } else {
+ rtl_eri_write(ioaddr, 0x1bc, ERIAR_MASK_1111,
+ 0x0000001f, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0x1dc, ERIAR_MASK_1111,
+ 0x0000003f, ERIAR_EXGMAC);
+ }
+ /* Reset packet filter */
+ rtl_w1w0_eri(ioaddr, 0xdc, ERIAR_MASK_0001, 0x00, 0x01,
+ ERIAR_EXGMAC);
+ rtl_w1w0_eri(ioaddr, 0xdc, ERIAR_MASK_0001, 0x01, 0x00,
+ ERIAR_EXGMAC);
+ } else if (tp->mac_version == RTL_GIGA_MAC_VER_35 ||
+ tp->mac_version == RTL_GIGA_MAC_VER_36) {
+ if (RTL_R8(PHYstatus) & _1000bpsF) {
+ rtl_eri_write(ioaddr, 0x1bc, ERIAR_MASK_1111,
+ 0x00000011, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0x1dc, ERIAR_MASK_1111,
+ 0x00000005, ERIAR_EXGMAC);
+ } else {
+ rtl_eri_write(ioaddr, 0x1bc, ERIAR_MASK_1111,
+ 0x0000001f, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0x1dc, ERIAR_MASK_1111,
+ 0x0000003f, ERIAR_EXGMAC);
+ }
+ }
+}
+
static void __rtl8169_check_link_status(struct net_device *dev,
struct rtl8169_private *tp,
void __iomem *ioaddr, bool pm)
@@ -1101,6 +1281,7 @@ static void __rtl8169_check_link_status(struct net_device *dev,
spin_lock_irqsave(&tp->lock, flags);
if (tp->link_ok(ioaddr)) {
+ rtl_link_chg_patch(tp);
/* This is to cancel a scheduled suspend if there's one. */
if (pm)
pm_request_resume(&tp->pci_dev->dev);
@@ -1111,7 +1292,7 @@ static void __rtl8169_check_link_status(struct net_device *dev,
netif_carrier_off(dev);
netif_info(tp, ifdown, dev, "link down\n");
if (pm)
- pm_schedule_suspend(&tp->pci_dev->dev, 100);
+ pm_schedule_suspend(&tp->pci_dev->dev, 5000);
}
spin_unlock_irqrestore(&tp->lock, flags);
}
@@ -1221,12 +1402,14 @@ static void rtl8169_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct rtl8169_private *tp = netdev_priv(dev);
+ struct rtl_fw *rtl_fw = tp->rtl_fw;
strcpy(info->driver, MODULENAME);
strcpy(info->version, RTL8169_VERSION);
strcpy(info->bus_info, pci_name(tp->pci_dev));
- strncpy(info->fw_version, IS_ERR_OR_NULL(tp->fw) ? "N/A" :
- rtl_lookup_firmware_name(tp), sizeof(info->fw_version) - 1);
+ BUILD_BUG_ON(sizeof(info->fw_version) < sizeof(rtl_fw->version));
+ strcpy(info->fw_version, IS_ERR_OR_NULL(rtl_fw) ? "N/A" :
+ rtl_fw->version);
}
static int rtl8169_get_regs_len(struct net_device *dev)
@@ -1372,9 +1555,15 @@ static int rtl8169_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static u32 rtl8169_fix_features(struct net_device *dev, u32 features)
{
+ struct rtl8169_private *tp = netdev_priv(dev);
+
if (dev->mtu > TD_MSS_MAX)
features &= ~NETIF_F_ALL_TSO;
+ if (dev->mtu > JUMBO_1K &&
+ !rtl_chip_infos[tp->mac_version].jumbo_tx_csum)
+ features &= ~NETIF_F_IP_CSUM;
+
return features;
}
@@ -1626,7 +1815,12 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp,
u32 val;
int mac_version;
} mac_info[] = {
+ /* 8168F family. */
+ { 0x7cf00000, 0x48100000, RTL_GIGA_MAC_VER_36 },
+ { 0x7cf00000, 0x48000000, RTL_GIGA_MAC_VER_35 },
+
/* 8168E family. */
+ { 0x7c800000, 0x2c800000, RTL_GIGA_MAC_VER_34 },
{ 0x7cf00000, 0x2c200000, RTL_GIGA_MAC_VER_33 },
{ 0x7cf00000, 0x2c100000, RTL_GIGA_MAC_VER_32 },
{ 0x7c800000, 0x2c000000, RTL_GIGA_MAC_VER_33 },
@@ -1741,21 +1935,75 @@ static void rtl_writephy_batch(struct rtl8169_private *tp,
#define PHY_DELAY_MS 0xe0000000
#define PHY_WRITE_ERI_WORD 0xf0000000
-static void
-rtl_phy_write_fw(struct rtl8169_private *tp, const struct firmware *fw)
+struct fw_info {
+ u32 magic;
+ char version[RTL_VER_SIZE];
+ __le32 fw_start;
+ __le32 fw_len;
+ u8 chksum;
+} __packed;
+
+#define FW_OPCODE_SIZE sizeof(typeof(*((struct rtl_fw_phy_action *)0)->code))
+
+static bool rtl_fw_format_ok(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
{
- __le32 *phytable = (__le32 *)fw->data;
- struct net_device *dev = tp->dev;
- size_t index, fw_size = fw->size / sizeof(*phytable);
- u32 predata, count;
+ const struct firmware *fw = rtl_fw->fw;
+ struct fw_info *fw_info = (struct fw_info *)fw->data;
+ struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
+ char *version = rtl_fw->version;
+ bool rc = false;
- if (fw->size % sizeof(*phytable)) {
- netif_err(tp, probe, dev, "odd sized firmware %zd\n", fw->size);
- return;
+ if (fw->size < FW_OPCODE_SIZE)
+ goto out;
+
+ if (!fw_info->magic) {
+ size_t i, size, start;
+ u8 checksum = 0;
+
+ if (fw->size < sizeof(*fw_info))
+ goto out;
+
+ for (i = 0; i < fw->size; i++)
+ checksum += fw->data[i];
+ if (checksum != 0)
+ goto out;
+
+ start = le32_to_cpu(fw_info->fw_start);
+ if (start > fw->size)
+ goto out;
+
+ size = le32_to_cpu(fw_info->fw_len);
+ if (size > (fw->size - start) / FW_OPCODE_SIZE)
+ goto out;
+
+ memcpy(version, fw_info->version, RTL_VER_SIZE);
+
+ pa->code = (__le32 *)(fw->data + start);
+ pa->size = size;
+ } else {
+ if (fw->size % FW_OPCODE_SIZE)
+ goto out;
+
+ strlcpy(version, rtl_lookup_firmware_name(tp), RTL_VER_SIZE);
+
+ pa->code = (__le32 *)fw->data;
+ pa->size = fw->size / FW_OPCODE_SIZE;
}
+ version[RTL_VER_SIZE - 1] = 0;
+
+ rc = true;
+out:
+ return rc;
+}
+
+static bool rtl_fw_data_ok(struct rtl8169_private *tp, struct net_device *dev,
+ struct rtl_fw_phy_action *pa)
+{
+ bool rc = false;
+ size_t index;
- for (index = 0; index < fw_size; index++) {
- u32 action = le32_to_cpu(phytable[index]);
+ for (index = 0; index < pa->size; index++) {
+ u32 action = le32_to_cpu(pa->code[index]);
u32 regno = (action & 0x0fff0000) >> 16;
switch(action & 0xf0000000) {
@@ -1771,25 +2019,25 @@ rtl_phy_write_fw(struct rtl8169_private *tp, const struct firmware *fw)
case PHY_BJMPN:
if (regno > index) {
- netif_err(tp, probe, tp->dev,
+ netif_err(tp, ifup, tp->dev,
"Out of range of firmware\n");
- return;
+ goto out;
}
break;
case PHY_READCOUNT_EQ_SKIP:
- if (index + 2 >= fw_size) {
- netif_err(tp, probe, tp->dev,
+ if (index + 2 >= pa->size) {
+ netif_err(tp, ifup, tp->dev,
"Out of range of firmware\n");
- return;
+ goto out;
}
break;
case PHY_COMP_EQ_SKIPN:
case PHY_COMP_NEQ_SKIPN:
case PHY_SKIPN:
- if (index + 1 + regno >= fw_size) {
- netif_err(tp, probe, tp->dev,
+ if (index + 1 + regno >= pa->size) {
+ netif_err(tp, ifup, tp->dev,
"Out of range of firmware\n");
- return;
+ goto out;
}
break;
@@ -1797,17 +2045,42 @@ rtl_phy_write_fw(struct rtl8169_private *tp, const struct firmware *fw)
case PHY_WRITE_MAC_BYTE:
case PHY_WRITE_ERI_WORD:
default:
- netif_err(tp, probe, tp->dev,
+ netif_err(tp, ifup, tp->dev,
"Invalid action 0x%08x\n", action);
- return;
+ goto out;
}
}
+ rc = true;
+out:
+ return rc;
+}
- predata = 0;
- count = 0;
+static int rtl_check_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
+{
+ struct net_device *dev = tp->dev;
+ int rc = -EINVAL;
- for (index = 0; index < fw_size; ) {
- u32 action = le32_to_cpu(phytable[index]);
+ if (!rtl_fw_format_ok(tp, rtl_fw)) {
+ netif_err(tp, ifup, dev, "invalid firwmare\n");
+ goto out;
+ }
+
+ if (rtl_fw_data_ok(tp, dev, &rtl_fw->phy_action))
+ rc = 0;
+out:
+ return rc;
+}
+
+static void rtl_phy_write_fw(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
+{
+ struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
+ u32 predata, count;
+ size_t index;
+
+ predata = count = 0;
+
+ for (index = 0; index < pa->size; ) {
+ u32 action = le32_to_cpu(pa->code[index]);
u32 data = action & 0x0000ffff;
u32 regno = (action & 0x0fff0000) >> 16;
@@ -1879,18 +2152,20 @@ rtl_phy_write_fw(struct rtl8169_private *tp, const struct firmware *fw)
static void rtl_release_firmware(struct rtl8169_private *tp)
{
- if (!IS_ERR_OR_NULL(tp->fw))
- release_firmware(tp->fw);
- tp->fw = RTL_FIRMWARE_UNKNOWN;
+ if (!IS_ERR_OR_NULL(tp->rtl_fw)) {
+ release_firmware(tp->rtl_fw->fw);
+ kfree(tp->rtl_fw);
+ }
+ tp->rtl_fw = RTL_FIRMWARE_UNKNOWN;
}
static void rtl_apply_firmware(struct rtl8169_private *tp)
{
- const struct firmware *fw = tp->fw;
+ struct rtl_fw *rtl_fw = tp->rtl_fw;
/* TODO: release firmware once rtl_phy_write_fw signals failures. */
- if (!IS_ERR_OR_NULL(fw))
- rtl_phy_write_fw(tp, fw);
+ if (!IS_ERR_OR_NULL(rtl_fw))
+ rtl_phy_write_fw(tp, rtl_fw);
}
static void rtl_apply_firmware_cond(struct rtl8169_private *tp, u8 reg, u16 val)
@@ -1982,12 +2257,9 @@ static void rtl8169sb_hw_phy_config(struct rtl8169_private *tp)
static void rtl8169scd_hw_phy_config_quirk(struct rtl8169_private *tp)
{
struct pci_dev *pdev = tp->pci_dev;
- u16 vendor_id, device_id;
- pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &vendor_id);
- pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &device_id);
-
- if ((vendor_id != PCI_VENDOR_ID_GIGABYTE) || (device_id != 0xe000))
+ if ((pdev->subsystem_vendor != PCI_VENDOR_ID_GIGABYTE) ||
+ (pdev->subsystem_device != 0xe000))
return;
rtl_writephy(tp, 0x1f, 0x0001);
@@ -2523,7 +2795,7 @@ static void rtl8168d_4_hw_phy_config(struct rtl8169_private *tp)
rtl_patchphy(tp, 0x0d, 1 << 5);
}
-static void rtl8168e_hw_phy_config(struct rtl8169_private *tp)
+static void rtl8168e_1_hw_phy_config(struct rtl8169_private *tp)
{
static const struct phy_reg phy_reg_init[] = {
/* Enable Delay cap */
@@ -2596,6 +2868,182 @@ static void rtl8168e_hw_phy_config(struct rtl8169_private *tp)
rtl_writephy(tp, 0x0d, 0x0000);
}
+static void rtl8168e_2_hw_phy_config(struct rtl8169_private *tp)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ /* Enable Delay cap */
+ { 0x1f, 0x0004 },
+ { 0x1f, 0x0007 },
+ { 0x1e, 0x00ac },
+ { 0x18, 0x0006 },
+ { 0x1f, 0x0002 },
+ { 0x1f, 0x0000 },
+ { 0x1f, 0x0000 },
+
+ /* Channel estimation fine tune */
+ { 0x1f, 0x0003 },
+ { 0x09, 0xa20f },
+ { 0x1f, 0x0000 },
+ { 0x1f, 0x0000 },
+
+ /* Green Setting */
+ { 0x1f, 0x0005 },
+ { 0x05, 0x8b5b },
+ { 0x06, 0x9222 },
+ { 0x05, 0x8b6d },
+ { 0x06, 0x8000 },
+ { 0x05, 0x8b76 },
+ { 0x06, 0x8000 },
+ { 0x1f, 0x0000 }
+ };
+
+ rtl_apply_firmware(tp);
+
+ rtl_writephy_batch(tp, phy_reg_init, ARRAY_SIZE(phy_reg_init));
+
+ /* For 4-corner performance improve */
+ rtl_writephy(tp, 0x1f, 0x0005);
+ rtl_writephy(tp, 0x05, 0x8b80);
+ rtl_w1w0_phy(tp, 0x17, 0x0006, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0000);
+
+ /* PHY auto speed down */
+ rtl_writephy(tp, 0x1f, 0x0004);
+ rtl_writephy(tp, 0x1f, 0x0007);
+ rtl_writephy(tp, 0x1e, 0x002d);
+ rtl_w1w0_phy(tp, 0x18, 0x0010, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0002);
+ rtl_writephy(tp, 0x1f, 0x0000);
+ rtl_w1w0_phy(tp, 0x14, 0x8000, 0x0000);
+
+ /* improve 10M EEE waveform */
+ rtl_writephy(tp, 0x1f, 0x0005);
+ rtl_writephy(tp, 0x05, 0x8b86);
+ rtl_w1w0_phy(tp, 0x06, 0x0001, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0000);
+
+ /* Improve 2-pair detection performance */
+ rtl_writephy(tp, 0x1f, 0x0005);
+ rtl_writephy(tp, 0x05, 0x8b85);
+ rtl_w1w0_phy(tp, 0x06, 0x4000, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0000);
+
+ /* EEE setting */
+ rtl_w1w0_eri(tp->mmio_addr, 0x1b0, ERIAR_MASK_1111, 0x0000, 0x0003,
+ ERIAR_EXGMAC);
+ rtl_writephy(tp, 0x1f, 0x0005);
+ rtl_writephy(tp, 0x05, 0x8b85);
+ rtl_w1w0_phy(tp, 0x06, 0x0000, 0x2000);
+ rtl_writephy(tp, 0x1f, 0x0004);
+ rtl_writephy(tp, 0x1f, 0x0007);
+ rtl_writephy(tp, 0x1e, 0x0020);
+ rtl_w1w0_phy(tp, 0x15, 0x0000, 0x0100);
+ rtl_writephy(tp, 0x1f, 0x0002);
+ rtl_writephy(tp, 0x1f, 0x0000);
+ rtl_writephy(tp, 0x0d, 0x0007);
+ rtl_writephy(tp, 0x0e, 0x003c);
+ rtl_writephy(tp, 0x0d, 0x4007);
+ rtl_writephy(tp, 0x0e, 0x0000);
+ rtl_writephy(tp, 0x0d, 0x0000);
+
+ /* Green feature */
+ rtl_writephy(tp, 0x1f, 0x0003);
+ rtl_w1w0_phy(tp, 0x19, 0x0000, 0x0001);
+ rtl_w1w0_phy(tp, 0x10, 0x0000, 0x0400);
+ rtl_writephy(tp, 0x1f, 0x0000);
+}
+
+static void rtl8168f_1_hw_phy_config(struct rtl8169_private *tp)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ /* Channel estimation fine tune */
+ { 0x1f, 0x0003 },
+ { 0x09, 0xa20f },
+ { 0x1f, 0x0000 },
+
+ /* Modify green table for giga & fnet */
+ { 0x1f, 0x0005 },
+ { 0x05, 0x8b55 },
+ { 0x06, 0x0000 },
+ { 0x05, 0x8b5e },
+ { 0x06, 0x0000 },
+ { 0x05, 0x8b67 },
+ { 0x06, 0x0000 },
+ { 0x05, 0x8b70 },
+ { 0x06, 0x0000 },
+ { 0x1f, 0x0000 },
+ { 0x1f, 0x0007 },
+ { 0x1e, 0x0078 },
+ { 0x17, 0x0000 },
+ { 0x19, 0x00fb },
+ { 0x1f, 0x0000 },
+
+ /* Modify green table for 10M */
+ { 0x1f, 0x0005 },
+ { 0x05, 0x8b79 },
+ { 0x06, 0xaa00 },
+ { 0x1f, 0x0000 },
+
+ /* Disable hiimpedance detection (RTCT) */
+ { 0x1f, 0x0003 },
+ { 0x01, 0x328a },
+ { 0x1f, 0x0000 }
+ };
+
+ rtl_apply_firmware(tp);
+
+ rtl_writephy_batch(tp, phy_reg_init, ARRAY_SIZE(phy_reg_init));
+
+ /* For 4-corner performance improve */
+ rtl_writephy(tp, 0x1f, 0x0005);
+ rtl_writephy(tp, 0x05, 0x8b80);
+ rtl_w1w0_phy(tp, 0x06, 0x0006, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0000);
+
+ /* PHY auto speed down */
+ rtl_writephy(tp, 0x1f, 0x0007);
+ rtl_writephy(tp, 0x1e, 0x002d);
+ rtl_w1w0_phy(tp, 0x18, 0x0010, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0000);
+ rtl_w1w0_phy(tp, 0x14, 0x8000, 0x0000);
+
+ /* Improve 10M EEE waveform */
+ rtl_writephy(tp, 0x1f, 0x0005);
+ rtl_writephy(tp, 0x05, 0x8b86);
+ rtl_w1w0_phy(tp, 0x06, 0x0001, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0000);
+
+ /* Improve 2-pair detection performance */
+ rtl_writephy(tp, 0x1f, 0x0005);
+ rtl_writephy(tp, 0x05, 0x8b85);
+ rtl_w1w0_phy(tp, 0x06, 0x4000, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0000);
+}
+
+static void rtl8168f_2_hw_phy_config(struct rtl8169_private *tp)
+{
+ rtl_apply_firmware(tp);
+
+ /* For 4-corner performance improve */
+ rtl_writephy(tp, 0x1f, 0x0005);
+ rtl_writephy(tp, 0x05, 0x8b80);
+ rtl_w1w0_phy(tp, 0x06, 0x0006, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0000);
+
+ /* PHY auto speed down */
+ rtl_writephy(tp, 0x1f, 0x0007);
+ rtl_writephy(tp, 0x1e, 0x002d);
+ rtl_w1w0_phy(tp, 0x18, 0x0010, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0000);
+ rtl_w1w0_phy(tp, 0x14, 0x8000, 0x0000);
+
+ /* Improve 10M EEE waveform */
+ rtl_writephy(tp, 0x1f, 0x0005);
+ rtl_writephy(tp, 0x05, 0x8b86);
+ rtl_w1w0_phy(tp, 0x06, 0x0001, 0x0000);
+ rtl_writephy(tp, 0x1f, 0x0000);
+}
+
static void rtl8102e_hw_phy_config(struct rtl8169_private *tp)
{
static const struct phy_reg phy_reg_init[] = {
@@ -2715,7 +3163,16 @@ static void rtl_hw_phy_config(struct net_device *dev)
break;
case RTL_GIGA_MAC_VER_32:
case RTL_GIGA_MAC_VER_33:
- rtl8168e_hw_phy_config(tp);
+ rtl8168e_1_hw_phy_config(tp);
+ break;
+ case RTL_GIGA_MAC_VER_34:
+ rtl8168e_2_hw_phy_config(tp);
+ break;
+ case RTL_GIGA_MAC_VER_35:
+ rtl8168f_1_hw_phy_config(tp);
+ break;
+ case RTL_GIGA_MAC_VER_36:
+ rtl8168f_2_hw_phy_config(tp);
break;
default:
@@ -2798,6 +3255,14 @@ static void rtl8169_phy_reset(struct net_device *dev,
netif_err(tp, link, dev, "PHY reset failed\n");
}
+static bool rtl_tbi_enabled(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ return (tp->mac_version == RTL_GIGA_MAC_VER_01) &&
+ (RTL_R8(PHYstatus) & TBI_Enable);
+}
+
static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp)
{
void __iomem *ioaddr = tp->mmio_addr;
@@ -2830,7 +3295,7 @@ static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp)
ADVERTISED_1000baseT_Half |
ADVERTISED_1000baseT_Full : 0));
- if (RTL_R8(PHYstatus) & TBI_Enable)
+ if (rtl_tbi_enabled(tp))
netif_info(tp, link, dev, "TBI auto-negotiating\n");
}
@@ -2853,6 +3318,18 @@ static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr)
RTL_W32(MAC0, low);
RTL_R32(MAC0);
+ if (tp->mac_version == RTL_GIGA_MAC_VER_34) {
+ const struct exgmac_reg e[] = {
+ { .addr = 0xe0, ERIAR_MASK_1111, .val = low },
+ { .addr = 0xe4, ERIAR_MASK_1111, .val = high },
+ { .addr = 0xf0, ERIAR_MASK_1111, .val = low << 16 },
+ { .addr = 0xf4, ERIAR_MASK_1111, .val = high << 16 |
+ low >> 16 },
+ };
+
+ rtl_write_exgmac_batch(ioaddr, e, ARRAY_SIZE(e));
+ }
+
RTL_W8(Cfg9346, Cfg9346_Lock);
spin_unlock_irq(&tp->lock);
@@ -2986,7 +3463,7 @@ static const struct net_device_ops rtl8169_netdev_ops = {
.ndo_set_features = rtl8169_set_features,
.ndo_set_mac_address = rtl_set_mac_address,
.ndo_do_ioctl = rtl8169_ioctl,
- .ndo_set_multicast_list = rtl_set_rx_mode,
+ .ndo_set_rx_mode = rtl_set_rx_mode,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = rtl8169_netpoll,
#endif
@@ -3014,6 +3491,37 @@ static void __devinit rtl_init_mdio_ops(struct rtl8169_private *tp)
}
}
+static void rtl_wol_suspend_quirk(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_29:
+ case RTL_GIGA_MAC_VER_30:
+ case RTL_GIGA_MAC_VER_32:
+ case RTL_GIGA_MAC_VER_33:
+ case RTL_GIGA_MAC_VER_34:
+ RTL_W32(RxConfig, RTL_R32(RxConfig) |
+ AcceptBroadcast | AcceptMulticast | AcceptMyPhys);
+ break;
+ default:
+ break;
+ }
+}
+
+static bool rtl_wol_pll_power_down(struct rtl8169_private *tp)
+{
+ if (!(__rtl8169_get_wol(tp) & WAKE_ANY))
+ return false;
+
+ rtl_writephy(tp, 0x1f, 0x0000);
+ rtl_writephy(tp, MII_BMCR, 0x0000);
+
+ rtl_wol_suspend_quirk(tp);
+
+ return true;
+}
+
static void r810x_phy_power_down(struct rtl8169_private *tp)
{
rtl_writephy(tp, 0x1f, 0x0000);
@@ -3028,11 +3536,8 @@ static void r810x_phy_power_up(struct rtl8169_private *tp)
static void r810x_pll_power_down(struct rtl8169_private *tp)
{
- if (__rtl8169_get_wol(tp) & WAKE_ANY) {
- rtl_writephy(tp, 0x1f, 0x0000);
- rtl_writephy(tp, MII_BMCR, 0x0000);
+ if (rtl_wol_pll_power_down(tp))
return;
- }
r810x_phy_power_down(tp);
}
@@ -3121,14 +3626,8 @@ static void r8168_pll_power_down(struct rtl8169_private *tp)
tp->mac_version == RTL_GIGA_MAC_VER_33)
rtl_ephy_write(ioaddr, 0x19, 0xff64);
- if (__rtl8169_get_wol(tp) & WAKE_ANY) {
- rtl_writephy(tp, 0x1f, 0x0000);
- rtl_writephy(tp, MII_BMCR, 0x0000);
-
- RTL_W32(RxConfig, RTL_R32(RxConfig) |
- AcceptBroadcast | AcceptMulticast | AcceptMyPhys);
+ if (rtl_wol_pll_power_down(tp))
return;
- }
r8168_phy_power_down(tp);
@@ -3171,8 +3670,8 @@ static void r8168_pll_power_up(struct rtl8169_private *tp)
r8168_phy_power_up(tp);
}
-static void rtl_pll_power_op(struct rtl8169_private *tp,
- void (*op)(struct rtl8169_private *))
+static void rtl_generic_op(struct rtl8169_private *tp,
+ void (*op)(struct rtl8169_private *))
{
if (op)
op(tp);
@@ -3180,12 +3679,12 @@ static void rtl_pll_power_op(struct rtl8169_private *tp,
static void rtl_pll_power_down(struct rtl8169_private *tp)
{
- rtl_pll_power_op(tp, tp->pll_power_ops.down);
+ rtl_generic_op(tp, tp->pll_power_ops.down);
}
static void rtl_pll_power_up(struct rtl8169_private *tp)
{
- rtl_pll_power_op(tp, tp->pll_power_ops.up);
+ rtl_generic_op(tp, tp->pll_power_ops.up);
}
static void __devinit rtl_init_pll_power_ops(struct rtl8169_private *tp)
@@ -3221,6 +3720,9 @@ static void __devinit rtl_init_pll_power_ops(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_31:
case RTL_GIGA_MAC_VER_32:
case RTL_GIGA_MAC_VER_33:
+ case RTL_GIGA_MAC_VER_34:
+ case RTL_GIGA_MAC_VER_35:
+ case RTL_GIGA_MAC_VER_36:
ops->down = r8168_pll_power_down;
ops->up = r8168_pll_power_up;
break;
@@ -3232,6 +3734,191 @@ static void __devinit rtl_init_pll_power_ops(struct rtl8169_private *tp)
}
}
+static void rtl_init_rxcfg(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_01:
+ case RTL_GIGA_MAC_VER_02:
+ case RTL_GIGA_MAC_VER_03:
+ case RTL_GIGA_MAC_VER_04:
+ case RTL_GIGA_MAC_VER_05:
+ case RTL_GIGA_MAC_VER_06:
+ case RTL_GIGA_MAC_VER_10:
+ case RTL_GIGA_MAC_VER_11:
+ case RTL_GIGA_MAC_VER_12:
+ case RTL_GIGA_MAC_VER_13:
+ case RTL_GIGA_MAC_VER_14:
+ case RTL_GIGA_MAC_VER_15:
+ case RTL_GIGA_MAC_VER_16:
+ case RTL_GIGA_MAC_VER_17:
+ RTL_W32(RxConfig, RX_FIFO_THRESH | RX_DMA_BURST);
+ break;
+ case RTL_GIGA_MAC_VER_18:
+ case RTL_GIGA_MAC_VER_19:
+ case RTL_GIGA_MAC_VER_20:
+ case RTL_GIGA_MAC_VER_21:
+ case RTL_GIGA_MAC_VER_22:
+ case RTL_GIGA_MAC_VER_23:
+ case RTL_GIGA_MAC_VER_24:
+ RTL_W32(RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST);
+ break;
+ default:
+ RTL_W32(RxConfig, RX128_INT_EN | RX_DMA_BURST);
+ break;
+ }
+}
+
+static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
+{
+ tp->dirty_tx = tp->dirty_rx = tp->cur_tx = tp->cur_rx = 0;
+}
+
+static void rtl_hw_jumbo_enable(struct rtl8169_private *tp)
+{
+ rtl_generic_op(tp, tp->jumbo_ops.enable);
+}
+
+static void rtl_hw_jumbo_disable(struct rtl8169_private *tp)
+{
+ rtl_generic_op(tp, tp->jumbo_ops.disable);
+}
+
+static void r8168c_hw_jumbo_enable(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ RTL_W8(Config3, RTL_R8(Config3) | Jumbo_En0);
+ RTL_W8(Config4, RTL_R8(Config4) | Jumbo_En1);
+ rtl_tx_performance_tweak(tp->pci_dev, 0x2 << MAX_READ_REQUEST_SHIFT);
+}
+
+static void r8168c_hw_jumbo_disable(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ RTL_W8(Config3, RTL_R8(Config3) & ~Jumbo_En0);
+ RTL_W8(Config4, RTL_R8(Config4) & ~Jumbo_En1);
+ rtl_tx_performance_tweak(tp->pci_dev, 0x5 << MAX_READ_REQUEST_SHIFT);
+}
+
+static void r8168dp_hw_jumbo_enable(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ RTL_W8(Config3, RTL_R8(Config3) | Jumbo_En0);
+}
+
+static void r8168dp_hw_jumbo_disable(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ RTL_W8(Config3, RTL_R8(Config3) & ~Jumbo_En0);
+}
+
+static void r8168e_hw_jumbo_enable(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ struct pci_dev *pdev = tp->pci_dev;
+
+ RTL_W8(MaxTxPacketSize, 0x3f);
+ RTL_W8(Config3, RTL_R8(Config3) | Jumbo_En0);
+ RTL_W8(Config4, RTL_R8(Config4) | 0x01);
+ pci_write_config_byte(pdev, 0x79, 0x20);
+}
+
+static void r8168e_hw_jumbo_disable(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ struct pci_dev *pdev = tp->pci_dev;
+
+ RTL_W8(MaxTxPacketSize, 0x0c);
+ RTL_W8(Config3, RTL_R8(Config3) & ~Jumbo_En0);
+ RTL_W8(Config4, RTL_R8(Config4) & ~0x01);
+ pci_write_config_byte(pdev, 0x79, 0x50);
+}
+
+static void r8168b_0_hw_jumbo_enable(struct rtl8169_private *tp)
+{
+ rtl_tx_performance_tweak(tp->pci_dev,
+ (0x2 << MAX_READ_REQUEST_SHIFT) | PCI_EXP_DEVCTL_NOSNOOP_EN);
+}
+
+static void r8168b_0_hw_jumbo_disable(struct rtl8169_private *tp)
+{
+ rtl_tx_performance_tweak(tp->pci_dev,
+ (0x5 << MAX_READ_REQUEST_SHIFT) | PCI_EXP_DEVCTL_NOSNOOP_EN);
+}
+
+static void r8168b_1_hw_jumbo_enable(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ r8168b_0_hw_jumbo_enable(tp);
+
+ RTL_W8(Config4, RTL_R8(Config4) | (1 << 0));
+}
+
+static void r8168b_1_hw_jumbo_disable(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ r8168b_0_hw_jumbo_disable(tp);
+
+ RTL_W8(Config4, RTL_R8(Config4) & ~(1 << 0));
+}
+
+static void __devinit rtl_init_jumbo_ops(struct rtl8169_private *tp)
+{
+ struct jumbo_ops *ops = &tp->jumbo_ops;
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_11:
+ ops->disable = r8168b_0_hw_jumbo_disable;
+ ops->enable = r8168b_0_hw_jumbo_enable;
+ break;
+ case RTL_GIGA_MAC_VER_12:
+ case RTL_GIGA_MAC_VER_17:
+ ops->disable = r8168b_1_hw_jumbo_disable;
+ ops->enable = r8168b_1_hw_jumbo_enable;
+ break;
+ case RTL_GIGA_MAC_VER_18: /* Wild guess. Needs info from Realtek. */
+ case RTL_GIGA_MAC_VER_19:
+ case RTL_GIGA_MAC_VER_20:
+ case RTL_GIGA_MAC_VER_21: /* Wild guess. Needs info from Realtek. */
+ case RTL_GIGA_MAC_VER_22:
+ case RTL_GIGA_MAC_VER_23:
+ case RTL_GIGA_MAC_VER_24:
+ case RTL_GIGA_MAC_VER_25:
+ case RTL_GIGA_MAC_VER_26:
+ ops->disable = r8168c_hw_jumbo_disable;
+ ops->enable = r8168c_hw_jumbo_enable;
+ break;
+ case RTL_GIGA_MAC_VER_27:
+ case RTL_GIGA_MAC_VER_28:
+ ops->disable = r8168dp_hw_jumbo_disable;
+ ops->enable = r8168dp_hw_jumbo_enable;
+ break;
+ case RTL_GIGA_MAC_VER_31: /* Wild guess. Needs info from Realtek. */
+ case RTL_GIGA_MAC_VER_32:
+ case RTL_GIGA_MAC_VER_33:
+ case RTL_GIGA_MAC_VER_34:
+ ops->disable = r8168e_hw_jumbo_disable;
+ ops->enable = r8168e_hw_jumbo_enable;
+ break;
+
+ /*
+ * No action needed for jumbo frames with 8169.
+ * No jumbo for 810x at all.
+ */
+ default:
+ ops->disable = NULL;
+ ops->enable = NULL;
+ break;
+ }
+}
+
static void rtl_hw_reset(struct rtl8169_private *tp)
{
void __iomem *ioaddr = tp->mmio_addr;
@@ -3244,8 +3931,10 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
for (i = 0; i < 100; i++) {
if ((RTL_R8(ChipCmd) & CmdReset) == 0)
break;
- msleep_interruptible(1);
+ udelay(100);
}
+
+ rtl8169_init_ring_indexes(tp);
}
static int __devinit
@@ -3349,9 +4038,13 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
tp->mmio_addr = ioaddr;
- tp->pcie_cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
- if (!tp->pcie_cap)
- netif_info(tp, probe, dev, "no PCI Express capability\n");
+ if (!pci_is_pcie(pdev))
+ netif_info(tp, probe, dev, "not PCI Express\n");
+
+ /* Identify chip attached to board */
+ rtl8169_get_mac_version(tp, dev, cfg->default_ver);
+
+ rtl_init_rxcfg(tp);
RTL_W16(IntrMask, 0x0000);
@@ -3361,9 +4054,6 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_master(pdev);
- /* Identify chip attached to board */
- rtl8169_get_mac_version(tp, dev, cfg->default_ver);
-
/*
* Pretend we are using VLANs; This bypasses a nasty bug where
* Interrupts stop flowing on high load on 8110SCd controllers.
@@ -3373,6 +4063,7 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
rtl_init_mdio_ops(tp);
rtl_init_pll_power_ops(tp);
+ rtl_init_jumbo_ops(tp);
rtl8169_print_mac_version(tp);
@@ -3389,8 +4080,7 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->features |= rtl_try_msi(pdev, ioaddr, cfg);
RTL_W8(Cfg9346, Cfg9346_Lock);
- if ((tp->mac_version <= RTL_GIGA_MAC_VER_06) &&
- (RTL_R8(PHYstatus) & TBI_Enable)) {
+ if (rtl_tbi_enabled(tp)) {
tp->set_speed = rtl8169_set_speed_tbi;
tp->get_settings = rtl8169_gset_tbi;
tp->phy_reset_enable = rtl8169_tbi_reset_enable;
@@ -3439,11 +4129,14 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->intr_event = cfg->intr_event;
tp->napi_event = cfg->napi_event;
+ tp->opts1_mask = (tp->mac_version != RTL_GIGA_MAC_VER_01) ?
+ ~(RxBOVF | RxFOVF) : ~0;
+
init_timer(&tp->timer);
tp->timer.data = (unsigned long) dev;
tp->timer.function = rtl8169_phy_timer;
- tp->fw = RTL_FIRMWARE_UNKNOWN;
+ tp->rtl_fw = RTL_FIRMWARE_UNKNOWN;
rc = register_netdev(dev);
if (rc < 0)
@@ -3454,6 +4147,12 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
netif_info(tp, probe, dev, "%s at 0x%lx, %pM, XID %08x IRQ %d\n",
rtl_chip_infos[chipset].name, dev->base_addr, dev->dev_addr,
(u32)(RTL_R32(TxConfig) & 0x9cf0f8ff), dev->irq);
+ if (rtl_chip_infos[chipset].jumbo_max != JUMBO_1K) {
+ netif_info(tp, probe, dev, "jumbo features [frames: %d bytes, "
+ "tx checksumming: %s]\n",
+ rtl_chip_infos[chipset].jumbo_max,
+ rtl_chip_infos[chipset].jumbo_tx_csum ? "ok" : "ko");
+ }
if (tp->mac_version == RTL_GIGA_MAC_VER_27 ||
tp->mac_version == RTL_GIGA_MAC_VER_28 ||
@@ -3512,25 +4211,48 @@ static void __devexit rtl8169_remove_one(struct pci_dev *pdev)
pci_set_drvdata(pdev, NULL);
}
-static void rtl_request_firmware(struct rtl8169_private *tp)
+static void rtl_request_uncached_firmware(struct rtl8169_private *tp)
{
- /* Return early if the firmware is already loaded / cached. */
- if (IS_ERR(tp->fw)) {
- const char *name;
+ struct rtl_fw *rtl_fw;
+ const char *name;
+ int rc = -ENOMEM;
- name = rtl_lookup_firmware_name(tp);
- if (name) {
- int rc;
+ name = rtl_lookup_firmware_name(tp);
+ if (!name)
+ goto out_no_firmware;
- rc = request_firmware(&tp->fw, name, &tp->pci_dev->dev);
- if (rc >= 0)
- return;
+ rtl_fw = kzalloc(sizeof(*rtl_fw), GFP_KERNEL);
+ if (!rtl_fw)
+ goto err_warn;
- netif_warn(tp, ifup, tp->dev, "unable to load "
- "firmware patch %s (%d)\n", name, rc);
- }
- tp->fw = NULL;
- }
+ rc = request_firmware(&rtl_fw->fw, name, &tp->pci_dev->dev);
+ if (rc < 0)
+ goto err_free;
+
+ rc = rtl_check_firmware(tp, rtl_fw);
+ if (rc < 0)
+ goto err_release_firmware;
+
+ tp->rtl_fw = rtl_fw;
+out:
+ return;
+
+err_release_firmware:
+ release_firmware(rtl_fw->fw);
+err_free:
+ kfree(rtl_fw);
+err_warn:
+ netif_warn(tp, ifup, tp->dev, "unable to load firmware patch %s (%d)\n",
+ name, rc);
+out_no_firmware:
+ tp->rtl_fw = NULL;
+ goto out;
+}
+
+static void rtl_request_firmware(struct rtl8169_private *tp)
+{
+ if (IS_ERR(tp->rtl_fw))
+ rtl_request_uncached_firmware(tp);
}
static int rtl8169_open(struct net_device *dev)
@@ -3605,6 +4327,13 @@ err_pm_runtime_put:
goto out;
}
+static void rtl_rx_close(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ RTL_W32(RxConfig, RTL_R32(RxConfig) & ~RX_CONFIG_ACCEPT_MASK);
+}
+
static void rtl8169_hw_reset(struct rtl8169_private *tp)
{
void __iomem *ioaddr = tp->mmio_addr;
@@ -3612,28 +4341,30 @@ static void rtl8169_hw_reset(struct rtl8169_private *tp)
/* Disable interrupts */
rtl8169_irq_mask_and_ack(ioaddr);
+ rtl_rx_close(tp);
+
if (tp->mac_version == RTL_GIGA_MAC_VER_27 ||
tp->mac_version == RTL_GIGA_MAC_VER_28 ||
tp->mac_version == RTL_GIGA_MAC_VER_31) {
while (RTL_R8(TxPoll) & NPQ)
udelay(20);
-
+ } else if (tp->mac_version == RTL_GIGA_MAC_VER_34 ||
+ tp->mac_version == RTL_GIGA_MAC_VER_35 ||
+ tp->mac_version == RTL_GIGA_MAC_VER_36) {
+ RTL_W8(ChipCmd, RTL_R8(ChipCmd) | StopReq);
+ while (!(RTL_R32(TxConfig) & TXCFG_EMPTY))
+ udelay(100);
+ } else {
+ RTL_W8(ChipCmd, RTL_R8(ChipCmd) | StopReq);
+ udelay(100);
}
- /* Reset the chipset */
- RTL_W8(ChipCmd, CmdReset);
-
- /* PCI commit */
- RTL_R8(ChipCmd);
+ rtl_hw_reset(tp);
}
static void rtl_set_rx_tx_config_registers(struct rtl8169_private *tp)
{
void __iomem *ioaddr = tp->mmio_addr;
- u32 cfg = rtl8169_rx_config;
-
- cfg |= (RTL_R32(RxConfig) & RTL_RX_CONFIG_MASK);
- RTL_W32(RxConfig, cfg);
/* Set DMA burst size and Interframe Gap Time */
RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) |
@@ -3644,8 +4375,6 @@ static void rtl_hw_start(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- rtl_hw_reset(tp);
-
tp->hw_start(dev);
netif_start_queue(dev);
@@ -3723,6 +4452,8 @@ static void rtl_hw_start_8169(struct net_device *dev)
tp->mac_version == RTL_GIGA_MAC_VER_04)
RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+ rtl_init_rxcfg(tp);
+
RTL_W8(EarlyTxThres, NoEarlyTx);
rtl_set_rx_max_size(ioaddr, rx_buf_sz);
@@ -3778,21 +4509,6 @@ static void rtl_hw_start_8169(struct net_device *dev)
RTL_W16(IntrMask, tp->intr_event);
}
-static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct rtl8169_private *tp = netdev_priv(dev);
- int cap = tp->pcie_cap;
-
- if (cap) {
- u16 ctl;
-
- pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl);
- ctl = (ctl & ~PCI_EXP_DEVCTL_READRQ) | force;
- pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl);
- }
-}
-
static void rtl_csi_access_enable(void __iomem *ioaddr, u32 bits)
{
u32 csi;
@@ -3830,9 +4546,7 @@ static void rtl_ephy_init(void __iomem *ioaddr, const struct ephy_info *e, int l
static void rtl_disable_clock_request(struct pci_dev *pdev)
{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct rtl8169_private *tp = netdev_priv(dev);
- int cap = tp->pcie_cap;
+ int cap = pci_pcie_cap(pdev);
if (cap) {
u16 ctl;
@@ -3845,9 +4559,7 @@ static void rtl_disable_clock_request(struct pci_dev *pdev)
static void rtl_enable_clock_request(struct pci_dev *pdev)
{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct rtl8169_private *tp = netdev_priv(dev);
- int cap = tp->pcie_cap;
+ int cap = pci_pcie_cap(pdev);
if (cap) {
u16 ctl;
@@ -4038,9 +4750,9 @@ static void rtl_hw_start_8168d_4(void __iomem *ioaddr, struct pci_dev *pdev)
rtl_enable_clock_request(pdev);
}
-static void rtl_hw_start_8168e(void __iomem *ioaddr, struct pci_dev *pdev)
+static void rtl_hw_start_8168e_1(void __iomem *ioaddr, struct pci_dev *pdev)
{
- static const struct ephy_info e_info_8168e[] = {
+ static const struct ephy_info e_info_8168e_1[] = {
{ 0x00, 0x0200, 0x0100 },
{ 0x00, 0x0000, 0x0004 },
{ 0x06, 0x0002, 0x0001 },
@@ -4058,7 +4770,7 @@ static void rtl_hw_start_8168e(void __iomem *ioaddr, struct pci_dev *pdev)
rtl_csi_access_enable_2(ioaddr);
- rtl_ephy_init(ioaddr, e_info_8168e, ARRAY_SIZE(e_info_8168e));
+ rtl_ephy_init(ioaddr, e_info_8168e_1, ARRAY_SIZE(e_info_8168e_1));
rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
@@ -4073,6 +4785,87 @@ static void rtl_hw_start_8168e(void __iomem *ioaddr, struct pci_dev *pdev)
RTL_W8(Config5, RTL_R8(Config5) & ~Spi_en);
}
+static void rtl_hw_start_8168e_2(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ static const struct ephy_info e_info_8168e_2[] = {
+ { 0x09, 0x0000, 0x0080 },
+ { 0x19, 0x0000, 0x0224 }
+ };
+
+ rtl_csi_access_enable_1(ioaddr);
+
+ rtl_ephy_init(ioaddr, e_info_8168e_2, ARRAY_SIZE(e_info_8168e_2));
+
+ rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+
+ rtl_eri_write(ioaddr, 0xc0, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0xb8, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0xc8, ERIAR_MASK_1111, 0x00100002, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0xe8, ERIAR_MASK_1111, 0x00100006, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0xcc, ERIAR_MASK_1111, 0x00000050, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0xd0, ERIAR_MASK_1111, 0x07ff0060, ERIAR_EXGMAC);
+ rtl_w1w0_eri(ioaddr, 0x1b0, ERIAR_MASK_0001, 0x10, 0x00, ERIAR_EXGMAC);
+ rtl_w1w0_eri(ioaddr, 0x0d4, ERIAR_MASK_0011, 0x0c00, 0xff00,
+ ERIAR_EXGMAC);
+
+ RTL_W8(MaxTxPacketSize, EarlySize);
+
+ rtl_disable_clock_request(pdev);
+
+ RTL_W32(TxConfig, RTL_R32(TxConfig) | TXCFG_AUTO_FIFO);
+ RTL_W8(MCU, RTL_R8(MCU) & ~NOW_IS_OOB);
+
+ /* Adjust EEE LED frequency */
+ RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
+
+ RTL_W8(DLLPR, RTL_R8(DLLPR) | PFM_EN);
+ RTL_W32(MISC, RTL_R32(MISC) | PWM_EN);
+ RTL_W8(Config5, RTL_R8(Config5) & ~Spi_en);
+}
+
+static void rtl_hw_start_8168f_1(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ static const struct ephy_info e_info_8168f_1[] = {
+ { 0x06, 0x00c0, 0x0020 },
+ { 0x08, 0x0001, 0x0002 },
+ { 0x09, 0x0000, 0x0080 },
+ { 0x19, 0x0000, 0x0224 }
+ };
+
+ rtl_csi_access_enable_1(ioaddr);
+
+ rtl_ephy_init(ioaddr, e_info_8168f_1, ARRAY_SIZE(e_info_8168f_1));
+
+ rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+
+ rtl_eri_write(ioaddr, 0xc0, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0xb8, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0xc8, ERIAR_MASK_1111, 0x00100002, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0xe8, ERIAR_MASK_1111, 0x00100006, ERIAR_EXGMAC);
+ rtl_w1w0_eri(ioaddr, 0xdc, ERIAR_MASK_0001, 0x00, 0x01, ERIAR_EXGMAC);
+ rtl_w1w0_eri(ioaddr, 0xdc, ERIAR_MASK_0001, 0x01, 0x00, ERIAR_EXGMAC);
+ rtl_w1w0_eri(ioaddr, 0x1b0, ERIAR_MASK_0001, 0x10, 0x00, ERIAR_EXGMAC);
+ rtl_w1w0_eri(ioaddr, 0x1d0, ERIAR_MASK_0001, 0x10, 0x00, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0xcc, ERIAR_MASK_1111, 0x00000050, ERIAR_EXGMAC);
+ rtl_eri_write(ioaddr, 0xd0, ERIAR_MASK_1111, 0x00000060, ERIAR_EXGMAC);
+ rtl_w1w0_eri(ioaddr, 0x0d4, ERIAR_MASK_0011, 0x0c00, 0xff00,
+ ERIAR_EXGMAC);
+
+ RTL_W8(MaxTxPacketSize, EarlySize);
+
+ rtl_disable_clock_request(pdev);
+
+ RTL_W32(TxConfig, RTL_R32(TxConfig) | TXCFG_AUTO_FIFO);
+ RTL_W8(MCU, RTL_R8(MCU) & ~NOW_IS_OOB);
+
+ /* Adjust EEE LED frequency */
+ RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
+
+ RTL_W8(DLLPR, RTL_R8(DLLPR) | PFM_EN);
+ RTL_W32(MISC, RTL_R32(MISC) | PWM_EN);
+ RTL_W8(Config5, RTL_R8(Config5) & ~Spi_en);
+}
+
static void rtl_hw_start_8168(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
@@ -4161,7 +4954,15 @@ static void rtl_hw_start_8168(struct net_device *dev)
case RTL_GIGA_MAC_VER_32:
case RTL_GIGA_MAC_VER_33:
- rtl_hw_start_8168e(ioaddr, pdev);
+ rtl_hw_start_8168e_1(ioaddr, pdev);
+ break;
+ case RTL_GIGA_MAC_VER_34:
+ rtl_hw_start_8168e_2(ioaddr, pdev);
+ break;
+
+ case RTL_GIGA_MAC_VER_35:
+ case RTL_GIGA_MAC_VER_36:
+ rtl_hw_start_8168f_1(ioaddr, pdev);
break;
default:
@@ -4258,7 +5059,7 @@ static void rtl_hw_start_8105e_1(void __iomem *ioaddr, struct pci_dev *pdev)
RTL_W32(FuncEvent, RTL_R32(FuncEvent) & ~0x010000);
RTL_W8(MCU, RTL_R8(MCU) | EN_NDP | EN_OOB_RESET);
- RTL_W8(DLLPR, RTL_R8(DLLPR) | PM_SWITCH);
+ RTL_W8(DLLPR, RTL_R8(DLLPR) | PFM_EN);
rtl_ephy_init(ioaddr, e_info_8105e_1, ARRAY_SIZE(e_info_8105e_1));
}
@@ -4277,7 +5078,7 @@ static void rtl_hw_start_8101(struct net_device *dev)
if (tp->mac_version == RTL_GIGA_MAC_VER_13 ||
tp->mac_version == RTL_GIGA_MAC_VER_16) {
- int cap = tp->pcie_cap;
+ int cap = pci_pcie_cap(pdev);
if (cap) {
pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL,
@@ -4335,9 +5136,17 @@ static void rtl_hw_start_8101(struct net_device *dev)
static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
{
- if (new_mtu < ETH_ZLEN || new_mtu > SafeMtu)
+ struct rtl8169_private *tp = netdev_priv(dev);
+
+ if (new_mtu < ETH_ZLEN ||
+ new_mtu > rtl_chip_infos[tp->mac_version].jumbo_max)
return -EINVAL;
+ if (new_mtu > ETH_DATA_LEN)
+ rtl_hw_jumbo_enable(tp);
+ else
+ rtl_hw_jumbo_disable(tp);
+
dev->mtu = new_mtu;
netdev_update_features(dev);
@@ -4460,11 +5269,6 @@ err_out:
return -ENOMEM;
}
-static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
-{
- tp->dirty_tx = tp->dirty_rx = tp->cur_tx = tp->cur_rx = 0;
-}
-
static int rtl8169_init_ring(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
@@ -4592,7 +5396,7 @@ static void rtl8169_reset_task(struct work_struct *work)
rtl8169_tx_clear(tp);
- rtl8169_init_ring_indexes(tp);
+ rtl8169_hw_reset(tp);
rtl_hw_start(dev);
netif_wake_queue(dev);
rtl8169_check_link_status(dev, tp, tp->mmio_addr);
@@ -4621,7 +5425,7 @@ static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
entry = tp->cur_tx;
for (cur_frag = 0; cur_frag < info->nr_frags; cur_frag++) {
- skb_frag_t *frag = info->frags + cur_frag;
+ const skb_frag_t *frag = info->frags + cur_frag;
dma_addr_t mapping;
u32 status, len;
void *addr;
@@ -4629,8 +5433,8 @@ static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
entry = (entry + 1) % NUM_TX_DESC;
txd = tp->TxDescArray + entry;
- len = frag->size;
- addr = ((void *) page_address(frag->page)) + frag->page_offset;
+ len = skb_frag_size(frag);
+ addr = skb_frag_address(frag);
mapping = dma_map_single(d, addr, len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(d, mapping))) {
if (net_ratelimit())
@@ -4917,7 +5721,7 @@ static int rtl8169_rx_interrupt(struct net_device *dev,
u32 status;
rmb();
- status = le32_to_cpu(desc->opts1);
+ status = le32_to_cpu(desc->opts1) & tp->opts1_mask;
if (status & DescOwn)
break;
@@ -4937,7 +5741,7 @@ static int rtl8169_rx_interrupt(struct net_device *dev,
} else {
struct sk_buff *skb;
dma_addr_t addr = le64_to_cpu(desc->addr);
- int pkt_size = (status & 0x00001FFF) - 4;
+ int pkt_size = (status & 0x00003fff) - 4;
/*
* The driver does not support incoming fragmented
@@ -5006,7 +5810,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
* the chip, so just exit the loop.
*/
if (unlikely(!netif_running(dev))) {
- rtl8169_asic_down(ioaddr);
+ rtl8169_hw_reset(tp);
break;
}
@@ -5129,7 +5933,7 @@ static void rtl8169_down(struct net_device *dev)
spin_lock_irq(&tp->lock);
- rtl8169_asic_down(ioaddr);
+ rtl8169_hw_reset(tp);
/*
* At this point device interrupts can not be enabled in any function,
* as netif_running is not true (rtl8169_interrupt, rtl8169_reset_task,
@@ -5212,8 +6016,7 @@ static void rtl_set_rx_mode(struct net_device *dev)
spin_lock_irqsave(&tp->lock, flags);
- tmp = rtl8169_rx_config | rx_mode |
- (RTL_R32(RxConfig) & RTL_RX_CONFIG_MASK);
+ tmp = (RTL_R32(RxConfig) & ~RX_CONFIG_ACCEPT_MASK) | rx_mode;
if (tp->mac_version > RTL_GIGA_MAC_VER_06) {
u32 data = mc_filter[0];
@@ -5370,11 +6173,30 @@ static const struct dev_pm_ops rtl8169_pm_ops = {
#endif /* !CONFIG_PM */
+static void rtl_wol_shutdown_quirk(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ /* WoL fails with 8168b when the receiver is disabled. */
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_11:
+ case RTL_GIGA_MAC_VER_12:
+ case RTL_GIGA_MAC_VER_17:
+ pci_clear_master(tp->pci_dev);
+
+ RTL_W8(ChipCmd, CmdRxEnb);
+ /* PCI commit */
+ RTL_R8(ChipCmd);
+ break;
+ default:
+ break;
+ }
+}
+
static void rtl_shutdown(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
rtl8169_net_suspend(dev);
@@ -5383,18 +6205,14 @@ static void rtl_shutdown(struct pci_dev *pdev)
spin_lock_irq(&tp->lock);
- rtl8169_asic_down(ioaddr);
+ rtl8169_hw_reset(tp);
spin_unlock_irq(&tp->lock);
if (system_state == SYSTEM_POWER_OFF) {
- /* WoL fails with some 8168 when the receiver is disabled. */
- if (tp->features & RTL_FEATURE_WOL) {
- pci_clear_master(pdev);
-
- RTL_W8(ChipCmd, CmdRxEnb);
- /* PCI commit */
- RTL_R8(ChipCmd);
+ if (__rtl8169_get_wol(tp) & WAKE_ANY) {
+ rtl_wol_suspend_quirk(tp);
+ rtl_wol_shutdown_quirk(tp);
}
pci_wake_from_d3(pdev, true);
diff --git a/drivers/net/ethernet/renesas/Kconfig b/drivers/net/ethernet/renesas/Kconfig
new file mode 100644
index 000000000000..9755b49bbefb
--- /dev/null
+++ b/drivers/net/ethernet/renesas/Kconfig
@@ -0,0 +1,19 @@
+#
+# Renesas device configuration
+#
+
+config SH_ETH
+ tristate "Renesas SuperH Ethernet support"
+ depends on SUPERH && \
+ (CPU_SUBTYPE_SH7710 || CPU_SUBTYPE_SH7712 || \
+ CPU_SUBTYPE_SH7763 || CPU_SUBTYPE_SH7619 || \
+ CPU_SUBTYPE_SH7724 || CPU_SUBTYPE_SH7757)
+ select CRC32
+ select NET_CORE
+ select MII
+ select MDIO_BITBANG
+ select PHYLIB
+ ---help---
+ Renesas SuperH Ethernet device driver.
+ This driver supporting CPUs are:
+ - SH7710, SH7712, SH7763, SH7619, SH7724, and SH7757.
diff --git a/drivers/net/ethernet/renesas/Makefile b/drivers/net/ethernet/renesas/Makefile
new file mode 100644
index 000000000000..1c278a8e066a
--- /dev/null
+++ b/drivers/net/ethernet/renesas/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Renesas device drivers.
+#
+
+obj-$(CONFIG_SH_ETH) += sh_eth.o
diff --git a/drivers/net/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 1f3f7b4dd638..9b230740c6ab 100644
--- a/drivers/net/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -21,6 +21,10 @@
*/
#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/delay.h>
@@ -30,10 +34,11 @@
#include <linux/phy.h>
#include <linux/cache.h>
#include <linux/io.h>
+#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/ethtool.h>
-#include <asm/cacheflush.h>
+#include <linux/sh_eth.h>
#include "sh_eth.h"
@@ -154,18 +159,18 @@ static void sh_eth_chip_reset_giga(struct net_device *ndev)
/* save MAHR and MALR */
for (i = 0; i < 2; i++) {
- malr[i] = readl(GIGA_MALR(i));
- mahr[i] = readl(GIGA_MAHR(i));
+ malr[i] = ioread32((void *)GIGA_MALR(i));
+ mahr[i] = ioread32((void *)GIGA_MAHR(i));
}
/* reset device */
- writel(ARSTR_ARSTR, SH_GIGA_ETH_BASE + 0x1800);
+ iowrite32(ARSTR_ARSTR, (void *)(SH_GIGA_ETH_BASE + 0x1800));
mdelay(1);
/* restore MAHR and MALR */
for (i = 0; i < 2; i++) {
- writel(malr[i], GIGA_MALR(i));
- writel(mahr[i], GIGA_MAHR(i));
+ iowrite32(malr[i], (void *)GIGA_MALR(i));
+ iowrite32(mahr[i], (void *)GIGA_MAHR(i));
}
}
@@ -514,9 +519,9 @@ static unsigned long sh_eth_get_edtrr_trns(struct sh_eth_private *mdp)
}
struct bb_info {
- void (*set_gate)(unsigned long addr);
+ void (*set_gate)(void *addr);
struct mdiobb_ctrl ctrl;
- u32 addr;
+ void *addr;
u32 mmd_msk;/* MMD */
u32 mdo_msk;
u32 mdi_msk;
@@ -524,21 +529,21 @@ struct bb_info {
};
/* PHY bit set */
-static void bb_set(u32 addr, u32 msk)
+static void bb_set(void *addr, u32 msk)
{
- writel(readl(addr) | msk, addr);
+ iowrite32(ioread32(addr) | msk, addr);
}
/* PHY bit clear */
-static void bb_clr(u32 addr, u32 msk)
+static void bb_clr(void *addr, u32 msk)
{
- writel((readl(addr) & ~msk), addr);
+ iowrite32((ioread32(addr) & ~msk), addr);
}
/* PHY bit read */
-static int bb_read(u32 addr, u32 msk)
+static int bb_read(void *addr, u32 msk)
{
- return (readl(addr) & msk) != 0;
+ return (ioread32(addr) & msk) != 0;
}
/* Data I/O pin control */
@@ -866,6 +871,8 @@ static int sh_eth_txfree(struct net_device *ndev)
break;
/* Free the original skb. */
if (mdp->tx_skbuff[entry]) {
+ dma_unmap_single(&ndev->dev, txdesc->addr,
+ txdesc->buffer_length, DMA_TO_DEVICE);
dev_kfree_skb_irq(mdp->tx_skbuff[entry]);
mdp->tx_skbuff[entry] = NULL;
freeNum++;
@@ -1489,13 +1496,12 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
entry = mdp->cur_tx % TX_RING_SIZE;
mdp->tx_skbuff[entry] = skb;
txdesc = &mdp->tx_ring[entry];
- txdesc->addr = virt_to_phys(skb->data);
/* soft swap. */
if (!mdp->cd->hw_swap)
sh_eth_soft_swap(phys_to_virt(ALIGN(txdesc->addr, 4)),
skb->len + 2);
- /* write back */
- __flush_purge_region(skb->data, skb->len);
+ txdesc->addr = dma_map_single(&ndev->dev, skb->data, skb->len,
+ DMA_TO_DEVICE);
if (skb->len < ETHERSMALL)
txdesc->buffer_length = ETHERSMALL;
else
@@ -1678,7 +1684,7 @@ static int sh_mdio_init(struct net_device *ndev, int id,
}
/* bitbang init */
- bitbang->addr = ndev->base_addr + mdp->reg_offset[PIR];
+ bitbang->addr = mdp->addr + mdp->reg_offset[PIR];
bitbang->set_gate = pd->set_mdio_gate;
bitbang->mdi_msk = 0x08;
bitbang->mdo_msk = 0x04;
@@ -1758,7 +1764,7 @@ static const struct net_device_ops sh_eth_netdev_ops = {
.ndo_start_xmit = sh_eth_start_xmit,
.ndo_get_stats = sh_eth_get_stats,
#if defined(SH_ETH_HAS_TSU)
- .ndo_set_multicast_list = sh_eth_set_multicast_list,
+ .ndo_set_rx_mode = sh_eth_set_multicast_list,
#endif
.ndo_tx_timeout = sh_eth_tx_timeout,
.ndo_do_ioctl = sh_eth_do_ioctl,
@@ -1772,7 +1778,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
int ret, devno = 0;
struct resource *res;
struct net_device *ndev = NULL;
- struct sh_eth_private *mdp;
+ struct sh_eth_private *mdp = NULL;
struct sh_eth_plat_data *pd;
/* get base addr */
@@ -1810,6 +1816,13 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
ether_setup(ndev);
mdp = netdev_priv(ndev);
+ mdp->addr = ioremap(res->start, resource_size(res));
+ if (mdp->addr == NULL) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "ioremap failed.\n");
+ goto out_release;
+ }
+
spin_lock_init(&mdp->lock);
mdp->pdev = pdev;
pm_runtime_enable(&pdev->dev);
@@ -1890,7 +1903,9 @@ out_unregister:
out_release:
/* net_dev free */
- if (mdp->tsu_addr)
+ if (mdp && mdp->addr)
+ iounmap(mdp->addr);
+ if (mdp && mdp->tsu_addr)
iounmap(mdp->tsu_addr);
if (ndev)
free_netdev(ndev);
@@ -1908,6 +1923,7 @@ static int sh_eth_drv_remove(struct platform_device *pdev)
sh_mdio_release(ndev);
unregister_netdev(ndev);
pm_runtime_disable(&pdev->dev);
+ iounmap(mdp->addr);
free_netdev(ndev);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/net/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index c3048a6ba676..47877b13ffad 100644
--- a/drivers/net/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -23,14 +23,6 @@
#ifndef __SH_ETH_H__
#define __SH_ETH_H__
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/netdevice.h>
-#include <linux/phy.h>
-
-#include <asm/sh_eth.h>
-
#define CARDNAME "sh-eth"
#define TX_TIMEOUT (5*HZ)
#define TX_RING_SIZE 64 /* Tx ring size */
@@ -762,6 +754,7 @@ struct sh_eth_private {
struct platform_device *pdev;
struct sh_eth_cpu_data *cd;
const u16 *reg_offset;
+ void __iomem *addr;
void __iomem *tsu_addr;
dma_addr_t rx_desc_dma;
dma_addr_t tx_desc_dma;
@@ -811,7 +804,7 @@ static inline void sh_eth_write(struct net_device *ndev, unsigned long data,
{
struct sh_eth_private *mdp = netdev_priv(ndev);
- writel(data, ndev->base_addr + mdp->reg_offset[enum_index]);
+ iowrite32(data, mdp->addr + mdp->reg_offset[enum_index]);
}
static inline unsigned long sh_eth_read(struct net_device *ndev,
@@ -819,19 +812,19 @@ static inline unsigned long sh_eth_read(struct net_device *ndev,
{
struct sh_eth_private *mdp = netdev_priv(ndev);
- return readl(ndev->base_addr + mdp->reg_offset[enum_index]);
+ return ioread32(mdp->addr + mdp->reg_offset[enum_index]);
}
static inline void sh_eth_tsu_write(struct sh_eth_private *mdp,
unsigned long data, int enum_index)
{
- writel(data, mdp->tsu_addr + mdp->reg_offset[enum_index]);
+ iowrite32(data, mdp->tsu_addr + mdp->reg_offset[enum_index]);
}
static inline unsigned long sh_eth_tsu_read(struct sh_eth_private *mdp,
int enum_index)
{
- return readl(mdp->tsu_addr + mdp->reg_offset[enum_index]);
+ return ioread32(mdp->tsu_addr + mdp->reg_offset[enum_index]);
}
#endif /* #ifndef __SH_ETH_H__ */
diff --git a/drivers/net/s6gmac.c b/drivers/net/ethernet/s6gmac.c
index a7ff8ea342b4..a7ff8ea342b4 100644
--- a/drivers/net/s6gmac.c
+++ b/drivers/net/ethernet/s6gmac.c
diff --git a/drivers/net/ethernet/seeq/Kconfig b/drivers/net/ethernet/seeq/Kconfig
new file mode 100644
index 000000000000..29f18533fdc7
--- /dev/null
+++ b/drivers/net/ethernet/seeq/Kconfig
@@ -0,0 +1,47 @@
+#
+# SEEQ device configuration
+#
+
+config NET_VENDOR_SEEQ
+ bool "SEEQ devices"
+ default y
+ depends on HAS_IOMEM
+ depends on (ARM && ARCH_ACORN) || SGI_HAS_SEEQ || EXPERIMENTAL
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about SEEQ devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_SEEQ
+
+config ARM_ETHER3
+ tristate "Acorn/ANT Ether3 support"
+ depends on ARM && ARCH_ACORN
+ ---help---
+ If you have an Acorn system with one of these network cards, you
+ should say Y to this option if you wish to use it with Linux.
+
+config SEEQ8005
+ tristate "SEEQ8005 support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ ---help---
+ This is a driver for the SEEQ 8005 network (Ethernet) card. If this
+ is for you, read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called seeq8005.
+
+config SGISEEQ
+ tristate "SGI Seeq ethernet controller support"
+ depends on SGI_HAS_SEEQ
+ ---help---
+ Say Y here if you have an Seeq based Ethernet network card. This is
+ used in many Silicon Graphics machines.
+
+endif # NET_VENDOR_SEEQ
diff --git a/drivers/net/ethernet/seeq/Makefile b/drivers/net/ethernet/seeq/Makefile
new file mode 100644
index 000000000000..3e258a580c05
--- /dev/null
+++ b/drivers/net/ethernet/seeq/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the SEEQ network device drivers
+#
+
+obj-$(CONFIG_ARM_ETHER3) += ether3.o
+obj-$(CONFIG_SEEQ8005) += seeq8005.o
+obj-$(CONFIG_SGISEEQ) += sgiseeq.o
diff --git a/drivers/net/arm/ether3.c b/drivers/net/ethernet/seeq/ether3.c
index 44a8746f4014..893c880dadf0 100644
--- a/drivers/net/arm/ether3.c
+++ b/drivers/net/ethernet/seeq/ether3.c
@@ -761,7 +761,7 @@ static const struct net_device_ops ether3_netdev_ops = {
.ndo_open = ether3_open,
.ndo_stop = ether3_close,
.ndo_start_xmit = ether3_sendpacket,
- .ndo_set_multicast_list = ether3_setmulticastlist,
+ .ndo_set_rx_mode = ether3_setmulticastlist,
.ndo_tx_timeout = ether3_timeout,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/arm/ether3.h b/drivers/net/ethernet/seeq/ether3.h
index 2db63b08bdf3..2db63b08bdf3 100644
--- a/drivers/net/arm/ether3.h
+++ b/drivers/net/ethernet/seeq/ether3.h
diff --git a/drivers/net/seeq8005.c b/drivers/net/ethernet/seeq/seeq8005.c
index d2fce98f557f..60561451789b 100644
--- a/drivers/net/seeq8005.c
+++ b/drivers/net/ethernet/seeq/seeq8005.c
@@ -148,7 +148,7 @@ static const struct net_device_ops seeq8005_netdev_ops = {
.ndo_stop = seeq8005_close,
.ndo_start_xmit = seeq8005_send_packet,
.ndo_tx_timeout = seeq8005_timeout,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/seeq8005.h b/drivers/net/ethernet/seeq/seeq8005.h
index 5dfb0098c6ca..5dfb0098c6ca 100644
--- a/drivers/net/seeq8005.h
+++ b/drivers/net/ethernet/seeq/seeq8005.h
diff --git a/drivers/net/sgiseeq.c b/drivers/net/ethernet/seeq/sgiseeq.c
index 54415c7b84a2..c3673f151a41 100644
--- a/drivers/net/sgiseeq.c
+++ b/drivers/net/ethernet/seeq/sgiseeq.c
@@ -6,6 +6,7 @@
#undef DEBUG
+#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -714,7 +715,7 @@ static const struct net_device_ops sgiseeq_netdev_ops = {
.ndo_stop = sgiseeq_close,
.ndo_start_xmit = sgiseeq_start_xmit,
.ndo_tx_timeout = timeout,
- .ndo_set_multicast_list = sgiseeq_set_multicast,
+ .ndo_set_rx_mode = sgiseeq_set_multicast,
.ndo_set_mac_address = sgiseeq_set_mac_address,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/sgiseeq.h b/drivers/net/ethernet/seeq/sgiseeq.h
index 2211e2987a8d..2211e2987a8d 100644
--- a/drivers/net/sgiseeq.h
+++ b/drivers/net/ethernet/seeq/sgiseeq.h
diff --git a/drivers/net/sfc/Kconfig b/drivers/net/ethernet/sfc/Kconfig
index a65c98638398..5d18841f0f3d 100644
--- a/drivers/net/sfc/Kconfig
+++ b/drivers/net/ethernet/sfc/Kconfig
@@ -1,22 +1,21 @@
config SFC
- tristate "Solarflare Solarstorm SFC4000/SFC9000-family support"
+ tristate "Solarflare SFC4000/SFC9000-family support"
depends on PCI && INET
select MDIO
select CRC32
select I2C
select I2C_ALGOBIT
- help
+ ---help---
This driver supports 10-gigabit Ethernet cards based on
- the Solarflare Communications Solarstorm SFC4000 and
- SFC9000-family controllers.
+ the Solarflare SFC4000 and SFC9000-family controllers.
To compile this driver as a module, choose M here. The module
will be called sfc.
config SFC_MTD
- bool "Solarflare Solarstorm SFC4000/SFC9000-family MTD support"
+ bool "Solarflare SFC4000/SFC9000-family MTD support"
depends on SFC && MTD && !(SFC=y && MTD=m)
default y
- help
+ ---help---
This exposes the on-board flash memory as MTD devices (e.g.
/dev/mtd1). This makes it possible to upload new firmware
to the NIC.
diff --git a/drivers/net/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile
index ab31c7124db1..ab31c7124db1 100644
--- a/drivers/net/sfc/Makefile
+++ b/drivers/net/ethernet/sfc/Makefile
diff --git a/drivers/net/sfc/bitfield.h b/drivers/net/ethernet/sfc/bitfield.h
index 098ac2ad757d..098ac2ad757d 100644
--- a/drivers/net/sfc/bitfield.h
+++ b/drivers/net/ethernet/sfc/bitfield.h
diff --git a/drivers/net/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index c914729f9554..d5731f1fe6d6 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -229,8 +229,7 @@ static int efx_process_channel(struct efx_channel *channel, int budget)
struct efx_nic *efx = channel->efx;
int spent;
- if (unlikely(efx->reset_pending != RESET_TYPE_NONE ||
- !channel->enabled))
+ if (unlikely(efx->reset_pending || !channel->enabled))
return 0;
spent = efx_nic_process_eventq(channel, budget);
@@ -1051,7 +1050,6 @@ static int efx_init_io(struct efx_nic *efx)
{
struct pci_dev *pci_dev = efx->pci_dev;
dma_addr_t dma_mask = efx->type->max_dma_mask;
- bool use_wc;
int rc;
netif_dbg(efx, probe, efx->net_dev, "initialising I/O\n");
@@ -1102,21 +1100,8 @@ static int efx_init_io(struct efx_nic *efx)
rc = -EIO;
goto fail3;
}
-
- /* bug22643: If SR-IOV is enabled then tx push over a write combined
- * mapping is unsafe. We need to disable write combining in this case.
- * MSI is unsupported when SR-IOV is enabled, and the firmware will
- * have removed the MSI capability. So write combining is safe if
- * there is an MSI capability.
- */
- use_wc = (!EFX_WORKAROUND_22643(efx) ||
- pci_find_capability(pci_dev, PCI_CAP_ID_MSI));
- if (use_wc)
- efx->membase = ioremap_wc(efx->membase_phys,
- efx->type->mem_map_size);
- else
- efx->membase = ioremap_nocache(efx->membase_phys,
- efx->type->mem_map_size);
+ efx->membase = ioremap_nocache(efx->membase_phys,
+ efx->type->mem_map_size);
if (!efx->membase) {
netif_err(efx, probe, efx->net_dev,
"could not map memory BAR at %llx+%x\n",
@@ -1358,7 +1343,8 @@ static int efx_probe_nic(struct efx_nic *efx)
netif_set_real_num_rx_queues(efx->net_dev, efx->n_rx_channels);
/* Initialise the interrupt moderation settings */
- efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, true);
+ efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, true,
+ true);
return 0;
@@ -1461,7 +1447,7 @@ static void efx_start_all(struct efx_nic *efx)
* reset_pending [modified from an atomic context], we instead guarantee
* that efx_mcdi_mode_poll() isn't reverted erroneously */
efx_mcdi_mode_event(efx);
- if (efx->reset_pending != RESET_TYPE_NONE)
+ if (efx->reset_pending)
efx_mcdi_mode_poll(efx);
/* Start the hardware monitor if there is one. Otherwise (we're link
@@ -1557,18 +1543,19 @@ static void efx_remove_all(struct efx_nic *efx)
*
**************************************************************************/
-static unsigned irq_mod_ticks(int usecs, int resolution)
+static unsigned int irq_mod_ticks(unsigned int usecs, unsigned int resolution)
{
- if (usecs <= 0)
- return 0; /* cannot receive interrupts ahead of time :-) */
+ if (usecs == 0)
+ return 0;
if (usecs < resolution)
return 1; /* never round down to 0 */
return usecs / resolution;
}
/* Set interrupt moderation parameters */
-void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs,
- bool rx_adaptive)
+int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs,
+ unsigned int rx_usecs, bool rx_adaptive,
+ bool rx_may_override_tx)
{
struct efx_channel *channel;
unsigned tx_ticks = irq_mod_ticks(tx_usecs, EFX_IRQ_MOD_RESOLUTION);
@@ -1576,6 +1563,16 @@ void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs,
EFX_ASSERT_RESET_SERIALISED(efx);
+ if (tx_ticks > EFX_IRQ_MOD_MAX || rx_ticks > EFX_IRQ_MOD_MAX)
+ return -EINVAL;
+
+ if (tx_ticks != rx_ticks && efx->tx_channel_offset == 0 &&
+ !rx_may_override_tx) {
+ netif_err(efx, drv, efx->net_dev, "Channels are shared. "
+ "RX and TX IRQ moderation must be equal\n");
+ return -EINVAL;
+ }
+
efx->irq_rx_adaptive = rx_adaptive;
efx->irq_rx_moderation = rx_ticks;
efx_for_each_channel(channel, efx) {
@@ -1584,6 +1581,26 @@ void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs,
else if (efx_channel_has_tx_queues(channel))
channel->irq_moderation = tx_ticks;
}
+
+ return 0;
+}
+
+void efx_get_irq_moderation(struct efx_nic *efx, unsigned int *tx_usecs,
+ unsigned int *rx_usecs, bool *rx_adaptive)
+{
+ *rx_adaptive = efx->irq_rx_adaptive;
+ *rx_usecs = efx->irq_rx_moderation * EFX_IRQ_MOD_RESOLUTION;
+
+ /* If channels are shared between RX and TX, so is IRQ
+ * moderation. Otherwise, IRQ moderation is the same for all
+ * TX channels and is not adaptive.
+ */
+ if (efx->tx_channel_offset == 0)
+ *tx_usecs = *rx_usecs;
+ else
+ *tx_usecs =
+ efx->channel[efx->tx_channel_offset]->irq_moderation *
+ EFX_IRQ_MOD_RESOLUTION;
}
/**************************************************************************
@@ -1904,7 +1921,7 @@ static const struct net_device_ops efx_netdev_ops = {
.ndo_do_ioctl = efx_ioctl,
.ndo_change_mtu = efx_change_mtu,
.ndo_set_mac_address = efx_set_mac_address,
- .ndo_set_multicast_list = efx_set_multicast_list,
+ .ndo_set_rx_mode = efx_set_multicast_list,
.ndo_set_features = efx_set_features,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = efx_netpoll,
@@ -2118,8 +2135,10 @@ int efx_reset(struct efx_nic *efx, enum reset_type method)
goto out;
}
- /* Allow resets to be rescheduled. */
- efx->reset_pending = RESET_TYPE_NONE;
+ /* Clear flags for the scopes we covered. We assume the NIC and
+ * driver are now quiescent so that there is no race here.
+ */
+ efx->reset_pending &= -(1 << (method + 1));
/* Reinitialise bus-mastering, which may have been turned off before
* the reset was scheduled. This is still appropriate, even in the
@@ -2154,12 +2173,13 @@ out:
static void efx_reset_work(struct work_struct *data)
{
struct efx_nic *efx = container_of(data, struct efx_nic, reset_work);
+ unsigned long pending = ACCESS_ONCE(efx->reset_pending);
- if (efx->reset_pending == RESET_TYPE_NONE)
+ if (!pending)
return;
/* If we're not RUNNING then don't reset. Leave the reset_pending
- * flag set so that efx_pci_probe_main will be retried */
+ * flags set so that efx_pci_probe_main will be retried */
if (efx->state != STATE_RUNNING) {
netif_info(efx, drv, efx->net_dev,
"scheduled reset quenched. NIC not RUNNING\n");
@@ -2167,7 +2187,7 @@ static void efx_reset_work(struct work_struct *data)
}
rtnl_lock();
- (void)efx_reset(efx, efx->reset_pending);
+ (void)efx_reset(efx, fls(pending) - 1);
rtnl_unlock();
}
@@ -2175,40 +2195,24 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
{
enum reset_type method;
- if (efx->reset_pending != RESET_TYPE_NONE) {
- netif_info(efx, drv, efx->net_dev,
- "quenching already scheduled reset\n");
- return;
- }
-
switch (type) {
case RESET_TYPE_INVISIBLE:
case RESET_TYPE_ALL:
case RESET_TYPE_WORLD:
case RESET_TYPE_DISABLE:
method = type;
+ netif_dbg(efx, drv, efx->net_dev, "scheduling %s reset\n",
+ RESET_TYPE(method));
break;
- case RESET_TYPE_RX_RECOVERY:
- case RESET_TYPE_RX_DESC_FETCH:
- case RESET_TYPE_TX_DESC_FETCH:
- case RESET_TYPE_TX_SKIP:
- method = RESET_TYPE_INVISIBLE;
- break;
- case RESET_TYPE_MC_FAILURE:
default:
- method = RESET_TYPE_ALL;
- break;
- }
-
- if (method != type)
+ method = efx->type->map_reset_reason(type);
netif_dbg(efx, drv, efx->net_dev,
"scheduling %s reset for %s\n",
RESET_TYPE(method), RESET_TYPE(type));
- else
- netif_dbg(efx, drv, efx->net_dev, "scheduling %s reset\n",
- RESET_TYPE(method));
+ break;
+ }
- efx->reset_pending = method;
+ set_bit(method, &efx->reset_pending);
/* efx_process_channel() will no longer read events once a
* reset is scheduled. So switch back to poll'd MCDI completions. */
@@ -2225,13 +2229,15 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
/* PCI device ID table */
static DEFINE_PCI_DEVICE_TABLE(efx_pci_table) = {
- {PCI_DEVICE(EFX_VENDID_SFC, FALCON_A_P_DEVID),
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE,
+ PCI_DEVICE_ID_SOLARFLARE_SFC4000A_0),
.driver_data = (unsigned long) &falcon_a1_nic_type},
- {PCI_DEVICE(EFX_VENDID_SFC, FALCON_B_P_DEVID),
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE,
+ PCI_DEVICE_ID_SOLARFLARE_SFC4000B),
.driver_data = (unsigned long) &falcon_b0_nic_type},
- {PCI_DEVICE(EFX_VENDID_SFC, BETHPAGE_A_P_DEVID),
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, BETHPAGE_A_P_DEVID),
.driver_data = (unsigned long) &siena_a0_nic_type},
- {PCI_DEVICE(EFX_VENDID_SFC, SIENA_A_P_DEVID),
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, SIENA_A_P_DEVID),
.driver_data = (unsigned long) &siena_a0_nic_type},
{0} /* end of list */
};
@@ -2288,7 +2294,6 @@ static int efx_init_struct(struct efx_nic *efx, const struct efx_nic_type *type,
efx->pci_dev = pci_dev;
efx->msg_enable = debug;
efx->state = STATE_INIT;
- efx->reset_pending = RESET_TYPE_NONE;
strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name));
efx->net_dev = net_dev;
@@ -2491,7 +2496,7 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
goto fail1;
netif_info(efx, probe, efx->net_dev,
- "Solarflare Communications NIC detected\n");
+ "Solarflare NIC detected\n");
/* Set up basic I/O (BAR mappings etc) */
rc = efx_init_io(efx);
@@ -2510,7 +2515,7 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
cancel_work_sync(&efx->reset_work);
if (rc == 0) {
- if (efx->reset_pending != RESET_TYPE_NONE) {
+ if (efx->reset_pending) {
/* If there was a scheduled reset during
* probe, the NIC is probably hosed anyway */
efx_pci_remove_main(efx);
@@ -2521,11 +2526,12 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
}
/* Retry if a recoverably reset event has been scheduled */
- if ((efx->reset_pending != RESET_TYPE_INVISIBLE) &&
- (efx->reset_pending != RESET_TYPE_ALL))
+ if (efx->reset_pending &
+ ~(1 << RESET_TYPE_INVISIBLE | 1 << RESET_TYPE_ALL) ||
+ !efx->reset_pending)
goto fail3;
- efx->reset_pending = RESET_TYPE_NONE;
+ efx->reset_pending = 0;
}
if (rc) {
@@ -2609,7 +2615,7 @@ static int efx_pm_poweroff(struct device *dev)
efx->type->fini(efx);
- efx->reset_pending = RESET_TYPE_NONE;
+ efx->reset_pending = 0;
pci_save_state(pci_dev);
return pci_set_power_state(pci_dev, PCI_D3hot);
diff --git a/drivers/net/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h
index b0d1209ea18d..4764793ed234 100644
--- a/drivers/net/sfc/efx.h
+++ b/drivers/net/ethernet/sfc/efx.h
@@ -15,10 +15,6 @@
#include "filter.h"
/* PCI IDs */
-#define EFX_VENDID_SFC 0x1924
-#define FALCON_A_P_DEVID 0x0703
-#define FALCON_A_S_DEVID 0x6703
-#define FALCON_B_P_DEVID 0x0710
#define BETHPAGE_A_P_DEVID 0x0803
#define SIENA_A_P_DEVID 0x0813
@@ -111,8 +107,11 @@ extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);
/* Global */
extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
-extern void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs,
- int rx_usecs, bool rx_adaptive);
+extern int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs,
+ unsigned int rx_usecs, bool rx_adaptive,
+ bool rx_may_override_tx);
+extern void efx_get_irq_moderation(struct efx_nic *efx, unsigned int *tx_usecs,
+ unsigned int *rx_usecs, bool *rx_adaptive);
/* Dummy PHY ops for PHY drivers */
extern int efx_port_dummy_op_int(struct efx_nic *efx);
diff --git a/drivers/net/sfc/enum.h b/drivers/net/ethernet/sfc/enum.h
index 384cfe3b1be1..d725a8fbe1a6 100644
--- a/drivers/net/sfc/enum.h
+++ b/drivers/net/ethernet/sfc/enum.h
@@ -134,6 +134,8 @@ enum efx_loopback_mode {
* other valuesspecify reasons, which efx_schedule_reset() will choose
* a method for.
*
+ * Reset methods are numbered in order of increasing scope.
+ *
* @RESET_TYPE_INVISIBLE: don't reset the PHYs or interrupts
* @RESET_TYPE_ALL: reset everything but PCI core blocks
* @RESET_TYPE_WORLD: reset everything, save & restore PCI config
@@ -147,7 +149,6 @@ enum efx_loopback_mode {
* @RESET_TYPE_MC_FAILURE: MC reboot/assertion
*/
enum reset_type {
- RESET_TYPE_NONE = -1,
RESET_TYPE_INVISIBLE = 0,
RESET_TYPE_ALL = 1,
RESET_TYPE_WORLD = 2,
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index d229027dc363..f3cd96dfa398 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -586,72 +586,89 @@ static int efx_ethtool_nway_reset(struct net_device *net_dev)
return mdio45_nway_restart(&efx->mdio);
}
+/*
+ * Each channel has a single IRQ and moderation timer, started by any
+ * completion (or other event). Unless the module parameter
+ * separate_tx_channels is set, IRQs and moderation are therefore
+ * shared between RX and TX completions. In this case, when RX IRQ
+ * moderation is explicitly changed then TX IRQ moderation is
+ * automatically changed too, but otherwise we fail if the two values
+ * are requested to be different.
+ *
+ * The hardware does not support a limit on the number of completions
+ * before an IRQ, so we do not use the max_frames fields. We should
+ * report and require that max_frames == (usecs != 0), but this would
+ * invalidate existing user documentation.
+ *
+ * The hardware does not have distinct settings for interrupt
+ * moderation while the previous IRQ is being handled, so we should
+ * not use the 'irq' fields. However, an earlier developer
+ * misunderstood the meaning of the 'irq' fields and the driver did
+ * not support the standard fields. To avoid invalidating existing
+ * user documentation, we report and accept changes through either the
+ * standard or 'irq' fields. If both are changed at the same time, we
+ * prefer the standard field.
+ *
+ * We implement adaptive IRQ moderation, but use a different algorithm
+ * from that assumed in the definition of struct ethtool_coalesce.
+ * Therefore we do not use any of the adaptive moderation parameters
+ * in it.
+ */
+
static int efx_ethtool_get_coalesce(struct net_device *net_dev,
struct ethtool_coalesce *coalesce)
{
struct efx_nic *efx = netdev_priv(net_dev);
- struct efx_channel *channel;
+ unsigned int tx_usecs, rx_usecs;
+ bool rx_adaptive;
- memset(coalesce, 0, sizeof(*coalesce));
+ efx_get_irq_moderation(efx, &tx_usecs, &rx_usecs, &rx_adaptive);
- /* Find lowest IRQ moderation across all used TX queues */
- coalesce->tx_coalesce_usecs_irq = ~((u32) 0);
- efx_for_each_channel(channel, efx) {
- if (!efx_channel_has_tx_queues(channel))
- continue;
- if (channel->irq_moderation < coalesce->tx_coalesce_usecs_irq) {
- if (channel->channel < efx->n_rx_channels)
- coalesce->tx_coalesce_usecs_irq =
- channel->irq_moderation;
- else
- coalesce->tx_coalesce_usecs_irq = 0;
- }
- }
-
- coalesce->use_adaptive_rx_coalesce = efx->irq_rx_adaptive;
- coalesce->rx_coalesce_usecs_irq = efx->irq_rx_moderation;
-
- coalesce->tx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION;
- coalesce->rx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION;
+ coalesce->tx_coalesce_usecs = tx_usecs;
+ coalesce->tx_coalesce_usecs_irq = tx_usecs;
+ coalesce->rx_coalesce_usecs = rx_usecs;
+ coalesce->rx_coalesce_usecs_irq = rx_usecs;
+ coalesce->use_adaptive_rx_coalesce = rx_adaptive;
return 0;
}
-/* Set coalescing parameters
- * The difficulties occur for shared channels
- */
static int efx_ethtool_set_coalesce(struct net_device *net_dev,
struct ethtool_coalesce *coalesce)
{
struct efx_nic *efx = netdev_priv(net_dev);
struct efx_channel *channel;
- unsigned tx_usecs, rx_usecs, adaptive;
+ unsigned int tx_usecs, rx_usecs;
+ bool adaptive, rx_may_override_tx;
+ int rc;
if (coalesce->use_adaptive_tx_coalesce)
- return -EOPNOTSUPP;
+ return -EINVAL;
- if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) {
- netif_err(efx, drv, efx->net_dev, "invalid coalescing setting. "
- "Only rx/tx_coalesce_usecs_irq are supported\n");
- return -EOPNOTSUPP;
- }
+ efx_get_irq_moderation(efx, &tx_usecs, &rx_usecs, &adaptive);
+
+ if (coalesce->rx_coalesce_usecs != rx_usecs)
+ rx_usecs = coalesce->rx_coalesce_usecs;
+ else
+ rx_usecs = coalesce->rx_coalesce_usecs_irq;
- rx_usecs = coalesce->rx_coalesce_usecs_irq;
- tx_usecs = coalesce->tx_coalesce_usecs_irq;
adaptive = coalesce->use_adaptive_rx_coalesce;
- /* If the channel is shared only allow RX parameters to be set */
- efx_for_each_channel(channel, efx) {
- if (efx_channel_has_rx_queue(channel) &&
- efx_channel_has_tx_queues(channel) &&
- tx_usecs) {
- netif_err(efx, drv, efx->net_dev, "Channel is shared. "
- "Only RX coalescing may be set\n");
- return -EOPNOTSUPP;
- }
- }
+ /* If channels are shared, TX IRQ moderation can be quietly
+ * overridden unless it is changed from its old value.
+ */
+ rx_may_override_tx = (coalesce->tx_coalesce_usecs == tx_usecs &&
+ coalesce->tx_coalesce_usecs_irq == tx_usecs);
+ if (coalesce->tx_coalesce_usecs != tx_usecs)
+ tx_usecs = coalesce->tx_coalesce_usecs;
+ else
+ tx_usecs = coalesce->tx_coalesce_usecs_irq;
+
+ rc = efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive,
+ rx_may_override_tx);
+ if (rc != 0)
+ return rc;
- efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive);
efx_for_each_channel(channel, efx)
efx->type->push_irq_moderation(channel);
@@ -665,12 +682,8 @@ static void efx_ethtool_get_ringparam(struct net_device *net_dev,
ring->rx_max_pending = EFX_MAX_DMAQ_SIZE;
ring->tx_max_pending = EFX_MAX_DMAQ_SIZE;
- ring->rx_mini_max_pending = 0;
- ring->rx_jumbo_max_pending = 0;
ring->rx_pending = efx->rxq_entries;
ring->tx_pending = efx->txq_entries;
- ring->rx_mini_pending = 0;
- ring->rx_jumbo_pending = 0;
}
static int efx_ethtool_set_ringparam(struct net_device *net_dev,
@@ -796,35 +809,18 @@ static int efx_ethtool_set_wol(struct net_device *net_dev,
static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
{
struct efx_nic *efx = netdev_priv(net_dev);
- enum reset_type method;
- enum {
- ETH_RESET_EFX_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER |
- ETH_RESET_OFFLOAD | ETH_RESET_MAC)
- };
-
- /* Check for minimal reset flags */
- if ((*flags & ETH_RESET_EFX_INVISIBLE) != ETH_RESET_EFX_INVISIBLE)
- return -EINVAL;
- *flags ^= ETH_RESET_EFX_INVISIBLE;
- method = RESET_TYPE_INVISIBLE;
-
- if (*flags & ETH_RESET_PHY) {
- *flags ^= ETH_RESET_PHY;
- method = RESET_TYPE_ALL;
- }
+ int rc;
- if ((*flags & efx->type->reset_world_flags) ==
- efx->type->reset_world_flags) {
- *flags ^= efx->type->reset_world_flags;
- method = RESET_TYPE_WORLD;
- }
+ rc = efx->type->map_reset_flags(flags);
+ if (rc < 0)
+ return rc;
- return efx_reset(efx, method);
+ return efx_reset(efx, rc);
}
static int
efx_ethtool_get_rxnfc(struct net_device *net_dev,
- struct ethtool_rxnfc *info, void *rules __always_unused)
+ struct ethtool_rxnfc *info, u32 *rules __always_unused)
{
struct efx_nic *efx = netdev_priv(net_dev);
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/ethernet/sfc/falcon.c
index 60176e873d62..97b606b92e88 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/ethernet/sfc/falcon.c
@@ -104,6 +104,8 @@ static void falcon_push_irq_moderation(struct efx_channel *channel)
efx_dword_t timer_cmd;
struct efx_nic *efx = channel->efx;
+ BUILD_BUG_ON(EFX_IRQ_MOD_MAX > (1 << FRF_AB_TC_TIMER_VAL_WIDTH));
+
/* Set timer register */
if (channel->irq_moderation) {
EFX_POPULATE_DWORD_2(timer_cmd,
@@ -536,7 +538,7 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx)
efx_oword_t reg;
int link_speed, isolate;
- isolate = (efx->reset_pending != RESET_TYPE_NONE);
+ isolate = !!ACCESS_ONCE(efx->reset_pending);
switch (link_state->speed) {
case 10000: link_speed = 3; break;
@@ -1051,6 +1053,49 @@ static int falcon_b0_test_registers(struct efx_nic *efx)
**************************************************************************
*/
+static enum reset_type falcon_map_reset_reason(enum reset_type reason)
+{
+ switch (reason) {
+ case RESET_TYPE_RX_RECOVERY:
+ case RESET_TYPE_RX_DESC_FETCH:
+ case RESET_TYPE_TX_DESC_FETCH:
+ case RESET_TYPE_TX_SKIP:
+ /* These can occasionally occur due to hardware bugs.
+ * We try to reset without disrupting the link.
+ */
+ return RESET_TYPE_INVISIBLE;
+ default:
+ return RESET_TYPE_ALL;
+ }
+}
+
+static int falcon_map_reset_flags(u32 *flags)
+{
+ enum {
+ FALCON_RESET_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER |
+ ETH_RESET_OFFLOAD | ETH_RESET_MAC),
+ FALCON_RESET_ALL = FALCON_RESET_INVISIBLE | ETH_RESET_PHY,
+ FALCON_RESET_WORLD = FALCON_RESET_ALL | ETH_RESET_IRQ,
+ };
+
+ if ((*flags & FALCON_RESET_WORLD) == FALCON_RESET_WORLD) {
+ *flags &= ~FALCON_RESET_WORLD;
+ return RESET_TYPE_WORLD;
+ }
+
+ if ((*flags & FALCON_RESET_ALL) == FALCON_RESET_ALL) {
+ *flags &= ~FALCON_RESET_ALL;
+ return RESET_TYPE_ALL;
+ }
+
+ if ((*flags & FALCON_RESET_INVISIBLE) == FALCON_RESET_INVISIBLE) {
+ *flags &= ~FALCON_RESET_INVISIBLE;
+ return RESET_TYPE_INVISIBLE;
+ }
+
+ return -EINVAL;
+}
+
/* Resets NIC to known state. This routine must be called in process
* context and is allowed to sleep. */
static int __falcon_reset_hw(struct efx_nic *efx, enum reset_type method)
@@ -1381,7 +1426,8 @@ static int falcon_probe_nic(struct efx_nic *efx)
}
dev = pci_dev_get(efx->pci_dev);
- while ((dev = pci_get_device(EFX_VENDID_SFC, FALCON_A_S_DEVID,
+ while ((dev = pci_get_device(PCI_VENDOR_ID_SOLARFLARE,
+ PCI_DEVICE_ID_SOLARFLARE_SFC4000A_1,
dev))) {
if (dev->bus == efx->pci_dev->bus &&
dev->devfn == efx->pci_dev->devfn + 1) {
@@ -1709,6 +1755,8 @@ const struct efx_nic_type falcon_a1_nic_type = {
.init = falcon_init_nic,
.fini = efx_port_dummy_op_void,
.monitor = falcon_monitor,
+ .map_reset_reason = falcon_map_reset_reason,
+ .map_reset_flags = falcon_map_reset_flags,
.reset = falcon_reset_hw,
.probe_port = falcon_probe_port,
.remove_port = falcon_remove_port,
@@ -1741,7 +1789,6 @@ const struct efx_nic_type falcon_a1_nic_type = {
.tx_dc_base = 0x130000,
.rx_dc_base = 0x100000,
.offload_features = NETIF_F_IP_CSUM,
- .reset_world_flags = ETH_RESET_IRQ,
};
const struct efx_nic_type falcon_b0_nic_type = {
@@ -1750,6 +1797,8 @@ const struct efx_nic_type falcon_b0_nic_type = {
.init = falcon_init_nic,
.fini = efx_port_dummy_op_void,
.monitor = falcon_monitor,
+ .map_reset_reason = falcon_map_reset_reason,
+ .map_reset_flags = falcon_map_reset_flags,
.reset = falcon_reset_hw,
.probe_port = falcon_probe_port,
.remove_port = falcon_remove_port,
@@ -1791,6 +1840,5 @@ const struct efx_nic_type falcon_b0_nic_type = {
.tx_dc_base = 0x130000,
.rx_dc_base = 0x100000,
.offload_features = NETIF_F_IP_CSUM | NETIF_F_RXHASH | NETIF_F_NTUPLE,
- .reset_world_flags = ETH_RESET_IRQ,
};
diff --git a/drivers/net/sfc/falcon_boards.c b/drivers/net/ethernet/sfc/falcon_boards.c
index b9cc846811d6..6cc16b8cc6f4 100644
--- a/drivers/net/sfc/falcon_boards.c
+++ b/drivers/net/ethernet/sfc/falcon_boards.c
@@ -764,7 +764,8 @@ int falcon_probe_board(struct efx_nic *efx, u16 revision_info)
if (board->type) {
netif_info(efx, probe, efx->net_dev, "board is %s rev %c%d\n",
- (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC)
+ (efx->pci_dev->subsystem_vendor ==
+ PCI_VENDOR_ID_SOLARFLARE)
? board->type->ref_model : board->type->gen_type,
'A' + board->major, board->minor);
return 0;
diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/ethernet/sfc/falcon_xmac.c
index 9516452c079c..9516452c079c 100644
--- a/drivers/net/sfc/falcon_xmac.c
+++ b/drivers/net/ethernet/sfc/falcon_xmac.c
diff --git a/drivers/net/sfc/filter.c b/drivers/net/ethernet/sfc/filter.c
index 95a980fd63d5..2b9636f96e05 100644
--- a/drivers/net/sfc/filter.c
+++ b/drivers/net/ethernet/sfc/filter.c
@@ -335,28 +335,35 @@ static int efx_filter_search(struct efx_filter_table *table,
bool for_insert, int *depth_required)
{
unsigned hash, incr, filter_idx, depth, depth_max;
- struct efx_filter_spec *cmp;
hash = efx_filter_hash(key);
incr = efx_filter_increment(key);
- depth_max = (spec->priority <= EFX_FILTER_PRI_HINT ?
- FILTER_CTL_SRCH_HINT_MAX : FILTER_CTL_SRCH_MAX);
-
- for (depth = 1, filter_idx = hash & (table->size - 1);
- depth <= depth_max && test_bit(filter_idx, table->used_bitmap);
- ++depth) {
- cmp = &table->spec[filter_idx];
- if (efx_filter_equal(spec, cmp))
- goto found;
+
+ filter_idx = hash & (table->size - 1);
+ depth = 1;
+ depth_max = (for_insert ?
+ (spec->priority <= EFX_FILTER_PRI_HINT ?
+ FILTER_CTL_SRCH_HINT_MAX : FILTER_CTL_SRCH_MAX) :
+ table->search_depth[spec->type]);
+
+ for (;;) {
+ /* Return success if entry is used and matches this spec
+ * or entry is unused and we are trying to insert.
+ */
+ if (test_bit(filter_idx, table->used_bitmap) ?
+ efx_filter_equal(spec, &table->spec[filter_idx]) :
+ for_insert) {
+ *depth_required = depth;
+ return filter_idx;
+ }
+
+ /* Return failure if we reached the maximum search depth */
+ if (depth == depth_max)
+ return for_insert ? -EBUSY : -ENOENT;
+
filter_idx = (filter_idx + incr) & (table->size - 1);
+ ++depth;
}
- if (!for_insert)
- return -ENOENT;
- if (depth > depth_max)
- return -EBUSY;
-found:
- *depth_required = depth;
- return filter_idx;
}
/* Construct/deconstruct external filter IDs */
@@ -650,11 +657,11 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
return -EPROTONOSUPPORT;
/* RFS must validate the IP header length before calling us */
- EFX_BUG_ON_PARANOID(!pskb_may_pull(skb, nhoff + sizeof(*ip)));
+ EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip));
ip = (const struct iphdr *)(skb->data + nhoff);
- if (ip->frag_off & htons(IP_MF | IP_OFFSET))
+ if (ip_is_fragment(ip))
return -EPROTONOSUPPORT;
- EFX_BUG_ON_PARANOID(!pskb_may_pull(skb, nhoff + 4 * ip->ihl + 4));
+ EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4);
ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl);
efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT, 0, rxq_index);
diff --git a/drivers/net/sfc/filter.h b/drivers/net/ethernet/sfc/filter.h
index 872f2132a496..872f2132a496 100644
--- a/drivers/net/sfc/filter.h
+++ b/drivers/net/ethernet/sfc/filter.h
diff --git a/drivers/net/sfc/io.h b/drivers/net/ethernet/sfc/io.h
index cc978803d484..751d1ec112cc 100644
--- a/drivers/net/sfc/io.h
+++ b/drivers/net/ethernet/sfc/io.h
@@ -103,7 +103,6 @@ static inline void efx_writeo(struct efx_nic *efx, efx_oword_t *value,
_efx_writed(efx, value->u32[2], reg + 8);
_efx_writed(efx, value->u32[3], reg + 12);
#endif
- wmb();
mmiowb();
spin_unlock_irqrestore(&efx->biu_lock, flags);
}
@@ -126,7 +125,6 @@ static inline void efx_sram_writeq(struct efx_nic *efx, void __iomem *membase,
__raw_writel((__force u32)value->u32[0], membase + addr);
__raw_writel((__force u32)value->u32[1], membase + addr + 4);
#endif
- wmb();
mmiowb();
spin_unlock_irqrestore(&efx->biu_lock, flags);
}
@@ -141,7 +139,6 @@ static inline void efx_writed(struct efx_nic *efx, efx_dword_t *value,
/* No lock required */
_efx_writed(efx, value->u32[0], reg);
- wmb();
}
/* Read a 128-bit CSR, locking as appropriate. */
@@ -152,7 +149,6 @@ static inline void efx_reado(struct efx_nic *efx, efx_oword_t *value,
spin_lock_irqsave(&efx->biu_lock, flags);
value->u32[0] = _efx_readd(efx, reg + 0);
- rmb();
value->u32[1] = _efx_readd(efx, reg + 4);
value->u32[2] = _efx_readd(efx, reg + 8);
value->u32[3] = _efx_readd(efx, reg + 12);
@@ -175,7 +171,6 @@ static inline void efx_sram_readq(struct efx_nic *efx, void __iomem *membase,
value->u64[0] = (__force __le64)__raw_readq(membase + addr);
#else
value->u32[0] = (__force __le32)__raw_readl(membase + addr);
- rmb();
value->u32[1] = (__force __le32)__raw_readl(membase + addr + 4);
#endif
spin_unlock_irqrestore(&efx->biu_lock, flags);
@@ -249,7 +244,6 @@ static inline void _efx_writeo_page(struct efx_nic *efx, efx_oword_t *value,
_efx_writed(efx, value->u32[2], reg + 8);
_efx_writed(efx, value->u32[3], reg + 12);
#endif
- wmb();
}
#define efx_writeo_page(efx, value, reg, page) \
_efx_writeo_page(efx, value, \
diff --git a/drivers/net/sfc/mac.h b/drivers/net/ethernet/sfc/mac.h
index d6a255d0856b..d6a255d0856b 100644
--- a/drivers/net/sfc/mac.h
+++ b/drivers/net/ethernet/sfc/mac.h
diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c
index 3dd45ed61f0a..81a425397468 100644
--- a/drivers/net/sfc/mcdi.c
+++ b/drivers/net/ethernet/sfc/mcdi.c
@@ -50,20 +50,6 @@ static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx)
return &nic_data->mcdi;
}
-static inline void
-efx_mcdi_readd(struct efx_nic *efx, efx_dword_t *value, unsigned reg)
-{
- struct siena_nic_data *nic_data = efx->nic_data;
- value->u32[0] = (__force __le32)__raw_readl(nic_data->mcdi_smem + reg);
-}
-
-static inline void
-efx_mcdi_writed(struct efx_nic *efx, const efx_dword_t *value, unsigned reg)
-{
- struct siena_nic_data *nic_data = efx->nic_data;
- __raw_writel((__force u32)value->u32[0], nic_data->mcdi_smem + reg);
-}
-
void efx_mcdi_init(struct efx_nic *efx)
{
struct efx_mcdi_iface *mcdi;
@@ -84,8 +70,8 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
const u8 *inbuf, size_t inlen)
{
struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
- unsigned pdu = MCDI_PDU(efx);
- unsigned doorbell = MCDI_DOORBELL(efx);
+ unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
+ unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx);
unsigned int i;
efx_dword_t hdr;
u32 xflags, seqno;
@@ -106,28 +92,29 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
MCDI_HEADER_SEQ, seqno,
MCDI_HEADER_XFLAGS, xflags);
- efx_mcdi_writed(efx, &hdr, pdu);
+ efx_writed(efx, &hdr, pdu);
for (i = 0; i < inlen; i += 4)
- efx_mcdi_writed(efx, (const efx_dword_t *)(inbuf + i),
- pdu + 4 + i);
+ _efx_writed(efx, *((__le32 *)(inbuf + i)), pdu + 4 + i);
+
+ /* Ensure the payload is written out before the header */
+ wmb();
/* ring the doorbell with a distinctive value */
- EFX_POPULATE_DWORD_1(hdr, EFX_DWORD_0, 0x45789abc);
- efx_mcdi_writed(efx, &hdr, doorbell);
+ _efx_writed(efx, (__force __le32) 0x45789abc, doorbell);
}
static void efx_mcdi_copyout(struct efx_nic *efx, u8 *outbuf, size_t outlen)
{
struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
- unsigned int pdu = MCDI_PDU(efx);
+ unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
int i;
BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
BUG_ON(outlen & 3 || outlen >= 0x100);
for (i = 0; i < outlen; i += 4)
- efx_mcdi_readd(efx, (efx_dword_t *)(outbuf + i), pdu + 4 + i);
+ *((__le32 *)(outbuf + i)) = _efx_readd(efx, pdu + 4 + i);
}
static int efx_mcdi_poll(struct efx_nic *efx)
@@ -135,7 +122,7 @@ static int efx_mcdi_poll(struct efx_nic *efx)
struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
unsigned int time, finish;
unsigned int respseq, respcmd, error;
- unsigned int pdu = MCDI_PDU(efx);
+ unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
unsigned int rc, spins;
efx_dword_t reg;
@@ -161,7 +148,8 @@ static int efx_mcdi_poll(struct efx_nic *efx)
time = get_seconds();
- efx_mcdi_readd(efx, &reg, pdu);
+ rmb();
+ efx_readd(efx, &reg, pdu);
/* All 1's indicates that shared memory is in reset (and is
* not a valid header). Wait for it to come out reset before
@@ -188,7 +176,7 @@ static int efx_mcdi_poll(struct efx_nic *efx)
respseq, mcdi->seqno);
rc = EIO;
} else if (error) {
- efx_mcdi_readd(efx, &reg, pdu + 4);
+ efx_readd(efx, &reg, pdu + 4);
switch (EFX_DWORD_FIELD(reg, EFX_DWORD_0)) {
#define TRANSLATE_ERROR(name) \
case MC_CMD_ERR_ ## name: \
@@ -222,21 +210,21 @@ out:
/* Test and clear MC-rebooted flag for this port/function */
int efx_mcdi_poll_reboot(struct efx_nic *efx)
{
- unsigned int addr = MCDI_REBOOT_FLAG(efx);
+ unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_REBOOT_FLAG(efx);
efx_dword_t reg;
uint32_t value;
if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
return false;
- efx_mcdi_readd(efx, &reg, addr);
+ efx_readd(efx, &reg, addr);
value = EFX_DWORD_FIELD(reg, EFX_DWORD_0);
if (value == 0)
return 0;
EFX_ZERO_DWORD(reg);
- efx_mcdi_writed(efx, &reg, addr);
+ efx_writed(efx, &reg, addr);
if (value == MC_STATUS_DWORD_ASSERT)
return -EINTR;
diff --git a/drivers/net/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h
index aced2a7856fc..aced2a7856fc 100644
--- a/drivers/net/sfc/mcdi.h
+++ b/drivers/net/ethernet/sfc/mcdi.h
diff --git a/drivers/net/sfc/mcdi_mac.c b/drivers/net/ethernet/sfc/mcdi_mac.c
index 50c20777a564..50c20777a564 100644
--- a/drivers/net/sfc/mcdi_mac.c
+++ b/drivers/net/ethernet/sfc/mcdi_mac.c
diff --git a/drivers/net/sfc/mcdi_pcol.h b/drivers/net/ethernet/sfc/mcdi_pcol.h
index 41fe06fa0600..41fe06fa0600 100644
--- a/drivers/net/sfc/mcdi_pcol.h
+++ b/drivers/net/ethernet/sfc/mcdi_pcol.h
diff --git a/drivers/net/sfc/mcdi_phy.c b/drivers/net/ethernet/sfc/mcdi_phy.c
index 6c63ab0710af..6c63ab0710af 100644
--- a/drivers/net/sfc/mcdi_phy.c
+++ b/drivers/net/ethernet/sfc/mcdi_phy.c
diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/ethernet/sfc/mdio_10g.c
index 7ab385c8136d..7ab385c8136d 100644
--- a/drivers/net/sfc/mdio_10g.c
+++ b/drivers/net/ethernet/sfc/mdio_10g.c
diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/ethernet/sfc/mdio_10g.h
index a97dbbd2de99..a97dbbd2de99 100644
--- a/drivers/net/sfc/mdio_10g.h
+++ b/drivers/net/ethernet/sfc/mdio_10g.h
diff --git a/drivers/net/sfc/mtd.c b/drivers/net/ethernet/sfc/mtd.c
index b6304486f244..b6304486f244 100644
--- a/drivers/net/sfc/mtd.c
+++ b/drivers/net/ethernet/sfc/mtd.c
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index e8d5f03a89fe..b8e251a1ee48 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -17,7 +17,6 @@
#define DEBUG
#endif
-#include <linux/version.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
@@ -645,7 +644,7 @@ struct efx_filter_state;
* @irq_rx_moderation: IRQ moderation time for RX event queues
* @msg_enable: Log message enable flags
* @state: Device state flag. Serialised by the rtnl_lock.
- * @reset_pending: Pending reset method (normally RESET_TYPE_NONE)
+ * @reset_pending: Bitmask for pending resets
* @tx_queue: TX DMA queues
* @rx_queue: RX DMA queues
* @channel: Channels
@@ -728,7 +727,7 @@ struct efx_nic {
u32 msg_enable;
enum nic_state state;
- enum reset_type reset_pending;
+ unsigned long reset_pending;
struct efx_channel *channel[EFX_MAX_CHANNELS];
char channel_name[EFX_MAX_CHANNELS][IFNAMSIZ + 6];
@@ -828,6 +827,8 @@ static inline unsigned int efx_port_num(struct efx_nic *efx)
* @init: Initialise the controller
* @fini: Shut down the controller
* @monitor: Periodic function for polling link state and hardware monitor
+ * @map_reset_reason: Map ethtool reset reason to a reset method
+ * @map_reset_flags: Map ethtool reset flags to a reset method, if possible
* @reset: Reset the controller hardware and possibly the PHY. This will
* be called while the controller is uninitialised.
* @probe_port: Probe the MAC and PHY
@@ -865,8 +866,6 @@ static inline unsigned int efx_port_num(struct efx_nic *efx)
* @rx_dc_base: Base address in SRAM of RX queue descriptor caches
* @offload_features: net_device feature flags for protocol offload
* features implemented in hardware
- * @reset_world_flags: Flags for additional components covered by
- * reset method RESET_TYPE_WORLD
*/
struct efx_nic_type {
int (*probe)(struct efx_nic *efx);
@@ -874,6 +873,8 @@ struct efx_nic_type {
int (*init)(struct efx_nic *efx);
void (*fini)(struct efx_nic *efx);
void (*monitor)(struct efx_nic *efx);
+ enum reset_type (*map_reset_reason)(enum reset_type reason);
+ int (*map_reset_flags)(u32 *flags);
int (*reset)(struct efx_nic *efx, enum reset_type method);
int (*probe_port)(struct efx_nic *efx);
void (*remove_port)(struct efx_nic *efx);
@@ -908,7 +909,6 @@ struct efx_nic_type {
unsigned int tx_dc_base;
unsigned int rx_dc_base;
u32 offload_features;
- u32 reset_world_flags;
};
/**************************************************************************
diff --git a/drivers/net/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c
index f2a2b947f860..3edfbaf5f022 100644
--- a/drivers/net/sfc/nic.c
+++ b/drivers/net/ethernet/sfc/nic.c
@@ -10,6 +10,7 @@
#include <linux/bitops.h>
#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/seq_file.h>
@@ -1935,13 +1936,6 @@ void efx_nic_get_regs(struct efx_nic *efx, void *buf)
size = min_t(size_t, table->step, 16);
- if (table->offset >= efx->type->mem_map_size) {
- /* No longer mapped; return dummy data */
- memcpy(buf, "\xde\xc0\xad\xde", 4);
- buf += table->rows * size;
- continue;
- }
-
for (i = 0; i < table->rows; i++) {
switch (table->step) {
case 4: /* 32-bit register or SRAM */
diff --git a/drivers/net/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h
index 4bd1f2839dfe..5fb24d3aa3ca 100644
--- a/drivers/net/sfc/nic.h
+++ b/drivers/net/ethernet/sfc/nic.h
@@ -143,12 +143,10 @@ static inline struct falcon_board *falcon_board(struct efx_nic *efx)
/**
* struct siena_nic_data - Siena NIC state
* @mcdi: Management-Controller-to-Driver Interface
- * @mcdi_smem: MCDI shared memory mapping. The mapping is always uncacheable.
* @wol_filter_id: Wake-on-LAN packet filter id
*/
struct siena_nic_data {
struct efx_mcdi_iface mcdi;
- void __iomem *mcdi_smem;
int wol_filter_id;
};
@@ -204,7 +202,8 @@ extern irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx);
extern irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id);
extern void falcon_irq_ack_a1(struct efx_nic *efx);
-#define EFX_IRQ_MOD_RESOLUTION 5
+#define EFX_IRQ_MOD_RESOLUTION 5
+#define EFX_IRQ_MOD_MAX 0x1000
/* Global Resources */
extern int efx_nic_flush_queues(struct efx_nic *efx);
diff --git a/drivers/net/sfc/phy.h b/drivers/net/ethernet/sfc/phy.h
index 11d148cd8441..11d148cd8441 100644
--- a/drivers/net/sfc/phy.h
+++ b/drivers/net/ethernet/sfc/phy.h
diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/ethernet/sfc/qt202x_phy.c
index 7ad97e397406..7ad97e397406 100644
--- a/drivers/net/sfc/qt202x_phy.c
+++ b/drivers/net/ethernet/sfc/qt202x_phy.c
diff --git a/drivers/net/sfc/regs.h b/drivers/net/ethernet/sfc/regs.h
index cc2c86b76a7b..cc2c86b76a7b 100644
--- a/drivers/net/sfc/regs.h
+++ b/drivers/net/ethernet/sfc/regs.h
diff --git a/drivers/net/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c
index 62e43649466e..752d521c09b1 100644
--- a/drivers/net/sfc/rx.c
+++ b/drivers/net/ethernet/sfc/rx.c
@@ -15,6 +15,7 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/prefetch.h>
+#include <linux/moduleparam.h>
#include <net/ip.h>
#include <net/checksum.h>
#include "net_driver.h"
@@ -478,10 +479,10 @@ static void efx_rx_packet_gro(struct efx_channel *channel,
if (efx->net_dev->features & NETIF_F_RXHASH)
skb->rxhash = efx_rx_buf_hash(eh);
- skb_shinfo(skb)->frags[0].page = page;
+ skb_frag_set_page(skb, 0, page);
skb_shinfo(skb)->frags[0].page_offset =
efx_rx_buf_offset(efx, rx_buf);
- skb_shinfo(skb)->frags[0].size = rx_buf->len;
+ skb_frag_size_set(&skb_shinfo(skb)->frags[0], rx_buf->len);
skb_shinfo(skb)->nr_frags = 1;
skb->len = rx_buf->len;
diff --git a/drivers/net/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c
index 822f6c2a6a7c..822f6c2a6a7c 100644
--- a/drivers/net/sfc/selftest.c
+++ b/drivers/net/ethernet/sfc/selftest.c
diff --git a/drivers/net/sfc/selftest.h b/drivers/net/ethernet/sfc/selftest.h
index dba5456e70f3..dba5456e70f3 100644
--- a/drivers/net/sfc/selftest.h
+++ b/drivers/net/ethernet/sfc/selftest.h
diff --git a/drivers/net/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c
index fb4721f780ff..cc2549cb7076 100644
--- a/drivers/net/sfc/siena.c
+++ b/drivers/net/ethernet/sfc/siena.c
@@ -36,6 +36,8 @@ static void siena_push_irq_moderation(struct efx_channel *channel)
{
efx_dword_t timer_cmd;
+ BUILD_BUG_ON(EFX_IRQ_MOD_MAX > (1 << FRF_CZ_TC_TIMER_VAL_WIDTH));
+
if (channel->irq_moderation)
EFX_POPULATE_DWORD_2(timer_cmd,
FRF_CZ_TC_TIMER_MODE,
@@ -177,6 +179,36 @@ static int siena_test_registers(struct efx_nic *efx)
**************************************************************************
*/
+static enum reset_type siena_map_reset_reason(enum reset_type reason)
+{
+ return RESET_TYPE_ALL;
+}
+
+static int siena_map_reset_flags(u32 *flags)
+{
+ enum {
+ SIENA_RESET_PORT = (ETH_RESET_DMA | ETH_RESET_FILTER |
+ ETH_RESET_OFFLOAD | ETH_RESET_MAC |
+ ETH_RESET_PHY),
+ SIENA_RESET_MC = (SIENA_RESET_PORT |
+ ETH_RESET_MGMT << ETH_RESET_SHARED_SHIFT),
+ };
+
+ if ((*flags & SIENA_RESET_MC) == SIENA_RESET_MC) {
+ *flags &= ~SIENA_RESET_MC;
+ return RESET_TYPE_WORLD;
+ }
+
+ if ((*flags & SIENA_RESET_PORT) == SIENA_RESET_PORT) {
+ *flags &= ~SIENA_RESET_PORT;
+ return RESET_TYPE_ALL;
+ }
+
+ /* no invisible reset implemented */
+
+ return -EINVAL;
+}
+
static int siena_reset_hw(struct efx_nic *efx, enum reset_type method)
{
int rc;
@@ -220,26 +252,12 @@ static int siena_probe_nic(struct efx_nic *efx)
efx_reado(efx, &reg, FR_AZ_CS_DEBUG);
efx->net_dev->dev_id = EFX_OWORD_FIELD(reg, FRF_CZ_CS_PORT_NUM) - 1;
- /* Initialise MCDI */
- nic_data->mcdi_smem = ioremap_nocache(efx->membase_phys +
- FR_CZ_MC_TREG_SMEM,
- FR_CZ_MC_TREG_SMEM_STEP *
- FR_CZ_MC_TREG_SMEM_ROWS);
- if (!nic_data->mcdi_smem) {
- netif_err(efx, probe, efx->net_dev,
- "could not map MCDI at %llx+%x\n",
- (unsigned long long)efx->membase_phys +
- FR_CZ_MC_TREG_SMEM,
- FR_CZ_MC_TREG_SMEM_STEP * FR_CZ_MC_TREG_SMEM_ROWS);
- rc = -ENOMEM;
- goto fail1;
- }
efx_mcdi_init(efx);
/* Recover from a failed assertion before probing */
rc = efx_mcdi_handle_assertion(efx);
if (rc)
- goto fail2;
+ goto fail1;
/* Let the BMC know that the driver is now in charge of link and
* filter settings. We must do this before we reset the NIC */
@@ -294,7 +312,6 @@ fail4:
fail3:
efx_mcdi_drv_attach(efx, false, NULL);
fail2:
- iounmap(nic_data->mcdi_smem);
fail1:
kfree(efx->nic_data);
return rc;
@@ -374,8 +391,6 @@ static int siena_init_nic(struct efx_nic *efx)
static void siena_remove_nic(struct efx_nic *efx)
{
- struct siena_nic_data *nic_data = efx->nic_data;
-
efx_nic_free_buffer(efx, &efx->irq_status);
siena_reset_hw(efx, RESET_TYPE_ALL);
@@ -385,22 +400,20 @@ static void siena_remove_nic(struct efx_nic *efx)
efx_mcdi_drv_attach(efx, false, NULL);
/* Tear down the private nic state */
- iounmap(nic_data->mcdi_smem);
- kfree(nic_data);
+ kfree(efx->nic_data);
efx->nic_data = NULL;
}
-#define STATS_GENERATION_INVALID ((u64)(-1))
+#define STATS_GENERATION_INVALID ((__force __le64)(-1))
static int siena_try_update_nic_stats(struct efx_nic *efx)
{
- u64 *dma_stats;
+ __le64 *dma_stats;
struct efx_mac_stats *mac_stats;
- u64 generation_start;
- u64 generation_end;
+ __le64 generation_start, generation_end;
mac_stats = &efx->mac_stats;
- dma_stats = (u64 *)efx->stats_buffer.addr;
+ dma_stats = efx->stats_buffer.addr;
generation_end = dma_stats[MC_CMD_MAC_GENERATION_END];
if (generation_end == STATS_GENERATION_INVALID)
@@ -408,7 +421,7 @@ static int siena_try_update_nic_stats(struct efx_nic *efx)
rmb();
#define MAC_STAT(M, D) \
- mac_stats->M = dma_stats[MC_CMD_MAC_ ## D]
+ mac_stats->M = le64_to_cpu(dma_stats[MC_CMD_MAC_ ## D])
MAC_STAT(tx_bytes, TX_BYTES);
MAC_STAT(tx_bad_bytes, TX_BAD_BYTES);
@@ -478,7 +491,8 @@ static int siena_try_update_nic_stats(struct efx_nic *efx)
MAC_STAT(rx_internal_error, RX_INTERNAL_ERROR_PKTS);
mac_stats->rx_good_lt64 = 0;
- efx->n_rx_nodesc_drop_cnt = dma_stats[MC_CMD_MAC_RX_NODESC_DROPS];
+ efx->n_rx_nodesc_drop_cnt =
+ le64_to_cpu(dma_stats[MC_CMD_MAC_RX_NODESC_DROPS]);
#undef MAC_STAT
@@ -507,7 +521,7 @@ static void siena_update_nic_stats(struct efx_nic *efx)
static void siena_start_nic_stats(struct efx_nic *efx)
{
- u64 *dma_stats = (u64 *)efx->stats_buffer.addr;
+ __le64 *dma_stats = efx->stats_buffer.addr;
dma_stats[MC_CMD_MAC_GENERATION_END] = STATS_GENERATION_INVALID;
@@ -605,6 +619,8 @@ const struct efx_nic_type siena_a0_nic_type = {
.init = siena_init_nic,
.fini = efx_port_dummy_op_void,
.monitor = NULL,
+ .map_reset_reason = siena_map_reset_reason,
+ .map_reset_flags = siena_map_reset_flags,
.reset = siena_reset_hw,
.probe_port = siena_probe_port,
.remove_port = siena_remove_port,
@@ -624,7 +640,8 @@ const struct efx_nic_type siena_a0_nic_type = {
.default_mac_ops = &efx_mcdi_mac_operations,
.revision = EFX_REV_SIENA_A0,
- .mem_map_size = FR_CZ_MC_TREG_SMEM, /* MC_TREG_SMEM mapped separately */
+ .mem_map_size = (FR_CZ_MC_TREG_SMEM +
+ FR_CZ_MC_TREG_SMEM_STEP * FR_CZ_MC_TREG_SMEM_ROWS),
.txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL,
.rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL,
.buf_tbl_base = FR_BZ_BUF_FULL_TBL,
@@ -641,5 +658,4 @@ const struct efx_nic_type siena_a0_nic_type = {
.rx_dc_base = 0x68000,
.offload_features = (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_RXHASH | NETIF_F_NTUPLE),
- .reset_world_flags = ETH_RESET_MGMT << ETH_RESET_SHARED_SHIFT,
};
diff --git a/drivers/net/sfc/spi.h b/drivers/net/ethernet/sfc/spi.h
index 71f2e3ebe1c7..71f2e3ebe1c7 100644
--- a/drivers/net/sfc/spi.h
+++ b/drivers/net/ethernet/sfc/spi.h
diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/ethernet/sfc/tenxpress.c
index 7b0fd89e7b85..7b0fd89e7b85 100644
--- a/drivers/net/sfc/tenxpress.c
+++ b/drivers/net/ethernet/sfc/tenxpress.c
diff --git a/drivers/net/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c
index 84eb99e0f8d2..df88c5430f95 100644
--- a/drivers/net/sfc/tx.c
+++ b/drivers/net/ethernet/sfc/tx.c
@@ -137,8 +137,6 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
struct pci_dev *pci_dev = efx->pci_dev;
struct efx_tx_buffer *buffer;
skb_frag_t *fragment;
- struct page *page;
- int page_offset;
unsigned int len, unmap_len = 0, fill_level, insert_ptr;
dma_addr_t dma_addr, unmap_addr = 0;
unsigned int dma_len;
@@ -240,14 +238,12 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
if (i >= skb_shinfo(skb)->nr_frags)
break;
fragment = &skb_shinfo(skb)->frags[i];
- len = fragment->size;
- page = fragment->page;
- page_offset = fragment->page_offset;
+ len = skb_frag_size(fragment);
i++;
/* Map for DMA */
unmap_single = false;
- dma_addr = pci_map_page(pci_dev, page, page_offset, len,
- PCI_DMA_TODEVICE);
+ dma_addr = skb_frag_dma_map(&pci_dev->dev, fragment, 0, len,
+ DMA_TO_DEVICE);
}
/* Transfer ownership of the skb to the final buffer */
@@ -929,13 +925,12 @@ static void tso_start(struct tso_state *st, const struct sk_buff *skb)
static int tso_get_fragment(struct tso_state *st, struct efx_nic *efx,
skb_frag_t *frag)
{
- st->unmap_addr = pci_map_page(efx->pci_dev, frag->page,
- frag->page_offset, frag->size,
- PCI_DMA_TODEVICE);
- if (likely(!pci_dma_mapping_error(efx->pci_dev, st->unmap_addr))) {
+ st->unmap_addr = skb_frag_dma_map(&efx->pci_dev->dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
+ if (likely(!dma_mapping_error(&efx->pci_dev->dev, st->unmap_addr))) {
st->unmap_single = false;
- st->unmap_len = frag->size;
- st->in_len = frag->size;
+ st->unmap_len = skb_frag_size(frag);
+ st->in_len = skb_frag_size(frag);
st->dma_addr = st->unmap_addr;
return 0;
}
diff --git a/drivers/net/sfc/txc43128_phy.c b/drivers/net/ethernet/sfc/txc43128_phy.c
index 7c21b334a75b..7c21b334a75b 100644
--- a/drivers/net/sfc/txc43128_phy.c
+++ b/drivers/net/ethernet/sfc/txc43128_phy.c
diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/ethernet/sfc/workarounds.h
index 99ff11400cef..e4dd3a7f304b 100644
--- a/drivers/net/sfc/workarounds.h
+++ b/drivers/net/ethernet/sfc/workarounds.h
@@ -38,8 +38,6 @@
#define EFX_WORKAROUND_15783 EFX_WORKAROUND_ALWAYS
/* Legacy interrupt storm when interrupt fifo fills */
#define EFX_WORKAROUND_17213 EFX_WORKAROUND_SIENA
-/* Write combining and sriov=enabled are incompatible */
-#define EFX_WORKAROUND_22643 EFX_WORKAROUND_SIENA
/* Spurious parity errors in TSORT buffers */
#define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A
diff --git a/drivers/net/ethernet/sgi/Kconfig b/drivers/net/ethernet/sgi/Kconfig
new file mode 100644
index 000000000000..c1c4bb868a3b
--- /dev/null
+++ b/drivers/net/ethernet/sgi/Kconfig
@@ -0,0 +1,36 @@
+#
+# SGI device configuration
+#
+
+config NET_VENDOR_SGI
+ bool "SGI devices"
+ default y
+ depends on (PCI && SGI_IP27) || SGI_IP32
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about SGI devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_SGI
+
+config SGI_IOC3_ETH
+ bool "SGI IOC3 Ethernet"
+ depends on PCI && SGI_IP27
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+config SGI_O2MACE_ETH
+ tristate "SGI O2 MACE Fast Ethernet support"
+ depends on SGI_IP32=y
+
+endif # NET_VENDOR_SGI
diff --git a/drivers/net/ethernet/sgi/Makefile b/drivers/net/ethernet/sgi/Makefile
new file mode 100644
index 000000000000..e5bedd271e29
--- /dev/null
+++ b/drivers/net/ethernet/sgi/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the SGI device drivers.
+#
+
+obj-$(CONFIG_SGI_O2MACE_ETH) += meth.o
+obj-$(CONFIG_SGI_IOC3_ETH) += ioc3-eth.o
diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c
index 32f07f868d89..ac149d99f78f 100644
--- a/drivers/net/ioc3-eth.c
+++ b/drivers/net/ethernet/sgi/ioc3-eth.c
@@ -532,7 +532,7 @@ static void ioc3_tcpudp_checksum(struct sk_buff *skb, uint32_t hwsum, int len)
return;
ih = (struct iphdr *) ((char *)eh + ETH_HLEN);
- if (ih->frag_off & htons(IP_MF | IP_OFFSET))
+ if (ip_is_fragment(ih))
return;
proto = ih->protocol;
@@ -1220,7 +1220,7 @@ static const struct net_device_ops ioc3_netdev_ops = {
.ndo_start_xmit = ioc3_start_xmit,
.ndo_tx_timeout = ioc3_timeout,
.ndo_get_stats = ioc3_get_stats,
- .ndo_set_multicast_list = ioc3_set_multicast_list,
+ .ndo_set_rx_mode = ioc3_set_multicast_list,
.ndo_do_ioctl = ioc3_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = ioc3_set_mac_address,
@@ -1664,12 +1664,7 @@ static void ioc3_set_multicast_list(struct net_device *dev)
ip->ehar_l = 0xffffffff;
} else {
netdev_for_each_mc_addr(ha, dev) {
- char *addr = ha->addr;
-
- if (!(*addr & 1))
- continue;
-
- ehar |= (1UL << ioc3_hash(addr));
+ ehar |= (1UL << ioc3_hash(ha->addr));
}
ip->ehar_h = ehar >> 32;
ip->ehar_l = ehar & 0xffffffff;
diff --git a/drivers/net/meth.c b/drivers/net/ethernet/sgi/meth.c
index 60135aa55802..60135aa55802 100644
--- a/drivers/net/meth.c
+++ b/drivers/net/ethernet/sgi/meth.c
diff --git a/drivers/net/meth.h b/drivers/net/ethernet/sgi/meth.h
index 5b145c6bad60..5b145c6bad60 100644
--- a/drivers/net/meth.h
+++ b/drivers/net/ethernet/sgi/meth.h
diff --git a/drivers/net/ethernet/silan/Kconfig b/drivers/net/ethernet/silan/Kconfig
new file mode 100644
index 000000000000..ae1ce170864d
--- /dev/null
+++ b/drivers/net/ethernet/silan/Kconfig
@@ -0,0 +1,33 @@
+#
+# Silan device configuration
+#
+
+config NET_VENDOR_SILAN
+ bool "Silan devices"
+ default y
+ depends on PCI && EXPERIMENTAL
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Silan devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_SILAN
+
+config SC92031
+ tristate "Silan SC92031 PCI Fast Ethernet Adapter driver (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ select CRC32
+ ---help---
+ This is a driver for the Fast Ethernet PCI network cards based on
+ the Silan SC92031 chip (sometimes also called Rsltek 8139D). If you
+ have one of these, say Y here.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sc92031. This is recommended.
+
+endif # NET_VENDOR_SILAN
diff --git a/drivers/net/ethernet/silan/Makefile b/drivers/net/ethernet/silan/Makefile
new file mode 100644
index 000000000000..4ad3523dcb92
--- /dev/null
+++ b/drivers/net/ethernet/silan/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Silan network device drivers.
+#
+
+obj-$(CONFIG_SC92031) += sc92031.o
diff --git a/drivers/net/sc92031.c b/drivers/net/ethernet/silan/sc92031.c
index fa74314ef789..a284d6440538 100644
--- a/drivers/net/sc92031.c
+++ b/drivers/net/ethernet/silan/sc92031.c
@@ -22,6 +22,7 @@
* matching, so you need to enable IFF_PROMISC when using it.
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
@@ -30,6 +31,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
+#include <linux/mii.h>
#include <linux/crc32.h>
#include <asm/irq.h>
@@ -115,16 +117,9 @@ enum silan_registers {
TestD8 = 0xD8,
};
-#define MII_BMCR 0 // Basic mode control register
-#define MII_BMSR 1 // Basic mode status register
#define MII_JAB 16
#define MII_OutputStatus 24
-#define BMCR_FULLDPLX 0x0100 // Full duplex
-#define BMCR_ANRESTART 0x0200 // Auto negotiation restart
-#define BMCR_ANENABLE 0x1000 // Enable auto negotiation
-#define BMCR_SPEED100 0x2000 // Select 100Mbps
-#define BMSR_LSTATUS 0x0004 // Link status
#define PHY_16_JAB_ENB 0x1000
#define PHY_16_PORT_ENB 0x1
@@ -1389,7 +1384,7 @@ static const struct net_device_ops sc92031_netdev_ops = {
.ndo_start_xmit = sc92031_start_xmit,
.ndo_open = sc92031_open,
.ndo_stop = sc92031_stop,
- .ndo_set_multicast_list = sc92031_set_multicast_list,
+ .ndo_set_rx_mode = sc92031_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/ethernet/sis/Kconfig b/drivers/net/ethernet/sis/Kconfig
new file mode 100644
index 000000000000..f1135cc1bd48
--- /dev/null
+++ b/drivers/net/ethernet/sis/Kconfig
@@ -0,0 +1,53 @@
+#
+# Silicon Integrated Systems (SiS) device configuration
+#
+
+config NET_VENDOR_SIS
+ bool "Silicon Integrated Systems (SiS) devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about SiS devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_SIS
+
+config SIS900
+ tristate "SiS 900/7016 PCI Fast Ethernet Adapter support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This is a driver for the Fast Ethernet PCI network cards based on
+ the SiS 900 and SiS 7016 chips. The SiS 900 core is also embedded in
+ SiS 630 and SiS 540 chipsets.
+
+ This driver also supports AMD 79C901 HomePNA so that you can use
+ your phone line as a network cable.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sis900. This is recommended.
+
+config SIS190
+ tristate "SiS190/SiS191 gigabit ethernet support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ Say Y here if you have a SiS 190 PCI Fast Ethernet adapter or
+ a SiS 191 PCI Gigabit Ethernet adapter. Both are expected to
+ appear in lan on motherboard designs which are based on SiS 965
+ and SiS 966 south bridge.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sis190. This is recommended.
+
+endif # NET_VENDOR_SIS
diff --git a/drivers/net/ethernet/sis/Makefile b/drivers/net/ethernet/sis/Makefile
new file mode 100644
index 000000000000..58d3ac1985df
--- /dev/null
+++ b/drivers/net/ethernet/sis/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Silicon Integrated Systems (SiS) network device drivers.
+#
+
+obj-$(CONFIG_SIS190) += sis190.o
+obj-$(CONFIG_SIS900) += sis900.o
diff --git a/drivers/net/sis190.c b/drivers/net/ethernet/sis/sis190.c
index b436e007eea0..1b4658c99391 100644
--- a/drivers/net/sis190.c
+++ b/drivers/net/ethernet/sis/sis190.c
@@ -21,6 +21,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netdevice.h>
@@ -1824,15 +1825,25 @@ static int sis190_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
generic_mii_ioctl(&tp->mii_if, if_mii(ifr), cmd, NULL);
}
+static int sis190_mac_addr(struct net_device *dev, void *p)
+{
+ int rc;
+
+ rc = eth_mac_addr(dev, p);
+ if (!rc)
+ sis190_init_rxfilter(dev);
+ return rc;
+}
+
static const struct net_device_ops sis190_netdev_ops = {
.ndo_open = sis190_open,
.ndo_stop = sis190_close,
.ndo_do_ioctl = sis190_ioctl,
.ndo_start_xmit = sis190_start_xmit,
.ndo_tx_timeout = sis190_tx_timeout,
- .ndo_set_multicast_list = sis190_set_rx_mode,
+ .ndo_set_rx_mode = sis190_set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
- .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_mac_address = sis190_mac_addr,
.ndo_validate_addr = eth_validate_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = sis190_netpoll,
diff --git a/drivers/net/sis900.c b/drivers/net/ethernet/sis/sis900.c
index 484f795a779d..a184abc5ef11 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/ethernet/sis/sis900.c
@@ -403,7 +403,7 @@ static const struct net_device_ops sis900_netdev_ops = {
.ndo_stop = sis900_close,
.ndo_start_xmit = sis900_start_xmit,
.ndo_set_config = sis900_set_config,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
@@ -482,7 +482,7 @@ static int __devinit sis900_probe(struct pci_dev *pci_dev,
ret = -ENOMEM;
goto err_out_cleardev;
}
- sis_priv->tx_ring = (BufferDesc *)ring_space;
+ sis_priv->tx_ring = ring_space;
sis_priv->tx_ring_dma = ring_dma;
ring_space = pci_alloc_consistent(pci_dev, RX_TOTAL_SIZE, &ring_dma);
@@ -490,7 +490,7 @@ static int __devinit sis900_probe(struct pci_dev *pci_dev,
ret = -ENOMEM;
goto err_unmap_tx;
}
- sis_priv->rx_ring = (BufferDesc *)ring_space;
+ sis_priv->rx_ring = ring_space;
sis_priv->rx_ring_dma = ring_dma;
/* The SiS900-specific entries in the device structure. */
diff --git a/drivers/net/sis900.h b/drivers/net/ethernet/sis/sis900.h
index 150511a922ef..150511a922ef 100644
--- a/drivers/net/sis900.h
+++ b/drivers/net/ethernet/sis/sis900.h
diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig
new file mode 100644
index 000000000000..5a689af516e9
--- /dev/null
+++ b/drivers/net/ethernet/smsc/Kconfig
@@ -0,0 +1,136 @@
+#
+# Western Digital/SMC network device configuration
+#
+
+config NET_VENDOR_SMSC
+ bool "SMC (SMSC)/Western Digital devices"
+ default y
+ depends on ARM || ISA || MAC || ARM || MIPS || M32R || SUPERH || \
+ BLACKFIN || MN10300 || COLDFIRE || PCI || PCMCIA
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about SMC/Western Digital cards. If you say Y, you will
+ be asked for your specific card in the following questions.
+
+if NET_VENDOR_SMSC
+
+config SMC9194
+ tristate "SMC 9194 support"
+ depends on (ISA || MAC && BROKEN)
+ select CRC32
+ ---help---
+ This is support for the SMC9xxx based Ethernet cards. Choose this
+ option if you have a DELL laptop with the docking station, or
+ another SMC9192/9194 based chipset. Say Y if you want it compiled
+ into the kernel, and read the file
+ <file:Documentation/networking/smc9.txt> and the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called smc9194.
+
+config SMC91X
+ tristate "SMC 91C9x/91C1xxx support"
+ select CRC32
+ select NET_CORE
+ select MII
+ depends on (ARM || M32R || SUPERH || MIPS || BLACKFIN || \
+ MN10300 || COLDFIRE)
+ ---help---
+ This is a driver for SMC's 91x series of Ethernet chipsets,
+ including the SMC91C94 and the SMC91C111. Say Y if you want it
+ compiled into the kernel, and read the file
+ <file:Documentation/networking/smc9.txt> and the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called smc91x. If you want to compile it as a
+ module, say M here and read <file:Documentation/kbuild/modules.txt>.
+
+config PCMCIA_SMC91C92
+ tristate "SMC 91Cxx PCMCIA support"
+ depends on PCMCIA
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
+ (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called smc91c92_cs. If unsure, say N.
+
+config EPIC100
+ tristate "SMC EtherPower II"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC,
+ which is based on the SMC83c17x (EPIC/100).
+ More specific information and updates are available from
+ <http://www.scyld.com/network/epic100.html>.
+
+config SMC911X
+ tristate "SMSC LAN911[5678] support"
+ select CRC32
+ select NET_CORE
+ select MII
+ depends on (ARM || SUPERH || MN10300)
+ ---help---
+ This is a driver for SMSC's LAN911x series of Ethernet chipsets
+ including the new LAN9115, LAN9116, LAN9117, and LAN9118.
+ Say Y if you want it compiled into the kernel,
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ This driver is also available as a module. The module will be
+ called smc911x. If you want to compile it as a module, say M
+ here and read <file:Documentation/kbuild/modules.txt>
+
+config SMSC911X
+ tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
+ depends on (ARM || SUPERH || BLACKFIN || MIPS || MN10300)
+ select CRC32
+ select NET_CORE
+ select MII
+ select PHYLIB
+ ---help---
+ Say Y here if you want support for SMSC LAN911x and LAN921x families
+ of ethernet controllers.
+
+ To compile this driver as a module, choose M here. The module
+ will be called smsc911x.
+
+config SMSC911X_ARCH_HOOKS
+ def_bool n
+ depends on SMSC911X
+ ---help---
+ If the arch enables this, it allows the arch to implement various
+ hooks for more comprehensive interrupt control and also to override
+ the source of the MAC address.
+
+config SMSC9420
+ tristate "SMSC LAN9420 PCI ethernet adapter support"
+ depends on PCI
+ select CRC32
+ select PHYLIB
+ select SMSC_PHY
+ ---help---
+ This is a driver for SMSC's LAN9420 PCI ethernet adapter.
+ Say Y if you want it compiled into the kernel,
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ This driver is also available as a module. The module will be
+ called smsc9420. If you want to compile it as a module, say M
+ here and read <file:Documentation/kbuild/modules.txt>
+
+endif # NET_VENDOR_SMSC
diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile
new file mode 100644
index 000000000000..f3438dec9d90
--- /dev/null
+++ b/drivers/net/ethernet/smsc/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the SMSC network device drivers.
+#
+
+obj-$(CONFIG_SMC9194) += smc9194.o
+obj-$(CONFIG_SMC91X) += smc91x.o
+obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o
+obj-$(CONFIG_EPIC100) += epic100.o
+obj-$(CONFIG_SMSC9420) += smsc9420.o
+obj-$(CONFIG_SMC911X) += smc911x.o
+obj-$(CONFIG_SMSC911X) += smsc911x.o
diff --git a/drivers/net/epic100.c b/drivers/net/ethernet/smsc/epic100.c
index c353bf3113cc..0a5dfb814157 100644
--- a/drivers/net/epic100.c
+++ b/drivers/net/ethernet/smsc/epic100.c
@@ -314,7 +314,7 @@ static const struct net_device_ops epic_netdev_ops = {
.ndo_start_xmit = epic_start_xmit,
.ndo_tx_timeout = epic_tx_timeout,
.ndo_get_stats = epic_get_stats,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_do_ioctl = netdev_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
@@ -391,13 +391,13 @@ static int __devinit epic_init_one (struct pci_dev *pdev,
ring_space = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &ring_dma);
if (!ring_space)
goto err_out_iounmap;
- ep->tx_ring = (struct epic_tx_desc *)ring_space;
+ ep->tx_ring = ring_space;
ep->tx_ring_dma = ring_dma;
ring_space = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &ring_dma);
if (!ring_space)
goto err_out_unmap_tx;
- ep->rx_ring = (struct epic_rx_desc *)ring_space;
+ ep->rx_ring = ring_space;
ep->rx_ring_dma = ring_dma;
if (dev->mem_start) {
diff --git a/drivers/net/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c
index 053863aefb12..8f61fe9db1d0 100644
--- a/drivers/net/smc911x.c
+++ b/drivers/net/ethernet/smsc/smc911x.c
@@ -1351,11 +1351,6 @@ static void smc911x_set_multicast_list(struct net_device *dev)
netdev_for_each_mc_addr(ha, dev) {
u32 position;
- /* make sure this is a multicast address -
- shouldn't this be a given if we have it here ? */
- if (!(*ha->addr & 1))
- continue;
-
/* upper 6 bits are used as hash index */
position = ether_crc(ETH_ALEN, ha->addr)>>26;
@@ -1773,7 +1768,7 @@ static const struct net_device_ops smc911x_netdev_ops = {
.ndo_stop = smc911x_close,
.ndo_start_xmit = smc911x_hard_start_xmit,
.ndo_tx_timeout = smc911x_timeout,
- .ndo_set_multicast_list = smc911x_set_multicast_list,
+ .ndo_set_rx_mode = smc911x_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/smc911x.h b/drivers/net/ethernet/smsc/smc911x.h
index 3269292efecc..3269292efecc 100644
--- a/drivers/net/smc911x.h
+++ b/drivers/net/ethernet/smsc/smc911x.h
diff --git a/drivers/net/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c
index 7486d0908064..4e45094efb18 100644
--- a/drivers/net/smc9194.c
+++ b/drivers/net/ethernet/smsc/smc9194.c
@@ -447,11 +447,6 @@ static void smc_setmulticast(int ioaddr, struct net_device *dev)
netdev_for_each_mc_addr(ha, dev) {
int position;
- /* make sure this is a multicast address - shouldn't this
- be a given if we have it here ? */
- if (!(*ha->addr & 1))
- continue;
-
/* only use the low order bits */
position = ether_crc_le(6, ha->addr) & 0x3f;
@@ -832,7 +827,7 @@ static const struct net_device_ops smc_netdev_ops = {
.ndo_stop = smc_close,
.ndo_start_xmit = smc_wait_to_send_packet,
.ndo_tx_timeout = smc_timeout,
- .ndo_set_multicast_list = smc_set_multicast_list,
+ .ndo_set_rx_mode = smc_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/smc9194.h b/drivers/net/ethernet/smsc/smc9194.h
index cf69d0a5a1cb..cf69d0a5a1cb 100644
--- a/drivers/net/smc9194.h
+++ b/drivers/net/ethernet/smsc/smc9194.h
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c
index 1cd9394c3359..cbfa98187131 100644
--- a/drivers/net/pcmcia/smc91c92_cs.c
+++ b/drivers/net/ethernet/smsc/smc91c92_cs.c
@@ -294,7 +294,7 @@ static const struct net_device_ops smc_netdev_ops = {
.ndo_start_xmit = smc_start_xmit,
.ndo_tx_timeout = smc_tx_timeout,
.ndo_set_config = s9k_config,
- .ndo_set_multicast_list = set_rx_mode,
+ .ndo_set_rx_mode = set_rx_mode,
.ndo_do_ioctl = smc_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
@@ -809,7 +809,7 @@ static int smc91c92_config(struct pcmcia_device *link)
struct net_device *dev = link->priv;
struct smc_private *smc = netdev_priv(dev);
char *name;
- int i, j, rev;
+ int i, rev, j = 0;
unsigned int ioaddr;
u_long mir;
diff --git a/drivers/net/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index f6285748bd3c..f47f81e25322 100644
--- a/drivers/net/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -1425,11 +1425,6 @@ static void smc_set_multicast_list(struct net_device *dev)
netdev_for_each_mc_addr(ha, dev) {
int position;
- /* make sure this is a multicast address -
- shouldn't this be a given if we have it here ? */
- if (!(*ha->addr & 1))
- continue;
-
/* only use the low order bits */
position = crc32_le(~0, ha->addr, 6) & 0x3f;
@@ -1773,7 +1768,7 @@ static const struct net_device_ops smc_netdev_ops = {
.ndo_stop = smc_close,
.ndo_start_xmit = smc_hard_start_xmit,
.ndo_tx_timeout = smc_timeout,
- .ndo_set_multicast_list = smc_set_multicast_list,
+ .ndo_set_rx_mode = smc_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h
index 5f53fbbf67be..5f53fbbf67be 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/ethernet/smsc/smc91x.h
diff --git a/drivers/net/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index c6d47d10590c..8843071fe987 100644
--- a/drivers/net/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -26,6 +26,7 @@
* LAN9215, LAN9216, LAN9217, LAN9218
* LAN9210, LAN9211
* LAN9220, LAN9221
+ * LAN89218
*
*/
@@ -37,6 +38,7 @@
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -52,6 +54,10 @@
#include <linux/phy.h>
#include <linux/smsc911x.h>
#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_net.h>
#include "smsc911x.h"
#define SMSC_CHIPNAME "smsc911x"
@@ -1473,6 +1479,7 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
pdata->ops->tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
freespace -= (skb->len + 32);
+ skb_tx_timestamp(skb);
dev_kfree_skb(skb);
if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
@@ -1900,7 +1907,7 @@ static const struct net_device_ops smsc911x_netdev_ops = {
.ndo_stop = smsc911x_stop,
.ndo_start_xmit = smsc911x_hard_start_xmit,
.ndo_get_stats = smsc911x_get_stats,
- .ndo_set_multicast_list = smsc911x_set_multicast_list,
+ .ndo_set_rx_mode = smsc911x_set_multicast_list,
.ndo_do_ioctl = smsc911x_do_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
@@ -1930,6 +1937,7 @@ static int __devinit smsc911x_init(struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
unsigned int byte_test;
+ unsigned int to = 100;
SMSC_TRACE(pdata, probe, "Driver Parameters:");
SMSC_TRACE(pdata, probe, "LAN base: 0x%08lX",
@@ -1945,6 +1953,17 @@ static int __devinit smsc911x_init(struct net_device *dev)
return -ENODEV;
}
+ /*
+ * poll the READY bit in PMT_CTRL. Any other access to the device is
+ * forbidden while this bit isn't set. Try for 100ms
+ */
+ while (!(smsc911x_reg_read(pdata, PMT_CTRL) & PMT_CTRL_READY_) && --to)
+ udelay(1000);
+ if (to == 0) {
+ pr_err("Device not READY in 100ms aborting\n");
+ return -ENODEV;
+ }
+
/* Check byte ordering */
byte_test = smsc911x_reg_read(pdata, BYTE_TEST);
SMSC_TRACE(pdata, probe, "BYTE_TEST: 0x%08X", byte_test);
@@ -1981,6 +2000,7 @@ static int __devinit smsc911x_init(struct net_device *dev)
case 0x01170000:
case 0x01160000:
case 0x01150000:
+ case 0x218A0000:
/* LAN911[5678] family */
pdata->generation = pdata->idrev & 0x0000FFFF;
break;
@@ -2093,8 +2113,60 @@ static const struct smsc911x_ops shifted_smsc911x_ops = {
.tx_writefifo = smsc911x_tx_writefifo_shift,
};
+#ifdef CONFIG_OF
+static int __devinit smsc911x_probe_config_dt(
+ struct smsc911x_platform_config *config,
+ struct device_node *np)
+{
+ const char *mac;
+ u32 width = 0;
+
+ if (!np)
+ return -ENODEV;
+
+ config->phy_interface = of_get_phy_mode(np);
+
+ mac = of_get_mac_address(np);
+ if (mac)
+ memcpy(config->mac, mac, ETH_ALEN);
+
+ of_property_read_u32(np, "reg-shift", &config->shift);
+
+ of_property_read_u32(np, "reg-io-width", &width);
+ if (width == 4)
+ config->flags |= SMSC911X_USE_32BIT;
+ else
+ config->flags |= SMSC911X_USE_16BIT;
+
+ if (of_get_property(np, "smsc,irq-active-high", NULL))
+ config->irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH;
+
+ if (of_get_property(np, "smsc,irq-push-pull", NULL))
+ config->irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL;
+
+ if (of_get_property(np, "smsc,force-internal-phy", NULL))
+ config->flags |= SMSC911X_FORCE_INTERNAL_PHY;
+
+ if (of_get_property(np, "smsc,force-external-phy", NULL))
+ config->flags |= SMSC911X_FORCE_EXTERNAL_PHY;
+
+ if (of_get_property(np, "smsc,save-mac-address", NULL))
+ config->flags |= SMSC911X_SAVE_MAC_ADDRESS;
+
+ return 0;
+}
+#else
+static inline int smsc911x_probe_config_dt(
+ struct smsc911x_platform_config *config,
+ struct device_node *np)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_OF */
+
static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct net_device *dev;
struct smsc911x_data *pdata;
struct smsc911x_platform_config *config = pdev->dev.platform_data;
@@ -2105,13 +2177,6 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
pr_info("Driver version %s\n", SMSC_DRV_VERSION);
- /* platform data specifies irq & dynamic bus configuration */
- if (!pdev->dev.platform_data) {
- pr_warn("platform_data not provided\n");
- retval = -ENODEV;
- goto out_0;
- }
-
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"smsc911x-memory");
if (!res)
@@ -2150,9 +2215,6 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
irq_flags = irq_res->flags & IRQF_TRIGGER_MASK;
pdata->ioaddr = ioremap_nocache(res->start, res_size);
- /* copy config parameters across to pdata */
- memcpy(&pdata->config, config, sizeof(pdata->config));
-
pdata->dev = dev;
pdata->msg_enable = ((1 << debug) - 1);
@@ -2162,10 +2224,22 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
goto out_free_netdev_2;
}
+ retval = smsc911x_probe_config_dt(&pdata->config, np);
+ if (retval && config) {
+ /* copy config parameters across to pdata */
+ memcpy(&pdata->config, config, sizeof(pdata->config));
+ retval = 0;
+ }
+
+ if (retval) {
+ SMSC_WARN(pdata, probe, "Error smsc911x config not found");
+ goto out_unmap_io_3;
+ }
+
/* assume standard, non-shifted, access to HW registers */
pdata->ops = &standard_smsc911x_ops;
/* apply the right access if shifting is needed */
- if (config->shift)
+ if (pdata->config.shift)
pdata->ops = &shifted_smsc911x_ops;
retval = smsc911x_init(dev);
@@ -2312,6 +2386,12 @@ static const struct dev_pm_ops smsc911x_pm_ops = {
#define SMSC911X_PM_OPS NULL
#endif
+static const struct of_device_id smsc911x_dt_ids[] = {
+ { .compatible = "smsc,lan9115", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, smsc911x_dt_ids);
+
static struct platform_driver smsc911x_driver = {
.probe = smsc911x_drv_probe,
.remove = __devexit_p(smsc911x_drv_remove),
@@ -2319,6 +2399,7 @@ static struct platform_driver smsc911x_driver = {
.name = SMSC_CHIPNAME,
.owner = THIS_MODULE,
.pm = SMSC911X_PM_OPS,
+ .of_match_table = smsc911x_dt_ids,
},
};
diff --git a/drivers/net/smsc911x.h b/drivers/net/ethernet/smsc/smsc911x.h
index 8d67aacf8867..8d67aacf8867 100644
--- a/drivers/net/smsc911x.h
+++ b/drivers/net/ethernet/smsc/smsc911x.h
diff --git a/drivers/net/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c
index 4c92ad8be765..edb24b0e337b 100644
--- a/drivers/net/smsc9420.c
+++ b/drivers/net/ethernet/smsc/smsc9420.c
@@ -19,6 +19,7 @@
***************************************************************************
*/
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
@@ -27,6 +28,7 @@
#include <linux/dma-mapping.h>
#include <linux/crc32.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <asm/unaligned.h>
#include "smsc9420.h"
@@ -1030,6 +1032,8 @@ static netdev_tx_t smsc9420_hard_start_xmit(struct sk_buff *skb,
pd->tx_ring[index].status = TDES0_OWN_;
wmb();
+ skb_tx_timestamp(skb);
+
/* kick the DMA */
smsc9420_reg_write(pd, TX_POLL_DEMAND, 1);
smsc9420_pci_flush_write(pd);
@@ -1563,7 +1567,7 @@ static const struct net_device_ops smsc9420_netdev_ops = {
.ndo_stop = smsc9420_stop,
.ndo_start_xmit = smsc9420_hard_start_xmit,
.ndo_get_stats = smsc9420_get_stats,
- .ndo_set_multicast_list = smsc9420_set_multicast_list,
+ .ndo_set_rx_mode = smsc9420_set_multicast_list,
.ndo_do_ioctl = smsc9420_do_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/smsc9420.h b/drivers/net/ethernet/smsc/smsc9420.h
index e441402f77a2..e441402f77a2 100644
--- a/drivers/net/smsc9420.h
+++ b/drivers/net/ethernet/smsc/smsc9420.h
diff --git a/drivers/net/ethernet/stmicro/Kconfig b/drivers/net/ethernet/stmicro/Kconfig
new file mode 100644
index 000000000000..f4a80da00650
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/Kconfig
@@ -0,0 +1,23 @@
+#
+# STMicroelectronics device configuration
+#
+
+config NET_VENDOR_STMICRO
+ bool "STMicroelectronics devices"
+ default y
+ depends on HAS_IOMEM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about STMicroelectronics cards. If you say Y, you will
+ be asked for your specific card in the following questions.
+
+if NET_VENDOR_STMICRO
+
+source "drivers/net/ethernet/stmicro/stmmac/Kconfig"
+
+endif # NET_VENDOR_STMICRO
diff --git a/drivers/net/ethernet/stmicro/Makefile b/drivers/net/ethernet/stmicro/Makefile
new file mode 100644
index 000000000000..9b3bfddda7dd
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the STMicroelectronics device drivers.
+#
+
+obj-$(CONFIG_STMMAC_ETH) += stmmac/
diff --git a/drivers/net/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 7df7df4e79c5..22745d7bf530 100644
--- a/drivers/net/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -1,39 +1,39 @@
config STMMAC_ETH
tristate "STMicroelectronics 10/100/1000 Ethernet driver"
+ depends on HAS_IOMEM
+ select NET_CORE
select MII
select PHYLIB
select CRC32
- depends on NETDEVICES && HAS_IOMEM
- help
+ ---help---
This is the driver for the Ethernet IPs are built around a
Synopsys IP Core and only tested on the STMicroelectronics
platforms.
if STMMAC_ETH
+config STMMAC_DEBUG_FS
+ bool "Enable monitoring via sysFS "
+ default n
+ depends on STMMAC_ETH && DEBUG_FS
+ -- help
+ The stmmac entry in /sys reports DMA TX/RX rings
+ or (if supported) the HW cap register.
+
config STMMAC_DA
bool "STMMAC DMA arbitration scheme"
default n
- help
+ ---help---
Selecting this option, rx has priority over Tx (only for Giga
Ethernet device).
By default, the DMA arbitration scheme is based on Round-robin
(rx:tx priority is 1:1).
-config STMMAC_DUAL_MAC
- bool "STMMAC: dual mac support (EXPERIMENTAL)"
- default n
- depends on EXPERIMENTAL && STMMAC_ETH && !STMMAC_TIMER
- help
- Some ST SoCs (for example the stx7141 and stx7200c2) have two
- Ethernet Controllers. This option turns on the second Ethernet
- device on this kind of platforms.
-
config STMMAC_TIMER
bool "STMMAC Timer optimisation"
default n
depends on RTC_HCTOSYS_DEVICE
- help
+ ---help---
Use an external timer for mitigating the number of network
interrupts. Currently, for SH architectures, it is possible
to use the TMU channel 2 and the SH-RTC device.
@@ -45,13 +45,31 @@ choice
config STMMAC_TMU_TIMER
bool "TMU channel 2"
depends on CPU_SH4
- help
+ ---help---
config STMMAC_RTC_TIMER
bool "Real time clock"
depends on RTC_CLASS
- help
+ ---help---
endchoice
+choice
+ prompt "Select the DMA TX/RX descriptor operating modes"
+ depends on STMMAC_ETH
+ ---help---
+ This driver supports DMA descriptor to operate both in dual buffer
+ (RING) and linked-list(CHAINED) mode. In RING mode each descriptor
+ points to two data buffer pointers whereas in CHAINED mode they
+ points to only one data buffer pointer.
+
+config STMMAC_RING
+ bool "Enable Descriptor Ring Mode"
+
+config STMMAC_CHAINED
+ bool "Enable Descriptor Chained Mode"
+
+endchoice
+
+
endif
diff --git a/drivers/net/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index 9691733ddb8e..d7c45164ea79 100644
--- a/drivers/net/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -1,5 +1,8 @@
obj-$(CONFIG_STMMAC_ETH) += stmmac.o
stmmac-$(CONFIG_STMMAC_TIMER) += stmmac_timer.o
+stmmac-$(CONFIG_STMMAC_RING) += ring_mode.o
+stmmac-$(CONFIG_STMMAC_CHAINED) += chain_mode.o
stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o \
dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
- dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o $(stmmac-y)
+ dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
+ mmc_core.o $(stmmac-y)
diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
new file mode 100644
index 000000000000..0668659803ed
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ Specialised functions for managing Chained mode
+
+ Copyright(C) 2011 STMicroelectronics Ltd
+
+ It defines all the functions used to handle the normal/enhanced
+ descriptors in case of the DMA is configured to work in chained or
+ in ring mode.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+*******************************************************************************/
+
+#include "stmmac.h"
+
+unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
+{
+ struct stmmac_priv *priv = (struct stmmac_priv *) p;
+ unsigned int txsize = priv->dma_tx_size;
+ unsigned int entry = priv->cur_tx % txsize;
+ struct dma_desc *desc = priv->dma_tx + entry;
+ unsigned int nopaged_len = skb_headlen(skb);
+ unsigned int bmax;
+ unsigned int i = 1, len;
+
+ if (priv->plat->enh_desc)
+ bmax = BUF_SIZE_8KiB;
+ else
+ bmax = BUF_SIZE_2KiB;
+
+ len = nopaged_len - bmax;
+
+ desc->des2 = dma_map_single(priv->device, skb->data,
+ bmax, DMA_TO_DEVICE);
+ priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum);
+
+ while (len != 0) {
+ entry = (++priv->cur_tx) % txsize;
+ desc = priv->dma_tx + entry;
+
+ if (len > bmax) {
+ desc->des2 = dma_map_single(priv->device,
+ (skb->data + bmax * i),
+ bmax, DMA_TO_DEVICE);
+ priv->hw->desc->prepare_tx_desc(desc, 0, bmax,
+ csum);
+ priv->hw->desc->set_tx_owner(desc);
+ priv->tx_skbuff[entry] = NULL;
+ len -= bmax;
+ i++;
+ } else {
+ desc->des2 = dma_map_single(priv->device,
+ (skb->data + bmax * i), len,
+ DMA_TO_DEVICE);
+ priv->hw->desc->prepare_tx_desc(desc, 0, len,
+ csum);
+ priv->hw->desc->set_tx_owner(desc);
+ priv->tx_skbuff[entry] = NULL;
+ len = 0;
+ }
+ }
+ return entry;
+}
+
+static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
+{
+ unsigned int ret = 0;
+
+ if ((enh_desc && (len > BUF_SIZE_8KiB)) ||
+ (!enh_desc && (len > BUF_SIZE_2KiB))) {
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static void stmmac_refill_desc3(int bfsize, struct dma_desc *p)
+{
+}
+
+static void stmmac_init_desc3(int des3_as_data_buf, struct dma_desc *p)
+{
+}
+
+static void stmmac_clean_desc3(struct dma_desc *p)
+{
+}
+
+static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr,
+ unsigned int size)
+{
+ /*
+ * In chained mode the des3 points to the next element in the ring.
+ * The latest element has to point to the head.
+ */
+ int i;
+ struct dma_desc *p = des;
+ dma_addr_t dma_phy = phy_addr;
+
+ for (i = 0; i < (size - 1); i++) {
+ dma_phy += sizeof(struct dma_desc);
+ p->des3 = (unsigned int)dma_phy;
+ p++;
+ }
+ p->des3 = (unsigned int)phy_addr;
+}
+
+static int stmmac_set_16kib_bfsize(int mtu)
+{
+ /* Not supported */
+ return 0;
+}
+
+const struct stmmac_ring_mode_ops ring_mode_ops = {
+ .is_jumbo_frm = stmmac_is_jumbo_frm,
+ .jumbo_frm = stmmac_jumbo_frm,
+ .refill_desc3 = stmmac_refill_desc3,
+ .init_desc3 = stmmac_init_desc3,
+ .init_dma_chain = stmmac_init_dma_chain,
+ .clean_desc3 = stmmac_clean_desc3,
+ .set_16kib_bfsize = stmmac_set_16kib_bfsize,
+};
diff --git a/drivers/net/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 375ea193e139..2cc119295821 100644
--- a/drivers/net/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -29,6 +29,7 @@
#endif
#include "descs.h"
+#include "mmc.h"
#undef CHIP_DEBUG_PRINT
/* Turn-on extra printk debug for MAC core, dma and descriptors */
@@ -48,7 +49,7 @@ struct stmmac_extra_stats {
unsigned long tx_underflow ____cacheline_aligned;
unsigned long tx_carrier;
unsigned long tx_losscarrier;
- unsigned long tx_heartbeat;
+ unsigned long vlan_tag;
unsigned long tx_deferred;
unsigned long tx_vlan;
unsigned long tx_jabber;
@@ -57,9 +58,9 @@ struct stmmac_extra_stats {
unsigned long tx_ip_header_error;
/* Receive errors */
unsigned long rx_desc;
- unsigned long rx_partial;
- unsigned long rx_runt;
- unsigned long rx_toolong;
+ unsigned long sa_filter_fail;
+ unsigned long overflow_error;
+ unsigned long ipc_csum_error;
unsigned long rx_collision;
unsigned long rx_crc;
unsigned long rx_length;
@@ -102,6 +103,36 @@ struct stmmac_extra_stats {
#define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */
+/* DAM HW feature register fields */
+#define DMA_HW_FEAT_MIISEL 0x00000001 /* 10/100 Mbps Support */
+#define DMA_HW_FEAT_GMIISEL 0x00000002 /* 1000 Mbps Support */
+#define DMA_HW_FEAT_HDSEL 0x00000004 /* Half-Duplex Support */
+#define DMA_HW_FEAT_EXTHASHEN 0x00000008 /* Expanded DA Hash Filter */
+#define DMA_HW_FEAT_HASHSEL 0x00000010 /* HASH Filter */
+#define DMA_HW_FEAT_ADDMACADRSEL 0x00000020 /* Multiple MAC Addr Reg */
+#define DMA_HW_FEAT_PCSSEL 0x00000040 /* PCS registers */
+#define DMA_HW_FEAT_L3L4FLTREN 0x00000080 /* Layer 3 & Layer 4 Feature */
+#define DMA_HW_FEAT_SMASEL 0x00000100 /* SMA(MDIO) Interface */
+#define DMA_HW_FEAT_RWKSEL 0x00000200 /* PMT Remote Wakeup */
+#define DMA_HW_FEAT_MGKSEL 0x00000400 /* PMT Magic Packet */
+#define DMA_HW_FEAT_MMCSEL 0x00000800 /* RMON Module */
+#define DMA_HW_FEAT_TSVER1SEL 0x00001000 /* Only IEEE 1588-2002 Timestamp */
+#define DMA_HW_FEAT_TSVER2SEL 0x00002000 /* IEEE 1588-2008 Adv Timestamp */
+#define DMA_HW_FEAT_EEESEL 0x00004000 /* Energy Efficient Ethernet */
+#define DMA_HW_FEAT_AVSEL 0x00008000 /* AV Feature */
+#define DMA_HW_FEAT_TXCOESEL 0x00010000 /* Checksum Offload in Tx */
+#define DMA_HW_FEAT_RXTYP1COE 0x00020000 /* IP csum Offload(Type 1) in Rx */
+#define DMA_HW_FEAT_RXTYP2COE 0x00040000 /* IP csum Offload(Type 2) in Rx */
+#define DMA_HW_FEAT_RXFIFOSIZE 0x00080000 /* Rx FIFO > 2048 Bytes */
+#define DMA_HW_FEAT_RXCHCNT 0x00300000 /* No. of additional Rx Channels */
+#define DMA_HW_FEAT_TXCHCNT 0x00c00000 /* No. of additional Tx Channels */
+#define DMA_HW_FEAT_ENHDESSEL 0x01000000 /* Alternate (Enhanced Descriptor) */
+#define DMA_HW_FEAT_INTTSEN 0x02000000 /* Timestamping with Internal
+ System Time */
+#define DMA_HW_FEAT_FLEXIPPSEN 0x04000000 /* Flexible PPS Output */
+#define DMA_HW_FEAT_SAVLANINS 0x08000000 /* Source Addr or VLAN Insertion */
+#define DMA_HW_FEAT_ACTPHYIF 0x70000000 /* Active/selected PHY interface */
+
enum rx_frame_status { /* IPC status */
good_frame = 0,
discard_frame = 1,
@@ -115,6 +146,37 @@ enum tx_dma_irq_status {
handle_tx_rx = 3,
};
+/* DMA HW capabilities */
+struct dma_features {
+ unsigned int mbps_10_100;
+ unsigned int mbps_1000;
+ unsigned int half_duplex;
+ unsigned int hash_filter;
+ unsigned int multi_addr;
+ unsigned int pcs;
+ unsigned int sma_mdio;
+ unsigned int pmt_remote_wake_up;
+ unsigned int pmt_magic_frame;
+ unsigned int rmon;
+ /* IEEE 1588-2002*/
+ unsigned int time_stamp;
+ /* IEEE 1588-2008*/
+ unsigned int atime_stamp;
+ /* 802.3az - Energy-Efficient Ethernet (EEE) */
+ unsigned int eee;
+ unsigned int av;
+ /* TX and RX csum */
+ unsigned int tx_coe;
+ unsigned int rx_coe_type1;
+ unsigned int rx_coe_type2;
+ unsigned int rxfifo_over_2048;
+ /* TX and RX number of channels */
+ unsigned int number_rx_channel;
+ unsigned int number_tx_channel;
+ /* Alternate (enhanced) DESC mode*/
+ unsigned int enh_desc;
+};
+
/* GMAC TX FIFO is 8K, Rx FIFO is 16K */
#define BUF_SIZE_16KiB 16384
#define BUF_SIZE_8KiB 8192
@@ -130,17 +192,6 @@ enum tx_dma_irq_status {
#define MAC_ENABLE_TX 0x00000008 /* Transmitter Enable */
#define MAC_RNABLE_RX 0x00000004 /* Receiver Enable */
-/* MAC Management Counters register */
-#define MMC_CONTROL 0x00000100 /* MMC Control */
-#define MMC_HIGH_INTR 0x00000104 /* MMC High Interrupt */
-#define MMC_LOW_INTR 0x00000108 /* MMC Low Interrupt */
-#define MMC_HIGH_INTR_MASK 0x0000010c /* MMC High Interrupt Mask */
-#define MMC_LOW_INTR_MASK 0x00000110 /* MMC Low Interrupt Mask */
-
-#define MMC_CONTROL_MAX_FRM_MASK 0x0003ff8 /* Maximum Frame Size */
-#define MMC_CONTROL_MAX_FRM_SHIFT 3
-#define MMC_CONTROL_MAX_FRAME 0x7FF
-
struct stmmac_desc_ops {
/* DMA RX descriptor ring initialization */
void (*init_rx_desc) (struct dma_desc *p, unsigned int ring_size,
@@ -198,6 +249,8 @@ struct stmmac_dma_ops {
void (*stop_rx) (void __iomem *ioaddr);
int (*dma_interrupt) (void __iomem *ioaddr,
struct stmmac_extra_stats *x);
+ /* If supported then get the optional core features */
+ unsigned int (*get_hw_feature) (void __iomem *ioaddr);
};
struct stmmac_ops {
@@ -234,12 +287,25 @@ struct mii_regs {
unsigned int data; /* MII Data */
};
+struct stmmac_ring_mode_ops {
+ unsigned int (*is_jumbo_frm) (int len, int ehn_desc);
+ unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum);
+ void (*refill_desc3) (int bfsize, struct dma_desc *p);
+ void (*init_desc3) (int des3_as_data_buf, struct dma_desc *p);
+ void (*init_dma_chain) (struct dma_desc *des, dma_addr_t phy_addr,
+ unsigned int size);
+ void (*clean_desc3) (struct dma_desc *p);
+ int (*set_16kib_bfsize) (int mtu);
+};
+
struct mac_device_info {
const struct stmmac_ops *mac;
const struct stmmac_desc_ops *desc;
const struct stmmac_dma_ops *dma;
+ const struct stmmac_ring_mode_ops *ring;
struct mii_regs mii; /* MII register Addresses */
struct mac_link link;
+ unsigned int synopsys_uid;
};
struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr);
@@ -250,3 +316,4 @@ extern void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6],
extern void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int high, unsigned int low);
extern void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);
+extern const struct stmmac_ring_mode_ops ring_mode_ops;
diff --git a/drivers/net/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h
index 63a03e264694..9820ec842cc0 100644
--- a/drivers/net/stmmac/descs.h
+++ b/drivers/net/ethernet/stmicro/stmmac/descs.h
@@ -25,33 +25,34 @@ struct dma_desc {
union {
struct {
/* RDES0 */
- u32 reserved1:1;
+ u32 payload_csum_error:1;
u32 crc_error:1;
u32 dribbling:1;
u32 mii_error:1;
u32 receive_watchdog:1;
u32 frame_type:1;
u32 collision:1;
- u32 frame_too_long:1;
+ u32 ipc_csum_error:1;
u32 last_descriptor:1;
u32 first_descriptor:1;
- u32 multicast_frame:1;
- u32 run_frame:1;
+ u32 vlan_tag:1;
+ u32 overflow_error:1;
u32 length_error:1;
- u32 partial_frame_error:1;
+ u32 sa_filter_fail:1;
u32 descriptor_error:1;
u32 error_summary:1;
u32 frame_length:14;
- u32 filtering_fail:1;
+ u32 da_filter_fail:1;
u32 own:1;
/* RDES1 */
u32 buffer1_size:11;
u32 buffer2_size:11;
- u32 reserved2:2;
+ u32 reserved1:2;
u32 second_address_chained:1;
u32 end_ring:1;
- u32 reserved3:5;
+ u32 reserved2:5;
u32 disable_ic:1;
+
} rx;
struct {
/* RDES0 */
@@ -91,24 +92,28 @@ struct dma_desc {
u32 underflow_error:1;
u32 excessive_deferral:1;
u32 collision_count:4;
- u32 heartbeat_fail:1;
+ u32 vlan_frame:1;
u32 excessive_collisions:1;
u32 late_collision:1;
u32 no_carrier:1;
u32 loss_carrier:1;
- u32 reserved1:3;
+ u32 payload_error:1;
+ u32 frame_flushed:1;
+ u32 jabber_timeout:1;
u32 error_summary:1;
- u32 reserved2:15;
+ u32 ip_header_error:1;
+ u32 time_stamp_status:1;
+ u32 reserved1:13;
u32 own:1;
/* TDES1 */
u32 buffer1_size:11;
u32 buffer2_size:11;
- u32 reserved3:1;
+ u32 time_stamp_enable:1;
u32 disable_padding:1;
u32 second_address_chained:1;
u32 end_ring:1;
u32 crc_disable:1;
- u32 reserved4:2;
+ u32 checksum_insertion:2;
u32 first_segment:1;
u32 last_segment:1;
u32 interrupt:1;
diff --git a/drivers/net/ethernet/stmicro/stmmac/descs_com.h b/drivers/net/ethernet/stmicro/stmmac/descs_com.h
new file mode 100644
index 000000000000..dd8d6e19dff6
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/descs_com.h
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ Header File to describe Normal/enhanced descriptor functions used for RING
+ and CHAINED modes.
+
+ Copyright(C) 2011 STMicroelectronics Ltd
+
+ It defines all the functions used to handle the normal/enhanced
+ descriptors in case of the DMA is configured to work in chained or
+ in ring mode.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+*******************************************************************************/
+
+#if defined(CONFIG_STMMAC_RING)
+static inline void ehn_desc_rx_set_on_ring_chain(struct dma_desc *p, int end)
+{
+ p->des01.erx.buffer2_size = BUF_SIZE_8KiB - 1;
+ if (end)
+ p->des01.erx.end_ring = 1;
+}
+
+static inline void ehn_desc_tx_set_on_ring_chain(struct dma_desc *p, int end)
+{
+ if (end)
+ p->des01.etx.end_ring = 1;
+}
+
+static inline void enh_desc_end_tx_desc(struct dma_desc *p, int ter)
+{
+ p->des01.etx.end_ring = ter;
+}
+
+static inline void enh_set_tx_desc_len(struct dma_desc *p, int len)
+{
+ if (unlikely(len > BUF_SIZE_4KiB)) {
+ p->des01.etx.buffer1_size = BUF_SIZE_4KiB;
+ p->des01.etx.buffer2_size = len - BUF_SIZE_4KiB;
+ } else
+ p->des01.etx.buffer1_size = len;
+}
+
+static inline void ndesc_rx_set_on_ring_chain(struct dma_desc *p, int end)
+{
+ p->des01.rx.buffer2_size = BUF_SIZE_2KiB - 1;
+ if (end)
+ p->des01.rx.end_ring = 1;
+}
+
+static inline void ndesc_tx_set_on_ring_chain(struct dma_desc *p, int end)
+{
+ if (end)
+ p->des01.tx.end_ring = 1;
+}
+
+static inline void ndesc_end_tx_desc(struct dma_desc *p, int ter)
+{
+ p->des01.tx.end_ring = ter;
+}
+
+static inline void norm_set_tx_desc_len(struct dma_desc *p, int len)
+{
+ if (unlikely(len > BUF_SIZE_2KiB)) {
+ p->des01.etx.buffer1_size = BUF_SIZE_2KiB - 1;
+ p->des01.etx.buffer2_size = len - p->des01.etx.buffer1_size;
+ } else
+ p->des01.tx.buffer1_size = len;
+}
+
+#else
+
+static inline void ehn_desc_rx_set_on_ring_chain(struct dma_desc *p, int end)
+{
+ p->des01.erx.second_address_chained = 1;
+}
+
+static inline void ehn_desc_tx_set_on_ring_chain(struct dma_desc *p, int end)
+{
+ p->des01.etx.second_address_chained = 1;
+}
+
+static inline void enh_desc_end_tx_desc(struct dma_desc *p, int ter)
+{
+ p->des01.etx.second_address_chained = 1;
+}
+
+static inline void enh_set_tx_desc_len(struct dma_desc *p, int len)
+{
+ p->des01.etx.buffer1_size = len;
+}
+
+static inline void ndesc_rx_set_on_ring_chain(struct dma_desc *p, int end)
+{
+ p->des01.rx.second_address_chained = 1;
+}
+
+static inline void ndesc_tx_set_on_ring_chain(struct dma_desc *p, int ring_size)
+{
+ p->des01.tx.second_address_chained = 1;
+}
+
+static inline void ndesc_end_tx_desc(struct dma_desc *p, int ter)
+{
+ p->des01.tx.second_address_chained = 1;
+}
+
+static inline void norm_set_tx_desc_len(struct dma_desc *p, int len)
+{
+ p->des01.tx.buffer1_size = len;
+}
+#endif
diff --git a/drivers/net/stmmac/dwmac100.h b/drivers/net/ethernet/stmicro/stmmac/dwmac100.h
index 7c6d857a9cc7..7c6d857a9cc7 100644
--- a/drivers/net/stmmac/dwmac100.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100.h
diff --git a/drivers/net/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index cfcef0ea0fa5..cfcef0ea0fa5 100644
--- a/drivers/net/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
diff --git a/drivers/net/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index f20455cbfbbc..b1c48b975945 100644
--- a/drivers/net/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -28,6 +28,7 @@
#include <linux/crc32.h>
#include <linux/slab.h>
+#include <asm/io.h>
#include "dwmac1000.h"
static void dwmac1000_core_init(void __iomem *ioaddr)
@@ -36,11 +37,6 @@ static void dwmac1000_core_init(void __iomem *ioaddr)
value |= GMAC_CORE_INIT;
writel(value, ioaddr + GMAC_CONTROL);
- /* STBus Bridge Configuration */
- /*writel(0xc5608, ioaddr + 0x00007000);*/
-
- /* Freeze MMC counters */
- writel(0x8, ioaddr + GMAC_MMC_CTRL);
/* Mask GMAC interrupts */
writel(0x207, ioaddr + GMAC_INT_MASK);
@@ -228,10 +224,7 @@ static const struct stmmac_ops dwmac1000_ops = {
struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr)
{
struct mac_device_info *mac;
- u32 uid = readl(ioaddr + GMAC_VERSION);
-
- pr_info("\tDWMAC1000 - user ID: 0x%x, Synopsys ID: 0x%x\n",
- ((uid & 0x0000ff00) >> 8), (uid & 0x000000ff));
+ u32 hwid = readl(ioaddr + GMAC_VERSION);
mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL);
if (!mac)
@@ -245,6 +238,7 @@ struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr)
mac->link.speed = GMAC_CONTROL_FES;
mac->mii.addr = GMAC_MII_ADDR;
mac->mii.data = GMAC_MII_DATA;
+ mac->synopsys_uid = hwid;
return mac;
}
diff --git a/drivers/net/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index 2c47712d45d0..4d5402a1d262 100644
--- a/drivers/net/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -26,6 +26,7 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#include <asm/io.h>
#include "dwmac1000.h"
#include "dwmac_dma.h"
@@ -38,10 +39,11 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, u32 dma_tx,
/* DMA SW reset */
value |= DMA_BUS_MODE_SFT_RESET;
writel(value, ioaddr + DMA_BUS_MODE);
- limit = 15000;
+ limit = 10;
while (limit--) {
if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
break;
+ mdelay(10);
}
if (limit < 0)
return -EBUSY;
@@ -117,13 +119,6 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
writel(csr6, ioaddr + DMA_CONTROL);
}
-/* Not yet implemented --- no RMON module */
-static void dwmac1000_dma_diagnostic_fr(void *data,
- struct stmmac_extra_stats *x, void __iomem *ioaddr)
-{
- return;
-}
-
static void dwmac1000_dump_dma_regs(void __iomem *ioaddr)
{
int i;
@@ -138,11 +133,15 @@ static void dwmac1000_dump_dma_regs(void __iomem *ioaddr)
}
}
+static unsigned int dwmac1000_get_hw_feature(void __iomem *ioaddr)
+{
+ return readl(ioaddr + DMA_HW_FEATURE);
+}
+
const struct stmmac_dma_ops dwmac1000_dma_ops = {
.init = dwmac1000_dma_init,
.dump_regs = dwmac1000_dump_dma_regs,
.dma_mode = dwmac1000_dma_operation_mode,
- .dma_diagnostic_fr = dwmac1000_dma_diagnostic_fr,
.enable_dma_transmission = dwmac_enable_dma_transmission,
.enable_dma_irq = dwmac_enable_dma_irq,
.disable_dma_irq = dwmac_disable_dma_irq,
@@ -151,4 +150,5 @@ const struct stmmac_dma_ops dwmac1000_dma_ops = {
.start_rx = dwmac_dma_start_rx,
.stop_rx = dwmac_dma_stop_rx,
.dma_interrupt = dwmac_dma_interrupt,
+ .get_hw_feature = dwmac1000_get_hw_feature,
};
diff --git a/drivers/net/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
index c724fc36a24f..138fb8dd1e87 100644
--- a/drivers/net/stmmac/dwmac100_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
@@ -29,6 +29,7 @@
*******************************************************************************/
#include <linux/crc32.h>
+#include <asm/io.h>
#include "dwmac100.h"
static void dwmac100_core_init(void __iomem *ioaddr)
@@ -69,17 +70,6 @@ static void dwmac100_dump_mac_regs(void __iomem *ioaddr)
readl(ioaddr + MAC_VLAN1));
pr_info("\tVLAN2 tag (offset 0x%x): 0x%08x\n", MAC_VLAN2,
readl(ioaddr + MAC_VLAN2));
- pr_info("\n\tMAC management counter registers\n");
- pr_info("\t MMC crtl (offset 0x%x): 0x%08x\n",
- MMC_CONTROL, readl(ioaddr + MMC_CONTROL));
- pr_info("\t MMC High Interrupt (offset 0x%x): 0x%08x\n",
- MMC_HIGH_INTR, readl(ioaddr + MMC_HIGH_INTR));
- pr_info("\t MMC Low Interrupt (offset 0x%x): 0x%08x\n",
- MMC_LOW_INTR, readl(ioaddr + MMC_LOW_INTR));
- pr_info("\t MMC High Interrupt Mask (offset 0x%x): 0x%08x\n",
- MMC_HIGH_INTR_MASK, readl(ioaddr + MMC_HIGH_INTR_MASK));
- pr_info("\t MMC Low Interrupt Mask (offset 0x%x): 0x%08x\n",
- MMC_LOW_INTR_MASK, readl(ioaddr + MMC_LOW_INTR_MASK));
}
static void dwmac100_irq_status(void __iomem *ioaddr)
@@ -198,6 +188,7 @@ struct mac_device_info *dwmac100_setup(void __iomem *ioaddr)
mac->link.speed = 0;
mac->mii.addr = MAC_MII_ADDR;
mac->mii.data = MAC_MII_DATA;
+ mac->synopsys_uid = 0;
return mac;
}
diff --git a/drivers/net/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
index e3e224b7d9e2..bc17fd08b55d 100644
--- a/drivers/net/stmmac/dwmac100_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
@@ -28,6 +28,7 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#include <asm/io.h>
#include "dwmac100.h"
#include "dwmac_dma.h"
@@ -40,10 +41,11 @@ static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, u32 dma_tx,
/* DMA SW reset */
value |= DMA_BUS_MODE_SFT_RESET;
writel(value, ioaddr + DMA_BUS_MODE);
- limit = 15000;
+ limit = 10;
while (limit--) {
if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
break;
+ mdelay(10);
}
if (limit < 0)
return -EBUSY;
diff --git a/drivers/net/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
index da3f5ccf83d3..437edacd602e 100644
--- a/drivers/net/stmmac/dwmac_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
@@ -34,6 +34,7 @@
#define DMA_MISSED_FRAME_CTR 0x00001020 /* Missed Frame Counter */
#define DMA_CUR_TX_BUF_ADDR 0x00001050 /* Current Host Tx Buffer */
#define DMA_CUR_RX_BUF_ADDR 0x00001054 /* Current Host Rx Buffer */
+#define DMA_HW_FEATURE 0x00001058 /* HW Feature Register */
/* DMA Control register defines */
#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */
diff --git a/drivers/net/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
index e25093510b0c..e25093510b0c 100644
--- a/drivers/net/stmmac/dwmac_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
diff --git a/drivers/net/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
index e5dfb6a30182..d87976364ec5 100644
--- a/drivers/net/stmmac/enh_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
@@ -23,6 +23,7 @@
*******************************************************************************/
#include "common.h"
+#include "descs_com.h"
static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr)
@@ -233,10 +234,9 @@ static void enh_desc_init_rx_desc(struct dma_desc *p, unsigned int ring_size,
for (i = 0; i < ring_size; i++) {
p->des01.erx.own = 1;
p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1;
- /* To support jumbo frames */
- p->des01.erx.buffer2_size = BUF_SIZE_8KiB - 1;
- if (i == ring_size - 1)
- p->des01.erx.end_ring = 1;
+
+ ehn_desc_rx_set_on_ring_chain(p, (i == ring_size - 1));
+
if (disable_rx_ic)
p->des01.erx.disable_ic = 1;
p++;
@@ -249,8 +249,7 @@ static void enh_desc_init_tx_desc(struct dma_desc *p, unsigned int ring_size)
for (i = 0; i < ring_size; i++) {
p->des01.etx.own = 0;
- if (i == ring_size - 1)
- p->des01.etx.end_ring = 1;
+ ehn_desc_tx_set_on_ring_chain(p, (i == ring_size - 1));
p++;
}
}
@@ -285,19 +284,16 @@ static void enh_desc_release_tx_desc(struct dma_desc *p)
int ter = p->des01.etx.end_ring;
memset(p, 0, offsetof(struct dma_desc, des2));
- p->des01.etx.end_ring = ter;
+ enh_desc_end_tx_desc(p, ter);
}
static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
int csum_flag)
{
p->des01.etx.first_segment = is_fs;
- if (unlikely(len > BUF_SIZE_4KiB)) {
- p->des01.etx.buffer1_size = BUF_SIZE_4KiB;
- p->des01.etx.buffer2_size = len - BUF_SIZE_4KiB;
- } else {
- p->des01.etx.buffer1_size = len;
- }
+
+ enh_set_tx_desc_len(p, len);
+
if (likely(csum_flag))
p->des01.etx.checksum_insertion = cic_full;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc.h b/drivers/net/ethernet/stmicro/stmmac/mmc.h
new file mode 100644
index 000000000000..a38352024cb8
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/mmc.h
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ MMC Header file
+
+ Copyright (C) 2011 STMicroelectronics Ltd
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+*******************************************************************************/
+
+/* MMC control register */
+/* When set, all counter are reset */
+#define MMC_CNTRL_COUNTER_RESET 0x1
+/* When set, do not roll over zero
+ * after reaching the max value*/
+#define MMC_CNTRL_COUNTER_STOP_ROLLOVER 0x2
+#define MMC_CNTRL_RESET_ON_READ 0x4 /* Reset after reading */
+#define MMC_CNTRL_COUNTER_FREEZER 0x8 /* Freeze counter values to the
+ * current value.*/
+#define MMC_CNTRL_PRESET 0x10
+#define MMC_CNTRL_FULL_HALF_PRESET 0x20
+struct stmmac_counters {
+ unsigned int mmc_tx_octetcount_gb;
+ unsigned int mmc_tx_framecount_gb;
+ unsigned int mmc_tx_broadcastframe_g;
+ unsigned int mmc_tx_multicastframe_g;
+ unsigned int mmc_tx_64_octets_gb;
+ unsigned int mmc_tx_65_to_127_octets_gb;
+ unsigned int mmc_tx_128_to_255_octets_gb;
+ unsigned int mmc_tx_256_to_511_octets_gb;
+ unsigned int mmc_tx_512_to_1023_octets_gb;
+ unsigned int mmc_tx_1024_to_max_octets_gb;
+ unsigned int mmc_tx_unicast_gb;
+ unsigned int mmc_tx_multicast_gb;
+ unsigned int mmc_tx_broadcast_gb;
+ unsigned int mmc_tx_underflow_error;
+ unsigned int mmc_tx_singlecol_g;
+ unsigned int mmc_tx_multicol_g;
+ unsigned int mmc_tx_deferred;
+ unsigned int mmc_tx_latecol;
+ unsigned int mmc_tx_exesscol;
+ unsigned int mmc_tx_carrier_error;
+ unsigned int mmc_tx_octetcount_g;
+ unsigned int mmc_tx_framecount_g;
+ unsigned int mmc_tx_excessdef;
+ unsigned int mmc_tx_pause_frame;
+ unsigned int mmc_tx_vlan_frame_g;
+
+ /* MMC RX counter registers */
+ unsigned int mmc_rx_framecount_gb;
+ unsigned int mmc_rx_octetcount_gb;
+ unsigned int mmc_rx_octetcount_g;
+ unsigned int mmc_rx_broadcastframe_g;
+ unsigned int mmc_rx_multicastframe_g;
+ unsigned int mmc_rx_crc_errror;
+ unsigned int mmc_rx_align_error;
+ unsigned int mmc_rx_run_error;
+ unsigned int mmc_rx_jabber_error;
+ unsigned int mmc_rx_undersize_g;
+ unsigned int mmc_rx_oversize_g;
+ unsigned int mmc_rx_64_octets_gb;
+ unsigned int mmc_rx_65_to_127_octets_gb;
+ unsigned int mmc_rx_128_to_255_octets_gb;
+ unsigned int mmc_rx_256_to_511_octets_gb;
+ unsigned int mmc_rx_512_to_1023_octets_gb;
+ unsigned int mmc_rx_1024_to_max_octets_gb;
+ unsigned int mmc_rx_unicast_g;
+ unsigned int mmc_rx_length_error;
+ unsigned int mmc_rx_autofrangetype;
+ unsigned int mmc_rx_pause_frames;
+ unsigned int mmc_rx_fifo_overflow;
+ unsigned int mmc_rx_vlan_frames_gb;
+ unsigned int mmc_rx_watchdog_error;
+ /* IPC */
+ unsigned int mmc_rx_ipc_intr_mask;
+ unsigned int mmc_rx_ipc_intr;
+ /* IPv4 */
+ unsigned int mmc_rx_ipv4_gd;
+ unsigned int mmc_rx_ipv4_hderr;
+ unsigned int mmc_rx_ipv4_nopay;
+ unsigned int mmc_rx_ipv4_frag;
+ unsigned int mmc_rx_ipv4_udsbl;
+
+ unsigned int mmc_rx_ipv4_gd_octets;
+ unsigned int mmc_rx_ipv4_hderr_octets;
+ unsigned int mmc_rx_ipv4_nopay_octets;
+ unsigned int mmc_rx_ipv4_frag_octets;
+ unsigned int mmc_rx_ipv4_udsbl_octets;
+
+ /* IPV6 */
+ unsigned int mmc_rx_ipv6_gd_octets;
+ unsigned int mmc_rx_ipv6_hderr_octets;
+ unsigned int mmc_rx_ipv6_nopay_octets;
+
+ unsigned int mmc_rx_ipv6_gd;
+ unsigned int mmc_rx_ipv6_hderr;
+ unsigned int mmc_rx_ipv6_nopay;
+
+ /* Protocols */
+ unsigned int mmc_rx_udp_gd;
+ unsigned int mmc_rx_udp_err;
+ unsigned int mmc_rx_tcp_gd;
+ unsigned int mmc_rx_tcp_err;
+ unsigned int mmc_rx_icmp_gd;
+ unsigned int mmc_rx_icmp_err;
+
+ unsigned int mmc_rx_udp_gd_octets;
+ unsigned int mmc_rx_udp_err_octets;
+ unsigned int mmc_rx_tcp_gd_octets;
+ unsigned int mmc_rx_tcp_err_octets;
+ unsigned int mmc_rx_icmp_gd_octets;
+ unsigned int mmc_rx_icmp_err_octets;
+};
+
+extern void dwmac_mmc_ctrl(void __iomem *ioaddr, unsigned int mode);
+extern void dwmac_mmc_intr_all_mask(void __iomem *ioaddr);
+extern void dwmac_mmc_read(void __iomem *ioaddr, struct stmmac_counters *mmc);
diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
new file mode 100644
index 000000000000..41e6b33e1b08
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ DWMAC Management Counters
+
+ Copyright (C) 2011 STMicroelectronics Ltd
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+*******************************************************************************/
+
+#include <linux/io.h>
+#include "mmc.h"
+
+/* MAC Management Counters register offset */
+
+#define MMC_CNTRL 0x00000100 /* MMC Control */
+#define MMC_RX_INTR 0x00000104 /* MMC RX Interrupt */
+#define MMC_TX_INTR 0x00000108 /* MMC TX Interrupt */
+#define MMC_RX_INTR_MASK 0x0000010c /* MMC Interrupt Mask */
+#define MMC_TX_INTR_MASK 0x00000110 /* MMC Interrupt Mask */
+#define MMC_DEFAUL_MASK 0xffffffff
+
+/* MMC TX counter registers */
+
+/* Note:
+ * _GB register stands for good and bad frames
+ * _G is for good only.
+ */
+#define MMC_TX_OCTETCOUNT_GB 0x00000114
+#define MMC_TX_FRAMECOUNT_GB 0x00000118
+#define MMC_TX_BROADCASTFRAME_G 0x0000011c
+#define MMC_TX_MULTICASTFRAME_G 0x00000120
+#define MMC_TX_64_OCTETS_GB 0x00000124
+#define MMC_TX_65_TO_127_OCTETS_GB 0x00000128
+#define MMC_TX_128_TO_255_OCTETS_GB 0x0000012c
+#define MMC_TX_256_TO_511_OCTETS_GB 0x00000130
+#define MMC_TX_512_TO_1023_OCTETS_GB 0x00000134
+#define MMC_TX_1024_TO_MAX_OCTETS_GB 0x00000138
+#define MMC_TX_UNICAST_GB 0x0000013c
+#define MMC_TX_MULTICAST_GB 0x00000140
+#define MMC_TX_BROADCAST_GB 0x00000144
+#define MMC_TX_UNDERFLOW_ERROR 0x00000148
+#define MMC_TX_SINGLECOL_G 0x0000014c
+#define MMC_TX_MULTICOL_G 0x00000150
+#define MMC_TX_DEFERRED 0x00000154
+#define MMC_TX_LATECOL 0x00000158
+#define MMC_TX_EXESSCOL 0x0000015c
+#define MMC_TX_CARRIER_ERROR 0x00000160
+#define MMC_TX_OCTETCOUNT_G 0x00000164
+#define MMC_TX_FRAMECOUNT_G 0x00000168
+#define MMC_TX_EXCESSDEF 0x0000016c
+#define MMC_TX_PAUSE_FRAME 0x00000170
+#define MMC_TX_VLAN_FRAME_G 0x00000174
+
+/* MMC RX counter registers */
+#define MMC_RX_FRAMECOUNT_GB 0x00000180
+#define MMC_RX_OCTETCOUNT_GB 0x00000184
+#define MMC_RX_OCTETCOUNT_G 0x00000188
+#define MMC_RX_BROADCASTFRAME_G 0x0000018c
+#define MMC_RX_MULTICASTFRAME_G 0x00000190
+#define MMC_RX_CRC_ERRROR 0x00000194
+#define MMC_RX_ALIGN_ERROR 0x00000198
+#define MMC_RX_RUN_ERROR 0x0000019C
+#define MMC_RX_JABBER_ERROR 0x000001A0
+#define MMC_RX_UNDERSIZE_G 0x000001A4
+#define MMC_RX_OVERSIZE_G 0x000001A8
+#define MMC_RX_64_OCTETS_GB 0x000001AC
+#define MMC_RX_65_TO_127_OCTETS_GB 0x000001b0
+#define MMC_RX_128_TO_255_OCTETS_GB 0x000001b4
+#define MMC_RX_256_TO_511_OCTETS_GB 0x000001b8
+#define MMC_RX_512_TO_1023_OCTETS_GB 0x000001bc
+#define MMC_RX_1024_TO_MAX_OCTETS_GB 0x000001c0
+#define MMC_RX_UNICAST_G 0x000001c4
+#define MMC_RX_LENGTH_ERROR 0x000001c8
+#define MMC_RX_AUTOFRANGETYPE 0x000001cc
+#define MMC_RX_PAUSE_FRAMES 0x000001d0
+#define MMC_RX_FIFO_OVERFLOW 0x000001d4
+#define MMC_RX_VLAN_FRAMES_GB 0x000001d8
+#define MMC_RX_WATCHDOG_ERROR 0x000001dc
+/* IPC*/
+#define MMC_RX_IPC_INTR_MASK 0x00000200
+#define MMC_RX_IPC_INTR 0x00000208
+/* IPv4*/
+#define MMC_RX_IPV4_GD 0x00000210
+#define MMC_RX_IPV4_HDERR 0x00000214
+#define MMC_RX_IPV4_NOPAY 0x00000218
+#define MMC_RX_IPV4_FRAG 0x0000021C
+#define MMC_RX_IPV4_UDSBL 0x00000220
+
+#define MMC_RX_IPV4_GD_OCTETS 0x00000250
+#define MMC_RX_IPV4_HDERR_OCTETS 0x00000254
+#define MMC_RX_IPV4_NOPAY_OCTETS 0x00000258
+#define MMC_RX_IPV4_FRAG_OCTETS 0x0000025c
+#define MMC_RX_IPV4_UDSBL_OCTETS 0x00000260
+
+/* IPV6*/
+#define MMC_RX_IPV6_GD_OCTETS 0x00000264
+#define MMC_RX_IPV6_HDERR_OCTETS 0x00000268
+#define MMC_RX_IPV6_NOPAY_OCTETS 0x0000026c
+
+#define MMC_RX_IPV6_GD 0x00000224
+#define MMC_RX_IPV6_HDERR 0x00000228
+#define MMC_RX_IPV6_NOPAY 0x0000022c
+
+/* Protocols*/
+#define MMC_RX_UDP_GD 0x00000230
+#define MMC_RX_UDP_ERR 0x00000234
+#define MMC_RX_TCP_GD 0x00000238
+#define MMC_RX_TCP_ERR 0x0000023c
+#define MMC_RX_ICMP_GD 0x00000240
+#define MMC_RX_ICMP_ERR 0x00000244
+
+#define MMC_RX_UDP_GD_OCTETS 0x00000270
+#define MMC_RX_UDP_ERR_OCTETS 0x00000274
+#define MMC_RX_TCP_GD_OCTETS 0x00000278
+#define MMC_RX_TCP_ERR_OCTETS 0x0000027c
+#define MMC_RX_ICMP_GD_OCTETS 0x00000280
+#define MMC_RX_ICMP_ERR_OCTETS 0x00000284
+
+void dwmac_mmc_ctrl(void __iomem *ioaddr, unsigned int mode)
+{
+ u32 value = readl(ioaddr + MMC_CNTRL);
+
+ value |= (mode & 0x3F);
+
+ writel(value, ioaddr + MMC_CNTRL);
+
+ pr_debug("stmmac: MMC ctrl register (offset 0x%x): 0x%08x\n",
+ MMC_CNTRL, value);
+}
+
+/* To mask all all interrupts.*/
+void dwmac_mmc_intr_all_mask(void __iomem *ioaddr)
+{
+ writel(MMC_DEFAUL_MASK, ioaddr + MMC_RX_INTR_MASK);
+ writel(MMC_DEFAUL_MASK, ioaddr + MMC_TX_INTR_MASK);
+}
+
+/* This reads the MAC core counters (if actaully supported).
+ * by default the MMC core is programmed to reset each
+ * counter after a read. So all the field of the mmc struct
+ * have to be incremented.
+ */
+void dwmac_mmc_read(void __iomem *ioaddr, struct stmmac_counters *mmc)
+{
+ mmc->mmc_tx_octetcount_gb += readl(ioaddr + MMC_TX_OCTETCOUNT_GB);
+ mmc->mmc_tx_framecount_gb += readl(ioaddr + MMC_TX_FRAMECOUNT_GB);
+ mmc->mmc_tx_broadcastframe_g += readl(ioaddr + MMC_TX_BROADCASTFRAME_G);
+ mmc->mmc_tx_multicastframe_g += readl(ioaddr + MMC_TX_MULTICASTFRAME_G);
+ mmc->mmc_tx_64_octets_gb += readl(ioaddr + MMC_TX_64_OCTETS_GB);
+ mmc->mmc_tx_65_to_127_octets_gb +=
+ readl(ioaddr + MMC_TX_65_TO_127_OCTETS_GB);
+ mmc->mmc_tx_128_to_255_octets_gb +=
+ readl(ioaddr + MMC_TX_128_TO_255_OCTETS_GB);
+ mmc->mmc_tx_256_to_511_octets_gb +=
+ readl(ioaddr + MMC_TX_256_TO_511_OCTETS_GB);
+ mmc->mmc_tx_512_to_1023_octets_gb +=
+ readl(ioaddr + MMC_TX_512_TO_1023_OCTETS_GB);
+ mmc->mmc_tx_1024_to_max_octets_gb +=
+ readl(ioaddr + MMC_TX_1024_TO_MAX_OCTETS_GB);
+ mmc->mmc_tx_unicast_gb += readl(ioaddr + MMC_TX_UNICAST_GB);
+ mmc->mmc_tx_multicast_gb += readl(ioaddr + MMC_TX_MULTICAST_GB);
+ mmc->mmc_tx_broadcast_gb += readl(ioaddr + MMC_TX_BROADCAST_GB);
+ mmc->mmc_tx_underflow_error += readl(ioaddr + MMC_TX_UNDERFLOW_ERROR);
+ mmc->mmc_tx_singlecol_g += readl(ioaddr + MMC_TX_SINGLECOL_G);
+ mmc->mmc_tx_multicol_g += readl(ioaddr + MMC_TX_MULTICOL_G);
+ mmc->mmc_tx_deferred += readl(ioaddr + MMC_TX_DEFERRED);
+ mmc->mmc_tx_latecol += readl(ioaddr + MMC_TX_LATECOL);
+ mmc->mmc_tx_exesscol += readl(ioaddr + MMC_TX_EXESSCOL);
+ mmc->mmc_tx_carrier_error += readl(ioaddr + MMC_TX_CARRIER_ERROR);
+ mmc->mmc_tx_octetcount_g += readl(ioaddr + MMC_TX_OCTETCOUNT_G);
+ mmc->mmc_tx_framecount_g += readl(ioaddr + MMC_TX_FRAMECOUNT_G);
+ mmc->mmc_tx_excessdef += readl(ioaddr + MMC_TX_EXCESSDEF);
+ mmc->mmc_tx_pause_frame += readl(ioaddr + MMC_TX_PAUSE_FRAME);
+ mmc->mmc_tx_vlan_frame_g += readl(ioaddr + MMC_TX_VLAN_FRAME_G);
+
+ /* MMC RX counter registers */
+ mmc->mmc_rx_framecount_gb += readl(ioaddr + MMC_RX_FRAMECOUNT_GB);
+ mmc->mmc_rx_octetcount_gb += readl(ioaddr + MMC_RX_OCTETCOUNT_GB);
+ mmc->mmc_rx_octetcount_g += readl(ioaddr + MMC_RX_OCTETCOUNT_G);
+ mmc->mmc_rx_broadcastframe_g += readl(ioaddr + MMC_RX_BROADCASTFRAME_G);
+ mmc->mmc_rx_multicastframe_g += readl(ioaddr + MMC_RX_MULTICASTFRAME_G);
+ mmc->mmc_rx_crc_errror += readl(ioaddr + MMC_RX_CRC_ERRROR);
+ mmc->mmc_rx_align_error += readl(ioaddr + MMC_RX_ALIGN_ERROR);
+ mmc->mmc_rx_run_error += readl(ioaddr + MMC_RX_RUN_ERROR);
+ mmc->mmc_rx_jabber_error += readl(ioaddr + MMC_RX_JABBER_ERROR);
+ mmc->mmc_rx_undersize_g += readl(ioaddr + MMC_RX_UNDERSIZE_G);
+ mmc->mmc_rx_oversize_g += readl(ioaddr + MMC_RX_OVERSIZE_G);
+ mmc->mmc_rx_64_octets_gb += readl(ioaddr + MMC_RX_64_OCTETS_GB);
+ mmc->mmc_rx_65_to_127_octets_gb +=
+ readl(ioaddr + MMC_RX_65_TO_127_OCTETS_GB);
+ mmc->mmc_rx_128_to_255_octets_gb +=
+ readl(ioaddr + MMC_RX_128_TO_255_OCTETS_GB);
+ mmc->mmc_rx_256_to_511_octets_gb +=
+ readl(ioaddr + MMC_RX_256_TO_511_OCTETS_GB);
+ mmc->mmc_rx_512_to_1023_octets_gb +=
+ readl(ioaddr + MMC_RX_512_TO_1023_OCTETS_GB);
+ mmc->mmc_rx_1024_to_max_octets_gb +=
+ readl(ioaddr + MMC_RX_1024_TO_MAX_OCTETS_GB);
+ mmc->mmc_rx_unicast_g += readl(ioaddr + MMC_RX_UNICAST_G);
+ mmc->mmc_rx_length_error += readl(ioaddr + MMC_RX_LENGTH_ERROR);
+ mmc->mmc_rx_autofrangetype += readl(ioaddr + MMC_RX_AUTOFRANGETYPE);
+ mmc->mmc_rx_pause_frames += readl(ioaddr + MMC_RX_PAUSE_FRAMES);
+ mmc->mmc_rx_fifo_overflow += readl(ioaddr + MMC_RX_FIFO_OVERFLOW);
+ mmc->mmc_rx_vlan_frames_gb += readl(ioaddr + MMC_RX_VLAN_FRAMES_GB);
+ mmc->mmc_rx_watchdog_error += readl(ioaddr + MMC_RX_WATCHDOG_ERROR);
+ /* IPC */
+ mmc->mmc_rx_ipc_intr_mask += readl(ioaddr + MMC_RX_IPC_INTR_MASK);
+ mmc->mmc_rx_ipc_intr += readl(ioaddr + MMC_RX_IPC_INTR);
+ /* IPv4 */
+ mmc->mmc_rx_ipv4_gd += readl(ioaddr + MMC_RX_IPV4_GD);
+ mmc->mmc_rx_ipv4_hderr += readl(ioaddr + MMC_RX_IPV4_HDERR);
+ mmc->mmc_rx_ipv4_nopay += readl(ioaddr + MMC_RX_IPV4_NOPAY);
+ mmc->mmc_rx_ipv4_frag += readl(ioaddr + MMC_RX_IPV4_FRAG);
+ mmc->mmc_rx_ipv4_udsbl += readl(ioaddr + MMC_RX_IPV4_UDSBL);
+
+ mmc->mmc_rx_ipv4_gd_octets += readl(ioaddr + MMC_RX_IPV4_GD_OCTETS);
+ mmc->mmc_rx_ipv4_hderr_octets +=
+ readl(ioaddr + MMC_RX_IPV4_HDERR_OCTETS);
+ mmc->mmc_rx_ipv4_nopay_octets +=
+ readl(ioaddr + MMC_RX_IPV4_NOPAY_OCTETS);
+ mmc->mmc_rx_ipv4_frag_octets += readl(ioaddr + MMC_RX_IPV4_FRAG_OCTETS);
+ mmc->mmc_rx_ipv4_udsbl_octets +=
+ readl(ioaddr + MMC_RX_IPV4_UDSBL_OCTETS);
+
+ /* IPV6 */
+ mmc->mmc_rx_ipv6_gd_octets += readl(ioaddr + MMC_RX_IPV6_GD_OCTETS);
+ mmc->mmc_rx_ipv6_hderr_octets +=
+ readl(ioaddr + MMC_RX_IPV6_HDERR_OCTETS);
+ mmc->mmc_rx_ipv6_nopay_octets +=
+ readl(ioaddr + MMC_RX_IPV6_NOPAY_OCTETS);
+
+ mmc->mmc_rx_ipv6_gd += readl(ioaddr + MMC_RX_IPV6_GD);
+ mmc->mmc_rx_ipv6_hderr += readl(ioaddr + MMC_RX_IPV6_HDERR);
+ mmc->mmc_rx_ipv6_nopay += readl(ioaddr + MMC_RX_IPV6_NOPAY);
+
+ /* Protocols */
+ mmc->mmc_rx_udp_gd += readl(ioaddr + MMC_RX_UDP_GD);
+ mmc->mmc_rx_udp_err += readl(ioaddr + MMC_RX_UDP_ERR);
+ mmc->mmc_rx_tcp_gd += readl(ioaddr + MMC_RX_TCP_GD);
+ mmc->mmc_rx_tcp_err += readl(ioaddr + MMC_RX_TCP_ERR);
+ mmc->mmc_rx_icmp_gd += readl(ioaddr + MMC_RX_ICMP_GD);
+ mmc->mmc_rx_icmp_err += readl(ioaddr + MMC_RX_ICMP_ERR);
+
+ mmc->mmc_rx_udp_gd_octets += readl(ioaddr + MMC_RX_UDP_GD_OCTETS);
+ mmc->mmc_rx_udp_err_octets += readl(ioaddr + MMC_RX_UDP_ERR_OCTETS);
+ mmc->mmc_rx_tcp_gd_octets += readl(ioaddr + MMC_RX_TCP_GD_OCTETS);
+ mmc->mmc_rx_tcp_err_octets += readl(ioaddr + MMC_RX_TCP_ERR_OCTETS);
+ mmc->mmc_rx_icmp_gd_octets += readl(ioaddr + MMC_RX_ICMP_GD_OCTETS);
+ mmc->mmc_rx_icmp_err_octets += readl(ioaddr + MMC_RX_ICMP_ERR_OCTETS);
+}
diff --git a/drivers/net/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
index 029c2a2cf524..fda5d2b31d3a 100644
--- a/drivers/net/stmmac/norm_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
@@ -23,6 +23,7 @@
*******************************************************************************/
#include "common.h"
+#include "descs_com.h"
static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr)
@@ -49,11 +50,12 @@ static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x,
stats->collisions += p->des01.tx.collision_count;
ret = -1;
}
- if (unlikely(p->des01.tx.heartbeat_fail)) {
- x->tx_heartbeat++;
- stats->tx_heartbeat_errors++;
- ret = -1;
+
+ if (p->des01.etx.vlan_frame) {
+ CHIP_DBG(KERN_INFO "GMAC TX status: VLAN frame\n");
+ x->tx_vlan++;
}
+
if (unlikely(p->des01.tx.deferred))
x->tx_deferred++;
@@ -67,12 +69,12 @@ static int ndesc_get_tx_len(struct dma_desc *p)
/* This function verifies if each incoming frame has some errors
* and, if required, updates the multicast statistics.
- * In case of success, it returns csum_none because the device
- * is not able to compute the csum in HW. */
+ * In case of success, it returns good_frame because the GMAC device
+ * is supposed to be able to compute the csum in HW. */
static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p)
{
- int ret = csum_none;
+ int ret = good_frame;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.rx.last_descriptor == 0)) {
@@ -85,12 +87,12 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
if (unlikely(p->des01.rx.error_summary)) {
if (unlikely(p->des01.rx.descriptor_error))
x->rx_desc++;
- if (unlikely(p->des01.rx.partial_frame_error))
- x->rx_partial++;
- if (unlikely(p->des01.rx.run_frame))
- x->rx_runt++;
- if (unlikely(p->des01.rx.frame_too_long))
- x->rx_toolong++;
+ if (unlikely(p->des01.rx.sa_filter_fail))
+ x->sa_filter_fail++;
+ if (unlikely(p->des01.rx.overflow_error))
+ x->overflow_error++;
+ if (unlikely(p->des01.rx.ipc_csum_error))
+ x->ipc_csum_error++;
if (unlikely(p->des01.rx.collision)) {
x->rx_collision++;
stats->collisions++;
@@ -112,10 +114,10 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
x->rx_mii++;
ret = discard_frame;
}
- if (p->des01.rx.multicast_frame) {
- x->rx_multicast++;
- stats->multicast++;
- }
+#ifdef STMMAC_VLAN_TAG_USED
+ if (p->des01.rx.vlan_tag)
+ x->vlan_tag++;
+#endif
return ret;
}
@@ -126,8 +128,9 @@ static void ndesc_init_rx_desc(struct dma_desc *p, unsigned int ring_size,
for (i = 0; i < ring_size; i++) {
p->des01.rx.own = 1;
p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1;
- if (i == ring_size - 1)
- p->des01.rx.end_ring = 1;
+
+ ndesc_rx_set_on_ring_chain(p, (i == ring_size - 1));
+
if (disable_rx_ic)
p->des01.rx.disable_ic = 1;
p++;
@@ -139,8 +142,7 @@ static void ndesc_init_tx_desc(struct dma_desc *p, unsigned int ring_size)
int i;
for (i = 0; i < ring_size; i++) {
p->des01.tx.own = 0;
- if (i == ring_size - 1)
- p->des01.tx.end_ring = 1;
+ ndesc_tx_set_on_ring_chain(p, (i == (ring_size - 1)));
p++;
}
}
@@ -175,15 +177,17 @@ static void ndesc_release_tx_desc(struct dma_desc *p)
int ter = p->des01.tx.end_ring;
memset(p, 0, offsetof(struct dma_desc, des2));
- /* set termination field */
- p->des01.tx.end_ring = ter;
+ ndesc_end_tx_desc(p, ter);
}
static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
int csum_flag)
{
p->des01.tx.first_segment = is_fs;
- p->des01.tx.buffer1_size = len;
+ norm_set_tx_desc_len(p, len);
+
+ if (likely(csum_flag))
+ p->des01.tx.checksum_insertion = cic_full;
}
static void ndesc_clear_tx_ic(struct dma_desc *p)
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
new file mode 100644
index 000000000000..fb8377da1687
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ Specialised functions for managing Ring mode
+
+ Copyright(C) 2011 STMicroelectronics Ltd
+
+ It defines all the functions used to handle the normal/enhanced
+ descriptors in case of the DMA is configured to work in chained or
+ in ring mode.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+*******************************************************************************/
+
+#include "stmmac.h"
+
+static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
+{
+ struct stmmac_priv *priv = (struct stmmac_priv *) p;
+ unsigned int txsize = priv->dma_tx_size;
+ unsigned int entry = priv->cur_tx % txsize;
+ struct dma_desc *desc = priv->dma_tx + entry;
+ unsigned int nopaged_len = skb_headlen(skb);
+ unsigned int bmax, len;
+
+ if (priv->plat->enh_desc)
+ bmax = BUF_SIZE_8KiB;
+ else
+ bmax = BUF_SIZE_2KiB;
+
+ len = nopaged_len - bmax;
+
+ if (nopaged_len > BUF_SIZE_8KiB) {
+
+ desc->des2 = dma_map_single(priv->device, skb->data,
+ bmax, DMA_TO_DEVICE);
+ desc->des3 = desc->des2 + BUF_SIZE_4KiB;
+ priv->hw->desc->prepare_tx_desc(desc, 1, bmax,
+ csum);
+
+ entry = (++priv->cur_tx) % txsize;
+ desc = priv->dma_tx + entry;
+
+ desc->des2 = dma_map_single(priv->device, skb->data + bmax,
+ len, DMA_TO_DEVICE);
+ desc->des3 = desc->des2 + BUF_SIZE_4KiB;
+ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum);
+ priv->hw->desc->set_tx_owner(desc);
+ priv->tx_skbuff[entry] = NULL;
+ } else {
+ desc->des2 = dma_map_single(priv->device, skb->data,
+ nopaged_len, DMA_TO_DEVICE);
+ desc->des3 = desc->des2 + BUF_SIZE_4KiB;
+ priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum);
+ }
+
+ return entry;
+}
+
+static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
+{
+ unsigned int ret = 0;
+
+ if (len >= BUF_SIZE_4KiB)
+ ret = 1;
+
+ return ret;
+}
+
+static void stmmac_refill_desc3(int bfsize, struct dma_desc *p)
+{
+ /* Fill DES3 in case of RING mode */
+ if (bfsize >= BUF_SIZE_8KiB)
+ p->des3 = p->des2 + BUF_SIZE_8KiB;
+}
+
+/* In ring mode we need to fill the desc3 because it is used
+ * as buffer */
+static void stmmac_init_desc3(int des3_as_data_buf, struct dma_desc *p)
+{
+ if (unlikely(des3_as_data_buf))
+ p->des3 = p->des2 + BUF_SIZE_8KiB;
+}
+
+static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr,
+ unsigned int size)
+{
+}
+
+static void stmmac_clean_desc3(struct dma_desc *p)
+{
+ if (unlikely(p->des3))
+ p->des3 = 0;
+}
+
+static int stmmac_set_16kib_bfsize(int mtu)
+{
+ int ret = 0;
+ if (unlikely(mtu >= BUF_SIZE_8KiB))
+ ret = BUF_SIZE_16KiB;
+ return ret;
+}
+
+const struct stmmac_ring_mode_ops ring_mode_ops = {
+ .is_jumbo_frm = stmmac_is_jumbo_frm,
+ .jumbo_frm = stmmac_jumbo_frm,
+ .refill_desc3 = stmmac_refill_desc3,
+ .init_desc3 = stmmac_init_desc3,
+ .init_dma_chain = stmmac_init_dma_chain,
+ .clean_desc3 = stmmac_clean_desc3,
+ .set_16kib_bfsize = stmmac_set_16kib_bfsize,
+};
diff --git a/drivers/net/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 2b076b313622..a140a8fbf051 100644
--- a/drivers/net/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -20,9 +20,9 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
-#define DRV_MODULE_VERSION "Nov_2010"
+#define DRV_MODULE_VERSION "Oct_2011"
#include <linux/stmmac.h>
-
+#include <linux/phy.h>
#include "common.h"
#ifdef CONFIG_STMMAC_TIMER
#include "stmmac_timer.h"
@@ -56,14 +56,9 @@ struct stmmac_priv {
struct stmmac_extra_stats xstats;
struct napi_struct napi;
- phy_interface_t phy_interface;
- int phy_addr;
- int phy_mask;
- int (*phy_reset) (void *priv);
int rx_coe;
int no_csum_insertion;
- int phy_irq;
struct phy_device *phydev;
int oldlink;
int speed;
@@ -71,18 +66,20 @@ struct stmmac_priv {
unsigned int flow_ctrl;
unsigned int pause;
struct mii_bus *mii;
+ int mii_irq[PHY_MAX_ADDR];
u32 msg_enable;
spinlock_t lock;
+ spinlock_t tx_lock;
int wolopts;
- int wolenabled;
+ int wol_irq;
#ifdef CONFIG_STMMAC_TIMER
struct stmmac_timer *tm;
#endif
-#ifdef STMMAC_VLAN_TAG_USED
- struct vlan_group *vlgrp;
-#endif
struct plat_stmmacenet_data *plat;
+ struct stmmac_counters mmc;
+ struct dma_features dma_cap;
+ int hw_cap_support;
};
extern int stmmac_mdio_unregister(struct net_device *ndev);
diff --git a/drivers/net/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index ae5213a8c4cd..0395f9eba801 100644
--- a/drivers/net/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -24,8 +24,10 @@
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
+#include <linux/interrupt.h>
#include <linux/mii.h>
#include <linux/phy.h>
+#include <asm/io.h>
#include "stmmac.h"
#include "dwmac_dma.h"
@@ -44,11 +46,11 @@ struct stmmac_stats {
{ #m, FIELD_SIZEOF(struct stmmac_extra_stats, m), \
offsetof(struct stmmac_priv, xstats.m)}
-static const struct stmmac_stats stmmac_gstrings_stats[] = {
+static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(tx_underflow),
STMMAC_STAT(tx_carrier),
STMMAC_STAT(tx_losscarrier),
- STMMAC_STAT(tx_heartbeat),
+ STMMAC_STAT(vlan_tag),
STMMAC_STAT(tx_deferred),
STMMAC_STAT(tx_vlan),
STMMAC_STAT(rx_vlan),
@@ -57,9 +59,9 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(tx_payload_error),
STMMAC_STAT(tx_ip_header_error),
STMMAC_STAT(rx_desc),
- STMMAC_STAT(rx_partial),
- STMMAC_STAT(rx_runt),
- STMMAC_STAT(rx_toolong),
+ STMMAC_STAT(sa_filter_fail),
+ STMMAC_STAT(overflow_error),
+ STMMAC_STAT(ipc_csum_error),
STMMAC_STAT(rx_collision),
STMMAC_STAT(rx_crc),
STMMAC_STAT(rx_length),
@@ -89,19 +91,106 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
};
#define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats)
+/* HW MAC Management counters (if supported) */
+#define STMMAC_MMC_STAT(m) \
+ { #m, FIELD_SIZEOF(struct stmmac_counters, m), \
+ offsetof(struct stmmac_priv, mmc.m)}
+
+static const struct stmmac_stats stmmac_mmc[] = {
+ STMMAC_MMC_STAT(mmc_tx_octetcount_gb),
+ STMMAC_MMC_STAT(mmc_tx_framecount_gb),
+ STMMAC_MMC_STAT(mmc_tx_broadcastframe_g),
+ STMMAC_MMC_STAT(mmc_tx_multicastframe_g),
+ STMMAC_MMC_STAT(mmc_tx_64_octets_gb),
+ STMMAC_MMC_STAT(mmc_tx_65_to_127_octets_gb),
+ STMMAC_MMC_STAT(mmc_tx_128_to_255_octets_gb),
+ STMMAC_MMC_STAT(mmc_tx_256_to_511_octets_gb),
+ STMMAC_MMC_STAT(mmc_tx_512_to_1023_octets_gb),
+ STMMAC_MMC_STAT(mmc_tx_1024_to_max_octets_gb),
+ STMMAC_MMC_STAT(mmc_tx_unicast_gb),
+ STMMAC_MMC_STAT(mmc_tx_multicast_gb),
+ STMMAC_MMC_STAT(mmc_tx_broadcast_gb),
+ STMMAC_MMC_STAT(mmc_tx_underflow_error),
+ STMMAC_MMC_STAT(mmc_tx_singlecol_g),
+ STMMAC_MMC_STAT(mmc_tx_multicol_g),
+ STMMAC_MMC_STAT(mmc_tx_deferred),
+ STMMAC_MMC_STAT(mmc_tx_latecol),
+ STMMAC_MMC_STAT(mmc_tx_exesscol),
+ STMMAC_MMC_STAT(mmc_tx_carrier_error),
+ STMMAC_MMC_STAT(mmc_tx_octetcount_g),
+ STMMAC_MMC_STAT(mmc_tx_framecount_g),
+ STMMAC_MMC_STAT(mmc_tx_excessdef),
+ STMMAC_MMC_STAT(mmc_tx_pause_frame),
+ STMMAC_MMC_STAT(mmc_tx_vlan_frame_g),
+ STMMAC_MMC_STAT(mmc_rx_framecount_gb),
+ STMMAC_MMC_STAT(mmc_rx_octetcount_gb),
+ STMMAC_MMC_STAT(mmc_rx_octetcount_g),
+ STMMAC_MMC_STAT(mmc_rx_broadcastframe_g),
+ STMMAC_MMC_STAT(mmc_rx_multicastframe_g),
+ STMMAC_MMC_STAT(mmc_rx_crc_errror),
+ STMMAC_MMC_STAT(mmc_rx_align_error),
+ STMMAC_MMC_STAT(mmc_rx_run_error),
+ STMMAC_MMC_STAT(mmc_rx_jabber_error),
+ STMMAC_MMC_STAT(mmc_rx_undersize_g),
+ STMMAC_MMC_STAT(mmc_rx_oversize_g),
+ STMMAC_MMC_STAT(mmc_rx_64_octets_gb),
+ STMMAC_MMC_STAT(mmc_rx_65_to_127_octets_gb),
+ STMMAC_MMC_STAT(mmc_rx_128_to_255_octets_gb),
+ STMMAC_MMC_STAT(mmc_rx_256_to_511_octets_gb),
+ STMMAC_MMC_STAT(mmc_rx_512_to_1023_octets_gb),
+ STMMAC_MMC_STAT(mmc_rx_1024_to_max_octets_gb),
+ STMMAC_MMC_STAT(mmc_rx_unicast_g),
+ STMMAC_MMC_STAT(mmc_rx_length_error),
+ STMMAC_MMC_STAT(mmc_rx_autofrangetype),
+ STMMAC_MMC_STAT(mmc_rx_pause_frames),
+ STMMAC_MMC_STAT(mmc_rx_fifo_overflow),
+ STMMAC_MMC_STAT(mmc_rx_vlan_frames_gb),
+ STMMAC_MMC_STAT(mmc_rx_watchdog_error),
+ STMMAC_MMC_STAT(mmc_rx_ipc_intr_mask),
+ STMMAC_MMC_STAT(mmc_rx_ipc_intr),
+ STMMAC_MMC_STAT(mmc_rx_ipv4_gd),
+ STMMAC_MMC_STAT(mmc_rx_ipv4_hderr),
+ STMMAC_MMC_STAT(mmc_rx_ipv4_nopay),
+ STMMAC_MMC_STAT(mmc_rx_ipv4_frag),
+ STMMAC_MMC_STAT(mmc_rx_ipv4_udsbl),
+ STMMAC_MMC_STAT(mmc_rx_ipv4_gd_octets),
+ STMMAC_MMC_STAT(mmc_rx_ipv4_hderr_octets),
+ STMMAC_MMC_STAT(mmc_rx_ipv4_nopay_octets),
+ STMMAC_MMC_STAT(mmc_rx_ipv4_frag_octets),
+ STMMAC_MMC_STAT(mmc_rx_ipv4_udsbl_octets),
+ STMMAC_MMC_STAT(mmc_rx_ipv6_gd_octets),
+ STMMAC_MMC_STAT(mmc_rx_ipv6_hderr_octets),
+ STMMAC_MMC_STAT(mmc_rx_ipv6_nopay_octets),
+ STMMAC_MMC_STAT(mmc_rx_ipv6_gd),
+ STMMAC_MMC_STAT(mmc_rx_ipv6_hderr),
+ STMMAC_MMC_STAT(mmc_rx_ipv6_nopay),
+ STMMAC_MMC_STAT(mmc_rx_udp_gd),
+ STMMAC_MMC_STAT(mmc_rx_udp_err),
+ STMMAC_MMC_STAT(mmc_rx_tcp_gd),
+ STMMAC_MMC_STAT(mmc_rx_tcp_err),
+ STMMAC_MMC_STAT(mmc_rx_icmp_gd),
+ STMMAC_MMC_STAT(mmc_rx_icmp_err),
+ STMMAC_MMC_STAT(mmc_rx_udp_gd_octets),
+ STMMAC_MMC_STAT(mmc_rx_udp_err_octets),
+ STMMAC_MMC_STAT(mmc_rx_tcp_gd_octets),
+ STMMAC_MMC_STAT(mmc_rx_tcp_err_octets),
+ STMMAC_MMC_STAT(mmc_rx_icmp_gd_octets),
+ STMMAC_MMC_STAT(mmc_rx_icmp_err_octets),
+};
+#define STMMAC_MMC_STATS_LEN ARRAY_SIZE(stmmac_mmc)
+
static void stmmac_ethtool_getdrvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct stmmac_priv *priv = netdev_priv(dev);
- if (!priv->plat->has_gmac)
- strcpy(info->driver, MAC100_ETHTOOL_NAME);
- else
+ if (priv->plat->has_gmac)
strcpy(info->driver, GMAC_ETHTOOL_NAME);
+ else
+ strcpy(info->driver, MAC100_ETHTOOL_NAME);
strcpy(info->version, DRV_MODULE_VERSION);
info->fw_version[0] = '\0';
- info->n_stats = STMMAC_STATS_LEN;
}
static int stmmac_ethtool_getsettings(struct net_device *dev,
@@ -250,24 +339,48 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *dummy, u64 *data)
{
struct stmmac_priv *priv = netdev_priv(dev);
- int i;
-
- /* Update HW stats if supported */
- priv->hw->dma->dma_diagnostic_fr(&dev->stats, (void *) &priv->xstats,
- priv->ioaddr);
+ int i, j = 0;
+ /* Update the DMA HW counters for dwmac10/100 */
+ if (!priv->plat->has_gmac)
+ priv->hw->dma->dma_diagnostic_fr(&dev->stats,
+ (void *) &priv->xstats,
+ priv->ioaddr);
+ else {
+ /* If supported, for new GMAC chips expose the MMC counters */
+ if (priv->dma_cap.rmon) {
+ dwmac_mmc_read(priv->ioaddr, &priv->mmc);
+
+ for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) {
+ char *p;
+ p = (char *)priv + stmmac_mmc[i].stat_offset;
+
+ data[j++] = (stmmac_mmc[i].sizeof_stat ==
+ sizeof(u64)) ? (*(u64 *)p) :
+ (*(u32 *)p);
+ }
+ }
+ }
for (i = 0; i < STMMAC_STATS_LEN; i++) {
char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset;
- data[i] = (stmmac_gstrings_stats[i].sizeof_stat ==
- sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p);
+ data[j++] = (stmmac_gstrings_stats[i].sizeof_stat ==
+ sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p);
}
}
static int stmmac_get_sset_count(struct net_device *netdev, int sset)
{
+ struct stmmac_priv *priv = netdev_priv(netdev);
+ int len;
+
switch (sset) {
case ETH_SS_STATS:
- return STMMAC_STATS_LEN;
+ len = STMMAC_STATS_LEN;
+
+ if (priv->dma_cap.rmon)
+ len += STMMAC_MMC_STATS_LEN;
+
+ return len;
default:
return -EOPNOTSUPP;
}
@@ -277,9 +390,16 @@ static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
int i;
u8 *p = data;
+ struct stmmac_priv *priv = netdev_priv(dev);
switch (stringset) {
case ETH_SS_STATS:
+ if (priv->dma_cap.rmon)
+ for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) {
+ memcpy(p, stmmac_mmc[i].stat_string,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
for (i = 0; i < STMMAC_STATS_LEN; i++) {
memcpy(p, stmmac_gstrings_stats[i].stat_string,
ETH_GSTRING_LEN);
@@ -310,6 +430,12 @@ static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
struct stmmac_priv *priv = netdev_priv(dev);
u32 support = WAKE_MAGIC | WAKE_UCAST;
+ /* By default almost all GMAC devices support the WoL via
+ * magic frame but we can disable it if the HW capability
+ * register shows no support for pmt_magic_frame. */
+ if ((priv->hw_cap_support) && (!priv->dma_cap.pmt_magic_frame))
+ wol->wolopts &= ~WAKE_MAGIC;
+
if (!device_can_wakeup(priv->device))
return -EINVAL;
@@ -319,10 +445,10 @@ static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
if (wol->wolopts) {
pr_info("stmmac: wakeup enable\n");
device_set_wakeup_enable(priv->device, 1);
- enable_irq_wake(dev->irq);
+ enable_irq_wake(priv->wol_irq);
} else {
device_set_wakeup_enable(priv->device, 0);
- disable_irq_wake(dev->irq);
+ disable_irq_wake(priv->wol_irq);
}
spin_lock_irq(&priv->lock);
diff --git a/drivers/net/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index e25e44a45c28..8ea770a89f25 100644
--- a/drivers/net/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2,7 +2,7 @@
This is the driver for the ST MAC 10/100/1000 on-chip Ethernet controllers.
ST Ethernet IPs are built around a Synopsys IP Core.
- Copyright (C) 2007-2009 STMicroelectronics Ltd
+ Copyright(C) 2007-2011 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -41,15 +41,18 @@
#include <linux/if_ether.h>
#include <linux/crc32.h>
#include <linux/mii.h>
-#include <linux/phy.h>
+#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/prefetch.h>
+#ifdef CONFIG_STMMAC_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#endif
#include "stmmac.h"
#define STMMAC_RESOURCE_NAME "stmmaceth"
-#define PHY_RESOURCE_NAME "stmmacphy"
#undef STMMAC_DEBUG
/*#define STMMAC_DEBUG*/
@@ -300,29 +303,29 @@ static int stmmac_init_phy(struct net_device *dev)
struct phy_device *phydev;
char phy_id[MII_BUS_ID_SIZE + 3];
char bus_id[MII_BUS_ID_SIZE];
-
+ int interface = priv->plat->interface;
priv->oldlink = 0;
priv->speed = 0;
priv->oldduplex = -1;
- if (priv->phy_addr == -1) {
- /* We don't have a PHY, so do nothing */
- return 0;
- }
-
snprintf(bus_id, MII_BUS_ID_SIZE, "%x", priv->plat->bus_id);
snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
- priv->phy_addr);
+ priv->plat->phy_addr);
pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id);
- phydev = phy_connect(dev, phy_id, &stmmac_adjust_link, 0,
- priv->phy_interface);
+ phydev = phy_connect(dev, phy_id, &stmmac_adjust_link, 0, interface);
if (IS_ERR(phydev)) {
pr_err("%s: Could not attach to PHY\n", dev->name);
return PTR_ERR(phydev);
}
+ /* Stop Advertising 1000BASE Capability if interface is not GMII */
+ if ((interface == PHY_INTERFACE_MODE_MII) ||
+ (interface == PHY_INTERFACE_MODE_RMII))
+ phydev->advertising &= ~(SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full);
+
/*
* Broken HW is sometimes missing the pull-up resistor on the
* MDIO line, which results in reads to non-existent devices returning
@@ -335,7 +338,7 @@ static int stmmac_init_phy(struct net_device *dev)
return -ENODEV;
}
pr_debug("stmmac_init_phy: %s: attached to PHY (UID 0x%x)"
- " Link = %d\n", dev->name, phydev->phy_id, phydev->link);
+ " Link = %d\n", dev->name, phydev->phy_id, phydev->link);
priv->phydev = phydev;
@@ -382,11 +385,28 @@ static void display_ring(struct dma_desc *p, int size)
}
}
+static int stmmac_set_bfsize(int mtu, int bufsize)
+{
+ int ret = bufsize;
+
+ if (mtu >= BUF_SIZE_4KiB)
+ ret = BUF_SIZE_8KiB;
+ else if (mtu >= BUF_SIZE_2KiB)
+ ret = BUF_SIZE_4KiB;
+ else if (mtu >= DMA_BUFFER_SIZE)
+ ret = BUF_SIZE_2KiB;
+ else
+ ret = DMA_BUFFER_SIZE;
+
+ return ret;
+}
+
/**
* init_dma_desc_rings - init the RX/TX descriptor rings
* @dev: net device structure
* Description: this function initializes the DMA RX/TX descriptors
- * and allocates the socket buffers.
+ * and allocates the socket buffers. It suppors the chained and ring
+ * modes.
*/
static void init_dma_desc_rings(struct net_device *dev)
{
@@ -395,31 +415,24 @@ static void init_dma_desc_rings(struct net_device *dev)
struct sk_buff *skb;
unsigned int txsize = priv->dma_tx_size;
unsigned int rxsize = priv->dma_rx_size;
- unsigned int bfsize = priv->dma_buf_sz;
- int buff2_needed = 0, dis_ic = 0;
+ unsigned int bfsize;
+ int dis_ic = 0;
+ int des3_as_data_buf = 0;
- /* Set the Buffer size according to the MTU;
- * indeed, in case of jumbo we need to bump-up the buffer sizes.
- */
- if (unlikely(dev->mtu >= BUF_SIZE_8KiB))
- bfsize = BUF_SIZE_16KiB;
- else if (unlikely(dev->mtu >= BUF_SIZE_4KiB))
- bfsize = BUF_SIZE_8KiB;
- else if (unlikely(dev->mtu >= BUF_SIZE_2KiB))
- bfsize = BUF_SIZE_4KiB;
- else if (unlikely(dev->mtu >= DMA_BUFFER_SIZE))
- bfsize = BUF_SIZE_2KiB;
+ /* Set the max buffer size according to the DESC mode
+ * and the MTU. Note that RING mode allows 16KiB bsize. */
+ bfsize = priv->hw->ring->set_16kib_bfsize(dev->mtu);
+
+ if (bfsize == BUF_SIZE_16KiB)
+ des3_as_data_buf = 1;
else
- bfsize = DMA_BUFFER_SIZE;
+ bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz);
#ifdef CONFIG_STMMAC_TIMER
/* Disable interrupts on completion for the reception if timer is on */
if (likely(priv->tm->enable))
dis_ic = 1;
#endif
- /* If the MTU exceeds 8k so use the second buffer in the chain */
- if (bfsize >= BUF_SIZE_8KiB)
- buff2_needed = 1;
DBG(probe, INFO, "stmmac: txsize %d, rxsize %d, bfsize %d\n",
txsize, rxsize, bfsize);
@@ -447,7 +460,7 @@ static void init_dma_desc_rings(struct net_device *dev)
return;
}
- DBG(probe, INFO, "stmmac (%s) DMA desc rings: virt addr (Rx %p, "
+ DBG(probe, INFO, "stmmac (%s) DMA desc: virt addr (Rx %p, "
"Tx %p)\n\tDMA phy addr (Rx 0x%08x, Tx 0x%08x)\n",
dev->name, priv->dma_rx, priv->dma_tx,
(unsigned int)priv->dma_rx_phy, (unsigned int)priv->dma_tx_phy);
@@ -459,18 +472,21 @@ static void init_dma_desc_rings(struct net_device *dev)
for (i = 0; i < rxsize; i++) {
struct dma_desc *p = priv->dma_rx + i;
- skb = netdev_alloc_skb_ip_align(dev, bfsize);
+ skb = __netdev_alloc_skb(dev, bfsize + NET_IP_ALIGN,
+ GFP_KERNEL);
if (unlikely(skb == NULL)) {
pr_err("%s: Rx init fails; skb is NULL\n", __func__);
break;
}
+ skb_reserve(skb, NET_IP_ALIGN);
priv->rx_skbuff[i] = skb;
priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
bfsize, DMA_FROM_DEVICE);
p->des2 = priv->rx_skbuff_dma[i];
- if (unlikely(buff2_needed))
- p->des3 = p->des2 + BUF_SIZE_8KiB;
+
+ priv->hw->ring->init_desc3(des3_as_data_buf, p);
+
DBG(probe, INFO, "[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i],
priv->rx_skbuff[i]->data, priv->rx_skbuff_dma[i]);
}
@@ -484,6 +500,12 @@ static void init_dma_desc_rings(struct net_device *dev)
priv->tx_skbuff[i] = NULL;
priv->dma_tx[i].des2 = 0;
}
+
+ /* In case of Chained mode this sets the des3 to the next
+ * element in the chain */
+ priv->hw->ring->init_dma_chain(priv->dma_rx, priv->dma_rx_phy, rxsize);
+ priv->hw->ring->init_dma_chain(priv->dma_tx, priv->dma_tx_phy, txsize);
+
priv->dirty_tx = 0;
priv->cur_tx = 0;
@@ -557,9 +579,11 @@ static void free_dma_desc_resources(struct stmmac_priv *priv)
*/
static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
{
- if (likely((priv->plat->tx_coe) && (!priv->no_csum_insertion))) {
- /* In case of GMAC, SF mode has to be enabled
- * to perform the TX COE. This depends on:
+ if (likely(priv->plat->force_sf_dma_mode ||
+ ((priv->plat->tx_coe) && (!priv->no_csum_insertion)))) {
+ /*
+ * In case of GMAC, SF mode can be enabled
+ * to perform the TX COE in HW. This depends on:
* 1) TX COE if actually supported
* 2) There is no bugged Jumbo frame support
* that needs to not insert csum in the TDES.
@@ -580,6 +604,8 @@ static void stmmac_tx(struct stmmac_priv *priv)
{
unsigned int txsize = priv->dma_tx_size;
+ spin_lock(&priv->tx_lock);
+
while (priv->dirty_tx != priv->cur_tx) {
int last;
unsigned int entry = priv->dirty_tx % txsize;
@@ -610,8 +636,7 @@ static void stmmac_tx(struct stmmac_priv *priv)
dma_unmap_single(priv->device, p->des2,
priv->hw->desc->get_tx_len(p),
DMA_TO_DEVICE);
- if (unlikely(p->des3))
- p->des3 = 0;
+ priv->hw->ring->clean_desc3(p);
if (likely(skb != NULL)) {
/*
@@ -643,6 +668,7 @@ static void stmmac_tx(struct stmmac_priv *priv)
}
netif_tx_unlock(priv->dev);
}
+ spin_unlock(&priv->tx_lock);
}
static inline void stmmac_enable_irq(struct stmmac_priv *priv)
@@ -717,7 +743,6 @@ static void stmmac_no_timer_stopped(void)
*/
static void stmmac_tx_err(struct stmmac_priv *priv)
{
-
netif_stop_queue(priv->dev);
priv->hw->dma->stop_tx(priv->ioaddr);
@@ -751,6 +776,111 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)
stmmac_tx_err(priv);
}
+static void stmmac_mmc_setup(struct stmmac_priv *priv)
+{
+ unsigned int mode = MMC_CNTRL_RESET_ON_READ | MMC_CNTRL_COUNTER_RESET |
+ MMC_CNTRL_PRESET | MMC_CNTRL_FULL_HALF_PRESET;
+
+ /* Do not manage MMC IRQ (FIXME) */
+ dwmac_mmc_intr_all_mask(priv->ioaddr);
+ dwmac_mmc_ctrl(priv->ioaddr, mode);
+ memset(&priv->mmc, 0, sizeof(struct stmmac_counters));
+}
+
+static u32 stmmac_get_synopsys_id(struct stmmac_priv *priv)
+{
+ u32 hwid = priv->hw->synopsys_uid;
+
+ /* Only check valid Synopsys Id because old MAC chips
+ * have no HW registers where get the ID */
+ if (likely(hwid)) {
+ u32 uid = ((hwid & 0x0000ff00) >> 8);
+ u32 synid = (hwid & 0x000000ff);
+
+ pr_info("STMMAC - user ID: 0x%x, Synopsys ID: 0x%x\n",
+ uid, synid);
+
+ return synid;
+ }
+ return 0;
+}
+
+/**
+ * stmmac_selec_desc_mode
+ * @dev : device pointer
+ * Description: select the Enhanced/Alternate or Normal descriptors */
+static void stmmac_selec_desc_mode(struct stmmac_priv *priv)
+{
+ if (priv->plat->enh_desc) {
+ pr_info(" Enhanced/Alternate descriptors\n");
+ priv->hw->desc = &enh_desc_ops;
+ } else {
+ pr_info(" Normal descriptors\n");
+ priv->hw->desc = &ndesc_ops;
+ }
+}
+
+/**
+ * stmmac_get_hw_features
+ * @priv : private device pointer
+ * Description:
+ * new GMAC chip generations have a new register to indicate the
+ * presence of the optional feature/functions.
+ * This can be also used to override the value passed through the
+ * platform and necessary for old MAC10/100 and GMAC chips.
+ */
+static int stmmac_get_hw_features(struct stmmac_priv *priv)
+{
+ u32 hw_cap = 0;
+
+ if (priv->hw->dma->get_hw_feature) {
+ hw_cap = priv->hw->dma->get_hw_feature(priv->ioaddr);
+
+ priv->dma_cap.mbps_10_100 = (hw_cap & DMA_HW_FEAT_MIISEL);
+ priv->dma_cap.mbps_1000 = (hw_cap & DMA_HW_FEAT_GMIISEL) >> 1;
+ priv->dma_cap.half_duplex = (hw_cap & DMA_HW_FEAT_HDSEL) >> 2;
+ priv->dma_cap.hash_filter = (hw_cap & DMA_HW_FEAT_HASHSEL) >> 4;
+ priv->dma_cap.multi_addr =
+ (hw_cap & DMA_HW_FEAT_ADDMACADRSEL) >> 5;
+ priv->dma_cap.pcs = (hw_cap & DMA_HW_FEAT_PCSSEL) >> 6;
+ priv->dma_cap.sma_mdio = (hw_cap & DMA_HW_FEAT_SMASEL) >> 8;
+ priv->dma_cap.pmt_remote_wake_up =
+ (hw_cap & DMA_HW_FEAT_RWKSEL) >> 9;
+ priv->dma_cap.pmt_magic_frame =
+ (hw_cap & DMA_HW_FEAT_MGKSEL) >> 10;
+ /* MMC */
+ priv->dma_cap.rmon = (hw_cap & DMA_HW_FEAT_MMCSEL) >> 11;
+ /* IEEE 1588-2002*/
+ priv->dma_cap.time_stamp =
+ (hw_cap & DMA_HW_FEAT_TSVER1SEL) >> 12;
+ /* IEEE 1588-2008*/
+ priv->dma_cap.atime_stamp =
+ (hw_cap & DMA_HW_FEAT_TSVER2SEL) >> 13;
+ /* 802.3az - Energy-Efficient Ethernet (EEE) */
+ priv->dma_cap.eee = (hw_cap & DMA_HW_FEAT_EEESEL) >> 14;
+ priv->dma_cap.av = (hw_cap & DMA_HW_FEAT_AVSEL) >> 15;
+ /* TX and RX csum */
+ priv->dma_cap.tx_coe = (hw_cap & DMA_HW_FEAT_TXCOESEL) >> 16;
+ priv->dma_cap.rx_coe_type1 =
+ (hw_cap & DMA_HW_FEAT_RXTYP1COE) >> 17;
+ priv->dma_cap.rx_coe_type2 =
+ (hw_cap & DMA_HW_FEAT_RXTYP2COE) >> 18;
+ priv->dma_cap.rxfifo_over_2048 =
+ (hw_cap & DMA_HW_FEAT_RXFIFOSIZE) >> 19;
+ /* TX and RX number of channels */
+ priv->dma_cap.number_rx_channel =
+ (hw_cap & DMA_HW_FEAT_RXCHCNT) >> 20;
+ priv->dma_cap.number_tx_channel =
+ (hw_cap & DMA_HW_FEAT_TXCHCNT) >> 22;
+ /* Alternate (enhanced) DESC mode*/
+ priv->dma_cap.enh_desc =
+ (hw_cap & DMA_HW_FEAT_ENHDESSEL) >> 24;
+
+ }
+
+ return hw_cap;
+}
+
/**
* stmmac_open - open entry point of the driver
* @dev : pointer to the device structure.
@@ -801,6 +931,44 @@ static int stmmac_open(struct net_device *dev)
goto open_error;
}
+ stmmac_get_synopsys_id(priv);
+
+ priv->hw_cap_support = stmmac_get_hw_features(priv);
+
+ if (priv->hw_cap_support) {
+ pr_info(" Support DMA HW capability register");
+
+ /* We can override some gmac/dma configuration fields: e.g.
+ * enh_desc, tx_coe (e.g. that are passed through the
+ * platform) with the values from the HW capability
+ * register (if supported).
+ */
+ priv->plat->enh_desc = priv->dma_cap.enh_desc;
+ priv->plat->tx_coe = priv->dma_cap.tx_coe;
+ priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up;
+
+ /* By default disable wol on magic frame if not supported */
+ if (!priv->dma_cap.pmt_magic_frame)
+ priv->wolopts &= ~WAKE_MAGIC;
+
+ } else
+ pr_info(" No HW DMA feature register supported");
+
+ /* Select the enhnaced/normal descriptor structures */
+ stmmac_selec_desc_mode(priv);
+
+ /* PMT module is not integrated in all the MAC devices. */
+ if (priv->plat->pmt) {
+ pr_info(" Remote wake-up capable\n");
+ device_set_wakeup_capable(priv->device, 1);
+ }
+
+ priv->rx_coe = priv->hw->mac->rx_coe(priv->ioaddr);
+ if (priv->rx_coe)
+ pr_info(" Checksum Offload Engine supported\n");
+ if (priv->plat->tx_coe)
+ pr_info(" Checksum insertion supported\n");
+
/* Create and initialize the TX/RX descriptors chains. */
priv->dma_tx_size = STMMAC_ALIGN(dma_txsize);
priv->dma_rx_size = STMMAC_ALIGN(dma_rxsize);
@@ -823,17 +991,8 @@ static int stmmac_open(struct net_device *dev)
/* Initialize the MAC Core */
priv->hw->mac->core_init(priv->ioaddr);
- priv->rx_coe = priv->hw->mac->rx_coe(priv->ioaddr);
- if (priv->rx_coe)
- pr_info("stmmac: Rx Checksum Offload Engine supported\n");
- if (priv->plat->tx_coe)
- pr_info("\tTX Checksum insertion supported\n");
netdev_update_features(dev);
- /* Initialise the MMC (if present) to disable all interrupts. */
- writel(0xffffffff, priv->ioaddr + MMC_HIGH_INTR_MASK);
- writel(0xffffffff, priv->ioaddr + MMC_LOW_INTR_MASK);
-
/* Request the IRQ lines */
ret = request_irq(dev->irq, stmmac_interrupt,
IRQF_SHARED, dev->name, dev);
@@ -853,6 +1012,9 @@ static int stmmac_open(struct net_device *dev)
memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats));
priv->xstats.threshold = tc;
+ if (priv->dma_cap.rmon)
+ stmmac_mmc_setup(priv);
+
/* Start the ball rolling... */
DBG(probe, DEBUG, "%s: DMA RX/TX processes started...\n", dev->name);
priv->hw->dma->start_tx(priv->ioaddr);
@@ -932,47 +1094,6 @@ static int stmmac_release(struct net_device *dev)
return 0;
}
-static unsigned int stmmac_handle_jumbo_frames(struct sk_buff *skb,
- struct net_device *dev,
- int csum_insertion)
-{
- struct stmmac_priv *priv = netdev_priv(dev);
- unsigned int nopaged_len = skb_headlen(skb);
- unsigned int txsize = priv->dma_tx_size;
- unsigned int entry = priv->cur_tx % txsize;
- struct dma_desc *desc = priv->dma_tx + entry;
-
- if (nopaged_len > BUF_SIZE_8KiB) {
-
- int buf2_size = nopaged_len - BUF_SIZE_8KiB;
-
- desc->des2 = dma_map_single(priv->device, skb->data,
- BUF_SIZE_8KiB, DMA_TO_DEVICE);
- desc->des3 = desc->des2 + BUF_SIZE_4KiB;
- priv->hw->desc->prepare_tx_desc(desc, 1, BUF_SIZE_8KiB,
- csum_insertion);
-
- entry = (++priv->cur_tx) % txsize;
- desc = priv->dma_tx + entry;
-
- desc->des2 = dma_map_single(priv->device,
- skb->data + BUF_SIZE_8KiB,
- buf2_size, DMA_TO_DEVICE);
- desc->des3 = desc->des2 + BUF_SIZE_4KiB;
- priv->hw->desc->prepare_tx_desc(desc, 0, buf2_size,
- csum_insertion);
- priv->hw->desc->set_tx_owner(desc);
- priv->tx_skbuff[entry] = NULL;
- } else {
- desc->des2 = dma_map_single(priv->device, skb->data,
- nopaged_len, DMA_TO_DEVICE);
- desc->des3 = desc->des2 + BUF_SIZE_4KiB;
- priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len,
- csum_insertion);
- }
- return entry;
-}
-
/**
* stmmac_xmit:
* @skb : the socket buffer
@@ -987,6 +1108,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
int i, csum_insertion = 0;
int nfrags = skb_shinfo(skb)->nr_frags;
struct dma_desc *desc, *first;
+ unsigned int nopaged_len = skb_headlen(skb);
if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) {
if (!netif_queue_stopped(dev)) {
@@ -998,6 +1120,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_BUSY;
}
+ spin_lock(&priv->tx_lock);
+
entry = priv->cur_tx % txsize;
#ifdef STMMAC_XMIT_DEBUG
@@ -1005,7 +1129,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
pr_info("stmmac xmit:\n"
"\tskb addr %p - len: %d - nopaged_len: %d\n"
"\tn_frags: %d - ip_summed: %d - %s gso\n",
- skb, skb->len, skb_headlen(skb), nfrags, skb->ip_summed,
+ skb, skb->len, nopaged_len, nfrags, skb->ip_summed,
!skb_is_gso(skb) ? "isn't" : "is");
#endif
@@ -1018,14 +1142,14 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if ((nfrags > 0) || (skb->len > ETH_FRAME_LEN))
pr_debug("stmmac xmit: skb len: %d, nopaged_len: %d,\n"
"\t\tn_frags: %d, ip_summed: %d\n",
- skb->len, skb_headlen(skb), nfrags, skb->ip_summed);
+ skb->len, nopaged_len, nfrags, skb->ip_summed);
#endif
priv->tx_skbuff[entry] = skb;
- if (unlikely(skb->len >= BUF_SIZE_4KiB)) {
- entry = stmmac_handle_jumbo_frames(skb, dev, csum_insertion);
+
+ if (priv->hw->ring->is_jumbo_frm(skb->len, priv->plat->enh_desc)) {
+ entry = priv->hw->ring->jumbo_frm(priv, skb, csum_insertion);
desc = priv->dma_tx + entry;
} else {
- unsigned int nopaged_len = skb_headlen(skb);
desc->des2 = dma_map_single(priv->device, skb->data,
nopaged_len, DMA_TO_DEVICE);
priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len,
@@ -1033,18 +1157,18 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
}
for (i = 0; i < nfrags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- int len = frag->size;
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ int len = skb_frag_size(frag);
entry = (++priv->cur_tx) % txsize;
desc = priv->dma_tx + entry;
TX_DBG("\t[entry %d] segment len: %d\n", entry, len);
- desc->des2 = dma_map_page(priv->device, frag->page,
- frag->page_offset,
- len, DMA_TO_DEVICE);
+ desc->des2 = skb_frag_dma_map(priv->device, frag, 0, len,
+ DMA_TO_DEVICE);
priv->tx_skbuff[entry] = NULL;
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion);
+ wmb();
priv->hw->desc->set_tx_owner(desc);
}
@@ -1056,6 +1180,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (likely(priv->tm->enable))
priv->hw->desc->clear_tx_ic(desc);
#endif
+
+ wmb();
+
/* To avoid raise condition */
priv->hw->desc->set_tx_owner(first);
@@ -1079,8 +1206,12 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
dev->stats.tx_bytes += skb->len;
+ skb_tx_timestamp(skb);
+
priv->hw->dma->enable_dma_transmission(priv->ioaddr);
+ spin_unlock(&priv->tx_lock);
+
return NETDEV_TX_OK;
}
@@ -1109,13 +1240,13 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv)
DMA_FROM_DEVICE);
(p + entry)->des2 = priv->rx_skbuff_dma[entry];
- if (unlikely(priv->plat->has_gmac)) {
- if (bfsize >= BUF_SIZE_8KiB)
- (p + entry)->des3 =
- (p + entry)->des2 + BUF_SIZE_8KiB;
- }
+
+ if (unlikely(priv->plat->has_gmac))
+ priv->hw->ring->refill_desc3(bfsize, p + entry);
+
RX_DBG(KERN_INFO "\trefill entry #%d\n", entry);
}
+ wmb();
priv->hw->desc->set_rx_owner(p + entry);
}
}
@@ -1193,8 +1324,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
#endif
skb->protocol = eth_type_trans(skb, priv->dev);
- if (unlikely(status == csum_none)) {
- /* always for the old mac 10/100 */
+ if (unlikely(!priv->rx_coe)) {
+ /* No RX COE for old mac10/100 devices */
skb_checksum_none_assert(skb);
netif_receive_skb(skb);
} else {
@@ -1281,7 +1412,7 @@ static int stmmac_config(struct net_device *dev, struct ifmap *map)
}
/**
- * stmmac_multicast_list - entry point for multicast addressing
+ * stmmac_set_rx_mode - entry point for multicast addressing
* @dev : pointer to the device structure
* Description:
* This function is a driver entry point which gets called by the kernel
@@ -1289,7 +1420,7 @@ static int stmmac_config(struct net_device *dev, struct ifmap *map)
* Return value:
* void.
*/
-static void stmmac_multicast_list(struct net_device *dev)
+static void stmmac_set_rx_mode(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
@@ -1319,10 +1450,10 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
return -EBUSY;
}
- if (priv->plat->has_gmac)
+ if (priv->plat->enh_desc)
max_mtu = JUMBO_LEN;
else
- max_mtu = ETH_DATA_LEN;
+ max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN);
if ((new_mtu < 46) || (new_mtu > max_mtu)) {
pr_err("%s: invalid MTU, max MTU is: %d\n", dev->name, max_mtu);
@@ -1405,26 +1536,186 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
if (!priv->phydev)
return -EINVAL;
- spin_lock(&priv->lock);
ret = phy_mii_ioctl(priv->phydev, rq, cmd);
- spin_unlock(&priv->lock);
return ret;
}
-#ifdef STMMAC_VLAN_TAG_USED
-static void stmmac_vlan_rx_register(struct net_device *dev,
- struct vlan_group *grp)
+#ifdef CONFIG_STMMAC_DEBUG_FS
+static struct dentry *stmmac_fs_dir;
+static struct dentry *stmmac_rings_status;
+static struct dentry *stmmac_dma_cap;
+
+static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v)
{
+ struct tmp_s {
+ u64 a;
+ unsigned int b;
+ unsigned int c;
+ };
+ int i;
+ struct net_device *dev = seq->private;
struct stmmac_priv *priv = netdev_priv(dev);
- DBG(probe, INFO, "%s: Setting vlgrp to %p\n", dev->name, grp);
+ seq_printf(seq, "=======================\n");
+ seq_printf(seq, " RX descriptor ring\n");
+ seq_printf(seq, "=======================\n");
- spin_lock(&priv->lock);
- priv->vlgrp = grp;
- spin_unlock(&priv->lock);
+ for (i = 0; i < priv->dma_rx_size; i++) {
+ struct tmp_s *x = (struct tmp_s *)(priv->dma_rx + i);
+ seq_printf(seq, "[%d] DES0=0x%x DES1=0x%x BUF1=0x%x BUF2=0x%x",
+ i, (unsigned int)(x->a),
+ (unsigned int)((x->a) >> 32), x->b, x->c);
+ seq_printf(seq, "\n");
+ }
+
+ seq_printf(seq, "\n");
+ seq_printf(seq, "=======================\n");
+ seq_printf(seq, " TX descriptor ring\n");
+ seq_printf(seq, "=======================\n");
+
+ for (i = 0; i < priv->dma_tx_size; i++) {
+ struct tmp_s *x = (struct tmp_s *)(priv->dma_tx + i);
+ seq_printf(seq, "[%d] DES0=0x%x DES1=0x%x BUF1=0x%x BUF2=0x%x",
+ i, (unsigned int)(x->a),
+ (unsigned int)((x->a) >> 32), x->b, x->c);
+ seq_printf(seq, "\n");
+ }
+
+ return 0;
}
-#endif
+
+static int stmmac_sysfs_ring_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, stmmac_sysfs_ring_read, inode->i_private);
+}
+
+static const struct file_operations stmmac_rings_status_fops = {
+ .owner = THIS_MODULE,
+ .open = stmmac_sysfs_ring_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int stmmac_sysfs_dma_cap_read(struct seq_file *seq, void *v)
+{
+ struct net_device *dev = seq->private;
+ struct stmmac_priv *priv = netdev_priv(dev);
+
+ if (!priv->hw_cap_support) {
+ seq_printf(seq, "DMA HW features not supported\n");
+ return 0;
+ }
+
+ seq_printf(seq, "==============================\n");
+ seq_printf(seq, "\tDMA HW features\n");
+ seq_printf(seq, "==============================\n");
+
+ seq_printf(seq, "\t10/100 Mbps %s\n",
+ (priv->dma_cap.mbps_10_100) ? "Y" : "N");
+ seq_printf(seq, "\t1000 Mbps %s\n",
+ (priv->dma_cap.mbps_1000) ? "Y" : "N");
+ seq_printf(seq, "\tHalf duple %s\n",
+ (priv->dma_cap.half_duplex) ? "Y" : "N");
+ seq_printf(seq, "\tHash Filter: %s\n",
+ (priv->dma_cap.hash_filter) ? "Y" : "N");
+ seq_printf(seq, "\tMultiple MAC address registers: %s\n",
+ (priv->dma_cap.multi_addr) ? "Y" : "N");
+ seq_printf(seq, "\tPCS (TBI/SGMII/RTBI PHY interfatces): %s\n",
+ (priv->dma_cap.pcs) ? "Y" : "N");
+ seq_printf(seq, "\tSMA (MDIO) Interface: %s\n",
+ (priv->dma_cap.sma_mdio) ? "Y" : "N");
+ seq_printf(seq, "\tPMT Remote wake up: %s\n",
+ (priv->dma_cap.pmt_remote_wake_up) ? "Y" : "N");
+ seq_printf(seq, "\tPMT Magic Frame: %s\n",
+ (priv->dma_cap.pmt_magic_frame) ? "Y" : "N");
+ seq_printf(seq, "\tRMON module: %s\n",
+ (priv->dma_cap.rmon) ? "Y" : "N");
+ seq_printf(seq, "\tIEEE 1588-2002 Time Stamp: %s\n",
+ (priv->dma_cap.time_stamp) ? "Y" : "N");
+ seq_printf(seq, "\tIEEE 1588-2008 Advanced Time Stamp:%s\n",
+ (priv->dma_cap.atime_stamp) ? "Y" : "N");
+ seq_printf(seq, "\t802.3az - Energy-Efficient Ethernet (EEE) %s\n",
+ (priv->dma_cap.eee) ? "Y" : "N");
+ seq_printf(seq, "\tAV features: %s\n", (priv->dma_cap.av) ? "Y" : "N");
+ seq_printf(seq, "\tChecksum Offload in TX: %s\n",
+ (priv->dma_cap.tx_coe) ? "Y" : "N");
+ seq_printf(seq, "\tIP Checksum Offload (type1) in RX: %s\n",
+ (priv->dma_cap.rx_coe_type1) ? "Y" : "N");
+ seq_printf(seq, "\tIP Checksum Offload (type2) in RX: %s\n",
+ (priv->dma_cap.rx_coe_type2) ? "Y" : "N");
+ seq_printf(seq, "\tRXFIFO > 2048bytes: %s\n",
+ (priv->dma_cap.rxfifo_over_2048) ? "Y" : "N");
+ seq_printf(seq, "\tNumber of Additional RX channel: %d\n",
+ priv->dma_cap.number_rx_channel);
+ seq_printf(seq, "\tNumber of Additional TX channel: %d\n",
+ priv->dma_cap.number_tx_channel);
+ seq_printf(seq, "\tEnhanced descriptors: %s\n",
+ (priv->dma_cap.enh_desc) ? "Y" : "N");
+
+ return 0;
+}
+
+static int stmmac_sysfs_dma_cap_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, stmmac_sysfs_dma_cap_read, inode->i_private);
+}
+
+static const struct file_operations stmmac_dma_cap_fops = {
+ .owner = THIS_MODULE,
+ .open = stmmac_sysfs_dma_cap_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int stmmac_init_fs(struct net_device *dev)
+{
+ /* Create debugfs entries */
+ stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
+
+ if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
+ pr_err("ERROR %s, debugfs create directory failed\n",
+ STMMAC_RESOURCE_NAME);
+
+ return -ENOMEM;
+ }
+
+ /* Entry to report DMA RX/TX rings */
+ stmmac_rings_status = debugfs_create_file("descriptors_status",
+ S_IRUGO, stmmac_fs_dir, dev,
+ &stmmac_rings_status_fops);
+
+ if (!stmmac_rings_status || IS_ERR(stmmac_rings_status)) {
+ pr_info("ERROR creating stmmac ring debugfs file\n");
+ debugfs_remove(stmmac_fs_dir);
+
+ return -ENOMEM;
+ }
+
+ /* Entry to report the DMA HW features */
+ stmmac_dma_cap = debugfs_create_file("dma_cap", S_IRUGO, stmmac_fs_dir,
+ dev, &stmmac_dma_cap_fops);
+
+ if (!stmmac_dma_cap || IS_ERR(stmmac_dma_cap)) {
+ pr_info("ERROR creating stmmac MMC debugfs file\n");
+ debugfs_remove(stmmac_rings_status);
+ debugfs_remove(stmmac_fs_dir);
+
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void stmmac_exit_fs(void)
+{
+ debugfs_remove(stmmac_rings_status);
+ debugfs_remove(stmmac_dma_cap);
+ debugfs_remove(stmmac_fs_dir);
+}
+#endif /* CONFIG_STMMAC_DEBUG_FS */
static const struct net_device_ops stmmac_netdev_ops = {
.ndo_open = stmmac_open,
@@ -1432,13 +1723,10 @@ static const struct net_device_ops stmmac_netdev_ops = {
.ndo_stop = stmmac_release,
.ndo_change_mtu = stmmac_change_mtu,
.ndo_fix_features = stmmac_fix_features,
- .ndo_set_multicast_list = stmmac_multicast_list,
+ .ndo_set_rx_mode = stmmac_set_rx_mode,
.ndo_tx_timeout = stmmac_tx_timeout,
.ndo_do_ioctl = stmmac_ioctl,
.ndo_set_config = stmmac_config,
-#ifdef STMMAC_VLAN_TAG_USED
- .ndo_vlan_rx_register = stmmac_vlan_rx_register,
-#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = stmmac_poll_controller,
#endif
@@ -1486,6 +1774,7 @@ static int stmmac_probe(struct net_device *dev)
"please, use ifconfig or nwhwconfig!\n");
spin_lock_init(&priv->lock);
+ spin_lock_init(&priv->tx_lock);
ret = register_netdev(dev);
if (ret) {
@@ -1512,95 +1801,27 @@ static int stmmac_mac_device_setup(struct net_device *dev)
struct mac_device_info *device;
- if (priv->plat->has_gmac)
+ if (priv->plat->has_gmac) {
+ dev->priv_flags |= IFF_UNICAST_FLT;
device = dwmac1000_setup(priv->ioaddr);
- else
+ } else {
device = dwmac100_setup(priv->ioaddr);
+ }
if (!device)
return -ENOMEM;
- if (priv->plat->enh_desc) {
- device->desc = &enh_desc_ops;
- pr_info("\tEnhanced descriptor structure\n");
- } else
- device->desc = &ndesc_ops;
-
priv->hw = device;
+ priv->hw->ring = &ring_mode_ops;
if (device_can_wakeup(priv->device)) {
priv->wolopts = WAKE_MAGIC; /* Magic Frame as default */
- enable_irq_wake(dev->irq);
+ enable_irq_wake(priv->wol_irq);
}
return 0;
}
-static int stmmacphy_dvr_probe(struct platform_device *pdev)
-{
- struct plat_stmmacphy_data *plat_dat = pdev->dev.platform_data;
-
- pr_debug("stmmacphy_dvr_probe: added phy for bus %d\n",
- plat_dat->bus_id);
-
- return 0;
-}
-
-static int stmmacphy_dvr_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static struct platform_driver stmmacphy_driver = {
- .driver = {
- .name = PHY_RESOURCE_NAME,
- },
- .probe = stmmacphy_dvr_probe,
- .remove = stmmacphy_dvr_remove,
-};
-
-/**
- * stmmac_associate_phy
- * @dev: pointer to device structure
- * @data: points to the private structure.
- * Description: Scans through all the PHYs we have registered and checks if
- * any are associated with our MAC. If so, then just fill in
- * the blanks in our local context structure
- */
-static int stmmac_associate_phy(struct device *dev, void *data)
-{
- struct stmmac_priv *priv = (struct stmmac_priv *)data;
- struct plat_stmmacphy_data *plat_dat = dev->platform_data;
-
- DBG(probe, DEBUG, "%s: checking phy for bus %d\n", __func__,
- plat_dat->bus_id);
-
- /* Check that this phy is for the MAC being initialised */
- if (priv->plat->bus_id != plat_dat->bus_id)
- return 0;
-
- /* OK, this PHY is connected to the MAC.
- Go ahead and get the parameters */
- DBG(probe, DEBUG, "%s: OK. Found PHY config\n", __func__);
- priv->phy_irq =
- platform_get_irq_byname(to_platform_device(dev), "phyirq");
- DBG(probe, DEBUG, "%s: PHY irq on bus %d is %d\n", __func__,
- plat_dat->bus_id, priv->phy_irq);
-
- /* Override with kernel parameters if supplied XXX CRS XXX
- * this needs to have multiple instances */
- if ((phyaddr >= 0) && (phyaddr <= 31))
- plat_dat->phy_addr = phyaddr;
-
- priv->phy_addr = plat_dat->phy_addr;
- priv->phy_mask = plat_dat->phy_mask;
- priv->phy_interface = plat_dat->interface;
- priv->phy_reset = plat_dat->phy_reset;
-
- DBG(probe, DEBUG, "%s: exiting\n", __func__);
- return 1; /* forces exit of driver_for_each_device() */
-}
-
/**
* stmmac_dvr_probe
* @pdev: platform device pointer
@@ -1663,11 +1884,17 @@ static int stmmac_dvr_probe(struct platform_device *pdev)
priv->ioaddr = addr;
- /* PMT module is not integrated in all the MAC devices. */
- if (plat_dat->pmt) {
- pr_info("\tPMT module supported\n");
- device_set_wakeup_capable(&pdev->dev, 1);
- }
+ /*
+ * On some platforms e.g. SPEAr the wake up irq differs from the mac irq
+ * The external wake up irq can be passed through the platform code
+ * named as "eth_wake_irq"
+ *
+ * In case the wake up interrupt is not passed from the platform
+ * so the driver will continue to use the mac irq (ndev->irq)
+ */
+ priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
+ if (priv->wol_irq == -ENXIO)
+ priv->wol_irq = ndev->irq;
platform_set_drvdata(pdev, ndev);
@@ -1681,7 +1908,7 @@ static int stmmac_dvr_probe(struct platform_device *pdev)
goto out_free_ndev;
}
- /* MAC HW revice detection */
+ /* MAC HW device detection */
ret = stmmac_mac_device_setup(ndev);
if (ret < 0)
goto out_plat_exit;
@@ -1691,14 +1918,10 @@ static int stmmac_dvr_probe(struct platform_device *pdev)
if (ret < 0)
goto out_plat_exit;
- /* associate a PHY - it is provided by another platform bus */
- if (!driver_for_each_device
- (&(stmmacphy_driver.driver), NULL, (void *)priv,
- stmmac_associate_phy)) {
- pr_err("No PHY device is associated with this MAC!\n");
- ret = -ENODEV;
- goto out_unregister;
- }
+ /* Override with kernel parameters if supplied XXX CRS XXX
+ * this needs to have multiple instances */
+ if ((phyaddr >= 0) && (phyaddr <= 31))
+ priv->plat->phy_addr = phyaddr;
pr_info("\t%s - (dev. name: %s - id: %d, IRQ #%d\n"
"\tIO base addr: 0x%p)\n", ndev->name, pdev->name,
@@ -1710,6 +1933,13 @@ static int stmmac_dvr_probe(struct platform_device *pdev)
if (ret < 0)
goto out_unregister;
pr_debug("registered!\n");
+
+#ifdef CONFIG_STMMAC_DEBUG_FS
+ ret = stmmac_init_fs(ndev);
+ if (ret < 0)
+ pr_warning("\tFailed debugFS registration");
+#endif
+
return 0;
out_unregister:
@@ -1762,6 +1992,10 @@ static int stmmac_dvr_remove(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
+#ifdef CONFIG_STMMAC_DEBUG_FS
+ stmmac_exit_fs();
+#endif
+
free_netdev(ndev);
return 0;
@@ -1777,12 +2011,13 @@ static int stmmac_suspend(struct device *dev)
if (!ndev || !netif_running(ndev))
return 0;
+ if (priv->phydev)
+ phy_stop(priv->phydev);
+
spin_lock(&priv->lock);
netif_device_detach(ndev);
netif_stop_queue(ndev);
- if (priv->phydev)
- phy_stop(priv->phydev);
#ifdef CONFIG_STMMAC_TIMER
priv->tm->timer_stop();
@@ -1840,12 +2075,13 @@ static int stmmac_resume(struct device *dev)
#endif
napi_enable(&priv->napi);
- if (priv->phydev)
- phy_start(priv->phydev);
-
netif_start_queue(ndev);
spin_unlock(&priv->lock);
+
+ if (priv->phydev)
+ phy_start(priv->phydev);
+
return 0;
}
@@ -1898,11 +2134,6 @@ static int __init stmmac_init_module(void)
{
int ret;
- if (platform_driver_register(&stmmacphy_driver)) {
- pr_err("No PHY devices registered!\n");
- return -ENODEV;
- }
-
ret = platform_driver_register(&stmmac_driver);
return ret;
}
@@ -1913,7 +2144,6 @@ static int __init stmmac_init_module(void)
*/
static void __exit stmmac_cleanup_module(void)
{
- platform_driver_unregister(&stmmacphy_driver);
platform_driver_unregister(&stmmac_driver);
}
@@ -1925,33 +2155,52 @@ static int __init stmmac_cmdline_opt(char *str)
if (!str || !*str)
return -EINVAL;
while ((opt = strsep(&str, ",")) != NULL) {
- if (!strncmp(opt, "debug:", 6))
- strict_strtoul(opt + 6, 0, (unsigned long *)&debug);
- else if (!strncmp(opt, "phyaddr:", 8))
- strict_strtoul(opt + 8, 0, (unsigned long *)&phyaddr);
- else if (!strncmp(opt, "dma_txsize:", 11))
- strict_strtoul(opt + 11, 0,
- (unsigned long *)&dma_txsize);
- else if (!strncmp(opt, "dma_rxsize:", 11))
- strict_strtoul(opt + 11, 0,
- (unsigned long *)&dma_rxsize);
- else if (!strncmp(opt, "buf_sz:", 7))
- strict_strtoul(opt + 7, 0, (unsigned long *)&buf_sz);
- else if (!strncmp(opt, "tc:", 3))
- strict_strtoul(opt + 3, 0, (unsigned long *)&tc);
- else if (!strncmp(opt, "watchdog:", 9))
- strict_strtoul(opt + 9, 0, (unsigned long *)&watchdog);
- else if (!strncmp(opt, "flow_ctrl:", 10))
- strict_strtoul(opt + 10, 0,
- (unsigned long *)&flow_ctrl);
- else if (!strncmp(opt, "pause:", 6))
- strict_strtoul(opt + 6, 0, (unsigned long *)&pause);
+ if (!strncmp(opt, "debug:", 6)) {
+ if (strict_strtoul(opt + 6, 0, (unsigned long *)&debug))
+ goto err;
+ } else if (!strncmp(opt, "phyaddr:", 8)) {
+ if (strict_strtoul(opt + 8, 0,
+ (unsigned long *)&phyaddr))
+ goto err;
+ } else if (!strncmp(opt, "dma_txsize:", 11)) {
+ if (strict_strtoul(opt + 11, 0,
+ (unsigned long *)&dma_txsize))
+ goto err;
+ } else if (!strncmp(opt, "dma_rxsize:", 11)) {
+ if (strict_strtoul(opt + 11, 0,
+ (unsigned long *)&dma_rxsize))
+ goto err;
+ } else if (!strncmp(opt, "buf_sz:", 7)) {
+ if (strict_strtoul(opt + 7, 0,
+ (unsigned long *)&buf_sz))
+ goto err;
+ } else if (!strncmp(opt, "tc:", 3)) {
+ if (strict_strtoul(opt + 3, 0, (unsigned long *)&tc))
+ goto err;
+ } else if (!strncmp(opt, "watchdog:", 9)) {
+ if (strict_strtoul(opt + 9, 0,
+ (unsigned long *)&watchdog))
+ goto err;
+ } else if (!strncmp(opt, "flow_ctrl:", 10)) {
+ if (strict_strtoul(opt + 10, 0,
+ (unsigned long *)&flow_ctrl))
+ goto err;
+ } else if (!strncmp(opt, "pause:", 6)) {
+ if (strict_strtoul(opt + 6, 0, (unsigned long *)&pause))
+ goto err;
#ifdef CONFIG_STMMAC_TIMER
- else if (!strncmp(opt, "tmrate:", 7))
- strict_strtoul(opt + 7, 0, (unsigned long *)&tmrate);
+ } else if (!strncmp(opt, "tmrate:", 7)) {
+ if (strict_strtoul(opt + 7, 0,
+ (unsigned long *)&tmrate))
+ goto err;
#endif
+ }
}
return 0;
+
+err:
+ pr_err("%s: ERROR broken module parameter conversion", __func__);
+ return -EINVAL;
}
__setup("stmmaceth=", stmmac_cmdline_opt);
diff --git a/drivers/net/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index 234b4068a1fc..9c3b9d5c3411 100644
--- a/drivers/net/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -27,6 +27,7 @@
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/slab.h>
+#include <asm/io.h>
#include "stmmac.h"
@@ -112,9 +113,9 @@ static int stmmac_mdio_reset(struct mii_bus *bus)
struct stmmac_priv *priv = netdev_priv(ndev);
unsigned int mii_address = priv->hw->mii.addr;
- if (priv->phy_reset) {
+ if (priv->plat->mdio_bus_data->phy_reset) {
pr_debug("stmmac_mdio_reset: calling phy_reset\n");
- priv->phy_reset(priv->plat->bsp_priv);
+ priv->plat->mdio_bus_data->phy_reset(priv->plat->bsp_priv);
}
/* This is a workaround for problems with the STE101P PHY.
@@ -137,30 +138,29 @@ int stmmac_mdio_register(struct net_device *ndev)
struct mii_bus *new_bus;
int *irqlist;
struct stmmac_priv *priv = netdev_priv(ndev);
+ struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
int addr, found;
+ if (!mdio_bus_data)
+ return 0;
+
new_bus = mdiobus_alloc();
if (new_bus == NULL)
return -ENOMEM;
- irqlist = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (irqlist == NULL) {
- err = -ENOMEM;
- goto irqlist_alloc_fail;
- }
-
- /* Assign IRQ to phy at address phy_addr */
- if (priv->phy_addr != -1)
- irqlist[priv->phy_addr] = priv->phy_irq;
+ if (mdio_bus_data->irqs)
+ irqlist = mdio_bus_data->irqs;
+ else
+ irqlist = priv->mii_irq;
new_bus->name = "STMMAC MII Bus";
new_bus->read = &stmmac_mdio_read;
new_bus->write = &stmmac_mdio_write;
new_bus->reset = &stmmac_mdio_reset;
- snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", priv->plat->bus_id);
+ snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", mdio_bus_data->bus_id);
new_bus->priv = ndev;
new_bus->irq = irqlist;
- new_bus->phy_mask = priv->phy_mask;
+ new_bus->phy_mask = mdio_bus_data->phy_mask;
new_bus->parent = priv->device;
err = mdiobus_register(new_bus);
if (err != 0) {
@@ -171,18 +171,50 @@ int stmmac_mdio_register(struct net_device *ndev)
priv->mii = new_bus;
found = 0;
- for (addr = 0; addr < 32; addr++) {
+ for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
struct phy_device *phydev = new_bus->phy_map[addr];
if (phydev) {
- if (priv->phy_addr == -1) {
- priv->phy_addr = addr;
- phydev->irq = priv->phy_irq;
- irqlist[addr] = priv->phy_irq;
+ int act = 0;
+ char irq_num[4];
+ char *irq_str;
+
+ /*
+ * If an IRQ was provided to be assigned after
+ * the bus probe, do it here.
+ */
+ if ((mdio_bus_data->irqs == NULL) &&
+ (mdio_bus_data->probed_phy_irq > 0)) {
+ irqlist[addr] = mdio_bus_data->probed_phy_irq;
+ phydev->irq = mdio_bus_data->probed_phy_irq;
}
- pr_info("%s: PHY ID %08x at %d IRQ %d (%s)%s\n",
- ndev->name, phydev->phy_id, addr,
- phydev->irq, dev_name(&phydev->dev),
- (addr == priv->phy_addr) ? " active" : "");
+
+ /*
+ * If we're going to bind the MAC to this PHY bus,
+ * and no PHY number was provided to the MAC,
+ * use the one probed here.
+ */
+ if ((priv->plat->bus_id == mdio_bus_data->bus_id) &&
+ (priv->plat->phy_addr == -1))
+ priv->plat->phy_addr = addr;
+
+ act = (priv->plat->bus_id == mdio_bus_data->bus_id) &&
+ (priv->plat->phy_addr == addr);
+ switch (phydev->irq) {
+ case PHY_POLL:
+ irq_str = "POLL";
+ break;
+ case PHY_IGNORE_INTERRUPT:
+ irq_str = "IGNORE";
+ break;
+ default:
+ sprintf(irq_num, "%d", phydev->irq);
+ irq_str = irq_num;
+ break;
+ }
+ pr_info("%s: PHY ID %08x at %d IRQ %s (%s)%s\n",
+ ndev->name, phydev->phy_id, addr,
+ irq_str, dev_name(&phydev->dev),
+ act ? " active" : "");
found = 1;
}
}
@@ -191,10 +223,9 @@ int stmmac_mdio_register(struct net_device *ndev)
pr_warning("%s: No PHY found\n", ndev->name);
return 0;
+
bus_register_fail:
- kfree(irqlist);
-irqlist_alloc_fail:
- kfree(new_bus);
+ mdiobus_free(new_bus);
return err;
}
@@ -209,7 +240,8 @@ int stmmac_mdio_unregister(struct net_device *ndev)
mdiobus_unregister(priv->mii);
priv->mii->priv = NULL;
- kfree(priv->mii);
+ mdiobus_free(priv->mii);
+ priv->mii = NULL;
return 0;
}
diff --git a/drivers/net/stmmac/stmmac_timer.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
index 2a0e1abde7e7..2a0e1abde7e7 100644
--- a/drivers/net/stmmac/stmmac_timer.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
diff --git a/drivers/net/stmmac/stmmac_timer.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h
index 6863590d184b..6863590d184b 100644
--- a/drivers/net/stmmac/stmmac_timer.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h
diff --git a/drivers/net/ethernet/sun/Kconfig b/drivers/net/ethernet/sun/Kconfig
new file mode 100644
index 000000000000..57bfd8599679
--- /dev/null
+++ b/drivers/net/ethernet/sun/Kconfig
@@ -0,0 +1,88 @@
+#
+# Sun network device configuration
+#
+
+config NET_VENDOR_SUN
+ bool "Sun devices"
+ default y
+ depends on SUN3 || SBUS || PCI || SUN_LDOMS
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say
+ Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Sun network interfaces. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_VENDOR_SUN
+
+config HAPPYMEAL
+ tristate "Sun Happy Meal 10/100baseT support"
+ depends on (SBUS || PCI)
+ select CRC32
+ ---help---
+ This driver supports the "hme" interface present on most Ultra
+ systems and as an option on older Sbus systems. This driver supports
+ both PCI and Sbus devices. This driver also supports the "qfe" quad
+ 100baseT device available in both PCI and Sbus configurations.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sunhme.
+
+config SUNBMAC
+ tristate "Sun BigMAC 10/100baseT support (EXPERIMENTAL)"
+ depends on SBUS && EXPERIMENTAL
+ select CRC32
+ ---help---
+ This driver supports the "be" interface available as an Sbus option.
+ This is Sun's older 100baseT Ethernet device.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sunbmac.
+
+config SUNQE
+ tristate "Sun QuadEthernet support"
+ depends on SBUS
+ select CRC32
+ ---help---
+ This driver supports the "qe" 10baseT Ethernet device, available as
+ an Sbus option. Note that this is not the same as Quad FastEthernet
+ "qfe" which is supported by the Happy Meal driver instead.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sunqe.
+
+config SUNGEM
+ tristate "Sun GEM support"
+ depends on PCI
+ select CRC32
+ select SUNGEM_PHY
+ ---help---
+ Support for the Sun GEM chip, aka Sun GigabitEthernet/P 2.0. See also
+ <http://www.sun.com/products-n-solutions/hardware/docs/pdf/806-3985-10.pdf>.
+
+config CASSINI
+ tristate "Sun Cassini support"
+ depends on PCI
+ select CRC32
+ ---help---
+ Support for the Sun Cassini chip, aka Sun GigaSwift Ethernet. See also
+ <http://www.sun.com/products-n-solutions/hardware/docs/pdf/817-4341-10.pdf>
+
+config SUNVNET
+ tristate "Sun Virtual Network support"
+ depends on SUN_LDOMS
+ ---help---
+ Support for virtual network devices under Sun Logical Domains.
+
+config NIU
+ tristate "Sun Neptune 10Gbit Ethernet support"
+ depends on PCI
+ select CRC32
+ ---help---
+ This enables support for cards based upon Sun's
+ Neptune chipset.
+
+endif # NET_VENDOR_SUN
diff --git a/drivers/net/ethernet/sun/Makefile b/drivers/net/ethernet/sun/Makefile
new file mode 100644
index 000000000000..1e620ff88eba
--- /dev/null
+++ b/drivers/net/ethernet/sun/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the Sun network device drivers.
+#
+
+obj-$(CONFIG_HAPPYMEAL) += sunhme.o
+obj-$(CONFIG_SUNQE) += sunqe.o
+obj-$(CONFIG_SUNBMAC) += sunbmac.o
+obj-$(CONFIG_SUNGEM) += sungem.o
+obj-$(CONFIG_CASSINI) += cassini.o
+obj-$(CONFIG_SUNVNET) += sunvnet.o
+obj-$(CONFIG_NIU) += niu.o
diff --git a/drivers/net/cassini.c b/drivers/net/ethernet/sun/cassini.c
index 22ce03e55b83..fd40988c19a6 100644
--- a/drivers/net/cassini.c
+++ b/drivers/net/ethernet/sun/cassini.c
@@ -75,6 +75,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/vmalloc.h>
#include <linux/ioport.h>
#include <linux/pci.h>
@@ -97,7 +98,7 @@
#include <net/checksum.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/byteorder.h>
@@ -2047,10 +2048,10 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc,
skb->truesize += hlen - swivel;
skb->len += hlen - swivel;
- get_page(page->buffer);
- frag->page = page->buffer;
+ __skb_frag_set_page(frag, page->buffer);
+ __skb_frag_ref(frag);
frag->page_offset = off;
- frag->size = hlen - swivel;
+ skb_frag_size_set(frag, hlen - swivel);
/* any more data? */
if ((words[0] & RX_COMP1_SPLIT_PKT) && ((dlen -= hlen) > 0)) {
@@ -2071,10 +2072,10 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc,
skb->len += hlen;
frag++;
- get_page(page->buffer);
- frag->page = page->buffer;
+ __skb_frag_set_page(frag, page->buffer);
+ __skb_frag_ref(frag);
frag->page_offset = 0;
- frag->size = hlen;
+ skb_frag_size_set(frag, hlen);
RX_USED_ADD(page, hlen + cp->crc_size);
}
@@ -2451,14 +2452,13 @@ static irqreturn_t cas_interruptN(int irq, void *dev_id)
struct net_device *dev = dev_id;
struct cas *cp = netdev_priv(dev);
unsigned long flags;
- int ring;
+ int ring = (irq == cp->pci_irq_INTC) ? 2 : 3;
u32 status = readl(cp->regs + REG_PLUS_INTRN_STATUS(ring));
/* check for shared irq */
if (status == 0)
return IRQ_NONE;
- ring = (irq == cp->pci_irq_INTC) ? 2 : 3;
spin_lock_irqsave(&cp->lock, flags);
if (status & INTR_RX_DONE_ALT) { /* handle rx separately */
#ifdef USE_NAPI
@@ -2826,12 +2826,11 @@ static inline int cas_xmit_tx_ringN(struct cas *cp, int ring,
entry = TX_DESC_NEXT(ring, entry);
for (frag = 0; frag < nr_frags; frag++) {
- skb_frag_t *fragp = &skb_shinfo(skb)->frags[frag];
+ const skb_frag_t *fragp = &skb_shinfo(skb)->frags[frag];
- len = fragp->size;
- mapping = pci_map_page(cp->pdev, fragp->page,
- fragp->page_offset, len,
- PCI_DMA_TODEVICE);
+ len = skb_frag_size(fragp);
+ mapping = skb_frag_dma_map(&cp->pdev->dev, fragp, 0, len,
+ DMA_TO_DEVICE);
tabort = cas_calc_tabort(cp, fragp->page_offset, len);
if (unlikely(tabort)) {
@@ -2842,7 +2841,7 @@ static inline int cas_xmit_tx_ringN(struct cas *cp, int ring,
ctrl, 0);
entry = TX_DESC_NEXT(ring, entry);
- addr = cas_page_map(fragp->page);
+ addr = cas_page_map(skb_frag_page(fragp));
memcpy(tx_tiny_buf(cp, ring, entry),
addr + fragp->page_offset + len - tabort,
tabort);
@@ -4909,7 +4908,7 @@ static const struct net_device_ops cas_netdev_ops = {
.ndo_stop = cas_close,
.ndo_start_xmit = cas_start_xmit,
.ndo_get_stats = cas_get_stats,
- .ndo_set_multicast_list = cas_set_multicast,
+ .ndo_set_rx_mode = cas_set_multicast,
.ndo_do_ioctl = cas_ioctl,
.ndo_tx_timeout = cas_tx_timeout,
.ndo_change_mtu = cas_change_mtu,
diff --git a/drivers/net/cassini.h b/drivers/net/ethernet/sun/cassini.h
index b361424d5f57..b361424d5f57 100644
--- a/drivers/net/cassini.h
+++ b/drivers/net/ethernet/sun/cassini.h
diff --git a/drivers/net/niu.c b/drivers/net/ethernet/sun/niu.c
index cc25bff0bd3b..73c708107a37 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/ethernet/sun/niu.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/netdevice.h>
@@ -16,6 +17,7 @@
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/mii.h>
+#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/ip.h>
@@ -3285,20 +3287,13 @@ static u16 tcam_get_valid_entry_cnt(struct niu *np)
}
static void niu_rx_skb_append(struct sk_buff *skb, struct page *page,
- u32 offset, u32 size)
+ u32 offset, u32 size, u32 truesize)
{
- int i = skb_shinfo(skb)->nr_frags;
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
- frag->page = page;
- frag->page_offset = offset;
- frag->size = size;
+ skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, offset, size);
skb->len += size;
skb->data_len += size;
- skb->truesize += size;
-
- skb_shinfo(skb)->nr_frags = i + 1;
+ skb->truesize += truesize;
}
static unsigned int niu_hash_rxaddr(struct rx_ring_info *rp, u64 a)
@@ -3481,7 +3476,7 @@ static int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np,
} else if (!(val & RCR_ENTRY_MULTI))
append_size = len - skb->len;
- niu_rx_skb_append(skb, page, off, append_size);
+ niu_rx_skb_append(skb, page, off, append_size, rcr_size);
if ((page->index + rp->rbr_block_size) - rcr_size == addr) {
*link = (struct page *) page->mapping;
np->ops->unmap_page(np->device, page->index,
@@ -3599,7 +3594,7 @@ static int release_tx_packet(struct niu *np, struct tx_ring_info *rp, int idx)
tb = &rp->tx_buffs[idx];
BUG_ON(tb->skb != NULL);
np->ops->unmap_page(np->device, tb->mapping,
- skb_shinfo(skb)->frags[i].size,
+ skb_frag_size(&skb_shinfo(skb)->frags[i]),
DMA_TO_DEVICE);
idx = NEXT_TX(rp, idx);
}
@@ -6248,9 +6243,10 @@ static void niu_sync_mac_stats(struct niu *np)
niu_sync_bmac_stats(np);
}
-static void niu_get_rx_stats(struct niu *np)
+static void niu_get_rx_stats(struct niu *np,
+ struct rtnl_link_stats64 *stats)
{
- unsigned long pkts, dropped, errors, bytes;
+ u64 pkts, dropped, errors, bytes;
struct rx_ring_info *rx_rings;
int i;
@@ -6272,15 +6268,16 @@ static void niu_get_rx_stats(struct niu *np)
}
no_rings:
- np->dev->stats.rx_packets = pkts;
- np->dev->stats.rx_bytes = bytes;
- np->dev->stats.rx_dropped = dropped;
- np->dev->stats.rx_errors = errors;
+ stats->rx_packets = pkts;
+ stats->rx_bytes = bytes;
+ stats->rx_dropped = dropped;
+ stats->rx_errors = errors;
}
-static void niu_get_tx_stats(struct niu *np)
+static void niu_get_tx_stats(struct niu *np,
+ struct rtnl_link_stats64 *stats)
{
- unsigned long pkts, errors, bytes;
+ u64 pkts, errors, bytes;
struct tx_ring_info *tx_rings;
int i;
@@ -6299,20 +6296,22 @@ static void niu_get_tx_stats(struct niu *np)
}
no_rings:
- np->dev->stats.tx_packets = pkts;
- np->dev->stats.tx_bytes = bytes;
- np->dev->stats.tx_errors = errors;
+ stats->tx_packets = pkts;
+ stats->tx_bytes = bytes;
+ stats->tx_errors = errors;
}
-static struct net_device_stats *niu_get_stats(struct net_device *dev)
+static struct rtnl_link_stats64 *niu_get_stats(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
{
struct niu *np = netdev_priv(dev);
if (netif_running(dev)) {
- niu_get_rx_stats(np);
- niu_get_tx_stats(np);
+ niu_get_rx_stats(np, stats);
+ niu_get_tx_stats(np, stats);
}
- return &dev->stats;
+
+ return stats;
}
static void niu_load_hash_xmac(struct niu *np, u16 *hash)
@@ -6728,10 +6727,10 @@ static netdev_tx_t niu_start_xmit(struct sk_buff *skb,
}
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- len = frag->size;
- mapping = np->ops->map_page(np->device, frag->page,
+ len = skb_frag_size(frag);
+ mapping = np->ops->map_page(np->device, skb_frag_page(frag),
frag->page_offset, len,
DMA_TO_DEVICE);
@@ -7296,11 +7295,13 @@ static int niu_get_ethtool_tcam_all(struct niu *np,
}
niu_unlock_parent(np, flags);
+ nfc->rule_cnt = cnt;
+
return ret;
}
static int niu_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
- void *rule_locs)
+ u32 *rule_locs)
{
struct niu *np = netdev_priv(dev);
int ret = 0;
@@ -7319,7 +7320,7 @@ static int niu_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
ret = niu_get_ethtool_tcam_entry(np, cmd);
break;
case ETHTOOL_GRXCLSRLALL:
- ret = niu_get_ethtool_tcam_all(np, cmd, (u32 *)rule_locs);
+ ret = niu_get_ethtool_tcam_all(np, cmd, rule_locs);
break;
default:
ret = -EINVAL;
@@ -9196,7 +9197,7 @@ static int __devinit niu_ldg_init(struct niu *np)
first_chan = 0;
for (i = 0; i < port; i++)
- first_chan += parent->rxchan_per_port[port];
+ first_chan += parent->rxchan_per_port[i];
num_chan = parent->rxchan_per_port[port];
for (i = first_chan; i < (first_chan + num_chan); i++) {
@@ -9212,7 +9213,7 @@ static int __devinit niu_ldg_init(struct niu *np)
first_chan = 0;
for (i = 0; i < port; i++)
- first_chan += parent->txchan_per_port[port];
+ first_chan += parent->txchan_per_port[i];
num_chan = parent->txchan_per_port[port];
for (i = first_chan; i < (first_chan + num_chan); i++) {
err = niu_ldg_assign_ldn(np, parent,
@@ -9710,8 +9711,8 @@ static const struct net_device_ops niu_netdev_ops = {
.ndo_open = niu_open,
.ndo_stop = niu_close,
.ndo_start_xmit = niu_start_xmit,
- .ndo_get_stats = niu_get_stats,
- .ndo_set_multicast_list = niu_set_rx_mode,
+ .ndo_get_stats64 = niu_get_stats,
+ .ndo_set_rx_mode = niu_set_rx_mode,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = niu_set_mac_addr,
.ndo_do_ioctl = niu_ioctl,
@@ -9792,7 +9793,7 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev,
goto err_out_disable_pdev;
}
- pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ pos = pci_pcie_cap(pdev);
if (pos <= 0) {
dev_err(&pdev->dev, "Cannot find PCI Express capability, aborting\n");
goto err_out_free_res;
@@ -9847,6 +9848,8 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev,
niu_set_basic_features(dev);
+ dev->priv_flags |= IFF_UNICAST_FLT;
+
np->regs = pci_ioremap_bar(pdev, 0);
if (!np->regs) {
dev_err(&pdev->dev, "Cannot map device registers, aborting\n");
diff --git a/drivers/net/niu.h b/drivers/net/ethernet/sun/niu.h
index 51e177e1860d..51e177e1860d 100644
--- a/drivers/net/niu.h
+++ b/drivers/net/ethernet/sun/niu.h
diff --git a/drivers/net/sunbmac.c b/drivers/net/ethernet/sun/sunbmac.c
index aa4765803a4c..0d8cfd9ea053 100644
--- a/drivers/net/sunbmac.c
+++ b/drivers/net/ethernet/sun/sunbmac.c
@@ -17,6 +17,7 @@
#include <linux/crc32.h>
#include <linux/errno.h>
#include <linux/ethtool.h>
+#include <linux/mii.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
@@ -500,13 +501,13 @@ static int try_next_permutation(struct bigmac *bp, void __iomem *tregs)
/* Reset the PHY. */
bp->sw_bmcr = (BMCR_ISOLATE | BMCR_PDOWN | BMCR_LOOPBACK);
- bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+ bigmac_tcvr_write(bp, tregs, MII_BMCR, bp->sw_bmcr);
bp->sw_bmcr = (BMCR_RESET);
- bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+ bigmac_tcvr_write(bp, tregs, MII_BMCR, bp->sw_bmcr);
timeout = 64;
while (--timeout) {
- bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+ bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, MII_BMCR);
if ((bp->sw_bmcr & BMCR_RESET) == 0)
break;
udelay(20);
@@ -514,11 +515,11 @@ static int try_next_permutation(struct bigmac *bp, void __iomem *tregs)
if (timeout == 0)
printk(KERN_ERR "%s: PHY reset failed.\n", bp->dev->name);
- bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+ bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, MII_BMCR);
/* Now we try 10baseT. */
bp->sw_bmcr &= ~(BMCR_SPEED100);
- bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+ bigmac_tcvr_write(bp, tregs, MII_BMCR, bp->sw_bmcr);
return 0;
}
@@ -534,8 +535,8 @@ static void bigmac_timer(unsigned long data)
bp->timer_ticks++;
if (bp->timer_state == ltrywait) {
- bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMSR);
- bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+ bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, MII_BMSR);
+ bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, MII_BMCR);
if (bp->sw_bmsr & BMSR_LSTATUS) {
printk(KERN_INFO "%s: Link is now up at %s.\n",
bp->dev->name,
@@ -588,18 +589,18 @@ static void bigmac_begin_auto_negotiation(struct bigmac *bp)
int timeout;
/* Grab new software copies of PHY registers. */
- bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMSR);
- bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+ bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, MII_BMSR);
+ bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, MII_BMCR);
/* Reset the PHY. */
bp->sw_bmcr = (BMCR_ISOLATE | BMCR_PDOWN | BMCR_LOOPBACK);
- bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+ bigmac_tcvr_write(bp, tregs, MII_BMCR, bp->sw_bmcr);
bp->sw_bmcr = (BMCR_RESET);
- bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+ bigmac_tcvr_write(bp, tregs, MII_BMCR, bp->sw_bmcr);
timeout = 64;
while (--timeout) {
- bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+ bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, MII_BMCR);
if ((bp->sw_bmcr & BMCR_RESET) == 0)
break;
udelay(20);
@@ -607,11 +608,11 @@ static void bigmac_begin_auto_negotiation(struct bigmac *bp)
if (timeout == 0)
printk(KERN_ERR "%s: PHY reset failed.\n", bp->dev->name);
- bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+ bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, MII_BMCR);
/* First we try 100baseT. */
bp->sw_bmcr |= BMCR_SPEED100;
- bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+ bigmac_tcvr_write(bp, tregs, MII_BMCR, bp->sw_bmcr);
bp->timer_state = ltrywait;
bp->timer_ticks = 0;
@@ -998,7 +999,6 @@ static void bigmac_set_multicast(struct net_device *dev)
struct bigmac *bp = netdev_priv(dev);
void __iomem *bregs = bp->bregs;
struct netdev_hw_addr *ha;
- char *addrs;
int i;
u32 tmp, crc;
@@ -1027,12 +1027,7 @@ static void bigmac_set_multicast(struct net_device *dev)
hash_table[i] = 0;
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
-
- if (!(*addrs & 1))
- continue;
-
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc >>= 26;
hash_table[crc >> 4] |= 1 << (crc & 0xf);
}
@@ -1060,7 +1055,7 @@ static u32 bigmac_get_link(struct net_device *dev)
struct bigmac *bp = netdev_priv(dev);
spin_lock_irq(&bp->lock);
- bp->sw_bmsr = bigmac_tcvr_read(bp, bp->tregs, BIGMAC_BMSR);
+ bp->sw_bmsr = bigmac_tcvr_read(bp, bp->tregs, MII_BMSR);
spin_unlock_irq(&bp->lock);
return (bp->sw_bmsr & BMSR_LSTATUS);
@@ -1076,7 +1071,7 @@ static const struct net_device_ops bigmac_ops = {
.ndo_stop = bigmac_close,
.ndo_start_xmit = bigmac_start_xmit,
.ndo_get_stats = bigmac_get_stats,
- .ndo_set_multicast_list = bigmac_set_multicast,
+ .ndo_set_rx_mode = bigmac_set_multicast,
.ndo_tx_timeout = bigmac_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/sunbmac.h b/drivers/net/ethernet/sun/sunbmac.h
index 4943e975a731..06dd21707353 100644
--- a/drivers/net/sunbmac.h
+++ b/drivers/net/ethernet/sun/sunbmac.h
@@ -223,23 +223,6 @@
#define BIGMAC_PHY_EXTERNAL 0 /* External transceiver */
#define BIGMAC_PHY_INTERNAL 1 /* Internal transceiver */
-/* PHY registers */
-#define BIGMAC_BMCR 0x00 /* Basic mode control register */
-#define BIGMAC_BMSR 0x01 /* Basic mode status register */
-
-/* BMCR bits */
-#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */
-#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */
-#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */
-#define BMCR_SPEED100 0x2000 /* Select 100Mbps */
-#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */
-#define BMCR_RESET 0x8000 /* Reset the DP83840 */
-
-/* BMSR bits */
-#define BMSR_ERCAP 0x0001 /* Ext-reg capability */
-#define BMSR_JCD 0x0002 /* Jabber detected */
-#define BMSR_LSTATUS 0x0004 /* Link status */
-
/* Ring descriptors and such, same as Quad Ethernet. */
struct be_rxd {
u32 rx_flags;
diff --git a/drivers/net/sungem.c b/drivers/net/ethernet/sun/sungem.c
index ab5930099267..ceab215bb4a3 100644
--- a/drivers/net/sungem.c
+++ b/drivers/net/ethernet/sun/sungem.c
@@ -10,25 +10,6 @@
* NAPI and NETPOLL support
* (C) 2004 by Eric Lemoine (eric.lemoine@gmail.com)
*
- * TODO:
- * - Now that the driver was significantly simplified, I need to rework
- * the locking. I'm sure we don't need _2_ spinlocks, and we probably
- * can avoid taking most of them for so long period of time (and schedule
- * instead). The main issues at this point are caused by the netdev layer
- * though:
- *
- * gem_change_mtu() and gem_set_multicast() are called with a read_lock()
- * help by net/core/dev.c, thus they can't schedule. That means they can't
- * call napi_disable() neither, thus force gem_poll() to keep a spinlock
- * where it could have been dropped. change_mtu especially would love also to
- * be able to msleep instead of horrid locked delays when resetting the HW,
- * but that read_lock() makes it impossible, unless I defer it's action to
- * the reset task, which means it'll be asynchronous (won't take effect until
- * the system schedules a bit).
- *
- * Also, it would probably be possible to also remove most of the long-life
- * locking in open/resume code path (gem_reinit_chip) by beeing more careful
- * about when we can start taking interrupts or get xmit() called...
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -57,7 +38,6 @@
#include <linux/workqueue.h>
#include <linux/if_vlan.h>
#include <linux/bitops.h>
-#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/gfp.h>
@@ -79,7 +59,7 @@
#include <asm/pmac_feature.h>
#endif
-#include "sungem_phy.h"
+#include <linux/sungem_phy.h>
#include "sungem.h"
/* Stripping FCS is causing problems, disabled for now */
@@ -95,12 +75,11 @@
SUPPORTED_Pause | SUPPORTED_Autoneg)
#define DRV_NAME "sungem"
-#define DRV_VERSION "0.98"
-#define DRV_RELDATE "8/24/03"
-#define DRV_AUTHOR "David S. Miller (davem@redhat.com)"
+#define DRV_VERSION "1.0"
+#define DRV_AUTHOR "David S. Miller <davem@redhat.com>"
static char version[] __devinitdata =
- DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n";
+ DRV_NAME ".c:v" DRV_VERSION " " DRV_AUTHOR "\n";
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION("Sun GEM Gbit ethernet driver");
@@ -218,6 +197,7 @@ static inline void gem_disable_ints(struct gem *gp)
{
/* Disable all interrupts, including TXDONE */
writel(GREG_STAT_NAPI | GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
+ (void)readl(gp->regs + GREG_IMASK); /* write posting */
}
static void gem_get_cell(struct gem *gp)
@@ -247,6 +227,29 @@ static void gem_put_cell(struct gem *gp)
#endif /* CONFIG_PPC_PMAC */
}
+static inline void gem_netif_stop(struct gem *gp)
+{
+ gp->dev->trans_start = jiffies; /* prevent tx timeout */
+ napi_disable(&gp->napi);
+ netif_tx_disable(gp->dev);
+}
+
+static inline void gem_netif_start(struct gem *gp)
+{
+ /* NOTE: unconditional netif_wake_queue is only
+ * appropriate so long as all callers are assured to
+ * have free tx slots.
+ */
+ netif_wake_queue(gp->dev);
+ napi_enable(&gp->napi);
+}
+
+static void gem_schedule_reset(struct gem *gp)
+{
+ gp->reset_task_pending = 1;
+ schedule_work(&gp->reset_task);
+}
+
static void gem_handle_mif_event(struct gem *gp, u32 reg_val, u32 changed_bits)
{
if (netif_msg_intr(gp))
@@ -604,56 +607,46 @@ static int gem_abnormal_irq(struct net_device *dev, struct gem *gp, u32 gem_stat
gp->dev->name);
dev->stats.rx_errors++;
- goto do_reset;
+ return 1;
}
if (gem_status & GREG_STAT_PCS) {
if (gem_pcs_interrupt(dev, gp, gem_status))
- goto do_reset;
+ return 1;
}
if (gem_status & GREG_STAT_TXMAC) {
if (gem_txmac_interrupt(dev, gp, gem_status))
- goto do_reset;
+ return 1;
}
if (gem_status & GREG_STAT_RXMAC) {
if (gem_rxmac_interrupt(dev, gp, gem_status))
- goto do_reset;
+ return 1;
}
if (gem_status & GREG_STAT_MAC) {
if (gem_mac_interrupt(dev, gp, gem_status))
- goto do_reset;
+ return 1;
}
if (gem_status & GREG_STAT_MIF) {
if (gem_mif_interrupt(dev, gp, gem_status))
- goto do_reset;
+ return 1;
}
if (gem_status & GREG_STAT_PCIERR) {
if (gem_pci_interrupt(dev, gp, gem_status))
- goto do_reset;
+ return 1;
}
return 0;
-
-do_reset:
- gp->reset_task_pending = 1;
- schedule_work(&gp->reset_task);
-
- return 1;
}
static __inline__ void gem_tx(struct net_device *dev, struct gem *gp, u32 gem_status)
{
int entry, limit;
- if (netif_msg_intr(gp))
- printk(KERN_DEBUG "%s: tx interrupt, gem_status: 0x%x\n",
- gp->dev->name, gem_status);
-
entry = gp->tx_old;
limit = ((gem_status & GREG_STAT_TXNR) >> GREG_STAT_TXNR_SHIFT);
while (entry != limit) {
@@ -697,13 +690,27 @@ static __inline__ void gem_tx(struct net_device *dev, struct gem *gp, u32 gem_st
}
dev->stats.tx_packets++;
- dev_kfree_skb_irq(skb);
+ dev_kfree_skb(skb);
}
gp->tx_old = entry;
- if (netif_queue_stopped(dev) &&
- TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))
- netif_wake_queue(dev);
+ /* Need to make the tx_old update visible to gem_start_xmit()
+ * before checking for netif_queue_stopped(). Without the
+ * memory barrier, there is a small possibility that gem_start_xmit()
+ * will miss it and cause the queue to be stopped forever.
+ */
+ smp_mb();
+
+ if (unlikely(netif_queue_stopped(dev) &&
+ TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))) {
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
+
+ __netif_tx_lock(txq, smp_processor_id());
+ if (netif_queue_stopped(dev) &&
+ TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))
+ netif_wake_queue(dev);
+ __netif_tx_unlock(txq);
+ }
}
static __inline__ void gem_post_rxds(struct gem *gp, int limit)
@@ -736,6 +743,21 @@ static __inline__ void gem_post_rxds(struct gem *gp, int limit)
}
}
+#define ALIGNED_RX_SKB_ADDR(addr) \
+ ((((unsigned long)(addr) + (64UL - 1UL)) & ~(64UL - 1UL)) - (unsigned long)(addr))
+static __inline__ struct sk_buff *gem_alloc_skb(struct net_device *dev, int size,
+ gfp_t gfp_flags)
+{
+ struct sk_buff *skb = alloc_skb(size + 64, gfp_flags);
+
+ if (likely(skb)) {
+ unsigned long offset = ALIGNED_RX_SKB_ADDR(skb->data);
+ skb_reserve(skb, offset);
+ skb->dev = dev;
+ }
+ return skb;
+}
+
static int gem_rx(struct gem *gp, int work_to_do)
{
struct net_device *dev = gp->dev;
@@ -799,7 +821,7 @@ static int gem_rx(struct gem *gp, int work_to_do)
if (len > RX_COPY_THRESHOLD) {
struct sk_buff *new_skb;
- new_skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC);
+ new_skb = gem_alloc_skb(dev, RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC);
if (new_skb == NULL) {
drops++;
goto drop_it;
@@ -808,7 +830,6 @@ static int gem_rx(struct gem *gp, int work_to_do)
RX_BUF_ALLOC_SIZE(gp),
PCI_DMA_FROMDEVICE);
gp->rx_skbs[entry] = new_skb;
- new_skb->dev = gp->dev;
skb_put(new_skb, (gp->rx_buf_sz + RX_OFFSET));
rxd->buffer = cpu_to_le64(pci_map_page(gp->pdev,
virt_to_page(new_skb->data),
@@ -820,7 +841,7 @@ static int gem_rx(struct gem *gp, int work_to_do)
/* Trim the original skb for the netif. */
skb_trim(skb, len);
} else {
- struct sk_buff *copy_skb = dev_alloc_skb(len + 2);
+ struct sk_buff *copy_skb = netdev_alloc_skb(dev, len + 2);
if (copy_skb == NULL) {
drops++;
@@ -842,7 +863,7 @@ static int gem_rx(struct gem *gp, int work_to_do)
skb->ip_summed = CHECKSUM_COMPLETE;
skb->protocol = eth_type_trans(skb, gp->dev);
- netif_receive_skb(skb);
+ napi_gro_receive(&gp->napi, skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
@@ -865,28 +886,32 @@ static int gem_poll(struct napi_struct *napi, int budget)
{
struct gem *gp = container_of(napi, struct gem, napi);
struct net_device *dev = gp->dev;
- unsigned long flags;
int work_done;
- /*
- * NAPI locking nightmare: See comment at head of driver
- */
- spin_lock_irqsave(&gp->lock, flags);
-
work_done = 0;
do {
/* Handle anomalies */
- if (gp->status & GREG_STAT_ABNORMAL) {
- if (gem_abnormal_irq(dev, gp, gp->status))
- break;
+ if (unlikely(gp->status & GREG_STAT_ABNORMAL)) {
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
+ int reset;
+
+ /* We run the abnormal interrupt handling code with
+ * the Tx lock. It only resets the Rx portion of the
+ * chip, but we need to guard it against DMA being
+ * restarted by the link poll timer
+ */
+ __netif_tx_lock(txq, smp_processor_id());
+ reset = gem_abnormal_irq(dev, gp, gp->status);
+ __netif_tx_unlock(txq);
+ if (reset) {
+ gem_schedule_reset(gp);
+ napi_complete(napi);
+ return work_done;
+ }
}
/* Run TX completion thread */
- spin_lock(&gp->tx_lock);
gem_tx(dev, gp, gp->status);
- spin_unlock(&gp->tx_lock);
-
- spin_unlock_irqrestore(&gp->lock, flags);
/* Run RX thread. We don't use any locking here,
* code willing to do bad things - like cleaning the
@@ -898,16 +923,12 @@ static int gem_poll(struct napi_struct *napi, int budget)
if (work_done >= budget)
return work_done;
- spin_lock_irqsave(&gp->lock, flags);
-
gp->status = readl(gp->regs + GREG_STAT);
} while (gp->status & GREG_STAT_NAPI);
- __napi_complete(napi);
+ napi_complete(napi);
gem_enable_ints(gp);
- spin_unlock_irqrestore(&gp->lock, flags);
-
return work_done;
}
@@ -915,32 +936,23 @@ static irqreturn_t gem_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct gem *gp = netdev_priv(dev);
- unsigned long flags;
-
- /* Swallow interrupts when shutting the chip down, though
- * that shouldn't happen, we should have done free_irq() at
- * this point...
- */
- if (!gp->running)
- return IRQ_HANDLED;
-
- spin_lock_irqsave(&gp->lock, flags);
if (napi_schedule_prep(&gp->napi)) {
u32 gem_status = readl(gp->regs + GREG_STAT);
- if (gem_status == 0) {
+ if (unlikely(gem_status == 0)) {
napi_enable(&gp->napi);
- spin_unlock_irqrestore(&gp->lock, flags);
return IRQ_NONE;
}
+ if (netif_msg_intr(gp))
+ printk(KERN_DEBUG "%s: gem_interrupt() gem_status: 0x%x\n",
+ gp->dev->name, gem_status);
+
gp->status = gem_status;
gem_disable_ints(gp);
__napi_schedule(&gp->napi);
}
- spin_unlock_irqrestore(&gp->lock, flags);
-
/* If polling was disabled at the time we received that
* interrupt, we may return IRQ_HANDLED here while we
* should return IRQ_NONE. No big deal...
@@ -951,10 +963,11 @@ static irqreturn_t gem_interrupt(int irq, void *dev_id)
#ifdef CONFIG_NET_POLL_CONTROLLER
static void gem_poll_controller(struct net_device *dev)
{
- /* gem_interrupt is safe to reentrance so no need
- * to disable_irq here.
- */
- gem_interrupt(dev->irq, dev);
+ struct gem *gp = netdev_priv(dev);
+
+ disable_irq(gp->pdev->irq);
+ gem_interrupt(gp->pdev->irq, dev);
+ enable_irq(gp->pdev->irq);
}
#endif
@@ -963,10 +976,7 @@ static void gem_tx_timeout(struct net_device *dev)
struct gem *gp = netdev_priv(dev);
netdev_err(dev, "transmit timed out, resetting\n");
- if (!gp->running) {
- netdev_err(dev, "hrm.. hw not running !\n");
- return;
- }
+
netdev_err(dev, "TX_STATE[%08x:%08x:%08x]\n",
readl(gp->regs + TXDMA_CFG),
readl(gp->regs + MAC_TXSTAT),
@@ -976,14 +986,7 @@ static void gem_tx_timeout(struct net_device *dev)
readl(gp->regs + MAC_RXSTAT),
readl(gp->regs + MAC_RXCFG));
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
-
- gp->reset_task_pending = 1;
- schedule_work(&gp->reset_task);
-
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ gem_schedule_reset(gp);
}
static __inline__ int gem_intme(int entry)
@@ -1001,7 +1004,6 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
struct gem *gp = netdev_priv(dev);
int entry;
u64 ctrl;
- unsigned long flags;
ctrl = 0;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
@@ -1013,21 +1015,12 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
(csum_stuff_off << 21));
}
- if (!spin_trylock_irqsave(&gp->tx_lock, flags)) {
- /* Tell upper layer to requeue */
- return NETDEV_TX_LOCKED;
- }
- /* We raced with gem_do_stop() */
- if (!gp->running) {
- spin_unlock_irqrestore(&gp->tx_lock, flags);
- return NETDEV_TX_BUSY;
- }
-
- /* This is a hard error, log it. */
- if (TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1)) {
- netif_stop_queue(dev);
- spin_unlock_irqrestore(&gp->tx_lock, flags);
- netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
+ if (unlikely(TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1))) {
+ /* This is a hard error, log it. */
+ if (!netif_queue_stopped(dev)) {
+ netif_stop_queue(dev);
+ netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
+ }
return NETDEV_TX_BUSY;
}
@@ -1072,16 +1065,14 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
entry = NEXT_TX(entry);
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
- skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
+ const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
u32 len;
dma_addr_t mapping;
u64 this_ctrl;
- len = this_frag->size;
- mapping = pci_map_page(gp->pdev,
- this_frag->page,
- this_frag->page_offset,
- len, PCI_DMA_TODEVICE);
+ len = skb_frag_size(this_frag);
+ mapping = skb_frag_dma_map(&gp->pdev->dev, this_frag,
+ 0, len, DMA_TO_DEVICE);
this_ctrl = ctrl;
if (frag == skb_shinfo(skb)->nr_frags - 1)
this_ctrl |= TXDCTRL_EOF;
@@ -1104,17 +1095,23 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
}
gp->tx_new = entry;
- if (TX_BUFFS_AVAIL(gp) <= (MAX_SKB_FRAGS + 1))
+ if (unlikely(TX_BUFFS_AVAIL(gp) <= (MAX_SKB_FRAGS + 1))) {
netif_stop_queue(dev);
+ /* netif_stop_queue() must be done before checking
+ * checking tx index in TX_BUFFS_AVAIL() below, because
+ * in gem_tx(), we update tx_old before checking for
+ * netif_queue_stopped().
+ */
+ smp_mb();
+ if (TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))
+ netif_wake_queue(dev);
+ }
if (netif_msg_tx_queued(gp))
printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n",
dev->name, entry, skb->len);
mb();
writel(gp->tx_new, gp->regs + TXDMA_KICK);
- spin_unlock_irqrestore(&gp->tx_lock, flags);
-
- dev->trans_start = jiffies; /* NETIF_F_LLTX driver :( */
return NETDEV_TX_OK;
}
@@ -1184,7 +1181,6 @@ static void gem_pcs_reinit_adv(struct gem *gp)
#define STOP_TRIES 32
-/* Must be invoked under gp->lock and gp->tx_lock. */
static void gem_reset(struct gem *gp)
{
int limit;
@@ -1213,7 +1209,6 @@ static void gem_reset(struct gem *gp)
gem_pcs_reinit_adv(gp);
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
static void gem_start_dma(struct gem *gp)
{
u32 val;
@@ -1236,8 +1231,7 @@ static void gem_start_dma(struct gem *gp)
writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK);
}
-/* Must be invoked under gp->lock and gp->tx_lock. DMA won't be
- * actually stopped before about 4ms tho ...
+/* DMA won't be actually stopped before about 4ms tho ...
*/
static void gem_stop_dma(struct gem *gp)
{
@@ -1259,7 +1253,6 @@ static void gem_stop_dma(struct gem *gp)
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
// XXX dbl check what that function should do when called on PCS PHY
static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep)
{
@@ -1319,7 +1312,7 @@ start_aneg:
/* If we are asleep, we don't try to actually setup the PHY, we
* just store the settings
*/
- if (gp->asleep) {
+ if (!netif_device_present(gp->dev)) {
gp->phy_mii.autoneg = gp->want_autoneg = autoneg;
gp->phy_mii.speed = speed;
gp->phy_mii.duplex = duplex;
@@ -1345,13 +1338,12 @@ non_mii:
/* A link-up condition has occurred, initialize and enable the
* rest of the chip.
- *
- * Must be invoked under gp->lock and gp->tx_lock.
*/
static int gem_set_link_modes(struct gem *gp)
{
- u32 val;
+ struct netdev_queue *txq = netdev_get_tx_queue(gp->dev, 0);
int full_duplex, speed, pause;
+ u32 val;
full_duplex = 0;
speed = SPEED_10;
@@ -1375,8 +1367,11 @@ static int gem_set_link_modes(struct gem *gp)
netif_info(gp, link, gp->dev, "Link is up at %d Mbps, %s-duplex\n",
speed, (full_duplex ? "full" : "half"));
- if (!gp->running)
- return 0;
+
+ /* We take the tx queue lock to avoid collisions between
+ * this code, the tx path and the NAPI-driven error path
+ */
+ __netif_tx_lock(txq, smp_processor_id());
val = (MAC_TXCFG_EIPG0 | MAC_TXCFG_NGU);
if (full_duplex) {
@@ -1425,18 +1420,6 @@ static int gem_set_link_modes(struct gem *gp)
pause = 1;
}
- if (netif_msg_link(gp)) {
- if (pause) {
- netdev_info(gp->dev,
- "Pause is enabled (rxfifo: %d off: %d on: %d)\n",
- gp->rx_fifo_sz,
- gp->rx_pause_off,
- gp->rx_pause_on);
- } else {
- netdev_info(gp->dev, "Pause is disabled\n");
- }
- }
-
if (!full_duplex)
writel(512, gp->regs + MAC_STIME);
else
@@ -1450,10 +1433,23 @@ static int gem_set_link_modes(struct gem *gp)
gem_start_dma(gp);
+ __netif_tx_unlock(txq);
+
+ if (netif_msg_link(gp)) {
+ if (pause) {
+ netdev_info(gp->dev,
+ "Pause is enabled (rxfifo: %d off: %d on: %d)\n",
+ gp->rx_fifo_sz,
+ gp->rx_pause_off,
+ gp->rx_pause_on);
+ } else {
+ netdev_info(gp->dev, "Pause is disabled\n");
+ }
+ }
+
return 0;
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
static int gem_mdio_link_not_up(struct gem *gp)
{
switch (gp->lstate) {
@@ -1501,20 +1497,12 @@ static int gem_mdio_link_not_up(struct gem *gp)
static void gem_link_timer(unsigned long data)
{
struct gem *gp = (struct gem *) data;
+ struct net_device *dev = gp->dev;
int restart_aneg = 0;
- if (gp->asleep)
- return;
-
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
- gem_get_cell(gp);
-
- /* If the reset task is still pending, we just
- * reschedule the link timer
- */
+ /* There's no point doing anything if we're going to be reset */
if (gp->reset_task_pending)
- goto restart;
+ return;
if (gp->phy_type == phy_serialink ||
gp->phy_type == phy_serdes) {
@@ -1528,7 +1516,7 @@ static void gem_link_timer(unsigned long data)
goto restart;
gp->lstate = link_up;
- netif_carrier_on(gp->dev);
+ netif_carrier_on(dev);
(void)gem_set_link_modes(gp);
}
goto restart;
@@ -1544,12 +1532,12 @@ static void gem_link_timer(unsigned long data)
gp->last_forced_speed = gp->phy_mii.speed;
gp->timer_ticks = 5;
if (netif_msg_link(gp))
- netdev_info(gp->dev,
+ netdev_info(dev,
"Got link after fallback, retrying autoneg once...\n");
gp->phy_mii.def->ops->setup_aneg(&gp->phy_mii, gp->phy_mii.advertising);
} else if (gp->lstate != link_up) {
gp->lstate = link_up;
- netif_carrier_on(gp->dev);
+ netif_carrier_on(dev);
if (gem_set_link_modes(gp))
restart_aneg = 1;
}
@@ -1559,11 +1547,11 @@ static void gem_link_timer(unsigned long data)
*/
if (gp->lstate == link_up) {
gp->lstate = link_down;
- netif_info(gp, link, gp->dev, "Link down\n");
- netif_carrier_off(gp->dev);
- gp->reset_task_pending = 1;
- schedule_work(&gp->reset_task);
- restart_aneg = 1;
+ netif_info(gp, link, dev, "Link down\n");
+ netif_carrier_off(dev);
+ gem_schedule_reset(gp);
+ /* The reset task will restart the timer */
+ return;
} else if (++gp->timer_ticks > 10) {
if (found_mii_phy(gp))
restart_aneg = gem_mdio_link_not_up(gp);
@@ -1573,17 +1561,12 @@ static void gem_link_timer(unsigned long data)
}
if (restart_aneg) {
gem_begin_auto_negotiation(gp, NULL);
- goto out_unlock;
+ return;
}
restart:
mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
-out_unlock:
- gem_put_cell(gp);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
static void gem_clean_rings(struct gem *gp)
{
struct gem_init_block *gb = gp->init_block;
@@ -1634,7 +1617,6 @@ static void gem_clean_rings(struct gem *gp)
}
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
static void gem_init_rings(struct gem *gp)
{
struct gem_init_block *gb = gp->init_block;
@@ -1653,7 +1635,7 @@ static void gem_init_rings(struct gem *gp)
struct sk_buff *skb;
struct gem_rxd *rxd = &gb->rxd[i];
- skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC);
+ skb = gem_alloc_skb(dev, RX_BUF_ALLOC_SIZE(gp), GFP_KERNEL);
if (!skb) {
rxd->buffer = 0;
rxd->status_word = 0;
@@ -1661,7 +1643,6 @@ static void gem_init_rings(struct gem *gp)
}
gp->rx_skbs[i] = skb;
- skb->dev = dev;
skb_put(skb, (gp->rx_buf_sz + RX_OFFSET));
dma_addr = pci_map_page(gp->pdev,
virt_to_page(skb->data),
@@ -1737,8 +1718,8 @@ static void gem_init_phy(struct gem *gp)
if (gp->phy_type == phy_mii_mdio0 ||
gp->phy_type == phy_mii_mdio1) {
- // XXX check for errors
- mii_phy_probe(&gp->phy_mii, gp->mii_phy_addr);
+ /* Reset and detect MII PHY */
+ sungem_phy_probe(&gp->phy_mii, gp->mii_phy_addr);
/* Init PHY */
if (gp->phy_mii.def && gp->phy_mii.def->ops->init)
@@ -1753,13 +1734,15 @@ static void gem_init_phy(struct gem *gp)
gp->lstate = link_down;
netif_carrier_off(gp->dev);
- /* Can I advertise gigabit here ? I'd need BCM PHY docs... */
- spin_lock_irq(&gp->lock);
+ /* Print things out */
+ if (gp->phy_type == phy_mii_mdio0 ||
+ gp->phy_type == phy_mii_mdio1)
+ netdev_info(gp->dev, "Found %s PHY\n",
+ gp->phy_mii.def ? gp->phy_mii.def->name : "no");
+
gem_begin_auto_negotiation(gp, NULL);
- spin_unlock_irq(&gp->lock);
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
static void gem_init_dma(struct gem *gp)
{
u64 desc_dma = (u64) gp->gblock_dvma;
@@ -1797,7 +1780,6 @@ static void gem_init_dma(struct gem *gp)
gp->regs + RXDMA_BLANK);
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
static u32 gem_setup_multicast(struct gem *gp)
{
u32 rxcfg = 0;
@@ -1818,12 +1800,7 @@ static u32 gem_setup_multicast(struct gem *gp)
memset(hash_table, 0, sizeof(hash_table));
netdev_for_each_mc_addr(ha, gp->dev) {
- char *addrs = ha->addr;
-
- if (!(*addrs & 1))
- continue;
-
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc >>= 24;
hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf));
}
@@ -1835,7 +1812,6 @@ static u32 gem_setup_multicast(struct gem *gp)
return rxcfg;
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
static void gem_init_mac(struct gem *gp)
{
unsigned char *e = &gp->dev->dev_addr[0];
@@ -1918,7 +1894,6 @@ static void gem_init_mac(struct gem *gp)
writel(0, gp->regs + WOL_WAKECSR);
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
static void gem_init_pause_thresholds(struct gem *gp)
{
u32 cfg;
@@ -2079,7 +2054,6 @@ static int gem_check_invariants(struct gem *gp)
return 0;
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
static void gem_reinit_chip(struct gem *gp)
{
/* Reset the chip */
@@ -2100,11 +2074,9 @@ static void gem_reinit_chip(struct gem *gp)
}
-/* Must be invoked with no lock held. */
static void gem_stop_phy(struct gem *gp, int wol)
{
u32 mifcfg;
- unsigned long flags;
/* Let the chip settle down a bit, it seems that helps
* for sleep mode on some models
@@ -2150,15 +2122,9 @@ static void gem_stop_phy(struct gem *gp, int wol)
writel(0, gp->regs + RXDMA_CFG);
if (!wol) {
- spin_lock_irqsave(&gp->lock, flags);
- spin_lock(&gp->tx_lock);
gem_reset(gp);
writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST);
writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irqrestore(&gp->lock, flags);
-
- /* No need to take the lock here */
if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
gp->phy_mii.def->ops->suspend(&gp->phy_mii);
@@ -2175,54 +2141,55 @@ static void gem_stop_phy(struct gem *gp, int wol)
}
}
-
static int gem_do_start(struct net_device *dev)
{
struct gem *gp = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&gp->lock, flags);
- spin_lock(&gp->tx_lock);
+ int rc;
/* Enable the cell */
gem_get_cell(gp);
- /* Init & setup chip hardware */
- gem_reinit_chip(gp);
-
- gp->running = 1;
-
- napi_enable(&gp->napi);
+ /* Make sure PCI access and bus master are enabled */
+ rc = pci_enable_device(gp->pdev);
+ if (rc) {
+ netdev_err(dev, "Failed to enable chip on PCI bus !\n");
- if (gp->lstate == link_up) {
- netif_carrier_on(gp->dev);
- gem_set_link_modes(gp);
+ /* Put cell and forget it for now, it will be considered as
+ * still asleep, a new sleep cycle may bring it back
+ */
+ gem_put_cell(gp);
+ return -ENXIO;
}
+ pci_set_master(gp->pdev);
- netif_wake_queue(gp->dev);
-
- spin_unlock(&gp->tx_lock);
- spin_unlock_irqrestore(&gp->lock, flags);
+ /* Init & setup chip hardware */
+ gem_reinit_chip(gp);
- if (request_irq(gp->pdev->irq, gem_interrupt,
- IRQF_SHARED, dev->name, (void *)dev)) {
+ /* An interrupt might come in handy */
+ rc = request_irq(gp->pdev->irq, gem_interrupt,
+ IRQF_SHARED, dev->name, (void *)dev);
+ if (rc) {
netdev_err(dev, "failed to request irq !\n");
- spin_lock_irqsave(&gp->lock, flags);
- spin_lock(&gp->tx_lock);
-
- napi_disable(&gp->napi);
-
- gp->running = 0;
gem_reset(gp);
gem_clean_rings(gp);
gem_put_cell(gp);
+ return rc;
+ }
- spin_unlock(&gp->tx_lock);
- spin_unlock_irqrestore(&gp->lock, flags);
+ /* Mark us as attached again if we come from resume(), this has
+ * no effect if we weren't detatched and needs to be done now.
+ */
+ netif_device_attach(dev);
- return -EAGAIN;
- }
+ /* Restart NAPI & queues */
+ gem_netif_start(gp);
+
+ /* Detect & init PHY, start autoneg etc... this will
+ * eventually result in starting DMA operations when
+ * the link is up
+ */
+ gem_init_phy(gp);
return 0;
}
@@ -2230,22 +2197,30 @@ static int gem_do_start(struct net_device *dev)
static void gem_do_stop(struct net_device *dev, int wol)
{
struct gem *gp = netdev_priv(dev);
- unsigned long flags;
- spin_lock_irqsave(&gp->lock, flags);
- spin_lock(&gp->tx_lock);
+ /* Stop NAPI and stop tx queue */
+ gem_netif_stop(gp);
- gp->running = 0;
-
- /* Stop netif queue */
- netif_stop_queue(dev);
-
- /* Make sure ints are disabled */
+ /* Make sure ints are disabled. We don't care about
+ * synchronizing as NAPI is disabled, thus a stray
+ * interrupt will do nothing bad (our irq handler
+ * just schedules NAPI)
+ */
gem_disable_ints(gp);
- /* We can drop the lock now */
- spin_unlock(&gp->tx_lock);
- spin_unlock_irqrestore(&gp->lock, flags);
+ /* Stop the link timer */
+ del_timer_sync(&gp->link_timer);
+
+ /* We cannot cancel the reset task while holding the
+ * rtnl lock, we'd get an A->B / B->A deadlock stituation
+ * if we did. This is not an issue however as the reset
+ * task is synchronized vs. us (rtnl_lock) and will do
+ * nothing if the device is down or suspended. We do
+ * still clear reset_task_pending to avoid a spurrious
+ * reset later on in case we do resume before it gets
+ * scheduled.
+ */
+ gp->reset_task_pending = 0;
/* If we are going to sleep with WOL */
gem_stop_dma(gp);
@@ -2260,79 +2235,79 @@ static void gem_do_stop(struct net_device *dev, int wol)
/* No irq needed anymore */
free_irq(gp->pdev->irq, (void *) dev);
+ /* Shut the PHY down eventually and setup WOL */
+ gem_stop_phy(gp, wol);
+
+ /* Make sure bus master is disabled */
+ pci_disable_device(gp->pdev);
+
/* Cell not needed neither if no WOL */
- if (!wol) {
- spin_lock_irqsave(&gp->lock, flags);
+ if (!wol)
gem_put_cell(gp);
- spin_unlock_irqrestore(&gp->lock, flags);
- }
}
static void gem_reset_task(struct work_struct *work)
{
struct gem *gp = container_of(work, struct gem, reset_task);
- mutex_lock(&gp->pm_mutex);
+ /* Lock out the network stack (essentially shield ourselves
+ * against a racing open, close, control call, or suspend
+ */
+ rtnl_lock();
- if (gp->opened)
- napi_disable(&gp->napi);
+ /* Skip the reset task if suspended or closed, or if it's
+ * been cancelled by gem_do_stop (see comment there)
+ */
+ if (!netif_device_present(gp->dev) ||
+ !netif_running(gp->dev) ||
+ !gp->reset_task_pending) {
+ rtnl_unlock();
+ return;
+ }
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ /* Stop the link timer */
+ del_timer_sync(&gp->link_timer);
- if (gp->running) {
- netif_stop_queue(gp->dev);
+ /* Stop NAPI and tx */
+ gem_netif_stop(gp);
- /* Reset the chip & rings */
- gem_reinit_chip(gp);
- if (gp->lstate == link_up)
- gem_set_link_modes(gp);
- netif_wake_queue(gp->dev);
- }
+ /* Reset the chip & rings */
+ gem_reinit_chip(gp);
+ if (gp->lstate == link_up)
+ gem_set_link_modes(gp);
- gp->reset_task_pending = 0;
+ /* Restart NAPI and Tx */
+ gem_netif_start(gp);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ /* We are back ! */
+ gp->reset_task_pending = 0;
- if (gp->opened)
- napi_enable(&gp->napi);
+ /* If the link is not up, restart autoneg, else restart the
+ * polling timer
+ */
+ if (gp->lstate != link_up)
+ gem_begin_auto_negotiation(gp, NULL);
+ else
+ mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
- mutex_unlock(&gp->pm_mutex);
+ rtnl_unlock();
}
-
static int gem_open(struct net_device *dev)
{
- struct gem *gp = netdev_priv(dev);
- int rc = 0;
-
- mutex_lock(&gp->pm_mutex);
-
- /* We need the cell enabled */
- if (!gp->asleep)
- rc = gem_do_start(dev);
- gp->opened = (rc == 0);
-
- mutex_unlock(&gp->pm_mutex);
-
- return rc;
+ /* We allow open while suspended, we just do nothing,
+ * the chip will be initialized in resume()
+ */
+ if (netif_device_present(dev))
+ return gem_do_start(dev);
+ return 0;
}
static int gem_close(struct net_device *dev)
{
- struct gem *gp = netdev_priv(dev);
-
- mutex_lock(&gp->pm_mutex);
-
- napi_disable(&gp->napi);
-
- gp->opened = 0;
- if (!gp->asleep)
+ if (netif_device_present(dev))
gem_do_stop(dev, 0);
- mutex_unlock(&gp->pm_mutex);
-
return 0;
}
@@ -2341,59 +2316,35 @@ static int gem_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct gem *gp = netdev_priv(dev);
- unsigned long flags;
- mutex_lock(&gp->pm_mutex);
-
- netdev_info(dev, "suspending, WakeOnLan %s\n",
- (gp->wake_on_lan && gp->opened) ? "enabled" : "disabled");
-
- /* Keep the cell enabled during the entire operation */
- spin_lock_irqsave(&gp->lock, flags);
- spin_lock(&gp->tx_lock);
- gem_get_cell(gp);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irqrestore(&gp->lock, flags);
-
- /* If the driver is opened, we stop the MAC */
- if (gp->opened) {
- napi_disable(&gp->napi);
+ /* Lock the network stack first to avoid racing with open/close,
+ * reset task and setting calls
+ */
+ rtnl_lock();
- /* Stop traffic, mark us closed */
+ /* Not running, mark ourselves non-present, no need for
+ * a lock here
+ */
+ if (!netif_running(dev)) {
netif_device_detach(dev);
+ rtnl_unlock();
+ return 0;
+ }
+ netdev_info(dev, "suspending, WakeOnLan %s\n",
+ (gp->wake_on_lan && netif_running(dev)) ?
+ "enabled" : "disabled");
- /* Switch off MAC, remember WOL setting */
- gp->asleep_wol = gp->wake_on_lan;
- gem_do_stop(dev, gp->asleep_wol);
- } else
- gp->asleep_wol = 0;
-
- /* Mark us asleep */
- gp->asleep = 1;
- wmb();
-
- /* Stop the link timer */
- del_timer_sync(&gp->link_timer);
-
- /* Now we release the mutex to not block the reset task who
- * can take it too. We are marked asleep, so there will be no
- * conflict here
+ /* Tell the network stack we're gone. gem_do_stop() below will
+ * synchronize with TX, stop NAPI etc...
*/
- mutex_unlock(&gp->pm_mutex);
+ netif_device_detach(dev);
- /* Wait for the pending reset task to complete */
- flush_work_sync(&gp->reset_task);
+ /* Switch off chip, remember WOL setting */
+ gp->asleep_wol = gp->wake_on_lan;
+ gem_do_stop(dev, gp->asleep_wol);
- /* Shut the PHY down eventually and setup WOL */
- gem_stop_phy(gp, gp->asleep_wol);
-
- /* Make sure bus master is disabled */
- pci_disable_device(gp->pdev);
-
- /* Release the cell, no need to take a lock at this point since
- * nothing else can happen now
- */
- gem_put_cell(gp);
+ /* Unlock the network stack */
+ rtnl_unlock();
return 0;
}
@@ -2402,53 +2353,23 @@ static int gem_resume(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct gem *gp = netdev_priv(dev);
- unsigned long flags;
-
- netdev_info(dev, "resuming\n");
- mutex_lock(&gp->pm_mutex);
+ /* See locking comment in gem_suspend */
+ rtnl_lock();
- /* Keep the cell enabled during the entire operation, no need to
- * take a lock here tho since nothing else can happen while we are
- * marked asleep
+ /* Not running, mark ourselves present, no need for
+ * a lock here
*/
- gem_get_cell(gp);
-
- /* Make sure PCI access and bus master are enabled */
- if (pci_enable_device(gp->pdev)) {
- netdev_err(dev, "Can't re-enable chip !\n");
- /* Put cell and forget it for now, it will be considered as
- * still asleep, a new sleep cycle may bring it back
- */
- gem_put_cell(gp);
- mutex_unlock(&gp->pm_mutex);
+ if (!netif_running(dev)) {
+ netif_device_attach(dev);
+ rtnl_unlock();
return 0;
}
- pci_set_master(gp->pdev);
-
- /* Reset everything */
- gem_reset(gp);
-
- /* Mark us woken up */
- gp->asleep = 0;
- wmb();
- /* Bring the PHY back. Again, lock is useless at this point as
- * nothing can be happening until we restart the whole thing
+ /* Restart chip. If that fails there isn't much we can do, we
+ * leave things stopped.
*/
- gem_init_phy(gp);
-
- /* If we were opened, bring everything back */
- if (gp->opened) {
- /* Restart MAC */
- gem_do_start(dev);
-
- /* Re-attach net device */
- netif_device_attach(dev);
- }
-
- spin_lock_irqsave(&gp->lock, flags);
- spin_lock(&gp->tx_lock);
+ gem_do_start(dev);
/* If we had WOL enabled, the cell clock was never turned off during
* sleep, so we end up beeing unbalanced. Fix that here
@@ -2456,15 +2377,8 @@ static int gem_resume(struct pci_dev *pdev)
if (gp->asleep_wol)
gem_put_cell(gp);
- /* This function doesn't need to hold the cell, it will be held if the
- * driver is open by gem_do_start().
- */
- gem_put_cell(gp);
-
- spin_unlock(&gp->tx_lock);
- spin_unlock_irqrestore(&gp->lock, flags);
-
- mutex_unlock(&gp->pm_mutex);
+ /* Unlock the network stack */
+ rtnl_unlock();
return 0;
}
@@ -2474,33 +2388,35 @@ static struct net_device_stats *gem_get_stats(struct net_device *dev)
{
struct gem *gp = netdev_priv(dev);
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
-
/* I have seen this being called while the PM was in progress,
- * so we shield against this
+ * so we shield against this. Let's also not poke at registers
+ * while the reset task is going on.
+ *
+ * TODO: Move stats collection elsewhere (link timer ?) and
+ * make this a nop to avoid all those synchro issues
*/
- if (gp->running) {
- dev->stats.rx_crc_errors += readl(gp->regs + MAC_FCSERR);
- writel(0, gp->regs + MAC_FCSERR);
+ if (!netif_device_present(dev) || !netif_running(dev))
+ goto bail;
- dev->stats.rx_frame_errors += readl(gp->regs + MAC_AERR);
- writel(0, gp->regs + MAC_AERR);
+ /* Better safe than sorry... */
+ if (WARN_ON(!gp->cell_enabled))
+ goto bail;
- dev->stats.rx_length_errors += readl(gp->regs + MAC_LERR);
- writel(0, gp->regs + MAC_LERR);
+ dev->stats.rx_crc_errors += readl(gp->regs + MAC_FCSERR);
+ writel(0, gp->regs + MAC_FCSERR);
- dev->stats.tx_aborted_errors += readl(gp->regs + MAC_ECOLL);
- dev->stats.collisions +=
- (readl(gp->regs + MAC_ECOLL) +
- readl(gp->regs + MAC_LCOLL));
- writel(0, gp->regs + MAC_ECOLL);
- writel(0, gp->regs + MAC_LCOLL);
- }
+ dev->stats.rx_frame_errors += readl(gp->regs + MAC_AERR);
+ writel(0, gp->regs + MAC_AERR);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ dev->stats.rx_length_errors += readl(gp->regs + MAC_LERR);
+ writel(0, gp->regs + MAC_LERR);
+ dev->stats.tx_aborted_errors += readl(gp->regs + MAC_ECOLL);
+ dev->stats.collisions +=
+ (readl(gp->regs + MAC_ECOLL) + readl(gp->regs + MAC_LCOLL));
+ writel(0, gp->regs + MAC_ECOLL);
+ writel(0, gp->regs + MAC_LCOLL);
+ bail:
return &dev->stats;
}
@@ -2513,22 +2429,19 @@ static int gem_set_mac_address(struct net_device *dev, void *addr)
if (!is_valid_ether_addr(macaddr->sa_data))
return -EADDRNOTAVAIL;
- if (!netif_running(dev) || !netif_device_present(dev)) {
- /* We'll just catch it later when the
- * device is up'd or resumed.
- */
- memcpy(dev->dev_addr, macaddr->sa_data, dev->addr_len);
+ memcpy(dev->dev_addr, macaddr->sa_data, dev->addr_len);
+
+ /* We'll just catch it later when the device is up'd or resumed */
+ if (!netif_running(dev) || !netif_device_present(dev))
return 0;
- }
- mutex_lock(&gp->pm_mutex);
- memcpy(dev->dev_addr, macaddr->sa_data, dev->addr_len);
- if (gp->running) {
- writel((e[4] << 8) | e[5], gp->regs + MAC_ADDR0);
- writel((e[2] << 8) | e[3], gp->regs + MAC_ADDR1);
- writel((e[0] << 8) | e[1], gp->regs + MAC_ADDR2);
- }
- mutex_unlock(&gp->pm_mutex);
+ /* Better safe than sorry... */
+ if (WARN_ON(!gp->cell_enabled))
+ return 0;
+
+ writel((e[4] << 8) | e[5], gp->regs + MAC_ADDR0);
+ writel((e[2] << 8) | e[3], gp->regs + MAC_ADDR1);
+ writel((e[0] << 8) | e[1], gp->regs + MAC_ADDR2);
return 0;
}
@@ -2539,14 +2452,12 @@ static void gem_set_multicast(struct net_device *dev)
u32 rxcfg, rxcfg_new;
int limit = 10000;
+ if (!netif_running(dev) || !netif_device_present(dev))
+ return;
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
-
- if (!gp->running)
- goto bail;
-
- netif_stop_queue(dev);
+ /* Better safe than sorry... */
+ if (gp->reset_task_pending || WARN_ON(!gp->cell_enabled))
+ return;
rxcfg = readl(gp->regs + MAC_RXCFG);
rxcfg_new = gem_setup_multicast(gp);
@@ -2566,12 +2477,6 @@ static void gem_set_multicast(struct net_device *dev)
rxcfg |= rxcfg_new;
writel(rxcfg, gp->regs + MAC_RXCFG);
-
- netif_wake_queue(dev);
-
- bail:
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
}
/* Jumbo-grams don't seem to work :-( */
@@ -2589,26 +2494,21 @@ static int gem_change_mtu(struct net_device *dev, int new_mtu)
if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
return -EINVAL;
- if (!netif_running(dev) || !netif_device_present(dev)) {
- /* We'll just catch it later when the
- * device is up'd or resumed.
- */
- dev->mtu = new_mtu;
+ dev->mtu = new_mtu;
+
+ /* We'll just catch it later when the device is up'd or resumed */
+ if (!netif_running(dev) || !netif_device_present(dev))
return 0;
- }
- mutex_lock(&gp->pm_mutex);
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
- dev->mtu = new_mtu;
- if (gp->running) {
- gem_reinit_chip(gp);
- if (gp->lstate == link_up)
- gem_set_link_modes(gp);
- }
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
- mutex_unlock(&gp->pm_mutex);
+ /* Better safe than sorry... */
+ if (WARN_ON(!gp->cell_enabled))
+ return 0;
+
+ gem_netif_stop(gp);
+ gem_reinit_chip(gp);
+ if (gp->lstate == link_up)
+ gem_set_link_modes(gp);
+ gem_netif_start(gp);
return 0;
}
@@ -2640,7 +2540,6 @@ static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
cmd->phy_address = 0; /* XXX fixed PHYAD */
/* Return current PHY settings */
- spin_lock_irq(&gp->lock);
cmd->autoneg = gp->want_autoneg;
ethtool_cmd_speed_set(cmd, gp->phy_mii.speed);
cmd->duplex = gp->phy_mii.duplex;
@@ -2652,7 +2551,6 @@ static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
*/
if (cmd->advertising == 0)
cmd->advertising = cmd->supported;
- spin_unlock_irq(&gp->lock);
} else { // XXX PCS ?
cmd->supported =
(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
@@ -2706,11 +2604,10 @@ static int gem_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
return -EINVAL;
/* Apply settings and restart link process. */
- spin_lock_irq(&gp->lock);
- gem_get_cell(gp);
- gem_begin_auto_negotiation(gp, cmd);
- gem_put_cell(gp);
- spin_unlock_irq(&gp->lock);
+ if (netif_device_present(gp->dev)) {
+ del_timer_sync(&gp->link_timer);
+ gem_begin_auto_negotiation(gp, cmd);
+ }
return 0;
}
@@ -2722,12 +2619,11 @@ static int gem_nway_reset(struct net_device *dev)
if (!gp->want_autoneg)
return -EINVAL;
- /* Restart link process. */
- spin_lock_irq(&gp->lock);
- gem_get_cell(gp);
- gem_begin_auto_negotiation(gp, NULL);
- gem_put_cell(gp);
- spin_unlock_irq(&gp->lock);
+ /* Restart link process */
+ if (netif_device_present(gp->dev)) {
+ del_timer_sync(&gp->link_timer);
+ gem_begin_auto_negotiation(gp, NULL);
+ }
return 0;
}
@@ -2791,16 +2687,11 @@ static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct gem *gp = netdev_priv(dev);
struct mii_ioctl_data *data = if_mii(ifr);
int rc = -EOPNOTSUPP;
- unsigned long flags;
- /* Hold the PM mutex while doing ioctl's or we may collide
- * with power management.
+ /* For SIOCGMIIREG and SIOCSMIIREG the core checks for us that
+ * netif_device_present() is true and holds rtnl_lock for us
+ * so we have nothing to worry about
*/
- mutex_lock(&gp->pm_mutex);
-
- spin_lock_irqsave(&gp->lock, flags);
- gem_get_cell(gp);
- spin_unlock_irqrestore(&gp->lock, flags);
switch (cmd) {
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
@@ -2808,32 +2699,17 @@ static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
/* Fallthrough... */
case SIOCGMIIREG: /* Read MII PHY register. */
- if (!gp->running)
- rc = -EAGAIN;
- else {
- data->val_out = __phy_read(gp, data->phy_id & 0x1f,
- data->reg_num & 0x1f);
- rc = 0;
- }
+ data->val_out = __phy_read(gp, data->phy_id & 0x1f,
+ data->reg_num & 0x1f);
+ rc = 0;
break;
case SIOCSMIIREG: /* Write MII PHY register. */
- if (!gp->running)
- rc = -EAGAIN;
- else {
- __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f,
- data->val_in);
- rc = 0;
- }
+ __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f,
+ data->val_in);
+ rc = 0;
break;
- };
-
- spin_lock_irqsave(&gp->lock, flags);
- gem_put_cell(gp);
- spin_unlock_irqrestore(&gp->lock, flags);
-
- mutex_unlock(&gp->pm_mutex);
-
+ }
return rc;
}
@@ -2921,23 +2797,9 @@ static void gem_remove_one(struct pci_dev *pdev)
unregister_netdev(dev);
- /* Stop the link timer */
- del_timer_sync(&gp->link_timer);
-
- /* We shouldn't need any locking here */
- gem_get_cell(gp);
-
- /* Cancel reset task */
+ /* Ensure reset task is truely gone */
cancel_work_sync(&gp->reset_task);
- /* Shut the PHY down */
- gem_stop_phy(gp, 0);
-
- gem_put_cell(gp);
-
- /* Make sure bus master is disabled */
- pci_disable_device(gp->pdev);
-
/* Free resources */
pci_free_consistent(pdev,
sizeof(struct gem_init_block),
@@ -2956,7 +2818,7 @@ static const struct net_device_ops gem_netdev_ops = {
.ndo_stop = gem_close,
.ndo_start_xmit = gem_start_xmit,
.ndo_get_stats = gem_get_stats,
- .ndo_set_multicast_list = gem_set_multicast,
+ .ndo_set_rx_mode = gem_set_multicast,
.ndo_do_ioctl = gem_ioctl,
.ndo_tx_timeout = gem_tx_timeout,
.ndo_change_mtu = gem_change_mtu,
@@ -3043,10 +2905,6 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
gp->msg_enable = DEFAULT_MSG;
- spin_lock_init(&gp->lock);
- spin_lock_init(&gp->tx_lock);
- mutex_init(&gp->pm_mutex);
-
init_timer(&gp->link_timer);
gp->link_timer.function = gem_link_timer;
gp->link_timer.data = (unsigned long) gp;
@@ -3122,14 +2980,11 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
/* Set that now, in case PM kicks in now */
pci_set_drvdata(pdev, dev);
- /* Detect & init PHY, start autoneg, we release the cell now
- * too, it will be managed by whoever needs it
- */
- gem_init_phy(gp);
-
- spin_lock_irq(&gp->lock);
- gem_put_cell(gp);
- spin_unlock_irq(&gp->lock);
+ /* We can do scatter/gather and HW checksum */
+ dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM;
+ dev->features |= dev->hw_features | NETIF_F_RXCSUM;
+ if (pci_using_dac)
+ dev->features |= NETIF_F_HIGHDMA;
/* Register with kernel */
if (register_netdev(dev)) {
@@ -3138,20 +2993,15 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
goto err_out_free_consistent;
}
+ /* Undo the get_cell with appropriate locking (we could use
+ * ndo_init/uninit but that would be even more clumsy imho)
+ */
+ rtnl_lock();
+ gem_put_cell(gp);
+ rtnl_unlock();
+
netdev_info(dev, "Sun GEM (PCI) 10/100/1000BaseT Ethernet %pM\n",
dev->dev_addr);
-
- if (gp->phy_type == phy_mii_mdio0 ||
- gp->phy_type == phy_mii_mdio1)
- netdev_info(dev, "Found %s PHY\n",
- gp->phy_mii.def ? gp->phy_mii.def->name : "no");
-
- /* GEM can do it all... */
- dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM;
- dev->features |= dev->hw_features | NETIF_F_RXCSUM | NETIF_F_LLTX;
- if (pci_using_dac)
- dev->features |= NETIF_F_HIGHDMA;
-
return 0;
err_out_free_consistent:
diff --git a/drivers/net/sungem.h b/drivers/net/ethernet/sun/sungem.h
index d225077964e2..835ce1b3cb9f 100644
--- a/drivers/net/sungem.h
+++ b/drivers/net/ethernet/sun/sungem.h
@@ -973,23 +973,14 @@ enum link_state {
};
struct gem {
- spinlock_t lock;
- spinlock_t tx_lock;
void __iomem *regs;
int rx_new, rx_old;
int tx_new, tx_old;
unsigned int has_wol : 1; /* chip supports wake-on-lan */
- unsigned int asleep : 1; /* chip asleep, protected by pm_mutex */
unsigned int asleep_wol : 1; /* was asleep with WOL enabled */
- unsigned int opened : 1; /* driver opened, protected by pm_mutex */
- unsigned int running : 1; /* chip running, protected by lock */
- /* cell enable count, protected by lock */
int cell_enabled;
-
- struct mutex pm_mutex;
-
u32 msg_enable;
u32 status;
@@ -1033,20 +1024,4 @@ struct gem {
#define found_mii_phy(gp) ((gp->phy_type == phy_mii_mdio0 || gp->phy_type == phy_mii_mdio1) && \
gp->phy_mii.def && gp->phy_mii.def->ops)
-#define ALIGNED_RX_SKB_ADDR(addr) \
- ((((unsigned long)(addr) + (64UL - 1UL)) & ~(64UL - 1UL)) - (unsigned long)(addr))
-static __inline__ struct sk_buff *gem_alloc_skb(int size,
- gfp_t gfp_flags)
-{
- struct sk_buff *skb = alloc_skb(size + 64, gfp_flags);
-
- if (skb) {
- int offset = (int) ALIGNED_RX_SKB_ADDR(skb->data);
- if (offset)
- skb_reserve(skb, offset);
- }
-
- return skb;
-}
-
#endif /* _SUNGEM_H */
diff --git a/drivers/net/sunhme.c b/drivers/net/ethernet/sun/sunhme.c
index 30aad54b1b3a..cf14ab9db576 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/ethernet/sun/sunhme.c
@@ -1524,17 +1524,11 @@ static int happy_meal_init(struct happy_meal *hp)
} else if ((hp->dev->flags & IFF_PROMISC) == 0) {
u16 hash_table[4];
struct netdev_hw_addr *ha;
- char *addrs;
u32 crc;
memset(hash_table, 0, sizeof(hash_table));
netdev_for_each_mc_addr(ha, hp->dev) {
- addrs = ha->addr;
-
- if (!(*addrs & 1))
- continue;
-
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc >>= 26;
hash_table[crc >> 4] |= 1 << (crc & 0xf);
}
@@ -2311,13 +2305,12 @@ static netdev_tx_t happy_meal_start_xmit(struct sk_buff *skb,
entry = NEXT_TX(entry);
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
- skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
+ const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
u32 len, mapping, this_txflags;
- len = this_frag->size;
- mapping = dma_map_page(hp->dma_dev, this_frag->page,
- this_frag->page_offset, len,
- DMA_TO_DEVICE);
+ len = skb_frag_size(this_frag);
+ mapping = skb_frag_dma_map(hp->dma_dev, this_frag,
+ 0, len, DMA_TO_DEVICE);
this_txflags = tx_flags;
if (frag == skb_shinfo(skb)->nr_frags - 1)
this_txflags |= TXFLAG_EOP;
@@ -2361,7 +2354,6 @@ static void happy_meal_set_multicast(struct net_device *dev)
struct happy_meal *hp = netdev_priv(dev);
void __iomem *bregs = hp->bigmacregs;
struct netdev_hw_addr *ha;
- char *addrs;
u32 crc;
spin_lock_irq(&hp->happy_lock);
@@ -2379,12 +2371,7 @@ static void happy_meal_set_multicast(struct net_device *dev)
memset(hash_table, 0, sizeof(hash_table));
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
-
- if (!(*addrs & 1))
- continue;
-
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc >>= 26;
hash_table[crc >> 4] |= 1 << (crc & 0xf);
}
@@ -2631,7 +2618,7 @@ static const struct net_device_ops hme_netdev_ops = {
.ndo_start_xmit = happy_meal_start_xmit,
.ndo_tx_timeout = happy_meal_tx_timeout,
.ndo_get_stats = happy_meal_get_stats,
- .ndo_set_multicast_list = happy_meal_set_multicast,
+ .ndo_set_rx_mode = happy_meal_set_multicast,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
@@ -2650,7 +2637,7 @@ static int __devinit happy_meal_sbus_probe_one(struct platform_device *op, int i
sbus_dp = op->dev.parent->of_node;
/* We can match PCI devices too, do not accept those here. */
- if (strcmp(sbus_dp->name, "sbus"))
+ if (strcmp(sbus_dp->name, "sbus") && strcmp(sbus_dp->name, "sbi"))
return err;
if (is_qfe) {
diff --git a/drivers/net/sunhme.h b/drivers/net/ethernet/sun/sunhme.h
index 64f278360d89..64f278360d89 100644
--- a/drivers/net/sunhme.h
+++ b/drivers/net/ethernet/sun/sunhme.h
diff --git a/drivers/net/sunqe.c b/drivers/net/ethernet/sun/sunqe.c
index 18ecdc303751..b28f74367ebe 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/ethernet/sun/sunqe.c
@@ -628,7 +628,6 @@ static void qe_set_multicast(struct net_device *dev)
struct sunqe *qep = netdev_priv(dev);
struct netdev_hw_addr *ha;
u8 new_mconfig = qep->mconfig;
- char *addrs;
int i;
u32 crc;
@@ -651,11 +650,7 @@ static void qe_set_multicast(struct net_device *dev)
memset(hash_table, 0, sizeof(hash_table));
netdev_for_each_mc_addr(ha, dev) {
- addrs = ha->addr;
-
- if (!(*addrs & 1))
- continue;
- crc = ether_crc_le(6, addrs);
+ crc = ether_crc_le(6, ha->addr);
crc >>= 26;
hash_table[crc >> 4] |= 1 << (crc & 0xf);
}
@@ -829,7 +824,7 @@ static const struct net_device_ops qec_ops = {
.ndo_open = qe_open,
.ndo_stop = qe_close,
.ndo_start_xmit = qe_start_xmit,
- .ndo_set_multicast_list = qe_set_multicast,
+ .ndo_set_rx_mode = qe_set_multicast,
.ndo_tx_timeout = qe_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/sunqe.h b/drivers/net/ethernet/sun/sunqe.h
index 581781b6b2fa..581781b6b2fa 100644
--- a/drivers/net/sunqe.h
+++ b/drivers/net/ethernet/sun/sunqe.h
diff --git a/drivers/net/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index bf3c762de620..8c6c059f3489 100644
--- a/drivers/net/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -1012,7 +1012,7 @@ static DEFINE_MUTEX(vnet_list_mutex);
static const struct net_device_ops vnet_ops = {
.ndo_open = vnet_open,
.ndo_stop = vnet_close,
- .ndo_set_multicast_list = vnet_set_rx_mode,
+ .ndo_set_rx_mode = vnet_set_rx_mode,
.ndo_set_mac_address = vnet_set_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_tx_timeout = vnet_tx_timeout,
diff --git a/drivers/net/sunvnet.h b/drivers/net/ethernet/sun/sunvnet.h
index d347a5bf24b0..d347a5bf24b0 100644
--- a/drivers/net/sunvnet.h
+++ b/drivers/net/ethernet/sun/sunvnet.h
diff --git a/drivers/net/ethernet/tehuti/Kconfig b/drivers/net/ethernet/tehuti/Kconfig
new file mode 100644
index 000000000000..1fc027eda33e
--- /dev/null
+++ b/drivers/net/ethernet/tehuti/Kconfig
@@ -0,0 +1,27 @@
+#
+# Tehuti network device configuration
+#
+
+config NET_VENDOR_TEHUTI
+ bool "Tehuti devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Tehuti cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_TEHUTI
+
+config TEHUTI
+ tristate "Tehuti Networks 10G Ethernet"
+ depends on PCI
+ ---help---
+ Tehuti Networks 10G Ethernet NIC
+
+endif # NET_VENDOR_TEHUTI
diff --git a/drivers/net/ethernet/tehuti/Makefile b/drivers/net/ethernet/tehuti/Makefile
new file mode 100644
index 000000000000..f995421ddbc8
--- /dev/null
+++ b/drivers/net/ethernet/tehuti/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Tehuti network device drivers.
+#
+
+obj-$(CONFIG_TEHUTI) += tehuti.o
diff --git a/drivers/net/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c
index 80fbee0d40af..3a90af6d111c 100644
--- a/drivers/net/tehuti.c
+++ b/drivers/net/ethernet/tehuti/tehuti.c
@@ -67,10 +67,10 @@
#include "tehuti.h"
static DEFINE_PCI_DEVICE_TABLE(bdx_pci_tbl) = {
- {0x1FC9, 0x3009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0x1FC9, 0x3010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0x1FC9, 0x3014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0}
+ { PCI_VDEVICE(TEHUTI, 0x3009), },
+ { PCI_VDEVICE(TEHUTI, 0x3010), },
+ { PCI_VDEVICE(TEHUTI, 0x3014), },
+ { 0 }
};
MODULE_DEVICE_TABLE(pci, bdx_pci_tbl);
@@ -742,22 +742,6 @@ static void bdx_vlan_rx_kill_vid(struct net_device *ndev, unsigned short vid)
__bdx_vlan_rx_vid(ndev, vid, 0);
}
-/*
- * bdx_vlan_rx_register - kernel hook for adding VLAN group
- * @ndev network device
- * @grp VLAN group
- */
-static void
-bdx_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp)
-{
- struct bdx_priv *priv = netdev_priv(ndev);
-
- ENTER;
- DBG("device='%s', group='%p'\n", ndev->name, grp);
- priv->vlgrp = grp;
- RET();
-}
-
/**
* bdx_change_mtu - Change the Maximum Transfer Unit
* @netdev: network interface device structure
@@ -1146,21 +1130,15 @@ NETIF_RX_MUX(struct bdx_priv *priv, u32 rxd_val1, u16 rxd_vlan,
struct sk_buff *skb)
{
ENTER;
- DBG("rxdd->flags.bits.vtag=%d vlgrp=%p\n", GET_RXD_VTAG(rxd_val1),
- priv->vlgrp);
- if (priv->vlgrp && GET_RXD_VTAG(rxd_val1)) {
- DBG("%s: vlan rcv vlan '%x' vtag '%x', device name '%s'\n",
+ DBG("rxdd->flags.bits.vtag=%d\n", GET_RXD_VTAG(rxd_val1));
+ if (GET_RXD_VTAG(rxd_val1)) {
+ DBG("%s: vlan rcv vlan '%x' vtag '%x'\n",
priv->ndev->name,
GET_RXD_VLAN_ID(rxd_vlan),
- GET_RXD_VTAG(rxd_val1),
- vlan_group_get_device(priv->vlgrp,
- GET_RXD_VLAN_ID(rxd_vlan))->name);
- /* NAPI variant of receive functions */
- vlan_hwaccel_receive_skb(skb, priv->vlgrp,
- GET_RXD_VLAN_TCI(rxd_vlan));
- } else {
- netif_receive_skb(skb);
+ GET_RXD_VTAG(rxd_val1));
+ __vlan_hwaccel_put_tag(skb, GET_RXD_VLAN_TCI(rxd_vlan));
}
+ netif_receive_skb(skb);
}
static void bdx_recycle_skb(struct bdx_priv *priv, struct rxd_desc *rxdd)
@@ -1515,13 +1493,13 @@ bdx_tx_map_skb(struct bdx_priv *priv, struct sk_buff *skb,
bdx_tx_db_inc_wptr(db);
for (i = 0; i < nr_frags; i++) {
- struct skb_frag_struct *frag;
+ const struct skb_frag_struct *frag;
frag = &skb_shinfo(skb)->frags[i];
- db->wptr->len = frag->size;
- db->wptr->addr.dma =
- pci_map_page(priv->pdev, frag->page, frag->page_offset,
- frag->size, PCI_DMA_TODEVICE);
+ db->wptr->len = skb_frag_size(frag);
+ db->wptr->addr.dma = skb_frag_dma_map(&priv->pdev->dev, frag,
+ 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
pbl++;
pbl->len = CPU_CHIP_SWAP32(db->wptr->len);
@@ -1877,15 +1855,14 @@ static void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size)
}
static const struct net_device_ops bdx_netdev_ops = {
- .ndo_open = bdx_open,
+ .ndo_open = bdx_open,
.ndo_stop = bdx_close,
.ndo_start_xmit = bdx_tx_transmit,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = bdx_ioctl,
- .ndo_set_multicast_list = bdx_setmulti,
+ .ndo_set_rx_mode = bdx_setmulti,
.ndo_change_mtu = bdx_change_mtu,
.ndo_set_mac_address = bdx_set_mac,
- .ndo_vlan_rx_register = bdx_vlan_rx_register,
.ndo_vlan_rx_add_vid = bdx_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = bdx_vlan_rx_kill_vid,
};
diff --git a/drivers/net/tehuti.h b/drivers/net/ethernet/tehuti/tehuti.h
index c5642fefc9e7..709ebd6e28b4 100644
--- a/drivers/net/tehuti.h
+++ b/drivers/net/ethernet/tehuti/tehuti.h
@@ -250,7 +250,6 @@ struct bdx_priv {
struct rxf_fifo rxf_fifo0;
struct rxdb *rxdb; /* rx dbs to store skb pointers */
int napi_stop;
- struct vlan_group *vlgrp;
/* Tx FIFOs: 1 for data desc, 1 for empty (acks) desc */
struct txd_fifo txd_fifo0;
diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig
new file mode 100644
index 000000000000..de76c70ec8fb
--- /dev/null
+++ b/drivers/net/ethernet/ti/Kconfig
@@ -0,0 +1,77 @@
+#
+# TI device configuration
+#
+
+config NET_VENDOR_TI
+ bool "Texas Instruments (TI) devices"
+ default y
+ depends on PCI || EISA || AR7 || (ARM && (ARCH_DAVINCI || ARCH_OMAP3))
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about TI devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_TI
+
+config TI_DAVINCI_EMAC
+ tristate "TI DaVinci EMAC Support"
+ depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+ select TI_DAVINCI_MDIO
+ select TI_DAVINCI_CPDMA
+ select PHYLIB
+ ---help---
+ This driver supports TI's DaVinci Ethernet .
+
+ To compile this driver as a module, choose M here: the module
+ will be called davinci_emac_driver. This is recommended.
+
+config TI_DAVINCI_MDIO
+ tristate "TI DaVinci MDIO Support"
+ depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+ select PHYLIB
+ ---help---
+ This driver supports TI's DaVinci MDIO module.
+
+ To compile this driver as a module, choose M here: the module
+ will be called davinci_mdio. This is recommended.
+
+config TI_DAVINCI_CPDMA
+ tristate "TI DaVinci CPDMA Support"
+ depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+ ---help---
+ This driver supports TI's DaVinci CPDMA dma engine.
+
+ To compile this driver as a module, choose M here: the module
+ will be called davinci_cpdma. This is recommended.
+
+config TLAN
+ tristate "TI ThunderLAN support"
+ depends on (PCI || EISA)
+ ---help---
+ If you have a PCI Ethernet network card based on the ThunderLAN chip
+ which is supported by this driver, say Y and read the
+ Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Devices currently supported by this driver are Compaq Netelligent,
+ Compaq NetFlex and Olicom cards. Please read the file
+ <file:Documentation/networking/tlan.txt> for more details.
+
+ To compile this driver as a module, choose M here. The module
+ will be called tlan.
+
+ Please email feedback to <torben.mathiasen@compaq.com>.
+
+config CPMAC
+ tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && AR7
+ select PHYLIB
+ ---help---
+ TI AR7 CPMAC Ethernet support
+
+endif # NET_VENDOR_TI
diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile
new file mode 100644
index 000000000000..aedb3af74e5a
--- /dev/null
+++ b/drivers/net/ethernet/ti/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the TI network device drivers.
+#
+
+obj-$(CONFIG_TLAN) += tlan.o
+obj-$(CONFIG_CPMAC) += cpmac.o
+obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o
+obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o
+obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o
diff --git a/drivers/net/cpmac.c b/drivers/net/ethernet/ti/cpmac.c
index fec939f8f65f..aaac0c7ad111 100644
--- a/drivers/net/cpmac.c
+++ b/drivers/net/ethernet/ti/cpmac.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
@@ -39,7 +40,7 @@
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/gpio.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
MODULE_AUTHOR("Eugene Konev <ejka@imfi.kspu.ru>");
MODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)");
@@ -1099,7 +1100,7 @@ static const struct net_device_ops cpmac_netdev_ops = {
.ndo_stop = cpmac_stop,
.ndo_start_xmit = cpmac_start_xmit,
.ndo_tx_timeout = cpmac_tx_timeout,
- .ndo_set_multicast_list = cpmac_set_multicast_list,
+ .ndo_set_rx_mode = cpmac_set_multicast_list,
.ndo_do_ioctl = cpmac_ioctl,
.ndo_set_config = cpmac_config,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index ae47f23ba930..dca9d3369cdd 100644
--- a/drivers/net/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -167,7 +167,7 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr,
} else {
pool->cpumap = dma_alloc_coherent(dev, size, &pool->phys,
GFP_KERNEL);
- pool->iomap = (void __force __iomem *)pool->cpumap;
+ pool->iomap = pool->cpumap;
pool->hw_addr = pool->phys;
}
diff --git a/drivers/net/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h
index afa19a0c0d81..afa19a0c0d81 100644
--- a/drivers/net/davinci_cpdma.h
+++ b/drivers/net/ethernet/ti/davinci_cpdma.h
diff --git a/drivers/net/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
index dcc4a170b0f3..815c7970261b 100644
--- a/drivers/net/davinci_emac.c
+++ b/drivers/net/ethernet/ti/davinci_emac.c
@@ -48,7 +48,6 @@
#include <linux/highmem.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
-#include <linux/version.h>
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
@@ -1083,6 +1082,8 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev)
goto fail_tx;
}
+ skb_tx_timestamp(skb);
+
ret_code = cpdma_chan_submit(priv->txchan, skb, skb->data, skb->len,
GFP_KERNEL);
if (unlikely(ret_code != 0)) {
@@ -1489,14 +1490,14 @@ static void emac_adjust_link(struct net_device *ndev)
*/
static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd)
{
- dev_warn(&ndev->dev, "DaVinci EMAC: ioctl not supported\n");
+ struct emac_priv *priv = netdev_priv(ndev);
if (!(netif_running(ndev)))
return -EINVAL;
/* TODO: Add phy read and write and private statistics get feature */
- return -EOPNOTSUPP;
+ return phy_mii_ioctl(priv->phydev, ifrq, cmd);
}
static int match_first_device(struct device *dev, void *data)
@@ -1740,7 +1741,7 @@ static const struct net_device_ops emac_netdev_ops = {
.ndo_open = emac_dev_open,
.ndo_stop = emac_dev_stop,
.ndo_start_xmit = emac_dev_xmit,
- .ndo_set_multicast_list = emac_dev_mcast_set,
+ .ndo_set_rx_mode = emac_dev_mcast_set,
.ndo_set_mac_address = emac_dev_setmac_addr,
.ndo_do_ioctl = emac_devioctl,
.ndo_tx_timeout = emac_dev_tx_timeout,
@@ -1821,7 +1822,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
}
priv->emac_base_phys = res->start + pdata->ctrl_reg_offset;
- size = res->end - res->start + 1;
+ size = resource_size(res);
if (!request_mem_region(res->start, size, ndev->name)) {
dev_err(&pdev->dev, "failed request_mem_region() for regs\n");
rc = -ENXIO;
@@ -1926,7 +1927,7 @@ no_irq_res:
cpdma_ctlr_destroy(priv->dma);
no_dma:
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
iounmap(priv->remap_addr);
probe_quit:
@@ -1960,7 +1961,7 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev)
cpdma_chan_destroy(priv->rxchan);
cpdma_ctlr_destroy(priv->dma);
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
unregister_netdev(ndev);
iounmap(priv->remap_addr);
diff --git a/drivers/net/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c
index 7615040df756..7615040df756 100644
--- a/drivers/net/davinci_mdio.c
+++ b/drivers/net/ethernet/ti/davinci_mdio.c
diff --git a/drivers/net/tlan.c b/drivers/net/ethernet/ti/tlan.c
index ace6404e2fac..9c0dd6b8d6c9 100644
--- a/drivers/net/tlan.c
+++ b/drivers/net/ethernet/ti/tlan.c
@@ -29,8 +29,10 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/hardirq.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/eisa.h>
#include <linux/pci.h>
@@ -772,7 +774,7 @@ static const struct net_device_ops tlan_netdev_ops = {
.ndo_start_xmit = tlan_start_tx,
.ndo_tx_timeout = tlan_tx_timeout,
.ndo_get_stats = tlan_get_stats,
- .ndo_set_multicast_list = tlan_set_multicast_list,
+ .ndo_set_rx_mode = tlan_set_multicast_list,
.ndo_do_ioctl = tlan_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/tlan.h b/drivers/net/ethernet/ti/tlan.h
index 5fc98a8e4889..5fc98a8e4889 100644
--- a/drivers/net/tlan.h
+++ b/drivers/net/ethernet/ti/tlan.h
diff --git a/drivers/net/ethernet/tile/Kconfig b/drivers/net/ethernet/tile/Kconfig
new file mode 100644
index 000000000000..2d9218f86bca
--- /dev/null
+++ b/drivers/net/ethernet/tile/Kconfig
@@ -0,0 +1,15 @@
+#
+# Tilera network device configuration
+#
+
+config TILE_NET
+ tristate "Tilera GBE/XGBE network driver support"
+ depends on TILE
+ default y
+ select CRC32
+ ---help---
+ This is a standard Linux network device driver for the
+ on-chip Tilera Gigabit Ethernet and XAUI interfaces.
+
+ To compile this driver as a module, choose M here: the module
+ will be called tile_net.
diff --git a/drivers/net/tile/Makefile b/drivers/net/ethernet/tile/Makefile
index f634f142cab4..f634f142cab4 100644
--- a/drivers/net/tile/Makefile
+++ b/drivers/net/ethernet/tile/Makefile
diff --git a/drivers/net/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c
index 1e2af96fc29c..10826d8a2a2d 100644
--- a/drivers/net/tile/tilepro.c
+++ b/drivers/net/ethernet/tile/tilepro.c
@@ -177,7 +177,7 @@ struct tile_net_cpu {
struct tile_net_stats_t stats;
/* True iff NAPI is enabled. */
bool napi_enabled;
- /* True if this tile has succcessfully registered with the IPP. */
+ /* True if this tile has successfully registered with the IPP. */
bool registered;
/* True if the link was down last time we tried to register. */
bool link_down;
@@ -1713,7 +1713,7 @@ static unsigned int tile_net_tx_frags(lepp_frag_t *frags,
cpa = ((phys_addr_t)pfn << PAGE_SHIFT) + f->page_offset;
frags[n].cpa_lo = cpa;
frags[n].cpa_hi = cpa >> 32;
- frags[n].length = f->size;
+ frags[n].length = skb_frag_size(f);
frags[n].hash_for_home = hash_for_home;
n++;
}
diff --git a/drivers/net/ethernet/toshiba/Kconfig b/drivers/net/ethernet/toshiba/Kconfig
new file mode 100644
index 000000000000..051764704559
--- /dev/null
+++ b/drivers/net/ethernet/toshiba/Kconfig
@@ -0,0 +1,57 @@
+#
+# Toshiba network device configuration
+#
+
+config NET_VENDOR_TOSHIBA
+ bool "Toshiba devices"
+ default y
+ depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB) || PPC_PS3
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Toshiba cards. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_TOSHIBA
+
+config GELIC_NET
+ tristate "PS3 Gigabit Ethernet driver"
+ depends on PPC_PS3
+ select PS3_SYS_MANAGER
+ ---help---
+ This driver supports the network device on the PS3 game
+ console. This driver has built-in support for Ethernet.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ps3_gelic.
+
+config GELIC_WIRELESS
+ bool "PS3 Wireless support"
+ depends on GELIC_NET && WLAN
+ select WIRELESS_EXT
+ ---help---
+ This option adds the support for the wireless feature of PS3.
+ If you have the wireless-less model of PS3 or have no plan to
+ use wireless feature, disabling this option saves memory. As
+ the driver automatically distinguishes the models, you can
+ safely enable this option even if you have a wireless-less model.
+
+config SPIDER_NET
+ tristate "Spider Gigabit Ethernet driver"
+ depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB)
+ select FW_LOADER
+ select SUNGEM_PHY
+ ---help---
+ This driver supports the Gigabit Ethernet chips present on the
+ Cell Processor-Based Blades from IBM.
+
+config TC35815
+ tristate "TOSHIBA TC35815 Ethernet support"
+ depends on PCI && MIPS
+ select PHYLIB
+
+endif # NET_VENDOR_TOSHIBA
diff --git a/drivers/net/ethernet/toshiba/Makefile b/drivers/net/ethernet/toshiba/Makefile
new file mode 100644
index 000000000000..a5069008435b
--- /dev/null
+++ b/drivers/net/ethernet/toshiba/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the Toshiba network device drivers.
+#
+
+obj-$(CONFIG_GELIC_NET) += ps3_gelic.o
+gelic_wireless-$(CONFIG_GELIC_WIRELESS) += ps3_gelic_wireless.o
+ps3_gelic-objs += ps3_gelic_net.o $(gelic_wireless-y)
+spidernet-y += spider_net.o spider_net_ethtool.o
+obj-$(CONFIG_SPIDER_NET) += spidernet.o
+obj-$(CONFIG_TC35815) += tc35815.o
diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c
index b1f251da1535..7bf1e2015784 100644
--- a/drivers/net/ps3_gelic_net.c
+++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c
@@ -28,6 +28,7 @@
#undef DEBUG
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -837,9 +838,11 @@ static int gelic_card_kick_txdma(struct gelic_card *card,
card->tx_dma_progress = 1;
status = lv1_net_start_tx_dma(bus_id(card), dev_id(card),
descr->bus_addr, 0);
- if (status)
+ if (status) {
+ card->tx_dma_progress = 0;
dev_info(ctodev(card), "lv1_net_start_txdma failed," \
"status=%d\n", status);
+ }
}
return status;
}
@@ -875,7 +878,7 @@ int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
result = gelic_descr_prepare_tx(card, descr, skb);
if (result) {
/*
- * DMA map failed. As chanses are that failure
+ * DMA map failed. As chances are that failure
* would continue, just release skb and return
*/
netdev->stats.tx_dropped++;
@@ -896,12 +899,16 @@ int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
if (gelic_card_kick_txdma(card, descr)) {
/*
* kick failed.
- * release descriptors which were just prepared
+ * release descriptor which was just prepared
*/
netdev->stats.tx_dropped++;
+ /* don't trigger BUG_ON() in gelic_descr_release_tx */
+ descr->data_status = cpu_to_be32(GELIC_DESCR_TX_TAIL);
gelic_descr_release_tx(card, descr);
- gelic_descr_release_tx(card, descr->next);
- card->tx_chain.tail = descr->next->next;
+ /* reset head */
+ card->tx_chain.head = descr;
+ /* reset hw termination */
+ descr->prev->next_descr_addr = 0;
dev_info(ctodev(card), "%s: kick failure\n", __func__);
}
@@ -986,10 +993,6 @@ static int gelic_card_decode_one_descr(struct gelic_card *card)
int dmac_chain_ended;
status = gelic_descr_get_status(descr);
- /* is this descriptor terminated with next_descr == NULL? */
- dmac_chain_ended =
- be32_to_cpu(descr->dmac_cmd_status) &
- GELIC_DESCR_RX_DMA_CHAIN_END;
if (status == GELIC_DESCR_DMA_CARDOWNED)
return 0;
@@ -1009,7 +1012,7 @@ static int gelic_card_decode_one_descr(struct gelic_card *card)
netdev = card->netdev[i];
break;
}
- };
+ }
if (GELIC_PORT_MAX <= i) {
pr_info("%s: unknown packet vid=%x\n", __func__, vid);
goto refill;
@@ -1040,7 +1043,7 @@ static int gelic_card_decode_one_descr(struct gelic_card *card)
goto refill;
}
/*
- * descriptoers any other than FRAME_END here should
+ * descriptors any other than FRAME_END here should
* be treated as error.
*/
if (status != GELIC_DESCR_DMA_FRAME_END) {
@@ -1052,6 +1055,11 @@ static int gelic_card_decode_one_descr(struct gelic_card *card)
/* ok, we've got a packet in descr */
gelic_net_pass_skb_up(descr, card, netdev);
refill:
+
+ /* is the current descriptor terminated with next_descr == NULL? */
+ dmac_chain_ended =
+ be32_to_cpu(descr->dmac_cmd_status) &
+ GELIC_DESCR_RX_DMA_CHAIN_END;
/*
* So that always DMAC can see the end
* of the descriptor chain to avoid
@@ -1080,10 +1088,9 @@ refill:
* If dmac chain was met, DMAC stopped.
* thus re-enable it
*/
- if (dmac_chain_ended) {
- card->rx_dma_restart_required = 1;
- dev_dbg(ctodev(card), "reenable rx dma scheduled\n");
- }
+
+ if (dmac_chain_ended)
+ gelic_card_enable_rxdmac(card);
return 1;
}
@@ -1149,11 +1156,6 @@ static irqreturn_t gelic_card_interrupt(int irq, void *ptr)
status &= card->irq_mask;
- if (card->rx_dma_restart_required) {
- card->rx_dma_restart_required = 0;
- gelic_card_enable_rxdmac(card);
- }
-
if (status & GELIC_CARD_RXINT) {
gelic_card_rx_irq_off(card);
napi_schedule(&card->napi);
@@ -1199,7 +1201,7 @@ void gelic_net_poll_controller(struct net_device *netdev)
#endif /* CONFIG_NET_POLL_CONTROLLER */
/**
- * gelic_net_open - called upon ifonfig up
+ * gelic_net_open - called upon ifconfig up
* @netdev: interface device structure
*
* returns 0 on success, <0 on failure
@@ -1450,7 +1452,7 @@ static const struct net_device_ops gelic_netdevice_ops = {
.ndo_open = gelic_net_open,
.ndo_stop = gelic_net_stop,
.ndo_start_xmit = gelic_net_xmit,
- .ndo_set_multicast_list = gelic_net_set_multi,
+ .ndo_set_rx_mode = gelic_net_set_multi,
.ndo_change_mtu = gelic_net_change_mtu,
.ndo_tx_timeout = gelic_net_tx_timeout,
.ndo_set_mac_address = eth_mac_addr,
@@ -1672,6 +1674,9 @@ static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
int result;
pr_debug("%s: called\n", __func__);
+
+ udbg_shutdown_ps3gelic();
+
result = ps3_open_hv_device(dev);
if (result) {
diff --git a/drivers/net/ps3_gelic_net.h b/drivers/net/ethernet/toshiba/ps3_gelic_net.h
index d9a55b93898b..a93df6ac1909 100644
--- a/drivers/net/ps3_gelic_net.h
+++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.h
@@ -289,7 +289,6 @@ struct gelic_card {
struct gelic_descr_chain tx_chain;
struct gelic_descr_chain rx_chain;
- int rx_dma_restart_required;
/*
* tx_lock guards tx descriptor list and
* tx_dma_progress.
@@ -360,6 +359,12 @@ static inline void *port_priv(struct gelic_port *port)
return port->priv;
}
+#ifdef CONFIG_PPC_EARLY_DEBUG_PS3GELIC
+extern void udbg_shutdown_ps3gelic(void);
+#else
+static inline void udbg_shutdown_ps3gelic(void) {}
+#endif
+
extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask);
/* shared netdev ops */
extern void gelic_card_up(struct gelic_card *card);
diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c
index 2e62938c0f82..fd4ed7f8cfa1 100644
--- a/drivers/net/ps3_gelic_wireless.c
+++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c
@@ -2568,7 +2568,7 @@ static const struct net_device_ops gelic_wl_netdevice_ops = {
.ndo_open = gelic_wl_open,
.ndo_stop = gelic_wl_stop,
.ndo_start_xmit = gelic_net_xmit,
- .ndo_set_multicast_list = gelic_net_set_multi,
+ .ndo_set_rx_mode = gelic_net_set_multi,
.ndo_change_mtu = gelic_net_change_mtu,
.ndo_tx_timeout = gelic_net_tx_timeout,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/ps3_gelic_wireless.h b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.h
index f7e51b7d7049..f7e51b7d7049 100644
--- a/drivers/net/ps3_gelic_wireless.h
+++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.h
diff --git a/drivers/net/spider_net.c b/drivers/net/ethernet/toshiba/spider_net.c
index 949f124e1278..6199f6b387b6 100644
--- a/drivers/net/spider_net.c
+++ b/drivers/net/ethernet/toshiba/spider_net.c
@@ -31,6 +31,7 @@
#include <linux/if_vlan.h>
#include <linux/in.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/gfp.h>
#include <linux/ioport.h>
#include <linux/ip.h>
@@ -195,7 +196,7 @@ spider_net_setup_aneg(struct spider_net_card *card)
if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_THALF))
advertise |= SUPPORTED_1000baseT_Half;
- mii_phy_probe(phy, phy->mii_id);
+ sungem_phy_probe(phy, phy->mii_id);
phy->def->ops->setup_aneg(phy, advertise);
}
@@ -1003,9 +1004,7 @@ spider_net_pass_skb_up(struct spider_net_descr *descr,
}
if (data_status & SPIDER_NET_VLAN_PACKET) {
- /* further enhancements: HW-accel VLAN
- * vlan_hwaccel_receive_skb
- */
+ /* further enhancements: HW-accel VLAN */
}
/* update netdevice statistics */
@@ -2121,7 +2120,7 @@ spider_net_setup_phy(struct spider_net_card *card)
unsigned short id;
id = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR);
if (id != 0x0000 && id != 0xffff) {
- if (!mii_phy_probe(phy, phy->mii_id)) {
+ if (!sungem_phy_probe(phy, phy->mii_id)) {
pr_info("Found %s.\n", phy->def->name);
break;
}
@@ -2260,7 +2259,7 @@ static const struct net_device_ops spider_net_ops = {
.ndo_open = spider_net_open,
.ndo_stop = spider_net_stop,
.ndo_start_xmit = spider_net_xmit,
- .ndo_set_multicast_list = spider_net_set_multi,
+ .ndo_set_rx_mode = spider_net_set_multi,
.ndo_set_mac_address = spider_net_set_mac,
.ndo_change_mtu = spider_net_change_mtu,
.ndo_do_ioctl = spider_net_do_ioctl,
diff --git a/drivers/net/spider_net.h b/drivers/net/ethernet/toshiba/spider_net.h
index 020f64a8fcf7..4ba2135474d1 100644
--- a/drivers/net/spider_net.h
+++ b/drivers/net/ethernet/toshiba/spider_net.h
@@ -27,7 +27,7 @@
#define VERSION "2.0 B"
-#include "sungem_phy.h"
+#include <linux/sungem_phy.h>
extern int spider_net_stop(struct net_device *netdev);
extern int spider_net_open(struct net_device *netdev);
diff --git a/drivers/net/spider_net_ethtool.c b/drivers/net/ethernet/toshiba/spider_net_ethtool.c
index 9c288cd7d171..9c288cd7d171 100644
--- a/drivers/net/spider_net_ethtool.c
+++ b/drivers/net/ethernet/toshiba/spider_net_ethtool.c
diff --git a/drivers/net/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c
index 4a55a162dfe6..71b785cd7563 100644
--- a/drivers/net/tc35815.c
+++ b/drivers/net/ethernet/toshiba/tc35815.c
@@ -774,7 +774,7 @@ static const struct net_device_ops tc35815_netdev_ops = {
.ndo_stop = tc35815_close,
.ndo_start_xmit = tc35815_send_packet,
.ndo_get_stats = tc35815_get_stats,
- .ndo_set_multicast_list = tc35815_set_multicast_list,
+ .ndo_set_rx_mode = tc35815_set_multicast_list,
.ndo_tx_timeout = tc35815_tx_timeout,
.ndo_do_ioctl = tc35815_ioctl,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/ethernet/tundra/Kconfig b/drivers/net/ethernet/tundra/Kconfig
new file mode 100644
index 000000000000..cf7d69b62c42
--- /dev/null
+++ b/drivers/net/ethernet/tundra/Kconfig
@@ -0,0 +1,29 @@
+#
+# Tundra network device configuration
+#
+
+config NET_VENDOR_TUNDRA
+ bool "Tundra devices"
+ default y
+ depends on TSI108_BRIDGE
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Tundra cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_TUNDRA
+
+config TSI108_ETH
+ tristate "Tundra TSI108 gigabit Ethernet support"
+ depends on TSI108_BRIDGE
+ ---help---
+ This driver supports Tundra TSI108 gigabit Ethernet ports.
+ To compile this driver as a module, choose M here: the module
+ will be called tsi108_eth.
+
+endif # NET_VENDOR_TUNDRA
diff --git a/drivers/net/ethernet/tundra/Makefile b/drivers/net/ethernet/tundra/Makefile
new file mode 100644
index 000000000000..439f6930235b
--- /dev/null
+++ b/drivers/net/ethernet/tundra/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Tundra network device drivers.
+#
+
+obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
diff --git a/drivers/net/tsi108_eth.c b/drivers/net/ethernet/tundra/tsi108_eth.c
index 5c633a32eaeb..a8df7eca0956 100644
--- a/drivers/net/tsi108_eth.c
+++ b/drivers/net/ethernet/tundra/tsi108_eth.c
@@ -33,6 +33,7 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -708,12 +709,13 @@ static int tsi108_send_packet(struct sk_buff * skb, struct net_device *dev)
data->txring[tx].len = skb_headlen(skb);
misc |= TSI108_TX_SOF;
} else {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
- data->txring[tx].buf0 =
- dma_map_page(NULL, frag->page, frag->page_offset,
- frag->size, DMA_TO_DEVICE);
- data->txring[tx].len = frag->size;
+ data->txring[tx].buf0 = skb_frag_dma_map(NULL, frag,
+ 0,
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ data->txring[tx].len = skb_frag_size(frag);
}
if (i == frags - 1)
@@ -1553,7 +1555,7 @@ static const struct net_device_ops tsi108_netdev_ops = {
.ndo_open = tsi108_open,
.ndo_stop = tsi108_close,
.ndo_start_xmit = tsi108_send_packet,
- .ndo_set_multicast_list = tsi108_set_rx_mode,
+ .ndo_set_rx_mode = tsi108_set_rx_mode,
.ndo_get_stats = tsi108_get_stats,
.ndo_do_ioctl = tsi108_do_ioctl,
.ndo_set_mac_address = tsi108_set_mac,
diff --git a/drivers/net/tsi108_eth.h b/drivers/net/ethernet/tundra/tsi108_eth.h
index 5fee7d78dc6d..5fee7d78dc6d 100644
--- a/drivers/net/tsi108_eth.h
+++ b/drivers/net/ethernet/tundra/tsi108_eth.h
diff --git a/drivers/net/ethernet/via/Kconfig b/drivers/net/ethernet/via/Kconfig
new file mode 100644
index 000000000000..68a9ba66feba
--- /dev/null
+++ b/drivers/net/ethernet/via/Kconfig
@@ -0,0 +1,59 @@
+#
+# VIA device configuration
+#
+
+config NET_VENDOR_VIA
+ bool "VIA devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about VIA devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_VIA
+
+config VIA_RHINE
+ tristate "VIA Rhine support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ If you have a VIA "Rhine" based network card (Rhine-I (VT86C100A),
+ Rhine-II (VT6102), or Rhine-III (VT6105)), say Y here. Rhine-type
+ Ethernet functions can also be found integrated on South Bridges
+ (e.g. VT8235).
+
+ To compile this driver as a module, choose M here. The module
+ will be called via-rhine.
+
+config VIA_RHINE_MMIO
+ bool "Use MMIO instead of PIO"
+ depends on VIA_RHINE
+ ---help---
+ This instructs the driver to use PCI shared memory (MMIO) instead of
+ programmed I/O ports (PIO). Enabling this gives an improvement in
+ processing time in parts of the driver.
+
+ If unsure, say Y.
+
+config VIA_VELOCITY
+ tristate "VIA Velocity support"
+ depends on PCI
+ select CRC32
+ select CRC_CCITT
+ select NET_CORE
+ select MII
+ ---help---
+ If you have a VIA "Velocity" based network card say Y here.
+
+ To compile this driver as a module, choose M here. The module
+ will be called via-velocity.
+
+endif # NET_VENDOR_VIA
diff --git a/drivers/net/ethernet/via/Makefile b/drivers/net/ethernet/via/Makefile
new file mode 100644
index 000000000000..46c5d4a3d8f1
--- /dev/null
+++ b/drivers/net/ethernet/via/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the VIA device drivers.
+#
+
+obj-$(CONFIG_VIA_RHINE) += via-rhine.o
+obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o
diff --git a/drivers/net/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c
index 7f23ab913fd9..f34dd99fe579 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/ethernet/via/via-rhine.c
@@ -697,7 +697,7 @@ static const struct net_device_ops rhine_netdev_ops = {
.ndo_stop = rhine_close,
.ndo_start_xmit = rhine_start_tx,
.ndo_get_stats = rhine_get_stats,
- .ndo_set_multicast_list = rhine_set_rx_mode,
+ .ndo_set_rx_mode = rhine_set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c
index 06daa9d6fee8..4535d7cc848e 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/ethernet/via/via-velocity.c
@@ -42,9 +42,9 @@
*
*/
-
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/errno.h>
@@ -111,7 +111,6 @@ static void mac_get_cam_mask(struct mac_regs __iomem *regs, u8 *mask)
BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
}
-
/**
* mac_set_cam_mask - Set a CAM mask
* @regs: register block for this velocity
@@ -501,6 +500,7 @@ static void __devinit velocity_get_options(struct velocity_opt *opts, int index,
static void velocity_init_cam_filter(struct velocity_info *vptr)
{
struct mac_regs __iomem *regs = vptr->mac_regs;
+ unsigned int vid, i = 0;
/* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */
WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, &regs->MCFG);
@@ -513,30 +513,13 @@ static void velocity_init_cam_filter(struct velocity_info *vptr)
mac_set_cam_mask(regs, vptr->mCAMmask);
/* Enable VCAMs */
- if (vptr->vlgrp) {
- unsigned int vid, i = 0;
-
- if (!vlan_group_get_device(vptr->vlgrp, 0))
- WORD_REG_BITS_ON(MCFG_RTGOPT, &regs->MCFG);
-
- for (vid = 1; (vid < VLAN_VID_MASK); vid++) {
- if (vlan_group_get_device(vptr->vlgrp, vid)) {
- mac_set_vlan_cam(regs, i, (u8 *) &vid);
- vptr->vCAMmask[i / 8] |= 0x1 << (i % 8);
- if (++i >= VCAM_SIZE)
- break;
- }
- }
- mac_set_vlan_cam_mask(regs, vptr->vCAMmask);
+ for_each_set_bit(vid, vptr->active_vlans, VLAN_N_VID) {
+ mac_set_vlan_cam(regs, i, (u8 *) &vid);
+ vptr->vCAMmask[i / 8] |= 0x1 << (i % 8);
+ if (++i >= VCAM_SIZE)
+ break;
}
-}
-
-static void velocity_vlan_rx_register(struct net_device *dev,
- struct vlan_group *grp)
-{
- struct velocity_info *vptr = netdev_priv(dev);
-
- vptr->vlgrp = grp;
+ mac_set_vlan_cam_mask(regs, vptr->vCAMmask);
}
static void velocity_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
@@ -544,6 +527,7 @@ static void velocity_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
struct velocity_info *vptr = netdev_priv(dev);
spin_lock_irq(&vptr->lock);
+ set_bit(vid, vptr->active_vlans);
velocity_init_cam_filter(vptr);
spin_unlock_irq(&vptr->lock);
}
@@ -553,7 +537,7 @@ static void velocity_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid
struct velocity_info *vptr = netdev_priv(dev);
spin_lock_irq(&vptr->lock);
- vlan_group_set_device(vptr->vlgrp, vid, NULL);
+ clear_bit(vid, vptr->active_vlans);
velocity_init_cam_filter(vptr);
spin_unlock_irq(&vptr->lock);
}
@@ -710,7 +694,6 @@ static int velocity_mii_read(struct mac_regs __iomem *regs, u8 index, u16 *data)
return 0;
}
-
/**
* mii_check_media_mode - check media state
* @regs: velocity registers
@@ -876,8 +859,6 @@ static u32 check_connection_type(struct mac_regs __iomem *regs)
return status;
}
-
-
/**
* velocity_set_media_mode - set media mode
* @mii_status: old MII link state
@@ -1272,6 +1253,7 @@ static void setup_queue_timers(struct velocity_info *vptr)
writeb(rxqueue_timer, &vptr->mac_regs->RQETMR);
}
}
+
/**
* setup_adaptive_interrupts - Setup interrupt suppression
*
@@ -1611,8 +1593,6 @@ static void velocity_free_rd_ring(struct velocity_info *vptr)
vptr->rx.info = NULL;
}
-
-
/**
* velocity_init_rd_ring - set up receive ring
* @vptr: velocity to configure
@@ -1686,7 +1666,6 @@ static void velocity_free_dma_rings(struct velocity_info *vptr)
pci_free_consistent(vptr->pdev, size, vptr->rx.ring, vptr->rx.pool_dma);
}
-
static int velocity_init_rings(struct velocity_info *vptr, int mtu)
{
int ret;
@@ -1749,7 +1728,6 @@ static void velocity_free_tx_buf(struct velocity_info *vptr,
tdinfo->skb = NULL;
}
-
/*
* FIXME: could we merge this with velocity_free_tx_buf ?
*/
@@ -1797,7 +1775,6 @@ static void velocity_free_td_ring(struct velocity_info *vptr)
}
}
-
static void velocity_free_rings(struct velocity_info *vptr)
{
velocity_free_td_ring(vptr);
@@ -1887,7 +1864,7 @@ static void velocity_error(struct velocity_info *vptr, int status)
else
netif_wake_queue(vptr->dev);
- };
+ }
if (status & ISR_MIBFI)
velocity_update_hw_mibs(vptr);
if (status & ISR_LSTEI)
@@ -2035,7 +2012,6 @@ static inline void velocity_iph_realign(struct velocity_info *vptr,
}
}
-
/**
* velocity_receive_frame - received packet processor
* @vptr: velocity we are handling
@@ -2094,18 +2070,19 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx)
skb_put(skb, pkt_len - 4);
skb->protocol = eth_type_trans(skb, vptr->dev);
- if (vptr->vlgrp && (rd->rdesc0.RSR & RSR_DETAG)) {
- vlan_hwaccel_rx(skb, vptr->vlgrp,
- swab16(le16_to_cpu(rd->rdesc1.PQTAG)));
- } else
- netif_rx(skb);
+ if (rd->rdesc0.RSR & RSR_DETAG) {
+ u16 vid = swab16(le16_to_cpu(rd->rdesc1.PQTAG));
+
+ __vlan_hwaccel_put_tag(skb, vid);
+ }
+ netif_rx(skb);
stats->rx_bytes += pkt_len;
+ stats->rx_packets++;
return 0;
}
-
/**
* velocity_rx_srv - service RX interrupt
* @vptr: velocity
@@ -2413,7 +2390,6 @@ static int velocity_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd
return 0;
}
-
/**
* velocity_ioctl - ioctl entry point
* @dev: network device
@@ -2578,15 +2554,16 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb,
/* Handle fragments */
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- tdinfo->skb_dma[i + 1] = pci_map_page(vptr->pdev, frag->page,
- frag->page_offset, frag->size,
- PCI_DMA_TODEVICE);
+ tdinfo->skb_dma[i + 1] = skb_frag_dma_map(&vptr->pdev->dev,
+ frag, 0,
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
td_ptr->td_buf[i + 1].pa_low = cpu_to_le32(tdinfo->skb_dma[i + 1]);
td_ptr->td_buf[i + 1].pa_high = 0;
- td_ptr->td_buf[i + 1].size = cpu_to_le16(frag->size);
+ td_ptr->td_buf[i + 1].size = cpu_to_le16(skb_frag_size(frag));
}
tdinfo->nskb_dma = i + 1;
@@ -2628,20 +2605,18 @@ out:
return NETDEV_TX_OK;
}
-
static const struct net_device_ops velocity_netdev_ops = {
.ndo_open = velocity_open,
.ndo_stop = velocity_close,
.ndo_start_xmit = velocity_xmit,
.ndo_get_stats = velocity_get_stats,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_set_multicast_list = velocity_set_multi,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_rx_mode = velocity_set_multi,
.ndo_change_mtu = velocity_change_mtu,
.ndo_do_ioctl = velocity_ioctl,
.ndo_vlan_rx_add_vid = velocity_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = velocity_vlan_rx_kill_vid,
- .ndo_vlan_rx_register = velocity_vlan_rx_register,
};
/**
@@ -2727,7 +2702,6 @@ static u32 velocity_get_link(struct net_device *dev)
return BYTE_REG_BITS_IS_ON(PHYSR0_LINKGD, &regs->PHYSR0) ? 1 : 0;
}
-
/**
* velocity_found1 - set up discovered velocity card
* @pdev: PCI device
@@ -2874,7 +2848,6 @@ err_free_dev:
goto out;
}
-
#ifdef CONFIG_PM
/**
* wol_calc_crc - WOL CRC
@@ -3045,7 +3018,7 @@ static int velocity_suspend(struct pci_dev *pdev, pm_message_t state)
spin_lock_irqsave(&vptr->lock, flags);
pci_save_state(pdev);
-#ifdef ETHTOOL_GWOL
+
if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) {
velocity_get_ip(vptr);
velocity_save_context(vptr, &vptr->context);
@@ -3059,9 +3032,7 @@ static int velocity_suspend(struct pci_dev *pdev, pm_message_t state)
pci_disable_device(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
}
-#else
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
-#endif
+
spin_unlock_irqrestore(&vptr->lock, flags);
return 0;
}
@@ -3142,13 +3113,13 @@ static int velocity_resume(struct pci_dev *pdev)
* uses this to handle all our card discover and plugging
*/
static struct pci_driver velocity_driver = {
- .name = VELOCITY_NAME,
- .id_table = velocity_id_table,
- .probe = velocity_found1,
- .remove = __devexit_p(velocity_remove1),
+ .name = VELOCITY_NAME,
+ .id_table = velocity_id_table,
+ .probe = velocity_found1,
+ .remove = __devexit_p(velocity_remove1),
#ifdef CONFIG_PM
- .suspend = velocity_suspend,
- .resume = velocity_resume,
+ .suspend = velocity_suspend,
+ .resume = velocity_resume,
#endif
};
@@ -3458,26 +3429,99 @@ static int velocity_set_coalesce(struct net_device *dev,
return 0;
}
+static const char velocity_gstrings[][ETH_GSTRING_LEN] = {
+ "rx_all",
+ "rx_ok",
+ "tx_ok",
+ "rx_error",
+ "rx_runt_ok",
+ "rx_runt_err",
+ "rx_64",
+ "tx_64",
+ "rx_65_to_127",
+ "tx_65_to_127",
+ "rx_128_to_255",
+ "tx_128_to_255",
+ "rx_256_to_511",
+ "tx_256_to_511",
+ "rx_512_to_1023",
+ "tx_512_to_1023",
+ "rx_1024_to_1518",
+ "tx_1024_to_1518",
+ "tx_ether_collisions",
+ "rx_crc_errors",
+ "rx_jumbo",
+ "tx_jumbo",
+ "rx_mac_control_frames",
+ "tx_mac_control_frames",
+ "rx_frame_alignement_errors",
+ "rx_long_ok",
+ "rx_long_err",
+ "tx_sqe_errors",
+ "rx_no_buf",
+ "rx_symbol_errors",
+ "in_range_length_errors",
+ "late_collisions"
+};
+
+static void velocity_get_strings(struct net_device *dev, u32 sset, u8 *data)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ memcpy(data, *velocity_gstrings, sizeof(velocity_gstrings));
+ break;
+ }
+}
+
+static int velocity_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(velocity_gstrings);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void velocity_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ if (netif_running(dev)) {
+ struct velocity_info *vptr = netdev_priv(dev);
+ u32 *p = vptr->mib_counter;
+ int i;
+
+ spin_lock_irq(&vptr->lock);
+ velocity_update_hw_mibs(vptr);
+ spin_unlock_irq(&vptr->lock);
+
+ for (i = 0; i < ARRAY_SIZE(velocity_gstrings); i++)
+ *data++ = *p++;
+ }
+}
+
static const struct ethtool_ops velocity_ethtool_ops = {
- .get_settings = velocity_get_settings,
- .set_settings = velocity_set_settings,
- .get_drvinfo = velocity_get_drvinfo,
- .get_wol = velocity_ethtool_get_wol,
- .set_wol = velocity_ethtool_set_wol,
- .get_msglevel = velocity_get_msglevel,
- .set_msglevel = velocity_set_msglevel,
- .get_link = velocity_get_link,
- .get_coalesce = velocity_get_coalesce,
- .set_coalesce = velocity_set_coalesce,
- .begin = velocity_ethtool_up,
- .complete = velocity_ethtool_down
+ .get_settings = velocity_get_settings,
+ .set_settings = velocity_set_settings,
+ .get_drvinfo = velocity_get_drvinfo,
+ .get_wol = velocity_ethtool_get_wol,
+ .set_wol = velocity_ethtool_set_wol,
+ .get_msglevel = velocity_get_msglevel,
+ .set_msglevel = velocity_set_msglevel,
+ .get_link = velocity_get_link,
+ .get_strings = velocity_get_strings,
+ .get_sset_count = velocity_get_sset_count,
+ .get_ethtool_stats = velocity_get_ethtool_stats,
+ .get_coalesce = velocity_get_coalesce,
+ .set_coalesce = velocity_set_coalesce,
+ .begin = velocity_ethtool_up,
+ .complete = velocity_ethtool_down
};
-#ifdef CONFIG_PM
-#ifdef CONFIG_INET
+#if defined(CONFIG_PM) && defined(CONFIG_INET)
static int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr)
{
- struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
+ struct in_ifaddr *ifa = ptr;
struct net_device *dev = ifa->ifa_dev->dev;
if (dev_net(dev) == &init_net &&
@@ -3486,12 +3530,9 @@ static int velocity_netdev_event(struct notifier_block *nb, unsigned long notifi
return NOTIFY_DONE;
}
-#endif /* CONFIG_INET */
-#endif /* CONFIG_PM */
-#if defined(CONFIG_PM) && defined(CONFIG_INET)
static struct notifier_block velocity_inetaddr_notifier = {
- .notifier_call = velocity_netdev_event,
+ .notifier_call = velocity_netdev_event,
};
static void velocity_register_notifier(void)
diff --git a/drivers/net/via-velocity.h b/drivers/net/ethernet/via/via-velocity.h
index 0f1f05f6c4f8..4cb9f13485e9 100644
--- a/drivers/net/via-velocity.h
+++ b/drivers/net/ethernet/via/via-velocity.h
@@ -1437,7 +1437,7 @@ struct velocity_info {
struct pci_dev *pdev;
struct net_device *dev;
- struct vlan_group *vlgrp;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
u8 ip_addr[4];
enum chip_type chip_id;
diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig
new file mode 100644
index 000000000000..d5a826063a82
--- /dev/null
+++ b/drivers/net/ethernet/xilinx/Kconfig
@@ -0,0 +1,36 @@
+#
+# Xilink device configuration
+#
+
+config NET_VENDOR_XILINX
+ bool "Xilinx devices"
+ default y
+ depends on PPC || PPC32 || MICROBLAZE
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Xilinx devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_XILINX
+
+config XILINX_EMACLITE
+ tristate "Xilinx 10/100 Ethernet Lite support"
+ depends on (PPC32 || MICROBLAZE)
+ select PHYLIB
+ ---help---
+ This driver supports the 10/100 Ethernet Lite from Xilinx.
+
+config XILINX_LL_TEMAC
+ tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver"
+ depends on (PPC || MICROBLAZE)
+ select PHYLIB
+ ---help---
+ This driver supports the Xilinx 10/100/1000 LocalLink TEMAC
+ core used in Xilinx Spartan and Virtex FPGAs
+
+endif # NET_VENDOR_XILINX
diff --git a/drivers/net/ethernet/xilinx/Makefile b/drivers/net/ethernet/xilinx/Makefile
new file mode 100644
index 000000000000..5feac734ea45
--- /dev/null
+++ b/drivers/net/ethernet/xilinx/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Xilink network device drivers.
+#
+
+ll_temac-objs := ll_temac_main.o ll_temac_mdio.o
+obj-$(CONFIG_XILINX_LL_TEMAC) += ll_temac.o
+obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
diff --git a/drivers/net/ll_temac.h b/drivers/net/ethernet/xilinx/ll_temac.h
index 522abe2ff25a..522abe2ff25a 100644
--- a/drivers/net/ll_temac.h
+++ b/drivers/net/ethernet/xilinx/ll_temac.h
diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c
index b7948ccfcf7d..2681b53820ee 100644
--- a/drivers/net/ll_temac_main.c
+++ b/drivers/net/ethernet/xilinx/ll_temac_main.c
@@ -48,6 +48,8 @@
#include <linux/io.h>
#include <linux/ip.h>
#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
#include "ll_temac.h"
@@ -112,6 +114,7 @@ void temac_indirect_out32(struct temac_local *lp, int reg, u32 value)
return;
temac_iow(lp, XTE_LSW0_OFFSET, value);
temac_iow(lp, XTE_CTL0_OFFSET, CNTLREG_WRITE_ENABLE_MASK | reg);
+ temac_indirect_busywait(lp);
}
/**
@@ -201,6 +204,9 @@ static void temac_dma_bd_release(struct net_device *ndev)
struct temac_local *lp = netdev_priv(ndev);
int i;
+ /* Reset Local Link (DMA) */
+ lp->dma_out(lp, DMA_CONTROL_REG, DMA_CONTROL_RST);
+
for (i = 0; i < RX_BD_NUM; i++) {
if (!lp->rx_skb[i])
break;
@@ -713,10 +719,9 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
cur_p->phys = dma_map_single(ndev->dev.parent,
- (void *)page_address(frag->page) +
- frag->page_offset,
- frag->size, DMA_TO_DEVICE);
- cur_p->len = frag->size;
+ skb_frag_address(frag),
+ skb_frag_size(frag), DMA_TO_DEVICE);
+ cur_p->len = skb_frag_size(frag);
cur_p->app0 = 0;
frag++;
}
@@ -727,6 +732,8 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
if (lp->tx_bd_tail >= TX_BD_NUM)
lp->tx_bd_tail = 0;
+ skb_tx_timestamp(skb);
+
/* Kick off the transfer */
lp->dma_out(lp, TX_TAILDESC_PTR, tail_p); /* DMA start */
@@ -772,7 +779,8 @@ static void ll_temac_recv(struct net_device *ndev)
skb->ip_summed = CHECKSUM_COMPLETE;
}
- netif_rx(skb);
+ if (!skb_defer_rx_timestamp(skb))
+ netif_rx(skb);
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += length;
@@ -856,6 +864,8 @@ static int temac_open(struct net_device *ndev)
phy_start(lp->phy_dev);
}
+ temac_device_reset(ndev);
+
rc = request_irq(lp->tx_irq, ll_temac_tx_irq, 0, ndev->name, ndev);
if (rc)
goto err_tx_irq;
@@ -863,7 +873,6 @@ static int temac_open(struct net_device *ndev)
if (rc)
goto err_rx_irq;
- temac_device_reset(ndev);
return 0;
err_rx_irq:
@@ -917,7 +926,6 @@ static const struct net_device_ops temac_netdev_ops = {
.ndo_start_xmit = temac_start_xmit,
.ndo_set_mac_address = netdev_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
- //.ndo_set_multicast_list = temac_set_multicast_list,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = temac_poll_controller,
#endif
@@ -952,6 +960,32 @@ static const struct attribute_group temac_attr_group = {
.attrs = temac_device_attrs,
};
+/* ethtool support */
+static int temac_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ return phy_ethtool_gset(lp->phy_dev, cmd);
+}
+
+static int temac_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ return phy_ethtool_sset(lp->phy_dev, cmd);
+}
+
+static int temac_nway_reset(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ return phy_start_aneg(lp->phy_dev);
+}
+
+static const struct ethtool_ops temac_ethtool_ops = {
+ .get_settings = temac_get_settings,
+ .set_settings = temac_set_settings,
+ .nway_reset = temac_nway_reset,
+ .get_link = ethtool_op_get_link,
+};
+
static int __devinit temac_of_probe(struct platform_device *op)
{
struct device_node *np;
@@ -973,6 +1007,7 @@ static int __devinit temac_of_probe(struct platform_device *op)
ndev->flags &= ~IFF_MULTICAST; /* clear multicast */
ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST;
ndev->netdev_ops = &temac_netdev_ops;
+ ndev->ethtool_ops = &temac_ethtool_ops;
#if 0
ndev->features |= NETIF_F_IP_CSUM; /* Can checksum TCP/UDP over IPv4. */
ndev->features |= NETIF_F_HW_CSUM; /* Can checksum all the packets. */
diff --git a/drivers/net/ll_temac_mdio.c b/drivers/net/ethernet/xilinx/ll_temac_mdio.c
index 8cf9d4f56bb2..8cf9d4f56bb2 100644
--- a/drivers/net/ll_temac_mdio.c
+++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
index 372572c0adc6..8018d7d045b0 100644
--- a/drivers/net/xilinx_emaclite.c
+++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
@@ -26,6 +26,7 @@
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/phy.h>
+#include <linux/interrupt.h>
#define DRIVER_NAME "xilinx_emaclite"
@@ -251,11 +252,11 @@ static void xemaclite_aligned_write(void *src_ptr, u32 *dest_ptr,
u16 *from_u16_ptr, *to_u16_ptr;
to_u32_ptr = dest_ptr;
- from_u16_ptr = (u16 *) src_ptr;
+ from_u16_ptr = src_ptr;
align_buffer = 0;
for (; length > 3; length -= 4) {
- to_u16_ptr = (u16 *) ((void *) &align_buffer);
+ to_u16_ptr = (u16 *)&align_buffer;
*to_u16_ptr++ = *from_u16_ptr++;
*to_u16_ptr++ = *from_u16_ptr++;
@@ -647,7 +648,8 @@ static void xemaclite_rx_handler(struct net_device *dev)
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
- netif_rx(skb); /* Send the packet upstream */
+ if (!skb_defer_rx_timestamp(skb))
+ netif_rx(skb); /* Send the packet upstream */
}
/**
@@ -1029,15 +1031,19 @@ static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
spin_lock_irqsave(&lp->reset_lock, flags);
if (xemaclite_send_data(lp, (u8 *) new_skb->data, len) != 0) {
/* If the Emaclite Tx buffer is busy, stop the Tx queue and
- * defer the skb for transmission at a later point when the
+ * defer the skb for transmission during the ISR, after the
* current transmission is complete */
netif_stop_queue(dev);
lp->deferred_skb = new_skb;
+ /* Take the time stamp now, since we can't do this in an ISR. */
+ skb_tx_timestamp(new_skb);
spin_unlock_irqrestore(&lp->reset_lock, flags);
return 0;
}
spin_unlock_irqrestore(&lp->reset_lock, flags);
+ skb_tx_timestamp(new_skb);
+
dev->stats.tx_bytes += len;
dev_kfree_skb(new_skb);
diff --git a/drivers/net/ethernet/xircom/Kconfig b/drivers/net/ethernet/xircom/Kconfig
new file mode 100644
index 000000000000..69f56a6de821
--- /dev/null
+++ b/drivers/net/ethernet/xircom/Kconfig
@@ -0,0 +1,31 @@
+#
+# Xircom network device configuration
+#
+
+config NET_VENDOR_XIRCOM
+ bool "Xircom devices"
+ default y
+ depends on PCMCIA
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Xircom cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_XIRCOM
+
+config PCMCIA_XIRC2PS
+ tristate "Xircom 16-bit PCMCIA support"
+ depends on PCMCIA
+ ---help---
+ Say Y here if you intend to attach a Xircom 16-bit PCMCIA (PC-card)
+ Ethernet or Fast Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called xirc2ps_cs. If unsure, say N.
+
+endif # NET_VENDOR_XIRCOM
diff --git a/drivers/net/ethernet/xircom/Makefile b/drivers/net/ethernet/xircom/Makefile
new file mode 100644
index 000000000000..3b7aebd8b849
--- /dev/null
+++ b/drivers/net/ethernet/xircom/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Xircom network device drivers.
+#
+
+obj-$(CONFIG_PCMCIA_XIRC2PS) += xirc2ps_cs.o
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c
index e33b190d716f..bbe8b7dbf3f3 100644
--- a/drivers/net/pcmcia/xirc2ps_cs.c
+++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c
@@ -467,7 +467,7 @@ static const struct net_device_ops netdev_ops = {
.ndo_tx_timeout = xirc_tx_timeout,
.ndo_set_config = do_config,
.ndo_do_ioctl = do_ioctl,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/ethernet/xscale/Kconfig b/drivers/net/ethernet/xscale/Kconfig
new file mode 100644
index 000000000000..cf67352cea14
--- /dev/null
+++ b/drivers/net/ethernet/xscale/Kconfig
@@ -0,0 +1,32 @@
+#
+# Intel XScale IXP device configuration
+#
+
+config NET_VENDOR_XSCALE
+ bool "Intel XScale IXP devices"
+ default y
+ depends on NET_VENDOR_INTEL && ((ARM && ARCH_IXP4XX && \
+ IXP4XX_NPE && IXP4XX_QMGR) || ARCH_ENP2611)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question does not directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about XSacle IXP devices. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_VENDOR_XSCALE
+
+config IXP4XX_ETH
+ tristate "Intel IXP4xx Ethernet support"
+ depends on ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR
+ select PHYLIB
+ ---help---
+ Say Y here if you want to use built-in Ethernet ports
+ on IXP4xx processor.
+
+source "drivers/net/ethernet/xscale/ixp2000/Kconfig"
+
+endif # NET_VENDOR_XSCALE
diff --git a/drivers/net/ethernet/xscale/Makefile b/drivers/net/ethernet/xscale/Makefile
new file mode 100644
index 000000000000..b195b9d7fe81
--- /dev/null
+++ b/drivers/net/ethernet/xscale/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Intel XScale IXP device drivers.
+#
+
+obj-$(CONFIG_ENP2611_MSF_NET) += ixp2000/
+obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
diff --git a/drivers/net/ixp2000/Kconfig b/drivers/net/ethernet/xscale/ixp2000/Kconfig
index 2fec2415651f..58dbc5b876bc 100644
--- a/drivers/net/ixp2000/Kconfig
+++ b/drivers/net/ethernet/xscale/ixp2000/Kconfig
@@ -1,6 +1,6 @@
config ENP2611_MSF_NET
tristate "Radisys ENP2611 MSF network interface support"
depends on ARCH_ENP2611
- help
+ ---help---
This is a driver for the MSF network interface unit in
the IXP2400 on the Radisys ENP2611 platform.
diff --git a/drivers/net/ixp2000/Makefile b/drivers/net/ethernet/xscale/ixp2000/Makefile
index fd38351ceaa7..fd38351ceaa7 100644
--- a/drivers/net/ixp2000/Makefile
+++ b/drivers/net/ethernet/xscale/ixp2000/Makefile
diff --git a/drivers/net/ixp2000/caleb.c b/drivers/net/ethernet/xscale/ixp2000/caleb.c
index 7dea5b95012c..7dea5b95012c 100644
--- a/drivers/net/ixp2000/caleb.c
+++ b/drivers/net/ethernet/xscale/ixp2000/caleb.c
diff --git a/drivers/net/ixp2000/caleb.h b/drivers/net/ethernet/xscale/ixp2000/caleb.h
index e93a1ef5b8a3..e93a1ef5b8a3 100644
--- a/drivers/net/ixp2000/caleb.h
+++ b/drivers/net/ethernet/xscale/ixp2000/caleb.h
diff --git a/drivers/net/ixp2000/enp2611.c b/drivers/net/ethernet/xscale/ixp2000/enp2611.c
index 34a6cfd17930..34a6cfd17930 100644
--- a/drivers/net/ixp2000/enp2611.c
+++ b/drivers/net/ethernet/xscale/ixp2000/enp2611.c
diff --git a/drivers/net/ixp2000/ixp2400-msf.c b/drivers/net/ethernet/xscale/ixp2000/ixp2400-msf.c
index f5ffd7e05d26..f5ffd7e05d26 100644
--- a/drivers/net/ixp2000/ixp2400-msf.c
+++ b/drivers/net/ethernet/xscale/ixp2000/ixp2400-msf.c
diff --git a/drivers/net/ixp2000/ixp2400-msf.h b/drivers/net/ethernet/xscale/ixp2000/ixp2400-msf.h
index 3ac1af2771da..3ac1af2771da 100644
--- a/drivers/net/ixp2000/ixp2400-msf.h
+++ b/drivers/net/ethernet/xscale/ixp2000/ixp2400-msf.h
diff --git a/drivers/net/ixp2000/ixp2400_rx.uc b/drivers/net/ethernet/xscale/ixp2000/ixp2400_rx.uc
index 42a73e357afa..42a73e357afa 100644
--- a/drivers/net/ixp2000/ixp2400_rx.uc
+++ b/drivers/net/ethernet/xscale/ixp2000/ixp2400_rx.uc
diff --git a/drivers/net/ixp2000/ixp2400_rx.ucode b/drivers/net/ethernet/xscale/ixp2000/ixp2400_rx.ucode
index e8aee2f81aad..e8aee2f81aad 100644
--- a/drivers/net/ixp2000/ixp2400_rx.ucode
+++ b/drivers/net/ethernet/xscale/ixp2000/ixp2400_rx.ucode
diff --git a/drivers/net/ixp2000/ixp2400_tx.uc b/drivers/net/ethernet/xscale/ixp2000/ixp2400_tx.uc
index d090d1884fb7..d090d1884fb7 100644
--- a/drivers/net/ixp2000/ixp2400_tx.uc
+++ b/drivers/net/ethernet/xscale/ixp2000/ixp2400_tx.uc
diff --git a/drivers/net/ixp2000/ixp2400_tx.ucode b/drivers/net/ethernet/xscale/ixp2000/ixp2400_tx.ucode
index a433e24b0a51..a433e24b0a51 100644
--- a/drivers/net/ixp2000/ixp2400_tx.ucode
+++ b/drivers/net/ethernet/xscale/ixp2000/ixp2400_tx.ucode
diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ethernet/xscale/ixp2000/ixpdev.c
index 78ddd8b79e7e..e122493ab70e 100644
--- a/drivers/net/ixp2000/ixpdev.c
+++ b/drivers/net/ethernet/xscale/ixp2000/ixpdev.c
@@ -14,6 +14,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <linux/gfp.h>
#include <asm/hardware/uengine.h>
diff --git a/drivers/net/ixp2000/ixpdev.h b/drivers/net/ethernet/xscale/ixp2000/ixpdev.h
index 391ece623243..391ece623243 100644
--- a/drivers/net/ixp2000/ixpdev.h
+++ b/drivers/net/ethernet/xscale/ixp2000/ixpdev.h
diff --git a/drivers/net/ixp2000/ixpdev_priv.h b/drivers/net/ethernet/xscale/ixp2000/ixpdev_priv.h
index 86aa08ea0c33..86aa08ea0c33 100644
--- a/drivers/net/ixp2000/ixpdev_priv.h
+++ b/drivers/net/ethernet/xscale/ixp2000/ixpdev_priv.h
diff --git a/drivers/net/ixp2000/pm3386.c b/drivers/net/ethernet/xscale/ixp2000/pm3386.c
index e08d3f9863b8..e08d3f9863b8 100644
--- a/drivers/net/ixp2000/pm3386.c
+++ b/drivers/net/ethernet/xscale/ixp2000/pm3386.c
diff --git a/drivers/net/ixp2000/pm3386.h b/drivers/net/ethernet/xscale/ixp2000/pm3386.h
index cc4183dca911..cc4183dca911 100644
--- a/drivers/net/ixp2000/pm3386.h
+++ b/drivers/net/ethernet/xscale/ixp2000/pm3386.h
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c
index de51e8453c13..f45c85a84261 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c
@@ -35,6 +35,7 @@
#include <linux/platform_device.h>
#include <linux/ptp_classify.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <mach/ixp46x_ts.h>
#include <mach/npe.h>
#include <mach/qmgr.h>
@@ -1339,7 +1340,7 @@ static const struct net_device_ops ixp4xx_netdev_ops = {
.ndo_open = eth_open,
.ndo_stop = eth_close,
.ndo_start_xmit = eth_xmit,
- .ndo_set_multicast_list = eth_set_mcast_list,
+ .ndo_set_rx_mode = eth_set_mcast_list,
.ndo_do_ioctl = eth_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/fddi/Kconfig b/drivers/net/fddi/Kconfig
new file mode 100644
index 000000000000..3a424c864f4d
--- /dev/null
+++ b/drivers/net/fddi/Kconfig
@@ -0,0 +1,77 @@
+#
+# FDDI network device configuration
+#
+
+config FDDI
+ tristate "FDDI driver support"
+ depends on PCI || EISA || TC
+ ---help---
+ Fiber Distributed Data Interface is a high speed local area network
+ design; essentially a replacement for high speed Ethernet. FDDI can
+ run over copper or fiber. If you are connected to such a network and
+ want a driver for the FDDI card in your computer, say Y here (and
+ then also Y to the driver for your FDDI card, below). Most people
+ will say N.
+
+if FDDI
+
+config DEFXX
+ tristate "Digital DEFTA/DEFEA/DEFPA adapter support"
+ depends on FDDI && (PCI || EISA || TC)
+ ---help---
+ This is support for the DIGITAL series of TURBOchannel (DEFTA),
+ EISA (DEFEA) and PCI (DEFPA) controllers which can connect you
+ to a local FDDI network.
+
+ To compile this driver as a module, choose M here: the module
+ will be called defxx. If unsure, say N.
+
+config DEFXX_MMIO
+ bool
+ prompt "Use MMIO instead of PIO" if PCI || EISA
+ depends on DEFXX
+ default n if PCI || EISA
+ default y
+ ---help---
+ This instructs the driver to use EISA or PCI memory-mapped I/O
+ (MMIO) as appropriate instead of programmed I/O ports (PIO).
+ Enabling this gives an improvement in processing time in parts
+ of the driver, but it may cause problems with EISA (DEFEA)
+ adapters. TURBOchannel does not have the concept of I/O ports,
+ so MMIO is always used for these (DEFTA) adapters.
+
+ If unsure, say N.
+
+config SKFP
+ tristate "SysKonnect FDDI PCI support"
+ depends on FDDI && PCI
+ select BITREVERSE
+ ---help---
+ Say Y here if you have a SysKonnect FDDI PCI adapter.
+ The following adapters are supported by this driver:
+ - SK-5521 (SK-NET FDDI-UP)
+ - SK-5522 (SK-NET FDDI-UP DAS)
+ - SK-5541 (SK-NET FDDI-FP)
+ - SK-5543 (SK-NET FDDI-LP)
+ - SK-5544 (SK-NET FDDI-LP DAS)
+ - SK-5821 (SK-NET FDDI-UP64)
+ - SK-5822 (SK-NET FDDI-UP64 DAS)
+ - SK-5841 (SK-NET FDDI-FP64)
+ - SK-5843 (SK-NET FDDI-LP64)
+ - SK-5844 (SK-NET FDDI-LP64 DAS)
+ - Netelligent 100 FDDI DAS Fibre SC
+ - Netelligent 100 FDDI SAS Fibre SC
+ - Netelligent 100 FDDI DAS UTP
+ - Netelligent 100 FDDI SAS UTP
+ - Netelligent 100 FDDI SAS Fibre MIC
+
+ Read <file:Documentation/networking/skfp.txt> for information about
+ the driver.
+
+ Questions concerning this driver can be addressed to:
+ <linux@syskonnect.de>
+
+ To compile this driver as a module, choose M here: the module
+ will be called skfp. This is recommended.
+
+endif # FDDI
diff --git a/drivers/net/fddi/Makefile b/drivers/net/fddi/Makefile
new file mode 100644
index 000000000000..36da19c9a8aa
--- /dev/null
+++ b/drivers/net/fddi/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux FDDI network device drivers.
+#
+
+obj-$(CONFIG_DEFXX) += defxx.o
+obj-$(CONFIG_SKFP) += skfp/
diff --git a/drivers/net/defxx.c b/drivers/net/fddi/defxx.c
index 417e14385623..4ad80f771099 100644
--- a/drivers/net/defxx.c
+++ b/drivers/net/fddi/defxx.c
@@ -483,7 +483,7 @@ static const struct net_device_ops dfx_netdev_ops = {
.ndo_stop = dfx_close,
.ndo_start_xmit = dfx_xmt_queue_pkt,
.ndo_get_stats = dfx_ctl_get_stats,
- .ndo_set_multicast_list = dfx_ctl_set_multicast_list,
+ .ndo_set_rx_mode = dfx_ctl_set_multicast_list,
.ndo_set_mac_address = dfx_ctl_set_mac_address,
};
diff --git a/drivers/net/defxx.h b/drivers/net/fddi/defxx.h
index 19a6f64df198..19a6f64df198 100644
--- a/drivers/net/defxx.h
+++ b/drivers/net/fddi/defxx.h
diff --git a/drivers/net/skfp/Makefile b/drivers/net/fddi/skfp/Makefile
index b0be0234abf6..b0be0234abf6 100644
--- a/drivers/net/skfp/Makefile
+++ b/drivers/net/fddi/skfp/Makefile
diff --git a/drivers/net/skfp/cfm.c b/drivers/net/fddi/skfp/cfm.c
index e395ace3120b..e395ace3120b 100644
--- a/drivers/net/skfp/cfm.c
+++ b/drivers/net/fddi/skfp/cfm.c
diff --git a/drivers/net/skfp/drvfbi.c b/drivers/net/fddi/skfp/drvfbi.c
index 07da97c303d6..07da97c303d6 100644
--- a/drivers/net/skfp/drvfbi.c
+++ b/drivers/net/fddi/skfp/drvfbi.c
diff --git a/drivers/net/skfp/ecm.c b/drivers/net/fddi/skfp/ecm.c
index 47d922cb3c08..47d922cb3c08 100644
--- a/drivers/net/skfp/ecm.c
+++ b/drivers/net/fddi/skfp/ecm.c
diff --git a/drivers/net/skfp/ess.c b/drivers/net/fddi/skfp/ess.c
index 2fc5987b41dc..2fc5987b41dc 100644
--- a/drivers/net/skfp/ess.c
+++ b/drivers/net/fddi/skfp/ess.c
diff --git a/drivers/net/skfp/fplustm.c b/drivers/net/fddi/skfp/fplustm.c
index a20ed1a98099..a20ed1a98099 100644
--- a/drivers/net/skfp/fplustm.c
+++ b/drivers/net/fddi/skfp/fplustm.c
diff --git a/drivers/net/skfp/h/cmtdef.h b/drivers/net/fddi/skfp/h/cmtdef.h
index 5a6c6122ccb0..f5bc90ff2a2a 100644
--- a/drivers/net/skfp/h/cmtdef.h
+++ b/drivers/net/fddi/skfp/h/cmtdef.h
@@ -477,8 +477,8 @@ struct s_plc {
/*
* function prototypes
*/
-#include "h/mbuf.h" /* Type definitions for MBUFs */
-#include "h/smtstate.h" /* struct smt_state */
+#include "mbuf.h" /* Type definitions for MBUFs */
+#include "smtstate.h" /* struct smt_state */
void hwt_restart(struct s_smc *smc); /* hwt.c */
SMbuf *smt_build_frame(struct s_smc *smc, int class, int type,
diff --git a/drivers/net/skfp/h/fddi.h b/drivers/net/fddi/skfp/h/fddi.h
index c9a28a8a383b..c9a28a8a383b 100644
--- a/drivers/net/skfp/h/fddi.h
+++ b/drivers/net/fddi/skfp/h/fddi.h
diff --git a/drivers/net/skfp/h/fddimib.h b/drivers/net/fddi/skfp/h/fddimib.h
index d1acdc773950..d1acdc773950 100644
--- a/drivers/net/skfp/h/fddimib.h
+++ b/drivers/net/fddi/skfp/h/fddimib.h
diff --git a/drivers/net/skfp/h/fplustm.h b/drivers/net/fddi/skfp/h/fplustm.h
index d43191ed938b..d43191ed938b 100644
--- a/drivers/net/skfp/h/fplustm.h
+++ b/drivers/net/fddi/skfp/h/fplustm.h
diff --git a/drivers/net/skfp/h/hwmtm.h b/drivers/net/fddi/skfp/h/hwmtm.h
index e1a7e5f683dc..5924d4219e9e 100644
--- a/drivers/net/skfp/h/hwmtm.h
+++ b/drivers/net/fddi/skfp/h/hwmtm.h
@@ -15,7 +15,7 @@
#ifndef _HWM_
#define _HWM_
-#include "h/mbuf.h"
+#include "mbuf.h"
/*
* MACRO for DMA synchronization:
diff --git a/drivers/net/skfp/h/mbuf.h b/drivers/net/fddi/skfp/h/mbuf.h
index f2aadcda9e7f..f2aadcda9e7f 100644
--- a/drivers/net/skfp/h/mbuf.h
+++ b/drivers/net/fddi/skfp/h/mbuf.h
diff --git a/drivers/net/skfp/h/osdef1st.h b/drivers/net/fddi/skfp/h/osdef1st.h
index 763ca18cbea8..763ca18cbea8 100644
--- a/drivers/net/skfp/h/osdef1st.h
+++ b/drivers/net/fddi/skfp/h/osdef1st.h
diff --git a/drivers/net/skfp/h/sba.h b/drivers/net/fddi/skfp/h/sba.h
index 638cf0283bc4..35ddb44a1120 100644
--- a/drivers/net/skfp/h/sba.h
+++ b/drivers/net/fddi/skfp/h/sba.h
@@ -19,8 +19,8 @@
#ifndef _SBA_
#define _SBA_
-#include "h/mbuf.h"
-#include "h/sba_def.h"
+#include "mbuf.h"
+#include "sba_def.h"
#ifdef SBA
diff --git a/drivers/net/skfp/h/sba_def.h b/drivers/net/fddi/skfp/h/sba_def.h
index 0459a095d0cd..0459a095d0cd 100644
--- a/drivers/net/skfp/h/sba_def.h
+++ b/drivers/net/fddi/skfp/h/sba_def.h
diff --git a/drivers/net/skfp/h/skfbi.h b/drivers/net/fddi/skfp/h/skfbi.h
index c1ba26c06d73..c1ba26c06d73 100644
--- a/drivers/net/skfp/h/skfbi.h
+++ b/drivers/net/fddi/skfp/h/skfbi.h
diff --git a/drivers/net/skfp/h/skfbiinc.h b/drivers/net/fddi/skfp/h/skfbiinc.h
index ac2d7192f1ca..ce72557c354c 100644
--- a/drivers/net/skfp/h/skfbiinc.h
+++ b/drivers/net/fddi/skfp/h/skfbiinc.h
@@ -15,7 +15,7 @@
#ifndef _SKFBIINC_
#define _SKFBIINC_
-#include "h/supern_2.h"
+#include "supern_2.h"
/*
* special defines for use into .asm files
diff --git a/drivers/net/skfp/h/smc.h b/drivers/net/fddi/skfp/h/smc.h
index c774a95902f5..3ca308b28214 100644
--- a/drivers/net/skfp/h/smc.h
+++ b/drivers/net/fddi/skfp/h/smc.h
@@ -38,18 +38,18 @@
* fddi.h
*/
#ifdef OSDEF
-#include "h/osdef1st.h"
+#include "osdef1st.h"
#endif /* OSDEF */
#ifdef OEM_CONCEPT
#include "oemdef.h"
#endif /* OEM_CONCEPT */
-#include "h/smt.h"
-#include "h/cmtdef.h"
-#include "h/fddimib.h"
-#include "h/targethw.h" /* all target hw dependencies */
-#include "h/targetos.h" /* all target os dependencies */
+#include "smt.h"
+#include "cmtdef.h"
+#include "fddimib.h"
+#include "targethw.h" /* all target hw dependencies */
+#include "targetos.h" /* all target os dependencies */
#ifdef ESS
-#include "h/sba.h"
+#include "sba.h"
#endif
/*
diff --git a/drivers/net/skfp/h/smt.h b/drivers/net/fddi/skfp/h/smt.h
index 2030f9cbb24b..2030f9cbb24b 100644
--- a/drivers/net/skfp/h/smt.h
+++ b/drivers/net/fddi/skfp/h/smt.h
diff --git a/drivers/net/skfp/h/smt_p.h b/drivers/net/fddi/skfp/h/smt_p.h
index 99f9be9552bb..99f9be9552bb 100644
--- a/drivers/net/skfp/h/smt_p.h
+++ b/drivers/net/fddi/skfp/h/smt_p.h
diff --git a/drivers/net/skfp/h/smtstate.h b/drivers/net/fddi/skfp/h/smtstate.h
index 62fe695077a9..62fe695077a9 100644
--- a/drivers/net/skfp/h/smtstate.h
+++ b/drivers/net/fddi/skfp/h/smtstate.h
diff --git a/drivers/net/skfp/h/supern_2.h b/drivers/net/fddi/skfp/h/supern_2.h
index 0b73690280f6..0b73690280f6 100644
--- a/drivers/net/skfp/h/supern_2.h
+++ b/drivers/net/fddi/skfp/h/supern_2.h
diff --git a/drivers/net/skfp/h/targethw.h b/drivers/net/fddi/skfp/h/targethw.h
index 626dc7263591..842a690446f3 100644
--- a/drivers/net/skfp/h/targethw.h
+++ b/drivers/net/fddi/skfp/h/targethw.h
@@ -25,11 +25,11 @@
#define SK_ML_ID_2 0x30
#endif
-#include "h/skfbi.h"
+#include "skfbi.h"
#ifndef TAG_MODE
-#include "h/fplus.h"
+#include "fplus.h"
#else
-#include "h/fplustm.h"
+#include "fplustm.h"
#endif
#ifndef HW_PTR
diff --git a/drivers/net/skfp/h/targetos.h b/drivers/net/fddi/skfp/h/targetos.h
index 5d940e7b8ea0..53bacc107160 100644
--- a/drivers/net/skfp/h/targetos.h
+++ b/drivers/net/fddi/skfp/h/targetos.h
@@ -58,7 +58,7 @@
#define ADDR(a) (((a)>>7) ? (outp(smc->hw.iop+B0_RAP,(a)>>7), (smc->hw.iop+( ((a)&0x7F) | ((a)>>7 ? 0x80:0)) )) : (smc->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0))))
#endif
-#include "h/hwmtm.h"
+#include "hwmtm.h"
#define TRUE 1
#define FALSE 0
diff --git a/drivers/net/skfp/h/types.h b/drivers/net/fddi/skfp/h/types.h
index 5a3bf8378f9e..5a3bf8378f9e 100644
--- a/drivers/net/skfp/h/types.h
+++ b/drivers/net/fddi/skfp/h/types.h
diff --git a/drivers/net/skfp/hwmtm.c b/drivers/net/fddi/skfp/hwmtm.c
index e26398b5a7dc..e26398b5a7dc 100644
--- a/drivers/net/skfp/hwmtm.c
+++ b/drivers/net/fddi/skfp/hwmtm.c
diff --git a/drivers/net/skfp/hwt.c b/drivers/net/fddi/skfp/hwt.c
index c0798fd2ca69..c0798fd2ca69 100644
--- a/drivers/net/skfp/hwt.c
+++ b/drivers/net/fddi/skfp/hwt.c
diff --git a/drivers/net/skfp/pcmplc.c b/drivers/net/fddi/skfp/pcmplc.c
index 88d02d0a42c4..88d02d0a42c4 100644
--- a/drivers/net/skfp/pcmplc.c
+++ b/drivers/net/fddi/skfp/pcmplc.c
diff --git a/drivers/net/skfp/pmf.c b/drivers/net/fddi/skfp/pmf.c
index 9ac4665d7411..9ac4665d7411 100644
--- a/drivers/net/skfp/pmf.c
+++ b/drivers/net/fddi/skfp/pmf.c
diff --git a/drivers/net/skfp/queue.c b/drivers/net/fddi/skfp/queue.c
index c1a0df455a59..c1a0df455a59 100644
--- a/drivers/net/skfp/queue.c
+++ b/drivers/net/fddi/skfp/queue.c
diff --git a/drivers/net/skfp/rmt.c b/drivers/net/fddi/skfp/rmt.c
index ef8d5672d9e8..ef8d5672d9e8 100644
--- a/drivers/net/skfp/rmt.c
+++ b/drivers/net/fddi/skfp/rmt.c
diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/fddi/skfp/skfddi.c
index 16c62659cdd9..3d9a4596a423 100644
--- a/drivers/net/skfp/skfddi.c
+++ b/drivers/net/fddi/skfp/skfddi.c
@@ -167,7 +167,7 @@ static const struct net_device_ops skfp_netdev_ops = {
.ndo_start_xmit = skfp_send_pkt,
.ndo_get_stats = skfp_ctl_get_stats,
.ndo_change_mtu = fddi_change_mtu,
- .ndo_set_multicast_list = skfp_ctl_set_multicast_list,
+ .ndo_set_rx_mode = skfp_ctl_set_multicast_list,
.ndo_set_mac_address = skfp_ctl_set_mac_address,
.ndo_do_ioctl = skfp_ioctl,
};
diff --git a/drivers/net/skfp/smt.c b/drivers/net/fddi/skfp/smt.c
index 08d94329c12f..08d94329c12f 100644
--- a/drivers/net/skfp/smt.c
+++ b/drivers/net/fddi/skfp/smt.c
diff --git a/drivers/net/skfp/smtdef.c b/drivers/net/fddi/skfp/smtdef.c
index 1acab0b368e3..1acab0b368e3 100644
--- a/drivers/net/skfp/smtdef.c
+++ b/drivers/net/fddi/skfp/smtdef.c
diff --git a/drivers/net/skfp/smtinit.c b/drivers/net/fddi/skfp/smtinit.c
index e3a0c0bc2233..e3a0c0bc2233 100644
--- a/drivers/net/skfp/smtinit.c
+++ b/drivers/net/fddi/skfp/smtinit.c
diff --git a/drivers/net/skfp/smttimer.c b/drivers/net/fddi/skfp/smttimer.c
index 531795e98c30..531795e98c30 100644
--- a/drivers/net/skfp/smttimer.c
+++ b/drivers/net/fddi/skfp/smttimer.c
diff --git a/drivers/net/skfp/srf.c b/drivers/net/fddi/skfp/srf.c
index f6f7baf9f27a..f6f7baf9f27a 100644
--- a/drivers/net/skfp/srf.c
+++ b/drivers/net/fddi/skfp/srf.c
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
deleted file mode 100644
index 239e3330495f..000000000000
--- a/drivers/net/gianfar_ethtool.c
+++ /dev/null
@@ -1,813 +0,0 @@
-/*
- * drivers/net/gianfar_ethtool.c
- *
- * Gianfar Ethernet Driver
- * Ethtool support for Gianfar Enet
- * Based on e1000 ethtool support
- *
- * Author: Andy Fleming
- * Maintainer: Kumar Gala
- * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
- *
- * Copyright 2003-2006, 2008-2009, 2011 Freescale Semiconductor, Inc.
- *
- * This software may be used and distributed according to
- * the terms of the GNU Public License, Version 2, incorporated herein
- * by reference.
- */
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include <linux/module.h>
-#include <linux/crc32.h>
-#include <asm/types.h>
-#include <linux/ethtool.h>
-#include <linux/mii.h>
-#include <linux/phy.h>
-
-#include "gianfar.h"
-
-extern void gfar_start(struct net_device *dev);
-extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
-
-#define GFAR_MAX_COAL_USECS 0xffff
-#define GFAR_MAX_COAL_FRAMES 0xff
-static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
- u64 * buf);
-static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf);
-static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
-static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
-static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
-static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
-static void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo);
-
-static char stat_gstrings[][ETH_GSTRING_LEN] = {
- "rx-dropped-by-kernel",
- "rx-large-frame-errors",
- "rx-short-frame-errors",
- "rx-non-octet-errors",
- "rx-crc-errors",
- "rx-overrun-errors",
- "rx-busy-errors",
- "rx-babbling-errors",
- "rx-truncated-frames",
- "ethernet-bus-error",
- "tx-babbling-errors",
- "tx-underrun-errors",
- "rx-skb-missing-errors",
- "tx-timeout-errors",
- "tx-rx-64-frames",
- "tx-rx-65-127-frames",
- "tx-rx-128-255-frames",
- "tx-rx-256-511-frames",
- "tx-rx-512-1023-frames",
- "tx-rx-1024-1518-frames",
- "tx-rx-1519-1522-good-vlan",
- "rx-bytes",
- "rx-packets",
- "rx-fcs-errors",
- "receive-multicast-packet",
- "receive-broadcast-packet",
- "rx-control-frame-packets",
- "rx-pause-frame-packets",
- "rx-unknown-op-code",
- "rx-alignment-error",
- "rx-frame-length-error",
- "rx-code-error",
- "rx-carrier-sense-error",
- "rx-undersize-packets",
- "rx-oversize-packets",
- "rx-fragmented-frames",
- "rx-jabber-frames",
- "rx-dropped-frames",
- "tx-byte-counter",
- "tx-packets",
- "tx-multicast-packets",
- "tx-broadcast-packets",
- "tx-pause-control-frames",
- "tx-deferral-packets",
- "tx-excessive-deferral-packets",
- "tx-single-collision-packets",
- "tx-multiple-collision-packets",
- "tx-late-collision-packets",
- "tx-excessive-collision-packets",
- "tx-total-collision",
- "reserved",
- "tx-dropped-frames",
- "tx-jabber-frames",
- "tx-fcs-errors",
- "tx-control-frames",
- "tx-oversize-frames",
- "tx-undersize-frames",
- "tx-fragmented-frames",
-};
-
-/* Fill in a buffer with the strings which correspond to the
- * stats */
-static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
-{
- struct gfar_private *priv = netdev_priv(dev);
-
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
- memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN);
- else
- memcpy(buf, stat_gstrings,
- GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN);
-}
-
-/* Fill in an array of 64-bit statistics from various sources.
- * This array will be appended to the end of the ethtool_stats
- * structure, and returned to user space
- */
-static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
-{
- int i;
- struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp[0].regs;
- u64 *extra = (u64 *) & priv->extra_stats;
-
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
- u32 __iomem *rmon = (u32 __iomem *) &regs->rmon;
- struct gfar_stats *stats = (struct gfar_stats *) buf;
-
- for (i = 0; i < GFAR_RMON_LEN; i++)
- stats->rmon[i] = (u64) gfar_read(&rmon[i]);
-
- for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++)
- stats->extra[i] = extra[i];
- } else
- for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++)
- buf[i] = extra[i];
-}
-
-static int gfar_sset_count(struct net_device *dev, int sset)
-{
- struct gfar_private *priv = netdev_priv(dev);
-
- switch (sset) {
- case ETH_SS_STATS:
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
- return GFAR_STATS_LEN;
- else
- return GFAR_EXTRA_STATS_LEN;
- default:
- return -EOPNOTSUPP;
- }
-}
-
-/* Fills in the drvinfo structure with some basic info */
-static void gfar_gdrvinfo(struct net_device *dev, struct
- ethtool_drvinfo *drvinfo)
-{
- strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN);
- strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN);
- strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN);
- strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN);
- drvinfo->regdump_len = 0;
- drvinfo->eedump_len = 0;
-}
-
-
-static int gfar_ssettings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
- struct gfar_private *priv = netdev_priv(dev);
- struct phy_device *phydev = priv->phydev;
-
- if (NULL == phydev)
- return -ENODEV;
-
- return phy_ethtool_sset(phydev, cmd);
-}
-
-
-/* Return the current settings in the ethtool_cmd structure */
-static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
- struct gfar_private *priv = netdev_priv(dev);
- struct phy_device *phydev = priv->phydev;
- struct gfar_priv_rx_q *rx_queue = NULL;
- struct gfar_priv_tx_q *tx_queue = NULL;
-
- if (NULL == phydev)
- return -ENODEV;
- tx_queue = priv->tx_queue[0];
- rx_queue = priv->rx_queue[0];
-
- /* etsec-1.7 and older versions have only one txic
- * and rxic regs although they support multiple queues */
- cmd->maxtxpkt = get_icft_value(tx_queue->txic);
- cmd->maxrxpkt = get_icft_value(rx_queue->rxic);
-
- return phy_ethtool_gset(phydev, cmd);
-}
-
-/* Return the length of the register structure */
-static int gfar_reglen(struct net_device *dev)
-{
- return sizeof (struct gfar);
-}
-
-/* Return a dump of the GFAR register space */
-static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
-{
- int i;
- struct gfar_private *priv = netdev_priv(dev);
- u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp[0].regs;
- u32 *buf = (u32 *) regbuf;
-
- for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++)
- buf[i] = gfar_read(&theregs[i]);
-}
-
-/* Convert microseconds to ethernet clock ticks, which changes
- * depending on what speed the controller is running at */
-static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int usecs)
-{
- unsigned int count;
-
- /* The timer is different, depending on the interface speed */
- switch (priv->phydev->speed) {
- case SPEED_1000:
- count = GFAR_GBIT_TIME;
- break;
- case SPEED_100:
- count = GFAR_100_TIME;
- break;
- case SPEED_10:
- default:
- count = GFAR_10_TIME;
- break;
- }
-
- /* Make sure we return a number greater than 0
- * if usecs > 0 */
- return (usecs * 1000 + count - 1) / count;
-}
-
-/* Convert ethernet clock ticks to microseconds */
-static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int ticks)
-{
- unsigned int count;
-
- /* The timer is different, depending on the interface speed */
- switch (priv->phydev->speed) {
- case SPEED_1000:
- count = GFAR_GBIT_TIME;
- break;
- case SPEED_100:
- count = GFAR_100_TIME;
- break;
- case SPEED_10:
- default:
- count = GFAR_10_TIME;
- break;
- }
-
- /* Make sure we return a number greater than 0 */
- /* if ticks is > 0 */
- return (ticks * count) / 1000;
-}
-
-/* Get the coalescing parameters, and put them in the cvals
- * structure. */
-static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
-{
- struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_rx_q *rx_queue = NULL;
- struct gfar_priv_tx_q *tx_queue = NULL;
- unsigned long rxtime;
- unsigned long rxcount;
- unsigned long txtime;
- unsigned long txcount;
-
- if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
- return -EOPNOTSUPP;
-
- if (NULL == priv->phydev)
- return -ENODEV;
-
- rx_queue = priv->rx_queue[0];
- tx_queue = priv->tx_queue[0];
-
- rxtime = get_ictt_value(rx_queue->rxic);
- rxcount = get_icft_value(rx_queue->rxic);
- txtime = get_ictt_value(tx_queue->txic);
- txcount = get_icft_value(tx_queue->txic);
- cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, rxtime);
- cvals->rx_max_coalesced_frames = rxcount;
-
- cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, txtime);
- cvals->tx_max_coalesced_frames = txcount;
-
- cvals->use_adaptive_rx_coalesce = 0;
- cvals->use_adaptive_tx_coalesce = 0;
-
- cvals->pkt_rate_low = 0;
- cvals->rx_coalesce_usecs_low = 0;
- cvals->rx_max_coalesced_frames_low = 0;
- cvals->tx_coalesce_usecs_low = 0;
- cvals->tx_max_coalesced_frames_low = 0;
-
- /* When the packet rate is below pkt_rate_high but above
- * pkt_rate_low (both measured in packets per second) the
- * normal {rx,tx}_* coalescing parameters are used.
- */
-
- /* When the packet rate is (measured in packets per second)
- * is above pkt_rate_high, the {rx,tx}_*_high parameters are
- * used.
- */
- cvals->pkt_rate_high = 0;
- cvals->rx_coalesce_usecs_high = 0;
- cvals->rx_max_coalesced_frames_high = 0;
- cvals->tx_coalesce_usecs_high = 0;
- cvals->tx_max_coalesced_frames_high = 0;
-
- /* How often to do adaptive coalescing packet rate sampling,
- * measured in seconds. Must not be zero.
- */
- cvals->rate_sample_interval = 0;
-
- return 0;
-}
-
-/* Change the coalescing values.
- * Both cvals->*_usecs and cvals->*_frames have to be > 0
- * in order for coalescing to be active
- */
-static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
-{
- struct gfar_private *priv = netdev_priv(dev);
- int i = 0;
-
- if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
- return -EOPNOTSUPP;
-
- /* Set up rx coalescing */
- /* As of now, we will enable/disable coalescing for all
- * queues together in case of eTSEC2, this will be modified
- * along with the ethtool interface */
- if ((cvals->rx_coalesce_usecs == 0) ||
- (cvals->rx_max_coalesced_frames == 0)) {
- for (i = 0; i < priv->num_rx_queues; i++)
- priv->rx_queue[i]->rxcoalescing = 0;
- } else {
- for (i = 0; i < priv->num_rx_queues; i++)
- priv->rx_queue[i]->rxcoalescing = 1;
- }
-
- if (NULL == priv->phydev)
- return -ENODEV;
-
- /* Check the bounds of the values */
- if (cvals->rx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
- pr_info("Coalescing is limited to %d microseconds\n",
- GFAR_MAX_COAL_USECS);
- return -EINVAL;
- }
-
- if (cvals->rx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
- pr_info("Coalescing is limited to %d frames\n",
- GFAR_MAX_COAL_FRAMES);
- return -EINVAL;
- }
-
- for (i = 0; i < priv->num_rx_queues; i++) {
- priv->rx_queue[i]->rxic = mk_ic_value(
- cvals->rx_max_coalesced_frames,
- gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs));
- }
-
- /* Set up tx coalescing */
- if ((cvals->tx_coalesce_usecs == 0) ||
- (cvals->tx_max_coalesced_frames == 0)) {
- for (i = 0; i < priv->num_tx_queues; i++)
- priv->tx_queue[i]->txcoalescing = 0;
- } else {
- for (i = 0; i < priv->num_tx_queues; i++)
- priv->tx_queue[i]->txcoalescing = 1;
- }
-
- /* Check the bounds of the values */
- if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
- pr_info("Coalescing is limited to %d microseconds\n",
- GFAR_MAX_COAL_USECS);
- return -EINVAL;
- }
-
- if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
- pr_info("Coalescing is limited to %d frames\n",
- GFAR_MAX_COAL_FRAMES);
- return -EINVAL;
- }
-
- for (i = 0; i < priv->num_tx_queues; i++) {
- priv->tx_queue[i]->txic = mk_ic_value(
- cvals->tx_max_coalesced_frames,
- gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
- }
-
- gfar_configure_coalescing(priv, 0xFF, 0xFF);
-
- return 0;
-}
-
-/* Fills in rvals with the current ring parameters. Currently,
- * rx, rx_mini, and rx_jumbo rings are the same size, as mini and
- * jumbo are ignored by the driver */
-static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
-{
- struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
-
- tx_queue = priv->tx_queue[0];
- rx_queue = priv->rx_queue[0];
-
- rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
- rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
- rvals->rx_jumbo_max_pending = GFAR_RX_MAX_RING_SIZE;
- rvals->tx_max_pending = GFAR_TX_MAX_RING_SIZE;
-
- /* Values changeable by the user. The valid values are
- * in the range 1 to the "*_max_pending" counterpart above.
- */
- rvals->rx_pending = rx_queue->rx_ring_size;
- rvals->rx_mini_pending = rx_queue->rx_ring_size;
- rvals->rx_jumbo_pending = rx_queue->rx_ring_size;
- rvals->tx_pending = tx_queue->tx_ring_size;
-}
-
-/* Change the current ring parameters, stopping the controller if
- * necessary so that we don't mess things up while we're in
- * motion. We wait for the ring to be clean before reallocating
- * the rings. */
-static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
-{
- struct gfar_private *priv = netdev_priv(dev);
- int err = 0, i = 0;
-
- if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
- return -EINVAL;
-
- if (!is_power_of_2(rvals->rx_pending)) {
- printk("%s: Ring sizes must be a power of 2\n",
- dev->name);
- return -EINVAL;
- }
-
- if (rvals->tx_pending > GFAR_TX_MAX_RING_SIZE)
- return -EINVAL;
-
- if (!is_power_of_2(rvals->tx_pending)) {
- printk("%s: Ring sizes must be a power of 2\n",
- dev->name);
- return -EINVAL;
- }
-
-
- if (dev->flags & IFF_UP) {
- unsigned long flags;
-
- /* Halt TX and RX, and process the frames which
- * have already been received */
- local_irq_save(flags);
- lock_tx_qs(priv);
- lock_rx_qs(priv);
-
- gfar_halt(dev);
-
- unlock_rx_qs(priv);
- unlock_tx_qs(priv);
- local_irq_restore(flags);
-
- for (i = 0; i < priv->num_rx_queues; i++)
- gfar_clean_rx_ring(priv->rx_queue[i],
- priv->rx_queue[i]->rx_ring_size);
-
- /* Now we take down the rings to rebuild them */
- stop_gfar(dev);
- }
-
- /* Change the size */
- for (i = 0; i < priv->num_rx_queues; i++) {
- priv->rx_queue[i]->rx_ring_size = rvals->rx_pending;
- priv->tx_queue[i]->tx_ring_size = rvals->tx_pending;
- priv->tx_queue[i]->num_txbdfree = priv->tx_queue[i]->tx_ring_size;
- }
-
- /* Rebuild the rings with the new size */
- if (dev->flags & IFF_UP) {
- err = startup_gfar(dev);
- netif_tx_wake_all_queues(dev);
- }
- return err;
-}
-
-int gfar_set_features(struct net_device *dev, u32 features)
-{
- struct gfar_private *priv = netdev_priv(dev);
- unsigned long flags;
- int err = 0, i = 0;
- u32 changed = dev->features ^ features;
-
- if (!(changed & NETIF_F_RXCSUM))
- return 0;
-
- if (dev->flags & IFF_UP) {
- /* Halt TX and RX, and process the frames which
- * have already been received */
- local_irq_save(flags);
- lock_tx_qs(priv);
- lock_rx_qs(priv);
-
- gfar_halt(dev);
-
- unlock_tx_qs(priv);
- unlock_rx_qs(priv);
- local_irq_restore(flags);
-
- for (i = 0; i < priv->num_rx_queues; i++)
- gfar_clean_rx_ring(priv->rx_queue[i],
- priv->rx_queue[i]->rx_ring_size);
-
- /* Now we take down the rings to rebuild them */
- stop_gfar(dev);
-
- dev->features = features;
-
- err = startup_gfar(dev);
- netif_tx_wake_all_queues(dev);
- }
- return err;
-}
-
-static uint32_t gfar_get_msglevel(struct net_device *dev)
-{
- struct gfar_private *priv = netdev_priv(dev);
- return priv->msg_enable;
-}
-
-static void gfar_set_msglevel(struct net_device *dev, uint32_t data)
-{
- struct gfar_private *priv = netdev_priv(dev);
- priv->msg_enable = data;
-}
-
-#ifdef CONFIG_PM
-static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
- struct gfar_private *priv = netdev_priv(dev);
-
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) {
- wol->supported = WAKE_MAGIC;
- wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0;
- } else {
- wol->supported = wol->wolopts = 0;
- }
-}
-
-static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
- struct gfar_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
- wol->wolopts != 0)
- return -EINVAL;
-
- if (wol->wolopts & ~WAKE_MAGIC)
- return -EINVAL;
-
- device_set_wakeup_enable(&dev->dev, wol->wolopts & WAKE_MAGIC);
-
- spin_lock_irqsave(&priv->bflock, flags);
- priv->wol_en = !!device_may_wakeup(&dev->dev);
- spin_unlock_irqrestore(&priv->bflock, flags);
-
- return 0;
-}
-#endif
-
-static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow)
-{
- u32 fcr = 0x0, fpr = FPR_FILER_MASK;
-
- if (ethflow & RXH_L2DA) {
- fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH |
- RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
- priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
- priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
- gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
- priv->cur_filer_idx = priv->cur_filer_idx - 1;
-
- fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH |
- RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
- priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
- priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
- gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
- priv->cur_filer_idx = priv->cur_filer_idx - 1;
- }
-
- if (ethflow & RXH_VLAN) {
- fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH |
- RQFCR_AND | RQFCR_HASHTBL_0;
- gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
- priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
- priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
- priv->cur_filer_idx = priv->cur_filer_idx - 1;
- }
-
- if (ethflow & RXH_IP_SRC) {
- fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
- RQFCR_AND | RQFCR_HASHTBL_0;
- priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
- priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
- gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
- priv->cur_filer_idx = priv->cur_filer_idx - 1;
- }
-
- if (ethflow & (RXH_IP_DST)) {
- fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
- RQFCR_AND | RQFCR_HASHTBL_0;
- priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
- priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
- gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
- priv->cur_filer_idx = priv->cur_filer_idx - 1;
- }
-
- if (ethflow & RXH_L3_PROTO) {
- fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH |
- RQFCR_AND | RQFCR_HASHTBL_0;
- priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
- priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
- gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
- priv->cur_filer_idx = priv->cur_filer_idx - 1;
- }
-
- if (ethflow & RXH_L4_B_0_1) {
- fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
- RQFCR_AND | RQFCR_HASHTBL_0;
- priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
- priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
- gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
- priv->cur_filer_idx = priv->cur_filer_idx - 1;
- }
-
- if (ethflow & RXH_L4_B_2_3) {
- fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
- RQFCR_AND | RQFCR_HASHTBL_0;
- priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
- priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
- gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
- priv->cur_filer_idx = priv->cur_filer_idx - 1;
- }
-}
-
-static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class)
-{
- unsigned int last_rule_idx = priv->cur_filer_idx;
- unsigned int cmp_rqfpr;
- unsigned int local_rqfpr[MAX_FILER_IDX + 1];
- unsigned int local_rqfcr[MAX_FILER_IDX + 1];
- int i = 0x0, k = 0x0;
- int j = MAX_FILER_IDX, l = 0x0;
-
- switch (class) {
- case TCP_V4_FLOW:
- cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP;
- break;
- case UDP_V4_FLOW:
- cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP;
- break;
- case TCP_V6_FLOW:
- cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP;
- break;
- case UDP_V6_FLOW:
- cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP;
- break;
- default:
- printk(KERN_ERR "Right now this class is not supported\n");
- return 0;
- }
-
- for (i = 0; i < MAX_FILER_IDX + 1; i++) {
- local_rqfpr[j] = priv->ftp_rqfpr[i];
- local_rqfcr[j] = priv->ftp_rqfcr[i];
- j--;
- if ((priv->ftp_rqfcr[i] == (RQFCR_PID_PARSE |
- RQFCR_CLE |RQFCR_AND)) &&
- (priv->ftp_rqfpr[i] == cmp_rqfpr))
- break;
- }
-
- if (i == MAX_FILER_IDX + 1) {
- printk(KERN_ERR "No parse rule found, ");
- printk(KERN_ERR "can't create hash rules\n");
- return 0;
- }
-
- /* If a match was found, then it begins the starting of a cluster rule
- * if it was already programmed, we need to overwrite these rules
- */
- for (l = i+1; l < MAX_FILER_IDX; l++) {
- if ((priv->ftp_rqfcr[l] & RQFCR_CLE) &&
- !(priv->ftp_rqfcr[l] & RQFCR_AND)) {
- priv->ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT |
- RQFCR_HASHTBL_0 | RQFCR_PID_MASK;
- priv->ftp_rqfpr[l] = FPR_FILER_MASK;
- gfar_write_filer(priv, l, priv->ftp_rqfcr[l],
- priv->ftp_rqfpr[l]);
- break;
- }
-
- if (!(priv->ftp_rqfcr[l] & RQFCR_CLE) &&
- (priv->ftp_rqfcr[l] & RQFCR_AND))
- continue;
- else {
- local_rqfpr[j] = priv->ftp_rqfpr[l];
- local_rqfcr[j] = priv->ftp_rqfcr[l];
- j--;
- }
- }
-
- priv->cur_filer_idx = l - 1;
- last_rule_idx = l;
-
- /* hash rules */
- ethflow_to_filer_rules(priv, ethflow);
-
- /* Write back the popped out rules again */
- for (k = j+1; k < MAX_FILER_IDX; k++) {
- priv->ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k];
- priv->ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k];
- gfar_write_filer(priv, priv->cur_filer_idx,
- local_rqfcr[k], local_rqfpr[k]);
- if (!priv->cur_filer_idx)
- break;
- priv->cur_filer_idx = priv->cur_filer_idx - 1;
- }
-
- return 1;
-}
-
-static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd)
-{
- /* write the filer rules here */
- if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type))
- return -EINVAL;
-
- return 0;
-}
-
-static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
-{
- struct gfar_private *priv = netdev_priv(dev);
- int ret = 0;
-
- switch(cmd->cmd) {
- case ETHTOOL_SRXFH:
- ret = gfar_set_hash_opts(priv, cmd);
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-const struct ethtool_ops gfar_ethtool_ops = {
- .get_settings = gfar_gsettings,
- .set_settings = gfar_ssettings,
- .get_drvinfo = gfar_gdrvinfo,
- .get_regs_len = gfar_reglen,
- .get_regs = gfar_get_regs,
- .get_link = ethtool_op_get_link,
- .get_coalesce = gfar_gcoalesce,
- .set_coalesce = gfar_scoalesce,
- .get_ringparam = gfar_gringparam,
- .set_ringparam = gfar_sringparam,
- .get_strings = gfar_gstrings,
- .get_sset_count = gfar_sset_count,
- .get_ethtool_stats = gfar_fill_stats,
- .get_msglevel = gfar_get_msglevel,
- .set_msglevel = gfar_set_msglevel,
-#ifdef CONFIG_PM
- .get_wol = gfar_get_wol,
- .set_wol = gfar_set_wol,
-#endif
- .set_rxnfc = gfar_set_nfc,
-};
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 0d283781bc5e..2a5a34d2d67b 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -36,7 +36,7 @@
#include <linux/tcp.h>
#include <linux/semaphore.h>
#include <linux/compat.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#define SIXPACK_VERSION "Revision: 0.3.0"
diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c
index 99cdce33df8b..a974727dd9a2 100644
--- a/drivers/net/hamradio/baycom_ser_fdx.c
+++ b/drivers/net/hamradio/baycom_ser_fdx.c
@@ -76,6 +76,7 @@
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/hdlcdrv.h>
#include <linux/baycom.h>
#include <linux/jiffies.h>
diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c
index d92fe6ca788f..e349d867449b 100644
--- a/drivers/net/hamradio/baycom_ser_hdx.c
+++ b/drivers/net/hamradio/baycom_ser_hdx.c
@@ -66,6 +66,7 @@
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/hdlcdrv.h>
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
index 52b14256e2c0..ce555d9ac02c 100644
--- a/drivers/net/hamradio/dmascc.c
+++ b/drivers/net/hamradio/dmascc.c
@@ -36,7 +36,7 @@
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <linux/workqueue.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/net/hippi/Kconfig b/drivers/net/hippi/Kconfig
new file mode 100644
index 000000000000..95eb34fdbba7
--- /dev/null
+++ b/drivers/net/hippi/Kconfig
@@ -0,0 +1,39 @@
+#
+# HIPPI network device configuration
+#
+
+config HIPPI
+ bool "HIPPI driver support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && INET && PCI
+ ---help---
+ HIgh Performance Parallel Interface (HIPPI) is a 800Mbit/sec and
+ 1600Mbit/sec dual-simplex switched or point-to-point network. HIPPI
+ can run over copper (25m) or fiber (300m on multi-mode or 10km on
+ single-mode). HIPPI networks are commonly used for clusters and to
+ connect to super computers. If you are connected to a HIPPI network
+ and have a HIPPI network card in your computer that you want to use
+ under Linux, say Y here (you must also remember to enable the driver
+ for your HIPPI card below). Most people will say N here.
+
+if HIPPI
+
+config ROADRUNNER
+ tristate "Essential RoadRunner HIPPI PCI adapter support (EXPERIMENTAL)"
+ depends on PCI
+ ---help---
+ Say Y here if this is your PCI HIPPI network card.
+
+ To compile this driver as a module, choose M here: the module
+ will be called rrunner. If unsure, say N.
+
+config ROADRUNNER_LARGE_RINGS
+ bool "Use large TX/RX rings (EXPERIMENTAL)"
+ depends on ROADRUNNER
+ ---help---
+ If you say Y here, the RoadRunner driver will preallocate up to 2 MB
+ of additional memory to allow for fastest operation, both for
+ transmitting and receiving. This memory cannot be used by any other
+ kernel code or by user space programs. Say Y here only if you have
+ the memory.
+
+endif # HIPPI
diff --git a/drivers/net/hippi/Makefile b/drivers/net/hippi/Makefile
new file mode 100644
index 000000000000..b95d629baee5
--- /dev/null
+++ b/drivers/net/hippi/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HIPPI network device drivers.
+#
+
+obj-$(CONFIG_ROADRUNNER) += rrunner.o
diff --git a/drivers/net/rrunner.c b/drivers/net/hippi/rrunner.c
index e68c941926f1..e68c941926f1 100644
--- a/drivers/net/rrunner.c
+++ b/drivers/net/hippi/rrunner.c
diff --git a/drivers/net/rrunner.h b/drivers/net/hippi/rrunner.h
index 28169043ae49..28169043ae49 100644
--- a/drivers/net/rrunner.h
+++ b/drivers/net/hippi/rrunner.h
diff --git a/drivers/net/ibm_newemac/Makefile b/drivers/net/ibm_newemac/Makefile
deleted file mode 100644
index 0b5c99512762..000000000000
--- a/drivers/net/ibm_newemac/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# Makefile for the PowerPC 4xx on-chip ethernet driver
-#
-
-obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac.o
-
-ibm_newemac-y := mal.o core.o phy.o
-ibm_newemac-$(CONFIG_IBM_NEW_EMAC_ZMII) += zmii.o
-ibm_newemac-$(CONFIG_IBM_NEW_EMAC_RGMII) += rgmii.o
-ibm_newemac-$(CONFIG_IBM_NEW_EMAC_TAH) += tah.o
-ibm_newemac-$(CONFIG_IBM_NEW_EMAC_DEBUG) += debug.o
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index 4fecaed67fc4..46b5f5fd686b 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -32,6 +32,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <net/pkt_sched.h>
#include <net/net_namespace.h>
@@ -40,8 +41,16 @@
struct ifb_private {
struct tasklet_struct ifb_tasklet;
int tasklet_pending;
+
+ struct u64_stats_sync rsync;
struct sk_buff_head rq;
+ u64 rx_packets;
+ u64 rx_bytes;
+
+ struct u64_stats_sync tsync;
struct sk_buff_head tq;
+ u64 tx_packets;
+ u64 tx_bytes;
};
static int numifbs = 2;
@@ -53,10 +62,8 @@ static int ifb_close(struct net_device *dev);
static void ri_tasklet(unsigned long dev)
{
-
struct net_device *_dev = (struct net_device *)dev;
struct ifb_private *dp = netdev_priv(_dev);
- struct net_device_stats *stats = &_dev->stats;
struct netdev_queue *txq;
struct sk_buff *skb;
@@ -76,15 +83,18 @@ static void ri_tasklet(unsigned long dev)
skb->tc_verd = 0;
skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
- stats->tx_packets++;
- stats->tx_bytes +=skb->len;
+
+ u64_stats_update_begin(&dp->tsync);
+ dp->tx_packets++;
+ dp->tx_bytes += skb->len;
+ u64_stats_update_end(&dp->tsync);
rcu_read_lock();
skb->dev = dev_get_by_index_rcu(&init_net, skb->skb_iif);
if (!skb->dev) {
rcu_read_unlock();
dev_kfree_skb(skb);
- stats->tx_dropped++;
+ _dev->stats.tx_dropped++;
if (skb_queue_len(&dp->tq) != 0)
goto resched;
break;
@@ -119,9 +129,37 @@ resched:
}
+static struct rtnl_link_stats64 *ifb_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct ifb_private *dp = netdev_priv(dev);
+ unsigned int start;
+
+ do {
+ start = u64_stats_fetch_begin_bh(&dp->rsync);
+ stats->rx_packets = dp->rx_packets;
+ stats->rx_bytes = dp->rx_bytes;
+ } while (u64_stats_fetch_retry_bh(&dp->rsync, start));
+
+ do {
+ start = u64_stats_fetch_begin_bh(&dp->tsync);
+
+ stats->tx_packets = dp->tx_packets;
+ stats->tx_bytes = dp->tx_bytes;
+
+ } while (u64_stats_fetch_retry_bh(&dp->tsync, start));
+
+ stats->rx_dropped = dev->stats.rx_dropped;
+ stats->tx_dropped = dev->stats.tx_dropped;
+
+ return stats;
+}
+
+
static const struct net_device_ops ifb_netdev_ops = {
.ndo_open = ifb_open,
.ndo_stop = ifb_close,
+ .ndo_get_stats64 = ifb_stats64,
.ndo_start_xmit = ifb_xmit,
.ndo_validate_addr = eth_validate_addr,
};
@@ -145,22 +183,23 @@ static void ifb_setup(struct net_device *dev)
dev->flags |= IFF_NOARP;
dev->flags &= ~IFF_MULTICAST;
- dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+ dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
random_ether_addr(dev->dev_addr);
}
static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ifb_private *dp = netdev_priv(dev);
- struct net_device_stats *stats = &dev->stats;
u32 from = G_TC_FROM(skb->tc_verd);
- stats->rx_packets++;
- stats->rx_bytes+=skb->len;
+ u64_stats_update_begin(&dp->rsync);
+ dp->rx_packets++;
+ dp->rx_bytes += skb->len;
+ u64_stats_update_end(&dp->rsync);
if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) {
dev_kfree_skb(skb);
- stats->rx_dropped++;
+ dev->stats.rx_dropped++;
return NETDEV_TX_OK;
}
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index 25bb2a015e18..d423d18b4ad6 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -183,7 +183,7 @@ config OLD_BELKIN_DONGLE
Say Y here if you want to build support for the Adaptec Airport 1000
and 2000 dongles. If you want to compile it as a module, choose
M here. Some information is contained in the comments
- at the top of <file:drivers/net/irda/old_belkin.c>.
+ at the top of <file:drivers/net/irda/old_belkin-sir.c>.
config ACT200L_DONGLE
tristate "ACTiSYS IR-200L dongle"
@@ -314,7 +314,7 @@ config TOSHIBA_FIR
config AU1000_FIR
tristate "Alchemy Au1000 SIR/FIR"
- depends on SOC_AU1000 && IRDA
+ depends on IRDA && MIPS_ALCHEMY
config SMC_IRCC_FIR
tristate "SMSC IrCC (EXPERIMENTAL)"
diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c
index d532dde5120f..963067d3bda2 100644
--- a/drivers/net/irda/ali-ircc.c
+++ b/drivers/net/irda/ali-ircc.c
@@ -31,6 +31,7 @@
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/rtnetlink.h>
#include <linux/serial_reg.h>
#include <linux/dma-mapping.h>
diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c
index 174cafad2c1a..b45b2cc42804 100644
--- a/drivers/net/irda/donauboe.c
+++ b/drivers/net/irda/donauboe.c
@@ -152,6 +152,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/rtnetlink.h>
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
index 7a963d4e6d06..b56636da6cc3 100644
--- a/drivers/net/irda/nsc-ircc.c
+++ b/drivers/net/irda/nsc-ircc.c
@@ -52,6 +52,7 @@
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/rtnetlink.h>
#include <linux/dma-mapping.h>
#include <linux/pnp.h>
diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c
index 001ed0a255f6..d0851dfa0378 100644
--- a/drivers/net/irda/pxaficp_ir.c
+++ b/drivers/net/irda/pxaficp_ir.c
@@ -12,6 +12,8 @@
* Infra-red driver (SIR/FIR) for the PXA2xx embedded microprocessor
*
*/
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
diff --git a/drivers/net/irda/sh_irda.c b/drivers/net/irda/sh_irda.c
index 4488bd581eca..d275e276e742 100644
--- a/drivers/net/irda/sh_irda.c
+++ b/drivers/net/irda/sh_irda.c
@@ -2,7 +2,7 @@
* SuperH IrDA Driver
*
* Copyright (C) 2010 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* Based on sh_sir.c
* Copyright (C) 2009 Renesas Solutions Corp.
@@ -22,8 +22,11 @@
* - DMA transfer support
* - FIFO mode support
*/
+#include <linux/io.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <net/irda/wrapper.h>
#include <net/irda/irda_device.h>
@@ -142,8 +145,8 @@ struct sh_irda_xir_func {
struct sh_irda_self {
void __iomem *membase;
- unsigned int irq;
- struct clk *clk;
+ unsigned int irq;
+ struct platform_device *pdev;
struct net_device *ndev;
@@ -262,7 +265,7 @@ static int sh_irda_set_baudrate(struct sh_irda_self *self, int baudrate)
return 0;
}
-static int xir_get_rcv_length(struct sh_irda_self *self)
+static int sh_irda_get_rcv_length(struct sh_irda_self *self)
{
return RFL_MASK & sh_irda_read(self, IRRFLR);
}
@@ -272,47 +275,47 @@ static int xir_get_rcv_length(struct sh_irda_self *self)
* NONE MODE
*
*=====================================*/
-static int xir_fre(struct sh_irda_self *self)
+static int sh_irda_xir_fre(struct sh_irda_self *self)
{
struct device *dev = &self->ndev->dev;
dev_err(dev, "none mode: frame recv\n");
return 0;
}
-static int xir_trov(struct sh_irda_self *self)
+static int sh_irda_xir_trov(struct sh_irda_self *self)
{
struct device *dev = &self->ndev->dev;
dev_err(dev, "none mode: buffer ram over\n");
return 0;
}
-static int xir_9(struct sh_irda_self *self)
+static int sh_irda_xir_9(struct sh_irda_self *self)
{
struct device *dev = &self->ndev->dev;
dev_err(dev, "none mode: time over\n");
return 0;
}
-static int xir_8(struct sh_irda_self *self)
+static int sh_irda_xir_8(struct sh_irda_self *self)
{
struct device *dev = &self->ndev->dev;
dev_err(dev, "none mode: framing error\n");
return 0;
}
-static int xir_fte(struct sh_irda_self *self)
+static int sh_irda_xir_fte(struct sh_irda_self *self)
{
struct device *dev = &self->ndev->dev;
dev_err(dev, "none mode: frame transmit end\n");
return 0;
}
-static struct sh_irda_xir_func xir_func = {
- .xir_fre = xir_fre,
- .xir_trov = xir_trov,
- .xir_9 = xir_9,
- .xir_8 = xir_8,
- .xir_fte = xir_fte,
+static struct sh_irda_xir_func sh_irda_xir_func = {
+ .xir_fre = sh_irda_xir_fre,
+ .xir_trov = sh_irda_xir_trov,
+ .xir_9 = sh_irda_xir_9,
+ .xir_8 = sh_irda_xir_8,
+ .xir_fte = sh_irda_xir_fte,
};
/*=====================================
@@ -321,12 +324,12 @@ static struct sh_irda_xir_func xir_func = {
*
* MIR/FIR are not supported now
*=====================================*/
-static struct sh_irda_xir_func mfir_func = {
- .xir_fre = xir_fre,
- .xir_trov = xir_trov,
- .xir_9 = xir_9,
- .xir_8 = xir_8,
- .xir_fte = xir_fte,
+static struct sh_irda_xir_func sh_irda_mfir_func = {
+ .xir_fre = sh_irda_xir_fre,
+ .xir_trov = sh_irda_xir_trov,
+ .xir_9 = sh_irda_xir_9,
+ .xir_8 = sh_irda_xir_8,
+ .xir_fte = sh_irda_xir_fte,
};
/*=====================================
@@ -334,12 +337,12 @@ static struct sh_irda_xir_func mfir_func = {
* SIR MODE
*
*=====================================*/
-static int sir_fre(struct sh_irda_self *self)
+static int sh_irda_sir_fre(struct sh_irda_self *self)
{
struct device *dev = &self->ndev->dev;
u16 data16;
u8 *data = (u8 *)&data16;
- int len = xir_get_rcv_length(self);
+ int len = sh_irda_get_rcv_length(self);
int i, j;
if (len > IRDARAM_LEN)
@@ -362,7 +365,7 @@ static int sir_fre(struct sh_irda_self *self)
return 0;
}
-static int sir_trov(struct sh_irda_self *self)
+static int sh_irda_sir_trov(struct sh_irda_self *self)
{
struct device *dev = &self->ndev->dev;
@@ -371,7 +374,7 @@ static int sir_trov(struct sh_irda_self *self)
return 0;
}
-static int sir_tot(struct sh_irda_self *self)
+static int sh_irda_sir_tot(struct sh_irda_self *self)
{
struct device *dev = &self->ndev->dev;
@@ -381,7 +384,7 @@ static int sir_tot(struct sh_irda_self *self)
return 0;
}
-static int sir_fer(struct sh_irda_self *self)
+static int sh_irda_sir_fer(struct sh_irda_self *self)
{
struct device *dev = &self->ndev->dev;
@@ -390,7 +393,7 @@ static int sir_fer(struct sh_irda_self *self)
return 0;
}
-static int sir_fte(struct sh_irda_self *self)
+static int sh_irda_sir_fte(struct sh_irda_self *self)
{
struct device *dev = &self->ndev->dev;
@@ -400,12 +403,12 @@ static int sir_fte(struct sh_irda_self *self)
return 0;
}
-static struct sh_irda_xir_func sir_func = {
- .xir_fre = sir_fre,
- .xir_trov = sir_trov,
- .xir_9 = sir_tot,
- .xir_8 = sir_fer,
- .xir_fte = sir_fte,
+static struct sh_irda_xir_func sh_irda_sir_func = {
+ .xir_fre = sh_irda_sir_fre,
+ .xir_trov = sh_irda_sir_trov,
+ .xir_9 = sh_irda_sir_tot,
+ .xir_8 = sh_irda_sir_fer,
+ .xir_fte = sh_irda_sir_fte,
};
static void sh_irda_set_mode(struct sh_irda_self *self, enum sh_irda_mode mode)
@@ -419,22 +422,22 @@ static void sh_irda_set_mode(struct sh_irda_self *self, enum sh_irda_mode mode)
case SH_IRDA_SIR:
name = "SIR";
data = TMD_SIR;
- func = &sir_func;
+ func = &sh_irda_sir_func;
break;
case SH_IRDA_MIR:
name = "MIR";
data = TMD_MIR;
- func = &mfir_func;
+ func = &sh_irda_mfir_func;
break;
case SH_IRDA_FIR:
name = "FIR";
data = TMD_FIR;
- func = &mfir_func;
+ func = &sh_irda_mfir_func;
break;
default:
- name = "NONE";
- data = 0;
- func = &xir_func;
+ name = "NONE";
+ data = 0;
+ func = &sh_irda_xir_func;
break;
}
@@ -692,7 +695,7 @@ static int sh_irda_open(struct net_device *ndev)
struct sh_irda_self *self = netdev_priv(ndev);
int err;
- clk_enable(self->clk);
+ pm_runtime_get_sync(&self->pdev->dev);
err = sh_irda_crc_init(self);
if (err)
goto open_err;
@@ -716,7 +719,7 @@ static int sh_irda_open(struct net_device *ndev)
return 0;
open_err:
- clk_disable(self->clk);
+ pm_runtime_put_sync(&self->pdev->dev);
return err;
}
@@ -732,6 +735,7 @@ static int sh_irda_stop(struct net_device *ndev)
}
netif_stop_queue(ndev);
+ pm_runtime_put_sync(&self->pdev->dev);
dev_info(&ndev->dev, "stoped\n");
@@ -784,11 +788,8 @@ static int __devinit sh_irda_probe(struct platform_device *pdev)
if (err)
goto err_mem_2;
- self->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(self->clk)) {
- dev_err(&pdev->dev, "cannot get irda clock\n");
- goto err_mem_3;
- }
+ self->pdev = pdev;
+ pm_runtime_enable(&pdev->dev);
irda_init_max_qos_capabilies(&self->qos);
@@ -818,8 +819,7 @@ static int __devinit sh_irda_probe(struct platform_device *pdev)
goto exit;
err_mem_4:
- clk_put(self->clk);
-err_mem_3:
+ pm_runtime_disable(&pdev->dev);
sh_irda_remove_iobuf(self);
err_mem_2:
iounmap(self->membase);
@@ -838,7 +838,7 @@ static int __devexit sh_irda_remove(struct platform_device *pdev)
return 0;
unregister_netdev(ndev);
- clk_put(self->clk);
+ pm_runtime_disable(&pdev->dev);
sh_irda_remove_iobuf(self);
iounmap(self->membase);
free_netdev(ndev);
@@ -847,11 +847,29 @@ static int __devexit sh_irda_remove(struct platform_device *pdev)
return 0;
}
+static int sh_irda_runtime_nop(struct device *dev)
+{
+ /* Runtime PM callback shared between ->runtime_suspend()
+ * and ->runtime_resume(). Simply returns success.
+ *
+ * This driver re-initializes all registers after
+ * pm_runtime_get_sync() anyway so there is no need
+ * to save and restore registers here.
+ */
+ return 0;
+}
+
+static const struct dev_pm_ops sh_irda_pm_ops = {
+ .runtime_suspend = sh_irda_runtime_nop,
+ .runtime_resume = sh_irda_runtime_nop,
+};
+
static struct platform_driver sh_irda_driver = {
- .probe = sh_irda_probe,
- .remove = __devexit_p(sh_irda_remove),
- .driver = {
- .name = DRIVER_NAME,
+ .probe = sh_irda_probe,
+ .remove = __devexit_p(sh_irda_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = &sh_irda_pm_ops,
},
};
@@ -868,6 +886,6 @@ static void __exit sh_irda_exit(void)
module_init(sh_irda_init);
module_exit(sh_irda_exit);
-MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
MODULE_DESCRIPTION("SuperH IrDA driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/irda/sh_sir.c b/drivers/net/irda/sh_sir.c
index 52a7c86af663..ed7d7d62bf68 100644
--- a/drivers/net/irda/sh_sir.c
+++ b/drivers/net/irda/sh_sir.c
@@ -12,6 +12,8 @@
* published by the Free Software Foundation.
*/
+#include <linux/io.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -511,7 +513,7 @@ static void sh_sir_tx(struct sh_sir_self *self, int phase)
static int sh_sir_read_data(struct sh_sir_self *self)
{
- u16 val;
+ u16 val = 0;
int timeout = 1024;
while (timeout--) {
diff --git a/drivers/net/irda/sir_dev.c b/drivers/net/irda/sir_dev.c
index efe05bb34dd8..5039f08f5a5b 100644
--- a/drivers/net/irda/sir_dev.c
+++ b/drivers/net/irda/sir_dev.c
@@ -11,6 +11,7 @@
*
********************************************************************/
+#include <linux/hardirq.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c
index 8800e1fe4129..8b1c3484d271 100644
--- a/drivers/net/irda/smsc-ircc2.c
+++ b/drivers/net/irda/smsc-ircc2.c
@@ -49,6 +49,7 @@
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/rtnetlink.h>
#include <linux/serial_reg.h>
#include <linux/dma-mapping.h>
@@ -515,7 +516,7 @@ static const struct net_device_ops smsc_ircc_netdev_ops = {
* Try to open driver instance
*
*/
-static int __init smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq)
+static int __devinit smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq)
{
struct smsc_ircc_cb *self;
struct net_device *dev;
@@ -2404,8 +2405,6 @@ static int __init smsc_superio_lpc(unsigned short cfg_base)
* addresses making a subsystem device table necessary.
*/
#ifdef CONFIG_PCI
-#define PCIID_VENDOR_INTEL 0x8086
-#define PCIID_VENDOR_ALI 0x10b9
static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __initdata = {
/*
* Subsystems needing entries:
@@ -2415,7 +2414,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
*/
{
/* Guessed entry */
- .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
.device = 0x24cc,
.subvendor = 0x103c,
.subdevice = 0x08bc,
@@ -2428,7 +2427,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
.name = "HP nx5000 family",
},
{
- .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
.device = 0x24cc,
.subvendor = 0x103c,
.subdevice = 0x088c,
@@ -2442,7 +2441,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
.name = "HP nc8000 family",
},
{
- .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
.device = 0x24cc,
.subvendor = 0x103c,
.subdevice = 0x0890,
@@ -2455,7 +2454,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
.name = "HP nc6000 family",
},
{
- .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
.device = 0x24cc,
.subvendor = 0x0e11,
.subdevice = 0x0860,
@@ -2470,7 +2469,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
},
{
/* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */
- .vendor = PCIID_VENDOR_INTEL,
+ .vendor = PCI_VENDOR_ID_INTEL,
.device = 0x24c0,
.subvendor = 0x1179,
.subdevice = 0xffff, /* 0xffff is "any" */
@@ -2483,7 +2482,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
.name = "Toshiba laptop with Intel 82801DB/DBL LPC bridge",
},
{
- .vendor = PCIID_VENDOR_INTEL, /* Intel 82801CAM ISA bridge */
+ .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801CAM ISA bridge */
.device = 0x248c,
.subvendor = 0x1179,
.subdevice = 0xffff, /* 0xffff is "any" */
@@ -2497,7 +2496,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
},
{
/* 82801DBM (ICH4-M) LPC Interface Bridge */
- .vendor = PCIID_VENDOR_INTEL,
+ .vendor = PCI_VENDOR_ID_INTEL,
.device = 0x24cc,
.subvendor = 0x1179,
.subdevice = 0xffff, /* 0xffff is "any" */
@@ -2511,7 +2510,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
},
{
/* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */
- .vendor = PCIID_VENDOR_ALI,
+ .vendor = PCI_VENDOR_ID_AL,
.device = 0x1533,
.subvendor = 0x1179,
.subdevice = 0xffff, /* 0xffff is "any" */
diff --git a/drivers/net/irda/toim3232-sir.c b/drivers/net/irda/toim3232-sir.c
index 99e1ec02a011..19ad4606b799 100644
--- a/drivers/net/irda/toim3232-sir.c
+++ b/drivers/net/irda/toim3232-sir.c
@@ -78,7 +78,7 @@
* Target hardware: IRWave IR320ST-2
*
* The IRWave IR320ST-2 is a simple dongle based on the Vishay/Temic
- * TOIM3232 SIR Endec and the Vishay/Temic TFDS4500 SIR IRDA transciever.
+ * TOIM3232 SIR Endec and the Vishay/Temic TFDS4500 SIR IRDA transceiver.
* It uses a hex inverter and some discrete components to buffer and
* line convert the RS232 down to 5V.
*
diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c
index f504b262ba36..6d6479049aa1 100644
--- a/drivers/net/irda/via-ircc.c
+++ b/drivers/net/irda/via-ircc.c
@@ -46,6 +46,7 @@ F02 Oct/28/02: Add SB device ID for 3147 and 3177.
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/rtnetlink.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
diff --git a/drivers/net/irda/via-ircc.h b/drivers/net/irda/via-ircc.h
index c6f58482b769..f903a6a2dcb7 100644
--- a/drivers/net/irda/via-ircc.h
+++ b/drivers/net/irda/via-ircc.h
@@ -210,7 +210,7 @@ static void DisableDmaChannel(unsigned int channel)
break;
default:
break;
- }; //Switch
+ }
}
static unsigned char ReadLPCReg(int iRegNum)
diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c
index c3d07382b7fa..9021d0131727 100644
--- a/drivers/net/irda/vlsi_ir.c
+++ b/drivers/net/irda/vlsi_ir.c
@@ -36,6 +36,7 @@ MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c
index 1f9c3f08d1a3..c4366601b067 100644
--- a/drivers/net/irda/w83977af_ir.c
+++ b/drivers/net/irda/w83977af_ir.c
@@ -47,6 +47,7 @@
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/rtnetlink.h>
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index d6aeaa5f25ea..74134970b709 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -169,6 +169,9 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
port = macvlan_port_get_rcu(skb->dev);
if (is_multicast_ether_addr(eth->h_dest)) {
+ skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN);
+ if (!skb)
+ return RX_HANDLER_CONSUMED;
src = macvlan_hash_lookup(port, eth->h_source);
if (!src)
/* frame comes from an external address */
@@ -189,6 +192,13 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
*/
macvlan_broadcast(skb, port, src->dev,
MACVLAN_MODE_VEPA);
+ else {
+ /* forward to original port. */
+ vlan = src;
+ ret = macvlan_broadcast_one(skb, vlan, eth, 0);
+ goto out;
+ }
+
return RX_HANDLER_PASS;
}
@@ -239,7 +249,7 @@ static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
dest = macvlan_hash_lookup(port, eth->h_dest);
if (dest && dest->mode == MACVLAN_MODE_BRIDGE) {
/* send to lowerdev first for its network taps */
- vlan->forward(vlan->lowerdev, skb);
+ dev_forward_skb(vlan->lowerdev, skb);
return NET_XMIT_SUCCESS;
}
@@ -414,7 +424,8 @@ static struct lock_class_key macvlan_netdev_addr_lock_key;
#define MACVLAN_FEATURES \
(NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_GSO_ROBUST | \
- NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM)
+ NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \
+ NETIF_F_HW_VLAN_FILTER)
#define MACVLAN_STATE_MASK \
((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT))
@@ -509,6 +520,28 @@ static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev,
return stats;
}
+static void macvlan_vlan_rx_add_vid(struct net_device *dev,
+ unsigned short vid)
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+ struct net_device *lowerdev = vlan->lowerdev;
+ const struct net_device_ops *ops = lowerdev->netdev_ops;
+
+ if (ops->ndo_vlan_rx_add_vid)
+ ops->ndo_vlan_rx_add_vid(lowerdev, vid);
+}
+
+static void macvlan_vlan_rx_kill_vid(struct net_device *dev,
+ unsigned short vid)
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+ struct net_device *lowerdev = vlan->lowerdev;
+ const struct net_device_ops *ops = lowerdev->netdev_ops;
+
+ if (ops->ndo_vlan_rx_kill_vid)
+ ops->ndo_vlan_rx_kill_vid(lowerdev, vid);
+}
+
static void macvlan_ethtool_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *drvinfo)
{
@@ -520,7 +553,8 @@ static int macvlan_ethtool_get_settings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
const struct macvlan_dev *vlan = netdev_priv(dev);
- return dev_ethtool_get_settings(vlan->lowerdev, cmd);
+
+ return __ethtool_get_settings(vlan->lowerdev, cmd);
}
static const struct ethtool_ops macvlan_ethtool_ops = {
@@ -538,16 +572,18 @@ static const struct net_device_ops macvlan_netdev_ops = {
.ndo_change_mtu = macvlan_change_mtu,
.ndo_change_rx_flags = macvlan_change_rx_flags,
.ndo_set_mac_address = macvlan_set_mac_address,
- .ndo_set_multicast_list = macvlan_set_multicast_list,
+ .ndo_set_rx_mode = macvlan_set_multicast_list,
.ndo_get_stats64 = macvlan_dev_get_stats64,
.ndo_validate_addr = eth_validate_addr,
+ .ndo_vlan_rx_add_vid = macvlan_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = macvlan_vlan_rx_kill_vid,
};
void macvlan_common_setup(struct net_device *dev)
{
ether_setup(dev);
- dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+ dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
dev->netdev_ops = &macvlan_netdev_ops;
dev->destructor = free_netdev;
dev->header_ops = &macvlan_hard_header_ops,
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 6696e56e6320..1b7082d08f33 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -51,15 +51,14 @@ static struct proto macvtap_proto = {
};
/*
- * Minor number matches netdev->ifindex, so need a potentially
- * large value. This also makes it possible to split the
- * tap functionality out again in the future by offering it
- * from other drivers besides macvtap. As long as every device
- * only has one tap, the interface numbers assure that the
- * device nodes are unique.
+ * Variables for dealing with macvtaps device numbers.
*/
static dev_t macvtap_major;
-#define MACVTAP_NUM_DEVS 65536
+#define MACVTAP_NUM_DEVS (1U << MINORBITS)
+static DEFINE_MUTEX(minor_lock);
+static DEFINE_IDR(minor_idr);
+
+#define GOODCOPY_LEN 128
static struct class *macvtap_class;
static struct cdev macvtap_cdev;
@@ -230,6 +229,8 @@ static void macvtap_del_queues(struct net_device *dev)
}
}
BUG_ON(vlan->numvtaps != 0);
+ /* guarantee that any future macvtap_set_queue will fail */
+ vlan->numvtaps = MAX_MACVTAP_QUEUES;
spin_unlock(&macvtap_lock);
synchronize_rcu();
@@ -272,39 +273,73 @@ static int macvtap_receive(struct sk_buff *skb)
return macvtap_forward(skb->dev, skb);
}
-static int macvtap_newlink(struct net *src_net,
- struct net_device *dev,
- struct nlattr *tb[],
- struct nlattr *data[])
+static int macvtap_get_minor(struct macvlan_dev *vlan)
{
- struct device *classdev;
- dev_t devt;
- int err;
+ int retval = -ENOMEM;
+ int id;
+
+ mutex_lock(&minor_lock);
+ if (idr_pre_get(&minor_idr, GFP_KERNEL) == 0)
+ goto exit;
+
+ retval = idr_get_new_above(&minor_idr, vlan, 1, &id);
+ if (retval < 0) {
+ if (retval == -EAGAIN)
+ retval = -ENOMEM;
+ goto exit;
+ }
+ if (id < MACVTAP_NUM_DEVS) {
+ vlan->minor = id;
+ } else {
+ printk(KERN_ERR "too many macvtap devices\n");
+ retval = -EINVAL;
+ idr_remove(&minor_idr, id);
+ }
+exit:
+ mutex_unlock(&minor_lock);
+ return retval;
+}
- err = macvlan_common_newlink(src_net, dev, tb, data,
- macvtap_receive, macvtap_forward);
- if (err)
- goto out;
+static void macvtap_free_minor(struct macvlan_dev *vlan)
+{
+ mutex_lock(&minor_lock);
+ if (vlan->minor) {
+ idr_remove(&minor_idr, vlan->minor);
+ vlan->minor = 0;
+ }
+ mutex_unlock(&minor_lock);
+}
- devt = MKDEV(MAJOR(macvtap_major), dev->ifindex);
+static struct net_device *dev_get_by_macvtap_minor(int minor)
+{
+ struct net_device *dev = NULL;
+ struct macvlan_dev *vlan;
- classdev = device_create(macvtap_class, &dev->dev, devt,
- dev, "tap%d", dev->ifindex);
- if (IS_ERR(classdev)) {
- err = PTR_ERR(classdev);
- macvtap_del_queues(dev);
+ mutex_lock(&minor_lock);
+ vlan = idr_find(&minor_idr, minor);
+ if (vlan) {
+ dev = vlan->dev;
+ dev_hold(dev);
}
+ mutex_unlock(&minor_lock);
+ return dev;
+}
-out:
- return err;
+static int macvtap_newlink(struct net *src_net,
+ struct net_device *dev,
+ struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ /* Don't put anything that may fail after macvlan_common_newlink
+ * because we can't undo what it does.
+ */
+ return macvlan_common_newlink(src_net, dev, tb, data,
+ macvtap_receive, macvtap_forward);
}
static void macvtap_dellink(struct net_device *dev,
struct list_head *head)
{
- device_destroy(macvtap_class,
- MKDEV(MAJOR(macvtap_major), dev->ifindex));
-
macvtap_del_queues(dev);
macvlan_dellink(dev, head);
}
@@ -336,10 +371,15 @@ static void macvtap_sock_write_space(struct sock *sk)
wake_up_interruptible_poll(wqueue, POLLOUT | POLLWRNORM | POLLWRBAND);
}
+static void macvtap_sock_destruct(struct sock *sk)
+{
+ skb_queue_purge(&sk->sk_receive_queue);
+}
+
static int macvtap_open(struct inode *inode, struct file *file)
{
struct net *net = current->nsproxy->net_ns;
- struct net_device *dev = dev_get_by_index(net, iminor(inode));
+ struct net_device *dev = dev_get_by_macvtap_minor(iminor(inode));
struct macvtap_queue *q;
int err;
@@ -347,11 +387,6 @@ static int macvtap_open(struct inode *inode, struct file *file)
if (!dev)
goto out;
- /* check if this is a macvtap device */
- err = -EINVAL;
- if (dev->rtnl_link_ops != &macvtap_link_ops)
- goto out;
-
err = -ENOMEM;
q = (struct macvtap_queue *)sk_alloc(net, AF_UNSPEC, GFP_KERNEL,
&macvtap_proto);
@@ -366,9 +401,20 @@ static int macvtap_open(struct inode *inode, struct file *file)
q->sock.ops = &macvtap_socket_ops;
sock_init_data(&q->sock, &q->sk);
q->sk.sk_write_space = macvtap_sock_write_space;
+ q->sk.sk_destruct = macvtap_sock_destruct;
q->flags = IFF_VNET_HDR | IFF_NO_PI | IFF_TAP;
q->vnet_hdr_sz = sizeof(struct virtio_net_hdr);
+ /*
+ * so far only KVM virtio_net uses macvtap, enable zero copy between
+ * guest kernel and host kernel when lower device supports zerocopy
+ *
+ * The macvlan supports zerocopy iff the lower device supports zero
+ * copy so we don't have to look at the lower device directly.
+ */
+ if ((dev->features & NETIF_F_HIGHDMA) && (dev->features & NETIF_F_SG))
+ sock_set_flag(&q->sk, SOCK_ZEROCOPY);
+
err = macvtap_set_queue(dev, file, q);
if (err)
sock_put(&q->sk);
@@ -433,6 +479,78 @@ static inline struct sk_buff *macvtap_alloc_skb(struct sock *sk, size_t prepad,
return skb;
}
+/* set skb frags from iovec, this can move to core network code for reuse */
+static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from,
+ int offset, size_t count)
+{
+ int len = iov_length(from, count) - offset;
+ int copy = skb_headlen(skb);
+ int size, offset1 = 0;
+ int i = 0;
+
+ /* Skip over from offset */
+ while (count && (offset >= from->iov_len)) {
+ offset -= from->iov_len;
+ ++from;
+ --count;
+ }
+
+ /* copy up to skb headlen */
+ while (count && (copy > 0)) {
+ size = min_t(unsigned int, copy, from->iov_len - offset);
+ if (copy_from_user(skb->data + offset1, from->iov_base + offset,
+ size))
+ return -EFAULT;
+ if (copy > size) {
+ ++from;
+ --count;
+ }
+ copy -= size;
+ offset1 += size;
+ offset = 0;
+ }
+
+ if (len == offset1)
+ return 0;
+
+ while (count--) {
+ struct page *page[MAX_SKB_FRAGS];
+ int num_pages;
+ unsigned long base;
+
+ len = from->iov_len - offset1;
+ if (!len) {
+ offset1 = 0;
+ ++from;
+ continue;
+ }
+ base = (unsigned long)from->iov_base + offset1;
+ size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
+ num_pages = get_user_pages_fast(base, size, 0, &page[i]);
+ if ((num_pages != size) ||
+ (num_pages > MAX_SKB_FRAGS - skb_shinfo(skb)->nr_frags))
+ /* put_page is in skb free */
+ return -EFAULT;
+ skb->data_len += len;
+ skb->len += len;
+ skb->truesize += len;
+ atomic_add(len, &skb->sk->sk_wmem_alloc);
+ while (len) {
+ int off = base & ~PAGE_MASK;
+ int size = min_t(int, len, PAGE_SIZE - off);
+ __skb_fill_page_desc(skb, i, page[i], off, size);
+ skb_shinfo(skb)->nr_frags++;
+ /* increase sk_wmem_alloc */
+ base += size;
+ len -= size;
+ i++;
+ }
+ offset1 = 0;
+ ++from;
+ }
+ return 0;
+}
+
/*
* macvtap_skb_from_vnet_hdr and macvtap_skb_to_vnet_hdr should
* be shared with the tun/tap driver.
@@ -508,6 +626,8 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
vnet_hdr->csum_start = skb_checksum_start_offset(skb);
vnet_hdr->csum_offset = skb->csum_offset;
+ } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+ vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
} /* else everything is zero */
return 0;
@@ -515,16 +635,18 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
/* Get packet from user space buffer */
-static ssize_t macvtap_get_user(struct macvtap_queue *q,
- const struct iovec *iv, size_t count,
- int noblock)
+static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
+ const struct iovec *iv, unsigned long total_len,
+ size_t count, int noblock)
{
struct sk_buff *skb;
struct macvlan_dev *vlan;
- size_t len = count;
+ unsigned long len = total_len;
int err;
struct virtio_net_hdr vnet_hdr = { 0 };
int vnet_hdr_len = 0;
+ int copylen;
+ bool zerocopy = false;
if (q->flags & IFF_VNET_HDR) {
vnet_hdr_len = q->vnet_hdr_sz;
@@ -552,12 +674,31 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q,
if (unlikely(len < ETH_HLEN))
goto err;
- skb = macvtap_alloc_skb(&q->sk, NET_IP_ALIGN, len, vnet_hdr.hdr_len,
- noblock, &err);
+ if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY))
+ zerocopy = true;
+
+ if (zerocopy) {
+ /* There are 256 bytes to be copied in skb, so there is enough
+ * room for skb expand head in case it is used.
+ * The rest buffer is mapped from userspace.
+ */
+ copylen = vnet_hdr.hdr_len;
+ if (!copylen)
+ copylen = GOODCOPY_LEN;
+ } else
+ copylen = len;
+
+ skb = macvtap_alloc_skb(&q->sk, NET_IP_ALIGN, copylen,
+ vnet_hdr.hdr_len, noblock, &err);
if (!skb)
goto err;
- err = skb_copy_datagram_from_iovec(skb, 0, iv, vnet_hdr_len, len);
+ if (zerocopy) {
+ err = zerocopy_sg_from_iovec(skb, iv, vnet_hdr_len, count);
+ skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
+ } else
+ err = skb_copy_datagram_from_iovec(skb, 0, iv, vnet_hdr_len,
+ len);
if (err)
goto err_kfree;
@@ -573,13 +714,16 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q,
rcu_read_lock_bh();
vlan = rcu_dereference_bh(q->vlan);
+ /* copy skb_ubuf_info for callback when skb has no error */
+ if (zerocopy)
+ skb_shinfo(skb)->destructor_arg = m->msg_control;
if (vlan)
macvlan_start_xmit(skb, vlan->dev);
else
kfree_skb(skb);
rcu_read_unlock_bh();
- return count;
+ return total_len;
err_kfree:
kfree_skb(skb);
@@ -601,8 +745,8 @@ static ssize_t macvtap_aio_write(struct kiocb *iocb, const struct iovec *iv,
ssize_t result = -ENOLINK;
struct macvtap_queue *q = file->private_data;
- result = macvtap_get_user(q, iv, iov_length(iv, count),
- file->f_flags & O_NONBLOCK);
+ result = macvtap_get_user(q, NULL, iv, iov_length(iv, count), count,
+ file->f_flags & O_NONBLOCK);
return result;
}
@@ -815,7 +959,7 @@ static int macvtap_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len)
{
struct macvtap_queue *q = container_of(sock, struct macvtap_queue, sock);
- return macvtap_get_user(q, m->msg_iov, total_len,
+ return macvtap_get_user(q, m, m->msg_iov, total_len, m->msg_iovlen,
m->msg_flags & MSG_DONTWAIT);
}
@@ -858,6 +1002,52 @@ struct socket *macvtap_get_socket(struct file *file)
}
EXPORT_SYMBOL_GPL(macvtap_get_socket);
+static int macvtap_device_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = ptr;
+ struct macvlan_dev *vlan;
+ struct device *classdev;
+ dev_t devt;
+ int err;
+
+ if (dev->rtnl_link_ops != &macvtap_link_ops)
+ return NOTIFY_DONE;
+
+ vlan = netdev_priv(dev);
+
+ switch (event) {
+ case NETDEV_REGISTER:
+ /* Create the device node here after the network device has
+ * been registered but before register_netdevice has
+ * finished running.
+ */
+ err = macvtap_get_minor(vlan);
+ if (err)
+ return notifier_from_errno(err);
+
+ devt = MKDEV(MAJOR(macvtap_major), vlan->minor);
+ classdev = device_create(macvtap_class, &dev->dev, devt,
+ dev, "tap%d", dev->ifindex);
+ if (IS_ERR(classdev)) {
+ macvtap_free_minor(vlan);
+ return notifier_from_errno(PTR_ERR(classdev));
+ }
+ break;
+ case NETDEV_UNREGISTER:
+ devt = MKDEV(MAJOR(macvtap_major), vlan->minor);
+ device_destroy(macvtap_class, devt);
+ macvtap_free_minor(vlan);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block macvtap_notifier_block __read_mostly = {
+ .notifier_call = macvtap_device_event,
+};
+
static int macvtap_init(void)
{
int err;
@@ -878,12 +1068,18 @@ static int macvtap_init(void)
goto out3;
}
- err = macvlan_link_register(&macvtap_link_ops);
+ err = register_netdevice_notifier(&macvtap_notifier_block);
if (err)
goto out4;
+ err = macvlan_link_register(&macvtap_link_ops);
+ if (err)
+ goto out5;
+
return 0;
+out5:
+ unregister_netdevice_notifier(&macvtap_notifier_block);
out4:
class_unregister(macvtap_class);
out3:
@@ -898,6 +1094,7 @@ module_init(macvtap_init);
static void macvtap_exit(void)
{
rtnl_link_unregister(&macvtap_link_ops);
+ unregister_netdevice_notifier(&macvtap_notifier_block);
class_unregister(macvtap_class);
cdev_del(&macvtap_cdev);
unregister_chrdev_region(macvtap_major, MACVTAP_NUM_DEVS);
diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c
deleted file mode 100644
index 53aeea4b536e..000000000000
--- a/drivers/net/myri_sbus.c
+++ /dev/null
@@ -1,1187 +0,0 @@
-/* myri_sbus.c: MyriCOM MyriNET SBUS card driver.
- *
- * Copyright (C) 1996, 1999, 2006, 2008 David S. Miller (davem@davemloft.net)
- */
-
-static char version[] =
- "myri_sbus.c:v2.0 June 23, 2006 David S. Miller (davem@davemloft.net)\n";
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/bitops.h>
-#include <linux/dma-mapping.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/firmware.h>
-#include <linux/gfp.h>
-
-#include <net/dst.h>
-#include <net/arp.h>
-#include <net/sock.h>
-#include <net/ipv6.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/byteorder.h>
-#include <asm/idprom.h>
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-#include <asm/auxio.h>
-#include <asm/pgtable.h>
-#include <asm/irq.h>
-
-#include "myri_sbus.h"
-
-/* #define DEBUG_DETECT */
-/* #define DEBUG_IRQ */
-/* #define DEBUG_TRANSMIT */
-/* #define DEBUG_RECEIVE */
-/* #define DEBUG_HEADER */
-
-#ifdef DEBUG_DETECT
-#define DET(x) printk x
-#else
-#define DET(x)
-#endif
-
-#ifdef DEBUG_IRQ
-#define DIRQ(x) printk x
-#else
-#define DIRQ(x)
-#endif
-
-#ifdef DEBUG_TRANSMIT
-#define DTX(x) printk x
-#else
-#define DTX(x)
-#endif
-
-#ifdef DEBUG_RECEIVE
-#define DRX(x) printk x
-#else
-#define DRX(x)
-#endif
-
-#ifdef DEBUG_HEADER
-#define DHDR(x) printk x
-#else
-#define DHDR(x)
-#endif
-
-/* Firmware name */
-#define FWNAME "myricom/lanai.bin"
-
-static void myri_reset_off(void __iomem *lp, void __iomem *cregs)
-{
- /* Clear IRQ mask. */
- sbus_writel(0, lp + LANAI_EIMASK);
-
- /* Turn RESET function off. */
- sbus_writel(CONTROL_ROFF, cregs + MYRICTRL_CTRL);
-}
-
-static void myri_reset_on(void __iomem *cregs)
-{
- /* Enable RESET function. */
- sbus_writel(CONTROL_RON, cregs + MYRICTRL_CTRL);
-
- /* Disable IRQ's. */
- sbus_writel(CONTROL_DIRQ, cregs + MYRICTRL_CTRL);
-}
-
-static void myri_disable_irq(void __iomem *lp, void __iomem *cregs)
-{
- sbus_writel(CONTROL_DIRQ, cregs + MYRICTRL_CTRL);
- sbus_writel(0, lp + LANAI_EIMASK);
- sbus_writel(ISTAT_HOST, lp + LANAI_ISTAT);
-}
-
-static void myri_enable_irq(void __iomem *lp, void __iomem *cregs)
-{
- sbus_writel(CONTROL_EIRQ, cregs + MYRICTRL_CTRL);
- sbus_writel(ISTAT_HOST, lp + LANAI_EIMASK);
-}
-
-static inline void bang_the_chip(struct myri_eth *mp)
-{
- struct myri_shmem __iomem *shmem = mp->shmem;
- void __iomem *cregs = mp->cregs;
-
- sbus_writel(1, &shmem->send);
- sbus_writel(CONTROL_WON, cregs + MYRICTRL_CTRL);
-}
-
-static int myri_do_handshake(struct myri_eth *mp)
-{
- struct myri_shmem __iomem *shmem = mp->shmem;
- void __iomem *cregs = mp->cregs;
- struct myri_channel __iomem *chan = &shmem->channel;
- int tick = 0;
-
- DET(("myri_do_handshake: "));
- if (sbus_readl(&chan->state) == STATE_READY) {
- DET(("Already STATE_READY, failed.\n"));
- return -1; /* We're hosed... */
- }
-
- myri_disable_irq(mp->lregs, cregs);
-
- while (tick++ < 25) {
- u32 softstate;
-
- /* Wake it up. */
- DET(("shakedown, CONTROL_WON, "));
- sbus_writel(1, &shmem->shakedown);
- sbus_writel(CONTROL_WON, cregs + MYRICTRL_CTRL);
-
- softstate = sbus_readl(&chan->state);
- DET(("chanstate[%08x] ", softstate));
- if (softstate == STATE_READY) {
- DET(("wakeup successful, "));
- break;
- }
-
- if (softstate != STATE_WFN) {
- DET(("not WFN setting that, "));
- sbus_writel(STATE_WFN, &chan->state);
- }
-
- udelay(20);
- }
-
- myri_enable_irq(mp->lregs, cregs);
-
- if (tick > 25) {
- DET(("25 ticks we lose, failure.\n"));
- return -1;
- }
- DET(("success\n"));
- return 0;
-}
-
-static int __devinit myri_load_lanai(struct myri_eth *mp)
-{
- const struct firmware *fw;
- struct net_device *dev = mp->dev;
- struct myri_shmem __iomem *shmem = mp->shmem;
- void __iomem *rptr;
- int i, lanai4_data_size;
-
- myri_disable_irq(mp->lregs, mp->cregs);
- myri_reset_on(mp->cregs);
-
- rptr = mp->lanai;
- for (i = 0; i < mp->eeprom.ramsz; i++)
- sbus_writeb(0, rptr + i);
-
- if (mp->eeprom.cpuvers >= CPUVERS_3_0)
- sbus_writel(mp->eeprom.cval, mp->lregs + LANAI_CVAL);
-
- i = request_firmware(&fw, FWNAME, &mp->myri_op->dev);
- if (i) {
- printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
- FWNAME, i);
- return i;
- }
- if (fw->size < 2) {
- printk(KERN_ERR "Bogus length %zu in image \"%s\"\n",
- fw->size, FWNAME);
- release_firmware(fw);
- return -EINVAL;
- }
- lanai4_data_size = fw->data[0] << 8 | fw->data[1];
-
- /* Load executable code. */
- for (i = 2; i < fw->size; i++)
- sbus_writeb(fw->data[i], rptr++);
-
- /* Load data segment. */
- for (i = 0; i < lanai4_data_size; i++)
- sbus_writeb(0, rptr++);
-
- /* Set device address. */
- sbus_writeb(0, &shmem->addr[0]);
- sbus_writeb(0, &shmem->addr[1]);
- for (i = 0; i < 6; i++)
- sbus_writeb(dev->dev_addr[i],
- &shmem->addr[i + 2]);
-
- /* Set SBUS bursts and interrupt mask. */
- sbus_writel(((mp->myri_bursts & 0xf8) >> 3), &shmem->burst);
- sbus_writel(SHMEM_IMASK_RX, &shmem->imask);
-
- /* Release the LANAI. */
- myri_disable_irq(mp->lregs, mp->cregs);
- myri_reset_off(mp->lregs, mp->cregs);
- myri_disable_irq(mp->lregs, mp->cregs);
-
- /* Wait for the reset to complete. */
- for (i = 0; i < 5000; i++) {
- if (sbus_readl(&shmem->channel.state) != STATE_READY)
- break;
- else
- udelay(10);
- }
-
- if (i == 5000)
- printk(KERN_ERR "myricom: Chip would not reset after firmware load.\n");
-
- i = myri_do_handshake(mp);
- if (i)
- printk(KERN_ERR "myricom: Handshake with LANAI failed.\n");
-
- if (mp->eeprom.cpuvers == CPUVERS_4_0)
- sbus_writel(0, mp->lregs + LANAI_VERS);
-
- release_firmware(fw);
- return i;
-}
-
-static void myri_clean_rings(struct myri_eth *mp)
-{
- struct sendq __iomem *sq = mp->sq;
- struct recvq __iomem *rq = mp->rq;
- int i;
-
- sbus_writel(0, &rq->tail);
- sbus_writel(0, &rq->head);
- for (i = 0; i < (RX_RING_SIZE+1); i++) {
- if (mp->rx_skbs[i] != NULL) {
- struct myri_rxd __iomem *rxd = &rq->myri_rxd[i];
- u32 dma_addr;
-
- dma_addr = sbus_readl(&rxd->myri_scatters[0].addr);
- dma_unmap_single(&mp->myri_op->dev, dma_addr,
- RX_ALLOC_SIZE, DMA_FROM_DEVICE);
- dev_kfree_skb(mp->rx_skbs[i]);
- mp->rx_skbs[i] = NULL;
- }
- }
-
- mp->tx_old = 0;
- sbus_writel(0, &sq->tail);
- sbus_writel(0, &sq->head);
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (mp->tx_skbs[i] != NULL) {
- struct sk_buff *skb = mp->tx_skbs[i];
- struct myri_txd __iomem *txd = &sq->myri_txd[i];
- u32 dma_addr;
-
- dma_addr = sbus_readl(&txd->myri_gathers[0].addr);
- dma_unmap_single(&mp->myri_op->dev, dma_addr,
- (skb->len + 3) & ~3,
- DMA_TO_DEVICE);
- dev_kfree_skb(mp->tx_skbs[i]);
- mp->tx_skbs[i] = NULL;
- }
- }
-}
-
-static void myri_init_rings(struct myri_eth *mp, int from_irq)
-{
- struct recvq __iomem *rq = mp->rq;
- struct myri_rxd __iomem *rxd = &rq->myri_rxd[0];
- struct net_device *dev = mp->dev;
- gfp_t gfp_flags = GFP_KERNEL;
- int i;
-
- if (from_irq || in_interrupt())
- gfp_flags = GFP_ATOMIC;
-
- myri_clean_rings(mp);
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = myri_alloc_skb(RX_ALLOC_SIZE, gfp_flags);
- u32 dma_addr;
-
- if (!skb)
- continue;
- mp->rx_skbs[i] = skb;
- skb->dev = dev;
- skb_put(skb, RX_ALLOC_SIZE);
-
- dma_addr = dma_map_single(&mp->myri_op->dev,
- skb->data, RX_ALLOC_SIZE,
- DMA_FROM_DEVICE);
- sbus_writel(dma_addr, &rxd[i].myri_scatters[0].addr);
- sbus_writel(RX_ALLOC_SIZE, &rxd[i].myri_scatters[0].len);
- sbus_writel(i, &rxd[i].ctx);
- sbus_writel(1, &rxd[i].num_sg);
- }
- sbus_writel(0, &rq->head);
- sbus_writel(RX_RING_SIZE, &rq->tail);
-}
-
-static int myri_init(struct myri_eth *mp, int from_irq)
-{
- myri_init_rings(mp, from_irq);
- return 0;
-}
-
-static void myri_is_not_so_happy(struct myri_eth *mp)
-{
-}
-
-#ifdef DEBUG_HEADER
-static void dump_ehdr(struct ethhdr *ehdr)
-{
- printk("ehdr[h_dst(%pM)"
- "h_source(%pM)"
- "h_proto(%04x)]\n",
- ehdr->h_dest, ehdr->h_source, ehdr->h_proto);
-}
-
-static void dump_ehdr_and_myripad(unsigned char *stuff)
-{
- struct ethhdr *ehdr = (struct ethhdr *) (stuff + 2);
-
- printk("pad[%02x:%02x]", stuff[0], stuff[1]);
- dump_ehdr(ehdr);
-}
-#endif
-
-static void myri_tx(struct myri_eth *mp, struct net_device *dev)
-{
- struct sendq __iomem *sq= mp->sq;
- int entry = mp->tx_old;
- int limit = sbus_readl(&sq->head);
-
- DTX(("entry[%d] limit[%d] ", entry, limit));
- if (entry == limit)
- return;
- while (entry != limit) {
- struct sk_buff *skb = mp->tx_skbs[entry];
- u32 dma_addr;
-
- DTX(("SKB[%d] ", entry));
- dma_addr = sbus_readl(&sq->myri_txd[entry].myri_gathers[0].addr);
- dma_unmap_single(&mp->myri_op->dev, dma_addr,
- skb->len, DMA_TO_DEVICE);
- dev_kfree_skb(skb);
- mp->tx_skbs[entry] = NULL;
- dev->stats.tx_packets++;
- entry = NEXT_TX(entry);
- }
- mp->tx_old = entry;
-}
-
-/* Determine the packet's protocol ID. The rule here is that we
- * assume 802.3 if the type field is short enough to be a length.
- * This is normal practice and works for any 'now in use' protocol.
- */
-static __be16 myri_type_trans(struct sk_buff *skb, struct net_device *dev)
-{
- struct ethhdr *eth;
- unsigned char *rawp;
-
- skb_set_mac_header(skb, MYRI_PAD_LEN);
- skb_pull(skb, dev->hard_header_len);
- eth = eth_hdr(skb);
-
-#ifdef DEBUG_HEADER
- DHDR(("myri_type_trans: "));
- dump_ehdr(eth);
-#endif
- if (*eth->h_dest & 1) {
- if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN)==0)
- skb->pkt_type = PACKET_BROADCAST;
- else
- skb->pkt_type = PACKET_MULTICAST;
- } else if (dev->flags & (IFF_PROMISC|IFF_ALLMULTI)) {
- if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
- skb->pkt_type = PACKET_OTHERHOST;
- }
-
- if (ntohs(eth->h_proto) >= 1536)
- return eth->h_proto;
-
- rawp = skb->data;
-
- /* This is a magic hack to spot IPX packets. Older Novell breaks
- * the protocol design and runs IPX over 802.3 without an 802.2 LLC
- * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
- * won't work for fault tolerant netware but does for the rest.
- */
- if (*(unsigned short *)rawp == 0xFFFF)
- return htons(ETH_P_802_3);
-
- /* Real 802.2 LLC */
- return htons(ETH_P_802_2);
-}
-
-static void myri_rx(struct myri_eth *mp, struct net_device *dev)
-{
- struct recvq __iomem *rq = mp->rq;
- struct recvq __iomem *rqa = mp->rqack;
- int entry = sbus_readl(&rqa->head);
- int limit = sbus_readl(&rqa->tail);
- int drops;
-
- DRX(("entry[%d] limit[%d] ", entry, limit));
- if (entry == limit)
- return;
- drops = 0;
- DRX(("\n"));
- while (entry != limit) {
- struct myri_rxd __iomem *rxdack = &rqa->myri_rxd[entry];
- u32 csum = sbus_readl(&rxdack->csum);
- int len = sbus_readl(&rxdack->myri_scatters[0].len);
- int index = sbus_readl(&rxdack->ctx);
- struct myri_rxd __iomem *rxd = &rq->myri_rxd[sbus_readl(&rq->tail)];
- struct sk_buff *skb = mp->rx_skbs[index];
-
- /* Ack it. */
- sbus_writel(NEXT_RX(entry), &rqa->head);
-
- /* Check for errors. */
- DRX(("rxd[%d]: %p len[%d] csum[%08x] ", entry, rxd, len, csum));
- dma_sync_single_for_cpu(&mp->myri_op->dev,
- sbus_readl(&rxd->myri_scatters[0].addr),
- RX_ALLOC_SIZE, DMA_FROM_DEVICE);
- if (len < (ETH_HLEN + MYRI_PAD_LEN) || (skb->data[0] != MYRI_PAD_LEN)) {
- DRX(("ERROR["));
- dev->stats.rx_errors++;
- if (len < (ETH_HLEN + MYRI_PAD_LEN)) {
- DRX(("BAD_LENGTH] "));
- dev->stats.rx_length_errors++;
- } else {
- DRX(("NO_PADDING] "));
- dev->stats.rx_frame_errors++;
- }
-
- /* Return it to the LANAI. */
- drop_it:
- drops++;
- DRX(("DROP "));
- dev->stats.rx_dropped++;
- dma_sync_single_for_device(&mp->myri_op->dev,
- sbus_readl(&rxd->myri_scatters[0].addr),
- RX_ALLOC_SIZE,
- DMA_FROM_DEVICE);
- sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len);
- sbus_writel(index, &rxd->ctx);
- sbus_writel(1, &rxd->num_sg);
- sbus_writel(NEXT_RX(sbus_readl(&rq->tail)), &rq->tail);
- goto next;
- }
-
- DRX(("len[%d] ", len));
- if (len > RX_COPY_THRESHOLD) {
- struct sk_buff *new_skb;
- u32 dma_addr;
-
- DRX(("BIGBUFF "));
- new_skb = myri_alloc_skb(RX_ALLOC_SIZE, GFP_ATOMIC);
- if (new_skb == NULL) {
- DRX(("skb_alloc(FAILED) "));
- goto drop_it;
- }
- dma_unmap_single(&mp->myri_op->dev,
- sbus_readl(&rxd->myri_scatters[0].addr),
- RX_ALLOC_SIZE,
- DMA_FROM_DEVICE);
- mp->rx_skbs[index] = new_skb;
- new_skb->dev = dev;
- skb_put(new_skb, RX_ALLOC_SIZE);
- dma_addr = dma_map_single(&mp->myri_op->dev,
- new_skb->data,
- RX_ALLOC_SIZE,
- DMA_FROM_DEVICE);
- sbus_writel(dma_addr, &rxd->myri_scatters[0].addr);
- sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len);
- sbus_writel(index, &rxd->ctx);
- sbus_writel(1, &rxd->num_sg);
- sbus_writel(NEXT_RX(sbus_readl(&rq->tail)), &rq->tail);
-
- /* Trim the original skb for the netif. */
- DRX(("trim(%d) ", len));
- skb_trim(skb, len);
- } else {
- struct sk_buff *copy_skb = dev_alloc_skb(len);
-
- DRX(("SMALLBUFF "));
- if (copy_skb == NULL) {
- DRX(("dev_alloc_skb(FAILED) "));
- goto drop_it;
- }
- /* DMA sync already done above. */
- copy_skb->dev = dev;
- DRX(("resv_and_put "));
- skb_put(copy_skb, len);
- skb_copy_from_linear_data(skb, copy_skb->data, len);
-
- /* Reuse original ring buffer. */
- DRX(("reuse "));
- dma_sync_single_for_device(&mp->myri_op->dev,
- sbus_readl(&rxd->myri_scatters[0].addr),
- RX_ALLOC_SIZE,
- DMA_FROM_DEVICE);
- sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len);
- sbus_writel(index, &rxd->ctx);
- sbus_writel(1, &rxd->num_sg);
- sbus_writel(NEXT_RX(sbus_readl(&rq->tail)), &rq->tail);
-
- skb = copy_skb;
- }
-
- /* Just like the happy meal we get checksums from this card. */
- skb->csum = csum;
- skb->ip_summed = CHECKSUM_UNNECESSARY; /* XXX */
-
- skb->protocol = myri_type_trans(skb, dev);
- DRX(("prot[%04x] netif_rx ", skb->protocol));
- netif_rx(skb);
-
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += len;
- next:
- DRX(("NEXT\n"));
- entry = NEXT_RX(entry);
- }
-}
-
-static irqreturn_t myri_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = (struct net_device *) dev_id;
- struct myri_eth *mp = netdev_priv(dev);
- void __iomem *lregs = mp->lregs;
- struct myri_channel __iomem *chan = &mp->shmem->channel;
- unsigned long flags;
- u32 status;
- int handled = 0;
-
- spin_lock_irqsave(&mp->irq_lock, flags);
-
- status = sbus_readl(lregs + LANAI_ISTAT);
- DIRQ(("myri_interrupt: status[%08x] ", status));
- if (status & ISTAT_HOST) {
- u32 softstate;
-
- handled = 1;
- DIRQ(("IRQ_DISAB "));
- myri_disable_irq(lregs, mp->cregs);
- softstate = sbus_readl(&chan->state);
- DIRQ(("state[%08x] ", softstate));
- if (softstate != STATE_READY) {
- DIRQ(("myri_not_so_happy "));
- myri_is_not_so_happy(mp);
- }
- DIRQ(("\nmyri_rx: "));
- myri_rx(mp, dev);
- DIRQ(("\nistat=ISTAT_HOST "));
- sbus_writel(ISTAT_HOST, lregs + LANAI_ISTAT);
- DIRQ(("IRQ_ENAB "));
- myri_enable_irq(lregs, mp->cregs);
- }
- DIRQ(("\n"));
-
- spin_unlock_irqrestore(&mp->irq_lock, flags);
-
- return IRQ_RETVAL(handled);
-}
-
-static int myri_open(struct net_device *dev)
-{
- struct myri_eth *mp = netdev_priv(dev);
-
- return myri_init(mp, in_interrupt());
-}
-
-static int myri_close(struct net_device *dev)
-{
- struct myri_eth *mp = netdev_priv(dev);
-
- myri_clean_rings(mp);
- return 0;
-}
-
-static void myri_tx_timeout(struct net_device *dev)
-{
- struct myri_eth *mp = netdev_priv(dev);
-
- printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
-
- dev->stats.tx_errors++;
- myri_init(mp, 0);
- netif_wake_queue(dev);
-}
-
-static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct myri_eth *mp = netdev_priv(dev);
- struct sendq __iomem *sq = mp->sq;
- struct myri_txd __iomem *txd;
- unsigned long flags;
- unsigned int head, tail;
- int len, entry;
- u32 dma_addr;
-
- DTX(("myri_start_xmit: "));
-
- myri_tx(mp, dev);
-
- netif_stop_queue(dev);
-
- /* This is just to prevent multiple PIO reads for TX_BUFFS_AVAIL. */
- head = sbus_readl(&sq->head);
- tail = sbus_readl(&sq->tail);
-
- if (!TX_BUFFS_AVAIL(head, tail)) {
- DTX(("no buffs available, returning 1\n"));
- return NETDEV_TX_BUSY;
- }
-
- spin_lock_irqsave(&mp->irq_lock, flags);
-
- DHDR(("xmit[skbdata(%p)]\n", skb->data));
-#ifdef DEBUG_HEADER
- dump_ehdr_and_myripad(((unsigned char *) skb->data));
-#endif
-
- /* XXX Maybe this can go as well. */
- len = skb->len;
- if (len & 3) {
- DTX(("len&3 "));
- len = (len + 4) & (~3);
- }
-
- entry = sbus_readl(&sq->tail);
-
- txd = &sq->myri_txd[entry];
- mp->tx_skbs[entry] = skb;
-
- /* Must do this before we sbus map it. */
- if (skb->data[MYRI_PAD_LEN] & 0x1) {
- sbus_writew(0xffff, &txd->addr[0]);
- sbus_writew(0xffff, &txd->addr[1]);
- sbus_writew(0xffff, &txd->addr[2]);
- sbus_writew(0xffff, &txd->addr[3]);
- } else {
- sbus_writew(0xffff, &txd->addr[0]);
- sbus_writew((skb->data[0] << 8) | skb->data[1], &txd->addr[1]);
- sbus_writew((skb->data[2] << 8) | skb->data[3], &txd->addr[2]);
- sbus_writew((skb->data[4] << 8) | skb->data[5], &txd->addr[3]);
- }
-
- dma_addr = dma_map_single(&mp->myri_op->dev, skb->data,
- len, DMA_TO_DEVICE);
- sbus_writel(dma_addr, &txd->myri_gathers[0].addr);
- sbus_writel(len, &txd->myri_gathers[0].len);
- sbus_writel(1, &txd->num_sg);
- sbus_writel(KERNEL_CHANNEL, &txd->chan);
- sbus_writel(len, &txd->len);
- sbus_writel((u32)-1, &txd->csum_off);
- sbus_writel(0, &txd->csum_field);
-
- sbus_writel(NEXT_TX(entry), &sq->tail);
- DTX(("BangTheChip "));
- bang_the_chip(mp);
-
- DTX(("tbusy=0, returning 0\n"));
- netif_start_queue(dev);
- spin_unlock_irqrestore(&mp->irq_lock, flags);
- return NETDEV_TX_OK;
-}
-
-/* Create the MyriNet MAC header for an arbitrary protocol layer
- *
- * saddr=NULL means use device source address
- * daddr=NULL means leave destination address (eg unresolved arp)
- */
-static int myri_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, const void *daddr,
- const void *saddr, unsigned len)
-{
- struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
- unsigned char *pad = (unsigned char *) skb_push(skb, MYRI_PAD_LEN);
-
-#ifdef DEBUG_HEADER
- DHDR(("myri_header: pad[%02x,%02x] ", pad[0], pad[1]));
- dump_ehdr(eth);
-#endif
-
- /* Set the MyriNET padding identifier. */
- pad[0] = MYRI_PAD_LEN;
- pad[1] = 0xab;
-
- /* Set the protocol type. For a packet of type ETH_P_802_3/2 we put the
- * length in here instead.
- */
- if (type != ETH_P_802_3 && type != ETH_P_802_2)
- eth->h_proto = htons(type);
- else
- eth->h_proto = htons(len);
-
- /* Set the source hardware address. */
- if (saddr)
- memcpy(eth->h_source, saddr, dev->addr_len);
- else
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-
- /* Anyway, the loopback-device should never use this function... */
- if (dev->flags & IFF_LOOPBACK) {
- int i;
- for (i = 0; i < dev->addr_len; i++)
- eth->h_dest[i] = 0;
- return dev->hard_header_len;
- }
-
- if (daddr) {
- memcpy(eth->h_dest, daddr, dev->addr_len);
- return dev->hard_header_len;
- }
- return -dev->hard_header_len;
-}
-
-/* Rebuild the MyriNet MAC header. This is called after an ARP
- * (or in future other address resolution) has completed on this
- * sk_buff. We now let ARP fill in the other fields.
- */
-static int myri_rebuild_header(struct sk_buff *skb)
-{
- unsigned char *pad = (unsigned char *) skb->data;
- struct ethhdr *eth = (struct ethhdr *) (pad + MYRI_PAD_LEN);
- struct net_device *dev = skb->dev;
-
-#ifdef DEBUG_HEADER
- DHDR(("myri_rebuild_header: pad[%02x,%02x] ", pad[0], pad[1]));
- dump_ehdr(eth);
-#endif
-
- /* Refill MyriNet padding identifiers, this is just being anal. */
- pad[0] = MYRI_PAD_LEN;
- pad[1] = 0xab;
-
- switch (eth->h_proto)
- {
-#ifdef CONFIG_INET
- case cpu_to_be16(ETH_P_IP):
- return arp_find(eth->h_dest, skb);
-#endif
-
- default:
- printk(KERN_DEBUG
- "%s: unable to resolve type %X addresses.\n",
- dev->name, (int)eth->h_proto);
-
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
- return 0;
- break;
- }
-
- return 0;
-}
-
-static int myri_header_cache(const struct neighbour *neigh, struct hh_cache *hh)
-{
- unsigned short type = hh->hh_type;
- unsigned char *pad;
- struct ethhdr *eth;
- const struct net_device *dev = neigh->dev;
-
- pad = ((unsigned char *) hh->hh_data) +
- HH_DATA_OFF(sizeof(*eth) + MYRI_PAD_LEN);
- eth = (struct ethhdr *) (pad + MYRI_PAD_LEN);
-
- if (type == htons(ETH_P_802_3))
- return -1;
-
- /* Refill MyriNet padding identifiers, this is just being anal. */
- pad[0] = MYRI_PAD_LEN;
- pad[1] = 0xab;
-
- eth->h_proto = type;
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
- memcpy(eth->h_dest, neigh->ha, dev->addr_len);
- hh->hh_len = 16;
- return 0;
-}
-
-
-/* Called by Address Resolution module to notify changes in address. */
-void myri_header_cache_update(struct hh_cache *hh,
- const struct net_device *dev,
- const unsigned char * haddr)
-{
- memcpy(((u8*)hh->hh_data) + HH_DATA_OFF(sizeof(struct ethhdr)),
- haddr, dev->addr_len);
-}
-
-static int myri_change_mtu(struct net_device *dev, int new_mtu)
-{
- if ((new_mtu < (ETH_HLEN + MYRI_PAD_LEN)) || (new_mtu > MYRINET_MTU))
- return -EINVAL;
- dev->mtu = new_mtu;
- return 0;
-}
-
-static void myri_set_multicast(struct net_device *dev)
-{
- /* Do nothing, all MyriCOM nodes transmit multicast frames
- * as broadcast packets...
- */
-}
-
-static inline void set_boardid_from_idprom(struct myri_eth *mp, int num)
-{
- mp->eeprom.id[0] = 0;
- mp->eeprom.id[1] = idprom->id_machtype;
- mp->eeprom.id[2] = (idprom->id_sernum >> 16) & 0xff;
- mp->eeprom.id[3] = (idprom->id_sernum >> 8) & 0xff;
- mp->eeprom.id[4] = (idprom->id_sernum >> 0) & 0xff;
- mp->eeprom.id[5] = num;
-}
-
-static inline void determine_reg_space_size(struct myri_eth *mp)
-{
- switch(mp->eeprom.cpuvers) {
- case CPUVERS_2_3:
- case CPUVERS_3_0:
- case CPUVERS_3_1:
- case CPUVERS_3_2:
- mp->reg_size = (3 * 128 * 1024) + 4096;
- break;
-
- case CPUVERS_4_0:
- case CPUVERS_4_1:
- mp->reg_size = ((4096<<1) + mp->eeprom.ramsz);
- break;
-
- case CPUVERS_4_2:
- case CPUVERS_5_0:
- default:
- printk("myricom: AIEEE weird cpu version %04x assuming pre4.0\n",
- mp->eeprom.cpuvers);
- mp->reg_size = (3 * 128 * 1024) + 4096;
- }
-}
-
-#ifdef DEBUG_DETECT
-static void dump_eeprom(struct myri_eth *mp)
-{
- printk("EEPROM: clockval[%08x] cpuvers[%04x] "
- "id[%02x,%02x,%02x,%02x,%02x,%02x]\n",
- mp->eeprom.cval, mp->eeprom.cpuvers,
- mp->eeprom.id[0], mp->eeprom.id[1], mp->eeprom.id[2],
- mp->eeprom.id[3], mp->eeprom.id[4], mp->eeprom.id[5]);
- printk("EEPROM: ramsz[%08x]\n", mp->eeprom.ramsz);
- printk("EEPROM: fvers[%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
- mp->eeprom.fvers[0], mp->eeprom.fvers[1], mp->eeprom.fvers[2],
- mp->eeprom.fvers[3], mp->eeprom.fvers[4], mp->eeprom.fvers[5],
- mp->eeprom.fvers[6], mp->eeprom.fvers[7]);
- printk("EEPROM: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
- mp->eeprom.fvers[8], mp->eeprom.fvers[9], mp->eeprom.fvers[10],
- mp->eeprom.fvers[11], mp->eeprom.fvers[12], mp->eeprom.fvers[13],
- mp->eeprom.fvers[14], mp->eeprom.fvers[15]);
- printk("EEPROM: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
- mp->eeprom.fvers[16], mp->eeprom.fvers[17], mp->eeprom.fvers[18],
- mp->eeprom.fvers[19], mp->eeprom.fvers[20], mp->eeprom.fvers[21],
- mp->eeprom.fvers[22], mp->eeprom.fvers[23]);
- printk("EEPROM: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x]\n",
- mp->eeprom.fvers[24], mp->eeprom.fvers[25], mp->eeprom.fvers[26],
- mp->eeprom.fvers[27], mp->eeprom.fvers[28], mp->eeprom.fvers[29],
- mp->eeprom.fvers[30], mp->eeprom.fvers[31]);
- printk("EEPROM: mvers[%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
- mp->eeprom.mvers[0], mp->eeprom.mvers[1], mp->eeprom.mvers[2],
- mp->eeprom.mvers[3], mp->eeprom.mvers[4], mp->eeprom.mvers[5],
- mp->eeprom.mvers[6], mp->eeprom.mvers[7]);
- printk("EEPROM: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x]\n",
- mp->eeprom.mvers[8], mp->eeprom.mvers[9], mp->eeprom.mvers[10],
- mp->eeprom.mvers[11], mp->eeprom.mvers[12], mp->eeprom.mvers[13],
- mp->eeprom.mvers[14], mp->eeprom.mvers[15]);
- printk("EEPROM: dlval[%04x] brd_type[%04x] bus_type[%04x] prod_code[%04x]\n",
- mp->eeprom.dlval, mp->eeprom.brd_type, mp->eeprom.bus_type,
- mp->eeprom.prod_code);
- printk("EEPROM: serial_num[%08x]\n", mp->eeprom.serial_num);
-}
-#endif
-
-static const struct header_ops myri_header_ops = {
- .create = myri_header,
- .rebuild = myri_rebuild_header,
- .cache = myri_header_cache,
- .cache_update = myri_header_cache_update,
-};
-
-static const struct net_device_ops myri_ops = {
- .ndo_open = myri_open,
- .ndo_stop = myri_close,
- .ndo_start_xmit = myri_start_xmit,
- .ndo_set_multicast_list = myri_set_multicast,
- .ndo_tx_timeout = myri_tx_timeout,
- .ndo_change_mtu = myri_change_mtu,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int __devinit myri_sbus_probe(struct platform_device *op)
-{
- struct device_node *dp = op->dev.of_node;
- static unsigned version_printed;
- struct net_device *dev;
- struct myri_eth *mp;
- const void *prop;
- static int num;
- int i, len;
-
- DET(("myri_ether_init(%p,%d):\n", op, num));
- dev = alloc_etherdev(sizeof(struct myri_eth));
- if (!dev)
- return -ENOMEM;
-
- if (version_printed++ == 0)
- printk(version);
-
- SET_NETDEV_DEV(dev, &op->dev);
-
- mp = netdev_priv(dev);
- spin_lock_init(&mp->irq_lock);
- mp->myri_op = op;
-
- /* Clean out skb arrays. */
- for (i = 0; i < (RX_RING_SIZE + 1); i++)
- mp->rx_skbs[i] = NULL;
-
- for (i = 0; i < TX_RING_SIZE; i++)
- mp->tx_skbs[i] = NULL;
-
- /* First check for EEPROM information. */
- prop = of_get_property(dp, "myrinet-eeprom-info", &len);
-
- if (prop)
- memcpy(&mp->eeprom, prop, sizeof(struct myri_eeprom));
- if (!prop) {
- /* No eeprom property, must cook up the values ourselves. */
- DET(("No EEPROM: "));
- mp->eeprom.bus_type = BUS_TYPE_SBUS;
- mp->eeprom.cpuvers =
- of_getintprop_default(dp, "cpu_version", 0);
- mp->eeprom.cval =
- of_getintprop_default(dp, "clock_value", 0);
- mp->eeprom.ramsz = of_getintprop_default(dp, "sram_size", 0);
- if (!mp->eeprom.cpuvers)
- mp->eeprom.cpuvers = CPUVERS_2_3;
- if (mp->eeprom.cpuvers < CPUVERS_3_0)
- mp->eeprom.cval = 0;
- if (!mp->eeprom.ramsz)
- mp->eeprom.ramsz = (128 * 1024);
-
- prop = of_get_property(dp, "myrinet-board-id", &len);
- if (prop)
- memcpy(&mp->eeprom.id[0], prop, 6);
- else
- set_boardid_from_idprom(mp, num);
-
- prop = of_get_property(dp, "fpga_version", &len);
- if (prop)
- memcpy(&mp->eeprom.fvers[0], prop, 32);
- else
- memset(&mp->eeprom.fvers[0], 0, 32);
-
- if (mp->eeprom.cpuvers == CPUVERS_4_1) {
- if (mp->eeprom.ramsz == (128 * 1024))
- mp->eeprom.ramsz = (256 * 1024);
- if ((mp->eeprom.cval == 0x40414041) ||
- (mp->eeprom.cval == 0x90449044))
- mp->eeprom.cval = 0x50e450e4;
- }
- }
-#ifdef DEBUG_DETECT
- dump_eeprom(mp);
-#endif
-
- for (i = 0; i < 6; i++)
- dev->dev_addr[i] = mp->eeprom.id[i];
-
- determine_reg_space_size(mp);
-
- /* Map in the MyriCOM register/localram set. */
- if (mp->eeprom.cpuvers < CPUVERS_4_0) {
- /* XXX Makes no sense, if control reg is non-existent this
- * XXX driver cannot function at all... maybe pre-4.0 is
- * XXX only a valid version for PCI cards? Ask feldy...
- */
- DET(("Mapping regs for cpuvers < CPUVERS_4_0\n"));
- mp->regs = of_ioremap(&op->resource[0], 0,
- mp->reg_size, "MyriCOM Regs");
- if (!mp->regs) {
- printk("MyriCOM: Cannot map MyriCOM registers.\n");
- goto err;
- }
- mp->lanai = mp->regs + (256 * 1024);
- mp->lregs = mp->lanai + (0x10000 * 2);
- } else {
- DET(("Mapping regs for cpuvers >= CPUVERS_4_0\n"));
- mp->cregs = of_ioremap(&op->resource[0], 0,
- PAGE_SIZE, "MyriCOM Control Regs");
- mp->lregs = of_ioremap(&op->resource[0], (256 * 1024),
- PAGE_SIZE, "MyriCOM LANAI Regs");
- mp->lanai = of_ioremap(&op->resource[0], (512 * 1024),
- mp->eeprom.ramsz, "MyriCOM SRAM");
- }
- DET(("Registers mapped: cregs[%p] lregs[%p] lanai[%p]\n",
- mp->cregs, mp->lregs, mp->lanai));
-
- if (mp->eeprom.cpuvers >= CPUVERS_4_0)
- mp->shmem_base = 0xf000;
- else
- mp->shmem_base = 0x8000;
-
- DET(("Shared memory base is %04x, ", mp->shmem_base));
-
- mp->shmem = (struct myri_shmem __iomem *)
- (mp->lanai + (mp->shmem_base * 2));
- DET(("shmem mapped at %p\n", mp->shmem));
-
- mp->rqack = &mp->shmem->channel.recvqa;
- mp->rq = &mp->shmem->channel.recvq;
- mp->sq = &mp->shmem->channel.sendq;
-
- /* Reset the board. */
- DET(("Resetting LANAI\n"));
- myri_reset_off(mp->lregs, mp->cregs);
- myri_reset_on(mp->cregs);
-
- /* Turn IRQ's off. */
- myri_disable_irq(mp->lregs, mp->cregs);
-
- /* Reset once more. */
- myri_reset_on(mp->cregs);
-
- /* Get the supported DVMA burst sizes from our SBUS. */
- mp->myri_bursts = of_getintprop_default(dp->parent,
- "burst-sizes", 0x00);
- if (!sbus_can_burst64())
- mp->myri_bursts &= ~(DMA_BURST64);
-
- DET(("MYRI bursts %02x\n", mp->myri_bursts));
-
- /* Encode SBUS interrupt level in second control register. */
- i = of_getintprop_default(dp, "interrupts", 0);
- if (i == 0)
- i = 4;
- DET(("prom_getint(interrupts)==%d, irqlvl set to %04x\n",
- i, (1 << i)));
-
- sbus_writel((1 << i), mp->cregs + MYRICTRL_IRQLVL);
-
- mp->dev = dev;
- dev->watchdog_timeo = 5*HZ;
- dev->irq = op->archdata.irqs[0];
- dev->netdev_ops = &myri_ops;
-
- /* Register interrupt handler now. */
- DET(("Requesting MYRIcom IRQ line.\n"));
- if (request_irq(dev->irq, myri_interrupt,
- IRQF_SHARED, "MyriCOM Ethernet", (void *) dev)) {
- printk("MyriCOM: Cannot register interrupt handler.\n");
- goto err;
- }
-
- dev->mtu = MYRINET_MTU;
- dev->header_ops = &myri_header_ops;
-
- dev->hard_header_len = (ETH_HLEN + MYRI_PAD_LEN);
-
- /* Load code onto the LANai. */
- DET(("Loading LANAI firmware\n"));
- if (myri_load_lanai(mp)) {
- printk(KERN_ERR "MyriCOM: Cannot Load LANAI firmware.\n");
- goto err_free_irq;
- }
-
- if (register_netdev(dev)) {
- printk("MyriCOM: Cannot register device.\n");
- goto err_free_irq;
- }
-
- dev_set_drvdata(&op->dev, mp);
-
- num++;
-
- printk("%s: MyriCOM MyriNET Ethernet %pM\n",
- dev->name, dev->dev_addr);
-
- return 0;
-
-err_free_irq:
- free_irq(dev->irq, dev);
-err:
- /* This will also free the co-allocated private data*/
- free_netdev(dev);
- return -ENODEV;
-}
-
-static int __devexit myri_sbus_remove(struct platform_device *op)
-{
- struct myri_eth *mp = dev_get_drvdata(&op->dev);
- struct net_device *net_dev = mp->dev;
-
- unregister_netdev(net_dev);
-
- free_irq(net_dev->irq, net_dev);
-
- if (mp->eeprom.cpuvers < CPUVERS_4_0) {
- of_iounmap(&op->resource[0], mp->regs, mp->reg_size);
- } else {
- of_iounmap(&op->resource[0], mp->cregs, PAGE_SIZE);
- of_iounmap(&op->resource[0], mp->lregs, (256 * 1024));
- of_iounmap(&op->resource[0], mp->lanai, (512 * 1024));
- }
-
- free_netdev(net_dev);
-
- dev_set_drvdata(&op->dev, NULL);
-
- return 0;
-}
-
-static const struct of_device_id myri_sbus_match[] = {
- {
- .name = "MYRICOM,mlanai",
- },
- {
- .name = "myri",
- },
- {},
-};
-
-MODULE_DEVICE_TABLE(of, myri_sbus_match);
-
-static struct platform_driver myri_sbus_driver = {
- .driver = {
- .name = "myri",
- .owner = THIS_MODULE,
- .of_match_table = myri_sbus_match,
- },
- .probe = myri_sbus_probe,
- .remove = __devexit_p(myri_sbus_remove),
-};
-
-static int __init myri_sbus_init(void)
-{
- return platform_driver_register(&myri_sbus_driver);
-}
-
-static void __exit myri_sbus_exit(void)
-{
- platform_driver_unregister(&myri_sbus_driver);
-}
-
-module_init(myri_sbus_init);
-module_exit(myri_sbus_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(FWNAME);
diff --git a/drivers/net/myri_sbus.h b/drivers/net/myri_sbus.h
deleted file mode 100644
index 80a2fa5cf757..000000000000
--- a/drivers/net/myri_sbus.h
+++ /dev/null
@@ -1,311 +0,0 @@
-/* myri_sbus.h: Defines for MyriCOM MyriNET SBUS card driver.
- *
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#ifndef _MYRI_SBUS_H
-#define _MYRI_SBUS_H
-
-/* LANAI Registers */
-#define LANAI_IPF0 0x00UL /* Context zero state registers.*/
-#define LANAI_CUR0 0x04UL
-#define LANAI_PREV0 0x08UL
-#define LANAI_DATA0 0x0cUL
-#define LANAI_DPF0 0x10UL
-#define LANAI_IPF1 0x14UL /* Context one state registers. */
-#define LANAI_CUR1 0x18UL
-#define LANAI_PREV1 0x1cUL
-#define LANAI_DATA1 0x20UL
-#define LANAI_DPF1 0x24UL
-#define LANAI_ISTAT 0x28UL /* Interrupt status. */
-#define LANAI_EIMASK 0x2cUL /* External IRQ mask. */
-#define LANAI_ITIMER 0x30UL /* IRQ timer. */
-#define LANAI_RTC 0x34UL /* Real Time Clock */
-#define LANAI_CSUM 0x38UL /* Checksum. */
-#define LANAI_DMAXADDR 0x3cUL /* SBUS DMA external address. */
-#define LANAI_DMALADDR 0x40UL /* SBUS DMA local address. */
-#define LANAI_DMACTR 0x44UL /* SBUS DMA counter. */
-#define LANAI_RXDMAPTR 0x48UL /* Receive DMA pointer. */
-#define LANAI_RXDMALIM 0x4cUL /* Receive DMA limit. */
-#define LANAI_TXDMAPTR 0x50UL /* Transmit DMA pointer. */
-#define LANAI_TXDMALIM 0x54UL /* Transmit DMA limit. */
-#define LANAI_TXDMALIMT 0x58UL /* Transmit DMA limit w/tail. */
- /* 0x5cUL, reserved */
-#define LANAI_RBYTE 0x60UL /* Receive byte. */
- /* 0x64-->0x6c, reserved */
-#define LANAI_RHALF 0x70UL /* Receive half-word. */
- /* 0x72UL, reserved */
-#define LANAI_RWORD 0x74UL /* Receive word. */
-#define LANAI_SALIGN 0x78UL /* Send align. */
-#define LANAI_SBYTE 0x7cUL /* SingleSend send-byte. */
-#define LANAI_SHALF 0x80UL /* SingleSend send-halfword. */
-#define LANAI_SWORD 0x84UL /* SingleSend send-word. */
-#define LANAI_SSENDT 0x88UL /* SingleSend special. */
-#define LANAI_DMADIR 0x8cUL /* DMA direction. */
-#define LANAI_DMASTAT 0x90UL /* DMA status. */
-#define LANAI_TIMEO 0x94UL /* Timeout register. */
-#define LANAI_MYRINET 0x98UL /* XXX MAGIC myricom thing */
-#define LANAI_HWDEBUG 0x9cUL /* Hardware debugging reg. */
-#define LANAI_LEDS 0xa0UL /* LED control. */
-#define LANAI_VERS 0xa4UL /* Version register. */
-#define LANAI_LINKON 0xa8UL /* Link activation reg. */
- /* 0xac-->0x104, reserved */
-#define LANAI_CVAL 0x108UL /* Clock value register. */
-#define LANAI_REG_SIZE 0x10cUL
-
-/* Interrupt status bits. */
-#define ISTAT_DEBUG 0x80000000
-#define ISTAT_HOST 0x40000000
-#define ISTAT_LAN7 0x00800000
-#define ISTAT_LAN6 0x00400000
-#define ISTAT_LAN5 0x00200000
-#define ISTAT_LAN4 0x00100000
-#define ISTAT_LAN3 0x00080000
-#define ISTAT_LAN2 0x00040000
-#define ISTAT_LAN1 0x00020000
-#define ISTAT_LAN0 0x00010000
-#define ISTAT_WRDY 0x00008000
-#define ISTAT_HRDY 0x00004000
-#define ISTAT_SRDY 0x00002000
-#define ISTAT_LINK 0x00001000
-#define ISTAT_FRES 0x00000800
-#define ISTAT_NRES 0x00000800
-#define ISTAT_WAKE 0x00000400
-#define ISTAT_OB2 0x00000200
-#define ISTAT_OB1 0x00000100
-#define ISTAT_TAIL 0x00000080
-#define ISTAT_WDOG 0x00000040
-#define ISTAT_TIME 0x00000020
-#define ISTAT_DMA 0x00000010
-#define ISTAT_SEND 0x00000008
-#define ISTAT_BUF 0x00000004
-#define ISTAT_RECV 0x00000002
-#define ISTAT_BRDY 0x00000001
-
-/* MYRI Registers */
-#define MYRI_RESETOFF 0x00UL
-#define MYRI_RESETON 0x04UL
-#define MYRI_IRQOFF 0x08UL
-#define MYRI_IRQON 0x0cUL
-#define MYRI_WAKEUPOFF 0x10UL
-#define MYRI_WAKEUPON 0x14UL
-#define MYRI_IRQREAD 0x18UL
- /* 0x1c-->0x3ffc, reserved */
-#define MYRI_LOCALMEM 0x4000UL
-#define MYRI_REG_SIZE 0x25000UL
-
-/* Shared memory interrupt mask. */
-#define SHMEM_IMASK_RX 0x00000002
-#define SHMEM_IMASK_TX 0x00000001
-
-/* Just to make things readable. */
-#define KERNEL_CHANNEL 0
-
-/* The size of this must be >= 129 bytes. */
-struct myri_eeprom {
- unsigned int cval;
- unsigned short cpuvers;
- unsigned char id[6];
- unsigned int ramsz;
- unsigned char fvers[32];
- unsigned char mvers[16];
- unsigned short dlval;
- unsigned short brd_type;
- unsigned short bus_type;
- unsigned short prod_code;
- unsigned int serial_num;
- unsigned short _reserved[24];
- unsigned int _unused[2];
-};
-
-/* EEPROM bus types, only SBUS is valid in this driver. */
-#define BUS_TYPE_SBUS 1
-
-/* EEPROM CPU revisions. */
-#define CPUVERS_2_3 0x0203
-#define CPUVERS_3_0 0x0300
-#define CPUVERS_3_1 0x0301
-#define CPUVERS_3_2 0x0302
-#define CPUVERS_4_0 0x0400
-#define CPUVERS_4_1 0x0401
-#define CPUVERS_4_2 0x0402
-#define CPUVERS_5_0 0x0500
-
-/* MYRI Control Registers */
-#define MYRICTRL_CTRL 0x00UL
-#define MYRICTRL_IRQLVL 0x02UL
-#define MYRICTRL_REG_SIZE 0x04UL
-
-/* Global control register defines. */
-#define CONTROL_ROFF 0x8000 /* Reset OFF. */
-#define CONTROL_RON 0x4000 /* Reset ON. */
-#define CONTROL_EIRQ 0x2000 /* Enable IRQ's. */
-#define CONTROL_DIRQ 0x1000 /* Disable IRQ's. */
-#define CONTROL_WON 0x0800 /* Wake-up ON. */
-
-#define MYRI_SCATTER_ENTRIES 8
-#define MYRI_GATHER_ENTRIES 16
-
-struct myri_sglist {
- u32 addr;
- u32 len;
-};
-
-struct myri_rxd {
- struct myri_sglist myri_scatters[MYRI_SCATTER_ENTRIES]; /* DMA scatter list.*/
- u32 csum; /* HW computed checksum. */
- u32 ctx;
- u32 num_sg; /* Total scatter entries. */
-};
-
-struct myri_txd {
- struct myri_sglist myri_gathers[MYRI_GATHER_ENTRIES]; /* DMA scatter list. */
- u32 num_sg; /* Total scatter entries. */
- u16 addr[4]; /* XXX address */
- u32 chan;
- u32 len; /* Total length of packet. */
- u32 csum_off; /* Where data to csum is. */
- u32 csum_field; /* Where csum goes in pkt. */
-};
-
-#define MYRINET_MTU 8432
-#define RX_ALLOC_SIZE 8448
-#define MYRI_PAD_LEN 2
-#define RX_COPY_THRESHOLD 256
-
-/* These numbers are cast in stone, new firmware is needed if
- * you want to change them.
- */
-#define TX_RING_MAXSIZE 16
-#define RX_RING_MAXSIZE 16
-
-#define TX_RING_SIZE 16
-#define RX_RING_SIZE 16
-
-/* GRRR... */
-static __inline__ int NEXT_RX(int num)
-{
- /* XXX >=??? */
- if(++num > RX_RING_SIZE)
- num = 0;
- return num;
-}
-
-static __inline__ int PREV_RX(int num)
-{
- if(--num < 0)
- num = RX_RING_SIZE;
- return num;
-}
-
-#define NEXT_TX(num) (((num) + 1) & (TX_RING_SIZE - 1))
-#define PREV_TX(num) (((num) - 1) & (TX_RING_SIZE - 1))
-
-#define TX_BUFFS_AVAIL(head, tail) \
- ((head) <= (tail) ? \
- (head) + (TX_RING_SIZE - 1) - (tail) : \
- (head) - (tail) - 1)
-
-struct sendq {
- u32 tail;
- u32 head;
- u32 hdebug;
- u32 mdebug;
- struct myri_txd myri_txd[TX_RING_MAXSIZE];
-};
-
-struct recvq {
- u32 head;
- u32 tail;
- u32 hdebug;
- u32 mdebug;
- struct myri_rxd myri_rxd[RX_RING_MAXSIZE + 1];
-};
-
-#define MYRI_MLIST_SIZE 8
-
-struct mclist {
- u32 maxlen;
- u32 len;
- u32 cache;
- struct pair {
- u8 addr[8];
- u32 val;
- } mc_pairs[MYRI_MLIST_SIZE];
- u8 bcast_addr[8];
-};
-
-struct myri_channel {
- u32 state; /* State of the channel. */
- u32 busy; /* Channel is busy. */
- struct sendq sendq; /* Device tx queue. */
- struct recvq recvq; /* Device rx queue. */
- struct recvq recvqa; /* Device rx queue acked. */
- u32 rbytes; /* Receive bytes. */
- u32 sbytes; /* Send bytes. */
- u32 rmsgs; /* Receive messages. */
- u32 smsgs; /* Send messages. */
- struct mclist mclist; /* Device multicast list. */
-};
-
-/* Values for per-channel state. */
-#define STATE_WFH 0 /* Waiting for HOST. */
-#define STATE_WFN 1 /* Waiting for NET. */
-#define STATE_READY 2 /* Ready. */
-
-struct myri_shmem {
- u8 addr[8]; /* Board's address. */
- u32 nchan; /* Number of channels. */
- u32 burst; /* SBUS dma burst enable. */
- u32 shakedown; /* DarkkkkStarrr Crashesss... */
- u32 send; /* Send wanted. */
- u32 imask; /* Interrupt enable mask. */
- u32 mlevel; /* Map level. */
- u32 debug[4]; /* Misc. debug areas. */
- struct myri_channel channel; /* Only one channel on a host. */
-};
-
-struct myri_eth {
- /* These are frequently accessed, keep together
- * to obtain good cache hit rates.
- */
- spinlock_t irq_lock;
- struct myri_shmem __iomem *shmem; /* Shared data structures. */
- void __iomem *cregs; /* Control register space. */
- struct recvq __iomem *rqack; /* Where we ack rx's. */
- struct recvq __iomem *rq; /* Where we put buffers. */
- struct sendq __iomem *sq; /* Where we stuff tx's. */
- struct net_device *dev; /* Linux/NET dev struct. */
- int tx_old; /* To speed up tx cleaning. */
- void __iomem *lregs; /* Quick ptr to LANAI regs. */
- struct sk_buff *rx_skbs[RX_RING_SIZE+1];/* RX skb's */
- struct sk_buff *tx_skbs[TX_RING_SIZE]; /* TX skb's */
-
- /* These are less frequently accessed. */
- void __iomem *regs; /* MyriCOM register space. */
- void __iomem *lanai; /* View 2 of register space. */
- unsigned int myri_bursts; /* SBUS bursts. */
- struct myri_eeprom eeprom; /* Local copy of EEPROM. */
- unsigned int reg_size; /* Size of register space. */
- unsigned int shmem_base; /* Offset to shared ram. */
- struct platform_device *myri_op; /* Our OF device struct. */
-};
-
-/* We use this to acquire receive skb's that we can DMA directly into. */
-#define ALIGNED_RX_SKB_ADDR(addr) \
- ((((unsigned long)(addr) + (64 - 1)) & ~(64 - 1)) - (unsigned long)(addr))
-static inline struct sk_buff *myri_alloc_skb(unsigned int length, gfp_t gfp_flags)
-{
- struct sk_buff *skb;
-
- skb = alloc_skb(length + 64, gfp_flags);
- if(skb) {
- int offset = ALIGNED_RX_SKB_ADDR(skb->data);
-
- if(offset)
- skb_reserve(skb, offset);
- }
- return skb;
-}
-
-#endif /* !(_MYRI_SBUS_H) */
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index dfc82720065a..e8882023576b 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -307,6 +307,11 @@ static ssize_t store_enabled(struct netconsole_target *nt,
return err;
if (enabled < 0 || enabled > 1)
return -EINVAL;
+ if (enabled == nt->enabled) {
+ printk(KERN_INFO "netconsole: network logging has already %s\n",
+ nt->enabled ? "started" : "stopped");
+ return -EINVAL;
+ }
if (enabled) { /* 1 */
@@ -799,5 +804,11 @@ static void __exit cleanup_netconsole(void)
}
}
-module_init(init_netconsole);
+/*
+ * Use late_initcall to ensure netconsole is
+ * initialized after network device driver if built-in.
+ *
+ * late_initcall() and module_init() are identical if built as module.
+ */
+late_initcall(init_netconsole);
module_exit(cleanup_netconsole);
diff --git a/drivers/net/octeon/Makefile b/drivers/net/octeon/Makefile
deleted file mode 100644
index 906edecacfd3..000000000000
--- a/drivers/net/octeon/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-
-obj-$(CONFIG_OCTEON_MGMT_ETHERNET) += octeon_mgmt.o
diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c
deleted file mode 100644
index c0f23376a462..000000000000
--- a/drivers/net/pci-skeleton.c
+++ /dev/null
@@ -1,1923 +0,0 @@
-/*
-
- drivers/net/pci-skeleton.c
-
- Maintained by Jeff Garzik <jgarzik@pobox.com>
-
- Original code came from 8139too.c, which in turns was based
- originally on Donald Becker's rtl8139.c driver, versions 1.11
- and older. This driver was originally based on rtl8139.c
- version 1.07. Header of rtl8139.c version 1.11:
-
- -----<snip>-----
-
- Written 1997-2000 by Donald Becker.
- This software may be used and distributed according to the
- terms of the GNU General Public License (GPL), incorporated
- herein by reference. Drivers based on or derived from this
- code fall under the GPL and must retain the authorship,
- copyright and license notice. This file is not a complete
- program and may only be used when the entire operating
- system is licensed under the GPL.
-
- This driver is for boards based on the RTL8129 and RTL8139
- PCI ethernet chips.
-
- The author may be reached as becker@scyld.com, or C/O Scyld
- Computing Corporation 410 Severn Ave., Suite 210 Annapolis
- MD 21403
-
- Support and updates available at
- http://www.scyld.com/network/rtl8139.html
-
- Twister-tuning table provided by Kinston
- <shangh@realtek.com.tw>.
-
- -----<snip>-----
-
- This software may be used and distributed according to the terms
- of the GNU General Public License, incorporated herein by reference.
-
-
------------------------------------------------------------------------------
-
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the RealTek RTL8139 series, the RealTek
-Fast Ethernet controllers for PCI and CardBus. This chip is used on many
-low-end boards, sometimes with its markings changed.
-
-
-II. Board-specific settings
-
-PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board. The system BIOS will assign the
-PCI INTA signal to a (preferably otherwise unused) system IRQ line.
-
-III. Driver operation
-
-IIIa. Rx Ring buffers
-
-The receive unit uses a single linear ring buffer rather than the more
-common (and more efficient) descriptor-based architecture. Incoming frames
-are sequentially stored into the Rx region, and the host copies them into
-skbuffs.
-
-Comment: While it is theoretically possible to process many frames in place,
-any delay in Rx processing would cause us to drop frames. More importantly,
-the Linux protocol stack is not designed to operate in this manner.
-
-IIIb. Tx operation
-
-The RTL8139 uses a fixed set of four Tx descriptors in register space.
-In a stunningly bad design choice, Tx frames must be 32 bit aligned. Linux
-aligns the IP header on word boundaries, and 14 byte ethernet header means
-that almost all frames will need to be copied to an alignment buffer.
-
-IVb. References
-
-http://www.realtek.com.tw/
-http://www.scyld.com/expert/NWay.html
-
-IVc. Errata
-
-*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/delay.h>
-#include <linux/ethtool.h>
-#include <linux/mii.h>
-#include <linux/crc32.h>
-#include <linux/io.h>
-
-#define NETDRV_VERSION "1.0.1"
-#define MODNAME "netdrv"
-#define NETDRV_DRIVER_LOAD_MSG "MyVendor Fast Ethernet driver " NETDRV_VERSION " loaded"
-
-static char version[] __devinitdata =
- KERN_INFO NETDRV_DRIVER_LOAD_MSG "\n"
- " Support available from http://foo.com/bar/baz.html\n";
-
-/* define to 1 to enable PIO instead of MMIO */
-#undef USE_IO_OPS
-
-/* define to 1 to enable copious debugging info */
-#undef NETDRV_DEBUG
-
-/* define to 1 to disable lightweight runtime debugging checks */
-#undef NETDRV_NDEBUG
-
-
-#ifdef NETDRV_DEBUG
-/* note: prints function name for you */
-#define DPRINTK(fmt, args...) \
- printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
-#else
-#define DPRINTK(fmt, args...) \
-do { \
- if (0) \
- printk(KERN_DEBUG fmt, ##args); \
-} while (0)
-#endif
-
-#ifdef NETDRV_NDEBUG
-#define assert(expr) do {} while (0)
-#else
-#define assert(expr) \
- if (!(expr)) { \
- printk("Assertion failed! %s,%s,%s,line=%d\n", \
- #expr, __FILE__, __func__, __LINE__); \
- }
-#endif
-
-
-/* A few user-configurable values. */
-/* media options */
-static int media[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
-
-/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
- The RTL chips use a 64 element hash table based on the Ethernet CRC. */
-static int multicast_filter_limit = 32;
-
-/* Size of the in-memory receive ring. */
-#define RX_BUF_LEN_IDX 2 /* 0==8K, 1==16K, 2==32K, 3==64K */
-#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
-#define RX_BUF_PAD 16
-#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
-#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
-
-/* Number of Tx descriptor registers. */
-#define NUM_TX_DESC 4
-
-/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/
-#define MAX_ETH_FRAME_SIZE 1536
-
-/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
-#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE
-#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC)
-
-/* PCI Tuning Parameters
- Threshold is bytes transferred to chip before transmission starts. */
-#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
-
-/* The following settings are log_2(bytes)-4:
- 0==16 bytes 1==32 2==64 3==128 4==256 5==512 6==1024 7==end of packet.
-*/
-#define RX_FIFO_THRESH 6 /* Rx buffer level before first PCI xfer. */
-#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
-#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
-
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (6 * HZ)
-
-enum {
- HAS_CHIP_XCVR = 0x020000,
- HAS_LNK_CHNG = 0x040000,
-};
-
-#define NETDRV_MIN_IO_SIZE 0x80
-#define RTL8139B_IO_SIZE 256
-
-#define NETDRV_CAPS (HAS_CHIP_XCVR | HAS_LNK_CHNG)
-
-typedef enum {
- RTL8139 = 0,
- NETDRV_CB,
- SMC1211TX,
- /*MPX5030,*/
- DELTA8139,
- ADDTRON8139,
-} board_t;
-
-
-/* indexed by board_t, above */
-static struct {
- const char *name;
-} board_info[] __devinitdata = {
- { "RealTek RTL8139 Fast Ethernet" },
- { "RealTek RTL8139B PCI/CardBus" },
- { "SMC1211TX EZCard 10/100 (RealTek RTL8139)" },
-/* { MPX5030, "Accton MPX5030 (RealTek RTL8139)" },*/
- { "Delta Electronics 8139 10/100BaseTX" },
- { "Addtron Technology 8139 10/100BaseTX" },
-};
-
-
-static DEFINE_PCI_DEVICE_TABLE(netdrv_pci_tbl) = {
- {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NETDRV_CB },
- {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMC1211TX },
-/* {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },*/
- {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DELTA8139 },
- {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADDTRON8139 },
- {0,}
-};
-MODULE_DEVICE_TABLE(pci, netdrv_pci_tbl);
-
-
-/* The rest of these values should never change. */
-
-/* Symbolic offsets to registers. */
-enum NETDRV_registers {
- MAC0 = 0, /* Ethernet hardware address. */
- MAR0 = 8, /* Multicast filter. */
- TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */
- TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */
- RxBuf = 0x30,
- RxEarlyCnt = 0x34,
- RxEarlyStatus = 0x36,
- ChipCmd = 0x37,
- RxBufPtr = 0x38,
- RxBufAddr = 0x3A,
- IntrMask = 0x3C,
- IntrStatus = 0x3E,
- TxConfig = 0x40,
- ChipVersion = 0x43,
- RxConfig = 0x44,
- Timer = 0x48, /* A general-purpose counter. */
- RxMissed = 0x4C, /* 24 bits valid, write clears. */
- Cfg9346 = 0x50,
- Config0 = 0x51,
- Config1 = 0x52,
- FlashReg = 0x54,
- MediaStatus = 0x58,
- Config3 = 0x59,
- Config4 = 0x5A, /* absent on RTL-8139A */
- HltClk = 0x5B,
- MultiIntr = 0x5C,
- TxSummary = 0x60,
- BasicModeCtrl = 0x62,
- BasicModeStatus = 0x64,
- NWayAdvert = 0x66,
- NWayLPAR = 0x68,
- NWayExpansion = 0x6A,
- /* Undocumented registers, but required for proper operation. */
- FIFOTMS = 0x70, /* FIFO Control and test. */
- CSCR = 0x74, /* Chip Status and Configuration Register. */
- PARA78 = 0x78,
- PARA7c = 0x7c, /* Magic transceiver parameter register. */
- Config5 = 0xD8, /* absent on RTL-8139A */
-};
-
-enum ClearBitMasks {
- MultiIntrClear = 0xF000,
- ChipCmdClear = 0xE2,
- Config1Clear = (1 << 7) | (1 << 6) | (1 << 3) | (1 << 2) | (1 << 1),
-};
-
-enum ChipCmdBits {
- CmdReset = 0x10,
- CmdRxEnb = 0x08,
- CmdTxEnb = 0x04,
- RxBufEmpty = 0x01,
-};
-
-/* Interrupt register bits, using my own meaningful names. */
-enum IntrStatusBits {
- PCIErr = 0x8000,
- PCSTimeout = 0x4000,
- RxFIFOOver = 0x40,
- RxUnderrun = 0x20,
- RxOverflow = 0x10,
- TxErr = 0x08,
- TxOK = 0x04,
- RxErr = 0x02,
- RxOK = 0x01,
-};
-enum TxStatusBits {
- TxHostOwns = 0x2000,
- TxUnderrun = 0x4000,
- TxStatOK = 0x8000,
- TxOutOfWindow = 0x20000000,
- TxAborted = 0x40000000,
- TxCarrierLost = 0x80000000,
-};
-enum RxStatusBits {
- RxMulticast = 0x8000,
- RxPhysical = 0x4000,
- RxBroadcast = 0x2000,
- RxBadSymbol = 0x0020,
- RxRunt = 0x0010,
- RxTooLong = 0x0008,
- RxCRCErr = 0x0004,
- RxBadAlign = 0x0002,
- RxStatusOK = 0x0001,
-};
-
-/* Bits in RxConfig. */
-enum rx_mode_bits {
- AcceptErr = 0x20,
- AcceptRunt = 0x10,
- AcceptBroadcast = 0x08,
- AcceptMulticast = 0x04,
- AcceptMyPhys = 0x02,
- AcceptAllPhys = 0x01,
-};
-
-/* Bits in TxConfig. */
-enum tx_config_bits {
- TxIFG1 = (1 << 25), /* Interframe Gap Time */
- TxIFG0 = (1 << 24), /* Enabling these bits violates IEEE 802.3 */
- TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
- TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */
- TxClearAbt = (1 << 0), /* Clear abort (WO) */
- TxDMAShift = 8, /* DMA burst value(0-7) is shift this many bits */
-
- TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
-};
-
-/* Bits in Config1 */
-enum Config1Bits {
- Cfg1_PM_Enable = 0x01,
- Cfg1_VPD_Enable = 0x02,
- Cfg1_PIO = 0x04,
- Cfg1_MMIO = 0x08,
- Cfg1_LWAKE = 0x10,
- Cfg1_Driver_Load = 0x20,
- Cfg1_LED0 = 0x40,
- Cfg1_LED1 = 0x80,
-};
-
-enum RxConfigBits {
- /* Early Rx threshold, none or X/16 */
- RxCfgEarlyRxNone = 0,
- RxCfgEarlyRxShift = 24,
-
- /* rx fifo threshold */
- RxCfgFIFOShift = 13,
- RxCfgFIFONone = (7 << RxCfgFIFOShift),
-
- /* Max DMA burst */
- RxCfgDMAShift = 8,
- RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
-
- /* rx ring buffer length */
- RxCfgRcv8K = 0,
- RxCfgRcv16K = (1 << 11),
- RxCfgRcv32K = (1 << 12),
- RxCfgRcv64K = (1 << 11) | (1 << 12),
-
- /* Disable packet wrap at end of Rx buffer */
- RxNoWrap = (1 << 7),
-};
-
-
-/* Twister tuning parameters from RealTek.
- Completely undocumented, but required to tune bad links. */
-enum CSCRBits {
- CSCR_LinkOKBit = 0x0400,
- CSCR_LinkChangeBit = 0x0800,
- CSCR_LinkStatusBits = 0x0f000,
- CSCR_LinkDownOffCmd = 0x003c0,
- CSCR_LinkDownCmd = 0x0f3c0,
-};
-
-
-enum Cfg9346Bits {
- Cfg9346_Lock = 0x00,
- Cfg9346_Unlock = 0xC0,
-};
-
-
-#define PARA78_default 0x78fa8388
-#define PARA7c_default 0xcb38de43 /* param[0][3] */
-#define PARA7c_xxx 0xcb38de43
-static const unsigned long param[4][4] = {
- {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
- {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
- {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
- {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
-};
-
-struct ring_info {
- struct sk_buff *skb;
- dma_addr_t mapping;
-};
-
-
-typedef enum {
- CH_8139 = 0,
- CH_8139_K,
- CH_8139A,
- CH_8139B,
- CH_8130,
- CH_8139C,
-} chip_t;
-
-
-/* directly indexed by chip_t, above */
-static const struct {
- const char *name;
- u8 version; /* from RTL8139C docs */
- u32 RxConfigMask; /* should clear the bits supported by this chip */
-} rtl_chip_info[] = {
- { "RTL-8139",
- 0x40,
- 0xf0fe0040, /* XXX copied from RTL8139A, verify */
- },
-
- { "RTL-8139 rev K",
- 0x60,
- 0xf0fe0040,
- },
-
- { "RTL-8139A",
- 0x70,
- 0xf0fe0040,
- },
-
- { "RTL-8139B",
- 0x78,
- 0xf0fc0040
- },
-
- { "RTL-8130",
- 0x7C,
- 0xf0fe0040, /* XXX copied from RTL8139A, verify */
- },
-
- { "RTL-8139C",
- 0x74,
- 0xf0fc0040, /* XXX copied from RTL8139B, verify */
- },
-
-};
-
-
-struct netdrv_private {
- board_t board;
- void *mmio_addr;
- int drv_flags;
- struct pci_dev *pci_dev;
- struct timer_list timer; /* Media selection timer. */
- unsigned char *rx_ring;
- unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */
- unsigned int tx_flag;
- atomic_t cur_tx;
- atomic_t dirty_tx;
- /* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct ring_info tx_info[NUM_TX_DESC];
- unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */
- unsigned char *tx_bufs; /* Tx bounce buffer region. */
- dma_addr_t rx_ring_dma;
- dma_addr_t tx_bufs_dma;
- char phys[4]; /* MII device addresses. */
- char twistie, twist_row, twist_col; /* Twister tune state. */
- unsigned int full_duplex:1; /* Full-duplex operation requested. */
- unsigned int duplex_lock:1;
- unsigned int default_port:4; /* Last dev->if_port value. */
- unsigned int media2:4; /* Secondary monitored media port. */
- unsigned int medialock:1; /* Don't sense media type. */
- unsigned int mediasense:1; /* Media sensing in progress. */
- spinlock_t lock;
- chip_t chipset;
-};
-
-MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>");
-MODULE_DESCRIPTION("Skeleton for a PCI Fast Ethernet driver");
-MODULE_LICENSE("GPL");
-module_param(multicast_filter_limit, int, 0);
-module_param(max_interrupt_work, int, 0);
-module_param_array(media, int, NULL, 0);
-MODULE_PARM_DESC(multicast_filter_limit,
- MODNAME " maximum number of filtered multicast addresses");
-MODULE_PARM_DESC(max_interrupt_work,
- MODNAME " maximum events handled per interrupt");
-MODULE_PARM_DESC(media,
- MODNAME " Bits 0-3: media type, bit 17: full duplex");
-
-static int read_eeprom(void *ioaddr, int location, int addr_len);
-static int netdrv_open(struct net_device *dev);
-static int mdio_read(struct net_device *dev, int phy_id, int location);
-static void mdio_write(struct net_device *dev, int phy_id, int location,
- int val);
-static void netdrv_timer(unsigned long data);
-static void netdrv_tx_timeout(struct net_device *dev);
-static void netdrv_init_ring(struct net_device *dev);
-static int netdrv_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static irqreturn_t netdrv_interrupt(int irq, void *dev_instance);
-static int netdrv_close(struct net_device *dev);
-static int netdrv_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void netdrv_set_rx_mode(struct net_device *dev);
-static void netdrv_hw_start(struct net_device *dev);
-
-
-#ifdef USE_IO_OPS
-
-#define NETDRV_R8(reg) inb(((unsigned long)ioaddr) + (reg))
-#define NETDRV_R16(reg) inw(((unsigned long)ioaddr) + (reg))
-#define NETDRV_R32(reg) ((unsigned long)inl(((unsigned long)ioaddr) + (reg)))
-#define NETDRV_W8(reg, val8) outb((val8), ((unsigned long)ioaddr) + (reg))
-#define NETDRV_W16(reg, val16) outw((val16), ((unsigned long)ioaddr) + (reg))
-#define NETDRV_W32(reg, val32) outl((val32), ((unsigned long)ioaddr) + (reg))
-#define NETDRV_W8_F NETDRV_W8
-#define NETDRV_W16_F NETDRV_W16
-#define NETDRV_W32_F NETDRV_W32
-#undef readb
-#undef readw
-#undef readl
-#undef writeb
-#undef writew
-#undef writel
-#define readb(addr) inb((unsigned long)(addr))
-#define readw(addr) inw((unsigned long)(addr))
-#define readl(addr) inl((unsigned long)(addr))
-#define writeb(val, addr) outb((val), (unsigned long)(addr))
-#define writew(val, addr) outw((val), (unsigned long)(addr))
-#define writel(val, addr) outl((val), (unsigned long)(addr))
-
-#else
-
-/* write MMIO register, with flush */
-/* Flush avoids rtl8139 bug w/ posted MMIO writes */
-#define NETDRV_W8_F(reg, val8) \
-do { \
- writeb((val8), ioaddr + (reg)); \
- readb(ioaddr + (reg)); \
-} while (0)
-#define NETDRV_W16_F(reg, val16) \
-do { \
- writew((val16), ioaddr + (reg)); \
- readw(ioaddr + (reg)); \
-} while (0)
-#define NETDRV_W32_F(reg, val32) \
-do { \
- writel((val32), ioaddr + (reg)); \
- readl(ioaddr + (reg)); \
-} while (0)
-
-
-#ifdef MMIO_FLUSH_AUDIT_COMPLETE
-
-/* write MMIO register */
-#define NETDRV_W8(reg, val8) writeb((val8), ioaddr + (reg))
-#define NETDRV_W16(reg, val16) writew((val16), ioaddr + (reg))
-#define NETDRV_W32(reg, val32) writel((val32), ioaddr + (reg))
-
-#else
-
-/* write MMIO register, then flush */
-#define NETDRV_W8 NETDRV_W8_F
-#define NETDRV_W16 NETDRV_W16_F
-#define NETDRV_W32 NETDRV_W32_F
-
-#endif /* MMIO_FLUSH_AUDIT_COMPLETE */
-
-/* read MMIO register */
-#define NETDRV_R8(reg) readb(ioaddr + (reg))
-#define NETDRV_R16(reg) readw(ioaddr + (reg))
-#define NETDRV_R32(reg) ((unsigned long) readl(ioaddr + (reg)))
-
-#endif /* USE_IO_OPS */
-
-
-static const u16 netdrv_intr_mask =
- PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
- TxErr | TxOK | RxErr | RxOK;
-
-static const unsigned int netdrv_rx_config =
- RxCfgEarlyRxNone | RxCfgRcv32K | RxNoWrap |
- (RX_FIFO_THRESH << RxCfgFIFOShift) |
- (RX_DMA_BURST << RxCfgDMAShift);
-
-
-static int __devinit netdrv_init_board(struct pci_dev *pdev,
- struct net_device **dev_out,
- void **ioaddr_out)
-{
- void *ioaddr = NULL;
- struct net_device *dev;
- struct netdrv_private *tp;
- int rc, i;
- u32 pio_start, pio_end, pio_flags, pio_len;
- unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
- u32 tmp;
-
- DPRINTK("ENTER\n");
-
- assert(pdev != NULL);
- assert(ioaddr_out != NULL);
-
- *ioaddr_out = NULL;
- *dev_out = NULL;
-
- /* dev zeroed in alloc_etherdev */
- dev = alloc_etherdev(sizeof(*tp));
- if (dev == NULL) {
- dev_err(&pdev->dev, "unable to alloc new ethernet\n");
- DPRINTK("EXIT, returning -ENOMEM\n");
- return -ENOMEM;
- }
- SET_NETDEV_DEV(dev, &pdev->dev);
- tp = netdev_priv(dev);
-
- /* enable device(incl. PCI PM wakeup), and bus-mastering */
- rc = pci_enable_device(pdev);
- if (rc)
- goto err_out;
-
- pio_start = pci_resource_start(pdev, 0);
- pio_end = pci_resource_end(pdev, 0);
- pio_flags = pci_resource_flags(pdev, 0);
- pio_len = pci_resource_len(pdev, 0);
-
- mmio_start = pci_resource_start(pdev, 1);
- mmio_end = pci_resource_end(pdev, 1);
- mmio_flags = pci_resource_flags(pdev, 1);
- mmio_len = pci_resource_len(pdev, 1);
-
- /* set this immediately, we need to know before
- * we talk to the chip directly */
- DPRINTK("PIO region size == %#02X\n", pio_len);
- DPRINTK("MMIO region size == %#02lX\n", mmio_len);
-
- /* make sure PCI base addr 0 is PIO */
- if (!(pio_flags & IORESOURCE_IO)) {
- dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n");
- rc = -ENODEV;
- goto err_out;
- }
-
- /* make sure PCI base addr 1 is MMIO */
- if (!(mmio_flags & IORESOURCE_MEM)) {
- dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n");
- rc = -ENODEV;
- goto err_out;
- }
-
- /* check for weird/broken PCI region reporting */
- if ((pio_len < NETDRV_MIN_IO_SIZE) ||
- (mmio_len < NETDRV_MIN_IO_SIZE)) {
- dev_err(&pdev->dev, "Invalid PCI region size(s), aborting\n");
- rc = -ENODEV;
- goto err_out;
- }
-
- rc = pci_request_regions(pdev, MODNAME);
- if (rc)
- goto err_out;
-
- pci_set_master(pdev);
-
-#ifdef USE_IO_OPS
- ioaddr = (void *)pio_start;
-#else
- /* ioremap MMIO region */
- ioaddr = ioremap(mmio_start, mmio_len);
- if (ioaddr == NULL) {
- dev_err(&pdev->dev, "cannot remap MMIO, aborting\n");
- rc = -EIO;
- goto err_out_free_res;
- }
-#endif /* USE_IO_OPS */
-
- /* Soft reset the chip. */
- NETDRV_W8(ChipCmd, (NETDRV_R8(ChipCmd) & ChipCmdClear) | CmdReset);
-
- /* Check that the chip has finished the reset. */
- for (i = 1000; i > 0; i--)
- if ((NETDRV_R8(ChipCmd) & CmdReset) == 0)
- break;
- else
- udelay(10);
-
- /* Bring the chip out of low-power mode. */
- /* <insert device-specific code here> */
-
-#ifndef USE_IO_OPS
- /* sanity checks -- ensure PIO and MMIO registers agree */
- assert(inb(pio_start+Config0) == readb(ioaddr+Config0));
- assert(inb(pio_start+Config1) == readb(ioaddr+Config1));
- assert(inb(pio_start+TxConfig) == readb(ioaddr+TxConfig));
- assert(inb(pio_start+RxConfig) == readb(ioaddr+RxConfig));
-#endif /* !USE_IO_OPS */
-
- /* identify chip attached to board */
- tmp = NETDRV_R8(ChipVersion);
- for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--)
- if (tmp == rtl_chip_info[i].version) {
- tp->chipset = i;
- goto match;
- }
-
- /* if unknown chip, assume array element #0, original RTL-8139 in this case */
- dev_printk(KERN_DEBUG, &pdev->dev,
- "unknown chip version, assuming RTL-8139\n");
- dev_printk(KERN_DEBUG, &pdev->dev, "TxConfig = %#lx\n",
- NETDRV_R32(TxConfig));
- tp->chipset = 0;
-
-match:
- DPRINTK("chipset id(%d) == index %d, '%s'\n",
- tmp, tp->chipset, rtl_chip_info[tp->chipset].name);
-
- rc = register_netdev(dev);
- if (rc)
- goto err_out_unmap;
-
- DPRINTK("EXIT, returning 0\n");
- *ioaddr_out = ioaddr;
- *dev_out = dev;
- return 0;
-
-err_out_unmap:
-#ifndef USE_IO_OPS
- iounmap(ioaddr);
-err_out_free_res:
-#endif
- pci_release_regions(pdev);
-err_out:
- free_netdev(dev);
- DPRINTK("EXIT, returning %d\n", rc);
- return rc;
-}
-
-static const struct net_device_ops netdrv_netdev_ops = {
- .ndo_open = netdrv_open,
- .ndo_stop = netdrv_close,
- .ndo_start_xmit = netdrv_start_xmit,
- .ndo_set_multicast_list = netdrv_set_rx_mode,
- .ndo_do_ioctl = netdrv_ioctl,
- .ndo_tx_timeout = netdrv_tx_timeout,
- .ndo_change_mtu = eth_change_mtu,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_set_mac_address = eth_mac_addr,
-};
-
-static int __devinit netdrv_init_one(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- struct net_device *dev = NULL;
- struct netdrv_private *tp;
- int i, addr_len, option;
- void *ioaddr = NULL;
- static int board_idx = -1;
-
-/* when built into the kernel, we only print version if device is found */
-#ifndef MODULE
- static int printed_version;
- if (!printed_version++)
- printk(version);
-#endif
-
- DPRINTK("ENTER\n");
-
- assert(pdev != NULL);
- assert(ent != NULL);
-
- board_idx++;
-
- i = netdrv_init_board(pdev, &dev, &ioaddr);
- if (i < 0) {
- DPRINTK("EXIT, returning %d\n", i);
- return i;
- }
-
- tp = netdev_priv(dev);
-
- assert(ioaddr != NULL);
- assert(dev != NULL);
- assert(tp != NULL);
-
- addr_len = read_eeprom(ioaddr, 0, 8) == 0x8129 ? 8 : 6;
- for (i = 0; i < 3; i++)
- ((u16 *)(dev->dev_addr))[i] =
- le16_to_cpu(read_eeprom(ioaddr, i + 7, addr_len));
-
- dev->netdev_ops = &netdrv_netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- dev->irq = pdev->irq;
- dev->base_addr = (unsigned long) ioaddr;
-
- /* netdev_priv()/tp zeroed and aligned in alloc_etherdev */
- tp = netdev_priv(dev);
-
- /* note: tp->chipset set in netdrv_init_board */
- tp->drv_flags = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
- PCI_COMMAND_MASTER | NETDRV_CAPS;
- tp->pci_dev = pdev;
- tp->board = ent->driver_data;
- tp->mmio_addr = ioaddr;
- spin_lock_init(&tp->lock);
-
- pci_set_drvdata(pdev, dev);
-
- tp->phys[0] = 32;
-
- netdev_info(dev, "%s at %#lx, %pM IRQ %d\n",
- board_info[ent->driver_data].name,
- dev->base_addr, dev->dev_addr, dev->irq);
-
- netdev_printk(KERN_DEBUG, dev, "Identified 8139 chip type '%s'\n",
- rtl_chip_info[tp->chipset].name);
-
- /* Put the chip into low-power mode. */
- NETDRV_W8_F(Cfg9346, Cfg9346_Unlock);
-
- /* The lower four bits are the media type. */
- option = (board_idx > 7) ? 0 : media[board_idx];
- if (option > 0) {
- tp->full_duplex = (option & 0x200) ? 1 : 0;
- tp->default_port = option & 15;
- if (tp->default_port)
- tp->medialock = 1;
- }
-
- if (tp->full_duplex) {
- netdev_info(dev, "Media type forced to Full Duplex\n");
- mdio_write(dev, tp->phys[0], MII_ADVERTISE, ADVERTISE_FULL);
- tp->duplex_lock = 1;
- }
-
- DPRINTK("EXIT - returning 0\n");
- return 0;
-}
-
-
-static void __devexit netdrv_remove_one(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct netdrv_private *np;
-
- DPRINTK("ENTER\n");
-
- assert(dev != NULL);
-
- np = netdev_priv(dev);
- assert(np != NULL);
-
- unregister_netdev(dev);
-
-#ifndef USE_IO_OPS
- iounmap(np->mmio_addr);
-#endif /* !USE_IO_OPS */
-
- pci_release_regions(pdev);
-
- free_netdev(dev);
-
- pci_set_drvdata(pdev, NULL);
-
- pci_disable_device(pdev);
-
- DPRINTK("EXIT\n");
-}
-
-
-/* Serial EEPROM section. */
-
-/* EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
-#define EE_CS 0x08 /* EEPROM chip select. */
-#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */
-#define EE_WRITE_0 0x00
-#define EE_WRITE_1 0x02
-#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
-#define EE_ENB (0x80 | EE_CS)
-
-/* Delay between EEPROM clock transitions.
- No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
-*/
-
-#define eeprom_delay() readl(ee_addr)
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD (5)
-#define EE_READ_CMD (6)
-#define EE_ERASE_CMD (7)
-
-static int __devinit read_eeprom(void *ioaddr, int location, int addr_len)
-{
- int i;
- unsigned retval = 0;
- void *ee_addr = ioaddr + Cfg9346;
- int read_cmd = location | (EE_READ_CMD << addr_len);
-
- DPRINTK("ENTER\n");
-
- writeb(EE_ENB & ~EE_CS, ee_addr);
- writeb(EE_ENB, ee_addr);
- eeprom_delay();
-
- /* Shift the read command bits out. */
- for (i = 4 + addr_len; i >= 0; i--) {
- int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
- writeb(EE_ENB | dataval, ee_addr);
- eeprom_delay();
- writeb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
- eeprom_delay();
- }
- writeb(EE_ENB, ee_addr);
- eeprom_delay();
-
- for (i = 16; i > 0; i--) {
- writeb(EE_ENB | EE_SHIFT_CLK, ee_addr);
- eeprom_delay();
- retval =
- (retval << 1) | ((readb(ee_addr) & EE_DATA_READ) ? 1 :
- 0);
- writeb(EE_ENB, ee_addr);
- eeprom_delay();
- }
-
- /* Terminate the EEPROM access. */
- writeb(~EE_CS, ee_addr);
- eeprom_delay();
-
- DPRINTK("EXIT - returning %d\n", retval);
- return retval;
-}
-
-/* MII serial management: mostly bogus for now. */
-/* Read and write the MII management registers using software-generated
- serial MDIO protocol.
- The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
- met by back-to-back PCI I/O cycles, but we insert a delay to avoid
- "overclocking" issues. */
-#define MDIO_DIR 0x80
-#define MDIO_DATA_OUT 0x04
-#define MDIO_DATA_IN 0x02
-#define MDIO_CLK 0x01
-#define MDIO_WRITE0 (MDIO_DIR)
-#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
-
-#define mdio_delay() readb(mdio_addr)
-
-
-static char mii_2_8139_map[8] = {
- BasicModeCtrl,
- BasicModeStatus,
- 0,
- 0,
- NWayAdvert,
- NWayLPAR,
- NWayExpansion,
- 0
-};
-
-
-/* Syncronize the MII management interface by shifting 32 one bits out. */
-static void mdio_sync(void *mdio_addr)
-{
- int i;
-
- DPRINTK("ENTER\n");
-
- for (i = 32; i >= 0; i--) {
- writeb(MDIO_WRITE1, mdio_addr);
- mdio_delay();
- writeb(MDIO_WRITE1 | MDIO_CLK, mdio_addr);
- mdio_delay();
- }
-
- DPRINTK("EXIT\n");
-}
-
-
-static int mdio_read(struct net_device *dev, int phy_id, int location)
-{
- struct netdrv_private *tp = netdev_priv(dev);
- void *mdio_addr = tp->mmio_addr + Config4;
- int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
- int retval = 0;
- int i;
-
- DPRINTK("ENTER\n");
-
- if (phy_id > 31) { /* Really a 8139. Use internal registers. */
- DPRINTK("EXIT after directly using 8139 internal regs\n");
- return location < 8 && mii_2_8139_map[location] ?
- readw(tp->mmio_addr + mii_2_8139_map[location]) : 0;
- }
- mdio_sync(mdio_addr);
- /* Shift the read command bits out. */
- for (i = 15; i >= 0; i--) {
- int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
-
- writeb(MDIO_DIR | dataval, mdio_addr);
- mdio_delay();
- writeb(MDIO_DIR | dataval | MDIO_CLK, mdio_addr);
- mdio_delay();
- }
-
- /* Read the two transition, 16 data, and wire-idle bits. */
- for (i = 19; i > 0; i--) {
- writeb(0, mdio_addr);
- mdio_delay();
- retval = ((retval << 1) | ((readb(mdio_addr) & MDIO_DATA_IN))
- ? 1 : 0);
- writeb(MDIO_CLK, mdio_addr);
- mdio_delay();
- }
-
- DPRINTK("EXIT, returning %d\n", (retval >> 1) & 0xffff);
- return (retval >> 1) & 0xffff;
-}
-
-
-static void mdio_write(struct net_device *dev, int phy_id, int location,
- int value)
-{
- struct netdrv_private *tp = netdev_priv(dev);
- void *mdio_addr = tp->mmio_addr + Config4;
- int mii_cmd =
- (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
- int i;
-
- DPRINTK("ENTER\n");
-
- if (phy_id > 31) { /* Really a 8139. Use internal registers. */
- if (location < 8 && mii_2_8139_map[location]) {
- writew(value,
- tp->mmio_addr + mii_2_8139_map[location]);
- readw(tp->mmio_addr + mii_2_8139_map[location]);
- }
- DPRINTK("EXIT after directly using 8139 internal regs\n");
- return;
- }
- mdio_sync(mdio_addr);
-
- /* Shift the command bits out. */
- for (i = 31; i >= 0; i--) {
- int dataval =
- (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
- writeb(dataval, mdio_addr);
- mdio_delay();
- writeb(dataval | MDIO_CLK, mdio_addr);
- mdio_delay();
- }
-
- /* Clear out extra bits. */
- for (i = 2; i > 0; i--) {
- writeb(0, mdio_addr);
- mdio_delay();
- writeb(MDIO_CLK, mdio_addr);
- mdio_delay();
- }
-
- DPRINTK("EXIT\n");
-}
-
-
-static int netdrv_open(struct net_device *dev)
-{
- struct netdrv_private *tp = netdev_priv(dev);
- int retval;
- void *ioaddr = tp->mmio_addr;
-
- DPRINTK("ENTER\n");
-
- retval = request_irq(dev->irq, netdrv_interrupt, IRQF_SHARED, dev->name, dev);
- if (retval) {
- DPRINTK("EXIT, returning %d\n", retval);
- return retval;
- }
-
- tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
- &tp->tx_bufs_dma);
- tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
- &tp->rx_ring_dma);
- if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
- free_irq(dev->irq, dev);
-
- if (tp->tx_bufs)
- pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
- tp->tx_bufs, tp->tx_bufs_dma);
- if (tp->rx_ring)
- pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
- tp->rx_ring, tp->rx_ring_dma);
-
- DPRINTK("EXIT, returning -ENOMEM\n");
- return -ENOMEM;
-
- }
-
- tp->full_duplex = tp->duplex_lock;
- tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
-
- netdrv_init_ring(dev);
- netdrv_hw_start(dev);
-
- netdev_dbg(dev, "ioaddr %#llx IRQ %d GP Pins %02x %s-duplex\n",
- (unsigned long long)pci_resource_start(tp->pci_dev, 1),
- dev->irq, NETDRV_R8(MediaStatus),
- tp->full_duplex ? "full" : "half");
-
- /* Set the timer to switch to check for link beat and perhaps switch
- to an alternate media type. */
- init_timer(&tp->timer);
- tp->timer.expires = jiffies + 3 * HZ;
- tp->timer.data = (unsigned long) dev;
- tp->timer.function = netdrv_timer;
- add_timer(&tp->timer);
-
- DPRINTK("EXIT, returning 0\n");
- return 0;
-}
-
-
-/* Start the hardware at open or resume. */
-static void netdrv_hw_start(struct net_device *dev)
-{
- struct netdrv_private *tp = netdev_priv(dev);
- void *ioaddr = tp->mmio_addr;
- u32 i;
-
- DPRINTK("ENTER\n");
-
- /* Soft reset the chip. */
- NETDRV_W8(ChipCmd, (NETDRV_R8(ChipCmd) & ChipCmdClear) | CmdReset);
- udelay(100);
-
- /* Check that the chip has finished the reset. */
- for (i = 1000; i > 0; i--)
- if ((NETDRV_R8(ChipCmd) & CmdReset) == 0)
- break;
-
- /* Restore our idea of the MAC address. */
- NETDRV_W32_F(MAC0 + 0, cpu_to_le32(*(u32 *)(dev->dev_addr + 0)));
- NETDRV_W32_F(MAC0 + 4, cpu_to_le32(*(u32 *)(dev->dev_addr + 4)));
-
- /* Must enable Tx/Rx before setting transfer thresholds! */
- NETDRV_W8_F(ChipCmd, (NETDRV_R8(ChipCmd) & ChipCmdClear) |
- CmdRxEnb | CmdTxEnb);
-
- i = netdrv_rx_config |
- (NETDRV_R32(RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask);
- NETDRV_W32_F(RxConfig, i);
-
- /* Check this value: the documentation for IFG contradicts ifself. */
- NETDRV_W32(TxConfig, (TX_DMA_BURST << TxDMAShift));
-
- /* unlock Config[01234] and BMCR register writes */
- NETDRV_W8_F(Cfg9346, Cfg9346_Unlock);
- udelay(10);
-
- tp->cur_rx = 0;
-
- /* Lock Config[01234] and BMCR register writes */
- NETDRV_W8_F(Cfg9346, Cfg9346_Lock);
- udelay(10);
-
- /* init Rx ring buffer DMA address */
- NETDRV_W32_F(RxBuf, tp->rx_ring_dma);
-
- /* init Tx buffer DMA addresses */
- for (i = 0; i < NUM_TX_DESC; i++)
- NETDRV_W32_F(TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs));
-
- NETDRV_W32_F(RxMissed, 0);
-
- netdrv_set_rx_mode(dev);
-
- /* no early-rx interrupts */
- NETDRV_W16(MultiIntr, NETDRV_R16(MultiIntr) & MultiIntrClear);
-
- /* make sure RxTx has started */
- NETDRV_W8_F(ChipCmd, (NETDRV_R8(ChipCmd) & ChipCmdClear) |
- CmdRxEnb | CmdTxEnb);
-
- /* Enable all known interrupts by setting the interrupt mask. */
- NETDRV_W16_F(IntrMask, netdrv_intr_mask);
-
- netif_start_queue(dev);
-
- DPRINTK("EXIT\n");
-}
-
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void netdrv_init_ring(struct net_device *dev)
-{
- struct netdrv_private *tp = netdev_priv(dev);
- int i;
-
- DPRINTK("ENTER\n");
-
- tp->cur_rx = 0;
- atomic_set(&tp->cur_tx, 0);
- atomic_set(&tp->dirty_tx, 0);
-
- for (i = 0; i < NUM_TX_DESC; i++) {
- tp->tx_info[i].skb = NULL;
- tp->tx_info[i].mapping = 0;
- tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE];
- }
-
- DPRINTK("EXIT\n");
-}
-
-
-static void netdrv_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *) data;
- struct netdrv_private *tp = netdev_priv(dev);
- void *ioaddr = tp->mmio_addr;
- int next_tick = 60 * HZ;
- int mii_lpa;
-
- mii_lpa = mdio_read(dev, tp->phys[0], MII_LPA);
-
- if (!tp->duplex_lock && mii_lpa != 0xffff) {
- int duplex = ((mii_lpa & LPA_100FULL) ||
- (mii_lpa & 0x01C0) == 0x0040);
- if (tp->full_duplex != duplex) {
- tp->full_duplex = duplex;
- netdev_info(dev, "Setting %s-duplex based on MII #%d link partner ability of %04x\n",
- tp->full_duplex ? "full" : "half",
- tp->phys[0], mii_lpa);
- NETDRV_W8(Cfg9346, Cfg9346_Unlock);
- NETDRV_W8(Config1, tp->full_duplex ? 0x60 : 0x20);
- NETDRV_W8(Cfg9346, Cfg9346_Lock);
- }
- }
-
- netdev_dbg(dev, "Media selection tick, Link partner %04x\n",
- NETDRV_R16(NWayLPAR));
- netdev_dbg(dev, "Other registers are IntMask %04x IntStatus %04x RxStatus %04lx\n",
- NETDRV_R16(IntrMask),
- NETDRV_R16(IntrStatus),
- NETDRV_R32(RxEarlyStatus));
- netdev_dbg(dev, "Chip config %02x %02x\n",
- NETDRV_R8(Config0), NETDRV_R8(Config1));
-
- tp->timer.expires = jiffies + next_tick;
- add_timer(&tp->timer);
-}
-
-
-static void netdrv_tx_clear(struct net_device *dev)
-{
- int i;
- struct netdrv_private *tp = netdev_priv(dev);
-
- atomic_set(&tp->cur_tx, 0);
- atomic_set(&tp->dirty_tx, 0);
-
- /* Dump the unsent Tx packets. */
- for (i = 0; i < NUM_TX_DESC; i++) {
- struct ring_info *rp = &tp->tx_info[i];
- if (rp->mapping != 0) {
- pci_unmap_single(tp->pci_dev, rp->mapping,
- rp->skb->len, PCI_DMA_TODEVICE);
- rp->mapping = 0;
- }
- if (rp->skb) {
- dev_kfree_skb(rp->skb);
- rp->skb = NULL;
- dev->stats.tx_dropped++;
- }
- }
-}
-
-
-static void netdrv_tx_timeout(struct net_device *dev)
-{
- struct netdrv_private *tp = netdev_priv(dev);
- void *ioaddr = tp->mmio_addr;
- int i;
- u8 tmp8;
- unsigned long flags;
-
- netdev_dbg(dev, "Transmit timeout, status %02x %04x media %02x\n",
- NETDRV_R8(ChipCmd),
- NETDRV_R16(IntrStatus),
- NETDRV_R8(MediaStatus));
-
- /* disable Tx ASAP, if not already */
- tmp8 = NETDRV_R8(ChipCmd);
- if (tmp8 & CmdTxEnb)
- NETDRV_W8(ChipCmd, tmp8 & ~CmdTxEnb);
-
- /* Disable interrupts by clearing the interrupt mask. */
- NETDRV_W16(IntrMask, 0x0000);
-
- /* Emit info to figure out what went wrong. */
- netdev_dbg(dev, "Tx queue start entry %d dirty entry %d\n",
- atomic_read(&tp->cur_tx),
- atomic_read(&tp->dirty_tx));
- for (i = 0; i < NUM_TX_DESC; i++)
- netdev_dbg(dev, "Tx descriptor %d is %08lx%s\n",
- i, NETDRV_R32(TxStatus0 + (i * 4)),
- i == atomic_read(&tp->dirty_tx) % NUM_TX_DESC ?
- "(queue head)" : "");
-
- /* Stop a shared interrupt from scavenging while we are. */
- spin_lock_irqsave(&tp->lock, flags);
-
- netdrv_tx_clear(dev);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
- /* ...and finally, reset everything */
- netdrv_hw_start(dev);
-
- netif_wake_queue(dev);
-}
-
-
-
-static int netdrv_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct netdrv_private *tp = netdev_priv(dev);
- void *ioaddr = tp->mmio_addr;
- int entry;
-
- /* Calculate the next Tx descriptor entry. */
- entry = atomic_read(&tp->cur_tx) % NUM_TX_DESC;
-
- assert(tp->tx_info[entry].skb == NULL);
- assert(tp->tx_info[entry].mapping == 0);
-
- tp->tx_info[entry].skb = skb;
- /* tp->tx_info[entry].mapping = 0; */
- skb_copy_from_linear_data(skb, tp->tx_buf[entry], skb->len);
-
- /* Note: the chip doesn't have auto-pad! */
- NETDRV_W32(TxStatus0 + (entry * sizeof(u32)),
- tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
-
- atomic_inc(&tp->cur_tx);
- if ((atomic_read(&tp->cur_tx) - atomic_read(&tp->dirty_tx)) >= NUM_TX_DESC)
- netif_stop_queue(dev);
-
- netdev_dbg(dev, "Queued Tx packet at %p size %u to slot %d\n",
- skb->data, skb->len, entry);
-
- return NETDEV_TX_OK;
-}
-
-
-static void netdrv_tx_interrupt(struct net_device *dev,
- struct netdrv_private *tp,
- void *ioaddr)
-{
- int cur_tx, dirty_tx, tx_left;
-
- assert(dev != NULL);
- assert(tp != NULL);
- assert(ioaddr != NULL);
-
- dirty_tx = atomic_read(&tp->dirty_tx);
-
- cur_tx = atomic_read(&tp->cur_tx);
- tx_left = cur_tx - dirty_tx;
- while (tx_left > 0) {
- int entry = dirty_tx % NUM_TX_DESC;
- int txstatus;
-
- txstatus = NETDRV_R32(TxStatus0 + (entry * sizeof(u32)));
-
- if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
- break; /* It still hasn't been Txed */
-
- /* Note: TxCarrierLost is always asserted at 100mbps. */
- if (txstatus & (TxOutOfWindow | TxAborted)) {
- /* There was an major error, log it. */
- netdev_dbg(dev, "Transmit error, Tx status %#08x\n",
- txstatus);
- dev->stats.tx_errors++;
- if (txstatus & TxAborted) {
- dev->stats.tx_aborted_errors++;
- NETDRV_W32(TxConfig, TxClearAbt | (TX_DMA_BURST << TxDMAShift));
- }
- if (txstatus & TxCarrierLost)
- dev->stats.tx_carrier_errors++;
- if (txstatus & TxOutOfWindow)
- dev->stats.tx_window_errors++;
- } else {
- if (txstatus & TxUnderrun) {
- /* Add 64 to the Tx FIFO threshold. */
- if (tp->tx_flag < 0x00300000)
- tp->tx_flag += 0x00020000;
- dev->stats.tx_fifo_errors++;
- }
- dev->stats.collisions += (txstatus >> 24) & 15;
- dev->stats.tx_bytes += txstatus & 0x7ff;
- dev->stats.tx_packets++;
- }
-
- /* Free the original skb. */
- if (tp->tx_info[entry].mapping != 0) {
- pci_unmap_single(tp->pci_dev,
- tp->tx_info[entry].mapping,
- tp->tx_info[entry].skb->len,
- PCI_DMA_TODEVICE);
- tp->tx_info[entry].mapping = 0;
- }
- dev_kfree_skb_irq(tp->tx_info[entry].skb);
- tp->tx_info[entry].skb = NULL;
- dirty_tx++;
- if (dirty_tx < 0) { /* handle signed int overflow */
- atomic_sub(cur_tx, &tp->cur_tx); /* XXX racy? */
- dirty_tx = cur_tx - tx_left + 1;
- }
- if (netif_queue_stopped(dev))
- netif_wake_queue(dev);
-
- cur_tx = atomic_read(&tp->cur_tx);
- tx_left = cur_tx - dirty_tx;
-
- }
-
-#ifndef NETDRV_NDEBUG
- if (atomic_read(&tp->cur_tx) - dirty_tx > NUM_TX_DESC) {
- netdev_err(dev, "Out-of-sync dirty pointer, %d vs. %d\n",
- dirty_tx, atomic_read(&tp->cur_tx));
- dirty_tx += NUM_TX_DESC;
- }
-#endif /* NETDRV_NDEBUG */
-
- atomic_set(&tp->dirty_tx, dirty_tx);
-}
-
-
-/* TODO: clean this up! Rx reset need not be this intensive */
-static void netdrv_rx_err(u32 rx_status, struct net_device *dev,
- struct netdrv_private *tp, void *ioaddr)
-{
- u8 tmp8;
- int tmp_work = 1000;
-
- netdev_dbg(dev, "Ethernet frame had errors, status %08x\n", rx_status);
- if (rx_status & RxTooLong)
- netdev_dbg(dev, "Oversized Ethernet frame, status %04x!\n",
- rx_status);
- /* A.C.: The chip hangs here. */
- dev->stats.rx_errors++;
- if (rx_status & (RxBadSymbol | RxBadAlign))
- dev->stats.rx_frame_errors++;
- if (rx_status & (RxRunt | RxTooLong))
- dev->stats.rx_length_errors++;
- if (rx_status & RxCRCErr)
- dev->stats.rx_crc_errors++;
- /* Reset the receiver, based on RealTek recommendation.(Bug?) */
- tp->cur_rx = 0;
-
- /* disable receive */
- tmp8 = NETDRV_R8(ChipCmd) & ChipCmdClear;
- NETDRV_W8_F(ChipCmd, tmp8 | CmdTxEnb);
-
- /* A.C.: Reset the multicast list. */
- netdrv_set_rx_mode(dev);
-
- /* XXX potentially temporary hack to
- * restart hung receiver */
- while (--tmp_work > 0) {
- tmp8 = NETDRV_R8(ChipCmd);
- if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))
- break;
- NETDRV_W8_F(ChipCmd,
- (tmp8 & ChipCmdClear) | CmdRxEnb | CmdTxEnb);
- }
-
- /* G.S.: Re-enable receiver */
- /* XXX temporary hack to work around receiver hang */
- netdrv_set_rx_mode(dev);
-
- if (tmp_work <= 0)
- netdev_warn(dev, "tx/rx enable wait too long\n");
-}
-
-
-/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
- field alignments and semantics. */
-static void netdrv_rx_interrupt(struct net_device *dev,
- struct netdrv_private *tp, void *ioaddr)
-{
- unsigned char *rx_ring;
- u16 cur_rx;
-
- assert(dev != NULL);
- assert(tp != NULL);
- assert(ioaddr != NULL);
-
- rx_ring = tp->rx_ring;
- cur_rx = tp->cur_rx;
-
- netdev_dbg(dev, "In netdrv_rx(), current %04x BufAddr %04x, free to %04x, Cmd %02x\n",
- cur_rx, NETDRV_R16(RxBufAddr),
- NETDRV_R16(RxBufPtr), NETDRV_R8(ChipCmd));
-
- while ((NETDRV_R8(ChipCmd) & RxBufEmpty) == 0) {
- int ring_offset = cur_rx % RX_BUF_LEN;
- u32 rx_status;
- unsigned int rx_size;
- unsigned int pkt_size;
- struct sk_buff *skb;
-
- /* read size+status of next frame from DMA ring buffer */
- rx_status = le32_to_cpu(*(u32 *)(rx_ring + ring_offset));
- rx_size = rx_status >> 16;
- pkt_size = rx_size - 4;
-
- netdev_dbg(dev, "netdrv_rx() status %04x, size %04x, cur %04x\n",
- rx_status, rx_size, cur_rx);
-#if defined(NETDRV_DEBUG) && (NETDRV_DEBUG > 2)
- print_hex_dump_bytes("Frame contents: ", HEX_DUMP_OFFSET,
- &rx_ring[ring_offset], 70);
-#endif
-
- /* If Rx err or invalid rx_size/rx_status received
- *(which happens if we get lost in the ring),
- * Rx process gets reset, so we abort any further
- * Rx processing.
- */
- if ((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||
- (!(rx_status & RxStatusOK))) {
- netdrv_rx_err(rx_status, dev, tp, ioaddr);
- return;
- }
-
- /* Malloc up new buffer, compatible with net-2e. */
- /* Omit the four octet CRC from the length. */
-
- /* TODO: consider allocating skb's outside of
- * interrupt context, both to speed interrupt processing,
- * and also to reduce the chances of having to
- * drop packets here under memory pressure.
- */
-
- skb = dev_alloc_skb(pkt_size + 2);
- if (skb) {
- skb_reserve(skb, 2); /* 16 byte align the IP fields. */
-
- skb_copy_to_linear_data(skb, &rx_ring[ring_offset + 4], pkt_size);
- skb_put(skb, pkt_size);
-
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_bytes += pkt_size;
- dev->stats.rx_packets++;
- } else {
- netdev_warn(dev, "Memory squeeze, dropping packet\n");
- dev->stats.rx_dropped++;
- }
-
- cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
- NETDRV_W16_F(RxBufPtr, cur_rx - 16);
- }
-
- netdev_dbg(dev, "Done netdrv_rx(), current %04x BufAddr %04x, free to %04x, Cmd %02x\n",
- cur_rx, NETDRV_R16(RxBufAddr),
- NETDRV_R16(RxBufPtr), NETDRV_R8(ChipCmd));
-
- tp->cur_rx = cur_rx;
-}
-
-
-static void netdrv_weird_interrupt(struct net_device *dev,
- struct netdrv_private *tp,
- void *ioaddr,
- int status, int link_changed)
-{
- netdev_printk(KERN_DEBUG, dev, "Abnormal interrupt, status %08x\n",
- status);
-
- assert(dev != NULL);
- assert(tp != NULL);
- assert(ioaddr != NULL);
-
- /* Update the error count. */
- dev->stats.rx_missed_errors += NETDRV_R32(RxMissed);
- NETDRV_W32(RxMissed, 0);
-
- if ((status & RxUnderrun) && link_changed &&
- (tp->drv_flags & HAS_LNK_CHNG)) {
- /* Really link-change on new chips. */
- int lpar = NETDRV_R16(NWayLPAR);
- int duplex = ((lpar & 0x0100) || (lpar & 0x01C0) == 0x0040 ||
- tp->duplex_lock);
- if (tp->full_duplex != duplex) {
- tp->full_duplex = duplex;
- NETDRV_W8(Cfg9346, Cfg9346_Unlock);
- NETDRV_W8(Config1, tp->full_duplex ? 0x60 : 0x20);
- NETDRV_W8(Cfg9346, Cfg9346_Lock);
- }
- status &= ~RxUnderrun;
- }
-
- /* XXX along with netdrv_rx_err, are we double-counting errors? */
- if (status & (RxUnderrun | RxOverflow | RxErr | RxFIFOOver))
- dev->stats.rx_errors++;
-
- if (status & (PCSTimeout))
- dev->stats.rx_length_errors++;
- if (status & (RxUnderrun | RxFIFOOver))
- dev->stats.rx_fifo_errors++;
- if (status & RxOverflow) {
- dev->stats.rx_over_errors++;
- tp->cur_rx = NETDRV_R16(RxBufAddr) % RX_BUF_LEN;
- NETDRV_W16_F(RxBufPtr, tp->cur_rx - 16);
- }
- if (status & PCIErr) {
- u16 pci_cmd_status;
- pci_read_config_word(tp->pci_dev, PCI_STATUS, &pci_cmd_status);
-
- netdev_err(dev, "PCI Bus error %04x\n", pci_cmd_status);
- }
-}
-
-
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
-static irqreturn_t netdrv_interrupt(int irq, void *dev_instance)
-{
- struct net_device *dev = (struct net_device *) dev_instance;
- struct netdrv_private *tp = netdev_priv(dev);
- int boguscnt = max_interrupt_work;
- void *ioaddr = tp->mmio_addr;
- int status = 0, link_changed = 0; /* avoid bogus "uninit" warning */
- int handled = 0;
-
- spin_lock(&tp->lock);
-
- do {
- status = NETDRV_R16(IntrStatus);
-
- /* h/w no longer present(hotplug?) or major error, bail */
- if (status == 0xFFFF)
- break;
-
- handled = 1;
- /* Acknowledge all of the current interrupt sources ASAP */
- NETDRV_W16_F(IntrStatus, status);
-
- netdev_dbg(dev, "interrupt status=%#04x new intstat=%#04x\n",
- status, NETDRV_R16(IntrStatus));
-
- if ((status &
- (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |
- RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0)
- break;
-
- /* Check uncommon events with one test. */
- if (status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |
- RxFIFOOver | TxErr | RxErr))
- netdrv_weird_interrupt(dev, tp, ioaddr,
- status, link_changed);
-
- if (status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOver)) /* Rx interrupt */
- netdrv_rx_interrupt(dev, tp, ioaddr);
-
- if (status & (TxOK | TxErr))
- netdrv_tx_interrupt(dev, tp, ioaddr);
-
- boguscnt--;
- } while (boguscnt > 0);
-
- if (boguscnt <= 0) {
- netdev_warn(dev, "Too much work at interrupt, IntrStatus=%#04x\n",
- status);
-
- /* Clear all interrupt sources. */
- NETDRV_W16(IntrStatus, 0xffff);
- }
-
- spin_unlock(&tp->lock);
-
- netdev_dbg(dev, "exiting interrupt, intr_status=%#04x\n",
- NETDRV_R16(IntrStatus));
- return IRQ_RETVAL(handled);
-}
-
-
-static int netdrv_close(struct net_device *dev)
-{
- struct netdrv_private *tp = netdev_priv(dev);
- void *ioaddr = tp->mmio_addr;
- unsigned long flags;
-
- DPRINTK("ENTER\n");
-
- netif_stop_queue(dev);
-
- netdev_dbg(dev, "Shutting down ethercard, status was %#04x\n",
- NETDRV_R16(IntrStatus));
-
- del_timer_sync(&tp->timer);
-
- spin_lock_irqsave(&tp->lock, flags);
-
- /* Stop the chip's Tx and Rx DMA processes. */
- NETDRV_W8(ChipCmd, (NETDRV_R8(ChipCmd) & ChipCmdClear));
-
- /* Disable interrupts by clearing the interrupt mask. */
- NETDRV_W16(IntrMask, 0x0000);
-
- /* Update the error counts. */
- dev->stats.rx_missed_errors += NETDRV_R32(RxMissed);
- NETDRV_W32(RxMissed, 0);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
- free_irq(dev->irq, dev);
-
- netdrv_tx_clear(dev);
-
- pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
- tp->rx_ring, tp->rx_ring_dma);
- pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
- tp->tx_bufs, tp->tx_bufs_dma);
- tp->rx_ring = NULL;
- tp->tx_bufs = NULL;
-
- /* Green! Put the chip in low-power mode. */
- NETDRV_W8(Cfg9346, Cfg9346_Unlock);
- NETDRV_W8(Config1, 0x03);
- NETDRV_W8(Cfg9346, Cfg9346_Lock);
-
- DPRINTK("EXIT\n");
- return 0;
-}
-
-
-static int netdrv_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct netdrv_private *tp = netdev_priv(dev);
- struct mii_ioctl_data *data = if_mii(rq);
- unsigned long flags;
- int rc = 0;
-
- DPRINTK("ENTER\n");
-
- switch (cmd) {
- case SIOCGMIIPHY: /* Get address of MII PHY in use. */
- data->phy_id = tp->phys[0] & 0x3f;
- /* Fall Through */
-
- case SIOCGMIIREG: /* Read MII PHY register. */
- spin_lock_irqsave(&tp->lock, flags);
- data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
- spin_unlock_irqrestore(&tp->lock, flags);
- break;
-
- case SIOCSMIIREG: /* Write MII PHY register. */
- spin_lock_irqsave(&tp->lock, flags);
- mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
- spin_unlock_irqrestore(&tp->lock, flags);
- break;
-
- default:
- rc = -EOPNOTSUPP;
- break;
- }
-
- DPRINTK("EXIT, returning %d\n", rc);
- return rc;
-}
-
-/* Set or clear the multicast filter for this adaptor.
- This routine is not state sensitive and need not be SMP locked. */
-
-static void netdrv_set_rx_mode(struct net_device *dev)
-{
- struct netdrv_private *tp = netdev_priv(dev);
- void *ioaddr = tp->mmio_addr;
- u32 mc_filter[2]; /* Multicast hash filter */
- int rx_mode;
- u32 tmp;
-
- DPRINTK("ENTER\n");
-
- netdev_dbg(dev, "%s(%04x) done -- Rx config %08lx\n",
- __func__, dev->flags, NETDRV_R32(RxConfig));
-
- /* Note: do not reorder, GCC is clever about common statements. */
- if (dev->flags & IFF_PROMISC) {
- rx_mode =
- AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
- AcceptAllPhys;
- mc_filter[1] = mc_filter[0] = 0xffffffff;
- } else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
- (dev->flags & IFF_ALLMULTI)) {
- /* Too many to filter perfectly -- accept all multicasts. */
- rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
- mc_filter[1] = mc_filter[0] = 0xffffffff;
- } else {
- struct netdev_hw_addr *ha;
-
- rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
- mc_filter[1] = mc_filter[0] = 0;
- netdev_for_each_mc_addr(ha, dev) {
- int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26;
-
- mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
- }
- }
-
- /* if called from irq handler, lock already acquired */
- if (!in_irq())
- spin_lock_irq(&tp->lock);
-
- /* We can safely update without stopping the chip. */
- tmp = netdrv_rx_config | rx_mode |
- (NETDRV_R32(RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask);
- NETDRV_W32_F(RxConfig, tmp);
- NETDRV_W32_F(MAR0 + 0, mc_filter[0]);
- NETDRV_W32_F(MAR0 + 4, mc_filter[1]);
-
- if (!in_irq())
- spin_unlock_irq(&tp->lock);
-
- DPRINTK("EXIT\n");
-}
-
-
-#ifdef CONFIG_PM
-
-static int netdrv_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct netdrv_private *tp = netdev_priv(dev);
- void *ioaddr = tp->mmio_addr;
- unsigned long flags;
-
- if (!netif_running(dev))
- return 0;
- netif_device_detach(dev);
-
- spin_lock_irqsave(&tp->lock, flags);
-
- /* Disable interrupts, stop Tx and Rx. */
- NETDRV_W16(IntrMask, 0x0000);
- NETDRV_W8(ChipCmd, (NETDRV_R8(ChipCmd) & ChipCmdClear));
-
- /* Update the error counts. */
- dev->stats.rx_missed_errors += NETDRV_R32(RxMissed);
- NETDRV_W32(RxMissed, 0);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
- pci_save_state(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
-
- return 0;
-}
-
-
-static int netdrv_resume(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- /*struct netdrv_private *tp = netdev_priv(dev);*/
-
- if (!netif_running(dev))
- return 0;
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
- netif_device_attach(dev);
- netdrv_hw_start(dev);
-
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
-
-static struct pci_driver netdrv_pci_driver = {
- .name = MODNAME,
- .id_table = netdrv_pci_tbl,
- .probe = netdrv_init_one,
- .remove = __devexit_p(netdrv_remove_one),
-#ifdef CONFIG_PM
- .suspend = netdrv_suspend,
- .resume = netdrv_resume,
-#endif /* CONFIG_PM */
-};
-
-
-static int __init netdrv_init_module(void)
-{
-/* when a module, this is printed whether or not devices are found in probe */
-#ifdef MODULE
- printk(version);
-#endif
- return pci_register_driver(&netdrv_pci_driver);
-}
-
-
-static void __exit netdrv_cleanup_module(void)
-{
- pci_unregister_driver(&netdrv_pci_driver);
-}
-
-
-module_init(netdrv_init_module);
-module_exit(netdrv_cleanup_module);
diff --git a/drivers/net/pcmcia/Kconfig b/drivers/net/pcmcia/Kconfig
deleted file mode 100644
index 9b8f793b1cc8..000000000000
--- a/drivers/net/pcmcia/Kconfig
+++ /dev/null
@@ -1,123 +0,0 @@
-#
-# PCMCIA Network device configuration
-#
-
-menuconfig NET_PCMCIA
- bool "PCMCIA network device support"
- depends on PCMCIA
- ---help---
- Say Y if you would like to include support for any PCMCIA or CardBus
- network adapters, then say Y to the driver for your particular card
- below. PCMCIA- or PC-cards are credit-card size devices often used
- with laptops computers; CardBus is the newer and faster version of
- PCMCIA.
-
- To use your PC-cards, you will need supporting software from David
- Hinds' pcmcia-cs package (see the file <file:Documentation/Changes>
- for location). You also want to check out the PCMCIA-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>.
-
- If unsure, say N.
-
-if NET_PCMCIA && PCMCIA
-
-config PCMCIA_3C589
- tristate "3Com 3c589 PCMCIA support"
- help
- Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA
- (PC-card) Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called 3c589_cs. If unsure, say N.
-
-config PCMCIA_3C574
- tristate "3Com 3c574 PCMCIA support"
- help
- Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA
- (PC-card) Fast Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called 3c574_cs. If unsure, say N.
-
-config PCMCIA_FMVJ18X
- tristate "Fujitsu FMV-J18x PCMCIA support"
- select CRC32
- help
- Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible
- PCMCIA (PC-card) Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called fmvj18x_cs. If unsure, say N.
-
-config PCMCIA_PCNET
- tristate "NE2000 compatible PCMCIA support"
- select CRC32
- help
- Say Y here if you intend to attach an NE2000 compatible PCMCIA
- (PC-card) Ethernet or Fast Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called pcnet_cs. If unsure, say N.
-
-config PCMCIA_NMCLAN
- tristate "New Media PCMCIA support"
- help
- Say Y here if you intend to attach a New Media Ethernet or LiveWire
- PCMCIA (PC-card) Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called nmclan_cs. If unsure, say N.
-
-config PCMCIA_SMC91C92
- tristate "SMC 91Cxx PCMCIA support"
- select CRC32
- select MII
- help
- Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
- (PC-card) Ethernet or Fast Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called smc91c92_cs. If unsure, say N.
-
-config PCMCIA_XIRC2PS
- tristate "Xircom 16-bit PCMCIA support"
- help
- Say Y here if you intend to attach a Xircom 16-bit PCMCIA (PC-card)
- Ethernet or Fast Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called xirc2ps_cs. If unsure, say N.
-
-config PCMCIA_AXNET
- tristate "Asix AX88190 PCMCIA support"
- ---help---
- Say Y here if you intend to attach an Asix AX88190-based PCMCIA
- (PC-card) Fast Ethernet card to your computer. These cards are
- nearly NE2000 compatible but need a separate driver due to a few
- misfeatures.
-
- To compile this driver as a module, choose M here: the module will be
- called axnet_cs. If unsure, say N.
-
-config ARCNET_COM20020_CS
- tristate "COM20020 ARCnet PCMCIA support"
- depends on ARCNET_COM20020
- help
- Say Y here if you intend to attach this type of ARCnet PCMCIA card
- to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called com20020_cs. If unsure, say N.
-
-config PCMCIA_IBMTR
- tristate "IBM PCMCIA tokenring adapter support"
- depends on IBMTR!=y && TR
- help
- Say Y here if you intend to attach this type of Token Ring PCMCIA
- card to your computer. You then also need to say Y to "Token Ring
- driver support".
-
- To compile this driver as a module, choose M here: the module will be
- called ibmtr_cs.
-
-endif # NET_PCMCIA
diff --git a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile
deleted file mode 100644
index 87d2d99f4c14..000000000000
--- a/drivers/net/pcmcia/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Makefile for the Linux PCMCIA network device drivers.
-#
-
-# 16-bit client drivers
-obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
-obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
-obj-$(CONFIG_PCMCIA_FMVJ18X) += fmvj18x_cs.o
-obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o
-obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o
-obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o
-obj-$(CONFIG_PCMCIA_XIRC2PS) += xirc2ps_cs.o
-obj-$(CONFIG_ARCNET_COM20020_CS)+= com20020_cs.o
-obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o
-
-obj-$(CONFIG_PCMCIA_IBMTR) += ibmtr_cs.o
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index a70244306c94..bb88e12101c7 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -3,7 +3,7 @@
#
menuconfig PHYLIB
- tristate "PHY Device support and infrastructure"
+ bool "PHY Device support and infrastructure"
depends on !S390
depends on NETDEVICES
help
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index d84c4224dd12..e8be47d6d7d0 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -553,7 +553,7 @@ static int bcm5481_config_aneg(struct phy_device *phydev)
/*
* There is no BCM5481 specification available, so down
* here is everything we know about "register 0x18". This
- * at least helps BCM5481 to successfuly receive packets
+ * at least helps BCM5481 to successfully receive packets
* on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
* says: "This sets delay between the RXD and RXC signals
* instead of using trace lengths to achieve timing".
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 2cd8dc5847b4..9663e0ba6003 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -34,18 +34,16 @@
#define PAGESEL 0x13
#define LAYER4 0x02
#define LAYER2 0x01
-#define MAX_RXTS 4
-#define MAX_TXTS 4
-#define N_EXT_TS 1
+#define MAX_RXTS 64
+#define N_EXT_TS 6
#define PSF_PTPVER 2
#define PSF_EVNT 0x4000
#define PSF_RX 0x2000
#define PSF_TX 0x1000
#define EXT_EVENT 1
-#define EXT_GPIO 1
-#define CAL_EVENT 2
-#define CAL_GPIO 9
-#define CAL_TRIGGER 2
+#define CAL_EVENT 7
+#define CAL_TRIGGER 7
+#define PER_TRIGGER 6
/* phyter seems to miss the mark by 16 ns */
#define ADJTIME_FIX 16
@@ -132,16 +130,30 @@ struct dp83640_clock {
/* globals */
+enum {
+ CALIBRATE_GPIO,
+ PEROUT_GPIO,
+ EXTTS0_GPIO,
+ EXTTS1_GPIO,
+ EXTTS2_GPIO,
+ EXTTS3_GPIO,
+ EXTTS4_GPIO,
+ EXTTS5_GPIO,
+ GPIO_TABLE_SIZE
+};
+
static int chosen_phy = -1;
-static ushort cal_gpio = 4;
+static ushort gpio_tab[GPIO_TABLE_SIZE] = {
+ 1, 2, 3, 4, 8, 9, 10, 11
+};
module_param(chosen_phy, int, 0444);
-module_param(cal_gpio, ushort, 0444);
+module_param_array(gpio_tab, ushort, NULL, 0444);
MODULE_PARM_DESC(chosen_phy, \
"The address of the PHY to use for the ancillary clock features");
-MODULE_PARM_DESC(cal_gpio, \
- "Which GPIO line to use for synchronizing multiple PHYs");
+MODULE_PARM_DESC(gpio_tab, \
+ "Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6");
/* a list of clocks and a mutex to protect it */
static LIST_HEAD(phyter_clocks);
@@ -218,7 +230,7 @@ static void phy2rxts(struct phy_rxts *p, struct rxts *rxts)
rxts->seqid = p->seqid;
rxts->msgtype = (p->msgtype >> 12) & 0xf;
rxts->hash = p->msgtype & 0x0fff;
- rxts->tmo = jiffies + HZ;
+ rxts->tmo = jiffies + 2;
}
static u64 phy2txts(struct phy_txts *p)
@@ -236,6 +248,61 @@ static u64 phy2txts(struct phy_txts *p)
return ns;
}
+static void periodic_output(struct dp83640_clock *clock,
+ struct ptp_clock_request *clkreq, bool on)
+{
+ struct dp83640_private *dp83640 = clock->chosen;
+ struct phy_device *phydev = dp83640->phydev;
+ u32 sec, nsec, period;
+ u16 gpio, ptp_trig, trigger, val;
+
+ gpio = on ? gpio_tab[PEROUT_GPIO] : 0;
+ trigger = PER_TRIGGER;
+
+ ptp_trig = TRIG_WR |
+ (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT |
+ (gpio & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT |
+ TRIG_PER |
+ TRIG_PULSE;
+
+ val = (trigger & TRIG_SEL_MASK) << TRIG_SEL_SHIFT;
+
+ if (!on) {
+ val |= TRIG_DIS;
+ mutex_lock(&clock->extreg_lock);
+ ext_write(0, phydev, PAGE5, PTP_TRIG, ptp_trig);
+ ext_write(0, phydev, PAGE4, PTP_CTL, val);
+ mutex_unlock(&clock->extreg_lock);
+ return;
+ }
+
+ sec = clkreq->perout.start.sec;
+ nsec = clkreq->perout.start.nsec;
+ period = clkreq->perout.period.sec * 1000000000UL;
+ period += clkreq->perout.period.nsec;
+
+ mutex_lock(&clock->extreg_lock);
+
+ ext_write(0, phydev, PAGE5, PTP_TRIG, ptp_trig);
+
+ /*load trigger*/
+ val |= TRIG_LOAD;
+ ext_write(0, phydev, PAGE4, PTP_CTL, val);
+ ext_write(0, phydev, PAGE4, PTP_TDR, nsec & 0xffff); /* ns[15:0] */
+ ext_write(0, phydev, PAGE4, PTP_TDR, nsec >> 16); /* ns[31:16] */
+ ext_write(0, phydev, PAGE4, PTP_TDR, sec & 0xffff); /* sec[15:0] */
+ ext_write(0, phydev, PAGE4, PTP_TDR, sec >> 16); /* sec[31:16] */
+ ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff); /* ns[15:0] */
+ ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16); /* ns[31:16] */
+
+ /*enable trigger*/
+ val &= ~TRIG_LOAD;
+ val |= TRIG_EN;
+ ext_write(0, phydev, PAGE4, PTP_CTL, val);
+
+ mutex_unlock(&clock->extreg_lock);
+}
+
/* ptp clock methods */
static int ptp_dp83640_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
@@ -339,19 +406,30 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
struct dp83640_clock *clock =
container_of(ptp, struct dp83640_clock, caps);
struct phy_device *phydev = clock->chosen->phydev;
- u16 evnt;
+ int index;
+ u16 evnt, event_num, gpio_num;
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
- if (rq->extts.index != 0)
+ index = rq->extts.index;
+ if (index < 0 || index >= N_EXT_TS)
return -EINVAL;
- evnt = EVNT_WR | (EXT_EVENT & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
+ event_num = EXT_EVENT + index;
+ evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) {
- evnt |= (EXT_GPIO & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+ gpio_num = gpio_tab[EXTTS0_GPIO + index];
+ evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
evnt |= EVNT_RISE;
}
ext_write(0, phydev, PAGE5, PTP_EVNT, evnt);
return 0;
+
+ case PTP_CLK_REQ_PEROUT:
+ if (rq->perout.index != 0)
+ return -EINVAL;
+ periodic_output(clock, rq, on);
+ return 0;
+
default:
break;
}
@@ -442,9 +520,10 @@ static void recalibrate(struct dp83640_clock *clock)
struct list_head *this;
struct dp83640_private *tmp;
struct phy_device *master = clock->chosen->phydev;
- u16 cfg0, evnt, ptp_trig, trigger, val;
+ u16 cal_gpio, cfg0, evnt, ptp_trig, trigger, val;
trigger = CAL_TRIGGER;
+ cal_gpio = gpio_tab[CALIBRATE_GPIO];
mutex_lock(&clock->extreg_lock);
@@ -543,11 +622,17 @@ static void recalibrate(struct dp83640_clock *clock)
/* time stamping methods */
+static inline u16 exts_chan_to_edata(int ch)
+{
+ return 1 << ((ch + EXT_EVENT) * 2);
+}
+
static int decode_evnt(struct dp83640_private *dp83640,
void *data, u16 ests)
{
struct phy_txts *phy_txts;
struct ptp_clock_event event;
+ int i, parsed;
int words = (ests >> EVNT_TS_LEN_SHIFT) & EVNT_TS_LEN_MASK;
u16 ext_status = 0;
@@ -569,14 +654,25 @@ static int decode_evnt(struct dp83640_private *dp83640,
dp83640->edata.ns_lo = phy_txts->ns_lo;
}
+ if (ext_status) {
+ parsed = words + 2;
+ } else {
+ parsed = words + 1;
+ i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK) - EXT_EVENT;
+ ext_status = exts_chan_to_edata(i);
+ }
+
event.type = PTP_CLOCK_EXTTS;
- event.index = 0;
event.timestamp = phy2txts(&dp83640->edata);
- ptp_clock_event(dp83640->clock->ptp_clock, &event);
+ for (i = 0; i < N_EXT_TS; i++) {
+ if (ext_status & exts_chan_to_edata(i)) {
+ event.index = i;
+ ptp_clock_event(dp83640->clock->ptp_clock, &event);
+ }
+ }
- words = ext_status ? words + 2 : words + 1;
- return words * sizeof(u16);
+ return parsed * sizeof(u16);
}
static void decode_rxts(struct dp83640_private *dp83640,
@@ -590,7 +686,7 @@ static void decode_rxts(struct dp83640_private *dp83640,
prune_rx_ts(dp83640);
if (list_empty(&dp83640->rxpool)) {
- pr_warning("dp83640: rx timestamp pool is empty\n");
+ pr_debug("dp83640: rx timestamp pool is empty\n");
goto out;
}
rxts = list_first_entry(&dp83640->rxpool, struct rxts, list);
@@ -613,7 +709,7 @@ static void decode_txts(struct dp83640_private *dp83640,
skb = skb_dequeue(&dp83640->tx_queue);
if (!skb) {
- pr_warning("dp83640: have timestamp but tx_queue empty\n");
+ pr_debug("dp83640: have timestamp but tx_queue empty\n");
return;
}
ns = phy2txts(phy_txts);
@@ -665,6 +761,41 @@ static void decode_status_frame(struct dp83640_private *dp83640,
}
}
+static int is_sync(struct sk_buff *skb, int type)
+{
+ u8 *data = skb->data, *msgtype;
+ unsigned int offset = 0;
+
+ switch (type) {
+ case PTP_CLASS_V1_IPV4:
+ case PTP_CLASS_V2_IPV4:
+ offset = ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
+ break;
+ case PTP_CLASS_V1_IPV6:
+ case PTP_CLASS_V2_IPV6:
+ offset = OFF_PTP6;
+ break;
+ case PTP_CLASS_V2_L2:
+ offset = ETH_HLEN;
+ break;
+ case PTP_CLASS_V2_VLAN:
+ offset = ETH_HLEN + VLAN_HLEN;
+ break;
+ default:
+ return 0;
+ }
+
+ if (type & PTP_CLASS_V1)
+ offset += OFF_PTP_CONTROL;
+
+ if (skb->len < offset + 1)
+ return 0;
+
+ msgtype = data + offset;
+
+ return (*msgtype & 0xf) == 0;
+}
+
static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts)
{
u16 *seqid;
@@ -741,7 +872,7 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
clock->caps.max_adj = 1953124;
clock->caps.n_alarm = 0;
clock->caps.n_ext_ts = N_EXT_TS;
- clock->caps.n_per_out = 0;
+ clock->caps.n_per_out = 1;
clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime;
@@ -876,6 +1007,7 @@ static void dp83640_remove(struct phy_device *phydev)
struct dp83640_clock *clock;
struct list_head *this, *next;
struct dp83640_private *tmp, *dp83640 = phydev->priv;
+ struct sk_buff *skb;
if (phydev->addr == BROADCAST_ADDR)
return;
@@ -883,6 +1015,12 @@ static void dp83640_remove(struct phy_device *phydev)
enable_status_frames(phydev, false);
cancel_work_sync(&dp83640->ts_work);
+ while ((skb = skb_dequeue(&dp83640->rx_queue)) != NULL)
+ kfree_skb(skb);
+
+ while ((skb = skb_dequeue(&dp83640->tx_queue)) != NULL)
+ skb_complete_tx_timestamp(skb, NULL);
+
clock = dp83640_clock_get(dp83640->clock);
if (dp83640 == clock->chosen) {
@@ -914,16 +1052,10 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
if (cfg.flags) /* reserved for future extensions */
return -EINVAL;
- switch (cfg.tx_type) {
- case HWTSTAMP_TX_OFF:
- dp83640->hwts_tx_en = 0;
- break;
- case HWTSTAMP_TX_ON:
- dp83640->hwts_tx_en = 1;
- break;
- default:
+ if (cfg.tx_type < 0 || cfg.tx_type > HWTSTAMP_TX_ONESTEP_SYNC)
return -ERANGE;
- }
+
+ dp83640->hwts_tx_en = cfg.tx_type;
switch (cfg.rx_filter) {
case HWTSTAMP_FILTER_NONE:
@@ -978,6 +1110,9 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
if (dp83640->hwts_tx_en)
txcfg0 |= TX_TS_EN;
+ if (dp83640->hwts_tx_en == HWTSTAMP_TX_ONESTEP_SYNC)
+ txcfg0 |= SYNC_1STEP | CHK_1STEP;
+
if (dp83640->hwts_rx_en)
rxcfg0 |= RX_TS_EN;
@@ -1060,12 +1195,24 @@ static void dp83640_txtstamp(struct phy_device *phydev,
{
struct dp83640_private *dp83640 = phydev->priv;
- if (!dp83640->hwts_tx_en) {
- kfree_skb(skb);
- return;
+ switch (dp83640->hwts_tx_en) {
+
+ case HWTSTAMP_TX_ONESTEP_SYNC:
+ if (is_sync(skb, type)) {
+ skb_complete_tx_timestamp(skb, NULL);
+ return;
+ }
+ /* fall through */
+ case HWTSTAMP_TX_ON:
+ skb_queue_tail(&dp83640->tx_queue, skb);
+ schedule_work(&dp83640->ts_work);
+ break;
+
+ case HWTSTAMP_TX_OFF:
+ default:
+ skb_complete_tx_timestamp(skb, NULL);
+ break;
}
- skb_queue_tail(&dp83640->tx_queue, skb);
- schedule_work(&dp83640->ts_work);
}
static struct phy_driver dp83640_driver = {
diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c
index 9a09e24c30bc..c81f136ae670 100644
--- a/drivers/net/phy/icplus.c
+++ b/drivers/net/phy/icplus.c
@@ -30,10 +30,17 @@
#include <asm/irq.h>
#include <asm/uaccess.h>
-MODULE_DESCRIPTION("ICPlus IP175C/IC1001 PHY drivers");
+MODULE_DESCRIPTION("ICPlus IP175C/IP101A/IC1001 PHY drivers");
MODULE_AUTHOR("Michael Barkowski");
MODULE_LICENSE("GPL");
+/* IP101A/IP1001 */
+#define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */
+#define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */
+#define IP1001_PHASE_SEL_MASK 3 /* IP1001 RX/TXPHASE_SEL */
+#define IP1001_APS_ON 11 /* IP1001 APS Mode bit */
+#define IP101A_APS_ON 2 /* IP101A APS Mode bit */
+
static int ip175c_config_init(struct phy_device *phydev)
{
int err, i;
@@ -42,36 +49,36 @@ static int ip175c_config_init(struct phy_device *phydev)
if (full_reset_performed == 0) {
/* master reset */
- err = phydev->bus->write(phydev->bus, 30, 0, 0x175c);
+ err = mdiobus_write(phydev->bus, 30, 0, 0x175c);
if (err < 0)
return err;
/* ensure no bus delays overlap reset period */
- err = phydev->bus->read(phydev->bus, 30, 0);
+ err = mdiobus_read(phydev->bus, 30, 0);
/* data sheet specifies reset period is 2 msec */
mdelay(2);
/* enable IP175C mode */
- err = phydev->bus->write(phydev->bus, 29, 31, 0x175c);
+ err = mdiobus_write(phydev->bus, 29, 31, 0x175c);
if (err < 0)
return err;
/* Set MII0 speed and duplex (in PHY mode) */
- err = phydev->bus->write(phydev->bus, 29, 22, 0x420);
+ err = mdiobus_write(phydev->bus, 29, 22, 0x420);
if (err < 0)
return err;
/* reset switch ports */
for (i = 0; i < 5; i++) {
- err = phydev->bus->write(phydev->bus, i,
- MII_BMCR, BMCR_RESET);
+ err = mdiobus_write(phydev->bus, i,
+ MII_BMCR, BMCR_RESET);
if (err < 0)
return err;
}
for (i = 0; i < 5; i++)
- err = phydev->bus->read(phydev->bus, i, MII_BMCR);
+ err = mdiobus_read(phydev->bus, i, MII_BMCR);
mdelay(2);
@@ -89,31 +96,61 @@ static int ip175c_config_init(struct phy_device *phydev)
return 0;
}
-static int ip1001_config_init(struct phy_device *phydev)
+static int ip1xx_reset(struct phy_device *phydev)
{
- int err, value;
+ int err, bmcr;
/* Software Reset PHY */
- value = phy_read(phydev, MII_BMCR);
- value |= BMCR_RESET;
- err = phy_write(phydev, MII_BMCR, value);
+ bmcr = phy_read(phydev, MII_BMCR);
+ bmcr |= BMCR_RESET;
+ err = phy_write(phydev, MII_BMCR, bmcr);
if (err < 0)
return err;
do {
- value = phy_read(phydev, MII_BMCR);
- } while (value & BMCR_RESET);
+ bmcr = phy_read(phydev, MII_BMCR);
+ } while (bmcr & BMCR_RESET);
- /* Additional delay (2ns) used to adjust RX clock phase
- * at GMII/ RGMII interface */
- value = phy_read(phydev, 16);
- value |= 0x3;
+ return err;
+}
- err = phy_write(phydev, 16, value);
- if (err < 0)
- return err;
+static int ip1001_config_init(struct phy_device *phydev)
+{
+ int c;
+
+ c = ip1xx_reset(phydev);
+ if (c < 0)
+ return c;
+
+ /* Enable Auto Power Saving mode */
+ c = phy_read(phydev, IP1001_SPEC_CTRL_STATUS_2);
+ c |= IP1001_APS_ON;
+ if (c < 0)
+ return c;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
+ /* Additional delay (2ns) used to adjust RX clock phase
+ * at RGMII interface */
+ c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS);
+ c |= IP1001_PHASE_SEL_MASK;
+ c = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c);
+ }
- return err;
+ return c;
+}
+
+static int ip101a_config_init(struct phy_device *phydev)
+{
+ int c;
+
+ c = ip1xx_reset(phydev);
+ if (c < 0)
+ return c;
+
+ /* Enable Auto Power Saving mode */
+ c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS);
+ c |= IP101A_APS_ON;
+ return c;
}
static int ip175c_read_status(struct phy_device *phydev)
@@ -162,6 +199,20 @@ static struct phy_driver ip1001_driver = {
.driver = { .owner = THIS_MODULE,},
};
+static struct phy_driver ip101a_driver = {
+ .phy_id = 0x02430c54,
+ .name = "ICPlus IP101A",
+ .phy_id_mask = 0x0ffffff0,
+ .features = PHY_BASIC_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause,
+ .config_init = &ip101a_config_init,
+ .config_aneg = &genphy_config_aneg,
+ .read_status = &genphy_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .driver = { .owner = THIS_MODULE,},
+};
+
static int __init icplus_init(void)
{
int ret = 0;
@@ -170,12 +221,17 @@ static int __init icplus_init(void)
if (ret < 0)
return -ENODEV;
+ ret = phy_driver_register(&ip101a_driver);
+ if (ret < 0)
+ return -ENODEV;
+
return phy_driver_register(&ip175c_driver);
}
static void __exit icplus_exit(void)
{
phy_driver_unregister(&ip1001_driver);
+ phy_driver_unregister(&ip101a_driver);
phy_driver_unregister(&ip175c_driver);
}
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index 47c8339a0359..2843c90f712f 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -241,7 +241,7 @@ MODULE_DEVICE_TABLE(of, mdio_ofgpio_match);
static struct platform_driver mdio_ofgpio_driver = {
.driver = {
- .name = "mdio-gpio",
+ .name = "mdio-ofgpio",
.owner = THIS_MODULE,
.of_match_table = mdio_ofgpio_match,
},
diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c
index 0620ba963508..04bb8fcc0cb5 100644
--- a/drivers/net/phy/national.c
+++ b/drivers/net/phy/national.c
@@ -25,8 +25,9 @@
/* DP83865 phy identifier values */
#define DP83865_PHY_ID 0x20005c7a
-#define DP83865_INT_MASK_REG 0x15
-#define DP83865_INT_MASK_STATUS 0x14
+#define DP83865_INT_STATUS 0x14
+#define DP83865_INT_MASK 0x15
+#define DP83865_INT_CLEAR 0x17
#define DP83865_INT_REMOTE_FAULT 0x0008
#define DP83865_INT_ANE_COMPLETED 0x0010
@@ -68,21 +69,25 @@ static int ns_config_intr(struct phy_device *phydev)
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
- err = phy_write(phydev, DP83865_INT_MASK_REG,
+ err = phy_write(phydev, DP83865_INT_MASK,
DP83865_INT_MASK_DEFAULT);
else
- err = phy_write(phydev, DP83865_INT_MASK_REG, 0);
+ err = phy_write(phydev, DP83865_INT_MASK, 0);
return err;
}
static int ns_ack_interrupt(struct phy_device *phydev)
{
- int ret = phy_read(phydev, DP83865_INT_MASK_STATUS);
+ int ret = phy_read(phydev, DP83865_INT_STATUS);
if (ret < 0)
return ret;
- return 0;
+ /* Clear the interrupt status bit by writing a “1â€
+ * to the corresponding bit in INT_CLEAR (2:0 are reserved) */
+ ret = phy_write(phydev, DP83865_INT_CLEAR, ret & ~0x7);
+
+ return ret;
}
static void ns_giga_speed_fallback(struct phy_device *phydev, int mode)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index a47595760751..3cbda0851f83 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -33,7 +33,7 @@
#include <linux/timer.h>
#include <linux/workqueue.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index ff109fe5af6b..83a5a5afec67 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -213,7 +213,7 @@ int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id)
/* Grab the bits from PHYIR1, and put them
* in the upper half */
- phy_reg = bus->read(bus, addr, MII_PHYSID1);
+ phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
if (phy_reg < 0)
return -EIO;
@@ -221,7 +221,7 @@ int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id)
*phy_id = (phy_reg & 0xffff) << 16;
/* Grab the bits from PHYIR2, and put them in the lower half */
- phy_reg = bus->read(bus, addr, MII_PHYSID2);
+ phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
if (phy_reg < 0)
return -EIO;
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
index a4eae750a414..f414ffb5b728 100644
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -14,6 +14,7 @@
*
*/
#include <linux/phy.h>
+#include <linux/module.h>
#define RTL821x_PHYSR 0x11
#define RTL821x_PHYSR_DUPLEX 0x2000
diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c
index 5d8f6e17bd55..0ec8e09cc2ac 100644
--- a/drivers/net/phy/vitesse.c
+++ b/drivers/net/phy/vitesse.c
@@ -3,7 +3,7 @@
*
* Author: Kriston Carson
*
- * Copyright (c) 2005 Freescale Semiconductor, Inc.
+ * Copyright (c) 2005, 2009 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -61,32 +61,42 @@ MODULE_DESCRIPTION("Vitesse PHY driver");
MODULE_AUTHOR("Kriston Carson");
MODULE_LICENSE("GPL");
-static int vsc824x_config_init(struct phy_device *phydev)
+int vsc824x_add_skew(struct phy_device *phydev)
{
- int extcon;
int err;
-
- err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
- MII_VSC8244_AUXCONSTAT_INIT);
- if (err < 0)
- return err;
+ int extcon;
extcon = phy_read(phydev, MII_VSC8244_EXT_CON1);
if (extcon < 0)
- return err;
+ return extcon;
extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK |
MII_VSC8244_EXTCON1_RX_SKEW_MASK);
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
- extcon |= (MII_VSC8244_EXTCON1_TX_SKEW |
- MII_VSC8244_EXTCON1_RX_SKEW);
+ extcon |= (MII_VSC8244_EXTCON1_TX_SKEW |
+ MII_VSC8244_EXTCON1_RX_SKEW);
err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon);
return err;
}
+EXPORT_SYMBOL(vsc824x_add_skew);
+
+static int vsc824x_config_init(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
+ MII_VSC8244_AUXCONSTAT_INIT);
+ if (err < 0)
+ return err;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ err = vsc824x_add_skew(phydev);
+
+ return err;
+}
static int vsc824x_ack_interrupt(struct phy_device *phydev)
{
diff --git a/drivers/net/plip/Kconfig b/drivers/net/plip/Kconfig
new file mode 100644
index 000000000000..80c4a3373e51
--- /dev/null
+++ b/drivers/net/plip/Kconfig
@@ -0,0 +1,38 @@
+#
+# Parallel Line Internet Protocol (PLIP) network device configuration
+#
+
+config PLIP
+ tristate "PLIP (parallel port) support"
+ depends on PARPORT
+ ---help---
+ PLIP (Parallel Line Internet Protocol) is used to create a
+ reasonably fast mini network consisting of two (or, rarely, more)
+ local machines. A PLIP link from a Linux box is a popular means to
+ install a Linux distribution on a machine which doesn't have a
+ CD-ROM drive (a minimal system has to be transferred with floppies
+ first). The kernels on both machines need to have this PLIP option
+ enabled for this to work.
+
+ The PLIP driver has two modes, mode 0 and mode 1. The parallel
+ ports (the connectors at the computers with 25 holes) are connected
+ with "null printer" or "Turbo Laplink" cables which can transmit 4
+ bits at a time (mode 0) or with special PLIP cables, to be used on
+ bidirectional parallel ports only, which can transmit 8 bits at a
+ time (mode 1); you can find the wiring of these cables in
+ <file:Documentation/networking/PLIP.txt>. The cables can be up to
+ 15m long. Mode 0 works also if one of the machines runs DOS/Windows
+ and has some PLIP software installed, e.g. the Crynwr PLIP packet
+ driver (<http://oak.oakland.edu/simtel.net/msdos/pktdrvr-pre.html>)
+ and winsock or NCSA's telnet.
+
+ If you want to use PLIP, say Y and read the PLIP mini-HOWTO as well
+ as the NET-3-HOWTO, both available from
+ <http://www.tldp.org/docs.html#howto>. Note that the PLIP
+ protocol has been changed and this PLIP driver won't work together
+ with the PLIP support in Linux versions 1.0.x. This option enlarges
+ your kernel by about 8 KB.
+
+ To compile this driver as a module, choose M here. The module
+ will be called plip. If unsure, say Y or M, in case you buy
+ a laptop later.
diff --git a/drivers/net/plip/Makefile b/drivers/net/plip/Makefile
new file mode 100644
index 000000000000..ed958796dc64
--- /dev/null
+++ b/drivers/net/plip/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the PLIP network device drivers.
+#
+
+obj-$(CONFIG_PLIP) += plip.o
diff --git a/drivers/net/plip.c b/drivers/net/plip/plip.c
index ca4df7f4cf21..a9e9ca8a86ed 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip/plip.c
@@ -152,7 +152,7 @@ static int plip_hard_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *daddr,
const void *saddr, unsigned len);
static int plip_hard_header_cache(const struct neighbour *neigh,
- struct hh_cache *hh);
+ struct hh_cache *hh, __be16 type);
static int plip_open(struct net_device *dev);
static int plip_close(struct net_device *dev);
static int plip_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
@@ -1026,11 +1026,11 @@ plip_hard_header(struct sk_buff *skb, struct net_device *dev,
}
static int plip_hard_header_cache(const struct neighbour *neigh,
- struct hh_cache *hh)
+ struct hh_cache *hh, __be16 type)
{
int ret;
- ret = eth_header_cache(neigh, hh);
+ ret = eth_header_cache(neigh, hh, type);
if (ret == 0) {
struct ethhdr *eth;
diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig
new file mode 100644
index 000000000000..872df3ef07a6
--- /dev/null
+++ b/drivers/net/ppp/Kconfig
@@ -0,0 +1,175 @@
+#
+# PPP network device configuration
+#
+
+config PPP
+ tristate "PPP (point-to-point protocol) support"
+ select SLHC
+ ---help---
+ PPP (Point to Point Protocol) is a newer and better SLIP. It serves
+ the same purpose: sending Internet traffic over telephone (and other
+ serial) lines. Ask your access provider if they support it, because
+ otherwise you can't use it; most Internet access providers these
+ days support PPP rather than SLIP.
+
+ To use PPP, you need an additional program called pppd as described
+ in the PPP-HOWTO, available at
+ <http://www.tldp.org/docs.html#howto>. Make sure that you have
+ the version of pppd recommended in <file:Documentation/Changes>.
+ The PPP option enlarges your kernel by about 16 KB.
+
+ There are actually two versions of PPP: the traditional PPP for
+ asynchronous lines, such as regular analog phone lines, and
+ synchronous PPP which can be used over digital ISDN lines for
+ example. If you want to use PPP over phone lines or other
+ asynchronous serial lines, you need to say Y (or M) here and also to
+ the next option, "PPP support for async serial ports". For PPP over
+ synchronous lines, you should say Y (or M) here and to "Support
+ synchronous PPP", below.
+
+ If you said Y to "Version information on all symbols" above, then
+ you cannot compile the PPP driver into the kernel; you can then only
+ compile it as a module. To compile this driver as a module, choose M
+ here. The module will be called ppp_generic.
+
+if PPP
+
+config PPP_BSDCOMP
+ tristate "PPP BSD-Compress compression"
+ depends on PPP
+ ---help---
+ Support for the BSD-Compress compression method for PPP, which uses
+ the LZW compression method to compress each PPP packet before it is
+ sent over the wire. The machine at the other end of the PPP link
+ (usually your ISP) has to support the BSD-Compress compression
+ method as well for this to be useful. Even if they don't support it,
+ it is safe to say Y here.
+
+ The PPP Deflate compression method ("PPP Deflate compression",
+ above) is preferable to BSD-Compress, because it compresses better
+ and is patent-free.
+
+ Note that the BSD compression code will always be compiled as a
+ module; it is called bsd_comp and will show up in the directory
+ modules once you have said "make modules". If unsure, say N.
+
+config PPP_DEFLATE
+ tristate "PPP Deflate compression"
+ depends on PPP
+ select ZLIB_INFLATE
+ select ZLIB_DEFLATE
+ ---help---
+ Support for the Deflate compression method for PPP, which uses the
+ Deflate algorithm (the same algorithm that gzip uses) to compress
+ each PPP packet before it is sent over the wire. The machine at the
+ other end of the PPP link (usually your ISP) has to support the
+ Deflate compression method as well for this to be useful. Even if
+ they don't support it, it is safe to say Y here.
+
+ To compile this driver as a module, choose M here.
+
+config PPP_FILTER
+ bool "PPP filtering"
+ depends on PPP
+ ---help---
+ Say Y here if you want to be able to filter the packets passing over
+ PPP interfaces. This allows you to control which packets count as
+ activity (i.e. which packets will reset the idle timer or bring up
+ a demand-dialed link) and which packets are to be dropped entirely.
+ You need to say Y here if you wish to use the pass-filter and
+ active-filter options to pppd.
+
+ If unsure, say N.
+
+config PPP_MPPE
+ tristate "PPP MPPE compression (encryption) (EXPERIMENTAL)"
+ depends on PPP && EXPERIMENTAL
+ select CRYPTO
+ select CRYPTO_SHA1
+ select CRYPTO_ARC4
+ select CRYPTO_ECB
+ ---help---
+ Support for the MPPE Encryption protocol, as employed by the
+ Microsoft Point-to-Point Tunneling Protocol.
+
+ See http://pptpclient.sourceforge.net/ for information on
+ configuring PPTP clients and servers to utilize this method.
+
+config PPP_MULTILINK
+ bool "PPP multilink support (EXPERIMENTAL)"
+ depends on PPP && EXPERIMENTAL
+ ---help---
+ PPP multilink is a protocol (defined in RFC 1990) which allows you
+ to combine several (logical or physical) lines into one logical PPP
+ connection, so that you can utilize your full bandwidth.
+
+ This has to be supported at the other end as well and you need a
+ version of the pppd daemon which understands the multilink protocol.
+
+ If unsure, say N.
+
+config PPPOATM
+ tristate "PPP over ATM"
+ depends on ATM && PPP
+ ---help---
+ Support PPP (Point to Point Protocol) encapsulated in ATM frames.
+ This implementation does not yet comply with section 8 of RFC2364,
+ which can lead to bad results if the ATM peer loses state and
+ changes its encapsulation unilaterally.
+
+config PPPOE
+ tristate "PPP over Ethernet (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && PPP
+ ---help---
+ Support for PPP over Ethernet.
+
+ This driver requires the latest version of pppd from the CVS
+ repository at cvs.samba.org. Alternatively, see the
+ RoaringPenguin package (<http://www.roaringpenguin.com/pppoe>)
+ which contains instruction on how to use this driver (under
+ the heading "Kernel mode PPPoE").
+
+config PPTP
+ tristate "PPP over IPv4 (PPTP) (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && PPP && NET_IPGRE_DEMUX
+ ---help---
+ Support for PPP over IPv4.(Point-to-Point Tunneling Protocol)
+
+ This driver requires pppd plugin to work in client mode or
+ modified pptpd (poptop) to work in server mode.
+ See http://accel-pptp.sourceforge.net/ for information how to
+ utilize this module.
+
+config PPPOL2TP
+ tristate "PPP over L2TP (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && L2TP && PPP
+ ---help---
+ Support for PPP-over-L2TP socket family. L2TP is a protocol
+ used by ISPs and enterprises to tunnel PPP traffic over UDP
+ tunnels. L2TP is replacing PPTP for VPN uses.
+
+config PPP_ASYNC
+ tristate "PPP support for async serial ports"
+ depends on PPP
+ select CRC_CCITT
+ ---help---
+ Say Y (or M) here if you want to be able to use PPP over standard
+ asynchronous serial ports, such as COM1 or COM2 on a PC. If you use
+ a modem (not a synchronous or ISDN modem) to contact your ISP, you
+ need this option.
+
+ To compile this driver as a module, choose M here.
+
+ If unsure, say Y.
+
+config PPP_SYNC_TTY
+ tristate "PPP support for sync tty ports"
+ depends on PPP
+ ---help---
+ Say Y (or M) here if you want to be able to use PPP over synchronous
+ (HDLC) tty devices, such as the SyncLink adapter. These devices
+ are often used for high-speed leased lines like T1/E1.
+
+ To compile this driver as a module, choose M here.
+
+endif # PPP
diff --git a/drivers/net/ppp/Makefile b/drivers/net/ppp/Makefile
new file mode 100644
index 000000000000..a6b6297b0066
--- /dev/null
+++ b/drivers/net/ppp/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the Linux PPP network device drivers.
+#
+
+obj-$(CONFIG_PPP) += ppp_generic.o
+obj-$(CONFIG_PPP_ASYNC) += ppp_async.o
+obj-$(CONFIG_PPP_BSDCOMP) += bsd_comp.o
+obj-$(CONFIG_PPP_DEFLATE) += ppp_deflate.o
+obj-$(CONFIG_PPP_MPPE) += ppp_mppe.o
+obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o
+obj-$(CONFIG_PPPOE) += pppox.o pppoe.o
+obj-$(CONFIG_PPPOL2TP) += pppox.o
+obj-$(CONFIG_PPTP) += pppox.o pptp.o
diff --git a/drivers/net/bsd_comp.c b/drivers/net/ppp/bsd_comp.c
index 6e99d80ec409..a9b759add187 100644
--- a/drivers/net/bsd_comp.c
+++ b/drivers/net/ppp/bsd_comp.c
@@ -201,7 +201,7 @@ extern void ppp_unregister_compressor (struct compressor *cp);
#define LAST 255
#define MAXCODE(b) ((1 << (b)) - 1)
-#define BADCODEM1 MAXCODE(MAX_BSD_BITS);
+#define BADCODEM1 MAXCODE(MAX_BSD_BITS)
#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
^ (unsigned long)(prefix))
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp/ppp_async.c
index c554a397e558..c6ba64380829 100644
--- a/drivers/net/ppp_async.c
+++ b/drivers/net/ppp/ppp_async.c
@@ -30,6 +30,7 @@
#include <linux/ppp_channel.h>
#include <linux/spinlock.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp/ppp_deflate.c
index 1dbdf82a6dfd..1dbdf82a6dfd 100644
--- a/drivers/net/ppp_deflate.c
+++ b/drivers/net/ppp/ppp_deflate.c
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 4609bc0e2f56..edfa15d2e795 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -48,7 +48,7 @@
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <net/slhc_vj.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/nsproxy.h>
#include <net/net_namespace.h>
@@ -1465,7 +1465,12 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
continue;
}
- mtu = pch->chan->mtu - hdrlen;
+ /*
+ * hdrlen includes the 2-byte PPP protocol field, but the
+ * MTU counts only the payload excluding the protocol field.
+ * (RFC1661 Section 2)
+ */
+ mtu = pch->chan->mtu - (hdrlen - 2);
if (mtu < 4)
mtu = 4;
if (flen > mtu)
diff --git a/drivers/net/ppp_mppe.c b/drivers/net/ppp/ppp_mppe.c
index 9a1849a83e2a..9a1849a83e2a 100644
--- a/drivers/net/ppp_mppe.c
+++ b/drivers/net/ppp/ppp_mppe.c
diff --git a/drivers/net/ppp_mppe.h b/drivers/net/ppp/ppp_mppe.h
index 7a14e058c668..7a14e058c668 100644
--- a/drivers/net/ppp_mppe.h
+++ b/drivers/net/ppp/ppp_mppe.h
diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c
index 2573f525f11c..736a39ee05bb 100644
--- a/drivers/net/ppp_synctty.c
+++ b/drivers/net/ppp/ppp_synctty.c
@@ -44,6 +44,7 @@
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/pppoe.c b/drivers/net/ppp/pppoe.c
index 718879b35b7d..bc9a4bb31980 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -348,8 +348,9 @@ static int pppoe_device_event(struct notifier_block *this,
/* Only look at sockets that are using this specific device. */
switch (event) {
+ case NETDEV_CHANGEADDR:
case NETDEV_CHANGEMTU:
- /* A change in mtu is a bad thing, requiring
+ /* A change in mtu or address is a bad thing, requiring
* LCP re-negotiation.
*/
diff --git a/drivers/net/pppox.c b/drivers/net/ppp/pppox.c
index 8c0d170dabcd..8c0d170dabcd 100644
--- a/drivers/net/pppox.c
+++ b/drivers/net/ppp/pppox.c
diff --git a/drivers/net/pptp.c b/drivers/net/ppp/pptp.c
index 1286fe212dc4..89f829f5f725 100644
--- a/drivers/net/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -30,7 +30,6 @@
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
-#include <linux/version.h>
#include <linux/rcupdate.h>
#include <linux/spinlock.h>
@@ -286,8 +285,10 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
ip_send_check(iph);
ip_local_out(skb);
+ return 1;
tx_error:
+ kfree_skb(skb);
return 1;
}
@@ -306,11 +307,18 @@ static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb)
}
header = (struct pptp_gre_header *)(skb->data);
+ headersize = sizeof(*header);
/* test if acknowledgement present */
if (PPTP_GRE_IS_A(header->ver)) {
- __u32 ack = (PPTP_GRE_IS_S(header->flags)) ?
- header->ack : header->seq; /* ack in different place if S = 0 */
+ __u32 ack;
+
+ if (!pskb_may_pull(skb, headersize))
+ goto drop;
+ header = (struct pptp_gre_header *)(skb->data);
+
+ /* ack in different place if S = 0 */
+ ack = PPTP_GRE_IS_S(header->flags) ? header->ack : header->seq;
ack = ntohl(ack);
@@ -319,21 +327,18 @@ static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb)
/* also handle sequence number wrap-around */
if (WRAPPED(ack, opt->ack_recv))
opt->ack_recv = ack;
+ } else {
+ headersize -= sizeof(header->ack);
}
-
/* test if payload present */
if (!PPTP_GRE_IS_S(header->flags))
goto drop;
- headersize = sizeof(*header);
payload_len = ntohs(header->payload_len);
seq = ntohl(header->seq);
- /* no ack present? */
- if (!PPTP_GRE_IS_A(header->ver))
- headersize -= sizeof(header->ack);
/* check for incomplete packet (length smaller than expected) */
- if (skb->len - headersize < payload_len)
+ if (!pskb_may_pull(skb, headersize + payload_len))
goto drop;
payload = skb->data + headersize;
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c
index 5d3436d47edd..7145714a5ec9 100644
--- a/drivers/net/rionet.c
+++ b/drivers/net/rionet.c
@@ -80,16 +80,16 @@ static int rionet_capable = 1;
*/
static struct rio_dev **rionet_active;
-#define is_rionet_capable(pef, src_ops, dst_ops) \
- ((pef & RIO_PEF_INB_MBOX) && \
- (pef & RIO_PEF_INB_DOORBELL) && \
+#define is_rionet_capable(src_ops, dst_ops) \
+ ((src_ops & RIO_SRC_OPS_DATA_MSG) && \
+ (dst_ops & RIO_DST_OPS_DATA_MSG) && \
(src_ops & RIO_SRC_OPS_DOORBELL) && \
(dst_ops & RIO_DST_OPS_DOORBELL))
#define dev_rionet_capable(dev) \
- is_rionet_capable(dev->pef, dev->src_ops, dev->dst_ops)
+ is_rionet_capable(dev->src_ops, dev->dst_ops)
-#define RIONET_MAC_MATCH(x) (*(u32 *)x == 0x00010001)
-#define RIONET_GET_DESTID(x) (*(u16 *)(x + 4))
+#define RIONET_MAC_MATCH(x) (!memcmp((x), "\00\01\00\01", 4))
+#define RIONET_GET_DESTID(x) ((*((u8 *)x + 4) << 8) | *((u8 *)x + 5))
static int rionet_rx_clean(struct net_device *ndev)
{
@@ -190,7 +190,7 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
return NETDEV_TX_BUSY;
}
- if (eth->h_dest[0] & 0x01) {
+ if (is_multicast_ether_addr(eth->h_dest)) {
for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size);
i++)
if (rionet_active[i])
@@ -282,7 +282,6 @@ static int rionet_open(struct net_device *ndev)
{
int i, rc = 0;
struct rionet_peer *peer, *tmp;
- u32 pwdcsr;
struct rionet_private *rnet = netdev_priv(ndev);
if (netif_msg_ifup(rnet))
@@ -332,13 +331,8 @@ static int rionet_open(struct net_device *ndev)
continue;
}
- /*
- * If device has initialized inbound doorbells,
- * send a join message
- */
- rio_read_config_32(peer->rdev, RIO_WRITE_PORT_CSR, &pwdcsr);
- if (pwdcsr & RIO_DOORBELL_AVAIL)
- rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN);
+ /* Send a join message */
+ rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN);
}
out:
@@ -492,7 +486,7 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)
static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id)
{
int rc = -ENODEV;
- u32 lpef, lsrc_ops, ldst_ops;
+ u32 lsrc_ops, ldst_ops;
struct rionet_peer *peer;
struct net_device *ndev = NULL;
@@ -515,12 +509,11 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id)
* on later probes
*/
if (!rionet_check) {
- rio_local_read_config_32(rdev->net->hport, RIO_PEF_CAR, &lpef);
rio_local_read_config_32(rdev->net->hport, RIO_SRC_OPS_CAR,
&lsrc_ops);
rio_local_read_config_32(rdev->net->hport, RIO_DST_OPS_CAR,
&ldst_ops);
- if (!is_rionet_capable(lpef, lsrc_ops, ldst_ops)) {
+ if (!is_rionet_capable(lsrc_ops, ldst_ops)) {
printk(KERN_ERR
"%s: local device is not network capable\n",
DRV_NAME);
diff --git a/drivers/net/slip/Kconfig b/drivers/net/slip/Kconfig
new file mode 100644
index 000000000000..211b160e4e9c
--- /dev/null
+++ b/drivers/net/slip/Kconfig
@@ -0,0 +1,79 @@
+#
+# SLIP network device configuration
+#
+
+config SLIP
+ tristate "SLIP (serial line) support"
+ ---help---
+ Say Y if you intend to use SLIP or CSLIP (compressed SLIP) to
+ connect to your Internet service provider or to connect to some
+ other local Unix box or if you want to configure your Linux box as a
+ Slip/CSlip server for other people to dial in. SLIP (Serial Line
+ Internet Protocol) is a protocol used to send Internet traffic over
+ serial connections such as telephone lines or null modem cables;
+ nowadays, the protocol PPP is more commonly used for this same
+ purpose.
+
+ Normally, your access provider has to support SLIP in order for you
+ to be able to use it, but there is now a SLIP emulator called SLiRP
+ around (available from
+ <ftp://ibiblio.org/pub/Linux/system/network/serial/>) which
+ allows you to use SLIP over a regular dial up shell connection. If
+ you plan to use SLiRP, make sure to say Y to CSLIP, below. The
+ NET-3-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>, explains how to
+ configure SLIP. Note that you don't need this option if you just
+ want to run term (term is a program which gives you almost full
+ Internet connectivity if you have a regular dial up shell account on
+ some Internet connected Unix computer. Read
+ <http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html>). SLIP
+ support will enlarge your kernel by about 4 KB. If unsure, say N.
+
+ To compile this driver as a module, choose M here. The module
+ will be called slip.
+
+config SLHC
+ tristate
+ ---help---
+ This option enables Van Jacobsen serial line header compression
+ routines.
+
+if SLIP
+
+config SLIP_COMPRESSED
+ bool "CSLIP compressed headers"
+ depends on SLIP
+ select SLHC
+ ---help---
+ This protocol is faster than SLIP because it uses compression on the
+ TCP/IP headers (not on the data itself), but it has to be supported
+ on both ends. Ask your access provider if you are not sure and
+ answer Y, just in case. You will still be able to use plain SLIP. If
+ you plan to use SLiRP, the SLIP emulator (available from
+ <ftp://ibiblio.org/pub/Linux/system/network/serial/>) which
+ allows you to use SLIP over a regular dial up shell connection, you
+ definitely want to say Y here. The NET-3-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>, explains how to configure
+ CSLIP. This won't enlarge your kernel.
+
+config SLIP_SMART
+ bool "Keepalive and linefill"
+ depends on SLIP
+ ---help---
+ Adds additional capabilities to the SLIP driver to support the
+ RELCOM line fill and keepalive monitoring. Ideal on poor quality
+ analogue lines.
+
+config SLIP_MODE_SLIP6
+ bool "Six bit SLIP encapsulation"
+ depends on SLIP
+ ---help---
+ Just occasionally you may need to run IP over hostile serial
+ networks that don't pass all control characters or are only seven
+ bit. Saying Y here adds an extra mode you can use with SLIP:
+ "slip6". In this mode, SLIP will only send normal ASCII symbols over
+ the serial device. Naturally, this has to be supported at the other
+ end of the link as well. It's good enough, for example, to run IP
+ over the async ports of a Camtec JNT Pad. If unsure, say N.
+
+endif # SLIP
diff --git a/drivers/net/slip/Makefile b/drivers/net/slip/Makefile
new file mode 100644
index 000000000000..e3ebc59e6fb9
--- /dev/null
+++ b/drivers/net/slip/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the SLIP network device drivers.
+#
+
+obj-$(CONFIG_SLIP) += slip.o
+obj-$(CONFIG_SLHC) += slhc.o
diff --git a/drivers/net/slhc.c b/drivers/net/slip/slhc.c
index ab9e3b785b5b..0a0a6643cf3a 100644
--- a/drivers/net/slhc.c
+++ b/drivers/net/slip/slhc.c
@@ -297,7 +297,7 @@ slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
lcs = cs;
cs = cs->next;
comp->sls_o_searches++;
- };
+ }
/*
* Didn't find it -- re-use oldest cstate. Send an
* uncompressed packet that tells the other side what
diff --git a/drivers/net/slip.c b/drivers/net/slip/slip.c
index 8ec1a9a0bb9a..ba08341fb92c 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip/slip.c
@@ -182,11 +182,11 @@ static int sl_alloc_bufs(struct slip *sl, int mtu)
#ifdef SL_INCLUDE_CSLIP
cbuff = xchg(&sl->cbuff, cbuff);
slcomp = xchg(&sl->slcomp, slcomp);
+#endif
#ifdef CONFIG_SLIP_MODE_SLIP6
sl->xdata = 0;
sl->xbits = 0;
#endif
-#endif
spin_unlock_bh(&sl->lock);
err = 0;
@@ -194,8 +194,7 @@ static int sl_alloc_bufs(struct slip *sl, int mtu)
err_exit:
#ifdef SL_INCLUDE_CSLIP
kfree(cbuff);
- if (slcomp)
- slhc_free(slcomp);
+ slhc_free(slcomp);
#endif
kfree(xbuff);
kfree(rbuff);
@@ -248,7 +247,7 @@ static int sl_realloc_bufs(struct slip *sl, int mtu)
#else
if (xbuff == NULL || rbuff == NULL) {
#endif
- if (mtu >= sl->mtu) {
+ if (mtu > sl->mtu) {
printk(KERN_WARNING "%s: unable to grow slip buffers, MTU change cancelled.\n",
dev->name);
err = -ENOBUFS;
@@ -368,7 +367,7 @@ static void sl_bump(struct slip *sl)
memcpy(skb_put(skb, count), sl->rbuff, count);
skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IP);
- netif_rx(skb);
+ netif_rx_ni(skb);
dev->stats.rx_packets++;
}
@@ -563,34 +562,33 @@ static struct rtnl_link_stats64 *
sl_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
{
struct net_device_stats *devstats = &dev->stats;
- unsigned long c_rx_dropped = 0;
#ifdef SL_INCLUDE_CSLIP
- unsigned long c_rx_fifo_errors = 0;
- unsigned long c_tx_fifo_errors = 0;
- unsigned long c_collisions = 0;
struct slip *sl = netdev_priv(dev);
struct slcompress *comp = sl->slcomp;
-
- if (comp) {
- c_rx_fifo_errors = comp->sls_i_compressed;
- c_rx_dropped = comp->sls_i_tossed;
- c_tx_fifo_errors = comp->sls_o_compressed;
- c_collisions = comp->sls_o_misses;
- }
- stats->rx_fifo_errors = sl->rx_compressed + c_rx_fifo_errors;
- stats->tx_fifo_errors = sl->tx_compressed + c_tx_fifo_errors;
- stats->collisions = sl->tx_misses + c_collisions;
#endif
stats->rx_packets = devstats->rx_packets;
stats->tx_packets = devstats->tx_packets;
stats->rx_bytes = devstats->rx_bytes;
stats->tx_bytes = devstats->tx_bytes;
- stats->rx_dropped = devstats->rx_dropped + c_rx_dropped;
+ stats->rx_dropped = devstats->rx_dropped;
stats->tx_dropped = devstats->tx_dropped;
stats->tx_errors = devstats->tx_errors;
stats->rx_errors = devstats->rx_errors;
stats->rx_over_errors = devstats->rx_over_errors;
+#ifdef SL_INCLUDE_CSLIP
+ if (comp) {
+ /* Generic compressed statistics */
+ stats->rx_compressed = comp->sls_i_compressed;
+ stats->tx_compressed = comp->sls_o_compressed;
+
+ /* Are we really still needs this? */
+ stats->rx_fifo_errors += comp->sls_i_compressed;
+ stats->rx_dropped += comp->sls_i_tossed;
+ stats->tx_fifo_errors += comp->sls_o_compressed;
+ stats->collisions += comp->sls_o_misses;
+ }
+#endif
return stats;
}
@@ -724,12 +722,10 @@ static void sl_sync(void)
static struct slip *sl_alloc(dev_t line)
{
int i;
+ char name[IFNAMSIZ];
struct net_device *dev = NULL;
struct slip *sl;
- if (slip_devs == NULL)
- return NULL; /* Master array missing ! */
-
for (i = 0; i < slip_maxdev; i++) {
dev = slip_devs[i];
if (dev == NULL)
@@ -739,25 +735,12 @@ static struct slip *sl_alloc(dev_t line)
if (i >= slip_maxdev)
return NULL;
- if (dev) {
- sl = netdev_priv(dev);
- if (test_bit(SLF_INUSE, &sl->flags)) {
- unregister_netdevice(dev);
- dev = NULL;
- slip_devs[i] = NULL;
- }
- }
-
- if (!dev) {
- char name[IFNAMSIZ];
- sprintf(name, "sl%d", i);
-
- dev = alloc_netdev(sizeof(*sl), name, sl_setup);
- if (!dev)
- return NULL;
- dev->base_addr = i;
- }
+ sprintf(name, "sl%d", i);
+ dev = alloc_netdev(sizeof(*sl), name, sl_setup);
+ if (!dev)
+ return NULL;
+ dev->base_addr = i;
sl = netdev_priv(dev);
/* Initialize channel control data */
@@ -823,7 +806,6 @@ static int slip_open(struct tty_struct *tty)
sl->tty = tty;
tty->disc_data = sl;
- sl->line = tty_devnum(tty);
sl->pid = current->pid;
if (!test_bit(SLF_INUSE, &sl->flags)) {
@@ -890,8 +872,6 @@ static void slip_close(struct tty_struct *tty)
tty->disc_data = NULL;
sl->tty = NULL;
- if (!sl->leased)
- sl->line = 0;
/* VSV = very important to remove timers */
#ifdef CONFIG_SLIP_SMART
diff --git a/drivers/net/slip.h b/drivers/net/slip/slip.h
index 914e958abbfc..67673cf1266b 100644
--- a/drivers/net/slip.h
+++ b/drivers/net/slip/slip.h
@@ -65,15 +65,6 @@ struct slip {
unsigned char *xbuff; /* transmitter buffer */
unsigned char *xhead; /* pointer to next byte to XMIT */
int xleft; /* bytes left in XMIT queue */
-
- /* SLIP interface statistics. */
-#ifdef SL_INCLUDE_CSLIP
- unsigned long tx_compressed;
- unsigned long rx_compressed;
- unsigned long tx_misses;
-#endif
- /* Detailed SLIP statistics. */
-
int mtu; /* Our mtu (to spot changes!) */
int buffsize; /* Max buffers sizes */
@@ -90,7 +81,6 @@ struct slip {
unsigned char mode; /* SLIP mode */
unsigned char leased;
- dev_t line;
pid_t pid;
#define SL_MODE_SLIP 0
#define SL_MODE_CSLIP 1
diff --git a/drivers/net/sungem_phy.c b/drivers/net/sungem_phy.c
index d16880d7099b..58f13adaa549 100644
--- a/drivers/net/sungem_phy.c
+++ b/drivers/net/sungem_phy.c
@@ -33,7 +33,7 @@
#include <asm/prom.h>
#endif
-#include "sungem_phy.h"
+#include <linux/sungem_phy.h>
/* Link modes of the BCM5400 PHY */
static const int phy_BCM5400_link_table[8][3] = {
@@ -1156,7 +1156,7 @@ static struct mii_phy_def* mii_phy_table[] = {
NULL
};
-int mii_phy_probe(struct mii_phy *phy, int mii_id)
+int sungem_phy_probe(struct mii_phy *phy, int mii_id)
{
int rc;
u32 id;
@@ -1195,6 +1195,5 @@ fail:
return -ENODEV;
}
-EXPORT_SYMBOL(mii_phy_probe);
+EXPORT_SYMBOL(sungem_phy_probe);
MODULE_LICENSE("GPL");
-
diff --git a/drivers/net/sungem_phy.h b/drivers/net/sungem_phy.h
deleted file mode 100644
index af02f9479cbb..000000000000
--- a/drivers/net/sungem_phy.h
+++ /dev/null
@@ -1,132 +0,0 @@
-#ifndef __SUNGEM_PHY_H__
-#define __SUNGEM_PHY_H__
-
-struct mii_phy;
-
-/* Operations supported by any kind of PHY */
-struct mii_phy_ops
-{
- int (*init)(struct mii_phy *phy);
- int (*suspend)(struct mii_phy *phy);
- int (*setup_aneg)(struct mii_phy *phy, u32 advertise);
- int (*setup_forced)(struct mii_phy *phy, int speed, int fd);
- int (*poll_link)(struct mii_phy *phy);
- int (*read_link)(struct mii_phy *phy);
- int (*enable_fiber)(struct mii_phy *phy, int autoneg);
-};
-
-/* Structure used to statically define an mii/gii based PHY */
-struct mii_phy_def
-{
- u32 phy_id; /* Concatenated ID1 << 16 | ID2 */
- u32 phy_id_mask; /* Significant bits */
- u32 features; /* Ethtool SUPPORTED_* defines */
- int magic_aneg; /* Autoneg does all speed test for us */
- const char* name;
- const struct mii_phy_ops* ops;
-};
-
-enum {
- BCM54XX_COPPER,
- BCM54XX_FIBER,
- BCM54XX_GBIC,
- BCM54XX_SGMII,
- BCM54XX_UNKNOWN,
-};
-
-/* An instance of a PHY, partially borrowed from mii_if_info */
-struct mii_phy
-{
- struct mii_phy_def* def;
- u32 advertising;
- int mii_id;
-
- /* 1: autoneg enabled, 0: disabled */
- int autoneg;
-
- /* forced speed & duplex (no autoneg)
- * partner speed & duplex & pause (autoneg)
- */
- int speed;
- int duplex;
- int pause;
-
- /* Provided by host chip */
- struct net_device *dev;
- int (*mdio_read) (struct net_device *dev, int mii_id, int reg);
- void (*mdio_write) (struct net_device *dev, int mii_id, int reg, int val);
- void *platform_data;
-};
-
-/* Pass in a struct mii_phy with dev, mdio_read and mdio_write
- * filled, the remaining fields will be filled on return
- */
-extern int mii_phy_probe(struct mii_phy *phy, int mii_id);
-
-
-/* MII definitions missing from mii.h */
-
-#define BMCR_SPD2 0x0040 /* Gigabit enable (bcm54xx) */
-#define LPA_PAUSE 0x0400
-
-/* More PHY registers (model specific) */
-
-/* MII BCM5201 MULTIPHY interrupt register */
-#define MII_BCM5201_INTERRUPT 0x1A
-#define MII_BCM5201_INTERRUPT_INTENABLE 0x4000
-
-#define MII_BCM5201_AUXMODE2 0x1B
-#define MII_BCM5201_AUXMODE2_LOWPOWER 0x0008
-
-#define MII_BCM5201_MULTIPHY 0x1E
-
-/* MII BCM5201 MULTIPHY register bits */
-#define MII_BCM5201_MULTIPHY_SERIALMODE 0x0002
-#define MII_BCM5201_MULTIPHY_SUPERISOLATE 0x0008
-
-/* MII BCM5221 Additional registers */
-#define MII_BCM5221_TEST 0x1f
-#define MII_BCM5221_TEST_ENABLE_SHADOWS 0x0080
-#define MII_BCM5221_SHDOW_AUX_STAT2 0x1b
-#define MII_BCM5221_SHDOW_AUX_STAT2_APD 0x0020
-#define MII_BCM5221_SHDOW_AUX_MODE4 0x1a
-#define MII_BCM5221_SHDOW_AUX_MODE4_IDDQMODE 0x0001
-#define MII_BCM5221_SHDOW_AUX_MODE4_CLKLOPWR 0x0004
-
-/* MII BCM5241 Additional registers */
-#define MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR 0x0008
-
-/* MII BCM5400 1000-BASET Control register */
-#define MII_BCM5400_GB_CONTROL 0x09
-#define MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP 0x0200
-
-/* MII BCM5400 AUXCONTROL register */
-#define MII_BCM5400_AUXCONTROL 0x18
-#define MII_BCM5400_AUXCONTROL_PWR10BASET 0x0004
-
-/* MII BCM5400 AUXSTATUS register */
-#define MII_BCM5400_AUXSTATUS 0x19
-#define MII_BCM5400_AUXSTATUS_LINKMODE_MASK 0x0700
-#define MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT 8
-
-/* 1000BT control (Marvell & BCM54xx at least) */
-#define MII_1000BASETCONTROL 0x09
-#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
-#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
-
-/* Marvell 88E1011 PHY control */
-#define MII_M1011_PHY_SPEC_CONTROL 0x10
-#define MII_M1011_PHY_SPEC_CONTROL_MANUAL_MDIX 0x20
-#define MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX 0x40
-
-/* Marvell 88E1011 PHY status */
-#define MII_M1011_PHY_SPEC_STATUS 0x11
-#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
-#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
-#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
-#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
-#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
-#define MII_M1011_PHY_SPEC_STATUS_TX_PAUSE 0x0008
-#define MII_M1011_PHY_SPEC_STATUS_RX_PAUSE 0x0004
-
-#endif /* __SUNGEM_PHY_H__ */
diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c
index ff32befd8443..ef9fdf3652f6 100644
--- a/drivers/net/tokenring/3c359.c
+++ b/drivers/net/tokenring/3c359.c
@@ -282,7 +282,7 @@ static const struct net_device_ops xl_netdev_ops = {
.ndo_stop = xl_close,
.ndo_start_xmit = xl_xmit,
.ndo_change_mtu = xl_change_mtu,
- .ndo_set_multicast_list = xl_set_rx_mode,
+ .ndo_set_rx_mode = xl_set_rx_mode,
.ndo_set_mac_address = xl_set_mac_address,
};
@@ -304,7 +304,7 @@ static int __devinit xl_probe(struct pci_dev *pdev,
if ((i = pci_request_regions(pdev,"3c359"))) {
return i ;
- } ;
+ }
/*
* Allowing init_trdev to allocate the private data will align
@@ -1773,7 +1773,9 @@ static void xl_wait_misr_flags(struct net_device *dev)
if (readb(xl_mmio + MMIO_MACDATA) != 0) { /* Misr not clear */
for (i=0; i<6; i++) {
writel(MEM_BYTE_READ | 0xDFFE0 | i, xl_mmio + MMIO_MAC_ACCESS_CMD) ;
- while (readb(xl_mmio + MMIO_MACDATA) != 0 ) {} ; /* Empty Loop */
+ while (readb(xl_mmio + MMIO_MACDATA) != 0) {
+ ; /* Empty Loop */
+ }
}
}
diff --git a/drivers/net/tokenring/Kconfig b/drivers/net/tokenring/Kconfig
index c4137b0f808e..c7e0149d1514 100644
--- a/drivers/net/tokenring/Kconfig
+++ b/drivers/net/tokenring/Kconfig
@@ -4,9 +4,9 @@
# So far, we only have PCI, ISA, and MCA token ring devices
menuconfig TR
- tristate "Token Ring driver support"
+ bool "Token Ring driver support"
depends on NETDEVICES && !UML
- depends on (PCI || ISA || MCA || CCW)
+ depends on (PCI || ISA || MCA || CCW || PCMCIA)
select LLC
help
Token Ring is IBM's way of communication on a local network; the
@@ -20,6 +20,17 @@ menuconfig TR
if TR
+config PCMCIA_IBMTR
+ tristate "IBM PCMCIA tokenring adapter support"
+ depends on IBMTR!=y && PCMCIA
+ ---help---
+ Say Y here if you intend to attach this type of Token Ring PCMCIA
+ card to your computer. You then also need to say Y to "Token Ring
+ driver support".
+
+ To compile this driver as a module, choose M here: the module will be
+ called ibmtr_cs.
+
config IBMTR
tristate "IBM Tropic chipset based adapter support"
depends on ISA || MCA
diff --git a/drivers/net/tokenring/Makefile b/drivers/net/tokenring/Makefile
index c88b0a5e5380..f1be8d97b7a8 100644
--- a/drivers/net/tokenring/Makefile
+++ b/drivers/net/tokenring/Makefile
@@ -2,14 +2,15 @@
# Makefile for drivers/net/tokenring
#
-obj-$(CONFIG_IBMTR) += ibmtr.o
-obj-$(CONFIG_IBMOL) += olympic.o
-obj-$(CONFIG_IBMLS) += lanstreamer.o
-obj-$(CONFIG_TMS380TR) += tms380tr.o
-obj-$(CONFIG_ABYSS) += abyss.o
-obj-$(CONFIG_MADGEMC) += madgemc.o
-obj-$(CONFIG_PROTEON) += proteon.o
-obj-$(CONFIG_TMSPCI) += tmspci.o
-obj-$(CONFIG_SKISA) += skisa.o
-obj-$(CONFIG_SMCTR) += smctr.o
+obj-$(CONFIG_PCMCIA_IBMTR) += ibmtr_cs.o
+obj-$(CONFIG_IBMTR) += ibmtr.o
+obj-$(CONFIG_IBMOL) += olympic.o
+obj-$(CONFIG_IBMLS) += lanstreamer.o
+obj-$(CONFIG_TMS380TR) += tms380tr.o
+obj-$(CONFIG_ABYSS) += abyss.o
+obj-$(CONFIG_MADGEMC) += madgemc.o
+obj-$(CONFIG_PROTEON) += proteon.o
+obj-$(CONFIG_TMSPCI) += tmspci.o
+obj-$(CONFIG_SKISA) += skisa.o
+obj-$(CONFIG_SMCTR) += smctr.o
obj-$(CONFIG_3C359) += 3c359.o
diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c
index 4786497de03e..b5c8c18f5046 100644
--- a/drivers/net/tokenring/ibmtr.c
+++ b/drivers/net/tokenring/ibmtr.c
@@ -123,6 +123,7 @@ in the event that chatty debug messages are desired - jjs 12/30/98 */
/* some 95 OS send many non UI frame; this allow removing the warning */
#define TR_FILTERNONUI 1
+#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/ip.h>
@@ -177,7 +178,7 @@ static char __devinit *adapter_def(char type)
case 0xD: return "16/4 Adapter/A (short) | 16/4 ISA-16 Adapter";
case 0xC: return "Auto 16/4 Adapter";
default: return "adapter (unknown type)";
- };
+ }
};
#define TRC_INIT 0x01 /* Trace initialization & PROBEs */
@@ -822,7 +823,7 @@ static const struct net_device_ops trdev_netdev_ops = {
.ndo_open = tok_open,
.ndo_stop = tok_close,
.ndo_start_xmit = tok_send_packet,
- .ndo_set_multicast_list = tok_set_multicast_list,
+ .ndo_set_rx_mode = tok_set_multicast_list,
.ndo_change_mtu = ibmtr_change_mtu,
};
diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/tokenring/ibmtr_cs.c
index 6006d5488fbe..91b684630fc5 100644
--- a/drivers/net/pcmcia/ibmtr_cs.c
+++ b/drivers/net/tokenring/ibmtr_cs.c
@@ -66,7 +66,7 @@
#include <asm/system.h>
#define PCMCIA
-#include "../tokenring/ibmtr.c"
+#include "ibmtr.c"
/*====================================================================*/
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
index 9354ca9da576..8d71e0d29062 100644
--- a/drivers/net/tokenring/lanstreamer.c
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -231,7 +231,7 @@ static const struct net_device_ops streamer_netdev_ops = {
#if STREAMER_IOCTL
.ndo_do_ioctl = streamer_ioctl,
#endif
- .ndo_set_multicast_list = streamer_set_rx_mode,
+ .ndo_set_rx_mode = streamer_set_rx_mode,
.ndo_set_mac_address = streamer_set_mac_address,
};
diff --git a/drivers/net/tokenring/madgemc.c b/drivers/net/tokenring/madgemc.c
index 2bedc0ace812..6153cfd696b6 100644
--- a/drivers/net/tokenring/madgemc.c
+++ b/drivers/net/tokenring/madgemc.c
@@ -418,7 +418,7 @@ static irqreturn_t madgemc_interrupt(int irq, void *dev_id)
return IRQ_NONE;
}
- dev = (struct net_device *)dev_id;
+ dev = dev_id;
/* Make sure its really us. -- the Madge way */
pending = inb(dev->base_addr + MC_CONTROL_REG0);
diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c
index e3855aeb13d4..fd8dce90c957 100644
--- a/drivers/net/tokenring/olympic.c
+++ b/drivers/net/tokenring/olympic.c
@@ -201,7 +201,7 @@ static const struct net_device_ops olympic_netdev_ops = {
.ndo_stop = olympic_close,
.ndo_start_xmit = olympic_xmit,
.ndo_change_mtu = olympic_change_mtu,
- .ndo_set_multicast_list = olympic_set_rx_mode,
+ .ndo_set_rx_mode = olympic_set_rx_mode,
.ndo_set_mac_address = olympic_set_mac_address,
};
diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c
index d9044aba7afa..029846a98636 100644
--- a/drivers/net/tokenring/smctr.c
+++ b/drivers/net/tokenring/smctr.c
@@ -3623,7 +3623,7 @@ static const struct net_device_ops smctr_netdev_ops = {
.ndo_start_xmit = smctr_send_packet,
.ndo_tx_timeout = smctr_timeout,
.ndo_get_stats = smctr_get_stats,
- .ndo_set_multicast_list = smctr_set_multicast_list,
+ .ndo_set_rx_mode = smctr_set_multicast_list,
};
static int __init smctr_probe1(struct net_device *dev, int ioaddr)
diff --git a/drivers/net/tokenring/tms380tr.c b/drivers/net/tokenring/tms380tr.c
index 793020347e54..65e9cf3a71fe 100644
--- a/drivers/net/tokenring/tms380tr.c
+++ b/drivers/net/tokenring/tms380tr.c
@@ -2289,7 +2289,7 @@ const struct net_device_ops tms380tr_netdev_ops = {
.ndo_start_xmit = tms380tr_send_packet,
.ndo_tx_timeout = tms380tr_timeout,
.ndo_get_stats = tms380tr_get_stats,
- .ndo_set_multicast_list = tms380tr_set_multicast_list,
+ .ndo_set_rx_mode = tms380tr_set_multicast_list,
.ndo_set_mac_address = tms380tr_set_mac_address,
};
EXPORT_SYMBOL(tms380tr_netdev_ops);
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 5235f48be1be..7bea9c65119e 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -496,7 +496,7 @@ static const struct net_device_ops tap_netdev_ops = {
.ndo_start_xmit = tun_net_xmit,
.ndo_change_mtu = tun_net_change_mtu,
.ndo_fix_features = tun_net_fix_features,
- .ndo_set_multicast_list = tun_net_mclist,
+ .ndo_set_rx_mode = tun_net_mclist,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -528,6 +528,7 @@ static void tun_net_init(struct net_device *dev)
dev->netdev_ops = &tap_netdev_ops;
/* Ethernet TAP Device */
ether_setup(dev);
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
random_ether_addr(dev->dev_addr);
@@ -572,9 +573,9 @@ static unsigned int tun_chr_poll(struct file *file, poll_table * wait)
/* prepad is the amount to reserve at front. len is length after that.
* linear is a hint as to how much to copy (usually headers). */
-static inline struct sk_buff *tun_alloc_skb(struct tun_struct *tun,
- size_t prepad, size_t len,
- size_t linear, int noblock)
+static struct sk_buff *tun_alloc_skb(struct tun_struct *tun,
+ size_t prepad, size_t len,
+ size_t linear, int noblock)
{
struct sock *sk = tun->socket.sk;
struct sk_buff *skb;
@@ -600,13 +601,13 @@ static inline struct sk_buff *tun_alloc_skb(struct tun_struct *tun,
}
/* Get packet from user space buffer */
-static __inline__ ssize_t tun_get_user(struct tun_struct *tun,
- const struct iovec *iv, size_t count,
- int noblock)
+static ssize_t tun_get_user(struct tun_struct *tun,
+ const struct iovec *iv, size_t count,
+ int noblock)
{
struct tun_pi pi = { 0, cpu_to_be16(ETH_P_IP) };
struct sk_buff *skb;
- size_t len = count, align = 0;
+ size_t len = count, align = NET_SKB_PAD;
struct virtio_net_hdr gso = { 0 };
int offset = 0;
@@ -636,7 +637,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun,
}
if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) {
- align = NET_IP_ALIGN;
+ align += NET_IP_ALIGN;
if (unlikely(len < ETH_HLEN ||
(gso.hdr_len && gso.hdr_len < ETH_HLEN)))
return -EINVAL;
@@ -688,7 +689,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun,
case TUN_TAP_DEV:
skb->protocol = eth_type_trans(skb, tun->dev);
break;
- };
+ }
if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
pr_debug("GSO!\n");
@@ -751,9 +752,9 @@ static ssize_t tun_chr_aio_write(struct kiocb *iocb, const struct iovec *iv,
}
/* Put packet to the user space buffer */
-static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
- struct sk_buff *skb,
- const struct iovec *iv, int len)
+static ssize_t tun_put_user(struct tun_struct *tun,
+ struct sk_buff *skb,
+ const struct iovec *iv, int len)
{
struct tun_pi pi = { 0, skb->protocol };
ssize_t total = 0;
@@ -810,6 +811,8 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
gso.csum_start = skb_checksum_start_offset(skb);
gso.csum_offset = skb->csum_offset;
+ } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+ gso.flags = VIRTIO_NET_HDR_F_DATA_VALID;
} /* else everything is zero */
if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total,
@@ -839,7 +842,8 @@ static ssize_t tun_do_read(struct tun_struct *tun,
tun_debug(KERN_INFO, tun, "tun_chr_read\n");
- add_wait_queue(&tun->wq.wait, &wait);
+ if (unlikely(!noblock))
+ add_wait_queue(&tun->wq.wait, &wait);
while (len) {
current->state = TASK_INTERRUPTIBLE;
@@ -870,7 +874,8 @@ static ssize_t tun_do_read(struct tun_struct *tun,
}
current->state = TASK_RUNNING;
- remove_wait_queue(&tun->wq.wait, &wait);
+ if (unlikely(!noblock))
+ remove_wait_queue(&tun->wq.wait, &wait);
return ret;
}
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 84d4608153c9..233576127934 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -68,6 +68,7 @@ config USB_KAWETH
config USB_PEGASUS
tristate "USB Pegasus/Pegasus-II based ethernet device support"
+ select NET_CORE
select MII
---help---
Say Y here if you know you have Pegasus or Pegasus-II based adapter.
@@ -84,6 +85,7 @@ config USB_PEGASUS
config USB_RTL8150
tristate "USB RTL8150 based ethernet device support (EXPERIMENTAL)"
depends on EXPERIMENTAL
+ select NET_CORE
select MII
help
Say Y here if you have RTL8150 based usb-ethernet adapter.
@@ -95,6 +97,7 @@ config USB_RTL8150
config USB_USBNET
tristate "Multi-purpose USB Networking Framework"
+ select NET_CORE
select MII
---help---
This driver supports several kinds of network links over USB,
diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c
index 6998aa6b7bb7..e6fed4d4cb77 100644
--- a/drivers/net/usb/asix.c
+++ b/drivers/net/usb/asix.c
@@ -36,8 +36,8 @@
#include <linux/usb/usbnet.h>
#include <linux/slab.h>
-#define DRIVER_VERSION "14-Jun-2006"
-static const char driver_name [] = "asix";
+#define DRIVER_VERSION "08-Nov-2011"
+#define DRIVER_NAME "asix"
/* ASIX AX8817X based USB 2.0 Ethernet Devices */
@@ -115,28 +115,27 @@ static const char driver_name [] = "asix";
#define AX88178_MEDIUM_DEFAULT \
(AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \
AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \
- AX_MEDIUM_RE )
+ AX_MEDIUM_RE)
#define AX88772_MEDIUM_DEFAULT \
(AX_MEDIUM_FD | AX_MEDIUM_RFC | \
AX_MEDIUM_TFC | AX_MEDIUM_PS | \
- AX_MEDIUM_AC | AX_MEDIUM_RE )
+ AX_MEDIUM_AC | AX_MEDIUM_RE)
/* AX88772 & AX88178 RX_CTL values */
-#define AX_RX_CTL_SO 0x0080
-#define AX_RX_CTL_AP 0x0020
-#define AX_RX_CTL_AM 0x0010
-#define AX_RX_CTL_AB 0x0008
-#define AX_RX_CTL_SEP 0x0004
-#define AX_RX_CTL_AMALL 0x0002
-#define AX_RX_CTL_PRO 0x0001
-#define AX_RX_CTL_MFB_2048 0x0000
-#define AX_RX_CTL_MFB_4096 0x0100
-#define AX_RX_CTL_MFB_8192 0x0200
-#define AX_RX_CTL_MFB_16384 0x0300
-
-#define AX_DEFAULT_RX_CTL \
- (AX_RX_CTL_SO | AX_RX_CTL_AB )
+#define AX_RX_CTL_SO 0x0080
+#define AX_RX_CTL_AP 0x0020
+#define AX_RX_CTL_AM 0x0010
+#define AX_RX_CTL_AB 0x0008
+#define AX_RX_CTL_SEP 0x0004
+#define AX_RX_CTL_AMALL 0x0002
+#define AX_RX_CTL_PRO 0x0001
+#define AX_RX_CTL_MFB_2048 0x0000
+#define AX_RX_CTL_MFB_4096 0x0100
+#define AX_RX_CTL_MFB_8192 0x0200
+#define AX_RX_CTL_MFB_16384 0x0300
+
+#define AX_DEFAULT_RX_CTL (AX_RX_CTL_SO | AX_RX_CTL_AB)
/* GPIO 0 .. 2 toggles */
#define AX_GPIO_GPO0EN 0x01 /* GPIO0 Output enable */
@@ -164,6 +163,8 @@ static const char driver_name [] = "asix";
#define MARVELL_CTRL_TXDELAY 0x0002
#define MARVELL_CTRL_RXDELAY 0x0080
+#define PHY_MODE_RTL8211CL 0x000C
+
/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
struct asix_data {
u8 multi_filter[AX_MCAST_FILTER_SIZE];
@@ -268,12 +269,15 @@ asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
netdev_dbg(dev->net, "asix_write_cmd_async() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n",
cmd, value, index, size);
- if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
netdev_err(dev->net, "Error allocating URB in write_cmd_async!\n");
return;
}
- if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
+ req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+ if (!req) {
netdev_err(dev->net, "Failed to allocate memory for control request\n");
usb_free_urb(urb);
return;
@@ -290,7 +294,8 @@ asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
(void *)req, data, size,
asix_async_cmd_callback, req);
- if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status < 0) {
netdev_err(dev->net, "Error submitting the control message: status=%d\n",
status);
kfree(req);
@@ -314,12 +319,11 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
skb_pull(skb, 4);
while (skb->len > 0) {
- if ((short)(header & 0x0000ffff) !=
- ~((short)((header & 0xffff0000) >> 16))) {
+ if ((header & 0x07ff) != ((~header >> 16) & 0x07ff))
netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n");
- }
+
/* get the packet length */
- size = (u16) (header & 0x0000ffff);
+ size = (u16) (header & 0x000007ff);
if ((skb->len) - ((size + 1) & 0xfffe) == 0) {
u8 alignment = (unsigned long)skb->data & 0x3;
@@ -532,11 +536,11 @@ static u16 asix_read_medium_status(struct usbnet *dev)
if (ret < 0) {
netdev_err(dev->net, "Error reading Medium Status register: %02x\n",
ret);
- goto out;
+ return ret; /* TODO: callers not checking for error ret */
}
- ret = le16_to_cpu(v);
-out:
- return ret;
+
+ return le16_to_cpu(v);
+
}
static int asix_write_medium_mode(struct usbnet *dev, u16 mode)
@@ -648,9 +652,17 @@ static u32 asix_get_phyid(struct usbnet *dev)
{
int phy_reg;
u32 phy_id;
+ int i;
- phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1);
- if (phy_reg < 0)
+ /* Poll for the rare case the FW or phy isn't ready yet. */
+ for (i = 0; i < 100; i++) {
+ phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1);
+ if (phy_reg != 0 && phy_reg != 0xFFFF)
+ break;
+ mdelay(1);
+ }
+
+ if (phy_reg <= 0 || phy_reg == 0xFFFF)
return 0;
phy_id = (phy_reg & 0xffff) << 16;
@@ -677,12 +689,6 @@ asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
}
wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
wolinfo->wolopts = 0;
- if (opt & AX_MONITOR_MODE) {
- if (opt & AX_MONITOR_LINK)
- wolinfo->wolopts |= WAKE_PHY;
- if (opt & AX_MONITOR_MAGIC)
- wolinfo->wolopts |= WAKE_MAGIC;
- }
}
static int
@@ -695,8 +701,6 @@ asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
opt |= AX_MONITOR_LINK;
if (wolinfo->wolopts & WAKE_MAGIC)
opt |= AX_MONITOR_MAGIC;
- if (opt != 0)
- opt |= AX_MONITOR_MODE;
if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE,
opt, 0, 0, NULL) < 0)
@@ -745,7 +749,7 @@ static void asix_get_drvinfo (struct net_device *net,
/* Inherit standard device info */
usbnet_get_drvinfo(net, info);
- strncpy (info->driver, driver_name, sizeof info->driver);
+ strncpy (info->driver, DRIVER_NAME, sizeof info->driver);
strncpy (info->version, DRIVER_VERSION, sizeof info->version);
info->eedump_len = data->eeprom_len;
}
@@ -873,7 +877,7 @@ static const struct net_device_ops ax88172_netdev_ops = {
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = asix_ioctl,
- .ndo_set_multicast_list = ax88172_set_multicast,
+ .ndo_set_rx_mode = ax88172_set_multicast,
};
static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
@@ -890,19 +894,20 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
/* Toggle the GPIOs in a manufacturer/model specific way */
for (i = 2; i >= 0; i--) {
- if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
- (gpio_bits >> (i * 8)) & 0xff, 0, 0,
- NULL)) < 0)
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+ (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL);
+ if (ret < 0)
goto out;
msleep(5);
}
- if ((ret = asix_write_rx_ctl(dev, 0x80)) < 0)
+ ret = asix_write_rx_ctl(dev, 0x80);
+ if (ret < 0)
goto out;
/* Get the MAC address */
- if ((ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID,
- 0, 0, ETH_ALEN, buf)) < 0) {
+ ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
+ if (ret < 0) {
dbg("read AX_CMD_READ_NODE_ID failed: %d", ret);
goto out;
}
@@ -967,117 +972,88 @@ static int ax88772_link_reset(struct usbnet *dev)
return 0;
}
-static const struct net_device_ops ax88772_netdev_ops = {
- .ndo_open = usbnet_open,
- .ndo_stop = usbnet_stop,
- .ndo_start_xmit = usbnet_start_xmit,
- .ndo_tx_timeout = usbnet_tx_timeout,
- .ndo_change_mtu = usbnet_change_mtu,
- .ndo_set_mac_address = asix_set_mac_address,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_do_ioctl = asix_ioctl,
- .ndo_set_multicast_list = asix_set_multicast,
-};
-
-static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
+static int ax88772_reset(struct usbnet *dev)
{
int ret, embd_phy;
u16 rx_ctl;
- struct asix_data *data = (struct asix_data *)&dev->data;
- u8 buf[ETH_ALEN];
- u32 phyid;
-
- data->eeprom_len = AX88772_EEPROM_LEN;
-
- usbnet_get_endpoints(dev,intf);
- if ((ret = asix_write_gpio(dev,
- AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5)) < 0)
+ ret = asix_write_gpio(dev,
+ AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5);
+ if (ret < 0)
goto out;
- /* 0x10 is the phy id of the embedded 10/100 ethernet phy */
embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0);
- if ((ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
- embd_phy, 0, 0, NULL)) < 0) {
+
+ ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL);
+ if (ret < 0) {
dbg("Select PHY #1 failed: %d", ret);
goto out;
}
- if ((ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL)) < 0)
+ ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL);
+ if (ret < 0)
goto out;
msleep(150);
- if ((ret = asix_sw_reset(dev, AX_SWRESET_CLEAR)) < 0)
+
+ ret = asix_sw_reset(dev, AX_SWRESET_CLEAR);
+ if (ret < 0)
goto out;
msleep(150);
+
if (embd_phy) {
- if ((ret = asix_sw_reset(dev, AX_SWRESET_IPRL)) < 0)
+ ret = asix_sw_reset(dev, AX_SWRESET_IPRL);
+ if (ret < 0)
goto out;
- }
- else {
- if ((ret = asix_sw_reset(dev, AX_SWRESET_PRTE)) < 0)
+ } else {
+ ret = asix_sw_reset(dev, AX_SWRESET_PRTE);
+ if (ret < 0)
goto out;
}
msleep(150);
rx_ctl = asix_read_rx_ctl(dev);
dbg("RX_CTL is 0x%04x after software reset", rx_ctl);
- if ((ret = asix_write_rx_ctl(dev, 0x0000)) < 0)
+ ret = asix_write_rx_ctl(dev, 0x0000);
+ if (ret < 0)
goto out;
rx_ctl = asix_read_rx_ctl(dev);
dbg("RX_CTL is 0x%04x setting to 0x0000", rx_ctl);
- /* Get the MAC address */
- if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
- 0, 0, ETH_ALEN, buf)) < 0) {
- dbg("Failed to read MAC address: %d", ret);
- goto out;
- }
- memcpy(dev->net->dev_addr, buf, ETH_ALEN);
-
- /* Initialize MII structure */
- dev->mii.dev = dev->net;
- dev->mii.mdio_read = asix_mdio_read;
- dev->mii.mdio_write = asix_mdio_write;
- dev->mii.phy_id_mask = 0x1f;
- dev->mii.reg_num_mask = 0x1f;
- dev->mii.phy_id = asix_get_phy_addr(dev);
-
- phyid = asix_get_phyid(dev);
- dbg("PHYID=0x%08x", phyid);
-
- if ((ret = asix_sw_reset(dev, AX_SWRESET_PRL)) < 0)
+ ret = asix_sw_reset(dev, AX_SWRESET_PRL);
+ if (ret < 0)
goto out;
msleep(150);
- if ((ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL)) < 0)
+ ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL);
+ if (ret < 0)
goto out;
msleep(150);
- dev->net->netdev_ops = &ax88772_netdev_ops;
- dev->net->ethtool_ops = &ax88772_ethtool_ops;
-
asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA);
mii_nway_restart(&dev->mii);
- if ((ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT)) < 0)
+ ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT);
+ if (ret < 0)
goto out;
- if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
- AX88772_IPG2_DEFAULT, 0, NULL)) < 0) {
+ AX88772_IPG2_DEFAULT, 0, NULL);
+ if (ret < 0) {
dbg("Write IPG,IPG1,IPG2 failed: %d", ret);
goto out;
}
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
- if ((ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL)) < 0)
+ ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
+ if (ret < 0)
goto out;
rx_ctl = asix_read_rx_ctl(dev);
@@ -1086,16 +1062,90 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
rx_ctl = asix_read_medium_status(dev);
dbg("Medium Status is 0x%04x after all initializations", rx_ctl);
+ return 0;
+
+out:
+ return ret;
+
+}
+
+static const struct net_device_ops ax88772_netdev_ops = {
+ .ndo_open = usbnet_open,
+ .ndo_stop = usbnet_stop,
+ .ndo_start_xmit = usbnet_start_xmit,
+ .ndo_tx_timeout = usbnet_tx_timeout,
+ .ndo_change_mtu = usbnet_change_mtu,
+ .ndo_set_mac_address = asix_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = asix_ioctl,
+ .ndo_set_rx_mode = asix_set_multicast,
+};
+
+static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ int ret, embd_phy;
+ struct asix_data *data = (struct asix_data *)&dev->data;
+ u8 buf[ETH_ALEN];
+ u32 phyid;
+
+ data->eeprom_len = AX88772_EEPROM_LEN;
+
+ usbnet_get_endpoints(dev,intf);
+
+ /* Get the MAC address */
+ ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
+ if (ret < 0) {
+ dbg("Failed to read MAC address: %d", ret);
+ return ret;
+ }
+ memcpy(dev->net->dev_addr, buf, ETH_ALEN);
+
+ /* Initialize MII structure */
+ dev->mii.dev = dev->net;
+ dev->mii.mdio_read = asix_mdio_read;
+ dev->mii.mdio_write = asix_mdio_write;
+ dev->mii.phy_id_mask = 0x1f;
+ dev->mii.reg_num_mask = 0x1f;
+ dev->mii.phy_id = asix_get_phy_addr(dev);
+
+ dev->net->netdev_ops = &ax88772_netdev_ops;
+ dev->net->ethtool_ops = &ax88772_ethtool_ops;
+
+ embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
+
+ /* Reset the PHY to normal operation mode */
+ ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL);
+ if (ret < 0) {
+ dbg("Select PHY #1 failed: %d", ret);
+ return ret;
+ }
+
+ ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL);
+ if (ret < 0)
+ return ret;
+
+ msleep(150);
+
+ ret = asix_sw_reset(dev, AX_SWRESET_CLEAR);
+ if (ret < 0)
+ return ret;
+
+ msleep(150);
+
+ ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_PRTE);
+
+ /* Read PHYID register *AFTER* the PHY was reset properly */
+ phyid = asix_get_phyid(dev);
+ dbg("PHYID=0x%08x", phyid);
+
/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
if (dev->driver_info->flags & FLAG_FRAMING_AX) {
/* hard_mtu is still the default - the device does not support
jumbo eth frames */
dev->rx_urb_size = 2048;
}
- return 0;
-out:
- return ret;
+ return 0;
}
static struct ethtool_ops ax88178_ethtool_ops = {
@@ -1144,6 +1194,27 @@ static int marvell_phy_init(struct usbnet *dev)
return 0;
}
+static int rtl8211cl_phy_init(struct usbnet *dev)
+{
+ struct asix_data *data = (struct asix_data *)&dev->data;
+
+ netdev_dbg(dev->net, "rtl8211cl_phy_init()\n");
+
+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0005);
+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x0c, 0);
+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x01,
+ asix_mdio_read (dev->net, dev->mii.phy_id, 0x01) | 0x0080);
+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0);
+
+ if (data->ledmode == 12) {
+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0002);
+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1a, 0x00cb);
+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0);
+ }
+
+ return 0;
+}
+
static int marvell_led_status(struct usbnet *dev, u16 speed)
{
u16 reg = asix_mdio_read(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL);
@@ -1170,6 +1241,88 @@ static int marvell_led_status(struct usbnet *dev, u16 speed)
return 0;
}
+static int ax88178_reset(struct usbnet *dev)
+{
+ struct asix_data *data = (struct asix_data *)&dev->data;
+ int ret;
+ __le16 eeprom;
+ u8 status;
+ int gpio0 = 0;
+ u32 phyid;
+
+ asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status);
+ dbg("GPIO Status: 0x%04x", status);
+
+ asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL);
+ asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom);
+ asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL);
+
+ dbg("EEPROM index 0x17 is 0x%04x", eeprom);
+
+ if (eeprom == cpu_to_le16(0xffff)) {
+ data->phymode = PHY_MODE_MARVELL;
+ data->ledmode = 0;
+ gpio0 = 1;
+ } else {
+ data->phymode = le16_to_cpu(eeprom) & 0x7F;
+ data->ledmode = le16_to_cpu(eeprom) >> 8;
+ gpio0 = (le16_to_cpu(eeprom) & 0x80) ? 0 : 1;
+ }
+ dbg("GPIO0: %d, PhyMode: %d", gpio0, data->phymode);
+
+ /* Power up external GigaPHY through AX88178 GPIO pin */
+ asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40);
+ if ((le16_to_cpu(eeprom) >> 8) != 1) {
+ asix_write_gpio(dev, 0x003c, 30);
+ asix_write_gpio(dev, 0x001c, 300);
+ asix_write_gpio(dev, 0x003c, 30);
+ } else {
+ dbg("gpio phymode == 1 path");
+ asix_write_gpio(dev, AX_GPIO_GPO1EN, 30);
+ asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30);
+ }
+
+ /* Read PHYID register *AFTER* powering up PHY */
+ phyid = asix_get_phyid(dev);
+ dbg("PHYID=0x%08x", phyid);
+
+ /* Set AX88178 to enable MII/GMII/RGMII interface for external PHY */
+ asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL);
+
+ asix_sw_reset(dev, 0);
+ msleep(150);
+
+ asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
+ msleep(150);
+
+ asix_write_rx_ctl(dev, 0);
+
+ if (data->phymode == PHY_MODE_MARVELL) {
+ marvell_phy_init(dev);
+ msleep(60);
+ } else if (data->phymode == PHY_MODE_RTL8211CL)
+ rtl8211cl_phy_init(dev);
+
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
+ BMCR_RESET | BMCR_ANENABLE);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000,
+ ADVERTISE_1000FULL);
+
+ mii_nway_restart(&dev->mii);
+
+ ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT);
+ if (ret < 0)
+ return ret;
+
+ ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int ax88178_link_reset(struct usbnet *dev)
{
u16 mode;
@@ -1271,67 +1424,26 @@ static const struct net_device_ops ax88178_netdev_ops = {
.ndo_tx_timeout = usbnet_tx_timeout,
.ndo_set_mac_address = asix_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = asix_set_multicast,
+ .ndo_set_rx_mode = asix_set_multicast,
.ndo_do_ioctl = asix_ioctl,
.ndo_change_mtu = ax88178_change_mtu,
};
static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
{
- struct asix_data *data = (struct asix_data *)&dev->data;
int ret;
u8 buf[ETH_ALEN];
- __le16 eeprom;
- u8 status;
- int gpio0 = 0;
- u32 phyid;
-
- usbnet_get_endpoints(dev,intf);
-
- asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status);
- dbg("GPIO Status: 0x%04x", status);
-
- asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL);
- asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom);
- asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL);
-
- dbg("EEPROM index 0x17 is 0x%04x", eeprom);
-
- if (eeprom == cpu_to_le16(0xffff)) {
- data->phymode = PHY_MODE_MARVELL;
- data->ledmode = 0;
- gpio0 = 1;
- } else {
- data->phymode = le16_to_cpu(eeprom) & 7;
- data->ledmode = le16_to_cpu(eeprom) >> 8;
- gpio0 = (le16_to_cpu(eeprom) & 0x80) ? 0 : 1;
- }
- dbg("GPIO0: %d, PhyMode: %d", gpio0, data->phymode);
-
- asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40);
- if ((le16_to_cpu(eeprom) >> 8) != 1) {
- asix_write_gpio(dev, 0x003c, 30);
- asix_write_gpio(dev, 0x001c, 300);
- asix_write_gpio(dev, 0x003c, 30);
- } else {
- dbg("gpio phymode == 1 path");
- asix_write_gpio(dev, AX_GPIO_GPO1EN, 30);
- asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30);
- }
-
- asix_sw_reset(dev, 0);
- msleep(150);
+ struct asix_data *data = (struct asix_data *)&dev->data;
- asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
- msleep(150);
+ data->eeprom_len = AX88772_EEPROM_LEN;
- asix_write_rx_ctl(dev, 0);
+ usbnet_get_endpoints(dev,intf);
/* Get the MAC address */
- if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
- 0, 0, ETH_ALEN, buf)) < 0) {
+ ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
+ if (ret < 0) {
dbg("Failed to read MAC address: %d", ret);
- goto out;
+ return ret;
}
memcpy(dev->net->dev_addr, buf, ETH_ALEN);
@@ -1347,28 +1459,12 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
dev->net->netdev_ops = &ax88178_netdev_ops;
dev->net->ethtool_ops = &ax88178_ethtool_ops;
- phyid = asix_get_phyid(dev);
- dbg("PHYID=0x%08x", phyid);
-
- if (data->phymode == PHY_MODE_MARVELL) {
- marvell_phy_init(dev);
- msleep(60);
- }
-
- asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
- BMCR_RESET | BMCR_ANENABLE);
- asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
- ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
- asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000,
- ADVERTISE_1000FULL);
-
- mii_nway_restart(&dev->mii);
-
- if ((ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT)) < 0)
- goto out;
+ /* Blink LEDS so users know driver saw dongle */
+ asix_sw_reset(dev, 0);
+ msleep(150);
- if ((ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL)) < 0)
- goto out;
+ asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
+ msleep(150);
/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
if (dev->driver_info->flags & FLAG_FRAMING_AX) {
@@ -1376,10 +1472,8 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
jumbo eth frames */
dev->rx_urb_size = 2048;
}
- return 0;
-out:
- return ret;
+ return 0;
}
static const struct driver_info ax8817x_info = {
@@ -1427,7 +1521,7 @@ static const struct driver_info ax88772_info = {
.bind = ax88772_bind,
.status = asix_status,
.link_reset = ax88772_link_reset,
- .reset = ax88772_link_reset,
+ .reset = ax88772_reset,
.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR,
.rx_fixup = asix_rx_fixup,
.tx_fixup = asix_tx_fixup,
@@ -1438,7 +1532,7 @@ static const struct driver_info ax88178_info = {
.bind = ax88178_bind,
.status = asix_status,
.link_reset = ax88178_link_reset,
- .reset = ax88178_link_reset,
+ .reset = ax88178_reset,
.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR,
.rx_fixup = asix_rx_fixup,
.tx_fixup = asix_tx_fixup,
@@ -1502,6 +1596,10 @@ static const struct usb_device_id products [] = {
USB_DEVICE (0x04f1, 0x3008),
.driver_info = (unsigned long) &ax8817x_info,
}, {
+ // ASIX AX88772B 10/100
+ USB_DEVICE (0x0b95, 0x772b),
+ .driver_info = (unsigned long) &ax88772_info,
+}, {
// ASIX AX88772 10/100
USB_DEVICE (0x0b95, 0x7720),
.driver_info = (unsigned long) &ax88772_info,
@@ -1563,7 +1661,7 @@ static const struct usb_device_id products [] = {
MODULE_DEVICE_TABLE(usb, products);
static struct usb_driver asix_driver = {
- .name = "asix",
+ .name = DRIVER_NAME,
.id_table = products,
.probe = usbnet_probe,
.suspend = usbnet_suspend,
@@ -1585,6 +1683,7 @@ static void __exit asix_exit(void)
module_exit(asix_exit);
MODULE_AUTHOR("David Hollis");
+MODULE_VERSION(DRIVER_VERSION);
MODULE_DESCRIPTION("ASIX AX8817X based USB 2.0 Ethernet Devices");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c
index 8056f8a27c6a..a68272c93381 100644
--- a/drivers/net/usb/catc.c
+++ b/drivers/net/usb/catc.c
@@ -749,7 +749,7 @@ static const struct net_device_ops catc_netdev_ops = {
.ndo_start_xmit = catc_start_xmit,
.ndo_tx_timeout = catc_tx_timeout,
- .ndo_set_multicast_list = catc_set_multicast_list,
+ .ndo_set_rx_mode = catc_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
index f967913e11bc..a60d0069cc45 100644
--- a/drivers/net/usb/cdc-phonet.c
+++ b/drivers/net/usb/cdc-phonet.c
@@ -21,6 +21,7 @@
*/
#include <linux/kernel.h>
+#include <linux/mm.h>
#include <linux/module.h>
#include <linux/gfp.h>
#include <linux/usb.h>
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index c924ea2bce07..99ed6eb4dfaf 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -567,7 +567,7 @@ static const struct usb_device_id products [] = {
{
USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM,
USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long)&wwan_info,
+ .driver_info = 0,
},
/*
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index f33ca6aa29e9..f06fb78383a1 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -47,14 +47,13 @@
#include <linux/mii.h>
#include <linux/crc32.h>
#include <linux/usb.h>
-#include <linux/version.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/atomic.h>
#include <linux/usb/usbnet.h>
#include <linux/usb/cdc.h>
-#define DRIVER_VERSION "01-June-2011"
+#define DRIVER_VERSION "04-Aug-2011"
/* CDC NCM subclass 3.2.1 */
#define USB_CDC_NCM_NDP16_LENGTH_MIN 0x10
@@ -164,35 +163,8 @@ cdc_ncm_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info));
}
-static int
-cdc_ncm_do_request(struct cdc_ncm_ctx *ctx, struct usb_cdc_notification *req,
- void *data, u16 flags, u16 *actlen, u16 timeout)
-{
- int err;
-
- err = usb_control_msg(ctx->udev, (req->bmRequestType & USB_DIR_IN) ?
- usb_rcvctrlpipe(ctx->udev, 0) :
- usb_sndctrlpipe(ctx->udev, 0),
- req->bNotificationType, req->bmRequestType,
- req->wValue,
- req->wIndex, data,
- req->wLength, timeout);
-
- if (err < 0) {
- if (actlen)
- *actlen = 0;
- return err;
- }
-
- if (actlen)
- *actlen = err;
-
- return 0;
-}
-
static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
{
- struct usb_cdc_notification req;
u32 val;
u8 flags;
u8 iface_no;
@@ -201,14 +173,14 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_GET_NTB_PARAMETERS;
- req.wValue = 0;
- req.wIndex = cpu_to_le16(iface_no);
- req.wLength = cpu_to_le16(sizeof(ctx->ncm_parm));
-
- err = cdc_ncm_do_request(ctx, &req, &ctx->ncm_parm, 0, NULL, 1000);
- if (err) {
+ err = usb_control_msg(ctx->udev,
+ usb_rcvctrlpipe(ctx->udev, 0),
+ USB_CDC_GET_NTB_PARAMETERS,
+ USB_TYPE_CLASS | USB_DIR_IN
+ | USB_RECIP_INTERFACE,
+ 0, iface_no, &ctx->ncm_parm,
+ sizeof(ctx->ncm_parm), 10000);
+ if (err < 0) {
pr_debug("failed GET_NTB_PARAMETERS\n");
return 1;
}
@@ -254,31 +226,43 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
/* inform device about NTB input size changes */
if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) {
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
- USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_SET_NTB_INPUT_SIZE;
- req.wValue = 0;
- req.wIndex = cpu_to_le16(iface_no);
if (flags & USB_CDC_NCM_NCAP_NTB_INPUT_SIZE) {
- struct usb_cdc_ncm_ndp_input_size ndp_in_sz;
-
- req.wLength = 8;
- ndp_in_sz.dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
- ndp_in_sz.wNtbInMaxDatagrams =
- cpu_to_le16(CDC_NCM_DPT_DATAGRAMS_MAX);
- ndp_in_sz.wReserved = 0;
- err = cdc_ncm_do_request(ctx, &req, &ndp_in_sz, 0, NULL,
- 1000);
- } else {
- __le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
+ struct usb_cdc_ncm_ndp_input_size *ndp_in_sz;
- req.wLength = 4;
- err = cdc_ncm_do_request(ctx, &req, &dwNtbInMaxSize, 0,
- NULL, 1000);
- }
+ ndp_in_sz = kzalloc(sizeof(*ndp_in_sz), GFP_KERNEL);
+ if (!ndp_in_sz) {
+ err = -ENOMEM;
+ goto size_err;
+ }
- if (err)
+ err = usb_control_msg(ctx->udev,
+ usb_sndctrlpipe(ctx->udev, 0),
+ USB_CDC_SET_NTB_INPUT_SIZE,
+ USB_TYPE_CLASS | USB_DIR_OUT
+ | USB_RECIP_INTERFACE,
+ 0, iface_no, ndp_in_sz, 8, 1000);
+ kfree(ndp_in_sz);
+ } else {
+ __le32 *dwNtbInMaxSize;
+ dwNtbInMaxSize = kzalloc(sizeof(*dwNtbInMaxSize),
+ GFP_KERNEL);
+ if (!dwNtbInMaxSize) {
+ err = -ENOMEM;
+ goto size_err;
+ }
+ *dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
+
+ err = usb_control_msg(ctx->udev,
+ usb_sndctrlpipe(ctx->udev, 0),
+ USB_CDC_SET_NTB_INPUT_SIZE,
+ USB_TYPE_CLASS | USB_DIR_OUT
+ | USB_RECIP_INTERFACE,
+ 0, iface_no, dwNtbInMaxSize, 4, 1000);
+ kfree(dwNtbInMaxSize);
+ }
+size_err:
+ if (err < 0)
pr_debug("Setting NTB Input Size failed\n");
}
@@ -333,29 +317,24 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
/* set CRC Mode */
if (flags & USB_CDC_NCM_NCAP_CRC_MODE) {
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
- USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_SET_CRC_MODE;
- req.wValue = cpu_to_le16(USB_CDC_NCM_CRC_NOT_APPENDED);
- req.wIndex = cpu_to_le16(iface_no);
- req.wLength = 0;
-
- err = cdc_ncm_do_request(ctx, &req, NULL, 0, NULL, 1000);
- if (err)
+ err = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0),
+ USB_CDC_SET_CRC_MODE,
+ USB_TYPE_CLASS | USB_DIR_OUT
+ | USB_RECIP_INTERFACE,
+ USB_CDC_NCM_CRC_NOT_APPENDED,
+ iface_no, NULL, 0, 1000);
+ if (err < 0)
pr_debug("Setting CRC mode off failed\n");
}
/* set NTB format, if both formats are supported */
if (ntb_fmt_supported & USB_CDC_NCM_NTH32_SIGN) {
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
- USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_SET_NTB_FORMAT;
- req.wValue = cpu_to_le16(USB_CDC_NCM_NTB16_FORMAT);
- req.wIndex = cpu_to_le16(iface_no);
- req.wLength = 0;
-
- err = cdc_ncm_do_request(ctx, &req, NULL, 0, NULL, 1000);
- if (err)
+ err = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0),
+ USB_CDC_SET_NTB_FORMAT, USB_TYPE_CLASS
+ | USB_DIR_OUT | USB_RECIP_INTERFACE,
+ USB_CDC_NCM_NTB16_FORMAT,
+ iface_no, NULL, 0, 1000);
+ if (err < 0)
pr_debug("Setting NTB format to 16-bit failed\n");
}
@@ -363,23 +342,29 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
/* set Max Datagram Size (MTU) */
if (flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE) {
- __le16 max_datagram_size;
+ __le16 *max_datagram_size;
u16 eth_max_sz = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN |
- USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_GET_MAX_DATAGRAM_SIZE;
- req.wValue = 0;
- req.wIndex = cpu_to_le16(iface_no);
- req.wLength = cpu_to_le16(2);
+ max_datagram_size = kzalloc(sizeof(*max_datagram_size),
+ GFP_KERNEL);
+ if (!max_datagram_size) {
+ err = -ENOMEM;
+ goto max_dgram_err;
+ }
- err = cdc_ncm_do_request(ctx, &req, &max_datagram_size, 0, NULL,
- 1000);
- if (err) {
+ err = usb_control_msg(ctx->udev, usb_rcvctrlpipe(ctx->udev, 0),
+ USB_CDC_GET_MAX_DATAGRAM_SIZE,
+ USB_TYPE_CLASS | USB_DIR_IN
+ | USB_RECIP_INTERFACE,
+ 0, iface_no, max_datagram_size,
+ 2, 1000);
+ if (err < 0) {
pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n",
CDC_NCM_MIN_DATAGRAM_SIZE);
+ kfree(max_datagram_size);
} else {
- ctx->max_datagram_size = le16_to_cpu(max_datagram_size);
+ ctx->max_datagram_size =
+ le16_to_cpu(*max_datagram_size);
/* Check Eth descriptor value */
if (eth_max_sz < CDC_NCM_MAX_DATAGRAM_SIZE) {
if (ctx->max_datagram_size > eth_max_sz)
@@ -396,17 +381,17 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
CDC_NCM_MIN_DATAGRAM_SIZE;
/* if value changed, update device */
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
- USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_SET_MAX_DATAGRAM_SIZE;
- req.wValue = 0;
- req.wIndex = cpu_to_le16(iface_no);
- req.wLength = 2;
- max_datagram_size = cpu_to_le16(ctx->max_datagram_size);
-
- err = cdc_ncm_do_request(ctx, &req, &max_datagram_size,
- 0, NULL, 1000);
- if (err)
+ err = usb_control_msg(ctx->udev,
+ usb_sndctrlpipe(ctx->udev, 0),
+ USB_CDC_SET_MAX_DATAGRAM_SIZE,
+ USB_TYPE_CLASS | USB_DIR_OUT
+ | USB_RECIP_INTERFACE,
+ 0,
+ iface_no, max_datagram_size,
+ 2, 1000);
+ kfree(max_datagram_size);
+max_dgram_err:
+ if (err < 0)
pr_debug("SET_MAX_DATAGRAM_SIZE failed\n");
}
@@ -672,7 +657,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
u32 rem;
u32 offset;
u32 last_offset;
- u16 n = 0;
+ u16 n = 0, index;
u8 ready2send = 0;
/* if there is a remaining skb, it gets priority */
@@ -860,8 +845,8 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
cpu_to_le16(sizeof(ctx->tx_ncm.nth16));
ctx->tx_ncm.nth16.wSequence = cpu_to_le16(ctx->tx_seq);
ctx->tx_ncm.nth16.wBlockLength = cpu_to_le16(last_offset);
- ctx->tx_ncm.nth16.wNdpIndex = ALIGN(sizeof(struct usb_cdc_ncm_nth16),
- ctx->tx_ndp_modulus);
+ index = ALIGN(sizeof(struct usb_cdc_ncm_nth16), ctx->tx_ndp_modulus);
+ ctx->tx_ncm.nth16.wNdpIndex = cpu_to_le16(index);
memcpy(skb_out->data, &(ctx->tx_ncm.nth16), sizeof(ctx->tx_ncm.nth16));
ctx->tx_seq++;
@@ -874,12 +859,11 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
ctx->tx_ncm.ndp16.wLength = cpu_to_le16(rem);
ctx->tx_ncm.ndp16.wNextNdpIndex = 0; /* reserved */
- memcpy(((u8 *)skb_out->data) + ctx->tx_ncm.nth16.wNdpIndex,
+ memcpy(((u8 *)skb_out->data) + index,
&(ctx->tx_ncm.ndp16),
sizeof(ctx->tx_ncm.ndp16));
- memcpy(((u8 *)skb_out->data) + ctx->tx_ncm.nth16.wNdpIndex +
- sizeof(ctx->tx_ncm.ndp16),
+ memcpy(((u8 *)skb_out->data) + index + sizeof(ctx->tx_ncm.ndp16),
&(ctx->tx_ncm.dpe16),
(ctx->tx_curr_frame_num + 1) *
sizeof(struct usb_cdc_ncm_dpe16));
diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c
index 1d93133e9b74..fbc0e4def767 100644
--- a/drivers/net/usb/dm9601.c
+++ b/drivers/net/usb/dm9601.c
@@ -428,7 +428,7 @@ static const struct net_device_ops dm9601_netdev_ops = {
.ndo_change_mtu = usbnet_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = dm9601_ioctl,
- .ndo_set_multicast_list = dm9601_set_multicast,
+ .ndo_set_rx_mode = dm9601_set_multicast,
.ndo_set_mac_address = dm9601_set_mac_address,
};
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index 387ca43f26f4..304fe78ff60e 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -2421,10 +2421,8 @@ static void hso_free_net_device(struct hso_device *hso_dev)
remove_net_device(hso_net->parent);
- if (hso_net->net) {
+ if (hso_net->net)
unregister_netdev(hso_net->net);
- free_netdev(hso_net->net);
- }
/* start freeing */
for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
@@ -2436,6 +2434,9 @@ static void hso_free_net_device(struct hso_device *hso_dev)
kfree(hso_net->mux_bulk_tx_buf);
hso_net->mux_bulk_tx_buf = NULL;
+ if (hso_net->net)
+ free_netdev(hso_net->net);
+
kfree(hso_dev);
}
diff --git a/drivers/net/usb/int51x1.c b/drivers/net/usb/int51x1.c
index be02a25da71a..131ac6c172f6 100644
--- a/drivers/net/usb/int51x1.c
+++ b/drivers/net/usb/int51x1.c
@@ -193,7 +193,7 @@ static const struct net_device_ops int51x1_netdev_ops = {
.ndo_change_mtu = usbnet_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = int51x1_set_multicast,
+ .ndo_set_rx_mode = int51x1_set_multicast,
};
static int int51x1_bind(struct usbnet *dev, struct usb_interface *intf)
diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
index 81126ff85e05..13c1f044b40d 100644
--- a/drivers/net/usb/ipheth.c
+++ b/drivers/net/usb/ipheth.c
@@ -59,6 +59,7 @@
#define USB_PRODUCT_IPHONE_3G 0x1292
#define USB_PRODUCT_IPHONE_3GS 0x1294
#define USB_PRODUCT_IPHONE_4 0x1297
+#define USB_PRODUCT_IPHONE_4_VZW 0x129c
#define IPHETH_USBINTF_CLASS 255
#define IPHETH_USBINTF_SUBCLASS 253
@@ -98,6 +99,10 @@ static struct usb_device_id ipheth_table[] = {
USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4,
IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
IPHETH_USBINTF_PROTO) },
+ { USB_DEVICE_AND_INTERFACE_INFO(
+ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4_VZW,
+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+ IPHETH_USBINTF_PROTO) },
{ }
};
MODULE_DEVICE_TABLE(usb, ipheth_table);
@@ -409,12 +414,6 @@ static void ipheth_tx_timeout(struct net_device *net)
usb_unlink_urb(dev->tx_urb);
}
-static struct net_device_stats *ipheth_stats(struct net_device *net)
-{
- struct ipheth_device *dev = netdev_priv(net);
- return &dev->net->stats;
-}
-
static u32 ipheth_ethtool_op_get_link(struct net_device *net)
{
struct ipheth_device *dev = netdev_priv(net);
@@ -426,11 +425,10 @@ static struct ethtool_ops ops = {
};
static const struct net_device_ops ipheth_netdev_ops = {
- .ndo_open = &ipheth_open,
- .ndo_stop = &ipheth_close,
- .ndo_start_xmit = &ipheth_tx,
- .ndo_tx_timeout = &ipheth_tx_timeout,
- .ndo_get_stats = &ipheth_stats,
+ .ndo_open = ipheth_open,
+ .ndo_stop = ipheth_close,
+ .ndo_start_xmit = ipheth_tx,
+ .ndo_tx_timeout = ipheth_tx_timeout,
};
static int ipheth_probe(struct usb_interface *intf,
diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c
index a9b6c63d54e4..5a6d0f88f43b 100644
--- a/drivers/net/usb/kalmia.c
+++ b/drivers/net/usb/kalmia.c
@@ -100,13 +100,13 @@ kalmia_send_init_packet(struct usbnet *dev, u8 *init_msg, u8 init_msg_len,
static int
kalmia_init_and_get_ethernet_addr(struct usbnet *dev, u8 *ethernet_addr)
{
- const static char init_msg_1[] =
+ static const char init_msg_1[] =
{ 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
0x00, 0x00 };
- const static char init_msg_2[] =
+ static const char init_msg_2[] =
{ 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xf4,
0x00, 0x00 };
- const static int buflen = 28;
+ static const int buflen = 28;
char *usb_buf;
int status;
@@ -239,11 +239,11 @@ kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
* Our task here is to strip off framing, leaving skb with one
* data frame for the usbnet framework code to process.
*/
- const static u8 HEADER_END_OF_USB_PACKET[] =
+ static const u8 HEADER_END_OF_USB_PACKET[] =
{ 0x57, 0x5a, 0x00, 0x00, 0x08, 0x00 };
- const static u8 EXPECTED_UNKNOWN_HEADER_1[] =
+ static const u8 EXPECTED_UNKNOWN_HEADER_1[] =
{ 0x57, 0x43, 0x1e, 0x00, 0x15, 0x02 };
- const static u8 EXPECTED_UNKNOWN_HEADER_2[] =
+ static const u8 EXPECTED_UNKNOWN_HEADER_2[] =
{ 0x57, 0x50, 0x0e, 0x00, 0x00, 0x00 };
int i = 0;
diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c
index ad0298f9b5f9..582ca2dfa5f9 100644
--- a/drivers/net/usb/kaweth.c
+++ b/drivers/net/usb/kaweth.c
@@ -985,7 +985,7 @@ static const struct net_device_ops kaweth_netdev_ops = {
.ndo_stop = kaweth_close,
.ndo_start_xmit = kaweth_start_xmit,
.ndo_tx_timeout = kaweth_tx_timeout,
- .ndo_set_multicast_list = kaweth_set_rx_mode,
+ .ndo_set_rx_mode = kaweth_set_rx_mode,
.ndo_get_stats = kaweth_netdev_stats,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
diff --git a/drivers/net/usb/lg-vl600.c b/drivers/net/usb/lg-vl600.c
index 1d83ccfd7277..9c26c6390d69 100644
--- a/drivers/net/usb/lg-vl600.c
+++ b/drivers/net/usb/lg-vl600.c
@@ -27,6 +27,7 @@
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/inetdevice.h>
+#include <linux/module.h>
/*
* The device has a CDC ACM port for modem control (it claims to be
@@ -89,6 +90,8 @@ static int vl600_bind(struct usbnet *dev, struct usb_interface *intf)
* addresses have no meaning, the destination and the source of every
* packet depend only on whether it is on the IN or OUT endpoint. */
dev->net->flags |= IFF_NOARP;
+ /* IPv6 NDP relies on multicast. Enable it by default. */
+ dev->net->flags |= IFF_MULTICAST;
return ret;
}
@@ -141,10 +144,11 @@ static int vl600_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
}
frame = (struct vl600_frame_hdr *) buf->data;
- /* NOTE: Should check that frame->magic == 0x53544448?
- * Otherwise if we receive garbage at the beginning of the frame
- * we may end up allocating a huge buffer and saving all the
- * future incoming data into it. */
+ /* Yes, check that frame->magic == 0x53544448 (or 0x44544d48),
+ * otherwise we may run out of memory w/a bad packet */
+ if (ntohl(frame->magic) != 0x53544448 &&
+ ntohl(frame->magic) != 0x44544d48)
+ goto error;
if (buf->len < sizeof(*frame) ||
buf->len != le32_to_cpup(&frame->len)) {
@@ -200,6 +204,14 @@ static int vl600_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
} else {
memset(ethhdr->h_source, 0, ETH_ALEN);
memcpy(ethhdr->h_dest, dev->net->dev_addr, ETH_ALEN);
+
+ /* Inbound IPv6 packets have an IPv4 ethertype (0x800)
+ * for some reason. Peek at the L3 header to check
+ * for IPv6 packets, and set the ethertype to IPv6
+ * (0x86dd) so Linux can understand it.
+ */
+ if ((buf->data[sizeof(*ethhdr)] & 0xf0) == 0x60)
+ ethhdr->h_proto = __constant_htons(ETH_P_IPV6);
}
if (count) {
@@ -285,6 +297,11 @@ encapsulate:
* overwrite the remaining fields.
*/
packet = (struct vl600_pkt_hdr *) skb->data;
+ /* The VL600 wants IPv6 packets to have an IPv4 ethertype
+ * Since this modem only supports IPv4 and IPv6, just set all
+ * frames to 0x0800 (ETH_P_IP)
+ */
+ packet->h_proto = htons(ETH_P_IP);
memset(&packet->dummy, 0, sizeof(packet->dummy));
packet->len = cpu_to_le32(orig_len);
@@ -302,7 +319,7 @@ encapsulate:
static const struct driver_info vl600_info = {
.description = "LG VL600 modem",
- .flags = FLAG_ETHER | FLAG_RX_ASSEMBLE,
+ .flags = FLAG_RX_ASSEMBLE | FLAG_WWAN,
.bind = vl600_bind,
.unbind = vl600_unbind,
.status = usbnet_cdc_status,
diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c
index 2b791392e788..db2cb74bf854 100644
--- a/drivers/net/usb/mcs7830.c
+++ b/drivers/net/usb/mcs7830.c
@@ -553,7 +553,7 @@ static const struct net_device_ops mcs7830_netdev_ops = {
.ndo_change_mtu = usbnet_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = mcs7830_ioctl,
- .ndo_set_multicast_list = mcs7830_set_multicast,
+ .ndo_set_rx_mode = mcs7830_set_multicast,
.ndo_set_mac_address = mcs7830_set_mac_address,
};
diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c
index ef3667690b12..769f5090bda1 100644
--- a/drivers/net/usb/pegasus.c
+++ b/drivers/net/usb/pegasus.c
@@ -1476,7 +1476,7 @@ static const struct net_device_ops pegasus_netdev_ops = {
.ndo_stop = pegasus_close,
.ndo_do_ioctl = pegasus_ioctl,
.ndo_start_xmit = pegasus_start_xmit,
- .ndo_set_multicast_list = pegasus_set_multicast,
+ .ndo_set_rx_mode = pegasus_set_multicast,
.ndo_get_stats = pegasus_netdev_stats,
.ndo_tx_timeout = pegasus_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c
index 041fb7d43c4f..bf8c84d0adf2 100644
--- a/drivers/net/usb/rtl8150.c
+++ b/drivers/net/usb/rtl8150.c
@@ -169,26 +169,8 @@ struct rtl8150 {
typedef struct rtl8150 rtl8150_t;
-static void fill_skb_pool(rtl8150_t *);
-static void free_skb_pool(rtl8150_t *);
-static inline struct sk_buff *pull_skb(rtl8150_t *);
-static void rtl8150_disconnect(struct usb_interface *intf);
-static int rtl8150_probe(struct usb_interface *intf,
- const struct usb_device_id *id);
-static int rtl8150_suspend(struct usb_interface *intf, pm_message_t message);
-static int rtl8150_resume(struct usb_interface *intf);
-
static const char driver_name [] = "rtl8150";
-static struct usb_driver rtl8150_driver = {
- .name = driver_name,
- .probe = rtl8150_probe,
- .disconnect = rtl8150_disconnect,
- .id_table = rtl8150_table,
- .suspend = rtl8150_suspend,
- .resume = rtl8150_resume
-};
-
/*
**
** device related part of the code
@@ -333,7 +315,7 @@ static int rtl8150_set_mac_address(struct net_device *netdev, void *p)
/* Write the MAC address into eeprom. Eeprom writes must be word-sized,
so we need to split them up. */
for (i = 0; i * 2 < netdev->addr_len; i++) {
- set_registers(dev, IDR_EEPROM + (i * 2), 2,
+ set_registers(dev, IDR_EEPROM + (i * 2), 2,
netdev->dev_addr + (i * 2));
}
/* Clear the WEPROM bit (preventing accidental eeprom writes). */
@@ -490,44 +472,6 @@ resched:
tasklet_schedule(&dev->tl);
}
-static void rx_fixup(unsigned long data)
-{
- rtl8150_t *dev;
- struct sk_buff *skb;
- int status;
-
- dev = (rtl8150_t *)data;
-
- spin_lock_irq(&dev->rx_pool_lock);
- fill_skb_pool(dev);
- spin_unlock_irq(&dev->rx_pool_lock);
- if (test_bit(RX_URB_FAIL, &dev->flags))
- if (dev->rx_skb)
- goto try_again;
- spin_lock_irq(&dev->rx_pool_lock);
- skb = pull_skb(dev);
- spin_unlock_irq(&dev->rx_pool_lock);
- if (skb == NULL)
- goto tlsched;
- dev->rx_skb = skb;
- usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
- dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
-try_again:
- status = usb_submit_urb(dev->rx_urb, GFP_ATOMIC);
- if (status == -ENODEV) {
- netif_device_detach(dev->netdev);
- } else if (status) {
- set_bit(RX_URB_FAIL, &dev->flags);
- goto tlsched;
- } else {
- clear_bit(RX_URB_FAIL, &dev->flags);
- }
-
- return;
-tlsched:
- tasklet_schedule(&dev->tl);
-}
-
static void write_bulk_callback(struct urb *urb)
{
rtl8150_t *dev;
@@ -665,6 +609,42 @@ static void free_skb_pool(rtl8150_t *dev)
dev_kfree_skb(dev->rx_skb_pool[i]);
}
+static void rx_fixup(unsigned long data)
+{
+ struct rtl8150 *dev = (struct rtl8150 *)data;
+ struct sk_buff *skb;
+ int status;
+
+ spin_lock_irq(&dev->rx_pool_lock);
+ fill_skb_pool(dev);
+ spin_unlock_irq(&dev->rx_pool_lock);
+ if (test_bit(RX_URB_FAIL, &dev->flags))
+ if (dev->rx_skb)
+ goto try_again;
+ spin_lock_irq(&dev->rx_pool_lock);
+ skb = pull_skb(dev);
+ spin_unlock_irq(&dev->rx_pool_lock);
+ if (skb == NULL)
+ goto tlsched;
+ dev->rx_skb = skb;
+ usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
+ dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
+try_again:
+ status = usb_submit_urb(dev->rx_urb, GFP_ATOMIC);
+ if (status == -ENODEV) {
+ netif_device_detach(dev->netdev);
+ } else if (status) {
+ set_bit(RX_URB_FAIL, &dev->flags);
+ goto tlsched;
+ } else {
+ clear_bit(RX_URB_FAIL, &dev->flags);
+ }
+
+ return;
+tlsched:
+ tasklet_schedule(&dev->tl);
+}
+
static int enable_net_traffic(rtl8150_t * dev)
{
u8 cr, tcr, rcr, msr;
@@ -778,7 +758,7 @@ static int rtl8150_open(struct net_device *netdev)
return -ENOMEM;
set_registers(dev, IDR, 6, netdev->dev_addr);
-
+
usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL))) {
@@ -898,8 +878,8 @@ static const struct net_device_ops rtl8150_netdev_ops = {
.ndo_stop = rtl8150_close,
.ndo_do_ioctl = rtl8150_ioctl,
.ndo_start_xmit = rtl8150_start_xmit,
- .ndo_tx_timeout = rtl8150_tx_timeout,
- .ndo_set_multicast_list = rtl8150_set_multicast,
+ .ndo_tx_timeout = rtl8150_tx_timeout,
+ .ndo_set_rx_mode = rtl8150_set_multicast,
.ndo_set_mac_address = rtl8150_set_mac_address,
.ndo_change_mtu = eth_change_mtu,
@@ -929,7 +909,7 @@ static int rtl8150_probe(struct usb_interface *intf,
tasklet_init(&dev->tl, rx_fixup, (unsigned long)dev);
spin_lock_init(&dev->rx_pool_lock);
-
+
dev->udev = udev;
dev->netdev = netdev;
netdev->netdev_ops = &rtl8150_netdev_ops;
@@ -947,7 +927,7 @@ static int rtl8150_probe(struct usb_interface *intf,
}
fill_skb_pool(dev);
set_ethernet_addr(dev);
-
+
usb_set_intfdata(intf, dev);
SET_NETDEV_DEV(netdev, &intf->dev);
if (register_netdev(netdev) != 0) {
@@ -977,7 +957,6 @@ static void rtl8150_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (dev) {
set_bit(RTL8150_UNPLUG, &dev->flags);
- tasklet_disable(&dev->tl);
tasklet_kill(&dev->tl);
unregister_netdev(dev->netdev);
unlink_all_urbs(dev);
@@ -990,6 +969,15 @@ static void rtl8150_disconnect(struct usb_interface *intf)
}
}
+static struct usb_driver rtl8150_driver = {
+ .name = driver_name,
+ .probe = rtl8150_probe,
+ .disconnect = rtl8150_disconnect,
+ .id_table = rtl8150_table,
+ .suspend = rtl8150_suspend,
+ .resume = rtl8150_resume
+};
+
static int __init usb_rtl8150_init(void)
{
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index 15b3d6888ae9..a5b9b12ef268 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -51,6 +51,7 @@
#define USB_VENDOR_ID_SMSC (0x0424)
#define USB_PRODUCT_ID_LAN7500 (0x7500)
#define USB_PRODUCT_ID_LAN7505 (0x7505)
+#define RXW_PADDING 2
#define check_warn(ret, fmt, args...) \
({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); })
@@ -1000,7 +1001,7 @@ static const struct net_device_ops smsc75xx_netdev_ops = {
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = smsc75xx_ioctl,
- .ndo_set_multicast_list = smsc75xx_set_multicast,
+ .ndo_set_rx_mode = smsc75xx_set_multicast,
.ndo_set_features = smsc75xx_set_features,
};
@@ -1088,13 +1089,13 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
memcpy(&rx_cmd_b, skb->data, sizeof(rx_cmd_b));
le32_to_cpus(&rx_cmd_b);
- skb_pull(skb, 4 + NET_IP_ALIGN);
+ skb_pull(skb, 4 + RXW_PADDING);
packet = skb->data;
/* get the packet length */
- size = (rx_cmd_a & RX_CMD_A_LEN) - NET_IP_ALIGN;
- align_count = (4 - ((size + NET_IP_ALIGN) % 4)) % 4;
+ size = (rx_cmd_a & RX_CMD_A_LEN) - RXW_PADDING;
+ align_count = (4 - ((size + RXW_PADDING) % 4)) % 4;
if (unlikely(rx_cmd_a & RX_CMD_A_RED)) {
netif_dbg(dev, rx_err, dev->net,
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index f74f3ce71526..eff67678c5a6 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -972,7 +972,7 @@ static const struct net_device_ops smsc95xx_netdev_ops = {
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = smsc95xx_ioctl,
- .ndo_set_multicast_list = smsc95xx_set_multicast,
+ .ndo_set_rx_mode = smsc95xx_set_multicast,
.ndo_set_features = smsc95xx_set_features,
};
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index ce395fe5de26..fae0fbd8bc88 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -238,6 +238,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
skb->len + sizeof (struct ethhdr), skb->protocol);
memset (skb->cb, 0, sizeof (struct skb_data));
+
+ if (skb_defer_rx_timestamp(skb))
+ return;
+
status = netif_rx (skb);
if (status != NET_RX_SUCCESS)
netif_dbg(dev, rx_err, dev->net,
@@ -1053,6 +1057,9 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
unsigned long flags;
int retval;
+ if (skb)
+ skb_tx_timestamp(skb);
+
// some devices want funky USB-level framing, for
// win32 driver (usually) and/or hardware quirks
if (info->tx_fixup) {
@@ -1470,7 +1477,7 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
if (!dev->suspend_count++) {
spin_lock_irq(&dev->txq.lock);
/* don't autosuspend while transmitting */
- if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) {
+ if (dev->txq.qlen && PMSG_IS_AUTO(message)) {
spin_unlock_irq(&dev->txq.lock);
return -EBUSY;
} else {
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 8461576fa015..ef883e97cee0 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -12,10 +12,12 @@
#include <linux/slab.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
+#include <linux/u64_stats_sync.h>
#include <net/dst.h>
#include <net/xfrm.h>
#include <linux/veth.h>
+#include <linux/module.h>
#define DRV_NAME "veth"
#define DRV_VERSION "1.0"
@@ -24,12 +26,12 @@
#define MAX_MTU 65535 /* Max L3 MTU (arbitrary) */
struct veth_net_stats {
- unsigned long rx_packets;
- unsigned long tx_packets;
- unsigned long rx_bytes;
- unsigned long tx_bytes;
- unsigned long tx_dropped;
- unsigned long rx_dropped;
+ u64 rx_packets;
+ u64 tx_packets;
+ u64 rx_bytes;
+ u64 tx_bytes;
+ u64 rx_dropped;
+ struct u64_stats_sync syncp;
};
struct veth_priv {
@@ -124,9 +126,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
stats = this_cpu_ptr(priv->stats);
rcv_stats = this_cpu_ptr(rcv_priv->stats);
- if (!(rcv->flags & IFF_UP))
- goto tx_drop;
-
/* don't change ip_summed == CHECKSUM_PARTIAL, as that
will cause bad checksum on forwarded packets */
if (skb->ip_summed == CHECKSUM_NONE &&
@@ -137,21 +136,22 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
if (dev_forward_skb(rcv, skb) != NET_RX_SUCCESS)
goto rx_drop;
+ u64_stats_update_begin(&stats->syncp);
stats->tx_bytes += length;
stats->tx_packets++;
+ u64_stats_update_end(&stats->syncp);
+ u64_stats_update_begin(&rcv_stats->syncp);
rcv_stats->rx_bytes += length;
rcv_stats->rx_packets++;
+ u64_stats_update_end(&rcv_stats->syncp);
return NETDEV_TX_OK;
-tx_drop:
- kfree_skb(skb);
- stats->tx_dropped++;
- return NETDEV_TX_OK;
-
rx_drop:
+ u64_stats_update_begin(&rcv_stats->syncp);
rcv_stats->rx_dropped++;
+ u64_stats_update_end(&rcv_stats->syncp);
return NETDEV_TX_OK;
}
@@ -159,32 +159,34 @@ rx_drop:
* general routines
*/
-static struct net_device_stats *veth_get_stats(struct net_device *dev)
+static struct rtnl_link_stats64 *veth_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *tot)
{
- struct veth_priv *priv;
+ struct veth_priv *priv = netdev_priv(dev);
int cpu;
- struct veth_net_stats *stats, total = {0};
-
- priv = netdev_priv(dev);
for_each_possible_cpu(cpu) {
- stats = per_cpu_ptr(priv->stats, cpu);
-
- total.rx_packets += stats->rx_packets;
- total.tx_packets += stats->tx_packets;
- total.rx_bytes += stats->rx_bytes;
- total.tx_bytes += stats->tx_bytes;
- total.tx_dropped += stats->tx_dropped;
- total.rx_dropped += stats->rx_dropped;
+ struct veth_net_stats *stats = per_cpu_ptr(priv->stats, cpu);
+ u64 rx_packets, rx_bytes, rx_dropped;
+ u64 tx_packets, tx_bytes;
+ unsigned int start;
+
+ do {
+ start = u64_stats_fetch_begin_bh(&stats->syncp);
+ rx_packets = stats->rx_packets;
+ tx_packets = stats->tx_packets;
+ rx_bytes = stats->rx_bytes;
+ tx_bytes = stats->tx_bytes;
+ rx_dropped = stats->rx_dropped;
+ } while (u64_stats_fetch_retry_bh(&stats->syncp, start));
+ tot->rx_packets += rx_packets;
+ tot->tx_packets += tx_packets;
+ tot->rx_bytes += rx_bytes;
+ tot->tx_bytes += tx_bytes;
+ tot->rx_dropped += rx_dropped;
}
- dev->stats.rx_packets = total.rx_packets;
- dev->stats.tx_packets = total.tx_packets;
- dev->stats.rx_bytes = total.rx_bytes;
- dev->stats.tx_bytes = total.tx_bytes;
- dev->stats.tx_dropped = total.tx_dropped;
- dev->stats.rx_dropped = total.rx_dropped;
-
- return &dev->stats;
+
+ return tot;
}
static int veth_open(struct net_device *dev)
@@ -254,7 +256,7 @@ static const struct net_device_ops veth_netdev_ops = {
.ndo_stop = veth_close,
.ndo_start_xmit = veth_xmit,
.ndo_change_mtu = veth_change_mtu,
- .ndo_get_stats = veth_get_stats,
+ .ndo_get_stats64 = veth_get_stats64,
.ndo_set_mac_address = eth_mac_addr,
};
@@ -262,6 +264,8 @@ static void veth_setup(struct net_device *dev)
{
ether_setup(dev);
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+
dev->netdev_ops = &veth_netdev_ops;
dev->ethtool_ops = &veth_ethtool_ops;
dev->features |= NETIF_F_LLTX;
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index f6853247a620..6ee8410443c4 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -40,6 +40,15 @@ module_param(gso, bool, 0444);
#define VIRTNET_SEND_COMMAND_SG_MAX 2
+struct virtnet_stats {
+ struct u64_stats_sync syncp;
+ u64 tx_bytes;
+ u64 tx_packets;
+
+ u64 rx_bytes;
+ u64 rx_packets;
+};
+
struct virtnet_info {
struct virtio_device *vdev;
struct virtqueue *rvq, *svq, *cvq;
@@ -56,6 +65,9 @@ struct virtnet_info {
/* Host will merge rx buffers for big packets (shake it! shake it!) */
bool mergeable_rx_bufs;
+ /* Active statistics */
+ struct virtnet_stats __percpu *stats;
+
/* Work struct for refilling if we run low on memory. */
struct delayed_work refill;
@@ -131,18 +143,16 @@ static void skb_xmit_done(struct virtqueue *svq)
static void set_skb_frag(struct sk_buff *skb, struct page *page,
unsigned int offset, unsigned int *len)
{
+ int size = min((unsigned)PAGE_SIZE - offset, *len);
int i = skb_shinfo(skb)->nr_frags;
- skb_frag_t *f;
- f = &skb_shinfo(skb)->frags[i];
- f->size = min((unsigned)PAGE_SIZE - offset, *len);
- f->page_offset = offset;
- f->page = page;
+ __skb_fill_page_desc(skb, i, page, offset, size);
- skb->data_len += f->size;
- skb->len += f->size;
+ skb->data_len += size;
+ skb->len += size;
+ skb->truesize += PAGE_SIZE;
skb_shinfo(skb)->nr_frags++;
- *len -= f->size;
+ *len -= size;
}
static struct sk_buff *page_to_skb(struct virtnet_info *vi,
@@ -183,6 +193,19 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi,
len -= copy;
offset += copy;
+ /*
+ * Verify that we can indeed put this data into a skb.
+ * This is here to handle cases when the device erroneously
+ * tries to receive more than is possible. This is usually
+ * the case of a broken device.
+ */
+ if (unlikely(len > MAX_SKB_FRAGS * PAGE_SIZE)) {
+ if (net_ratelimit())
+ pr_debug("%s: too much data\n", skb->dev->name);
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+
while (len) {
set_skb_frag(skb, page, offset, &len);
page = (struct page *)page->private;
@@ -209,7 +232,6 @@ static int receive_mergeable(struct virtnet_info *vi, struct sk_buff *skb)
skb->dev->stats.rx_length_errors++;
return -EINVAL;
}
-
page = virtqueue_get_buf(vi->rvq, &len);
if (!page) {
pr_debug("%s: rx error: %d buffers missing\n",
@@ -217,6 +239,7 @@ static int receive_mergeable(struct virtnet_info *vi, struct sk_buff *skb)
skb->dev->stats.rx_length_errors++;
return -EINVAL;
}
+
if (len > PAGE_SIZE)
len = PAGE_SIZE;
@@ -230,6 +253,7 @@ static int receive_mergeable(struct virtnet_info *vi, struct sk_buff *skb)
static void receive_buf(struct net_device *dev, void *buf, unsigned int len)
{
struct virtnet_info *vi = netdev_priv(dev);
+ struct virtnet_stats __percpu *stats = this_cpu_ptr(vi->stats);
struct sk_buff *skb;
struct page *page;
struct skb_vnet_hdr *hdr;
@@ -264,9 +288,11 @@ static void receive_buf(struct net_device *dev, void *buf, unsigned int len)
}
hdr = skb_vnet_hdr(skb);
- skb->truesize += skb->data_len;
- dev->stats.rx_bytes += skb->len;
- dev->stats.rx_packets++;
+
+ u64_stats_update_begin(&stats->syncp);
+ stats->rx_bytes += skb->len;
+ stats->rx_packets++;
+ u64_stats_update_end(&stats->syncp);
if (hdr->hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
pr_debug("Needs csum!\n");
@@ -274,6 +300,8 @@ static void receive_buf(struct net_device *dev, void *buf, unsigned int len)
hdr->hdr.csum_start,
hdr->hdr.csum_offset))
goto frame_err;
+ } else if (hdr->hdr.flags & VIRTIO_NET_HDR_F_DATA_VALID) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
}
skb->protocol = eth_type_trans(skb, dev);
@@ -513,11 +541,16 @@ static unsigned int free_old_xmit_skbs(struct virtnet_info *vi)
{
struct sk_buff *skb;
unsigned int len, tot_sgs = 0;
+ struct virtnet_stats __percpu *stats = this_cpu_ptr(vi->stats);
while ((skb = virtqueue_get_buf(vi->svq, &len)) != NULL) {
pr_debug("Sent skb %p\n", skb);
- vi->dev->stats.tx_bytes += skb->len;
- vi->dev->stats.tx_packets++;
+
+ u64_stats_update_begin(&stats->syncp);
+ stats->tx_bytes += skb->len;
+ stats->tx_packets++;
+ u64_stats_update_end(&stats->syncp);
+
tot_sgs += skb_vnet_hdr(skb)->num_sg;
dev_kfree_skb_any(skb);
}
@@ -639,6 +672,40 @@ static int virtnet_set_mac_address(struct net_device *dev, void *p)
return 0;
}
+static struct rtnl_link_stats64 *virtnet_stats(struct net_device *dev,
+ struct rtnl_link_stats64 *tot)
+{
+ struct virtnet_info *vi = netdev_priv(dev);
+ int cpu;
+ unsigned int start;
+
+ for_each_possible_cpu(cpu) {
+ struct virtnet_stats __percpu *stats
+ = per_cpu_ptr(vi->stats, cpu);
+ u64 tpackets, tbytes, rpackets, rbytes;
+
+ do {
+ start = u64_stats_fetch_begin(&stats->syncp);
+ tpackets = stats->tx_packets;
+ tbytes = stats->tx_bytes;
+ rpackets = stats->rx_packets;
+ rbytes = stats->rx_bytes;
+ } while (u64_stats_fetch_retry(&stats->syncp, start));
+
+ tot->rx_packets += rpackets;
+ tot->tx_packets += tpackets;
+ tot->rx_bytes += rbytes;
+ tot->tx_bytes += tbytes;
+ }
+
+ tot->tx_dropped = dev->stats.tx_dropped;
+ tot->rx_dropped = dev->stats.rx_dropped;
+ tot->rx_length_errors = dev->stats.rx_length_errors;
+ tot->rx_frame_errors = dev->stats.rx_frame_errors;
+
+ return tot;
+}
+
#ifdef CONFIG_NET_POLL_CONTROLLER
static void virtnet_netpoll(struct net_device *dev)
{
@@ -810,8 +877,21 @@ static void virtnet_vlan_rx_kill_vid(struct net_device *dev, u16 vid)
dev_warn(&dev->dev, "Failed to kill VLAN ID %d.\n", vid);
}
+static void virtnet_get_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ring)
+{
+ struct virtnet_info *vi = netdev_priv(dev);
+
+ ring->rx_max_pending = virtqueue_get_vring_size(vi->rvq);
+ ring->tx_max_pending = virtqueue_get_vring_size(vi->svq);
+ ring->rx_pending = ring->rx_max_pending;
+ ring->tx_pending = ring->tx_max_pending;
+
+}
+
static const struct ethtool_ops virtnet_ethtool_ops = {
.get_link = ethtool_op_get_link,
+ .get_ringparam = virtnet_get_ringparam,
};
#define MIN_MTU 68
@@ -833,6 +913,7 @@ static const struct net_device_ops virtnet_netdev = {
.ndo_set_mac_address = virtnet_set_mac_address,
.ndo_set_rx_mode = virtnet_set_rx_mode,
.ndo_change_mtu = virtnet_change_mtu,
+ .ndo_get_stats64 = virtnet_stats,
.ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -844,12 +925,10 @@ static void virtnet_update_status(struct virtnet_info *vi)
{
u16 v;
- if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS))
- return;
-
- vi->vdev->config->get(vi->vdev,
+ if (virtio_config_val(vi->vdev, VIRTIO_NET_F_STATUS,
offsetof(struct virtio_net_config, status),
- &v, sizeof(v));
+ &v) < 0)
+ return;
/* Ignore unknown (future) status bits */
v &= VIRTIO_NET_S_LINK_UP;
@@ -891,8 +970,10 @@ static int virtnet_probe(struct virtio_device *vdev)
return -ENOMEM;
/* Set up network device as normal. */
+ dev->priv_flags |= IFF_UNICAST_FLT;
dev->netdev_ops = &virtnet_netdev;
dev->features = NETIF_F_HIGHDMA;
+
SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops);
SET_NETDEV_DEV(dev, &vdev->dev);
@@ -923,11 +1004,9 @@ static int virtnet_probe(struct virtio_device *vdev)
}
/* Configuration may specify what MAC to use. Otherwise random. */
- if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) {
- vdev->config->get(vdev,
+ if (virtio_config_val_len(vdev, VIRTIO_NET_F_MAC,
offsetof(struct virtio_net_config, mac),
- dev->dev_addr, dev->addr_len);
- } else
+ dev->dev_addr, dev->addr_len) < 0)
random_ether_addr(dev->dev_addr);
/* Set up our device-specific information */
@@ -937,6 +1016,11 @@ static int virtnet_probe(struct virtio_device *vdev)
vi->vdev = vdev;
vdev->priv = vi;
vi->pages = NULL;
+ vi->stats = alloc_percpu(struct virtnet_stats);
+ err = -ENOMEM;
+ if (vi->stats == NULL)
+ goto free;
+
INIT_DELAYED_WORK(&vi->refill, refill_work);
sg_init_table(vi->rx_sg, ARRAY_SIZE(vi->rx_sg));
sg_init_table(vi->tx_sg, ARRAY_SIZE(vi->tx_sg));
@@ -956,7 +1040,7 @@ static int virtnet_probe(struct virtio_device *vdev)
err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names);
if (err)
- goto free;
+ goto free_stats;
vi->rvq = vqs[0];
vi->svq = vqs[1];
@@ -1001,6 +1085,8 @@ unregister:
cancel_delayed_work_sync(&vi->refill);
free_vqs:
vdev->config->del_vqs(vdev);
+free_stats:
+ free_percpu(vi->stats);
free:
free_netdev(dev);
return err;
@@ -1047,6 +1133,7 @@ static void __devexit virtnet_remove(struct virtio_device *vdev)
while (vi->pages)
__free_pages(get_a_page(vi, GFP_KERNEL), 0);
+ free_percpu(vi->stats);
free_netdev(vi->dev);
}
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 67402350d0df..d96bfb1ac20b 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -24,6 +24,7 @@
*
*/
+#include <linux/module.h>
#include <net/ip6_checksum.h>
#include "vmxnet3_int.h"
@@ -405,10 +406,8 @@ vmxnet3_tq_cleanup(struct vmxnet3_tx_queue *tq,
while (tq->tx_ring.next2comp != tq->tx_ring.next2fill) {
struct vmxnet3_tx_buf_info *tbi;
- union Vmxnet3_GenericDesc *gdesc;
tbi = tq->buf_info + tq->tx_ring.next2comp;
- gdesc = tq->tx_ring.base + tq->tx_ring.next2comp;
vmxnet3_unmap_tx_buf(tbi, adapter->pdev);
if (tbi->skb) {
@@ -656,10 +655,11 @@ vmxnet3_append_frag(struct sk_buff *skb, struct Vmxnet3_RxCompDesc *rcd,
BUG_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS);
- frag->page = rbi->page;
+ __skb_frag_set_page(frag, rbi->page);
frag->page_offset = 0;
- frag->size = rcd->len;
- skb->data_len += frag->size;
+ skb_frag_size_set(frag, rcd->len);
+ skb->data_len += rcd->len;
+ skb->truesize += PAGE_SIZE;
skb_shinfo(skb)->nr_frags++;
}
@@ -746,21 +746,21 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
}
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
+ const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
tbi = tq->buf_info + tq->tx_ring.next2fill;
tbi->map_type = VMXNET3_MAP_PAGE;
- tbi->dma_addr = pci_map_page(adapter->pdev, frag->page,
- frag->page_offset, frag->size,
- PCI_DMA_TODEVICE);
+ tbi->dma_addr = skb_frag_dma_map(&adapter->pdev->dev, frag,
+ 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
- tbi->len = frag->size;
+ tbi->len = skb_frag_size(frag);
gdesc = tq->tx_ring.base + tq->tx_ring.next2fill;
BUG_ON(gdesc->txd.gen == tq->tx_ring.gen);
gdesc->txd.addr = cpu_to_le64(tbi->dma_addr);
- gdesc->dword[2] = cpu_to_le32(dw2 | frag->size);
+ gdesc->dword[2] = cpu_to_le32(dw2 | skb_frag_size(frag));
gdesc->dword[3] = 0;
dev_dbg(&adapter->netdev->dev,
@@ -926,7 +926,7 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
count = VMXNET3_TXD_NEEDED(skb_headlen(skb)) +
skb_shinfo(skb)->nr_frags + 1;
- ctx.ipv4 = (skb->protocol == cpu_to_be16(ETH_P_IP));
+ ctx.ipv4 = (vlan_get_protocol(skb) == cpu_to_be16(ETH_P_IP));
ctx.mss = skb_shinfo(skb)->gso_size;
if (ctx.mss) {
@@ -1279,18 +1279,18 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
skb = ctx->skb;
if (rcd->eop) {
skb->len += skb->data_len;
- skb->truesize += skb->data_len;
vmxnet3_rx_csum(adapter, skb,
(union Vmxnet3_GenericDesc *)rcd);
skb->protocol = eth_type_trans(skb, adapter->netdev);
- if (unlikely(adapter->vlan_grp && rcd->ts)) {
- vlan_hwaccel_receive_skb(skb,
- adapter->vlan_grp, rcd->tci);
- } else {
+ if (unlikely(rcd->ts))
+ __vlan_hwaccel_put_tag(skb, rcd->tci);
+
+ if (adapter->netdev->features & NETIF_F_LRO)
netif_receive_skb(skb);
- }
+ else
+ napi_gro_receive(&rq->napi, skb);
ctx->skb = NULL;
}
@@ -1911,94 +1911,38 @@ vmxnet3_free_irqs(struct vmxnet3_adapter *adapter)
}
}
+
static void
-vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
+vmxnet3_restore_vlan(struct vmxnet3_adapter *adapter)
{
- struct vmxnet3_adapter *adapter = netdev_priv(netdev);
- struct Vmxnet3_DriverShared *shared = adapter->shared;
u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable;
- unsigned long flags;
-
- if (grp) {
- /* add vlan rx stripping. */
- if (adapter->netdev->features & NETIF_F_HW_VLAN_RX) {
- int i;
- adapter->vlan_grp = grp;
-
- /*
- * Clear entire vfTable; then enable untagged pkts.
- * Note: setting one entry in vfTable to non-zero turns
- * on VLAN rx filtering.
- */
- for (i = 0; i < VMXNET3_VFT_SIZE; i++)
- vfTable[i] = 0;
-
- VMXNET3_SET_VFTABLE_ENTRY(vfTable, 0);
- spin_lock_irqsave(&adapter->cmd_lock, flags);
- VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
- VMXNET3_CMD_UPDATE_VLAN_FILTERS);
- spin_unlock_irqrestore(&adapter->cmd_lock, flags);
- } else {
- printk(KERN_ERR "%s: vlan_rx_register when device has "
- "no NETIF_F_HW_VLAN_RX\n", netdev->name);
- }
- } else {
- /* remove vlan rx stripping. */
- struct Vmxnet3_DSDevRead *devRead = &shared->devRead;
- adapter->vlan_grp = NULL;
+ u16 vid;
- if (devRead->misc.uptFeatures & UPT1_F_RXVLAN) {
- int i;
+ /* allow untagged pkts */
+ VMXNET3_SET_VFTABLE_ENTRY(vfTable, 0);
- for (i = 0; i < VMXNET3_VFT_SIZE; i++) {
- /* clear entire vfTable; this also disables
- * VLAN rx filtering
- */
- vfTable[i] = 0;
- }
- spin_lock_irqsave(&adapter->cmd_lock, flags);
- VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
- VMXNET3_CMD_UPDATE_VLAN_FILTERS);
- spin_unlock_irqrestore(&adapter->cmd_lock, flags);
- }
- }
+ for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
+ VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid);
}
static void
-vmxnet3_restore_vlan(struct vmxnet3_adapter *adapter)
+vmxnet3_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
{
- if (adapter->vlan_grp) {
- u16 vid;
+ struct vmxnet3_adapter *adapter = netdev_priv(netdev);
+
+ if (!(netdev->flags & IFF_PROMISC)) {
u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable;
- bool activeVlan = false;
+ unsigned long flags;
- for (vid = 0; vid < VLAN_N_VID; vid++) {
- if (vlan_group_get_device(adapter->vlan_grp, vid)) {
- VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid);
- activeVlan = true;
- }
- }
- if (activeVlan) {
- /* continue to allow untagged pkts */
- VMXNET3_SET_VFTABLE_ENTRY(vfTable, 0);
- }
+ VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid);
+ spin_lock_irqsave(&adapter->cmd_lock, flags);
+ VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
+ VMXNET3_CMD_UPDATE_VLAN_FILTERS);
+ spin_unlock_irqrestore(&adapter->cmd_lock, flags);
}
-}
-
-static void
-vmxnet3_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
-{
- struct vmxnet3_adapter *adapter = netdev_priv(netdev);
- u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable;
- unsigned long flags;
-
- VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid);
- spin_lock_irqsave(&adapter->cmd_lock, flags);
- VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
- VMXNET3_CMD_UPDATE_VLAN_FILTERS);
- spin_unlock_irqrestore(&adapter->cmd_lock, flags);
+ set_bit(vid, adapter->active_vlans);
}
@@ -2006,14 +1950,19 @@ static void
vmxnet3_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
{
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
- u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable;
- unsigned long flags;
- VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid);
- spin_lock_irqsave(&adapter->cmd_lock, flags);
- VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
- VMXNET3_CMD_UPDATE_VLAN_FILTERS);
- spin_unlock_irqrestore(&adapter->cmd_lock, flags);
+ if (!(netdev->flags & IFF_PROMISC)) {
+ u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable;
+ unsigned long flags;
+
+ VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid);
+ spin_lock_irqsave(&adapter->cmd_lock, flags);
+ VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
+ VMXNET3_CMD_UPDATE_VLAN_FILTERS);
+ spin_unlock_irqrestore(&adapter->cmd_lock, flags);
+ }
+
+ clear_bit(vid, adapter->active_vlans);
}
@@ -2050,8 +1999,14 @@ vmxnet3_set_mc(struct net_device *netdev)
u8 *new_table = NULL;
u32 new_mode = VMXNET3_RXM_UCAST;
- if (netdev->flags & IFF_PROMISC)
+ if (netdev->flags & IFF_PROMISC) {
+ u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable;
+ memset(vfTable, 0, VMXNET3_VFT_SIZE * sizeof(*vfTable));
+
new_mode |= VMXNET3_RXM_PROMISC;
+ } else {
+ vmxnet3_restore_vlan(adapter);
+ }
if (netdev->flags & IFF_BROADCAST)
new_mode |= VMXNET3_RXM_BCAST;
@@ -2085,6 +2040,8 @@ vmxnet3_set_mc(struct net_device *netdev)
rxConf->rxMode = cpu_to_le32(new_mode);
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
VMXNET3_CMD_UPDATE_RX_MODE);
+ VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
+ VMXNET3_CMD_UPDATE_VLAN_FILTERS);
}
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
@@ -2694,12 +2651,13 @@ vmxnet3_declare_features(struct vmxnet3_adapter *adapter, bool dma64)
netdev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM |
NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_TX |
- NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_LRO;
+ NETIF_F_HW_VLAN_RX | NETIF_F_TSO | NETIF_F_TSO6 |
+ NETIF_F_LRO;
if (dma64)
- netdev->features |= NETIF_F_HIGHDMA;
- netdev->vlan_features = netdev->hw_features & ~NETIF_F_HW_VLAN_TX;
- netdev->features = netdev->hw_features |
- NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER;
+ netdev->hw_features |= NETIF_F_HIGHDMA;
+ netdev->vlan_features = netdev->hw_features &
+ ~(NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX);
+ netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_FILTER;
netdev_info(adapter->netdev,
"features: sg csum vlan jf tso tsoIPv6 lro%s\n",
@@ -2917,10 +2875,9 @@ vmxnet3_probe_device(struct pci_dev *pdev,
.ndo_set_mac_address = vmxnet3_set_mac_addr,
.ndo_change_mtu = vmxnet3_change_mtu,
.ndo_set_features = vmxnet3_set_features,
- .ndo_get_stats = vmxnet3_get_stats,
+ .ndo_get_stats64 = vmxnet3_get_stats64,
.ndo_tx_timeout = vmxnet3_tx_timeout,
- .ndo_set_multicast_list = vmxnet3_set_mc,
- .ndo_vlan_rx_register = vmxnet3_vlan_rx_register,
+ .ndo_set_rx_mode = vmxnet3_set_mc,
.ndo_vlan_rx_add_vid = vmxnet3_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = vmxnet3_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -3043,6 +3000,7 @@ vmxnet3_probe_device(struct pci_dev *pdev,
goto err_ver;
}
+ SET_NETDEV_DEV(netdev, &pdev->dev);
vmxnet3_declare_features(adapter, dma64);
adapter->dev_number = atomic_read(&devices_found);
@@ -3088,7 +3046,6 @@ vmxnet3_probe_device(struct pci_dev *pdev,
netif_set_real_num_tx_queues(adapter->netdev, adapter->num_tx_queues);
netif_set_real_num_rx_queues(adapter->netdev, adapter->num_rx_queues);
- SET_NETDEV_DEV(netdev, &pdev->dev);
err = register_netdev(netdev);
if (err) {
diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c
index dc959fe27aa5..e662cbc8bfbd 100644
--- a/drivers/net/vmxnet3/vmxnet3_ethtool.c
+++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c
@@ -113,15 +113,15 @@ vmxnet3_global_stats[] = {
};
-struct net_device_stats *
-vmxnet3_get_stats(struct net_device *netdev)
+struct rtnl_link_stats64 *
+vmxnet3_get_stats64(struct net_device *netdev,
+ struct rtnl_link_stats64 *stats)
{
struct vmxnet3_adapter *adapter;
struct vmxnet3_tq_driver_stats *drvTxStats;
struct vmxnet3_rq_driver_stats *drvRxStats;
struct UPT1_TxStats *devTxStats;
struct UPT1_RxStats *devRxStats;
- struct net_device_stats *net_stats = &netdev->stats;
unsigned long flags;
int i;
@@ -132,36 +132,36 @@ vmxnet3_get_stats(struct net_device *netdev)
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS);
spin_unlock_irqrestore(&adapter->cmd_lock, flags);
- memset(net_stats, 0, sizeof(*net_stats));
for (i = 0; i < adapter->num_tx_queues; i++) {
devTxStats = &adapter->tqd_start[i].stats;
drvTxStats = &adapter->tx_queue[i].stats;
- net_stats->tx_packets += devTxStats->ucastPktsTxOK +
- devTxStats->mcastPktsTxOK +
- devTxStats->bcastPktsTxOK;
- net_stats->tx_bytes += devTxStats->ucastBytesTxOK +
- devTxStats->mcastBytesTxOK +
- devTxStats->bcastBytesTxOK;
- net_stats->tx_errors += devTxStats->pktsTxError;
- net_stats->tx_dropped += drvTxStats->drop_total;
+ stats->tx_packets += devTxStats->ucastPktsTxOK +
+ devTxStats->mcastPktsTxOK +
+ devTxStats->bcastPktsTxOK;
+ stats->tx_bytes += devTxStats->ucastBytesTxOK +
+ devTxStats->mcastBytesTxOK +
+ devTxStats->bcastBytesTxOK;
+ stats->tx_errors += devTxStats->pktsTxError;
+ stats->tx_dropped += drvTxStats->drop_total;
}
for (i = 0; i < adapter->num_rx_queues; i++) {
devRxStats = &adapter->rqd_start[i].stats;
drvRxStats = &adapter->rx_queue[i].stats;
- net_stats->rx_packets += devRxStats->ucastPktsRxOK +
- devRxStats->mcastPktsRxOK +
- devRxStats->bcastPktsRxOK;
+ stats->rx_packets += devRxStats->ucastPktsRxOK +
+ devRxStats->mcastPktsRxOK +
+ devRxStats->bcastPktsRxOK;
- net_stats->rx_bytes += devRxStats->ucastBytesRxOK +
- devRxStats->mcastBytesRxOK +
- devRxStats->bcastBytesRxOK;
+ stats->rx_bytes += devRxStats->ucastBytesRxOK +
+ devRxStats->mcastBytesRxOK +
+ devRxStats->bcastBytesRxOK;
- net_stats->rx_errors += devRxStats->pktsRxError;
- net_stats->rx_dropped += drvRxStats->drop_total;
- net_stats->multicast += devRxStats->mcastPktsRxOK;
+ stats->rx_errors += devRxStats->pktsRxError;
+ stats->rx_dropped += drvRxStats->drop_total;
+ stats->multicast += devRxStats->mcastPktsRxOK;
}
- return net_stats;
+
+ return stats;
}
static int
@@ -268,7 +268,7 @@ int vmxnet3_set_features(struct net_device *netdev, u32 features)
unsigned long flags;
u32 changed = features ^ netdev->features;
- if (changed & (NETIF_F_RXCSUM|NETIF_F_LRO)) {
+ if (changed & (NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_HW_VLAN_RX)) {
if (features & NETIF_F_RXCSUM)
adapter->shared->devRead.misc.uptFeatures |=
UPT1_F_RXCSUM;
@@ -284,6 +284,13 @@ int vmxnet3_set_features(struct net_device *netdev, u32 features)
adapter->shared->devRead.misc.uptFeatures &=
~UPT1_F_LRO;
+ if (features & NETIF_F_HW_VLAN_RX)
+ adapter->shared->devRead.misc.uptFeatures |=
+ UPT1_F_RXVLAN;
+ else
+ adapter->shared->devRead.misc.uptFeatures &=
+ ~UPT1_F_RXVLAN;
+
spin_lock_irqsave(&adapter->cmd_lock, flags);
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
VMXNET3_CMD_UPDATE_FEATURE);
@@ -551,7 +558,7 @@ out:
static int
vmxnet3_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info,
- void *rules)
+ u32 *rules)
{
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
switch (info->cmd) {
diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h
index e08d75e3f170..b18eac1dccaa 100644
--- a/drivers/net/vmxnet3/vmxnet3_int.h
+++ b/drivers/net/vmxnet3/vmxnet3_int.h
@@ -27,6 +27,7 @@
#ifndef _VMXNET3_INT_H
#define _VMXNET3_INT_H
+#include <linux/bitops.h>
#include <linux/ethtool.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
@@ -316,7 +317,7 @@ struct vmxnet3_intr {
struct vmxnet3_adapter {
struct vmxnet3_tx_queue tx_queue[VMXNET3_DEVICE_MAX_TX_QUEUES];
struct vmxnet3_rx_queue rx_queue[VMXNET3_DEVICE_MAX_RX_QUEUES];
- struct vlan_group *vlan_grp;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
struct vmxnet3_intr intr;
spinlock_t cmd_lock;
struct Vmxnet3_DriverShared *shared;
@@ -324,7 +325,6 @@ struct vmxnet3_adapter {
struct Vmxnet3_TxQueueDesc *tqd_start; /* all tx queue desc */
struct Vmxnet3_RxQueueDesc *rqd_start; /* all rx queue desc */
struct net_device *netdev;
- struct net_device_stats net_stats;
struct pci_dev *pdev;
u8 __iomem *hw_addr0; /* for BAR 0 */
@@ -408,7 +408,9 @@ vmxnet3_create_queues(struct vmxnet3_adapter *adapter,
u32 tx_ring_size, u32 rx_ring_size, u32 rx_ring2_size);
extern void vmxnet3_set_ethtool_ops(struct net_device *netdev);
-extern struct net_device_stats *vmxnet3_get_stats(struct net_device *netdev);
+
+extern struct rtnl_link_stats64 *
+vmxnet3_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats);
extern char vmxnet3_driver_name[];
#endif
diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c
index 4ac85a09c5a6..54f995f4a5a3 100644
--- a/drivers/net/wan/c101.c
+++ b/drivers/net/wan/c101.c
@@ -14,6 +14,8 @@
* Moxa C101 User's Manual
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/capability.h>
@@ -313,44 +315,44 @@ static int __init c101_run(unsigned long irq, unsigned long winbase)
int result;
if (irq<3 || irq>15 || irq == 6) /* FIXME */ {
- printk(KERN_ERR "c101: invalid IRQ value\n");
+ pr_err("invalid IRQ value\n");
return -ENODEV;
}
if (winbase < 0xC0000 || winbase > 0xDFFFF || (winbase & 0x3FFF) !=0) {
- printk(KERN_ERR "c101: invalid RAM value\n");
+ pr_err("invalid RAM value\n");
return -ENODEV;
}
card = kzalloc(sizeof(card_t), GFP_KERNEL);
if (card == NULL) {
- printk(KERN_ERR "c101: unable to allocate memory\n");
+ pr_err("unable to allocate memory\n");
return -ENOBUFS;
}
card->dev = alloc_hdlcdev(card);
if (!card->dev) {
- printk(KERN_ERR "c101: unable to allocate memory\n");
+ pr_err("unable to allocate memory\n");
kfree(card);
return -ENOBUFS;
}
if (request_irq(irq, sca_intr, 0, devname, card)) {
- printk(KERN_ERR "c101: could not allocate IRQ\n");
+ pr_err("could not allocate IRQ\n");
c101_destroy_card(card);
return -EBUSY;
}
card->irq = irq;
if (!request_mem_region(winbase, C101_MAPPED_RAM_SIZE, devname)) {
- printk(KERN_ERR "c101: could not request RAM window\n");
+ pr_err("could not request RAM window\n");
c101_destroy_card(card);
return -EBUSY;
}
card->phy_winbase = winbase;
card->win0base = ioremap(winbase, C101_MAPPED_RAM_SIZE);
if (!card->win0base) {
- printk(KERN_ERR "c101: could not map I/O address\n");
+ pr_err("could not map I/O address\n");
c101_destroy_card(card);
return -EFAULT;
}
@@ -381,7 +383,7 @@ static int __init c101_run(unsigned long irq, unsigned long winbase)
result = register_hdlc_device(dev);
if (result) {
- printk(KERN_WARNING "c101: unable to register hdlc device\n");
+ pr_warn("unable to register hdlc device\n");
c101_destroy_card(card);
return result;
}
@@ -389,10 +391,8 @@ static int __init c101_run(unsigned long irq, unsigned long winbase)
sca_init_port(card); /* Set up C101 memory */
set_carrier(card);
- printk(KERN_INFO "%s: Moxa C101 on IRQ%u,"
- " using %u TX + %u RX packets rings\n",
- dev->name, card->irq,
- card->tx_ring_buffers, card->rx_ring_buffers);
+ netdev_info(dev, "Moxa C101 on IRQ%u, using %u TX + %u RX packets rings\n",
+ card->irq, card->tx_ring_buffers, card->rx_ring_buffers);
*new_card = card;
new_card = &card->next_card;
@@ -405,12 +405,12 @@ static int __init c101_init(void)
{
if (hw == NULL) {
#ifdef MODULE
- printk(KERN_INFO "c101: no card initialized\n");
+ pr_info("no card initialized\n");
#endif
return -EINVAL; /* no parameters specified, abort */
}
- printk(KERN_INFO "%s\n", version);
+ pr_info("%s\n", version);
do {
unsigned long irq, ram;
@@ -428,7 +428,7 @@ static int __init c101_init(void)
return first_card ? 0 : -EINVAL;
}while(*hw++ == ':');
- printk(KERN_ERR "c101: invalid hardware parameters\n");
+ pr_err("invalid hardware parameters\n");
return first_card ? 0 : -EINVAL;
}
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index 6fb6f8e667d0..6aed238e573e 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -74,6 +74,8 @@
* The Sync PPP/Cisco HDLC layer (syncppp.c) ported to Linux by Alan Cox
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -361,14 +363,13 @@ static int __init cosa_init(void)
if (cosa_major > 0) {
if (register_chrdev(cosa_major, "cosa", &cosa_fops)) {
- printk(KERN_WARNING "cosa: unable to get major %d\n",
- cosa_major);
+ pr_warn("unable to get major %d\n", cosa_major);
err = -EIO;
goto out;
}
} else {
if (!(cosa_major=register_chrdev(0, "cosa", &cosa_fops))) {
- printk(KERN_WARNING "cosa: unable to register chardev\n");
+ pr_warn("unable to register chardev\n");
err = -EIO;
goto out;
}
@@ -378,7 +379,7 @@ static int __init cosa_init(void)
for (i=0; io[i] != 0 && i < MAX_CARDS; i++)
cosa_probe(io[i], irq[i], dma[i]);
if (!nr_cards) {
- printk(KERN_WARNING "cosa: no devices found.\n");
+ pr_warn("no devices found\n");
unregister_chrdev(cosa_major, "cosa");
err = -ENODEV;
goto out;
@@ -447,26 +448,25 @@ static int cosa_probe(int base, int irq, int dma)
/* Checking validity of parameters: */
/* IRQ should be 2-7 or 10-15; negative IRQ means autoprobe */
if ((irq >= 0 && irq < 2) || irq > 15 || (irq < 10 && irq > 7)) {
- printk (KERN_INFO "cosa_probe: invalid IRQ %d\n", irq);
+ pr_info("invalid IRQ %d\n", irq);
return -1;
}
/* I/O address should be between 0x100 and 0x3ff and should be
* multiple of 8. */
if (base < 0x100 || base > 0x3ff || base & 0x7) {
- printk (KERN_INFO "cosa_probe: invalid I/O address 0x%x\n",
- base);
+ pr_info("invalid I/O address 0x%x\n", base);
return -1;
}
/* DMA should be 0,1 or 3-7 */
if (dma < 0 || dma == 4 || dma > 7) {
- printk (KERN_INFO "cosa_probe: invalid DMA %d\n", dma);
+ pr_info("invalid DMA %d\n", dma);
return -1;
}
/* and finally, on 16-bit COSA DMA should be 4-7 and
* I/O base should not be multiple of 0x10 */
if (((base & 0x8) && dma < 4) || (!(base & 0x8) && dma > 3)) {
- printk (KERN_INFO "cosa_probe: 8/16 bit base and DMA mismatch"
- " (base=0x%x, dma=%d)\n", base, dma);
+ pr_info("8/16 bit base and DMA mismatch (base=0x%x, dma=%d)\n",
+ base, dma);
return -1;
}
@@ -479,7 +479,7 @@ static int cosa_probe(int base, int irq, int dma)
return -1;
if (cosa_reset_and_read_id(cosa, cosa->id_string) < 0) {
- printk(KERN_DEBUG "cosa: probe at 0x%x failed.\n", base);
+ printk(KERN_DEBUG "probe at 0x%x failed.\n", base);
err = -1;
goto err_out;
}
@@ -492,8 +492,7 @@ static int cosa_probe(int base, int irq, int dma)
else {
/* Print a warning only if we are not autoprobing */
#ifndef COSA_ISA_AUTOPROBE
- printk(KERN_INFO "cosa: valid signature not found at 0x%x.\n",
- base);
+ pr_info("valid signature not found at 0x%x\n", base);
#endif
err = -1;
goto err_out;
@@ -501,14 +500,14 @@ static int cosa_probe(int base, int irq, int dma)
/* Update the name of the region now we know the type of card */
release_region(base, is_8bit(cosa)?2:4);
if (!request_region(base, is_8bit(cosa)?2:4, cosa->type)) {
- printk(KERN_DEBUG "cosa: changing name at 0x%x failed.\n", base);
+ printk(KERN_DEBUG "changing name at 0x%x failed.\n", base);
return -1;
}
/* Now do IRQ autoprobe */
if (irq < 0) {
unsigned long irqs;
-/* printk(KERN_INFO "IRQ autoprobe\n"); */
+/* pr_info("IRQ autoprobe\n"); */
irqs = probe_irq_on();
/*
* Enable interrupt on tx buffer empty (it sure is)
@@ -526,13 +525,13 @@ static int cosa_probe(int base, int irq, int dma)
cosa_getdata8(cosa);
if (irq < 0) {
- printk (KERN_INFO "cosa IRQ autoprobe: multiple interrupts obtained (%d, board at 0x%x)\n",
+ pr_info("multiple interrupts obtained (%d, board at 0x%x)\n",
irq, cosa->datareg);
err = -1;
goto err_out;
}
if (irq == 0) {
- printk (KERN_INFO "cosa IRQ autoprobe: no interrupt obtained (board at 0x%x)\n",
+ pr_info("no interrupt obtained (board at 0x%x)\n",
cosa->datareg);
/* return -1; */
}
@@ -579,8 +578,7 @@ static int cosa_probe(int base, int irq, int dma)
/* Register the network interface */
if (!(chan->netdev = alloc_hdlcdev(chan))) {
- printk(KERN_WARNING "%s: alloc_hdlcdev failed.\n",
- chan->name);
+ pr_warn("%s: alloc_hdlcdev failed\n", chan->name);
goto err_hdlcdev;
}
dev_to_hdlc(chan->netdev)->attach = cosa_net_attach;
@@ -591,14 +589,14 @@ static int cosa_probe(int base, int irq, int dma)
chan->netdev->irq = chan->cosa->irq;
chan->netdev->dma = chan->cosa->dma;
if (register_hdlc_device(chan->netdev)) {
- printk(KERN_WARNING "%s: register_hdlc_device()"
- " failed.\n", chan->netdev->name);
+ netdev_warn(chan->netdev,
+ "register_hdlc_device() failed\n");
free_netdev(chan->netdev);
goto err_hdlcdev;
}
}
- printk (KERN_INFO "cosa%d: %s (%s at 0x%x irq %d dma %d), %d channels\n",
+ pr_info("cosa%d: %s (%s at 0x%x irq %d dma %d), %d channels\n",
cosa->num, cosa->id_string, cosa->type,
cosa->datareg, cosa->irq, cosa->dma, cosa->nchannels);
@@ -618,8 +616,7 @@ err_out1:
free_irq(cosa->irq, cosa);
err_out:
release_region(cosa->datareg,is_8bit(cosa)?2:4);
- printk(KERN_NOTICE "cosa%d: allocating resources failed\n",
- cosa->num);
+ pr_notice("cosa%d: allocating resources failed\n", cosa->num);
return err;
}
@@ -641,14 +638,14 @@ static int cosa_net_open(struct net_device *dev)
unsigned long flags;
if (!(chan->cosa->firmware_status & COSA_FW_START)) {
- printk(KERN_NOTICE "%s: start the firmware first (status %d)\n",
- chan->cosa->name, chan->cosa->firmware_status);
+ pr_notice("%s: start the firmware first (status %d)\n",
+ chan->cosa->name, chan->cosa->firmware_status);
return -EPERM;
}
spin_lock_irqsave(&chan->cosa->lock, flags);
if (chan->usage != 0) {
- printk(KERN_WARNING "%s: cosa_net_open called with usage count"
- " %d\n", chan->name, chan->usage);
+ pr_warn("%s: cosa_net_open called with usage count %d\n",
+ chan->name, chan->usage);
spin_unlock_irqrestore(&chan->cosa->lock, flags);
return -EBUSY;
}
@@ -736,8 +733,7 @@ static char *cosa_net_setup_rx(struct channel_data *chan, int size)
kfree_skb(chan->rx_skb);
chan->rx_skb = dev_alloc_skb(size);
if (chan->rx_skb == NULL) {
- printk(KERN_NOTICE "%s: Memory squeeze, dropping packet\n",
- chan->name);
+ pr_notice("%s: Memory squeeze, dropping packet\n", chan->name);
chan->netdev->stats.rx_dropped++;
return NULL;
}
@@ -748,8 +744,7 @@ static char *cosa_net_setup_rx(struct channel_data *chan, int size)
static int cosa_net_rx_done(struct channel_data *chan)
{
if (!chan->rx_skb) {
- printk(KERN_WARNING "%s: rx_done with empty skb!\n",
- chan->name);
+ pr_warn("%s: rx_done with empty skb!\n", chan->name);
chan->netdev->stats.rx_errors++;
chan->netdev->stats.rx_frame_errors++;
return 0;
@@ -768,8 +763,7 @@ static int cosa_net_rx_done(struct channel_data *chan)
static int cosa_net_tx_done(struct channel_data *chan, int size)
{
if (!chan->tx_skb) {
- printk(KERN_WARNING "%s: tx_done with empty skb!\n",
- chan->name);
+ pr_warn("%s: tx_done with empty skb!\n", chan->name);
chan->netdev->stats.tx_errors++;
chan->netdev->stats.tx_aborted_errors++;
return 1;
@@ -794,15 +788,15 @@ static ssize_t cosa_read(struct file *file,
char *kbuf;
if (!(cosa->firmware_status & COSA_FW_START)) {
- printk(KERN_NOTICE "%s: start the firmware first (status %d)\n",
- cosa->name, cosa->firmware_status);
+ pr_notice("%s: start the firmware first (status %d)\n",
+ cosa->name, cosa->firmware_status);
return -EPERM;
}
if (mutex_lock_interruptible(&chan->rlock))
return -ERESTARTSYS;
if ((chan->rxdata = kmalloc(COSA_MTU, GFP_DMA|GFP_KERNEL)) == NULL) {
- printk(KERN_INFO "%s: cosa_read() - OOM\n", cosa->name);
+ pr_info("%s: cosa_read() - OOM\n", cosa->name);
mutex_unlock(&chan->rlock);
return -ENOMEM;
}
@@ -869,8 +863,8 @@ static ssize_t cosa_write(struct file *file,
char *kbuf;
if (!(cosa->firmware_status & COSA_FW_START)) {
- printk(KERN_NOTICE "%s: start the firmware first (status %d)\n",
- cosa->name, cosa->firmware_status);
+ pr_notice("%s: start the firmware first (status %d)\n",
+ cosa->name, cosa->firmware_status);
return -EPERM;
}
if (down_interruptible(&chan->wsem))
@@ -881,8 +875,8 @@ static ssize_t cosa_write(struct file *file,
/* Allocate the buffer */
if ((kbuf = kmalloc(count, GFP_KERNEL|GFP_DMA)) == NULL) {
- printk(KERN_NOTICE "%s: cosa_write() OOM - dropping packet\n",
- cosa->name);
+ pr_notice("%s: cosa_write() OOM - dropping packet\n",
+ cosa->name);
up(&chan->wsem);
return -ENOMEM;
}
@@ -932,7 +926,7 @@ static int chrdev_tx_done(struct channel_data *chan, int size)
static unsigned int cosa_poll(struct file *file, poll_table *poll)
{
- printk(KERN_INFO "cosa_poll is here\n");
+ pr_info("cosa_poll is here\n");
return 0;
}
@@ -1017,15 +1011,14 @@ static inline int cosa_reset(struct cosa_data *cosa)
{
char idstring[COSA_MAX_ID_STRING];
if (cosa->usage > 1)
- printk(KERN_INFO "cosa%d: WARNING: reset requested with cosa->usage > 1 (%d). Odd things may happen.\n",
+ pr_info("cosa%d: WARNING: reset requested with cosa->usage > 1 (%d). Odd things may happen.\n",
cosa->num, cosa->usage);
cosa->firmware_status &= ~(COSA_FW_RESET|COSA_FW_START);
if (cosa_reset_and_read_id(cosa, idstring) < 0) {
- printk(KERN_NOTICE "cosa%d: reset failed\n", cosa->num);
+ pr_notice("cosa%d: reset failed\n", cosa->num);
return -EIO;
}
- printk(KERN_INFO "cosa%d: resetting device: %s\n", cosa->num,
- idstring);
+ pr_info("cosa%d: resetting device: %s\n", cosa->num, idstring);
cosa->firmware_status |= COSA_FW_RESET;
return 0;
}
@@ -1037,11 +1030,11 @@ static inline int cosa_download(struct cosa_data *cosa, void __user *arg)
int i;
if (cosa->usage > 1)
- printk(KERN_INFO "%s: WARNING: download of microcode requested with cosa->usage > 1 (%d). Odd things may happen.\n",
+ pr_info("%s: WARNING: download of microcode requested with cosa->usage > 1 (%d). Odd things may happen.\n",
cosa->name, cosa->usage);
if (!(cosa->firmware_status & COSA_FW_RESET)) {
- printk(KERN_NOTICE "%s: reset the card first (status %d).\n",
- cosa->name, cosa->firmware_status);
+ pr_notice("%s: reset the card first (status %d)\n",
+ cosa->name, cosa->firmware_status);
return -EPERM;
}
@@ -1059,11 +1052,11 @@ static inline int cosa_download(struct cosa_data *cosa, void __user *arg)
i = download(cosa, d.code, d.len, d.addr);
if (i < 0) {
- printk(KERN_NOTICE "cosa%d: microcode download failed: %d\n",
- cosa->num, i);
+ pr_notice("cosa%d: microcode download failed: %d\n",
+ cosa->num, i);
return -EIO;
}
- printk(KERN_INFO "cosa%d: downloading microcode - 0x%04x bytes at 0x%04x\n",
+ pr_info("cosa%d: downloading microcode - 0x%04x bytes at 0x%04x\n",
cosa->num, d.len, d.addr);
cosa->firmware_status |= COSA_FW_RESET|COSA_FW_DOWNLOAD;
return 0;
@@ -1076,12 +1069,11 @@ static inline int cosa_readmem(struct cosa_data *cosa, void __user *arg)
int i;
if (cosa->usage > 1)
- printk(KERN_INFO "cosa%d: WARNING: readmem requested with "
- "cosa->usage > 1 (%d). Odd things may happen.\n",
+ pr_info("cosa%d: WARNING: readmem requested with cosa->usage > 1 (%d). Odd things may happen.\n",
cosa->num, cosa->usage);
if (!(cosa->firmware_status & COSA_FW_RESET)) {
- printk(KERN_NOTICE "%s: reset the card first (status %d).\n",
- cosa->name, cosa->firmware_status);
+ pr_notice("%s: reset the card first (status %d)\n",
+ cosa->name, cosa->firmware_status);
return -EPERM;
}
@@ -1093,11 +1085,10 @@ static inline int cosa_readmem(struct cosa_data *cosa, void __user *arg)
i = readmem(cosa, d.code, d.len, d.addr);
if (i < 0) {
- printk(KERN_NOTICE "cosa%d: reading memory failed: %d\n",
- cosa->num, i);
+ pr_notice("cosa%d: reading memory failed: %d\n", cosa->num, i);
return -EIO;
}
- printk(KERN_INFO "cosa%d: reading card memory - 0x%04x bytes at 0x%04x\n",
+ pr_info("cosa%d: reading card memory - 0x%04x bytes at 0x%04x\n",
cosa->num, d.len, d.addr);
cosa->firmware_status |= COSA_FW_RESET;
return 0;
@@ -1109,23 +1100,22 @@ static inline int cosa_start(struct cosa_data *cosa, int address)
int i;
if (cosa->usage > 1)
- printk(KERN_INFO "cosa%d: WARNING: start microcode requested with cosa->usage > 1 (%d). Odd things may happen.\n",
+ pr_info("cosa%d: WARNING: start microcode requested with cosa->usage > 1 (%d). Odd things may happen.\n",
cosa->num, cosa->usage);
if ((cosa->firmware_status & (COSA_FW_RESET|COSA_FW_DOWNLOAD))
!= (COSA_FW_RESET|COSA_FW_DOWNLOAD)) {
- printk(KERN_NOTICE "%s: download the microcode and/or reset the card first (status %d).\n",
- cosa->name, cosa->firmware_status);
+ pr_notice("%s: download the microcode and/or reset the card first (status %d)\n",
+ cosa->name, cosa->firmware_status);
return -EPERM;
}
cosa->firmware_status &= ~COSA_FW_RESET;
if ((i=startmicrocode(cosa, address)) < 0) {
- printk(KERN_NOTICE "cosa%d: start microcode at 0x%04x failed: %d\n",
- cosa->num, address, i);
+ pr_notice("cosa%d: start microcode at 0x%04x failed: %d\n",
+ cosa->num, address, i);
return -EIO;
}
- printk(KERN_INFO "cosa%d: starting microcode at 0x%04x\n",
- cosa->num, address);
+ pr_info("cosa%d: starting microcode at 0x%04x\n", cosa->num, address);
cosa->startaddr = address;
cosa->firmware_status |= COSA_FW_START;
return 0;
@@ -1255,11 +1245,11 @@ static int cosa_start_tx(struct channel_data *chan, char *buf, int len)
#ifdef DEBUG_DATA
int i;
- printk(KERN_INFO "cosa%dc%d: starting tx(0x%x)", chan->cosa->num,
- chan->num, len);
+ pr_info("cosa%dc%d: starting tx(0x%x)",
+ chan->cosa->num, chan->num, len);
for (i=0; i<len; i++)
- printk(" %02x", buf[i]&0xff);
- printk("\n");
+ pr_cont(" %02x", buf[i]&0xff);
+ pr_cont("\n");
#endif
spin_lock_irqsave(&cosa->lock, flags);
chan->txbuf = buf;
@@ -1353,7 +1343,7 @@ static void cosa_kick(struct cosa_data *cosa)
if (test_bit(TXBIT, &cosa->rxtx))
s = "TX DMA";
- printk(KERN_INFO "%s: %s timeout - restarting.\n", cosa->name, s);
+ pr_info("%s: %s timeout - restarting\n", cosa->name, s);
spin_lock_irqsave(&cosa->lock, flags);
cosa->rxtx = 0;
@@ -1387,7 +1377,7 @@ static int cosa_dma_able(struct channel_data *chan, char *buf, int len)
return 0;
if ((b^ (b+len)) & 0x10000) {
if (count++ < 5)
- printk(KERN_INFO "%s: packet spanning a 64k boundary\n",
+ pr_info("%s: packet spanning a 64k boundary\n",
chan->name);
return 0;
}
@@ -1498,8 +1488,7 @@ static int readmem(struct cosa_data *cosa, char __user *microcode, int length, i
char c;
int i;
if ((i=get_wait_data(cosa)) == -1) {
- printk (KERN_INFO "cosa: 0x%04x bytes remaining\n",
- length);
+ pr_info("0x%04x bytes remaining\n", length);
return -11;
}
c=i;
@@ -1582,14 +1571,15 @@ static int get_wait_data(struct cosa_data *cosa)
short r;
r = cosa_getdata8(cosa);
#if 0
- printk(KERN_INFO "cosa: get_wait_data returning after %d retries\n", 999-retries);
+ pr_info("get_wait_data returning after %d retries\n",
+ 999-retries);
#endif
return r;
}
/* sleep if not ready to read */
schedule_timeout_interruptible(1);
}
- printk(KERN_INFO "cosa: timeout in get_wait_data (status 0x%x)\n",
+ pr_info("timeout in get_wait_data (status 0x%x)\n",
cosa_getstatus(cosa));
return -1;
}
@@ -1607,7 +1597,7 @@ static int put_wait_data(struct cosa_data *cosa, int data)
if (cosa_getstatus(cosa) & SR_TX_RDY) {
cosa_putdata8(cosa, data);
#if 0
- printk(KERN_INFO "Putdata: %d retries\n", 999-retries);
+ pr_info("Putdata: %d retries\n", 999-retries);
#endif
return 0;
}
@@ -1616,7 +1606,7 @@ static int put_wait_data(struct cosa_data *cosa, int data)
schedule_timeout_interruptible(1);
#endif
}
- printk(KERN_INFO "cosa%d: timeout in put_wait_data (status 0x%x)\n",
+ pr_info("cosa%d: timeout in put_wait_data (status 0x%x)\n",
cosa->num, cosa_getstatus(cosa));
return -1;
}
@@ -1636,13 +1626,13 @@ static int puthexnumber(struct cosa_data *cosa, int number)
sprintf(temp, "%04X", number);
for (i=0; i<4; i++) {
if (put_wait_data(cosa, temp[i]) == -1) {
- printk(KERN_NOTICE "cosa%d: puthexnumber failed to write byte %d\n",
- cosa->num, i);
+ pr_notice("cosa%d: puthexnumber failed to write byte %d\n",
+ cosa->num, i);
return -1-2*i;
}
if (get_wait_data(cosa) != temp[i]) {
- printk(KERN_NOTICE "cosa%d: puthexhumber failed to read echo of byte %d\n",
- cosa->num, i);
+ pr_notice("cosa%d: puthexhumber failed to read echo of byte %d\n",
+ cosa->num, i);
return -2-2*i;
}
}
@@ -1687,8 +1677,7 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status)
{
unsigned long flags, flags1;
#ifdef DEBUG_IRQS
- printk(KERN_INFO "cosa%d: SR_DOWN_REQUEST status=0x%04x\n",
- cosa->num, status);
+ pr_info("cosa%d: SR_DOWN_REQUEST status=0x%04x\n", cosa->num, status);
#endif
spin_lock_irqsave(&cosa->lock, flags);
set_bit(TXBIT, &cosa->rxtx);
@@ -1696,8 +1685,7 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status)
/* flow control, see the comment above */
int i=0;
if (!cosa->txbitmap) {
- printk(KERN_WARNING "%s: No channel wants data "
- "in TX IRQ. Expect DMA timeout.",
+ pr_warn("%s: No channel wants data in TX IRQ. Expect DMA timeout.\n",
cosa->name);
put_driver_status_nolock(cosa);
clear_bit(TXBIT, &cosa->rxtx);
@@ -1780,14 +1768,14 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status)
if (cosa->busmaster) {
unsigned long addr = virt_to_bus(cosa->txbuf);
int count=0;
- printk(KERN_INFO "busmaster IRQ\n");
+ pr_info("busmaster IRQ\n");
while (!(cosa_getstatus(cosa)&SR_TX_RDY)) {
count++;
udelay(10);
if (count > 1000) break;
}
- printk(KERN_INFO "status %x\n", cosa_getstatus(cosa));
- printk(KERN_INFO "ready after %d loops\n", count);
+ pr_info("status %x\n", cosa_getstatus(cosa));
+ pr_info("ready after %d loops\n", count);
cosa_putdata16(cosa, (addr >> 16)&0xffff);
count = 0;
@@ -1796,7 +1784,7 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status)
if (count > 1000) break;
udelay(10);
}
- printk(KERN_INFO "ready after %d loops\n", count);
+ pr_info("ready after %d loops\n", count);
cosa_putdata16(cosa, addr &0xffff);
flags1 = claim_dma_lock();
set_dma_mode(cosa->dma, DMA_MODE_CASCADE);
@@ -1824,7 +1812,7 @@ static inline void rx_interrupt(struct cosa_data *cosa, int status)
{
unsigned long flags;
#ifdef DEBUG_IRQS
- printk(KERN_INFO "cosa%d: SR_UP_REQUEST\n", cosa->num);
+ pr_info("cosa%d: SR_UP_REQUEST\n", cosa->num);
#endif
spin_lock_irqsave(&cosa->lock, flags);
@@ -1847,7 +1835,7 @@ static inline void rx_interrupt(struct cosa_data *cosa, int status)
debug_data_in(cosa, cosa->rxsize & 0xff);
#endif
#if 0
- printk(KERN_INFO "cosa%d: receive rxsize = (0x%04x).\n",
+ pr_info("cosa%d: receive rxsize = (0x%04x)\n",
cosa->num, cosa->rxsize);
#endif
}
@@ -1857,12 +1845,12 @@ static inline void rx_interrupt(struct cosa_data *cosa, int status)
debug_data_in(cosa, cosa->rxsize);
#endif
#if 0
- printk(KERN_INFO "cosa%d: receive rxsize = (0x%04x).\n",
+ pr_info("cosa%d: receive rxsize = (0x%04x)\n",
cosa->num, cosa->rxsize);
#endif
}
if (((cosa->rxsize & 0xe000) >> 13) >= cosa->nchannels) {
- printk(KERN_WARNING "%s: rx for unknown channel (0x%04x)\n",
+ pr_warn("%s: rx for unknown channel (0x%04x)\n",
cosa->name, cosa->rxsize);
spin_unlock_irqrestore(&cosa->lock, flags);
goto reject;
@@ -1877,7 +1865,7 @@ static inline void rx_interrupt(struct cosa_data *cosa, int status)
if (!cosa->rxbuf) {
reject: /* Reject the packet */
- printk(KERN_INFO "cosa%d: rejecting packet on channel %d\n",
+ pr_info("cosa%d: rejecting packet on channel %d\n",
cosa->num, cosa->rxchan->num);
cosa->rxbuf = cosa->bouncebuf;
}
@@ -1924,11 +1912,11 @@ static inline void eot_interrupt(struct cosa_data *cosa, int status)
#ifdef DEBUG_DATA
{
int i;
- printk(KERN_INFO "cosa%dc%d: done rx(0x%x)", cosa->num,
- cosa->rxchan->num, cosa->rxsize);
+ pr_info("cosa%dc%d: done rx(0x%x)",
+ cosa->num, cosa->rxchan->num, cosa->rxsize);
for (i=0; i<cosa->rxsize; i++)
- printk (" %02x", cosa->rxbuf[i]&0xff);
- printk("\n");
+ pr_cont(" %02x", cosa->rxbuf[i]&0xff);
+ pr_cont("\n");
}
#endif
/* Packet for unknown channel? */
@@ -1940,8 +1928,7 @@ static inline void eot_interrupt(struct cosa_data *cosa, int status)
if (cosa->rxchan->rx_done(cosa->rxchan))
clear_bit(cosa->rxchan->num, &cosa->rxbitmap);
} else {
- printk(KERN_NOTICE "cosa%d: unexpected EOT interrupt\n",
- cosa->num);
+ pr_notice("cosa%d: unexpected EOT interrupt\n", cosa->num);
}
/*
* Clear the RXBIT, TXBIT and IRQBIT (the latest should be
@@ -1963,8 +1950,7 @@ static irqreturn_t cosa_interrupt(int irq, void *cosa_)
again:
status = cosa_getstatus(cosa);
#ifdef DEBUG_IRQS
- printk(KERN_INFO "cosa%d: got IRQ, status 0x%02x\n", cosa->num,
- status & 0xff);
+ pr_info("cosa%d: got IRQ, status 0x%02x\n", cosa->num, status & 0xff);
#endif
#ifdef DEBUG_IO
debug_status_in(cosa, status);
@@ -1985,15 +1971,15 @@ again:
udelay(100);
goto again;
}
- printk(KERN_INFO "cosa%d: unknown status 0x%02x in IRQ after %d retries\n",
+ pr_info("cosa%d: unknown status 0x%02x in IRQ after %d retries\n",
cosa->num, status & 0xff, count);
}
#ifdef DEBUG_IRQS
if (count)
- printk(KERN_INFO "%s: %d-times got unknown status in IRQ\n",
+ pr_info("%s: %d-times got unknown status in IRQ\n",
cosa->name, count);
else
- printk(KERN_INFO "%s: returning from IRQ\n", cosa->name);
+ pr_info("%s: returning from IRQ\n", cosa->name);
#endif
return IRQ_HANDLED;
}
@@ -2024,41 +2010,41 @@ static void debug_status_in(struct cosa_data *cosa, int status)
s = "NO_REQ";
break;
}
- printk(KERN_INFO "%s: IO: status -> 0x%02x (%s%s%s%s)\n",
+ pr_info("%s: IO: status -> 0x%02x (%s%s%s%s)\n",
cosa->name,
status,
- status & SR_USR_RQ ? "USR_RQ|":"",
- status & SR_TX_RDY ? "TX_RDY|":"",
- status & SR_RX_RDY ? "RX_RDY|":"",
+ status & SR_USR_RQ ? "USR_RQ|" : "",
+ status & SR_TX_RDY ? "TX_RDY|" : "",
+ status & SR_RX_RDY ? "RX_RDY|" : "",
s);
}
static void debug_status_out(struct cosa_data *cosa, int status)
{
- printk(KERN_INFO "%s: IO: status <- 0x%02x (%s%s%s%s%s%s)\n",
+ pr_info("%s: IO: status <- 0x%02x (%s%s%s%s%s%s)\n",
cosa->name,
status,
- status & SR_RX_DMA_ENA ? "RXDMA|":"!rxdma|",
- status & SR_TX_DMA_ENA ? "TXDMA|":"!txdma|",
- status & SR_RST ? "RESET|":"",
- status & SR_USR_INT_ENA ? "USRINT|":"!usrint|",
- status & SR_TX_INT_ENA ? "TXINT|":"!txint|",
- status & SR_RX_INT_ENA ? "RXINT":"!rxint");
+ status & SR_RX_DMA_ENA ? "RXDMA|" : "!rxdma|",
+ status & SR_TX_DMA_ENA ? "TXDMA|" : "!txdma|",
+ status & SR_RST ? "RESET|" : "",
+ status & SR_USR_INT_ENA ? "USRINT|" : "!usrint|",
+ status & SR_TX_INT_ENA ? "TXINT|" : "!txint|",
+ status & SR_RX_INT_ENA ? "RXINT" : "!rxint");
}
static void debug_data_in(struct cosa_data *cosa, int data)
{
- printk(KERN_INFO "%s: IO: data -> 0x%04x\n", cosa->name, data);
+ pr_info("%s: IO: data -> 0x%04x\n", cosa->name, data);
}
static void debug_data_out(struct cosa_data *cosa, int data)
{
- printk(KERN_INFO "%s: IO: data <- 0x%04x\n", cosa->name, data);
+ pr_info("%s: IO: data <- 0x%04x\n", cosa->name, data);
}
static void debug_data_cmd(struct cosa_data *cosa, int data)
{
- printk(KERN_INFO "%s: IO: data <- 0x%04x (%s|%s)\n",
+ pr_info("%s: IO: data <- 0x%04x (%s|%s)\n",
cosa->name, data,
data & SR_RDY_RCV ? "RX_RDY" : "!rx_rdy",
data & SR_RDY_SND ? "TX_RDY" : "!tx_rdy");
diff --git a/drivers/net/wan/cycx_drv.c b/drivers/net/wan/cycx_drv.c
index 164c3624ba89..2a3ecae67a90 100644
--- a/drivers/net/wan/cycx_drv.c
+++ b/drivers/net/wan/cycx_drv.c
@@ -48,6 +48,8 @@
* Aug 8, 1998 acme Initial version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h> /* __init */
#include <linux/module.h>
#include <linux/kernel.h> /* printk(), and other useful stuff */
@@ -81,10 +83,9 @@ static u16 checksum(u8 *buf, u32 len);
/* Global Data */
/* private data */
-static const char modname[] = "cycx_drv";
static const char fullname[] = "Cyclom 2X Support Module";
-static const char copyright[] = "(c) 1998-2003 Arnaldo Carvalho de Melo "
- "<acme@conectiva.com.br>";
+static const char copyright[] =
+ "(c) 1998-2003 Arnaldo Carvalho de Melo <acme@conectiva.com.br>";
/* Hardware configuration options.
* These are arrays of configuration options used by verification routines.
@@ -110,8 +111,8 @@ static const long cycx_2x_irq_options[] = { 7, 3, 5, 9, 10, 11, 12, 15 };
static int __init cycx_drv_init(void)
{
- printk(KERN_INFO "%s v%u.%u %s\n", fullname, MOD_VERSION, MOD_RELEASE,
- copyright);
+ pr_info("%s v%u.%u %s\n",
+ fullname, MOD_VERSION, MOD_RELEASE, copyright);
return 0;
}
@@ -139,18 +140,16 @@ int cycx_setup(struct cycx_hw *hw, void *cfm, u32 len, unsigned long dpmbase)
/* Verify IRQ configuration options */
if (!get_option_index(cycx_2x_irq_options, hw->irq)) {
- printk(KERN_ERR "%s: IRQ %d is invalid!\n", modname, hw->irq);
+ pr_err("IRQ %d is invalid!\n", hw->irq);
return -EINVAL;
}
/* Setup adapter dual-port memory window and test memory */
if (!dpmbase) {
- printk(KERN_ERR "%s: you must specify the dpm address!\n",
- modname);
+ pr_err("you must specify the dpm address!\n");
return -EINVAL;
} else if (!get_option_index(cyc2x_dpmbase_options, dpmbase)) {
- printk(KERN_ERR "%s: memory address 0x%lX is invalid!\n",
- modname, dpmbase);
+ pr_err("memory address 0x%lX is invalid!\n", dpmbase);
return -EINVAL;
}
@@ -158,13 +157,12 @@ int cycx_setup(struct cycx_hw *hw, void *cfm, u32 len, unsigned long dpmbase)
hw->dpmsize = CYCX_WINDOWSIZE;
if (!detect_cyc2x(hw->dpmbase)) {
- printk(KERN_ERR "%s: adapter Cyclom 2X not found at "
- "address 0x%lX!\n", modname, dpmbase);
+ pr_err("adapter Cyclom 2X not found at address 0x%lX!\n",
+ dpmbase);
return -EINVAL;
}
- printk(KERN_INFO "%s: found Cyclom 2X card at address 0x%lX.\n",
- modname, dpmbase);
+ pr_info("found Cyclom 2X card at address 0x%lX\n", dpmbase);
/* Load firmware. If loader fails then shut down adapter */
err = load_cyc2x(hw, cfm, len);
@@ -339,7 +337,7 @@ static int cycx_data_boot(void __iomem *addr, u8 *code, u32 len)
for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ)
if (buffer_load(addr, code + i,
min_t(u32, CFM_LOAD_BUFSZ, (len - i))) < 0) {
- printk(KERN_ERR "%s: Error !!\n", modname);
+ pr_err("Error !!\n");
return -1;
}
@@ -370,7 +368,7 @@ static int cycx_code_boot(void __iomem *addr, u8 *code, u32 len)
for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ)
if (buffer_load(addr, code + i,
min_t(u32, CFM_LOAD_BUFSZ, (len - i)))) {
- printk(KERN_ERR "%s: Error !!\n", modname);
+ pr_err("Error !!\n");
return -1;
}
@@ -391,23 +389,20 @@ static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len)
u16 cksum;
/* Announce */
- printk(KERN_INFO "%s: firmware signature=\"%s\"\n", modname,
- cfm->signature);
+ pr_info("firmware signature=\"%s\"\n", cfm->signature);
/* Verify firmware signature */
if (strcmp(cfm->signature, CFM_SIGNATURE)) {
- printk(KERN_ERR "%s:load_cyc2x: not Cyclom-2X firmware!\n",
- modname);
+ pr_err("load_cyc2x: not Cyclom-2X firmware!\n");
return -EINVAL;
}
- printk(KERN_INFO "%s: firmware version=%u\n", modname, cfm->version);
+ pr_info("firmware version=%u\n", cfm->version);
/* Verify firmware module format version */
if (cfm->version != CFM_VERSION) {
- printk(KERN_ERR "%s:%s: firmware format %u rejected! "
- "Expecting %u.\n",
- modname, __func__, cfm->version, CFM_VERSION);
+ pr_err("%s: firmware format %u rejected! Expecting %u.\n",
+ __func__, cfm->version, CFM_VERSION);
return -EINVAL;
}
@@ -419,23 +414,22 @@ static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len)
if (((len - sizeof(struct cycx_firmware) - 1) != cfm->info.codesize) ||
*/
if (cksum != cfm->checksum) {
- printk(KERN_ERR "%s:%s: firmware corrupted!\n",
- modname, __func__);
- printk(KERN_ERR " cdsize = 0x%x (expected 0x%lx)\n",
- len - (int)sizeof(struct cycx_firmware) - 1,
- cfm->info.codesize);
- printk(KERN_ERR " chksum = 0x%x (expected 0x%x)\n",
- cksum, cfm->checksum);
+ pr_err("%s: firmware corrupted!\n", __func__);
+ pr_err(" cdsize = 0x%x (expected 0x%lx)\n",
+ len - (int)sizeof(struct cycx_firmware) - 1,
+ cfm->info.codesize);
+ pr_err(" chksum = 0x%x (expected 0x%x)\n",
+ cksum, cfm->checksum);
return -EINVAL;
}
/* If everything is ok, set reset, data and code pointers */
img_hdr = (struct cycx_fw_header *)&cfm->image;
#ifdef FIRMWARE_DEBUG
- printk(KERN_INFO "%s:%s: image sizes\n", __func__, modname);
- printk(KERN_INFO " reset=%lu\n", img_hdr->reset_size);
- printk(KERN_INFO " data=%lu\n", img_hdr->data_size);
- printk(KERN_INFO " code=%lu\n", img_hdr->code_size);
+ pr_info("%s: image sizes\n", __func__);
+ pr_info(" reset=%lu\n", img_hdr->reset_size);
+ pr_info(" data=%lu\n", img_hdr->data_size);
+ pr_info(" code=%lu\n", img_hdr->code_size);
#endif
reset_image = ((u8 *)img_hdr) + sizeof(struct cycx_fw_header);
data_image = reset_image + img_hdr->reset_size;
@@ -443,15 +437,14 @@ static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len)
/*---- Start load ----*/
/* Announce */
- printk(KERN_INFO "%s: loading firmware %s (ID=%u)...\n", modname,
- cfm->descr[0] ? cfm->descr : "unknown firmware",
- cfm->info.codeid);
+ pr_info("loading firmware %s (ID=%u)...\n",
+ cfm->descr[0] ? cfm->descr : "unknown firmware",
+ cfm->info.codeid);
for (i = 0 ; i < 5 ; i++) {
/* Reset Cyclom hardware */
if (!reset_cyc2x(hw->dpmbase)) {
- printk(KERN_ERR "%s: dpm problem or board not found\n",
- modname);
+ pr_err("dpm problem or board not found\n");
return -EINVAL;
}
@@ -468,19 +461,19 @@ static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len)
msleep_interruptible(1 * 1000);
}
- printk(KERN_ERR "%s: reset not started.\n", modname);
+ pr_err("reset not started\n");
return -EINVAL;
reset_loaded:
/* Load data.bin */
if (cycx_data_boot(hw->dpmbase, data_image, img_hdr->data_size)) {
- printk(KERN_ERR "%s: cannot load data file.\n", modname);
+ pr_err("cannot load data file\n");
return -EINVAL;
}
/* Load code.bin */
if (cycx_code_boot(hw->dpmbase, code_image, img_hdr->code_size)) {
- printk(KERN_ERR "%s: cannot load code file.\n", modname);
+ pr_err("cannot load code file\n");
return -EINVAL;
}
@@ -493,7 +486,7 @@ reset_loaded:
/* Arthur Ganzert's tip: wait a while after the firmware loading...
seg abr 26 17:17:12 EST 1999 - acme */
msleep_interruptible(7 * 1000);
- printk(KERN_INFO "%s: firmware loaded!\n", modname);
+ pr_info("firmware loaded!\n");
/* enable interrupts */
cycx_inten(hw);
diff --git a/drivers/net/wan/cycx_main.c b/drivers/net/wan/cycx_main.c
index 859dba9b972e..81fbbad406be 100644
--- a/drivers/net/wan/cycx_main.c
+++ b/drivers/net/wan/cycx_main.c
@@ -40,6 +40,8 @@
* 1998/08/08 acme Initial version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/stddef.h> /* offsetof(), etc. */
#include <linux/errno.h> /* return codes */
#include <linux/string.h> /* inline memset(), etc. */
@@ -50,6 +52,7 @@
#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/cyclomx.h> /* cyclomx common user API definitions */
#include <linux/init.h> /* __init (when not using as a module) */
+#include <linux/interrupt.h>
unsigned int cycx_debug;
@@ -106,7 +109,7 @@ static int __init cycx_init(void)
{
int cnt, err = -ENOMEM;
- printk(KERN_INFO "%s v%u.%u %s\n",
+ pr_info("%s v%u.%u %s\n",
cycx_fullname, CYCX_DRV_VERSION, CYCX_DRV_RELEASE,
cycx_copyright);
@@ -132,9 +135,8 @@ static int __init cycx_init(void)
err = register_wan_device(wandev);
if (err) {
- printk(KERN_ERR "%s: %s registration failed with "
- "error %d!\n",
- cycx_drvname, card->devname, err);
+ pr_err("%s registration failed with error %d!\n",
+ card->devname, err);
break;
}
}
@@ -197,14 +199,13 @@ static int cycx_wan_setup(struct wan_device *wandev, wandev_conf_t *conf)
rc = -EINVAL;
if (!conf->data_size || !conf->data) {
- printk(KERN_ERR "%s: firmware not found in configuration "
- "data!\n", wandev->name);
+ pr_err("%s: firmware not found in configuration data!\n",
+ wandev->name);
goto out;
}
if (conf->irq <= 0) {
- printk(KERN_ERR "%s: can't configure without IRQ!\n",
- wandev->name);
+ pr_err("%s: can't configure without IRQ!\n", wandev->name);
goto out;
}
@@ -212,8 +213,7 @@ static int cycx_wan_setup(struct wan_device *wandev, wandev_conf_t *conf)
irq = conf->irq == 2 ? 9 : conf->irq; /* IRQ2 -> IRQ9 */
if (request_irq(irq, cycx_isr, 0, wandev->name, card)) {
- printk(KERN_ERR "%s: can't reserve IRQ %d!\n",
- wandev->name, irq);
+ pr_err("%s: can't reserve IRQ %d!\n", wandev->name, irq);
goto out;
}
@@ -245,8 +245,7 @@ static int cycx_wan_setup(struct wan_device *wandev, wandev_conf_t *conf)
break;
#endif
default:
- printk(KERN_ERR "%s: this firmware is not supported!\n",
- wandev->name);
+ pr_err("%s: this firmware is not supported!\n", wandev->name);
rc = -EINVAL;
}
@@ -287,8 +286,7 @@ static int cycx_wan_shutdown(struct wan_device *wandev)
card = wandev->private;
wandev->state = WAN_UNCONFIGURED;
cycx_down(&card->hw);
- printk(KERN_INFO "%s: irq %d being freed!\n", wandev->name,
- wandev->irq);
+ pr_info("%s: irq %d being freed!\n", wandev->name, wandev->irq);
free_irq(wandev->irq, card);
out: return ret;
}
@@ -307,8 +305,8 @@ static irqreturn_t cycx_isr(int irq, void *dev_id)
goto out;
if (card->in_isr) {
- printk(KERN_WARNING "%s: interrupt re-entrancy on IRQ %d!\n",
- card->devname, card->wandev.irq);
+ pr_warn("%s: interrupt re-entrancy on IRQ %d!\n",
+ card->devname, card->wandev.irq);
goto out;
}
@@ -336,7 +334,7 @@ void cycx_set_state(struct cycx_device *card, int state)
string_state = "disconnected!";
break;
}
- printk(KERN_INFO "%s: link %s\n", card->devname, string_state);
+ pr_info("%s: link %s\n", card->devname, string_state);
card->wandev.state = state;
}
diff --git a/drivers/net/wan/cycx_x25.c b/drivers/net/wan/cycx_x25.c
index cf9e15fd8d91..06f3f6309e4b 100644
--- a/drivers/net/wan/cycx_x25.c
+++ b/drivers/net/wan/cycx_x25.c
@@ -76,6 +76,8 @@
* 1998/08/08 acme Initial version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define CYCLOMX_X25_DEBUG 1
#include <linux/ctype.h> /* isdigit() */
@@ -230,8 +232,8 @@ int cycx_x25_wan_init(struct cycx_device *card, wandev_conf_t *conf)
/* Verify configuration ID */
if (conf->config_id != WANCONFIG_X25) {
- printk(KERN_INFO "%s: invalid configuration ID %u!\n",
- card->devname, conf->config_id);
+ pr_info("%s: invalid configuration ID %u!\n",
+ card->devname, conf->config_id);
return -EINVAL;
}
@@ -374,8 +376,7 @@ static int cycx_wan_new_if(struct wan_device *wandev, struct net_device *dev,
int err = 0;
if (!conf->name[0] || strlen(conf->name) > WAN_IFNAME_SZ) {
- printk(KERN_INFO "%s: invalid interface name!\n",
- card->devname);
+ pr_info("%s: invalid interface name!\n", card->devname);
return -EINVAL;
}
@@ -398,8 +399,8 @@ static int cycx_wan_new_if(struct wan_device *wandev, struct net_device *dev,
if (len) {
if (len > WAN_ADDRESS_SZ) {
- printk(KERN_ERR "%s: %s local addr too long!\n",
- wandev->name, chan->name);
+ pr_err("%s: %s local addr too long!\n",
+ wandev->name, chan->name);
err = -EINVAL;
goto error;
} else {
@@ -429,15 +430,14 @@ static int cycx_wan_new_if(struct wan_device *wandev, struct net_device *dev,
if (lcn >= card->u.x.lo_pvc && lcn <= card->u.x.hi_pvc)
chan->lcn = lcn;
else {
- printk(KERN_ERR
- "%s: PVC %u is out of range on interface %s!\n",
- wandev->name, lcn, chan->name);
+ pr_err("%s: PVC %u is out of range on interface %s!\n",
+ wandev->name, lcn, chan->name);
err = -EINVAL;
goto error;
}
} else {
- printk(KERN_ERR "%s: invalid media address on interface %s!\n",
- wandev->name, chan->name);
+ pr_err("%s: invalid media address on interface %s!\n",
+ wandev->name, chan->name);
err = -EINVAL;
goto error;
}
@@ -607,9 +607,8 @@ static netdev_tx_t cycx_netdevice_hard_start_xmit(struct sk_buff *skb,
++chan->ifstats.tx_dropped;
else if (chan->svc && chan->protocol &&
chan->protocol != ntohs(skb->protocol)) {
- printk(KERN_INFO
- "%s: unsupported Ethertype 0x%04X on interface %s!\n",
- card->devname, ntohs(skb->protocol), dev->name);
+ pr_info("%s: unsupported Ethertype 0x%04X on interface %s!\n",
+ card->devname, ntohs(skb->protocol), dev->name);
++chan->ifstats.tx_errors;
} else if (chan->protocol == ETH_P_IP) {
switch (chan->state) {
@@ -643,9 +642,8 @@ static netdev_tx_t cycx_netdevice_hard_start_xmit(struct sk_buff *skb,
cycx_x25_chan_disconnect(dev);
goto free_packet;
default:
- printk(KERN_INFO
- "%s: unknown %d x25-iface request on %s!\n",
- card->devname, skb->data[0], dev->name);
+ pr_info("%s: unknown %d x25-iface request on %s!\n",
+ card->devname, skb->data[0], dev->name);
++chan->ifstats.tx_errors;
goto free_packet;
}
@@ -746,8 +744,7 @@ static void cycx_x25_irq_tx(struct cycx_device *card, struct cycx_x25_cmd *cmd)
card->buff_int_mode_unbusy = 1;
netif_wake_queue(dev);
} else
- printk(KERN_ERR "%s:ackvc for inexistent lcn %d\n",
- card->devname, lcn);
+ pr_err("%s:ackvc for inexistent lcn %d\n", card->devname, lcn);
}
/* Receive interrupt handler.
@@ -780,8 +777,8 @@ static void cycx_x25_irq_rx(struct cycx_device *card, struct cycx_x25_cmd *cmd)
dev = cycx_x25_get_dev_by_lcn(wandev, lcn);
if (!dev) {
/* Invalid channel, discard packet */
- printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n",
- card->devname, lcn);
+ pr_info("%s: receiving on orphaned LCN %d!\n",
+ card->devname, lcn);
return;
}
@@ -802,8 +799,8 @@ static void cycx_x25_irq_rx(struct cycx_device *card, struct cycx_x25_cmd *cmd)
if ((skb = dev_alloc_skb((chan->protocol == ETH_P_X25 ? 1 : 0) +
bufsize +
dev->hard_header_len)) == NULL) {
- printk(KERN_INFO "%s: no socket buffers available!\n",
- card->devname);
+ pr_info("%s: no socket buffers available!\n",
+ card->devname);
chan->drop_sequence = 1;
++chan->ifstats.rx_dropped;
return;
@@ -826,8 +823,8 @@ static void cycx_x25_irq_rx(struct cycx_device *card, struct cycx_x25_cmd *cmd)
if (bitm)
chan->drop_sequence = 1;
- printk(KERN_INFO "%s: unexpectedly long packet sequence "
- "on interface %s!\n", card->devname, dev->name);
+ pr_info("%s: unexpectedly long packet sequence on interface %s!\n",
+ card->devname, dev->name);
++chan->ifstats.rx_length_errors;
return;
}
@@ -880,8 +877,8 @@ static void cycx_x25_irq_connect(struct cycx_device *card,
dev = cycx_x25_get_dev_by_dte_addr(wandev, rem);
if (!dev) {
/* Invalid channel, discard packet */
- printk(KERN_INFO "%s: connect not expected: remote %s!\n",
- card->devname, rem);
+ pr_info("%s: connect not expected: remote %s!\n",
+ card->devname, rem);
return;
}
@@ -909,8 +906,8 @@ static void cycx_x25_irq_connect_confirm(struct cycx_device *card,
if (!dev) {
/* Invalid channel, discard packet */
clear_bit(--key, (void*)&card->u.x.connection_keys);
- printk(KERN_INFO "%s: connect confirm not expected: lcn %d, "
- "key=%d!\n", card->devname, lcn, key);
+ pr_info("%s: connect confirm not expected: lcn %d, key=%d!\n",
+ card->devname, lcn, key);
return;
}
@@ -934,8 +931,8 @@ static void cycx_x25_irq_disconnect_confirm(struct cycx_device *card,
dev = cycx_x25_get_dev_by_lcn(wandev, lcn);
if (!dev) {
/* Invalid channel, discard packet */
- printk(KERN_INFO "%s:disconnect confirm not expected!:lcn %d\n",
- card->devname, lcn);
+ pr_info("%s:disconnect confirm not expected!:lcn %d\n",
+ card->devname, lcn);
return;
}
@@ -980,13 +977,13 @@ static void cycx_x25_irq_log(struct cycx_device *card, struct cycx_x25_cmd *cmd)
cycx_peek(&card->hw, cmd->buf + 10 + toread, &code, 1);
cycx_peek(&card->hw, cmd->buf + 10 + toread + 1, &routine, 1);
- printk(KERN_INFO "cycx_x25_irq_handler: X25_LOG (0x4500) indic.:\n");
- printk(KERN_INFO "cmd->buf=0x%X\n", cmd->buf);
- printk(KERN_INFO "Log message code=0x%X\n", msg_code);
- printk(KERN_INFO "Link=%d\n", link);
- printk(KERN_INFO "log code=0x%X\n", code);
- printk(KERN_INFO "log routine=0x%X\n", routine);
- printk(KERN_INFO "Message size=%d\n", size);
+ pr_info("cycx_x25_irq_handler: X25_LOG (0x4500) indic.:\n");
+ pr_info("cmd->buf=0x%X\n", cmd->buf);
+ pr_info("Log message code=0x%X\n", msg_code);
+ pr_info("Link=%d\n", link);
+ pr_info("log code=0x%X\n", code);
+ pr_info("log routine=0x%X\n", routine);
+ pr_info("Message size=%d\n", size);
hex_dump("Message", bf, toread);
#endif
}
@@ -1009,24 +1006,14 @@ static void cycx_x25_irq_stat(struct cycx_device *card,
static void cycx_x25_irq_spurious(struct cycx_device *card,
struct cycx_x25_cmd *cmd)
{
- printk(KERN_INFO "%s: spurious interrupt (0x%X)!\n",
- card->devname, cmd->command);
+ pr_info("%s: spurious interrupt (0x%X)!\n",
+ card->devname, cmd->command);
}
#ifdef CYCLOMX_X25_DEBUG
static void hex_dump(char *msg, unsigned char *p, int len)
{
- unsigned char hex[1024],
- * phex = hex;
-
- if (len >= (sizeof(hex) / 2))
- len = (sizeof(hex) / 2) - 1;
-
- while (len--) {
- sprintf(phex, "%02x", *p++);
- phex += 2;
- }
-
- printk(KERN_INFO "%s: %s\n", msg, hex);
+ print_hex_dump(KERN_INFO, msg, DUMP_PREFIX_OFFSET, 16, 1,
+ p, len, true);
}
#endif
@@ -1203,8 +1190,8 @@ static int x25_place_call(struct cycx_device *card,
u8 key;
if (card->u.x.connection_keys == ~0U) {
- printk(KERN_INFO "%s: too many simultaneous connection "
- "requests!\n", card->devname);
+ pr_info("%s: too many simultaneous connection requests!\n",
+ card->devname);
return -EAGAIN;
}
@@ -1381,8 +1368,8 @@ static void cycx_x25_chan_timer(unsigned long d)
if (chan->state == WAN_CONNECTED)
cycx_x25_chan_disconnect(dev);
else
- printk(KERN_ERR "%s: %s for svc (%s) not connected!\n",
- chan->card->devname, __func__, dev->name);
+ pr_err("%s: %s for svc (%s) not connected!\n",
+ chan->card->devname, __func__, dev->name);
}
/* Set logical channel state. */
@@ -1433,8 +1420,8 @@ static void cycx_x25_set_chan_state(struct net_device *dev, u8 state)
break;
}
- printk(KERN_INFO "%s: interface %s %s\n", card->devname,
- dev->name, string_state);
+ pr_info("%s: interface %s %s\n",
+ card->devname, dev->name, string_state);
chan->state = state;
}
@@ -1488,7 +1475,7 @@ static void cycx_x25_chan_send_event(struct net_device *dev, u8 event)
unsigned char *ptr;
if ((skb = dev_alloc_skb(1)) == NULL) {
- printk(KERN_ERR "%s: out of memory\n", __func__);
+ pr_err("%s: out of memory\n", __func__);
return;
}
@@ -1557,56 +1544,56 @@ static void reset_timer(struct net_device *dev)
#ifdef CYCLOMX_X25_DEBUG
static void cycx_x25_dump_config(struct cycx_x25_config *conf)
{
- printk(KERN_INFO "X.25 configuration\n");
- printk(KERN_INFO "-----------------\n");
- printk(KERN_INFO "link number=%d\n", conf->link);
- printk(KERN_INFO "line speed=%d\n", conf->speed);
- printk(KERN_INFO "clock=%sternal\n", conf->clock == 8 ? "Ex" : "In");
- printk(KERN_INFO "# level 2 retransm.=%d\n", conf->n2);
- printk(KERN_INFO "level 2 window=%d\n", conf->n2win);
- printk(KERN_INFO "level 3 window=%d\n", conf->n3win);
- printk(KERN_INFO "# logical channels=%d\n", conf->nvc);
- printk(KERN_INFO "level 3 pkt len=%d\n", conf->pktlen);
- printk(KERN_INFO "my address=%d\n", conf->locaddr);
- printk(KERN_INFO "remote address=%d\n", conf->remaddr);
- printk(KERN_INFO "t1=%d seconds\n", conf->t1);
- printk(KERN_INFO "t2=%d seconds\n", conf->t2);
- printk(KERN_INFO "t21=%d seconds\n", conf->t21);
- printk(KERN_INFO "# PVCs=%d\n", conf->npvc);
- printk(KERN_INFO "t23=%d seconds\n", conf->t23);
- printk(KERN_INFO "flags=0x%x\n", conf->flags);
+ pr_info("X.25 configuration\n");
+ pr_info("-----------------\n");
+ pr_info("link number=%d\n", conf->link);
+ pr_info("line speed=%d\n", conf->speed);
+ pr_info("clock=%sternal\n", conf->clock == 8 ? "Ex" : "In");
+ pr_info("# level 2 retransm.=%d\n", conf->n2);
+ pr_info("level 2 window=%d\n", conf->n2win);
+ pr_info("level 3 window=%d\n", conf->n3win);
+ pr_info("# logical channels=%d\n", conf->nvc);
+ pr_info("level 3 pkt len=%d\n", conf->pktlen);
+ pr_info("my address=%d\n", conf->locaddr);
+ pr_info("remote address=%d\n", conf->remaddr);
+ pr_info("t1=%d seconds\n", conf->t1);
+ pr_info("t2=%d seconds\n", conf->t2);
+ pr_info("t21=%d seconds\n", conf->t21);
+ pr_info("# PVCs=%d\n", conf->npvc);
+ pr_info("t23=%d seconds\n", conf->t23);
+ pr_info("flags=0x%x\n", conf->flags);
}
static void cycx_x25_dump_stats(struct cycx_x25_stats *stats)
{
- printk(KERN_INFO "X.25 statistics\n");
- printk(KERN_INFO "--------------\n");
- printk(KERN_INFO "rx_crc_errors=%d\n", stats->rx_crc_errors);
- printk(KERN_INFO "rx_over_errors=%d\n", stats->rx_over_errors);
- printk(KERN_INFO "n2_tx_frames=%d\n", stats->n2_tx_frames);
- printk(KERN_INFO "n2_rx_frames=%d\n", stats->n2_rx_frames);
- printk(KERN_INFO "tx_timeouts=%d\n", stats->tx_timeouts);
- printk(KERN_INFO "rx_timeouts=%d\n", stats->rx_timeouts);
- printk(KERN_INFO "n3_tx_packets=%d\n", stats->n3_tx_packets);
- printk(KERN_INFO "n3_rx_packets=%d\n", stats->n3_rx_packets);
- printk(KERN_INFO "tx_aborts=%d\n", stats->tx_aborts);
- printk(KERN_INFO "rx_aborts=%d\n", stats->rx_aborts);
+ pr_info("X.25 statistics\n");
+ pr_info("--------------\n");
+ pr_info("rx_crc_errors=%d\n", stats->rx_crc_errors);
+ pr_info("rx_over_errors=%d\n", stats->rx_over_errors);
+ pr_info("n2_tx_frames=%d\n", stats->n2_tx_frames);
+ pr_info("n2_rx_frames=%d\n", stats->n2_rx_frames);
+ pr_info("tx_timeouts=%d\n", stats->tx_timeouts);
+ pr_info("rx_timeouts=%d\n", stats->rx_timeouts);
+ pr_info("n3_tx_packets=%d\n", stats->n3_tx_packets);
+ pr_info("n3_rx_packets=%d\n", stats->n3_rx_packets);
+ pr_info("tx_aborts=%d\n", stats->tx_aborts);
+ pr_info("rx_aborts=%d\n", stats->rx_aborts);
}
static void cycx_x25_dump_devs(struct wan_device *wandev)
{
struct net_device *dev = wandev->dev;
- printk(KERN_INFO "X.25 dev states\n");
- printk(KERN_INFO "name: addr: txoff: protocol:\n");
- printk(KERN_INFO "---------------------------------------\n");
+ pr_info("X.25 dev states\n");
+ pr_info("name: addr: txoff: protocol:\n");
+ pr_info("---------------------------------------\n");
while(dev) {
struct cycx_x25_channel *chan = netdev_priv(dev);
- printk(KERN_INFO "%-5.5s %-15.15s %d ETH_P_%s\n",
- chan->name, chan->addr, netif_queue_stopped(dev),
- chan->protocol == ETH_P_IP ? "IP" : "X25");
+ pr_info("%-5.5s %-15.15s %d ETH_P_%s\n",
+ chan->name, chan->addr, netif_queue_stopped(dev),
+ chan->protocol == ETH_P_IP ? "IP" : "X25");
dev = chan->slave;
}
}
diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c
index 21b104db5a90..48ab38a34c5a 100644
--- a/drivers/net/wan/dlci.c
+++ b/drivers/net/wan/dlci.c
@@ -28,6 +28,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
@@ -112,8 +114,7 @@ static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
dlp = netdev_priv(dev);
if (!pskb_may_pull(skb, sizeof(*hdr))) {
- printk(KERN_NOTICE "%s: invalid data no header\n",
- dev->name);
+ netdev_notice(dev, "invalid data no header\n");
dev->stats.rx_errors++;
kfree_skb(skb);
return;
@@ -126,7 +127,8 @@ static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
if (hdr->control != FRAD_I_UI)
{
- printk(KERN_NOTICE "%s: Invalid header flag 0x%02X.\n", dev->name, hdr->control);
+ netdev_notice(dev, "Invalid header flag 0x%02X\n",
+ hdr->control);
dev->stats.rx_errors++;
}
else
@@ -135,14 +137,18 @@ static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
case FRAD_P_PADDING:
if (hdr->NLPID != FRAD_P_SNAP)
{
- printk(KERN_NOTICE "%s: Unsupported NLPID 0x%02X.\n", dev->name, hdr->NLPID);
+ netdev_notice(dev, "Unsupported NLPID 0x%02X\n",
+ hdr->NLPID);
dev->stats.rx_errors++;
break;
}
if (hdr->OUI[0] + hdr->OUI[1] + hdr->OUI[2] != 0)
{
- printk(KERN_NOTICE "%s: Unsupported organizationally unique identifier 0x%02X-%02X-%02X.\n", dev->name, hdr->OUI[0], hdr->OUI[1], hdr->OUI[2]);
+ netdev_notice(dev, "Unsupported organizationally unique identifier 0x%02X-%02X-%02X\n",
+ hdr->OUI[0],
+ hdr->OUI[1],
+ hdr->OUI[2]);
dev->stats.rx_errors++;
break;
}
@@ -163,12 +169,14 @@ static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
case FRAD_P_SNAP:
case FRAD_P_Q933:
case FRAD_P_CLNP:
- printk(KERN_NOTICE "%s: Unsupported NLPID 0x%02X.\n", dev->name, hdr->pad);
+ netdev_notice(dev, "Unsupported NLPID 0x%02X\n",
+ hdr->pad);
dev->stats.rx_errors++;
break;
default:
- printk(KERN_NOTICE "%s: Invalid pad byte 0x%02X.\n", dev->name, hdr->pad);
+ netdev_notice(dev, "Invalid pad byte 0x%02X\n",
+ hdr->pad);
dev->stats.rx_errors++;
break;
}
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index acb9ea830628..058e1697c174 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -80,6 +80,8 @@
* - misc crapectomy.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -99,6 +101,7 @@
#include <asm/irq.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/if_arp.h>
@@ -552,7 +555,7 @@ static int dscc4_wait_ack_cec(struct dscc4_dev_priv *dpriv,
schedule_timeout_uninterruptible(10);
rmb();
} while (++i > 0);
- printk(KERN_ERR "%s: %s timeout\n", dev->name, msg);
+ netdev_err(dev, "%s timeout\n", msg);
done:
return (i >= 0) ? i : -EAGAIN;
}
@@ -568,18 +571,18 @@ static int dscc4_do_action(struct net_device *dev, char *msg)
u32 state = readl(ioaddr);
if (state & ArAck) {
- printk(KERN_DEBUG "%s: %s ack\n", dev->name, msg);
+ netdev_dbg(dev, "%s ack\n", msg);
writel(ArAck, ioaddr);
goto done;
} else if (state & Arf) {
- printk(KERN_ERR "%s: %s failed\n", dev->name, msg);
+ netdev_err(dev, "%s failed\n", msg);
writel(Arf, ioaddr);
i = -1;
goto done;
}
rmb();
} while (++i > 0);
- printk(KERN_ERR "%s: %s timeout\n", dev->name, msg);
+ netdev_err(dev, "%s timeout\n", msg);
done:
return i;
}
@@ -635,7 +638,7 @@ static void dscc4_tx_reset(struct dscc4_dev_priv *dpriv, struct net_device *dev)
writel(MTFi|Rdt, dpriv->base_addr + dpriv->dev_id*0x0c + CH0CFG);
if (dscc4_do_action(dev, "Rdt") < 0)
- printk(KERN_ERR "%s: Tx reset failed\n", dev->name);
+ netdev_err(dev, "Tx reset failed\n");
}
#endif
@@ -721,22 +724,20 @@ static int __devinit dscc4_init_one(struct pci_dev *pdev,
rc = pci_request_region(pdev, 0, "registers");
if (rc < 0) {
- printk(KERN_ERR "%s: can't reserve MMIO region (regs)\n",
- DRV_NAME);
+ pr_err("can't reserve MMIO region (regs)\n");
goto err_disable_0;
}
rc = pci_request_region(pdev, 1, "LBI interface");
if (rc < 0) {
- printk(KERN_ERR "%s: can't reserve MMIO region (lbi)\n",
- DRV_NAME);
+ pr_err("can't reserve MMIO region (lbi)\n");
goto err_free_mmio_region_1;
}
ioaddr = pci_ioremap_bar(pdev, 0);
if (!ioaddr) {
- printk(KERN_ERR "%s: cannot remap MMIO region %llx @ %llx\n",
- DRV_NAME, (unsigned long long)pci_resource_len(pdev, 0),
- (unsigned long long)pci_resource_start(pdev, 0));
+ pr_err("cannot remap MMIO region %llx @ %llx\n",
+ (unsigned long long)pci_resource_len(pdev, 0),
+ (unsigned long long)pci_resource_start(pdev, 0));
rc = -EIO;
goto err_free_mmio_regions_2;
}
@@ -756,7 +757,7 @@ static int __devinit dscc4_init_one(struct pci_dev *pdev,
rc = request_irq(pdev->irq, dscc4_irq, IRQF_SHARED, DRV_NAME, priv->root);
if (rc < 0) {
- printk(KERN_WARNING "%s: IRQ %d busy\n", DRV_NAME, pdev->irq);
+ pr_warn("IRQ %d busy\n", pdev->irq);
goto err_release_4;
}
@@ -903,7 +904,7 @@ static int dscc4_found1(struct pci_dev *pdev, void __iomem *ioaddr)
root = kcalloc(dev_per_card, sizeof(*root), GFP_KERNEL);
if (!root) {
- printk(KERN_ERR "%s: can't allocate data\n", DRV_NAME);
+ pr_err("can't allocate data\n");
goto err_out;
}
@@ -915,7 +916,7 @@ static int dscc4_found1(struct pci_dev *pdev, void __iomem *ioaddr)
ppriv = kzalloc(sizeof(*ppriv), GFP_KERNEL);
if (!ppriv) {
- printk(KERN_ERR "%s: can't allocate private data\n", DRV_NAME);
+ pr_err("can't allocate private data\n");
goto err_free_dev;
}
@@ -951,7 +952,7 @@ static int dscc4_found1(struct pci_dev *pdev, void __iomem *ioaddr)
ret = register_hdlc_device(d);
if (ret < 0) {
- printk(KERN_ERR "%s: unable to register\n", DRV_NAME);
+ pr_err("unable to register\n");
dscc4_release_ring(dpriv);
goto err_unregister;
}
@@ -1004,7 +1005,7 @@ static int dscc4_loopback_check(struct dscc4_dev_priv *dpriv)
if (settings->loopback && (settings->clock_type != CLOCK_INT)) {
struct net_device *dev = dscc4_to_dev(dpriv);
- printk(KERN_INFO "%s: loopback requires clock\n", dev->name);
+ netdev_info(dev, "loopback requires clock\n");
return -1;
}
return 0;
@@ -1077,7 +1078,7 @@ static int dscc4_open(struct net_device *dev)
scc_patchl(0, PowerUp, dpriv, dev, CCR0);
scc_patchl(0, 0x00050000, dpriv, dev, CCR2);
scc_writel(EventsMask, dpriv, dev, IMR);
- printk(KERN_INFO "%s: up again.\n", dev->name);
+ netdev_info(dev, "up again\n");
goto done;
}
@@ -1094,11 +1095,11 @@ static int dscc4_open(struct net_device *dev)
* situations.
*/
if (scc_readl_star(dpriv, dev) & SccBusy) {
- printk(KERN_ERR "%s busy. Try later\n", dev->name);
+ netdev_err(dev, "busy - try later\n");
ret = -EAGAIN;
goto err_out;
} else
- printk(KERN_INFO "%s: available. Good\n", dev->name);
+ netdev_info(dev, "available - good\n");
scc_writel(EventsMask, dpriv, dev, IMR);
@@ -1116,7 +1117,7 @@ static int dscc4_open(struct net_device *dev)
* reset is needed. Suggestions anyone ?
*/
if ((ret = dscc4_xpr_ack(dpriv)) < 0) {
- printk(KERN_ERR "%s: %s timeout\n", DRV_NAME, "XPR");
+ pr_err("XPR timeout\n");
goto err_disable_scc_events;
}
@@ -1341,8 +1342,7 @@ static int dscc4_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -EPERM;
if (dpriv->flags & FakeReset) {
- printk(KERN_INFO "%s: please reset the device"
- " before this command\n", dev->name);
+ netdev_info(dev, "please reset the device before this command\n");
return -EPERM;
}
if (copy_from_user(&dpriv->settings, line, size))
@@ -1505,8 +1505,7 @@ static irqreturn_t dscc4_irq(int irq, void *token)
writel(state, ioaddr + GSTAR);
if (state & Arf) {
- printk(KERN_ERR "%s: failure (Arf). Harass the maintener\n",
- dev->name);
+ netdev_err(dev, "failure (Arf). Harass the maintainer\n");
goto out;
}
state &= ~ArAck;
@@ -1514,7 +1513,7 @@ static irqreturn_t dscc4_irq(int irq, void *token)
if (debug > 0)
printk(KERN_DEBUG "%s: CfgIV\n", DRV_NAME);
if (priv->iqcfg[priv->cfg_cur++%IRQ_RING_SIZE] & cpu_to_le32(Arf))
- printk(KERN_ERR "%s: %s failed\n", dev->name, "CFG");
+ netdev_err(dev, "CFG failed\n");
if (!(state &= ~Cfg))
goto out;
}
@@ -1595,8 +1594,8 @@ try:
++dpriv->tx_dirty;
} else {
if (debug > 1)
- printk(KERN_ERR "%s Tx: NULL skb %d\n",
- dev->name, cur);
+ netdev_err(dev, "Tx: NULL skb %d\n",
+ cur);
}
/*
* If the driver ends sending crap on the wire, it
@@ -1615,7 +1614,7 @@ try:
* Transmit Data Underrun
*/
if (state & Xdu) {
- printk(KERN_ERR "%s: XDU. Ask maintainer\n", DRV_NAME);
+ netdev_err(dev, "Tx Data Underrun. Ask maintainer\n");
dpriv->flags = NeedIDT;
/* Tx reset */
writel(MTFi | Rdt,
@@ -1624,13 +1623,13 @@ try:
return;
}
if (state & Cts) {
- printk(KERN_INFO "%s: CTS transition\n", dev->name);
+ netdev_info(dev, "CTS transition\n");
if (!(state &= ~Cts)) /* DEBUG */
goto try;
}
if (state & Xmr) {
/* Frame needs to be sent again - FIXME */
- printk(KERN_ERR "%s: Xmr. Ask maintainer\n", DRV_NAME);
+ netdev_err(dev, "Tx ReTx. Ask maintainer\n");
if (!(state &= ~Xmr)) /* DEBUG */
goto try;
}
@@ -1648,7 +1647,7 @@ try:
break;
}
if (!i)
- printk(KERN_INFO "%s busy in irq\n", dev->name);
+ netdev_info(dev, "busy in irq\n");
scc_addr = dpriv->base_addr + 0x0c*dpriv->dev_id;
/* Keep this order: IDT before IDR */
@@ -1685,7 +1684,7 @@ try:
}
if (state & Cd) {
if (debug > 0)
- printk(KERN_INFO "%s: CD transition\n", dev->name);
+ netdev_info(dev, "CD transition\n");
if (!(state &= ~Cd)) /* DEBUG */
goto try;
}
@@ -1694,11 +1693,11 @@ try:
#ifdef DSCC4_POLLING
while (!dscc4_tx_poll(dpriv, dev));
#endif
- printk(KERN_INFO "%s: Tx Hi\n", dev->name);
+ netdev_info(dev, "Tx Hi\n");
state &= ~Hi;
}
if (state & Err) {
- printk(KERN_INFO "%s: Tx ERR\n", dev->name);
+ netdev_info(dev, "Tx ERR\n");
dev->stats.tx_errors++;
state &= ~Err;
}
@@ -1768,7 +1767,7 @@ try:
goto try;
}
if (state & Hi ) { /* HI bit */
- printk(KERN_INFO "%s: Rx Hi\n", dev->name);
+ netdev_info(dev, "Rx Hi\n");
state &= ~Hi;
goto try;
}
@@ -1799,7 +1798,7 @@ try:
goto try;
}
if (state & Cts) {
- printk(KERN_INFO "%s: CTS transition\n", dev->name);
+ netdev_info(dev, "CTS transition\n");
if (!(state &= ~Cts)) /* DEBUG */
goto try;
}
@@ -1858,14 +1857,12 @@ try:
sizeof(struct RxFD), scc_addr + CH0BRDA);
writel(MTFi|Rdr|Idr, scc_addr + CH0CFG);
if (dscc4_do_action(dev, "RDR") < 0) {
- printk(KERN_ERR "%s: RDO recovery failed(%s)\n",
- dev->name, "RDR");
+ netdev_err(dev, "RDO recovery failed(RDR)\n");
goto rdo_end;
}
writel(MTFi|Idr, scc_addr + CH0CFG);
if (dscc4_do_action(dev, "IDR") < 0) {
- printk(KERN_ERR "%s: RDO recovery failed(%s)\n",
- dev->name, "IDR");
+ netdev_err(dev, "RDO recovery failed(IDR)\n");
goto rdo_end;
}
rdo_end:
@@ -1874,7 +1871,7 @@ try:
goto try;
}
if (state & Cd) {
- printk(KERN_INFO "%s: CD transition\n", dev->name);
+ netdev_info(dev, "CD transition\n");
if (!(state &= ~Cd)) /* DEBUG */
goto try;
}
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index 777d1a4e81b2..ebb9f24eefb5 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -25,6 +25,7 @@
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/if.h>
#include <linux/hdlc.h>
#include <asm/io.h>
@@ -1664,10 +1665,9 @@ check_started_ok(struct fst_card_info *card)
* existing firmware etc so we just report it for the moment.
*/
if (FST_RDL(card, numberOfPorts) != card->nports) {
- pr_warning("Port count mismatch on card %d. "
- "Firmware thinks %d we say %d\n",
- card->card_no,
- FST_RDL(card, numberOfPorts), card->nports);
+ pr_warn("Port count mismatch on card %d. Firmware thinks %d we say %d\n",
+ card->card_no,
+ FST_RDL(card, numberOfPorts), card->nports);
}
}
diff --git a/drivers/net/wan/hd64570.c b/drivers/net/wan/hd64570.c
index a3ea27ce04f2..33b67d88fceb 100644
--- a/drivers/net/wan/hd64570.c
+++ b/drivers/net/wan/hd64570.c
@@ -582,8 +582,8 @@ static void sca_dump_rings(struct net_device *dev)
sca_in(DSR_RX(phy_node(port)), card), port->rxin,
sca_in(DSR_RX(phy_node(port)), card) & DSR_DE ? "" : "in");
for (cnt = 0; cnt < port_to_card(port)->rx_ring_buffers; cnt++)
- printk(" %02X", readb(&(desc_address(port, cnt, 0)->stat)));
- printk(KERN_CONT "\n");
+ pr_cont(" %02X", readb(&(desc_address(port, cnt, 0)->stat)));
+ pr_cont("\n");
printk(KERN_DEBUG "TX ring: CDA=%u EDA=%u DSR=%02X in=%u "
"last=%u %sactive",
@@ -593,8 +593,8 @@ static void sca_dump_rings(struct net_device *dev)
sca_in(DSR_TX(phy_node(port)), card) & DSR_DE ? "" : "in");
for (cnt = 0; cnt < port_to_card(port)->tx_ring_buffers; cnt++)
- printk(" %02X", readb(&(desc_address(port, cnt, 1)->stat)));
- printk("\n");
+ pr_cont(" %02X", readb(&(desc_address(port, cnt, 1)->stat)));
+ pr_cont("\n");
printk(KERN_DEBUG "MSCI: MD: %02x %02x %02x, ST: %02x %02x %02x %02x,"
" FST: %02x CST: %02x %02x\n",
diff --git a/drivers/net/wan/hd64572.c b/drivers/net/wan/hd64572.c
index e305274f83fb..efc0db101183 100644
--- a/drivers/net/wan/hd64572.c
+++ b/drivers/net/wan/hd64572.c
@@ -530,8 +530,8 @@ static void sca_dump_rings(struct net_device *dev)
sca_in(DSR_RX(port->chan), card), port->rxin,
sca_in(DSR_RX(port->chan), card) & DSR_DE ? "" : "in");
for (cnt = 0; cnt < port->card->rx_ring_buffers; cnt++)
- printk(" %02X", readb(&(desc_address(port, cnt, 0)->stat)));
- printk(KERN_CONT "\n");
+ pr_cont(" %02X", readb(&(desc_address(port, cnt, 0)->stat)));
+ pr_cont("\n");
printk(KERN_DEBUG "TX ring: CDA=%u EDA=%u DSR=%02X in=%u "
"last=%u %sactive",
@@ -541,8 +541,8 @@ static void sca_dump_rings(struct net_device *dev)
sca_in(DSR_TX(port->chan), card) & DSR_DE ? "" : "in");
for (cnt = 0; cnt < port->card->tx_ring_buffers; cnt++)
- printk(" %02X", readb(&(desc_address(port, cnt, 1)->stat)));
- printk("\n");
+ pr_cont(" %02X", readb(&(desc_address(port, cnt, 1)->stat)));
+ pr_cont("\n");
printk(KERN_DEBUG "MSCI: MD: %02x %02x %02x,"
" ST: %02x %02x %02x %02x %02x, FST: %02x CST: %02x %02x\n",
diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c
index 5d4bb615ccce..10cc7df95498 100644
--- a/drivers/net/wan/hdlc.c
+++ b/drivers/net/wan/hdlc.c
@@ -22,6 +22,8 @@
* - proto->start() and stop() are called with spin_lock_irq held.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/errno.h>
#include <linux/hdlc.h>
#include <linux/if_arp.h>
@@ -130,10 +132,10 @@ static int hdlc_device_event(struct notifier_block *this, unsigned long event,
goto carrier_exit;
if (hdlc->carrier) {
- printk(KERN_INFO "%s: Carrier detected\n", dev->name);
+ netdev_info(dev, "Carrier detected\n");
hdlc_proto_start(dev);
} else {
- printk(KERN_INFO "%s: Carrier lost\n", dev->name);
+ netdev_info(dev, "Carrier lost\n");
hdlc_proto_stop(dev);
}
@@ -165,10 +167,10 @@ int hdlc_open(struct net_device *dev)
spin_lock_irq(&hdlc->state_lock);
if (hdlc->carrier) {
- printk(KERN_INFO "%s: Carrier detected\n", dev->name);
+ netdev_info(dev, "Carrier detected\n");
hdlc_proto_start(dev);
} else
- printk(KERN_INFO "%s: No carrier\n", dev->name);
+ netdev_info(dev, "No carrier\n");
hdlc->open = 1;
@@ -281,8 +283,8 @@ int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
if (size)
if ((dev_to_hdlc(dev)->state = kmalloc(size,
GFP_KERNEL)) == NULL) {
- printk(KERN_WARNING "Memory squeeze on"
- " hdlc_proto_attach()\n");
+ netdev_warn(dev,
+ "Memory squeeze on hdlc_proto_attach()\n");
module_put(proto->module);
return -ENOBUFS;
}
@@ -363,7 +365,7 @@ static int __init hdlc_module_init(void)
{
int result;
- printk(KERN_INFO "%s\n", version);
+ pr_info("%s\n", version);
if ((result = register_netdevice_notifier(&hdlc_notifier)) != 0)
return result;
dev_add_pack(&hdlc_packet_type);
diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c
index b1e5e5b69c2a..3f20808b5ff8 100644
--- a/drivers/net/wan/hdlc_cisco.c
+++ b/drivers/net/wan/hdlc_cisco.c
@@ -103,9 +103,7 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
skb = dev_alloc_skb(sizeof(struct hdlc_header) +
sizeof(struct cisco_packet));
if (!skb) {
- printk(KERN_WARNING
- "%s: Memory squeeze on cisco_keepalive_send()\n",
- dev->name);
+ netdev_warn(dev, "Memory squeeze on cisco_keepalive_send()\n");
return;
}
skb_reserve(skb, 4);
@@ -181,8 +179,8 @@ static int cisco_rx(struct sk_buff *skb)
CISCO_PACKET_LEN) &&
(skb->len != sizeof(struct hdlc_header) +
CISCO_BIG_PACKET_LEN)) {
- printk(KERN_INFO "%s: Invalid length of Cisco control"
- " packet (%d bytes)\n", dev->name, skb->len);
+ netdev_info(dev, "Invalid length of Cisco control packet (%d bytes)\n",
+ skb->len);
goto rx_error;
}
@@ -217,8 +215,7 @@ static int cisco_rx(struct sk_buff *skb)
return NET_RX_SUCCESS;
case CISCO_ADDR_REPLY:
- printk(KERN_INFO "%s: Unexpected Cisco IP address "
- "reply\n", dev->name);
+ netdev_info(dev, "Unexpected Cisco IP address reply\n");
goto rx_error;
case CISCO_KEEPALIVE_REQ:
@@ -235,9 +232,8 @@ static int cisco_rx(struct sk_buff *skb)
min = sec / 60; sec -= min * 60;
hrs = min / 60; min -= hrs * 60;
days = hrs / 24; hrs -= days * 24;
- printk(KERN_INFO "%s: Link up (peer "
- "uptime %ud%uh%um%us)\n",
- dev->name, days, hrs, min, sec);
+ netdev_info(dev, "Link up (peer uptime %ud%uh%um%us)\n",
+ days, hrs, min, sec);
netif_dormant_off(dev);
st->up = 1;
}
@@ -249,8 +245,7 @@ static int cisco_rx(struct sk_buff *skb)
} /* switch (keepalive type) */
} /* switch (protocol) */
- printk(KERN_INFO "%s: Unsupported protocol %x\n", dev->name,
- ntohs(data->protocol));
+ netdev_info(dev, "Unsupported protocol %x\n", ntohs(data->protocol));
dev_kfree_skb_any(skb);
return NET_RX_DROP;
@@ -272,7 +267,7 @@ static void cisco_timer(unsigned long arg)
if (st->up &&
time_after(jiffies, st->last_poll + st->settings.timeout * HZ)) {
st->up = 0;
- printk(KERN_INFO "%s: Link down\n", dev->name);
+ netdev_info(dev, "Link down\n");
netif_dormant_on(dev);
}
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index fc433f28c047..eb2028187fbe 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -446,15 +446,14 @@ static netdev_tx_t pvc_xmit(struct sk_buff *skb, struct net_device *dev)
static inline void fr_log_dlci_active(pvc_device *pvc)
{
- printk(KERN_INFO "%s: DLCI %d [%s%s%s]%s %s\n",
- pvc->frad->name,
- pvc->dlci,
- pvc->main ? pvc->main->name : "",
- pvc->main && pvc->ether ? " " : "",
- pvc->ether ? pvc->ether->name : "",
- pvc->state.new ? " new" : "",
- !pvc->state.exist ? "deleted" :
- pvc->state.active ? "active" : "inactive");
+ netdev_info(pvc->frad, "DLCI %d [%s%s%s]%s %s\n",
+ pvc->dlci,
+ pvc->main ? pvc->main->name : "",
+ pvc->main && pvc->ether ? " " : "",
+ pvc->ether ? pvc->ether->name : "",
+ pvc->state.new ? " new" : "",
+ !pvc->state.exist ? "deleted" :
+ pvc->state.active ? "active" : "inactive");
}
@@ -481,16 +480,14 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
if (dce && fullrep) {
len += state(hdlc)->dce_pvc_count * (2 + stat_len);
if (len > HDLC_MAX_MRU) {
- printk(KERN_WARNING "%s: Too many PVCs while sending "
- "LMI full report\n", dev->name);
+ netdev_warn(dev, "Too many PVCs while sending LMI full report\n");
return;
}
}
skb = dev_alloc_skb(len);
if (!skb) {
- printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",
- dev->name);
+ netdev_warn(dev, "Memory squeeze on fr_lmi_send()\n");
return;
}
memset(skb->data, 0, len);
@@ -615,8 +612,7 @@ static void fr_timer(unsigned long arg)
state(hdlc)->last_errors <<= 1; /* Shift the list */
if (state(hdlc)->request) {
if (state(hdlc)->reliable)
- printk(KERN_INFO "%s: No LMI status reply "
- "received\n", dev->name);
+ netdev_info(dev, "No LMI status reply received\n");
state(hdlc)->last_errors |= 1;
}
@@ -628,8 +624,7 @@ static void fr_timer(unsigned long arg)
}
if (state(hdlc)->reliable != reliable) {
- printk(KERN_INFO "%s: Link %sreliable\n", dev->name,
- reliable ? "" : "un");
+ netdev_info(dev, "Link %sreliable\n", reliable ? "" : "un");
fr_set_link_state(reliable, dev);
}
@@ -665,33 +660,32 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
if (skb->len < (lmi == LMI_ANSI ? LMI_ANSI_LENGTH :
LMI_CCITT_CISCO_LENGTH)) {
- printk(KERN_INFO "%s: Short LMI frame\n", dev->name);
+ netdev_info(dev, "Short LMI frame\n");
return 1;
}
if (skb->data[3] != (lmi == LMI_CISCO ? NLPID_CISCO_LMI :
NLPID_CCITT_ANSI_LMI)) {
- printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
- dev->name);
+ netdev_info(dev, "Received non-LMI frame with LMI DLCI\n");
return 1;
}
if (skb->data[4] != LMI_CALLREF) {
- printk(KERN_INFO "%s: Invalid LMI Call reference (0x%02X)\n",
- dev->name, skb->data[4]);
+ netdev_info(dev, "Invalid LMI Call reference (0x%02X)\n",
+ skb->data[4]);
return 1;
}
if (skb->data[5] != (dce ? LMI_STATUS_ENQUIRY : LMI_STATUS)) {
- printk(KERN_INFO "%s: Invalid LMI Message type (0x%02X)\n",
- dev->name, skb->data[5]);
+ netdev_info(dev, "Invalid LMI Message type (0x%02X)\n",
+ skb->data[5]);
return 1;
}
if (lmi == LMI_ANSI) {
if (skb->data[6] != LMI_ANSI_LOCKSHIFT) {
- printk(KERN_INFO "%s: Not ANSI locking shift in LMI"
- " message (0x%02X)\n", dev->name, skb->data[6]);
+ netdev_info(dev, "Not ANSI locking shift in LMI message (0x%02X)\n",
+ skb->data[6]);
return 1;
}
i = 7;
@@ -700,34 +694,34 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
if (skb->data[i] != (lmi == LMI_CCITT ? LMI_CCITT_REPTYPE :
LMI_ANSI_CISCO_REPTYPE)) {
- printk(KERN_INFO "%s: Not an LMI Report type IE (0x%02X)\n",
- dev->name, skb->data[i]);
+ netdev_info(dev, "Not an LMI Report type IE (0x%02X)\n",
+ skb->data[i]);
return 1;
}
if (skb->data[++i] != LMI_REPT_LEN) {
- printk(KERN_INFO "%s: Invalid LMI Report type IE length"
- " (%u)\n", dev->name, skb->data[i]);
+ netdev_info(dev, "Invalid LMI Report type IE length (%u)\n",
+ skb->data[i]);
return 1;
}
reptype = skb->data[++i];
if (reptype != LMI_INTEGRITY && reptype != LMI_FULLREP) {
- printk(KERN_INFO "%s: Unsupported LMI Report type (0x%02X)\n",
- dev->name, reptype);
+ netdev_info(dev, "Unsupported LMI Report type (0x%02X)\n",
+ reptype);
return 1;
}
if (skb->data[++i] != (lmi == LMI_CCITT ? LMI_CCITT_ALIVE :
LMI_ANSI_CISCO_ALIVE)) {
- printk(KERN_INFO "%s: Not an LMI Link integrity verification"
- " IE (0x%02X)\n", dev->name, skb->data[i]);
+ netdev_info(dev, "Not an LMI Link integrity verification IE (0x%02X)\n",
+ skb->data[i]);
return 1;
}
if (skb->data[++i] != LMI_INTEG_LEN) {
- printk(KERN_INFO "%s: Invalid LMI Link integrity verification"
- " IE length (%u)\n", dev->name, skb->data[i]);
+ netdev_info(dev, "Invalid LMI Link integrity verification IE length (%u)\n",
+ skb->data[i]);
return 1;
}
i++;
@@ -801,14 +795,14 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
if (skb->data[i] != (lmi == LMI_CCITT ? LMI_CCITT_PVCSTAT :
LMI_ANSI_CISCO_PVCSTAT)) {
- printk(KERN_INFO "%s: Not an LMI PVC status IE"
- " (0x%02X)\n", dev->name, skb->data[i]);
+ netdev_info(dev, "Not an LMI PVC status IE (0x%02X)\n",
+ skb->data[i]);
return 1;
}
if (skb->data[++i] != stat_len) {
- printk(KERN_INFO "%s: Invalid LMI PVC status IE length"
- " (%u)\n", dev->name, skb->data[i]);
+ netdev_info(dev, "Invalid LMI PVC status IE length (%u)\n",
+ skb->data[i]);
return 1;
}
i++;
@@ -829,9 +823,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
pvc = add_pvc(dev, dlci);
if (!pvc && !no_ram) {
- printk(KERN_WARNING
- "%s: Memory squeeze on fr_lmi_recv()\n",
- dev->name);
+ netdev_warn(dev, "Memory squeeze on fr_lmi_recv()\n");
no_ram = 1;
}
@@ -902,8 +894,8 @@ static int fr_rx(struct sk_buff *skb)
pvc = find_pvc(hdlc, dlci);
if (!pvc) {
#ifdef DEBUG_PKT
- printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
- frad->name, dlci);
+ netdev_info(frad, "No PVC for received frame's DLCI %d\n",
+ dlci);
#endif
dev_kfree_skb_any(skb);
return NET_RX_DROP;
@@ -962,14 +954,14 @@ static int fr_rx(struct sk_buff *skb)
break;
default:
- printk(KERN_INFO "%s: Unsupported protocol, OUI=%x "
- "PID=%x\n", frad->name, oui, pid);
+ netdev_info(frad, "Unsupported protocol, OUI=%x PID=%x\n",
+ oui, pid);
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
} else {
- printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x "
- "length = %i\n", frad->name, data[3], skb->len);
+ netdev_info(frad, "Unsupported protocol, NLPID=%x length=%i\n",
+ data[3], skb->len);
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@@ -1073,8 +1065,7 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
int used;
if ((pvc = add_pvc(frad, dlci)) == NULL) {
- printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n",
- frad->name);
+ netdev_warn(frad, "Memory squeeze on fr_add_pvc()\n");
return -ENOBUFS;
}
@@ -1083,14 +1074,14 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
used = pvc_is_used(pvc);
- if (type == ARPHRD_ETHER)
+ if (type == ARPHRD_ETHER) {
dev = alloc_netdev(0, "pvceth%d", ether_setup);
- else
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+ } else
dev = alloc_netdev(0, "pvc%d", pvc_setup);
if (!dev) {
- printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
- frad->name);
+ netdev_warn(frad, "Memory squeeze on fr_pvc()\n");
delete_unused_pvcs(hdlc);
return -ENOBUFS;
}
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
index 941f053e650e..0d7645581f91 100644
--- a/drivers/net/wan/hdlc_ppp.c
+++ b/drivers/net/wan/hdlc_ppp.c
@@ -223,8 +223,7 @@ static void ppp_tx_cp(struct net_device *dev, u16 pid, u8 code,
skb = dev_alloc_skb(sizeof(struct hdlc_header) +
sizeof(struct cp_header) + magic_len + len);
if (!skb) {
- printk(KERN_WARNING "%s: out of memory in ppp_tx_cp()\n",
- dev->name);
+ netdev_warn(dev, "out of memory in ppp_tx_cp()\n");
return;
}
skb_reserve(skb, sizeof(struct hdlc_header));
@@ -345,7 +344,7 @@ static void ppp_cp_event(struct net_device *dev, u16 pid, u16 event, u8 code,
ppp_tx_cp(dev, pid, CP_CODE_REJ, ++ppp->seq, len, data);
if (old_state != OPENED && proto->state == OPENED) {
- printk(KERN_INFO "%s: %s up\n", dev->name, proto_name(pid));
+ netdev_info(dev, "%s up\n", proto_name(pid));
if (pid == PID_LCP) {
netif_dormant_off(dev);
ppp_cp_event(dev, PID_IPCP, START, 0, 0, 0, NULL);
@@ -356,7 +355,7 @@ static void ppp_cp_event(struct net_device *dev, u16 pid, u16 event, u8 code,
}
}
if (old_state == OPENED && proto->state != OPENED) {
- printk(KERN_INFO "%s: %s down\n", dev->name, proto_name(pid));
+ netdev_info(dev, "%s down\n", proto_name(pid));
if (pid == PID_LCP) {
netif_dormant_on(dev);
ppp_cp_event(dev, PID_IPCP, STOP, 0, 0, 0, NULL);
@@ -516,37 +515,37 @@ static int ppp_rx(struct sk_buff *skb)
switch (cp->code) {
case CP_CONF_REQ:
ppp_cp_parse_cr(dev, pid, cp->id, len, skb->data);
- goto out;
+ break;
case CP_CONF_ACK:
if (cp->id == proto->cr_id)
ppp_cp_event(dev, pid, RCA, 0, 0, 0, NULL);
- goto out;
+ break;
case CP_CONF_REJ:
case CP_CONF_NAK:
if (cp->id == proto->cr_id)
ppp_cp_event(dev, pid, RCN, 0, 0, 0, NULL);
- goto out;
+ break;
case CP_TERM_REQ:
ppp_cp_event(dev, pid, RTR, 0, cp->id, 0, NULL);
- goto out;
+ break;
case CP_TERM_ACK:
ppp_cp_event(dev, pid, RTA, 0, 0, 0, NULL);
- goto out;
+ break;
case CP_CODE_REJ:
ppp_cp_event(dev, pid, RXJ_BAD, 0, 0, 0, NULL);
- goto out;
+ break;
default:
len += sizeof(struct cp_header);
if (len > dev->mtu)
len = dev->mtu;
ppp_cp_event(dev, pid, RUC, 0, 0, len, cp);
- goto out;
+ break;
}
goto out;
@@ -585,7 +584,7 @@ static void ppp_timer(unsigned long arg)
break;
if (time_after(jiffies, ppp->last_pong +
ppp->keepalive_timeout * HZ)) {
- printk(KERN_INFO "%s: Link down\n", proto->dev->name);
+ netdev_info(proto->dev, "Link down\n");
ppp_cp_event(proto->dev, PID_LCP, STOP, 0, 0, 0, NULL);
ppp_cp_event(proto->dev, PID_LCP, START, 0, 0, 0, NULL);
} else { /* send keep-alive packet */
diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c
index 70527e5a54a2..a49aec5efd20 100644
--- a/drivers/net/wan/hdlc_x25.c
+++ b/drivers/net/wan/hdlc_x25.c
@@ -34,7 +34,7 @@ static void x25_connect_disconnect(struct net_device *dev, int reason, int code)
unsigned char *ptr;
if ((skb = dev_alloc_skb(1)) == NULL) {
- printk(KERN_ERR "%s: out of memory\n", dev->name);
+ netdev_err(dev, "out of memory\n");
return;
}
@@ -106,9 +106,8 @@ static netdev_tx_t x25_xmit(struct sk_buff *skb, struct net_device *dev)
/* Send connect confirm. msg to level 3 */
x25_connected(dev, 0);
else
- printk(KERN_ERR "%s: LAPB connect request "
- "failed, error code = %i\n",
- dev->name, result);
+ netdev_err(dev, "LAPB connect request failed, error code = %i\n",
+ result);
}
break;
@@ -118,9 +117,8 @@ static netdev_tx_t x25_xmit(struct sk_buff *skb, struct net_device *dev)
/* Send disconnect confirm. msg to level 3 */
x25_disconnected(dev, 0);
else
- printk(KERN_ERR "%s: LAPB disconnect request "
- "failed, error code = %i\n",
- dev->name, result);
+ netdev_err(dev, "LAPB disconnect request failed, error code = %i\n",
+ result);
}
break;
@@ -136,15 +134,15 @@ static netdev_tx_t x25_xmit(struct sk_buff *skb, struct net_device *dev)
static int x25_open(struct net_device *dev)
{
- struct lapb_register_struct cb;
int result;
-
- cb.connect_confirmation = x25_connected;
- cb.connect_indication = x25_connected;
- cb.disconnect_confirmation = x25_disconnected;
- cb.disconnect_indication = x25_disconnected;
- cb.data_indication = x25_data_indication;
- cb.data_transmit = x25_data_transmit;
+ static const struct lapb_register_struct cb = {
+ .connect_confirmation = x25_connected,
+ .connect_indication = x25_connected,
+ .disconnect_confirmation = x25_disconnected,
+ .disconnect_indication = x25_disconnected,
+ .data_indication = x25_data_indication,
+ .data_transmit = x25_data_transmit,
+ };
result = lapb_register(dev, &cb);
if (result != LAPB_OK)
diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c
index e817583e6ec5..3d80e4267de8 100644
--- a/drivers/net/wan/hostess_sv11.c
+++ b/drivers/net/wan/hostess_sv11.c
@@ -20,6 +20,8 @@
* Generic HDLC port Copyright (C) 2008 Krzysztof Halasa <khc@pm.waw.pl>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
@@ -192,8 +194,7 @@ static struct z8530_dev *sv11_init(int iobase, int irq)
*/
if (!request_region(iobase, 8, "Comtrol SV11")) {
- printk(KERN_WARNING "hostess: I/O 0x%X already in use.\n",
- iobase);
+ pr_warn("I/O 0x%X already in use\n", iobase);
return NULL;
}
@@ -221,7 +222,7 @@ static struct z8530_dev *sv11_init(int iobase, int irq)
if (request_irq(irq, z8530_interrupt, IRQF_DISABLED,
"Hostess SV11", sv) < 0) {
- printk(KERN_WARNING "hostess: IRQ %d already in use.\n", irq);
+ pr_warn("IRQ %d already in use\n", irq);
goto err_irq;
}
@@ -255,7 +256,7 @@ static struct z8530_dev *sv11_init(int iobase, int irq)
*/
if (z8530_init(sv)) {
- printk(KERN_ERR "Z8530 series device not found.\n");
+ pr_err("Z8530 series device not found\n");
enable_irq(irq);
goto free_dma;
}
@@ -282,7 +283,7 @@ static struct z8530_dev *sv11_init(int iobase, int irq)
netdev->irq = irq;
if (register_hdlc_device(netdev)) {
- printk(KERN_ERR "hostess: unable to register HDLC device.\n");
+ pr_err("unable to register HDLC device\n");
free_netdev(netdev);
goto free_dma;
}
diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c
index f1e1643dc3eb..aaaca9aa2293 100644
--- a/drivers/net/wan/ixp4xx_hss.c
+++ b/drivers/net/wan/ixp4xx_hss.c
@@ -8,6 +8,8 @@
* as published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/bitops.h>
#include <linux/cdev.h>
#include <linux/dma-mapping.h>
@@ -358,9 +360,8 @@ static void hss_npe_send(struct port *port, struct msg *msg, const char* what)
{
u32 *val = (u32*)msg;
if (npe_send_message(port->npe, msg, what)) {
- printk(KERN_CRIT "HSS-%i: unable to send command [%08X:%08X]"
- " to %s\n", port->id, val[0], val[1],
- npe_name(port->npe));
+ pr_crit("HSS-%i: unable to send command [%08X:%08X] to %s\n",
+ port->id, val[0], val[1], npe_name(port->npe));
BUG();
}
}
@@ -447,8 +448,7 @@ static void hss_config(struct port *port)
if (npe_recv_message(port->npe, &msg, "HSS_LOAD_CONFIG") ||
/* HSS_LOAD_CONFIG for port #1 returns port_id = #4 */
msg.cmd != PORT_CONFIG_LOAD || msg.data32) {
- printk(KERN_CRIT "HSS-%i: HSS_LOAD_CONFIG failed\n",
- port->id);
+ pr_crit("HSS-%i: HSS_LOAD_CONFIG failed\n", port->id);
BUG();
}
@@ -477,8 +477,7 @@ static u32 hss_get_status(struct port *port)
msg.hss_port = port->id;
hss_npe_send(port, &msg, "PORT_ERROR_READ");
if (npe_recv_message(port->npe, &msg, "PORT_ERROR_READ")) {
- printk(KERN_CRIT "HSS-%i: unable to read HSS status\n",
- port->id);
+ pr_crit("HSS-%i: unable to read HSS status\n", port->id);
BUG();
}
@@ -736,9 +735,8 @@ static int hss_hdlc_poll(struct napi_struct *napi, int budget)
dev->stats.rx_errors++;
break;
default: /* FIXME - remove printk */
- printk(KERN_ERR "%s: hss_hdlc_poll: status 0x%02X"
- " errors %u\n", dev->name, desc->status,
- desc->error_count);
+ netdev_err(dev, "hss_hdlc_poll: status 0x%02X errors %u\n",
+ desc->status, desc->error_count);
dev->stats.rx_errors++;
}
@@ -1127,8 +1125,8 @@ static int hss_hdlc_close(struct net_device *dev)
buffs--;
if (buffs)
- printk(KERN_CRIT "%s: unable to drain RX queue, %i buffer(s)"
- " left in NPE\n", dev->name, buffs);
+ netdev_crit(dev, "unable to drain RX queue, %i buffer(s) left in NPE\n",
+ buffs);
buffs = TX_DESCS;
while (queue_get_desc(queue_ids[port->id].tx, port, 1) >= 0)
@@ -1143,8 +1141,8 @@ static int hss_hdlc_close(struct net_device *dev)
} while (++i < MAX_CLOSE_WAIT);
if (buffs)
- printk(KERN_CRIT "%s: unable to drain TX queue, %i buffer(s) "
- "left in NPE\n", dev->name, buffs);
+ netdev_crit(dev, "unable to drain TX queue, %i buffer(s) left in NPE\n",
+ buffs);
#if DEBUG_CLOSE
if (!buffs)
printk(KERN_DEBUG "Draining TX queues took %i cycles\n", i);
@@ -1364,7 +1362,7 @@ static int __devinit hss_init_one(struct platform_device *pdev)
platform_set_drvdata(pdev, port);
- printk(KERN_INFO "%s: HSS-%i\n", dev->name, port->id);
+ netdev_info(dev, "HSS-%i\n", port->id);
return 0;
err_free_netdev:
diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
index eec463f99c09..7beeb9b88a3b 100644
--- a/drivers/net/wan/lapbether.c
+++ b/drivers/net/wan/lapbether.c
@@ -20,6 +20,8 @@
* 2000-11-14 Henner Eisen dev_hold/put, NETDEV_GOING_DOWN support
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -165,13 +167,11 @@ static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,
break;
case X25_IFACE_CONNECT:
if ((err = lapb_connect_request(dev)) != LAPB_OK)
- printk(KERN_ERR "lapbeth: lapb_connect_request "
- "error: %d\n", err);
+ pr_err("lapb_connect_request error: %d\n", err);
goto drop;
case X25_IFACE_DISCONNECT:
if ((err = lapb_disconnect_request(dev)) != LAPB_OK)
- printk(KERN_ERR "lapbeth: lapb_disconnect_request "
- "err: %d\n", err);
+ pr_err("lapb_disconnect_request err: %d\n", err);
/* Fall thru */
default:
goto drop;
@@ -180,7 +180,7 @@ static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,
skb_pull(skb, 1);
if ((err = lapb_data_request(dev, skb)) != LAPB_OK) {
- printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err);
+ pr_err("lapb_data_request error - %d\n", err);
goto drop;
}
out:
@@ -220,7 +220,7 @@ static void lapbeth_connected(struct net_device *dev, int reason)
struct sk_buff *skb = dev_alloc_skb(1);
if (!skb) {
- printk(KERN_ERR "lapbeth: out of memory\n");
+ pr_err("out of memory\n");
return;
}
@@ -237,7 +237,7 @@ static void lapbeth_disconnected(struct net_device *dev, int reason)
struct sk_buff *skb = dev_alloc_skb(1);
if (!skb) {
- printk(KERN_ERR "lapbeth: out of memory\n");
+ pr_err("out of memory\n");
return;
}
@@ -259,14 +259,13 @@ static int lapbeth_set_mac_address(struct net_device *dev, void *addr)
}
-static struct lapb_register_struct lapbeth_callbacks = {
+static const struct lapb_register_struct lapbeth_callbacks = {
.connect_confirmation = lapbeth_connected,
.connect_indication = lapbeth_connected,
.disconnect_confirmation = lapbeth_disconnected,
.disconnect_indication = lapbeth_disconnected,
.data_indication = lapbeth_data_indication,
.data_transmit = lapbeth_data_transmit,
-
};
/*
@@ -277,7 +276,7 @@ static int lapbeth_open(struct net_device *dev)
int err;
if ((err = lapb_register(dev, &lapbeth_callbacks)) != LAPB_OK) {
- printk(KERN_ERR "lapbeth: lapb_register error - %d\n", err);
+ pr_err("lapb_register error: %d\n", err);
return -ENODEV;
}
@@ -292,7 +291,7 @@ static int lapbeth_close(struct net_device *dev)
netif_stop_queue(dev);
if ((err = lapb_unregister(dev)) != LAPB_OK)
- printk(KERN_ERR "lapbeth: lapb_unregister error - %d\n", err);
+ pr_err("lapb_unregister error: %d\n", err);
return 0;
}
diff --git a/drivers/net/wan/lmc/lmc_var.h b/drivers/net/wan/lmc/lmc_var.h
index 01ad45218d19..a1d202d8ad67 100644
--- a/drivers/net/wan/lmc/lmc_var.h
+++ b/drivers/net/wan/lmc/lmc_var.h
@@ -380,7 +380,7 @@ struct lmc___softc {
/* CSR6 settings */
#define OPERATION_MODE 0x00000200 /* Full Duplex */
#define PROMISC_MODE 0x00000040 /* Promiscuous Mode */
-#define RECIEVE_ALL 0x40000000 /* Receive All */
+#define RECEIVE_ALL 0x40000000 /* Receive All */
#define PASS_BAD_FRAMES 0x00000008 /* Pass Bad Frames */
/* Dec control registers CSR6 as well */
diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c
index 17d408fe693f..5129ad514d26 100644
--- a/drivers/net/wan/n2.c
+++ b/drivers/net/wan/n2.c
@@ -16,6 +16,8 @@
* SDL Inc. PPP/HDLC/CISCO driver
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/capability.h>
@@ -341,57 +343,57 @@ static int __init n2_run(unsigned long io, unsigned long irq,
int i;
if (io < 0x200 || io > 0x3FF || (io % N2_IOPORTS) != 0) {
- printk(KERN_ERR "n2: invalid I/O port value\n");
+ pr_err("invalid I/O port value\n");
return -ENODEV;
}
if (irq < 3 || irq > 15 || irq == 6) /* FIXME */ {
- printk(KERN_ERR "n2: invalid IRQ value\n");
+ pr_err("invalid IRQ value\n");
return -ENODEV;
}
if (winbase < 0xA0000 || winbase > 0xFFFFF || (winbase & 0xFFF) != 0) {
- printk(KERN_ERR "n2: invalid RAM value\n");
+ pr_err("invalid RAM value\n");
return -ENODEV;
}
card = kzalloc(sizeof(card_t), GFP_KERNEL);
if (card == NULL) {
- printk(KERN_ERR "n2: unable to allocate memory\n");
+ pr_err("unable to allocate memory\n");
return -ENOBUFS;
}
card->ports[0].dev = alloc_hdlcdev(&card->ports[0]);
card->ports[1].dev = alloc_hdlcdev(&card->ports[1]);
if (!card->ports[0].dev || !card->ports[1].dev) {
- printk(KERN_ERR "n2: unable to allocate memory\n");
+ pr_err("unable to allocate memory\n");
n2_destroy_card(card);
return -ENOMEM;
}
if (!request_region(io, N2_IOPORTS, devname)) {
- printk(KERN_ERR "n2: I/O port region in use\n");
+ pr_err("I/O port region in use\n");
n2_destroy_card(card);
return -EBUSY;
}
card->io = io;
if (request_irq(irq, sca_intr, 0, devname, card)) {
- printk(KERN_ERR "n2: could not allocate IRQ\n");
+ pr_err("could not allocate IRQ\n");
n2_destroy_card(card);
return -EBUSY;
}
card->irq = irq;
if (!request_mem_region(winbase, USE_WINDOWSIZE, devname)) {
- printk(KERN_ERR "n2: could not request RAM window\n");
+ pr_err("could not request RAM window\n");
n2_destroy_card(card);
return -EBUSY;
}
card->phy_winbase = winbase;
card->winbase = ioremap(winbase, USE_WINDOWSIZE);
if (!card->winbase) {
- printk(KERN_ERR "n2: ioremap() failed\n");
+ pr_err("ioremap() failed\n");
n2_destroy_card(card);
return -EFAULT;
}
@@ -413,7 +415,7 @@ static int __init n2_run(unsigned long io, unsigned long irq,
break;
default:
- printk(KERN_ERR "n2: invalid window size\n");
+ pr_err("invalid window size\n");
n2_destroy_card(card);
return -ENODEV;
}
@@ -433,12 +435,12 @@ static int __init n2_run(unsigned long io, unsigned long irq,
card->buff_offset = (valid0 + valid1) * sizeof(pkt_desc) *
(card->tx_ring_buffers + card->rx_ring_buffers);
- printk(KERN_INFO "n2: RISCom/N2 %u KB RAM, IRQ%u, "
- "using %u TX + %u RX packets rings\n", card->ram_size / 1024,
- card->irq, card->tx_ring_buffers, card->rx_ring_buffers);
+ pr_info("RISCom/N2 %u KB RAM, IRQ%u, using %u TX + %u RX packets rings\n",
+ card->ram_size / 1024, card->irq,
+ card->tx_ring_buffers, card->rx_ring_buffers);
if (card->tx_ring_buffers < 1) {
- printk(KERN_ERR "n2: RAM test failed\n");
+ pr_err("RAM test failed\n");
n2_destroy_card(card);
return -EIO;
}
@@ -474,16 +476,14 @@ static int __init n2_run(unsigned long io, unsigned long irq,
port->card = card;
if (register_hdlc_device(dev)) {
- printk(KERN_WARNING "n2: unable to register hdlc "
- "device\n");
+ pr_warn("unable to register hdlc device\n");
port->card = NULL;
n2_destroy_card(card);
return -ENOBUFS;
}
sca_init_port(port); /* Set up SCA memory */
- printk(KERN_INFO "%s: RISCom/N2 node %d\n",
- dev->name, port->phy_node);
+ netdev_info(dev, "RISCom/N2 node %d\n", port->phy_node);
}
*new_card = card;
@@ -498,12 +498,12 @@ static int __init n2_init(void)
{
if (hw==NULL) {
#ifdef MODULE
- printk(KERN_INFO "n2: no card initialized\n");
+ pr_info("no card initialized\n");
#endif
return -EINVAL; /* no parameters specified, abort */
}
- printk(KERN_INFO "%s\n", version);
+ pr_info("%s\n", version);
do {
unsigned long io, irq, ram;
@@ -541,7 +541,7 @@ static int __init n2_init(void)
return first_card ? 0 : -EINVAL;
}while(*hw++ == ':');
- printk(KERN_ERR "n2: invalid hardware parameters\n");
+ pr_err("invalid hardware parameters\n");
return first_card ? 0 : -EINVAL;
}
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
index 9617d3d0ee39..1eeedd6a10b1 100644
--- a/drivers/net/wan/pc300_drv.c
+++ b/drivers/net/wan/pc300_drv.c
@@ -212,6 +212,8 @@ static const char rcsid[] =
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
@@ -3242,7 +3244,7 @@ static inline void show_version(void)
rcsdate++;
tmp = strrchr(rcsdate, ' ');
*tmp = '\0';
- printk(KERN_INFO "Cyclades-PC300 driver %s %s\n", rcsvers, rcsdate);
+ pr_info("Cyclades-PC300 driver %s %s\n", rcsvers, rcsdate);
} /* show_version */
static const struct net_device_ops cpc_netdev_ops = {
diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c
index 1c65d1c33873..d47d2cd10475 100644
--- a/drivers/net/wan/pc300_tty.c
+++ b/drivers/net/wan/pc300_tty.c
@@ -755,7 +755,7 @@ void cpc_tty_receive(pc300dev_t *pc300dev)
dsr_rx = cpc_readb(card->hw.scabase + DSR_RX(ch));
- cpc_tty = (st_cpc_tty_area *)pc300dev->cpc_tty;
+ cpc_tty = pc300dev->cpc_tty;
while (1) {
rx_len = 0;
diff --git a/drivers/net/wan/pc300too.c b/drivers/net/wan/pc300too.c
index c7ab3becd261..c49c1b3c7aad 100644
--- a/drivers/net/wan/pc300too.c
+++ b/drivers/net/wan/pc300too.c
@@ -17,6 +17,8 @@
* PC300/X21 cards.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -318,7 +320,7 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
card = kzalloc(sizeof(card_t), GFP_KERNEL);
if (card == NULL) {
- printk(KERN_ERR "pc300: unable to allocate memory\n");
+ pr_err("unable to allocate memory\n");
pci_release_regions(pdev);
pci_disable_device(pdev);
return -ENOBUFS;
@@ -328,7 +330,7 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
if (pci_resource_len(pdev, 0) != PC300_PLX_SIZE ||
pci_resource_len(pdev, 2) != PC300_SCA_SIZE ||
pci_resource_len(pdev, 3) < 16384) {
- printk(KERN_ERR "pc300: invalid card EEPROM parameters\n");
+ pr_err("invalid card EEPROM parameters\n");
pc300_pci_remove_one(pdev);
return -EFAULT;
}
@@ -345,7 +347,7 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
if (card->plxbase == NULL ||
card->scabase == NULL ||
card->rambase == NULL) {
- printk(KERN_ERR "pc300: ioremap() failed\n");
+ pr_err("ioremap() failed\n");
pc300_pci_remove_one(pdev);
}
@@ -370,7 +372,7 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
for (i = 0; i < card->n_ports; i++)
if (!(card->ports[i].netdev = alloc_hdlcdev(&card->ports[i]))) {
- printk(KERN_ERR "pc300: unable to allocate memory\n");
+ pr_err("unable to allocate memory\n");
pc300_pci_remove_one(pdev);
return -ENOMEM;
}
@@ -411,15 +413,14 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
card->buff_offset = card->n_ports * sizeof(pkt_desc) *
(card->tx_ring_buffers + card->rx_ring_buffers);
- printk(KERN_INFO "pc300: PC300/%s, %u KB RAM at 0x%x, IRQ%u, "
- "using %u TX + %u RX packets rings\n",
- card->type == PC300_X21 ? "X21" :
- card->type == PC300_TE ? "TE" : "RSV",
- ramsize / 1024, ramphys, pdev->irq,
- card->tx_ring_buffers, card->rx_ring_buffers);
+ pr_info("PC300/%s, %u KB RAM at 0x%x, IRQ%u, using %u TX + %u RX packets rings\n",
+ card->type == PC300_X21 ? "X21" :
+ card->type == PC300_TE ? "TE" : "RSV",
+ ramsize / 1024, ramphys, pdev->irq,
+ card->tx_ring_buffers, card->rx_ring_buffers);
if (card->tx_ring_buffers < 1) {
- printk(KERN_ERR "pc300: RAM test failed\n");
+ pr_err("RAM test failed\n");
pc300_pci_remove_one(pdev);
return -EFAULT;
}
@@ -429,8 +430,7 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
/* Allocate IRQ */
if (request_irq(pdev->irq, sca_intr, IRQF_SHARED, "pc300", card)) {
- printk(KERN_WARNING "pc300: could not allocate IRQ%d.\n",
- pdev->irq);
+ pr_warn("could not allocate IRQ%d\n", pdev->irq);
pc300_pci_remove_one(pdev);
return -EBUSY;
}
@@ -466,15 +466,13 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
sca_init_port(port);
if (register_hdlc_device(dev)) {
- printk(KERN_ERR "pc300: unable to register hdlc "
- "device\n");
+ pr_err("unable to register hdlc device\n");
port->card = NULL;
pc300_pci_remove_one(pdev);
return -ENOBUFS;
}
- printk(KERN_INFO "%s: PC300 channel %d\n",
- dev->name, port->chan);
+ netdev_info(dev, "PC300 channel %d\n", port->chan);
}
return 0;
}
@@ -505,11 +503,11 @@ static struct pci_driver pc300_pci_driver = {
static int __init pc300_init_module(void)
{
if (pci_clock_freq < 1000000 || pci_clock_freq > 80000000) {
- printk(KERN_ERR "pc300: Invalid PCI clock frequency\n");
+ pr_err("Invalid PCI clock frequency\n");
return -EINVAL;
}
if (use_crystal_clock != 0 && use_crystal_clock != 1) {
- printk(KERN_ERR "pc300: Invalid 'use_crystal_clock' value\n");
+ pr_err("Invalid 'use_crystal_clock' value\n");
return -EINVAL;
}
diff --git a/drivers/net/wan/pci200syn.c b/drivers/net/wan/pci200syn.c
index fd7375955e41..1ce21163c776 100644
--- a/drivers/net/wan/pci200syn.c
+++ b/drivers/net/wan/pci200syn.c
@@ -14,6 +14,8 @@
* PLX Technology Inc. PCI9052 Data Book
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/capability.h>
@@ -297,7 +299,7 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
card = kzalloc(sizeof(card_t), GFP_KERNEL);
if (card == NULL) {
- printk(KERN_ERR "pci200syn: unable to allocate memory\n");
+ pr_err("unable to allocate memory\n");
pci_release_regions(pdev);
pci_disable_device(pdev);
return -ENOBUFS;
@@ -306,7 +308,7 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
card->ports[0].netdev = alloc_hdlcdev(&card->ports[0]);
card->ports[1].netdev = alloc_hdlcdev(&card->ports[1]);
if (!card->ports[0].netdev || !card->ports[1].netdev) {
- printk(KERN_ERR "pci200syn: unable to allocate memory\n");
+ pr_err("unable to allocate memory\n");
pci200_pci_remove_one(pdev);
return -ENOMEM;
}
@@ -314,7 +316,7 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
if (pci_resource_len(pdev, 0) != PCI200SYN_PLX_SIZE ||
pci_resource_len(pdev, 2) != PCI200SYN_SCA_SIZE ||
pci_resource_len(pdev, 3) < 16384) {
- printk(KERN_ERR "pci200syn: invalid card EEPROM parameters\n");
+ pr_err("invalid card EEPROM parameters\n");
pci200_pci_remove_one(pdev);
return -EFAULT;
}
@@ -331,7 +333,7 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
if (card->plxbase == NULL ||
card->scabase == NULL ||
card->rambase == NULL) {
- printk(KERN_ERR "pci200syn: ioremap() failed\n");
+ pr_err("ioremap() failed\n");
pci200_pci_remove_one(pdev);
return -EFAULT;
}
@@ -357,12 +359,12 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
card->buff_offset = 2 * sizeof(pkt_desc) * (card->tx_ring_buffers +
card->rx_ring_buffers);
- printk(KERN_INFO "pci200syn: %u KB RAM at 0x%x, IRQ%u, using %u TX +"
- " %u RX packets rings\n", ramsize / 1024, ramphys,
- pdev->irq, card->tx_ring_buffers, card->rx_ring_buffers);
+ pr_info("%u KB RAM at 0x%x, IRQ%u, using %u TX + %u RX packets rings\n",
+ ramsize / 1024, ramphys,
+ pdev->irq, card->tx_ring_buffers, card->rx_ring_buffers);
if (card->tx_ring_buffers < 1) {
- printk(KERN_ERR "pci200syn: RAM test failed\n");
+ pr_err("RAM test failed\n");
pci200_pci_remove_one(pdev);
return -EFAULT;
}
@@ -373,8 +375,7 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
/* Allocate IRQ */
if (request_irq(pdev->irq, sca_intr, IRQF_SHARED, "pci200syn", card)) {
- printk(KERN_WARNING "pci200syn: could not allocate IRQ%d.\n",
- pdev->irq);
+ pr_warn("could not allocate IRQ%d\n", pdev->irq);
pci200_pci_remove_one(pdev);
return -EBUSY;
}
@@ -400,15 +401,13 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
port->card = card;
sca_init_port(port);
if (register_hdlc_device(dev)) {
- printk(KERN_ERR "pci200syn: unable to register hdlc "
- "device\n");
+ pr_err("unable to register hdlc device\n");
port->card = NULL;
pci200_pci_remove_one(pdev);
return -ENOBUFS;
}
- printk(KERN_INFO "%s: PCI200SYN channel %d\n",
- dev->name, port->chan);
+ netdev_info(dev, "PCI200SYN channel %d\n", port->chan);
}
sca_flush(card);
@@ -435,7 +434,7 @@ static struct pci_driver pci200_pci_driver = {
static int __init pci200_init_module(void)
{
if (pci_clock_freq < 1000000 || pci_clock_freq > 80000000) {
- printk(KERN_ERR "pci200syn: Invalid PCI clock frequency\n");
+ pr_err("Invalid PCI clock frequency\n");
return -EINVAL;
}
return pci_register_driver(&pci200_pci_driver);
diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c
index cff13a9597cd..783168cce077 100644
--- a/drivers/net/wan/sbni.c
+++ b/drivers/net/wan/sbni.c
@@ -37,6 +37,8 @@
* Known problem: this driver wasn't tested on multiprocessor machine.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
@@ -200,8 +202,8 @@ sbni_isa_probe( struct net_device *dev )
return 0;
else {
- printk( KERN_ERR "sbni: base address 0x%lx is busy, or adapter "
- "is malfunctional!\n", dev->base_addr );
+ pr_err("base address 0x%lx is busy, or adapter is malfunctional!\n",
+ dev->base_addr);
return -ENODEV;
}
}
@@ -210,7 +212,7 @@ static const struct net_device_ops sbni_netdev_ops = {
.ndo_open = sbni_open,
.ndo_stop = sbni_close,
.ndo_start_xmit = sbni_start_xmit,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_do_ioctl = sbni_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
@@ -226,7 +228,6 @@ static void __init sbni_devsetup(struct net_device *dev)
int __init sbni_probe(int unit)
{
struct net_device *dev;
- static unsigned version_printed __initdata = 0;
int err;
dev = alloc_netdev(sizeof(struct net_local), "sbni", sbni_devsetup);
@@ -250,8 +251,7 @@ int __init sbni_probe(int unit)
free_netdev(dev);
return err;
}
- if( version_printed++ == 0 )
- printk( KERN_INFO "%s", version );
+ pr_info_once("%s", version);
return 0;
}
@@ -303,7 +303,6 @@ sbni_pci_probe( struct net_device *dev )
!= NULL ) {
int pci_irq_line;
unsigned long pci_ioaddr;
- u16 subsys;
if( pdev->vendor != SBNI_PCI_VENDOR &&
pdev->device != SBNI_PCI_DEVICE )
@@ -314,9 +313,7 @@ sbni_pci_probe( struct net_device *dev )
/* Avoid already found cards from previous calls */
if( !request_region( pci_ioaddr, SBNI_IO_EXTENT, dev->name ) ) {
- pci_read_config_word( pdev, PCI_SUBSYSTEM_ID, &subsys );
-
- if (subsys != 2)
+ if (pdev->subsystem_device != 2)
continue;
/* Dual adapter is present */
@@ -326,9 +323,9 @@ sbni_pci_probe( struct net_device *dev )
}
if (pci_irq_line <= 0 || pci_irq_line >= nr_irqs)
- printk( KERN_WARNING
- " WARNING: The PCI BIOS assigned this PCI card to IRQ %d, which is unlikely to work!.\n"
- " You should use the PCI BIOS setup to assign a valid IRQ line.\n",
+ pr_warn(
+"WARNING: The PCI BIOS assigned this PCI card to IRQ %d, which is unlikely to work!.\n"
+"You should use the PCI BIOS setup to assign a valid IRQ line.\n",
pci_irq_line );
/* avoiding re-enable dual adapters */
@@ -372,8 +369,7 @@ sbni_probe1( struct net_device *dev, unsigned long ioaddr, int irq )
outb( 0, ioaddr + CSR0 );
if( !irq ) {
- printk( KERN_ERR "%s: can't detect device irq!\n",
- dev->name );
+ pr_err("%s: can't detect device irq!\n", dev->name);
release_region( ioaddr, SBNI_IO_EXTENT );
return NULL;
}
@@ -386,7 +382,7 @@ sbni_probe1( struct net_device *dev, unsigned long ioaddr, int irq )
/* Fill in sbni-specific dev fields. */
nl = netdev_priv(dev);
if( !nl ) {
- printk( KERN_ERR "%s: unable to get memory!\n", dev->name );
+ pr_err("%s: unable to get memory!\n", dev->name);
release_region( ioaddr, SBNI_IO_EXTENT );
return NULL;
}
@@ -415,21 +411,21 @@ sbni_probe1( struct net_device *dev, unsigned long ioaddr, int irq )
if( inb( ioaddr + CSR0 ) & 0x01 )
nl->state |= FL_SLOW_MODE;
- printk( KERN_NOTICE "%s: ioaddr %#lx, irq %d, "
- "MAC: 00:ff:01:%02x:%02x:%02x\n",
- dev->name, dev->base_addr, dev->irq,
- ((u8 *) dev->dev_addr) [3],
- ((u8 *) dev->dev_addr) [4],
- ((u8 *) dev->dev_addr) [5] );
+ pr_notice("%s: ioaddr %#lx, irq %d, MAC: 00:ff:01:%02x:%02x:%02x\n",
+ dev->name, dev->base_addr, dev->irq,
+ ((u8 *)dev->dev_addr)[3],
+ ((u8 *)dev->dev_addr)[4],
+ ((u8 *)dev->dev_addr)[5]);
- printk( KERN_NOTICE "%s: speed %d, receive level ", dev->name,
- ( (nl->state & FL_SLOW_MODE) ? 500000 : 2000000)
- / (1 << nl->csr1.rate) );
+ pr_notice("%s: speed %d",
+ dev->name,
+ ((nl->state & FL_SLOW_MODE) ? 500000 : 2000000)
+ / (1 << nl->csr1.rate));
if( nl->delta_rxl == 0 )
- printk( "0x%x (fixed)\n", nl->cur_rxl_index );
+ pr_cont(", receive level 0x%x (fixed)\n", nl->cur_rxl_index);
else
- printk( "(auto)\n");
+ pr_cont(", receive level (auto)\n");
#ifdef CONFIG_SBNI_MULTILINE
nl->master = dev;
@@ -568,7 +564,7 @@ handle_channel( struct net_device *dev )
*/
csr0 = inb( ioaddr + CSR0 );
if( !(csr0 & TR_RDY) || (csr0 & RC_RDY) )
- printk( KERN_ERR "%s: internal error!\n", dev->name );
+ netdev_err(dev, "internal error!\n");
/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */
if( req_ans || nl->tx_frameno != 0 )
@@ -851,7 +847,7 @@ prepare_to_send( struct sk_buff *skb, struct net_device *dev )
/* nl->tx_buf_p == NULL here! */
if( nl->tx_buf_p )
- printk( KERN_ERR "%s: memory leak!\n", dev->name );
+ netdev_err(dev, "memory leak!\n");
nl->outpos = 0;
nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
@@ -1179,16 +1175,15 @@ sbni_open( struct net_device *dev )
((struct net_local *) (netdev_priv(*p)))
->second = dev;
- printk( KERN_NOTICE "%s: using shared irq "
- "with %s\n", dev->name, (*p)->name );
+ netdev_notice(dev, "using shared irq with %s\n",
+ (*p)->name);
nl->state |= FL_SECONDARY;
goto handler_attached;
}
}
if( request_irq(dev->irq, sbni_interrupt, IRQF_SHARED, dev->name, dev) ) {
- printk( KERN_ERR "%s: unable to get IRQ %d.\n",
- dev->name, dev->irq );
+ netdev_err(dev, "unable to get IRQ %d\n", dev->irq);
return -EAGAIN;
}
@@ -1220,8 +1215,8 @@ sbni_close( struct net_device *dev )
struct net_local *nl = netdev_priv(dev);
if( nl->second && nl->second->flags & IFF_UP ) {
- printk( KERN_NOTICE "Secondary channel (%s) is active!\n",
- nl->second->name );
+ netdev_notice(dev, "Secondary channel (%s) is active!\n",
+ nl->second->name);
return -EBUSY;
}
@@ -1363,8 +1358,8 @@ sbni_ioctl( struct net_device *dev, struct ifreq *ifr, int cmd )
return -EFAULT;
slave_dev = dev_get_by_name(&init_net, slave_name );
if( !slave_dev || !(slave_dev->flags & IFF_UP) ) {
- printk( KERN_ERR "%s: trying to enslave non-active "
- "device %s\n", dev->name, slave_name );
+ netdev_err(dev, "trying to enslave non-active device %s\n",
+ slave_name);
return -EPERM;
}
@@ -1417,8 +1412,7 @@ enslave( struct net_device *dev, struct net_device *slave_dev )
spin_unlock( &snl->lock );
spin_unlock( &nl->lock );
- printk( KERN_NOTICE "%s: slave device (%s) attached.\n",
- dev->name, slave_dev->name );
+ netdev_notice(dev, "slave device (%s) attached\n", slave_dev->name);
return 0;
}
@@ -1547,7 +1541,7 @@ sbni_setup( char *p )
break;
}
bad_param:
- printk( KERN_ERR "Error in sbni kernel parameter!\n" );
+ pr_err("Error in sbni kernel parameter!\n");
return 0;
}
diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c
index 3f4e2b5684db..c8531612eea9 100644
--- a/drivers/net/wan/sdla.c
+++ b/drivers/net/wan/sdla.c
@@ -32,6 +32,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
@@ -350,24 +352,24 @@ static void sdla_errors(struct net_device *dev, int cmd, int dlci, int ret, int
case SDLA_RET_MODEM:
state = data;
if (*state & SDLA_MODEM_DCD_LOW)
- printk(KERN_INFO "%s: Modem DCD unexpectedly low!\n", dev->name);
+ netdev_info(dev, "Modem DCD unexpectedly low!\n");
if (*state & SDLA_MODEM_CTS_LOW)
- printk(KERN_INFO "%s: Modem CTS unexpectedly low!\n", dev->name);
+ netdev_info(dev, "Modem CTS unexpectedly low!\n");
/* I should probably do something about this! */
break;
case SDLA_RET_CHANNEL_OFF:
- printk(KERN_INFO "%s: Channel became inoperative!\n", dev->name);
+ netdev_info(dev, "Channel became inoperative!\n");
/* same here */
break;
case SDLA_RET_CHANNEL_ON:
- printk(KERN_INFO "%s: Channel became operative!\n", dev->name);
+ netdev_info(dev, "Channel became operative!\n");
/* same here */
break;
case SDLA_RET_DLCI_STATUS:
- printk(KERN_INFO "%s: Status change reported by Access Node.\n", dev->name);
+ netdev_info(dev, "Status change reported by Access Node\n");
len /= sizeof(struct _dlci_stat);
for(pstatus = data, i=0;i < len;i++,pstatus++)
{
@@ -382,29 +384,32 @@ static void sdla_errors(struct net_device *dev, int cmd, int dlci, int ret, int
sprintf(line, "unknown status: %02X", pstatus->flags);
state = line;
}
- printk(KERN_INFO "%s: DLCI %i: %s.\n", dev->name, pstatus->dlci, state);
+ netdev_info(dev, "DLCI %i: %s\n",
+ pstatus->dlci, state);
/* same here */
}
break;
case SDLA_RET_DLCI_UNKNOWN:
- printk(KERN_INFO "%s: Received unknown DLCIs:", dev->name);
+ netdev_info(dev, "Received unknown DLCIs:");
len /= sizeof(short);
for(pdlci = data,i=0;i < len;i++,pdlci++)
- printk(" %i", *pdlci);
- printk("\n");
+ pr_cont(" %i", *pdlci);
+ pr_cont("\n");
break;
case SDLA_RET_TIMEOUT:
- printk(KERN_ERR "%s: Command timed out!\n", dev->name);
+ netdev_err(dev, "Command timed out!\n");
break;
case SDLA_RET_BUF_OVERSIZE:
- printk(KERN_INFO "%s: Bc/CIR overflow, acceptable size is %i\n", dev->name, len);
+ netdev_info(dev, "Bc/CIR overflow, acceptable size is %i\n",
+ len);
break;
case SDLA_RET_BUF_TOO_BIG:
- printk(KERN_INFO "%s: Buffer size over specified max of %i\n", dev->name, len);
+ netdev_info(dev, "Buffer size over specified max of %i\n",
+ len);
break;
case SDLA_RET_CHANNEL_INACTIVE:
@@ -415,7 +420,8 @@ static void sdla_errors(struct net_device *dev, int cmd, int dlci, int ret, int
break;
default:
- printk(KERN_DEBUG "%s: Cmd 0x%2.2X generated return code 0x%2.2X\n", dev->name, cmd, ret);
+ netdev_dbg(dev, "Cmd 0x%02X generated return code 0x%02X\n",
+ cmd, ret);
/* Further processing could be done here */
break;
}
@@ -678,12 +684,14 @@ static netdev_tx_t sdla_transmit(struct sk_buff *skb,
case ARPHRD_FRAD:
if (skb->dev->type != ARPHRD_DLCI)
{
- printk(KERN_WARNING "%s: Non DLCI device, type %i, tried to send on FRAD module.\n", dev->name, skb->dev->type);
+ netdev_warn(dev, "Non DLCI device, type %i, tried to send on FRAD module\n",
+ skb->dev->type);
accept = 0;
}
break;
default:
- printk(KERN_WARNING "%s: unknown firmware type 0x%4.4X\n", dev->name, dev->type);
+ netdev_warn(dev, "unknown firmware type 0x%04X\n",
+ dev->type);
accept = 0;
break;
}
@@ -807,7 +815,8 @@ static void sdla_receive(struct net_device *dev)
if (i == CONFIG_DLCI_MAX)
{
- printk(KERN_NOTICE "%s: Received packet from invalid DLCI %i, ignoring.", dev->name, dlci);
+ netdev_notice(dev, "Received packet from invalid DLCI %i, ignoring\n",
+ dlci);
dev->stats.rx_errors++;
success = 0;
}
@@ -819,7 +828,7 @@ static void sdla_receive(struct net_device *dev)
skb = dev_alloc_skb(len + sizeof(struct frhdr));
if (skb == NULL)
{
- printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
+ netdev_notice(dev, "Memory squeeze, dropping packet\n");
dev->stats.rx_dropped++;
success = 0;
}
@@ -880,8 +889,7 @@ static irqreturn_t sdla_isr(int dummy, void *dev_id)
if (!flp->initialized)
{
- printk(KERN_WARNING "%s: irq %d for uninitialized device.\n",
- dev->name, dev->irq);
+ netdev_warn(dev, "irq %d for uninitialized device\n", dev->irq);
return IRQ_NONE;
}
@@ -901,7 +909,7 @@ static irqreturn_t sdla_isr(int dummy, void *dev_id)
case SDLA_INTR_TX:
case SDLA_INTR_COMPLETE:
case SDLA_INTR_TIMER:
- printk(KERN_WARNING "%s: invalid irq flag 0x%02X.\n", dev->name, byte);
+ netdev_warn(dev, "invalid irq flag 0x%02X\n", byte);
break;
}
@@ -1347,7 +1355,7 @@ static int sdla_set_config(struct net_device *dev, struct ifmap *map)
return -EINVAL;
if (!request_region(map->base_addr, SDLA_IO_EXTENTS, dev->name)){
- printk(KERN_WARNING "SDLA: io-port 0x%04lx in use\n", dev->base_addr);
+ pr_warn("io-port 0x%04lx in use\n", dev->base_addr);
return -EINVAL;
}
base = map->base_addr;
@@ -1412,7 +1420,7 @@ static int sdla_set_config(struct net_device *dev, struct ifmap *map)
}
}
- printk(KERN_NOTICE "%s: Unknown card type\n", dev->name);
+ netdev_notice(dev, "Unknown card type\n");
err = -ENODEV;
goto fail;
diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c
index e91457d6023e..0b4fd05e1508 100644
--- a/drivers/net/wan/sealevel.c
+++ b/drivers/net/wan/sealevel.c
@@ -12,6 +12,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
@@ -190,7 +192,7 @@ static int slvl_setup(struct slvl_device *sv, int iobase, int irq)
dev->irq = irq;
if (register_hdlc_device(dev)) {
- printk(KERN_ERR "sealevel: unable to register HDLC device\n");
+ pr_err("unable to register HDLC device\n");
free_netdev(dev);
return -1;
}
@@ -215,8 +217,7 @@ static __init struct slvl_board *slvl_init(int iobase, int irq,
*/
if (!request_region(iobase, 8, "Sealevel 4021")) {
- printk(KERN_WARNING "sealevel: I/O 0x%X already in use.\n",
- iobase);
+ pr_warn("I/O 0x%X already in use\n", iobase);
return NULL;
}
@@ -267,7 +268,7 @@ static __init struct slvl_board *slvl_init(int iobase, int irq,
if (request_irq(irq, z8530_interrupt, IRQF_DISABLED,
"SeaLevel", dev) < 0) {
- printk(KERN_WARNING "sealevel: IRQ %d already in use.\n", irq);
+ pr_warn("IRQ %d already in use\n", irq);
goto err_request_irq;
}
@@ -292,7 +293,7 @@ static __init struct slvl_board *slvl_init(int iobase, int irq,
*/
if (z8530_init(dev) != 0) {
- printk(KERN_ERR "Z8530 series device not found.\n");
+ pr_err("Z8530 series device not found\n");
enable_irq(irq);
goto free_hw;
}
diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c
index db73a7be199f..44b707197258 100644
--- a/drivers/net/wan/wanxl.c
+++ b/drivers/net/wan/wanxl.c
@@ -13,6 +13,8 @@
* - wanXL100 will require minor driver modifications, no access to hw
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -22,6 +24,7 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/hdlc.h>
@@ -101,9 +104,8 @@ static inline dma_addr_t pci_map_single_debug(struct pci_dev *pdev, void *ptr,
{
dma_addr_t addr = pci_map_single(pdev, ptr, size, direction);
if (addr + size > 0x100000000LL)
- printk(KERN_CRIT "wanXL %s: pci_map_single() returned memory"
- " at 0x%LX!\n", pci_name(pdev),
- (unsigned long long)addr);
+ pr_crit("%s: pci_map_single() returned memory at 0x%llx!\n",
+ pci_name(pdev), (unsigned long long)addr);
return addr;
}
@@ -146,8 +148,8 @@ static inline void wanxl_cable_intr(port_t *port)
}
dte = (value & STATUS_CABLE_DCE) ? " DCE" : " DTE";
}
- printk(KERN_INFO "%s: %s%s module, %s cable%s%s\n",
- port->dev->name, pm, dte, cable, dsr, dcd);
+ netdev_info(port->dev, "%s%s module, %s cable%s%s\n",
+ pm, dte, cable, dsr, dcd);
if (value & STATUS_CABLE_DCD)
netif_carrier_on(port->dev);
@@ -197,8 +199,8 @@ static inline void wanxl_rx_intr(card_t *card)
while (desc = &card->status->rx_descs[card->rx_in],
desc->stat != PACKET_EMPTY) {
if ((desc->stat & PACKET_PORT_MASK) > card->n_ports)
- printk(KERN_CRIT "wanXL %s: received packet for"
- " nonexistent port\n", pci_name(card->pdev));
+ pr_crit("%s: received packet for nonexistent port\n",
+ pci_name(card->pdev));
else {
struct sk_buff *skb = card->rx_skbs[card->rx_in];
port_t *port = &card->ports[desc->stat &
@@ -282,7 +284,7 @@ static netdev_tx_t wanxl_xmit(struct sk_buff *skb, struct net_device *dev)
printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name);
#endif
netif_stop_queue(dev);
- spin_unlock_irq(&port->lock);
+ spin_unlock(&port->lock);
return NETDEV_TX_BUSY; /* request packet to be queued */
}
@@ -396,7 +398,7 @@ static int wanxl_open(struct net_device *dev)
int i;
if (get_status(port)->open) {
- printk(KERN_ERR "%s: port already open\n", dev->name);
+ netdev_err(dev, "port already open\n");
return -EIO;
}
if ((i = hdlc_open(dev)) != 0)
@@ -416,7 +418,7 @@ static int wanxl_open(struct net_device *dev)
}
} while (time_after(timeout, jiffies));
- printk(KERN_ERR "%s: unable to open port\n", dev->name);
+ netdev_err(dev, "unable to open port\n");
/* ask the card to close the port, should it be still alive */
writel(1 << (DOORBELL_TO_CARD_CLOSE_0 + port->node), dbr);
return -EFAULT;
@@ -442,7 +444,7 @@ static int wanxl_close(struct net_device *dev)
} while (time_after(timeout, jiffies));
if (get_status(port)->open)
- printk(KERN_ERR "%s: unable to close port\n", dev->name);
+ netdev_err(dev, "unable to close port\n");
netif_stop_queue(dev);
@@ -567,11 +569,7 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
int i, ports, alloc_size;
#ifndef MODULE
- static int printed_version;
- if (!printed_version) {
- printed_version++;
- printk(KERN_INFO "%s\n", version);
- }
+ pr_info_once("%s\n", version);
#endif
i = pci_enable_device(pdev);
@@ -587,7 +585,7 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
work on most platforms */
if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(28)) ||
pci_set_dma_mask(pdev, DMA_BIT_MASK(28))) {
- printk(KERN_ERR "wanXL: No usable DMA configuration\n");
+ pr_err("No usable DMA configuration\n");
return -EIO;
}
@@ -606,8 +604,7 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
alloc_size = sizeof(card_t) + ports * sizeof(port_t);
card = kzalloc(alloc_size, GFP_KERNEL);
if (card == NULL) {
- printk(KERN_ERR "wanXL %s: unable to allocate memory\n",
- pci_name(pdev));
+ pr_err("%s: unable to allocate memory\n", pci_name(pdev));
pci_release_regions(pdev);
pci_disable_device(pdev);
return -ENOBUFS;
@@ -634,7 +631,7 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
to indicate the card can do 32-bit DMA addressing */
if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) ||
pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
- printk(KERN_ERR "wanXL: No usable DMA configuration\n");
+ pr_err("No usable DMA configuration\n");
wanxl_pci_remove_one(pdev);
return -EIO;
}
@@ -644,7 +641,7 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
card->plx = ioremap_nocache(plx_phy, 0x70);
if (!card->plx) {
- printk(KERN_ERR "wanxl: ioremap() failed\n");
+ pr_err("ioremap() failed\n");
wanxl_pci_remove_one(pdev);
return -EFAULT;
}
@@ -656,8 +653,8 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
timeout = jiffies + 20 * HZ;
while ((stat = readl(card->plx + PLX_MAILBOX_0)) != 0) {
if (time_before(timeout, jiffies)) {
- printk(KERN_WARNING "wanXL %s: timeout waiting for"
- " PUTS to complete\n", pci_name(pdev));
+ pr_warn("%s: timeout waiting for PUTS to complete\n",
+ pci_name(pdev));
wanxl_pci_remove_one(pdev);
return -ENODEV;
}
@@ -668,8 +665,8 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
break;
default:
- printk(KERN_WARNING "wanXL %s: PUTS test 0x%X"
- " failed\n", pci_name(pdev), stat & 0x30);
+ pr_warn("%s: PUTS test 0x%X failed\n",
+ pci_name(pdev), stat & 0x30);
wanxl_pci_remove_one(pdev);
return -ENODEV;
}
@@ -687,17 +684,16 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
/* sanity check the board's reported memory size */
if (ramsize < BUFFERS_ADDR +
(TX_BUFFERS + RX_BUFFERS) * BUFFER_LENGTH * ports) {
- printk(KERN_WARNING "wanXL %s: no enough on-board RAM"
- " (%u bytes detected, %u bytes required)\n",
- pci_name(pdev), ramsize, BUFFERS_ADDR +
- (TX_BUFFERS + RX_BUFFERS) * BUFFER_LENGTH * ports);
+ pr_warn("%s: no enough on-board RAM (%u bytes detected, %u bytes required)\n",
+ pci_name(pdev), ramsize,
+ BUFFERS_ADDR +
+ (TX_BUFFERS + RX_BUFFERS) * BUFFER_LENGTH * ports);
wanxl_pci_remove_one(pdev);
return -ENODEV;
}
if (wanxl_puts_command(card, MBX1_CMD_BSWAP)) {
- printk(KERN_WARNING "wanXL %s: unable to Set Byte Swap"
- " Mode\n", pci_name(pdev));
+ pr_warn("%s: unable to Set Byte Swap Mode\n", pci_name(pdev));
wanxl_pci_remove_one(pdev);
return -ENODEV;
}
@@ -714,7 +710,7 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
mem = ioremap_nocache(mem_phy, PDM_OFFSET + sizeof(firmware));
if (!mem) {
- printk(KERN_ERR "wanxl: ioremap() failed\n");
+ pr_err("ioremap() failed\n");
wanxl_pci_remove_one(pdev);
return -EFAULT;
}
@@ -733,8 +729,7 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
writel(0, card->plx + PLX_MAILBOX_5);
if (wanxl_puts_command(card, MBX1_CMD_ABORTJ)) {
- printk(KERN_WARNING "wanXL %s: unable to Abort and Jump\n",
- pci_name(pdev));
+ pr_warn("%s: unable to Abort and Jump\n", pci_name(pdev));
wanxl_pci_remove_one(pdev);
return -ENODEV;
}
@@ -748,8 +743,8 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
}while (time_after(timeout, jiffies));
if (!stat) {
- printk(KERN_WARNING "wanXL %s: timeout while initializing card "
- "firmware\n", pci_name(pdev));
+ pr_warn("%s: timeout while initializing card firmware\n",
+ pci_name(pdev));
wanxl_pci_remove_one(pdev);
return -ENODEV;
}
@@ -758,13 +753,13 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
ramsize = stat;
#endif
- printk(KERN_INFO "wanXL %s: at 0x%X, %u KB of RAM at 0x%X, irq %u\n",
- pci_name(pdev), plx_phy, ramsize / 1024, mem_phy, pdev->irq);
+ pr_info("%s: at 0x%X, %u KB of RAM at 0x%X, irq %u\n",
+ pci_name(pdev), plx_phy, ramsize / 1024, mem_phy, pdev->irq);
/* Allocate IRQ */
if (request_irq(pdev->irq, wanxl_intr, IRQF_SHARED, "wanXL", card)) {
- printk(KERN_WARNING "wanXL %s: could not allocate IRQ%i.\n",
- pci_name(pdev), pdev->irq);
+ pr_warn("%s: could not allocate IRQ%i\n",
+ pci_name(pdev), pdev->irq);
wanxl_pci_remove_one(pdev);
return -EBUSY;
}
@@ -775,8 +770,8 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
port_t *port = &card->ports[i];
struct net_device *dev = alloc_hdlcdev(port);
if (!dev) {
- printk(KERN_ERR "wanXL %s: unable to allocate"
- " memory\n", pci_name(pdev));
+ pr_err("%s: unable to allocate memory\n",
+ pci_name(pdev));
wanxl_pci_remove_one(pdev);
return -ENOMEM;
}
@@ -792,8 +787,8 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
port->node = i;
get_status(port)->clocking = CLOCK_EXT;
if (register_hdlc_device(dev)) {
- printk(KERN_ERR "wanXL %s: unable to register hdlc"
- " device\n", pci_name(pdev));
+ pr_err("%s: unable to register hdlc device\n",
+ pci_name(pdev));
free_netdev(dev);
wanxl_pci_remove_one(pdev);
return -ENOBUFS;
@@ -801,11 +796,11 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
card->n_ports++;
}
- printk(KERN_INFO "wanXL %s: port", pci_name(pdev));
+ pr_info("%s: port", pci_name(pdev));
for (i = 0; i < ports; i++)
- printk("%s #%i: %s", i ? "," : "", i,
- card->ports[i].dev->name);
- printk("\n");
+ pr_cont("%s #%i: %s",
+ i ? "," : "", i, card->ports[i].dev->name);
+ pr_cont("\n");
for (i = 0; i < ports; i++)
wanxl_cable_intr(&card->ports[i]); /* get carrier status etc.*/
@@ -835,7 +830,7 @@ static struct pci_driver wanxl_pci_driver = {
static int __init wanxl_init_module(void)
{
#ifdef MODULE
- printk(KERN_INFO "%s\n", version);
+ pr_info("%s\n", version);
#endif
return pci_register_driver(&wanxl_pci_driver);
}
diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c
index 24297b274cd4..8a10bb730d5a 100644
--- a/drivers/net/wan/x25_asy.c
+++ b/drivers/net/wan/x25_asy.c
@@ -14,6 +14,8 @@
* 2000-10-29 Henner Eisen lapb_data_indication() return status.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <asm/system.h>
@@ -96,7 +98,7 @@ static struct x25_asy *x25_asy_alloc(void)
x25_asy_devs[i] = dev;
return sl;
} else {
- printk(KERN_WARNING "x25_asy_alloc() - register_netdev() failure.\n");
+ pr_warn("%s(): register_netdev() failure\n", __func__);
free_netdev(dev);
}
}
@@ -114,8 +116,7 @@ static void x25_asy_free(struct x25_asy *sl)
sl->xbuff = NULL;
if (!test_and_clear_bit(SLF_INUSE, &sl->flags))
- printk(KERN_ERR "%s: x25_asy_free for already free unit.\n",
- sl->dev->name);
+ netdev_err(sl->dev, "x25_asy_free for already free unit\n");
}
static int x25_asy_change_mtu(struct net_device *dev, int newmtu)
@@ -128,8 +129,7 @@ static int x25_asy_change_mtu(struct net_device *dev, int newmtu)
rbuff = kmalloc(len + 4, GFP_ATOMIC);
if (xbuff == NULL || rbuff == NULL) {
- printk(KERN_WARNING "%s: unable to grow X.25 buffers, MTU change cancelled.\n",
- dev->name);
+ netdev_warn(dev, "unable to grow X.25 buffers, MTU change cancelled\n");
kfree(xbuff);
kfree(rbuff);
return -ENOMEM;
@@ -198,8 +198,7 @@ static void x25_asy_bump(struct x25_asy *sl)
skb = dev_alloc_skb(count+1);
if (skb == NULL) {
- printk(KERN_WARNING "%s: memory squeeze, dropping packet.\n",
- sl->dev->name);
+ netdev_warn(sl->dev, "memory squeeze, dropping packet\n");
dev->stats.rx_dropped++;
return;
}
@@ -287,9 +286,9 @@ static void x25_asy_timeout(struct net_device *dev)
/* May be we must check transmitter timeout here ?
* 14 Oct 1994 Dmitry Gorodchanin.
*/
- printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
- (tty_chars_in_buffer(sl->tty) || sl->xleft) ?
- "bad line quality" : "driver error");
+ netdev_warn(dev, "transmit timed out, %s?\n",
+ (tty_chars_in_buffer(sl->tty) || sl->xleft) ?
+ "bad line quality" : "driver error");
sl->xleft = 0;
clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
x25_asy_unlock(sl);
@@ -306,8 +305,7 @@ static netdev_tx_t x25_asy_xmit(struct sk_buff *skb,
int err;
if (!netif_running(sl->dev)) {
- printk(KERN_ERR "%s: xmit call when iface is down\n",
- dev->name);
+ netdev_err(dev, "xmit call when iface is down\n");
kfree_skb(skb);
return NETDEV_TX_OK;
}
@@ -318,13 +316,15 @@ static netdev_tx_t x25_asy_xmit(struct sk_buff *skb,
case X25_IFACE_CONNECT: /* Connection request .. do nothing */
err = lapb_connect_request(dev);
if (err != LAPB_OK)
- printk(KERN_ERR "x25_asy: lapb_connect_request error - %d\n", err);
+ netdev_err(dev, "lapb_connect_request error: %d\n",
+ err);
kfree_skb(skb);
return NETDEV_TX_OK;
case X25_IFACE_DISCONNECT: /* do nothing - hang up ?? */
err = lapb_disconnect_request(dev);
if (err != LAPB_OK)
- printk(KERN_ERR "x25_asy: lapb_disconnect_request error - %d\n", err);
+ netdev_err(dev, "lapb_disconnect_request error: %d\n",
+ err);
default:
kfree_skb(skb);
return NETDEV_TX_OK;
@@ -343,7 +343,7 @@ static netdev_tx_t x25_asy_xmit(struct sk_buff *skb,
err = lapb_data_request(dev, skb);
if (err != LAPB_OK) {
- printk(KERN_ERR "x25_asy: lapb_data_request error - %d\n", err);
+ netdev_err(dev, "lapb_data_request error: %d\n", err);
kfree_skb(skb);
return NETDEV_TX_OK;
}
@@ -378,7 +378,7 @@ static void x25_asy_data_transmit(struct net_device *dev, struct sk_buff *skb)
spin_lock(&sl->lock);
if (netif_queue_stopped(sl->dev) || sl->tty == NULL) {
spin_unlock(&sl->lock);
- printk(KERN_ERR "x25_asy: tbusy drop\n");
+ netdev_err(dev, "tbusy drop\n");
kfree_skb(skb);
return;
}
@@ -404,7 +404,7 @@ static void x25_asy_connected(struct net_device *dev, int reason)
skb = dev_alloc_skb(1);
if (skb == NULL) {
- printk(KERN_ERR "x25_asy: out of memory\n");
+ netdev_err(dev, "out of memory\n");
return;
}
@@ -423,7 +423,7 @@ static void x25_asy_disconnected(struct net_device *dev, int reason)
skb = dev_alloc_skb(1);
if (skb == NULL) {
- printk(KERN_ERR "x25_asy: out of memory\n");
+ netdev_err(dev, "out of memory\n");
return;
}
@@ -434,14 +434,13 @@ static void x25_asy_disconnected(struct net_device *dev, int reason)
netif_rx(skb);
}
-static struct lapb_register_struct x25_asy_callbacks = {
+static const struct lapb_register_struct x25_asy_callbacks = {
.connect_confirmation = x25_asy_connected,
.connect_indication = x25_asy_connected,
.disconnect_confirmation = x25_asy_disconnected,
.disconnect_indication = x25_asy_disconnected,
.data_indication = x25_asy_data_indication,
.data_transmit = x25_asy_data_transmit,
-
};
@@ -603,8 +602,8 @@ static void x25_asy_close_tty(struct tty_struct *tty)
err = lapb_unregister(sl->dev);
if (err != LAPB_OK)
- printk(KERN_ERR "x25_asy_close: lapb_unregister error -%d\n",
- err);
+ pr_err("x25_asy_close: lapb_unregister error: %d\n",
+ err);
tty->disc_data = NULL;
sl->tty = NULL;
@@ -782,14 +781,13 @@ static int __init init_x25_asy(void)
if (x25_asy_maxdev < 4)
x25_asy_maxdev = 4; /* Sanity */
- printk(KERN_INFO "X.25 async: version 0.00 ALPHA "
- "(dynamic channels, max=%d).\n", x25_asy_maxdev);
+ pr_info("X.25 async: version 0.00 ALPHA (dynamic channels, max=%d)\n",
+ x25_asy_maxdev);
x25_asy_devs = kcalloc(x25_asy_maxdev, sizeof(struct net_device *),
GFP_KERNEL);
if (!x25_asy_devs) {
- printk(KERN_WARNING "X25 async: Can't allocate x25_asy_ctrls[] "
- "array! Uaargh! (-> No X.25 available)\n");
+ pr_warn("Can't allocate x25_asy_ctrls[] array! Uaargh! (-> No X.25 available)\n");
return -ENOMEM;
}
diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c
index 0806232e0f8f..0e5769061702 100644
--- a/drivers/net/wan/z85230.c
+++ b/drivers/net/wan/z85230.c
@@ -36,6 +36,8 @@
* Synchronous mode without DMA is unlikely to pass about 2400 baud.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
@@ -365,7 +367,7 @@ static void z8530_rx(struct z8530_channel *c)
c->count=0;
if(stat&Rx_OVR)
{
- printk(KERN_WARNING "%s: overrun\n", c->dev->name);
+ pr_warn("%s: overrun\n", c->dev->name);
c->rx_overrun++;
}
if(stat&CRC_ERR)
@@ -464,12 +466,12 @@ static void z8530_status(struct z8530_channel *chan)
if (altered & chan->dcdcheck)
{
if (status & chan->dcdcheck) {
- printk(KERN_INFO "%s: DCD raised\n", chan->dev->name);
+ pr_info("%s: DCD raised\n", chan->dev->name);
write_zsreg(chan, R3, chan->regs[3] | RxENABLE);
if (chan->netdevice)
netif_carrier_on(chan->netdevice);
} else {
- printk(KERN_INFO "%s: DCD lost\n", chan->dev->name);
+ pr_info("%s: DCD lost\n", chan->dev->name);
write_zsreg(chan, R3, chan->regs[3] & ~RxENABLE);
z8530_flush_fifo(chan);
if (chan->netdevice)
@@ -538,12 +540,12 @@ static void z8530_dma_tx(struct z8530_channel *chan)
{
if(!chan->dma_tx)
{
- printk(KERN_WARNING "Hey who turned the DMA off?\n");
+ pr_warn("Hey who turned the DMA off?\n");
z8530_tx(chan);
return;
}
/* This shouldn't occur in DMA mode */
- printk(KERN_ERR "DMA tx - bogus event!\n");
+ pr_err("DMA tx - bogus event!\n");
z8530_tx(chan);
}
@@ -585,12 +587,12 @@ static void z8530_dma_status(struct z8530_channel *chan)
if (altered & chan->dcdcheck)
{
if (status & chan->dcdcheck) {
- printk(KERN_INFO "%s: DCD raised\n", chan->dev->name);
+ pr_info("%s: DCD raised\n", chan->dev->name);
write_zsreg(chan, R3, chan->regs[3] | RxENABLE);
if (chan->netdevice)
netif_carrier_on(chan->netdevice);
} else {
- printk(KERN_INFO "%s:DCD lost\n", chan->dev->name);
+ pr_info("%s: DCD lost\n", chan->dev->name);
write_zsreg(chan, R3, chan->regs[3] & ~RxENABLE);
z8530_flush_fifo(chan);
if (chan->netdevice)
@@ -712,7 +714,7 @@ irqreturn_t z8530_interrupt(int irq, void *dev_id)
if(locker)
{
- printk(KERN_ERR "IRQ re-enter\n");
+ pr_err("IRQ re-enter\n");
return IRQ_NONE;
}
locker=1;
@@ -758,7 +760,8 @@ irqreturn_t z8530_interrupt(int irq, void *dev_id)
}
spin_unlock(&dev->lock);
if(work==5000)
- printk(KERN_ERR "%s: interrupt jammed - abort(0x%X)!\n", dev->name, intr);
+ pr_err("%s: interrupt jammed - abort(0x%X)!\n",
+ dev->name, intr);
/* Ok all done */
locker=0;
return IRQ_HANDLED;
@@ -1225,7 +1228,7 @@ static const char *z8530_type_name[]={
void z8530_describe(struct z8530_dev *dev, char *mapping, unsigned long io)
{
- printk(KERN_INFO "%s: %s found at %s 0x%lX, IRQ %d.\n",
+ pr_info("%s: %s found at %s 0x%lX, IRQ %d\n",
dev->name,
z8530_type_name[dev->type],
mapping,
@@ -1621,8 +1624,7 @@ static void z8530_rx_done(struct z8530_channel *c)
else
/* Can't occur as we dont reenable the DMA irq until
after the flip is done */
- printk(KERN_WARNING "%s: DMA flip overrun!\n",
- c->netdevice->name);
+ netdev_warn(c->netdevice, "DMA flip overrun!\n");
release_dma_lock(flags);
@@ -1637,8 +1639,7 @@ static void z8530_rx_done(struct z8530_channel *c)
skb = dev_alloc_skb(ct);
if (skb == NULL) {
c->netdevice->stats.rx_dropped++;
- printk(KERN_WARNING "%s: Memory squeeze.\n",
- c->netdevice->name);
+ netdev_warn(c->netdevice, "Memory squeeze\n");
} else {
skb_put(skb, ct);
skb_copy_to_linear_data(skb, rxb, ct);
@@ -1678,8 +1679,7 @@ static void z8530_rx_done(struct z8530_channel *c)
c->skb2 = dev_alloc_skb(c->mtu);
if (c->skb2 == NULL)
- printk(KERN_WARNING "%s: memory squeeze.\n",
- c->netdevice->name);
+ netdev_warn(c->netdevice, "memory squeeze\n");
else
skb_put(c->skb2, c->mtu);
c->netdevice->stats.rx_packets++;
@@ -1693,7 +1693,7 @@ static void z8530_rx_done(struct z8530_channel *c)
c->rx_function(c, skb);
} else {
c->netdevice->stats.rx_dropped++;
- printk(KERN_ERR "%s: Lost a frame\n", c->netdevice->name);
+ netdev_err(c->netdevice, "Lost a frame\n");
}
}
diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c
index 727d728649b7..2fea02b35b2d 100644
--- a/drivers/net/wimax/i2400m/control.c
+++ b/drivers/net/wimax/i2400m/control.c
@@ -78,6 +78,8 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/wimax/i2400m.h>
+#include <linux/export.h>
+#include <linux/moduleparam.h>
#define D_SUBMODULE control
diff --git a/drivers/net/wimax/i2400m/debugfs.c b/drivers/net/wimax/i2400m/debugfs.c
index 9c70b5fa3f51..129ba36bd04d 100644
--- a/drivers/net/wimax/i2400m/debugfs.c
+++ b/drivers/net/wimax/i2400m/debugfs.c
@@ -26,6 +26,7 @@
#include <linux/etherdevice.h>
#include <linux/spinlock.h>
#include <linux/device.h>
+#include <linux/export.h>
#include "i2400m.h"
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 85dadd5bf4be..7cbd7d231e11 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -158,6 +158,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/usb.h>
+#include <linux/export.h>
#include "i2400m.h"
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 5eacc653a94d..c421a6141854 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -155,7 +155,7 @@
#include <linux/netdevice.h>
#include <linux/completion.h>
#include <linux/rwsem.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <net/wimax.h>
#include <linux/wimax/i2400m.h>
#include <asm/byteorder.h>
diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c
index 2edd8fe1c1f3..64a110604ad3 100644
--- a/drivers/net/wimax/i2400m/netdev.c
+++ b/drivers/net/wimax/i2400m/netdev.c
@@ -76,6 +76,7 @@
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
+#include <linux/export.h>
#include "i2400m.h"
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c
index 2f94a872101f..37becfcc98f2 100644
--- a/drivers/net/wimax/i2400m/rx.c
+++ b/drivers/net/wimax/i2400m/rx.c
@@ -149,6 +149,8 @@
#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include <linux/workqueue.h>
+#include <linux/export.h>
+#include <linux/moduleparam.h>
#include "i2400m.h"
diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c
index be428cae28d8..21a9edd6e75d 100644
--- a/drivers/net/wimax/i2400m/sdio.c
+++ b/drivers/net/wimax/i2400m/sdio.c
@@ -55,6 +55,7 @@
#include <linux/mmc/sdio_func.h>
#include "i2400m-sdio.h"
#include <linux/wimax/i2400m.h>
+#include <linux/module.h>
#define D_SUBMODULE main
#include "sdio-debug-levels.h"
diff --git a/drivers/net/wimax/i2400m/tx.c b/drivers/net/wimax/i2400m/tx.c
index 4b30ed11d785..4b9ecb20deec 100644
--- a/drivers/net/wimax/i2400m/tx.c
+++ b/drivers/net/wimax/i2400m/tx.c
@@ -245,6 +245,7 @@
*/
#include <linux/netdevice.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "i2400m.h"
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index 298f2b0b6311..2c1b8b687646 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -67,6 +67,7 @@
#include <linux/wimax/i2400m.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
+#include <linux/module.h>
#define D_SUBMODULE usb
@@ -599,7 +600,7 @@ void i2400mu_disconnect(struct usb_interface *iface)
*
* As well, the device might refuse going to sleep for whichever
* reason. In this case we just fail. For system suspend/hibernate,
- * we *can't* fail. We check PM_EVENT_AUTO to see if the
+ * we *can't* fail. We check PMSG_IS_AUTO to see if the
* suspend call comes from the USB stack or from the system and act
* in consequence.
*
@@ -615,7 +616,7 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg)
struct i2400m *i2400m = &i2400mu->i2400m;
#ifdef CONFIG_PM
- if (pm_msg.event & PM_EVENT_AUTO)
+ if (PMSG_IS_AUTO(pm_msg))
is_autosuspend = 1;
#endif
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index f354bd4e121e..abd3b71cd4ab 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -271,6 +271,7 @@ config MWL8K
source "drivers/net/wireless/ath/Kconfig"
source "drivers/net/wireless/b43/Kconfig"
source "drivers/net/wireless/b43legacy/Kconfig"
+source "drivers/net/wireless/brcm80211/Kconfig"
source "drivers/net/wireless/hostap/Kconfig"
source "drivers/net/wireless/ipw2x00/Kconfig"
source "drivers/net/wireless/iwlwifi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 7bba6a82b875..0a304b060b6c 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -41,7 +41,7 @@ obj-$(CONFIG_ADM8211) += adm8211.o
obj-$(CONFIG_MWL8K) += mwl8k.o
-obj-$(CONFIG_IWLAGN) += iwlwifi/
+obj-$(CONFIG_IWLWIFI) += iwlwifi/
obj-$(CONFIG_IWLWIFI_LEGACY) += iwlegacy/
obj-$(CONFIG_RT2X00) += rt2x00/
@@ -58,3 +58,6 @@ obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx/
obj-$(CONFIG_IWM) += iwmc3200wifi/
obj-$(CONFIG_MWIFIEX) += mwifiex/
+obj-$(CONFIG_BRCMFMAC) += brcm80211/
+obj-$(CONFIG_BRCMUMAC) += brcm80211/
+obj-$(CONFIG_BRCMSMAC) += brcm80211/
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index afe2cbc6cb24..f5ce5623da99 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -16,6 +16,7 @@
*/
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/if.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
@@ -24,6 +25,7 @@
#include <linux/delay.h>
#include <linux/crc32.h>
#include <linux/eeprom_93cx6.h>
+#include <linux/module.h>
#include <net/mac80211.h>
#include "adm8211.h"
@@ -1248,7 +1250,8 @@ static int adm8211_hw_reset(struct ieee80211_hw *dev)
return 0;
}
-static u64 adm8211_get_tsft(struct ieee80211_hw *dev)
+static u64 adm8211_get_tsft(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
{
struct adm8211_priv *priv = dev->priv;
u32 tsftl;
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 55cf71fbffe3..ac1176a4f465 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -2754,7 +2754,7 @@ static const struct net_device_ops airo_netdev_ops = {
.ndo_stop = airo_close,
.ndo_start_xmit = airo_start_xmit,
.ndo_get_stats = airo_get_stats,
- .ndo_set_multicast_list = airo_set_multicast_list,
+ .ndo_set_rx_mode = airo_set_multicast_list,
.ndo_set_mac_address = airo_set_mac_address,
.ndo_do_ioctl = airo_ioctl,
.ndo_change_mtu = airo_change_mtu,
@@ -2766,7 +2766,7 @@ static const struct net_device_ops mpi_netdev_ops = {
.ndo_stop = airo_close,
.ndo_start_xmit = mpi_start_xmit,
.ndo_get_stats = airo_get_stats,
- .ndo_set_multicast_list = airo_set_multicast_list,
+ .ndo_set_rx_mode = airo_set_multicast_list,
.ndo_set_mac_address = airo_set_mac_address,
.ndo_do_ioctl = airo_ioctl,
.ndo_change_mtu = airo_change_mtu,
@@ -2823,6 +2823,7 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
dev->wireless_data = &ai->wireless_data;
dev->irq = irq;
dev->base_addr = port;
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
SET_NETDEV_DEV(dev, dmdev);
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index 298601436ee2..4045e5ab0555 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -500,10 +500,9 @@ exit:
#define HEX2STR_BUFFERS 4
#define HEX2STR_MAX_LEN 64
-#define BIN2HEX(x) ((x) < 10 ? '0' + (x) : (x) + 'A' - 10)
/* Convert binary data into hex string */
-static char *hex2str(void *buf, int len)
+static char *hex2str(void *buf, size_t len)
{
static atomic_t a = ATOMIC_INIT(0);
static char bufs[HEX2STR_BUFFERS][3 * HEX2STR_MAX_LEN + 1];
@@ -514,18 +513,17 @@ static char *hex2str(void *buf, int len)
if (len > HEX2STR_MAX_LEN)
len = HEX2STR_MAX_LEN;
- if (len <= 0) {
- ret[0] = '\0';
- return ret;
- }
+ if (len == 0)
+ goto exit;
while (len--) {
- *obuf++ = BIN2HEX(*ibuf >> 4);
- *obuf++ = BIN2HEX(*ibuf & 0xf);
+ obuf = hex_byte_pack(obuf, *ibuf++);
*obuf++ = '-';
- ibuf++;
}
- *(--obuf) = '\0';
+ obuf--;
+
+exit:
+ *obuf = '\0';
return ret;
}
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
index d1b23067619f..09602241901b 100644
--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -1,6 +1,6 @@
menuconfig ATH_COMMON
tristate "Atheros Wireless Cards"
- depends on CFG80211
+ depends on CFG80211 && (!UML || BROKEN)
---help---
This will enable the support for the Atheros wireless drivers.
ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option
@@ -25,5 +25,6 @@ config ATH_DEBUG
source "drivers/net/wireless/ath/ath5k/Kconfig"
source "drivers/net/wireless/ath/ath9k/Kconfig"
source "drivers/net/wireless/ath/carl9170/Kconfig"
+source "drivers/net/wireless/ath/ath6kl/Kconfig"
endif
diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile
index 0e8f528c81c0..d1214696a35b 100644
--- a/drivers/net/wireless/ath/Makefile
+++ b/drivers/net/wireless/ath/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_ATH5K) += ath5k/
obj-$(CONFIG_ATH9K_HW) += ath9k/
obj-$(CONFIG_CARL9170) += carl9170/
+obj-$(CONFIG_ATH6KL) += ath6kl/
obj-$(CONFIG_ATH_COMMON) += ath.o
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index 7cf4317a2a84..0f9ee46cfc97 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -71,9 +71,7 @@ struct ath_regulatory {
char alpha2[2];
u16 country_code;
u16 max_power_level;
- u32 tp_scale;
u16 current_rd;
- u16 current_rd_ext;
int16_t power_limit;
struct reg_dmn_pair_mapping *regpair;
};
@@ -140,9 +138,6 @@ struct ath_common {
u8 curbssid[ETH_ALEN];
u8 bssidmask[ETH_ALEN];
- u8 tx_chainmask;
- u8 rx_chainmask;
-
u32 rx_bufsize;
u32 keymax;
@@ -161,6 +156,7 @@ struct ath_common {
const struct ath_bus_ops *bus_ops;
bool btcoex_enabled;
+ bool disable_ani;
};
struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
@@ -177,23 +173,28 @@ bool ath_hw_keyreset(struct ath_common *common, u16 entry);
void ath_hw_cycle_counters_update(struct ath_common *common);
int32_t ath_hw_get_listen_time(struct ath_common *common);
-extern __attribute__ ((format (printf, 3, 4))) int
-ath_printk(const char *level, struct ath_common *common, const char *fmt, ...);
+extern __printf(2, 3) void ath_printk(const char *level, const char *fmt, ...);
+
+#define _ath_printk(level, common, fmt, ...) \
+do { \
+ __always_unused struct ath_common *unused = common; \
+ ath_printk(level, fmt, ##__VA_ARGS__); \
+} while (0)
#define ath_emerg(common, fmt, ...) \
- ath_printk(KERN_EMERG, common, fmt, ##__VA_ARGS__)
+ _ath_printk(KERN_EMERG, common, fmt, ##__VA_ARGS__)
#define ath_alert(common, fmt, ...) \
- ath_printk(KERN_ALERT, common, fmt, ##__VA_ARGS__)
+ _ath_printk(KERN_ALERT, common, fmt, ##__VA_ARGS__)
#define ath_crit(common, fmt, ...) \
- ath_printk(KERN_CRIT, common, fmt, ##__VA_ARGS__)
+ _ath_printk(KERN_CRIT, common, fmt, ##__VA_ARGS__)
#define ath_err(common, fmt, ...) \
- ath_printk(KERN_ERR, common, fmt, ##__VA_ARGS__)
+ _ath_printk(KERN_ERR, common, fmt, ##__VA_ARGS__)
#define ath_warn(common, fmt, ...) \
- ath_printk(KERN_WARNING, common, fmt, ##__VA_ARGS__)
+ _ath_printk(KERN_WARNING, common, fmt, ##__VA_ARGS__)
#define ath_notice(common, fmt, ...) \
- ath_printk(KERN_NOTICE, common, fmt, ##__VA_ARGS__)
+ _ath_printk(KERN_NOTICE, common, fmt, ##__VA_ARGS__)
#define ath_info(common, fmt, ...) \
- ath_printk(KERN_INFO, common, fmt, ##__VA_ARGS__)
+ _ath_printk(KERN_INFO, common, fmt, ##__VA_ARGS__)
/**
* enum ath_debug_level - atheros wireless debug level
@@ -245,27 +246,21 @@ enum ATH_DEBUG {
#ifdef CONFIG_ATH_DEBUG
-#define ath_dbg(common, dbg_mask, fmt, ...) \
-({ \
- int rtn; \
- if ((common)->debug_mask & dbg_mask) \
- rtn = ath_printk(KERN_DEBUG, common, fmt, \
- ##__VA_ARGS__); \
- else \
- rtn = 0; \
- \
- rtn; \
-})
+#define ath_dbg(common, dbg_mask, fmt, ...) \
+do { \
+ if ((common)->debug_mask & dbg_mask) \
+ _ath_printk(KERN_DEBUG, common, fmt, ##__VA_ARGS__); \
+} while (0)
+
#define ATH_DBG_WARN(foo, arg...) WARN(foo, arg)
#define ATH_DBG_WARN_ON_ONCE(foo) WARN_ON_ONCE(foo)
#else
-static inline __attribute__ ((format (printf, 3, 4))) int
-ath_dbg(struct ath_common *common, enum ATH_DEBUG dbg_mask,
- const char *fmt, ...)
+static inline __attribute__ ((format (printf, 3, 4)))
+void ath_dbg(struct ath_common *common, enum ATH_DEBUG dbg_mask,
+ const char *fmt, ...)
{
- return 0;
}
#define ATH_DBG_WARN(foo, arg...) do {} while (0)
#define ATH_DBG_WARN_ON_ONCE(foo) ({ \
diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c
index ea9982781559..e5be7e701816 100644
--- a/drivers/net/wireless/ath/ath5k/ahb.c
+++ b/drivers/net/wireless/ath/ath5k/ahb.c
@@ -24,7 +24,6 @@
#include "debug.h"
#include "base.h"
#include "reg.h"
-#include "debug.h"
/* return bus cachesize in 4B word units */
static void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz)
@@ -35,8 +34,8 @@ static void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz)
static bool
ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
{
- struct ath5k_softc *sc = common->priv;
- struct platform_device *pdev = to_platform_device(sc->dev);
+ struct ath5k_hw *ah = common->priv;
+ struct platform_device *pdev = to_platform_device(ah->dev);
struct ar231x_board_config *bcfg = pdev->dev.platform_data;
u16 *eeprom, *eeprom_end;
@@ -56,8 +55,7 @@ ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
int ath5k_hw_read_srev(struct ath5k_hw *ah)
{
- struct ath5k_softc *sc = ah->ah_sc;
- struct platform_device *pdev = to_platform_device(sc->dev);
+ struct platform_device *pdev = to_platform_device(ah->dev);
struct ar231x_board_config *bcfg = pdev->dev.platform_data;
ah->ah_mac_srev = bcfg->devid;
return 0;
@@ -65,12 +63,11 @@ int ath5k_hw_read_srev(struct ath5k_hw *ah)
static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
{
- struct ath5k_softc *sc = ah->ah_sc;
- struct platform_device *pdev = to_platform_device(sc->dev);
+ struct platform_device *pdev = to_platform_device(ah->dev);
struct ar231x_board_config *bcfg = pdev->dev.platform_data;
u8 *cfg_mac;
- if (to_platform_device(sc->dev)->id == 0)
+ if (to_platform_device(ah->dev)->id == 0)
cfg_mac = bcfg->config->wlan0_mac;
else
cfg_mac = bcfg->config->wlan1_mac;
@@ -90,7 +87,7 @@ static const struct ath_bus_ops ath_ahb_bus_ops = {
static int ath_ahb_probe(struct platform_device *pdev)
{
struct ar231x_board_config *bcfg = pdev->dev.platform_data;
- struct ath5k_softc *sc;
+ struct ath5k_hw *ah;
struct ieee80211_hw *hw;
struct resource *res;
void __iomem *mem;
@@ -127,19 +124,19 @@ static int ath_ahb_probe(struct platform_device *pdev)
irq = res->start;
- hw = ieee80211_alloc_hw(sizeof(struct ath5k_softc), &ath5k_hw_ops);
+ hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops);
if (hw == NULL) {
dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
ret = -ENOMEM;
goto err_out;
}
- sc = hw->priv;
- sc->hw = hw;
- sc->dev = &pdev->dev;
- sc->iobase = mem;
- sc->irq = irq;
- sc->devid = bcfg->devid;
+ ah = hw->priv;
+ ah->hw = hw;
+ ah->dev = &pdev->dev;
+ ah->iobase = mem;
+ ah->irq = irq;
+ ah->devid = bcfg->devid;
if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
/* Enable WMAC AHB arbitration */
@@ -155,7 +152,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
/* Enable WMAC DMA access (assuming 5312 or 231x*/
/* TODO: check other platforms */
reg = __raw_readl((void __iomem *) AR5K_AR5312_ENABLE);
- if (to_platform_device(sc->dev)->id == 0)
+ if (to_platform_device(ah->dev)->id == 0)
reg |= AR5K_AR5312_ENABLE_WLAN0;
else
reg |= AR5K_AR5312_ENABLE_WLAN1;
@@ -166,13 +163,13 @@ static int ath_ahb_probe(struct platform_device *pdev)
* used as pass-through. Disable 2 GHz support in the
* driver for it
*/
- if (to_platform_device(sc->dev)->id == 0 &&
- (bcfg->config->flags & (BD_WLAN0|BD_WLAN1)) ==
- (BD_WLAN1|BD_WLAN0))
- __set_bit(ATH_STAT_2G_DISABLED, sc->status);
+ if (to_platform_device(ah->dev)->id == 0 &&
+ (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) ==
+ (BD_WLAN1 | BD_WLAN0))
+ __set_bit(ATH_STAT_2G_DISABLED, ah->status);
}
- ret = ath5k_init_softc(sc, &ath_ahb_bus_ops);
+ ret = ath5k_init_ah(ah, &ath_ahb_bus_ops);
if (ret != 0) {
dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret);
ret = -ENODEV;
@@ -194,13 +191,13 @@ static int ath_ahb_remove(struct platform_device *pdev)
{
struct ar231x_board_config *bcfg = pdev->dev.platform_data;
struct ieee80211_hw *hw = platform_get_drvdata(pdev);
- struct ath5k_softc *sc;
+ struct ath5k_hw *ah;
u32 reg;
if (!hw)
return 0;
- sc = hw->priv;
+ ah = hw->priv;
if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
/* Disable WMAC AHB arbitration */
@@ -210,15 +207,16 @@ static int ath_ahb_remove(struct platform_device *pdev)
} else {
/*Stop DMA access */
reg = __raw_readl((void __iomem *) AR5K_AR5312_ENABLE);
- if (to_platform_device(sc->dev)->id == 0)
+ if (to_platform_device(ah->dev)->id == 0)
reg &= ~AR5K_AR5312_ENABLE_WLAN0;
else
reg &= ~AR5K_AR5312_ENABLE_WLAN1;
__raw_writel(reg, (void __iomem *) AR5K_AR5312_ENABLE);
}
- ath5k_deinit_softc(sc);
+ ath5k_deinit_ah(ah);
platform_set_drvdata(pdev, NULL);
+ ieee80211_free_hw(hw);
return 0;
}
diff --git a/drivers/net/wireless/ath/ath5k/ani.c b/drivers/net/wireless/ath/ath5k/ani.c
index f915f404302d..bea90e6be70e 100644
--- a/drivers/net/wireless/ath/ath5k/ani.c
+++ b/drivers/net/wireless/ath/ath5k/ani.c
@@ -15,7 +15,6 @@
*/
#include "ath5k.h"
-#include "base.h"
#include "reg.h"
#include "debug.h"
#include "ani.h"
@@ -74,7 +73,7 @@ ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level)
static const s8 fr[] = { -78, -80 };
#endif
if (level < 0 || level >= ARRAY_SIZE(sz)) {
- ATH5K_ERR(ah->ah_sc, "noise immuniy level %d out of range",
+ ATH5K_ERR(ah, "noise immunity level %d out of range",
level);
return;
}
@@ -88,8 +87,8 @@ ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level)
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG,
AR5K_PHY_SIG_FIRPWR, fr[level]);
- ah->ah_sc->ani_state.noise_imm_level = level;
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI, "new level %d", level);
+ ah->ani_state.noise_imm_level = level;
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
}
@@ -105,8 +104,8 @@ ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level)
static const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
if (level < 0 || level >= ARRAY_SIZE(val) ||
- level > ah->ah_sc->ani_state.max_spur_level) {
- ATH5K_ERR(ah->ah_sc, "spur immunity level %d out of range",
+ level > ah->ani_state.max_spur_level) {
+ ATH5K_ERR(ah, "spur immunity level %d out of range",
level);
return;
}
@@ -114,8 +113,8 @@ ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level)
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, val[level]);
- ah->ah_sc->ani_state.spur_level = level;
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI, "new level %d", level);
+ ah->ani_state.spur_level = level;
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
}
@@ -130,15 +129,15 @@ ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level)
static const int val[] = { 0, 4, 8 };
if (level < 0 || level >= ARRAY_SIZE(val)) {
- ATH5K_ERR(ah->ah_sc, "firstep level %d out of range", level);
+ ATH5K_ERR(ah, "firstep level %d out of range", level);
return;
}
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG,
AR5K_PHY_SIG_FIRSTEP, val[level]);
- ah->ah_sc->ani_state.firstep_level = level;
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI, "new level %d", level);
+ ah->ani_state.firstep_level = level;
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
}
@@ -178,8 +177,8 @@ ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on)
AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN);
- ah->ah_sc->ani_state.ofdm_weak_sig = on;
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI, "turned %s",
+ ah->ani_state.ofdm_weak_sig = on;
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
on ? "on" : "off");
}
@@ -195,8 +194,8 @@ ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on)
static const int val[] = { 8, 6 };
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_CCK_CROSSCORR,
AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR, val[on]);
- ah->ah_sc->ani_state.cck_weak_sig = on;
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI, "turned %s",
+ ah->ani_state.cck_weak_sig = on;
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
on ? "on" : "off");
}
@@ -218,7 +217,7 @@ ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as,
{
int rssi = ewma_read(&ah->ah_beacon_rssi_avg);
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI, "raise immunity (%s)",
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "raise immunity (%s)",
ofdm_trigger ? "ODFM" : "CCK");
/* first: raise noise immunity */
@@ -229,13 +228,13 @@ ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as,
/* only OFDM: raise spur immunity level */
if (ofdm_trigger &&
- as->spur_level < ah->ah_sc->ani_state.max_spur_level) {
+ as->spur_level < ah->ani_state.max_spur_level) {
ath5k_ani_set_spur_immunity_level(ah, as->spur_level + 1);
return;
}
/* AP mode */
- if (ah->ah_sc->opmode == NL80211_IFTYPE_AP) {
+ if (ah->opmode == NL80211_IFTYPE_AP) {
if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL)
ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
return;
@@ -248,7 +247,7 @@ ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as,
* don't shut out a remote node by raising immunity too high. */
if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"beacon RSSI high");
/* only OFDM: beacon RSSI is high, we can disable ODFM weak
* signal detection */
@@ -265,7 +264,7 @@ ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as,
} else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
/* beacon RSSI in mid range, we need OFDM weak signal detect,
* but can raise firstep level */
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"beacon RSSI mid");
if (ofdm_trigger && as->ofdm_weak_sig == false)
ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
@@ -275,7 +274,7 @@ ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as,
} else if (ah->ah_current_channel->band == IEEE80211_BAND_2GHZ) {
/* beacon RSSI is low. in B/G mode turn of OFDM weak signal
* detect and zero firstep level to maximize CCK sensitivity */
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"beacon RSSI low, 2GHz");
if (ofdm_trigger && as->ofdm_weak_sig == true)
ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
@@ -303,9 +302,9 @@ ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as)
{
int rssi = ewma_read(&ah->ah_beacon_rssi_avg);
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI, "lower immunity");
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "lower immunity");
- if (ah->ah_sc->opmode == NL80211_IFTYPE_AP) {
+ if (ah->opmode == NL80211_IFTYPE_AP) {
/* AP mode */
if (as->firstep_level > 0) {
ath5k_ani_set_firstep_level(ah, as->firstep_level - 1);
@@ -464,7 +463,7 @@ ath5k_ani_period_restart(struct ath5k_hw *ah, struct ath5k_ani_state *as)
void
ath5k_ani_calibration(struct ath5k_hw *ah)
{
- struct ath5k_ani_state *as = &ah->ah_sc->ani_state;
+ struct ath5k_ani_state *as = &ah->ani_state;
int listen, ofdm_high, ofdm_low, cck_high, cck_low;
/* get listen time since last call and add it to the counter because we
@@ -483,9 +482,9 @@ ath5k_ani_calibration(struct ath5k_hw *ah)
ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000;
cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000;
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"listen %d (now %d)", as->listen_time, listen);
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"check high ofdm %d/%d cck %d/%d",
as->ofdm_errors, ofdm_high, as->cck_errors, cck_high);
@@ -498,7 +497,7 @@ ath5k_ani_calibration(struct ath5k_hw *ah)
} else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) {
/* If more than 5 (TODO: why 5?) periods have passed and we got
* relatively little errors we can try to lower immunity */
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"check low ofdm %d/%d cck %d/%d",
as->ofdm_errors, ofdm_low, as->cck_errors, cck_low);
@@ -525,7 +524,7 @@ ath5k_ani_calibration(struct ath5k_hw *ah)
void
ath5k_ani_mib_intr(struct ath5k_hw *ah)
{
- struct ath5k_ani_state *as = &ah->ah_sc->ani_state;
+ struct ath5k_ani_state *as = &ah->ani_state;
/* nothing to do here if HW does not have PHY error counters - they
* can't be the reason for the MIB interrupt then */
@@ -536,7 +535,7 @@ ath5k_ani_mib_intr(struct ath5k_hw *ah)
ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
- if (ah->ah_sc->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO)
+ if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO)
return;
/* If one of the errors triggered, we can get a superfluous second
@@ -547,7 +546,7 @@ ath5k_ani_mib_intr(struct ath5k_hw *ah)
if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH ||
as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH)
- tasklet_schedule(&ah->ah_sc->ani_tasklet);
+ tasklet_schedule(&ah->ani_tasklet);
}
@@ -561,16 +560,16 @@ void
ath5k_ani_phy_error_report(struct ath5k_hw *ah,
enum ath5k_phy_error_code phyerr)
{
- struct ath5k_ani_state *as = &ah->ah_sc->ani_state;
+ struct ath5k_ani_state *as = &ah->ani_state;
if (phyerr == AR5K_RX_PHY_ERROR_OFDM_TIMING) {
as->ofdm_errors++;
if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH)
- tasklet_schedule(&ah->ah_sc->ani_tasklet);
+ tasklet_schedule(&ah->ani_tasklet);
} else if (phyerr == AR5K_RX_PHY_ERROR_CCK_TIMING) {
as->cck_errors++;
if (as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH)
- tasklet_schedule(&ah->ah_sc->ani_tasklet);
+ tasklet_schedule(&ah->ani_tasklet);
}
}
@@ -630,20 +629,25 @@ ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode)
if (ah->ah_version < AR5K_AR5212)
return;
+ if (mode < ATH5K_ANI_MODE_OFF || mode > ATH5K_ANI_MODE_AUTO) {
+ ATH5K_ERR(ah, "ANI mode %d out of range", mode);
+ return;
+ }
+
/* clear old state information */
- memset(&ah->ah_sc->ani_state, 0, sizeof(ah->ah_sc->ani_state));
+ memset(&ah->ani_state, 0, sizeof(ah->ani_state));
/* older hardware has more spur levels than newer */
if (ah->ah_mac_srev < AR5K_SREV_AR2414)
- ah->ah_sc->ani_state.max_spur_level = 7;
+ ah->ani_state.max_spur_level = 7;
else
- ah->ah_sc->ani_state.max_spur_level = 2;
+ ah->ani_state.max_spur_level = 2;
/* initial values for our ani parameters */
if (mode == ATH5K_ANI_MODE_OFF) {
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI, "ANI off\n");
- } else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) {
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI off\n");
+ } else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) {
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"ANI manual low -> high sensitivity\n");
ath5k_ani_set_noise_immunity_level(ah, 0);
ath5k_ani_set_spur_immunity_level(ah, 0);
@@ -651,17 +655,17 @@ ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode)
ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
ath5k_ani_set_cck_weak_signal_detection(ah, true);
} else if (mode == ATH5K_ANI_MODE_MANUAL_HIGH) {
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"ANI manual high -> low sensitivity\n");
ath5k_ani_set_noise_immunity_level(ah,
ATH5K_ANI_MAX_NOISE_IMM_LVL);
ath5k_ani_set_spur_immunity_level(ah,
- ah->ah_sc->ani_state.max_spur_level);
+ ah->ani_state.max_spur_level);
ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL);
ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
ath5k_ani_set_cck_weak_signal_detection(ah, false);
} else if (mode == ATH5K_ANI_MODE_AUTO) {
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI, "ANI auto\n");
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI auto\n");
ath5k_ani_set_noise_immunity_level(ah, 0);
ath5k_ani_set_spur_immunity_level(ah, 0);
ath5k_ani_set_firstep_level(ah, 0);
@@ -687,7 +691,7 @@ ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode)
~AR5K_RX_FILTER_PHYERR);
}
- ah->ah_sc->ani_state.ani_mode = mode;
+ ah->ani_state.ani_mode = mode;
}
diff --git a/drivers/net/wireless/ath/ath5k/ani.h b/drivers/net/wireless/ath/ath5k/ani.h
index 034015397093..7358b6c83c6c 100644
--- a/drivers/net/wireless/ath/ath5k/ani.h
+++ b/drivers/net/wireless/ath/ath5k/ani.h
@@ -16,6 +16,10 @@
#ifndef ANI_H
#define ANI_H
+#include "../ath.h"
+
+enum ath5k_phy_error_code;
+
/* these thresholds are relative to the ATH5K_ANI_LISTEN_PERIOD */
#define ATH5K_ANI_LISTEN_PERIOD 100
#define ATH5K_ANI_OFDM_TRIG_HIGH 500
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index bb50700436fe..fecbcd9a4259 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -18,14 +18,16 @@
#ifndef _ATH5K_H
#define _ATH5K_H
-/* TODO: Clean up channel debuging -doesn't work anyway- and start
+/* TODO: Clean up channel debugging (doesn't work anyway) and start
* working on reg. control code using all available eeprom information
- * -rev. engineering needed- */
+ * (rev. engineering needed) */
#define CHAN_DEBUG 0
#include <linux/io.h>
+#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/average.h>
+#include <linux/leds.h>
#include <net/mac80211.h>
/* RX/TX descriptor hw structs
@@ -36,43 +38,46 @@
* TODO: Make a more generic struct (eg. add more stuff to ath5k_capabilities)
* and clean up common bits, then introduce set/get functions in eeprom.c */
#include "eeprom.h"
+#include "debug.h"
#include "../ath.h"
+#include "ani.h"
/* PCI IDs */
-#define PCI_DEVICE_ID_ATHEROS_AR5210 0x0007 /* AR5210 */
-#define PCI_DEVICE_ID_ATHEROS_AR5311 0x0011 /* AR5311 */
-#define PCI_DEVICE_ID_ATHEROS_AR5211 0x0012 /* AR5211 */
-#define PCI_DEVICE_ID_ATHEROS_AR5212 0x0013 /* AR5212 */
-#define PCI_DEVICE_ID_3COM_3CRDAG675 0x0013 /* 3CRDAG675 (Atheros AR5212) */
-#define PCI_DEVICE_ID_3COM_2_3CRPAG175 0x0013 /* 3CRPAG175 (Atheros AR5212) */
-#define PCI_DEVICE_ID_ATHEROS_AR5210_AP 0x0207 /* AR5210 (Early) */
+#define PCI_DEVICE_ID_ATHEROS_AR5210 0x0007 /* AR5210 */
+#define PCI_DEVICE_ID_ATHEROS_AR5311 0x0011 /* AR5311 */
+#define PCI_DEVICE_ID_ATHEROS_AR5211 0x0012 /* AR5211 */
+#define PCI_DEVICE_ID_ATHEROS_AR5212 0x0013 /* AR5212 */
+#define PCI_DEVICE_ID_3COM_3CRDAG675 0x0013 /* 3CRDAG675 (Atheros AR5212) */
+#define PCI_DEVICE_ID_3COM_2_3CRPAG175 0x0013 /* 3CRPAG175 (Atheros AR5212) */
+#define PCI_DEVICE_ID_ATHEROS_AR5210_AP 0x0207 /* AR5210 (Early) */
#define PCI_DEVICE_ID_ATHEROS_AR5212_IBM 0x1014 /* AR5212 (IBM MiniPCI) */
-#define PCI_DEVICE_ID_ATHEROS_AR5210_DEFAULT 0x1107 /* AR5210 (no eeprom) */
-#define PCI_DEVICE_ID_ATHEROS_AR5212_DEFAULT 0x1113 /* AR5212 (no eeprom) */
-#define PCI_DEVICE_ID_ATHEROS_AR5211_DEFAULT 0x1112 /* AR5211 (no eeprom) */
-#define PCI_DEVICE_ID_ATHEROS_AR5212_FPGA 0xf013 /* AR5212 (emulation board) */
-#define PCI_DEVICE_ID_ATHEROS_AR5211_LEGACY 0xff12 /* AR5211 (emulation board) */
-#define PCI_DEVICE_ID_ATHEROS_AR5211_FPGA11B 0xf11b /* AR5211 (emulation board) */
-#define PCI_DEVICE_ID_ATHEROS_AR5312_REV2 0x0052 /* AR5312 WMAC (AP31) */
-#define PCI_DEVICE_ID_ATHEROS_AR5312_REV7 0x0057 /* AR5312 WMAC (AP30-040) */
-#define PCI_DEVICE_ID_ATHEROS_AR5312_REV8 0x0058 /* AR5312 WMAC (AP43-030) */
-#define PCI_DEVICE_ID_ATHEROS_AR5212_0014 0x0014 /* AR5212 compatible */
-#define PCI_DEVICE_ID_ATHEROS_AR5212_0015 0x0015 /* AR5212 compatible */
-#define PCI_DEVICE_ID_ATHEROS_AR5212_0016 0x0016 /* AR5212 compatible */
-#define PCI_DEVICE_ID_ATHEROS_AR5212_0017 0x0017 /* AR5212 compatible */
-#define PCI_DEVICE_ID_ATHEROS_AR5212_0018 0x0018 /* AR5212 compatible */
-#define PCI_DEVICE_ID_ATHEROS_AR5212_0019 0x0019 /* AR5212 compatible */
-#define PCI_DEVICE_ID_ATHEROS_AR2413 0x001a /* AR2413 (Griffin-lite) */
-#define PCI_DEVICE_ID_ATHEROS_AR5413 0x001b /* AR5413 (Eagle) */
-#define PCI_DEVICE_ID_ATHEROS_AR5424 0x001c /* AR5424 (Condor PCI-E) */
-#define PCI_DEVICE_ID_ATHEROS_AR5416 0x0023 /* AR5416 */
-#define PCI_DEVICE_ID_ATHEROS_AR5418 0x0024 /* AR5418 */
+#define PCI_DEVICE_ID_ATHEROS_AR5210_DEFAULT 0x1107 /* AR5210 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_DEFAULT 0x1113 /* AR5212 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_DEFAULT 0x1112 /* AR5211 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_FPGA 0xf013 /* AR5212 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_LEGACY 0xff12 /* AR5211 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_FPGA11B 0xf11b /* AR5211 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5312_REV2 0x0052 /* AR5312 WMAC (AP31) */
+#define PCI_DEVICE_ID_ATHEROS_AR5312_REV7 0x0057 /* AR5312 WMAC (AP30-040) */
+#define PCI_DEVICE_ID_ATHEROS_AR5312_REV8 0x0058 /* AR5312 WMAC (AP43-030) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0014 0x0014 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0015 0x0015 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0016 0x0016 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0017 0x0017 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0018 0x0018 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0019 0x0019 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR2413 0x001a /* AR2413 (Griffin-lite) */
+#define PCI_DEVICE_ID_ATHEROS_AR5413 0x001b /* AR5413 (Eagle) */
+#define PCI_DEVICE_ID_ATHEROS_AR5424 0x001c /* AR5424 (Condor PCI-E) */
+#define PCI_DEVICE_ID_ATHEROS_AR5416 0x0023 /* AR5416 */
+#define PCI_DEVICE_ID_ATHEROS_AR5418 0x0024 /* AR5418 */
/****************************\
GENERIC DRIVER DEFINITIONS
\****************************/
-#define ATH5K_PRINTF(fmt, ...) printk("%s: " fmt, __func__, ##__VA_ARGS__)
+#define ATH5K_PRINTF(fmt, ...) \
+ printk(KERN_WARNING "%s: " fmt, __func__, ##__VA_ARGS__)
#define ATH5K_PRINTK(_sc, _level, _fmt, ...) \
printk(_level "ath5k %s: " _fmt, \
@@ -126,13 +131,6 @@
#define AR5K_REG_DISABLE_BITS(ah, _reg, _flags) \
ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) & ~(_flags), _reg)
-/* Access to PHY registers */
-#define AR5K_PHY_READ(ah, _reg) \
- ath5k_hw_reg_read(ah, (ah)->ah_phy + ((_reg) << 2))
-
-#define AR5K_PHY_WRITE(ah, _reg, _val) \
- ath5k_hw_reg_write(ah, _val, (ah)->ah_phy + ((_reg) << 2))
-
/* Access QCU registers per queue */
#define AR5K_REG_READ_Q(ah, _reg, _queue) \
(ath5k_hw_reg_read(ah, _reg) & (1 << _queue)) \
@@ -155,13 +153,12 @@
} while (0)
/*
- * Some tuneable values (these should be changeable by the user)
+ * Some tunable values (these should be changeable by the user)
* TODO: Make use of them and add more options OR use debug/configfs
*/
#define AR5K_TUNE_DMA_BEACON_RESP 2
#define AR5K_TUNE_SW_BEACON_RESP 10
#define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF 0
-#define AR5K_TUNE_RADAR_ALERT false
#define AR5K_TUNE_MIN_TX_FIFO_THRES 1
#define AR5K_TUNE_MAX_TX_FIFO_THRES ((IEEE80211_MAX_FRAME_LEN / 64) + 1)
#define AR5K_TUNE_REGISTER_TIMEOUT 20000
@@ -170,8 +167,8 @@
#define AR5K_TUNE_RSSI_THRES 129
/* This must be set when setting the RSSI threshold otherwise it can
* prevent a reset. If AR5K_RSSI_THR is read after writing to it
- * the BMISS_THRES will be seen as 0, seems harware doesn't keep
- * track of it. Max value depends on harware. For AR5210 this is just 7.
+ * the BMISS_THRES will be seen as 0, seems hardware doesn't keep
+ * track of it. Max value depends on hardware. For AR5210 this is just 7.
* For AR5211+ this seems to be up to 255. */
#define AR5K_TUNE_BMISS_THRES 7
#define AR5K_TUNE_REGISTER_DWELL_TIME 20000
@@ -290,17 +287,6 @@ enum ath5k_radio {
* Common silicon revision/version values
*/
-enum ath5k_srev_type {
- AR5K_VERSION_MAC,
- AR5K_VERSION_RAD,
-};
-
-struct ath5k_srev_name {
- const char *sr_name;
- enum ath5k_srev_type sr_type;
- u_int sr_val;
-};
-
#define AR5K_SREV_UNKNOWN 0xffff
#define AR5K_SREV_AR5210 0x00 /* Crete */
@@ -361,7 +347,7 @@ struct ath5k_srev_name {
/*
* Some of this information is based on Documentation from:
*
- * http://madwifi-project.org/wiki/ChipsetFeatures/SuperAG
+ * http://madwifi-project.org/wiki/ChipsetFeatures/SuperAG
*
* Modulation for Atheros' eXtended Range - range enhancing extension that is
* supposed to double the distance an Atheros client device can keep a
@@ -374,12 +360,12 @@ struct ath5k_srev_name {
* they are exclusive.
*
*/
-#define MODULATION_XR 0x00000200
+#define MODULATION_XR 0x00000200
/*
* Modulation for Atheros' Turbo G and Turbo A, its supposed to provide a
* throughput transmission speed up to 40Mbit/s-60Mbit/s at a 108Mbit/s
* signaling rate achieved through the bonding of two 54Mbit/s 802.11g
- * channels. To use this feature your Access Point must also suport it.
+ * channels. To use this feature your Access Point must also support it.
* There is also a distinction between "static" and "dynamic" turbo modes:
*
* - Static: is the dumb version: devices set to this mode stick to it until
@@ -419,7 +405,6 @@ enum ath5k_driver_mode {
AR5K_MODE_11A = 0,
AR5K_MODE_11B = 1,
AR5K_MODE_11G = 2,
- AR5K_MODE_XR = 0,
AR5K_MODE_MAX = 3
};
@@ -495,9 +480,9 @@ enum ath5k_tx_queue {
*/
enum ath5k_tx_queue_subtype {
AR5K_WME_AC_BK = 0, /*Background traffic*/
- AR5K_WME_AC_BE, /*Best-effort (normal) traffic)*/
- AR5K_WME_AC_VI, /*Video traffic*/
- AR5K_WME_AC_VO, /*Voice traffic*/
+ AR5K_WME_AC_BE, /*Best-effort (normal) traffic*/
+ AR5K_WME_AC_VI, /*Video traffic*/
+ AR5K_WME_AC_VO, /*Voice traffic*/
};
/*
@@ -537,6 +522,27 @@ enum ath5k_tx_queue_id {
#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE 0x2000 /* Enable hw compression -not implemented-*/
/*
+ * Data transmit queue state. One of these exists for each
+ * hardware transmit queue. Packets sent to us from above
+ * are assigned to queues based on their priority. Not all
+ * devices support a complete set of hardware transmit queues.
+ * For those devices the array sc_ac2q will map multiple
+ * priorities to fewer hardware queues (typically all to one
+ * hardware queue).
+ */
+struct ath5k_txq {
+ unsigned int qnum; /* hardware q number */
+ u32 *link; /* link ptr in last TX desc */
+ struct list_head q; /* transmit queue */
+ spinlock_t lock; /* lock on q and link */
+ bool setup;
+ int txq_len; /* number of queued buffers */
+ int txq_max; /* max allowed num of queued buffers */
+ bool txq_poll_mark;
+ unsigned int txq_stuck; /* informational counter */
+};
+
+/*
* A struct to hold tx queue's parameters
*/
struct ath5k_txq_info {
@@ -616,8 +622,8 @@ struct ath5k_rx_status {
#define AR5K_RXERR_FIFO 0x04
#define AR5K_RXERR_DECRYPT 0x08
#define AR5K_RXERR_MIC 0x10
-#define AR5K_RXKEYIX_INVALID ((u8) - 1)
-#define AR5K_TXKEYIX_INVALID ((u32) - 1)
+#define AR5K_RXKEYIX_INVALID ((u8) -1)
+#define AR5K_TXKEYIX_INVALID ((u32) -1)
/**************************\
@@ -668,32 +674,6 @@ struct ath5k_gain {
#define AR5K_SLOT_TIME_20 880
#define AR5K_SLOT_TIME_MAX 0xffff
-/* channel_flags */
-#define CHANNEL_CW_INT 0x0008 /* Contention Window interference detected */
-#define CHANNEL_CCK 0x0020 /* CCK channel */
-#define CHANNEL_OFDM 0x0040 /* OFDM channel */
-#define CHANNEL_2GHZ 0x0080 /* 2GHz channel. */
-#define CHANNEL_5GHZ 0x0100 /* 5GHz channel */
-#define CHANNEL_PASSIVE 0x0200 /* Only passive scan allowed */
-#define CHANNEL_DYN 0x0400 /* Dynamic CCK-OFDM channel (for g operation) */
-#define CHANNEL_XR 0x0800 /* XR channel */
-
-#define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM)
-#define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK)
-#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM)
-#define CHANNEL_X (CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_XR)
-
-#define CHANNEL_ALL (CHANNEL_OFDM|CHANNEL_CCK|CHANNEL_2GHZ|CHANNEL_5GHZ)
-
-#define CHANNEL_MODES CHANNEL_ALL
-
-/*
- * Used internaly for reset_tx_queue).
- * Also see struct struct ieee80211_channel.
- */
-#define IS_CHAN_XR(_c) ((_c->hw_value & CHANNEL_XR) != 0)
-#define IS_CHAN_B(_c) ((_c->hw_value & CHANNEL_B) != 0)
-
/*
* The following structure is used to map 2GHz channels to
* 5GHz Atheros channels.
@@ -710,7 +690,7 @@ struct ath5k_athchan_2ghz {
\******************/
/**
- * Seems the ar5xxx harware supports up to 32 rates, indexed by 1-32.
+ * Seems the ar5xxx hardware supports up to 32 rates, indexed by 1-32.
*
* The rate code is used to get the RX rate or set the TX rate on the
* hardware descriptors. It is also used for internal modulation control
@@ -767,6 +747,7 @@ struct ath5k_athchan_2ghz {
*/
#define AR5K_KEYCACHE_SIZE 8
+extern int ath5k_modparam_nohwcrypt;
/***********************\
HW RELATED DEFINITIONS
@@ -775,11 +756,11 @@ struct ath5k_athchan_2ghz {
/*
* Misc definitions
*/
-#define AR5K_RSSI_EP_MULTIPLIER (1<<7)
+#define AR5K_RSSI_EP_MULTIPLIER (1 << 7)
#define AR5K_ASSERT_ENTRY(_e, _s) do { \
if (_e >= _s) \
- return (false); \
+ return false; \
} while (0)
/*
@@ -790,52 +771,52 @@ struct ath5k_athchan_2ghz {
* enum ath5k_int - Hardware interrupt masks helpers
*
* @AR5K_INT_RX: mask to identify received frame interrupts, of type
- * AR5K_ISR_RXOK or AR5K_ISR_RXERR
+ * AR5K_ISR_RXOK or AR5K_ISR_RXERR
* @AR5K_INT_RXDESC: Request RX descriptor/Read RX descriptor (?)
* @AR5K_INT_RXNOFRM: No frame received (?)
* @AR5K_INT_RXEOL: received End Of List for VEOL (Virtual End Of List). The
- * Queue Control Unit (QCU) signals an EOL interrupt only if a descriptor's
- * LinkPtr is NULL. For more details, refer to:
- * http://www.freepatentsonline.com/20030225739.html
+ * Queue Control Unit (QCU) signals an EOL interrupt only if a descriptor's
+ * LinkPtr is NULL. For more details, refer to:
+ * http://www.freepatentsonline.com/20030225739.html
* @AR5K_INT_RXORN: Indicates we got RX overrun (eg. no more descriptors).
- * Note that Rx overrun is not always fatal, on some chips we can continue
- * operation without reseting the card, that's why int_fatal is not
- * common for all chips.
+ * Note that Rx overrun is not always fatal, on some chips we can continue
+ * operation without resetting the card, that's why int_fatal is not
+ * common for all chips.
* @AR5K_INT_TX: mask to identify received frame interrupts, of type
- * AR5K_ISR_TXOK or AR5K_ISR_TXERR
+ * AR5K_ISR_TXOK or AR5K_ISR_TXERR
* @AR5K_INT_TXDESC: Request TX descriptor/Read TX status descriptor (?)
* @AR5K_INT_TXURN: received when we should increase the TX trigger threshold
- * We currently do increments on interrupt by
- * (AR5K_TUNE_MAX_TX_FIFO_THRES - current_trigger_level) / 2
+ * We currently do increments on interrupt by
+ * (AR5K_TUNE_MAX_TX_FIFO_THRES - current_trigger_level) / 2
* @AR5K_INT_MIB: Indicates the either Management Information Base counters or
* one of the PHY error counters reached the maximum value and should be
* read and cleared.
* @AR5K_INT_RXPHY: RX PHY Error
* @AR5K_INT_RXKCM: RX Key cache miss
* @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a
- * beacon that must be handled in software. The alternative is if you
- * have VEOL support, in that case you let the hardware deal with things.
+ * beacon that must be handled in software. The alternative is if you
+ * have VEOL support, in that case you let the hardware deal with things.
* @AR5K_INT_BMISS: If in STA mode this indicates we have stopped seeing
- * beacons from the AP have associated with, we should probably try to
- * reassociate. When in IBSS mode this might mean we have not received
- * any beacons from any local stations. Note that every station in an
- * IBSS schedules to send beacons at the Target Beacon Transmission Time
- * (TBTT) with a random backoff.
+ * beacons from the AP have associated with, we should probably try to
+ * reassociate. When in IBSS mode this might mean we have not received
+ * any beacons from any local stations. Note that every station in an
+ * IBSS schedules to send beacons at the Target Beacon Transmission Time
+ * (TBTT) with a random backoff.
* @AR5K_INT_BNR: Beacon Not Ready interrupt - ??
* @AR5K_INT_GPIO: GPIO interrupt is used for RF Kill, disabled for now
- * until properly handled
+ * until properly handled
* @AR5K_INT_FATAL: Fatal errors were encountered, typically caused by DMA
- * errors. These types of errors we can enable seem to be of type
- * AR5K_SIMR2_MCABT, AR5K_SIMR2_SSERR and AR5K_SIMR2_DPERR.
+ * errors. These types of errors we can enable seem to be of type
+ * AR5K_SIMR2_MCABT, AR5K_SIMR2_SSERR and AR5K_SIMR2_DPERR.
* @AR5K_INT_GLOBAL: Used to clear and set the IER
* @AR5K_INT_NOCARD: signals the card has been removed
- * @AR5K_INT_COMMON: common interrupts shared amogst MACs with the same
- * bit value
+ * @AR5K_INT_COMMON: common interrupts shared among MACs with the same
+ * bit value
*
* These are mapped to take advantage of some common bits
* between the MACs, to be able to set intr properties
* easier. Some of them are not used yet inside hw.c. Most map
- * to the respective hw interrupt value as they are common amogst different
+ * to the respective hw interrupt value as they are common among different
* MACs.
*/
enum ath5k_int {
@@ -944,41 +925,12 @@ enum ath5k_power_mode {
#define AR5K_SOFTLED_ON 0
#define AR5K_SOFTLED_OFF 1
-/*
- * Chipset capabilities -see ath5k_hw_get_capability-
- * get_capability function is not yet fully implemented
- * in ath5k so most of these don't work yet...
- * TODO: Implement these & merge with _TUNE_ stuff above
- */
-enum ath5k_capability_type {
- AR5K_CAP_REG_DMN = 0, /* Used to get current reg. domain id */
- AR5K_CAP_TKIP_MIC = 2, /* Can handle TKIP MIC in hardware */
- AR5K_CAP_TKIP_SPLIT = 3, /* TKIP uses split keys */
- AR5K_CAP_PHYCOUNTERS = 4, /* PHY error counters */
- AR5K_CAP_DIVERSITY = 5, /* Supports fast diversity */
- AR5K_CAP_NUM_TXQUEUES = 6, /* Used to get max number of hw txqueues */
- AR5K_CAP_VEOL = 7, /* Supports virtual EOL */
- AR5K_CAP_COMPRESSION = 8, /* Supports compression */
- AR5K_CAP_BURST = 9, /* Supports packet bursting */
- AR5K_CAP_FASTFRAME = 10, /* Supports fast frames */
- AR5K_CAP_TXPOW = 11, /* Used to get global tx power limit */
- AR5K_CAP_TPC = 12, /* Can do per-packet tx power control (needed for 802.11a) */
- AR5K_CAP_BSSIDMASK = 13, /* Supports bssid mask */
- AR5K_CAP_MCAST_KEYSRCH = 14, /* Supports multicast key search */
- AR5K_CAP_TSF_ADJUST = 15, /* Supports beacon tsf adjust */
- AR5K_CAP_XR = 16, /* Supports XR mode */
- AR5K_CAP_WME_TKIPMIC = 17, /* Supports TKIP MIC when using WMM */
- AR5K_CAP_CHAN_HALFRATE = 18, /* Supports half rate channels */
- AR5K_CAP_CHAN_QUARTERRATE = 19, /* Supports quarter rate channels */
- AR5K_CAP_RFSILENT = 20, /* Supports RFsilent */
-};
-
/* XXX: we *may* move cap_range stuff to struct wiphy */
struct ath5k_capabilities {
/*
* Supported PHY modes
- * (ie. CHANNEL_A, CHANNEL_B, ...)
+ * (ie. AR5K_MODE_11A, AR5K_MODE_11B, ...)
*/
DECLARE_BITMAP(cap_mode, AR5K_MODE_MAX);
@@ -1009,25 +961,71 @@ struct ath5k_capabilities {
/* size of noise floor history (keep it a power of two) */
#define ATH5K_NF_CAL_HIST_MAX 8
-struct ath5k_nfcal_hist
-{
+struct ath5k_nfcal_hist {
s16 index; /* current index into nfval */
s16 nfval[ATH5K_NF_CAL_HIST_MAX]; /* last few noise floors */
};
-/**
- * struct avg_val - Helper structure for average calculation
- * @avg: contains the actual average value
- * @avg_weight: is used internally during calculation to prevent rounding errors
+#define ATH5K_LED_MAX_NAME_LEN 31
+
+/*
+ * State for LED triggers
*/
-struct ath5k_avg_val {
- int avg;
- int avg_weight;
+struct ath5k_led {
+ char name[ATH5K_LED_MAX_NAME_LEN + 1]; /* name of the LED in sysfs */
+ struct ath5k_hw *ah; /* driver state */
+ struct led_classdev led_dev; /* led classdev */
};
-/***************************************\
- HARDWARE ABSTRACTION LAYER STRUCTURE
-\***************************************/
+/* Rfkill */
+struct ath5k_rfkill {
+ /* GPIO PIN for rfkill */
+ u16 gpio;
+ /* polarity of rfkill GPIO PIN */
+ bool polarity;
+ /* RFKILL toggle tasklet */
+ struct tasklet_struct toggleq;
+};
+
+/* statistics */
+struct ath5k_statistics {
+ /* antenna use */
+ unsigned int antenna_rx[5]; /* frames count per antenna RX */
+ unsigned int antenna_tx[5]; /* frames count per antenna TX */
+
+ /* frame errors */
+ unsigned int rx_all_count; /* all RX frames, including errors */
+ unsigned int tx_all_count; /* all TX frames, including errors */
+ unsigned int rx_bytes_count; /* all RX bytes, including errored pkts
+ * and the MAC headers for each packet
+ */
+ unsigned int tx_bytes_count; /* all TX bytes, including errored pkts
+ * and the MAC headers and padding for
+ * each packet.
+ */
+ unsigned int rxerr_crc;
+ unsigned int rxerr_phy;
+ unsigned int rxerr_phy_code[32];
+ unsigned int rxerr_fifo;
+ unsigned int rxerr_decrypt;
+ unsigned int rxerr_mic;
+ unsigned int rxerr_proc;
+ unsigned int rxerr_jumbo;
+ unsigned int txerr_retry;
+ unsigned int txerr_fifo;
+ unsigned int txerr_filt;
+
+ /* MIB counters */
+ unsigned int ack_fail;
+ unsigned int rts_fail;
+ unsigned int rts_ok;
+ unsigned int fcs_error;
+ unsigned int beacons;
+
+ unsigned int mib_intr;
+ unsigned int rxorn_intr;
+ unsigned int rxeol_intr;
+};
/*
* Misc defines
@@ -1036,12 +1034,112 @@ struct ath5k_avg_val {
#define AR5K_MAX_GPIO 10
#define AR5K_MAX_RF_BANKS 8
-/* TODO: Clean up and merge with ath5k_softc */
+#if CHAN_DEBUG
+#define ATH_CHAN_MAX (26 + 26 + 26 + 200 + 200)
+#else
+#define ATH_CHAN_MAX (14 + 14 + 14 + 252 + 20)
+#endif
+
+#define ATH_RXBUF 40 /* number of RX buffers */
+#define ATH_TXBUF 200 /* number of TX buffers */
+#define ATH_BCBUF 4 /* number of beacon buffers */
+#define ATH5K_TXQ_LEN_MAX (ATH_TXBUF / 4) /* bufs per queue */
+#define ATH5K_TXQ_LEN_LOW (ATH5K_TXQ_LEN_MAX / 2) /* low mark */
+
+/* Driver state associated with an instance of a device */
struct ath5k_hw {
struct ath_common common;
- struct ath5k_softc *ah_sc;
- void __iomem *ah_iobase;
+ struct pci_dev *pdev;
+ struct device *dev; /* for dma mapping */
+ int irq;
+ u16 devid;
+ void __iomem *iobase; /* address of the device */
+ struct mutex lock; /* dev-level lock */
+ struct ieee80211_hw *hw; /* IEEE 802.11 common */
+ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+ struct ieee80211_channel channels[ATH_CHAN_MAX];
+ struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
+ s8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
+ enum nl80211_iftype opmode;
+
+#ifdef CONFIG_ATH5K_DEBUG
+ struct ath5k_dbg_info debug; /* debug info */
+#endif /* CONFIG_ATH5K_DEBUG */
+
+ struct ath5k_buf *bufptr; /* allocated buffer ptr */
+ struct ath5k_desc *desc; /* TX/RX descriptors */
+ dma_addr_t desc_daddr; /* DMA (physical) address */
+ size_t desc_len; /* size of TX/RX descriptors */
+
+ DECLARE_BITMAP(status, 6);
+#define ATH_STAT_INVALID 0 /* disable hardware accesses */
+#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */
+#define ATH_STAT_PROMISC 2
+#define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */
+#define ATH_STAT_STARTED 4 /* opened & irqs enabled */
+#define ATH_STAT_2G_DISABLED 5 /* multiband radio without 2G */
+
+ unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
+ struct ieee80211_channel *curchan; /* current h/w channel */
+
+ u16 nvifs;
+
+ enum ath5k_int imask; /* interrupt mask copy */
+
+ spinlock_t irqlock;
+ bool rx_pending; /* rx tasklet pending */
+ bool tx_pending; /* tx tasklet pending */
+
+ u8 bssidmask[ETH_ALEN];
+
+ unsigned int led_pin, /* GPIO pin for driving LED */
+ led_on; /* pin setting for LED on */
+
+ struct work_struct reset_work; /* deferred chip reset */
+
+ struct list_head rxbuf; /* receive buffer */
+ spinlock_t rxbuflock;
+ u32 *rxlink; /* link ptr in last RX desc */
+ struct tasklet_struct rxtq; /* rx intr tasklet */
+ struct ath5k_led rx_led; /* rx led */
+
+ struct list_head txbuf; /* transmit buffer */
+ spinlock_t txbuflock;
+ unsigned int txbuf_len; /* buf count in txbuf list */
+ struct ath5k_txq txqs[AR5K_NUM_TX_QUEUES]; /* tx queues */
+ struct tasklet_struct txtq; /* tx intr tasklet */
+ struct ath5k_led tx_led; /* tx led */
+
+ struct ath5k_rfkill rf_kill;
+
+ struct tasklet_struct calib; /* calibration tasklet */
+
+ spinlock_t block; /* protects beacon */
+ struct tasklet_struct beacontq; /* beacon intr tasklet */
+ struct list_head bcbuf; /* beacon buffer */
+ struct ieee80211_vif *bslot[ATH_BCBUF];
+ u16 num_ap_vifs;
+ u16 num_adhoc_vifs;
+ unsigned int bhalq, /* SW q for outgoing beacons */
+ bmisscount, /* missed beacon transmits */
+ bintval, /* beacon interval in TU */
+ bsent;
+ unsigned int nexttbtt; /* next beacon time in TU */
+ struct ath5k_txq *cabq; /* content after beacon */
+
+ int power_level; /* Requested tx power in dBm */
+ bool assoc; /* associate state */
+ bool enable_beacon; /* true if beacons are on */
+
+ struct ath5k_statistics stats;
+
+ struct ath5k_ani_state ani_state;
+ struct tasklet_struct ani_tasklet; /* ANI calibration */
+
+ struct delayed_work tx_complete_work;
+
+ struct survey_info survey; /* collected survey info */
enum ath5k_int ah_imr;
@@ -1051,10 +1149,8 @@ struct ath5k_hw {
enum ath5k_version ah_version;
enum ath5k_radio ah_radio;
- u32 ah_phy;
u32 ah_mac_srev;
u16 ah_mac_version;
- u16 ah_mac_revision;
u16 ah_phy_revision;
u16 ah_radio_5ghz_revision;
u16 ah_radio_2ghz_revision;
@@ -1065,6 +1161,8 @@ struct ath5k_hw {
u8 ah_retry_long;
u8 ah_retry_short;
+ u32 ah_use_32khz_clock;
+
u8 ah_coverage_class;
bool ah_ack_bitrate_high;
u8 ah_bwmode;
@@ -1120,12 +1218,6 @@ struct ath5k_hw {
bool txp_setup;
} ah_txpower;
- struct {
- bool r_enabled;
- int r_last_alert;
- struct ieee80211_channel r_last_channel;
- } ah_radar;
-
struct ath5k_nfcal_hist ah_nfcal_hist;
/* average beacon RSSI in our BSS (used by ANI) */
@@ -1168,47 +1260,24 @@ struct ath_bus_ops {
extern const struct ieee80211_ops ath5k_hw_ops;
/* Initialization and detach functions */
-int ath5k_init_softc(struct ath5k_softc *sc, const struct ath_bus_ops *bus_ops);
-void ath5k_deinit_softc(struct ath5k_softc *sc);
-int ath5k_hw_init(struct ath5k_softc *sc);
+int ath5k_hw_init(struct ath5k_hw *ah);
void ath5k_hw_deinit(struct ath5k_hw *ah);
-int ath5k_sysfs_register(struct ath5k_softc *sc);
-void ath5k_sysfs_unregister(struct ath5k_softc *sc);
-
-/* base.c */
-struct ath5k_buf;
-struct ath5k_txq;
-
-void set_beacon_filter(struct ieee80211_hw *hw, bool enable);
-bool ath_any_vif_assoc(struct ath5k_softc *sc);
-void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ath5k_txq *txq);
-int ath5k_init_hw(struct ath5k_softc *sc);
-int ath5k_stop_hw(struct ath5k_softc *sc);
-void ath5k_mode_setup(struct ath5k_softc *sc, struct ieee80211_vif *vif);
-void ath5k_update_bssid_mask_and_opmode(struct ath5k_softc *sc,
- struct ieee80211_vif *vif);
-int ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan);
-void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf);
-int ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
-void ath5k_beacon_config(struct ath5k_softc *sc);
-void ath5k_txbuf_free_skb(struct ath5k_softc *sc, struct ath5k_buf *bf);
-void ath5k_rxbuf_free_skb(struct ath5k_softc *sc, struct ath5k_buf *bf);
+int ath5k_sysfs_register(struct ath5k_hw *ah);
+void ath5k_sysfs_unregister(struct ath5k_hw *ah);
/*Chip id helper functions */
-const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
int ath5k_hw_read_srev(struct ath5k_hw *ah);
/* LED functions */
-int ath5k_init_leds(struct ath5k_softc *sc);
-void ath5k_led_enable(struct ath5k_softc *sc);
-void ath5k_led_off(struct ath5k_softc *sc);
-void ath5k_unregister_leds(struct ath5k_softc *sc);
+int ath5k_init_leds(struct ath5k_hw *ah);
+void ath5k_led_enable(struct ath5k_hw *ah);
+void ath5k_led_off(struct ath5k_hw *ah);
+void ath5k_unregister_leds(struct ath5k_hw *ah);
/* Reset Functions */
-int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial);
+int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel);
int ath5k_hw_on_hold(struct ath5k_hw *ah);
int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
struct ieee80211_channel *channel, bool fast, bool skip_pcu);
@@ -1253,7 +1322,7 @@ int ath5k_hw_get_frame_duration(struct ath5k_hw *ah,
int len, struct ieee80211_rate *rate, bool shortpre);
unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah);
unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah);
-extern int ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype opmode);
+int ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype opmode);
void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class);
/* RX filter control*/
int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac);
@@ -1318,9 +1387,6 @@ void ath5k_rfkill_hw_stop(struct ath5k_hw *ah);
/* Misc functions TODO: Cleanup */
int ath5k_hw_set_capabilities(struct ath5k_hw *ah);
-int ath5k_hw_get_capability(struct ath5k_hw *ah,
- enum ath5k_capability_type cap_type, u32 capability,
- u32 *result);
int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid, u16 assoc_id);
int ath5k_hw_disable_pspoll(struct ath5k_hw *ah);
@@ -1331,13 +1397,13 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel);
/* PHY functions */
/* Misc PHY functions */
-u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan);
+u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band);
int ath5k_hw_phy_disable(struct ath5k_hw *ah);
/* Gain_F optimization */
enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah);
int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah);
/* PHY/RF channel functions */
-bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags);
+bool ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel);
/* PHY calibration */
void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah);
int ath5k_hw_phy_calibrate(struct ath5k_hw *ah,
@@ -1356,17 +1422,17 @@ int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
u8 mode, bool fast);
/*
- * Functions used internaly
+ * Functions used internally
*/
static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah)
{
- return &ah->common;
+ return &ah->common;
}
static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah)
{
- return &(ath5k_hw_common(ah)->regulatory);
+ return &(ath5k_hw_common(ah)->regulatory);
}
#ifdef CONFIG_ATHEROS_AR231X
@@ -1377,10 +1443,10 @@ static inline void __iomem *ath5k_ahb_reg(struct ath5k_hw *ah, u16 reg)
/* On AR2315 and AR2317 the PCI clock domain registers
* are outside of the WMAC register space */
if (unlikely((reg >= 0x4000) && (reg < 0x5000) &&
- (ah->ah_mac_srev >= AR5K_SREV_AR2315_R6)))
+ (ah->ah_mac_srev >= AR5K_SREV_AR2315_R6)))
return AR5K_AR2315_PCI_BASE + reg;
- return ah->ah_iobase + reg;
+ return ah->iobase + reg;
}
static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg)
@@ -1397,12 +1463,12 @@ static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg)
static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg)
{
- return ioread32(ah->ah_iobase + reg);
+ return ioread32(ah->iobase + reg);
}
static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg)
{
- iowrite32(val, ah->ah_iobase + reg);
+ iowrite32(val, ah->iobase + reg);
}
#endif
diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c
index 1588401de3c4..91627dd2c26a 100644
--- a/drivers/net/wireless/ath/ath5k/attach.c
+++ b/drivers/net/wireless/ath/ath5k/attach.c
@@ -25,7 +25,6 @@
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
-#include "base.h"
/**
* ath5k_hw_post - Power On Self Test helper function
@@ -59,7 +58,7 @@ static int ath5k_hw_post(struct ath5k_hw *ah)
cur_val = ath5k_hw_reg_read(ah, cur_reg);
if (cur_val != var_pattern) {
- ATH5K_ERR(ah->ah_sc, "POST Failed !!!\n");
+ ATH5K_ERR(ah, "POST Failed !!!\n");
return -EAGAIN;
}
@@ -74,7 +73,7 @@ static int ath5k_hw_post(struct ath5k_hw *ah)
cur_val = ath5k_hw_reg_read(ah, cur_reg);
if (cur_val != var_pattern) {
- ATH5K_ERR(ah->ah_sc, "POST Failed !!!\n");
+ ATH5K_ERR(ah, "POST Failed !!!\n");
return -EAGAIN;
}
@@ -95,18 +94,18 @@ static int ath5k_hw_post(struct ath5k_hw *ah)
/**
* ath5k_hw_init - Check if hw is supported and init the needed structs
*
- * @sc: The &struct ath5k_softc we got from the driver's init_softc function
+ * @ah: The &struct ath5k_hw associated with the device
*
* Check if the device is supported, perform a POST and initialize the needed
* structs. Returns -ENOMEM if we don't have memory for the needed structs,
* -ENODEV if the device is not supported or prints an error msg if something
* else went wrong.
*/
-int ath5k_hw_init(struct ath5k_softc *sc)
+int ath5k_hw_init(struct ath5k_hw *ah)
{
- struct ath5k_hw *ah = sc->ah;
+ static const u8 zero_mac[ETH_ALEN] = { };
struct ath_common *common = ath5k_hw_common(ah);
- struct pci_dev *pdev = sc->pdev;
+ struct pci_dev *pdev = ah->pdev;
struct ath5k_eeprom_info *ee;
int ret;
u32 srev;
@@ -114,7 +113,6 @@ int ath5k_hw_init(struct ath5k_softc *sc)
/*
* HW information
*/
- ah->ah_radar.r_enabled = AR5K_TUNE_RADAR_ALERT;
ah->ah_bwmode = AR5K_BWMODE_DEFAULT;
ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
ah->ah_imr = 0;
@@ -122,8 +120,8 @@ int ath5k_hw_init(struct ath5k_softc *sc)
ah->ah_retry_long = AR5K_INIT_RETRY_LONG;
ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT;
ah->ah_noise_floor = -95; /* until first NF calibration is run */
- sc->ani_state.ani_mode = ATH5K_ANI_MODE_AUTO;
- ah->ah_current_channel = &sc->channels[0];
+ ah->ani_state.ani_mode = ATH5K_ANI_MODE_AUTO;
+ ah->ah_current_channel = &ah->channels[0];
/*
* Find the mac version
@@ -137,9 +135,8 @@ int ath5k_hw_init(struct ath5k_softc *sc)
else
ah->ah_version = AR5K_AR5212;
- /* Get the MAC revision */
+ /* Get the MAC version */
ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER);
- ah->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV);
/* Fill the ath5k_hw struct with the needed functions */
ret = ath5k_hw_init_desc_functions(ah);
@@ -147,7 +144,7 @@ int ath5k_hw_init(struct ath5k_softc *sc)
goto err;
/* Bring device out of sleep and reset its units */
- ret = ath5k_hw_nic_wakeup(ah, 0, true);
+ ret = ath5k_hw_nic_wakeup(ah, NULL);
if (ret)
goto err;
@@ -155,8 +152,7 @@ int ath5k_hw_init(struct ath5k_softc *sc)
ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) &
0xffffffff;
ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah,
- CHANNEL_5GHZ);
- ah->ah_phy = AR5K_PHY(0);
+ IEEE80211_BAND_5GHZ);
/* Try to identify radio chip based on its srev */
switch (ah->ah_radio_5ghz_revision & 0xf0) {
@@ -164,14 +160,14 @@ int ath5k_hw_init(struct ath5k_softc *sc)
ah->ah_radio = AR5K_RF5111;
ah->ah_single_chip = false;
ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
- CHANNEL_2GHZ);
+ IEEE80211_BAND_2GHZ);
break;
case AR5K_SREV_RAD_5112:
case AR5K_SREV_RAD_2112:
ah->ah_radio = AR5K_RF5112;
ah->ah_single_chip = false;
ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
- CHANNEL_2GHZ);
+ IEEE80211_BAND_2GHZ);
break;
case AR5K_SREV_RAD_2413:
ah->ah_radio = AR5K_RF2413;
@@ -191,7 +187,7 @@ int ath5k_hw_init(struct ath5k_softc *sc)
break;
case AR5K_SREV_RAD_5424:
if (ah->ah_mac_version == AR5K_SREV_AR2425 ||
- ah->ah_mac_version == AR5K_SREV_AR2417){
+ ah->ah_mac_version == AR5K_SREV_AR2417) {
ah->ah_radio = AR5K_RF2425;
ah->ah_single_chip = true;
} else {
@@ -208,45 +204,44 @@ int ath5k_hw_init(struct ath5k_softc *sc)
ah->ah_radio = AR5K_RF5111;
ah->ah_single_chip = false;
ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
- CHANNEL_2GHZ);
+ IEEE80211_BAND_2GHZ);
} else if (ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4) ||
- ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4) ||
- ah->ah_phy_revision == AR5K_SREV_PHY_2425) {
+ ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4) ||
+ ah->ah_phy_revision == AR5K_SREV_PHY_2425) {
ah->ah_radio = AR5K_RF2425;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2425;
} else if (srev == AR5K_SREV_AR5213A &&
- ah->ah_phy_revision == AR5K_SREV_PHY_5212B) {
+ ah->ah_phy_revision == AR5K_SREV_PHY_5212B) {
ah->ah_radio = AR5K_RF5112;
ah->ah_single_chip = false;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5112B;
} else if (ah->ah_mac_version == (AR5K_SREV_AR2415 >> 4) ||
- ah->ah_mac_version == (AR5K_SREV_AR2315_R6 >> 4)) {
+ ah->ah_mac_version == (AR5K_SREV_AR2315_R6 >> 4)) {
ah->ah_radio = AR5K_RF2316;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2316;
} else if (ah->ah_mac_version == (AR5K_SREV_AR5414 >> 4) ||
- ah->ah_phy_revision == AR5K_SREV_PHY_5413) {
+ ah->ah_phy_revision == AR5K_SREV_PHY_5413) {
ah->ah_radio = AR5K_RF5413;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5413;
} else if (ah->ah_mac_version == (AR5K_SREV_AR2414 >> 4) ||
- ah->ah_phy_revision == AR5K_SREV_PHY_2413) {
+ ah->ah_phy_revision == AR5K_SREV_PHY_2413) {
ah->ah_radio = AR5K_RF2413;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2413;
} else {
- ATH5K_ERR(sc, "Couldn't identify radio revision.\n");
+ ATH5K_ERR(ah, "Couldn't identify radio revision.\n");
ret = -ENODEV;
goto err;
}
}
- /* Return on unsuported chips (unsupported eeprom etc) */
- if ((srev >= AR5K_SREV_AR5416) &&
- (srev < AR5K_SREV_AR2425)) {
- ATH5K_ERR(sc, "Device not yet supported.\n");
+ /* Return on unsupported chips (unsupported eeprom etc) */
+ if ((srev >= AR5K_SREV_AR5416) && (srev < AR5K_SREV_AR2425)) {
+ ATH5K_ERR(ah, "Device not yet supported.\n");
ret = -ENODEV;
goto err;
}
@@ -268,7 +263,7 @@ int ath5k_hw_init(struct ath5k_softc *sc)
*/
ret = ath5k_eeprom_init(ah);
if (ret) {
- ATH5K_ERR(sc, "unable to init EEPROM\n");
+ ATH5K_ERR(ah, "unable to init EEPROM\n");
goto err;
}
@@ -285,7 +280,7 @@ int ath5k_hw_init(struct ath5k_softc *sc)
ath5k_hw_reg_write(ah, 0x28000039, AR5K_PCIE_SERDES);
ath5k_hw_reg_write(ah, 0x53160824, AR5K_PCIE_SERDES);
- /* If serdes programing is enabled, increase PCI-E
+ /* If serdes programming is enabled, increase PCI-E
* tx power for systems with long trace from host
* to minicard connector. */
if (ee->ee_serdes)
@@ -309,17 +304,17 @@ int ath5k_hw_init(struct ath5k_softc *sc)
/* Get misc capabilities */
ret = ath5k_hw_set_capabilities(ah);
if (ret) {
- ATH5K_ERR(sc, "unable to get device capabilities\n");
+ ATH5K_ERR(ah, "unable to get device capabilities\n");
goto err;
}
- if (test_bit(ATH_STAT_2G_DISABLED, sc->status)) {
+ if (test_bit(ATH_STAT_2G_DISABLED, ah->status)) {
__clear_bit(AR5K_MODE_11B, ah->ah_capabilities.cap_mode);
__clear_bit(AR5K_MODE_11G, ah->ah_capabilities.cap_mode);
}
/* Crypto settings */
- common->keymax = (sc->ah->ah_version == AR5K_AR5210 ?
+ common->keymax = (ah->ah_version == AR5K_AR5210 ?
AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211);
if (srev >= AR5K_SREV_AR5212_V4 &&
@@ -334,12 +329,12 @@ int ath5k_hw_init(struct ath5k_softc *sc)
}
/* MAC address is cleared until add_interface */
- ath5k_hw_set_lladdr(ah, (u8[ETH_ALEN]){});
+ ath5k_hw_set_lladdr(ah, zero_mac);
/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
memcpy(common->curbssid, ath_bcast_mac, ETH_ALEN);
ath5k_hw_set_bssid(ah);
- ath5k_hw_set_opmode(ah, sc->opmode);
+ ath5k_hw_set_opmode(ah, ah->opmode);
ath5k_hw_rfgain_opt_init(ah);
@@ -360,7 +355,7 @@ err:
*/
void ath5k_hw_deinit(struct ath5k_hw *ah)
{
- __set_bit(ATH_STAT_INVALID, ah->ah_sc->status);
+ __set_bit(ATH_STAT_INVALID, ah->status);
if (ah->ah_rf_banks != NULL)
kfree(ah->ah_rf_banks);
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index b6c5d3715b96..b346d0492001 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -42,6 +42,7 @@
#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/hardirq.h>
#include <linux/if.h>
#include <linux/io.h>
@@ -51,6 +52,7 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/etherdevice.h>
+#include <linux/nl80211.h>
#include <net/ieee80211_radiotap.h>
@@ -60,6 +62,8 @@
#include "reg.h"
#include "debug.h"
#include "ani.h"
+#include "ath5k.h"
+#include "../regd.h"
#define CREATE_TRACE_POINTS
#include "trace.h"
@@ -85,10 +89,8 @@ MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards");
MODULE_LICENSE("Dual BSD/GPL");
static int ath5k_init(struct ieee80211_hw *hw);
-static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan,
+static int ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
bool skip_pcu);
-int ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
-void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf);
/* Known SREVs */
static const struct ath5k_srev_name srev_names[] = {
@@ -239,8 +241,8 @@ static const struct ath_ops ath5k_common_ops = {
static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
- struct ath5k_softc *sc = hw->priv;
- struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah);
+ struct ath5k_hw *ah = hw->priv;
+ struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
return ath_reg_notifier_apply(wiphy, request, regulatory);
}
@@ -273,24 +275,22 @@ static unsigned int
ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels,
unsigned int mode, unsigned int max)
{
- unsigned int count, size, chfreq, freq, ch;
+ unsigned int count, size, freq, ch;
enum ieee80211_band band;
switch (mode) {
case AR5K_MODE_11A:
/* 1..220, but 2GHz frequencies are filtered by check_channel */
size = 220;
- chfreq = CHANNEL_5GHZ;
band = IEEE80211_BAND_5GHZ;
break;
case AR5K_MODE_11B:
case AR5K_MODE_11G:
size = 26;
- chfreq = CHANNEL_2GHZ;
band = IEEE80211_BAND_2GHZ;
break;
default:
- ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n");
+ ATH5K_WARN(ah, "bad mode, not copying channels\n");
return 0;
}
@@ -301,26 +301,19 @@ ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels,
if (freq == 0) /* mapping failed - not a standard channel */
continue;
+ /* Write channel info, needed for ath5k_channel_ok() */
+ channels[count].center_freq = freq;
+ channels[count].band = band;
+ channels[count].hw_value = mode;
+
/* Check if channel is supported by the chipset */
- if (!ath5k_channel_ok(ah, freq, chfreq))
+ if (!ath5k_channel_ok(ah, &channels[count]))
continue;
if (!modparam_all_channels &&
!ath5k_is_standard_channel(ch, band))
continue;
- /* Write channel info and increment counter */
- channels[count].center_freq = freq;
- channels[count].band = band;
- switch (mode) {
- case AR5K_MODE_11A:
- case AR5K_MODE_11G:
- channels[count].hw_value = chfreq | CHANNEL_OFDM;
- break;
- case AR5K_MODE_11B:
- channels[count].hw_value = CHANNEL_B;
- }
-
count++;
}
@@ -328,51 +321,50 @@ ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels,
}
static void
-ath5k_setup_rate_idx(struct ath5k_softc *sc, struct ieee80211_supported_band *b)
+ath5k_setup_rate_idx(struct ath5k_hw *ah, struct ieee80211_supported_band *b)
{
u8 i;
for (i = 0; i < AR5K_MAX_RATES; i++)
- sc->rate_idx[b->band][i] = -1;
+ ah->rate_idx[b->band][i] = -1;
for (i = 0; i < b->n_bitrates; i++) {
- sc->rate_idx[b->band][b->bitrates[i].hw_value] = i;
+ ah->rate_idx[b->band][b->bitrates[i].hw_value] = i;
if (b->bitrates[i].hw_value_short)
- sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i;
+ ah->rate_idx[b->band][b->bitrates[i].hw_value_short] = i;
}
}
static int
ath5k_setup_bands(struct ieee80211_hw *hw)
{
- struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = hw->priv;
struct ieee80211_supported_band *sband;
int max_c, count_c = 0;
int i;
- BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS);
- max_c = ARRAY_SIZE(sc->channels);
+ BUILD_BUG_ON(ARRAY_SIZE(ah->sbands) < IEEE80211_NUM_BANDS);
+ max_c = ARRAY_SIZE(ah->channels);
/* 2GHz band */
- sband = &sc->sbands[IEEE80211_BAND_2GHZ];
+ sband = &ah->sbands[IEEE80211_BAND_2GHZ];
sband->band = IEEE80211_BAND_2GHZ;
- sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0];
+ sband->bitrates = &ah->rates[IEEE80211_BAND_2GHZ][0];
- if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) {
+ if (test_bit(AR5K_MODE_11G, ah->ah_capabilities.cap_mode)) {
/* G mode */
memcpy(sband->bitrates, &ath5k_rates[0],
sizeof(struct ieee80211_rate) * 12);
sband->n_bitrates = 12;
- sband->channels = sc->channels;
+ sband->channels = ah->channels;
sband->n_channels = ath5k_setup_channels(ah, sband->channels,
AR5K_MODE_11G, max_c);
hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
count_c = sband->n_channels;
max_c -= count_c;
- } else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) {
+ } else if (test_bit(AR5K_MODE_11B, ah->ah_capabilities.cap_mode)) {
/* B mode */
memcpy(sband->bitrates, &ath5k_rates[0],
sizeof(struct ieee80211_rate) * 4);
@@ -391,7 +383,7 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
}
}
- sband->channels = sc->channels;
+ sband->channels = ah->channels;
sband->n_channels = ath5k_setup_channels(ah, sband->channels,
AR5K_MODE_11B, max_c);
@@ -399,27 +391,27 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
count_c = sband->n_channels;
max_c -= count_c;
}
- ath5k_setup_rate_idx(sc, sband);
+ ath5k_setup_rate_idx(ah, sband);
/* 5GHz band, A mode */
- if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) {
- sband = &sc->sbands[IEEE80211_BAND_5GHZ];
+ if (test_bit(AR5K_MODE_11A, ah->ah_capabilities.cap_mode)) {
+ sband = &ah->sbands[IEEE80211_BAND_5GHZ];
sband->band = IEEE80211_BAND_5GHZ;
- sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0];
+ sband->bitrates = &ah->rates[IEEE80211_BAND_5GHZ][0];
memcpy(sband->bitrates, &ath5k_rates[4],
sizeof(struct ieee80211_rate) * 8);
sband->n_bitrates = 8;
- sband->channels = &sc->channels[count_c];
+ sband->channels = &ah->channels[count_c];
sband->n_channels = ath5k_setup_channels(ah, sband->channels,
AR5K_MODE_11A, max_c);
hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband;
}
- ath5k_setup_rate_idx(sc, sband);
+ ath5k_setup_rate_idx(ah, sband);
- ath5k_debug_dump_bands(sc);
+ ath5k_debug_dump_bands(ah);
return 0;
}
@@ -429,14 +421,14 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
* To accomplish this we must first cleanup any pending DMA,
* then restart stuff after a la ath5k_init.
*
- * Called with sc->lock.
+ * Called with ah->lock.
*/
int
-ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
+ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan)
{
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"channel set, resetting (%u -> %u MHz)\n",
- sc->curchan->center_freq, chan->center_freq);
+ ah->curchan->center_freq, chan->center_freq);
/*
* To switch channels clear any pending DMA operations;
@@ -444,7 +436,7 @@ ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
* hardware at the new frequency, and then re-enable
* the relevant bits of the h/w.
*/
- return ath5k_reset(sc, chan, true);
+ return ath5k_reset(ah, chan, true);
}
void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
@@ -488,10 +480,10 @@ void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
}
void
-ath5k_update_bssid_mask_and_opmode(struct ath5k_softc *sc,
+ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
struct ieee80211_vif *vif)
{
- struct ath_common *common = ath5k_hw_common(sc->ah);
+ struct ath_common *common = ath5k_hw_common(ah);
struct ath5k_vif_iter_data iter_data;
u32 rfilt;
@@ -510,41 +502,41 @@ ath5k_update_bssid_mask_and_opmode(struct ath5k_softc *sc,
ath5k_vif_iter(&iter_data, vif->addr, vif);
/* Get list of all active MAC addresses */
- ieee80211_iterate_active_interfaces_atomic(sc->hw, ath5k_vif_iter,
+ ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
&iter_data);
- memcpy(sc->bssidmask, iter_data.mask, ETH_ALEN);
+ memcpy(ah->bssidmask, iter_data.mask, ETH_ALEN);
- sc->opmode = iter_data.opmode;
- if (sc->opmode == NL80211_IFTYPE_UNSPECIFIED)
+ ah->opmode = iter_data.opmode;
+ if (ah->opmode == NL80211_IFTYPE_UNSPECIFIED)
/* Nothing active, default to station mode */
- sc->opmode = NL80211_IFTYPE_STATION;
+ ah->opmode = NL80211_IFTYPE_STATION;
- ath5k_hw_set_opmode(sc->ah, sc->opmode);
- ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d (%s)\n",
- sc->opmode, ath_opmode_to_string(sc->opmode));
+ ath5k_hw_set_opmode(ah, ah->opmode);
+ ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "mode setup opmode %d (%s)\n",
+ ah->opmode, ath_opmode_to_string(ah->opmode));
if (iter_data.need_set_hw_addr && iter_data.found_active)
- ath5k_hw_set_lladdr(sc->ah, iter_data.active_mac);
+ ath5k_hw_set_lladdr(ah, iter_data.active_mac);
- if (ath5k_hw_hasbssidmask(sc->ah))
- ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
+ if (ath5k_hw_hasbssidmask(ah))
+ ath5k_hw_set_bssid_mask(ah, ah->bssidmask);
/* Set up RX Filter */
if (iter_data.n_stas > 1) {
/* If you have multiple STA interfaces connected to
* different APs, ARPs are not received (most of the time?)
- * Enabling PROMISC appears to fix that probem.
+ * Enabling PROMISC appears to fix that problem.
*/
- sc->filter_flags |= AR5K_RX_FILTER_PROM;
+ ah->filter_flags |= AR5K_RX_FILTER_PROM;
}
- rfilt = sc->filter_flags;
- ath5k_hw_set_rx_filter(sc->ah, rfilt);
- ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt);
+ rfilt = ah->filter_flags;
+ ath5k_hw_set_rx_filter(ah, rfilt);
+ ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt);
}
static inline int
-ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
+ath5k_hw_to_driver_rix(struct ath5k_hw *ah, int hw_rix)
{
int rix;
@@ -553,7 +545,7 @@ ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
"hw_rix out of bounds: %x\n", hw_rix))
return 0;
- rix = sc->rate_idx[sc->curchan->band][hw_rix];
+ rix = ah->rate_idx[ah->curchan->band][hw_rix];
if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
rix = 0;
@@ -565,9 +557,9 @@ ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
\***************/
static
-struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr)
+struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_hw *ah, dma_addr_t *skb_addr)
{
- struct ath_common *common = ath5k_hw_common(sc->ah);
+ struct ath_common *common = ath5k_hw_common(ah);
struct sk_buff *skb;
/*
@@ -579,17 +571,17 @@ struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr)
GFP_ATOMIC);
if (!skb) {
- ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
+ ATH5K_ERR(ah, "can't alloc skbuff of size %u\n",
common->rx_bufsize);
return NULL;
}
- *skb_addr = dma_map_single(sc->dev,
+ *skb_addr = dma_map_single(ah->dev,
skb->data, common->rx_bufsize,
DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(sc->dev, *skb_addr))) {
- ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__);
+ if (unlikely(dma_mapping_error(ah->dev, *skb_addr))) {
+ ATH5K_ERR(ah, "%s: DMA mapping failed\n", __func__);
dev_kfree_skb(skb);
return NULL;
}
@@ -597,15 +589,14 @@ struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr)
}
static int
-ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+ath5k_rxbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf)
{
- struct ath5k_hw *ah = sc->ah;
struct sk_buff *skb = bf->skb;
struct ath5k_desc *ds;
int ret;
if (!skb) {
- skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr);
+ skb = ath5k_rx_skb_alloc(ah, &bf->skbaddr);
if (!skb)
return -ENOMEM;
bf->skb = skb;
@@ -631,13 +622,13 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
ds->ds_data = bf->skbaddr;
ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0);
if (ret) {
- ATH5K_ERR(sc, "%s: could not setup RX desc\n", __func__);
+ ATH5K_ERR(ah, "%s: could not setup RX desc\n", __func__);
return ret;
}
- if (sc->rxlink != NULL)
- *sc->rxlink = bf->daddr;
- sc->rxlink = &ds->ds_link;
+ if (ah->rxlink != NULL)
+ *ah->rxlink = bf->daddr;
+ ah->rxlink = &ds->ds_link;
return 0;
}
@@ -665,10 +656,9 @@ static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
}
static int
-ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
+ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
struct ath5k_txq *txq, int padsize)
{
- struct ath5k_hw *ah = sc->ah;
struct ath5k_desc *ds = bf->desc;
struct sk_buff *skb = bf->skb;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -684,10 +674,10 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
/* XXX endianness */
- bf->skbaddr = dma_map_single(sc->dev, skb->data, skb->len,
+ bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len,
DMA_TO_DEVICE);
- rate = ieee80211_get_tx_rate(sc->hw, info);
+ rate = ieee80211_get_tx_rate(ah->hw, info);
if (!rate) {
ret = -EINVAL;
goto err_unmap;
@@ -711,20 +701,20 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
}
if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
flags |= AR5K_TXDESC_RTSENA;
- cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
- duration = le16_to_cpu(ieee80211_rts_duration(sc->hw,
+ cts_rate = ieee80211_get_rts_cts_rate(ah->hw, info)->hw_value;
+ duration = le16_to_cpu(ieee80211_rts_duration(ah->hw,
info->control.vif, pktlen, info));
}
if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
flags |= AR5K_TXDESC_CTSENA;
- cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
- duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw,
+ cts_rate = ieee80211_get_rts_cts_rate(ah->hw, info)->hw_value;
+ duration = le16_to_cpu(ieee80211_ctstoself_duration(ah->hw,
info->control.vif, pktlen, info));
}
ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
ieee80211_get_hdrlen_from_skb(skb), padsize,
get_hw_packet_type(skb),
- (sc->power_level * 2),
+ (ah->power_level * 2),
hw_rate,
info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
cts_rate, duration);
@@ -734,7 +724,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
memset(mrr_rate, 0, sizeof(mrr_rate));
memset(mrr_tries, 0, sizeof(mrr_tries));
for (i = 0; i < 3; i++) {
- rate = ieee80211_get_alt_retry_rate(sc->hw, info, i);
+ rate = ieee80211_get_alt_retry_rate(ah->hw, info, i);
if (!rate)
break;
@@ -765,7 +755,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
return 0;
err_unmap:
- dma_unmap_single(sc->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE);
+ dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE);
return ret;
}
@@ -774,7 +764,7 @@ err_unmap:
\*******************/
static int
-ath5k_desc_alloc(struct ath5k_softc *sc)
+ath5k_desc_alloc(struct ath5k_hw *ah)
{
struct ath5k_desc *ds;
struct ath5k_buf *bf;
@@ -783,69 +773,68 @@ ath5k_desc_alloc(struct ath5k_softc *sc)
int ret;
/* allocate descriptors */
- sc->desc_len = sizeof(struct ath5k_desc) *
+ ah->desc_len = sizeof(struct ath5k_desc) *
(ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1);
- sc->desc = dma_alloc_coherent(sc->dev, sc->desc_len,
- &sc->desc_daddr, GFP_KERNEL);
- if (sc->desc == NULL) {
- ATH5K_ERR(sc, "can't allocate descriptors\n");
+ ah->desc = dma_alloc_coherent(ah->dev, ah->desc_len,
+ &ah->desc_daddr, GFP_KERNEL);
+ if (ah->desc == NULL) {
+ ATH5K_ERR(ah, "can't allocate descriptors\n");
ret = -ENOMEM;
goto err;
}
- ds = sc->desc;
- da = sc->desc_daddr;
- ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n",
- ds, sc->desc_len, (unsigned long long)sc->desc_daddr);
+ ds = ah->desc;
+ da = ah->desc_daddr;
+ ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n",
+ ds, ah->desc_len, (unsigned long long)ah->desc_daddr);
bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
sizeof(struct ath5k_buf), GFP_KERNEL);
if (bf == NULL) {
- ATH5K_ERR(sc, "can't allocate bufptr\n");
+ ATH5K_ERR(ah, "can't allocate bufptr\n");
ret = -ENOMEM;
goto err_free;
}
- sc->bufptr = bf;
+ ah->bufptr = bf;
- INIT_LIST_HEAD(&sc->rxbuf);
+ INIT_LIST_HEAD(&ah->rxbuf);
for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
bf->desc = ds;
bf->daddr = da;
- list_add_tail(&bf->list, &sc->rxbuf);
+ list_add_tail(&bf->list, &ah->rxbuf);
}
- INIT_LIST_HEAD(&sc->txbuf);
- sc->txbuf_len = ATH_TXBUF;
- for (i = 0; i < ATH_TXBUF; i++, bf++, ds++,
- da += sizeof(*ds)) {
+ INIT_LIST_HEAD(&ah->txbuf);
+ ah->txbuf_len = ATH_TXBUF;
+ for (i = 0; i < ATH_TXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
bf->desc = ds;
bf->daddr = da;
- list_add_tail(&bf->list, &sc->txbuf);
+ list_add_tail(&bf->list, &ah->txbuf);
}
/* beacon buffers */
- INIT_LIST_HEAD(&sc->bcbuf);
+ INIT_LIST_HEAD(&ah->bcbuf);
for (i = 0; i < ATH_BCBUF; i++, bf++, ds++, da += sizeof(*ds)) {
bf->desc = ds;
bf->daddr = da;
- list_add_tail(&bf->list, &sc->bcbuf);
+ list_add_tail(&bf->list, &ah->bcbuf);
}
return 0;
err_free:
- dma_free_coherent(sc->dev, sc->desc_len, sc->desc, sc->desc_daddr);
+ dma_free_coherent(ah->dev, ah->desc_len, ah->desc, ah->desc_daddr);
err:
- sc->desc = NULL;
+ ah->desc = NULL;
return ret;
}
void
-ath5k_txbuf_free_skb(struct ath5k_softc *sc, struct ath5k_buf *bf)
+ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf)
{
BUG_ON(!bf);
if (!bf->skb)
return;
- dma_unmap_single(sc->dev, bf->skbaddr, bf->skb->len,
+ dma_unmap_single(ah->dev, bf->skbaddr, bf->skb->len,
DMA_TO_DEVICE);
dev_kfree_skb_any(bf->skb);
bf->skb = NULL;
@@ -854,15 +843,14 @@ ath5k_txbuf_free_skb(struct ath5k_softc *sc, struct ath5k_buf *bf)
}
void
-ath5k_rxbuf_free_skb(struct ath5k_softc *sc, struct ath5k_buf *bf)
+ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf)
{
- struct ath5k_hw *ah = sc->ah;
struct ath_common *common = ath5k_hw_common(ah);
BUG_ON(!bf);
if (!bf->skb)
return;
- dma_unmap_single(sc->dev, bf->skbaddr, common->rx_bufsize,
+ dma_unmap_single(ah->dev, bf->skbaddr, common->rx_bufsize,
DMA_FROM_DEVICE);
dev_kfree_skb_any(bf->skb);
bf->skb = NULL;
@@ -871,24 +859,24 @@ ath5k_rxbuf_free_skb(struct ath5k_softc *sc, struct ath5k_buf *bf)
}
static void
-ath5k_desc_free(struct ath5k_softc *sc)
+ath5k_desc_free(struct ath5k_hw *ah)
{
struct ath5k_buf *bf;
- list_for_each_entry(bf, &sc->txbuf, list)
- ath5k_txbuf_free_skb(sc, bf);
- list_for_each_entry(bf, &sc->rxbuf, list)
- ath5k_rxbuf_free_skb(sc, bf);
- list_for_each_entry(bf, &sc->bcbuf, list)
- ath5k_txbuf_free_skb(sc, bf);
+ list_for_each_entry(bf, &ah->txbuf, list)
+ ath5k_txbuf_free_skb(ah, bf);
+ list_for_each_entry(bf, &ah->rxbuf, list)
+ ath5k_rxbuf_free_skb(ah, bf);
+ list_for_each_entry(bf, &ah->bcbuf, list)
+ ath5k_txbuf_free_skb(ah, bf);
/* Free memory associated with all descriptors */
- dma_free_coherent(sc->dev, sc->desc_len, sc->desc, sc->desc_daddr);
- sc->desc = NULL;
- sc->desc_daddr = 0;
+ dma_free_coherent(ah->dev, ah->desc_len, ah->desc, ah->desc_daddr);
+ ah->desc = NULL;
+ ah->desc_daddr = 0;
- kfree(sc->bufptr);
- sc->bufptr = NULL;
+ kfree(ah->bufptr);
+ ah->bufptr = NULL;
}
@@ -897,10 +885,9 @@ ath5k_desc_free(struct ath5k_softc *sc)
\**************/
static struct ath5k_txq *
-ath5k_txq_setup(struct ath5k_softc *sc,
+ath5k_txq_setup(struct ath5k_hw *ah,
int qtype, int subtype)
{
- struct ath5k_hw *ah = sc->ah;
struct ath5k_txq *txq;
struct ath5k_txq_info qi = {
.tqi_subtype = subtype,
@@ -934,13 +921,7 @@ ath5k_txq_setup(struct ath5k_softc *sc,
*/
return ERR_PTR(qnum);
}
- if (qnum >= ARRAY_SIZE(sc->txqs)) {
- ATH5K_ERR(sc, "hw qnum %u out of range, max %tu!\n",
- qnum, ARRAY_SIZE(sc->txqs));
- ath5k_hw_release_tx_queue(ah, qnum);
- return ERR_PTR(-EINVAL);
- }
- txq = &sc->txqs[qnum];
+ txq = &ah->txqs[qnum];
if (!txq->setup) {
txq->qnum = qnum;
txq->link = NULL;
@@ -952,7 +933,7 @@ ath5k_txq_setup(struct ath5k_softc *sc,
txq->txq_poll_mark = false;
txq->txq_stuck = 0;
}
- return &sc->txqs[qnum];
+ return &ah->txqs[qnum];
}
static int
@@ -972,18 +953,17 @@ ath5k_beaconq_setup(struct ath5k_hw *ah)
}
static int
-ath5k_beaconq_config(struct ath5k_softc *sc)
+ath5k_beaconq_config(struct ath5k_hw *ah)
{
- struct ath5k_hw *ah = sc->ah;
struct ath5k_txq_info qi;
int ret;
- ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
+ ret = ath5k_hw_get_tx_queueprops(ah, ah->bhalq, &qi);
if (ret)
goto err;
- if (sc->opmode == NL80211_IFTYPE_AP ||
- sc->opmode == NL80211_IFTYPE_MESH_POINT) {
+ if (ah->opmode == NL80211_IFTYPE_AP ||
+ ah->opmode == NL80211_IFTYPE_MESH_POINT) {
/*
* Always burst out beacon and CAB traffic
* (aifs = cwmin = cwmax = 0)
@@ -991,7 +971,7 @@ ath5k_beaconq_config(struct ath5k_softc *sc)
qi.tqi_aifs = 0;
qi.tqi_cw_min = 0;
qi.tqi_cw_max = 0;
- } else if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+ } else if (ah->opmode == NL80211_IFTYPE_ADHOC) {
/*
* Adhoc mode; backoff between 0 and (2 * cw_min).
*/
@@ -1000,17 +980,17 @@ ath5k_beaconq_config(struct ath5k_softc *sc)
qi.tqi_cw_max = 2 * AR5K_TUNE_CWMIN;
}
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
"beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n",
qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max);
- ret = ath5k_hw_set_tx_queueprops(ah, sc->bhalq, &qi);
+ ret = ath5k_hw_set_tx_queueprops(ah, ah->bhalq, &qi);
if (ret) {
- ATH5K_ERR(sc, "%s: unable to update parameters for beacon "
+ ATH5K_ERR(ah, "%s: unable to update parameters for beacon "
"hardware queue!\n", __func__);
goto err;
}
- ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */
+ ret = ath5k_hw_reset_tx_queue(ah, ah->bhalq); /* push to h/w */
if (ret)
goto err;
@@ -1019,7 +999,7 @@ ath5k_beaconq_config(struct ath5k_softc *sc)
if (ret)
goto err;
- qi.tqi_ready_time = (sc->bintval * 80) / 100;
+ qi.tqi_ready_time = (ah->bintval * 80) / 100;
ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
if (ret)
goto err;
@@ -1032,7 +1012,7 @@ err:
/**
* ath5k_drain_tx_buffs - Empty tx buffers
*
- * @sc The &struct ath5k_softc
+ * @ah The &struct ath5k_hw
*
* Empty tx buffers from all queues in preparation
* of a reset or during shutdown.
@@ -1041,26 +1021,26 @@ err:
* we do not need to block ath5k_tx_tasklet
*/
static void
-ath5k_drain_tx_buffs(struct ath5k_softc *sc)
+ath5k_drain_tx_buffs(struct ath5k_hw *ah)
{
struct ath5k_txq *txq;
struct ath5k_buf *bf, *bf0;
int i;
- for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) {
- if (sc->txqs[i].setup) {
- txq = &sc->txqs[i];
+ for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) {
+ if (ah->txqs[i].setup) {
+ txq = &ah->txqs[i];
spin_lock_bh(&txq->lock);
list_for_each_entry_safe(bf, bf0, &txq->q, list) {
- ath5k_debug_printtxbuf(sc, bf);
+ ath5k_debug_printtxbuf(ah, bf);
- ath5k_txbuf_free_skb(sc, bf);
+ ath5k_txbuf_free_skb(ah, bf);
- spin_lock_bh(&sc->txbuflock);
- list_move_tail(&bf->list, &sc->txbuf);
- sc->txbuf_len++;
+ spin_lock_bh(&ah->txbuflock);
+ list_move_tail(&bf->list, &ah->txbuf);
+ ah->txbuf_len++;
txq->txq_len--;
- spin_unlock_bh(&sc->txbuflock);
+ spin_unlock_bh(&ah->txbuflock);
}
txq->link = NULL;
txq->txq_poll_mark = false;
@@ -1070,14 +1050,14 @@ ath5k_drain_tx_buffs(struct ath5k_softc *sc)
}
static void
-ath5k_txq_release(struct ath5k_softc *sc)
+ath5k_txq_release(struct ath5k_hw *ah)
{
- struct ath5k_txq *txq = sc->txqs;
+ struct ath5k_txq *txq = ah->txqs;
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++)
+ for (i = 0; i < ARRAY_SIZE(ah->txqs); i++, txq++)
if (txq->setup) {
- ath5k_hw_release_tx_queue(sc->ah, txq->qnum);
+ ath5k_hw_release_tx_queue(ah, txq->qnum);
txq->setup = false;
}
}
@@ -1091,33 +1071,32 @@ ath5k_txq_release(struct ath5k_softc *sc)
* Enable the receive h/w following a reset.
*/
static int
-ath5k_rx_start(struct ath5k_softc *sc)
+ath5k_rx_start(struct ath5k_hw *ah)
{
- struct ath5k_hw *ah = sc->ah;
struct ath_common *common = ath5k_hw_common(ah);
struct ath5k_buf *bf;
int ret;
common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz);
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
common->cachelsz, common->rx_bufsize);
- spin_lock_bh(&sc->rxbuflock);
- sc->rxlink = NULL;
- list_for_each_entry(bf, &sc->rxbuf, list) {
- ret = ath5k_rxbuf_setup(sc, bf);
+ spin_lock_bh(&ah->rxbuflock);
+ ah->rxlink = NULL;
+ list_for_each_entry(bf, &ah->rxbuf, list) {
+ ret = ath5k_rxbuf_setup(ah, bf);
if (ret != 0) {
- spin_unlock_bh(&sc->rxbuflock);
+ spin_unlock_bh(&ah->rxbuflock);
goto err;
}
}
- bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
+ bf = list_first_entry(&ah->rxbuf, struct ath5k_buf, list);
ath5k_hw_set_rxdp(ah, bf->daddr);
- spin_unlock_bh(&sc->rxbuflock);
+ spin_unlock_bh(&ah->rxbuflock);
ath5k_hw_start_rx_dma(ah); /* enable recv descriptors */
- ath5k_update_bssid_mask_and_opmode(sc, NULL); /* set filters, etc. */
+ ath5k_update_bssid_mask_and_opmode(ah, NULL); /* set filters, etc. */
ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */
return 0;
@@ -1133,21 +1112,19 @@ err:
* does.
*/
static void
-ath5k_rx_stop(struct ath5k_softc *sc)
+ath5k_rx_stop(struct ath5k_hw *ah)
{
- struct ath5k_hw *ah = sc->ah;
ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */
ath5k_hw_stop_rx_pcu(ah); /* disable PCU */
- ath5k_debug_printrxbuffs(sc, ah);
+ ath5k_debug_printrxbuffs(ah);
}
static unsigned int
-ath5k_rx_decrypted(struct ath5k_softc *sc, struct sk_buff *skb,
+ath5k_rx_decrypted(struct ath5k_hw *ah, struct sk_buff *skb,
struct ath5k_rx_status *rs)
{
- struct ath5k_hw *ah = sc->ah;
struct ath_common *common = ath5k_hw_common(ah);
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int keyix, hlen;
@@ -1174,10 +1151,10 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct sk_buff *skb,
static void
-ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
+ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb,
struct ieee80211_rx_status *rxs)
{
- struct ath_common *common = ath5k_hw_common(sc->ah);
+ struct ath_common *common = ath5k_hw_common(ah);
u64 tsf, bc_tstamp;
u32 hw_tu;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
@@ -1190,11 +1167,11 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
* have updated the local TSF. We have to work around various
* hardware bugs, though...
*/
- tsf = ath5k_hw_get_tsf64(sc->ah);
+ tsf = ath5k_hw_get_tsf64(ah);
bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
hw_tu = TSF_TO_TU(tsf);
- ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
"beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
(unsigned long long)bc_tstamp,
(unsigned long long)rxs->mactime,
@@ -1213,7 +1190,7 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
* received, not like mac80211 which defines it at the start.
*/
if (bc_tstamp > rxs->mactime) {
- ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
"fixing mactime from %llx to %llx\n",
(unsigned long long)rxs->mactime,
(unsigned long long)tsf);
@@ -1226,25 +1203,24 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
* beacons. This also takes care of synchronizing beacon sending
* times with other stations.
*/
- if (hw_tu >= sc->nexttbtt)
- ath5k_beacon_update_timers(sc, bc_tstamp);
+ if (hw_tu >= ah->nexttbtt)
+ ath5k_beacon_update_timers(ah, bc_tstamp);
/* Check if the beacon timers are still correct, because a TSF
* update might have created a window between them - for a
* longer description see the comment of this function: */
- if (!ath5k_hw_check_beacon_timers(sc->ah, sc->bintval)) {
- ath5k_beacon_update_timers(sc, bc_tstamp);
- ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ if (!ath5k_hw_check_beacon_timers(ah, ah->bintval)) {
+ ath5k_beacon_update_timers(ah, bc_tstamp);
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
"fixed beacon timers after beacon receive\n");
}
}
}
static void
-ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi)
+ath5k_update_beacon_rssi(struct ath5k_hw *ah, struct sk_buff *skb, int rssi)
{
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
- struct ath5k_hw *ah = sc->ah;
struct ath_common *common = ath5k_hw_common(ah);
/* only beacons from our BSSID */
@@ -1263,16 +1239,15 @@ ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi)
*/
static int ath5k_common_padpos(struct sk_buff *skb)
{
- struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
__le16 frame_control = hdr->frame_control;
int padpos = 24;
- if (ieee80211_has_a4(frame_control)) {
+ if (ieee80211_has_a4(frame_control))
padpos += ETH_ALEN;
- }
- if (ieee80211_is_data_qos(frame_control)) {
+
+ if (ieee80211_is_data_qos(frame_control))
padpos += IEEE80211_QOS_CTL_LEN;
- }
return padpos;
}
@@ -1286,13 +1261,13 @@ static int ath5k_add_padding(struct sk_buff *skb)
int padpos = ath5k_common_padpos(skb);
int padsize = padpos & 3;
- if (padsize && skb->len>padpos) {
+ if (padsize && skb->len > padpos) {
if (skb_headroom(skb) < padsize)
return -1;
skb_push(skb, padsize);
- memmove(skb->data, skb->data+padsize, padpos);
+ memmove(skb->data, skb->data + padsize, padpos);
return padsize;
}
@@ -1317,7 +1292,7 @@ static int ath5k_remove_padding(struct sk_buff *skb)
int padpos = ath5k_common_padpos(skb);
int padsize = padpos & 3;
- if (padsize && skb->len>=padpos+padsize) {
+ if (padsize && skb->len >= padpos + padsize) {
memmove(skb->data + padsize, skb->data, padpos);
skb_pull(skb, padsize);
return padsize;
@@ -1327,7 +1302,7 @@ static int ath5k_remove_padding(struct sk_buff *skb)
}
static void
-ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb,
+ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
struct ath5k_rx_status *rs)
{
struct ieee80211_rx_status *rxs;
@@ -1353,44 +1328,44 @@ ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb,
* timestamp (beginning of phy frame, data frame, end of rx?).
* The only thing we know is that it is hardware specific...
* On AR5213 it seems the rx timestamp is at the end of the
- * frame, but i'm not sure.
+ * frame, but I'm not sure.
*
* NOTE: mac80211 defines mactime at the beginning of the first
* data symbol. Since we don't have any time references it's
* impossible to comply to that. This affects IBSS merge only
* right now, so it's not too bad...
*/
- rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp);
+ rxs->mactime = ath5k_extend_tsf(ah, rs->rs_tstamp);
rxs->flag |= RX_FLAG_MACTIME_MPDU;
- rxs->freq = sc->curchan->center_freq;
- rxs->band = sc->curchan->band;
+ rxs->freq = ah->curchan->center_freq;
+ rxs->band = ah->curchan->band;
- rxs->signal = sc->ah->ah_noise_floor + rs->rs_rssi;
+ rxs->signal = ah->ah_noise_floor + rs->rs_rssi;
rxs->antenna = rs->rs_antenna;
if (rs->rs_antenna > 0 && rs->rs_antenna < 5)
- sc->stats.antenna_rx[rs->rs_antenna]++;
+ ah->stats.antenna_rx[rs->rs_antenna]++;
else
- sc->stats.antenna_rx[0]++; /* invalid */
+ ah->stats.antenna_rx[0]++; /* invalid */
- rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs->rs_rate);
- rxs->flag |= ath5k_rx_decrypted(sc, skb, rs);
+ rxs->rate_idx = ath5k_hw_to_driver_rix(ah, rs->rs_rate);
+ rxs->flag |= ath5k_rx_decrypted(ah, skb, rs);
if (rxs->rate_idx >= 0 && rs->rs_rate ==
- sc->sbands[sc->curchan->band].bitrates[rxs->rate_idx].hw_value_short)
+ ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short)
rxs->flag |= RX_FLAG_SHORTPRE;
- trace_ath5k_rx(sc, skb);
+ trace_ath5k_rx(ah, skb);
- ath5k_update_beacon_rssi(sc, skb, rs->rs_rssi);
+ ath5k_update_beacon_rssi(ah, skb, rs->rs_rssi);
/* check beacons in IBSS mode */
- if (sc->opmode == NL80211_IFTYPE_ADHOC)
- ath5k_check_ibss_tsf(sc, skb, rxs);
+ if (ah->opmode == NL80211_IFTYPE_ADHOC)
+ ath5k_check_ibss_tsf(ah, skb, rxs);
- ieee80211_rx(sc->hw, skb);
+ ieee80211_rx(ah->hw, skb);
}
/** ath5k_frame_receive_ok() - Do we want to receive this frame or not?
@@ -1399,20 +1374,20 @@ ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb,
* statistics. Return true if we want this frame, false if not.
*/
static bool
-ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
+ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
{
- sc->stats.rx_all_count++;
- sc->stats.rx_bytes_count += rs->rs_datalen;
+ ah->stats.rx_all_count++;
+ ah->stats.rx_bytes_count += rs->rs_datalen;
if (unlikely(rs->rs_status)) {
if (rs->rs_status & AR5K_RXERR_CRC)
- sc->stats.rxerr_crc++;
+ ah->stats.rxerr_crc++;
if (rs->rs_status & AR5K_RXERR_FIFO)
- sc->stats.rxerr_fifo++;
+ ah->stats.rxerr_fifo++;
if (rs->rs_status & AR5K_RXERR_PHY) {
- sc->stats.rxerr_phy++;
+ ah->stats.rxerr_phy++;
if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32)
- sc->stats.rxerr_phy_code[rs->rs_phyerr]++;
+ ah->stats.rxerr_phy_code[rs->rs_phyerr]++;
return false;
}
if (rs->rs_status & AR5K_RXERR_DECRYPT) {
@@ -1426,13 +1401,13 @@ ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
*
* XXX do key cache faulting
*/
- sc->stats.rxerr_decrypt++;
+ ah->stats.rxerr_decrypt++;
if (rs->rs_keyix == AR5K_RXKEYIX_INVALID &&
!(rs->rs_status & AR5K_RXERR_CRC))
return true;
}
if (rs->rs_status & AR5K_RXERR_MIC) {
- sc->stats.rxerr_mic++;
+ ah->stats.rxerr_mic++;
return true;
}
@@ -1442,25 +1417,26 @@ ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
}
if (unlikely(rs->rs_more)) {
- sc->stats.rxerr_jumbo++;
+ ah->stats.rxerr_jumbo++;
return false;
}
return true;
}
static void
-ath5k_set_current_imask(struct ath5k_softc *sc)
+ath5k_set_current_imask(struct ath5k_hw *ah)
{
- enum ath5k_int imask = sc->imask;
+ enum ath5k_int imask;
unsigned long flags;
- spin_lock_irqsave(&sc->irqlock, flags);
- if (sc->rx_pending)
+ spin_lock_irqsave(&ah->irqlock, flags);
+ imask = ah->imask;
+ if (ah->rx_pending)
imask &= ~AR5K_INT_RX_ALL;
- if (sc->tx_pending)
+ if (ah->tx_pending)
imask &= ~AR5K_INT_TX_ALL;
- ath5k_hw_set_imr(sc->ah, imask);
- spin_unlock_irqrestore(&sc->irqlock, flags);
+ ath5k_hw_set_imr(ah, imask);
+ spin_unlock_irqrestore(&ah->irqlock, flags);
}
static void
@@ -1469,39 +1445,38 @@ ath5k_tasklet_rx(unsigned long data)
struct ath5k_rx_status rs = {};
struct sk_buff *skb, *next_skb;
dma_addr_t next_skb_addr;
- struct ath5k_softc *sc = (void *)data;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = (void *)data;
struct ath_common *common = ath5k_hw_common(ah);
struct ath5k_buf *bf;
struct ath5k_desc *ds;
int ret;
- spin_lock(&sc->rxbuflock);
- if (list_empty(&sc->rxbuf)) {
- ATH5K_WARN(sc, "empty rx buf pool\n");
+ spin_lock(&ah->rxbuflock);
+ if (list_empty(&ah->rxbuf)) {
+ ATH5K_WARN(ah, "empty rx buf pool\n");
goto unlock;
}
do {
- bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
+ bf = list_first_entry(&ah->rxbuf, struct ath5k_buf, list);
BUG_ON(bf->skb == NULL);
skb = bf->skb;
ds = bf->desc;
/* bail if HW is still using self-linked descriptor */
- if (ath5k_hw_get_rxdp(sc->ah) == bf->daddr)
+ if (ath5k_hw_get_rxdp(ah) == bf->daddr)
break;
- ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs);
+ ret = ah->ah_proc_rx_desc(ah, ds, &rs);
if (unlikely(ret == -EINPROGRESS))
break;
else if (unlikely(ret)) {
- ATH5K_ERR(sc, "error in processing rx descriptor\n");
- sc->stats.rxerr_proc++;
+ ATH5K_ERR(ah, "error in processing rx descriptor\n");
+ ah->stats.rxerr_proc++;
break;
}
- if (ath5k_receive_frame_ok(sc, &rs)) {
- next_skb = ath5k_rx_skb_alloc(sc, &next_skb_addr);
+ if (ath5k_receive_frame_ok(ah, &rs)) {
+ next_skb = ath5k_rx_skb_alloc(ah, &next_skb_addr);
/*
* If we can't replace bf->skb with a new skb under
@@ -1510,24 +1485,24 @@ ath5k_tasklet_rx(unsigned long data)
if (!next_skb)
goto next;
- dma_unmap_single(sc->dev, bf->skbaddr,
+ dma_unmap_single(ah->dev, bf->skbaddr,
common->rx_bufsize,
DMA_FROM_DEVICE);
skb_put(skb, rs.rs_datalen);
- ath5k_receive_frame(sc, skb, &rs);
+ ath5k_receive_frame(ah, skb, &rs);
bf->skb = next_skb;
bf->skbaddr = next_skb_addr;
}
next:
- list_move_tail(&bf->list, &sc->rxbuf);
- } while (ath5k_rxbuf_setup(sc, bf) == 0);
+ list_move_tail(&bf->list, &ah->rxbuf);
+ } while (ath5k_rxbuf_setup(ah, bf) == 0);
unlock:
- spin_unlock(&sc->rxbuflock);
- sc->rx_pending = false;
- ath5k_set_current_imask(sc);
+ spin_unlock(&ah->rxbuflock);
+ ah->rx_pending = false;
+ ath5k_set_current_imask(ah);
}
@@ -1539,12 +1514,12 @@ void
ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ath5k_txq *txq)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
struct ath5k_buf *bf;
unsigned long flags;
int padsize;
- trace_ath5k_tx(sc, skb, txq);
+ trace_ath5k_tx(ah, skb, txq);
/*
* The hardware expects the header padded to 4 byte boundaries.
@@ -1552,36 +1527,37 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
*/
padsize = ath5k_add_padding(skb);
if (padsize < 0) {
- ATH5K_ERR(sc, "tx hdrlen not %%4: not enough"
+ ATH5K_ERR(ah, "tx hdrlen not %%4: not enough"
" headroom to pad");
goto drop_packet;
}
- if (txq->txq_len >= txq->txq_max)
+ if (txq->txq_len >= txq->txq_max &&
+ txq->qnum <= AR5K_TX_QUEUE_ID_DATA_MAX)
ieee80211_stop_queue(hw, txq->qnum);
- spin_lock_irqsave(&sc->txbuflock, flags);
- if (list_empty(&sc->txbuf)) {
- ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
- spin_unlock_irqrestore(&sc->txbuflock, flags);
+ spin_lock_irqsave(&ah->txbuflock, flags);
+ if (list_empty(&ah->txbuf)) {
+ ATH5K_ERR(ah, "no further txbuf available, dropping packet\n");
+ spin_unlock_irqrestore(&ah->txbuflock, flags);
ieee80211_stop_queues(hw);
goto drop_packet;
}
- bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
+ bf = list_first_entry(&ah->txbuf, struct ath5k_buf, list);
list_del(&bf->list);
- sc->txbuf_len--;
- if (list_empty(&sc->txbuf))
+ ah->txbuf_len--;
+ if (list_empty(&ah->txbuf))
ieee80211_stop_queues(hw);
- spin_unlock_irqrestore(&sc->txbuflock, flags);
+ spin_unlock_irqrestore(&ah->txbuflock, flags);
bf->skb = skb;
- if (ath5k_txbuf_setup(sc, bf, txq, padsize)) {
+ if (ath5k_txbuf_setup(ah, bf, txq, padsize)) {
bf->skb = NULL;
- spin_lock_irqsave(&sc->txbuflock, flags);
- list_add_tail(&bf->list, &sc->txbuf);
- sc->txbuf_len++;
- spin_unlock_irqrestore(&sc->txbuflock, flags);
+ spin_lock_irqsave(&ah->txbuflock, flags);
+ list_add_tail(&bf->list, &ah->txbuf);
+ ah->txbuf_len++;
+ spin_unlock_irqrestore(&ah->txbuflock, flags);
goto drop_packet;
}
return;
@@ -1591,15 +1567,15 @@ drop_packet:
}
static void
-ath5k_tx_frame_completed(struct ath5k_softc *sc, struct sk_buff *skb,
+ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
struct ath5k_txq *txq, struct ath5k_tx_status *ts)
{
struct ieee80211_tx_info *info;
u8 tries[3];
int i;
- sc->stats.tx_all_count++;
- sc->stats.tx_bytes_count += skb->len;
+ ah->stats.tx_all_count++;
+ ah->stats.tx_bytes_count += skb->len;
info = IEEE80211_SKB_CB(skb);
tries[0] = info->status.rates[0].count;
@@ -1619,15 +1595,15 @@ ath5k_tx_frame_completed(struct ath5k_softc *sc, struct sk_buff *skb,
info->status.rates[ts->ts_final_idx + 1].idx = -1;
if (unlikely(ts->ts_status)) {
- sc->stats.ack_fail++;
+ ah->stats.ack_fail++;
if (ts->ts_status & AR5K_TXERR_FILT) {
info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
- sc->stats.txerr_filt++;
+ ah->stats.txerr_filt++;
}
if (ts->ts_status & AR5K_TXERR_XRETRY)
- sc->stats.txerr_retry++;
+ ah->stats.txerr_retry++;
if (ts->ts_status & AR5K_TXERR_FIFO)
- sc->stats.txerr_fifo++;
+ ah->stats.txerr_fifo++;
} else {
info->flags |= IEEE80211_TX_STAT_ACK;
info->status.ack_signal = ts->ts_rssi;
@@ -1643,16 +1619,16 @@ ath5k_tx_frame_completed(struct ath5k_softc *sc, struct sk_buff *skb,
ath5k_remove_padding(skb);
if (ts->ts_antenna > 0 && ts->ts_antenna < 5)
- sc->stats.antenna_tx[ts->ts_antenna]++;
+ ah->stats.antenna_tx[ts->ts_antenna]++;
else
- sc->stats.antenna_tx[0]++; /* invalid */
+ ah->stats.antenna_tx[0]++; /* invalid */
- trace_ath5k_tx_complete(sc, skb, txq, ts);
- ieee80211_tx_status(sc->hw, skb);
+ trace_ath5k_tx_complete(ah, skb, txq, ts);
+ ieee80211_tx_status(ah->hw, skb);
}
static void
-ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+ath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq *txq)
{
struct ath5k_tx_status ts = {};
struct ath5k_buf *bf, *bf0;
@@ -1669,11 +1645,11 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
if (bf->skb != NULL) {
ds = bf->desc;
- ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
+ ret = ah->ah_proc_tx_desc(ah, ds, &ts);
if (unlikely(ret == -EINPROGRESS))
break;
else if (unlikely(ret)) {
- ATH5K_ERR(sc,
+ ATH5K_ERR(ah,
"error %d while processing "
"queue %u\n", ret, txq->qnum);
break;
@@ -1682,9 +1658,9 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
skb = bf->skb;
bf->skb = NULL;
- dma_unmap_single(sc->dev, bf->skbaddr, skb->len,
+ dma_unmap_single(ah->dev, bf->skbaddr, skb->len,
DMA_TO_DEVICE);
- ath5k_tx_frame_completed(sc, skb, txq, &ts);
+ ath5k_tx_frame_completed(ah, skb, txq, &ts);
}
/*
@@ -1693,31 +1669,31 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
* host memory and moved on.
* Always keep the last descriptor to avoid HW races...
*/
- if (ath5k_hw_get_txdp(sc->ah, txq->qnum) != bf->daddr) {
- spin_lock(&sc->txbuflock);
- list_move_tail(&bf->list, &sc->txbuf);
- sc->txbuf_len++;
+ if (ath5k_hw_get_txdp(ah, txq->qnum) != bf->daddr) {
+ spin_lock(&ah->txbuflock);
+ list_move_tail(&bf->list, &ah->txbuf);
+ ah->txbuf_len++;
txq->txq_len--;
- spin_unlock(&sc->txbuflock);
+ spin_unlock(&ah->txbuflock);
}
}
spin_unlock(&txq->lock);
if (txq->txq_len < ATH5K_TXQ_LEN_LOW && txq->qnum < 4)
- ieee80211_wake_queue(sc->hw, txq->qnum);
+ ieee80211_wake_queue(ah->hw, txq->qnum);
}
static void
ath5k_tasklet_tx(unsigned long data)
{
int i;
- struct ath5k_softc *sc = (void *)data;
+ struct ath5k_hw *ah = (void *)data;
- for (i=0; i < AR5K_NUM_TX_QUEUES; i++)
- if (sc->txqs[i].setup && (sc->ah->ah_txq_isr & BIT(i)))
- ath5k_tx_processq(sc, &sc->txqs[i]);
+ for (i = 0; i < AR5K_NUM_TX_QUEUES; i++)
+ if (ah->txqs[i].setup && (ah->ah_txq_isr & BIT(i)))
+ ath5k_tx_processq(ah, &ah->txqs[i]);
- sc->tx_pending = false;
- ath5k_set_current_imask(sc);
+ ah->tx_pending = false;
+ ath5k_set_current_imask(ah);
}
@@ -1729,25 +1705,26 @@ ath5k_tasklet_tx(unsigned long data)
* Setup the beacon frame for transmit.
*/
static int
-ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+ath5k_beacon_setup(struct ath5k_hw *ah, struct ath5k_buf *bf)
{
struct sk_buff *skb = bf->skb;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ath5k_hw *ah = sc->ah;
struct ath5k_desc *ds;
int ret = 0;
u8 antenna;
u32 flags;
const int padsize = 0;
- bf->skbaddr = dma_map_single(sc->dev, skb->data, skb->len,
+ bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len,
DMA_TO_DEVICE);
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] "
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] "
"skbaddr %llx\n", skb, skb->data, skb->len,
(unsigned long long)bf->skbaddr);
- if (dma_mapping_error(sc->dev, bf->skbaddr)) {
- ATH5K_ERR(sc, "beacon DMA mapping failed\n");
+ if (dma_mapping_error(ah->dev, bf->skbaddr)) {
+ ATH5K_ERR(ah, "beacon DMA mapping failed\n");
+ dev_kfree_skb_any(skb);
+ bf->skb = NULL;
return -EIO;
}
@@ -1755,7 +1732,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
antenna = ah->ah_tx_ant;
flags = AR5K_TXDESC_NOACK;
- if (sc->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) {
+ if (ah->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) {
ds->ds_link = bf->daddr; /* self-linked */
flags |= AR5K_TXDESC_VEOL;
} else
@@ -1767,7 +1744,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
* 4 beacons to make sure everybody hears our AP.
* When a client tries to associate, hw will keep
* track of the tx antenna to be used for this client
- * automaticaly, based on ACKed packets.
+ * automatically, based on ACKed packets.
*
* Note: AP still listens and transmits RTS on the
* default antenna which is supposed to be an omni.
@@ -1780,7 +1757,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
* on all of them.
*/
if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP)
- antenna = sc->bsent & 4 ? 2 : 1;
+ antenna = ah->bsent & 4 ? 2 : 1;
/* FIXME: If we are in g mode and rate is a CCK rate
@@ -1789,8 +1766,8 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
ds->ds_data = bf->skbaddr;
ret = ah->ah_setup_tx_desc(ah, ds, skb->len,
ieee80211_get_hdrlen_from_skb(skb), padsize,
- AR5K_PKT_TYPE_BEACON, (sc->power_level * 2),
- ieee80211_get_tx_rate(sc->hw, info)->hw_value,
+ AR5K_PKT_TYPE_BEACON, (ah->power_level * 2),
+ ieee80211_get_tx_rate(ah->hw, info)->hw_value,
1, AR5K_TXKEYIX_INVALID,
antenna, flags, 0, 0);
if (ret)
@@ -1798,7 +1775,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
return 0;
err_unmap:
- dma_unmap_single(sc->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE);
+ dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE);
return ret;
}
@@ -1813,7 +1790,7 @@ int
ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
int ret;
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
struct ath5k_vif *avf = (void *)vif->drv_priv;
struct sk_buff *skb;
@@ -1829,11 +1806,9 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
goto out;
}
- ath5k_txbuf_free_skb(sc, avf->bbuf);
+ ath5k_txbuf_free_skb(ah, avf->bbuf);
avf->bbuf->skb = skb;
- ret = ath5k_beacon_setup(sc, avf->bbuf);
- if (ret)
- avf->bbuf->skb = NULL;
+ ret = ath5k_beacon_setup(ah, avf->bbuf);
out:
return ret;
}
@@ -1847,15 +1822,15 @@ out:
* or user context from ath5k_beacon_config.
*/
static void
-ath5k_beacon_send(struct ath5k_softc *sc)
+ath5k_beacon_send(struct ath5k_hw *ah)
{
- struct ath5k_hw *ah = sc->ah;
struct ieee80211_vif *vif;
struct ath5k_vif *avf;
struct ath5k_buf *bf;
struct sk_buff *skb;
+ int err;
- ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n");
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "in beacon_send\n");
/*
* Check if the previous beacon has gone out. If
@@ -1864,85 +1839,93 @@ ath5k_beacon_send(struct ath5k_softc *sc)
* indicate a problem and should not occur. If we
* miss too many consecutive beacons reset the device.
*/
- if (unlikely(ath5k_hw_num_tx_pending(ah, sc->bhalq) != 0)) {
- sc->bmisscount++;
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
- "missed %u consecutive beacons\n", sc->bmisscount);
- if (sc->bmisscount > 10) { /* NB: 10 is a guess */
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+ if (unlikely(ath5k_hw_num_tx_pending(ah, ah->bhalq) != 0)) {
+ ah->bmisscount++;
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
+ "missed %u consecutive beacons\n", ah->bmisscount);
+ if (ah->bmisscount > 10) { /* NB: 10 is a guess */
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
"stuck beacon time (%u missed)\n",
- sc->bmisscount);
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+ ah->bmisscount);
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"stuck beacon, resetting\n");
- ieee80211_queue_work(sc->hw, &sc->reset_work);
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
}
return;
}
- if (unlikely(sc->bmisscount != 0)) {
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+ if (unlikely(ah->bmisscount != 0)) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
"resume beacon xmit after %u misses\n",
- sc->bmisscount);
- sc->bmisscount = 0;
+ ah->bmisscount);
+ ah->bmisscount = 0;
}
- if ((sc->opmode == NL80211_IFTYPE_AP && sc->num_ap_vifs > 1) ||
- sc->opmode == NL80211_IFTYPE_MESH_POINT) {
+ if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs > 1) ||
+ ah->opmode == NL80211_IFTYPE_MESH_POINT) {
u64 tsf = ath5k_hw_get_tsf64(ah);
u32 tsftu = TSF_TO_TU(tsf);
- int slot = ((tsftu % sc->bintval) * ATH_BCBUF) / sc->bintval;
- vif = sc->bslot[(slot + 1) % ATH_BCBUF];
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+ int slot = ((tsftu % ah->bintval) * ATH_BCBUF) / ah->bintval;
+ vif = ah->bslot[(slot + 1) % ATH_BCBUF];
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
"tsf %llx tsftu %x intval %u slot %u vif %p\n",
- (unsigned long long)tsf, tsftu, sc->bintval, slot, vif);
+ (unsigned long long)tsf, tsftu, ah->bintval, slot, vif);
} else /* only one interface */
- vif = sc->bslot[0];
+ vif = ah->bslot[0];
if (!vif)
return;
avf = (void *)vif->drv_priv;
bf = avf->bbuf;
- if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION ||
- sc->opmode == NL80211_IFTYPE_MONITOR)) {
- ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL);
- return;
- }
/*
* Stop any current dma and put the new frame on the queue.
* This should never fail since we check above that no frames
* are still pending on the queue.
*/
- if (unlikely(ath5k_hw_stop_beacon_queue(ah, sc->bhalq))) {
- ATH5K_WARN(sc, "beacon queue %u didn't start/stop ?\n", sc->bhalq);
+ if (unlikely(ath5k_hw_stop_beacon_queue(ah, ah->bhalq))) {
+ ATH5K_WARN(ah, "beacon queue %u didn't start/stop ?\n", ah->bhalq);
/* NB: hw still stops DMA, so proceed */
}
/* refresh the beacon for AP or MESH mode */
- if (sc->opmode == NL80211_IFTYPE_AP ||
- sc->opmode == NL80211_IFTYPE_MESH_POINT)
- ath5k_beacon_update(sc->hw, vif);
+ if (ah->opmode == NL80211_IFTYPE_AP ||
+ ah->opmode == NL80211_IFTYPE_MESH_POINT) {
+ err = ath5k_beacon_update(ah->hw, vif);
+ if (err)
+ return;
+ }
- trace_ath5k_tx(sc, bf->skb, &sc->txqs[sc->bhalq]);
+ if (unlikely(bf->skb == NULL || ah->opmode == NL80211_IFTYPE_STATION ||
+ ah->opmode == NL80211_IFTYPE_MONITOR)) {
+ ATH5K_WARN(ah, "bf=%p bf_skb=%p\n", bf, bf->skb);
+ return;
+ }
+
+ trace_ath5k_tx(ah, bf->skb, &ah->txqs[ah->bhalq]);
- ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr);
- ath5k_hw_start_tx_dma(ah, sc->bhalq);
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
- sc->bhalq, (unsigned long long)bf->daddr, bf->desc);
+ ath5k_hw_set_txdp(ah, ah->bhalq, bf->daddr);
+ ath5k_hw_start_tx_dma(ah, ah->bhalq);
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
+ ah->bhalq, (unsigned long long)bf->daddr, bf->desc);
- skb = ieee80211_get_buffered_bc(sc->hw, vif);
+ skb = ieee80211_get_buffered_bc(ah->hw, vif);
while (skb) {
- ath5k_tx_queue(sc->hw, skb, sc->cabq);
- skb = ieee80211_get_buffered_bc(sc->hw, vif);
+ ath5k_tx_queue(ah->hw, skb, ah->cabq);
+
+ if (ah->cabq->txq_len >= ah->cabq->txq_max)
+ break;
+
+ skb = ieee80211_get_buffered_bc(ah->hw, vif);
}
- sc->bsent++;
+ ah->bsent++;
}
/**
* ath5k_beacon_update_timers - update beacon timers
*
- * @sc: struct ath5k_softc pointer we are operating on
+ * @ah: struct ath5k_hw pointer we are operating on
* @bc_tsf: the timestamp of the beacon. 0 to reset the TSF. -1 to perform a
* beacon timer update based on the current HW TSF.
*
@@ -1956,17 +1939,16 @@ ath5k_beacon_send(struct ath5k_softc *sc)
* function to have it all together in one place.
*/
void
-ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
+ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf)
{
- struct ath5k_hw *ah = sc->ah;
u32 nexttbtt, intval, hw_tu, bc_tu;
u64 hw_tsf;
- intval = sc->bintval & AR5K_BEACON_PERIOD;
- if (sc->opmode == NL80211_IFTYPE_AP && sc->num_ap_vifs > 1) {
+ intval = ah->bintval & AR5K_BEACON_PERIOD;
+ if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs > 1) {
intval /= ATH_BCBUF; /* staggered multi-bss beacons */
if (intval < 15)
- ATH5K_WARN(sc, "intval %u is too low, min 15\n",
+ ATH5K_WARN(ah, "intval %u is too low, min 15\n",
intval);
}
if (WARN_ON(!intval))
@@ -1979,7 +1961,7 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
hw_tsf = ath5k_hw_get_tsf64(ah);
hw_tu = TSF_TO_TU(hw_tsf);
-#define FUDGE AR5K_TUNE_SW_BEACON_RESP + 3
+#define FUDGE (AR5K_TUNE_SW_BEACON_RESP + 3)
/* We use FUDGE to make sure the next TBTT is ahead of the current TU.
* Since we later subtract AR5K_TUNE_SW_BEACON_RESP (10) in the timer
* configuration we need to make sure it is bigger than that. */
@@ -2005,7 +1987,7 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
* automatically update the TSF and then we need to reconfigure
* the timers.
*/
- ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
"need to wait for HW TSF sync\n");
return;
} else {
@@ -2020,7 +2002,7 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
}
#undef FUDGE
- sc->nexttbtt = nexttbtt;
+ ah->nexttbtt = nexttbtt;
intval |= AR5K_BEACON_ENA;
ath5k_hw_init_beacon(ah, nexttbtt, intval);
@@ -2030,20 +2012,20 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
* of this function
*/
if (bc_tsf == -1)
- ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
"reconfigured timers based on HW TSF\n");
else if (bc_tsf == 0)
- ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
"reset HW TSF and timers\n");
else
- ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
"updated timers based on beacon TSF\n");
- ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
"bc_tsf %llx hw_tsf %llx bc_tu %u hw_tu %u nexttbtt %u\n",
(unsigned long long) bc_tsf,
(unsigned long long) hw_tsf, bc_tu, hw_tu, nexttbtt);
- ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "intval %u %s %s\n",
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "intval %u %s %s\n",
intval & AR5K_BEACON_PERIOD,
intval & AR5K_BEACON_ENA ? "AR5K_BEACON_ENA" : "",
intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : "");
@@ -2052,22 +2034,21 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
/**
* ath5k_beacon_config - Configure the beacon queues and interrupts
*
- * @sc: struct ath5k_softc pointer we are operating on
+ * @ah: struct ath5k_hw pointer we are operating on
*
* In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
* interrupts to detect TSF updates only.
*/
void
-ath5k_beacon_config(struct ath5k_softc *sc)
+ath5k_beacon_config(struct ath5k_hw *ah)
{
- struct ath5k_hw *ah = sc->ah;
unsigned long flags;
- spin_lock_irqsave(&sc->block, flags);
- sc->bmisscount = 0;
- sc->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
+ spin_lock_irqsave(&ah->block, flags);
+ ah->bmisscount = 0;
+ ah->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
- if (sc->enable_beacon) {
+ if (ah->enable_beacon) {
/*
* In IBSS mode we use a self-linked tx descriptor and let the
* hardware send the beacons automatically. We have to load it
@@ -2075,50 +2056,50 @@ ath5k_beacon_config(struct ath5k_softc *sc)
* We use the SWBA interrupt only to keep track of the beacon
* timers in order to detect automatic TSF updates.
*/
- ath5k_beaconq_config(sc);
+ ath5k_beaconq_config(ah);
- sc->imask |= AR5K_INT_SWBA;
+ ah->imask |= AR5K_INT_SWBA;
- if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+ if (ah->opmode == NL80211_IFTYPE_ADHOC) {
if (ath5k_hw_hasveol(ah))
- ath5k_beacon_send(sc);
+ ath5k_beacon_send(ah);
} else
- ath5k_beacon_update_timers(sc, -1);
+ ath5k_beacon_update_timers(ah, -1);
} else {
- ath5k_hw_stop_beacon_queue(sc->ah, sc->bhalq);
+ ath5k_hw_stop_beacon_queue(ah, ah->bhalq);
}
- ath5k_hw_set_imr(ah, sc->imask);
+ ath5k_hw_set_imr(ah, ah->imask);
mmiowb();
- spin_unlock_irqrestore(&sc->block, flags);
+ spin_unlock_irqrestore(&ah->block, flags);
}
static void ath5k_tasklet_beacon(unsigned long data)
{
- struct ath5k_softc *sc = (struct ath5k_softc *) data;
+ struct ath5k_hw *ah = (struct ath5k_hw *) data;
/*
* Software beacon alert--time to send a beacon.
*
* In IBSS mode we use this interrupt just to
* keep track of the next TBTT (target beacon
- * transmission time) in order to detect wether
+ * transmission time) in order to detect whether
* automatic TSF updates happened.
*/
- if (sc->opmode == NL80211_IFTYPE_ADHOC) {
- /* XXX: only if VEOL suppported */
- u64 tsf = ath5k_hw_get_tsf64(sc->ah);
- sc->nexttbtt += sc->bintval;
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+ if (ah->opmode == NL80211_IFTYPE_ADHOC) {
+ /* XXX: only if VEOL supported */
+ u64 tsf = ath5k_hw_get_tsf64(ah);
+ ah->nexttbtt += ah->bintval;
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
"SWBA nexttbtt: %x hw_tu: %x "
"TSF: %llx\n",
- sc->nexttbtt,
+ ah->nexttbtt,
TSF_TO_TU(tsf),
(unsigned long long) tsf);
} else {
- spin_lock(&sc->block);
- ath5k_beacon_send(sc);
- spin_unlock(&sc->block);
+ spin_lock(&ah->block);
+ ath5k_beacon_send(ah);
+ spin_unlock(&ah->block);
}
}
@@ -2135,12 +2116,12 @@ ath5k_intr_calibration_poll(struct ath5k_hw *ah)
/* run ANI only when full calibration is not active */
ah->ah_cal_next_ani = jiffies +
msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
- tasklet_schedule(&ah->ah_sc->ani_tasklet);
+ tasklet_schedule(&ah->ani_tasklet);
} else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
ah->ah_cal_next_full = jiffies +
msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
- tasklet_schedule(&ah->ah_sc->calib);
+ tasklet_schedule(&ah->calib);
}
/* we could use SWI to generate enough interrupts to meet our
* calibration interval requirements, if necessary:
@@ -2148,44 +2129,43 @@ ath5k_intr_calibration_poll(struct ath5k_hw *ah)
}
static void
-ath5k_schedule_rx(struct ath5k_softc *sc)
+ath5k_schedule_rx(struct ath5k_hw *ah)
{
- sc->rx_pending = true;
- tasklet_schedule(&sc->rxtq);
+ ah->rx_pending = true;
+ tasklet_schedule(&ah->rxtq);
}
static void
-ath5k_schedule_tx(struct ath5k_softc *sc)
+ath5k_schedule_tx(struct ath5k_hw *ah)
{
- sc->tx_pending = true;
- tasklet_schedule(&sc->txtq);
+ ah->tx_pending = true;
+ tasklet_schedule(&ah->txtq);
}
-irqreturn_t
+static irqreturn_t
ath5k_intr(int irq, void *dev_id)
{
- struct ath5k_softc *sc = dev_id;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = dev_id;
enum ath5k_int status;
unsigned int counter = 1000;
- if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
+ if (unlikely(test_bit(ATH_STAT_INVALID, ah->status) ||
((ath5k_get_bus_type(ah) != ATH_AHB) &&
!ath5k_hw_is_intr_pending(ah))))
return IRQ_NONE;
do {
ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */
- ATH5K_DBG(sc, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n",
- status, sc->imask);
+ ATH5K_DBG(ah, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n",
+ status, ah->imask);
if (unlikely(status & AR5K_INT_FATAL)) {
/*
* Fatal errors are unrecoverable.
* Typically these are caused by DMA errors.
*/
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"fatal int, resetting\n");
- ieee80211_queue_work(sc->hw, &sc->reset_work);
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
} else if (unlikely(status & AR5K_INT_RXORN)) {
/*
* Receive buffers are full. Either the bus is busy or
@@ -2196,45 +2176,44 @@ ath5k_intr(int irq, void *dev_id)
* We don't know exactly which versions need a reset -
* this guess is copied from the HAL.
*/
- sc->stats.rxorn_intr++;
+ ah->stats.rxorn_intr++;
if (ah->ah_mac_srev < AR5K_SREV_AR5212) {
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"rx overrun, resetting\n");
- ieee80211_queue_work(sc->hw, &sc->reset_work);
- }
- else
- ath5k_schedule_rx(sc);
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
+ } else
+ ath5k_schedule_rx(ah);
} else {
- if (status & AR5K_INT_SWBA) {
- tasklet_hi_schedule(&sc->beacontq);
- }
+ if (status & AR5K_INT_SWBA)
+ tasklet_hi_schedule(&ah->beacontq);
+
if (status & AR5K_INT_RXEOL) {
/*
* NB: the hardware should re-read the link when
* RXE bit is written, but it doesn't work at
* least on older hardware revs.
*/
- sc->stats.rxeol_intr++;
+ ah->stats.rxeol_intr++;
}
if (status & AR5K_INT_TXURN) {
/* bump tx trigger level */
ath5k_hw_update_tx_triglevel(ah, true);
}
if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
- ath5k_schedule_rx(sc);
+ ath5k_schedule_rx(ah);
if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
| AR5K_INT_TXERR | AR5K_INT_TXEOL))
- ath5k_schedule_tx(sc);
+ ath5k_schedule_tx(ah);
if (status & AR5K_INT_BMISS) {
/* TODO */
}
if (status & AR5K_INT_MIB) {
- sc->stats.mib_intr++;
+ ah->stats.mib_intr++;
ath5k_hw_update_mib_counters(ah);
ath5k_ani_mib_intr(ah);
}
if (status & AR5K_INT_GPIO)
- tasklet_schedule(&sc->rf_kill.toggleq);
+ tasklet_schedule(&ah->rf_kill.toggleq);
}
@@ -2243,11 +2222,11 @@ ath5k_intr(int irq, void *dev_id)
} while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
- if (sc->rx_pending || sc->tx_pending)
- ath5k_set_current_imask(sc);
+ if (ah->rx_pending || ah->tx_pending)
+ ath5k_set_current_imask(ah);
if (unlikely(!counter))
- ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
+ ATH5K_WARN(ah, "too many interrupts, giving up for now\n");
ath5k_intr_calibration_poll(ah);
@@ -2261,28 +2240,27 @@ ath5k_intr(int irq, void *dev_id)
static void
ath5k_tasklet_calibrate(unsigned long data)
{
- struct ath5k_softc *sc = (void *)data;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = (void *)data;
/* Only full calibration for now */
ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
- ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
- ieee80211_frequency_to_channel(sc->curchan->center_freq),
- sc->curchan->hw_value);
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
+ ieee80211_frequency_to_channel(ah->curchan->center_freq),
+ ah->curchan->hw_value);
if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
/*
* Rfgain is out of bounds, reset the chip
* to load new gain values.
*/
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n");
- ieee80211_queue_work(sc->hw, &sc->reset_work);
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "calibration, resetting\n");
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
}
- if (ath5k_hw_phy_calibrate(ah, sc->curchan))
- ATH5K_ERR(sc, "calibration of channel %u failed\n",
+ if (ath5k_hw_phy_calibrate(ah, ah->curchan))
+ ATH5K_ERR(ah, "calibration of channel %u failed\n",
ieee80211_frequency_to_channel(
- sc->curchan->center_freq));
+ ah->curchan->center_freq));
/* Noise floor calibration interrupts rx/tx path while I/Q calibration
* doesn't.
@@ -2301,8 +2279,7 @@ ath5k_tasklet_calibrate(unsigned long data)
static void
ath5k_tasklet_ani(unsigned long data)
{
- struct ath5k_softc *sc = (void *)data;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = (void *)data;
ah->ah_cal_mask |= AR5K_CALIBRATION_ANI;
ath5k_ani_calibration(ah);
@@ -2313,21 +2290,21 @@ ath5k_tasklet_ani(unsigned long data)
static void
ath5k_tx_complete_poll_work(struct work_struct *work)
{
- struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
+ struct ath5k_hw *ah = container_of(work, struct ath5k_hw,
tx_complete_work.work);
struct ath5k_txq *txq;
int i;
bool needreset = false;
- mutex_lock(&sc->lock);
+ mutex_lock(&ah->lock);
- for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) {
- if (sc->txqs[i].setup) {
- txq = &sc->txqs[i];
+ for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) {
+ if (ah->txqs[i].setup) {
+ txq = &ah->txqs[i];
spin_lock_bh(&txq->lock);
if (txq->txq_len > 1) {
if (txq->txq_poll_mark) {
- ATH5K_DBG(sc, ATH5K_DEBUG_XMIT,
+ ATH5K_DBG(ah, ATH5K_DEBUG_XMIT,
"TX queue stuck %d\n",
txq->qnum);
needreset = true;
@@ -2343,14 +2320,14 @@ ath5k_tx_complete_poll_work(struct work_struct *work)
}
if (needreset) {
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"TX queues stuck, resetting\n");
- ath5k_reset(sc, NULL, true);
+ ath5k_reset(ah, NULL, true);
}
- mutex_unlock(&sc->lock);
+ mutex_unlock(&ah->lock);
- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
+ ieee80211_queue_delayed_work(ah->hw, &ah->tx_complete_work,
msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
}
@@ -2359,16 +2336,16 @@ ath5k_tx_complete_poll_work(struct work_struct *work)
* Initialization routines *
\*************************/
-int
-ath5k_init_softc(struct ath5k_softc *sc, const struct ath_bus_ops *bus_ops)
+int __devinit
+ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
{
- struct ieee80211_hw *hw = sc->hw;
+ struct ieee80211_hw *hw = ah->hw;
struct ath_common *common;
int ret;
int csz;
/* Initialize driver private data */
- SET_IEEE80211_DEV(hw, sc->dev);
+ SET_IEEE80211_DEV(hw, ah->dev);
hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM |
@@ -2391,39 +2368,30 @@ ath5k_init_softc(struct ath5k_softc *sc, const struct ath_bus_ops *bus_ops)
* Mark the device as detached to avoid processing
* interrupts until setup is complete.
*/
- __set_bit(ATH_STAT_INVALID, sc->status);
+ __set_bit(ATH_STAT_INVALID, ah->status);
- sc->opmode = NL80211_IFTYPE_STATION;
- sc->bintval = 1000;
- mutex_init(&sc->lock);
- spin_lock_init(&sc->rxbuflock);
- spin_lock_init(&sc->txbuflock);
- spin_lock_init(&sc->block);
- spin_lock_init(&sc->irqlock);
+ ah->opmode = NL80211_IFTYPE_STATION;
+ ah->bintval = 1000;
+ mutex_init(&ah->lock);
+ spin_lock_init(&ah->rxbuflock);
+ spin_lock_init(&ah->txbuflock);
+ spin_lock_init(&ah->block);
+ spin_lock_init(&ah->irqlock);
/* Setup interrupt handler */
- ret = request_irq(sc->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
+ ret = request_irq(ah->irq, ath5k_intr, IRQF_SHARED, "ath", ah);
if (ret) {
- ATH5K_ERR(sc, "request_irq failed\n");
+ ATH5K_ERR(ah, "request_irq failed\n");
goto err;
}
- /* If we passed the test, malloc an ath5k_hw struct */
- sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
- if (!sc->ah) {
- ret = -ENOMEM;
- ATH5K_ERR(sc, "out of memory\n");
- goto err_irq;
- }
-
- sc->ah->ah_sc = sc;
- sc->ah->ah_iobase = sc->iobase;
- common = ath5k_hw_common(sc->ah);
+ common = ath5k_hw_common(ah);
common->ops = &ath5k_common_ops;
common->bus_ops = bus_ops;
- common->ah = sc->ah;
+ common->ah = ah;
common->hw = hw;
- common->priv = sc;
+ common->priv = ah;
+ common->clockrate = 40;
/*
* Cache line size is used to size and align various
@@ -2435,12 +2403,12 @@ ath5k_init_softc(struct ath5k_softc *sc, const struct ath_bus_ops *bus_ops)
spin_lock_init(&common->cc_lock);
/* Initialize device */
- ret = ath5k_hw_init(sc);
+ ret = ath5k_hw_init(ah);
if (ret)
- goto err_free_ah;
+ goto err_irq;
/* set up multi-rate retry capabilities */
- if (sc->ah->ah_version == AR5K_AR5212) {
+ if (ah->ah_version == AR5K_AR5212) {
hw->max_rates = 4;
hw->max_rate_tries = max(AR5K_INIT_RETRY_SHORT,
AR5K_INIT_RETRY_LONG);
@@ -2453,77 +2421,74 @@ ath5k_init_softc(struct ath5k_softc *sc, const struct ath_bus_ops *bus_ops)
if (ret)
goto err_ah;
- ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n",
- ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev),
- sc->ah->ah_mac_srev,
- sc->ah->ah_phy_revision);
+ ATH5K_INFO(ah, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n",
+ ath5k_chip_name(AR5K_VERSION_MAC, ah->ah_mac_srev),
+ ah->ah_mac_srev,
+ ah->ah_phy_revision);
- if (!sc->ah->ah_single_chip) {
+ if (!ah->ah_single_chip) {
/* Single chip radio (!RF5111) */
- if (sc->ah->ah_radio_5ghz_revision &&
- !sc->ah->ah_radio_2ghz_revision) {
+ if (ah->ah_radio_5ghz_revision &&
+ !ah->ah_radio_2ghz_revision) {
/* No 5GHz support -> report 2GHz radio */
if (!test_bit(AR5K_MODE_11A,
- sc->ah->ah_capabilities.cap_mode)) {
- ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
+ ah->ah_capabilities.cap_mode)) {
+ ATH5K_INFO(ah, "RF%s 2GHz radio found (0x%x)\n",
ath5k_chip_name(AR5K_VERSION_RAD,
- sc->ah->ah_radio_5ghz_revision),
- sc->ah->ah_radio_5ghz_revision);
+ ah->ah_radio_5ghz_revision),
+ ah->ah_radio_5ghz_revision);
/* No 2GHz support (5110 and some
- * 5Ghz only cards) -> report 5Ghz radio */
+ * 5GHz only cards) -> report 5GHz radio */
} else if (!test_bit(AR5K_MODE_11B,
- sc->ah->ah_capabilities.cap_mode)) {
- ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
+ ah->ah_capabilities.cap_mode)) {
+ ATH5K_INFO(ah, "RF%s 5GHz radio found (0x%x)\n",
ath5k_chip_name(AR5K_VERSION_RAD,
- sc->ah->ah_radio_5ghz_revision),
- sc->ah->ah_radio_5ghz_revision);
+ ah->ah_radio_5ghz_revision),
+ ah->ah_radio_5ghz_revision);
/* Multiband radio */
} else {
- ATH5K_INFO(sc, "RF%s multiband radio found"
+ ATH5K_INFO(ah, "RF%s multiband radio found"
" (0x%x)\n",
ath5k_chip_name(AR5K_VERSION_RAD,
- sc->ah->ah_radio_5ghz_revision),
- sc->ah->ah_radio_5ghz_revision);
+ ah->ah_radio_5ghz_revision),
+ ah->ah_radio_5ghz_revision);
}
}
/* Multi chip radio (RF5111 - RF2111) ->
* report both 2GHz/5GHz radios */
- else if (sc->ah->ah_radio_5ghz_revision &&
- sc->ah->ah_radio_2ghz_revision){
- ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
+ else if (ah->ah_radio_5ghz_revision &&
+ ah->ah_radio_2ghz_revision) {
+ ATH5K_INFO(ah, "RF%s 5GHz radio found (0x%x)\n",
ath5k_chip_name(AR5K_VERSION_RAD,
- sc->ah->ah_radio_5ghz_revision),
- sc->ah->ah_radio_5ghz_revision);
- ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
+ ah->ah_radio_5ghz_revision),
+ ah->ah_radio_5ghz_revision);
+ ATH5K_INFO(ah, "RF%s 2GHz radio found (0x%x)\n",
ath5k_chip_name(AR5K_VERSION_RAD,
- sc->ah->ah_radio_2ghz_revision),
- sc->ah->ah_radio_2ghz_revision);
+ ah->ah_radio_2ghz_revision),
+ ah->ah_radio_2ghz_revision);
}
}
- ath5k_debug_init_device(sc);
+ ath5k_debug_init_device(ah);
/* ready to process interrupts */
- __clear_bit(ATH_STAT_INVALID, sc->status);
+ __clear_bit(ATH_STAT_INVALID, ah->status);
return 0;
err_ah:
- ath5k_hw_deinit(sc->ah);
-err_free_ah:
- kfree(sc->ah);
+ ath5k_hw_deinit(ah);
err_irq:
- free_irq(sc->irq, sc);
+ free_irq(ah->irq, ah);
err:
return ret;
}
static int
-ath5k_stop_locked(struct ath5k_softc *sc)
+ath5k_stop_locked(struct ath5k_hw *ah)
{
- struct ath5k_hw *ah = sc->ah;
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n",
- test_bit(ATH_STAT_INVALID, sc->status));
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "invalid %u\n",
+ test_bit(ATH_STAT_INVALID, ah->status));
/*
* Shutdown the hardware and driver:
@@ -2540,37 +2505,36 @@ ath5k_stop_locked(struct ath5k_softc *sc)
* Note that some of this work is not possible if the
* hardware is gone (invalid).
*/
- ieee80211_stop_queues(sc->hw);
+ ieee80211_stop_queues(ah->hw);
- if (!test_bit(ATH_STAT_INVALID, sc->status)) {
- ath5k_led_off(sc);
+ if (!test_bit(ATH_STAT_INVALID, ah->status)) {
+ ath5k_led_off(ah);
ath5k_hw_set_imr(ah, 0);
- synchronize_irq(sc->irq);
- ath5k_rx_stop(sc);
+ synchronize_irq(ah->irq);
+ ath5k_rx_stop(ah);
ath5k_hw_dma_stop(ah);
- ath5k_drain_tx_buffs(sc);
+ ath5k_drain_tx_buffs(ah);
ath5k_hw_phy_disable(ah);
}
return 0;
}
-int
-ath5k_init_hw(struct ath5k_softc *sc)
+int ath5k_start(struct ieee80211_hw *hw)
{
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = hw->priv;
struct ath_common *common = ath5k_hw_common(ah);
int ret, i;
- mutex_lock(&sc->lock);
+ mutex_lock(&ah->lock);
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode);
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "mode %d\n", ah->opmode);
/*
* Stop anything previously setup. This is safe
* no matter this is the first time through or not.
*/
- ath5k_stop_locked(sc);
+ ath5k_stop_locked(ah);
/*
* The basic interface to setting the hardware in a good
@@ -2579,12 +2543,12 @@ ath5k_init_hw(struct ath5k_softc *sc)
* be followed by initialization of the appropriate bits
* and then setup of the interrupt mask.
*/
- sc->curchan = sc->hw->conf.channel;
- sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
+ ah->curchan = ah->hw->conf.channel;
+ ah->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
- ret = ath5k_reset(sc, NULL, false);
+ ret = ath5k_reset(ah, NULL, false);
if (ret)
goto done;
@@ -2601,29 +2565,29 @@ ath5k_init_hw(struct ath5k_softc *sc)
* rate */
ah->ah_ack_bitrate_high = true;
- for (i = 0; i < ARRAY_SIZE(sc->bslot); i++)
- sc->bslot[i] = NULL;
+ for (i = 0; i < ARRAY_SIZE(ah->bslot); i++)
+ ah->bslot[i] = NULL;
ret = 0;
done:
mmiowb();
- mutex_unlock(&sc->lock);
+ mutex_unlock(&ah->lock);
- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
+ ieee80211_queue_delayed_work(ah->hw, &ah->tx_complete_work,
msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
return ret;
}
-static void stop_tasklets(struct ath5k_softc *sc)
+static void ath5k_stop_tasklets(struct ath5k_hw *ah)
{
- sc->rx_pending = false;
- sc->tx_pending = false;
- tasklet_kill(&sc->rxtq);
- tasklet_kill(&sc->txtq);
- tasklet_kill(&sc->calib);
- tasklet_kill(&sc->beacontq);
- tasklet_kill(&sc->ani_tasklet);
+ ah->rx_pending = false;
+ ah->tx_pending = false;
+ tasklet_kill(&ah->rxtq);
+ tasklet_kill(&ah->txtq);
+ tasklet_kill(&ah->calib);
+ tasklet_kill(&ah->beacontq);
+ tasklet_kill(&ah->ani_tasklet);
}
/*
@@ -2632,14 +2596,14 @@ static void stop_tasklets(struct ath5k_softc *sc)
* if another thread does a system call and the thread doing the
* stop is preempted).
*/
-int
-ath5k_stop_hw(struct ath5k_softc *sc)
+void ath5k_stop(struct ieee80211_hw *hw)
{
+ struct ath5k_hw *ah = hw->priv;
int ret;
- mutex_lock(&sc->lock);
- ret = ath5k_stop_locked(sc);
- if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) {
+ mutex_lock(&ah->lock);
+ ret = ath5k_stop_locked(ah);
+ if (ret == 0 && !test_bit(ATH_STAT_INVALID, ah->status)) {
/*
* Don't set the card in full sleep mode!
*
@@ -2660,82 +2624,78 @@ ath5k_stop_hw(struct ath5k_softc *sc)
* and Sam's HAL do anyway). Instead Perform a full reset
* on the device (same as initial state after attach) and
* leave it idle (keep MAC/BB on warm reset) */
- ret = ath5k_hw_on_hold(sc->ah);
+ ret = ath5k_hw_on_hold(ah);
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"putting device to sleep\n");
}
mmiowb();
- mutex_unlock(&sc->lock);
-
- stop_tasklets(sc);
+ mutex_unlock(&ah->lock);
- cancel_delayed_work_sync(&sc->tx_complete_work);
+ ath5k_stop_tasklets(ah);
- ath5k_rfkill_hw_stop(sc->ah);
+ cancel_delayed_work_sync(&ah->tx_complete_work);
- return ret;
+ ath5k_rfkill_hw_stop(ah);
}
/*
* Reset the hardware. If chan is not NULL, then also pause rx/tx
* and change to the given channel.
*
- * This should be called with sc->lock.
+ * This should be called with ah->lock.
*/
static int
-ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan,
+ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
bool skip_pcu)
{
- struct ath5k_hw *ah = sc->ah;
struct ath_common *common = ath5k_hw_common(ah);
int ret, ani_mode;
bool fast;
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
ath5k_hw_set_imr(ah, 0);
- synchronize_irq(sc->irq);
- stop_tasklets(sc);
+ synchronize_irq(ah->irq);
+ ath5k_stop_tasklets(ah);
/* Save ani mode and disable ANI during
* reset. If we don't we might get false
* PHY error interrupts. */
- ani_mode = ah->ah_sc->ani_state.ani_mode;
+ ani_mode = ah->ani_state.ani_mode;
ath5k_ani_init(ah, ATH5K_ANI_MODE_OFF);
/* We are going to empty hw queues
* so we should also free any remaining
* tx buffers */
- ath5k_drain_tx_buffs(sc);
+ ath5k_drain_tx_buffs(ah);
if (chan)
- sc->curchan = chan;
+ ah->curchan = chan;
fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0;
- ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, fast,
- skip_pcu);
+ ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu);
if (ret) {
- ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret);
+ ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret);
goto err;
}
- ret = ath5k_rx_start(sc);
+ ret = ath5k_rx_start(ah);
if (ret) {
- ATH5K_ERR(sc, "can't start recv logic\n");
+ ATH5K_ERR(ah, "can't start recv logic\n");
goto err;
}
ath5k_ani_init(ah, ani_mode);
- ah->ah_cal_next_full = jiffies;
+ ah->ah_cal_next_full = jiffies + msecs_to_jiffies(100);
ah->ah_cal_next_ani = jiffies;
ah->ah_cal_next_nf = jiffies;
ewma_init(&ah->ah_beacon_rssi_avg, 1024, 8);
/* clear survey data and cycle counters */
- memset(&sc->survey, 0, sizeof(sc->survey));
+ memset(&ah->survey, 0, sizeof(ah->survey));
spin_lock_bh(&common->cc_lock);
ath_hw_cycle_counters_update(common);
memset(&common->cc_survey, 0, sizeof(common->cc_survey));
@@ -2751,12 +2711,12 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan,
*
* XXX needed?
*/
-/* ath5k_chan_change(sc, c); */
+/* ath5k_chan_change(ah, c); */
- ath5k_beacon_config(sc);
+ ath5k_beacon_config(ah);
/* intrs are enabled by ath5k_beacon_config */
- ieee80211_wake_queues(sc->hw);
+ ieee80211_wake_queues(ah->hw);
return 0;
err:
@@ -2765,20 +2725,19 @@ err:
static void ath5k_reset_work(struct work_struct *work)
{
- struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
+ struct ath5k_hw *ah = container_of(work, struct ath5k_hw,
reset_work);
- mutex_lock(&sc->lock);
- ath5k_reset(sc, NULL, true);
- mutex_unlock(&sc->lock);
+ mutex_lock(&ah->lock);
+ ath5k_reset(ah, NULL, true);
+ mutex_unlock(&ah->lock);
}
-static int
+static int __devinit
ath5k_init(struct ieee80211_hw *hw)
{
- struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = hw->priv;
struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
struct ath5k_txq *txq;
u8 mac[ETH_ALEN] = {};
@@ -2797,26 +2756,26 @@ ath5k_init(struct ieee80211_hw *hw)
if (ret < 0)
goto err;
if (ret > 0)
- __set_bit(ATH_STAT_MRRETRY, sc->status);
+ __set_bit(ATH_STAT_MRRETRY, ah->status);
/*
* Collect the channel list. The 802.11 layer
- * is resposible for filtering this list based
+ * is responsible for filtering this list based
* on settings like the phy mode and regulatory
* domain restrictions.
*/
ret = ath5k_setup_bands(hw);
if (ret) {
- ATH5K_ERR(sc, "can't get channels\n");
+ ATH5K_ERR(ah, "can't get channels\n");
goto err;
}
/*
* Allocate tx+rx descriptors and populate the lists.
*/
- ret = ath5k_desc_alloc(sc);
+ ret = ath5k_desc_alloc(ah);
if (ret) {
- ATH5K_ERR(sc, "can't allocate descriptors\n");
+ ATH5K_ERR(ah, "can't allocate descriptors\n");
goto err;
}
@@ -2828,14 +2787,14 @@ ath5k_init(struct ieee80211_hw *hw)
*/
ret = ath5k_beaconq_setup(ah);
if (ret < 0) {
- ATH5K_ERR(sc, "can't setup a beacon xmit queue\n");
+ ATH5K_ERR(ah, "can't setup a beacon xmit queue\n");
goto err_desc;
}
- sc->bhalq = ret;
- sc->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0);
- if (IS_ERR(sc->cabq)) {
- ATH5K_ERR(sc, "can't setup cab queue\n");
- ret = PTR_ERR(sc->cabq);
+ ah->bhalq = ret;
+ ah->cabq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_CAB, 0);
+ if (IS_ERR(ah->cabq)) {
+ ATH5K_ERR(ah, "can't setup cab queue\n");
+ ret = PTR_ERR(ah->cabq);
goto err_bhal;
}
@@ -2844,97 +2803,96 @@ ath5k_init(struct ieee80211_hw *hw)
if (ah->ah_capabilities.cap_queues.q_tx_num >= 6) {
/* This order matches mac80211's queue priority, so we can
* directly use the mac80211 queue number without any mapping */
- txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VO);
+ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VO);
if (IS_ERR(txq)) {
- ATH5K_ERR(sc, "can't setup xmit queue\n");
+ ATH5K_ERR(ah, "can't setup xmit queue\n");
ret = PTR_ERR(txq);
goto err_queues;
}
- txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VI);
+ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VI);
if (IS_ERR(txq)) {
- ATH5K_ERR(sc, "can't setup xmit queue\n");
+ ATH5K_ERR(ah, "can't setup xmit queue\n");
ret = PTR_ERR(txq);
goto err_queues;
}
- txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE);
+ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE);
if (IS_ERR(txq)) {
- ATH5K_ERR(sc, "can't setup xmit queue\n");
+ ATH5K_ERR(ah, "can't setup xmit queue\n");
ret = PTR_ERR(txq);
goto err_queues;
}
- txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
+ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
if (IS_ERR(txq)) {
- ATH5K_ERR(sc, "can't setup xmit queue\n");
+ ATH5K_ERR(ah, "can't setup xmit queue\n");
ret = PTR_ERR(txq);
goto err_queues;
}
hw->queues = 4;
} else {
/* older hardware (5210) can only support one data queue */
- txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE);
+ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE);
if (IS_ERR(txq)) {
- ATH5K_ERR(sc, "can't setup xmit queue\n");
+ ATH5K_ERR(ah, "can't setup xmit queue\n");
ret = PTR_ERR(txq);
goto err_queues;
}
hw->queues = 1;
}
- tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
- tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
- tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
- tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
- tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
+ tasklet_init(&ah->rxtq, ath5k_tasklet_rx, (unsigned long)ah);
+ tasklet_init(&ah->txtq, ath5k_tasklet_tx, (unsigned long)ah);
+ tasklet_init(&ah->calib, ath5k_tasklet_calibrate, (unsigned long)ah);
+ tasklet_init(&ah->beacontq, ath5k_tasklet_beacon, (unsigned long)ah);
+ tasklet_init(&ah->ani_tasklet, ath5k_tasklet_ani, (unsigned long)ah);
- INIT_WORK(&sc->reset_work, ath5k_reset_work);
- INIT_DELAYED_WORK(&sc->tx_complete_work, ath5k_tx_complete_poll_work);
+ INIT_WORK(&ah->reset_work, ath5k_reset_work);
+ INIT_DELAYED_WORK(&ah->tx_complete_work, ath5k_tx_complete_poll_work);
ret = ath5k_hw_common(ah)->bus_ops->eeprom_read_mac(ah, mac);
if (ret) {
- ATH5K_ERR(sc, "unable to read address from EEPROM\n");
+ ATH5K_ERR(ah, "unable to read address from EEPROM\n");
goto err_queues;
}
SET_IEEE80211_PERM_ADDR(hw, mac);
- memcpy(&sc->lladdr, mac, ETH_ALEN);
/* All MAC address bits matter for ACKs */
- ath5k_update_bssid_mask_and_opmode(sc, NULL);
+ ath5k_update_bssid_mask_and_opmode(ah, NULL);
regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
if (ret) {
- ATH5K_ERR(sc, "can't initialize regulatory system\n");
+ ATH5K_ERR(ah, "can't initialize regulatory system\n");
goto err_queues;
}
ret = ieee80211_register_hw(hw);
if (ret) {
- ATH5K_ERR(sc, "can't register ieee80211 hw\n");
+ ATH5K_ERR(ah, "can't register ieee80211 hw\n");
goto err_queues;
}
if (!ath_is_world_regd(regulatory))
regulatory_hint(hw->wiphy, regulatory->alpha2);
- ath5k_init_leds(sc);
+ ath5k_init_leds(ah);
- ath5k_sysfs_register(sc);
+ ath5k_sysfs_register(ah);
return 0;
err_queues:
- ath5k_txq_release(sc);
+ ath5k_txq_release(ah);
err_bhal:
- ath5k_hw_release_tx_queue(ah, sc->bhalq);
+ ath5k_hw_release_tx_queue(ah, ah->bhalq);
err_desc:
- ath5k_desc_free(sc);
+ ath5k_desc_free(ah);
err:
return ret;
}
void
-ath5k_deinit_softc(struct ath5k_softc *sc)
+ath5k_deinit_ah(struct ath5k_hw *ah)
{
- struct ieee80211_hw *hw = sc->hw;
+ struct ieee80211_hw *hw = ah->hw;
/*
* NB: the order of these is important:
@@ -2950,23 +2908,23 @@ ath5k_deinit_softc(struct ath5k_softc *sc)
* Other than that, it's straightforward...
*/
ieee80211_unregister_hw(hw);
- ath5k_desc_free(sc);
- ath5k_txq_release(sc);
- ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
- ath5k_unregister_leds(sc);
+ ath5k_desc_free(ah);
+ ath5k_txq_release(ah);
+ ath5k_hw_release_tx_queue(ah, ah->bhalq);
+ ath5k_unregister_leds(ah);
- ath5k_sysfs_unregister(sc);
+ ath5k_sysfs_unregister(ah);
/*
* NB: can't reclaim these until after ieee80211_ifdetach
* returns because we'll get called back to reclaim node
* state and potentially want to use them.
*/
- ath5k_hw_deinit(sc->ah);
- free_irq(sc->irq, sc);
+ ath5k_hw_deinit(ah);
+ free_irq(ah->irq, ah);
}
bool
-ath_any_vif_assoc(struct ath5k_softc *sc)
+ath5k_any_vif_assoc(struct ath5k_hw *ah)
{
struct ath5k_vif_iter_data iter_data;
iter_data.hw_macaddr = NULL;
@@ -2974,16 +2932,15 @@ ath_any_vif_assoc(struct ath5k_softc *sc)
iter_data.need_set_hw_addr = false;
iter_data.found_active = true;
- ieee80211_iterate_active_interfaces_atomic(sc->hw, ath5k_vif_iter,
+ ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
&iter_data);
return iter_data.any_assoc;
}
void
-set_beacon_filter(struct ieee80211_hw *hw, bool enable)
+ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable)
{
- struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = hw->priv;
u32 rfilt;
rfilt = ath5k_hw_get_rx_filter(ah);
if (enable)
@@ -2991,5 +2948,5 @@ set_beacon_filter(struct ieee80211_hw *hw, bool enable)
else
rfilt &= ~AR5K_RX_FILTER_BEACON;
ath5k_hw_set_rx_filter(ah, rfilt);
- sc->filter_flags = rfilt;
+ ah->filter_flags = rfilt;
}
diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h
index b294f3305011..6c94c7ff2350 100644
--- a/drivers/net/wireless/ath/ath5k/base.h
+++ b/drivers/net/wireless/ath/ath5k/base.h
@@ -38,29 +38,27 @@
/*
* Definitions for the Atheros Wireless LAN controller driver.
*/
-#ifndef _DEV_ATH_ATHVAR_H
-#define _DEV_ATH_ATHVAR_H
-
-#include <linux/interrupt.h>
-#include <linux/list.h>
-#include <linux/wireless.h>
-#include <linux/if_ether.h>
-#include <linux/leds.h>
-#include <linux/rfkill.h>
-#include <linux/workqueue.h>
-
-#include "ath5k.h"
-#include "debug.h"
-#include "ani.h"
-
-#include "../regd.h"
-#include "../ath.h"
+#ifndef _DEV_ATH5K_BASE_H
+#define _DEV_ATH5K_BASE_H
+
+struct ieee80211_vif;
+struct ieee80211_hw;
+struct ath5k_hw;
+struct ath5k_txq;
+struct ieee80211_channel;
+struct ath_bus_ops;
+enum nl80211_iftype;
+
+enum ath5k_srev_type {
+ AR5K_VERSION_MAC,
+ AR5K_VERSION_RAD,
+};
-#define ATH_RXBUF 40 /* number of RX buffers */
-#define ATH_TXBUF 200 /* number of TX buffers */
-#define ATH_BCBUF 4 /* number of beacon buffers */
-#define ATH5K_TXQ_LEN_MAX (ATH_TXBUF / 4) /* bufs per queue */
-#define ATH5K_TXQ_LEN_LOW (ATH5K_TXQ_LEN_MAX / 2) /* low mark */
+struct ath5k_srev_name {
+ const char *sr_name;
+ enum ath5k_srev_type sr_type;
+ u_int sr_val;
+};
struct ath5k_buf {
struct list_head list;
@@ -70,199 +68,11 @@ struct ath5k_buf {
dma_addr_t skbaddr;/* physical addr of skb data */
};
-/*
- * Data transmit queue state. One of these exists for each
- * hardware transmit queue. Packets sent to us from above
- * are assigned to queues based on their priority. Not all
- * devices support a complete set of hardware transmit queues.
- * For those devices the array sc_ac2q will map multiple
- * priorities to fewer hardware queues (typically all to one
- * hardware queue).
- */
-struct ath5k_txq {
- unsigned int qnum; /* hardware q number */
- u32 *link; /* link ptr in last TX desc */
- struct list_head q; /* transmit queue */
- spinlock_t lock; /* lock on q and link */
- bool setup;
- int txq_len; /* number of queued buffers */
- int txq_max; /* max allowed num of queued buffers */
- bool txq_poll_mark;
- unsigned int txq_stuck; /* informational counter */
-};
-
-#define ATH5K_LED_MAX_NAME_LEN 31
-
-/*
- * State for LED triggers
- */
-struct ath5k_led
-{
- char name[ATH5K_LED_MAX_NAME_LEN + 1]; /* name of the LED in sysfs */
- struct ath5k_softc *sc; /* driver state */
- struct led_classdev led_dev; /* led classdev */
-};
-
-/* Rfkill */
-struct ath5k_rfkill {
- /* GPIO PIN for rfkill */
- u16 gpio;
- /* polarity of rfkill GPIO PIN */
- bool polarity;
- /* RFKILL toggle tasklet */
- struct tasklet_struct toggleq;
-};
-
-/* statistics */
-struct ath5k_statistics {
- /* antenna use */
- unsigned int antenna_rx[5]; /* frames count per antenna RX */
- unsigned int antenna_tx[5]; /* frames count per antenna TX */
-
- /* frame errors */
- unsigned int rx_all_count; /* all RX frames, including errors */
- unsigned int tx_all_count; /* all TX frames, including errors */
- unsigned int rx_bytes_count; /* all RX bytes, including errored pks
- * and the MAC headers for each packet
- */
- unsigned int tx_bytes_count; /* all TX bytes, including errored pkts
- * and the MAC headers and padding for
- * each packet.
- */
- unsigned int rxerr_crc;
- unsigned int rxerr_phy;
- unsigned int rxerr_phy_code[32];
- unsigned int rxerr_fifo;
- unsigned int rxerr_decrypt;
- unsigned int rxerr_mic;
- unsigned int rxerr_proc;
- unsigned int rxerr_jumbo;
- unsigned int txerr_retry;
- unsigned int txerr_fifo;
- unsigned int txerr_filt;
-
- /* MIB counters */
- unsigned int ack_fail;
- unsigned int rts_fail;
- unsigned int rts_ok;
- unsigned int fcs_error;
- unsigned int beacons;
-
- unsigned int mib_intr;
- unsigned int rxorn_intr;
- unsigned int rxeol_intr;
-};
-
-#if CHAN_DEBUG
-#define ATH_CHAN_MAX (26+26+26+200+200)
-#else
-#define ATH_CHAN_MAX (14+14+14+252+20)
-#endif
-
struct ath5k_vif {
bool assoc; /* are we associated or not */
enum nl80211_iftype opmode;
int bslot;
struct ath5k_buf *bbuf; /* beacon buffer */
- u8 lladdr[ETH_ALEN];
-};
-
-/* Software Carrier, keeps track of the driver state
- * associated with an instance of a device */
-struct ath5k_softc {
- struct pci_dev *pdev;
- struct device *dev; /* for dma mapping */
- int irq;
- u16 devid;
- void __iomem *iobase; /* address of the device */
- struct mutex lock; /* dev-level lock */
- struct ieee80211_hw *hw; /* IEEE 802.11 common */
- struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
- struct ieee80211_channel channels[ATH_CHAN_MAX];
- struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
- s8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
- enum nl80211_iftype opmode;
- struct ath5k_hw *ah; /* Atheros HW */
-
-#ifdef CONFIG_ATH5K_DEBUG
- struct ath5k_dbg_info debug; /* debug info */
-#endif /* CONFIG_ATH5K_DEBUG */
-
- struct ath5k_buf *bufptr; /* allocated buffer ptr */
- struct ath5k_desc *desc; /* TX/RX descriptors */
- dma_addr_t desc_daddr; /* DMA (physical) address */
- size_t desc_len; /* size of TX/RX descriptors */
-
- DECLARE_BITMAP(status, 6);
-#define ATH_STAT_INVALID 0 /* disable hardware accesses */
-#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */
-#define ATH_STAT_PROMISC 2
-#define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */
-#define ATH_STAT_STARTED 4 /* opened & irqs enabled */
-#define ATH_STAT_2G_DISABLED 5 /* multiband radio without 2G */
-
- unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
- struct ieee80211_channel *curchan; /* current h/w channel */
-
- u16 nvifs;
-
- enum ath5k_int imask; /* interrupt mask copy */
-
- spinlock_t irqlock;
- bool rx_pending; /* rx tasklet pending */
- bool tx_pending; /* tx tasklet pending */
-
- u8 lladdr[ETH_ALEN];
- u8 bssidmask[ETH_ALEN];
-
- unsigned int led_pin, /* GPIO pin for driving LED */
- led_on; /* pin setting for LED on */
-
- struct work_struct reset_work; /* deferred chip reset */
-
- unsigned int rxbufsize; /* rx size based on mtu */
- struct list_head rxbuf; /* receive buffer */
- spinlock_t rxbuflock;
- u32 *rxlink; /* link ptr in last RX desc */
- struct tasklet_struct rxtq; /* rx intr tasklet */
- struct ath5k_led rx_led; /* rx led */
-
- struct list_head txbuf; /* transmit buffer */
- spinlock_t txbuflock;
- unsigned int txbuf_len; /* buf count in txbuf list */
- struct ath5k_txq txqs[AR5K_NUM_TX_QUEUES]; /* tx queues */
- struct tasklet_struct txtq; /* tx intr tasklet */
- struct ath5k_led tx_led; /* tx led */
-
- struct ath5k_rfkill rf_kill;
-
- struct tasklet_struct calib; /* calibration tasklet */
-
- spinlock_t block; /* protects beacon */
- struct tasklet_struct beacontq; /* beacon intr tasklet */
- struct list_head bcbuf; /* beacon buffer */
- struct ieee80211_vif *bslot[ATH_BCBUF];
- u16 num_ap_vifs;
- u16 num_adhoc_vifs;
- unsigned int bhalq, /* SW q for outgoing beacons */
- bmisscount, /* missed beacon transmits */
- bintval, /* beacon interval in TU */
- bsent;
- unsigned int nexttbtt; /* next beacon time in TU */
- struct ath5k_txq *cabq; /* content after beacon */
-
- int power_level; /* Requested tx power in dbm */
- bool assoc; /* associate state */
- bool enable_beacon; /* true if beacons are on */
-
- struct ath5k_statistics stats;
-
- struct ath5k_ani_state ani_state;
- struct tasklet_struct ani_tasklet; /* ANI calibration */
-
- struct delayed_work tx_complete_work;
-
- struct survey_info survey; /* collected survey info */
};
struct ath5k_vif_iter_data {
@@ -275,12 +85,35 @@ struct ath5k_vif_iter_data {
enum nl80211_iftype opmode;
int n_stas;
};
+
void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif);
+bool ath5k_any_vif_assoc(struct ath5k_hw *ah);
+
+int ath5k_start(struct ieee80211_hw *hw);
+void ath5k_stop(struct ieee80211_hw *hw);
+
+void ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf);
+int ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+void ath5k_beacon_config(struct ath5k_hw *ah);
+void ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable);
+
+void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
+ struct ieee80211_vif *vif);
+int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan);
+void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
+void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
+void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct ath5k_txq *txq);
+
+const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
+
+int ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops);
+void ath5k_deinit_ah(struct ath5k_hw *ah);
+/* Check whether BSSID mask is supported */
+#define ath5k_hw_hasbssidmask(_ah) (ah->ah_version == AR5K_AR5212)
-#define ath5k_hw_hasbssidmask(_ah) \
- (ath5k_hw_get_capability(_ah, AR5K_CAP_BSSIDMASK, 0, NULL) == 0)
-#define ath5k_hw_hasveol(_ah) \
- (ath5k_hw_get_capability(_ah, AR5K_CAP_VEOL, 0, NULL) == 0)
+/* Check whether virtual EOL is supported */
+#define ath5k_hw_hasveol(_ah) (ah->ah_version != AR5K_AR5210)
-#endif
+#endif /* _DEV_ATH5K_BASE_H */
diff --git a/drivers/net/wireless/ath/ath5k/caps.c b/drivers/net/wireless/ath/ath5k/caps.c
index 7dd88e1c3ff8..810fba96702b 100644
--- a/drivers/net/wireless/ath/ath5k/caps.c
+++ b/drivers/net/wireless/ath/ath5k/caps.c
@@ -24,7 +24,7 @@
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
-#include "base.h"
+#include "../regd.h"
/*
* Fill the capabilities struct
@@ -52,8 +52,8 @@ int ath5k_hw_set_capabilities(struct ath5k_hw *ah)
__set_bit(AR5K_MODE_11A, caps->cap_mode);
} else {
/*
- * XXX The tranceiver supports frequencies from 4920 to 6100GHz
- * XXX and from 2312 to 2732GHz. There are problems with the
+ * XXX The transceiver supports frequencies from 4920 to 6100MHz
+ * XXX and from 2312 to 2732MHz. There are problems with the
* XXX current ieee80211 implementation because the IEEE
* XXX channel mapping does not support negative channel
* XXX numbers (2312MHz is channel -19). Of course, this
@@ -112,51 +112,6 @@ int ath5k_hw_set_capabilities(struct ath5k_hw *ah)
return 0;
}
-/* Main function used by the driver part to check caps */
-int ath5k_hw_get_capability(struct ath5k_hw *ah,
- enum ath5k_capability_type cap_type,
- u32 capability, u32 *result)
-{
- switch (cap_type) {
- case AR5K_CAP_NUM_TXQUEUES:
- if (result) {
- if (ah->ah_version == AR5K_AR5210)
- *result = AR5K_NUM_TX_QUEUES_NOQCU;
- else
- *result = AR5K_NUM_TX_QUEUES;
- goto yes;
- }
- case AR5K_CAP_VEOL:
- goto yes;
- case AR5K_CAP_COMPRESSION:
- if (ah->ah_version == AR5K_AR5212)
- goto yes;
- else
- goto no;
- case AR5K_CAP_BURST:
- goto yes;
- case AR5K_CAP_TPC:
- goto yes;
- case AR5K_CAP_BSSIDMASK:
- if (ah->ah_version == AR5K_AR5212)
- goto yes;
- else
- goto no;
- case AR5K_CAP_XR:
- if (ah->ah_version == AR5K_AR5212)
- goto yes;
- else
- goto no;
- default:
- goto no;
- }
-
-no:
- return -EINVAL;
-yes:
- return 0;
-}
-
/*
* TODO: Following functions should be part of a new function
* set_capability
diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
index 0bf7313b8a17..8c5ce8b0c734 100644
--- a/drivers/net/wireless/ath/ath5k/debug.c
+++ b/drivers/net/wireless/ath/ath5k/debug.c
@@ -57,20 +57,20 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
+#include <linux/export.h>
+#include <linux/moduleparam.h>
-#include "base.h"
+#include <linux/seq_file.h>
+#include <linux/list.h>
#include "debug.h"
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
static unsigned int ath5k_debug;
module_param_named(debug, ath5k_debug, uint, 0);
-#ifdef CONFIG_ATH5K_DEBUG
-
-#include <linux/seq_file.h>
-#include "reg.h"
-#include "ani.h"
-
static int ath5k_debugfs_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
@@ -157,10 +157,10 @@ static void *reg_next(struct seq_file *seq, void *p, loff_t *pos)
static int reg_show(struct seq_file *seq, void *p)
{
- struct ath5k_softc *sc = seq->private;
+ struct ath5k_hw *ah = seq->private;
struct reg *r = p;
seq_printf(seq, "%-25s0x%08x\n", r->name,
- ath5k_hw_reg_read(sc->ah, r->addr));
+ ath5k_hw_reg_read(ah, r->addr));
return 0;
}
@@ -197,43 +197,42 @@ static const struct file_operations fops_registers = {
static ssize_t read_file_beacon(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = file->private_data;
char buf[500];
unsigned int len = 0;
unsigned int v;
u64 tsf;
- v = ath5k_hw_reg_read(sc->ah, AR5K_BEACON);
- len += snprintf(buf+len, sizeof(buf)-len,
+ v = ath5k_hw_reg_read(ah, AR5K_BEACON);
+ len += snprintf(buf + len, sizeof(buf) - len,
"%-24s0x%08x\tintval: %d\tTIM: 0x%x\n",
"AR5K_BEACON", v, v & AR5K_BEACON_PERIOD,
(v & AR5K_BEACON_TIM) >> AR5K_BEACON_TIM_S);
- len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\n",
- "AR5K_LAST_TSTP", ath5k_hw_reg_read(sc->ah, AR5K_LAST_TSTP));
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\n",
+ "AR5K_LAST_TSTP", ath5k_hw_reg_read(ah, AR5K_LAST_TSTP));
- len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\n\n",
- "AR5K_BEACON_CNT", ath5k_hw_reg_read(sc->ah, AR5K_BEACON_CNT));
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\n\n",
+ "AR5K_BEACON_CNT", ath5k_hw_reg_read(ah, AR5K_BEACON_CNT));
- v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER0);
- len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
+ v = ath5k_hw_reg_read(ah, AR5K_TIMER0);
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER0 (TBTT)", v, v);
- v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER1);
- len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
+ v = ath5k_hw_reg_read(ah, AR5K_TIMER1);
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER1 (DMA)", v, v >> 3);
- v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER2);
- len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
+ v = ath5k_hw_reg_read(ah, AR5K_TIMER2);
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER2 (SWBA)", v, v >> 3);
- v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER3);
- len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
+ v = ath5k_hw_reg_read(ah, AR5K_TIMER3);
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER3 (ATIM)", v, v);
- tsf = ath5k_hw_get_tsf64(sc->ah);
- len += snprintf(buf+len, sizeof(buf)-len,
+ tsf = ath5k_hw_get_tsf64(ah);
+ len += snprintf(buf + len, sizeof(buf) - len,
"TSF\t\t0x%016llx\tTU: %08x\n",
(unsigned long long)tsf, TSF_TO_TU(tsf));
@@ -247,8 +246,7 @@ static ssize_t write_file_beacon(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = file->private_data;
char buf[20];
if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
@@ -279,9 +277,9 @@ static ssize_t write_file_reset(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "debug file triggered reset\n");
- ieee80211_queue_work(sc->hw, &sc->reset_work);
+ struct ath5k_hw *ah = file->private_data;
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "debug file triggered reset\n");
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
return count;
}
@@ -318,23 +316,23 @@ static const struct {
static ssize_t read_file_debug(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
+ struct ath5k_hw *ah = file->private_data;
char buf[700];
unsigned int len = 0;
unsigned int i;
- len += snprintf(buf+len, sizeof(buf)-len,
- "DEBUG LEVEL: 0x%08x\n\n", sc->debug.level);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "DEBUG LEVEL: 0x%08x\n\n", ah->debug.level);
for (i = 0; i < ARRAY_SIZE(dbg_info) - 1; i++) {
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"%10s %c 0x%08x - %s\n", dbg_info[i].name,
- sc->debug.level & dbg_info[i].level ? '+' : ' ',
+ ah->debug.level & dbg_info[i].level ? '+' : ' ',
dbg_info[i].level, dbg_info[i].desc);
}
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"%10s %c 0x%08x - %s\n", dbg_info[i].name,
- sc->debug.level == dbg_info[i].level ? '+' : ' ',
+ ah->debug.level == dbg_info[i].level ? '+' : ' ',
dbg_info[i].level, dbg_info[i].desc);
if (len > sizeof(buf))
@@ -347,7 +345,7 @@ static ssize_t write_file_debug(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
+ struct ath5k_hw *ah = file->private_data;
unsigned int i;
char buf[20];
@@ -357,7 +355,7 @@ static ssize_t write_file_debug(struct file *file,
for (i = 0; i < ARRAY_SIZE(dbg_info); i++) {
if (strncmp(buf, dbg_info[i].name,
strlen(dbg_info[i].name)) == 0) {
- sc->debug.level ^= dbg_info[i].level; /* toggle bit */
+ ah->debug.level ^= dbg_info[i].level; /* toggle bit */
break;
}
}
@@ -378,66 +376,66 @@ static const struct file_operations fops_debug = {
static ssize_t read_file_antenna(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
+ struct ath5k_hw *ah = file->private_data;
char buf[700];
unsigned int len = 0;
unsigned int i;
unsigned int v;
- len += snprintf(buf+len, sizeof(buf)-len, "antenna mode\t%d\n",
- sc->ah->ah_ant_mode);
- len += snprintf(buf+len, sizeof(buf)-len, "default antenna\t%d\n",
- sc->ah->ah_def_ant);
- len += snprintf(buf+len, sizeof(buf)-len, "tx antenna\t%d\n",
- sc->ah->ah_tx_ant);
+ len += snprintf(buf + len, sizeof(buf) - len, "antenna mode\t%d\n",
+ ah->ah_ant_mode);
+ len += snprintf(buf + len, sizeof(buf) - len, "default antenna\t%d\n",
+ ah->ah_def_ant);
+ len += snprintf(buf + len, sizeof(buf) - len, "tx antenna\t%d\n",
+ ah->ah_tx_ant);
- len += snprintf(buf+len, sizeof(buf)-len, "\nANTENNA\t\tRX\tTX\n");
- for (i = 1; i < ARRAY_SIZE(sc->stats.antenna_rx); i++) {
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len, "\nANTENNA\t\tRX\tTX\n");
+ for (i = 1; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) {
+ len += snprintf(buf + len, sizeof(buf) - len,
"[antenna %d]\t%d\t%d\n",
- i, sc->stats.antenna_rx[i], sc->stats.antenna_tx[i]);
+ i, ah->stats.antenna_rx[i], ah->stats.antenna_tx[i]);
}
- len += snprintf(buf+len, sizeof(buf)-len, "[invalid]\t%d\t%d\n",
- sc->stats.antenna_rx[0], sc->stats.antenna_tx[0]);
+ len += snprintf(buf + len, sizeof(buf) - len, "[invalid]\t%d\t%d\n",
+ ah->stats.antenna_rx[0], ah->stats.antenna_tx[0]);
- v = ath5k_hw_reg_read(sc->ah, AR5K_DEFAULT_ANTENNA);
- len += snprintf(buf+len, sizeof(buf)-len,
+ v = ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA);
+ len += snprintf(buf + len, sizeof(buf) - len,
"\nAR5K_DEFAULT_ANTENNA\t0x%08x\n", v);
- v = ath5k_hw_reg_read(sc->ah, AR5K_STA_ID1);
- len += snprintf(buf+len, sizeof(buf)-len,
+ v = ath5k_hw_reg_read(ah, AR5K_STA_ID1);
+ len += snprintf(buf + len, sizeof(buf) - len,
"AR5K_STA_ID1_DEFAULT_ANTENNA\t%d\n",
(v & AR5K_STA_ID1_DEFAULT_ANTENNA) != 0);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"AR5K_STA_ID1_DESC_ANTENNA\t%d\n",
(v & AR5K_STA_ID1_DESC_ANTENNA) != 0);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"AR5K_STA_ID1_RTS_DEF_ANTENNA\t%d\n",
(v & AR5K_STA_ID1_RTS_DEF_ANTENNA) != 0);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"AR5K_STA_ID1_SELFGEN_DEF_ANT\t%d\n",
(v & AR5K_STA_ID1_SELFGEN_DEF_ANT) != 0);
- v = ath5k_hw_reg_read(sc->ah, AR5K_PHY_AGCCTL);
- len += snprintf(buf+len, sizeof(buf)-len,
+ v = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL);
+ len += snprintf(buf + len, sizeof(buf) - len,
"\nAR5K_PHY_AGCCTL_OFDM_DIV_DIS\t%d\n",
(v & AR5K_PHY_AGCCTL_OFDM_DIV_DIS) != 0);
- v = ath5k_hw_reg_read(sc->ah, AR5K_PHY_RESTART);
- len += snprintf(buf+len, sizeof(buf)-len,
+ v = ath5k_hw_reg_read(ah, AR5K_PHY_RESTART);
+ len += snprintf(buf + len, sizeof(buf) - len,
"AR5K_PHY_RESTART_DIV_GC\t\t%x\n",
(v & AR5K_PHY_RESTART_DIV_GC) >> AR5K_PHY_RESTART_DIV_GC_S);
- v = ath5k_hw_reg_read(sc->ah, AR5K_PHY_FAST_ANT_DIV);
- len += snprintf(buf+len, sizeof(buf)-len,
+ v = ath5k_hw_reg_read(ah, AR5K_PHY_FAST_ANT_DIV);
+ len += snprintf(buf + len, sizeof(buf) - len,
"AR5K_PHY_FAST_ANT_DIV_EN\t%d\n",
(v & AR5K_PHY_FAST_ANT_DIV_EN) != 0);
- v = ath5k_hw_reg_read(sc->ah, AR5K_PHY_ANT_SWITCH_TABLE_0);
- len += snprintf(buf+len, sizeof(buf)-len,
+ v = ath5k_hw_reg_read(ah, AR5K_PHY_ANT_SWITCH_TABLE_0);
+ len += snprintf(buf + len, sizeof(buf) - len,
"\nAR5K_PHY_ANT_SWITCH_TABLE_0\t0x%08x\n", v);
- v = ath5k_hw_reg_read(sc->ah, AR5K_PHY_ANT_SWITCH_TABLE_1);
- len += snprintf(buf+len, sizeof(buf)-len,
+ v = ath5k_hw_reg_read(ah, AR5K_PHY_ANT_SWITCH_TABLE_1);
+ len += snprintf(buf + len, sizeof(buf) - len,
"AR5K_PHY_ANT_SWITCH_TABLE_1\t0x%08x\n", v);
if (len > sizeof(buf))
@@ -450,7 +448,7 @@ static ssize_t write_file_antenna(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
+ struct ath5k_hw *ah = file->private_data;
unsigned int i;
char buf[20];
@@ -458,18 +456,18 @@ static ssize_t write_file_antenna(struct file *file,
return -EFAULT;
if (strncmp(buf, "diversity", 9) == 0) {
- ath5k_hw_set_antenna_mode(sc->ah, AR5K_ANTMODE_DEFAULT);
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT);
printk(KERN_INFO "ath5k debug: enable diversity\n");
} else if (strncmp(buf, "fixed-a", 7) == 0) {
- ath5k_hw_set_antenna_mode(sc->ah, AR5K_ANTMODE_FIXED_A);
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A);
printk(KERN_INFO "ath5k debugfs: fixed antenna A\n");
} else if (strncmp(buf, "fixed-b", 7) == 0) {
- ath5k_hw_set_antenna_mode(sc->ah, AR5K_ANTMODE_FIXED_B);
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B);
printk(KERN_INFO "ath5k debug: fixed antenna B\n");
} else if (strncmp(buf, "clear", 5) == 0) {
- for (i = 0; i < ARRAY_SIZE(sc->stats.antenna_rx); i++) {
- sc->stats.antenna_rx[i] = 0;
- sc->stats.antenna_tx[i] = 0;
+ for (i = 0; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) {
+ ah->stats.antenna_rx[i] = 0;
+ ah->stats.antenna_tx[i] = 0;
}
printk(KERN_INFO "ath5k debug: cleared antenna stats\n");
}
@@ -489,42 +487,42 @@ static const struct file_operations fops_antenna = {
static ssize_t read_file_misc(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
+ struct ath5k_hw *ah = file->private_data;
char buf[700];
unsigned int len = 0;
- u32 filt = ath5k_hw_get_rx_filter(sc->ah);
+ u32 filt = ath5k_hw_get_rx_filter(ah);
- len += snprintf(buf+len, sizeof(buf)-len, "bssid-mask: %pM\n",
- sc->bssidmask);
- len += snprintf(buf+len, sizeof(buf)-len, "filter-flags: 0x%x ",
+ len += snprintf(buf + len, sizeof(buf) - len, "bssid-mask: %pM\n",
+ ah->bssidmask);
+ len += snprintf(buf + len, sizeof(buf) - len, "filter-flags: 0x%x ",
filt);
if (filt & AR5K_RX_FILTER_UCAST)
- len += snprintf(buf+len, sizeof(buf)-len, " UCAST");
+ len += snprintf(buf + len, sizeof(buf) - len, " UCAST");
if (filt & AR5K_RX_FILTER_MCAST)
- len += snprintf(buf+len, sizeof(buf)-len, " MCAST");
+ len += snprintf(buf + len, sizeof(buf) - len, " MCAST");
if (filt & AR5K_RX_FILTER_BCAST)
- len += snprintf(buf+len, sizeof(buf)-len, " BCAST");
+ len += snprintf(buf + len, sizeof(buf) - len, " BCAST");
if (filt & AR5K_RX_FILTER_CONTROL)
- len += snprintf(buf+len, sizeof(buf)-len, " CONTROL");
+ len += snprintf(buf + len, sizeof(buf) - len, " CONTROL");
if (filt & AR5K_RX_FILTER_BEACON)
- len += snprintf(buf+len, sizeof(buf)-len, " BEACON");
+ len += snprintf(buf + len, sizeof(buf) - len, " BEACON");
if (filt & AR5K_RX_FILTER_PROM)
- len += snprintf(buf+len, sizeof(buf)-len, " PROM");
+ len += snprintf(buf + len, sizeof(buf) - len, " PROM");
if (filt & AR5K_RX_FILTER_XRPOLL)
- len += snprintf(buf+len, sizeof(buf)-len, " XRPOLL");
+ len += snprintf(buf + len, sizeof(buf) - len, " XRPOLL");
if (filt & AR5K_RX_FILTER_PROBEREQ)
- len += snprintf(buf+len, sizeof(buf)-len, " PROBEREQ");
+ len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ");
if (filt & AR5K_RX_FILTER_PHYERR_5212)
- len += snprintf(buf+len, sizeof(buf)-len, " PHYERR-5212");
+ len += snprintf(buf + len, sizeof(buf) - len, " PHYERR-5212");
if (filt & AR5K_RX_FILTER_RADARERR_5212)
- len += snprintf(buf+len, sizeof(buf)-len, " RADARERR-5212");
+ len += snprintf(buf + len, sizeof(buf) - len, " RADARERR-5212");
if (filt & AR5K_RX_FILTER_PHYERR_5211)
- snprintf(buf+len, sizeof(buf)-len, " PHYERR-5211");
+ snprintf(buf + len, sizeof(buf) - len, " PHYERR-5211");
if (filt & AR5K_RX_FILTER_RADARERR_5211)
- len += snprintf(buf+len, sizeof(buf)-len, " RADARERR-5211");
+ len += snprintf(buf + len, sizeof(buf) - len, " RADARERR-5211");
- len += snprintf(buf+len, sizeof(buf)-len, "\nopmode: %s (%d)\n",
- ath_opmode_to_string(sc->opmode), sc->opmode);
+ len += snprintf(buf + len, sizeof(buf) - len, "\nopmode: %s (%d)\n",
+ ath_opmode_to_string(ah->opmode), ah->opmode);
if (len > sizeof(buf))
len = sizeof(buf);
@@ -544,71 +542,71 @@ static const struct file_operations fops_misc = {
static ssize_t read_file_frameerrors(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
- struct ath5k_statistics *st = &sc->stats;
+ struct ath5k_hw *ah = file->private_data;
+ struct ath5k_statistics *st = &ah->stats;
char buf[700];
unsigned int len = 0;
int i;
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"RX\n---------------------\n");
- len += snprintf(buf+len, sizeof(buf)-len, "CRC\t%u\t(%u%%)\n",
+ len += snprintf(buf + len, sizeof(buf) - len, "CRC\t%u\t(%u%%)\n",
st->rxerr_crc,
st->rx_all_count > 0 ?
- st->rxerr_crc*100/st->rx_all_count : 0);
- len += snprintf(buf+len, sizeof(buf)-len, "PHY\t%u\t(%u%%)\n",
+ st->rxerr_crc * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "PHY\t%u\t(%u%%)\n",
st->rxerr_phy,
st->rx_all_count > 0 ?
- st->rxerr_phy*100/st->rx_all_count : 0);
+ st->rxerr_phy * 100 / st->rx_all_count : 0);
for (i = 0; i < 32; i++) {
if (st->rxerr_phy_code[i])
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
" phy_err[%u]\t%u\n",
i, st->rxerr_phy_code[i]);
}
- len += snprintf(buf+len, sizeof(buf)-len, "FIFO\t%u\t(%u%%)\n",
+ len += snprintf(buf + len, sizeof(buf) - len, "FIFO\t%u\t(%u%%)\n",
st->rxerr_fifo,
st->rx_all_count > 0 ?
- st->rxerr_fifo*100/st->rx_all_count : 0);
- len += snprintf(buf+len, sizeof(buf)-len, "decrypt\t%u\t(%u%%)\n",
+ st->rxerr_fifo * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "decrypt\t%u\t(%u%%)\n",
st->rxerr_decrypt,
st->rx_all_count > 0 ?
- st->rxerr_decrypt*100/st->rx_all_count : 0);
- len += snprintf(buf+len, sizeof(buf)-len, "MIC\t%u\t(%u%%)\n",
+ st->rxerr_decrypt * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "MIC\t%u\t(%u%%)\n",
st->rxerr_mic,
st->rx_all_count > 0 ?
- st->rxerr_mic*100/st->rx_all_count : 0);
- len += snprintf(buf+len, sizeof(buf)-len, "process\t%u\t(%u%%)\n",
+ st->rxerr_mic * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "process\t%u\t(%u%%)\n",
st->rxerr_proc,
st->rx_all_count > 0 ?
- st->rxerr_proc*100/st->rx_all_count : 0);
- len += snprintf(buf+len, sizeof(buf)-len, "jumbo\t%u\t(%u%%)\n",
+ st->rxerr_proc * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "jumbo\t%u\t(%u%%)\n",
st->rxerr_jumbo,
st->rx_all_count > 0 ?
- st->rxerr_jumbo*100/st->rx_all_count : 0);
- len += snprintf(buf+len, sizeof(buf)-len, "[RX all\t%u]\n",
+ st->rxerr_jumbo * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "[RX all\t%u]\n",
st->rx_all_count);
- len += snprintf(buf+len, sizeof(buf)-len, "RX-all-bytes\t%u\n",
+ len += snprintf(buf + len, sizeof(buf) - len, "RX-all-bytes\t%u\n",
st->rx_bytes_count);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"\nTX\n---------------------\n");
- len += snprintf(buf+len, sizeof(buf)-len, "retry\t%u\t(%u%%)\n",
+ len += snprintf(buf + len, sizeof(buf) - len, "retry\t%u\t(%u%%)\n",
st->txerr_retry,
st->tx_all_count > 0 ?
- st->txerr_retry*100/st->tx_all_count : 0);
- len += snprintf(buf+len, sizeof(buf)-len, "FIFO\t%u\t(%u%%)\n",
+ st->txerr_retry * 100 / st->tx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "FIFO\t%u\t(%u%%)\n",
st->txerr_fifo,
st->tx_all_count > 0 ?
- st->txerr_fifo*100/st->tx_all_count : 0);
- len += snprintf(buf+len, sizeof(buf)-len, "filter\t%u\t(%u%%)\n",
+ st->txerr_fifo * 100 / st->tx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "filter\t%u\t(%u%%)\n",
st->txerr_filt,
st->tx_all_count > 0 ?
- st->txerr_filt*100/st->tx_all_count : 0);
- len += snprintf(buf+len, sizeof(buf)-len, "[TX all\t%u]\n",
+ st->txerr_filt * 100 / st->tx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "[TX all\t%u]\n",
st->tx_all_count);
- len += snprintf(buf+len, sizeof(buf)-len, "TX-all-bytes\t%u\n",
+ len += snprintf(buf + len, sizeof(buf) - len, "TX-all-bytes\t%u\n",
st->tx_bytes_count);
if (len > sizeof(buf))
@@ -621,8 +619,8 @@ static ssize_t write_file_frameerrors(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
- struct ath5k_statistics *st = &sc->stats;
+ struct ath5k_hw *ah = file->private_data;
+ struct ath5k_statistics *st = &ah->stats;
char buf[20];
if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
@@ -660,100 +658,104 @@ static const struct file_operations fops_frameerrors = {
static ssize_t read_file_ani(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
- struct ath5k_statistics *st = &sc->stats;
- struct ath5k_ani_state *as = &sc->ani_state;
+ struct ath5k_hw *ah = file->private_data;
+ struct ath5k_statistics *st = &ah->stats;
+ struct ath5k_ani_state *as = &ah->ani_state;
char buf[700];
unsigned int len = 0;
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"HW has PHY error counters:\t%s\n",
- sc->ah->ah_capabilities.cap_has_phyerr_counters ?
+ ah->ah_capabilities.cap_has_phyerr_counters ?
"yes" : "no");
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"HW max spur immunity level:\t%d\n",
as->max_spur_level);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"\nANI state\n--------------------------------------------\n");
- len += snprintf(buf+len, sizeof(buf)-len, "operating mode:\t\t\t");
+ len += snprintf(buf + len, sizeof(buf) - len, "operating mode:\t\t\t");
switch (as->ani_mode) {
case ATH5K_ANI_MODE_OFF:
- len += snprintf(buf+len, sizeof(buf)-len, "OFF\n");
+ len += snprintf(buf + len, sizeof(buf) - len, "OFF\n");
break;
case ATH5K_ANI_MODE_MANUAL_LOW:
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"MANUAL LOW\n");
break;
case ATH5K_ANI_MODE_MANUAL_HIGH:
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"MANUAL HIGH\n");
break;
case ATH5K_ANI_MODE_AUTO:
- len += snprintf(buf+len, sizeof(buf)-len, "AUTO\n");
+ len += snprintf(buf + len, sizeof(buf) - len, "AUTO\n");
break;
default:
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"??? (not good)\n");
break;
}
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"noise immunity level:\t\t%d\n",
as->noise_imm_level);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"spur immunity level:\t\t%d\n",
as->spur_level);
- len += snprintf(buf+len, sizeof(buf)-len, "firstep level:\t\t\t%d\n",
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "firstep level:\t\t\t%d\n",
as->firstep_level);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"OFDM weak signal detection:\t%s\n",
as->ofdm_weak_sig ? "on" : "off");
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"CCK weak signal detection:\t%s\n",
as->cck_weak_sig ? "on" : "off");
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"\nMIB INTERRUPTS:\t\t%u\n",
st->mib_intr);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"beacon RSSI average:\t%d\n",
- (int)ewma_read(&sc->ah->ah_beacon_rssi_avg));
+ (int)ewma_read(&ah->ah_beacon_rssi_avg));
#define CC_PRINT(_struct, _field) \
_struct._field, \
_struct.cycles > 0 ? \
- _struct._field*100/_struct.cycles : 0
+ _struct._field * 100 / _struct.cycles : 0
- len += snprintf(buf+len, sizeof(buf)-len, "profcnt tx\t\t%u\t(%d%%)\n",
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "profcnt tx\t\t%u\t(%d%%)\n",
CC_PRINT(as->last_cc, tx_frame));
- len += snprintf(buf+len, sizeof(buf)-len, "profcnt rx\t\t%u\t(%d%%)\n",
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "profcnt rx\t\t%u\t(%d%%)\n",
CC_PRINT(as->last_cc, rx_frame));
- len += snprintf(buf+len, sizeof(buf)-len, "profcnt busy\t\t%u\t(%d%%)\n",
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "profcnt busy\t\t%u\t(%d%%)\n",
CC_PRINT(as->last_cc, rx_busy));
#undef CC_PRINT
- len += snprintf(buf+len, sizeof(buf)-len, "profcnt cycles\t\t%u\n",
+ len += snprintf(buf + len, sizeof(buf) - len, "profcnt cycles\t\t%u\n",
as->last_cc.cycles);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"listen time\t\t%d\tlast: %d\n",
as->listen_time, as->last_listen);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"OFDM errors\t\t%u\tlast: %u\tsum: %u\n",
as->ofdm_errors, as->last_ofdm_errors,
as->sum_ofdm_errors);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"CCK errors\t\t%u\tlast: %u\tsum: %u\n",
as->cck_errors, as->last_cck_errors,
as->sum_cck_errors);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"AR5K_PHYERR_CNT1\t%x\t(=%d)\n",
- ath5k_hw_reg_read(sc->ah, AR5K_PHYERR_CNT1),
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1),
ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX -
- ath5k_hw_reg_read(sc->ah, AR5K_PHYERR_CNT1)));
- len += snprintf(buf+len, sizeof(buf)-len,
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1)));
+ len += snprintf(buf + len, sizeof(buf) - len,
"AR5K_PHYERR_CNT2\t%x\t(=%d)\n",
- ath5k_hw_reg_read(sc->ah, AR5K_PHYERR_CNT2),
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2),
ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX -
- ath5k_hw_reg_read(sc->ah, AR5K_PHYERR_CNT2)));
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2)));
if (len > sizeof(buf))
len = sizeof(buf);
@@ -765,42 +767,42 @@ static ssize_t write_file_ani(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
+ struct ath5k_hw *ah = file->private_data;
char buf[20];
if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
return -EFAULT;
if (strncmp(buf, "sens-low", 8) == 0) {
- ath5k_ani_init(sc->ah, ATH5K_ANI_MODE_MANUAL_HIGH);
+ ath5k_ani_init(ah, ATH5K_ANI_MODE_MANUAL_HIGH);
} else if (strncmp(buf, "sens-high", 9) == 0) {
- ath5k_ani_init(sc->ah, ATH5K_ANI_MODE_MANUAL_LOW);
+ ath5k_ani_init(ah, ATH5K_ANI_MODE_MANUAL_LOW);
} else if (strncmp(buf, "ani-off", 7) == 0) {
- ath5k_ani_init(sc->ah, ATH5K_ANI_MODE_OFF);
+ ath5k_ani_init(ah, ATH5K_ANI_MODE_OFF);
} else if (strncmp(buf, "ani-on", 6) == 0) {
- ath5k_ani_init(sc->ah, ATH5K_ANI_MODE_AUTO);
+ ath5k_ani_init(ah, ATH5K_ANI_MODE_AUTO);
} else if (strncmp(buf, "noise-low", 9) == 0) {
- ath5k_ani_set_noise_immunity_level(sc->ah, 0);
+ ath5k_ani_set_noise_immunity_level(ah, 0);
} else if (strncmp(buf, "noise-high", 10) == 0) {
- ath5k_ani_set_noise_immunity_level(sc->ah,
+ ath5k_ani_set_noise_immunity_level(ah,
ATH5K_ANI_MAX_NOISE_IMM_LVL);
} else if (strncmp(buf, "spur-low", 8) == 0) {
- ath5k_ani_set_spur_immunity_level(sc->ah, 0);
+ ath5k_ani_set_spur_immunity_level(ah, 0);
} else if (strncmp(buf, "spur-high", 9) == 0) {
- ath5k_ani_set_spur_immunity_level(sc->ah,
- sc->ani_state.max_spur_level);
+ ath5k_ani_set_spur_immunity_level(ah,
+ ah->ani_state.max_spur_level);
} else if (strncmp(buf, "fir-low", 7) == 0) {
- ath5k_ani_set_firstep_level(sc->ah, 0);
+ ath5k_ani_set_firstep_level(ah, 0);
} else if (strncmp(buf, "fir-high", 8) == 0) {
- ath5k_ani_set_firstep_level(sc->ah, ATH5K_ANI_MAX_FIRSTEP_LVL);
+ ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL);
} else if (strncmp(buf, "ofdm-off", 8) == 0) {
- ath5k_ani_set_ofdm_weak_signal_detection(sc->ah, false);
+ ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
} else if (strncmp(buf, "ofdm-on", 7) == 0) {
- ath5k_ani_set_ofdm_weak_signal_detection(sc->ah, true);
+ ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
} else if (strncmp(buf, "cck-off", 7) == 0) {
- ath5k_ani_set_cck_weak_signal_detection(sc->ah, false);
+ ath5k_ani_set_cck_weak_signal_detection(ah, false);
} else if (strncmp(buf, "cck-on", 6) == 0) {
- ath5k_ani_set_cck_weak_signal_detection(sc->ah, true);
+ ath5k_ani_set_cck_weak_signal_detection(ah, true);
}
return count;
}
@@ -819,7 +821,7 @@ static const struct file_operations fops_ani = {
static ssize_t read_file_queue(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
+ struct ath5k_hw *ah = file->private_data;
char buf[700];
unsigned int len = 0;
@@ -827,13 +829,13 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf,
struct ath5k_buf *bf, *bf0;
int i, n;
- len += snprintf(buf+len, sizeof(buf)-len,
- "available txbuffers: %d\n", sc->txbuf_len);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "available txbuffers: %d\n", ah->txbuf_len);
- for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) {
- txq = &sc->txqs[i];
+ for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) {
+ txq = &ah->txqs[i];
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
"%02d: %ssetup\n", i, txq->setup ? "" : "not ");
if (!txq->setup)
@@ -845,9 +847,9 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf,
n++;
spin_unlock_bh(&txq->lock);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
" len: %d bufs: %d\n", txq->txq_len, n);
- len += snprintf(buf+len, sizeof(buf)-len,
+ len += snprintf(buf + len, sizeof(buf) - len,
" stuck: %d\n", txq->txq_stuck);
}
@@ -861,16 +863,16 @@ static ssize_t write_file_queue(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct ath5k_softc *sc = file->private_data;
+ struct ath5k_hw *ah = file->private_data;
char buf[20];
if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
return -EFAULT;
if (strncmp(buf, "start", 5) == 0)
- ieee80211_wake_queues(sc->hw);
+ ieee80211_wake_queues(ah->hw);
else if (strncmp(buf, "stop", 4) == 0)
- ieee80211_stop_queues(sc->hw);
+ ieee80211_stop_queues(ah->hw);
return count;
}
@@ -886,54 +888,57 @@ static const struct file_operations fops_queue = {
void
-ath5k_debug_init_device(struct ath5k_softc *sc)
+ath5k_debug_init_device(struct ath5k_hw *ah)
{
struct dentry *phydir;
- sc->debug.level = ath5k_debug;
+ ah->debug.level = ath5k_debug;
- phydir = debugfs_create_dir("ath5k", sc->hw->wiphy->debugfsdir);
+ phydir = debugfs_create_dir("ath5k", ah->hw->wiphy->debugfsdir);
if (!phydir)
- return;
+ return;
- debugfs_create_file("debug", S_IWUSR | S_IRUSR, phydir, sc,
+ debugfs_create_file("debug", S_IWUSR | S_IRUSR, phydir, ah,
&fops_debug);
- debugfs_create_file("registers", S_IRUSR, phydir, sc, &fops_registers);
+ debugfs_create_file("registers", S_IRUSR, phydir, ah, &fops_registers);
- debugfs_create_file("beacon", S_IWUSR | S_IRUSR, phydir, sc,
+ debugfs_create_file("beacon", S_IWUSR | S_IRUSR, phydir, ah,
&fops_beacon);
- debugfs_create_file("reset", S_IWUSR, phydir, sc, &fops_reset);
+ debugfs_create_file("reset", S_IWUSR, phydir, ah, &fops_reset);
- debugfs_create_file("antenna", S_IWUSR | S_IRUSR, phydir, sc,
+ debugfs_create_file("antenna", S_IWUSR | S_IRUSR, phydir, ah,
&fops_antenna);
- debugfs_create_file("misc", S_IRUSR, phydir, sc, &fops_misc);
+ debugfs_create_file("misc", S_IRUSR, phydir, ah, &fops_misc);
- debugfs_create_file("frameerrors", S_IWUSR | S_IRUSR, phydir, sc,
+ debugfs_create_file("frameerrors", S_IWUSR | S_IRUSR, phydir, ah,
&fops_frameerrors);
- debugfs_create_file("ani", S_IWUSR | S_IRUSR, phydir, sc, &fops_ani);
+ debugfs_create_file("ani", S_IWUSR | S_IRUSR, phydir, ah, &fops_ani);
- debugfs_create_file("queue", S_IWUSR | S_IRUSR, phydir, sc,
+ debugfs_create_file("queue", S_IWUSR | S_IRUSR, phydir, ah,
&fops_queue);
+
+ debugfs_create_bool("32khz_clock", S_IWUSR | S_IRUSR, phydir,
+ &ah->ah_use_32khz_clock);
}
/* functions used in other places */
void
-ath5k_debug_dump_bands(struct ath5k_softc *sc)
+ath5k_debug_dump_bands(struct ath5k_hw *ah)
{
unsigned int b, i;
- if (likely(!(sc->debug.level & ATH5K_DEBUG_DUMPBANDS)))
+ if (likely(!(ah->debug.level & ATH5K_DEBUG_DUMPBANDS)))
return;
- BUG_ON(!sc->sbands);
+ BUG_ON(!ah->sbands);
for (b = 0; b < IEEE80211_NUM_BANDS; b++) {
- struct ieee80211_supported_band *band = &sc->sbands[b];
+ struct ieee80211_supported_band *band = &ah->sbands[b];
char bname[6];
switch (band->band) {
case IEEE80211_BAND_2GHZ:
@@ -983,41 +988,41 @@ ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done,
}
void
-ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah)
+ath5k_debug_printrxbuffs(struct ath5k_hw *ah)
{
struct ath5k_desc *ds;
struct ath5k_buf *bf;
struct ath5k_rx_status rs = {};
int status;
- if (likely(!(sc->debug.level & ATH5K_DEBUG_DESC)))
+ if (likely(!(ah->debug.level & ATH5K_DEBUG_DESC)))
return;
printk(KERN_DEBUG "rxdp %x, rxlink %p\n",
- ath5k_hw_get_rxdp(ah), sc->rxlink);
+ ath5k_hw_get_rxdp(ah), ah->rxlink);
- spin_lock_bh(&sc->rxbuflock);
- list_for_each_entry(bf, &sc->rxbuf, list) {
+ spin_lock_bh(&ah->rxbuflock);
+ list_for_each_entry(bf, &ah->rxbuf, list) {
ds = bf->desc;
status = ah->ah_proc_rx_desc(ah, ds, &rs);
if (!status)
ath5k_debug_printrxbuf(bf, status == 0, &rs);
}
- spin_unlock_bh(&sc->rxbuflock);
+ spin_unlock_bh(&ah->rxbuflock);
}
void
-ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf)
+ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf)
{
struct ath5k_desc *ds = bf->desc;
struct ath5k_hw_5212_tx_desc *td = &ds->ud.ds_tx5212;
struct ath5k_tx_status ts = {};
int done;
- if (likely(!(sc->debug.level & ATH5K_DEBUG_DESC)))
+ if (likely(!(ah->debug.level & ATH5K_DEBUG_DESC)))
return;
- done = sc->ah->ah_proc_tx_desc(sc->ah, bf->desc, &ts);
+ done = ah->ah_proc_tx_desc(ah, bf->desc, &ts);
printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x "
"%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link,
@@ -1026,5 +1031,3 @@ ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf)
td->tx_stat.tx_status_0, td->tx_stat.tx_status_1,
done ? ' ' : (ts.ts_status == 0) ? '*' : '!');
}
-
-#endif /* ifdef CONFIG_ATH5K_DEBUG */
diff --git a/drivers/net/wireless/ath/ath5k/debug.h b/drivers/net/wireless/ath/ath5k/debug.h
index 193dd2d4ea3c..0a3f916a1ef3 100644
--- a/drivers/net/wireless/ath/ath5k/debug.h
+++ b/drivers/net/wireless/ath/ath5k/debug.h
@@ -61,7 +61,6 @@
#ifndef _ATH5K_DEBUG_H
#define _ATH5K_DEBUG_H
-struct ath5k_softc;
struct ath5k_hw;
struct sk_buff;
struct ath5k_buf;
@@ -127,39 +126,39 @@ enum ath5k_debug_level {
} while (0)
void
-ath5k_debug_init_device(struct ath5k_softc *sc);
+ath5k_debug_init_device(struct ath5k_hw *ah);
void
-ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah);
+ath5k_debug_printrxbuffs(struct ath5k_hw *ah);
void
-ath5k_debug_dump_bands(struct ath5k_softc *sc);
+ath5k_debug_dump_bands(struct ath5k_hw *ah);
void
-ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf);
+ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf);
#else /* no debugging */
#include <linux/compiler.h>
-static inline void __attribute__ ((format (printf, 3, 4)))
-ATH5K_DBG(struct ath5k_softc *sc, unsigned int m, const char *fmt, ...) {}
+static inline __printf(3, 4) void
+ATH5K_DBG(struct ath5k_hw *ah, unsigned int m, const char *fmt, ...) {}
-static inline void __attribute__ ((format (printf, 3, 4)))
-ATH5K_DBG_UNLIMIT(struct ath5k_softc *sc, unsigned int m, const char *fmt, ...)
+static inline __printf(3, 4) void
+ATH5K_DBG_UNLIMIT(struct ath5k_hw *ah, unsigned int m, const char *fmt, ...)
{}
static inline void
-ath5k_debug_init_device(struct ath5k_softc *sc) {}
+ath5k_debug_init_device(struct ath5k_hw *ah) {}
static inline void
-ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah) {}
+ath5k_debug_printrxbuffs(struct ath5k_hw *ah) {}
static inline void
-ath5k_debug_dump_bands(struct ath5k_softc *sc) {}
+ath5k_debug_dump_bands(struct ath5k_hw *ah) {}
static inline void
-ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf) {}
+ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf) {}
#endif /* ifdef CONFIG_ATH5K_DEBUG */
diff --git a/drivers/net/wireless/ath/ath5k/desc.c b/drivers/net/wireless/ath/ath5k/desc.c
index f82383b3ed30..7e88dda82221 100644
--- a/drivers/net/wireless/ath/ath5k/desc.c
+++ b/drivers/net/wireless/ath/ath5k/desc.c
@@ -24,7 +24,6 @@
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
-#include "base.h"
/************************\
@@ -55,12 +54,12 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
* noise on the channel, so it is important to avoid this.
*/
if (unlikely(tx_tries0 == 0)) {
- ATH5K_ERR(ah->ah_sc, "zero retries\n");
+ ATH5K_ERR(ah, "zero retries\n");
WARN_ON(1);
return -EINVAL;
}
if (unlikely(tx_rate0 == 0)) {
- ATH5K_ERR(ah->ah_sc, "zero rate\n");
+ ATH5K_ERR(ah, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
@@ -203,12 +202,12 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
* noise on the channel, so it is important to avoid this.
*/
if (unlikely(tx_tries0 == 0)) {
- ATH5K_ERR(ah->ah_sc, "zero retries\n");
+ ATH5K_ERR(ah, "zero retries\n");
WARN_ON(1);
return -EINVAL;
}
if (unlikely(tx_rate0 == 0)) {
- ATH5K_ERR(ah->ah_sc, "zero rate\n");
+ ATH5K_ERR(ah, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
@@ -316,7 +315,7 @@ ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
if (unlikely((tx_rate1 == 0 && tx_tries1 != 0) ||
(tx_rate2 == 0 && tx_tries2 != 0) ||
(tx_rate3 == 0 && tx_tries3 != 0))) {
- ATH5K_ERR(ah->ah_sc, "zero rate\n");
+ ATH5K_ERR(ah, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
diff --git a/drivers/net/wireless/ath/ath5k/desc.h b/drivers/net/wireless/ath/ath5k/desc.h
index 2509d0bf037d..cfd529b548f3 100644
--- a/drivers/net/wireless/ath/ath5k/desc.h
+++ b/drivers/net/wireless/ath/ath5k/desc.h
@@ -58,11 +58,11 @@ struct ath5k_hw_rx_status {
#define AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 /* reception success */
#define AR5K_5210_RX_DESC_STATUS1_CRC_ERROR 0x00000004 /* CRC error */
#define AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN_5210 0x00000008 /* [5210] FIFO overrun */
-#define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010 /* decyption CRC failure */
+#define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010 /* decryption CRC failure */
#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR 0x000000e0 /* PHY error */
#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR_S 5
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 /* key index valid */
-#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX 0x00007e00 /* decyption key index */
+#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX 0x00007e00 /* decryption key index */
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x0fff8000 /* 13 bit of TSF */
#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 15
diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c
index 21091c26a9a5..2481f9c7f4b6 100644
--- a/drivers/net/wireless/ath/ath5k/dma.c
+++ b/drivers/net/wireless/ath/ath5k/dma.c
@@ -25,7 +25,7 @@
*
* Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and
* handle queue setup for 5210 chipset (rest are handled on qcu.c).
- * Also we setup interrupt mask register (IMR) and read the various iterrupt
+ * Also we setup interrupt mask register (IMR) and read the various interrupt
* status registers (ISR).
*
* TODO: Handle SISR on 5211+ and introduce a function to return the queue
@@ -35,7 +35,6 @@
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
-#include "base.h"
/*********\
@@ -73,7 +72,7 @@ static int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
udelay(100);
if (!i)
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"failed to stop RX DMA !\n");
return i ? 0 : -EBUSY;
@@ -100,7 +99,7 @@ u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah)
int ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr)
{
if (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) {
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"tried to set RXDP while rx was active !\n");
return -EIO;
}
@@ -243,7 +242,7 @@ static int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
udelay(100);
if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"queue %i didn't stop !\n", queue);
/* Check for pending frames */
@@ -258,7 +257,7 @@ static int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
/* For 2413+ order PCU to drop packets using
* QUIET mechanism */
if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) &&
- pending){
+ pending) {
/* Set periodicity and duration */
ath5k_hw_reg_write(ah,
AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)|
@@ -295,7 +294,7 @@ static int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
if (pending)
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"quiet mechanism didn't work q:%i !\n",
queue);
}
@@ -309,7 +308,7 @@ static int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
/* Clear register */
ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD);
if (pending) {
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"tx dma didn't stop (q:%i, frm:%i) !\n",
queue, pending);
return -EBUSY;
@@ -333,7 +332,7 @@ int ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue)
int ret;
ret = ath5k_hw_stop_tx_dma(ah, queue);
if (ret) {
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"beacon queue didn't stop !\n");
return -EIO;
}
@@ -726,7 +725,7 @@ enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask)
int_mask |= AR5K_IMR_RXDOPPLER;
/* Note: Per queue interrupt masks
- * are set via reset_tx_queue (qcu.c) */
+ * are set via ath5k_hw_reset_tx_queue() (qcu.c) */
ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR);
ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2);
@@ -783,7 +782,7 @@ void ath5k_hw_dma_init(struct ath5k_hw *ah)
* for all PCI-E cards to be safe).
*
* XXX: need to check 5210 for this
- * TODO: Check out tx triger level, it's always 64 on dumps but I
+ * TODO: Check out tx trigger level, it's always 64 on dumps but I
* guess we can tweak it and see how it goes ;-)
*/
if (ah->ah_version != AR5K_AR5210) {
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c
index 392771f93759..cd708c15b774 100644
--- a/drivers/net/wireless/ath/ath5k/eeprom.c
+++ b/drivers/net/wireless/ath/ath5k/eeprom.c
@@ -26,7 +26,6 @@
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
-#include "base.h"
/******************\
@@ -105,7 +104,7 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah)
* big still, waiting on a better value.
*/
if (eep_max > (3 * AR5K_EEPROM_INFO_MAX)) {
- ATH5K_ERR(ah->ah_sc, "Invalid max custom EEPROM size: "
+ ATH5K_ERR(ah, "Invalid max custom EEPROM size: "
"%d (0x%04x) max expected: %d (0x%04x)\n",
eep_max, eep_max,
3 * AR5K_EEPROM_INFO_MAX,
@@ -119,7 +118,7 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah)
cksum ^= val;
}
if (cksum != AR5K_EEPROM_INFO_CKSUM) {
- ATH5K_ERR(ah->ah_sc, "Invalid EEPROM "
+ ATH5K_ERR(ah, "Invalid EEPROM "
"checksum: 0x%04x eep_max: 0x%04x (%s)\n",
cksum, eep_max,
eep_max == AR5K_EEPROM_INFO_MAX ?
@@ -223,14 +222,14 @@ static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset,
ah->ah_ant_ctl[mode][AR5K_ANT_CTL] =
(ee->ee_ant_control[mode][0] << 4);
ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_A] =
- ee->ee_ant_control[mode][1] |
- (ee->ee_ant_control[mode][2] << 6) |
+ ee->ee_ant_control[mode][1] |
+ (ee->ee_ant_control[mode][2] << 6) |
(ee->ee_ant_control[mode][3] << 12) |
(ee->ee_ant_control[mode][4] << 18) |
(ee->ee_ant_control[mode][5] << 24);
ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_B] =
- ee->ee_ant_control[mode][6] |
- (ee->ee_ant_control[mode][7] << 6) |
+ ee->ee_ant_control[mode][6] |
+ (ee->ee_ant_control[mode][7] << 6) |
(ee->ee_ant_control[mode][8] << 12) |
(ee->ee_ant_control[mode][9] << 18) |
(ee->ee_ant_control[mode][10] << 24);
@@ -255,7 +254,7 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
ee->ee_n_piers[mode] = 0;
AR5K_EEPROM_READ(o++, val);
ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
- switch(mode) {
+ switch (mode) {
case AR5K_EEPROM_MODE_11A:
ee->ee_ob[mode][3] = (val >> 5) & 0x7;
ee->ee_db[mode][3] = (val >> 2) & 0x7;
@@ -349,7 +348,7 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
/* Note: >= v5 have bg freq piers on another location
* so these freq piers are ignored for >= v5 (should be 0xff
* anyway) */
- switch(mode) {
+ switch (mode) {
case AR5K_EEPROM_MODE_11A:
if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_1)
break;
@@ -422,7 +421,7 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
if (ee->ee_version < AR5K_EEPROM_VERSION_5_0)
goto done;
- switch (mode){
+ switch (mode) {
case AR5K_EEPROM_MODE_11A:
ee->ee_switch_settling_turbo[mode] = (val >> 6) & 0x7f;
@@ -436,7 +435,7 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
ee->ee_adc_desired_size_turbo[mode] |= (val & 0x1) << 7;
ee->ee_pga_desired_size_turbo[mode] = (val >> 1) & 0xff;
- if (AR5K_EEPROM_EEMAP(ee->ee_misc0) >=2)
+ if (AR5K_EEPROM_EEMAP(ee->ee_misc0) >= 2)
ee->ee_pd_gain_overlap = (val >> 9) & 0xf;
break;
case AR5K_EEPROM_MODE_11G:
@@ -516,7 +515,7 @@ ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max,
u16 val;
ee->ee_n_piers[mode] = 0;
- while(i < max) {
+ while (i < max) {
AR5K_EEPROM_READ(o++, val);
freq1 = val & 0xff;
@@ -602,7 +601,7 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
struct ath5k_chan_pcal_info *pcal;
- switch(mode) {
+ switch (mode) {
case AR5K_EEPROM_MODE_11B:
pcal = ee->ee_pwr_cal_b;
break;
@@ -634,7 +633,7 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
/* Used to match PCDAC steps with power values on RF5111 chips
* (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC
* steps that match with the power values we read from eeprom. On
- * older eeprom versions (< 3.2) these steps are equaly spaced at
+ * older eeprom versions (< 3.2) these steps are equally spaced at
* 10% of the pcdac curve -until the curve reaches its maximum-
* (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2)
* these 11 steps are spaced in a different way. This function returns
@@ -644,10 +643,12 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
static inline void
ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
{
- static const u16 intercepts3[] =
- { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 };
- static const u16 intercepts3_2[] =
- { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
+ static const u16 intercepts3[] = {
+ 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100
+ };
+ static const u16 intercepts3_2[] = {
+ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100
+ };
const u16 *ip;
int i;
@@ -762,7 +763,7 @@ ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode,
/* Fill raw dataset
* (convert power to 0.25dB units
- * for RF5112 combatibility) */
+ * for RF5112 compatibility) */
for (point = 0; point < pd->pd_points; point++) {
/* Absolute values */
@@ -796,7 +797,7 @@ ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
u16 val;
offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
- switch(mode) {
+ switch (mode) {
case AR5K_EEPROM_MODE_11A:
if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
return 0;
@@ -882,7 +883,7 @@ ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
* Read power calibration for RF5112 chips
*
* For RF5112 we have 4 XPD -eXternal Power Detector- curves
- * for each calibrated channel on 0, -6, -12 and -18dbm but we only
+ * for each calibrated channel on 0, -6, -12 and -18dBm but we only
* use the higher (3) and the lower (0) curves. Each curve has 0.5dB
* power steps on x axis and PCDAC steps on y axis and looks like a
* linear function. To recreate the curve and pass the power values
@@ -1163,7 +1164,7 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
{
u32 offset = AR5K_EEPROM_CAL_DATA_START(ee->ee_misc4);
- switch(mode) {
+ switch (mode) {
case AR5K_EEPROM_MODE_11G:
if (AR5K_EEPROM_HDR_11B(ee->ee_header))
offset += ath5k_pdgains_size_2413(ee,
@@ -1239,7 +1240,7 @@ ath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode,
/* Fill raw dataset
* convert all pwr levels to
- * quarter dB for RF5112 combatibility */
+ * quarter dB for RF5112 compatibility */
pd->pd_step[0] = pcinfo->pddac_i[pdg];
pd->pd_pwr[0] = 4 * pcinfo->pwr_i[pdg];
@@ -1620,8 +1621,8 @@ ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah)
offset += AR5K_EEPROM_GROUPS_START(ee->ee_version);
rep = ee->ee_ctl_pwr;
- for(i = 0; i < ee->ee_ctls; i++) {
- switch(ee->ee_ctl[i] & AR5K_CTL_MODE_M) {
+ for (i = 0; i < ee->ee_ctls; i++) {
+ switch (ee->ee_ctl[i] & AR5K_CTL_MODE_M) {
case AR5K_CTL_11A:
case AR5K_CTL_TURBO:
ctl_mode = AR5K_EEPROM_MODE_11A;
@@ -1778,13 +1779,12 @@ ath5k_eeprom_detach(struct ath5k_hw *ah)
int
ath5k_eeprom_mode_from_channel(struct ieee80211_channel *channel)
{
- switch (channel->hw_value & CHANNEL_MODES) {
- case CHANNEL_A:
- case CHANNEL_XR:
+ switch (channel->hw_value) {
+ case AR5K_MODE_11A:
return AR5K_EEPROM_MODE_11A;
- case CHANNEL_G:
+ case AR5K_MODE_11G:
return AR5K_EEPROM_MODE_11G;
- case CHANNEL_B:
+ case AR5K_MODE_11B:
return AR5K_EEPROM_MODE_11B;
default:
return -1;
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.h b/drivers/net/wireless/ath/ath5k/eeprom.h
index 6511c27d938e..dc2bcfeadeb4 100644
--- a/drivers/net/wireless/ath/ath5k/eeprom.h
+++ b/drivers/net/wireless/ath/ath5k/eeprom.h
@@ -50,7 +50,7 @@
#define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1) /* EEPROM Version */
#define AR5K_EEPROM_VERSION_3_0 0x3000 /* No idea what's going on before this version */
-#define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2Ghz (ar5211_rfregs) */
+#define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2GHz (ar5211_rfregs) */
#define AR5K_EEPROM_VERSION_3_2 0x3002 /* different frequency representation (eeprom_bin2freq) */
#define AR5K_EEPROM_VERSION_3_3 0x3003 /* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */
#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain, ee_cck_ofdm_power_delta (eeprom_read_modes) */
@@ -75,11 +75,11 @@
#define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1)
#define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1)
#define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1)
-#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2Ghz */
+#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2GHz */
#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for < 2W power consumption */
#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7) /* Device type (1 Cardbus, 2 PCI, 3 MiniPCI, 4 AP) */
#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */
-#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz */
+#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5GHz */
/* Newer EEPROMs are using a different offset */
#define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \
@@ -120,7 +120,7 @@
#define AR5K_EEPROM_FF_DIS(_v) (((_v) >> 2) & 0x1) /* disable fast frames */
#define AR5K_EEPROM_BURST_DIS(_v) (((_v) >> 3) & 0x1) /* disable bursting */
#define AR5K_EEPROM_MAX_QCU(_v) (((_v) >> 4) & 0xf) /* max number of QCUs. defaults to 10 */
-#define AR5K_EEPROM_HEAVY_CLIP_EN(_v) (((_v) >> 8) & 0x1) /* enable heayy clipping */
+#define AR5K_EEPROM_HEAVY_CLIP_EN(_v) (((_v) >> 8) & 0x1) /* enable heavy clipping */
#define AR5K_EEPROM_KEY_CACHE_SIZE(_v) (((_v) >> 12) & 0xf) /* key cache size. defaults to 128 */
#define AR5K_EEPROM_MISC6 AR5K_EEPROM_INFO(10)
@@ -223,7 +223,7 @@
#define AR5K_EEPROM_CCK_OFDM_DELTA 15
#define AR5K_EEPROM_N_IQ_CAL 2
/* 5GHz/2GHz */
-enum ath5k_eeprom_freq_bands{
+enum ath5k_eeprom_freq_bands {
AR5K_EEPROM_BAND_5GHZ = 0,
AR5K_EEPROM_BAND_2GHZ = 1,
AR5K_EEPROM_N_FREQ_BANDS,
@@ -270,7 +270,7 @@ enum ath5k_ctl_mode {
/* Per channel calibration data, used for power table setup */
struct ath5k_chan_pcal_info_rf5111 {
- /* Power levels in half dbm units
+ /* Power levels in half dBm units
* for one power curve. */
u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
/* PCDAC table steps
diff --git a/drivers/net/wireless/ath/ath5k/gpio.c b/drivers/net/wireless/ath/ath5k/gpio.c
index bc90503f4b7a..859297811914 100644
--- a/drivers/net/wireless/ath/ath5k/gpio.c
+++ b/drivers/net/wireless/ath/ath5k/gpio.c
@@ -23,7 +23,6 @@
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
-#include "base.h"
/*
* Set led state
diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c
index e49340d18df4..1ffecc0fd3ed 100644
--- a/drivers/net/wireless/ath/ath5k/initvals.c
+++ b/drivers/net/wireless/ath/ath5k/initvals.c
@@ -22,7 +22,6 @@
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
-#include "base.h"
/*
* Mode-independent initial register writes
@@ -113,8 +112,8 @@ static const struct ath5k_ini ar5210_ini[] = {
{ AR5K_PHY(28), 0x0000000f },
{ AR5K_PHY(29), 0x00000080 },
{ AR5K_PHY(30), 0x00000004 },
- { AR5K_PHY(31), 0x00000018 }, /* 0x987c */
- { AR5K_PHY(64), 0x00000000 }, /* 0x9900 */
+ { AR5K_PHY(31), 0x00000018 }, /* 0x987c */
+ { AR5K_PHY(64), 0x00000000 }, /* 0x9900 */
{ AR5K_PHY(65), 0x00000000 },
{ AR5K_PHY(66), 0x00000000 },
{ AR5K_PHY(67), 0x00800000 },
@@ -549,7 +548,7 @@ static const struct ath5k_ini ar5212_ini_common_start[] = {
{ AR5K_DIAG_SW_5211, 0x00000000 },
{ AR5K_ADDAC_TEST, 0x00000000 },
{ AR5K_DEFAULT_ANTENNA, 0x00000000 },
- { AR5K_FRAME_CTL_QOSM, 0x000fc78f },
+ { AR5K_FRAME_CTL_QOSM, 0x000fc78f },
{ AR5K_XRMODE, 0x2a82301a },
{ AR5K_XRDELAY, 0x05dc01e0 },
{ AR5K_XRTIMEOUT, 0x1f402710 },
@@ -760,9 +759,9 @@ static const struct ath5k_ini_mode rf5111_ini_mode_end[] = {
static const struct ath5k_ini rf5111_ini_common_end[] = {
{ AR5K_DCU_FP, 0x00000000 },
- { AR5K_PHY_AGC, 0x00000000 },
- { AR5K_PHY_ADC_CTL, 0x00022ffe },
- { 0x983c, 0x00020100 },
+ { AR5K_PHY_AGC, 0x00000000 },
+ { AR5K_PHY_ADC_CTL, 0x00022ffe },
+ { 0x983c, 0x00020100 },
{ AR5K_PHY_GAIN_OFFSET, 0x1284613c },
{ AR5K_PHY_PAPD_PROBE, 0x00004883 },
{ 0x9940, 0x00000004 },
@@ -1409,7 +1408,7 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool skip_pcu)
* Write initial register settings
*/
- /* For AR5212 and combatible */
+ /* For AR5212 and compatible */
if (ah->ah_version == AR5K_AR5212) {
/* First set of mode-specific settings */
@@ -1542,7 +1541,7 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool skip_pcu)
/* AR5K_MODE_11B */
if (mode > 2) {
- ATH5K_ERR(ah->ah_sc,
+ ATH5K_ERR(ah,
"unsupported channel mode: %d\n", mode);
return -EINVAL;
}
diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c
index 576edf2965dc..c1151c723711 100644
--- a/drivers/net/wireless/ath/ath5k/led.c
+++ b/drivers/net/wireless/ath/ath5k/led.c
@@ -41,18 +41,17 @@
#include <linux/pci.h>
#include "ath5k.h"
-#include "base.h"
-#define ATH_SDEVICE(subv,subd) \
+#define ATH_SDEVICE(subv, subd) \
.vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
.subvendor = (subv), .subdevice = (subd)
-#define ATH_LED(pin,polarity) .driver_data = (((pin) << 8) | (polarity))
+#define ATH_LED(pin, polarity) .driver_data = (((pin) << 8) | (polarity))
#define ATH_PIN(data) ((data) >> 8)
#define ATH_POLARITY(data) ((data) & 0xff)
/* Devices we match on for LED config info (typically laptops) */
-static const struct pci_device_id ath5k_led_devices[] = {
+static DEFINE_PCI_DEVICE_TABLE(ath5k_led_devices) = {
/* AR5211 */
{ PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5211), ATH_LED(0, 0) },
/* HP Compaq nc6xx, nc4000, nx6000 */
@@ -86,26 +85,26 @@ static const struct pci_device_id ath5k_led_devices[] = {
{ }
};
-void ath5k_led_enable(struct ath5k_softc *sc)
+void ath5k_led_enable(struct ath5k_hw *ah)
{
- if (test_bit(ATH_STAT_LEDSOFT, sc->status)) {
- ath5k_hw_set_gpio_output(sc->ah, sc->led_pin);
- ath5k_led_off(sc);
+ if (test_bit(ATH_STAT_LEDSOFT, ah->status)) {
+ ath5k_hw_set_gpio_output(ah, ah->led_pin);
+ ath5k_led_off(ah);
}
}
-static void ath5k_led_on(struct ath5k_softc *sc)
+static void ath5k_led_on(struct ath5k_hw *ah)
{
- if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
+ if (!test_bit(ATH_STAT_LEDSOFT, ah->status))
return;
- ath5k_hw_set_gpio(sc->ah, sc->led_pin, sc->led_on);
+ ath5k_hw_set_gpio(ah, ah->led_pin, ah->led_on);
}
-void ath5k_led_off(struct ath5k_softc *sc)
+void ath5k_led_off(struct ath5k_hw *ah)
{
- if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
+ if (!test_bit(ATH_STAT_LEDSOFT, ah->status))
return;
- ath5k_hw_set_gpio(sc->ah, sc->led_pin, !sc->led_on);
+ ath5k_hw_set_gpio(ah, ah->led_pin, !ah->led_on);
}
static void
@@ -116,27 +115,27 @@ ath5k_led_brightness_set(struct led_classdev *led_dev,
led_dev);
if (brightness == LED_OFF)
- ath5k_led_off(led->sc);
+ ath5k_led_off(led->ah);
else
- ath5k_led_on(led->sc);
+ ath5k_led_on(led->ah);
}
static int
-ath5k_register_led(struct ath5k_softc *sc, struct ath5k_led *led,
+ath5k_register_led(struct ath5k_hw *ah, struct ath5k_led *led,
const char *name, char *trigger)
{
int err;
- led->sc = sc;
+ led->ah = ah;
strncpy(led->name, name, sizeof(led->name));
led->led_dev.name = led->name;
led->led_dev.default_trigger = trigger;
led->led_dev.brightness_set = ath5k_led_brightness_set;
- err = led_classdev_register(sc->dev, &led->led_dev);
+ err = led_classdev_register(ah->dev, &led->led_dev);
if (err) {
- ATH5K_WARN(sc, "could not register LED %s\n", name);
- led->sc = NULL;
+ ATH5K_WARN(ah, "could not register LED %s\n", name);
+ led->ah = NULL;
}
return err;
}
@@ -144,30 +143,30 @@ ath5k_register_led(struct ath5k_softc *sc, struct ath5k_led *led,
static void
ath5k_unregister_led(struct ath5k_led *led)
{
- if (!led->sc)
+ if (!led->ah)
return;
led_classdev_unregister(&led->led_dev);
- ath5k_led_off(led->sc);
- led->sc = NULL;
+ ath5k_led_off(led->ah);
+ led->ah = NULL;
}
-void ath5k_unregister_leds(struct ath5k_softc *sc)
+void ath5k_unregister_leds(struct ath5k_hw *ah)
{
- ath5k_unregister_led(&sc->rx_led);
- ath5k_unregister_led(&sc->tx_led);
+ ath5k_unregister_led(&ah->rx_led);
+ ath5k_unregister_led(&ah->tx_led);
}
-int ath5k_init_leds(struct ath5k_softc *sc)
+int __devinit ath5k_init_leds(struct ath5k_hw *ah)
{
int ret = 0;
- struct ieee80211_hw *hw = sc->hw;
+ struct ieee80211_hw *hw = ah->hw;
#ifndef CONFIG_ATHEROS_AR231X
- struct pci_dev *pdev = sc->pdev;
+ struct pci_dev *pdev = ah->pdev;
#endif
char name[ATH5K_LED_MAX_NAME_LEN + 1];
const struct pci_device_id *match;
- if (!sc->pdev)
+ if (!ah->pdev)
return 0;
#ifdef CONFIG_ATHEROS_AR231X
@@ -176,24 +175,24 @@ int ath5k_init_leds(struct ath5k_softc *sc)
match = pci_match_id(&ath5k_led_devices[0], pdev);
#endif
if (match) {
- __set_bit(ATH_STAT_LEDSOFT, sc->status);
- sc->led_pin = ATH_PIN(match->driver_data);
- sc->led_on = ATH_POLARITY(match->driver_data);
+ __set_bit(ATH_STAT_LEDSOFT, ah->status);
+ ah->led_pin = ATH_PIN(match->driver_data);
+ ah->led_on = ATH_POLARITY(match->driver_data);
}
- if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
+ if (!test_bit(ATH_STAT_LEDSOFT, ah->status))
goto out;
- ath5k_led_enable(sc);
+ ath5k_led_enable(ah);
snprintf(name, sizeof(name), "ath5k-%s::rx", wiphy_name(hw->wiphy));
- ret = ath5k_register_led(sc, &sc->rx_led, name,
+ ret = ath5k_register_led(ah, &ah->rx_led, name,
ieee80211_get_rx_led_name(hw));
if (ret)
goto out;
snprintf(name, sizeof(name), "ath5k-%s::tx", wiphy_name(hw->wiphy));
- ret = ath5k_register_led(sc, &sc->tx_led, name,
+ ret = ath5k_register_led(ah, &ah->tx_led, name,
ieee80211_get_tx_led_name(hw));
out:
return ret;
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index 807bd6440169..6ed4c0717e3e 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -41,13 +41,13 @@
*
*/
+#include <net/mac80211.h>
#include <asm/unaligned.h>
+#include "ath5k.h"
#include "base.h"
#include "reg.h"
-extern int ath5k_modparam_nohwcrypt;
-
/********************\
* Mac80211 functions *
\********************/
@@ -55,44 +55,30 @@ extern int ath5k_modparam_nohwcrypt;
static void
ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
u16 qnum = skb_get_queue_mapping(skb);
- if (WARN_ON(qnum >= sc->ah->ah_capabilities.cap_queues.q_tx_num)) {
+ if (WARN_ON(qnum >= ah->ah_capabilities.cap_queues.q_tx_num)) {
dev_kfree_skb_any(skb);
return;
}
- ath5k_tx_queue(hw, skb, &sc->txqs[qnum]);
-}
-
-
-static int
-ath5k_start(struct ieee80211_hw *hw)
-{
- return ath5k_init_hw(hw->priv);
-}
-
-
-static void
-ath5k_stop(struct ieee80211_hw *hw)
-{
- ath5k_stop_hw(hw->priv);
+ ath5k_tx_queue(hw, skb, &ah->txqs[qnum]);
}
static int
ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
int ret;
struct ath5k_vif *avf = (void *)vif->drv_priv;
- mutex_lock(&sc->lock);
+ mutex_lock(&ah->lock);
if ((vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_ADHOC)
- && (sc->num_ap_vifs + sc->num_adhoc_vifs) >= ATH_BCBUF) {
+ && (ah->num_ap_vifs + ah->num_adhoc_vifs) >= ATH_BCBUF) {
ret = -ELNRNG;
goto end;
}
@@ -102,9 +88,9 @@ ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
* We would need to operate the HW in ad-hoc mode to allow TSF updates
* for the IBSS, but this breaks with additional AP or STA interfaces
* at the moment. */
- if (sc->num_adhoc_vifs ||
- (sc->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
- ATH5K_ERR(sc, "Only one single ad-hoc interface is allowed.\n");
+ if (ah->num_adhoc_vifs ||
+ (ah->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
+ ATH5K_ERR(ah, "Only one single ad-hoc interface is allowed.\n");
ret = -ELNRNG;
goto end;
}
@@ -121,8 +107,8 @@ ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
goto end;
}
- sc->nvifs++;
- ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", avf->opmode);
+ ah->nvifs++;
+ ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "add interface mode %d\n", avf->opmode);
/* Assign the vap/adhoc to a beacon xmit slot. */
if ((avf->opmode == NL80211_IFTYPE_AP) ||
@@ -130,38 +116,35 @@ ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
(avf->opmode == NL80211_IFTYPE_MESH_POINT)) {
int slot;
- WARN_ON(list_empty(&sc->bcbuf));
- avf->bbuf = list_first_entry(&sc->bcbuf, struct ath5k_buf,
+ WARN_ON(list_empty(&ah->bcbuf));
+ avf->bbuf = list_first_entry(&ah->bcbuf, struct ath5k_buf,
list);
list_del(&avf->bbuf->list);
avf->bslot = 0;
for (slot = 0; slot < ATH_BCBUF; slot++) {
- if (!sc->bslot[slot]) {
+ if (!ah->bslot[slot]) {
avf->bslot = slot;
break;
}
}
- BUG_ON(sc->bslot[avf->bslot] != NULL);
- sc->bslot[avf->bslot] = vif;
+ BUG_ON(ah->bslot[avf->bslot] != NULL);
+ ah->bslot[avf->bslot] = vif;
if (avf->opmode == NL80211_IFTYPE_AP)
- sc->num_ap_vifs++;
+ ah->num_ap_vifs++;
else if (avf->opmode == NL80211_IFTYPE_ADHOC)
- sc->num_adhoc_vifs++;
+ ah->num_adhoc_vifs++;
}
/* Any MAC address is fine, all others are included through the
* filter.
*/
- memcpy(&sc->lladdr, vif->addr, ETH_ALEN);
- ath5k_hw_set_lladdr(sc->ah, vif->addr);
-
- memcpy(&avf->lladdr, vif->addr, ETH_ALEN);
+ ath5k_hw_set_lladdr(ah, vif->addr);
- ath5k_update_bssid_mask_and_opmode(sc, vif);
+ ath5k_update_bssid_mask_and_opmode(ah, vif);
ret = 0;
end:
- mutex_unlock(&sc->lock);
+ mutex_unlock(&ah->lock);
return ret;
}
@@ -170,31 +153,31 @@ static void
ath5k_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
struct ath5k_vif *avf = (void *)vif->drv_priv;
unsigned int i;
- mutex_lock(&sc->lock);
- sc->nvifs--;
+ mutex_lock(&ah->lock);
+ ah->nvifs--;
if (avf->bbuf) {
- ath5k_txbuf_free_skb(sc, avf->bbuf);
- list_add_tail(&avf->bbuf->list, &sc->bcbuf);
+ ath5k_txbuf_free_skb(ah, avf->bbuf);
+ list_add_tail(&avf->bbuf->list, &ah->bcbuf);
for (i = 0; i < ATH_BCBUF; i++) {
- if (sc->bslot[i] == vif) {
- sc->bslot[i] = NULL;
+ if (ah->bslot[i] == vif) {
+ ah->bslot[i] = NULL;
break;
}
}
avf->bbuf = NULL;
}
if (avf->opmode == NL80211_IFTYPE_AP)
- sc->num_ap_vifs--;
+ ah->num_ap_vifs--;
else if (avf->opmode == NL80211_IFTYPE_ADHOC)
- sc->num_adhoc_vifs--;
+ ah->num_adhoc_vifs--;
- ath5k_update_bssid_mask_and_opmode(sc, NULL);
- mutex_unlock(&sc->lock);
+ ath5k_update_bssid_mask_and_opmode(ah, NULL);
+ mutex_unlock(&ah->lock);
}
@@ -204,23 +187,22 @@ ath5k_remove_interface(struct ieee80211_hw *hw,
static int
ath5k_config(struct ieee80211_hw *hw, u32 changed)
{
- struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
int ret = 0;
int i;
- mutex_lock(&sc->lock);
+ mutex_lock(&ah->lock);
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- ret = ath5k_chan_set(sc, conf->channel);
+ ret = ath5k_chan_set(ah, conf->channel);
if (ret < 0)
goto unlock;
}
if ((changed & IEEE80211_CONF_CHANGE_POWER) &&
- (sc->power_level != conf->power_level)) {
- sc->power_level = conf->power_level;
+ (ah->power_level != conf->power_level)) {
+ ah->power_level = conf->power_level;
/* Half dB steps */
ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2));
@@ -254,7 +236,7 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed)
ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
unlock:
- mutex_unlock(&sc->lock);
+ mutex_unlock(&ah->lock);
return ret;
}
@@ -264,12 +246,11 @@ ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf, u32 changes)
{
struct ath5k_vif *avf = (void *)vif->drv_priv;
- struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = hw->priv;
struct ath_common *common = ath5k_hw_common(ah);
unsigned long flags;
- mutex_lock(&sc->lock);
+ mutex_lock(&ah->lock);
if (changes & BSS_CHANGED_BSSID) {
/* Cache for later use during resets */
@@ -280,7 +261,7 @@ ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
if (changes & BSS_CHANGED_BEACON_INT)
- sc->bintval = bss_conf->beacon_int;
+ ah->bintval = bss_conf->beacon_int;
if (changes & BSS_CHANGED_ERP_SLOT) {
int slot_time;
@@ -294,16 +275,16 @@ ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (changes & BSS_CHANGED_ASSOC) {
avf->assoc = bss_conf->assoc;
if (bss_conf->assoc)
- sc->assoc = bss_conf->assoc;
+ ah->assoc = bss_conf->assoc;
else
- sc->assoc = ath_any_vif_assoc(sc);
+ ah->assoc = ath5k_any_vif_assoc(ah);
- if (sc->opmode == NL80211_IFTYPE_STATION)
- set_beacon_filter(hw, sc->assoc);
- ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
+ if (ah->opmode == NL80211_IFTYPE_STATION)
+ ath5k_set_beacon_filter(hw, ah->assoc);
+ ath5k_hw_set_ledstate(ah, ah->assoc ?
AR5K_LED_ASSOC : AR5K_LED_INIT);
if (bss_conf->assoc) {
- ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
+ ATH5K_DBG(ah, ATH5K_DEBUG_ANY,
"Bss Info ASSOC %d, bssid: %pM\n",
bss_conf->aid, common->curbssid);
common->curaid = bss_conf->aid;
@@ -313,19 +294,19 @@ ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
if (changes & BSS_CHANGED_BEACON) {
- spin_lock_irqsave(&sc->block, flags);
+ spin_lock_irqsave(&ah->block, flags);
ath5k_beacon_update(hw, vif);
- spin_unlock_irqrestore(&sc->block, flags);
+ spin_unlock_irqrestore(&ah->block, flags);
}
if (changes & BSS_CHANGED_BEACON_ENABLED)
- sc->enable_beacon = bss_conf->enable_beacon;
+ ah->enable_beacon = bss_conf->enable_beacon;
if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_BEACON_INT))
- ath5k_beacon_config(sc);
+ ath5k_beacon_config(ah);
- mutex_unlock(&sc->lock);
+ mutex_unlock(&ah->lock);
}
@@ -350,7 +331,7 @@ ath5k_prepare_multicast(struct ieee80211_hw *hw,
mfilt[pos / 32] |= (1 << (pos % 32));
/* XXX: we might be able to just do this instead,
* but not sure, needs testing, if we do use this we'd
- * neet to inform below to not reset the mcast */
+ * need to inform below not to reset the mcast */
/* ath5k_hw_set_mcast_filterindex(ah,
* ha->addr[5]); */
}
@@ -386,12 +367,11 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC)
- struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = hw->priv;
u32 mfilt[2], rfilt;
struct ath5k_vif_iter_data iter_data; /* to count STA interfaces */
- mutex_lock(&sc->lock);
+ mutex_lock(&ah->lock);
mfilt[0] = multicast;
mfilt[1] = multicast >> 32;
@@ -409,12 +389,12 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) {
if (*new_flags & FIF_PROMISC_IN_BSS)
- __set_bit(ATH_STAT_PROMISC, sc->status);
+ __set_bit(ATH_STAT_PROMISC, ah->status);
else
- __clear_bit(ATH_STAT_PROMISC, sc->status);
+ __clear_bit(ATH_STAT_PROMISC, ah->status);
}
- if (test_bit(ATH_STAT_PROMISC, sc->status))
+ if (test_bit(ATH_STAT_PROMISC, ah->status))
rfilt |= AR5K_RX_FILTER_PROM;
/* Note, AR5K_RX_FILTER_MCAST is already enabled */
@@ -429,7 +409,7 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
/* FIF_BCN_PRBRESP_PROMISC really means to enable beacons
* and probes for any BSSID */
- if ((*new_flags & FIF_BCN_PRBRESP_PROMISC) || (sc->nvifs > 1))
+ if ((*new_flags & FIF_BCN_PRBRESP_PROMISC) || (ah->nvifs > 1))
rfilt |= AR5K_RX_FILTER_BEACON;
/* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not
@@ -444,7 +424,7 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
/* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */
- switch (sc->opmode) {
+ switch (ah->opmode) {
case NL80211_IFTYPE_MESH_POINT:
rfilt |= AR5K_RX_FILTER_CONTROL |
AR5K_RX_FILTER_BEACON |
@@ -457,7 +437,7 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
AR5K_RX_FILTER_BEACON;
break;
case NL80211_IFTYPE_STATION:
- if (sc->assoc)
+ if (ah->assoc)
rfilt |= AR5K_RX_FILTER_BEACON;
default:
break;
@@ -466,14 +446,14 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
iter_data.hw_macaddr = NULL;
iter_data.n_stas = 0;
iter_data.need_set_hw_addr = false;
- ieee80211_iterate_active_interfaces_atomic(sc->hw, ath5k_vif_iter,
+ ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
&iter_data);
/* Set up RX Filter */
if (iter_data.n_stas > 1) {
/* If you have multiple STA interfaces connected to
* different APs, ARPs are not received (most of the time?)
- * Enabling PROMISC appears to fix that probem.
+ * Enabling PROMISC appears to fix that problem.
*/
rfilt |= AR5K_RX_FILTER_PROM;
}
@@ -485,9 +465,9 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
/* Set the cached hw filter flags, this will later actually
* be set in HW */
- sc->filter_flags = rfilt;
+ ah->filter_flags = rfilt;
- mutex_unlock(&sc->lock);
+ mutex_unlock(&ah->lock);
}
@@ -496,8 +476,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
{
- struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = hw->priv;
struct ath_common *common = ath5k_hw_common(ah);
int ret = 0;
@@ -518,7 +497,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
return -EINVAL;
}
- mutex_lock(&sc->lock);
+ mutex_lock(&ah->lock);
switch (cmd) {
case SET_KEY:
@@ -542,7 +521,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
}
mmiowb();
- mutex_unlock(&sc->lock);
+ mutex_unlock(&ah->lock);
return ret;
}
@@ -550,17 +529,17 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
static void
ath5k_sw_scan_start(struct ieee80211_hw *hw)
{
- struct ath5k_softc *sc = hw->priv;
- if (!sc->assoc)
- ath5k_hw_set_ledstate(sc->ah, AR5K_LED_SCAN);
+ struct ath5k_hw *ah = hw->priv;
+ if (!ah->assoc)
+ ath5k_hw_set_ledstate(ah, AR5K_LED_SCAN);
}
static void
ath5k_sw_scan_complete(struct ieee80211_hw *hw)
{
- struct ath5k_softc *sc = hw->priv;
- ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
+ struct ath5k_hw *ah = hw->priv;
+ ath5k_hw_set_ledstate(ah, ah->assoc ?
AR5K_LED_ASSOC : AR5K_LED_INIT);
}
@@ -569,33 +548,32 @@ static int
ath5k_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
/* Force update */
- ath5k_hw_update_mib_counters(sc->ah);
+ ath5k_hw_update_mib_counters(ah);
- stats->dot11ACKFailureCount = sc->stats.ack_fail;
- stats->dot11RTSFailureCount = sc->stats.rts_fail;
- stats->dot11RTSSuccessCount = sc->stats.rts_ok;
- stats->dot11FCSErrorCount = sc->stats.fcs_error;
+ stats->dot11ACKFailureCount = ah->stats.ack_fail;
+ stats->dot11RTSFailureCount = ah->stats.rts_fail;
+ stats->dot11RTSSuccessCount = ah->stats.rts_ok;
+ stats->dot11FCSErrorCount = ah->stats.fcs_error;
return 0;
}
static int
-ath5k_conf_tx(struct ieee80211_hw *hw, u16 queue,
+ath5k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
- struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
+ struct ath5k_hw *ah = hw->priv;
struct ath5k_txq_info qi;
int ret = 0;
if (queue >= ah->ah_capabilities.cap_queues.q_tx_num)
return 0;
- mutex_lock(&sc->lock);
+ mutex_lock(&ah->lock);
ath5k_hw_get_tx_queueprops(ah, queue, &qi);
@@ -604,65 +582,65 @@ ath5k_conf_tx(struct ieee80211_hw *hw, u16 queue,
qi.tqi_cw_max = params->cw_max;
qi.tqi_burst_time = params->txop;
- ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
+ ATH5K_DBG(ah, ATH5K_DEBUG_ANY,
"Configure tx [queue %d], "
"aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
queue, params->aifs, params->cw_min,
params->cw_max, params->txop);
if (ath5k_hw_set_tx_queueprops(ah, queue, &qi)) {
- ATH5K_ERR(sc,
+ ATH5K_ERR(ah,
"Unable to update hardware queue %u!\n", queue);
ret = -EIO;
} else
ath5k_hw_reset_tx_queue(ah, queue);
- mutex_unlock(&sc->lock);
+ mutex_unlock(&ah->lock);
return ret;
}
static u64
-ath5k_get_tsf(struct ieee80211_hw *hw)
+ath5k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
- return ath5k_hw_get_tsf64(sc->ah);
+ return ath5k_hw_get_tsf64(ah);
}
static void
-ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+ath5k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
- ath5k_hw_set_tsf64(sc->ah, tsf);
+ ath5k_hw_set_tsf64(ah, tsf);
}
static void
-ath5k_reset_tsf(struct ieee80211_hw *hw)
+ath5k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
/*
* in IBSS mode we need to update the beacon timers too.
* this will also reset the TSF if we call it with 0
*/
- if (sc->opmode == NL80211_IFTYPE_ADHOC)
- ath5k_beacon_update_timers(sc, 0);
+ if (ah->opmode == NL80211_IFTYPE_ADHOC)
+ ath5k_beacon_update_timers(ah, 0);
else
- ath5k_hw_reset_tsf(sc->ah);
+ ath5k_hw_reset_tsf(ah);
}
static int
ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
- struct ath_common *common = ath5k_hw_common(sc->ah);
+ struct ath_common *common = ath5k_hw_common(ah);
struct ath_cycle_counters *cc = &common->cc_survey;
unsigned int div = common->clockrate * 1000;
@@ -672,18 +650,18 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
spin_lock_bh(&common->cc_lock);
ath_hw_cycle_counters_update(common);
if (cc->cycles > 0) {
- sc->survey.channel_time += cc->cycles / div;
- sc->survey.channel_time_busy += cc->rx_busy / div;
- sc->survey.channel_time_rx += cc->rx_frame / div;
- sc->survey.channel_time_tx += cc->tx_frame / div;
+ ah->survey.channel_time += cc->cycles / div;
+ ah->survey.channel_time_busy += cc->rx_busy / div;
+ ah->survey.channel_time_rx += cc->rx_frame / div;
+ ah->survey.channel_time_tx += cc->tx_frame / div;
}
memset(cc, 0, sizeof(*cc));
spin_unlock_bh(&common->cc_lock);
- memcpy(survey, &sc->survey, sizeof(*survey));
+ memcpy(survey, &ah->survey, sizeof(*survey));
survey->channel = conf->channel;
- survey->noise = sc->ah->ah_noise_floor;
+ survey->noise = ah->ah_noise_floor;
survey->filled = SURVEY_INFO_NOISE_DBM |
SURVEY_INFO_CHANNEL_TIME |
SURVEY_INFO_CHANNEL_TIME_BUSY |
@@ -707,25 +685,25 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
static void
ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
- mutex_lock(&sc->lock);
- ath5k_hw_set_coverage_class(sc->ah, coverage_class);
- mutex_unlock(&sc->lock);
+ mutex_lock(&ah->lock);
+ ath5k_hw_set_coverage_class(ah, coverage_class);
+ mutex_unlock(&ah->lock);
}
static int
ath5k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
if (tx_ant == 1 && rx_ant == 1)
- ath5k_hw_set_antenna_mode(sc->ah, AR5K_ANTMODE_FIXED_A);
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A);
else if (tx_ant == 2 && rx_ant == 2)
- ath5k_hw_set_antenna_mode(sc->ah, AR5K_ANTMODE_FIXED_B);
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B);
else if ((tx_ant & 3) == 3 && (rx_ant & 3) == 3)
- ath5k_hw_set_antenna_mode(sc->ah, AR5K_ANTMODE_DEFAULT);
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT);
else
return -EINVAL;
return 0;
@@ -735,9 +713,9 @@ ath5k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
static int
ath5k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
- switch (sc->ah->ah_ant_mode) {
+ switch (ah->ah_ant_mode) {
case AR5K_ANTMODE_FIXED_A:
*tx_ant = 1; *rx_ant = 1; break;
case AR5K_ANTMODE_FIXED_B:
@@ -752,9 +730,9 @@ ath5k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
static void ath5k_get_ringparam(struct ieee80211_hw *hw,
u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
- *tx = sc->txqs[AR5K_TX_QUEUE_ID_DATA_MIN].txq_max;
+ *tx = ah->txqs[AR5K_TX_QUEUE_ID_DATA_MIN].txq_max;
*tx_max = ATH5K_TXQ_LEN_MAX;
*rx = *rx_max = ATH_RXBUF;
@@ -763,7 +741,7 @@ static void ath5k_get_ringparam(struct ieee80211_hw *hw,
static int ath5k_set_ringparam(struct ieee80211_hw *hw, u32 tx, u32 rx)
{
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
u16 qnum;
/* only support setting tx ring size for now */
@@ -774,16 +752,16 @@ static int ath5k_set_ringparam(struct ieee80211_hw *hw, u32 tx, u32 rx)
if (!tx || tx > ATH5K_TXQ_LEN_MAX)
return -EINVAL;
- for (qnum = 0; qnum < ARRAY_SIZE(sc->txqs); qnum++) {
- if (!sc->txqs[qnum].setup)
+ for (qnum = 0; qnum < ARRAY_SIZE(ah->txqs); qnum++) {
+ if (!ah->txqs[qnum].setup)
continue;
- if (sc->txqs[qnum].qnum < AR5K_TX_QUEUE_ID_DATA_MIN ||
- sc->txqs[qnum].qnum > AR5K_TX_QUEUE_ID_DATA_MAX)
+ if (ah->txqs[qnum].qnum < AR5K_TX_QUEUE_ID_DATA_MIN ||
+ ah->txqs[qnum].qnum > AR5K_TX_QUEUE_ID_DATA_MAX)
continue;
- sc->txqs[qnum].txq_max = tx;
- if (sc->txqs[qnum].txq_len >= sc->txqs[qnum].txq_max)
- ieee80211_stop_queue(hw, sc->txqs[qnum].qnum);
+ ah->txqs[qnum].txq_max = tx;
+ if (ah->txqs[qnum].txq_len >= ah->txqs[qnum].txq_max)
+ ieee80211_stop_queue(hw, ah->txqs[qnum].qnum);
}
return 0;
diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c
index 296c316a8341..dfa48eb7d953 100644
--- a/drivers/net/wireless/ath/ath5k/pci.c
+++ b/drivers/net/wireless/ath/ath5k/pci.c
@@ -18,6 +18,7 @@
#include <linux/pci.h>
#include <linux/pci-aspm.h>
#include <linux/etherdevice.h>
+#include <linux/module.h>
#include "../ath.h"
#include "ath5k.h"
#include "debug.h"
@@ -34,12 +35,12 @@ static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = {
{ PCI_VDEVICE(3COM_2, 0x0013) }, /* 3com 5212 */
{ PCI_VDEVICE(3COM, 0x0013) }, /* 3com 3CRDAG675 5212 */
{ PCI_VDEVICE(ATHEROS, 0x1014) }, /* IBM minipci 5212 */
- { PCI_VDEVICE(ATHEROS, 0x0014) }, /* 5212 combatible */
- { PCI_VDEVICE(ATHEROS, 0x0015) }, /* 5212 combatible */
- { PCI_VDEVICE(ATHEROS, 0x0016) }, /* 5212 combatible */
- { PCI_VDEVICE(ATHEROS, 0x0017) }, /* 5212 combatible */
- { PCI_VDEVICE(ATHEROS, 0x0018) }, /* 5212 combatible */
- { PCI_VDEVICE(ATHEROS, 0x0019) }, /* 5212 combatible */
+ { PCI_VDEVICE(ATHEROS, 0x0014) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x0015) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x0016) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x0017) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x0018) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x0019) }, /* 5212 compatible */
{ PCI_VDEVICE(ATHEROS, 0x001a) }, /* 2413 Griffin-lite */
{ PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */
{ PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */
@@ -51,10 +52,10 @@ MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table);
/* return bus cachesize in 4B word units */
static void ath5k_pci_read_cachesize(struct ath_common *common, int *csz)
{
- struct ath5k_softc *sc = (struct ath5k_softc *) common->priv;
+ struct ath5k_hw *ah = (struct ath5k_hw *) common->priv;
u8 u8tmp;
- pci_read_config_byte(sc->pdev, PCI_CACHE_LINE_SIZE, &u8tmp);
+ pci_read_config_byte(ah->pdev, PCI_CACHE_LINE_SIZE, &u8tmp);
*csz = (int)u8tmp;
/*
@@ -156,7 +157,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
void __iomem *mem;
- struct ath5k_softc *sc;
+ struct ath5k_hw *ah;
struct ieee80211_hw *hw;
int ret;
u8 csz;
@@ -234,7 +235,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
mem = pci_iomap(pdev, 0, 0);
if (!mem) {
- dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
+ dev_err(&pdev->dev, "cannot remap PCI memory region\n");
ret = -EIO;
goto err_reg;
}
@@ -243,7 +244,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
* Allocate hw (mac80211 main struct)
* and hw->priv (driver private data)
*/
- hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops);
+ hw = ieee80211_alloc_hw(sizeof(*ah), &ath5k_hw_ops);
if (hw == NULL) {
dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
ret = -ENOMEM;
@@ -252,16 +253,16 @@ ath5k_pci_probe(struct pci_dev *pdev,
dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy));
- sc = hw->priv;
- sc->hw = hw;
- sc->pdev = pdev;
- sc->dev = &pdev->dev;
- sc->irq = pdev->irq;
- sc->devid = id->device;
- sc->iobase = mem; /* So we can unmap it on detach */
+ ah = hw->priv;
+ ah->hw = hw;
+ ah->pdev = pdev;
+ ah->dev = &pdev->dev;
+ ah->irq = pdev->irq;
+ ah->devid = id->device;
+ ah->iobase = mem; /* So we can unmap it on detach */
/* Initialize */
- ret = ath5k_init_softc(sc, &ath_pci_bus_ops);
+ ret = ath5k_init_ah(ah, &ath_pci_bus_ops);
if (ret)
goto err_free;
@@ -285,10 +286,10 @@ static void __devexit
ath5k_pci_remove(struct pci_dev *pdev)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
- struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = hw->priv;
- ath5k_deinit_softc(sc);
- pci_iounmap(pdev, sc->iobase);
+ ath5k_deinit_ah(ah);
+ pci_iounmap(pdev, ah->iobase);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
ieee80211_free_hw(hw);
@@ -297,16 +298,19 @@ ath5k_pci_remove(struct pci_dev *pdev)
#ifdef CONFIG_PM_SLEEP
static int ath5k_pci_suspend(struct device *dev)
{
- struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev));
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct ath5k_hw *ah = hw->priv;
- ath5k_led_off(sc);
+ ath5k_led_off(ah);
return 0;
}
static int ath5k_pci_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
- struct ath5k_softc *sc = pci_get_drvdata(pdev);
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct ath5k_hw *ah = hw->priv;
/*
* Suspend/Resume resets the PCI configuration space, so we have to
@@ -315,7 +319,7 @@ static int ath5k_pci_resume(struct device *dev)
*/
pci_write_config_byte(pdev, 0x41, 0);
- ath5k_led_enable(sc);
+ ath5k_led_enable(ah);
return 0;
}
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
index 712a9ac4000e..a7eafa3edc21 100644
--- a/drivers/net/wireless/ath/ath5k/pcu.c
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -29,10 +29,9 @@
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
-#include "base.h"
/*
- * AR5212+ can use higher rates for ack transmition
+ * AR5212+ can use higher rates for ack transmission
* based on current tx rate instead of the base rate.
* It does this to better utilize channel usage.
* This is a mapping between G rates (that cover both
@@ -77,14 +76,13 @@ static const unsigned int ack_rates_high[] =
int ath5k_hw_get_frame_duration(struct ath5k_hw *ah,
int len, struct ieee80211_rate *rate, bool shortpre)
{
- struct ath5k_softc *sc = ah->ah_sc;
int sifs, preamble, plcp_bits, sym_time;
int bitrate, bits, symbols, symbol_bits;
int dur;
/* Fallback */
if (!ah->ah_bwmode) {
- __le16 raw_dur = ieee80211_generic_frame_duration(sc->hw,
+ __le16 raw_dur = ieee80211_generic_frame_duration(ah->hw,
NULL, len, rate);
/* subtract difference between long and short preamble */
@@ -153,7 +151,7 @@ unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah)
case AR5K_BWMODE_DEFAULT:
default:
slot_time = AR5K_INIT_SLOT_TIME_DEFAULT;
- if ((channel->hw_value & CHANNEL_CCK) && !ah->ah_short_slot)
+ if ((channel->hw_value == AR5K_MODE_11B) && !ah->ah_short_slot)
slot_time = AR5K_INIT_SLOT_TIME_B;
break;
}
@@ -184,7 +182,7 @@ unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah)
case AR5K_BWMODE_DEFAULT:
sifs = AR5K_INIT_SIFS_DEFAULT_BG;
default:
- if (channel->hw_value & CHANNEL_5GHZ)
+ if (channel->band == IEEE80211_BAND_5GHZ)
sifs = AR5K_INIT_SIFS_DEFAULT_A;
break;
}
@@ -205,7 +203,7 @@ unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah)
*/
void ath5k_hw_update_mib_counters(struct ath5k_hw *ah)
{
- struct ath5k_statistics *stats = &ah->ah_sc->stats;
+ struct ath5k_statistics *stats = &ah->stats;
/* Read-And-Clear */
stats->ack_fail += ath5k_hw_reg_read(ah, AR5K_ACK_FAIL);
@@ -240,25 +238,24 @@ void ath5k_hw_update_mib_counters(struct ath5k_hw *ah)
*/
static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah)
{
- struct ath5k_softc *sc = ah->ah_sc;
struct ieee80211_rate *rate;
unsigned int i;
/* 802.11g covers both OFDM and CCK */
u8 band = IEEE80211_BAND_2GHZ;
/* Write rate duration table */
- for (i = 0; i < sc->sbands[band].n_bitrates; i++) {
+ for (i = 0; i < ah->sbands[band].n_bitrates; i++) {
u32 reg;
u16 tx_time;
if (ah->ah_ack_bitrate_high)
- rate = &sc->sbands[band].bitrates[ack_rates_high[i]];
+ rate = &ah->sbands[band].bitrates[ack_rates_high[i]];
/* CCK -> 1Mb */
else if (i < 4)
- rate = &sc->sbands[band].bitrates[0];
+ rate = &ah->sbands[band].bitrates[0];
/* OFDM -> 6Mb */
else
- rate = &sc->sbands[band].bitrates[4];
+ rate = &ah->sbands[band].bitrates[4];
/* Set ACK timeout */
reg = AR5K_RATE_DUR(rate->hw_value);
@@ -534,9 +531,9 @@ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah)
local_irq_restore(flags);
- WARN_ON( i == ATH5K_MAX_TSF_READ );
+ WARN_ON(i == ATH5K_MAX_TSF_READ);
- return (((u64)tsf_upper1 << 32) | tsf_lower);
+ return ((u64)tsf_upper1 << 32) | tsf_lower;
}
/**
@@ -586,7 +583,7 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
/*
* Set the additional timers by mode
*/
- switch (ah->ah_sc->opmode) {
+ switch (ah->opmode) {
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_STATION:
/* In STA mode timer1 is used as next wakeup
@@ -623,8 +620,8 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
* Set the beacon register and enable all timers.
*/
/* When in AP or Mesh Point mode zero timer0 to start TSF */
- if (ah->ah_sc->opmode == NL80211_IFTYPE_AP ||
- ah->ah_sc->opmode == NL80211_IFTYPE_MESH_POINT)
+ if (ah->opmode == NL80211_IFTYPE_AP ||
+ ah->opmode == NL80211_IFTYPE_MESH_POINT)
ath5k_hw_reg_write(ah, 0, AR5K_TIMER0);
ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0);
@@ -643,14 +640,14 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
/* Flush any pending BMISS interrupts on ISR by
* performing a clear-on-write operation on PISR
* register for the BMISS bit (writing a bit on
- * ISR togles a reset for that bit and leaves
- * the rest bits intact) */
+ * ISR toggles a reset for that bit and leaves
+ * the remaining bits intact) */
if (ah->ah_version == AR5K_AR5210)
ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_ISR);
else
ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_PISR);
- /* TODO: Set enchanced sleep registers on AR5212
+ /* TODO: Set enhanced sleep registers on AR5212
* based on vif->bss_conf params, until then
* disable power save reporting.*/
AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PWR_SV);
@@ -738,7 +735,7 @@ ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval)
dma = ath5k_hw_reg_read(ah, AR5K_TIMER1) >> 3;
/* NOTE: SWBA is different. Having a wrong window there does not
- * stop us from sending data and this condition is catched thru
+ * stop us from sending data and this condition is caught by
* other means (SWBA interrupt) */
if (ath5k_check_timer_win(nbtt, atim, 1, intval) &&
@@ -814,7 +811,7 @@ int ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype op_mode)
struct ath_common *common = ath5k_hw_common(ah);
u32 pcu_reg, beacon_reg, low_id, high_id;
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_MODE, "mode %d\n", op_mode);
+ ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "mode %d\n", op_mode);
/* Preserve rest settings */
pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000;
@@ -890,13 +887,13 @@ void ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
* XXX: rethink this after new mode changes to
* mac80211 are integrated */
if (ah->ah_version == AR5K_AR5212 &&
- ah->ah_sc->nvifs)
+ ah->nvifs)
ath5k_hw_write_rate_duration(ah);
/* Set RSSI/BRSSI thresholds
*
* Note: If we decide to set this value
- * dynamicaly, have in mind that when AR5K_RSSI_THR
+ * dynamically, have in mind that when AR5K_RSSI_THR
* register is read it might return 0x40 if we haven't
* wrote anything to it plus BMISS RSSI threshold is zeroed.
* So doing a save/restore procedure here isn't the right
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
index 55441913344d..01cb72de44cb 100644
--- a/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -22,12 +22,13 @@
#include <linux/delay.h>
#include <linux/slab.h>
+#include <asm/unaligned.h>
#include "ath5k.h"
#include "reg.h"
-#include "base.h"
#include "rfbuffer.h"
#include "rfgain.h"
+#include "../regd.h"
/******************\
@@ -37,7 +38,7 @@
/*
* Get the PHY Chip revision
*/
-u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan)
+u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band)
{
unsigned int i;
u32 srev;
@@ -46,11 +47,11 @@ u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan)
/*
* Set the radio chip access register
*/
- switch (chan) {
- case CHANNEL_2GHZ:
+ switch (band) {
+ case IEEE80211_BAND_2GHZ:
ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0));
break;
- case CHANNEL_5GHZ:
+ case IEEE80211_BAND_5GHZ:
ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
break;
default:
@@ -83,14 +84,16 @@ u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan)
/*
* Check if a channel is supported
*/
-bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags)
+bool ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel)
{
+ u16 freq = channel->center_freq;
+
/* Check if the channel is in our supported range */
- if (flags & CHANNEL_2GHZ) {
+ if (channel->band == IEEE80211_BAND_2GHZ) {
if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) &&
(freq <= ah->ah_capabilities.cap_range.range_2ghz_max))
return true;
- } else if (flags & CHANNEL_5GHZ)
+ } else if (channel->band == IEEE80211_BAND_5GHZ)
if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) &&
(freq <= ah->ah_capabilities.cap_range.range_5ghz_max))
return true;
@@ -105,6 +108,7 @@ bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
if ((ah->ah_radio == AR5K_RF5112) ||
(ah->ah_radio == AR5K_RF5413) ||
+ (ah->ah_radio == AR5K_RF2413) ||
(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
refclk_freq = 40;
else
@@ -173,7 +177,7 @@ static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah,
data = ath5k_hw_bitswap(val, num_bits);
for (bits_shifted = 0, bits_left = num_bits; bits_left > 0;
- position = 0, entry++) {
+ position = 0, entry++) {
last_bit = (position + bits_left > 8) ? 8 :
position + bits_left;
@@ -222,7 +226,7 @@ static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah,
ds_coef_exp, ds_coef_man, clock;
BUG_ON(!(ah->ah_version == AR5K_AR5212) ||
- !(channel->hw_value & CHANNEL_OFDM));
+ (channel->hw_value == AR5K_MODE_11B));
/* Get coefficient
* ALGO: coef = (5 * clock / carrier_freq) / 2
@@ -296,7 +300,7 @@ static void ath5k_hw_wait_for_synth(struct ath5k_hw *ah,
u32 delay;
delay = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) &
AR5K_PHY_RX_DELAY_M;
- delay = (channel->hw_value & CHANNEL_CCK) ?
+ delay = (channel->hw_value == AR5K_MODE_11B) ?
((delay << 2) / 22) : (delay / 10);
if (ah->ah_bwmode == AR5K_BWMODE_10MHZ)
delay = delay << 1;
@@ -363,7 +367,7 @@ int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah)
return 0;
}
-/* Schedule a gain probe check on the next transmited packet.
+/* Schedule a gain probe check on the next transmitted packet.
* That means our next packet is going to be sent with lower
* tx power and a Peak to Average Power Detector (PAPD) will try
* to measure the gain.
@@ -472,7 +476,7 @@ static bool ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah)
level[0] = 0;
level[1] = (step == 63) ? 50 : step + 4;
level[2] = (step != 63) ? 64 : level[0];
- level[3] = level[2] + 50 ;
+ level[3] = level[2] + 50;
ah->ah_gain.g_high = level[3] -
(step == 63 ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5);
@@ -549,7 +553,7 @@ static s8 ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah)
for (ah->ah_gain.g_target = ah->ah_gain.g_current;
ah->ah_gain.g_target <= ah->ah_gain.g_low &&
- ah->ah_gain.g_step_idx < go->go_steps_count-1;
+ ah->ah_gain.g_step_idx < go->go_steps_count - 1;
g_step = &go->go_step[ah->ah_gain.g_step_idx])
ah->ah_gain.g_target -= 2 *
(go->go_step[++ah->ah_gain.g_step_idx].gos_gain -
@@ -560,7 +564,7 @@ static s8 ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah)
}
done:
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
"ret %d, gain step %u, current gain %u, target gain %u\n",
ret, ah->ah_gain.g_step_idx, ah->ah_gain.g_current,
ah->ah_gain.g_target);
@@ -614,13 +618,13 @@ enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah)
ath5k_hw_rf_gainf_corr(ah);
ah->ah_gain.g_current =
ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ?
- (ah->ah_gain.g_current-ah->ah_gain.g_f_corr) :
+ (ah->ah_gain.g_current - ah->ah_gain.g_f_corr) :
0;
}
/* Check if measurement is ok and if we need
* to adjust gain, schedule a gain adjustment,
- * else switch back to the acive state */
+ * else switch back to the active state */
if (ath5k_hw_rf_check_gainf_readback(ah) &&
AR5K_GAIN_CHECK_ADJUST(&ah->ah_gain) &&
ath5k_hw_rf_gainf_adjust(ah)) {
@@ -772,7 +776,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
ah->ah_rf_banks = kmalloc(sizeof(u32) * ah->ah_rf_banks_size,
GFP_KERNEL);
if (ah->ah_rf_banks == NULL) {
- ATH5K_ERR(ah->ah_sc, "out of memory\n");
+ ATH5K_ERR(ah, "out of memory\n");
return -ENOMEM;
}
}
@@ -782,7 +786,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
for (i = 0; i < ah->ah_rf_banks_size; i++) {
if (ini_rfb[i].rfb_bank >= AR5K_MAX_RF_BANKS) {
- ATH5K_ERR(ah->ah_sc, "invalid bank\n");
+ ATH5K_ERR(ah, "invalid bank\n");
return -EINVAL;
}
@@ -796,9 +800,9 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
}
/* Set Output and Driver bias current (OB/DB) */
- if (channel->hw_value & CHANNEL_2GHZ) {
+ if (channel->band == IEEE80211_BAND_2GHZ) {
- if (channel->hw_value & CHANNEL_CCK)
+ if (channel->hw_value == AR5K_MODE_11B)
ee_mode = AR5K_EEPROM_MODE_11B;
else
ee_mode = AR5K_EEPROM_MODE_11G;
@@ -807,7 +811,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
* use b_OB and b_DB parameters stored
* in eeprom on ee->ee_ob[ee_mode][0]
*
- * For all other chips we use OB/DB for 2Ghz
+ * For all other chips we use OB/DB for 2GHz
* stored in the b/g modal section just like
* 802.11a on ee->ee_ob[ee_mode][1] */
if ((ah->ah_radio == AR5K_RF5111) ||
@@ -823,7 +827,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
AR5K_RF_DB_2GHZ, true);
/* RF5111 always needs OB/DB for 5GHz, even if we use 2GHz */
- } else if ((channel->hw_value & CHANNEL_5GHZ) ||
+ } else if ((channel->band == IEEE80211_BAND_5GHZ) ||
(ah->ah_radio == AR5K_RF5111)) {
/* For 11a, Turbo and XR we need to choose
@@ -855,7 +859,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
if (ah->ah_radio == AR5K_RF5111) {
/* Set gain_F settings according to current step */
- if (channel->hw_value & CHANNEL_OFDM) {
+ if (channel->hw_value != AR5K_MODE_11B) {
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL,
AR5K_PHY_FRAME_CTL_TX_CLIP,
@@ -912,7 +916,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
if (ah->ah_radio == AR5K_RF5112) {
/* Set gain_F settings according to current step */
- if (channel->hw_value & CHANNEL_OFDM) {
+ if (channel->hw_value != AR5K_MODE_11B) {
ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[0],
AR5K_RF_MIXGAIN_OVR, true);
@@ -970,17 +974,20 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
}
/* Lower synth voltage on Rev 2 */
- ath5k_hw_rfb_op(ah, rf_regs, 2,
- AR5K_RF_HIGH_VC_CP, true);
+ if (ah->ah_radio == AR5K_RF5112 &&
+ (ah->ah_radio_5ghz_revision & AR5K_SREV_REV) > 0) {
+ ath5k_hw_rfb_op(ah, rf_regs, 2,
+ AR5K_RF_HIGH_VC_CP, true);
- ath5k_hw_rfb_op(ah, rf_regs, 2,
- AR5K_RF_MID_VC_CP, true);
+ ath5k_hw_rfb_op(ah, rf_regs, 2,
+ AR5K_RF_MID_VC_CP, true);
- ath5k_hw_rfb_op(ah, rf_regs, 2,
- AR5K_RF_LOW_VC_CP, true);
+ ath5k_hw_rfb_op(ah, rf_regs, 2,
+ AR5K_RF_LOW_VC_CP, true);
- ath5k_hw_rfb_op(ah, rf_regs, 2,
- AR5K_RF_PUSH_UP, true);
+ ath5k_hw_rfb_op(ah, rf_regs, 2,
+ AR5K_RF_PUSH_UP, true);
+ }
/* Decrease power consumption on 5213+ BaseBand */
if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
@@ -1021,7 +1028,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
}
if (ah->ah_radio == AR5K_RF5413 &&
- channel->hw_value & CHANNEL_2GHZ) {
+ channel->band == IEEE80211_BAND_2GHZ) {
ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_DERBY_CHAN_SEL_MODE,
true);
@@ -1133,7 +1140,7 @@ static int ath5k_hw_rf5111_channel(struct ath5k_hw *ah,
*/
data0 = data1 = 0;
- if (channel->hw_value & CHANNEL_2GHZ) {
+ if (channel->band == IEEE80211_BAND_2GHZ) {
/* Map 2GHz channel to 5GHz Atheros channel ID */
ret = ath5k_hw_rf5111_chan2athchan(
ieee80211_frequency_to_channel(channel->center_freq),
@@ -1259,12 +1266,11 @@ static int ath5k_hw_channel(struct ath5k_hw *ah,
{
int ret;
/*
- * Check bounds supported by the PHY (we don't care about regultory
- * restrictions at this point). Note: hw_value already has the band
- * (CHANNEL_2GHZ, or CHANNEL_5GHZ) so we inform ath5k_channel_ok()
- * of the band by that */
- if (!ath5k_channel_ok(ah, channel->center_freq, channel->hw_value)) {
- ATH5K_ERR(ah->ah_sc,
+ * Check bounds supported by the PHY (we don't care about regulatory
+ * restrictions at this point).
+ */
+ if (!ath5k_channel_ok(ah, channel)) {
+ ATH5K_ERR(ah,
"channel frequency (%u MHz) out of supported "
"band range\n",
channel->center_freq);
@@ -1331,7 +1337,7 @@ void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah)
static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor)
{
struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist;
- hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX-1);
+ hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX - 1);
hist->nfval[hist->index] = noise_floor;
}
@@ -1344,18 +1350,18 @@ static s16 ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah)
memcpy(sort, ah->ah_nfcal_hist.nfval, sizeof(sort));
for (i = 0; i < ATH5K_NF_CAL_HIST_MAX - 1; i++) {
for (j = 1; j < ATH5K_NF_CAL_HIST_MAX - i; j++) {
- if (sort[j] > sort[j-1]) {
+ if (sort[j] > sort[j - 1]) {
tmp = sort[j];
- sort[j] = sort[j-1];
- sort[j-1] = tmp;
+ sort[j] = sort[j - 1];
+ sort[j - 1] = tmp;
}
}
}
for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) {
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
"cal %d:%d\n", i, sort[i]);
}
- return sort[(ATH5K_NF_CAL_HIST_MAX-1) / 2];
+ return sort[(ATH5K_NF_CAL_HIST_MAX - 1) / 2];
}
/*
@@ -1378,7 +1384,7 @@ void ath5k_hw_update_noise_floor(struct ath5k_hw *ah)
/* keep last value if calibration hasn't completed */
if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) {
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
"NF did not complete in calibration window\n");
return;
@@ -1391,7 +1397,7 @@ void ath5k_hw_update_noise_floor(struct ath5k_hw *ah)
threshold = ee->ee_noise_floor_thr[ee_mode];
if (nf > threshold) {
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
"noise floor failure detected; "
"read %d, threshold %d\n",
nf, threshold);
@@ -1428,7 +1434,7 @@ void ath5k_hw_update_noise_floor(struct ath5k_hw *ah)
ah->ah_noise_floor = nf;
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
"noise floor calibrated: %d\n", nf);
}
@@ -1516,7 +1522,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
ath5k_hw_reg_write(ah, phy_sat, AR5K_PHY_ADCSAT);
if (ret) {
- ATH5K_ERR(ah->ah_sc, "calibration timeout (%uMHz)\n",
+ ATH5K_ERR(ah, "calibration timeout (%uMHz)\n",
channel->center_freq);
return ret;
}
@@ -1551,7 +1557,7 @@ ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah)
iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR);
i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I);
q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q);
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
"iq_corr:%x i_pwr:%x q_pwr:%x", iq_corr, i_pwr, q_pwr);
if (i_pwr && q_pwr)
break;
@@ -1577,7 +1583,7 @@ ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah)
q_coff = (i_pwr / q_coffd) - 128;
q_coff = clamp(q_coff, -16, 15); /* signed 5 bit */
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
"new I:%d Q:%d (i_coffd:%x q_coffd:%x)",
i_coff, q_coff, i_coffd, q_coffd);
@@ -1604,11 +1610,13 @@ int ath5k_hw_phy_calibrate(struct ath5k_hw *ah,
int ret;
if (ah->ah_radio == AR5K_RF5110)
- ret = ath5k_hw_rf5110_calibrate(ah, channel);
- else {
- ret = ath5k_hw_rf511x_iq_calibrate(ah);
+ return ath5k_hw_rf5110_calibrate(ah, channel);
+
+ ret = ath5k_hw_rf511x_iq_calibrate(ah);
+
+ if ((ah->ah_radio == AR5K_RF5111 || ah->ah_radio == AR5K_RF5112) &&
+ (channel->hw_value != AR5K_MODE_11B))
ath5k_hw_request_rfgain_probe(ah);
- }
return ret;
}
@@ -1634,7 +1642,7 @@ ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
/* Convert current frequency to fbin value (the same way channels
* are stored on EEPROM, check out ath5k_eeprom_bin2freq) and scale
* up by 2 so we can compare it later */
- if (channel->hw_value & CHANNEL_2GHZ) {
+ if (channel->band == IEEE80211_BAND_2GHZ) {
chan_fbin = (channel->center_freq - 2300) * 10;
freq_band = AR5K_EEPROM_BAND_2GHZ;
} else {
@@ -1696,7 +1704,7 @@ ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
spur_freq_sigma_delta = (spur_delta_phase >> 10);
symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4;
default:
- if (channel->hw_value == CHANNEL_A) {
+ if (channel->band == IEEE80211_BAND_5GHZ) {
/* Both sample_freq and chip_freq are 40MHz */
spur_delta_phase = (spur_offset << 17) / 25;
spur_freq_sigma_delta =
@@ -1815,7 +1823,7 @@ ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
} else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) &
AR5K_PHY_IQ_SPUR_FILT_EN) {
- /* Clean up spur mitigation settings and disable fliter */
+ /* Clean up spur mitigation settings and disable filter */
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
AR5K_PHY_BIN_MASK_CTL_RATE, 0);
AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_IQ,
@@ -1960,7 +1968,7 @@ ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode)
ee_mode = ath5k_eeprom_mode_from_channel(channel);
if (ee_mode < 0) {
- ATH5K_ERR(ah->ah_sc,
+ ATH5K_ERR(ah,
"invalid channel: %d\n", channel->center_freq);
return;
}
@@ -2080,7 +2088,7 @@ ath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right,
* always 1 instead of 1.25, 1.75 etc). We scale up by 100
* to have some accuracy both for 0.5 and 0.25 steps.
*/
- ratio = ((100 * y_right - 100 * y_left)/(x_right - x_left));
+ ratio = ((100 * y_right - 100 * y_left) / (x_right - x_left));
/* Now scale down to be in range */
result = y_left + (ratio * (target - x_left) / 100);
@@ -2159,7 +2167,7 @@ ath5k_create_power_curve(s16 pmin, s16 pmax,
u8 *vpd_table, u8 type)
{
u8 idx[2] = { 0, 1 };
- s16 pwr_i = 2*pmin;
+ s16 pwr_i = 2 * pmin;
int i;
if (num_points < 2)
@@ -2219,15 +2227,20 @@ ath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah,
idx_l = 0;
idx_r = 0;
- if (!(channel->hw_value & CHANNEL_OFDM)) {
+ switch (channel->hw_value) {
+ case AR5K_EEPROM_MODE_11A:
+ pcinfo = ee->ee_pwr_cal_a;
+ mode = AR5K_EEPROM_MODE_11A;
+ break;
+ case AR5K_EEPROM_MODE_11B:
pcinfo = ee->ee_pwr_cal_b;
mode = AR5K_EEPROM_MODE_11B;
- } else if (channel->hw_value & CHANNEL_2GHZ) {
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ default:
pcinfo = ee->ee_pwr_cal_g;
mode = AR5K_EEPROM_MODE_11G;
- } else {
- pcinfo = ee->ee_pwr_cal_a;
- mode = AR5K_EEPROM_MODE_11A;
+ break;
}
max = ee->ee_n_piers[mode] - 1;
@@ -2296,15 +2309,20 @@ ath5k_get_rate_pcal_data(struct ath5k_hw *ah,
idx_l = 0;
idx_r = 0;
- if (!(channel->hw_value & CHANNEL_OFDM)) {
+ switch (channel->hw_value) {
+ case AR5K_MODE_11A:
+ rpinfo = ee->ee_rate_tpwr_a;
+ mode = AR5K_EEPROM_MODE_11A;
+ break;
+ case AR5K_MODE_11B:
rpinfo = ee->ee_rate_tpwr_b;
mode = AR5K_EEPROM_MODE_11B;
- } else if (channel->hw_value & CHANNEL_2GHZ) {
+ break;
+ case AR5K_MODE_11G:
+ default:
rpinfo = ee->ee_rate_tpwr_g;
mode = AR5K_EEPROM_MODE_11G;
- } else {
- rpinfo = ee->ee_rate_tpwr_a;
- mode = AR5K_EEPROM_MODE_11A;
+ break;
}
max = ee->ee_rate_target_pwr_num[mode] - 1;
@@ -2385,24 +2403,22 @@ ath5k_get_max_ctl_power(struct ath5k_hw *ah,
ctl_mode = ath_regd_get_band_ctl(regulatory, channel->band);
- switch (channel->hw_value & CHANNEL_MODES) {
- case CHANNEL_A:
+ switch (channel->hw_value) {
+ case AR5K_MODE_11A:
if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
ctl_mode |= AR5K_CTL_TURBO;
else
ctl_mode |= AR5K_CTL_11A;
break;
- case CHANNEL_G:
+ case AR5K_MODE_11G:
if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
ctl_mode |= AR5K_CTL_TURBOG;
else
ctl_mode |= AR5K_CTL_11G;
break;
- case CHANNEL_B:
+ case AR5K_MODE_11B:
ctl_mode |= AR5K_CTL_11B;
break;
- case CHANNEL_XR:
- /* Fall through */
default:
return;
}
@@ -2437,7 +2453,7 @@ ath5k_get_max_ctl_power(struct ath5k_hw *ah,
}
if (edge_pwr)
- ah->ah_txpower.txp_max_pwr = 4*min(edge_pwr, max_chan_pwr);
+ ah->ah_txpower.txp_max_pwr = 4 * min(edge_pwr, max_chan_pwr);
}
@@ -2456,7 +2472,7 @@ static void
ath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min,
s16 *table_max)
{
- u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
+ u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
u8 *pcdac_tmp = ah->ah_txpower.tmpL[0];
u8 pcdac_0, pcdac_n, pcdac_i, pwr_idx, i;
s16 min_pwr, max_pwr;
@@ -2475,8 +2491,8 @@ ath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min,
/* Copy values from pcdac_tmp */
pwr_idx = min_pwr;
- for (i = 0 ; pwr_idx <= max_pwr &&
- pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) {
+ for (i = 0; pwr_idx <= max_pwr &&
+ pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) {
pcdac_out[pcdac_i++] = pcdac_tmp[i];
pwr_idx++;
}
@@ -2502,7 +2518,7 @@ static void
ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
s16 *table_max, u8 pdcurves)
{
- u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
+ u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
u8 *pcdac_low_pwr;
u8 *pcdac_high_pwr;
u8 *pcdac_tmp;
@@ -2510,8 +2526,8 @@ ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
s16 max_pwr_idx;
s16 min_pwr_idx;
s16 mid_pwr_idx = 0;
- /* Edge flag turs on the 7nth bit on the PCDAC
- * to delcare the higher power curve (force values
+ /* Edge flag turns on the 7nth bit on the PCDAC
+ * to declare the higher power curve (force values
* to be greater than 64). If we only have one curve
* we don't need to set this, if we have 2 curves and
* fill the table backwards this can also be used to
@@ -2552,7 +2568,7 @@ ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
}
/* This is used when setting tx power*/
- ah->ah_txpower.txp_min_idx = min_pwr_idx/2;
+ ah->ah_txpower.txp_min_idx = min_pwr_idx / 2;
/* Fill Power to PCDAC table backwards */
pwr = max_pwr_idx;
@@ -2561,14 +2577,14 @@ ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
* edge flag and set pcdac_tmp to lower
* power curve.*/
if (edge_flag == 0x40 &&
- (2*pwr <= (table_max[1] - table_min[0]) || pwr == 0)) {
+ (2 * pwr <= (table_max[1] - table_min[0]) || pwr == 0)) {
edge_flag = 0x00;
pcdac_tmp = pcdac_low_pwr;
- pwr = mid_pwr_idx/2;
+ pwr = mid_pwr_idx / 2;
}
/* Don't go below 1, extrapolate below if we have
- * already swithced to the lower power curve -or
+ * already switched to the lower power curve -or
* we only have one curve and edge_flag is zero
* anyway */
if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) {
@@ -2596,7 +2612,7 @@ ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
static void
ath5k_write_pcdac_table(struct ath5k_hw *ah)
{
- u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
+ u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
int i;
/*
@@ -2604,8 +2620,8 @@ ath5k_write_pcdac_table(struct ath5k_hw *ah)
*/
for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
ath5k_hw_reg_write(ah,
- (((pcdac_out[2*i + 0] << 8 | 0xff) & 0xffff) << 0) |
- (((pcdac_out[2*i + 1] << 8 | 0xff) & 0xffff) << 16),
+ (((pcdac_out[2 * i + 0] << 8 | 0xff) & 0xffff) << 0) |
+ (((pcdac_out[2 * i + 1] << 8 | 0xff) & 0xffff) << 16),
AR5K_PHY_PCDAC_TXPOWER(i));
}
}
@@ -2788,12 +2804,8 @@ ath5k_write_pwr_to_pdadc_table(struct ath5k_hw *ah, u8 ee_mode)
* Write TX power values
*/
for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
- ath5k_hw_reg_write(ah,
- ((pdadc_out[4*i + 0] & 0xff) << 0) |
- ((pdadc_out[4*i + 1] & 0xff) << 8) |
- ((pdadc_out[4*i + 2] & 0xff) << 16) |
- ((pdadc_out[4*i + 3] & 0xff) << 24),
- AR5K_PHY_PDADC_TXPOWER(i));
+ u32 val = get_unaligned_le32(&pdadc_out[4 * i]);
+ ath5k_hw_reg_write(ah, val, AR5K_PHY_PDADC_TXPOWER(i));
}
}
@@ -2805,7 +2817,7 @@ ath5k_write_pwr_to_pdadc_table(struct ath5k_hw *ah, u8 ee_mode)
/*
* This is the main function that uses all of the above
* to set PCDAC/PDADC table on hw for the current channel.
- * This table is used for tx power calibration on the basband,
+ * This table is used for tx power calibration on the baseband,
* without it we get weird tx power levels and in some cases
* distorted spectral mask
*/
@@ -3116,13 +3128,13 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
int ret;
if (txpower > AR5K_TUNE_MAX_TXPOWER) {
- ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower);
+ ATH5K_ERR(ah, "invalid tx power: %u\n", txpower);
return -EINVAL;
}
ee_mode = ath5k_eeprom_mode_from_channel(channel);
if (ee_mode < 0) {
- ATH5K_ERR(ah->ah_sc,
+ ATH5K_ERR(ah,
"invalid channel: %d\n", channel->center_freq);
return -EINVAL;
}
@@ -3223,7 +3235,7 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower)
{
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_TXPOWER,
+ ATH5K_DBG(ah, ATH5K_DEBUG_TXPOWER,
"changing txpower to %d\n", txpower);
return ath5k_hw_txpower(ah, ah->ah_current_channel, txpower);
@@ -3289,7 +3301,7 @@ int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
/* Write OFDM timings on 5212*/
if (ah->ah_version == AR5K_AR5212 &&
- channel->hw_value & CHANNEL_OFDM) {
+ channel->hw_value != AR5K_MODE_11B) {
ret = ath5k_hw_write_ofdm_timings(ah, channel);
if (ret)
@@ -3434,7 +3446,7 @@ int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
* during ath5k_phy_calibrate) */
if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
AR5K_PHY_AGCCTL_CAL, 0, false)) {
- ATH5K_ERR(ah->ah_sc, "gain calibration timeout (%uMHz)\n",
+ ATH5K_ERR(ah, "gain calibration timeout (%uMHz)\n",
channel->center_freq);
}
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c
index b18c5021aac3..776654228eaa 100644
--- a/drivers/net/wireless/ath/ath5k/qcu.c
+++ b/drivers/net/wireless/ath/ath5k/qcu.c
@@ -23,7 +23,6 @@ Queue Control Unit, DFS Control Unit Functions
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
-#include "base.h"
/******************\
@@ -185,13 +184,6 @@ int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
case AR5K_TX_QUEUE_CAB:
queue = AR5K_TX_QUEUE_ID_CAB;
break;
- case AR5K_TX_QUEUE_XR_DATA:
- if (ah->ah_version != AR5K_AR5212)
- ATH5K_ERR(ah->ah_sc,
- "XR data queues only supported in"
- " 5212!\n");
- queue = AR5K_TX_QUEUE_ID_XR_DATA;
- break;
default:
return -EINVAL;
}
@@ -510,7 +502,6 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
{
struct ieee80211_channel *channel = ah->ah_current_channel;
- struct ath5k_softc *sc = ah->ah_sc;
struct ieee80211_rate *rate;
u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock;
u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time);
@@ -545,10 +536,10 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
*
* Also we have different lowest rate for 802.11a
*/
- if (channel->hw_value & CHANNEL_5GHZ)
- rate = &sc->sbands[IEEE80211_BAND_5GHZ].bitrates[0];
+ if (channel->band == IEEE80211_BAND_5GHZ)
+ rate = &ah->sbands[IEEE80211_BAND_5GHZ].bitrates[0];
else
- rate = &sc->sbands[IEEE80211_BAND_2GHZ].bitrates[0];
+ rate = &ah->sbands[IEEE80211_BAND_2GHZ].bitrates[0];
ack_tx_time = ath5k_hw_get_frame_duration(ah, 10, rate, false);
@@ -622,7 +613,7 @@ int ath5k_hw_init_queues(struct ath5k_hw *ah)
for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) {
ret = ath5k_hw_reset_tx_queue(ah, i);
if (ret) {
- ATH5K_ERR(ah->ah_sc,
+ ATH5K_ERR(ah,
"failed to reset TX queue #%d\n", i);
return ret;
}
diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h
index d12b827033c1..f5c1000045d3 100644
--- a/drivers/net/wireless/ath/ath5k/reg.h
+++ b/drivers/net/wireless/ath/ath5k/reg.h
@@ -72,7 +72,7 @@
#define AR5K_CFG_SWRD 0x00000004 /* Byte-swap RX descriptor */
#define AR5K_CFG_SWRB 0x00000008 /* Byte-swap RX buffer */
#define AR5K_CFG_SWRG 0x00000010 /* Byte-swap Register access */
-#define AR5K_CFG_IBSS 0x00000020 /* 0-BSS, 1-IBSS [5211+] */
+#define AR5K_CFG_IBSS 0x00000020 /* 0-BSS, 1-IBSS [5211+] */
#define AR5K_CFG_PHY_OK 0x00000100 /* [5211+] */
#define AR5K_CFG_EEBS 0x00000200 /* EEPROM is busy */
#define AR5K_CFG_CLKGD 0x00000400 /* Clock gated (Disable dynamic clock) */
@@ -170,7 +170,7 @@
#define AR5K_TXCFG_SDMAMR_S 0
#define AR5K_TXCFG_B_MODE 0x00000008 /* Set b mode for 5111 (enable 2111) */
#define AR5K_TXCFG_TXFSTP 0x00000008 /* TX DMA full Stop [5210] */
-#define AR5K_TXCFG_TXFULL 0x000003f0 /* TX Triger level mask */
+#define AR5K_TXCFG_TXFULL 0x000003f0 /* TX Trigger level mask */
#define AR5K_TXCFG_TXFULL_S 4
#define AR5K_TXCFG_TXFULL_0B 0x00000000
#define AR5K_TXCFG_TXFULL_64B 0x00000010
@@ -283,16 +283,16 @@
*/
#define AR5K_ISR 0x001c /* Register Address [5210] */
#define AR5K_PISR 0x0080 /* Register Address [5211+] */
-#define AR5K_ISR_RXOK 0x00000001 /* Frame successfuly received */
+#define AR5K_ISR_RXOK 0x00000001 /* Frame successfully received */
#define AR5K_ISR_RXDESC 0x00000002 /* RX descriptor request */
#define AR5K_ISR_RXERR 0x00000004 /* Receive error */
#define AR5K_ISR_RXNOFRM 0x00000008 /* No frame received (receive timeout) */
#define AR5K_ISR_RXEOL 0x00000010 /* Empty RX descriptor */
#define AR5K_ISR_RXORN 0x00000020 /* Receive FIFO overrun */
-#define AR5K_ISR_TXOK 0x00000040 /* Frame successfuly transmited */
+#define AR5K_ISR_TXOK 0x00000040 /* Frame successfully transmitted */
#define AR5K_ISR_TXDESC 0x00000080 /* TX descriptor request */
#define AR5K_ISR_TXERR 0x00000100 /* Transmit error */
-#define AR5K_ISR_TXNOFRM 0x00000200 /* No frame transmited (transmit timeout) */
+#define AR5K_ISR_TXNOFRM 0x00000200 /* No frame transmitted (transmit timeout) */
#define AR5K_ISR_TXEOL 0x00000400 /* Empty TX descriptor */
#define AR5K_ISR_TXURN 0x00000800 /* Transmit FIFO underrun */
#define AR5K_ISR_MIB 0x00001000 /* Update MIB counters */
@@ -303,7 +303,7 @@
#define AR5K_ISR_BRSSI 0x00020000 /* Beacon rssi below threshold (?) */
#define AR5K_ISR_BMISS 0x00040000 /* Beacon missed */
#define AR5K_ISR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */
-#define AR5K_ISR_BNR 0x00100000 /* Beacon not ready [5211+] */
+#define AR5K_ISR_BNR 0x00100000 /* Beacon not ready [5211+] */
#define AR5K_ISR_MCABT 0x00100000 /* Master Cycle Abort [5210] */
#define AR5K_ISR_RXCHIRP 0x00200000 /* CHIRP Received [5212+] */
#define AR5K_ISR_SSERR 0x00200000 /* Signaled System Error [5210] */
@@ -377,16 +377,16 @@
*/
#define AR5K_IMR 0x0020 /* Register Address [5210] */
#define AR5K_PIMR 0x00a0 /* Register Address [5211+] */
-#define AR5K_IMR_RXOK 0x00000001 /* Frame successfuly received*/
+#define AR5K_IMR_RXOK 0x00000001 /* Frame successfully received*/
#define AR5K_IMR_RXDESC 0x00000002 /* RX descriptor request*/
#define AR5K_IMR_RXERR 0x00000004 /* Receive error*/
#define AR5K_IMR_RXNOFRM 0x00000008 /* No frame received (receive timeout)*/
#define AR5K_IMR_RXEOL 0x00000010 /* Empty RX descriptor*/
#define AR5K_IMR_RXORN 0x00000020 /* Receive FIFO overrun*/
-#define AR5K_IMR_TXOK 0x00000040 /* Frame successfuly transmited*/
+#define AR5K_IMR_TXOK 0x00000040 /* Frame successfully transmitted*/
#define AR5K_IMR_TXDESC 0x00000080 /* TX descriptor request*/
#define AR5K_IMR_TXERR 0x00000100 /* Transmit error*/
-#define AR5K_IMR_TXNOFRM 0x00000200 /* No frame transmited (transmit timeout)*/
+#define AR5K_IMR_TXNOFRM 0x00000200 /* No frame transmitted (transmit timeout)*/
#define AR5K_IMR_TXEOL 0x00000400 /* Empty TX descriptor*/
#define AR5K_IMR_TXURN 0x00000800 /* Transmit FIFO underrun*/
#define AR5K_IMR_MIB 0x00001000 /* Update MIB counters*/
@@ -397,7 +397,7 @@
#define AR5K_IMR_BRSSI 0x00020000 /* Beacon rssi below threshold (?) */
#define AR5K_IMR_BMISS 0x00040000 /* Beacon missed*/
#define AR5K_IMR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */
-#define AR5K_IMR_BNR 0x00100000 /* Beacon not ready [5211+] */
+#define AR5K_IMR_BNR 0x00100000 /* Beacon not ready [5211+] */
#define AR5K_IMR_MCABT 0x00100000 /* Master Cycle Abort [5210] */
#define AR5K_IMR_RXCHIRP 0x00200000 /* CHIRP Received [5212+]*/
#define AR5K_IMR_SSERR 0x00200000 /* Signaled System Error [5210] */
@@ -601,7 +601,7 @@
* QCU misc registers
*/
#define AR5K_QCU_MISC_BASE 0x09c0 /* Register Address -Queue0 MISC */
-#define AR5K_QCU_MISC_FRSHED_M 0x0000000f /* Frame sheduling mask */
+#define AR5K_QCU_MISC_FRSHED_M 0x0000000f /* Frame scheduling mask */
#define AR5K_QCU_MISC_FRSHED_ASAP 0 /* ASAP */
#define AR5K_QCU_MISC_FRSHED_CBR 1 /* Constant Bit Rate */
#define AR5K_QCU_MISC_FRSHED_DBA_GT 2 /* DMA Beacon alert gated */
@@ -653,13 +653,13 @@
* registers [5211+]
*
* These registers control the various characteristics of each queue
- * for 802.11e (WME) combatibility so they go together with
+ * for 802.11e (WME) compatibility so they go together with
* QCU registers in pairs. For each queue we have a QCU mask register,
* (0x1000 - 0x102c), a local-IFS settings register (0x1040 - 0x106c),
* a retry limit register (0x1080 - 0x10ac), a channel time register
* (0x10c0 - 0x10ec), a misc-settings register (0x1100 - 0x112c) and
* a sequence number register (0x1140 - 0x116c). It seems that "global"
- * registers here afect all queues (see use of DCU_GBL_IFS_SLOT in ar5k).
+ * registers here affect all queues (see use of DCU_GBL_IFS_SLOT in ar5k).
* We use the same macros here for easier register access.
*
*/
@@ -779,7 +779,7 @@
* and it's used for generating pseudo-random
* number sequences.
*
- * (If i understand corectly, random numbers are
+ * (If i understand correctly, random numbers are
* used for idle sensing -multiplied with cwmin/max etc-)
*/
#define AR5K_DCU_GBL_IFS_MISC 0x10f0 /* Register Address */
@@ -1007,7 +1007,7 @@
#define AR5K_PCIE_WAEN 0x407c
/*
- * PCI-E Serializer/Desirializer
+ * PCI-E Serializer/Deserializer
* registers
*/
#define AR5K_PCIE_SERDES 0x4080
@@ -1227,7 +1227,7 @@
AR5K_USEC_5210 : AR5K_USEC_5211)
#define AR5K_USEC_1 0x0000007f /* clock cycles for 1us */
#define AR5K_USEC_1_S 0
-#define AR5K_USEC_32 0x00003f80 /* clock cycles for 1us while on 32Mhz clock */
+#define AR5K_USEC_32 0x00003f80 /* clock cycles for 1us while on 32MHz clock */
#define AR5K_USEC_32_S 7
#define AR5K_USEC_TX_LATENCY_5211 0x007fc000
#define AR5K_USEC_TX_LATENCY_5211_S 14
@@ -1328,16 +1328,16 @@
#define AR5K_RX_FILTER_5211 0x803c /* Register Address [5211+] */
#define AR5K_RX_FILTER (ah->ah_version == AR5K_AR5210 ? \
AR5K_RX_FILTER_5210 : AR5K_RX_FILTER_5211)
-#define AR5K_RX_FILTER_UCAST 0x00000001 /* Don't filter unicast frames */
-#define AR5K_RX_FILTER_MCAST 0x00000002 /* Don't filter multicast frames */
-#define AR5K_RX_FILTER_BCAST 0x00000004 /* Don't filter broadcast frames */
-#define AR5K_RX_FILTER_CONTROL 0x00000008 /* Don't filter control frames */
-#define AR5K_RX_FILTER_BEACON 0x00000010 /* Don't filter beacon frames */
-#define AR5K_RX_FILTER_PROM 0x00000020 /* Set promiscuous mode */
-#define AR5K_RX_FILTER_XRPOLL 0x00000040 /* Don't filter XR poll frame [5212+] */
+#define AR5K_RX_FILTER_UCAST 0x00000001 /* Don't filter unicast frames */
+#define AR5K_RX_FILTER_MCAST 0x00000002 /* Don't filter multicast frames */
+#define AR5K_RX_FILTER_BCAST 0x00000004 /* Don't filter broadcast frames */
+#define AR5K_RX_FILTER_CONTROL 0x00000008 /* Don't filter control frames */
+#define AR5K_RX_FILTER_BEACON 0x00000010 /* Don't filter beacon frames */
+#define AR5K_RX_FILTER_PROM 0x00000020 /* Set promiscuous mode */
+#define AR5K_RX_FILTER_XRPOLL 0x00000040 /* Don't filter XR poll frame [5212+] */
#define AR5K_RX_FILTER_PROBEREQ 0x00000080 /* Don't filter probe requests [5212+] */
#define AR5K_RX_FILTER_PHYERR_5212 0x00000100 /* Don't filter phy errors [5212+] */
-#define AR5K_RX_FILTER_RADARERR_5212 0x00000200 /* Don't filter phy radar errors [5212+] */
+#define AR5K_RX_FILTER_RADARERR_5212 0x00000200 /* Don't filter phy radar errors [5212+] */
#define AR5K_RX_FILTER_PHYERR_5211 0x00000040 /* [5211] */
#define AR5K_RX_FILTER_RADARERR_5211 0x00000080 /* [5211] */
#define AR5K_RX_FILTER_PHYERR \
@@ -1461,7 +1461,7 @@
* ADDAC test register [5211+]
*/
#define AR5K_ADDAC_TEST 0x8054 /* Register Address */
-#define AR5K_ADDAC_TEST_TXCONT 0x00000001 /* Test continuous tx */
+#define AR5K_ADDAC_TEST_TXCONT 0x00000001 /* Test continuous tx */
#define AR5K_ADDAC_TEST_TST_MODE 0x00000002 /* Test mode */
#define AR5K_ADDAC_TEST_LOOP_EN 0x00000004 /* Enable loop */
#define AR5K_ADDAC_TEST_LOOP_LEN 0x00000008 /* Loop length (field) */
@@ -1632,7 +1632,7 @@
#define AR5K_SLEEP0_NEXT_DTIM 0x0007ffff /* Mask for next DTIM (?) */
#define AR5K_SLEEP0_NEXT_DTIM_S 0
#define AR5K_SLEEP0_ASSUME_DTIM 0x00080000 /* Assume DTIM */
-#define AR5K_SLEEP0_ENH_SLEEP_EN 0x00100000 /* Enable enchanced sleep control */
+#define AR5K_SLEEP0_ENH_SLEEP_EN 0x00100000 /* Enable enhanced sleep control */
#define AR5K_SLEEP0_CABTO 0xff000000 /* Mask for CAB Time Out */
#define AR5K_SLEEP0_CABTO_S 24
@@ -1657,7 +1657,7 @@
/*
* TX power control (TPC) register
*
- * XXX: PCDAC steps (0.5dbm) or DBM ?
+ * XXX: PCDAC steps (0.5dBm) or dBm ?
*
*/
#define AR5K_TXPC 0x80e8 /* Register Address */
@@ -1673,7 +1673,7 @@
/*
* Profile count registers
*
- * These registers can be cleared and freezed with ATH5K_MIBC, but they do not
+ * These registers can be cleared and frozen with ATH5K_MIBC, but they do not
* generate a MIB interrupt.
* Instead of overflowing, they shift by one bit to the right. All registers
* shift together, i.e. when one reaches the max, all shift at the same time by
@@ -1838,7 +1838,7 @@
#define AR5K_PHY_TST2_TRIG_SEL 0x00000007 /* Trigger select (?)*/
#define AR5K_PHY_TST2_TRIG 0x00000010 /* Trigger (?) */
#define AR5K_PHY_TST2_CBUS_MODE 0x00000060 /* Cardbus mode (?) */
-#define AR5K_PHY_TST2_CLK32 0x00000400 /* CLK_OUT is CLK32 (32Khz external) */
+#define AR5K_PHY_TST2_CLK32 0x00000400 /* CLK_OUT is CLK32 (32kHz external) */
#define AR5K_PHY_TST2_CHANCOR_DUMP_EN 0x00000800 /* Enable Chancor dump (?) */
#define AR5K_PHY_TST2_EVEN_CHANCOR_DUMP 0x00001000 /* Even Chancor dump (?) */
#define AR5K_PHY_TST2_RFSILENT_EN 0x00002000 /* Enable RFSILENT */
@@ -2002,7 +2002,7 @@
#define AR5K_PHY_AGCCTL_OFDM_DIV_DIS 0x00000008 /* Disable antenna diversity on OFDM modes */
#define AR5K_PHY_AGCCTL_NF_EN 0x00008000 /* Enable nf calibration to happen (?) */
#define AR5K_PHY_AGCTL_FLTR_CAL 0x00010000 /* Allow filter calibration (?) */
-#define AR5K_PHY_AGCCTL_NF_NOUPDATE 0x00020000 /* Don't update nf automaticaly */
+#define AR5K_PHY_AGCCTL_NF_NOUPDATE 0x00020000 /* Don't update nf automatically */
/*
* PHY noise floor status register (CCA = Clear Channel Assessment)
@@ -2038,7 +2038,7 @@
#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_S 24
/* Low thresholds */
-#define AR5K_PHY_WEAK_OFDM_LOW_THR 0x986c
+#define AR5K_PHY_WEAK_OFDM_LOW_THR 0x986c
#define AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN 0x00000001
#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT 0x00003f00
#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT_S 8
@@ -2089,7 +2089,7 @@
*
* It's obvious from the code that 0x989c is the buffer register but
* for the other special registers that we write to after sending each
- * packet, i have no idea. So i'll name them BUFFER_CONTROL_X registers
+ * packet, i have no idea. So I'll name them BUFFER_CONTROL_X registers
* for now. It's interesting that they are also used for some other operations.
*/
@@ -2259,12 +2259,13 @@
#define AR5K_PHY_FRAME_CTL_ILLLEN_ERR 0x08000000 /* Illegal length */
#define AR5K_PHY_FRAME_CTL_SERVICE_ERR 0x20000000
#define AR5K_PHY_FRAME_CTL_TXURN_ERR 0x40000000 /* TX underrun */
-#define AR5K_PHY_FRAME_CTL_INI AR5K_PHY_FRAME_CTL_SERVICE_ERR | \
- AR5K_PHY_FRAME_CTL_TXURN_ERR | \
- AR5K_PHY_FRAME_CTL_ILLLEN_ERR | \
- AR5K_PHY_FRAME_CTL_ILLRATE_ERR | \
- AR5K_PHY_FRAME_CTL_PARITY_ERR | \
- AR5K_PHY_FRAME_CTL_TIMING_ERR
+#define AR5K_PHY_FRAME_CTL_INI \
+ (AR5K_PHY_FRAME_CTL_SERVICE_ERR | \
+ AR5K_PHY_FRAME_CTL_TXURN_ERR | \
+ AR5K_PHY_FRAME_CTL_ILLLEN_ERR | \
+ AR5K_PHY_FRAME_CTL_ILLRATE_ERR | \
+ AR5K_PHY_FRAME_CTL_PARITY_ERR | \
+ AR5K_PHY_FRAME_CTL_TIMING_ERR)
/*
* PHY Tx Power adjustment register [5212A+]
@@ -2281,22 +2282,22 @@
#define AR5K_PHY_RADAR 0x9954
#define AR5K_PHY_RADAR_ENABLE 0x00000001
#define AR5K_PHY_RADAR_DISABLE 0x00000000
-#define AR5K_PHY_RADAR_INBANDTHR 0x0000003e /* Inband threshold
+#define AR5K_PHY_RADAR_INBANDTHR 0x0000003e /* Inband threshold
5-bits, units unknown {0..31}
(? MHz ?) */
#define AR5K_PHY_RADAR_INBANDTHR_S 1
-#define AR5K_PHY_RADAR_PRSSI_THR 0x00000fc0 /* Pulse RSSI/SNR threshold
+#define AR5K_PHY_RADAR_PRSSI_THR 0x00000fc0 /* Pulse RSSI/SNR threshold
6-bits, dBm range {0..63}
in dBm units. */
#define AR5K_PHY_RADAR_PRSSI_THR_S 6
-#define AR5K_PHY_RADAR_PHEIGHT_THR 0x0003f000 /* Pulse height threshold
+#define AR5K_PHY_RADAR_PHEIGHT_THR 0x0003f000 /* Pulse height threshold
6-bits, dBm range {0..63}
in dBm units. */
#define AR5K_PHY_RADAR_PHEIGHT_THR_S 12
-#define AR5K_PHY_RADAR_RSSI_THR 0x00fc0000 /* Radar RSSI/SNR threshold.
+#define AR5K_PHY_RADAR_RSSI_THR 0x00fc0000 /* Radar RSSI/SNR threshold.
6-bits, dBm range {0..63}
in dBm units. */
#define AR5K_PHY_RADAR_RSSI_THR_S 18
@@ -2339,7 +2340,7 @@
#define AR5K_PHY_RESTART_DIV_GC_S 18
/*
- * RF Bus access request register (for synth-oly channel switching)
+ * RF Bus access request register (for synth-only channel switching)
*/
#define AR5K_PHY_RFBUS_REQ 0x997C
#define AR5K_PHY_RFBUS_REQ_REQUEST 0x00000001
@@ -2381,7 +2382,7 @@
*/
#define AR5K_BB_GAIN_BASE 0x9b00 /* BaseBand Amplifier Gain table base address */
#define AR5K_BB_GAIN(_n) (AR5K_BB_GAIN_BASE + ((_n) << 2))
-#define AR5K_RF_GAIN_BASE 0x9a00 /* RF Amplrifier Gain table base address */
+#define AR5K_RF_GAIN_BASE 0x9a00 /* RF Amplifier Gain table base address */
#define AR5K_RF_GAIN(_n) (AR5K_RF_GAIN_BASE + ((_n) << 2))
/*
diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c
index 126a4eab35f3..2abac257b4b4 100644
--- a/drivers/net/wireless/ath/ath5k/reset.c
+++ b/drivers/net/wireless/ath/ath5k/reset.c
@@ -25,12 +25,11 @@
#include <asm/unaligned.h>
-#include <linux/pci.h> /* To determine if a card is pci-e */
+#include <linux/pci.h> /* To determine if a card is pci-e */
#include <linux/log2.h>
#include <linux/platform_device.h>
#include "ath5k.h"
#include "reg.h"
-#include "base.h"
#include "debug.h"
@@ -102,12 +101,18 @@ static void ath5k_hw_init_core_clock(struct ath5k_hw *ah)
/*
* Set core clock frequency
*/
- if (channel->hw_value & CHANNEL_5GHZ)
- clock = 40; /* 802.11a */
- else if (channel->hw_value & CHANNEL_CCK)
- clock = 22; /* 802.11b */
- else
- clock = 44; /* 802.11g */
+ switch (channel->hw_value) {
+ case AR5K_MODE_11A:
+ clock = 40;
+ break;
+ case AR5K_MODE_11B:
+ clock = 22;
+ break;
+ case AR5K_MODE_11G:
+ default:
+ clock = 44;
+ break;
+ }
/* Use clock multiplier for non-default
* bwmode */
@@ -142,10 +147,11 @@ static void ath5k_hw_init_core_clock(struct ath5k_hw *ah)
/* Set 32MHz USEC counter */
if ((ah->ah_radio == AR5K_RF5112) ||
- (ah->ah_radio == AR5K_RF5413) ||
- (ah->ah_radio == AR5K_RF2316) ||
- (ah->ah_radio == AR5K_RF2317))
- /* Remain on 40MHz clock ? */
+ (ah->ah_radio == AR5K_RF2413) ||
+ (ah->ah_radio == AR5K_RF5413) ||
+ (ah->ah_radio == AR5K_RF2316) ||
+ (ah->ah_radio == AR5K_RF2317))
+ /* Remain on 40MHz clock ? */
sclock = 40 - 1;
else
sclock = 32 - 1;
@@ -213,7 +219,7 @@ static void ath5k_hw_init_core_clock(struct ath5k_hw *ah)
usec_reg = (usec | sclock | txlat | rxlat);
ath5k_hw_reg_write(ah, usec_reg, AR5K_USEC);
- /* On 5112 set tx frane to tx data start delay */
+ /* On 5112 set tx frame to tx data start delay */
if (ah->ah_radio == AR5K_RF5112) {
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RF_CTL2,
AR5K_PHY_RF_CTL2_TXF2TXD_START,
@@ -233,7 +239,7 @@ static void ath5k_hw_init_core_clock(struct ath5k_hw *ah)
static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
- u32 scal, spending;
+ u32 scal, spending, sclock;
/* Only set 32KHz settings if we have an external
* 32KHz crystal present */
@@ -317,6 +323,15 @@ static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable)
/* Set up tsf increment on each cycle */
AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 1);
+
+ if ((ah->ah_radio == AR5K_RF5112) ||
+ (ah->ah_radio == AR5K_RF5413) ||
+ (ah->ah_radio == AR5K_RF2316) ||
+ (ah->ah_radio == AR5K_RF2317))
+ sclock = 40 - 1;
+ else
+ sclock = 32 - 1;
+ AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, sclock);
}
}
@@ -375,20 +390,20 @@ static int ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val)
static int ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags)
{
u32 mask = flags ? flags : ~0U;
- volatile u32 *reg;
+ u32 __iomem *reg;
u32 regval;
u32 val = 0;
/* ah->ah_mac_srev is not available at this point yet */
- if (ah->ah_sc->devid >= AR5K_SREV_AR2315_R6) {
- reg = (u32 *) AR5K_AR2315_RESET;
+ if (ah->devid >= AR5K_SREV_AR2315_R6) {
+ reg = (u32 __iomem *) AR5K_AR2315_RESET;
if (mask & AR5K_RESET_CTL_PCU)
val |= AR5K_AR2315_RESET_WMAC;
if (mask & AR5K_RESET_CTL_BASEBAND)
val |= AR5K_AR2315_RESET_BB_WARM;
} else {
- reg = (u32 *) AR5K_AR5312_RESET;
- if (to_platform_device(ah->ah_sc->dev)->id == 0) {
+ reg = (u32 __iomem *) AR5K_AR5312_RESET;
+ if (to_platform_device(ah->dev)->id == 0) {
if (mask & AR5K_RESET_CTL_PCU)
val |= AR5K_AR5312_RESET_WMAC0;
if (mask & AR5K_RESET_CTL_BASEBAND)
@@ -520,7 +535,7 @@ commit:
*/
int ath5k_hw_on_hold(struct ath5k_hw *ah)
{
- struct pci_dev *pdev = ah->ah_sc->pdev;
+ struct pci_dev *pdev = ah->pdev;
u32 bus_flags;
int ret;
@@ -530,7 +545,7 @@ int ath5k_hw_on_hold(struct ath5k_hw *ah)
/* Make sure device is awake */
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
if (ret) {
- ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n");
+ ATH5K_ERR(ah, "failed to wakeup the MAC Chip\n");
return ret;
}
@@ -539,7 +554,7 @@ int ath5k_hw_on_hold(struct ath5k_hw *ah)
*
* Note: putting PCI core on warm reset on PCI-E cards
* results card to hang and always return 0xffff... so
- * we ingore that flag for PCI-E cards. On PCI cards
+ * we ignore that flag for PCI-E cards. On PCI cards
* this flag gets cleared after 64 PCI clocks.
*/
bus_flags = (pdev && pci_is_pcie(pdev)) ? 0 : AR5K_RESET_CTL_PCI;
@@ -555,14 +570,14 @@ int ath5k_hw_on_hold(struct ath5k_hw *ah)
}
if (ret) {
- ATH5K_ERR(ah->ah_sc, "failed to put device on warm reset\n");
+ ATH5K_ERR(ah, "failed to put device on warm reset\n");
return -EIO;
}
/* ...wakeup again!*/
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
if (ret) {
- ATH5K_ERR(ah->ah_sc, "failed to put device on hold\n");
+ ATH5K_ERR(ah, "failed to put device on hold\n");
return ret;
}
@@ -571,10 +586,11 @@ int ath5k_hw_on_hold(struct ath5k_hw *ah)
/*
* Bring up MAC + PHY Chips and program PLL
+ * Channel is NULL for the initial wakeup.
*/
-int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
+int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel)
{
- struct pci_dev *pdev = ah->ah_sc->pdev;
+ struct pci_dev *pdev = ah->pdev;
u32 turbo, mode, clock, bus_flags;
int ret;
@@ -582,11 +598,11 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
mode = 0;
clock = 0;
- if ((ath5k_get_bus_type(ah) != ATH_AHB) || !initial) {
+ if ((ath5k_get_bus_type(ah) != ATH_AHB) || channel) {
/* Wakeup the device */
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
if (ret) {
- ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n");
+ ATH5K_ERR(ah, "failed to wakeup the MAC Chip\n");
return ret;
}
}
@@ -596,7 +612,7 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
*
* Note: putting PCI core on warm reset on PCI-E cards
* results card to hang and always return 0xffff... so
- * we ingore that flag for PCI-E cards. On PCI cards
+ * we ignore that flag for PCI-E cards. On PCI cards
* this flag gets cleared after 64 PCI clocks.
*/
bus_flags = (pdev && pci_is_pcie(pdev)) ? 0 : AR5K_RESET_CTL_PCI;
@@ -616,18 +632,18 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
}
if (ret) {
- ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip\n");
+ ATH5K_ERR(ah, "failed to reset the MAC Chip\n");
return -EIO;
}
/* ...wakeup again!...*/
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
if (ret) {
- ATH5K_ERR(ah->ah_sc, "failed to resume the MAC Chip\n");
+ ATH5K_ERR(ah, "failed to resume the MAC Chip\n");
return ret;
}
- /* ...reset configuration regiter on Wisoc ...
+ /* ...reset configuration register on Wisoc ...
* ...clear reset control register and pull device out of
* warm reset on others */
if (ath5k_get_bus_type(ah) == ATH_AHB)
@@ -636,13 +652,13 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
ret = ath5k_hw_nic_reset(ah, 0);
if (ret) {
- ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n");
+ ATH5K_ERR(ah, "failed to warm reset the MAC Chip\n");
return -EIO;
}
/* On initialization skip PLL programming since we don't have
* a channel / mode set yet */
- if (initial)
+ if (!channel)
return 0;
if (ah->ah_version != AR5K_AR5210) {
@@ -658,13 +674,13 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
clock = AR5K_PHY_PLL_RF5111; /*Zero*/
}
- if (flags & CHANNEL_2GHZ) {
+ if (channel->band == IEEE80211_BAND_2GHZ) {
mode |= AR5K_PHY_MODE_FREQ_2GHZ;
clock |= AR5K_PHY_PLL_44MHZ;
- if (flags & CHANNEL_CCK) {
+ if (channel->hw_value == AR5K_MODE_11B) {
mode |= AR5K_PHY_MODE_MOD_CCK;
- } else if (flags & CHANNEL_OFDM) {
+ } else {
/* XXX Dynamic OFDM/CCK is not supported by the
* AR5211 so we set MOD_OFDM for plain g (no
* CCK headers) operation. We need to test
@@ -676,35 +692,24 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
mode |= AR5K_PHY_MODE_MOD_OFDM;
else
mode |= AR5K_PHY_MODE_MOD_DYN;
- } else {
- ATH5K_ERR(ah->ah_sc,
- "invalid radio modulation mode\n");
- return -EINVAL;
}
- } else if (flags & CHANNEL_5GHZ) {
- mode |= AR5K_PHY_MODE_FREQ_5GHZ;
+ } else if (channel->band == IEEE80211_BAND_5GHZ) {
+ mode |= (AR5K_PHY_MODE_FREQ_5GHZ |
+ AR5K_PHY_MODE_MOD_OFDM);
/* Different PLL setting for 5413 */
if (ah->ah_radio == AR5K_RF5413)
clock = AR5K_PHY_PLL_40MHZ_5413;
else
clock |= AR5K_PHY_PLL_40MHZ;
-
- if (flags & CHANNEL_OFDM)
- mode |= AR5K_PHY_MODE_MOD_OFDM;
- else {
- ATH5K_ERR(ah->ah_sc,
- "invalid radio modulation mode\n");
- return -EINVAL;
- }
} else {
- ATH5K_ERR(ah->ah_sc, "invalid radio frequency mode\n");
+ ATH5K_ERR(ah, "invalid radio frequency mode\n");
return -EINVAL;
}
/*XXX: Can bwmode be used with dynamic mode ?
* (I don't think it supports 44MHz) */
- /* On 2425 initvals TURBO_SHORT is not pressent */
+ /* On 2425 initvals TURBO_SHORT is not present */
if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) {
turbo = AR5K_PHY_TURBO_MODE |
(ah->ah_radio == AR5K_RF2425) ? 0 :
@@ -812,7 +817,7 @@ static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
u32 data;
ath5k_hw_reg_write(ah, AR5K_PHY_CCKTXCTL_WORLD,
AR5K_PHY_CCKTXCTL);
- if (channel->hw_value & CHANNEL_5GHZ)
+ if (channel->band == IEEE80211_BAND_5GHZ)
data = 0xffb81020;
else
data = 0xffb80d20;
@@ -895,7 +900,7 @@ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
/* Set CCK to OFDM power delta on tx power
* adjustment register */
if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
- if (channel->hw_value == CHANNEL_G)
+ if (channel->hw_value == AR5K_MODE_11G)
ath5k_hw_reg_write(ah,
AR5K_REG_SM((ee->ee_cck_ofdm_gain_delta * -1),
AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA) |
@@ -1066,7 +1071,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
/* RF Bus grant won't work if we have pending
* frames */
if (ret && fast) {
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_RESET,
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"DMA didn't stop, falling back to normal reset\n");
fast = 0;
/* Non fatal, just continue with
@@ -1074,40 +1079,26 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
ret = 0;
}
- switch (channel->hw_value & CHANNEL_MODES) {
- case CHANNEL_A:
- mode = AR5K_MODE_11A;
+ mode = channel->hw_value;
+ switch (mode) {
+ case AR5K_MODE_11A:
break;
- case CHANNEL_G:
-
+ case AR5K_MODE_11G:
if (ah->ah_version <= AR5K_AR5211) {
- ATH5K_ERR(ah->ah_sc,
+ ATH5K_ERR(ah,
"G mode not available on 5210/5211");
return -EINVAL;
}
-
- mode = AR5K_MODE_11G;
break;
- case CHANNEL_B:
-
+ case AR5K_MODE_11B:
if (ah->ah_version < AR5K_AR5211) {
- ATH5K_ERR(ah->ah_sc,
+ ATH5K_ERR(ah,
"B mode not available on 5210");
return -EINVAL;
}
-
- mode = AR5K_MODE_11B;
- break;
- case CHANNEL_XR:
- if (ah->ah_version == AR5K_AR5211) {
- ATH5K_ERR(ah->ah_sc,
- "XR mode not available on 5211");
- return -EINVAL;
- }
- mode = AR5K_MODE_XR;
break;
default:
- ATH5K_ERR(ah->ah_sc,
+ ATH5K_ERR(ah,
"invalid channel: %d\n", channel->center_freq);
return -EINVAL;
}
@@ -1119,13 +1110,13 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
if (fast) {
ret = ath5k_hw_phy_init(ah, channel, mode, true);
if (ret) {
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_RESET,
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"fast chan change failed, falling back to normal reset\n");
/* Non fatal, can happen eg.
* on mode change */
ret = 0;
} else {
- ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_RESET,
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"fast chan change successful\n");
return 0;
}
@@ -1190,7 +1181,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
}
/* Wakeup the device */
- ret = ath5k_hw_nic_wakeup(ah, channel->hw_value, false);
+ ret = ath5k_hw_nic_wakeup(ah, channel);
if (ret)
return ret;
@@ -1258,7 +1249,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
*/
ret = ath5k_hw_phy_init(ah, channel, mode, false);
if (ret) {
- ATH5K_ERR(ah->ah_sc,
+ ATH5K_ERR(ah,
"failed to initialize PHY (%i) !\n", ret);
return ret;
}
@@ -1277,11 +1268,16 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
ath5k_hw_dma_init(ah);
- /* Enable 32KHz clock function for AR5212+ chips
+ /*
+ * Enable 32KHz clock function for AR5212+ chips
* Set clocks to 32KHz operation and use an
* external 32KHz crystal when sleeping if one
- * exists */
- if (ah->ah_version == AR5K_AR5212 &&
+ * exists.
+ * Disabled by default because it is also disabled in
+ * other drivers and it is known to cause stability
+ * issues on some devices
+ */
+ if (ah->ah_use_32khz_clock && ah->ah_version == AR5K_AR5212 &&
op_mode != NL80211_IFTYPE_AP)
ath5k_hw_set_sleep_clock(ah, true);
diff --git a/drivers/net/wireless/ath/ath5k/rfbuffer.h b/drivers/net/wireless/ath/ath5k/rfbuffer.h
index 16b67e84906d..5d11c23b4297 100644
--- a/drivers/net/wireless/ath/ath5k/rfbuffer.h
+++ b/drivers/net/wireless/ath/ath5k/rfbuffer.h
@@ -254,7 +254,7 @@ static const struct ath5k_ini_rfbuffer rfb_5111[] = {
/* RFX112 (Derby 1) */
-/* BANK 6 len pos col */
+/* BANK 6 len pos col */
#define AR5K_RF5112_OB_2GHZ { 3, 269, 0 }
#define AR5K_RF5112_DB_2GHZ { 3, 272, 0 }
@@ -495,7 +495,7 @@ static const struct ath5k_ini_rfbuffer rfb_5112a[] = {
/* BANK 2 len pos col */
#define AR5K_RF2413_RF_TURBO { 1, 1, 2 }
-/* BANK 6 len pos col */
+/* BANK 6 len pos col */
#define AR5K_RF2413_OB_2GHZ { 3, 168, 0 }
#define AR5K_RF2413_DB_2GHZ { 3, 165, 0 }
diff --git a/drivers/net/wireless/ath/ath5k/rfgain.h b/drivers/net/wireless/ath/ath5k/rfgain.h
index 1354d8c392c8..ebfae052d89e 100644
--- a/drivers/net/wireless/ath/ath5k/rfgain.h
+++ b/drivers/net/wireless/ath/ath5k/rfgain.h
@@ -30,7 +30,7 @@ struct ath5k_ini_rfgain {
/* Initial RF Gain settings for RF5111 */
static const struct ath5k_ini_rfgain rfgain_5111[] = {
- /* 5Ghz 2Ghz */
+ /* 5GHz 2GHz */
{ AR5K_RF_GAIN(0), { 0x000001a9, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x000001e9, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000029, 0x00000080 } },
@@ -99,7 +99,7 @@ static const struct ath5k_ini_rfgain rfgain_5111[] = {
/* Initial RF Gain settings for RF5112 */
static const struct ath5k_ini_rfgain rfgain_5112[] = {
- /* 5Ghz 2Ghz */
+ /* 5GHz 2GHz */
{ AR5K_RF_GAIN(0), { 0x00000007, 0x00000007 } },
{ AR5K_RF_GAIN(1), { 0x00000047, 0x00000047 } },
{ AR5K_RF_GAIN(2), { 0x00000087, 0x00000087 } },
@@ -305,7 +305,7 @@ static const struct ath5k_ini_rfgain rfgain_2316[] = {
/* Initial RF Gain settings for RF5413 */
static const struct ath5k_ini_rfgain rfgain_5413[] = {
- /* 5Ghz 2Ghz */
+ /* 5GHz 2GHz */
{ AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x00000040, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000080, 0x00000080 } },
@@ -452,7 +452,7 @@ static const struct ath5k_ini_rfgain rfgain_2425[] = {
/* Check if our current measurement is inside our
* current variable attenuation window */
-#define AR5K_GAIN_CHECK_ADJUST(_g) \
+#define AR5K_GAIN_CHECK_ADJUST(_g) \
((_g)->g_current <= (_g)->g_low || (_g)->g_current >= (_g)->g_high)
struct ath5k_gain_opt_step {
diff --git a/drivers/net/wireless/ath/ath5k/rfkill.c b/drivers/net/wireless/ath/ath5k/rfkill.c
index 41a877b73fce..270a319f3aeb 100644
--- a/drivers/net/wireless/ath/ath5k/rfkill.c
+++ b/drivers/net/wireless/ath/ath5k/rfkill.c
@@ -33,89 +33,84 @@
* THE POSSIBILITY OF SUCH DAMAGES.
*/
-#include "base.h"
+#include "ath5k.h"
-static inline void ath5k_rfkill_disable(struct ath5k_softc *sc)
+static inline void ath5k_rfkill_disable(struct ath5k_hw *ah)
{
- ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "rfkill disable (gpio:%d polarity:%d)\n",
- sc->rf_kill.gpio, sc->rf_kill.polarity);
- ath5k_hw_set_gpio_output(sc->ah, sc->rf_kill.gpio);
- ath5k_hw_set_gpio(sc->ah, sc->rf_kill.gpio, !sc->rf_kill.polarity);
+ ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "rfkill disable (gpio:%d polarity:%d)\n",
+ ah->rf_kill.gpio, ah->rf_kill.polarity);
+ ath5k_hw_set_gpio_output(ah, ah->rf_kill.gpio);
+ ath5k_hw_set_gpio(ah, ah->rf_kill.gpio, !ah->rf_kill.polarity);
}
-static inline void ath5k_rfkill_enable(struct ath5k_softc *sc)
+static inline void ath5k_rfkill_enable(struct ath5k_hw *ah)
{
- ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "rfkill enable (gpio:%d polarity:%d)\n",
- sc->rf_kill.gpio, sc->rf_kill.polarity);
- ath5k_hw_set_gpio_output(sc->ah, sc->rf_kill.gpio);
- ath5k_hw_set_gpio(sc->ah, sc->rf_kill.gpio, sc->rf_kill.polarity);
+ ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "rfkill enable (gpio:%d polarity:%d)\n",
+ ah->rf_kill.gpio, ah->rf_kill.polarity);
+ ath5k_hw_set_gpio_output(ah, ah->rf_kill.gpio);
+ ath5k_hw_set_gpio(ah, ah->rf_kill.gpio, ah->rf_kill.polarity);
}
-static inline void ath5k_rfkill_set_intr(struct ath5k_softc *sc, bool enable)
+static inline void ath5k_rfkill_set_intr(struct ath5k_hw *ah, bool enable)
{
- struct ath5k_hw *ah = sc->ah;
u32 curval;
- ath5k_hw_set_gpio_input(ah, sc->rf_kill.gpio);
- curval = ath5k_hw_get_gpio(ah, sc->rf_kill.gpio);
- ath5k_hw_set_gpio_intr(ah, sc->rf_kill.gpio, enable ?
+ ath5k_hw_set_gpio_input(ah, ah->rf_kill.gpio);
+ curval = ath5k_hw_get_gpio(ah, ah->rf_kill.gpio);
+ ath5k_hw_set_gpio_intr(ah, ah->rf_kill.gpio, enable ?
!!curval : !curval);
}
static bool
-ath5k_is_rfkill_set(struct ath5k_softc *sc)
+ath5k_is_rfkill_set(struct ath5k_hw *ah)
{
/* configuring GPIO for input for some reason disables rfkill */
- /*ath5k_hw_set_gpio_input(sc->ah, sc->rf_kill.gpio);*/
- return ath5k_hw_get_gpio(sc->ah, sc->rf_kill.gpio) ==
- sc->rf_kill.polarity;
+ /*ath5k_hw_set_gpio_input(ah, ah->rf_kill.gpio);*/
+ return ath5k_hw_get_gpio(ah, ah->rf_kill.gpio) ==
+ ah->rf_kill.polarity;
}
static void
ath5k_tasklet_rfkill_toggle(unsigned long data)
{
- struct ath5k_softc *sc = (void *)data;
+ struct ath5k_hw *ah = (void *)data;
bool blocked;
- blocked = ath5k_is_rfkill_set(sc);
- wiphy_rfkill_set_hw_state(sc->hw->wiphy, blocked);
+ blocked = ath5k_is_rfkill_set(ah);
+ wiphy_rfkill_set_hw_state(ah->hw->wiphy, blocked);
}
void
ath5k_rfkill_hw_start(struct ath5k_hw *ah)
{
- struct ath5k_softc *sc = ah->ah_sc;
-
/* read rfkill GPIO configuration from EEPROM header */
- sc->rf_kill.gpio = ah->ah_capabilities.cap_eeprom.ee_rfkill_pin;
- sc->rf_kill.polarity = ah->ah_capabilities.cap_eeprom.ee_rfkill_pol;
+ ah->rf_kill.gpio = ah->ah_capabilities.cap_eeprom.ee_rfkill_pin;
+ ah->rf_kill.polarity = ah->ah_capabilities.cap_eeprom.ee_rfkill_pol;
- tasklet_init(&sc->rf_kill.toggleq, ath5k_tasklet_rfkill_toggle,
- (unsigned long)sc);
+ tasklet_init(&ah->rf_kill.toggleq, ath5k_tasklet_rfkill_toggle,
+ (unsigned long)ah);
- ath5k_rfkill_disable(sc);
+ ath5k_rfkill_disable(ah);
/* enable interrupt for rfkill switch */
if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header))
- ath5k_rfkill_set_intr(sc, true);
+ ath5k_rfkill_set_intr(ah, true);
}
void
ath5k_rfkill_hw_stop(struct ath5k_hw *ah)
{
- struct ath5k_softc *sc = ah->ah_sc;
-
/* disable interrupt for rfkill switch */
if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header))
- ath5k_rfkill_set_intr(sc, false);
+ ath5k_rfkill_set_intr(ah, false);
- tasklet_kill(&sc->rf_kill.toggleq);
+ tasklet_kill(&ah->rf_kill.toggleq);
/* enable RFKILL when stopping HW so Wifi LED is turned off */
- ath5k_rfkill_enable(sc);
+ ath5k_rfkill_enable(ah);
}
diff --git a/drivers/net/wireless/ath/ath5k/sysfs.c b/drivers/net/wireless/ath/ath5k/sysfs.c
index 929c68cdf8ab..9364da7bd131 100644
--- a/drivers/net/wireless/ath/ath5k/sysfs.c
+++ b/drivers/net/wireless/ath/ath5k/sysfs.c
@@ -1,7 +1,6 @@
#include <linux/device.h>
#include <linux/pci.h>
-#include "base.h"
#include "ath5k.h"
#include "reg.h"
@@ -10,19 +9,23 @@ static ssize_t ath5k_attr_show_##name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
- struct ath5k_softc *sc = dev_get_drvdata(dev); \
- return snprintf(buf, PAGE_SIZE, "%d\n", get); \
+ struct ieee80211_hw *hw = dev_get_drvdata(dev); \
+ struct ath5k_hw *ah = hw->priv; \
+ return snprintf(buf, PAGE_SIZE, "%d\n", get); \
} \
\
static ssize_t ath5k_attr_store_##name(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
- struct ath5k_softc *sc = dev_get_drvdata(dev); \
- int val; \
+ struct ieee80211_hw *hw = dev_get_drvdata(dev); \
+ struct ath5k_hw *ah = hw->priv; \
+ int val, ret; \
\
- val = (int)simple_strtoul(buf, NULL, 10); \
- set(sc->ah, val); \
+ ret = kstrtoint(buf, 10, &val); \
+ if (ret < 0) \
+ return ret; \
+ set(ah, val); \
return count; \
} \
static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \
@@ -33,25 +36,26 @@ static ssize_t ath5k_attr_show_##name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
- struct ath5k_softc *sc = dev_get_drvdata(dev); \
- return snprintf(buf, PAGE_SIZE, "%d\n", get); \
+ struct ieee80211_hw *hw = dev_get_drvdata(dev); \
+ struct ath5k_hw *ah = hw->priv; \
+ return snprintf(buf, PAGE_SIZE, "%d\n", get); \
} \
static DEVICE_ATTR(name, S_IRUGO, ath5k_attr_show_##name, NULL)
/*** ANI ***/
-SIMPLE_SHOW_STORE(ani_mode, sc->ani_state.ani_mode, ath5k_ani_init);
-SIMPLE_SHOW_STORE(noise_immunity_level, sc->ani_state.noise_imm_level,
+SIMPLE_SHOW_STORE(ani_mode, ah->ani_state.ani_mode, ath5k_ani_init);
+SIMPLE_SHOW_STORE(noise_immunity_level, ah->ani_state.noise_imm_level,
ath5k_ani_set_noise_immunity_level);
-SIMPLE_SHOW_STORE(spur_level, sc->ani_state.spur_level,
+SIMPLE_SHOW_STORE(spur_level, ah->ani_state.spur_level,
ath5k_ani_set_spur_immunity_level);
-SIMPLE_SHOW_STORE(firstep_level, sc->ani_state.firstep_level,
+SIMPLE_SHOW_STORE(firstep_level, ah->ani_state.firstep_level,
ath5k_ani_set_firstep_level);
-SIMPLE_SHOW_STORE(ofdm_weak_signal_detection, sc->ani_state.ofdm_weak_sig,
+SIMPLE_SHOW_STORE(ofdm_weak_signal_detection, ah->ani_state.ofdm_weak_sig,
ath5k_ani_set_ofdm_weak_signal_detection);
-SIMPLE_SHOW_STORE(cck_weak_signal_detection, sc->ani_state.cck_weak_sig,
+SIMPLE_SHOW_STORE(cck_weak_signal_detection, ah->ani_state.cck_weak_sig,
ath5k_ani_set_cck_weak_signal_detection);
-SIMPLE_SHOW(spur_level_max, sc->ani_state.max_spur_level);
+SIMPLE_SHOW(spur_level_max, ah->ani_state.max_spur_level);
static ssize_t ath5k_attr_show_noise_immunity_level_max(struct device *dev,
struct device_attribute *attr,
@@ -93,14 +97,14 @@ static struct attribute_group ath5k_attribute_group_ani = {
/*** register / unregister ***/
int
-ath5k_sysfs_register(struct ath5k_softc *sc)
+ath5k_sysfs_register(struct ath5k_hw *ah)
{
- struct device *dev = sc->dev;
+ struct device *dev = ah->dev;
int err;
err = sysfs_create_group(&dev->kobj, &ath5k_attribute_group_ani);
if (err) {
- ATH5K_ERR(sc, "failed to create sysfs group\n");
+ ATH5K_ERR(ah, "failed to create sysfs group\n");
return err;
}
@@ -108,9 +112,9 @@ ath5k_sysfs_register(struct ath5k_softc *sc)
}
void
-ath5k_sysfs_unregister(struct ath5k_softc *sc)
+ath5k_sysfs_unregister(struct ath5k_hw *ah)
{
- struct device *dev = sc->dev;
+ struct device *dev = ah->dev;
sysfs_remove_group(&dev->kobj, &ath5k_attribute_group_ani);
}
diff --git a/drivers/net/wireless/ath/ath5k/trace.h b/drivers/net/wireless/ath/ath5k/trace.h
index 2de68adb6240..39f002ed4a88 100644
--- a/drivers/net/wireless/ath/ath5k/trace.h
+++ b/drivers/net/wireless/ath/ath5k/trace.h
@@ -2,7 +2,6 @@
#define __TRACE_ATH5K_H
#include <linux/tracepoint.h>
-#include "base.h"
#ifndef CONFIG_ATH5K_TRACER
#undef TRACE_EVENT
@@ -11,23 +10,22 @@ static inline void trace_ ## name(proto) {}
#endif
struct sk_buff;
-
-#define PRIV_ENTRY __field(struct ath5k_softc *, priv)
-#define PRIV_ASSIGN __entry->priv = priv
+struct ath5k_txq;
+struct ath5k_tx_status;
#undef TRACE_SYSTEM
#define TRACE_SYSTEM ath5k
TRACE_EVENT(ath5k_rx,
- TP_PROTO(struct ath5k_softc *priv, struct sk_buff *skb),
+ TP_PROTO(struct ath5k_hw *priv, struct sk_buff *skb),
TP_ARGS(priv, skb),
TP_STRUCT__entry(
- PRIV_ENTRY
+ __field(struct ath5k_hw *, priv)
__field(unsigned long, skbaddr)
__dynamic_array(u8, frame, skb->len)
),
TP_fast_assign(
- PRIV_ASSIGN;
+ __entry->priv = priv;
__entry->skbaddr = (unsigned long) skb;
memcpy(__get_dynamic_array(frame), skb->data, skb->len);
),
@@ -37,20 +35,20 @@ TRACE_EVENT(ath5k_rx,
);
TRACE_EVENT(ath5k_tx,
- TP_PROTO(struct ath5k_softc *priv, struct sk_buff *skb,
+ TP_PROTO(struct ath5k_hw *priv, struct sk_buff *skb,
struct ath5k_txq *q),
TP_ARGS(priv, skb, q),
TP_STRUCT__entry(
- PRIV_ENTRY
+ __field(struct ath5k_hw *, priv)
__field(unsigned long, skbaddr)
__field(u8, qnum)
__dynamic_array(u8, frame, skb->len)
),
TP_fast_assign(
- PRIV_ASSIGN;
+ __entry->priv = priv;
__entry->skbaddr = (unsigned long) skb;
__entry->qnum = (u8) q->qnum;
memcpy(__get_dynamic_array(frame), skb->data, skb->len);
@@ -63,13 +61,13 @@ TRACE_EVENT(ath5k_tx,
);
TRACE_EVENT(ath5k_tx_complete,
- TP_PROTO(struct ath5k_softc *priv, struct sk_buff *skb,
+ TP_PROTO(struct ath5k_hw *priv, struct sk_buff *skb,
struct ath5k_txq *q, struct ath5k_tx_status *ts),
TP_ARGS(priv, skb, q, ts),
TP_STRUCT__entry(
- PRIV_ENTRY
+ __field(struct ath5k_hw *, priv)
__field(unsigned long, skbaddr)
__field(u8, qnum)
__field(u8, ts_status)
@@ -78,7 +76,7 @@ TRACE_EVENT(ath5k_tx_complete,
),
TP_fast_assign(
- PRIV_ASSIGN;
+ __entry->priv = priv;
__entry->skbaddr = (unsigned long) skb;
__entry->qnum = (u8) q->qnum;
__entry->ts_status = ts->ts_status;
diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig
new file mode 100644
index 000000000000..3d5f8be20eac
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/Kconfig
@@ -0,0 +1,15 @@
+config ATH6KL
+ tristate "Atheros ath6kl support"
+ depends on MMC
+ depends on CFG80211
+ ---help---
+ This module adds support for wireless adapters based on
+ Atheros AR6003 chipset running over SDIO. If you choose to
+ build it as a module, it will be called ath6kl. Pls note
+ that AR6002 and AR6001 are not supported by this driver.
+
+config ATH6KL_DEBUG
+ bool "Atheros ath6kl debugging"
+ depends on ATH6KL
+ ---help---
+ Enables debug support
diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile
new file mode 100644
index 000000000000..8f7a0d1c290c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/Makefile
@@ -0,0 +1,37 @@
+#------------------------------------------------------------------------------
+# Copyright (c) 2004-2010 Atheros Communications Inc.
+# All rights reserved.
+#
+#
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+#
+#
+# Author(s): ="Atheros"
+#------------------------------------------------------------------------------
+
+obj-$(CONFIG_ATH6KL) := ath6kl.o
+ath6kl-y += debug.o
+ath6kl-y += htc_hif.o
+ath6kl-y += htc.o
+ath6kl-y += bmi.o
+ath6kl-y += cfg80211.o
+ath6kl-y += init.o
+ath6kl-y += main.o
+ath6kl-y += txrx.o
+ath6kl-y += wmi.o
+ath6kl-y += sdio.o
+ath6kl-$(CONFIG_NL80211_TESTMODE) += testmode.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/ath/ath6kl/bmi.c b/drivers/net/wireless/ath/ath6kl/bmi.c
new file mode 100644
index 000000000000..c5d11cc536e0
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/bmi.c
@@ -0,0 +1,689 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "hif-ops.h"
+#include "target.h"
+#include "debug.h"
+
+static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar)
+{
+ u32 addr;
+ unsigned long timeout;
+ int ret;
+
+ ar->bmi.cmd_credits = 0;
+
+ /* Read the counter register to get the command credits */
+ addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4;
+
+ timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
+ while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) {
+
+ /*
+ * Hit the credit counter with a 4-byte access, the first byte
+ * read will hit the counter and cause a decrement, while the
+ * remaining 3 bytes has no effect. The rationale behind this
+ * is to make all HIF accesses 4-byte aligned.
+ */
+ ret = hif_read_write_sync(ar, addr,
+ (u8 *)&ar->bmi.cmd_credits, 4,
+ HIF_RD_SYNC_BYTE_INC);
+ if (ret) {
+ ath6kl_err("Unable to decrement the command credit count register: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* The counter is only 8 bits.
+ * Ignore anything in the upper 3 bytes
+ */
+ ar->bmi.cmd_credits &= 0xFF;
+ }
+
+ if (!ar->bmi.cmd_credits) {
+ ath6kl_err("bmi communication timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar)
+{
+ unsigned long timeout;
+ u32 rx_word = 0;
+ int ret = 0;
+
+ timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
+ while (time_before(jiffies, timeout) && !rx_word) {
+ ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS,
+ (u8 *)&rx_word, sizeof(rx_word),
+ HIF_RD_SYNC_BYTE_INC);
+ if (ret) {
+ ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n");
+ return ret;
+ }
+
+ /* all we really want is one bit */
+ rx_word &= (1 << ENDPOINT1);
+ }
+
+ if (!rx_word) {
+ ath6kl_err("bmi_recv_buf FIFO empty\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len)
+{
+ int ret;
+ u32 addr;
+
+ ret = ath6kl_get_bmi_cmd_credits(ar);
+ if (ret)
+ return ret;
+
+ addr = ar->mbox_info.htc_addr;
+
+ ret = hif_read_write_sync(ar, addr, buf, len,
+ HIF_WR_SYNC_BYTE_INC);
+ if (ret)
+ ath6kl_err("unable to send the bmi data to the device\n");
+
+ return ret;
+}
+
+static int ath6kl_bmi_recv_buf(struct ath6kl *ar, u8 *buf, u32 len)
+{
+ int ret;
+ u32 addr;
+
+ /*
+ * During normal bootup, small reads may be required.
+ * Rather than issue an HIF Read and then wait as the Target
+ * adds successive bytes to the FIFO, we wait here until
+ * we know that response data is available.
+ *
+ * This allows us to cleanly timeout on an unexpected
+ * Target failure rather than risk problems at the HIF level.
+ * In particular, this avoids SDIO timeouts and possibly garbage
+ * data on some host controllers. And on an interconnect
+ * such as Compact Flash (as well as some SDIO masters) which
+ * does not provide any indication on data timeout, it avoids
+ * a potential hang or garbage response.
+ *
+ * Synchronization is more difficult for reads larger than the
+ * size of the MBOX FIFO (128B), because the Target is unable
+ * to push the 129th byte of data until AFTER the Host posts an
+ * HIF Read and removes some FIFO data. So for large reads the
+ * Host proceeds to post an HIF Read BEFORE all the data is
+ * actually available to read. Fortunately, large BMI reads do
+ * not occur in practice -- they're supported for debug/development.
+ *
+ * So Host/Target BMI synchronization is divided into these cases:
+ * CASE 1: length < 4
+ * Should not happen
+ *
+ * CASE 2: 4 <= length <= 128
+ * Wait for first 4 bytes to be in FIFO
+ * If CONSERVATIVE_BMI_READ is enabled, also wait for
+ * a BMI command credit, which indicates that the ENTIRE
+ * response is available in the the FIFO
+ *
+ * CASE 3: length > 128
+ * Wait for the first 4 bytes to be in FIFO
+ *
+ * For most uses, a small timeout should be sufficient and we will
+ * usually see a response quickly; but there may be some unusual
+ * (debug) cases of BMI_EXECUTE where we want an larger timeout.
+ * For now, we use an unbounded busy loop while waiting for
+ * BMI_EXECUTE.
+ *
+ * If BMI_EXECUTE ever needs to support longer-latency execution,
+ * especially in production, this code needs to be enhanced to sleep
+ * and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently
+ * a function of Host processor speed.
+ */
+ if (len >= 4) { /* NB: Currently, always true */
+ ret = ath6kl_bmi_get_rx_lkahd(ar);
+ if (ret)
+ return ret;
+ }
+
+ addr = ar->mbox_info.htc_addr;
+ ret = hif_read_write_sync(ar, addr, buf, len,
+ HIF_RD_SYNC_BYTE_INC);
+ if (ret) {
+ ath6kl_err("Unable to read the bmi data from the device: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int ath6kl_bmi_done(struct ath6kl *ar)
+{
+ int ret;
+ u32 cid = BMI_DONE;
+
+ if (ar->bmi.done_sent) {
+ ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n");
+ return 0;
+ }
+
+ ar->bmi.done_sent = true;
+
+ ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid));
+ if (ret) {
+ ath6kl_err("Unable to send bmi done: %d\n", ret);
+ return ret;
+ }
+
+ ath6kl_bmi_cleanup(ar);
+
+ return 0;
+}
+
+int ath6kl_bmi_get_target_info(struct ath6kl *ar,
+ struct ath6kl_bmi_target_info *targ_info)
+{
+ int ret;
+ u32 cid = BMI_GET_TARGET_INFO;
+
+ if (ar->bmi.done_sent) {
+ ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+ return -EACCES;
+ }
+
+ ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid));
+ if (ret) {
+ ath6kl_err("Unable to send get target info: %d\n", ret);
+ return ret;
+ }
+
+ ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version,
+ sizeof(targ_info->version));
+ if (ret) {
+ ath6kl_err("Unable to recv target info: %d\n", ret);
+ return ret;
+ }
+
+ if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) {
+ /* Determine how many bytes are in the Target's targ_info */
+ ret = ath6kl_bmi_recv_buf(ar,
+ (u8 *)&targ_info->byte_count,
+ sizeof(targ_info->byte_count));
+ if (ret) {
+ ath6kl_err("unable to read target info byte count: %d\n",
+ ret);
+ return ret;
+ }
+
+ /*
+ * The target's targ_info doesn't match the host's targ_info.
+ * We need to do some backwards compatibility to make this work.
+ */
+ if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ /* Read the remainder of the targ_info */
+ ret = ath6kl_bmi_recv_buf(ar,
+ ((u8 *)targ_info) +
+ sizeof(targ_info->byte_count),
+ sizeof(*targ_info) -
+ sizeof(targ_info->byte_count));
+
+ if (ret) {
+ ath6kl_err("Unable to read target info (%d bytes): %d\n",
+ targ_info->byte_count, ret);
+ return ret;
+ }
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n",
+ targ_info->version, targ_info->type);
+
+ return 0;
+}
+
+int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
+{
+ u32 cid = BMI_READ_MEMORY;
+ int ret;
+ u32 offset;
+ u32 len_remain, rx_len;
+ u16 size;
+
+ if (ar->bmi.done_sent) {
+ ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+ return -EACCES;
+ }
+
+ size = BMI_DATASZ_MAX + sizeof(cid) + sizeof(addr) + sizeof(len);
+ if (size > MAX_BMI_CMDBUF_SZ) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ memset(ar->bmi.cmd_buf, 0, size);
+
+ ath6kl_dbg(ATH6KL_DBG_BMI,
+ "bmi read memory: device: addr: 0x%x, len: %d\n",
+ addr, len);
+
+ len_remain = len;
+
+ while (len_remain) {
+ rx_len = (len_remain < BMI_DATASZ_MAX) ?
+ len_remain : BMI_DATASZ_MAX;
+ offset = 0;
+ memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+ offset += sizeof(cid);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+ offset += sizeof(addr);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len));
+ offset += sizeof(len);
+
+ ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ if (ret) {
+ ath6kl_err("Unable to write to the device: %d\n",
+ ret);
+ return ret;
+ }
+ ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len);
+ if (ret) {
+ ath6kl_err("Unable to read from the device: %d\n",
+ ret);
+ return ret;
+ }
+ memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len);
+ len_remain -= rx_len; addr += rx_len;
+ }
+
+ return 0;
+}
+
+int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
+{
+ u32 cid = BMI_WRITE_MEMORY;
+ int ret;
+ u32 offset;
+ u32 len_remain, tx_len;
+ const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
+ u8 aligned_buf[BMI_DATASZ_MAX];
+ u8 *src;
+
+ if (ar->bmi.done_sent) {
+ ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+ return -EACCES;
+ }
+
+ if ((BMI_DATASZ_MAX + header) > MAX_BMI_CMDBUF_SZ) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ memset(ar->bmi.cmd_buf, 0, BMI_DATASZ_MAX + header);
+
+ ath6kl_dbg(ATH6KL_DBG_BMI,
+ "bmi write memory: addr: 0x%x, len: %d\n", addr, len);
+
+ len_remain = len;
+ while (len_remain) {
+ src = &buf[len - len_remain];
+
+ if (len_remain < (BMI_DATASZ_MAX - header)) {
+ if (len_remain & 3) {
+ /* align it with 4 bytes */
+ len_remain = len_remain +
+ (4 - (len_remain & 3));
+ memcpy(aligned_buf, src, len_remain);
+ src = aligned_buf;
+ }
+ tx_len = len_remain;
+ } else {
+ tx_len = (BMI_DATASZ_MAX - header);
+ }
+
+ offset = 0;
+ memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+ offset += sizeof(cid);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+ offset += sizeof(addr);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
+ offset += sizeof(tx_len);
+ memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
+ offset += tx_len;
+
+ ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ if (ret) {
+ ath6kl_err("Unable to write to the device: %d\n",
+ ret);
+ return ret;
+ }
+ len_remain -= tx_len; addr += tx_len;
+ }
+
+ return 0;
+}
+
+int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
+{
+ u32 cid = BMI_EXECUTE;
+ int ret;
+ u32 offset;
+ u16 size;
+
+ if (ar->bmi.done_sent) {
+ ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+ return -EACCES;
+ }
+
+ size = sizeof(cid) + sizeof(addr) + sizeof(param);
+ if (size > MAX_BMI_CMDBUF_SZ) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ memset(ar->bmi.cmd_buf, 0, size);
+
+ ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n",
+ addr, *param);
+
+ offset = 0;
+ memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+ offset += sizeof(cid);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+ offset += sizeof(addr);
+ memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
+ offset += sizeof(*param);
+
+ ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ if (ret) {
+ ath6kl_err("Unable to write to the device: %d\n", ret);
+ return ret;
+ }
+
+ ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param));
+ if (ret) {
+ ath6kl_err("Unable to read from the device: %d\n", ret);
+ return ret;
+ }
+
+ memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
+
+ return 0;
+}
+
+int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
+{
+ u32 cid = BMI_SET_APP_START;
+ int ret;
+ u32 offset;
+ u16 size;
+
+ if (ar->bmi.done_sent) {
+ ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+ return -EACCES;
+ }
+
+ size = sizeof(cid) + sizeof(addr);
+ if (size > MAX_BMI_CMDBUF_SZ) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ memset(ar->bmi.cmd_buf, 0, size);
+
+ ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr);
+
+ offset = 0;
+ memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+ offset += sizeof(cid);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+ offset += sizeof(addr);
+
+ ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ if (ret) {
+ ath6kl_err("Unable to write to the device: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
+{
+ u32 cid = BMI_READ_SOC_REGISTER;
+ int ret;
+ u32 offset;
+ u16 size;
+
+ if (ar->bmi.done_sent) {
+ ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+ return -EACCES;
+ }
+
+ size = sizeof(cid) + sizeof(addr);
+ if (size > MAX_BMI_CMDBUF_SZ) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ memset(ar->bmi.cmd_buf, 0, size);
+
+ ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr);
+
+ offset = 0;
+ memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+ offset += sizeof(cid);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+ offset += sizeof(addr);
+
+ ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ if (ret) {
+ ath6kl_err("Unable to write to the device: %d\n", ret);
+ return ret;
+ }
+
+ ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param));
+ if (ret) {
+ ath6kl_err("Unable to read from the device: %d\n", ret);
+ return ret;
+ }
+ memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
+
+ return 0;
+}
+
+int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
+{
+ u32 cid = BMI_WRITE_SOC_REGISTER;
+ int ret;
+ u32 offset;
+ u16 size;
+
+ if (ar->bmi.done_sent) {
+ ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+ return -EACCES;
+ }
+
+ size = sizeof(cid) + sizeof(addr) + sizeof(param);
+ if (size > MAX_BMI_CMDBUF_SZ) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ memset(ar->bmi.cmd_buf, 0, size);
+
+ ath6kl_dbg(ATH6KL_DBG_BMI,
+ "bmi write SOC reg: addr: 0x%x, param: %d\n",
+ addr, param);
+
+ offset = 0;
+ memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+ offset += sizeof(cid);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+ offset += sizeof(addr);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &param, sizeof(param));
+ offset += sizeof(param);
+
+ ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ if (ret) {
+ ath6kl_err("Unable to write to the device: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
+{
+ u32 cid = BMI_LZ_DATA;
+ int ret;
+ u32 offset;
+ u32 len_remain, tx_len;
+ const u32 header = sizeof(cid) + sizeof(len);
+ u16 size;
+
+ if (ar->bmi.done_sent) {
+ ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+ return -EACCES;
+ }
+
+ size = BMI_DATASZ_MAX + header;
+ if (size > MAX_BMI_CMDBUF_SZ) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ memset(ar->bmi.cmd_buf, 0, size);
+
+ ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n",
+ len);
+
+ len_remain = len;
+ while (len_remain) {
+ tx_len = (len_remain < (BMI_DATASZ_MAX - header)) ?
+ len_remain : (BMI_DATASZ_MAX - header);
+
+ offset = 0;
+ memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+ offset += sizeof(cid);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
+ offset += sizeof(tx_len);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain],
+ tx_len);
+ offset += tx_len;
+
+ ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ if (ret) {
+ ath6kl_err("Unable to write to the device: %d\n",
+ ret);
+ return ret;
+ }
+
+ len_remain -= tx_len;
+ }
+
+ return 0;
+}
+
+int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
+{
+ u32 cid = BMI_LZ_STREAM_START;
+ int ret;
+ u32 offset;
+ u16 size;
+
+ if (ar->bmi.done_sent) {
+ ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+ return -EACCES;
+ }
+
+ size = sizeof(cid) + sizeof(addr);
+ if (size > MAX_BMI_CMDBUF_SZ) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ memset(ar->bmi.cmd_buf, 0, size);
+
+ ath6kl_dbg(ATH6KL_DBG_BMI,
+ "bmi LZ stream start: addr: 0x%x)\n",
+ addr);
+
+ offset = 0;
+ memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+ offset += sizeof(cid);
+ memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+ offset += sizeof(addr);
+
+ ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ if (ret) {
+ ath6kl_err("Unable to start LZ stream to the device: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
+{
+ int ret;
+ u32 last_word = 0;
+ u32 last_word_offset = len & ~0x3;
+ u32 unaligned_bytes = len & 0x3;
+
+ ret = ath6kl_bmi_lz_stream_start(ar, addr);
+ if (ret)
+ return ret;
+
+ if (unaligned_bytes) {
+ /* copy the last word into a zero padded buffer */
+ memcpy(&last_word, &buf[last_word_offset], unaligned_bytes);
+ }
+
+ ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset);
+ if (ret)
+ return ret;
+
+ if (unaligned_bytes)
+ ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4);
+
+ if (!ret) {
+ /* Close compressed stream and open a new (fake) one.
+ * This serves mainly to flush Target caches. */
+ ret = ath6kl_bmi_lz_stream_start(ar, 0x00);
+ }
+ return ret;
+}
+
+int ath6kl_bmi_init(struct ath6kl *ar)
+{
+ ar->bmi.cmd_buf = kzalloc(MAX_BMI_CMDBUF_SZ, GFP_ATOMIC);
+
+ if (!ar->bmi.cmd_buf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void ath6kl_bmi_cleanup(struct ath6kl *ar)
+{
+ kfree(ar->bmi.cmd_buf);
+ ar->bmi.cmd_buf = NULL;
+}
diff --git a/drivers/net/wireless/ath/ath6kl/bmi.h b/drivers/net/wireless/ath/ath6kl/bmi.h
new file mode 100644
index 000000000000..96851d5df24b
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/bmi.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef BMI_H
+#define BMI_H
+
+/*
+ * Bootloader Messaging Interface (BMI)
+ *
+ * BMI is a very simple messaging interface used during initialization
+ * to read memory, write memory, execute code, and to define an
+ * application entry PC.
+ *
+ * It is used to download an application to ATH6KL, to provide
+ * patches to code that is already resident on ATH6KL, and generally
+ * to examine and modify state. The Host has an opportunity to use
+ * BMI only once during bootup. Once the Host issues a BMI_DONE
+ * command, this opportunity ends.
+ *
+ * The Host writes BMI requests to mailbox0, and reads BMI responses
+ * from mailbox0. BMI requests all begin with a command
+ * (see below for specific commands), and are followed by
+ * command-specific data.
+ *
+ * Flow control:
+ * The Host can only issue a command once the Target gives it a
+ * "BMI Command Credit", using ATH6KL Counter #4. As soon as the
+ * Target has completed a command, it issues another BMI Command
+ * Credit (so the Host can issue the next command).
+ *
+ * BMI handles all required Target-side cache flushing.
+ */
+
+#define MAX_BMI_CMDBUF_SZ (BMI_DATASZ_MAX + \
+ (sizeof(u32) * 3 /* cmd + addr + len */))
+
+/* Maximum data size used for BMI transfers */
+#define BMI_DATASZ_MAX 256
+
+/* BMI Commands */
+
+#define BMI_NO_COMMAND 0
+
+#define BMI_DONE 1
+/*
+ * Semantics: Host is done using BMI
+ * Request format:
+ * u32 command (BMI_DONE)
+ * Response format: none
+ */
+
+#define BMI_READ_MEMORY 2
+/*
+ * Semantics: Host reads ATH6KL memory
+ * Request format:
+ * u32 command (BMI_READ_MEMORY)
+ * u32 address
+ * u32 length, at most BMI_DATASZ_MAX
+ * Response format:
+ * u8 data[length]
+ */
+
+#define BMI_WRITE_MEMORY 3
+/*
+ * Semantics: Host writes ATH6KL memory
+ * Request format:
+ * u32 command (BMI_WRITE_MEMORY)
+ * u32 address
+ * u32 length, at most BMI_DATASZ_MAX
+ * u8 data[length]
+ * Response format: none
+ */
+
+#define BMI_EXECUTE 4
+/*
+ * Semantics: Causes ATH6KL to execute code
+ * Request format:
+ * u32 command (BMI_EXECUTE)
+ * u32 address
+ * u32 parameter
+ * Response format:
+ * u32 return value
+ */
+
+#define BMI_SET_APP_START 5
+/*
+ * Semantics: Set Target application starting address
+ * Request format:
+ * u32 command (BMI_SET_APP_START)
+ * u32 address
+ * Response format: none
+ */
+
+#define BMI_READ_SOC_REGISTER 6
+/*
+ * Semantics: Read a 32-bit Target SOC register.
+ * Request format:
+ * u32 command (BMI_READ_REGISTER)
+ * u32 address
+ * Response format:
+ * u32 value
+ */
+
+#define BMI_WRITE_SOC_REGISTER 7
+/*
+ * Semantics: Write a 32-bit Target SOC register.
+ * Request format:
+ * u32 command (BMI_WRITE_REGISTER)
+ * u32 address
+ * u32 value
+ *
+ * Response format: none
+ */
+
+#define BMI_GET_TARGET_ID 8
+#define BMI_GET_TARGET_INFO 8
+/*
+ * Semantics: Fetch the 4-byte Target information
+ * Request format:
+ * u32 command (BMI_GET_TARGET_ID/INFO)
+ * Response format1 (old firmware):
+ * u32 TargetVersionID
+ * Response format2 (newer firmware):
+ * u32 TARGET_VERSION_SENTINAL
+ * struct bmi_target_info;
+ */
+
+#define TARGET_VERSION_SENTINAL 0xffffffff
+#define TARGET_TYPE_AR6003 3
+#define TARGET_TYPE_AR6004 5
+#define BMI_ROMPATCH_INSTALL 9
+/*
+ * Semantics: Install a ROM Patch.
+ * Request format:
+ * u32 command (BMI_ROMPATCH_INSTALL)
+ * u32 Target ROM Address
+ * u32 Target RAM Address or Value (depending on Target Type)
+ * u32 Size, in bytes
+ * u32 Activate? 1-->activate;
+ * 0-->install but do not activate
+ * Response format:
+ * u32 PatchID
+ */
+
+#define BMI_ROMPATCH_UNINSTALL 10
+/*
+ * Semantics: Uninstall a previously-installed ROM Patch,
+ * automatically deactivating, if necessary.
+ * Request format:
+ * u32 command (BMI_ROMPATCH_UNINSTALL)
+ * u32 PatchID
+ *
+ * Response format: none
+ */
+
+#define BMI_ROMPATCH_ACTIVATE 11
+/*
+ * Semantics: Activate a list of previously-installed ROM Patches.
+ * Request format:
+ * u32 command (BMI_ROMPATCH_ACTIVATE)
+ * u32 rompatch_count
+ * u32 PatchID[rompatch_count]
+ *
+ * Response format: none
+ */
+
+#define BMI_ROMPATCH_DEACTIVATE 12
+/*
+ * Semantics: Deactivate a list of active ROM Patches.
+ * Request format:
+ * u32 command (BMI_ROMPATCH_DEACTIVATE)
+ * u32 rompatch_count
+ * u32 PatchID[rompatch_count]
+ *
+ * Response format: none
+ */
+
+
+#define BMI_LZ_STREAM_START 13
+/*
+ * Semantics: Begin an LZ-compressed stream of input
+ * which is to be uncompressed by the Target to an
+ * output buffer at address. The output buffer must
+ * be sufficiently large to hold the uncompressed
+ * output from the compressed input stream. This BMI
+ * command should be followed by a series of 1 or more
+ * BMI_LZ_DATA commands.
+ * u32 command (BMI_LZ_STREAM_START)
+ * u32 address
+ * Note: Not supported on all versions of ROM firmware.
+ */
+
+#define BMI_LZ_DATA 14
+/*
+ * Semantics: Host writes ATH6KL memory with LZ-compressed
+ * data which is uncompressed by the Target. This command
+ * must be preceded by a BMI_LZ_STREAM_START command. A series
+ * of BMI_LZ_DATA commands are considered part of a single
+ * input stream until another BMI_LZ_STREAM_START is issued.
+ * Request format:
+ * u32 command (BMI_LZ_DATA)
+ * u32 length (of compressed data),
+ * at most BMI_DATASZ_MAX
+ * u8 CompressedData[length]
+ * Response format: none
+ * Note: Not supported on all versions of ROM firmware.
+ */
+
+#define BMI_COMMUNICATION_TIMEOUT 1000 /* in msec */
+
+struct ath6kl;
+struct ath6kl_bmi_target_info {
+ __le32 byte_count; /* size of this structure */
+ __le32 version; /* target version id */
+ __le32 type; /* target type */
+} __packed;
+
+int ath6kl_bmi_init(struct ath6kl *ar);
+void ath6kl_bmi_cleanup(struct ath6kl *ar);
+int ath6kl_bmi_done(struct ath6kl *ar);
+int ath6kl_bmi_get_target_info(struct ath6kl *ar,
+ struct ath6kl_bmi_target_info *targ_info);
+int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len);
+int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len);
+int ath6kl_bmi_execute(struct ath6kl *ar,
+ u32 addr, u32 *param);
+int ath6kl_bmi_set_app_start(struct ath6kl *ar,
+ u32 addr);
+int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param);
+int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param);
+int ath6kl_bmi_lz_data(struct ath6kl *ar,
+ u8 *buf, u32 len);
+int ath6kl_bmi_lz_stream_start(struct ath6kl *ar,
+ u32 addr);
+int ath6kl_bmi_fast_download(struct ath6kl *ar,
+ u32 addr, u8 *buf, u32 len);
+#endif
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
new file mode 100644
index 000000000000..f517eb8f7b44
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -0,0 +1,1916 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/moduleparam.h>
+
+#include "core.h"
+#include "cfg80211.h"
+#include "debug.h"
+#include "hif-ops.h"
+#include "testmode.h"
+
+static unsigned int ath6kl_p2p;
+
+module_param(ath6kl_p2p, uint, 0644);
+
+#define RATETAB_ENT(_rate, _rateid, _flags) { \
+ .bitrate = (_rate), \
+ .flags = (_flags), \
+ .hw_value = (_rateid), \
+}
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .hw_value = (_channel), \
+ .center_freq = (_freq), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_channel, _flags) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .hw_value = (_channel), \
+ .center_freq = 5000 + (5 * (_channel)), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_rate ath6kl_rates[] = {
+ RATETAB_ENT(10, 0x1, 0),
+ RATETAB_ENT(20, 0x2, 0),
+ RATETAB_ENT(55, 0x4, 0),
+ RATETAB_ENT(110, 0x8, 0),
+ RATETAB_ENT(60, 0x10, 0),
+ RATETAB_ENT(90, 0x20, 0),
+ RATETAB_ENT(120, 0x40, 0),
+ RATETAB_ENT(180, 0x80, 0),
+ RATETAB_ENT(240, 0x100, 0),
+ RATETAB_ENT(360, 0x200, 0),
+ RATETAB_ENT(480, 0x400, 0),
+ RATETAB_ENT(540, 0x800, 0),
+};
+
+#define ath6kl_a_rates (ath6kl_rates + 4)
+#define ath6kl_a_rates_size 8
+#define ath6kl_g_rates (ath6kl_rates + 0)
+#define ath6kl_g_rates_size 12
+
+static struct ieee80211_channel ath6kl_2ghz_channels[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
+ CHAN5G(34, 0), CHAN5G(36, 0),
+ CHAN5G(38, 0), CHAN5G(40, 0),
+ CHAN5G(42, 0), CHAN5G(44, 0),
+ CHAN5G(46, 0), CHAN5G(48, 0),
+ CHAN5G(52, 0), CHAN5G(56, 0),
+ CHAN5G(60, 0), CHAN5G(64, 0),
+ CHAN5G(100, 0), CHAN5G(104, 0),
+ CHAN5G(108, 0), CHAN5G(112, 0),
+ CHAN5G(116, 0), CHAN5G(120, 0),
+ CHAN5G(124, 0), CHAN5G(128, 0),
+ CHAN5G(132, 0), CHAN5G(136, 0),
+ CHAN5G(140, 0), CHAN5G(149, 0),
+ CHAN5G(153, 0), CHAN5G(157, 0),
+ CHAN5G(161, 0), CHAN5G(165, 0),
+ CHAN5G(184, 0), CHAN5G(188, 0),
+ CHAN5G(192, 0), CHAN5G(196, 0),
+ CHAN5G(200, 0), CHAN5G(204, 0),
+ CHAN5G(208, 0), CHAN5G(212, 0),
+ CHAN5G(216, 0),
+};
+
+static struct ieee80211_supported_band ath6kl_band_2ghz = {
+ .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
+ .channels = ath6kl_2ghz_channels,
+ .n_bitrates = ath6kl_g_rates_size,
+ .bitrates = ath6kl_g_rates,
+};
+
+static struct ieee80211_supported_band ath6kl_band_5ghz = {
+ .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
+ .channels = ath6kl_5ghz_a_channels,
+ .n_bitrates = ath6kl_a_rates_size,
+ .bitrates = ath6kl_a_rates,
+};
+
+static int ath6kl_set_wpa_version(struct ath6kl *ar,
+ enum nl80211_wpa_versions wpa_version)
+{
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
+
+ if (!wpa_version) {
+ ar->auth_mode = NONE_AUTH;
+ } else if (wpa_version & NL80211_WPA_VERSION_2) {
+ ar->auth_mode = WPA2_AUTH;
+ } else if (wpa_version & NL80211_WPA_VERSION_1) {
+ ar->auth_mode = WPA_AUTH;
+ } else {
+ ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ath6kl_set_auth_type(struct ath6kl *ar,
+ enum nl80211_auth_type auth_type)
+{
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
+
+ switch (auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ ar->dot11_auth_mode = OPEN_AUTH;
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ ar->dot11_auth_mode = SHARED_AUTH;
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ ar->dot11_auth_mode = LEAP_AUTH;
+ break;
+
+ case NL80211_AUTHTYPE_AUTOMATIC:
+ ar->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
+ break;
+
+ default:
+ ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type);
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast)
+{
+ u8 *ar_cipher = ucast ? &ar->prwise_crypto : &ar->grp_crypto;
+ u8 *ar_cipher_len = ucast ? &ar->prwise_crypto_len :
+ &ar->grp_crypto_len;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
+ __func__, cipher, ucast);
+
+ switch (cipher) {
+ case 0:
+ /* our own hack to use value 0 as no crypto used */
+ *ar_cipher = NONE_CRYPT;
+ *ar_cipher_len = 0;
+ break;
+ case WLAN_CIPHER_SUITE_WEP40:
+ *ar_cipher = WEP_CRYPT;
+ *ar_cipher_len = 5;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ *ar_cipher = WEP_CRYPT;
+ *ar_cipher_len = 13;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ *ar_cipher = TKIP_CRYPT;
+ *ar_cipher_len = 0;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ *ar_cipher = AES_CRYPT;
+ *ar_cipher_len = 0;
+ break;
+ default:
+ ath6kl_err("cipher 0x%x not supported\n", cipher);
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static void ath6kl_set_key_mgmt(struct ath6kl *ar, u32 key_mgmt)
+{
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
+
+ if (key_mgmt == WLAN_AKM_SUITE_PSK) {
+ if (ar->auth_mode == WPA_AUTH)
+ ar->auth_mode = WPA_PSK_AUTH;
+ else if (ar->auth_mode == WPA2_AUTH)
+ ar->auth_mode = WPA2_PSK_AUTH;
+ } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
+ ar->auth_mode = NONE_AUTH;
+ }
+}
+
+static bool ath6kl_cfg80211_ready(struct ath6kl *ar)
+{
+ if (!test_bit(WMI_READY, &ar->flag)) {
+ ath6kl_err("wmi is not ready\n");
+ return false;
+ }
+
+ if (!test_bit(WLAN_ENABLED, &ar->flag)) {
+ ath6kl_err("wlan disabled\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ int status;
+
+ ar->sme_state = SME_CONNECTING;
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
+ ath6kl_err("destroy in progress\n");
+ return -EBUSY;
+ }
+
+ if (test_bit(SKIP_SCAN, &ar->flag) &&
+ ((sme->channel && sme->channel->center_freq == 0) ||
+ (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
+ ath6kl_err("SkipScan: channel or bssid invalid\n");
+ return -EINVAL;
+ }
+
+ if (down_interruptible(&ar->sem)) {
+ ath6kl_err("busy, couldn't get access\n");
+ return -ERESTARTSYS;
+ }
+
+ if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
+ ath6kl_err("busy, destroy in progress\n");
+ up(&ar->sem);
+ return -EBUSY;
+ }
+
+ if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
+ /*
+ * sleep until the command queue drains
+ */
+ wait_event_interruptible_timeout(ar->event_wq,
+ ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
+ WMI_TIMEOUT);
+ if (signal_pending(current)) {
+ ath6kl_err("cmd queue drain timeout\n");
+ up(&ar->sem);
+ return -EINTR;
+ }
+ }
+
+ if (test_bit(CONNECTED, &ar->flag) &&
+ ar->ssid_len == sme->ssid_len &&
+ !memcmp(ar->ssid, sme->ssid, ar->ssid_len)) {
+ ar->reconnect_flag = true;
+ status = ath6kl_wmi_reconnect_cmd(ar->wmi, ar->req_bssid,
+ ar->ch_hint);
+
+ up(&ar->sem);
+ if (status) {
+ ath6kl_err("wmi_reconnect_cmd failed\n");
+ return -EIO;
+ }
+ return 0;
+ } else if (ar->ssid_len == sme->ssid_len &&
+ !memcmp(ar->ssid, sme->ssid, ar->ssid_len)) {
+ ath6kl_disconnect(ar);
+ }
+
+ memset(ar->ssid, 0, sizeof(ar->ssid));
+ ar->ssid_len = sme->ssid_len;
+ memcpy(ar->ssid, sme->ssid, sme->ssid_len);
+
+ if (sme->channel)
+ ar->ch_hint = sme->channel->center_freq;
+
+ memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+ if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
+ memcpy(ar->req_bssid, sme->bssid, sizeof(ar->req_bssid));
+
+ ath6kl_set_wpa_version(ar, sme->crypto.wpa_versions);
+
+ status = ath6kl_set_auth_type(ar, sme->auth_type);
+ if (status) {
+ up(&ar->sem);
+ return status;
+ }
+
+ if (sme->crypto.n_ciphers_pairwise)
+ ath6kl_set_cipher(ar, sme->crypto.ciphers_pairwise[0], true);
+ else
+ ath6kl_set_cipher(ar, 0, true);
+
+ ath6kl_set_cipher(ar, sme->crypto.cipher_group, false);
+
+ if (sme->crypto.n_akm_suites)
+ ath6kl_set_key_mgmt(ar, sme->crypto.akm_suites[0]);
+
+ if ((sme->key_len) &&
+ (ar->auth_mode == NONE_AUTH) && (ar->prwise_crypto == WEP_CRYPT)) {
+ struct ath6kl_key *key = NULL;
+
+ if (sme->key_idx < WMI_MIN_KEY_INDEX ||
+ sme->key_idx > WMI_MAX_KEY_INDEX) {
+ ath6kl_err("key index %d out of bounds\n",
+ sme->key_idx);
+ up(&ar->sem);
+ return -ENOENT;
+ }
+
+ key = &ar->keys[sme->key_idx];
+ key->key_len = sme->key_len;
+ memcpy(key->key, sme->key, key->key_len);
+ key->cipher = ar->prwise_crypto;
+ ar->def_txkey_index = sme->key_idx;
+
+ ath6kl_wmi_addkey_cmd(ar->wmi, sme->key_idx,
+ ar->prwise_crypto,
+ GROUP_USAGE | TX_USAGE,
+ key->key_len,
+ NULL,
+ key->key, KEY_OP_INIT_VAL, NULL,
+ NO_SYNC_WMIFLAG);
+ }
+
+ if (!ar->usr_bss_filter) {
+ clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
+ if (ath6kl_wmi_bssfilter_cmd(ar->wmi, ALL_BSS_FILTER, 0) != 0) {
+ ath6kl_err("couldn't set bss filtering\n");
+ up(&ar->sem);
+ return -EIO;
+ }
+ }
+
+ ar->nw_type = ar->next_mode;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: connect called with authmode %d dot11 auth %d"
+ " PW crypto %d PW crypto len %d GRP crypto %d"
+ " GRP crypto len %d channel hint %u\n",
+ __func__,
+ ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto,
+ ar->prwise_crypto_len, ar->grp_crypto,
+ ar->grp_crypto_len, ar->ch_hint);
+
+ ar->reconnect_flag = 0;
+ status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type,
+ ar->dot11_auth_mode, ar->auth_mode,
+ ar->prwise_crypto,
+ ar->prwise_crypto_len,
+ ar->grp_crypto, ar->grp_crypto_len,
+ ar->ssid_len, ar->ssid,
+ ar->req_bssid, ar->ch_hint,
+ ar->connect_ctrl_flags);
+
+ up(&ar->sem);
+
+ if (status == -EINVAL) {
+ memset(ar->ssid, 0, sizeof(ar->ssid));
+ ar->ssid_len = 0;
+ ath6kl_err("invalid request\n");
+ return -ENOENT;
+ } else if (status) {
+ ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
+ return -EIO;
+ }
+
+ if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
+ ((ar->auth_mode == WPA_PSK_AUTH)
+ || (ar->auth_mode == WPA2_PSK_AUTH))) {
+ mod_timer(&ar->disconnect_timer,
+ jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
+ }
+
+ ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
+ set_bit(CONNECT_PEND, &ar->flag);
+
+ return 0;
+}
+
+static int ath6kl_add_bss_if_needed(struct ath6kl *ar, const u8 *bssid,
+ struct ieee80211_channel *chan,
+ const u8 *beacon_ie, size_t beacon_ie_len)
+{
+ struct cfg80211_bss *bss;
+ u8 *ie;
+
+ bss = cfg80211_get_bss(ar->wdev->wiphy, chan, bssid,
+ ar->ssid, ar->ssid_len, WLAN_CAPABILITY_ESS,
+ WLAN_CAPABILITY_ESS);
+ if (bss == NULL) {
+ /*
+ * Since cfg80211 may not yet know about the BSS,
+ * generate a partial entry until the first BSS info
+ * event becomes available.
+ *
+ * Prepend SSID element since it is not included in the Beacon
+ * IEs from the target.
+ */
+ ie = kmalloc(2 + ar->ssid_len + beacon_ie_len, GFP_KERNEL);
+ if (ie == NULL)
+ return -ENOMEM;
+ ie[0] = WLAN_EID_SSID;
+ ie[1] = ar->ssid_len;
+ memcpy(ie + 2, ar->ssid, ar->ssid_len);
+ memcpy(ie + 2 + ar->ssid_len, beacon_ie, beacon_ie_len);
+ bss = cfg80211_inform_bss(ar->wdev->wiphy, chan,
+ bssid, 0, WLAN_CAPABILITY_ESS, 100,
+ ie, 2 + ar->ssid_len + beacon_ie_len,
+ 0, GFP_KERNEL);
+ if (bss)
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added dummy bss for "
+ "%pM prior to indicating connect/roamed "
+ "event\n", bssid);
+ kfree(ie);
+ } else
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
+ "entry\n");
+
+ if (bss == NULL)
+ return -ENOMEM;
+
+ cfg80211_put_bss(bss);
+
+ return 0;
+}
+
+void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
+ u8 *bssid, u16 listen_intvl,
+ u16 beacon_intvl,
+ enum network_type nw_type,
+ u8 beacon_ie_len, u8 assoc_req_len,
+ u8 assoc_resp_len, u8 *assoc_info)
+{
+ struct ieee80211_channel *chan;
+
+ /* capinfo + listen interval */
+ u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
+
+ /* capinfo + status code + associd */
+ u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
+
+ u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
+ u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
+ assoc_resp_ie_offset;
+
+ assoc_req_len -= assoc_req_ie_offset;
+ assoc_resp_len -= assoc_resp_ie_offset;
+
+ /*
+ * Store Beacon interval here; DTIM period will be available only once
+ * a Beacon frame from the AP is seen.
+ */
+ ar->assoc_bss_beacon_int = beacon_intvl;
+ clear_bit(DTIM_PERIOD_AVAIL, &ar->flag);
+
+ if (nw_type & ADHOC_NETWORK) {
+ if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: ath6k not in ibss mode\n", __func__);
+ return;
+ }
+ }
+
+ if (nw_type & INFRA_NETWORK) {
+ if (ar->wdev->iftype != NL80211_IFTYPE_STATION &&
+ ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: ath6k not in station mode\n", __func__);
+ return;
+ }
+ }
+
+ chan = ieee80211_get_channel(ar->wdev->wiphy, (int) channel);
+
+
+ if (nw_type & ADHOC_NETWORK) {
+ cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
+ return;
+ }
+
+ if (ath6kl_add_bss_if_needed(ar, bssid, chan, assoc_info,
+ beacon_ie_len) < 0) {
+ ath6kl_err("could not add cfg80211 bss entry for "
+ "connect/roamed notification\n");
+ return;
+ }
+
+ if (ar->sme_state == SME_CONNECTING) {
+ /* inform connect result to cfg80211 */
+ ar->sme_state = SME_CONNECTED;
+ cfg80211_connect_result(ar->net_dev, bssid,
+ assoc_req_ie, assoc_req_len,
+ assoc_resp_ie, assoc_resp_len,
+ WLAN_STATUS_SUCCESS, GFP_KERNEL);
+ } else if (ar->sme_state == SME_CONNECTED) {
+ /* inform roam event to cfg80211 */
+ cfg80211_roamed(ar->net_dev, chan, bssid,
+ assoc_req_ie, assoc_req_len,
+ assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
+ }
+}
+
+static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
+ struct net_device *dev, u16 reason_code)
+{
+ struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
+ reason_code);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
+ ath6kl_err("busy, destroy in progress\n");
+ return -EBUSY;
+ }
+
+ if (down_interruptible(&ar->sem)) {
+ ath6kl_err("busy, couldn't get access\n");
+ return -ERESTARTSYS;
+ }
+
+ ar->reconnect_flag = 0;
+ ath6kl_disconnect(ar);
+ memset(ar->ssid, 0, sizeof(ar->ssid));
+ ar->ssid_len = 0;
+
+ if (!test_bit(SKIP_SCAN, &ar->flag))
+ memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+
+ up(&ar->sem);
+
+ ar->sme_state = SME_DISCONNECTED;
+
+ return 0;
+}
+
+void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
+ u8 *bssid, u8 assoc_resp_len,
+ u8 *assoc_info, u16 proto_reason)
+{
+ if (ar->scan_req) {
+ cfg80211_scan_done(ar->scan_req, true);
+ ar->scan_req = NULL;
+ }
+
+ if (ar->nw_type & ADHOC_NETWORK) {
+ if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: ath6k not in ibss mode\n", __func__);
+ return;
+ }
+ memset(bssid, 0, ETH_ALEN);
+ cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
+ return;
+ }
+
+ if (ar->nw_type & INFRA_NETWORK) {
+ if (ar->wdev->iftype != NL80211_IFTYPE_STATION &&
+ ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: ath6k not in station mode\n", __func__);
+ return;
+ }
+ }
+
+ /*
+ * Send a disconnect command to target when a disconnect event is
+ * received with reason code other than 3 (DISCONNECT_CMD - disconnect
+ * request from host) to make the firmware stop trying to connect even
+ * after giving disconnect event. There will be one more disconnect
+ * event for this disconnect command with reason code DISCONNECT_CMD
+ * which will be notified to cfg80211.
+ */
+
+ if (reason != DISCONNECT_CMD) {
+ ath6kl_wmi_disconnect_cmd(ar->wmi);
+ return;
+ }
+
+ clear_bit(CONNECT_PEND, &ar->flag);
+
+ if (ar->sme_state == SME_CONNECTING) {
+ cfg80211_connect_result(ar->net_dev,
+ bssid, NULL, 0,
+ NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);
+ } else if (ar->sme_state == SME_CONNECTED) {
+ cfg80211_disconnected(ar->net_dev, reason,
+ NULL, 0, GFP_KERNEL);
+ }
+
+ ar->sme_state = SME_DISCONNECTED;
+}
+
+static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_scan_request *request)
+{
+ struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+ s8 n_channels = 0;
+ u16 *channels = NULL;
+ int ret = 0;
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (!ar->usr_bss_filter) {
+ clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
+ ret = ath6kl_wmi_bssfilter_cmd(
+ ar->wmi,
+ (test_bit(CONNECTED, &ar->flag) ?
+ ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
+ if (ret) {
+ ath6kl_err("couldn't set bss filtering\n");
+ return ret;
+ }
+ }
+
+ if (request->n_ssids && request->ssids[0].ssid_len) {
+ u8 i;
+
+ if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
+ request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
+
+ for (i = 0; i < request->n_ssids; i++)
+ ath6kl_wmi_probedssid_cmd(ar->wmi, i + 1,
+ SPECIFIC_SSID_FLAG,
+ request->ssids[i].ssid_len,
+ request->ssids[i].ssid);
+ }
+
+ if (request->ie) {
+ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_REQ,
+ request->ie, request->ie_len);
+ if (ret) {
+ ath6kl_err("failed to set Probe Request appie for "
+ "scan");
+ return ret;
+ }
+ }
+
+ /*
+ * Scan only the requested channels if the request specifies a set of
+ * channels. If the list is longer than the target supports, do not
+ * configure the list and instead, scan all available channels.
+ */
+ if (request->n_channels > 0 &&
+ request->n_channels <= WMI_MAX_CHANNELS) {
+ u8 i;
+
+ n_channels = request->n_channels;
+
+ channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
+ if (channels == NULL) {
+ ath6kl_warn("failed to set scan channels, "
+ "scan all channels");
+ n_channels = 0;
+ }
+
+ for (i = 0; i < n_channels; i++)
+ channels[i] = request->channels[i]->center_freq;
+ }
+
+ ret = ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0,
+ false, 0, 0, n_channels, channels);
+ if (ret)
+ ath6kl_err("wmi_startscan_cmd failed\n");
+ else
+ ar->scan_req = request;
+
+ kfree(channels);
+
+ return ret;
+}
+
+void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status)
+{
+ int i;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status %d\n", __func__, status);
+
+ if (!ar->scan_req)
+ return;
+
+ if ((status == -ECANCELED) || (status == -EBUSY)) {
+ cfg80211_scan_done(ar->scan_req, true);
+ goto out;
+ }
+
+ cfg80211_scan_done(ar->scan_req, false);
+
+ if (ar->scan_req->n_ssids && ar->scan_req->ssids[0].ssid_len) {
+ for (i = 0; i < ar->scan_req->n_ssids; i++) {
+ ath6kl_wmi_probedssid_cmd(ar->wmi, i + 1,
+ DISABLE_SSID_FLAG,
+ 0, NULL);
+ }
+ }
+
+out:
+ ar->scan_req = NULL;
+}
+
+static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_index, bool pairwise,
+ const u8 *mac_addr,
+ struct key_params *params)
+{
+ struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+ struct ath6kl_key *key = NULL;
+ u8 key_usage;
+ u8 key_type;
+ int status = 0;
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: key index %d out of bounds\n", __func__,
+ key_index);
+ return -ENOENT;
+ }
+
+ key = &ar->keys[key_index];
+ memset(key, 0, sizeof(struct ath6kl_key));
+
+ if (pairwise)
+ key_usage = PAIRWISE_USAGE;
+ else
+ key_usage = GROUP_USAGE;
+
+ if (params) {
+ if (params->key_len > WLAN_MAX_KEY_LEN ||
+ params->seq_len > sizeof(key->seq))
+ return -EINVAL;
+
+ key->key_len = params->key_len;
+ memcpy(key->key, params->key, key->key_len);
+ key->seq_len = params->seq_len;
+ memcpy(key->seq, params->seq, key->seq_len);
+ key->cipher = params->cipher;
+ }
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ key_type = WEP_CRYPT;
+ break;
+
+ case WLAN_CIPHER_SUITE_TKIP:
+ key_type = TKIP_CRYPT;
+ break;
+
+ case WLAN_CIPHER_SUITE_CCMP:
+ key_type = AES_CRYPT;
+ break;
+
+ default:
+ return -ENOTSUPP;
+ }
+
+ if (((ar->auth_mode == WPA_PSK_AUTH)
+ || (ar->auth_mode == WPA2_PSK_AUTH))
+ && (key_usage & GROUP_USAGE))
+ del_timer(&ar->disconnect_timer);
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
+ __func__, key_index, key->key_len, key_type,
+ key_usage, key->seq_len);
+
+ ar->def_txkey_index = key_index;
+
+ if (ar->nw_type == AP_NETWORK && !pairwise &&
+ (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
+ ar->ap_mode_bkey.valid = true;
+ ar->ap_mode_bkey.key_index = key_index;
+ ar->ap_mode_bkey.key_type = key_type;
+ ar->ap_mode_bkey.key_len = key->key_len;
+ memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
+ if (!test_bit(CONNECTED, &ar->flag)) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
+ "key configuration until AP mode has been "
+ "started\n");
+ /*
+ * The key will be set in ath6kl_connect_ap_mode() once
+ * the connected event is received from the target.
+ */
+ return 0;
+ }
+ }
+
+ if (ar->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
+ !test_bit(CONNECTED, &ar->flag)) {
+ /*
+ * Store the key locally so that it can be re-configured after
+ * the AP mode has properly started
+ * (ath6kl_install_statioc_wep_keys).
+ */
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
+ "until AP mode has been started\n");
+ ar->wep_key_list[key_index].key_len = key->key_len;
+ memcpy(ar->wep_key_list[key_index].key, key->key, key->key_len);
+ return 0;
+ }
+
+ status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index,
+ key_type, key_usage, key->key_len,
+ key->seq, key->key, KEY_OP_INIT_VAL,
+ (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
+
+ if (status)
+ return -EIO;
+
+ return 0;
+}
+
+static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_index, bool pairwise,
+ const u8 *mac_addr)
+{
+ struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: key index %d out of bounds\n", __func__,
+ key_index);
+ return -ENOENT;
+ }
+
+ if (!ar->keys[key_index].key_len) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: index %d is empty\n", __func__, key_index);
+ return 0;
+ }
+
+ ar->keys[key_index].key_len = 0;
+
+ return ath6kl_wmi_deletekey_cmd(ar->wmi, key_index);
+}
+
+static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_index, bool pairwise,
+ const u8 *mac_addr, void *cookie,
+ void (*callback) (void *cookie,
+ struct key_params *))
+{
+ struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+ struct ath6kl_key *key = NULL;
+ struct key_params params;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: key index %d out of bounds\n", __func__,
+ key_index);
+ return -ENOENT;
+ }
+
+ key = &ar->keys[key_index];
+ memset(&params, 0, sizeof(params));
+ params.cipher = key->cipher;
+ params.key_len = key->key_len;
+ params.seq_len = key->seq_len;
+ params.seq = key->seq;
+ params.key = key->key;
+
+ callback(cookie, &params);
+
+ return key->key_len ? 0 : -ENOENT;
+}
+
+static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
+ struct net_device *ndev,
+ u8 key_index, bool unicast,
+ bool multicast)
+{
+ struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+ struct ath6kl_key *key = NULL;
+ int status = 0;
+ u8 key_usage;
+ enum crypto_type key_type = NONE_CRYPT;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: key index %d out of bounds\n",
+ __func__, key_index);
+ return -ENOENT;
+ }
+
+ if (!ar->keys[key_index].key_len) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
+ __func__, key_index);
+ return -EINVAL;
+ }
+
+ ar->def_txkey_index = key_index;
+ key = &ar->keys[ar->def_txkey_index];
+ key_usage = GROUP_USAGE;
+ if (ar->prwise_crypto == WEP_CRYPT)
+ key_usage |= TX_USAGE;
+ if (unicast)
+ key_type = ar->prwise_crypto;
+ if (multicast)
+ key_type = ar->grp_crypto;
+
+ if (ar->next_mode == AP_NETWORK && !test_bit(CONNECTED, &ar->flag))
+ return 0; /* Delay until AP mode has been started */
+
+ status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index,
+ key_type, key_usage,
+ key->key_len, key->seq, key->key,
+ KEY_OP_INIT_VAL, NULL,
+ SYNC_BOTH_WMIFLAG);
+ if (status)
+ return -EIO;
+
+ return 0;
+}
+
+void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl *ar, u8 keyid,
+ bool ismcast)
+{
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
+
+ cfg80211_michael_mic_failure(ar->net_dev, ar->bssid,
+ (ismcast ? NL80211_KEYTYPE_GROUP :
+ NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
+ GFP_KERNEL);
+}
+
+static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
+ int ret;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
+ changed);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+ ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
+ if (ret != 0) {
+ ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * The type nl80211_tx_power_setting replaces the following
+ * data type from 2.6.36 onwards
+*/
+static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
+ enum nl80211_tx_power_setting type,
+ int dbm)
+{
+ struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
+ u8 ath6kl_dbm;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
+ type, dbm);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ switch (type) {
+ case NL80211_TX_POWER_AUTOMATIC:
+ return 0;
+ case NL80211_TX_POWER_LIMITED:
+ ar->tx_pwr = ath6kl_dbm = dbm;
+ break;
+ default:
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
+ __func__, type);
+ return -EOPNOTSUPP;
+ }
+
+ ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, ath6kl_dbm);
+
+ return 0;
+}
+
+static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
+{
+ struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (test_bit(CONNECTED, &ar->flag)) {
+ ar->tx_pwr = 0;
+
+ if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi) != 0) {
+ ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
+ return -EIO;
+ }
+
+ wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
+ 5 * HZ);
+
+ if (signal_pending(current)) {
+ ath6kl_err("target did not respond\n");
+ return -EINTR;
+ }
+ }
+
+ *dbm = ar->tx_pwr;
+ return 0;
+}
+
+static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+ struct net_device *dev,
+ bool pmgmt, int timeout)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ struct wmi_power_mode_cmd mode;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
+ __func__, pmgmt, timeout);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (pmgmt) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
+ mode.pwr_mode = REC_POWER;
+ } else {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
+ mode.pwr_mode = MAX_PERF_POWER;
+ }
+
+ if (ath6kl_wmi_powermode_cmd(ar->wmi, mode.pwr_mode) != 0) {
+ ath6kl_err("wmi_powermode_cmd failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
+ struct net_device *ndev,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct ath6kl *ar = ath6kl_priv(ndev);
+ struct wireless_dev *wdev = ar->wdev;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ ar->next_mode = INFRA_NETWORK;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ ar->next_mode = ADHOC_NETWORK;
+ break;
+ case NL80211_IFTYPE_AP:
+ ar->next_mode = AP_NETWORK;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ ar->next_mode = INFRA_NETWORK;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ ar->next_mode = AP_NETWORK;
+ break;
+ default:
+ ath6kl_err("invalid interface type %u\n", type);
+ return -EOPNOTSUPP;
+ }
+
+ wdev->iftype = type;
+
+ return 0;
+}
+
+static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *ibss_param)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ int status;
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ ar->ssid_len = ibss_param->ssid_len;
+ memcpy(ar->ssid, ibss_param->ssid, ar->ssid_len);
+
+ if (ibss_param->channel)
+ ar->ch_hint = ibss_param->channel->center_freq;
+
+ if (ibss_param->channel_fixed) {
+ /*
+ * TODO: channel_fixed: The channel should be fixed, do not
+ * search for IBSSs to join on other channels. Target
+ * firmware does not support this feature, needs to be
+ * updated.
+ */
+ return -EOPNOTSUPP;
+ }
+
+ memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+ if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
+ memcpy(ar->req_bssid, ibss_param->bssid, sizeof(ar->req_bssid));
+
+ ath6kl_set_wpa_version(ar, 0);
+
+ status = ath6kl_set_auth_type(ar, NL80211_AUTHTYPE_OPEN_SYSTEM);
+ if (status)
+ return status;
+
+ if (ibss_param->privacy) {
+ ath6kl_set_cipher(ar, WLAN_CIPHER_SUITE_WEP40, true);
+ ath6kl_set_cipher(ar, WLAN_CIPHER_SUITE_WEP40, false);
+ } else {
+ ath6kl_set_cipher(ar, 0, true);
+ ath6kl_set_cipher(ar, 0, false);
+ }
+
+ ar->nw_type = ar->next_mode;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: connect called with authmode %d dot11 auth %d"
+ " PW crypto %d PW crypto len %d GRP crypto %d"
+ " GRP crypto len %d channel hint %u\n",
+ __func__,
+ ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto,
+ ar->prwise_crypto_len, ar->grp_crypto,
+ ar->grp_crypto_len, ar->ch_hint);
+
+ status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type,
+ ar->dot11_auth_mode, ar->auth_mode,
+ ar->prwise_crypto,
+ ar->prwise_crypto_len,
+ ar->grp_crypto, ar->grp_crypto_len,
+ ar->ssid_len, ar->ssid,
+ ar->req_bssid, ar->ch_hint,
+ ar->connect_ctrl_flags);
+ set_bit(CONNECT_PEND, &ar->flag);
+
+ return 0;
+}
+
+static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
+ struct net_device *dev)
+{
+ struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ ath6kl_disconnect(ar);
+ memset(ar->ssid, 0, sizeof(ar->ssid));
+ ar->ssid_len = 0;
+
+ return 0;
+}
+
+static const u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+};
+
+static bool is_rate_legacy(s32 rate)
+{
+ static const s32 legacy[] = { 1000, 2000, 5500, 11000,
+ 6000, 9000, 12000, 18000, 24000,
+ 36000, 48000, 54000
+ };
+ u8 i;
+
+ for (i = 0; i < ARRAY_SIZE(legacy); i++)
+ if (rate == legacy[i])
+ return true;
+
+ return false;
+}
+
+static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
+{
+ static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
+ 52000, 58500, 65000, 72200
+ };
+ u8 i;
+
+ for (i = 0; i < ARRAY_SIZE(ht20); i++) {
+ if (rate == ht20[i]) {
+ if (i == ARRAY_SIZE(ht20) - 1)
+ /* last rate uses sgi */
+ *sgi = true;
+ else
+ *sgi = false;
+
+ *mcs = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
+{
+ static const s32 ht40[] = { 13500, 27000, 40500, 54000,
+ 81000, 108000, 121500, 135000,
+ 150000
+ };
+ u8 i;
+
+ for (i = 0; i < ARRAY_SIZE(ht40); i++) {
+ if (rate == ht40[i]) {
+ if (i == ARRAY_SIZE(ht40) - 1)
+ /* last rate uses sgi */
+ *sgi = true;
+ else
+ *sgi = false;
+
+ *mcs = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac, struct station_info *sinfo)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ long left;
+ bool sgi;
+ s32 rate;
+ int ret;
+ u8 mcs;
+
+ if (memcmp(mac, ar->bssid, ETH_ALEN) != 0)
+ return -ENOENT;
+
+ if (down_interruptible(&ar->sem))
+ return -EBUSY;
+
+ set_bit(STATS_UPDATE_PEND, &ar->flag);
+
+ ret = ath6kl_wmi_get_stats_cmd(ar->wmi);
+
+ if (ret != 0) {
+ up(&ar->sem);
+ return -EIO;
+ }
+
+ left = wait_event_interruptible_timeout(ar->event_wq,
+ !test_bit(STATS_UPDATE_PEND,
+ &ar->flag),
+ WMI_TIMEOUT);
+
+ up(&ar->sem);
+
+ if (left == 0)
+ return -ETIMEDOUT;
+ else if (left < 0)
+ return left;
+
+ if (ar->target_stats.rx_byte) {
+ sinfo->rx_bytes = ar->target_stats.rx_byte;
+ sinfo->filled |= STATION_INFO_RX_BYTES;
+ sinfo->rx_packets = ar->target_stats.rx_pkt;
+ sinfo->filled |= STATION_INFO_RX_PACKETS;
+ }
+
+ if (ar->target_stats.tx_byte) {
+ sinfo->tx_bytes = ar->target_stats.tx_byte;
+ sinfo->filled |= STATION_INFO_TX_BYTES;
+ sinfo->tx_packets = ar->target_stats.tx_pkt;
+ sinfo->filled |= STATION_INFO_TX_PACKETS;
+ }
+
+ sinfo->signal = ar->target_stats.cs_rssi;
+ sinfo->filled |= STATION_INFO_SIGNAL;
+
+ rate = ar->target_stats.tx_ucast_rate;
+
+ if (is_rate_legacy(rate)) {
+ sinfo->txrate.legacy = rate / 100;
+ } else if (is_rate_ht20(rate, &mcs, &sgi)) {
+ if (sgi) {
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ sinfo->txrate.mcs = mcs - 1;
+ } else {
+ sinfo->txrate.mcs = mcs;
+ }
+
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+ } else if (is_rate_ht40(rate, &mcs, &sgi)) {
+ if (sgi) {
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ sinfo->txrate.mcs = mcs - 1;
+ } else {
+ sinfo->txrate.mcs = mcs;
+ }
+
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+ } else {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "invalid rate from stats: %d\n", rate);
+ ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
+ return 0;
+ }
+
+ sinfo->filled |= STATION_INFO_TX_BITRATE;
+
+ if (test_bit(CONNECTED, &ar->flag) &&
+ test_bit(DTIM_PERIOD_AVAIL, &ar->flag) &&
+ ar->nw_type == INFRA_NETWORK) {
+ sinfo->filled |= STATION_INFO_BSS_PARAM;
+ sinfo->bss_param.flags = 0;
+ sinfo->bss_param.dtim_period = ar->assoc_bss_dtim_period;
+ sinfo->bss_param.beacon_interval = ar->assoc_bss_beacon_int;
+ }
+
+ return 0;
+}
+
+static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct ath6kl *ar = ath6kl_priv(netdev);
+ return ath6kl_wmi_setpmkid_cmd(ar->wmi, pmksa->bssid,
+ pmksa->pmkid, true);
+}
+
+static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct ath6kl *ar = ath6kl_priv(netdev);
+ return ath6kl_wmi_setpmkid_cmd(ar->wmi, pmksa->bssid,
+ pmksa->pmkid, false);
+}
+
+static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
+{
+ struct ath6kl *ar = ath6kl_priv(netdev);
+ if (test_bit(CONNECTED, &ar->flag))
+ return ath6kl_wmi_setpmkid_cmd(ar->wmi, ar->bssid, NULL, false);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ar6k_cfg80211_suspend(struct wiphy *wiphy,
+ struct cfg80211_wowlan *wow)
+{
+ struct ath6kl *ar = wiphy_priv(wiphy);
+
+ return ath6kl_hif_suspend(ar);
+}
+#endif
+
+static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
+ __func__, chan->center_freq, chan->hw_value);
+ ar->next_chan = chan->center_freq;
+
+ return 0;
+}
+
+static bool ath6kl_is_p2p_ie(const u8 *pos)
+{
+ return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ pos[2] == 0x50 && pos[3] == 0x6f &&
+ pos[4] == 0x9a && pos[5] == 0x09;
+}
+
+static int ath6kl_set_ap_probe_resp_ies(struct ath6kl *ar, const u8 *ies,
+ size_t ies_len)
+{
+ const u8 *pos;
+ u8 *buf = NULL;
+ size_t len = 0;
+ int ret;
+
+ /*
+ * Filter out P2P IE(s) since they will be included depending on
+ * the Probe Request frame in ath6kl_send_go_probe_resp().
+ */
+
+ if (ies && ies_len) {
+ buf = kmalloc(ies_len, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+ pos = ies;
+ while (pos + 1 < ies + ies_len) {
+ if (pos + 2 + pos[1] > ies + ies_len)
+ break;
+ if (!ath6kl_is_p2p_ie(pos)) {
+ memcpy(buf + len, pos, 2 + pos[1]);
+ len += 2 + pos[1];
+ }
+ pos += 2 + pos[1];
+ }
+ }
+
+ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_RESP,
+ buf, len);
+ kfree(buf);
+ return ret;
+}
+
+static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
+ struct beacon_parameters *info, bool add)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ struct ieee80211_mgmt *mgmt;
+ u8 *ies;
+ int ies_len;
+ struct wmi_connect_cmd p;
+ int res;
+ int i;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
+
+ if (!ath6kl_cfg80211_ready(ar))
+ return -EIO;
+
+ if (ar->next_mode != AP_NETWORK)
+ return -EOPNOTSUPP;
+
+ if (info->beacon_ies) {
+ res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_BEACON,
+ info->beacon_ies,
+ info->beacon_ies_len);
+ if (res)
+ return res;
+ }
+ if (info->proberesp_ies) {
+ res = ath6kl_set_ap_probe_resp_ies(ar, info->proberesp_ies,
+ info->proberesp_ies_len);
+ if (res)
+ return res;
+ }
+ if (info->assocresp_ies) {
+ res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_ASSOC_RESP,
+ info->assocresp_ies,
+ info->assocresp_ies_len);
+ if (res)
+ return res;
+ }
+
+ if (!add)
+ return 0;
+
+ ar->ap_mode_bkey.valid = false;
+
+ /* TODO:
+ * info->interval
+ * info->dtim_period
+ */
+
+ if (info->head == NULL)
+ return -EINVAL;
+ mgmt = (struct ieee80211_mgmt *) info->head;
+ ies = mgmt->u.beacon.variable;
+ if (ies > info->head + info->head_len)
+ return -EINVAL;
+ ies_len = info->head + info->head_len - ies;
+
+ if (info->ssid == NULL)
+ return -EINVAL;
+ memcpy(ar->ssid, info->ssid, info->ssid_len);
+ ar->ssid_len = info->ssid_len;
+ if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
+ return -EOPNOTSUPP; /* TODO */
+
+ ar->dot11_auth_mode = OPEN_AUTH;
+
+ memset(&p, 0, sizeof(p));
+
+ for (i = 0; i < info->crypto.n_akm_suites; i++) {
+ switch (info->crypto.akm_suites[i]) {
+ case WLAN_AKM_SUITE_8021X:
+ if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ p.auth_mode |= WPA_AUTH;
+ if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ p.auth_mode |= WPA2_AUTH;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ p.auth_mode |= WPA_PSK_AUTH;
+ if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ p.auth_mode |= WPA2_PSK_AUTH;
+ break;
+ }
+ }
+ if (p.auth_mode == 0)
+ p.auth_mode = NONE_AUTH;
+ ar->auth_mode = p.auth_mode;
+
+ for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
+ switch (info->crypto.ciphers_pairwise[i]) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ p.prwise_crypto_type |= WEP_CRYPT;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ p.prwise_crypto_type |= TKIP_CRYPT;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ p.prwise_crypto_type |= AES_CRYPT;
+ break;
+ }
+ }
+ if (p.prwise_crypto_type == 0) {
+ p.prwise_crypto_type = NONE_CRYPT;
+ ath6kl_set_cipher(ar, 0, true);
+ } else if (info->crypto.n_ciphers_pairwise == 1)
+ ath6kl_set_cipher(ar, info->crypto.ciphers_pairwise[0], true);
+
+ switch (info->crypto.cipher_group) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ p.grp_crypto_type = WEP_CRYPT;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ p.grp_crypto_type = TKIP_CRYPT;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ p.grp_crypto_type = AES_CRYPT;
+ break;
+ default:
+ p.grp_crypto_type = NONE_CRYPT;
+ break;
+ }
+ ath6kl_set_cipher(ar, info->crypto.cipher_group, false);
+
+ p.nw_type = AP_NETWORK;
+ ar->nw_type = ar->next_mode;
+
+ p.ssid_len = ar->ssid_len;
+ memcpy(p.ssid, ar->ssid, ar->ssid_len);
+ p.dot11_auth_mode = ar->dot11_auth_mode;
+ p.ch = cpu_to_le16(ar->next_chan);
+
+ res = ath6kl_wmi_ap_profile_commit(ar->wmi, &p);
+ if (res < 0)
+ return res;
+
+ return 0;
+}
+
+static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
+ struct beacon_parameters *info)
+{
+ return ath6kl_ap_beacon(wiphy, dev, info, true);
+}
+
+static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
+ struct beacon_parameters *info)
+{
+ return ath6kl_ap_beacon(wiphy, dev, info, false);
+}
+
+static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+
+ if (ar->nw_type != AP_NETWORK)
+ return -EOPNOTSUPP;
+ if (!test_bit(CONNECTED, &ar->flag))
+ return -ENOTCONN;
+
+ ath6kl_wmi_disconnect_cmd(ar->wmi);
+ clear_bit(CONNECTED, &ar->flag);
+
+ return 0;
+}
+
+static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac, struct station_parameters *params)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+
+ if (ar->nw_type != AP_NETWORK)
+ return -EOPNOTSUPP;
+
+ /* Use this only for authorizing/unauthorizing a station */
+ if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
+ return -EOPNOTSUPP;
+
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
+ return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_AUTHORIZE,
+ mac, 0);
+ return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_UNAUTHORIZE, mac,
+ 0);
+}
+
+static int ath6kl_remain_on_channel(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ unsigned int duration,
+ u64 *cookie)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+
+ /* TODO: if already pending or ongoing remain-on-channel,
+ * return -EBUSY */
+ *cookie = 1; /* only a single pending request is supported */
+
+ return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, chan->center_freq,
+ duration);
+}
+
+static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct net_device *dev,
+ u64 cookie)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+
+ if (cookie != 1)
+ return -ENOENT;
+
+ return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi);
+}
+
+static int ath6kl_send_go_probe_resp(struct ath6kl *ar, const u8 *buf,
+ size_t len, unsigned int freq)
+{
+ const u8 *pos;
+ u8 *p2p;
+ int p2p_len;
+ int ret;
+ const struct ieee80211_mgmt *mgmt;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+
+ /* Include P2P IE(s) from the frame generated in user space. */
+
+ p2p = kmalloc(len, GFP_KERNEL);
+ if (p2p == NULL)
+ return -ENOMEM;
+ p2p_len = 0;
+
+ pos = mgmt->u.probe_resp.variable;
+ while (pos + 1 < buf + len) {
+ if (pos + 2 + pos[1] > buf + len)
+ break;
+ if (ath6kl_is_p2p_ie(pos)) {
+ memcpy(p2p + p2p_len, pos, 2 + pos[1]);
+ p2p_len += 2 + pos[1];
+ }
+ pos += 2 + pos[1];
+ }
+
+ ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, freq, mgmt->da,
+ p2p, p2p_len);
+ kfree(p2p);
+ return ret;
+}
+
+static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
+ struct ieee80211_channel *chan, bool offchan,
+ enum nl80211_channel_type channel_type,
+ bool channel_type_valid, unsigned int wait,
+ const u8 *buf, size_t len, bool no_cck, u64 *cookie)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ u32 id;
+ const struct ieee80211_mgmt *mgmt;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+ if (buf + len >= mgmt->u.probe_resp.variable &&
+ ar->nw_type == AP_NETWORK && test_bit(CONNECTED, &ar->flag) &&
+ ieee80211_is_probe_resp(mgmt->frame_control)) {
+ /*
+ * Send Probe Response frame in AP mode using a separate WMI
+ * command to allow the target to fill in the generic IEs.
+ */
+ *cookie = 0; /* TX status not supported */
+ return ath6kl_send_go_probe_resp(ar, buf, len,
+ chan->center_freq);
+ }
+
+ id = ar->send_action_id++;
+ if (id == 0) {
+ /*
+ * 0 is a reserved value in the WMI command and shall not be
+ * used for the command.
+ */
+ id = ar->send_action_id++;
+ }
+
+ *cookie = id;
+ return ath6kl_wmi_send_action_cmd(ar->wmi, id, chan->center_freq, wait,
+ buf, len);
+}
+
+static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
+ struct net_device *dev,
+ u16 frame_type, bool reg)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
+ __func__, frame_type, reg);
+ if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
+ /*
+ * Note: This notification callback is not allowed to sleep, so
+ * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
+ * hardcode target to report Probe Request frames all the time.
+ */
+ ar->probe_req_report = reg;
+ }
+}
+
+static const struct ieee80211_txrx_stypes
+ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_P2P_GO] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+};
+
+static struct cfg80211_ops ath6kl_cfg80211_ops = {
+ .change_virtual_intf = ath6kl_cfg80211_change_iface,
+ .scan = ath6kl_cfg80211_scan,
+ .connect = ath6kl_cfg80211_connect,
+ .disconnect = ath6kl_cfg80211_disconnect,
+ .add_key = ath6kl_cfg80211_add_key,
+ .get_key = ath6kl_cfg80211_get_key,
+ .del_key = ath6kl_cfg80211_del_key,
+ .set_default_key = ath6kl_cfg80211_set_default_key,
+ .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
+ .set_tx_power = ath6kl_cfg80211_set_txpower,
+ .get_tx_power = ath6kl_cfg80211_get_txpower,
+ .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
+ .join_ibss = ath6kl_cfg80211_join_ibss,
+ .leave_ibss = ath6kl_cfg80211_leave_ibss,
+ .get_station = ath6kl_get_station,
+ .set_pmksa = ath6kl_set_pmksa,
+ .del_pmksa = ath6kl_del_pmksa,
+ .flush_pmksa = ath6kl_flush_pmksa,
+ CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
+#ifdef CONFIG_PM
+ .suspend = ar6k_cfg80211_suspend,
+#endif
+ .set_channel = ath6kl_set_channel,
+ .add_beacon = ath6kl_add_beacon,
+ .set_beacon = ath6kl_set_beacon,
+ .del_beacon = ath6kl_del_beacon,
+ .change_station = ath6kl_change_station,
+ .remain_on_channel = ath6kl_remain_on_channel,
+ .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
+ .mgmt_tx = ath6kl_mgmt_tx,
+ .mgmt_frame_register = ath6kl_mgmt_frame_register,
+};
+
+struct wireless_dev *ath6kl_cfg80211_init(struct device *dev)
+{
+ int ret = 0;
+ struct wireless_dev *wdev;
+ struct ath6kl *ar;
+
+ wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ if (!wdev) {
+ ath6kl_err("couldn't allocate wireless device\n");
+ return NULL;
+ }
+
+ /* create a new wiphy for use with cfg80211 */
+ wdev->wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
+ if (!wdev->wiphy) {
+ ath6kl_err("couldn't allocate wiphy device\n");
+ kfree(wdev);
+ return NULL;
+ }
+
+ ar = wiphy_priv(wdev->wiphy);
+ ar->p2p = !!ath6kl_p2p;
+
+ wdev->wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
+
+ wdev->wiphy->max_remain_on_channel_duration = 5000;
+
+ /* set device pointer for wiphy */
+ set_wiphy_dev(wdev->wiphy, dev);
+
+ wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
+ if (ar->p2p) {
+ wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT);
+ }
+ /* max num of ssids that can be probed during scanning */
+ wdev->wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
+ wdev->wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
+ wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
+ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+ wdev->wiphy->cipher_suites = cipher_suites;
+ wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+ ret = wiphy_register(wdev->wiphy);
+ if (ret < 0) {
+ ath6kl_err("couldn't register wiphy device\n");
+ wiphy_free(wdev->wiphy);
+ kfree(wdev);
+ return NULL;
+ }
+
+ return wdev;
+}
+
+void ath6kl_cfg80211_deinit(struct ath6kl *ar)
+{
+ struct wireless_dev *wdev = ar->wdev;
+
+ if (ar->scan_req) {
+ cfg80211_scan_done(ar->scan_req, true);
+ ar->scan_req = NULL;
+ }
+
+ if (!wdev)
+ return;
+
+ wiphy_unregister(wdev->wiphy);
+ wiphy_free(wdev->wiphy);
+ kfree(wdev);
+}
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h
new file mode 100644
index 000000000000..a84adc249c61
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ATH6KL_CFG80211_H
+#define ATH6KL_CFG80211_H
+
+struct wireless_dev *ath6kl_cfg80211_init(struct device *dev);
+void ath6kl_cfg80211_deinit(struct ath6kl *ar);
+
+void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status);
+
+void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
+ u8 *bssid, u16 listen_intvl,
+ u16 beacon_intvl,
+ enum network_type nw_type,
+ u8 beacon_ie_len, u8 assoc_req_len,
+ u8 assoc_resp_len, u8 *assoc_info);
+
+void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
+ u8 *bssid, u8 assoc_resp_len,
+ u8 *assoc_info, u16 proto_reason);
+
+void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl *ar, u8 keyid,
+ bool ismcast);
+
+#endif /* ATH6KL_CFG80211_H */
diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h
new file mode 100644
index 000000000000..b92f0e5d2336
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/common.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <linux/netdevice.h>
+
+#define ATH6KL_MAX_IE 256
+
+extern int ath6kl_printk(const char *level, const char *fmt, ...);
+
+#define A_CACHE_LINE_PAD 128
+
+/*
+ * Reflects the version of binary interface exposed by ATH6KL target
+ * firmware. Needs to be incremented by 1 for any change in the firmware
+ * that requires upgrade of the driver on the host side for the change to
+ * work correctly
+ */
+#define ATH6KL_ABI_VERSION 1
+
+#define SIGNAL_QUALITY_METRICS_NUM_MAX 2
+
+enum {
+ SIGNAL_QUALITY_METRICS_SNR = 0,
+ SIGNAL_QUALITY_METRICS_RSSI,
+ SIGNAL_QUALITY_METRICS_ALL,
+};
+
+/*
+ * Data Path
+ */
+
+#define WMI_MAX_TX_DATA_FRAME_LENGTH \
+ (1500 + sizeof(struct wmi_data_hdr) + \
+ sizeof(struct ethhdr) + \
+ sizeof(struct ath6kl_llc_snap_hdr))
+
+/* An AMSDU frame */ /* The MAX AMSDU length of AR6003 is 3839 */
+#define WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH \
+ (3840 + sizeof(struct wmi_data_hdr) + \
+ sizeof(struct ethhdr) + \
+ sizeof(struct ath6kl_llc_snap_hdr))
+
+#define EPPING_ALIGNMENT_PAD \
+ (((sizeof(struct htc_frame_hdr) + 3) & (~0x3)) \
+ - sizeof(struct htc_frame_hdr))
+
+struct ath6kl_llc_snap_hdr {
+ u8 dsap;
+ u8 ssap;
+ u8 cntl;
+ u8 org_code[3];
+ __be16 eth_type;
+} __packed;
+
+enum crypto_type {
+ NONE_CRYPT = 0x01,
+ WEP_CRYPT = 0x02,
+ TKIP_CRYPT = 0x04,
+ AES_CRYPT = 0x08,
+};
+
+struct htc_endpoint_credit_dist;
+struct ath6kl;
+enum htc_credit_dist_reason;
+struct htc_credit_state_info;
+
+int ath6k_setup_credit_dist(void *htc_handle,
+ struct htc_credit_state_info *cred_info);
+void ath6k_credit_distribute(struct htc_credit_state_info *cred_inf,
+ struct list_head *epdist_list,
+ enum htc_credit_dist_reason reason);
+void ath6k_credit_init(struct htc_credit_state_info *cred_inf,
+ struct list_head *ep_list,
+ int tot_credits);
+void ath6k_seek_credits(struct htc_credit_state_info *cred_inf,
+ struct htc_endpoint_credit_dist *ep_dist);
+struct ath6kl *ath6kl_core_alloc(struct device *sdev);
+int ath6kl_core_init(struct ath6kl *ar);
+int ath6kl_unavail_ev(struct ath6kl *ar);
+struct sk_buff *ath6kl_buf_alloc(int size);
+#endif /* COMMON_H */
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
new file mode 100644
index 000000000000..6d8a4845baaf
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -0,0 +1,637 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef CORE_H
+#define CORE_H
+
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/circ_buf.h>
+#include <net/cfg80211.h>
+#include "htc.h"
+#include "wmi.h"
+#include "bmi.h"
+#include "target.h"
+
+#define MAX_ATH6KL 1
+#define ATH6KL_MAX_RX_BUFFERS 16
+#define ATH6KL_BUFFER_SIZE 1664
+#define ATH6KL_MAX_AMSDU_RX_BUFFERS 4
+#define ATH6KL_AMSDU_REFILL_THRESHOLD 3
+#define ATH6KL_AMSDU_BUFFER_SIZE (WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH + 128)
+#define MAX_MSDU_SUBFRAME_PAYLOAD_LEN 1508
+#define MIN_MSDU_SUBFRAME_PAYLOAD_LEN 46
+
+#define USER_SAVEDKEYS_STAT_INIT 0
+#define USER_SAVEDKEYS_STAT_RUN 1
+
+#define ATH6KL_TX_TIMEOUT 10
+#define ATH6KL_MAX_ENDPOINTS 4
+#define MAX_NODE_NUM 15
+
+/* Extra bytes for htc header alignment */
+#define ATH6KL_HTC_ALIGN_BYTES 3
+
+/* MAX_HI_COOKIE_NUM are reserved for high priority traffic */
+#define MAX_DEF_COOKIE_NUM 180
+#define MAX_HI_COOKIE_NUM 18 /* 10% of MAX_COOKIE_NUM */
+#define MAX_COOKIE_NUM (MAX_DEF_COOKIE_NUM + MAX_HI_COOKIE_NUM)
+
+#define MAX_DEFAULT_SEND_QUEUE_DEPTH (MAX_DEF_COOKIE_NUM / WMM_NUM_AC)
+
+#define DISCON_TIMER_INTVAL 10000 /* in msec */
+#define A_DEFAULT_LISTEN_INTERVAL 100
+#define A_MAX_WOW_LISTEN_INTERVAL 1000
+
+/* includes also the null byte */
+#define ATH6KL_FIRMWARE_MAGIC "QCA-ATH6KL"
+
+enum ath6kl_fw_ie_type {
+ ATH6KL_FW_IE_FW_VERSION = 0,
+ ATH6KL_FW_IE_TIMESTAMP = 1,
+ ATH6KL_FW_IE_OTP_IMAGE = 2,
+ ATH6KL_FW_IE_FW_IMAGE = 3,
+ ATH6KL_FW_IE_PATCH_IMAGE = 4,
+ ATH6KL_FW_IE_RESERVED_RAM_SIZE = 5,
+ ATH6KL_FW_IE_CAPABILITIES = 6,
+ ATH6KL_FW_IE_PATCH_ADDR = 7,
+};
+
+enum ath6kl_fw_capability {
+ ATH6KL_FW_CAPABILITY_HOST_P2P = 0,
+
+ /* this needs to be last */
+ ATH6KL_FW_CAPABILITY_MAX,
+};
+
+#define ATH6KL_CAPABILITY_LEN (ALIGN(ATH6KL_FW_CAPABILITY_MAX, 32) / 32)
+
+struct ath6kl_fw_ie {
+ __le32 id;
+ __le32 len;
+ u8 data[0];
+};
+
+/* AR6003 1.0 definitions */
+#define AR6003_REV1_VERSION 0x300002ba
+
+/* AR6003 2.0 definitions */
+#define AR6003_REV2_VERSION 0x30000384
+#define AR6003_REV2_PATCH_DOWNLOAD_ADDRESS 0x57e910
+#define AR6003_REV2_OTP_FILE "ath6k/AR6003/hw2.0/otp.bin.z77"
+#define AR6003_REV2_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athwlan.bin.z77"
+#define AR6003_REV2_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athtcmd_ram.bin"
+#define AR6003_REV2_PATCH_FILE "ath6k/AR6003/hw2.0/data.patch.bin"
+#define AR6003_REV2_FIRMWARE_2_FILE "ath6k/AR6003/hw2.0/fw-2.bin"
+#define AR6003_REV2_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin"
+#define AR6003_REV2_DEFAULT_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.SD31.bin"
+
+/* AR6003 3.0 definitions */
+#define AR6003_REV3_VERSION 0x30000582
+#define AR6003_REV3_OTP_FILE "ath6k/AR6003/hw2.1.1/otp.bin"
+#define AR6003_REV3_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athwlan.bin"
+#define AR6003_REV3_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athtcmd_ram.bin"
+#define AR6003_REV3_PATCH_FILE "ath6k/AR6003/hw2.1.1/data.patch.bin"
+#define AR6003_REV3_FIRMWARE_2_FILE "ath6k/AR6003/hw2.1.1/fw-2.bin"
+#define AR6003_REV3_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin"
+#define AR6003_REV3_DEFAULT_BOARD_DATA_FILE \
+ "ath6k/AR6003/hw2.1.1/bdata.SD31.bin"
+
+/* AR6004 1.0 definitions */
+#define AR6004_REV1_VERSION 0x30000623
+#define AR6004_REV1_FIRMWARE_FILE "ath6k/AR6004/hw6.1/fw.ram.bin"
+#define AR6004_REV1_FIRMWARE_2_FILE "ath6k/AR6004/hw6.1/fw-2.bin"
+#define AR6004_REV1_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.bin"
+#define AR6004_REV1_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.DB132.bin"
+#define AR6004_REV1_EPPING_FIRMWARE_FILE "ath6k/AR6004/hw6.1/endpointping.bin"
+
+/* Per STA data, used in AP mode */
+#define STA_PS_AWAKE BIT(0)
+#define STA_PS_SLEEP BIT(1)
+#define STA_PS_POLLED BIT(2)
+
+/* HTC TX packet tagging definitions */
+#define ATH6KL_CONTROL_PKT_TAG HTC_TX_PACKET_TAG_USER_DEFINED
+#define ATH6KL_DATA_PKT_TAG (ATH6KL_CONTROL_PKT_TAG + 1)
+
+#define AR6003_CUST_DATA_SIZE 16
+
+#define AGGR_WIN_IDX(x, y) ((x) % (y))
+#define AGGR_INCR_IDX(x, y) AGGR_WIN_IDX(((x) + 1), (y))
+#define AGGR_DCRM_IDX(x, y) AGGR_WIN_IDX(((x) - 1), (y))
+#define ATH6KL_MAX_SEQ_NO 0xFFF
+#define ATH6KL_NEXT_SEQ_NO(x) (((x) + 1) & ATH6KL_MAX_SEQ_NO)
+
+#define NUM_OF_TIDS 8
+#define AGGR_SZ_DEFAULT 8
+
+#define AGGR_WIN_SZ_MIN 2
+#define AGGR_WIN_SZ_MAX 8
+
+#define TID_WINDOW_SZ(_x) ((_x) << 1)
+
+#define AGGR_NUM_OF_FREE_NETBUFS 16
+
+#define AGGR_RX_TIMEOUT 400 /* in ms */
+
+#define WMI_TIMEOUT (2 * HZ)
+
+#define MBOX_YIELD_LIMIT 99
+
+/* configuration lags */
+/*
+ * ATH6KL_CONF_IGNORE_ERP_BARKER: Ignore the barker premable in
+ * ERP IE of beacon to determine the short premable support when
+ * sending (Re)Assoc req.
+ * ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN: Don't send the power
+ * module state transition failure events which happen during
+ * scan, to the host.
+ */
+#define ATH6KL_CONF_IGNORE_ERP_BARKER BIT(0)
+#define ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN BIT(1)
+#define ATH6KL_CONF_ENABLE_11N BIT(2)
+#define ATH6KL_CONF_ENABLE_TX_BURST BIT(3)
+
+enum wlan_low_pwr_state {
+ WLAN_POWER_STATE_ON,
+ WLAN_POWER_STATE_CUT_PWR,
+ WLAN_POWER_STATE_DEEP_SLEEP,
+ WLAN_POWER_STATE_WOW
+};
+
+enum sme_state {
+ SME_DISCONNECTED,
+ SME_CONNECTING,
+ SME_CONNECTED
+};
+
+struct skb_hold_q {
+ struct sk_buff *skb;
+ bool is_amsdu;
+ u16 seq_no;
+};
+
+struct rxtid {
+ bool aggr;
+ bool progress;
+ bool timer_mon;
+ u16 win_sz;
+ u16 seq_next;
+ u32 hold_q_sz;
+ struct skb_hold_q *hold_q;
+ struct sk_buff_head q;
+ spinlock_t lock;
+};
+
+struct rxtid_stats {
+ u32 num_into_aggr;
+ u32 num_dups;
+ u32 num_oow;
+ u32 num_mpdu;
+ u32 num_amsdu;
+ u32 num_delivered;
+ u32 num_timeouts;
+ u32 num_hole;
+ u32 num_bar;
+};
+
+struct aggr_info {
+ u8 aggr_sz;
+ u8 timer_scheduled;
+ struct timer_list timer;
+ struct net_device *dev;
+ struct rxtid rx_tid[NUM_OF_TIDS];
+ struct sk_buff_head free_q;
+ struct rxtid_stats stat[NUM_OF_TIDS];
+};
+
+struct ath6kl_wep_key {
+ u8 key_index;
+ u8 key_len;
+ u8 key[64];
+};
+
+#define ATH6KL_KEY_SEQ_LEN 8
+
+struct ath6kl_key {
+ u8 key[WLAN_MAX_KEY_LEN];
+ u8 key_len;
+ u8 seq[ATH6KL_KEY_SEQ_LEN];
+ u8 seq_len;
+ u32 cipher;
+};
+
+struct ath6kl_node_mapping {
+ u8 mac_addr[ETH_ALEN];
+ u8 ep_id;
+ u8 tx_pend;
+};
+
+struct ath6kl_cookie {
+ struct sk_buff *skb;
+ u32 map_no;
+ struct htc_packet htc_pkt;
+ struct ath6kl_cookie *arc_list_next;
+};
+
+struct ath6kl_sta {
+ u16 sta_flags;
+ u8 mac[ETH_ALEN];
+ u8 aid;
+ u8 keymgmt;
+ u8 ucipher;
+ u8 auth;
+ u8 wpa_ie[ATH6KL_MAX_IE];
+ struct sk_buff_head psq;
+ spinlock_t psq_lock;
+};
+
+struct ath6kl_version {
+ u32 target_ver;
+ u32 wlan_ver;
+ u32 abi_ver;
+};
+
+struct ath6kl_bmi {
+ u32 cmd_credits;
+ bool done_sent;
+ u8 *cmd_buf;
+};
+
+struct target_stats {
+ u64 tx_pkt;
+ u64 tx_byte;
+ u64 tx_ucast_pkt;
+ u64 tx_ucast_byte;
+ u64 tx_mcast_pkt;
+ u64 tx_mcast_byte;
+ u64 tx_bcast_pkt;
+ u64 tx_bcast_byte;
+ u64 tx_rts_success_cnt;
+ u64 tx_pkt_per_ac[4];
+
+ u64 tx_err;
+ u64 tx_fail_cnt;
+ u64 tx_retry_cnt;
+ u64 tx_mult_retry_cnt;
+ u64 tx_rts_fail_cnt;
+
+ u64 rx_pkt;
+ u64 rx_byte;
+ u64 rx_ucast_pkt;
+ u64 rx_ucast_byte;
+ u64 rx_mcast_pkt;
+ u64 rx_mcast_byte;
+ u64 rx_bcast_pkt;
+ u64 rx_bcast_byte;
+ u64 rx_frgment_pkt;
+
+ u64 rx_err;
+ u64 rx_crc_err;
+ u64 rx_key_cache_miss;
+ u64 rx_decrypt_err;
+ u64 rx_dupl_frame;
+
+ u64 tkip_local_mic_fail;
+ u64 tkip_cnter_measures_invoked;
+ u64 tkip_replays;
+ u64 tkip_fmt_err;
+ u64 ccmp_fmt_err;
+ u64 ccmp_replays;
+
+ u64 pwr_save_fail_cnt;
+
+ u64 cs_bmiss_cnt;
+ u64 cs_low_rssi_cnt;
+ u64 cs_connect_cnt;
+ u64 cs_discon_cnt;
+
+ s32 tx_ucast_rate;
+ s32 rx_ucast_rate;
+
+ u32 lq_val;
+
+ u32 wow_pkt_dropped;
+ u16 wow_evt_discarded;
+
+ s16 noise_floor_calib;
+ s16 cs_rssi;
+ s16 cs_ave_beacon_rssi;
+ u8 cs_ave_beacon_snr;
+ u8 cs_last_roam_msec;
+ u8 cs_snr;
+
+ u8 wow_host_pkt_wakeups;
+ u8 wow_host_evt_wakeups;
+
+ u32 arp_received;
+ u32 arp_matched;
+ u32 arp_replied;
+};
+
+struct ath6kl_mbox_info {
+ u32 htc_addr;
+ u32 htc_ext_addr;
+ u32 htc_ext_sz;
+
+ u32 block_size;
+
+ u32 gmbox_addr;
+
+ u32 gmbox_sz;
+};
+
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP. For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+
+#define ATH6KL_KEYBUF_SIZE 16
+#define ATH6KL_MICBUF_SIZE (8+8) /* space for both tx and rx */
+
+#define ATH6KL_KEY_XMIT 0x01
+#define ATH6KL_KEY_RECV 0x02
+#define ATH6KL_KEY_DEFAULT 0x80 /* default xmit key */
+
+/* Initial group key for AP mode */
+struct ath6kl_req_key {
+ bool valid;
+ u8 key_index;
+ int key_type;
+ u8 key[WLAN_MAX_KEY_LEN];
+ u8 key_len;
+};
+
+/* Flag info */
+#define WMI_ENABLED 0
+#define WMI_READY 1
+#define CONNECTED 2
+#define STATS_UPDATE_PEND 3
+#define CONNECT_PEND 4
+#define WMM_ENABLED 5
+#define NETQ_STOPPED 6
+#define WMI_CTRL_EP_FULL 7
+#define DTIM_EXPIRED 8
+#define DESTROY_IN_PROGRESS 9
+#define NETDEV_REGISTERED 10
+#define SKIP_SCAN 11
+#define WLAN_ENABLED 12
+#define TESTMODE 13
+#define CLEAR_BSSFILTER_ON_BEACON 14
+#define DTIM_PERIOD_AVAIL 15
+
+struct ath6kl {
+ struct device *dev;
+ struct net_device *net_dev;
+ struct ath6kl_bmi bmi;
+ const struct ath6kl_hif_ops *hif_ops;
+ struct wmi *wmi;
+ int tx_pending[ENDPOINT_MAX];
+ int total_tx_data_pend;
+ struct htc_target *htc_target;
+ void *hif_priv;
+ spinlock_t lock;
+ struct semaphore sem;
+ int ssid_len;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 next_mode;
+ u8 nw_type;
+ u8 dot11_auth_mode;
+ u8 auth_mode;
+ u8 prwise_crypto;
+ u8 prwise_crypto_len;
+ u8 grp_crypto;
+ u8 grp_crypto_len;
+ u8 def_txkey_index;
+ struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1];
+ u8 bssid[ETH_ALEN];
+ u8 req_bssid[ETH_ALEN];
+ u16 ch_hint;
+ u16 bss_ch;
+ u16 listen_intvl_b;
+ u16 listen_intvl_t;
+ u8 lrssi_roam_threshold;
+ struct ath6kl_version version;
+ u32 target_type;
+ u8 tx_pwr;
+ struct net_device_stats net_stats;
+ struct target_stats target_stats;
+ struct ath6kl_node_mapping node_map[MAX_NODE_NUM];
+ u8 ibss_ps_enable;
+ u8 node_num;
+ u8 next_ep_id;
+ struct ath6kl_cookie *cookie_list;
+ u32 cookie_count;
+ enum htc_endpoint_id ac2ep_map[WMM_NUM_AC];
+ bool ac_stream_active[WMM_NUM_AC];
+ u8 ac_stream_pri_map[WMM_NUM_AC];
+ u8 hiac_stream_active_pri;
+ u8 ep2ac_map[ENDPOINT_MAX];
+ enum htc_endpoint_id ctrl_ep;
+ struct htc_credit_state_info credit_state_info;
+ u32 connect_ctrl_flags;
+ u32 user_key_ctrl;
+ u8 usr_bss_filter;
+ struct ath6kl_sta sta_list[AP_MAX_NUM_STA];
+ u8 sta_list_index;
+ struct ath6kl_req_key ap_mode_bkey;
+ struct sk_buff_head mcastpsq;
+ spinlock_t mcastpsq_lock;
+ u8 intra_bss;
+ struct aggr_info *aggr_cntxt;
+ struct wmi_ap_mode_stat ap_stats;
+ u8 ap_country_code[3];
+ struct list_head amsdu_rx_buffer_queue;
+ struct timer_list disconnect_timer;
+ u8 rx_meta_ver;
+ struct wireless_dev *wdev;
+ struct cfg80211_scan_request *scan_req;
+ struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1];
+ enum sme_state sme_state;
+ enum wlan_low_pwr_state wlan_pwr_state;
+ struct wmi_scan_params_cmd sc_params;
+#define AR_MCAST_FILTER_MAC_ADDR_SIZE 4
+ struct {
+ void *rx_report;
+ size_t rx_report_len;
+ } tm;
+
+ struct {
+ u32 dataset_patch_addr;
+ u32 app_load_addr;
+ u32 app_start_override_addr;
+ u32 board_ext_data_addr;
+ u32 reserved_ram_size;
+ } hw;
+
+ u16 conf_flags;
+ wait_queue_head_t event_wq;
+ struct ath6kl_mbox_info mbox_info;
+
+ struct ath6kl_cookie cookie_mem[MAX_COOKIE_NUM];
+ int reconnect_flag;
+ unsigned long flag;
+
+ u8 *fw_board;
+ size_t fw_board_len;
+
+ u8 *fw_otp;
+ size_t fw_otp_len;
+
+ u8 *fw;
+ size_t fw_len;
+
+ u8 *fw_patch;
+ size_t fw_patch_len;
+
+ unsigned long fw_capabilities[ATH6KL_CAPABILITY_LEN];
+
+ struct workqueue_struct *ath6kl_wq;
+
+ struct dentry *debugfs_phy;
+
+ u32 send_action_id;
+ bool probe_req_report;
+ u16 next_chan;
+
+ bool p2p;
+ u16 assoc_bss_beacon_int;
+ u8 assoc_bss_dtim_period;
+
+#ifdef CONFIG_ATH6KL_DEBUG
+ struct {
+ struct circ_buf fwlog_buf;
+ spinlock_t fwlog_lock;
+ void *fwlog_tmp;
+ u32 fwlog_mask;
+ unsigned int dbgfs_diag_reg;
+ u32 diag_reg_addr_wr;
+ u32 diag_reg_val_wr;
+
+ struct {
+ unsigned int invalid_rate;
+ } war_stats;
+ } debug;
+#endif /* CONFIG_ATH6KL_DEBUG */
+};
+
+static inline void *ath6kl_priv(struct net_device *dev)
+{
+ return wdev_priv(dev->ieee80211_ptr);
+}
+
+static inline void ath6kl_deposit_credit_to_ep(struct htc_credit_state_info
+ *cred_info,
+ struct htc_endpoint_credit_dist
+ *ep_dist, int credits)
+{
+ ep_dist->credits += credits;
+ ep_dist->cred_assngd += credits;
+ cred_info->cur_free_credits -= credits;
+}
+
+static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar,
+ u32 item_offset)
+{
+ u32 addr = 0;
+
+ if (ar->target_type == TARGET_TYPE_AR6003)
+ addr = ATH6KL_AR6003_HI_START_ADDR + item_offset;
+ else if (ar->target_type == TARGET_TYPE_AR6004)
+ addr = ATH6KL_AR6004_HI_START_ADDR + item_offset;
+
+ return addr;
+}
+
+void ath6kl_destroy(struct net_device *dev, unsigned int unregister);
+int ath6kl_configure_target(struct ath6kl *ar);
+void ath6kl_detect_error(unsigned long ptr);
+void disconnect_timer_handler(unsigned long ptr);
+void init_netdev(struct net_device *dev);
+void ath6kl_cookie_init(struct ath6kl *ar);
+void ath6kl_cookie_cleanup(struct ath6kl *ar);
+void ath6kl_rx(struct htc_target *target, struct htc_packet *packet);
+void ath6kl_tx_complete(void *context, struct list_head *packet_queue);
+enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
+ struct htc_packet *packet);
+void ath6kl_stop_txrx(struct ath6kl *ar);
+void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar);
+int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value);
+int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length);
+int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value);
+int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length);
+int ath6kl_read_fwlogs(struct ath6kl *ar);
+void ath6kl_init_profile_info(struct ath6kl *ar);
+void ath6kl_tx_data_cleanup(struct ath6kl *ar);
+void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile,
+ bool get_dbglogs);
+
+struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar);
+void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie);
+int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev);
+
+struct aggr_info *aggr_init(struct net_device *dev);
+void ath6kl_rx_refill(struct htc_target *target,
+ enum htc_endpoint_id endpoint);
+void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count);
+struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target,
+ enum htc_endpoint_id endpoint,
+ int len);
+void aggr_module_destroy(struct aggr_info *aggr_info);
+void aggr_reset_state(struct aggr_info *aggr_info);
+
+struct ath6kl_sta *ath6kl_find_sta(struct ath6kl *ar, u8 * node_addr);
+struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid);
+
+void ath6kl_ready_event(void *devt, u8 * datap, u32 sw_ver, u32 abi_ver);
+int ath6kl_control_tx(void *devt, struct sk_buff *skb,
+ enum htc_endpoint_id eid);
+void ath6kl_connect_event(struct ath6kl *ar, u16 channel,
+ u8 *bssid, u16 listen_int,
+ u16 beacon_int, enum network_type net_type,
+ u8 beacon_ie_len, u8 assoc_req_len,
+ u8 assoc_resp_len, u8 *assoc_info);
+void ath6kl_connect_ap_mode_bss(struct ath6kl *ar, u16 channel);
+void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr,
+ u8 keymgmt, u8 ucipher, u8 auth,
+ u8 assoc_req_len, u8 *assoc_info);
+void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason,
+ u8 *bssid, u8 assoc_resp_len,
+ u8 *assoc_info, u16 prot_reason_status);
+void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast);
+void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr);
+void ath6kl_scan_complete_evt(struct ath6kl *ar, int status);
+void ath6kl_tgt_stats_event(struct ath6kl *ar, u8 *ptr, u32 len);
+void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active);
+enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac);
+
+void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid);
+
+void ath6kl_dtimexpiry_event(struct ath6kl *ar);
+void ath6kl_disconnect(struct ath6kl *ar);
+void ath6kl_deep_sleep_enable(struct ath6kl *ar);
+void aggr_recv_delba_req_evt(struct ath6kl *ar, u8 tid);
+void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no,
+ u8 win_sz);
+void ath6kl_wakeup_event(void *dev);
+void ath6kl_target_failure(struct ath6kl *ar);
+
+#endif /* CORE_H */
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c
new file mode 100644
index 000000000000..7879b5314285
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/debug.c
@@ -0,0 +1,935 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+
+#include <linux/circ_buf.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/export.h>
+
+#include "debug.h"
+#include "target.h"
+
+struct ath6kl_fwlog_slot {
+ __le32 timestamp;
+ __le32 length;
+
+ /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */
+ u8 payload[0];
+};
+
+#define ATH6KL_FWLOG_SIZE 32768
+#define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \
+ ATH6KL_FWLOG_PAYLOAD_SIZE)
+#define ATH6KL_FWLOG_VALID_MASK 0x1ffff
+
+int ath6kl_printk(const char *level, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int rtn;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ rtn = printk("%sath6kl: %pV", level, &vaf);
+
+ va_end(args);
+
+ return rtn;
+}
+
+#ifdef CONFIG_ATH6KL_DEBUG
+
+#define REG_OUTPUT_LEN_PER_LINE 25
+#define REGTYPE_STR_LEN 100
+
+struct ath6kl_diag_reg_info {
+ u32 reg_start;
+ u32 reg_end;
+ const char *reg_info;
+};
+
+static const struct ath6kl_diag_reg_info diag_reg[] = {
+ { 0x20000, 0x200fc, "General DMA and Rx registers" },
+ { 0x28000, 0x28900, "MAC PCU register & keycache" },
+ { 0x20800, 0x20a40, "QCU" },
+ { 0x21000, 0x212f0, "DCU" },
+ { 0x4000, 0x42e4, "RTC" },
+ { 0x540000, 0x540000 + (256 * 1024), "RAM" },
+ { 0x29800, 0x2B210, "Base Band" },
+ { 0x1C000, 0x1C748, "Analog" },
+};
+
+void ath6kl_dump_registers(struct ath6kl_device *dev,
+ struct ath6kl_irq_proc_registers *irq_proc_reg,
+ struct ath6kl_irq_enable_reg *irq_enable_reg)
+{
+
+ ath6kl_dbg(ATH6KL_DBG_ANY, ("<------- Register Table -------->\n"));
+
+ if (irq_proc_reg != NULL) {
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "Host Int status: 0x%x\n",
+ irq_proc_reg->host_int_status);
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "CPU Int status: 0x%x\n",
+ irq_proc_reg->cpu_int_status);
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "Error Int status: 0x%x\n",
+ irq_proc_reg->error_int_status);
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "Counter Int status: 0x%x\n",
+ irq_proc_reg->counter_int_status);
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "Mbox Frame: 0x%x\n",
+ irq_proc_reg->mbox_frame);
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "Rx Lookahead Valid: 0x%x\n",
+ irq_proc_reg->rx_lkahd_valid);
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "Rx Lookahead 0: 0x%x\n",
+ irq_proc_reg->rx_lkahd[0]);
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "Rx Lookahead 1: 0x%x\n",
+ irq_proc_reg->rx_lkahd[1]);
+
+ if (dev->ar->mbox_info.gmbox_addr != 0) {
+ /*
+ * If the target supports GMBOX hardware, dump some
+ * additional state.
+ */
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "GMBOX Host Int status 2: 0x%x\n",
+ irq_proc_reg->host_int_status2);
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "GMBOX RX Avail: 0x%x\n",
+ irq_proc_reg->gmbox_rx_avail);
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "GMBOX lookahead alias 0: 0x%x\n",
+ irq_proc_reg->rx_gmbox_lkahd_alias[0]);
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "GMBOX lookahead alias 1: 0x%x\n",
+ irq_proc_reg->rx_gmbox_lkahd_alias[1]);
+ }
+
+ }
+
+ if (irq_enable_reg != NULL) {
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "Int status Enable: 0x%x\n",
+ irq_enable_reg->int_status_en);
+ ath6kl_dbg(ATH6KL_DBG_ANY, "Counter Int status Enable: 0x%x\n",
+ irq_enable_reg->cntr_int_status_en);
+ }
+ ath6kl_dbg(ATH6KL_DBG_ANY, "<------------------------------->\n");
+}
+
+static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist)
+{
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "--- endpoint: %d svc_id: 0x%X ---\n",
+ ep_dist->endpoint, ep_dist->svc_id);
+ ath6kl_dbg(ATH6KL_DBG_ANY, " dist_flags : 0x%X\n",
+ ep_dist->dist_flags);
+ ath6kl_dbg(ATH6KL_DBG_ANY, " cred_norm : %d\n",
+ ep_dist->cred_norm);
+ ath6kl_dbg(ATH6KL_DBG_ANY, " cred_min : %d\n",
+ ep_dist->cred_min);
+ ath6kl_dbg(ATH6KL_DBG_ANY, " credits : %d\n",
+ ep_dist->credits);
+ ath6kl_dbg(ATH6KL_DBG_ANY, " cred_assngd : %d\n",
+ ep_dist->cred_assngd);
+ ath6kl_dbg(ATH6KL_DBG_ANY, " seek_cred : %d\n",
+ ep_dist->seek_cred);
+ ath6kl_dbg(ATH6KL_DBG_ANY, " cred_sz : %d\n",
+ ep_dist->cred_sz);
+ ath6kl_dbg(ATH6KL_DBG_ANY, " cred_per_msg : %d\n",
+ ep_dist->cred_per_msg);
+ ath6kl_dbg(ATH6KL_DBG_ANY, " cred_to_dist : %d\n",
+ ep_dist->cred_to_dist);
+ ath6kl_dbg(ATH6KL_DBG_ANY, " txq_depth : %d\n",
+ get_queue_depth(&((struct htc_endpoint *)
+ ep_dist->htc_rsvd)->txq));
+ ath6kl_dbg(ATH6KL_DBG_ANY,
+ "----------------------------------\n");
+}
+
+void dump_cred_dist_stats(struct htc_target *target)
+{
+ struct htc_endpoint_credit_dist *ep_list;
+
+ if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_TRC))
+ return;
+
+ list_for_each_entry(ep_list, &target->cred_dist_list, list)
+ dump_cred_dist(ep_list);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:%p dist:%p\n",
+ target->cred_dist_cntxt, NULL);
+ ath6kl_dbg(ATH6KL_DBG_TRC, "credit distribution, total : %d, free : %d\n",
+ target->cred_dist_cntxt->total_avail_credits,
+ target->cred_dist_cntxt->cur_free_credits);
+}
+
+static int ath6kl_debugfs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war)
+{
+ switch (war) {
+ case ATH6KL_WAR_INVALID_RATE:
+ ar->debug.war_stats.invalid_rate++;
+ break;
+ }
+}
+
+static ssize_t read_file_war_stats(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ char *buf;
+ unsigned int len = 0, buf_len = 1500;
+ ssize_t ret_cnt;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%25s\n",
+ "Workaround stats");
+ len += scnprintf(buf + len, buf_len - len, "%25s\n\n",
+ "=================");
+ len += scnprintf(buf + len, buf_len - len, "%20s %10u\n",
+ "Invalid rates", ar->debug.war_stats.invalid_rate);
+
+ if (WARN_ON(len > buf_len))
+ len = buf_len;
+
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+ return ret_cnt;
+}
+
+static const struct file_operations fops_war_stats = {
+ .read = read_file_war_stats,
+ .open = ath6kl_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf,
+ size_t buf_len)
+{
+ struct circ_buf *fwlog = &ar->debug.fwlog_buf;
+ size_t space;
+ int i;
+
+ /* entries must all be equal size */
+ if (WARN_ON(buf_len != ATH6KL_FWLOG_SLOT_SIZE))
+ return;
+
+ space = CIRC_SPACE(fwlog->head, fwlog->tail, ATH6KL_FWLOG_SIZE);
+ if (space < buf_len)
+ /* discard oldest slot */
+ fwlog->tail = (fwlog->tail + ATH6KL_FWLOG_SLOT_SIZE) &
+ (ATH6KL_FWLOG_SIZE - 1);
+
+ for (i = 0; i < buf_len; i += space) {
+ space = CIRC_SPACE_TO_END(fwlog->head, fwlog->tail,
+ ATH6KL_FWLOG_SIZE);
+
+ if ((size_t) space > buf_len - i)
+ space = buf_len - i;
+
+ memcpy(&fwlog->buf[fwlog->head], buf, space);
+ fwlog->head = (fwlog->head + space) & (ATH6KL_FWLOG_SIZE - 1);
+ }
+
+}
+
+void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
+{
+ struct ath6kl_fwlog_slot *slot = ar->debug.fwlog_tmp;
+ size_t slot_len;
+
+ if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
+ return;
+
+ spin_lock_bh(&ar->debug.fwlog_lock);
+
+ slot->timestamp = cpu_to_le32(jiffies);
+ slot->length = cpu_to_le32(len);
+ memcpy(slot->payload, buf, len);
+
+ slot_len = sizeof(*slot) + len;
+
+ if (slot_len < ATH6KL_FWLOG_SLOT_SIZE)
+ memset(slot->payload + len, 0,
+ ATH6KL_FWLOG_SLOT_SIZE - slot_len);
+
+ ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE);
+
+ spin_unlock_bh(&ar->debug.fwlog_lock);
+}
+
+static bool ath6kl_debug_fwlog_empty(struct ath6kl *ar)
+{
+ return CIRC_CNT(ar->debug.fwlog_buf.head,
+ ar->debug.fwlog_buf.tail,
+ ATH6KL_FWLOG_SLOT_SIZE) == 0;
+}
+
+static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct circ_buf *fwlog = &ar->debug.fwlog_buf;
+ size_t len = 0, buf_len = count;
+ ssize_t ret_cnt;
+ char *buf;
+ int ccnt;
+
+ buf = vmalloc(buf_len);
+ if (!buf)
+ return -ENOMEM;
+
+ /* read undelivered logs from firmware */
+ ath6kl_read_fwlogs(ar);
+
+ spin_lock_bh(&ar->debug.fwlog_lock);
+
+ while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) {
+ ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail,
+ ATH6KL_FWLOG_SIZE);
+
+ if ((size_t) ccnt > buf_len - len)
+ ccnt = buf_len - len;
+
+ memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt);
+ len += ccnt;
+
+ fwlog->tail = (fwlog->tail + ccnt) &
+ (ATH6KL_FWLOG_SIZE - 1);
+ }
+
+ spin_unlock_bh(&ar->debug.fwlog_lock);
+
+ if (WARN_ON(len > buf_len))
+ len = buf_len;
+
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ vfree(buf);
+
+ return ret_cnt;
+}
+
+static const struct file_operations fops_fwlog = {
+ .open = ath6kl_debugfs_open,
+ .read = ath6kl_fwlog_read,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ char buf[16];
+ int len;
+
+ len = snprintf(buf, sizeof(buf), "0x%x\n", ar->debug.fwlog_mask);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath6kl_fwlog_mask_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ int ret;
+
+ ret = kstrtou32_from_user(user_buf, count, 0, &ar->debug.fwlog_mask);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_wmi_config_debug_module_cmd(ar->wmi,
+ ATH6KL_FWLOG_VALID_MASK,
+ ar->debug.fwlog_mask);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations fops_fwlog_mask = {
+ .open = ath6kl_debugfs_open,
+ .read = ath6kl_fwlog_mask_read,
+ .write = ath6kl_fwlog_mask_write,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct target_stats *tgt_stats = &ar->target_stats;
+ char *buf;
+ unsigned int len = 0, buf_len = 1500;
+ int i;
+ long left;
+ ssize_t ret_cnt;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (down_interruptible(&ar->sem)) {
+ kfree(buf);
+ return -EBUSY;
+ }
+
+ set_bit(STATS_UPDATE_PEND, &ar->flag);
+
+ if (ath6kl_wmi_get_stats_cmd(ar->wmi)) {
+ up(&ar->sem);
+ kfree(buf);
+ return -EIO;
+ }
+
+ left = wait_event_interruptible_timeout(ar->event_wq,
+ !test_bit(STATS_UPDATE_PEND,
+ &ar->flag), WMI_TIMEOUT);
+
+ up(&ar->sem);
+
+ if (left <= 0) {
+ kfree(buf);
+ return -ETIMEDOUT;
+ }
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%25s\n",
+ "Target Tx stats");
+ len += scnprintf(buf + len, buf_len - len, "%25s\n\n",
+ "=================");
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Ucast packets", tgt_stats->tx_ucast_pkt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Bcast packets", tgt_stats->tx_bcast_pkt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Ucast byte", tgt_stats->tx_ucast_byte);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Bcast byte", tgt_stats->tx_bcast_byte);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Rts success cnt", tgt_stats->tx_rts_success_cnt);
+ for (i = 0; i < 4; i++)
+ len += scnprintf(buf + len, buf_len - len,
+ "%18s %d %10llu\n", "PER on ac",
+ i, tgt_stats->tx_pkt_per_ac[i]);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Error", tgt_stats->tx_err);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Fail count", tgt_stats->tx_fail_cnt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Retry count", tgt_stats->tx_retry_cnt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Multi retry cnt", tgt_stats->tx_mult_retry_cnt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Rts fail cnt", tgt_stats->tx_rts_fail_cnt);
+ len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n\n",
+ "TKIP counter measure used",
+ tgt_stats->tkip_cnter_measures_invoked);
+
+ len += scnprintf(buf + len, buf_len - len, "%25s\n",
+ "Target Rx stats");
+ len += scnprintf(buf + len, buf_len - len, "%25s\n",
+ "=================");
+
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Ucast packets", tgt_stats->rx_ucast_pkt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
+ "Ucast Rate", tgt_stats->rx_ucast_rate);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Bcast packets", tgt_stats->rx_bcast_pkt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Ucast byte", tgt_stats->rx_ucast_byte);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Bcast byte", tgt_stats->rx_bcast_byte);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Fragmented pkt", tgt_stats->rx_frgment_pkt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Error", tgt_stats->rx_err);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "CRC Err", tgt_stats->rx_crc_err);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Key chache miss", tgt_stats->rx_key_cache_miss);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Decrypt Err", tgt_stats->rx_decrypt_err);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Duplicate frame", tgt_stats->rx_dupl_frame);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Tkip Mic failure", tgt_stats->tkip_local_mic_fail);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "TKIP format err", tgt_stats->tkip_fmt_err);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "CCMP format Err", tgt_stats->ccmp_fmt_err);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n\n",
+ "CCMP Replay Err", tgt_stats->ccmp_replays);
+
+ len += scnprintf(buf + len, buf_len - len, "%25s\n",
+ "Misc Target stats");
+ len += scnprintf(buf + len, buf_len - len, "%25s\n",
+ "=================");
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Beacon Miss count", tgt_stats->cs_bmiss_cnt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Num Connects", tgt_stats->cs_connect_cnt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+ "Num disconnects", tgt_stats->cs_discon_cnt);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
+ "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi);
+
+ if (len > buf_len)
+ len = buf_len;
+
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+ return ret_cnt;
+}
+
+static const struct file_operations fops_tgt_stats = {
+ .read = read_file_tgt_stats,
+ .open = ath6kl_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+#define print_credit_info(fmt_str, ep_list_field) \
+ (len += scnprintf(buf + len, buf_len - len, fmt_str, \
+ ep_list->ep_list_field))
+#define CREDIT_INFO_DISPLAY_STRING_LEN 200
+#define CREDIT_INFO_LEN 128
+
+static ssize_t read_file_credit_dist_stats(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct htc_target *target = ar->htc_target;
+ struct htc_endpoint_credit_dist *ep_list;
+ char *buf;
+ unsigned int buf_len, len = 0;
+ ssize_t ret_cnt;
+
+ buf_len = CREDIT_INFO_DISPLAY_STRING_LEN +
+ get_queue_depth(&target->cred_dist_list) * CREDIT_INFO_LEN;
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
+ "Total Avail Credits: ",
+ target->cred_dist_cntxt->total_avail_credits);
+ len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
+ "Free credits :",
+ target->cred_dist_cntxt->cur_free_credits);
+
+ len += scnprintf(buf + len, buf_len - len,
+ " Epid Flags Cred_norm Cred_min Credits Cred_assngd"
+ " Seek_cred Cred_sz Cred_per_msg Cred_to_dist"
+ " qdepth\n");
+
+ list_for_each_entry(ep_list, &target->cred_dist_list, list) {
+ print_credit_info(" %2d", endpoint);
+ print_credit_info("%10x", dist_flags);
+ print_credit_info("%8d", cred_norm);
+ print_credit_info("%9d", cred_min);
+ print_credit_info("%9d", credits);
+ print_credit_info("%10d", cred_assngd);
+ print_credit_info("%13d", seek_cred);
+ print_credit_info("%12d", cred_sz);
+ print_credit_info("%9d", cred_per_msg);
+ print_credit_info("%14d", cred_to_dist);
+ len += scnprintf(buf + len, buf_len - len, "%12d\n",
+ get_queue_depth(&((struct htc_endpoint *)
+ ep_list->htc_rsvd)->txq));
+ }
+
+ if (len > buf_len)
+ len = buf_len;
+
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+ return ret_cnt;
+}
+
+static const struct file_operations fops_credit_dist_stats = {
+ .read = read_file_credit_dist_stats,
+ .open = ath6kl_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static unsigned long ath6kl_get_num_reg(void)
+{
+ int i;
+ unsigned long n_reg = 0;
+
+ for (i = 0; i < ARRAY_SIZE(diag_reg); i++)
+ n_reg = n_reg +
+ (diag_reg[i].reg_end - diag_reg[i].reg_start) / 4 + 1;
+
+ return n_reg;
+}
+
+static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(diag_reg); i++) {
+ if (reg_addr >= diag_reg[i].reg_start &&
+ reg_addr <= diag_reg[i].reg_end)
+ return true;
+ }
+
+ return false;
+}
+
+static ssize_t ath6kl_regread_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ u8 buf[50];
+ unsigned int len = 0;
+
+ if (ar->debug.dbgfs_diag_reg)
+ len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n",
+ ar->debug.dbgfs_diag_reg);
+ else
+ len += scnprintf(buf + len, sizeof(buf) - len,
+ "All diag registers\n");
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath6kl_regread_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ u8 buf[50];
+ unsigned int len;
+ unsigned long reg_addr;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (strict_strtoul(buf, 0, &reg_addr))
+ return -EINVAL;
+
+ if ((reg_addr % 4) != 0)
+ return -EINVAL;
+
+ if (reg_addr && !ath6kl_dbg_is_diag_reg_valid(reg_addr))
+ return -EINVAL;
+
+ ar->debug.dbgfs_diag_reg = reg_addr;
+
+ return count;
+}
+
+static const struct file_operations fops_diag_reg_read = {
+ .read = ath6kl_regread_read,
+ .write = ath6kl_regread_write,
+ .open = ath6kl_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static int ath6kl_regdump_open(struct inode *inode, struct file *file)
+{
+ struct ath6kl *ar = inode->i_private;
+ u8 *buf;
+ unsigned long int reg_len;
+ unsigned int len = 0, n_reg;
+ u32 addr;
+ __le32 reg_val;
+ int i, status;
+
+ /* Dump all the registers if no register is specified */
+ if (!ar->debug.dbgfs_diag_reg)
+ n_reg = ath6kl_get_num_reg();
+ else
+ n_reg = 1;
+
+ reg_len = n_reg * REG_OUTPUT_LEN_PER_LINE;
+ if (n_reg > 1)
+ reg_len += REGTYPE_STR_LEN;
+
+ buf = vmalloc(reg_len);
+ if (!buf)
+ return -ENOMEM;
+
+ if (n_reg == 1) {
+ addr = ar->debug.dbgfs_diag_reg;
+
+ status = ath6kl_diag_read32(ar,
+ TARG_VTOP(ar->target_type, addr),
+ (u32 *)&reg_val);
+ if (status)
+ goto fail_reg_read;
+
+ len += scnprintf(buf + len, reg_len - len,
+ "0x%06x 0x%08x\n", addr, le32_to_cpu(reg_val));
+ goto done;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(diag_reg); i++) {
+ len += scnprintf(buf + len, reg_len - len,
+ "%s\n", diag_reg[i].reg_info);
+ for (addr = diag_reg[i].reg_start;
+ addr <= diag_reg[i].reg_end; addr += 4) {
+ status = ath6kl_diag_read32(ar,
+ TARG_VTOP(ar->target_type, addr),
+ (u32 *)&reg_val);
+ if (status)
+ goto fail_reg_read;
+
+ len += scnprintf(buf + len, reg_len - len,
+ "0x%06x 0x%08x\n",
+ addr, le32_to_cpu(reg_val));
+ }
+ }
+
+done:
+ file->private_data = buf;
+ return 0;
+
+fail_reg_read:
+ ath6kl_warn("Unable to read memory:%u\n", addr);
+ vfree(buf);
+ return -EIO;
+}
+
+static ssize_t ath6kl_regdump_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ u8 *buf = file->private_data;
+ return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static int ath6kl_regdump_release(struct inode *inode, struct file *file)
+{
+ vfree(file->private_data);
+ return 0;
+}
+
+static const struct file_operations fops_reg_dump = {
+ .open = ath6kl_regdump_open,
+ .read = ath6kl_regdump_read,
+ .release = ath6kl_regdump_release,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_lrssi_roam_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ unsigned long lrssi_roam_threshold;
+ char buf[32];
+ ssize_t len;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ if (strict_strtoul(buf, 0, &lrssi_roam_threshold))
+ return -EINVAL;
+
+ ar->lrssi_roam_threshold = lrssi_roam_threshold;
+
+ ath6kl_wmi_set_roam_lrssi_cmd(ar->wmi, ar->lrssi_roam_threshold);
+
+ return count;
+}
+
+static ssize_t ath6kl_lrssi_roam_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ char buf[32];
+ unsigned int len;
+
+ len = snprintf(buf, sizeof(buf), "%u\n", ar->lrssi_roam_threshold);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_lrssi_roam_threshold = {
+ .read = ath6kl_lrssi_roam_read,
+ .write = ath6kl_lrssi_roam_write,
+ .open = ath6kl_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_regwrite_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ u8 buf[32];
+ unsigned int len = 0;
+
+ len = scnprintf(buf, sizeof(buf), "Addr: 0x%x Val: 0x%x\n",
+ ar->debug.diag_reg_addr_wr, ar->debug.diag_reg_val_wr);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath6kl_regwrite_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ char buf[32];
+ char *sptr, *token;
+ unsigned int len = 0;
+ u32 reg_addr, reg_val;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, "=");
+ if (!token)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &reg_addr))
+ return -EINVAL;
+
+ if (!ath6kl_dbg_is_diag_reg_valid(reg_addr))
+ return -EINVAL;
+
+ if (kstrtou32(sptr, 0, &reg_val))
+ return -EINVAL;
+
+ ar->debug.diag_reg_addr_wr = reg_addr;
+ ar->debug.diag_reg_val_wr = reg_val;
+
+ if (ath6kl_diag_write32(ar, ar->debug.diag_reg_addr_wr,
+ cpu_to_le32(ar->debug.diag_reg_val_wr)))
+ return -EIO;
+
+ return count;
+}
+
+static const struct file_operations fops_diag_reg_write = {
+ .read = ath6kl_regwrite_read,
+ .write = ath6kl_regwrite_write,
+ .open = ath6kl_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+int ath6kl_debug_init(struct ath6kl *ar)
+{
+ ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE);
+ if (ar->debug.fwlog_buf.buf == NULL)
+ return -ENOMEM;
+
+ ar->debug.fwlog_tmp = kmalloc(ATH6KL_FWLOG_SLOT_SIZE, GFP_KERNEL);
+ if (ar->debug.fwlog_tmp == NULL) {
+ vfree(ar->debug.fwlog_buf.buf);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&ar->debug.fwlog_lock);
+
+ /*
+ * Actually we are lying here but don't know how to read the mask
+ * value from the firmware.
+ */
+ ar->debug.fwlog_mask = 0;
+
+ ar->debugfs_phy = debugfs_create_dir("ath6kl",
+ ar->wdev->wiphy->debugfsdir);
+ if (!ar->debugfs_phy) {
+ vfree(ar->debug.fwlog_buf.buf);
+ kfree(ar->debug.fwlog_tmp);
+ return -ENOMEM;
+ }
+
+ debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
+ &fops_tgt_stats);
+
+ debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar,
+ &fops_credit_dist_stats);
+
+ debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
+ &fops_fwlog);
+
+ debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy,
+ ar, &fops_fwlog_mask);
+
+ debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
+ &fops_diag_reg_read);
+
+ debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar,
+ &fops_reg_dump);
+
+ debugfs_create_file("lrssi_roam_threshold", S_IRUSR | S_IWUSR,
+ ar->debugfs_phy, ar, &fops_lrssi_roam_threshold);
+
+ debugfs_create_file("reg_write", S_IRUSR | S_IWUSR,
+ ar->debugfs_phy, ar, &fops_diag_reg_write);
+
+ debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar,
+ &fops_war_stats);
+
+ return 0;
+}
+
+void ath6kl_debug_cleanup(struct ath6kl *ar)
+{
+ vfree(ar->debug.fwlog_buf.buf);
+ kfree(ar->debug.fwlog_tmp);
+}
+
+#endif
diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h
new file mode 100644
index 000000000000..7b7675f70a10
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/debug.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include "htc_hif.h"
+
+enum ATH6K_DEBUG_MASK {
+ ATH6KL_DBG_WLAN_CONNECT = BIT(0), /* wlan connect */
+ ATH6KL_DBG_WLAN_SCAN = BIT(1), /* wlan scan */
+ ATH6KL_DBG_WLAN_TX = BIT(2), /* wlan tx */
+ ATH6KL_DBG_WLAN_RX = BIT(3), /* wlan rx */
+ ATH6KL_DBG_BMI = BIT(4), /* bmi tracing */
+ ATH6KL_DBG_HTC_SEND = BIT(5), /* htc send */
+ ATH6KL_DBG_HTC_RECV = BIT(6), /* htc recv */
+ ATH6KL_DBG_IRQ = BIT(7), /* interrupt processing */
+ ATH6KL_DBG_PM = BIT(8), /* power management */
+ ATH6KL_DBG_WLAN_NODE = BIT(9), /* general wlan node tracing */
+ ATH6KL_DBG_WMI = BIT(10), /* wmi tracing */
+ ATH6KL_DBG_TRC = BIT(11), /* generic func tracing */
+ ATH6KL_DBG_SCATTER = BIT(12), /* hif scatter tracing */
+ ATH6KL_DBG_WLAN_CFG = BIT(13), /* cfg80211 i/f file tracing */
+ ATH6KL_DBG_RAW_BYTES = BIT(14), /* dump tx/rx frames */
+ ATH6KL_DBG_AGGR = BIT(15), /* aggregation */
+ ATH6KL_DBG_SDIO = BIT(16),
+ ATH6KL_DBG_SDIO_DUMP = BIT(17),
+ ATH6KL_DBG_BOOT = BIT(18), /* driver init and fw boot */
+ ATH6KL_DBG_WMI_DUMP = BIT(19),
+ ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */
+};
+
+extern unsigned int debug_mask;
+extern __printf(2, 3)
+int ath6kl_printk(const char *level, const char *fmt, ...);
+
+#define ath6kl_info(fmt, ...) \
+ ath6kl_printk(KERN_INFO, fmt, ##__VA_ARGS__)
+#define ath6kl_err(fmt, ...) \
+ ath6kl_printk(KERN_ERR, fmt, ##__VA_ARGS__)
+#define ath6kl_warn(fmt, ...) \
+ ath6kl_printk(KERN_WARNING, fmt, ##__VA_ARGS__)
+
+#define AR_DBG_LVL_CHECK(mask) (debug_mask & mask)
+
+enum ath6kl_war {
+ ATH6KL_WAR_INVALID_RATE,
+};
+
+#ifdef CONFIG_ATH6KL_DEBUG
+#define ath6kl_dbg(mask, fmt, ...) \
+ ({ \
+ int rtn; \
+ if (debug_mask & mask) \
+ rtn = ath6kl_printk(KERN_DEBUG, fmt, ##__VA_ARGS__); \
+ else \
+ rtn = 0; \
+ \
+ rtn; \
+ })
+
+static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
+ const char *msg, const char *prefix,
+ const void *buf, size_t len)
+{
+ if (debug_mask & mask) {
+ if (msg)
+ ath6kl_dbg(mask, "%s\n", msg);
+
+ print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
+ }
+}
+
+void ath6kl_dump_registers(struct ath6kl_device *dev,
+ struct ath6kl_irq_proc_registers *irq_proc_reg,
+ struct ath6kl_irq_enable_reg *irq_en_reg);
+void dump_cred_dist_stats(struct htc_target *target);
+void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len);
+void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war);
+int ath6kl_debug_init(struct ath6kl *ar);
+void ath6kl_debug_cleanup(struct ath6kl *ar);
+
+#else
+static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
+ const char *fmt, ...)
+{
+ return 0;
+}
+
+static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
+ const char *msg, const char *prefix,
+ const void *buf, size_t len)
+{
+}
+
+static inline void ath6kl_dump_registers(struct ath6kl_device *dev,
+ struct ath6kl_irq_proc_registers *irq_proc_reg,
+ struct ath6kl_irq_enable_reg *irq_en_reg)
+{
+
+}
+static inline void dump_cred_dist_stats(struct htc_target *target)
+{
+}
+
+static inline void ath6kl_debug_fwlog_event(struct ath6kl *ar,
+ const void *buf, size_t len)
+{
+}
+
+static inline void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war)
+{
+}
+
+static inline int ath6kl_debug_init(struct ath6kl *ar)
+{
+ return 0;
+}
+
+static inline void ath6kl_debug_cleanup(struct ath6kl *ar)
+{
+}
+
+#endif
+#endif
diff --git a/drivers/net/wireless/ath/ath6kl/hif-ops.h b/drivers/net/wireless/ath/ath6kl/hif-ops.h
new file mode 100644
index 000000000000..d6c898f3d0b3
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/hif-ops.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HIF_OPS_H
+#define HIF_OPS_H
+
+#include "hif.h"
+
+static inline int hif_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
+ u32 len, u32 request)
+{
+ return ar->hif_ops->read_write_sync(ar, addr, buf, len, request);
+}
+
+static inline int hif_write_async(struct ath6kl *ar, u32 address, u8 *buffer,
+ u32 length, u32 request,
+ struct htc_packet *packet)
+{
+ return ar->hif_ops->write_async(ar, address, buffer, length,
+ request, packet);
+}
+static inline void ath6kl_hif_irq_enable(struct ath6kl *ar)
+{
+ return ar->hif_ops->irq_enable(ar);
+}
+
+static inline void ath6kl_hif_irq_disable(struct ath6kl *ar)
+{
+ return ar->hif_ops->irq_disable(ar);
+}
+
+static inline struct hif_scatter_req *hif_scatter_req_get(struct ath6kl *ar)
+{
+ return ar->hif_ops->scatter_req_get(ar);
+}
+
+static inline void hif_scatter_req_add(struct ath6kl *ar,
+ struct hif_scatter_req *s_req)
+{
+ return ar->hif_ops->scatter_req_add(ar, s_req);
+}
+
+static inline int ath6kl_hif_enable_scatter(struct ath6kl *ar)
+{
+ return ar->hif_ops->enable_scatter(ar);
+}
+
+static inline int ath6kl_hif_scat_req_rw(struct ath6kl *ar,
+ struct hif_scatter_req *scat_req)
+{
+ return ar->hif_ops->scat_req_rw(ar, scat_req);
+}
+
+static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar)
+{
+ return ar->hif_ops->cleanup_scatter(ar);
+}
+
+static inline int ath6kl_hif_suspend(struct ath6kl *ar)
+{
+ return ar->hif_ops->suspend(ar);
+}
+
+#endif
diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h
new file mode 100644
index 000000000000..797e2d1d9bf9
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/hif.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HIF_H
+#define HIF_H
+
+#include "common.h"
+#include "core.h"
+
+#include <linux/scatterlist.h>
+
+#define BUS_REQUEST_MAX_NUM 64
+#define HIF_MBOX_BLOCK_SIZE 128
+#define HIF_MBOX0_BLOCK_SIZE 1
+
+#define HIF_DMA_BUFFER_SIZE (32 * 1024)
+#define CMD53_FIXED_ADDRESS 1
+#define CMD53_INCR_ADDRESS 2
+
+#define MAX_SCATTER_REQUESTS 4
+#define MAX_SCATTER_ENTRIES_PER_REQ 16
+#define MAX_SCATTER_REQ_TRANSFER_SIZE (32 * 1024)
+
+#define MANUFACTURER_ID_AR6003_BASE 0x300
+ /* SDIO manufacturer ID and Codes */
+#define MANUFACTURER_ID_ATH6KL_BASE_MASK 0xFF00
+#define MANUFACTURER_CODE 0x271 /* Atheros */
+
+/* Mailbox address in SDIO address space */
+#define HIF_MBOX_BASE_ADDR 0x800
+#define HIF_MBOX_WIDTH 0x800
+
+#define HIF_MBOX_END_ADDR (HTC_MAILBOX_NUM_MAX * HIF_MBOX_WIDTH - 1)
+
+/* version 1 of the chip has only a 12K extended mbox range */
+#define HIF_MBOX0_EXT_BASE_ADDR 0x4000
+#define HIF_MBOX0_EXT_WIDTH (12*1024)
+
+/* GMBOX addresses */
+#define HIF_GMBOX_BASE_ADDR 0x7000
+#define HIF_GMBOX_WIDTH 0x4000
+
+/* interrupt mode register */
+#define CCCR_SDIO_IRQ_MODE_REG 0xF0
+
+/* mode to enable special 4-bit interrupt assertion without clock */
+#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ (1 << 0)
+
+struct bus_request {
+ struct list_head list;
+
+ /* request data */
+ u32 address;
+
+ u8 *buffer;
+ u32 length;
+ u32 request;
+ struct htc_packet *packet;
+ int status;
+
+ /* this is a scatter request */
+ struct hif_scatter_req *scat_req;
+};
+
+/* direction of transfer (read/write) */
+#define HIF_READ 0x00000001
+#define HIF_WRITE 0x00000002
+#define HIF_DIR_MASK (HIF_READ | HIF_WRITE)
+
+/*
+ * emode - This indicates the whether the command is to be executed in a
+ * blocking or non-blocking fashion (HIF_SYNCHRONOUS/
+ * HIF_ASYNCHRONOUS). The read/write data paths in HTC have been
+ * implemented using the asynchronous mode allowing the the bus
+ * driver to indicate the completion of operation through the
+ * registered callback routine. The requirement primarily comes
+ * from the contexts these operations get called from (a driver's
+ * transmit context or the ISR context in case of receive).
+ * Support for both of these modes is essential.
+ */
+#define HIF_SYNCHRONOUS 0x00000010
+#define HIF_ASYNCHRONOUS 0x00000020
+#define HIF_EMODE_MASK (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS)
+
+/*
+ * dmode - An interface may support different kinds of commands based on
+ * the tradeoff between the amount of data it can carry and the
+ * setup time. Byte and Block modes are supported (HIF_BYTE_BASIS/
+ * HIF_BLOCK_BASIS). In case of latter, the data is rounded off
+ * to the nearest block size by padding. The size of the block is
+ * configurable at compile time using the HIF_BLOCK_SIZE and is
+ * negotiated with the target during initialization after the
+ * ATH6KL interrupts are enabled.
+ */
+#define HIF_BYTE_BASIS 0x00000040
+#define HIF_BLOCK_BASIS 0x00000080
+#define HIF_DMODE_MASK (HIF_BYTE_BASIS | HIF_BLOCK_BASIS)
+
+/*
+ * amode - This indicates if the address has to be incremented on ATH6KL
+ * after every read/write operation (HIF?FIXED_ADDRESS/
+ * HIF_INCREMENTAL_ADDRESS).
+ */
+#define HIF_FIXED_ADDRESS 0x00000100
+#define HIF_INCREMENTAL_ADDRESS 0x00000200
+#define HIF_AMODE_MASK (HIF_FIXED_ADDRESS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_ASYNC_BYTE_INC \
+ (HIF_WRITE | HIF_ASYNCHRONOUS | \
+ HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_ASYNC_BLOCK_INC \
+ (HIF_WRITE | HIF_ASYNCHRONOUS | \
+ HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_SYNC_BYTE_FIX \
+ (HIF_WRITE | HIF_SYNCHRONOUS | \
+ HIF_BYTE_BASIS | HIF_FIXED_ADDRESS)
+
+#define HIF_WR_SYNC_BYTE_INC \
+ (HIF_WRITE | HIF_SYNCHRONOUS | \
+ HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_SYNC_BLOCK_INC \
+ (HIF_WRITE | HIF_SYNCHRONOUS | \
+ HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_RD_SYNC_BYTE_INC \
+ (HIF_READ | HIF_SYNCHRONOUS | \
+ HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_RD_SYNC_BYTE_FIX \
+ (HIF_READ | HIF_SYNCHRONOUS | \
+ HIF_BYTE_BASIS | HIF_FIXED_ADDRESS)
+
+#define HIF_RD_ASYNC_BLOCK_FIX \
+ (HIF_READ | HIF_ASYNCHRONOUS | \
+ HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS)
+
+#define HIF_RD_SYNC_BLOCK_FIX \
+ (HIF_READ | HIF_SYNCHRONOUS | \
+ HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS)
+
+struct hif_scatter_item {
+ u8 *buf;
+ int len;
+ struct htc_packet *packet;
+};
+
+struct hif_scatter_req {
+ struct list_head list;
+ /* address for the read/write operation */
+ u32 addr;
+
+ /* request flags */
+ u32 req;
+
+ /* total length of entire transfer */
+ u32 len;
+
+ bool virt_scat;
+
+ void (*complete) (struct htc_target *, struct hif_scatter_req *);
+ int status;
+ int scat_entries;
+
+ struct bus_request *busrequest;
+ struct scatterlist *sgentries;
+
+ /* bounce buffer for upper layers to copy to/from */
+ u8 *virt_dma_buf;
+
+ struct hif_scatter_item scat_list[1];
+};
+
+struct ath6kl_hif_ops {
+ int (*read_write_sync)(struct ath6kl *ar, u32 addr, u8 *buf,
+ u32 len, u32 request);
+ int (*write_async)(struct ath6kl *ar, u32 address, u8 *buffer,
+ u32 length, u32 request, struct htc_packet *packet);
+
+ void (*irq_enable)(struct ath6kl *ar);
+ void (*irq_disable)(struct ath6kl *ar);
+
+ struct hif_scatter_req *(*scatter_req_get)(struct ath6kl *ar);
+ void (*scatter_req_add)(struct ath6kl *ar,
+ struct hif_scatter_req *s_req);
+ int (*enable_scatter)(struct ath6kl *ar);
+ int (*scat_req_rw) (struct ath6kl *ar,
+ struct hif_scatter_req *scat_req);
+ void (*cleanup_scatter)(struct ath6kl *ar);
+ int (*suspend)(struct ath6kl *ar);
+};
+
+#endif
diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c
new file mode 100644
index 000000000000..f88a7c9e4148
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/htc.c
@@ -0,0 +1,2478 @@
+/*
+ * Copyright (c) 2007-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "htc_hif.h"
+#include "debug.h"
+#include "hif-ops.h"
+#include <asm/unaligned.h>
+
+#define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask))
+
+static void ath6kl_htc_tx_buf_align(u8 **buf, unsigned long len)
+{
+ u8 *align_addr;
+
+ if (!IS_ALIGNED((unsigned long) *buf, 4)) {
+ align_addr = PTR_ALIGN(*buf - 4, 4);
+ memmove(align_addr, *buf, len);
+ *buf = align_addr;
+ }
+}
+
+static void ath6kl_htc_tx_prep_pkt(struct htc_packet *packet, u8 flags,
+ int ctrl0, int ctrl1)
+{
+ struct htc_frame_hdr *hdr;
+
+ packet->buf -= HTC_HDR_LENGTH;
+ hdr = (struct htc_frame_hdr *)packet->buf;
+
+ /* Endianess? */
+ put_unaligned((u16)packet->act_len, &hdr->payld_len);
+ hdr->flags = flags;
+ hdr->eid = packet->endpoint;
+ hdr->ctrl[0] = ctrl0;
+ hdr->ctrl[1] = ctrl1;
+}
+
+static void htc_reclaim_txctrl_buf(struct htc_target *target,
+ struct htc_packet *pkt)
+{
+ spin_lock_bh(&target->htc_lock);
+ list_add_tail(&pkt->list, &target->free_ctrl_txbuf);
+ spin_unlock_bh(&target->htc_lock);
+}
+
+static struct htc_packet *htc_get_control_buf(struct htc_target *target,
+ bool tx)
+{
+ struct htc_packet *packet = NULL;
+ struct list_head *buf_list;
+
+ buf_list = tx ? &target->free_ctrl_txbuf : &target->free_ctrl_rxbuf;
+
+ spin_lock_bh(&target->htc_lock);
+
+ if (list_empty(buf_list)) {
+ spin_unlock_bh(&target->htc_lock);
+ return NULL;
+ }
+
+ packet = list_first_entry(buf_list, struct htc_packet, list);
+ list_del(&packet->list);
+ spin_unlock_bh(&target->htc_lock);
+
+ if (tx)
+ packet->buf = packet->buf_start + HTC_HDR_LENGTH;
+
+ return packet;
+}
+
+static void htc_tx_comp_update(struct htc_target *target,
+ struct htc_endpoint *endpoint,
+ struct htc_packet *packet)
+{
+ packet->completion = NULL;
+ packet->buf += HTC_HDR_LENGTH;
+
+ if (!packet->status)
+ return;
+
+ ath6kl_err("req failed (status:%d, ep:%d, len:%d creds:%d)\n",
+ packet->status, packet->endpoint, packet->act_len,
+ packet->info.tx.cred_used);
+
+ /* on failure to submit, reclaim credits for this packet */
+ spin_lock_bh(&target->tx_lock);
+ endpoint->cred_dist.cred_to_dist +=
+ packet->info.tx.cred_used;
+ endpoint->cred_dist.txq_depth = get_queue_depth(&endpoint->txq);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:0x%p dist:0x%p\n",
+ target->cred_dist_cntxt, &target->cred_dist_list);
+
+ ath6k_credit_distribute(target->cred_dist_cntxt,
+ &target->cred_dist_list,
+ HTC_CREDIT_DIST_SEND_COMPLETE);
+
+ spin_unlock_bh(&target->tx_lock);
+}
+
+static void htc_tx_complete(struct htc_endpoint *endpoint,
+ struct list_head *txq)
+{
+ if (list_empty(txq))
+ return;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "send complete ep %d, (%d pkts)\n",
+ endpoint->eid, get_queue_depth(txq));
+
+ ath6kl_tx_complete(endpoint->target->dev->ar, txq);
+}
+
+static void htc_tx_comp_handler(struct htc_target *target,
+ struct htc_packet *packet)
+{
+ struct htc_endpoint *endpoint = &target->endpoint[packet->endpoint];
+ struct list_head container;
+
+ htc_tx_comp_update(target, endpoint, packet);
+ INIT_LIST_HEAD(&container);
+ list_add_tail(&packet->list, &container);
+ /* do completion */
+ htc_tx_complete(endpoint, &container);
+}
+
+static void htc_async_tx_scat_complete(struct htc_target *target,
+ struct hif_scatter_req *scat_req)
+{
+ struct htc_endpoint *endpoint;
+ struct htc_packet *packet;
+ struct list_head tx_compq;
+ int i;
+
+ INIT_LIST_HEAD(&tx_compq);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "htc_async_tx_scat_complete total len: %d entries: %d\n",
+ scat_req->len, scat_req->scat_entries);
+
+ if (scat_req->status)
+ ath6kl_err("send scatter req failed: %d\n", scat_req->status);
+
+ packet = scat_req->scat_list[0].packet;
+ endpoint = &target->endpoint[packet->endpoint];
+
+ /* walk through the scatter list and process */
+ for (i = 0; i < scat_req->scat_entries; i++) {
+ packet = scat_req->scat_list[i].packet;
+ if (!packet) {
+ WARN_ON(1);
+ return;
+ }
+
+ packet->status = scat_req->status;
+ htc_tx_comp_update(target, endpoint, packet);
+ list_add_tail(&packet->list, &tx_compq);
+ }
+
+ /* free scatter request */
+ hif_scatter_req_add(target->dev->ar, scat_req);
+
+ /* complete all packets */
+ htc_tx_complete(endpoint, &tx_compq);
+}
+
+static int ath6kl_htc_tx_issue(struct htc_target *target,
+ struct htc_packet *packet)
+{
+ int status;
+ bool sync = false;
+ u32 padded_len, send_len;
+
+ if (!packet->completion)
+ sync = true;
+
+ send_len = packet->act_len + HTC_HDR_LENGTH;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "%s: transmit len : %d (%s)\n",
+ __func__, send_len, sync ? "sync" : "async");
+
+ padded_len = CALC_TXRX_PADDED_LEN(target, send_len);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "DevSendPacket, padded len: %d mbox:0x%X (mode:%s)\n",
+ padded_len,
+ target->dev->ar->mbox_info.htc_addr,
+ sync ? "sync" : "async");
+
+ if (sync) {
+ status = hif_read_write_sync(target->dev->ar,
+ target->dev->ar->mbox_info.htc_addr,
+ packet->buf, padded_len,
+ HIF_WR_SYNC_BLOCK_INC);
+
+ packet->status = status;
+ packet->buf += HTC_HDR_LENGTH;
+ } else
+ status = hif_write_async(target->dev->ar,
+ target->dev->ar->mbox_info.htc_addr,
+ packet->buf, padded_len,
+ HIF_WR_ASYNC_BLOCK_INC, packet);
+
+ return status;
+}
+
+static int htc_check_credits(struct htc_target *target,
+ struct htc_endpoint *ep, u8 *flags,
+ enum htc_endpoint_id eid, unsigned int len,
+ int *req_cred)
+{
+
+ *req_cred = (len > target->tgt_cred_sz) ?
+ DIV_ROUND_UP(len, target->tgt_cred_sz) : 1;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "creds required:%d got:%d\n",
+ *req_cred, ep->cred_dist.credits);
+
+ if (ep->cred_dist.credits < *req_cred) {
+ if (eid == ENDPOINT_0)
+ return -EINVAL;
+
+ /* Seek more credits */
+ ep->cred_dist.seek_cred = *req_cred - ep->cred_dist.credits;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:0x%p dist:0x%p\n",
+ target->cred_dist_cntxt, &ep->cred_dist);
+
+ ath6k_seek_credits(target->cred_dist_cntxt, &ep->cred_dist);
+
+ ep->cred_dist.seek_cred = 0;
+
+ if (ep->cred_dist.credits < *req_cred) {
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "not enough credits for ep %d - leaving packet in queue\n",
+ eid);
+ return -EINVAL;
+ }
+ }
+
+ ep->cred_dist.credits -= *req_cred;
+ ep->ep_st.cred_cosumd += *req_cred;
+
+ /* When we are getting low on credits, ask for more */
+ if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) {
+ ep->cred_dist.seek_cred =
+ ep->cred_dist.cred_per_msg - ep->cred_dist.credits;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:0x%p dist:0x%p\n",
+ target->cred_dist_cntxt, &ep->cred_dist);
+
+ ath6k_seek_credits(target->cred_dist_cntxt, &ep->cred_dist);
+
+ /* see if we were successful in getting more */
+ if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) {
+ /* tell the target we need credits ASAP! */
+ *flags |= HTC_FLAGS_NEED_CREDIT_UPDATE;
+ ep->ep_st.cred_low_indicate += 1;
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "host needs credits\n");
+ }
+ }
+
+ return 0;
+}
+
+static void ath6kl_htc_tx_pkts_get(struct htc_target *target,
+ struct htc_endpoint *endpoint,
+ struct list_head *queue)
+{
+ int req_cred;
+ u8 flags;
+ struct htc_packet *packet;
+ unsigned int len;
+
+ while (true) {
+
+ flags = 0;
+
+ if (list_empty(&endpoint->txq))
+ break;
+ packet = list_first_entry(&endpoint->txq, struct htc_packet,
+ list);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "got head pkt:0x%p , queue depth: %d\n",
+ packet, get_queue_depth(&endpoint->txq));
+
+ len = CALC_TXRX_PADDED_LEN(target,
+ packet->act_len + HTC_HDR_LENGTH);
+
+ if (htc_check_credits(target, endpoint, &flags,
+ packet->endpoint, len, &req_cred))
+ break;
+
+ /* now we can fully move onto caller's queue */
+ packet = list_first_entry(&endpoint->txq, struct htc_packet,
+ list);
+ list_move_tail(&packet->list, queue);
+
+ /* save the number of credits this packet consumed */
+ packet->info.tx.cred_used = req_cred;
+
+ /* all TX packets are handled asynchronously */
+ packet->completion = htc_tx_comp_handler;
+ packet->context = target;
+ endpoint->ep_st.tx_issued += 1;
+
+ /* save send flags */
+ packet->info.tx.flags = flags;
+ packet->info.tx.seqno = endpoint->seqno;
+ endpoint->seqno++;
+ }
+}
+
+/* See if the padded tx length falls on a credit boundary */
+static int htc_get_credit_padding(unsigned int cred_sz, int *len,
+ struct htc_endpoint *ep)
+{
+ int rem_cred, cred_pad;
+
+ rem_cred = *len % cred_sz;
+
+ /* No padding needed */
+ if (!rem_cred)
+ return 0;
+
+ if (!(ep->conn_flags & HTC_FLGS_TX_BNDL_PAD_EN))
+ return -1;
+
+ /*
+ * The transfer consumes a "partial" credit, this
+ * packet cannot be bundled unless we add
+ * additional "dummy" padding (max 255 bytes) to
+ * consume the entire credit.
+ */
+ cred_pad = *len < cred_sz ? (cred_sz - *len) : rem_cred;
+
+ if ((cred_pad > 0) && (cred_pad <= 255))
+ *len += cred_pad;
+ else
+ /* The amount of padding is too large, send as non-bundled */
+ return -1;
+
+ return cred_pad;
+}
+
+static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target,
+ struct htc_endpoint *endpoint,
+ struct hif_scatter_req *scat_req,
+ int n_scat,
+ struct list_head *queue)
+{
+ struct htc_packet *packet;
+ int i, len, rem_scat, cred_pad;
+ int status = 0;
+
+ rem_scat = target->max_tx_bndl_sz;
+
+ for (i = 0; i < n_scat; i++) {
+ scat_req->scat_list[i].packet = NULL;
+
+ if (list_empty(queue))
+ break;
+
+ packet = list_first_entry(queue, struct htc_packet, list);
+ len = CALC_TXRX_PADDED_LEN(target,
+ packet->act_len + HTC_HDR_LENGTH);
+
+ cred_pad = htc_get_credit_padding(target->tgt_cred_sz,
+ &len, endpoint);
+ if (cred_pad < 0 || rem_scat < len) {
+ status = -ENOSPC;
+ break;
+ }
+
+ rem_scat -= len;
+ /* now remove it from the queue */
+ list_del(&packet->list);
+
+ scat_req->scat_list[i].packet = packet;
+ /* prepare packet and flag message as part of a send bundle */
+ ath6kl_htc_tx_prep_pkt(packet,
+ packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE,
+ cred_pad, packet->info.tx.seqno);
+ /* Make sure the buffer is 4-byte aligned */
+ ath6kl_htc_tx_buf_align(&packet->buf,
+ packet->act_len + HTC_HDR_LENGTH);
+ scat_req->scat_list[i].buf = packet->buf;
+ scat_req->scat_list[i].len = len;
+
+ scat_req->len += len;
+ scat_req->scat_entries++;
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "%d, adding pkt : 0x%p len:%d (remaining space:%d)\n",
+ i, packet, len, rem_scat);
+ }
+
+ /* Roll back scatter setup in case of any failure */
+ if (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE) {
+ for (i = scat_req->scat_entries - 1; i >= 0; i--) {
+ packet = scat_req->scat_list[i].packet;
+ if (packet) {
+ packet->buf += HTC_HDR_LENGTH;
+ list_add(&packet->list, queue);
+ }
+ }
+ return -EAGAIN;
+ }
+
+ return status;
+}
+
+/*
+ * Drain a queue and send as bundles this function may return without fully
+ * draining the queue when
+ *
+ * 1. scatter resources are exhausted
+ * 2. a message that will consume a partial credit will stop the
+ * bundling process early
+ * 3. we drop below the minimum number of messages for a bundle
+ */
+static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
+ struct list_head *queue,
+ int *sent_bundle, int *n_bundle_pkts)
+{
+ struct htc_target *target = endpoint->target;
+ struct hif_scatter_req *scat_req = NULL;
+ int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0;
+ int status;
+
+ while (true) {
+ status = 0;
+ n_scat = get_queue_depth(queue);
+ n_scat = min(n_scat, target->msg_per_bndl_max);
+
+ if (n_scat < HTC_MIN_HTC_MSGS_TO_BUNDLE)
+ /* not enough to bundle */
+ break;
+
+ scat_req = hif_scatter_req_get(target->dev->ar);
+
+ if (!scat_req) {
+ /* no scatter resources */
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "no more scatter resources\n");
+ break;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "pkts to scatter: %d\n",
+ n_scat);
+
+ scat_req->len = 0;
+ scat_req->scat_entries = 0;
+
+ status = ath6kl_htc_tx_setup_scat_list(target, endpoint,
+ scat_req, n_scat,
+ queue);
+ if (status == -EAGAIN) {
+ hif_scatter_req_add(target->dev->ar, scat_req);
+ break;
+ }
+
+ /* send path is always asynchronous */
+ scat_req->complete = htc_async_tx_scat_complete;
+ n_sent_bundle++;
+ tot_pkts_bundle += scat_req->scat_entries;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "send scatter total bytes: %d , entries: %d\n",
+ scat_req->len, scat_req->scat_entries);
+ ath6kldev_submit_scat_req(target->dev, scat_req, false);
+
+ if (status)
+ break;
+ }
+
+ *sent_bundle = n_sent_bundle;
+ *n_bundle_pkts = tot_pkts_bundle;
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "%s (sent:%d)\n",
+ __func__, n_sent_bundle);
+
+ return;
+}
+
+static void ath6kl_htc_tx_from_queue(struct htc_target *target,
+ struct htc_endpoint *endpoint)
+{
+ struct list_head txq;
+ struct htc_packet *packet;
+ int bundle_sent;
+ int n_pkts_bundle;
+
+ spin_lock_bh(&target->tx_lock);
+
+ endpoint->tx_proc_cnt++;
+ if (endpoint->tx_proc_cnt > 1) {
+ endpoint->tx_proc_cnt--;
+ spin_unlock_bh(&target->tx_lock);
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "htc_try_send (busy)\n");
+ return;
+ }
+
+ /*
+ * drain the endpoint TX queue for transmission as long
+ * as we have enough credits.
+ */
+ INIT_LIST_HEAD(&txq);
+
+ while (true) {
+
+ if (list_empty(&endpoint->txq))
+ break;
+
+ ath6kl_htc_tx_pkts_get(target, endpoint, &txq);
+
+ if (list_empty(&txq))
+ break;
+
+ spin_unlock_bh(&target->tx_lock);
+
+ bundle_sent = 0;
+ n_pkts_bundle = 0;
+
+ while (true) {
+ /* try to send a bundle on each pass */
+ if ((target->tx_bndl_enable) &&
+ (get_queue_depth(&txq) >=
+ HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
+ int temp1 = 0, temp2 = 0;
+
+ ath6kl_htc_tx_bundle(endpoint, &txq,
+ &temp1, &temp2);
+ bundle_sent += temp1;
+ n_pkts_bundle += temp2;
+ }
+
+ if (list_empty(&txq))
+ break;
+
+ packet = list_first_entry(&txq, struct htc_packet,
+ list);
+ list_del(&packet->list);
+
+ ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags,
+ 0, packet->info.tx.seqno);
+ ath6kl_htc_tx_issue(target, packet);
+ }
+
+ spin_lock_bh(&target->tx_lock);
+
+ endpoint->ep_st.tx_bundles += bundle_sent;
+ endpoint->ep_st.tx_pkt_bundled += n_pkts_bundle;
+ }
+
+ endpoint->tx_proc_cnt = 0;
+ spin_unlock_bh(&target->tx_lock);
+}
+
+static bool ath6kl_htc_tx_try(struct htc_target *target,
+ struct htc_endpoint *endpoint,
+ struct htc_packet *tx_pkt)
+{
+ struct htc_ep_callbacks ep_cb;
+ int txq_depth;
+ bool overflow = false;
+
+ ep_cb = endpoint->ep_cb;
+
+ spin_lock_bh(&target->tx_lock);
+ txq_depth = get_queue_depth(&endpoint->txq);
+ spin_unlock_bh(&target->tx_lock);
+
+ if (txq_depth >= endpoint->max_txq_depth)
+ overflow = true;
+
+ if (overflow)
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "ep %d, tx queue will overflow :%d , tx depth:%d, max:%d\n",
+ endpoint->eid, overflow, txq_depth,
+ endpoint->max_txq_depth);
+
+ if (overflow && ep_cb.tx_full) {
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "indicating overflowed tx packet: 0x%p\n", tx_pkt);
+
+ if (ep_cb.tx_full(endpoint->target, tx_pkt) ==
+ HTC_SEND_FULL_DROP) {
+ endpoint->ep_st.tx_dropped += 1;
+ return false;
+ }
+ }
+
+ spin_lock_bh(&target->tx_lock);
+ list_add_tail(&tx_pkt->list, &endpoint->txq);
+ spin_unlock_bh(&target->tx_lock);
+
+ ath6kl_htc_tx_from_queue(target, endpoint);
+
+ return true;
+}
+
+static void htc_chk_ep_txq(struct htc_target *target)
+{
+ struct htc_endpoint *endpoint;
+ struct htc_endpoint_credit_dist *cred_dist;
+
+ /*
+ * Run through the credit distribution list to see if there are
+ * packets queued. NOTE: no locks need to be taken since the
+ * distribution list is not dynamic (cannot be re-ordered) and we
+ * are not modifying any state.
+ */
+ list_for_each_entry(cred_dist, &target->cred_dist_list, list) {
+ endpoint = (struct htc_endpoint *)cred_dist->htc_rsvd;
+
+ spin_lock_bh(&target->tx_lock);
+ if (!list_empty(&endpoint->txq)) {
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "ep %d has %d credits and %d packets in tx queue\n",
+ cred_dist->endpoint,
+ endpoint->cred_dist.credits,
+ get_queue_depth(&endpoint->txq));
+ spin_unlock_bh(&target->tx_lock);
+ /*
+ * Try to start the stalled queue, this list is
+ * ordered by priority. If there are credits
+ * available the highest priority queue will get a
+ * chance to reclaim credits from lower priority
+ * ones.
+ */
+ ath6kl_htc_tx_from_queue(target, endpoint);
+ spin_lock_bh(&target->tx_lock);
+ }
+ spin_unlock_bh(&target->tx_lock);
+ }
+}
+
+static int htc_setup_tx_complete(struct htc_target *target)
+{
+ struct htc_packet *send_pkt = NULL;
+ int status;
+
+ send_pkt = htc_get_control_buf(target, true);
+
+ if (!send_pkt)
+ return -ENOMEM;
+
+ if (target->htc_tgt_ver >= HTC_VERSION_2P1) {
+ struct htc_setup_comp_ext_msg *setup_comp_ext;
+ u32 flags = 0;
+
+ setup_comp_ext =
+ (struct htc_setup_comp_ext_msg *)send_pkt->buf;
+ memset(setup_comp_ext, 0, sizeof(*setup_comp_ext));
+ setup_comp_ext->msg_id =
+ cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID);
+
+ if (target->msg_per_bndl_max > 0) {
+ /* Indicate HTC bundling to the target */
+ flags |= HTC_SETUP_COMP_FLG_RX_BNDL_EN;
+ setup_comp_ext->msg_per_rxbndl =
+ target->msg_per_bndl_max;
+ }
+
+ memcpy(&setup_comp_ext->flags, &flags,
+ sizeof(setup_comp_ext->flags));
+ set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp_ext,
+ sizeof(struct htc_setup_comp_ext_msg),
+ ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
+
+ } else {
+ struct htc_setup_comp_msg *setup_comp;
+ setup_comp = (struct htc_setup_comp_msg *)send_pkt->buf;
+ memset(setup_comp, 0, sizeof(struct htc_setup_comp_msg));
+ setup_comp->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_ID);
+ set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp,
+ sizeof(struct htc_setup_comp_msg),
+ ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
+ }
+
+ /* we want synchronous operation */
+ send_pkt->completion = NULL;
+ ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0);
+ status = ath6kl_htc_tx_issue(target, send_pkt);
+
+ if (send_pkt != NULL)
+ htc_reclaim_txctrl_buf(target, send_pkt);
+
+ return status;
+}
+
+void ath6kl_htc_set_credit_dist(struct htc_target *target,
+ struct htc_credit_state_info *cred_dist_cntxt,
+ u16 srvc_pri_order[], int list_len)
+{
+ struct htc_endpoint *endpoint;
+ int i, ep;
+
+ target->cred_dist_cntxt = cred_dist_cntxt;
+
+ list_add_tail(&target->endpoint[ENDPOINT_0].cred_dist.list,
+ &target->cred_dist_list);
+
+ for (i = 0; i < list_len; i++) {
+ for (ep = ENDPOINT_1; ep < ENDPOINT_MAX; ep++) {
+ endpoint = &target->endpoint[ep];
+ if (endpoint->svc_id == srvc_pri_order[i]) {
+ list_add_tail(&endpoint->cred_dist.list,
+ &target->cred_dist_list);
+ break;
+ }
+ }
+ if (ep >= ENDPOINT_MAX) {
+ WARN_ON(1);
+ return;
+ }
+ }
+}
+
+int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet)
+{
+ struct htc_endpoint *endpoint;
+ struct list_head queue;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "htc_tx: ep id: %d, buf: 0x%p, len: %d\n",
+ packet->endpoint, packet->buf, packet->act_len);
+
+ if (packet->endpoint >= ENDPOINT_MAX) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ endpoint = &target->endpoint[packet->endpoint];
+
+ if (!ath6kl_htc_tx_try(target, endpoint, packet)) {
+ packet->status = (target->htc_flags & HTC_OP_STATE_STOPPING) ?
+ -ECANCELED : -ENOSPC;
+ INIT_LIST_HEAD(&queue);
+ list_add(&packet->list, &queue);
+ htc_tx_complete(endpoint, &queue);
+ }
+
+ return 0;
+}
+
+/* flush endpoint TX queue */
+void ath6kl_htc_flush_txep(struct htc_target *target,
+ enum htc_endpoint_id eid, u16 tag)
+{
+ struct htc_packet *packet, *tmp_pkt;
+ struct list_head discard_q, container;
+ struct htc_endpoint *endpoint = &target->endpoint[eid];
+
+ if (!endpoint->svc_id) {
+ WARN_ON(1);
+ return;
+ }
+
+ /* initialize the discard queue */
+ INIT_LIST_HEAD(&discard_q);
+
+ spin_lock_bh(&target->tx_lock);
+
+ list_for_each_entry_safe(packet, tmp_pkt, &endpoint->txq, list) {
+ if ((tag == HTC_TX_PACKET_TAG_ALL) ||
+ (tag == packet->info.tx.tag))
+ list_move_tail(&packet->list, &discard_q);
+ }
+
+ spin_unlock_bh(&target->tx_lock);
+
+ list_for_each_entry_safe(packet, tmp_pkt, &discard_q, list) {
+ packet->status = -ECANCELED;
+ list_del(&packet->list);
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "flushing tx pkt:0x%p, len:%d, ep:%d tag:0x%X\n",
+ packet, packet->act_len,
+ packet->endpoint, packet->info.tx.tag);
+
+ INIT_LIST_HEAD(&container);
+ list_add_tail(&packet->list, &container);
+ htc_tx_complete(endpoint, &container);
+ }
+
+}
+
+static void ath6kl_htc_flush_txep_all(struct htc_target *target)
+{
+ struct htc_endpoint *endpoint;
+ int i;
+
+ dump_cred_dist_stats(target);
+
+ for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+ endpoint = &target->endpoint[i];
+ if (endpoint->svc_id == 0)
+ /* not in use.. */
+ continue;
+ ath6kl_htc_flush_txep(target, i, HTC_TX_PACKET_TAG_ALL);
+ }
+}
+
+void ath6kl_htc_indicate_activity_change(struct htc_target *target,
+ enum htc_endpoint_id eid, bool active)
+{
+ struct htc_endpoint *endpoint = &target->endpoint[eid];
+ bool dist = false;
+
+ if (endpoint->svc_id == 0) {
+ WARN_ON(1);
+ return;
+ }
+
+ spin_lock_bh(&target->tx_lock);
+
+ if (active) {
+ if (!(endpoint->cred_dist.dist_flags & HTC_EP_ACTIVE)) {
+ endpoint->cred_dist.dist_flags |= HTC_EP_ACTIVE;
+ dist = true;
+ }
+ } else {
+ if (endpoint->cred_dist.dist_flags & HTC_EP_ACTIVE) {
+ endpoint->cred_dist.dist_flags &= ~HTC_EP_ACTIVE;
+ dist = true;
+ }
+ }
+
+ if (dist) {
+ endpoint->cred_dist.txq_depth =
+ get_queue_depth(&endpoint->txq);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:0x%p dist:0x%p\n",
+ target->cred_dist_cntxt, &target->cred_dist_list);
+
+ ath6k_credit_distribute(target->cred_dist_cntxt,
+ &target->cred_dist_list,
+ HTC_CREDIT_DIST_ACTIVITY_CHANGE);
+ }
+
+ spin_unlock_bh(&target->tx_lock);
+
+ if (dist && !active)
+ htc_chk_ep_txq(target);
+}
+
+/* HTC Rx */
+
+static inline void ath6kl_htc_rx_update_stats(struct htc_endpoint *endpoint,
+ int n_look_ahds)
+{
+ endpoint->ep_st.rx_pkts++;
+ if (n_look_ahds == 1)
+ endpoint->ep_st.rx_lkahds++;
+ else if (n_look_ahds > 1)
+ endpoint->ep_st.rx_bundle_lkahd++;
+}
+
+static inline bool htc_valid_rx_frame_len(struct htc_target *target,
+ enum htc_endpoint_id eid, int len)
+{
+ return (eid == target->dev->ar->ctrl_ep) ?
+ len <= ATH6KL_BUFFER_SIZE : len <= ATH6KL_AMSDU_BUFFER_SIZE;
+}
+
+static int htc_add_rxbuf(struct htc_target *target, struct htc_packet *packet)
+{
+ struct list_head queue;
+
+ INIT_LIST_HEAD(&queue);
+ list_add_tail(&packet->list, &queue);
+ return ath6kl_htc_add_rxbuf_multiple(target, &queue);
+}
+
+static void htc_reclaim_rxbuf(struct htc_target *target,
+ struct htc_packet *packet,
+ struct htc_endpoint *ep)
+{
+ if (packet->info.rx.rx_flags & HTC_RX_PKT_NO_RECYCLE) {
+ htc_rxpkt_reset(packet);
+ packet->status = -ECANCELED;
+ ep->ep_cb.rx(ep->target, packet);
+ } else {
+ htc_rxpkt_reset(packet);
+ htc_add_rxbuf((void *)(target), packet);
+ }
+}
+
+static void reclaim_rx_ctrl_buf(struct htc_target *target,
+ struct htc_packet *packet)
+{
+ spin_lock_bh(&target->htc_lock);
+ list_add_tail(&packet->list, &target->free_ctrl_rxbuf);
+ spin_unlock_bh(&target->htc_lock);
+}
+
+static int ath6kl_htc_rx_packet(struct htc_target *target,
+ struct htc_packet *packet,
+ u32 rx_len)
+{
+ struct ath6kl_device *dev = target->dev;
+ u32 padded_len;
+ int status;
+
+ padded_len = CALC_TXRX_PADDED_LEN(target, rx_len);
+
+ if (padded_len > packet->buf_len) {
+ ath6kl_err("not enough receive space for packet - padlen:%d recvlen:%d bufferlen:%d\n",
+ padded_len, rx_len, packet->buf_len);
+ return -ENOMEM;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "dev_rx_pkt (0x%p : hdr:0x%X) padded len: %d mbox:0x%X (mode:%s)\n",
+ packet, packet->info.rx.exp_hdr,
+ padded_len, dev->ar->mbox_info.htc_addr, "sync");
+
+ status = hif_read_write_sync(dev->ar,
+ dev->ar->mbox_info.htc_addr,
+ packet->buf, padded_len,
+ HIF_RD_SYNC_BLOCK_FIX);
+
+ packet->status = status;
+
+ return status;
+}
+
+/*
+ * optimization for recv packets, we can indicate a
+ * "hint" that there are more single-packets to fetch
+ * on this endpoint.
+ */
+static void ath6kl_htc_rx_set_indicate(u32 lk_ahd,
+ struct htc_endpoint *endpoint,
+ struct htc_packet *packet)
+{
+ struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)&lk_ahd;
+
+ if (htc_hdr->eid == packet->endpoint) {
+ if (!list_empty(&endpoint->rx_bufq))
+ packet->info.rx.indicat_flags |=
+ HTC_RX_FLAGS_INDICATE_MORE_PKTS;
+ }
+}
+
+static void ath6kl_htc_rx_chk_water_mark(struct htc_endpoint *endpoint)
+{
+ struct htc_ep_callbacks ep_cb = endpoint->ep_cb;
+
+ if (ep_cb.rx_refill_thresh > 0) {
+ spin_lock_bh(&endpoint->target->rx_lock);
+ if (get_queue_depth(&endpoint->rx_bufq)
+ < ep_cb.rx_refill_thresh) {
+ spin_unlock_bh(&endpoint->target->rx_lock);
+ ep_cb.rx_refill(endpoint->target, endpoint->eid);
+ return;
+ }
+ spin_unlock_bh(&endpoint->target->rx_lock);
+ }
+}
+
+/* This function is called with rx_lock held */
+static int ath6kl_htc_rx_setup(struct htc_target *target,
+ struct htc_endpoint *ep,
+ u32 *lk_ahds, struct list_head *queue, int n_msg)
+{
+ struct htc_packet *packet;
+ /* FIXME: type of lk_ahds can't be right */
+ struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)lk_ahds;
+ struct htc_ep_callbacks ep_cb;
+ int status = 0, j, full_len;
+ bool no_recycle;
+
+ full_len = CALC_TXRX_PADDED_LEN(target,
+ le16_to_cpu(htc_hdr->payld_len) +
+ sizeof(*htc_hdr));
+
+ if (!htc_valid_rx_frame_len(target, ep->eid, full_len)) {
+ ath6kl_warn("Rx buffer requested with invalid length\n");
+ return -EINVAL;
+ }
+
+ ep_cb = ep->ep_cb;
+ for (j = 0; j < n_msg; j++) {
+
+ /*
+ * Reset flag, any packets allocated using the
+ * rx_alloc() API cannot be recycled on
+ * cleanup,they must be explicitly returned.
+ */
+ no_recycle = false;
+
+ if (ep_cb.rx_allocthresh &&
+ (full_len > ep_cb.rx_alloc_thresh)) {
+ ep->ep_st.rx_alloc_thresh_hit += 1;
+ ep->ep_st.rxalloc_thresh_byte +=
+ le16_to_cpu(htc_hdr->payld_len);
+
+ spin_unlock_bh(&target->rx_lock);
+ no_recycle = true;
+
+ packet = ep_cb.rx_allocthresh(ep->target, ep->eid,
+ full_len);
+ spin_lock_bh(&target->rx_lock);
+ } else {
+ /* refill handler is being used */
+ if (list_empty(&ep->rx_bufq)) {
+ if (ep_cb.rx_refill) {
+ spin_unlock_bh(&target->rx_lock);
+ ep_cb.rx_refill(ep->target, ep->eid);
+ spin_lock_bh(&target->rx_lock);
+ }
+ }
+
+ if (list_empty(&ep->rx_bufq))
+ packet = NULL;
+ else {
+ packet = list_first_entry(&ep->rx_bufq,
+ struct htc_packet, list);
+ list_del(&packet->list);
+ }
+ }
+
+ if (!packet) {
+ target->rx_st_flags |= HTC_RECV_WAIT_BUFFERS;
+ target->ep_waiting = ep->eid;
+ return -ENOSPC;
+ }
+
+ /* clear flags */
+ packet->info.rx.rx_flags = 0;
+ packet->info.rx.indicat_flags = 0;
+ packet->status = 0;
+
+ if (no_recycle)
+ /*
+ * flag that these packets cannot be
+ * recycled, they have to be returned to
+ * the user
+ */
+ packet->info.rx.rx_flags |= HTC_RX_PKT_NO_RECYCLE;
+
+ /* Caller needs to free this upon any failure */
+ list_add_tail(&packet->list, queue);
+
+ if (target->htc_flags & HTC_OP_STATE_STOPPING) {
+ status = -ECANCELED;
+ break;
+ }
+
+ if (j) {
+ packet->info.rx.rx_flags |= HTC_RX_PKT_REFRESH_HDR;
+ packet->info.rx.exp_hdr = 0xFFFFFFFF;
+ } else
+ /* set expected look ahead */
+ packet->info.rx.exp_hdr = *lk_ahds;
+
+ packet->act_len = le16_to_cpu(htc_hdr->payld_len) +
+ HTC_HDR_LENGTH;
+ }
+
+ return status;
+}
+
+static int ath6kl_htc_rx_alloc(struct htc_target *target,
+ u32 lk_ahds[], int msg,
+ struct htc_endpoint *endpoint,
+ struct list_head *queue)
+{
+ int status = 0;
+ struct htc_packet *packet, *tmp_pkt;
+ struct htc_frame_hdr *htc_hdr;
+ int i, n_msg;
+
+ spin_lock_bh(&target->rx_lock);
+
+ for (i = 0; i < msg; i++) {
+
+ htc_hdr = (struct htc_frame_hdr *)&lk_ahds[i];
+
+ if (htc_hdr->eid >= ENDPOINT_MAX) {
+ ath6kl_err("invalid ep in look-ahead: %d\n",
+ htc_hdr->eid);
+ status = -ENOMEM;
+ break;
+ }
+
+ if (htc_hdr->eid != endpoint->eid) {
+ ath6kl_err("invalid ep in look-ahead: %d should be : %d (index:%d)\n",
+ htc_hdr->eid, endpoint->eid, i);
+ status = -ENOMEM;
+ break;
+ }
+
+ if (le16_to_cpu(htc_hdr->payld_len) > HTC_MAX_PAYLOAD_LENGTH) {
+ ath6kl_err("payload len %d exceeds max htc : %d !\n",
+ htc_hdr->payld_len,
+ (u32) HTC_MAX_PAYLOAD_LENGTH);
+ status = -ENOMEM;
+ break;
+ }
+
+ if (endpoint->svc_id == 0) {
+ ath6kl_err("ep %d is not connected !\n", htc_hdr->eid);
+ status = -ENOMEM;
+ break;
+ }
+
+ if (htc_hdr->flags & HTC_FLG_RX_BNDL_CNT) {
+ /*
+ * HTC header indicates that every packet to follow
+ * has the same padded length so that it can be
+ * optimally fetched as a full bundle.
+ */
+ n_msg = (htc_hdr->flags & HTC_FLG_RX_BNDL_CNT) >>
+ HTC_FLG_RX_BNDL_CNT_S;
+
+ /* the count doesn't include the starter frame */
+ n_msg++;
+ if (n_msg > target->msg_per_bndl_max) {
+ status = -ENOMEM;
+ break;
+ }
+
+ endpoint->ep_st.rx_bundle_from_hdr += 1;
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "htc hdr indicates :%d msg can be fetched as a bundle\n",
+ n_msg);
+ } else
+ /* HTC header only indicates 1 message to fetch */
+ n_msg = 1;
+
+ /* Setup packet buffers for each message */
+ status = ath6kl_htc_rx_setup(target, endpoint, &lk_ahds[i],
+ queue, n_msg);
+
+ /*
+ * This is due to unavailabilty of buffers to rx entire data.
+ * Return no error so that free buffers from queue can be used
+ * to receive partial data.
+ */
+ if (status == -ENOSPC) {
+ spin_unlock_bh(&target->rx_lock);
+ return 0;
+ }
+
+ if (status)
+ break;
+ }
+
+ spin_unlock_bh(&target->rx_lock);
+
+ if (status) {
+ list_for_each_entry_safe(packet, tmp_pkt, queue, list) {
+ list_del(&packet->list);
+ htc_reclaim_rxbuf(target, packet,
+ &target->endpoint[packet->endpoint]);
+ }
+ }
+
+ return status;
+}
+
+static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets)
+{
+ if (packets->endpoint != ENDPOINT_0) {
+ WARN_ON(1);
+ return;
+ }
+
+ if (packets->status == -ECANCELED) {
+ reclaim_rx_ctrl_buf(context, packets);
+ return;
+ }
+
+ if (packets->act_len > 0) {
+ ath6kl_err("htc_ctrl_rx, got message with len:%zu\n",
+ packets->act_len + HTC_HDR_LENGTH);
+
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES,
+ "Unexpected ENDPOINT 0 Message", "",
+ packets->buf - HTC_HDR_LENGTH,
+ packets->act_len + HTC_HDR_LENGTH);
+ }
+
+ htc_reclaim_rxbuf(context, packets, &context->endpoint[0]);
+}
+
+static void htc_proc_cred_rpt(struct htc_target *target,
+ struct htc_credit_report *rpt,
+ int n_entries,
+ enum htc_endpoint_id from_ep)
+{
+ struct htc_endpoint *endpoint;
+ int tot_credits = 0, i;
+ bool dist = false;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "htc_proc_cred_rpt, credit report entries:%d\n", n_entries);
+
+ spin_lock_bh(&target->tx_lock);
+
+ for (i = 0; i < n_entries; i++, rpt++) {
+ if (rpt->eid >= ENDPOINT_MAX) {
+ WARN_ON(1);
+ spin_unlock_bh(&target->tx_lock);
+ return;
+ }
+
+ endpoint = &target->endpoint[rpt->eid];
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, " ep %d got %d credits\n",
+ rpt->eid, rpt->credits);
+
+ endpoint->ep_st.tx_cred_rpt += 1;
+ endpoint->ep_st.cred_retnd += rpt->credits;
+
+ if (from_ep == rpt->eid) {
+ /*
+ * This credit report arrived on the same endpoint
+ * indicating it arrived in an RX packet.
+ */
+ endpoint->ep_st.cred_from_rx += rpt->credits;
+ endpoint->ep_st.cred_rpt_from_rx += 1;
+ } else if (from_ep == ENDPOINT_0) {
+ /* credit arrived on endpoint 0 as a NULL message */
+ endpoint->ep_st.cred_from_ep0 += rpt->credits;
+ endpoint->ep_st.cred_rpt_ep0 += 1;
+ } else {
+ endpoint->ep_st.cred_from_other += rpt->credits;
+ endpoint->ep_st.cred_rpt_from_other += 1;
+ }
+
+ if (rpt->eid == ENDPOINT_0)
+ /* always give endpoint 0 credits back */
+ endpoint->cred_dist.credits += rpt->credits;
+ else {
+ endpoint->cred_dist.cred_to_dist += rpt->credits;
+ dist = true;
+ }
+
+ /*
+ * Refresh tx depth for distribution function that will
+ * recover these credits NOTE: this is only valid when
+ * there are credits to recover!
+ */
+ endpoint->cred_dist.txq_depth =
+ get_queue_depth(&endpoint->txq);
+
+ tot_credits += rpt->credits;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+ "report indicated %d credits to distribute\n",
+ tot_credits);
+
+ if (dist) {
+ /*
+ * This was a credit return based on a completed send
+ * operations note, this is done with the lock held
+ */
+ ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:0x%p dist:0x%p\n",
+ target->cred_dist_cntxt, &target->cred_dist_list);
+
+ ath6k_credit_distribute(target->cred_dist_cntxt,
+ &target->cred_dist_list,
+ HTC_CREDIT_DIST_SEND_COMPLETE);
+ }
+
+ spin_unlock_bh(&target->tx_lock);
+
+ if (tot_credits)
+ htc_chk_ep_txq(target);
+}
+
+static int htc_parse_trailer(struct htc_target *target,
+ struct htc_record_hdr *record,
+ u8 *record_buf, u32 *next_lk_ahds,
+ enum htc_endpoint_id endpoint,
+ int *n_lk_ahds)
+{
+ struct htc_bundle_lkahd_rpt *bundle_lkahd_rpt;
+ struct htc_lookahead_report *lk_ahd;
+ int len;
+
+ switch (record->rec_id) {
+ case HTC_RECORD_CREDITS:
+ len = record->len / sizeof(struct htc_credit_report);
+ if (!len) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ htc_proc_cred_rpt(target,
+ (struct htc_credit_report *) record_buf,
+ len, endpoint);
+ break;
+ case HTC_RECORD_LOOKAHEAD:
+ len = record->len / sizeof(*lk_ahd);
+ if (!len) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ lk_ahd = (struct htc_lookahead_report *) record_buf;
+ if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF))
+ && next_lk_ahds) {
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "lk_ahd report found (pre valid:0x%X, post valid:0x%X)\n",
+ lk_ahd->pre_valid, lk_ahd->post_valid);
+
+ /* look ahead bytes are valid, copy them over */
+ memcpy((u8 *)&next_lk_ahds[0], lk_ahd->lk_ahd, 4);
+
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Next Look Ahead",
+ "", next_lk_ahds, 4);
+
+ *n_lk_ahds = 1;
+ }
+ break;
+ case HTC_RECORD_LOOKAHEAD_BUNDLE:
+ len = record->len / sizeof(*bundle_lkahd_rpt);
+ if (!len || (len > HTC_HOST_MAX_MSG_PER_BUNDLE)) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (next_lk_ahds) {
+ int i;
+
+ bundle_lkahd_rpt =
+ (struct htc_bundle_lkahd_rpt *) record_buf;
+
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Bundle lk_ahd",
+ "", record_buf, record->len);
+
+ for (i = 0; i < len; i++) {
+ memcpy((u8 *)&next_lk_ahds[i],
+ bundle_lkahd_rpt->lk_ahd, 4);
+ bundle_lkahd_rpt++;
+ }
+
+ *n_lk_ahds = i;
+ }
+ break;
+ default:
+ ath6kl_err("unhandled record: id:%d len:%d\n",
+ record->rec_id, record->len);
+ break;
+ }
+
+ return 0;
+
+}
+
+static int htc_proc_trailer(struct htc_target *target,
+ u8 *buf, int len, u32 *next_lk_ahds,
+ int *n_lk_ahds, enum htc_endpoint_id endpoint)
+{
+ struct htc_record_hdr *record;
+ int orig_len;
+ int status;
+ u8 *record_buf;
+ u8 *orig_buf;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "+htc_proc_trailer (len:%d)\n", len);
+
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Recv Trailer", "",
+ buf, len);
+
+ orig_buf = buf;
+ orig_len = len;
+ status = 0;
+
+ while (len > 0) {
+
+ if (len < sizeof(struct htc_record_hdr)) {
+ status = -ENOMEM;
+ break;
+ }
+ /* these are byte aligned structs */
+ record = (struct htc_record_hdr *) buf;
+ len -= sizeof(struct htc_record_hdr);
+ buf += sizeof(struct htc_record_hdr);
+
+ if (record->len > len) {
+ ath6kl_err("invalid record len: %d (id:%d) buf has: %d bytes left\n",
+ record->len, record->rec_id, len);
+ status = -ENOMEM;
+ break;
+ }
+ record_buf = buf;
+
+ status = htc_parse_trailer(target, record, record_buf,
+ next_lk_ahds, endpoint, n_lk_ahds);
+
+ if (status)
+ break;
+
+ /* advance buffer past this record for next time around */
+ buf += record->len;
+ len -= record->len;
+ }
+
+ if (status)
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD Recv Trailer",
+ "", orig_buf, orig_len);
+
+ return status;
+}
+
+static int ath6kl_htc_rx_process_hdr(struct htc_target *target,
+ struct htc_packet *packet,
+ u32 *next_lkahds, int *n_lkahds)
+{
+ int status = 0;
+ u16 payload_len;
+ u32 lk_ahd;
+ struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)packet->buf;
+
+ if (n_lkahds != NULL)
+ *n_lkahds = 0;
+
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "HTC Recv PKT", "htc ",
+ packet->buf, packet->act_len);
+
+ /*
+ * NOTE: we cannot assume the alignment of buf, so we use the safe
+ * macros to retrieve 16 bit fields.
+ */
+ payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len));
+
+ memcpy((u8 *)&lk_ahd, packet->buf, sizeof(lk_ahd));
+
+ if (packet->info.rx.rx_flags & HTC_RX_PKT_REFRESH_HDR) {
+ /*
+ * Refresh the expected header and the actual length as it
+ * was unknown when this packet was grabbed as part of the
+ * bundle.
+ */
+ packet->info.rx.exp_hdr = lk_ahd;
+ packet->act_len = payload_len + HTC_HDR_LENGTH;
+
+ /* validate the actual header that was refreshed */
+ if (packet->act_len > packet->buf_len) {
+ ath6kl_err("refreshed hdr payload len (%d) in bundled recv is invalid (hdr: 0x%X)\n",
+ payload_len, lk_ahd);
+ /*
+ * Limit this to max buffer just to print out some
+ * of the buffer.
+ */
+ packet->act_len = min(packet->act_len, packet->buf_len);
+ status = -ENOMEM;
+ goto fail_rx;
+ }
+
+ if (packet->endpoint != htc_hdr->eid) {
+ ath6kl_err("refreshed hdr ep (%d) does not match expected ep (%d)\n",
+ htc_hdr->eid, packet->endpoint);
+ status = -ENOMEM;
+ goto fail_rx;
+ }
+ }
+
+ if (lk_ahd != packet->info.rx.exp_hdr) {
+ ath6kl_err("%s(): lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n",
+ __func__, packet, packet->info.rx.rx_flags);
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Expected Message lk_ahd",
+ "", &packet->info.rx.exp_hdr, 4);
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Current Frame Header",
+ "", (u8 *)&lk_ahd, sizeof(lk_ahd));
+ status = -ENOMEM;
+ goto fail_rx;
+ }
+
+ if (htc_hdr->flags & HTC_FLG_RX_TRAILER) {
+ if (htc_hdr->ctrl[0] < sizeof(struct htc_record_hdr) ||
+ htc_hdr->ctrl[0] > payload_len) {
+ ath6kl_err("%s(): invalid hdr (payload len should be :%d, CB[0] is:%d)\n",
+ __func__, payload_len, htc_hdr->ctrl[0]);
+ status = -ENOMEM;
+ goto fail_rx;
+ }
+
+ if (packet->info.rx.rx_flags & HTC_RX_PKT_IGNORE_LOOKAHEAD) {
+ next_lkahds = NULL;
+ n_lkahds = NULL;
+ }
+
+ status = htc_proc_trailer(target, packet->buf + HTC_HDR_LENGTH
+ + payload_len - htc_hdr->ctrl[0],
+ htc_hdr->ctrl[0], next_lkahds,
+ n_lkahds, packet->endpoint);
+
+ if (status)
+ goto fail_rx;
+
+ packet->act_len -= htc_hdr->ctrl[0];
+ }
+
+ packet->buf += HTC_HDR_LENGTH;
+ packet->act_len -= HTC_HDR_LENGTH;
+
+fail_rx:
+ if (status)
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD HTC Recv PKT",
+ "", packet->buf,
+ packet->act_len < 256 ? packet->act_len : 256);
+ else {
+ if (packet->act_len > 0)
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES,
+ "HTC - Application Msg", "",
+ packet->buf, packet->act_len);
+ }
+
+ return status;
+}
+
+static void ath6kl_htc_rx_complete(struct htc_endpoint *endpoint,
+ struct htc_packet *packet)
+{
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "htc calling ep %d recv callback on packet 0x%p\n",
+ endpoint->eid, packet);
+ endpoint->ep_cb.rx(endpoint->target, packet);
+}
+
+static int ath6kl_htc_rx_bundle(struct htc_target *target,
+ struct list_head *rxq,
+ struct list_head *sync_compq,
+ int *n_pkt_fetched, bool part_bundle)
+{
+ struct hif_scatter_req *scat_req;
+ struct htc_packet *packet;
+ int rem_space = target->max_rx_bndl_sz;
+ int n_scat_pkt, status = 0, i, len;
+
+ n_scat_pkt = get_queue_depth(rxq);
+ n_scat_pkt = min(n_scat_pkt, target->msg_per_bndl_max);
+
+ if ((get_queue_depth(rxq) - n_scat_pkt) > 0) {
+ /*
+ * We were forced to split this bundle receive operation
+ * all packets in this partial bundle must have their
+ * lookaheads ignored.
+ */
+ part_bundle = true;
+
+ /*
+ * This would only happen if the target ignored our max
+ * bundle limit.
+ */
+ ath6kl_warn("%s(): partial bundle detected num:%d , %d\n",
+ __func__, get_queue_depth(rxq), n_scat_pkt);
+ }
+
+ len = 0;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "%s(): (numpackets: %d , actual : %d)\n",
+ __func__, get_queue_depth(rxq), n_scat_pkt);
+
+ scat_req = hif_scatter_req_get(target->dev->ar);
+
+ if (scat_req == NULL)
+ goto fail_rx_pkt;
+
+ for (i = 0; i < n_scat_pkt; i++) {
+ int pad_len;
+
+ packet = list_first_entry(rxq, struct htc_packet, list);
+ list_del(&packet->list);
+
+ pad_len = CALC_TXRX_PADDED_LEN(target,
+ packet->act_len);
+
+ if ((rem_space - pad_len) < 0) {
+ list_add(&packet->list, rxq);
+ break;
+ }
+
+ rem_space -= pad_len;
+
+ if (part_bundle || (i < (n_scat_pkt - 1)))
+ /*
+ * Packet 0..n-1 cannot be checked for look-aheads
+ * since we are fetching a bundle the last packet
+ * however can have it's lookahead used
+ */
+ packet->info.rx.rx_flags |=
+ HTC_RX_PKT_IGNORE_LOOKAHEAD;
+
+ /* NOTE: 1 HTC packet per scatter entry */
+ scat_req->scat_list[i].buf = packet->buf;
+ scat_req->scat_list[i].len = pad_len;
+
+ packet->info.rx.rx_flags |= HTC_RX_PKT_PART_OF_BUNDLE;
+
+ list_add_tail(&packet->list, sync_compq);
+
+ WARN_ON(!scat_req->scat_list[i].len);
+ len += scat_req->scat_list[i].len;
+ }
+
+ scat_req->len = len;
+ scat_req->scat_entries = i;
+
+ status = ath6kldev_submit_scat_req(target->dev, scat_req, true);
+
+ if (!status)
+ *n_pkt_fetched = i;
+
+ /* free scatter request */
+ hif_scatter_req_add(target->dev->ar, scat_req);
+
+fail_rx_pkt:
+
+ return status;
+}
+
+static int ath6kl_htc_rx_process_packets(struct htc_target *target,
+ struct list_head *comp_pktq,
+ u32 lk_ahds[],
+ int *n_lk_ahd)
+{
+ struct htc_packet *packet, *tmp_pkt;
+ struct htc_endpoint *ep;
+ int status = 0;
+
+ list_for_each_entry_safe(packet, tmp_pkt, comp_pktq, list) {
+ list_del(&packet->list);
+ ep = &target->endpoint[packet->endpoint];
+
+ /* process header for each of the recv packet */
+ status = ath6kl_htc_rx_process_hdr(target, packet, lk_ahds,
+ n_lk_ahd);
+ if (status)
+ return status;
+
+ if (list_empty(comp_pktq)) {
+ /*
+ * Last packet's more packet flag is set
+ * based on the lookahead.
+ */
+ if (*n_lk_ahd > 0)
+ ath6kl_htc_rx_set_indicate(lk_ahds[0],
+ ep, packet);
+ } else
+ /*
+ * Packets in a bundle automatically have
+ * this flag set.
+ */
+ packet->info.rx.indicat_flags |=
+ HTC_RX_FLAGS_INDICATE_MORE_PKTS;
+
+ ath6kl_htc_rx_update_stats(ep, *n_lk_ahd);
+
+ if (packet->info.rx.rx_flags & HTC_RX_PKT_PART_OF_BUNDLE)
+ ep->ep_st.rx_bundl += 1;
+
+ ath6kl_htc_rx_complete(ep, packet);
+ }
+
+ return status;
+}
+
+static int ath6kl_htc_rx_fetch(struct htc_target *target,
+ struct list_head *rx_pktq,
+ struct list_head *comp_pktq)
+{
+ int fetched_pkts;
+ bool part_bundle = false;
+ int status = 0;
+
+ /* now go fetch the list of HTC packets */
+ while (!list_empty(rx_pktq)) {
+ fetched_pkts = 0;
+
+ if (target->rx_bndl_enable && (get_queue_depth(rx_pktq) > 1)) {
+ /*
+ * There are enough packets to attempt a
+ * bundle transfer and recv bundling is
+ * allowed.
+ */
+ status = ath6kl_htc_rx_bundle(target, rx_pktq,
+ comp_pktq,
+ &fetched_pkts,
+ part_bundle);
+ if (status)
+ return status;
+
+ if (!list_empty(rx_pktq))
+ part_bundle = true;
+ }
+
+ if (!fetched_pkts) {
+ struct htc_packet *packet;
+
+ packet = list_first_entry(rx_pktq, struct htc_packet,
+ list);
+
+ list_del(&packet->list);
+
+ /* fully synchronous */
+ packet->completion = NULL;
+
+ if (!list_empty(rx_pktq))
+ /*
+ * look_aheads in all packet
+ * except the last one in the
+ * bundle must be ignored
+ */
+ packet->info.rx.rx_flags |=
+ HTC_RX_PKT_IGNORE_LOOKAHEAD;
+
+ /* go fetch the packet */
+ status = ath6kl_htc_rx_packet(target, packet,
+ packet->act_len);
+ if (status)
+ return status;
+
+ list_add_tail(&packet->list, comp_pktq);
+ }
+ }
+
+ return status;
+}
+
+int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
+ u32 msg_look_ahead[], int *num_pkts)
+{
+ struct htc_packet *packets, *tmp_pkt;
+ struct htc_endpoint *endpoint;
+ struct list_head rx_pktq, comp_pktq;
+ int status = 0;
+ u32 look_aheads[HTC_HOST_MAX_MSG_PER_BUNDLE];
+ int num_look_ahead = 1;
+ enum htc_endpoint_id id;
+ int n_fetched = 0;
+
+ *num_pkts = 0;
+
+ /*
+ * On first entry copy the look_aheads into our temp array for
+ * processing
+ */
+ memcpy(look_aheads, msg_look_ahead, sizeof(look_aheads));
+
+ while (true) {
+
+ /*
+ * First lookahead sets the expected endpoint IDs for all
+ * packets in a bundle.
+ */
+ id = ((struct htc_frame_hdr *)&look_aheads[0])->eid;
+ endpoint = &target->endpoint[id];
+
+ if (id >= ENDPOINT_MAX) {
+ ath6kl_err("MsgPend, invalid endpoint in look-ahead: %d\n",
+ id);
+ status = -ENOMEM;
+ break;
+ }
+
+ INIT_LIST_HEAD(&rx_pktq);
+ INIT_LIST_HEAD(&comp_pktq);
+
+ /*
+ * Try to allocate as many HTC RX packets indicated by the
+ * look_aheads.
+ */
+ status = ath6kl_htc_rx_alloc(target, look_aheads,
+ num_look_ahead, endpoint,
+ &rx_pktq);
+ if (status)
+ break;
+
+ if (get_queue_depth(&rx_pktq) >= 2)
+ /*
+ * A recv bundle was detected, force IRQ status
+ * re-check again
+ */
+ target->chk_irq_status_cnt = 1;
+
+ n_fetched += get_queue_depth(&rx_pktq);
+
+ num_look_ahead = 0;
+
+ status = ath6kl_htc_rx_fetch(target, &rx_pktq, &comp_pktq);
+
+ if (!status)
+ ath6kl_htc_rx_chk_water_mark(endpoint);
+
+ /* Process fetched packets */
+ status = ath6kl_htc_rx_process_packets(target, &comp_pktq,
+ look_aheads,
+ &num_look_ahead);
+
+ if (!num_look_ahead || status)
+ break;
+
+ /*
+ * For SYNCH processing, if we get here, we are running
+ * through the loop again due to a detected lookahead. Set
+ * flag that we should re-check IRQ status registers again
+ * before leaving IRQ processing, this can net better
+ * performance in high throughput situations.
+ */
+ target->chk_irq_status_cnt = 1;
+ }
+
+ if (status) {
+ ath6kl_err("failed to get pending recv messages: %d\n",
+ status);
+ /*
+ * Cleanup any packets we allocated but didn't use to
+ * actually fetch any packets.
+ */
+ list_for_each_entry_safe(packets, tmp_pkt, &rx_pktq, list) {
+ list_del(&packets->list);
+ htc_reclaim_rxbuf(target, packets,
+ &target->endpoint[packets->endpoint]);
+ }
+
+ /* cleanup any packets in sync completion queue */
+ list_for_each_entry_safe(packets, tmp_pkt, &comp_pktq, list) {
+ list_del(&packets->list);
+ htc_reclaim_rxbuf(target, packets,
+ &target->endpoint[packets->endpoint]);
+ }
+
+ if (target->htc_flags & HTC_OP_STATE_STOPPING) {
+ ath6kl_warn("host is going to stop blocking receiver for htc_stop\n");
+ ath6kldev_rx_control(target->dev, false);
+ }
+ }
+
+ /*
+ * Before leaving, check to see if host ran out of buffers and
+ * needs to stop the receiver.
+ */
+ if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) {
+ ath6kl_warn("host has no rx buffers blocking receiver to prevent overrun\n");
+ ath6kldev_rx_control(target->dev, false);
+ }
+ *num_pkts = n_fetched;
+
+ return status;
+}
+
+/*
+ * Synchronously wait for a control message from the target,
+ * This function is used at initialization time ONLY. At init messages
+ * on ENDPOINT 0 are expected.
+ */
+static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target)
+{
+ struct htc_packet *packet = NULL;
+ struct htc_frame_hdr *htc_hdr;
+ u32 look_ahead;
+
+ if (ath6kldev_poll_mboxmsg_rx(target->dev, &look_ahead,
+ HTC_TARGET_RESPONSE_TIMEOUT))
+ return NULL;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "htc_wait_for_ctrl_msg: look_ahead : 0x%X\n", look_ahead);
+
+ htc_hdr = (struct htc_frame_hdr *)&look_ahead;
+
+ if (htc_hdr->eid != ENDPOINT_0)
+ return NULL;
+
+ packet = htc_get_control_buf(target, false);
+
+ if (!packet)
+ return NULL;
+
+ packet->info.rx.rx_flags = 0;
+ packet->info.rx.exp_hdr = look_ahead;
+ packet->act_len = le16_to_cpu(htc_hdr->payld_len) + HTC_HDR_LENGTH;
+
+ if (packet->act_len > packet->buf_len)
+ goto fail_ctrl_rx;
+
+ /* we want synchronous operation */
+ packet->completion = NULL;
+
+ /* get the message from the device, this will block */
+ if (ath6kl_htc_rx_packet(target, packet, packet->act_len))
+ goto fail_ctrl_rx;
+
+ /* process receive header */
+ packet->status = ath6kl_htc_rx_process_hdr(target, packet, NULL, NULL);
+
+ if (packet->status) {
+ ath6kl_err("htc_wait_for_ctrl_msg, ath6kl_htc_rx_process_hdr failed (status = %d)\n",
+ packet->status);
+ goto fail_ctrl_rx;
+ }
+
+ return packet;
+
+fail_ctrl_rx:
+ if (packet != NULL) {
+ htc_rxpkt_reset(packet);
+ reclaim_rx_ctrl_buf(target, packet);
+ }
+
+ return NULL;
+}
+
+int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
+ struct list_head *pkt_queue)
+{
+ struct htc_endpoint *endpoint;
+ struct htc_packet *first_pkt;
+ bool rx_unblock = false;
+ int status = 0, depth;
+
+ if (list_empty(pkt_queue))
+ return -ENOMEM;
+
+ first_pkt = list_first_entry(pkt_queue, struct htc_packet, list);
+
+ if (first_pkt->endpoint >= ENDPOINT_MAX)
+ return status;
+
+ depth = get_queue_depth(pkt_queue);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "htc_add_rxbuf_multiple: ep id: %d, cnt:%d, len: %d\n",
+ first_pkt->endpoint, depth, first_pkt->buf_len);
+
+ endpoint = &target->endpoint[first_pkt->endpoint];
+
+ if (target->htc_flags & HTC_OP_STATE_STOPPING) {
+ struct htc_packet *packet, *tmp_pkt;
+
+ /* walk through queue and mark each one canceled */
+ list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) {
+ packet->status = -ECANCELED;
+ list_del(&packet->list);
+ ath6kl_htc_rx_complete(endpoint, packet);
+ }
+
+ return status;
+ }
+
+ spin_lock_bh(&target->rx_lock);
+
+ list_splice_tail_init(pkt_queue, &endpoint->rx_bufq);
+
+ /* check if we are blocked waiting for a new buffer */
+ if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) {
+ if (target->ep_waiting == first_pkt->endpoint) {
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "receiver was blocked on ep:%d, unblocking.\n",
+ target->ep_waiting);
+ target->rx_st_flags &= ~HTC_RECV_WAIT_BUFFERS;
+ target->ep_waiting = ENDPOINT_MAX;
+ rx_unblock = true;
+ }
+ }
+
+ spin_unlock_bh(&target->rx_lock);
+
+ if (rx_unblock && !(target->htc_flags & HTC_OP_STATE_STOPPING))
+ /* TODO : implement a buffer threshold count? */
+ ath6kldev_rx_control(target->dev, true);
+
+ return status;
+}
+
+void ath6kl_htc_flush_rx_buf(struct htc_target *target)
+{
+ struct htc_endpoint *endpoint;
+ struct htc_packet *packet, *tmp_pkt;
+ int i;
+
+ for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+ endpoint = &target->endpoint[i];
+ if (!endpoint->svc_id)
+ /* not in use.. */
+ continue;
+
+ spin_lock_bh(&target->rx_lock);
+ list_for_each_entry_safe(packet, tmp_pkt,
+ &endpoint->rx_bufq, list) {
+ list_del(&packet->list);
+ spin_unlock_bh(&target->rx_lock);
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "flushing rx pkt:0x%p, len:%d, ep:%d\n",
+ packet, packet->buf_len,
+ packet->endpoint);
+ dev_kfree_skb(packet->pkt_cntxt);
+ spin_lock_bh(&target->rx_lock);
+ }
+ spin_unlock_bh(&target->rx_lock);
+ }
+}
+
+int ath6kl_htc_conn_service(struct htc_target *target,
+ struct htc_service_connect_req *conn_req,
+ struct htc_service_connect_resp *conn_resp)
+{
+ struct htc_packet *rx_pkt = NULL;
+ struct htc_packet *tx_pkt = NULL;
+ struct htc_conn_service_resp *resp_msg;
+ struct htc_conn_service_msg *conn_msg;
+ struct htc_endpoint *endpoint;
+ enum htc_endpoint_id assigned_ep = ENDPOINT_MAX;
+ unsigned int max_msg_sz = 0;
+ int status = 0;
+
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "htc_conn_service, target:0x%p service id:0x%X\n",
+ target, conn_req->svc_id);
+
+ if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) {
+ /* special case for pseudo control service */
+ assigned_ep = ENDPOINT_0;
+ max_msg_sz = HTC_MAX_CTRL_MSG_LEN;
+ } else {
+ /* allocate a packet to send to the target */
+ tx_pkt = htc_get_control_buf(target, true);
+
+ if (!tx_pkt)
+ return -ENOMEM;
+
+ conn_msg = (struct htc_conn_service_msg *)tx_pkt->buf;
+ memset(conn_msg, 0, sizeof(*conn_msg));
+ conn_msg->msg_id = cpu_to_le16(HTC_MSG_CONN_SVC_ID);
+ conn_msg->svc_id = cpu_to_le16(conn_req->svc_id);
+ conn_msg->conn_flags = cpu_to_le16(conn_req->conn_flags);
+
+ set_htc_pkt_info(tx_pkt, NULL, (u8 *) conn_msg,
+ sizeof(*conn_msg) + conn_msg->svc_meta_len,
+ ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
+
+ /* we want synchronous operation */
+ tx_pkt->completion = NULL;
+ ath6kl_htc_tx_prep_pkt(tx_pkt, 0, 0, 0);
+ status = ath6kl_htc_tx_issue(target, tx_pkt);
+
+ if (status)
+ goto fail_tx;
+
+ /* wait for response */
+ rx_pkt = htc_wait_for_ctrl_msg(target);
+
+ if (!rx_pkt) {
+ status = -ENOMEM;
+ goto fail_tx;
+ }
+
+ resp_msg = (struct htc_conn_service_resp *)rx_pkt->buf;
+
+ if ((le16_to_cpu(resp_msg->msg_id) != HTC_MSG_CONN_SVC_RESP_ID)
+ || (rx_pkt->act_len < sizeof(*resp_msg))) {
+ status = -ENOMEM;
+ goto fail_tx;
+ }
+
+ conn_resp->resp_code = resp_msg->status;
+ /* check response status */
+ if (resp_msg->status != HTC_SERVICE_SUCCESS) {
+ ath6kl_err("target failed service 0x%X connect request (status:%d)\n",
+ resp_msg->svc_id, resp_msg->status);
+ status = -ENOMEM;
+ goto fail_tx;
+ }
+
+ assigned_ep = (enum htc_endpoint_id)resp_msg->eid;
+ max_msg_sz = le16_to_cpu(resp_msg->max_msg_sz);
+ }
+
+ if (assigned_ep >= ENDPOINT_MAX || !max_msg_sz) {
+ status = -ENOMEM;
+ goto fail_tx;
+ }
+
+ endpoint = &target->endpoint[assigned_ep];
+ endpoint->eid = assigned_ep;
+ if (endpoint->svc_id) {
+ status = -ENOMEM;
+ goto fail_tx;
+ }
+
+ /* return assigned endpoint to caller */
+ conn_resp->endpoint = assigned_ep;
+ conn_resp->len_max = max_msg_sz;
+
+ /* setup the endpoint */
+
+ /* this marks the endpoint in use */
+ endpoint->svc_id = conn_req->svc_id;
+
+ endpoint->max_txq_depth = conn_req->max_txq_depth;
+ endpoint->len_max = max_msg_sz;
+ endpoint->ep_cb = conn_req->ep_cb;
+ endpoint->cred_dist.svc_id = conn_req->svc_id;
+ endpoint->cred_dist.htc_rsvd = endpoint;
+ endpoint->cred_dist.endpoint = assigned_ep;
+ endpoint->cred_dist.cred_sz = target->tgt_cred_sz;
+
+ if (conn_req->max_rxmsg_sz) {
+ /*
+ * Override cred_per_msg calculation, this optimizes
+ * the credit-low indications since the host will actually
+ * issue smaller messages in the Send path.
+ */
+ if (conn_req->max_rxmsg_sz > max_msg_sz) {
+ status = -ENOMEM;
+ goto fail_tx;
+ }
+ endpoint->cred_dist.cred_per_msg =
+ conn_req->max_rxmsg_sz / target->tgt_cred_sz;
+ } else
+ endpoint->cred_dist.cred_per_msg =
+ max_msg_sz / target->tgt_cred_sz;
+
+ if (!endpoint->cred_dist.cred_per_msg)
+ endpoint->cred_dist.cred_per_msg = 1;
+
+ /* save local connection flags */
+ endpoint->conn_flags = conn_req->flags;
+
+fail_tx:
+ if (tx_pkt)
+ htc_reclaim_txctrl_buf(target, tx_pkt);
+
+ if (rx_pkt) {
+ htc_rxpkt_reset(rx_pkt);
+ reclaim_rx_ctrl_buf(target, rx_pkt);
+ }
+
+ return status;
+}
+
+static void reset_ep_state(struct htc_target *target)
+{
+ struct htc_endpoint *endpoint;
+ int i;
+
+ for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+ endpoint = &target->endpoint[i];
+ memset(&endpoint->cred_dist, 0, sizeof(endpoint->cred_dist));
+ endpoint->svc_id = 0;
+ endpoint->len_max = 0;
+ endpoint->max_txq_depth = 0;
+ memset(&endpoint->ep_st, 0,
+ sizeof(endpoint->ep_st));
+ INIT_LIST_HEAD(&endpoint->rx_bufq);
+ INIT_LIST_HEAD(&endpoint->txq);
+ endpoint->target = target;
+ }
+
+ /* reset distribution list */
+ INIT_LIST_HEAD(&target->cred_dist_list);
+}
+
+int ath6kl_htc_get_rxbuf_num(struct htc_target *target,
+ enum htc_endpoint_id endpoint)
+{
+ int num;
+
+ spin_lock_bh(&target->rx_lock);
+ num = get_queue_depth(&(target->endpoint[endpoint].rx_bufq));
+ spin_unlock_bh(&target->rx_lock);
+ return num;
+}
+
+static void htc_setup_msg_bndl(struct htc_target *target)
+{
+ /* limit what HTC can handle */
+ target->msg_per_bndl_max = min(HTC_HOST_MAX_MSG_PER_BUNDLE,
+ target->msg_per_bndl_max);
+
+ if (ath6kl_hif_enable_scatter(target->dev->ar)) {
+ target->msg_per_bndl_max = 0;
+ return;
+ }
+
+ /* limit bundle what the device layer can handle */
+ target->msg_per_bndl_max = min(target->max_scat_entries,
+ target->msg_per_bndl_max);
+
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "htc bundling allowed. max msg per htc bundle: %d\n",
+ target->msg_per_bndl_max);
+
+ /* Max rx bundle size is limited by the max tx bundle size */
+ target->max_rx_bndl_sz = target->max_xfer_szper_scatreq;
+ /* Max tx bundle size if limited by the extended mbox address range */
+ target->max_tx_bndl_sz = min(HIF_MBOX0_EXT_WIDTH,
+ target->max_xfer_szper_scatreq);
+
+ ath6kl_dbg(ATH6KL_DBG_ANY, "max recv: %d max send: %d\n",
+ target->max_rx_bndl_sz, target->max_tx_bndl_sz);
+
+ if (target->max_tx_bndl_sz)
+ target->tx_bndl_enable = true;
+
+ if (target->max_rx_bndl_sz)
+ target->rx_bndl_enable = true;
+
+ if ((target->tgt_cred_sz % target->block_sz) != 0) {
+ ath6kl_warn("credit size: %d is not block aligned! Disabling send bundling\n",
+ target->tgt_cred_sz);
+
+ /*
+ * Disallow send bundling since the credit size is
+ * not aligned to a block size the I/O block
+ * padding will spill into the next credit buffer
+ * which is fatal.
+ */
+ target->tx_bndl_enable = false;
+ }
+}
+
+int ath6kl_htc_wait_target(struct htc_target *target)
+{
+ struct htc_packet *packet = NULL;
+ struct htc_ready_ext_msg *rdy_msg;
+ struct htc_service_connect_req connect;
+ struct htc_service_connect_resp resp;
+ int status;
+
+ /* we should be getting 1 control message that the target is ready */
+ packet = htc_wait_for_ctrl_msg(target);
+
+ if (!packet)
+ return -ENOMEM;
+
+ /* we controlled the buffer creation so it's properly aligned */
+ rdy_msg = (struct htc_ready_ext_msg *)packet->buf;
+
+ if ((le16_to_cpu(rdy_msg->ver2_0_info.msg_id) != HTC_MSG_READY_ID) ||
+ (packet->act_len < sizeof(struct htc_ready_msg))) {
+ status = -ENOMEM;
+ goto fail_wait_target;
+ }
+
+ if (!rdy_msg->ver2_0_info.cred_cnt || !rdy_msg->ver2_0_info.cred_sz) {
+ status = -ENOMEM;
+ goto fail_wait_target;
+ }
+
+ target->tgt_creds = le16_to_cpu(rdy_msg->ver2_0_info.cred_cnt);
+ target->tgt_cred_sz = le16_to_cpu(rdy_msg->ver2_0_info.cred_sz);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "target ready: credits: %d credit size: %d\n",
+ target->tgt_creds, target->tgt_cred_sz);
+
+ /* check if this is an extended ready message */
+ if (packet->act_len >= sizeof(struct htc_ready_ext_msg)) {
+ /* this is an extended message */
+ target->htc_tgt_ver = rdy_msg->htc_ver;
+ target->msg_per_bndl_max = rdy_msg->msg_per_htc_bndl;
+ } else {
+ /* legacy */
+ target->htc_tgt_ver = HTC_VERSION_2P0;
+ target->msg_per_bndl_max = 0;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "using htc protocol version : %s (%d)\n",
+ (target->htc_tgt_ver == HTC_VERSION_2P0) ? "2.0" : ">= 2.1",
+ target->htc_tgt_ver);
+
+ if (target->msg_per_bndl_max > 0)
+ htc_setup_msg_bndl(target);
+
+ /* setup our pseudo HTC control endpoint connection */
+ memset(&connect, 0, sizeof(connect));
+ memset(&resp, 0, sizeof(resp));
+ connect.ep_cb.rx = htc_ctrl_rx;
+ connect.ep_cb.rx_refill = NULL;
+ connect.ep_cb.tx_full = NULL;
+ connect.max_txq_depth = NUM_CONTROL_BUFFERS;
+ connect.svc_id = HTC_CTRL_RSVD_SVC;
+
+ /* connect fake service */
+ status = ath6kl_htc_conn_service((void *)target, &connect, &resp);
+
+ if (status)
+ ath6kl_hif_cleanup_scatter(target->dev->ar);
+
+fail_wait_target:
+ if (packet) {
+ htc_rxpkt_reset(packet);
+ reclaim_rx_ctrl_buf(target, packet);
+ }
+
+ return status;
+}
+
+/*
+ * Start HTC, enable interrupts and let the target know
+ * host has finished setup.
+ */
+int ath6kl_htc_start(struct htc_target *target)
+{
+ struct htc_packet *packet;
+ int status;
+
+ /* Disable interrupts at the chip level */
+ ath6kldev_disable_intrs(target->dev);
+
+ target->htc_flags = 0;
+ target->rx_st_flags = 0;
+
+ /* Push control receive buffers into htc control endpoint */
+ while ((packet = htc_get_control_buf(target, false)) != NULL) {
+ status = htc_add_rxbuf(target, packet);
+ if (status)
+ return status;
+ }
+
+ /* NOTE: the first entry in the distribution list is ENDPOINT_0 */
+ ath6k_credit_init(target->cred_dist_cntxt, &target->cred_dist_list,
+ target->tgt_creds);
+
+ dump_cred_dist_stats(target);
+
+ /* Indicate to the target of the setup completion */
+ status = htc_setup_tx_complete(target);
+
+ if (status)
+ return status;
+
+ /* unmask interrupts */
+ status = ath6kldev_unmask_intrs(target->dev);
+
+ if (status)
+ ath6kl_htc_stop(target);
+
+ return status;
+}
+
+/* htc_stop: stop interrupt reception, and flush all queued buffers */
+void ath6kl_htc_stop(struct htc_target *target)
+{
+ spin_lock_bh(&target->htc_lock);
+ target->htc_flags |= HTC_OP_STATE_STOPPING;
+ spin_unlock_bh(&target->htc_lock);
+
+ /*
+ * Masking interrupts is a synchronous operation, when this
+ * function returns all pending HIF I/O has completed, we can
+ * safely flush the queues.
+ */
+ ath6kldev_mask_intrs(target->dev);
+
+ ath6kl_htc_flush_txep_all(target);
+
+ ath6kl_htc_flush_rx_buf(target);
+
+ reset_ep_state(target);
+}
+
+void *ath6kl_htc_create(struct ath6kl *ar)
+{
+ struct htc_target *target = NULL;
+ struct htc_packet *packet;
+ int status = 0, i = 0;
+ u32 block_size, ctrl_bufsz;
+
+ target = kzalloc(sizeof(*target), GFP_KERNEL);
+ if (!target) {
+ ath6kl_err("unable to allocate memory\n");
+ return NULL;
+ }
+
+ target->dev = kzalloc(sizeof(*target->dev), GFP_KERNEL);
+ if (!target->dev) {
+ ath6kl_err("unable to allocate memory\n");
+ status = -ENOMEM;
+ goto fail_create_htc;
+ }
+
+ spin_lock_init(&target->htc_lock);
+ spin_lock_init(&target->rx_lock);
+ spin_lock_init(&target->tx_lock);
+
+ INIT_LIST_HEAD(&target->free_ctrl_txbuf);
+ INIT_LIST_HEAD(&target->free_ctrl_rxbuf);
+ INIT_LIST_HEAD(&target->cred_dist_list);
+
+ target->dev->ar = ar;
+ target->dev->htc_cnxt = target;
+ target->ep_waiting = ENDPOINT_MAX;
+
+ reset_ep_state(target);
+
+ status = ath6kldev_setup(target->dev);
+
+ if (status)
+ goto fail_create_htc;
+
+ block_size = ar->mbox_info.block_size;
+
+ ctrl_bufsz = (block_size > HTC_MAX_CTRL_MSG_LEN) ?
+ (block_size + HTC_HDR_LENGTH) :
+ (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH);
+
+ for (i = 0; i < NUM_CONTROL_BUFFERS; i++) {
+ packet = kzalloc(sizeof(*packet), GFP_KERNEL);
+ if (!packet)
+ break;
+
+ packet->buf_start = kzalloc(ctrl_bufsz, GFP_KERNEL);
+ if (!packet->buf_start) {
+ kfree(packet);
+ break;
+ }
+
+ packet->buf_len = ctrl_bufsz;
+ if (i < NUM_CONTROL_RX_BUFFERS) {
+ packet->act_len = 0;
+ packet->buf = packet->buf_start;
+ packet->endpoint = ENDPOINT_0;
+ list_add_tail(&packet->list, &target->free_ctrl_rxbuf);
+ } else
+ list_add_tail(&packet->list, &target->free_ctrl_txbuf);
+ }
+
+fail_create_htc:
+ if (i != NUM_CONTROL_BUFFERS || status) {
+ if (target) {
+ ath6kl_htc_cleanup(target);
+ target = NULL;
+ }
+ }
+
+ return target;
+}
+
+/* cleanup the HTC instance */
+void ath6kl_htc_cleanup(struct htc_target *target)
+{
+ struct htc_packet *packet, *tmp_packet;
+
+ ath6kl_hif_cleanup_scatter(target->dev->ar);
+
+ list_for_each_entry_safe(packet, tmp_packet,
+ &target->free_ctrl_txbuf, list) {
+ list_del(&packet->list);
+ kfree(packet->buf_start);
+ kfree(packet);
+ }
+
+ list_for_each_entry_safe(packet, tmp_packet,
+ &target->free_ctrl_rxbuf, list) {
+ list_del(&packet->list);
+ kfree(packet->buf_start);
+ kfree(packet);
+ }
+
+ kfree(target->dev);
+ kfree(target);
+}
diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h
new file mode 100644
index 000000000000..8ce0c2c07ded
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/htc.h
@@ -0,0 +1,607 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HTC_H
+#define HTC_H
+
+#include "common.h"
+
+/* frame header flags */
+
+/* send direction */
+#define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0)
+#define HTC_FLAGS_SEND_BUNDLE (1 << 1)
+
+/* receive direction */
+#define HTC_FLG_RX_UNUSED (1 << 0)
+#define HTC_FLG_RX_TRAILER (1 << 1)
+/* Bundle count maske and shift */
+#define HTC_FLG_RX_BNDL_CNT (0xF0)
+#define HTC_FLG_RX_BNDL_CNT_S 4
+
+#define HTC_HDR_LENGTH (sizeof(struct htc_frame_hdr))
+#define HTC_MAX_PAYLOAD_LENGTH (4096 - sizeof(struct htc_frame_hdr))
+
+/* HTC control message IDs */
+
+#define HTC_MSG_READY_ID 1
+#define HTC_MSG_CONN_SVC_ID 2
+#define HTC_MSG_CONN_SVC_RESP_ID 3
+#define HTC_MSG_SETUP_COMPLETE_ID 4
+#define HTC_MSG_SETUP_COMPLETE_EX_ID 5
+
+#define HTC_MAX_CTRL_MSG_LEN 256
+
+#define HTC_VERSION_2P0 0x00
+#define HTC_VERSION_2P1 0x01
+
+#define HTC_SERVICE_META_DATA_MAX_LENGTH 128
+
+#define HTC_CONN_FLGS_THRESH_LVL_QUAT 0x0
+#define HTC_CONN_FLGS_THRESH_LVL_HALF 0x1
+#define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT 0x2
+#define HTC_CONN_FLGS_REDUCE_CRED_DRIB 0x4
+#define HTC_CONN_FLGS_THRESH_MASK 0x3
+
+/* connect response status codes */
+#define HTC_SERVICE_SUCCESS 0
+#define HTC_SERVICE_NOT_FOUND 1
+#define HTC_SERVICE_FAILED 2
+
+/* no resources (i.e. no more endpoints) */
+#define HTC_SERVICE_NO_RESOURCES 3
+
+/* specific service is not allowing any more endpoints */
+#define HTC_SERVICE_NO_MORE_EP 4
+
+/* report record IDs */
+#define HTC_RECORD_NULL 0
+#define HTC_RECORD_CREDITS 1
+#define HTC_RECORD_LOOKAHEAD 2
+#define HTC_RECORD_LOOKAHEAD_BUNDLE 3
+
+#define HTC_SETUP_COMP_FLG_RX_BNDL_EN (1 << 0)
+
+#define MAKE_SERVICE_ID(group, index) \
+ (int)(((int)group << 8) | (int)(index))
+
+/* NOTE: service ID of 0x0000 is reserved and should never be used */
+#define HTC_CTRL_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 1)
+#define WMI_CONTROL_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 0)
+#define WMI_DATA_BE_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 1)
+#define WMI_DATA_BK_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 2)
+#define WMI_DATA_VI_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 3)
+#define WMI_DATA_VO_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4)
+#define WMI_MAX_SERVICES 5
+
+/* reserved and used to flush ALL packets */
+#define HTC_TX_PACKET_TAG_ALL 0
+#define HTC_SERVICE_TX_PACKET_TAG 1
+#define HTC_TX_PACKET_TAG_USER_DEFINED (HTC_SERVICE_TX_PACKET_TAG + 9)
+
+/* more packets on this endpoint are being fetched */
+#define HTC_RX_FLAGS_INDICATE_MORE_PKTS (1 << 0)
+
+/* TODO.. for BMI */
+#define ENDPOINT1 0
+/* TODO -remove me, but we have to fix BMI first */
+#define HTC_MAILBOX_NUM_MAX 4
+
+/* enable send bundle padding for this endpoint */
+#define HTC_FLGS_TX_BNDL_PAD_EN (1 << 0)
+#define HTC_EP_ACTIVE ((u32) (1u << 31))
+
+/* HTC operational parameters */
+#define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */
+#define HTC_TARGET_DEBUG_INTR_MASK 0x01
+#define HTC_TARGET_CREDIT_INTR_MASK 0xF0
+
+#define HTC_HOST_MAX_MSG_PER_BUNDLE 8
+#define HTC_MIN_HTC_MSGS_TO_BUNDLE 2
+
+/* packet flags */
+
+#define HTC_RX_PKT_IGNORE_LOOKAHEAD (1 << 0)
+#define HTC_RX_PKT_REFRESH_HDR (1 << 1)
+#define HTC_RX_PKT_PART_OF_BUNDLE (1 << 2)
+#define HTC_RX_PKT_NO_RECYCLE (1 << 3)
+
+#define NUM_CONTROL_BUFFERS 8
+#define NUM_CONTROL_TX_BUFFERS 2
+#define NUM_CONTROL_RX_BUFFERS (NUM_CONTROL_BUFFERS - NUM_CONTROL_TX_BUFFERS)
+
+#define HTC_RECV_WAIT_BUFFERS (1 << 0)
+#define HTC_OP_STATE_STOPPING (1 << 0)
+
+/*
+ * The frame header length and message formats defined herein were selected
+ * to accommodate optimal alignment for target processing. This reduces
+ * code size and improves performance. Any changes to the header length may
+ * alter the alignment and cause exceptions on the target. When adding to
+ * the messagestructures insure that fields are properly aligned.
+ */
+
+/* HTC frame header
+ *
+ * NOTE: do not remove or re-arrange the fields, these are minimally
+ * required to take advantage of 4-byte lookaheads in some hardware
+ * implementations.
+ */
+struct htc_frame_hdr {
+ u8 eid;
+ u8 flags;
+
+ /* length of data (including trailer) that follows the header */
+ __le16 payld_len;
+
+ /* end of 4-byte lookahead */
+
+ u8 ctrl[2];
+} __packed;
+
+/* HTC ready message */
+struct htc_ready_msg {
+ __le16 msg_id;
+ __le16 cred_cnt;
+ __le16 cred_sz;
+ u8 max_ep;
+ u8 pad;
+} __packed;
+
+/* extended HTC ready message */
+struct htc_ready_ext_msg {
+ struct htc_ready_msg ver2_0_info;
+ u8 htc_ver;
+ u8 msg_per_htc_bndl;
+} __packed;
+
+/* connect service */
+struct htc_conn_service_msg {
+ __le16 msg_id;
+ __le16 svc_id;
+ __le16 conn_flags;
+ u8 svc_meta_len;
+ u8 pad;
+} __packed;
+
+/* connect response */
+struct htc_conn_service_resp {
+ __le16 msg_id;
+ __le16 svc_id;
+ u8 status;
+ u8 eid;
+ __le16 max_msg_sz;
+ u8 svc_meta_len;
+ u8 pad;
+} __packed;
+
+struct htc_setup_comp_msg {
+ __le16 msg_id;
+} __packed;
+
+/* extended setup completion message */
+struct htc_setup_comp_ext_msg {
+ __le16 msg_id;
+ __le32 flags;
+ u8 msg_per_rxbndl;
+ u8 Rsvd[3];
+} __packed;
+
+struct htc_record_hdr {
+ u8 rec_id;
+ u8 len;
+} __packed;
+
+struct htc_credit_report {
+ u8 eid;
+ u8 credits;
+} __packed;
+
+/*
+ * NOTE: The lk_ahd array is guarded by a pre_valid
+ * and Post Valid guard bytes. The pre_valid bytes must
+ * equal the inverse of the post_valid byte.
+ */
+struct htc_lookahead_report {
+ u8 pre_valid;
+ u8 lk_ahd[4];
+ u8 post_valid;
+} __packed;
+
+struct htc_bundle_lkahd_rpt {
+ u8 lk_ahd[4];
+} __packed;
+
+/* Current service IDs */
+
+enum htc_service_grp_ids {
+ RSVD_SERVICE_GROUP = 0,
+ WMI_SERVICE_GROUP = 1,
+
+ HTC_TEST_GROUP = 254,
+ HTC_SERVICE_GROUP_LAST = 255
+};
+
+/* ------ endpoint IDS ------ */
+
+enum htc_endpoint_id {
+ ENDPOINT_UNUSED = -1,
+ ENDPOINT_0 = 0,
+ ENDPOINT_1 = 1,
+ ENDPOINT_2 = 2,
+ ENDPOINT_3,
+ ENDPOINT_4,
+ ENDPOINT_5,
+ ENDPOINT_6,
+ ENDPOINT_7,
+ ENDPOINT_8,
+ ENDPOINT_MAX,
+};
+
+struct htc_tx_packet_info {
+ u16 tag;
+ int cred_used;
+ u8 flags;
+ int seqno;
+};
+
+struct htc_rx_packet_info {
+ u32 exp_hdr;
+ u32 rx_flags;
+ u32 indicat_flags;
+};
+
+struct htc_target;
+
+/* wrapper around endpoint-specific packets */
+struct htc_packet {
+ struct list_head list;
+
+ /* caller's per packet specific context */
+ void *pkt_cntxt;
+
+ /*
+ * the true buffer start , the caller can store the real
+ * buffer start here. In receive callbacks, the HTC layer
+ * sets buf to the start of the payload past the header.
+ * This field allows the caller to reset buf when it recycles
+ * receive packets back to HTC.
+ */
+ u8 *buf_start;
+
+ /*
+ * Pointer to the start of the buffer. In the transmit
+ * direction this points to the start of the payload. In the
+ * receive direction, however, the buffer when queued up
+ * points to the start of the HTC header but when returned
+ * to the caller points to the start of the payload
+ */
+ u8 *buf;
+ u32 buf_len;
+
+ /* actual length of payload */
+ u32 act_len;
+
+ /* endpoint that this packet was sent/recv'd from */
+ enum htc_endpoint_id endpoint;
+
+ /* completion status */
+
+ int status;
+ union {
+ struct htc_tx_packet_info tx;
+ struct htc_rx_packet_info rx;
+ } info;
+
+ void (*completion) (struct htc_target *, struct htc_packet *);
+ struct htc_target *context;
+};
+
+enum htc_send_full_action {
+ HTC_SEND_FULL_KEEP = 0,
+ HTC_SEND_FULL_DROP = 1,
+};
+
+struct htc_ep_callbacks {
+ void (*rx) (struct htc_target *, struct htc_packet *);
+ void (*rx_refill) (struct htc_target *, enum htc_endpoint_id endpoint);
+ enum htc_send_full_action (*tx_full) (struct htc_target *,
+ struct htc_packet *);
+ struct htc_packet *(*rx_allocthresh) (struct htc_target *,
+ enum htc_endpoint_id, int);
+ int rx_alloc_thresh;
+ int rx_refill_thresh;
+};
+
+/* service connection information */
+struct htc_service_connect_req {
+ u16 svc_id;
+ u16 conn_flags;
+ struct htc_ep_callbacks ep_cb;
+ int max_txq_depth;
+ u32 flags;
+ unsigned int max_rxmsg_sz;
+};
+
+/* service connection response information */
+struct htc_service_connect_resp {
+ u8 buf_len;
+ u8 act_len;
+ enum htc_endpoint_id endpoint;
+ unsigned int len_max;
+ u8 resp_code;
+};
+
+/* endpoint distributionstructure */
+struct htc_endpoint_credit_dist {
+ struct list_head list;
+
+ /* Service ID (set by HTC) */
+ u16 svc_id;
+
+ /* endpoint for this distributionstruct (set by HTC) */
+ enum htc_endpoint_id endpoint;
+
+ u32 dist_flags;
+
+ /*
+ * credits for normal operation, anything above this
+ * indicates the endpoint is over-subscribed.
+ */
+ int cred_norm;
+
+ /* floor for credit distribution */
+ int cred_min;
+
+ int cred_assngd;
+
+ /* current credits available */
+ int credits;
+
+ /*
+ * pending credits to distribute on this endpoint, this
+ * is set by HTC when credit reports arrive. The credit
+ * distribution functions sets this to zero when it distributes
+ * the credits.
+ */
+ int cred_to_dist;
+
+ /*
+ * the number of credits that the current pending TX packet needs
+ * to transmit. This is set by HTC when endpoint needs credits in
+ * order to transmit.
+ */
+ int seek_cred;
+
+ /* size in bytes of each credit */
+ int cred_sz;
+
+ /* credits required for a maximum sized messages */
+ int cred_per_msg;
+
+ /* reserved for HTC use */
+ void *htc_rsvd;
+
+ /*
+ * current depth of TX queue , i.e. messages waiting for credits
+ * This field is valid only when HTC_CREDIT_DIST_ACTIVITY_CHANGE
+ * or HTC_CREDIT_DIST_SEND_COMPLETE is indicated on an endpoint
+ * that has non-zero credits to recover.
+ */
+ int txq_depth;
+};
+
+/*
+ * credit distibution code that is passed into the distrbution function,
+ * there are mandatory and optional codes that must be handled
+ */
+enum htc_credit_dist_reason {
+ HTC_CREDIT_DIST_SEND_COMPLETE = 0,
+ HTC_CREDIT_DIST_ACTIVITY_CHANGE = 1,
+ HTC_CREDIT_DIST_SEEK_CREDITS,
+};
+
+struct htc_credit_state_info {
+ int total_avail_credits;
+ int cur_free_credits;
+ struct list_head lowestpri_ep_dist;
+};
+
+/* endpoint statistics */
+struct htc_endpoint_stats {
+ /*
+ * number of times the host set the credit-low flag in a send
+ * message on this endpoint
+ */
+ u32 cred_low_indicate;
+
+ u32 tx_issued;
+ u32 tx_pkt_bundled;
+ u32 tx_bundles;
+ u32 tx_dropped;
+
+ /* running count of total credit reports received for this endpoint */
+ u32 tx_cred_rpt;
+
+ /* credit reports received from this endpoint's RX packets */
+ u32 cred_rpt_from_rx;
+
+ /* credit reports received from RX packets of other endpoints */
+ u32 cred_rpt_from_other;
+
+ /* credit reports received from endpoint 0 RX packets */
+ u32 cred_rpt_ep0;
+
+ /* count of credits received via Rx packets on this endpoint */
+ u32 cred_from_rx;
+
+ /* count of credits received via another endpoint */
+ u32 cred_from_other;
+
+ /* count of credits received via another endpoint */
+ u32 cred_from_ep0;
+
+ /* count of consummed credits */
+ u32 cred_cosumd;
+
+ /* count of credits returned */
+ u32 cred_retnd;
+
+ u32 rx_pkts;
+
+ /* count of lookahead records found in Rx msg */
+ u32 rx_lkahds;
+
+ /* count of recv packets received in a bundle */
+ u32 rx_bundl;
+
+ /* count of number of bundled lookaheads */
+ u32 rx_bundle_lkahd;
+
+ /* count of the number of bundle indications from the HTC header */
+ u32 rx_bundle_from_hdr;
+
+ /* the number of times the recv allocation threshold was hit */
+ u32 rx_alloc_thresh_hit;
+
+ /* total number of bytes */
+ u32 rxalloc_thresh_byte;
+};
+
+struct htc_endpoint {
+ enum htc_endpoint_id eid;
+ u16 svc_id;
+ struct list_head txq;
+ struct list_head rx_bufq;
+ struct htc_endpoint_credit_dist cred_dist;
+ struct htc_ep_callbacks ep_cb;
+ int max_txq_depth;
+ int len_max;
+ int tx_proc_cnt;
+ int rx_proc_cnt;
+ struct htc_target *target;
+ u8 seqno;
+ u32 conn_flags;
+ struct htc_endpoint_stats ep_st;
+};
+
+struct htc_control_buffer {
+ struct htc_packet packet;
+ u8 *buf;
+};
+
+struct ath6kl_device;
+
+/* our HTC target state */
+struct htc_target {
+ struct htc_endpoint endpoint[ENDPOINT_MAX];
+ struct list_head cred_dist_list;
+ struct list_head free_ctrl_txbuf;
+ struct list_head free_ctrl_rxbuf;
+ struct htc_credit_state_info *cred_dist_cntxt;
+ int tgt_creds;
+ unsigned int tgt_cred_sz;
+ spinlock_t htc_lock;
+ spinlock_t rx_lock;
+ spinlock_t tx_lock;
+ struct ath6kl_device *dev;
+ u32 htc_flags;
+ u32 rx_st_flags;
+ enum htc_endpoint_id ep_waiting;
+ u8 htc_tgt_ver;
+
+ /* max messages per bundle for HTC */
+ int msg_per_bndl_max;
+
+ bool tx_bndl_enable;
+ int rx_bndl_enable;
+ int max_rx_bndl_sz;
+ int max_tx_bndl_sz;
+
+ u32 block_sz;
+ u32 block_mask;
+
+ int max_scat_entries;
+ int max_xfer_szper_scatreq;
+
+ int chk_irq_status_cnt;
+};
+
+void *ath6kl_htc_create(struct ath6kl *ar);
+void ath6kl_htc_set_credit_dist(struct htc_target *target,
+ struct htc_credit_state_info *cred_info,
+ u16 svc_pri_order[], int len);
+int ath6kl_htc_wait_target(struct htc_target *target);
+int ath6kl_htc_start(struct htc_target *target);
+int ath6kl_htc_conn_service(struct htc_target *target,
+ struct htc_service_connect_req *req,
+ struct htc_service_connect_resp *resp);
+int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet);
+void ath6kl_htc_stop(struct htc_target *target);
+void ath6kl_htc_cleanup(struct htc_target *target);
+void ath6kl_htc_flush_txep(struct htc_target *target,
+ enum htc_endpoint_id endpoint, u16 tag);
+void ath6kl_htc_flush_rx_buf(struct htc_target *target);
+void ath6kl_htc_indicate_activity_change(struct htc_target *target,
+ enum htc_endpoint_id endpoint,
+ bool active);
+int ath6kl_htc_get_rxbuf_num(struct htc_target *target,
+ enum htc_endpoint_id endpoint);
+int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
+ struct list_head *pktq);
+int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
+ u32 msg_look_ahead[], int *n_pkts);
+
+static inline void set_htc_pkt_info(struct htc_packet *packet, void *context,
+ u8 *buf, unsigned int len,
+ enum htc_endpoint_id eid, u16 tag)
+{
+ packet->pkt_cntxt = context;
+ packet->buf = buf;
+ packet->act_len = len;
+ packet->endpoint = eid;
+ packet->info.tx.tag = tag;
+}
+
+static inline void htc_rxpkt_reset(struct htc_packet *packet)
+{
+ packet->buf = packet->buf_start;
+ packet->act_len = 0;
+}
+
+static inline void set_htc_rxpkt_info(struct htc_packet *packet, void *context,
+ u8 *buf, unsigned long len,
+ enum htc_endpoint_id eid)
+{
+ packet->pkt_cntxt = context;
+ packet->buf = buf;
+ packet->buf_start = buf;
+ packet->buf_len = len;
+ packet->endpoint = eid;
+}
+
+static inline int get_queue_depth(struct list_head *queue)
+{
+ struct list_head *tmp_list;
+ int depth = 0;
+
+ list_for_each(tmp_list, queue)
+ depth++;
+
+ return depth;
+}
+
+#endif
diff --git a/drivers/net/wireless/ath/ath6kl/htc_hif.c b/drivers/net/wireless/ath/ath6kl/htc_hif.c
new file mode 100644
index 000000000000..86b1cc7409c2
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/htc_hif.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) 2007-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "target.h"
+#include "hif-ops.h"
+#include "htc_hif.h"
+#include "debug.h"
+
+#define MAILBOX_FOR_BLOCK_SIZE 1
+
+#define ATH6KL_TIME_QUANTUM 10 /* in ms */
+
+static int ath6kldev_cp_scat_dma_buf(struct hif_scatter_req *req, bool from_dma)
+{
+ u8 *buf;
+ int i;
+
+ buf = req->virt_dma_buf;
+
+ for (i = 0; i < req->scat_entries; i++) {
+
+ if (from_dma)
+ memcpy(req->scat_list[i].buf, buf,
+ req->scat_list[i].len);
+ else
+ memcpy(buf, req->scat_list[i].buf,
+ req->scat_list[i].len);
+
+ buf += req->scat_list[i].len;
+ }
+
+ return 0;
+}
+
+int ath6kldev_rw_comp_handler(void *context, int status)
+{
+ struct htc_packet *packet = context;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+ "ath6kldev_rw_comp_handler (pkt:0x%p , status: %d\n",
+ packet, status);
+
+ packet->status = status;
+ packet->completion(packet->context, packet);
+
+ return 0;
+}
+
+static int ath6kldev_proc_dbg_intr(struct ath6kl_device *dev)
+{
+ u32 dummy;
+ int status;
+
+ ath6kl_err("target debug interrupt\n");
+
+ ath6kl_target_failure(dev->ar);
+
+ /*
+ * read counter to clear the interrupt, the debug error interrupt is
+ * counter 0.
+ */
+ status = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
+ (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
+ if (status)
+ WARN_ON(1);
+
+ return status;
+}
+
+/* mailbox recv message polling */
+int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
+ int timeout)
+{
+ struct ath6kl_irq_proc_registers *rg;
+ int status = 0, i;
+ u8 htc_mbox = 1 << HTC_MAILBOX;
+
+ for (i = timeout / ATH6KL_TIME_QUANTUM; i > 0; i--) {
+ /* this is the standard HIF way, load the reg table */
+ status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
+ (u8 *) &dev->irq_proc_reg,
+ sizeof(dev->irq_proc_reg),
+ HIF_RD_SYNC_BYTE_INC);
+
+ if (status) {
+ ath6kl_err("failed to read reg table\n");
+ return status;
+ }
+
+ /* check for MBOX data and valid lookahead */
+ if (dev->irq_proc_reg.host_int_status & htc_mbox) {
+ if (dev->irq_proc_reg.rx_lkahd_valid &
+ htc_mbox) {
+ /*
+ * Mailbox has a message and the look ahead
+ * is valid.
+ */
+ rg = &dev->irq_proc_reg;
+ *lk_ahd =
+ le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
+ break;
+ }
+ }
+
+ /* delay a little */
+ mdelay(ATH6KL_TIME_QUANTUM);
+ ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "retry mbox poll : %d\n", i);
+ }
+
+ if (i == 0) {
+ ath6kl_err("timeout waiting for recv message\n");
+ status = -ETIME;
+ /* check if the target asserted */
+ if (dev->irq_proc_reg.counter_int_status &
+ ATH6KL_TARGET_DEBUG_INTR_MASK)
+ /*
+ * Target failure handler will be called in case of
+ * an assert.
+ */
+ ath6kldev_proc_dbg_intr(dev);
+ }
+
+ return status;
+}
+
+/*
+ * Disable packet reception (used in case the host runs out of buffers)
+ * using the interrupt enable registers through the host I/F
+ */
+int ath6kldev_rx_control(struct ath6kl_device *dev, bool enable_rx)
+{
+ struct ath6kl_irq_enable_reg regs;
+ int status = 0;
+
+ /* take the lock to protect interrupt enable shadows */
+ spin_lock_bh(&dev->lock);
+
+ if (enable_rx)
+ dev->irq_en_reg.int_status_en |=
+ SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
+ else
+ dev->irq_en_reg.int_status_en &=
+ ~SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
+
+ memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
+
+ spin_unlock_bh(&dev->lock);
+
+ status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
+ &regs.int_status_en,
+ sizeof(struct ath6kl_irq_enable_reg),
+ HIF_WR_SYNC_BYTE_INC);
+
+ return status;
+}
+
+int ath6kldev_submit_scat_req(struct ath6kl_device *dev,
+ struct hif_scatter_req *scat_req, bool read)
+{
+ int status = 0;
+
+ if (read) {
+ scat_req->req = HIF_RD_SYNC_BLOCK_FIX;
+ scat_req->addr = dev->ar->mbox_info.htc_addr;
+ } else {
+ scat_req->req = HIF_WR_ASYNC_BLOCK_INC;
+
+ scat_req->addr =
+ (scat_req->len > HIF_MBOX_WIDTH) ?
+ dev->ar->mbox_info.htc_ext_addr :
+ dev->ar->mbox_info.htc_addr;
+ }
+
+ ath6kl_dbg((ATH6KL_DBG_HTC_RECV | ATH6KL_DBG_HTC_SEND),
+ "ath6kldev_submit_scat_req, entries: %d, total len: %d mbox:0x%X (mode: %s : %s)\n",
+ scat_req->scat_entries, scat_req->len,
+ scat_req->addr, !read ? "async" : "sync",
+ (read) ? "rd" : "wr");
+
+ if (!read && scat_req->virt_scat) {
+ status = ath6kldev_cp_scat_dma_buf(scat_req, false);
+ if (status) {
+ scat_req->status = status;
+ scat_req->complete(dev->ar->htc_target, scat_req);
+ return 0;
+ }
+ }
+
+ status = ath6kl_hif_scat_req_rw(dev->ar, scat_req);
+
+ if (read) {
+ /* in sync mode, we can touch the scatter request */
+ scat_req->status = status;
+ if (!status && scat_req->virt_scat)
+ scat_req->status =
+ ath6kldev_cp_scat_dma_buf(scat_req, true);
+ }
+
+ return status;
+}
+
+static int ath6kldev_proc_counter_intr(struct ath6kl_device *dev)
+{
+ u8 counter_int_status;
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ, "counter interrupt\n");
+
+ counter_int_status = dev->irq_proc_reg.counter_int_status &
+ dev->irq_en_reg.cntr_int_status_en;
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
+ counter_int_status);
+
+ /*
+ * NOTE: other modules like GMBOX may use the counter interrupt for
+ * credit flow control on other counters, we only need to check for
+ * the debug assertion counter interrupt.
+ */
+ if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK)
+ return ath6kldev_proc_dbg_intr(dev);
+
+ return 0;
+}
+
+static int ath6kldev_proc_err_intr(struct ath6kl_device *dev)
+{
+ int status;
+ u8 error_int_status;
+ u8 reg_buf[4];
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ, "error interrupt\n");
+
+ error_int_status = dev->irq_proc_reg.error_int_status & 0x0F;
+ if (!error_int_status) {
+ WARN_ON(1);
+ return -EIO;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n",
+ error_int_status);
+
+ if (MS(ERROR_INT_STATUS_WAKEUP, error_int_status))
+ ath6kl_dbg(ATH6KL_DBG_IRQ, "error : wakeup\n");
+
+ if (MS(ERROR_INT_STATUS_RX_UNDERFLOW, error_int_status))
+ ath6kl_err("rx underflow\n");
+
+ if (MS(ERROR_INT_STATUS_TX_OVERFLOW, error_int_status))
+ ath6kl_err("tx overflow\n");
+
+ /* Clear the interrupt */
+ dev->irq_proc_reg.error_int_status &= ~error_int_status;
+
+ /* set W1C value to clear the interrupt, this hits the register first */
+ reg_buf[0] = error_int_status;
+ reg_buf[1] = 0;
+ reg_buf[2] = 0;
+ reg_buf[3] = 0;
+
+ status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS,
+ reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
+
+ if (status)
+ WARN_ON(1);
+
+ return status;
+}
+
+static int ath6kldev_proc_cpu_intr(struct ath6kl_device *dev)
+{
+ int status;
+ u8 cpu_int_status;
+ u8 reg_buf[4];
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ, "cpu interrupt\n");
+
+ cpu_int_status = dev->irq_proc_reg.cpu_int_status &
+ dev->irq_en_reg.cpu_int_status_en;
+ if (!cpu_int_status) {
+ WARN_ON(1);
+ return -EIO;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
+ cpu_int_status);
+
+ /* Clear the interrupt */
+ dev->irq_proc_reg.cpu_int_status &= ~cpu_int_status;
+
+ /*
+ * Set up the register transfer buffer to hit the register 4 times ,
+ * this is done to make the access 4-byte aligned to mitigate issues
+ * with host bus interconnects that restrict bus transfer lengths to
+ * be a multiple of 4-bytes.
+ */
+
+ /* set W1C value to clear the interrupt, this hits the register first */
+ reg_buf[0] = cpu_int_status;
+ /* the remaining are set to zero which have no-effect */
+ reg_buf[1] = 0;
+ reg_buf[2] = 0;
+ reg_buf[3] = 0;
+
+ status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS,
+ reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
+
+ if (status)
+ WARN_ON(1);
+
+ return status;
+}
+
+/* process pending interrupts synchronously */
+static int proc_pending_irqs(struct ath6kl_device *dev, bool *done)
+{
+ struct ath6kl_irq_proc_registers *rg;
+ int status = 0;
+ u8 host_int_status = 0;
+ u32 lk_ahd = 0;
+ u8 htc_mbox = 1 << HTC_MAILBOX;
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev);
+
+ /*
+ * NOTE: HIF implementation guarantees that the context of this
+ * call allows us to perform SYNCHRONOUS I/O, that is we can block,
+ * sleep or call any API that can block or switch thread/task
+ * contexts. This is a fully schedulable context.
+ */
+
+ /*
+ * Process pending intr only when int_status_en is clear, it may
+ * result in unnecessary bus transaction otherwise. Target may be
+ * unresponsive at the time.
+ */
+ if (dev->irq_en_reg.int_status_en) {
+ /*
+ * Read the first 28 bytes of the HTC register table. This
+ * will yield us the value of different int status
+ * registers and the lookahead registers.
+ *
+ * length = sizeof(int_status) + sizeof(cpu_int_status)
+ * + sizeof(error_int_status) +
+ * sizeof(counter_int_status) +
+ * sizeof(mbox_frame) + sizeof(rx_lkahd_valid)
+ * + sizeof(hole) + sizeof(rx_lkahd) +
+ * sizeof(int_status_en) +
+ * sizeof(cpu_int_status_en) +
+ * sizeof(err_int_status_en) +
+ * sizeof(cntr_int_status_en);
+ */
+ status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
+ (u8 *) &dev->irq_proc_reg,
+ sizeof(dev->irq_proc_reg),
+ HIF_RD_SYNC_BYTE_INC);
+ if (status)
+ goto out;
+
+ if (AR_DBG_LVL_CHECK(ATH6KL_DBG_IRQ))
+ ath6kl_dump_registers(dev, &dev->irq_proc_reg,
+ &dev->irq_en_reg);
+
+ /* Update only those registers that are enabled */
+ host_int_status = dev->irq_proc_reg.host_int_status &
+ dev->irq_en_reg.int_status_en;
+
+ /* Look at mbox status */
+ if (host_int_status & htc_mbox) {
+ /*
+ * Mask out pending mbox value, we use "lookAhead as
+ * the real flag for mbox processing.
+ */
+ host_int_status &= ~htc_mbox;
+ if (dev->irq_proc_reg.rx_lkahd_valid &
+ htc_mbox) {
+ rg = &dev->irq_proc_reg;
+ lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
+ if (!lk_ahd)
+ ath6kl_err("lookAhead is zero!\n");
+ }
+ }
+ }
+
+ if (!host_int_status && !lk_ahd) {
+ *done = true;
+ goto out;
+ }
+
+ if (lk_ahd) {
+ int fetched = 0;
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd);
+ /*
+ * Mailbox Interrupt, the HTC layer may issue async
+ * requests to empty the mailbox. When emptying the recv
+ * mailbox we use the async handler above called from the
+ * completion routine of the callers read request. This can
+ * improve performance by reducing context switching when
+ * we rapidly pull packets.
+ */
+ status = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt,
+ &lk_ahd, &fetched);
+ if (status)
+ goto out;
+
+ if (!fetched)
+ /*
+ * HTC could not pull any messages out due to lack
+ * of resources.
+ */
+ dev->htc_cnxt->chk_irq_status_cnt = 0;
+ }
+
+ /* now handle the rest of them */
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "valid interrupt source(s) for other interrupts: 0x%x\n",
+ host_int_status);
+
+ if (MS(HOST_INT_STATUS_CPU, host_int_status)) {
+ /* CPU Interrupt */
+ status = ath6kldev_proc_cpu_intr(dev);
+ if (status)
+ goto out;
+ }
+
+ if (MS(HOST_INT_STATUS_ERROR, host_int_status)) {
+ /* Error Interrupt */
+ status = ath6kldev_proc_err_intr(dev);
+ if (status)
+ goto out;
+ }
+
+ if (MS(HOST_INT_STATUS_COUNTER, host_int_status))
+ /* Counter Interrupt */
+ status = ath6kldev_proc_counter_intr(dev);
+
+out:
+ /*
+ * An optimization to bypass reading the IRQ status registers
+ * unecessarily which can re-wake the target, if upper layers
+ * determine that we are in a low-throughput mode, we can rely on
+ * taking another interrupt rather than re-checking the status
+ * registers which can re-wake the target.
+ *
+ * NOTE : for host interfaces that makes use of detecting pending
+ * mbox messages at hif can not use this optimization due to
+ * possible side effects, SPI requires the host to drain all
+ * messages from the mailbox before exiting the ISR routine.
+ */
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "bypassing irq status re-check, forcing done\n");
+
+ if (!dev->htc_cnxt->chk_irq_status_cnt)
+ *done = true;
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "proc_pending_irqs: (done:%d, status=%d\n", *done, status);
+
+ return status;
+}
+
+/* interrupt handler, kicks off all interrupt processing */
+int ath6kldev_intr_bh_handler(struct ath6kl *ar)
+{
+ struct ath6kl_device *dev = ar->htc_target->dev;
+ int status = 0;
+ bool done = false;
+
+ /*
+ * Reset counter used to flag a re-scan of IRQ status registers on
+ * the target.
+ */
+ dev->htc_cnxt->chk_irq_status_cnt = 0;
+
+ /*
+ * IRQ processing is synchronous, interrupt status registers can be
+ * re-read.
+ */
+ while (!done) {
+ status = proc_pending_irqs(dev, &done);
+ if (status)
+ break;
+ }
+
+ return status;
+}
+
+static int ath6kldev_enable_intrs(struct ath6kl_device *dev)
+{
+ struct ath6kl_irq_enable_reg regs;
+ int status;
+
+ spin_lock_bh(&dev->lock);
+
+ /* Enable all but ATH6KL CPU interrupts */
+ dev->irq_en_reg.int_status_en =
+ SM(INT_STATUS_ENABLE_ERROR, 0x01) |
+ SM(INT_STATUS_ENABLE_CPU, 0x01) |
+ SM(INT_STATUS_ENABLE_COUNTER, 0x01);
+
+ /*
+ * NOTE: There are some cases where HIF can do detection of
+ * pending mbox messages which is disabled now.
+ */
+ dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
+
+ /* Set up the CPU Interrupt status Register */
+ dev->irq_en_reg.cpu_int_status_en = 0;
+
+ /* Set up the Error Interrupt status Register */
+ dev->irq_en_reg.err_int_status_en =
+ SM(ERROR_STATUS_ENABLE_RX_UNDERFLOW, 0x01) |
+ SM(ERROR_STATUS_ENABLE_TX_OVERFLOW, 0x1);
+
+ /*
+ * Enable Counter interrupt status register to get fatal errors for
+ * debugging.
+ */
+ dev->irq_en_reg.cntr_int_status_en = SM(COUNTER_INT_STATUS_ENABLE_BIT,
+ ATH6KL_TARGET_DEBUG_INTR_MASK);
+ memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
+
+ spin_unlock_bh(&dev->lock);
+
+ status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
+ &regs.int_status_en, sizeof(regs),
+ HIF_WR_SYNC_BYTE_INC);
+
+ if (status)
+ ath6kl_err("failed to update interrupt ctl reg err: %d\n",
+ status);
+
+ return status;
+}
+
+int ath6kldev_disable_intrs(struct ath6kl_device *dev)
+{
+ struct ath6kl_irq_enable_reg regs;
+
+ spin_lock_bh(&dev->lock);
+ /* Disable all interrupts */
+ dev->irq_en_reg.int_status_en = 0;
+ dev->irq_en_reg.cpu_int_status_en = 0;
+ dev->irq_en_reg.err_int_status_en = 0;
+ dev->irq_en_reg.cntr_int_status_en = 0;
+ memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
+ spin_unlock_bh(&dev->lock);
+
+ return hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
+ &regs.int_status_en, sizeof(regs),
+ HIF_WR_SYNC_BYTE_INC);
+}
+
+/* enable device interrupts */
+int ath6kldev_unmask_intrs(struct ath6kl_device *dev)
+{
+ int status = 0;
+
+ /*
+ * Make sure interrupt are disabled before unmasking at the HIF
+ * layer. The rationale here is that between device insertion
+ * (where we clear the interrupts the first time) and when HTC
+ * is finally ready to handle interrupts, other software can perform
+ * target "soft" resets. The ATH6KL interrupt enables reset back to an
+ * "enabled" state when this happens.
+ */
+ ath6kldev_disable_intrs(dev);
+
+ /* unmask the host controller interrupts */
+ ath6kl_hif_irq_enable(dev->ar);
+ status = ath6kldev_enable_intrs(dev);
+
+ return status;
+}
+
+/* disable all device interrupts */
+int ath6kldev_mask_intrs(struct ath6kl_device *dev)
+{
+ /*
+ * Mask the interrupt at the HIF layer to avoid any stray interrupt
+ * taken while we zero out our shadow registers in
+ * ath6kldev_disable_intrs().
+ */
+ ath6kl_hif_irq_disable(dev->ar);
+
+ return ath6kldev_disable_intrs(dev);
+}
+
+int ath6kldev_setup(struct ath6kl_device *dev)
+{
+ int status = 0;
+
+ spin_lock_init(&dev->lock);
+
+ /*
+ * NOTE: we actually get the block size of a mailbox other than 0,
+ * for SDIO the block size on mailbox 0 is artificially set to 1.
+ * So we use the block size that is set for the other 3 mailboxes.
+ */
+ dev->htc_cnxt->block_sz = dev->ar->mbox_info.block_size;
+
+ /* must be a power of 2 */
+ if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) {
+ WARN_ON(1);
+ goto fail_setup;
+ }
+
+ /* assemble mask, used for padding to a block */
+ dev->htc_cnxt->block_mask = dev->htc_cnxt->block_sz - 1;
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "block size: %d, mbox addr:0x%X\n",
+ dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr);
+
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "hif interrupt processing is sync only\n");
+
+ status = ath6kldev_disable_intrs(dev);
+
+fail_setup:
+ return status;
+
+}
diff --git a/drivers/net/wireless/ath/ath6kl/htc_hif.h b/drivers/net/wireless/ath/ath6kl/htc_hif.h
new file mode 100644
index 000000000000..171ad63d89b0
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/htc_hif.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2007-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HTC_HIF_H
+#define HTC_HIF_H
+
+#include "htc.h"
+#include "hif.h"
+
+#define ATH6KL_MAILBOXES 4
+
+/* HTC runs over mailbox 0 */
+#define HTC_MAILBOX 0
+
+#define ATH6KL_TARGET_DEBUG_INTR_MASK 0x01
+
+#define OTHER_INTS_ENABLED (INT_STATUS_ENABLE_ERROR_MASK | \
+ INT_STATUS_ENABLE_CPU_MASK | \
+ INT_STATUS_ENABLE_COUNTER_MASK)
+
+#define ATH6KL_REG_IO_BUFFER_SIZE 32
+#define ATH6KL_MAX_REG_IO_BUFFERS 8
+#define ATH6KL_SCATTER_ENTRIES_PER_REQ 16
+#define ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER (16 * 1024)
+#define ATH6KL_SCATTER_REQS 4
+
+#ifndef A_CACHE_LINE_PAD
+#define A_CACHE_LINE_PAD 128
+#endif
+#define ATH6KL_MIN_SCATTER_ENTRIES_PER_REQ 2
+#define ATH6KL_MIN_TRANSFER_SIZE_PER_SCATTER (4 * 1024)
+
+struct ath6kl_irq_proc_registers {
+ u8 host_int_status;
+ u8 cpu_int_status;
+ u8 error_int_status;
+ u8 counter_int_status;
+ u8 mbox_frame;
+ u8 rx_lkahd_valid;
+ u8 host_int_status2;
+ u8 gmbox_rx_avail;
+ __le32 rx_lkahd[2];
+ __le32 rx_gmbox_lkahd_alias[2];
+} __packed;
+
+struct ath6kl_irq_enable_reg {
+ u8 int_status_en;
+ u8 cpu_int_status_en;
+ u8 err_int_status_en;
+ u8 cntr_int_status_en;
+} __packed;
+
+struct ath6kl_device {
+ spinlock_t lock;
+ u8 pad1[A_CACHE_LINE_PAD];
+ struct ath6kl_irq_proc_registers irq_proc_reg;
+ u8 pad2[A_CACHE_LINE_PAD];
+ struct ath6kl_irq_enable_reg irq_en_reg;
+ u8 pad3[A_CACHE_LINE_PAD];
+ struct htc_target *htc_cnxt;
+ struct ath6kl *ar;
+};
+
+int ath6kldev_setup(struct ath6kl_device *dev);
+int ath6kldev_unmask_intrs(struct ath6kl_device *dev);
+int ath6kldev_mask_intrs(struct ath6kl_device *dev);
+int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev,
+ u32 *lk_ahd, int timeout);
+int ath6kldev_rx_control(struct ath6kl_device *dev, bool enable_rx);
+int ath6kldev_disable_intrs(struct ath6kl_device *dev);
+
+int ath6kldev_rw_comp_handler(void *context, int status);
+int ath6kldev_intr_bh_handler(struct ath6kl *ar);
+
+/* Scatter Function and Definitions */
+int ath6kldev_submit_scat_req(struct ath6kl_device *dev,
+ struct hif_scatter_req *scat_req, bool read);
+
+#endif /*ATH6KL_H_ */
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
new file mode 100644
index 000000000000..c1d2366704b5
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -0,0 +1,1727 @@
+
+/*
+ * Copyright (c) 2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/mmc/sdio_func.h>
+#include "core.h"
+#include "cfg80211.h"
+#include "target.h"
+#include "debug.h"
+#include "hif-ops.h"
+
+unsigned int debug_mask;
+static unsigned int testmode;
+
+module_param(debug_mask, uint, 0644);
+module_param(testmode, uint, 0644);
+
+/*
+ * Include definitions here that can be used to tune the WLAN module
+ * behavior. Different customers can tune the behavior as per their needs,
+ * here.
+ */
+
+/*
+ * This configuration item enable/disable keepalive support.
+ * Keepalive support: In the absence of any data traffic to AP, null
+ * frames will be sent to the AP at periodic interval, to keep the association
+ * active. This configuration item defines the periodic interval.
+ * Use value of zero to disable keepalive support
+ * Default: 60 seconds
+ */
+#define WLAN_CONFIG_KEEP_ALIVE_INTERVAL 60
+
+/*
+ * This configuration item sets the value of disconnect timeout
+ * Firmware delays sending the disconnec event to the host for this
+ * timeout after is gets disconnected from the current AP.
+ * If the firmware successly roams within the disconnect timeout
+ * it sends a new connect event
+ */
+#define WLAN_CONFIG_DISCONNECT_TIMEOUT 10
+
+#define CONFIG_AR600x_DEBUG_UART_TX_PIN 8
+
+#define ATH6KL_DATA_OFFSET 64
+struct sk_buff *ath6kl_buf_alloc(int size)
+{
+ struct sk_buff *skb;
+ u16 reserved;
+
+ /* Add chacheline space at front and back of buffer */
+ reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET +
+ sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES;
+ skb = dev_alloc_skb(size + reserved);
+
+ if (skb)
+ skb_reserve(skb, reserved - L1_CACHE_BYTES);
+ return skb;
+}
+
+void ath6kl_init_profile_info(struct ath6kl *ar)
+{
+ ar->ssid_len = 0;
+ memset(ar->ssid, 0, sizeof(ar->ssid));
+
+ ar->dot11_auth_mode = OPEN_AUTH;
+ ar->auth_mode = NONE_AUTH;
+ ar->prwise_crypto = NONE_CRYPT;
+ ar->prwise_crypto_len = 0;
+ ar->grp_crypto = NONE_CRYPT;
+ ar->grp_crypto_len = 0;
+ memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list));
+ memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+ memset(ar->bssid, 0, sizeof(ar->bssid));
+ ar->bss_ch = 0;
+ ar->nw_type = ar->next_mode = INFRA_NETWORK;
+}
+
+static u8 ath6kl_get_fw_iftype(struct ath6kl *ar)
+{
+ switch (ar->nw_type) {
+ case INFRA_NETWORK:
+ return HI_OPTION_FW_MODE_BSS_STA;
+ case ADHOC_NETWORK:
+ return HI_OPTION_FW_MODE_IBSS;
+ case AP_NETWORK:
+ return HI_OPTION_FW_MODE_AP;
+ default:
+ ath6kl_err("Unsupported interface type :%d\n", ar->nw_type);
+ return 0xff;
+ }
+}
+
+static int ath6kl_set_host_app_area(struct ath6kl *ar)
+{
+ u32 address, data;
+ struct host_app_area host_app_area;
+
+ /* Fetch the address of the host_app_area_s
+ * instance in the host interest area */
+ address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest));
+ address = TARG_VTOP(ar->target_type, address);
+
+ if (ath6kl_diag_read32(ar, address, &data))
+ return -EIO;
+
+ address = TARG_VTOP(ar->target_type, data);
+ host_app_area.wmi_protocol_ver = WMI_PROTOCOL_VERSION;
+ if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area,
+ sizeof(struct host_app_area)))
+ return -EIO;
+
+ return 0;
+}
+
+static inline void set_ac2_ep_map(struct ath6kl *ar,
+ u8 ac,
+ enum htc_endpoint_id ep)
+{
+ ar->ac2ep_map[ac] = ep;
+ ar->ep2ac_map[ep] = ac;
+}
+
+/* connect to a service */
+static int ath6kl_connectservice(struct ath6kl *ar,
+ struct htc_service_connect_req *con_req,
+ char *desc)
+{
+ int status;
+ struct htc_service_connect_resp response;
+
+ memset(&response, 0, sizeof(response));
+
+ status = ath6kl_htc_conn_service(ar->htc_target, con_req, &response);
+ if (status) {
+ ath6kl_err("failed to connect to %s service status:%d\n",
+ desc, status);
+ return status;
+ }
+
+ switch (con_req->svc_id) {
+ case WMI_CONTROL_SVC:
+ if (test_bit(WMI_ENABLED, &ar->flag))
+ ath6kl_wmi_set_control_ep(ar->wmi, response.endpoint);
+ ar->ctrl_ep = response.endpoint;
+ break;
+ case WMI_DATA_BE_SVC:
+ set_ac2_ep_map(ar, WMM_AC_BE, response.endpoint);
+ break;
+ case WMI_DATA_BK_SVC:
+ set_ac2_ep_map(ar, WMM_AC_BK, response.endpoint);
+ break;
+ case WMI_DATA_VI_SVC:
+ set_ac2_ep_map(ar, WMM_AC_VI, response.endpoint);
+ break;
+ case WMI_DATA_VO_SVC:
+ set_ac2_ep_map(ar, WMM_AC_VO, response.endpoint);
+ break;
+ default:
+ ath6kl_err("service id is not mapped %d\n", con_req->svc_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ath6kl_init_service_ep(struct ath6kl *ar)
+{
+ struct htc_service_connect_req connect;
+
+ memset(&connect, 0, sizeof(connect));
+
+ /* these fields are the same for all service endpoints */
+ connect.ep_cb.rx = ath6kl_rx;
+ connect.ep_cb.rx_refill = ath6kl_rx_refill;
+ connect.ep_cb.tx_full = ath6kl_tx_queue_full;
+
+ /*
+ * Set the max queue depth so that our ath6kl_tx_queue_full handler
+ * gets called.
+ */
+ connect.max_txq_depth = MAX_DEFAULT_SEND_QUEUE_DEPTH;
+ connect.ep_cb.rx_refill_thresh = ATH6KL_MAX_RX_BUFFERS / 4;
+ if (!connect.ep_cb.rx_refill_thresh)
+ connect.ep_cb.rx_refill_thresh++;
+
+ /* connect to control service */
+ connect.svc_id = WMI_CONTROL_SVC;
+ if (ath6kl_connectservice(ar, &connect, "WMI CONTROL"))
+ return -EIO;
+
+ connect.flags |= HTC_FLGS_TX_BNDL_PAD_EN;
+
+ /*
+ * Limit the HTC message size on the send path, although e can
+ * receive A-MSDU frames of 4K, we will only send ethernet-sized
+ * (802.3) frames on the send path.
+ */
+ connect.max_rxmsg_sz = WMI_MAX_TX_DATA_FRAME_LENGTH;
+
+ /*
+ * To reduce the amount of committed memory for larger A_MSDU
+ * frames, use the recv-alloc threshold mechanism for larger
+ * packets.
+ */
+ connect.ep_cb.rx_alloc_thresh = ATH6KL_BUFFER_SIZE;
+ connect.ep_cb.rx_allocthresh = ath6kl_alloc_amsdu_rxbuf;
+
+ /*
+ * For the remaining data services set the connection flag to
+ * reduce dribbling, if configured to do so.
+ */
+ connect.conn_flags |= HTC_CONN_FLGS_REDUCE_CRED_DRIB;
+ connect.conn_flags &= ~HTC_CONN_FLGS_THRESH_MASK;
+ connect.conn_flags |= HTC_CONN_FLGS_THRESH_LVL_HALF;
+
+ connect.svc_id = WMI_DATA_BE_SVC;
+
+ if (ath6kl_connectservice(ar, &connect, "WMI DATA BE"))
+ return -EIO;
+
+ /* connect to back-ground map this to WMI LOW_PRI */
+ connect.svc_id = WMI_DATA_BK_SVC;
+ if (ath6kl_connectservice(ar, &connect, "WMI DATA BK"))
+ return -EIO;
+
+ /* connect to Video service, map this to to HI PRI */
+ connect.svc_id = WMI_DATA_VI_SVC;
+ if (ath6kl_connectservice(ar, &connect, "WMI DATA VI"))
+ return -EIO;
+
+ /*
+ * Connect to VO service, this is currently not mapped to a WMI
+ * priority stream due to historical reasons. WMI originally
+ * defined 3 priorities over 3 mailboxes We can change this when
+ * WMI is reworked so that priorities are not dependent on
+ * mailboxes.
+ */
+ connect.svc_id = WMI_DATA_VO_SVC;
+ if (ath6kl_connectservice(ar, &connect, "WMI DATA VO"))
+ return -EIO;
+
+ return 0;
+}
+
+static void ath6kl_init_control_info(struct ath6kl *ar)
+{
+ u8 ctr;
+
+ clear_bit(WMI_ENABLED, &ar->flag);
+ ath6kl_init_profile_info(ar);
+ ar->def_txkey_index = 0;
+ memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list));
+ ar->ch_hint = 0;
+ ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL;
+ ar->listen_intvl_b = 0;
+ ar->tx_pwr = 0;
+ clear_bit(SKIP_SCAN, &ar->flag);
+ set_bit(WMM_ENABLED, &ar->flag);
+ ar->intra_bss = 1;
+ memset(&ar->sc_params, 0, sizeof(ar->sc_params));
+ ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT;
+ ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS;
+ ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
+
+ memset((u8 *)ar->sta_list, 0,
+ AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
+
+ spin_lock_init(&ar->mcastpsq_lock);
+
+ /* Init the PS queues */
+ for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
+ spin_lock_init(&ar->sta_list[ctr].psq_lock);
+ skb_queue_head_init(&ar->sta_list[ctr].psq);
+ }
+
+ skb_queue_head_init(&ar->mcastpsq);
+
+ memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3);
+}
+
+/*
+ * Set HTC/Mbox operational parameters, this can only be called when the
+ * target is in the BMI phase.
+ */
+static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val,
+ u8 htc_ctrl_buf)
+{
+ int status;
+ u32 blk_size;
+
+ blk_size = ar->mbox_info.block_size;
+
+ if (htc_ctrl_buf)
+ blk_size |= ((u32)htc_ctrl_buf) << 16;
+
+ /* set the host interest area for the block size */
+ status = ath6kl_bmi_write(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_mbox_io_block_sz)),
+ (u8 *)&blk_size,
+ 4);
+ if (status) {
+ ath6kl_err("bmi_write_memory for IO block size failed\n");
+ goto out;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "block size set: %d (target addr:0x%X)\n",
+ blk_size,
+ ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_mbox_io_block_sz)));
+
+ if (mbox_isr_yield_val) {
+ /* set the host interest area for the mbox ISR yield limit */
+ status = ath6kl_bmi_write(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_mbox_isr_yield_limit)),
+ (u8 *)&mbox_isr_yield_val,
+ 4);
+ if (status) {
+ ath6kl_err("bmi_write_memory for yield limit failed\n");
+ goto out;
+ }
+ }
+
+out:
+ return status;
+}
+
+#define REG_DUMP_COUNT_AR6003 60
+#define REGISTER_DUMP_LEN_MAX 60
+
+static void ath6kl_dump_target_assert_info(struct ath6kl *ar)
+{
+ u32 address;
+ u32 regdump_loc = 0;
+ int status;
+ u32 regdump_val[REGISTER_DUMP_LEN_MAX];
+ u32 i;
+
+ if (ar->target_type != TARGET_TYPE_AR6003)
+ return;
+
+ /* the reg dump pointer is copied to the host interest area */
+ address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state));
+ address = TARG_VTOP(ar->target_type, address);
+
+ /* read RAM location through diagnostic window */
+ status = ath6kl_diag_read32(ar, address, &regdump_loc);
+
+ if (status || !regdump_loc) {
+ ath6kl_err("failed to get ptr to register dump area\n");
+ return;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "location of register dump data: 0x%X\n",
+ regdump_loc);
+ regdump_loc = TARG_VTOP(ar->target_type, regdump_loc);
+
+ /* fetch register dump data */
+ status = ath6kl_diag_read(ar, regdump_loc, (u8 *)&regdump_val[0],
+ REG_DUMP_COUNT_AR6003 * (sizeof(u32)));
+
+ if (status) {
+ ath6kl_err("failed to get register dump\n");
+ return;
+ }
+ ath6kl_dbg(ATH6KL_DBG_TRC, "Register Dump:\n");
+
+ for (i = 0; i < REG_DUMP_COUNT_AR6003; i++)
+ ath6kl_dbg(ATH6KL_DBG_TRC, " %d : 0x%8.8X\n",
+ i, regdump_val[i]);
+
+}
+
+void ath6kl_target_failure(struct ath6kl *ar)
+{
+ ath6kl_err("target asserted\n");
+
+ /* try dumping target assertion information (if any) */
+ ath6kl_dump_target_assert_info(ar);
+
+}
+
+static int ath6kl_target_config_wlan_params(struct ath6kl *ar)
+{
+ int status = 0;
+ int ret;
+
+ /*
+ * Configure the device for rx dot11 header rules. "0,0" are the
+ * default values. Required if checksum offload is needed. Set
+ * RxMetaVersion to 2.
+ */
+ if (ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi,
+ ar->rx_meta_ver, 0, 0)) {
+ ath6kl_err("unable to set the rx frame format\n");
+ status = -EIO;
+ }
+
+ if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN)
+ if ((ath6kl_wmi_pmparams_cmd(ar->wmi, 0, 1, 0, 0, 1,
+ IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN)) != 0) {
+ ath6kl_err("unable to set power save fail event policy\n");
+ status = -EIO;
+ }
+
+ if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER))
+ if ((ath6kl_wmi_set_lpreamble_cmd(ar->wmi, 0,
+ WMI_DONOT_IGNORE_BARKER_IN_ERP)) != 0) {
+ ath6kl_err("unable to set barker preamble policy\n");
+ status = -EIO;
+ }
+
+ if (ath6kl_wmi_set_keepalive_cmd(ar->wmi,
+ WLAN_CONFIG_KEEP_ALIVE_INTERVAL)) {
+ ath6kl_err("unable to set keep alive interval\n");
+ status = -EIO;
+ }
+
+ if (ath6kl_wmi_disctimeout_cmd(ar->wmi,
+ WLAN_CONFIG_DISCONNECT_TIMEOUT)) {
+ ath6kl_err("unable to set disconnect timeout\n");
+ status = -EIO;
+ }
+
+ if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST))
+ if (ath6kl_wmi_set_wmm_txop(ar->wmi, WMI_TXOP_DISABLED)) {
+ ath6kl_err("unable to set txop bursting\n");
+ status = -EIO;
+ }
+
+ if (ar->p2p) {
+ ret = ath6kl_wmi_info_req_cmd(ar->wmi,
+ P2P_FLAG_CAPABILITIES_REQ |
+ P2P_FLAG_MACADDR_REQ |
+ P2P_FLAG_HMODEL_REQ);
+ if (ret) {
+ ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P "
+ "capabilities (%d) - assuming P2P not "
+ "supported\n", ret);
+ ar->p2p = 0;
+ }
+ }
+
+ if (ar->p2p) {
+ /* Enable Probe Request reporting for P2P */
+ ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, true);
+ if (ret) {
+ ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe "
+ "Request reporting (%d)\n", ret);
+ }
+ }
+
+ return status;
+}
+
+int ath6kl_configure_target(struct ath6kl *ar)
+{
+ u32 param, ram_reserved_size;
+ u8 fw_iftype;
+
+ fw_iftype = ath6kl_get_fw_iftype(ar);
+ if (fw_iftype == 0xff)
+ return -EINVAL;
+
+ /* Tell target which HTC version it is used*/
+ param = HTC_PROTOCOL_VERSION;
+ if (ath6kl_bmi_write(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_app_host_interest)),
+ (u8 *)&param, 4) != 0) {
+ ath6kl_err("bmi_write_memory for htc version failed\n");
+ return -EIO;
+ }
+
+ /* set the firmware mode to STA/IBSS/AP */
+ param = 0;
+
+ if (ath6kl_bmi_read(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_option_flag)),
+ (u8 *)&param, 4) != 0) {
+ ath6kl_err("bmi_read_memory for setting fwmode failed\n");
+ return -EIO;
+ }
+
+ param |= (1 << HI_OPTION_NUM_DEV_SHIFT);
+ param |= (fw_iftype << HI_OPTION_FW_MODE_SHIFT);
+ if (ar->p2p && fw_iftype == HI_OPTION_FW_MODE_BSS_STA) {
+ param |= HI_OPTION_FW_SUBMODE_P2PDEV <<
+ HI_OPTION_FW_SUBMODE_SHIFT;
+ }
+ param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT);
+ param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT);
+
+ if (ath6kl_bmi_write(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_option_flag)),
+ (u8 *)&param,
+ 4) != 0) {
+ ath6kl_err("bmi_write_memory for setting fwmode failed\n");
+ return -EIO;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "firmware mode set\n");
+
+ /*
+ * Hardcode the address use for the extended board data
+ * Ideally this should be pre-allocate by the OS at boot time
+ * But since it is a new feature and board data is loaded
+ * at init time, we have to workaround this from host.
+ * It is difficult to patch the firmware boot code,
+ * but possible in theory.
+ */
+
+ param = ar->hw.board_ext_data_addr;
+ ram_reserved_size = ar->hw.reserved_ram_size;
+
+ if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_board_ext_data)),
+ (u8 *)&param, 4) != 0) {
+ ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n");
+ return -EIO;
+ }
+
+ if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_end_ram_reserve_sz)),
+ (u8 *)&ram_reserved_size, 4) != 0) {
+ ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n");
+ return -EIO;
+ }
+
+ /* set the block size for the target */
+ if (ath6kl_set_htc_params(ar, MBOX_YIELD_LIMIT, 0))
+ /* use default number of control buffers */
+ return -EIO;
+
+ return 0;
+}
+
+struct ath6kl *ath6kl_core_alloc(struct device *sdev)
+{
+ struct net_device *dev;
+ struct ath6kl *ar;
+ struct wireless_dev *wdev;
+
+ wdev = ath6kl_cfg80211_init(sdev);
+ if (!wdev) {
+ ath6kl_err("ath6kl_cfg80211_init failed\n");
+ return NULL;
+ }
+
+ ar = wdev_priv(wdev);
+ ar->dev = sdev;
+ ar->wdev = wdev;
+ wdev->iftype = NL80211_IFTYPE_STATION;
+
+ if (ath6kl_debug_init(ar)) {
+ ath6kl_err("Failed to initialize debugfs\n");
+ ath6kl_cfg80211_deinit(ar);
+ return NULL;
+ }
+
+ dev = alloc_netdev(0, "wlan%d", ether_setup);
+ if (!dev) {
+ ath6kl_err("no memory for network device instance\n");
+ ath6kl_cfg80211_deinit(ar);
+ return NULL;
+ }
+
+ dev->ieee80211_ptr = wdev;
+ SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy));
+ wdev->netdev = dev;
+ ar->sme_state = SME_DISCONNECTED;
+
+ init_netdev(dev);
+
+ ar->net_dev = dev;
+ set_bit(WLAN_ENABLED, &ar->flag);
+
+ ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
+
+ spin_lock_init(&ar->lock);
+
+ ath6kl_init_control_info(ar);
+ init_waitqueue_head(&ar->event_wq);
+ sema_init(&ar->sem, 1);
+ clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
+
+ INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue);
+
+ setup_timer(&ar->disconnect_timer, disconnect_timer_handler,
+ (unsigned long) dev);
+
+ return ar;
+}
+
+int ath6kl_unavail_ev(struct ath6kl *ar)
+{
+ ath6kl_destroy(ar->net_dev, 1);
+
+ return 0;
+}
+
+/* firmware upload */
+static int ath6kl_get_fw(struct ath6kl *ar, const char *filename,
+ u8 **fw, size_t *fw_len)
+{
+ const struct firmware *fw_entry;
+ int ret;
+
+ ret = request_firmware(&fw_entry, filename, ar->dev);
+ if (ret)
+ return ret;
+
+ *fw_len = fw_entry->size;
+ *fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
+
+ if (*fw == NULL)
+ ret = -ENOMEM;
+
+ release_firmware(fw_entry);
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const char *get_target_ver_dir(const struct ath6kl *ar)
+{
+ switch (ar->version.target_ver) {
+ case AR6003_REV1_VERSION:
+ return "ath6k/AR6003/hw1.0";
+ case AR6003_REV2_VERSION:
+ return "ath6k/AR6003/hw2.0";
+ case AR6003_REV3_VERSION:
+ return "ath6k/AR6003/hw2.1.1";
+ }
+ ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__,
+ ar->version.target_ver);
+ return NULL;
+}
+
+/*
+ * Check the device tree for a board-id and use it to construct
+ * the pathname to the firmware file. Used (for now) to find a
+ * fallback to the "bdata.bin" file--typically a symlink to the
+ * appropriate board-specific file.
+ */
+static bool check_device_tree(struct ath6kl *ar)
+{
+ static const char *board_id_prop = "atheros,board-id";
+ struct device_node *node;
+ char board_filename[64];
+ const char *board_id;
+ int ret;
+
+ for_each_compatible_node(node, NULL, "atheros,ath6kl") {
+ board_id = of_get_property(node, board_id_prop, NULL);
+ if (board_id == NULL) {
+ ath6kl_warn("No \"%s\" property on %s node.\n",
+ board_id_prop, node->name);
+ continue;
+ }
+ snprintf(board_filename, sizeof(board_filename),
+ "%s/bdata.%s.bin", get_target_ver_dir(ar), board_id);
+
+ ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board,
+ &ar->fw_board_len);
+ if (ret) {
+ ath6kl_err("Failed to get DT board file %s: %d\n",
+ board_filename, ret);
+ continue;
+ }
+ return true;
+ }
+ return false;
+}
+#else
+static bool check_device_tree(struct ath6kl *ar)
+{
+ return false;
+}
+#endif /* CONFIG_OF */
+
+static int ath6kl_fetch_board_file(struct ath6kl *ar)
+{
+ const char *filename;
+ int ret;
+
+ if (ar->fw_board != NULL)
+ return 0;
+
+ switch (ar->version.target_ver) {
+ case AR6003_REV2_VERSION:
+ filename = AR6003_REV2_BOARD_DATA_FILE;
+ break;
+ case AR6004_REV1_VERSION:
+ filename = AR6004_REV1_BOARD_DATA_FILE;
+ break;
+ default:
+ filename = AR6003_REV3_BOARD_DATA_FILE;
+ break;
+ }
+
+ ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
+ &ar->fw_board_len);
+ if (ret == 0) {
+ /* managed to get proper board file */
+ return 0;
+ }
+
+ if (check_device_tree(ar)) {
+ /* got board file from device tree */
+ return 0;
+ }
+
+ /* there was no proper board file, try to use default instead */
+ ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n",
+ filename, ret);
+
+ switch (ar->version.target_ver) {
+ case AR6003_REV2_VERSION:
+ filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE;
+ break;
+ case AR6004_REV1_VERSION:
+ filename = AR6004_REV1_DEFAULT_BOARD_DATA_FILE;
+ break;
+ default:
+ filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE;
+ break;
+ }
+
+ ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
+ &ar->fw_board_len);
+ if (ret) {
+ ath6kl_err("Failed to get default board file %s: %d\n",
+ filename, ret);
+ return ret;
+ }
+
+ ath6kl_warn("WARNING! No proper board file was not found, instead using a default board file.\n");
+ ath6kl_warn("Most likely your hardware won't work as specified. Install correct board file!\n");
+
+ return 0;
+}
+
+static int ath6kl_fetch_otp_file(struct ath6kl *ar)
+{
+ const char *filename;
+ int ret;
+
+ if (ar->fw_otp != NULL)
+ return 0;
+
+ switch (ar->version.target_ver) {
+ case AR6003_REV2_VERSION:
+ filename = AR6003_REV2_OTP_FILE;
+ break;
+ case AR6004_REV1_VERSION:
+ ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n");
+ return 0;
+ break;
+ default:
+ filename = AR6003_REV3_OTP_FILE;
+ break;
+ }
+
+ ret = ath6kl_get_fw(ar, filename, &ar->fw_otp,
+ &ar->fw_otp_len);
+ if (ret) {
+ ath6kl_err("Failed to get OTP file %s: %d\n",
+ filename, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath6kl_fetch_fw_file(struct ath6kl *ar)
+{
+ const char *filename;
+ int ret;
+
+ if (ar->fw != NULL)
+ return 0;
+
+ if (testmode) {
+ switch (ar->version.target_ver) {
+ case AR6003_REV2_VERSION:
+ filename = AR6003_REV2_TCMD_FIRMWARE_FILE;
+ break;
+ case AR6003_REV3_VERSION:
+ filename = AR6003_REV3_TCMD_FIRMWARE_FILE;
+ break;
+ case AR6004_REV1_VERSION:
+ ath6kl_warn("testmode not supported with ar6004\n");
+ return -EOPNOTSUPP;
+ default:
+ ath6kl_warn("unknown target version: 0x%x\n",
+ ar->version.target_ver);
+ return -EINVAL;
+ }
+
+ set_bit(TESTMODE, &ar->flag);
+
+ goto get_fw;
+ }
+
+ switch (ar->version.target_ver) {
+ case AR6003_REV2_VERSION:
+ filename = AR6003_REV2_FIRMWARE_FILE;
+ break;
+ case AR6004_REV1_VERSION:
+ filename = AR6004_REV1_FIRMWARE_FILE;
+ break;
+ default:
+ filename = AR6003_REV3_FIRMWARE_FILE;
+ break;
+ }
+
+get_fw:
+ ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len);
+ if (ret) {
+ ath6kl_err("Failed to get firmware file %s: %d\n",
+ filename, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath6kl_fetch_patch_file(struct ath6kl *ar)
+{
+ const char *filename;
+ int ret;
+
+ switch (ar->version.target_ver) {
+ case AR6003_REV2_VERSION:
+ filename = AR6003_REV2_PATCH_FILE;
+ break;
+ case AR6004_REV1_VERSION:
+ /* FIXME: implement for AR6004 */
+ return 0;
+ break;
+ default:
+ filename = AR6003_REV3_PATCH_FILE;
+ break;
+ }
+
+ if (ar->fw_patch == NULL) {
+ ret = ath6kl_get_fw(ar, filename, &ar->fw_patch,
+ &ar->fw_patch_len);
+ if (ret) {
+ ath6kl_err("Failed to get patch file %s: %d\n",
+ filename, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ath6kl_fetch_fw_api1(struct ath6kl *ar)
+{
+ int ret;
+
+ ret = ath6kl_fetch_otp_file(ar);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_fetch_fw_file(ar);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_fetch_patch_file(ar);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ath6kl_fetch_fw_api2(struct ath6kl *ar)
+{
+ size_t magic_len, len, ie_len;
+ const struct firmware *fw;
+ struct ath6kl_fw_ie *hdr;
+ const char *filename;
+ const u8 *data;
+ int ret, ie_id, i, index, bit;
+ __le32 *val;
+
+ switch (ar->version.target_ver) {
+ case AR6003_REV2_VERSION:
+ filename = AR6003_REV2_FIRMWARE_2_FILE;
+ break;
+ case AR6003_REV3_VERSION:
+ filename = AR6003_REV3_FIRMWARE_2_FILE;
+ break;
+ case AR6004_REV1_VERSION:
+ filename = AR6004_REV1_FIRMWARE_2_FILE;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = request_firmware(&fw, filename, ar->dev);
+ if (ret)
+ return ret;
+
+ data = fw->data;
+ len = fw->size;
+
+ /* magic also includes the null byte, check that as well */
+ magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1;
+
+ if (len < magic_len) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ len -= magic_len;
+ data += magic_len;
+
+ /* loop elements */
+ while (len > sizeof(struct ath6kl_fw_ie)) {
+ /* hdr is unaligned! */
+ hdr = (struct ath6kl_fw_ie *) data;
+
+ ie_id = le32_to_cpup(&hdr->id);
+ ie_len = le32_to_cpup(&hdr->len);
+
+ len -= sizeof(*hdr);
+ data += sizeof(*hdr);
+
+ if (len < ie_len) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ switch (ie_id) {
+ case ATH6KL_FW_IE_OTP_IMAGE:
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n",
+ ie_len);
+
+ ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL);
+
+ if (ar->fw_otp == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ar->fw_otp_len = ie_len;
+ break;
+ case ATH6KL_FW_IE_FW_IMAGE:
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n",
+ ie_len);
+
+ ar->fw = kmemdup(data, ie_len, GFP_KERNEL);
+
+ if (ar->fw == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ar->fw_len = ie_len;
+ break;
+ case ATH6KL_FW_IE_PATCH_IMAGE:
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n",
+ ie_len);
+
+ ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL);
+
+ if (ar->fw_patch == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ar->fw_patch_len = ie_len;
+ break;
+ case ATH6KL_FW_IE_RESERVED_RAM_SIZE:
+ val = (__le32 *) data;
+ ar->hw.reserved_ram_size = le32_to_cpup(val);
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "found reserved ram size ie 0x%d\n",
+ ar->hw.reserved_ram_size);
+ break;
+ case ATH6KL_FW_IE_CAPABILITIES:
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "found firmware capabilities ie (%zd B)\n",
+ ie_len);
+
+ for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) {
+ index = ALIGN(i, 8) / 8;
+ bit = i % 8;
+
+ if (data[index] & (1 << bit))
+ __set_bit(i, ar->fw_capabilities);
+ }
+
+ ath6kl_dbg_dump(ATH6KL_DBG_BOOT, "capabilities", "",
+ ar->fw_capabilities,
+ sizeof(ar->fw_capabilities));
+ break;
+ case ATH6KL_FW_IE_PATCH_ADDR:
+ if (ie_len != sizeof(*val))
+ break;
+
+ val = (__le32 *) data;
+ ar->hw.dataset_patch_addr = le32_to_cpup(val);
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "found patch address ie 0x%d\n",
+ ar->hw.dataset_patch_addr);
+ break;
+ default:
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n",
+ le32_to_cpup(&hdr->id));
+ break;
+ }
+
+ len -= ie_len;
+ data += ie_len;
+ };
+
+ ret = 0;
+out:
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int ath6kl_fetch_firmwares(struct ath6kl *ar)
+{
+ int ret;
+
+ ret = ath6kl_fetch_board_file(ar);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_fetch_fw_api2(ar);
+ if (ret == 0) {
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 2\n");
+ return 0;
+ }
+
+ ret = ath6kl_fetch_fw_api1(ar);
+ if (ret)
+ return ret;
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 1\n");
+
+ return 0;
+}
+
+static int ath6kl_upload_board_file(struct ath6kl *ar)
+{
+ u32 board_address, board_ext_address, param;
+ u32 board_data_size, board_ext_data_size;
+ int ret;
+
+ if (WARN_ON(ar->fw_board == NULL))
+ return -ENOENT;
+
+ /*
+ * Determine where in Target RAM to write Board Data.
+ * For AR6004, host determine Target RAM address for
+ * writing board data.
+ */
+ if (ar->target_type == TARGET_TYPE_AR6004) {
+ board_address = AR6004_REV1_BOARD_DATA_ADDRESS;
+ ath6kl_bmi_write(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_board_data)),
+ (u8 *) &board_address, 4);
+ } else {
+ ath6kl_bmi_read(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_board_data)),
+ (u8 *) &board_address, 4);
+ }
+
+ /* determine where in target ram to write extended board data */
+ ath6kl_bmi_read(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_board_ext_data)),
+ (u8 *) &board_ext_address, 4);
+
+ if (board_ext_address == 0) {
+ ath6kl_err("Failed to get board file target address.\n");
+ return -EINVAL;
+ }
+
+ switch (ar->target_type) {
+ case TARGET_TYPE_AR6003:
+ board_data_size = AR6003_BOARD_DATA_SZ;
+ board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ;
+ break;
+ case TARGET_TYPE_AR6004:
+ board_data_size = AR6004_BOARD_DATA_SZ;
+ board_ext_data_size = AR6004_BOARD_EXT_DATA_SZ;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ break;
+ }
+
+ if (ar->fw_board_len == (board_data_size +
+ board_ext_data_size)) {
+
+ /* write extended board data */
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "writing extended board data to 0x%x (%d B)\n",
+ board_ext_address, board_ext_data_size);
+
+ ret = ath6kl_bmi_write(ar, board_ext_address,
+ ar->fw_board + board_data_size,
+ board_ext_data_size);
+ if (ret) {
+ ath6kl_err("Failed to write extended board data: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* record that extended board data is initialized */
+ param = (board_ext_data_size << 16) | 1;
+
+ ath6kl_bmi_write(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_board_ext_data_config)),
+ (unsigned char *) &param, 4);
+ }
+
+ if (ar->fw_board_len < board_data_size) {
+ ath6kl_err("Too small board file: %zu\n", ar->fw_board_len);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "writing board file to 0x%x (%d B)\n",
+ board_address, board_data_size);
+
+ ret = ath6kl_bmi_write(ar, board_address, ar->fw_board,
+ board_data_size);
+
+ if (ret) {
+ ath6kl_err("Board file bmi write failed: %d\n", ret);
+ return ret;
+ }
+
+ /* record the fact that Board Data IS initialized */
+ param = 1;
+ ath6kl_bmi_write(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_board_data_initialized)),
+ (u8 *)&param, 4);
+
+ return ret;
+}
+
+static int ath6kl_upload_otp(struct ath6kl *ar)
+{
+ u32 address, param;
+ int ret;
+
+ if (WARN_ON(ar->fw_otp == NULL))
+ return -ENOENT;
+
+ address = ar->hw.app_load_addr;
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%zd B)\n", address,
+ ar->fw_otp_len);
+
+ ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp,
+ ar->fw_otp_len);
+ if (ret) {
+ ath6kl_err("Failed to upload OTP file: %d\n", ret);
+ return ret;
+ }
+
+ /* read firmware start address */
+ ret = ath6kl_bmi_read(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_app_start)),
+ (u8 *) &address, sizeof(address));
+
+ if (ret) {
+ ath6kl_err("Failed to read hi_app_start: %d\n", ret);
+ return ret;
+ }
+
+ ar->hw.app_start_override_addr = address;
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr 0x%x\n",
+ ar->hw.app_start_override_addr);
+
+ /* execute the OTP code */
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", address);
+ param = 0;
+ ath6kl_bmi_execute(ar, address, &param);
+
+ return ret;
+}
+
+static int ath6kl_upload_firmware(struct ath6kl *ar)
+{
+ u32 address;
+ int ret;
+
+ if (WARN_ON(ar->fw == NULL))
+ return -ENOENT;
+
+ address = ar->hw.app_load_addr;
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%zd B)\n",
+ address, ar->fw_len);
+
+ ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len);
+
+ if (ret) {
+ ath6kl_err("Failed to write firmware: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Set starting address for firmware
+ * Don't need to setup app_start override addr on AR6004
+ */
+ if (ar->target_type != TARGET_TYPE_AR6004) {
+ address = ar->hw.app_start_override_addr;
+ ath6kl_bmi_set_app_start(ar, address);
+ }
+ return ret;
+}
+
+static int ath6kl_upload_patch(struct ath6kl *ar)
+{
+ u32 address, param;
+ int ret;
+
+ if (WARN_ON(ar->fw_patch == NULL))
+ return -ENOENT;
+
+ address = ar->hw.dataset_patch_addr;
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%zd B)\n",
+ address, ar->fw_patch_len);
+
+ ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len);
+ if (ret) {
+ ath6kl_err("Failed to write patch file: %d\n", ret);
+ return ret;
+ }
+
+ param = address;
+ ath6kl_bmi_write(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_dset_list_head)),
+ (unsigned char *) &param, 4);
+
+ return 0;
+}
+
+static int ath6kl_init_upload(struct ath6kl *ar)
+{
+ u32 param, options, sleep, address;
+ int status = 0;
+
+ if (ar->target_type != TARGET_TYPE_AR6003 &&
+ ar->target_type != TARGET_TYPE_AR6004)
+ return -EINVAL;
+
+ /* temporarily disable system sleep */
+ address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS;
+ status = ath6kl_bmi_reg_read(ar, address, &param);
+ if (status)
+ return status;
+
+ options = param;
+
+ param |= ATH6KL_OPTION_SLEEP_DISABLE;
+ status = ath6kl_bmi_reg_write(ar, address, param);
+ if (status)
+ return status;
+
+ address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS;
+ status = ath6kl_bmi_reg_read(ar, address, &param);
+ if (status)
+ return status;
+
+ sleep = param;
+
+ param |= SM(SYSTEM_SLEEP_DISABLE, 1);
+ status = ath6kl_bmi_reg_write(ar, address, param);
+ if (status)
+ return status;
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "old options: %d, old sleep: %d\n",
+ options, sleep);
+
+ /* program analog PLL register */
+ /* no need to control 40/44MHz clock on AR6004 */
+ if (ar->target_type != TARGET_TYPE_AR6004) {
+ status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER,
+ 0xF9104001);
+
+ if (status)
+ return status;
+
+ /* Run at 80/88MHz by default */
+ param = SM(CPU_CLOCK_STANDARD, 1);
+
+ address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS;
+ status = ath6kl_bmi_reg_write(ar, address, param);
+ if (status)
+ return status;
+ }
+
+ param = 0;
+ address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS;
+ param = SM(LPO_CAL_ENABLE, 1);
+ status = ath6kl_bmi_reg_write(ar, address, param);
+ if (status)
+ return status;
+
+ /* WAR to avoid SDIO CRC err */
+ if (ar->version.target_ver == AR6003_REV2_VERSION) {
+ ath6kl_err("temporary war to avoid sdio crc error\n");
+
+ param = 0x20;
+
+ address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS;
+ status = ath6kl_bmi_reg_write(ar, address, param);
+ if (status)
+ return status;
+
+ address = GPIO_BASE_ADDRESS + GPIO_PIN11_ADDRESS;
+ status = ath6kl_bmi_reg_write(ar, address, param);
+ if (status)
+ return status;
+
+ address = GPIO_BASE_ADDRESS + GPIO_PIN12_ADDRESS;
+ status = ath6kl_bmi_reg_write(ar, address, param);
+ if (status)
+ return status;
+
+ address = GPIO_BASE_ADDRESS + GPIO_PIN13_ADDRESS;
+ status = ath6kl_bmi_reg_write(ar, address, param);
+ if (status)
+ return status;
+ }
+
+ /* write EEPROM data to Target RAM */
+ status = ath6kl_upload_board_file(ar);
+ if (status)
+ return status;
+
+ /* transfer One time Programmable data */
+ status = ath6kl_upload_otp(ar);
+ if (status)
+ return status;
+
+ /* Download Target firmware */
+ status = ath6kl_upload_firmware(ar);
+ if (status)
+ return status;
+
+ status = ath6kl_upload_patch(ar);
+ if (status)
+ return status;
+
+ /* Restore system sleep */
+ address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS;
+ status = ath6kl_bmi_reg_write(ar, address, sleep);
+ if (status)
+ return status;
+
+ address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS;
+ param = options | 0x20;
+ status = ath6kl_bmi_reg_write(ar, address, param);
+ if (status)
+ return status;
+
+ /* Configure GPIO AR6003 UART */
+ param = CONFIG_AR600x_DEBUG_UART_TX_PIN;
+ status = ath6kl_bmi_write(ar,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_dbg_uart_txpin)),
+ (u8 *)&param, 4);
+
+ return status;
+}
+
+static int ath6kl_init_hw_params(struct ath6kl *ar)
+{
+ switch (ar->version.target_ver) {
+ case AR6003_REV2_VERSION:
+ ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
+ ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS;
+ ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS;
+ ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE;
+ break;
+ case AR6003_REV3_VERSION:
+ ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS;
+ ar->hw.app_load_addr = 0x1234;
+ ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS;
+ ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE;
+ break;
+ case AR6004_REV1_VERSION:
+ ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
+ ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS;
+ ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS;
+ ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE;
+ break;
+ default:
+ ath6kl_err("Unsupported hardware version: 0x%x\n",
+ ar->version.target_ver);
+ return -EINVAL;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n",
+ ar->version.target_ver, ar->target_type,
+ ar->hw.dataset_patch_addr, ar->hw.app_load_addr);
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x",
+ ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr,
+ ar->hw.reserved_ram_size);
+
+ return 0;
+}
+
+static int ath6kl_init(struct net_device *dev)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ int status = 0;
+ s32 timeleft;
+
+ if (!ar)
+ return -EIO;
+
+ /* Do we need to finish the BMI phase */
+ if (ath6kl_bmi_done(ar)) {
+ status = -EIO;
+ goto ath6kl_init_done;
+ }
+
+ /* Indicate that WMI is enabled (although not ready yet) */
+ set_bit(WMI_ENABLED, &ar->flag);
+ ar->wmi = ath6kl_wmi_init(ar);
+ if (!ar->wmi) {
+ ath6kl_err("failed to initialize wmi\n");
+ status = -EIO;
+ goto ath6kl_init_done;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi);
+
+ /*
+ * The reason we have to wait for the target here is that the
+ * driver layer has to init BMI in order to set the host block
+ * size.
+ */
+ if (ath6kl_htc_wait_target(ar->htc_target)) {
+ status = -EIO;
+ goto err_node_cleanup;
+ }
+
+ if (ath6kl_init_service_ep(ar)) {
+ status = -EIO;
+ goto err_cleanup_scatter;
+ }
+
+ /* setup access class priority mappings */
+ ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */
+ ar->ac_stream_pri_map[WMM_AC_BE] = 1;
+ ar->ac_stream_pri_map[WMM_AC_VI] = 2;
+ ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */
+
+ /* give our connected endpoints some buffers */
+ ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep);
+ ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]);
+
+ /* allocate some buffers that handle larger AMSDU frames */
+ ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS);
+
+ /* setup credit distribution */
+ ath6k_setup_credit_dist(ar->htc_target, &ar->credit_state_info);
+
+ ath6kl_cookie_init(ar);
+
+ /* start HTC */
+ status = ath6kl_htc_start(ar->htc_target);
+
+ if (status) {
+ ath6kl_cookie_cleanup(ar);
+ goto err_rxbuf_cleanup;
+ }
+
+ /* Wait for Wmi event to be ready */
+ timeleft = wait_event_interruptible_timeout(ar->event_wq,
+ test_bit(WMI_READY,
+ &ar->flag),
+ WMI_TIMEOUT);
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n");
+
+ if (ar->version.abi_ver != ATH6KL_ABI_VERSION) {
+ ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n",
+ ATH6KL_ABI_VERSION, ar->version.abi_ver);
+ status = -EIO;
+ goto err_htc_stop;
+ }
+
+ if (!timeleft || signal_pending(current)) {
+ ath6kl_err("wmi is not ready or wait was interrupted\n");
+ status = -EIO;
+ goto err_htc_stop;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__);
+
+ /* communicate the wmi protocol verision to the target */
+ if ((ath6kl_set_host_app_area(ar)) != 0)
+ ath6kl_err("unable to set the host app area\n");
+
+ ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER |
+ ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST;
+
+ ar->wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
+
+ status = ath6kl_target_config_wlan_params(ar);
+ if (!status)
+ goto ath6kl_init_done;
+
+err_htc_stop:
+ ath6kl_htc_stop(ar->htc_target);
+err_rxbuf_cleanup:
+ ath6kl_htc_flush_rx_buf(ar->htc_target);
+ ath6kl_cleanup_amsdu_rxbufs(ar);
+err_cleanup_scatter:
+ ath6kl_hif_cleanup_scatter(ar);
+err_node_cleanup:
+ ath6kl_wmi_shutdown(ar->wmi);
+ clear_bit(WMI_ENABLED, &ar->flag);
+ ar->wmi = NULL;
+
+ath6kl_init_done:
+ return status;
+}
+
+int ath6kl_core_init(struct ath6kl *ar)
+{
+ int ret = 0;
+ struct ath6kl_bmi_target_info targ_info;
+
+ ar->ath6kl_wq = create_singlethread_workqueue("ath6kl");
+ if (!ar->ath6kl_wq)
+ return -ENOMEM;
+
+ ret = ath6kl_bmi_init(ar);
+ if (ret)
+ goto err_wq;
+
+ ret = ath6kl_bmi_get_target_info(ar, &targ_info);
+ if (ret)
+ goto err_bmi_cleanup;
+
+ ar->version.target_ver = le32_to_cpu(targ_info.version);
+ ar->target_type = le32_to_cpu(targ_info.type);
+ ar->wdev->wiphy->hw_version = le32_to_cpu(targ_info.version);
+
+ ret = ath6kl_init_hw_params(ar);
+ if (ret)
+ goto err_bmi_cleanup;
+
+ ret = ath6kl_configure_target(ar);
+ if (ret)
+ goto err_bmi_cleanup;
+
+ ar->htc_target = ath6kl_htc_create(ar);
+
+ if (!ar->htc_target) {
+ ret = -ENOMEM;
+ goto err_bmi_cleanup;
+ }
+
+ ar->aggr_cntxt = aggr_init(ar->net_dev);
+ if (!ar->aggr_cntxt) {
+ ath6kl_err("failed to initialize aggr\n");
+ ret = -ENOMEM;
+ goto err_htc_cleanup;
+ }
+
+ ret = ath6kl_fetch_firmwares(ar);
+ if (ret)
+ goto err_htc_cleanup;
+
+ ret = ath6kl_init_upload(ar);
+ if (ret)
+ goto err_htc_cleanup;
+
+ ret = ath6kl_init(ar->net_dev);
+ if (ret)
+ goto err_htc_cleanup;
+
+ /* This runs the init function if registered */
+ ret = register_netdev(ar->net_dev);
+ if (ret) {
+ ath6kl_err("register_netdev failed\n");
+ ath6kl_destroy(ar->net_dev, 0);
+ return ret;
+ }
+
+ set_bit(NETDEV_REGISTERED, &ar->flag);
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n",
+ __func__, ar->net_dev->name, ar->net_dev, ar);
+
+ return ret;
+
+err_htc_cleanup:
+ ath6kl_htc_cleanup(ar->htc_target);
+err_bmi_cleanup:
+ ath6kl_bmi_cleanup(ar);
+err_wq:
+ destroy_workqueue(ar->ath6kl_wq);
+ return ret;
+}
+
+void ath6kl_stop_txrx(struct ath6kl *ar)
+{
+ struct net_device *ndev = ar->net_dev;
+
+ if (!ndev)
+ return;
+
+ set_bit(DESTROY_IN_PROGRESS, &ar->flag);
+
+ if (down_interruptible(&ar->sem)) {
+ ath6kl_err("down_interruptible failed\n");
+ return;
+ }
+
+ if (ar->wlan_pwr_state != WLAN_POWER_STATE_CUT_PWR)
+ ath6kl_stop_endpoint(ndev, false, true);
+
+ clear_bit(WLAN_ENABLED, &ar->flag);
+}
+
+/*
+ * We need to differentiate between the surprise and planned removal of the
+ * device because of the following consideration:
+ *
+ * - In case of surprise removal, the hcd already frees up the pending
+ * for the device and hence there is no need to unregister the function
+ * driver inorder to get these requests. For planned removal, the function
+ * driver has to explicitly unregister itself to have the hcd return all the
+ * pending requests before the data structures for the devices are freed up.
+ * Note that as per the current implementation, the function driver will
+ * end up releasing all the devices since there is no API to selectively
+ * release a particular device.
+ *
+ * - Certain commands issued to the target can be skipped for surprise
+ * removal since they will anyway not go through.
+ */
+void ath6kl_destroy(struct net_device *dev, unsigned int unregister)
+{
+ struct ath6kl *ar;
+
+ if (!dev || !ath6kl_priv(dev)) {
+ ath6kl_err("failed to get device structure\n");
+ return;
+ }
+
+ ar = ath6kl_priv(dev);
+
+ destroy_workqueue(ar->ath6kl_wq);
+
+ if (ar->htc_target)
+ ath6kl_htc_cleanup(ar->htc_target);
+
+ aggr_module_destroy(ar->aggr_cntxt);
+
+ ath6kl_cookie_cleanup(ar);
+
+ ath6kl_cleanup_amsdu_rxbufs(ar);
+
+ ath6kl_bmi_cleanup(ar);
+
+ ath6kl_debug_cleanup(ar);
+
+ if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) {
+ unregister_netdev(dev);
+ clear_bit(NETDEV_REGISTERED, &ar->flag);
+ }
+
+ free_netdev(dev);
+
+ kfree(ar->fw_board);
+ kfree(ar->fw_otp);
+ kfree(ar->fw);
+ kfree(ar->fw_patch);
+
+ ath6kl_cfg80211_deinit(ar);
+}
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
new file mode 100644
index 000000000000..30b5a53db9ed
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -0,0 +1,1477 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "hif-ops.h"
+#include "cfg80211.h"
+#include "target.h"
+#include "debug.h"
+
+struct ath6kl_sta *ath6kl_find_sta(struct ath6kl *ar, u8 *node_addr)
+{
+ struct ath6kl_sta *conn = NULL;
+ u8 i, max_conn;
+
+ max_conn = (ar->nw_type == AP_NETWORK) ? AP_MAX_NUM_STA : 0;
+
+ for (i = 0; i < max_conn; i++) {
+ if (memcmp(node_addr, ar->sta_list[i].mac, ETH_ALEN) == 0) {
+ conn = &ar->sta_list[i];
+ break;
+ }
+ }
+
+ return conn;
+}
+
+struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid)
+{
+ struct ath6kl_sta *conn = NULL;
+ u8 ctr;
+
+ for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
+ if (ar->sta_list[ctr].aid == aid) {
+ conn = &ar->sta_list[ctr];
+ break;
+ }
+ }
+ return conn;
+}
+
+static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie,
+ u8 ielen, u8 keymgmt, u8 ucipher, u8 auth)
+{
+ struct ath6kl_sta *sta;
+ u8 free_slot;
+
+ free_slot = aid - 1;
+
+ sta = &ar->sta_list[free_slot];
+ memcpy(sta->mac, mac, ETH_ALEN);
+ if (ielen <= ATH6KL_MAX_IE)
+ memcpy(sta->wpa_ie, wpaie, ielen);
+ sta->aid = aid;
+ sta->keymgmt = keymgmt;
+ sta->ucipher = ucipher;
+ sta->auth = auth;
+
+ ar->sta_list_index = ar->sta_list_index | (1 << free_slot);
+ ar->ap_stats.sta[free_slot].aid = cpu_to_le32(aid);
+}
+
+static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
+{
+ struct ath6kl_sta *sta = &ar->sta_list[i];
+
+ /* empty the queued pkts in the PS queue if any */
+ spin_lock_bh(&sta->psq_lock);
+ skb_queue_purge(&sta->psq);
+ spin_unlock_bh(&sta->psq_lock);
+
+ memset(&ar->ap_stats.sta[sta->aid - 1], 0,
+ sizeof(struct wmi_per_sta_stat));
+ memset(sta->mac, 0, ETH_ALEN);
+ memset(sta->wpa_ie, 0, ATH6KL_MAX_IE);
+ sta->aid = 0;
+ sta->sta_flags = 0;
+
+ ar->sta_list_index = ar->sta_list_index & ~(1 << i);
+
+}
+
+static u8 ath6kl_remove_sta(struct ath6kl *ar, u8 *mac, u16 reason)
+{
+ u8 i, removed = 0;
+
+ if (is_zero_ether_addr(mac))
+ return removed;
+
+ if (is_broadcast_ether_addr(mac)) {
+ ath6kl_dbg(ATH6KL_DBG_TRC, "deleting all station\n");
+
+ for (i = 0; i < AP_MAX_NUM_STA; i++) {
+ if (!is_zero_ether_addr(ar->sta_list[i].mac)) {
+ ath6kl_sta_cleanup(ar, i);
+ removed = 1;
+ }
+ }
+ } else {
+ for (i = 0; i < AP_MAX_NUM_STA; i++) {
+ if (memcmp(ar->sta_list[i].mac, mac, ETH_ALEN) == 0) {
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "deleting station %pM aid=%d reason=%d\n",
+ mac, ar->sta_list[i].aid, reason);
+ ath6kl_sta_cleanup(ar, i);
+ removed = 1;
+ break;
+ }
+ }
+ }
+
+ return removed;
+}
+
+enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac)
+{
+ struct ath6kl *ar = devt;
+ return ar->ac2ep_map[ac];
+}
+
+struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar)
+{
+ struct ath6kl_cookie *cookie;
+
+ cookie = ar->cookie_list;
+ if (cookie != NULL) {
+ ar->cookie_list = cookie->arc_list_next;
+ ar->cookie_count--;
+ }
+
+ return cookie;
+}
+
+void ath6kl_cookie_init(struct ath6kl *ar)
+{
+ u32 i;
+
+ ar->cookie_list = NULL;
+ ar->cookie_count = 0;
+
+ memset(ar->cookie_mem, 0, sizeof(ar->cookie_mem));
+
+ for (i = 0; i < MAX_COOKIE_NUM; i++)
+ ath6kl_free_cookie(ar, &ar->cookie_mem[i]);
+}
+
+void ath6kl_cookie_cleanup(struct ath6kl *ar)
+{
+ ar->cookie_list = NULL;
+ ar->cookie_count = 0;
+}
+
+void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie)
+{
+ /* Insert first */
+
+ if (!ar || !cookie)
+ return;
+
+ cookie->arc_list_next = ar->cookie_list;
+ ar->cookie_list = cookie;
+ ar->cookie_count++;
+}
+
+/* set the window address register (using 4-byte register access ). */
+static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
+{
+ int status;
+ s32 i;
+ __le32 addr_val;
+
+ /*
+ * Write bytes 1,2,3 of the register to set the upper address bytes,
+ * the LSB is written last to initiate the access cycle
+ */
+
+ for (i = 1; i <= 3; i++) {
+ /*
+ * Fill the buffer with the address byte value we want to
+ * hit 4 times. No need to worry about endianness as the
+ * same byte is copied to all four bytes of addr_val at
+ * any time.
+ */
+ memset((u8 *)&addr_val, ((u8 *)&addr)[i], 4);
+
+ /*
+ * Hit each byte of the register address with a 4-byte
+ * write operation to the same address, this is a harmless
+ * operation.
+ */
+ status = hif_read_write_sync(ar, reg_addr + i, (u8 *)&addr_val,
+ 4, HIF_WR_SYNC_BYTE_FIX);
+ if (status)
+ break;
+ }
+
+ if (status) {
+ ath6kl_err("failed to write initial bytes of 0x%x to window reg: 0x%X\n",
+ addr, reg_addr);
+ return status;
+ }
+
+ /*
+ * Write the address register again, this time write the whole
+ * 4-byte value. The effect here is that the LSB write causes the
+ * cycle to start, the extra 3 byte write to bytes 1,2,3 has no
+ * effect since we are writing the same values again
+ */
+ addr_val = cpu_to_le32(addr);
+ status = hif_read_write_sync(ar, reg_addr,
+ (u8 *)&(addr_val),
+ 4, HIF_WR_SYNC_BYTE_INC);
+
+ if (status) {
+ ath6kl_err("failed to write 0x%x to window reg: 0x%X\n",
+ addr, reg_addr);
+ return status;
+ }
+
+ return 0;
+}
+
+/*
+ * Read from the hardware through its diagnostic window. No cooperation
+ * from the firmware is required for this.
+ */
+int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value)
+{
+ int ret;
+
+ /* set window register to start read cycle */
+ ret = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, address);
+ if (ret)
+ return ret;
+
+ /* read the data */
+ ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) value,
+ sizeof(*value), HIF_RD_SYNC_BYTE_INC);
+ if (ret) {
+ ath6kl_warn("failed to read32 through diagnose window: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Write to the ATH6KL through its diagnostic window. No cooperation from
+ * the Target is required for this.
+ */
+int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value)
+{
+ int ret;
+
+ /* set write data */
+ ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) &value,
+ sizeof(value), HIF_WR_SYNC_BYTE_INC);
+ if (ret) {
+ ath6kl_err("failed to write 0x%x during diagnose window to 0x%d\n",
+ address, value);
+ return ret;
+ }
+
+ /* set window register, which starts the write cycle */
+ return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS,
+ address);
+}
+
+int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length)
+{
+ u32 count, *buf = data;
+ int ret;
+
+ if (WARN_ON(length % 4))
+ return -EINVAL;
+
+ for (count = 0; count < length / 4; count++, address += 4) {
+ ret = ath6kl_diag_read32(ar, address, &buf[count]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length)
+{
+ u32 count;
+ __le32 *buf = data;
+ int ret;
+
+ if (WARN_ON(length % 4))
+ return -EINVAL;
+
+ for (count = 0; count < length / 4; count++, address += 4) {
+ ret = ath6kl_diag_write32(ar, address, buf[count]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int ath6kl_read_fwlogs(struct ath6kl *ar)
+{
+ struct ath6kl_dbglog_hdr debug_hdr;
+ struct ath6kl_dbglog_buf debug_buf;
+ u32 address, length, dropped, firstbuf, debug_hdr_addr;
+ int ret = 0, loop;
+ u8 *buf;
+
+ buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ address = TARG_VTOP(ar->target_type,
+ ath6kl_get_hi_item_addr(ar,
+ HI_ITEM(hi_dbglog_hdr)));
+
+ ret = ath6kl_diag_read32(ar, address, &debug_hdr_addr);
+ if (ret)
+ goto out;
+
+ /* Get the contents of the ring buffer */
+ if (debug_hdr_addr == 0) {
+ ath6kl_warn("Invalid address for debug_hdr_addr\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ address = TARG_VTOP(ar->target_type, debug_hdr_addr);
+ ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr));
+
+ address = TARG_VTOP(ar->target_type,
+ le32_to_cpu(debug_hdr.dbuf_addr));
+ firstbuf = address;
+ dropped = le32_to_cpu(debug_hdr.dropped);
+ ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
+
+ loop = 100;
+
+ do {
+ address = TARG_VTOP(ar->target_type,
+ le32_to_cpu(debug_buf.buffer_addr));
+ length = le32_to_cpu(debug_buf.length);
+
+ if (length != 0 && (le32_to_cpu(debug_buf.length) <=
+ le32_to_cpu(debug_buf.bufsize))) {
+ length = ALIGN(length, 4);
+
+ ret = ath6kl_diag_read(ar, address,
+ buf, length);
+ if (ret)
+ goto out;
+
+ ath6kl_debug_fwlog_event(ar, buf, length);
+ }
+
+ address = TARG_VTOP(ar->target_type,
+ le32_to_cpu(debug_buf.next));
+ ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
+ if (ret)
+ goto out;
+
+ loop--;
+
+ if (WARN_ON(loop == 0)) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+ } while (address != firstbuf);
+
+out:
+ kfree(buf);
+
+ return ret;
+}
+
+/* FIXME: move to a better place, target.h? */
+#define AR6003_RESET_CONTROL_ADDRESS 0x00004000
+#define AR6004_RESET_CONTROL_ADDRESS 0x00004000
+
+static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type,
+ bool wait_fot_compltn, bool cold_reset)
+{
+ int status = 0;
+ u32 address;
+ __le32 data;
+
+ if (target_type != TARGET_TYPE_AR6003 &&
+ target_type != TARGET_TYPE_AR6004)
+ return;
+
+ data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) :
+ cpu_to_le32(RESET_CONTROL_MBOX_RST);
+
+ switch (target_type) {
+ case TARGET_TYPE_AR6003:
+ address = AR6003_RESET_CONTROL_ADDRESS;
+ break;
+ case TARGET_TYPE_AR6004:
+ address = AR6004_RESET_CONTROL_ADDRESS;
+ break;
+ default:
+ address = AR6003_RESET_CONTROL_ADDRESS;
+ break;
+ }
+
+ status = ath6kl_diag_write32(ar, address, data);
+
+ if (status)
+ ath6kl_err("failed to reset target\n");
+}
+
+void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile,
+ bool get_dbglogs)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ static u8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ bool discon_issued;
+
+ netif_stop_queue(dev);
+
+ /* disable the target and the interrupts associated with it */
+ if (test_bit(WMI_READY, &ar->flag)) {
+ discon_issued = (test_bit(CONNECTED, &ar->flag) ||
+ test_bit(CONNECT_PEND, &ar->flag));
+ ath6kl_disconnect(ar);
+ if (!keep_profile)
+ ath6kl_init_profile_info(ar);
+
+ del_timer(&ar->disconnect_timer);
+
+ clear_bit(WMI_READY, &ar->flag);
+ ath6kl_wmi_shutdown(ar->wmi);
+ clear_bit(WMI_ENABLED, &ar->flag);
+ ar->wmi = NULL;
+
+ /*
+ * After wmi_shudown all WMI events will be dropped. We
+ * need to cleanup the buffers allocated in AP mode and
+ * give disconnect notification to stack, which usually
+ * happens in the disconnect_event. Simulate the disconnect
+ * event by calling the function directly. Sometimes
+ * disconnect_event will be received when the debug logs
+ * are collected.
+ */
+ if (discon_issued)
+ ath6kl_disconnect_event(ar, DISCONNECT_CMD,
+ (ar->nw_type & AP_NETWORK) ?
+ bcast_mac : ar->bssid,
+ 0, NULL, 0);
+
+ ar->user_key_ctrl = 0;
+
+ } else {
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "%s: wmi is not ready 0x%p 0x%p\n",
+ __func__, ar, ar->wmi);
+
+ /* Shut down WMI if we have started it */
+ if (test_bit(WMI_ENABLED, &ar->flag)) {
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "%s: shut down wmi\n", __func__);
+ ath6kl_wmi_shutdown(ar->wmi);
+ clear_bit(WMI_ENABLED, &ar->flag);
+ ar->wmi = NULL;
+ }
+ }
+
+ if (ar->htc_target) {
+ ath6kl_dbg(ATH6KL_DBG_TRC, "%s: shut down htc\n", __func__);
+ ath6kl_htc_stop(ar->htc_target);
+ }
+
+ /*
+ * Try to reset the device if we can. The driver may have been
+ * configure NOT to reset the target during a debug session.
+ */
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "attempting to reset target on instance destroy\n");
+ ath6kl_reset_device(ar, ar->target_type, true, true);
+}
+
+static void ath6kl_install_static_wep_keys(struct ath6kl *ar)
+{
+ u8 index;
+ u8 keyusage;
+
+ for (index = WMI_MIN_KEY_INDEX; index <= WMI_MAX_KEY_INDEX; index++) {
+ if (ar->wep_key_list[index].key_len) {
+ keyusage = GROUP_USAGE;
+ if (index == ar->def_txkey_index)
+ keyusage |= TX_USAGE;
+
+ ath6kl_wmi_addkey_cmd(ar->wmi,
+ index,
+ WEP_CRYPT,
+ keyusage,
+ ar->wep_key_list[index].key_len,
+ NULL,
+ ar->wep_key_list[index].key,
+ KEY_OP_INIT_VAL, NULL,
+ NO_SYNC_WMIFLAG);
+ }
+ }
+}
+
+void ath6kl_connect_ap_mode_bss(struct ath6kl *ar, u16 channel)
+{
+ struct ath6kl_req_key *ik;
+ int res;
+ u8 key_rsc[ATH6KL_KEY_SEQ_LEN];
+
+ ik = &ar->ap_mode_bkey;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", channel);
+
+ switch (ar->auth_mode) {
+ case NONE_AUTH:
+ if (ar->prwise_crypto == WEP_CRYPT)
+ ath6kl_install_static_wep_keys(ar);
+ break;
+ case WPA_PSK_AUTH:
+ case WPA2_PSK_AUTH:
+ case (WPA_PSK_AUTH | WPA2_PSK_AUTH):
+ if (!ik->valid)
+ break;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for "
+ "the initial group key for AP mode\n");
+ memset(key_rsc, 0, sizeof(key_rsc));
+ res = ath6kl_wmi_addkey_cmd(
+ ar->wmi, ik->key_index, ik->key_type,
+ GROUP_USAGE, ik->key_len, key_rsc, ik->key,
+ KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG);
+ if (res) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed "
+ "addkey failed: %d\n", res);
+ }
+ break;
+ }
+
+ ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+ set_bit(CONNECTED, &ar->flag);
+ netif_carrier_on(ar->net_dev);
+}
+
+void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr,
+ u8 keymgmt, u8 ucipher, u8 auth,
+ u8 assoc_req_len, u8 *assoc_info)
+{
+ u8 *ies = NULL, *wpa_ie = NULL, *pos;
+ size_t ies_len = 0;
+ struct station_info sinfo;
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", mac_addr, aid);
+
+ if (assoc_req_len > sizeof(struct ieee80211_hdr_3addr)) {
+ struct ieee80211_mgmt *mgmt =
+ (struct ieee80211_mgmt *) assoc_info;
+ if (ieee80211_is_assoc_req(mgmt->frame_control) &&
+ assoc_req_len >= sizeof(struct ieee80211_hdr_3addr) +
+ sizeof(mgmt->u.assoc_req)) {
+ ies = mgmt->u.assoc_req.variable;
+ ies_len = assoc_info + assoc_req_len - ies;
+ } else if (ieee80211_is_reassoc_req(mgmt->frame_control) &&
+ assoc_req_len >= sizeof(struct ieee80211_hdr_3addr)
+ + sizeof(mgmt->u.reassoc_req)) {
+ ies = mgmt->u.reassoc_req.variable;
+ ies_len = assoc_info + assoc_req_len - ies;
+ }
+ }
+
+ pos = ies;
+ while (pos && pos + 1 < ies + ies_len) {
+ if (pos + 2 + pos[1] > ies + ies_len)
+ break;
+ if (pos[0] == WLAN_EID_RSN)
+ wpa_ie = pos; /* RSN IE */
+ else if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
+ pos[1] >= 4 &&
+ pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2) {
+ if (pos[5] == 0x01)
+ wpa_ie = pos; /* WPA IE */
+ else if (pos[5] == 0x04) {
+ wpa_ie = pos; /* WPS IE */
+ break; /* overrides WPA/RSN IE */
+ }
+ }
+ pos += 2 + pos[1];
+ }
+
+ ath6kl_add_new_sta(ar, mac_addr, aid, wpa_ie,
+ wpa_ie ? 2 + wpa_ie[1] : 0,
+ keymgmt, ucipher, auth);
+
+ /* send event to application */
+ memset(&sinfo, 0, sizeof(sinfo));
+
+ /* TODO: sinfo.generation */
+
+ sinfo.assoc_req_ies = ies;
+ sinfo.assoc_req_ies_len = ies_len;
+ sinfo.filled |= STATION_INFO_ASSOC_REQ_IES;
+
+ cfg80211_new_sta(ar->net_dev, mac_addr, &sinfo, GFP_KERNEL);
+
+ netif_wake_queue(ar->net_dev);
+}
+
+/* Functions for Tx credit handling */
+void ath6k_credit_init(struct htc_credit_state_info *cred_info,
+ struct list_head *ep_list,
+ int tot_credits)
+{
+ struct htc_endpoint_credit_dist *cur_ep_dist;
+ int count;
+
+ cred_info->cur_free_credits = tot_credits;
+ cred_info->total_avail_credits = tot_credits;
+
+ list_for_each_entry(cur_ep_dist, ep_list, list) {
+ if (cur_ep_dist->endpoint == ENDPOINT_0)
+ continue;
+
+ cur_ep_dist->cred_min = cur_ep_dist->cred_per_msg;
+
+ if (tot_credits > 4)
+ if ((cur_ep_dist->svc_id == WMI_DATA_BK_SVC) ||
+ (cur_ep_dist->svc_id == WMI_DATA_BE_SVC)) {
+ ath6kl_deposit_credit_to_ep(cred_info,
+ cur_ep_dist,
+ cur_ep_dist->cred_min);
+ cur_ep_dist->dist_flags |= HTC_EP_ACTIVE;
+ }
+
+ if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) {
+ ath6kl_deposit_credit_to_ep(cred_info, cur_ep_dist,
+ cur_ep_dist->cred_min);
+ /*
+ * Control service is always marked active, it
+ * never goes inactive EVER.
+ */
+ cur_ep_dist->dist_flags |= HTC_EP_ACTIVE;
+ } else if (cur_ep_dist->svc_id == WMI_DATA_BK_SVC)
+ /* this is the lowest priority data endpoint */
+ cred_info->lowestpri_ep_dist = cur_ep_dist->list;
+
+ /*
+ * Streams have to be created (explicit | implicit) for all
+ * kinds of traffic. BE endpoints are also inactive in the
+ * beginning. When BE traffic starts it creates implicit
+ * streams that redistributes credits.
+ *
+ * Note: all other endpoints have minimums set but are
+ * initially given NO credits. credits will be distributed
+ * as traffic activity demands
+ */
+ }
+
+ WARN_ON(cred_info->cur_free_credits <= 0);
+
+ list_for_each_entry(cur_ep_dist, ep_list, list) {
+ if (cur_ep_dist->endpoint == ENDPOINT_0)
+ continue;
+
+ if (cur_ep_dist->svc_id == WMI_CONTROL_SVC)
+ cur_ep_dist->cred_norm = cur_ep_dist->cred_per_msg;
+ else {
+ /*
+ * For the remaining data endpoints, we assume that
+ * each cred_per_msg are the same. We use a simple
+ * calculation here, we take the remaining credits
+ * and determine how many max messages this can
+ * cover and then set each endpoint's normal value
+ * equal to 3/4 this amount.
+ */
+ count = (cred_info->cur_free_credits /
+ cur_ep_dist->cred_per_msg)
+ * cur_ep_dist->cred_per_msg;
+ count = (count * 3) >> 2;
+ count = max(count, cur_ep_dist->cred_per_msg);
+ cur_ep_dist->cred_norm = count;
+
+ }
+ }
+}
+
+/* initialize and setup credit distribution */
+int ath6k_setup_credit_dist(void *htc_handle,
+ struct htc_credit_state_info *cred_info)
+{
+ u16 servicepriority[5];
+
+ memset(cred_info, 0, sizeof(struct htc_credit_state_info));
+
+ servicepriority[0] = WMI_CONTROL_SVC; /* highest */
+ servicepriority[1] = WMI_DATA_VO_SVC;
+ servicepriority[2] = WMI_DATA_VI_SVC;
+ servicepriority[3] = WMI_DATA_BE_SVC;
+ servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */
+
+ /* set priority list */
+ ath6kl_htc_set_credit_dist(htc_handle, cred_info, servicepriority, 5);
+
+ return 0;
+}
+
+/* reduce an ep's credits back to a set limit */
+static void ath6k_reduce_credits(struct htc_credit_state_info *cred_info,
+ struct htc_endpoint_credit_dist *ep_dist,
+ int limit)
+{
+ int credits;
+
+ ep_dist->cred_assngd = limit;
+
+ if (ep_dist->credits <= limit)
+ return;
+
+ credits = ep_dist->credits - limit;
+ ep_dist->credits -= credits;
+ cred_info->cur_free_credits += credits;
+}
+
+static void ath6k_credit_update(struct htc_credit_state_info *cred_info,
+ struct list_head *epdist_list)
+{
+ struct htc_endpoint_credit_dist *cur_dist_list;
+
+ list_for_each_entry(cur_dist_list, epdist_list, list) {
+ if (cur_dist_list->endpoint == ENDPOINT_0)
+ continue;
+
+ if (cur_dist_list->cred_to_dist > 0) {
+ cur_dist_list->credits +=
+ cur_dist_list->cred_to_dist;
+ cur_dist_list->cred_to_dist = 0;
+ if (cur_dist_list->credits >
+ cur_dist_list->cred_assngd)
+ ath6k_reduce_credits(cred_info,
+ cur_dist_list,
+ cur_dist_list->cred_assngd);
+
+ if (cur_dist_list->credits >
+ cur_dist_list->cred_norm)
+ ath6k_reduce_credits(cred_info, cur_dist_list,
+ cur_dist_list->cred_norm);
+
+ if (!(cur_dist_list->dist_flags & HTC_EP_ACTIVE)) {
+ if (cur_dist_list->txq_depth == 0)
+ ath6k_reduce_credits(cred_info,
+ cur_dist_list, 0);
+ }
+ }
+ }
+}
+
+/*
+ * HTC has an endpoint that needs credits, ep_dist is the endpoint in
+ * question.
+ */
+void ath6k_seek_credits(struct htc_credit_state_info *cred_info,
+ struct htc_endpoint_credit_dist *ep_dist)
+{
+ struct htc_endpoint_credit_dist *curdist_list;
+ int credits = 0;
+ int need;
+
+ if (ep_dist->svc_id == WMI_CONTROL_SVC)
+ goto out;
+
+ if ((ep_dist->svc_id == WMI_DATA_VI_SVC) ||
+ (ep_dist->svc_id == WMI_DATA_VO_SVC))
+ if ((ep_dist->cred_assngd >= ep_dist->cred_norm))
+ goto out;
+
+ /*
+ * For all other services, we follow a simple algorithm of:
+ *
+ * 1. checking the free pool for credits
+ * 2. checking lower priority endpoints for credits to take
+ */
+
+ credits = min(cred_info->cur_free_credits, ep_dist->seek_cred);
+
+ if (credits >= ep_dist->seek_cred)
+ goto out;
+
+ /*
+ * We don't have enough in the free pool, try taking away from
+ * lower priority services The rule for taking away credits:
+ *
+ * 1. Only take from lower priority endpoints
+ * 2. Only take what is allocated above the minimum (never
+ * starve an endpoint completely)
+ * 3. Only take what you need.
+ */
+
+ list_for_each_entry_reverse(curdist_list,
+ &cred_info->lowestpri_ep_dist,
+ list) {
+ if (curdist_list == ep_dist)
+ break;
+
+ need = ep_dist->seek_cred - cred_info->cur_free_credits;
+
+ if ((curdist_list->cred_assngd - need) >=
+ curdist_list->cred_min) {
+ /*
+ * The current one has been allocated more than
+ * it's minimum and it has enough credits assigned
+ * above it's minimum to fulfill our need try to
+ * take away just enough to fulfill our need.
+ */
+ ath6k_reduce_credits(cred_info, curdist_list,
+ curdist_list->cred_assngd - need);
+
+ if (cred_info->cur_free_credits >=
+ ep_dist->seek_cred)
+ break;
+ }
+
+ if (curdist_list->endpoint == ENDPOINT_0)
+ break;
+ }
+
+ credits = min(cred_info->cur_free_credits, ep_dist->seek_cred);
+
+out:
+ /* did we find some credits? */
+ if (credits)
+ ath6kl_deposit_credit_to_ep(cred_info, ep_dist, credits);
+
+ ep_dist->seek_cred = 0;
+}
+
+/* redistribute credits based on activity change */
+static void ath6k_redistribute_credits(struct htc_credit_state_info *info,
+ struct list_head *ep_dist_list)
+{
+ struct htc_endpoint_credit_dist *curdist_list;
+
+ list_for_each_entry(curdist_list, ep_dist_list, list) {
+ if (curdist_list->endpoint == ENDPOINT_0)
+ continue;
+
+ if ((curdist_list->svc_id == WMI_DATA_BK_SVC) ||
+ (curdist_list->svc_id == WMI_DATA_BE_SVC))
+ curdist_list->dist_flags |= HTC_EP_ACTIVE;
+
+ if ((curdist_list->svc_id != WMI_CONTROL_SVC) &&
+ !(curdist_list->dist_flags & HTC_EP_ACTIVE)) {
+ if (curdist_list->txq_depth == 0)
+ ath6k_reduce_credits(info,
+ curdist_list, 0);
+ else
+ ath6k_reduce_credits(info,
+ curdist_list,
+ curdist_list->cred_min);
+ }
+ }
+}
+
+/*
+ *
+ * This function is invoked whenever endpoints require credit
+ * distributions. A lock is held while this function is invoked, this
+ * function shall NOT block. The ep_dist_list is a list of distribution
+ * structures in prioritized order as defined by the call to the
+ * htc_set_credit_dist() api.
+ */
+void ath6k_credit_distribute(struct htc_credit_state_info *cred_info,
+ struct list_head *ep_dist_list,
+ enum htc_credit_dist_reason reason)
+{
+ switch (reason) {
+ case HTC_CREDIT_DIST_SEND_COMPLETE:
+ ath6k_credit_update(cred_info, ep_dist_list);
+ break;
+ case HTC_CREDIT_DIST_ACTIVITY_CHANGE:
+ ath6k_redistribute_credits(cred_info, ep_dist_list);
+ break;
+ default:
+ break;
+ }
+
+ WARN_ON(cred_info->cur_free_credits > cred_info->total_avail_credits);
+ WARN_ON(cred_info->cur_free_credits < 0);
+}
+
+void disconnect_timer_handler(unsigned long ptr)
+{
+ struct net_device *dev = (struct net_device *)ptr;
+ struct ath6kl *ar = ath6kl_priv(dev);
+
+ ath6kl_init_profile_info(ar);
+ ath6kl_disconnect(ar);
+}
+
+void ath6kl_disconnect(struct ath6kl *ar)
+{
+ if (test_bit(CONNECTED, &ar->flag) ||
+ test_bit(CONNECT_PEND, &ar->flag)) {
+ ath6kl_wmi_disconnect_cmd(ar->wmi);
+ /*
+ * Disconnect command is issued, clear the connect pending
+ * flag. The connected flag will be cleared in
+ * disconnect event notification.
+ */
+ clear_bit(CONNECT_PEND, &ar->flag);
+ }
+}
+
+void ath6kl_deep_sleep_enable(struct ath6kl *ar)
+{
+ switch (ar->sme_state) {
+ case SME_CONNECTING:
+ cfg80211_connect_result(ar->net_dev, ar->bssid, NULL, 0,
+ NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);
+ break;
+ case SME_CONNECTED:
+ default:
+ /*
+ * FIXME: oddly enough smeState is in DISCONNECTED during
+ * suspend, why? Need to send disconnected event in that
+ * state.
+ */
+ cfg80211_disconnected(ar->net_dev, 0, NULL, 0, GFP_KERNEL);
+ break;
+ }
+
+ if (test_bit(CONNECTED, &ar->flag) ||
+ test_bit(CONNECT_PEND, &ar->flag))
+ ath6kl_wmi_disconnect_cmd(ar->wmi);
+
+ ar->sme_state = SME_DISCONNECTED;
+
+ /* disable scanning */
+ if (ath6kl_wmi_scanparams_cmd(ar->wmi, 0xFFFF, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0) != 0)
+ printk(KERN_WARNING "ath6kl: failed to disable scan "
+ "during suspend\n");
+
+ ath6kl_cfg80211_scan_complete_event(ar, -ECANCELED);
+}
+
+/* WMI Event handlers */
+
+static const char *get_hw_id_string(u32 id)
+{
+ switch (id) {
+ case AR6003_REV1_VERSION:
+ return "1.0";
+ case AR6003_REV2_VERSION:
+ return "2.0";
+ case AR6003_REV3_VERSION:
+ return "2.1.1";
+ default:
+ return "unknown";
+ }
+}
+
+void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver)
+{
+ struct ath6kl *ar = devt;
+ struct net_device *dev = ar->net_dev;
+
+ memcpy(dev->dev_addr, datap, ETH_ALEN);
+ ath6kl_dbg(ATH6KL_DBG_TRC, "%s: mac addr = %pM\n",
+ __func__, dev->dev_addr);
+
+ ar->version.wlan_ver = sw_ver;
+ ar->version.abi_ver = abi_ver;
+
+ snprintf(ar->wdev->wiphy->fw_version,
+ sizeof(ar->wdev->wiphy->fw_version),
+ "%u.%u.%u.%u",
+ (ar->version.wlan_ver & 0xf0000000) >> 28,
+ (ar->version.wlan_ver & 0x0f000000) >> 24,
+ (ar->version.wlan_ver & 0x00ff0000) >> 16,
+ (ar->version.wlan_ver & 0x0000ffff));
+
+ /* indicate to the waiting thread that the ready event was received */
+ set_bit(WMI_READY, &ar->flag);
+ wake_up(&ar->event_wq);
+
+ ath6kl_info("hw %s fw %s%s\n",
+ get_hw_id_string(ar->wdev->wiphy->hw_version),
+ ar->wdev->wiphy->fw_version,
+ test_bit(TESTMODE, &ar->flag) ? " testmode" : "");
+}
+
+void ath6kl_scan_complete_evt(struct ath6kl *ar, int status)
+{
+ ath6kl_cfg80211_scan_complete_event(ar, status);
+
+ if (!ar->usr_bss_filter) {
+ clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
+ ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_SCAN, "scan complete: %d\n", status);
+}
+
+void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid,
+ u16 listen_int, u16 beacon_int,
+ enum network_type net_type, u8 beacon_ie_len,
+ u8 assoc_req_len, u8 assoc_resp_len,
+ u8 *assoc_info)
+{
+ unsigned long flags;
+
+ ath6kl_cfg80211_connect_event(ar, channel, bssid,
+ listen_int, beacon_int,
+ net_type, beacon_ie_len,
+ assoc_req_len, assoc_resp_len,
+ assoc_info);
+
+ memcpy(ar->bssid, bssid, sizeof(ar->bssid));
+ ar->bss_ch = channel;
+
+ if ((ar->nw_type == INFRA_NETWORK))
+ ath6kl_wmi_listeninterval_cmd(ar->wmi, ar->listen_intvl_t,
+ ar->listen_intvl_b);
+
+ netif_wake_queue(ar->net_dev);
+
+ /* Update connect & link status atomically */
+ spin_lock_irqsave(&ar->lock, flags);
+ set_bit(CONNECTED, &ar->flag);
+ clear_bit(CONNECT_PEND, &ar->flag);
+ netif_carrier_on(ar->net_dev);
+ spin_unlock_irqrestore(&ar->lock, flags);
+
+ aggr_reset_state(ar->aggr_cntxt);
+ ar->reconnect_flag = 0;
+
+ if ((ar->nw_type == ADHOC_NETWORK) && ar->ibss_ps_enable) {
+ memset(ar->node_map, 0, sizeof(ar->node_map));
+ ar->node_num = 0;
+ ar->next_ep_id = ENDPOINT_2;
+ }
+
+ if (!ar->usr_bss_filter) {
+ set_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
+ ath6kl_wmi_bssfilter_cmd(ar->wmi, CURRENT_BSS_FILTER, 0);
+ }
+}
+
+void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast)
+{
+ struct ath6kl_sta *sta;
+ u8 tsc[6];
+ /*
+ * For AP case, keyid will have aid of STA which sent pkt with
+ * MIC error. Use this aid to get MAC & send it to hostapd.
+ */
+ if (ar->nw_type == AP_NETWORK) {
+ sta = ath6kl_find_sta_by_aid(ar, (keyid >> 2));
+ if (!sta)
+ return;
+
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "ap tkip mic error received from aid=%d\n", keyid);
+
+ memset(tsc, 0, sizeof(tsc)); /* FIX: get correct TSC */
+ cfg80211_michael_mic_failure(ar->net_dev, sta->mac,
+ NL80211_KEYTYPE_PAIRWISE, keyid,
+ tsc, GFP_KERNEL);
+ } else
+ ath6kl_cfg80211_tkip_micerr_event(ar, keyid, ismcast);
+
+}
+
+static void ath6kl_update_target_stats(struct ath6kl *ar, u8 *ptr, u32 len)
+{
+ struct wmi_target_stats *tgt_stats =
+ (struct wmi_target_stats *) ptr;
+ struct target_stats *stats = &ar->target_stats;
+ struct tkip_ccmp_stats *ccmp_stats;
+ u8 ac;
+
+ if (len < sizeof(*tgt_stats))
+ return;
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "updating target stats\n");
+
+ stats->tx_pkt += le32_to_cpu(tgt_stats->stats.tx.pkt);
+ stats->tx_byte += le32_to_cpu(tgt_stats->stats.tx.byte);
+ stats->tx_ucast_pkt += le32_to_cpu(tgt_stats->stats.tx.ucast_pkt);
+ stats->tx_ucast_byte += le32_to_cpu(tgt_stats->stats.tx.ucast_byte);
+ stats->tx_mcast_pkt += le32_to_cpu(tgt_stats->stats.tx.mcast_pkt);
+ stats->tx_mcast_byte += le32_to_cpu(tgt_stats->stats.tx.mcast_byte);
+ stats->tx_bcast_pkt += le32_to_cpu(tgt_stats->stats.tx.bcast_pkt);
+ stats->tx_bcast_byte += le32_to_cpu(tgt_stats->stats.tx.bcast_byte);
+ stats->tx_rts_success_cnt +=
+ le32_to_cpu(tgt_stats->stats.tx.rts_success_cnt);
+
+ for (ac = 0; ac < WMM_NUM_AC; ac++)
+ stats->tx_pkt_per_ac[ac] +=
+ le32_to_cpu(tgt_stats->stats.tx.pkt_per_ac[ac]);
+
+ stats->tx_err += le32_to_cpu(tgt_stats->stats.tx.err);
+ stats->tx_fail_cnt += le32_to_cpu(tgt_stats->stats.tx.fail_cnt);
+ stats->tx_retry_cnt += le32_to_cpu(tgt_stats->stats.tx.retry_cnt);
+ stats->tx_mult_retry_cnt +=
+ le32_to_cpu(tgt_stats->stats.tx.mult_retry_cnt);
+ stats->tx_rts_fail_cnt +=
+ le32_to_cpu(tgt_stats->stats.tx.rts_fail_cnt);
+ stats->tx_ucast_rate =
+ ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate));
+
+ stats->rx_pkt += le32_to_cpu(tgt_stats->stats.rx.pkt);
+ stats->rx_byte += le32_to_cpu(tgt_stats->stats.rx.byte);
+ stats->rx_ucast_pkt += le32_to_cpu(tgt_stats->stats.rx.ucast_pkt);
+ stats->rx_ucast_byte += le32_to_cpu(tgt_stats->stats.rx.ucast_byte);
+ stats->rx_mcast_pkt += le32_to_cpu(tgt_stats->stats.rx.mcast_pkt);
+ stats->rx_mcast_byte += le32_to_cpu(tgt_stats->stats.rx.mcast_byte);
+ stats->rx_bcast_pkt += le32_to_cpu(tgt_stats->stats.rx.bcast_pkt);
+ stats->rx_bcast_byte += le32_to_cpu(tgt_stats->stats.rx.bcast_byte);
+ stats->rx_frgment_pkt += le32_to_cpu(tgt_stats->stats.rx.frgment_pkt);
+ stats->rx_err += le32_to_cpu(tgt_stats->stats.rx.err);
+ stats->rx_crc_err += le32_to_cpu(tgt_stats->stats.rx.crc_err);
+ stats->rx_key_cache_miss +=
+ le32_to_cpu(tgt_stats->stats.rx.key_cache_miss);
+ stats->rx_decrypt_err += le32_to_cpu(tgt_stats->stats.rx.decrypt_err);
+ stats->rx_dupl_frame += le32_to_cpu(tgt_stats->stats.rx.dupl_frame);
+ stats->rx_ucast_rate =
+ ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate));
+
+ ccmp_stats = &tgt_stats->stats.tkip_ccmp_stats;
+
+ stats->tkip_local_mic_fail +=
+ le32_to_cpu(ccmp_stats->tkip_local_mic_fail);
+ stats->tkip_cnter_measures_invoked +=
+ le32_to_cpu(ccmp_stats->tkip_cnter_measures_invoked);
+ stats->tkip_fmt_err += le32_to_cpu(ccmp_stats->tkip_fmt_err);
+
+ stats->ccmp_fmt_err += le32_to_cpu(ccmp_stats->ccmp_fmt_err);
+ stats->ccmp_replays += le32_to_cpu(ccmp_stats->ccmp_replays);
+
+ stats->pwr_save_fail_cnt +=
+ le32_to_cpu(tgt_stats->pm_stats.pwr_save_failure_cnt);
+ stats->noise_floor_calib =
+ a_sle32_to_cpu(tgt_stats->noise_floor_calib);
+
+ stats->cs_bmiss_cnt +=
+ le32_to_cpu(tgt_stats->cserv_stats.cs_bmiss_cnt);
+ stats->cs_low_rssi_cnt +=
+ le32_to_cpu(tgt_stats->cserv_stats.cs_low_rssi_cnt);
+ stats->cs_connect_cnt +=
+ le16_to_cpu(tgt_stats->cserv_stats.cs_connect_cnt);
+ stats->cs_discon_cnt +=
+ le16_to_cpu(tgt_stats->cserv_stats.cs_discon_cnt);
+
+ stats->cs_ave_beacon_rssi =
+ a_sle16_to_cpu(tgt_stats->cserv_stats.cs_ave_beacon_rssi);
+
+ stats->cs_last_roam_msec =
+ tgt_stats->cserv_stats.cs_last_roam_msec;
+ stats->cs_snr = tgt_stats->cserv_stats.cs_snr;
+ stats->cs_rssi = a_sle16_to_cpu(tgt_stats->cserv_stats.cs_rssi);
+
+ stats->lq_val = le32_to_cpu(tgt_stats->lq_val);
+
+ stats->wow_pkt_dropped +=
+ le32_to_cpu(tgt_stats->wow_stats.wow_pkt_dropped);
+ stats->wow_host_pkt_wakeups +=
+ tgt_stats->wow_stats.wow_host_pkt_wakeups;
+ stats->wow_host_evt_wakeups +=
+ tgt_stats->wow_stats.wow_host_evt_wakeups;
+ stats->wow_evt_discarded +=
+ le16_to_cpu(tgt_stats->wow_stats.wow_evt_discarded);
+
+ if (test_bit(STATS_UPDATE_PEND, &ar->flag)) {
+ clear_bit(STATS_UPDATE_PEND, &ar->flag);
+ wake_up(&ar->event_wq);
+ }
+}
+
+static void ath6kl_add_le32(__le32 *var, __le32 val)
+{
+ *var = cpu_to_le32(le32_to_cpu(*var) + le32_to_cpu(val));
+}
+
+void ath6kl_tgt_stats_event(struct ath6kl *ar, u8 *ptr, u32 len)
+{
+ struct wmi_ap_mode_stat *p = (struct wmi_ap_mode_stat *) ptr;
+ struct wmi_ap_mode_stat *ap = &ar->ap_stats;
+ struct wmi_per_sta_stat *st_ap, *st_p;
+ u8 ac;
+
+ if (ar->nw_type == AP_NETWORK) {
+ if (len < sizeof(*p))
+ return;
+
+ for (ac = 0; ac < AP_MAX_NUM_STA; ac++) {
+ st_ap = &ap->sta[ac];
+ st_p = &p->sta[ac];
+
+ ath6kl_add_le32(&st_ap->tx_bytes, st_p->tx_bytes);
+ ath6kl_add_le32(&st_ap->tx_pkts, st_p->tx_pkts);
+ ath6kl_add_le32(&st_ap->tx_error, st_p->tx_error);
+ ath6kl_add_le32(&st_ap->tx_discard, st_p->tx_discard);
+ ath6kl_add_le32(&st_ap->rx_bytes, st_p->rx_bytes);
+ ath6kl_add_le32(&st_ap->rx_pkts, st_p->rx_pkts);
+ ath6kl_add_le32(&st_ap->rx_error, st_p->rx_error);
+ ath6kl_add_le32(&st_ap->rx_discard, st_p->rx_discard);
+ }
+
+ } else {
+ ath6kl_update_target_stats(ar, ptr, len);
+ }
+}
+
+void ath6kl_wakeup_event(void *dev)
+{
+ struct ath6kl *ar = (struct ath6kl *) dev;
+
+ wake_up(&ar->event_wq);
+}
+
+void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr)
+{
+ struct ath6kl *ar = (struct ath6kl *) devt;
+
+ ar->tx_pwr = tx_pwr;
+ wake_up(&ar->event_wq);
+}
+
+void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid)
+{
+ struct ath6kl_sta *conn;
+ struct sk_buff *skb;
+ bool psq_empty = false;
+
+ conn = ath6kl_find_sta_by_aid(ar, aid);
+
+ if (!conn)
+ return;
+ /*
+ * Send out a packet queued on ps queue. When the ps queue
+ * becomes empty update the PVB for this station.
+ */
+ spin_lock_bh(&conn->psq_lock);
+ psq_empty = skb_queue_empty(&conn->psq);
+ spin_unlock_bh(&conn->psq_lock);
+
+ if (psq_empty)
+ /* TODO: Send out a NULL data frame */
+ return;
+
+ spin_lock_bh(&conn->psq_lock);
+ skb = skb_dequeue(&conn->psq);
+ spin_unlock_bh(&conn->psq_lock);
+
+ conn->sta_flags |= STA_PS_POLLED;
+ ath6kl_data_tx(skb, ar->net_dev);
+ conn->sta_flags &= ~STA_PS_POLLED;
+
+ spin_lock_bh(&conn->psq_lock);
+ psq_empty = skb_queue_empty(&conn->psq);
+ spin_unlock_bh(&conn->psq_lock);
+
+ if (psq_empty)
+ ath6kl_wmi_set_pvb_cmd(ar->wmi, conn->aid, 0);
+}
+
+void ath6kl_dtimexpiry_event(struct ath6kl *ar)
+{
+ bool mcastq_empty = false;
+ struct sk_buff *skb;
+
+ /*
+ * If there are no associated STAs, ignore the DTIM expiry event.
+ * There can be potential race conditions where the last associated
+ * STA may disconnect & before the host could clear the 'Indicate
+ * DTIM' request to the firmware, the firmware would have just
+ * indicated a DTIM expiry event. The race is between 'clear DTIM
+ * expiry cmd' going from the host to the firmware & the DTIM
+ * expiry event happening from the firmware to the host.
+ */
+ if (!ar->sta_list_index)
+ return;
+
+ spin_lock_bh(&ar->mcastpsq_lock);
+ mcastq_empty = skb_queue_empty(&ar->mcastpsq);
+ spin_unlock_bh(&ar->mcastpsq_lock);
+
+ if (mcastq_empty)
+ return;
+
+ /* set the STA flag to dtim_expired for the frame to go out */
+ set_bit(DTIM_EXPIRED, &ar->flag);
+
+ spin_lock_bh(&ar->mcastpsq_lock);
+ while ((skb = skb_dequeue(&ar->mcastpsq)) != NULL) {
+ spin_unlock_bh(&ar->mcastpsq_lock);
+
+ ath6kl_data_tx(skb, ar->net_dev);
+
+ spin_lock_bh(&ar->mcastpsq_lock);
+ }
+ spin_unlock_bh(&ar->mcastpsq_lock);
+
+ clear_bit(DTIM_EXPIRED, &ar->flag);
+
+ /* clear the LSB of the BitMapCtl field of the TIM IE */
+ ath6kl_wmi_set_pvb_cmd(ar->wmi, MCAST_AID, 0);
+}
+
+void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
+ u8 assoc_resp_len, u8 *assoc_info,
+ u16 prot_reason_status)
+{
+ unsigned long flags;
+
+ if (ar->nw_type == AP_NETWORK) {
+ if (!ath6kl_remove_sta(ar, bssid, prot_reason_status))
+ return;
+
+ /* if no more associated STAs, empty the mcast PS q */
+ if (ar->sta_list_index == 0) {
+ spin_lock_bh(&ar->mcastpsq_lock);
+ skb_queue_purge(&ar->mcastpsq);
+ spin_unlock_bh(&ar->mcastpsq_lock);
+
+ /* clear the LSB of the TIM IE's BitMapCtl field */
+ if (test_bit(WMI_READY, &ar->flag))
+ ath6kl_wmi_set_pvb_cmd(ar->wmi, MCAST_AID, 0);
+ }
+
+ if (!is_broadcast_ether_addr(bssid)) {
+ /* send event to application */
+ cfg80211_del_sta(ar->net_dev, bssid, GFP_KERNEL);
+ }
+
+ if (memcmp(ar->net_dev->dev_addr, bssid, ETH_ALEN) == 0) {
+ memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list));
+ clear_bit(CONNECTED, &ar->flag);
+ }
+ return;
+ }
+
+ ath6kl_cfg80211_disconnect_event(ar, reason, bssid,
+ assoc_resp_len, assoc_info,
+ prot_reason_status);
+
+ aggr_reset_state(ar->aggr_cntxt);
+
+ del_timer(&ar->disconnect_timer);
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CONNECT,
+ "disconnect reason is %d\n", reason);
+
+ /*
+ * If the event is due to disconnect cmd from the host, only they
+ * the target would stop trying to connect. Under any other
+ * condition, target would keep trying to connect.
+ */
+ if (reason == DISCONNECT_CMD) {
+ if (!ar->usr_bss_filter && test_bit(WMI_READY, &ar->flag))
+ ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+ } else {
+ set_bit(CONNECT_PEND, &ar->flag);
+ if (((reason == ASSOC_FAILED) &&
+ (prot_reason_status == 0x11)) ||
+ ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0)
+ && (ar->reconnect_flag == 1))) {
+ set_bit(CONNECTED, &ar->flag);
+ return;
+ }
+ }
+
+ /* update connect & link status atomically */
+ spin_lock_irqsave(&ar->lock, flags);
+ clear_bit(CONNECTED, &ar->flag);
+ netif_carrier_off(ar->net_dev);
+ spin_unlock_irqrestore(&ar->lock, flags);
+
+ if ((reason != CSERV_DISCONNECT) || (ar->reconnect_flag != 1))
+ ar->reconnect_flag = 0;
+
+ if (reason != CSERV_DISCONNECT)
+ ar->user_key_ctrl = 0;
+
+ netif_stop_queue(ar->net_dev);
+ memset(ar->bssid, 0, sizeof(ar->bssid));
+ ar->bss_ch = 0;
+
+ ath6kl_tx_data_cleanup(ar);
+}
+
+static int ath6kl_open(struct net_device *dev)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ar->lock, flags);
+
+ set_bit(WLAN_ENABLED, &ar->flag);
+
+ if (test_bit(CONNECTED, &ar->flag)) {
+ netif_carrier_on(dev);
+ netif_wake_queue(dev);
+ } else
+ netif_carrier_off(dev);
+
+ spin_unlock_irqrestore(&ar->lock, flags);
+
+ return 0;
+}
+
+static int ath6kl_close(struct net_device *dev)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+
+ netif_stop_queue(dev);
+
+ ath6kl_disconnect(ar);
+
+ if (test_bit(WMI_READY, &ar->flag)) {
+ if (ath6kl_wmi_scanparams_cmd(ar->wmi, 0xFFFF, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0))
+ return -EIO;
+
+ clear_bit(WLAN_ENABLED, &ar->flag);
+ }
+
+ ath6kl_cfg80211_scan_complete_event(ar, -ECANCELED);
+
+ return 0;
+}
+
+static struct net_device_stats *ath6kl_get_stats(struct net_device *dev)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+
+ return &ar->net_stats;
+}
+
+static struct net_device_ops ath6kl_netdev_ops = {
+ .ndo_open = ath6kl_open,
+ .ndo_stop = ath6kl_close,
+ .ndo_start_xmit = ath6kl_data_tx,
+ .ndo_get_stats = ath6kl_get_stats,
+};
+
+void init_netdev(struct net_device *dev)
+{
+ dev->netdev_ops = &ath6kl_netdev_ops;
+ dev->watchdog_timeo = ATH6KL_TX_TIMEOUT;
+
+ dev->needed_headroom = ETH_HLEN;
+ dev->needed_headroom += sizeof(struct ath6kl_llc_snap_hdr) +
+ sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH
+ + WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES;
+
+ return;
+}
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c
new file mode 100644
index 000000000000..066d4f88807f
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/sdio.c
@@ -0,0 +1,950 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sd.h>
+#include "htc_hif.h"
+#include "hif-ops.h"
+#include "target.h"
+#include "debug.h"
+#include "cfg80211.h"
+
+struct ath6kl_sdio {
+ struct sdio_func *func;
+
+ spinlock_t lock;
+
+ /* free list */
+ struct list_head bus_req_freeq;
+
+ /* available bus requests */
+ struct bus_request bus_req[BUS_REQUEST_MAX_NUM];
+
+ struct ath6kl *ar;
+ u8 *dma_buffer;
+
+ /* scatter request list head */
+ struct list_head scat_req;
+
+ spinlock_t scat_lock;
+ bool is_disabled;
+ atomic_t irq_handling;
+ const struct sdio_device_id *id;
+ struct work_struct wr_async_work;
+ struct list_head wr_asyncq;
+ spinlock_t wr_async_lock;
+};
+
+#define CMD53_ARG_READ 0
+#define CMD53_ARG_WRITE 1
+#define CMD53_ARG_BLOCK_BASIS 1
+#define CMD53_ARG_FIXED_ADDRESS 0
+#define CMD53_ARG_INCR_ADDRESS 1
+
+static inline struct ath6kl_sdio *ath6kl_sdio_priv(struct ath6kl *ar)
+{
+ return ar->hif_priv;
+}
+
+/*
+ * Macro to check if DMA buffer is WORD-aligned and DMA-able.
+ * Most host controllers assume the buffer is DMA'able and will
+ * bug-check otherwise (i.e. buffers on the stack). virt_addr_valid
+ * check fails on stack memory.
+ */
+static inline bool buf_needs_bounce(u8 *buf)
+{
+ return ((unsigned long) buf & 0x3) || !virt_addr_valid(buf);
+}
+
+static void ath6kl_sdio_set_mbox_info(struct ath6kl *ar)
+{
+ struct ath6kl_mbox_info *mbox_info = &ar->mbox_info;
+
+ /* EP1 has an extended range */
+ mbox_info->htc_addr = HIF_MBOX_BASE_ADDR;
+ mbox_info->htc_ext_addr = HIF_MBOX0_EXT_BASE_ADDR;
+ mbox_info->htc_ext_sz = HIF_MBOX0_EXT_WIDTH;
+ mbox_info->block_size = HIF_MBOX_BLOCK_SIZE;
+ mbox_info->gmbox_addr = HIF_GMBOX_BASE_ADDR;
+ mbox_info->gmbox_sz = HIF_GMBOX_WIDTH;
+}
+
+static inline void ath6kl_sdio_set_cmd53_arg(u32 *arg, u8 rw, u8 func,
+ u8 mode, u8 opcode, u32 addr,
+ u16 blksz)
+{
+ *arg = (((rw & 1) << 31) |
+ ((func & 0x7) << 28) |
+ ((mode & 1) << 27) |
+ ((opcode & 1) << 26) |
+ ((addr & 0x1FFFF) << 9) |
+ (blksz & 0x1FF));
+}
+
+static inline void ath6kl_sdio_set_cmd52_arg(u32 *arg, u8 write, u8 raw,
+ unsigned int address,
+ unsigned char val)
+{
+ const u8 func = 0;
+
+ *arg = ((write & 1) << 31) |
+ ((func & 0x7) << 28) |
+ ((raw & 1) << 27) |
+ (1 << 26) |
+ ((address & 0x1FFFF) << 9) |
+ (1 << 8) |
+ (val & 0xFF);
+}
+
+static int ath6kl_sdio_func0_cmd52_wr_byte(struct mmc_card *card,
+ unsigned int address,
+ unsigned char byte)
+{
+ struct mmc_command io_cmd;
+
+ memset(&io_cmd, 0, sizeof(io_cmd));
+ ath6kl_sdio_set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte);
+ io_cmd.opcode = SD_IO_RW_DIRECT;
+ io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+ return mmc_wait_for_cmd(card->host, &io_cmd, 0);
+}
+
+static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr,
+ u8 *buf, u32 len)
+{
+ int ret = 0;
+
+ if (request & HIF_WRITE) {
+ /* FIXME: looks like ugly workaround for something */
+ if (addr >= HIF_MBOX_BASE_ADDR &&
+ addr <= HIF_MBOX_END_ADDR)
+ addr += (HIF_MBOX_WIDTH - len);
+
+ /* FIXME: this also looks like ugly workaround */
+ if (addr == HIF_MBOX0_EXT_BASE_ADDR)
+ addr += HIF_MBOX0_EXT_WIDTH - len;
+
+ if (request & HIF_FIXED_ADDRESS)
+ ret = sdio_writesb(func, addr, buf, len);
+ else
+ ret = sdio_memcpy_toio(func, addr, buf, len);
+ } else {
+ if (request & HIF_FIXED_ADDRESS)
+ ret = sdio_readsb(func, buf, addr, len);
+ else
+ ret = sdio_memcpy_fromio(func, buf, addr, len);
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_SDIO, "%s addr 0x%x%s buf 0x%p len %d\n",
+ request & HIF_WRITE ? "wr" : "rd", addr,
+ request & HIF_FIXED_ADDRESS ? " (fixed)" : "", buf, len);
+ ath6kl_dbg_dump(ATH6KL_DBG_SDIO_DUMP, NULL, "sdio ", buf, len);
+
+ return ret;
+}
+
+static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio)
+{
+ struct bus_request *bus_req;
+ unsigned long flag;
+
+ spin_lock_irqsave(&ar_sdio->lock, flag);
+
+ if (list_empty(&ar_sdio->bus_req_freeq)) {
+ spin_unlock_irqrestore(&ar_sdio->lock, flag);
+ return NULL;
+ }
+
+ bus_req = list_first_entry(&ar_sdio->bus_req_freeq,
+ struct bus_request, list);
+ list_del(&bus_req->list);
+
+ spin_unlock_irqrestore(&ar_sdio->lock, flag);
+ ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n",
+ __func__, bus_req);
+
+ return bus_req;
+}
+
+static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio,
+ struct bus_request *bus_req)
+{
+ unsigned long flag;
+
+ ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n",
+ __func__, bus_req);
+
+ spin_lock_irqsave(&ar_sdio->lock, flag);
+ list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq);
+ spin_unlock_irqrestore(&ar_sdio->lock, flag);
+}
+
+static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req,
+ struct mmc_data *data)
+{
+ struct scatterlist *sg;
+ int i;
+
+ data->blksz = HIF_MBOX_BLOCK_SIZE;
+ data->blocks = scat_req->len / HIF_MBOX_BLOCK_SIZE;
+
+ ath6kl_dbg(ATH6KL_DBG_SCATTER,
+ "hif-scatter: (%s) addr: 0x%X, (block len: %d, block count: %d) , (tot:%d,sg:%d)\n",
+ (scat_req->req & HIF_WRITE) ? "WR" : "RD", scat_req->addr,
+ data->blksz, data->blocks, scat_req->len,
+ scat_req->scat_entries);
+
+ data->flags = (scat_req->req & HIF_WRITE) ? MMC_DATA_WRITE :
+ MMC_DATA_READ;
+
+ /* fill SG entries */
+ sg = scat_req->sgentries;
+ sg_init_table(sg, scat_req->scat_entries);
+
+ /* assemble SG list */
+ for (i = 0; i < scat_req->scat_entries; i++, sg++) {
+ ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n",
+ i, scat_req->scat_list[i].buf,
+ scat_req->scat_list[i].len);
+
+ sg_set_buf(sg, scat_req->scat_list[i].buf,
+ scat_req->scat_list[i].len);
+ }
+
+ /* set scatter-gather table for request */
+ data->sg = scat_req->sgentries;
+ data->sg_len = scat_req->scat_entries;
+}
+
+static int ath6kl_sdio_scat_rw(struct ath6kl_sdio *ar_sdio,
+ struct bus_request *req)
+{
+ struct mmc_request mmc_req;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ struct hif_scatter_req *scat_req;
+ u8 opcode, rw;
+ int status, len;
+
+ scat_req = req->scat_req;
+
+ if (scat_req->virt_scat) {
+ len = scat_req->len;
+ if (scat_req->req & HIF_BLOCK_BASIS)
+ len = round_down(len, HIF_MBOX_BLOCK_SIZE);
+
+ status = ath6kl_sdio_io(ar_sdio->func, scat_req->req,
+ scat_req->addr, scat_req->virt_dma_buf,
+ len);
+ goto scat_complete;
+ }
+
+ memset(&mmc_req, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+
+ ath6kl_sdio_setup_scat_data(scat_req, &data);
+
+ opcode = (scat_req->req & HIF_FIXED_ADDRESS) ?
+ CMD53_ARG_FIXED_ADDRESS : CMD53_ARG_INCR_ADDRESS;
+
+ rw = (scat_req->req & HIF_WRITE) ? CMD53_ARG_WRITE : CMD53_ARG_READ;
+
+ /* Fixup the address so that the last byte will fall on MBOX EOM */
+ if (scat_req->req & HIF_WRITE) {
+ if (scat_req->addr == HIF_MBOX_BASE_ADDR)
+ scat_req->addr += HIF_MBOX_WIDTH - scat_req->len;
+ else
+ /* Uses extended address range */
+ scat_req->addr += HIF_MBOX0_EXT_WIDTH - scat_req->len;
+ }
+
+ /* set command argument */
+ ath6kl_sdio_set_cmd53_arg(&cmd.arg, rw, ar_sdio->func->num,
+ CMD53_ARG_BLOCK_BASIS, opcode, scat_req->addr,
+ data.blocks);
+
+ cmd.opcode = SD_IO_RW_EXTENDED;
+ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+
+ mmc_req.cmd = &cmd;
+ mmc_req.data = &data;
+
+ mmc_set_data_timeout(&data, ar_sdio->func->card);
+ /* synchronous call to process request */
+ mmc_wait_for_req(ar_sdio->func->card->host, &mmc_req);
+
+ status = cmd.error ? cmd.error : data.error;
+
+scat_complete:
+ scat_req->status = status;
+
+ if (scat_req->status)
+ ath6kl_err("Scatter write request failed:%d\n",
+ scat_req->status);
+
+ if (scat_req->req & HIF_ASYNCHRONOUS)
+ scat_req->complete(ar_sdio->ar->htc_target, scat_req);
+
+ return status;
+}
+
+static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
+ int n_scat_entry, int n_scat_req,
+ bool virt_scat)
+{
+ struct hif_scatter_req *s_req;
+ struct bus_request *bus_req;
+ int i, scat_req_sz, scat_list_sz, sg_sz, buf_sz;
+ u8 *virt_buf;
+
+ scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item);
+ scat_req_sz = sizeof(*s_req) + scat_list_sz;
+
+ if (!virt_scat)
+ sg_sz = sizeof(struct scatterlist) * n_scat_entry;
+ else
+ buf_sz = 2 * L1_CACHE_BYTES +
+ ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
+
+ for (i = 0; i < n_scat_req; i++) {
+ /* allocate the scatter request */
+ s_req = kzalloc(scat_req_sz, GFP_KERNEL);
+ if (!s_req)
+ return -ENOMEM;
+
+ if (virt_scat) {
+ virt_buf = kzalloc(buf_sz, GFP_KERNEL);
+ if (!virt_buf) {
+ kfree(s_req);
+ return -ENOMEM;
+ }
+
+ s_req->virt_dma_buf =
+ (u8 *)L1_CACHE_ALIGN((unsigned long)virt_buf);
+ } else {
+ /* allocate sglist */
+ s_req->sgentries = kzalloc(sg_sz, GFP_KERNEL);
+
+ if (!s_req->sgentries) {
+ kfree(s_req);
+ return -ENOMEM;
+ }
+ }
+
+ /* allocate a bus request for this scatter request */
+ bus_req = ath6kl_sdio_alloc_busreq(ar_sdio);
+ if (!bus_req) {
+ kfree(s_req->sgentries);
+ kfree(s_req->virt_dma_buf);
+ kfree(s_req);
+ return -ENOMEM;
+ }
+
+ /* assign the scatter request to this bus request */
+ bus_req->scat_req = s_req;
+ s_req->busrequest = bus_req;
+
+ s_req->virt_scat = virt_scat;
+
+ /* add it to the scatter pool */
+ hif_scatter_req_add(ar_sdio->ar, s_req);
+ }
+
+ return 0;
+}
+
+static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
+ u32 len, u32 request)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ u8 *tbuf = NULL;
+ int ret;
+ bool bounced = false;
+
+ if (request & HIF_BLOCK_BASIS)
+ len = round_down(len, HIF_MBOX_BLOCK_SIZE);
+
+ if (buf_needs_bounce(buf)) {
+ if (!ar_sdio->dma_buffer)
+ return -ENOMEM;
+ tbuf = ar_sdio->dma_buffer;
+ memcpy(tbuf, buf, len);
+ bounced = true;
+ } else
+ tbuf = buf;
+
+ sdio_claim_host(ar_sdio->func);
+ ret = ath6kl_sdio_io(ar_sdio->func, request, addr, tbuf, len);
+ if ((request & HIF_READ) && bounced)
+ memcpy(buf, tbuf, len);
+ sdio_release_host(ar_sdio->func);
+
+ return ret;
+}
+
+static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio,
+ struct bus_request *req)
+{
+ if (req->scat_req)
+ ath6kl_sdio_scat_rw(ar_sdio, req);
+ else {
+ void *context;
+ int status;
+
+ status = ath6kl_sdio_read_write_sync(ar_sdio->ar, req->address,
+ req->buffer, req->length,
+ req->request);
+ context = req->packet;
+ ath6kl_sdio_free_bus_req(ar_sdio, req);
+ ath6kldev_rw_comp_handler(context, status);
+ }
+}
+
+static void ath6kl_sdio_write_async_work(struct work_struct *work)
+{
+ struct ath6kl_sdio *ar_sdio;
+ unsigned long flags;
+ struct bus_request *req, *tmp_req;
+
+ ar_sdio = container_of(work, struct ath6kl_sdio, wr_async_work);
+ sdio_claim_host(ar_sdio->func);
+
+ spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
+ list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
+ list_del(&req->list);
+ spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
+ __ath6kl_sdio_write_async(ar_sdio, req);
+ spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
+ }
+ spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
+
+ sdio_release_host(ar_sdio->func);
+}
+
+static void ath6kl_sdio_irq_handler(struct sdio_func *func)
+{
+ int status;
+ struct ath6kl_sdio *ar_sdio;
+
+ ath6kl_dbg(ATH6KL_DBG_SDIO, "irq\n");
+
+ ar_sdio = sdio_get_drvdata(func);
+ atomic_set(&ar_sdio->irq_handling, 1);
+
+ /*
+ * Release the host during interrups so we can pick it back up when
+ * we process commands.
+ */
+ sdio_release_host(ar_sdio->func);
+
+ status = ath6kldev_intr_bh_handler(ar_sdio->ar);
+ sdio_claim_host(ar_sdio->func);
+ atomic_set(&ar_sdio->irq_handling, 0);
+ WARN_ON(status && status != -ECANCELED);
+}
+
+static int ath6kl_sdio_power_on(struct ath6kl_sdio *ar_sdio)
+{
+ struct sdio_func *func = ar_sdio->func;
+ int ret = 0;
+
+ if (!ar_sdio->is_disabled)
+ return 0;
+
+ sdio_claim_host(func);
+
+ ret = sdio_enable_func(func);
+ if (ret) {
+ ath6kl_err("Unable to enable sdio func: %d)\n", ret);
+ sdio_release_host(func);
+ return ret;
+ }
+
+ sdio_release_host(func);
+
+ /*
+ * Wait for hardware to initialise. It should take a lot less than
+ * 10 ms but let's be conservative here.
+ */
+ msleep(10);
+
+ ar_sdio->is_disabled = false;
+
+ return ret;
+}
+
+static int ath6kl_sdio_power_off(struct ath6kl_sdio *ar_sdio)
+{
+ int ret;
+
+ if (ar_sdio->is_disabled)
+ return 0;
+
+ /* Disable the card */
+ sdio_claim_host(ar_sdio->func);
+ ret = sdio_disable_func(ar_sdio->func);
+ sdio_release_host(ar_sdio->func);
+
+ if (ret)
+ return ret;
+
+ ar_sdio->is_disabled = true;
+
+ return ret;
+}
+
+static int ath6kl_sdio_write_async(struct ath6kl *ar, u32 address, u8 *buffer,
+ u32 length, u32 request,
+ struct htc_packet *packet)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ struct bus_request *bus_req;
+ unsigned long flags;
+
+ bus_req = ath6kl_sdio_alloc_busreq(ar_sdio);
+
+ if (!bus_req)
+ return -ENOMEM;
+
+ bus_req->address = address;
+ bus_req->buffer = buffer;
+ bus_req->length = length;
+ bus_req->request = request;
+ bus_req->packet = packet;
+
+ spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
+ list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq);
+ spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
+ queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work);
+
+ return 0;
+}
+
+static void ath6kl_sdio_irq_enable(struct ath6kl *ar)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ int ret;
+
+ sdio_claim_host(ar_sdio->func);
+
+ /* Register the isr */
+ ret = sdio_claim_irq(ar_sdio->func, ath6kl_sdio_irq_handler);
+ if (ret)
+ ath6kl_err("Failed to claim sdio irq: %d\n", ret);
+
+ sdio_release_host(ar_sdio->func);
+}
+
+static void ath6kl_sdio_irq_disable(struct ath6kl *ar)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ int ret;
+
+ sdio_claim_host(ar_sdio->func);
+
+ /* Mask our function IRQ */
+ while (atomic_read(&ar_sdio->irq_handling)) {
+ sdio_release_host(ar_sdio->func);
+ schedule_timeout(HZ / 10);
+ sdio_claim_host(ar_sdio->func);
+ }
+
+ ret = sdio_release_irq(ar_sdio->func);
+ if (ret)
+ ath6kl_err("Failed to release sdio irq: %d\n", ret);
+
+ sdio_release_host(ar_sdio->func);
+}
+
+static struct hif_scatter_req *ath6kl_sdio_scatter_req_get(struct ath6kl *ar)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ struct hif_scatter_req *node = NULL;
+ unsigned long flag;
+
+ spin_lock_irqsave(&ar_sdio->scat_lock, flag);
+
+ if (!list_empty(&ar_sdio->scat_req)) {
+ node = list_first_entry(&ar_sdio->scat_req,
+ struct hif_scatter_req, list);
+ list_del(&node->list);
+ }
+
+ spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
+
+ return node;
+}
+
+static void ath6kl_sdio_scatter_req_add(struct ath6kl *ar,
+ struct hif_scatter_req *s_req)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ unsigned long flag;
+
+ spin_lock_irqsave(&ar_sdio->scat_lock, flag);
+
+ list_add_tail(&s_req->list, &ar_sdio->scat_req);
+
+ spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
+
+}
+
+/* scatter gather read write request */
+static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar,
+ struct hif_scatter_req *scat_req)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ u32 request = scat_req->req;
+ int status = 0;
+ unsigned long flags;
+
+ if (!scat_req->len)
+ return -EINVAL;
+
+ ath6kl_dbg(ATH6KL_DBG_SCATTER,
+ "hif-scatter: total len: %d scatter entries: %d\n",
+ scat_req->len, scat_req->scat_entries);
+
+ if (request & HIF_SYNCHRONOUS) {
+ sdio_claim_host(ar_sdio->func);
+ status = ath6kl_sdio_scat_rw(ar_sdio, scat_req->busrequest);
+ sdio_release_host(ar_sdio->func);
+ } else {
+ spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
+ list_add_tail(&scat_req->busrequest->list, &ar_sdio->wr_asyncq);
+ spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
+ queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work);
+ }
+
+ return status;
+}
+
+/* clean up scatter support */
+static void ath6kl_sdio_cleanup_scatter(struct ath6kl *ar)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ struct hif_scatter_req *s_req, *tmp_req;
+ unsigned long flag;
+
+ /* empty the free list */
+ spin_lock_irqsave(&ar_sdio->scat_lock, flag);
+ list_for_each_entry_safe(s_req, tmp_req, &ar_sdio->scat_req, list) {
+ list_del(&s_req->list);
+ spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
+
+ if (s_req->busrequest)
+ ath6kl_sdio_free_bus_req(ar_sdio, s_req->busrequest);
+ kfree(s_req->virt_dma_buf);
+ kfree(s_req->sgentries);
+ kfree(s_req);
+
+ spin_lock_irqsave(&ar_sdio->scat_lock, flag);
+ }
+ spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
+}
+
+/* setup of HIF scatter resources */
+static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ struct htc_target *target = ar->htc_target;
+ int ret;
+ bool virt_scat = false;
+
+ /* check if host supports scatter and it meets our requirements */
+ if (ar_sdio->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) {
+ ath6kl_err("host only supports scatter of :%d entries, need: %d\n",
+ ar_sdio->func->card->host->max_segs,
+ MAX_SCATTER_ENTRIES_PER_REQ);
+ virt_scat = true;
+ }
+
+ if (!virt_scat) {
+ ret = ath6kl_sdio_alloc_prep_scat_req(ar_sdio,
+ MAX_SCATTER_ENTRIES_PER_REQ,
+ MAX_SCATTER_REQUESTS, virt_scat);
+
+ if (!ret) {
+ ath6kl_dbg(ATH6KL_DBG_SCATTER,
+ "hif-scatter enabled: max scatter req : %d entries: %d\n",
+ MAX_SCATTER_REQUESTS,
+ MAX_SCATTER_ENTRIES_PER_REQ);
+
+ target->max_scat_entries = MAX_SCATTER_ENTRIES_PER_REQ;
+ target->max_xfer_szper_scatreq =
+ MAX_SCATTER_REQ_TRANSFER_SIZE;
+ } else {
+ ath6kl_sdio_cleanup_scatter(ar);
+ ath6kl_warn("hif scatter resource setup failed, trying virtual scatter method\n");
+ }
+ }
+
+ if (virt_scat || ret) {
+ ret = ath6kl_sdio_alloc_prep_scat_req(ar_sdio,
+ ATH6KL_SCATTER_ENTRIES_PER_REQ,
+ ATH6KL_SCATTER_REQS, virt_scat);
+
+ if (ret) {
+ ath6kl_err("failed to alloc virtual scatter resources !\n");
+ ath6kl_sdio_cleanup_scatter(ar);
+ return ret;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_SCATTER,
+ "Vitual scatter enabled, max_scat_req:%d, entries:%d\n",
+ ATH6KL_SCATTER_REQS, ATH6KL_SCATTER_ENTRIES_PER_REQ);
+
+ target->max_scat_entries = ATH6KL_SCATTER_ENTRIES_PER_REQ;
+ target->max_xfer_szper_scatreq =
+ ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
+ }
+
+ return 0;
+}
+
+static int ath6kl_sdio_suspend(struct ath6kl *ar)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ struct sdio_func *func = ar_sdio->func;
+ mmc_pm_flag_t flags;
+ int ret;
+
+ flags = sdio_get_host_pm_caps(func);
+
+ if (!(flags & MMC_PM_KEEP_POWER))
+ /* as host doesn't support keep power we need to bail out */
+ ath6kl_dbg(ATH6KL_DBG_SDIO,
+ "func %d doesn't support MMC_PM_KEEP_POWER\n",
+ func->num);
+ return -EINVAL;
+
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+ if (ret) {
+ printk(KERN_ERR "ath6kl: set sdio pm flags failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ ath6kl_deep_sleep_enable(ar);
+
+ return 0;
+}
+
+static const struct ath6kl_hif_ops ath6kl_sdio_ops = {
+ .read_write_sync = ath6kl_sdio_read_write_sync,
+ .write_async = ath6kl_sdio_write_async,
+ .irq_enable = ath6kl_sdio_irq_enable,
+ .irq_disable = ath6kl_sdio_irq_disable,
+ .scatter_req_get = ath6kl_sdio_scatter_req_get,
+ .scatter_req_add = ath6kl_sdio_scatter_req_add,
+ .enable_scatter = ath6kl_sdio_enable_scatter,
+ .scat_req_rw = ath6kl_sdio_async_rw_scatter,
+ .cleanup_scatter = ath6kl_sdio_cleanup_scatter,
+ .suspend = ath6kl_sdio_suspend,
+};
+
+static int ath6kl_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int ret;
+ struct ath6kl_sdio *ar_sdio;
+ struct ath6kl *ar;
+ int count;
+
+ ath6kl_dbg(ATH6KL_DBG_SDIO,
+ "new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
+ func->num, func->vendor, func->device,
+ func->max_blksize, func->cur_blksize);
+
+ ar_sdio = kzalloc(sizeof(struct ath6kl_sdio), GFP_KERNEL);
+ if (!ar_sdio)
+ return -ENOMEM;
+
+ ar_sdio->dma_buffer = kzalloc(HIF_DMA_BUFFER_SIZE, GFP_KERNEL);
+ if (!ar_sdio->dma_buffer) {
+ ret = -ENOMEM;
+ goto err_hif;
+ }
+
+ ar_sdio->func = func;
+ sdio_set_drvdata(func, ar_sdio);
+
+ ar_sdio->id = id;
+ ar_sdio->is_disabled = true;
+
+ spin_lock_init(&ar_sdio->lock);
+ spin_lock_init(&ar_sdio->scat_lock);
+ spin_lock_init(&ar_sdio->wr_async_lock);
+
+ INIT_LIST_HEAD(&ar_sdio->scat_req);
+ INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
+ INIT_LIST_HEAD(&ar_sdio->wr_asyncq);
+
+ INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work);
+
+ for (count = 0; count < BUS_REQUEST_MAX_NUM; count++)
+ ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]);
+
+ ar = ath6kl_core_alloc(&ar_sdio->func->dev);
+ if (!ar) {
+ ath6kl_err("Failed to alloc ath6kl core\n");
+ ret = -ENOMEM;
+ goto err_dma;
+ }
+
+ ar_sdio->ar = ar;
+ ar->hif_priv = ar_sdio;
+ ar->hif_ops = &ath6kl_sdio_ops;
+
+ ath6kl_sdio_set_mbox_info(ar);
+
+ sdio_claim_host(func);
+
+ if ((ar_sdio->id->device & MANUFACTURER_ID_ATH6KL_BASE_MASK) >=
+ MANUFACTURER_ID_AR6003_BASE) {
+ /* enable 4-bit ASYNC interrupt on AR6003 or later */
+ ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card,
+ CCCR_SDIO_IRQ_MODE_REG,
+ SDIO_IRQ_MODE_ASYNC_4BIT_IRQ);
+ if (ret) {
+ ath6kl_err("Failed to enable 4-bit async irq mode %d\n",
+ ret);
+ sdio_release_host(func);
+ goto err_cfg80211;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_SDIO, "4-bit async irq mode enabled\n");
+ }
+
+ /* give us some time to enable, in ms */
+ func->enable_timeout = 100;
+
+ sdio_release_host(func);
+
+ ret = ath6kl_sdio_power_on(ar_sdio);
+ if (ret)
+ goto err_cfg80211;
+
+ sdio_claim_host(func);
+
+ ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE);
+ if (ret) {
+ ath6kl_err("Set sdio block size %d failed: %d)\n",
+ HIF_MBOX_BLOCK_SIZE, ret);
+ sdio_release_host(func);
+ goto err_off;
+ }
+
+ sdio_release_host(func);
+
+ ret = ath6kl_core_init(ar);
+ if (ret) {
+ ath6kl_err("Failed to init ath6kl core\n");
+ goto err_off;
+ }
+
+ return ret;
+
+err_off:
+ ath6kl_sdio_power_off(ar_sdio);
+err_cfg80211:
+ ath6kl_cfg80211_deinit(ar_sdio->ar);
+err_dma:
+ kfree(ar_sdio->dma_buffer);
+err_hif:
+ kfree(ar_sdio);
+
+ return ret;
+}
+
+static void ath6kl_sdio_remove(struct sdio_func *func)
+{
+ struct ath6kl_sdio *ar_sdio;
+
+ ath6kl_dbg(ATH6KL_DBG_SDIO,
+ "removed func %d vendor 0x%x device 0x%x\n",
+ func->num, func->vendor, func->device);
+
+ ar_sdio = sdio_get_drvdata(func);
+
+ ath6kl_stop_txrx(ar_sdio->ar);
+ cancel_work_sync(&ar_sdio->wr_async_work);
+
+ ath6kl_unavail_ev(ar_sdio->ar);
+
+ ath6kl_sdio_power_off(ar_sdio);
+
+ kfree(ar_sdio->dma_buffer);
+ kfree(ar_sdio);
+}
+
+static const struct sdio_device_id ath6kl_sdio_devices[] = {
+ {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))},
+ {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
+ {},
+};
+
+MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices);
+
+static struct sdio_driver ath6kl_sdio_driver = {
+ .name = "ath6kl_sdio",
+ .id_table = ath6kl_sdio_devices,
+ .probe = ath6kl_sdio_probe,
+ .remove = ath6kl_sdio_remove,
+};
+
+static int __init ath6kl_sdio_init(void)
+{
+ int ret;
+
+ ret = sdio_register_driver(&ath6kl_sdio_driver);
+ if (ret)
+ ath6kl_err("sdio driver registration failed: %d\n", ret);
+
+ return ret;
+}
+
+static void __exit ath6kl_sdio_exit(void)
+{
+ sdio_unregister_driver(&ath6kl_sdio_driver);
+}
+
+module_init(ath6kl_sdio_init);
+module_exit(ath6kl_sdio_exit);
+
+MODULE_AUTHOR("Atheros Communications, Inc.");
+MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices");
+MODULE_LICENSE("Dual BSD/GPL");
+
+MODULE_FIRMWARE(AR6003_REV2_OTP_FILE);
+MODULE_FIRMWARE(AR6003_REV2_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6003_REV2_PATCH_FILE);
+MODULE_FIRMWARE(AR6003_REV2_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6003_REV2_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6003_REV3_OTP_FILE);
+MODULE_FIRMWARE(AR6003_REV3_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6003_REV3_PATCH_FILE);
+MODULE_FIRMWARE(AR6003_REV3_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6003_REV3_DEFAULT_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h
new file mode 100644
index 000000000000..c9a76051f042
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/target.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2004-2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef TARGET_H
+#define TARGET_H
+
+#define AR6003_BOARD_DATA_SZ 1024
+#define AR6003_BOARD_EXT_DATA_SZ 768
+
+#define AR6004_BOARD_DATA_SZ 7168
+#define AR6004_BOARD_EXT_DATA_SZ 0
+
+#define RESET_CONTROL_ADDRESS 0x00000000
+#define RESET_CONTROL_COLD_RST 0x00000100
+#define RESET_CONTROL_MBOX_RST 0x00000004
+
+#define CPU_CLOCK_STANDARD_S 0
+#define CPU_CLOCK_STANDARD 0x00000003
+#define CPU_CLOCK_ADDRESS 0x00000020
+
+#define CLOCK_CONTROL_ADDRESS 0x00000028
+#define CLOCK_CONTROL_LF_CLK32_S 2
+#define CLOCK_CONTROL_LF_CLK32 0x00000004
+
+#define SYSTEM_SLEEP_ADDRESS 0x000000c4
+#define SYSTEM_SLEEP_DISABLE_S 0
+#define SYSTEM_SLEEP_DISABLE 0x00000001
+
+#define LPO_CAL_ADDRESS 0x000000e0
+#define LPO_CAL_ENABLE_S 20
+#define LPO_CAL_ENABLE 0x00100000
+
+#define GPIO_PIN10_ADDRESS 0x00000050
+#define GPIO_PIN11_ADDRESS 0x00000054
+#define GPIO_PIN12_ADDRESS 0x00000058
+#define GPIO_PIN13_ADDRESS 0x0000005c
+
+#define HOST_INT_STATUS_ADDRESS 0x00000400
+#define HOST_INT_STATUS_ERROR_S 7
+#define HOST_INT_STATUS_ERROR 0x00000080
+
+#define HOST_INT_STATUS_CPU_S 6
+#define HOST_INT_STATUS_CPU 0x00000040
+
+#define HOST_INT_STATUS_COUNTER_S 4
+#define HOST_INT_STATUS_COUNTER 0x00000010
+
+#define CPU_INT_STATUS_ADDRESS 0x00000401
+
+#define ERROR_INT_STATUS_ADDRESS 0x00000402
+#define ERROR_INT_STATUS_WAKEUP_S 2
+#define ERROR_INT_STATUS_WAKEUP 0x00000004
+
+#define ERROR_INT_STATUS_RX_UNDERFLOW_S 1
+#define ERROR_INT_STATUS_RX_UNDERFLOW 0x00000002
+
+#define ERROR_INT_STATUS_TX_OVERFLOW_S 0
+#define ERROR_INT_STATUS_TX_OVERFLOW 0x00000001
+
+#define COUNTER_INT_STATUS_ADDRESS 0x00000403
+#define COUNTER_INT_STATUS_COUNTER_S 0
+#define COUNTER_INT_STATUS_COUNTER 0x000000ff
+
+#define RX_LOOKAHEAD_VALID_ADDRESS 0x00000405
+
+#define INT_STATUS_ENABLE_ADDRESS 0x00000418
+#define INT_STATUS_ENABLE_ERROR_S 7
+#define INT_STATUS_ENABLE_ERROR 0x00000080
+
+#define INT_STATUS_ENABLE_CPU_S 6
+#define INT_STATUS_ENABLE_CPU 0x00000040
+
+#define INT_STATUS_ENABLE_INT_S 5
+#define INT_STATUS_ENABLE_INT 0x00000020
+#define INT_STATUS_ENABLE_COUNTER_S 4
+#define INT_STATUS_ENABLE_COUNTER 0x00000010
+
+#define INT_STATUS_ENABLE_MBOX_DATA_S 0
+#define INT_STATUS_ENABLE_MBOX_DATA 0x0000000f
+
+#define CPU_INT_STATUS_ENABLE_ADDRESS 0x00000419
+#define CPU_INT_STATUS_ENABLE_BIT_S 0
+#define CPU_INT_STATUS_ENABLE_BIT 0x000000ff
+
+#define ERROR_STATUS_ENABLE_ADDRESS 0x0000041a
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_S 1
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW 0x00000002
+
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_S 0
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW 0x00000001
+
+#define COUNTER_INT_STATUS_ENABLE_ADDRESS 0x0000041b
+#define COUNTER_INT_STATUS_ENABLE_BIT_S 0
+#define COUNTER_INT_STATUS_ENABLE_BIT 0x000000ff
+
+#define COUNT_ADDRESS 0x00000420
+
+#define COUNT_DEC_ADDRESS 0x00000440
+
+#define WINDOW_DATA_ADDRESS 0x00000474
+#define WINDOW_WRITE_ADDR_ADDRESS 0x00000478
+#define WINDOW_READ_ADDR_ADDRESS 0x0000047c
+#define CPU_DBG_SEL_ADDRESS 0x00000483
+#define CPU_DBG_ADDRESS 0x00000484
+
+#define LOCAL_SCRATCH_ADDRESS 0x000000c0
+#define ATH6KL_OPTION_SLEEP_DISABLE 0x08
+
+#define RTC_BASE_ADDRESS 0x00004000
+#define GPIO_BASE_ADDRESS 0x00014000
+#define MBOX_BASE_ADDRESS 0x00018000
+#define ANALOG_INTF_BASE_ADDRESS 0x0001c000
+
+/* real name of the register is unknown */
+#define ATH6KL_ANALOG_PLL_REGISTER (ANALOG_INTF_BASE_ADDRESS + 0x284)
+
+#define SM(f, v) (((v) << f##_S) & f)
+#define MS(f, v) (((v) & f) >> f##_S)
+
+/*
+ * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
+ * host_interest structure.
+ *
+ * Host Interest is shared between Host and Target in order to coordinate
+ * between the two, and is intended to remain constant (with additions only
+ * at the end).
+ */
+#define ATH6KL_AR6003_HI_START_ADDR 0x00540600
+#define ATH6KL_AR6004_HI_START_ADDR 0x00400800
+
+/*
+ * These are items that the Host may need to access
+ * via BMI or via the Diagnostic Window. The position
+ * of items in this structure must remain constant.
+ * across firmware revisions!
+ *
+ * Types for each item must be fixed size across target and host platforms.
+ * The structure is used only to calculate offset for each register with
+ * HI_ITEM() macro, no values are stored to it.
+ *
+ * More items may be added at the end.
+ */
+struct host_interest {
+ /*
+ * Pointer to application-defined area, if any.
+ * Set by Target application during startup.
+ */
+ u32 hi_app_host_interest; /* 0x00 */
+
+ /* Pointer to register dump area, valid after Target crash. */
+ u32 hi_failure_state; /* 0x04 */
+
+ /* Pointer to debug logging header */
+ u32 hi_dbglog_hdr; /* 0x08 */
+
+ u32 hi_unused1; /* 0x0c */
+
+ /*
+ * General-purpose flag bits, similar to ATH6KL_OPTION_* flags.
+ * Can be used by application rather than by OS.
+ */
+ u32 hi_option_flag; /* 0x10 */
+
+ /*
+ * Boolean that determines whether or not to
+ * display messages on the serial port.
+ */
+ u32 hi_serial_enable; /* 0x14 */
+
+ /* Start address of DataSet index, if any */
+ u32 hi_dset_list_head; /* 0x18 */
+
+ /* Override Target application start address */
+ u32 hi_app_start; /* 0x1c */
+
+ /* Clock and voltage tuning */
+ u32 hi_skip_clock_init; /* 0x20 */
+ u32 hi_core_clock_setting; /* 0x24 */
+ u32 hi_cpu_clock_setting; /* 0x28 */
+ u32 hi_system_sleep_setting; /* 0x2c */
+ u32 hi_xtal_control_setting; /* 0x30 */
+ u32 hi_pll_ctrl_setting_24ghz; /* 0x34 */
+ u32 hi_pll_ctrl_setting_5ghz; /* 0x38 */
+ u32 hi_ref_voltage_trim_setting; /* 0x3c */
+ u32 hi_clock_info; /* 0x40 */
+
+ /*
+ * Flash configuration overrides, used only
+ * when firmware is not executing from flash.
+ * (When using flash, modify the global variables
+ * with equivalent names.)
+ */
+ u32 hi_bank0_addr_value; /* 0x44 */
+ u32 hi_bank0_read_value; /* 0x48 */
+ u32 hi_bank0_write_value; /* 0x4c */
+ u32 hi_bank0_config_value; /* 0x50 */
+
+ /* Pointer to Board Data */
+ u32 hi_board_data; /* 0x54 */
+ u32 hi_board_data_initialized; /* 0x58 */
+
+ u32 hi_dset_ram_index_tbl; /* 0x5c */
+
+ u32 hi_desired_baud_rate; /* 0x60 */
+ u32 hi_dbglog_config; /* 0x64 */
+ u32 hi_end_ram_reserve_sz; /* 0x68 */
+ u32 hi_mbox_io_block_sz; /* 0x6c */
+
+ u32 hi_num_bpatch_streams; /* 0x70 -- unused */
+ u32 hi_mbox_isr_yield_limit; /* 0x74 */
+
+ u32 hi_refclk_hz; /* 0x78 */
+ u32 hi_ext_clk_detected; /* 0x7c */
+ u32 hi_dbg_uart_txpin; /* 0x80 */
+ u32 hi_dbg_uart_rxpin; /* 0x84 */
+ u32 hi_hci_uart_baud; /* 0x88 */
+ u32 hi_hci_uart_pin_assignments; /* 0x8C */
+ /*
+ * NOTE: byte [0] = tx pin, [1] = rx pin, [2] = rts pin, [3] = cts
+ * pin
+ */
+ u32 hi_hci_uart_baud_scale_val; /* 0x90 */
+ u32 hi_hci_uart_baud_step_val; /* 0x94 */
+
+ u32 hi_allocram_start; /* 0x98 */
+ u32 hi_allocram_sz; /* 0x9c */
+ u32 hi_hci_bridge_flags; /* 0xa0 */
+ u32 hi_hci_uart_support_pins; /* 0xa4 */
+ /*
+ * NOTE: byte [0] = RESET pin (bit 7 is polarity),
+ * bytes[1]..bytes[3] are for future use
+ */
+ u32 hi_hci_uart_pwr_mgmt_params; /* 0xa8 */
+ /*
+ * 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high
+ * [31:16]: wakeup timeout in ms
+ */
+
+ /* Pointer to extended board data */
+ u32 hi_board_ext_data; /* 0xac */
+ u32 hi_board_ext_data_config; /* 0xb0 */
+
+ /*
+ * Bit [0] : valid
+ * Bit[31:16: size
+ */
+ /*
+ * hi_reset_flag is used to do some stuff when target reset.
+ * such as restore app_start after warm reset or
+ * preserve host Interest area, or preserve ROM data, literals etc.
+ */
+ u32 hi_reset_flag; /* 0xb4 */
+ /* indicate hi_reset_flag is valid */
+ u32 hi_reset_flag_valid; /* 0xb8 */
+ u32 hi_hci_uart_pwr_mgmt_params_ext; /* 0xbc */
+ /*
+ * 0xbc - [31:0]: idle timeout in ms
+ */
+ /* ACS flags */
+ u32 hi_acs_flags; /* 0xc0 */
+ u32 hi_console_flags; /* 0xc4 */
+ u32 hi_nvram_state; /* 0xc8 */
+ u32 hi_option_flag2; /* 0xcc */
+
+ /* If non-zero, override values sent to Host in WMI_READY event. */
+ u32 hi_sw_version_override; /* 0xd0 */
+ u32 hi_abi_version_override; /* 0xd4 */
+
+ /*
+ * Percentage of high priority RX traffic to total expected RX traffic -
+ * applicable only to ar6004
+ */
+ u32 hi_hp_rx_traffic_ratio; /* 0xd8 */
+
+ /* test applications flags */
+ u32 hi_test_apps_related ; /* 0xdc */
+ /* location of test script */
+ u32 hi_ota_testscript; /* 0xe0 */
+ /* location of CAL data */
+ u32 hi_cal_data; /* 0xe4 */
+ /* Number of packet log buffers */
+ u32 hi_pktlog_num_buffers; /* 0xe8 */
+
+} __packed;
+
+#define HI_ITEM(item) offsetof(struct host_interest, item)
+
+#define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3
+
+#define HI_OPTION_FW_MODE_IBSS 0x0
+#define HI_OPTION_FW_MODE_BSS_STA 0x1
+#define HI_OPTION_FW_MODE_AP 0x2
+
+#define HI_OPTION_FW_SUBMODE_NONE 0x0
+#define HI_OPTION_FW_SUBMODE_P2PDEV 0x1
+#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2
+#define HI_OPTION_FW_SUBMODE_P2PGO 0x3
+
+#define HI_OPTION_NUM_DEV_SHIFT 0x9
+
+#define HI_OPTION_FW_BRIDGE_SHIFT 0x04
+
+/* Fw Mode/SubMode Mask
+|------------------------------------------------------------------------------|
+| SUB | SUB | SUB | SUB | | | |
+| MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0|
+| (2) | (2) | (2) | (2) | (2) | (2) | (2) | (2)
+|------------------------------------------------------------------------------|
+*/
+#define HI_OPTION_FW_MODE_SHIFT 0xC
+#define HI_OPTION_FW_SUBMODE_SHIFT 0x14
+
+/* Convert a Target virtual address into a Target physical address */
+#define AR6003_VTOP(vaddr) ((vaddr) & 0x001fffff)
+#define AR6004_VTOP(vaddr) (vaddr)
+
+#define TARG_VTOP(target_type, vaddr) \
+ (((target_type) == TARGET_TYPE_AR6003) ? AR6003_VTOP(vaddr) : \
+ (((target_type) == TARGET_TYPE_AR6004) ? AR6004_VTOP(vaddr) : 0))
+
+#define AR6003_REV2_APP_LOAD_ADDRESS 0x543180
+#define AR6003_REV2_BOARD_EXT_DATA_ADDRESS 0x57E500
+#define AR6003_REV2_DATASET_PATCH_ADDRESS 0x57e884
+#define AR6003_REV2_RAM_RESERVE_SIZE 6912
+
+#define AR6003_REV3_APP_LOAD_ADDRESS 0x545000
+#define AR6003_REV3_BOARD_EXT_DATA_ADDRESS 0x542330
+#define AR6003_REV3_DATASET_PATCH_ADDRESS 0x57FF74
+#define AR6003_REV3_RAM_RESERVE_SIZE 512
+
+#define AR6004_REV1_BOARD_DATA_ADDRESS 0x435400
+#define AR6004_REV1_BOARD_EXT_DATA_ADDRESS 0x437000
+#define AR6004_REV1_RAM_RESERVE_SIZE 11264
+
+#define ATH6KL_FWLOG_PAYLOAD_SIZE 1500
+
+struct ath6kl_dbglog_buf {
+ __le32 next;
+ __le32 buffer_addr;
+ __le32 bufsize;
+ __le32 length;
+ __le32 count;
+ __le32 free;
+} __packed;
+
+struct ath6kl_dbglog_hdr {
+ __le32 dbuf_addr;
+ __le32 dropped;
+} __packed;
+
+#endif
diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c
new file mode 100644
index 000000000000..381eb66a605f
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/testmode.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "testmode.h"
+
+#include <net/netlink.h>
+
+enum ath6kl_tm_attr {
+ __ATH6KL_TM_ATTR_INVALID = 0,
+ ATH6KL_TM_ATTR_CMD = 1,
+ ATH6KL_TM_ATTR_DATA = 2,
+
+ /* keep last */
+ __ATH6KL_TM_ATTR_AFTER_LAST,
+ ATH6KL_TM_ATTR_MAX = __ATH6KL_TM_ATTR_AFTER_LAST - 1,
+};
+
+enum ath6kl_tm_cmd {
+ ATH6KL_TM_CMD_TCMD = 0,
+ ATH6KL_TM_CMD_RX_REPORT = 1,
+};
+
+#define ATH6KL_TM_DATA_MAX_LEN 5000
+
+static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = {
+ [ATH6KL_TM_ATTR_CMD] = { .type = NLA_U32 },
+ [ATH6KL_TM_ATTR_DATA] = { .type = NLA_BINARY,
+ .len = ATH6KL_TM_DATA_MAX_LEN },
+};
+
+void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len)
+{
+ if (down_interruptible(&ar->sem))
+ return;
+
+ kfree(ar->tm.rx_report);
+
+ ar->tm.rx_report = kmemdup(buf, buf_len, GFP_KERNEL);
+ ar->tm.rx_report_len = buf_len;
+
+ up(&ar->sem);
+
+ wake_up(&ar->event_wq);
+}
+
+static int ath6kl_tm_rx_report(struct ath6kl *ar, void *buf, size_t buf_len,
+ struct sk_buff *skb)
+{
+ int ret = 0;
+ long left;
+
+ if (down_interruptible(&ar->sem))
+ return -ERESTARTSYS;
+
+ if (!test_bit(WMI_READY, &ar->flag)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len) < 0) {
+ up(&ar->sem);
+ return -EIO;
+ }
+
+ left = wait_event_interruptible_timeout(ar->event_wq,
+ ar->tm.rx_report != NULL,
+ WMI_TIMEOUT);
+
+ if (left == 0) {
+ ret = -ETIMEDOUT;
+ goto out;
+ } else if (left < 0) {
+ ret = left;
+ goto out;
+ }
+
+ if (ar->tm.rx_report == NULL || ar->tm.rx_report_len == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, ar->tm.rx_report_len,
+ ar->tm.rx_report);
+
+ kfree(ar->tm.rx_report);
+ ar->tm.rx_report = NULL;
+
+out:
+ up(&ar->sem);
+
+ return ret;
+
+nla_put_failure:
+ ret = -ENOBUFS;
+ goto out;
+}
+
+int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
+{
+ struct ath6kl *ar = wiphy_priv(wiphy);
+ struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1];
+ int err, buf_len, reply_len;
+ struct sk_buff *skb;
+ void *buf;
+
+ err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len,
+ ath6kl_tm_policy);
+ if (err)
+ return err;
+
+ if (!tb[ATH6KL_TM_ATTR_CMD])
+ return -EINVAL;
+
+ switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) {
+ case ATH6KL_TM_CMD_TCMD:
+ if (!tb[ATH6KL_TM_ATTR_DATA])
+ return -EINVAL;
+
+ buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]);
+ buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]);
+
+ ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len);
+
+ return 0;
+
+ break;
+ case ATH6KL_TM_CMD_RX_REPORT:
+ if (!tb[ATH6KL_TM_ATTR_DATA])
+ return -EINVAL;
+
+ buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]);
+ buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]);
+
+ reply_len = nla_total_size(ATH6KL_TM_DATA_MAX_LEN);
+ skb = cfg80211_testmode_alloc_reply_skb(wiphy, reply_len);
+ if (!skb)
+ return -ENOMEM;
+
+ err = ath6kl_tm_rx_report(ar, buf, buf_len, skb);
+ if (err < 0) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ return cfg80211_testmode_reply(skb);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
diff --git a/drivers/net/wireless/ath/ath6kl/testmode.h b/drivers/net/wireless/ath/ath6kl/testmode.h
new file mode 100644
index 000000000000..43dffcc11fb1
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/testmode.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+
+#ifdef CONFIG_NL80211_TESTMODE
+
+void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len);
+int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len);
+
+#else
+
+static inline void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf,
+ size_t buf_len)
+{
+}
+
+static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
+{
+ return 0;
+}
+
+#endif
diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c
new file mode 100644
index 000000000000..a7117074f81c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/txrx.c
@@ -0,0 +1,1478 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "debug.h"
+
+static u8 ath6kl_ibss_map_epid(struct sk_buff *skb, struct net_device *dev,
+ u32 *map_no)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ struct ethhdr *eth_hdr;
+ u32 i, ep_map = -1;
+ u8 *datap;
+
+ *map_no = 0;
+ datap = skb->data;
+ eth_hdr = (struct ethhdr *) (datap + sizeof(struct wmi_data_hdr));
+
+ if (is_multicast_ether_addr(eth_hdr->h_dest))
+ return ENDPOINT_2;
+
+ for (i = 0; i < ar->node_num; i++) {
+ if (memcmp(eth_hdr->h_dest, ar->node_map[i].mac_addr,
+ ETH_ALEN) == 0) {
+ *map_no = i + 1;
+ ar->node_map[i].tx_pend++;
+ return ar->node_map[i].ep_id;
+ }
+
+ if ((ep_map == -1) && !ar->node_map[i].tx_pend)
+ ep_map = i;
+ }
+
+ if (ep_map == -1) {
+ ep_map = ar->node_num;
+ ar->node_num++;
+ if (ar->node_num > MAX_NODE_NUM)
+ return ENDPOINT_UNUSED;
+ }
+
+ memcpy(ar->node_map[ep_map].mac_addr, eth_hdr->h_dest, ETH_ALEN);
+
+ for (i = ENDPOINT_2; i <= ENDPOINT_5; i++) {
+ if (!ar->tx_pending[i]) {
+ ar->node_map[ep_map].ep_id = i;
+ break;
+ }
+
+ /*
+ * No free endpoint is available, start redistribution on
+ * the inuse endpoints.
+ */
+ if (i == ENDPOINT_5) {
+ ar->node_map[ep_map].ep_id = ar->next_ep_id;
+ ar->next_ep_id++;
+ if (ar->next_ep_id > ENDPOINT_5)
+ ar->next_ep_id = ENDPOINT_2;
+ }
+ }
+
+ *map_no = ep_map + 1;
+ ar->node_map[ep_map].tx_pend++;
+
+ return ar->node_map[ep_map].ep_id;
+}
+
+static bool ath6kl_powersave_ap(struct ath6kl *ar, struct sk_buff *skb,
+ bool *more_data)
+{
+ struct ethhdr *datap = (struct ethhdr *) skb->data;
+ struct ath6kl_sta *conn = NULL;
+ bool ps_queued = false, is_psq_empty = false;
+
+ if (is_multicast_ether_addr(datap->h_dest)) {
+ u8 ctr = 0;
+ bool q_mcast = false;
+
+ for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
+ if (ar->sta_list[ctr].sta_flags & STA_PS_SLEEP) {
+ q_mcast = true;
+ break;
+ }
+ }
+
+ if (q_mcast) {
+ /*
+ * If this transmit is not because of a Dtim Expiry
+ * q it.
+ */
+ if (!test_bit(DTIM_EXPIRED, &ar->flag)) {
+ bool is_mcastq_empty = false;
+
+ spin_lock_bh(&ar->mcastpsq_lock);
+ is_mcastq_empty =
+ skb_queue_empty(&ar->mcastpsq);
+ skb_queue_tail(&ar->mcastpsq, skb);
+ spin_unlock_bh(&ar->mcastpsq_lock);
+
+ /*
+ * If this is the first Mcast pkt getting
+ * queued indicate to the target to set the
+ * BitmapControl LSB of the TIM IE.
+ */
+ if (is_mcastq_empty)
+ ath6kl_wmi_set_pvb_cmd(ar->wmi,
+ MCAST_AID, 1);
+
+ ps_queued = true;
+ } else {
+ /*
+ * This transmit is because of Dtim expiry.
+ * Determine if MoreData bit has to be set.
+ */
+ spin_lock_bh(&ar->mcastpsq_lock);
+ if (!skb_queue_empty(&ar->mcastpsq))
+ *more_data = true;
+ spin_unlock_bh(&ar->mcastpsq_lock);
+ }
+ }
+ } else {
+ conn = ath6kl_find_sta(ar, datap->h_dest);
+ if (!conn) {
+ dev_kfree_skb(skb);
+
+ /* Inform the caller that the skb is consumed */
+ return true;
+ }
+
+ if (conn->sta_flags & STA_PS_SLEEP) {
+ if (!(conn->sta_flags & STA_PS_POLLED)) {
+ /* Queue the frames if the STA is sleeping */
+ spin_lock_bh(&conn->psq_lock);
+ is_psq_empty = skb_queue_empty(&conn->psq);
+ skb_queue_tail(&conn->psq, skb);
+ spin_unlock_bh(&conn->psq_lock);
+
+ /*
+ * If this is the first pkt getting queued
+ * for this STA, update the PVB for this
+ * STA.
+ */
+ if (is_psq_empty)
+ ath6kl_wmi_set_pvb_cmd(ar->wmi,
+ conn->aid, 1);
+
+ ps_queued = true;
+ } else {
+ /*
+ * This tx is because of a PsPoll.
+ * Determine if MoreData bit has to be set.
+ */
+ spin_lock_bh(&conn->psq_lock);
+ if (!skb_queue_empty(&conn->psq))
+ *more_data = true;
+ spin_unlock_bh(&conn->psq_lock);
+ }
+ }
+ }
+
+ return ps_queued;
+}
+
+/* Tx functions */
+
+int ath6kl_control_tx(void *devt, struct sk_buff *skb,
+ enum htc_endpoint_id eid)
+{
+ struct ath6kl *ar = devt;
+ int status = 0;
+ struct ath6kl_cookie *cookie = NULL;
+
+ spin_lock_bh(&ar->lock);
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
+ "%s: skb=0x%p, len=0x%x eid =%d\n", __func__,
+ skb, skb->len, eid);
+
+ if (test_bit(WMI_CTRL_EP_FULL, &ar->flag) && (eid == ar->ctrl_ep)) {
+ /*
+ * Control endpoint is full, don't allocate resources, we
+ * are just going to drop this packet.
+ */
+ cookie = NULL;
+ ath6kl_err("wmi ctrl ep full, dropping pkt : 0x%p, len:%d\n",
+ skb, skb->len);
+ } else
+ cookie = ath6kl_alloc_cookie(ar);
+
+ if (cookie == NULL) {
+ spin_unlock_bh(&ar->lock);
+ status = -ENOMEM;
+ goto fail_ctrl_tx;
+ }
+
+ ar->tx_pending[eid]++;
+
+ if (eid != ar->ctrl_ep)
+ ar->total_tx_data_pend++;
+
+ spin_unlock_bh(&ar->lock);
+
+ cookie->skb = skb;
+ cookie->map_no = 0;
+ set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
+ eid, ATH6KL_CONTROL_PKT_TAG);
+
+ /*
+ * This interface is asynchronous, if there is an error, cleanup
+ * will happen in the TX completion callback.
+ */
+ ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt);
+
+ return 0;
+
+fail_ctrl_tx:
+ dev_kfree_skb(skb);
+ return status;
+}
+
+int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_cookie *cookie = NULL;
+ enum htc_endpoint_id eid = ENDPOINT_UNUSED;
+ u32 map_no = 0;
+ u16 htc_tag = ATH6KL_DATA_PKT_TAG;
+ u8 ac = 99 ; /* initialize to unmapped ac */
+ bool chk_adhoc_ps_mapping = false, more_data = false;
+ int ret;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
+ "%s: skb=0x%p, data=0x%p, len=0x%x\n", __func__,
+ skb, skb->data, skb->len);
+
+ /* If target is not associated */
+ if (!test_bit(CONNECTED, &ar->flag)) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ if (!test_bit(WMI_READY, &ar->flag))
+ goto fail_tx;
+
+ /* AP mode Power saving processing */
+ if (ar->nw_type == AP_NETWORK) {
+ if (ath6kl_powersave_ap(ar, skb, &more_data))
+ return 0;
+ }
+
+ if (test_bit(WMI_ENABLED, &ar->flag)) {
+ if (skb_headroom(skb) < dev->needed_headroom) {
+ WARN_ON(1);
+ goto fail_tx;
+ }
+
+ if (ath6kl_wmi_dix_2_dot3(ar->wmi, skb)) {
+ ath6kl_err("ath6kl_wmi_dix_2_dot3 failed\n");
+ goto fail_tx;
+ }
+
+ if (ath6kl_wmi_data_hdr_add(ar->wmi, skb, DATA_MSGTYPE,
+ more_data, 0, 0, NULL)) {
+ ath6kl_err("wmi_data_hdr_add failed\n");
+ goto fail_tx;
+ }
+
+ if ((ar->nw_type == ADHOC_NETWORK) &&
+ ar->ibss_ps_enable && test_bit(CONNECTED, &ar->flag))
+ chk_adhoc_ps_mapping = true;
+ else {
+ /* get the stream mapping */
+ ret = ath6kl_wmi_implicit_create_pstream(ar->wmi, skb,
+ 0, test_bit(WMM_ENABLED, &ar->flag), &ac);
+ if (ret)
+ goto fail_tx;
+ }
+ } else
+ goto fail_tx;
+
+ spin_lock_bh(&ar->lock);
+
+ if (chk_adhoc_ps_mapping)
+ eid = ath6kl_ibss_map_epid(skb, dev, &map_no);
+ else
+ eid = ar->ac2ep_map[ac];
+
+ if (eid == 0 || eid == ENDPOINT_UNUSED) {
+ ath6kl_err("eid %d is not mapped!\n", eid);
+ spin_unlock_bh(&ar->lock);
+ goto fail_tx;
+ }
+
+ /* allocate resource for this packet */
+ cookie = ath6kl_alloc_cookie(ar);
+
+ if (!cookie) {
+ spin_unlock_bh(&ar->lock);
+ goto fail_tx;
+ }
+
+ /* update counts while the lock is held */
+ ar->tx_pending[eid]++;
+ ar->total_tx_data_pend++;
+
+ spin_unlock_bh(&ar->lock);
+
+ if (!IS_ALIGNED((unsigned long) skb->data - HTC_HDR_LENGTH, 4) &&
+ skb_cloned(skb)) {
+ /*
+ * We will touch (move the buffer data to align it. Since the
+ * skb buffer is cloned and not only the header is changed, we
+ * have to copy it to allow the changes. Since we are copying
+ * the data here, we may as well align it by reserving suitable
+ * headroom to avoid the memmove in ath6kl_htc_tx_buf_align().
+ */
+ struct sk_buff *nskb;
+
+ nskb = skb_copy_expand(skb, HTC_HDR_LENGTH, 0, GFP_ATOMIC);
+ if (nskb == NULL)
+ goto fail_tx;
+ kfree_skb(skb);
+ skb = nskb;
+ }
+
+ cookie->skb = skb;
+ cookie->map_no = map_no;
+ set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
+ eid, htc_tag);
+
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ",
+ skb->data, skb->len);
+
+ /*
+ * HTC interface is asynchronous, if this fails, cleanup will
+ * happen in the ath6kl_tx_complete callback.
+ */
+ ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt);
+
+ return 0;
+
+fail_tx:
+ dev_kfree_skb(skb);
+
+ ar->net_stats.tx_dropped++;
+ ar->net_stats.tx_aborted_errors++;
+
+ return 0;
+}
+
+/* indicate tx activity or inactivity on a WMI stream */
+void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active)
+{
+ struct ath6kl *ar = devt;
+ enum htc_endpoint_id eid;
+ int i;
+
+ eid = ar->ac2ep_map[traffic_class];
+
+ if (!test_bit(WMI_ENABLED, &ar->flag))
+ goto notify_htc;
+
+ spin_lock_bh(&ar->lock);
+
+ ar->ac_stream_active[traffic_class] = active;
+
+ if (active) {
+ /*
+ * Keep track of the active stream with the highest
+ * priority.
+ */
+ if (ar->ac_stream_pri_map[traffic_class] >
+ ar->hiac_stream_active_pri)
+ /* set the new highest active priority */
+ ar->hiac_stream_active_pri =
+ ar->ac_stream_pri_map[traffic_class];
+
+ } else {
+ /*
+ * We may have to search for the next active stream
+ * that is the highest priority.
+ */
+ if (ar->hiac_stream_active_pri ==
+ ar->ac_stream_pri_map[traffic_class]) {
+ /*
+ * The highest priority stream just went inactive
+ * reset and search for the "next" highest "active"
+ * priority stream.
+ */
+ ar->hiac_stream_active_pri = 0;
+
+ for (i = 0; i < WMM_NUM_AC; i++) {
+ if (ar->ac_stream_active[i] &&
+ (ar->ac_stream_pri_map[i] >
+ ar->hiac_stream_active_pri))
+ /*
+ * Set the new highest active
+ * priority.
+ */
+ ar->hiac_stream_active_pri =
+ ar->ac_stream_pri_map[i];
+ }
+ }
+ }
+
+ spin_unlock_bh(&ar->lock);
+
+notify_htc:
+ /* notify HTC, this may cause credit distribution changes */
+ ath6kl_htc_indicate_activity_change(ar->htc_target, eid, active);
+}
+
+enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
+ struct htc_packet *packet)
+{
+ struct ath6kl *ar = target->dev->ar;
+ enum htc_endpoint_id endpoint = packet->endpoint;
+
+ if (endpoint == ar->ctrl_ep) {
+ /*
+ * Under normal WMI if this is getting full, then something
+ * is running rampant the host should not be exhausting the
+ * WMI queue with too many commands the only exception to
+ * this is during testing using endpointping.
+ */
+ spin_lock_bh(&ar->lock);
+ set_bit(WMI_CTRL_EP_FULL, &ar->flag);
+ spin_unlock_bh(&ar->lock);
+ ath6kl_err("wmi ctrl ep is full\n");
+ return HTC_SEND_FULL_KEEP;
+ }
+
+ if (packet->info.tx.tag == ATH6KL_CONTROL_PKT_TAG)
+ return HTC_SEND_FULL_KEEP;
+
+ if (ar->nw_type == ADHOC_NETWORK)
+ /*
+ * In adhoc mode, we cannot differentiate traffic
+ * priorities so there is no need to continue, however we
+ * should stop the network.
+ */
+ goto stop_net_queues;
+
+ /*
+ * The last MAX_HI_COOKIE_NUM "batch" of cookies are reserved for
+ * the highest active stream.
+ */
+ if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] <
+ ar->hiac_stream_active_pri &&
+ ar->cookie_count <= MAX_HI_COOKIE_NUM)
+ /*
+ * Give preference to the highest priority stream by
+ * dropping the packets which overflowed.
+ */
+ return HTC_SEND_FULL_DROP;
+
+stop_net_queues:
+ spin_lock_bh(&ar->lock);
+ set_bit(NETQ_STOPPED, &ar->flag);
+ spin_unlock_bh(&ar->lock);
+ netif_stop_queue(ar->net_dev);
+
+ return HTC_SEND_FULL_KEEP;
+}
+
+/* TODO this needs to be looked at */
+static void ath6kl_tx_clear_node_map(struct ath6kl *ar,
+ enum htc_endpoint_id eid, u32 map_no)
+{
+ u32 i;
+
+ if (ar->nw_type != ADHOC_NETWORK)
+ return;
+
+ if (!ar->ibss_ps_enable)
+ return;
+
+ if (eid == ar->ctrl_ep)
+ return;
+
+ if (map_no == 0)
+ return;
+
+ map_no--;
+ ar->node_map[map_no].tx_pend--;
+
+ if (ar->node_map[map_no].tx_pend)
+ return;
+
+ if (map_no != (ar->node_num - 1))
+ return;
+
+ for (i = ar->node_num; i > 0; i--) {
+ if (ar->node_map[i - 1].tx_pend)
+ break;
+
+ memset(&ar->node_map[i - 1], 0,
+ sizeof(struct ath6kl_node_mapping));
+ ar->node_num--;
+ }
+}
+
+void ath6kl_tx_complete(void *context, struct list_head *packet_queue)
+{
+ struct ath6kl *ar = context;
+ struct sk_buff_head skb_queue;
+ struct htc_packet *packet;
+ struct sk_buff *skb;
+ struct ath6kl_cookie *ath6kl_cookie;
+ u32 map_no = 0;
+ int status;
+ enum htc_endpoint_id eid;
+ bool wake_event = false;
+ bool flushing = false;
+
+ skb_queue_head_init(&skb_queue);
+
+ /* lock the driver as we update internal state */
+ spin_lock_bh(&ar->lock);
+
+ /* reap completed packets */
+ while (!list_empty(packet_queue)) {
+
+ packet = list_first_entry(packet_queue, struct htc_packet,
+ list);
+ list_del(&packet->list);
+
+ ath6kl_cookie = (struct ath6kl_cookie *)packet->pkt_cntxt;
+ if (!ath6kl_cookie)
+ goto fatal;
+
+ status = packet->status;
+ skb = ath6kl_cookie->skb;
+ eid = packet->endpoint;
+ map_no = ath6kl_cookie->map_no;
+
+ if (!skb || !skb->data)
+ goto fatal;
+
+ packet->buf = skb->data;
+
+ __skb_queue_tail(&skb_queue, skb);
+
+ if (!status && (packet->act_len != skb->len))
+ goto fatal;
+
+ ar->tx_pending[eid]--;
+
+ if (eid != ar->ctrl_ep)
+ ar->total_tx_data_pend--;
+
+ if (eid == ar->ctrl_ep) {
+ if (test_bit(WMI_CTRL_EP_FULL, &ar->flag))
+ clear_bit(WMI_CTRL_EP_FULL, &ar->flag);
+
+ if (ar->tx_pending[eid] == 0)
+ wake_event = true;
+ }
+
+ if (status) {
+ if (status == -ECANCELED)
+ /* a packet was flushed */
+ flushing = true;
+
+ ar->net_stats.tx_errors++;
+
+ if (status != -ENOSPC)
+ ath6kl_err("tx error, status: 0x%x\n", status);
+ ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
+ "%s: skb=0x%p data=0x%p len=0x%x eid=%d %s\n",
+ __func__, skb, packet->buf, packet->act_len,
+ eid, "error!");
+ } else {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
+ "%s: skb=0x%p data=0x%p len=0x%x eid=%d %s\n",
+ __func__, skb, packet->buf, packet->act_len,
+ eid, "OK");
+
+ flushing = false;
+ ar->net_stats.tx_packets++;
+ ar->net_stats.tx_bytes += skb->len;
+ }
+
+ ath6kl_tx_clear_node_map(ar, eid, map_no);
+
+ ath6kl_free_cookie(ar, ath6kl_cookie);
+
+ if (test_bit(NETQ_STOPPED, &ar->flag))
+ clear_bit(NETQ_STOPPED, &ar->flag);
+ }
+
+ spin_unlock_bh(&ar->lock);
+
+ __skb_queue_purge(&skb_queue);
+
+ if (test_bit(CONNECTED, &ar->flag)) {
+ if (!flushing)
+ netif_wake_queue(ar->net_dev);
+ }
+
+ if (wake_event)
+ wake_up(&ar->event_wq);
+
+ return;
+
+fatal:
+ WARN_ON(1);
+ spin_unlock_bh(&ar->lock);
+ return;
+}
+
+void ath6kl_tx_data_cleanup(struct ath6kl *ar)
+{
+ int i;
+
+ /* flush all the data (non-control) streams */
+ for (i = 0; i < WMM_NUM_AC; i++)
+ ath6kl_htc_flush_txep(ar->htc_target, ar->ac2ep_map[i],
+ ATH6KL_DATA_PKT_TAG);
+}
+
+/* Rx functions */
+
+static void ath6kl_deliver_frames_to_nw_stack(struct net_device *dev,
+ struct sk_buff *skb)
+{
+ if (!skb)
+ return;
+
+ skb->dev = dev;
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ netif_rx_ni(skb);
+}
+
+static void ath6kl_alloc_netbufs(struct sk_buff_head *q, u16 num)
+{
+ struct sk_buff *skb;
+
+ while (num) {
+ skb = ath6kl_buf_alloc(ATH6KL_BUFFER_SIZE);
+ if (!skb) {
+ ath6kl_err("netbuf allocation failed\n");
+ return;
+ }
+ skb_queue_tail(q, skb);
+ num--;
+ }
+}
+
+static struct sk_buff *aggr_get_free_skb(struct aggr_info *p_aggr)
+{
+ struct sk_buff *skb = NULL;
+
+ if (skb_queue_len(&p_aggr->free_q) < (AGGR_NUM_OF_FREE_NETBUFS >> 2))
+ ath6kl_alloc_netbufs(&p_aggr->free_q, AGGR_NUM_OF_FREE_NETBUFS);
+
+ skb = skb_dequeue(&p_aggr->free_q);
+
+ return skb;
+}
+
+void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint)
+{
+ struct ath6kl *ar = target->dev->ar;
+ struct sk_buff *skb;
+ int rx_buf;
+ int n_buf_refill;
+ struct htc_packet *packet;
+ struct list_head queue;
+
+ n_buf_refill = ATH6KL_MAX_RX_BUFFERS -
+ ath6kl_htc_get_rxbuf_num(ar->htc_target, endpoint);
+
+ if (n_buf_refill <= 0)
+ return;
+
+ INIT_LIST_HEAD(&queue);
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_RX,
+ "%s: providing htc with %d buffers at eid=%d\n",
+ __func__, n_buf_refill, endpoint);
+
+ for (rx_buf = 0; rx_buf < n_buf_refill; rx_buf++) {
+ skb = ath6kl_buf_alloc(ATH6KL_BUFFER_SIZE);
+ if (!skb)
+ break;
+
+ packet = (struct htc_packet *) skb->head;
+ if (!IS_ALIGNED((unsigned long) skb->data, 4))
+ skb->data = PTR_ALIGN(skb->data - 4, 4);
+ set_htc_rxpkt_info(packet, skb, skb->data,
+ ATH6KL_BUFFER_SIZE, endpoint);
+ list_add_tail(&packet->list, &queue);
+ }
+
+ if (!list_empty(&queue))
+ ath6kl_htc_add_rxbuf_multiple(ar->htc_target, &queue);
+}
+
+void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count)
+{
+ struct htc_packet *packet;
+ struct sk_buff *skb;
+
+ while (count) {
+ skb = ath6kl_buf_alloc(ATH6KL_AMSDU_BUFFER_SIZE);
+ if (!skb)
+ return;
+
+ packet = (struct htc_packet *) skb->head;
+ if (!IS_ALIGNED((unsigned long) skb->data, 4))
+ skb->data = PTR_ALIGN(skb->data - 4, 4);
+ set_htc_rxpkt_info(packet, skb, skb->data,
+ ATH6KL_AMSDU_BUFFER_SIZE, 0);
+ spin_lock_bh(&ar->lock);
+ list_add_tail(&packet->list, &ar->amsdu_rx_buffer_queue);
+ spin_unlock_bh(&ar->lock);
+ count--;
+ }
+}
+
+/*
+ * Callback to allocate a receive buffer for a pending packet. We use a
+ * pre-allocated list of buffers of maximum AMSDU size (4K).
+ */
+struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target,
+ enum htc_endpoint_id endpoint,
+ int len)
+{
+ struct ath6kl *ar = target->dev->ar;
+ struct htc_packet *packet = NULL;
+ struct list_head *pkt_pos;
+ int refill_cnt = 0, depth = 0;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: eid=%d, len:%d\n",
+ __func__, endpoint, len);
+
+ if ((len <= ATH6KL_BUFFER_SIZE) ||
+ (len > ATH6KL_AMSDU_BUFFER_SIZE))
+ return NULL;
+
+ spin_lock_bh(&ar->lock);
+
+ if (list_empty(&ar->amsdu_rx_buffer_queue)) {
+ spin_unlock_bh(&ar->lock);
+ refill_cnt = ATH6KL_MAX_AMSDU_RX_BUFFERS;
+ goto refill_buf;
+ }
+
+ packet = list_first_entry(&ar->amsdu_rx_buffer_queue,
+ struct htc_packet, list);
+ list_del(&packet->list);
+ list_for_each(pkt_pos, &ar->amsdu_rx_buffer_queue)
+ depth++;
+
+ refill_cnt = ATH6KL_MAX_AMSDU_RX_BUFFERS - depth;
+ spin_unlock_bh(&ar->lock);
+
+ /* set actual endpoint ID */
+ packet->endpoint = endpoint;
+
+refill_buf:
+ if (refill_cnt >= ATH6KL_AMSDU_REFILL_THRESHOLD)
+ ath6kl_refill_amsdu_rxbufs(ar, refill_cnt);
+
+ return packet;
+}
+
+static void aggr_slice_amsdu(struct aggr_info *p_aggr,
+ struct rxtid *rxtid, struct sk_buff *skb)
+{
+ struct sk_buff *new_skb;
+ struct ethhdr *hdr;
+ u16 frame_8023_len, payload_8023_len, mac_hdr_len, amsdu_len;
+ u8 *framep;
+
+ mac_hdr_len = sizeof(struct ethhdr);
+ framep = skb->data + mac_hdr_len;
+ amsdu_len = skb->len - mac_hdr_len;
+
+ while (amsdu_len > mac_hdr_len) {
+ hdr = (struct ethhdr *) framep;
+ payload_8023_len = ntohs(hdr->h_proto);
+
+ if (payload_8023_len < MIN_MSDU_SUBFRAME_PAYLOAD_LEN ||
+ payload_8023_len > MAX_MSDU_SUBFRAME_PAYLOAD_LEN) {
+ ath6kl_err("802.3 AMSDU frame bound check failed. len %d\n",
+ payload_8023_len);
+ break;
+ }
+
+ frame_8023_len = payload_8023_len + mac_hdr_len;
+ new_skb = aggr_get_free_skb(p_aggr);
+ if (!new_skb) {
+ ath6kl_err("no buffer available\n");
+ break;
+ }
+
+ memcpy(new_skb->data, framep, frame_8023_len);
+ skb_put(new_skb, frame_8023_len);
+ if (ath6kl_wmi_dot3_2_dix(new_skb)) {
+ ath6kl_err("dot3_2_dix error\n");
+ dev_kfree_skb(new_skb);
+ break;
+ }
+
+ skb_queue_tail(&rxtid->q, new_skb);
+
+ /* Is this the last subframe within this aggregate ? */
+ if ((amsdu_len - frame_8023_len) == 0)
+ break;
+
+ /* Add the length of A-MSDU subframe padding bytes -
+ * Round to nearest word.
+ */
+ frame_8023_len = ALIGN(frame_8023_len, 4);
+
+ framep += frame_8023_len;
+ amsdu_len -= frame_8023_len;
+ }
+
+ dev_kfree_skb(skb);
+}
+
+static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid,
+ u16 seq_no, u8 order)
+{
+ struct sk_buff *skb;
+ struct rxtid *rxtid;
+ struct skb_hold_q *node;
+ u16 idx, idx_end, seq_end;
+ struct rxtid_stats *stats;
+
+ if (!p_aggr)
+ return;
+
+ rxtid = &p_aggr->rx_tid[tid];
+ stats = &p_aggr->stat[tid];
+
+ idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
+
+ /*
+ * idx_end is typically the last possible frame in the window,
+ * but changes to 'the' seq_no, when BAR comes. If seq_no
+ * is non-zero, we will go up to that and stop.
+ * Note: last seq no in current window will occupy the same
+ * index position as index that is just previous to start.
+ * An imp point : if win_sz is 7, for seq_no space of 4095,
+ * then, there would be holes when sequence wrap around occurs.
+ * Target should judiciously choose the win_sz, based on
+ * this condition. For 4095, (TID_WINDOW_SZ = 2 x win_sz
+ * 2, 4, 8, 16 win_sz works fine).
+ * We must deque from "idx" to "idx_end", including both.
+ */
+ seq_end = seq_no ? seq_no : rxtid->seq_next;
+ idx_end = AGGR_WIN_IDX(seq_end, rxtid->hold_q_sz);
+
+ spin_lock_bh(&rxtid->lock);
+
+ do {
+ node = &rxtid->hold_q[idx];
+ if ((order == 1) && (!node->skb))
+ break;
+
+ if (node->skb) {
+ if (node->is_amsdu)
+ aggr_slice_amsdu(p_aggr, rxtid, node->skb);
+ else
+ skb_queue_tail(&rxtid->q, node->skb);
+ node->skb = NULL;
+ } else
+ stats->num_hole++;
+
+ rxtid->seq_next = ATH6KL_NEXT_SEQ_NO(rxtid->seq_next);
+ idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
+ } while (idx != idx_end);
+
+ spin_unlock_bh(&rxtid->lock);
+
+ stats->num_delivered += skb_queue_len(&rxtid->q);
+
+ while ((skb = skb_dequeue(&rxtid->q)))
+ ath6kl_deliver_frames_to_nw_stack(p_aggr->dev, skb);
+}
+
+static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid,
+ u16 seq_no,
+ bool is_amsdu, struct sk_buff *frame)
+{
+ struct rxtid *rxtid;
+ struct rxtid_stats *stats;
+ struct sk_buff *skb;
+ struct skb_hold_q *node;
+ u16 idx, st, cur, end;
+ bool is_queued = false;
+ u16 extended_end;
+
+ rxtid = &agg_info->rx_tid[tid];
+ stats = &agg_info->stat[tid];
+
+ stats->num_into_aggr++;
+
+ if (!rxtid->aggr) {
+ if (is_amsdu) {
+ aggr_slice_amsdu(agg_info, rxtid, frame);
+ is_queued = true;
+ stats->num_amsdu++;
+ while ((skb = skb_dequeue(&rxtid->q)))
+ ath6kl_deliver_frames_to_nw_stack(agg_info->dev,
+ skb);
+ }
+ return is_queued;
+ }
+
+ /* Check the incoming sequence no, if it's in the window */
+ st = rxtid->seq_next;
+ cur = seq_no;
+ end = (st + rxtid->hold_q_sz-1) & ATH6KL_MAX_SEQ_NO;
+
+ if (((st < end) && (cur < st || cur > end)) ||
+ ((st > end) && (cur > end) && (cur < st))) {
+ extended_end = (end + rxtid->hold_q_sz - 1) &
+ ATH6KL_MAX_SEQ_NO;
+
+ if (((end < extended_end) &&
+ (cur < end || cur > extended_end)) ||
+ ((end > extended_end) && (cur > extended_end) &&
+ (cur < end))) {
+ aggr_deque_frms(agg_info, tid, 0, 0);
+ if (cur >= rxtid->hold_q_sz - 1)
+ rxtid->seq_next = cur - (rxtid->hold_q_sz - 1);
+ else
+ rxtid->seq_next = ATH6KL_MAX_SEQ_NO -
+ (rxtid->hold_q_sz - 2 - cur);
+ } else {
+ /*
+ * Dequeue only those frames that are outside the
+ * new shifted window.
+ */
+ if (cur >= rxtid->hold_q_sz - 1)
+ st = cur - (rxtid->hold_q_sz - 1);
+ else
+ st = ATH6KL_MAX_SEQ_NO -
+ (rxtid->hold_q_sz - 2 - cur);
+
+ aggr_deque_frms(agg_info, tid, st, 0);
+ }
+
+ stats->num_oow++;
+ }
+
+ idx = AGGR_WIN_IDX(seq_no, rxtid->hold_q_sz);
+
+ node = &rxtid->hold_q[idx];
+
+ spin_lock_bh(&rxtid->lock);
+
+ /*
+ * Is the cur frame duplicate or something beyond our window(hold_q
+ * -> which is 2x, already)?
+ *
+ * 1. Duplicate is easy - drop incoming frame.
+ * 2. Not falling in current sliding window.
+ * 2a. is the frame_seq_no preceding current tid_seq_no?
+ * -> drop the frame. perhaps sender did not get our ACK.
+ * this is taken care of above.
+ * 2b. is the frame_seq_no beyond window(st, TID_WINDOW_SZ);
+ * -> Taken care of it above, by moving window forward.
+ */
+ dev_kfree_skb(node->skb);
+ stats->num_dups++;
+
+ node->skb = frame;
+ is_queued = true;
+ node->is_amsdu = is_amsdu;
+ node->seq_no = seq_no;
+
+ if (node->is_amsdu)
+ stats->num_amsdu++;
+ else
+ stats->num_mpdu++;
+
+ spin_unlock_bh(&rxtid->lock);
+
+ aggr_deque_frms(agg_info, tid, 0, 1);
+
+ if (agg_info->timer_scheduled)
+ rxtid->progress = true;
+ else
+ for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) {
+ if (rxtid->hold_q[idx].skb) {
+ /*
+ * There is a frame in the queue and no
+ * timer so start a timer to ensure that
+ * the frame doesn't remain stuck
+ * forever.
+ */
+ agg_info->timer_scheduled = true;
+ mod_timer(&agg_info->timer,
+ (jiffies +
+ HZ * (AGGR_RX_TIMEOUT) / 1000));
+ rxtid->progress = false;
+ rxtid->timer_mon = true;
+ break;
+ }
+ }
+
+ return is_queued;
+}
+
+void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
+{
+ struct ath6kl *ar = target->dev->ar;
+ struct sk_buff *skb = packet->pkt_cntxt;
+ struct wmi_rx_meta_v2 *meta;
+ struct wmi_data_hdr *dhdr;
+ int min_hdr_len;
+ u8 meta_type, dot11_hdr = 0;
+ int status = packet->status;
+ enum htc_endpoint_id ept = packet->endpoint;
+ bool is_amsdu, prev_ps, ps_state = false;
+ struct ath6kl_sta *conn = NULL;
+ struct sk_buff *skb1 = NULL;
+ struct ethhdr *datap = NULL;
+ u16 seq_no, offset;
+ u8 tid;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_RX,
+ "%s: ar=0x%p eid=%d, skb=0x%p, data=0x%p, len=0x%x status:%d",
+ __func__, ar, ept, skb, packet->buf,
+ packet->act_len, status);
+
+ if (status || !(skb->data + HTC_HDR_LENGTH)) {
+ ar->net_stats.rx_errors++;
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ /*
+ * Take lock to protect buffer counts and adaptive power throughput
+ * state.
+ */
+ spin_lock_bh(&ar->lock);
+
+ ar->net_stats.rx_packets++;
+ ar->net_stats.rx_bytes += packet->act_len;
+
+ spin_unlock_bh(&ar->lock);
+
+ skb_put(skb, packet->act_len + HTC_HDR_LENGTH);
+ skb_pull(skb, HTC_HDR_LENGTH);
+
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ",
+ skb->data, skb->len);
+
+ skb->dev = ar->net_dev;
+
+ if (!test_bit(WMI_ENABLED, &ar->flag)) {
+ if (EPPING_ALIGNMENT_PAD > 0)
+ skb_pull(skb, EPPING_ALIGNMENT_PAD);
+ ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb);
+ return;
+ }
+
+ if (ept == ar->ctrl_ep) {
+ ath6kl_wmi_control_rx(ar->wmi, skb);
+ return;
+ }
+
+ min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) +
+ sizeof(struct ath6kl_llc_snap_hdr);
+
+ dhdr = (struct wmi_data_hdr *) skb->data;
+
+ /*
+ * In the case of AP mode we may receive NULL data frames
+ * that do not have LLC hdr. They are 16 bytes in size.
+ * Allow these frames in the AP mode.
+ */
+ if (ar->nw_type != AP_NETWORK &&
+ ((packet->act_len < min_hdr_len) ||
+ (packet->act_len > WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH))) {
+ ath6kl_info("frame len is too short or too long\n");
+ ar->net_stats.rx_errors++;
+ ar->net_stats.rx_length_errors++;
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ /* Get the Power save state of the STA */
+ if (ar->nw_type == AP_NETWORK) {
+ meta_type = wmi_data_hdr_get_meta(dhdr);
+
+ ps_state = !!((dhdr->info >> WMI_DATA_HDR_PS_SHIFT) &
+ WMI_DATA_HDR_PS_MASK);
+
+ offset = sizeof(struct wmi_data_hdr);
+
+ switch (meta_type) {
+ case 0:
+ break;
+ case WMI_META_VERSION_1:
+ offset += sizeof(struct wmi_rx_meta_v1);
+ break;
+ case WMI_META_VERSION_2:
+ offset += sizeof(struct wmi_rx_meta_v2);
+ break;
+ default:
+ break;
+ }
+
+ datap = (struct ethhdr *) (skb->data + offset);
+ conn = ath6kl_find_sta(ar, datap->h_source);
+
+ if (!conn) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ /*
+ * If there is a change in PS state of the STA,
+ * take appropriate steps:
+ *
+ * 1. If Sleep-->Awake, flush the psq for the STA
+ * Clear the PVB for the STA.
+ * 2. If Awake-->Sleep, Starting queueing frames
+ * the STA.
+ */
+ prev_ps = !!(conn->sta_flags & STA_PS_SLEEP);
+
+ if (ps_state)
+ conn->sta_flags |= STA_PS_SLEEP;
+ else
+ conn->sta_flags &= ~STA_PS_SLEEP;
+
+ if (prev_ps ^ !!(conn->sta_flags & STA_PS_SLEEP)) {
+ if (!(conn->sta_flags & STA_PS_SLEEP)) {
+ struct sk_buff *skbuff = NULL;
+
+ spin_lock_bh(&conn->psq_lock);
+ while ((skbuff = skb_dequeue(&conn->psq))
+ != NULL) {
+ spin_unlock_bh(&conn->psq_lock);
+ ath6kl_data_tx(skbuff, ar->net_dev);
+ spin_lock_bh(&conn->psq_lock);
+ }
+ spin_unlock_bh(&conn->psq_lock);
+ /* Clear the PVB for this STA */
+ ath6kl_wmi_set_pvb_cmd(ar->wmi, conn->aid, 0);
+ }
+ }
+
+ /* drop NULL data frames here */
+ if ((packet->act_len < min_hdr_len) ||
+ (packet->act_len >
+ WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH)) {
+ dev_kfree_skb(skb);
+ return;
+ }
+ }
+
+ is_amsdu = wmi_data_hdr_is_amsdu(dhdr) ? true : false;
+ tid = wmi_data_hdr_get_up(dhdr);
+ seq_no = wmi_data_hdr_get_seqno(dhdr);
+ meta_type = wmi_data_hdr_get_meta(dhdr);
+ dot11_hdr = wmi_data_hdr_get_dot11(dhdr);
+ skb_pull(skb, sizeof(struct wmi_data_hdr));
+
+ switch (meta_type) {
+ case WMI_META_VERSION_1:
+ skb_pull(skb, sizeof(struct wmi_rx_meta_v1));
+ break;
+ case WMI_META_VERSION_2:
+ meta = (struct wmi_rx_meta_v2 *) skb->data;
+ if (meta->csum_flags & 0x1) {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->csum = (__force __wsum) meta->csum;
+ }
+ skb_pull(skb, sizeof(struct wmi_rx_meta_v2));
+ break;
+ default:
+ break;
+ }
+
+ if (dot11_hdr)
+ status = ath6kl_wmi_dot11_hdr_remove(ar->wmi, skb);
+ else if (!is_amsdu)
+ status = ath6kl_wmi_dot3_2_dix(skb);
+
+ if (status) {
+ /*
+ * Drop frames that could not be processed (lack of
+ * memory, etc.)
+ */
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if (!(ar->net_dev->flags & IFF_UP)) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if (ar->nw_type == AP_NETWORK) {
+ datap = (struct ethhdr *) skb->data;
+ if (is_multicast_ether_addr(datap->h_dest))
+ /*
+ * Bcast/Mcast frames should be sent to the
+ * OS stack as well as on the air.
+ */
+ skb1 = skb_copy(skb, GFP_ATOMIC);
+ else {
+ /*
+ * Search for a connected STA with dstMac
+ * as the Mac address. If found send the
+ * frame to it on the air else send the
+ * frame up the stack.
+ */
+ struct ath6kl_sta *conn = NULL;
+ conn = ath6kl_find_sta(ar, datap->h_dest);
+
+ if (conn && ar->intra_bss) {
+ skb1 = skb;
+ skb = NULL;
+ } else if (conn && !ar->intra_bss) {
+ dev_kfree_skb(skb);
+ skb = NULL;
+ }
+ }
+ if (skb1)
+ ath6kl_data_tx(skb1, ar->net_dev);
+ }
+
+ datap = (struct ethhdr *) skb->data;
+
+ if (is_unicast_ether_addr(datap->h_dest) &&
+ aggr_process_recv_frm(ar->aggr_cntxt, tid, seq_no,
+ is_amsdu, skb))
+ /* aggregation code will handle the skb */
+ return;
+
+ ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb);
+}
+
+static void aggr_timeout(unsigned long arg)
+{
+ u8 i, j;
+ struct aggr_info *p_aggr = (struct aggr_info *) arg;
+ struct rxtid *rxtid;
+ struct rxtid_stats *stats;
+
+ for (i = 0; i < NUM_OF_TIDS; i++) {
+ rxtid = &p_aggr->rx_tid[i];
+ stats = &p_aggr->stat[i];
+
+ if (!rxtid->aggr || !rxtid->timer_mon || rxtid->progress)
+ continue;
+
+ stats->num_timeouts++;
+ ath6kl_dbg(ATH6KL_DBG_AGGR,
+ "aggr timeout (st %d end %d)\n",
+ rxtid->seq_next,
+ ((rxtid->seq_next + rxtid->hold_q_sz-1) &
+ ATH6KL_MAX_SEQ_NO));
+ aggr_deque_frms(p_aggr, i, 0, 0);
+ }
+
+ p_aggr->timer_scheduled = false;
+
+ for (i = 0; i < NUM_OF_TIDS; i++) {
+ rxtid = &p_aggr->rx_tid[i];
+
+ if (rxtid->aggr && rxtid->hold_q) {
+ for (j = 0; j < rxtid->hold_q_sz; j++) {
+ if (rxtid->hold_q[j].skb) {
+ p_aggr->timer_scheduled = true;
+ rxtid->timer_mon = true;
+ rxtid->progress = false;
+ break;
+ }
+ }
+
+ if (j >= rxtid->hold_q_sz)
+ rxtid->timer_mon = false;
+ }
+ }
+
+ if (p_aggr->timer_scheduled)
+ mod_timer(&p_aggr->timer,
+ jiffies + msecs_to_jiffies(AGGR_RX_TIMEOUT));
+}
+
+static void aggr_delete_tid_state(struct aggr_info *p_aggr, u8 tid)
+{
+ struct rxtid *rxtid;
+ struct rxtid_stats *stats;
+
+ if (!p_aggr || tid >= NUM_OF_TIDS)
+ return;
+
+ rxtid = &p_aggr->rx_tid[tid];
+ stats = &p_aggr->stat[tid];
+
+ if (rxtid->aggr)
+ aggr_deque_frms(p_aggr, tid, 0, 0);
+
+ rxtid->aggr = false;
+ rxtid->progress = false;
+ rxtid->timer_mon = false;
+ rxtid->win_sz = 0;
+ rxtid->seq_next = 0;
+ rxtid->hold_q_sz = 0;
+
+ kfree(rxtid->hold_q);
+ rxtid->hold_q = NULL;
+
+ memset(stats, 0, sizeof(struct rxtid_stats));
+}
+
+void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no, u8 win_sz)
+{
+ struct aggr_info *p_aggr = ar->aggr_cntxt;
+ struct rxtid *rxtid;
+ struct rxtid_stats *stats;
+ u16 hold_q_size;
+
+ if (!p_aggr)
+ return;
+
+ rxtid = &p_aggr->rx_tid[tid];
+ stats = &p_aggr->stat[tid];
+
+ if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX)
+ ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n",
+ __func__, win_sz, tid);
+
+ if (rxtid->aggr)
+ aggr_delete_tid_state(p_aggr, tid);
+
+ rxtid->seq_next = seq_no;
+ hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q);
+ rxtid->hold_q = kzalloc(hold_q_size, GFP_KERNEL);
+ if (!rxtid->hold_q)
+ return;
+
+ rxtid->win_sz = win_sz;
+ rxtid->hold_q_sz = TID_WINDOW_SZ(win_sz);
+ if (!skb_queue_empty(&rxtid->q))
+ return;
+
+ rxtid->aggr = true;
+}
+
+struct aggr_info *aggr_init(struct net_device *dev)
+{
+ struct aggr_info *p_aggr = NULL;
+ struct rxtid *rxtid;
+ u8 i;
+
+ p_aggr = kzalloc(sizeof(struct aggr_info), GFP_KERNEL);
+ if (!p_aggr) {
+ ath6kl_err("failed to alloc memory for aggr_node\n");
+ return NULL;
+ }
+
+ p_aggr->aggr_sz = AGGR_SZ_DEFAULT;
+ p_aggr->dev = dev;
+ init_timer(&p_aggr->timer);
+ p_aggr->timer.function = aggr_timeout;
+ p_aggr->timer.data = (unsigned long) p_aggr;
+
+ p_aggr->timer_scheduled = false;
+ skb_queue_head_init(&p_aggr->free_q);
+
+ ath6kl_alloc_netbufs(&p_aggr->free_q, AGGR_NUM_OF_FREE_NETBUFS);
+
+ for (i = 0; i < NUM_OF_TIDS; i++) {
+ rxtid = &p_aggr->rx_tid[i];
+ rxtid->aggr = false;
+ rxtid->progress = false;
+ rxtid->timer_mon = false;
+ skb_queue_head_init(&rxtid->q);
+ spin_lock_init(&rxtid->lock);
+ }
+
+ return p_aggr;
+}
+
+void aggr_recv_delba_req_evt(struct ath6kl *ar, u8 tid)
+{
+ struct aggr_info *p_aggr = ar->aggr_cntxt;
+ struct rxtid *rxtid;
+
+ if (!p_aggr)
+ return;
+
+ rxtid = &p_aggr->rx_tid[tid];
+
+ if (rxtid->aggr)
+ aggr_delete_tid_state(p_aggr, tid);
+}
+
+void aggr_reset_state(struct aggr_info *aggr_info)
+{
+ u8 tid;
+
+ for (tid = 0; tid < NUM_OF_TIDS; tid++)
+ aggr_delete_tid_state(aggr_info, tid);
+}
+
+/* clean up our amsdu buffer list */
+void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar)
+{
+ struct htc_packet *packet, *tmp_pkt;
+
+ spin_lock_bh(&ar->lock);
+ if (list_empty(&ar->amsdu_rx_buffer_queue)) {
+ spin_unlock_bh(&ar->lock);
+ return;
+ }
+
+ list_for_each_entry_safe(packet, tmp_pkt, &ar->amsdu_rx_buffer_queue,
+ list) {
+ list_del(&packet->list);
+ spin_unlock_bh(&ar->lock);
+ dev_kfree_skb(packet->pkt_cntxt);
+ spin_lock_bh(&ar->lock);
+ }
+
+ spin_unlock_bh(&ar->lock);
+}
+
+void aggr_module_destroy(struct aggr_info *aggr_info)
+{
+ struct rxtid *rxtid;
+ u8 i, k;
+
+ if (!aggr_info)
+ return;
+
+ if (aggr_info->timer_scheduled) {
+ del_timer(&aggr_info->timer);
+ aggr_info->timer_scheduled = false;
+ }
+
+ for (i = 0; i < NUM_OF_TIDS; i++) {
+ rxtid = &aggr_info->rx_tid[i];
+ if (rxtid->hold_q) {
+ for (k = 0; k < rxtid->hold_q_sz; k++)
+ dev_kfree_skb(rxtid->hold_q[k].skb);
+ kfree(rxtid->hold_q);
+ }
+
+ skb_queue_purge(&rxtid->q);
+ }
+
+ skb_queue_purge(&aggr_info->free_q);
+ kfree(aggr_info);
+}
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
new file mode 100644
index 000000000000..a7de23cbd2c7
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -0,0 +1,3127 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/ip.h>
+#include "core.h"
+#include "debug.h"
+#include "testmode.h"
+#include "../regd.h"
+#include "../regd_common.h"
+
+static int ath6kl_wmi_sync_point(struct wmi *wmi);
+
+static const s32 wmi_rate_tbl[][2] = {
+ /* {W/O SGI, with SGI} */
+ {1000, 1000},
+ {2000, 2000},
+ {5500, 5500},
+ {11000, 11000},
+ {6000, 6000},
+ {9000, 9000},
+ {12000, 12000},
+ {18000, 18000},
+ {24000, 24000},
+ {36000, 36000},
+ {48000, 48000},
+ {54000, 54000},
+ {6500, 7200},
+ {13000, 14400},
+ {19500, 21700},
+ {26000, 28900},
+ {39000, 43300},
+ {52000, 57800},
+ {58500, 65000},
+ {65000, 72200},
+ {13500, 15000},
+ {27000, 30000},
+ {40500, 45000},
+ {54000, 60000},
+ {81000, 90000},
+ {108000, 120000},
+ {121500, 135000},
+ {135000, 150000},
+ {0, 0}
+};
+
+/* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */
+static const u8 up_to_ac[] = {
+ WMM_AC_BE,
+ WMM_AC_BK,
+ WMM_AC_BK,
+ WMM_AC_BE,
+ WMM_AC_VI,
+ WMM_AC_VI,
+ WMM_AC_VO,
+ WMM_AC_VO,
+};
+
+void ath6kl_wmi_set_control_ep(struct wmi *wmi, enum htc_endpoint_id ep_id)
+{
+ if (WARN_ON(ep_id == ENDPOINT_UNUSED || ep_id >= ENDPOINT_MAX))
+ return;
+
+ wmi->ep_id = ep_id;
+}
+
+enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi)
+{
+ return wmi->ep_id;
+}
+
+/* Performs DIX to 802.3 encapsulation for transmit packets.
+ * Assumes the entire DIX header is contigous and that there is
+ * enough room in the buffer for a 802.3 mac header and LLC+SNAP headers.
+ */
+int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb)
+{
+ struct ath6kl_llc_snap_hdr *llc_hdr;
+ struct ethhdr *eth_hdr;
+ size_t new_len;
+ __be16 type;
+ u8 *datap;
+ u16 size;
+
+ if (WARN_ON(skb == NULL))
+ return -EINVAL;
+
+ size = sizeof(struct ath6kl_llc_snap_hdr) + sizeof(struct wmi_data_hdr);
+ if (skb_headroom(skb) < size)
+ return -ENOMEM;
+
+ eth_hdr = (struct ethhdr *) skb->data;
+ type = eth_hdr->h_proto;
+
+ if (!is_ethertype(be16_to_cpu(type))) {
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "%s: pkt is already in 802.3 format\n", __func__);
+ return 0;
+ }
+
+ new_len = skb->len - sizeof(*eth_hdr) + sizeof(*llc_hdr);
+
+ skb_push(skb, sizeof(struct ath6kl_llc_snap_hdr));
+ datap = skb->data;
+
+ eth_hdr->h_proto = cpu_to_be16(new_len);
+
+ memcpy(datap, eth_hdr, sizeof(*eth_hdr));
+
+ llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap + sizeof(*eth_hdr));
+ llc_hdr->dsap = 0xAA;
+ llc_hdr->ssap = 0xAA;
+ llc_hdr->cntl = 0x03;
+ llc_hdr->org_code[0] = 0x0;
+ llc_hdr->org_code[1] = 0x0;
+ llc_hdr->org_code[2] = 0x0;
+ llc_hdr->eth_type = type;
+
+ return 0;
+}
+
+static int ath6kl_wmi_meta_add(struct wmi *wmi, struct sk_buff *skb,
+ u8 *version, void *tx_meta_info)
+{
+ struct wmi_tx_meta_v1 *v1;
+ struct wmi_tx_meta_v2 *v2;
+
+ if (WARN_ON(skb == NULL || version == NULL))
+ return -EINVAL;
+
+ switch (*version) {
+ case WMI_META_VERSION_1:
+ skb_push(skb, WMI_MAX_TX_META_SZ);
+ v1 = (struct wmi_tx_meta_v1 *) skb->data;
+ v1->pkt_id = 0;
+ v1->rate_plcy_id = 0;
+ *version = WMI_META_VERSION_1;
+ break;
+ case WMI_META_VERSION_2:
+ skb_push(skb, WMI_MAX_TX_META_SZ);
+ v2 = (struct wmi_tx_meta_v2 *) skb->data;
+ memcpy(v2, (struct wmi_tx_meta_v2 *) tx_meta_info,
+ sizeof(struct wmi_tx_meta_v2));
+ break;
+ }
+
+ return 0;
+}
+
+int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb,
+ u8 msg_type, bool more_data,
+ enum wmi_data_hdr_data_type data_type,
+ u8 meta_ver, void *tx_meta_info)
+{
+ struct wmi_data_hdr *data_hdr;
+ int ret;
+
+ if (WARN_ON(skb == NULL))
+ return -EINVAL;
+
+ if (tx_meta_info) {
+ ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info);
+ if (ret)
+ return ret;
+ }
+
+ skb_push(skb, sizeof(struct wmi_data_hdr));
+
+ data_hdr = (struct wmi_data_hdr *)skb->data;
+ memset(data_hdr, 0, sizeof(struct wmi_data_hdr));
+
+ data_hdr->info = msg_type << WMI_DATA_HDR_MSG_TYPE_SHIFT;
+ data_hdr->info |= data_type << WMI_DATA_HDR_DATA_TYPE_SHIFT;
+
+ if (more_data)
+ data_hdr->info |=
+ WMI_DATA_HDR_MORE_MASK << WMI_DATA_HDR_MORE_SHIFT;
+
+ data_hdr->info2 = cpu_to_le16(meta_ver << WMI_DATA_HDR_META_SHIFT);
+ data_hdr->info3 = 0;
+
+ return 0;
+}
+
+static u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri)
+{
+ struct iphdr *ip_hdr = (struct iphdr *) pkt;
+ u8 ip_pri;
+
+ /*
+ * Determine IPTOS priority
+ *
+ * IP-TOS - 8bits
+ * : DSCP(6-bits) ECN(2-bits)
+ * : DSCP - P2 P1 P0 X X X
+ * where (P2 P1 P0) form 802.1D
+ */
+ ip_pri = ip_hdr->tos >> 5;
+ ip_pri &= 0x7;
+
+ if ((layer2_pri & 0x7) > ip_pri)
+ return (u8) layer2_pri & 0x7;
+ else
+ return ip_pri;
+}
+
+int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb,
+ u32 layer2_priority, bool wmm_enabled,
+ u8 *ac)
+{
+ struct wmi_data_hdr *data_hdr;
+ struct ath6kl_llc_snap_hdr *llc_hdr;
+ struct wmi_create_pstream_cmd cmd;
+ u32 meta_size, hdr_size;
+ u16 ip_type = IP_ETHERTYPE;
+ u8 stream_exist, usr_pri;
+ u8 traffic_class = WMM_AC_BE;
+ u8 *datap;
+
+ if (WARN_ON(skb == NULL))
+ return -EINVAL;
+
+ datap = skb->data;
+ data_hdr = (struct wmi_data_hdr *) datap;
+
+ meta_size = ((le16_to_cpu(data_hdr->info2) >> WMI_DATA_HDR_META_SHIFT) &
+ WMI_DATA_HDR_META_MASK) ? WMI_MAX_TX_META_SZ : 0;
+
+ if (!wmm_enabled) {
+ /* If WMM is disabled all traffic goes as BE traffic */
+ usr_pri = 0;
+ } else {
+ hdr_size = sizeof(struct ethhdr);
+
+ llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap +
+ sizeof(struct
+ wmi_data_hdr) +
+ meta_size + hdr_size);
+
+ if (llc_hdr->eth_type == htons(ip_type)) {
+ /*
+ * Extract the endpoint info from the TOS field
+ * in the IP header.
+ */
+ usr_pri =
+ ath6kl_wmi_determine_user_priority(((u8 *) llc_hdr) +
+ sizeof(struct ath6kl_llc_snap_hdr),
+ layer2_priority);
+ } else
+ usr_pri = layer2_priority & 0x7;
+ }
+
+ /* workaround for WMM S5 */
+ if ((wmi->traffic_class == WMM_AC_VI) &&
+ ((usr_pri == 5) || (usr_pri == 4)))
+ usr_pri = 1;
+
+ /* Convert user priority to traffic class */
+ traffic_class = up_to_ac[usr_pri & 0x7];
+
+ wmi_data_hdr_set_up(data_hdr, usr_pri);
+
+ spin_lock_bh(&wmi->lock);
+ stream_exist = wmi->fat_pipe_exist;
+ spin_unlock_bh(&wmi->lock);
+
+ if (!(stream_exist & (1 << traffic_class))) {
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.traffic_class = traffic_class;
+ cmd.user_pri = usr_pri;
+ cmd.inactivity_int =
+ cpu_to_le32(WMI_IMPLICIT_PSTREAM_INACTIVITY_INT);
+ /* Implicit streams are created with TSID 0xFF */
+ cmd.tsid = WMI_IMPLICIT_PSTREAM;
+ ath6kl_wmi_create_pstream_cmd(wmi, &cmd);
+ }
+
+ *ac = traffic_class;
+
+ return 0;
+}
+
+int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb)
+{
+ struct ieee80211_hdr_3addr *pwh, wh;
+ struct ath6kl_llc_snap_hdr *llc_hdr;
+ struct ethhdr eth_hdr;
+ u32 hdr_size;
+ u8 *datap;
+ __le16 sub_type;
+
+ if (WARN_ON(skb == NULL))
+ return -EINVAL;
+
+ datap = skb->data;
+ pwh = (struct ieee80211_hdr_3addr *) datap;
+
+ sub_type = pwh->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE);
+
+ memcpy((u8 *) &wh, datap, sizeof(struct ieee80211_hdr_3addr));
+
+ /* Strip off the 802.11 header */
+ if (sub_type == cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
+ hdr_size = roundup(sizeof(struct ieee80211_qos_hdr),
+ sizeof(u32));
+ skb_pull(skb, hdr_size);
+ } else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA))
+ skb_pull(skb, sizeof(struct ieee80211_hdr_3addr));
+
+ datap = skb->data;
+ llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap);
+
+ memset(&eth_hdr, 0, sizeof(eth_hdr));
+ eth_hdr.h_proto = llc_hdr->eth_type;
+
+ switch ((le16_to_cpu(wh.frame_control)) &
+ (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+ case 0:
+ memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN);
+ memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN);
+ break;
+ case IEEE80211_FCTL_TODS:
+ memcpy(eth_hdr.h_dest, wh.addr3, ETH_ALEN);
+ memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN);
+ break;
+ case IEEE80211_FCTL_FROMDS:
+ memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN);
+ memcpy(eth_hdr.h_source, wh.addr3, ETH_ALEN);
+ break;
+ case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+ break;
+ }
+
+ skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr));
+ skb_push(skb, sizeof(eth_hdr));
+
+ datap = skb->data;
+
+ memcpy(datap, &eth_hdr, sizeof(eth_hdr));
+
+ return 0;
+}
+
+/*
+ * Performs 802.3 to DIX encapsulation for received packets.
+ * Assumes the entire 802.3 header is contigous.
+ */
+int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb)
+{
+ struct ath6kl_llc_snap_hdr *llc_hdr;
+ struct ethhdr eth_hdr;
+ u8 *datap;
+
+ if (WARN_ON(skb == NULL))
+ return -EINVAL;
+
+ datap = skb->data;
+
+ memcpy(&eth_hdr, datap, sizeof(eth_hdr));
+
+ llc_hdr = (struct ath6kl_llc_snap_hdr *) (datap + sizeof(eth_hdr));
+ eth_hdr.h_proto = llc_hdr->eth_type;
+
+ skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr));
+ datap = skb->data;
+
+ memcpy(datap, &eth_hdr, sizeof(eth_hdr));
+
+ return 0;
+}
+
+static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
+{
+ struct tx_complete_msg_v1 *msg_v1;
+ struct wmi_tx_complete_event *evt;
+ int index;
+ u16 size;
+
+ evt = (struct wmi_tx_complete_event *) datap;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n",
+ evt->num_msg, evt->msg_len, evt->msg_type);
+
+ if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_WMI))
+ return 0;
+
+ for (index = 0; index < evt->num_msg; index++) {
+ size = sizeof(struct wmi_tx_complete_event) +
+ (index * sizeof(struct tx_complete_msg_v1));
+ msg_v1 = (struct tx_complete_msg_v1 *)(datap + size);
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "msg: %d %d %d %d\n",
+ msg_v1->status, msg_v1->pkt_id,
+ msg_v1->rate_idx, msg_v1->ack_failures);
+ }
+
+ return 0;
+}
+
+static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap,
+ int len)
+{
+ struct wmi_remain_on_chnl_event *ev;
+ u32 freq;
+ u32 dur;
+ struct ieee80211_channel *chan;
+ struct ath6kl *ar = wmi->parent_dev;
+
+ if (len < sizeof(*ev))
+ return -EINVAL;
+
+ ev = (struct wmi_remain_on_chnl_event *) datap;
+ freq = le32_to_cpu(ev->freq);
+ dur = le32_to_cpu(ev->duration);
+ ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: freq=%u dur=%u\n",
+ freq, dur);
+ chan = ieee80211_get_channel(ar->wdev->wiphy, freq);
+ if (!chan) {
+ ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: Unknown channel "
+ "(freq=%u)\n", freq);
+ return -EINVAL;
+ }
+ cfg80211_ready_on_channel(ar->net_dev, 1, chan, NL80211_CHAN_NO_HT,
+ dur, GFP_ATOMIC);
+
+ return 0;
+}
+
+static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi,
+ u8 *datap, int len)
+{
+ struct wmi_cancel_remain_on_chnl_event *ev;
+ u32 freq;
+ u32 dur;
+ struct ieee80211_channel *chan;
+ struct ath6kl *ar = wmi->parent_dev;
+
+ if (len < sizeof(*ev))
+ return -EINVAL;
+
+ ev = (struct wmi_cancel_remain_on_chnl_event *) datap;
+ freq = le32_to_cpu(ev->freq);
+ dur = le32_to_cpu(ev->duration);
+ ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: freq=%u dur=%u "
+ "status=%u\n", freq, dur, ev->status);
+ chan = ieee80211_get_channel(ar->wdev->wiphy, freq);
+ if (!chan) {
+ ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: Unknown "
+ "channel (freq=%u)\n", freq);
+ return -EINVAL;
+ }
+ cfg80211_remain_on_channel_expired(ar->net_dev, 1, chan,
+ NL80211_CHAN_NO_HT, GFP_ATOMIC);
+
+ return 0;
+}
+
+static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_tx_status_event *ev;
+ u32 id;
+ struct ath6kl *ar = wmi->parent_dev;
+
+ if (len < sizeof(*ev))
+ return -EINVAL;
+
+ ev = (struct wmi_tx_status_event *) datap;
+ id = le32_to_cpu(ev->id);
+ ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n",
+ id, ev->ack_status);
+ if (wmi->last_mgmt_tx_frame) {
+ cfg80211_mgmt_tx_status(ar->net_dev, id,
+ wmi->last_mgmt_tx_frame,
+ wmi->last_mgmt_tx_frame_len,
+ !!ev->ack_status, GFP_ATOMIC);
+ kfree(wmi->last_mgmt_tx_frame);
+ wmi->last_mgmt_tx_frame = NULL;
+ wmi->last_mgmt_tx_frame_len = 0;
+ }
+
+ return 0;
+}
+
+static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_p2p_rx_probe_req_event *ev;
+ u32 freq;
+ u16 dlen;
+ struct ath6kl *ar = wmi->parent_dev;
+
+ if (len < sizeof(*ev))
+ return -EINVAL;
+
+ ev = (struct wmi_p2p_rx_probe_req_event *) datap;
+ freq = le32_to_cpu(ev->freq);
+ dlen = le16_to_cpu(ev->len);
+ if (datap + len < ev->data + dlen) {
+ ath6kl_err("invalid wmi_p2p_rx_probe_req_event: "
+ "len=%d dlen=%u\n", len, dlen);
+ return -EINVAL;
+ }
+ ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_req: len=%u freq=%u "
+ "probe_req_report=%d\n",
+ dlen, freq, ar->probe_req_report);
+
+ if (ar->probe_req_report || ar->nw_type == AP_NETWORK)
+ cfg80211_rx_mgmt(ar->net_dev, freq, ev->data, dlen, GFP_ATOMIC);
+
+ return 0;
+}
+
+static int ath6kl_wmi_p2p_capabilities_event_rx(u8 *datap, int len)
+{
+ struct wmi_p2p_capabilities_event *ev;
+ u16 dlen;
+
+ if (len < sizeof(*ev))
+ return -EINVAL;
+
+ ev = (struct wmi_p2p_capabilities_event *) datap;
+ dlen = le16_to_cpu(ev->len);
+ ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_capab: len=%u\n", dlen);
+
+ return 0;
+}
+
+static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_rx_action_event *ev;
+ u32 freq;
+ u16 dlen;
+ struct ath6kl *ar = wmi->parent_dev;
+
+ if (len < sizeof(*ev))
+ return -EINVAL;
+
+ ev = (struct wmi_rx_action_event *) datap;
+ freq = le32_to_cpu(ev->freq);
+ dlen = le16_to_cpu(ev->len);
+ if (datap + len < ev->data + dlen) {
+ ath6kl_err("invalid wmi_rx_action_event: "
+ "len=%d dlen=%u\n", len, dlen);
+ return -EINVAL;
+ }
+ ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq);
+ cfg80211_rx_mgmt(ar->net_dev, freq, ev->data, dlen, GFP_ATOMIC);
+
+ return 0;
+}
+
+static int ath6kl_wmi_p2p_info_event_rx(u8 *datap, int len)
+{
+ struct wmi_p2p_info_event *ev;
+ u32 flags;
+ u16 dlen;
+
+ if (len < sizeof(*ev))
+ return -EINVAL;
+
+ ev = (struct wmi_p2p_info_event *) datap;
+ flags = le32_to_cpu(ev->info_req_flags);
+ dlen = le16_to_cpu(ev->len);
+ ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: flags=%x len=%d\n", flags, dlen);
+
+ if (flags & P2P_FLAG_CAPABILITIES_REQ) {
+ struct wmi_p2p_capabilities *cap;
+ if (dlen < sizeof(*cap))
+ return -EINVAL;
+ cap = (struct wmi_p2p_capabilities *) ev->data;
+ ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: GO Power Save = %d\n",
+ cap->go_power_save);
+ }
+
+ if (flags & P2P_FLAG_MACADDR_REQ) {
+ struct wmi_p2p_macaddr *mac;
+ if (dlen < sizeof(*mac))
+ return -EINVAL;
+ mac = (struct wmi_p2p_macaddr *) ev->data;
+ ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: MAC Address = %pM\n",
+ mac->mac_addr);
+ }
+
+ if (flags & P2P_FLAG_HMODEL_REQ) {
+ struct wmi_p2p_hmodel *mod;
+ if (dlen < sizeof(*mod))
+ return -EINVAL;
+ mod = (struct wmi_p2p_hmodel *) ev->data;
+ ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: P2P Model = %d (%s)\n",
+ mod->p2p_model,
+ mod->p2p_model ? "host" : "firmware");
+ }
+ return 0;
+}
+
+static inline struct sk_buff *ath6kl_wmi_get_new_buf(u32 size)
+{
+ struct sk_buff *skb;
+
+ skb = ath6kl_buf_alloc(size);
+ if (!skb)
+ return NULL;
+
+ skb_put(skb, size);
+ if (size)
+ memset(skb->data, 0, size);
+
+ return skb;
+}
+
+/* Send a "simple" wmi command -- one with no arguments */
+static int ath6kl_wmi_simple_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(0);
+ if (!skb)
+ return -ENOMEM;
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, cmd_id, NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_ready_event_2 *ev = (struct wmi_ready_event_2 *) datap;
+
+ if (len < sizeof(struct wmi_ready_event_2))
+ return -EINVAL;
+
+ wmi->ready = true;
+ ath6kl_ready_event(wmi->parent_dev, ev->mac_addr,
+ le32_to_cpu(ev->sw_version),
+ le32_to_cpu(ev->abi_version));
+
+ return 0;
+}
+
+/*
+ * Mechanism to modify the roaming behavior in the firmware. The lower rssi
+ * at which the station has to roam can be passed with
+ * WMI_SET_LRSSI_SCAN_PARAMS. Subtract 96 from RSSI to get the signal level
+ * in dBm.
+ */
+int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi)
+{
+ struct sk_buff *skb;
+ struct roam_ctrl_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct roam_ctrl_cmd *) skb->data;
+
+ cmd->info.params.lrssi_scan_period = cpu_to_le16(DEF_LRSSI_SCAN_PERIOD);
+ cmd->info.params.lrssi_scan_threshold = a_cpu_to_sle16(lrssi +
+ DEF_SCAN_FOR_ROAM_INTVL);
+ cmd->info.params.lrssi_roam_threshold = a_cpu_to_sle16(lrssi);
+ cmd->info.params.roam_rssi_floor = DEF_LRSSI_ROAM_FLOOR;
+ cmd->roam_ctrl = WMI_SET_LRSSI_SCAN_PARAMS;
+
+ ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_ROAM_CTRL_CMDID, NO_SYNC_WMIFLAG);
+
+ return 0;
+}
+
+static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_connect_event *ev;
+ u8 *pie, *peie;
+ struct ath6kl *ar = wmi->parent_dev;
+
+ if (len < sizeof(struct wmi_connect_event))
+ return -EINVAL;
+
+ ev = (struct wmi_connect_event *) datap;
+
+ if (ar->nw_type == AP_NETWORK) {
+ /* AP mode start/STA connected event */
+ struct net_device *dev = ar->net_dev;
+ if (memcmp(dev->dev_addr, ev->u.ap_bss.bssid, ETH_ALEN) == 0) {
+ ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM "
+ "(AP started)\n",
+ __func__, le16_to_cpu(ev->u.ap_bss.ch),
+ ev->u.ap_bss.bssid);
+ ath6kl_connect_ap_mode_bss(
+ ar, le16_to_cpu(ev->u.ap_bss.ch));
+ } else {
+ ath6kl_dbg(ATH6KL_DBG_WMI, "%s: aid %u mac_addr %pM "
+ "auth=%u keymgmt=%u cipher=%u apsd_info=%u "
+ "(STA connected)\n",
+ __func__, ev->u.ap_sta.aid,
+ ev->u.ap_sta.mac_addr,
+ ev->u.ap_sta.auth,
+ ev->u.ap_sta.keymgmt,
+ le16_to_cpu(ev->u.ap_sta.cipher),
+ ev->u.ap_sta.apsd_info);
+ ath6kl_connect_ap_mode_sta(
+ ar, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr,
+ ev->u.ap_sta.keymgmt,
+ le16_to_cpu(ev->u.ap_sta.cipher),
+ ev->u.ap_sta.auth, ev->assoc_req_len,
+ ev->assoc_info + ev->beacon_ie_len);
+ }
+ return 0;
+ }
+
+ /* STA/IBSS mode connection event */
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "wmi event connect freq %d bssid %pM listen_intvl %d beacon_intvl %d type %d\n",
+ le16_to_cpu(ev->u.sta.ch), ev->u.sta.bssid,
+ le16_to_cpu(ev->u.sta.listen_intvl),
+ le16_to_cpu(ev->u.sta.beacon_intvl),
+ le32_to_cpu(ev->u.sta.nw_type));
+
+ /* Start of assoc rsp IEs */
+ pie = ev->assoc_info + ev->beacon_ie_len +
+ ev->assoc_req_len + (sizeof(u16) * 3); /* capinfo, status, aid */
+
+ /* End of assoc rsp IEs */
+ peie = ev->assoc_info + ev->beacon_ie_len + ev->assoc_req_len +
+ ev->assoc_resp_len;
+
+ while (pie < peie) {
+ switch (*pie) {
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (pie[1] > 3 && pie[2] == 0x00 && pie[3] == 0x50 &&
+ pie[4] == 0xf2 && pie[5] == WMM_OUI_TYPE) {
+ /* WMM OUT (00:50:F2) */
+ if (pie[1] > 5
+ && pie[6] == WMM_PARAM_OUI_SUBTYPE)
+ wmi->is_wmm_enabled = true;
+ }
+ break;
+ }
+
+ if (wmi->is_wmm_enabled)
+ break;
+
+ pie += pie[1] + 2;
+ }
+
+ ath6kl_connect_event(wmi->parent_dev, le16_to_cpu(ev->u.sta.ch),
+ ev->u.sta.bssid,
+ le16_to_cpu(ev->u.sta.listen_intvl),
+ le16_to_cpu(ev->u.sta.beacon_intvl),
+ le32_to_cpu(ev->u.sta.nw_type),
+ ev->beacon_ie_len, ev->assoc_req_len,
+ ev->assoc_resp_len, ev->assoc_info);
+
+ return 0;
+}
+
+static struct country_code_to_enum_rd *
+ath6kl_regd_find_country(u16 countryCode)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
+ if (allCountries[i].countryCode == countryCode)
+ return &allCountries[i];
+ }
+
+ return NULL;
+}
+
+static struct reg_dmn_pair_mapping *
+ath6kl_get_regpair(u16 regdmn)
+{
+ int i;
+
+ if (regdmn == NO_ENUMRD)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
+ if (regDomainPairs[i].regDmnEnum == regdmn)
+ return &regDomainPairs[i];
+ }
+
+ return NULL;
+}
+
+static struct country_code_to_enum_rd *
+ath6kl_regd_find_country_by_rd(u16 regdmn)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
+ if (allCountries[i].regDmnEnum == regdmn)
+ return &allCountries[i];
+ }
+
+ return NULL;
+}
+
+static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
+{
+
+ struct ath6kl_wmi_regdomain *ev;
+ struct country_code_to_enum_rd *country = NULL;
+ struct reg_dmn_pair_mapping *regpair = NULL;
+ char alpha2[2];
+ u32 reg_code;
+
+ ev = (struct ath6kl_wmi_regdomain *) datap;
+ reg_code = le32_to_cpu(ev->reg_code);
+
+ if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG)
+ country = ath6kl_regd_find_country((u16) reg_code);
+ else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) {
+
+ regpair = ath6kl_get_regpair((u16) reg_code);
+ country = ath6kl_regd_find_country_by_rd((u16) reg_code);
+ ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n",
+ regpair->regDmnEnum);
+ }
+
+ if (country) {
+ alpha2[0] = country->isoName[0];
+ alpha2[1] = country->isoName[1];
+
+ regulatory_hint(wmi->parent_dev->wdev->wiphy, alpha2);
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "Country alpha2 being used: %c%c\n",
+ alpha2[0], alpha2[1]);
+ }
+}
+
+static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_disconnect_event *ev;
+ wmi->traffic_class = 100;
+
+ if (len < sizeof(struct wmi_disconnect_event))
+ return -EINVAL;
+
+ ev = (struct wmi_disconnect_event *) datap;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "wmi event disconnect proto_reason %d bssid %pM wmi_reason %d assoc_resp_len %d\n",
+ le16_to_cpu(ev->proto_reason_status), ev->bssid,
+ ev->disconn_reason, ev->assoc_resp_len);
+
+ wmi->is_wmm_enabled = false;
+ wmi->pair_crypto_type = NONE_CRYPT;
+ wmi->grp_crypto_type = NONE_CRYPT;
+
+ ath6kl_disconnect_event(wmi->parent_dev, ev->disconn_reason,
+ ev->bssid, ev->assoc_resp_len, ev->assoc_info,
+ le16_to_cpu(ev->proto_reason_status));
+
+ return 0;
+}
+
+static int ath6kl_wmi_peer_node_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_peer_node_event *ev;
+
+ if (len < sizeof(struct wmi_peer_node_event))
+ return -EINVAL;
+
+ ev = (struct wmi_peer_node_event *) datap;
+
+ if (ev->event_code == PEER_NODE_JOIN_EVENT)
+ ath6kl_dbg(ATH6KL_DBG_WMI, "joined node with mac addr: %pM\n",
+ ev->peer_mac_addr);
+ else if (ev->event_code == PEER_NODE_LEAVE_EVENT)
+ ath6kl_dbg(ATH6KL_DBG_WMI, "left node with mac addr: %pM\n",
+ ev->peer_mac_addr);
+
+ return 0;
+}
+
+static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_tkip_micerr_event *ev;
+
+ if (len < sizeof(struct wmi_tkip_micerr_event))
+ return -EINVAL;
+
+ ev = (struct wmi_tkip_micerr_event *) datap;
+
+ ath6kl_tkip_micerr_event(wmi->parent_dev, ev->key_id, ev->is_mcast);
+
+ return 0;
+}
+
+static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_bss_info_hdr2 *bih;
+ u8 *buf;
+ struct ieee80211_channel *channel;
+ struct ath6kl *ar = wmi->parent_dev;
+ struct ieee80211_mgmt *mgmt;
+ struct cfg80211_bss *bss;
+
+ if (len <= sizeof(struct wmi_bss_info_hdr2))
+ return -EINVAL;
+
+ bih = (struct wmi_bss_info_hdr2 *) datap;
+ buf = datap + sizeof(struct wmi_bss_info_hdr2);
+ len -= sizeof(struct wmi_bss_info_hdr2);
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "bss info evt - ch %u, snr %d, rssi %d, bssid \"%pM\" "
+ "frame_type=%d\n",
+ bih->ch, bih->snr, bih->snr - 95, bih->bssid,
+ bih->frame_type);
+
+ if (bih->frame_type != BEACON_FTYPE &&
+ bih->frame_type != PROBERESP_FTYPE)
+ return 0; /* Only update BSS table for now */
+
+ if (bih->frame_type == BEACON_FTYPE &&
+ test_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag)) {
+ clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
+ ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+ }
+
+ channel = ieee80211_get_channel(ar->wdev->wiphy, le16_to_cpu(bih->ch));
+ if (channel == NULL)
+ return -EINVAL;
+
+ if (len < 8 + 2 + 2)
+ return -EINVAL;
+
+ if (bih->frame_type == BEACON_FTYPE && test_bit(CONNECTED, &ar->flag) &&
+ memcmp(bih->bssid, ar->bssid, ETH_ALEN) == 0) {
+ const u8 *tim;
+ tim = cfg80211_find_ie(WLAN_EID_TIM, buf + 8 + 2 + 2,
+ len - 8 - 2 - 2);
+ if (tim && tim[1] >= 2) {
+ ar->assoc_bss_dtim_period = tim[3];
+ set_bit(DTIM_PERIOD_AVAIL, &ar->flag);
+ }
+ }
+
+ /*
+ * In theory, use of cfg80211_inform_bss() would be more natural here
+ * since we do not have the full frame. However, at least for now,
+ * cfg80211 can only distinguish Beacon and Probe Response frames from
+ * each other when using cfg80211_inform_bss_frame(), so let's build a
+ * fake IEEE 802.11 header to be able to take benefit of this.
+ */
+ mgmt = kmalloc(24 + len, GFP_ATOMIC);
+ if (mgmt == NULL)
+ return -EINVAL;
+
+ if (bih->frame_type == BEACON_FTYPE) {
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_BEACON);
+ memset(mgmt->da, 0xff, ETH_ALEN);
+ } else {
+ struct net_device *dev = ar->net_dev;
+
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+ memcpy(mgmt->da, dev->dev_addr, ETH_ALEN);
+ }
+ mgmt->duration = cpu_to_le16(0);
+ memcpy(mgmt->sa, bih->bssid, ETH_ALEN);
+ memcpy(mgmt->bssid, bih->bssid, ETH_ALEN);
+ mgmt->seq_ctrl = cpu_to_le16(0);
+
+ memcpy(&mgmt->u.beacon, buf, len);
+
+ bss = cfg80211_inform_bss_frame(ar->wdev->wiphy, channel, mgmt,
+ 24 + len, (bih->snr - 95) * 100,
+ GFP_ATOMIC);
+ kfree(mgmt);
+ if (bss == NULL)
+ return -ENOMEM;
+ cfg80211_put_bss(bss);
+
+ return 0;
+}
+
+/* Inactivity timeout of a fatpipe(pstream) at the target */
+static int ath6kl_wmi_pstream_timeout_event_rx(struct wmi *wmi, u8 *datap,
+ int len)
+{
+ struct wmi_pstream_timeout_event *ev;
+
+ if (len < sizeof(struct wmi_pstream_timeout_event))
+ return -EINVAL;
+
+ ev = (struct wmi_pstream_timeout_event *) datap;
+
+ /*
+ * When the pstream (fat pipe == AC) timesout, it means there were
+ * no thinStreams within this pstream & it got implicitly created
+ * due to data flow on this AC. We start the inactivity timer only
+ * for implicitly created pstream. Just reset the host state.
+ */
+ spin_lock_bh(&wmi->lock);
+ wmi->stream_exist_for_ac[ev->traffic_class] = 0;
+ wmi->fat_pipe_exist &= ~(1 << ev->traffic_class);
+ spin_unlock_bh(&wmi->lock);
+
+ /* Indicate inactivity to driver layer for this fatpipe (pstream) */
+ ath6kl_indicate_tx_activity(wmi->parent_dev, ev->traffic_class, false);
+
+ return 0;
+}
+
+static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_bit_rate_reply *reply;
+ s32 rate;
+ u32 sgi, index;
+
+ if (len < sizeof(struct wmi_bit_rate_reply))
+ return -EINVAL;
+
+ reply = (struct wmi_bit_rate_reply *) datap;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "rateindex %d\n", reply->rate_index);
+
+ if (reply->rate_index == (s8) RATE_AUTO) {
+ rate = RATE_AUTO;
+ } else {
+ index = reply->rate_index & 0x7f;
+ sgi = (reply->rate_index & 0x80) ? 1 : 0;
+ rate = wmi_rate_tbl[index][sgi];
+ }
+
+ ath6kl_wakeup_event(wmi->parent_dev);
+
+ return 0;
+}
+
+static int ath6kl_wmi_tcmd_test_report_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ ath6kl_tm_rx_report_event(wmi->parent_dev, datap, len);
+
+ return 0;
+}
+
+static int ath6kl_wmi_ratemask_reply_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ if (len < sizeof(struct wmi_fix_rates_reply))
+ return -EINVAL;
+
+ ath6kl_wakeup_event(wmi->parent_dev);
+
+ return 0;
+}
+
+static int ath6kl_wmi_ch_list_reply_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ if (len < sizeof(struct wmi_channel_list_reply))
+ return -EINVAL;
+
+ ath6kl_wakeup_event(wmi->parent_dev);
+
+ return 0;
+}
+
+static int ath6kl_wmi_tx_pwr_reply_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_tx_pwr_reply *reply;
+
+ if (len < sizeof(struct wmi_tx_pwr_reply))
+ return -EINVAL;
+
+ reply = (struct wmi_tx_pwr_reply *) datap;
+ ath6kl_txpwr_rx_evt(wmi->parent_dev, reply->dbM);
+
+ return 0;
+}
+
+static int ath6kl_wmi_keepalive_reply_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ if (len < sizeof(struct wmi_get_keepalive_cmd))
+ return -EINVAL;
+
+ ath6kl_wakeup_event(wmi->parent_dev);
+
+ return 0;
+}
+
+static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_scan_complete_event *ev;
+
+ ev = (struct wmi_scan_complete_event *) datap;
+
+ ath6kl_scan_complete_evt(wmi->parent_dev, a_sle32_to_cpu(ev->status));
+ wmi->is_probe_ssid = false;
+
+ return 0;
+}
+
+static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap,
+ int len)
+{
+ struct wmi_neighbor_report_event *ev;
+ u8 i;
+
+ if (len < sizeof(*ev))
+ return -EINVAL;
+ ev = (struct wmi_neighbor_report_event *) datap;
+ if (sizeof(*ev) + ev->num_neighbors * sizeof(struct wmi_neighbor_info)
+ > len) {
+ ath6kl_dbg(ATH6KL_DBG_WMI, "truncated neighbor event "
+ "(num=%d len=%d)\n", ev->num_neighbors, len);
+ return -EINVAL;
+ }
+ for (i = 0; i < ev->num_neighbors; i++) {
+ ath6kl_dbg(ATH6KL_DBG_WMI, "neighbor %d/%d - %pM 0x%x\n",
+ i + 1, ev->num_neighbors, ev->neighbor[i].bssid,
+ ev->neighbor[i].bss_flags);
+ cfg80211_pmksa_candidate_notify(wmi->parent_dev->net_dev, i,
+ ev->neighbor[i].bssid,
+ !!(ev->neighbor[i].bss_flags &
+ WMI_PREAUTH_CAPABLE_BSS),
+ GFP_ATOMIC);
+ }
+
+ return 0;
+}
+
+/*
+ * Target is reporting a programming error. This is for
+ * developer aid only. Target only checks a few common violations
+ * and it is responsibility of host to do all error checking.
+ * Behavior of target after wmi error event is undefined.
+ * A reset is recommended.
+ */
+static int ath6kl_wmi_error_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ const char *type = "unknown error";
+ struct wmi_cmd_error_event *ev;
+ ev = (struct wmi_cmd_error_event *) datap;
+
+ switch (ev->err_code) {
+ case INVALID_PARAM:
+ type = "invalid parameter";
+ break;
+ case ILLEGAL_STATE:
+ type = "invalid state";
+ break;
+ case INTERNAL_ERROR:
+ type = "internal error";
+ break;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "programming error, cmd=%d %s\n",
+ ev->cmd_id, type);
+
+ return 0;
+}
+
+static int ath6kl_wmi_stats_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ ath6kl_tgt_stats_event(wmi->parent_dev, datap, len);
+
+ return 0;
+}
+
+static u8 ath6kl_wmi_get_upper_threshold(s16 rssi,
+ struct sq_threshold_params *sq_thresh,
+ u32 size)
+{
+ u32 index;
+ u8 threshold = (u8) sq_thresh->upper_threshold[size - 1];
+
+ /* The list is already in sorted order. Get the next lower value */
+ for (index = 0; index < size; index++) {
+ if (rssi < sq_thresh->upper_threshold[index]) {
+ threshold = (u8) sq_thresh->upper_threshold[index];
+ break;
+ }
+ }
+
+ return threshold;
+}
+
+static u8 ath6kl_wmi_get_lower_threshold(s16 rssi,
+ struct sq_threshold_params *sq_thresh,
+ u32 size)
+{
+ u32 index;
+ u8 threshold = (u8) sq_thresh->lower_threshold[size - 1];
+
+ /* The list is already in sorted order. Get the next lower value */
+ for (index = 0; index < size; index++) {
+ if (rssi > sq_thresh->lower_threshold[index]) {
+ threshold = (u8) sq_thresh->lower_threshold[index];
+ break;
+ }
+ }
+
+ return threshold;
+}
+
+static int ath6kl_wmi_send_rssi_threshold_params(struct wmi *wmi,
+ struct wmi_rssi_threshold_params_cmd *rssi_cmd)
+{
+ struct sk_buff *skb;
+ struct wmi_rssi_threshold_params_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_rssi_threshold_params_cmd *) skb->data;
+ memcpy(cmd, rssi_cmd, sizeof(struct wmi_rssi_threshold_params_cmd));
+
+ return ath6kl_wmi_cmd_send(wmi, skb, WMI_RSSI_THRESHOLD_PARAMS_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap,
+ int len)
+{
+ struct wmi_rssi_threshold_event *reply;
+ struct wmi_rssi_threshold_params_cmd cmd;
+ struct sq_threshold_params *sq_thresh;
+ enum wmi_rssi_threshold_val new_threshold;
+ u8 upper_rssi_threshold, lower_rssi_threshold;
+ s16 rssi;
+ int ret;
+
+ if (len < sizeof(struct wmi_rssi_threshold_event))
+ return -EINVAL;
+
+ reply = (struct wmi_rssi_threshold_event *) datap;
+ new_threshold = (enum wmi_rssi_threshold_val) reply->range;
+ rssi = a_sle16_to_cpu(reply->rssi);
+
+ sq_thresh = &wmi->sq_threshld[SIGNAL_QUALITY_METRICS_RSSI];
+
+ /*
+ * Identify the threshold breached and communicate that to the app.
+ * After that install a new set of thresholds based on the signal
+ * quality reported by the target
+ */
+ if (new_threshold) {
+ /* Upper threshold breached */
+ if (rssi < sq_thresh->upper_threshold[0]) {
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "spurious upper rssi threshold event: %d\n",
+ rssi);
+ } else if ((rssi < sq_thresh->upper_threshold[1]) &&
+ (rssi >= sq_thresh->upper_threshold[0])) {
+ new_threshold = WMI_RSSI_THRESHOLD1_ABOVE;
+ } else if ((rssi < sq_thresh->upper_threshold[2]) &&
+ (rssi >= sq_thresh->upper_threshold[1])) {
+ new_threshold = WMI_RSSI_THRESHOLD2_ABOVE;
+ } else if ((rssi < sq_thresh->upper_threshold[3]) &&
+ (rssi >= sq_thresh->upper_threshold[2])) {
+ new_threshold = WMI_RSSI_THRESHOLD3_ABOVE;
+ } else if ((rssi < sq_thresh->upper_threshold[4]) &&
+ (rssi >= sq_thresh->upper_threshold[3])) {
+ new_threshold = WMI_RSSI_THRESHOLD4_ABOVE;
+ } else if ((rssi < sq_thresh->upper_threshold[5]) &&
+ (rssi >= sq_thresh->upper_threshold[4])) {
+ new_threshold = WMI_RSSI_THRESHOLD5_ABOVE;
+ } else if (rssi >= sq_thresh->upper_threshold[5]) {
+ new_threshold = WMI_RSSI_THRESHOLD6_ABOVE;
+ }
+ } else {
+ /* Lower threshold breached */
+ if (rssi > sq_thresh->lower_threshold[0]) {
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "spurious lower rssi threshold event: %d %d\n",
+ rssi, sq_thresh->lower_threshold[0]);
+ } else if ((rssi > sq_thresh->lower_threshold[1]) &&
+ (rssi <= sq_thresh->lower_threshold[0])) {
+ new_threshold = WMI_RSSI_THRESHOLD6_BELOW;
+ } else if ((rssi > sq_thresh->lower_threshold[2]) &&
+ (rssi <= sq_thresh->lower_threshold[1])) {
+ new_threshold = WMI_RSSI_THRESHOLD5_BELOW;
+ } else if ((rssi > sq_thresh->lower_threshold[3]) &&
+ (rssi <= sq_thresh->lower_threshold[2])) {
+ new_threshold = WMI_RSSI_THRESHOLD4_BELOW;
+ } else if ((rssi > sq_thresh->lower_threshold[4]) &&
+ (rssi <= sq_thresh->lower_threshold[3])) {
+ new_threshold = WMI_RSSI_THRESHOLD3_BELOW;
+ } else if ((rssi > sq_thresh->lower_threshold[5]) &&
+ (rssi <= sq_thresh->lower_threshold[4])) {
+ new_threshold = WMI_RSSI_THRESHOLD2_BELOW;
+ } else if (rssi <= sq_thresh->lower_threshold[5]) {
+ new_threshold = WMI_RSSI_THRESHOLD1_BELOW;
+ }
+ }
+
+ /* Calculate and install the next set of thresholds */
+ lower_rssi_threshold = ath6kl_wmi_get_lower_threshold(rssi, sq_thresh,
+ sq_thresh->lower_threshold_valid_count);
+ upper_rssi_threshold = ath6kl_wmi_get_upper_threshold(rssi, sq_thresh,
+ sq_thresh->upper_threshold_valid_count);
+
+ /* Issue a wmi command to install the thresholds */
+ cmd.thresh_above1_val = a_cpu_to_sle16(upper_rssi_threshold);
+ cmd.thresh_below1_val = a_cpu_to_sle16(lower_rssi_threshold);
+ cmd.weight = sq_thresh->weight;
+ cmd.poll_time = cpu_to_le32(sq_thresh->polling_interval);
+
+ ret = ath6kl_wmi_send_rssi_threshold_params(wmi, &cmd);
+ if (ret) {
+ ath6kl_err("unable to configure rssi thresholds\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_cac_event *reply;
+ struct ieee80211_tspec_ie *ts;
+ u16 active_tsids, tsinfo;
+ u8 tsid, index;
+ u8 ts_id;
+
+ if (len < sizeof(struct wmi_cac_event))
+ return -EINVAL;
+
+ reply = (struct wmi_cac_event *) datap;
+
+ if ((reply->cac_indication == CAC_INDICATION_ADMISSION_RESP) &&
+ (reply->status_code != IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED)) {
+
+ ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion);
+ tsinfo = le16_to_cpu(ts->tsinfo);
+ tsid = (tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) &
+ IEEE80211_WMM_IE_TSPEC_TID_MASK;
+
+ ath6kl_wmi_delete_pstream_cmd(wmi, reply->ac, tsid);
+ } else if (reply->cac_indication == CAC_INDICATION_NO_RESP) {
+ /*
+ * Following assumes that there is only one outstanding
+ * ADDTS request when this event is received
+ */
+ spin_lock_bh(&wmi->lock);
+ active_tsids = wmi->stream_exist_for_ac[reply->ac];
+ spin_unlock_bh(&wmi->lock);
+
+ for (index = 0; index < sizeof(active_tsids) * 8; index++) {
+ if ((active_tsids >> index) & 1)
+ break;
+ }
+ if (index < (sizeof(active_tsids) * 8))
+ ath6kl_wmi_delete_pstream_cmd(wmi, reply->ac, index);
+ }
+
+ /*
+ * Clear active tsids and Add missing handling
+ * for delete qos stream from AP
+ */
+ else if (reply->cac_indication == CAC_INDICATION_DELETE) {
+
+ ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion);
+ tsinfo = le16_to_cpu(ts->tsinfo);
+ ts_id = ((tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) &
+ IEEE80211_WMM_IE_TSPEC_TID_MASK);
+
+ spin_lock_bh(&wmi->lock);
+ wmi->stream_exist_for_ac[reply->ac] &= ~(1 << ts_id);
+ active_tsids = wmi->stream_exist_for_ac[reply->ac];
+ spin_unlock_bh(&wmi->lock);
+
+ /* Indicate stream inactivity to driver layer only if all tsids
+ * within this AC are deleted.
+ */
+ if (!active_tsids) {
+ ath6kl_indicate_tx_activity(wmi->parent_dev, reply->ac,
+ false);
+ wmi->fat_pipe_exist &= ~(1 << reply->ac);
+ }
+ }
+
+ return 0;
+}
+
+static int ath6kl_wmi_send_snr_threshold_params(struct wmi *wmi,
+ struct wmi_snr_threshold_params_cmd *snr_cmd)
+{
+ struct sk_buff *skb;
+ struct wmi_snr_threshold_params_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_snr_threshold_params_cmd *) skb->data;
+ memcpy(cmd, snr_cmd, sizeof(struct wmi_snr_threshold_params_cmd));
+
+ return ath6kl_wmi_cmd_send(wmi, skb, WMI_SNR_THRESHOLD_PARAMS_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap,
+ int len)
+{
+ struct wmi_snr_threshold_event *reply;
+ struct sq_threshold_params *sq_thresh;
+ struct wmi_snr_threshold_params_cmd cmd;
+ enum wmi_snr_threshold_val new_threshold;
+ u8 upper_snr_threshold, lower_snr_threshold;
+ s16 snr;
+ int ret;
+
+ if (len < sizeof(struct wmi_snr_threshold_event))
+ return -EINVAL;
+
+ reply = (struct wmi_snr_threshold_event *) datap;
+
+ new_threshold = (enum wmi_snr_threshold_val) reply->range;
+ snr = reply->snr;
+
+ sq_thresh = &wmi->sq_threshld[SIGNAL_QUALITY_METRICS_SNR];
+
+ /*
+ * Identify the threshold breached and communicate that to the app.
+ * After that install a new set of thresholds based on the signal
+ * quality reported by the target.
+ */
+ if (new_threshold) {
+ /* Upper threshold breached */
+ if (snr < sq_thresh->upper_threshold[0]) {
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "spurious upper snr threshold event: %d\n",
+ snr);
+ } else if ((snr < sq_thresh->upper_threshold[1]) &&
+ (snr >= sq_thresh->upper_threshold[0])) {
+ new_threshold = WMI_SNR_THRESHOLD1_ABOVE;
+ } else if ((snr < sq_thresh->upper_threshold[2]) &&
+ (snr >= sq_thresh->upper_threshold[1])) {
+ new_threshold = WMI_SNR_THRESHOLD2_ABOVE;
+ } else if ((snr < sq_thresh->upper_threshold[3]) &&
+ (snr >= sq_thresh->upper_threshold[2])) {
+ new_threshold = WMI_SNR_THRESHOLD3_ABOVE;
+ } else if (snr >= sq_thresh->upper_threshold[3]) {
+ new_threshold = WMI_SNR_THRESHOLD4_ABOVE;
+ }
+ } else {
+ /* Lower threshold breached */
+ if (snr > sq_thresh->lower_threshold[0]) {
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "spurious lower snr threshold event: %d\n",
+ sq_thresh->lower_threshold[0]);
+ } else if ((snr > sq_thresh->lower_threshold[1]) &&
+ (snr <= sq_thresh->lower_threshold[0])) {
+ new_threshold = WMI_SNR_THRESHOLD4_BELOW;
+ } else if ((snr > sq_thresh->lower_threshold[2]) &&
+ (snr <= sq_thresh->lower_threshold[1])) {
+ new_threshold = WMI_SNR_THRESHOLD3_BELOW;
+ } else if ((snr > sq_thresh->lower_threshold[3]) &&
+ (snr <= sq_thresh->lower_threshold[2])) {
+ new_threshold = WMI_SNR_THRESHOLD2_BELOW;
+ } else if (snr <= sq_thresh->lower_threshold[3]) {
+ new_threshold = WMI_SNR_THRESHOLD1_BELOW;
+ }
+ }
+
+ /* Calculate and install the next set of thresholds */
+ lower_snr_threshold = ath6kl_wmi_get_lower_threshold(snr, sq_thresh,
+ sq_thresh->lower_threshold_valid_count);
+ upper_snr_threshold = ath6kl_wmi_get_upper_threshold(snr, sq_thresh,
+ sq_thresh->upper_threshold_valid_count);
+
+ /* Issue a wmi command to install the thresholds */
+ cmd.thresh_above1_val = upper_snr_threshold;
+ cmd.thresh_below1_val = lower_snr_threshold;
+ cmd.weight = sq_thresh->weight;
+ cmd.poll_time = cpu_to_le32(sq_thresh->polling_interval);
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "snr: %d, threshold: %d, lower: %d, upper: %d\n",
+ snr, new_threshold,
+ lower_snr_threshold, upper_snr_threshold);
+
+ ret = ath6kl_wmi_send_snr_threshold_params(wmi, &cmd);
+ if (ret) {
+ ath6kl_err("unable to configure snr threshold\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int ath6kl_wmi_aplist_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ u16 ap_info_entry_size;
+ struct wmi_aplist_event *ev = (struct wmi_aplist_event *) datap;
+ struct wmi_ap_info_v1 *ap_info_v1;
+ u8 index;
+
+ if (len < sizeof(struct wmi_aplist_event) ||
+ ev->ap_list_ver != APLIST_VER1)
+ return -EINVAL;
+
+ ap_info_entry_size = sizeof(struct wmi_ap_info_v1);
+ ap_info_v1 = (struct wmi_ap_info_v1 *) ev->ap_list;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "number of APs in aplist event: %d\n", ev->num_ap);
+
+ if (len < (int) (sizeof(struct wmi_aplist_event) +
+ (ev->num_ap - 1) * ap_info_entry_size))
+ return -EINVAL;
+
+ /* AP list version 1 contents */
+ for (index = 0; index < ev->num_ap; index++) {
+ ath6kl_dbg(ATH6KL_DBG_WMI, "AP#%d BSSID %pM Channel %d\n",
+ index, ap_info_v1->bssid, ap_info_v1->channel);
+ ap_info_v1++;
+ }
+
+ return 0;
+}
+
+int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
+ enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag)
+{
+ struct wmi_cmd_hdr *cmd_hdr;
+ enum htc_endpoint_id ep_id = wmi->ep_id;
+ int ret;
+
+ if (WARN_ON(skb == NULL))
+ return -EINVAL;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n",
+ cmd_id, skb->len, sync_flag);
+ ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi tx ",
+ skb->data, skb->len);
+
+ if (sync_flag >= END_WMIFLAG) {
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ if ((sync_flag == SYNC_BEFORE_WMIFLAG) ||
+ (sync_flag == SYNC_BOTH_WMIFLAG)) {
+ /*
+ * Make sure all data currently queued is transmitted before
+ * the cmd execution. Establish a new sync point.
+ */
+ ath6kl_wmi_sync_point(wmi);
+ }
+
+ skb_push(skb, sizeof(struct wmi_cmd_hdr));
+
+ cmd_hdr = (struct wmi_cmd_hdr *) skb->data;
+ cmd_hdr->cmd_id = cpu_to_le16(cmd_id);
+ cmd_hdr->info1 = 0; /* added for virtual interface */
+
+ /* Only for OPT_TX_CMD, use BE endpoint. */
+ if (cmd_id == WMI_OPT_TX_FRAME_CMDID) {
+ ret = ath6kl_wmi_data_hdr_add(wmi, skb, OPT_MSGTYPE,
+ false, false, 0, NULL);
+ if (ret) {
+ dev_kfree_skb(skb);
+ return ret;
+ }
+ ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev, WMM_AC_BE);
+ }
+
+ ath6kl_control_tx(wmi->parent_dev, skb, ep_id);
+
+ if ((sync_flag == SYNC_AFTER_WMIFLAG) ||
+ (sync_flag == SYNC_BOTH_WMIFLAG)) {
+ /*
+ * Make sure all new data queued waits for the command to
+ * execute. Establish a new sync point.
+ */
+ ath6kl_wmi_sync_point(wmi);
+ }
+
+ return 0;
+}
+
+int ath6kl_wmi_connect_cmd(struct wmi *wmi, enum network_type nw_type,
+ enum dot11_auth_mode dot11_auth_mode,
+ enum auth_mode auth_mode,
+ enum crypto_type pairwise_crypto,
+ u8 pairwise_crypto_len,
+ enum crypto_type group_crypto,
+ u8 group_crypto_len, int ssid_len, u8 *ssid,
+ u8 *bssid, u16 channel, u32 ctrl_flags)
+{
+ struct sk_buff *skb;
+ struct wmi_connect_cmd *cc;
+ int ret;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "wmi connect bssid %pM freq %d flags 0x%x ssid_len %d "
+ "type %d dot11_auth %d auth %d pairwise %d group %d\n",
+ bssid, channel, ctrl_flags, ssid_len, nw_type,
+ dot11_auth_mode, auth_mode, pairwise_crypto, group_crypto);
+ ath6kl_dbg_dump(ATH6KL_DBG_WMI, NULL, "ssid ", ssid, ssid_len);
+
+ wmi->traffic_class = 100;
+
+ if ((pairwise_crypto == NONE_CRYPT) && (group_crypto != NONE_CRYPT))
+ return -EINVAL;
+
+ if ((pairwise_crypto != NONE_CRYPT) && (group_crypto == NONE_CRYPT))
+ return -EINVAL;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_connect_cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cc = (struct wmi_connect_cmd *) skb->data;
+
+ if (ssid_len)
+ memcpy(cc->ssid, ssid, ssid_len);
+
+ cc->ssid_len = ssid_len;
+ cc->nw_type = nw_type;
+ cc->dot11_auth_mode = dot11_auth_mode;
+ cc->auth_mode = auth_mode;
+ cc->prwise_crypto_type = pairwise_crypto;
+ cc->prwise_crypto_len = pairwise_crypto_len;
+ cc->grp_crypto_type = group_crypto;
+ cc->grp_crypto_len = group_crypto_len;
+ cc->ch = cpu_to_le16(channel);
+ cc->ctrl_flags = cpu_to_le32(ctrl_flags);
+
+ if (bssid != NULL)
+ memcpy(cc->bssid, bssid, ETH_ALEN);
+
+ wmi->pair_crypto_type = pairwise_crypto;
+ wmi->grp_crypto_type = group_crypto;
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_CONNECT_CMDID, NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 *bssid, u16 channel)
+{
+ struct sk_buff *skb;
+ struct wmi_reconnect_cmd *cc;
+ int ret;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "wmi reconnect bssid %pM freq %d\n",
+ bssid, channel);
+
+ wmi->traffic_class = 100;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_reconnect_cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cc = (struct wmi_reconnect_cmd *) skb->data;
+ cc->channel = cpu_to_le16(channel);
+
+ if (bssid != NULL)
+ memcpy(cc->bssid, bssid, ETH_ALEN);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_RECONNECT_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+int ath6kl_wmi_disconnect_cmd(struct wmi *wmi)
+{
+ int ret;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "wmi disconnect\n");
+
+ wmi->traffic_class = 100;
+
+ /* Disconnect command does not need to do a SYNC before. */
+ ret = ath6kl_wmi_simple_cmd(wmi, WMI_DISCONNECT_CMDID);
+
+ return ret;
+}
+
+int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type,
+ u32 force_fgscan, u32 is_legacy,
+ u32 home_dwell_time, u32 force_scan_interval,
+ s8 num_chan, u16 *ch_list)
+{
+ struct sk_buff *skb;
+ struct wmi_start_scan_cmd *sc;
+ s8 size;
+ int i, ret;
+
+ size = sizeof(struct wmi_start_scan_cmd);
+
+ if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN))
+ return -EINVAL;
+
+ if (num_chan > WMI_MAX_CHANNELS)
+ return -EINVAL;
+
+ if (num_chan)
+ size += sizeof(u16) * (num_chan - 1);
+
+ skb = ath6kl_wmi_get_new_buf(size);
+ if (!skb)
+ return -ENOMEM;
+
+ sc = (struct wmi_start_scan_cmd *) skb->data;
+ sc->scan_type = scan_type;
+ sc->force_fg_scan = cpu_to_le32(force_fgscan);
+ sc->is_legacy = cpu_to_le32(is_legacy);
+ sc->home_dwell_time = cpu_to_le32(home_dwell_time);
+ sc->force_scan_intvl = cpu_to_le32(force_scan_interval);
+ sc->num_ch = num_chan;
+
+ for (i = 0; i < num_chan; i++)
+ sc->ch_list[i] = cpu_to_le16(ch_list[i]);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_START_SCAN_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u16 fg_start_sec,
+ u16 fg_end_sec, u16 bg_sec,
+ u16 minact_chdw_msec, u16 maxact_chdw_msec,
+ u16 pas_chdw_msec, u8 short_scan_ratio,
+ u8 scan_ctrl_flag, u32 max_dfsch_act_time,
+ u16 maxact_scan_per_ssid)
+{
+ struct sk_buff *skb;
+ struct wmi_scan_params_cmd *sc;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*sc));
+ if (!skb)
+ return -ENOMEM;
+
+ sc = (struct wmi_scan_params_cmd *) skb->data;
+ sc->fg_start_period = cpu_to_le16(fg_start_sec);
+ sc->fg_end_period = cpu_to_le16(fg_end_sec);
+ sc->bg_period = cpu_to_le16(bg_sec);
+ sc->minact_chdwell_time = cpu_to_le16(minact_chdw_msec);
+ sc->maxact_chdwell_time = cpu_to_le16(maxact_chdw_msec);
+ sc->pas_chdwell_time = cpu_to_le16(pas_chdw_msec);
+ sc->short_scan_ratio = short_scan_ratio;
+ sc->scan_ctrl_flags = scan_ctrl_flag;
+ sc->max_dfsch_act_time = cpu_to_le32(max_dfsch_act_time);
+ sc->maxact_scan_per_ssid = cpu_to_le16(maxact_scan_per_ssid);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_SCAN_PARAMS_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 filter, u32 ie_mask)
+{
+ struct sk_buff *skb;
+ struct wmi_bss_filter_cmd *cmd;
+ int ret;
+
+ if (filter >= LAST_BSS_FILTER)
+ return -EINVAL;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_bss_filter_cmd *) skb->data;
+ cmd->bss_filter = filter;
+ cmd->ie_mask = cpu_to_le32(ie_mask);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_BSS_FILTER_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 index, u8 flag,
+ u8 ssid_len, u8 *ssid)
+{
+ struct sk_buff *skb;
+ struct wmi_probed_ssid_cmd *cmd;
+ int ret;
+
+ if (index > MAX_PROBED_SSID_INDEX)
+ return -EINVAL;
+
+ if (ssid_len > sizeof(cmd->ssid))
+ return -EINVAL;
+
+ if ((flag & (DISABLE_SSID_FLAG | ANY_SSID_FLAG)) && (ssid_len > 0))
+ return -EINVAL;
+
+ if ((flag & SPECIFIC_SSID_FLAG) && !ssid_len)
+ return -EINVAL;
+
+ if (flag & SPECIFIC_SSID_FLAG)
+ wmi->is_probe_ssid = true;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_probed_ssid_cmd *) skb->data;
+ cmd->entry_index = index;
+ cmd->flag = flag;
+ cmd->ssid_len = ssid_len;
+ memcpy(cmd->ssid, ssid, ssid_len);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_PROBED_SSID_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u16 listen_interval,
+ u16 listen_beacons)
+{
+ struct sk_buff *skb;
+ struct wmi_listen_int_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_listen_int_cmd *) skb->data;
+ cmd->listen_intvl = cpu_to_le16(listen_interval);
+ cmd->num_beacons = cpu_to_le16(listen_beacons);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_LISTEN_INT_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 pwr_mode)
+{
+ struct sk_buff *skb;
+ struct wmi_power_mode_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_power_mode_cmd *) skb->data;
+ cmd->pwr_mode = pwr_mode;
+ wmi->pwr_mode = pwr_mode;
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_POWER_MODE_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u16 idle_period,
+ u16 ps_poll_num, u16 dtim_policy,
+ u16 tx_wakeup_policy, u16 num_tx_to_wakeup,
+ u16 ps_fail_event_policy)
+{
+ struct sk_buff *skb;
+ struct wmi_power_params_cmd *pm;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*pm));
+ if (!skb)
+ return -ENOMEM;
+
+ pm = (struct wmi_power_params_cmd *)skb->data;
+ pm->idle_period = cpu_to_le16(idle_period);
+ pm->pspoll_number = cpu_to_le16(ps_poll_num);
+ pm->dtim_policy = cpu_to_le16(dtim_policy);
+ pm->tx_wakeup_policy = cpu_to_le16(tx_wakeup_policy);
+ pm->num_tx_to_wakeup = cpu_to_le16(num_tx_to_wakeup);
+ pm->ps_fail_event_policy = cpu_to_le16(ps_fail_event_policy);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_POWER_PARAMS_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 timeout)
+{
+ struct sk_buff *skb;
+ struct wmi_disc_timeout_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_disc_timeout_cmd *) skb->data;
+ cmd->discon_timeout = timeout;
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_DISC_TIMEOUT_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
+ enum crypto_type key_type,
+ u8 key_usage, u8 key_len,
+ u8 *key_rsc, u8 *key_material,
+ u8 key_op_ctrl, u8 *mac_addr,
+ enum wmi_sync_flag sync_flag)
+{
+ struct sk_buff *skb;
+ struct wmi_add_cipher_key_cmd *cmd;
+ int ret;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "addkey cmd: key_index=%u key_type=%d "
+ "key_usage=%d key_len=%d key_op_ctrl=%d\n",
+ key_index, key_type, key_usage, key_len, key_op_ctrl);
+
+ if ((key_index > WMI_MAX_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) ||
+ (key_material == NULL))
+ return -EINVAL;
+
+ if ((WEP_CRYPT != key_type) && (NULL == key_rsc))
+ return -EINVAL;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_add_cipher_key_cmd *) skb->data;
+ cmd->key_index = key_index;
+ cmd->key_type = key_type;
+ cmd->key_usage = key_usage;
+ cmd->key_len = key_len;
+ memcpy(cmd->key, key_material, key_len);
+
+ if (key_rsc != NULL)
+ memcpy(cmd->key_rsc, key_rsc, sizeof(cmd->key_rsc));
+
+ cmd->key_op_ctrl = key_op_ctrl;
+
+ if (mac_addr)
+ memcpy(cmd->key_mac_addr, mac_addr, ETH_ALEN);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_ADD_CIPHER_KEY_CMDID,
+ sync_flag);
+
+ return ret;
+}
+
+int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 *krk)
+{
+ struct sk_buff *skb;
+ struct wmi_add_krk_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_add_krk_cmd *) skb->data;
+ memcpy(cmd->krk, krk, WMI_KRK_LEN);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_ADD_KRK_CMDID, NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 key_index)
+{
+ struct sk_buff *skb;
+ struct wmi_delete_cipher_key_cmd *cmd;
+ int ret;
+
+ if (key_index > WMI_MAX_KEY_INDEX)
+ return -EINVAL;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_delete_cipher_key_cmd *) skb->data;
+ cmd->key_index = key_index;
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_DELETE_CIPHER_KEY_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, const u8 *bssid,
+ const u8 *pmkid, bool set)
+{
+ struct sk_buff *skb;
+ struct wmi_setpmkid_cmd *cmd;
+ int ret;
+
+ if (bssid == NULL)
+ return -EINVAL;
+
+ if (set && pmkid == NULL)
+ return -EINVAL;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_setpmkid_cmd *) skb->data;
+ memcpy(cmd->bssid, bssid, ETH_ALEN);
+ if (set) {
+ memcpy(cmd->pmkid, pmkid, sizeof(cmd->pmkid));
+ cmd->enable = PMKID_ENABLE;
+ } else {
+ memset(cmd->pmkid, 0, sizeof(cmd->pmkid));
+ cmd->enable = PMKID_DISABLE;
+ }
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_PMKID_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+static int ath6kl_wmi_data_sync_send(struct wmi *wmi, struct sk_buff *skb,
+ enum htc_endpoint_id ep_id)
+{
+ struct wmi_data_hdr *data_hdr;
+ int ret;
+
+ if (WARN_ON(skb == NULL || ep_id == wmi->ep_id))
+ return -EINVAL;
+
+ skb_push(skb, sizeof(struct wmi_data_hdr));
+
+ data_hdr = (struct wmi_data_hdr *) skb->data;
+ data_hdr->info = SYNC_MSGTYPE << WMI_DATA_HDR_MSG_TYPE_SHIFT;
+ data_hdr->info3 = 0;
+
+ ret = ath6kl_control_tx(wmi->parent_dev, skb, ep_id);
+
+ return ret;
+}
+
+static int ath6kl_wmi_sync_point(struct wmi *wmi)
+{
+ struct sk_buff *skb;
+ struct wmi_sync_cmd *cmd;
+ struct wmi_data_sync_bufs data_sync_bufs[WMM_NUM_AC];
+ enum htc_endpoint_id ep_id;
+ u8 index, num_pri_streams = 0;
+ int ret = 0;
+
+ memset(data_sync_bufs, 0, sizeof(data_sync_bufs));
+
+ spin_lock_bh(&wmi->lock);
+
+ for (index = 0; index < WMM_NUM_AC; index++) {
+ if (wmi->fat_pipe_exist & (1 << index)) {
+ num_pri_streams++;
+ data_sync_bufs[num_pri_streams - 1].traffic_class =
+ index;
+ }
+ }
+
+ spin_unlock_bh(&wmi->lock);
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb) {
+ ret = -ENOMEM;
+ goto free_skb;
+ }
+
+ cmd = (struct wmi_sync_cmd *) skb->data;
+
+ /*
+ * In the SYNC cmd sent on the control Ep, send a bitmap
+ * of the data eps on which the Data Sync will be sent
+ */
+ cmd->data_sync_map = wmi->fat_pipe_exist;
+
+ for (index = 0; index < num_pri_streams; index++) {
+ data_sync_bufs[index].skb = ath6kl_buf_alloc(0);
+ if (data_sync_bufs[index].skb == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+ }
+
+ /*
+ * If buffer allocation for any of the dataSync fails,
+ * then do not send the Synchronize cmd on the control ep
+ */
+ if (ret)
+ goto free_skb;
+
+ /*
+ * Send sync cmd followed by sync data messages on all
+ * endpoints being used
+ */
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SYNCHRONIZE_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ if (ret)
+ goto free_skb;
+
+ /* cmd buffer sent, we no longer own it */
+ skb = NULL;
+
+ for (index = 0; index < num_pri_streams; index++) {
+
+ if (WARN_ON(!data_sync_bufs[index].skb))
+ break;
+
+ ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev,
+ data_sync_bufs[index].
+ traffic_class);
+ ret =
+ ath6kl_wmi_data_sync_send(wmi, data_sync_bufs[index].skb,
+ ep_id);
+
+ if (ret)
+ break;
+
+ data_sync_bufs[index].skb = NULL;
+ }
+
+free_skb:
+ /* free up any resources left over (possibly due to an error) */
+ if (skb)
+ dev_kfree_skb(skb);
+
+ for (index = 0; index < num_pri_streams; index++) {
+ if (data_sync_bufs[index].skb != NULL) {
+ dev_kfree_skb((struct sk_buff *)data_sync_bufs[index].
+ skb);
+ }
+ }
+
+ return ret;
+}
+
+int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi,
+ struct wmi_create_pstream_cmd *params)
+{
+ struct sk_buff *skb;
+ struct wmi_create_pstream_cmd *cmd;
+ u8 fatpipe_exist_for_ac = 0;
+ s32 min_phy = 0;
+ s32 nominal_phy = 0;
+ int ret;
+
+ if (!((params->user_pri < 8) &&
+ (params->user_pri <= 0x7) &&
+ (up_to_ac[params->user_pri & 0x7] == params->traffic_class) &&
+ (params->traffic_direc == UPLINK_TRAFFIC ||
+ params->traffic_direc == DNLINK_TRAFFIC ||
+ params->traffic_direc == BIDIR_TRAFFIC) &&
+ (params->traffic_type == TRAFFIC_TYPE_APERIODIC ||
+ params->traffic_type == TRAFFIC_TYPE_PERIODIC) &&
+ (params->voice_psc_cap == DISABLE_FOR_THIS_AC ||
+ params->voice_psc_cap == ENABLE_FOR_THIS_AC ||
+ params->voice_psc_cap == ENABLE_FOR_ALL_AC) &&
+ (params->tsid == WMI_IMPLICIT_PSTREAM ||
+ params->tsid <= WMI_MAX_THINSTREAM))) {
+ return -EINVAL;
+ }
+
+ /*
+ * Check nominal PHY rate is >= minimalPHY,
+ * so that DUT can allow TSRS IE
+ */
+
+ /* Get the physical rate (units of bps) */
+ min_phy = ((le32_to_cpu(params->min_phy_rate) / 1000) / 1000);
+
+ /* Check minimal phy < nominal phy rate */
+ if (params->nominal_phy >= min_phy) {
+ /* unit of 500 kbps */
+ nominal_phy = (params->nominal_phy * 1000) / 500;
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "TSRS IE enabled::MinPhy %x->NominalPhy ===> %x\n",
+ min_phy, nominal_phy);
+
+ params->nominal_phy = nominal_phy;
+ } else {
+ params->nominal_phy = 0;
+ }
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "sending create_pstream_cmd: ac=%d tsid:%d\n",
+ params->traffic_class, params->tsid);
+
+ cmd = (struct wmi_create_pstream_cmd *) skb->data;
+ memcpy(cmd, params, sizeof(*cmd));
+
+ /* This is an implicitly created Fat pipe */
+ if ((u32) params->tsid == (u32) WMI_IMPLICIT_PSTREAM) {
+ spin_lock_bh(&wmi->lock);
+ fatpipe_exist_for_ac = (wmi->fat_pipe_exist &
+ (1 << params->traffic_class));
+ wmi->fat_pipe_exist |= (1 << params->traffic_class);
+ spin_unlock_bh(&wmi->lock);
+ } else {
+ /* explicitly created thin stream within a fat pipe */
+ spin_lock_bh(&wmi->lock);
+ fatpipe_exist_for_ac = (wmi->fat_pipe_exist &
+ (1 << params->traffic_class));
+ wmi->stream_exist_for_ac[params->traffic_class] |=
+ (1 << params->tsid);
+ /*
+ * If a thinstream becomes active, the fat pipe automatically
+ * becomes active
+ */
+ wmi->fat_pipe_exist |= (1 << params->traffic_class);
+ spin_unlock_bh(&wmi->lock);
+ }
+
+ /*
+ * Indicate activty change to driver layer only if this is the
+ * first TSID to get created in this AC explicitly or an implicit
+ * fat pipe is getting created.
+ */
+ if (!fatpipe_exist_for_ac)
+ ath6kl_indicate_tx_activity(wmi->parent_dev,
+ params->traffic_class, true);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_CREATE_PSTREAM_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 traffic_class, u8 tsid)
+{
+ struct sk_buff *skb;
+ struct wmi_delete_pstream_cmd *cmd;
+ u16 active_tsids = 0;
+ int ret;
+
+ if (traffic_class > 3) {
+ ath6kl_err("invalid traffic class: %d\n", traffic_class);
+ return -EINVAL;
+ }
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_delete_pstream_cmd *) skb->data;
+ cmd->traffic_class = traffic_class;
+ cmd->tsid = tsid;
+
+ spin_lock_bh(&wmi->lock);
+ active_tsids = wmi->stream_exist_for_ac[traffic_class];
+ spin_unlock_bh(&wmi->lock);
+
+ if (!(active_tsids & (1 << tsid))) {
+ dev_kfree_skb(skb);
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "TSID %d doesn't exist for traffic class: %d\n",
+ tsid, traffic_class);
+ return -ENODATA;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "sending delete_pstream_cmd: traffic class: %d tsid=%d\n",
+ traffic_class, tsid);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_DELETE_PSTREAM_CMDID,
+ SYNC_BEFORE_WMIFLAG);
+
+ spin_lock_bh(&wmi->lock);
+ wmi->stream_exist_for_ac[traffic_class] &= ~(1 << tsid);
+ active_tsids = wmi->stream_exist_for_ac[traffic_class];
+ spin_unlock_bh(&wmi->lock);
+
+ /*
+ * Indicate stream inactivity to driver layer only if all tsids
+ * within this AC are deleted.
+ */
+ if (!active_tsids) {
+ ath6kl_indicate_tx_activity(wmi->parent_dev,
+ traffic_class, false);
+ wmi->fat_pipe_exist &= ~(1 << traffic_class);
+ }
+
+ return ret;
+}
+
+int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd)
+{
+ struct sk_buff *skb;
+ struct wmi_set_ip_cmd *cmd;
+ int ret;
+
+ /* Multicast address are not valid */
+ if ((*((u8 *) &ip_cmd->ips[0]) >= 0xE0) ||
+ (*((u8 *) &ip_cmd->ips[1]) >= 0xE0))
+ return -EINVAL;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_ip_cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_ip_cmd *) skb->data;
+ memcpy(cmd, ip_cmd, sizeof(struct wmi_set_ip_cmd));
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_IP_CMDID, NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+static int ath6kl_wmi_get_wow_list_event_rx(struct wmi *wmi, u8 * datap,
+ int len)
+{
+ if (len < sizeof(struct wmi_get_wow_list_reply))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ath6kl_wmi_cmd_send_xtnd(struct wmi *wmi, struct sk_buff *skb,
+ enum wmix_command_id cmd_id,
+ enum wmi_sync_flag sync_flag)
+{
+ struct wmix_cmd_hdr *cmd_hdr;
+ int ret;
+
+ skb_push(skb, sizeof(struct wmix_cmd_hdr));
+
+ cmd_hdr = (struct wmix_cmd_hdr *) skb->data;
+ cmd_hdr->cmd_id = cpu_to_le32(cmd_id);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_EXTENSION_CMDID, sync_flag);
+
+ return ret;
+}
+
+int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source)
+{
+ struct sk_buff *skb;
+ struct wmix_hb_challenge_resp_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmix_hb_challenge_resp_cmd *) skb->data;
+ cmd->cookie = cpu_to_le32(cookie);
+ cmd->source = cpu_to_le32(source);
+
+ ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_HB_CHALLENGE_RESP_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config)
+{
+ struct ath6kl_wmix_dbglog_cfg_module_cmd *cmd;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct ath6kl_wmix_dbglog_cfg_module_cmd *) skb->data;
+ cmd->valid = cpu_to_le32(valid);
+ cmd->config = cpu_to_le32(config);
+
+ ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_DBGLOG_CFG_MODULE_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_get_stats_cmd(struct wmi *wmi)
+{
+ return ath6kl_wmi_simple_cmd(wmi, WMI_GET_STATISTICS_CMDID);
+}
+
+int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 dbM)
+{
+ struct sk_buff *skb;
+ struct wmi_set_tx_pwr_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_tx_pwr_cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_tx_pwr_cmd *) skb->data;
+ cmd->dbM = dbM;
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_TX_PWR_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi)
+{
+ return ath6kl_wmi_simple_cmd(wmi, WMI_GET_TX_PWR_CMDID);
+}
+
+int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status, u8 preamble_policy)
+{
+ struct sk_buff *skb;
+ struct wmi_set_lpreamble_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_lpreamble_cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_lpreamble_cmd *) skb->data;
+ cmd->status = status;
+ cmd->preamble_policy = preamble_policy;
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_LPREAMBLE_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_set_rts_cmd(struct wmi *wmi, u16 threshold)
+{
+ struct sk_buff *skb;
+ struct wmi_set_rts_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_rts_cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_rts_cmd *) skb->data;
+ cmd->threshold = cpu_to_le16(threshold);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_RTS_CMDID, NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, enum wmi_txop_cfg cfg)
+{
+ struct sk_buff *skb;
+ struct wmi_set_wmm_txop_cmd *cmd;
+ int ret;
+
+ if (!((cfg == WMI_TXOP_DISABLED) || (cfg == WMI_TXOP_ENABLED)))
+ return -EINVAL;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_wmm_txop_cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_wmm_txop_cmd *) skb->data;
+ cmd->txop_enable = cfg;
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_WMM_TXOP_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl)
+{
+ struct sk_buff *skb;
+ struct wmi_set_keepalive_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_keepalive_cmd *) skb->data;
+ cmd->keep_alive_intvl = keep_alive_intvl;
+ wmi->keep_alive_intvl = keep_alive_intvl;
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_KEEPALIVE_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(len);
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb->data, buf, len);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_TEST_CMDID, NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+
+s32 ath6kl_wmi_get_rate(s8 rate_index)
+{
+ if (rate_index == RATE_AUTO)
+ return 0;
+
+ return wmi_rate_tbl[(u32) rate_index][0];
+}
+
+static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap,
+ u32 len)
+{
+ struct wmi_pmkid_list_reply *reply;
+ u32 expected_len;
+
+ if (len < sizeof(struct wmi_pmkid_list_reply))
+ return -EINVAL;
+
+ reply = (struct wmi_pmkid_list_reply *)datap;
+ expected_len = sizeof(reply->num_pmkid) +
+ le32_to_cpu(reply->num_pmkid) * WMI_PMKID_LEN;
+
+ if (len < expected_len)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ath6kl_wmi_addba_req_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_addba_req_event *cmd = (struct wmi_addba_req_event *) datap;
+
+ aggr_recv_addba_req_evt(wmi->parent_dev, cmd->tid,
+ le16_to_cpu(cmd->st_seq_no), cmd->win_sz);
+
+ return 0;
+}
+
+static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_delba_event *cmd = (struct wmi_delba_event *) datap;
+
+ aggr_recv_delba_req_evt(wmi->parent_dev, cmd->tid);
+
+ return 0;
+}
+
+/* AP mode functions */
+
+int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p)
+{
+ struct sk_buff *skb;
+ struct wmi_connect_cmd *cm;
+ int res;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cm));
+ if (!skb)
+ return -ENOMEM;
+
+ cm = (struct wmi_connect_cmd *) skb->data;
+ memcpy(cm, p, sizeof(*cm));
+
+ res = ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_CONFIG_COMMIT_CMDID,
+ NO_SYNC_WMIFLAG);
+ ath6kl_dbg(ATH6KL_DBG_WMI, "%s: nw_type=%u auth_mode=%u ch=%u "
+ "ctrl_flags=0x%x-> res=%d\n",
+ __func__, p->nw_type, p->auth_mode, le16_to_cpu(p->ch),
+ le32_to_cpu(p->ctrl_flags), res);
+ return res;
+}
+
+int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason)
+{
+ struct sk_buff *skb;
+ struct wmi_ap_set_mlme_cmd *cm;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cm));
+ if (!skb)
+ return -ENOMEM;
+
+ cm = (struct wmi_ap_set_mlme_cmd *) skb->data;
+ memcpy(cm->mac, mac, ETH_ALEN);
+ cm->reason = cpu_to_le16(reason);
+ cm->cmd = cmd;
+
+ return ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_SET_MLME_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ struct wmi_pspoll_event *ev;
+
+ if (len < sizeof(struct wmi_pspoll_event))
+ return -EINVAL;
+
+ ev = (struct wmi_pspoll_event *) datap;
+
+ ath6kl_pspoll_event(wmi->parent_dev, le16_to_cpu(ev->aid));
+
+ return 0;
+}
+
+static int ath6kl_wmi_dtimexpiry_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+ ath6kl_dtimexpiry_event(wmi->parent_dev);
+
+ return 0;
+}
+
+int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag)
+{
+ struct sk_buff *skb;
+ struct wmi_ap_set_pvb_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_ap_set_pvb_cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_ap_set_pvb_cmd *) skb->data;
+ cmd->aid = cpu_to_le16(aid);
+ cmd->rsvd = cpu_to_le16(0);
+ cmd->flag = cpu_to_le32(flag);
+
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_AP_SET_PVB_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ return 0;
+}
+
+int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_ver,
+ bool rx_dot11_hdr, bool defrag_on_host)
+{
+ struct sk_buff *skb;
+ struct wmi_rx_frame_format_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_rx_frame_format_cmd *) skb->data;
+ cmd->dot11_hdr = rx_dot11_hdr ? 1 : 0;
+ cmd->defrag_on_host = defrag_on_host ? 1 : 0;
+ cmd->meta_ver = rx_meta_ver;
+
+ /* Delete the local aggr state, on host */
+ ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_RX_FRAME_FORMAT_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
+ u8 ie_len)
+{
+ struct sk_buff *skb;
+ struct wmi_set_appie_cmd *p;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len);
+ if (!skb)
+ return -ENOMEM;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "set_appie_cmd: mgmt_frm_type=%u "
+ "ie_len=%u\n", mgmt_frm_type, ie_len);
+ p = (struct wmi_set_appie_cmd *) skb->data;
+ p->mgmt_frm_type = mgmt_frm_type;
+ p->ie_len = ie_len;
+ memcpy(p->ie_info, ie, ie_len);
+ return ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_APPIE_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable)
+{
+ struct sk_buff *skb;
+ struct wmi_disable_11b_rates_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "disable_11b_rates_cmd: disable=%u\n",
+ disable);
+ cmd = (struct wmi_disable_11b_rates_cmd *) skb->data;
+ cmd->disable = disable ? 1 : 0;
+
+ return ath6kl_wmi_cmd_send(wmi, skb, WMI_DISABLE_11B_RATES_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur)
+{
+ struct sk_buff *skb;
+ struct wmi_remain_on_chnl_cmd *p;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*p));
+ if (!skb)
+ return -ENOMEM;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl_cmd: freq=%u dur=%u\n",
+ freq, dur);
+ p = (struct wmi_remain_on_chnl_cmd *) skb->data;
+ p->freq = cpu_to_le32(freq);
+ p->duration = cpu_to_le32(dur);
+ return ath6kl_wmi_cmd_send(wmi, skb, WMI_REMAIN_ON_CHNL_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait,
+ const u8 *data, u16 data_len)
+{
+ struct sk_buff *skb;
+ struct wmi_send_action_cmd *p;
+ u8 *buf;
+
+ if (wait)
+ return -EINVAL; /* Offload for wait not supported */
+
+ buf = kmalloc(data_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len);
+ if (!skb) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ kfree(wmi->last_mgmt_tx_frame);
+ wmi->last_mgmt_tx_frame = buf;
+ wmi->last_mgmt_tx_frame_len = data_len;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u "
+ "len=%u\n", id, freq, wait, data_len);
+ p = (struct wmi_send_action_cmd *) skb->data;
+ p->id = cpu_to_le32(id);
+ p->freq = cpu_to_le32(freq);
+ p->wait = cpu_to_le32(wait);
+ p->len = cpu_to_le16(data_len);
+ memcpy(p->data, data, data_len);
+ return ath6kl_wmi_cmd_send(wmi, skb, WMI_SEND_ACTION_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u32 freq,
+ const u8 *dst,
+ const u8 *data, u16 data_len)
+{
+ struct sk_buff *skb;
+ struct wmi_p2p_probe_response_cmd *p;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len);
+ if (!skb)
+ return -ENOMEM;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "send_probe_response_cmd: freq=%u dst=%pM "
+ "len=%u\n", freq, dst, data_len);
+ p = (struct wmi_p2p_probe_response_cmd *) skb->data;
+ p->freq = cpu_to_le32(freq);
+ memcpy(p->destination_addr, dst, ETH_ALEN);
+ p->len = cpu_to_le16(data_len);
+ memcpy(p->data, data, data_len);
+ return ath6kl_wmi_cmd_send(wmi, skb, WMI_SEND_PROBE_RESPONSE_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable)
+{
+ struct sk_buff *skb;
+ struct wmi_probe_req_report_cmd *p;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*p));
+ if (!skb)
+ return -ENOMEM;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "probe_report_req_cmd: enable=%u\n",
+ enable);
+ p = (struct wmi_probe_req_report_cmd *) skb->data;
+ p->enable = enable ? 1 : 0;
+ return ath6kl_wmi_cmd_send(wmi, skb, WMI_PROBE_REQ_REPORT_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags)
+{
+ struct sk_buff *skb;
+ struct wmi_get_p2p_info *p;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*p));
+ if (!skb)
+ return -ENOMEM;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "info_req_cmd: flags=%x\n",
+ info_req_flags);
+ p = (struct wmi_get_p2p_info *) skb->data;
+ p->info_req_flags = cpu_to_le32(info_req_flags);
+ return ath6kl_wmi_cmd_send(wmi, skb, WMI_GET_P2P_INFO_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi)
+{
+ ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl_cmd\n");
+ return ath6kl_wmi_simple_cmd(wmi, WMI_CANCEL_REMAIN_ON_CHNL_CMDID);
+}
+
+static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
+{
+ struct wmix_cmd_hdr *cmd;
+ u32 len;
+ u16 id;
+ u8 *datap;
+ int ret = 0;
+
+ if (skb->len < sizeof(struct wmix_cmd_hdr)) {
+ ath6kl_err("bad packet 1\n");
+ wmi->stat.cmd_len_err++;
+ return -EINVAL;
+ }
+
+ cmd = (struct wmix_cmd_hdr *) skb->data;
+ id = le32_to_cpu(cmd->cmd_id);
+
+ skb_pull(skb, sizeof(struct wmix_cmd_hdr));
+
+ datap = skb->data;
+ len = skb->len;
+
+ switch (id) {
+ case WMIX_HB_CHALLENGE_RESP_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n");
+ break;
+ case WMIX_DBGLOG_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len);
+ ath6kl_debug_fwlog_event(wmi->parent_dev, datap, len);
+ break;
+ default:
+ ath6kl_warn("unknown cmd id 0x%x\n", id);
+ wmi->stat.cmd_id_err++;
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/* Control Path */
+int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
+{
+ struct wmi_cmd_hdr *cmd;
+ u32 len;
+ u16 id;
+ u8 *datap;
+ int ret = 0;
+
+ if (WARN_ON(skb == NULL))
+ return -EINVAL;
+
+ if (skb->len < sizeof(struct wmi_cmd_hdr)) {
+ ath6kl_err("bad packet 1\n");
+ dev_kfree_skb(skb);
+ wmi->stat.cmd_len_err++;
+ return -EINVAL;
+ }
+
+ cmd = (struct wmi_cmd_hdr *) skb->data;
+ id = le16_to_cpu(cmd->cmd_id);
+
+ skb_pull(skb, sizeof(struct wmi_cmd_hdr));
+
+ datap = skb->data;
+ len = skb->len;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "wmi rx id %d len %d\n", id, len);
+ ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi rx ",
+ datap, len);
+
+ switch (id) {
+ case WMI_GET_BITRATE_CMDID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_BITRATE_CMDID\n");
+ ret = ath6kl_wmi_bitrate_reply_rx(wmi, datap, len);
+ break;
+ case WMI_GET_CHANNEL_LIST_CMDID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_CHANNEL_LIST_CMDID\n");
+ ret = ath6kl_wmi_ch_list_reply_rx(wmi, datap, len);
+ break;
+ case WMI_GET_TX_PWR_CMDID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_TX_PWR_CMDID\n");
+ ret = ath6kl_wmi_tx_pwr_reply_rx(wmi, datap, len);
+ break;
+ case WMI_READY_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_READY_EVENTID\n");
+ ret = ath6kl_wmi_ready_event_rx(wmi, datap, len);
+ break;
+ case WMI_CONNECT_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n");
+ ret = ath6kl_wmi_connect_event_rx(wmi, datap, len);
+ break;
+ case WMI_DISCONNECT_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n");
+ ret = ath6kl_wmi_disconnect_event_rx(wmi, datap, len);
+ break;
+ case WMI_PEER_NODE_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PEER_NODE_EVENTID\n");
+ ret = ath6kl_wmi_peer_node_event_rx(wmi, datap, len);
+ break;
+ case WMI_TKIP_MICERR_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n");
+ ret = ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len);
+ break;
+ case WMI_BSSINFO_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n");
+ ret = ath6kl_wmi_bssinfo_event_rx(wmi, datap, len);
+ break;
+ case WMI_REGDOMAIN_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n");
+ ath6kl_wmi_regdomain_event(wmi, datap, len);
+ break;
+ case WMI_PSTREAM_TIMEOUT_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n");
+ ret = ath6kl_wmi_pstream_timeout_event_rx(wmi, datap, len);
+ break;
+ case WMI_NEIGHBOR_REPORT_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n");
+ ret = ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len);
+ break;
+ case WMI_SCAN_COMPLETE_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
+ ret = ath6kl_wmi_scan_complete_rx(wmi, datap, len);
+ break;
+ case WMI_CMDERROR_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CMDERROR_EVENTID\n");
+ ret = ath6kl_wmi_error_event_rx(wmi, datap, len);
+ break;
+ case WMI_REPORT_STATISTICS_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n");
+ ret = ath6kl_wmi_stats_event_rx(wmi, datap, len);
+ break;
+ case WMI_RSSI_THRESHOLD_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RSSI_THRESHOLD_EVENTID\n");
+ ret = ath6kl_wmi_rssi_threshold_event_rx(wmi, datap, len);
+ break;
+ case WMI_ERROR_REPORT_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ERROR_REPORT_EVENTID\n");
+ break;
+ case WMI_OPT_RX_FRAME_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_OPT_RX_FRAME_EVENTID\n");
+ /* this event has been deprecated */
+ break;
+ case WMI_REPORT_ROAM_TBL_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_TBL_EVENTID\n");
+ break;
+ case WMI_EXTENSION_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_EXTENSION_EVENTID\n");
+ ret = ath6kl_wmi_control_rx_xtnd(wmi, skb);
+ break;
+ case WMI_CAC_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n");
+ ret = ath6kl_wmi_cac_event_rx(wmi, datap, len);
+ break;
+ case WMI_CHANNEL_CHANGE_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CHANNEL_CHANGE_EVENTID\n");
+ break;
+ case WMI_REPORT_ROAM_DATA_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_DATA_EVENTID\n");
+ break;
+ case WMI_TEST_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TEST_EVENTID\n");
+ ret = ath6kl_wmi_tcmd_test_report_rx(wmi, datap, len);
+ break;
+ case WMI_GET_FIXRATES_CMDID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_FIXRATES_CMDID\n");
+ ret = ath6kl_wmi_ratemask_reply_rx(wmi, datap, len);
+ break;
+ case WMI_TX_RETRY_ERR_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_RETRY_ERR_EVENTID\n");
+ break;
+ case WMI_SNR_THRESHOLD_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SNR_THRESHOLD_EVENTID\n");
+ ret = ath6kl_wmi_snr_threshold_event_rx(wmi, datap, len);
+ break;
+ case WMI_LQ_THRESHOLD_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_LQ_THRESHOLD_EVENTID\n");
+ break;
+ case WMI_APLIST_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_APLIST_EVENTID\n");
+ ret = ath6kl_wmi_aplist_event_rx(wmi, datap, len);
+ break;
+ case WMI_GET_KEEPALIVE_CMDID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_KEEPALIVE_CMDID\n");
+ ret = ath6kl_wmi_keepalive_reply_rx(wmi, datap, len);
+ break;
+ case WMI_GET_WOW_LIST_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_WOW_LIST_EVENTID\n");
+ ret = ath6kl_wmi_get_wow_list_event_rx(wmi, datap, len);
+ break;
+ case WMI_GET_PMKID_LIST_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_PMKID_LIST_EVENTID\n");
+ ret = ath6kl_wmi_get_pmkid_list_event_rx(wmi, datap, len);
+ break;
+ case WMI_PSPOLL_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n");
+ ret = ath6kl_wmi_pspoll_event_rx(wmi, datap, len);
+ break;
+ case WMI_DTIMEXPIRY_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n");
+ ret = ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len);
+ break;
+ case WMI_SET_PARAMS_REPLY_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SET_PARAMS_REPLY_EVENTID\n");
+ break;
+ case WMI_ADDBA_REQ_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n");
+ ret = ath6kl_wmi_addba_req_event_rx(wmi, datap, len);
+ break;
+ case WMI_ADDBA_RESP_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_RESP_EVENTID\n");
+ break;
+ case WMI_DELBA_REQ_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n");
+ ret = ath6kl_wmi_delba_req_event_rx(wmi, datap, len);
+ break;
+ case WMI_REPORT_BTCOEX_CONFIG_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "WMI_REPORT_BTCOEX_CONFIG_EVENTID\n");
+ break;
+ case WMI_REPORT_BTCOEX_STATS_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "WMI_REPORT_BTCOEX_STATS_EVENTID\n");
+ break;
+ case WMI_TX_COMPLETE_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n");
+ ret = ath6kl_wmi_tx_complete_event_rx(datap, len);
+ break;
+ case WMI_REMAIN_ON_CHNL_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n");
+ ret = ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len);
+ break;
+ case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n");
+ ret = ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap,
+ len);
+ break;
+ case WMI_TX_STATUS_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n");
+ ret = ath6kl_wmi_tx_status_event_rx(wmi, datap, len);
+ break;
+ case WMI_RX_PROBE_REQ_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n");
+ ret = ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len);
+ break;
+ case WMI_P2P_CAPABILITIES_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_CAPABILITIES_EVENTID\n");
+ ret = ath6kl_wmi_p2p_capabilities_event_rx(datap, len);
+ break;
+ case WMI_RX_ACTION_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n");
+ ret = ath6kl_wmi_rx_action_event_rx(wmi, datap, len);
+ break;
+ case WMI_P2P_INFO_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_INFO_EVENTID\n");
+ ret = ath6kl_wmi_p2p_info_event_rx(datap, len);
+ break;
+ default:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", id);
+ wmi->stat.cmd_id_err++;
+ ret = -EINVAL;
+ break;
+ }
+
+ dev_kfree_skb(skb);
+
+ return ret;
+}
+
+static void ath6kl_wmi_qos_state_init(struct wmi *wmi)
+{
+ if (!wmi)
+ return;
+
+ spin_lock_bh(&wmi->lock);
+
+ wmi->fat_pipe_exist = 0;
+ memset(wmi->stream_exist_for_ac, 0, sizeof(wmi->stream_exist_for_ac));
+
+ spin_unlock_bh(&wmi->lock);
+}
+
+void *ath6kl_wmi_init(struct ath6kl *dev)
+{
+ struct wmi *wmi;
+
+ wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL);
+ if (!wmi)
+ return NULL;
+
+ spin_lock_init(&wmi->lock);
+
+ wmi->parent_dev = dev;
+
+ ath6kl_wmi_qos_state_init(wmi);
+
+ wmi->pwr_mode = REC_POWER;
+ wmi->phy_mode = WMI_11G_MODE;
+
+ wmi->pair_crypto_type = NONE_CRYPT;
+ wmi->grp_crypto_type = NONE_CRYPT;
+
+ wmi->ht_allowed[A_BAND_24GHZ] = 1;
+ wmi->ht_allowed[A_BAND_5GHZ] = 1;
+
+ return wmi;
+}
+
+void ath6kl_wmi_shutdown(struct wmi *wmi)
+{
+ if (!wmi)
+ return;
+
+ kfree(wmi->last_mgmt_tx_frame);
+ kfree(wmi);
+}
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h
new file mode 100644
index 000000000000..f8e644d54aa7
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/wmi.h
@@ -0,0 +1,2282 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This file contains the definitions of the WMI protocol specified in the
+ * Wireless Module Interface (WMI). It includes definitions of all the
+ * commands and events. Commands are messages from the host to the WM.
+ * Events and Replies are messages from the WM to the host.
+ */
+
+#ifndef WMI_H
+#define WMI_H
+
+#include <linux/ieee80211.h>
+
+#include "htc.h"
+
+#define HTC_PROTOCOL_VERSION 0x0002
+#define WMI_PROTOCOL_VERSION 0x0002
+#define WMI_CONTROL_MSG_MAX_LEN 256
+#define is_ethertype(type_or_len) ((type_or_len) >= 0x0600)
+
+#define IP_ETHERTYPE 0x0800
+
+#define WMI_IMPLICIT_PSTREAM 0xFF
+#define WMI_MAX_THINSTREAM 15
+
+#define SSID_IE_LEN_INDEX 13
+
+/* Host side link management data structures */
+#define SIG_QUALITY_THRESH_LVLS 6
+#define SIG_QUALITY_UPPER_THRESH_LVLS SIG_QUALITY_THRESH_LVLS
+#define SIG_QUALITY_LOWER_THRESH_LVLS SIG_QUALITY_THRESH_LVLS
+
+#define A_BAND_24GHZ 0
+#define A_BAND_5GHZ 1
+#define A_NUM_BANDS 2
+
+/* in ms */
+#define WMI_IMPLICIT_PSTREAM_INACTIVITY_INT 5000
+
+/*
+ * There are no signed versions of __le16 and __le32, so for a temporary
+ * solution come up with our own version. The idea is from fs/ntfs/types.h.
+ *
+ * Use a_ prefix so that it doesn't conflict if we get proper support to
+ * linux/types.h.
+ */
+typedef __s16 __bitwise a_sle16;
+typedef __s32 __bitwise a_sle32;
+
+static inline a_sle32 a_cpu_to_sle32(s32 val)
+{
+ return (__force a_sle32) cpu_to_le32(val);
+}
+
+static inline s32 a_sle32_to_cpu(a_sle32 val)
+{
+ return le32_to_cpu((__force __le32) val);
+}
+
+static inline a_sle16 a_cpu_to_sle16(s16 val)
+{
+ return (__force a_sle16) cpu_to_le16(val);
+}
+
+static inline s16 a_sle16_to_cpu(a_sle16 val)
+{
+ return le16_to_cpu((__force __le16) val);
+}
+
+struct sq_threshold_params {
+ s16 upper_threshold[SIG_QUALITY_UPPER_THRESH_LVLS];
+ s16 lower_threshold[SIG_QUALITY_LOWER_THRESH_LVLS];
+ u32 upper_threshold_valid_count;
+ u32 lower_threshold_valid_count;
+ u32 polling_interval;
+ u8 weight;
+ u8 last_rssi;
+ u8 last_rssi_poll_event;
+};
+
+struct wmi_stats {
+ u32 cmd_len_err;
+ u32 cmd_id_err;
+};
+
+struct wmi_data_sync_bufs {
+ u8 traffic_class;
+ struct sk_buff *skb;
+};
+
+/* WMM stream classes */
+#define WMM_NUM_AC 4
+#define WMM_AC_BE 0 /* best effort */
+#define WMM_AC_BK 1 /* background */
+#define WMM_AC_VI 2 /* video */
+#define WMM_AC_VO 3 /* voice */
+
+struct wmi {
+ bool ready;
+ u16 stream_exist_for_ac[WMM_NUM_AC];
+ u8 fat_pipe_exist;
+ struct ath6kl *parent_dev;
+ struct wmi_stats stat;
+ u8 pwr_mode;
+ u8 phy_mode;
+ u8 keep_alive_intvl;
+ spinlock_t lock;
+ enum htc_endpoint_id ep_id;
+ struct sq_threshold_params
+ sq_threshld[SIGNAL_QUALITY_METRICS_NUM_MAX];
+ enum crypto_type pair_crypto_type;
+ enum crypto_type grp_crypto_type;
+ bool is_wmm_enabled;
+ u8 ht_allowed[A_NUM_BANDS];
+ u8 traffic_class;
+ bool is_probe_ssid;
+
+ u8 *last_mgmt_tx_frame;
+ size_t last_mgmt_tx_frame_len;
+};
+
+struct host_app_area {
+ u32 wmi_protocol_ver;
+};
+
+enum wmi_msg_type {
+ DATA_MSGTYPE = 0x0,
+ CNTL_MSGTYPE,
+ SYNC_MSGTYPE,
+ OPT_MSGTYPE,
+};
+
+/*
+ * Macros for operating on WMI_DATA_HDR (info) field
+ */
+
+#define WMI_DATA_HDR_MSG_TYPE_MASK 0x03
+#define WMI_DATA_HDR_MSG_TYPE_SHIFT 0
+#define WMI_DATA_HDR_UP_MASK 0x07
+#define WMI_DATA_HDR_UP_SHIFT 2
+
+/* In AP mode, the same bit (b5) is used to indicate Power save state in
+ * the Rx dir and More data bit state in the tx direction.
+ */
+#define WMI_DATA_HDR_PS_MASK 0x1
+#define WMI_DATA_HDR_PS_SHIFT 5
+
+#define WMI_DATA_HDR_MORE_MASK 0x1
+#define WMI_DATA_HDR_MORE_SHIFT 5
+
+enum wmi_data_hdr_data_type {
+ WMI_DATA_HDR_DATA_TYPE_802_3 = 0,
+ WMI_DATA_HDR_DATA_TYPE_802_11,
+
+ /* used to be used for the PAL */
+ WMI_DATA_HDR_DATA_TYPE_ACL,
+};
+
+#define WMI_DATA_HDR_DATA_TYPE_MASK 0x3
+#define WMI_DATA_HDR_DATA_TYPE_SHIFT 6
+
+/* Macros for operating on WMI_DATA_HDR (info2) field */
+#define WMI_DATA_HDR_SEQNO_MASK 0xFFF
+#define WMI_DATA_HDR_SEQNO_SHIFT 0
+
+#define WMI_DATA_HDR_AMSDU_MASK 0x1
+#define WMI_DATA_HDR_AMSDU_SHIFT 12
+
+#define WMI_DATA_HDR_META_MASK 0x7
+#define WMI_DATA_HDR_META_SHIFT 13
+
+struct wmi_data_hdr {
+ s8 rssi;
+
+ /*
+ * usage of 'info' field(8-bit):
+ *
+ * b1:b0 - WMI_MSG_TYPE
+ * b4:b3:b2 - UP(tid)
+ * b5 - Used in AP mode.
+ * More-data in tx dir, PS in rx.
+ * b7:b6 - Dot3 header(0),
+ * Dot11 Header(1),
+ * ACL data(2)
+ */
+ u8 info;
+
+ /*
+ * usage of 'info2' field(16-bit):
+ *
+ * b11:b0 - seq_no
+ * b12 - A-MSDU?
+ * b15:b13 - META_DATA_VERSION 0 - 7
+ */
+ __le16 info2;
+ __le16 info3;
+} __packed;
+
+static inline u8 wmi_data_hdr_get_up(struct wmi_data_hdr *dhdr)
+{
+ return (dhdr->info >> WMI_DATA_HDR_UP_SHIFT) & WMI_DATA_HDR_UP_MASK;
+}
+
+static inline void wmi_data_hdr_set_up(struct wmi_data_hdr *dhdr,
+ u8 usr_pri)
+{
+ dhdr->info &= ~(WMI_DATA_HDR_UP_MASK << WMI_DATA_HDR_UP_SHIFT);
+ dhdr->info |= usr_pri << WMI_DATA_HDR_UP_SHIFT;
+}
+
+static inline u8 wmi_data_hdr_get_dot11(struct wmi_data_hdr *dhdr)
+{
+ u8 data_type;
+
+ data_type = (dhdr->info >> WMI_DATA_HDR_DATA_TYPE_SHIFT) &
+ WMI_DATA_HDR_DATA_TYPE_MASK;
+ return (data_type == WMI_DATA_HDR_DATA_TYPE_802_11);
+}
+
+static inline u16 wmi_data_hdr_get_seqno(struct wmi_data_hdr *dhdr)
+{
+ return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_SEQNO_SHIFT) &
+ WMI_DATA_HDR_SEQNO_MASK;
+}
+
+static inline u8 wmi_data_hdr_is_amsdu(struct wmi_data_hdr *dhdr)
+{
+ return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_AMSDU_SHIFT) &
+ WMI_DATA_HDR_AMSDU_MASK;
+}
+
+static inline u8 wmi_data_hdr_get_meta(struct wmi_data_hdr *dhdr)
+{
+ return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_META_SHIFT) &
+ WMI_DATA_HDR_META_MASK;
+}
+
+/* Tx meta version definitions */
+#define WMI_MAX_TX_META_SZ 12
+#define WMI_META_VERSION_1 0x01
+#define WMI_META_VERSION_2 0x02
+
+struct wmi_tx_meta_v1 {
+ /* packet ID to identify the tx request */
+ u8 pkt_id;
+
+ /* rate policy to be used for the tx of this frame */
+ u8 rate_plcy_id;
+} __packed;
+
+struct wmi_tx_meta_v2 {
+ /*
+ * Offset from start of the WMI header for csum calculation to
+ * begin.
+ */
+ u8 csum_start;
+
+ /* offset from start of WMI header where final csum goes */
+ u8 csum_dest;
+
+ /* no of bytes over which csum is calculated */
+ u8 csum_flags;
+} __packed;
+
+struct wmi_rx_meta_v1 {
+ u8 status;
+
+ /* rate index mapped to rate at which this packet was received. */
+ u8 rix;
+
+ /* rssi of packet */
+ u8 rssi;
+
+ /* rf channel during packet reception */
+ u8 channel;
+
+ __le16 flags;
+} __packed;
+
+struct wmi_rx_meta_v2 {
+ __le16 csum;
+
+ /* bit 0 set -partial csum valid bit 1 set -test mode */
+ u8 csum_flags;
+} __packed;
+
+/* Control Path */
+struct wmi_cmd_hdr {
+ __le16 cmd_id;
+
+ /* info1 - 16 bits
+ * b03:b00 - id
+ * b15:b04 - unused */
+ __le16 info1;
+
+ /* for alignment */
+ __le16 reserved;
+} __packed;
+
+/* List of WMI commands */
+enum wmi_cmd_id {
+ WMI_CONNECT_CMDID = 0x0001,
+ WMI_RECONNECT_CMDID,
+ WMI_DISCONNECT_CMDID,
+ WMI_SYNCHRONIZE_CMDID,
+ WMI_CREATE_PSTREAM_CMDID,
+ WMI_DELETE_PSTREAM_CMDID,
+ WMI_START_SCAN_CMDID,
+ WMI_SET_SCAN_PARAMS_CMDID,
+ WMI_SET_BSS_FILTER_CMDID,
+ WMI_SET_PROBED_SSID_CMDID, /* 10 */
+ WMI_SET_LISTEN_INT_CMDID,
+ WMI_SET_BMISS_TIME_CMDID,
+ WMI_SET_DISC_TIMEOUT_CMDID,
+ WMI_GET_CHANNEL_LIST_CMDID,
+ WMI_SET_BEACON_INT_CMDID,
+ WMI_GET_STATISTICS_CMDID,
+ WMI_SET_CHANNEL_PARAMS_CMDID,
+ WMI_SET_POWER_MODE_CMDID,
+ WMI_SET_IBSS_PM_CAPS_CMDID,
+ WMI_SET_POWER_PARAMS_CMDID, /* 20 */
+ WMI_SET_POWERSAVE_TIMERS_POLICY_CMDID,
+ WMI_ADD_CIPHER_KEY_CMDID,
+ WMI_DELETE_CIPHER_KEY_CMDID,
+ WMI_ADD_KRK_CMDID,
+ WMI_DELETE_KRK_CMDID,
+ WMI_SET_PMKID_CMDID,
+ WMI_SET_TX_PWR_CMDID,
+ WMI_GET_TX_PWR_CMDID,
+ WMI_SET_ASSOC_INFO_CMDID,
+ WMI_ADD_BAD_AP_CMDID, /* 30 */
+ WMI_DELETE_BAD_AP_CMDID,
+ WMI_SET_TKIP_COUNTERMEASURES_CMDID,
+ WMI_RSSI_THRESHOLD_PARAMS_CMDID,
+ WMI_TARGET_ERROR_REPORT_BITMASK_CMDID,
+ WMI_SET_ACCESS_PARAMS_CMDID,
+ WMI_SET_RETRY_LIMITS_CMDID,
+ WMI_SET_OPT_MODE_CMDID,
+ WMI_OPT_TX_FRAME_CMDID,
+ WMI_SET_VOICE_PKT_SIZE_CMDID,
+ WMI_SET_MAX_SP_LEN_CMDID, /* 40 */
+ WMI_SET_ROAM_CTRL_CMDID,
+ WMI_GET_ROAM_TBL_CMDID,
+ WMI_GET_ROAM_DATA_CMDID,
+ WMI_ENABLE_RM_CMDID,
+ WMI_SET_MAX_OFFHOME_DURATION_CMDID,
+ WMI_EXTENSION_CMDID, /* Non-wireless extensions */
+ WMI_SNR_THRESHOLD_PARAMS_CMDID,
+ WMI_LQ_THRESHOLD_PARAMS_CMDID,
+ WMI_SET_LPREAMBLE_CMDID,
+ WMI_SET_RTS_CMDID, /* 50 */
+ WMI_CLR_RSSI_SNR_CMDID,
+ WMI_SET_FIXRATES_CMDID,
+ WMI_GET_FIXRATES_CMDID,
+ WMI_SET_AUTH_MODE_CMDID,
+ WMI_SET_REASSOC_MODE_CMDID,
+ WMI_SET_WMM_CMDID,
+ WMI_SET_WMM_TXOP_CMDID,
+ WMI_TEST_CMDID,
+
+ /* COEX AR6002 only */
+ WMI_SET_BT_STATUS_CMDID,
+ WMI_SET_BT_PARAMS_CMDID, /* 60 */
+
+ WMI_SET_KEEPALIVE_CMDID,
+ WMI_GET_KEEPALIVE_CMDID,
+ WMI_SET_APPIE_CMDID,
+ WMI_GET_APPIE_CMDID,
+ WMI_SET_WSC_STATUS_CMDID,
+
+ /* Wake on Wireless */
+ WMI_SET_HOST_SLEEP_MODE_CMDID,
+ WMI_SET_WOW_MODE_CMDID,
+ WMI_GET_WOW_LIST_CMDID,
+ WMI_ADD_WOW_PATTERN_CMDID,
+ WMI_DEL_WOW_PATTERN_CMDID, /* 70 */
+
+ WMI_SET_FRAMERATES_CMDID,
+ WMI_SET_AP_PS_CMDID,
+ WMI_SET_QOS_SUPP_CMDID,
+
+ /* WMI_THIN_RESERVED_... mark the start and end
+ * values for WMI_THIN_RESERVED command IDs. These
+ * command IDs can be found in wmi_thin.h */
+ WMI_THIN_RESERVED_START = 0x8000,
+ WMI_THIN_RESERVED_END = 0x8fff,
+
+ /* Developer commands starts at 0xF000 */
+ WMI_SET_BITRATE_CMDID = 0xF000,
+ WMI_GET_BITRATE_CMDID,
+ WMI_SET_WHALPARAM_CMDID,
+ WMI_SET_MAC_ADDRESS_CMDID,
+ WMI_SET_AKMP_PARAMS_CMDID,
+ WMI_SET_PMKID_LIST_CMDID,
+ WMI_GET_PMKID_LIST_CMDID,
+ WMI_ABORT_SCAN_CMDID,
+ WMI_SET_TARGET_EVENT_REPORT_CMDID,
+
+ /* Unused */
+ WMI_UNUSED1,
+ WMI_UNUSED2,
+
+ /* AP mode commands */
+ WMI_AP_HIDDEN_SSID_CMDID,
+ WMI_AP_SET_NUM_STA_CMDID,
+ WMI_AP_ACL_POLICY_CMDID,
+ WMI_AP_ACL_MAC_LIST_CMDID,
+ WMI_AP_CONFIG_COMMIT_CMDID,
+ WMI_AP_SET_MLME_CMDID,
+ WMI_AP_SET_PVB_CMDID,
+ WMI_AP_CONN_INACT_CMDID,
+ WMI_AP_PROT_SCAN_TIME_CMDID,
+ WMI_AP_SET_COUNTRY_CMDID,
+ WMI_AP_SET_DTIM_CMDID,
+ WMI_AP_MODE_STAT_CMDID,
+
+ WMI_SET_IP_CMDID,
+ WMI_SET_PARAMS_CMDID,
+ WMI_SET_MCAST_FILTER_CMDID,
+ WMI_DEL_MCAST_FILTER_CMDID,
+
+ WMI_ALLOW_AGGR_CMDID,
+ WMI_ADDBA_REQ_CMDID,
+ WMI_DELBA_REQ_CMDID,
+ WMI_SET_HT_CAP_CMDID,
+ WMI_SET_HT_OP_CMDID,
+ WMI_SET_TX_SELECT_RATES_CMDID,
+ WMI_SET_TX_SGI_PARAM_CMDID,
+ WMI_SET_RATE_POLICY_CMDID,
+
+ WMI_HCI_CMD_CMDID,
+ WMI_RX_FRAME_FORMAT_CMDID,
+ WMI_SET_THIN_MODE_CMDID,
+ WMI_SET_BT_WLAN_CONN_PRECEDENCE_CMDID,
+
+ WMI_AP_SET_11BG_RATESET_CMDID,
+ WMI_SET_PMK_CMDID,
+ WMI_MCAST_FILTER_CMDID,
+
+ /* COEX CMDID AR6003 */
+ WMI_SET_BTCOEX_FE_ANT_CMDID,
+ WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID,
+ WMI_SET_BTCOEX_SCO_CONFIG_CMDID,
+ WMI_SET_BTCOEX_A2DP_CONFIG_CMDID,
+ WMI_SET_BTCOEX_ACLCOEX_CONFIG_CMDID,
+ WMI_SET_BTCOEX_BTINQUIRY_PAGE_CONFIG_CMDID,
+ WMI_SET_BTCOEX_DEBUG_CMDID,
+ WMI_SET_BTCOEX_BT_OPERATING_STATUS_CMDID,
+ WMI_GET_BTCOEX_STATS_CMDID,
+ WMI_GET_BTCOEX_CONFIG_CMDID,
+
+ WMI_SET_DFS_ENABLE_CMDID, /* F034 */
+ WMI_SET_DFS_MINRSSITHRESH_CMDID,
+ WMI_SET_DFS_MAXPULSEDUR_CMDID,
+ WMI_DFS_RADAR_DETECTED_CMDID,
+
+ /* P2P commands */
+ WMI_P2P_SET_CONFIG_CMDID, /* F038 */
+ WMI_WPS_SET_CONFIG_CMDID,
+ WMI_SET_REQ_DEV_ATTR_CMDID,
+ WMI_P2P_FIND_CMDID,
+ WMI_P2P_STOP_FIND_CMDID,
+ WMI_P2P_GO_NEG_START_CMDID,
+ WMI_P2P_LISTEN_CMDID,
+
+ WMI_CONFIG_TX_MAC_RULES_CMDID, /* F040 */
+ WMI_SET_PROMISCUOUS_MODE_CMDID,
+ WMI_RX_FRAME_FILTER_CMDID,
+ WMI_SET_CHANNEL_CMDID,
+
+ /* WAC commands */
+ WMI_ENABLE_WAC_CMDID,
+ WMI_WAC_SCAN_REPLY_CMDID,
+ WMI_WAC_CTRL_REQ_CMDID,
+ WMI_SET_DIV_PARAMS_CMDID,
+
+ WMI_GET_PMK_CMDID,
+ WMI_SET_PASSPHRASE_CMDID,
+ WMI_SEND_ASSOC_RES_CMDID,
+ WMI_SET_ASSOC_REQ_RELAY_CMDID,
+
+ /* ACS command, consists of sub-commands */
+ WMI_ACS_CTRL_CMDID,
+ WMI_SET_EXCESS_TX_RETRY_THRES_CMDID,
+ WMI_SET_TBD_TIME_CMDID, /*added for wmiconfig command for TBD */
+
+ /* Pktlog cmds */
+ WMI_PKTLOG_ENABLE_CMDID,
+ WMI_PKTLOG_DISABLE_CMDID,
+
+ /* More P2P Cmds */
+ WMI_P2P_GO_NEG_REQ_RSP_CMDID,
+ WMI_P2P_GRP_INIT_CMDID,
+ WMI_P2P_GRP_FORMATION_DONE_CMDID,
+ WMI_P2P_INVITE_CMDID,
+ WMI_P2P_INVITE_REQ_RSP_CMDID,
+ WMI_P2P_PROV_DISC_REQ_CMDID,
+ WMI_P2P_SET_CMDID,
+
+ WMI_GET_RFKILL_MODE_CMDID,
+ WMI_SET_RFKILL_MODE_CMDID,
+ WMI_AP_SET_APSD_CMDID,
+ WMI_AP_APSD_BUFFERED_TRAFFIC_CMDID,
+
+ WMI_P2P_SDPD_TX_CMDID, /* F05C */
+ WMI_P2P_STOP_SDPD_CMDID,
+ WMI_P2P_CANCEL_CMDID,
+ /* Ultra low power store / recall commands */
+ WMI_STORERECALL_CONFIGURE_CMDID,
+ WMI_STORERECALL_RECALL_CMDID,
+ WMI_STORERECALL_HOST_READY_CMDID,
+ WMI_FORCE_TARGET_ASSERT_CMDID,
+
+ WMI_SET_PROBED_SSID_EX_CMDID,
+ WMI_SET_NETWORK_LIST_OFFLOAD_CMDID,
+ WMI_SET_ARP_NS_OFFLOAD_CMDID,
+ WMI_ADD_WOW_EXT_PATTERN_CMDID,
+ WMI_GTK_OFFLOAD_OP_CMDID,
+ WMI_REMAIN_ON_CHNL_CMDID,
+ WMI_CANCEL_REMAIN_ON_CHNL_CMDID,
+ WMI_SEND_ACTION_CMDID,
+ WMI_PROBE_REQ_REPORT_CMDID,
+ WMI_DISABLE_11B_RATES_CMDID,
+ WMI_SEND_PROBE_RESPONSE_CMDID,
+ WMI_GET_P2P_INFO_CMDID,
+ WMI_AP_JOIN_BSS_CMDID,
+};
+
+enum wmi_mgmt_frame_type {
+ WMI_FRAME_BEACON = 0,
+ WMI_FRAME_PROBE_REQ,
+ WMI_FRAME_PROBE_RESP,
+ WMI_FRAME_ASSOC_REQ,
+ WMI_FRAME_ASSOC_RESP,
+ WMI_NUM_MGMT_FRAME
+};
+
+/* WMI_CONNECT_CMDID */
+enum network_type {
+ INFRA_NETWORK = 0x01,
+ ADHOC_NETWORK = 0x02,
+ ADHOC_CREATOR = 0x04,
+ AP_NETWORK = 0x10,
+};
+
+enum dot11_auth_mode {
+ OPEN_AUTH = 0x01,
+ SHARED_AUTH = 0x02,
+
+ /* different from IEEE_AUTH_MODE definitions */
+ LEAP_AUTH = 0x04,
+};
+
+enum auth_mode {
+ NONE_AUTH = 0x01,
+ WPA_AUTH = 0x02,
+ WPA2_AUTH = 0x04,
+ WPA_PSK_AUTH = 0x08,
+ WPA2_PSK_AUTH = 0x10,
+ WPA_AUTH_CCKM = 0x20,
+ WPA2_AUTH_CCKM = 0x40,
+};
+
+#define WMI_MIN_CRYPTO_TYPE NONE_CRYPT
+#define WMI_MAX_CRYPTO_TYPE (AES_CRYPT + 1)
+
+#define WMI_MIN_KEY_INDEX 0
+#define WMI_MAX_KEY_INDEX 3
+
+#define WMI_MAX_KEY_LEN 32
+
+/*
+ * NB: these values are ordered carefully; there are lots of
+ * of implications in any reordering. In particular beware
+ * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ */
+#define ATH6KL_CIPHER_WEP 0
+#define ATH6KL_CIPHER_TKIP 1
+#define ATH6KL_CIPHER_AES_OCB 2
+#define ATH6KL_CIPHER_AES_CCM 3
+#define ATH6KL_CIPHER_CKIP 5
+#define ATH6KL_CIPHER_CCKM_KRK 6
+#define ATH6KL_CIPHER_NONE 7 /* pseudo value */
+
+/*
+ * 802.11 rate set.
+ */
+#define ATH6KL_RATE_MAXSIZE 15 /* max rates we'll handle */
+
+#define ATH_OUI_TYPE 0x01
+#define WPA_OUI_TYPE 0x01
+#define WMM_PARAM_OUI_SUBTYPE 0x01
+#define WMM_OUI_TYPE 0x02
+#define WSC_OUT_TYPE 0x04
+
+enum wmi_connect_ctrl_flags_bits {
+ CONNECT_ASSOC_POLICY_USER = 0x0001,
+ CONNECT_SEND_REASSOC = 0x0002,
+ CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004,
+ CONNECT_PROFILE_MATCH_DONE = 0x0008,
+ CONNECT_IGNORE_AAC_BEACON = 0x0010,
+ CONNECT_CSA_FOLLOW_BSS = 0x0020,
+ CONNECT_DO_WPA_OFFLOAD = 0x0040,
+ CONNECT_DO_NOT_DEAUTH = 0x0080,
+};
+
+struct wmi_connect_cmd {
+ u8 nw_type;
+ u8 dot11_auth_mode;
+ u8 auth_mode;
+ u8 prwise_crypto_type;
+ u8 prwise_crypto_len;
+ u8 grp_crypto_type;
+ u8 grp_crypto_len;
+ u8 ssid_len;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ __le16 ch;
+ u8 bssid[ETH_ALEN];
+ __le32 ctrl_flags;
+} __packed;
+
+/* WMI_RECONNECT_CMDID */
+struct wmi_reconnect_cmd {
+ /* channel hint */
+ __le16 channel;
+
+ /* mandatory if set */
+ u8 bssid[ETH_ALEN];
+} __packed;
+
+/* WMI_ADD_CIPHER_KEY_CMDID */
+enum key_usage {
+ PAIRWISE_USAGE = 0x00,
+ GROUP_USAGE = 0x01,
+
+ /* default Tx Key - static WEP only */
+ TX_USAGE = 0x02,
+};
+
+/*
+ * Bit Flag
+ * Bit 0 - Initialise TSC - default is Initialize
+ */
+#define KEY_OP_INIT_TSC 0x01
+#define KEY_OP_INIT_RSC 0x02
+
+/* default initialise the TSC & RSC */
+#define KEY_OP_INIT_VAL 0x03
+#define KEY_OP_VALID_MASK 0x03
+
+struct wmi_add_cipher_key_cmd {
+ u8 key_index;
+ u8 key_type;
+
+ /* enum key_usage */
+ u8 key_usage;
+
+ u8 key_len;
+
+ /* key replay sequence counter */
+ u8 key_rsc[8];
+
+ u8 key[WLAN_MAX_KEY_LEN];
+
+ /* additional key control info */
+ u8 key_op_ctrl;
+
+ u8 key_mac_addr[ETH_ALEN];
+} __packed;
+
+/* WMI_DELETE_CIPHER_KEY_CMDID */
+struct wmi_delete_cipher_key_cmd {
+ u8 key_index;
+} __packed;
+
+#define WMI_KRK_LEN 16
+
+/* WMI_ADD_KRK_CMDID */
+struct wmi_add_krk_cmd {
+ u8 krk[WMI_KRK_LEN];
+} __packed;
+
+/* WMI_SETPMKID_CMDID */
+
+#define WMI_PMKID_LEN 16
+
+enum pmkid_enable_flg {
+ PMKID_DISABLE = 0,
+ PMKID_ENABLE = 1,
+};
+
+struct wmi_setpmkid_cmd {
+ u8 bssid[ETH_ALEN];
+
+ /* enum pmkid_enable_flg */
+ u8 enable;
+
+ u8 pmkid[WMI_PMKID_LEN];
+} __packed;
+
+/* WMI_START_SCAN_CMD */
+enum wmi_scan_type {
+ WMI_LONG_SCAN = 0,
+ WMI_SHORT_SCAN = 1,
+};
+
+struct wmi_start_scan_cmd {
+ __le32 force_fg_scan;
+
+ /* for legacy cisco AP compatibility */
+ __le32 is_legacy;
+
+ /* max duration in the home channel(msec) */
+ __le32 home_dwell_time;
+
+ /* time interval between scans (msec) */
+ __le32 force_scan_intvl;
+
+ /* enum wmi_scan_type */
+ u8 scan_type;
+
+ /* how many channels follow */
+ u8 num_ch;
+
+ /* channels in Mhz */
+ __le16 ch_list[1];
+} __packed;
+
+/* WMI_SET_SCAN_PARAMS_CMDID */
+#define WMI_SHORTSCANRATIO_DEFAULT 3
+
+/*
+ * Warning: scan control flag value of 0xFF is used to disable
+ * all flags in WMI_SCAN_PARAMS_CMD. Do not add any more
+ * flags here
+ */
+enum wmi_scan_ctrl_flags_bits {
+
+ /* set if can scan in the connect cmd */
+ CONNECT_SCAN_CTRL_FLAGS = 0x01,
+
+ /* set if scan for the SSID it is already connected to */
+ SCAN_CONNECTED_CTRL_FLAGS = 0x02,
+
+ /* set if enable active scan */
+ ACTIVE_SCAN_CTRL_FLAGS = 0x04,
+
+ /* set if enable roam scan when bmiss and lowrssi */
+ ROAM_SCAN_CTRL_FLAGS = 0x08,
+
+ /* set if follows customer BSSINFO reporting rule */
+ REPORT_BSSINFO_CTRL_FLAGS = 0x10,
+
+ /* if disabled, target doesn't scan after a disconnect event */
+ ENABLE_AUTO_CTRL_FLAGS = 0x20,
+
+ /*
+ * Scan complete event with canceled status will be generated when
+ * a scan is prempted before it gets completed.
+ */
+ ENABLE_SCAN_ABORT_EVENT = 0x40
+};
+
+#define DEFAULT_SCAN_CTRL_FLAGS \
+ (CONNECT_SCAN_CTRL_FLAGS | \
+ SCAN_CONNECTED_CTRL_FLAGS | \
+ ACTIVE_SCAN_CTRL_FLAGS | \
+ ROAM_SCAN_CTRL_FLAGS | \
+ ENABLE_AUTO_CTRL_FLAGS)
+
+struct wmi_scan_params_cmd {
+ /* sec */
+ __le16 fg_start_period;
+
+ /* sec */
+ __le16 fg_end_period;
+
+ /* sec */
+ __le16 bg_period;
+
+ /* msec */
+ __le16 maxact_chdwell_time;
+
+ /* msec */
+ __le16 pas_chdwell_time;
+
+ /* how many shorts scan for one long */
+ u8 short_scan_ratio;
+
+ u8 scan_ctrl_flags;
+
+ /* msec */
+ __le16 minact_chdwell_time;
+
+ /* max active scans per ssid */
+ __le16 maxact_scan_per_ssid;
+
+ /* msecs */
+ __le32 max_dfsch_act_time;
+} __packed;
+
+/* WMI_SET_BSS_FILTER_CMDID */
+enum wmi_bss_filter {
+ /* no beacons forwarded */
+ NONE_BSS_FILTER = 0x0,
+
+ /* all beacons forwarded */
+ ALL_BSS_FILTER,
+
+ /* only beacons matching profile */
+ PROFILE_FILTER,
+
+ /* all but beacons matching profile */
+ ALL_BUT_PROFILE_FILTER,
+
+ /* only beacons matching current BSS */
+ CURRENT_BSS_FILTER,
+
+ /* all but beacons matching BSS */
+ ALL_BUT_BSS_FILTER,
+
+ /* beacons matching probed ssid */
+ PROBED_SSID_FILTER,
+
+ /* marker only */
+ LAST_BSS_FILTER,
+};
+
+struct wmi_bss_filter_cmd {
+ /* see, enum wmi_bss_filter */
+ u8 bss_filter;
+
+ /* for alignment */
+ u8 reserved1;
+
+ /* for alignment */
+ __le16 reserved2;
+
+ __le32 ie_mask;
+} __packed;
+
+/* WMI_SET_PROBED_SSID_CMDID */
+#define MAX_PROBED_SSID_INDEX 9
+
+enum wmi_ssid_flag {
+ /* disables entry */
+ DISABLE_SSID_FLAG = 0,
+
+ /* probes specified ssid */
+ SPECIFIC_SSID_FLAG = 0x01,
+
+ /* probes for any ssid */
+ ANY_SSID_FLAG = 0x02,
+};
+
+struct wmi_probed_ssid_cmd {
+ /* 0 to MAX_PROBED_SSID_INDEX */
+ u8 entry_index;
+
+ /* see, enum wmi_ssid_flg */
+ u8 flag;
+
+ u8 ssid_len;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+} __packed;
+
+/*
+ * WMI_SET_LISTEN_INT_CMDID
+ * The Listen interval is between 15 and 3000 TUs
+ */
+struct wmi_listen_int_cmd {
+ __le16 listen_intvl;
+ __le16 num_beacons;
+} __packed;
+
+/* WMI_SET_POWER_MODE_CMDID */
+enum wmi_power_mode {
+ REC_POWER = 0x01,
+ MAX_PERF_POWER,
+};
+
+struct wmi_power_mode_cmd {
+ /* see, enum wmi_power_mode */
+ u8 pwr_mode;
+} __packed;
+
+/*
+ * Policy to determnine whether power save failure event should be sent to
+ * host during scanning
+ */
+enum power_save_fail_event_policy {
+ SEND_POWER_SAVE_FAIL_EVENT_ALWAYS = 1,
+ IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN = 2,
+};
+
+struct wmi_power_params_cmd {
+ /* msec */
+ __le16 idle_period;
+
+ __le16 pspoll_number;
+ __le16 dtim_policy;
+ __le16 tx_wakeup_policy;
+ __le16 num_tx_to_wakeup;
+ __le16 ps_fail_event_policy;
+} __packed;
+
+/* WMI_SET_DISC_TIMEOUT_CMDID */
+struct wmi_disc_timeout_cmd {
+ /* seconds */
+ u8 discon_timeout;
+} __packed;
+
+enum dir_type {
+ UPLINK_TRAFFIC = 0,
+ DNLINK_TRAFFIC = 1,
+ BIDIR_TRAFFIC = 2,
+};
+
+enum voiceps_cap_type {
+ DISABLE_FOR_THIS_AC = 0,
+ ENABLE_FOR_THIS_AC = 1,
+ ENABLE_FOR_ALL_AC = 2,
+};
+
+enum traffic_type {
+ TRAFFIC_TYPE_APERIODIC = 0,
+ TRAFFIC_TYPE_PERIODIC = 1,
+};
+
+/* WMI_SYNCHRONIZE_CMDID */
+struct wmi_sync_cmd {
+ u8 data_sync_map;
+} __packed;
+
+/* WMI_CREATE_PSTREAM_CMDID */
+struct wmi_create_pstream_cmd {
+ /* msec */
+ __le32 min_service_int;
+
+ /* msec */
+ __le32 max_service_int;
+
+ /* msec */
+ __le32 inactivity_int;
+
+ /* msec */
+ __le32 suspension_int;
+
+ __le32 service_start_time;
+
+ /* in bps */
+ __le32 min_data_rate;
+
+ /* in bps */
+ __le32 mean_data_rate;
+
+ /* in bps */
+ __le32 peak_data_rate;
+
+ __le32 max_burst_size;
+ __le32 delay_bound;
+
+ /* in bps */
+ __le32 min_phy_rate;
+
+ __le32 sba;
+ __le32 medium_time;
+
+ /* in octects */
+ __le16 nominal_msdu;
+
+ /* in octects */
+ __le16 max_msdu;
+
+ u8 traffic_class;
+
+ /* see, enum dir_type */
+ u8 traffic_direc;
+
+ u8 rx_queue_num;
+
+ /* see, enum traffic_type */
+ u8 traffic_type;
+
+ /* see, enum voiceps_cap_type */
+ u8 voice_psc_cap;
+ u8 tsid;
+
+ /* 802.1D user priority */
+ u8 user_pri;
+
+ /* nominal phy rate */
+ u8 nominal_phy;
+} __packed;
+
+/* WMI_DELETE_PSTREAM_CMDID */
+struct wmi_delete_pstream_cmd {
+ u8 tx_queue_num;
+ u8 rx_queue_num;
+ u8 traffic_direc;
+ u8 traffic_class;
+ u8 tsid;
+} __packed;
+
+/* WMI_SET_CHANNEL_PARAMS_CMDID */
+enum wmi_phy_mode {
+ WMI_11A_MODE = 0x1,
+ WMI_11G_MODE = 0x2,
+ WMI_11AG_MODE = 0x3,
+ WMI_11B_MODE = 0x4,
+ WMI_11GONLY_MODE = 0x5,
+};
+
+#define WMI_MAX_CHANNELS 32
+
+/*
+ * WMI_RSSI_THRESHOLD_PARAMS_CMDID
+ * Setting the polltime to 0 would disable polling. Threshold values are
+ * in the ascending order, and should agree to:
+ * (lowThreshold_lowerVal < lowThreshold_upperVal < highThreshold_lowerVal
+ * < highThreshold_upperVal)
+ */
+
+struct wmi_rssi_threshold_params_cmd {
+ /* polling time as a factor of LI */
+ __le32 poll_time;
+
+ /* lowest of upper */
+ a_sle16 thresh_above1_val;
+
+ a_sle16 thresh_above2_val;
+ a_sle16 thresh_above3_val;
+ a_sle16 thresh_above4_val;
+ a_sle16 thresh_above5_val;
+
+ /* highest of upper */
+ a_sle16 thresh_above6_val;
+
+ /* lowest of bellow */
+ a_sle16 thresh_below1_val;
+
+ a_sle16 thresh_below2_val;
+ a_sle16 thresh_below3_val;
+ a_sle16 thresh_below4_val;
+ a_sle16 thresh_below5_val;
+
+ /* highest of bellow */
+ a_sle16 thresh_below6_val;
+
+ /* "alpha" */
+ u8 weight;
+
+ u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_SNR_THRESHOLD_PARAMS_CMDID
+ * Setting the polltime to 0 would disable polling.
+ */
+
+struct wmi_snr_threshold_params_cmd {
+ /* polling time as a factor of LI */
+ __le32 poll_time;
+
+ /* "alpha" */
+ u8 weight;
+
+ /* lowest of uppper */
+ u8 thresh_above1_val;
+
+ u8 thresh_above2_val;
+ u8 thresh_above3_val;
+
+ /* highest of upper */
+ u8 thresh_above4_val;
+
+ /* lowest of bellow */
+ u8 thresh_below1_val;
+
+ u8 thresh_below2_val;
+ u8 thresh_below3_val;
+
+ /* highest of bellow */
+ u8 thresh_below4_val;
+
+ u8 reserved[3];
+} __packed;
+
+enum wmi_preamble_policy {
+ WMI_IGNORE_BARKER_IN_ERP = 0,
+ WMI_DONOT_IGNORE_BARKER_IN_ERP
+};
+
+struct wmi_set_lpreamble_cmd {
+ u8 status;
+ u8 preamble_policy;
+} __packed;
+
+struct wmi_set_rts_cmd {
+ __le16 threshold;
+} __packed;
+
+/* WMI_SET_TX_PWR_CMDID */
+struct wmi_set_tx_pwr_cmd {
+ /* in dbM units */
+ u8 dbM;
+} __packed;
+
+struct wmi_tx_pwr_reply {
+ /* in dbM units */
+ u8 dbM;
+} __packed;
+
+struct wmi_report_sleep_state_event {
+ __le32 sleep_state;
+};
+
+enum wmi_report_sleep_status {
+ WMI_REPORT_SLEEP_STATUS_IS_DEEP_SLEEP = 0,
+ WMI_REPORT_SLEEP_STATUS_IS_AWAKE
+};
+enum target_event_report_config {
+ /* default */
+ DISCONN_EVT_IN_RECONN = 0,
+
+ NO_DISCONN_EVT_IN_RECONN
+};
+
+/* Command Replies */
+
+/* WMI_GET_CHANNEL_LIST_CMDID reply */
+struct wmi_channel_list_reply {
+ u8 reserved;
+
+ /* number of channels in reply */
+ u8 num_ch;
+
+ /* channel in Mhz */
+ __le16 ch_list[1];
+} __packed;
+
+/* List of Events (target to host) */
+enum wmi_event_id {
+ WMI_READY_EVENTID = 0x1001,
+ WMI_CONNECT_EVENTID,
+ WMI_DISCONNECT_EVENTID,
+ WMI_BSSINFO_EVENTID,
+ WMI_CMDERROR_EVENTID,
+ WMI_REGDOMAIN_EVENTID,
+ WMI_PSTREAM_TIMEOUT_EVENTID,
+ WMI_NEIGHBOR_REPORT_EVENTID,
+ WMI_TKIP_MICERR_EVENTID,
+ WMI_SCAN_COMPLETE_EVENTID, /* 0x100a */
+ WMI_REPORT_STATISTICS_EVENTID,
+ WMI_RSSI_THRESHOLD_EVENTID,
+ WMI_ERROR_REPORT_EVENTID,
+ WMI_OPT_RX_FRAME_EVENTID,
+ WMI_REPORT_ROAM_TBL_EVENTID,
+ WMI_EXTENSION_EVENTID,
+ WMI_CAC_EVENTID,
+ WMI_SNR_THRESHOLD_EVENTID,
+ WMI_LQ_THRESHOLD_EVENTID,
+ WMI_TX_RETRY_ERR_EVENTID, /* 0x1014 */
+ WMI_REPORT_ROAM_DATA_EVENTID,
+ WMI_TEST_EVENTID,
+ WMI_APLIST_EVENTID,
+ WMI_GET_WOW_LIST_EVENTID,
+ WMI_GET_PMKID_LIST_EVENTID,
+ WMI_CHANNEL_CHANGE_EVENTID,
+ WMI_PEER_NODE_EVENTID,
+ WMI_PSPOLL_EVENTID,
+ WMI_DTIMEXPIRY_EVENTID,
+ WMI_WLAN_VERSION_EVENTID,
+ WMI_SET_PARAMS_REPLY_EVENTID,
+ WMI_ADDBA_REQ_EVENTID, /*0x1020 */
+ WMI_ADDBA_RESP_EVENTID,
+ WMI_DELBA_REQ_EVENTID,
+ WMI_TX_COMPLETE_EVENTID,
+ WMI_HCI_EVENT_EVENTID,
+ WMI_ACL_DATA_EVENTID,
+ WMI_REPORT_SLEEP_STATE_EVENTID,
+ WMI_REPORT_BTCOEX_STATS_EVENTID,
+ WMI_REPORT_BTCOEX_CONFIG_EVENTID,
+ WMI_GET_PMK_EVENTID,
+
+ /* DFS Events */
+ WMI_DFS_HOST_ATTACH_EVENTID,
+ WMI_DFS_HOST_INIT_EVENTID,
+ WMI_DFS_RESET_DELAYLINES_EVENTID,
+ WMI_DFS_RESET_RADARQ_EVENTID,
+ WMI_DFS_RESET_AR_EVENTID,
+ WMI_DFS_RESET_ARQ_EVENTID,
+ WMI_DFS_SET_DUR_MULTIPLIER_EVENTID,
+ WMI_DFS_SET_BANGRADAR_EVENTID,
+ WMI_DFS_SET_DEBUGLEVEL_EVENTID,
+ WMI_DFS_PHYERR_EVENTID,
+
+ /* CCX Evants */
+ WMI_CCX_RM_STATUS_EVENTID,
+
+ /* P2P Events */
+ WMI_P2P_GO_NEG_RESULT_EVENTID,
+
+ WMI_WAC_SCAN_DONE_EVENTID,
+ WMI_WAC_REPORT_BSS_EVENTID,
+ WMI_WAC_START_WPS_EVENTID,
+ WMI_WAC_CTRL_REQ_REPLY_EVENTID,
+
+ WMI_REPORT_WMM_PARAMS_EVENTID,
+ WMI_WAC_REJECT_WPS_EVENTID,
+
+ /* More P2P Events */
+ WMI_P2P_GO_NEG_REQ_EVENTID,
+ WMI_P2P_INVITE_REQ_EVENTID,
+ WMI_P2P_INVITE_RCVD_RESULT_EVENTID,
+ WMI_P2P_INVITE_SENT_RESULT_EVENTID,
+ WMI_P2P_PROV_DISC_RESP_EVENTID,
+ WMI_P2P_PROV_DISC_REQ_EVENTID,
+
+ /* RFKILL Events */
+ WMI_RFKILL_STATE_CHANGE_EVENTID,
+ WMI_RFKILL_GET_MODE_CMD_EVENTID,
+
+ WMI_P2P_START_SDPD_EVENTID,
+ WMI_P2P_SDPD_RX_EVENTID,
+
+ WMI_THIN_RESERVED_START_EVENTID = 0x8000,
+ /* Events in this range are reserved for thinmode */
+ WMI_THIN_RESERVED_END_EVENTID = 0x8fff,
+
+ WMI_SET_CHANNEL_EVENTID,
+ WMI_ASSOC_REQ_EVENTID,
+
+ /* Generic ACS event */
+ WMI_ACS_EVENTID,
+ WMI_STORERECALL_STORE_EVENTID,
+ WMI_WOW_EXT_WAKE_EVENTID,
+ WMI_GTK_OFFLOAD_STATUS_EVENTID,
+ WMI_NETWORK_LIST_OFFLOAD_EVENTID,
+ WMI_REMAIN_ON_CHNL_EVENTID,
+ WMI_CANCEL_REMAIN_ON_CHNL_EVENTID,
+ WMI_TX_STATUS_EVENTID,
+ WMI_RX_PROBE_REQ_EVENTID,
+ WMI_P2P_CAPABILITIES_EVENTID,
+ WMI_RX_ACTION_EVENTID,
+ WMI_P2P_INFO_EVENTID,
+};
+
+struct wmi_ready_event_2 {
+ __le32 sw_version;
+ __le32 abi_version;
+ u8 mac_addr[ETH_ALEN];
+ u8 phy_cap;
+} __packed;
+
+/* Connect Event */
+struct wmi_connect_event {
+ union {
+ struct {
+ __le16 ch;
+ u8 bssid[ETH_ALEN];
+ __le16 listen_intvl;
+ __le16 beacon_intvl;
+ __le32 nw_type;
+ } sta;
+ struct {
+ u8 phymode;
+ u8 aid;
+ u8 mac_addr[ETH_ALEN];
+ u8 auth;
+ u8 keymgmt;
+ __le16 cipher;
+ u8 apsd_info;
+ u8 unused[3];
+ } ap_sta;
+ struct {
+ __le16 ch;
+ u8 bssid[ETH_ALEN];
+ u8 unused[8];
+ } ap_bss;
+ } u;
+ u8 beacon_ie_len;
+ u8 assoc_req_len;
+ u8 assoc_resp_len;
+ u8 assoc_info[1];
+} __packed;
+
+/* Disconnect Event */
+enum wmi_disconnect_reason {
+ NO_NETWORK_AVAIL = 0x01,
+
+ /* bmiss */
+ LOST_LINK = 0x02,
+
+ DISCONNECT_CMD = 0x03,
+ BSS_DISCONNECTED = 0x04,
+ AUTH_FAILED = 0x05,
+ ASSOC_FAILED = 0x06,
+ NO_RESOURCES_AVAIL = 0x07,
+ CSERV_DISCONNECT = 0x08,
+ INVALID_PROFILE = 0x0a,
+ DOT11H_CHANNEL_SWITCH = 0x0b,
+ PROFILE_MISMATCH = 0x0c,
+ CONNECTION_EVICTED = 0x0d,
+ IBSS_MERGE = 0xe,
+};
+
+#define ATH6KL_COUNTRY_RD_SHIFT 16
+
+struct ath6kl_wmi_regdomain {
+ __le32 reg_code;
+};
+
+struct wmi_disconnect_event {
+ /* reason code, see 802.11 spec. */
+ __le16 proto_reason_status;
+
+ /* set if known */
+ u8 bssid[ETH_ALEN];
+
+ /* see WMI_DISCONNECT_REASON */
+ u8 disconn_reason;
+
+ u8 assoc_resp_len;
+ u8 assoc_info[1];
+} __packed;
+
+/*
+ * BSS Info Event.
+ * Mechanism used to inform host of the presence and characteristic of
+ * wireless networks present. Consists of bss info header followed by
+ * the beacon or probe-response frame body. The 802.11 header is no included.
+ */
+enum wmi_bi_ftype {
+ BEACON_FTYPE = 0x1,
+ PROBERESP_FTYPE,
+ ACTION_MGMT_FTYPE,
+ PROBEREQ_FTYPE,
+};
+
+#define DEF_LRSSI_SCAN_PERIOD 5
+#define DEF_LRSSI_ROAM_THRESHOLD 20
+#define DEF_LRSSI_ROAM_FLOOR 60
+#define DEF_SCAN_FOR_ROAM_INTVL 2
+
+enum wmi_roam_ctrl {
+ WMI_FORCE_ROAM = 1,
+ WMI_SET_ROAM_MODE,
+ WMI_SET_HOST_BIAS,
+ WMI_SET_LRSSI_SCAN_PARAMS,
+};
+
+struct bss_bias {
+ u8 bssid[ETH_ALEN];
+ u8 bias;
+} __packed;
+
+struct bss_bias_info {
+ u8 num_bss;
+ struct bss_bias bss_bias[1];
+} __packed;
+
+struct low_rssi_scan_params {
+ __le16 lrssi_scan_period;
+ a_sle16 lrssi_scan_threshold;
+ a_sle16 lrssi_roam_threshold;
+ u8 roam_rssi_floor;
+ u8 reserved[1];
+} __packed;
+
+struct roam_ctrl_cmd {
+ union {
+ u8 bssid[ETH_ALEN];
+ u8 roam_mode;
+ struct bss_bias_info bss;
+ struct low_rssi_scan_params params;
+ } __packed info;
+ u8 roam_ctrl;
+} __packed;
+
+/* BSS INFO HDR version 2.0 */
+struct wmi_bss_info_hdr2 {
+ __le16 ch; /* frequency in MHz */
+
+ /* see, enum wmi_bi_ftype */
+ u8 frame_type;
+
+ u8 snr; /* note: rssi = snr - 95 dBm */
+ u8 bssid[ETH_ALEN];
+ __le16 ie_mask;
+} __packed;
+
+/* Command Error Event */
+enum wmi_error_code {
+ INVALID_PARAM = 0x01,
+ ILLEGAL_STATE = 0x02,
+ INTERNAL_ERROR = 0x03,
+};
+
+struct wmi_cmd_error_event {
+ __le16 cmd_id;
+ u8 err_code;
+} __packed;
+
+struct wmi_pstream_timeout_event {
+ u8 tx_queue_num;
+ u8 rx_queue_num;
+ u8 traffic_direc;
+ u8 traffic_class;
+} __packed;
+
+/*
+ * The WMI_NEIGHBOR_REPORT Event is generated by the target to inform
+ * the host of BSS's it has found that matches the current profile.
+ * It can be used by the host to cache PMKs and/to initiate pre-authentication
+ * if the BSS supports it. The first bssid is always the current associated
+ * BSS.
+ * The bssid and bssFlags information repeats according to the number
+ * or APs reported.
+ */
+enum wmi_bss_flags {
+ WMI_DEFAULT_BSS_FLAGS = 0x00,
+ WMI_PREAUTH_CAPABLE_BSS = 0x01,
+ WMI_PMKID_VALID_BSS = 0x02,
+};
+
+struct wmi_neighbor_info {
+ u8 bssid[ETH_ALEN];
+ u8 bss_flags; /* enum wmi_bss_flags */
+} __packed;
+
+struct wmi_neighbor_report_event {
+ u8 num_neighbors;
+ struct wmi_neighbor_info neighbor[0];
+} __packed;
+
+/* TKIP MIC Error Event */
+struct wmi_tkip_micerr_event {
+ u8 key_id;
+ u8 is_mcast;
+} __packed;
+
+/* WMI_SCAN_COMPLETE_EVENTID */
+struct wmi_scan_complete_event {
+ a_sle32 status;
+} __packed;
+
+#define MAX_OPT_DATA_LEN 1400
+
+/*
+ * Special frame receive Event.
+ * Mechanism used to inform host of the receiption of the special frames.
+ * Consists of special frame info header followed by special frame body.
+ * The 802.11 header is not included.
+ */
+struct wmi_opt_rx_info_hdr {
+ __le16 ch;
+ u8 frame_type;
+ s8 snr;
+ u8 src_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+} __packed;
+
+/* Reporting statistic */
+struct tx_stats {
+ __le32 pkt;
+ __le32 byte;
+ __le32 ucast_pkt;
+ __le32 ucast_byte;
+ __le32 mcast_pkt;
+ __le32 mcast_byte;
+ __le32 bcast_pkt;
+ __le32 bcast_byte;
+ __le32 rts_success_cnt;
+ __le32 pkt_per_ac[4];
+ __le32 err_per_ac[4];
+
+ __le32 err;
+ __le32 fail_cnt;
+ __le32 retry_cnt;
+ __le32 mult_retry_cnt;
+ __le32 rts_fail_cnt;
+ a_sle32 ucast_rate;
+} __packed;
+
+struct rx_stats {
+ __le32 pkt;
+ __le32 byte;
+ __le32 ucast_pkt;
+ __le32 ucast_byte;
+ __le32 mcast_pkt;
+ __le32 mcast_byte;
+ __le32 bcast_pkt;
+ __le32 bcast_byte;
+ __le32 frgment_pkt;
+
+ __le32 err;
+ __le32 crc_err;
+ __le32 key_cache_miss;
+ __le32 decrypt_err;
+ __le32 dupl_frame;
+ a_sle32 ucast_rate;
+} __packed;
+
+struct tkip_ccmp_stats {
+ __le32 tkip_local_mic_fail;
+ __le32 tkip_cnter_measures_invoked;
+ __le32 tkip_replays;
+ __le32 tkip_fmt_err;
+ __le32 ccmp_fmt_err;
+ __le32 ccmp_replays;
+} __packed;
+
+struct pm_stats {
+ __le32 pwr_save_failure_cnt;
+ __le16 stop_tx_failure_cnt;
+ __le16 atim_tx_failure_cnt;
+ __le16 atim_rx_failure_cnt;
+ __le16 bcn_rx_failure_cnt;
+} __packed;
+
+struct cserv_stats {
+ __le32 cs_bmiss_cnt;
+ __le32 cs_low_rssi_cnt;
+ __le16 cs_connect_cnt;
+ __le16 cs_discon_cnt;
+ a_sle16 cs_ave_beacon_rssi;
+ __le16 cs_roam_count;
+ a_sle16 cs_rssi;
+ u8 cs_snr;
+ u8 cs_ave_beacon_snr;
+ u8 cs_last_roam_msec;
+} __packed;
+
+struct wlan_net_stats {
+ struct tx_stats tx;
+ struct rx_stats rx;
+ struct tkip_ccmp_stats tkip_ccmp_stats;
+} __packed;
+
+struct arp_stats {
+ __le32 arp_received;
+ __le32 arp_matched;
+ __le32 arp_replied;
+} __packed;
+
+struct wlan_wow_stats {
+ __le32 wow_pkt_dropped;
+ __le16 wow_evt_discarded;
+ u8 wow_host_pkt_wakeups;
+ u8 wow_host_evt_wakeups;
+} __packed;
+
+struct wmi_target_stats {
+ __le32 lq_val;
+ a_sle32 noise_floor_calib;
+ struct pm_stats pm_stats;
+ struct wlan_net_stats stats;
+ struct wlan_wow_stats wow_stats;
+ struct arp_stats arp_stats;
+ struct cserv_stats cserv_stats;
+} __packed;
+
+/*
+ * WMI_RSSI_THRESHOLD_EVENTID.
+ * Indicate the RSSI events to host. Events are indicated when we breach a
+ * thresold value.
+ */
+enum wmi_rssi_threshold_val {
+ WMI_RSSI_THRESHOLD1_ABOVE = 0,
+ WMI_RSSI_THRESHOLD2_ABOVE,
+ WMI_RSSI_THRESHOLD3_ABOVE,
+ WMI_RSSI_THRESHOLD4_ABOVE,
+ WMI_RSSI_THRESHOLD5_ABOVE,
+ WMI_RSSI_THRESHOLD6_ABOVE,
+ WMI_RSSI_THRESHOLD1_BELOW,
+ WMI_RSSI_THRESHOLD2_BELOW,
+ WMI_RSSI_THRESHOLD3_BELOW,
+ WMI_RSSI_THRESHOLD4_BELOW,
+ WMI_RSSI_THRESHOLD5_BELOW,
+ WMI_RSSI_THRESHOLD6_BELOW
+};
+
+struct wmi_rssi_threshold_event {
+ a_sle16 rssi;
+ u8 range;
+} __packed;
+
+enum wmi_snr_threshold_val {
+ WMI_SNR_THRESHOLD1_ABOVE = 1,
+ WMI_SNR_THRESHOLD1_BELOW,
+ WMI_SNR_THRESHOLD2_ABOVE,
+ WMI_SNR_THRESHOLD2_BELOW,
+ WMI_SNR_THRESHOLD3_ABOVE,
+ WMI_SNR_THRESHOLD3_BELOW,
+ WMI_SNR_THRESHOLD4_ABOVE,
+ WMI_SNR_THRESHOLD4_BELOW
+};
+
+struct wmi_snr_threshold_event {
+ /* see, enum wmi_snr_threshold_val */
+ u8 range;
+
+ u8 snr;
+} __packed;
+
+/* WMI_REPORT_ROAM_TBL_EVENTID */
+#define MAX_ROAM_TBL_CAND 5
+
+struct wmi_bss_roam_info {
+ a_sle32 roam_util;
+ u8 bssid[ETH_ALEN];
+ s8 rssi;
+ s8 rssidt;
+ s8 last_rssi;
+ s8 util;
+ s8 bias;
+
+ /* for alignment */
+ u8 reserved;
+} __packed;
+
+/* WMI_CAC_EVENTID */
+enum cac_indication {
+ CAC_INDICATION_ADMISSION = 0x00,
+ CAC_INDICATION_ADMISSION_RESP = 0x01,
+ CAC_INDICATION_DELETE = 0x02,
+ CAC_INDICATION_NO_RESP = 0x03,
+};
+
+#define WMM_TSPEC_IE_LEN 63
+
+struct wmi_cac_event {
+ u8 ac;
+ u8 cac_indication;
+ u8 status_code;
+ u8 tspec_suggestion[WMM_TSPEC_IE_LEN];
+} __packed;
+
+/* WMI_APLIST_EVENTID */
+
+enum aplist_ver {
+ APLIST_VER1 = 1,
+};
+
+struct wmi_ap_info_v1 {
+ u8 bssid[ETH_ALEN];
+ __le16 channel;
+} __packed;
+
+union wmi_ap_info {
+ struct wmi_ap_info_v1 ap_info_v1;
+} __packed;
+
+struct wmi_aplist_event {
+ u8 ap_list_ver;
+ u8 num_ap;
+ union wmi_ap_info ap_list[1];
+} __packed;
+
+/* Developer Commands */
+
+/*
+ * WMI_SET_BITRATE_CMDID
+ *
+ * Get bit rate cmd uses same definition as set bit rate cmd
+ */
+enum wmi_bit_rate {
+ RATE_AUTO = -1,
+ RATE_1Mb = 0,
+ RATE_2Mb = 1,
+ RATE_5_5Mb = 2,
+ RATE_11Mb = 3,
+ RATE_6Mb = 4,
+ RATE_9Mb = 5,
+ RATE_12Mb = 6,
+ RATE_18Mb = 7,
+ RATE_24Mb = 8,
+ RATE_36Mb = 9,
+ RATE_48Mb = 10,
+ RATE_54Mb = 11,
+ RATE_MCS_0_20 = 12,
+ RATE_MCS_1_20 = 13,
+ RATE_MCS_2_20 = 14,
+ RATE_MCS_3_20 = 15,
+ RATE_MCS_4_20 = 16,
+ RATE_MCS_5_20 = 17,
+ RATE_MCS_6_20 = 18,
+ RATE_MCS_7_20 = 19,
+ RATE_MCS_0_40 = 20,
+ RATE_MCS_1_40 = 21,
+ RATE_MCS_2_40 = 22,
+ RATE_MCS_3_40 = 23,
+ RATE_MCS_4_40 = 24,
+ RATE_MCS_5_40 = 25,
+ RATE_MCS_6_40 = 26,
+ RATE_MCS_7_40 = 27,
+};
+
+struct wmi_bit_rate_reply {
+ /* see, enum wmi_bit_rate */
+ s8 rate_index;
+} __packed;
+
+/*
+ * WMI_SET_FIXRATES_CMDID
+ *
+ * Get fix rates cmd uses same definition as set fix rates cmd
+ */
+struct wmi_fix_rates_reply {
+ /* see wmi_bit_rate */
+ __le32 fix_rate_mask;
+} __packed;
+
+enum roam_data_type {
+ /* get the roam time data */
+ ROAM_DATA_TIME = 1,
+};
+
+struct wmi_target_roam_time {
+ __le32 disassoc_time;
+ __le32 no_txrx_time;
+ __le32 assoc_time;
+ __le32 allow_txrx_time;
+ u8 disassoc_bssid[ETH_ALEN];
+ s8 disassoc_bss_rssi;
+ u8 assoc_bssid[ETH_ALEN];
+ s8 assoc_bss_rssi;
+} __packed;
+
+enum wmi_txop_cfg {
+ WMI_TXOP_DISABLED = 0,
+ WMI_TXOP_ENABLED
+};
+
+struct wmi_set_wmm_txop_cmd {
+ u8 txop_enable;
+} __packed;
+
+struct wmi_set_keepalive_cmd {
+ u8 keep_alive_intvl;
+} __packed;
+
+struct wmi_get_keepalive_cmd {
+ __le32 configured;
+ u8 keep_alive_intvl;
+} __packed;
+
+struct wmi_set_appie_cmd {
+ u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */
+ u8 ie_len;
+ u8 ie_info[0];
+} __packed;
+
+/* Notify the WSC registration status to the target */
+#define WSC_REG_ACTIVE 1
+#define WSC_REG_INACTIVE 0
+
+#define WOW_MAX_FILTER_LISTS 1
+#define WOW_MAX_FILTERS_PER_LIST 4
+#define WOW_PATTERN_SIZE 64
+#define WOW_MASK_SIZE 64
+
+#define MAC_MAX_FILTERS_PER_LIST 4
+
+struct wow_filter {
+ u8 wow_valid_filter;
+ u8 wow_filter_id;
+ u8 wow_filter_size;
+ u8 wow_filter_offset;
+ u8 wow_filter_mask[WOW_MASK_SIZE];
+ u8 wow_filter_pattern[WOW_PATTERN_SIZE];
+} __packed;
+
+#define MAX_IP_ADDRS 2
+
+struct wmi_set_ip_cmd {
+ /* IP in network byte order */
+ __le32 ips[MAX_IP_ADDRS];
+} __packed;
+
+/* WMI_GET_WOW_LIST_CMD reply */
+struct wmi_get_wow_list_reply {
+ /* number of patterns in reply */
+ u8 num_filters;
+
+ /* this is filter # x of total num_filters */
+ u8 this_filter_num;
+
+ u8 wow_mode;
+ u8 host_mode;
+ struct wow_filter wow_filters[1];
+} __packed;
+
+/* WMI_SET_AKMP_PARAMS_CMD */
+
+struct wmi_pmkid {
+ u8 pmkid[WMI_PMKID_LEN];
+} __packed;
+
+/* WMI_GET_PMKID_LIST_CMD Reply */
+struct wmi_pmkid_list_reply {
+ __le32 num_pmkid;
+ u8 bssid_list[ETH_ALEN][1];
+ struct wmi_pmkid pmkid_list[1];
+} __packed;
+
+/* WMI_ADDBA_REQ_EVENTID */
+struct wmi_addba_req_event {
+ u8 tid;
+ u8 win_sz;
+ __le16 st_seq_no;
+
+ /* f/w response for ADDBA Req; OK (0) or failure (!=0) */
+ u8 status;
+} __packed;
+
+/* WMI_ADDBA_RESP_EVENTID */
+struct wmi_addba_resp_event {
+ u8 tid;
+
+ /* OK (0), failure (!=0) */
+ u8 status;
+
+ /* three values: not supported(0), 3839, 8k */
+ __le16 amsdu_sz;
+} __packed;
+
+/* WMI_DELBA_EVENTID
+ * f/w received a DELBA for peer and processed it.
+ * Host is notified of this
+ */
+struct wmi_delba_event {
+ u8 tid;
+ u8 is_peer_initiator;
+ __le16 reason_code;
+} __packed;
+
+#define PEER_NODE_JOIN_EVENT 0x00
+#define PEER_NODE_LEAVE_EVENT 0x01
+#define PEER_FIRST_NODE_JOIN_EVENT 0x10
+#define PEER_LAST_NODE_LEAVE_EVENT 0x11
+
+struct wmi_peer_node_event {
+ u8 event_code;
+ u8 peer_mac_addr[ETH_ALEN];
+} __packed;
+
+/* Transmit complete event data structure(s) */
+
+/* version 1 of tx complete msg */
+struct tx_complete_msg_v1 {
+#define TX_COMPLETE_STATUS_SUCCESS 0
+#define TX_COMPLETE_STATUS_RETRIES 1
+#define TX_COMPLETE_STATUS_NOLINK 2
+#define TX_COMPLETE_STATUS_TIMEOUT 3
+#define TX_COMPLETE_STATUS_OTHER 4
+
+ u8 status;
+
+ /* packet ID to identify parent packet */
+ u8 pkt_id;
+
+ /* rate index on successful transmission */
+ u8 rate_idx;
+
+ /* number of ACK failures in tx attempt */
+ u8 ack_failures;
+} __packed;
+
+struct wmi_tx_complete_event {
+ /* no of tx comp msgs following this struct */
+ u8 num_msg;
+
+ /* length in bytes for each individual msg following this struct */
+ u8 msg_len;
+
+ /* version of tx complete msg data following this struct */
+ u8 msg_type;
+
+ /* individual messages follow this header */
+ u8 reserved;
+} __packed;
+
+/*
+ * ------- AP Mode definitions --------------
+ */
+
+/*
+ * !!! Warning !!!
+ * -Changing the following values needs compilation of both driver and firmware
+ */
+#define AP_MAX_NUM_STA 8
+
+/* Spl. AID used to set DTIM flag in the beacons */
+#define MCAST_AID 0xFF
+
+#define DEF_AP_COUNTRY_CODE "US "
+
+/* Used with WMI_AP_SET_NUM_STA_CMDID */
+
+/*
+ * Used with WMI_AP_SET_MLME_CMDID
+ */
+
+/* MLME Commands */
+#define WMI_AP_MLME_ASSOC 1 /* associate station */
+#define WMI_AP_DISASSOC 2 /* disassociate station */
+#define WMI_AP_DEAUTH 3 /* deauthenticate station */
+#define WMI_AP_MLME_AUTHORIZE 4 /* authorize station */
+#define WMI_AP_MLME_UNAUTHORIZE 5 /* unauthorize station */
+
+struct wmi_ap_set_mlme_cmd {
+ u8 mac[ETH_ALEN];
+ __le16 reason; /* 802.11 reason code */
+ u8 cmd; /* operation to perform (WMI_AP_*) */
+} __packed;
+
+struct wmi_ap_set_pvb_cmd {
+ __le32 flag;
+ __le16 rsvd;
+ __le16 aid;
+} __packed;
+
+struct wmi_rx_frame_format_cmd {
+ /* version of meta data for rx packets <0 = default> (0-7 = valid) */
+ u8 meta_ver;
+
+ /*
+ * 1 == leave .11 header intact,
+ * 0 == replace .11 header with .3 <default>
+ */
+ u8 dot11_hdr;
+
+ /*
+ * 1 == defragmentation is performed by host,
+ * 0 == performed by target <default>
+ */
+ u8 defrag_on_host;
+
+ /* for alignment */
+ u8 reserved[1];
+} __packed;
+
+/* AP mode events */
+
+/* WMI_PS_POLL_EVENT */
+struct wmi_pspoll_event {
+ __le16 aid;
+} __packed;
+
+struct wmi_per_sta_stat {
+ __le32 tx_bytes;
+ __le32 tx_pkts;
+ __le32 tx_error;
+ __le32 tx_discard;
+ __le32 rx_bytes;
+ __le32 rx_pkts;
+ __le32 rx_error;
+ __le32 rx_discard;
+ __le32 aid;
+} __packed;
+
+struct wmi_ap_mode_stat {
+ __le32 action;
+ struct wmi_per_sta_stat sta[AP_MAX_NUM_STA + 1];
+} __packed;
+
+/* End of AP mode definitions */
+
+struct wmi_remain_on_chnl_cmd {
+ __le32 freq;
+ __le32 duration;
+} __packed;
+
+struct wmi_send_action_cmd {
+ __le32 id;
+ __le32 freq;
+ __le32 wait;
+ __le16 len;
+ u8 data[0];
+} __packed;
+
+struct wmi_tx_status_event {
+ __le32 id;
+ u8 ack_status;
+} __packed;
+
+struct wmi_probe_req_report_cmd {
+ u8 enable;
+} __packed;
+
+struct wmi_disable_11b_rates_cmd {
+ u8 disable;
+} __packed;
+
+struct wmi_set_appie_extended_cmd {
+ u8 role_id;
+ u8 mgmt_frm_type;
+ u8 ie_len;
+ u8 ie_info[0];
+} __packed;
+
+struct wmi_remain_on_chnl_event {
+ __le32 freq;
+ __le32 duration;
+} __packed;
+
+struct wmi_cancel_remain_on_chnl_event {
+ __le32 freq;
+ __le32 duration;
+ u8 status;
+} __packed;
+
+struct wmi_rx_action_event {
+ __le32 freq;
+ __le16 len;
+ u8 data[0];
+} __packed;
+
+struct wmi_p2p_capabilities_event {
+ __le16 len;
+ u8 data[0];
+} __packed;
+
+struct wmi_p2p_rx_probe_req_event {
+ __le32 freq;
+ __le16 len;
+ u8 data[0];
+} __packed;
+
+#define P2P_FLAG_CAPABILITIES_REQ (0x00000001)
+#define P2P_FLAG_MACADDR_REQ (0x00000002)
+#define P2P_FLAG_HMODEL_REQ (0x00000002)
+
+struct wmi_get_p2p_info {
+ __le32 info_req_flags;
+} __packed;
+
+struct wmi_p2p_info_event {
+ __le32 info_req_flags;
+ __le16 len;
+ u8 data[0];
+} __packed;
+
+struct wmi_p2p_capabilities {
+ u8 go_power_save;
+} __packed;
+
+struct wmi_p2p_macaddr {
+ u8 mac_addr[ETH_ALEN];
+} __packed;
+
+struct wmi_p2p_hmodel {
+ u8 p2p_model;
+} __packed;
+
+struct wmi_p2p_probe_response_cmd {
+ __le32 freq;
+ u8 destination_addr[ETH_ALEN];
+ __le16 len;
+ u8 data[0];
+} __packed;
+
+/* Extended WMI (WMIX)
+ *
+ * Extended WMIX commands are encapsulated in a WMI message with
+ * cmd=WMI_EXTENSION_CMD.
+ *
+ * Extended WMI commands are those that are needed during wireless
+ * operation, but which are not really wireless commands. This allows,
+ * for instance, platform-specific commands. Extended WMI commands are
+ * embedded in a WMI command message with WMI_COMMAND_ID=WMI_EXTENSION_CMDID.
+ * Extended WMI events are similarly embedded in a WMI event message with
+ * WMI_EVENT_ID=WMI_EXTENSION_EVENTID.
+ */
+struct wmix_cmd_hdr {
+ __le32 cmd_id;
+} __packed;
+
+enum wmix_command_id {
+ WMIX_DSETOPEN_REPLY_CMDID = 0x2001,
+ WMIX_DSETDATA_REPLY_CMDID,
+ WMIX_GPIO_OUTPUT_SET_CMDID,
+ WMIX_GPIO_INPUT_GET_CMDID,
+ WMIX_GPIO_REGISTER_SET_CMDID,
+ WMIX_GPIO_REGISTER_GET_CMDID,
+ WMIX_GPIO_INTR_ACK_CMDID,
+ WMIX_HB_CHALLENGE_RESP_CMDID,
+ WMIX_DBGLOG_CFG_MODULE_CMDID,
+ WMIX_PROF_CFG_CMDID, /* 0x200a */
+ WMIX_PROF_ADDR_SET_CMDID,
+ WMIX_PROF_START_CMDID,
+ WMIX_PROF_STOP_CMDID,
+ WMIX_PROF_COUNT_GET_CMDID,
+};
+
+enum wmix_event_id {
+ WMIX_DSETOPENREQ_EVENTID = 0x3001,
+ WMIX_DSETCLOSE_EVENTID,
+ WMIX_DSETDATAREQ_EVENTID,
+ WMIX_GPIO_INTR_EVENTID,
+ WMIX_GPIO_DATA_EVENTID,
+ WMIX_GPIO_ACK_EVENTID,
+ WMIX_HB_CHALLENGE_RESP_EVENTID,
+ WMIX_DBGLOG_EVENTID,
+ WMIX_PROF_COUNT_EVENTID,
+};
+
+/*
+ * ------Error Detection support-------
+ */
+
+/*
+ * WMIX_HB_CHALLENGE_RESP_CMDID
+ * Heartbeat Challenge Response command
+ */
+struct wmix_hb_challenge_resp_cmd {
+ __le32 cookie;
+ __le32 source;
+} __packed;
+
+struct ath6kl_wmix_dbglog_cfg_module_cmd {
+ __le32 valid;
+ __le32 config;
+} __packed;
+
+/* End of Extended WMI (WMIX) */
+
+enum wmi_sync_flag {
+ NO_SYNC_WMIFLAG = 0,
+
+ /* transmit all queued data before cmd */
+ SYNC_BEFORE_WMIFLAG,
+
+ /* any new data waits until cmd execs */
+ SYNC_AFTER_WMIFLAG,
+
+ SYNC_BOTH_WMIFLAG,
+
+ /* end marker */
+ END_WMIFLAG
+};
+
+enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi);
+void ath6kl_wmi_set_control_ep(struct wmi *wmi, enum htc_endpoint_id ep_id);
+int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb);
+int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb,
+ u8 msg_type, bool more_data,
+ enum wmi_data_hdr_data_type data_type,
+ u8 meta_ver, void *tx_meta_info);
+
+int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb);
+int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb);
+int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb,
+ u32 layer2_priority, bool wmm_enabled,
+ u8 *ac);
+
+int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb);
+
+int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
+ enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag);
+
+int ath6kl_wmi_connect_cmd(struct wmi *wmi, enum network_type nw_type,
+ enum dot11_auth_mode dot11_auth_mode,
+ enum auth_mode auth_mode,
+ enum crypto_type pairwise_crypto,
+ u8 pairwise_crypto_len,
+ enum crypto_type group_crypto,
+ u8 group_crypto_len, int ssid_len, u8 *ssid,
+ u8 *bssid, u16 channel, u32 ctrl_flags);
+
+int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 *bssid, u16 channel);
+int ath6kl_wmi_disconnect_cmd(struct wmi *wmi);
+int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type,
+ u32 force_fgscan, u32 is_legacy,
+ u32 home_dwell_time, u32 force_scan_interval,
+ s8 num_chan, u16 *ch_list);
+int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u16 fg_start_sec,
+ u16 fg_end_sec, u16 bg_sec,
+ u16 minact_chdw_msec, u16 maxact_chdw_msec,
+ u16 pas_chdw_msec, u8 short_scan_ratio,
+ u8 scan_ctrl_flag, u32 max_dfsch_act_time,
+ u16 maxact_scan_per_ssid);
+int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 filter, u32 ie_mask);
+int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 index, u8 flag,
+ u8 ssid_len, u8 *ssid);
+int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u16 listen_interval,
+ u16 listen_beacons);
+int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 pwr_mode);
+int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u16 idle_period,
+ u16 ps_poll_num, u16 dtim_policy,
+ u16 tx_wakup_policy, u16 num_tx_to_wakeup,
+ u16 ps_fail_event_policy);
+int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 timeout);
+int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi,
+ struct wmi_create_pstream_cmd *pstream);
+int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 traffic_class, u8 tsid);
+
+int ath6kl_wmi_set_rts_cmd(struct wmi *wmi, u16 threshold);
+int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status,
+ u8 preamble_policy);
+
+int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source);
+int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config);
+
+int ath6kl_wmi_get_stats_cmd(struct wmi *wmi);
+int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
+ enum crypto_type key_type,
+ u8 key_usage, u8 key_len,
+ u8 *key_rsc, u8 *key_material,
+ u8 key_op_ctrl, u8 *mac_addr,
+ enum wmi_sync_flag sync_flag);
+int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 *krk);
+int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 key_index);
+int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, const u8 *bssid,
+ const u8 *pmkid, bool set);
+int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 dbM);
+int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi);
+
+int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, enum wmi_txop_cfg cfg);
+int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl);
+int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len);
+
+s32 ath6kl_wmi_get_rate(s8 rate_index);
+
+int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd);
+int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi);
+
+/* AP mode */
+int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p);
+
+int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason);
+
+int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag);
+
+int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version,
+ bool rx_dot11_hdr, bool defrag_on_host);
+
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
+ u8 ie_len);
+
+/* P2P */
+int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable);
+
+int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur);
+
+int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait,
+ const u8 *data, u16 data_len);
+
+int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u32 freq,
+ const u8 *dst,
+ const u8 *data, u16 data_len);
+
+int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable);
+
+int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags);
+
+int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi);
+
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
+ u8 ie_len);
+
+void *ath6kl_wmi_init(struct ath6kl *devt);
+void ath6kl_wmi_shutdown(struct wmi *wmi);
+
+#endif /* WMI_H */
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 05a6fade7b1c..36ed3c46fec6 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -21,6 +21,7 @@ ath9k_hw-y:= \
ar5008_phy.o \
ar9002_calib.o \
ar9003_calib.o \
+ ar9003_rtt.o \
calib.o \
eeprom.o \
eeprom_def.o \
diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c
index 5b49cd03bfdf..5e47ca6d16a8 100644
--- a/drivers/net/wireless/ath/ath9k/ahb.c
+++ b/drivers/net/wireless/ath/ath9k/ahb.c
@@ -19,6 +19,7 @@
#include <linux/nl80211.h>
#include <linux/platform_device.h>
#include <linux/ath9k_platform.h>
+#include <linux/module.h>
#include "ath9k.h"
static const struct platform_device_id ath9k_platform_id_table[] = {
@@ -27,6 +28,10 @@ static const struct platform_device_id ath9k_platform_id_table[] = {
.driver_data = AR5416_AR9100_DEVID,
},
{
+ .name = "ar933x_wmac",
+ .driver_data = AR9300_DEVID_AR9330,
+ },
+ {
.name = "ar934x_wmac",
.driver_data = AR9300_DEVID_AR9340,
},
@@ -129,7 +134,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
goto err_free_hw;
}
- ret = ath9k_init_device(id->driver_data, sc, 0x0, &ath_ahb_bus_ops);
+ ret = ath9k_init_device(id->driver_data, sc, &ath_ahb_bus_ops);
if (ret) {
dev_err(&pdev->dev, "failed to initialize device\n");
goto err_irq;
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index bfb6481f01f9..a639b94f7643 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -15,6 +15,7 @@
*/
#include <linux/kernel.h>
+#include <linux/export.h>
#include "hw.h"
#include "hw-ops.h"
@@ -273,7 +274,8 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel)
immunityLevel, aniState->noiseFloor,
aniState->rssiThrLow, aniState->rssiThrHigh);
- aniState->ofdmNoiseImmunityLevel = immunityLevel;
+ if (aniState->update_ani)
+ aniState->ofdmNoiseImmunityLevel = immunityLevel;
entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];
entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];
@@ -346,7 +348,8 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel)
immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI)
immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI;
- aniState->cckNoiseImmunityLevel = immunityLevel;
+ if (aniState->update_ani)
+ aniState->cckNoiseImmunityLevel = immunityLevel;
entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];
entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];
@@ -502,9 +505,6 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning)
ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
ATH9K_ANI_CCK_WEAK_SIG_THR);
- ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
- ATH9K_RX_FILTER_PHYERR);
-
ath9k_ani_restart(ah);
return;
}
@@ -525,8 +525,6 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning)
ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
aniState->firstepLevel);
- ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) &
- ~ATH9K_RX_FILTER_PHYERR);
ath9k_ani_restart(ah);
ENABLE_REGWRITE_BUFFER(ah);
@@ -593,6 +591,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
aniState->ofdmNoiseImmunityLevel,
aniState->cckNoiseImmunityLevel);
+ aniState->update_ani = false;
ath9k_hw_set_ofdm_nil(ah, ATH9K_ANI_OFDM_DEF_LEVEL);
ath9k_hw_set_cck_nil(ah, ATH9K_ANI_CCK_DEF_LEVEL);
}
@@ -609,6 +608,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
aniState->ofdmNoiseImmunityLevel,
aniState->cckNoiseImmunityLevel);
+ aniState->update_ani = true;
ath9k_hw_set_ofdm_nil(ah,
aniState->ofdmNoiseImmunityLevel);
ath9k_hw_set_cck_nil(ah,
@@ -643,7 +643,7 @@ static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)
listenTime = ath_hw_get_listen_time(common);
if (listenTime <= 0) {
- ah->stats.ast_ani_lneg++;
+ ah->stats.ast_ani_lneg_or_lzero++;
ath9k_ani_restart(ah);
return false;
}
@@ -892,6 +892,8 @@ void ath9k_hw_ani_init(struct ath_hw *ah)
ani->ofdmWeakSigDetectOff =
!ATH9K_ANI_USE_OFDM_WEAK_SIG;
ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
+ ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
+ ani->update_ani = false;
}
/*
diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h
index dbab5b9ce494..83029d6c7b22 100644
--- a/drivers/net/wireless/ath/ath9k/ani.h
+++ b/drivers/net/wireless/ath/ath9k/ani.h
@@ -122,6 +122,7 @@ struct ar5416AniState {
u8 firstepLevel;
u8 ofdmWeakSigDetectOff;
u8 cckWeakSigThreshold;
+ bool update_ani;
u32 listenTime;
int32_t rssiThrLow;
int32_t rssiThrHigh;
@@ -148,8 +149,7 @@ struct ar5416Stats {
u32 ast_ani_ofdmerrs;
u32 ast_ani_cckerrs;
u32 ast_ani_reset;
- u32 ast_ani_lzero;
- u32 ast_ani_lneg;
+ u32 ast_ani_lneg_or_lzero;
u32 avgbrssi;
struct ath9k_mib_stats ast_mibstats;
};
@@ -159,7 +159,5 @@ void ath9k_enable_mib_counters(struct ath_hw *ah);
void ath9k_hw_disable_mib_counters(struct ath_hw *ah);
void ath9k_hw_ani_setup(struct ath_hw *ah);
void ath9k_hw_ani_init(struct ath_hw *ah);
-int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah,
- struct ath9k_channel *chan);
#endif /* ANI_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_initvals.h b/drivers/net/wireless/ath/ath9k/ar5008_initvals.h
index 234617c948a1..f81e7fc60a36 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar5008_initvals.h
@@ -14,70 +14,71 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-static const u32 ar5416Modes[][6] = {
- {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0},
- {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0},
- {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180},
- {0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008},
- {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0},
- {0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf},
- {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810, 0x08f04810},
- {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a, 0x0000320a},
- {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303},
- {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
- {0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
- {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
- {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
- {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
- {0x00009844, 0x1372161e, 0x1372161e, 0x137216a0, 0x137216a0, 0x137216a0},
- {0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68},
- {0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68},
- {0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68},
- {0x00009850, 0x6c48b4e0, 0x6d48b4e0, 0x6d48b0de, 0x6c48b0de, 0x6c48b0de},
- {0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e},
- {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e, 0x31395d5e},
- {0x00009860, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18},
- {0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
- {0x00009868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190},
- {0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081},
- {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0},
- {0x00009918, 0x000001b8, 0x00000370, 0x00000268, 0x00000134, 0x00000134},
- {0x00009924, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b},
- {0x00009944, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020},
- {0x00009960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80},
- {0x0000a960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80},
- {0x0000b960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80},
- {0x00009964, 0x00000000, 0x00000000, 0x00001120, 0x00001120, 0x00001120},
- {0x000099bc, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00},
- {0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be},
- {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77},
- {0x000099c8, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c},
- {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8},
- {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384},
- {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880, 0x00000880},
- {0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, 0xd03e4788},
- {0x0000a20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120},
- {0x0000b20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120},
- {0x0000c20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120},
- {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
- {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000},
- {0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, 0x0a1a7caa},
- {0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000},
- {0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, 0x2e032402},
- {0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, 0x4a0a3c06},
- {0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, 0x621a540b},
- {0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, 0x764f6c1b},
- {0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, 0x845b7a5a},
- {0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, 0x950f8ccf},
- {0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, 0xa5cf9b4f},
- {0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, 0xbddfaf1f},
- {0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, 0xd1ffc93f},
- {0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+static const u32 ar5416Modes[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab},
+ {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+ {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+ {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300},
+ {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
+ {0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
+ {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
+ {0x00009844, 0x1372161e, 0x1372161e, 0x137216a0, 0x137216a0},
+ {0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68},
+ {0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68},
+ {0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68},
+ {0x00009850, 0x6c48b4e0, 0x6d48b4e0, 0x6d48b0de, 0x6c48b0de},
+ {0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e},
+ {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e},
+ {0x00009860, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18},
+ {0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
+ {0x00009868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190},
+ {0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081},
+ {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x00009918, 0x000001b8, 0x00000370, 0x00000268, 0x00000134},
+ {0x00009924, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b},
+ {0x00009944, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020},
+ {0x00009960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80},
+ {0x0000a960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80},
+ {0x0000b960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80},
+ {0x00009964, 0x00000000, 0x00000000, 0x00001120, 0x00001120},
+ {0x000099bc, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00},
+ {0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be},
+ {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77},
+ {0x000099c8, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c},
+ {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8},
+ {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384},
+ {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880},
+ {0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788},
+ {0x0000a20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120},
+ {0x0000b20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120},
+ {0x0000c20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120},
+ {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
+ {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa},
+ {0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000},
+ {0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402},
+ {0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06},
+ {0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b},
+ {0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b},
+ {0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a},
+ {0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf},
+ {0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f},
+ {0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f},
+ {0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f},
+ {0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000},
+ {0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
};
static const u32 ar5416Common[][2] = {
@@ -668,6 +669,6 @@ static const u32 ar5416Addac[][2] = {
{0x0000989c, 0x00000000},
{0x0000989c, 0x00000000},
{0x0000989c, 0x00000000},
- {0x000098cc, 0x00000000},
+ {0x000098c4, 0x00000000},
};
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index 441bb33f17ad..f199e9e25149 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -627,6 +627,11 @@ static void ar5008_hw_init_bb(struct ath_hw *ah,
else
synthDelay /= 10;
+ if (IS_CHAN_HALF_RATE(chan))
+ synthDelay *= 2;
+ else if (IS_CHAN_QUARTER_RATE(chan))
+ synthDelay *= 4;
+
REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
udelay(synthDelay + BASE_ACTIVATE_DELAY);
@@ -699,8 +704,10 @@ static void ar5008_hw_override_ini(struct ath_hw *ah,
REG_WRITE(ah, AR_PCU_MISC_MODE2, val);
}
- if (!AR_SREV_5416_20_OR_LATER(ah) ||
- AR_SREV_9280_20_OR_LATER(ah))
+ REG_SET_BIT(ah, AR_PHY_CCK_DETECT,
+ AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+
+ if (AR_SREV_9280_20_OR_LATER(ah))
return;
/*
* Disable BB clock gating
@@ -756,10 +763,8 @@ static void ar5008_hw_set_channel_regs(struct ath_hw *ah,
static int ar5008_hw_process_ini(struct ath_hw *ah,
struct ath9k_channel *chan)
{
- struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ath_common *common = ath9k_hw_common(ah);
int i, regWrites = 0;
- struct ieee80211_channel *channel = chan->chan;
u32 modesIndex, freqIndex;
switch (chan->chanmode) {
@@ -797,7 +802,8 @@ static int ar5008_hw_process_ini(struct ath_hw *ah,
/* Write ADDAC shifts */
REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO);
- ah->eep_ops->set_addac(ah, chan);
+ if (ah->eep_ops->set_addac)
+ ah->eep_ops->set_addac(ah, chan);
if (AR_SREV_5416_22_OR_LATER(ah)) {
REG_WRITE_ARRAY(&ah->iniAddac, 1, regWrites);
@@ -895,14 +901,7 @@ static int ar5008_hw_process_ini(struct ath_hw *ah,
ar5008_hw_set_channel_regs(ah, chan);
ar5008_hw_init_chain_masks(ah);
ath9k_olc_init(ah);
-
- /* Set TX power */
- ah->eep_ops->set_txpower(ah, chan,
- ath9k_regd_get_ctl(regulatory, chan),
- channel->max_antenna_gain * 2,
- channel->max_power * 2,
- min((u32) MAX_RATE_POWER,
- (u32) regulatory->power_limit), false);
+ ath9k_hw_apply_txpower(ah, chan);
/* Write analog registers */
if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) {
@@ -1002,24 +1001,6 @@ static void ar5008_restore_chainmask(struct ath_hw *ah)
}
}
-static void ar5008_set_diversity(struct ath_hw *ah, bool value)
-{
- u32 v = REG_READ(ah, AR_PHY_CCK_DETECT);
- if (value)
- v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
- else
- v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
- REG_WRITE(ah, AR_PHY_CCK_DETECT, v);
-}
-
-static u32 ar9100_hw_compute_pll_control(struct ath_hw *ah,
- struct ath9k_channel *chan)
-{
- if (chan && IS_CHAN_5GHZ(chan))
- return 0x1450;
- return 0x1458;
-}
-
static u32 ar9160_hw_compute_pll_control(struct ath_hw *ah,
struct ath9k_channel *chan)
{
@@ -1649,7 +1630,6 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah)
priv_ops->rfbus_req = ar5008_hw_rfbus_req;
priv_ops->rfbus_done = ar5008_hw_rfbus_done;
priv_ops->restore_chainmask = ar5008_restore_chainmask;
- priv_ops->set_diversity = ar5008_set_diversity;
priv_ops->do_getnf = ar5008_hw_do_getnf;
priv_ops->set_radar_params = ar5008_hw_set_radar_params;
@@ -1659,9 +1639,7 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah)
} else
priv_ops->ani_control = ar5008_hw_ani_control_old;
- if (AR_SREV_9100(ah))
- priv_ops->compute_pll_control = ar9100_hw_compute_pll_control;
- else if (AR_SREV_9160_10_OR_LATER(ah))
+ if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah))
priv_ops->compute_pll_control = ar9160_hw_compute_pll_control;
else
priv_ops->compute_pll_control = ar5008_hw_compute_pll_control;
diff --git a/drivers/net/wireless/ath/ath9k/ar9001_initvals.h b/drivers/net/wireless/ath/ath9k/ar9001_initvals.h
index 6d2e2f3303f9..e8bdc75405f1 100644
--- a/drivers/net/wireless/ath/ath9k/ar9001_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9001_initvals.h
@@ -14,73 +14,74 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-static const u32 ar5416Modes_9100[][6] = {
- {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0},
- {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0},
- {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180},
- {0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008},
- {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0},
- {0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf},
- {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810, 0x08f04810},
- {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a, 0x0000320a},
- {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303},
- {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
- {0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
- {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
- {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
- {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
- {0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0},
- {0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68},
- {0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68},
- {0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68},
- {0x00009850, 0x6c48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6c48b0e2, 0x6c48b0e2},
- {0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e},
- {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e, 0x31395d5e},
- {0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18},
- {0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
- {0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0},
- {0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081},
- {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0},
- {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016},
- {0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d},
- {0x00009940, 0x00750604, 0x00754604, 0xfff81204, 0xfff81204, 0xfff81204},
- {0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020},
- {0x00009954, 0x5f3ca3de, 0x5f3ca3de, 0xe250a51e, 0xe250a51e, 0xe250a51e},
- {0x00009958, 0x2108ecff, 0x2108ecff, 0x3388ffff, 0x3388ffff, 0x3388ffff},
- {0x00009960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0},
- {0x0000a960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0},
- {0x0000b960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0},
- {0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, 0x00001120},
- {0x0000c9bc, 0x001a0600, 0x001a0600, 0x001a1000, 0x001a0c00, 0x001a0c00},
- {0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be},
- {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77},
- {0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329},
- {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8},
- {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384},
- {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880, 0x00000880},
- {0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, 0xd03e4788},
- {0x0000a20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120},
- {0x0000b20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120},
- {0x0000c20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120},
- {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
- {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000},
- {0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, 0x0a1a7caa},
- {0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000},
- {0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, 0x2e032402},
- {0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, 0x4a0a3c06},
- {0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, 0x621a540b},
- {0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, 0x764f6c1b},
- {0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, 0x845b7a5a},
- {0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, 0x950f8ccf},
- {0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, 0xa5cf9b4f},
- {0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, 0xbddfaf1f},
- {0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, 0xd1ffc93f},
- {0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+static const u32 ar5416Modes_9100[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab},
+ {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+ {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+ {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300},
+ {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
+ {0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
+ {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
+ {0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0},
+ {0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68},
+ {0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68},
+ {0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68},
+ {0x00009850, 0x6c48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6c48b0e2},
+ {0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e},
+ {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e},
+ {0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20},
+ {0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
+ {0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0},
+ {0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081},
+ {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+ {0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d},
+ {0x00009940, 0x00750604, 0x00754604, 0xfff81204, 0xfff81204},
+ {0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020},
+ {0x00009954, 0x5f3ca3de, 0x5f3ca3de, 0xe250a51e, 0xe250a51e},
+ {0x00009958, 0x2108ecff, 0x2108ecff, 0x3388ffff, 0x3388ffff},
+ {0x00009960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0},
+ {0x0000a960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0},
+ {0x0000b960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0},
+ {0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120},
+ {0x0000c9bc, 0x001a0600, 0x001a0600, 0x001a1000, 0x001a0c00},
+ {0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be},
+ {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77},
+ {0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329},
+ {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8},
+ {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384},
+ {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880},
+ {0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788},
+ {0x0000a20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120},
+ {0x0000b20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120},
+ {0x0000c20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120},
+ {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
+ {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa},
+ {0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000},
+ {0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402},
+ {0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06},
+ {0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b},
+ {0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b},
+ {0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a},
+ {0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf},
+ {0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f},
+ {0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f},
+ {0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f},
+ {0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000},
+ {0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
};
static const u32 ar5416Common_9100[][2] = {
@@ -666,71 +667,72 @@ static const u32 ar5416Addac_9100[][2] = {
{0x000098cc, 0x00000000},
};
-static const u32 ar5416Modes_9160[][6] = {
- {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0},
- {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0},
- {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180},
- {0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008},
- {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0},
- {0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf},
- {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810, 0x08f04810},
- {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a, 0x0000320a},
- {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303},
- {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
- {0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
- {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
- {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
- {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
- {0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0},
- {0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68},
- {0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68},
- {0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68},
- {0x00009850, 0x6c48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6c48b0e2, 0x6c48b0e2},
- {0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e},
- {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e, 0x31395d5e},
- {0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18},
- {0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
- {0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0},
- {0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081},
- {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0},
- {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016},
- {0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d},
- {0x00009944, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020},
- {0x00009960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40},
- {0x0000a960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40},
- {0x0000b960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40},
- {0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, 0x00001120},
- {0x0000c968, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce, 0x000003ce},
- {0x000099bc, 0x001a0600, 0x001a0600, 0x001a0c00, 0x001a0c00, 0x001a0c00},
- {0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be},
- {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77},
- {0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329},
- {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8},
- {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384},
- {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880, 0x00000880},
- {0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, 0xd03e4788},
- {0x0000a20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120},
- {0x0000b20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120},
- {0x0000c20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120},
- {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
- {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000},
- {0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, 0x0a1a7caa},
- {0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000},
- {0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, 0x2e032402},
- {0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, 0x4a0a3c06},
- {0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, 0x621a540b},
- {0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, 0x764f6c1b},
- {0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, 0x845b7a5a},
- {0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, 0x950f8ccf},
- {0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, 0xa5cf9b4f},
- {0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, 0xbddfaf1f},
- {0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, 0xd1ffc93f},
- {0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+static const u32 ar5416Modes_9160[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab},
+ {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+ {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+ {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300},
+ {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
+ {0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
+ {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
+ {0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0},
+ {0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68},
+ {0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68},
+ {0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68},
+ {0x00009850, 0x6c48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6c48b0e2},
+ {0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e},
+ {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e},
+ {0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20},
+ {0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
+ {0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0},
+ {0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081},
+ {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+ {0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d},
+ {0x00009944, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020},
+ {0x00009960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40},
+ {0x0000a960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40},
+ {0x0000b960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40},
+ {0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120},
+ {0x0000c968, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+ {0x000099bc, 0x001a0600, 0x001a0600, 0x001a0c00, 0x001a0c00},
+ {0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be},
+ {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77},
+ {0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329},
+ {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8},
+ {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384},
+ {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880},
+ {0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788},
+ {0x0000a20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120},
+ {0x0000b20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120},
+ {0x0000c20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120},
+ {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
+ {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa},
+ {0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000},
+ {0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402},
+ {0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06},
+ {0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b},
+ {0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b},
+ {0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a},
+ {0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf},
+ {0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f},
+ {0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f},
+ {0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f},
+ {0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000},
+ {0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
};
static const u32 ar5416Common_9160[][2] = {
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
index 2d4c0910295b..88279e325dca 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
@@ -41,7 +41,8 @@ static bool ar9002_hw_is_cal_supported(struct ath_hw *ah,
case ADC_DC_CAL:
/* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */
if (!IS_CHAN_B(chan) &&
- !(IS_CHAN_2GHZ(chan) && IS_CHAN_HT20(chan)))
+ !((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) &&
+ IS_CHAN_HT20(chan)))
supported = true;
break;
}
@@ -867,9 +868,6 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
/* Do PA Calibration */
ar9002_hw_pa_cal(ah, true);
- /* Do NF Calibration after DC offset and other calibrations */
- ath9k_hw_start_nfcal(ah, true);
-
if (ah->caldata)
ah->caldata->nfcal_pending = true;
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
index f344cc2b3d59..11f192a1ceb7 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/moduleparam.h>
#include "hw.h"
#include "ar5008_initvals.h"
#include "ar9001_initvals.h"
@@ -30,7 +31,7 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
{
if (AR_SREV_9271(ah)) {
INIT_INI_ARRAY(&ah->iniModes, ar9271Modes_9271,
- ARRAY_SIZE(ar9271Modes_9271), 6);
+ ARRAY_SIZE(ar9271Modes_9271), 5);
INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271,
ARRAY_SIZE(ar9271Common_9271), 2);
INIT_INI_ARRAY(&ah->iniCommon_normal_cck_fir_coeff_9271,
@@ -41,21 +42,21 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
ARRAY_SIZE(ar9271Common_japan_2484_cck_fir_coeff_9271), 2);
INIT_INI_ARRAY(&ah->iniModes_9271_1_0_only,
ar9271Modes_9271_1_0_only,
- ARRAY_SIZE(ar9271Modes_9271_1_0_only), 6);
+ ARRAY_SIZE(ar9271Modes_9271_1_0_only), 5);
INIT_INI_ARRAY(&ah->iniModes_9271_ANI_reg, ar9271Modes_9271_ANI_reg,
- ARRAY_SIZE(ar9271Modes_9271_ANI_reg), 6);
+ ARRAY_SIZE(ar9271Modes_9271_ANI_reg), 5);
INIT_INI_ARRAY(&ah->iniModes_high_power_tx_gain_9271,
ar9271Modes_high_power_tx_gain_9271,
- ARRAY_SIZE(ar9271Modes_high_power_tx_gain_9271), 6);
+ ARRAY_SIZE(ar9271Modes_high_power_tx_gain_9271), 5);
INIT_INI_ARRAY(&ah->iniModes_normal_power_tx_gain_9271,
ar9271Modes_normal_power_tx_gain_9271,
- ARRAY_SIZE(ar9271Modes_normal_power_tx_gain_9271), 6);
+ ARRAY_SIZE(ar9271Modes_normal_power_tx_gain_9271), 5);
return;
}
if (AR_SREV_9287_11_OR_LATER(ah)) {
INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1,
- ARRAY_SIZE(ar9287Modes_9287_1_1), 6);
+ ARRAY_SIZE(ar9287Modes_9287_1_1), 5);
INIT_INI_ARRAY(&ah->iniCommon, ar9287Common_9287_1_1,
ARRAY_SIZE(ar9287Common_9287_1_1), 2);
if (ah->config.pcie_clock_req)
@@ -71,7 +72,7 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
INIT_INI_ARRAY(&ah->iniModes, ar9285Modes_9285_1_2,
- ARRAY_SIZE(ar9285Modes_9285_1_2), 6);
+ ARRAY_SIZE(ar9285Modes_9285_1_2), 5);
INIT_INI_ARRAY(&ah->iniCommon, ar9285Common_9285_1_2,
ARRAY_SIZE(ar9285Common_9285_1_2), 2);
@@ -87,7 +88,7 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
}
} else if (AR_SREV_9280_20_OR_LATER(ah)) {
INIT_INI_ARRAY(&ah->iniModes, ar9280Modes_9280_2,
- ARRAY_SIZE(ar9280Modes_9280_2), 6);
+ ARRAY_SIZE(ar9280Modes_9280_2), 5);
INIT_INI_ARRAY(&ah->iniCommon, ar9280Common_9280_2,
ARRAY_SIZE(ar9280Common_9280_2), 2);
@@ -105,7 +106,7 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
ARRAY_SIZE(ar9280Modes_fast_clock_9280_2), 3);
} else if (AR_SREV_9160_10_OR_LATER(ah)) {
INIT_INI_ARRAY(&ah->iniModes, ar5416Modes_9160,
- ARRAY_SIZE(ar5416Modes_9160), 6);
+ ARRAY_SIZE(ar5416Modes_9160), 5);
INIT_INI_ARRAY(&ah->iniCommon, ar5416Common_9160,
ARRAY_SIZE(ar5416Common_9160), 2);
INIT_INI_ARRAY(&ah->iniBank0, ar5416Bank0_9160,
@@ -134,7 +135,7 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
}
} else if (AR_SREV_9100_OR_LATER(ah)) {
INIT_INI_ARRAY(&ah->iniModes, ar5416Modes_9100,
- ARRAY_SIZE(ar5416Modes_9100), 6);
+ ARRAY_SIZE(ar5416Modes_9100), 5);
INIT_INI_ARRAY(&ah->iniCommon, ar5416Common_9100,
ARRAY_SIZE(ar5416Common_9100), 2);
INIT_INI_ARRAY(&ah->iniBank0, ar5416Bank0_9100,
@@ -157,7 +158,7 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
ARRAY_SIZE(ar5416Addac_9100), 2);
} else {
INIT_INI_ARRAY(&ah->iniModes, ar5416Modes,
- ARRAY_SIZE(ar5416Modes), 6);
+ ARRAY_SIZE(ar5416Modes), 5);
INIT_INI_ARRAY(&ah->iniCommon, ar5416Common,
ARRAY_SIZE(ar5416Common), 2);
INIT_INI_ARRAY(&ah->iniBank0, ar5416Bank0,
@@ -207,19 +208,19 @@ static void ar9280_20_hw_init_rxgain_ini(struct ath_hw *ah)
if (rxgain_type == AR5416_EEP_RXGAIN_13DB_BACKOFF)
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9280Modes_backoff_13db_rxgain_9280_2,
- ARRAY_SIZE(ar9280Modes_backoff_13db_rxgain_9280_2), 6);
+ ARRAY_SIZE(ar9280Modes_backoff_13db_rxgain_9280_2), 5);
else if (rxgain_type == AR5416_EEP_RXGAIN_23DB_BACKOFF)
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9280Modes_backoff_23db_rxgain_9280_2,
- ARRAY_SIZE(ar9280Modes_backoff_23db_rxgain_9280_2), 6);
+ ARRAY_SIZE(ar9280Modes_backoff_23db_rxgain_9280_2), 5);
else
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9280Modes_original_rxgain_9280_2,
- ARRAY_SIZE(ar9280Modes_original_rxgain_9280_2), 6);
+ ARRAY_SIZE(ar9280Modes_original_rxgain_9280_2), 5);
} else {
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9280Modes_original_rxgain_9280_2,
- ARRAY_SIZE(ar9280Modes_original_rxgain_9280_2), 6);
+ ARRAY_SIZE(ar9280Modes_original_rxgain_9280_2), 5);
}
}
@@ -234,15 +235,15 @@ static void ar9280_20_hw_init_txgain_ini(struct ath_hw *ah)
if (txgain_type == AR5416_EEP_TXGAIN_HIGH_POWER)
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9280Modes_high_power_tx_gain_9280_2,
- ARRAY_SIZE(ar9280Modes_high_power_tx_gain_9280_2), 6);
+ ARRAY_SIZE(ar9280Modes_high_power_tx_gain_9280_2), 5);
else
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9280Modes_original_tx_gain_9280_2,
- ARRAY_SIZE(ar9280Modes_original_tx_gain_9280_2), 6);
+ ARRAY_SIZE(ar9280Modes_original_tx_gain_9280_2), 5);
} else {
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9280Modes_original_tx_gain_9280_2,
- ARRAY_SIZE(ar9280Modes_original_tx_gain_9280_2), 6);
+ ARRAY_SIZE(ar9280Modes_original_tx_gain_9280_2), 5);
}
}
@@ -251,14 +252,14 @@ static void ar9002_hw_init_mode_gain_regs(struct ath_hw *ah)
if (AR_SREV_9287_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9287Modes_rx_gain_9287_1_1,
- ARRAY_SIZE(ar9287Modes_rx_gain_9287_1_1), 6);
+ ARRAY_SIZE(ar9287Modes_rx_gain_9287_1_1), 5);
else if (AR_SREV_9280_20(ah))
ar9280_20_hw_init_rxgain_ini(ah);
if (AR_SREV_9287_11_OR_LATER(ah)) {
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9287Modes_tx_gain_9287_1_1,
- ARRAY_SIZE(ar9287Modes_tx_gain_9287_1_1), 6);
+ ARRAY_SIZE(ar9287Modes_tx_gain_9287_1_1), 5);
} else if (AR_SREV_9280_20(ah)) {
ar9280_20_hw_init_txgain_ini(ah);
} else if (AR_SREV_9285_12_OR_LATER(ah)) {
@@ -270,24 +271,24 @@ static void ar9002_hw_init_mode_gain_regs(struct ath_hw *ah)
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9285Modes_XE2_0_high_power,
ARRAY_SIZE(
- ar9285Modes_XE2_0_high_power), 6);
+ ar9285Modes_XE2_0_high_power), 5);
} else {
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9285Modes_high_power_tx_gain_9285_1_2,
ARRAY_SIZE(
- ar9285Modes_high_power_tx_gain_9285_1_2), 6);
+ ar9285Modes_high_power_tx_gain_9285_1_2), 5);
}
} else {
if (AR_SREV_9285E_20(ah)) {
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9285Modes_XE2_0_normal_power,
ARRAY_SIZE(
- ar9285Modes_XE2_0_normal_power), 6);
+ ar9285Modes_XE2_0_normal_power), 5);
} else {
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9285Modes_original_tx_gain_9285_1_2,
ARRAY_SIZE(
- ar9285Modes_original_tx_gain_9285_1_2), 6);
+ ar9285Modes_original_tx_gain_9285_1_2), 5);
}
}
}
@@ -303,21 +304,13 @@ static void ar9002_hw_init_mode_gain_regs(struct ath_hw *ah)
* register as the other analog registers. Hence the 9 writes.
*/
static void ar9002_hw_configpcipowersave(struct ath_hw *ah,
- int restore,
- int power_off)
+ bool power_off)
{
u8 i;
u32 val;
- if (ah->is_pciexpress != true)
- return;
-
- /* Do not touch SerDes registers */
- if (ah->config.pcie_powersave_enable == 2)
- return;
-
/* Nothing to do on restore for 11N */
- if (!restore) {
+ if (!power_off /* !restore */) {
if (AR_SREV_9280_20_OR_LATER(ah)) {
/*
* AR9280 2.0 or later chips use SerDes values from the
@@ -499,45 +492,6 @@ void ar9002_hw_enable_async_fifo(struct ath_hw *ah)
}
}
-/*
- * If Async FIFO is enabled, the following counters change as MAC now runs
- * at 117 Mhz instead of 88/44MHz when async FIFO is disabled.
- *
- * The values below tested for ht40 2 chain.
- * Overwrite the delay/timeouts initialized in process ini.
- */
-void ar9002_hw_update_async_fifo(struct ath_hw *ah)
-{
- if (AR_SREV_9287_13_OR_LATER(ah)) {
- REG_WRITE(ah, AR_D_GBL_IFS_SIFS,
- AR_D_GBL_IFS_SIFS_ASYNC_FIFO_DUR);
- REG_WRITE(ah, AR_D_GBL_IFS_SLOT,
- AR_D_GBL_IFS_SLOT_ASYNC_FIFO_DUR);
- REG_WRITE(ah, AR_D_GBL_IFS_EIFS,
- AR_D_GBL_IFS_EIFS_ASYNC_FIFO_DUR);
-
- REG_WRITE(ah, AR_TIME_OUT, AR_TIME_OUT_ACK_CTS_ASYNC_FIFO_DUR);
- REG_WRITE(ah, AR_USEC, AR_USEC_ASYNC_FIFO_DUR);
-
- REG_SET_BIT(ah, AR_MAC_PCU_LOGIC_ANALYZER,
- AR_MAC_PCU_LOGIC_ANALYZER_DISBUG20768);
- REG_RMW_FIELD(ah, AR_AHB_MODE, AR_AHB_CUSTOM_BURST_EN,
- AR_AHB_CUSTOM_BURST_ASYNC_FIFO_VAL);
- }
-}
-
-/*
- * We don't enable WEP aggregation on mac80211 but we keep this
- * around for HAL unification purposes.
- */
-void ar9002_hw_enable_wep_aggregation(struct ath_hw *ah)
-{
- if (AR_SREV_9287_13_OR_LATER(ah)) {
- REG_SET_BIT(ah, AR_PCU_MISC_MODE2,
- AR_PCU_MISC_MODE2_ENABLE_AGGWEP);
- }
-}
-
/* Sets up the AR5008/AR9001/AR9002 hardware familiy callbacks */
void ar9002_hw_attach_ops(struct ath_hw *ah)
{
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h
index 7573257731b6..863db321070d 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h
@@ -14,53 +14,54 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-static const u32 ar9280Modes_9280_2[][6] = {
- {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0},
- {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0},
- {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180},
- {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008},
- {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0},
- {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f},
- {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810, 0x08f04810},
- {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a, 0x0000320a},
- {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880},
- {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303},
- {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
- {0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
- {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
- {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
- {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
- {0x00009840, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e, 0x206a012e},
- {0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0},
- {0x00009850, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2, 0x6c4000e2},
- {0x00009858, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e},
- {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e, 0x31395d5e},
- {0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18},
- {0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
- {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
- {0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881},
- {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0},
- {0x00009918, 0x0000000a, 0x00000014, 0x00000268, 0x0000000b, 0x00000016},
- {0x00009924, 0xd00a8a0b, 0xd00a8a0b, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d},
- {0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010},
- {0x00009960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010},
- {0x0000a960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010},
- {0x00009964, 0x00000210, 0x00000210, 0x00000210, 0x00000210, 0x00000210},
- {0x0000c968, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce, 0x000003ce},
- {0x000099b8, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c},
- {0x000099bc, 0x00000a00, 0x00000a00, 0x00000c00, 0x00000c00, 0x00000c00},
- {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
- {0x0000a204, 0x00000444, 0x00000444, 0x00000444, 0x00000444, 0x00000444},
- {0x0000a20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019},
- {0x0000b20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019},
- {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
- {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000},
- {0x0000a23c, 0x13c88000, 0x13c88000, 0x13c88001, 0x13c88000, 0x13c88000},
- {0x0000a250, 0x001ff000, 0x001ff000, 0x0004a000, 0x0004a000, 0x0004a000},
- {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e},
- {0x0000a388, 0x0c000000, 0x0c000000, 0x08000000, 0x0c000000, 0x0c000000},
- {0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000},
+static const u32 ar9280Modes_9280_2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b},
+ {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+ {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+ {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
+ {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300},
+ {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
+ {0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
+ {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
+ {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
+ {0x00009840, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e},
+ {0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0},
+ {0x00009850, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
+ {0x00009858, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e},
+ {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e},
+ {0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20},
+ {0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
+ {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+ {0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+ {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x00009918, 0x0000000a, 0x00000014, 0x00000268, 0x0000000b},
+ {0x00009924, 0xd00a8a0b, 0xd00a8a0b, 0xd00a8a0d, 0xd00a8a0d},
+ {0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010},
+ {0x00009960, 0x00000010, 0x00000010, 0x00000010, 0x00000010},
+ {0x0000a960, 0x00000010, 0x00000010, 0x00000010, 0x00000010},
+ {0x00009964, 0x00000210, 0x00000210, 0x00000210, 0x00000210},
+ {0x0000c968, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+ {0x000099b8, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c},
+ {0x000099bc, 0x00000a00, 0x00000a00, 0x00000c00, 0x00000c00},
+ {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+ {0x0000a204, 0x00000444, 0x00000444, 0x00000444, 0x00000444},
+ {0x0000a20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019},
+ {0x0000b20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019},
+ {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
+ {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a23c, 0x13c88000, 0x13c88000, 0x13c88001, 0x13c88000},
+ {0x0000a250, 0x001ff000, 0x001ff000, 0x0004a000, 0x0004a000},
+ {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e},
+ {0x0000a388, 0x0c000000, 0x0c000000, 0x08000000, 0x0c000000},
+ {0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000},
};
static const u32 ar9280Common_9280_2[][2] = {
@@ -424,471 +425,476 @@ static const u32 ar9280Modes_fast_clock_9280_2[][3] = {
{0x00009918, 0x0000000b, 0x00000016},
};
-static const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][6] = {
- {0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290},
- {0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300},
- {0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304},
- {0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308, 0x00000308},
- {0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c, 0x0000030c},
- {0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000, 0x00008000},
- {0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004, 0x00008004},
- {0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008, 0x00008008},
- {0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c, 0x0000800c},
- {0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080, 0x00008080},
- {0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084, 0x00008084},
- {0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088, 0x00008088},
- {0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c, 0x0000808c},
- {0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100, 0x00008100},
- {0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104, 0x00008104},
- {0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108, 0x00008108},
- {0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c, 0x0000810c},
- {0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110, 0x00008110},
- {0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114, 0x00008114},
- {0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180, 0x00008180},
- {0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184, 0x00008184},
- {0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188, 0x00008188},
- {0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c, 0x0000818c},
- {0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190, 0x00008190},
- {0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194, 0x00008194},
- {0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0, 0x000081a0},
- {0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c, 0x0000820c},
- {0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8, 0x000081a8},
- {0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284, 0x00008284},
- {0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288, 0x00008288},
- {0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224, 0x00008224},
- {0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290, 0x00008290},
- {0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300, 0x00008300},
- {0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304, 0x00008304},
- {0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308, 0x00008308},
- {0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c, 0x0000830c},
- {0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380, 0x00008380},
- {0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384, 0x00008384},
- {0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700, 0x00008700},
- {0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704, 0x00008704},
- {0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708, 0x00008708},
- {0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c, 0x0000870c},
- {0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780, 0x00008780},
- {0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784, 0x00008784},
- {0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00, 0x00008b00},
- {0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04, 0x00008b04},
- {0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08, 0x00008b08},
- {0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c, 0x00008b0c},
- {0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b10, 0x00008b10, 0x00008b10},
- {0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b80, 0x00008b80, 0x00008b80},
- {0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b84, 0x00008b84, 0x00008b84},
- {0x00009acc, 0x0000b380, 0x0000b380, 0x00008b88, 0x00008b88, 0x00008b88},
- {0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b8c, 0x00008b8c, 0x00008b8c},
- {0x00009ad4, 0x0000b388, 0x0000b388, 0x00008b90, 0x00008b90, 0x00008b90},
- {0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008b94, 0x00008b94, 0x00008b94},
- {0x00009adc, 0x0000b390, 0x0000b390, 0x00008b98, 0x00008b98, 0x00008b98},
- {0x00009ae0, 0x0000b394, 0x0000b394, 0x00008ba4, 0x00008ba4, 0x00008ba4},
- {0x00009ae4, 0x0000b398, 0x0000b398, 0x00008ba8, 0x00008ba8, 0x00008ba8},
- {0x00009ae8, 0x0000b780, 0x0000b780, 0x00008bac, 0x00008bac, 0x00008bac},
- {0x00009aec, 0x0000b784, 0x0000b784, 0x00008bb0, 0x00008bb0, 0x00008bb0},
- {0x00009af0, 0x0000b788, 0x0000b788, 0x00008bb4, 0x00008bb4, 0x00008bb4},
- {0x00009af4, 0x0000b78c, 0x0000b78c, 0x00008ba1, 0x00008ba1, 0x00008ba1},
- {0x00009af8, 0x0000b790, 0x0000b790, 0x00008ba5, 0x00008ba5, 0x00008ba5},
- {0x00009afc, 0x0000b794, 0x0000b794, 0x00008ba9, 0x00008ba9, 0x00008ba9},
- {0x00009b00, 0x0000b798, 0x0000b798, 0x00008bad, 0x00008bad, 0x00008bad},
- {0x00009b04, 0x0000d784, 0x0000d784, 0x00008bb1, 0x00008bb1, 0x00008bb1},
- {0x00009b08, 0x0000d788, 0x0000d788, 0x00008bb5, 0x00008bb5, 0x00008bb5},
- {0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00008ba2, 0x00008ba2, 0x00008ba2},
- {0x00009b10, 0x0000d790, 0x0000d790, 0x00008ba6, 0x00008ba6, 0x00008ba6},
- {0x00009b14, 0x0000f780, 0x0000f780, 0x00008baa, 0x00008baa, 0x00008baa},
- {0x00009b18, 0x0000f784, 0x0000f784, 0x00008bae, 0x00008bae, 0x00008bae},
- {0x00009b1c, 0x0000f788, 0x0000f788, 0x00008bb2, 0x00008bb2, 0x00008bb2},
- {0x00009b20, 0x0000f78c, 0x0000f78c, 0x00008bb6, 0x00008bb6, 0x00008bb6},
- {0x00009b24, 0x0000f790, 0x0000f790, 0x00008ba3, 0x00008ba3, 0x00008ba3},
- {0x00009b28, 0x0000f794, 0x0000f794, 0x00008ba7, 0x00008ba7, 0x00008ba7},
- {0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x00008bab, 0x00008bab, 0x00008bab},
- {0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x00008baf, 0x00008baf, 0x00008baf},
- {0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x00008bb3, 0x00008bb3, 0x00008bb3},
- {0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x00008bb7, 0x00008bb7, 0x00008bb7},
- {0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x00008bc3, 0x00008bc3, 0x00008bc3},
- {0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x00008bc7, 0x00008bc7, 0x00008bc7},
- {0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x00008bcb, 0x00008bcb, 0x00008bcb},
- {0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x00008bcf, 0x00008bcf, 0x00008bcf},
- {0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x00008bd3, 0x00008bd3, 0x00008bd3},
- {0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x00008bd7, 0x00008bd7, 0x00008bd7},
- {0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b98, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009b9c, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009ba0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009ba4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009ba8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bac, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bb0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bb4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bb8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bbc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bc0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bc4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bc8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bcc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bd0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bd4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bd8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bdc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009be0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009be4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009be8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bec, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bf0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bf4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bf8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009bfc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb, 0x00008bdb},
- {0x00009848, 0x00001066, 0x00001066, 0x00001055, 0x00001055, 0x00001055},
- {0x0000a848, 0x00001066, 0x00001066, 0x00001055, 0x00001055, 0x00001055},
+static const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290},
+ {0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300},
+ {0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304},
+ {0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308},
+ {0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c},
+ {0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000},
+ {0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004},
+ {0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008},
+ {0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c},
+ {0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080},
+ {0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084},
+ {0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088},
+ {0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c},
+ {0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100},
+ {0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104},
+ {0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108},
+ {0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c},
+ {0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110},
+ {0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114},
+ {0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180},
+ {0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184},
+ {0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188},
+ {0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c},
+ {0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190},
+ {0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194},
+ {0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0},
+ {0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c},
+ {0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8},
+ {0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284},
+ {0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288},
+ {0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224},
+ {0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290},
+ {0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300},
+ {0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304},
+ {0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308},
+ {0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c},
+ {0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380},
+ {0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384},
+ {0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700},
+ {0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704},
+ {0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708},
+ {0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c},
+ {0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780},
+ {0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784},
+ {0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00},
+ {0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04},
+ {0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08},
+ {0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c},
+ {0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b10, 0x00008b10},
+ {0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b80, 0x00008b80},
+ {0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b84, 0x00008b84},
+ {0x00009acc, 0x0000b380, 0x0000b380, 0x00008b88, 0x00008b88},
+ {0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b8c, 0x00008b8c},
+ {0x00009ad4, 0x0000b388, 0x0000b388, 0x00008b90, 0x00008b90},
+ {0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008b94, 0x00008b94},
+ {0x00009adc, 0x0000b390, 0x0000b390, 0x00008b98, 0x00008b98},
+ {0x00009ae0, 0x0000b394, 0x0000b394, 0x00008ba4, 0x00008ba4},
+ {0x00009ae4, 0x0000b398, 0x0000b398, 0x00008ba8, 0x00008ba8},
+ {0x00009ae8, 0x0000b780, 0x0000b780, 0x00008bac, 0x00008bac},
+ {0x00009aec, 0x0000b784, 0x0000b784, 0x00008bb0, 0x00008bb0},
+ {0x00009af0, 0x0000b788, 0x0000b788, 0x00008bb4, 0x00008bb4},
+ {0x00009af4, 0x0000b78c, 0x0000b78c, 0x00008ba1, 0x00008ba1},
+ {0x00009af8, 0x0000b790, 0x0000b790, 0x00008ba5, 0x00008ba5},
+ {0x00009afc, 0x0000b794, 0x0000b794, 0x00008ba9, 0x00008ba9},
+ {0x00009b00, 0x0000b798, 0x0000b798, 0x00008bad, 0x00008bad},
+ {0x00009b04, 0x0000d784, 0x0000d784, 0x00008bb1, 0x00008bb1},
+ {0x00009b08, 0x0000d788, 0x0000d788, 0x00008bb5, 0x00008bb5},
+ {0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00008ba2, 0x00008ba2},
+ {0x00009b10, 0x0000d790, 0x0000d790, 0x00008ba6, 0x00008ba6},
+ {0x00009b14, 0x0000f780, 0x0000f780, 0x00008baa, 0x00008baa},
+ {0x00009b18, 0x0000f784, 0x0000f784, 0x00008bae, 0x00008bae},
+ {0x00009b1c, 0x0000f788, 0x0000f788, 0x00008bb2, 0x00008bb2},
+ {0x00009b20, 0x0000f78c, 0x0000f78c, 0x00008bb6, 0x00008bb6},
+ {0x00009b24, 0x0000f790, 0x0000f790, 0x00008ba3, 0x00008ba3},
+ {0x00009b28, 0x0000f794, 0x0000f794, 0x00008ba7, 0x00008ba7},
+ {0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x00008bab, 0x00008bab},
+ {0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x00008baf, 0x00008baf},
+ {0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x00008bb3, 0x00008bb3},
+ {0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x00008bb7, 0x00008bb7},
+ {0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x00008bc3, 0x00008bc3},
+ {0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x00008bc7, 0x00008bc7},
+ {0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x00008bcb, 0x00008bcb},
+ {0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x00008bcf, 0x00008bcf},
+ {0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x00008bd3, 0x00008bd3},
+ {0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x00008bd7, 0x00008bd7},
+ {0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x00008bdb, 0x00008bdb},
+ {0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x00008bdb, 0x00008bdb},
+ {0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x00008bdb, 0x00008bdb},
+ {0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x00008bdb, 0x00008bdb},
+ {0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x00008bdb, 0x00008bdb},
+ {0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x00008bdb, 0x00008bdb},
+ {0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x00008bdb, 0x00008bdb},
+ {0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x00008bdb, 0x00008bdb},
+ {0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x00008bdb, 0x00008bdb},
+ {0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x00008bdb, 0x00008bdb},
+ {0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x00008bdb, 0x00008bdb},
+ {0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x00008bdb, 0x00008bdb},
+ {0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x00008bdb, 0x00008bdb},
+ {0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x00008bdb, 0x00008bdb},
+ {0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x00008bdb, 0x00008bdb},
+ {0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x00008bdb, 0x00008bdb},
+ {0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x00008bdb, 0x00008bdb},
+ {0x00009b98, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009b9c, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009ba0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009ba4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009ba8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bac, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bb0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bb4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bb8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bbc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bc0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bc4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bc8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bcc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bd0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bd4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bd8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bdc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009be0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009be4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009be8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bec, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bf0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bf4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bf8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009bfc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb},
+ {0x00009848, 0x00001066, 0x00001066, 0x00001055, 0x00001055},
+ {0x0000a848, 0x00001066, 0x00001066, 0x00001055, 0x00001055},
};
-static const u32 ar9280Modes_original_rxgain_9280_2[][6] = {
- {0x00009a00, 0x00008184, 0x00008184, 0x00008000, 0x00008000, 0x00008000},
- {0x00009a04, 0x00008188, 0x00008188, 0x00008000, 0x00008000, 0x00008000},
- {0x00009a08, 0x0000818c, 0x0000818c, 0x00008000, 0x00008000, 0x00008000},
- {0x00009a0c, 0x00008190, 0x00008190, 0x00008000, 0x00008000, 0x00008000},
- {0x00009a10, 0x00008194, 0x00008194, 0x00008000, 0x00008000, 0x00008000},
- {0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000, 0x00008000},
- {0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004, 0x00008004},
- {0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008, 0x00008008},
- {0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c, 0x0000800c},
- {0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080, 0x00008080},
- {0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084, 0x00008084},
- {0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088, 0x00008088},
- {0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c, 0x0000808c},
- {0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100, 0x00008100},
- {0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104, 0x00008104},
- {0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108, 0x00008108},
- {0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c, 0x0000810c},
- {0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110, 0x00008110},
- {0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114, 0x00008114},
- {0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180, 0x00008180},
- {0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184, 0x00008184},
- {0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188, 0x00008188},
- {0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c, 0x0000818c},
- {0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190, 0x00008190},
- {0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194, 0x00008194},
- {0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0, 0x000081a0},
- {0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c, 0x0000820c},
- {0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8, 0x000081a8},
- {0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284, 0x00008284},
- {0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288, 0x00008288},
- {0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224, 0x00008224},
- {0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290, 0x00008290},
- {0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300, 0x00008300},
- {0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304, 0x00008304},
- {0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308, 0x00008308},
- {0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c, 0x0000830c},
- {0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380, 0x00008380},
- {0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384, 0x00008384},
- {0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700, 0x00008700},
- {0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704, 0x00008704},
- {0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708, 0x00008708},
- {0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c, 0x0000870c},
- {0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780, 0x00008780},
- {0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784, 0x00008784},
- {0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00, 0x00008b00},
- {0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04, 0x00008b04},
- {0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08, 0x00008b08},
- {0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c, 0x00008b0c},
- {0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b80, 0x00008b80, 0x00008b80},
- {0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b84, 0x00008b84, 0x00008b84},
- {0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b88, 0x00008b88, 0x00008b88},
- {0x00009acc, 0x0000b380, 0x0000b380, 0x00008b8c, 0x00008b8c, 0x00008b8c},
- {0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b90, 0x00008b90, 0x00008b90},
- {0x00009ad4, 0x0000b388, 0x0000b388, 0x00008f80, 0x00008f80, 0x00008f80},
- {0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008f84, 0x00008f84, 0x00008f84},
- {0x00009adc, 0x0000b390, 0x0000b390, 0x00008f88, 0x00008f88, 0x00008f88},
- {0x00009ae0, 0x0000b394, 0x0000b394, 0x00008f8c, 0x00008f8c, 0x00008f8c},
- {0x00009ae4, 0x0000b398, 0x0000b398, 0x00008f90, 0x00008f90, 0x00008f90},
- {0x00009ae8, 0x0000b780, 0x0000b780, 0x0000930c, 0x0000930c, 0x0000930c},
- {0x00009aec, 0x0000b784, 0x0000b784, 0x00009310, 0x00009310, 0x00009310},
- {0x00009af0, 0x0000b788, 0x0000b788, 0x00009384, 0x00009384, 0x00009384},
- {0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009388, 0x00009388, 0x00009388},
- {0x00009af8, 0x0000b790, 0x0000b790, 0x00009324, 0x00009324, 0x00009324},
- {0x00009afc, 0x0000b794, 0x0000b794, 0x00009704, 0x00009704, 0x00009704},
- {0x00009b00, 0x0000b798, 0x0000b798, 0x000096a4, 0x000096a4, 0x000096a4},
- {0x00009b04, 0x0000d784, 0x0000d784, 0x000096a8, 0x000096a8, 0x000096a8},
- {0x00009b08, 0x0000d788, 0x0000d788, 0x00009710, 0x00009710, 0x00009710},
- {0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009714, 0x00009714, 0x00009714},
- {0x00009b10, 0x0000d790, 0x0000d790, 0x00009720, 0x00009720, 0x00009720},
- {0x00009b14, 0x0000f780, 0x0000f780, 0x00009724, 0x00009724, 0x00009724},
- {0x00009b18, 0x0000f784, 0x0000f784, 0x00009728, 0x00009728, 0x00009728},
- {0x00009b1c, 0x0000f788, 0x0000f788, 0x0000972c, 0x0000972c, 0x0000972c},
- {0x00009b20, 0x0000f78c, 0x0000f78c, 0x000097a0, 0x000097a0, 0x000097a0},
- {0x00009b24, 0x0000f790, 0x0000f790, 0x000097a4, 0x000097a4, 0x000097a4},
- {0x00009b28, 0x0000f794, 0x0000f794, 0x000097a8, 0x000097a8, 0x000097a8},
- {0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x000097b0, 0x000097b0, 0x000097b0},
- {0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x000097b4, 0x000097b4, 0x000097b4},
- {0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x000097b8, 0x000097b8, 0x000097b8},
- {0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x000097a5, 0x000097a5, 0x000097a5},
- {0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x000097a9, 0x000097a9, 0x000097a9},
- {0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x000097ad, 0x000097ad, 0x000097ad},
- {0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x000097b1, 0x000097b1, 0x000097b1},
- {0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x000097b5, 0x000097b5, 0x000097b5},
- {0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x000097b9, 0x000097b9, 0x000097b9},
- {0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x000097c5, 0x000097c5, 0x000097c5},
- {0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x000097c9, 0x000097c9, 0x000097c9},
- {0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x000097d1, 0x000097d1, 0x000097d1},
- {0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x000097d5, 0x000097d5, 0x000097d5},
- {0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x000097d9, 0x000097d9, 0x000097d9},
- {0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x000097c6, 0x000097c6, 0x000097c6},
- {0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x000097ca, 0x000097ca, 0x000097ca},
- {0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x000097ce, 0x000097ce, 0x000097ce},
- {0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x000097d2, 0x000097d2, 0x000097d2},
- {0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x000097d6, 0x000097d6, 0x000097d6},
- {0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x000097c3, 0x000097c3, 0x000097c3},
- {0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x000097c7, 0x000097c7, 0x000097c7},
- {0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x000097cb, 0x000097cb, 0x000097cb},
- {0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x000097cf, 0x000097cf, 0x000097cf},
- {0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x000097d7, 0x000097d7, 0x000097d7},
- {0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x000097db, 0x000097db, 0x000097db},
- {0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x000097db, 0x000097db, 0x000097db},
- {0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x000097db, 0x000097db, 0x000097db},
- {0x00009b98, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009b9c, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009ba0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009ba4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009ba8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bac, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bb0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bb4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bb8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bbc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bc0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bc4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bc8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bcc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bd0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bd4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bd8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bdc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009be0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009be4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009be8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bec, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bf0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bf4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bf8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009bfc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db},
- {0x00009848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063},
- {0x0000a848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063},
+static const u32 ar9280Modes_original_rxgain_9280_2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009a00, 0x00008184, 0x00008184, 0x00008000, 0x00008000},
+ {0x00009a04, 0x00008188, 0x00008188, 0x00008000, 0x00008000},
+ {0x00009a08, 0x0000818c, 0x0000818c, 0x00008000, 0x00008000},
+ {0x00009a0c, 0x00008190, 0x00008190, 0x00008000, 0x00008000},
+ {0x00009a10, 0x00008194, 0x00008194, 0x00008000, 0x00008000},
+ {0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000},
+ {0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004},
+ {0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008},
+ {0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c},
+ {0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080},
+ {0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084},
+ {0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088},
+ {0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c},
+ {0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100},
+ {0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104},
+ {0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108},
+ {0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c},
+ {0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110},
+ {0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114},
+ {0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180},
+ {0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184},
+ {0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188},
+ {0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c},
+ {0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190},
+ {0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194},
+ {0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0},
+ {0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c},
+ {0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8},
+ {0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284},
+ {0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288},
+ {0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224},
+ {0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290},
+ {0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300},
+ {0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304},
+ {0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308},
+ {0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c},
+ {0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380},
+ {0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384},
+ {0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700},
+ {0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704},
+ {0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708},
+ {0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c},
+ {0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780},
+ {0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784},
+ {0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00},
+ {0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04},
+ {0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08},
+ {0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c},
+ {0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b80, 0x00008b80},
+ {0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b84, 0x00008b84},
+ {0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b88, 0x00008b88},
+ {0x00009acc, 0x0000b380, 0x0000b380, 0x00008b8c, 0x00008b8c},
+ {0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b90, 0x00008b90},
+ {0x00009ad4, 0x0000b388, 0x0000b388, 0x00008f80, 0x00008f80},
+ {0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008f84, 0x00008f84},
+ {0x00009adc, 0x0000b390, 0x0000b390, 0x00008f88, 0x00008f88},
+ {0x00009ae0, 0x0000b394, 0x0000b394, 0x00008f8c, 0x00008f8c},
+ {0x00009ae4, 0x0000b398, 0x0000b398, 0x00008f90, 0x00008f90},
+ {0x00009ae8, 0x0000b780, 0x0000b780, 0x0000930c, 0x0000930c},
+ {0x00009aec, 0x0000b784, 0x0000b784, 0x00009310, 0x00009310},
+ {0x00009af0, 0x0000b788, 0x0000b788, 0x00009384, 0x00009384},
+ {0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009388, 0x00009388},
+ {0x00009af8, 0x0000b790, 0x0000b790, 0x00009324, 0x00009324},
+ {0x00009afc, 0x0000b794, 0x0000b794, 0x00009704, 0x00009704},
+ {0x00009b00, 0x0000b798, 0x0000b798, 0x000096a4, 0x000096a4},
+ {0x00009b04, 0x0000d784, 0x0000d784, 0x000096a8, 0x000096a8},
+ {0x00009b08, 0x0000d788, 0x0000d788, 0x00009710, 0x00009710},
+ {0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009714, 0x00009714},
+ {0x00009b10, 0x0000d790, 0x0000d790, 0x00009720, 0x00009720},
+ {0x00009b14, 0x0000f780, 0x0000f780, 0x00009724, 0x00009724},
+ {0x00009b18, 0x0000f784, 0x0000f784, 0x00009728, 0x00009728},
+ {0x00009b1c, 0x0000f788, 0x0000f788, 0x0000972c, 0x0000972c},
+ {0x00009b20, 0x0000f78c, 0x0000f78c, 0x000097a0, 0x000097a0},
+ {0x00009b24, 0x0000f790, 0x0000f790, 0x000097a4, 0x000097a4},
+ {0x00009b28, 0x0000f794, 0x0000f794, 0x000097a8, 0x000097a8},
+ {0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x000097b0, 0x000097b0},
+ {0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x000097b4, 0x000097b4},
+ {0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x000097b8, 0x000097b8},
+ {0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x000097a5, 0x000097a5},
+ {0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x000097a9, 0x000097a9},
+ {0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x000097ad, 0x000097ad},
+ {0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x000097b1, 0x000097b1},
+ {0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x000097b5, 0x000097b5},
+ {0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x000097b9, 0x000097b9},
+ {0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x000097c5, 0x000097c5},
+ {0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x000097c9, 0x000097c9},
+ {0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x000097d1, 0x000097d1},
+ {0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x000097d5, 0x000097d5},
+ {0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x000097d9, 0x000097d9},
+ {0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x000097c6, 0x000097c6},
+ {0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x000097ca, 0x000097ca},
+ {0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x000097ce, 0x000097ce},
+ {0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x000097d2, 0x000097d2},
+ {0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x000097d6, 0x000097d6},
+ {0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x000097c3, 0x000097c3},
+ {0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x000097c7, 0x000097c7},
+ {0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x000097cb, 0x000097cb},
+ {0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x000097cf, 0x000097cf},
+ {0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x000097d7, 0x000097d7},
+ {0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x000097db, 0x000097db},
+ {0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x000097db, 0x000097db},
+ {0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x000097db, 0x000097db},
+ {0x00009b98, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009b9c, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009ba0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009ba4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009ba8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bac, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bb0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bb4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bb8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bbc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bc0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bc4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bc8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bcc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bd0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bd4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bd8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bdc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009be0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009be4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009be8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bec, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bf0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bf4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bf8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009bfc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db},
+ {0x00009848, 0x00001066, 0x00001066, 0x00001063, 0x00001063},
+ {0x0000a848, 0x00001066, 0x00001066, 0x00001063, 0x00001063},
};
-static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = {
- {0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290},
- {0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300},
- {0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304},
- {0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308, 0x00000308},
- {0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c, 0x0000030c},
- {0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000, 0x00008000},
- {0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004, 0x00008004},
- {0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008, 0x00008008},
- {0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c, 0x0000800c},
- {0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080, 0x00008080},
- {0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084, 0x00008084},
- {0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088, 0x00008088},
- {0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c, 0x0000808c},
- {0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100, 0x00008100},
- {0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104, 0x00008104},
- {0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108, 0x00008108},
- {0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c, 0x0000810c},
- {0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110, 0x00008110},
- {0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114, 0x00008114},
- {0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180, 0x00008180},
- {0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184, 0x00008184},
- {0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188, 0x00008188},
- {0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c, 0x0000818c},
- {0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190, 0x00008190},
- {0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194, 0x00008194},
- {0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0, 0x000081a0},
- {0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c, 0x0000820c},
- {0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8, 0x000081a8},
- {0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284, 0x00008284},
- {0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288, 0x00008288},
- {0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224, 0x00008224},
- {0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290, 0x00008290},
- {0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300, 0x00008300},
- {0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304, 0x00008304},
- {0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308, 0x00008308},
- {0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c, 0x0000830c},
- {0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380, 0x00008380},
- {0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384, 0x00008384},
- {0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700, 0x00008700},
- {0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704, 0x00008704},
- {0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708, 0x00008708},
- {0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c, 0x0000870c},
- {0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780, 0x00008780},
- {0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784, 0x00008784},
- {0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00, 0x00008b00},
- {0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04, 0x00008b04},
- {0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08, 0x00008b08},
- {0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c, 0x00008b0c},
- {0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b80, 0x00008b80, 0x00008b80},
- {0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b84, 0x00008b84, 0x00008b84},
- {0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b88, 0x00008b88, 0x00008b88},
- {0x00009acc, 0x0000b380, 0x0000b380, 0x00008b8c, 0x00008b8c, 0x00008b8c},
- {0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b90, 0x00008b90, 0x00008b90},
- {0x00009ad4, 0x0000b388, 0x0000b388, 0x00008f80, 0x00008f80, 0x00008f80},
- {0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008f84, 0x00008f84, 0x00008f84},
- {0x00009adc, 0x0000b390, 0x0000b390, 0x00008f88, 0x00008f88, 0x00008f88},
- {0x00009ae0, 0x0000b394, 0x0000b394, 0x00008f8c, 0x00008f8c, 0x00008f8c},
- {0x00009ae4, 0x0000b398, 0x0000b398, 0x00008f90, 0x00008f90, 0x00008f90},
- {0x00009ae8, 0x0000b780, 0x0000b780, 0x00009310, 0x00009310, 0x00009310},
- {0x00009aec, 0x0000b784, 0x0000b784, 0x00009314, 0x00009314, 0x00009314},
- {0x00009af0, 0x0000b788, 0x0000b788, 0x00009320, 0x00009320, 0x00009320},
- {0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009324, 0x00009324, 0x00009324},
- {0x00009af8, 0x0000b790, 0x0000b790, 0x00009328, 0x00009328, 0x00009328},
- {0x00009afc, 0x0000b794, 0x0000b794, 0x0000932c, 0x0000932c, 0x0000932c},
- {0x00009b00, 0x0000b798, 0x0000b798, 0x00009330, 0x00009330, 0x00009330},
- {0x00009b04, 0x0000d784, 0x0000d784, 0x00009334, 0x00009334, 0x00009334},
- {0x00009b08, 0x0000d788, 0x0000d788, 0x00009321, 0x00009321, 0x00009321},
- {0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009325, 0x00009325, 0x00009325},
- {0x00009b10, 0x0000d790, 0x0000d790, 0x00009329, 0x00009329, 0x00009329},
- {0x00009b14, 0x0000f780, 0x0000f780, 0x0000932d, 0x0000932d, 0x0000932d},
- {0x00009b18, 0x0000f784, 0x0000f784, 0x00009331, 0x00009331, 0x00009331},
- {0x00009b1c, 0x0000f788, 0x0000f788, 0x00009335, 0x00009335, 0x00009335},
- {0x00009b20, 0x0000f78c, 0x0000f78c, 0x00009322, 0x00009322, 0x00009322},
- {0x00009b24, 0x0000f790, 0x0000f790, 0x00009326, 0x00009326, 0x00009326},
- {0x00009b28, 0x0000f794, 0x0000f794, 0x0000932a, 0x0000932a, 0x0000932a},
- {0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x0000932e, 0x0000932e, 0x0000932e},
- {0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x00009332, 0x00009332, 0x00009332},
- {0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x00009336, 0x00009336, 0x00009336},
- {0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x00009323, 0x00009323, 0x00009323},
- {0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x00009327, 0x00009327, 0x00009327},
- {0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x0000932b, 0x0000932b, 0x0000932b},
- {0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x0000932f, 0x0000932f, 0x0000932f},
- {0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x00009333, 0x00009333, 0x00009333},
- {0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x00009337, 0x00009337, 0x00009337},
- {0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x00009343, 0x00009343, 0x00009343},
- {0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x00009347, 0x00009347, 0x00009347},
- {0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x0000934b, 0x0000934b, 0x0000934b},
- {0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x0000934f, 0x0000934f, 0x0000934f},
- {0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x00009353, 0x00009353, 0x00009353},
- {0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x00009357, 0x00009357, 0x00009357},
- {0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b98, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009b9c, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009ba0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009ba4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009ba8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bac, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bb0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bb4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bb8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bbc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bc0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bc4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bc8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bcc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bd0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bd4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bd8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bdc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009be0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009be4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009be8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bec, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bf0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bf4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bf8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009bfc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b},
- {0x00009848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a, 0x0000105a},
- {0x0000a848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a, 0x0000105a},
+static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290},
+ {0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300},
+ {0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304},
+ {0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308},
+ {0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c},
+ {0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000},
+ {0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004},
+ {0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008},
+ {0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c},
+ {0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080},
+ {0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084},
+ {0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088},
+ {0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c},
+ {0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100},
+ {0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104},
+ {0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108},
+ {0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c},
+ {0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110},
+ {0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114},
+ {0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180},
+ {0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184},
+ {0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188},
+ {0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c},
+ {0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190},
+ {0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194},
+ {0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0},
+ {0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c},
+ {0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8},
+ {0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284},
+ {0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288},
+ {0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224},
+ {0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290},
+ {0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300},
+ {0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304},
+ {0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308},
+ {0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c},
+ {0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380},
+ {0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384},
+ {0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700},
+ {0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704},
+ {0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708},
+ {0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c},
+ {0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780},
+ {0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784},
+ {0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00},
+ {0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04},
+ {0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08},
+ {0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c},
+ {0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b80, 0x00008b80},
+ {0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b84, 0x00008b84},
+ {0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b88, 0x00008b88},
+ {0x00009acc, 0x0000b380, 0x0000b380, 0x00008b8c, 0x00008b8c},
+ {0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b90, 0x00008b90},
+ {0x00009ad4, 0x0000b388, 0x0000b388, 0x00008f80, 0x00008f80},
+ {0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008f84, 0x00008f84},
+ {0x00009adc, 0x0000b390, 0x0000b390, 0x00008f88, 0x00008f88},
+ {0x00009ae0, 0x0000b394, 0x0000b394, 0x00008f8c, 0x00008f8c},
+ {0x00009ae4, 0x0000b398, 0x0000b398, 0x00008f90, 0x00008f90},
+ {0x00009ae8, 0x0000b780, 0x0000b780, 0x00009310, 0x00009310},
+ {0x00009aec, 0x0000b784, 0x0000b784, 0x00009314, 0x00009314},
+ {0x00009af0, 0x0000b788, 0x0000b788, 0x00009320, 0x00009320},
+ {0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009324, 0x00009324},
+ {0x00009af8, 0x0000b790, 0x0000b790, 0x00009328, 0x00009328},
+ {0x00009afc, 0x0000b794, 0x0000b794, 0x0000932c, 0x0000932c},
+ {0x00009b00, 0x0000b798, 0x0000b798, 0x00009330, 0x00009330},
+ {0x00009b04, 0x0000d784, 0x0000d784, 0x00009334, 0x00009334},
+ {0x00009b08, 0x0000d788, 0x0000d788, 0x00009321, 0x00009321},
+ {0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009325, 0x00009325},
+ {0x00009b10, 0x0000d790, 0x0000d790, 0x00009329, 0x00009329},
+ {0x00009b14, 0x0000f780, 0x0000f780, 0x0000932d, 0x0000932d},
+ {0x00009b18, 0x0000f784, 0x0000f784, 0x00009331, 0x00009331},
+ {0x00009b1c, 0x0000f788, 0x0000f788, 0x00009335, 0x00009335},
+ {0x00009b20, 0x0000f78c, 0x0000f78c, 0x00009322, 0x00009322},
+ {0x00009b24, 0x0000f790, 0x0000f790, 0x00009326, 0x00009326},
+ {0x00009b28, 0x0000f794, 0x0000f794, 0x0000932a, 0x0000932a},
+ {0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x0000932e, 0x0000932e},
+ {0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x00009332, 0x00009332},
+ {0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x00009336, 0x00009336},
+ {0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x00009323, 0x00009323},
+ {0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x00009327, 0x00009327},
+ {0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x0000932b, 0x0000932b},
+ {0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x0000932f, 0x0000932f},
+ {0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x00009333, 0x00009333},
+ {0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x00009337, 0x00009337},
+ {0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x00009343, 0x00009343},
+ {0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x00009347, 0x00009347},
+ {0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x0000934b, 0x0000934b},
+ {0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x0000934f, 0x0000934f},
+ {0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x00009353, 0x00009353},
+ {0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x00009357, 0x00009357},
+ {0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x0000935b, 0x0000935b},
+ {0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x0000935b, 0x0000935b},
+ {0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x0000935b, 0x0000935b},
+ {0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x0000935b, 0x0000935b},
+ {0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x0000935b, 0x0000935b},
+ {0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x0000935b, 0x0000935b},
+ {0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x0000935b, 0x0000935b},
+ {0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x0000935b, 0x0000935b},
+ {0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x0000935b, 0x0000935b},
+ {0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x0000935b, 0x0000935b},
+ {0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x0000935b, 0x0000935b},
+ {0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x0000935b, 0x0000935b},
+ {0x00009b98, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009b9c, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009ba0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009ba4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009ba8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bac, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bb0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bb4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bb8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bbc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bc0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bc4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bc8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bcc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bd0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bd4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bd8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bdc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009be0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009be4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009be8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bec, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bf0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bf4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bf8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009bfc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b},
+ {0x00009848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a},
+ {0x0000a848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a},
};
-static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = {
- {0x0000a274, 0x0a19e652, 0x0a19e652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652},
- {0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce},
- {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a304, 0x00003002, 0x00003002, 0x00004002, 0x00004002, 0x00004002},
- {0x0000a308, 0x00006004, 0x00006004, 0x00007008, 0x00007008, 0x00007008},
- {0x0000a30c, 0x0000a006, 0x0000a006, 0x0000c010, 0x0000c010, 0x0000c010},
- {0x0000a310, 0x0000e012, 0x0000e012, 0x00010012, 0x00010012, 0x00010012},
- {0x0000a314, 0x00011014, 0x00011014, 0x00013014, 0x00013014, 0x00013014},
- {0x0000a318, 0x0001504a, 0x0001504a, 0x0001820a, 0x0001820a, 0x0001820a},
- {0x0000a31c, 0x0001904c, 0x0001904c, 0x0001b211, 0x0001b211, 0x0001b211},
- {0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213, 0x0001e213},
- {0x0000a324, 0x00021092, 0x00021092, 0x00022411, 0x00022411, 0x00022411},
- {0x0000a328, 0x0002510a, 0x0002510a, 0x00025413, 0x00025413, 0x00025413},
- {0x0000a32c, 0x0002910c, 0x0002910c, 0x00029811, 0x00029811, 0x00029811},
- {0x0000a330, 0x0002c18b, 0x0002c18b, 0x0002c813, 0x0002c813, 0x0002c813},
- {0x0000a334, 0x0002f1cc, 0x0002f1cc, 0x00030a14, 0x00030a14, 0x00030a14},
- {0x0000a338, 0x000321eb, 0x000321eb, 0x00035a50, 0x00035a50, 0x00035a50},
- {0x0000a33c, 0x000341ec, 0x000341ec, 0x00039c4c, 0x00039c4c, 0x00039c4c},
- {0x0000a340, 0x000341ec, 0x000341ec, 0x0003de8a, 0x0003de8a, 0x0003de8a},
- {0x0000a344, 0x000341ec, 0x000341ec, 0x00042e92, 0x00042e92, 0x00042e92},
- {0x0000a348, 0x000341ec, 0x000341ec, 0x00046ed2, 0x00046ed2, 0x00046ed2},
- {0x0000a34c, 0x000341ec, 0x000341ec, 0x0004bed5, 0x0004bed5, 0x0004bed5},
- {0x0000a350, 0x000341ec, 0x000341ec, 0x0004ff54, 0x0004ff54, 0x0004ff54},
- {0x0000a354, 0x000341ec, 0x000341ec, 0x00055fd5, 0x00055fd5, 0x00055fd5},
- {0x0000a3ec, 0x00f70081, 0x00f70081, 0x00f70081, 0x00f70081, 0x00f70081},
- {0x00007814, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff},
- {0x00007838, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff},
- {0x0000781c, 0x00172000, 0x00172000, 0x00172000, 0x00172000, 0x00172000},
- {0x00007840, 0x00172000, 0x00172000, 0x00172000, 0x00172000, 0x00172000},
- {0x00007820, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480},
- {0x00007844, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480},
+static const u32 ar9280Modes_high_power_tx_gain_9280_2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a274, 0x0a19e652, 0x0a19e652, 0x0a1aa652, 0x0a1aa652},
+ {0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce},
+ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a304, 0x00003002, 0x00003002, 0x00004002, 0x00004002},
+ {0x0000a308, 0x00006004, 0x00006004, 0x00007008, 0x00007008},
+ {0x0000a30c, 0x0000a006, 0x0000a006, 0x0000c010, 0x0000c010},
+ {0x0000a310, 0x0000e012, 0x0000e012, 0x00010012, 0x00010012},
+ {0x0000a314, 0x00011014, 0x00011014, 0x00013014, 0x00013014},
+ {0x0000a318, 0x0001504a, 0x0001504a, 0x0001820a, 0x0001820a},
+ {0x0000a31c, 0x0001904c, 0x0001904c, 0x0001b211, 0x0001b211},
+ {0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213},
+ {0x0000a324, 0x00021092, 0x00021092, 0x00022411, 0x00022411},
+ {0x0000a328, 0x0002510a, 0x0002510a, 0x00025413, 0x00025413},
+ {0x0000a32c, 0x0002910c, 0x0002910c, 0x00029811, 0x00029811},
+ {0x0000a330, 0x0002c18b, 0x0002c18b, 0x0002c813, 0x0002c813},
+ {0x0000a334, 0x0002f1cc, 0x0002f1cc, 0x00030a14, 0x00030a14},
+ {0x0000a338, 0x000321eb, 0x000321eb, 0x00035a50, 0x00035a50},
+ {0x0000a33c, 0x000341ec, 0x000341ec, 0x00039c4c, 0x00039c4c},
+ {0x0000a340, 0x000341ec, 0x000341ec, 0x0003de8a, 0x0003de8a},
+ {0x0000a344, 0x000341ec, 0x000341ec, 0x00042e92, 0x00042e92},
+ {0x0000a348, 0x000341ec, 0x000341ec, 0x00046ed2, 0x00046ed2},
+ {0x0000a34c, 0x000341ec, 0x000341ec, 0x0004bed5, 0x0004bed5},
+ {0x0000a350, 0x000341ec, 0x000341ec, 0x0004ff54, 0x0004ff54},
+ {0x0000a354, 0x000341ec, 0x000341ec, 0x00055fd5, 0x00055fd5},
+ {0x0000a3ec, 0x00f70081, 0x00f70081, 0x00f70081, 0x00f70081},
+ {0x00007814, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff},
+ {0x00007838, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff},
+ {0x0000781c, 0x00172000, 0x00172000, 0x00172000, 0x00172000},
+ {0x00007840, 0x00172000, 0x00172000, 0x00172000, 0x00172000},
+ {0x00007820, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480},
+ {0x00007844, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480},
};
-static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = {
- {0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652},
- {0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce},
- {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a304, 0x00003002, 0x00003002, 0x00003002, 0x00003002, 0x00003002},
- {0x0000a308, 0x00006004, 0x00006004, 0x00008009, 0x00008009, 0x00008009},
- {0x0000a30c, 0x0000a006, 0x0000a006, 0x0000b00b, 0x0000b00b, 0x0000b00b},
- {0x0000a310, 0x0000e012, 0x0000e012, 0x0000e012, 0x0000e012, 0x0000e012},
- {0x0000a314, 0x00011014, 0x00011014, 0x00012048, 0x00012048, 0x00012048},
- {0x0000a318, 0x0001504a, 0x0001504a, 0x0001604a, 0x0001604a, 0x0001604a},
- {0x0000a31c, 0x0001904c, 0x0001904c, 0x0001a211, 0x0001a211, 0x0001a211},
- {0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213, 0x0001e213},
- {0x0000a324, 0x00020092, 0x00020092, 0x0002121b, 0x0002121b, 0x0002121b},
- {0x0000a328, 0x0002410a, 0x0002410a, 0x00024412, 0x00024412, 0x00024412},
- {0x0000a32c, 0x0002710c, 0x0002710c, 0x00028414, 0x00028414, 0x00028414},
- {0x0000a330, 0x0002b18b, 0x0002b18b, 0x0002b44a, 0x0002b44a, 0x0002b44a},
- {0x0000a334, 0x0002e1cc, 0x0002e1cc, 0x00030649, 0x00030649, 0x00030649},
- {0x0000a338, 0x000321ec, 0x000321ec, 0x0003364b, 0x0003364b, 0x0003364b},
- {0x0000a33c, 0x000321ec, 0x000321ec, 0x00038a49, 0x00038a49, 0x00038a49},
- {0x0000a340, 0x000321ec, 0x000321ec, 0x0003be48, 0x0003be48, 0x0003be48},
- {0x0000a344, 0x000321ec, 0x000321ec, 0x0003ee4a, 0x0003ee4a, 0x0003ee4a},
- {0x0000a348, 0x000321ec, 0x000321ec, 0x00042e88, 0x00042e88, 0x00042e88},
- {0x0000a34c, 0x000321ec, 0x000321ec, 0x00046e8a, 0x00046e8a, 0x00046e8a},
- {0x0000a350, 0x000321ec, 0x000321ec, 0x00049ec9, 0x00049ec9, 0x00049ec9},
- {0x0000a354, 0x000321ec, 0x000321ec, 0x0004bf42, 0x0004bf42, 0x0004bf42},
- {0x0000a3ec, 0x00f70081, 0x00f70081, 0x00f70081, 0x00f70081, 0x00f70081},
- {0x00007814, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff},
- {0x00007838, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff},
- {0x0000781c, 0x00392000, 0x00392000, 0x00392000, 0x00392000, 0x00392000},
- {0x00007840, 0x00392000, 0x00392000, 0x00392000, 0x00392000, 0x00392000},
- {0x00007820, 0x92592480, 0x92592480, 0x92592480, 0x92592480, 0x92592480},
- {0x00007844, 0x92592480, 0x92592480, 0x92592480, 0x92592480, 0x92592480},
+static const u32 ar9280Modes_original_tx_gain_9280_2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652},
+ {0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce},
+ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a304, 0x00003002, 0x00003002, 0x00003002, 0x00003002},
+ {0x0000a308, 0x00006004, 0x00006004, 0x00008009, 0x00008009},
+ {0x0000a30c, 0x0000a006, 0x0000a006, 0x0000b00b, 0x0000b00b},
+ {0x0000a310, 0x0000e012, 0x0000e012, 0x0000e012, 0x0000e012},
+ {0x0000a314, 0x00011014, 0x00011014, 0x00012048, 0x00012048},
+ {0x0000a318, 0x0001504a, 0x0001504a, 0x0001604a, 0x0001604a},
+ {0x0000a31c, 0x0001904c, 0x0001904c, 0x0001a211, 0x0001a211},
+ {0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213},
+ {0x0000a324, 0x00020092, 0x00020092, 0x0002121b, 0x0002121b},
+ {0x0000a328, 0x0002410a, 0x0002410a, 0x00024412, 0x00024412},
+ {0x0000a32c, 0x0002710c, 0x0002710c, 0x00028414, 0x00028414},
+ {0x0000a330, 0x0002b18b, 0x0002b18b, 0x0002b44a, 0x0002b44a},
+ {0x0000a334, 0x0002e1cc, 0x0002e1cc, 0x00030649, 0x00030649},
+ {0x0000a338, 0x000321ec, 0x000321ec, 0x0003364b, 0x0003364b},
+ {0x0000a33c, 0x000321ec, 0x000321ec, 0x00038a49, 0x00038a49},
+ {0x0000a340, 0x000321ec, 0x000321ec, 0x0003be48, 0x0003be48},
+ {0x0000a344, 0x000321ec, 0x000321ec, 0x0003ee4a, 0x0003ee4a},
+ {0x0000a348, 0x000321ec, 0x000321ec, 0x00042e88, 0x00042e88},
+ {0x0000a34c, 0x000321ec, 0x000321ec, 0x00046e8a, 0x00046e8a},
+ {0x0000a350, 0x000321ec, 0x000321ec, 0x00049ec9, 0x00049ec9},
+ {0x0000a354, 0x000321ec, 0x000321ec, 0x0004bf42, 0x0004bf42},
+ {0x0000a3ec, 0x00f70081, 0x00f70081, 0x00f70081, 0x00f70081},
+ {0x00007814, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff},
+ {0x00007838, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff},
+ {0x0000781c, 0x00392000, 0x00392000, 0x00392000, 0x00392000},
+ {0x00007840, 0x00392000, 0x00392000, 0x00392000, 0x00392000},
+ {0x00007820, 0x92592480, 0x92592480, 0x92592480, 0x92592480},
+ {0x00007844, 0x92592480, 0x92592480, 0x92592480, 0x92592480},
};
static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = {
@@ -947,309 +953,310 @@ static const u32 ar9285PciePhy_clkreq_off_L1_9285[][2] = {
{0x00004044, 0x00000000},
};
-static const u32 ar9285Modes_9285_1_2[][6] = {
- {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0},
- {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0},
- {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180},
- {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008},
- {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0},
- {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f},
- {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880},
- {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303},
- {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
- {0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
- {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
- {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
- {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
- {0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e},
- {0x00009844, 0x0372161e, 0x0372161e, 0x03721620, 0x03721620, 0x037216a0},
- {0x00009848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059},
- {0x0000a848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059},
- {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2},
- {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e},
- {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e},
- {0x00009860, 0x00058d18, 0x00058d18, 0x00058d20, 0x00058d20, 0x00058d18},
- {0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
- {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
- {0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881},
- {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0},
- {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016},
- {0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d},
- {0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1020, 0xffbc1020, 0xffbc1010},
- {0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x000099b8, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c},
- {0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00},
- {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
- {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77},
- {0x000099c8, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f},
- {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8},
- {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384},
- {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x00009a00, 0x00000000, 0x00000000, 0x00058084, 0x00058084, 0x00000000},
- {0x00009a04, 0x00000000, 0x00000000, 0x00058088, 0x00058088, 0x00000000},
- {0x00009a08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c, 0x00000000},
- {0x00009a0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100, 0x00000000},
- {0x00009a10, 0x00000000, 0x00000000, 0x00058104, 0x00058104, 0x00000000},
- {0x00009a14, 0x00000000, 0x00000000, 0x00058108, 0x00058108, 0x00000000},
- {0x00009a18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c, 0x00000000},
- {0x00009a1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110, 0x00000000},
- {0x00009a20, 0x00000000, 0x00000000, 0x00058114, 0x00058114, 0x00000000},
- {0x00009a24, 0x00000000, 0x00000000, 0x00058180, 0x00058180, 0x00000000},
- {0x00009a28, 0x00000000, 0x00000000, 0x00058184, 0x00058184, 0x00000000},
- {0x00009a2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188, 0x00000000},
- {0x00009a30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c, 0x00000000},
- {0x00009a34, 0x00000000, 0x00000000, 0x00058190, 0x00058190, 0x00000000},
- {0x00009a38, 0x00000000, 0x00000000, 0x00058194, 0x00058194, 0x00000000},
- {0x00009a3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0, 0x00000000},
- {0x00009a40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c, 0x00000000},
- {0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000},
- {0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000},
- {0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000},
- {0x00009a50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000},
- {0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000},
- {0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000},
- {0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000},
- {0x00009a60, 0x00000000, 0x00000000, 0x00058308, 0x00058308, 0x00000000},
- {0x00009a64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c, 0x00000000},
- {0x00009a68, 0x00000000, 0x00000000, 0x00058380, 0x00058380, 0x00000000},
- {0x00009a6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384, 0x00000000},
- {0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000},
- {0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000},
- {0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000},
- {0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000},
- {0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000},
- {0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000},
- {0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000},
- {0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000},
- {0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000},
- {0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000},
- {0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000},
- {0x00009a9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84, 0x00000000},
- {0x00009aa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88, 0x00000000},
- {0x00009aa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c, 0x00000000},
- {0x00009aa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90, 0x00000000},
- {0x00009aac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80, 0x00000000},
- {0x00009ab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84, 0x00000000},
- {0x00009ab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88, 0x00000000},
- {0x00009ab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c, 0x00000000},
- {0x00009abc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90, 0x00000000},
- {0x00009ac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c, 0x00000000},
- {0x00009ac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310, 0x00000000},
- {0x00009ac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384, 0x00000000},
- {0x00009acc, 0x00000000, 0x00000000, 0x000db388, 0x000db388, 0x00000000},
- {0x00009ad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324, 0x00000000},
- {0x00009ad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704, 0x00000000},
- {0x00009ad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4, 0x00000000},
- {0x00009adc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8, 0x00000000},
- {0x00009ae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710, 0x00000000},
- {0x00009ae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714, 0x00000000},
- {0x00009ae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720, 0x00000000},
- {0x00009aec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724, 0x00000000},
- {0x00009af0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728, 0x00000000},
- {0x00009af4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c, 0x00000000},
- {0x00009af8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0, 0x00000000},
- {0x00009afc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4, 0x00000000},
- {0x00009b00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8, 0x00000000},
- {0x00009b04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0, 0x00000000},
- {0x00009b08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4, 0x00000000},
- {0x00009b0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8, 0x00000000},
- {0x00009b10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5, 0x00000000},
- {0x00009b14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9, 0x00000000},
- {0x00009b18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad, 0x00000000},
- {0x00009b1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1, 0x00000000},
- {0x00009b20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5, 0x00000000},
- {0x00009b24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9, 0x00000000},
- {0x00009b28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5, 0x00000000},
- {0x00009b2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9, 0x00000000},
- {0x00009b30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1, 0x00000000},
- {0x00009b34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5, 0x00000000},
- {0x00009b38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9, 0x00000000},
- {0x00009b3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6, 0x00000000},
- {0x00009b40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca, 0x00000000},
- {0x00009b44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce, 0x00000000},
- {0x00009b48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2, 0x00000000},
- {0x00009b4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6, 0x00000000},
- {0x00009b50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3, 0x00000000},
- {0x00009b54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7, 0x00000000},
- {0x00009b58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb, 0x00000000},
- {0x00009b5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf, 0x00000000},
- {0x00009b60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7, 0x00000000},
- {0x00009b64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009ba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009ba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009ba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009be0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009be4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009be8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000aa00, 0x00000000, 0x00000000, 0x00058084, 0x00058084, 0x00000000},
- {0x0000aa04, 0x00000000, 0x00000000, 0x00058088, 0x00058088, 0x00000000},
- {0x0000aa08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c, 0x00000000},
- {0x0000aa0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100, 0x00000000},
- {0x0000aa10, 0x00000000, 0x00000000, 0x00058104, 0x00058104, 0x00000000},
- {0x0000aa14, 0x00000000, 0x00000000, 0x00058108, 0x00058108, 0x00000000},
- {0x0000aa18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c, 0x00000000},
- {0x0000aa1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110, 0x00000000},
- {0x0000aa20, 0x00000000, 0x00000000, 0x00058114, 0x00058114, 0x00000000},
- {0x0000aa24, 0x00000000, 0x00000000, 0x00058180, 0x00058180, 0x00000000},
- {0x0000aa28, 0x00000000, 0x00000000, 0x00058184, 0x00058184, 0x00000000},
- {0x0000aa2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188, 0x00000000},
- {0x0000aa30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c, 0x00000000},
- {0x0000aa34, 0x00000000, 0x00000000, 0x00058190, 0x00058190, 0x00000000},
- {0x0000aa38, 0x00000000, 0x00000000, 0x00058194, 0x00058194, 0x00000000},
- {0x0000aa3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0, 0x00000000},
- {0x0000aa40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c, 0x00000000},
- {0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000},
- {0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000},
- {0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000},
- {0x0000aa50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000},
- {0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000},
- {0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000},
- {0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000},
- {0x0000aa60, 0x00000000, 0x00000000, 0x00058308, 0x00058308, 0x00000000},
- {0x0000aa64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c, 0x00000000},
- {0x0000aa68, 0x00000000, 0x00000000, 0x00058380, 0x00058380, 0x00000000},
- {0x0000aa6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384, 0x00000000},
- {0x0000aa70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000},
- {0x0000aa74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000},
- {0x0000aa78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000},
- {0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000},
- {0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000},
- {0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000},
- {0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000},
- {0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000},
- {0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000},
- {0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000},
- {0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000},
- {0x0000aa9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84, 0x00000000},
- {0x0000aaa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88, 0x00000000},
- {0x0000aaa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c, 0x00000000},
- {0x0000aaa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90, 0x00000000},
- {0x0000aaac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80, 0x00000000},
- {0x0000aab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84, 0x00000000},
- {0x0000aab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88, 0x00000000},
- {0x0000aab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c, 0x00000000},
- {0x0000aabc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90, 0x00000000},
- {0x0000aac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c, 0x00000000},
- {0x0000aac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310, 0x00000000},
- {0x0000aac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384, 0x00000000},
- {0x0000aacc, 0x00000000, 0x00000000, 0x000db388, 0x000db388, 0x00000000},
- {0x0000aad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324, 0x00000000},
- {0x0000aad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704, 0x00000000},
- {0x0000aad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4, 0x00000000},
- {0x0000aadc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8, 0x00000000},
- {0x0000aae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710, 0x00000000},
- {0x0000aae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714, 0x00000000},
- {0x0000aae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720, 0x00000000},
- {0x0000aaec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724, 0x00000000},
- {0x0000aaf0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728, 0x00000000},
- {0x0000aaf4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c, 0x00000000},
- {0x0000aaf8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0, 0x00000000},
- {0x0000aafc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4, 0x00000000},
- {0x0000ab00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8, 0x00000000},
- {0x0000ab04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0, 0x00000000},
- {0x0000ab08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4, 0x00000000},
- {0x0000ab0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8, 0x00000000},
- {0x0000ab10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5, 0x00000000},
- {0x0000ab14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9, 0x00000000},
- {0x0000ab18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad, 0x00000000},
- {0x0000ab1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1, 0x00000000},
- {0x0000ab20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5, 0x00000000},
- {0x0000ab24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9, 0x00000000},
- {0x0000ab28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5, 0x00000000},
- {0x0000ab2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9, 0x00000000},
- {0x0000ab30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1, 0x00000000},
- {0x0000ab34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5, 0x00000000},
- {0x0000ab38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9, 0x00000000},
- {0x0000ab3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6, 0x00000000},
- {0x0000ab40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca, 0x00000000},
- {0x0000ab44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce, 0x00000000},
- {0x0000ab48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2, 0x00000000},
- {0x0000ab4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6, 0x00000000},
- {0x0000ab50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3, 0x00000000},
- {0x0000ab54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7, 0x00000000},
- {0x0000ab58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb, 0x00000000},
- {0x0000ab5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf, 0x00000000},
- {0x0000ab60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7, 0x00000000},
- {0x0000ab64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000aba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000aba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000aba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abe0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abe4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abe8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004},
- {0x0000a20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000},
- {0x0000b20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000},
- {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
- {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000},
- {0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000},
- {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e},
+static const u32 ar9285Modes_9285_1_2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b},
+ {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
+ {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300},
+ {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
+ {0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
+ {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
+ {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
+ {0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e},
+ {0x00009844, 0x0372161e, 0x0372161e, 0x03721620, 0x03721620},
+ {0x00009848, 0x00001066, 0x00001066, 0x00001053, 0x00001053},
+ {0x0000a848, 0x00001066, 0x00001066, 0x00001053, 0x00001053},
+ {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2},
+ {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e},
+ {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e},
+ {0x00009860, 0x00058d18, 0x00058d18, 0x00058d20, 0x00058d20},
+ {0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00},
+ {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+ {0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+ {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+ {0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d},
+ {0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1020, 0xffbc1020},
+ {0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x000099b8, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c},
+ {0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00},
+ {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+ {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77},
+ {0x000099c8, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f},
+ {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8},
+ {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384},
+ {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009a00, 0x00000000, 0x00000000, 0x00058084, 0x00058084},
+ {0x00009a04, 0x00000000, 0x00000000, 0x00058088, 0x00058088},
+ {0x00009a08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c},
+ {0x00009a0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100},
+ {0x00009a10, 0x00000000, 0x00000000, 0x00058104, 0x00058104},
+ {0x00009a14, 0x00000000, 0x00000000, 0x00058108, 0x00058108},
+ {0x00009a18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c},
+ {0x00009a1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110},
+ {0x00009a20, 0x00000000, 0x00000000, 0x00058114, 0x00058114},
+ {0x00009a24, 0x00000000, 0x00000000, 0x00058180, 0x00058180},
+ {0x00009a28, 0x00000000, 0x00000000, 0x00058184, 0x00058184},
+ {0x00009a2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188},
+ {0x00009a30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c},
+ {0x00009a34, 0x00000000, 0x00000000, 0x00058190, 0x00058190},
+ {0x00009a38, 0x00000000, 0x00000000, 0x00058194, 0x00058194},
+ {0x00009a3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0},
+ {0x00009a40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c},
+ {0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8},
+ {0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284},
+ {0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288},
+ {0x00009a50, 0x00000000, 0x00000000, 0x00058224, 0x00058224},
+ {0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290},
+ {0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300},
+ {0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304},
+ {0x00009a60, 0x00000000, 0x00000000, 0x00058308, 0x00058308},
+ {0x00009a64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c},
+ {0x00009a68, 0x00000000, 0x00000000, 0x00058380, 0x00058380},
+ {0x00009a6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384},
+ {0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700},
+ {0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704},
+ {0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708},
+ {0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c},
+ {0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780},
+ {0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784},
+ {0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00},
+ {0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04},
+ {0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08},
+ {0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c},
+ {0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80},
+ {0x00009a9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84},
+ {0x00009aa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88},
+ {0x00009aa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c},
+ {0x00009aa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90},
+ {0x00009aac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80},
+ {0x00009ab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84},
+ {0x00009ab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88},
+ {0x00009ab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c},
+ {0x00009abc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90},
+ {0x00009ac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c},
+ {0x00009ac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310},
+ {0x00009ac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384},
+ {0x00009acc, 0x00000000, 0x00000000, 0x000db388, 0x000db388},
+ {0x00009ad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324},
+ {0x00009ad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704},
+ {0x00009ad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4},
+ {0x00009adc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8},
+ {0x00009ae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710},
+ {0x00009ae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714},
+ {0x00009ae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720},
+ {0x00009aec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724},
+ {0x00009af0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728},
+ {0x00009af4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c},
+ {0x00009af8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0},
+ {0x00009afc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4},
+ {0x00009b00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8},
+ {0x00009b04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0},
+ {0x00009b08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4},
+ {0x00009b0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8},
+ {0x00009b10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5},
+ {0x00009b14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9},
+ {0x00009b18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad},
+ {0x00009b1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1},
+ {0x00009b20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5},
+ {0x00009b24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9},
+ {0x00009b28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5},
+ {0x00009b2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9},
+ {0x00009b30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1},
+ {0x00009b34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5},
+ {0x00009b38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9},
+ {0x00009b3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6},
+ {0x00009b40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca},
+ {0x00009b44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce},
+ {0x00009b48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2},
+ {0x00009b4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6},
+ {0x00009b50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3},
+ {0x00009b54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7},
+ {0x00009b58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb},
+ {0x00009b5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf},
+ {0x00009b60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7},
+ {0x00009b64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009ba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009ba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009ba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009be0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009be4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009be8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000aa00, 0x00000000, 0x00000000, 0x00058084, 0x00058084},
+ {0x0000aa04, 0x00000000, 0x00000000, 0x00058088, 0x00058088},
+ {0x0000aa08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c},
+ {0x0000aa0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100},
+ {0x0000aa10, 0x00000000, 0x00000000, 0x00058104, 0x00058104},
+ {0x0000aa14, 0x00000000, 0x00000000, 0x00058108, 0x00058108},
+ {0x0000aa18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c},
+ {0x0000aa1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110},
+ {0x0000aa20, 0x00000000, 0x00000000, 0x00058114, 0x00058114},
+ {0x0000aa24, 0x00000000, 0x00000000, 0x00058180, 0x00058180},
+ {0x0000aa28, 0x00000000, 0x00000000, 0x00058184, 0x00058184},
+ {0x0000aa2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188},
+ {0x0000aa30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c},
+ {0x0000aa34, 0x00000000, 0x00000000, 0x00058190, 0x00058190},
+ {0x0000aa38, 0x00000000, 0x00000000, 0x00058194, 0x00058194},
+ {0x0000aa3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0},
+ {0x0000aa40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c},
+ {0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8},
+ {0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284},
+ {0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288},
+ {0x0000aa50, 0x00000000, 0x00000000, 0x00058224, 0x00058224},
+ {0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290},
+ {0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300},
+ {0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304},
+ {0x0000aa60, 0x00000000, 0x00000000, 0x00058308, 0x00058308},
+ {0x0000aa64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c},
+ {0x0000aa68, 0x00000000, 0x00000000, 0x00058380, 0x00058380},
+ {0x0000aa6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384},
+ {0x0000aa70, 0x00000000, 0x00000000, 0x00068700, 0x00068700},
+ {0x0000aa74, 0x00000000, 0x00000000, 0x00068704, 0x00068704},
+ {0x0000aa78, 0x00000000, 0x00000000, 0x00068708, 0x00068708},
+ {0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c},
+ {0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780},
+ {0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784},
+ {0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00},
+ {0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04},
+ {0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08},
+ {0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c},
+ {0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80},
+ {0x0000aa9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84},
+ {0x0000aaa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88},
+ {0x0000aaa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c},
+ {0x0000aaa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90},
+ {0x0000aaac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80},
+ {0x0000aab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84},
+ {0x0000aab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88},
+ {0x0000aab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c},
+ {0x0000aabc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90},
+ {0x0000aac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c},
+ {0x0000aac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310},
+ {0x0000aac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384},
+ {0x0000aacc, 0x00000000, 0x00000000, 0x000db388, 0x000db388},
+ {0x0000aad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324},
+ {0x0000aad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704},
+ {0x0000aad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4},
+ {0x0000aadc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8},
+ {0x0000aae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710},
+ {0x0000aae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714},
+ {0x0000aae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720},
+ {0x0000aaec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724},
+ {0x0000aaf0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728},
+ {0x0000aaf4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c},
+ {0x0000aaf8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0},
+ {0x0000aafc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4},
+ {0x0000ab00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8},
+ {0x0000ab04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0},
+ {0x0000ab08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4},
+ {0x0000ab0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8},
+ {0x0000ab10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5},
+ {0x0000ab14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9},
+ {0x0000ab18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad},
+ {0x0000ab1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1},
+ {0x0000ab20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5},
+ {0x0000ab24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9},
+ {0x0000ab28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5},
+ {0x0000ab2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9},
+ {0x0000ab30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1},
+ {0x0000ab34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5},
+ {0x0000ab38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9},
+ {0x0000ab3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6},
+ {0x0000ab40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca},
+ {0x0000ab44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce},
+ {0x0000ab48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2},
+ {0x0000ab4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6},
+ {0x0000ab50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3},
+ {0x0000ab54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7},
+ {0x0000ab58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb},
+ {0x0000ab5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf},
+ {0x0000ab60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7},
+ {0x0000ab64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000aba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000aba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000aba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abe0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abe4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abe8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004},
+ {0x0000a20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000},
+ {0x0000b20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000},
+ {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
+ {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000},
+ {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e},
};
static const u32 ar9285Common_9285_1_2[][2] = {
@@ -1572,164 +1579,168 @@ static const u32 ar9285Common_9285_1_2[][2] = {
{0x00007870, 0x10142c00},
};
-static const u32 ar9285Modes_high_power_tx_gain_9285_1_2[][6] = {
- {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200, 0x00000000},
- {0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201, 0x00000000},
- {0x0000a30c, 0x00000000, 0x00000000, 0x0000b240, 0x0000b240, 0x00000000},
- {0x0000a310, 0x00000000, 0x00000000, 0x0000d241, 0x0000d241, 0x00000000},
- {0x0000a314, 0x00000000, 0x00000000, 0x0000f600, 0x0000f600, 0x00000000},
- {0x0000a318, 0x00000000, 0x00000000, 0x00012800, 0x00012800, 0x00000000},
- {0x0000a31c, 0x00000000, 0x00000000, 0x00016802, 0x00016802, 0x00000000},
- {0x0000a320, 0x00000000, 0x00000000, 0x0001b805, 0x0001b805, 0x00000000},
- {0x0000a324, 0x00000000, 0x00000000, 0x00021a80, 0x00021a80, 0x00000000},
- {0x0000a328, 0x00000000, 0x00000000, 0x00028b00, 0x00028b00, 0x00000000},
- {0x0000a32c, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40, 0x00000000},
- {0x0000a330, 0x00000000, 0x00000000, 0x0002cd80, 0x0002cd80, 0x00000000},
- {0x0000a334, 0x00000000, 0x00000000, 0x00033d82, 0x00033d82, 0x00000000},
- {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000},
- {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000},
- {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x00007814, 0x924934a8, 0x924934a8, 0x924934a8, 0x924934a8, 0x924934a8},
- {0x00007828, 0x26d2491b, 0x26d2491b, 0x26d2491b, 0x26d2491b, 0x26d2491b},
- {0x00007830, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e},
- {0x00007838, 0xfac68803, 0xfac68803, 0xfac68803, 0xfac68803, 0xfac68803},
- {0x0000783c, 0x0001fffe, 0x0001fffe, 0x0001fffe, 0x0001fffe, 0x0001fffe},
- {0x00007840, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20},
- {0x0000786c, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe},
- {0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00},
- {0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a216652, 0x0a216652, 0x0a22a652},
- {0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
- {0x0000a27c, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7},
- {0x0000a394, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
- {0x0000a398, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7},
- {0x0000a3dc, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
- {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7},
+static const u32 ar9285Modes_high_power_tx_gain_9285_1_2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200},
+ {0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201},
+ {0x0000a30c, 0x00000000, 0x00000000, 0x0000b240, 0x0000b240},
+ {0x0000a310, 0x00000000, 0x00000000, 0x0000d241, 0x0000d241},
+ {0x0000a314, 0x00000000, 0x00000000, 0x0000f600, 0x0000f600},
+ {0x0000a318, 0x00000000, 0x00000000, 0x00012800, 0x00012800},
+ {0x0000a31c, 0x00000000, 0x00000000, 0x00016802, 0x00016802},
+ {0x0000a320, 0x00000000, 0x00000000, 0x0001b805, 0x0001b805},
+ {0x0000a324, 0x00000000, 0x00000000, 0x00021a80, 0x00021a80},
+ {0x0000a328, 0x00000000, 0x00000000, 0x00028b00, 0x00028b00},
+ {0x0000a32c, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40},
+ {0x0000a330, 0x00000000, 0x00000000, 0x0002cd80, 0x0002cd80},
+ {0x0000a334, 0x00000000, 0x00000000, 0x00033d82, 0x00033d82},
+ {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e},
+ {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e},
+ {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x00007814, 0x924934a8, 0x924934a8, 0x924934a8, 0x924934a8},
+ {0x00007828, 0x26d2491b, 0x26d2491b, 0x26d2491b, 0x26d2491b},
+ {0x00007830, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e},
+ {0x00007838, 0xfac68803, 0xfac68803, 0xfac68803, 0xfac68803},
+ {0x0000783c, 0x0001fffe, 0x0001fffe, 0x0001fffe, 0x0001fffe},
+ {0x00007840, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20},
+ {0x0000786c, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe},
+ {0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00},
+ {0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a216652, 0x0a216652},
+ {0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
+ {0x0000a27c, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7},
+ {0x0000a394, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
+ {0x0000a398, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7},
+ {0x0000a3dc, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
+ {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7},
};
-static const u32 ar9285Modes_original_tx_gain_9285_1_2[][6] = {
- {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000},
- {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000},
- {0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000},
- {0x0000a310, 0x00000000, 0x00000000, 0x00022618, 0x00022618, 0x00000000},
- {0x0000a314, 0x00000000, 0x00000000, 0x0002a6c9, 0x0002a6c9, 0x00000000},
- {0x0000a318, 0x00000000, 0x00000000, 0x00031710, 0x00031710, 0x00000000},
- {0x0000a31c, 0x00000000, 0x00000000, 0x00035718, 0x00035718, 0x00000000},
- {0x0000a320, 0x00000000, 0x00000000, 0x00038758, 0x00038758, 0x00000000},
- {0x0000a324, 0x00000000, 0x00000000, 0x0003c75a, 0x0003c75a, 0x00000000},
- {0x0000a328, 0x00000000, 0x00000000, 0x0004075c, 0x0004075c, 0x00000000},
- {0x0000a32c, 0x00000000, 0x00000000, 0x0004475e, 0x0004475e, 0x00000000},
- {0x0000a330, 0x00000000, 0x00000000, 0x0004679f, 0x0004679f, 0x00000000},
- {0x0000a334, 0x00000000, 0x00000000, 0x000487df, 0x000487df, 0x00000000},
- {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000},
- {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000},
- {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x00007814, 0x924934a8, 0x924934a8, 0x924934a8, 0x924934a8, 0x924934a8},
- {0x00007828, 0x26d2491b, 0x26d2491b, 0x26d2491b, 0x26d2491b, 0x26d2491b},
- {0x00007830, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e},
- {0x00007838, 0xfac68801, 0xfac68801, 0xfac68801, 0xfac68801, 0xfac68801},
- {0x0000783c, 0x0001fffe, 0x0001fffe, 0x0001fffe, 0x0001fffe, 0x0001fffe},
- {0x00007840, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20},
- {0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4},
- {0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04},
- {0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a21a652, 0x0a21a652, 0x0a22a652},
- {0x0000a278, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
- {0x0000a27c, 0x050e039c, 0x050e039c, 0x050e039c, 0x050e039c, 0x050e039c},
- {0x0000a394, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
- {0x0000a398, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c},
- {0x0000a3dc, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
- {0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c},
+static const u32 ar9285Modes_original_tx_gain_9285_1_2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200},
+ {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208},
+ {0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608},
+ {0x0000a310, 0x00000000, 0x00000000, 0x00022618, 0x00022618},
+ {0x0000a314, 0x00000000, 0x00000000, 0x0002a6c9, 0x0002a6c9},
+ {0x0000a318, 0x00000000, 0x00000000, 0x00031710, 0x00031710},
+ {0x0000a31c, 0x00000000, 0x00000000, 0x00035718, 0x00035718},
+ {0x0000a320, 0x00000000, 0x00000000, 0x00038758, 0x00038758},
+ {0x0000a324, 0x00000000, 0x00000000, 0x0003c75a, 0x0003c75a},
+ {0x0000a328, 0x00000000, 0x00000000, 0x0004075c, 0x0004075c},
+ {0x0000a32c, 0x00000000, 0x00000000, 0x0004475e, 0x0004475e},
+ {0x0000a330, 0x00000000, 0x00000000, 0x0004679f, 0x0004679f},
+ {0x0000a334, 0x00000000, 0x00000000, 0x000487df, 0x000487df},
+ {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e},
+ {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e},
+ {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x00007814, 0x924934a8, 0x924934a8, 0x924934a8, 0x924934a8},
+ {0x00007828, 0x26d2491b, 0x26d2491b, 0x26d2491b, 0x26d2491b},
+ {0x00007830, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e},
+ {0x00007838, 0xfac68801, 0xfac68801, 0xfac68801, 0xfac68801},
+ {0x0000783c, 0x0001fffe, 0x0001fffe, 0x0001fffe, 0x0001fffe},
+ {0x00007840, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20},
+ {0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4},
+ {0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04},
+ {0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a21a652, 0x0a21a652},
+ {0x0000a278, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
+ {0x0000a27c, 0x050e039c, 0x050e039c, 0x050e039c, 0x050e039c},
+ {0x0000a394, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
+ {0x0000a398, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c},
+ {0x0000a3dc, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
+ {0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c},
};
-static const u32 ar9285Modes_XE2_0_normal_power[][6] = {
- {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000},
- {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000},
- {0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000},
- {0x0000a310, 0x00000000, 0x00000000, 0x00022618, 0x00022618, 0x00000000},
- {0x0000a314, 0x00000000, 0x00000000, 0x0002a6c9, 0x0002a6c9, 0x00000000},
- {0x0000a318, 0x00000000, 0x00000000, 0x00031710, 0x00031710, 0x00000000},
- {0x0000a31c, 0x00000000, 0x00000000, 0x00035718, 0x00035718, 0x00000000},
- {0x0000a320, 0x00000000, 0x00000000, 0x00038758, 0x00038758, 0x00000000},
- {0x0000a324, 0x00000000, 0x00000000, 0x0003c75a, 0x0003c75a, 0x00000000},
- {0x0000a328, 0x00000000, 0x00000000, 0x0004075c, 0x0004075c, 0x00000000},
- {0x0000a32c, 0x00000000, 0x00000000, 0x0004475e, 0x0004475e, 0x00000000},
- {0x0000a330, 0x00000000, 0x00000000, 0x0004679f, 0x0004679f, 0x00000000},
- {0x0000a334, 0x00000000, 0x00000000, 0x000487df, 0x000487df, 0x00000000},
- {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000},
- {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000},
- {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x00007814, 0x92497ca8, 0x92497ca8, 0x92497ca8, 0x92497ca8, 0x92497ca8},
- {0x00007828, 0x4ad2491b, 0x4ad2491b, 0x2ad2491b, 0x4ad2491b, 0x4ad2491b},
- {0x00007830, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e, 0xedb6dbae},
- {0x00007838, 0xdac71441, 0xdac71441, 0xdac71441, 0xdac71441, 0xdac71441},
- {0x0000783c, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe},
- {0x00007840, 0xba5f638c, 0xba5f638c, 0xba5f638c, 0xba5f638c, 0xba5f638c},
- {0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4},
- {0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04},
- {0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a21a652, 0x0a21a652, 0x0a22a652},
- {0x0000a278, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
- {0x0000a27c, 0x050e039c, 0x050e039c, 0x050e039c, 0x050e039c, 0x050e039c},
- {0x0000a394, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
- {0x0000a398, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c},
- {0x0000a3dc, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
- {0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c},
+static const u32 ar9285Modes_XE2_0_normal_power[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200},
+ {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208},
+ {0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608},
+ {0x0000a310, 0x00000000, 0x00000000, 0x00022618, 0x00022618},
+ {0x0000a314, 0x00000000, 0x00000000, 0x0002a6c9, 0x0002a6c9},
+ {0x0000a318, 0x00000000, 0x00000000, 0x00031710, 0x00031710},
+ {0x0000a31c, 0x00000000, 0x00000000, 0x00035718, 0x00035718},
+ {0x0000a320, 0x00000000, 0x00000000, 0x00038758, 0x00038758},
+ {0x0000a324, 0x00000000, 0x00000000, 0x0003c75a, 0x0003c75a},
+ {0x0000a328, 0x00000000, 0x00000000, 0x0004075c, 0x0004075c},
+ {0x0000a32c, 0x00000000, 0x00000000, 0x0004475e, 0x0004475e},
+ {0x0000a330, 0x00000000, 0x00000000, 0x0004679f, 0x0004679f},
+ {0x0000a334, 0x00000000, 0x00000000, 0x000487df, 0x000487df},
+ {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e},
+ {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e},
+ {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x00007814, 0x92497ca8, 0x92497ca8, 0x92497ca8, 0x92497ca8},
+ {0x00007828, 0x4ad2491b, 0x4ad2491b, 0x2ad2491b, 0x4ad2491b},
+ {0x00007830, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e},
+ {0x00007838, 0xdac71441, 0xdac71441, 0xdac71441, 0xdac71441},
+ {0x0000783c, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe},
+ {0x00007840, 0xba5f638c, 0xba5f638c, 0xba5f638c, 0xba5f638c},
+ {0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4},
+ {0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04},
+ {0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a21a652, 0x0a21a652},
+ {0x0000a278, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
+ {0x0000a27c, 0x050e039c, 0x050e039c, 0x050e039c, 0x050e039c},
+ {0x0000a394, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
+ {0x0000a398, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c},
+ {0x0000a3dc, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c},
+ {0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c},
};
-static const u32 ar9285Modes_XE2_0_high_power[][6] = {
- {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200, 0x00000000},
- {0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201, 0x00000000},
- {0x0000a30c, 0x00000000, 0x00000000, 0x0000b240, 0x0000b240, 0x00000000},
- {0x0000a310, 0x00000000, 0x00000000, 0x0000d241, 0x0000d241, 0x00000000},
- {0x0000a314, 0x00000000, 0x00000000, 0x0000f600, 0x0000f600, 0x00000000},
- {0x0000a318, 0x00000000, 0x00000000, 0x00012800, 0x00012800, 0x00000000},
- {0x0000a31c, 0x00000000, 0x00000000, 0x00016802, 0x00016802, 0x00000000},
- {0x0000a320, 0x00000000, 0x00000000, 0x0001b805, 0x0001b805, 0x00000000},
- {0x0000a324, 0x00000000, 0x00000000, 0x00021a80, 0x00021a80, 0x00000000},
- {0x0000a328, 0x00000000, 0x00000000, 0x00028b00, 0x00028b00, 0x00000000},
- {0x0000a32c, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40, 0x00000000},
- {0x0000a330, 0x00000000, 0x00000000, 0x0002cd80, 0x0002cd80, 0x00000000},
- {0x0000a334, 0x00000000, 0x00000000, 0x00033d82, 0x00033d82, 0x00000000},
- {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000},
- {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000},
- {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x00007814, 0x92497ca8, 0x92497ca8, 0x92497ca8, 0x92497ca8, 0x92497ca8},
- {0x00007828, 0x4ad2491b, 0x4ad2491b, 0x2ad2491b, 0x4ad2491b, 0x4ad2491b},
- {0x00007830, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e},
- {0x00007838, 0xdac71443, 0xdac71443, 0xdac71443, 0xdac71443, 0xdac71443},
- {0x0000783c, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe},
- {0x00007840, 0xba5f638c, 0xba5f638c, 0xba5f638c, 0xba5f638c, 0xba5f638c},
- {0x0000786c, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe},
- {0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00},
- {0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a216652, 0x0a216652, 0x0a22a652},
- {0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
- {0x0000a27c, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7},
- {0x0000a394, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
- {0x0000a398, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7},
- {0x0000a3dc, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
- {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7},
+static const u32 ar9285Modes_XE2_0_high_power[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200},
+ {0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201},
+ {0x0000a30c, 0x00000000, 0x00000000, 0x0000b240, 0x0000b240},
+ {0x0000a310, 0x00000000, 0x00000000, 0x0000d241, 0x0000d241},
+ {0x0000a314, 0x00000000, 0x00000000, 0x0000f600, 0x0000f600},
+ {0x0000a318, 0x00000000, 0x00000000, 0x00012800, 0x00012800},
+ {0x0000a31c, 0x00000000, 0x00000000, 0x00016802, 0x00016802},
+ {0x0000a320, 0x00000000, 0x00000000, 0x0001b805, 0x0001b805},
+ {0x0000a324, 0x00000000, 0x00000000, 0x00021a80, 0x00021a80},
+ {0x0000a328, 0x00000000, 0x00000000, 0x00028b00, 0x00028b00},
+ {0x0000a32c, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40},
+ {0x0000a330, 0x00000000, 0x00000000, 0x0002cd80, 0x0002cd80},
+ {0x0000a334, 0x00000000, 0x00000000, 0x00033d82, 0x00033d82},
+ {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e},
+ {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e},
+ {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x00007814, 0x92497ca8, 0x92497ca8, 0x92497ca8, 0x92497ca8},
+ {0x00007828, 0x4ad2491b, 0x4ad2491b, 0x2ad2491b, 0x4ad2491b},
+ {0x00007830, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e},
+ {0x00007838, 0xdac71443, 0xdac71443, 0xdac71443, 0xdac71443},
+ {0x0000783c, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe},
+ {0x00007840, 0xba5f638c, 0xba5f638c, 0xba5f638c, 0xba5f638c},
+ {0x0000786c, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe},
+ {0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00},
+ {0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a216652, 0x0a216652},
+ {0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
+ {0x0000a27c, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7},
+ {0x0000a394, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
+ {0x0000a398, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7},
+ {0x0000a3dc, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
+ {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7},
};
static const u32 ar9285PciePhy_clkreq_always_on_L1_9285_1_2[][2] = {
@@ -1760,50 +1771,51 @@ static const u32 ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = {
{0x00004044, 0x00000000},
};
-static const u32 ar9287Modes_9287_1_1[][6] = {
- {0x00001030, 0x00000000, 0x00000000, 0x000002c0, 0x00000160, 0x000001e0},
- {0x00001070, 0x00000000, 0x00000000, 0x00000318, 0x0000018c, 0x000001e0},
- {0x000010b0, 0x00000000, 0x00000000, 0x00007c70, 0x00003e38, 0x00001180},
- {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008},
- {0x00008014, 0x00000000, 0x00000000, 0x10801600, 0x08400b00, 0x06e006e0},
- {0x0000801c, 0x00000000, 0x00000000, 0x12e00057, 0x12e0002b, 0x0988004f},
- {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810, 0x08f04810},
- {0x000081d0, 0x00003200, 0x00003200, 0x0000320a, 0x0000320a, 0x0000320a},
- {0x00008318, 0x00000000, 0x00000000, 0x00006880, 0x00003440, 0x00006880},
- {0x00009804, 0x00000000, 0x00000000, 0x000003c4, 0x00000300, 0x00000303},
- {0x00009820, 0x00000000, 0x00000000, 0x02020200, 0x02020200, 0x02020200},
- {0x00009824, 0x00000000, 0x00000000, 0x01000e0e, 0x01000e0e, 0x01000e0e},
- {0x00009828, 0x00000000, 0x00000000, 0x3a020001, 0x3a020001, 0x3a020001},
- {0x00009834, 0x00000000, 0x00000000, 0x00000e0e, 0x00000e0e, 0x00000e0e},
- {0x00009838, 0x00000003, 0x00000003, 0x00000007, 0x00000007, 0x00000007},
- {0x00009840, 0x206a002e, 0x206a002e, 0x206a012e, 0x206a012e, 0x206a012e},
- {0x00009844, 0x03720000, 0x03720000, 0x037216a0, 0x037216a0, 0x037216a0},
- {0x00009850, 0x60000000, 0x60000000, 0x6d4000e2, 0x6c4000e2, 0x6c4000e2},
- {0x00009858, 0x7c000d00, 0x7c000d00, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e},
- {0x0000985c, 0x3100005e, 0x3100005e, 0x3139605e, 0x31395d5e, 0x31395d5e},
- {0x00009860, 0x00058d00, 0x00058d00, 0x00058d20, 0x00058d20, 0x00058d18},
- {0x00009864, 0x00000e00, 0x00000e00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
- {0x00009868, 0x000040c0, 0x000040c0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
- {0x0000986c, 0x00000080, 0x00000080, 0x06903881, 0x06903881, 0x06903881},
- {0x00009914, 0x00000000, 0x00000000, 0x00001130, 0x00000898, 0x000007d0},
- {0x00009918, 0x00000000, 0x00000000, 0x00000016, 0x0000000b, 0x00000016},
- {0x00009924, 0xd00a8a01, 0xd00a8a01, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d},
- {0x00009944, 0xefbc0000, 0xefbc0000, 0xefbc1010, 0xefbc1010, 0xefbc1010},
- {0x00009960, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000010},
- {0x0000a960, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000010},
- {0x00009964, 0x00000000, 0x00000000, 0x00000210, 0x00000210, 0x00000210},
- {0x0000c968, 0x00000200, 0x00000200, 0x000003ce, 0x000003ce, 0x000003ce},
- {0x000099b8, 0x00000000, 0x00000000, 0x0000001c, 0x0000001c, 0x0000001c},
- {0x000099bc, 0x00000000, 0x00000000, 0x00000c00, 0x00000c00, 0x00000c00},
- {0x000099c0, 0x00000000, 0x00000000, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
- {0x0000a204, 0x00000440, 0x00000440, 0x00000444, 0x00000444, 0x00000444},
- {0x0000a20c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000b20c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a21c, 0x1803800a, 0x1803800a, 0x1883800a, 0x1883800a, 0x1883800a},
- {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000},
- {0x0000a250, 0x00000000, 0x00000000, 0x0004a000, 0x0004a000, 0x0004a000},
- {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e},
- {0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+static const u32 ar9287Modes_9287_1_1[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000000, 0x00000000, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000000, 0x00000000, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000000, 0x00000000, 0x00007c70, 0x00003e38},
+ {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00008014, 0x00000000, 0x00000000, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x00000000, 0x00000000, 0x12e00057, 0x12e0002b},
+ {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+ {0x000081d0, 0x00003200, 0x00003200, 0x0000320a, 0x0000320a},
+ {0x00008318, 0x00000000, 0x00000000, 0x00006880, 0x00003440},
+ {0x00009804, 0x00000000, 0x00000000, 0x000003c4, 0x00000300},
+ {0x00009820, 0x00000000, 0x00000000, 0x02020200, 0x02020200},
+ {0x00009824, 0x00000000, 0x00000000, 0x01000e0e, 0x01000e0e},
+ {0x00009828, 0x00000000, 0x00000000, 0x3a020001, 0x3a020001},
+ {0x00009834, 0x00000000, 0x00000000, 0x00000e0e, 0x00000e0e},
+ {0x00009838, 0x00000003, 0x00000003, 0x00000007, 0x00000007},
+ {0x00009840, 0x206a002e, 0x206a002e, 0x206a012e, 0x206a012e},
+ {0x00009844, 0x03720000, 0x03720000, 0x037216a0, 0x037216a0},
+ {0x00009850, 0x60000000, 0x60000000, 0x6d4000e2, 0x6c4000e2},
+ {0x00009858, 0x7c000d00, 0x7c000d00, 0x7ec84d2e, 0x7ec84d2e},
+ {0x0000985c, 0x3100005e, 0x3100005e, 0x3139605e, 0x31395d5e},
+ {0x00009860, 0x00058d00, 0x00058d00, 0x00058d20, 0x00058d20},
+ {0x00009864, 0x00000e00, 0x00000e00, 0x0001ce00, 0x0001ce00},
+ {0x00009868, 0x000040c0, 0x000040c0, 0x5ac640d0, 0x5ac640d0},
+ {0x0000986c, 0x00000080, 0x00000080, 0x06903881, 0x06903881},
+ {0x00009914, 0x00000000, 0x00000000, 0x00001130, 0x00000898},
+ {0x00009918, 0x00000000, 0x00000000, 0x00000016, 0x0000000b},
+ {0x00009924, 0xd00a8a01, 0xd00a8a01, 0xd00a8a0d, 0xd00a8a0d},
+ {0x00009944, 0xefbc0000, 0xefbc0000, 0xefbc1010, 0xefbc1010},
+ {0x00009960, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+ {0x0000a960, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+ {0x00009964, 0x00000000, 0x00000000, 0x00000210, 0x00000210},
+ {0x0000c968, 0x00000200, 0x00000200, 0x000003ce, 0x000003ce},
+ {0x000099b8, 0x00000000, 0x00000000, 0x0000001c, 0x0000001c},
+ {0x000099bc, 0x00000000, 0x00000000, 0x00000c00, 0x00000c00},
+ {0x000099c0, 0x00000000, 0x00000000, 0x05eea6d4, 0x05eea6d4},
+ {0x0000a204, 0x00000440, 0x00000440, 0x00000444, 0x00000444},
+ {0x0000a20c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000b20c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a21c, 0x1803800a, 0x1803800a, 0x1883800a, 0x1883800a},
+ {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a250, 0x00000000, 0x00000000, 0x0004a000, 0x0004a000},
+ {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e},
+ {0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
};
static const u32 ar9287Common_9287_1_1[][2] = {
@@ -2189,313 +2201,315 @@ static const u32 ar9287Common_japan_2484_cck_fir_coeff_9287_1_1[][2] = {
{0x0000a1fc, 0xca9228ee},
};
-static const u32 ar9287Modes_tx_gain_9287_1_1[][6] = {
- {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a304, 0x00000000, 0x00000000, 0x00004002, 0x00004002, 0x00004002},
- {0x0000a308, 0x00000000, 0x00000000, 0x00008004, 0x00008004, 0x00008004},
- {0x0000a30c, 0x00000000, 0x00000000, 0x0000c00a, 0x0000c00a, 0x0000c00a},
- {0x0000a310, 0x00000000, 0x00000000, 0x0001000c, 0x0001000c, 0x0001000c},
- {0x0000a314, 0x00000000, 0x00000000, 0x0001420b, 0x0001420b, 0x0001420b},
- {0x0000a318, 0x00000000, 0x00000000, 0x0001824a, 0x0001824a, 0x0001824a},
- {0x0000a31c, 0x00000000, 0x00000000, 0x0001c44a, 0x0001c44a, 0x0001c44a},
- {0x0000a320, 0x00000000, 0x00000000, 0x0002064a, 0x0002064a, 0x0002064a},
- {0x0000a324, 0x00000000, 0x00000000, 0x0002484a, 0x0002484a, 0x0002484a},
- {0x0000a328, 0x00000000, 0x00000000, 0x00028a4a, 0x00028a4a, 0x00028a4a},
- {0x0000a32c, 0x00000000, 0x00000000, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a},
- {0x0000a330, 0x00000000, 0x00000000, 0x00030e4a, 0x00030e4a, 0x00030e4a},
- {0x0000a334, 0x00000000, 0x00000000, 0x00034e8a, 0x00034e8a, 0x00034e8a},
- {0x0000a338, 0x00000000, 0x00000000, 0x00038e8c, 0x00038e8c, 0x00038e8c},
- {0x0000a33c, 0x00000000, 0x00000000, 0x0003cecc, 0x0003cecc, 0x0003cecc},
- {0x0000a340, 0x00000000, 0x00000000, 0x00040ed4, 0x00040ed4, 0x00040ed4},
- {0x0000a344, 0x00000000, 0x00000000, 0x00044edc, 0x00044edc, 0x00044edc},
- {0x0000a348, 0x00000000, 0x00000000, 0x00048ede, 0x00048ede, 0x00048ede},
- {0x0000a34c, 0x00000000, 0x00000000, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e},
- {0x0000a350, 0x00000000, 0x00000000, 0x00050f5e, 0x00050f5e, 0x00050f5e},
- {0x0000a354, 0x00000000, 0x00000000, 0x00054f9e, 0x00054f9e, 0x00054f9e},
- {0x0000a780, 0x00000000, 0x00000000, 0x00000062, 0x00000062, 0x00000062},
- {0x0000a784, 0x00000000, 0x00000000, 0x00004064, 0x00004064, 0x00004064},
- {0x0000a788, 0x00000000, 0x00000000, 0x000080a4, 0x000080a4, 0x000080a4},
- {0x0000a78c, 0x00000000, 0x00000000, 0x0000c0aa, 0x0000c0aa, 0x0000c0aa},
- {0x0000a790, 0x00000000, 0x00000000, 0x000100ac, 0x000100ac, 0x000100ac},
- {0x0000a794, 0x00000000, 0x00000000, 0x000140b4, 0x000140b4, 0x000140b4},
- {0x0000a798, 0x00000000, 0x00000000, 0x000180f4, 0x000180f4, 0x000180f4},
- {0x0000a79c, 0x00000000, 0x00000000, 0x0001c134, 0x0001c134, 0x0001c134},
- {0x0000a7a0, 0x00000000, 0x00000000, 0x00020174, 0x00020174, 0x00020174},
- {0x0000a7a4, 0x00000000, 0x00000000, 0x0002417c, 0x0002417c, 0x0002417c},
- {0x0000a7a8, 0x00000000, 0x00000000, 0x0002817e, 0x0002817e, 0x0002817e},
- {0x0000a7ac, 0x00000000, 0x00000000, 0x0002c1be, 0x0002c1be, 0x0002c1be},
- {0x0000a7b0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe},
- {0x0000a7b4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe},
- {0x0000a7b8, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe},
- {0x0000a7bc, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe},
- {0x0000a7c0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe},
- {0x0000a7c4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe},
- {0x0000a7c8, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe},
- {0x0000a7cc, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe},
- {0x0000a7d0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe},
- {0x0000a7d4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe},
- {0x0000a274, 0x0a180000, 0x0a180000, 0x0a1aa000, 0x0a1aa000, 0x0a1aa000},
+static const u32 ar9287Modes_tx_gain_9287_1_1[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a304, 0x00000000, 0x00000000, 0x00004002, 0x00004002},
+ {0x0000a308, 0x00000000, 0x00000000, 0x00008004, 0x00008004},
+ {0x0000a30c, 0x00000000, 0x00000000, 0x0000c00a, 0x0000c00a},
+ {0x0000a310, 0x00000000, 0x00000000, 0x0001000c, 0x0001000c},
+ {0x0000a314, 0x00000000, 0x00000000, 0x0001420b, 0x0001420b},
+ {0x0000a318, 0x00000000, 0x00000000, 0x0001824a, 0x0001824a},
+ {0x0000a31c, 0x00000000, 0x00000000, 0x0001c44a, 0x0001c44a},
+ {0x0000a320, 0x00000000, 0x00000000, 0x0002064a, 0x0002064a},
+ {0x0000a324, 0x00000000, 0x00000000, 0x0002484a, 0x0002484a},
+ {0x0000a328, 0x00000000, 0x00000000, 0x00028a4a, 0x00028a4a},
+ {0x0000a32c, 0x00000000, 0x00000000, 0x0002cc4a, 0x0002cc4a},
+ {0x0000a330, 0x00000000, 0x00000000, 0x00030e4a, 0x00030e4a},
+ {0x0000a334, 0x00000000, 0x00000000, 0x00034e8a, 0x00034e8a},
+ {0x0000a338, 0x00000000, 0x00000000, 0x00038e8c, 0x00038e8c},
+ {0x0000a33c, 0x00000000, 0x00000000, 0x0003cecc, 0x0003cecc},
+ {0x0000a340, 0x00000000, 0x00000000, 0x00040ed4, 0x00040ed4},
+ {0x0000a344, 0x00000000, 0x00000000, 0x00044edc, 0x00044edc},
+ {0x0000a348, 0x00000000, 0x00000000, 0x00048ede, 0x00048ede},
+ {0x0000a34c, 0x00000000, 0x00000000, 0x0004cf1e, 0x0004cf1e},
+ {0x0000a350, 0x00000000, 0x00000000, 0x00050f5e, 0x00050f5e},
+ {0x0000a354, 0x00000000, 0x00000000, 0x00054f9e, 0x00054f9e},
+ {0x0000a780, 0x00000000, 0x00000000, 0x00000062, 0x00000062},
+ {0x0000a784, 0x00000000, 0x00000000, 0x00004064, 0x00004064},
+ {0x0000a788, 0x00000000, 0x00000000, 0x000080a4, 0x000080a4},
+ {0x0000a78c, 0x00000000, 0x00000000, 0x0000c0aa, 0x0000c0aa},
+ {0x0000a790, 0x00000000, 0x00000000, 0x000100ac, 0x000100ac},
+ {0x0000a794, 0x00000000, 0x00000000, 0x000140b4, 0x000140b4},
+ {0x0000a798, 0x00000000, 0x00000000, 0x000180f4, 0x000180f4},
+ {0x0000a79c, 0x00000000, 0x00000000, 0x0001c134, 0x0001c134},
+ {0x0000a7a0, 0x00000000, 0x00000000, 0x00020174, 0x00020174},
+ {0x0000a7a4, 0x00000000, 0x00000000, 0x0002417c, 0x0002417c},
+ {0x0000a7a8, 0x00000000, 0x00000000, 0x0002817e, 0x0002817e},
+ {0x0000a7ac, 0x00000000, 0x00000000, 0x0002c1be, 0x0002c1be},
+ {0x0000a7b0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe},
+ {0x0000a7b4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe},
+ {0x0000a7b8, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe},
+ {0x0000a7bc, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe},
+ {0x0000a7c0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe},
+ {0x0000a7c4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe},
+ {0x0000a7c8, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe},
+ {0x0000a7cc, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe},
+ {0x0000a7d0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe},
+ {0x0000a7d4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe},
+ {0x0000a274, 0x0a180000, 0x0a180000, 0x0a1aa000, 0x0a1aa000},
};
-static const u32 ar9287Modes_rx_gain_9287_1_1[][6] = {
- {0x00009a00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120, 0x0000a120},
- {0x00009a04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124, 0x0000a124},
- {0x00009a08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128, 0x0000a128},
- {0x00009a0c, 0x00000000, 0x00000000, 0x0000a12c, 0x0000a12c, 0x0000a12c},
- {0x00009a10, 0x00000000, 0x00000000, 0x0000a130, 0x0000a130, 0x0000a130},
- {0x00009a14, 0x00000000, 0x00000000, 0x0000a194, 0x0000a194, 0x0000a194},
- {0x00009a18, 0x00000000, 0x00000000, 0x0000a198, 0x0000a198, 0x0000a198},
- {0x00009a1c, 0x00000000, 0x00000000, 0x0000a20c, 0x0000a20c, 0x0000a20c},
- {0x00009a20, 0x00000000, 0x00000000, 0x0000a210, 0x0000a210, 0x0000a210},
- {0x00009a24, 0x00000000, 0x00000000, 0x0000a284, 0x0000a284, 0x0000a284},
- {0x00009a28, 0x00000000, 0x00000000, 0x0000a288, 0x0000a288, 0x0000a288},
- {0x00009a2c, 0x00000000, 0x00000000, 0x0000a28c, 0x0000a28c, 0x0000a28c},
- {0x00009a30, 0x00000000, 0x00000000, 0x0000a290, 0x0000a290, 0x0000a290},
- {0x00009a34, 0x00000000, 0x00000000, 0x0000a294, 0x0000a294, 0x0000a294},
- {0x00009a38, 0x00000000, 0x00000000, 0x0000a2a0, 0x0000a2a0, 0x0000a2a0},
- {0x00009a3c, 0x00000000, 0x00000000, 0x0000a2a4, 0x0000a2a4, 0x0000a2a4},
- {0x00009a40, 0x00000000, 0x00000000, 0x0000a2a8, 0x0000a2a8, 0x0000a2a8},
- {0x00009a44, 0x00000000, 0x00000000, 0x0000a2ac, 0x0000a2ac, 0x0000a2ac},
- {0x00009a48, 0x00000000, 0x00000000, 0x0000a2b0, 0x0000a2b0, 0x0000a2b0},
- {0x00009a4c, 0x00000000, 0x00000000, 0x0000a2b4, 0x0000a2b4, 0x0000a2b4},
- {0x00009a50, 0x00000000, 0x00000000, 0x0000a2b8, 0x0000a2b8, 0x0000a2b8},
- {0x00009a54, 0x00000000, 0x00000000, 0x0000a2c4, 0x0000a2c4, 0x0000a2c4},
- {0x00009a58, 0x00000000, 0x00000000, 0x0000a708, 0x0000a708, 0x0000a708},
- {0x00009a5c, 0x00000000, 0x00000000, 0x0000a70c, 0x0000a70c, 0x0000a70c},
- {0x00009a60, 0x00000000, 0x00000000, 0x0000a710, 0x0000a710, 0x0000a710},
- {0x00009a64, 0x00000000, 0x00000000, 0x0000ab04, 0x0000ab04, 0x0000ab04},
- {0x00009a68, 0x00000000, 0x00000000, 0x0000ab08, 0x0000ab08, 0x0000ab08},
- {0x00009a6c, 0x00000000, 0x00000000, 0x0000ab0c, 0x0000ab0c, 0x0000ab0c},
- {0x00009a70, 0x00000000, 0x00000000, 0x0000ab10, 0x0000ab10, 0x0000ab10},
- {0x00009a74, 0x00000000, 0x00000000, 0x0000ab14, 0x0000ab14, 0x0000ab14},
- {0x00009a78, 0x00000000, 0x00000000, 0x0000ab18, 0x0000ab18, 0x0000ab18},
- {0x00009a7c, 0x00000000, 0x00000000, 0x0000ab8c, 0x0000ab8c, 0x0000ab8c},
- {0x00009a80, 0x00000000, 0x00000000, 0x0000ab90, 0x0000ab90, 0x0000ab90},
- {0x00009a84, 0x00000000, 0x00000000, 0x0000ab94, 0x0000ab94, 0x0000ab94},
- {0x00009a88, 0x00000000, 0x00000000, 0x0000ab98, 0x0000ab98, 0x0000ab98},
- {0x00009a8c, 0x00000000, 0x00000000, 0x0000aba4, 0x0000aba4, 0x0000aba4},
- {0x00009a90, 0x00000000, 0x00000000, 0x0000aba8, 0x0000aba8, 0x0000aba8},
- {0x00009a94, 0x00000000, 0x00000000, 0x0000cb04, 0x0000cb04, 0x0000cb04},
- {0x00009a98, 0x00000000, 0x00000000, 0x0000cb08, 0x0000cb08, 0x0000cb08},
- {0x00009a9c, 0x00000000, 0x00000000, 0x0000cb0c, 0x0000cb0c, 0x0000cb0c},
- {0x00009aa0, 0x00000000, 0x00000000, 0x0000cb10, 0x0000cb10, 0x0000cb10},
- {0x00009aa4, 0x00000000, 0x00000000, 0x0000cb14, 0x0000cb14, 0x0000cb14},
- {0x00009aa8, 0x00000000, 0x00000000, 0x0000cb18, 0x0000cb18, 0x0000cb18},
- {0x00009aac, 0x00000000, 0x00000000, 0x0000cb8c, 0x0000cb8c, 0x0000cb8c},
- {0x00009ab0, 0x00000000, 0x00000000, 0x0000cb90, 0x0000cb90, 0x0000cb90},
- {0x00009ab4, 0x00000000, 0x00000000, 0x0000cf18, 0x0000cf18, 0x0000cf18},
- {0x00009ab8, 0x00000000, 0x00000000, 0x0000cf24, 0x0000cf24, 0x0000cf24},
- {0x00009abc, 0x00000000, 0x00000000, 0x0000cf28, 0x0000cf28, 0x0000cf28},
- {0x00009ac0, 0x00000000, 0x00000000, 0x0000d314, 0x0000d314, 0x0000d314},
- {0x00009ac4, 0x00000000, 0x00000000, 0x0000d318, 0x0000d318, 0x0000d318},
- {0x00009ac8, 0x00000000, 0x00000000, 0x0000d38c, 0x0000d38c, 0x0000d38c},
- {0x00009acc, 0x00000000, 0x00000000, 0x0000d390, 0x0000d390, 0x0000d390},
- {0x00009ad0, 0x00000000, 0x00000000, 0x0000d394, 0x0000d394, 0x0000d394},
- {0x00009ad4, 0x00000000, 0x00000000, 0x0000d398, 0x0000d398, 0x0000d398},
- {0x00009ad8, 0x00000000, 0x00000000, 0x0000d3a4, 0x0000d3a4, 0x0000d3a4},
- {0x00009adc, 0x00000000, 0x00000000, 0x0000d3a8, 0x0000d3a8, 0x0000d3a8},
- {0x00009ae0, 0x00000000, 0x00000000, 0x0000d3ac, 0x0000d3ac, 0x0000d3ac},
- {0x00009ae4, 0x00000000, 0x00000000, 0x0000d3b0, 0x0000d3b0, 0x0000d3b0},
- {0x00009ae8, 0x00000000, 0x00000000, 0x0000f380, 0x0000f380, 0x0000f380},
- {0x00009aec, 0x00000000, 0x00000000, 0x0000f384, 0x0000f384, 0x0000f384},
- {0x00009af0, 0x00000000, 0x00000000, 0x0000f388, 0x0000f388, 0x0000f388},
- {0x00009af4, 0x00000000, 0x00000000, 0x0000f710, 0x0000f710, 0x0000f710},
- {0x00009af8, 0x00000000, 0x00000000, 0x0000f714, 0x0000f714, 0x0000f714},
- {0x00009afc, 0x00000000, 0x00000000, 0x0000f718, 0x0000f718, 0x0000f718},
- {0x00009b00, 0x00000000, 0x00000000, 0x0000fb10, 0x0000fb10, 0x0000fb10},
- {0x00009b04, 0x00000000, 0x00000000, 0x0000fb14, 0x0000fb14, 0x0000fb14},
- {0x00009b08, 0x00000000, 0x00000000, 0x0000fb18, 0x0000fb18, 0x0000fb18},
- {0x00009b0c, 0x00000000, 0x00000000, 0x0000fb8c, 0x0000fb8c, 0x0000fb8c},
- {0x00009b10, 0x00000000, 0x00000000, 0x0000fb90, 0x0000fb90, 0x0000fb90},
- {0x00009b14, 0x00000000, 0x00000000, 0x0000fb94, 0x0000fb94, 0x0000fb94},
- {0x00009b18, 0x00000000, 0x00000000, 0x0000ff8c, 0x0000ff8c, 0x0000ff8c},
- {0x00009b1c, 0x00000000, 0x00000000, 0x0000ff90, 0x0000ff90, 0x0000ff90},
- {0x00009b20, 0x00000000, 0x00000000, 0x0000ff94, 0x0000ff94, 0x0000ff94},
- {0x00009b24, 0x00000000, 0x00000000, 0x0000ffa0, 0x0000ffa0, 0x0000ffa0},
- {0x00009b28, 0x00000000, 0x00000000, 0x0000ffa4, 0x0000ffa4, 0x0000ffa4},
- {0x00009b2c, 0x00000000, 0x00000000, 0x0000ffa8, 0x0000ffa8, 0x0000ffa8},
- {0x00009b30, 0x00000000, 0x00000000, 0x0000ffac, 0x0000ffac, 0x0000ffac},
- {0x00009b34, 0x00000000, 0x00000000, 0x0000ffb0, 0x0000ffb0, 0x0000ffb0},
- {0x00009b38, 0x00000000, 0x00000000, 0x0000ffb4, 0x0000ffb4, 0x0000ffb4},
- {0x00009b3c, 0x00000000, 0x00000000, 0x0000ffa1, 0x0000ffa1, 0x0000ffa1},
- {0x00009b40, 0x00000000, 0x00000000, 0x0000ffa5, 0x0000ffa5, 0x0000ffa5},
- {0x00009b44, 0x00000000, 0x00000000, 0x0000ffa9, 0x0000ffa9, 0x0000ffa9},
- {0x00009b48, 0x00000000, 0x00000000, 0x0000ffad, 0x0000ffad, 0x0000ffad},
- {0x00009b4c, 0x00000000, 0x00000000, 0x0000ffb1, 0x0000ffb1, 0x0000ffb1},
- {0x00009b50, 0x00000000, 0x00000000, 0x0000ffb5, 0x0000ffb5, 0x0000ffb5},
- {0x00009b54, 0x00000000, 0x00000000, 0x0000ffb9, 0x0000ffb9, 0x0000ffb9},
- {0x00009b58, 0x00000000, 0x00000000, 0x0000ffc5, 0x0000ffc5, 0x0000ffc5},
- {0x00009b5c, 0x00000000, 0x00000000, 0x0000ffc9, 0x0000ffc9, 0x0000ffc9},
- {0x00009b60, 0x00000000, 0x00000000, 0x0000ffcd, 0x0000ffcd, 0x0000ffcd},
- {0x00009b64, 0x00000000, 0x00000000, 0x0000ffd1, 0x0000ffd1, 0x0000ffd1},
- {0x00009b68, 0x00000000, 0x00000000, 0x0000ffd5, 0x0000ffd5, 0x0000ffd5},
- {0x00009b6c, 0x00000000, 0x00000000, 0x0000ffc2, 0x0000ffc2, 0x0000ffc2},
- {0x00009b70, 0x00000000, 0x00000000, 0x0000ffc6, 0x0000ffc6, 0x0000ffc6},
- {0x00009b74, 0x00000000, 0x00000000, 0x0000ffca, 0x0000ffca, 0x0000ffca},
- {0x00009b78, 0x00000000, 0x00000000, 0x0000ffce, 0x0000ffce, 0x0000ffce},
- {0x00009b7c, 0x00000000, 0x00000000, 0x0000ffd2, 0x0000ffd2, 0x0000ffd2},
- {0x00009b80, 0x00000000, 0x00000000, 0x0000ffd6, 0x0000ffd6, 0x0000ffd6},
- {0x00009b84, 0x00000000, 0x00000000, 0x0000ffda, 0x0000ffda, 0x0000ffda},
- {0x00009b88, 0x00000000, 0x00000000, 0x0000ffc7, 0x0000ffc7, 0x0000ffc7},
- {0x00009b8c, 0x00000000, 0x00000000, 0x0000ffcb, 0x0000ffcb, 0x0000ffcb},
- {0x00009b90, 0x00000000, 0x00000000, 0x0000ffcf, 0x0000ffcf, 0x0000ffcf},
- {0x00009b94, 0x00000000, 0x00000000, 0x0000ffd3, 0x0000ffd3, 0x0000ffd3},
- {0x00009b98, 0x00000000, 0x00000000, 0x0000ffd7, 0x0000ffd7, 0x0000ffd7},
- {0x00009b9c, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009ba0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009ba4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009ba8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bac, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bb0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bb4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bb8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bbc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bc0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bc4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bc8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bcc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bd0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bd4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bd8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bdc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009be0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009be4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009be8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bec, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bf0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bf4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bf8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009bfc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000aa00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120, 0x0000a120},
- {0x0000aa04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124, 0x0000a124},
- {0x0000aa08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128, 0x0000a128},
- {0x0000aa0c, 0x00000000, 0x00000000, 0x0000a12c, 0x0000a12c, 0x0000a12c},
- {0x0000aa10, 0x00000000, 0x00000000, 0x0000a130, 0x0000a130, 0x0000a130},
- {0x0000aa14, 0x00000000, 0x00000000, 0x0000a194, 0x0000a194, 0x0000a194},
- {0x0000aa18, 0x00000000, 0x00000000, 0x0000a198, 0x0000a198, 0x0000a198},
- {0x0000aa1c, 0x00000000, 0x00000000, 0x0000a20c, 0x0000a20c, 0x0000a20c},
- {0x0000aa20, 0x00000000, 0x00000000, 0x0000a210, 0x0000a210, 0x0000a210},
- {0x0000aa24, 0x00000000, 0x00000000, 0x0000a284, 0x0000a284, 0x0000a284},
- {0x0000aa28, 0x00000000, 0x00000000, 0x0000a288, 0x0000a288, 0x0000a288},
- {0x0000aa2c, 0x00000000, 0x00000000, 0x0000a28c, 0x0000a28c, 0x0000a28c},
- {0x0000aa30, 0x00000000, 0x00000000, 0x0000a290, 0x0000a290, 0x0000a290},
- {0x0000aa34, 0x00000000, 0x00000000, 0x0000a294, 0x0000a294, 0x0000a294},
- {0x0000aa38, 0x00000000, 0x00000000, 0x0000a2a0, 0x0000a2a0, 0x0000a2a0},
- {0x0000aa3c, 0x00000000, 0x00000000, 0x0000a2a4, 0x0000a2a4, 0x0000a2a4},
- {0x0000aa40, 0x00000000, 0x00000000, 0x0000a2a8, 0x0000a2a8, 0x0000a2a8},
- {0x0000aa44, 0x00000000, 0x00000000, 0x0000a2ac, 0x0000a2ac, 0x0000a2ac},
- {0x0000aa48, 0x00000000, 0x00000000, 0x0000a2b0, 0x0000a2b0, 0x0000a2b0},
- {0x0000aa4c, 0x00000000, 0x00000000, 0x0000a2b4, 0x0000a2b4, 0x0000a2b4},
- {0x0000aa50, 0x00000000, 0x00000000, 0x0000a2b8, 0x0000a2b8, 0x0000a2b8},
- {0x0000aa54, 0x00000000, 0x00000000, 0x0000a2c4, 0x0000a2c4, 0x0000a2c4},
- {0x0000aa58, 0x00000000, 0x00000000, 0x0000a708, 0x0000a708, 0x0000a708},
- {0x0000aa5c, 0x00000000, 0x00000000, 0x0000a70c, 0x0000a70c, 0x0000a70c},
- {0x0000aa60, 0x00000000, 0x00000000, 0x0000a710, 0x0000a710, 0x0000a710},
- {0x0000aa64, 0x00000000, 0x00000000, 0x0000ab04, 0x0000ab04, 0x0000ab04},
- {0x0000aa68, 0x00000000, 0x00000000, 0x0000ab08, 0x0000ab08, 0x0000ab08},
- {0x0000aa6c, 0x00000000, 0x00000000, 0x0000ab0c, 0x0000ab0c, 0x0000ab0c},
- {0x0000aa70, 0x00000000, 0x00000000, 0x0000ab10, 0x0000ab10, 0x0000ab10},
- {0x0000aa74, 0x00000000, 0x00000000, 0x0000ab14, 0x0000ab14, 0x0000ab14},
- {0x0000aa78, 0x00000000, 0x00000000, 0x0000ab18, 0x0000ab18, 0x0000ab18},
- {0x0000aa7c, 0x00000000, 0x00000000, 0x0000ab8c, 0x0000ab8c, 0x0000ab8c},
- {0x0000aa80, 0x00000000, 0x00000000, 0x0000ab90, 0x0000ab90, 0x0000ab90},
- {0x0000aa84, 0x00000000, 0x00000000, 0x0000ab94, 0x0000ab94, 0x0000ab94},
- {0x0000aa88, 0x00000000, 0x00000000, 0x0000ab98, 0x0000ab98, 0x0000ab98},
- {0x0000aa8c, 0x00000000, 0x00000000, 0x0000aba4, 0x0000aba4, 0x0000aba4},
- {0x0000aa90, 0x00000000, 0x00000000, 0x0000aba8, 0x0000aba8, 0x0000aba8},
- {0x0000aa94, 0x00000000, 0x00000000, 0x0000cb04, 0x0000cb04, 0x0000cb04},
- {0x0000aa98, 0x00000000, 0x00000000, 0x0000cb08, 0x0000cb08, 0x0000cb08},
- {0x0000aa9c, 0x00000000, 0x00000000, 0x0000cb0c, 0x0000cb0c, 0x0000cb0c},
- {0x0000aaa0, 0x00000000, 0x00000000, 0x0000cb10, 0x0000cb10, 0x0000cb10},
- {0x0000aaa4, 0x00000000, 0x00000000, 0x0000cb14, 0x0000cb14, 0x0000cb14},
- {0x0000aaa8, 0x00000000, 0x00000000, 0x0000cb18, 0x0000cb18, 0x0000cb18},
- {0x0000aaac, 0x00000000, 0x00000000, 0x0000cb8c, 0x0000cb8c, 0x0000cb8c},
- {0x0000aab0, 0x00000000, 0x00000000, 0x0000cb90, 0x0000cb90, 0x0000cb90},
- {0x0000aab4, 0x00000000, 0x00000000, 0x0000cf18, 0x0000cf18, 0x0000cf18},
- {0x0000aab8, 0x00000000, 0x00000000, 0x0000cf24, 0x0000cf24, 0x0000cf24},
- {0x0000aabc, 0x00000000, 0x00000000, 0x0000cf28, 0x0000cf28, 0x0000cf28},
- {0x0000aac0, 0x00000000, 0x00000000, 0x0000d314, 0x0000d314, 0x0000d314},
- {0x0000aac4, 0x00000000, 0x00000000, 0x0000d318, 0x0000d318, 0x0000d318},
- {0x0000aac8, 0x00000000, 0x00000000, 0x0000d38c, 0x0000d38c, 0x0000d38c},
- {0x0000aacc, 0x00000000, 0x00000000, 0x0000d390, 0x0000d390, 0x0000d390},
- {0x0000aad0, 0x00000000, 0x00000000, 0x0000d394, 0x0000d394, 0x0000d394},
- {0x0000aad4, 0x00000000, 0x00000000, 0x0000d398, 0x0000d398, 0x0000d398},
- {0x0000aad8, 0x00000000, 0x00000000, 0x0000d3a4, 0x0000d3a4, 0x0000d3a4},
- {0x0000aadc, 0x00000000, 0x00000000, 0x0000d3a8, 0x0000d3a8, 0x0000d3a8},
- {0x0000aae0, 0x00000000, 0x00000000, 0x0000d3ac, 0x0000d3ac, 0x0000d3ac},
- {0x0000aae4, 0x00000000, 0x00000000, 0x0000d3b0, 0x0000d3b0, 0x0000d3b0},
- {0x0000aae8, 0x00000000, 0x00000000, 0x0000f380, 0x0000f380, 0x0000f380},
- {0x0000aaec, 0x00000000, 0x00000000, 0x0000f384, 0x0000f384, 0x0000f384},
- {0x0000aaf0, 0x00000000, 0x00000000, 0x0000f388, 0x0000f388, 0x0000f388},
- {0x0000aaf4, 0x00000000, 0x00000000, 0x0000f710, 0x0000f710, 0x0000f710},
- {0x0000aaf8, 0x00000000, 0x00000000, 0x0000f714, 0x0000f714, 0x0000f714},
- {0x0000aafc, 0x00000000, 0x00000000, 0x0000f718, 0x0000f718, 0x0000f718},
- {0x0000ab00, 0x00000000, 0x00000000, 0x0000fb10, 0x0000fb10, 0x0000fb10},
- {0x0000ab04, 0x00000000, 0x00000000, 0x0000fb14, 0x0000fb14, 0x0000fb14},
- {0x0000ab08, 0x00000000, 0x00000000, 0x0000fb18, 0x0000fb18, 0x0000fb18},
- {0x0000ab0c, 0x00000000, 0x00000000, 0x0000fb8c, 0x0000fb8c, 0x0000fb8c},
- {0x0000ab10, 0x00000000, 0x00000000, 0x0000fb90, 0x0000fb90, 0x0000fb90},
- {0x0000ab14, 0x00000000, 0x00000000, 0x0000fb94, 0x0000fb94, 0x0000fb94},
- {0x0000ab18, 0x00000000, 0x00000000, 0x0000ff8c, 0x0000ff8c, 0x0000ff8c},
- {0x0000ab1c, 0x00000000, 0x00000000, 0x0000ff90, 0x0000ff90, 0x0000ff90},
- {0x0000ab20, 0x00000000, 0x00000000, 0x0000ff94, 0x0000ff94, 0x0000ff94},
- {0x0000ab24, 0x00000000, 0x00000000, 0x0000ffa0, 0x0000ffa0, 0x0000ffa0},
- {0x0000ab28, 0x00000000, 0x00000000, 0x0000ffa4, 0x0000ffa4, 0x0000ffa4},
- {0x0000ab2c, 0x00000000, 0x00000000, 0x0000ffa8, 0x0000ffa8, 0x0000ffa8},
- {0x0000ab30, 0x00000000, 0x00000000, 0x0000ffac, 0x0000ffac, 0x0000ffac},
- {0x0000ab34, 0x00000000, 0x00000000, 0x0000ffb0, 0x0000ffb0, 0x0000ffb0},
- {0x0000ab38, 0x00000000, 0x00000000, 0x0000ffb4, 0x0000ffb4, 0x0000ffb4},
- {0x0000ab3c, 0x00000000, 0x00000000, 0x0000ffa1, 0x0000ffa1, 0x0000ffa1},
- {0x0000ab40, 0x00000000, 0x00000000, 0x0000ffa5, 0x0000ffa5, 0x0000ffa5},
- {0x0000ab44, 0x00000000, 0x00000000, 0x0000ffa9, 0x0000ffa9, 0x0000ffa9},
- {0x0000ab48, 0x00000000, 0x00000000, 0x0000ffad, 0x0000ffad, 0x0000ffad},
- {0x0000ab4c, 0x00000000, 0x00000000, 0x0000ffb1, 0x0000ffb1, 0x0000ffb1},
- {0x0000ab50, 0x00000000, 0x00000000, 0x0000ffb5, 0x0000ffb5, 0x0000ffb5},
- {0x0000ab54, 0x00000000, 0x00000000, 0x0000ffb9, 0x0000ffb9, 0x0000ffb9},
- {0x0000ab58, 0x00000000, 0x00000000, 0x0000ffc5, 0x0000ffc5, 0x0000ffc5},
- {0x0000ab5c, 0x00000000, 0x00000000, 0x0000ffc9, 0x0000ffc9, 0x0000ffc9},
- {0x0000ab60, 0x00000000, 0x00000000, 0x0000ffcd, 0x0000ffcd, 0x0000ffcd},
- {0x0000ab64, 0x00000000, 0x00000000, 0x0000ffd1, 0x0000ffd1, 0x0000ffd1},
- {0x0000ab68, 0x00000000, 0x00000000, 0x0000ffd5, 0x0000ffd5, 0x0000ffd5},
- {0x0000ab6c, 0x00000000, 0x00000000, 0x0000ffc2, 0x0000ffc2, 0x0000ffc2},
- {0x0000ab70, 0x00000000, 0x00000000, 0x0000ffc6, 0x0000ffc6, 0x0000ffc6},
- {0x0000ab74, 0x00000000, 0x00000000, 0x0000ffca, 0x0000ffca, 0x0000ffca},
- {0x0000ab78, 0x00000000, 0x00000000, 0x0000ffce, 0x0000ffce, 0x0000ffce},
- {0x0000ab7c, 0x00000000, 0x00000000, 0x0000ffd2, 0x0000ffd2, 0x0000ffd2},
- {0x0000ab80, 0x00000000, 0x00000000, 0x0000ffd6, 0x0000ffd6, 0x0000ffd6},
- {0x0000ab84, 0x00000000, 0x00000000, 0x0000ffda, 0x0000ffda, 0x0000ffda},
- {0x0000ab88, 0x00000000, 0x00000000, 0x0000ffc7, 0x0000ffc7, 0x0000ffc7},
- {0x0000ab8c, 0x00000000, 0x00000000, 0x0000ffcb, 0x0000ffcb, 0x0000ffcb},
- {0x0000ab90, 0x00000000, 0x00000000, 0x0000ffcf, 0x0000ffcf, 0x0000ffcf},
- {0x0000ab94, 0x00000000, 0x00000000, 0x0000ffd3, 0x0000ffd3, 0x0000ffd3},
- {0x0000ab98, 0x00000000, 0x00000000, 0x0000ffd7, 0x0000ffd7, 0x0000ffd7},
- {0x0000ab9c, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000aba0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000aba4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000aba8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abac, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abb0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abb4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abb8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abbc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abc0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abc4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abc8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abcc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abd0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abd4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abd8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abdc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abe0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abe4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abe8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abec, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abf0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abf4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abf8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x0000abfc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb},
- {0x00009848, 0x00000000, 0x00000000, 0x00001067, 0x00001067, 0x00001067},
- {0x0000a848, 0x00000000, 0x00000000, 0x00001067, 0x00001067, 0x00001067},
+static const u32 ar9287Modes_rx_gain_9287_1_1[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009a00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120},
+ {0x00009a04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124},
+ {0x00009a08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128},
+ {0x00009a0c, 0x00000000, 0x00000000, 0x0000a12c, 0x0000a12c},
+ {0x00009a10, 0x00000000, 0x00000000, 0x0000a130, 0x0000a130},
+ {0x00009a14, 0x00000000, 0x00000000, 0x0000a194, 0x0000a194},
+ {0x00009a18, 0x00000000, 0x00000000, 0x0000a198, 0x0000a198},
+ {0x00009a1c, 0x00000000, 0x00000000, 0x0000a20c, 0x0000a20c},
+ {0x00009a20, 0x00000000, 0x00000000, 0x0000a210, 0x0000a210},
+ {0x00009a24, 0x00000000, 0x00000000, 0x0000a284, 0x0000a284},
+ {0x00009a28, 0x00000000, 0x00000000, 0x0000a288, 0x0000a288},
+ {0x00009a2c, 0x00000000, 0x00000000, 0x0000a28c, 0x0000a28c},
+ {0x00009a30, 0x00000000, 0x00000000, 0x0000a290, 0x0000a290},
+ {0x00009a34, 0x00000000, 0x00000000, 0x0000a294, 0x0000a294},
+ {0x00009a38, 0x00000000, 0x00000000, 0x0000a2a0, 0x0000a2a0},
+ {0x00009a3c, 0x00000000, 0x00000000, 0x0000a2a4, 0x0000a2a4},
+ {0x00009a40, 0x00000000, 0x00000000, 0x0000a2a8, 0x0000a2a8},
+ {0x00009a44, 0x00000000, 0x00000000, 0x0000a2ac, 0x0000a2ac},
+ {0x00009a48, 0x00000000, 0x00000000, 0x0000a2b0, 0x0000a2b0},
+ {0x00009a4c, 0x00000000, 0x00000000, 0x0000a2b4, 0x0000a2b4},
+ {0x00009a50, 0x00000000, 0x00000000, 0x0000a2b8, 0x0000a2b8},
+ {0x00009a54, 0x00000000, 0x00000000, 0x0000a2c4, 0x0000a2c4},
+ {0x00009a58, 0x00000000, 0x00000000, 0x0000a708, 0x0000a708},
+ {0x00009a5c, 0x00000000, 0x00000000, 0x0000a70c, 0x0000a70c},
+ {0x00009a60, 0x00000000, 0x00000000, 0x0000a710, 0x0000a710},
+ {0x00009a64, 0x00000000, 0x00000000, 0x0000ab04, 0x0000ab04},
+ {0x00009a68, 0x00000000, 0x00000000, 0x0000ab08, 0x0000ab08},
+ {0x00009a6c, 0x00000000, 0x00000000, 0x0000ab0c, 0x0000ab0c},
+ {0x00009a70, 0x00000000, 0x00000000, 0x0000ab10, 0x0000ab10},
+ {0x00009a74, 0x00000000, 0x00000000, 0x0000ab14, 0x0000ab14},
+ {0x00009a78, 0x00000000, 0x00000000, 0x0000ab18, 0x0000ab18},
+ {0x00009a7c, 0x00000000, 0x00000000, 0x0000ab8c, 0x0000ab8c},
+ {0x00009a80, 0x00000000, 0x00000000, 0x0000ab90, 0x0000ab90},
+ {0x00009a84, 0x00000000, 0x00000000, 0x0000ab94, 0x0000ab94},
+ {0x00009a88, 0x00000000, 0x00000000, 0x0000ab98, 0x0000ab98},
+ {0x00009a8c, 0x00000000, 0x00000000, 0x0000aba4, 0x0000aba4},
+ {0x00009a90, 0x00000000, 0x00000000, 0x0000aba8, 0x0000aba8},
+ {0x00009a94, 0x00000000, 0x00000000, 0x0000cb04, 0x0000cb04},
+ {0x00009a98, 0x00000000, 0x00000000, 0x0000cb08, 0x0000cb08},
+ {0x00009a9c, 0x00000000, 0x00000000, 0x0000cb0c, 0x0000cb0c},
+ {0x00009aa0, 0x00000000, 0x00000000, 0x0000cb10, 0x0000cb10},
+ {0x00009aa4, 0x00000000, 0x00000000, 0x0000cb14, 0x0000cb14},
+ {0x00009aa8, 0x00000000, 0x00000000, 0x0000cb18, 0x0000cb18},
+ {0x00009aac, 0x00000000, 0x00000000, 0x0000cb8c, 0x0000cb8c},
+ {0x00009ab0, 0x00000000, 0x00000000, 0x0000cb90, 0x0000cb90},
+ {0x00009ab4, 0x00000000, 0x00000000, 0x0000cf18, 0x0000cf18},
+ {0x00009ab8, 0x00000000, 0x00000000, 0x0000cf24, 0x0000cf24},
+ {0x00009abc, 0x00000000, 0x00000000, 0x0000cf28, 0x0000cf28},
+ {0x00009ac0, 0x00000000, 0x00000000, 0x0000d314, 0x0000d314},
+ {0x00009ac4, 0x00000000, 0x00000000, 0x0000d318, 0x0000d318},
+ {0x00009ac8, 0x00000000, 0x00000000, 0x0000d38c, 0x0000d38c},
+ {0x00009acc, 0x00000000, 0x00000000, 0x0000d390, 0x0000d390},
+ {0x00009ad0, 0x00000000, 0x00000000, 0x0000d394, 0x0000d394},
+ {0x00009ad4, 0x00000000, 0x00000000, 0x0000d398, 0x0000d398},
+ {0x00009ad8, 0x00000000, 0x00000000, 0x0000d3a4, 0x0000d3a4},
+ {0x00009adc, 0x00000000, 0x00000000, 0x0000d3a8, 0x0000d3a8},
+ {0x00009ae0, 0x00000000, 0x00000000, 0x0000d3ac, 0x0000d3ac},
+ {0x00009ae4, 0x00000000, 0x00000000, 0x0000d3b0, 0x0000d3b0},
+ {0x00009ae8, 0x00000000, 0x00000000, 0x0000f380, 0x0000f380},
+ {0x00009aec, 0x00000000, 0x00000000, 0x0000f384, 0x0000f384},
+ {0x00009af0, 0x00000000, 0x00000000, 0x0000f388, 0x0000f388},
+ {0x00009af4, 0x00000000, 0x00000000, 0x0000f710, 0x0000f710},
+ {0x00009af8, 0x00000000, 0x00000000, 0x0000f714, 0x0000f714},
+ {0x00009afc, 0x00000000, 0x00000000, 0x0000f718, 0x0000f718},
+ {0x00009b00, 0x00000000, 0x00000000, 0x0000fb10, 0x0000fb10},
+ {0x00009b04, 0x00000000, 0x00000000, 0x0000fb14, 0x0000fb14},
+ {0x00009b08, 0x00000000, 0x00000000, 0x0000fb18, 0x0000fb18},
+ {0x00009b0c, 0x00000000, 0x00000000, 0x0000fb8c, 0x0000fb8c},
+ {0x00009b10, 0x00000000, 0x00000000, 0x0000fb90, 0x0000fb90},
+ {0x00009b14, 0x00000000, 0x00000000, 0x0000fb94, 0x0000fb94},
+ {0x00009b18, 0x00000000, 0x00000000, 0x0000ff8c, 0x0000ff8c},
+ {0x00009b1c, 0x00000000, 0x00000000, 0x0000ff90, 0x0000ff90},
+ {0x00009b20, 0x00000000, 0x00000000, 0x0000ff94, 0x0000ff94},
+ {0x00009b24, 0x00000000, 0x00000000, 0x0000ffa0, 0x0000ffa0},
+ {0x00009b28, 0x00000000, 0x00000000, 0x0000ffa4, 0x0000ffa4},
+ {0x00009b2c, 0x00000000, 0x00000000, 0x0000ffa8, 0x0000ffa8},
+ {0x00009b30, 0x00000000, 0x00000000, 0x0000ffac, 0x0000ffac},
+ {0x00009b34, 0x00000000, 0x00000000, 0x0000ffb0, 0x0000ffb0},
+ {0x00009b38, 0x00000000, 0x00000000, 0x0000ffb4, 0x0000ffb4},
+ {0x00009b3c, 0x00000000, 0x00000000, 0x0000ffa1, 0x0000ffa1},
+ {0x00009b40, 0x00000000, 0x00000000, 0x0000ffa5, 0x0000ffa5},
+ {0x00009b44, 0x00000000, 0x00000000, 0x0000ffa9, 0x0000ffa9},
+ {0x00009b48, 0x00000000, 0x00000000, 0x0000ffad, 0x0000ffad},
+ {0x00009b4c, 0x00000000, 0x00000000, 0x0000ffb1, 0x0000ffb1},
+ {0x00009b50, 0x00000000, 0x00000000, 0x0000ffb5, 0x0000ffb5},
+ {0x00009b54, 0x00000000, 0x00000000, 0x0000ffb9, 0x0000ffb9},
+ {0x00009b58, 0x00000000, 0x00000000, 0x0000ffc5, 0x0000ffc5},
+ {0x00009b5c, 0x00000000, 0x00000000, 0x0000ffc9, 0x0000ffc9},
+ {0x00009b60, 0x00000000, 0x00000000, 0x0000ffcd, 0x0000ffcd},
+ {0x00009b64, 0x00000000, 0x00000000, 0x0000ffd1, 0x0000ffd1},
+ {0x00009b68, 0x00000000, 0x00000000, 0x0000ffd5, 0x0000ffd5},
+ {0x00009b6c, 0x00000000, 0x00000000, 0x0000ffc2, 0x0000ffc2},
+ {0x00009b70, 0x00000000, 0x00000000, 0x0000ffc6, 0x0000ffc6},
+ {0x00009b74, 0x00000000, 0x00000000, 0x0000ffca, 0x0000ffca},
+ {0x00009b78, 0x00000000, 0x00000000, 0x0000ffce, 0x0000ffce},
+ {0x00009b7c, 0x00000000, 0x00000000, 0x0000ffd2, 0x0000ffd2},
+ {0x00009b80, 0x00000000, 0x00000000, 0x0000ffd6, 0x0000ffd6},
+ {0x00009b84, 0x00000000, 0x00000000, 0x0000ffda, 0x0000ffda},
+ {0x00009b88, 0x00000000, 0x00000000, 0x0000ffc7, 0x0000ffc7},
+ {0x00009b8c, 0x00000000, 0x00000000, 0x0000ffcb, 0x0000ffcb},
+ {0x00009b90, 0x00000000, 0x00000000, 0x0000ffcf, 0x0000ffcf},
+ {0x00009b94, 0x00000000, 0x00000000, 0x0000ffd3, 0x0000ffd3},
+ {0x00009b98, 0x00000000, 0x00000000, 0x0000ffd7, 0x0000ffd7},
+ {0x00009b9c, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009ba0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009ba4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009ba8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bac, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bb0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bb4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bb8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bbc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bc0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bc4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bc8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bcc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bd0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bd4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bd8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bdc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009be0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009be4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009be8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bec, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bf0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bf4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bf8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009bfc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000aa00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120},
+ {0x0000aa04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124},
+ {0x0000aa08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128},
+ {0x0000aa0c, 0x00000000, 0x00000000, 0x0000a12c, 0x0000a12c},
+ {0x0000aa10, 0x00000000, 0x00000000, 0x0000a130, 0x0000a130},
+ {0x0000aa14, 0x00000000, 0x00000000, 0x0000a194, 0x0000a194},
+ {0x0000aa18, 0x00000000, 0x00000000, 0x0000a198, 0x0000a198},
+ {0x0000aa1c, 0x00000000, 0x00000000, 0x0000a20c, 0x0000a20c},
+ {0x0000aa20, 0x00000000, 0x00000000, 0x0000a210, 0x0000a210},
+ {0x0000aa24, 0x00000000, 0x00000000, 0x0000a284, 0x0000a284},
+ {0x0000aa28, 0x00000000, 0x00000000, 0x0000a288, 0x0000a288},
+ {0x0000aa2c, 0x00000000, 0x00000000, 0x0000a28c, 0x0000a28c},
+ {0x0000aa30, 0x00000000, 0x00000000, 0x0000a290, 0x0000a290},
+ {0x0000aa34, 0x00000000, 0x00000000, 0x0000a294, 0x0000a294},
+ {0x0000aa38, 0x00000000, 0x00000000, 0x0000a2a0, 0x0000a2a0},
+ {0x0000aa3c, 0x00000000, 0x00000000, 0x0000a2a4, 0x0000a2a4},
+ {0x0000aa40, 0x00000000, 0x00000000, 0x0000a2a8, 0x0000a2a8},
+ {0x0000aa44, 0x00000000, 0x00000000, 0x0000a2ac, 0x0000a2ac},
+ {0x0000aa48, 0x00000000, 0x00000000, 0x0000a2b0, 0x0000a2b0},
+ {0x0000aa4c, 0x00000000, 0x00000000, 0x0000a2b4, 0x0000a2b4},
+ {0x0000aa50, 0x00000000, 0x00000000, 0x0000a2b8, 0x0000a2b8},
+ {0x0000aa54, 0x00000000, 0x00000000, 0x0000a2c4, 0x0000a2c4},
+ {0x0000aa58, 0x00000000, 0x00000000, 0x0000a708, 0x0000a708},
+ {0x0000aa5c, 0x00000000, 0x00000000, 0x0000a70c, 0x0000a70c},
+ {0x0000aa60, 0x00000000, 0x00000000, 0x0000a710, 0x0000a710},
+ {0x0000aa64, 0x00000000, 0x00000000, 0x0000ab04, 0x0000ab04},
+ {0x0000aa68, 0x00000000, 0x00000000, 0x0000ab08, 0x0000ab08},
+ {0x0000aa6c, 0x00000000, 0x00000000, 0x0000ab0c, 0x0000ab0c},
+ {0x0000aa70, 0x00000000, 0x00000000, 0x0000ab10, 0x0000ab10},
+ {0x0000aa74, 0x00000000, 0x00000000, 0x0000ab14, 0x0000ab14},
+ {0x0000aa78, 0x00000000, 0x00000000, 0x0000ab18, 0x0000ab18},
+ {0x0000aa7c, 0x00000000, 0x00000000, 0x0000ab8c, 0x0000ab8c},
+ {0x0000aa80, 0x00000000, 0x00000000, 0x0000ab90, 0x0000ab90},
+ {0x0000aa84, 0x00000000, 0x00000000, 0x0000ab94, 0x0000ab94},
+ {0x0000aa88, 0x00000000, 0x00000000, 0x0000ab98, 0x0000ab98},
+ {0x0000aa8c, 0x00000000, 0x00000000, 0x0000aba4, 0x0000aba4},
+ {0x0000aa90, 0x00000000, 0x00000000, 0x0000aba8, 0x0000aba8},
+ {0x0000aa94, 0x00000000, 0x00000000, 0x0000cb04, 0x0000cb04},
+ {0x0000aa98, 0x00000000, 0x00000000, 0x0000cb08, 0x0000cb08},
+ {0x0000aa9c, 0x00000000, 0x00000000, 0x0000cb0c, 0x0000cb0c},
+ {0x0000aaa0, 0x00000000, 0x00000000, 0x0000cb10, 0x0000cb10},
+ {0x0000aaa4, 0x00000000, 0x00000000, 0x0000cb14, 0x0000cb14},
+ {0x0000aaa8, 0x00000000, 0x00000000, 0x0000cb18, 0x0000cb18},
+ {0x0000aaac, 0x00000000, 0x00000000, 0x0000cb8c, 0x0000cb8c},
+ {0x0000aab0, 0x00000000, 0x00000000, 0x0000cb90, 0x0000cb90},
+ {0x0000aab4, 0x00000000, 0x00000000, 0x0000cf18, 0x0000cf18},
+ {0x0000aab8, 0x00000000, 0x00000000, 0x0000cf24, 0x0000cf24},
+ {0x0000aabc, 0x00000000, 0x00000000, 0x0000cf28, 0x0000cf28},
+ {0x0000aac0, 0x00000000, 0x00000000, 0x0000d314, 0x0000d314},
+ {0x0000aac4, 0x00000000, 0x00000000, 0x0000d318, 0x0000d318},
+ {0x0000aac8, 0x00000000, 0x00000000, 0x0000d38c, 0x0000d38c},
+ {0x0000aacc, 0x00000000, 0x00000000, 0x0000d390, 0x0000d390},
+ {0x0000aad0, 0x00000000, 0x00000000, 0x0000d394, 0x0000d394},
+ {0x0000aad4, 0x00000000, 0x00000000, 0x0000d398, 0x0000d398},
+ {0x0000aad8, 0x00000000, 0x00000000, 0x0000d3a4, 0x0000d3a4},
+ {0x0000aadc, 0x00000000, 0x00000000, 0x0000d3a8, 0x0000d3a8},
+ {0x0000aae0, 0x00000000, 0x00000000, 0x0000d3ac, 0x0000d3ac},
+ {0x0000aae4, 0x00000000, 0x00000000, 0x0000d3b0, 0x0000d3b0},
+ {0x0000aae8, 0x00000000, 0x00000000, 0x0000f380, 0x0000f380},
+ {0x0000aaec, 0x00000000, 0x00000000, 0x0000f384, 0x0000f384},
+ {0x0000aaf0, 0x00000000, 0x00000000, 0x0000f388, 0x0000f388},
+ {0x0000aaf4, 0x00000000, 0x00000000, 0x0000f710, 0x0000f710},
+ {0x0000aaf8, 0x00000000, 0x00000000, 0x0000f714, 0x0000f714},
+ {0x0000aafc, 0x00000000, 0x00000000, 0x0000f718, 0x0000f718},
+ {0x0000ab00, 0x00000000, 0x00000000, 0x0000fb10, 0x0000fb10},
+ {0x0000ab04, 0x00000000, 0x00000000, 0x0000fb14, 0x0000fb14},
+ {0x0000ab08, 0x00000000, 0x00000000, 0x0000fb18, 0x0000fb18},
+ {0x0000ab0c, 0x00000000, 0x00000000, 0x0000fb8c, 0x0000fb8c},
+ {0x0000ab10, 0x00000000, 0x00000000, 0x0000fb90, 0x0000fb90},
+ {0x0000ab14, 0x00000000, 0x00000000, 0x0000fb94, 0x0000fb94},
+ {0x0000ab18, 0x00000000, 0x00000000, 0x0000ff8c, 0x0000ff8c},
+ {0x0000ab1c, 0x00000000, 0x00000000, 0x0000ff90, 0x0000ff90},
+ {0x0000ab20, 0x00000000, 0x00000000, 0x0000ff94, 0x0000ff94},
+ {0x0000ab24, 0x00000000, 0x00000000, 0x0000ffa0, 0x0000ffa0},
+ {0x0000ab28, 0x00000000, 0x00000000, 0x0000ffa4, 0x0000ffa4},
+ {0x0000ab2c, 0x00000000, 0x00000000, 0x0000ffa8, 0x0000ffa8},
+ {0x0000ab30, 0x00000000, 0x00000000, 0x0000ffac, 0x0000ffac},
+ {0x0000ab34, 0x00000000, 0x00000000, 0x0000ffb0, 0x0000ffb0},
+ {0x0000ab38, 0x00000000, 0x00000000, 0x0000ffb4, 0x0000ffb4},
+ {0x0000ab3c, 0x00000000, 0x00000000, 0x0000ffa1, 0x0000ffa1},
+ {0x0000ab40, 0x00000000, 0x00000000, 0x0000ffa5, 0x0000ffa5},
+ {0x0000ab44, 0x00000000, 0x00000000, 0x0000ffa9, 0x0000ffa9},
+ {0x0000ab48, 0x00000000, 0x00000000, 0x0000ffad, 0x0000ffad},
+ {0x0000ab4c, 0x00000000, 0x00000000, 0x0000ffb1, 0x0000ffb1},
+ {0x0000ab50, 0x00000000, 0x00000000, 0x0000ffb5, 0x0000ffb5},
+ {0x0000ab54, 0x00000000, 0x00000000, 0x0000ffb9, 0x0000ffb9},
+ {0x0000ab58, 0x00000000, 0x00000000, 0x0000ffc5, 0x0000ffc5},
+ {0x0000ab5c, 0x00000000, 0x00000000, 0x0000ffc9, 0x0000ffc9},
+ {0x0000ab60, 0x00000000, 0x00000000, 0x0000ffcd, 0x0000ffcd},
+ {0x0000ab64, 0x00000000, 0x00000000, 0x0000ffd1, 0x0000ffd1},
+ {0x0000ab68, 0x00000000, 0x00000000, 0x0000ffd5, 0x0000ffd5},
+ {0x0000ab6c, 0x00000000, 0x00000000, 0x0000ffc2, 0x0000ffc2},
+ {0x0000ab70, 0x00000000, 0x00000000, 0x0000ffc6, 0x0000ffc6},
+ {0x0000ab74, 0x00000000, 0x00000000, 0x0000ffca, 0x0000ffca},
+ {0x0000ab78, 0x00000000, 0x00000000, 0x0000ffce, 0x0000ffce},
+ {0x0000ab7c, 0x00000000, 0x00000000, 0x0000ffd2, 0x0000ffd2},
+ {0x0000ab80, 0x00000000, 0x00000000, 0x0000ffd6, 0x0000ffd6},
+ {0x0000ab84, 0x00000000, 0x00000000, 0x0000ffda, 0x0000ffda},
+ {0x0000ab88, 0x00000000, 0x00000000, 0x0000ffc7, 0x0000ffc7},
+ {0x0000ab8c, 0x00000000, 0x00000000, 0x0000ffcb, 0x0000ffcb},
+ {0x0000ab90, 0x00000000, 0x00000000, 0x0000ffcf, 0x0000ffcf},
+ {0x0000ab94, 0x00000000, 0x00000000, 0x0000ffd3, 0x0000ffd3},
+ {0x0000ab98, 0x00000000, 0x00000000, 0x0000ffd7, 0x0000ffd7},
+ {0x0000ab9c, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000aba0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000aba4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000aba8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abac, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abb0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abb4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abb8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abbc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abc0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abc4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abc8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abcc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abd0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abd4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abd8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abdc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abe0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abe4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abe8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abec, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abf0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abf4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abf8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x0000abfc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb},
+ {0x00009848, 0x00000000, 0x00000000, 0x00001067, 0x00001067},
+ {0x0000a848, 0x00000000, 0x00000000, 0x00001067, 0x00001067},
};
static const u32 ar9287PciePhy_clkreq_always_on_L1_9287_1_1[][2] = {
@@ -2526,310 +2540,311 @@ static const u32 ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = {
{0x00004044, 0x00000000},
};
-static const u32 ar9271Modes_9271[][6] = {
- {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0},
- {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0},
- {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180},
- {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008},
- {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0},
- {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f},
- {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880},
- {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303},
- {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
- {0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
- {0x00009828, 0x3a020001, 0x3a020001, 0x3a020001, 0x3a020001, 0x3a020001},
- {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
- {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
- {0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e},
- {0x00009844, 0x0372161e, 0x0372161e, 0x03721620, 0x03721620, 0x037216a0},
- {0x00009848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059},
- {0x0000a848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059},
- {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2},
- {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e},
- {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e},
- {0x00009860, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
- {0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00},
- {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
- {0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881},
- {0x00009910, 0x30002310, 0x30002310, 0x30002310, 0x30002310, 0x30002310},
- {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0},
- {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016},
- {0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d},
- {0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1020, 0xffbc1020, 0xffbc1010},
- {0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x000099b8, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c},
- {0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00},
- {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
- {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77},
- {0x000099c8, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f},
- {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8},
- {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384},
- {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x00009a00, 0x00000000, 0x00000000, 0x00058084, 0x00058084, 0x00000000},
- {0x00009a04, 0x00000000, 0x00000000, 0x00058088, 0x00058088, 0x00000000},
- {0x00009a08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c, 0x00000000},
- {0x00009a0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100, 0x00000000},
- {0x00009a10, 0x00000000, 0x00000000, 0x00058104, 0x00058104, 0x00000000},
- {0x00009a14, 0x00000000, 0x00000000, 0x00058108, 0x00058108, 0x00000000},
- {0x00009a18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c, 0x00000000},
- {0x00009a1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110, 0x00000000},
- {0x00009a20, 0x00000000, 0x00000000, 0x00058114, 0x00058114, 0x00000000},
- {0x00009a24, 0x00000000, 0x00000000, 0x00058180, 0x00058180, 0x00000000},
- {0x00009a28, 0x00000000, 0x00000000, 0x00058184, 0x00058184, 0x00000000},
- {0x00009a2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188, 0x00000000},
- {0x00009a30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c, 0x00000000},
- {0x00009a34, 0x00000000, 0x00000000, 0x00058190, 0x00058190, 0x00000000},
- {0x00009a38, 0x00000000, 0x00000000, 0x00058194, 0x00058194, 0x00000000},
- {0x00009a3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0, 0x00000000},
- {0x00009a40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c, 0x00000000},
- {0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000},
- {0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000},
- {0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000},
- {0x00009a50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000},
- {0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000},
- {0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000},
- {0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000},
- {0x00009a60, 0x00000000, 0x00000000, 0x00058308, 0x00058308, 0x00000000},
- {0x00009a64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c, 0x00000000},
- {0x00009a68, 0x00000000, 0x00000000, 0x00058380, 0x00058380, 0x00000000},
- {0x00009a6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384, 0x00000000},
- {0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000},
- {0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000},
- {0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000},
- {0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000},
- {0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000},
- {0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000},
- {0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000},
- {0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000},
- {0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000},
- {0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000},
- {0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000},
- {0x00009a9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84, 0x00000000},
- {0x00009aa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88, 0x00000000},
- {0x00009aa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c, 0x00000000},
- {0x00009aa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90, 0x00000000},
- {0x00009aac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80, 0x00000000},
- {0x00009ab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84, 0x00000000},
- {0x00009ab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88, 0x00000000},
- {0x00009ab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c, 0x00000000},
- {0x00009abc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90, 0x00000000},
- {0x00009ac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c, 0x00000000},
- {0x00009ac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310, 0x00000000},
- {0x00009ac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384, 0x00000000},
- {0x00009acc, 0x00000000, 0x00000000, 0x000db388, 0x000db388, 0x00000000},
- {0x00009ad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324, 0x00000000},
- {0x00009ad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704, 0x00000000},
- {0x00009ad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4, 0x00000000},
- {0x00009adc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8, 0x00000000},
- {0x00009ae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710, 0x00000000},
- {0x00009ae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714, 0x00000000},
- {0x00009ae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720, 0x00000000},
- {0x00009aec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724, 0x00000000},
- {0x00009af0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728, 0x00000000},
- {0x00009af4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c, 0x00000000},
- {0x00009af8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0, 0x00000000},
- {0x00009afc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4, 0x00000000},
- {0x00009b00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8, 0x00000000},
- {0x00009b04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0, 0x00000000},
- {0x00009b08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4, 0x00000000},
- {0x00009b0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8, 0x00000000},
- {0x00009b10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5, 0x00000000},
- {0x00009b14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9, 0x00000000},
- {0x00009b18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad, 0x00000000},
- {0x00009b1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1, 0x00000000},
- {0x00009b20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5, 0x00000000},
- {0x00009b24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9, 0x00000000},
- {0x00009b28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5, 0x00000000},
- {0x00009b2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9, 0x00000000},
- {0x00009b30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1, 0x00000000},
- {0x00009b34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5, 0x00000000},
- {0x00009b38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9, 0x00000000},
- {0x00009b3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6, 0x00000000},
- {0x00009b40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca, 0x00000000},
- {0x00009b44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce, 0x00000000},
- {0x00009b48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2, 0x00000000},
- {0x00009b4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6, 0x00000000},
- {0x00009b50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3, 0x00000000},
- {0x00009b54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7, 0x00000000},
- {0x00009b58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb, 0x00000000},
- {0x00009b5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf, 0x00000000},
- {0x00009b60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7, 0x00000000},
- {0x00009b64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009b9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009ba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009ba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009ba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009be0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009be4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009be8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x00009bfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000aa00, 0x00000000, 0x00000000, 0x00058084, 0x00058084, 0x00000000},
- {0x0000aa04, 0x00000000, 0x00000000, 0x00058088, 0x00058088, 0x00000000},
- {0x0000aa08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c, 0x00000000},
- {0x0000aa0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100, 0x00000000},
- {0x0000aa10, 0x00000000, 0x00000000, 0x00058104, 0x00058104, 0x00000000},
- {0x0000aa14, 0x00000000, 0x00000000, 0x00058108, 0x00058108, 0x00000000},
- {0x0000aa18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c, 0x00000000},
- {0x0000aa1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110, 0x00000000},
- {0x0000aa20, 0x00000000, 0x00000000, 0x00058114, 0x00058114, 0x00000000},
- {0x0000aa24, 0x00000000, 0x00000000, 0x00058180, 0x00058180, 0x00000000},
- {0x0000aa28, 0x00000000, 0x00000000, 0x00058184, 0x00058184, 0x00000000},
- {0x0000aa2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188, 0x00000000},
- {0x0000aa30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c, 0x00000000},
- {0x0000aa34, 0x00000000, 0x00000000, 0x00058190, 0x00058190, 0x00000000},
- {0x0000aa38, 0x00000000, 0x00000000, 0x00058194, 0x00058194, 0x00000000},
- {0x0000aa3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0, 0x00000000},
- {0x0000aa40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c, 0x00000000},
- {0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000},
- {0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000},
- {0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000},
- {0x0000aa50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000},
- {0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000},
- {0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000},
- {0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000},
- {0x0000aa60, 0x00000000, 0x00000000, 0x00058308, 0x00058308, 0x00000000},
- {0x0000aa64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c, 0x00000000},
- {0x0000aa68, 0x00000000, 0x00000000, 0x00058380, 0x00058380, 0x00000000},
- {0x0000aa6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384, 0x00000000},
- {0x0000aa70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000},
- {0x0000aa74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000},
- {0x0000aa78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000},
- {0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000},
- {0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000},
- {0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000},
- {0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000},
- {0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000},
- {0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000},
- {0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000},
- {0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000},
- {0x0000aa9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84, 0x00000000},
- {0x0000aaa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88, 0x00000000},
- {0x0000aaa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c, 0x00000000},
- {0x0000aaa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90, 0x00000000},
- {0x0000aaac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80, 0x00000000},
- {0x0000aab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84, 0x00000000},
- {0x0000aab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88, 0x00000000},
- {0x0000aab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c, 0x00000000},
- {0x0000aabc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90, 0x00000000},
- {0x0000aac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c, 0x00000000},
- {0x0000aac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310, 0x00000000},
- {0x0000aac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384, 0x00000000},
- {0x0000aacc, 0x00000000, 0x00000000, 0x000db388, 0x000db388, 0x00000000},
- {0x0000aad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324, 0x00000000},
- {0x0000aad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704, 0x00000000},
- {0x0000aad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4, 0x00000000},
- {0x0000aadc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8, 0x00000000},
- {0x0000aae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710, 0x00000000},
- {0x0000aae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714, 0x00000000},
- {0x0000aae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720, 0x00000000},
- {0x0000aaec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724, 0x00000000},
- {0x0000aaf0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728, 0x00000000},
- {0x0000aaf4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c, 0x00000000},
- {0x0000aaf8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0, 0x00000000},
- {0x0000aafc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4, 0x00000000},
- {0x0000ab00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8, 0x00000000},
- {0x0000ab04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0, 0x00000000},
- {0x0000ab08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4, 0x00000000},
- {0x0000ab0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8, 0x00000000},
- {0x0000ab10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5, 0x00000000},
- {0x0000ab14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9, 0x00000000},
- {0x0000ab18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad, 0x00000000},
- {0x0000ab1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1, 0x00000000},
- {0x0000ab20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5, 0x00000000},
- {0x0000ab24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9, 0x00000000},
- {0x0000ab28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5, 0x00000000},
- {0x0000ab2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9, 0x00000000},
- {0x0000ab30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1, 0x00000000},
- {0x0000ab34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5, 0x00000000},
- {0x0000ab38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9, 0x00000000},
- {0x0000ab3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6, 0x00000000},
- {0x0000ab40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca, 0x00000000},
- {0x0000ab44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce, 0x00000000},
- {0x0000ab48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2, 0x00000000},
- {0x0000ab4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6, 0x00000000},
- {0x0000ab50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3, 0x00000000},
- {0x0000ab54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7, 0x00000000},
- {0x0000ab58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb, 0x00000000},
- {0x0000ab5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf, 0x00000000},
- {0x0000ab60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7, 0x00000000},
- {0x0000ab64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000ab9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000aba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000aba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000aba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abe0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abe4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abe8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000abfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000},
- {0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004},
- {0x0000a20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000},
- {0x0000b20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000},
- {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
- {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000},
- {0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000},
- {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e},
+static const u32 ar9271Modes_9271[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b},
+ {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
+ {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300},
+ {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
+ {0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
+ {0x00009828, 0x3a020001, 0x3a020001, 0x3a020001, 0x3a020001},
+ {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
+ {0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e},
+ {0x00009844, 0x0372161e, 0x0372161e, 0x03721620, 0x03721620},
+ {0x00009848, 0x00001066, 0x00001066, 0x00001053, 0x00001053},
+ {0x0000a848, 0x00001066, 0x00001066, 0x00001053, 0x00001053},
+ {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2},
+ {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e},
+ {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e},
+ {0x00009860, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
+ {0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00},
+ {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+ {0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+ {0x00009910, 0x30002310, 0x30002310, 0x30002310, 0x30002310},
+ {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+ {0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d},
+ {0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1020, 0xffbc1020},
+ {0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x000099b8, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c},
+ {0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00},
+ {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+ {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77},
+ {0x000099c8, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f},
+ {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8},
+ {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384},
+ {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009a00, 0x00000000, 0x00000000, 0x00058084, 0x00058084},
+ {0x00009a04, 0x00000000, 0x00000000, 0x00058088, 0x00058088},
+ {0x00009a08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c},
+ {0x00009a0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100},
+ {0x00009a10, 0x00000000, 0x00000000, 0x00058104, 0x00058104},
+ {0x00009a14, 0x00000000, 0x00000000, 0x00058108, 0x00058108},
+ {0x00009a18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c},
+ {0x00009a1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110},
+ {0x00009a20, 0x00000000, 0x00000000, 0x00058114, 0x00058114},
+ {0x00009a24, 0x00000000, 0x00000000, 0x00058180, 0x00058180},
+ {0x00009a28, 0x00000000, 0x00000000, 0x00058184, 0x00058184},
+ {0x00009a2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188},
+ {0x00009a30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c},
+ {0x00009a34, 0x00000000, 0x00000000, 0x00058190, 0x00058190},
+ {0x00009a38, 0x00000000, 0x00000000, 0x00058194, 0x00058194},
+ {0x00009a3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0},
+ {0x00009a40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c},
+ {0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8},
+ {0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284},
+ {0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288},
+ {0x00009a50, 0x00000000, 0x00000000, 0x00058224, 0x00058224},
+ {0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290},
+ {0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300},
+ {0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304},
+ {0x00009a60, 0x00000000, 0x00000000, 0x00058308, 0x00058308},
+ {0x00009a64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c},
+ {0x00009a68, 0x00000000, 0x00000000, 0x00058380, 0x00058380},
+ {0x00009a6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384},
+ {0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700},
+ {0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704},
+ {0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708},
+ {0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c},
+ {0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780},
+ {0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784},
+ {0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00},
+ {0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04},
+ {0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08},
+ {0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c},
+ {0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80},
+ {0x00009a9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84},
+ {0x00009aa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88},
+ {0x00009aa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c},
+ {0x00009aa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90},
+ {0x00009aac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80},
+ {0x00009ab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84},
+ {0x00009ab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88},
+ {0x00009ab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c},
+ {0x00009abc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90},
+ {0x00009ac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c},
+ {0x00009ac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310},
+ {0x00009ac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384},
+ {0x00009acc, 0x00000000, 0x00000000, 0x000db388, 0x000db388},
+ {0x00009ad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324},
+ {0x00009ad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704},
+ {0x00009ad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4},
+ {0x00009adc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8},
+ {0x00009ae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710},
+ {0x00009ae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714},
+ {0x00009ae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720},
+ {0x00009aec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724},
+ {0x00009af0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728},
+ {0x00009af4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c},
+ {0x00009af8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0},
+ {0x00009afc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4},
+ {0x00009b00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8},
+ {0x00009b04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0},
+ {0x00009b08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4},
+ {0x00009b0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8},
+ {0x00009b10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5},
+ {0x00009b14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9},
+ {0x00009b18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad},
+ {0x00009b1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1},
+ {0x00009b20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5},
+ {0x00009b24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9},
+ {0x00009b28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5},
+ {0x00009b2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9},
+ {0x00009b30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1},
+ {0x00009b34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5},
+ {0x00009b38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9},
+ {0x00009b3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6},
+ {0x00009b40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca},
+ {0x00009b44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce},
+ {0x00009b48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2},
+ {0x00009b4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6},
+ {0x00009b50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3},
+ {0x00009b54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7},
+ {0x00009b58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb},
+ {0x00009b5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf},
+ {0x00009b60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7},
+ {0x00009b64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009b9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009ba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009ba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009ba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009be0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009be4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009be8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x00009bfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000aa00, 0x00000000, 0x00000000, 0x00058084, 0x00058084},
+ {0x0000aa04, 0x00000000, 0x00000000, 0x00058088, 0x00058088},
+ {0x0000aa08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c},
+ {0x0000aa0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100},
+ {0x0000aa10, 0x00000000, 0x00000000, 0x00058104, 0x00058104},
+ {0x0000aa14, 0x00000000, 0x00000000, 0x00058108, 0x00058108},
+ {0x0000aa18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c},
+ {0x0000aa1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110},
+ {0x0000aa20, 0x00000000, 0x00000000, 0x00058114, 0x00058114},
+ {0x0000aa24, 0x00000000, 0x00000000, 0x00058180, 0x00058180},
+ {0x0000aa28, 0x00000000, 0x00000000, 0x00058184, 0x00058184},
+ {0x0000aa2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188},
+ {0x0000aa30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c},
+ {0x0000aa34, 0x00000000, 0x00000000, 0x00058190, 0x00058190},
+ {0x0000aa38, 0x00000000, 0x00000000, 0x00058194, 0x00058194},
+ {0x0000aa3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0},
+ {0x0000aa40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c},
+ {0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8},
+ {0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284},
+ {0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288},
+ {0x0000aa50, 0x00000000, 0x00000000, 0x00058224, 0x00058224},
+ {0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290},
+ {0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300},
+ {0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304},
+ {0x0000aa60, 0x00000000, 0x00000000, 0x00058308, 0x00058308},
+ {0x0000aa64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c},
+ {0x0000aa68, 0x00000000, 0x00000000, 0x00058380, 0x00058380},
+ {0x0000aa6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384},
+ {0x0000aa70, 0x00000000, 0x00000000, 0x00068700, 0x00068700},
+ {0x0000aa74, 0x00000000, 0x00000000, 0x00068704, 0x00068704},
+ {0x0000aa78, 0x00000000, 0x00000000, 0x00068708, 0x00068708},
+ {0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c},
+ {0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780},
+ {0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784},
+ {0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00},
+ {0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04},
+ {0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08},
+ {0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c},
+ {0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80},
+ {0x0000aa9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84},
+ {0x0000aaa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88},
+ {0x0000aaa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c},
+ {0x0000aaa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90},
+ {0x0000aaac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80},
+ {0x0000aab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84},
+ {0x0000aab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88},
+ {0x0000aab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c},
+ {0x0000aabc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90},
+ {0x0000aac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c},
+ {0x0000aac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310},
+ {0x0000aac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384},
+ {0x0000aacc, 0x00000000, 0x00000000, 0x000db388, 0x000db388},
+ {0x0000aad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324},
+ {0x0000aad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704},
+ {0x0000aad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4},
+ {0x0000aadc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8},
+ {0x0000aae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710},
+ {0x0000aae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714},
+ {0x0000aae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720},
+ {0x0000aaec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724},
+ {0x0000aaf0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728},
+ {0x0000aaf4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c},
+ {0x0000aaf8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0},
+ {0x0000aafc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4},
+ {0x0000ab00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8},
+ {0x0000ab04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0},
+ {0x0000ab08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4},
+ {0x0000ab0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8},
+ {0x0000ab10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5},
+ {0x0000ab14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9},
+ {0x0000ab18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad},
+ {0x0000ab1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1},
+ {0x0000ab20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5},
+ {0x0000ab24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9},
+ {0x0000ab28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5},
+ {0x0000ab2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9},
+ {0x0000ab30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1},
+ {0x0000ab34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5},
+ {0x0000ab38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9},
+ {0x0000ab3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6},
+ {0x0000ab40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca},
+ {0x0000ab44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce},
+ {0x0000ab48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2},
+ {0x0000ab4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6},
+ {0x0000ab50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3},
+ {0x0000ab54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7},
+ {0x0000ab58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb},
+ {0x0000ab5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf},
+ {0x0000ab60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7},
+ {0x0000ab64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000ab9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000aba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000aba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000aba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abe0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abe4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abe8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000abfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db},
+ {0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004},
+ {0x0000a20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000},
+ {0x0000b20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000},
+ {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a},
+ {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000},
+ {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e},
};
static const u32 ar9271Common_9271[][2] = {
@@ -3175,91 +3190,95 @@ static const u32 ar9271Common_japan_2484_cck_fir_coeff_9271[][2] = {
{0x0000a1fc, 0xca9228ee},
};
-static const u32 ar9271Modes_9271_1_0_only[][6] = {
- {0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311},
- {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
+static const u32 ar9271Modes_9271_1_0_only[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311},
+ {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001},
};
-static const u32 ar9271Modes_9271_ANI_reg[][6] = {
- {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2},
- {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e},
- {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e},
- {0x0000986c, 0x06903881, 0x06903881, 0x06903881, 0x06903881, 0x06903881},
- {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
- {0x0000a208, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8},
- {0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d},
- {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+static const u32 ar9271Modes_9271_ANI_reg[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2},
+ {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e},
+ {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e},
+ {0x0000986c, 0x06903881, 0x06903881, 0x06903881, 0x06903881},
+ {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+ {0x0000a208, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8},
+ {0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d},
+ {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
};
-static const u32 ar9271Modes_normal_power_tx_gain_9271[][6] = {
- {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000},
- {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000},
- {0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000},
- {0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610, 0x00000000},
- {0x0000a314, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0, 0x00000000},
- {0x0000a318, 0x00000000, 0x00000000, 0x00039758, 0x00039758, 0x00000000},
- {0x0000a31c, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759, 0x00000000},
- {0x0000a320, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a, 0x00000000},
- {0x0000a324, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c, 0x00000000},
- {0x0000a328, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e, 0x00000000},
- {0x0000a32c, 0x00000000, 0x00000000, 0x0004979f, 0x0004979f, 0x00000000},
- {0x0000a330, 0x00000000, 0x00000000, 0x0004d7df, 0x0004d7df, 0x00000000},
- {0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000},
- {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000},
- {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000},
- {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x00007838, 0x00000029, 0x00000029, 0x00000029, 0x00000029, 0x00000029},
- {0x00007824, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff},
- {0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4},
- {0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04},
- {0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a218652, 0x0a218652, 0x0a22a652},
- {0x0000a278, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd},
- {0x0000a27c, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd},
- {0x0000a394, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd},
- {0x0000a398, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd},
- {0x0000a3dc, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd},
- {0x0000a3e0, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd},
+static const u32 ar9271Modes_normal_power_tx_gain_9271[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200},
+ {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208},
+ {0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608},
+ {0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610},
+ {0x0000a314, 0x00000000, 0x00000000, 0x00024650, 0x00024650},
+ {0x0000a318, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0},
+ {0x0000a31c, 0x00000000, 0x00000000, 0x000316d2, 0x000316d2},
+ {0x0000a320, 0x00000000, 0x00000000, 0x00039758, 0x00039758},
+ {0x0000a324, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759},
+ {0x0000a328, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a},
+ {0x0000a32c, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c},
+ {0x0000a330, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e},
+ {0x0000a334, 0x000368de, 0x000368de, 0x0004979f, 0x0004979f},
+ {0x0000a338, 0x0003891e, 0x0003891e, 0x0004d7df, 0x0004d7df},
+ {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e},
+ {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x00007838, 0x00000029, 0x00000029, 0x00000029, 0x00000029},
+ {0x00007824, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff},
+ {0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4},
+ {0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04},
+ {0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a21c652, 0x0a21c652},
+ {0x0000a278, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd},
+ {0x0000a27c, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd},
+ {0x0000a394, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd},
+ {0x0000a398, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd},
+ {0x0000a3dc, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd},
+ {0x0000a3e0, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd},
};
-static const u32 ar9271Modes_high_power_tx_gain_9271[][6] = {
- {0x0000a300, 0x00000000, 0x00000000, 0x00010000, 0x00010000, 0x00000000},
- {0x0000a304, 0x00000000, 0x00000000, 0x00016200, 0x00016200, 0x00000000},
- {0x0000a308, 0x00000000, 0x00000000, 0x00018201, 0x00018201, 0x00000000},
- {0x0000a30c, 0x00000000, 0x00000000, 0x0001b240, 0x0001b240, 0x00000000},
- {0x0000a310, 0x00000000, 0x00000000, 0x0001d241, 0x0001d241, 0x00000000},
- {0x0000a314, 0x00000000, 0x00000000, 0x0001f600, 0x0001f600, 0x00000000},
- {0x0000a318, 0x00000000, 0x00000000, 0x00022800, 0x00022800, 0x00000000},
- {0x0000a31c, 0x00000000, 0x00000000, 0x00026802, 0x00026802, 0x00000000},
- {0x0000a320, 0x00000000, 0x00000000, 0x0002b805, 0x0002b805, 0x00000000},
- {0x0000a324, 0x00000000, 0x00000000, 0x0002ea41, 0x0002ea41, 0x00000000},
- {0x0000a328, 0x00000000, 0x00000000, 0x00038b00, 0x00038b00, 0x00000000},
- {0x0000a32c, 0x00000000, 0x00000000, 0x0003ab40, 0x0003ab40, 0x00000000},
- {0x0000a330, 0x00000000, 0x00000000, 0x0003cd80, 0x0003cd80, 0x00000000},
- {0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000},
- {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000},
- {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000},
- {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000},
- {0x00007838, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b},
- {0x00007824, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff},
- {0x0000786c, 0x08609eb6, 0x08609eb6, 0x08609eba, 0x08609eba, 0x08609eb6},
- {0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00},
- {0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a214652, 0x0a214652, 0x0a22a652},
- {0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
- {0x0000a27c, 0x05018063, 0x05038063, 0x05018063, 0x05018063, 0x05018063},
- {0x0000a394, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63},
- {0x0000a398, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063},
- {0x0000a3dc, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63},
- {0x0000a3e0, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063},
+static const u32 ar9271Modes_high_power_tx_gain_9271[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a300, 0x00000000, 0x00000000, 0x00010000, 0x00010000},
+ {0x0000a304, 0x00000000, 0x00000000, 0x00016200, 0x00016200},
+ {0x0000a308, 0x00000000, 0x00000000, 0x00018201, 0x00018201},
+ {0x0000a30c, 0x00000000, 0x00000000, 0x0001b240, 0x0001b240},
+ {0x0000a310, 0x00000000, 0x00000000, 0x0001d241, 0x0001d241},
+ {0x0000a314, 0x00000000, 0x00000000, 0x0001f600, 0x0001f600},
+ {0x0000a318, 0x00000000, 0x00000000, 0x00022800, 0x00022800},
+ {0x0000a31c, 0x00000000, 0x00000000, 0x00026802, 0x00026802},
+ {0x0000a320, 0x00000000, 0x00000000, 0x0002b805, 0x0002b805},
+ {0x0000a324, 0x00000000, 0x00000000, 0x0002ea41, 0x0002ea41},
+ {0x0000a328, 0x00000000, 0x00000000, 0x00038b00, 0x00038b00},
+ {0x0000a32c, 0x00000000, 0x00000000, 0x0003ab40, 0x0003ab40},
+ {0x0000a330, 0x00000000, 0x00000000, 0x0003cd80, 0x0003cd80},
+ {0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de},
+ {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e},
+ {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e},
+ {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df},
+ {0x00007838, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b},
+ {0x00007824, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff},
+ {0x0000786c, 0x08609eb6, 0x08609eb6, 0x08609eba, 0x08609eba},
+ {0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00},
+ {0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a214652, 0x0a214652},
+ {0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7},
+ {0x0000a27c, 0x05018063, 0x05038063, 0x05018063, 0x05018063},
+ {0x0000a394, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63},
+ {0x0000a398, 0x00000063, 0x00000063, 0x00000063, 0x00000063},
+ {0x0000a3dc, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63},
+ {0x0000a3e0, 0x00000063, 0x00000063, 0x00000063, 0x00000063},
};
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
index 077e8a6983fa..b5920168606d 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
@@ -15,6 +15,7 @@
*/
#include "hw.h"
+#include <linux/export.h>
#define AR_BufLen 0x00000fff
@@ -28,11 +29,6 @@ static void ar9002_hw_set_desc_link(void *ds, u32 ds_link)
((struct ath_desc*) ds)->ds_link = ds_link;
}
-static void ar9002_hw_get_desc_link(void *ds, u32 **ds_link)
-{
- *ds_link = &((struct ath_desc *)ds)->ds_link;
-}
-
static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
{
u32 isr = 0;
@@ -175,33 +171,104 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
return true;
}
-static void ar9002_hw_fill_txdesc(struct ath_hw *ah, void *ds, u32 seglen,
- bool is_firstseg, bool is_lastseg,
- const void *ds0, dma_addr_t buf_addr,
- unsigned int qcu)
+static void
+ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
{
struct ar5416_desc *ads = AR5416DESC(ds);
+ u32 ctl1, ctl6;
- ads->ds_data = buf_addr;
-
- if (is_firstseg) {
- ads->ds_ctl1 |= seglen | (is_lastseg ? 0 : AR_TxMore);
- } else if (is_lastseg) {
- ads->ds_ctl0 = 0;
- ads->ds_ctl1 = seglen;
- ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2;
- ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3;
- } else {
- ads->ds_ctl0 = 0;
- ads->ds_ctl1 = seglen | AR_TxMore;
- ads->ds_ctl2 = 0;
- ads->ds_ctl3 = 0;
- }
ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
ads->ds_txstatus2 = ads->ds_txstatus3 = 0;
ads->ds_txstatus4 = ads->ds_txstatus5 = 0;
ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
+
+ ACCESS_ONCE(ads->ds_link) = i->link;
+ ACCESS_ONCE(ads->ds_data) = i->buf_addr[0];
+
+ ctl1 = i->buf_len[0] | (i->is_last ? 0 : AR_TxMore);
+ ctl6 = SM(i->keytype, AR_EncrType);
+
+ if (AR_SREV_9285(ah)) {
+ ads->ds_ctl8 = 0;
+ ads->ds_ctl9 = 0;
+ ads->ds_ctl10 = 0;
+ ads->ds_ctl11 = 0;
+ }
+
+ if ((i->is_first || i->is_last) &&
+ i->aggr != AGGR_BUF_MIDDLE && i->aggr != AGGR_BUF_LAST) {
+ ACCESS_ONCE(ads->ds_ctl2) = set11nTries(i->rates, 0)
+ | set11nTries(i->rates, 1)
+ | set11nTries(i->rates, 2)
+ | set11nTries(i->rates, 3)
+ | (i->dur_update ? AR_DurUpdateEna : 0)
+ | SM(0, AR_BurstDur);
+
+ ACCESS_ONCE(ads->ds_ctl3) = set11nRate(i->rates, 0)
+ | set11nRate(i->rates, 1)
+ | set11nRate(i->rates, 2)
+ | set11nRate(i->rates, 3);
+ } else {
+ ACCESS_ONCE(ads->ds_ctl2) = 0;
+ ACCESS_ONCE(ads->ds_ctl3) = 0;
+ }
+
+ if (!i->is_first) {
+ ACCESS_ONCE(ads->ds_ctl0) = 0;
+ ACCESS_ONCE(ads->ds_ctl1) = ctl1;
+ ACCESS_ONCE(ads->ds_ctl6) = ctl6;
+ return;
+ }
+
+ ctl1 |= (i->keyix != ATH9K_TXKEYIX_INVALID ? SM(i->keyix, AR_DestIdx) : 0)
+ | SM(i->type, AR_FrameType)
+ | (i->flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0)
+ | (i->flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0)
+ | (i->flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0);
+
+ switch (i->aggr) {
+ case AGGR_BUF_FIRST:
+ ctl6 |= SM(i->aggr_len, AR_AggrLen);
+ /* fall through */
+ case AGGR_BUF_MIDDLE:
+ ctl1 |= AR_IsAggr | AR_MoreAggr;
+ ctl6 |= SM(i->ndelim, AR_PadDelim);
+ break;
+ case AGGR_BUF_LAST:
+ ctl1 |= AR_IsAggr;
+ break;
+ case AGGR_BUF_NONE:
+ break;
+ }
+
+ ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen)
+ | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
+ | SM(i->txpower, AR_XmitPower)
+ | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
+ | (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
+ | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
+ | (i->flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0)
+ | (i->flags & ATH9K_TXDESC_RTSENA ? AR_RTSEnable :
+ (i->flags & ATH9K_TXDESC_CTSENA ? AR_CTSEnable : 0));
+
+ ACCESS_ONCE(ads->ds_ctl1) = ctl1;
+ ACCESS_ONCE(ads->ds_ctl6) = ctl6;
+
+ if (i->aggr == AGGR_BUF_MIDDLE || i->aggr == AGGR_BUF_LAST)
+ return;
+
+ ACCESS_ONCE(ads->ds_ctl4) = set11nPktDurRTSCTS(i->rates, 0)
+ | set11nPktDurRTSCTS(i->rates, 1);
+
+ ACCESS_ONCE(ads->ds_ctl5) = set11nPktDurRTSCTS(i->rates, 2)
+ | set11nPktDurRTSCTS(i->rates, 3);
+
+ ACCESS_ONCE(ads->ds_ctl7) = set11nRateFlags(i->rates, 0)
+ | set11nRateFlags(i->rates, 1)
+ | set11nRateFlags(i->rates, 2)
+ | set11nRateFlags(i->rates, 3)
+ | SM(i->rtscts_rate, AR_RTSCTSRate);
}
static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
@@ -276,145 +343,6 @@ static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
return 0;
}
-static void ar9002_hw_set11n_txdesc(struct ath_hw *ah, void *ds,
- u32 pktLen, enum ath9k_pkt_type type,
- u32 txPower, u32 keyIx,
- enum ath9k_key_type keyType, u32 flags)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
-
- if (txPower > 63)
- txPower = 63;
-
- ads->ds_ctl0 = (pktLen & AR_FrameLen)
- | (flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
- | SM(txPower, AR_XmitPower)
- | (flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
- | (flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
- | (keyIx != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0);
-
- ads->ds_ctl1 =
- (keyIx != ATH9K_TXKEYIX_INVALID ? SM(keyIx, AR_DestIdx) : 0)
- | SM(type, AR_FrameType)
- | (flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0)
- | (flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0)
- | (flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0);
-
- ads->ds_ctl6 = SM(keyType, AR_EncrType);
-
- if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) {
- ads->ds_ctl8 = 0;
- ads->ds_ctl9 = 0;
- ads->ds_ctl10 = 0;
- ads->ds_ctl11 = 0;
- }
-}
-
-static void ar9002_hw_set_clrdmask(struct ath_hw *ah, void *ds, bool val)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
-
- if (val)
- ads->ds_ctl0 |= AR_ClrDestMask;
- else
- ads->ds_ctl0 &= ~AR_ClrDestMask;
-}
-
-static void ar9002_hw_set11n_ratescenario(struct ath_hw *ah, void *ds,
- void *lastds,
- u32 durUpdateEn, u32 rtsctsRate,
- u32 rtsctsDuration,
- struct ath9k_11n_rate_series series[],
- u32 nseries, u32 flags)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
- struct ar5416_desc *last_ads = AR5416DESC(lastds);
- u32 ds_ctl0;
-
- if (flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA)) {
- ds_ctl0 = ads->ds_ctl0;
-
- if (flags & ATH9K_TXDESC_RTSENA) {
- ds_ctl0 &= ~AR_CTSEnable;
- ds_ctl0 |= AR_RTSEnable;
- } else {
- ds_ctl0 &= ~AR_RTSEnable;
- ds_ctl0 |= AR_CTSEnable;
- }
-
- ads->ds_ctl0 = ds_ctl0;
- } else {
- ads->ds_ctl0 =
- (ads->ds_ctl0 & ~(AR_RTSEnable | AR_CTSEnable));
- }
-
- ads->ds_ctl2 = set11nTries(series, 0)
- | set11nTries(series, 1)
- | set11nTries(series, 2)
- | set11nTries(series, 3)
- | (durUpdateEn ? AR_DurUpdateEna : 0)
- | SM(0, AR_BurstDur);
-
- ads->ds_ctl3 = set11nRate(series, 0)
- | set11nRate(series, 1)
- | set11nRate(series, 2)
- | set11nRate(series, 3);
-
- ads->ds_ctl4 = set11nPktDurRTSCTS(series, 0)
- | set11nPktDurRTSCTS(series, 1);
-
- ads->ds_ctl5 = set11nPktDurRTSCTS(series, 2)
- | set11nPktDurRTSCTS(series, 3);
-
- ads->ds_ctl7 = set11nRateFlags(series, 0)
- | set11nRateFlags(series, 1)
- | set11nRateFlags(series, 2)
- | set11nRateFlags(series, 3)
- | SM(rtsctsRate, AR_RTSCTSRate);
- last_ads->ds_ctl2 = ads->ds_ctl2;
- last_ads->ds_ctl3 = ads->ds_ctl3;
-}
-
-static void ar9002_hw_set11n_aggr_first(struct ath_hw *ah, void *ds,
- u32 aggrLen)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
-
- ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr);
- ads->ds_ctl6 &= ~AR_AggrLen;
- ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen);
-}
-
-static void ar9002_hw_set11n_aggr_middle(struct ath_hw *ah, void *ds,
- u32 numDelims)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
- unsigned int ctl6;
-
- ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr);
-
- ctl6 = ads->ds_ctl6;
- ctl6 &= ~AR_PadDelim;
- ctl6 |= SM(numDelims, AR_PadDelim);
- ads->ds_ctl6 = ctl6;
-}
-
-static void ar9002_hw_set11n_aggr_last(struct ath_hw *ah, void *ds)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
-
- ads->ds_ctl1 |= AR_IsAggr;
- ads->ds_ctl1 &= ~AR_MoreAggr;
- ads->ds_ctl6 &= ~AR_PadDelim;
-}
-
-static void ar9002_hw_clr11n_aggr(struct ath_hw *ah, void *ds)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
-
- ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr);
-}
-
void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 size, u32 flags)
{
@@ -437,15 +365,7 @@ void ar9002_hw_attach_mac_ops(struct ath_hw *ah)
ops->rx_enable = ar9002_hw_rx_enable;
ops->set_desc_link = ar9002_hw_set_desc_link;
- ops->get_desc_link = ar9002_hw_get_desc_link;
ops->get_isr = ar9002_hw_get_isr;
- ops->fill_txdesc = ar9002_hw_fill_txdesc;
+ ops->set_txdesc = ar9002_set_txdesc;
ops->proc_txdesc = ar9002_hw_proc_txdesc;
- ops->set11n_txdesc = ar9002_hw_set11n_txdesc;
- ops->set11n_ratescenario = ar9002_hw_set11n_ratescenario;
- ops->set11n_aggr_first = ar9002_hw_set11n_aggr_first;
- ops->set11n_aggr_middle = ar9002_hw_set11n_aggr_middle;
- ops->set11n_aggr_last = ar9002_hw_set11n_aggr_last;
- ops->clr11n_aggr = ar9002_hw_clr11n_aggr;
- ops->set_clrdmask = ar9002_hw_set_clrdmask;
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
index 2fe0a34cbabc..3cbbb033fcea 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
@@ -111,7 +111,9 @@ static int ar9002_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
switch (ah->eep_ops->get_eeprom(ah, EEP_FRAC_N_5G)) {
case 0:
- if ((freq % 20) == 0)
+ if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan))
+ aModeRefSel = 0;
+ else if ((freq % 20) == 0)
aModeRefSel = 3;
else if ((freq % 10) == 0)
aModeRefSel = 2;
@@ -129,8 +131,9 @@ static int ar9002_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
channelSel = CHANSEL_5G(freq);
/* RefDivA setting */
- REG_RMW_FIELD(ah, AR_AN_SYNTH9,
- AR_AN_SYNTH9_REFDIVA, refDivA);
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_SYNTH9,
+ AR_AN_SYNTH9_REFDIVA,
+ AR_AN_SYNTH9_REFDIVA_S, refDivA);
}
@@ -447,26 +450,27 @@ static void ar9002_olc_init(struct ath_hw *ah)
static u32 ar9002_hw_compute_pll_control(struct ath_hw *ah,
struct ath9k_channel *chan)
{
+ int ref_div = 5;
+ int pll_div = 0x2c;
u32 pll;
- pll = SM(0x5, AR_RTC_9160_PLL_REFDIV);
+ if (chan && IS_CHAN_5GHZ(chan) && !IS_CHAN_A_FAST_CLOCK(ah, chan)) {
+ if (AR_SREV_9280_20(ah)) {
+ ref_div = 10;
+ pll_div = 0x50;
+ } else {
+ pll_div = 0x28;
+ }
+ }
+
+ pll = SM(ref_div, AR_RTC_9160_PLL_REFDIV);
+ pll |= SM(pll_div, AR_RTC_9160_PLL_DIV);
if (chan && IS_CHAN_HALF_RATE(chan))
pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL);
else if (chan && IS_CHAN_QUARTER_RATE(chan))
pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL);
- if (chan && IS_CHAN_5GHZ(chan)) {
- if (IS_CHAN_A_FAST_CLOCK(ah, chan))
- pll = 0x142c;
- else if (AR_SREV_9280_20(ah))
- pll = 0x2850;
- else
- pll |= SM(0x28, AR_RTC_9160_PLL_DIV);
- } else {
- pll |= SM(0x2c, AR_RTC_9160_PLL_DIV);
- }
-
return pll;
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
index e8ac70da5ac7..026f9de15d15 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
@@ -24,11 +24,11 @@ static const u32 ar9300_2p2_radio_postamble[][5] = {
{0x0001609c, 0x0dd08f29, 0x0dd08f29, 0x0b283f31, 0x0b283f31},
{0x000160ac, 0xa4653c00, 0xa4653c00, 0x24652800, 0x24652800},
{0x000160b0, 0x03284f3e, 0x03284f3e, 0x05d08f20, 0x05d08f20},
- {0x0001610c, 0x08000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0001610c, 0xc8000000, 0xc0000000, 0xc0000000, 0xc0000000},
{0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008},
- {0x0001650c, 0x08000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0001650c, 0xc8000000, 0xc0000000, 0xc0000000, 0xc0000000},
{0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008},
- {0x0001690c, 0x08000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0001690c, 0xc8000000, 0xc0000000, 0xc0000000, 0xc0000000},
{0x00016940, 0x10804008, 0x10804008, 0x50804008, 0x50804008},
};
@@ -190,7 +190,7 @@ static const u32 ar9300_2p2_radio_core[][2] = {
{0x00016288, 0x05a20408},
{0x0001628c, 0x00038c07},
{0x00016290, 0x00000004},
- {0x00016294, 0x458aa14f},
+ {0x00016294, 0x458a214f},
{0x00016380, 0x00000000},
{0x00016384, 0x00000000},
{0x00016388, 0x00800700},
@@ -636,7 +636,7 @@ static const u32 ar9300_2p2_baseband_postamble[][5] = {
{0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
- {0x0000a204, 0x000037c0, 0x000037c4, 0x000037c4, 0x000037c0},
+ {0x0000a204, 0x000036c0, 0x000036c4, 0x000036c4, 0x000036c0},
{0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
{0x0000a22c, 0x01026a2f, 0x01026a2f, 0x01026a2f, 0x01026a2f},
{0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
@@ -653,8 +653,8 @@ static const u32 ar9300_2p2_baseband_postamble[][5] = {
{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
- {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071982},
- {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
+ {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
+ {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
{0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000},
@@ -761,7 +761,7 @@ static const u32 ar9300_2p2_baseband_core[][2] = {
{0x0000a3ec, 0x20202020},
{0x0000a3f0, 0x00000000},
{0x0000a3f4, 0x00000246},
- {0x0000a3f8, 0x0cdbd380},
+ {0x0000a3f8, 0x0c9bd380},
{0x0000a3fc, 0x000f0f01},
{0x0000a400, 0x8fa91f01},
{0x0000a404, 0x00000000},
@@ -780,7 +780,7 @@ static const u32 ar9300_2p2_baseband_core[][2] = {
{0x0000a43c, 0x00100000},
{0x0000a440, 0x00000000},
{0x0000a444, 0x00000000},
- {0x0000a448, 0x06000080},
+ {0x0000a448, 0x05000080},
{0x0000a44c, 0x00000001},
{0x0000a450, 0x00010000},
{0x0000a458, 0x00000000},
@@ -835,108 +835,108 @@ static const u32 ar9300_2p2_baseband_core[][2] = {
static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = {
/* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
- {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
- {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
- {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
{0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
- {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9},
- {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
- {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002},
- {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004},
- {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200},
- {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202},
- {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400},
- {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402},
- {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404},
- {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603},
- {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02},
- {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04},
- {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20},
- {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20},
- {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22},
- {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24},
- {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640},
- {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660},
- {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861},
- {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81},
- {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83},
- {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84},
- {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3},
- {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5},
- {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9},
- {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb},
- {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
- {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
- {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
- {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
- {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
- {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
- {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
- {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000},
- {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002},
- {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004},
- {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200},
- {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202},
- {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400},
- {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402},
- {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404},
- {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603},
- {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02},
- {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04},
- {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20},
- {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20},
- {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22},
- {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24},
- {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640},
- {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660},
- {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861},
- {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81},
- {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83},
- {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84},
- {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3},
- {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5},
- {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9},
- {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb},
- {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
- {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
- {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
- {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
- {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
- {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
- {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
+ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+ {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x15000028, 0x15000028, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x1b00002b, 0x1b00002b, 0x12000400, 0x12000400},
+ {0x0000a518, 0x1f020028, 0x1f020028, 0x16000402, 0x16000402},
+ {0x0000a51c, 0x2502002b, 0x2502002b, 0x19000404, 0x19000404},
+ {0x0000a520, 0x2a04002a, 0x2a04002a, 0x1c000603, 0x1c000603},
+ {0x0000a524, 0x2e06002a, 0x2e06002a, 0x21000a02, 0x21000a02},
+ {0x0000a528, 0x3302202d, 0x3302202d, 0x25000a04, 0x25000a04},
+ {0x0000a52c, 0x3804202c, 0x3804202c, 0x28000a20, 0x28000a20},
+ {0x0000a530, 0x3c06202c, 0x3c06202c, 0x2c000e20, 0x2c000e20},
+ {0x0000a534, 0x4108202d, 0x4108202d, 0x30000e22, 0x30000e22},
+ {0x0000a538, 0x4506402d, 0x4506402d, 0x34000e24, 0x34000e24},
+ {0x0000a53c, 0x4906222d, 0x4906222d, 0x38001640, 0x38001640},
+ {0x0000a540, 0x4d062231, 0x4d062231, 0x3c001660, 0x3c001660},
+ {0x0000a544, 0x50082231, 0x50082231, 0x3f001861, 0x3f001861},
+ {0x0000a548, 0x5608422e, 0x5608422e, 0x43001a81, 0x43001a81},
+ {0x0000a54c, 0x5a08442e, 0x5a08442e, 0x47001a83, 0x47001a83},
+ {0x0000a550, 0x5e0a4431, 0x5e0a4431, 0x4a001c84, 0x4a001c84},
+ {0x0000a554, 0x640a4432, 0x640a4432, 0x4e001ce3, 0x4e001ce3},
+ {0x0000a558, 0x680a4434, 0x680a4434, 0x52001ce5, 0x52001ce5},
+ {0x0000a55c, 0x6c0a6434, 0x6c0a6434, 0x56001ce9, 0x56001ce9},
+ {0x0000a560, 0x6f0a6633, 0x6f0a6633, 0x5a001ceb, 0x5a001ceb},
+ {0x0000a564, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+ {0x0000a568, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+ {0x0000a56c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+ {0x0000a570, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+ {0x0000a574, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+ {0x0000a578, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+ {0x0000a57c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+ {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
+ {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002},
+ {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004},
+ {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200},
+ {0x0000a590, 0x15800028, 0x15800028, 0x0f800202, 0x0f800202},
+ {0x0000a594, 0x1b80002b, 0x1b80002b, 0x12800400, 0x12800400},
+ {0x0000a598, 0x1f820028, 0x1f820028, 0x16800402, 0x16800402},
+ {0x0000a59c, 0x2582002b, 0x2582002b, 0x19800404, 0x19800404},
+ {0x0000a5a0, 0x2a84002a, 0x2a84002a, 0x1c800603, 0x1c800603},
+ {0x0000a5a4, 0x2e86002a, 0x2e86002a, 0x21800a02, 0x21800a02},
+ {0x0000a5a8, 0x3382202d, 0x3382202d, 0x25800a04, 0x25800a04},
+ {0x0000a5ac, 0x3884202c, 0x3884202c, 0x28800a20, 0x28800a20},
+ {0x0000a5b0, 0x3c86202c, 0x3c86202c, 0x2c800e20, 0x2c800e20},
+ {0x0000a5b4, 0x4188202d, 0x4188202d, 0x30800e22, 0x30800e22},
+ {0x0000a5b8, 0x4586402d, 0x4586402d, 0x34800e24, 0x34800e24},
+ {0x0000a5bc, 0x4986222d, 0x4986222d, 0x38801640, 0x38801640},
+ {0x0000a5c0, 0x4d862231, 0x4d862231, 0x3c801660, 0x3c801660},
+ {0x0000a5c4, 0x50882231, 0x50882231, 0x3f801861, 0x3f801861},
+ {0x0000a5c8, 0x5688422e, 0x5688422e, 0x43801a81, 0x43801a81},
+ {0x0000a5cc, 0x5a88442e, 0x5a88442e, 0x47801a83, 0x47801a83},
+ {0x0000a5d0, 0x5e8a4431, 0x5e8a4431, 0x4a801c84, 0x4a801c84},
+ {0x0000a5d4, 0x648a4432, 0x648a4432, 0x4e801ce3, 0x4e801ce3},
+ {0x0000a5d8, 0x688a4434, 0x688a4434, 0x52801ce5, 0x52801ce5},
+ {0x0000a5dc, 0x6c8a6434, 0x6c8a6434, 0x56801ce9, 0x56801ce9},
+ {0x0000a5e0, 0x6f8a6633, 0x6f8a6633, 0x5a801ceb, 0x5a801ceb},
+ {0x0000a5e4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+ {0x0000a5e8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+ {0x0000a5ec, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f0, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+ {0x0000a5fc, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
{0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
- {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
- {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
- {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
- {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
- {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
- {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
- {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
- {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
- {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
- {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
- {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
- {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
- {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
- {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a608, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+ {0x0000a610, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01804601, 0x01804601, 0x01404000, 0x01404000},
+ {0x0000a618, 0x01804601, 0x01804601, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x01804601, 0x01804601, 0x02008501, 0x02008501},
+ {0x0000a620, 0x03408d02, 0x03408d02, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x03410d04, 0x03410d04, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+ {0x0000a630, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+ {0x0000a634, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+ {0x0000a638, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
{0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
- {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
- {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
- {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000c2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+ {0x0000c2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+ {0x0000c2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
{0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
- {0x00016044, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
- {0x00016048, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
- {0x00016068, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
- {0x00016444, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
- {0x00016448, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
- {0x00016468, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
- {0x00016844, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
- {0x00016848, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
- {0x00016868, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
+ {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016048, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
+ {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016448, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
+ {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016848, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
+ {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
};
static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = {
@@ -1500,8 +1500,6 @@ static const u32 ar9300_2p2_mac_core[][2] = {
{0x0000816c, 0x00000000},
{0x000081c0, 0x00000000},
{0x000081c4, 0x33332210},
- {0x000081c8, 0x00000000},
- {0x000081cc, 0x00000000},
{0x000081ec, 0x00000000},
{0x000081f0, 0x00000000},
{0x000081f4, 0x00000000},
@@ -1516,7 +1514,7 @@ static const u32 ar9300_2p2_mac_core[][2] = {
{0x00008258, 0x00000000},
{0x0000825c, 0x40000000},
{0x00008260, 0x00080922},
- {0x00008264, 0x9bc00010},
+ {0x00008264, 0x9d400010},
{0x00008268, 0xffffffff},
{0x0000826c, 0x0000ffff},
{0x00008270, 0x00000000},
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
index f48051c50092..12a730dcb500 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
@@ -17,8 +17,9 @@
#include "hw.h"
#include "hw-ops.h"
#include "ar9003_phy.h"
+#include "ar9003_rtt.h"
-#define MAX_MEASUREMENT 8
+#define MAX_MEASUREMENT MAX_IQCAL_MEASUREMENT
#define MAX_MAG_DELTA 11
#define MAX_PHS_DELTA 10
@@ -615,11 +616,10 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
{
int mp_max = -64, max_idx = 0;
int mp_min = 63, min_idx = 0;
- int mp_avg = 0, i, outlier_idx = 0;
+ int mp_avg = 0, i, outlier_idx = 0, mp_count = 0;
/* find min/max mismatch across all calibrated gains */
for (i = 0; i < nmeasurement; i++) {
- mp_avg += mp_coeff[i];
if (mp_coeff[i] > mp_max) {
mp_max = mp_coeff[i];
max_idx = i;
@@ -632,10 +632,20 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
/* find average (exclude max abs value) */
for (i = 0; i < nmeasurement; i++) {
if ((abs(mp_coeff[i]) < abs(mp_max)) ||
- (abs(mp_coeff[i]) < abs(mp_min)))
+ (abs(mp_coeff[i]) < abs(mp_min))) {
mp_avg += mp_coeff[i];
+ mp_count++;
+ }
}
- mp_avg /= (nmeasurement - 1);
+
+ /*
+ * finding mean magnitude/phase if possible, otherwise
+ * just use the last value as the mean
+ */
+ if (mp_count)
+ mp_avg /= mp_count;
+ else
+ mp_avg = mp_coeff[nmeasurement - 1];
/* detect outlier */
if (abs(mp_max - mp_min) > max_delta) {
@@ -643,16 +653,19 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
outlier_idx = max_idx;
else
outlier_idx = min_idx;
+
+ mp_coeff[outlier_idx] = mp_avg;
}
- mp_coeff[outlier_idx] = mp_avg;
}
static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
u8 num_chains,
- struct coeff *coeff)
+ struct coeff *coeff,
+ bool is_reusable)
{
int i, im, nmeasurement;
u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];
+ struct ath9k_hw_cal_data *caldata = ah->caldata;
memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff));
for (i = 0; i < MAX_MEASUREMENT / 2; i++) {
@@ -702,7 +715,13 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE,
coeff->iqc_coeff[0]);
+
+ if (caldata)
+ caldata->tx_corr_coeff[im][i] =
+ coeff->iqc_coeff[0];
}
+ if (caldata)
+ caldata->num_measures[i] = nmeasurement;
}
REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3,
@@ -710,8 +729,10 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0,
AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1);
- return;
+ if (caldata)
+ caldata->done_txiqcal_once = is_reusable;
+ return;
}
static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah)
@@ -738,7 +759,7 @@ static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah)
return true;
}
-static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah)
+static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
{
struct ath_common *common = ath9k_hw_common(ah);
const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
@@ -827,7 +848,8 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah)
coeff.phs_coeff[i][im] -= 128;
}
}
- ar9003_hw_tx_iqcal_load_avg_2_passes(ah, num_chains, &coeff);
+ ar9003_hw_tx_iqcal_load_avg_2_passes(ah, num_chains,
+ &coeff, is_reusable);
return;
@@ -835,23 +857,132 @@ tx_iqcal_fail:
ath_dbg(common, ATH_DBG_CALIBRATE, "Tx IQ Cal failed\n");
return;
}
+
+static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah)
+{
+ struct ath9k_hw_cal_data *caldata = ah->caldata;
+ u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];
+ int i, im;
+
+ memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff));
+ for (i = 0; i < MAX_MEASUREMENT / 2; i++) {
+ tx_corr_coeff[i * 2][0] = tx_corr_coeff[(i * 2) + 1][0] =
+ AR_PHY_TX_IQCAL_CORR_COEFF_B0(i);
+ if (!AR_SREV_9485(ah)) {
+ tx_corr_coeff[i * 2][1] =
+ tx_corr_coeff[(i * 2) + 1][1] =
+ AR_PHY_TX_IQCAL_CORR_COEFF_B1(i);
+
+ tx_corr_coeff[i * 2][2] =
+ tx_corr_coeff[(i * 2) + 1][2] =
+ AR_PHY_TX_IQCAL_CORR_COEFF_B2(i);
+ }
+ }
+
+ for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+ if (!(ah->txchainmask & (1 << i)))
+ continue;
+
+ for (im = 0; im < caldata->num_measures[i]; im++) {
+ if ((im % 2) == 0)
+ REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
+ AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE,
+ caldata->tx_corr_coeff[im][i]);
+ else
+ REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
+ AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE,
+ caldata->tx_corr_coeff[im][i]);
+ }
+ }
+
+ REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3,
+ AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1);
+ REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0,
+ AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1);
+}
+
+static bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+ struct ath9k_rtt_hist *hist;
+ u32 *table;
+ int i;
+ bool restore;
+
+ if (!ah->caldata)
+ return false;
+
+ hist = &ah->caldata->rtt_hist;
+ if (!hist->num_readings)
+ return false;
+
+ ar9003_hw_rtt_enable(ah);
+ ar9003_hw_rtt_set_mask(ah, 0x00);
+ for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+ if (!(ah->rxchainmask & (1 << i)))
+ continue;
+ table = &hist->table[i][hist->num_readings][0];
+ ar9003_hw_rtt_load_hist(ah, i, table);
+ }
+ restore = ar9003_hw_rtt_force_restore(ah);
+ ar9003_hw_rtt_disable(ah);
+
+ return restore;
+}
+
static bool ar9003_hw_init_cal(struct ath_hw *ah,
struct ath9k_channel *chan)
{
struct ath_common *common = ath9k_hw_common(ah);
- struct ath9k_hw_capabilities *pCap = &ah->caps;
- int val;
- bool txiqcal_done = false;
+ struct ath9k_hw_cal_data *caldata = ah->caldata;
+ bool txiqcal_done = false, txclcal_done = false;
+ bool is_reusable = true, status = true;
+ bool run_rtt_cal = false, run_agc_cal;
+ bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT);
+ u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL |
+ AR_PHY_AGC_CONTROL_FLTR_CAL |
+ AR_PHY_AGC_CONTROL_PKDET_CAL;
+ int i, j;
+ u32 cl_idx[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0,
+ AR_PHY_CL_TAB_1,
+ AR_PHY_CL_TAB_2 };
+
+ if (rtt) {
+ if (!ar9003_hw_rtt_restore(ah, chan))
+ run_rtt_cal = true;
+
+ ath_dbg(common, ATH_DBG_CALIBRATE, "RTT restore %s\n",
+ run_rtt_cal ? "failed" : "succeed");
+ }
+ run_agc_cal = run_rtt_cal;
- val = REG_READ(ah, AR_ENT_OTP);
- ath_dbg(common, ATH_DBG_CALIBRATE, "ath9k: AR_ENT_OTP 0x%x\n", val);
+ if (run_rtt_cal) {
+ ar9003_hw_rtt_enable(ah);
+ ar9003_hw_rtt_set_mask(ah, 0x00);
+ ar9003_hw_rtt_clear_hist(ah);
+ }
- /* Configure rx/tx chains before running AGC/TxiQ cals */
- if (val & AR_ENT_OTP_CHAIN2_DISABLE)
- ar9003_hw_set_chain_masks(ah, 0x3, 0x3);
- else
- ar9003_hw_set_chain_masks(ah, pCap->rx_chainmask,
- pCap->tx_chainmask);
+ if (rtt && !run_rtt_cal) {
+ agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL);
+ agc_supp_cals &= agc_ctrl;
+ agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL |
+ AR_PHY_AGC_CONTROL_FLTR_CAL |
+ AR_PHY_AGC_CONTROL_PKDET_CAL);
+ REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl);
+ }
+
+ if (ah->enabled_cals & TX_CL_CAL) {
+ if (caldata && caldata->done_txclcal_once)
+ REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL,
+ AR_PHY_CL_CAL_ENABLE);
+ else {
+ REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL,
+ AR_PHY_CL_CAL_ENABLE);
+ run_agc_cal = true;
+ }
+ }
+
+ if (!(ah->enabled_cals & TX_IQ_CAL))
+ goto skip_tx_iqcal;
/* Do Tx IQ Calibration */
REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1,
@@ -862,35 +993,97 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
* For AR9485 or later chips, TxIQ cal runs as part of
* AGC calibration
*/
- if (AR_SREV_9485_OR_LATER(ah))
- txiqcal_done = true;
- else {
- txiqcal_done = ar9003_hw_tx_iq_cal_run(ah);
- REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
- udelay(5);
- REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
- }
-
- /* Calibrate the AGC */
- REG_WRITE(ah, AR_PHY_AGC_CONTROL,
- REG_READ(ah, AR_PHY_AGC_CONTROL) |
- AR_PHY_AGC_CONTROL_CAL);
-
- /* Poll for offset calibration complete */
- if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL,
- 0, AH_WAIT_TIMEOUT)) {
+ if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) {
+ if (caldata && !caldata->done_txiqcal_once)
+ REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
+ AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
+ else
+ REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
+ AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
+ txiqcal_done = run_agc_cal = true;
+ goto skip_tx_iqcal;
+ } else if (caldata && !caldata->done_txiqcal_once)
+ run_agc_cal = true;
+
+ txiqcal_done = ar9003_hw_tx_iq_cal_run(ah);
+ REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
+ udelay(5);
+ REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
+
+skip_tx_iqcal:
+ if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) {
+ /* Calibrate the AGC */
+ REG_WRITE(ah, AR_PHY_AGC_CONTROL,
+ REG_READ(ah, AR_PHY_AGC_CONTROL) |
+ AR_PHY_AGC_CONTROL_CAL);
+
+ /* Poll for offset calibration complete */
+ status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_CAL,
+ 0, AH_WAIT_TIMEOUT);
+ }
+ if (rtt && !run_rtt_cal) {
+ agc_ctrl |= agc_supp_cals;
+ REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl);
+ }
+
+ if (!status) {
+ if (run_rtt_cal)
+ ar9003_hw_rtt_disable(ah);
+
ath_dbg(common, ATH_DBG_CALIBRATE,
- "offset calibration failed to complete in 1ms; noisy environment?\n");
+ "offset calibration failed to complete in 1ms;"
+ "noisy environment?\n");
return false;
}
if (txiqcal_done)
- ar9003_hw_tx_iq_cal_post_proc(ah);
-
- /* Revert chainmasks to their original values before NF cal */
- ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
+ ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable);
+ else if (caldata && caldata->done_txiqcal_once)
+ ar9003_hw_tx_iq_cal_reload(ah);
+
+#define CL_TAB_ENTRY(reg_base) (reg_base + (4 * j))
+ if (caldata && (ah->enabled_cals & TX_CL_CAL)) {
+ txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) &
+ AR_PHY_AGC_CONTROL_CLC_SUCCESS);
+ if (caldata->done_txclcal_once) {
+ for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+ if (!(ah->txchainmask & (1 << i)))
+ continue;
+ for (j = 0; j < MAX_CL_TAB_ENTRY; j++)
+ REG_WRITE(ah, CL_TAB_ENTRY(cl_idx[i]),
+ caldata->tx_clcal[i][j]);
+ }
+ } else if (is_reusable && txclcal_done) {
+ for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+ if (!(ah->txchainmask & (1 << i)))
+ continue;
+ for (j = 0; j < MAX_CL_TAB_ENTRY; j++)
+ caldata->tx_clcal[i][j] =
+ REG_READ(ah,
+ CL_TAB_ENTRY(cl_idx[i]));
+ }
+ caldata->done_txclcal_once = true;
+ }
+ }
+#undef CL_TAB_ENTRY
+
+ if (run_rtt_cal && caldata) {
+ struct ath9k_rtt_hist *hist = &caldata->rtt_hist;
+ if (is_reusable && (hist->num_readings < RTT_HIST_MAX)) {
+ u32 *table;
+
+ hist->num_readings++;
+ for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+ if (!(ah->rxchainmask & (1 << i)))
+ continue;
+ table = &hist->table[i][hist->num_readings][0];
+ ar9003_hw_rtt_fill_hist(ah, i, table);
+ }
+ }
- ath9k_hw_start_nfcal(ah, true);
+ ar9003_hw_rtt_disable(ah);
+ }
/* Initialize list pointers */
ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
@@ -916,8 +1109,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
if (ah->cal_list_curr)
ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
- if (ah->caldata)
- ah->caldata->CalValid = 0;
+ if (caldata)
+ caldata->CalValid = 0;
return true;
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index ff8150e46f0e..3b262ba6b172 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <asm/unaligned.h>
#include "hw.h"
#include "ar9003_phy.h"
#include "ar9003_eeprom.h"
@@ -21,25 +22,6 @@
#define COMP_HDR_LEN 4
#define COMP_CKSUM_LEN 2
-#define AR_CH0_TOP (0x00016288)
-#define AR_CH0_TOP_XPABIASLVL (0x300)
-#define AR_CH0_TOP_XPABIASLVL_S (8)
-
-#define AR_CH0_THERM (0x00016290)
-#define AR_CH0_THERM_XPABIASLVL_MSB 0x3
-#define AR_CH0_THERM_XPABIASLVL_MSB_S 0
-#define AR_CH0_THERM_XPASHORT2GND 0x4
-#define AR_CH0_THERM_XPASHORT2GND_S 2
-
-#define AR_SWITCH_TABLE_COM_ALL (0xffff)
-#define AR_SWITCH_TABLE_COM_ALL_S (0)
-
-#define AR_SWITCH_TABLE_COM2_ALL (0xffffff)
-#define AR_SWITCH_TABLE_COM2_ALL_S (0)
-
-#define AR_SWITCH_TABLE_ALL (0xfff)
-#define AR_SWITCH_TABLE_ALL_S (0)
-
#define LE16(x) __constant_cpu_to_le16(x)
#define LE32(x) __constant_cpu_to_le32(x)
@@ -68,7 +50,7 @@ static int ar9003_hw_power_interpolate(int32_t x,
static const struct ar9300_eeprom ar9300_default = {
.eepromVersion = 2,
.templateVersion = 2,
- .macAddr = {1, 2, 3, 4, 5, 6},
+ .macAddr = {0, 2, 3, 4, 5, 6},
.custData = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
.baseEepHeader = {
@@ -157,7 +139,7 @@ static const struct ar9300_eeprom ar9300_default = {
.papdRateMaskHt20 = LE32(0x0cf0e0e0),
.papdRateMaskHt40 = LE32(0x6cf0e0e0),
.futureModal = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
},
},
.base_ext1 = {
@@ -306,7 +288,7 @@ static const struct ar9300_eeprom ar9300_default = {
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } },
- { { CTL(60, 1), CTL(60, 0), CTL(0, 0), CTL(0, 0) } },
+ { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
@@ -359,7 +341,7 @@ static const struct ar9300_eeprom ar9300_default = {
.papdRateMaskHt20 = LE32(0x0c80c080),
.papdRateMaskHt40 = LE32(0x0080c080),
.futureModal = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
},
},
.base_ext2 = {
@@ -734,7 +716,7 @@ static const struct ar9300_eeprom ar9300_x113 = {
.papdRateMaskHt20 = LE32(0x0c80c080),
.papdRateMaskHt40 = LE32(0x0080c080),
.futureModal = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
},
},
.base_ext1 = {
@@ -883,7 +865,7 @@ static const struct ar9300_eeprom ar9300_x113 = {
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } },
- { { CTL(60, 1), CTL(60, 0), CTL(0, 0), CTL(0, 0) } },
+ { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
@@ -936,7 +918,7 @@ static const struct ar9300_eeprom ar9300_x113 = {
.papdRateMaskHt20 = LE32(0x0cf0e0e0),
.papdRateMaskHt40 = LE32(0x6cf0e0e0),
.futureModal = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
},
},
.base_ext2 = {
@@ -1312,7 +1294,7 @@ static const struct ar9300_eeprom ar9300_h112 = {
.papdRateMaskHt20 = LE32(0x80c080),
.papdRateMaskHt40 = LE32(0x80c080),
.futureModal = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
},
},
.base_ext1 = {
@@ -1461,7 +1443,7 @@ static const struct ar9300_eeprom ar9300_h112 = {
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } },
- { { CTL(60, 1), CTL(60, 0), CTL(0, 0), CTL(0, 0) } },
+ { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
@@ -1514,7 +1496,7 @@ static const struct ar9300_eeprom ar9300_h112 = {
.papdRateMaskHt20 = LE32(0x0cf0e0e0),
.papdRateMaskHt40 = LE32(0x6cf0e0e0),
.futureModal = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
},
},
.base_ext2 = {
@@ -1890,7 +1872,7 @@ static const struct ar9300_eeprom ar9300_x112 = {
.papdRateMaskHt20 = LE32(0x0c80c080),
.papdRateMaskHt40 = LE32(0x0080c080),
.futureModal = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
},
},
.base_ext1 = {
@@ -2039,7 +2021,7 @@ static const struct ar9300_eeprom ar9300_x112 = {
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } },
- { { CTL(60, 1), CTL(60, 0), CTL(0, 0), CTL(0, 0) } },
+ { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
@@ -2092,7 +2074,7 @@ static const struct ar9300_eeprom ar9300_x112 = {
.papdRateMaskHt20 = LE32(0x0cf0e0e0),
.papdRateMaskHt40 = LE32(0x6cf0e0e0),
.futureModal = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
},
},
.base_ext2 = {
@@ -2467,7 +2449,7 @@ static const struct ar9300_eeprom ar9300_h116 = {
.papdRateMaskHt20 = LE32(0x0c80C080),
.papdRateMaskHt40 = LE32(0x0080C080),
.futureModal = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
},
},
.base_ext1 = {
@@ -2616,7 +2598,7 @@ static const struct ar9300_eeprom ar9300_h116 = {
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } },
- { { CTL(60, 1), CTL(60, 0), CTL(0, 0), CTL(0, 0) } },
+ { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
@@ -2669,7 +2651,7 @@ static const struct ar9300_eeprom ar9300_h116 = {
.papdRateMaskHt20 = LE32(0x0cf0e0e0),
.papdRateMaskHt40 = LE32(0x6cf0e0e0),
.futureModal = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
},
},
.base_ext2 = {
@@ -3006,15 +2988,13 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah,
switch (param) {
case EEP_MAC_LSW:
- return eep->macAddr[0] << 8 | eep->macAddr[1];
+ return get_unaligned_be16(eep->macAddr);
case EEP_MAC_MID:
- return eep->macAddr[2] << 8 | eep->macAddr[3];
+ return get_unaligned_be16(eep->macAddr + 2);
case EEP_MAC_MSW:
- return eep->macAddr[4] << 8 | eep->macAddr[5];
+ return get_unaligned_be16(eep->macAddr + 4);
case EEP_REG_0:
return le16_to_cpu(pBase->regDmn[0]);
- case EEP_REG_1:
- return le16_to_cpu(pBase->regDmn[1]);
case EEP_OP_CAP:
return pBase->deviceCap;
case EEP_OP_MODE:
@@ -3038,7 +3018,11 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah,
case EEP_CHAIN_MASK_REDUCE:
return (pBase->miscConfiguration >> 0x3) & 0x1;
case EEP_ANT_DIV_CTL1:
- return le32_to_cpu(eep->base_ext1.ant_div_control);
+ return eep->base_ext1.ant_div_control;
+ case EEP_ANTENNA_GAIN_5G:
+ return eep->modalHeader5G.antennaGain;
+ case EEP_ANTENNA_GAIN_2G:
+ return eep->modalHeader2G.antennaGain;
default:
return 0;
}
@@ -3317,13 +3301,15 @@ static int ar9300_eeprom_restore_internal(struct ath_hw *ah,
word = kzalloc(2048, GFP_KERNEL);
if (!word)
- return -1;
+ return -ENOMEM;
memcpy(mptr, &ar9300_default, mdata_size);
read = ar9300_read_eeprom;
if (AR_SREV_9485(ah))
cptr = AR9300_BASE_ADDR_4K;
+ else if (AR_SREV_9330(ah))
+ cptr = AR9300_BASE_ADDR_512;
else
cptr = AR9300_BASE_ADDR;
ath_dbg(common, ATH_DBG_EEPROM,
@@ -3378,8 +3364,7 @@ found:
osize = length;
read(ah, cptr, word, COMP_HDR_LEN + osize + COMP_CKSUM_LEN);
checksum = ar9300_comp_cksum(&word[COMP_HDR_LEN], length);
- mchecksum = word[COMP_HDR_LEN + osize] |
- (word[COMP_HDR_LEN + osize + 1] << 8);
+ mchecksum = get_unaligned_le16(&word[COMP_HDR_LEN + osize]);
ath_dbg(common, ATH_DBG_EEPROM,
"checksum %x %x\n", checksum, mchecksum);
if (checksum == mchecksum) {
@@ -3416,6 +3401,133 @@ static bool ath9k_hw_ar9300_fill_eeprom(struct ath_hw *ah)
return true;
}
+#if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS)
+static u32 ar9003_dump_modal_eeprom(char *buf, u32 len, u32 size,
+ struct ar9300_modal_eep_header *modal_hdr)
+{
+ PR_EEP("Chain0 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[0]));
+ PR_EEP("Chain1 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[1]));
+ PR_EEP("Chain2 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[2]));
+ PR_EEP("Ant. Common Control", le32_to_cpu(modal_hdr->antCtrlCommon));
+ PR_EEP("Ant. Common Control2", le32_to_cpu(modal_hdr->antCtrlCommon2));
+ PR_EEP("Ant. Gain", modal_hdr->antennaGain);
+ PR_EEP("Switch Settle", modal_hdr->switchSettling);
+ PR_EEP("Chain0 xatten1DB", modal_hdr->xatten1DB[0]);
+ PR_EEP("Chain1 xatten1DB", modal_hdr->xatten1DB[1]);
+ PR_EEP("Chain2 xatten1DB", modal_hdr->xatten1DB[2]);
+ PR_EEP("Chain0 xatten1Margin", modal_hdr->xatten1Margin[0]);
+ PR_EEP("Chain1 xatten1Margin", modal_hdr->xatten1Margin[1]);
+ PR_EEP("Chain2 xatten1Margin", modal_hdr->xatten1Margin[2]);
+ PR_EEP("Temp Slope", modal_hdr->tempSlope);
+ PR_EEP("Volt Slope", modal_hdr->voltSlope);
+ PR_EEP("spur Channels0", modal_hdr->spurChans[0]);
+ PR_EEP("spur Channels1", modal_hdr->spurChans[1]);
+ PR_EEP("spur Channels2", modal_hdr->spurChans[2]);
+ PR_EEP("spur Channels3", modal_hdr->spurChans[3]);
+ PR_EEP("spur Channels4", modal_hdr->spurChans[4]);
+ PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]);
+ PR_EEP("Chain1 NF Threshold", modal_hdr->noiseFloorThreshCh[1]);
+ PR_EEP("Chain2 NF Threshold", modal_hdr->noiseFloorThreshCh[2]);
+ PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl);
+ PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart);
+ PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn);
+ PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn);
+ PR_EEP("txClip", modal_hdr->txClip);
+ PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize);
+ PR_EEP("Chain0 ob", modal_hdr->ob[0]);
+ PR_EEP("Chain1 ob", modal_hdr->ob[1]);
+ PR_EEP("Chain2 ob", modal_hdr->ob[2]);
+
+ PR_EEP("Chain0 db_stage2", modal_hdr->db_stage2[0]);
+ PR_EEP("Chain1 db_stage2", modal_hdr->db_stage2[1]);
+ PR_EEP("Chain2 db_stage2", modal_hdr->db_stage2[2]);
+ PR_EEP("Chain0 db_stage3", modal_hdr->db_stage3[0]);
+ PR_EEP("Chain1 db_stage3", modal_hdr->db_stage3[1]);
+ PR_EEP("Chain2 db_stage3", modal_hdr->db_stage3[2]);
+ PR_EEP("Chain0 db_stage4", modal_hdr->db_stage4[0]);
+ PR_EEP("Chain1 db_stage4", modal_hdr->db_stage4[1]);
+ PR_EEP("Chain2 db_stage4", modal_hdr->db_stage4[2]);
+
+ return len;
+}
+
+static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+ u8 *buf, u32 len, u32 size)
+{
+ struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
+ struct ar9300_base_eep_hdr *pBase;
+
+ if (!dump_base_hdr) {
+ len += snprintf(buf + len, size - len,
+ "%20s :\n", "2GHz modal Header");
+ len += ar9003_dump_modal_eeprom(buf, len, size,
+ &eep->modalHeader2G);
+ len += snprintf(buf + len, size - len,
+ "%20s :\n", "5GHz modal Header");
+ len += ar9003_dump_modal_eeprom(buf, len, size,
+ &eep->modalHeader5G);
+ goto out;
+ }
+
+ pBase = &eep->baseEepHeader;
+
+ PR_EEP("EEPROM Version", ah->eeprom.ar9300_eep.eepromVersion);
+ PR_EEP("RegDomain1", le16_to_cpu(pBase->regDmn[0]));
+ PR_EEP("RegDomain2", le16_to_cpu(pBase->regDmn[1]));
+ PR_EEP("TX Mask", (pBase->txrxMask >> 4));
+ PR_EEP("RX Mask", (pBase->txrxMask & 0x0f));
+ PR_EEP("Allow 5GHz", !!(pBase->opCapFlags.opFlags &
+ AR5416_OPFLAGS_11A));
+ PR_EEP("Allow 2GHz", !!(pBase->opCapFlags.opFlags &
+ AR5416_OPFLAGS_11G));
+ PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags.opFlags &
+ AR5416_OPFLAGS_N_2G_HT20));
+ PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags.opFlags &
+ AR5416_OPFLAGS_N_2G_HT40));
+ PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags.opFlags &
+ AR5416_OPFLAGS_N_5G_HT20));
+ PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags.opFlags &
+ AR5416_OPFLAGS_N_5G_HT40));
+ PR_EEP("Big Endian", !!(pBase->opCapFlags.eepMisc & 0x01));
+ PR_EEP("RF Silent", pBase->rfSilent);
+ PR_EEP("BT option", pBase->blueToothOptions);
+ PR_EEP("Device Cap", pBase->deviceCap);
+ PR_EEP("Device Type", pBase->deviceType);
+ PR_EEP("Power Table Offset", pBase->pwrTableOffset);
+ PR_EEP("Tuning Caps1", pBase->params_for_tuning_caps[0]);
+ PR_EEP("Tuning Caps2", pBase->params_for_tuning_caps[1]);
+ PR_EEP("Enable Tx Temp Comp", !!(pBase->featureEnable & BIT(0)));
+ PR_EEP("Enable Tx Volt Comp", !!(pBase->featureEnable & BIT(1)));
+ PR_EEP("Enable fast clock", !!(pBase->featureEnable & BIT(2)));
+ PR_EEP("Enable doubling", !!(pBase->featureEnable & BIT(3)));
+ PR_EEP("Internal regulator", !!(pBase->featureEnable & BIT(4)));
+ PR_EEP("Enable Paprd", !!(pBase->featureEnable & BIT(5)));
+ PR_EEP("Driver Strength", !!(pBase->miscConfiguration & BIT(0)));
+ PR_EEP("Chain mask Reduce", (pBase->miscConfiguration >> 0x3) & 0x1);
+ PR_EEP("Write enable Gpio", pBase->eepromWriteEnableGpio);
+ PR_EEP("WLAN Disable Gpio", pBase->wlanDisableGpio);
+ PR_EEP("WLAN LED Gpio", pBase->wlanLedGpio);
+ PR_EEP("Rx Band Select Gpio", pBase->rxBandSelectGpio);
+ PR_EEP("Tx Gain", pBase->txrxgain >> 4);
+ PR_EEP("Rx Gain", pBase->txrxgain & 0xf);
+ PR_EEP("SW Reg", le32_to_cpu(pBase->swreg));
+
+ len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+ ah->eeprom.ar9300_eep.macAddr);
+out:
+ if (len > size)
+ len = size;
+
+ return len;
+}
+#else
+static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+ u8 *buf, u32 len, u32 size)
+{
+ return 0;
+}
+#endif
+
/* XXX: review hardware docs */
static int ath9k_hw_ar9300_get_eeprom_ver(struct ath_hw *ah)
{
@@ -3442,8 +3554,10 @@ static void ar9003_hw_xpa_bias_level_apply(struct ath_hw *ah, bool is2ghz)
{
int bias = ar9003_hw_xpa_bias_level_get(ah, is2ghz);
- if (AR_SREV_9485(ah) || AR_SREV_9340(ah))
+ if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah))
REG_RMW_FIELD(ah, AR_CH0_TOP2, AR_CH0_TOP2_XPABIASLVL, bias);
+ else if (AR_SREV_9462(ah))
+ REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias);
else {
REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias);
REG_RMW_FIELD(ah, AR_CH0_THERM,
@@ -3454,6 +3568,19 @@ static void ar9003_hw_xpa_bias_level_apply(struct ath_hw *ah, bool is2ghz)
}
}
+static u16 ar9003_switch_com_spdt_get(struct ath_hw *ah, bool is_2ghz)
+{
+ struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
+ __le32 val;
+
+ if (is_2ghz)
+ val = eep->modalHeader2G.switchcomspdt;
+ else
+ val = eep->modalHeader5G.switchcomspdt;
+ return le32_to_cpu(val);
+}
+
+
static u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz)
{
struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
@@ -3508,7 +3635,36 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
u32 value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz);
- REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_ALL, value);
+ if (AR_SREV_9462(ah)) {
+ if (AR_SREV_9462_10(ah)) {
+ value &= ~AR_SWITCH_TABLE_COM_SPDT;
+ value |= 0x00100000;
+ }
+ REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM,
+ AR_SWITCH_TABLE_COM_AR9462_ALL, value);
+ } else
+ REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM,
+ AR_SWITCH_TABLE_COM_ALL, value);
+
+
+ /*
+ * AR9462 defines new switch table for BT/WLAN,
+ * here's new field name in XXX.ref for both 2G and 5G.
+ * Register: [GLB_CONTROL] GLB_CONTROL (@0x20044)
+ * 15:12 R/W SWITCH_TABLE_COM_SPDT_WLAN_RX
+ * SWITCH_TABLE_COM_SPDT_WLAN_RX
+ *
+ * 11:8 R/W SWITCH_TABLE_COM_SPDT_WLAN_TX
+ * SWITCH_TABLE_COM_SPDT_WLAN_TX
+ *
+ * 7:4 R/W SWITCH_TABLE_COM_SPDT_WLAN_IDLE
+ * SWITCH_TABLE_COM_SPDT_WLAN_IDLE
+ */
+ if (AR_SREV_9462_20_OR_LATER(ah)) {
+ value = ar9003_switch_com_spdt_get(ah, is2ghz);
+ REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL,
+ AR_SWITCH_TABLE_COM_SPDT_ALL, value);
+ }
value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz);
REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value);
@@ -3523,7 +3679,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
}
}
- if (AR_SREV_9485(ah)) {
+ if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) {
value = ath9k_hw_ar9300_get_eeprom(ah, EEP_ANT_DIV_CTL1);
/*
* main_lnaconf, alt_lnaconf, main_tb, alt_tb
@@ -3708,9 +3864,10 @@ static void ar9003_hw_internal_regulator_apply(struct ath_hw *ah)
{
int internal_regulator =
ath9k_hw_ar9300_get_eeprom(ah, EEP_INTERNAL_REGULATOR);
+ u32 reg_val;
if (internal_regulator) {
- if (AR_SREV_9485(ah)) {
+ if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) {
int reg_pmu_set;
reg_pmu_set = REG_READ(ah, AR_PHY_PMU2) & ~AR_PHY_PMU2_PGM;
@@ -3718,9 +3875,24 @@ static void ar9003_hw_internal_regulator_apply(struct ath_hw *ah)
if (!is_pmu_set(ah, AR_PHY_PMU2, reg_pmu_set))
return;
- reg_pmu_set = (5 << 1) | (7 << 4) | (1 << 8) |
- (2 << 14) | (6 << 17) | (1 << 20) |
- (3 << 24) | (1 << 28);
+ if (AR_SREV_9330(ah)) {
+ if (ah->is_clk_25mhz) {
+ reg_pmu_set = (3 << 1) | (8 << 4) |
+ (3 << 8) | (1 << 14) |
+ (6 << 17) | (1 << 20) |
+ (3 << 24);
+ } else {
+ reg_pmu_set = (4 << 1) | (7 << 4) |
+ (3 << 8) | (1 << 14) |
+ (6 << 17) | (1 << 20) |
+ (3 << 24);
+ }
+ } else {
+ reg_pmu_set = (5 << 1) | (7 << 4) |
+ (2 << 8) | (2 << 14) |
+ (6 << 17) | (1 << 20) |
+ (3 << 24) | (1 << 28);
+ }
REG_WRITE(ah, AR_PHY_PMU1, reg_pmu_set);
if (!is_pmu_set(ah, AR_PHY_PMU1, reg_pmu_set))
@@ -3737,13 +3909,16 @@ static void ar9003_hw_internal_regulator_apply(struct ath_hw *ah)
REG_WRITE(ah, AR_PHY_PMU2, reg_pmu_set);
if (!is_pmu_set(ah, AR_PHY_PMU2, reg_pmu_set))
return;
+ } else if (AR_SREV_9462(ah)) {
+ reg_val = ath9k_hw_ar9300_get_eeprom(ah, EEP_SWREG);
+ REG_WRITE(ah, AR_PHY_PMU1, reg_val);
} else {
/* Internal regulator is ON. Write swreg register. */
- int swreg = ath9k_hw_ar9300_get_eeprom(ah, EEP_SWREG);
+ reg_val = ath9k_hw_ar9300_get_eeprom(ah, EEP_SWREG);
REG_WRITE(ah, AR_RTC_REG_CONTROL1,
REG_READ(ah, AR_RTC_REG_CONTROL1) &
(~AR_RTC_REG_CONTROL1_SWREG_PROGRAM));
- REG_WRITE(ah, AR_RTC_REG_CONTROL0, swreg);
+ REG_WRITE(ah, AR_RTC_REG_CONTROL0, reg_val);
/* Set REG_CONTROL1.SWREG_PROGRAM */
REG_WRITE(ah, AR_RTC_REG_CONTROL1,
REG_READ(ah,
@@ -3751,25 +3926,27 @@ static void ar9003_hw_internal_regulator_apply(struct ath_hw *ah)
AR_RTC_REG_CONTROL1_SWREG_PROGRAM);
}
} else {
- if (AR_SREV_9485(ah)) {
+ if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) {
REG_RMW_FIELD(ah, AR_PHY_PMU2, AR_PHY_PMU2_PGM, 0);
while (REG_READ_FIELD(ah, AR_PHY_PMU2,
- AR_PHY_PMU2_PGM))
+ AR_PHY_PMU2_PGM))
udelay(10);
REG_RMW_FIELD(ah, AR_PHY_PMU1, AR_PHY_PMU1_PWD, 0x1);
while (!REG_READ_FIELD(ah, AR_PHY_PMU1,
- AR_PHY_PMU1_PWD))
+ AR_PHY_PMU1_PWD))
udelay(10);
REG_RMW_FIELD(ah, AR_PHY_PMU2, AR_PHY_PMU2_PGM, 0x1);
while (!REG_READ_FIELD(ah, AR_PHY_PMU2,
- AR_PHY_PMU2_PGM))
+ AR_PHY_PMU2_PGM))
udelay(10);
- } else
- REG_WRITE(ah, AR_RTC_SLEEP_CLK,
- (REG_READ(ah,
- AR_RTC_SLEEP_CLK) |
- AR_RTC_FORCE_SWREG_PRD));
+ } else if (AR_SREV_9462(ah))
+ REG_RMW_FIELD(ah, AR_PHY_PMU1, AR_PHY_PMU1_PWD, 0x1);
+ else {
+ reg_val = REG_READ(ah, AR_RTC_SLEEP_CLK) |
+ AR_RTC_FORCE_SWREG_PRD;
+ REG_WRITE(ah, AR_RTC_SLEEP_CLK, reg_val);
+ }
}
}
@@ -3795,9 +3972,9 @@ static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah,
ar9003_hw_ant_ctrl_apply(ah, IS_CHAN_2GHZ(chan));
ar9003_hw_drive_strength_apply(ah);
ar9003_hw_atten_apply(ah, chan);
- if (!AR_SREV_9340(ah))
+ if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah))
ar9003_hw_internal_regulator_apply(ah);
- if (AR_SREV_9485(ah) || AR_SREV_9340(ah))
+ if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah))
ar9003_hw_apply_tuning_caps(ah);
}
@@ -4044,7 +4221,7 @@ static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray)
/* Write the power for duplicated frames - HT40 */
/* dup40_cck (LSB), dup40_ofdm, ext20_cck, ext20_ofdm (MSB) */
- REG_WRITE(ah, 0xa3e0,
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(8),
POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 24) |
POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 16) |
POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 8) |
@@ -4349,6 +4526,12 @@ static int ar9003_hw_power_control_override(struct ath_hw *ah,
tempSlope = eep->modalHeader5G.tempSlope;
REG_RMW_FIELD(ah, AR_PHY_TPC_19, AR_PHY_TPC_19_ALPHA_THERM, tempSlope);
+
+ if (AR_SREV_9462_20(ah))
+ REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
+ AR_PHY_TPC_19_B1_ALPHA_THERM, tempSlope);
+
+
REG_RMW_FIELD(ah, AR_PHY_TPC_18, AR_PHY_TPC_18_THERM_CAL_VALUE,
temperature[0]);
@@ -4583,20 +4766,14 @@ static u16 ar9003_hw_get_max_edge_power(struct ar9300_eeprom *eep,
static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah,
struct ath9k_channel *chan,
u8 *pPwrArray, u16 cfgCtl,
- u8 twiceAntennaReduction,
- u8 twiceMaxRegulatoryPower,
+ u8 antenna_reduction,
u16 powerLimit)
{
- struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ath_common *common = ath9k_hw_common(ah);
struct ar9300_eeprom *pEepData = &ah->eeprom.ar9300_eep;
u16 twiceMaxEdgePower = MAX_RATE_POWER;
- static const u16 tpScaleReductionTable[5] = {
- 0, 3, 6, 9, MAX_RATE_POWER
- };
int i;
- int16_t twiceLargestAntenna;
- u16 scaledPower = 0, minCtlPower, maxRegAllowedPower;
+ u16 scaledPower = 0, minCtlPower;
static const u16 ctlModesFor11a[] = {
CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40
};
@@ -4614,28 +4791,7 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah,
bool is2ghz = IS_CHAN_2GHZ(chan);
ath9k_hw_get_channel_centers(ah, chan, &centers);
-
- /* Compute TxPower reduction due to Antenna Gain */
- if (is2ghz)
- twiceLargestAntenna = pEepData->modalHeader2G.antennaGain;
- else
- twiceLargestAntenna = pEepData->modalHeader5G.antennaGain;
-
- twiceLargestAntenna = (int16_t)min((twiceAntennaReduction) -
- twiceLargestAntenna, 0);
-
- /*
- * scaledPower is the minimum of the user input power level
- * and the regulatory allowed power level
- */
- maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna;
-
- if (regulatory->tp_scale != ATH9K_TP_SCALE_MAX) {
- maxRegAllowedPower -=
- (tpScaleReductionTable[(regulatory->tp_scale)] * 2);
- }
-
- scaledPower = min(powerLimit, maxRegAllowedPower);
+ scaledPower = powerLimit - antenna_reduction;
/*
* Reduce scaled Power by number of chains active to get
@@ -4822,7 +4978,6 @@ static inline u8 mcsidx_to_tgtpwridx(unsigned int mcs_idx, u8 base_pwridx)
static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
struct ath9k_channel *chan, u16 cfgCtl,
u8 twiceAntennaReduction,
- u8 twiceMaxRegulatoryPower,
u8 powerLimit, bool test)
{
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
@@ -4875,7 +5030,6 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
ar9003_hw_set_power_per_rate_table(ah, chan,
targetPowerValT2, cfgCtl,
twiceAntennaReduction,
- twiceMaxRegulatoryPower,
powerLimit);
if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) {
@@ -4905,25 +5059,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
"TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]);
}
- /*
- * This is the TX power we send back to driver core,
- * and it can use to pass to userspace to display our
- * currently configured TX power setting.
- *
- * Since power is rate dependent, use one of the indices
- * from the AR9300_Rates enum to select an entry from
- * targetPowerValT2[] to report. Currently returns the
- * power for HT40 MCS 0, HT20 MCS 0, or OFDM 6 Mbps
- * as CCK power is less interesting (?).
- */
- i = ALL_TARGET_LEGACY_6_24; /* legacy */
- if (IS_CHAN_HT40(chan))
- i = ALL_TARGET_HT40_0_8_16; /* ht40 */
- else if (IS_CHAN_HT20(chan))
- i = ALL_TARGET_HT20_0_8_16; /* ht20 */
-
- ah->txpower_limit = targetPowerValT2[i];
- regulatory->max_power_level = targetPowerValT2[i];
+ ah->txpower_limit = regulatory->max_power_level;
/* Write target power array to registers */
ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
@@ -4998,6 +5134,7 @@ const struct eeprom_ops eep_ar9300_ops = {
.check_eeprom = ath9k_hw_ar9300_check_eeprom,
.get_eeprom = ath9k_hw_ar9300_get_eeprom,
.fill_eeprom = ath9k_hw_ar9300_fill_eeprom,
+ .dump_eeprom = ath9k_hw_ar9003_dump_eeprom,
.get_eeprom_ver = ath9k_hw_ar9300_get_eeprom_ver,
.get_eeprom_rev = ath9k_hw_ar9300_get_eeprom_rev,
.set_board_values = ath9k_hw_ar9300_set_board_values,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
index ab21a4915981..6335a867527e 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
@@ -233,7 +233,8 @@ struct ar9300_modal_eep_header {
u8 thresh62;
__le32 papdRateMaskHt20;
__le32 papdRateMaskHt40;
- u8 futureModal[10];
+ __le16 switchcomspdt;
+ u8 futureModal[8];
} __packed;
struct ar9300_cal_data_per_freq_op_loop {
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index 392bf0f8ff16..fb937ba93e0c 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -19,6 +19,11 @@
#include "ar9003_2p2_initvals.h"
#include "ar9485_initvals.h"
#include "ar9340_initvals.h"
+#include "ar9330_1p1_initvals.h"
+#include "ar9330_1p2_initvals.h"
+#include "ar9580_1p0_initvals.h"
+#include "ar9462_1p0_initvals.h"
+#include "ar9462_2p0_initvals.h"
/* General hardware code for the AR9003 hadware family */
@@ -29,7 +34,121 @@
*/
static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
{
- if (AR_SREV_9340(ah)) {
+#define PCIE_PLL_ON_CREQ_DIS_L1_2P0 \
+ ar9462_pciephy_pll_on_clkreq_disable_L1_2p0
+
+#define AR9462_BB_CTX_COEFJ(x) \
+ ar9462_##x##_baseband_core_txfir_coeff_japan_2484
+
+#define AR9462_BBC_TXIFR_COEFFJ \
+ ar9462_2p0_baseband_core_txfir_coeff_japan_2484
+ if (AR_SREV_9330_11(ah)) {
+ /* mac */
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
+ ar9331_1p1_mac_core,
+ ARRAY_SIZE(ar9331_1p1_mac_core), 2);
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
+ ar9331_1p1_mac_postamble,
+ ARRAY_SIZE(ar9331_1p1_mac_postamble), 5);
+
+ /* bb */
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+ ar9331_1p1_baseband_core,
+ ARRAY_SIZE(ar9331_1p1_baseband_core), 2);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+ ar9331_1p1_baseband_postamble,
+ ARRAY_SIZE(ar9331_1p1_baseband_postamble), 5);
+
+ /* radio */
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
+ ar9331_1p1_radio_core,
+ ARRAY_SIZE(ar9331_1p1_radio_core), 2);
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], NULL, 0, 0);
+
+ /* soc */
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE],
+ ar9331_1p1_soc_preamble,
+ ARRAY_SIZE(ar9331_1p1_soc_preamble), 2);
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_CORE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST],
+ ar9331_1p1_soc_postamble,
+ ARRAY_SIZE(ar9331_1p1_soc_postamble), 2);
+
+ /* rx/tx gain */
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9331_common_rx_gain_1p1,
+ ARRAY_SIZE(ar9331_common_rx_gain_1p1), 2);
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9331_modes_lowest_ob_db_tx_gain_1p1,
+ ARRAY_SIZE(ar9331_modes_lowest_ob_db_tx_gain_1p1),
+ 5);
+
+ /* additional clock settings */
+ if (ah->is_clk_25mhz)
+ INIT_INI_ARRAY(&ah->iniModesAdditional,
+ ar9331_1p1_xtal_25M,
+ ARRAY_SIZE(ar9331_1p1_xtal_25M), 2);
+ else
+ INIT_INI_ARRAY(&ah->iniModesAdditional,
+ ar9331_1p1_xtal_40M,
+ ARRAY_SIZE(ar9331_1p1_xtal_40M), 2);
+ } else if (AR_SREV_9330_12(ah)) {
+ /* mac */
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
+ ar9331_1p2_mac_core,
+ ARRAY_SIZE(ar9331_1p2_mac_core), 2);
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
+ ar9331_1p2_mac_postamble,
+ ARRAY_SIZE(ar9331_1p2_mac_postamble), 5);
+
+ /* bb */
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+ ar9331_1p2_baseband_core,
+ ARRAY_SIZE(ar9331_1p2_baseband_core), 2);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+ ar9331_1p2_baseband_postamble,
+ ARRAY_SIZE(ar9331_1p2_baseband_postamble), 5);
+
+ /* radio */
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
+ ar9331_1p2_radio_core,
+ ARRAY_SIZE(ar9331_1p2_radio_core), 2);
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], NULL, 0, 0);
+
+ /* soc */
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE],
+ ar9331_1p2_soc_preamble,
+ ARRAY_SIZE(ar9331_1p2_soc_preamble), 2);
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_CORE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST],
+ ar9331_1p2_soc_postamble,
+ ARRAY_SIZE(ar9331_1p2_soc_postamble), 2);
+
+ /* rx/tx gain */
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9331_common_rx_gain_1p2,
+ ARRAY_SIZE(ar9331_common_rx_gain_1p2), 2);
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9331_modes_lowest_ob_db_tx_gain_1p2,
+ ARRAY_SIZE(ar9331_modes_lowest_ob_db_tx_gain_1p2),
+ 5);
+
+ /* additional clock settings */
+ if (ah->is_clk_25mhz)
+ INIT_INI_ARRAY(&ah->iniModesAdditional,
+ ar9331_1p2_xtal_25M,
+ ARRAY_SIZE(ar9331_1p2_xtal_25M), 2);
+ else
+ INIT_INI_ARRAY(&ah->iniModesAdditional,
+ ar9331_1p2_xtal_40M,
+ ARRAY_SIZE(ar9331_1p2_xtal_40M), 2);
+ } else if (AR_SREV_9340(ah)) {
/* mac */
INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0);
INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
@@ -145,6 +264,182 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
ar9485_1_1_pcie_phy_clkreq_disable_L1,
ARRAY_SIZE(ar9485_1_1_pcie_phy_clkreq_disable_L1),
2);
+ } else if (AR_SREV_9462_10(ah)) {
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_1p0_mac_core,
+ ARRAY_SIZE(ar9462_1p0_mac_core), 2);
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
+ ar9462_1p0_mac_postamble,
+ ARRAY_SIZE(ar9462_1p0_mac_postamble),
+ 5);
+
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+ ar9462_1p0_baseband_core,
+ ARRAY_SIZE(ar9462_1p0_baseband_core),
+ 2);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+ ar9462_1p0_baseband_postamble,
+ ARRAY_SIZE(ar9462_1p0_baseband_postamble), 5);
+
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
+ ar9462_1p0_radio_core,
+ ARRAY_SIZE(ar9462_1p0_radio_core), 2);
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST],
+ ar9462_1p0_radio_postamble,
+ ARRAY_SIZE(ar9462_1p0_radio_postamble), 5);
+
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE],
+ ar9462_1p0_soc_preamble,
+ ARRAY_SIZE(ar9462_1p0_soc_preamble), 2);
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_CORE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST],
+ ar9462_1p0_soc_postamble,
+ ARRAY_SIZE(ar9462_1p0_soc_postamble), 5);
+
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9462_common_rx_gain_table_1p0,
+ ARRAY_SIZE(ar9462_common_rx_gain_table_1p0), 2);
+
+ /* Awake -> Sleep Setting */
+ INIT_INI_ARRAY(&ah->iniPcieSerdes,
+ ar9462_pcie_phy_clkreq_disable_L1_1p0,
+ ARRAY_SIZE(ar9462_pcie_phy_clkreq_disable_L1_1p0),
+ 2);
+
+ /* Sleep -> Awake Setting */
+ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+ ar9462_pcie_phy_clkreq_disable_L1_1p0,
+ ARRAY_SIZE(ar9462_pcie_phy_clkreq_disable_L1_1p0),
+ 2);
+
+ INIT_INI_ARRAY(&ah->iniModesAdditional,
+ ar9462_modes_fast_clock_1p0,
+ ARRAY_SIZE(ar9462_modes_fast_clock_1p0), 3);
+ INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
+ AR9462_BB_CTX_COEFJ(1p0),
+ ARRAY_SIZE(AR9462_BB_CTX_COEFJ(1p0)), 2);
+
+ } else if (AR_SREV_9462_20(ah)) {
+
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_2p0_mac_core,
+ ARRAY_SIZE(ar9462_2p0_mac_core), 2);
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
+ ar9462_2p0_mac_postamble,
+ ARRAY_SIZE(ar9462_2p0_mac_postamble), 5);
+
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+ ar9462_2p0_baseband_core,
+ ARRAY_SIZE(ar9462_2p0_baseband_core), 2);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+ ar9462_2p0_baseband_postamble,
+ ARRAY_SIZE(ar9462_2p0_baseband_postamble), 5);
+
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
+ ar9462_2p0_radio_core,
+ ARRAY_SIZE(ar9462_2p0_radio_core), 2);
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST],
+ ar9462_2p0_radio_postamble,
+ ARRAY_SIZE(ar9462_2p0_radio_postamble), 5);
+ INIT_INI_ARRAY(&ah->ini_radio_post_sys2ant,
+ ar9462_2p0_radio_postamble_sys2ant,
+ ARRAY_SIZE(ar9462_2p0_radio_postamble_sys2ant),
+ 5);
+
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE],
+ ar9462_2p0_soc_preamble,
+ ARRAY_SIZE(ar9462_2p0_soc_preamble), 2);
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_CORE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST],
+ ar9462_2p0_soc_postamble,
+ ARRAY_SIZE(ar9462_2p0_soc_postamble), 5);
+
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9462_common_rx_gain_table_2p0,
+ ARRAY_SIZE(ar9462_common_rx_gain_table_2p0), 2);
+
+ INIT_INI_ARRAY(&ah->ini_BTCOEX_MAX_TXPWR,
+ ar9462_2p0_BTCOEX_MAX_TXPWR_table,
+ ARRAY_SIZE(ar9462_2p0_BTCOEX_MAX_TXPWR_table),
+ 2);
+
+ /* Awake -> Sleep Setting */
+ INIT_INI_ARRAY(&ah->iniPcieSerdes,
+ PCIE_PLL_ON_CREQ_DIS_L1_2P0,
+ ARRAY_SIZE(PCIE_PLL_ON_CREQ_DIS_L1_2P0),
+ 2);
+ /* Sleep -> Awake Setting */
+ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+ PCIE_PLL_ON_CREQ_DIS_L1_2P0,
+ ARRAY_SIZE(PCIE_PLL_ON_CREQ_DIS_L1_2P0),
+ 2);
+
+ /* Fast clock modal settings */
+ INIT_INI_ARRAY(&ah->iniModesAdditional,
+ ar9462_modes_fast_clock_2p0,
+ ARRAY_SIZE(ar9462_modes_fast_clock_2p0), 3);
+
+ INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
+ AR9462_BB_CTX_COEFJ(2p0),
+ ARRAY_SIZE(AR9462_BB_CTX_COEFJ(2p0)), 2);
+
+ INIT_INI_ARRAY(&ah->ini_japan2484, AR9462_BBC_TXIFR_COEFFJ,
+ ARRAY_SIZE(AR9462_BBC_TXIFR_COEFFJ), 2);
+
+ } else if (AR_SREV_9580(ah)) {
+ /* mac */
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
+ ar9580_1p0_mac_core,
+ ARRAY_SIZE(ar9580_1p0_mac_core), 2);
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
+ ar9580_1p0_mac_postamble,
+ ARRAY_SIZE(ar9580_1p0_mac_postamble), 5);
+
+ /* bb */
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+ ar9580_1p0_baseband_core,
+ ARRAY_SIZE(ar9580_1p0_baseband_core), 2);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+ ar9580_1p0_baseband_postamble,
+ ARRAY_SIZE(ar9580_1p0_baseband_postamble), 5);
+
+ /* radio */
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_PRE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
+ ar9580_1p0_radio_core,
+ ARRAY_SIZE(ar9580_1p0_radio_core), 2);
+ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST],
+ ar9580_1p0_radio_postamble,
+ ARRAY_SIZE(ar9580_1p0_radio_postamble), 5);
+
+ /* soc */
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE],
+ ar9580_1p0_soc_preamble,
+ ARRAY_SIZE(ar9580_1p0_soc_preamble), 2);
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_CORE], NULL, 0, 0);
+ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST],
+ ar9580_1p0_soc_postamble,
+ ARRAY_SIZE(ar9580_1p0_soc_postamble), 5);
+
+ /* rx/tx gain */
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9580_1p0_rx_gain_table,
+ ARRAY_SIZE(ar9580_1p0_rx_gain_table), 2);
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9580_1p0_low_ob_db_tx_gain_table,
+ ARRAY_SIZE(ar9580_1p0_low_ob_db_tx_gain_table),
+ 5);
+
+ INIT_INI_ARRAY(&ah->iniModesAdditional,
+ ar9580_1p0_modes_fast_clock,
+ ARRAY_SIZE(ar9580_1p0_modes_fast_clock),
+ 3);
} else {
/* mac */
INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0);
@@ -215,118 +510,293 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
}
}
+static void ar9003_tx_gain_table_mode0(struct ath_hw *ah)
+{
+ if (AR_SREV_9330_12(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9331_modes_lowest_ob_db_tx_gain_1p2,
+ ARRAY_SIZE(ar9331_modes_lowest_ob_db_tx_gain_1p2),
+ 5);
+ else if (AR_SREV_9330_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9331_modes_lowest_ob_db_tx_gain_1p1,
+ ARRAY_SIZE(ar9331_modes_lowest_ob_db_tx_gain_1p1),
+ 5);
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9340Modes_lowest_ob_db_tx_gain_table_1p0,
+ ARRAY_SIZE(ar9340Modes_lowest_ob_db_tx_gain_table_1p0),
+ 5);
+ else if (AR_SREV_9485_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9485_modes_lowest_ob_db_tx_gain_1_1,
+ ARRAY_SIZE(ar9485_modes_lowest_ob_db_tx_gain_1_1),
+ 5);
+ else if (AR_SREV_9580(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9580_1p0_lowest_ob_db_tx_gain_table,
+ ARRAY_SIZE(ar9580_1p0_lowest_ob_db_tx_gain_table),
+ 5);
+ else if (AR_SREV_9462_10(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9462_modes_low_ob_db_tx_gain_table_1p0,
+ ARRAY_SIZE(ar9462_modes_low_ob_db_tx_gain_table_1p0),
+ 5);
+ else if (AR_SREV_9462_20(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9462_modes_low_ob_db_tx_gain_table_2p0,
+ ARRAY_SIZE(ar9462_modes_low_ob_db_tx_gain_table_2p0),
+ 5);
+ else
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9300Modes_lowest_ob_db_tx_gain_table_2p2,
+ ARRAY_SIZE(ar9300Modes_lowest_ob_db_tx_gain_table_2p2),
+ 5);
+}
+
+static void ar9003_tx_gain_table_mode1(struct ath_hw *ah)
+{
+ if (AR_SREV_9330_12(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9331_modes_high_ob_db_tx_gain_1p2,
+ ARRAY_SIZE(ar9331_modes_high_ob_db_tx_gain_1p2),
+ 5);
+ else if (AR_SREV_9330_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9331_modes_high_ob_db_tx_gain_1p1,
+ ARRAY_SIZE(ar9331_modes_high_ob_db_tx_gain_1p1),
+ 5);
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9340Modes_lowest_ob_db_tx_gain_table_1p0,
+ ARRAY_SIZE(ar9340Modes_lowest_ob_db_tx_gain_table_1p0),
+ 5);
+ else if (AR_SREV_9485_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9485Modes_high_ob_db_tx_gain_1_1,
+ ARRAY_SIZE(ar9485Modes_high_ob_db_tx_gain_1_1),
+ 5);
+ else if (AR_SREV_9580(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9580_1p0_high_ob_db_tx_gain_table,
+ ARRAY_SIZE(ar9580_1p0_high_ob_db_tx_gain_table),
+ 5);
+ else if (AR_SREV_9462_10(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9462_modes_high_ob_db_tx_gain_table_1p0,
+ ARRAY_SIZE(ar9462_modes_high_ob_db_tx_gain_table_1p0),
+ 5);
+ else if (AR_SREV_9462_20(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9462_modes_high_ob_db_tx_gain_table_2p0,
+ ARRAY_SIZE(ar9462_modes_high_ob_db_tx_gain_table_2p0),
+ 5);
+ else
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9300Modes_high_ob_db_tx_gain_table_2p2,
+ ARRAY_SIZE(ar9300Modes_high_ob_db_tx_gain_table_2p2),
+ 5);
+}
+
+static void ar9003_tx_gain_table_mode2(struct ath_hw *ah)
+{
+ if (AR_SREV_9330_12(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9331_modes_low_ob_db_tx_gain_1p2,
+ ARRAY_SIZE(ar9331_modes_low_ob_db_tx_gain_1p2),
+ 5);
+ else if (AR_SREV_9330_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9331_modes_low_ob_db_tx_gain_1p1,
+ ARRAY_SIZE(ar9331_modes_low_ob_db_tx_gain_1p1),
+ 5);
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9340Modes_lowest_ob_db_tx_gain_table_1p0,
+ ARRAY_SIZE(ar9340Modes_lowest_ob_db_tx_gain_table_1p0),
+ 5);
+ else if (AR_SREV_9485_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9485Modes_low_ob_db_tx_gain_1_1,
+ ARRAY_SIZE(ar9485Modes_low_ob_db_tx_gain_1_1),
+ 5);
+ else if (AR_SREV_9580(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9580_1p0_low_ob_db_tx_gain_table,
+ ARRAY_SIZE(ar9580_1p0_low_ob_db_tx_gain_table),
+ 5);
+ else
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9300Modes_low_ob_db_tx_gain_table_2p2,
+ ARRAY_SIZE(ar9300Modes_low_ob_db_tx_gain_table_2p2),
+ 5);
+}
+
+static void ar9003_tx_gain_table_mode3(struct ath_hw *ah)
+{
+ if (AR_SREV_9330_12(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9331_modes_high_power_tx_gain_1p2,
+ ARRAY_SIZE(ar9331_modes_high_power_tx_gain_1p2),
+ 5);
+ else if (AR_SREV_9330_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9331_modes_high_power_tx_gain_1p1,
+ ARRAY_SIZE(ar9331_modes_high_power_tx_gain_1p1),
+ 5);
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9340Modes_lowest_ob_db_tx_gain_table_1p0,
+ ARRAY_SIZE(ar9340Modes_lowest_ob_db_tx_gain_table_1p0),
+ 5);
+ else if (AR_SREV_9485_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9485Modes_high_power_tx_gain_1_1,
+ ARRAY_SIZE(ar9485Modes_high_power_tx_gain_1_1),
+ 5);
+ else if (AR_SREV_9580(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9580_1p0_high_power_tx_gain_table,
+ ARRAY_SIZE(ar9580_1p0_high_power_tx_gain_table),
+ 5);
+ else
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9300Modes_high_power_tx_gain_table_2p2,
+ ARRAY_SIZE(ar9300Modes_high_power_tx_gain_table_2p2),
+ 5);
+}
+
static void ar9003_tx_gain_table_apply(struct ath_hw *ah)
{
switch (ar9003_hw_get_tx_gain_idx(ah)) {
case 0:
default:
- if (AR_SREV_9340(ah))
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9340Modes_lowest_ob_db_tx_gain_table_1p0,
- ARRAY_SIZE(ar9340Modes_lowest_ob_db_tx_gain_table_1p0),
- 5);
- else if (AR_SREV_9485_11(ah))
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9485_modes_lowest_ob_db_tx_gain_1_1,
- ARRAY_SIZE(ar9485_modes_lowest_ob_db_tx_gain_1_1),
- 5);
- else
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9300Modes_lowest_ob_db_tx_gain_table_2p2,
- ARRAY_SIZE(ar9300Modes_lowest_ob_db_tx_gain_table_2p2),
- 5);
+ ar9003_tx_gain_table_mode0(ah);
break;
case 1:
- if (AR_SREV_9340(ah))
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9340Modes_lowest_ob_db_tx_gain_table_1p0,
- ARRAY_SIZE(ar9340Modes_lowest_ob_db_tx_gain_table_1p0),
- 5);
- else if (AR_SREV_9485_11(ah))
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9485Modes_high_ob_db_tx_gain_1_1,
- ARRAY_SIZE(ar9485Modes_high_ob_db_tx_gain_1_1),
- 5);
- else
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9300Modes_high_ob_db_tx_gain_table_2p2,
- ARRAY_SIZE(ar9300Modes_high_ob_db_tx_gain_table_2p2),
- 5);
+ ar9003_tx_gain_table_mode1(ah);
break;
case 2:
- if (AR_SREV_9340(ah))
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9340Modes_lowest_ob_db_tx_gain_table_1p0,
- ARRAY_SIZE(ar9340Modes_lowest_ob_db_tx_gain_table_1p0),
- 5);
- else if (AR_SREV_9485_11(ah))
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9485Modes_low_ob_db_tx_gain_1_1,
- ARRAY_SIZE(ar9485Modes_low_ob_db_tx_gain_1_1),
- 5);
- else
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9300Modes_low_ob_db_tx_gain_table_2p2,
- ARRAY_SIZE(ar9300Modes_low_ob_db_tx_gain_table_2p2),
- 5);
+ ar9003_tx_gain_table_mode2(ah);
break;
case 3:
- if (AR_SREV_9340(ah))
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9340Modes_lowest_ob_db_tx_gain_table_1p0,
- ARRAY_SIZE(ar9340Modes_lowest_ob_db_tx_gain_table_1p0),
- 5);
- else if (AR_SREV_9485_11(ah))
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9485Modes_high_power_tx_gain_1_1,
- ARRAY_SIZE(ar9485Modes_high_power_tx_gain_1_1),
- 5);
- else
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9300Modes_high_power_tx_gain_table_2p2,
- ARRAY_SIZE(ar9300Modes_high_power_tx_gain_table_2p2),
- 5);
+ ar9003_tx_gain_table_mode3(ah);
break;
}
}
+static void ar9003_rx_gain_table_mode0(struct ath_hw *ah)
+{
+ if (AR_SREV_9330_12(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9331_common_rx_gain_1p2,
+ ARRAY_SIZE(ar9331_common_rx_gain_1p2),
+ 2);
+ else if (AR_SREV_9330_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9331_common_rx_gain_1p1,
+ ARRAY_SIZE(ar9331_common_rx_gain_1p1),
+ 2);
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9340Common_rx_gain_table_1p0,
+ ARRAY_SIZE(ar9340Common_rx_gain_table_1p0),
+ 2);
+ else if (AR_SREV_9485_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9485Common_wo_xlna_rx_gain_1_1,
+ ARRAY_SIZE(ar9485Common_wo_xlna_rx_gain_1_1),
+ 2);
+ else if (AR_SREV_9580(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9580_1p0_rx_gain_table,
+ ARRAY_SIZE(ar9580_1p0_rx_gain_table),
+ 2);
+ else if (AR_SREV_9462_10(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9462_common_rx_gain_table_1p0,
+ ARRAY_SIZE(ar9462_common_rx_gain_table_1p0),
+ 2);
+ else if (AR_SREV_9462_20(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9462_common_rx_gain_table_2p0,
+ ARRAY_SIZE(ar9462_common_rx_gain_table_2p0),
+ 2);
+ else
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9300Common_rx_gain_table_2p2,
+ ARRAY_SIZE(ar9300Common_rx_gain_table_2p2),
+ 2);
+}
+
+static void ar9003_rx_gain_table_mode1(struct ath_hw *ah)
+{
+ if (AR_SREV_9330_12(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9331_common_wo_xlna_rx_gain_1p2,
+ ARRAY_SIZE(ar9331_common_wo_xlna_rx_gain_1p2),
+ 2);
+ else if (AR_SREV_9330_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9331_common_wo_xlna_rx_gain_1p1,
+ ARRAY_SIZE(ar9331_common_wo_xlna_rx_gain_1p1),
+ 2);
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9340Common_wo_xlna_rx_gain_table_1p0,
+ ARRAY_SIZE(ar9340Common_wo_xlna_rx_gain_table_1p0),
+ 2);
+ else if (AR_SREV_9485_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9485Common_wo_xlna_rx_gain_1_1,
+ ARRAY_SIZE(ar9485Common_wo_xlna_rx_gain_1_1),
+ 2);
+ else if (AR_SREV_9462_10(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9462_common_wo_xlna_rx_gain_table_1p0,
+ ARRAY_SIZE(ar9462_common_wo_xlna_rx_gain_table_1p0),
+ 2);
+ else if (AR_SREV_9462_20(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9462_common_wo_xlna_rx_gain_table_2p0,
+ ARRAY_SIZE(ar9462_common_wo_xlna_rx_gain_table_2p0),
+ 2);
+ else if (AR_SREV_9580(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9580_1p0_wo_xlna_rx_gain_table,
+ ARRAY_SIZE(ar9580_1p0_wo_xlna_rx_gain_table),
+ 2);
+ else
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9300Common_wo_xlna_rx_gain_table_2p2,
+ ARRAY_SIZE(ar9300Common_wo_xlna_rx_gain_table_2p2),
+ 2);
+}
+
+static void ar9003_rx_gain_table_mode2(struct ath_hw *ah)
+{
+ if (AR_SREV_9462_10(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9462_common_mixed_rx_gain_table_1p0,
+ ARRAY_SIZE(ar9462_common_mixed_rx_gain_table_1p0), 2);
+ else if (AR_SREV_9462_20(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9462_common_mixed_rx_gain_table_2p0,
+ ARRAY_SIZE(ar9462_common_mixed_rx_gain_table_2p0), 2);
+}
+
static void ar9003_rx_gain_table_apply(struct ath_hw *ah)
{
switch (ar9003_hw_get_rx_gain_idx(ah)) {
case 0:
default:
- if (AR_SREV_9340(ah))
- INIT_INI_ARRAY(&ah->iniModesRxGain,
- ar9340Common_rx_gain_table_1p0,
- ARRAY_SIZE(ar9340Common_rx_gain_table_1p0),
- 2);
- else if (AR_SREV_9485_11(ah))
- INIT_INI_ARRAY(&ah->iniModesRxGain,
- ar9485Common_wo_xlna_rx_gain_1_1,
- ARRAY_SIZE(ar9485Common_wo_xlna_rx_gain_1_1),
- 2);
- else
- INIT_INI_ARRAY(&ah->iniModesRxGain,
- ar9300Common_rx_gain_table_2p2,
- ARRAY_SIZE(ar9300Common_rx_gain_table_2p2),
- 2);
+ ar9003_rx_gain_table_mode0(ah);
break;
case 1:
- if (AR_SREV_9340(ah))
- INIT_INI_ARRAY(&ah->iniModesRxGain,
- ar9340Common_wo_xlna_rx_gain_table_1p0,
- ARRAY_SIZE(ar9340Common_wo_xlna_rx_gain_table_1p0),
- 2);
- else if (AR_SREV_9485_11(ah))
- INIT_INI_ARRAY(&ah->iniModesRxGain,
- ar9485Common_wo_xlna_rx_gain_1_1,
- ARRAY_SIZE(ar9485Common_wo_xlna_rx_gain_1_1),
- 2);
- else
- INIT_INI_ARRAY(&ah->iniModesRxGain,
- ar9300Common_wo_xlna_rx_gain_table_2p2,
- ARRAY_SIZE(ar9300Common_wo_xlna_rx_gain_table_2p2),
- 2);
+ ar9003_rx_gain_table_mode1(ah);
+ break;
+ case 2:
+ ar9003_rx_gain_table_mode2(ah);
break;
}
}
@@ -348,18 +818,10 @@ static void ar9003_hw_init_mode_gain_regs(struct ath_hw *ah)
* register as the other analog registers. Hence the 9 writes.
*/
static void ar9003_hw_configpcipowersave(struct ath_hw *ah,
- int restore,
- int power_off)
+ bool power_off)
{
- if (ah->is_pciexpress != true)
- return;
-
- /* Do not touch SerDes registers */
- if (ah->config.pcie_powersave_enable == 2)
- return;
-
/* Nothing to do on restore for 11N */
- if (!restore) {
+ if (!power_off /* !restore */) {
/* set bit 19 to allow forcing of pcie core into L1 state */
REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
index 10d71f7d3fc2..ccde784a842f 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
@@ -13,6 +13,7 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/export.h>
#include "hw.h"
#include "ar9003_mac.h"
@@ -21,6 +22,132 @@ static void ar9003_hw_rx_enable(struct ath_hw *hw)
REG_WRITE(hw, AR_CR, 0);
}
+static void
+ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
+{
+ struct ar9003_txc *ads = ds;
+ int checksum = 0;
+ u32 val, ctl12, ctl17;
+
+ val = (ATHEROS_VENDOR_ID << AR_DescId_S) |
+ (1 << AR_TxRxDesc_S) |
+ (1 << AR_CtrlStat_S) |
+ (i->qcu << AR_TxQcuNum_S) | 0x17;
+
+ checksum += val;
+ ACCESS_ONCE(ads->info) = val;
+
+ checksum += i->link;
+ ACCESS_ONCE(ads->link) = i->link;
+
+ checksum += i->buf_addr[0];
+ ACCESS_ONCE(ads->data0) = i->buf_addr[0];
+ checksum += i->buf_addr[1];
+ ACCESS_ONCE(ads->data1) = i->buf_addr[1];
+ checksum += i->buf_addr[2];
+ ACCESS_ONCE(ads->data2) = i->buf_addr[2];
+ checksum += i->buf_addr[3];
+ ACCESS_ONCE(ads->data3) = i->buf_addr[3];
+
+ checksum += (val = (i->buf_len[0] << AR_BufLen_S) & AR_BufLen);
+ ACCESS_ONCE(ads->ctl3) = val;
+ checksum += (val = (i->buf_len[1] << AR_BufLen_S) & AR_BufLen);
+ ACCESS_ONCE(ads->ctl5) = val;
+ checksum += (val = (i->buf_len[2] << AR_BufLen_S) & AR_BufLen);
+ ACCESS_ONCE(ads->ctl7) = val;
+ checksum += (val = (i->buf_len[3] << AR_BufLen_S) & AR_BufLen);
+ ACCESS_ONCE(ads->ctl9) = val;
+
+ checksum = (u16) (((checksum & 0xffff) + (checksum >> 16)) & 0xffff);
+ ACCESS_ONCE(ads->ctl10) = checksum;
+
+ if (i->is_first || i->is_last) {
+ ACCESS_ONCE(ads->ctl13) = set11nTries(i->rates, 0)
+ | set11nTries(i->rates, 1)
+ | set11nTries(i->rates, 2)
+ | set11nTries(i->rates, 3)
+ | (i->dur_update ? AR_DurUpdateEna : 0)
+ | SM(0, AR_BurstDur);
+
+ ACCESS_ONCE(ads->ctl14) = set11nRate(i->rates, 0)
+ | set11nRate(i->rates, 1)
+ | set11nRate(i->rates, 2)
+ | set11nRate(i->rates, 3);
+ } else {
+ ACCESS_ONCE(ads->ctl13) = 0;
+ ACCESS_ONCE(ads->ctl14) = 0;
+ }
+
+ ads->ctl20 = 0;
+ ads->ctl21 = 0;
+ ads->ctl22 = 0;
+
+ ctl17 = SM(i->keytype, AR_EncrType);
+ if (!i->is_first) {
+ ACCESS_ONCE(ads->ctl11) = 0;
+ ACCESS_ONCE(ads->ctl12) = i->is_last ? 0 : AR_TxMore;
+ ACCESS_ONCE(ads->ctl15) = 0;
+ ACCESS_ONCE(ads->ctl16) = 0;
+ ACCESS_ONCE(ads->ctl17) = ctl17;
+ ACCESS_ONCE(ads->ctl18) = 0;
+ ACCESS_ONCE(ads->ctl19) = 0;
+ return;
+ }
+
+ ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen)
+ | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
+ | SM(i->txpower, AR_XmitPower)
+ | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
+ | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
+ | (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0)
+ | (i->flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0)
+ | (i->flags & ATH9K_TXDESC_RTSENA ? AR_RTSEnable :
+ (i->flags & ATH9K_TXDESC_CTSENA ? AR_CTSEnable : 0));
+
+ ctl12 = (i->keyix != ATH9K_TXKEYIX_INVALID ?
+ SM(i->keyix, AR_DestIdx) : 0)
+ | SM(i->type, AR_FrameType)
+ | (i->flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0)
+ | (i->flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0)
+ | (i->flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0);
+
+ ctl17 |= (i->flags & ATH9K_TXDESC_LDPC ? AR_LDPC : 0);
+ switch (i->aggr) {
+ case AGGR_BUF_FIRST:
+ ctl17 |= SM(i->aggr_len, AR_AggrLen);
+ /* fall through */
+ case AGGR_BUF_MIDDLE:
+ ctl12 |= AR_IsAggr | AR_MoreAggr;
+ ctl17 |= SM(i->ndelim, AR_PadDelim);
+ break;
+ case AGGR_BUF_LAST:
+ ctl12 |= AR_IsAggr;
+ break;
+ case AGGR_BUF_NONE:
+ break;
+ }
+
+ val = (i->flags & ATH9K_TXDESC_PAPRD) >> ATH9K_TXDESC_PAPRD_S;
+ ctl12 |= SM(val, AR_PAPRDChainMask);
+
+ ACCESS_ONCE(ads->ctl12) = ctl12;
+ ACCESS_ONCE(ads->ctl17) = ctl17;
+
+ ACCESS_ONCE(ads->ctl15) = set11nPktDurRTSCTS(i->rates, 0)
+ | set11nPktDurRTSCTS(i->rates, 1);
+
+ ACCESS_ONCE(ads->ctl16) = set11nPktDurRTSCTS(i->rates, 2)
+ | set11nPktDurRTSCTS(i->rates, 3);
+
+ ACCESS_ONCE(ads->ctl18) = set11nRateFlags(i->rates, 0)
+ | set11nRateFlags(i->rates, 1)
+ | set11nRateFlags(i->rates, 2)
+ | set11nRateFlags(i->rates, 3)
+ | SM(i->rtscts_rate, AR_RTSCTSRate);
+
+ ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
+}
+
static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads)
{
int checksum;
@@ -43,13 +170,6 @@ static void ar9003_hw_set_desc_link(void *ds, u32 ds_link)
ads->ctl10 |= ar9003_calc_ptr_chksum(ads);
}
-static void ar9003_hw_get_desc_link(void *ds, u32 **ds_link)
-{
- struct ar9003_txc *ads = ds;
-
- *ds_link = &ads->link;
-}
-
static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
{
u32 isr = 0;
@@ -192,50 +312,10 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
return true;
}
-static void ar9003_hw_fill_txdesc(struct ath_hw *ah, void *ds, u32 seglen,
- bool is_firstseg, bool is_lastseg,
- const void *ds0, dma_addr_t buf_addr,
- unsigned int qcu)
-{
- struct ar9003_txc *ads = (struct ar9003_txc *) ds;
- unsigned int descid = 0;
-
- ads->info = (ATHEROS_VENDOR_ID << AR_DescId_S) |
- (1 << AR_TxRxDesc_S) |
- (1 << AR_CtrlStat_S) |
- (qcu << AR_TxQcuNum_S) | 0x17;
-
- ads->data0 = buf_addr;
- ads->data1 = 0;
- ads->data2 = 0;
- ads->data3 = 0;
-
- ads->ctl3 = (seglen << AR_BufLen_S);
- ads->ctl3 &= AR_BufLen;
-
- /* Fill in pointer checksum and descriptor id */
- ads->ctl10 = ar9003_calc_ptr_chksum(ads);
- ads->ctl10 |= (descid << AR_TxDescId_S);
-
- if (is_firstseg) {
- ads->ctl12 |= (is_lastseg ? 0 : AR_TxMore);
- } else if (is_lastseg) {
- ads->ctl11 = 0;
- ads->ctl12 = 0;
- ads->ctl13 = AR9003TXC_CONST(ds0)->ctl13;
- ads->ctl14 = AR9003TXC_CONST(ds0)->ctl14;
- } else {
- /* XXX Intermediate descriptor in a multi-descriptor frame.*/
- ads->ctl11 = 0;
- ads->ctl12 = AR_TxMore;
- ads->ctl13 = 0;
- ads->ctl14 = 0;
- }
-}
-
static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
struct ath_tx_status *ts)
{
+ struct ar9003_txc *txc = (struct ar9003_txc *) ds;
struct ar9003_txs *ads;
u32 status;
@@ -245,7 +325,11 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
if ((status & AR_TxDone) == 0)
return -EINPROGRESS;
- ah->ts_tail = (ah->ts_tail + 1) % ah->ts_size;
+ ts->qid = MS(ads->ds_info, AR_TxQcuNum);
+ if (!txc || (MS(txc->info, AR_TxQcuNum) == ts->qid))
+ ah->ts_tail = (ah->ts_tail + 1) % ah->ts_size;
+ else
+ return -ENOENT;
if ((MS(ads->ds_info, AR_DescId) != ATHEROS_VENDOR_ID) ||
(MS(ads->ds_info, AR_TxRxDesc) != 1)) {
@@ -255,18 +339,17 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
return -EIO;
}
- if (status & AR_TxOpExceeded)
- ts->ts_status |= ATH9K_TXERR_XTXOP;
ts->ts_rateindex = MS(status, AR_FinalTxIdx);
ts->ts_seqnum = MS(status, AR_SeqNum);
ts->tid = MS(status, AR_TxTid);
- ts->qid = MS(ads->ds_info, AR_TxQcuNum);
ts->desc_id = MS(ads->status1, AR_TxDescId);
ts->ts_tstamp = ads->status4;
ts->ts_status = 0;
ts->ts_flags = 0;
+ if (status & AR_TxOpExceeded)
+ ts->ts_status |= ATH9K_TXERR_XTXOP;
status = ACCESS_ONCE(ads->status2);
ts->ts_rssi_ctl0 = MS(status, AR_TxRSSIAnt00);
ts->ts_rssi_ctl1 = MS(status, AR_TxRSSIAnt01);
@@ -313,202 +396,15 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
return 0;
}
-static void ar9003_hw_set11n_txdesc(struct ath_hw *ah, void *ds,
- u32 pktlen, enum ath9k_pkt_type type, u32 txpower,
- u32 keyIx, enum ath9k_key_type keyType, u32 flags)
-{
- struct ar9003_txc *ads = (struct ar9003_txc *) ds;
-
- if (txpower > ah->txpower_limit)
- txpower = ah->txpower_limit;
-
- if (txpower > 63)
- txpower = 63;
-
- ads->ctl11 = (pktlen & AR_FrameLen)
- | (flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
- | SM(txpower, AR_XmitPower)
- | (flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
- | (keyIx != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
- | (flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0);
-
- ads->ctl12 =
- (keyIx != ATH9K_TXKEYIX_INVALID ? SM(keyIx, AR_DestIdx) : 0)
- | SM(type, AR_FrameType)
- | (flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0)
- | (flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0)
- | (flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0);
-
- ads->ctl17 = SM(keyType, AR_EncrType) |
- (flags & ATH9K_TXDESC_LDPC ? AR_LDPC : 0);
- ads->ctl18 = 0;
- ads->ctl19 = AR_Not_Sounding;
-
- ads->ctl20 = 0;
- ads->ctl21 = 0;
- ads->ctl22 = 0;
-}
-
-static void ar9003_hw_set_clrdmask(struct ath_hw *ah, void *ds, bool val)
-{
- struct ar9003_txc *ads = (struct ar9003_txc *) ds;
-
- if (val)
- ads->ctl11 |= AR_ClrDestMask;
- else
- ads->ctl11 &= ~AR_ClrDestMask;
-}
-
-static void ar9003_hw_set11n_ratescenario(struct ath_hw *ah, void *ds,
- void *lastds,
- u32 durUpdateEn, u32 rtsctsRate,
- u32 rtsctsDuration,
- struct ath9k_11n_rate_series series[],
- u32 nseries, u32 flags)
-{
- struct ar9003_txc *ads = (struct ar9003_txc *) ds;
- struct ar9003_txc *last_ads = (struct ar9003_txc *) lastds;
- u_int32_t ctl11;
-
- if (flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA)) {
- ctl11 = ads->ctl11;
-
- if (flags & ATH9K_TXDESC_RTSENA) {
- ctl11 &= ~AR_CTSEnable;
- ctl11 |= AR_RTSEnable;
- } else {
- ctl11 &= ~AR_RTSEnable;
- ctl11 |= AR_CTSEnable;
- }
-
- ads->ctl11 = ctl11;
- } else {
- ads->ctl11 = (ads->ctl11 & ~(AR_RTSEnable | AR_CTSEnable));
- }
-
- ads->ctl13 = set11nTries(series, 0)
- | set11nTries(series, 1)
- | set11nTries(series, 2)
- | set11nTries(series, 3)
- | (durUpdateEn ? AR_DurUpdateEna : 0)
- | SM(0, AR_BurstDur);
-
- ads->ctl14 = set11nRate(series, 0)
- | set11nRate(series, 1)
- | set11nRate(series, 2)
- | set11nRate(series, 3);
-
- ads->ctl15 = set11nPktDurRTSCTS(series, 0)
- | set11nPktDurRTSCTS(series, 1);
-
- ads->ctl16 = set11nPktDurRTSCTS(series, 2)
- | set11nPktDurRTSCTS(series, 3);
-
- ads->ctl18 = set11nRateFlags(series, 0)
- | set11nRateFlags(series, 1)
- | set11nRateFlags(series, 2)
- | set11nRateFlags(series, 3)
- | SM(rtsctsRate, AR_RTSCTSRate);
- ads->ctl19 = AR_Not_Sounding;
-
- last_ads->ctl13 = ads->ctl13;
- last_ads->ctl14 = ads->ctl14;
-}
-
-static void ar9003_hw_set11n_aggr_first(struct ath_hw *ah, void *ds,
- u32 aggrLen)
-{
-#define FIRST_DESC_NDELIMS 60
- struct ar9003_txc *ads = (struct ar9003_txc *) ds;
-
- ads->ctl12 |= (AR_IsAggr | AR_MoreAggr);
-
- if (ah->ent_mode & AR_ENT_OTP_MPSD) {
- u32 ctl17, ndelim;
- /*
- * Add delimiter when using RTS/CTS with aggregation
- * and non enterprise AR9003 card
- */
- ctl17 = ads->ctl17;
- ndelim = MS(ctl17, AR_PadDelim);
-
- if (ndelim < FIRST_DESC_NDELIMS) {
- aggrLen += (FIRST_DESC_NDELIMS - ndelim) * 4;
- ndelim = FIRST_DESC_NDELIMS;
- }
-
- ctl17 &= ~AR_AggrLen;
- ctl17 |= SM(aggrLen, AR_AggrLen);
-
- ctl17 &= ~AR_PadDelim;
- ctl17 |= SM(ndelim, AR_PadDelim);
-
- ads->ctl17 = ctl17;
- } else {
- ads->ctl17 &= ~AR_AggrLen;
- ads->ctl17 |= SM(aggrLen, AR_AggrLen);
- }
-}
-
-static void ar9003_hw_set11n_aggr_middle(struct ath_hw *ah, void *ds,
- u32 numDelims)
-{
- struct ar9003_txc *ads = (struct ar9003_txc *) ds;
- unsigned int ctl17;
-
- ads->ctl12 |= (AR_IsAggr | AR_MoreAggr);
-
- /*
- * We use a stack variable to manipulate ctl6 to reduce uncached
- * read modify, modfiy, write.
- */
- ctl17 = ads->ctl17;
- ctl17 &= ~AR_PadDelim;
- ctl17 |= SM(numDelims, AR_PadDelim);
- ads->ctl17 = ctl17;
-}
-
-static void ar9003_hw_set11n_aggr_last(struct ath_hw *ah, void *ds)
-{
- struct ar9003_txc *ads = (struct ar9003_txc *) ds;
-
- ads->ctl12 |= AR_IsAggr;
- ads->ctl12 &= ~AR_MoreAggr;
- ads->ctl17 &= ~AR_PadDelim;
-}
-
-static void ar9003_hw_clr11n_aggr(struct ath_hw *ah, void *ds)
-{
- struct ar9003_txc *ads = (struct ar9003_txc *) ds;
-
- ads->ctl12 &= (~AR_IsAggr & ~AR_MoreAggr);
-}
-
-void ar9003_hw_set_paprd_txdesc(struct ath_hw *ah, void *ds, u8 chains)
-{
- struct ar9003_txc *ads = ds;
-
- ads->ctl12 |= SM(chains, AR_PAPRDChainMask);
-}
-EXPORT_SYMBOL(ar9003_hw_set_paprd_txdesc);
-
void ar9003_hw_attach_mac_ops(struct ath_hw *hw)
{
struct ath_hw_ops *ops = ath9k_hw_ops(hw);
ops->rx_enable = ar9003_hw_rx_enable;
ops->set_desc_link = ar9003_hw_set_desc_link;
- ops->get_desc_link = ar9003_hw_get_desc_link;
ops->get_isr = ar9003_hw_get_isr;
- ops->fill_txdesc = ar9003_hw_fill_txdesc;
+ ops->set_txdesc = ar9003_set_txdesc;
ops->proc_txdesc = ar9003_hw_proc_txdesc;
- ops->set11n_txdesc = ar9003_hw_set11n_txdesc;
- ops->set11n_ratescenario = ar9003_hw_set11n_ratescenario;
- ops->set11n_aggr_first = ar9003_hw_set11n_aggr_first;
- ops->set11n_aggr_middle = ar9003_hw_set11n_aggr_middle;
- ops->set11n_aggr_last = ar9003_hw_set11n_aggr_last;
- ops->clr11n_aggr = ar9003_hw_clr11n_aggr;
- ops->set_clrdmask = ar9003_hw_set_clrdmask;
}
void ath9k_hw_set_rx_bufsize(struct ath_hw *ah, u16 buf_size)
@@ -535,17 +431,18 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
/* TODO: byte swap on big endian for ar9300_10 */
- if ((rxsp->status11 & AR_RxDone) == 0)
- return -EINPROGRESS;
+ if (!rxs) {
+ if ((rxsp->status11 & AR_RxDone) == 0)
+ return -EINPROGRESS;
- if (MS(rxsp->ds_info, AR_DescId) != 0x168c)
- return -EINVAL;
+ if (MS(rxsp->ds_info, AR_DescId) != 0x168c)
+ return -EINVAL;
- if ((rxsp->ds_info & (AR_TxRxDesc | AR_CtrlStat)) != 0)
- return -EINPROGRESS;
+ if ((rxsp->ds_info & (AR_TxRxDesc | AR_CtrlStat)) != 0)
+ return -EINPROGRESS;
- if (!rxs)
return 0;
+ }
rxs->rs_status = 0;
rxs->rs_flags = 0;
@@ -629,9 +526,8 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
rxs->rs_status |= ATH9K_RXERR_DECRYPT;
else if (rxsp->status11 & AR_MichaelErr)
rxs->rs_status |= ATH9K_RXERR_MIC;
-
if (rxsp->status11 & AR_KeyMiss)
- rxs->rs_status |= ATH9K_RXERR_DECRYPT;
+ rxs->rs_status |= ATH9K_RXERR_KEYMISS;
}
return 0;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
index e4d6a87ec538..a4450cba0653 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
@@ -14,23 +14,47 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/export.h>
#include "hw.h"
#include "ar9003_phy.h"
void ar9003_paprd_enable(struct ath_hw *ah, bool val)
{
- struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ath9k_channel *chan = ah->curchan;
+ struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
+
+ /*
+ * 3 bits for modalHeader5G.papdRateMaskHt20
+ * is used for sub-band disabling of PAPRD.
+ * 5G band is divided into 3 sub-bands -- upper,
+ * middle, lower.
+ * if bit 30 of modalHeader5G.papdRateMaskHt20 is set
+ * -- disable PAPRD for upper band 5GHz
+ * if bit 29 of modalHeader5G.papdRateMaskHt20 is set
+ * -- disable PAPRD for middle band 5GHz
+ * if bit 28 of modalHeader5G.papdRateMaskHt20 is set
+ * -- disable PAPRD for lower band 5GHz
+ */
+
+ if (IS_CHAN_5GHZ(chan)) {
+ if (chan->channel >= UPPER_5G_SUB_BAND_START) {
+ if (le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20)
+ & BIT(30))
+ val = false;
+ } else if (chan->channel >= MID_5G_SUB_BAND_START) {
+ if (le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20)
+ & BIT(29))
+ val = false;
+ } else {
+ if (le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20)
+ & BIT(28))
+ val = false;
+ }
+ }
if (val) {
ah->paprd_table_write_done = true;
-
- ah->eep_ops->set_txpower(ah, chan,
- ath9k_regd_get_ctl(regulatory, chan),
- chan->chan->max_antenna_gain * 2,
- chan->chan->max_power * 2,
- min((u32) MAX_RATE_POWER,
- (u32) regulatory->power_limit), false);
+ ath9k_hw_apply_txpower(ah, chan);
}
REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B0,
@@ -46,11 +70,10 @@ EXPORT_SYMBOL(ar9003_paprd_enable);
static int ar9003_get_training_power_2g(struct ath_hw *ah)
{
- struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
- struct ar9300_modal_eep_header *hdr = &eep->modalHeader2G;
+ struct ath9k_channel *chan = ah->curchan;
unsigned int power, scale, delta;
- scale = MS(le32_to_cpu(hdr->papdRateMaskHt20), AR9300_PAPRD_SCALE_1);
+ scale = ar9003_get_paprd_scale_factor(ah, chan);
power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE5,
AR_PHY_POWERTX_RATE5_POWERTXHT20_0);
@@ -67,20 +90,10 @@ static int ar9003_get_training_power_2g(struct ath_hw *ah)
static int ar9003_get_training_power_5g(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
- struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
- struct ar9300_modal_eep_header *hdr = &eep->modalHeader5G;
struct ath9k_channel *chan = ah->curchan;
unsigned int power, scale, delta;
- if (chan->channel >= 5700)
- scale = MS(le32_to_cpu(hdr->papdRateMaskHt20),
- AR9300_PAPRD_SCALE_1);
- else if (chan->channel >= 5400)
- scale = MS(le32_to_cpu(hdr->papdRateMaskHt40),
- AR9300_PAPRD_SCALE_2);
- else
- scale = MS(le32_to_cpu(hdr->papdRateMaskHt40),
- AR9300_PAPRD_SCALE_1);
+ scale = ar9003_get_paprd_scale_factor(ah, chan);
if (IS_CHAN_HT40(chan))
power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE8,
@@ -94,7 +107,23 @@ static int ar9003_get_training_power_5g(struct ath_hw *ah)
if (delta > scale)
return -1;
- power += 2 * get_streams(common->tx_chainmask);
+ switch (get_streams(ah->txchainmask)) {
+ case 1:
+ delta = 6;
+ break;
+ case 2:
+ delta = 4;
+ break;
+ case 3:
+ delta = 2;
+ break;
+ default:
+ delta = 0;
+ ath_dbg(common, ATH_DBG_CALIBRATE,
+ "Invalid tx-chainmask: %u\n", ah->txchainmask);
+ }
+
+ power += delta;
return power;
}
@@ -112,22 +141,23 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
AR_PHY_PAPRD_CTRL1_B2
};
int training_power;
- int i;
+ int i, val;
if (IS_CHAN_2GHZ(ah->curchan))
training_power = ar9003_get_training_power_2g(ah);
else
training_power = ar9003_get_training_power_5g(ah);
+ ath_dbg(common, ATH_DBG_CALIBRATE,
+ "Training power: %d, Target power: %d\n",
+ training_power, ah->paprd_target_power);
+
if (training_power < 0) {
ath_dbg(common, ATH_DBG_CALIBRATE,
"PAPRD target power delta out of range");
return -ERANGE;
}
ah->paprd_training_power = training_power;
- ath_dbg(common, ATH_DBG_CALIBRATE,
- "Training power: %d, Target power: %d\n",
- ah->paprd_training_power, ah->paprd_target_power);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2AM, AR_PHY_PAPRD_AM2AM_MASK,
ah->paprd_ratemask);
@@ -171,8 +201,9 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_AGC2_SETTLING, 28);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1,
AR_PHY_PAPRD_TRAINER_CNTL1_CF_CF_PAPRD_TRAIN_ENABLE, 1);
+ val = AR_SREV_9462(ah) ? 0x91 : 147;
REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL2,
- AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN, 147);
+ AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN, val);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_FINE_CORR_LEN, 4);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
@@ -181,7 +212,7 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_NUM_CORR_STAGES, 7);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_MIN_LOOPBACK_DEL, 1);
- if (AR_SREV_9485(ah))
+ if (AR_SREV_9485(ah) || AR_SREV_9462(ah))
REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP,
-3);
@@ -189,9 +220,10 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP,
-6);
+ val = AR_SREV_9462(ah) ? -10 : -15;
REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_ADC_DESIRED_SIZE,
- -15);
+ val);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_BBTXMIX_DISABLE, 1);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4,
@@ -230,7 +262,7 @@ static void ar9003_paprd_get_gain_table(struct ath_hw *ah)
memset(entry, 0, sizeof(ah->paprd_gain_table_entries));
memset(index, 0, sizeof(ah->paprd_gain_table_index));
- for (i = 0; i < 32; i++) {
+ for (i = 0; i < PAPRD_GAIN_TABLE_ENTRIES; i++) {
entry[i] = REG_READ(ah, reg);
index[i] = (entry[i] >> 24) & 0xff;
reg += 4;
@@ -240,13 +272,13 @@ static void ar9003_paprd_get_gain_table(struct ath_hw *ah)
static unsigned int ar9003_get_desired_gain(struct ath_hw *ah, int chain,
int target_power)
{
- int olpc_gain_delta = 0;
+ int olpc_gain_delta = 0, cl_gain_mod;
int alpha_therm, alpha_volt;
int therm_cal_value, volt_cal_value;
int therm_value, volt_value;
int thermal_gain_corr, voltage_gain_corr;
int desired_scale, desired_gain = 0;
- u32 reg;
+ u32 reg_olpc = 0, reg_cl_gain = 0;
REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1,
AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE);
@@ -265,15 +297,29 @@ static unsigned int ar9003_get_desired_gain(struct ath_hw *ah, int chain,
volt_value = REG_READ_FIELD(ah, AR_PHY_BB_THERM_ADC_4,
AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE);
- if (chain == 0)
- reg = AR_PHY_TPC_11_B0;
- else if (chain == 1)
- reg = AR_PHY_TPC_11_B1;
- else
- reg = AR_PHY_TPC_11_B2;
+ switch (chain) {
+ case 0:
+ reg_olpc = AR_PHY_TPC_11_B0;
+ reg_cl_gain = AR_PHY_CL_TAB_0;
+ break;
+ case 1:
+ reg_olpc = AR_PHY_TPC_11_B1;
+ reg_cl_gain = AR_PHY_CL_TAB_1;
+ break;
+ case 2:
+ reg_olpc = AR_PHY_TPC_11_B2;
+ reg_cl_gain = AR_PHY_CL_TAB_2;
+ break;
+ default:
+ ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
+ "Invalid chainmask: %d\n", chain);
+ break;
+ }
- olpc_gain_delta = REG_READ_FIELD(ah, reg,
+ olpc_gain_delta = REG_READ_FIELD(ah, reg_olpc,
AR_PHY_TPC_11_OLPC_GAIN_DELTA);
+ cl_gain_mod = REG_READ_FIELD(ah, reg_cl_gain,
+ AR_PHY_CL_TAB_CL_GAIN_MOD);
if (olpc_gain_delta >= 128)
olpc_gain_delta = olpc_gain_delta - 256;
@@ -283,7 +329,7 @@ static unsigned int ar9003_get_desired_gain(struct ath_hw *ah, int chain,
voltage_gain_corr = (alpha_volt * (volt_value - volt_cal_value) +
(128 / 2)) / 128;
desired_gain = target_power - olpc_gain_delta - thermal_gain_corr -
- voltage_gain_corr + desired_scale;
+ voltage_gain_corr + desired_scale + cl_gain_mod;
return desired_gain;
}
@@ -707,6 +753,7 @@ void ar9003_paprd_populate_single_table(struct ath_hw *ah,
training_power);
if (ah->caps.tx_chainmask & BIT(2))
+ /* val AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL correct? */
REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B2,
AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL,
training_power);
@@ -721,7 +768,7 @@ int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain)
desired_gain = ar9003_get_desired_gain(ah, chain, train_power);
gain_index = 0;
- for (i = 0; i < 32; i++) {
+ for (i = 0; i < PAPRD_GAIN_TABLE_ENTRIES; i++) {
if (ah->paprd_gain_table_index[i] >= desired_gain)
break;
gain_index++;
@@ -795,7 +842,26 @@ EXPORT_SYMBOL(ar9003_paprd_init_table);
bool ar9003_paprd_is_done(struct ath_hw *ah)
{
- return !!REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1,
+ int paprd_done, agc2_pwr;
+ paprd_done = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1,
AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE);
+
+ if (paprd_done == 0x1) {
+ agc2_pwr = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1,
+ AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR);
+
+ ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
+ "AGC2_PWR = 0x%x training done = 0x%x\n",
+ agc2_pwr, paprd_done);
+ /*
+ * agc2_pwr range should not be less than 'IDEAL_AGC2_PWR_CHANGE'
+ * when the training is completely done, otherwise retraining is
+ * done to make sure the value is in ideal range
+ */
+ if (agc2_pwr <= PAPRD_IDEAL_AGC2_PWR_RANGE)
+ paprd_done = 0;
+ }
+
+ return !!paprd_done;
}
EXPORT_SYMBOL(ar9003_paprd_is_done);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 892c48b15434..2330e7ede199 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/export.h>
#include "hw.h"
#include "ar9003_phy.h"
@@ -75,7 +76,19 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
freq = centers.synth_center;
if (freq < 4800) { /* 2 GHz, fractional mode */
- if (AR_SREV_9485(ah)) {
+ if (AR_SREV_9330(ah)) {
+ u32 chan_frac;
+ u32 div;
+
+ if (ah->is_clk_25mhz)
+ div = 75;
+ else
+ div = 120;
+
+ channelSel = (freq * 4) / div;
+ chan_frac = (((freq * 4) % div) * 0x20000) / div;
+ channelSel = (channelSel << 17) | chan_frac;
+ } else if (AR_SREV_9485(ah)) {
u32 chan_frac;
/*
@@ -104,7 +117,7 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
u32 chan_frac;
channelSel = (freq * 2) / 75;
- chan_frac = ((freq % 75) * 0x20000) / 75;
+ chan_frac = (((freq * 2) % 75) * 0x20000) / 75;
channelSel = (channelSel << 17) | chan_frac;
} else {
channelSel = CHANSEL_5G(freq);
@@ -168,7 +181,7 @@ static void ar9003_hw_spur_mitigate_mrc_cck(struct ath_hw *ah,
* is out-of-band and can be ignored.
*/
- if (AR_SREV_9485(ah) || AR_SREV_9340(ah)) {
+ if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah)) {
spur_fbin_ptr = ar9003_get_spur_chan_ptr(ah,
IS_CHAN_2GHZ(chan));
if (spur_fbin_ptr[0] == 0) /* No spur */
@@ -193,7 +206,7 @@ static void ar9003_hw_spur_mitigate_mrc_cck(struct ath_hw *ah,
for (i = 0; i < max_spur_cnts; i++) {
negative = 0;
- if (AR_SREV_9485(ah) || AR_SREV_9340(ah))
+ if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah))
cur_bb_spur = FBIN2FREQ(spur_fbin_ptr[i],
IS_CHAN_2GHZ(chan)) - synth_freq;
else
@@ -358,7 +371,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah,
else
spur_subchannel_sd = 0;
- spur_freq_sd = ((freq_offset + 10) << 9) / 11;
+ spur_freq_sd = (freq_offset << 9) / 11;
} else {
if (REG_READ_FIELD(ah, AR_PHY_GEN_CTRL,
@@ -367,7 +380,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah,
else
spur_subchannel_sd = 1;
- spur_freq_sd = ((freq_offset - 10) << 9) / 11;
+ spur_freq_sd = (freq_offset << 9) / 11;
}
@@ -470,7 +483,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah,
(REG_READ(ah, AR_PHY_GEN_CTRL) & AR_PHY_GC_ENABLE_DAC_FIFO);
/* Enable 11n HT, 20 MHz */
- phymode = AR_PHY_GC_HT_EN | AR_PHY_GC_SINGLE_HT_LTF1 | AR_PHY_GC_WALSH |
+ phymode = AR_PHY_GC_HT_EN | AR_PHY_GC_SINGLE_HT_LTF1 |
AR_PHY_GC_SHORT_GI_40 | enableDacFifo;
/* Configure baseband for dynamic 20/40 operation */
@@ -528,7 +541,7 @@ static void ar9003_hw_init_bb(struct ath_hw *ah,
udelay(synthDelay + BASE_ACTIVATE_DELAY);
}
-void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx)
+static void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx)
{
switch (rx) {
case 0x5:
@@ -547,6 +560,9 @@ void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx)
if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && (tx == 0x7))
REG_WRITE(ah, AR_SELFGEN_MASK, 0x3);
+ else if (AR_SREV_9462(ah))
+ /* xxx only when MCI support is enabled */
+ REG_WRITE(ah, AR_SELFGEN_MASK, 0x3);
else
REG_WRITE(ah, AR_SELFGEN_MASK, tx);
@@ -580,6 +596,9 @@ static void ar9003_hw_override_ini(struct ath_hw *ah)
val = REG_READ(ah, AR_PCU_MISC_MODE2) & (~AR_ADHOC_MCAST_KEYID_ENABLE);
REG_WRITE(ah, AR_PCU_MISC_MODE2,
val | AR_AGG_WEP_ENABLE_FIX | AR_AGG_WEP_ENABLE);
+
+ REG_SET_BIT(ah, AR_PHY_CCK_DETECT,
+ AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
}
static void ar9003_hw_prog_ini(struct ath_hw *ah,
@@ -613,9 +632,7 @@ static void ar9003_hw_prog_ini(struct ath_hw *ah,
static int ar9003_hw_process_ini(struct ath_hw *ah,
struct ath9k_channel *chan)
{
- struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
unsigned int regWrites = 0, i;
- struct ieee80211_channel *channel = chan->chan;
u32 modesIndex;
switch (chan->chanmode) {
@@ -646,6 +663,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
ar9003_hw_prog_ini(ah, &ah->iniMac[i], modesIndex);
ar9003_hw_prog_ini(ah, &ah->iniBB[i], modesIndex);
ar9003_hw_prog_ini(ah, &ah->iniRadio[i], modesIndex);
+ if (i == ATH_INI_POST && AR_SREV_9462_20(ah))
+ ar9003_hw_prog_ini(ah,
+ &ah->ini_radio_post_sys2ant,
+ modesIndex);
}
REG_WRITE_ARRAY(&ah->iniModesRxGain, 1, regWrites);
@@ -659,20 +680,33 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
REG_WRITE_ARRAY(&ah->iniModesAdditional,
modesIndex, regWrites);
+ if (AR_SREV_9330(ah))
+ REG_WRITE_ARRAY(&ah->iniModesAdditional, 1, regWrites);
+
if (AR_SREV_9340(ah) && !ah->is_clk_25mhz)
REG_WRITE_ARRAY(&ah->iniModesAdditional_40M, 1, regWrites);
+ if (AR_SREV_9462(ah))
+ ar9003_hw_prog_ini(ah, &ah->ini_BTCOEX_MAX_TXPWR, 1);
+
+ ah->modes_index = modesIndex;
ar9003_hw_override_ini(ah);
ar9003_hw_set_channel_regs(ah, chan);
ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
+ ath9k_hw_apply_txpower(ah, chan);
- /* Set TX power */
- ah->eep_ops->set_txpower(ah, chan,
- ath9k_regd_get_ctl(regulatory, chan),
- channel->max_antenna_gain * 2,
- channel->max_power * 2,
- min((u32) MAX_RATE_POWER,
- (u32) regulatory->power_limit), false);
+ if (AR_SREV_9462(ah)) {
+ if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0,
+ AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL))
+ ah->enabled_cals |= TX_IQ_CAL;
+ else
+ ah->enabled_cals &= ~TX_IQ_CAL;
+
+ if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE)
+ ah->enabled_cals |= TX_CL_CAL;
+ else
+ ah->enabled_cals &= ~TX_CL_CAL;
+ }
return 0;
}
@@ -770,16 +804,6 @@ static void ar9003_hw_rfbus_done(struct ath_hw *ah)
REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0);
}
-static void ar9003_hw_set_diversity(struct ath_hw *ah, bool value)
-{
- u32 v = REG_READ(ah, AR_PHY_CCK_DETECT);
- if (value)
- v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
- else
- v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
- REG_WRITE(ah, AR_PHY_CCK_DETECT, v);
-}
-
static bool ar9003_hw_ani_control(struct ath_hw *ah,
enum ath9k_ani_cmd cmd, int param)
{
@@ -1074,7 +1098,10 @@ static void ar9003_hw_set_nf_limits(struct ath_hw *ah)
{
ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ;
ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ;
- ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9300_2GHZ;
+ if (AR_SREV_9330(ah))
+ ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9330_2GHZ;
+ else
+ ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9300_2GHZ;
ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ;
ah->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ;
ah->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9300_5GHZ;
@@ -1196,8 +1223,17 @@ static void ar9003_hw_antdiv_comb_conf_get(struct ath_hw *ah,
AR_PHY_9485_ANT_DIV_ALT_LNACONF_S;
antconf->fast_div_bias = (regval & AR_PHY_9485_ANT_FAST_DIV_BIAS) >>
AR_PHY_9485_ANT_FAST_DIV_BIAS_S;
- antconf->lna1_lna2_delta = -9;
- antconf->div_group = 2;
+
+ if (AR_SREV_9330_11(ah)) {
+ antconf->lna1_lna2_delta = -9;
+ antconf->div_group = 1;
+ } else if (AR_SREV_9485(ah)) {
+ antconf->lna1_lna2_delta = -9;
+ antconf->div_group = 2;
+ } else {
+ antconf->lna1_lna2_delta = -3;
+ antconf->div_group = 0;
+ }
}
static void ar9003_hw_antdiv_comb_conf_set(struct ath_hw *ah,
@@ -1226,6 +1262,73 @@ static void ar9003_hw_antdiv_comb_conf_set(struct ath_hw *ah,
REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
}
+static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
+ struct ath9k_channel *chan,
+ u8 *ini_reloaded)
+{
+ unsigned int regWrites = 0;
+ u32 modesIndex;
+
+ switch (chan->chanmode) {
+ case CHANNEL_A:
+ case CHANNEL_A_HT20:
+ modesIndex = 1;
+ break;
+ case CHANNEL_A_HT40PLUS:
+ case CHANNEL_A_HT40MINUS:
+ modesIndex = 2;
+ break;
+ case CHANNEL_G:
+ case CHANNEL_G_HT20:
+ case CHANNEL_B:
+ modesIndex = 4;
+ break;
+ case CHANNEL_G_HT40PLUS:
+ case CHANNEL_G_HT40MINUS:
+ modesIndex = 3;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (modesIndex == ah->modes_index) {
+ *ini_reloaded = false;
+ goto set_rfmode;
+ }
+
+ ar9003_hw_prog_ini(ah, &ah->iniSOC[ATH_INI_POST], modesIndex);
+ ar9003_hw_prog_ini(ah, &ah->iniMac[ATH_INI_POST], modesIndex);
+ ar9003_hw_prog_ini(ah, &ah->iniBB[ATH_INI_POST], modesIndex);
+ ar9003_hw_prog_ini(ah, &ah->iniRadio[ATH_INI_POST], modesIndex);
+ if (AR_SREV_9462_20(ah))
+ ar9003_hw_prog_ini(ah,
+ &ah->ini_radio_post_sys2ant,
+ modesIndex);
+
+ REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
+
+ /*
+ * For 5GHz channels requiring Fast Clock, apply
+ * different modal values.
+ */
+ if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+ REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex, regWrites);
+
+ if (AR_SREV_9330(ah))
+ REG_WRITE_ARRAY(&ah->iniModesAdditional, 1, regWrites);
+
+ if (AR_SREV_9340(ah) && !ah->is_clk_25mhz)
+ REG_WRITE_ARRAY(&ah->iniModesAdditional_40M, 1, regWrites);
+
+ ah->modes_index = modesIndex;
+ *ini_reloaded = true;
+
+set_rfmode:
+ ar9003_hw_set_rfmode(ah, chan);
+ return 0;
+}
+
void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
{
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
@@ -1250,11 +1353,11 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
priv_ops->set_delta_slope = ar9003_hw_set_delta_slope;
priv_ops->rfbus_req = ar9003_hw_rfbus_req;
priv_ops->rfbus_done = ar9003_hw_rfbus_done;
- priv_ops->set_diversity = ar9003_hw_set_diversity;
priv_ops->ani_control = ar9003_hw_ani_control;
priv_ops->do_getnf = ar9003_hw_do_getnf;
priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs;
priv_ops->set_radar_params = ar9003_hw_set_radar_params;
+ priv_ops->fast_chan_change = ar9003_hw_fast_chan_change;
ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
index 443090d278e3..4114fe752c6b 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
@@ -325,13 +325,15 @@
#define AR_PHY_RX_OCGAIN (AR_AGC_BASE + 0x200)
-#define AR_PHY_CCA_NOM_VAL_9300_2GHZ -110
-#define AR_PHY_CCA_NOM_VAL_9300_5GHZ -115
-#define AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ -125
-#define AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ -125
+#define AR_PHY_CCA_NOM_VAL_9300_2GHZ (AR_SREV_9462(ah) ? -127 : -110)
+#define AR_PHY_CCA_NOM_VAL_9300_5GHZ (AR_SREV_9462(ah) ? -127 : -115)
+#define AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ (AR_SREV_9462(ah) ? -127 : -125)
+#define AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ (AR_SREV_9462(ah) ? -127 : -125)
#define AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ -95
#define AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ -100
+#define AR_PHY_CCA_NOM_VAL_9330_2GHZ -118
+
/*
* AGC Field Definitions
*/
@@ -570,15 +572,18 @@
#define AR_PHY_TXGAIN_TABLE (AR_SM_BASE + 0x300)
-#define AR_PHY_TX_IQCAL_CONTROL_1 (AR_SM_BASE + AR_SREV_9485(ah) ? \
- 0x3c8 : 0x448)
-#define AR_PHY_TX_IQCAL_START (AR_SM_BASE + AR_SREV_9485(ah) ? \
- 0x3c4 : 0x440)
-#define AR_PHY_TX_IQCAL_STATUS_B0 (AR_SM_BASE + AR_SREV_9485(ah) ? \
- 0x3f0 : 0x48c)
+#define AR_PHY_TX_IQCAL_CONTROL_0 (AR_SM_BASE + (AR_SREV_9485(ah) ? \
+ 0x3c4 : 0x444))
+#define AR_PHY_TX_IQCAL_CONTROL_1 (AR_SM_BASE + (AR_SREV_9485(ah) ? \
+ 0x3c8 : 0x448))
+#define AR_PHY_TX_IQCAL_START (AR_SM_BASE + (AR_SREV_9485(ah) ? \
+ 0x3c4 : 0x440))
+#define AR_PHY_TX_IQCAL_STATUS_B0 (AR_SM_BASE + (AR_SREV_9485(ah) ? \
+ 0x3f0 : 0x48c))
#define AR_PHY_TX_IQCAL_CORR_COEFF_B0(_i) (AR_SM_BASE + \
(AR_SREV_9485(ah) ? \
0x3d0 : 0x450) + ((_i) << 2))
+#define AR_PHY_RTT_CTRL (AR_SM_BASE + 0x380)
#define AR_PHY_WATCHDOG_STATUS (AR_SM_BASE + 0x5c0)
#define AR_PHY_WATCHDOG_CTL_1 (AR_SM_BASE + 0x5c4)
@@ -598,6 +603,17 @@
#define AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE 0x0000ff00
#define AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE_S 8
+/* AIC Registers */
+#define AR_PHY_AIC_CTRL_0_B0 (AR_SM_BASE + 0x4b0)
+#define AR_PHY_AIC_CTRL_1_B0 (AR_SM_BASE + 0x4b4)
+#define AR_PHY_AIC_CTRL_2_B0 (AR_SM_BASE + 0x4b8)
+#define AR_PHY_AIC_CTRL_3_B0 (AR_SM_BASE + 0x4bc)
+#define AR_PHY_AIC_STAT_0_B0 (AR_SM_BASE + (AR_SREV_9462_10(ah) ? \
+ 0x4c0 : 0x4c4))
+#define AR_PHY_AIC_STAT_1_B0 (AR_SM_BASE + (AR_SREV_9462_10(ah) ? \
+ 0x4c4 : 0x4c8))
+#define AR_PHY_AIC_CTRL_4_B0 (AR_SM_BASE + 0x4c0)
+#define AR_PHY_AIC_STAT_2_B0 (AR_SM_BASE + 0x4cc)
#define AR_PHY_65NM_CH0_SYNTH4 0x1608c
#define AR_PHY_SYNTH4_LONG_SHIFT_SELECT 0x00000002
@@ -607,7 +623,35 @@
#define AR_PHY_65NM_CH0_BIAS2 0x160c4
#define AR_PHY_65NM_CH0_BIAS4 0x160cc
#define AR_PHY_65NM_CH0_RXTX4 0x1610c
-#define AR_PHY_65NM_CH0_THERM (AR_SREV_9300(ah) ? 0x16290 : 0x1628c)
+
+#define AR_CH0_TOP (AR_SREV_9300(ah) ? 0x16288 : \
+ ((AR_SREV_9462(ah) ? 0x1628c : 0x16280)))
+#define AR_CH0_TOP_XPABIASLVL (0x300)
+#define AR_CH0_TOP_XPABIASLVL_S (8)
+
+#define AR_CH0_THERM (AR_SREV_9300(ah) ? 0x16290 : \
+ ((AR_SREV_9485(ah) ? 0x1628c : 0x16294)))
+#define AR_CH0_THERM_XPABIASLVL_MSB 0x3
+#define AR_CH0_THERM_XPABIASLVL_MSB_S 0
+#define AR_CH0_THERM_XPASHORT2GND 0x4
+#define AR_CH0_THERM_XPASHORT2GND_S 2
+
+#define AR_SWITCH_TABLE_COM_ALL (0xffff)
+#define AR_SWITCH_TABLE_COM_ALL_S (0)
+#define AR_SWITCH_TABLE_COM_AR9462_ALL (0xffffff)
+#define AR_SWITCH_TABLE_COM_AR9462_ALL_S (0)
+#define AR_SWITCH_TABLE_COM_SPDT (0x00f00000)
+#define AR_SWITCH_TABLE_COM_SPDT_ALL (0x0000fff0)
+#define AR_SWITCH_TABLE_COM_SPDT_ALL_S (4)
+
+#define AR_SWITCH_TABLE_COM2_ALL (0xffffff)
+#define AR_SWITCH_TABLE_COM2_ALL_S (0)
+
+#define AR_SWITCH_TABLE_ALL (0xfff)
+#define AR_SWITCH_TABLE_ALL_S (0)
+
+#define AR_PHY_65NM_CH0_THERM (AR_SREV_9300(ah) ? 0x16290 :\
+ (AR_SREV_9462(ah) ? 0x16294 : 0x1628c))
#define AR_PHY_65NM_CH0_THERM_LOCAL 0x80000000
#define AR_PHY_65NM_CH0_THERM_LOCAL_S 31
@@ -623,21 +667,23 @@
#define AR_PHY_65NM_CH2_RXTX1 0x16900
#define AR_PHY_65NM_CH2_RXTX2 0x16904
-#define AR_CH0_TOP2 (AR_SREV_9485(ah) ? 0x00016284 : 0x0001628c)
+#define AR_CH0_TOP2 (AR_SREV_9300(ah) ? 0x1628c : \
+ (AR_SREV_9462(ah) ? 0x16290 : 0x16284))
#define AR_CH0_TOP2_XPABIASLVL 0xf000
#define AR_CH0_TOP2_XPABIASLVL_S 12
-#define AR_CH0_XTAL (AR_SREV_9485(ah) ? 0x16290 : 0x16294)
+#define AR_CH0_XTAL (AR_SREV_9300(ah) ? 0x16294 : \
+ (AR_SREV_9462(ah) ? 0x16298 : 0x16290))
#define AR_CH0_XTAL_CAPINDAC 0x7f000000
#define AR_CH0_XTAL_CAPINDAC_S 24
#define AR_CH0_XTAL_CAPOUTDAC 0x00fe0000
#define AR_CH0_XTAL_CAPOUTDAC_S 17
-#define AR_PHY_PMU1 0x16c40
+#define AR_PHY_PMU1 (AR_SREV_9462(ah) ? 0x16340 : 0x16c40)
#define AR_PHY_PMU1_PWD 0x1
#define AR_PHY_PMU1_PWD_S 0
-#define AR_PHY_PMU2 0x16c44
+#define AR_PHY_PMU2 (AR_SREV_9462(ah) ? 0x16344 : 0x16c44)
#define AR_PHY_PMU2_PGM 0x00200000
#define AR_PHY_PMU2_PGM_S 21
@@ -777,6 +823,22 @@
#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT 0x01000000
#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 24
#define AR_PHY_CHANNEL_STATUS_RX_CLEAR 0x00000004
+#define AR_PHY_RTT_CTRL_ENA_RADIO_RETENTION 0x00000001
+#define AR_PHY_RTT_CTRL_ENA_RADIO_RETENTION_S 0
+#define AR_PHY_RTT_CTRL_RESTORE_MASK 0x0000007E
+#define AR_PHY_RTT_CTRL_RESTORE_MASK_S 1
+#define AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE 0x00000080
+#define AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE_S 7
+#define AR_PHY_RTT_SW_RTT_TABLE_ACCESS 0x00000001
+#define AR_PHY_RTT_SW_RTT_TABLE_ACCESS_S 0
+#define AR_PHY_RTT_SW_RTT_TABLE_WRITE 0x00000002
+#define AR_PHY_RTT_SW_RTT_TABLE_WRITE_S 1
+#define AR_PHY_RTT_SW_RTT_TABLE_ADDR 0x0000001C
+#define AR_PHY_RTT_SW_RTT_TABLE_ADDR_S 2
+#define AR_PHY_RTT_SW_RTT_TABLE_DATA 0xFFFFFFF0
+#define AR_PHY_RTT_SW_RTT_TABLE_DATA_S 4
+#define AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL 0x80000000
+#define AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL_S 31
#define AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT 0x01fc0000
#define AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT_S 18
#define AR_PHY_TX_IQCAL_START_DO_CAL 0x00000001
@@ -837,19 +899,42 @@
*/
#define AR_SM1_BASE 0xb200
-#define AR_PHY_SWITCH_CHAIN_1 (AR_SM1_BASE + 0x84)
-#define AR_PHY_FCAL_2_1 (AR_SM1_BASE + 0xd0)
-#define AR_PHY_DFT_TONE_CTL_1 (AR_SM1_BASE + 0xd4)
-#define AR_PHY_CL_TAB_1 (AR_SM1_BASE + 0x100)
-#define AR_PHY_CHAN_INFO_GAIN_1 (AR_SM1_BASE + 0x180)
-#define AR_PHY_TPC_4_B1 (AR_SM1_BASE + 0x204)
-#define AR_PHY_TPC_5_B1 (AR_SM1_BASE + 0x208)
-#define AR_PHY_TPC_6_B1 (AR_SM1_BASE + 0x20c)
-#define AR_PHY_TPC_11_B1 (AR_SM1_BASE + 0x220)
-#define AR_PHY_PDADC_TAB_1 (AR_SM1_BASE + 0x240)
+#define AR_PHY_SWITCH_CHAIN_1 (AR_SM1_BASE + 0x84)
+#define AR_PHY_FCAL_2_1 (AR_SM1_BASE + 0xd0)
+#define AR_PHY_DFT_TONE_CTL_1 (AR_SM1_BASE + 0xd4)
+#define AR_PHY_CL_TAB_1 (AR_SM1_BASE + 0x100)
+#define AR_PHY_CHAN_INFO_GAIN_1 (AR_SM1_BASE + 0x180)
+#define AR_PHY_TPC_4_B1 (AR_SM1_BASE + 0x204)
+#define AR_PHY_TPC_5_B1 (AR_SM1_BASE + 0x208)
+#define AR_PHY_TPC_6_B1 (AR_SM1_BASE + 0x20c)
+#define AR_PHY_TPC_11_B1 (AR_SM1_BASE + 0x220)
+#define AR_PHY_PDADC_TAB_1 (AR_SM1_BASE + (AR_SREV_AR9462(ah) ? \
+ 0x280 : 0x240))
+#define AR_PHY_TPC_19_B1 (AR_SM1_BASE + 0x240)
+#define AR_PHY_TPC_19_B1_ALPHA_THERM 0xff
+#define AR_PHY_TPC_19_B1_ALPHA_THERM_S 0
#define AR_PHY_TX_IQCAL_STATUS_B1 (AR_SM1_BASE + 0x48c)
-#define AR_PHY_TX_IQCAL_CORR_COEFF_B1(_i) (AR_SM_BASE + 0x450 + ((_i) << 2))
-
+#define AR_PHY_TX_IQCAL_CORR_COEFF_B1(_i) (AR_SM1_BASE + 0x450 + ((_i) << 2))
+
+/* SM 1 AIC Registers */
+
+#define AR_PHY_AIC_CTRL_0_B1 (AR_SM1_BASE + 0x4b0)
+#define AR_PHY_AIC_CTRL_1_B1 (AR_SM1_BASE + 0x4b4)
+#define AR_PHY_AIC_CTRL_2_B1 (AR_SM1_BASE + 0x4b8)
+#define AR_PHY_AIC_STAT_0_B1 (AR_SM1_BASE + (AR_SREV_9462_10(ah) ? \
+ 0x4c0 : 0x4c4))
+#define AR_PHY_AIC_STAT_1_B1 (AR_SM1_BASE + (AR_SREV_9462_10(ah) ? \
+ 0x4c4 : 0x4c8))
+#define AR_PHY_AIC_CTRL_4_B1 (AR_SM1_BASE + 0x4c0)
+#define AR_PHY_AIC_STAT_2_B1 (AR_SM1_BASE + 0x4cc)
+
+#define AR_PHY_AIC_SRAM_ADDR_B1 (AR_SM1_BASE + 0x5f0)
+#define AR_PHY_AIC_SRAM_DATA_B1 (AR_SM1_BASE + 0x5f4)
+
+#define AR_PHY_RTT_TABLE_SW_INTF_B(i) (0x384 + ((i) ? \
+ AR_SM1_BASE : AR_SM_BASE))
+#define AR_PHY_RTT_TABLE_SW_INTF_1_B(i) (0x388 + ((i) ? \
+ AR_SM1_BASE : AR_SM_BASE))
/*
* Channel 2 Register Map
*/
@@ -912,6 +997,13 @@
#define AR_PHY_RSSI_3 (AR_AGC3_BASE + 0x180)
+/* GLB Registers */
+#define AR_GLB_BASE 0x20000
+#define AR_PHY_GLB_CONTROL (AR_GLB_BASE + 0x44)
+#define AR_GLB_SCRATCH(_ah) (AR_GLB_BASE + \
+ (AR_SREV_9462_20(_ah) ? 0x4c : 0x50))
+#define AR_GLB_STATUS (AR_GLB_BASE + 0x48)
+
/*
* Misc helper defines
*/
@@ -1119,6 +1211,7 @@
#define AR_PHY_POWERTX_RATE8_POWERTXHT40_5 0x3F00
#define AR_PHY_POWERTX_RATE8_POWERTXHT40_5_S 8
-void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
+#define AR_PHY_CL_TAB_CL_GAIN_MOD 0x1f
+#define AR_PHY_CL_TAB_CL_GAIN_MOD_S 0
#endif /* AR9003_PHY_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c
new file mode 100644
index 000000000000..48803ee9c0d6
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "hw.h"
+#include "ar9003_phy.h"
+
+#define RTT_RESTORE_TIMEOUT 1000
+#define RTT_ACCESS_TIMEOUT 100
+#define RTT_BAD_VALUE 0x0bad0bad
+
+/*
+ * RTT (Radio Retention Table) hardware implementation information
+ *
+ * There is an internal table (i.e. the rtt) for each chain (or bank).
+ * Each table contains 6 entries and each entry is corresponding to
+ * a specific calibration parameter as depicted below.
+ * 0~2 - DC offset DAC calibration: loop, low, high (offsetI/Q_...)
+ * 3 - Filter cal (filterfc)
+ * 4 - RX gain settings
+ * 5 - Peak detector offset calibration (agc_caldac)
+ */
+
+void ar9003_hw_rtt_enable(struct ath_hw *ah)
+{
+ REG_WRITE(ah, AR_PHY_RTT_CTRL, 1);
+}
+
+void ar9003_hw_rtt_disable(struct ath_hw *ah)
+{
+ REG_WRITE(ah, AR_PHY_RTT_CTRL, 0);
+}
+
+void ar9003_hw_rtt_set_mask(struct ath_hw *ah, u32 rtt_mask)
+{
+ REG_RMW_FIELD(ah, AR_PHY_RTT_CTRL,
+ AR_PHY_RTT_CTRL_RESTORE_MASK, rtt_mask);
+}
+
+bool ar9003_hw_rtt_force_restore(struct ath_hw *ah)
+{
+ if (!ath9k_hw_wait(ah, AR_PHY_RTT_CTRL,
+ AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE,
+ 0, RTT_RESTORE_TIMEOUT))
+ return false;
+
+ REG_RMW_FIELD(ah, AR_PHY_RTT_CTRL,
+ AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE, 1);
+
+ if (!ath9k_hw_wait(ah, AR_PHY_RTT_CTRL,
+ AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE,
+ 0, RTT_RESTORE_TIMEOUT))
+ return false;
+
+ return true;
+}
+
+static void ar9003_hw_rtt_load_hist_entry(struct ath_hw *ah, u8 chain,
+ u32 index, u32 data28)
+{
+ u32 val;
+
+ val = SM(data28, AR_PHY_RTT_SW_RTT_TABLE_DATA);
+ REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_1_B(chain), val);
+
+ val = SM(0, AR_PHY_RTT_SW_RTT_TABLE_ACCESS) |
+ SM(1, AR_PHY_RTT_SW_RTT_TABLE_WRITE) |
+ SM(index, AR_PHY_RTT_SW_RTT_TABLE_ADDR);
+ REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val);
+ udelay(1);
+
+ val |= SM(1, AR_PHY_RTT_SW_RTT_TABLE_ACCESS);
+ REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val);
+ udelay(1);
+
+ if (!ath9k_hw_wait(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain),
+ AR_PHY_RTT_SW_RTT_TABLE_ACCESS, 0,
+ RTT_ACCESS_TIMEOUT))
+ return;
+
+ val &= ~SM(1, AR_PHY_RTT_SW_RTT_TABLE_WRITE);
+ REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val);
+ udelay(1);
+
+ ath9k_hw_wait(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain),
+ AR_PHY_RTT_SW_RTT_TABLE_ACCESS, 0,
+ RTT_ACCESS_TIMEOUT);
+}
+
+void ar9003_hw_rtt_load_hist(struct ath_hw *ah, u8 chain, u32 *table)
+{
+ int i;
+
+ for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++)
+ ar9003_hw_rtt_load_hist_entry(ah, chain, i, table[i]);
+}
+
+static int ar9003_hw_rtt_fill_hist_entry(struct ath_hw *ah, u8 chain, u32 index)
+{
+ u32 val;
+
+ val = SM(0, AR_PHY_RTT_SW_RTT_TABLE_ACCESS) |
+ SM(0, AR_PHY_RTT_SW_RTT_TABLE_WRITE) |
+ SM(index, AR_PHY_RTT_SW_RTT_TABLE_ADDR);
+
+ REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val);
+ udelay(1);
+
+ val |= SM(1, AR_PHY_RTT_SW_RTT_TABLE_ACCESS);
+ REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val);
+ udelay(1);
+
+ if (!ath9k_hw_wait(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain),
+ AR_PHY_RTT_SW_RTT_TABLE_ACCESS, 0,
+ RTT_ACCESS_TIMEOUT))
+ return RTT_BAD_VALUE;
+
+ val = REG_READ(ah, AR_PHY_RTT_TABLE_SW_INTF_1_B(chain));
+
+ return val;
+}
+
+void ar9003_hw_rtt_fill_hist(struct ath_hw *ah, u8 chain, u32 *table)
+{
+ int i;
+
+ for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++)
+ table[i] = ar9003_hw_rtt_fill_hist_entry(ah, chain, i);
+}
+
+void ar9003_hw_rtt_clear_hist(struct ath_hw *ah)
+{
+ int i, j;
+
+ for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+ if (!(ah->rxchainmask & (1 << i)))
+ continue;
+ for (j = 0; j < MAX_RTT_TABLE_ENTRY; j++)
+ ar9003_hw_rtt_load_hist_entry(ah, i, j, 0);
+ }
+}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.h b/drivers/net/wireless/ath/ath9k/ar9003_rtt.h
new file mode 100644
index 000000000000..030758d087d6
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef AR9003_RTT_H
+#define AR9003_RTT_H
+
+void ar9003_hw_rtt_enable(struct ath_hw *ah);
+void ar9003_hw_rtt_disable(struct ath_hw *ah);
+void ar9003_hw_rtt_set_mask(struct ath_hw *ah, u32 rtt_mask);
+bool ar9003_hw_rtt_force_restore(struct ath_hw *ah);
+void ar9003_hw_rtt_load_hist(struct ath_hw *ah, u8 chain, u32 *table);
+void ar9003_hw_rtt_fill_hist(struct ath_hw *ah, u8 chain, u32 *table);
+void ar9003_hw_rtt_clear_hist(struct ath_hw *ah);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h b/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h
new file mode 100644
index 000000000000..f11d9b2677fd
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h
@@ -0,0 +1,1147 @@
+/*
+ * Copyright (c) 2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INITVALS_9330_1P1_H
+#define INITVALS_9330_1P1_H
+
+static const u32 ar9331_1p1_baseband_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005},
+ {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e},
+ {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+ {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+ {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+ {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c},
+ {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044},
+ {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a4, 0x037216a4},
+ {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020},
+ {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
+ {0x00009e10, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e},
+ {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e},
+ {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
+ {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+ {0x00009e2c, 0x0000001c, 0x0000001c, 0x00003221, 0x00003221},
+ {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222},
+ {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324},
+ {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010},
+ {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
+ {0x0000a204, 0x00003fc0, 0x00003fc4, 0x00003fc4, 0x00003fc0},
+ {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
+ {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+ {0x0000a234, 0x00000fff, 0x00000fff, 0x10000fff, 0x00000fff},
+ {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018},
+ {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
+ {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
+ {0x0000a260, 0x3a021501, 0x3a021501, 0x3a021501, 0x3a021501},
+ {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
+ {0x0000a284, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+ {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071981},
+ {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a},
+ {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000ae04, 0x00802020, 0x00802020, 0x00802020, 0x00802020},
+ {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+};
+
+static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p1[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
+ {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52},
+ {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84},
+ {0x0000a2e4, 0xfffff000, 0xfffff000, 0xfffff000, 0xfffff000},
+ {0x0000a2e8, 0xfffe0000, 0xfffe0000, 0xfffe0000, 0xfffe0000},
+ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d0, 0x000050d0},
+ {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200},
+ {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202},
+ {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400},
+ {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402},
+ {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404},
+ {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00},
+ {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02},
+ {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04},
+ {0x0000a52c, 0x41023e85, 0x41023e85, 0x2d000a20, 0x2d000a20},
+ {0x0000a530, 0x48023ec6, 0x48023ec6, 0x31000a22, 0x31000a22},
+ {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000a24, 0x35000a24},
+ {0x0000a538, 0x53023f4b, 0x53023f4b, 0x38000a43, 0x38000a43},
+ {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x3b000e42, 0x3b000e42},
+ {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x3f000e44, 0x3f000e44},
+ {0x0000a544, 0x6502feca, 0x6502feca, 0x42000e64, 0x42000e64},
+ {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x46000e66, 0x46000e66},
+ {0x0000a54c, 0x7203feca, 0x7203feca, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a560, 0x900fff0b, 0x900fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a564, 0x960fffcb, 0x960fffcb, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200},
+ {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202},
+ {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400},
+ {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402},
+ {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404},
+ {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603},
+ {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02},
+ {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04},
+ {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20},
+ {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20},
+ {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22},
+ {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24},
+ {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640},
+ {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660},
+ {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861},
+ {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81},
+ {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83},
+ {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84},
+ {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3},
+ {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9},
+ {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb},
+ {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec},
+ {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802},
+ {0x0000a620, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802},
+ {0x0000a624, 0x03010a03, 0x03010a03, 0x03010a03, 0x03010a03},
+ {0x0000a628, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a62c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a630, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a634, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a638, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a63c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x00016044, 0x034922db, 0x034922db, 0x034922db, 0x034922db},
+ {0x00016284, 0x14d3f000, 0x14d3f000, 0x14d3f000, 0x14d3f000},
+};
+
+static const u32 ar9331_modes_high_ob_db_tx_gain_1p1[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
+ {0x0000a2dc, 0xffaa9a52, 0xffaa9a52, 0xffaa9a52, 0xffaa9a52},
+ {0x0000a2e0, 0xffb31c84, 0xffb31c84, 0xffb31c84, 0xffb31c84},
+ {0x0000a2e4, 0xff43e000, 0xff43e000, 0xff43e000, 0xff43e000},
+ {0x0000a2e8, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000},
+ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7},
+ {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200},
+ {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202},
+ {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400},
+ {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402},
+ {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404},
+ {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00},
+ {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02},
+ {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04},
+ {0x0000a52c, 0x41023e85, 0x41023e85, 0x3d001620, 0x3d001620},
+ {0x0000a530, 0x48023ec6, 0x48023ec6, 0x3f001621, 0x3f001621},
+ {0x0000a534, 0x4d023f01, 0x4d023f01, 0x42001640, 0x42001640},
+ {0x0000a538, 0x53023f4b, 0x53023f4b, 0x44001641, 0x44001641},
+ {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x46001642, 0x46001642},
+ {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49001644, 0x49001644},
+ {0x0000a544, 0x6502feca, 0x6502feca, 0x4c001a81, 0x4c001a81},
+ {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4f001a83, 0x4f001a83},
+ {0x0000a54c, 0x7203feca, 0x7203feca, 0x52001c84, 0x52001c84},
+ {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001ce3, 0x55001ce3},
+ {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x59001ce5, 0x59001ce5},
+ {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5d001ce9, 0x5d001ce9},
+ {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x64001eec, 0x64001eec},
+ {0x0000a560, 0x900fff0b, 0x900fff0b, 0x64001eec, 0x64001eec},
+ {0x0000a564, 0x960fffcb, 0x960fffcb, 0x64001eec, 0x64001eec},
+ {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec},
+ {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec},
+ {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec},
+ {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec},
+ {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec},
+ {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec},
+ {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200},
+ {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202},
+ {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400},
+ {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402},
+ {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404},
+ {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603},
+ {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02},
+ {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04},
+ {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20},
+ {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20},
+ {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22},
+ {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24},
+ {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640},
+ {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660},
+ {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861},
+ {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81},
+ {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83},
+ {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84},
+ {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3},
+ {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9},
+ {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb},
+ {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec},
+ {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x02008501, 0x02008501, 0x02008501, 0x02008501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802},
+ {0x0000a620, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802},
+ {0x0000a624, 0x0280ca03, 0x0280ca03, 0x0280ca03, 0x0280ca03},
+ {0x0000a628, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a62c, 0x04015005, 0x04015005, 0x04015005, 0x04015005},
+ {0x0000a630, 0x04015005, 0x04015005, 0x04015005, 0x04015005},
+ {0x0000a634, 0x04015005, 0x04015005, 0x04015005, 0x04015005},
+ {0x0000a638, 0x04015005, 0x04015005, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x04015005, 0x04015005, 0x04015005, 0x04015005},
+};
+
+static const u32 ar9331_modes_low_ob_db_tx_gain_1p1[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
+ {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52},
+ {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84},
+ {0x0000a2e4, 0xfffff000, 0xfffff000, 0xfffff000, 0xfffff000},
+ {0x0000a2e8, 0xfffe0000, 0xfffe0000, 0xfffe0000, 0xfffe0000},
+ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d0, 0x000050d0},
+ {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200},
+ {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202},
+ {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400},
+ {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402},
+ {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404},
+ {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00},
+ {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02},
+ {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04},
+ {0x0000a52c, 0x41023e85, 0x41023e85, 0x2d000a20, 0x2d000a20},
+ {0x0000a530, 0x48023ec6, 0x48023ec6, 0x31000a22, 0x31000a22},
+ {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000a24, 0x35000a24},
+ {0x0000a538, 0x53023f4b, 0x53023f4b, 0x38000a43, 0x38000a43},
+ {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x3b000e42, 0x3b000e42},
+ {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x3f000e44, 0x3f000e44},
+ {0x0000a544, 0x6502feca, 0x6502feca, 0x42000e64, 0x42000e64},
+ {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x46000e66, 0x46000e66},
+ {0x0000a54c, 0x7203feca, 0x7203feca, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a560, 0x900fff0b, 0x900fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a564, 0x960fffcb, 0x960fffcb, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200},
+ {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202},
+ {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400},
+ {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402},
+ {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404},
+ {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603},
+ {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02},
+ {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04},
+ {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20},
+ {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20},
+ {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22},
+ {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24},
+ {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640},
+ {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660},
+ {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861},
+ {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81},
+ {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83},
+ {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84},
+ {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3},
+ {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9},
+ {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb},
+ {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec},
+ {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802},
+ {0x0000a620, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802},
+ {0x0000a624, 0x03010a03, 0x03010a03, 0x03010a03, 0x03010a03},
+ {0x0000a628, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a62c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a630, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a634, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a638, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a63c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x00016044, 0x034922db, 0x034922db, 0x034922db, 0x034922db},
+ {0x00016284, 0x14d3f000, 0x14d3f000, 0x14d3f000, 0x14d3f000},
+};
+
+static const u32 ar9331_1p1_baseband_core_txfir_coeff_japan_2484[][2] = {
+ /* Addr allmodes */
+ {0x0000a398, 0x00000000},
+ {0x0000a39c, 0x6f7f0301},
+ {0x0000a3a0, 0xca9228ee},
+};
+
+static const u32 ar9331_1p1_xtal_25M[][2] = {
+ /* Addr allmodes */
+ {0x00007038, 0x000002f8},
+ {0x00008244, 0x0010f3d7},
+ {0x0000824c, 0x0001e7ae},
+ {0x0001609c, 0x0f508f29},
+};
+
+static const u32 ar9331_1p1_radio_core[][2] = {
+ /* Addr allmodes */
+ {0x00016000, 0x36db6db6},
+ {0x00016004, 0x6db6db40},
+ {0x00016008, 0x73800000},
+ {0x0001600c, 0x00000000},
+ {0x00016040, 0x7f80fff8},
+ {0x00016044, 0x03db62db},
+ {0x00016048, 0x6c924268},
+ {0x0001604c, 0x000f0278},
+ {0x00016050, 0x4db6db8c},
+ {0x00016054, 0x6db60000},
+ {0x00016080, 0x00080000},
+ {0x00016084, 0x0e48048c},
+ {0x00016088, 0x14214514},
+ {0x0001608c, 0x119f081c},
+ {0x00016090, 0x24926490},
+ {0x00016098, 0xd411eb84},
+ {0x000160a0, 0xc2108ffe},
+ {0x000160a4, 0x812fc370},
+ {0x000160a8, 0x423c8000},
+ {0x000160ac, 0x24651800},
+ {0x000160b0, 0x03284f3e},
+ {0x000160b4, 0x92480040},
+ {0x000160c0, 0x006db6db},
+ {0x000160c4, 0x0186db60},
+ {0x000160c8, 0x6db6db6c},
+ {0x000160cc, 0x6de6c300},
+ {0x000160d0, 0x14500820},
+ {0x00016100, 0x04cb0001},
+ {0x00016104, 0xfff80015},
+ {0x00016108, 0x00080010},
+ {0x0001610c, 0x00170000},
+ {0x00016140, 0x10804000},
+ {0x00016144, 0x01884080},
+ {0x00016148, 0x000080c0},
+ {0x00016280, 0x01000015},
+ {0x00016284, 0x14d20000},
+ {0x00016288, 0x00318000},
+ {0x0001628c, 0x50000000},
+ {0x00016290, 0x4b96210f},
+ {0x00016380, 0x00000000},
+ {0x00016384, 0x00000000},
+ {0x00016388, 0x00800700},
+ {0x0001638c, 0x00800700},
+ {0x00016390, 0x00800700},
+ {0x00016394, 0x00000000},
+ {0x00016398, 0x00000000},
+ {0x0001639c, 0x00000000},
+ {0x000163a0, 0x00000001},
+ {0x000163a4, 0x00000001},
+ {0x000163a8, 0x00000000},
+ {0x000163ac, 0x00000000},
+ {0x000163b0, 0x00000000},
+ {0x000163b4, 0x00000000},
+ {0x000163b8, 0x00000000},
+ {0x000163bc, 0x00000000},
+ {0x000163c0, 0x000000a0},
+ {0x000163c4, 0x000c0000},
+ {0x000163c8, 0x14021402},
+ {0x000163cc, 0x00001402},
+ {0x000163d0, 0x00000000},
+ {0x000163d4, 0x00000000},
+};
+
+static const u32 ar9331_1p1_soc_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00007010, 0x00000022, 0x00000022, 0x00000022, 0x00000022},
+};
+
+static const u32 ar9331_common_wo_xlna_rx_gain_1p1[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00060005},
+ {0x0000a004, 0x00810080},
+ {0x0000a008, 0x00830082},
+ {0x0000a00c, 0x00850084},
+ {0x0000a010, 0x01820181},
+ {0x0000a014, 0x01840183},
+ {0x0000a018, 0x01880185},
+ {0x0000a01c, 0x018a0189},
+ {0x0000a020, 0x02850284},
+ {0x0000a024, 0x02890288},
+ {0x0000a028, 0x028b028a},
+ {0x0000a02c, 0x03850384},
+ {0x0000a030, 0x03890388},
+ {0x0000a034, 0x038b038a},
+ {0x0000a038, 0x038d038c},
+ {0x0000a03c, 0x03910390},
+ {0x0000a040, 0x03930392},
+ {0x0000a044, 0x03950394},
+ {0x0000a048, 0x00000396},
+ {0x0000a04c, 0x00000000},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x28282828},
+ {0x0000a084, 0x28282828},
+ {0x0000a088, 0x28282828},
+ {0x0000a08c, 0x28282828},
+ {0x0000a090, 0x28282828},
+ {0x0000a094, 0x24242428},
+ {0x0000a098, 0x171e1e1e},
+ {0x0000a09c, 0x02020b0b},
+ {0x0000a0a0, 0x02020202},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x22072208},
+ {0x0000a0c4, 0x22052206},
+ {0x0000a0c8, 0x22032204},
+ {0x0000a0cc, 0x22012202},
+ {0x0000a0d0, 0x221f2200},
+ {0x0000a0d4, 0x221d221e},
+ {0x0000a0d8, 0x33023303},
+ {0x0000a0dc, 0x33003301},
+ {0x0000a0e0, 0x331e331f},
+ {0x0000a0e4, 0x4402331d},
+ {0x0000a0e8, 0x44004401},
+ {0x0000a0ec, 0x441e441f},
+ {0x0000a0f0, 0x55025503},
+ {0x0000a0f4, 0x55005501},
+ {0x0000a0f8, 0x551e551f},
+ {0x0000a0fc, 0x6602551d},
+ {0x0000a100, 0x66006601},
+ {0x0000a104, 0x661e661f},
+ {0x0000a108, 0x7703661d},
+ {0x0000a10c, 0x77017702},
+ {0x0000a110, 0x00007700},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x111f1100},
+ {0x0000a148, 0x111d111e},
+ {0x0000a14c, 0x111b111c},
+ {0x0000a150, 0x22032204},
+ {0x0000a154, 0x22012202},
+ {0x0000a158, 0x221f2200},
+ {0x0000a15c, 0x221d221e},
+ {0x0000a160, 0x33013302},
+ {0x0000a164, 0x331f3300},
+ {0x0000a168, 0x4402331e},
+ {0x0000a16c, 0x44004401},
+ {0x0000a170, 0x441e441f},
+ {0x0000a174, 0x55015502},
+ {0x0000a178, 0x551f5500},
+ {0x0000a17c, 0x6602551e},
+ {0x0000a180, 0x66006601},
+ {0x0000a184, 0x661e661f},
+ {0x0000a188, 0x7703661d},
+ {0x0000a18c, 0x77017702},
+ {0x0000a190, 0x00007700},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000296},
+};
+
+static const u32 ar9331_1p1_baseband_core[][2] = {
+ /* Addr allmodes */
+ {0x00009800, 0xafe68e30},
+ {0x00009804, 0xfd14e000},
+ {0x00009808, 0x9c0a8f6b},
+ {0x0000980c, 0x04800000},
+ {0x00009814, 0x9280c00a},
+ {0x00009818, 0x00000000},
+ {0x0000981c, 0x00020028},
+ {0x00009834, 0x5f3ca3de},
+ {0x00009838, 0x0108ecff},
+ {0x0000983c, 0x14750600},
+ {0x00009880, 0x201fff00},
+ {0x00009884, 0x00001042},
+ {0x000098a4, 0x00200400},
+ {0x000098b0, 0x32840bbe},
+ {0x000098d0, 0x004b6a8e},
+ {0x000098d4, 0x00000820},
+ {0x000098dc, 0x00000000},
+ {0x000098f0, 0x00000000},
+ {0x000098f4, 0x00000000},
+ {0x00009c04, 0x00000000},
+ {0x00009c08, 0x03200000},
+ {0x00009c0c, 0x00000000},
+ {0x00009c10, 0x00000000},
+ {0x00009c14, 0x00046384},
+ {0x00009c18, 0x05b6b440},
+ {0x00009c1c, 0x00b6b440},
+ {0x00009d00, 0xc080a333},
+ {0x00009d04, 0x40206c10},
+ {0x00009d08, 0x009c4060},
+ {0x00009d0c, 0x1883800a},
+ {0x00009d10, 0x01834061},
+ {0x00009d14, 0x00c00400},
+ {0x00009d18, 0x00000000},
+ {0x00009e08, 0x0038233c},
+ {0x00009e24, 0x9927b515},
+ {0x00009e28, 0x12ef0200},
+ {0x00009e30, 0x06336f77},
+ {0x00009e34, 0x6af6532f},
+ {0x00009e38, 0x0cc80c00},
+ {0x00009e40, 0x0d261820},
+ {0x00009e4c, 0x00001004},
+ {0x00009e50, 0x00ff03f1},
+ {0x00009fc0, 0x803e4788},
+ {0x00009fc4, 0x0001efb5},
+ {0x00009fcc, 0x40000014},
+ {0x0000a20c, 0x00000000},
+ {0x0000a220, 0x00000000},
+ {0x0000a224, 0x00000000},
+ {0x0000a228, 0x10002310},
+ {0x0000a23c, 0x00000000},
+ {0x0000a244, 0x0c000000},
+ {0x0000a2a0, 0x00000001},
+ {0x0000a2c0, 0x00000001},
+ {0x0000a2c8, 0x00000000},
+ {0x0000a2cc, 0x18c43433},
+ {0x0000a2d4, 0x00000000},
+ {0x0000a2dc, 0x00000000},
+ {0x0000a2e0, 0x00000000},
+ {0x0000a2e4, 0x00000000},
+ {0x0000a2e8, 0x00000000},
+ {0x0000a2ec, 0x00000000},
+ {0x0000a2f0, 0x00000000},
+ {0x0000a2f4, 0x00000000},
+ {0x0000a2f8, 0x00000000},
+ {0x0000a344, 0x00000000},
+ {0x0000a34c, 0x00000000},
+ {0x0000a350, 0x0000a000},
+ {0x0000a364, 0x00000000},
+ {0x0000a370, 0x00000000},
+ {0x0000a390, 0x00000001},
+ {0x0000a394, 0x00000444},
+ {0x0000a398, 0x001f0e0f},
+ {0x0000a39c, 0x0075393f},
+ {0x0000a3a0, 0xb79f6427},
+ {0x0000a3a4, 0x00000000},
+ {0x0000a3a8, 0xaaaaaaaa},
+ {0x0000a3ac, 0x3c466478},
+ {0x0000a3c0, 0x20202020},
+ {0x0000a3c4, 0x22222220},
+ {0x0000a3c8, 0x20200020},
+ {0x0000a3cc, 0x20202020},
+ {0x0000a3d0, 0x20202020},
+ {0x0000a3d4, 0x20202020},
+ {0x0000a3d8, 0x20202020},
+ {0x0000a3dc, 0x20202020},
+ {0x0000a3e0, 0x20202020},
+ {0x0000a3e4, 0x20202020},
+ {0x0000a3e8, 0x20202020},
+ {0x0000a3ec, 0x20202020},
+ {0x0000a3f0, 0x00000000},
+ {0x0000a3f4, 0x00000006},
+ {0x0000a3f8, 0x0cdbd380},
+ {0x0000a3fc, 0x000f0f01},
+ {0x0000a400, 0x8fa91f01},
+ {0x0000a404, 0x00000000},
+ {0x0000a408, 0x0e79e5c6},
+ {0x0000a40c, 0x00820820},
+ {0x0000a414, 0x1ce739ce},
+ {0x0000a418, 0x2d001dce},
+ {0x0000a41c, 0x1ce739ce},
+ {0x0000a420, 0x000001ce},
+ {0x0000a424, 0x1ce739ce},
+ {0x0000a428, 0x000001ce},
+ {0x0000a42c, 0x1ce739ce},
+ {0x0000a430, 0x1ce739ce},
+ {0x0000a434, 0x00000000},
+ {0x0000a438, 0x00001801},
+ {0x0000a43c, 0x00000000},
+ {0x0000a440, 0x00000000},
+ {0x0000a444, 0x00000000},
+ {0x0000a448, 0x04000000},
+ {0x0000a44c, 0x00000001},
+ {0x0000a450, 0x00010000},
+ {0x0000a458, 0x00000000},
+ {0x0000a640, 0x00000000},
+ {0x0000a644, 0x3fad9d74},
+ {0x0000a648, 0x0048060a},
+ {0x0000a64c, 0x00003c37},
+ {0x0000a670, 0x03020100},
+ {0x0000a674, 0x09080504},
+ {0x0000a678, 0x0d0c0b0a},
+ {0x0000a67c, 0x13121110},
+ {0x0000a680, 0x31301514},
+ {0x0000a684, 0x35343332},
+ {0x0000a688, 0x00000036},
+ {0x0000a690, 0x00000838},
+ {0x0000a7c0, 0x00000000},
+ {0x0000a7c4, 0xfffffffc},
+ {0x0000a7c8, 0x00000000},
+ {0x0000a7cc, 0x00000000},
+ {0x0000a7d0, 0x00000000},
+ {0x0000a7d4, 0x00000004},
+ {0x0000a7dc, 0x00000001},
+};
+
+static const u32 ar9331_modes_high_power_tx_gain_1p1[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
+ {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52},
+ {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84},
+ {0x0000a2e4, 0xfffff000, 0xfffff000, 0xfffff000, 0xfffff000},
+ {0x0000a2e8, 0xfffe0000, 0xfffe0000, 0xfffe0000, 0xfffe0000},
+ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d0, 0x000050d0},
+ {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200},
+ {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202},
+ {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400},
+ {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402},
+ {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404},
+ {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00},
+ {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02},
+ {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04},
+ {0x0000a52c, 0x41023e85, 0x41023e85, 0x2d000a20, 0x2d000a20},
+ {0x0000a530, 0x48023ec6, 0x48023ec6, 0x31000a22, 0x31000a22},
+ {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000a24, 0x35000a24},
+ {0x0000a538, 0x53023f4b, 0x53023f4b, 0x38000a43, 0x38000a43},
+ {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x3b000e42, 0x3b000e42},
+ {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x3f000e44, 0x3f000e44},
+ {0x0000a544, 0x6502feca, 0x6502feca, 0x42000e64, 0x42000e64},
+ {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x46000e66, 0x46000e66},
+ {0x0000a54c, 0x7203feca, 0x7203feca, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a560, 0x900fff0b, 0x900fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a564, 0x960fffcb, 0x960fffcb, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6},
+ {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200},
+ {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202},
+ {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400},
+ {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402},
+ {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404},
+ {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603},
+ {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02},
+ {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04},
+ {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20},
+ {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20},
+ {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22},
+ {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24},
+ {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640},
+ {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660},
+ {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861},
+ {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81},
+ {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83},
+ {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84},
+ {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3},
+ {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9},
+ {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb},
+ {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec},
+ {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802},
+ {0x0000a620, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802},
+ {0x0000a624, 0x03010a03, 0x03010a03, 0x03010a03, 0x03010a03},
+ {0x0000a628, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a62c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a630, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a634, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a638, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x0000a63c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04},
+ {0x00016044, 0x034922db, 0x034922db, 0x034922db, 0x034922db},
+ {0x00016284, 0x14d3f000, 0x14d3f000, 0x14d3f000, 0x14d3f000},
+};
+
+static const u32 ar9331_1p1_mac_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b},
+ {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+ {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+ {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
+};
+
+static const u32 ar9331_1p1_soc_preamble[][2] = {
+ /* Addr allmodes */
+ {0x00007020, 0x00000000},
+ {0x00007034, 0x00000002},
+ {0x00007038, 0x000002f8},
+};
+
+static const u32 ar9331_1p1_xtal_40M[][2] = {
+ /* Addr allmodes */
+ {0x00007038, 0x000004c2},
+ {0x00008244, 0x0010f400},
+ {0x0000824c, 0x0001e800},
+ {0x0001609c, 0x0b283f31},
+};
+
+static const u32 ar9331_1p1_mac_core[][2] = {
+ /* Addr allmodes */
+ {0x00000008, 0x00000000},
+ {0x00000030, 0x00020085},
+ {0x00000034, 0x00000005},
+ {0x00000040, 0x00000000},
+ {0x00000044, 0x00000000},
+ {0x00000048, 0x00000008},
+ {0x0000004c, 0x00000010},
+ {0x00000050, 0x00000000},
+ {0x00001040, 0x002ffc0f},
+ {0x00001044, 0x002ffc0f},
+ {0x00001048, 0x002ffc0f},
+ {0x0000104c, 0x002ffc0f},
+ {0x00001050, 0x002ffc0f},
+ {0x00001054, 0x002ffc0f},
+ {0x00001058, 0x002ffc0f},
+ {0x0000105c, 0x002ffc0f},
+ {0x00001060, 0x002ffc0f},
+ {0x00001064, 0x002ffc0f},
+ {0x000010f0, 0x00000100},
+ {0x00001270, 0x00000000},
+ {0x000012b0, 0x00000000},
+ {0x000012f0, 0x00000000},
+ {0x0000143c, 0x00000000},
+ {0x0000147c, 0x00000000},
+ {0x00008000, 0x00000000},
+ {0x00008004, 0x00000000},
+ {0x00008008, 0x00000000},
+ {0x0000800c, 0x00000000},
+ {0x00008018, 0x00000000},
+ {0x00008020, 0x00000000},
+ {0x00008038, 0x00000000},
+ {0x0000803c, 0x00000000},
+ {0x00008040, 0x00000000},
+ {0x00008044, 0x00000000},
+ {0x00008048, 0x00000000},
+ {0x0000804c, 0xffffffff},
+ {0x00008054, 0x00000000},
+ {0x00008058, 0x00000000},
+ {0x0000805c, 0x000fc78f},
+ {0x00008060, 0x0000000f},
+ {0x00008064, 0x00000000},
+ {0x00008070, 0x00000310},
+ {0x00008074, 0x00000020},
+ {0x00008078, 0x00000000},
+ {0x0000809c, 0x0000000f},
+ {0x000080a0, 0x00000000},
+ {0x000080a4, 0x02ff0000},
+ {0x000080a8, 0x0e070605},
+ {0x000080ac, 0x0000000d},
+ {0x000080b0, 0x00000000},
+ {0x000080b4, 0x00000000},
+ {0x000080b8, 0x00000000},
+ {0x000080bc, 0x00000000},
+ {0x000080c0, 0x2a800000},
+ {0x000080c4, 0x06900168},
+ {0x000080c8, 0x13881c20},
+ {0x000080cc, 0x01f40000},
+ {0x000080d0, 0x00252500},
+ {0x000080d4, 0x00a00000},
+ {0x000080d8, 0x00400000},
+ {0x000080dc, 0x00000000},
+ {0x000080e0, 0xffffffff},
+ {0x000080e4, 0x0000ffff},
+ {0x000080e8, 0x3f3f3f3f},
+ {0x000080ec, 0x00000000},
+ {0x000080f0, 0x00000000},
+ {0x000080f4, 0x00000000},
+ {0x000080fc, 0x00020000},
+ {0x00008100, 0x00000000},
+ {0x00008108, 0x00000052},
+ {0x0000810c, 0x00000000},
+ {0x00008110, 0x00000000},
+ {0x00008114, 0x000007ff},
+ {0x00008118, 0x000000aa},
+ {0x0000811c, 0x00003210},
+ {0x00008124, 0x00000000},
+ {0x00008128, 0x00000000},
+ {0x0000812c, 0x00000000},
+ {0x00008130, 0x00000000},
+ {0x00008134, 0x00000000},
+ {0x00008138, 0x00000000},
+ {0x0000813c, 0x0000ffff},
+ {0x00008144, 0xffffffff},
+ {0x00008168, 0x00000000},
+ {0x0000816c, 0x00000000},
+ {0x00008170, 0x18486200},
+ {0x00008174, 0x33332210},
+ {0x00008178, 0x00000000},
+ {0x0000817c, 0x00020000},
+ {0x000081c0, 0x00000000},
+ {0x000081c4, 0x33332210},
+ {0x000081c8, 0x00000000},
+ {0x000081cc, 0x00000000},
+ {0x000081d4, 0x00000000},
+ {0x000081ec, 0x00000000},
+ {0x000081f0, 0x00000000},
+ {0x000081f4, 0x00000000},
+ {0x000081f8, 0x00000000},
+ {0x000081fc, 0x00000000},
+ {0x00008240, 0x00100000},
+ {0x00008248, 0x00000800},
+ {0x00008250, 0x00000000},
+ {0x00008254, 0x00000000},
+ {0x00008258, 0x00000000},
+ {0x0000825c, 0x40000000},
+ {0x00008260, 0x00080922},
+ {0x00008264, 0x9d400010},
+ {0x00008268, 0xffffffff},
+ {0x0000826c, 0x0000ffff},
+ {0x00008270, 0x00000000},
+ {0x00008274, 0x40000000},
+ {0x00008278, 0x003e4180},
+ {0x0000827c, 0x00000004},
+ {0x00008284, 0x0000002c},
+ {0x00008288, 0x0000002c},
+ {0x0000828c, 0x000000ff},
+ {0x00008294, 0x00000000},
+ {0x00008298, 0x00000000},
+ {0x0000829c, 0x00000000},
+ {0x00008300, 0x00000140},
+ {0x00008314, 0x00000000},
+ {0x0000831c, 0x0000010d},
+ {0x00008328, 0x00000000},
+ {0x0000832c, 0x00000007},
+ {0x00008330, 0x00000302},
+ {0x00008334, 0x00000700},
+ {0x00008338, 0x00ff0000},
+ {0x0000833c, 0x02400000},
+ {0x00008340, 0x000107ff},
+ {0x00008344, 0xaa48105b},
+ {0x00008348, 0x008f0000},
+ {0x0000835c, 0x00000000},
+ {0x00008360, 0xffffffff},
+ {0x00008364, 0xffffffff},
+ {0x00008368, 0x00000000},
+ {0x00008370, 0x00000000},
+ {0x00008374, 0x000000ff},
+ {0x00008378, 0x00000000},
+ {0x0000837c, 0x00000000},
+ {0x00008380, 0xffffffff},
+ {0x00008384, 0xffffffff},
+ {0x00008390, 0xffffffff},
+ {0x00008394, 0xffffffff},
+ {0x00008398, 0x00000000},
+ {0x0000839c, 0x00000000},
+ {0x000083a0, 0x00000000},
+ {0x000083a4, 0x0000fa14},
+ {0x000083a8, 0x000f0c00},
+ {0x000083ac, 0x33332210},
+ {0x000083b0, 0x33332210},
+ {0x000083b4, 0x33332210},
+ {0x000083b8, 0x33332210},
+ {0x000083bc, 0x00000000},
+ {0x000083c0, 0x00000000},
+ {0x000083c4, 0x00000000},
+ {0x000083c8, 0x00000000},
+ {0x000083cc, 0x00000200},
+ {0x000083d0, 0x000301ff},
+};
+
+static const u32 ar9331_common_rx_gain_1p1[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x01910190},
+ {0x0000a030, 0x01930192},
+ {0x0000a034, 0x01950194},
+ {0x0000a038, 0x038a0196},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x22222229},
+ {0x0000a084, 0x1d1d1d1d},
+ {0x0000a088, 0x1d1d1d1d},
+ {0x0000a08c, 0x1d1d1d1d},
+ {0x0000a090, 0x171d1d1d},
+ {0x0000a094, 0x11111717},
+ {0x0000a098, 0x00030311},
+ {0x0000a09c, 0x00000000},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+};
+
+static const u32 ar9331_common_tx_gain_offset1_1[][1] = {
+ {0},
+ {3},
+ {0},
+ {0},
+};
+
+static const u32 ar9331_1p1_chansel_xtal_25M[] = {
+ 0x0101479e,
+ 0x0101d027,
+ 0x010258af,
+ 0x0102e138,
+ 0x010369c0,
+ 0x0103f249,
+ 0x01047ad1,
+ 0x0105035a,
+ 0x01058be2,
+ 0x0106146b,
+ 0x01069cf3,
+ 0x0107257c,
+ 0x0107ae04,
+ 0x0108f5b2,
+};
+
+static const u32 ar9331_1p1_chansel_xtal_40M[] = {
+ 0x00a0ccbe,
+ 0x00a12213,
+ 0x00a17769,
+ 0x00a1ccbe,
+ 0x00a22213,
+ 0x00a27769,
+ 0x00a2ccbe,
+ 0x00a32213,
+ 0x00a37769,
+ 0x00a3ccbe,
+ 0x00a42213,
+ 0x00a47769,
+ 0x00a4ccbe,
+ 0x00a5998b,
+};
+
+#endif /* INITVALS_9330_1P1_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h
new file mode 100644
index 000000000000..0e6ca0834b34
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h
@@ -0,0 +1,1080 @@
+/*
+ * Copyright (c) 2011 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INITVALS_9330_1P2_H
+#define INITVALS_9330_1P2_H
+
+static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7},
+ {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200},
+ {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202},
+ {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400},
+ {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402},
+ {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404},
+ {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00},
+ {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02},
+ {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04},
+ {0x0000a52c, 0x41023e85, 0x41023e85, 0x3f001620, 0x3f001620},
+ {0x0000a530, 0x48023ec6, 0x48023ec6, 0x41001621, 0x41001621},
+ {0x0000a534, 0x4d023f01, 0x4d023f01, 0x44001640, 0x44001640},
+ {0x0000a538, 0x53023f4b, 0x53023f4b, 0x46001641, 0x46001641},
+ {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x48001642, 0x48001642},
+ {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4b001644, 0x4b001644},
+ {0x0000a544, 0x6502feca, 0x6502feca, 0x4e001a81, 0x4e001a81},
+ {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x51001a83, 0x51001a83},
+ {0x0000a54c, 0x7203feca, 0x7203feca, 0x54001c84, 0x54001c84},
+ {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x57001ce3, 0x57001ce3},
+ {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5b001ce5, 0x5b001ce5},
+ {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5f001ce9, 0x5f001ce9},
+ {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001eec, 0x66001eec},
+ {0x0000a560, 0x900fff0b, 0x900fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001eec, 0x66001eec},
+ {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200},
+ {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202},
+ {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400},
+ {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402},
+ {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404},
+ {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603},
+ {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02},
+ {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04},
+ {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20},
+ {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20},
+ {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22},
+ {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24},
+ {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640},
+ {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660},
+ {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861},
+ {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81},
+ {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83},
+ {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84},
+ {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3},
+ {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9},
+ {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb},
+ {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec},
+ {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x02008501, 0x02008501, 0x02008501, 0x02008501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802},
+ {0x0000a620, 0x0300c802, 0x0300c802, 0x0300c802, 0x0300c802},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x0300cc03, 0x0300cc03},
+ {0x0000a628, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a62c, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a630, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a634, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a638, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a63c, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+};
+
+static const u32 ar9331_1p2_baseband_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005},
+ {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e},
+ {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+ {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+ {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+ {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c},
+ {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044},
+ {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a4, 0x037216a4},
+ {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020},
+ {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
+ {0x00009e10, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e},
+ {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e},
+ {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
+ {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+ {0x00009e2c, 0x0000001c, 0x0000001c, 0x00003221, 0x00003221},
+ {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222},
+ {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324},
+ {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010},
+ {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
+ {0x0000a204, 0x00003fc0, 0x00003fc4, 0x00003fc4, 0x00003fc0},
+ {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
+ {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+ {0x0000a234, 0x00000fff, 0x00000fff, 0x10000fff, 0x00000fff},
+ {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018},
+ {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
+ {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
+ {0x0000a260, 0x3a021501, 0x3a021501, 0x3a021501, 0x3a021501},
+ {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
+ {0x0000a284, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+ {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071981},
+ {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a},
+ {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000ae04, 0x00802020, 0x00802020, 0x00802020, 0x00802020},
+ {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+};
+
+static const u32 ar9331_modes_high_ob_db_tx_gain_1p2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7},
+ {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200},
+ {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202},
+ {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400},
+ {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402},
+ {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404},
+ {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00},
+ {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02},
+ {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04},
+ {0x0000a52c, 0x41023e85, 0x41023e85, 0x3f001620, 0x3f001620},
+ {0x0000a530, 0x48023ec6, 0x48023ec6, 0x41001621, 0x41001621},
+ {0x0000a534, 0x4d023f01, 0x4d023f01, 0x44001640, 0x44001640},
+ {0x0000a538, 0x53023f4b, 0x53023f4b, 0x46001641, 0x46001641},
+ {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x48001642, 0x48001642},
+ {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4b001644, 0x4b001644},
+ {0x0000a544, 0x6502feca, 0x6502feca, 0x4e001a81, 0x4e001a81},
+ {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x51001a83, 0x51001a83},
+ {0x0000a54c, 0x7203feca, 0x7203feca, 0x54001c84, 0x54001c84},
+ {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x57001ce3, 0x57001ce3},
+ {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5b001ce5, 0x5b001ce5},
+ {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5f001ce9, 0x5f001ce9},
+ {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001eec, 0x66001eec},
+ {0x0000a560, 0x900fff0b, 0x900fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001eec, 0x66001eec},
+ {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200},
+ {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202},
+ {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400},
+ {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402},
+ {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404},
+ {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603},
+ {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02},
+ {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04},
+ {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20},
+ {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20},
+ {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22},
+ {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24},
+ {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640},
+ {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660},
+ {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861},
+ {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81},
+ {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83},
+ {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84},
+ {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3},
+ {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9},
+ {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb},
+ {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec},
+ {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x02008501, 0x02008501, 0x02008501, 0x02008501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802},
+ {0x0000a620, 0x0300c802, 0x0300c802, 0x0300c802, 0x0300c802},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x0300cc03, 0x0300cc03},
+ {0x0000a628, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a62c, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a630, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a634, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a638, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a63c, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+};
+
+static const u32 ar9331_modes_low_ob_db_tx_gain_1p2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7},
+ {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200},
+ {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202},
+ {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400},
+ {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402},
+ {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404},
+ {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00},
+ {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02},
+ {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04},
+ {0x0000a52c, 0x41023e85, 0x41023e85, 0x3f001620, 0x3f001620},
+ {0x0000a530, 0x48023ec6, 0x48023ec6, 0x41001621, 0x41001621},
+ {0x0000a534, 0x4d023f01, 0x4d023f01, 0x44001640, 0x44001640},
+ {0x0000a538, 0x53023f4b, 0x53023f4b, 0x46001641, 0x46001641},
+ {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x48001642, 0x48001642},
+ {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4b001644, 0x4b001644},
+ {0x0000a544, 0x6502feca, 0x6502feca, 0x4e001a81, 0x4e001a81},
+ {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x51001a83, 0x51001a83},
+ {0x0000a54c, 0x7203feca, 0x7203feca, 0x54001c84, 0x54001c84},
+ {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x57001ce3, 0x57001ce3},
+ {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5b001ce5, 0x5b001ce5},
+ {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5f001ce9, 0x5f001ce9},
+ {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001eec, 0x66001eec},
+ {0x0000a560, 0x900fff0b, 0x900fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001eec, 0x66001eec},
+ {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200},
+ {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202},
+ {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400},
+ {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402},
+ {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404},
+ {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603},
+ {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02},
+ {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04},
+ {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20},
+ {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20},
+ {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22},
+ {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24},
+ {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640},
+ {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660},
+ {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861},
+ {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81},
+ {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83},
+ {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84},
+ {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3},
+ {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9},
+ {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb},
+ {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec},
+ {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x02008501, 0x02008501, 0x02008501, 0x02008501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802},
+ {0x0000a620, 0x0300c802, 0x0300c802, 0x0300c802, 0x0300c802},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x0300cc03, 0x0300cc03},
+ {0x0000a628, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a62c, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a630, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a634, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a638, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a63c, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+};
+
+static const u32 ar9331_1p2_baseband_core_txfir_coeff_japan_2484[][2] = {
+ /* Addr allmodes */
+ {0x0000a398, 0x00000000},
+ {0x0000a39c, 0x6f7f0301},
+ {0x0000a3a0, 0xca9228ee},
+};
+
+static const u32 ar9331_1p2_xtal_25M[][2] = {
+ /* Addr allmodes */
+ {0x00007038, 0x000002f8},
+ {0x00008244, 0x0010f3d7},
+ {0x0000824c, 0x0001e7ae},
+ {0x0001609c, 0x0f508f29},
+};
+
+static const u32 ar9331_1p2_radio_core[][2] = {
+ /* Addr allmodes */
+ {0x00016000, 0x36db6db6},
+ {0x00016004, 0x6db6db40},
+ {0x00016008, 0x73800000},
+ {0x0001600c, 0x00000000},
+ {0x00016040, 0x7f80fff8},
+ {0x00016044, 0x03d6d2db},
+ {0x00016048, 0x6c924268},
+ {0x0001604c, 0x000f0278},
+ {0x00016050, 0x4db6db8c},
+ {0x00016054, 0x6db60000},
+ {0x00016080, 0x00080000},
+ {0x00016084, 0x0e48048c},
+ {0x00016088, 0x14214514},
+ {0x0001608c, 0x119f081c},
+ {0x00016090, 0x24926490},
+ {0x00016098, 0xd411eb84},
+ {0x000160a0, 0xc2108ffe},
+ {0x000160a4, 0x812fc370},
+ {0x000160a8, 0x423c8000},
+ {0x000160ac, 0x24651800},
+ {0x000160b0, 0x03284f3e},
+ {0x000160b4, 0x92480040},
+ {0x000160c0, 0x006db6db},
+ {0x000160c4, 0x0186db60},
+ {0x000160c8, 0x6db6db6c},
+ {0x000160cc, 0x6de6c300},
+ {0x000160d0, 0x14500820},
+ {0x00016100, 0x04cb0001},
+ {0x00016104, 0xfff80015},
+ {0x00016108, 0x00080010},
+ {0x0001610c, 0x00170000},
+ {0x00016140, 0x10804000},
+ {0x00016144, 0x01884080},
+ {0x00016148, 0x000080c0},
+ {0x00016280, 0x01000015},
+ {0x00016284, 0x14d20000},
+ {0x00016288, 0x00318000},
+ {0x0001628c, 0x50000000},
+ {0x00016290, 0x4b96210f},
+ {0x00016380, 0x00000000},
+ {0x00016384, 0x00000000},
+ {0x00016388, 0x00800700},
+ {0x0001638c, 0x00800700},
+ {0x00016390, 0x00800700},
+ {0x00016394, 0x00000000},
+ {0x00016398, 0x00000000},
+ {0x0001639c, 0x00000000},
+ {0x000163a0, 0x00000001},
+ {0x000163a4, 0x00000001},
+ {0x000163a8, 0x00000000},
+ {0x000163ac, 0x00000000},
+ {0x000163b0, 0x00000000},
+ {0x000163b4, 0x00000000},
+ {0x000163b8, 0x00000000},
+ {0x000163bc, 0x00000000},
+ {0x000163c0, 0x000000a0},
+ {0x000163c4, 0x000c0000},
+ {0x000163c8, 0x14021402},
+ {0x000163cc, 0x00001402},
+ {0x000163d0, 0x00000000},
+ {0x000163d4, 0x00000000},
+};
+
+static const u32 ar9331_1p2_soc_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00007010, 0x00000022, 0x00000022, 0x00000022, 0x00000022},
+};
+
+static const u32 ar9331_common_wo_xlna_rx_gain_1p2[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00060005},
+ {0x0000a004, 0x00810080},
+ {0x0000a008, 0x00830082},
+ {0x0000a00c, 0x00850084},
+ {0x0000a010, 0x01820181},
+ {0x0000a014, 0x01840183},
+ {0x0000a018, 0x01880185},
+ {0x0000a01c, 0x018a0189},
+ {0x0000a020, 0x02850284},
+ {0x0000a024, 0x02890288},
+ {0x0000a028, 0x028b028a},
+ {0x0000a02c, 0x03850384},
+ {0x0000a030, 0x03890388},
+ {0x0000a034, 0x038b038a},
+ {0x0000a038, 0x038d038c},
+ {0x0000a03c, 0x03910390},
+ {0x0000a040, 0x03930392},
+ {0x0000a044, 0x03950394},
+ {0x0000a048, 0x00000396},
+ {0x0000a04c, 0x00000000},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x28282828},
+ {0x0000a084, 0x28282828},
+ {0x0000a088, 0x28282828},
+ {0x0000a08c, 0x28282828},
+ {0x0000a090, 0x28282828},
+ {0x0000a094, 0x24242428},
+ {0x0000a098, 0x171e1e1e},
+ {0x0000a09c, 0x02020b0b},
+ {0x0000a0a0, 0x02020202},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x22072208},
+ {0x0000a0c4, 0x22052206},
+ {0x0000a0c8, 0x22032204},
+ {0x0000a0cc, 0x22012202},
+ {0x0000a0d0, 0x221f2200},
+ {0x0000a0d4, 0x221d221e},
+ {0x0000a0d8, 0x33023303},
+ {0x0000a0dc, 0x33003301},
+ {0x0000a0e0, 0x331e331f},
+ {0x0000a0e4, 0x4402331d},
+ {0x0000a0e8, 0x44004401},
+ {0x0000a0ec, 0x441e441f},
+ {0x0000a0f0, 0x55025503},
+ {0x0000a0f4, 0x55005501},
+ {0x0000a0f8, 0x551e551f},
+ {0x0000a0fc, 0x6602551d},
+ {0x0000a100, 0x66006601},
+ {0x0000a104, 0x661e661f},
+ {0x0000a108, 0x7703661d},
+ {0x0000a10c, 0x77017702},
+ {0x0000a110, 0x00007700},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x111f1100},
+ {0x0000a148, 0x111d111e},
+ {0x0000a14c, 0x111b111c},
+ {0x0000a150, 0x22032204},
+ {0x0000a154, 0x22012202},
+ {0x0000a158, 0x221f2200},
+ {0x0000a15c, 0x221d221e},
+ {0x0000a160, 0x33013302},
+ {0x0000a164, 0x331f3300},
+ {0x0000a168, 0x4402331e},
+ {0x0000a16c, 0x44004401},
+ {0x0000a170, 0x441e441f},
+ {0x0000a174, 0x55015502},
+ {0x0000a178, 0x551f5500},
+ {0x0000a17c, 0x6602551e},
+ {0x0000a180, 0x66006601},
+ {0x0000a184, 0x661e661f},
+ {0x0000a188, 0x7703661d},
+ {0x0000a18c, 0x77017702},
+ {0x0000a190, 0x00007700},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000296},
+};
+
+static const u32 ar9331_1p2_baseband_core[][2] = {
+ /* Addr allmodes */
+ {0x00009800, 0xafe68e30},
+ {0x00009804, 0xfd14e000},
+ {0x00009808, 0x9c0a8f6b},
+ {0x0000980c, 0x04800000},
+ {0x00009814, 0x9280c00a},
+ {0x00009818, 0x00000000},
+ {0x0000981c, 0x00020028},
+ {0x00009834, 0x5f3ca3de},
+ {0x00009838, 0x0108ecff},
+ {0x0000983c, 0x14750600},
+ {0x00009880, 0x201fff00},
+ {0x00009884, 0x00001042},
+ {0x000098a4, 0x00200400},
+ {0x000098b0, 0x32840bbe},
+ {0x000098d0, 0x004b6a8e},
+ {0x000098d4, 0x00000820},
+ {0x000098dc, 0x00000000},
+ {0x000098f0, 0x00000000},
+ {0x000098f4, 0x00000000},
+ {0x00009c04, 0x00000000},
+ {0x00009c08, 0x03200000},
+ {0x00009c0c, 0x00000000},
+ {0x00009c10, 0x00000000},
+ {0x00009c14, 0x00046384},
+ {0x00009c18, 0x05b6b440},
+ {0x00009c1c, 0x00b6b440},
+ {0x00009d00, 0xc080a333},
+ {0x00009d04, 0x40206c10},
+ {0x00009d08, 0x009c4060},
+ {0x00009d0c, 0x1883800a},
+ {0x00009d10, 0x01834061},
+ {0x00009d14, 0x00c00400},
+ {0x00009d18, 0x00000000},
+ {0x00009e08, 0x0038233c},
+ {0x00009e24, 0x9927b515},
+ {0x00009e28, 0x12ef0200},
+ {0x00009e30, 0x06336f77},
+ {0x00009e34, 0x6af6532f},
+ {0x00009e38, 0x0cc80c00},
+ {0x00009e40, 0x0d261820},
+ {0x00009e4c, 0x00001004},
+ {0x00009e50, 0x00ff03f1},
+ {0x00009fc0, 0x803e4788},
+ {0x00009fc4, 0x0001efb5},
+ {0x00009fcc, 0x40000014},
+ {0x0000a20c, 0x00000000},
+ {0x0000a220, 0x00000000},
+ {0x0000a224, 0x00000000},
+ {0x0000a228, 0x10002310},
+ {0x0000a23c, 0x00000000},
+ {0x0000a244, 0x0c000000},
+ {0x0000a2a0, 0x00000001},
+ {0x0000a2c0, 0x00000001},
+ {0x0000a2c8, 0x00000000},
+ {0x0000a2cc, 0x18c43433},
+ {0x0000a2d4, 0x00000000},
+ {0x0000a2dc, 0x00000000},
+ {0x0000a2e0, 0x00000000},
+ {0x0000a2e4, 0x00000000},
+ {0x0000a2e8, 0x00000000},
+ {0x0000a2ec, 0x00000000},
+ {0x0000a2f0, 0x00000000},
+ {0x0000a2f4, 0x00000000},
+ {0x0000a2f8, 0x00000000},
+ {0x0000a344, 0x00000000},
+ {0x0000a34c, 0x00000000},
+ {0x0000a350, 0x0000a000},
+ {0x0000a364, 0x00000000},
+ {0x0000a370, 0x00000000},
+ {0x0000a390, 0x00000001},
+ {0x0000a394, 0x00000444},
+ {0x0000a398, 0x001f0e0f},
+ {0x0000a39c, 0x0075393f},
+ {0x0000a3a0, 0xb79f6427},
+ {0x0000a3a4, 0x00000000},
+ {0x0000a3a8, 0xaaaaaaaa},
+ {0x0000a3ac, 0x3c466478},
+ {0x0000a3c0, 0x20202020},
+ {0x0000a3c4, 0x22222220},
+ {0x0000a3c8, 0x20200020},
+ {0x0000a3cc, 0x20202020},
+ {0x0000a3d0, 0x20202020},
+ {0x0000a3d4, 0x20202020},
+ {0x0000a3d8, 0x20202020},
+ {0x0000a3dc, 0x20202020},
+ {0x0000a3e0, 0x20202020},
+ {0x0000a3e4, 0x20202020},
+ {0x0000a3e8, 0x20202020},
+ {0x0000a3ec, 0x20202020},
+ {0x0000a3f0, 0x00000000},
+ {0x0000a3f4, 0x00000006},
+ {0x0000a3f8, 0x0cdbd380},
+ {0x0000a3fc, 0x000f0f01},
+ {0x0000a400, 0x8fa91f01},
+ {0x0000a404, 0x00000000},
+ {0x0000a408, 0x0e79e5c6},
+ {0x0000a40c, 0x00820820},
+ {0x0000a414, 0x1ce739ce},
+ {0x0000a418, 0x2d001dce},
+ {0x0000a41c, 0x1ce739ce},
+ {0x0000a420, 0x000001ce},
+ {0x0000a424, 0x1ce739ce},
+ {0x0000a428, 0x000001ce},
+ {0x0000a42c, 0x1ce739ce},
+ {0x0000a430, 0x1ce739ce},
+ {0x0000a434, 0x00000000},
+ {0x0000a438, 0x00001801},
+ {0x0000a43c, 0x00000000},
+ {0x0000a440, 0x00000000},
+ {0x0000a444, 0x00000000},
+ {0x0000a448, 0x04000000},
+ {0x0000a44c, 0x00000001},
+ {0x0000a450, 0x00010000},
+ {0x0000a458, 0x00000000},
+ {0x0000a640, 0x00000000},
+ {0x0000a644, 0x3fad9d74},
+ {0x0000a648, 0x0048060a},
+ {0x0000a64c, 0x00003c37},
+ {0x0000a670, 0x03020100},
+ {0x0000a674, 0x09080504},
+ {0x0000a678, 0x0d0c0b0a},
+ {0x0000a67c, 0x13121110},
+ {0x0000a680, 0x31301514},
+ {0x0000a684, 0x35343332},
+ {0x0000a688, 0x00000036},
+ {0x0000a690, 0x00000838},
+ {0x0000a7c0, 0x00000000},
+ {0x0000a7c4, 0xfffffffc},
+ {0x0000a7c8, 0x00000000},
+ {0x0000a7cc, 0x00000000},
+ {0x0000a7d0, 0x00000000},
+ {0x0000a7d4, 0x00000004},
+ {0x0000a7dc, 0x00000001},
+};
+
+static const u32 ar9331_modes_high_power_tx_gain_1p2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7},
+ {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200},
+ {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202},
+ {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400},
+ {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402},
+ {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404},
+ {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00},
+ {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02},
+ {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04},
+ {0x0000a52c, 0x41023e85, 0x41023e85, 0x3f001620, 0x3f001620},
+ {0x0000a530, 0x48023ec6, 0x48023ec6, 0x41001621, 0x41001621},
+ {0x0000a534, 0x4d023f01, 0x4d023f01, 0x44001640, 0x44001640},
+ {0x0000a538, 0x53023f4b, 0x53023f4b, 0x46001641, 0x46001641},
+ {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x48001642, 0x48001642},
+ {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4b001644, 0x4b001644},
+ {0x0000a544, 0x6502feca, 0x6502feca, 0x4e001a81, 0x4e001a81},
+ {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x51001a83, 0x51001a83},
+ {0x0000a54c, 0x7203feca, 0x7203feca, 0x54001c84, 0x54001c84},
+ {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x57001ce3, 0x57001ce3},
+ {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5b001ce5, 0x5b001ce5},
+ {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5f001ce9, 0x5f001ce9},
+ {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001eec, 0x66001eec},
+ {0x0000a560, 0x900fff0b, 0x900fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001eec, 0x66001eec},
+ {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec},
+ {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+ {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+ {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+ {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200},
+ {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202},
+ {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400},
+ {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402},
+ {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404},
+ {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603},
+ {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02},
+ {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04},
+ {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20},
+ {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20},
+ {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22},
+ {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24},
+ {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640},
+ {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660},
+ {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861},
+ {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81},
+ {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83},
+ {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84},
+ {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3},
+ {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9},
+ {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb},
+ {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec},
+ {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x02008501, 0x02008501, 0x02008501, 0x02008501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802},
+ {0x0000a620, 0x0300c802, 0x0300c802, 0x0300c802, 0x0300c802},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x0300cc03, 0x0300cc03},
+ {0x0000a628, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a62c, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a630, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a634, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a638, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+ {0x0000a63c, 0x04011004, 0x04011004, 0x04011004, 0x04011004},
+};
+
+static const u32 ar9331_1p2_mac_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b},
+ {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+ {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+ {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
+};
+
+static const u32 ar9331_1p2_soc_preamble[][2] = {
+ /* Addr allmodes */
+ {0x00007020, 0x00000000},
+ {0x00007034, 0x00000002},
+ {0x00007038, 0x000002f8},
+};
+
+static const u32 ar9331_1p2_xtal_40M[][2] = {
+ /* Addr allmodes */
+ {0x00007038, 0x000004c2},
+ {0x00008244, 0x0010f400},
+ {0x0000824c, 0x0001e800},
+ {0x0001609c, 0x0b283f31},
+};
+
+static const u32 ar9331_1p2_mac_core[][2] = {
+ /* Addr allmodes */
+ {0x00000008, 0x00000000},
+ {0x00000030, 0x00020085},
+ {0x00000034, 0x00000005},
+ {0x00000040, 0x00000000},
+ {0x00000044, 0x00000000},
+ {0x00000048, 0x00000008},
+ {0x0000004c, 0x00000010},
+ {0x00000050, 0x00000000},
+ {0x00001040, 0x002ffc0f},
+ {0x00001044, 0x002ffc0f},
+ {0x00001048, 0x002ffc0f},
+ {0x0000104c, 0x002ffc0f},
+ {0x00001050, 0x002ffc0f},
+ {0x00001054, 0x002ffc0f},
+ {0x00001058, 0x002ffc0f},
+ {0x0000105c, 0x002ffc0f},
+ {0x00001060, 0x002ffc0f},
+ {0x00001064, 0x002ffc0f},
+ {0x000010f0, 0x00000100},
+ {0x00001270, 0x00000000},
+ {0x000012b0, 0x00000000},
+ {0x000012f0, 0x00000000},
+ {0x0000143c, 0x00000000},
+ {0x0000147c, 0x00000000},
+ {0x00008000, 0x00000000},
+ {0x00008004, 0x00000000},
+ {0x00008008, 0x00000000},
+ {0x0000800c, 0x00000000},
+ {0x00008018, 0x00000000},
+ {0x00008020, 0x00000000},
+ {0x00008038, 0x00000000},
+ {0x0000803c, 0x00000000},
+ {0x00008040, 0x00000000},
+ {0x00008044, 0x00000000},
+ {0x00008048, 0x00000000},
+ {0x0000804c, 0xffffffff},
+ {0x00008054, 0x00000000},
+ {0x00008058, 0x00000000},
+ {0x0000805c, 0x000fc78f},
+ {0x00008060, 0x0000000f},
+ {0x00008064, 0x00000000},
+ {0x00008070, 0x00000310},
+ {0x00008074, 0x00000020},
+ {0x00008078, 0x00000000},
+ {0x0000809c, 0x0000000f},
+ {0x000080a0, 0x00000000},
+ {0x000080a4, 0x02ff0000},
+ {0x000080a8, 0x0e070605},
+ {0x000080ac, 0x0000000d},
+ {0x000080b0, 0x00000000},
+ {0x000080b4, 0x00000000},
+ {0x000080b8, 0x00000000},
+ {0x000080bc, 0x00000000},
+ {0x000080c0, 0x2a800000},
+ {0x000080c4, 0x06900168},
+ {0x000080c8, 0x13881c20},
+ {0x000080cc, 0x01f40000},
+ {0x000080d0, 0x00252500},
+ {0x000080d4, 0x00a00000},
+ {0x000080d8, 0x00400000},
+ {0x000080dc, 0x00000000},
+ {0x000080e0, 0xffffffff},
+ {0x000080e4, 0x0000ffff},
+ {0x000080e8, 0x3f3f3f3f},
+ {0x000080ec, 0x00000000},
+ {0x000080f0, 0x00000000},
+ {0x000080f4, 0x00000000},
+ {0x000080fc, 0x00020000},
+ {0x00008100, 0x00000000},
+ {0x00008108, 0x00000052},
+ {0x0000810c, 0x00000000},
+ {0x00008110, 0x00000000},
+ {0x00008114, 0x000007ff},
+ {0x00008118, 0x000000aa},
+ {0x0000811c, 0x00003210},
+ {0x00008124, 0x00000000},
+ {0x00008128, 0x00000000},
+ {0x0000812c, 0x00000000},
+ {0x00008130, 0x00000000},
+ {0x00008134, 0x00000000},
+ {0x00008138, 0x00000000},
+ {0x0000813c, 0x0000ffff},
+ {0x00008144, 0xffffffff},
+ {0x00008168, 0x00000000},
+ {0x0000816c, 0x00000000},
+ {0x00008170, 0x18486200},
+ {0x00008174, 0x33332210},
+ {0x00008178, 0x00000000},
+ {0x0000817c, 0x00020000},
+ {0x000081c0, 0x00000000},
+ {0x000081c4, 0x33332210},
+ {0x000081c8, 0x00000000},
+ {0x000081cc, 0x00000000},
+ {0x000081d4, 0x00000000},
+ {0x000081ec, 0x00000000},
+ {0x000081f0, 0x00000000},
+ {0x000081f4, 0x00000000},
+ {0x000081f8, 0x00000000},
+ {0x000081fc, 0x00000000},
+ {0x00008240, 0x00100000},
+ {0x00008248, 0x00000800},
+ {0x00008250, 0x00000000},
+ {0x00008254, 0x00000000},
+ {0x00008258, 0x00000000},
+ {0x0000825c, 0x40000000},
+ {0x00008260, 0x00080922},
+ {0x00008264, 0x9d400010},
+ {0x00008268, 0xffffffff},
+ {0x0000826c, 0x0000ffff},
+ {0x00008270, 0x00000000},
+ {0x00008274, 0x40000000},
+ {0x00008278, 0x003e4180},
+ {0x0000827c, 0x00000004},
+ {0x00008284, 0x0000002c},
+ {0x00008288, 0x0000002c},
+ {0x0000828c, 0x000000ff},
+ {0x00008294, 0x00000000},
+ {0x00008298, 0x00000000},
+ {0x0000829c, 0x00000000},
+ {0x00008300, 0x00000140},
+ {0x00008314, 0x00000000},
+ {0x0000831c, 0x0000010d},
+ {0x00008328, 0x00000000},
+ {0x0000832c, 0x00000007},
+ {0x00008330, 0x00000302},
+ {0x00008334, 0x00000700},
+ {0x00008338, 0x00ff0000},
+ {0x0000833c, 0x02400000},
+ {0x00008340, 0x000107ff},
+ {0x00008344, 0xaa48105b},
+ {0x00008348, 0x008f0000},
+ {0x0000835c, 0x00000000},
+ {0x00008360, 0xffffffff},
+ {0x00008364, 0xffffffff},
+ {0x00008368, 0x00000000},
+ {0x00008370, 0x00000000},
+ {0x00008374, 0x000000ff},
+ {0x00008378, 0x00000000},
+ {0x0000837c, 0x00000000},
+ {0x00008380, 0xffffffff},
+ {0x00008384, 0xffffffff},
+ {0x00008390, 0xffffffff},
+ {0x00008394, 0xffffffff},
+ {0x00008398, 0x00000000},
+ {0x0000839c, 0x00000000},
+ {0x000083a0, 0x00000000},
+ {0x000083a4, 0x0000fa14},
+ {0x000083a8, 0x000f0c00},
+ {0x000083ac, 0x33332210},
+ {0x000083b0, 0x33332210},
+ {0x000083b4, 0x33332210},
+ {0x000083b8, 0x33332210},
+ {0x000083bc, 0x00000000},
+ {0x000083c0, 0x00000000},
+ {0x000083c4, 0x00000000},
+ {0x000083c8, 0x00000000},
+ {0x000083cc, 0x00000200},
+ {0x000083d0, 0x000301ff},
+};
+
+static const u32 ar9331_common_rx_gain_1p2[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x01800082},
+ {0x0000a014, 0x01820181},
+ {0x0000a018, 0x01840183},
+ {0x0000a01c, 0x01880185},
+ {0x0000a020, 0x018a0189},
+ {0x0000a024, 0x02850284},
+ {0x0000a028, 0x02890288},
+ {0x0000a02c, 0x03850384},
+ {0x0000a030, 0x03890388},
+ {0x0000a034, 0x038b038a},
+ {0x0000a038, 0x038d038c},
+ {0x0000a03c, 0x03910390},
+ {0x0000a040, 0x03930392},
+ {0x0000a044, 0x03950394},
+ {0x0000a048, 0x00000396},
+ {0x0000a04c, 0x00000000},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x28282828},
+ {0x0000a084, 0x28282828},
+ {0x0000a088, 0x28282828},
+ {0x0000a08c, 0x28282828},
+ {0x0000a090, 0x28282828},
+ {0x0000a094, 0x21212128},
+ {0x0000a098, 0x171c1c1c},
+ {0x0000a09c, 0x02020212},
+ {0x0000a0a0, 0x00000202},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x111f1100},
+ {0x0000a0c8, 0x111d111e},
+ {0x0000a0cc, 0x111b111c},
+ {0x0000a0d0, 0x22032204},
+ {0x0000a0d4, 0x22012202},
+ {0x0000a0d8, 0x221f2200},
+ {0x0000a0dc, 0x221d221e},
+ {0x0000a0e0, 0x33013302},
+ {0x0000a0e4, 0x331f3300},
+ {0x0000a0e8, 0x4402331e},
+ {0x0000a0ec, 0x44004401},
+ {0x0000a0f0, 0x441e441f},
+ {0x0000a0f4, 0x55015502},
+ {0x0000a0f8, 0x551f5500},
+ {0x0000a0fc, 0x6602551e},
+ {0x0000a100, 0x66006601},
+ {0x0000a104, 0x661e661f},
+ {0x0000a108, 0x7703661d},
+ {0x0000a10c, 0x77017702},
+ {0x0000a110, 0x00007700},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x111f1100},
+ {0x0000a148, 0x111d111e},
+ {0x0000a14c, 0x111b111c},
+ {0x0000a150, 0x22032204},
+ {0x0000a154, 0x22012202},
+ {0x0000a158, 0x221f2200},
+ {0x0000a15c, 0x221d221e},
+ {0x0000a160, 0x33013302},
+ {0x0000a164, 0x331f3300},
+ {0x0000a168, 0x4402331e},
+ {0x0000a16c, 0x44004401},
+ {0x0000a170, 0x441e441f},
+ {0x0000a174, 0x55015502},
+ {0x0000a178, 0x551f5500},
+ {0x0000a17c, 0x6602551e},
+ {0x0000a180, 0x66006601},
+ {0x0000a184, 0x661e661f},
+ {0x0000a188, 0x7703661d},
+ {0x0000a18c, 0x77017702},
+ {0x0000a190, 0x00007700},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000296},
+};
+
+#endif /* INITVALS_9330_1P2_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar9462_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_1p0_initvals.h
new file mode 100644
index 000000000000..5c55ae389adb
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9462_1p0_initvals.h
@@ -0,0 +1,1833 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INITVALS_9462_1P0_H
+#define INITVALS_9462_1P0_H
+
+/* AR9462 1.0 */
+
+static const u32 ar9462_1p0_mac_core[][2] = {
+ /* Addr allmodes */
+ {0x00000008, 0x00000000},
+ {0x00000030, 0x00060085},
+ {0x00000034, 0x00000005},
+ {0x00000040, 0x00000000},
+ {0x00000044, 0x00000000},
+ {0x00000048, 0x00000008},
+ {0x0000004c, 0x00000010},
+ {0x00000050, 0x00000000},
+ {0x00001040, 0x002ffc0f},
+ {0x00001044, 0x002ffc0f},
+ {0x00001048, 0x002ffc0f},
+ {0x0000104c, 0x002ffc0f},
+ {0x00001050, 0x002ffc0f},
+ {0x00001054, 0x002ffc0f},
+ {0x00001058, 0x002ffc0f},
+ {0x0000105c, 0x002ffc0f},
+ {0x00001060, 0x002ffc0f},
+ {0x00001064, 0x002ffc0f},
+ {0x000010f0, 0x00000100},
+ {0x00001270, 0x00000000},
+ {0x000012b0, 0x00000000},
+ {0x000012f0, 0x00000000},
+ {0x0000143c, 0x00000000},
+ {0x0000147c, 0x00000000},
+ {0x00001810, 0x0f000003},
+ {0x00008000, 0x00000000},
+ {0x00008004, 0x00000000},
+ {0x00008008, 0x00000000},
+ {0x0000800c, 0x00000000},
+ {0x00008018, 0x00000000},
+ {0x00008020, 0x00000000},
+ {0x00008038, 0x00000000},
+ {0x0000803c, 0x00080000},
+ {0x00008040, 0x00000000},
+ {0x00008044, 0x00000000},
+ {0x00008048, 0x00000000},
+ {0x0000804c, 0xffffffff},
+ {0x00008050, 0xffffffff},
+ {0x00008054, 0x00000000},
+ {0x00008058, 0x00000000},
+ {0x0000805c, 0x000fc78f},
+ {0x00008060, 0x0000000f},
+ {0x00008064, 0x00000000},
+ {0x00008070, 0x00000310},
+ {0x00008074, 0x00000020},
+ {0x00008078, 0x00000000},
+ {0x0000809c, 0x0000000f},
+ {0x000080a0, 0x00000000},
+ {0x000080a4, 0x02ff0000},
+ {0x000080a8, 0x0e070605},
+ {0x000080ac, 0x0000000d},
+ {0x000080b0, 0x00000000},
+ {0x000080b4, 0x00000000},
+ {0x000080b8, 0x00000000},
+ {0x000080bc, 0x00000000},
+ {0x000080c0, 0x2a800000},
+ {0x000080c4, 0x06900168},
+ {0x000080c8, 0x13881c20},
+ {0x000080cc, 0x01f40000},
+ {0x000080d0, 0x00252500},
+ {0x000080d4, 0x00a00005},
+ {0x000080d8, 0x00400002},
+ {0x000080dc, 0x00000000},
+ {0x000080e0, 0xffffffff},
+ {0x000080e4, 0x0000ffff},
+ {0x000080e8, 0x3f3f3f3f},
+ {0x000080ec, 0x00000000},
+ {0x000080f0, 0x00000000},
+ {0x000080f4, 0x00000000},
+ {0x000080fc, 0x00020000},
+ {0x00008100, 0x00000000},
+ {0x00008108, 0x00000052},
+ {0x0000810c, 0x00000000},
+ {0x00008110, 0x00000000},
+ {0x00008114, 0x000007ff},
+ {0x00008118, 0x000000aa},
+ {0x0000811c, 0x00003210},
+ {0x00008124, 0x00000000},
+ {0x00008128, 0x00000000},
+ {0x0000812c, 0x00000000},
+ {0x00008130, 0x00000000},
+ {0x00008134, 0x00000000},
+ {0x00008138, 0x00000000},
+ {0x0000813c, 0x0000ffff},
+ {0x00008144, 0xffffffff},
+ {0x00008168, 0x00000000},
+ {0x0000816c, 0x00000000},
+ {0x00008170, 0x18486e00},
+ {0x00008174, 0x33332210},
+ {0x00008178, 0x00000000},
+ {0x0000817c, 0x00020000},
+ {0x000081c4, 0x33332210},
+ {0x000081c8, 0x00000000},
+ {0x000081cc, 0x00000000},
+ {0x000081d4, 0x00000000},
+ {0x000081ec, 0x00000000},
+ {0x000081f0, 0x00000000},
+ {0x000081f4, 0x00000000},
+ {0x000081f8, 0x00000000},
+ {0x000081fc, 0x00000000},
+ {0x00008240, 0x00100000},
+ {0x00008244, 0x0010f400},
+ {0x00008248, 0x00000800},
+ {0x0000824c, 0x0001e800},
+ {0x00008250, 0x00000000},
+ {0x00008254, 0x00000000},
+ {0x00008258, 0x00000000},
+ {0x0000825c, 0x40000000},
+ {0x00008260, 0x00080922},
+ {0x00008264, 0x99c00010},
+ {0x00008268, 0xffffffff},
+ {0x0000826c, 0x0000ffff},
+ {0x00008270, 0x00000000},
+ {0x00008274, 0x40000000},
+ {0x00008278, 0x003e4180},
+ {0x0000827c, 0x00000004},
+ {0x00008284, 0x0000002c},
+ {0x00008288, 0x0000002c},
+ {0x0000828c, 0x000000ff},
+ {0x00008294, 0x00000000},
+ {0x00008298, 0x00000000},
+ {0x0000829c, 0x00000000},
+ {0x00008300, 0x00000140},
+ {0x00008314, 0x00000000},
+ {0x0000831c, 0x0000010d},
+ {0x00008328, 0x00000000},
+ {0x0000832c, 0x0000001f},
+ {0x00008330, 0x00000302},
+ {0x00008334, 0x00000700},
+ {0x00008338, 0xffff0000},
+ {0x0000833c, 0x02400000},
+ {0x00008340, 0x000107ff},
+ {0x00008344, 0xaa48105b},
+ {0x00008348, 0x008f0000},
+ {0x0000835c, 0x00000000},
+ {0x00008360, 0xffffffff},
+ {0x00008364, 0xffffffff},
+ {0x00008368, 0x00000000},
+ {0x00008370, 0x00000000},
+ {0x00008374, 0x000000ff},
+ {0x00008378, 0x00000000},
+ {0x0000837c, 0x00000000},
+ {0x00008380, 0xffffffff},
+ {0x00008384, 0xffffffff},
+ {0x00008390, 0xffffffff},
+ {0x00008394, 0xffffffff},
+ {0x00008398, 0x00000000},
+ {0x0000839c, 0x00000000},
+ {0x000083a4, 0x0000fa14},
+ {0x000083a8, 0x000f0c00},
+ {0x000083ac, 0x33332210},
+ {0x000083b0, 0x33332210},
+ {0x000083b4, 0x33332210},
+ {0x000083b8, 0x33332210},
+ {0x000083bc, 0x00000000},
+ {0x000083c0, 0x00000000},
+ {0x000083c4, 0x00000000},
+ {0x000083c8, 0x00000000},
+ {0x000083cc, 0x00000200},
+ {0x000083d0, 0x000301ff},
+};
+
+static const u32 ar9462_1p0_baseband_core_txfir_coeff_japan_2484[][2] = {
+ /* Addr allmodes */
+ {0x0000a398, 0x00000000},
+ {0x0000a39c, 0x6f7f0301},
+ {0x0000a3a0, 0xca9228ee},
+};
+
+static const u32 ar9462_1p0_sys3ant[][2] = {
+ /* Addr allmodes */
+ {0x00063280, 0x00040807},
+ {0x00063284, 0x104ccccc},
+};
+
+static const u32 ar9462_pcie_phy_clkreq_enable_L1_1p0[][2] = {
+ /* Addr allmodes */
+ {0x00018c00, 0x10053e5e},
+ {0x00018c04, 0x000801d8},
+ {0x00018c08, 0x0000580c},
+};
+
+static const u32 ar9462_1p0_mac_core_emulation[][2] = {
+ /* Addr allmodes */
+ {0x00000030, 0x00060085},
+ {0x00000044, 0x00000008},
+ {0x0000805c, 0xffffc7ff},
+ {0x00008344, 0xaa4a105b},
+};
+
+static const u32 ar9462_common_rx_gain_table_ar9280_2p0_1p0[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x02000101},
+ {0x0000a004, 0x02000102},
+ {0x0000a008, 0x02000103},
+ {0x0000a00c, 0x02000104},
+ {0x0000a010, 0x02000200},
+ {0x0000a014, 0x02000201},
+ {0x0000a018, 0x02000202},
+ {0x0000a01c, 0x02000203},
+ {0x0000a020, 0x02000204},
+ {0x0000a024, 0x02000205},
+ {0x0000a028, 0x02000208},
+ {0x0000a02c, 0x02000302},
+ {0x0000a030, 0x02000303},
+ {0x0000a034, 0x02000304},
+ {0x0000a038, 0x02000400},
+ {0x0000a03c, 0x02010300},
+ {0x0000a040, 0x02010301},
+ {0x0000a044, 0x02010302},
+ {0x0000a048, 0x02000500},
+ {0x0000a04c, 0x02010400},
+ {0x0000a050, 0x02020300},
+ {0x0000a054, 0x02020301},
+ {0x0000a058, 0x02020302},
+ {0x0000a05c, 0x02020303},
+ {0x0000a060, 0x02020400},
+ {0x0000a064, 0x02030300},
+ {0x0000a068, 0x02030301},
+ {0x0000a06c, 0x02030302},
+ {0x0000a070, 0x02030303},
+ {0x0000a074, 0x02030400},
+ {0x0000a078, 0x02040300},
+ {0x0000a07c, 0x02040301},
+ {0x0000a080, 0x02040302},
+ {0x0000a084, 0x02040303},
+ {0x0000a088, 0x02030500},
+ {0x0000a08c, 0x02040400},
+ {0x0000a090, 0x02050203},
+ {0x0000a094, 0x02050204},
+ {0x0000a098, 0x02050205},
+ {0x0000a09c, 0x02040500},
+ {0x0000a0a0, 0x02050301},
+ {0x0000a0a4, 0x02050302},
+ {0x0000a0a8, 0x02050303},
+ {0x0000a0ac, 0x02050400},
+ {0x0000a0b0, 0x02050401},
+ {0x0000a0b4, 0x02050402},
+ {0x0000a0b8, 0x02050403},
+ {0x0000a0bc, 0x02050500},
+ {0x0000a0c0, 0x02050501},
+ {0x0000a0c4, 0x02050502},
+ {0x0000a0c8, 0x02050503},
+ {0x0000a0cc, 0x02050504},
+ {0x0000a0d0, 0x02050600},
+ {0x0000a0d4, 0x02050601},
+ {0x0000a0d8, 0x02050602},
+ {0x0000a0dc, 0x02050603},
+ {0x0000a0e0, 0x02050604},
+ {0x0000a0e4, 0x02050700},
+ {0x0000a0e8, 0x02050701},
+ {0x0000a0ec, 0x02050702},
+ {0x0000a0f0, 0x02050703},
+ {0x0000a0f4, 0x02050704},
+ {0x0000a0f8, 0x02050705},
+ {0x0000a0fc, 0x02050708},
+ {0x0000a100, 0x02050709},
+ {0x0000a104, 0x0205070a},
+ {0x0000a108, 0x0205070b},
+ {0x0000a10c, 0x0205070c},
+ {0x0000a110, 0x0205070d},
+ {0x0000a114, 0x02050710},
+ {0x0000a118, 0x02050711},
+ {0x0000a11c, 0x02050712},
+ {0x0000a120, 0x02050713},
+ {0x0000a124, 0x02050714},
+ {0x0000a128, 0x02050715},
+ {0x0000a12c, 0x02050730},
+ {0x0000a130, 0x02050731},
+ {0x0000a134, 0x02050732},
+ {0x0000a138, 0x02050733},
+ {0x0000a13c, 0x02050734},
+ {0x0000a140, 0x02050735},
+ {0x0000a144, 0x02050750},
+ {0x0000a148, 0x02050751},
+ {0x0000a14c, 0x02050752},
+ {0x0000a150, 0x02050753},
+ {0x0000a154, 0x02050754},
+ {0x0000a158, 0x02050755},
+ {0x0000a15c, 0x02050770},
+ {0x0000a160, 0x02050771},
+ {0x0000a164, 0x02050772},
+ {0x0000a168, 0x02050773},
+ {0x0000a16c, 0x02050774},
+ {0x0000a170, 0x02050775},
+ {0x0000a174, 0x00000776},
+ {0x0000a178, 0x00000776},
+ {0x0000a17c, 0x00000776},
+ {0x0000a180, 0x00000776},
+ {0x0000a184, 0x00000776},
+ {0x0000a188, 0x00000776},
+ {0x0000a18c, 0x00000776},
+ {0x0000a190, 0x00000776},
+ {0x0000a194, 0x00000776},
+ {0x0000a198, 0x00000776},
+ {0x0000a19c, 0x00000776},
+ {0x0000a1a0, 0x00000776},
+ {0x0000a1a4, 0x00000776},
+ {0x0000a1a8, 0x00000776},
+ {0x0000a1ac, 0x00000776},
+ {0x0000a1b0, 0x00000776},
+ {0x0000a1b4, 0x00000776},
+ {0x0000a1b8, 0x00000776},
+ {0x0000a1bc, 0x00000776},
+ {0x0000a1c0, 0x00000776},
+ {0x0000a1c4, 0x00000776},
+ {0x0000a1c8, 0x00000776},
+ {0x0000a1cc, 0x00000776},
+ {0x0000a1d0, 0x00000776},
+ {0x0000a1d4, 0x00000776},
+ {0x0000a1d8, 0x00000776},
+ {0x0000a1dc, 0x00000776},
+ {0x0000a1e0, 0x00000776},
+ {0x0000a1e4, 0x00000776},
+ {0x0000a1e8, 0x00000776},
+ {0x0000a1ec, 0x00000776},
+ {0x0000a1f0, 0x00000776},
+ {0x0000a1f4, 0x00000776},
+ {0x0000a1f8, 0x00000776},
+ {0x0000a1fc, 0x00000776},
+ {0x0000b000, 0x02000101},
+ {0x0000b004, 0x02000102},
+ {0x0000b008, 0x02000103},
+ {0x0000b00c, 0x02000104},
+ {0x0000b010, 0x02000200},
+ {0x0000b014, 0x02000201},
+ {0x0000b018, 0x02000202},
+ {0x0000b01c, 0x02000203},
+ {0x0000b020, 0x02000204},
+ {0x0000b024, 0x02000205},
+ {0x0000b028, 0x02000208},
+ {0x0000b02c, 0x02000302},
+ {0x0000b030, 0x02000303},
+ {0x0000b034, 0x02000304},
+ {0x0000b038, 0x02000400},
+ {0x0000b03c, 0x02010300},
+ {0x0000b040, 0x02010301},
+ {0x0000b044, 0x02010302},
+ {0x0000b048, 0x02000500},
+ {0x0000b04c, 0x02010400},
+ {0x0000b050, 0x02020300},
+ {0x0000b054, 0x02020301},
+ {0x0000b058, 0x02020302},
+ {0x0000b05c, 0x02020303},
+ {0x0000b060, 0x02020400},
+ {0x0000b064, 0x02030300},
+ {0x0000b068, 0x02030301},
+ {0x0000b06c, 0x02030302},
+ {0x0000b070, 0x02030303},
+ {0x0000b074, 0x02030400},
+ {0x0000b078, 0x02040300},
+ {0x0000b07c, 0x02040301},
+ {0x0000b080, 0x02040302},
+ {0x0000b084, 0x02040303},
+ {0x0000b088, 0x02030500},
+ {0x0000b08c, 0x02040400},
+ {0x0000b090, 0x02050203},
+ {0x0000b094, 0x02050204},
+ {0x0000b098, 0x02050205},
+ {0x0000b09c, 0x02040500},
+ {0x0000b0a0, 0x02050301},
+ {0x0000b0a4, 0x02050302},
+ {0x0000b0a8, 0x02050303},
+ {0x0000b0ac, 0x02050400},
+ {0x0000b0b0, 0x02050401},
+ {0x0000b0b4, 0x02050402},
+ {0x0000b0b8, 0x02050403},
+ {0x0000b0bc, 0x02050500},
+ {0x0000b0c0, 0x02050501},
+ {0x0000b0c4, 0x02050502},
+ {0x0000b0c8, 0x02050503},
+ {0x0000b0cc, 0x02050504},
+ {0x0000b0d0, 0x02050600},
+ {0x0000b0d4, 0x02050601},
+ {0x0000b0d8, 0x02050602},
+ {0x0000b0dc, 0x02050603},
+ {0x0000b0e0, 0x02050604},
+ {0x0000b0e4, 0x02050700},
+ {0x0000b0e8, 0x02050701},
+ {0x0000b0ec, 0x02050702},
+ {0x0000b0f0, 0x02050703},
+ {0x0000b0f4, 0x02050704},
+ {0x0000b0f8, 0x02050705},
+ {0x0000b0fc, 0x02050708},
+ {0x0000b100, 0x02050709},
+ {0x0000b104, 0x0205070a},
+ {0x0000b108, 0x0205070b},
+ {0x0000b10c, 0x0205070c},
+ {0x0000b110, 0x0205070d},
+ {0x0000b114, 0x02050710},
+ {0x0000b118, 0x02050711},
+ {0x0000b11c, 0x02050712},
+ {0x0000b120, 0x02050713},
+ {0x0000b124, 0x02050714},
+ {0x0000b128, 0x02050715},
+ {0x0000b12c, 0x02050730},
+ {0x0000b130, 0x02050731},
+ {0x0000b134, 0x02050732},
+ {0x0000b138, 0x02050733},
+ {0x0000b13c, 0x02050734},
+ {0x0000b140, 0x02050735},
+ {0x0000b144, 0x02050750},
+ {0x0000b148, 0x02050751},
+ {0x0000b14c, 0x02050752},
+ {0x0000b150, 0x02050753},
+ {0x0000b154, 0x02050754},
+ {0x0000b158, 0x02050755},
+ {0x0000b15c, 0x02050770},
+ {0x0000b160, 0x02050771},
+ {0x0000b164, 0x02050772},
+ {0x0000b168, 0x02050773},
+ {0x0000b16c, 0x02050774},
+ {0x0000b170, 0x02050775},
+ {0x0000b174, 0x00000776},
+ {0x0000b178, 0x00000776},
+ {0x0000b17c, 0x00000776},
+ {0x0000b180, 0x00000776},
+ {0x0000b184, 0x00000776},
+ {0x0000b188, 0x00000776},
+ {0x0000b18c, 0x00000776},
+ {0x0000b190, 0x00000776},
+ {0x0000b194, 0x00000776},
+ {0x0000b198, 0x00000776},
+ {0x0000b19c, 0x00000776},
+ {0x0000b1a0, 0x00000776},
+ {0x0000b1a4, 0x00000776},
+ {0x0000b1a8, 0x00000776},
+ {0x0000b1ac, 0x00000776},
+ {0x0000b1b0, 0x00000776},
+ {0x0000b1b4, 0x00000776},
+ {0x0000b1b8, 0x00000776},
+ {0x0000b1bc, 0x00000776},
+ {0x0000b1c0, 0x00000776},
+ {0x0000b1c4, 0x00000776},
+ {0x0000b1c8, 0x00000776},
+ {0x0000b1cc, 0x00000776},
+ {0x0000b1d0, 0x00000776},
+ {0x0000b1d4, 0x00000776},
+ {0x0000b1d8, 0x00000776},
+ {0x0000b1dc, 0x00000776},
+ {0x0000b1e0, 0x00000776},
+ {0x0000b1e4, 0x00000776},
+ {0x0000b1e8, 0x00000776},
+ {0x0000b1ec, 0x00000776},
+ {0x0000b1f0, 0x00000776},
+ {0x0000b1f4, 0x00000776},
+ {0x0000b1f8, 0x00000776},
+ {0x0000b1fc, 0x00000776},
+};
+
+static const u32 ar9200_ar9280_2p0_radio_core_1p0[][2] = {
+ /* Addr allmodes */
+ {0x00007800, 0x00040000},
+ {0x00007804, 0xdb005012},
+ {0x00007808, 0x04924914},
+ {0x0000780c, 0x21084210},
+ {0x00007810, 0x6d801300},
+ {0x00007814, 0x0019beff},
+ {0x00007818, 0x07e41000},
+ {0x0000781c, 0x00392000},
+ {0x00007820, 0x92592480},
+ {0x00007824, 0x00040000},
+ {0x00007828, 0xdb005012},
+ {0x0000782c, 0x04924914},
+ {0x00007830, 0x21084210},
+ {0x00007834, 0x6d801300},
+ {0x00007838, 0x0019beff},
+ {0x0000783c, 0x07e40000},
+ {0x00007840, 0x00392000},
+ {0x00007844, 0x92592480},
+ {0x00007848, 0x00100000},
+ {0x0000784c, 0x773f0567},
+ {0x00007850, 0x54214514},
+ {0x00007854, 0x12035828},
+ {0x00007858, 0x92592692},
+ {0x0000785c, 0x00000000},
+ {0x00007860, 0x56400000},
+ {0x00007864, 0x0a8e370e},
+ {0x00007868, 0xc0102850},
+ {0x0000786c, 0x812d4000},
+ {0x00007870, 0x807ec400},
+ {0x00007874, 0x001b6db0},
+ {0x00007878, 0x00376b63},
+ {0x0000787c, 0x06db6db6},
+ {0x00007880, 0x006d8000},
+ {0x00007884, 0xffeffffe},
+ {0x00007888, 0xffeffffe},
+ {0x0000788c, 0x00010000},
+ {0x00007890, 0x02060aeb},
+ {0x00007894, 0x5a108000},
+};
+
+static const u32 ar9462_1p0_baseband_postamble_emulation[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009e3c, 0xcf946221, 0xcf946221, 0xcf946221, 0xcf946221},
+ {0x00009e44, 0x005c0000, 0x005c0000, 0x005c0000, 0x005c0000},
+ {0x0000a258, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
+ {0x0000a25c, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x0000a28c, 0x00011111, 0x00011111, 0x00011111, 0x00011111},
+ {0x0000a2c4, 0x00148d18, 0x00148d18, 0x00148d20, 0x00148d20},
+ {0x0000a2d8, 0xf999a800, 0xf999a800, 0xf999a80c, 0xf999a80c},
+ {0x0000a50c, 0x0000c00a, 0x0000c00a, 0x0000c00a, 0x0000c00a},
+ {0x0000a538, 0x00038e8c, 0x00038e8c, 0x00038e8c, 0x00038e8c},
+ {0x0000a53c, 0x0003cecc, 0x0003cecc, 0x0003cecc, 0x0003cecc},
+ {0x0000a540, 0x00040ed4, 0x00040ed4, 0x00040ed4, 0x00040ed4},
+ {0x0000a544, 0x00044edc, 0x00044edc, 0x00044edc, 0x00044edc},
+ {0x0000a548, 0x00048ede, 0x00048ede, 0x00048ede, 0x00048ede},
+ {0x0000a54c, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e},
+ {0x0000a550, 0x00050f5e, 0x00050f5e, 0x00050f5e, 0x00050f5e},
+ {0x0000a554, 0x00054f9e, 0x00054f9e, 0x00054f9e, 0x00054f9e},
+ {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+};
+
+static const u32 ar9462_pcie_phy_pll_on_clkreq_disable_L1_1p0[][2] = {
+ /* Addr allmodes */
+ {0x00018c00, 0x10012e5e},
+ {0x00018c04, 0x000801d8},
+ {0x00018c08, 0x0000580c},
+};
+
+static const u32 ar9462_common_rx_gain_table_1p0[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x01910190},
+ {0x0000a030, 0x01930192},
+ {0x0000a034, 0x01950194},
+ {0x0000a038, 0x038a0196},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x22222229},
+ {0x0000a084, 0x1d1d1d1d},
+ {0x0000a088, 0x1d1d1d1d},
+ {0x0000a08c, 0x1d1d1d1d},
+ {0x0000a090, 0x171d1d1d},
+ {0x0000a094, 0x11111717},
+ {0x0000a098, 0x00030311},
+ {0x0000a09c, 0x00000000},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+ {0x0000b000, 0x00010000},
+ {0x0000b004, 0x00030002},
+ {0x0000b008, 0x00050004},
+ {0x0000b00c, 0x00810080},
+ {0x0000b010, 0x00830082},
+ {0x0000b014, 0x01810180},
+ {0x0000b018, 0x01830182},
+ {0x0000b01c, 0x01850184},
+ {0x0000b020, 0x02810280},
+ {0x0000b024, 0x02830282},
+ {0x0000b028, 0x02850284},
+ {0x0000b02c, 0x02890288},
+ {0x0000b030, 0x028b028a},
+ {0x0000b034, 0x0388028c},
+ {0x0000b038, 0x038a0389},
+ {0x0000b03c, 0x038c038b},
+ {0x0000b040, 0x0390038d},
+ {0x0000b044, 0x03920391},
+ {0x0000b048, 0x03940393},
+ {0x0000b04c, 0x03960395},
+ {0x0000b050, 0x00000000},
+ {0x0000b054, 0x00000000},
+ {0x0000b058, 0x00000000},
+ {0x0000b05c, 0x00000000},
+ {0x0000b060, 0x00000000},
+ {0x0000b064, 0x00000000},
+ {0x0000b068, 0x00000000},
+ {0x0000b06c, 0x00000000},
+ {0x0000b070, 0x00000000},
+ {0x0000b074, 0x00000000},
+ {0x0000b078, 0x00000000},
+ {0x0000b07c, 0x00000000},
+ {0x0000b080, 0x2a2d2f32},
+ {0x0000b084, 0x21232328},
+ {0x0000b088, 0x19191c1e},
+ {0x0000b08c, 0x12141417},
+ {0x0000b090, 0x07070e0e},
+ {0x0000b094, 0x03030305},
+ {0x0000b098, 0x00000003},
+ {0x0000b09c, 0x00000000},
+ {0x0000b0a0, 0x00000000},
+ {0x0000b0a4, 0x00000000},
+ {0x0000b0a8, 0x00000000},
+ {0x0000b0ac, 0x00000000},
+ {0x0000b0b0, 0x00000000},
+ {0x0000b0b4, 0x00000000},
+ {0x0000b0b8, 0x00000000},
+ {0x0000b0bc, 0x00000000},
+ {0x0000b0c0, 0x003f0020},
+ {0x0000b0c4, 0x00400041},
+ {0x0000b0c8, 0x0140005f},
+ {0x0000b0cc, 0x0160015f},
+ {0x0000b0d0, 0x017e017f},
+ {0x0000b0d4, 0x02410242},
+ {0x0000b0d8, 0x025f0240},
+ {0x0000b0dc, 0x027f0260},
+ {0x0000b0e0, 0x0341027e},
+ {0x0000b0e4, 0x035f0340},
+ {0x0000b0e8, 0x037f0360},
+ {0x0000b0ec, 0x04400441},
+ {0x0000b0f0, 0x0460045f},
+ {0x0000b0f4, 0x0541047f},
+ {0x0000b0f8, 0x055f0540},
+ {0x0000b0fc, 0x057f0560},
+ {0x0000b100, 0x06400641},
+ {0x0000b104, 0x0660065f},
+ {0x0000b108, 0x067e067f},
+ {0x0000b10c, 0x07410742},
+ {0x0000b110, 0x075f0740},
+ {0x0000b114, 0x077f0760},
+ {0x0000b118, 0x07800781},
+ {0x0000b11c, 0x07a0079f},
+ {0x0000b120, 0x07c107bf},
+ {0x0000b124, 0x000007c0},
+ {0x0000b128, 0x00000000},
+ {0x0000b12c, 0x00000000},
+ {0x0000b130, 0x00000000},
+ {0x0000b134, 0x00000000},
+ {0x0000b138, 0x00000000},
+ {0x0000b13c, 0x00000000},
+ {0x0000b140, 0x003f0020},
+ {0x0000b144, 0x00400041},
+ {0x0000b148, 0x0140005f},
+ {0x0000b14c, 0x0160015f},
+ {0x0000b150, 0x017e017f},
+ {0x0000b154, 0x02410242},
+ {0x0000b158, 0x025f0240},
+ {0x0000b15c, 0x027f0260},
+ {0x0000b160, 0x0341027e},
+ {0x0000b164, 0x035f0340},
+ {0x0000b168, 0x037f0360},
+ {0x0000b16c, 0x04400441},
+ {0x0000b170, 0x0460045f},
+ {0x0000b174, 0x0541047f},
+ {0x0000b178, 0x055f0540},
+ {0x0000b17c, 0x057f0560},
+ {0x0000b180, 0x06400641},
+ {0x0000b184, 0x0660065f},
+ {0x0000b188, 0x067e067f},
+ {0x0000b18c, 0x07410742},
+ {0x0000b190, 0x075f0740},
+ {0x0000b194, 0x077f0760},
+ {0x0000b198, 0x07800781},
+ {0x0000b19c, 0x07a0079f},
+ {0x0000b1a0, 0x07c107bf},
+ {0x0000b1a4, 0x000007c0},
+ {0x0000b1a8, 0x00000000},
+ {0x0000b1ac, 0x00000000},
+ {0x0000b1b0, 0x00000000},
+ {0x0000b1b4, 0x00000000},
+ {0x0000b1b8, 0x00000000},
+ {0x0000b1bc, 0x00000000},
+ {0x0000b1c0, 0x00000000},
+ {0x0000b1c4, 0x00000000},
+ {0x0000b1c8, 0x00000000},
+ {0x0000b1cc, 0x00000000},
+ {0x0000b1d0, 0x00000000},
+ {0x0000b1d4, 0x00000000},
+ {0x0000b1d8, 0x00000000},
+ {0x0000b1dc, 0x00000000},
+ {0x0000b1e0, 0x00000000},
+ {0x0000b1e4, 0x00000000},
+ {0x0000b1e8, 0x00000000},
+ {0x0000b1ec, 0x00000000},
+ {0x0000b1f0, 0x00000396},
+ {0x0000b1f4, 0x00000396},
+ {0x0000b1f8, 0x00000396},
+ {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_modes_high_ob_db_tx_gain_table_1p0[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+ {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
+ {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400},
+ {0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402},
+ {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404},
+ {0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603},
+ {0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02},
+ {0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04},
+ {0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20},
+ {0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20},
+ {0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22},
+ {0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24},
+ {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640},
+ {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660},
+ {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861},
+ {0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81},
+ {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83},
+ {0x0000a550, 0x5f025ef6, 0x5f025ef6, 0x44001c84, 0x44001c84},
+ {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3},
+ {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9},
+ {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb},
+ {0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
+ {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
+ {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x00016044, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4},
+ {0x00016048, 0x8db49060, 0x8db49060, 0x8db49060, 0x8db49060},
+ {0x00016444, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4},
+ {0x00016448, 0x8db49000, 0x8db49000, 0x8db49000, 0x8db49000},
+};
+
+static const u32 ar9462_common_wo_xlna_rx_gain_table_1p0[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x03820190},
+ {0x0000a030, 0x03840383},
+ {0x0000a034, 0x03880385},
+ {0x0000a038, 0x038a0389},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x29292929},
+ {0x0000a084, 0x29292929},
+ {0x0000a088, 0x29292929},
+ {0x0000a08c, 0x29292929},
+ {0x0000a090, 0x22292929},
+ {0x0000a094, 0x1d1d2222},
+ {0x0000a098, 0x0c111117},
+ {0x0000a09c, 0x00030303},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+ {0x0000b000, 0x00010000},
+ {0x0000b004, 0x00030002},
+ {0x0000b008, 0x00050004},
+ {0x0000b00c, 0x00810080},
+ {0x0000b010, 0x00830082},
+ {0x0000b014, 0x01810180},
+ {0x0000b018, 0x01830182},
+ {0x0000b01c, 0x01850184},
+ {0x0000b020, 0x02810280},
+ {0x0000b024, 0x02830282},
+ {0x0000b028, 0x02850284},
+ {0x0000b02c, 0x02890288},
+ {0x0000b030, 0x028b028a},
+ {0x0000b034, 0x0388028c},
+ {0x0000b038, 0x038a0389},
+ {0x0000b03c, 0x038c038b},
+ {0x0000b040, 0x0390038d},
+ {0x0000b044, 0x03920391},
+ {0x0000b048, 0x03940393},
+ {0x0000b04c, 0x03960395},
+ {0x0000b050, 0x00000000},
+ {0x0000b054, 0x00000000},
+ {0x0000b058, 0x00000000},
+ {0x0000b05c, 0x00000000},
+ {0x0000b060, 0x00000000},
+ {0x0000b064, 0x00000000},
+ {0x0000b068, 0x00000000},
+ {0x0000b06c, 0x00000000},
+ {0x0000b070, 0x00000000},
+ {0x0000b074, 0x00000000},
+ {0x0000b078, 0x00000000},
+ {0x0000b07c, 0x00000000},
+ {0x0000b080, 0x32323232},
+ {0x0000b084, 0x2f2f3232},
+ {0x0000b088, 0x23282a2d},
+ {0x0000b08c, 0x1c1e2123},
+ {0x0000b090, 0x14171919},
+ {0x0000b094, 0x0e0e1214},
+ {0x0000b098, 0x03050707},
+ {0x0000b09c, 0x00030303},
+ {0x0000b0a0, 0x00000000},
+ {0x0000b0a4, 0x00000000},
+ {0x0000b0a8, 0x00000000},
+ {0x0000b0ac, 0x00000000},
+ {0x0000b0b0, 0x00000000},
+ {0x0000b0b4, 0x00000000},
+ {0x0000b0b8, 0x00000000},
+ {0x0000b0bc, 0x00000000},
+ {0x0000b0c0, 0x003f0020},
+ {0x0000b0c4, 0x00400041},
+ {0x0000b0c8, 0x0140005f},
+ {0x0000b0cc, 0x0160015f},
+ {0x0000b0d0, 0x017e017f},
+ {0x0000b0d4, 0x02410242},
+ {0x0000b0d8, 0x025f0240},
+ {0x0000b0dc, 0x027f0260},
+ {0x0000b0e0, 0x0341027e},
+ {0x0000b0e4, 0x035f0340},
+ {0x0000b0e8, 0x037f0360},
+ {0x0000b0ec, 0x04400441},
+ {0x0000b0f0, 0x0460045f},
+ {0x0000b0f4, 0x0541047f},
+ {0x0000b0f8, 0x055f0540},
+ {0x0000b0fc, 0x057f0560},
+ {0x0000b100, 0x06400641},
+ {0x0000b104, 0x0660065f},
+ {0x0000b108, 0x067e067f},
+ {0x0000b10c, 0x07410742},
+ {0x0000b110, 0x075f0740},
+ {0x0000b114, 0x077f0760},
+ {0x0000b118, 0x07800781},
+ {0x0000b11c, 0x07a0079f},
+ {0x0000b120, 0x07c107bf},
+ {0x0000b124, 0x000007c0},
+ {0x0000b128, 0x00000000},
+ {0x0000b12c, 0x00000000},
+ {0x0000b130, 0x00000000},
+ {0x0000b134, 0x00000000},
+ {0x0000b138, 0x00000000},
+ {0x0000b13c, 0x00000000},
+ {0x0000b140, 0x003f0020},
+ {0x0000b144, 0x00400041},
+ {0x0000b148, 0x0140005f},
+ {0x0000b14c, 0x0160015f},
+ {0x0000b150, 0x017e017f},
+ {0x0000b154, 0x02410242},
+ {0x0000b158, 0x025f0240},
+ {0x0000b15c, 0x027f0260},
+ {0x0000b160, 0x0341027e},
+ {0x0000b164, 0x035f0340},
+ {0x0000b168, 0x037f0360},
+ {0x0000b16c, 0x04400441},
+ {0x0000b170, 0x0460045f},
+ {0x0000b174, 0x0541047f},
+ {0x0000b178, 0x055f0540},
+ {0x0000b17c, 0x057f0560},
+ {0x0000b180, 0x06400641},
+ {0x0000b184, 0x0660065f},
+ {0x0000b188, 0x067e067f},
+ {0x0000b18c, 0x07410742},
+ {0x0000b190, 0x075f0740},
+ {0x0000b194, 0x077f0760},
+ {0x0000b198, 0x07800781},
+ {0x0000b19c, 0x07a0079f},
+ {0x0000b1a0, 0x07c107bf},
+ {0x0000b1a4, 0x000007c0},
+ {0x0000b1a8, 0x00000000},
+ {0x0000b1ac, 0x00000000},
+ {0x0000b1b0, 0x00000000},
+ {0x0000b1b4, 0x00000000},
+ {0x0000b1b8, 0x00000000},
+ {0x0000b1bc, 0x00000000},
+ {0x0000b1c0, 0x00000000},
+ {0x0000b1c4, 0x00000000},
+ {0x0000b1c8, 0x00000000},
+ {0x0000b1cc, 0x00000000},
+ {0x0000b1d0, 0x00000000},
+ {0x0000b1d4, 0x00000000},
+ {0x0000b1d8, 0x00000000},
+ {0x0000b1dc, 0x00000000},
+ {0x0000b1e0, 0x00000000},
+ {0x0000b1e4, 0x00000000},
+ {0x0000b1e8, 0x00000000},
+ {0x0000b1ec, 0x00000000},
+ {0x0000b1f0, 0x00000396},
+ {0x0000b1f4, 0x00000396},
+ {0x0000b1f8, 0x00000396},
+ {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_1p0_mac_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b},
+ {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+ {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+ {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
+};
+
+static const u32 ar9462_1p0_mac_postamble_emulation[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00008014, 0x10f810f8, 0x10f810f8, 0x10f810f8, 0x10f810f8},
+ {0x0000801c, 0x0e8d8017, 0x0e8d8017, 0x0e8d8017, 0x0e8d8017},
+};
+
+static const u32 ar9462_1p0_tx_gain_table_baseband_postamble_emulation[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a410, 0x000000d5, 0x000000d5, 0x000000d5, 0x000000d5},
+ {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a504, 0x00004002, 0x00004002, 0x00004002, 0x00004002},
+ {0x0000a508, 0x00008004, 0x00008004, 0x00008004, 0x00008004},
+ {0x0000a510, 0x0001000c, 0x0001000c, 0x0001000c, 0x0001000c},
+ {0x0000a514, 0x0001420b, 0x0001420b, 0x0001420b, 0x0001420b},
+ {0x0000a518, 0x0001824a, 0x0001824a, 0x0001824a, 0x0001824a},
+ {0x0000a51c, 0x0001c44a, 0x0001c44a, 0x0001c44a, 0x0001c44a},
+ {0x0000a520, 0x0002064a, 0x0002064a, 0x0002064a, 0x0002064a},
+ {0x0000a524, 0x0002484a, 0x0002484a, 0x0002484a, 0x0002484a},
+ {0x0000a528, 0x00028a4a, 0x00028a4a, 0x00028a4a, 0x00028a4a},
+ {0x0000a52c, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a},
+ {0x0000a530, 0x00030e4a, 0x00030e4a, 0x00030e4a, 0x00030e4a},
+ {0x0000a534, 0x00034e8a, 0x00034e8a, 0x00034e8a, 0x00034e8a},
+};
+
+static const u32 ar9462_1p0_radio_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0001609c, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524},
+ {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24646c08, 0x24646c08},
+ {0x000160b0, 0x01d67f70, 0x01d67f70, 0x01d67f70, 0x01d67f70},
+ {0x0001610c, 0x48000000, 0x40000000, 0x40000000, 0x40000000},
+ {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008},
+ {0x0001650c, 0x48000000, 0x40000000, 0x40000000, 0x40000000},
+ {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008},
+};
+
+static const u32 ar9462_1p0_soc_postamble_emulation[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00007010, 0x00001133, 0x00001133, 0x00001133, 0x00001133},
+};
+
+static const u32 ar9462_1p0_baseband_core[][2] = {
+ /* Addr allmodes */
+ {0x00009800, 0xafe68e30},
+ {0x00009804, 0xfd14e000},
+ {0x00009808, 0x9c0a9f6b},
+ {0x0000980c, 0x04900000},
+ {0x00009814, 0x9280c00a},
+ {0x00009818, 0x00000000},
+ {0x0000981c, 0x00020028},
+ {0x00009834, 0x6400a290},
+ {0x00009838, 0x0108ecff},
+ {0x0000983c, 0x0d000600},
+ {0x00009880, 0x201fff00},
+ {0x00009884, 0x00001042},
+ {0x000098a4, 0x00200400},
+ {0x000098b0, 0x32840bbe},
+ {0x000098d0, 0x004b6a8e},
+ {0x000098d4, 0x00000820},
+ {0x000098dc, 0x00000000},
+ {0x000098e4, 0x01ffffff},
+ {0x000098e8, 0x01ffffff},
+ {0x000098ec, 0x01ffffff},
+ {0x000098f0, 0x00000000},
+ {0x000098f4, 0x00000000},
+ {0x00009c04, 0xff55ff55},
+ {0x00009c08, 0x0320ff55},
+ {0x00009c0c, 0x00000000},
+ {0x00009c10, 0x00000000},
+ {0x00009c14, 0x00046384},
+ {0x00009c18, 0x05b6b440},
+ {0x00009c1c, 0x00b6b440},
+ {0x00009d00, 0xc080a333},
+ {0x00009d04, 0x40206c10},
+ {0x00009d08, 0x009c4060},
+ {0x00009d0c, 0x9883800a},
+ {0x00009d10, 0x01834061},
+ {0x00009d14, 0x00c0040b},
+ {0x00009d18, 0x00000000},
+ {0x00009e08, 0x0038230c},
+ {0x00009e24, 0x990bb514},
+ {0x00009e28, 0x0c6f0000},
+ {0x00009e30, 0x06336f77},
+ {0x00009e34, 0x6af6532f},
+ {0x00009e38, 0x0cc80c00},
+ {0x00009e40, 0x0d261820},
+ {0x00009e4c, 0x00001004},
+ {0x00009e50, 0x00ff03f1},
+ {0x00009e54, 0x64c355c7},
+ {0x00009e58, 0xfd897735},
+ {0x00009e5c, 0xe9198724},
+ {0x00009fc0, 0x803e4788},
+ {0x00009fc4, 0x0001efb5},
+ {0x00009fcc, 0x40000014},
+ {0x00009fd0, 0x01193b93},
+ {0x0000a20c, 0x00000000},
+ {0x0000a220, 0x00000000},
+ {0x0000a224, 0x00000000},
+ {0x0000a228, 0x10002310},
+ {0x0000a23c, 0x00000000},
+ {0x0000a244, 0x0c000000},
+ {0x0000a2a0, 0x00000001},
+ {0x0000a2c0, 0x00000001},
+ {0x0000a2c8, 0x00000000},
+ {0x0000a2cc, 0x18c43433},
+ {0x0000a2d4, 0x00000000},
+ {0x0000a2ec, 0x00000000},
+ {0x0000a2f0, 0x00000000},
+ {0x0000a2f4, 0x00000000},
+ {0x0000a2f8, 0x00000000},
+ {0x0000a344, 0x00000000},
+ {0x0000a34c, 0x00000000},
+ {0x0000a350, 0x0000a000},
+ {0x0000a364, 0x00000000},
+ {0x0000a370, 0x00000000},
+ {0x0000a390, 0x00000001},
+ {0x0000a394, 0x00000444},
+ {0x0000a398, 0x001f0e0f},
+ {0x0000a39c, 0x0075393f},
+ {0x0000a3a0, 0xb79f6427},
+ {0x0000a3a4, 0x00000000},
+ {0x0000a3a8, 0xaaaaaaaa},
+ {0x0000a3ac, 0x3c466478},
+ {0x0000a3c0, 0x20202020},
+ {0x0000a3c4, 0x22222220},
+ {0x0000a3c8, 0x20200020},
+ {0x0000a3cc, 0x20202020},
+ {0x0000a3d0, 0x20202020},
+ {0x0000a3d4, 0x20202020},
+ {0x0000a3d8, 0x20202020},
+ {0x0000a3dc, 0x20202020},
+ {0x0000a3e0, 0x20202020},
+ {0x0000a3e4, 0x20202020},
+ {0x0000a3e8, 0x20202020},
+ {0x0000a3ec, 0x20202020},
+ {0x0000a3f0, 0x00000000},
+ {0x0000a3f4, 0x00000006},
+ {0x0000a3f8, 0x0c9bd380},
+ {0x0000a3fc, 0x000f0f01},
+ {0x0000a400, 0x8fa91f01},
+ {0x0000a404, 0x00000000},
+ {0x0000a408, 0x0e79e5c6},
+ {0x0000a40c, 0x00820820},
+ {0x0000a414, 0x1ce739ce},
+ {0x0000a418, 0x2d001dce},
+ {0x0000a41c, 0x1ce739ce},
+ {0x0000a420, 0x000001ce},
+ {0x0000a424, 0x1ce739ce},
+ {0x0000a428, 0x000001ce},
+ {0x0000a42c, 0x1ce739ce},
+ {0x0000a430, 0x1ce739ce},
+ {0x0000a434, 0x00000000},
+ {0x0000a438, 0x00001801},
+ {0x0000a43c, 0x00100000},
+ {0x0000a440, 0x00000000},
+ {0x0000a444, 0x00000000},
+ {0x0000a448, 0x05000080},
+ {0x0000a44c, 0x00000001},
+ {0x0000a450, 0x00010000},
+ {0x0000a458, 0x00000000},
+ {0x0000a644, 0xbfad9d74},
+ {0x0000a648, 0x0048060a},
+ {0x0000a64c, 0x00003c37},
+ {0x0000a670, 0x03020100},
+ {0x0000a674, 0x09080504},
+ {0x0000a678, 0x0d0c0b0a},
+ {0x0000a67c, 0x13121110},
+ {0x0000a680, 0x31301514},
+ {0x0000a684, 0x35343332},
+ {0x0000a688, 0x00000036},
+ {0x0000a690, 0x00000838},
+ {0x0000a6b0, 0x0000000a},
+ {0x0000a6b4, 0x28f12c01},
+ {0x0000a7c0, 0x00000000},
+ {0x0000a7c4, 0xfffffffc},
+ {0x0000a7c8, 0x00000000},
+ {0x0000a7cc, 0x00000000},
+ {0x0000a7d0, 0x00000000},
+ {0x0000a7d4, 0x00000004},
+ {0x0000a7dc, 0x00000001},
+ {0x0000a8d0, 0x004b6a8e},
+ {0x0000a8d4, 0x00000820},
+ {0x0000a8dc, 0x00000000},
+ {0x0000a8f0, 0x00000000},
+ {0x0000a8f4, 0x00000000},
+ {0x0000b2d0, 0x00000080},
+ {0x0000b2d4, 0x00000000},
+ {0x0000b2ec, 0x00000000},
+ {0x0000b2f0, 0x00000000},
+ {0x0000b2f4, 0x00000000},
+ {0x0000b2f8, 0x00000000},
+ {0x0000b408, 0x0e79e5c0},
+ {0x0000b40c, 0x00820820},
+ {0x0000b420, 0x00000000},
+ {0x0000b6b0, 0x0000000a},
+ {0x0000b6b4, 0x00c00001},
+};
+
+static const u32 ar9462_1p0_baseband_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011},
+ {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e},
+ {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+ {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+ {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+ {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
+ {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
+ {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0},
+ {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
+ {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
+ {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e},
+ {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e},
+ {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
+ {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+ {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
+ {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c782},
+ {0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27},
+ {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+ {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
+ {0x0000a204, 0x0131b7c0, 0x0131b7c4, 0x0131b7c4, 0x0131b7c0},
+ {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
+ {0x0000a22c, 0x01026a2f, 0x01026a27, 0x01026a2f, 0x01026a2f},
+ {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b},
+ {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff},
+ {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018},
+ {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
+ {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
+ {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501},
+ {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
+ {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
+ {0x0000a288, 0x00000110, 0x00000110, 0x00100110, 0x00100110},
+ {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
+ {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+ {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
+ {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
+ {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+ {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000},
+ {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+ {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce},
+ {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550},
+};
+
+static const u32 ar9462_modes_fast_clock_1p0[][3] = {
+ /* Addr 5G_HT20 5G_HT40 */
+ {0x00001030, 0x00000268, 0x000004d0},
+ {0x00001070, 0x0000018c, 0x00000318},
+ {0x000010b0, 0x00000fd0, 0x00001fa0},
+ {0x00008014, 0x044c044c, 0x08980898},
+ {0x0000801c, 0x148ec02b, 0x148ec057},
+ {0x00008318, 0x000044c0, 0x00008980},
+ {0x00009e00, 0x0372131c, 0x0372131c},
+ {0x0000a230, 0x0000400b, 0x00004016},
+ {0x0000a254, 0x00000898, 0x00001130},
+};
+
+static const u32 ar9462_modes_low_ob_db_tx_gain_table_1p0[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+ {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400},
+ {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402},
+ {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404},
+ {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603},
+ {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02},
+ {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04},
+ {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20},
+ {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20},
+ {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22},
+ {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24},
+ {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640},
+ {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660},
+ {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861},
+ {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81},
+ {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83},
+ {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84},
+ {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3},
+ {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5},
+ {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9},
+ {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb},
+ {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501},
+ {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005},
+ {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x00016044, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4},
+ {0x00016048, 0x64992060, 0x64992060, 0x64992060, 0x64992060},
+ {0x00016444, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4},
+ {0x00016448, 0x64992000, 0x64992000, 0x64992000, 0x64992000},
+};
+
+static const u32 ar9462_1p0_soc_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00007010, 0x00002233, 0x00002233, 0x00002233, 0x00002233},
+};
+
+static const u32 ar9462_common_mixed_rx_gain_table_1p0[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x03820190},
+ {0x0000a030, 0x03840383},
+ {0x0000a034, 0x03880385},
+ {0x0000a038, 0x038a0389},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x29292929},
+ {0x0000a084, 0x29292929},
+ {0x0000a088, 0x29292929},
+ {0x0000a08c, 0x29292929},
+ {0x0000a090, 0x22292929},
+ {0x0000a094, 0x1d1d2222},
+ {0x0000a098, 0x0c111117},
+ {0x0000a09c, 0x00030303},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+ {0x0000b000, 0x00010000},
+ {0x0000b004, 0x00030002},
+ {0x0000b008, 0x00050004},
+ {0x0000b00c, 0x00810080},
+ {0x0000b010, 0x00830082},
+ {0x0000b014, 0x01810180},
+ {0x0000b018, 0x01830182},
+ {0x0000b01c, 0x01850184},
+ {0x0000b020, 0x02810280},
+ {0x0000b024, 0x02830282},
+ {0x0000b028, 0x02850284},
+ {0x0000b02c, 0x02890288},
+ {0x0000b030, 0x028b028a},
+ {0x0000b034, 0x0388028c},
+ {0x0000b038, 0x038a0389},
+ {0x0000b03c, 0x038c038b},
+ {0x0000b040, 0x0390038d},
+ {0x0000b044, 0x03920391},
+ {0x0000b048, 0x03940393},
+ {0x0000b04c, 0x03960395},
+ {0x0000b050, 0x00000000},
+ {0x0000b054, 0x00000000},
+ {0x0000b058, 0x00000000},
+ {0x0000b05c, 0x00000000},
+ {0x0000b060, 0x00000000},
+ {0x0000b064, 0x00000000},
+ {0x0000b068, 0x00000000},
+ {0x0000b06c, 0x00000000},
+ {0x0000b070, 0x00000000},
+ {0x0000b074, 0x00000000},
+ {0x0000b078, 0x00000000},
+ {0x0000b07c, 0x00000000},
+ {0x0000b080, 0x2a2d2f32},
+ {0x0000b084, 0x21232328},
+ {0x0000b088, 0x19191c1e},
+ {0x0000b08c, 0x12141417},
+ {0x0000b090, 0x07070e0e},
+ {0x0000b094, 0x03030305},
+ {0x0000b098, 0x00000003},
+ {0x0000b09c, 0x00000000},
+ {0x0000b0a0, 0x00000000},
+ {0x0000b0a4, 0x00000000},
+ {0x0000b0a8, 0x00000000},
+ {0x0000b0ac, 0x00000000},
+ {0x0000b0b0, 0x00000000},
+ {0x0000b0b4, 0x00000000},
+ {0x0000b0b8, 0x00000000},
+ {0x0000b0bc, 0x00000000},
+ {0x0000b0c0, 0x003f0020},
+ {0x0000b0c4, 0x00400041},
+ {0x0000b0c8, 0x0140005f},
+ {0x0000b0cc, 0x0160015f},
+ {0x0000b0d0, 0x017e017f},
+ {0x0000b0d4, 0x02410242},
+ {0x0000b0d8, 0x025f0240},
+ {0x0000b0dc, 0x027f0260},
+ {0x0000b0e0, 0x0341027e},
+ {0x0000b0e4, 0x035f0340},
+ {0x0000b0e8, 0x037f0360},
+ {0x0000b0ec, 0x04400441},
+ {0x0000b0f0, 0x0460045f},
+ {0x0000b0f4, 0x0541047f},
+ {0x0000b0f8, 0x055f0540},
+ {0x0000b0fc, 0x057f0560},
+ {0x0000b100, 0x06400641},
+ {0x0000b104, 0x0660065f},
+ {0x0000b108, 0x067e067f},
+ {0x0000b10c, 0x07410742},
+ {0x0000b110, 0x075f0740},
+ {0x0000b114, 0x077f0760},
+ {0x0000b118, 0x07800781},
+ {0x0000b11c, 0x07a0079f},
+ {0x0000b120, 0x07c107bf},
+ {0x0000b124, 0x000007c0},
+ {0x0000b128, 0x00000000},
+ {0x0000b12c, 0x00000000},
+ {0x0000b130, 0x00000000},
+ {0x0000b134, 0x00000000},
+ {0x0000b138, 0x00000000},
+ {0x0000b13c, 0x00000000},
+ {0x0000b140, 0x003f0020},
+ {0x0000b144, 0x00400041},
+ {0x0000b148, 0x0140005f},
+ {0x0000b14c, 0x0160015f},
+ {0x0000b150, 0x017e017f},
+ {0x0000b154, 0x02410242},
+ {0x0000b158, 0x025f0240},
+ {0x0000b15c, 0x027f0260},
+ {0x0000b160, 0x0341027e},
+ {0x0000b164, 0x035f0340},
+ {0x0000b168, 0x037f0360},
+ {0x0000b16c, 0x04400441},
+ {0x0000b170, 0x0460045f},
+ {0x0000b174, 0x0541047f},
+ {0x0000b178, 0x055f0540},
+ {0x0000b17c, 0x057f0560},
+ {0x0000b180, 0x06400641},
+ {0x0000b184, 0x0660065f},
+ {0x0000b188, 0x067e067f},
+ {0x0000b18c, 0x07410742},
+ {0x0000b190, 0x075f0740},
+ {0x0000b194, 0x077f0760},
+ {0x0000b198, 0x07800781},
+ {0x0000b19c, 0x07a0079f},
+ {0x0000b1a0, 0x07c107bf},
+ {0x0000b1a4, 0x000007c0},
+ {0x0000b1a8, 0x00000000},
+ {0x0000b1ac, 0x00000000},
+ {0x0000b1b0, 0x00000000},
+ {0x0000b1b4, 0x00000000},
+ {0x0000b1b8, 0x00000000},
+ {0x0000b1bc, 0x00000000},
+ {0x0000b1c0, 0x00000000},
+ {0x0000b1c4, 0x00000000},
+ {0x0000b1c8, 0x00000000},
+ {0x0000b1cc, 0x00000000},
+ {0x0000b1d0, 0x00000000},
+ {0x0000b1d4, 0x00000000},
+ {0x0000b1d8, 0x00000000},
+ {0x0000b1dc, 0x00000000},
+ {0x0000b1e0, 0x00000000},
+ {0x0000b1e4, 0x00000000},
+ {0x0000b1e8, 0x00000000},
+ {0x0000b1ec, 0x00000000},
+ {0x0000b1f0, 0x00000396},
+ {0x0000b1f4, 0x00000396},
+ {0x0000b1f8, 0x00000396},
+ {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_pcie_phy_clkreq_disable_L1_1p0[][2] = {
+ /* Addr allmodes */
+ {0x00018c00, 0x10013e5e},
+ {0x00018c04, 0x000801d8},
+ {0x00018c08, 0x0000580c},
+};
+
+static const u32 ar9462_1p0_baseband_core_emulation[][2] = {
+ /* Addr allmodes */
+ {0x00009800, 0xafa68e30},
+ {0x00009884, 0x00002842},
+ {0x00009c04, 0xff55ff55},
+ {0x00009c08, 0x0320ff55},
+ {0x00009e50, 0x00000000},
+ {0x00009fcc, 0x00000014},
+ {0x0000a344, 0x00000010},
+ {0x0000a398, 0x00000000},
+ {0x0000a39c, 0x71733d01},
+ {0x0000a3a0, 0xd0ad5c12},
+ {0x0000a3c0, 0x22222220},
+ {0x0000a3c4, 0x22222222},
+ {0x0000a404, 0x00418a11},
+ {0x0000a418, 0x050001ce},
+ {0x0000a438, 0x00001800},
+ {0x0000a458, 0x01444452},
+ {0x0000a644, 0x3fad9d74},
+ {0x0000a690, 0x00000038},
+};
+
+static const u32 ar9462_1p0_radio_core[][2] = {
+ /* Addr allmodes */
+ {0x00016000, 0x36db6db6},
+ {0x00016004, 0x6db6db40},
+ {0x00016008, 0x73f00000},
+ {0x0001600c, 0x00000000},
+ {0x00016010, 0x6d820001},
+ {0x00016040, 0x7f80fff8},
+ {0x0001604c, 0x2699e04f},
+ {0x00016050, 0x6db6db6c},
+ {0x00016054, 0x6db60000},
+ {0x00016058, 0x6c200000},
+ {0x00016080, 0x00040000},
+ {0x00016084, 0x9a68048c},
+ {0x00016088, 0x54214514},
+ {0x0001608c, 0x12030409},
+ {0x00016090, 0x24926490},
+ {0x00016098, 0xd2888888},
+ {0x000160a0, 0x0a108ffe},
+ {0x000160a4, 0x812fc490},
+ {0x000160a8, 0x423c8000},
+ {0x000160b4, 0x92000000},
+ {0x000160b8, 0x0285dddc},
+ {0x000160bc, 0x02908888},
+ {0x000160c0, 0x00adb6d0},
+ {0x000160c4, 0x6db6db60},
+ {0x000160c8, 0x6db6db6c},
+ {0x000160cc, 0x0de6c1b0},
+ {0x00016100, 0x3fffbe04},
+ {0x00016104, 0xfff80000},
+ {0x00016108, 0x00200400},
+ {0x00016110, 0x00000000},
+ {0x00016144, 0x02084080},
+ {0x00016148, 0x000080c0},
+ {0x00016280, 0x050a0001},
+ {0x00016284, 0x3d841400},
+ {0x00016288, 0x00000000},
+ {0x0001628c, 0xe3000000},
+ {0x00016290, 0xa1005080},
+ {0x00016294, 0x00000020},
+ {0x00016298, 0x50a02900},
+ {0x00016340, 0x121e4276},
+ {0x00016344, 0x00300000},
+ {0x00016400, 0x36db6db6},
+ {0x00016404, 0x6db6db40},
+ {0x00016408, 0x73f00000},
+ {0x0001640c, 0x00000000},
+ {0x00016410, 0x6c800001},
+ {0x00016440, 0x7f80fff8},
+ {0x0001644c, 0x4699e04f},
+ {0x00016450, 0x6db6db6c},
+ {0x00016454, 0x6db60000},
+ {0x00016500, 0x3fffbe04},
+ {0x00016504, 0xfff80000},
+ {0x00016508, 0x00200400},
+ {0x00016510, 0x00000000},
+ {0x00016544, 0x02084080},
+ {0x00016548, 0x000080c0},
+};
+
+static const u32 ar9462_1p0_soc_preamble[][2] = {
+ /* Addr allmodes */
+ {0x00007020, 0x00000000},
+ {0x00007034, 0x00000002},
+ {0x00007038, 0x000004c2},
+};
+
+static const u32 ar9462_1p0_sys2ant[][2] = {
+ /* Addr allmodes */
+ {0x00063120, 0x00801980},
+};
+
+#endif /* INITVALS_9462_1P0_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h
new file mode 100644
index 000000000000..9c51b395b4ff
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h
@@ -0,0 +1,1928 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INITVALS_9462_2P0_H
+#define INITVALS_9462_2P0_H
+
+/* AR9462 2.0 */
+
+static const u32 ar9462_modes_fast_clock_2p0[][3] = {
+ /* Addr 5G_HT20 5G_HT40 */
+ {0x00001030, 0x00000268, 0x000004d0},
+ {0x00001070, 0x0000018c, 0x00000318},
+ {0x000010b0, 0x00000fd0, 0x00001fa0},
+ {0x00008014, 0x044c044c, 0x08980898},
+ {0x0000801c, 0x148ec02b, 0x148ec057},
+ {0x00008318, 0x000044c0, 0x00008980},
+ {0x00009e00, 0x0372131c, 0x0372131c},
+ {0x0000a230, 0x0000400b, 0x00004016},
+ {0x0000a254, 0x00000898, 0x00001130},
+};
+
+static const u32 ar9462_pciephy_clkreq_enable_L1_2p0[][2] = {
+ /* Addr allmodes */
+ {0x00018c00, 0x18253ede},
+ {0x00018c04, 0x000801d8},
+ {0x00018c08, 0x0003580c},
+};
+
+static const u32 ar9462_2p0_baseband_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011},
+ {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e},
+ {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+ {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+ {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+ {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
+ {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
+ {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0},
+ {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
+ {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
+ {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e},
+ {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3039605e, 0x33795d5e},
+ {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
+ {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+ {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
+ {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c782},
+ {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
+ {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+ {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
+ {0x0000a204, 0x013187c0, 0x013187c4, 0x013187c4, 0x013187c0},
+ {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
+ {0x0000a22c, 0x01026a2f, 0x01026a27, 0x01026a2f, 0x01026a2f},
+ {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b},
+ {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff},
+ {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018},
+ {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
+ {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
+ {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501},
+ {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
+ {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
+ {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
+ {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
+ {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+ {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
+ {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
+ {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+ {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000},
+ {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+ {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce},
+ {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550},
+};
+
+static const u32 ar9462_2p0_mac_core_emulation[][2] = {
+ /* Addr allmodes */
+ {0x00000030, 0x000e0085},
+ {0x00000044, 0x00000008},
+ {0x0000805c, 0xffffc7ff},
+ {0x00008344, 0xaa4a105b},
+};
+
+static const u32 ar9462_common_rx_gain_table_2p0[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x01910190},
+ {0x0000a030, 0x01930192},
+ {0x0000a034, 0x01950194},
+ {0x0000a038, 0x038a0196},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x22222229},
+ {0x0000a084, 0x1d1d1d1d},
+ {0x0000a088, 0x1d1d1d1d},
+ {0x0000a08c, 0x1d1d1d1d},
+ {0x0000a090, 0x171d1d1d},
+ {0x0000a094, 0x11111717},
+ {0x0000a098, 0x00030311},
+ {0x0000a09c, 0x00000000},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+ {0x0000b000, 0x00010000},
+ {0x0000b004, 0x00030002},
+ {0x0000b008, 0x00050004},
+ {0x0000b00c, 0x00810080},
+ {0x0000b010, 0x00830082},
+ {0x0000b014, 0x01810180},
+ {0x0000b018, 0x01830182},
+ {0x0000b01c, 0x01850184},
+ {0x0000b020, 0x02810280},
+ {0x0000b024, 0x02830282},
+ {0x0000b028, 0x02850284},
+ {0x0000b02c, 0x02890288},
+ {0x0000b030, 0x028b028a},
+ {0x0000b034, 0x0388028c},
+ {0x0000b038, 0x038a0389},
+ {0x0000b03c, 0x038c038b},
+ {0x0000b040, 0x0390038d},
+ {0x0000b044, 0x03920391},
+ {0x0000b048, 0x03940393},
+ {0x0000b04c, 0x03960395},
+ {0x0000b050, 0x00000000},
+ {0x0000b054, 0x00000000},
+ {0x0000b058, 0x00000000},
+ {0x0000b05c, 0x00000000},
+ {0x0000b060, 0x00000000},
+ {0x0000b064, 0x00000000},
+ {0x0000b068, 0x00000000},
+ {0x0000b06c, 0x00000000},
+ {0x0000b070, 0x00000000},
+ {0x0000b074, 0x00000000},
+ {0x0000b078, 0x00000000},
+ {0x0000b07c, 0x00000000},
+ {0x0000b080, 0x2a2d2f32},
+ {0x0000b084, 0x21232328},
+ {0x0000b088, 0x19191c1e},
+ {0x0000b08c, 0x12141417},
+ {0x0000b090, 0x07070e0e},
+ {0x0000b094, 0x03030305},
+ {0x0000b098, 0x00000003},
+ {0x0000b09c, 0x00000000},
+ {0x0000b0a0, 0x00000000},
+ {0x0000b0a4, 0x00000000},
+ {0x0000b0a8, 0x00000000},
+ {0x0000b0ac, 0x00000000},
+ {0x0000b0b0, 0x00000000},
+ {0x0000b0b4, 0x00000000},
+ {0x0000b0b8, 0x00000000},
+ {0x0000b0bc, 0x00000000},
+ {0x0000b0c0, 0x003f0020},
+ {0x0000b0c4, 0x00400041},
+ {0x0000b0c8, 0x0140005f},
+ {0x0000b0cc, 0x0160015f},
+ {0x0000b0d0, 0x017e017f},
+ {0x0000b0d4, 0x02410242},
+ {0x0000b0d8, 0x025f0240},
+ {0x0000b0dc, 0x027f0260},
+ {0x0000b0e0, 0x0341027e},
+ {0x0000b0e4, 0x035f0340},
+ {0x0000b0e8, 0x037f0360},
+ {0x0000b0ec, 0x04400441},
+ {0x0000b0f0, 0x0460045f},
+ {0x0000b0f4, 0x0541047f},
+ {0x0000b0f8, 0x055f0540},
+ {0x0000b0fc, 0x057f0560},
+ {0x0000b100, 0x06400641},
+ {0x0000b104, 0x0660065f},
+ {0x0000b108, 0x067e067f},
+ {0x0000b10c, 0x07410742},
+ {0x0000b110, 0x075f0740},
+ {0x0000b114, 0x077f0760},
+ {0x0000b118, 0x07800781},
+ {0x0000b11c, 0x07a0079f},
+ {0x0000b120, 0x07c107bf},
+ {0x0000b124, 0x000007c0},
+ {0x0000b128, 0x00000000},
+ {0x0000b12c, 0x00000000},
+ {0x0000b130, 0x00000000},
+ {0x0000b134, 0x00000000},
+ {0x0000b138, 0x00000000},
+ {0x0000b13c, 0x00000000},
+ {0x0000b140, 0x003f0020},
+ {0x0000b144, 0x00400041},
+ {0x0000b148, 0x0140005f},
+ {0x0000b14c, 0x0160015f},
+ {0x0000b150, 0x017e017f},
+ {0x0000b154, 0x02410242},
+ {0x0000b158, 0x025f0240},
+ {0x0000b15c, 0x027f0260},
+ {0x0000b160, 0x0341027e},
+ {0x0000b164, 0x035f0340},
+ {0x0000b168, 0x037f0360},
+ {0x0000b16c, 0x04400441},
+ {0x0000b170, 0x0460045f},
+ {0x0000b174, 0x0541047f},
+ {0x0000b178, 0x055f0540},
+ {0x0000b17c, 0x057f0560},
+ {0x0000b180, 0x06400641},
+ {0x0000b184, 0x0660065f},
+ {0x0000b188, 0x067e067f},
+ {0x0000b18c, 0x07410742},
+ {0x0000b190, 0x075f0740},
+ {0x0000b194, 0x077f0760},
+ {0x0000b198, 0x07800781},
+ {0x0000b19c, 0x07a0079f},
+ {0x0000b1a0, 0x07c107bf},
+ {0x0000b1a4, 0x000007c0},
+ {0x0000b1a8, 0x00000000},
+ {0x0000b1ac, 0x00000000},
+ {0x0000b1b0, 0x00000000},
+ {0x0000b1b4, 0x00000000},
+ {0x0000b1b8, 0x00000000},
+ {0x0000b1bc, 0x00000000},
+ {0x0000b1c0, 0x00000000},
+ {0x0000b1c4, 0x00000000},
+ {0x0000b1c8, 0x00000000},
+ {0x0000b1cc, 0x00000000},
+ {0x0000b1d0, 0x00000000},
+ {0x0000b1d4, 0x00000000},
+ {0x0000b1d8, 0x00000000},
+ {0x0000b1dc, 0x00000000},
+ {0x0000b1e0, 0x00000000},
+ {0x0000b1e4, 0x00000000},
+ {0x0000b1e8, 0x00000000},
+ {0x0000b1ec, 0x00000000},
+ {0x0000b1f0, 0x00000396},
+ {0x0000b1f4, 0x00000396},
+ {0x0000b1f8, 0x00000396},
+ {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_pciephy_clkreq_disable_L1_2p0[][2] = {
+ /* Addr allmodes */
+ {0x00018c00, 0x18213ede},
+ {0x00018c04, 0x000801d8},
+ {0x00018c08, 0x0003580c},
+};
+
+static const u32 ar9462_pciephy_pll_on_clkreq_disable_L1_2p0[][2] = {
+ /* Addr allmodes */
+ {0x00018c00, 0x18212ede},
+ {0x00018c04, 0x000801d8},
+ {0x00018c08, 0x0003580c},
+};
+
+static const u32 ar9462_2p0_sys3ant[][2] = {
+ /* Addr allmodes */
+ {0x00063280, 0x00040807},
+ {0x00063284, 0x104ccccc},
+};
+
+static const u32 ar9462_common_rx_gain_table_ar9280_2p0[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x02000101},
+ {0x0000a004, 0x02000102},
+ {0x0000a008, 0x02000103},
+ {0x0000a00c, 0x02000104},
+ {0x0000a010, 0x02000200},
+ {0x0000a014, 0x02000201},
+ {0x0000a018, 0x02000202},
+ {0x0000a01c, 0x02000203},
+ {0x0000a020, 0x02000204},
+ {0x0000a024, 0x02000205},
+ {0x0000a028, 0x02000208},
+ {0x0000a02c, 0x02000302},
+ {0x0000a030, 0x02000303},
+ {0x0000a034, 0x02000304},
+ {0x0000a038, 0x02000400},
+ {0x0000a03c, 0x02010300},
+ {0x0000a040, 0x02010301},
+ {0x0000a044, 0x02010302},
+ {0x0000a048, 0x02000500},
+ {0x0000a04c, 0x02010400},
+ {0x0000a050, 0x02020300},
+ {0x0000a054, 0x02020301},
+ {0x0000a058, 0x02020302},
+ {0x0000a05c, 0x02020303},
+ {0x0000a060, 0x02020400},
+ {0x0000a064, 0x02030300},
+ {0x0000a068, 0x02030301},
+ {0x0000a06c, 0x02030302},
+ {0x0000a070, 0x02030303},
+ {0x0000a074, 0x02030400},
+ {0x0000a078, 0x02040300},
+ {0x0000a07c, 0x02040301},
+ {0x0000a080, 0x02040302},
+ {0x0000a084, 0x02040303},
+ {0x0000a088, 0x02030500},
+ {0x0000a08c, 0x02040400},
+ {0x0000a090, 0x02050203},
+ {0x0000a094, 0x02050204},
+ {0x0000a098, 0x02050205},
+ {0x0000a09c, 0x02040500},
+ {0x0000a0a0, 0x02050301},
+ {0x0000a0a4, 0x02050302},
+ {0x0000a0a8, 0x02050303},
+ {0x0000a0ac, 0x02050400},
+ {0x0000a0b0, 0x02050401},
+ {0x0000a0b4, 0x02050402},
+ {0x0000a0b8, 0x02050403},
+ {0x0000a0bc, 0x02050500},
+ {0x0000a0c0, 0x02050501},
+ {0x0000a0c4, 0x02050502},
+ {0x0000a0c8, 0x02050503},
+ {0x0000a0cc, 0x02050504},
+ {0x0000a0d0, 0x02050600},
+ {0x0000a0d4, 0x02050601},
+ {0x0000a0d8, 0x02050602},
+ {0x0000a0dc, 0x02050603},
+ {0x0000a0e0, 0x02050604},
+ {0x0000a0e4, 0x02050700},
+ {0x0000a0e8, 0x02050701},
+ {0x0000a0ec, 0x02050702},
+ {0x0000a0f0, 0x02050703},
+ {0x0000a0f4, 0x02050704},
+ {0x0000a0f8, 0x02050705},
+ {0x0000a0fc, 0x02050708},
+ {0x0000a100, 0x02050709},
+ {0x0000a104, 0x0205070a},
+ {0x0000a108, 0x0205070b},
+ {0x0000a10c, 0x0205070c},
+ {0x0000a110, 0x0205070d},
+ {0x0000a114, 0x02050710},
+ {0x0000a118, 0x02050711},
+ {0x0000a11c, 0x02050712},
+ {0x0000a120, 0x02050713},
+ {0x0000a124, 0x02050714},
+ {0x0000a128, 0x02050715},
+ {0x0000a12c, 0x02050730},
+ {0x0000a130, 0x02050731},
+ {0x0000a134, 0x02050732},
+ {0x0000a138, 0x02050733},
+ {0x0000a13c, 0x02050734},
+ {0x0000a140, 0x02050735},
+ {0x0000a144, 0x02050750},
+ {0x0000a148, 0x02050751},
+ {0x0000a14c, 0x02050752},
+ {0x0000a150, 0x02050753},
+ {0x0000a154, 0x02050754},
+ {0x0000a158, 0x02050755},
+ {0x0000a15c, 0x02050770},
+ {0x0000a160, 0x02050771},
+ {0x0000a164, 0x02050772},
+ {0x0000a168, 0x02050773},
+ {0x0000a16c, 0x02050774},
+ {0x0000a170, 0x02050775},
+ {0x0000a174, 0x00000776},
+ {0x0000a178, 0x00000776},
+ {0x0000a17c, 0x00000776},
+ {0x0000a180, 0x00000776},
+ {0x0000a184, 0x00000776},
+ {0x0000a188, 0x00000776},
+ {0x0000a18c, 0x00000776},
+ {0x0000a190, 0x00000776},
+ {0x0000a194, 0x00000776},
+ {0x0000a198, 0x00000776},
+ {0x0000a19c, 0x00000776},
+ {0x0000a1a0, 0x00000776},
+ {0x0000a1a4, 0x00000776},
+ {0x0000a1a8, 0x00000776},
+ {0x0000a1ac, 0x00000776},
+ {0x0000a1b0, 0x00000776},
+ {0x0000a1b4, 0x00000776},
+ {0x0000a1b8, 0x00000776},
+ {0x0000a1bc, 0x00000776},
+ {0x0000a1c0, 0x00000776},
+ {0x0000a1c4, 0x00000776},
+ {0x0000a1c8, 0x00000776},
+ {0x0000a1cc, 0x00000776},
+ {0x0000a1d0, 0x00000776},
+ {0x0000a1d4, 0x00000776},
+ {0x0000a1d8, 0x00000776},
+ {0x0000a1dc, 0x00000776},
+ {0x0000a1e0, 0x00000776},
+ {0x0000a1e4, 0x00000776},
+ {0x0000a1e8, 0x00000776},
+ {0x0000a1ec, 0x00000776},
+ {0x0000a1f0, 0x00000776},
+ {0x0000a1f4, 0x00000776},
+ {0x0000a1f8, 0x00000776},
+ {0x0000a1fc, 0x00000776},
+ {0x0000b000, 0x02000101},
+ {0x0000b004, 0x02000102},
+ {0x0000b008, 0x02000103},
+ {0x0000b00c, 0x02000104},
+ {0x0000b010, 0x02000200},
+ {0x0000b014, 0x02000201},
+ {0x0000b018, 0x02000202},
+ {0x0000b01c, 0x02000203},
+ {0x0000b020, 0x02000204},
+ {0x0000b024, 0x02000205},
+ {0x0000b028, 0x02000208},
+ {0x0000b02c, 0x02000302},
+ {0x0000b030, 0x02000303},
+ {0x0000b034, 0x02000304},
+ {0x0000b038, 0x02000400},
+ {0x0000b03c, 0x02010300},
+ {0x0000b040, 0x02010301},
+ {0x0000b044, 0x02010302},
+ {0x0000b048, 0x02000500},
+ {0x0000b04c, 0x02010400},
+ {0x0000b050, 0x02020300},
+ {0x0000b054, 0x02020301},
+ {0x0000b058, 0x02020302},
+ {0x0000b05c, 0x02020303},
+ {0x0000b060, 0x02020400},
+ {0x0000b064, 0x02030300},
+ {0x0000b068, 0x02030301},
+ {0x0000b06c, 0x02030302},
+ {0x0000b070, 0x02030303},
+ {0x0000b074, 0x02030400},
+ {0x0000b078, 0x02040300},
+ {0x0000b07c, 0x02040301},
+ {0x0000b080, 0x02040302},
+ {0x0000b084, 0x02040303},
+ {0x0000b088, 0x02030500},
+ {0x0000b08c, 0x02040400},
+ {0x0000b090, 0x02050203},
+ {0x0000b094, 0x02050204},
+ {0x0000b098, 0x02050205},
+ {0x0000b09c, 0x02040500},
+ {0x0000b0a0, 0x02050301},
+ {0x0000b0a4, 0x02050302},
+ {0x0000b0a8, 0x02050303},
+ {0x0000b0ac, 0x02050400},
+ {0x0000b0b0, 0x02050401},
+ {0x0000b0b4, 0x02050402},
+ {0x0000b0b8, 0x02050403},
+ {0x0000b0bc, 0x02050500},
+ {0x0000b0c0, 0x02050501},
+ {0x0000b0c4, 0x02050502},
+ {0x0000b0c8, 0x02050503},
+ {0x0000b0cc, 0x02050504},
+ {0x0000b0d0, 0x02050600},
+ {0x0000b0d4, 0x02050601},
+ {0x0000b0d8, 0x02050602},
+ {0x0000b0dc, 0x02050603},
+ {0x0000b0e0, 0x02050604},
+ {0x0000b0e4, 0x02050700},
+ {0x0000b0e8, 0x02050701},
+ {0x0000b0ec, 0x02050702},
+ {0x0000b0f0, 0x02050703},
+ {0x0000b0f4, 0x02050704},
+ {0x0000b0f8, 0x02050705},
+ {0x0000b0fc, 0x02050708},
+ {0x0000b100, 0x02050709},
+ {0x0000b104, 0x0205070a},
+ {0x0000b108, 0x0205070b},
+ {0x0000b10c, 0x0205070c},
+ {0x0000b110, 0x0205070d},
+ {0x0000b114, 0x02050710},
+ {0x0000b118, 0x02050711},
+ {0x0000b11c, 0x02050712},
+ {0x0000b120, 0x02050713},
+ {0x0000b124, 0x02050714},
+ {0x0000b128, 0x02050715},
+ {0x0000b12c, 0x02050730},
+ {0x0000b130, 0x02050731},
+ {0x0000b134, 0x02050732},
+ {0x0000b138, 0x02050733},
+ {0x0000b13c, 0x02050734},
+ {0x0000b140, 0x02050735},
+ {0x0000b144, 0x02050750},
+ {0x0000b148, 0x02050751},
+ {0x0000b14c, 0x02050752},
+ {0x0000b150, 0x02050753},
+ {0x0000b154, 0x02050754},
+ {0x0000b158, 0x02050755},
+ {0x0000b15c, 0x02050770},
+ {0x0000b160, 0x02050771},
+ {0x0000b164, 0x02050772},
+ {0x0000b168, 0x02050773},
+ {0x0000b16c, 0x02050774},
+ {0x0000b170, 0x02050775},
+ {0x0000b174, 0x00000776},
+ {0x0000b178, 0x00000776},
+ {0x0000b17c, 0x00000776},
+ {0x0000b180, 0x00000776},
+ {0x0000b184, 0x00000776},
+ {0x0000b188, 0x00000776},
+ {0x0000b18c, 0x00000776},
+ {0x0000b190, 0x00000776},
+ {0x0000b194, 0x00000776},
+ {0x0000b198, 0x00000776},
+ {0x0000b19c, 0x00000776},
+ {0x0000b1a0, 0x00000776},
+ {0x0000b1a4, 0x00000776},
+ {0x0000b1a8, 0x00000776},
+ {0x0000b1ac, 0x00000776},
+ {0x0000b1b0, 0x00000776},
+ {0x0000b1b4, 0x00000776},
+ {0x0000b1b8, 0x00000776},
+ {0x0000b1bc, 0x00000776},
+ {0x0000b1c0, 0x00000776},
+ {0x0000b1c4, 0x00000776},
+ {0x0000b1c8, 0x00000776},
+ {0x0000b1cc, 0x00000776},
+ {0x0000b1d0, 0x00000776},
+ {0x0000b1d4, 0x00000776},
+ {0x0000b1d8, 0x00000776},
+ {0x0000b1dc, 0x00000776},
+ {0x0000b1e0, 0x00000776},
+ {0x0000b1e4, 0x00000776},
+ {0x0000b1e8, 0x00000776},
+ {0x0000b1ec, 0x00000776},
+ {0x0000b1f0, 0x00000776},
+ {0x0000b1f4, 0x00000776},
+ {0x0000b1f8, 0x00000776},
+ {0x0000b1fc, 0x00000776},
+};
+
+static const u32 ar9200_ar9280_2p0_radio_core[][2] = {
+ /* Addr allmodes */
+ {0x00007800, 0x00040000},
+ {0x00007804, 0xdb005012},
+ {0x00007808, 0x04924914},
+ {0x0000780c, 0x21084210},
+ {0x00007810, 0x6d801300},
+ {0x00007814, 0x0019beff},
+ {0x00007818, 0x07e41000},
+ {0x0000781c, 0x00392000},
+ {0x00007820, 0x92592480},
+ {0x00007824, 0x00040000},
+ {0x00007828, 0xdb005012},
+ {0x0000782c, 0x04924914},
+ {0x00007830, 0x21084210},
+ {0x00007834, 0x6d801300},
+ {0x00007838, 0x0019beff},
+ {0x0000783c, 0x07e40000},
+ {0x00007840, 0x00392000},
+ {0x00007844, 0x92592480},
+ {0x00007848, 0x00100000},
+ {0x0000784c, 0x773f0567},
+ {0x00007850, 0x54214514},
+ {0x00007854, 0x12035828},
+ {0x00007858, 0x92592692},
+ {0x0000785c, 0x00000000},
+ {0x00007860, 0x56400000},
+ {0x00007864, 0x0a8e370e},
+ {0x00007868, 0xc0102850},
+ {0x0000786c, 0x812d4000},
+ {0x00007870, 0x807ec400},
+ {0x00007874, 0x001b6db0},
+ {0x00007878, 0x00376b63},
+ {0x0000787c, 0x06db6db6},
+ {0x00007880, 0x006d8000},
+ {0x00007884, 0xffeffffe},
+ {0x00007888, 0xffeffffe},
+ {0x0000788c, 0x00010000},
+ {0x00007890, 0x02060aeb},
+ {0x00007894, 0x5a108000},
+};
+
+static const u32 ar9462_2p0_mac_postamble_emulation[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00008014, 0x10f810f8, 0x10f810f8, 0x10f810f8, 0x10f810f8},
+ {0x0000801c, 0x0e8d8017, 0x0e8d8017, 0x0e8d8017, 0x0e8d8017},
+};
+
+static const u32 ar9462_2p0_radio_postamble_sys3ant[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808},
+ {0x00016140, 0x10804008, 0x10804008, 0x90804008, 0x90804008},
+ {0x00016540, 0x10804008, 0x10804008, 0x90804008, 0x90804008},
+};
+
+static const u32 ar9462_2p0_baseband_postamble_emulation[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009e3c, 0xcf946221, 0xcf946221, 0xcf946221, 0xcf946221},
+ {0x00009e44, 0xfc5c0000, 0xfc5c0000, 0xfc5c0000, 0xfc5c0000},
+ {0x0000a258, 0x02020200, 0x02020200, 0x02020200, 0x02020200},
+ {0x0000a25c, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x0000a28c, 0x00011111, 0x00011111, 0x00011111, 0x00011111},
+ {0x0000a2c4, 0x00148d18, 0x00148d18, 0x00148d20, 0x00148d20},
+ {0x0000a2d8, 0xf999a800, 0xf999a800, 0xf999a80c, 0xf999a80c},
+ {0x0000a50c, 0x0000c00a, 0x0000c00a, 0x0000c00a, 0x0000c00a},
+ {0x0000a538, 0x00038e8c, 0x00038e8c, 0x00038e8c, 0x00038e8c},
+ {0x0000a53c, 0x0003cecc, 0x0003cecc, 0x0003cecc, 0x0003cecc},
+ {0x0000a540, 0x00040ed4, 0x00040ed4, 0x00040ed4, 0x00040ed4},
+ {0x0000a544, 0x00044edc, 0x00044edc, 0x00044edc, 0x00044edc},
+ {0x0000a548, 0x00048ede, 0x00048ede, 0x00048ede, 0x00048ede},
+ {0x0000a54c, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e},
+ {0x0000a550, 0x00050f5e, 0x00050f5e, 0x00050f5e, 0x00050f5e},
+ {0x0000a554, 0x00054f9e, 0x00054f9e, 0x00054f9e, 0x00054f9e},
+ {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+};
+
+static const u32 ar9462_2p0_radio_postamble_sys2ant[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808},
+ {0x00016140, 0x10804008, 0x10804008, 0x90804008, 0x90804008},
+ {0x00016540, 0x10804008, 0x10804008, 0x90804008, 0x90804008},
+};
+
+static const u32 ar9462_common_wo_xlna_rx_gain_table_2p0[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x03820190},
+ {0x0000a030, 0x03840383},
+ {0x0000a034, 0x03880385},
+ {0x0000a038, 0x038a0389},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x29292929},
+ {0x0000a084, 0x29292929},
+ {0x0000a088, 0x29292929},
+ {0x0000a08c, 0x29292929},
+ {0x0000a090, 0x22292929},
+ {0x0000a094, 0x1d1d2222},
+ {0x0000a098, 0x0c111117},
+ {0x0000a09c, 0x00030303},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+ {0x0000b000, 0x00010000},
+ {0x0000b004, 0x00030002},
+ {0x0000b008, 0x00050004},
+ {0x0000b00c, 0x00810080},
+ {0x0000b010, 0x00830082},
+ {0x0000b014, 0x01810180},
+ {0x0000b018, 0x01830182},
+ {0x0000b01c, 0x01850184},
+ {0x0000b020, 0x02810280},
+ {0x0000b024, 0x02830282},
+ {0x0000b028, 0x02850284},
+ {0x0000b02c, 0x02890288},
+ {0x0000b030, 0x028b028a},
+ {0x0000b034, 0x0388028c},
+ {0x0000b038, 0x038a0389},
+ {0x0000b03c, 0x038c038b},
+ {0x0000b040, 0x0390038d},
+ {0x0000b044, 0x03920391},
+ {0x0000b048, 0x03940393},
+ {0x0000b04c, 0x03960395},
+ {0x0000b050, 0x00000000},
+ {0x0000b054, 0x00000000},
+ {0x0000b058, 0x00000000},
+ {0x0000b05c, 0x00000000},
+ {0x0000b060, 0x00000000},
+ {0x0000b064, 0x00000000},
+ {0x0000b068, 0x00000000},
+ {0x0000b06c, 0x00000000},
+ {0x0000b070, 0x00000000},
+ {0x0000b074, 0x00000000},
+ {0x0000b078, 0x00000000},
+ {0x0000b07c, 0x00000000},
+ {0x0000b080, 0x32323232},
+ {0x0000b084, 0x2f2f3232},
+ {0x0000b088, 0x23282a2d},
+ {0x0000b08c, 0x1c1e2123},
+ {0x0000b090, 0x14171919},
+ {0x0000b094, 0x0e0e1214},
+ {0x0000b098, 0x03050707},
+ {0x0000b09c, 0x00030303},
+ {0x0000b0a0, 0x00000000},
+ {0x0000b0a4, 0x00000000},
+ {0x0000b0a8, 0x00000000},
+ {0x0000b0ac, 0x00000000},
+ {0x0000b0b0, 0x00000000},
+ {0x0000b0b4, 0x00000000},
+ {0x0000b0b8, 0x00000000},
+ {0x0000b0bc, 0x00000000},
+ {0x0000b0c0, 0x003f0020},
+ {0x0000b0c4, 0x00400041},
+ {0x0000b0c8, 0x0140005f},
+ {0x0000b0cc, 0x0160015f},
+ {0x0000b0d0, 0x017e017f},
+ {0x0000b0d4, 0x02410242},
+ {0x0000b0d8, 0x025f0240},
+ {0x0000b0dc, 0x027f0260},
+ {0x0000b0e0, 0x0341027e},
+ {0x0000b0e4, 0x035f0340},
+ {0x0000b0e8, 0x037f0360},
+ {0x0000b0ec, 0x04400441},
+ {0x0000b0f0, 0x0460045f},
+ {0x0000b0f4, 0x0541047f},
+ {0x0000b0f8, 0x055f0540},
+ {0x0000b0fc, 0x057f0560},
+ {0x0000b100, 0x06400641},
+ {0x0000b104, 0x0660065f},
+ {0x0000b108, 0x067e067f},
+ {0x0000b10c, 0x07410742},
+ {0x0000b110, 0x075f0740},
+ {0x0000b114, 0x077f0760},
+ {0x0000b118, 0x07800781},
+ {0x0000b11c, 0x07a0079f},
+ {0x0000b120, 0x07c107bf},
+ {0x0000b124, 0x000007c0},
+ {0x0000b128, 0x00000000},
+ {0x0000b12c, 0x00000000},
+ {0x0000b130, 0x00000000},
+ {0x0000b134, 0x00000000},
+ {0x0000b138, 0x00000000},
+ {0x0000b13c, 0x00000000},
+ {0x0000b140, 0x003f0020},
+ {0x0000b144, 0x00400041},
+ {0x0000b148, 0x0140005f},
+ {0x0000b14c, 0x0160015f},
+ {0x0000b150, 0x017e017f},
+ {0x0000b154, 0x02410242},
+ {0x0000b158, 0x025f0240},
+ {0x0000b15c, 0x027f0260},
+ {0x0000b160, 0x0341027e},
+ {0x0000b164, 0x035f0340},
+ {0x0000b168, 0x037f0360},
+ {0x0000b16c, 0x04400441},
+ {0x0000b170, 0x0460045f},
+ {0x0000b174, 0x0541047f},
+ {0x0000b178, 0x055f0540},
+ {0x0000b17c, 0x057f0560},
+ {0x0000b180, 0x06400641},
+ {0x0000b184, 0x0660065f},
+ {0x0000b188, 0x067e067f},
+ {0x0000b18c, 0x07410742},
+ {0x0000b190, 0x075f0740},
+ {0x0000b194, 0x077f0760},
+ {0x0000b198, 0x07800781},
+ {0x0000b19c, 0x07a0079f},
+ {0x0000b1a0, 0x07c107bf},
+ {0x0000b1a4, 0x000007c0},
+ {0x0000b1a8, 0x00000000},
+ {0x0000b1ac, 0x00000000},
+ {0x0000b1b0, 0x00000000},
+ {0x0000b1b4, 0x00000000},
+ {0x0000b1b8, 0x00000000},
+ {0x0000b1bc, 0x00000000},
+ {0x0000b1c0, 0x00000000},
+ {0x0000b1c4, 0x00000000},
+ {0x0000b1c8, 0x00000000},
+ {0x0000b1cc, 0x00000000},
+ {0x0000b1d0, 0x00000000},
+ {0x0000b1d4, 0x00000000},
+ {0x0000b1d8, 0x00000000},
+ {0x0000b1dc, 0x00000000},
+ {0x0000b1e0, 0x00000000},
+ {0x0000b1e4, 0x00000000},
+ {0x0000b1e8, 0x00000000},
+ {0x0000b1ec, 0x00000000},
+ {0x0000b1f0, 0x00000396},
+ {0x0000b1f4, 0x00000396},
+ {0x0000b1f8, 0x00000396},
+ {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_2p0_baseband_core_txfir_coeff_japan_2484[][2] = {
+ /* Addr allmodes */
+ {0x0000a398, 0x00000000},
+ {0x0000a39c, 0x6f7f0301},
+ {0x0000a3a0, 0xca9228ee},
+};
+
+static const u32 ar9462_modes_low_ob_db_tx_gain_table_2p0[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002},
+ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+ {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400},
+ {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402},
+ {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404},
+ {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603},
+ {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02},
+ {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04},
+ {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20},
+ {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20},
+ {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22},
+ {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24},
+ {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640},
+ {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660},
+ {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861},
+ {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81},
+ {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83},
+ {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84},
+ {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3},
+ {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5},
+ {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9},
+ {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb},
+ {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501},
+ {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005},
+ {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x00016044, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4},
+ {0x00016048, 0x64992060, 0x64992060, 0x64992060, 0x64992060},
+ {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000},
+ {0x00016444, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4},
+ {0x00016448, 0x64992000, 0x64992000, 0x64992000, 0x64992000},
+ {0x00016454, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000},
+};
+
+static const u32 ar9462_2p0_soc_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00007010, 0x00002233, 0x00002233, 0x00002233, 0x00002233},
+};
+
+static const u32 ar9462_2p0_baseband_core[][2] = {
+ /* Addr allmodes */
+ {0x00009800, 0xafe68e30},
+ {0x00009804, 0xfd14e000},
+ {0x00009808, 0x9c0a9f6b},
+ {0x0000980c, 0x04900000},
+ {0x00009814, 0x9280c00a},
+ {0x00009818, 0x00000000},
+ {0x0000981c, 0x00020028},
+ {0x00009834, 0x6400a290},
+ {0x00009838, 0x0108ecff},
+ {0x0000983c, 0x0d000600},
+ {0x00009880, 0x201fff00},
+ {0x00009884, 0x00001042},
+ {0x000098a4, 0x00200400},
+ {0x000098b0, 0x32440bbe},
+ {0x000098d0, 0x004b6a8e},
+ {0x000098d4, 0x00000820},
+ {0x000098dc, 0x00000000},
+ {0x000098e4, 0x01ffffff},
+ {0x000098e8, 0x01ffffff},
+ {0x000098ec, 0x01ffffff},
+ {0x000098f0, 0x00000000},
+ {0x000098f4, 0x00000000},
+ {0x00009bf0, 0x80000000},
+ {0x00009c04, 0xff55ff55},
+ {0x00009c08, 0x0320ff55},
+ {0x00009c0c, 0x00000000},
+ {0x00009c10, 0x00000000},
+ {0x00009c14, 0x00046384},
+ {0x00009c18, 0x05b6b440},
+ {0x00009c1c, 0x00b6b440},
+ {0x00009d00, 0xc080a333},
+ {0x00009d04, 0x40206c10},
+ {0x00009d08, 0x009c4060},
+ {0x00009d0c, 0x9883800a},
+ {0x00009d10, 0x01834061},
+ {0x00009d14, 0x00c0040b},
+ {0x00009d18, 0x00000000},
+ {0x00009e08, 0x0038230c},
+ {0x00009e24, 0x990bb515},
+ {0x00009e28, 0x0c6f0000},
+ {0x00009e30, 0x06336f77},
+ {0x00009e34, 0x6af6532f},
+ {0x00009e38, 0x0cc80c00},
+ {0x00009e40, 0x0d261820},
+ {0x00009e4c, 0x00001004},
+ {0x00009e50, 0x00ff03f1},
+ {0x00009e54, 0xe4c355c7},
+ {0x00009e58, 0xfd897735},
+ {0x00009e5c, 0xe9198724},
+ {0x00009fc0, 0x803e4788},
+ {0x00009fc4, 0x0001efb5},
+ {0x00009fcc, 0x40000014},
+ {0x00009fd0, 0x01193b93},
+ {0x0000a20c, 0x00000000},
+ {0x0000a220, 0x00000000},
+ {0x0000a224, 0x00000000},
+ {0x0000a228, 0x10002310},
+ {0x0000a23c, 0x00000000},
+ {0x0000a244, 0x0c000000},
+ {0x0000a2a0, 0x00000001},
+ {0x0000a2c0, 0x00000001},
+ {0x0000a2c8, 0x00000000},
+ {0x0000a2cc, 0x18c43433},
+ {0x0000a2d4, 0x00000000},
+ {0x0000a2ec, 0x00000000},
+ {0x0000a2f0, 0x00000000},
+ {0x0000a2f4, 0x00000000},
+ {0x0000a2f8, 0x00000000},
+ {0x0000a344, 0x00000000},
+ {0x0000a34c, 0x00000000},
+ {0x0000a350, 0x0000a000},
+ {0x0000a364, 0x00000000},
+ {0x0000a370, 0x00000000},
+ {0x0000a390, 0x00000001},
+ {0x0000a394, 0x00000444},
+ {0x0000a398, 0x001f0e0f},
+ {0x0000a39c, 0x0075393f},
+ {0x0000a3a0, 0xb79f6427},
+ {0x0000a3a4, 0x00000000},
+ {0x0000a3a8, 0xaaaaaaaa},
+ {0x0000a3ac, 0x3c466478},
+ {0x0000a3c0, 0x20202020},
+ {0x0000a3c4, 0x22222220},
+ {0x0000a3c8, 0x20200020},
+ {0x0000a3cc, 0x20202020},
+ {0x0000a3d0, 0x20202020},
+ {0x0000a3d4, 0x20202020},
+ {0x0000a3d8, 0x20202020},
+ {0x0000a3dc, 0x20202020},
+ {0x0000a3e0, 0x20202020},
+ {0x0000a3e4, 0x20202020},
+ {0x0000a3e8, 0x20202020},
+ {0x0000a3ec, 0x20202020},
+ {0x0000a3f0, 0x00000000},
+ {0x0000a3f4, 0x00000006},
+ {0x0000a3f8, 0x0c9bd380},
+ {0x0000a3fc, 0x000f0f01},
+ {0x0000a400, 0x8fa91f01},
+ {0x0000a404, 0x00000000},
+ {0x0000a408, 0x0e79e5c6},
+ {0x0000a40c, 0x00820820},
+ {0x0000a414, 0x1ce739ce},
+ {0x0000a418, 0x2d001dce},
+ {0x0000a41c, 0x1ce739ce},
+ {0x0000a420, 0x000001ce},
+ {0x0000a424, 0x1ce739ce},
+ {0x0000a428, 0x000001ce},
+ {0x0000a42c, 0x1ce739ce},
+ {0x0000a430, 0x1ce739ce},
+ {0x0000a434, 0x00000000},
+ {0x0000a438, 0x00001801},
+ {0x0000a43c, 0x00100000},
+ {0x0000a444, 0x00000000},
+ {0x0000a448, 0x05000080},
+ {0x0000a44c, 0x00000001},
+ {0x0000a450, 0x00010000},
+ {0x0000a454, 0x07000000},
+ {0x0000a644, 0xbfad9d74},
+ {0x0000a648, 0x0048060a},
+ {0x0000a64c, 0x00002037},
+ {0x0000a670, 0x03020100},
+ {0x0000a674, 0x09080504},
+ {0x0000a678, 0x0d0c0b0a},
+ {0x0000a67c, 0x13121110},
+ {0x0000a680, 0x31301514},
+ {0x0000a684, 0x35343332},
+ {0x0000a688, 0x00000036},
+ {0x0000a690, 0x00000838},
+ {0x0000a6b0, 0x0000000a},
+ {0x0000a6b4, 0x00512c01},
+ {0x0000a7c0, 0x00000000},
+ {0x0000a7c4, 0xfffffffc},
+ {0x0000a7c8, 0x00000000},
+ {0x0000a7cc, 0x00000000},
+ {0x0000a7d0, 0x00000000},
+ {0x0000a7d4, 0x00000004},
+ {0x0000a7dc, 0x00000001},
+ {0x0000a7f0, 0x80000000},
+ {0x0000a8d0, 0x004b6a8e},
+ {0x0000a8d4, 0x00000820},
+ {0x0000a8dc, 0x00000000},
+ {0x0000a8f0, 0x00000000},
+ {0x0000a8f4, 0x00000000},
+ {0x0000abf0, 0x80000000},
+ {0x0000b2d0, 0x00000080},
+ {0x0000b2d4, 0x00000000},
+ {0x0000b2ec, 0x00000000},
+ {0x0000b2f0, 0x00000000},
+ {0x0000b2f4, 0x00000000},
+ {0x0000b2f8, 0x00000000},
+ {0x0000b408, 0x0e79e5c0},
+ {0x0000b40c, 0x00820820},
+ {0x0000b420, 0x00000000},
+ {0x0000b6b0, 0x0000000a},
+ {0x0000b6b4, 0x00000001},
+};
+
+static const u32 ar9462_2p0_radio_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0001609c, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524},
+ {0x000160b0, 0x01d67f70, 0x01d67f70, 0x01d67f70, 0x01d67f70},
+ {0x0001610c, 0x48000000, 0x40000000, 0x40000000, 0x40000000},
+ {0x0001650c, 0x48000000, 0x40000000, 0x40000000, 0x40000000},
+};
+
+static const u32 ar9462_modes_high_ob_db_tx_gain_table_2p0[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002},
+ {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+ {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
+ {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400},
+ {0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402},
+ {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404},
+ {0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603},
+ {0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02},
+ {0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04},
+ {0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20},
+ {0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20},
+ {0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22},
+ {0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24},
+ {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640},
+ {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660},
+ {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861},
+ {0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81},
+ {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83},
+ {0x0000a550, 0x5f025ef6, 0x5f025ef6, 0x44001c84, 0x44001c84},
+ {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3},
+ {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9},
+ {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb},
+ {0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
+ {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
+ {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x00016044, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4},
+ {0x00016048, 0x8db49060, 0x8db49060, 0x8db49060, 0x8db49060},
+ {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000},
+ {0x00016444, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4},
+ {0x00016448, 0x8db49000, 0x8db49000, 0x8db49000, 0x8db49000},
+ {0x00016454, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000},
+};
+
+static const u32 ar9462_2p0_radio_core[][2] = {
+ /* Addr allmodes */
+ {0x00016000, 0x36db6db6},
+ {0x00016004, 0x6db6db40},
+ {0x00016008, 0x73f00000},
+ {0x0001600c, 0x00000000},
+ {0x00016010, 0x6d820001},
+ {0x00016040, 0x7f80fff8},
+ {0x0001604c, 0x2699e04f},
+ {0x00016050, 0x6db6db6c},
+ {0x00016058, 0x6c200000},
+ {0x00016080, 0x00040000},
+ {0x00016084, 0x9a68048c},
+ {0x00016088, 0x54214514},
+ {0x0001608c, 0x1203040b},
+ {0x00016090, 0x24926490},
+ {0x00016098, 0xd2888888},
+ {0x000160a0, 0x0a108ffe},
+ {0x000160a4, 0x812fc491},
+ {0x000160a8, 0x423c8000},
+ {0x000160b4, 0x92000000},
+ {0x000160b8, 0x0285dddc},
+ {0x000160bc, 0x02908888},
+ {0x000160c0, 0x00adb6d0},
+ {0x000160c4, 0x6db6db60},
+ {0x000160c8, 0x6db6db6c},
+ {0x000160cc, 0x0de6c1b0},
+ {0x00016100, 0x3fffbe04},
+ {0x00016104, 0xfff80000},
+ {0x00016108, 0x00200400},
+ {0x00016110, 0x00000000},
+ {0x00016144, 0x02084080},
+ {0x00016148, 0x000080c0},
+ {0x00016280, 0x050a0001},
+ {0x00016284, 0x3d841400},
+ {0x00016288, 0x00000000},
+ {0x0001628c, 0xe3000000},
+ {0x00016290, 0xa1005080},
+ {0x00016294, 0x00000020},
+ {0x00016298, 0x54a82900},
+ {0x00016340, 0x121e4276},
+ {0x00016344, 0x00300000},
+ {0x00016400, 0x36db6db6},
+ {0x00016404, 0x6db6db40},
+ {0x00016408, 0x73f00000},
+ {0x0001640c, 0x00000000},
+ {0x00016410, 0x6c800001},
+ {0x00016440, 0x7f80fff8},
+ {0x0001644c, 0x4699e04f},
+ {0x00016450, 0x6db6db6c},
+ {0x00016500, 0x3fffbe04},
+ {0x00016504, 0xfff80000},
+ {0x00016508, 0x00200400},
+ {0x00016510, 0x00000000},
+ {0x00016544, 0x02084080},
+ {0x00016548, 0x000080c0},
+};
+
+static const u32 ar9462_2p0_tx_gain_table_baseband_postamble_emulation[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a410, 0x000000d5, 0x000000d5, 0x000000d5, 0x000000d5},
+ {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a504, 0x00004002, 0x00004002, 0x00004002, 0x00004002},
+ {0x0000a508, 0x00008004, 0x00008004, 0x00008004, 0x00008004},
+ {0x0000a510, 0x0001000c, 0x0001000c, 0x0001000c, 0x0001000c},
+ {0x0000a514, 0x0001420b, 0x0001420b, 0x0001420b, 0x0001420b},
+ {0x0000a518, 0x0001824a, 0x0001824a, 0x0001824a, 0x0001824a},
+ {0x0000a51c, 0x0001c44a, 0x0001c44a, 0x0001c44a, 0x0001c44a},
+ {0x0000a520, 0x0002064a, 0x0002064a, 0x0002064a, 0x0002064a},
+ {0x0000a524, 0x0002484a, 0x0002484a, 0x0002484a, 0x0002484a},
+ {0x0000a528, 0x00028a4a, 0x00028a4a, 0x00028a4a, 0x00028a4a},
+ {0x0000a52c, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a},
+ {0x0000a530, 0x00030e4a, 0x00030e4a, 0x00030e4a, 0x00030e4a},
+ {0x0000a534, 0x00034e8a, 0x00034e8a, 0x00034e8a, 0x00034e8a},
+};
+
+static const u32 ar9462_2p0_soc_preamble[][2] = {
+ /* Addr allmodes */
+ {0x00007020, 0x00000000},
+ {0x00007034, 0x00000002},
+ {0x00007038, 0x000004c2},
+};
+
+static const u32 ar9462_2p0_sys2ant[][2] = {
+ /* Addr allmodes */
+ {0x00063120, 0x00801980},
+};
+
+static const u32 ar9462_2p0_mac_core[][2] = {
+ /* Addr allmodes */
+ {0x00000008, 0x00000000},
+ {0x00000030, 0x000e0085},
+ {0x00000034, 0x00000005},
+ {0x00000040, 0x00000000},
+ {0x00000044, 0x00000000},
+ {0x00000048, 0x00000008},
+ {0x0000004c, 0x00000010},
+ {0x00000050, 0x00000000},
+ {0x00001040, 0x002ffc0f},
+ {0x00001044, 0x002ffc0f},
+ {0x00001048, 0x002ffc0f},
+ {0x0000104c, 0x002ffc0f},
+ {0x00001050, 0x002ffc0f},
+ {0x00001054, 0x002ffc0f},
+ {0x00001058, 0x002ffc0f},
+ {0x0000105c, 0x002ffc0f},
+ {0x00001060, 0x002ffc0f},
+ {0x00001064, 0x002ffc0f},
+ {0x000010f0, 0x00000100},
+ {0x00001270, 0x00000000},
+ {0x000012b0, 0x00000000},
+ {0x000012f0, 0x00000000},
+ {0x0000143c, 0x00000000},
+ {0x0000147c, 0x00000000},
+ {0x00001810, 0x0f000003},
+ {0x00008000, 0x00000000},
+ {0x00008004, 0x00000000},
+ {0x00008008, 0x00000000},
+ {0x0000800c, 0x00000000},
+ {0x00008018, 0x00000000},
+ {0x00008020, 0x00000000},
+ {0x00008038, 0x00000000},
+ {0x0000803c, 0x00080000},
+ {0x00008040, 0x00000000},
+ {0x00008044, 0x00000000},
+ {0x00008048, 0x00000000},
+ {0x0000804c, 0xffffffff},
+ {0x00008050, 0xffffffff},
+ {0x00008054, 0x00000000},
+ {0x00008058, 0x00000000},
+ {0x0000805c, 0x000fc78f},
+ {0x00008060, 0x0000000f},
+ {0x00008064, 0x00000000},
+ {0x00008070, 0x00000310},
+ {0x00008074, 0x00000020},
+ {0x00008078, 0x00000000},
+ {0x0000809c, 0x0000000f},
+ {0x000080a0, 0x00000000},
+ {0x000080a4, 0x02ff0000},
+ {0x000080a8, 0x0e070605},
+ {0x000080ac, 0x0000000d},
+ {0x000080b0, 0x00000000},
+ {0x000080b4, 0x00000000},
+ {0x000080b8, 0x00000000},
+ {0x000080bc, 0x00000000},
+ {0x000080c0, 0x2a800000},
+ {0x000080c4, 0x06900168},
+ {0x000080c8, 0x13881c20},
+ {0x000080cc, 0x01f40000},
+ {0x000080d0, 0x00252500},
+ {0x000080d4, 0x00b00005},
+ {0x000080d8, 0x00400002},
+ {0x000080dc, 0x00000000},
+ {0x000080e0, 0xffffffff},
+ {0x000080e4, 0x0000ffff},
+ {0x000080e8, 0x3f3f3f3f},
+ {0x000080ec, 0x00000000},
+ {0x000080f0, 0x00000000},
+ {0x000080f4, 0x00000000},
+ {0x000080fc, 0x00020000},
+ {0x00008100, 0x00000000},
+ {0x00008108, 0x00000052},
+ {0x0000810c, 0x00000000},
+ {0x00008110, 0x00000000},
+ {0x00008114, 0x000007ff},
+ {0x00008118, 0x000000aa},
+ {0x0000811c, 0x00003210},
+ {0x00008124, 0x00000000},
+ {0x00008128, 0x00000000},
+ {0x0000812c, 0x00000000},
+ {0x00008130, 0x00000000},
+ {0x00008134, 0x00000000},
+ {0x00008138, 0x00000000},
+ {0x0000813c, 0x0000ffff},
+ {0x00008144, 0xffffffff},
+ {0x00008168, 0x00000000},
+ {0x0000816c, 0x00000000},
+ {0x00008170, 0x18486e00},
+ {0x00008174, 0x33332210},
+ {0x00008178, 0x00000000},
+ {0x0000817c, 0x00020000},
+ {0x000081c4, 0x33332210},
+ {0x000081c8, 0x00000000},
+ {0x000081cc, 0x00000000},
+ {0x000081d4, 0x00000000},
+ {0x000081ec, 0x00000000},
+ {0x000081f0, 0x00000000},
+ {0x000081f4, 0x00000000},
+ {0x000081f8, 0x00000000},
+ {0x000081fc, 0x00000000},
+ {0x00008240, 0x00100000},
+ {0x00008244, 0x0010f400},
+ {0x00008248, 0x00000800},
+ {0x0000824c, 0x0001e800},
+ {0x00008250, 0x00000000},
+ {0x00008254, 0x00000000},
+ {0x00008258, 0x00000000},
+ {0x0000825c, 0x40000000},
+ {0x00008260, 0x00080922},
+ {0x00008264, 0x99c00010},
+ {0x00008268, 0xffffffff},
+ {0x0000826c, 0x0000ffff},
+ {0x00008270, 0x00000000},
+ {0x00008274, 0x40000000},
+ {0x00008278, 0x003e4180},
+ {0x0000827c, 0x00000004},
+ {0x00008284, 0x0000002c},
+ {0x00008288, 0x0000002c},
+ {0x0000828c, 0x000000ff},
+ {0x00008294, 0x00000000},
+ {0x00008298, 0x00000000},
+ {0x0000829c, 0x00000000},
+ {0x00008300, 0x00000140},
+ {0x00008314, 0x00000000},
+ {0x0000831c, 0x0000010d},
+ {0x00008328, 0x00000000},
+ {0x0000832c, 0x0000001f},
+ {0x00008330, 0x00000302},
+ {0x00008334, 0x00000700},
+ {0x00008338, 0xffff0000},
+ {0x0000833c, 0x02400000},
+ {0x00008340, 0x000107ff},
+ {0x00008344, 0xaa48105b},
+ {0x00008348, 0x008f0000},
+ {0x0000835c, 0x00000000},
+ {0x00008360, 0xffffffff},
+ {0x00008364, 0xffffffff},
+ {0x00008368, 0x00000000},
+ {0x00008370, 0x00000000},
+ {0x00008374, 0x000000ff},
+ {0x00008378, 0x00000000},
+ {0x0000837c, 0x00000000},
+ {0x00008380, 0xffffffff},
+ {0x00008384, 0xffffffff},
+ {0x00008390, 0xffffffff},
+ {0x00008394, 0xffffffff},
+ {0x00008398, 0x00000000},
+ {0x0000839c, 0x00000000},
+ {0x000083a4, 0x0000fa14},
+ {0x000083a8, 0x000f0c00},
+ {0x000083ac, 0x33332210},
+ {0x000083b0, 0x33332210},
+ {0x000083b4, 0x33332210},
+ {0x000083b8, 0x33332210},
+ {0x000083bc, 0x00000000},
+ {0x000083c0, 0x00000000},
+ {0x000083c4, 0x00000000},
+ {0x000083c8, 0x00000000},
+ {0x000083cc, 0x00000200},
+ {0x000083d0, 0x000301ff},
+};
+
+static const u32 ar9462_2p0_mac_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b},
+ {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+ {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+ {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
+};
+
+static const u32 ar9462_common_mixed_rx_gain_table_2p0[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x03820190},
+ {0x0000a030, 0x03840383},
+ {0x0000a034, 0x03880385},
+ {0x0000a038, 0x038a0389},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x29292929},
+ {0x0000a084, 0x29292929},
+ {0x0000a088, 0x29292929},
+ {0x0000a08c, 0x29292929},
+ {0x0000a090, 0x22292929},
+ {0x0000a094, 0x1d1d2222},
+ {0x0000a098, 0x0c111117},
+ {0x0000a09c, 0x00030303},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+ {0x0000b000, 0x00010000},
+ {0x0000b004, 0x00030002},
+ {0x0000b008, 0x00050004},
+ {0x0000b00c, 0x00810080},
+ {0x0000b010, 0x00830082},
+ {0x0000b014, 0x01810180},
+ {0x0000b018, 0x01830182},
+ {0x0000b01c, 0x01850184},
+ {0x0000b020, 0x02810280},
+ {0x0000b024, 0x02830282},
+ {0x0000b028, 0x02850284},
+ {0x0000b02c, 0x02890288},
+ {0x0000b030, 0x028b028a},
+ {0x0000b034, 0x0388028c},
+ {0x0000b038, 0x038a0389},
+ {0x0000b03c, 0x038c038b},
+ {0x0000b040, 0x0390038d},
+ {0x0000b044, 0x03920391},
+ {0x0000b048, 0x03940393},
+ {0x0000b04c, 0x03960395},
+ {0x0000b050, 0x00000000},
+ {0x0000b054, 0x00000000},
+ {0x0000b058, 0x00000000},
+ {0x0000b05c, 0x00000000},
+ {0x0000b060, 0x00000000},
+ {0x0000b064, 0x00000000},
+ {0x0000b068, 0x00000000},
+ {0x0000b06c, 0x00000000},
+ {0x0000b070, 0x00000000},
+ {0x0000b074, 0x00000000},
+ {0x0000b078, 0x00000000},
+ {0x0000b07c, 0x00000000},
+ {0x0000b080, 0x2a2d2f32},
+ {0x0000b084, 0x21232328},
+ {0x0000b088, 0x19191c1e},
+ {0x0000b08c, 0x12141417},
+ {0x0000b090, 0x07070e0e},
+ {0x0000b094, 0x03030305},
+ {0x0000b098, 0x00000003},
+ {0x0000b09c, 0x00000000},
+ {0x0000b0a0, 0x00000000},
+ {0x0000b0a4, 0x00000000},
+ {0x0000b0a8, 0x00000000},
+ {0x0000b0ac, 0x00000000},
+ {0x0000b0b0, 0x00000000},
+ {0x0000b0b4, 0x00000000},
+ {0x0000b0b8, 0x00000000},
+ {0x0000b0bc, 0x00000000},
+ {0x0000b0c0, 0x003f0020},
+ {0x0000b0c4, 0x00400041},
+ {0x0000b0c8, 0x0140005f},
+ {0x0000b0cc, 0x0160015f},
+ {0x0000b0d0, 0x017e017f},
+ {0x0000b0d4, 0x02410242},
+ {0x0000b0d8, 0x025f0240},
+ {0x0000b0dc, 0x027f0260},
+ {0x0000b0e0, 0x0341027e},
+ {0x0000b0e4, 0x035f0340},
+ {0x0000b0e8, 0x037f0360},
+ {0x0000b0ec, 0x04400441},
+ {0x0000b0f0, 0x0460045f},
+ {0x0000b0f4, 0x0541047f},
+ {0x0000b0f8, 0x055f0540},
+ {0x0000b0fc, 0x057f0560},
+ {0x0000b100, 0x06400641},
+ {0x0000b104, 0x0660065f},
+ {0x0000b108, 0x067e067f},
+ {0x0000b10c, 0x07410742},
+ {0x0000b110, 0x075f0740},
+ {0x0000b114, 0x077f0760},
+ {0x0000b118, 0x07800781},
+ {0x0000b11c, 0x07a0079f},
+ {0x0000b120, 0x07c107bf},
+ {0x0000b124, 0x000007c0},
+ {0x0000b128, 0x00000000},
+ {0x0000b12c, 0x00000000},
+ {0x0000b130, 0x00000000},
+ {0x0000b134, 0x00000000},
+ {0x0000b138, 0x00000000},
+ {0x0000b13c, 0x00000000},
+ {0x0000b140, 0x003f0020},
+ {0x0000b144, 0x00400041},
+ {0x0000b148, 0x0140005f},
+ {0x0000b14c, 0x0160015f},
+ {0x0000b150, 0x017e017f},
+ {0x0000b154, 0x02410242},
+ {0x0000b158, 0x025f0240},
+ {0x0000b15c, 0x027f0260},
+ {0x0000b160, 0x0341027e},
+ {0x0000b164, 0x035f0340},
+ {0x0000b168, 0x037f0360},
+ {0x0000b16c, 0x04400441},
+ {0x0000b170, 0x0460045f},
+ {0x0000b174, 0x0541047f},
+ {0x0000b178, 0x055f0540},
+ {0x0000b17c, 0x057f0560},
+ {0x0000b180, 0x06400641},
+ {0x0000b184, 0x0660065f},
+ {0x0000b188, 0x067e067f},
+ {0x0000b18c, 0x07410742},
+ {0x0000b190, 0x075f0740},
+ {0x0000b194, 0x077f0760},
+ {0x0000b198, 0x07800781},
+ {0x0000b19c, 0x07a0079f},
+ {0x0000b1a0, 0x07c107bf},
+ {0x0000b1a4, 0x000007c0},
+ {0x0000b1a8, 0x00000000},
+ {0x0000b1ac, 0x00000000},
+ {0x0000b1b0, 0x00000000},
+ {0x0000b1b4, 0x00000000},
+ {0x0000b1b8, 0x00000000},
+ {0x0000b1bc, 0x00000000},
+ {0x0000b1c0, 0x00000000},
+ {0x0000b1c4, 0x00000000},
+ {0x0000b1c8, 0x00000000},
+ {0x0000b1cc, 0x00000000},
+ {0x0000b1d0, 0x00000000},
+ {0x0000b1d4, 0x00000000},
+ {0x0000b1d8, 0x00000000},
+ {0x0000b1dc, 0x00000000},
+ {0x0000b1e0, 0x00000000},
+ {0x0000b1e4, 0x00000000},
+ {0x0000b1e8, 0x00000000},
+ {0x0000b1ec, 0x00000000},
+ {0x0000b1f0, 0x00000396},
+ {0x0000b1f4, 0x00000396},
+ {0x0000b1f8, 0x00000396},
+ {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_modes_green_ob_db_tx_gain_table_2p0[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003},
+ {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+ {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000},
+ {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
+ {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400},
+ {0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402},
+ {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404},
+ {0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603},
+ {0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02},
+ {0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04},
+ {0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20},
+ {0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20},
+ {0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22},
+ {0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24},
+ {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640},
+ {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660},
+ {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861},
+ {0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81},
+ {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83},
+ {0x0000a550, 0x5f025ef6, 0x5f025ef6, 0x44001c84, 0x44001c84},
+ {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3},
+ {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9},
+ {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb},
+ {0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
+ {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
+ {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x00016044, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4},
+ {0x00016048, 0x8db49060, 0x8db49060, 0x8db49060, 0x8db49060},
+ {0x00016054, 0x6db60180, 0x6db60180, 0x6db60180, 0x6db60180},
+ {0x00016444, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4},
+ {0x00016448, 0x8db49000, 0x8db49000, 0x8db49000, 0x8db49000},
+ {0x00016454, 0x6db60180, 0x6db60180, 0x6db60180, 0x6db60180},
+};
+
+static const u32 ar9462_2p0_BTCOEX_MAX_TXPWR_table[][2] = {
+ /* Addr allmodes */
+ {0x000018c0, 0x10101010},
+ {0x000018c4, 0x10101010},
+ {0x000018c8, 0x10101010},
+ {0x000018cc, 0x10101010},
+ {0x000018d0, 0x10101010},
+ {0x000018d4, 0x10101010},
+ {0x000018d8, 0x10101010},
+ {0x000018dc, 0x10101010},
+};
+
+static const u32 ar9462_2p0_baseband_core_emulation[][2] = {
+ /* Addr allmodes */
+ {0x00009800, 0xafa68e30},
+ {0x00009884, 0x00002842},
+ {0x00009c04, 0xff55ff55},
+ {0x00009c08, 0x0320ff55},
+ {0x00009e50, 0x00000000},
+ {0x00009fcc, 0x00000014},
+ {0x0000a344, 0x00000010},
+ {0x0000a398, 0x00000000},
+ {0x0000a39c, 0x71733d01},
+ {0x0000a3a0, 0xd0ad5c12},
+ {0x0000a3c0, 0x22222220},
+ {0x0000a3c4, 0x22222222},
+ {0x0000a404, 0x00418a11},
+ {0x0000a418, 0x050001ce},
+ {0x0000a438, 0x00001800},
+ {0x0000a458, 0x01444452},
+ {0x0000a644, 0x3fad9d74},
+ {0x0000a690, 0x00000038},
+};
+
+#endif /* INITVALS_9462_2P0_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h
index 611ea6ce8508..d16d029f81a9 100644
--- a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h
@@ -521,7 +521,7 @@ static const u32 ar9485_1_1_radio_postamble[][2] = {
{0x000160ac, 0x24611800},
{0x000160b0, 0x03284f3e},
{0x0001610c, 0x00170000},
- {0x00016140, 0x10804008},
+ {0x00016140, 0x50804008},
};
static const u32 ar9485_1_1_mac_postamble[][5] = {
@@ -603,7 +603,7 @@ static const u32 ar9485_1_1_radio_core[][2] = {
static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = {
/* Addr allmodes */
- {0x00018c00, 0x10052e5e},
+ {0x00018c00, 0x18052e5e},
{0x00018c04, 0x000801d8},
{0x00018c08, 0x0000080c},
};
@@ -776,7 +776,7 @@ static const u32 ar9485_modes_green_ob_db_tx_gain_1_1[][5] = {
static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = {
/* Addr allmodes */
- {0x00018c00, 0x10013e5e},
+ {0x00018c00, 0x18013e5e},
{0x00018c04, 0x000801d8},
{0x00018c08, 0x0000080c},
};
@@ -882,7 +882,7 @@ static const u32 ar9485_fast_clock_1_1_baseband_postamble[][3] = {
static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = {
/* Addr allmodes */
- {0x00018c00, 0x10012e5e},
+ {0x00018c00, 0x18012e5e},
{0x00018c04, 0x000801d8},
{0x00018c08, 0x0000080c},
};
@@ -1021,7 +1021,7 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = {
static const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = {
/* Addr allmodes */
- {0x00018c00, 0x10053e5e},
+ {0x00018c00, 0x18053e5e},
{0x00018c04, 0x000801d8},
{0x00018c08, 0x0000080c},
};
diff --git a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
new file mode 100644
index 000000000000..06b3f0df9fad
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
@@ -0,0 +1,1673 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INITVALS_9580_1P0_H
+#define INITVALS_9580_1P0_H
+
+/* AR9580 1.0 */
+
+static const u32 ar9580_1p0_modes_fast_clock[][3] = {
+ /* Addr 5G_HT20 5G_HT40 */
+ {0x00001030, 0x00000268, 0x000004d0},
+ {0x00001070, 0x0000018c, 0x00000318},
+ {0x000010b0, 0x00000fd0, 0x00001fa0},
+ {0x00008014, 0x044c044c, 0x08980898},
+ {0x0000801c, 0x148ec02b, 0x148ec057},
+ {0x00008318, 0x000044c0, 0x00008980},
+ {0x00009e00, 0x0372131c, 0x0372131c},
+ {0x0000a230, 0x0000000b, 0x00000016},
+ {0x0000a254, 0x00000898, 0x00001130},
+};
+
+static const u32 ar9580_1p0_radio_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0001609c, 0x0dd08f29, 0x0dd08f29, 0x0b283f31, 0x0b283f31},
+ {0x000160ac, 0xa4653c00, 0xa4653c00, 0x24652800, 0x24652800},
+ {0x000160b0, 0x03284f3e, 0x03284f3e, 0x05d08f20, 0x05d08f20},
+ {0x0001610c, 0x08000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008},
+ {0x0001650c, 0x08000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008},
+ {0x0001690c, 0x08000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00016940, 0x10804008, 0x10804008, 0x50804008, 0x50804008},
+};
+
+static const u32 ar9580_1p0_baseband_core[][2] = {
+ /* Addr allmodes */
+ {0x00009800, 0xafe68e30},
+ {0x00009804, 0xfd14e000},
+ {0x00009808, 0x9c0a9f6b},
+ {0x0000980c, 0x04900000},
+ {0x00009814, 0x3280c00a},
+ {0x00009818, 0x00000000},
+ {0x0000981c, 0x00020028},
+ {0x00009834, 0x6400a290},
+ {0x00009838, 0x0108ecff},
+ {0x0000983c, 0x0d000600},
+ {0x00009880, 0x201fff00},
+ {0x00009884, 0x00001042},
+ {0x000098a4, 0x00200400},
+ {0x000098b0, 0x32840bbe},
+ {0x000098d0, 0x004b6a8e},
+ {0x000098d4, 0x00000820},
+ {0x000098dc, 0x00000000},
+ {0x000098f0, 0x00000000},
+ {0x000098f4, 0x00000000},
+ {0x00009c04, 0xff55ff55},
+ {0x00009c08, 0x0320ff55},
+ {0x00009c0c, 0x00000000},
+ {0x00009c10, 0x00000000},
+ {0x00009c14, 0x00046384},
+ {0x00009c18, 0x05b6b440},
+ {0x00009c1c, 0x00b6b440},
+ {0x00009d00, 0xc080a333},
+ {0x00009d04, 0x40206c10},
+ {0x00009d08, 0x009c4060},
+ {0x00009d0c, 0x9883800a},
+ {0x00009d10, 0x01834061},
+ {0x00009d14, 0x00c0040b},
+ {0x00009d18, 0x00000000},
+ {0x00009e08, 0x0038230c},
+ {0x00009e24, 0x990bb515},
+ {0x00009e28, 0x0c6f0000},
+ {0x00009e30, 0x06336f77},
+ {0x00009e34, 0x6af6532f},
+ {0x00009e38, 0x0cc80c00},
+ {0x00009e40, 0x0d261820},
+ {0x00009e4c, 0x00001004},
+ {0x00009e50, 0x00ff03f1},
+ {0x00009e54, 0x00000000},
+ {0x00009fc0, 0x803e4788},
+ {0x00009fc4, 0x0001efb5},
+ {0x00009fcc, 0x40000014},
+ {0x00009fd0, 0x01193b93},
+ {0x0000a20c, 0x00000000},
+ {0x0000a220, 0x00000000},
+ {0x0000a224, 0x00000000},
+ {0x0000a228, 0x10002310},
+ {0x0000a23c, 0x00000000},
+ {0x0000a244, 0x0c000000},
+ {0x0000a2a0, 0x00000001},
+ {0x0000a2c0, 0x00000001},
+ {0x0000a2c8, 0x00000000},
+ {0x0000a2cc, 0x18c43433},
+ {0x0000a2d4, 0x00000000},
+ {0x0000a2ec, 0x00000000},
+ {0x0000a2f0, 0x00000000},
+ {0x0000a2f4, 0x00000000},
+ {0x0000a2f8, 0x00000000},
+ {0x0000a344, 0x00000000},
+ {0x0000a34c, 0x00000000},
+ {0x0000a350, 0x0000a000},
+ {0x0000a364, 0x00000000},
+ {0x0000a370, 0x00000000},
+ {0x0000a390, 0x00000001},
+ {0x0000a394, 0x00000444},
+ {0x0000a398, 0x001f0e0f},
+ {0x0000a39c, 0x0075393f},
+ {0x0000a3a0, 0xb79f6427},
+ {0x0000a3a4, 0x00000000},
+ {0x0000a3a8, 0xaaaaaaaa},
+ {0x0000a3ac, 0x3c466478},
+ {0x0000a3c0, 0x20202020},
+ {0x0000a3c4, 0x22222220},
+ {0x0000a3c8, 0x20200020},
+ {0x0000a3cc, 0x20202020},
+ {0x0000a3d0, 0x20202020},
+ {0x0000a3d4, 0x20202020},
+ {0x0000a3d8, 0x20202020},
+ {0x0000a3dc, 0x20202020},
+ {0x0000a3e0, 0x20202020},
+ {0x0000a3e4, 0x20202020},
+ {0x0000a3e8, 0x20202020},
+ {0x0000a3ec, 0x20202020},
+ {0x0000a3f0, 0x00000000},
+ {0x0000a3f4, 0x00000000},
+ {0x0000a3f8, 0x0c9bd380},
+ {0x0000a3fc, 0x000f0f01},
+ {0x0000a400, 0x8fa91f01},
+ {0x0000a404, 0x00000000},
+ {0x0000a408, 0x0e79e5c6},
+ {0x0000a40c, 0x00820820},
+ {0x0000a414, 0x1ce739ce},
+ {0x0000a418, 0x2d001dce},
+ {0x0000a41c, 0x1ce739ce},
+ {0x0000a420, 0x000001ce},
+ {0x0000a424, 0x1ce739ce},
+ {0x0000a428, 0x000001ce},
+ {0x0000a42c, 0x1ce739ce},
+ {0x0000a430, 0x1ce739ce},
+ {0x0000a434, 0x00000000},
+ {0x0000a438, 0x00001801},
+ {0x0000a43c, 0x00100000},
+ {0x0000a440, 0x00000000},
+ {0x0000a444, 0x00000000},
+ {0x0000a448, 0x05000080},
+ {0x0000a44c, 0x00000001},
+ {0x0000a450, 0x00010000},
+ {0x0000a458, 0x00000000},
+ {0x0000a640, 0x00000000},
+ {0x0000a644, 0x3fad9d74},
+ {0x0000a648, 0x0048060a},
+ {0x0000a64c, 0x00003c37},
+ {0x0000a670, 0x03020100},
+ {0x0000a674, 0x09080504},
+ {0x0000a678, 0x0d0c0b0a},
+ {0x0000a67c, 0x13121110},
+ {0x0000a680, 0x31301514},
+ {0x0000a684, 0x35343332},
+ {0x0000a688, 0x00000036},
+ {0x0000a690, 0x00000838},
+ {0x0000a7c0, 0x00000000},
+ {0x0000a7c4, 0xfffffffc},
+ {0x0000a7c8, 0x00000000},
+ {0x0000a7cc, 0x00000000},
+ {0x0000a7d0, 0x00000000},
+ {0x0000a7d4, 0x00000004},
+ {0x0000a7dc, 0x00000000},
+ {0x0000a8d0, 0x004b6a8e},
+ {0x0000a8d4, 0x00000820},
+ {0x0000a8dc, 0x00000000},
+ {0x0000a8f0, 0x00000000},
+ {0x0000a8f4, 0x00000000},
+ {0x0000b2d0, 0x00000080},
+ {0x0000b2d4, 0x00000000},
+ {0x0000b2ec, 0x00000000},
+ {0x0000b2f0, 0x00000000},
+ {0x0000b2f4, 0x00000000},
+ {0x0000b2f8, 0x00000000},
+ {0x0000b408, 0x0e79e5c0},
+ {0x0000b40c, 0x00820820},
+ {0x0000b420, 0x00000000},
+ {0x0000b8d0, 0x004b6a8e},
+ {0x0000b8d4, 0x00000820},
+ {0x0000b8dc, 0x00000000},
+ {0x0000b8f0, 0x00000000},
+ {0x0000b8f4, 0x00000000},
+ {0x0000c2d0, 0x00000080},
+ {0x0000c2d4, 0x00000000},
+ {0x0000c2ec, 0x00000000},
+ {0x0000c2f0, 0x00000000},
+ {0x0000c2f4, 0x00000000},
+ {0x0000c2f8, 0x00000000},
+ {0x0000c408, 0x0e79e5c0},
+ {0x0000c40c, 0x00820820},
+ {0x0000c420, 0x00000000},
+};
+
+static const u32 ar9580_1p0_mac_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+ {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+ {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+ {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+ {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b},
+ {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+ {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+ {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
+};
+
+static const u32 ar9580_1p0_low_ob_db_tx_gain_table[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+ {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400},
+ {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402},
+ {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404},
+ {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603},
+ {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02},
+ {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04},
+ {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20},
+ {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20},
+ {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22},
+ {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24},
+ {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640},
+ {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660},
+ {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861},
+ {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81},
+ {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x47001a83, 0x47001a83},
+ {0x0000a550, 0x61024a6c, 0x61024a6c, 0x4a001c84, 0x4a001c84},
+ {0x0000a554, 0x66026a6c, 0x66026a6c, 0x4e001ce3, 0x4e001ce3},
+ {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x52001ce5, 0x52001ce5},
+ {0x0000a55c, 0x7002708c, 0x7002708c, 0x56001ce9, 0x56001ce9},
+ {0x0000a560, 0x7302b08a, 0x7302b08a, 0x5a001ceb, 0x5a001ceb},
+ {0x0000a564, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a568, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a570, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a574, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a578, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
+ {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002},
+ {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004},
+ {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200},
+ {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202},
+ {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400},
+ {0x0000a598, 0x21802220, 0x21802220, 0x16800402, 0x16800402},
+ {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404},
+ {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603},
+ {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02},
+ {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04},
+ {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20},
+ {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20},
+ {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22},
+ {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24},
+ {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640},
+ {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660},
+ {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3f801861, 0x3f801861},
+ {0x0000a5c8, 0x5782286c, 0x5782286c, 0x43801a81, 0x43801a81},
+ {0x0000a5cc, 0x5c82486b, 0x5c82486b, 0x47801a83, 0x47801a83},
+ {0x0000a5d0, 0x61824a6c, 0x61824a6c, 0x4a801c84, 0x4a801c84},
+ {0x0000a5d4, 0x66826a6c, 0x66826a6c, 0x4e801ce3, 0x4e801ce3},
+ {0x0000a5d8, 0x6b826e6c, 0x6b826e6c, 0x52801ce5, 0x52801ce5},
+ {0x0000a5dc, 0x7082708c, 0x7082708c, 0x56801ce9, 0x56801ce9},
+ {0x0000a5e0, 0x7382b08a, 0x7382b08a, 0x5a801ceb, 0x5a801ceb},
+ {0x0000a5e4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5e8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5ec, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f0, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5fc, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501},
+ {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005},
+ {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016048, 0x66480001, 0x66480001, 0x66480001, 0x66480001},
+ {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016448, 0x66480001, 0x66480001, 0x66480001, 0x66480001},
+ {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016848, 0x66480001, 0x66480001, 0x66480001, 0x66480001},
+ {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+};
+
+static const u32 ar9580_1p0_high_power_tx_gain_table[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+ {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400},
+ {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402},
+ {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404},
+ {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603},
+ {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02},
+ {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04},
+ {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20},
+ {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20},
+ {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22},
+ {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24},
+ {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640},
+ {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660},
+ {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861},
+ {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81},
+ {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x47001a83, 0x47001a83},
+ {0x0000a550, 0x61024a6c, 0x61024a6c, 0x4a001c84, 0x4a001c84},
+ {0x0000a554, 0x66026a6c, 0x66026a6c, 0x4e001ce3, 0x4e001ce3},
+ {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x52001ce5, 0x52001ce5},
+ {0x0000a55c, 0x7002708c, 0x7002708c, 0x56001ce9, 0x56001ce9},
+ {0x0000a560, 0x7302b08a, 0x7302b08a, 0x5a001ceb, 0x5a001ceb},
+ {0x0000a564, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a568, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a570, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a574, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a578, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
+ {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002},
+ {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004},
+ {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200},
+ {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202},
+ {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400},
+ {0x0000a598, 0x21802220, 0x21802220, 0x16800402, 0x16800402},
+ {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404},
+ {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603},
+ {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02},
+ {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04},
+ {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20},
+ {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20},
+ {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22},
+ {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24},
+ {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640},
+ {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660},
+ {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3f801861, 0x3f801861},
+ {0x0000a5c8, 0x5782286c, 0x5782286c, 0x43801a81, 0x43801a81},
+ {0x0000a5cc, 0x5c82486b, 0x5c82486b, 0x47801a83, 0x47801a83},
+ {0x0000a5d0, 0x61824a6c, 0x61824a6c, 0x4a801c84, 0x4a801c84},
+ {0x0000a5d4, 0x66826a6c, 0x66826a6c, 0x4e801ce3, 0x4e801ce3},
+ {0x0000a5d8, 0x6b826e6c, 0x6b826e6c, 0x52801ce5, 0x52801ce5},
+ {0x0000a5dc, 0x7082708c, 0x7082708c, 0x56801ce9, 0x56801ce9},
+ {0x0000a5e0, 0x7382b08a, 0x7382b08a, 0x5a801ceb, 0x5a801ceb},
+ {0x0000a5e4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5e8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5ec, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f0, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5fc, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501},
+ {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005},
+ {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016048, 0x66480001, 0x66480001, 0x66480001, 0x66480001},
+ {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016448, 0x66480001, 0x66480001, 0x66480001, 0x66480001},
+ {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016848, 0x66480001, 0x66480001, 0x66480001, 0x66480001},
+ {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+};
+
+static const u32 ar9580_1p0_lowest_ob_db_tx_gain_table[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+ {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400},
+ {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402},
+ {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404},
+ {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603},
+ {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02},
+ {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04},
+ {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20},
+ {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20},
+ {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22},
+ {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24},
+ {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640},
+ {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660},
+ {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861},
+ {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81},
+ {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x47001a83, 0x47001a83},
+ {0x0000a550, 0x61024a6c, 0x61024a6c, 0x4a001c84, 0x4a001c84},
+ {0x0000a554, 0x66026a6c, 0x66026a6c, 0x4e001ce3, 0x4e001ce3},
+ {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x52001ce5, 0x52001ce5},
+ {0x0000a55c, 0x7002708c, 0x7002708c, 0x56001ce9, 0x56001ce9},
+ {0x0000a560, 0x7302b08a, 0x7302b08a, 0x5a001ceb, 0x5a001ceb},
+ {0x0000a564, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a568, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a570, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a574, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a578, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec},
+ {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
+ {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002},
+ {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004},
+ {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200},
+ {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202},
+ {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400},
+ {0x0000a598, 0x21802220, 0x21802220, 0x16800402, 0x16800402},
+ {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404},
+ {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603},
+ {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02},
+ {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04},
+ {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20},
+ {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20},
+ {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22},
+ {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24},
+ {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640},
+ {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660},
+ {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3f801861, 0x3f801861},
+ {0x0000a5c8, 0x5782286c, 0x5782286c, 0x43801a81, 0x43801a81},
+ {0x0000a5cc, 0x5c82486b, 0x5c82486b, 0x47801a83, 0x47801a83},
+ {0x0000a5d0, 0x61824a6c, 0x61824a6c, 0x4a801c84, 0x4a801c84},
+ {0x0000a5d4, 0x66826a6c, 0x66826a6c, 0x4e801ce3, 0x4e801ce3},
+ {0x0000a5d8, 0x6b826e6c, 0x6b826e6c, 0x52801ce5, 0x52801ce5},
+ {0x0000a5dc, 0x7082708c, 0x7082708c, 0x56801ce9, 0x56801ce9},
+ {0x0000a5e0, 0x7382b08a, 0x7382b08a, 0x5a801ceb, 0x5a801ceb},
+ {0x0000a5e4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5e8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5ec, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f0, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5f8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a5fc, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501},
+ {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005},
+ {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016048, 0x62480001, 0x62480001, 0x62480001, 0x62480001},
+ {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016448, 0x62480001, 0x62480001, 0x62480001, 0x62480001},
+ {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+ {0x00016848, 0x62480001, 0x62480001, 0x62480001, 0x62480001},
+ {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+};
+
+static const u32 ar9580_1p0_baseband_core_txfir_coeff_japan_2484[][2] = {
+ /* Addr allmodes */
+ {0x0000a398, 0x00000000},
+ {0x0000a39c, 0x6f7f0301},
+ {0x0000a3a0, 0xca9228ee},
+};
+
+static const u32 ar9580_1p0_mac_core[][2] = {
+ /* Addr allmodes */
+ {0x00000008, 0x00000000},
+ {0x00000030, 0x00020085},
+ {0x00000034, 0x00000005},
+ {0x00000040, 0x00000000},
+ {0x00000044, 0x00000000},
+ {0x00000048, 0x00000008},
+ {0x0000004c, 0x00000010},
+ {0x00000050, 0x00000000},
+ {0x00001040, 0x002ffc0f},
+ {0x00001044, 0x002ffc0f},
+ {0x00001048, 0x002ffc0f},
+ {0x0000104c, 0x002ffc0f},
+ {0x00001050, 0x002ffc0f},
+ {0x00001054, 0x002ffc0f},
+ {0x00001058, 0x002ffc0f},
+ {0x0000105c, 0x002ffc0f},
+ {0x00001060, 0x002ffc0f},
+ {0x00001064, 0x002ffc0f},
+ {0x000010f0, 0x00000100},
+ {0x00001270, 0x00000000},
+ {0x000012b0, 0x00000000},
+ {0x000012f0, 0x00000000},
+ {0x0000143c, 0x00000000},
+ {0x0000147c, 0x00000000},
+ {0x00008000, 0x00000000},
+ {0x00008004, 0x00000000},
+ {0x00008008, 0x00000000},
+ {0x0000800c, 0x00000000},
+ {0x00008018, 0x00000000},
+ {0x00008020, 0x00000000},
+ {0x00008038, 0x00000000},
+ {0x0000803c, 0x00000000},
+ {0x00008040, 0x00000000},
+ {0x00008044, 0x00000000},
+ {0x00008048, 0x00000000},
+ {0x0000804c, 0xffffffff},
+ {0x00008054, 0x00000000},
+ {0x00008058, 0x00000000},
+ {0x0000805c, 0x000fc78f},
+ {0x00008060, 0x0000000f},
+ {0x00008064, 0x00000000},
+ {0x00008070, 0x00000310},
+ {0x00008074, 0x00000020},
+ {0x00008078, 0x00000000},
+ {0x0000809c, 0x0000000f},
+ {0x000080a0, 0x00000000},
+ {0x000080a4, 0x02ff0000},
+ {0x000080a8, 0x0e070605},
+ {0x000080ac, 0x0000000d},
+ {0x000080b0, 0x00000000},
+ {0x000080b4, 0x00000000},
+ {0x000080b8, 0x00000000},
+ {0x000080bc, 0x00000000},
+ {0x000080c0, 0x2a800000},
+ {0x000080c4, 0x06900168},
+ {0x000080c8, 0x13881c22},
+ {0x000080cc, 0x01f40000},
+ {0x000080d0, 0x00252500},
+ {0x000080d4, 0x00a00000},
+ {0x000080d8, 0x00400000},
+ {0x000080dc, 0x00000000},
+ {0x000080e0, 0xffffffff},
+ {0x000080e4, 0x0000ffff},
+ {0x000080e8, 0x3f3f3f3f},
+ {0x000080ec, 0x00000000},
+ {0x000080f0, 0x00000000},
+ {0x000080f4, 0x00000000},
+ {0x000080fc, 0x00020000},
+ {0x00008100, 0x00000000},
+ {0x00008108, 0x00000052},
+ {0x0000810c, 0x00000000},
+ {0x00008110, 0x00000000},
+ {0x00008114, 0x000007ff},
+ {0x00008118, 0x000000aa},
+ {0x0000811c, 0x00003210},
+ {0x00008124, 0x00000000},
+ {0x00008128, 0x00000000},
+ {0x0000812c, 0x00000000},
+ {0x00008130, 0x00000000},
+ {0x00008134, 0x00000000},
+ {0x00008138, 0x00000000},
+ {0x0000813c, 0x0000ffff},
+ {0x00008144, 0xffffffff},
+ {0x00008168, 0x00000000},
+ {0x0000816c, 0x00000000},
+ {0x000081c0, 0x00000000},
+ {0x000081c4, 0x33332210},
+ {0x000081ec, 0x00000000},
+ {0x000081f0, 0x00000000},
+ {0x000081f4, 0x00000000},
+ {0x000081f8, 0x00000000},
+ {0x000081fc, 0x00000000},
+ {0x00008240, 0x00100000},
+ {0x00008244, 0x0010f400},
+ {0x00008248, 0x00000800},
+ {0x0000824c, 0x0001e800},
+ {0x00008250, 0x00000000},
+ {0x00008254, 0x00000000},
+ {0x00008258, 0x00000000},
+ {0x0000825c, 0x40000000},
+ {0x00008260, 0x00080922},
+ {0x00008264, 0x9bc00010},
+ {0x00008268, 0xffffffff},
+ {0x0000826c, 0x0000ffff},
+ {0x00008270, 0x00000000},
+ {0x00008274, 0x40000000},
+ {0x00008278, 0x003e4180},
+ {0x0000827c, 0x00000004},
+ {0x00008284, 0x0000002c},
+ {0x00008288, 0x0000002c},
+ {0x0000828c, 0x000000ff},
+ {0x00008294, 0x00000000},
+ {0x00008298, 0x00000000},
+ {0x0000829c, 0x00000000},
+ {0x00008300, 0x00000140},
+ {0x00008314, 0x00000000},
+ {0x0000831c, 0x0000010d},
+ {0x00008328, 0x00000000},
+ {0x0000832c, 0x00000007},
+ {0x00008330, 0x00000302},
+ {0x00008334, 0x00000700},
+ {0x00008338, 0x00ff0000},
+ {0x0000833c, 0x02400000},
+ {0x00008340, 0x000107ff},
+ {0x00008344, 0xaa48105b},
+ {0x00008348, 0x008f0000},
+ {0x0000835c, 0x00000000},
+ {0x00008360, 0xffffffff},
+ {0x00008364, 0xffffffff},
+ {0x00008368, 0x00000000},
+ {0x00008370, 0x00000000},
+ {0x00008374, 0x000000ff},
+ {0x00008378, 0x00000000},
+ {0x0000837c, 0x00000000},
+ {0x00008380, 0xffffffff},
+ {0x00008384, 0xffffffff},
+ {0x00008390, 0xffffffff},
+ {0x00008394, 0xffffffff},
+ {0x00008398, 0x00000000},
+ {0x0000839c, 0x00000000},
+ {0x000083a0, 0x00000000},
+ {0x000083a4, 0x0000fa14},
+ {0x000083a8, 0x000f0c00},
+ {0x000083ac, 0x33332210},
+ {0x000083b0, 0x33332210},
+ {0x000083b4, 0x33332210},
+ {0x000083b8, 0x33332210},
+ {0x000083bc, 0x00000000},
+ {0x000083c0, 0x00000000},
+ {0x000083c4, 0x00000000},
+ {0x000083c8, 0x00000000},
+ {0x000083cc, 0x00000200},
+ {0x000083d0, 0x000301ff},
+};
+
+static const u32 ar9580_1p0_mixed_ob_db_tx_gain_table[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+ {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+ {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x1c000223, 0x1c000223, 0x11000400, 0x11000400},
+ {0x0000a518, 0x21002220, 0x21002220, 0x15000402, 0x15000402},
+ {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404},
+ {0x0000a520, 0x2b022220, 0x2b022220, 0x1b000603, 0x1b000603},
+ {0x0000a524, 0x2f022222, 0x2f022222, 0x1f000a02, 0x1f000a02},
+ {0x0000a528, 0x34022225, 0x34022225, 0x23000a04, 0x23000a04},
+ {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x26000a20, 0x26000a20},
+ {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2a000e20, 0x2a000e20},
+ {0x0000a534, 0x4202242a, 0x4202242a, 0x2e000e22, 0x2e000e22},
+ {0x0000a538, 0x4702244a, 0x4702244a, 0x31000e24, 0x31000e24},
+ {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x34001640, 0x34001640},
+ {0x0000a540, 0x4e02246c, 0x4e02246c, 0x38001660, 0x38001660},
+ {0x0000a544, 0x5302266c, 0x5302266c, 0x3b001861, 0x3b001861},
+ {0x0000a548, 0x5702286c, 0x5702286c, 0x3e001a81, 0x3e001a81},
+ {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x42001a83, 0x42001a83},
+ {0x0000a550, 0x61024a6c, 0x61024a6c, 0x44001c84, 0x44001c84},
+ {0x0000a554, 0x66026a6c, 0x66026a6c, 0x48001ce3, 0x48001ce3},
+ {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a55c, 0x7002708c, 0x7002708c, 0x50001ce9, 0x50001ce9},
+ {0x0000a560, 0x7302b08a, 0x7302b08a, 0x54001ceb, 0x54001ceb},
+ {0x0000a564, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec},
+ {0x0000a568, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec},
+ {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec},
+ {0x0000a570, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec},
+ {0x0000a574, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec},
+ {0x0000a578, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec},
+ {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec},
+ {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
+ {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002},
+ {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004},
+ {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200},
+ {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202},
+ {0x0000a594, 0x1c800223, 0x1c800223, 0x11800400, 0x11800400},
+ {0x0000a598, 0x21802220, 0x21802220, 0x15800402, 0x15800402},
+ {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404},
+ {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1b800603, 0x1b800603},
+ {0x0000a5a4, 0x2f822222, 0x2f822222, 0x1f800a02, 0x1f800a02},
+ {0x0000a5a8, 0x34822225, 0x34822225, 0x23800a04, 0x23800a04},
+ {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x26800a20, 0x26800a20},
+ {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2a800e20, 0x2a800e20},
+ {0x0000a5b4, 0x4282242a, 0x4282242a, 0x2e800e22, 0x2e800e22},
+ {0x0000a5b8, 0x4782244a, 0x4782244a, 0x31800e24, 0x31800e24},
+ {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x34801640, 0x34801640},
+ {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x38801660, 0x38801660},
+ {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3b801861, 0x3b801861},
+ {0x0000a5c8, 0x5782286c, 0x5782286c, 0x3e801a81, 0x3e801a81},
+ {0x0000a5cc, 0x5c82486b, 0x5c82486b, 0x42801a83, 0x42801a83},
+ {0x0000a5d0, 0x61824a6c, 0x61824a6c, 0x44801c84, 0x44801c84},
+ {0x0000a5d4, 0x66826a6c, 0x66826a6c, 0x48801ce3, 0x48801ce3},
+ {0x0000a5d8, 0x6b826e6c, 0x6b826e6c, 0x4c801ce5, 0x4c801ce5},
+ {0x0000a5dc, 0x7082708c, 0x7082708c, 0x50801ce9, 0x50801ce9},
+ {0x0000a5e0, 0x7382b08a, 0x7382b08a, 0x54801ceb, 0x54801ceb},
+ {0x0000a5e4, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec},
+ {0x0000a5e8, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec},
+ {0x0000a5ec, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec},
+ {0x0000a5f0, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec},
+ {0x0000a5f4, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec},
+ {0x0000a5f8, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec},
+ {0x0000a5fc, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+ {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501},
+ {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005},
+ {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+ {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+ {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x00016044, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4},
+ {0x00016048, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001},
+ {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016444, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4},
+ {0x00016448, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001},
+ {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016844, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4},
+ {0x00016848, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001},
+ {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+};
+
+static const u32 ar9580_1p0_wo_xlna_rx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x03820190},
+ {0x0000a030, 0x03840383},
+ {0x0000a034, 0x03880385},
+ {0x0000a038, 0x038a0389},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x29292929},
+ {0x0000a084, 0x29292929},
+ {0x0000a088, 0x29292929},
+ {0x0000a08c, 0x29292929},
+ {0x0000a090, 0x22292929},
+ {0x0000a094, 0x1d1d2222},
+ {0x0000a098, 0x0c111117},
+ {0x0000a09c, 0x00030303},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+ {0x0000b000, 0x00010000},
+ {0x0000b004, 0x00030002},
+ {0x0000b008, 0x00050004},
+ {0x0000b00c, 0x00810080},
+ {0x0000b010, 0x00830082},
+ {0x0000b014, 0x01810180},
+ {0x0000b018, 0x01830182},
+ {0x0000b01c, 0x01850184},
+ {0x0000b020, 0x02810280},
+ {0x0000b024, 0x02830282},
+ {0x0000b028, 0x02850284},
+ {0x0000b02c, 0x02890288},
+ {0x0000b030, 0x028b028a},
+ {0x0000b034, 0x0388028c},
+ {0x0000b038, 0x038a0389},
+ {0x0000b03c, 0x038c038b},
+ {0x0000b040, 0x0390038d},
+ {0x0000b044, 0x03920391},
+ {0x0000b048, 0x03940393},
+ {0x0000b04c, 0x03960395},
+ {0x0000b050, 0x00000000},
+ {0x0000b054, 0x00000000},
+ {0x0000b058, 0x00000000},
+ {0x0000b05c, 0x00000000},
+ {0x0000b060, 0x00000000},
+ {0x0000b064, 0x00000000},
+ {0x0000b068, 0x00000000},
+ {0x0000b06c, 0x00000000},
+ {0x0000b070, 0x00000000},
+ {0x0000b074, 0x00000000},
+ {0x0000b078, 0x00000000},
+ {0x0000b07c, 0x00000000},
+ {0x0000b080, 0x32323232},
+ {0x0000b084, 0x2f2f3232},
+ {0x0000b088, 0x23282a2d},
+ {0x0000b08c, 0x1c1e2123},
+ {0x0000b090, 0x14171919},
+ {0x0000b094, 0x0e0e1214},
+ {0x0000b098, 0x03050707},
+ {0x0000b09c, 0x00030303},
+ {0x0000b0a0, 0x00000000},
+ {0x0000b0a4, 0x00000000},
+ {0x0000b0a8, 0x00000000},
+ {0x0000b0ac, 0x00000000},
+ {0x0000b0b0, 0x00000000},
+ {0x0000b0b4, 0x00000000},
+ {0x0000b0b8, 0x00000000},
+ {0x0000b0bc, 0x00000000},
+ {0x0000b0c0, 0x003f0020},
+ {0x0000b0c4, 0x00400041},
+ {0x0000b0c8, 0x0140005f},
+ {0x0000b0cc, 0x0160015f},
+ {0x0000b0d0, 0x017e017f},
+ {0x0000b0d4, 0x02410242},
+ {0x0000b0d8, 0x025f0240},
+ {0x0000b0dc, 0x027f0260},
+ {0x0000b0e0, 0x0341027e},
+ {0x0000b0e4, 0x035f0340},
+ {0x0000b0e8, 0x037f0360},
+ {0x0000b0ec, 0x04400441},
+ {0x0000b0f0, 0x0460045f},
+ {0x0000b0f4, 0x0541047f},
+ {0x0000b0f8, 0x055f0540},
+ {0x0000b0fc, 0x057f0560},
+ {0x0000b100, 0x06400641},
+ {0x0000b104, 0x0660065f},
+ {0x0000b108, 0x067e067f},
+ {0x0000b10c, 0x07410742},
+ {0x0000b110, 0x075f0740},
+ {0x0000b114, 0x077f0760},
+ {0x0000b118, 0x07800781},
+ {0x0000b11c, 0x07a0079f},
+ {0x0000b120, 0x07c107bf},
+ {0x0000b124, 0x000007c0},
+ {0x0000b128, 0x00000000},
+ {0x0000b12c, 0x00000000},
+ {0x0000b130, 0x00000000},
+ {0x0000b134, 0x00000000},
+ {0x0000b138, 0x00000000},
+ {0x0000b13c, 0x00000000},
+ {0x0000b140, 0x003f0020},
+ {0x0000b144, 0x00400041},
+ {0x0000b148, 0x0140005f},
+ {0x0000b14c, 0x0160015f},
+ {0x0000b150, 0x017e017f},
+ {0x0000b154, 0x02410242},
+ {0x0000b158, 0x025f0240},
+ {0x0000b15c, 0x027f0260},
+ {0x0000b160, 0x0341027e},
+ {0x0000b164, 0x035f0340},
+ {0x0000b168, 0x037f0360},
+ {0x0000b16c, 0x04400441},
+ {0x0000b170, 0x0460045f},
+ {0x0000b174, 0x0541047f},
+ {0x0000b178, 0x055f0540},
+ {0x0000b17c, 0x057f0560},
+ {0x0000b180, 0x06400641},
+ {0x0000b184, 0x0660065f},
+ {0x0000b188, 0x067e067f},
+ {0x0000b18c, 0x07410742},
+ {0x0000b190, 0x075f0740},
+ {0x0000b194, 0x077f0760},
+ {0x0000b198, 0x07800781},
+ {0x0000b19c, 0x07a0079f},
+ {0x0000b1a0, 0x07c107bf},
+ {0x0000b1a4, 0x000007c0},
+ {0x0000b1a8, 0x00000000},
+ {0x0000b1ac, 0x00000000},
+ {0x0000b1b0, 0x00000000},
+ {0x0000b1b4, 0x00000000},
+ {0x0000b1b8, 0x00000000},
+ {0x0000b1bc, 0x00000000},
+ {0x0000b1c0, 0x00000000},
+ {0x0000b1c4, 0x00000000},
+ {0x0000b1c8, 0x00000000},
+ {0x0000b1cc, 0x00000000},
+ {0x0000b1d0, 0x00000000},
+ {0x0000b1d4, 0x00000000},
+ {0x0000b1d8, 0x00000000},
+ {0x0000b1dc, 0x00000000},
+ {0x0000b1e0, 0x00000000},
+ {0x0000b1e4, 0x00000000},
+ {0x0000b1e8, 0x00000000},
+ {0x0000b1ec, 0x00000000},
+ {0x0000b1f0, 0x00000396},
+ {0x0000b1f4, 0x00000396},
+ {0x0000b1f8, 0x00000396},
+ {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9580_1p0_soc_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023},
+};
+
+static const u32 ar9580_1p0_high_ob_db_tx_gain_table[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+ {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+ {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9},
+ {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
+ {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002},
+ {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004},
+ {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200},
+ {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202},
+ {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400},
+ {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402},
+ {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404},
+ {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603},
+ {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02},
+ {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04},
+ {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20},
+ {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20},
+ {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22},
+ {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24},
+ {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640},
+ {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660},
+ {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861},
+ {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81},
+ {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83},
+ {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84},
+ {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3},
+ {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5},
+ {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9},
+ {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb},
+ {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
+ {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
+ {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
+ {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
+ {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
+ {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
+ {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
+ {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000},
+ {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002},
+ {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004},
+ {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200},
+ {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202},
+ {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400},
+ {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402},
+ {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404},
+ {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603},
+ {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02},
+ {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04},
+ {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20},
+ {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20},
+ {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22},
+ {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24},
+ {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640},
+ {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660},
+ {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861},
+ {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81},
+ {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83},
+ {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84},
+ {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3},
+ {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5},
+ {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9},
+ {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb},
+ {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
+ {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
+ {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
+ {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
+ {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
+ {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
+ {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
+ {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
+ {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
+ {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
+ {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
+ {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
+ {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
+ {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+ {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+ {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+ {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x0000c2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+ {0x0000c2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+ {0x0000c2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+ {0x00016044, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4},
+ {0x00016048, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001},
+ {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016444, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4},
+ {0x00016448, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001},
+ {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+ {0x00016844, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4},
+ {0x00016848, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001},
+ {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+};
+
+static const u32 ar9580_1p0_soc_preamble[][2] = {
+ /* Addr allmodes */
+ {0x000040a4, 0x00a0c1c9},
+ {0x00007008, 0x00000000},
+ {0x00007020, 0x00000000},
+ {0x00007034, 0x00000002},
+ {0x00007038, 0x000004c2},
+ {0x00007048, 0x00000008},
+};
+
+static const u32 ar9580_1p0_rx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x01910190},
+ {0x0000a030, 0x01930192},
+ {0x0000a034, 0x01950194},
+ {0x0000a038, 0x038a0196},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x22222229},
+ {0x0000a084, 0x1d1d1d1d},
+ {0x0000a088, 0x1d1d1d1d},
+ {0x0000a08c, 0x1d1d1d1d},
+ {0x0000a090, 0x171d1d1d},
+ {0x0000a094, 0x11111717},
+ {0x0000a098, 0x00030311},
+ {0x0000a09c, 0x00000000},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+ {0x0000b000, 0x00010000},
+ {0x0000b004, 0x00030002},
+ {0x0000b008, 0x00050004},
+ {0x0000b00c, 0x00810080},
+ {0x0000b010, 0x00830082},
+ {0x0000b014, 0x01810180},
+ {0x0000b018, 0x01830182},
+ {0x0000b01c, 0x01850184},
+ {0x0000b020, 0x02810280},
+ {0x0000b024, 0x02830282},
+ {0x0000b028, 0x02850284},
+ {0x0000b02c, 0x02890288},
+ {0x0000b030, 0x028b028a},
+ {0x0000b034, 0x0388028c},
+ {0x0000b038, 0x038a0389},
+ {0x0000b03c, 0x038c038b},
+ {0x0000b040, 0x0390038d},
+ {0x0000b044, 0x03920391},
+ {0x0000b048, 0x03940393},
+ {0x0000b04c, 0x03960395},
+ {0x0000b050, 0x00000000},
+ {0x0000b054, 0x00000000},
+ {0x0000b058, 0x00000000},
+ {0x0000b05c, 0x00000000},
+ {0x0000b060, 0x00000000},
+ {0x0000b064, 0x00000000},
+ {0x0000b068, 0x00000000},
+ {0x0000b06c, 0x00000000},
+ {0x0000b070, 0x00000000},
+ {0x0000b074, 0x00000000},
+ {0x0000b078, 0x00000000},
+ {0x0000b07c, 0x00000000},
+ {0x0000b080, 0x2a2d2f32},
+ {0x0000b084, 0x21232328},
+ {0x0000b088, 0x19191c1e},
+ {0x0000b08c, 0x12141417},
+ {0x0000b090, 0x07070e0e},
+ {0x0000b094, 0x03030305},
+ {0x0000b098, 0x00000003},
+ {0x0000b09c, 0x00000000},
+ {0x0000b0a0, 0x00000000},
+ {0x0000b0a4, 0x00000000},
+ {0x0000b0a8, 0x00000000},
+ {0x0000b0ac, 0x00000000},
+ {0x0000b0b0, 0x00000000},
+ {0x0000b0b4, 0x00000000},
+ {0x0000b0b8, 0x00000000},
+ {0x0000b0bc, 0x00000000},
+ {0x0000b0c0, 0x003f0020},
+ {0x0000b0c4, 0x00400041},
+ {0x0000b0c8, 0x0140005f},
+ {0x0000b0cc, 0x0160015f},
+ {0x0000b0d0, 0x017e017f},
+ {0x0000b0d4, 0x02410242},
+ {0x0000b0d8, 0x025f0240},
+ {0x0000b0dc, 0x027f0260},
+ {0x0000b0e0, 0x0341027e},
+ {0x0000b0e4, 0x035f0340},
+ {0x0000b0e8, 0x037f0360},
+ {0x0000b0ec, 0x04400441},
+ {0x0000b0f0, 0x0460045f},
+ {0x0000b0f4, 0x0541047f},
+ {0x0000b0f8, 0x055f0540},
+ {0x0000b0fc, 0x057f0560},
+ {0x0000b100, 0x06400641},
+ {0x0000b104, 0x0660065f},
+ {0x0000b108, 0x067e067f},
+ {0x0000b10c, 0x07410742},
+ {0x0000b110, 0x075f0740},
+ {0x0000b114, 0x077f0760},
+ {0x0000b118, 0x07800781},
+ {0x0000b11c, 0x07a0079f},
+ {0x0000b120, 0x07c107bf},
+ {0x0000b124, 0x000007c0},
+ {0x0000b128, 0x00000000},
+ {0x0000b12c, 0x00000000},
+ {0x0000b130, 0x00000000},
+ {0x0000b134, 0x00000000},
+ {0x0000b138, 0x00000000},
+ {0x0000b13c, 0x00000000},
+ {0x0000b140, 0x003f0020},
+ {0x0000b144, 0x00400041},
+ {0x0000b148, 0x0140005f},
+ {0x0000b14c, 0x0160015f},
+ {0x0000b150, 0x017e017f},
+ {0x0000b154, 0x02410242},
+ {0x0000b158, 0x025f0240},
+ {0x0000b15c, 0x027f0260},
+ {0x0000b160, 0x0341027e},
+ {0x0000b164, 0x035f0340},
+ {0x0000b168, 0x037f0360},
+ {0x0000b16c, 0x04400441},
+ {0x0000b170, 0x0460045f},
+ {0x0000b174, 0x0541047f},
+ {0x0000b178, 0x055f0540},
+ {0x0000b17c, 0x057f0560},
+ {0x0000b180, 0x06400641},
+ {0x0000b184, 0x0660065f},
+ {0x0000b188, 0x067e067f},
+ {0x0000b18c, 0x07410742},
+ {0x0000b190, 0x075f0740},
+ {0x0000b194, 0x077f0760},
+ {0x0000b198, 0x07800781},
+ {0x0000b19c, 0x07a0079f},
+ {0x0000b1a0, 0x07c107bf},
+ {0x0000b1a4, 0x000007c0},
+ {0x0000b1a8, 0x00000000},
+ {0x0000b1ac, 0x00000000},
+ {0x0000b1b0, 0x00000000},
+ {0x0000b1b4, 0x00000000},
+ {0x0000b1b8, 0x00000000},
+ {0x0000b1bc, 0x00000000},
+ {0x0000b1c0, 0x00000000},
+ {0x0000b1c4, 0x00000000},
+ {0x0000b1c8, 0x00000000},
+ {0x0000b1cc, 0x00000000},
+ {0x0000b1d0, 0x00000000},
+ {0x0000b1d4, 0x00000000},
+ {0x0000b1d8, 0x00000000},
+ {0x0000b1dc, 0x00000000},
+ {0x0000b1e0, 0x00000000},
+ {0x0000b1e4, 0x00000000},
+ {0x0000b1e8, 0x00000000},
+ {0x0000b1ec, 0x00000000},
+ {0x0000b1f0, 0x00000396},
+ {0x0000b1f4, 0x00000396},
+ {0x0000b1f8, 0x00000396},
+ {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9580_1p0_radio_core[][2] = {
+ /* Addr allmodes */
+ {0x00016000, 0x36db6db6},
+ {0x00016004, 0x6db6db40},
+ {0x00016008, 0x73f00000},
+ {0x0001600c, 0x00000000},
+ {0x00016040, 0x7f80fff8},
+ {0x0001604c, 0x76d005b5},
+ {0x00016050, 0x556cf031},
+ {0x00016054, 0x13449440},
+ {0x00016058, 0x0c51c92c},
+ {0x0001605c, 0x3db7fffc},
+ {0x00016060, 0xfffffffc},
+ {0x00016064, 0x000f0278},
+ {0x0001606c, 0x6db60000},
+ {0x00016080, 0x00000000},
+ {0x00016084, 0x0e48048c},
+ {0x00016088, 0x54214514},
+ {0x0001608c, 0x119f481e},
+ {0x00016090, 0x24926490},
+ {0x00016098, 0xd2888888},
+ {0x000160a0, 0x0a108ffe},
+ {0x000160a4, 0x812fc370},
+ {0x000160a8, 0x423c8000},
+ {0x000160b4, 0x92480080},
+ {0x000160c0, 0x00adb6d0},
+ {0x000160c4, 0x6db6db60},
+ {0x000160c8, 0x6db6db6c},
+ {0x000160cc, 0x01e6c000},
+ {0x00016100, 0x3fffbe01},
+ {0x00016104, 0xfff80000},
+ {0x00016108, 0x00080010},
+ {0x00016144, 0x02084080},
+ {0x00016148, 0x00000000},
+ {0x00016280, 0x058a0001},
+ {0x00016284, 0x3d840208},
+ {0x00016288, 0x05a20408},
+ {0x0001628c, 0x00038c07},
+ {0x00016290, 0x00000004},
+ {0x00016294, 0x458aa14f},
+ {0x00016380, 0x00000000},
+ {0x00016384, 0x00000000},
+ {0x00016388, 0x00800700},
+ {0x0001638c, 0x00800700},
+ {0x00016390, 0x00800700},
+ {0x00016394, 0x00000000},
+ {0x00016398, 0x00000000},
+ {0x0001639c, 0x00000000},
+ {0x000163a0, 0x00000001},
+ {0x000163a4, 0x00000001},
+ {0x000163a8, 0x00000000},
+ {0x000163ac, 0x00000000},
+ {0x000163b0, 0x00000000},
+ {0x000163b4, 0x00000000},
+ {0x000163b8, 0x00000000},
+ {0x000163bc, 0x00000000},
+ {0x000163c0, 0x000000a0},
+ {0x000163c4, 0x000c0000},
+ {0x000163c8, 0x14021402},
+ {0x000163cc, 0x00001402},
+ {0x000163d0, 0x00000000},
+ {0x000163d4, 0x00000000},
+ {0x00016400, 0x36db6db6},
+ {0x00016404, 0x6db6db40},
+ {0x00016408, 0x73f00000},
+ {0x0001640c, 0x00000000},
+ {0x00016440, 0x7f80fff8},
+ {0x0001644c, 0x76d005b5},
+ {0x00016450, 0x556cf031},
+ {0x00016454, 0x13449440},
+ {0x00016458, 0x0c51c92c},
+ {0x0001645c, 0x3db7fffc},
+ {0x00016460, 0xfffffffc},
+ {0x00016464, 0x000f0278},
+ {0x0001646c, 0x6db60000},
+ {0x00016500, 0x3fffbe01},
+ {0x00016504, 0xfff80000},
+ {0x00016508, 0x00080010},
+ {0x00016544, 0x02084080},
+ {0x00016548, 0x00000000},
+ {0x00016780, 0x00000000},
+ {0x00016784, 0x00000000},
+ {0x00016788, 0x00800700},
+ {0x0001678c, 0x00800700},
+ {0x00016790, 0x00800700},
+ {0x00016794, 0x00000000},
+ {0x00016798, 0x00000000},
+ {0x0001679c, 0x00000000},
+ {0x000167a0, 0x00000001},
+ {0x000167a4, 0x00000001},
+ {0x000167a8, 0x00000000},
+ {0x000167ac, 0x00000000},
+ {0x000167b0, 0x00000000},
+ {0x000167b4, 0x00000000},
+ {0x000167b8, 0x00000000},
+ {0x000167bc, 0x00000000},
+ {0x000167c0, 0x000000a0},
+ {0x000167c4, 0x000c0000},
+ {0x000167c8, 0x14021402},
+ {0x000167cc, 0x00001402},
+ {0x000167d0, 0x00000000},
+ {0x000167d4, 0x00000000},
+ {0x00016800, 0x36db6db6},
+ {0x00016804, 0x6db6db40},
+ {0x00016808, 0x73f00000},
+ {0x0001680c, 0x00000000},
+ {0x00016840, 0x7f80fff8},
+ {0x0001684c, 0x76d005b5},
+ {0x00016850, 0x556cf031},
+ {0x00016854, 0x13449440},
+ {0x00016858, 0x0c51c92c},
+ {0x0001685c, 0x3db7fffc},
+ {0x00016860, 0xfffffffc},
+ {0x00016864, 0x000f0278},
+ {0x0001686c, 0x6db60000},
+ {0x00016900, 0x3fffbe01},
+ {0x00016904, 0xfff80000},
+ {0x00016908, 0x00080010},
+ {0x00016944, 0x02084080},
+ {0x00016948, 0x00000000},
+ {0x00016b80, 0x00000000},
+ {0x00016b84, 0x00000000},
+ {0x00016b88, 0x00800700},
+ {0x00016b8c, 0x00800700},
+ {0x00016b90, 0x00800700},
+ {0x00016b94, 0x00000000},
+ {0x00016b98, 0x00000000},
+ {0x00016b9c, 0x00000000},
+ {0x00016ba0, 0x00000001},
+ {0x00016ba4, 0x00000001},
+ {0x00016ba8, 0x00000000},
+ {0x00016bac, 0x00000000},
+ {0x00016bb0, 0x00000000},
+ {0x00016bb4, 0x00000000},
+ {0x00016bb8, 0x00000000},
+ {0x00016bbc, 0x00000000},
+ {0x00016bc0, 0x000000a0},
+ {0x00016bc4, 0x000c0000},
+ {0x00016bc8, 0x14021402},
+ {0x00016bcc, 0x00001402},
+ {0x00016bd0, 0x00000000},
+ {0x00016bd4, 0x00000000},
+};
+
+static const u32 ar9580_1p0_baseband_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011},
+ {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e},
+ {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+ {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+ {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+ {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
+ {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
+ {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0},
+ {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
+ {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
+ {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e},
+ {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e},
+ {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
+ {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+ {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
+ {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222},
+ {0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27},
+ {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+ {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
+ {0x0000a204, 0x000036c0, 0x000036c4, 0x000036c4, 0x000036c0},
+ {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
+ {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f},
+ {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+ {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff},
+ {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018},
+ {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
+ {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
+ {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501},
+ {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+ {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
+ {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
+ {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
+ {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
+ {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+ {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
+ {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
+ {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+ {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000},
+ {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+ {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce},
+ {0x0000b284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
+ {0x0000b830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+ {0x0000be04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000},
+ {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000be1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+ {0x0000be20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce},
+ {0x0000c284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
+};
+
+static const u32 ar9580_1p0_pcie_phy_clkreq_enable_L1[][2] = {
+ /* Addr allmodes */
+ {0x00004040, 0x0835365e},
+ {0x00004040, 0x0008003b},
+ {0x00004044, 0x00000000},
+};
+
+static const u32 ar9580_1p0_pcie_phy_clkreq_disable_L1[][2] = {
+ /* Addr allmodes */
+ {0x00004040, 0x0831365e},
+ {0x00004040, 0x0008003b},
+ {0x00004044, 0x00000000},
+};
+
+static const u32 ar9580_1p0_pcie_phy_pll_on_clkreq[][2] = {
+ /* Addr allmodes */
+ {0x00004040, 0x0831265e},
+ {0x00004040, 0x0008003b},
+ {0x00004044, 0x00000000},
+};
+
+#endif /* INITVALS_9580_1P0_H */
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index f75068b4b310..1c269f50822b 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -19,6 +19,7 @@
#include <linux/etherdevice.h>
#include <linux/device.h>
+#include <linux/interrupt.h>
#include <linux/leds.h>
#include <linux/completion.h>
@@ -54,8 +55,6 @@ struct ath_node;
(_l) &= ((_sz) - 1); \
} while (0)
-#define A_MAX(a, b) ((a) > (b) ? (a) : (b))
-
#define TSF_TO_TU(_h,_l) \
((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
@@ -88,20 +87,22 @@ struct ath_config {
* @BUF_AMPDU: This buffer is an ampdu, as part of an aggregate (during TX)
* @BUF_AGGR: Indicates whether the buffer can be aggregated
* (used in aggregation scheduling)
- * @BUF_XRETRY: To denote excessive retries of the buffer
*/
enum buffer_type {
BUF_AMPDU = BIT(0),
BUF_AGGR = BIT(1),
- BUF_XRETRY = BIT(2),
};
#define bf_isampdu(bf) (bf->bf_state.bf_type & BUF_AMPDU)
#define bf_isaggr(bf) (bf->bf_state.bf_type & BUF_AGGR)
-#define bf_isxretried(bf) (bf->bf_state.bf_type & BUF_XRETRY)
#define ATH_TXSTATUS_RING_SIZE 64
+#define DS2PHYS(_dd, _ds) \
+ ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
+#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
+#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096)
+
struct ath_descdma {
void *dd_desc;
dma_addr_t dd_desc_paddr;
@@ -179,7 +180,7 @@ enum ATH_AGGR_STATUS {
struct ath_txq {
int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */
u32 axq_qnum; /* ath9k hardware queue number */
- u32 *axq_link;
+ void *axq_link;
struct list_head axq_q;
spinlock_t axq_lock;
u32 axq_depth;
@@ -188,7 +189,6 @@ struct ath_txq {
bool axq_tx_inprogress;
struct list_head axq_acq;
struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
- struct list_head txq_fifo_pending;
u8 txq_headidx;
u8 txq_tailidx;
int pending_frames;
@@ -203,18 +203,19 @@ struct ath_atx_ac {
};
struct ath_frame_info {
+ struct ath_buf *bf;
int framelen;
- u32 keyix;
enum ath9k_key_type keytype;
+ u8 keyix;
u8 retries;
- u16 seqno;
};
struct ath_buf_state {
u8 bf_type;
u8 bfs_paprd;
+ u8 ndelim;
+ u16 seqno;
unsigned long bfs_paprd_timestamp;
- enum ath9k_internal_frame_type bfs_ftype;
};
struct ath_buf {
@@ -227,13 +228,12 @@ struct ath_buf {
dma_addr_t bf_daddr; /* physical addr of desc */
dma_addr_t bf_buf_addr; /* physical addr of data buffer, for DMA */
bool bf_stale;
- u16 bf_flags;
struct ath_buf_state bf_state;
};
struct ath_atx_tid {
struct list_head list;
- struct list_head buf_q;
+ struct sk_buff_head buf_q;
struct ath_node *an;
struct ath_atx_ac *ac;
unsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)];
@@ -270,14 +270,11 @@ struct ath_node {
struct ath_tx_control {
struct ath_txq *txq;
struct ath_node *an;
- int if_id;
- enum ath9k_internal_frame_type frame_type;
u8 paprd;
};
#define ATH_TX_ERROR 0x01
-#define ATH_TX_XRETRY 0x02
-#define ATH_TX_BAR 0x04
+#define ATH_TX_BAR 0x02
/**
* @txq_map: Index is mac80211 queue number. This is
@@ -343,7 +340,8 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an);
-bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an);
+void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
+ struct ath_node *an);
/********/
/* VIFs */
@@ -424,10 +422,12 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status);
#define ATH_PAPRD_TIMEOUT 100 /* msecs */
+void ath_reset_work(struct work_struct *work);
void ath_hw_check(struct work_struct *work);
void ath_hw_pll_work(struct work_struct *work);
void ath_paprd_calibrate(struct work_struct *work);
void ath_ani_calibrate(unsigned long data);
+void ath_start_ani(struct ath_common *common);
/**********/
/* BTCOEX */
@@ -458,6 +458,7 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc);
#define ATH_LED_PIN_9287 8
#define ATH_LED_PIN_9300 10
#define ATH_LED_PIN_9485 6
+#define ATH_LED_PIN_9462 0
#ifdef CONFIG_MAC80211_LEDS
void ath_init_leds(struct ath_softc *sc);
@@ -557,8 +558,7 @@ struct ath_ant_comb {
#define SC_OP_BT_PRIORITY_DETECTED BIT(12)
#define SC_OP_BT_SCAN BIT(13)
#define SC_OP_ANI_RUN BIT(14)
-#define SC_OP_ENABLE_APM BIT(15)
-#define SC_OP_PRIM_STA_VIF BIT(16)
+#define SC_OP_PRIM_STA_VIF BIT(15)
/* Powersave flags */
#define PS_WAIT_FOR_BEACON BIT(0)
@@ -566,7 +566,6 @@ struct ath_ant_comb {
#define PS_WAIT_FOR_PSPOLL_DATA BIT(2)
#define PS_WAIT_FOR_TX_ACK BIT(3)
#define PS_BEACON_SYNC BIT(4)
-#define PS_TSFOOR_SYNC BIT(5)
struct ath_rate_table;
@@ -579,7 +578,7 @@ struct ath9k_vif_iter_data {
int naps; /* number of AP vifs */
int nmeshes; /* number of mesh vifs */
int nstations; /* number of station vifs */
- int nwds; /* number of nwd vifs */
+ int nwds; /* number of WDS vifs */
int nadhocs; /* number of adhoc vifs */
int nothers; /* number of vifs not specified above. */
};
@@ -604,6 +603,7 @@ struct ath_softc {
struct mutex mutex;
struct work_struct paprd_work;
struct work_struct hw_check_work;
+ struct work_struct hw_reset_work;
struct completion paprd_complete;
unsigned int hw_busy_count;
@@ -647,10 +647,10 @@ struct ath_softc {
struct ath_descdma txsdma;
struct ath_ant_comb ant_comb;
+ u8 ant_tx, ant_rx;
};
void ath9k_tasklet(unsigned long data);
-int ath_reset(struct ath_softc *sc, bool retry_tx);
int ath_cabq_update(struct ath_softc *);
static inline void ath_read_cachesize(struct ath_common *common, int *csz)
@@ -664,17 +664,13 @@ extern int led_blink;
extern bool is_ath9k_unloaded;
irqreturn_t ath_isr(int irq, void *dev);
-void ath9k_init_crypto(struct ath_softc *sc);
-int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
+int ath9k_init_device(u16 devid, struct ath_softc *sc,
const struct ath_bus_ops *bus_ops);
void ath9k_deinit_device(struct ath_softc *sc);
void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
-int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
- struct ath9k_channel *hchan);
+void ath9k_reload_chainmask_settings(struct ath_softc *sc);
-void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw);
void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw);
-bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode);
bool ath9k_uses_beacons(int type);
#ifdef CONFIG_ATH9K_PCI
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index d4d8ceced89b..a13cabb95435 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/dma-mapping.h>
#include "ath9k.h"
#define FUDGE 2
@@ -72,44 +73,39 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
struct sk_buff *skb = bf->bf_mpdu;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- struct ath_desc *ds;
- struct ath9k_11n_rate_series series[4];
- int flags, ctsrate = 0, ctsduration = 0;
+ struct ath_tx_info info;
struct ieee80211_supported_band *sband;
+ u8 chainmask = ah->txchainmask;
u8 rate = 0;
ath9k_reset_beacon_status(sc);
- ds = bf->bf_desc;
- flags = ATH9K_TXDESC_NOACK;
-
- ds->ds_link = 0;
-
sband = &sc->sbands[common->hw->conf.channel->band];
rate = sband->bitrates[rateidx].hw_value;
if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
rate |= sband->bitrates[rateidx].hw_value_short;
- ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN,
- ATH9K_PKT_TYPE_BEACON,
- MAX_RATE_POWER,
- ATH9K_TXKEYIX_INVALID,
- ATH9K_KEY_TYPE_CLEAR,
- flags);
-
- /* NB: beacon's BufLen must be a multiple of 4 bytes */
- ath9k_hw_filltxdesc(ah, ds, roundup(skb->len, 4),
- true, true, ds, bf->bf_buf_addr,
- sc->beacon.beaconq);
-
- memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4);
- series[0].Tries = 1;
- series[0].Rate = rate;
- series[0].ChSel = ath_txchainmask_reduction(sc,
- common->tx_chainmask, series[0].Rate);
- series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0;
- ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration,
- series, 4, 0);
+ memset(&info, 0, sizeof(info));
+ info.pkt_len = skb->len + FCS_LEN;
+ info.type = ATH9K_PKT_TYPE_BEACON;
+ info.txpower = MAX_RATE_POWER;
+ info.keyix = ATH9K_TXKEYIX_INVALID;
+ info.keytype = ATH9K_KEY_TYPE_CLEAR;
+ info.flags = ATH9K_TXDESC_NOACK;
+
+ info.buf_addr[0] = bf->bf_buf_addr;
+ info.buf_len[0] = roundup(skb->len, 4);
+
+ info.is_first = true;
+ info.is_last = true;
+
+ info.qcu = sc->beacon.beaconq;
+
+ info.rates[0].Tries = 1;
+ info.rates[0].Rate = rate;
+ info.rates[0].ChSel = ath_txchainmask_reduction(sc, chainmask, rate);
+
+ ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
}
static void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
@@ -360,6 +356,7 @@ void ath_beacon_tasklet(unsigned long data)
struct ath_common *common = ath9k_hw_common(ah);
struct ath_buf *bf = NULL;
struct ieee80211_vif *vif;
+ struct ath_tx_status ts;
int slot;
u32 bfaddr, bc = 0;
@@ -384,7 +381,7 @@ void ath_beacon_tasklet(unsigned long data)
ath_dbg(common, ATH_DBG_BSTUCK,
"beacon is officially stuck\n");
sc->sc_flags |= SC_OP_TSF_RESET;
- ath_reset(sc, true);
+ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
}
return;
@@ -464,6 +461,11 @@ void ath_beacon_tasklet(unsigned long data)
ath9k_hw_txstart(ah, sc->beacon.beaconq);
sc->beacon.ast_be_xmit += bc; /* XXX per-vif? */
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
+ spin_lock_bh(&sc->sc_pcu_lock);
+ ath9k_hw_txprocdesc(ah, bf->bf_desc, (void *)&ts);
+ spin_unlock_bh(&sc->sc_pcu_lock);
+ }
}
}
@@ -496,7 +498,7 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
u32 nexttbtt, intval;
/* NB: the beacon interval is kept internally in TU's */
- intval = TU_TO_USEC(conf->beacon_interval & ATH9K_BEACON_PERIOD);
+ intval = TU_TO_USEC(conf->beacon_interval);
intval /= ATH_BCBUF; /* for staggered beacons */
nexttbtt = intval;
@@ -510,9 +512,11 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
/* Set the computed AP beacon timers */
ath9k_hw_disable_interrupts(ah);
+ sc->sc_flags |= SC_OP_TSF_RESET;
ath9k_beacon_init(sc, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_set_interrupts(ah);
+ ath9k_hw_enable_interrupts(ah);
}
/*
@@ -543,7 +547,7 @@ static void ath_beacon_config_sta(struct ath_softc *sc,
}
memset(&bs, 0, sizeof(bs));
- intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
+ intval = conf->beacon_interval;
/*
* Setup dtim and cfp parameters according to
@@ -639,12 +643,8 @@ static void ath_beacon_config_sta(struct ath_softc *sc,
ath9k_hw_set_sta_beacon_timers(ah, &bs);
ah->imask |= ATH9K_INT_BMISS;
- /*
- * If the beacon config is called beacause of TSFOOR,
- * Interrupts will be enabled back at the end of ath9k_tasklet
- */
- if (!(sc->ps_flags & PS_TSFOOR_SYNC))
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_set_interrupts(ah);
+ ath9k_hw_enable_interrupts(ah);
}
static void ath_beacon_config_adhoc(struct ath_softc *sc,
@@ -652,22 +652,13 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- u32 tsf, delta, intval, nexttbtt;
+ u32 tsf, intval, nexttbtt;
ath9k_reset_beacon_status(sc);
- tsf = ath9k_hw_gettsf32(ah) + TU_TO_USEC(FUDGE);
- intval = TU_TO_USEC(conf->beacon_interval & ATH9K_BEACON_PERIOD);
-
- if (!sc->beacon.bc_tstamp)
- nexttbtt = tsf + intval;
- else {
- if (tsf > sc->beacon.bc_tstamp)
- delta = (tsf - sc->beacon.bc_tstamp);
- else
- delta = (tsf + 1 + (~0U - sc->beacon.bc_tstamp));
- nexttbtt = tsf + intval - (delta % intval);
- }
+ intval = TU_TO_USEC(conf->beacon_interval);
+ tsf = roundup(ath9k_hw_gettsf32(ah) + TU_TO_USEC(FUDGE), intval);
+ nexttbtt = tsf + intval;
ath_dbg(common, ATH_DBG_BEACON,
"IBSS nexttbtt %u intval %u (%u)\n",
@@ -687,12 +678,9 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
ath9k_hw_disable_interrupts(ah);
ath9k_beacon_init(sc, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
- /*
- * If the beacon config is called beacause of TSFOOR,
- * Interrupts will be enabled back at the end of ath9k_tasklet
- */
- if (!(sc->ps_flags & PS_TSFOOR_SYNC))
- ath9k_hw_set_interrupts(ah, ah->imask);
+
+ ath9k_hw_set_interrupts(ah);
+ ath9k_hw_enable_interrupts(ah);
}
static bool ath9k_allow_beacon_config(struct ath_softc *sc,
@@ -833,11 +821,11 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status)
if (status) {
/* Re-enable beaconing */
ah->imask |= ATH9K_INT_SWBA;
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_set_interrupts(ah);
} else {
/* Disable SWBA interrupt */
ah->imask &= ~ATH9K_INT_SWBA;
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_set_interrupts(ah);
tasklet_kill(&sc->bcon_tasklet);
ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq);
}
diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c
index 41ce0b139886..012263968d64 100644
--- a/drivers/net/wireless/ath/ath9k/btcoex.c
+++ b/drivers/net/wireless/ath/ath9k/btcoex.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/export.h>
#include "hw.h"
enum ath_bt_mode {
@@ -50,7 +51,7 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum)
.bt_first_slot_time = 5,
.bt_hold_rx_clear = true,
};
- u32 i;
+ u32 i, idx;
bool rxclear_polarity = ath_bt_config.bt_rxclear_polarity;
if (AR_SREV_9300_20_OR_LATER(ah))
@@ -73,8 +74,10 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum)
SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) |
AR_BT_DISABLE_BT_ANT;
- for (i = 0; i < 32; i++)
- ah->hw_gen_timers.gen_timer_index[(debruijn32 << i) >> 27] = i;
+ for (i = 0; i < 32; i++) {
+ idx = (debruijn32 << i) >> 27;
+ ah->hw_gen_timers.gen_timer_index[idx] = i;
+ }
}
EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw);
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index a1250c586e40..99538810a312 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -16,6 +16,7 @@
#include "hw.h"
#include "hw-ops.h"
+#include <linux/export.h>
/* Common calibration code */
@@ -63,13 +64,25 @@ static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
return ath9k_hw_get_nf_limits(ah, chan)->nominal;
}
+s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+ s8 noise = ATH_DEFAULT_NOISE_FLOOR;
+
+ if (chan && chan->noisefloor) {
+ s8 delta = chan->noisefloor -
+ ath9k_hw_get_default_nf(ah, chan);
+ if (delta > 0)
+ noise += delta;
+ }
+ return noise;
+}
+EXPORT_SYMBOL(ath9k_hw_getchan_noise);
static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
struct ath9k_hw_cal_data *cal,
int16_t *nfarray)
{
struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_conf *conf = &common->hw->conf;
struct ath_nf_limits *limit;
struct ath9k_nfcal_hist *h;
bool high_nf_mid = false;
@@ -81,7 +94,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
for (i = 0; i < NUM_NF_READINGS; i++) {
if (!(chainmask & (1 << i)) ||
- ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)))
+ ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(ah->curchan)))
continue;
h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
@@ -378,6 +391,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
if (!caldata) {
chan->noisefloor = nf;
+ ah->noise = ath9k_hw_getchan_noise(ah, chan);
return false;
}
@@ -385,6 +399,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
caldata->nfcal_pending = false;
ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray);
chan->noisefloor = h[0].privNF;
+ ah->noise = ath9k_hw_getchan_noise(ah, chan);
return true;
}
diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h
index 1bef41d1b1ff..05b9dbf81850 100644
--- a/drivers/net/wireless/ath/ath9k/calib.h
+++ b/drivers/net/wireless/ath/ath9k/calib.h
@@ -108,6 +108,7 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
void ath9k_hw_bstuck_nfcal(struct ath_hw *ah);
void ath9k_hw_reset_calibration(struct ath_hw *ah,
struct ath9k_cal_list *currCal);
+s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan);
#endif /* CALIB_H */
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index fa6bd2d189e5..905f1b313961 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -161,14 +161,42 @@ EXPORT_SYMBOL(ath9k_cmn_count_streams);
void ath9k_cmn_update_txpow(struct ath_hw *ah, u16 cur_txpow,
u16 new_txpow, u16 *txpower)
{
- if (cur_txpow != new_txpow) {
+ struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
+
+ if (reg->power_limit != new_txpow) {
ath9k_hw_set_txpowerlimit(ah, new_txpow, false);
/* read back in case value is clamped */
- *txpower = ath9k_hw_regulatory(ah)->power_limit;
+ *txpower = reg->max_power_level;
}
}
EXPORT_SYMBOL(ath9k_cmn_update_txpow);
+void ath9k_cmn_init_crypto(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ int i = 0;
+
+ /* Get the hardware key cache size. */
+ common->keymax = AR_KEYTABLE_SIZE;
+
+ /*
+ * Check whether the separate key cache entries
+ * are required to handle both tx+rx MIC keys.
+ * With split mic keys the number of stations is limited
+ * to 27 otherwise 59.
+ */
+ if (ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA)
+ common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED;
+
+ /*
+ * Reset the key cache since some parts do not
+ * reset the contents on initial power up.
+ */
+ for (i = 0; i < common->keymax; i++)
+ ath_hw_keyreset(common, (u16) i);
+}
+EXPORT_SYMBOL(ath9k_cmn_init_crypto);
+
static int __init ath9k_cmn_init(void)
{
return 0;
diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h
index 77ec288b5a70..ad14fecc76c6 100644
--- a/drivers/net/wireless/ath/ath9k/common.h
+++ b/drivers/net/wireless/ath/ath9k/common.h
@@ -62,3 +62,4 @@ void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
enum ath_stomp_type stomp_type);
void ath9k_cmn_update_txpow(struct ath_hw *ah, u16 cur_txpow,
u16 new_txpow, u16 *txpower);
+void ath9k_cmn_init_crypto(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index d55ffd7d4bd2..2741203e803f 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <asm/unaligned.h>
#include "ath9k.h"
@@ -95,11 +96,11 @@ static ssize_t read_file_tx_chainmask(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_hw *ah = sc->sc_ah;
char buf[32];
unsigned int len;
- len = sprintf(buf, "0x%08x\n", common->tx_chainmask);
+ len = sprintf(buf, "0x%08x\n", ah->txchainmask);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -107,7 +108,7 @@ static ssize_t write_file_tx_chainmask(struct file *file, const char __user *use
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_hw *ah = sc->sc_ah;
unsigned long mask;
char buf[32];
ssize_t len;
@@ -120,8 +121,8 @@ static ssize_t write_file_tx_chainmask(struct file *file, const char __user *use
if (strict_strtoul(buf, 0, &mask))
return -EINVAL;
- common->tx_chainmask = mask;
- sc->sc_ah->caps.tx_chainmask = mask;
+ ah->txchainmask = mask;
+ ah->caps.tx_chainmask = mask;
return count;
}
@@ -138,11 +139,11 @@ static ssize_t read_file_rx_chainmask(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_hw *ah = sc->sc_ah;
char buf[32];
unsigned int len;
- len = sprintf(buf, "0x%08x\n", common->rx_chainmask);
+ len = sprintf(buf, "0x%08x\n", ah->rxchainmask);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -150,7 +151,7 @@ static ssize_t write_file_rx_chainmask(struct file *file, const char __user *use
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_hw *ah = sc->sc_ah;
unsigned long mask;
char buf[32];
ssize_t len;
@@ -163,8 +164,8 @@ static ssize_t write_file_rx_chainmask(struct file *file, const char __user *use
if (strict_strtoul(buf, 0, &mask))
return -EINVAL;
- common->rx_chainmask = mask;
- sc->sc_ah->caps.rx_chainmask = mask;
+ ah->rxchainmask = mask;
+ ah->caps.rx_chainmask = mask;
return count;
}
@@ -176,6 +177,56 @@ static const struct file_operations fops_rx_chainmask = {
.llseek = default_llseek,
};
+static ssize_t read_file_disable_ani(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ char buf[32];
+ unsigned int len;
+
+ len = sprintf(buf, "%d\n", common->disable_ani);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_disable_ani(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ unsigned long disable_ani;
+ char buf[32];
+ ssize_t len;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ if (strict_strtoul(buf, 0, &disable_ani))
+ return -EINVAL;
+
+ common->disable_ani = !!disable_ani;
+
+ if (disable_ani) {
+ sc->sc_flags &= ~SC_OP_ANI_RUN;
+ del_timer_sync(&common->ani.timer);
+ } else {
+ sc->sc_flags |= SC_OP_ANI_RUN;
+ ath_start_ani(common);
+ }
+
+ return count;
+}
+
+static const struct file_operations fops_disable_ani = {
+ .read = read_file_disable_ani,
+ .write = write_file_disable_ani,
+ .open = ath9k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
static ssize_t read_file_dma(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
@@ -473,9 +524,22 @@ static ssize_t read_file_wiphy(struct file *file, char __user *user_buf,
if (tmp & ATH9K_RX_FILTER_PHYRADAR)
len += snprintf(buf + len, sizeof(buf) - len, " PHYRADAR");
if (tmp & ATH9K_RX_FILTER_MCAST_BCAST_ALL)
- len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL\n");
- else
- len += snprintf(buf + len, sizeof(buf) - len, "\n");
+ len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL");
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "\n\nReset causes:\n"
+ " baseband hang: %d\n"
+ " baseband watchdog: %d\n"
+ " fatal hardware error interrupt: %d\n"
+ " tx hardware error: %d\n"
+ " tx path hang: %d\n"
+ " pll rx hang: %d\n",
+ sc->debug.stats.reset[RESET_TYPE_BB_HANG],
+ sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG],
+ sc->debug.stats.reset[RESET_TYPE_FATAL_INT],
+ sc->debug.stats.reset[RESET_TYPE_TX_ERROR],
+ sc->debug.stats.reset[RESET_TYPE_TX_HANG],
+ sc->debug.stats.reset[RESET_TYPE_PLL_HANG]);
if (len > sizeof(buf))
len = sizeof(buf);
@@ -550,6 +614,7 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
PR("MPDUs Queued: ", queued);
PR("MPDUs Completed: ", completed);
+ PR("MPDUs XRetried: ", xretries);
PR("Aggregates: ", a_aggr);
PR("AMPDUs Queued HW:", a_queued_hw);
PR("AMPDUs Queued SW:", a_queued_sw);
@@ -587,7 +652,6 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
PRQLE("axq_q empty: ", axq_q);
PRQLE("axq_acq empty: ", axq_acq);
- PRQLE("txq_fifo_pending: ", txq_fifo_pending);
for (i = 0; i < ATH_TXFIFO_DEPTH; i++) {
snprintf(tmp, sizeof(tmp) - 1, "txq_fifo[%i] empty: ", i);
PRQLE(tmp, txq_fifo[i]);
@@ -661,7 +725,7 @@ static ssize_t read_file_stations(struct file *file, char __user *user_buf,
" tid: %p %s %s %i %p %p\n",
tid, tid->sched ? "sched" : "idle",
tid->paused ? "paused" : "running",
- list_empty(&tid->buf_q),
+ skb_queue_empty(&tid->buf_q),
tid->an, tid->ac);
if (len >= size)
goto done;
@@ -699,7 +763,6 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
char *buf;
unsigned int len = 0, size = 8000;
ssize_t retval = 0;
- const char *tmp;
unsigned int reg;
struct ath9k_vif_iter_data iter_data;
@@ -709,31 +772,14 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
if (buf == NULL)
return -ENOMEM;
- switch (sc->sc_ah->opmode) {
- case NL80211_IFTYPE_ADHOC:
- tmp = "ADHOC";
- break;
- case NL80211_IFTYPE_MESH_POINT:
- tmp = "MESH";
- break;
- case NL80211_IFTYPE_AP:
- tmp = "AP";
- break;
- case NL80211_IFTYPE_STATION:
- tmp = "STATION";
- break;
- default:
- tmp = "???";
- break;
- }
-
ath9k_ps_wakeup(sc);
len += snprintf(buf + len, size - len,
"curbssid: %pM\n"
"OP-Mode: %s(%i)\n"
"Beacon-Timer-Register: 0x%x\n",
common->curbssid,
- tmp, (int)(sc->sc_ah->opmode),
+ ath_opmode_to_string(sc->sc_ah->opmode),
+ (int)(sc->sc_ah->opmode),
REG_READ(ah, AR_BEACON_PERIOD));
reg = REG_READ(ah, AR_TIMER_MODE);
@@ -794,20 +840,26 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
}
void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
- struct ath_tx_status *ts, struct ath_txq *txq)
+ struct ath_tx_status *ts, struct ath_txq *txq,
+ unsigned int flags)
{
+#define TX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].ts\
+ [sc->debug.tsidx].c)
int qnum = txq->axq_qnum;
TX_STAT_INC(qnum, tx_pkts_all);
sc->debug.stats.txstats[qnum].tx_bytes_all += bf->bf_mpdu->len;
if (bf_isampdu(bf)) {
- if (bf_isxretried(bf))
+ if (flags & ATH_TX_BAR)
TX_STAT_INC(qnum, a_xretries);
else
TX_STAT_INC(qnum, a_completed);
} else {
- TX_STAT_INC(qnum, completed);
+ if (ts->ts_status & ATH9K_TXERR_XRETRY)
+ TX_STAT_INC(qnum, xretries);
+ else
+ TX_STAT_INC(qnum, completed);
}
if (ts->ts_status & ATH9K_TXERR_FIFO)
@@ -822,6 +874,35 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
TX_STAT_INC(qnum, data_underrun);
if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN)
TX_STAT_INC(qnum, delim_underrun);
+
+ spin_lock(&sc->debug.samp_lock);
+ TX_SAMP_DBG(jiffies) = jiffies;
+ TX_SAMP_DBG(rssi_ctl0) = ts->ts_rssi_ctl0;
+ TX_SAMP_DBG(rssi_ctl1) = ts->ts_rssi_ctl1;
+ TX_SAMP_DBG(rssi_ctl2) = ts->ts_rssi_ctl2;
+ TX_SAMP_DBG(rssi_ext0) = ts->ts_rssi_ext0;
+ TX_SAMP_DBG(rssi_ext1) = ts->ts_rssi_ext1;
+ TX_SAMP_DBG(rssi_ext2) = ts->ts_rssi_ext2;
+ TX_SAMP_DBG(rateindex) = ts->ts_rateindex;
+ TX_SAMP_DBG(isok) = !!(ts->ts_status & ATH9K_TXERR_MASK);
+ TX_SAMP_DBG(rts_fail_cnt) = ts->ts_shortretry;
+ TX_SAMP_DBG(data_fail_cnt) = ts->ts_longretry;
+ TX_SAMP_DBG(rssi) = ts->ts_rssi;
+ TX_SAMP_DBG(tid) = ts->tid;
+ TX_SAMP_DBG(qid) = ts->qid;
+
+ if (ts->ts_flags & ATH9K_TX_BA) {
+ TX_SAMP_DBG(ba_low) = ts->ba_low;
+ TX_SAMP_DBG(ba_high) = ts->ba_high;
+ } else {
+ TX_SAMP_DBG(ba_low) = 0;
+ TX_SAMP_DBG(ba_high) = 0;
+ }
+
+ sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES;
+ spin_unlock(&sc->debug.samp_lock);
+
+#undef TX_SAMP_DBG
}
static const struct file_operations fops_xmit = {
@@ -960,6 +1041,8 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
{
#define RX_STAT_INC(c) sc->debug.stats.rxstats.c++
#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++
+#define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\
+ [sc->debug.rsidx].c)
u32 phyerr;
@@ -995,8 +1078,25 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
sc->debug.stats.rxstats.rs_antenna = rs->rs_antenna;
+ spin_lock(&sc->debug.samp_lock);
+ RX_SAMP_DBG(jiffies) = jiffies;
+ RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0;
+ RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1;
+ RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2;
+ RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0;
+ RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1;
+ RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2;
+ RX_SAMP_DBG(antenna) = rs->rs_antenna;
+ RX_SAMP_DBG(rssi) = rs->rs_rssi;
+ RX_SAMP_DBG(rate) = rs->rs_rate;
+ RX_SAMP_DBG(is_mybeacon) = rs->is_mybeacon;
+
+ sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES;
+ spin_unlock(&sc->debug.samp_lock);
+
#undef RX_STAT_INC
#undef RX_PHY_ERR_INC
+#undef RX_SAMP_DBG
}
static const struct file_operations fops_recv = {
@@ -1128,6 +1228,389 @@ static const struct file_operations fops_regdump = {
.llseek = default_llseek,/* read accesses f_pos */
};
+static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath9k_nfcal_hist *h = sc->caldata.nfCalHist;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ieee80211_conf *conf = &common->hw->conf;
+ u32 len = 0, size = 1500;
+ u32 i, j;
+ ssize_t retval = 0;
+ char *buf;
+ u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
+ u8 nread;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ len += snprintf(buf + len, size - len,
+ "Channel Noise Floor : %d\n", ah->noise);
+ len += snprintf(buf + len, size - len,
+ "Chain | privNF | # Readings | NF Readings\n");
+ for (i = 0; i < NUM_NF_READINGS; i++) {
+ if (!(chainmask & (1 << i)) ||
+ ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)))
+ continue;
+
+ nread = AR_PHY_CCA_FILTERWINDOW_LENGTH - h[i].invalidNFcount;
+ len += snprintf(buf + len, size - len, " %d\t %d\t %d\t\t",
+ i, h[i].privNF, nread);
+ for (j = 0; j < nread; j++)
+ len += snprintf(buf + len, size - len,
+ " %d", h[i].nfCalBuffer[j]);
+ len += snprintf(buf + len, size - len, "\n");
+ }
+
+ if (len > size)
+ len = size;
+
+ retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+
+ return retval;
+}
+
+static const struct file_operations fops_dump_nfcal = {
+ .read = read_file_dump_nfcal,
+ .open = ath9k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ struct ath_hw *ah = sc->sc_ah;
+ u32 len = 0, size = 1500;
+ ssize_t retval = 0;
+ char *buf;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size);
+
+ retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+
+ return retval;
+}
+
+static const struct file_operations fops_base_eeprom = {
+ .read = read_file_base_eeprom,
+ .open = ath9k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ struct ath_hw *ah = sc->sc_ah;
+ u32 len = 0, size = 6000;
+ char *buf;
+ size_t retval;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size);
+
+ retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+
+ return retval;
+}
+
+static const struct file_operations fops_modal_eeprom = {
+ .read = read_file_modal_eeprom,
+ .open = ath9k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
+{
+#define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c)
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ unsigned long flags;
+ int i;
+
+ ath9k_ps_wakeup(sc);
+
+ spin_lock_bh(&sc->debug.samp_lock);
+
+ spin_lock_irqsave(&common->cc_lock, flags);
+ ath_hw_cycle_counters_update(common);
+
+ ATH_SAMP_DBG(cc.cycles) = common->cc_ani.cycles;
+ ATH_SAMP_DBG(cc.rx_busy) = common->cc_ani.rx_busy;
+ ATH_SAMP_DBG(cc.rx_frame) = common->cc_ani.rx_frame;
+ ATH_SAMP_DBG(cc.tx_frame) = common->cc_ani.tx_frame;
+ spin_unlock_irqrestore(&common->cc_lock, flags);
+
+ ATH_SAMP_DBG(noise) = ah->noise;
+
+ REG_WRITE_D(ah, AR_MACMISC,
+ ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
+ (AR_MACMISC_MISC_OBS_BUS_1 <<
+ AR_MACMISC_MISC_OBS_BUS_MSB_S)));
+
+ for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
+ ATH_SAMP_DBG(dma_dbg_reg_vals[i]) = REG_READ_D(ah,
+ AR_DMADBG_0 + (i * sizeof(u32)));
+
+ ATH_SAMP_DBG(pcu_obs) = REG_READ_D(ah, AR_OBS_BUS_1);
+ ATH_SAMP_DBG(pcu_cr) = REG_READ_D(ah, AR_CR);
+
+ memcpy(ATH_SAMP_DBG(nfCalHist), sc->caldata.nfCalHist,
+ sizeof(ATH_SAMP_DBG(nfCalHist)));
+
+ sc->debug.sampidx = (sc->debug.sampidx + 1) % ATH_DBG_MAX_SAMPLES;
+ spin_unlock_bh(&sc->debug.samp_lock);
+ ath9k_ps_restore(sc);
+
+#undef ATH_SAMP_DBG
+}
+
+static int open_file_bb_mac_samps(struct inode *inode, struct file *file)
+{
+#define ATH_SAMP_DBG(c) bb_mac_samp[sampidx].c
+ struct ath_softc *sc = inode->i_private;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ieee80211_conf *conf = &common->hw->conf;
+ struct ath_dbg_bb_mac_samp *bb_mac_samp;
+ struct ath9k_nfcal_hist *h;
+ int i, j, qcuOffset = 0, dcuOffset = 0;
+ u32 *qcuBase, *dcuBase, size = 30000, len = 0;
+ u32 sampidx = 0;
+ u8 *buf;
+ u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
+ u8 nread;
+
+ if (sc->sc_flags & SC_OP_INVALID)
+ return -EAGAIN;
+
+ buf = vmalloc(size);
+ if (!buf)
+ return -ENOMEM;
+ bb_mac_samp = vmalloc(sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
+ if (!bb_mac_samp) {
+ vfree(buf);
+ return -ENOMEM;
+ }
+ /* Account the current state too */
+ ath9k_debug_samp_bb_mac(sc);
+
+ spin_lock_bh(&sc->debug.samp_lock);
+ memcpy(bb_mac_samp, sc->debug.bb_mac_samp,
+ sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
+ len += snprintf(buf + len, size - len,
+ "Current Sample Index: %d\n", sc->debug.sampidx);
+ spin_unlock_bh(&sc->debug.samp_lock);
+
+ len += snprintf(buf + len, size - len,
+ "Raw DMA Debug Dump:\n");
+ len += snprintf(buf + len, size - len, "Sample |\t");
+ for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
+ len += snprintf(buf + len, size - len, " DMA Reg%d |\t", i);
+ len += snprintf(buf + len, size - len, "\n");
+
+ for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
+ len += snprintf(buf + len, size - len, "%d\t", sampidx);
+
+ for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
+ len += snprintf(buf + len, size - len, " %08x\t",
+ ATH_SAMP_DBG(dma_dbg_reg_vals[i]));
+ len += snprintf(buf + len, size - len, "\n");
+ }
+ len += snprintf(buf + len, size - len, "\n");
+
+ len += snprintf(buf + len, size - len,
+ "Sample Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
+ for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
+ qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
+ dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
+
+ for (i = 0; i < ATH9K_NUM_QUEUES; i++,
+ qcuOffset += 4, dcuOffset += 5) {
+ if (i == 8) {
+ qcuOffset = 0;
+ qcuBase++;
+ }
+
+ if (i == 6) {
+ dcuOffset = 0;
+ dcuBase++;
+ }
+ if (!sc->debug.stats.txstats[i].queued)
+ continue;
+
+ len += snprintf(buf + len, size - len,
+ "%4d %7d %2x %1x %2x %2x\n",
+ sampidx, i,
+ (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
+ (*qcuBase & (0x8 << qcuOffset)) >>
+ (qcuOffset + 3),
+ ATH_SAMP_DBG(dma_dbg_reg_vals[2]) &
+ (0x7 << (i * 3)) >> (i * 3),
+ (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
+ }
+ len += snprintf(buf + len, size - len, "\n");
+ }
+ len += snprintf(buf + len, size - len,
+ "samp qcu_sh qcu_fh qcu_comp dcu_comp dcu_arb dcu_fp "
+ "ch_idle_dur ch_idle_dur_val txfifo_val0 txfifo_val1 "
+ "txfifo_dcu0 txfifo_dcu1 pcu_obs AR_CR\n");
+
+ for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
+ qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
+ dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
+
+ len += snprintf(buf + len, size - len, "%4d %5x %5x ", sampidx,
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x003c0000) >> 18,
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x03c00000) >> 22);
+ len += snprintf(buf + len, size - len, "%7x %8x ",
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x1c000000) >> 26,
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x3));
+ len += snprintf(buf + len, size - len, "%7x %7x ",
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x06000000) >> 25,
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x38000000) >> 27);
+ len += snprintf(buf + len, size - len, "%7d %12d ",
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x000003fc) >> 2,
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000400) >> 10);
+ len += snprintf(buf + len, size - len, "%12d %12d ",
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000800) >> 11,
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00001000) >> 12);
+ len += snprintf(buf + len, size - len, "%12d %12d ",
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x0001e000) >> 13,
+ (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x001e0000) >> 17);
+ len += snprintf(buf + len, size - len, "0x%07x 0x%07x\n",
+ ATH_SAMP_DBG(pcu_obs), ATH_SAMP_DBG(pcu_cr));
+ }
+
+ len += snprintf(buf + len, size - len,
+ "Sample ChNoise Chain privNF #Reading Readings\n");
+ for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
+ h = ATH_SAMP_DBG(nfCalHist);
+ if (!ATH_SAMP_DBG(noise))
+ continue;
+
+ for (i = 0; i < NUM_NF_READINGS; i++) {
+ if (!(chainmask & (1 << i)) ||
+ ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)))
+ continue;
+
+ nread = AR_PHY_CCA_FILTERWINDOW_LENGTH -
+ h[i].invalidNFcount;
+ len += snprintf(buf + len, size - len,
+ "%4d %5d %4d\t %d\t %d\t",
+ sampidx, ATH_SAMP_DBG(noise),
+ i, h[i].privNF, nread);
+ for (j = 0; j < nread; j++)
+ len += snprintf(buf + len, size - len,
+ " %d", h[i].nfCalBuffer[j]);
+ len += snprintf(buf + len, size - len, "\n");
+ }
+ }
+ len += snprintf(buf + len, size - len, "\nCycle counters:\n"
+ "Sample Total Rxbusy Rxframes Txframes\n");
+ for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
+ if (!ATH_SAMP_DBG(cc.cycles))
+ continue;
+ len += snprintf(buf + len, size - len,
+ "%4d %08x %08x %08x %08x\n",
+ sampidx, ATH_SAMP_DBG(cc.cycles),
+ ATH_SAMP_DBG(cc.rx_busy),
+ ATH_SAMP_DBG(cc.rx_frame),
+ ATH_SAMP_DBG(cc.tx_frame));
+ }
+
+ len += snprintf(buf + len, size - len, "Tx status Dump :\n");
+ len += snprintf(buf + len, size - len,
+ "Sample rssi:- ctl0 ctl1 ctl2 ext0 ext1 ext2 comb "
+ "isok rts_fail data_fail rate tid qid "
+ "ba_low ba_high tx_before(ms)\n");
+ for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
+ for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
+ if (!ATH_SAMP_DBG(ts[i].jiffies))
+ continue;
+ len += snprintf(buf + len, size - len, "%-14d"
+ "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-8d "
+ "%-9d %-4d %-3d %-3d %08x %08x %-11d\n",
+ sampidx,
+ ATH_SAMP_DBG(ts[i].rssi_ctl0),
+ ATH_SAMP_DBG(ts[i].rssi_ctl1),
+ ATH_SAMP_DBG(ts[i].rssi_ctl2),
+ ATH_SAMP_DBG(ts[i].rssi_ext0),
+ ATH_SAMP_DBG(ts[i].rssi_ext1),
+ ATH_SAMP_DBG(ts[i].rssi_ext2),
+ ATH_SAMP_DBG(ts[i].rssi),
+ ATH_SAMP_DBG(ts[i].isok),
+ ATH_SAMP_DBG(ts[i].rts_fail_cnt),
+ ATH_SAMP_DBG(ts[i].data_fail_cnt),
+ ATH_SAMP_DBG(ts[i].rateindex),
+ ATH_SAMP_DBG(ts[i].tid),
+ ATH_SAMP_DBG(ts[i].qid),
+ ATH_SAMP_DBG(ts[i].ba_low),
+ ATH_SAMP_DBG(ts[i].ba_high),
+ jiffies_to_msecs(jiffies -
+ ATH_SAMP_DBG(ts[i].jiffies)));
+ }
+ }
+
+ len += snprintf(buf + len, size - len, "Rx status Dump :\n");
+ len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 "
+ "ext0 ext1 ext2 comb beacon ant rate rx_before(ms)\n");
+ for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
+ for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
+ if (!ATH_SAMP_DBG(rs[i].jiffies))
+ continue;
+ len += snprintf(buf + len, size - len, "%-14d"
+ "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-9s %-2d %02x %-13d\n",
+ sampidx,
+ ATH_SAMP_DBG(rs[i].rssi_ctl0),
+ ATH_SAMP_DBG(rs[i].rssi_ctl1),
+ ATH_SAMP_DBG(rs[i].rssi_ctl2),
+ ATH_SAMP_DBG(rs[i].rssi_ext0),
+ ATH_SAMP_DBG(rs[i].rssi_ext1),
+ ATH_SAMP_DBG(rs[i].rssi_ext2),
+ ATH_SAMP_DBG(rs[i].rssi),
+ ATH_SAMP_DBG(rs[i].is_mybeacon) ?
+ "True" : "False",
+ ATH_SAMP_DBG(rs[i].antenna),
+ ATH_SAMP_DBG(rs[i].rate),
+ jiffies_to_msecs(jiffies -
+ ATH_SAMP_DBG(rs[i].jiffies)));
+ }
+ }
+
+ vfree(bb_mac_samp);
+ file->private_data = buf;
+
+ return 0;
+#undef ATH_SAMP_DBG
+}
+
+static const struct file_operations fops_samps = {
+ .open = open_file_bb_mac_samps,
+ .read = ath9k_debugfs_read_buf,
+ .release = ath9k_debugfs_release_buf,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+
int ath9k_init_debug(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
@@ -1160,6 +1643,8 @@ int ath9k_init_debug(struct ath_hw *ah)
sc->debug.debugfs_phy, sc, &fops_rx_chainmask);
debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
+ debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR,
+ sc->debug.debugfs_phy, sc, &fops_disable_ani);
debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
sc, &fops_regidx);
debugfs_create_file("regval", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
@@ -1169,6 +1654,14 @@ int ath9k_init_debug(struct ath_hw *ah)
&ah->config.cwm_ignore_extcca);
debugfs_create_file("regdump", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_regdump);
+ debugfs_create_file("dump_nfcal", S_IRUSR, sc->debug.debugfs_phy, sc,
+ &fops_dump_nfcal);
+ debugfs_create_file("base_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
+ &fops_base_eeprom);
+ debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
+ &fops_modal_eeprom);
+ debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc,
+ &fops_samps);
debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
@@ -1177,5 +1670,9 @@ int ath9k_init_debug(struct ath_hw *ah)
sc->debug.debugfs_phy, &sc->sc_ah->gpio_val);
sc->debug.regidx = 0;
+ memset(&sc->debug.bb_mac_samp, 0, sizeof(sc->debug.bb_mac_samp));
+ sc->debug.sampidx = 0;
+ sc->debug.tsidx = 0;
+ sc->debug.rsidx = 0;
return 0;
}
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 8ce6ad80f4e2..356352ac2d6e 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -25,8 +25,10 @@ struct ath_buf;
#ifdef CONFIG_ATH9K_DEBUGFS
#define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++
+#define RESET_STAT_INC(sc, type) sc->debug.stats.reset[type]++
#else
#define TX_STAT_INC(q, c) do { } while (0)
+#define RESET_STAT_INC(sc, type) do { } while (0)
#endif
#ifdef CONFIG_ATH9K_DEBUGFS
@@ -116,6 +118,7 @@ struct ath_tx_stats {
u32 tx_bytes_all;
u32 queued;
u32 completed;
+ u32 xretries;
u32 a_aggr;
u32 a_queued_hw;
u32 a_queued_sw;
@@ -170,23 +173,80 @@ struct ath_rx_stats {
u8 rs_antenna;
};
+enum ath_reset_type {
+ RESET_TYPE_BB_HANG,
+ RESET_TYPE_BB_WATCHDOG,
+ RESET_TYPE_FATAL_INT,
+ RESET_TYPE_TX_ERROR,
+ RESET_TYPE_TX_HANG,
+ RESET_TYPE_PLL_HANG,
+ __RESET_TYPE_MAX
+};
+
struct ath_stats {
struct ath_interrupt_stats istats;
struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES];
struct ath_rx_stats rxstats;
+ u32 reset[__RESET_TYPE_MAX];
+};
+
+#define ATH_DBG_MAX_SAMPLES 10
+struct ath_dbg_bb_mac_samp {
+ u32 dma_dbg_reg_vals[ATH9K_NUM_DMA_DEBUG_REGS];
+ u32 pcu_obs, pcu_cr, noise;
+ struct {
+ u64 jiffies;
+ int8_t rssi_ctl0;
+ int8_t rssi_ctl1;
+ int8_t rssi_ctl2;
+ int8_t rssi_ext0;
+ int8_t rssi_ext1;
+ int8_t rssi_ext2;
+ int8_t rssi;
+ bool isok;
+ u8 rts_fail_cnt;
+ u8 data_fail_cnt;
+ u8 rateindex;
+ u8 qid;
+ u8 tid;
+ u32 ba_low;
+ u32 ba_high;
+ } ts[ATH_DBG_MAX_SAMPLES];
+ struct {
+ u64 jiffies;
+ int8_t rssi_ctl0;
+ int8_t rssi_ctl1;
+ int8_t rssi_ctl2;
+ int8_t rssi_ext0;
+ int8_t rssi_ext1;
+ int8_t rssi_ext2;
+ int8_t rssi;
+ bool is_mybeacon;
+ u8 antenna;
+ u8 rate;
+ } rs[ATH_DBG_MAX_SAMPLES];
+ struct ath_cycle_counters cc;
+ struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
};
struct ath9k_debug {
struct dentry *debugfs_phy;
u32 regidx;
struct ath_stats stats;
+ spinlock_t samp_lock;
+ struct ath_dbg_bb_mac_samp bb_mac_samp[ATH_DBG_MAX_SAMPLES];
+ u8 sampidx;
+ u8 tsidx;
+ u8 rsidx;
};
int ath9k_init_debug(struct ath_hw *ah);
+void ath9k_debug_samp_bb_mac(struct ath_softc *sc);
void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status);
void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
- struct ath_tx_status *ts, struct ath_txq *txq);
+ struct ath_tx_status *ts, struct ath_txq *txq,
+ unsigned int flags);
void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs);
#else
@@ -196,6 +256,10 @@ static inline int ath9k_init_debug(struct ath_hw *ah)
return 0;
}
+static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
+{
+}
+
static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
enum ath9k_int status)
{
@@ -204,7 +268,8 @@ static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
static inline void ath_debug_stat_tx(struct ath_softc *sc,
struct ath_buf *bf,
struct ath_tx_status *ts,
- struct ath_txq *txq)
+ struct ath_txq *txq,
+ unsigned int flags)
{
}
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index e61404dda8c5..e46f751ab508 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -456,12 +456,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
pPdGainBoundaries[i] =
min((u16)MAX_RATE_POWER, pPdGainBoundaries[i]);
- if ((i == 0) && !AR_SREV_5416_20_OR_LATER(ah)) {
- minDelta = pPdGainBoundaries[0] - 23;
- pPdGainBoundaries[0] = 23;
- } else {
- minDelta = 0;
- }
+ minDelta = 0;
if (i == 0) {
if (AR_SREV_9280_20_OR_LATER(ah))
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h
index de99c0da52e4..49abd34be741 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/eeprom.h
@@ -104,16 +104,11 @@
#define OLC_FOR_AR9287_10_LATER (AR_SREV_9287_11_OR_LATER(ah) && \
ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL))
-#define AR_EEPROM_RFSILENT_GPIO_SEL 0x001c
-#define AR_EEPROM_RFSILENT_GPIO_SEL_S 2
-#define AR_EEPROM_RFSILENT_POLARITY 0x0002
-#define AR_EEPROM_RFSILENT_POLARITY_S 1
-
#define EEP_RFSILENT_ENABLED 0x0001
#define EEP_RFSILENT_ENABLED_S 0
#define EEP_RFSILENT_POLARITY 0x0002
#define EEP_RFSILENT_POLARITY_S 1
-#define EEP_RFSILENT_GPIO_SEL 0x001c
+#define EEP_RFSILENT_GPIO_SEL (AR_SREV_9462(ah) ? 0x00fc : 0x001c)
#define EEP_RFSILENT_GPIO_SEL_S 2
#define AR5416_OPFLAGS_11A 0x01
@@ -225,7 +220,6 @@ enum eeprom_param {
EEP_MAC_MID,
EEP_MAC_LSW,
EEP_REG_0,
- EEP_REG_1,
EEP_OP_CAP,
EEP_OP_MODE,
EEP_RF_SILENT,
@@ -253,7 +247,9 @@ enum eeprom_param {
EEP_PAPRD,
EEP_MODAL_VER,
EEP_ANT_DIV_CTL1,
- EEP_CHAIN_MASK_REDUCE
+ EEP_CHAIN_MASK_REDUCE,
+ EEP_ANTENNA_GAIN_2G,
+ EEP_ANTENNA_GAIN_5G
};
enum ar5416_rates {
@@ -649,14 +645,15 @@ struct eeprom_ops {
int (*check_eeprom)(struct ath_hw *hw);
u32 (*get_eeprom)(struct ath_hw *hw, enum eeprom_param param);
bool (*fill_eeprom)(struct ath_hw *hw);
+ u32 (*dump_eeprom)(struct ath_hw *hw, bool dump_base_hdr, u8 *buf,
+ u32 len, u32 size);
int (*get_eeprom_ver)(struct ath_hw *hw);
int (*get_eeprom_rev)(struct ath_hw *hw);
void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan);
void (*set_addac)(struct ath_hw *hw, struct ath9k_channel *chan);
void (*set_txpower)(struct ath_hw *hw, struct ath9k_channel *chan,
u16 cfgCtl, u8 twiceAntennaReduction,
- u8 twiceMaxRegulatoryPower, u8 powerLimit,
- bool test);
+ u8 powerLimit, bool test);
u16 (*get_spur_channel)(struct ath_hw *ah, u16 i, bool is2GHz);
};
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
index 5b1e894f3d67..9a7520f987f0 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <asm/unaligned.h>
#include "hw.h"
#include "ar9002_phy.h"
@@ -71,6 +72,117 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah)
return __ath9k_hw_4k_fill_eeprom(ah);
}
+#if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS)
+static u32 ath9k_dump_4k_modal_eeprom(char *buf, u32 len, u32 size,
+ struct modal_eep_4k_header *modal_hdr)
+{
+ PR_EEP("Chain0 Ant. Control", modal_hdr->antCtrlChain[0]);
+ PR_EEP("Ant. Common Control", modal_hdr->antCtrlCommon);
+ PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]);
+ PR_EEP("Switch Settle", modal_hdr->switchSettling);
+ PR_EEP("Chain0 TxRxAtten", modal_hdr->txRxAttenCh[0]);
+ PR_EEP("Chain0 RxTxMargin", modal_hdr->rxTxMarginCh[0]);
+ PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize);
+ PR_EEP("PGA Desired size", modal_hdr->pgaDesiredSize);
+ PR_EEP("Chain0 xlna Gain", modal_hdr->xlnaGainCh[0]);
+ PR_EEP("txEndToXpaOff", modal_hdr->txEndToXpaOff);
+ PR_EEP("txEndToRxOn", modal_hdr->txEndToRxOn);
+ PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn);
+ PR_EEP("CCA Threshold)", modal_hdr->thresh62);
+ PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]);
+ PR_EEP("xpdGain", modal_hdr->xpdGain);
+ PR_EEP("External PD", modal_hdr->xpd);
+ PR_EEP("Chain0 I Coefficient", modal_hdr->iqCalICh[0]);
+ PR_EEP("Chain0 Q Coefficient", modal_hdr->iqCalQCh[0]);
+ PR_EEP("pdGainOverlap", modal_hdr->pdGainOverlap);
+ PR_EEP("O/D Bias Version", modal_hdr->version);
+ PR_EEP("CCK OutputBias", modal_hdr->ob_0);
+ PR_EEP("BPSK OutputBias", modal_hdr->ob_1);
+ PR_EEP("QPSK OutputBias", modal_hdr->ob_2);
+ PR_EEP("16QAM OutputBias", modal_hdr->ob_3);
+ PR_EEP("64QAM OutputBias", modal_hdr->ob_4);
+ PR_EEP("CCK Driver1_Bias", modal_hdr->db1_0);
+ PR_EEP("BPSK Driver1_Bias", modal_hdr->db1_1);
+ PR_EEP("QPSK Driver1_Bias", modal_hdr->db1_2);
+ PR_EEP("16QAM Driver1_Bias", modal_hdr->db1_3);
+ PR_EEP("64QAM Driver1_Bias", modal_hdr->db1_4);
+ PR_EEP("CCK Driver2_Bias", modal_hdr->db2_0);
+ PR_EEP("BPSK Driver2_Bias", modal_hdr->db2_1);
+ PR_EEP("QPSK Driver2_Bias", modal_hdr->db2_2);
+ PR_EEP("16QAM Driver2_Bias", modal_hdr->db2_3);
+ PR_EEP("64QAM Driver2_Bias", modal_hdr->db2_4);
+ PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl);
+ PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart);
+ PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn);
+ PR_EEP("HT40 Power Inc.", modal_hdr->ht40PowerIncForPdadc);
+ PR_EEP("Chain0 bswAtten", modal_hdr->bswAtten[0]);
+ PR_EEP("Chain0 bswMargin", modal_hdr->bswMargin[0]);
+ PR_EEP("HT40 Switch Settle", modal_hdr->swSettleHt40);
+ PR_EEP("Chain0 xatten2Db", modal_hdr->xatten2Db[0]);
+ PR_EEP("Chain0 xatten2Margin", modal_hdr->xatten2Margin[0]);
+ PR_EEP("Ant. Diversity ctl1", modal_hdr->antdiv_ctl1);
+ PR_EEP("Ant. Diversity ctl2", modal_hdr->antdiv_ctl2);
+ PR_EEP("TX Diversity", modal_hdr->tx_diversity);
+
+ return len;
+}
+
+static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+ u8 *buf, u32 len, u32 size)
+{
+ struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
+ struct base_eep_header_4k *pBase = &eep->baseEepHeader;
+
+ if (!dump_base_hdr) {
+ len += snprintf(buf + len, size - len,
+ "%20s :\n", "2GHz modal Header");
+ len += ath9k_dump_4k_modal_eeprom(buf, len, size,
+ &eep->modalHeader);
+ goto out;
+ }
+
+ PR_EEP("Major Version", pBase->version >> 12);
+ PR_EEP("Minor Version", pBase->version & 0xFFF);
+ PR_EEP("Checksum", pBase->checksum);
+ PR_EEP("Length", pBase->length);
+ PR_EEP("RegDomain1", pBase->regDmn[0]);
+ PR_EEP("RegDomain2", pBase->regDmn[1]);
+ PR_EEP("TX Mask", pBase->txMask);
+ PR_EEP("RX Mask", pBase->rxMask);
+ PR_EEP("Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A));
+ PR_EEP("Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G));
+ PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_2G_HT20));
+ PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_2G_HT40));
+ PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_5G_HT20));
+ PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_5G_HT40));
+ PR_EEP("Big Endian", !!(pBase->eepMisc & 0x01));
+ PR_EEP("Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF);
+ PR_EEP("Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF);
+ PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF);
+ PR_EEP("TX Gain type", pBase->txGainType);
+
+ len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+ pBase->macAddr);
+
+out:
+ if (len > size)
+ len = size;
+
+ return len;
+}
+#else
+static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+ u8 *buf, u32 len, u32 size)
+{
+ return 0;
+}
+#endif
+
+
#undef SIZE_EEPROM_4K
static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
@@ -203,15 +315,13 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah,
case EEP_NFTHRESH_2:
return pModal->noiseFloorThreshCh[0];
case EEP_MAC_LSW:
- return pBase->macAddr[0] << 8 | pBase->macAddr[1];
+ return get_unaligned_be16(pBase->macAddr);
case EEP_MAC_MID:
- return pBase->macAddr[2] << 8 | pBase->macAddr[3];
+ return get_unaligned_be16(pBase->macAddr + 2);
case EEP_MAC_MSW:
- return pBase->macAddr[4] << 8 | pBase->macAddr[5];
+ return get_unaligned_be16(pBase->macAddr + 4);
case EEP_REG_0:
return pBase->regDmn[0];
- case EEP_REG_1:
- return pBase->regDmn[1];
case EEP_OP_CAP:
return pBase->deviceCap;
case EEP_OP_MODE:
@@ -237,18 +347,16 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah,
case EEP_ANT_DIV_CTL1:
return pModal->antdiv_ctl1;
case EEP_TXGAIN_TYPE:
- if (ver_minor >= AR5416_EEP_MINOR_VER_19)
- return pBase->txGainType;
- else
- return AR5416_EEP_TXGAIN_ORIGINAL;
+ return pBase->txGainType;
+ case EEP_ANTENNA_GAIN_2G:
+ return pModal->antennaGainCh[0];
default:
return 0;
}
}
static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah,
- struct ath9k_channel *chan,
- int16_t *pTxPowerIndexOffset)
+ struct ath9k_channel *chan)
{
struct ath_common *common = ath9k_hw_common(ah);
struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k;
@@ -297,12 +405,7 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah,
REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, 0);
for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) {
- if (AR_SREV_5416_20_OR_LATER(ah) &&
- (ah->rxchainmask == 5 || ah->txchainmask == 5) &&
- (i != 0)) {
- regChainOffset = (i == 1) ? 0x2000 : 0x1000;
- } else
- regChainOffset = i * 0x1000;
+ regChainOffset = i * 0x1000;
if (pEepData->baseEepHeader.txMask & (1 << i)) {
pRawDataset = pEepData->calPierData2G[i];
@@ -315,26 +418,21 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah,
ENABLE_REGWRITE_BUFFER(ah);
- if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) {
- REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
- SM(pdGainOverlap_t2,
- AR_PHY_TPCRG5_PD_GAIN_OVERLAP)
- | SM(gainBoundaries[0],
- AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)
- | SM(gainBoundaries[1],
- AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)
- | SM(gainBoundaries[2],
- AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)
- | SM(gainBoundaries[3],
- AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
- }
+ REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
+ SM(pdGainOverlap_t2,
+ AR_PHY_TPCRG5_PD_GAIN_OVERLAP)
+ | SM(gainBoundaries[0],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)
+ | SM(gainBoundaries[1],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)
+ | SM(gainBoundaries[2],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)
+ | SM(gainBoundaries[3],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset;
for (j = 0; j < 32; j++) {
- reg32 = ((pdadcValues[4 * j + 0] & 0xFF) << 0) |
- ((pdadcValues[4 * j + 1] & 0xFF) << 8) |
- ((pdadcValues[4 * j + 2] & 0xFF) << 16)|
- ((pdadcValues[4 * j + 3] & 0xFF) << 24);
+ reg32 = get_unaligned_le32(&pdadcValues[4 * j]);
REG_WRITE(ah, regOffset, reg32);
ath_dbg(common, ATH_DBG_EEPROM,
@@ -358,16 +456,13 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah,
REGWRITE_BUFFER_FLUSH(ah);
}
}
-
- *pTxPowerIndexOffset = 0;
}
static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah,
struct ath9k_channel *chan,
int16_t *ratesArray,
u16 cfgCtl,
- u16 AntennaReduction,
- u16 twiceMaxRegulatoryPower,
+ u16 antenna_reduction,
u16 powerLimit)
{
#define CMP_TEST_GRP \
@@ -376,20 +471,16 @@ static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah,
|| (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == \
((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))
- struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
int i;
- int16_t twiceLargestAntenna;
u16 twiceMinEdgePower;
u16 twiceMaxEdgePower = MAX_RATE_POWER;
- u16 scaledPower = 0, minCtlPower, maxRegAllowedPower;
+ u16 scaledPower = 0, minCtlPower;
u16 numCtlModes;
const u16 *pCtlMode;
u16 ctlMode, freq;
struct chan_centers centers;
struct cal_ctl_data_4k *rep;
struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k;
- static const u16 tpScaleReductionTable[5] =
- { 0, 3, 6, 9, MAX_RATE_POWER };
struct cal_target_power_leg targetPowerOfdm, targetPowerCck = {
0, { 0, 0, 0, 0}
};
@@ -407,19 +498,7 @@ static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah,
ath9k_hw_get_channel_centers(ah, chan, &centers);
- twiceLargestAntenna = pEepData->modalHeader.antennaGainCh[0];
- twiceLargestAntenna = (int16_t)min(AntennaReduction -
- twiceLargestAntenna, 0);
-
- maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna;
- if (regulatory->tp_scale != ATH9K_TP_SCALE_MAX) {
- maxRegAllowedPower -=
- (tpScaleReductionTable[(regulatory->tp_scale)] * 2);
- }
-
- scaledPower = min(powerLimit, maxRegAllowedPower);
- scaledPower = max((u16)0, scaledPower);
-
+ scaledPower = powerLimit - antenna_reduction;
numCtlModes = ARRAY_SIZE(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40;
pCtlMode = ctlModesFor11g;
@@ -575,14 +654,12 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah,
struct ath9k_channel *chan,
u16 cfgCtl,
u8 twiceAntennaReduction,
- u8 twiceMaxRegulatoryPower,
u8 powerLimit, bool test)
{
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k;
struct modal_eep_4k_header *pModal = &pEepData->modalHeader;
int16_t ratesArray[Ar5416RateSize];
- int16_t txPowerIndexOffset = 0;
u8 ht40PowerIncForPdadc = 2;
int i;
@@ -596,14 +673,12 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah,
ath9k_hw_set_4k_power_per_rate_table(ah, chan,
&ratesArray[0], cfgCtl,
twiceAntennaReduction,
- twiceMaxRegulatoryPower,
powerLimit);
- ath9k_hw_set_4k_power_cal_table(ah, chan, &txPowerIndexOffset);
+ ath9k_hw_set_4k_power_cal_table(ah, chan);
regulatory->max_power_level = 0;
for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
- ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]);
if (ratesArray[i] > MAX_RATE_POWER)
ratesArray[i] = MAX_RATE_POWER;
@@ -614,19 +689,8 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah,
if (test)
return;
- /* Update regulatory */
- i = rate6mb;
- if (IS_CHAN_HT40(chan))
- i = rateHt40_0;
- else if (IS_CHAN_HT20(chan))
- i = rateHt20_0;
-
- regulatory->max_power_level = ratesArray[i];
-
- if (AR_SREV_9280_20_OR_LATER(ah)) {
- for (i = 0; i < Ar5416RateSize; i++)
- ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2;
- }
+ for (i = 0; i < Ar5416RateSize; i++)
+ ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2;
ENABLE_REGWRITE_BUFFER(ah);
@@ -696,28 +760,6 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah,
REGWRITE_BUFFER_FLUSH(ah);
}
-static void ath9k_hw_4k_set_addac(struct ath_hw *ah,
- struct ath9k_channel *chan)
-{
- struct modal_eep_4k_header *pModal;
- struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
- u8 biaslevel;
-
- if (ah->hw_version.macVersion != AR_SREV_VERSION_9160)
- return;
-
- if (ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_MINOR_VER_7)
- return;
-
- pModal = &eep->modalHeader;
-
- if (pModal->xpaBiasLvl != 0xff) {
- biaslevel = pModal->xpaBiasLvl;
- INI_RA(&ah->iniAddac, 7, 1) =
- (INI_RA(&ah->iniAddac, 7, 1) & (~0x18)) | biaslevel << 3;
- }
-}
-
static void ath9k_hw_4k_set_gain(struct ath_hw *ah,
struct modal_eep_4k_header *pModal,
struct ar5416_eeprom_4k *eep,
@@ -785,6 +827,7 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
u8 txRxAttenLocal;
u8 ob[5], db1[5], db2[5];
u8 ant_div_control1, ant_div_control2;
+ u8 bb_desired_scale;
u32 regVal;
pModal = &eep->modalHeader;
@@ -1004,30 +1047,29 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
AR_PHY_SETTLING_SWITCH,
pModal->swSettleHt40);
}
- if (AR_SREV_9271(ah) || AR_SREV_9285(ah)) {
- u8 bb_desired_scale = (pModal->bb_scale_smrt_antenna &
- EEP_4K_BB_DESIRED_SCALE_MASK);
- if ((pBase->txGainType == 0) && (bb_desired_scale != 0)) {
- u32 pwrctrl, mask, clr;
-
- mask = BIT(0)|BIT(5)|BIT(10)|BIT(15)|BIT(20)|BIT(25);
- pwrctrl = mask * bb_desired_scale;
- clr = mask * 0x1f;
- REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr);
- REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr);
- REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr);
-
- mask = BIT(0)|BIT(5)|BIT(15);
- pwrctrl = mask * bb_desired_scale;
- clr = mask * 0x1f;
- REG_RMW(ah, AR_PHY_TX_PWRCTRL9, pwrctrl, clr);
-
- mask = BIT(0)|BIT(5);
- pwrctrl = mask * bb_desired_scale;
- clr = mask * 0x1f;
- REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr);
- REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr);
- }
+
+ bb_desired_scale = (pModal->bb_scale_smrt_antenna &
+ EEP_4K_BB_DESIRED_SCALE_MASK);
+ if ((pBase->txGainType == 0) && (bb_desired_scale != 0)) {
+ u32 pwrctrl, mask, clr;
+
+ mask = BIT(0)|BIT(5)|BIT(10)|BIT(15)|BIT(20)|BIT(25);
+ pwrctrl = mask * bb_desired_scale;
+ clr = mask * 0x1f;
+ REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr);
+ REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr);
+ REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr);
+
+ mask = BIT(0)|BIT(5)|BIT(15);
+ pwrctrl = mask * bb_desired_scale;
+ clr = mask * 0x1f;
+ REG_RMW(ah, AR_PHY_TX_PWRCTRL9, pwrctrl, clr);
+
+ mask = BIT(0)|BIT(5);
+ pwrctrl = mask * bb_desired_scale;
+ clr = mask * 0x1f;
+ REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr);
+ REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr);
}
}
@@ -1065,10 +1107,10 @@ const struct eeprom_ops eep_4k_ops = {
.check_eeprom = ath9k_hw_4k_check_eeprom,
.get_eeprom = ath9k_hw_4k_get_eeprom,
.fill_eeprom = ath9k_hw_4k_fill_eeprom,
+ .dump_eeprom = ath9k_hw_4k_dump_eeprom,
.get_eeprom_ver = ath9k_hw_4k_get_eeprom_ver,
.get_eeprom_rev = ath9k_hw_4k_get_eeprom_rev,
.set_board_values = ath9k_hw_4k_set_board_values,
- .set_addac = ath9k_hw_4k_set_addac,
.set_txpower = ath9k_hw_4k_set_txpower,
.get_spur_channel = ath9k_hw_4k_get_spur_channel
};
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
index 343fc9f946db..4f5c50a87ce3 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <asm/unaligned.h>
#include "hw.h"
#include "ar9002_phy.h"
@@ -75,6 +76,111 @@ static bool ath9k_hw_ar9287_fill_eeprom(struct ath_hw *ah)
return __ath9k_hw_ar9287_fill_eeprom(ah);
}
+#if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS)
+static u32 ar9287_dump_modal_eeprom(char *buf, u32 len, u32 size,
+ struct modal_eep_ar9287_header *modal_hdr)
+{
+ PR_EEP("Chain0 Ant. Control", modal_hdr->antCtrlChain[0]);
+ PR_EEP("Chain1 Ant. Control", modal_hdr->antCtrlChain[1]);
+ PR_EEP("Ant. Common Control", modal_hdr->antCtrlCommon);
+ PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]);
+ PR_EEP("Chain1 Ant. Gain", modal_hdr->antennaGainCh[1]);
+ PR_EEP("Switch Settle", modal_hdr->switchSettling);
+ PR_EEP("Chain0 TxRxAtten", modal_hdr->txRxAttenCh[0]);
+ PR_EEP("Chain1 TxRxAtten", modal_hdr->txRxAttenCh[1]);
+ PR_EEP("Chain0 RxTxMargin", modal_hdr->rxTxMarginCh[0]);
+ PR_EEP("Chain1 RxTxMargin", modal_hdr->rxTxMarginCh[1]);
+ PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize);
+ PR_EEP("txEndToXpaOff", modal_hdr->txEndToXpaOff);
+ PR_EEP("txEndToRxOn", modal_hdr->txEndToRxOn);
+ PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn);
+ PR_EEP("CCA Threshold)", modal_hdr->thresh62);
+ PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]);
+ PR_EEP("Chain1 NF Threshold", modal_hdr->noiseFloorThreshCh[1]);
+ PR_EEP("xpdGain", modal_hdr->xpdGain);
+ PR_EEP("External PD", modal_hdr->xpd);
+ PR_EEP("Chain0 I Coefficient", modal_hdr->iqCalICh[0]);
+ PR_EEP("Chain1 I Coefficient", modal_hdr->iqCalICh[1]);
+ PR_EEP("Chain0 Q Coefficient", modal_hdr->iqCalQCh[0]);
+ PR_EEP("Chain1 Q Coefficient", modal_hdr->iqCalQCh[1]);
+ PR_EEP("pdGainOverlap", modal_hdr->pdGainOverlap);
+ PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl);
+ PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart);
+ PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn);
+ PR_EEP("HT40 Power Inc.", modal_hdr->ht40PowerIncForPdadc);
+ PR_EEP("Chain0 bswAtten", modal_hdr->bswAtten[0]);
+ PR_EEP("Chain1 bswAtten", modal_hdr->bswAtten[1]);
+ PR_EEP("Chain0 bswMargin", modal_hdr->bswMargin[0]);
+ PR_EEP("Chain1 bswMargin", modal_hdr->bswMargin[1]);
+ PR_EEP("HT40 Switch Settle", modal_hdr->swSettleHt40);
+ PR_EEP("AR92x7 Version", modal_hdr->version);
+ PR_EEP("DriverBias1", modal_hdr->db1);
+ PR_EEP("DriverBias2", modal_hdr->db1);
+ PR_EEP("CCK OutputBias", modal_hdr->ob_cck);
+ PR_EEP("PSK OutputBias", modal_hdr->ob_psk);
+ PR_EEP("QAM OutputBias", modal_hdr->ob_qam);
+ PR_EEP("PAL_OFF OutputBias", modal_hdr->ob_pal_off);
+
+ return len;
+}
+
+static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+ u8 *buf, u32 len, u32 size)
+{
+ struct ar9287_eeprom *eep = &ah->eeprom.map9287;
+ struct base_eep_ar9287_header *pBase = &eep->baseEepHeader;
+
+ if (!dump_base_hdr) {
+ len += snprintf(buf + len, size - len,
+ "%20s :\n", "2GHz modal Header");
+ len += ar9287_dump_modal_eeprom(buf, len, size,
+ &eep->modalHeader);
+ goto out;
+ }
+
+ PR_EEP("Major Version", pBase->version >> 12);
+ PR_EEP("Minor Version", pBase->version & 0xFFF);
+ PR_EEP("Checksum", pBase->checksum);
+ PR_EEP("Length", pBase->length);
+ PR_EEP("RegDomain1", pBase->regDmn[0]);
+ PR_EEP("RegDomain2", pBase->regDmn[1]);
+ PR_EEP("TX Mask", pBase->txMask);
+ PR_EEP("RX Mask", pBase->rxMask);
+ PR_EEP("Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A));
+ PR_EEP("Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G));
+ PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_2G_HT20));
+ PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_2G_HT40));
+ PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_5G_HT20));
+ PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_5G_HT40));
+ PR_EEP("Big Endian", !!(pBase->eepMisc & 0x01));
+ PR_EEP("Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF);
+ PR_EEP("Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF);
+ PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF);
+ PR_EEP("Power Table Offset", pBase->pwrTableOffset);
+ PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl);
+
+ len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+ pBase->macAddr);
+
+out:
+ if (len > size)
+ len = size;
+
+ return len;
+}
+#else
+static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+ u8 *buf, u32 len, u32 size)
+{
+ return 0;
+}
+#endif
+
+
static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah)
{
u32 sum = 0, el, integer;
@@ -195,15 +301,13 @@ static u32 ath9k_hw_ar9287_get_eeprom(struct ath_hw *ah,
case EEP_NFTHRESH_2:
return pModal->noiseFloorThreshCh[0];
case EEP_MAC_LSW:
- return pBase->macAddr[0] << 8 | pBase->macAddr[1];
+ return get_unaligned_be16(pBase->macAddr);
case EEP_MAC_MID:
- return pBase->macAddr[2] << 8 | pBase->macAddr[3];
+ return get_unaligned_be16(pBase->macAddr + 2);
case EEP_MAC_MSW:
- return pBase->macAddr[4] << 8 | pBase->macAddr[5];
+ return get_unaligned_be16(pBase->macAddr + 4);
case EEP_REG_0:
return pBase->regDmn[0];
- case EEP_REG_1:
- return pBase->regDmn[1];
case EEP_OP_CAP:
return pBase->deviceCap;
case EEP_OP_MODE:
@@ -230,6 +334,9 @@ static u32 ath9k_hw_ar9287_get_eeprom(struct ath_hw *ah,
return pBase->tempSensSlopePalOn;
else
return 0;
+ case EEP_ANTENNA_GAIN_2G:
+ return max_t(u8, pModal->antennaGainCh[0],
+ pModal->antennaGainCh[1]);
default:
return 0;
}
@@ -306,8 +413,7 @@ static void ar9287_eeprom_olpc_set_pdadcs(struct ath_hw *ah,
}
static void ath9k_hw_set_ar9287_power_cal_table(struct ath_hw *ah,
- struct ath9k_channel *chan,
- int16_t *pTxPowerIndexOffset)
+ struct ath9k_channel *chan)
{
struct cal_data_per_freq_ar9287 *pRawDataset;
struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop;
@@ -434,10 +540,7 @@ static void ath9k_hw_set_ar9287_power_cal_table(struct ath_hw *ah,
(672 << 2) + regChainOffset;
for (j = 0; j < 32; j++) {
- reg32 = ((pdadcValues[4*j + 0] & 0xFF) << 0)
- | ((pdadcValues[4*j + 1] & 0xFF) << 8)
- | ((pdadcValues[4*j + 2] & 0xFF) << 16)
- | ((pdadcValues[4*j + 3] & 0xFF) << 24);
+ reg32 = get_unaligned_le32(&pdadcValues[4 * j]);
REG_WRITE(ah, regOffset, reg32);
regOffset += 4;
@@ -446,16 +549,13 @@ static void ath9k_hw_set_ar9287_power_cal_table(struct ath_hw *ah,
REGWRITE_BUFFER_FLUSH(ah);
}
}
-
- *pTxPowerIndexOffset = 0;
}
static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah,
struct ath9k_channel *chan,
int16_t *ratesArray,
u16 cfgCtl,
- u16 AntennaReduction,
- u16 twiceMaxRegulatoryPower,
+ u16 antenna_reduction,
u16 powerLimit)
{
#define CMP_CTL \
@@ -469,12 +569,8 @@ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah,
#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6
#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10
- struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
u16 twiceMaxEdgePower = MAX_RATE_POWER;
- static const u16 tpScaleReductionTable[5] =
- { 0, 3, 6, 9, MAX_RATE_POWER };
int i;
- int16_t twiceLargestAntenna;
struct cal_ctl_data_ar9287 *rep;
struct cal_target_power_leg targetPowerOfdm = {0, {0, 0, 0, 0} },
targetPowerCck = {0, {0, 0, 0, 0} };
@@ -482,7 +578,7 @@ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah,
targetPowerCckExt = {0, {0, 0, 0, 0} };
struct cal_target_power_ht targetPowerHt20,
targetPowerHt40 = {0, {0, 0, 0, 0} };
- u16 scaledPower = 0, minCtlPower, maxRegAllowedPower;
+ u16 scaledPower = 0, minCtlPower;
static const u16 ctlModesFor11g[] = {
CTL_11B, CTL_11G, CTL_2GHT20,
CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40
@@ -497,24 +593,7 @@ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah,
tx_chainmask = ah->txchainmask;
ath9k_hw_get_channel_centers(ah, chan, &centers);
-
- /* Compute TxPower reduction due to Antenna Gain */
- twiceLargestAntenna = max(pEepData->modalHeader.antennaGainCh[0],
- pEepData->modalHeader.antennaGainCh[1]);
- twiceLargestAntenna = (int16_t)min((AntennaReduction) -
- twiceLargestAntenna, 0);
-
- /*
- * scaledPower is the minimum of the user input power level
- * and the regulatory allowed power level.
- */
- maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna;
-
- if (regulatory->tp_scale != ATH9K_TP_SCALE_MAX)
- maxRegAllowedPower -=
- (tpScaleReductionTable[(regulatory->tp_scale)] * 2);
-
- scaledPower = min(powerLimit, maxRegAllowedPower);
+ scaledPower = powerLimit - antenna_reduction;
/*
* Reduce scaled Power by number of chains active
@@ -715,14 +794,12 @@ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah,
static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah,
struct ath9k_channel *chan, u16 cfgCtl,
u8 twiceAntennaReduction,
- u8 twiceMaxRegulatoryPower,
u8 powerLimit, bool test)
{
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ar9287_eeprom *pEepData = &ah->eeprom.map9287;
struct modal_eep_ar9287_header *pModal = &pEepData->modalHeader;
int16_t ratesArray[Ar5416RateSize];
- int16_t txPowerIndexOffset = 0;
u8 ht40PowerIncForPdadc = 2;
int i;
@@ -735,14 +812,12 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah,
ath9k_hw_set_ar9287_power_per_rate_table(ah, chan,
&ratesArray[0], cfgCtl,
twiceAntennaReduction,
- twiceMaxRegulatoryPower,
powerLimit);
- ath9k_hw_set_ar9287_power_cal_table(ah, chan, &txPowerIndexOffset);
+ ath9k_hw_set_ar9287_power_cal_table(ah, chan);
regulatory->max_power_level = 0;
for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
- ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]);
if (ratesArray[i] > MAX_RATE_POWER)
ratesArray[i] = MAX_RATE_POWER;
@@ -753,17 +828,8 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah,
if (test)
return;
- if (IS_CHAN_2GHZ(chan))
- i = rate1l;
- else
- i = rate6mb;
-
- regulatory->max_power_level = ratesArray[i];
-
- if (AR_SREV_9280_20_OR_LATER(ah)) {
- for (i = 0; i < Ar5416RateSize; i++)
- ratesArray[i] -= AR9287_PWR_TABLE_OFFSET_DB * 2;
- }
+ for (i = 0; i < Ar5416RateSize; i++)
+ ratesArray[i] -= AR9287_PWR_TABLE_OFFSET_DB * 2;
ENABLE_REGWRITE_BUFFER(ah);
@@ -853,11 +919,6 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah,
REGWRITE_BUFFER_FLUSH(ah);
}
-static void ath9k_hw_ar9287_set_addac(struct ath_hw *ah,
- struct ath9k_channel *chan)
-{
-}
-
static void ath9k_hw_ar9287_set_board_values(struct ath_hw *ah,
struct ath9k_channel *chan)
{
@@ -1005,10 +1066,10 @@ const struct eeprom_ops eep_ar9287_ops = {
.check_eeprom = ath9k_hw_ar9287_check_eeprom,
.get_eeprom = ath9k_hw_ar9287_get_eeprom,
.fill_eeprom = ath9k_hw_ar9287_fill_eeprom,
+ .dump_eeprom = ath9k_hw_ar9287_dump_eeprom,
.get_eeprom_ver = ath9k_hw_ar9287_get_eeprom_ver,
.get_eeprom_rev = ath9k_hw_ar9287_get_eeprom_rev,
.set_board_values = ath9k_hw_ar9287_set_board_values,
- .set_addac = ath9k_hw_ar9287_set_addac,
.set_txpower = ath9k_hw_ar9287_set_txpower,
.get_spur_channel = ath9k_hw_ar9287_get_spur_channel
};
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index 17f0a6806207..81e629671679 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <asm/unaligned.h>
#include "hw.h"
#include "ar9002_phy.h"
@@ -132,6 +133,136 @@ static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah)
#undef SIZE_EEPROM_DEF
+#if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS)
+static u32 ath9k_def_dump_modal_eeprom(char *buf, u32 len, u32 size,
+ struct modal_eep_header *modal_hdr)
+{
+ PR_EEP("Chain0 Ant. Control", modal_hdr->antCtrlChain[0]);
+ PR_EEP("Chain1 Ant. Control", modal_hdr->antCtrlChain[1]);
+ PR_EEP("Chain2 Ant. Control", modal_hdr->antCtrlChain[2]);
+ PR_EEP("Ant. Common Control", modal_hdr->antCtrlCommon);
+ PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]);
+ PR_EEP("Chain1 Ant. Gain", modal_hdr->antennaGainCh[1]);
+ PR_EEP("Chain2 Ant. Gain", modal_hdr->antennaGainCh[2]);
+ PR_EEP("Switch Settle", modal_hdr->switchSettling);
+ PR_EEP("Chain0 TxRxAtten", modal_hdr->txRxAttenCh[0]);
+ PR_EEP("Chain1 TxRxAtten", modal_hdr->txRxAttenCh[1]);
+ PR_EEP("Chain2 TxRxAtten", modal_hdr->txRxAttenCh[2]);
+ PR_EEP("Chain0 RxTxMargin", modal_hdr->rxTxMarginCh[0]);
+ PR_EEP("Chain1 RxTxMargin", modal_hdr->rxTxMarginCh[1]);
+ PR_EEP("Chain2 RxTxMargin", modal_hdr->rxTxMarginCh[2]);
+ PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize);
+ PR_EEP("PGA Desired size", modal_hdr->pgaDesiredSize);
+ PR_EEP("Chain0 xlna Gain", modal_hdr->xlnaGainCh[0]);
+ PR_EEP("Chain1 xlna Gain", modal_hdr->xlnaGainCh[1]);
+ PR_EEP("Chain2 xlna Gain", modal_hdr->xlnaGainCh[2]);
+ PR_EEP("txEndToXpaOff", modal_hdr->txEndToXpaOff);
+ PR_EEP("txEndToRxOn", modal_hdr->txEndToRxOn);
+ PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn);
+ PR_EEP("CCA Threshold)", modal_hdr->thresh62);
+ PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]);
+ PR_EEP("Chain1 NF Threshold", modal_hdr->noiseFloorThreshCh[1]);
+ PR_EEP("Chain2 NF Threshold", modal_hdr->noiseFloorThreshCh[2]);
+ PR_EEP("xpdGain", modal_hdr->xpdGain);
+ PR_EEP("External PD", modal_hdr->xpd);
+ PR_EEP("Chain0 I Coefficient", modal_hdr->iqCalICh[0]);
+ PR_EEP("Chain1 I Coefficient", modal_hdr->iqCalICh[1]);
+ PR_EEP("Chain2 I Coefficient", modal_hdr->iqCalICh[2]);
+ PR_EEP("Chain0 Q Coefficient", modal_hdr->iqCalQCh[0]);
+ PR_EEP("Chain1 Q Coefficient", modal_hdr->iqCalQCh[1]);
+ PR_EEP("Chain2 Q Coefficient", modal_hdr->iqCalQCh[2]);
+ PR_EEP("pdGainOverlap", modal_hdr->pdGainOverlap);
+ PR_EEP("Chain0 OutputBias", modal_hdr->ob);
+ PR_EEP("Chain0 DriverBias", modal_hdr->db);
+ PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl);
+ PR_EEP("2chain pwr decrease", modal_hdr->pwrDecreaseFor2Chain);
+ PR_EEP("3chain pwr decrease", modal_hdr->pwrDecreaseFor3Chain);
+ PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart);
+ PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn);
+ PR_EEP("HT40 Power Inc.", modal_hdr->ht40PowerIncForPdadc);
+ PR_EEP("Chain0 bswAtten", modal_hdr->bswAtten[0]);
+ PR_EEP("Chain1 bswAtten", modal_hdr->bswAtten[1]);
+ PR_EEP("Chain2 bswAtten", modal_hdr->bswAtten[2]);
+ PR_EEP("Chain0 bswMargin", modal_hdr->bswMargin[0]);
+ PR_EEP("Chain1 bswMargin", modal_hdr->bswMargin[1]);
+ PR_EEP("Chain2 bswMargin", modal_hdr->bswMargin[2]);
+ PR_EEP("HT40 Switch Settle", modal_hdr->swSettleHt40);
+ PR_EEP("Chain0 xatten2Db", modal_hdr->xatten2Db[0]);
+ PR_EEP("Chain1 xatten2Db", modal_hdr->xatten2Db[1]);
+ PR_EEP("Chain2 xatten2Db", modal_hdr->xatten2Db[2]);
+ PR_EEP("Chain0 xatten2Margin", modal_hdr->xatten2Margin[0]);
+ PR_EEP("Chain1 xatten2Margin", modal_hdr->xatten2Margin[1]);
+ PR_EEP("Chain2 xatten2Margin", modal_hdr->xatten2Margin[2]);
+ PR_EEP("Chain1 OutputBias", modal_hdr->ob_ch1);
+ PR_EEP("Chain1 DriverBias", modal_hdr->db_ch1);
+ PR_EEP("LNA Control", modal_hdr->lna_ctl);
+ PR_EEP("XPA Bias Freq0", modal_hdr->xpaBiasLvlFreq[0]);
+ PR_EEP("XPA Bias Freq1", modal_hdr->xpaBiasLvlFreq[1]);
+ PR_EEP("XPA Bias Freq2", modal_hdr->xpaBiasLvlFreq[2]);
+
+ return len;
+}
+
+static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+ u8 *buf, u32 len, u32 size)
+{
+ struct ar5416_eeprom_def *eep = &ah->eeprom.def;
+ struct base_eep_header *pBase = &eep->baseEepHeader;
+
+ if (!dump_base_hdr) {
+ len += snprintf(buf + len, size - len,
+ "%20s :\n", "2GHz modal Header");
+ len += ath9k_def_dump_modal_eeprom(buf, len, size,
+ &eep->modalHeader[0]);
+ len += snprintf(buf + len, size - len,
+ "%20s :\n", "5GHz modal Header");
+ len += ath9k_def_dump_modal_eeprom(buf, len, size,
+ &eep->modalHeader[1]);
+ goto out;
+ }
+
+ PR_EEP("Major Version", pBase->version >> 12);
+ PR_EEP("Minor Version", pBase->version & 0xFFF);
+ PR_EEP("Checksum", pBase->checksum);
+ PR_EEP("Length", pBase->length);
+ PR_EEP("RegDomain1", pBase->regDmn[0]);
+ PR_EEP("RegDomain2", pBase->regDmn[1]);
+ PR_EEP("TX Mask", pBase->txMask);
+ PR_EEP("RX Mask", pBase->rxMask);
+ PR_EEP("Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A));
+ PR_EEP("Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G));
+ PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_2G_HT20));
+ PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_2G_HT40));
+ PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_5G_HT20));
+ PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags &
+ AR5416_OPFLAGS_N_5G_HT40));
+ PR_EEP("Big Endian", !!(pBase->eepMisc & 0x01));
+ PR_EEP("Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF);
+ PR_EEP("Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF);
+ PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF);
+ PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl);
+
+ len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+ pBase->macAddr);
+
+out:
+ if (len > size)
+ len = size;
+
+ return len;
+}
+#else
+static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+ u8 *buf, u32 len, u32 size)
+{
+ return 0;
+}
+#endif
+
+
static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
{
struct ar5416_eeprom_def *eep =
@@ -269,6 +400,7 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
struct ar5416_eeprom_def *eep = &ah->eeprom.def;
struct modal_eep_header *pModal = eep->modalHeader;
struct base_eep_header *pBase = &eep->baseEepHeader;
+ int band = 0;
switch (param) {
case EEP_NFTHRESH_5:
@@ -276,15 +408,13 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
case EEP_NFTHRESH_2:
return pModal[1].noiseFloorThreshCh[0];
case EEP_MAC_LSW:
- return pBase->macAddr[0] << 8 | pBase->macAddr[1];
+ return get_unaligned_be16(pBase->macAddr);
case EEP_MAC_MID:
- return pBase->macAddr[2] << 8 | pBase->macAddr[3];
+ return get_unaligned_be16(pBase->macAddr + 2);
case EEP_MAC_MSW:
- return pBase->macAddr[4] << 8 | pBase->macAddr[5];
+ return get_unaligned_be16(pBase->macAddr + 4);
case EEP_REG_0:
return pBase->regDmn[0];
- case EEP_REG_1:
- return pBase->regDmn[1];
case EEP_OP_CAP:
return pBase->deviceCap;
case EEP_OP_MODE:
@@ -336,6 +466,14 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
return pBase->pwr_table_offset;
else
return AR5416_PWR_TABLE_OFFSET_DB;
+ case EEP_ANTENNA_GAIN_2G:
+ band = 1;
+ /* fall through */
+ case EEP_ANTENNA_GAIN_5G:
+ return max_t(u8, max_t(u8,
+ pModal[band].antennaGainCh[0],
+ pModal[band].antennaGainCh[1]),
+ pModal[band].antennaGainCh[2]);
default:
return 0;
}
@@ -416,8 +554,7 @@ static void ath9k_hw_def_set_board_values(struct ath_hw *ah,
break;
}
- if (AR_SREV_5416_20_OR_LATER(ah) &&
- (ah->rxchainmask == 5 || ah->txchainmask == 5) && (i != 0))
+ if ((ah->rxchainmask == 5 || ah->txchainmask == 5) && (i != 0))
regChainOffset = (i == 1) ? 0x2000 : 0x1000;
else
regChainOffset = i * 0x1000;
@@ -434,9 +571,8 @@ static void ath9k_hw_def_set_board_values(struct ath_hw *ah,
SM(pModal->iqCalQCh[i],
AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
- if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah))
- ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal,
- regChainOffset, i);
+ ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal,
+ regChainOffset, i);
}
if (AR_SREV_9280_20_OR_LATER(ah)) {
@@ -692,8 +828,7 @@ static void ath9k_adjust_pdadc_values(struct ath_hw *ah,
}
static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
- struct ath9k_channel *chan,
- int16_t *pTxPowerIndexOffset)
+ struct ath9k_channel *chan)
{
#define SM_PD_GAIN(x) SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##x)
#define SM_PDGAIN_B(x, y) \
@@ -763,8 +898,7 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
xpdGainValues[2]);
for (i = 0; i < AR5416_MAX_CHAINS; i++) {
- if (AR_SREV_5416_20_OR_LATER(ah) &&
- (ah->rxchainmask == 5 || ah->txchainmask == 5) &&
+ if ((ah->rxchainmask == 5 || ah->txchainmask == 5) &&
(i != 0)) {
regChainOffset = (i == 1) ? 0x2000 : 0x1000;
} else
@@ -805,36 +939,30 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
ENABLE_REGWRITE_BUFFER(ah);
- if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) {
- if (OLC_FOR_AR9280_20_LATER) {
- REG_WRITE(ah,
- AR_PHY_TPCRG5 + regChainOffset,
- SM(0x6,
- AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
- SM_PD_GAIN(1) | SM_PD_GAIN(2) |
- SM_PD_GAIN(3) | SM_PD_GAIN(4));
- } else {
- REG_WRITE(ah,
- AR_PHY_TPCRG5 + regChainOffset,
- SM(pdGainOverlap_t2,
- AR_PHY_TPCRG5_PD_GAIN_OVERLAP)|
- SM_PDGAIN_B(0, 1) |
- SM_PDGAIN_B(1, 2) |
- SM_PDGAIN_B(2, 3) |
- SM_PDGAIN_B(3, 4));
- }
+ if (OLC_FOR_AR9280_20_LATER) {
+ REG_WRITE(ah,
+ AR_PHY_TPCRG5 + regChainOffset,
+ SM(0x6,
+ AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
+ SM_PD_GAIN(1) | SM_PD_GAIN(2) |
+ SM_PD_GAIN(3) | SM_PD_GAIN(4));
+ } else {
+ REG_WRITE(ah,
+ AR_PHY_TPCRG5 + regChainOffset,
+ SM(pdGainOverlap_t2,
+ AR_PHY_TPCRG5_PD_GAIN_OVERLAP)|
+ SM_PDGAIN_B(0, 1) |
+ SM_PDGAIN_B(1, 2) |
+ SM_PDGAIN_B(2, 3) |
+ SM_PDGAIN_B(3, 4));
}
-
ath9k_adjust_pdadc_values(ah, pwr_table_offset,
diff, pdadcValues);
regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset;
for (j = 0; j < 32; j++) {
- reg32 = ((pdadcValues[4 * j + 0] & 0xFF) << 0) |
- ((pdadcValues[4 * j + 1] & 0xFF) << 8) |
- ((pdadcValues[4 * j + 2] & 0xFF) << 16)|
- ((pdadcValues[4 * j + 3] & 0xFF) << 24);
+ reg32 = get_unaligned_le32(&pdadcValues[4 * j]);
REG_WRITE(ah, regOffset, reg32);
ath_dbg(common, ATH_DBG_EEPROM,
@@ -857,7 +985,6 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
}
}
- *pTxPowerIndexOffset = 0;
#undef SM_PD_GAIN
#undef SM_PDGAIN_B
}
@@ -866,21 +993,15 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah,
struct ath9k_channel *chan,
int16_t *ratesArray,
u16 cfgCtl,
- u16 AntennaReduction,
- u16 twiceMaxRegulatoryPower,
+ u16 antenna_reduction,
u16 powerLimit)
{
#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */
#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 9 /* 10*log10(3)*2 */
- struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ar5416_eeprom_def *pEepData = &ah->eeprom.def;
u16 twiceMaxEdgePower = MAX_RATE_POWER;
- static const u16 tpScaleReductionTable[5] =
- { 0, 3, 6, 9, MAX_RATE_POWER };
-
int i;
- int16_t twiceLargestAntenna;
struct cal_ctl_data *rep;
struct cal_target_power_leg targetPowerOfdm, targetPowerCck = {
0, { 0, 0, 0, 0}
@@ -892,7 +1013,7 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah,
struct cal_target_power_ht targetPowerHt20, targetPowerHt40 = {
0, {0, 0, 0, 0}
};
- u16 scaledPower = 0, minCtlPower, maxRegAllowedPower;
+ u16 scaledPower = 0, minCtlPower;
static const u16 ctlModesFor11a[] = {
CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40
};
@@ -911,27 +1032,7 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah,
ath9k_hw_get_channel_centers(ah, chan, &centers);
- twiceLargestAntenna = max(
- pEepData->modalHeader
- [IS_CHAN_2GHZ(chan)].antennaGainCh[0],
- pEepData->modalHeader
- [IS_CHAN_2GHZ(chan)].antennaGainCh[1]);
-
- twiceLargestAntenna = max((u8)twiceLargestAntenna,
- pEepData->modalHeader
- [IS_CHAN_2GHZ(chan)].antennaGainCh[2]);
-
- twiceLargestAntenna = (int16_t)min(AntennaReduction -
- twiceLargestAntenna, 0);
-
- maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna;
-
- if (regulatory->tp_scale != ATH9K_TP_SCALE_MAX) {
- maxRegAllowedPower -=
- (tpScaleReductionTable[(regulatory->tp_scale)] * 2);
- }
-
- scaledPower = min(powerLimit, maxRegAllowedPower);
+ scaledPower = powerLimit - antenna_reduction;
switch (ar5416_get_ntxchains(tx_chainmask)) {
case 1:
@@ -1136,7 +1237,6 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
struct ath9k_channel *chan,
u16 cfgCtl,
u8 twiceAntennaReduction,
- u8 twiceMaxRegulatoryPower,
u8 powerLimit, bool test)
{
#define RT_AR_DELTA(x) (ratesArray[x] - cck_ofdm_delta)
@@ -1145,7 +1245,6 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
struct modal_eep_header *pModal =
&(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]);
int16_t ratesArray[Ar5416RateSize];
- int16_t txPowerIndexOffset = 0;
u8 ht40PowerIncForPdadc = 2;
int i, cck_ofdm_delta = 0;
@@ -1159,31 +1258,18 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
ath9k_hw_set_def_power_per_rate_table(ah, chan,
&ratesArray[0], cfgCtl,
twiceAntennaReduction,
- twiceMaxRegulatoryPower,
powerLimit);
- ath9k_hw_set_def_power_cal_table(ah, chan, &txPowerIndexOffset);
+ ath9k_hw_set_def_power_cal_table(ah, chan);
regulatory->max_power_level = 0;
for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
- ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]);
if (ratesArray[i] > MAX_RATE_POWER)
ratesArray[i] = MAX_RATE_POWER;
if (ratesArray[i] > regulatory->max_power_level)
regulatory->max_power_level = ratesArray[i];
}
- if (!test) {
- i = rate6mb;
-
- if (IS_CHAN_HT40(chan))
- i = rateHt40_0;
- else if (IS_CHAN_HT20(chan))
- i = rateHt20_0;
-
- regulatory->max_power_level = ratesArray[i];
- }
-
switch(ar5416_get_ntxchains(ah->txchainmask)) {
case 1:
break;
@@ -1338,6 +1424,7 @@ const struct eeprom_ops eep_def_ops = {
.check_eeprom = ath9k_hw_def_check_eeprom,
.get_eeprom = ath9k_hw_def_get_eeprom,
.fill_eeprom = ath9k_hw_def_fill_eeprom,
+ .dump_eeprom = ath9k_hw_def_dump_eeprom,
.get_eeprom_ver = ath9k_hw_def_get_eeprom_ver,
.get_eeprom_rev = ath9k_hw_def_get_eeprom_rev,
.set_board_values = ath9k_hw_def_set_board_values,
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index bc713fc28191..655576c8fdab 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -48,6 +48,8 @@ void ath_init_leds(struct ath_softc *sc)
sc->sc_ah->led_pin = ATH_LED_PIN_9485;
else if (AR_SREV_9300(sc->sc_ah))
sc->sc_ah->led_pin = ATH_LED_PIN_9300;
+ else if (AR_SREV_9462(sc->sc_ah))
+ sc->sc_ah->led_pin = ATH_LED_PIN_9462;
else
sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
}
@@ -82,9 +84,14 @@ void ath_init_leds(struct ath_softc *sc)
static bool ath_is_rfkill_set(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
+ bool is_blocked;
- return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
+ ath9k_ps_wakeup(sc);
+ is_blocked = ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
ah->rfkill_polarity;
+ ath9k_ps_restore(sc);
+
+ return is_blocked;
}
void ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
@@ -148,7 +155,8 @@ static void ath9k_gen_timer_start(struct ath_hw *ah,
if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
ath9k_hw_disable_interrupts(ah);
ah->imask |= ATH9K_INT_GENTIMER;
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_set_interrupts(ah);
+ ath9k_hw_enable_interrupts(ah);
}
}
@@ -162,7 +170,8 @@ static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
if (timer_table->timer_mask.val == 0) {
ath9k_hw_disable_interrupts(ah);
ah->imask &= ~ATH9K_INT_GENTIMER;
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_set_interrupts(ah);
+ ath9k_hw_enable_interrupts(ah);
}
}
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 260f1f37a60e..77c8ded8de57 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <asm/unaligned.h>
#include "htc.h"
/* identify firmware images */
@@ -37,6 +38,7 @@ static struct usb_device_id ath9k_hif_usb_ids[] = {
{ USB_DEVICE(0x04CA, 0x4605) }, /* Liteon */
{ USB_DEVICE(0x040D, 0x3801) }, /* VIA */
{ USB_DEVICE(0x0cf3, 0xb003) }, /* Ubiquiti WifiStation Ext */
+ { USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */
{ USB_DEVICE(0x0cf3, 0x7015),
.driver_info = AR9287_USB }, /* Atheros */
@@ -49,6 +51,8 @@ static struct usb_device_id ath9k_hif_usb_ids[] = {
.driver_info = AR9280_USB }, /* Netgear WNDA3200 */
{ USB_DEVICE(0x083A, 0xA704),
.driver_info = AR9280_USB }, /* SMC Networks */
+ { USB_DEVICE(0x0411, 0x017f),
+ .driver_info = AR9280_USB }, /* Sony UWA-BR100 */
{ USB_DEVICE(0x0cf3, 0x20ff),
.driver_info = STORAGE_DEVICE },
@@ -127,12 +131,14 @@ static int hif_usb_send_regout(struct hif_device_usb *hif_dev,
static void hif_usb_mgmt_cb(struct urb *urb)
{
struct cmd_buf *cmd = (struct cmd_buf *)urb->context;
- struct hif_device_usb *hif_dev = cmd->hif_dev;
+ struct hif_device_usb *hif_dev;
bool txok = true;
if (!cmd || !cmd->skb || !cmd->hif_dev)
return;
+ hif_dev = cmd->hif_dev;
+
switch (urb->status) {
case 0:
break;
@@ -555,8 +561,8 @@ static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev,
ptr = (u8 *) skb->data;
- pkt_len = ptr[index] + (ptr[index+1] << 8);
- pkt_tag = ptr[index+2] + (ptr[index+3] << 8);
+ pkt_len = get_unaligned_le16(ptr + index);
+ pkt_tag = get_unaligned_le16(ptr + index + 2);
if (pkt_tag != ATH_USB_RX_STREAM_MODE_TAG) {
RX_STAT_INC(skb_dropped);
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 5bc022087e65..da5596766d82 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -521,8 +521,6 @@ void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb,
int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv,
u8 enable_coex);
-void ath9k_htc_station_work(struct work_struct *work);
-void ath9k_htc_aggr_work(struct work_struct *work);
void ath9k_htc_ani_work(struct work_struct *work);
void ath9k_htc_start_ani(struct ath9k_htc_priv *priv);
void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv);
@@ -542,7 +540,6 @@ int ath9k_htc_tx_get_slot(struct ath9k_htc_priv *priv);
void ath9k_htc_tx_clear_slot(struct ath9k_htc_priv *priv, int slot);
void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv);
void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event);
-void ath9k_htc_tx_failed(struct ath9k_htc_priv *priv);
void ath9k_tx_failed_tasklet(unsigned long data);
void ath9k_htc_tx_cleanup_timer(unsigned long data);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
index aa6a73118706..57fe22b24247 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
@@ -79,7 +79,7 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
memset(&bs, 0, sizeof(bs));
- intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD;
+ intval = bss_conf->beacon_interval;
bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_interval);
/*
@@ -194,7 +194,7 @@ static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv,
u8 cmd_rsp;
u64 tsf;
- intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD;
+ intval = bss_conf->beacon_interval;
intval /= ATH9K_HTC_MAX_BCN_VIF;
nexttbtt = intval;
@@ -250,7 +250,7 @@ static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv,
u8 cmd_rsp;
u64 tsf;
- intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD;
+ intval = bss_conf->beacon_interval;
nexttbtt = intval;
/*
@@ -427,7 +427,7 @@ static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv,
u16 intval;
int slot;
- intval = priv->cur_beacon_conf.beacon_interval & ATH9K_BEACON_PERIOD;
+ intval = priv->cur_beacon_conf.beacon_interval;
tsf = be64_to_cpu(swba->tsf);
tsftu = TSF_TO_TU(tsf >> 32, tsf);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
index aa48b3abbc48..d3ff33c71aa5 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
@@ -623,11 +623,8 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
pBase9287->openLoopPwrCntl);
}
- len += snprintf(buf + len, size - len,
- "%20s : %02X:%02X:%02X:%02X:%02X:%02X\n",
- "MacAddress",
- pBase->macAddr[0], pBase->macAddr[1], pBase->macAddr[2],
- pBase->macAddr[3], pBase->macAddr[4], pBase->macAddr[5]);
+ len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+ pBase->macAddr);
if (len > size)
len = size;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
index db2352e5cc0d..e3a02eb8e0cc 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
@@ -228,8 +228,14 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv)
static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv)
{
- return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) ==
- priv->ah->rfkill_polarity;
+ bool is_blocked;
+
+ ath9k_htc_ps_wakeup(priv);
+ is_blocked = ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) ==
+ priv->ah->rfkill_polarity;
+ ath9k_htc_ps_restore(priv);
+
+ return is_blocked;
}
void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw)
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index 61e6d3950718..966661c9e586 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -509,8 +509,8 @@ static void setup_ht_cap(struct ath9k_htc_priv *priv,
memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
/* ath9k_htc supports only 1 or 2 stream devices */
- tx_streams = ath9k_cmn_count_streams(common->tx_chainmask, 2);
- rx_streams = ath9k_cmn_count_streams(common->rx_chainmask, 2);
+ tx_streams = ath9k_cmn_count_streams(priv->ah->txchainmask, 2);
+ rx_streams = ath9k_cmn_count_streams(priv->ah->rxchainmask, 2);
ath_dbg(common, ATH_DBG_CONFIG,
"TX streams %d, RX streams: %d\n",
@@ -572,25 +572,6 @@ err:
return -EINVAL;
}
-static void ath9k_init_crypto(struct ath9k_htc_priv *priv)
-{
- struct ath_common *common = ath9k_hw_common(priv->ah);
- int i = 0;
-
- /* Get the hardware key cache size. */
- common->keymax = AR_KEYTABLE_SIZE;
-
- if (priv->ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA)
- common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED;
-
- /*
- * Reset the key cache since some parts do not
- * reset the contents on initial power up.
- */
- for (i = 0; i < common->keymax; i++)
- ath_hw_keyreset(common, (u16) i);
-}
-
static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv)
{
if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
@@ -620,9 +601,6 @@ static void ath9k_init_misc(struct ath9k_htc_priv *priv)
{
struct ath_common *common = ath9k_hw_common(priv->ah);
- common->tx_chainmask = priv->ah->caps.tx_chainmask;
- common->rx_chainmask = priv->ah->caps.rx_chainmask;
-
memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
priv->ah->opmode = NL80211_IFTYPE_STATION;
@@ -666,7 +644,6 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
return -ENOMEM;
ah->hw_version.devid = devid;
- ah->hw_version.subsysid = 0; /* FIXME */
ah->hw_version.usbdev = drv_info;
ah->ah_flags |= AH_USE_EEPROM;
ah->reg_ops.read = ath9k_regread;
@@ -721,7 +698,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++)
priv->cur_beacon_conf.bslot[i] = NULL;
- ath9k_init_crypto(priv);
+ ath9k_cmn_init_crypto(ah);
ath9k_init_channels_rates(priv);
ath9k_init_misc(priv);
@@ -754,6 +731,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK |
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
hw->wiphy->interface_modes =
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 7b7796895432..0b9a0e8a4958 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -826,8 +826,7 @@ void ath9k_htc_ani_work(struct work_struct *work)
if (longcal || shortcal)
common->ani.caldone =
ath9k_hw_calibrate(ah, ah->curchan,
- common->rx_chainmask,
- longcal);
+ ah->rxchainmask, longcal);
ath9k_htc_ps_restore(priv);
}
@@ -1294,11 +1293,17 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
u32 rfilt;
mutex_lock(&priv->mutex);
- ath9k_htc_ps_wakeup(priv);
-
changed_flags &= SUPPORTED_FILTERS;
*total_flags &= SUPPORTED_FILTERS;
+ if (priv->op_flags & OP_INVALID) {
+ ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_ANY,
+ "Unable to configure filter on invalid state\n");
+ mutex_unlock(&priv->mutex);
+ return;
+ }
+ ath9k_htc_ps_wakeup(priv);
+
priv->rxfilter = *total_flags;
rfilt = ath9k_htc_calcrxfilter(priv);
ath9k_hw_setrxfilter(priv->ah, rfilt);
@@ -1347,7 +1352,8 @@ static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
return ret;
}
-static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
+static int ath9k_htc_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct ath9k_htc_priv *priv = hw->priv;
@@ -1558,7 +1564,8 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
mutex_unlock(&priv->mutex);
}
-static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
+static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct ath9k_htc_priv *priv = hw->priv;
u64 tsf;
@@ -1572,7 +1579,8 @@ static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
return tsf;
}
-static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+static void ath9k_htc_set_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u64 tsf)
{
struct ath9k_htc_priv *priv = hw->priv;
@@ -1583,7 +1591,8 @@ static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
mutex_unlock(&priv->mutex);
}
-static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
+static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct ath9k_htc_priv *priv = hw->priv;
@@ -1731,6 +1740,22 @@ out:
return ret;
}
+
+static int ath9k_htc_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct ath9k_htc_priv *priv = hw->priv;
+ struct ath_hw *ah = priv->ah;
+ struct ath9k_mib_stats *mib_stats = &ah->ah_mibStats;
+
+ stats->dot11ACKFailureCount = mib_stats->ackrcv_bad;
+ stats->dot11RTSFailureCount = mib_stats->rts_bad;
+ stats->dot11FCSErrorCount = mib_stats->fcs_bad;
+ stats->dot11RTSSuccessCount = mib_stats->rts_good;
+
+ return 0;
+}
+
struct ieee80211_ops ath9k_htc_ops = {
.tx = ath9k_htc_tx,
.start = ath9k_htc_start,
@@ -1754,4 +1779,5 @@ struct ieee80211_ops ath9k_htc_ops = {
.rfkill_poll = ath9k_htc_rfkill_poll_state,
.set_coverage_class = ath9k_htc_set_coverage_class,
.set_bitrate_mask = ath9k_htc_set_bitrate_mask,
+ .get_stats = ath9k_htc_get_stats,
};
diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
index 2f3e07263fcb..e74c233757a2 100644
--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
+++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
@@ -22,10 +22,12 @@
/* Hardware core and driver accessible callbacks */
static inline void ath9k_hw_configpcipowersave(struct ath_hw *ah,
- int restore,
- int power_off)
+ bool power_off)
{
- ath9k_hw_ops(ah)->config_pci_powersave(ah, restore, power_off);
+ if (ah->aspm_enabled != true)
+ return;
+
+ ath9k_hw_ops(ah)->config_pci_powersave(ah, power_off);
}
static inline void ath9k_hw_rxena(struct ath_hw *ah)
@@ -39,11 +41,6 @@ static inline void ath9k_hw_set_desc_link(struct ath_hw *ah, void *ds,
ath9k_hw_ops(ah)->set_desc_link(ds, link);
}
-static inline void ath9k_hw_get_desc_link(struct ath_hw *ah, void *ds,
- u32 **link)
-{
- ath9k_hw_ops(ah)->get_desc_link(ds, link);
-}
static inline bool ath9k_hw_calibrate(struct ath_hw *ah,
struct ath9k_channel *chan,
u8 rxchainmask,
@@ -57,13 +54,10 @@ static inline bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked)
return ath9k_hw_ops(ah)->get_isr(ah, masked);
}
-static inline void ath9k_hw_filltxdesc(struct ath_hw *ah, void *ds, u32 seglen,
- bool is_firstseg, bool is_lastseg,
- const void *ds0, dma_addr_t buf_addr,
- unsigned int qcu)
+static inline void ath9k_hw_set_txdesc(struct ath_hw *ah, void *ds,
+ struct ath_tx_info *i)
{
- ath9k_hw_ops(ah)->fill_txdesc(ah, ds, seglen, is_firstseg, is_lastseg,
- ds0, buf_addr, qcu);
+ return ath9k_hw_ops(ah)->set_txdesc(ah, ds, i);
}
static inline int ath9k_hw_txprocdesc(struct ath_hw *ah, void *ds,
@@ -72,55 +66,6 @@ static inline int ath9k_hw_txprocdesc(struct ath_hw *ah, void *ds,
return ath9k_hw_ops(ah)->proc_txdesc(ah, ds, ts);
}
-static inline void ath9k_hw_set11n_txdesc(struct ath_hw *ah, void *ds,
- u32 pktLen, enum ath9k_pkt_type type,
- u32 txPower, u32 keyIx,
- enum ath9k_key_type keyType,
- u32 flags)
-{
- ath9k_hw_ops(ah)->set11n_txdesc(ah, ds, pktLen, type, txPower, keyIx,
- keyType, flags);
-}
-
-static inline void ath9k_hw_set11n_ratescenario(struct ath_hw *ah, void *ds,
- void *lastds,
- u32 durUpdateEn, u32 rtsctsRate,
- u32 rtsctsDuration,
- struct ath9k_11n_rate_series series[],
- u32 nseries, u32 flags)
-{
- ath9k_hw_ops(ah)->set11n_ratescenario(ah, ds, lastds, durUpdateEn,
- rtsctsRate, rtsctsDuration, series,
- nseries, flags);
-}
-
-static inline void ath9k_hw_set11n_aggr_first(struct ath_hw *ah, void *ds,
- u32 aggrLen)
-{
- ath9k_hw_ops(ah)->set11n_aggr_first(ah, ds, aggrLen);
-}
-
-static inline void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, void *ds,
- u32 numDelims)
-{
- ath9k_hw_ops(ah)->set11n_aggr_middle(ah, ds, numDelims);
-}
-
-static inline void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, void *ds)
-{
- ath9k_hw_ops(ah)->set11n_aggr_last(ah, ds);
-}
-
-static inline void ath9k_hw_clr11n_aggr(struct ath_hw *ah, void *ds)
-{
- ath9k_hw_ops(ah)->clr11n_aggr(ah, ds);
-}
-
-static inline void ath9k_hw_set_clrdmask(struct ath_hw *ah, void *ds, bool val)
-{
- ath9k_hw_ops(ah)->set_clrdmask(ah, ds, val);
-}
-
static inline void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah,
struct ath_hw_antcomb_conf *antconf)
{
@@ -236,11 +181,6 @@ static inline void ath9k_hw_restore_chainmask(struct ath_hw *ah)
return ath9k_hw_private_ops(ah)->restore_chainmask(ah);
}
-static inline void ath9k_hw_set_diversity(struct ath_hw *ah, bool value)
-{
- return ath9k_hw_private_ops(ah)->set_diversity(ah, value);
-}
-
static inline bool ath9k_hw_ani_control(struct ath_hw *ah,
enum ath9k_ani_cmd cmd, int param)
{
@@ -265,4 +205,11 @@ static inline void ath9k_hw_setup_calibration(struct ath_hw *ah,
ath9k_hw_private_ops(ah)->setup_calibration(ah, currCal);
}
+static inline int ath9k_hw_fast_chan_change(struct ath_hw *ah,
+ struct ath9k_channel *chan,
+ u8 *ini_reloaded)
+{
+ return ath9k_hw_private_ops(ah)->fast_chan_change(ah, chan,
+ ini_reloaded);
+}
#endif /* ATH9K_HW_OPS_H */
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 1be7c8bbef84..8873c6e6fb96 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -16,6 +16,7 @@
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <asm/unaligned.h>
#include "hw.h"
@@ -87,7 +88,10 @@ static void ath9k_hw_set_clockrate(struct ath_hw *ah)
struct ath_common *common = ath9k_hw_common(ah);
unsigned int clockrate;
- if (!ah->curchan) /* should really check for CCK instead */
+ /* AR9287 v1.3+ uses async FIFO and runs the MAC at 117 MHz */
+ if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah))
+ clockrate = 117;
+ else if (!ah->curchan) /* should really check for CCK instead */
clockrate = ATH9K_CLOCK_RATE_CCK;
else if (conf->channel->band == IEEE80211_BAND_2GHZ)
clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
@@ -99,6 +103,13 @@ static void ath9k_hw_set_clockrate(struct ath_hw *ah)
if (conf_is_ht40(conf))
clockrate *= 2;
+ if (ah->curchan) {
+ if (IS_CHAN_HALF_RATE(ah->curchan))
+ clockrate /= 2;
+ if (IS_CHAN_QUARTER_RATE(ah->curchan))
+ clockrate /= 4;
+ }
+
common->clockrate = clockrate;
}
@@ -251,6 +262,15 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah)
case AR5416_AR9100_DEVID:
ah->hw_version.macVersion = AR_SREV_VERSION_9100;
break;
+ case AR9300_DEVID_AR9330:
+ ah->hw_version.macVersion = AR_SREV_VERSION_9330;
+ if (ah->get_mac_revision) {
+ ah->hw_version.macRev = ah->get_mac_revision();
+ } else {
+ val = REG_READ(ah, AR_SREV);
+ ah->hw_version.macRev = MS(val, AR_SREV_REVISION2);
+ }
+ return;
case AR9300_DEVID_AR9340:
ah->hw_version.macVersion = AR_SREV_VERSION_9340;
val = REG_READ(ah, AR_SREV);
@@ -265,7 +285,12 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah)
ah->hw_version.macVersion =
(val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S;
ah->hw_version.macRev = MS(val, AR_SREV_REVISION2);
- ah->is_pciexpress = (val & AR_SREV_TYPE2_HOST_MODE) ? 0 : 1;
+
+ if (AR_SREV_9462(ah))
+ ah->is_pciexpress = true;
+ else
+ ah->is_pciexpress = (val &
+ AR_SREV_TYPE2_HOST_MODE) ? 0 : 1;
} else {
if (!AR_SREV_9100(ah))
ah->hw_version.macVersion = MS(val, AR_SREV_VERSION);
@@ -299,6 +324,14 @@ static void ath9k_hw_disablepcie(struct ath_hw *ah)
REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
}
+static void ath9k_hw_aspm_init(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (common->bus_ops->aspm_init)
+ common->bus_ops->aspm_init(common);
+}
+
/* This should work for all families including legacy */
static bool ath9k_hw_chip_test(struct ath_hw *ah)
{
@@ -359,7 +392,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
ah->config.additional_swba_backoff = 0;
ah->config.ack_6mb = 0x0;
ah->config.cwm_ignore_extcca = 0;
- ah->config.pcie_powersave_enable = 0;
ah->config.pcie_clock_req = 0;
ah->config.pcie_waen = 0;
ah->config.analog_shiftreg = 1;
@@ -402,7 +434,6 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah)
regulatory->country_code = CTRY_DEFAULT;
regulatory->power_limit = MAX_RATE_POWER;
- regulatory->tp_scale = ATH9K_TP_SCALE_MAX;
ah->hw_version.magic = AR5416_MAGIC;
ah->hw_version.subvendorid = 0;
@@ -414,7 +445,7 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah)
if (AR_SREV_9100(ah))
ah->sta_id1_defaults |= AR_STA_ID1_AR9100_BA_FIX;
ah->enable_32kHz_clock = DONT_USE_32KHZ;
- ah->slottime = 20;
+ ah->slottime = ATH9K_SLOT_TIME_9;
ah->globaltxtimeout = (u32) -1;
ah->power_mode = ATH9K_PM_UNDEFINED;
}
@@ -511,6 +542,9 @@ static int __ath9k_hw_init(struct ath_hw *ah)
return -EIO;
}
+ if (AR_SREV_9462(ah))
+ ah->WARegVal &= ~AR_WA_D3_L1_DISABLE;
+
ath9k_hw_init_defaults(ah);
ath9k_hw_init_config(ah);
@@ -551,8 +585,10 @@ static int __ath9k_hw_init(struct ath_hw *ah)
case AR_SREV_VERSION_9287:
case AR_SREV_VERSION_9271:
case AR_SREV_VERSION_9300:
+ case AR_SREV_VERSION_9330:
case AR_SREV_VERSION_9485:
case AR_SREV_VERSION_9340:
+ case AR_SREV_VERSION_9462:
break;
default:
ath_err(common,
@@ -561,7 +597,8 @@ static int __ath9k_hw_init(struct ath_hw *ah)
return -EOPNOTSUPP;
}
- if (AR_SREV_9271(ah) || AR_SREV_9100(ah) || AR_SREV_9340(ah))
+ if (AR_SREV_9271(ah) || AR_SREV_9100(ah) || AR_SREV_9340(ah) ||
+ AR_SREV_9330(ah))
ah->is_pciexpress = false;
ah->hw_version.phyRev = REG_READ(ah, AR_PHY_CHIP_ID);
@@ -575,10 +612,7 @@ static int __ath9k_hw_init(struct ath_hw *ah)
ath9k_hw_init_mode_regs(ah);
-
- if (ah->is_pciexpress)
- ath9k_hw_configpcipowersave(ah, 0, 0);
- else
+ if (!ah->is_pciexpress)
ath9k_hw_disablepcie(ah);
if (!AR_SREV_9300_20_OR_LATER(ah))
@@ -593,6 +627,9 @@ static int __ath9k_hw_init(struct ath_hw *ah)
if (r)
return r;
+ if (ah->is_pciexpress)
+ ath9k_hw_aspm_init(ah);
+
r = ath9k_hw_init_macaddr(ah);
if (r) {
ath_err(common, "Failed to initialize MAC address\n");
@@ -604,7 +641,10 @@ static int __ath9k_hw_init(struct ath_hw *ah)
else
ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S);
- ah->bb_watchdog_timeout_ms = 25;
+ if (AR_SREV_9330(ah))
+ ah->bb_watchdog_timeout_ms = 85;
+ else
+ ah->bb_watchdog_timeout_ms = 25;
common->state = ATH_HW_INITIALIZED;
@@ -630,7 +670,10 @@ int ath9k_hw_init(struct ath_hw *ah)
case AR2427_DEVID_PCIE:
case AR9300_DEVID_PCIE:
case AR9300_DEVID_AR9485_PCIE:
+ case AR9300_DEVID_AR9330:
case AR9300_DEVID_AR9340:
+ case AR9300_DEVID_AR9580:
+ case AR9300_DEVID_AR9462:
break;
default:
if (common->bus_ops->ath_bus_type == ATH_USB)
@@ -722,6 +765,39 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2,
AR_CH0_BB_DPLL2_PLL_PWD, 0x0);
udelay(1000);
+ } else if (AR_SREV_9330(ah)) {
+ u32 ddr_dpll2, pll_control2, kd;
+
+ if (ah->is_clk_25mhz) {
+ ddr_dpll2 = 0x18e82f01;
+ pll_control2 = 0xe04a3d;
+ kd = 0x1d;
+ } else {
+ ddr_dpll2 = 0x19e82f01;
+ pll_control2 = 0x886666;
+ kd = 0x3d;
+ }
+
+ /* program DDR PLL ki and kd value */
+ REG_WRITE(ah, AR_CH0_DDR_DPLL2, ddr_dpll2);
+
+ /* program DDR PLL phase_shift */
+ REG_RMW_FIELD(ah, AR_CH0_DDR_DPLL3,
+ AR_CH0_DPLL3_PHASE_SHIFT, 0x1);
+
+ REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c);
+ udelay(1000);
+
+ /* program refdiv, nint, frac to RTC register */
+ REG_WRITE(ah, AR_RTC_PLL_CONTROL2, pll_control2);
+
+ /* program BB PLL kd and ki value */
+ REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_DPLL2_KD, kd);
+ REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_DPLL2_KI, 0x06);
+
+ /* program BB PLL phase_shift */
+ REG_RMW_FIELD(ah, AR_CH0_BB_DPLL3,
+ AR_CH0_BB_DPLL3_PHASE_SHIFT, 0x1);
} else if (AR_SREV_9340(ah)) {
u32 regval, pll2_divint, pll2_divfrac, refdiv;
@@ -763,7 +839,7 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll);
- if (AR_SREV_9485(ah) || AR_SREV_9340(ah))
+ if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah))
udelay(1000);
/* Switch the core clock for ar9271 to 117Mhz */
@@ -847,6 +923,13 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
}
}
+static void ath9k_hw_set_sifs_time(struct ath_hw *ah, u32 us)
+{
+ u32 val = ath9k_hw_mac_to_clks(ah, us - 2);
+ val = min(val, (u32) 0xFFFF);
+ REG_WRITE(ah, AR_D_GBL_IFS_SIFS, val);
+}
+
static void ath9k_hw_setslottime(struct ath_hw *ah, u32 us)
{
u32 val = ath9k_hw_mac_to_clks(ah, us);
@@ -884,25 +967,70 @@ static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu)
void ath9k_hw_init_global_settings(struct ath_hw *ah)
{
- struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
- int acktimeout;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ieee80211_conf *conf = &common->hw->conf;
+ const struct ath9k_channel *chan = ah->curchan;
+ int acktimeout, ctstimeout;
int slottime;
int sifstime;
+ int rx_lat = 0, tx_lat = 0, eifs = 0;
+ u32 reg;
ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET, "ah->misc_mode 0x%x\n",
ah->misc_mode);
+ if (!chan)
+ return;
+
if (ah->misc_mode != 0)
REG_SET_BIT(ah, AR_PCU_MISC, ah->misc_mode);
- if (conf->channel && conf->channel->band == IEEE80211_BAND_5GHZ)
- sifstime = 16;
+ if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+ rx_lat = 41;
else
- sifstime = 10;
+ rx_lat = 37;
+ tx_lat = 54;
+
+ if (IS_CHAN_HALF_RATE(chan)) {
+ eifs = 175;
+ rx_lat *= 2;
+ tx_lat *= 2;
+ if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+ tx_lat += 11;
+
+ slottime = 13;
+ sifstime = 32;
+ } else if (IS_CHAN_QUARTER_RATE(chan)) {
+ eifs = 340;
+ rx_lat = (rx_lat * 4) - 1;
+ tx_lat *= 4;
+ if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+ tx_lat += 22;
+
+ slottime = 21;
+ sifstime = 64;
+ } else {
+ if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) {
+ eifs = AR_D_GBL_IFS_EIFS_ASYNC_FIFO;
+ reg = AR_USEC_ASYNC_FIFO;
+ } else {
+ eifs = REG_READ(ah, AR_D_GBL_IFS_EIFS)/
+ common->clockrate;
+ reg = REG_READ(ah, AR_USEC);
+ }
+ rx_lat = MS(reg, AR_USEC_RX_LAT);
+ tx_lat = MS(reg, AR_USEC_TX_LAT);
+
+ slottime = ah->slottime;
+ if (IS_CHAN_5GHZ(chan))
+ sifstime = 16;
+ else
+ sifstime = 10;
+ }
/* As defined by IEEE 802.11-2007 17.3.8.6 */
- slottime = ah->slottime + 3 * ah->coverage_class;
- acktimeout = slottime + sifstime;
+ acktimeout = slottime + sifstime + 3 * ah->coverage_class;
+ ctstimeout = acktimeout;
/*
* Workaround for early ACK timeouts, add an offset to match the
@@ -914,11 +1042,20 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ)
acktimeout += 64 - sifstime - ah->slottime;
- ath9k_hw_setslottime(ah, ah->slottime);
+ ath9k_hw_set_sifs_time(ah, sifstime);
+ ath9k_hw_setslottime(ah, slottime);
ath9k_hw_set_ack_timeout(ah, acktimeout);
- ath9k_hw_set_cts_timeout(ah, acktimeout);
+ ath9k_hw_set_cts_timeout(ah, ctstimeout);
if (ah->globaltxtimeout != (u32) -1)
ath9k_hw_set_global_txtimeout(ah, ah->globaltxtimeout);
+
+ REG_WRITE(ah, AR_D_GBL_IFS_EIFS, ath9k_hw_mac_to_clks(ah, eifs));
+ REG_RMW(ah, AR_USEC,
+ (common->clockrate - 1) |
+ SM(rx_lat, AR_USEC_RX_LAT) |
+ SM(tx_lat, AR_USEC_TX_LAT),
+ AR_USEC_TX_LAT | AR_USEC_RX_LAT | AR_USEC_USEC);
+
}
EXPORT_SYMBOL(ath9k_hw_init_global_settings);
@@ -1114,6 +1251,41 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
rst_flags |= AR_RTC_RC_MAC_COLD;
}
+ if (AR_SREV_9330(ah)) {
+ int npend = 0;
+ int i;
+
+ /* AR9330 WAR:
+ * call external reset function to reset WMAC if:
+ * - doing a cold reset
+ * - we have pending frames in the TX queues
+ */
+
+ for (i = 0; i < AR_NUM_QCU; i++) {
+ npend = ath9k_hw_numtxpending(ah, i);
+ if (npend)
+ break;
+ }
+
+ if (ah->external_reset &&
+ (npend || type == ATH9K_RESET_COLD)) {
+ int reset_err = 0;
+
+ ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET,
+ "reset MAC via external reset\n");
+
+ reset_err = ah->external_reset();
+ if (reset_err) {
+ ath_err(ath9k_hw_common(ah),
+ "External reset failed, err=%d\n",
+ reset_err);
+ return false;
+ }
+
+ REG_WRITE(ah, AR_RTC_RESET, 1);
+ }
+ }
+
REG_WRITE(ah, AR_RTC_RC, rst_flags);
REGWRITE_BUFFER_FLUSH(ah);
@@ -1178,6 +1350,7 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah)
static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type)
{
+
if (AR_SREV_9300_20_OR_LATER(ah)) {
REG_WRITE(ah, AR_WA, ah->WARegVal);
udelay(10);
@@ -1219,11 +1392,17 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah,
static bool ath9k_hw_channel_change(struct ath_hw *ah,
struct ath9k_channel *chan)
{
- struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_channel *channel = chan->chan;
u32 qnum;
int r;
+ bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
+ bool band_switch, mode_diff;
+ u8 ini_reloaded;
+
+ band_switch = (chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)) !=
+ (ah->curchan->channelFlags & (CHANNEL_2GHZ |
+ CHANNEL_5GHZ));
+ mode_diff = (chan->chanmode != ah->curchan->chanmode);
for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
if (ath9k_hw_numtxpending(ah, qnum)) {
@@ -1238,6 +1417,18 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
return false;
}
+ if (edma && (band_switch || mode_diff)) {
+ ath9k_hw_mark_phy_inactive(ah);
+ udelay(5);
+
+ ath9k_hw_init_pll(ah, NULL);
+
+ if (ath9k_hw_fast_chan_change(ah, chan, &ini_reloaded)) {
+ ath_err(common, "Failed to do fast channel change\n");
+ return false;
+ }
+ }
+
ath9k_hw_set_channel_regs(ah, chan);
r = ath9k_hw_rf_set_freq(ah, chan);
@@ -1246,14 +1437,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
return false;
}
ath9k_hw_set_clockrate(ah);
-
- ah->eep_ops->set_txpower(ah, chan,
- ath9k_regd_get_ctl(regulatory, chan),
- channel->max_antenna_gain * 2,
- channel->max_power * 2,
- min((u32) MAX_RATE_POWER,
- (u32) regulatory->power_limit), false);
-
+ ath9k_hw_apply_txpower(ah, chan);
ath9k_hw_rfbus_done(ah);
if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
@@ -1261,6 +1445,18 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
ath9k_hw_spur_mitigate_freq(ah, chan);
+ if (edma && (band_switch || mode_diff)) {
+ ah->ah_flags |= AH_FASTCC;
+ if (band_switch || ini_reloaded)
+ ah->eep_ops->set_board_values(ah, chan);
+
+ ath9k_hw_init_bb(ah, chan);
+
+ if (band_switch || ini_reloaded)
+ ath9k_hw_init_cal(ah, chan);
+ ah->ah_flags &= ~AH_FASTCC;
+ }
+
return true;
}
@@ -1316,9 +1512,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
u32 macStaId1;
u64 tsf = 0;
int i, r;
-
- ah->txchainmask = common->tx_chainmask;
- ah->rxchainmask = common->rx_chainmask;
+ bool allow_fbs = false;
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
return -EIO;
@@ -1335,15 +1529,24 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
memset(caldata, 0, sizeof(*caldata));
ath9k_init_nfcal_hist_buffer(ah, chan);
}
+ ah->noise = ath9k_hw_getchan_noise(ah, chan);
+
+ if (AR_SREV_9280(ah) && common->bus_ops->ath_bus_type == ATH_PCI)
+ bChannelChange = false;
+
+ if (caldata &&
+ caldata->done_txiqcal_once &&
+ caldata->done_txclcal_once &&
+ caldata->rtt_hist.num_readings)
+ allow_fbs = true;
if (bChannelChange &&
(ah->chip_fullsleep != true) &&
(ah->curchan != NULL) &&
(chan->channel != ah->curchan->channel) &&
- ((chan->channelFlags & CHANNEL_ALL) ==
- (ah->curchan->channelFlags & CHANNEL_ALL)) &&
- (!AR_SREV_9280(ah) || AR_DEVID_7010(ah))) {
-
+ (allow_fbs ||
+ ((chan->channelFlags & CHANNEL_ALL) ==
+ (ah->curchan->channelFlags & CHANNEL_ALL)))) {
if (ath9k_hw_channel_change(ah, chan)) {
ath9k_hw_loadnf(ah, ah->curchan);
ath9k_hw_start_nfcal(ah, true);
@@ -1487,9 +1690,13 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
ath9k_hw_init_global_settings(ah);
- if (!AR_SREV_9300_20_OR_LATER(ah)) {
- ar9002_hw_update_async_fifo(ah);
- ar9002_hw_enable_wep_aggregation(ah);
+ if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) {
+ REG_SET_BIT(ah, AR_MAC_PCU_LOGIC_ANALYZER,
+ AR_MAC_PCU_LOGIC_ANALYZER_DISBUG20768);
+ REG_RMW_FIELD(ah, AR_AHB_MODE, AR_AHB_CUSTOM_BURST_EN,
+ AR_AHB_CUSTOM_BURST_ASYNC_FIFO_VAL);
+ REG_SET_BIT(ah, AR_PCU_MISC_MODE2,
+ AR_PCU_MISC_MODE2_ENABLE_AGGWEP);
}
REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);
@@ -1510,9 +1717,17 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
ath9k_hw_init_bb(ah, chan);
+ if (caldata) {
+ caldata->done_txiqcal_once = false;
+ caldata->done_txclcal_once = false;
+ caldata->rtt_hist.num_readings = 0;
+ }
if (!ath9k_hw_init_cal(ah, chan))
return -EIO;
+ ath9k_hw_loadnf(ah, chan);
+ ath9k_hw_start_nfcal(ah, true);
+
ENABLE_REGWRITE_BUFFER(ah);
ath9k_hw_restore_chainmask(ah);
@@ -1545,7 +1760,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD);
}
#ifdef __BIG_ENDIAN
- else if (AR_SREV_9340(ah))
+ else if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
REG_RMW(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB, 0);
else
REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD);
@@ -1579,25 +1794,41 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
{
REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
if (setChip) {
+ if (AR_SREV_9462(ah)) {
+ REG_WRITE(ah, AR_TIMER_MODE,
+ REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00);
+ REG_WRITE(ah, AR_NDP2_TIMER_MODE, REG_READ(ah,
+ AR_NDP2_TIMER_MODE) & 0xFFFFFF00);
+ REG_WRITE(ah, AR_SLP32_INC,
+ REG_READ(ah, AR_SLP32_INC) & 0xFFF00000);
+ /* xxx Required for WLAN only case ? */
+ REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0);
+ udelay(100);
+ }
+
/*
* Clear the RTC force wake bit to allow the
* mac to go to sleep.
*/
- REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE,
- AR_RTC_FORCE_WAKE_EN);
+ REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
+
+ if (AR_SREV_9462(ah))
+ udelay(100);
+
if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah))
REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
/* Shutdown chip. Active low */
- if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah))
- REG_CLR_BIT(ah, (AR_RTC_RESET),
- AR_RTC_RESET_EN);
+ if (!AR_SREV_5416(ah) &&
+ !AR_SREV_9271(ah) && !AR_SREV_9462_10(ah)) {
+ REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
+ udelay(2);
+ }
}
/* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */
if (AR_SREV_9300_20_OR_LATER(ah))
- REG_WRITE(ah, AR_WA,
- ah->WARegVal & ~AR_WA_D3_L1_DISABLE);
+ REG_WRITE(ah, AR_WA, ah->WARegVal & ~AR_WA_D3_L1_DISABLE);
}
/*
@@ -1607,6 +1838,8 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
*/
static void ath9k_set_power_network_sleep(struct ath_hw *ah, int setChip)
{
+ u32 val;
+
REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
if (setChip) {
struct ath9k_hw_capabilities *pCap = &ah->caps;
@@ -1616,12 +1849,30 @@ static void ath9k_set_power_network_sleep(struct ath_hw *ah, int setChip)
REG_WRITE(ah, AR_RTC_FORCE_WAKE,
AR_RTC_FORCE_WAKE_ON_INT);
} else {
+
+ /* When chip goes into network sleep, it could be waken
+ * up by MCI_INT interrupt caused by BT's HW messages
+ * (LNA_xxx, CONT_xxx) which chould be in a very fast
+ * rate (~100us). This will cause chip to leave and
+ * re-enter network sleep mode frequently, which in
+ * consequence will have WLAN MCI HW to generate lots of
+ * SYS_WAKING and SYS_SLEEPING messages which will make
+ * BT CPU to busy to process.
+ */
+ if (AR_SREV_9462(ah)) {
+ val = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) &
+ ~AR_MCI_INTERRUPT_RX_HW_MSG_MASK;
+ REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, val);
+ }
/*
* Clear the RTC force wake bit to allow the
* mac to go to sleep.
*/
REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE,
AR_RTC_FORCE_WAKE_EN);
+
+ if (AR_SREV_9462(ah))
+ udelay(30);
}
}
@@ -1785,16 +2036,16 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt));
REG_WRITE(ah, AR_BEACON_PERIOD,
- TU_TO_USEC(bs->bs_intval & ATH9K_BEACON_PERIOD));
+ TU_TO_USEC(bs->bs_intval));
REG_WRITE(ah, AR_DMA_BEACON_PERIOD,
- TU_TO_USEC(bs->bs_intval & ATH9K_BEACON_PERIOD));
+ TU_TO_USEC(bs->bs_intval));
REGWRITE_BUFFER_FLUSH(ah);
REG_RMW_FIELD(ah, AR_RSSI_THR,
AR_RSSI_THR_BM_THR, bs->bs_bmissthreshold);
- beaconintval = bs->bs_intval & ATH9K_BEACON_PERIOD;
+ beaconintval = bs->bs_intval;
if (bs->bs_sleepduration > beaconintval)
beaconintval = bs->bs_sleepduration;
@@ -1849,12 +2100,22 @@ EXPORT_SYMBOL(ath9k_hw_set_sta_beacon_timers);
/* HW Capabilities */
/*******************/
+static u8 fixup_chainmask(u8 chip_chainmask, u8 eeprom_chainmask)
+{
+ eeprom_chainmask &= chip_chainmask;
+ if (eeprom_chainmask)
+ return eeprom_chainmask;
+ else
+ return chip_chainmask;
+}
+
int ath9k_hw_fill_cap_info(struct ath_hw *ah)
{
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ath_common *common = ath9k_hw_common(ah);
struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
+ unsigned int chip_chainmask;
u16 eeval;
u8 ant_div_ctl1, tx_chainmask, rx_chainmask;
@@ -1862,11 +2123,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_0);
regulatory->current_rd = eeval;
- eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_1);
- if (AR_SREV_9285_12_OR_LATER(ah))
- eeval |= AR9285_RDEXT_DEFAULT;
- regulatory->current_rd_ext = eeval;
-
if (ah->opmode != NL80211_IFTYPE_AP &&
ah->hw_version.subvendorid == AR_SUBVENDOR_ID_NEW_A) {
if (regulatory->current_rd == 0x64 ||
@@ -1891,6 +2147,15 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
if (eeval & AR5416_OPFLAGS_11G)
pCap->hw_caps |= ATH9K_HW_CAP_2GHZ;
+ if (AR_SREV_9485(ah) || AR_SREV_9285(ah) || AR_SREV_9330(ah))
+ chip_chainmask = 1;
+ else if (!AR_SREV_9280_20_OR_LATER(ah))
+ chip_chainmask = 7;
+ else if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9340(ah))
+ chip_chainmask = 3;
+ else
+ chip_chainmask = 7;
+
pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK);
/*
* For AR9271 we will temporarilly uses the rx chainmax as read from
@@ -1907,6 +2172,11 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
/* Use rx_chainmask from EEPROM. */
pCap->rx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_RX_MASK);
+ pCap->tx_chainmask = fixup_chainmask(chip_chainmask, pCap->tx_chainmask);
+ pCap->rx_chainmask = fixup_chainmask(chip_chainmask, pCap->rx_chainmask);
+ ah->txchainmask = pCap->tx_chainmask;
+ ah->rxchainmask = pCap->rx_chainmask;
+
ah->misc_mode |= AR_PCU_MIC_NEW_LOC_ENA;
/* enable key search for every frame in an aggregate */
@@ -1924,6 +2194,10 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
pCap->num_gpio_pins = AR9271_NUM_GPIO;
else if (AR_DEVID_7010(ah))
pCap->num_gpio_pins = AR7010_NUM_GPIO;
+ else if (AR_SREV_9300_20_OR_LATER(ah))
+ pCap->num_gpio_pins = AR9300_NUM_GPIO;
+ else if (AR_SREV_9287_11_OR_LATER(ah))
+ pCap->num_gpio_pins = AR9287_NUM_GPIO;
else if (AR_SREV_9285_12_OR_LATER(ah))
pCap->num_gpio_pins = AR9285_NUM_GPIO;
else if (AR_SREV_9280_20_OR_LATER(ah))
@@ -1983,7 +2257,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
if (AR_SREV_9300_20_OR_LATER(ah)) {
pCap->hw_caps |= ATH9K_HW_CAP_EDMA | ATH9K_HW_CAP_FASTCLOCK;
- if (!AR_SREV_9485(ah))
+ if (!AR_SREV_9330(ah) && !AR_SREV_9485(ah))
pCap->hw_caps |= ATH9K_HW_CAP_LDPC;
pCap->rx_hp_qdepth = ATH9K_HW_RX_HP_QDEPTH;
@@ -1996,10 +2270,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
} else {
pCap->tx_desc_len = sizeof(struct ath_desc);
- if (AR_SREV_9280_20(ah) &&
- ((ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV) <=
- AR5416_EEP_MINOR_VER_16) ||
- ah->eep_ops->get_eeprom(ah, EEP_FSTCLK_5G)))
+ if (AR_SREV_9280_20(ah))
pCap->hw_caps |= ATH9K_HW_CAP_FASTCLOCK;
}
@@ -2025,7 +2296,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
}
- if (AR_SREV_9485(ah)) {
+ if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) {
ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
/*
* enable the diversity-combining algorithm only when
@@ -2059,6 +2330,14 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
rx_chainmask >>= 1;
}
+ if (AR_SREV_9300_20_OR_LATER(ah)) {
+ ah->enabled_cals |= TX_IQ_CAL;
+ if (!AR_SREV_9330(ah))
+ ah->enabled_cals |= TX_IQ_ON_AGC_CAL;
+ }
+ if (AR_SREV_9462(ah))
+ pCap->hw_caps |= ATH9K_HW_CAP_RTT;
+
return 0;
}
@@ -2219,6 +2498,9 @@ void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits)
ENABLE_REGWRITE_BUFFER(ah);
+ if (AR_SREV_9462(ah))
+ bits |= ATH9K_RX_FILTER_CONTROL_WRAPPER;
+
REG_WRITE(ah, AR_RX_FILTER, bits);
phybits = 0;
@@ -2260,20 +2542,56 @@ bool ath9k_hw_disable(struct ath_hw *ah)
}
EXPORT_SYMBOL(ath9k_hw_disable);
+static int get_antenna_gain(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+ enum eeprom_param gain_param;
+
+ if (IS_CHAN_2GHZ(chan))
+ gain_param = EEP_ANTENNA_GAIN_2G;
+ else
+ gain_param = EEP_ANTENNA_GAIN_5G;
+
+ return ah->eep_ops->get_eeprom(ah, gain_param);
+}
+
+void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+ struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
+ struct ieee80211_channel *channel;
+ int chan_pwr, new_pwr, max_gain;
+ int ant_gain, ant_reduction = 0;
+
+ if (!chan)
+ return;
+
+ channel = chan->chan;
+ chan_pwr = min_t(int, channel->max_power * 2, MAX_RATE_POWER);
+ new_pwr = min_t(int, chan_pwr, reg->power_limit);
+ max_gain = chan_pwr - new_pwr + channel->max_antenna_gain * 2;
+
+ ant_gain = get_antenna_gain(ah, chan);
+ if (ant_gain > max_gain)
+ ant_reduction = ant_gain - max_gain;
+
+ ah->eep_ops->set_txpower(ah, chan,
+ ath9k_regd_get_ctl(reg, chan),
+ ant_reduction, new_pwr, false);
+}
+
void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test)
{
- struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
+ struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
struct ath9k_channel *chan = ah->curchan;
struct ieee80211_channel *channel = chan->chan;
- regulatory->power_limit = min(limit, (u32) MAX_RATE_POWER);
+ reg->power_limit = min_t(int, limit, MAX_RATE_POWER);
+ if (test)
+ channel->max_power = MAX_RATE_POWER / 2;
- ah->eep_ops->set_txpower(ah, chan,
- ath9k_regd_get_ctl(regulatory, chan),
- channel->max_antenna_gain * 2,
- channel->max_power * 2,
- min((u32) MAX_RATE_POWER,
- (u32) regulatory->power_limit), test);
+ ath9k_hw_apply_txpower(ah, chan);
+
+ if (test)
+ channel->max_power = DIV_ROUND_UP(reg->max_power_level, 2);
}
EXPORT_SYMBOL(ath9k_hw_set_txpowerlimit);
@@ -2472,6 +2790,20 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah,
REG_SET_BIT(ah, gen_tmr_configuration[timer->index].mode_addr,
gen_tmr_configuration[timer->index].mode_mask);
+ if (AR_SREV_9462(ah)) {
+ /*
+ * Starting from AR9462, each generic timer can select which tsf
+ * to use. But we still follow the old rule, 0 - 7 use tsf and
+ * 8 - 15 use tsf2.
+ */
+ if ((timer->index < AR_GEN_TIMER_BANK_1_LEN))
+ REG_CLR_BIT(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL,
+ (1 << timer->index));
+ else
+ REG_SET_BIT(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL,
+ (1 << timer->index));
+ }
+
/* Enable both trigger and thresh interrupt masks */
REG_SET_BIT(ah, AR_IMR_S5,
(SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) |
@@ -2574,7 +2906,10 @@ static struct {
{ AR_SREV_VERSION_9287, "9287" },
{ AR_SREV_VERSION_9271, "9271" },
{ AR_SREV_VERSION_9300, "9300" },
+ { AR_SREV_VERSION_9330, "9330" },
+ { AR_SREV_VERSION_9340, "9340" },
{ AR_SREV_VERSION_9485, "9485" },
+ { AR_SREV_VERSION_9462, "9462" },
};
/* For devices with external radios */
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 4b157c53d1a8..f389b3c93cf3 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -45,6 +45,9 @@
#define AR9300_DEVID_PCIE 0x0030
#define AR9300_DEVID_AR9340 0x0031
#define AR9300_DEVID_AR9485_PCIE 0x0032
+#define AR9300_DEVID_AR9580 0x0033
+#define AR9300_DEVID_AR9462 0x0034
+#define AR9300_DEVID_AR9330 0x0035
#define AR5416_AR9100_DEVID 0x000b
@@ -92,6 +95,12 @@
(_ah)->reg_ops.write_flush((_ah)); \
} while (0)
+#define PR_EEP(_s, _val) \
+ do { \
+ len += snprintf(buf + len, size - len, "%20s : %10d\n", \
+ _s, (_val)); \
+ } while (0)
+
#define SM(_v, _f) (((_v) << _f##_S) & _f)
#define MS(_v, _f) (((_v) & _f) >> _f##_S)
#define REG_RMW_FIELD(_a, _r, _f, _v) \
@@ -142,6 +151,8 @@
#define AR_KEYTABLE_SIZE 128
#define POWER_UP_TIME 10000
#define SPUR_RSSI_THRESH 40
+#define UPPER_5G_SUB_BAND_START 5700
+#define MID_5G_SUB_BAND_START 5400
#define CAB_TIMEOUT_VAL 10
#define BEACON_TIMEOUT_VAL 10
@@ -157,8 +168,9 @@
#define ATH9K_HW_RX_HP_QDEPTH 16
#define ATH9K_HW_RX_LP_QDEPTH 128
-#define PAPRD_GAIN_TABLE_ENTRIES 32
-#define PAPRD_TABLE_SZ 24
+#define PAPRD_GAIN_TABLE_ENTRIES 32
+#define PAPRD_TABLE_SZ 24
+#define PAPRD_IDEAL_AGC2_PWR_RANGE 0xe0
enum ath_hw_txq_subtype {
ATH_TXQ_AC_BE = 0,
@@ -190,6 +202,7 @@ enum ath9k_hw_caps {
ATH9K_HW_CAP_2GHZ = BIT(13),
ATH9K_HW_CAP_5GHZ = BIT(14),
ATH9K_HW_CAP_APM = BIT(15),
+ ATH9K_HW_CAP_RTT = BIT(16),
};
struct ath9k_hw_capabilities {
@@ -215,7 +228,6 @@ struct ath9k_ops_config {
int additional_swba_backoff;
int ack_6mb;
u32 cwm_ignore_extcca;
- u8 pcie_powersave_enable;
bool pcieSerDesWrite;
u8 pcie_clock_req;
u32 pcie_waen;
@@ -326,6 +338,16 @@ enum ath9k_int {
CHANNEL_HT40PLUS | \
CHANNEL_HT40MINUS)
+#define MAX_RTT_TABLE_ENTRY 6
+#define RTT_HIST_MAX 3
+struct ath9k_rtt_hist {
+ u32 table[AR9300_MAX_CHAINS][RTT_HIST_MAX][MAX_RTT_TABLE_ENTRY];
+ u8 num_readings;
+};
+
+#define MAX_IQCAL_MEASUREMENT 8
+#define MAX_CL_TAB_ENTRY 16
+
struct ath9k_hw_cal_data {
u16 channel;
u32 channelFlags;
@@ -335,9 +357,15 @@ struct ath9k_hw_cal_data {
bool paprd_done;
bool nfcal_pending;
bool nfcal_interference;
+ bool done_txiqcal_once;
+ bool done_txclcal_once;
u16 small_signal_gain[AR9300_MAX_CHAINS];
u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ];
+ u32 num_measures[AR9300_MAX_CHAINS];
+ int tx_corr_coeff[MAX_IQCAL_MEASUREMENT][AR9300_MAX_CHAINS];
+ u32 tx_clcal[AR9300_MAX_CHAINS][MAX_CL_TAB_ENTRY];
struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
+ struct ath9k_rtt_hist rtt_hist;
};
struct ath9k_channel {
@@ -379,14 +407,6 @@ enum ath9k_power_mode {
ATH9K_PM_UNDEFINED
};
-enum ath9k_tp_scale {
- ATH9K_TP_SCALE_MAX = 0,
- ATH9K_TP_SCALE_50,
- ATH9K_TP_SCALE_25,
- ATH9K_TP_SCALE_12,
- ATH9K_TP_SCALE_MIN
-};
-
enum ser_reg_mode {
SER_REG_MODE_OFF = 0,
SER_REG_MODE_ON = 1,
@@ -403,7 +423,6 @@ struct ath9k_beacon_state {
u32 bs_nexttbtt;
u32 bs_nextdtim;
u32 bs_intval;
-#define ATH9K_BEACON_PERIOD 0x0000ffff
#define ATH9K_TSFOOR_THRESHOLD 0x00004240 /* 16k us */
u32 bs_dtimperiod;
u16 bs_cfpperiod;
@@ -436,7 +455,6 @@ struct ath9k_hw_version {
u16 phyRev;
u16 analog5GhzRev;
u16 analog2GhzRev;
- u16 subsysid;
enum ath_usb_dev usbdev;
};
@@ -575,7 +593,6 @@ struct ath_hw_private_ops {
bool (*rfbus_req)(struct ath_hw *ah);
void (*rfbus_done)(struct ath_hw *ah);
void (*restore_chainmask)(struct ath_hw *ah);
- void (*set_diversity)(struct ath_hw *ah, bool value);
u32 (*compute_pll_control)(struct ath_hw *ah,
struct ath9k_channel *chan);
bool (*ani_control)(struct ath_hw *ah, enum ath9k_ani_cmd cmd,
@@ -583,6 +600,8 @@ struct ath_hw_private_ops {
void (*do_getnf)(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]);
void (*set_radar_params)(struct ath_hw *ah,
struct ath_hw_radar_conf *conf);
+ int (*fast_chan_change)(struct ath_hw *ah, struct ath9k_channel *chan,
+ u8 *ini_reloaded);
/* ANI */
void (*ani_cache_ini_regs)(struct ath_hw *ah);
@@ -599,40 +618,18 @@ struct ath_hw_private_ops {
*/
struct ath_hw_ops {
void (*config_pci_powersave)(struct ath_hw *ah,
- int restore,
- int power_off);
+ bool power_off);
void (*rx_enable)(struct ath_hw *ah);
void (*set_desc_link)(void *ds, u32 link);
- void (*get_desc_link)(void *ds, u32 **link);
bool (*calibrate)(struct ath_hw *ah,
struct ath9k_channel *chan,
u8 rxchainmask,
bool longcal);
bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked);
- void (*fill_txdesc)(struct ath_hw *ah, void *ds, u32 seglen,
- bool is_firstseg, bool is_is_lastseg,
- const void *ds0, dma_addr_t buf_addr,
- unsigned int qcu);
+ void (*set_txdesc)(struct ath_hw *ah, void *ds,
+ struct ath_tx_info *i);
int (*proc_txdesc)(struct ath_hw *ah, void *ds,
struct ath_tx_status *ts);
- void (*set11n_txdesc)(struct ath_hw *ah, void *ds,
- u32 pktLen, enum ath9k_pkt_type type,
- u32 txPower, u32 keyIx,
- enum ath9k_key_type keyType,
- u32 flags);
- void (*set11n_ratescenario)(struct ath_hw *ah, void *ds,
- void *lastds,
- u32 durUpdateEn, u32 rtsctsRate,
- u32 rtsctsDuration,
- struct ath9k_11n_rate_series series[],
- u32 nseries, u32 flags);
- void (*set11n_aggr_first)(struct ath_hw *ah, void *ds,
- u32 aggrLen);
- void (*set11n_aggr_middle)(struct ath_hw *ah, void *ds,
- u32 numDelims);
- void (*set11n_aggr_last)(struct ath_hw *ah, void *ds);
- void (*clr11n_aggr)(struct ath_hw *ah, void *ds);
- void (*set_clrdmask)(struct ath_hw *ah, void *ds, bool val);
void (*antdiv_comb_conf_get)(struct ath_hw *ah,
struct ath_hw_antcomb_conf *antconf);
void (*antdiv_comb_conf_set)(struct ath_hw *ah,
@@ -646,9 +643,16 @@ struct ath_nf_limits {
s16 nominal;
};
+enum ath_cal_list {
+ TX_IQ_CAL = BIT(0),
+ TX_IQ_ON_AGC_CAL = BIT(1),
+ TX_CL_CAL = BIT(2),
+};
+
/* ah_flags */
#define AH_USE_EEPROM 0x1
#define AH_UNPLUGGED 0x2 /* The card has been physically removed. */
+#define AH_FASTCC 0x4
struct ath_hw {
struct ath_ops reg_ops;
@@ -671,6 +675,7 @@ struct ath_hw {
bool sw_mgmt_crypto;
bool is_pciexpress;
+ bool aspm_enabled;
bool is_monitoring;
bool need_an_top2_fixup;
u16 tx_trig_level;
@@ -688,6 +693,7 @@ struct ath_hw {
enum nl80211_iftype opmode;
enum ath9k_power_mode power_mode;
+ s8 noise;
struct ath9k_hw_cal_data *caldata;
struct ath9k_pacal_info pacal_info;
struct ar5416Stats stats;
@@ -701,8 +707,10 @@ struct ath_hw {
u32 txdesc_interrupt_mask;
u32 txeol_interrupt_mask;
u32 txurn_interrupt_mask;
+ atomic_t intr_ref_cnt;
bool chip_fullsleep;
u32 atim_window;
+ u32 modes_index;
/* Calibration */
u32 supp_cals;
@@ -741,6 +749,7 @@ struct ath_hw {
int32_t sign[AR5416_MAX_CHAINS];
} meas3;
u16 cal_samples;
+ u8 enabled_cals;
u32 sta_id1_defaults;
u32 misc_mode;
@@ -818,11 +827,14 @@ struct ath_hw {
struct ar5416IniArray iniModes_9271_1_0_only;
struct ar5416IniArray iniCckfirNormal;
struct ar5416IniArray iniCckfirJapan2484;
+ struct ar5416IniArray ini_japan2484;
struct ar5416IniArray iniCommon_normal_cck_fir_coeff_9271;
struct ar5416IniArray iniCommon_japan_2484_cck_fir_coeff_9271;
struct ar5416IniArray iniModes_9271_ANI_reg;
struct ar5416IniArray iniModes_high_power_tx_gain_9271;
struct ar5416IniArray iniModes_normal_power_tx_gain_9271;
+ struct ar5416IniArray ini_radio_post_sys2ant;
+ struct ar5416IniArray ini_BTCOEX_MAX_TXPWR;
struct ar5416IniArray iniMac[ATH_INI_NUM_SPLIT];
struct ar5416IniArray iniBB[ATH_INI_NUM_SPLIT];
@@ -862,6 +874,8 @@ struct ath_hw {
u32 ent_mode;
bool is_clk_25mhz;
+ int (*get_mac_revision)(void);
+ int (*external_reset)(void);
};
struct ath_bus_ops {
@@ -870,6 +884,7 @@ struct ath_bus_ops {
bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data);
void (*bt_coex_prep)(struct ath_common *common);
void (*extn_synch_en)(struct ath_common *common);
+ void (*aspm_init)(struct ath_common *common);
};
static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah)
@@ -973,6 +988,7 @@ void ath9k_hw_htc_resetinit(struct ath_hw *ah);
/* PHY */
void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
u32 *coef_mantissa, u32 *coef_exponent);
+void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan);
/*
* Code Specific to AR5008, AR9001 or AR9002,
@@ -981,8 +997,6 @@ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
void ar9002_hw_cck_chan14_spread(struct ath_hw *ah);
int ar9002_hw_rf_claim(struct ath_hw *ah);
void ar9002_hw_enable_async_fifo(struct ath_hw *ah);
-void ar9002_hw_update_async_fifo(struct ath_hw *ah);
-void ar9002_hw_enable_wep_aggregation(struct ath_hw *ah);
/*
* Code specific to AR9003, we stuff these here to avoid callbacks
@@ -1027,10 +1041,6 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning);
void ath9k_hw_proc_mib_event(struct ath_hw *ah);
void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan);
-#define ATH_PCIE_CAP_LINK_CTRL 0x70
-#define ATH_PCIE_CAP_LINK_L0S 1
-#define ATH_PCIE_CAP_LINK_L1 2
-
#define ATH9K_CLOCK_RATE_CCK 22
#define ATH9K_CLOCK_RATE_5GHZ_OFDM 40
#define ATH9K_CLOCK_RATE_2GHZ_OFDM 44
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 45c585a337e9..d4c909f8e474 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -14,8 +14,10 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/ath9k_platform.h>
+#include <linux/module.h>
#include "ath9k.h"
@@ -196,6 +198,19 @@ static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset)
return val;
}
+static unsigned int __ath9k_reg_rmw(struct ath_softc *sc, u32 reg_offset,
+ u32 set, u32 clr)
+{
+ u32 val;
+
+ val = ioread32(sc->mem + reg_offset);
+ val &= ~clr;
+ val |= set;
+ iowrite32(val, sc->mem + reg_offset);
+
+ return val;
+}
+
static unsigned int ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
{
struct ath_hw *ah = (struct ath_hw *) hw_priv;
@@ -204,16 +219,12 @@ static unsigned int ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 cl
unsigned long uninitialized_var(flags);
u32 val;
- if (ah->config.serialize_regmode == SER_REG_MODE_ON)
+ if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
spin_lock_irqsave(&sc->sc_serial_rw, flags);
-
- val = ioread32(sc->mem + reg_offset);
- val &= ~clr;
- val |= set;
- iowrite32(val, sc->mem + reg_offset);
-
- if (ah->config.serialize_regmode == SER_REG_MODE_ON)
+ val = __ath9k_reg_rmw(sc, reg_offset, set, clr);
spin_unlock_irqrestore(&sc->sc_serial_rw, flags);
+ } else
+ val = __ath9k_reg_rmw(sc, reg_offset, set, clr);
return val;
}
@@ -245,7 +256,7 @@ static void setup_ht_cap(struct ath_softc *sc,
ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
- if (AR_SREV_9485(ah))
+ if (AR_SREV_9330(ah) || AR_SREV_9485(ah))
max_streams = 1;
else if (AR_SREV_9300_20_OR_LATER(ah))
max_streams = 3;
@@ -260,8 +271,8 @@ static void setup_ht_cap(struct ath_softc *sc,
/* set up supported mcs set */
memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
- tx_streams = ath9k_cmn_count_streams(common->tx_chainmask, max_streams);
- rx_streams = ath9k_cmn_count_streams(common->rx_chainmask, max_streams);
+ tx_streams = ath9k_cmn_count_streams(ah->txchainmask, max_streams);
+ rx_streams = ath9k_cmn_count_streams(ah->rxchainmask, max_streams);
ath_dbg(common, ATH_DBG_CONFIG,
"TX streams %d, RX streams: %d\n",
@@ -298,10 +309,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
struct list_head *head, const char *name,
int nbuf, int ndesc, bool is_tx)
{
-#define DS2PHYS(_dd, _ds) \
- ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
-#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
-#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096)
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u8 *ds;
struct ath_buf *bf;
@@ -396,34 +403,6 @@ fail2:
fail:
memset(dd, 0, sizeof(*dd));
return error;
-#undef ATH_DESC_4KB_BOUND_CHECK
-#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED
-#undef DS2PHYS
-}
-
-void ath9k_init_crypto(struct ath_softc *sc)
-{
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- int i = 0;
-
- /* Get the hardware key cache size. */
- common->keymax = AR_KEYTABLE_SIZE;
-
- /*
- * Reset the key cache since some parts do not
- * reset the contents on initial power up.
- */
- for (i = 0; i < common->keymax; i++)
- ath_hw_keyreset(common, (u16) i);
-
- /*
- * Check whether the separate key cache entries
- * are required to handle both tx+rx MIC keys.
- * With split mic keys the number of stations is limited
- * to 27 otherwise 59.
- */
- if (sc->sc_ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA)
- common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED;
}
static int ath9k_init_btcoex(struct ath_softc *sc)
@@ -519,7 +498,6 @@ static void ath9k_init_misc(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int i = 0;
-
setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
sc->config.txpowlimit = ATH_TXPOWER_MAX;
@@ -529,10 +507,6 @@ static void ath9k_init_misc(struct ath_softc *sc)
sc->sc_flags |= SC_OP_RXAGGR;
}
- common->tx_chainmask = sc->sc_ah->caps.tx_chainmask;
- common->rx_chainmask = sc->sc_ah->caps.rx_chainmask;
-
- ath9k_hw_set_diversity(sc->sc_ah, true);
sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah);
memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
@@ -546,7 +520,7 @@ static void ath9k_init_misc(struct ath_softc *sc)
sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
}
-static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
+static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
const struct ath_bus_ops *bus_ops)
{
struct ath9k_platform_data *pdata = sc->dev->platform_data;
@@ -561,10 +535,10 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
ah->hw = sc->hw;
ah->hw_version.devid = devid;
- ah->hw_version.subsysid = subsysid;
ah->reg_ops.read = ath9k_ioread32;
ah->reg_ops.write = ath9k_iowrite32;
ah->reg_ops.rmw = ath9k_reg_rmw;
+ atomic_set(&ah->intr_ref_cnt, -1);
sc->sc_ah = ah;
if (!pdata) {
@@ -575,6 +549,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
sc->sc_ah->gpio_val = pdata->gpio_val;
sc->sc_ah->led_pin = pdata->led_pin;
ah->is_clk_25mhz = pdata->is_clk_25mhz;
+ ah->get_mac_revision = pdata->get_mac_revision;
+ ah->external_reset = pdata->external_reset;
}
common = ath9k_hw_common(ah);
@@ -585,6 +561,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
common->priv = sc;
common->debug_mask = ath9k_debug;
common->btcoex_enabled = ath9k_btcoex_enable == 1;
+ common->disable_ani = false;
spin_lock_init(&common->cc_lock);
spin_lock_init(&sc->sc_serial_rw);
@@ -592,6 +569,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
mutex_init(&sc->mutex);
#ifdef CONFIG_ATH9K_DEBUGFS
spin_lock_init(&sc->nodes_lock);
+ spin_lock_init(&sc->debug.samp_lock);
INIT_LIST_HEAD(&sc->nodes);
#endif
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
@@ -625,7 +603,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
if (ret)
goto err_btcoex;
- ath9k_init_crypto(sc);
+ ath9k_cmn_init_crypto(sc->sc_ah);
ath9k_init_misc(sc);
return 0;
@@ -649,7 +627,6 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
struct ath_hw *ah = sc->sc_ah;
- struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
int i;
sband = &sc->sbands[band];
@@ -658,7 +635,6 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
ah->curchan = &ah->channels[chan->hw_value];
ath9k_cmn_update_ichannel(ah->curchan, chan, NL80211_CHAN_HT20);
ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true);
- chan->max_power = reg->max_power_level / 2;
}
}
@@ -675,9 +651,22 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc)
ah->curchan = curchan;
}
+void ath9k_reload_chainmask_settings(struct ath_softc *sc)
+{
+ if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT))
+ return;
+
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
+ setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
+ setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
+}
+
+
void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
{
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
@@ -715,6 +704,16 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->sta_data_size = sizeof(struct ath_node);
hw->vif_data_size = sizeof(struct ath_vif);
+ hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1;
+ hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1;
+
+ /* single chain devices with rx diversity */
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
+ hw->wiphy->available_antennas_rx = BIT(0) | BIT(1);
+
+ sc->ant_rx = hw->wiphy->available_antennas_rx;
+ sc->ant_tx = hw->wiphy->available_antennas_tx;
+
#ifdef CONFIG_ATH9K_RATE_CONTROL
hw->rate_control_algorithm = "ath9k_rate_control";
#endif
@@ -726,17 +725,12 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
&sc->sbands[IEEE80211_BAND_5GHZ];
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
- }
+ ath9k_reload_chainmask_settings(sc);
SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
}
-int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
+int ath9k_init_device(u16 devid, struct ath_softc *sc,
const struct ath_bus_ops *bus_ops)
{
struct ieee80211_hw *hw = sc->hw;
@@ -746,7 +740,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
struct ath_regulatory *reg;
/* Bring up device */
- error = ath9k_init_softc(devid, sc, subsysid, bus_ops);
+ error = ath9k_init_softc(devid, sc, bus_ops);
if (error != 0)
goto error_init;
@@ -799,6 +793,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
goto error_world;
}
+ INIT_WORK(&sc->hw_reset_work, ath_reset_work);
INIT_WORK(&sc->hw_check_work, ath_hw_check);
INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index c2091f1f4096..ecdb6fd29079 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -16,6 +16,7 @@
#include "hw.h"
#include "hw-ops.h"
+#include <linux/export.h>
static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah,
struct ath9k_tx_queue_info *qi)
@@ -62,18 +63,6 @@ void ath9k_hw_txstart(struct ath_hw *ah, u32 q)
}
EXPORT_SYMBOL(ath9k_hw_txstart);
-void ath9k_hw_cleartxdesc(struct ath_hw *ah, void *ds)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
-
- ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
- ads->ds_txstatus2 = ads->ds_txstatus3 = 0;
- ads->ds_txstatus4 = ads->ds_txstatus5 = 0;
- ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
- ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
-}
-EXPORT_SYMBOL(ath9k_hw_cleartxdesc);
-
u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q)
{
u32 npend;
@@ -345,21 +334,8 @@ int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type,
}
memset(qi, 0, sizeof(struct ath9k_tx_queue_info));
qi->tqi_type = type;
- if (qinfo == NULL) {
- qi->tqi_qflags =
- TXQ_FLAG_TXOKINT_ENABLE
- | TXQ_FLAG_TXERRINT_ENABLE
- | TXQ_FLAG_TXDESCINT_ENABLE | TXQ_FLAG_TXURNINT_ENABLE;
- qi->tqi_aifs = INIT_AIFS;
- qi->tqi_cwmin = ATH9K_TXQ_USEDEFAULT;
- qi->tqi_cwmax = INIT_CWMAX;
- qi->tqi_shretry = INIT_SH_RETRY;
- qi->tqi_lgretry = INIT_LG_RETRY;
- qi->tqi_physCompBuf = 0;
- } else {
- qi->tqi_physCompBuf = qinfo->tqi_physCompBuf;
- (void) ath9k_hw_set_txq_props(ah, q, qinfo);
- }
+ qi->tqi_physCompBuf = qinfo->tqi_physCompBuf;
+ (void) ath9k_hw_set_txq_props(ah, q, qinfo);
return q;
}
@@ -564,7 +540,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
EXPORT_SYMBOL(ath9k_hw_resettxqueue);
int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
- struct ath_rx_status *rs, u64 tsf)
+ struct ath_rx_status *rs)
{
struct ar5416_desc ads;
struct ar5416_desc *adsp = AR5416DESC(ds);
@@ -609,7 +585,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
else
rs->rs_keyix = ATH9K_RXKEYIX_INVALID;
- rs->rs_rate = RXSTATUS_RATE(ah, (&ads));
+ rs->rs_rate = MS(ads.ds_rxstatus0, AR_RxRate);
rs->rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0;
rs->rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0;
@@ -645,9 +621,8 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
rs->rs_status |= ATH9K_RXERR_DECRYPT;
else if (ads.ds_rxstatus8 & AR_MichaelErr)
rs->rs_status |= ATH9K_RXERR_MIC;
-
if (ads.ds_rxstatus8 & AR_KeyMiss)
- rs->rs_status |= ATH9K_RXERR_DECRYPT;
+ rs->rs_status |= ATH9K_RXERR_KEYMISS;
}
return 0;
@@ -801,6 +776,11 @@ void ath9k_hw_disable_interrupts(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
+ atomic_set(&ah->intr_ref_cnt, -1);
+ else
+ atomic_dec(&ah->intr_ref_cnt);
+
ath_dbg(common, ATH_DBG_INTERRUPT, "disable IER\n");
REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
(void) REG_READ(ah, AR_IER);
@@ -822,6 +802,13 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah)
if (!(ah->imask & ATH9K_INT_GLOBAL))
return;
+ if (!atomic_inc_and_test(&ah->intr_ref_cnt)) {
+ ath_dbg(common, ATH_DBG_INTERRUPT,
+ "Do not enable IER ref count %d\n",
+ atomic_read(&ah->intr_ref_cnt));
+ return;
+ }
+
if (AR_SREV_9340(ah))
sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
@@ -841,9 +828,9 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah)
}
EXPORT_SYMBOL(ath9k_hw_enable_interrupts);
-void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
+void ath9k_hw_set_interrupts(struct ath_hw *ah)
{
- enum ath9k_int omask = ah->imask;
+ enum ath9k_int ints = ah->imask;
u32 mask, mask2;
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_common *common = ath9k_hw_common(ah);
@@ -851,9 +838,8 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
if (!(ints & ATH9K_INT_GLOBAL))
ath9k_hw_disable_interrupts(ah);
- ath_dbg(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);
+ ath_dbg(common, ATH_DBG_INTERRUPT, "New interrupt mask 0x%x\n", ints);
- /* TODO: global int Ref count */
mask = ints & ATH9K_INT_COMMON;
mask2 = 0;
@@ -930,9 +916,6 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER);
}
- if (ints & ATH9K_INT_GLOBAL)
- ath9k_hw_enable_interrupts(ah);
-
return;
}
EXPORT_SYMBOL(ath9k_hw_set_interrupts);
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 8e848c4d16ba..11dbd1473a13 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -17,10 +17,6 @@
#ifndef MAC_H
#define MAC_H
-#define RXSTATUS_RATE(ah, ads) (AR_SREV_5416_20_OR_LATER(ah) ? \
- MS(ads->ds_rxstatus0, AR_RxRate) : \
- (ads->ds_rxstatus3 >> 2) & 0xFF)
-
#define set11nTries(_series, _index) \
(SM((_series)[_index].Tries, AR_XmitDataTries##_index))
@@ -79,9 +75,10 @@
#define ATH9K_TXERR_XTXOP 0x08
#define ATH9K_TXERR_TIMER_EXPIRED 0x10
#define ATH9K_TX_ACKED 0x20
+#define ATH9K_TX_FLUSH 0x40
#define ATH9K_TXERR_MASK \
(ATH9K_TXERR_XRETRY | ATH9K_TXERR_FILT | ATH9K_TXERR_FIFO | \
- ATH9K_TXERR_XTXOP | ATH9K_TXERR_TIMER_EXPIRED)
+ ATH9K_TXERR_XTXOP | ATH9K_TXERR_TIMER_EXPIRED | ATH9K_TX_FLUSH)
#define ATH9K_TX_BA 0x01
#define ATH9K_TX_PWRMGMT 0x02
@@ -146,6 +143,7 @@ struct ath_rx_status {
u8 rs_moreaggr;
u8 rs_num_delims;
u8 rs_flags;
+ bool is_mybeacon;
u32 evm0;
u32 evm1;
u32 evm2;
@@ -184,6 +182,7 @@ struct ath_htc_rx_status {
#define ATH9K_RXERR_FIFO 0x04
#define ATH9K_RXERR_DECRYPT 0x08
#define ATH9K_RXERR_MIC 0x10
+#define ATH9K_RXERR_KEYMISS 0x20
#define ATH9K_RX_MORE 0x01
#define ATH9K_RX_MORE_AGGR 0x02
@@ -194,7 +193,7 @@ struct ath_htc_rx_status {
#define ATH9K_RX_DECRYPT_BUSY 0x40
#define ATH9K_RXKEYIX_INVALID ((u8)-1)
-#define ATH9K_TXKEYIX_INVALID ((u32)-1)
+#define ATH9K_TXKEYIX_INVALID ((u8)-1)
enum ath9k_phyerr {
ATH9K_PHYERR_UNDERRUN = 0, /* Transmit underrun */
@@ -262,7 +261,11 @@ struct ath_desc {
#define ATH9K_TXDESC_VMF 0x0100
#define ATH9K_TXDESC_FRAG_IS_ON 0x0200
#define ATH9K_TXDESC_LOWRXCHAIN 0x0400
-#define ATH9K_TXDESC_LDPC 0x00010000
+#define ATH9K_TXDESC_LDPC 0x0800
+#define ATH9K_TXDESC_CLRDMASK 0x1000
+
+#define ATH9K_TXDESC_PAPRD 0x70000
+#define ATH9K_TXDESC_PAPRD_S 16
#define ATH9K_RXDESC_INTREQ 0x0020
@@ -643,6 +646,7 @@ enum ath9k_rx_filter {
ATH9K_RX_FILTER_PSPOLL = 0x00004000,
ATH9K_RX_FILTER_PHYRADAR = 0x00002000,
ATH9K_RX_FILTER_MCAST_BCAST_ALL = 0x00008000,
+ ATH9K_RX_FILTER_CONTROL_WRAPPER = 0x00080000,
};
#define ATH9K_RATESERIES_RTS_CTS 0x0001
@@ -658,6 +662,13 @@ struct ath9k_11n_rate_series {
u32 RateFlags;
};
+enum aggr_type {
+ AGGR_BUF_NONE,
+ AGGR_BUF_FIRST,
+ AGGR_BUF_MIDDLE,
+ AGGR_BUF_LAST,
+};
+
enum ath9k_key_type {
ATH9K_KEY_TYPE_CLEAR,
ATH9K_KEY_TYPE_WEP,
@@ -665,6 +676,33 @@ enum ath9k_key_type {
ATH9K_KEY_TYPE_TKIP,
};
+struct ath_tx_info {
+ u8 qcu;
+
+ bool is_first;
+ bool is_last;
+
+ enum aggr_type aggr;
+ u8 ndelim;
+ u16 aggr_len;
+
+ dma_addr_t link;
+ int pkt_len;
+ u32 flags;
+
+ dma_addr_t buf_addr[4];
+ int buf_len[4];
+
+ struct ath9k_11n_rate_series rates[4];
+ u8 rtscts_rate;
+ bool dur_update;
+
+ enum ath9k_pkt_type type;
+ enum ath9k_key_type keytype;
+ u8 keyix;
+ u8 txpower;
+};
+
struct ath_hw;
struct ath9k_channel;
enum ath9k_int;
@@ -672,7 +710,6 @@ enum ath9k_int;
u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q);
void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp);
void ath9k_hw_txstart(struct ath_hw *ah, u32 q);
-void ath9k_hw_cleartxdesc(struct ath_hw *ah, void *ds);
u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q);
bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel);
bool ath9k_hw_stop_dma_queue(struct ath_hw *ah, u32 q);
@@ -687,7 +724,7 @@ int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type,
bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q);
bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q);
int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
- struct ath_rx_status *rs, u64 tsf);
+ struct ath_rx_status *rs);
void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 size, u32 flags);
bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set);
@@ -699,7 +736,7 @@ int ath9k_hw_beaconq_setup(struct ath_hw *ah);
/* Interrupt Handling */
bool ath9k_hw_intrpend(struct ath_hw *ah);
-void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints);
+void ath9k_hw_set_interrupts(struct ath_hw *ah);
void ath9k_hw_enable_interrupts(struct ath_hw *ah);
void ath9k_hw_disable_interrupts(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 2ca351fe6d3c..93fbe6f40898 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -62,14 +62,12 @@ static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
if (txq->axq_depth || !list_empty(&txq->axq_acq))
pending = true;
- else if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
- pending = !list_empty(&txq->txq_fifo_pending);
spin_unlock_bh(&txq->axq_lock);
return pending;
}
-bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode)
+static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode)
{
unsigned long flags;
bool ret;
@@ -113,30 +111,35 @@ void ath9k_ps_wakeup(struct ath_softc *sc)
void ath9k_ps_restore(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ enum ath9k_power_mode mode;
unsigned long flags;
spin_lock_irqsave(&sc->sc_pm_lock, flags);
if (--sc->ps_usecount != 0)
goto unlock;
- spin_lock(&common->cc_lock);
- ath_hw_cycle_counters_update(common);
- spin_unlock(&common->cc_lock);
-
if (sc->ps_idle)
- ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
+ mode = ATH9K_PM_FULL_SLEEP;
else if (sc->ps_enabled &&
!(sc->ps_flags & (PS_WAIT_FOR_BEACON |
PS_WAIT_FOR_CAB |
PS_WAIT_FOR_PSPOLL_DATA |
PS_WAIT_FOR_TX_ACK)))
- ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
+ mode = ATH9K_PM_NETWORK_SLEEP;
+ else
+ goto unlock;
+
+ spin_lock(&common->cc_lock);
+ ath_hw_cycle_counters_update(common);
+ spin_unlock(&common->cc_lock);
+
+ ath9k_hw_setpower(sc->sc_ah, mode);
unlock:
spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
}
-static void ath_start_ani(struct ath_common *common)
+void ath_start_ani(struct ath_common *common)
{
struct ath_hw *ah = common->ah;
unsigned long timestamp = jiffies_to_msecs(jiffies);
@@ -165,7 +168,7 @@ static void ath_update_survey_nf(struct ath_softc *sc, int channel)
if (chan->noisefloor) {
survey->filled |= SURVEY_INFO_NOISE_DBM;
- survey->noise = chan->noisefloor;
+ survey->noise = ath9k_hw_getchan_noise(ah, chan);
}
}
@@ -214,103 +217,168 @@ static int ath_update_survey_stats(struct ath_softc *sc)
return ret;
}
-/*
- * Set/change channels. If the channel is really being changed, it's done
- * by reseting the chip. To accomplish this we must first cleanup any pending
- * DMA, then restart stuff.
-*/
-int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
- struct ath9k_channel *hchan)
+static void __ath_cancel_work(struct ath_softc *sc)
{
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_conf *conf = &common->hw->conf;
- bool fastcc = true, stopped;
- struct ieee80211_channel *channel = hw->conf.channel;
- struct ath9k_hw_cal_data *caldata = NULL;
- int r;
-
- if (sc->sc_flags & SC_OP_INVALID)
- return -EIO;
-
- sc->hw_busy_count = 0;
-
- del_timer_sync(&common->ani.timer);
cancel_work_sync(&sc->paprd_work);
cancel_work_sync(&sc->hw_check_work);
cancel_delayed_work_sync(&sc->tx_complete_work);
cancel_delayed_work_sync(&sc->hw_pll_work);
+}
- ath9k_ps_wakeup(sc);
+static void ath_cancel_work(struct ath_softc *sc)
+{
+ __ath_cancel_work(sc);
+ cancel_work_sync(&sc->hw_reset_work);
+}
- spin_lock_bh(&sc->sc_pcu_lock);
+static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ bool ret;
- /*
- * This is only performed if the channel settings have
- * actually changed.
- *
- * To switch channels clear any pending DMA operations;
- * wait long enough for the RX fifo to drain, reset the
- * hardware at the new frequency, and then re-enable
- * the relevant bits of the h/w.
- */
- ath9k_hw_disable_interrupts(ah);
- stopped = ath_drain_all_txq(sc, false);
+ ieee80211_stop_queues(sc->hw);
- if (!ath_stoprecv(sc))
- stopped = false;
+ sc->hw_busy_count = 0;
+ del_timer_sync(&common->ani.timer);
- if (!ath9k_hw_check_alive(ah))
- stopped = false;
+ ath9k_debug_samp_bb_mac(sc);
+ ath9k_hw_disable_interrupts(ah);
- /* XXX: do not flush receive queue here. We don't want
- * to flush data frames already in queue because of
- * changing channel. */
+ ret = ath_drain_all_txq(sc, retry_tx);
- if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL))
- fastcc = false;
+ if (!ath_stoprecv(sc))
+ ret = false;
- if (!(sc->sc_flags & SC_OP_OFFCHANNEL))
- caldata = &sc->caldata;
+ if (!flush) {
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
+ ath_rx_tasklet(sc, 1, true);
+ ath_rx_tasklet(sc, 1, false);
+ } else {
+ ath_flushrecv(sc);
+ }
- ath_dbg(common, ATH_DBG_CONFIG,
- "(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n",
- sc->sc_ah->curchan->channel,
- channel->center_freq, conf_is_ht40(conf),
- fastcc);
+ return ret;
+}
- r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
- if (r) {
- ath_err(common,
- "Unable to reset channel (%u MHz), reset status %d\n",
- channel->center_freq, r);
- goto ps_restore;
- }
+static bool ath_complete_reset(struct ath_softc *sc, bool start)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
if (ath_startrecv(sc) != 0) {
ath_err(common, "Unable to restart recv logic\n");
- r = -EIO;
- goto ps_restore;
+ return false;
}
ath9k_cmn_update_txpow(ah, sc->curtxpow,
sc->config.txpowlimit, &sc->curtxpow);
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_set_interrupts(ah);
+ ath9k_hw_enable_interrupts(ah);
- if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) {
+ if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) && start) {
if (sc->sc_flags & SC_OP_BEACONS)
ath_set_beacon(sc);
+
ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
- ath_start_ani(common);
+ if (!common->disable_ani)
+ ath_start_ani(common);
}
- ps_restore:
- ieee80211_wake_queues(hw);
+ if (ath9k_hw_ops(ah)->antdiv_comb_conf_get && sc->ant_rx != 3) {
+ struct ath_hw_antcomb_conf div_ant_conf;
+ u8 lna_conf;
+
+ ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
+ if (sc->ant_rx == 1)
+ lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ else
+ lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ div_ant_conf.main_lna_conf = lna_conf;
+ div_ant_conf.alt_lna_conf = lna_conf;
+
+ ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
+ }
+
+ ieee80211_wake_queues(sc->hw);
+
+ return true;
+}
+
+static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
+ bool retry_tx)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_hw_cal_data *caldata = NULL;
+ bool fastcc = true;
+ bool flush = false;
+ int r;
+
+ __ath_cancel_work(sc);
+
+ spin_lock_bh(&sc->sc_pcu_lock);
+
+ if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) {
+ fastcc = false;
+ caldata = &sc->caldata;
+ }
+
+ if (!hchan) {
+ fastcc = false;
+ flush = true;
+ hchan = ah->curchan;
+ }
+
+ if (fastcc && !ath9k_hw_check_alive(ah))
+ fastcc = false;
+
+ if (!ath_prepare_reset(sc, retry_tx, flush))
+ fastcc = false;
+
+ ath_dbg(common, ATH_DBG_CONFIG,
+ "Reset to %u MHz, HT40: %d fastcc: %d\n",
+ hchan->channel, !!(hchan->channelFlags & (CHANNEL_HT40MINUS |
+ CHANNEL_HT40PLUS)),
+ fastcc);
+
+ r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
+ if (r) {
+ ath_err(common,
+ "Unable to reset channel, reset status %d\n", r);
+ goto out;
+ }
+
+ if (!ath_complete_reset(sc, true))
+ r = -EIO;
+
+out:
spin_unlock_bh(&sc->sc_pcu_lock);
+ return r;
+}
+
+
+/*
+ * Set/change channels. If the channel is really being changed, it's done
+ * by reseting the chip. To accomplish this we must first cleanup any pending
+ * DMA, then restart stuff.
+*/
+static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
+ struct ath9k_channel *hchan)
+{
+ int r;
+
+ if (sc->sc_flags & SC_OP_INVALID)
+ return -EIO;
+
+ ath9k_ps_wakeup(sc);
+
+ r = ath_reset_internal(sc, hchan, false);
ath9k_ps_restore(sc);
+
return r;
}
@@ -318,7 +386,6 @@ static void ath_paprd_activate(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath9k_hw_cal_data *caldata = ah->caldata;
- struct ath_common *common = ath9k_hw_common(ah);
int chain;
if (!caldata || !caldata->paprd_done)
@@ -327,7 +394,7 @@ static void ath_paprd_activate(struct ath_softc *sc)
ath9k_ps_wakeup(sc);
ar9003_paprd_enable(ah, false);
for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
- if (!(common->tx_chainmask & BIT(chain)))
+ if (!(ah->txchainmask & BIT(chain)))
continue;
ar9003_paprd_populate_single_table(ah, caldata, chain);
@@ -361,7 +428,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int
txctl.paprd = BIT(chain);
if (ath_tx_start(hw, skb, &txctl) != 0) {
- ath_dbg(common, ATH_DBG_XMIT, "PAPRD TX failed\n");
+ ath_dbg(common, ATH_DBG_CALIBRATE, "PAPRD TX failed\n");
dev_kfree_skb_any(skb);
return false;
}
@@ -370,7 +437,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int
msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
if (!time_left)
- ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_CALIBRATE,
+ ath_dbg(common, ATH_DBG_CALIBRATE,
"Timeout waiting for paprd training on TX chain %d\n",
chain);
@@ -394,12 +461,14 @@ void ath_paprd_calibrate(struct work_struct *work)
if (!caldata)
return;
+ ath9k_ps_wakeup(sc);
+
if (ar9003_paprd_init_table(ah) < 0)
- return;
+ goto fail_paprd;
skb = alloc_skb(len, GFP_KERNEL);
if (!skb)
- return;
+ goto fail_paprd;
skb_put(skb, len);
memset(skb->data, 0, len);
@@ -411,9 +480,8 @@ void ath_paprd_calibrate(struct work_struct *work)
memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
- ath9k_ps_wakeup(sc);
for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
- if (!(common->tx_chainmask & BIT(chain)))
+ if (!(ah->txchainmask & BIT(chain)))
continue;
chain_ok = 0;
@@ -431,11 +499,18 @@ void ath_paprd_calibrate(struct work_struct *work)
if (!ath_paprd_send_frame(sc, skb, chain))
goto fail_paprd;
- if (!ar9003_paprd_is_done(ah))
+ if (!ar9003_paprd_is_done(ah)) {
+ ath_dbg(common, ATH_DBG_CALIBRATE,
+ "PAPRD not yet done on chain %d\n", chain);
break;
+ }
- if (ar9003_paprd_create_curve(ah, caldata, chain) != 0)
+ if (ar9003_paprd_create_curve(ah, caldata, chain)) {
+ ath_dbg(common, ATH_DBG_CALIBRATE,
+ "PAPRD create curve failed on chain %d\n",
+ chain);
break;
+ }
chain_ok = 1;
}
@@ -515,24 +590,19 @@ void ath_ani_calibrate(unsigned long data)
common->ani.checkani_timer = timestamp;
}
- /* Skip all processing if there's nothing to do. */
- if (longcal || shortcal || aniflag) {
- /* Call ANI routine if necessary */
- if (aniflag) {
- spin_lock_irqsave(&common->cc_lock, flags);
- ath9k_hw_ani_monitor(ah, ah->curchan);
- ath_update_survey_stats(sc);
- spin_unlock_irqrestore(&common->cc_lock, flags);
- }
+ /* Call ANI routine if necessary */
+ if (aniflag) {
+ spin_lock_irqsave(&common->cc_lock, flags);
+ ath9k_hw_ani_monitor(ah, ah->curchan);
+ ath_update_survey_stats(sc);
+ spin_unlock_irqrestore(&common->cc_lock, flags);
+ }
- /* Perform calibration if necessary */
- if (longcal || shortcal) {
- common->ani.caldone =
- ath9k_hw_calibrate(ah,
- ah->curchan,
- common->rx_chainmask,
- longcal);
- }
+ /* Perform calibration if necessary */
+ if (longcal || shortcal) {
+ common->ani.caldone =
+ ath9k_hw_calibrate(ah, ah->curchan,
+ ah->rxchainmask, longcal);
}
ath9k_ps_restore(sc);
@@ -543,6 +613,7 @@ set_timer:
* The interval must be the shortest necessary to satisfy ANI,
* short calibration and long calibration.
*/
+ ath9k_debug_samp_bb_mac(sc);
cal_interval = ATH_LONG_CALINTERVAL;
if (sc->sc_ah->config.enable_ani)
cal_interval = min(cal_interval,
@@ -562,7 +633,6 @@ set_timer:
static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
{
struct ath_node *an;
- struct ath_hw *ah = sc->sc_ah;
an = (struct ath_node *)sta->drv_priv;
#ifdef CONFIG_ATH9K_DEBUGFS
@@ -571,9 +641,6 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
spin_unlock(&sc->nodes_lock);
an->sta = sta;
#endif
- if ((ah->caps.hw_caps) & ATH9K_HW_CAP_APM)
- sc->sc_flags |= SC_OP_ENABLE_APM;
-
if (sc->sc_flags & SC_OP_TXAGGR) {
ath_tx_node_init(sc, an);
an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
@@ -597,69 +664,6 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
ath_tx_node_cleanup(sc, an);
}
-void ath_hw_check(struct work_struct *work)
-{
- struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- unsigned long flags;
- int busy;
-
- ath9k_ps_wakeup(sc);
- if (ath9k_hw_check_alive(sc->sc_ah))
- goto out;
-
- spin_lock_irqsave(&common->cc_lock, flags);
- busy = ath_update_survey_stats(sc);
- spin_unlock_irqrestore(&common->cc_lock, flags);
-
- ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, "
- "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1);
- if (busy >= 99) {
- if (++sc->hw_busy_count >= 3)
- ath_reset(sc, true);
- } else if (busy >= 0)
- sc->hw_busy_count = 0;
-
-out:
- ath9k_ps_restore(sc);
-}
-
-static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
-{
- static int count;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-
- if (pll_sqsum >= 0x40000) {
- count++;
- if (count == 3) {
- /* Rx is hung for more than 500ms. Reset it */
- ath_dbg(common, ATH_DBG_RESET,
- "Possible RX hang, resetting");
- ath_reset(sc, true);
- count = 0;
- }
- } else
- count = 0;
-}
-
-void ath_hw_pll_work(struct work_struct *work)
-{
- struct ath_softc *sc = container_of(work, struct ath_softc,
- hw_pll_work.work);
- u32 pll_sqsum;
-
- if (AR_SREV_9485(sc->sc_ah)) {
-
- ath9k_ps_wakeup(sc);
- pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
- ath9k_ps_restore(sc);
-
- ath_hw_pll_rx_hang_check(sc, pll_sqsum);
-
- ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5);
- }
-}
-
void ath9k_tasklet(unsigned long data)
{
@@ -670,14 +674,24 @@ void ath9k_tasklet(unsigned long data)
u32 status = sc->intrstatus;
u32 rxmask;
+ ath9k_ps_wakeup(sc);
+ spin_lock(&sc->sc_pcu_lock);
+
if ((status & ATH9K_INT_FATAL) ||
(status & ATH9K_INT_BB_WATCHDOG)) {
- ath_reset(sc, true);
- return;
- }
+#ifdef CONFIG_ATH9K_DEBUGFS
+ enum ath_reset_type type;
- ath9k_ps_wakeup(sc);
- spin_lock(&sc->sc_pcu_lock);
+ if (status & ATH9K_INT_FATAL)
+ type = RESET_TYPE_FATAL_INT;
+ else
+ type = RESET_TYPE_BB_WATCHDOG;
+
+ RESET_STAT_INC(sc, type);
+#endif
+ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ goto out;
+ }
/*
* Only run the baseband hang check if beacons stop working in AP or
@@ -697,8 +711,7 @@ void ath9k_tasklet(unsigned long data)
*/
ath_dbg(common, ATH_DBG_PS,
"TSFOOR - Sync with next Beacon\n");
- sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC |
- PS_TSFOOR_SYNC;
+ sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC;
}
if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
@@ -727,6 +740,7 @@ void ath9k_tasklet(unsigned long data)
if (status & ATH9K_INT_GENTIMER)
ath_gen_timer_isr(sc->sc_ah);
+out:
/* re-enable hardware interrupt */
ath9k_hw_enable_interrupts(ah);
@@ -817,11 +831,9 @@ irqreturn_t ath_isr(int irq, void *dev)
if (status & ATH9K_INT_TXURN)
ath9k_hw_updatetxtriglevel(ah, true);
- if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
- if (status & ATH9K_INT_RXEOL) {
- ah->imask &= ~(ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
- ath9k_hw_set_interrupts(ah, ah->imask);
- }
+ if (status & ATH9K_INT_RXEOL) {
+ ah->imask &= ~(ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
+ ath9k_hw_set_interrupts(ah);
}
if (status & ATH9K_INT_MIB) {
@@ -868,7 +880,7 @@ chip_reset:
#undef SCHED_INTR
}
-void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
+static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
@@ -877,8 +889,9 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
ath9k_ps_wakeup(sc);
spin_lock_bh(&sc->sc_pcu_lock);
+ atomic_set(&ah->intr_ref_cnt, -1);
- ath9k_hw_configpcipowersave(ah, 0, 0);
+ ath9k_hw_configpcipowersave(ah, false);
if (!ah->curchan)
ah->curchan = ath9k_cmn_get_curchannel(sc->hw, ah);
@@ -890,27 +903,13 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
channel->center_freq, r);
}
- ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->config.txpowlimit, &sc->curtxpow);
- if (ath_startrecv(sc) != 0) {
- ath_err(common, "Unable to restart recv logic\n");
- goto out;
- }
- if (sc->sc_flags & SC_OP_BEACONS)
- ath_set_beacon(sc); /* restart beacons */
-
- /* Re-Enable interrupts */
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath_complete_reset(sc, true);
/* Enable LED */
ath9k_hw_cfg_output(ah, ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_set_gpio(ah, ah->led_pin, 0);
- ieee80211_wake_queues(hw);
- ieee80211_queue_delayed_work(hw, &sc->hw_pll_work, HZ/2);
-
-out:
spin_unlock_bh(&sc->sc_pcu_lock);
ath9k_ps_restore(sc);
@@ -923,11 +922,10 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
int r;
ath9k_ps_wakeup(sc);
- cancel_delayed_work_sync(&sc->hw_pll_work);
- spin_lock_bh(&sc->sc_pcu_lock);
+ ath_cancel_work(sc);
- ieee80211_stop_queues(hw);
+ spin_lock_bh(&sc->sc_pcu_lock);
/*
* Keep the LED on when the radio is disabled
@@ -938,13 +936,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
}
- /* Disable interrupts */
- ath9k_hw_disable_interrupts(ah);
-
- ath_drain_all_txq(sc, false); /* clear pending tx frames */
-
- ath_stoprecv(sc); /* turn off frame recv */
- ath_flushrecv(sc); /* flush recv queue */
+ ath_prepare_reset(sc, false, true);
if (!ah->curchan)
ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
@@ -958,55 +950,19 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
ath9k_hw_phy_disable(ah);
- ath9k_hw_configpcipowersave(ah, 1, 1);
+ ath9k_hw_configpcipowersave(ah, true);
spin_unlock_bh(&sc->sc_pcu_lock);
ath9k_ps_restore(sc);
}
-int ath_reset(struct ath_softc *sc, bool retry_tx)
+static int ath_reset(struct ath_softc *sc, bool retry_tx)
{
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_hw *hw = sc->hw;
int r;
- sc->hw_busy_count = 0;
-
- /* Stop ANI */
- del_timer_sync(&common->ani.timer);
-
ath9k_ps_wakeup(sc);
- spin_lock_bh(&sc->sc_pcu_lock);
-
- ieee80211_stop_queues(hw);
-
- ath9k_hw_disable_interrupts(ah);
- ath_drain_all_txq(sc, retry_tx);
-
- ath_stoprecv(sc);
- ath_flushrecv(sc);
- r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
- if (r)
- ath_err(common,
- "Unable to reset hardware; reset status %d\n", r);
-
- if (ath_startrecv(sc) != 0)
- ath_err(common, "Unable to start recv logic\n");
-
- /*
- * We may be doing a reset in response to a request
- * that changes the channel so update any state that
- * might change as a result.
- */
- ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->config.txpowlimit, &sc->curtxpow);
-
- if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL)))
- ath_set_beacon(sc); /* restart beacons */
-
- ath9k_hw_set_interrupts(ah, ah->imask);
+ r = ath_reset_internal(sc, NULL, retry_tx);
if (retry_tx) {
int i;
@@ -1019,16 +975,85 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
}
}
- ieee80211_wake_queues(hw);
- spin_unlock_bh(&sc->sc_pcu_lock);
-
- /* Start ANI */
- ath_start_ani(common);
ath9k_ps_restore(sc);
return r;
}
+void ath_reset_work(struct work_struct *work)
+{
+ struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work);
+
+ ath_reset(sc, true);
+}
+
+void ath_hw_check(struct work_struct *work)
+{
+ struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ unsigned long flags;
+ int busy;
+
+ ath9k_ps_wakeup(sc);
+ if (ath9k_hw_check_alive(sc->sc_ah))
+ goto out;
+
+ spin_lock_irqsave(&common->cc_lock, flags);
+ busy = ath_update_survey_stats(sc);
+ spin_unlock_irqrestore(&common->cc_lock, flags);
+
+ ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, "
+ "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1);
+ if (busy >= 99) {
+ if (++sc->hw_busy_count >= 3) {
+ RESET_STAT_INC(sc, RESET_TYPE_BB_HANG);
+ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ }
+
+ } else if (busy >= 0)
+ sc->hw_busy_count = 0;
+
+out:
+ ath9k_ps_restore(sc);
+}
+
+static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
+{
+ static int count;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ if (pll_sqsum >= 0x40000) {
+ count++;
+ if (count == 3) {
+ /* Rx is hung for more than 500ms. Reset it */
+ ath_dbg(common, ATH_DBG_RESET,
+ "Possible RX hang, resetting");
+ RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG);
+ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ count = 0;
+ }
+ } else
+ count = 0;
+}
+
+void ath_hw_pll_work(struct work_struct *work)
+{
+ struct ath_softc *sc = container_of(work, struct ath_softc,
+ hw_pll_work.work);
+ u32 pll_sqsum;
+
+ if (AR_SREV_9485(sc->sc_ah)) {
+
+ ath9k_ps_wakeup(sc);
+ pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
+ ath9k_ps_restore(sc);
+
+ ath_hw_pll_rx_hang_check(sc, pll_sqsum);
+
+ ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5);
+ }
+}
+
/**********************/
/* mac80211 callbacks */
/**********************/
@@ -1056,7 +1081,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
init_channel = ath9k_cmn_get_curchannel(hw, ah);
/* Reset SERDES registers */
- ath9k_hw_configpcipowersave(ah, 0, 0);
+ ath9k_hw_configpcipowersave(ah, false);
/*
* The basic interface to setting the hardware in a good
@@ -1075,28 +1100,6 @@ static int ath9k_start(struct ieee80211_hw *hw)
goto mutex_unlock;
}
- /*
- * This is needed only to setup initial state
- * but it's best done after a reset.
- */
- ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->config.txpowlimit, &sc->curtxpow);
-
- /*
- * Setup the hardware after reset:
- * The receive engine is set going.
- * Frame transmit is handled entirely
- * in the frame output path; there's nothing to do
- * here except setup the interrupt mask.
- */
- if (ath_startrecv(sc) != 0) {
- ath_err(common, "Unable to start recv logic\n");
- r = -EIO;
- spin_unlock_bh(&sc->sc_pcu_lock);
- goto mutex_unlock;
- }
- spin_unlock_bh(&sc->sc_pcu_lock);
-
/* Setup our intr mask. */
ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
ATH9K_INT_RXORN | ATH9K_INT_FATAL |
@@ -1119,11 +1122,14 @@ static int ath9k_start(struct ieee80211_hw *hw)
/* Disable BMISS interrupt when we're not associated */
ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
- ath9k_hw_set_interrupts(ah, ah->imask);
- ieee80211_wake_queues(hw);
+ if (!ath_complete_reset(sc, false)) {
+ r = -EIO;
+ spin_unlock_bh(&sc->sc_pcu_lock);
+ goto mutex_unlock;
+ }
- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+ spin_unlock_bh(&sc->sc_pcu_lock);
if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
!ah->btcoex_hw.enabled) {
@@ -1131,8 +1137,6 @@ static int ath9k_start(struct ieee80211_hw *hw)
AR_STOMP_LOW_WLAN_WGHT);
ath9k_hw_btcoex_enable(ah);
- if (common->bus_ops->bt_coex_prep)
- common->bus_ops->bt_coex_prep(common);
if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
ath9k_btcoex_timer_resume(sc);
}
@@ -1218,10 +1222,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
mutex_lock(&sc->mutex);
- cancel_delayed_work_sync(&sc->tx_complete_work);
- cancel_delayed_work_sync(&sc->hw_pll_work);
- cancel_work_sync(&sc->paprd_work);
- cancel_work_sync(&sc->hw_check_work);
+ ath_cancel_work(sc);
if (sc->sc_flags & SC_OP_INVALID) {
ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
@@ -1261,7 +1262,6 @@ static void ath9k_stop(struct ieee80211_hw *hw)
/* disable HAL and put h/w to sleep */
ath9k_hw_disable(ah);
- ath9k_hw_configpcipowersave(ah, 1, 1);
spin_unlock_bh(&sc->sc_pcu_lock);
@@ -1409,13 +1409,17 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
ah->imask &= ~ATH9K_INT_TSFOOR;
}
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_set_interrupts(ah);
/* Set up ANI */
- if ((iter_data.naps + iter_data.nadhocs) > 0) {
+ if (iter_data.naps > 0) {
sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
- sc->sc_flags |= SC_OP_ANI_RUN;
- ath_start_ani(common);
+
+ if (!common->disable_ani) {
+ sc->sc_flags |= SC_OP_ANI_RUN;
+ ath_start_ani(common);
+ }
+
} else {
sc->sc_flags &= ~SC_OP_ANI_RUN;
del_timer_sync(&common->ani.timer);
@@ -1580,7 +1584,7 @@ static void ath9k_enable_ps(struct ath_softc *sc)
if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) {
ah->imask |= ATH9K_INT_TIM_TIMER;
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_set_interrupts(ah);
}
ath9k_hw_setrxabort(ah, 1);
}
@@ -1600,7 +1604,7 @@ static void ath9k_disable_ps(struct ath_softc *sc)
PS_WAIT_FOR_TX_ACK);
if (ah->imask & ATH9K_INT_TIM_TIMER) {
ah->imask &= ~ATH9K_INT_TIM_TIMER;
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_set_interrupts(ah);
}
}
@@ -1663,6 +1667,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
struct ieee80211_channel *curchan = hw->conf.channel;
+ struct ath9k_channel old_chan;
int pos = curchan->hw_value;
int old_pos = -1;
unsigned long flags;
@@ -1679,15 +1684,25 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
"Set channel: %d MHz type: %d\n",
curchan->center_freq, conf->channel_type);
- ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
- curchan, conf->channel_type);
-
/* update survey stats for the old channel before switching */
spin_lock_irqsave(&common->cc_lock, flags);
ath_update_survey_stats(sc);
spin_unlock_irqrestore(&common->cc_lock, flags);
/*
+ * Preserve the current channel values, before updating
+ * the same channel
+ */
+ if (old_pos == pos) {
+ memcpy(&old_chan, &sc->sc_ah->channels[pos],
+ sizeof(struct ath9k_channel));
+ ah->curchan = &old_chan;
+ }
+
+ ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
+ curchan, conf->channel_type);
+
+ /*
* If the operating channel changes, change the survey in-use flags
* along with it.
* Reset the survey data for the new channel, unless we're switching
@@ -1831,8 +1846,7 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw,
switch (cmd) {
case STA_NOTIFY_SLEEP:
an->sleeping = true;
- if (ath_tx_aggr_sleep(sc, an))
- ieee80211_sta_set_tim(sta);
+ ath_tx_aggr_sleep(sta, sc, an);
break;
case STA_NOTIFY_AWAKE:
an->sleeping = false;
@@ -1841,7 +1855,8 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw,
}
}
-static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue,
+static int ath9k_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct ath_softc *sc = hw->priv;
@@ -1952,50 +1967,38 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
struct ath_vif *avp = (void *)vif->drv_priv;
- switch (sc->sc_ah->opmode) {
- case NL80211_IFTYPE_ADHOC:
- /* There can be only one vif available */
+ /*
+ * Skip iteration if primary station vif's bss info
+ * was not changed
+ */
+ if (sc->sc_flags & SC_OP_PRIM_STA_VIF)
+ return;
+
+ if (bss_conf->assoc) {
+ sc->sc_flags |= SC_OP_PRIM_STA_VIF;
+ avp->primary_sta_vif = true;
memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
common->curaid = bss_conf->aid;
ath9k_hw_write_associd(sc->sc_ah);
- /* configure beacon */
- if (bss_conf->enable_beacon)
- ath_beacon_config(sc, vif);
- break;
- case NL80211_IFTYPE_STATION:
- /*
- * Skip iteration if primary station vif's bss info
- * was not changed
- */
- if (sc->sc_flags & SC_OP_PRIM_STA_VIF)
- break;
-
- if (bss_conf->assoc) {
- sc->sc_flags |= SC_OP_PRIM_STA_VIF;
- avp->primary_sta_vif = true;
- memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
- common->curaid = bss_conf->aid;
- ath9k_hw_write_associd(sc->sc_ah);
- ath_dbg(common, ATH_DBG_CONFIG,
+ ath_dbg(common, ATH_DBG_CONFIG,
"Bss Info ASSOC %d, bssid: %pM\n",
bss_conf->aid, common->curbssid);
- ath_beacon_config(sc, vif);
- /*
- * Request a re-configuration of Beacon related timers
- * on the receipt of the first Beacon frame (i.e.,
- * after time sync with the AP).
- */
- sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
- /* Reset rssi stats */
- sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
- sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
+ ath_beacon_config(sc, vif);
+ /*
+ * Request a re-configuration of Beacon related timers
+ * on the receipt of the first Beacon frame (i.e.,
+ * after time sync with the AP).
+ */
+ sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
+ /* Reset rssi stats */
+ sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
+ sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
+ if (!common->disable_ani) {
sc->sc_flags |= SC_OP_ANI_RUN;
ath_start_ani(common);
}
- break;
- default:
- break;
+
}
}
@@ -2005,6 +2008,9 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif)
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
struct ath_vif *avp = (void *)vif->drv_priv;
+ if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION)
+ return;
+
/* Reconfigure bss info */
if (avp->primary_sta_vif && !bss_conf->assoc) {
ath_dbg(common, ATH_DBG_CONFIG,
@@ -2023,12 +2029,12 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif)
* None of station vifs are associated.
* Clear bssid & aid
*/
- if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) &&
- !(sc->sc_flags & SC_OP_PRIM_STA_VIF)) {
+ if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF)) {
ath9k_hw_write_associd(sc->sc_ah);
/* Stop ANI */
sc->sc_flags &= ~SC_OP_ANI_RUN;
del_timer_sync(&common->ani.timer);
+ memset(&sc->caldata, 0, sizeof(sc->caldata));
}
}
@@ -2054,6 +2060,26 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
common->curbssid, common->curaid);
}
+ if (changed & BSS_CHANGED_IBSS) {
+ /* There can be only one vif available */
+ memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+ common->curaid = bss_conf->aid;
+ ath9k_hw_write_associd(sc->sc_ah);
+
+ if (bss_conf->ibss_joined) {
+ sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
+
+ if (!common->disable_ani) {
+ sc->sc_flags |= SC_OP_ANI_RUN;
+ ath_start_ani(common);
+ }
+
+ } else {
+ sc->sc_flags &= ~SC_OP_ANI_RUN;
+ del_timer_sync(&common->ani.timer);
+ }
+ }
+
/* Enable transmission of beacons (AP, IBSS, MESH) */
if ((changed & BSS_CHANGED_BEACON) ||
((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon)) {
@@ -2130,7 +2156,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
ath9k_ps_restore(sc);
}
-static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
+static u64 ath9k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct ath_softc *sc = hw->priv;
u64 tsf;
@@ -2144,7 +2170,9 @@ static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
return tsf;
}
-static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+static void ath9k_set_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u64 tsf)
{
struct ath_softc *sc = hw->priv;
@@ -2155,7 +2183,7 @@ static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
mutex_unlock(&sc->mutex);
}
-static void ath9k_reset_tsf(struct ieee80211_hw *hw)
+static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct ath_softc *sc = hw->priv;
@@ -2260,7 +2288,11 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
mutex_lock(&sc->mutex);
ah->coverage_class = coverage_class;
+
+ ath9k_ps_wakeup(sc);
ath9k_hw_init_global_settings(ah);
+ ath9k_ps_restore(sc);
+
mutex_unlock(&sc->mutex);
}
@@ -2276,6 +2308,12 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
mutex_lock(&sc->mutex);
cancel_delayed_work_sync(&sc->tx_complete_work);
+ if (ah->ah_flags & AH_UNPLUGGED) {
+ ath_dbg(common, ATH_DBG_ANY, "Device has been unplugged!\n");
+ mutex_unlock(&sc->mutex);
+ return;
+ }
+
if (sc->sc_flags & SC_OP_INVALID) {
ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
mutex_unlock(&sc->mutex);
@@ -2309,8 +2347,10 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
spin_lock_bh(&sc->sc_pcu_lock);
drain_txq = ath_drain_all_txq(sc, false);
spin_unlock_bh(&sc->sc_pcu_lock);
+
if (!drain_txq)
ath_reset(sc, false);
+
ath9k_ps_restore(sc);
ieee80211_wake_queues(hw);
@@ -2334,7 +2374,7 @@ static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw)
return false;
}
-int ath9k_tx_last_beacon(struct ieee80211_hw *hw)
+static int ath9k_tx_last_beacon(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
@@ -2373,6 +2413,73 @@ skip:
return sc->beacon.tx_last;
}
+static int ath9k_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath9k_mib_stats *mib_stats = &ah->ah_mibStats;
+
+ stats->dot11ACKFailureCount = mib_stats->ackrcv_bad;
+ stats->dot11RTSFailureCount = mib_stats->rts_bad;
+ stats->dot11FCSErrorCount = mib_stats->fcs_bad;
+ stats->dot11RTSSuccessCount = mib_stats->rts_good;
+ return 0;
+}
+
+static u32 fill_chainmask(u32 cap, u32 new)
+{
+ u32 filled = 0;
+ int i;
+
+ for (i = 0; cap && new; i++, cap >>= 1) {
+ if (!(cap & BIT(0)))
+ continue;
+
+ if (new & BIT(0))
+ filled |= BIT(i);
+
+ new >>= 1;
+ }
+
+ return filled;
+}
+
+static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_hw *ah = sc->sc_ah;
+
+ if (!rx_ant || !tx_ant)
+ return -EINVAL;
+
+ sc->ant_rx = rx_ant;
+ sc->ant_tx = tx_ant;
+
+ if (ah->caps.rx_chainmask == 1)
+ return 0;
+
+ /* AR9100 runs into calibration issues if not all rx chains are enabled */
+ if (AR_SREV_9100(ah))
+ ah->rxchainmask = 0x7;
+ else
+ ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant);
+
+ ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant);
+ ath9k_reload_chainmask_settings(sc);
+
+ return 0;
+}
+
+static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
+{
+ struct ath_softc *sc = hw->priv;
+
+ *tx_ant = sc->ant_tx;
+ *rx_ant = sc->ant_rx;
+ return 0;
+}
+
struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
@@ -2397,5 +2504,8 @@ struct ieee80211_ops ath9k_ops = {
.set_coverage_class = ath9k_set_coverage_class,
.flush = ath9k_flush,
.tx_frames_pending = ath9k_tx_frames_pending,
- .tx_last_beacon = ath9k_tx_last_beacon,
+ .tx_last_beacon = ath9k_tx_last_beacon,
+ .get_stats = ath9k_get_stats,
+ .set_antenna = ath9k_set_antenna,
+ .get_antenna = ath9k_get_antenna,
};
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 3bad0b2cf9a3..2dcdf63cb390 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -16,7 +16,9 @@
#include <linux/nl80211.h>
#include <linux/pci.h>
+#include <linux/pci-aspm.h>
#include <linux/ath9k_platform.h>
+#include <linux/module.h>
#include "ath9k.h"
static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
@@ -31,9 +33,12 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
{ PCI_VDEVICE(ATHEROS, 0x002E) }, /* PCI-E */
{ PCI_VDEVICE(ATHEROS, 0x0030) }, /* PCI-E AR9300 */
{ PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */
+ { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */
+ { PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E AR9462 */
{ 0 }
};
+
/* return bus cachesize in 4B word units */
static void ath_pci_read_cachesize(struct ath_common *common, int *csz)
{
@@ -87,40 +92,68 @@ static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data)
return true;
}
-/*
- * Bluetooth coexistance requires disabling ASPM.
- */
-static void ath_pci_bt_coex_prep(struct ath_common *common)
+static void ath_pci_extn_synch_enable(struct ath_common *common)
{
struct ath_softc *sc = (struct ath_softc *) common->priv;
struct pci_dev *pdev = to_pci_dev(sc->dev);
- u8 aspm;
-
- if (!pci_is_pcie(pdev))
- return;
+ u8 lnkctl;
- pci_read_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, &aspm);
- aspm &= ~(ATH_PCIE_CAP_LINK_L0S | ATH_PCIE_CAP_LINK_L1);
- pci_write_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, aspm);
+ pci_read_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, &lnkctl);
+ lnkctl |= PCI_EXP_LNKCTL_ES;
+ pci_write_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, lnkctl);
}
-static void ath_pci_extn_synch_enable(struct ath_common *common)
+/* Need to be called after we discover btcoex capabilities */
+static void ath_pci_aspm_init(struct ath_common *common)
{
struct ath_softc *sc = (struct ath_softc *) common->priv;
+ struct ath_hw *ah = sc->sc_ah;
struct pci_dev *pdev = to_pci_dev(sc->dev);
- u8 lnkctl;
+ struct pci_dev *parent;
+ int pos;
+ u8 aspm;
- pci_read_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, &lnkctl);
- lnkctl |= PCI_EXP_LNKCTL_ES;
- pci_write_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, lnkctl);
+ pos = pci_pcie_cap(pdev);
+ if (!pos)
+ return;
+
+ parent = pdev->bus->self;
+ if (!parent)
+ return;
+
+ if (ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) {
+ /* Bluetooth coexistance requires disabling ASPM. */
+ pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &aspm);
+ aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
+ pci_write_config_byte(pdev, pos + PCI_EXP_LNKCTL, aspm);
+
+ /*
+ * Both upstream and downstream PCIe components should
+ * have the same ASPM settings.
+ */
+ pos = pci_pcie_cap(parent);
+ pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm);
+ aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
+ pci_write_config_byte(parent, pos + PCI_EXP_LNKCTL, aspm);
+
+ return;
+ }
+
+ pos = pci_pcie_cap(parent);
+ pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm);
+ if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) {
+ ah->aspm_enabled = true;
+ /* Initialize PCIe PM and SERDES registers. */
+ ath9k_hw_configpcipowersave(ah, false);
+ }
}
static const struct ath_bus_ops ath_pci_bus_ops = {
.ath_bus_type = ATH_PCI,
.read_cachesize = ath_pci_read_cachesize,
.eeprom_read = ath_pci_eeprom_read,
- .bt_coex_prep = ath_pci_bt_coex_prep,
.extn_synch_en = ath_pci_extn_synch_enable,
+ .aspm_init = ath_pci_aspm_init,
};
static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -129,7 +162,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct ath_softc *sc;
struct ieee80211_hw *hw;
u8 csz;
- u16 subsysid;
u32 val;
int ret = 0;
char hw_name[64];
@@ -223,8 +255,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sc->irq = pdev->irq;
- pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsysid);
- ret = ath9k_init_device(id->device, sc, subsysid, &ath_pci_bus_ops);
+ ret = ath9k_init_device(id->device, sc, &ath_pci_bus_ops);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize device\n");
goto err_init;
@@ -303,17 +334,17 @@ static int ath_pci_resume(struct device *device)
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+ ath9k_ps_wakeup(sc);
/* Enable LED */
ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
+ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
/*
* Reset key cache to sane defaults (all entries cleared) instead of
* semi-random values after suspend/resume.
*/
- ath9k_ps_wakeup(sc);
- ath9k_init_crypto(sc);
+ ath9k_cmn_init_crypto(sc->sc_ah);
ath9k_ps_restore(sc);
sc->ps_idle = true;
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index ba7f36ab0a74..888abc2be3a5 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -16,6 +16,7 @@
*/
#include <linux/slab.h>
+#include <linux/export.h>
#include "ath9k.h"
@@ -379,7 +380,30 @@ static const struct ath_rate_table ar5416_11g_ratetable = {
};
static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table,
- struct ieee80211_tx_rate *rate);
+ struct ieee80211_tx_rate *rate)
+{
+ int rix = 0, i = 0;
+ static const int mcs_rix_off[] = { 7, 15, 20, 21, 22, 23 };
+
+ if (!(rate->flags & IEEE80211_TX_RC_MCS))
+ return rate->idx;
+
+ while (i < ARRAY_SIZE(mcs_rix_off) && rate->idx > mcs_rix_off[i]) {
+ rix++; i++;
+ }
+
+ rix += rate->idx + rate_table->mcs_start;
+
+ if ((rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
+ (rate->flags & IEEE80211_TX_RC_SHORT_GI))
+ rix = rate_table->info[rix].ht_index;
+ else if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
+ rix = rate_table->info[rix].sgi_index;
+ else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ rix = rate_table->info[rix].cw40index;
+
+ return rix;
+}
static void ath_rc_sort_validrates(const struct ath_rate_table *rate_table,
struct ath_rate_priv *ath_rc_priv)
@@ -533,7 +557,7 @@ static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv,
[valid_rate_count] = j;
ath_rc_priv->valid_phy_ratecnt[phy] += 1;
ath_rc_set_valid_rate_idx(ath_rc_priv, j, 1);
- hi = A_MAX(hi, j);
+ hi = max(hi, j);
}
}
}
@@ -569,7 +593,7 @@ static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv,
[ath_rc_priv->valid_phy_ratecnt[phy]] = j;
ath_rc_priv->valid_phy_ratecnt[phy] += 1;
ath_rc_set_valid_rate_idx(ath_rc_priv, j, 1);
- hi = A_MAX(hi, j);
+ hi = max(hi, j);
}
}
@@ -580,7 +604,8 @@ static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv,
static u8 ath_rc_get_highest_rix(struct ath_softc *sc,
struct ath_rate_priv *ath_rc_priv,
const struct ath_rate_table *rate_table,
- int *is_probing)
+ int *is_probing,
+ bool legacy)
{
u32 best_thruput, this_thruput, now_msec;
u8 rate, next_rate, best_rate, maxindex, minindex;
@@ -601,6 +626,8 @@ static u8 ath_rc_get_highest_rix(struct ath_softc *sc,
u8 per_thres;
rate = ath_rc_priv->valid_rate_index[index];
+ if (legacy && !(rate_table->info[rate].rate_flags & RC_LEGACY))
+ continue;
if (rate > ath_rc_priv->rate_max_phy)
continue;
@@ -744,7 +771,7 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
struct ieee80211_tx_rate *rates = tx_info->control.rates;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
__le16 fc = hdr->frame_control;
- u8 try_per_rate, i = 0, rix;
+ u8 try_per_rate, i = 0, rix, high_rix;
int is_probe = 0;
if (rate_control_send_low(sta, priv_sta, txrc))
@@ -763,7 +790,9 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
try_per_rate = 4;
rate_table = ath_rc_priv->rate_table;
- rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe);
+ rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table,
+ &is_probe, false);
+ high_rix = rix;
/*
* If we're in HT mode and both us and our peer supports LDPC.
@@ -799,10 +828,7 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
}
/* Fill in the other rates for multirate retry */
- for ( ; i < 4; i++) {
- /* Use twice the number of tries for the last MRR segment. */
- if (i + 1 == 4)
- try_per_rate = 8;
+ for ( ; i < 3; i++) {
ath_rc_get_lower_rix(rate_table, ath_rc_priv, rix, &rix);
/* All other rates in the series have RTS enabled */
@@ -810,6 +836,24 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
try_per_rate, rix, 1);
}
+ /* Use twice the number of tries for the last MRR segment. */
+ try_per_rate = 8;
+
+ /*
+ * Use a legacy rate as last retry to ensure that the frame
+ * is tried in both MCS and legacy rates.
+ */
+ if ((rates[2].flags & IEEE80211_TX_RC_MCS) &&
+ (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU) ||
+ (ath_rc_priv->per[high_rix] > 45)))
+ rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table,
+ &is_probe, true);
+ else
+ ath_rc_get_lower_rix(rate_table, ath_rc_priv, rix, &rix);
+
+ /* All other rates in the series have RTS enabled */
+ ath_rc_rate_set_series(rate_table, &rates[i], txrc,
+ try_per_rate, rix, 1);
/*
* NB:Change rate series to enable aggregation when operating
* at lower MCS rates. When first rate in series is MCS2
@@ -1080,31 +1124,6 @@ static void ath_rc_update_ht(struct ath_softc *sc,
}
-static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table,
- struct ieee80211_tx_rate *rate)
-{
- int rix = 0, i = 0;
- static const int mcs_rix_off[] = { 7, 15, 20, 21, 22, 23 };
-
- if (!(rate->flags & IEEE80211_TX_RC_MCS))
- return rate->idx;
-
- while (i < ARRAY_SIZE(mcs_rix_off) && rate->idx > mcs_rix_off[i]) {
- rix++; i++;
- }
-
- rix += rate->idx + rate_table->mcs_start;
-
- if ((rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
- (rate->flags & IEEE80211_TX_RC_SHORT_GI))
- rix = rate_table->info[rix].ht_index;
- else if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
- rix = rate_table->info[rix].sgi_index;
- else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
- rix = rate_table->info[rix].cw40index;
-
- return rix;
-}
static void ath_rc_tx_status(struct ath_softc *sc,
struct ath_rate_priv *ath_rc_priv,
@@ -1228,7 +1247,7 @@ static void ath_rc_init(struct ath_softc *sc,
ht_mcs,
ath_rc_priv->ht_cap);
}
- hi = A_MAX(hi, hthi);
+ hi = max(hi, hthi);
}
ath_rc_priv->rate_table_size = hi + 1;
@@ -1344,12 +1363,6 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED)
return;
- if (!(tx_info->flags & IEEE80211_TX_STAT_AMPDU)) {
- tx_info->status.ampdu_ack_len =
- (tx_info->flags & IEEE80211_TX_STAT_ACK ? 1 : 0);
- tx_info->status.ampdu_len = 1;
- }
-
if (!(tx_info->flags & IEEE80211_TX_STAT_ACK))
tx_status = 1;
@@ -1486,7 +1499,7 @@ static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
if (rc->rate_table == NULL)
return 0;
- max = 80 + rc->rate_table->rate_cnt * 1024 + 1;
+ max = 80 + rc->rate_table_size * 1024 + 1;
buf = kmalloc(max, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
@@ -1496,7 +1509,7 @@ static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
"HT", "MCS", "Rate",
"Success", "Retries", "XRetries", "PER");
- for (i = 0; i < rc->rate_table->rate_cnt; i++) {
+ for (i = 0; i < rc->rate_table_size; i++) {
u32 ratekbps = rc->rate_table->info[i].ratekbps;
struct ath_rc_stats *stats = &rc->rcstats[i];
char mcs[5];
diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h
index c3d850207bee..b7a4bcd3eec7 100644
--- a/drivers/net/wireless/ath/ath9k/rc.h
+++ b/drivers/net/wireless/ath/ath9k/rc.h
@@ -221,12 +221,6 @@ struct ath_rate_priv {
struct ath_rc_stats rcstats[RATE_TABLE_SIZE];
};
-enum ath9k_internal_frame_type {
- ATH9K_IFT_NOT_INTERNAL,
- ATH9K_IFT_PAUSE,
- ATH9K_IFT_UNPAUSE
-};
-
#ifdef CONFIG_ATH9K_RATE_CONTROL
int ath_rate_control_register(void);
void ath_rate_control_unregister(void);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 07e35e59c9e3..67b862cdae6d 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/dma-mapping.h>
#include "ath9k.h"
#include "ar9003_mac.h"
@@ -39,6 +40,7 @@ static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
result = true;
break;
case 1:
+ case 2:
if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
(curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
(alt_rssi_avg >= (main_rssi_avg - 5))) ||
@@ -203,14 +205,22 @@ static void ath_rx_remove_buffer(struct ath_softc *sc,
static void ath_rx_edma_cleanup(struct ath_softc *sc)
{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath_buf *bf;
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP);
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP);
list_for_each_entry(bf, &sc->rx.rxbuf, list) {
- if (bf->bf_mpdu)
+ if (bf->bf_mpdu) {
+ dma_unmap_single(sc->dev, bf->bf_buf_addr,
+ common->rx_bufsize,
+ DMA_BIDIRECTIONAL);
dev_kfree_skb_any(bf->bf_mpdu);
+ bf->bf_buf_addr = 0;
+ bf->bf_mpdu = NULL;
+ }
}
INIT_LIST_HEAD(&sc->rx.rxbuf);
@@ -423,12 +433,9 @@ void ath_rx_cleanup(struct ath_softc *sc)
u32 ath_calcrxfilter(struct ath_softc *sc)
{
-#define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR)
-
u32 rfilt;
- rfilt = (ath9k_hw_getrxfilter(sc->sc_ah) & RX_FILTER_PRESERVE)
- | ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
+ rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
| ATH9K_RX_FILTER_MCAST;
if (sc->rx.rxfilter & FIF_PROBE_REQ)
@@ -576,22 +583,11 @@ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
{
- struct ieee80211_mgmt *mgmt;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
if (skb->len < 24 + 8 + 2 + 2)
return;
- mgmt = (struct ieee80211_mgmt *)skb->data;
- if (memcmp(common->curbssid, mgmt->bssid, ETH_ALEN) != 0) {
- /* TODO: This doesn't work well if you have stations
- * associated to two different APs because curbssid
- * is just the last AP that any of the stations associated
- * with.
- */
- return; /* not from our current AP */
- }
-
sc->ps_flags &= ~PS_WAIT_FOR_BEACON;
if (sc->ps_flags & PS_BEACON_SYNC) {
@@ -599,7 +595,6 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
ath_dbg(common, ATH_DBG_PS,
"Reconfigure Beacon timers based on timestamp from the AP\n");
ath_set_beacon(sc);
- sc->ps_flags &= ~PS_TSFOOR_SYNC;
}
if (ath_beacon_dtim_pending_cab(skb)) {
@@ -628,7 +623,7 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
}
}
-static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb)
+static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon)
{
struct ieee80211_hdr *hdr;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -637,7 +632,7 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb)
/* Process Beacon and CAB receive in PS state */
if (((sc->ps_flags & PS_WAIT_FOR_BEACON) || ath9k_check_auto_sleep(sc))
- && ieee80211_is_beacon(hdr->frame_control))
+ && mybeacon)
ath_rx_ps_beacon(sc, skb);
else if ((sc->ps_flags & PS_WAIT_FOR_CAB) &&
(ieee80211_is_data(hdr->frame_control) ||
@@ -760,7 +755,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
* on. All this is necessary because of our use of
* a self-linked list to avoid rx overruns.
*/
- ret = ath9k_hw_rxprocdesc(ah, ds, rs, 0);
+ ret = ath9k_hw_rxprocdesc(ah, ds, rs);
if (ret == -EINPROGRESS) {
struct ath_rx_status trs;
struct ath_buf *tbf;
@@ -786,7 +781,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
*/
tds = tbf->bf_desc;
- ret = ath9k_hw_rxprocdesc(ah, tds, &trs, 0);
+ ret = ath9k_hw_rxprocdesc(ah, tds, &trs);
if (ret == -EINPROGRESS)
return NULL;
}
@@ -813,16 +808,22 @@ static bool ath9k_rx_accept(struct ath_common *common,
struct ath_rx_status *rx_stats,
bool *decrypt_error)
{
-#define is_mc_or_valid_tkip_keyix ((is_mc || \
- (rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID && \
- test_bit(rx_stats->rs_keyix, common->tkip_keymap))))
-
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
+ bool is_mc, is_valid_tkip, strip_mic, mic_error;
struct ath_hw *ah = common->ah;
__le16 fc;
u8 rx_status_len = ah->caps.rx_status_len;
fc = hdr->frame_control;
+ is_mc = !!is_multicast_ether_addr(hdr->addr1);
+ is_valid_tkip = rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID &&
+ test_bit(rx_stats->rs_keyix, common->tkip_keymap);
+ strip_mic = is_valid_tkip && ieee80211_is_data(fc) &&
+ !(rx_stats->rs_status &
+ (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC |
+ ATH9K_RXERR_KEYMISS));
+
if (!rx_stats->rs_datalen)
return false;
/*
@@ -837,6 +838,11 @@ static bool ath9k_rx_accept(struct ath_common *common,
if (rx_stats->rs_more)
return true;
+ mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) &&
+ !ieee80211_has_morefrags(fc) &&
+ !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) &&
+ (rx_stats->rs_status & ATH9K_RXERR_MIC);
+
/*
* The rx_stats->rs_status will not be set until the end of the
* chained descriptors so it can be ignored if rs_more is set. The
@@ -844,47 +850,47 @@ static bool ath9k_rx_accept(struct ath_common *common,
* descriptors.
*/
if (rx_stats->rs_status != 0) {
- if (rx_stats->rs_status & ATH9K_RXERR_CRC)
+ u8 status_mask;
+
+ if (rx_stats->rs_status & ATH9K_RXERR_CRC) {
rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
+ mic_error = false;
+ }
if (rx_stats->rs_status & ATH9K_RXERR_PHY)
return false;
- if (rx_stats->rs_status & ATH9K_RXERR_DECRYPT) {
+ if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) ||
+ (!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) {
*decrypt_error = true;
- } else if (rx_stats->rs_status & ATH9K_RXERR_MIC) {
- bool is_mc;
- /*
- * The MIC error bit is only valid if the frame
- * is not a control frame or fragment, and it was
- * decrypted using a valid TKIP key.
- */
- is_mc = !!is_multicast_ether_addr(hdr->addr1);
-
- if (!ieee80211_is_ctl(fc) &&
- !ieee80211_has_morefrags(fc) &&
- !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) &&
- is_mc_or_valid_tkip_keyix)
- rxs->flag |= RX_FLAG_MMIC_ERROR;
- else
- rx_stats->rs_status &= ~ATH9K_RXERR_MIC;
+ mic_error = false;
}
+
/*
* Reject error frames with the exception of
* decryption and MIC failures. For monitor mode,
* we also ignore the CRC error.
*/
- if (ah->is_monitoring) {
- if (rx_stats->rs_status &
- ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
- ATH9K_RXERR_CRC))
- return false;
- } else {
- if (rx_stats->rs_status &
- ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) {
- return false;
- }
- }
+ status_mask = ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
+ ATH9K_RXERR_KEYMISS;
+
+ if (ah->is_monitoring && (sc->rx.rxfilter & FIF_FCSFAIL))
+ status_mask |= ATH9K_RXERR_CRC;
+
+ if (rx_stats->rs_status & ~status_mask)
+ return false;
}
+
+ /*
+ * For unicast frames the MIC error bit can have false positives,
+ * so all MIC error reports need to be validated in software.
+ * False negatives are not common, so skip software verification
+ * if the hardware considers the MIC valid.
+ */
+ if (strip_mic)
+ rxs->flag |= RX_FLAG_MMIC_STRIPPED;
+ else if (is_mc && mic_error)
+ rxs->flag |= RX_FLAG_MMIC_ERROR;
+
return true;
}
@@ -927,7 +933,7 @@ static int ath9k_process_rate(struct ath_common *common,
* No valid hardware bitrate found -- we should not get here
* because hardware has already validated this frame as OK.
*/
- ath_dbg(common, ATH_DBG_XMIT,
+ ath_dbg(common, ATH_DBG_ANY,
"unsupported hw bitrate detected 0x%02x using 1 Mbit\n",
rx_stats->rs_rate);
@@ -942,22 +948,11 @@ static void ath9k_process_rssi(struct ath_common *common,
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = common->ah;
int last_rssi;
- __le16 fc;
-
- if ((ah->opmode != NL80211_IFTYPE_STATION) &&
- (ah->opmode != NL80211_IFTYPE_ADHOC))
- return;
- fc = hdr->frame_control;
- if (!ieee80211_is_beacon(fc) ||
- compare_ether_addr(hdr->addr3, common->curbssid)) {
- /* TODO: This doesn't work well if you have stations
- * associated to two different APs because curbssid
- * is just the last AP that any of the stations associated
- * with.
- */
+ if (!rx_stats->is_mybeacon ||
+ ((ah->opmode != NL80211_IFTYPE_STATION) &&
+ (ah->opmode != NL80211_IFTYPE_ADHOC)))
return;
- }
if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && !rx_stats->rs_moreaggr)
ATH_RSSI_LPF(sc->last_rssi, rx_stats->rs_rssi);
@@ -985,6 +980,8 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common,
struct ieee80211_rx_status *rx_status,
bool *decrypt_error)
{
+ struct ath_hw *ah = common->ah;
+
memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
/*
@@ -1005,7 +1002,7 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common,
rx_status->band = hw->conf.channel->band;
rx_status->freq = hw->conf.channel->center_freq;
- rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + rx_stats->rs_rssi;
+ rx_status->signal = ah->noise + rx_stats->rs_rssi;
rx_status->antenna = rx_stats->rs_antenna;
rx_status->flag |= RX_FLAG_MACTIME_MPDU;
@@ -1075,39 +1072,39 @@ static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
antcomb->rssi_lna1 = main_rssi_avg;
switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
- case (0x10): /* LNA2 A-B */
+ case 0x10: /* LNA2 A-B */
antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
antcomb->first_quick_scan_conf =
ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
break;
- case (0x20): /* LNA1 A-B */
+ case 0x20: /* LNA1 A-B */
antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
antcomb->first_quick_scan_conf =
ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
break;
- case (0x21): /* LNA1 LNA2 */
+ case 0x21: /* LNA1 LNA2 */
antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
antcomb->first_quick_scan_conf =
ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
antcomb->second_quick_scan_conf =
ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
break;
- case (0x12): /* LNA2 LNA1 */
+ case 0x12: /* LNA2 LNA1 */
antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
antcomb->first_quick_scan_conf =
ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
antcomb->second_quick_scan_conf =
ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
break;
- case (0x13): /* LNA2 A+B */
+ case 0x13: /* LNA2 A+B */
antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
antcomb->first_quick_scan_conf =
ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
break;
- case (0x23): /* LNA1 A+B */
+ case 0x23: /* LNA1 A+B */
antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
antcomb->first_quick_scan_conf =
ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
@@ -1324,65 +1321,148 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
/* Adjust the fast_div_bias based on main and alt lna conf */
switch ((ant_conf->main_lna_conf << 4) |
ant_conf->alt_lna_conf) {
- case (0x01): /* A-B LNA2 */
+ case 0x01: /* A-B LNA2 */
ant_conf->fast_div_bias = 0x3b;
break;
- case (0x02): /* A-B LNA1 */
+ case 0x02: /* A-B LNA1 */
ant_conf->fast_div_bias = 0x3d;
break;
- case (0x03): /* A-B A+B */
+ case 0x03: /* A-B A+B */
ant_conf->fast_div_bias = 0x1;
break;
- case (0x10): /* LNA2 A-B */
+ case 0x10: /* LNA2 A-B */
ant_conf->fast_div_bias = 0x7;
break;
- case (0x12): /* LNA2 LNA1 */
+ case 0x12: /* LNA2 LNA1 */
ant_conf->fast_div_bias = 0x2;
break;
- case (0x13): /* LNA2 A+B */
+ case 0x13: /* LNA2 A+B */
ant_conf->fast_div_bias = 0x7;
break;
- case (0x20): /* LNA1 A-B */
+ case 0x20: /* LNA1 A-B */
ant_conf->fast_div_bias = 0x6;
break;
- case (0x21): /* LNA1 LNA2 */
+ case 0x21: /* LNA1 LNA2 */
ant_conf->fast_div_bias = 0x0;
break;
- case (0x23): /* LNA1 A+B */
+ case 0x23: /* LNA1 A+B */
ant_conf->fast_div_bias = 0x6;
break;
- case (0x30): /* A+B A-B */
+ case 0x30: /* A+B A-B */
ant_conf->fast_div_bias = 0x1;
break;
- case (0x31): /* A+B LNA2 */
+ case 0x31: /* A+B LNA2 */
ant_conf->fast_div_bias = 0x3b;
break;
- case (0x32): /* A+B LNA1 */
+ case 0x32: /* A+B LNA1 */
ant_conf->fast_div_bias = 0x3d;
break;
default:
break;
}
+ } else if (ant_conf->div_group == 1) {
+ /* Adjust the fast_div_bias based on main and alt_lna_conf */
+ switch ((ant_conf->main_lna_conf << 4) |
+ ant_conf->alt_lna_conf) {
+ case 0x01: /* A-B LNA2 */
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x02: /* A-B LNA1 */
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x03: /* A-B A+B */
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x10: /* LNA2 A-B */
+ if (!(antcomb->scan) &&
+ (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+ ant_conf->fast_div_bias = 0x3f;
+ else
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x12: /* LNA2 LNA1 */
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x13: /* LNA2 A+B */
+ if (!(antcomb->scan) &&
+ (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+ ant_conf->fast_div_bias = 0x3f;
+ else
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x20: /* LNA1 A-B */
+ if (!(antcomb->scan) &&
+ (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+ ant_conf->fast_div_bias = 0x3f;
+ else
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x21: /* LNA1 LNA2 */
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x23: /* LNA1 A+B */
+ if (!(antcomb->scan) &&
+ (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+ ant_conf->fast_div_bias = 0x3f;
+ else
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x30: /* A+B A-B */
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x31: /* A+B LNA2 */
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ case 0x32: /* A+B LNA1 */
+ ant_conf->fast_div_bias = 0x1;
+ ant_conf->main_gaintb = 0;
+ ant_conf->alt_gaintb = 0;
+ break;
+ default:
+ break;
+ }
} else if (ant_conf->div_group == 2) {
/* Adjust the fast_div_bias based on main and alt_lna_conf */
switch ((ant_conf->main_lna_conf << 4) |
ant_conf->alt_lna_conf) {
- case (0x01): /* A-B LNA2 */
+ case 0x01: /* A-B LNA2 */
ant_conf->fast_div_bias = 0x1;
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x02): /* A-B LNA1 */
+ case 0x02: /* A-B LNA1 */
ant_conf->fast_div_bias = 0x1;
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x03): /* A-B A+B */
+ case 0x03: /* A-B A+B */
ant_conf->fast_div_bias = 0x1;
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x10): /* LNA2 A-B */
+ case 0x10: /* LNA2 A-B */
if (!(antcomb->scan) &&
(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
ant_conf->fast_div_bias = 0x1;
@@ -1391,12 +1471,12 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x12): /* LNA2 LNA1 */
+ case 0x12: /* LNA2 LNA1 */
ant_conf->fast_div_bias = 0x1;
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x13): /* LNA2 A+B */
+ case 0x13: /* LNA2 A+B */
if (!(antcomb->scan) &&
(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
ant_conf->fast_div_bias = 0x1;
@@ -1405,7 +1485,7 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x20): /* LNA1 A-B */
+ case 0x20: /* LNA1 A-B */
if (!(antcomb->scan) &&
(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
ant_conf->fast_div_bias = 0x1;
@@ -1414,12 +1494,12 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x21): /* LNA1 LNA2 */
+ case 0x21: /* LNA1 LNA2 */
ant_conf->fast_div_bias = 0x1;
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x23): /* LNA1 A+B */
+ case 0x23: /* LNA1 A+B */
if (!(antcomb->scan) &&
(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
ant_conf->fast_div_bias = 0x1;
@@ -1428,17 +1508,17 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x30): /* A+B A-B */
+ case 0x30: /* A+B A-B */
ant_conf->fast_div_bias = 0x1;
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x31): /* A+B LNA2 */
+ case 0x31: /* A+B LNA2 */
ant_conf->fast_div_bias = 0x1;
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
break;
- case (0x32): /* A+B LNA1 */
+ case 0x32: /* A+B LNA1 */
ant_conf->fast_div_bias = 0x1;
ant_conf->main_gaintb = 0;
ant_conf->alt_gaintb = 0;
@@ -1446,9 +1526,7 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
default:
break;
}
-
}
-
}
/* Antenna diversity and combining */
@@ -1692,11 +1770,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
struct ieee80211_rx_status *rxs;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- /*
- * The hw can technically differ from common->hw when using ath9k
- * virtual wiphy so to account for that we iterate over the active
- * wiphys and find the appropriate wiphy and therefore hw.
- */
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_hdr *hdr;
int retval;
@@ -1750,6 +1823,11 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
hdr = (struct ieee80211_hdr *) (hdr_skb->data + rx_status_len);
rxs = IEEE80211_SKB_RXCB(hdr_skb);
+ if (ieee80211_is_beacon(hdr->frame_control) &&
+ !compare_ether_addr(hdr->addr3, common->curbssid))
+ rs.is_mybeacon = true;
+ else
+ rs.is_mybeacon = false;
ath_debug_stat_rx(sc, &rs);
@@ -1757,7 +1835,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
* If we're asked to flush receive queue, directly
* chain it back at the queue without processing it.
*/
- if (flush)
+ if (sc->sc_flags & SC_OP_RXFLUSH)
goto requeue_drop_frag;
retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs,
@@ -1856,16 +1934,19 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
sc->rx.rxotherant = 0;
}
+ if (rxs->flag & RX_FLAG_MMIC_STRIPPED)
+ skb_trim(skb, skb->len - 8);
+
spin_lock_irqsave(&sc->sc_pm_lock, flags);
if ((sc->ps_flags & (PS_WAIT_FOR_BEACON |
- PS_WAIT_FOR_CAB |
- PS_WAIT_FOR_PSPOLL_DATA)) ||
- ath9k_check_auto_sleep(sc))
- ath_rx_ps(sc, skb);
+ PS_WAIT_FOR_CAB |
+ PS_WAIT_FOR_PSPOLL_DATA)) ||
+ ath9k_check_auto_sleep(sc))
+ ath_rx_ps(sc, skb, rs.is_mybeacon);
spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
- if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
+ if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3)
ath_ant_comb_scan(sc, &rs);
ieee80211_rx(hw, skb);
@@ -1882,11 +1963,17 @@ requeue:
} else {
list_move_tail(&bf->list, &sc->rx.rxbuf);
ath_rx_buf_link(sc, bf);
- ath9k_hw_rxena(ah);
+ if (!flush)
+ ath9k_hw_rxena(ah);
}
} while (1);
spin_unlock_bh(&sc->rx.rxbuflock);
+ if (!(ah->imask & ATH9K_INT_RXEOL)) {
+ ah->imask |= (ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
+ ath9k_hw_set_interrupts(ah);
+ }
+
return 0;
}
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index c18ee9921fb1..8fcb7e9e8399 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -600,7 +600,6 @@
#define AR_D_GBL_IFS_SIFS 0x1030
#define AR_D_GBL_IFS_SIFS_M 0x0000FFFF
-#define AR_D_GBL_IFS_SIFS_ASYNC_FIFO_DUR 0x000003AB
#define AR_D_GBL_IFS_SIFS_RESV0 0xFFFFFFFF
#define AR_D_TXBLK_BASE 0x1038
@@ -616,12 +615,11 @@
#define AR_D_GBL_IFS_SLOT 0x1070
#define AR_D_GBL_IFS_SLOT_M 0x0000FFFF
#define AR_D_GBL_IFS_SLOT_RESV0 0xFFFF0000
-#define AR_D_GBL_IFS_SLOT_ASYNC_FIFO_DUR 0x00000420
#define AR_D_GBL_IFS_EIFS 0x10b0
#define AR_D_GBL_IFS_EIFS_M 0x0000FFFF
#define AR_D_GBL_IFS_EIFS_RESV0 0xFFFF0000
-#define AR_D_GBL_IFS_EIFS_ASYNC_FIFO_DUR 0x0000A5EB
+#define AR_D_GBL_IFS_EIFS_ASYNC_FIFO 363
#define AR_D_GBL_IFS_MISC 0x10f0
#define AR_D_GBL_IFS_MISC_LFSR_SLICE_SEL 0x00000007
@@ -788,18 +786,23 @@
#define AR_SREV_REVISION_9271_11 1
#define AR_SREV_VERSION_9300 0x1c0
#define AR_SREV_REVISION_9300_20 2 /* 2.0 and 2.1 */
+#define AR_SREV_VERSION_9330 0x200
+#define AR_SREV_REVISION_9330_10 0
+#define AR_SREV_REVISION_9330_11 1
+#define AR_SREV_REVISION_9330_12 2
#define AR_SREV_VERSION_9485 0x240
#define AR_SREV_REVISION_9485_10 0
#define AR_SREV_REVISION_9485_11 1
#define AR_SREV_VERSION_9340 0x300
+#define AR_SREV_VERSION_9580 0x1C0
+#define AR_SREV_REVISION_9580_10 4 /* AR9580 1.0 */
+#define AR_SREV_VERSION_9462 0x280
+#define AR_SREV_REVISION_9462_10 0
+#define AR_SREV_REVISION_9462_20 2
#define AR_SREV_5416(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \
((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE))
-#define AR_SREV_5416_20_OR_LATER(_ah) \
- (((AR_SREV_5416(_ah)) && \
- ((_ah)->hw_version.macRev >= AR_SREV_REVISION_5416_20)) || \
- ((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9100))
#define AR_SREV_5416_22_OR_LATER(_ah) \
(((AR_SREV_5416(_ah)) && \
((_ah)->hw_version.macRev >= AR_SREV_REVISION_5416_22)) || \
@@ -862,6 +865,18 @@
#define AR_SREV_9300_20_OR_LATER(_ah) \
((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9300)
+#define AR_SREV_9330(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9330))
+#define AR_SREV_9330_10(_ah) \
+ (AR_SREV_9330((_ah)) && \
+ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9330_10))
+#define AR_SREV_9330_11(_ah) \
+ (AR_SREV_9330((_ah)) && \
+ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9330_11))
+#define AR_SREV_9330_12(_ah) \
+ (AR_SREV_9330((_ah)) && \
+ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9330_12))
+
#define AR_SREV_9485(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485))
#define AR_SREV_9485_10(_ah) \
@@ -880,6 +895,33 @@
(AR_SREV_9285_12_OR_LATER(_ah) && \
((REG_READ(_ah, AR_AN_SYNTH9) & 0x7) == 0x1))
+#define AR_SREV_9462(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462))
+
+#define AR_SREV_9462_10(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_10))
+
+#define AR_SREV_9462_20(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_20))
+
+#define AR_SREV_9462_20_OR_LATER(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_20))
+
+#define AR_SREV_9580(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \
+ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9580_10))
+
+#define AR_SREV_9580_10(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \
+ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9580_10))
+
+/* NOTE: When adding chips newer than Peacock, add chip check here */
+#define AR_SREV_9580_10_OR_LATER(_ah) \
+ (AR_SREV_9580(_ah))
+
enum ath_usb_dev {
AR9280_USB = 1, /* AR7010 + AR9280, UB94 */
AR9287_USB = 2, /* AR7010 + AR9287, UB95 */
@@ -1104,7 +1146,7 @@ enum {
#define AR_INTR_PRIO_ASYNC_ENABLE (AR_SREV_9340(ah) ? 0x4094 : 0x40d4)
#define AR_ENT_OTP 0x40d8
#define AR_ENT_OTP_CHAIN2_DISABLE 0x00020000
-#define AR_ENT_OTP_MPSD 0x00800000
+#define AR_ENT_OTP_MIN_PKT_SIZE_DISABLE 0x00800000
#define AR_CH0_BB_DPLL1 0x16180
#define AR_CH0_BB_DPLL1_REFDIV 0xF8000000
@@ -1461,7 +1503,6 @@ enum {
#define AR_TIME_OUT_ACK_S 0
#define AR_TIME_OUT_CTS 0x3FFF0000
#define AR_TIME_OUT_CTS_S 16
-#define AR_TIME_OUT_ACK_CTS_ASYNC_FIFO_DUR 0x16001D56
#define AR_RSSI_THR 0x8018
#define AR_RSSI_THR_MASK 0x000000FF
@@ -1477,7 +1518,7 @@ enum {
#define AR_USEC_TX_LAT_S 14
#define AR_USEC_RX_LAT 0x1F800000
#define AR_USEC_RX_LAT_S 23
-#define AR_USEC_ASYNC_FIFO_DUR 0x12e00074
+#define AR_USEC_ASYNC_FIFO 0x12E00074
#define AR_RESET_TSF 0x8020
#define AR_RESET_TSF_ONCE 0x01000000
@@ -1752,6 +1793,7 @@ enum {
#define AR_TXOP_12_15 0x81fc
#define AR_NEXT_NDP2_TIMER 0x8180
+#define AR_GEN_TIMER_BANK_1_LEN 8
#define AR_FIRST_NDP_TIMER 7
#define AR_NDP2_PERIOD 0x81a0
#define AR_NDP2_TIMER_MODE 0x81c0
@@ -1840,9 +1882,10 @@ enum {
#define AR_PCU_MISC_MODE2_HWWAR2 0x02000000
#define AR_PCU_MISC_MODE2_RESERVED2 0xFFFE0000
-#define AR_MAC_PCU_ASYNC_FIFO_REG3 0x8358
-#define AR_MAC_PCU_ASYNC_FIFO_REG3_DATAPATH_SEL 0x00000400
-#define AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET 0x80000000
+#define AR_MAC_PCU_ASYNC_FIFO_REG3 0x8358
+#define AR_MAC_PCU_ASYNC_FIFO_REG3_DATAPATH_SEL 0x00000400
+#define AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET 0x80000000
+#define AR_MAC_PCU_GEN_TIMER_TSF_SEL 0x83d8
#define AR_AES_MUTE_MASK0 0x805c
@@ -1862,29 +1905,6 @@ enum {
#define AR_RATE_DURATION(_n) (AR_RATE_DURATION_0 + ((_n)<<2))
-#define AR_KEYTABLE_0 0x8800
-#define AR_KEYTABLE(_n) (AR_KEYTABLE_0 + ((_n)*32))
-#define AR_KEY_CACHE_SIZE 128
-#define AR_RSVD_KEYTABLE_ENTRIES 4
-#define AR_KEY_TYPE 0x00000007
-#define AR_KEYTABLE_TYPE_40 0x00000000
-#define AR_KEYTABLE_TYPE_104 0x00000001
-#define AR_KEYTABLE_TYPE_128 0x00000003
-#define AR_KEYTABLE_TYPE_TKIP 0x00000004
-#define AR_KEYTABLE_TYPE_AES 0x00000005
-#define AR_KEYTABLE_TYPE_CCM 0x00000006
-#define AR_KEYTABLE_TYPE_CLR 0x00000007
-#define AR_KEYTABLE_ANT 0x00000008
-#define AR_KEYTABLE_VALID 0x00008000
-#define AR_KEYTABLE_KEY0(_n) (AR_KEYTABLE(_n) + 0)
-#define AR_KEYTABLE_KEY1(_n) (AR_KEYTABLE(_n) + 4)
-#define AR_KEYTABLE_KEY2(_n) (AR_KEYTABLE(_n) + 8)
-#define AR_KEYTABLE_KEY3(_n) (AR_KEYTABLE(_n) + 12)
-#define AR_KEYTABLE_KEY4(_n) (AR_KEYTABLE(_n) + 16)
-#define AR_KEYTABLE_TYPE(_n) (AR_KEYTABLE(_n) + 20)
-#define AR_KEYTABLE_MAC0(_n) (AR_KEYTABLE(_n) + 24)
-#define AR_KEYTABLE_MAC1(_n) (AR_KEYTABLE(_n) + 28)
-
#define AR9271_CORE_CLOCK 117 /* clock to 117Mhz */
#define AR9271_TARGET_BAUD_RATE 19200 /* 115200 */
@@ -1913,7 +1933,42 @@ enum {
#define AR_PHY_AGC_CONTROL_NO_UPDATE_NF 0x00020000 /* don't update noise floor automatically */
#define AR_PHY_AGC_CONTROL_EXT_NF_PWR_MEAS 0x00040000 /* extend noise floor power measurement */
#define AR_PHY_AGC_CONTROL_CLC_SUCCESS 0x00080000 /* carrier leak calibration done */
+#define AR_PHY_AGC_CONTROL_PKDET_CAL 0x00100000
#define AR_PHY_AGC_CONTROL_YCOK_MAX 0x000003c0
#define AR_PHY_AGC_CONTROL_YCOK_MAX_S 6
+/* MCI Registers */
+#define AR_MCI_INTERRUPT_RX_MSG_EN 0x183c
+#define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET 0x00000001
+#define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET_S 0
+#define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL 0x00000002
+#define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL_S 1
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK 0x00000004
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK_S 2
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO 0x00000008
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO_S 3
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_RST 0x00000010
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_RST_S 4
+#define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO 0x00000020
+#define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO_S 5
+#define AR_MCI_INTERRUPT_RX_MSG_CPU_INT 0x00000040
+#define AR_MCI_INTERRUPT_RX_MSG_CPU_INT_S 6
+#define AR_MCI_INTERRUPT_RX_MSG_GPM 0x00000100
+#define AR_MCI_INTERRUPT_RX_MSG_GPM_S 8
+#define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO 0x00000200
+#define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO_S 9
+#define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING 0x00000400
+#define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING_S 10
+#define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING 0x00000800
+#define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING_S 11
+#define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE 0x00001000
+#define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE_S 12
+#define AR_MCI_INTERRUPT_RX_HW_MSG_MASK (AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO | \
+ AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL| \
+ AR_MCI_INTERRUPT_RX_MSG_LNA_INFO | \
+ AR_MCI_INTERRUPT_RX_MSG_CONT_NACK | \
+ AR_MCI_INTERRUPT_RX_MSG_CONT_INFO | \
+ AR_MCI_INTERRUPT_RX_MSG_CONT_RST)
+
+
#endif
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 3779b8977d47..03b0a651a591 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/dma-mapping.h>
#include "ath9k.h"
#include "ar9003_mac.h"
@@ -47,19 +48,23 @@ static u16 bits_per_symbol[][2] = {
#define IS_HT_RATE(_rate) ((_rate) & 0x80)
static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
- struct ath_atx_tid *tid,
- struct list_head *bf_head);
+ struct ath_atx_tid *tid, struct sk_buff *skb);
+static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
+ int tx_flags, struct ath_txq *txq);
static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
struct ath_txq *txq, struct list_head *bf_q,
struct ath_tx_status *ts, int txok, int sendbar);
static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
- struct list_head *head);
-static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, int len);
+ struct list_head *head, bool internal);
static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts, int nframes, int nbad,
- int txok, bool update_rc);
+ int txok);
static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
int seqno);
+static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
+ struct ath_txq *txq,
+ struct ath_atx_tid *tid,
+ struct sk_buff *skb);
enum {
MCS_HT20,
@@ -128,7 +133,7 @@ static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
spin_lock_bh(&txq->axq_lock);
tid->paused = false;
- if (list_empty(&tid->buf_q))
+ if (skb_queue_empty(&tid->buf_q))
goto unlock;
ath_tx_queue_tid(txq, tid);
@@ -148,6 +153,7 @@ static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
{
struct ath_txq *txq = tid->ac->txq;
+ struct sk_buff *skb;
struct ath_buf *bf;
struct list_head bf_head;
struct ath_tx_status ts;
@@ -158,17 +164,17 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
memset(&ts, 0, sizeof(ts));
spin_lock_bh(&txq->axq_lock);
- while (!list_empty(&tid->buf_q)) {
- bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
- list_move_tail(&bf->list, &bf_head);
+ while ((skb = __skb_dequeue(&tid->buf_q))) {
+ fi = get_frame_info(skb);
+ bf = fi->bf;
spin_unlock_bh(&txq->axq_lock);
- fi = get_frame_info(bf->bf_mpdu);
- if (fi->retries) {
- ath_tx_update_baw(sc, tid, fi->seqno);
+ if (bf && fi->retries) {
+ list_add_tail(&bf->list, &bf_head);
+ ath_tx_update_baw(sc, tid, bf->bf_state.seqno);
ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 1);
} else {
- ath_tx_send_normal(sc, txq, NULL, &bf_head);
+ ath_tx_send_normal(sc, txq, NULL, skb);
}
spin_lock_bh(&txq->axq_lock);
}
@@ -218,6 +224,7 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
struct ath_atx_tid *tid)
{
+ struct sk_buff *skb;
struct ath_buf *bf;
struct list_head bf_head;
struct ath_tx_status ts;
@@ -226,16 +233,21 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
memset(&ts, 0, sizeof(ts));
INIT_LIST_HEAD(&bf_head);
- for (;;) {
- if (list_empty(&tid->buf_q))
- break;
+ while ((skb = __skb_dequeue(&tid->buf_q))) {
+ fi = get_frame_info(skb);
+ bf = fi->bf;
- bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
- list_move_tail(&bf->list, &bf_head);
+ if (!bf) {
+ spin_unlock(&txq->axq_lock);
+ ath_tx_complete(sc, skb, ATH_TX_ERROR, txq);
+ spin_lock(&txq->axq_lock);
+ continue;
+ }
+
+ list_add_tail(&bf->list, &bf_head);
- fi = get_frame_info(bf->bf_mpdu);
if (fi->retries)
- ath_tx_update_baw(sc, tid, fi->seqno);
+ ath_tx_update_baw(sc, tid, bf->bf_state.seqno);
spin_unlock(&txq->axq_lock);
ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
@@ -250,6 +262,7 @@ static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq,
struct sk_buff *skb)
{
struct ath_frame_info *fi = get_frame_info(skb);
+ struct ath_buf *bf = fi->bf;
struct ieee80211_hdr *hdr;
TX_STAT_INC(txq->axq_qnum, a_retries);
@@ -258,6 +271,8 @@ static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq,
hdr = (struct ieee80211_hdr *)skb->data;
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_RETRY);
+ dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
+ sizeof(*hdr), DMA_TO_DEVICE);
}
static struct ath_buf *ath_tx_get_buffer(struct ath_softc *sc)
@@ -325,7 +340,7 @@ static void ath_tx_count_frames(struct ath_softc *sc, struct ath_buf *bf,
while (bf) {
fi = get_frame_info(bf->bf_mpdu);
- ba_index = ATH_BA_INDEX(seq_st, fi->seqno);
+ ba_index = ATH_BA_INDEX(seq_st, bf->bf_state.seqno);
(*nframes)++;
if (!txok || (isaggr && !ATH_BA_ISSET(ba, ba_index)))
@@ -348,7 +363,8 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
struct ieee80211_tx_info *tx_info;
struct ath_atx_tid *tid = NULL;
struct ath_buf *bf_next, *bf_last = bf->bf_lastbf;
- struct list_head bf_head, bf_pending;
+ struct list_head bf_head;
+ struct sk_buff_head bf_pending;
u16 seq_st = 0, acked_cnt = 0, txfail_cnt = 0;
u32 ba[WME_BA_BMP_SIZE >> 5];
int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0;
@@ -357,7 +373,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
struct ath_frame_info *fi;
int nframes;
u8 tidno;
- bool clear_filter;
+ bool flush = !!(ts->ts_status & ATH9K_TX_FLUSH);
skb = bf->bf_mpdu;
hdr = (struct ieee80211_hdr *)skb->data;
@@ -376,12 +392,9 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
while (bf) {
bf_next = bf->bf_next;
- bf->bf_state.bf_type |= BUF_XRETRY;
- if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ||
- !bf->bf_stale || bf_next != NULL)
+ if (!bf->bf_stale || bf_next != NULL)
list_move_tail(&bf->list, &bf_head);
- ath_tx_rc_status(sc, bf, ts, 1, 1, 0, false);
ath_tx_complete_buf(sc, bf, txq, &bf_head, ts,
0, 0);
@@ -422,11 +435,12 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
}
}
- INIT_LIST_HEAD(&bf_pending);
- INIT_LIST_HEAD(&bf_head);
+ __skb_queue_head_init(&bf_pending);
ath_tx_count_frames(sc, bf, ts, txok, &nframes, &nbad);
while (bf) {
+ u16 seqno = bf->bf_state.seqno;
+
txfail = txpending = sendbar = 0;
bf_next = bf->bf_next;
@@ -434,7 +448,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
tx_info = IEEE80211_SKB_CB(skb);
fi = get_frame_info(skb);
- if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, fi->seqno))) {
+ if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, seqno))) {
/* transmit completion, subframe is
* acked by block ack */
acked_cnt++;
@@ -448,35 +462,28 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
* the un-acked sub-frames
*/
txfail = 1;
+ } else if (flush) {
+ txpending = 1;
} else if (fi->retries < ATH_MAX_SW_RETRIES) {
- if (!(ts->ts_status & ATH9K_TXERR_FILT) ||
- !an->sleeping)
+ if (txok || !an->sleeping)
ath_tx_set_retry(sc, txq, bf->bf_mpdu);
- clear_filter = true;
txpending = 1;
} else {
- bf->bf_state.bf_type |= BUF_XRETRY;
txfail = 1;
sendbar = 1;
txfail_cnt++;
}
}
- if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
- bf_next == NULL) {
- /*
- * Make sure the last desc is reclaimed if it
- * not a holding desc.
- */
- if (!bf_last->bf_stale)
- list_move_tail(&bf->list, &bf_head);
- else
- INIT_LIST_HEAD(&bf_head);
- } else {
- BUG_ON(list_empty(bf_q));
+ /*
+ * Make sure the last desc is reclaimed if it
+ * not a holding desc.
+ */
+ INIT_LIST_HEAD(&bf_head);
+ if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ||
+ bf_next != NULL || !bf_last->bf_stale)
list_move_tail(&bf->list, &bf_head);
- }
if (!txpending || (tid->state & AGGR_CLEANUP)) {
/*
@@ -484,22 +491,19 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
* block-ack window
*/
spin_lock_bh(&txq->axq_lock);
- ath_tx_update_baw(sc, tid, fi->seqno);
+ ath_tx_update_baw(sc, tid, seqno);
spin_unlock_bh(&txq->axq_lock);
if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) {
memcpy(tx_info->control.rates, rates, sizeof(rates));
- ath_tx_rc_status(sc, bf, ts, nframes, nbad, txok, true);
+ ath_tx_rc_status(sc, bf, ts, nframes, nbad, txok);
rc_update = false;
- } else {
- ath_tx_rc_status(sc, bf, ts, nframes, nbad, txok, false);
}
ath_tx_complete_buf(sc, bf, txq, &bf_head, ts,
!txfail, sendbar);
} else {
/* retry the un-acked ones */
- ath9k_hw_set_clrdmask(sc->sc_ah, bf->bf_desc, false);
if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)) {
if (bf->bf_next == NULL && bf_last->bf_stale) {
struct ath_buf *tbf;
@@ -512,29 +516,17 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
*/
if (!tbf) {
spin_lock_bh(&txq->axq_lock);
- ath_tx_update_baw(sc, tid, fi->seqno);
+ ath_tx_update_baw(sc, tid, seqno);
spin_unlock_bh(&txq->axq_lock);
- bf->bf_state.bf_type |=
- BUF_XRETRY;
- ath_tx_rc_status(sc, bf, ts, nframes,
- nbad, 0, false);
ath_tx_complete_buf(sc, bf, txq,
&bf_head,
- ts, 0, 0);
+ ts, 0,
+ !flush);
break;
}
- ath9k_hw_cleartxdesc(sc->sc_ah,
- tbf->bf_desc);
- list_add_tail(&tbf->list, &bf_head);
- } else {
- /*
- * Clear descriptor status words for
- * software retry
- */
- ath9k_hw_cleartxdesc(sc->sc_ah,
- bf->bf_desc);
+ fi->bf = tbf;
}
}
@@ -542,22 +534,25 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
* Put this buffer to the temporary pending
* queue to retain ordering
*/
- list_splice_tail_init(&bf_head, &bf_pending);
+ __skb_queue_tail(&bf_pending, skb);
}
bf = bf_next;
}
/* prepend un-acked frames to the beginning of the pending frame queue */
- if (!list_empty(&bf_pending)) {
+ if (!skb_queue_empty(&bf_pending)) {
if (an->sleeping)
- ieee80211_sta_set_tim(sta);
+ ieee80211_sta_set_buffered(sta, tid->tidno, true);
spin_lock_bh(&txq->axq_lock);
- if (clear_filter)
- tid->ac->clear_ps_filter = true;
- list_splice(&bf_pending, &tid->buf_q);
- ath_tx_queue_tid(txq, tid);
+ skb_queue_splice(&bf_pending, &tid->buf_q);
+ if (!an->sleeping) {
+ ath_tx_queue_tid(txq, tid);
+
+ if (ts->ts_status & ATH9K_TXERR_FILT)
+ tid->ac->clear_ps_filter = true;
+ }
spin_unlock_bh(&txq->axq_lock);
}
@@ -573,12 +568,33 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
rcu_read_unlock();
if (needreset) {
- spin_unlock_bh(&sc->sc_pcu_lock);
- ath_reset(sc, false);
- spin_lock_bh(&sc->sc_pcu_lock);
+ RESET_STAT_INC(sc, RESET_TYPE_TX_ERROR);
+ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
}
}
+static bool ath_lookup_legacy(struct ath_buf *bf)
+{
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *tx_info;
+ struct ieee80211_tx_rate *rates;
+ int i;
+
+ skb = bf->bf_mpdu;
+ tx_info = IEEE80211_SKB_CB(skb);
+ rates = tx_info->control.rates;
+
+ for (i = 0; i < 4; i++) {
+ if (!rates[i].count || rates[i].idx < 0)
+ break;
+
+ if (!(rates[i].flags & IEEE80211_TX_RC_MCS))
+ return true;
+ }
+
+ return false;
+}
+
static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
struct ath_atx_tid *tid)
{
@@ -652,8 +668,10 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
* meet the minimum required mpdudensity.
*/
static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
- struct ath_buf *bf, u16 frmlen)
+ struct ath_buf *bf, u16 frmlen,
+ bool first_subfrm)
{
+#define FIRST_DESC_NDELIMS 60
struct sk_buff *skb = bf->bf_mpdu;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
u32 nsymbits, nsymbols;
@@ -671,10 +689,19 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
* TODO - this could be improved to be dependent on the rate.
* The hardware can keep up at lower rates, but not higher rates
*/
- if (fi->keyix != ATH9K_TXKEYIX_INVALID)
+ if ((fi->keyix != ATH9K_TXKEYIX_INVALID) &&
+ !(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA))
ndelim += ATH_AGGR_ENCRYPTDELIM;
/*
+ * Add delimiter when using RTS/CTS with aggregation
+ * and non enterprise AR9003 card
+ */
+ if (first_subfrm && !AR_SREV_9580_10_OR_LATER(sc->sc_ah) &&
+ (sc->sc_ah->ent_mode & AR_ENT_OTP_MIN_PKT_SIZE_DISABLE))
+ ndelim = max(ndelim, FIRST_DESC_NDELIMS);
+
+ /*
* Convert desired mpdu density from microeconds to bytes based
* on highest rate in rate series (i.e. first rate) to determine
* required minimum length for subframe. Take into account
@@ -719,22 +746,33 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
int *aggr_len)
{
#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
- struct ath_buf *bf, *bf_first, *bf_prev = NULL;
+ struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
int rl = 0, nframes = 0, ndelim, prev_al = 0;
u16 aggr_limit = 0, al = 0, bpad = 0,
al_delta, h_baw = tid->baw_size / 2;
enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
struct ieee80211_tx_info *tx_info;
struct ath_frame_info *fi;
-
- bf_first = list_first_entry(&tid->buf_q, struct ath_buf, list);
+ struct sk_buff *skb;
+ u16 seqno;
do {
- bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
- fi = get_frame_info(bf->bf_mpdu);
+ skb = skb_peek(&tid->buf_q);
+ fi = get_frame_info(skb);
+ bf = fi->bf;
+ if (!fi->bf)
+ bf = ath_tx_setup_buffer(sc, txq, tid, skb);
+
+ if (!bf)
+ continue;
+
+ bf->bf_state.bf_type = BUF_AMPDU | BUF_AGGR;
+ seqno = bf->bf_state.seqno;
+ if (!bf_first)
+ bf_first = bf;
/* do not step over block-ack window */
- if (!BAW_WITHIN(tid->seq_start, tid->baw_size, fi->seqno)) {
+ if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
status = ATH_AGGR_BAW_CLOSED;
break;
}
@@ -748,14 +786,14 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
al_delta = ATH_AGGR_DELIM_SZ + fi->framelen;
if (nframes &&
- (aggr_limit < (al + bpad + al_delta + prev_al))) {
+ ((aggr_limit < (al + bpad + al_delta + prev_al)) ||
+ ath_lookup_legacy(bf))) {
status = ATH_AGGR_LIMITED;
break;
}
tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
- if (nframes && ((tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) ||
- !(tx_info->control.rates[0].flags & IEEE80211_TX_RC_MCS)))
+ if (nframes && (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE))
break;
/* do not exceed subframe limit */
@@ -763,7 +801,6 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
status = ATH_AGGR_LIMITED;
break;
}
- nframes++;
/* add padding for previous frame to aggregation length */
al += bpad + al_delta;
@@ -772,25 +809,26 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
* Get the delimiters needed to meet the MPDU
* density for this node.
*/
- ndelim = ath_compute_num_delims(sc, tid, bf_first, fi->framelen);
+ ndelim = ath_compute_num_delims(sc, tid, bf_first, fi->framelen,
+ !nframes);
bpad = PADBYTES(al_delta) + (ndelim << 2);
+ nframes++;
bf->bf_next = NULL;
- ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, 0);
/* link buffers of this frame to the aggregate */
if (!fi->retries)
- ath_tx_addto_baw(sc, tid, fi->seqno);
- ath9k_hw_set11n_aggr_middle(sc->sc_ah, bf->bf_desc, ndelim);
- list_move_tail(&bf->list, bf_q);
- if (bf_prev) {
+ ath_tx_addto_baw(sc, tid, seqno);
+ bf->bf_state.ndelim = ndelim;
+
+ __skb_unlink(skb, &tid->buf_q);
+ list_add_tail(&bf->list, bf_q);
+ if (bf_prev)
bf_prev->bf_next = bf;
- ath9k_hw_set_desc_link(sc->sc_ah, bf_prev->bf_desc,
- bf->bf_daddr);
- }
+
bf_prev = bf;
- } while (!list_empty(&tid->buf_q));
+ } while (!skb_queue_empty(&tid->buf_q));
*aggr_len = al;
@@ -798,17 +836,236 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
#undef PADBYTES
}
+/*
+ * rix - rate index
+ * pktlen - total bytes (delims + data + fcs + pads + pad delims)
+ * width - 0 for 20 MHz, 1 for 40 MHz
+ * half_gi - to use 4us v/s 3.6 us for symbol time
+ */
+static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen,
+ int width, int half_gi, bool shortPreamble)
+{
+ u32 nbits, nsymbits, duration, nsymbols;
+ int streams;
+
+ /* find number of symbols: PLCP + data */
+ streams = HT_RC_2_STREAMS(rix);
+ nbits = (pktlen << 3) + OFDM_PLCP_BITS;
+ nsymbits = bits_per_symbol[rix % 8][width] * streams;
+ nsymbols = (nbits + nsymbits - 1) / nsymbits;
+
+ if (!half_gi)
+ duration = SYMBOL_TIME(nsymbols);
+ else
+ duration = SYMBOL_TIME_HALFGI(nsymbols);
+
+ /* addup duration for legacy/ht training and signal fields */
+ duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
+
+ return duration;
+}
+
+static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
+ struct ath_tx_info *info, int len)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *tx_info;
+ struct ieee80211_tx_rate *rates;
+ const struct ieee80211_rate *rate;
+ struct ieee80211_hdr *hdr;
+ int i;
+ u8 rix = 0;
+
+ skb = bf->bf_mpdu;
+ tx_info = IEEE80211_SKB_CB(skb);
+ rates = tx_info->control.rates;
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ /* set dur_update_en for l-sig computation except for PS-Poll frames */
+ info->dur_update = !ieee80211_is_pspoll(hdr->frame_control);
+
+ /*
+ * We check if Short Preamble is needed for the CTS rate by
+ * checking the BSS's global flag.
+ * But for the rate series, IEEE80211_TX_RC_USE_SHORT_PREAMBLE is used.
+ */
+ rate = ieee80211_get_rts_cts_rate(sc->hw, tx_info);
+ info->rtscts_rate = rate->hw_value;
+ if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
+ info->rtscts_rate |= rate->hw_value_short;
+
+ for (i = 0; i < 4; i++) {
+ bool is_40, is_sgi, is_sp;
+ int phy;
+
+ if (!rates[i].count || (rates[i].idx < 0))
+ continue;
+
+ rix = rates[i].idx;
+ info->rates[i].Tries = rates[i].count;
+
+ if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+ info->rates[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
+ info->flags |= ATH9K_TXDESC_RTSENA;
+ } else if (rates[i].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ info->rates[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
+ info->flags |= ATH9K_TXDESC_CTSENA;
+ }
+
+ if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ info->rates[i].RateFlags |= ATH9K_RATESERIES_2040;
+ if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI)
+ info->rates[i].RateFlags |= ATH9K_RATESERIES_HALFGI;
+
+ is_sgi = !!(rates[i].flags & IEEE80211_TX_RC_SHORT_GI);
+ is_40 = !!(rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH);
+ is_sp = !!(rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
+
+ if (rates[i].flags & IEEE80211_TX_RC_MCS) {
+ /* MCS rates */
+ info->rates[i].Rate = rix | 0x80;
+ info->rates[i].ChSel = ath_txchainmask_reduction(sc,
+ ah->txchainmask, info->rates[i].Rate);
+ info->rates[i].PktDuration = ath_pkt_duration(sc, rix, len,
+ is_40, is_sgi, is_sp);
+ if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
+ info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC;
+ continue;
+ }
+
+ /* legacy rates */
+ if ((tx_info->band == IEEE80211_BAND_2GHZ) &&
+ !(rate->flags & IEEE80211_RATE_ERP_G))
+ phy = WLAN_RC_PHY_CCK;
+ else
+ phy = WLAN_RC_PHY_OFDM;
+
+ rate = &sc->sbands[tx_info->band].bitrates[rates[i].idx];
+ info->rates[i].Rate = rate->hw_value;
+ if (rate->hw_value_short) {
+ if (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+ info->rates[i].Rate |= rate->hw_value_short;
+ } else {
+ is_sp = false;
+ }
+
+ if (bf->bf_state.bfs_paprd)
+ info->rates[i].ChSel = ah->txchainmask;
+ else
+ info->rates[i].ChSel = ath_txchainmask_reduction(sc,
+ ah->txchainmask, info->rates[i].Rate);
+
+ info->rates[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
+ phy, rate->bitrate * 100, len, rix, is_sp);
+ }
+
+ /* For AR5416 - RTS cannot be followed by a frame larger than 8K */
+ if (bf_isaggr(bf) && (len > sc->sc_ah->caps.rts_aggr_limit))
+ info->flags &= ~ATH9K_TXDESC_RTSENA;
+
+ /* ATH9K_TXDESC_RTSENA and ATH9K_TXDESC_CTSENA are mutually exclusive. */
+ if (info->flags & ATH9K_TXDESC_RTSENA)
+ info->flags &= ~ATH9K_TXDESC_CTSENA;
+}
+
+static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+ enum ath9k_pkt_type htype;
+ __le16 fc;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ fc = hdr->frame_control;
+
+ if (ieee80211_is_beacon(fc))
+ htype = ATH9K_PKT_TYPE_BEACON;
+ else if (ieee80211_is_probe_resp(fc))
+ htype = ATH9K_PKT_TYPE_PROBE_RESP;
+ else if (ieee80211_is_atim(fc))
+ htype = ATH9K_PKT_TYPE_ATIM;
+ else if (ieee80211_is_pspoll(fc))
+ htype = ATH9K_PKT_TYPE_PSPOLL;
+ else
+ htype = ATH9K_PKT_TYPE_NORMAL;
+
+ return htype;
+}
+
+static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
+ struct ath_txq *txq, int len)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
+ struct ath_buf *bf_first = bf;
+ struct ath_tx_info info;
+ bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR);
+
+ memset(&info, 0, sizeof(info));
+ info.is_first = true;
+ info.is_last = true;
+ info.txpower = MAX_RATE_POWER;
+ info.qcu = txq->axq_qnum;
+
+ info.flags = ATH9K_TXDESC_INTREQ;
+ if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
+ info.flags |= ATH9K_TXDESC_NOACK;
+ if (tx_info->flags & IEEE80211_TX_CTL_LDPC)
+ info.flags |= ATH9K_TXDESC_LDPC;
+
+ ath_buf_set_rate(sc, bf, &info, len);
+
+ if (tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
+ info.flags |= ATH9K_TXDESC_CLRDMASK;
+
+ if (bf->bf_state.bfs_paprd)
+ info.flags |= (u32) bf->bf_state.bfs_paprd << ATH9K_TXDESC_PAPRD_S;
+
+
+ while (bf) {
+ struct sk_buff *skb = bf->bf_mpdu;
+ struct ath_frame_info *fi = get_frame_info(skb);
+
+ info.type = get_hw_packet_type(skb);
+ if (bf->bf_next)
+ info.link = bf->bf_next->bf_daddr;
+ else
+ info.link = 0;
+
+ info.buf_addr[0] = bf->bf_buf_addr;
+ info.buf_len[0] = skb->len;
+ info.pkt_len = fi->framelen;
+ info.keyix = fi->keyix;
+ info.keytype = fi->keytype;
+
+ if (aggr) {
+ if (bf == bf_first)
+ info.aggr = AGGR_BUF_FIRST;
+ else if (!bf->bf_next)
+ info.aggr = AGGR_BUF_LAST;
+ else
+ info.aggr = AGGR_BUF_MIDDLE;
+
+ info.ndelim = bf->bf_state.ndelim;
+ info.aggr_len = len;
+ }
+
+ ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
+ bf = bf->bf_next;
+ }
+}
+
static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
struct ath_atx_tid *tid)
{
struct ath_buf *bf;
enum ATH_AGGR_STATUS status;
- struct ath_frame_info *fi;
+ struct ieee80211_tx_info *tx_info;
struct list_head bf_q;
int aggr_len;
do {
- if (list_empty(&tid->buf_q))
+ if (skb_queue_empty(&tid->buf_q))
return;
INIT_LIST_HEAD(&bf_q);
@@ -824,34 +1081,25 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
bf = list_first_entry(&bf_q, struct ath_buf, list);
bf->bf_lastbf = list_entry(bf_q.prev, struct ath_buf, list);
+ tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
if (tid->ac->clear_ps_filter) {
tid->ac->clear_ps_filter = false;
- ath9k_hw_set_clrdmask(sc->sc_ah, bf->bf_desc, true);
+ tx_info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
+ } else {
+ tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT;
}
/* if only one frame, send as non-aggregate */
if (bf == bf->bf_lastbf) {
- fi = get_frame_info(bf->bf_mpdu);
-
- bf->bf_state.bf_type &= ~BUF_AGGR;
- ath9k_hw_clr11n_aggr(sc->sc_ah, bf->bf_desc);
- ath_buf_set_rate(sc, bf, fi->framelen);
- ath_tx_txqaddbuf(sc, txq, &bf_q);
- continue;
+ aggr_len = get_frame_info(bf->bf_mpdu)->framelen;
+ bf->bf_state.bf_type = BUF_AMPDU;
+ } else {
+ TX_STAT_INC(txq->axq_qnum, a_aggr);
}
- /* setup first desc of aggregate */
- bf->bf_state.bf_type |= BUF_AGGR;
- ath_buf_set_rate(sc, bf, aggr_len);
- ath9k_hw_set11n_aggr_first(sc->sc_ah, bf->bf_desc, aggr_len);
-
- /* anchor last desc of aggregate */
- ath9k_hw_set11n_aggr_last(sc->sc_ah, bf->bf_lastbf->bf_desc);
-
- ath_tx_txqaddbuf(sc, txq, &bf_q);
- TX_STAT_INC(txq->axq_qnum, a_aggr);
-
+ ath_tx_fill_desc(sc, bf, txq, aggr_len);
+ ath_tx_txqaddbuf(sc, txq, &bf_q, false);
} while (txq->axq_ampdu_depth < ATH_AGGR_MIN_QDEPTH &&
status != ATH_AGGR_BAW_CLOSED);
}
@@ -910,12 +1158,13 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
ath_tx_flush_tid(sc, txtid);
}
-bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an)
+void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
+ struct ath_node *an)
{
struct ath_atx_tid *tid;
struct ath_atx_ac *ac;
struct ath_txq *txq;
- bool buffered = false;
+ bool buffered;
int tidno;
for (tidno = 0, tid = &an->tid[tidno];
@@ -929,8 +1178,7 @@ bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an)
spin_lock_bh(&txq->axq_lock);
- if (!list_empty(&tid->buf_q))
- buffered = true;
+ buffered = !skb_queue_empty(&tid->buf_q);
tid->sched = false;
list_del(&tid->list);
@@ -941,9 +1189,9 @@ bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an)
}
spin_unlock_bh(&txq->axq_lock);
- }
- return buffered;
+ ieee80211_sta_set_buffered(sta, tidno, buffered);
+ }
}
void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
@@ -962,7 +1210,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
spin_lock_bh(&txq->axq_lock);
ac->clear_ps_filter = true;
- if (!list_empty(&tid->buf_q) && !tid->paused) {
+ if (!skb_queue_empty(&tid->buf_q) && !tid->paused) {
ath_tx_queue_tid(txq, tid);
ath_txq_schedule(sc, txq);
}
@@ -1012,7 +1260,6 @@ static void ath_txq_drain_pending_buffers(struct ath_softc *sc,
struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
{
struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_tx_queue_info qi;
static const int subtype_txq_to_hwq[] = {
[WME_AC_BE] = ATH_TXQ_AC_BE,
@@ -1062,12 +1309,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
*/
return NULL;
}
- if (axq_qnum >= ARRAY_SIZE(sc->tx.txq)) {
- ath_err(common, "qnum %u out of range, max %zu!\n",
- axq_qnum, ARRAY_SIZE(sc->tx.txq));
- ath9k_hw_releasetxqueue(ah, axq_qnum);
- return NULL;
- }
if (!ATH_TXQ_SETUP(sc, axq_qnum)) {
struct ath_txq *txq = &sc->tx.txq[axq_qnum];
@@ -1085,7 +1326,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
txq->txq_headidx = txq->txq_tailidx = 0;
for (i = 0; i < ATH_TXFIFO_DEPTH; i++)
INIT_LIST_HEAD(&txq->txq_fifo[i]);
- INIT_LIST_HEAD(&txq->txq_fifo_pending);
}
return &sc->tx.txq[axq_qnum];
}
@@ -1155,108 +1395,76 @@ static bool bf_is_ampdu_not_probing(struct ath_buf *bf)
return bf_isampdu(bf) && !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE);
}
-/*
- * Drain a given TX queue (could be Beacon or Data)
- *
- * This assumes output has been stopped and
- * we do not need to block ath_tx_tasklet.
- */
-void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
+static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq,
+ struct list_head *list, bool retry_tx)
+ __releases(txq->axq_lock)
+ __acquires(txq->axq_lock)
{
struct ath_buf *bf, *lastbf;
struct list_head bf_head;
struct ath_tx_status ts;
memset(&ts, 0, sizeof(ts));
+ ts.ts_status = ATH9K_TX_FLUSH;
INIT_LIST_HEAD(&bf_head);
- for (;;) {
- spin_lock_bh(&txq->axq_lock);
-
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
- if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) {
- txq->txq_headidx = txq->txq_tailidx = 0;
- spin_unlock_bh(&txq->axq_lock);
- break;
- } else {
- bf = list_first_entry(&txq->txq_fifo[txq->txq_tailidx],
- struct ath_buf, list);
- }
- } else {
- if (list_empty(&txq->axq_q)) {
- txq->axq_link = NULL;
- spin_unlock_bh(&txq->axq_lock);
- break;
- }
- bf = list_first_entry(&txq->axq_q, struct ath_buf,
- list);
+ while (!list_empty(list)) {
+ bf = list_first_entry(list, struct ath_buf, list);
- if (bf->bf_stale) {
- list_del(&bf->list);
- spin_unlock_bh(&txq->axq_lock);
+ if (bf->bf_stale) {
+ list_del(&bf->list);
- ath_tx_return_buffer(sc, bf);
- continue;
- }
+ ath_tx_return_buffer(sc, bf);
+ continue;
}
lastbf = bf->bf_lastbf;
-
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
- list_cut_position(&bf_head,
- &txq->txq_fifo[txq->txq_tailidx],
- &lastbf->list);
- INCR(txq->txq_tailidx, ATH_TXFIFO_DEPTH);
- } else {
- /* remove ath_buf's of the same mpdu from txq */
- list_cut_position(&bf_head, &txq->axq_q, &lastbf->list);
- }
+ list_cut_position(&bf_head, list, &lastbf->list);
txq->axq_depth--;
if (bf_is_ampdu_not_probing(bf))
txq->axq_ampdu_depth--;
- spin_unlock_bh(&txq->axq_lock);
+ spin_unlock_bh(&txq->axq_lock);
if (bf_isampdu(bf))
ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, 0,
retry_tx);
else
ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
+ spin_lock_bh(&txq->axq_lock);
}
+}
+/*
+ * Drain a given TX queue (could be Beacon or Data)
+ *
+ * This assumes output has been stopped and
+ * we do not need to block ath_tx_tasklet.
+ */
+void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
+{
spin_lock_bh(&txq->axq_lock);
- txq->axq_tx_inprogress = false;
- spin_unlock_bh(&txq->axq_lock);
-
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
- spin_lock_bh(&txq->axq_lock);
- while (!list_empty(&txq->txq_fifo_pending)) {
- bf = list_first_entry(&txq->txq_fifo_pending,
- struct ath_buf, list);
- list_cut_position(&bf_head,
- &txq->txq_fifo_pending,
- &bf->bf_lastbf->list);
- spin_unlock_bh(&txq->axq_lock);
+ int idx = txq->txq_tailidx;
- if (bf_isampdu(bf))
- ath_tx_complete_aggr(sc, txq, bf, &bf_head,
- &ts, 0, retry_tx);
- else
- ath_tx_complete_buf(sc, bf, txq, &bf_head,
- &ts, 0, 0);
- spin_lock_bh(&txq->axq_lock);
+ while (!list_empty(&txq->txq_fifo[idx])) {
+ ath_drain_txq_list(sc, txq, &txq->txq_fifo[idx],
+ retry_tx);
+
+ INCR(idx, ATH_TXFIFO_DEPTH);
}
- spin_unlock_bh(&txq->axq_lock);
+ txq->txq_tailidx = idx;
}
+ txq->axq_link = NULL;
+ txq->axq_tx_inprogress = false;
+ ath_drain_txq_list(sc, txq, &txq->axq_q, retry_tx);
+
/* flush any pending frames if aggregation is enabled */
- if (sc->sc_flags & SC_OP_TXAGGR) {
- if (!retry_tx) {
- spin_lock_bh(&txq->axq_lock);
- ath_txq_drain_pending_buffers(sc, txq);
- spin_unlock_bh(&txq->axq_lock);
- }
- }
+ if ((sc->sc_flags & SC_OP_TXAGGR) && !retry_tx)
+ ath_txq_drain_pending_buffers(sc, txq);
+
+ spin_unlock_bh(&txq->axq_lock);
}
bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
@@ -1264,7 +1472,8 @@ bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_txq *txq;
- int i, npend = 0;
+ int i;
+ u32 npend = 0;
if (sc->sc_flags & SC_OP_INVALID)
return true;
@@ -1276,11 +1485,12 @@ bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
if (!ATH_TXQ_SETUP(sc, i))
continue;
- npend += ath9k_hw_numtxpending(ah, sc->tx.txq[i].axq_qnum);
+ if (ath9k_hw_numtxpending(ah, sc->tx.txq[i].axq_qnum))
+ npend |= BIT(i);
}
if (npend)
- ath_err(common, "Failed to stop TX DMA!\n");
+ ath_err(common, "Failed to stop TX DMA, queues=0x%03x!\n", npend);
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (!ATH_TXQ_SETUP(sc, i))
@@ -1313,7 +1523,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
struct ath_atx_ac *ac, *ac_tmp, *last_ac;
struct ath_atx_tid *tid, *last_tid;
- if (list_empty(&txq->axq_acq) ||
+ if (work_pending(&sc->hw_reset_work) || list_empty(&txq->axq_acq) ||
txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH)
return;
@@ -1340,7 +1550,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
* add tid to round-robin queue if more frames
* are pending for the tid
*/
- if (!list_empty(&tid->buf_q))
+ if (!skb_queue_empty(&tid->buf_q))
ath_tx_queue_tid(txq, tid);
if (tid == last_tid ||
@@ -1370,11 +1580,13 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
* assume the descriptors are already chained together by caller.
*/
static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
- struct list_head *head)
+ struct list_head *head, bool internal)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- struct ath_buf *bf;
+ struct ath_buf *bf, *bf_last;
+ bool puttxbuf = false;
+ bool edma;
/*
* Insert the frame on the outbound list and
@@ -1384,60 +1596,57 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
if (list_empty(head))
return;
+ edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
bf = list_first_entry(head, struct ath_buf, list);
+ bf_last = list_entry(head->prev, struct ath_buf, list);
ath_dbg(common, ATH_DBG_QUEUE,
"qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth);
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
- if (txq->axq_depth >= ATH_TXFIFO_DEPTH) {
- list_splice_tail_init(head, &txq->txq_fifo_pending);
- return;
- }
- if (!list_empty(&txq->txq_fifo[txq->txq_headidx]))
- ath_dbg(common, ATH_DBG_XMIT,
- "Initializing tx fifo %d which is non-empty\n",
- txq->txq_headidx);
- INIT_LIST_HEAD(&txq->txq_fifo[txq->txq_headidx]);
- list_splice_init(head, &txq->txq_fifo[txq->txq_headidx]);
+ if (edma && list_empty(&txq->txq_fifo[txq->txq_headidx])) {
+ list_splice_tail_init(head, &txq->txq_fifo[txq->txq_headidx]);
INCR(txq->txq_headidx, ATH_TXFIFO_DEPTH);
- TX_STAT_INC(txq->axq_qnum, puttxbuf);
- ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
- ath_dbg(common, ATH_DBG_XMIT, "TXDP[%u] = %llx (%p)\n",
- txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc);
+ puttxbuf = true;
} else {
list_splice_tail_init(head, &txq->axq_q);
- if (txq->axq_link == NULL) {
- TX_STAT_INC(txq->axq_qnum, puttxbuf);
- ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
- ath_dbg(common, ATH_DBG_XMIT, "TXDP[%u] = %llx (%p)\n",
- txq->axq_qnum, ito64(bf->bf_daddr),
- bf->bf_desc);
- } else {
- *txq->axq_link = bf->bf_daddr;
+ if (txq->axq_link) {
+ ath9k_hw_set_desc_link(ah, txq->axq_link, bf->bf_daddr);
ath_dbg(common, ATH_DBG_XMIT,
"link[%u] (%p)=%llx (%p)\n",
txq->axq_qnum, txq->axq_link,
ito64(bf->bf_daddr), bf->bf_desc);
- }
- ath9k_hw_get_desc_link(ah, bf->bf_lastbf->bf_desc,
- &txq->axq_link);
+ } else if (!edma)
+ puttxbuf = true;
+
+ txq->axq_link = bf_last->bf_desc;
+ }
+
+ if (puttxbuf) {
+ TX_STAT_INC(txq->axq_qnum, puttxbuf);
+ ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+ ath_dbg(common, ATH_DBG_XMIT, "TXDP[%u] = %llx (%p)\n",
+ txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc);
+ }
+
+ if (!edma) {
TX_STAT_INC(txq->axq_qnum, txstart);
ath9k_hw_txstart(ah, txq->axq_qnum);
}
- txq->axq_depth++;
- if (bf_is_ampdu_not_probing(bf))
- txq->axq_ampdu_depth++;
+
+ if (!internal) {
+ txq->axq_depth++;
+ if (bf_is_ampdu_not_probing(bf))
+ txq->axq_ampdu_depth++;
+ }
}
static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
- struct ath_buf *bf, struct ath_tx_control *txctl)
+ struct sk_buff *skb, struct ath_tx_control *txctl)
{
- struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu);
+ struct ath_frame_info *fi = get_frame_info(skb);
struct list_head bf_head;
-
- bf->bf_state.bf_type |= BUF_AMPDU;
+ struct ath_buf *bf;
/*
* Do not queue to h/w when any of the following conditions is true:
@@ -1446,113 +1655,82 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
* - seqno is not within block-ack window
* - h/w queue depth exceeds low water mark
*/
- if (!list_empty(&tid->buf_q) || tid->paused ||
- !BAW_WITHIN(tid->seq_start, tid->baw_size, fi->seqno) ||
+ if (!skb_queue_empty(&tid->buf_q) || tid->paused ||
+ !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) ||
txctl->txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) {
/*
* Add this frame to software queue for scheduling later
* for aggregation.
*/
TX_STAT_INC(txctl->txq->axq_qnum, a_queued_sw);
- list_add_tail(&bf->list, &tid->buf_q);
- ath_tx_queue_tid(txctl->txq, tid);
+ __skb_queue_tail(&tid->buf_q, skb);
+ if (!txctl->an || !txctl->an->sleeping)
+ ath_tx_queue_tid(txctl->txq, tid);
return;
}
+ bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+ if (!bf)
+ return;
+
+ bf->bf_state.bf_type = BUF_AMPDU;
INIT_LIST_HEAD(&bf_head);
list_add(&bf->list, &bf_head);
/* Add sub-frame to BAW */
- if (!fi->retries)
- ath_tx_addto_baw(sc, tid, fi->seqno);
+ ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
/* Queue to h/w without aggregation */
TX_STAT_INC(txctl->txq->axq_qnum, a_queued_hw);
bf->bf_lastbf = bf;
- ath_buf_set_rate(sc, bf, fi->framelen);
- ath_tx_txqaddbuf(sc, txctl->txq, &bf_head);
+ ath_tx_fill_desc(sc, bf, txctl->txq, fi->framelen);
+ ath_tx_txqaddbuf(sc, txctl->txq, &bf_head, false);
}
static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
- struct ath_atx_tid *tid,
- struct list_head *bf_head)
+ struct ath_atx_tid *tid, struct sk_buff *skb)
{
- struct ath_frame_info *fi;
+ struct ath_frame_info *fi = get_frame_info(skb);
+ struct list_head bf_head;
struct ath_buf *bf;
- bf = list_first_entry(bf_head, struct ath_buf, list);
- bf->bf_state.bf_type &= ~BUF_AMPDU;
+ bf = fi->bf;
+ if (!bf)
+ bf = ath_tx_setup_buffer(sc, txq, tid, skb);
+
+ if (!bf)
+ return;
+
+ INIT_LIST_HEAD(&bf_head);
+ list_add_tail(&bf->list, &bf_head);
+ bf->bf_state.bf_type = 0;
/* update starting sequence number for subsequent ADDBA request */
if (tid)
INCR(tid->seq_start, IEEE80211_SEQ_MAX);
bf->bf_lastbf = bf;
- fi = get_frame_info(bf->bf_mpdu);
- ath_buf_set_rate(sc, bf, fi->framelen);
- ath_tx_txqaddbuf(sc, txq, bf_head);
+ ath_tx_fill_desc(sc, bf, txq, fi->framelen);
+ ath_tx_txqaddbuf(sc, txq, &bf_head, false);
TX_STAT_INC(txq->axq_qnum, queued);
}
-static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr;
- enum ath9k_pkt_type htype;
- __le16 fc;
-
- hdr = (struct ieee80211_hdr *)skb->data;
- fc = hdr->frame_control;
-
- if (ieee80211_is_beacon(fc))
- htype = ATH9K_PKT_TYPE_BEACON;
- else if (ieee80211_is_probe_resp(fc))
- htype = ATH9K_PKT_TYPE_PROBE_RESP;
- else if (ieee80211_is_atim(fc))
- htype = ATH9K_PKT_TYPE_ATIM;
- else if (ieee80211_is_pspoll(fc))
- htype = ATH9K_PKT_TYPE_PSPOLL;
- else
- htype = ATH9K_PKT_TYPE_NORMAL;
-
- return htype;
-}
-
static void setup_frame_info(struct ieee80211_hw *hw, struct sk_buff *skb,
int framelen)
{
- struct ath_softc *sc = hw->priv;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_sta *sta = tx_info->control.sta;
struct ieee80211_key_conf *hw_key = tx_info->control.hw_key;
- struct ieee80211_hdr *hdr;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ath_frame_info *fi = get_frame_info(skb);
struct ath_node *an = NULL;
- struct ath_atx_tid *tid;
enum ath9k_key_type keytype;
- u16 seqno = 0;
- u8 tidno;
keytype = ath9k_cmn_get_hw_crypto_keytype(skb);
if (sta)
an = (struct ath_node *) sta->drv_priv;
- hdr = (struct ieee80211_hdr *)skb->data;
- if (an && ieee80211_is_data_qos(hdr->frame_control) &&
- conf_is_ht(&hw->conf) && (sc->sc_flags & SC_OP_TXAGGR)) {
-
- tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
-
- /*
- * Override seqno set by upper layer with the one
- * in tx aggregation state.
- */
- tid = ATH_AN_2_TID(an, tidno);
- seqno = tid->seq_next;
- hdr->seq_ctrl = cpu_to_le16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
- INCR(tid->seq_next, IEEE80211_SEQ_MAX);
- }
-
memset(fi, 0, sizeof(*fi));
if (hw_key)
fi->keyix = hw_key->hw_key_idx;
@@ -1562,199 +1740,50 @@ static void setup_frame_info(struct ieee80211_hw *hw, struct sk_buff *skb,
fi->keyix = ATH9K_TXKEYIX_INVALID;
fi->keytype = keytype;
fi->framelen = framelen;
- fi->seqno = seqno;
-}
-
-static int setup_tx_flags(struct sk_buff *skb)
-{
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- int flags = 0;
-
- flags |= ATH9K_TXDESC_INTREQ;
-
- if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
- flags |= ATH9K_TXDESC_NOACK;
-
- if (tx_info->flags & IEEE80211_TX_CTL_LDPC)
- flags |= ATH9K_TXDESC_LDPC;
-
- return flags;
-}
-
-/*
- * rix - rate index
- * pktlen - total bytes (delims + data + fcs + pads + pad delims)
- * width - 0 for 20 MHz, 1 for 40 MHz
- * half_gi - to use 4us v/s 3.6 us for symbol time
- */
-static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen,
- int width, int half_gi, bool shortPreamble)
-{
- u32 nbits, nsymbits, duration, nsymbols;
- int streams;
-
- /* find number of symbols: PLCP + data */
- streams = HT_RC_2_STREAMS(rix);
- nbits = (pktlen << 3) + OFDM_PLCP_BITS;
- nsymbits = bits_per_symbol[rix % 8][width] * streams;
- nsymbols = (nbits + nsymbits - 1) / nsymbits;
-
- if (!half_gi)
- duration = SYMBOL_TIME(nsymbols);
- else
- duration = SYMBOL_TIME_HALFGI(nsymbols);
-
- /* addup duration for legacy/ht training and signal fields */
- duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
-
- return duration;
}
u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate)
{
struct ath_hw *ah = sc->sc_ah;
struct ath9k_channel *curchan = ah->curchan;
- if ((sc->sc_flags & SC_OP_ENABLE_APM) &&
- (curchan->channelFlags & CHANNEL_5GHZ) &&
- (chainmask == 0x7) && (rate < 0x90))
+ if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) &&
+ (curchan->channelFlags & CHANNEL_5GHZ) &&
+ (chainmask == 0x7) && (rate < 0x90))
return 0x3;
else
return chainmask;
}
-static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, int len)
-{
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ath9k_11n_rate_series series[4];
- struct sk_buff *skb;
- struct ieee80211_tx_info *tx_info;
- struct ieee80211_tx_rate *rates;
- const struct ieee80211_rate *rate;
- struct ieee80211_hdr *hdr;
- int i, flags = 0;
- u8 rix = 0, ctsrate = 0;
- bool is_pspoll;
-
- memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4);
-
- skb = bf->bf_mpdu;
- tx_info = IEEE80211_SKB_CB(skb);
- rates = tx_info->control.rates;
- hdr = (struct ieee80211_hdr *)skb->data;
- is_pspoll = ieee80211_is_pspoll(hdr->frame_control);
-
- /*
- * We check if Short Preamble is needed for the CTS rate by
- * checking the BSS's global flag.
- * But for the rate series, IEEE80211_TX_RC_USE_SHORT_PREAMBLE is used.
- */
- rate = ieee80211_get_rts_cts_rate(sc->hw, tx_info);
- ctsrate = rate->hw_value;
- if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
- ctsrate |= rate->hw_value_short;
-
- for (i = 0; i < 4; i++) {
- bool is_40, is_sgi, is_sp;
- int phy;
-
- if (!rates[i].count || (rates[i].idx < 0))
- continue;
-
- rix = rates[i].idx;
- series[i].Tries = rates[i].count;
-
- if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
- series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
- flags |= ATH9K_TXDESC_RTSENA;
- } else if (rates[i].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
- series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
- flags |= ATH9K_TXDESC_CTSENA;
- }
-
- if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
- series[i].RateFlags |= ATH9K_RATESERIES_2040;
- if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI)
- series[i].RateFlags |= ATH9K_RATESERIES_HALFGI;
-
- is_sgi = !!(rates[i].flags & IEEE80211_TX_RC_SHORT_GI);
- is_40 = !!(rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH);
- is_sp = !!(rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
-
- if (rates[i].flags & IEEE80211_TX_RC_MCS) {
- /* MCS rates */
- series[i].Rate = rix | 0x80;
- series[i].ChSel = ath_txchainmask_reduction(sc,
- common->tx_chainmask, series[i].Rate);
- series[i].PktDuration = ath_pkt_duration(sc, rix, len,
- is_40, is_sgi, is_sp);
- if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
- series[i].RateFlags |= ATH9K_RATESERIES_STBC;
- continue;
- }
-
- /* legacy rates */
- if ((tx_info->band == IEEE80211_BAND_2GHZ) &&
- !(rate->flags & IEEE80211_RATE_ERP_G))
- phy = WLAN_RC_PHY_CCK;
- else
- phy = WLAN_RC_PHY_OFDM;
-
- rate = &sc->sbands[tx_info->band].bitrates[rates[i].idx];
- series[i].Rate = rate->hw_value;
- if (rate->hw_value_short) {
- if (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
- series[i].Rate |= rate->hw_value_short;
- } else {
- is_sp = false;
- }
-
- if (bf->bf_state.bfs_paprd)
- series[i].ChSel = common->tx_chainmask;
- else
- series[i].ChSel = ath_txchainmask_reduction(sc,
- common->tx_chainmask, series[i].Rate);
-
- series[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
- phy, rate->bitrate * 100, len, rix, is_sp);
- }
-
- /* For AR5416 - RTS cannot be followed by a frame larger than 8K */
- if (bf_isaggr(bf) && (len > sc->sc_ah->caps.rts_aggr_limit))
- flags &= ~ATH9K_TXDESC_RTSENA;
-
- /* ATH9K_TXDESC_RTSENA and ATH9K_TXDESC_CTSENA are mutually exclusive. */
- if (flags & ATH9K_TXDESC_RTSENA)
- flags &= ~ATH9K_TXDESC_CTSENA;
-
- /* set dur_update_en for l-sig computation except for PS-Poll frames */
- ath9k_hw_set11n_ratescenario(sc->sc_ah, bf->bf_desc,
- bf->bf_lastbf->bf_desc,
- !is_pspoll, ctsrate,
- 0, series, 4, flags);
-
-}
-
-static struct ath_buf *ath_tx_setup_buffer(struct ieee80211_hw *hw,
+/*
+ * Assign a descriptor (and sequence number if necessary,
+ * and map buffer for DMA. Frees skb on error
+ */
+static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
struct ath_txq *txq,
+ struct ath_atx_tid *tid,
struct sk_buff *skb)
{
- struct ath_softc *sc = hw->priv;
- struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_frame_info *fi = get_frame_info(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ath_buf *bf;
- struct ath_desc *ds;
- int frm_type;
+ u16 seqno;
bf = ath_tx_get_buffer(sc);
if (!bf) {
ath_dbg(common, ATH_DBG_XMIT, "TX buffers are full\n");
- return NULL;
+ goto error;
}
ATH_TXBUF_RESET(bf);
- bf->bf_flags = setup_tx_flags(skb);
+ if (tid) {
+ seqno = tid->seq_next;
+ hdr->seq_ctrl = cpu_to_le16(tid->seq_next << IEEE80211_SEQ_SEQ_SHIFT);
+ INCR(tid->seq_next, IEEE80211_SEQ_MAX);
+ bf->bf_state.seqno = seqno;
+ }
+
bf->bf_mpdu = skb;
bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
@@ -1765,38 +1794,26 @@ static struct ath_buf *ath_tx_setup_buffer(struct ieee80211_hw *hw,
ath_err(ath9k_hw_common(sc->sc_ah),
"dma_mapping_error() on TX\n");
ath_tx_return_buffer(sc, bf);
- return NULL;
+ goto error;
}
- frm_type = get_hw_packet_type(skb);
-
- ds = bf->bf_desc;
- ath9k_hw_set_desc_link(ah, ds, 0);
-
- ath9k_hw_set11n_txdesc(ah, ds, fi->framelen, frm_type, MAX_RATE_POWER,
- fi->keyix, fi->keytype, bf->bf_flags);
-
- ath9k_hw_filltxdesc(ah, ds,
- skb->len, /* segment length */
- true, /* first segment */
- true, /* last segment */
- ds, /* first descriptor */
- bf->bf_buf_addr,
- txq->axq_qnum);
-
+ fi->bf = bf;
return bf;
+
+error:
+ dev_kfree_skb_any(skb);
+ return NULL;
}
/* FIXME: tx power */
-static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf,
+static void ath_tx_start_dma(struct ath_softc *sc, struct sk_buff *skb,
struct ath_tx_control *txctl)
{
- struct sk_buff *skb = bf->bf_mpdu;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct list_head bf_head;
struct ath_atx_tid *tid = NULL;
+ struct ath_buf *bf;
u8 tidno;
spin_lock_bh(&txctl->txq->axq_lock);
@@ -1814,27 +1831,21 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf,
* Try aggregation if it's a unicast data frame
* and the destination is HT capable.
*/
- ath_tx_send_ampdu(sc, tid, bf, txctl);
+ ath_tx_send_ampdu(sc, tid, skb, txctl);
} else {
- INIT_LIST_HEAD(&bf_head);
- list_add_tail(&bf->list, &bf_head);
+ bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+ if (!bf)
+ goto out;
- bf->bf_state.bfs_ftype = txctl->frame_type;
bf->bf_state.bfs_paprd = txctl->paprd;
- if (bf->bf_state.bfs_paprd)
- ar9003_hw_set_paprd_txdesc(sc->sc_ah, bf->bf_desc,
- bf->bf_state.bfs_paprd);
-
if (txctl->paprd)
bf->bf_state.bfs_paprd_timestamp = jiffies;
- if (tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
- ath9k_hw_set_clrdmask(sc->sc_ah, bf->bf_desc, true);
-
- ath_tx_send_normal(sc, txctl->txq, tid, &bf_head);
+ ath_tx_send_normal(sc, txctl->txq, tid, skb);
}
+out:
spin_unlock_bh(&txctl->txq->axq_lock);
}
@@ -1848,7 +1859,6 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_vif *vif = info->control.vif;
struct ath_softc *sc = hw->priv;
struct ath_txq *txq = txctl->txq;
- struct ath_buf *bf;
int padpos, padsize;
int frmlen = skb->len + FCS_LEN;
int q;
@@ -1881,6 +1891,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
skb_push(skb, padsize);
memmove(skb->data, skb->data + padsize, padpos);
+ hdr = (struct ieee80211_hdr *) skb->data;
}
if ((vif && vif->type != NL80211_IFTYPE_AP &&
@@ -1895,10 +1906,6 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
* info are no longer valid (overwritten by the ath_frame_info data.
*/
- bf = ath_tx_setup_buffer(hw, txctl->txq, skb);
- if (unlikely(!bf))
- return -ENOMEM;
-
q = skb_get_queue_mapping(skb);
spin_lock_bh(&txq->axq_lock);
if (txq == sc->tx.txq_map[q] &&
@@ -1908,8 +1915,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
}
spin_unlock_bh(&txq->axq_lock);
- ath_tx_start_dma(sc, bf, txctl);
-
+ ath_tx_start_dma(sc, skb, txctl);
return 0;
}
@@ -1918,7 +1924,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
/*****************/
static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
- int tx_flags, int ftype, struct ath_txq *txq)
+ int tx_flags, struct ath_txq *txq)
{
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
@@ -1931,10 +1937,9 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
if (tx_flags & ATH_TX_BAR)
tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
- if (!(tx_flags & (ATH_TX_ERROR | ATH_TX_XRETRY))) {
+ if (!(tx_flags & ATH_TX_ERROR))
/* Frame was ACKed */
tx_info->flags |= IEEE80211_TX_STAT_ACK;
- }
padpos = ath9k_cmn_padpos(hdr->frame_control);
padsize = padpos & 3;
@@ -1978,18 +1983,18 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts, int txok, int sendbar)
{
struct sk_buff *skb = bf->bf_mpdu;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
unsigned long flags;
int tx_flags = 0;
if (sendbar)
tx_flags = ATH_TX_BAR;
- if (!txok) {
+ if (!txok)
tx_flags |= ATH_TX_ERROR;
- if (bf_isxretried(bf))
- tx_flags |= ATH_TX_XRETRY;
- }
+ if (ts->ts_status & ATH9K_TXERR_FILT)
+ tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE);
bf->bf_buf_addr = 0;
@@ -2002,9 +2007,8 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
else
complete(&sc->paprd_complete);
} else {
- ath_debug_stat_tx(sc, bf, ts, txq);
- ath_tx_complete(sc, skb, tx_flags,
- bf->bf_state.bfs_ftype, txq);
+ ath_debug_stat_tx(sc, bf, ts, txq, tx_flags);
+ ath_tx_complete(sc, skb, tx_flags, txq);
}
/* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't
* accidentally reference it later.
@@ -2021,7 +2025,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts, int nframes, int nbad,
- int txok, bool update_rc)
+ int txok)
{
struct sk_buff *skb = bf->bf_mpdu;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
@@ -2036,19 +2040,16 @@ static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf,
tx_rateindex = ts->ts_rateindex;
WARN_ON(tx_rateindex >= hw->max_rates);
- if (ts->ts_status & ATH9K_TXERR_FILT)
- tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
- if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && update_rc) {
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
BUG_ON(nbad > nframes);
-
- tx_info->status.ampdu_len = nframes;
- tx_info->status.ampdu_ack_len = nframes - nbad;
}
+ tx_info->status.ampdu_len = nframes;
+ tx_info->status.ampdu_ack_len = nframes - nbad;
if ((ts->ts_status & ATH9K_TXERR_FILT) == 0 &&
- (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) {
+ (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) == 0) {
/*
* If an underrun error is seen assume it as an excessive
* retry only if max frame trigger level has been reached
@@ -2061,9 +2062,9 @@ static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf,
* successfully by eventually preferring slower rates.
* This itself should also alleviate congestion on the bus.
*/
- if (ieee80211_is_data(hdr->frame_control) &&
- (ts->ts_flags & (ATH9K_TX_DATA_UNDERRUN |
- ATH9K_TX_DELIM_UNDERRUN)) &&
+ if (unlikely(ts->ts_flags & (ATH9K_TX_DATA_UNDERRUN |
+ ATH9K_TX_DELIM_UNDERRUN)) &&
+ ieee80211_is_data(hdr->frame_control) &&
ah->tx_trig_level >= sc->sc_ah->config.max_txtrig_level)
tx_info->status.rates[tx_rateindex].count =
hw->max_rate_tries;
@@ -2077,6 +2078,34 @@ static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf,
tx_info->status.rates[tx_rateindex].count = ts->ts_longretry + 1;
}
+static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq,
+ struct ath_tx_status *ts, struct ath_buf *bf,
+ struct list_head *bf_head)
+ __releases(txq->axq_lock)
+ __acquires(txq->axq_lock)
+{
+ int txok;
+
+ txq->axq_depth--;
+ txok = !(ts->ts_status & ATH9K_TXERR_MASK);
+ txq->axq_tx_inprogress = false;
+ if (bf_is_ampdu_not_probing(bf))
+ txq->axq_ampdu_depth--;
+
+ spin_unlock_bh(&txq->axq_lock);
+
+ if (!bf_isampdu(bf)) {
+ ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok);
+ ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok, 0);
+ } else
+ ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok, true);
+
+ spin_lock_bh(&txq->axq_lock);
+
+ if (sc->sc_flags & SC_OP_TXAGGR)
+ ath_txq_schedule(sc, txq);
+}
+
static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_hw *ah = sc->sc_ah;
@@ -2085,20 +2114,21 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
struct list_head bf_head;
struct ath_desc *ds;
struct ath_tx_status ts;
- int txok;
int status;
ath_dbg(common, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n",
txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum),
txq->axq_link);
+ spin_lock_bh(&txq->axq_lock);
for (;;) {
- spin_lock_bh(&txq->axq_lock);
+ if (work_pending(&sc->hw_reset_work))
+ break;
+
if (list_empty(&txq->axq_q)) {
txq->axq_link = NULL;
if (sc->sc_flags & SC_OP_TXAGGR)
ath_txq_schedule(sc, txq);
- spin_unlock_bh(&txq->axq_lock);
break;
}
bf = list_first_entry(&txq->axq_q, struct ath_buf, list);
@@ -2114,13 +2144,11 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
bf_held = NULL;
if (bf->bf_stale) {
bf_held = bf;
- if (list_is_last(&bf_held->list, &txq->axq_q)) {
- spin_unlock_bh(&txq->axq_lock);
+ if (list_is_last(&bf_held->list, &txq->axq_q))
break;
- } else {
- bf = list_entry(bf_held->list.next,
- struct ath_buf, list);
- }
+
+ bf = list_entry(bf_held->list.next, struct ath_buf,
+ list);
}
lastbf = bf->bf_lastbf;
@@ -2128,10 +2156,9 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
memset(&ts, 0, sizeof(ts));
status = ath9k_hw_txprocdesc(ah, ds, &ts);
- if (status == -EINPROGRESS) {
- spin_unlock_bh(&txq->axq_lock);
+ if (status == -EINPROGRESS)
break;
- }
+
TX_STAT_INC(txq->axq_qnum, txprocdesc);
/*
@@ -2145,42 +2172,14 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
list_cut_position(&bf_head,
&txq->axq_q, lastbf->list.prev);
- txq->axq_depth--;
- txok = !(ts.ts_status & ATH9K_TXERR_MASK);
- txq->axq_tx_inprogress = false;
- if (bf_held)
+ if (bf_held) {
list_del(&bf_held->list);
-
- if (bf_is_ampdu_not_probing(bf))
- txq->axq_ampdu_depth--;
-
- spin_unlock_bh(&txq->axq_lock);
-
- if (bf_held)
ath_tx_return_buffer(sc, bf_held);
-
- if (!bf_isampdu(bf)) {
- /*
- * This frame is sent out as a single frame.
- * Use hardware retry status for this frame.
- */
- if (ts.ts_status & ATH9K_TXERR_XRETRY)
- bf->bf_state.bf_type |= BUF_XRETRY;
- ath_tx_rc_status(sc, bf, &ts, 1, txok ? 0 : 1, txok, true);
}
- if (bf_isampdu(bf))
- ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, txok,
- true);
- else
- ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, txok, 0);
-
- spin_lock_bh(&txq->axq_lock);
-
- if (sc->sc_flags & SC_OP_TXAGGR)
- ath_txq_schedule(sc, txq);
- spin_unlock_bh(&txq->axq_lock);
+ ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head);
}
+ spin_unlock_bh(&txq->axq_lock);
}
static void ath_tx_complete_poll_work(struct work_struct *work)
@@ -2213,7 +2212,8 @@ static void ath_tx_complete_poll_work(struct work_struct *work)
if (needreset) {
ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET,
"tx hung, resetting the chip\n");
- ath_reset(sc, true);
+ RESET_STAT_INC(sc, RESET_TYPE_TX_HANG);
+ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
}
ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
@@ -2237,17 +2237,19 @@ void ath_tx_tasklet(struct ath_softc *sc)
void ath_tx_edma_tasklet(struct ath_softc *sc)
{
- struct ath_tx_status txs;
+ struct ath_tx_status ts;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_hw *ah = sc->sc_ah;
struct ath_txq *txq;
struct ath_buf *bf, *lastbf;
struct list_head bf_head;
int status;
- int txok;
for (;;) {
- status = ath9k_hw_txprocdesc(ah, NULL, (void *)&txs);
+ if (work_pending(&sc->hw_reset_work))
+ break;
+
+ status = ath9k_hw_txprocdesc(ah, NULL, (void *)&ts);
if (status == -EINPROGRESS)
break;
if (status == -EIO) {
@@ -2257,12 +2259,13 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
}
/* Skip beacon completions */
- if (txs.qid == sc->beacon.beaconq)
+ if (ts.qid == sc->beacon.beaconq)
continue;
- txq = &sc->tx.txq[txs.qid];
+ txq = &sc->tx.txq[ts.qid];
spin_lock_bh(&txq->axq_lock);
+
if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) {
spin_unlock_bh(&txq->axq_lock);
return;
@@ -2275,41 +2278,21 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
INIT_LIST_HEAD(&bf_head);
list_cut_position(&bf_head, &txq->txq_fifo[txq->txq_tailidx],
&lastbf->list);
- INCR(txq->txq_tailidx, ATH_TXFIFO_DEPTH);
- txq->axq_depth--;
- txq->axq_tx_inprogress = false;
- if (bf_is_ampdu_not_probing(bf))
- txq->axq_ampdu_depth--;
- spin_unlock_bh(&txq->axq_lock);
- txok = !(txs.ts_status & ATH9K_TXERR_MASK);
-
- if (!bf_isampdu(bf)) {
- if (txs.ts_status & ATH9K_TXERR_XRETRY)
- bf->bf_state.bf_type |= BUF_XRETRY;
- ath_tx_rc_status(sc, bf, &txs, 1, txok ? 0 : 1, txok, true);
- }
-
- if (bf_isampdu(bf))
- ath_tx_complete_aggr(sc, txq, bf, &bf_head, &txs,
- txok, true);
- else
- ath_tx_complete_buf(sc, bf, txq, &bf_head,
- &txs, txok, 0);
+ if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) {
+ INCR(txq->txq_tailidx, ATH_TXFIFO_DEPTH);
- spin_lock_bh(&txq->axq_lock);
+ if (!list_empty(&txq->axq_q)) {
+ struct list_head bf_q;
- if (!list_empty(&txq->txq_fifo_pending)) {
- INIT_LIST_HEAD(&bf_head);
- bf = list_first_entry(&txq->txq_fifo_pending,
- struct ath_buf, list);
- list_cut_position(&bf_head,
- &txq->txq_fifo_pending,
- &bf->bf_lastbf->list);
- ath_tx_txqaddbuf(sc, txq, &bf_head);
- } else if (sc->sc_flags & SC_OP_TXAGGR)
- ath_txq_schedule(sc, txq);
+ INIT_LIST_HEAD(&bf_q);
+ txq->axq_link = NULL;
+ list_splice_tail_init(&txq->axq_q, &bf_q);
+ ath_tx_txqaddbuf(sc, txq, &bf_q, true);
+ }
+ }
+ ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head);
spin_unlock_bh(&txq->axq_lock);
}
}
@@ -2420,7 +2403,7 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an)
tid->sched = false;
tid->paused = false;
tid->state &= ~AGGR_CLEANUP;
- INIT_LIST_HEAD(&tid->buf_q);
+ __skb_queue_head_init(&tid->buf_q);
acno = TID_TO_WME_AC(tidno);
tid->ac = &an->ac[acno];
tid->state &= ~AGGR_ADDBA_COMPLETE;
diff --git a/drivers/net/wireless/ath/carl9170/Kconfig b/drivers/net/wireless/ath/carl9170/Kconfig
index 2d1b821b440d..267d5dcf82dc 100644
--- a/drivers/net/wireless/ath/carl9170/Kconfig
+++ b/drivers/net/wireless/ath/carl9170/Kconfig
@@ -39,3 +39,17 @@ config CARL9170_WPC
bool
depends on CARL9170 && (INPUT = y || INPUT = CARL9170)
default y
+
+config CARL9170_HWRNG
+ bool "Random number generator"
+ depends on CARL9170 && (HW_RANDOM = y || HW_RANDOM = CARL9170)
+ default n
+ help
+ Provides a hardware random number generator to the kernel.
+
+ SECURITY WARNING: It's relatively easy to eavesdrop all
+ generated random numbers from the transport stream with
+ usbmon [software] or special usb sniffer hardware.
+
+ Say N, unless your setup[i.e.: embedded system] has no
+ other rng source and you can afford to take the risk.
diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
index 4da01a9f5680..6cfbb419e2f6 100644
--- a/drivers/net/wireless/ath/carl9170/carl9170.h
+++ b/drivers/net/wireless/ath/carl9170/carl9170.h
@@ -43,6 +43,7 @@
#include <linux/firmware.h>
#include <linux/completion.h>
#include <linux/spinlock.h>
+#include <linux/hw_random.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <linux/usb.h>
@@ -67,6 +68,8 @@
#define PAYLOAD_MAX (CARL9170_MAX_CMD_LEN / 4 - 1)
+static const u8 ar9170_qmap[__AR9170_NUM_TXQ] = { 3, 2, 1, 0 };
+
enum carl9170_rf_init_mode {
CARL9170_RFI_NONE,
CARL9170_RFI_WARM,
@@ -149,6 +152,7 @@ struct carl9170_sta_tid {
#define CARL9170_TX_TIMEOUT 2500
#define CARL9170_JANITOR_DELAY 128
#define CARL9170_QUEUE_STUCK_TIMEOUT 5500
+#define CARL9170_STAT_WORK 30000
#define CARL9170_NUM_TX_AGG_MAX 30
@@ -175,7 +179,7 @@ struct carl9170_tx_queue_stats {
struct carl9170_vif {
unsigned int id;
- struct ieee80211_vif *vif;
+ struct ieee80211_vif __rcu *vif;
};
struct carl9170_vif_info {
@@ -280,6 +284,7 @@ struct ar9170 {
bool rx_stream;
bool tx_stream;
bool rx_filter;
+ bool hw_counters;
unsigned int mem_blocks;
unsigned int mem_block_size;
unsigned int rx_size;
@@ -309,7 +314,7 @@ struct ar9170 {
spinlock_t beacon_lock;
unsigned int global_pretbtt;
unsigned int global_beacon_int;
- struct carl9170_vif_info *beacon_iter;
+ struct carl9170_vif_info __rcu *beacon_iter;
unsigned int beacon_enabled;
/* cryptographic engine */
@@ -329,11 +334,21 @@ struct ar9170 {
/* PHY */
struct ieee80211_channel *channel;
+ unsigned int num_channels;
int noise[4];
unsigned int chan_fail;
unsigned int total_chan_fail;
u8 heavy_clip;
u8 ht_settings;
+ struct {
+ u64 active; /* usec */
+ u64 cca; /* usec */
+ u64 tx_time; /* usec */
+ u64 rx_total;
+ u64 rx_overrun;
+ } tally;
+ struct delayed_work stat_work;
+ struct survey_info *survey;
/* power calibration data */
u8 power_5G_leg[4];
@@ -387,7 +402,7 @@ struct ar9170 {
/* tx ampdu */
struct work_struct ampdu_work;
spinlock_t tx_ampdu_list_lock;
- struct carl9170_sta_tid *tx_ampdu_iter;
+ struct carl9170_sta_tid __rcu *tx_ampdu_iter;
struct list_head tx_ampdu_list;
atomic_t tx_ampdu_upload;
atomic_t tx_ampdu_scheduler;
@@ -435,12 +450,22 @@ struct ar9170 {
unsigned int off_override;
bool state;
} ps;
+
+#ifdef CONFIG_CARL9170_HWRNG
+# define CARL9170_HWRNG_CACHE_SIZE CARL9170_MAX_CMD_PAYLOAD_LEN
+ struct {
+ struct hwrng rng;
+ bool initialized;
+ char name[30 + 1];
+ u16 cache[CARL9170_HWRNG_CACHE_SIZE / sizeof(u16)];
+ unsigned int cache_idx;
+ } rng;
+#endif /* CONFIG_CARL9170_HWRNG */
};
enum carl9170_ps_off_override_reasons {
PS_OFF_VIF = BIT(0),
PS_OFF_BCN = BIT(1),
- PS_OFF_5GHZ = BIT(2),
};
struct carl9170_ba_stats {
@@ -455,7 +480,7 @@ struct carl9170_sta_info {
bool sleeping;
atomic_t pending_frames;
unsigned int ampdu_max_len;
- struct carl9170_sta_tid *agg[CARL9170_NUM_TID];
+ struct carl9170_sta_tid __rcu *agg[CARL9170_NUM_TID];
struct carl9170_ba_stats stats[CARL9170_NUM_TID];
};
@@ -531,7 +556,6 @@ int carl9170_set_ampdu_settings(struct ar9170 *ar);
int carl9170_set_slot_time(struct ar9170 *ar);
int carl9170_set_mac_rates(struct ar9170 *ar);
int carl9170_set_hwretry_limit(struct ar9170 *ar, const u32 max_retry);
-int carl9170_update_beacon(struct ar9170 *ar, const bool submit);
int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac,
const u8 ktype, const u8 keyidx, const u8 *keydata, const int keylen);
int carl9170_disable_key(struct ar9170 *ar, const u8 id);
@@ -552,6 +576,7 @@ void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb);
void carl9170_tx_scheduler(struct ar9170 *ar);
void carl9170_tx_get_skb(struct sk_buff *skb);
int carl9170_tx_put_skb(struct sk_buff *skb);
+int carl9170_update_beacon(struct ar9170 *ar, const bool submit);
/* LEDs */
#ifdef CONFIG_CARL9170_LEDS
diff --git a/drivers/net/wireless/ath/carl9170/cmd.c b/drivers/net/wireless/ath/carl9170/cmd.c
index cdfc94c371b4..195dc6538110 100644
--- a/drivers/net/wireless/ath/carl9170/cmd.c
+++ b/drivers/net/wireless/ath/carl9170/cmd.c
@@ -36,6 +36,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <asm/div64.h>
#include "carl9170.h"
#include "cmd.h"
@@ -165,6 +166,39 @@ int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id,
return __carl9170_exec_cmd(ar, cmd, true);
}
+int carl9170_collect_tally(struct ar9170 *ar)
+{
+ struct carl9170_tally_rsp tally;
+ struct survey_info *info;
+ unsigned int tick;
+ int err;
+
+ err = carl9170_exec_cmd(ar, CARL9170_CMD_TALLY, 0, NULL,
+ sizeof(tally), (u8 *)&tally);
+ if (err)
+ return err;
+
+ tick = le32_to_cpu(tally.tick);
+ if (tick) {
+ ar->tally.active += le32_to_cpu(tally.active) / tick;
+ ar->tally.cca += le32_to_cpu(tally.cca) / tick;
+ ar->tally.tx_time += le32_to_cpu(tally.tx_time) / tick;
+ ar->tally.rx_total += le32_to_cpu(tally.rx_total);
+ ar->tally.rx_overrun += le32_to_cpu(tally.rx_overrun);
+
+ if (ar->channel) {
+ info = &ar->survey[ar->channel->hw_value];
+ info->channel_time = ar->tally.active;
+ info->channel_time_busy = ar->tally.cca;
+ info->channel_time_tx = ar->tally.tx_time;
+ do_div(info->channel_time, 1000);
+ do_div(info->channel_time_busy, 1000);
+ do_div(info->channel_time_tx, 1000);
+ }
+ }
+ return 0;
+}
+
int carl9170_powersave(struct ar9170 *ar, const bool ps)
{
struct carl9170_cmd *cmd;
diff --git a/drivers/net/wireless/ath/carl9170/cmd.h b/drivers/net/wireless/ath/carl9170/cmd.h
index 568174c71b94..885c42778b8b 100644
--- a/drivers/net/wireless/ath/carl9170/cmd.h
+++ b/drivers/net/wireless/ath/carl9170/cmd.h
@@ -50,6 +50,7 @@ int carl9170_echo_test(struct ar9170 *ar, u32 v);
int carl9170_reboot(struct ar9170 *ar);
int carl9170_mac_reset(struct ar9170 *ar);
int carl9170_powersave(struct ar9170 *ar, const bool power_on);
+int carl9170_collect_tally(struct ar9170 *ar);
int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id,
const u32 mode, const u32 addr, const u32 len);
@@ -87,7 +88,7 @@ do { \
__ar->cmd_buf[2 * __nreg + 1] = cpu_to_le32(r); \
__ar->cmd_buf[2 * __nreg + 2] = cpu_to_le32(v); \
__nreg++; \
- if ((__nreg >= PAYLOAD_MAX/2)) { \
+ if ((__nreg >= PAYLOAD_MAX / 2)) { \
if (IS_ACCEPTING_CMD(__ar)) \
__err = carl9170_exec_cmd(__ar, \
CARL9170_CMD_WREG, 8 * __nreg, \
@@ -160,7 +161,7 @@ do { \
} while (0)
#define carl9170_async_regwrite_finish() do { \
-__async_regwrite_out : \
+__async_regwrite_out: \
if (__cmd != NULL && __err == 0) \
carl9170_async_regwrite_flush(); \
kfree(__cmd); \
diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c
index 0ac1124c2a0b..de57f90e1d5f 100644
--- a/drivers/net/wireless/ath/carl9170/debug.c
+++ b/drivers/net/wireless/ath/carl9170/debug.c
@@ -695,7 +695,7 @@ static char *carl9170_debugfs_bug_read(struct ar9170 *ar, char *buf,
}
__DEBUGFS_DECLARE_RW_FILE(bug, 400, CARL9170_STOPPED);
-static const char *erp_modes[] = {
+static const char *const erp_modes[] = {
[CARL9170_ERP_INVALID] = "INVALID",
[CARL9170_ERP_AUTO] = "Automatic",
[CARL9170_ERP_MAC80211] = "Set by MAC80211",
diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c
index 221957c5d373..cba9d0435dc4 100644
--- a/drivers/net/wireless/ath/carl9170/fw.c
+++ b/drivers/net/wireless/ath/carl9170/fw.c
@@ -23,6 +23,7 @@
#include <linux/kernel.h>
#include <linux/firmware.h>
#include <linux/crc32.h>
+#include <linux/module.h>
#include "carl9170.h"
#include "fwcmd.h"
#include "version.h"
@@ -237,7 +238,7 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
ar->disable_offload = true;
}
- if (SUPP(CARL9170FW_PSM))
+ if (SUPP(CARL9170FW_PSM) && SUPP(CARL9170FW_FIXED_5GHZ_PSM))
ar->hw->flags |= IEEE80211_HW_SUPPORTS_PS;
if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) {
@@ -266,6 +267,9 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
FIF_PROMISC_IN_BSS;
}
+ if (SUPP(CARL9170FW_HW_COUNTERS))
+ ar->fw.hw_counters = true;
+
if (SUPP(CARL9170FW_WOL))
device_set_wakeup_enable(&ar->udev->dev, true);
diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h
index 30449d21b762..9443c802b25b 100644
--- a/drivers/net/wireless/ath/carl9170/fwcmd.h
+++ b/drivers/net/wireless/ath/carl9170/fwcmd.h
@@ -4,7 +4,7 @@
* Firmware command interface definitions
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.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
@@ -54,6 +54,8 @@ enum carl9170_cmd_oids {
CARL9170_CMD_BCN_CTRL = 0x05,
CARL9170_CMD_READ_TSF = 0x06,
CARL9170_CMD_RX_FILTER = 0x07,
+ CARL9170_CMD_WOL = 0x08,
+ CARL9170_CMD_TALLY = 0x09,
/* CAM */
CARL9170_CMD_EKEY = 0x10,
@@ -180,6 +182,21 @@ struct carl9170_bcn_ctrl_cmd {
#define CARL9170_BCN_CTRL_DRAIN 0
#define CARL9170_BCN_CTRL_CAB_TRIGGER 1
+struct carl9170_wol_cmd {
+ __le32 flags;
+ u8 mac[6];
+ u8 bssid[6];
+ __le32 null_interval;
+ __le32 free_for_use2;
+ __le32 mask;
+ u8 pattern[32];
+} __packed;
+
+#define CARL9170_WOL_CMD_SIZE 60
+
+#define CARL9170_WOL_DISCONNECT 1
+#define CARL9170_WOL_MAGIC_PKT 2
+
struct carl9170_cmd_head {
union {
struct {
@@ -203,6 +220,7 @@ struct carl9170_cmd {
struct carl9170_write_reg wreg;
struct carl9170_rf_init rf_init;
struct carl9170_psm psm;
+ struct carl9170_wol_cmd wol;
struct carl9170_bcn_ctrl_cmd bcn_ctrl;
struct carl9170_rx_filter_cmd rx_filter;
u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN];
@@ -269,6 +287,15 @@ struct carl9170_tsf_rsp {
} __packed;
#define CARL9170_TSF_RSP_SIZE 8
+struct carl9170_tally_rsp {
+ __le32 active;
+ __le32 cca;
+ __le32 tx_time;
+ __le32 rx_total;
+ __le32 rx_overrun;
+ __le32 tick;
+} __packed;
+
struct carl9170_rsp {
struct carl9170_cmd_head hdr;
@@ -283,6 +310,7 @@ struct carl9170_rsp {
struct carl9170_gpio gpio;
struct carl9170_tsf_rsp tsf;
struct carl9170_psm psm;
+ struct carl9170_tally_rsp tally;
u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN];
} __packed;
} __packed __aligned(4);
diff --git a/drivers/net/wireless/ath/carl9170/fwdesc.h b/drivers/net/wireless/ath/carl9170/fwdesc.h
index 921066822dd5..6d9c0891ce7f 100644
--- a/drivers/net/wireless/ath/carl9170/fwdesc.h
+++ b/drivers/net/wireless/ath/carl9170/fwdesc.h
@@ -3,7 +3,7 @@
*
* Firmware descriptor format
*
- * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.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
@@ -72,6 +72,12 @@ enum carl9170fw_feature_list {
/* Wake up on WLAN */
CARL9170FW_WOL,
+ /* Firmware supports PSM in the 5GHZ Band */
+ CARL9170FW_FIXED_5GHZ_PSM,
+
+ /* HW (ANI, CCA, MIB) tally counters */
+ CARL9170FW_HW_COUNTERS,
+
/* KEEP LAST */
__CARL9170FW_FEATURE_NUM
};
@@ -82,6 +88,7 @@ enum carl9170fw_feature_list {
#define DBG_MAGIC "DBG\0"
#define CHK_MAGIC "CHK\0"
#define TXSQ_MAGIC "TXSQ"
+#define WOL_MAGIC "WOL\0"
#define LAST_MAGIC "LAST"
#define CARL9170FW_SET_DAY(d) (((d) - 1) % 31)
@@ -104,7 +111,7 @@ struct carl9170fw_desc_head {
(sizeof(struct carl9170fw_desc_head))
#define CARL9170FW_OTUS_DESC_MIN_VER 6
-#define CARL9170FW_OTUS_DESC_CUR_VER 6
+#define CARL9170FW_OTUS_DESC_CUR_VER 7
struct carl9170fw_otus_desc {
struct carl9170fw_desc_head head;
__le32 feature_set;
@@ -186,6 +193,16 @@ struct carl9170fw_txsq_desc {
#define CARL9170FW_TXSQ_DESC_SIZE \
(sizeof(struct carl9170fw_txsq_desc))
+#define CARL9170FW_WOL_DESC_MIN_VER 1
+#define CARL9170FW_WOL_DESC_CUR_VER 1
+struct carl9170fw_wol_desc {
+ struct carl9170fw_desc_head head;
+
+ __le32 supported_triggers; /* CARL9170_WOL_ */
+} __packed;
+#define CARL9170FW_WOL_DESC_SIZE \
+ (sizeof(struct carl9170fw_wol_desc))
+
#define CARL9170FW_LAST_DESC_MIN_VER 1
#define CARL9170FW_LAST_DESC_CUR_VER 2
struct carl9170fw_last_desc {
diff --git a/drivers/net/wireless/ath/carl9170/hw.h b/drivers/net/wireless/ath/carl9170/hw.h
index 4e30762dd903..fa834c1460f0 100644
--- a/drivers/net/wireless/ath/carl9170/hw.h
+++ b/drivers/net/wireless/ath/carl9170/hw.h
@@ -4,7 +4,7 @@
* Register map, hardware-specific definitions
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.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
@@ -174,6 +174,7 @@
#define AR9170_MAC_SNIFFER_ENABLE_PROMISC BIT(0)
#define AR9170_MAC_SNIFFER_DEFAULTS 0x02000000
#define AR9170_MAC_REG_ENCRYPTION (AR9170_MAC_REG_BASE + 0x678)
+#define AR9170_MAC_ENCRYPTION_MGMT_RX_SOFTWARE BIT(2)
#define AR9170_MAC_ENCRYPTION_RX_SOFTWARE BIT(3)
#define AR9170_MAC_ENCRYPTION_DEFAULTS 0x70
@@ -222,6 +223,12 @@
#define AR9170_MAC_REG_TX_BLOCKACKS (AR9170_MAC_REG_BASE + 0x6c0)
#define AR9170_MAC_REG_NAV_COUNT (AR9170_MAC_REG_BASE + 0x6c4)
#define AR9170_MAC_REG_BACKOFF_STATUS (AR9170_MAC_REG_BASE + 0x6c8)
+#define AR9170_MAC_BACKOFF_CCA BIT(24)
+#define AR9170_MAC_BACKOFF_TX_PEX BIT(25)
+#define AR9170_MAC_BACKOFF_RX_PE BIT(26)
+#define AR9170_MAC_BACKOFF_MD_READY BIT(27)
+#define AR9170_MAC_BACKOFF_TX_PE BIT(28)
+
#define AR9170_MAC_REG_TX_RETRY (AR9170_MAC_REG_BASE + 0x6cc)
#define AR9170_MAC_REG_TX_COMPLETE (AR9170_MAC_REG_BASE + 0x6d4)
@@ -357,7 +364,18 @@
#define AR9170_MAC_REG_DMA_WLAN_STATUS (AR9170_MAC_REG_BASE + 0xd38)
#define AR9170_MAC_REG_DMA_STATUS (AR9170_MAC_REG_BASE + 0xd3c)
-
+#define AR9170_MAC_REG_DMA_TXQ_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd40)
+#define AR9170_MAC_REG_DMA_TXQ0_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd40)
+#define AR9170_MAC_REG_DMA_TXQ1_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd44)
+#define AR9170_MAC_REG_DMA_TXQ2_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd48)
+#define AR9170_MAC_REG_DMA_TXQ3_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd4c)
+#define AR9170_MAC_REG_DMA_TXQ4_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd50)
+#define AR9170_MAC_REG_DMA_TXQ0Q1_LEN (AR9170_MAC_REG_BASE + 0xd54)
+#define AR9170_MAC_REG_DMA_TXQ2Q3_LEN (AR9170_MAC_REG_BASE + 0xd58)
+#define AR9170_MAC_REG_DMA_TXQ4_LEN (AR9170_MAC_REG_BASE + 0xd5c)
+
+#define AR9170_MAC_REG_DMA_TXQX_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd74)
+#define AR9170_MAC_REG_DMA_TXQX_FAIL_ADDR (AR9170_MAC_REG_BASE + 0xd78)
#define AR9170_MAC_REG_TXRX_MPI (AR9170_MAC_REG_BASE + 0xd7c)
#define AR9170_MAC_TXRX_MPI_TX_MPI_MASK 0x0000000f
#define AR9170_MAC_TXRX_MPI_TX_TO_MASK 0x0000fff0
@@ -377,10 +395,40 @@
#define AR9170_MAC_REG_BCN_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd98)
#define AR9170_MAC_REG_BCN_COUNT (AR9170_MAC_REG_BASE + 0xd9c)
-
-
#define AR9170_MAC_REG_BCN_HT1 (AR9170_MAC_REG_BASE + 0xda0)
+#define AR9170_MAC_BCN_HT1_HT_EN BIT(0)
+#define AR9170_MAC_BCN_HT1_GF_PMB BIT(1)
+#define AR9170_MAC_BCN_HT1_SP_EXP BIT(2)
+#define AR9170_MAC_BCN_HT1_TX_BF BIT(3)
+#define AR9170_MAC_BCN_HT1_PWR_CTRL_S 4
+#define AR9170_MAC_BCN_HT1_PWR_CTRL 0x70
+#define AR9170_MAC_BCN_HT1_TX_ANT1 BIT(7)
+#define AR9170_MAC_BCN_HT1_TX_ANT0 BIT(8)
+#define AR9170_MAC_BCN_HT1_NUM_LFT_S 9
+#define AR9170_MAC_BCN_HT1_NUM_LFT 0x600
+#define AR9170_MAC_BCN_HT1_BWC_20M_EXT BIT(16)
+#define AR9170_MAC_BCN_HT1_BWC_40M_SHARED BIT(17)
+#define AR9170_MAC_BCN_HT1_BWC_40M_DUP (BIT(16) | BIT(17))
+#define AR9170_MAC_BCN_HT1_BF_MCS_S 18
+#define AR9170_MAC_BCN_HT1_BF_MCS 0x1c0000
+#define AR9170_MAC_BCN_HT1_TPC_S 21
+#define AR9170_MAC_BCN_HT1_TPC 0x7e00000
+#define AR9170_MAC_BCN_HT1_CHAIN_MASK_S 27
+#define AR9170_MAC_BCN_HT1_CHAIN_MASK 0x38000000
+
#define AR9170_MAC_REG_BCN_HT2 (AR9170_MAC_REG_BASE + 0xda4)
+#define AR9170_MAC_BCN_HT2_MCS_S 0
+#define AR9170_MAC_BCN_HT2_MCS 0x7f
+#define AR9170_MAC_BCN_HT2_BW40 BIT(8)
+#define AR9170_MAC_BCN_HT2_SMOOTHING BIT(9)
+#define AR9170_MAC_BCN_HT2_SS BIT(10)
+#define AR9170_MAC_BCN_HT2_NSS BIT(11)
+#define AR9170_MAC_BCN_HT2_STBC_S 12
+#define AR9170_MAC_BCN_HT2_STBC 0x3000
+#define AR9170_MAC_BCN_HT2_ADV_COD BIT(14)
+#define AR9170_MAC_BCN_HT2_SGI BIT(15)
+#define AR9170_MAC_BCN_HT2_LEN_S 16
+#define AR9170_MAC_BCN_HT2_LEN 0xffff0000
#define AR9170_MAC_REG_DMA_TXQX_ADDR_CURR (AR9170_MAC_REG_BASE + 0xdc0)
diff --git a/drivers/net/wireless/ath/carl9170/led.c b/drivers/net/wireless/ath/carl9170/led.c
index 4bb2cbd8bd9b..78dadc797558 100644
--- a/drivers/net/wireless/ath/carl9170/led.c
+++ b/drivers/net/wireless/ath/carl9170/led.c
@@ -118,7 +118,7 @@ static void carl9170_led_set_brightness(struct led_classdev *led,
}
if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled))
- ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ/10);
+ ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ / 10);
}
static int carl9170_led_register_led(struct ar9170 *ar, int i, char *name,
diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c
index 385cf508479b..dfda91970995 100644
--- a/drivers/net/wireless/ath/carl9170/mac.c
+++ b/drivers/net/wireless/ath/carl9170/mac.c
@@ -455,135 +455,6 @@ int carl9170_set_beacon_timers(struct ar9170 *ar)
return carl9170_regwrite_result();
}
-int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
-{
- struct sk_buff *skb = NULL;
- struct carl9170_vif_info *cvif;
- struct ieee80211_tx_info *txinfo;
- __le32 *data, *old = NULL;
- u32 word, off, addr, len;
- int i = 0, err = 0;
-
- rcu_read_lock();
- cvif = rcu_dereference(ar->beacon_iter);
-retry:
- if (ar->vifs == 0 || !cvif)
- goto out_unlock;
-
- list_for_each_entry_continue_rcu(cvif, &ar->vif_list, list) {
- if (cvif->active && cvif->enable_beacon)
- goto found;
- }
-
- if (!ar->beacon_enabled || i++)
- goto out_unlock;
-
- goto retry;
-
-found:
- rcu_assign_pointer(ar->beacon_iter, cvif);
-
- skb = ieee80211_beacon_get_tim(ar->hw, carl9170_get_vif(cvif),
- NULL, NULL);
-
- if (!skb) {
- err = -ENOMEM;
- goto err_free;
- }
-
- txinfo = IEEE80211_SKB_CB(skb);
- if (txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS) {
- err = -EINVAL;
- goto err_free;
- }
-
- spin_lock_bh(&ar->beacon_lock);
- data = (__le32 *)skb->data;
- if (cvif->beacon)
- old = (__le32 *)cvif->beacon->data;
-
- off = cvif->id * AR9170_MAC_BCN_LENGTH_MAX;
- addr = ar->fw.beacon_addr + off;
- len = roundup(skb->len + FCS_LEN, 4);
-
- if ((off + len) > ar->fw.beacon_max_len) {
- if (net_ratelimit()) {
- wiphy_err(ar->hw->wiphy, "beacon does not "
- "fit into device memory!\n");
- }
- err = -EINVAL;
- goto err_unlock;
- }
-
- if (len > AR9170_MAC_BCN_LENGTH_MAX) {
- if (net_ratelimit()) {
- wiphy_err(ar->hw->wiphy, "no support for beacons "
- "bigger than %d (yours:%d).\n",
- AR9170_MAC_BCN_LENGTH_MAX, len);
- }
-
- err = -EMSGSIZE;
- goto err_unlock;
- }
-
- i = txinfo->control.rates[0].idx;
- if (txinfo->band != IEEE80211_BAND_2GHZ)
- i += 4;
-
- word = __carl9170_ratetable[i].hw_value & 0xf;
- if (i < 4)
- word |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400;
- else
- word |= ((skb->len + FCS_LEN) << 16) + 0x0010;
-
- carl9170_async_regwrite_begin(ar);
- carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, word);
-
- for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
- /*
- * XXX: This accesses beyond skb data for up
- * to the last 3 bytes!!
- */
-
- if (old && (data[i] == old[i]))
- continue;
-
- word = le32_to_cpu(data[i]);
- carl9170_async_regwrite(addr + 4 * i, word);
- }
- carl9170_async_regwrite_finish();
-
- dev_kfree_skb_any(cvif->beacon);
- cvif->beacon = NULL;
-
- err = carl9170_async_regwrite_result();
- if (!err)
- cvif->beacon = skb;
- spin_unlock_bh(&ar->beacon_lock);
- if (err)
- goto err_free;
-
- if (submit) {
- err = carl9170_bcn_ctrl(ar, cvif->id,
- CARL9170_BCN_CTRL_CAB_TRIGGER,
- addr, skb->len + FCS_LEN);
-
- if (err)
- goto err_free;
- }
-out_unlock:
- rcu_read_unlock();
- return 0;
-
-err_unlock:
- spin_unlock_bh(&ar->beacon_lock);
-
-err_free:
- rcu_read_unlock();
- dev_kfree_skb_any(skb);
- return err;
-}
-
int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac,
const u8 ktype, const u8 keyidx, const u8 *keydata,
const int keylen)
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index 54d093c2ab44..f06e0695d412 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -345,11 +345,11 @@ static int carl9170_op_start(struct ieee80211_hw *hw)
carl9170_zap_queues(ar);
/* reset QoS defaults */
- CARL9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023, 0); /* BEST EFFORT */
- CARL9170_FILL_QUEUE(ar->edcf[1], 2, 7, 15, 94); /* VIDEO */
- CARL9170_FILL_QUEUE(ar->edcf[2], 2, 3, 7, 47); /* VOICE */
- CARL9170_FILL_QUEUE(ar->edcf[3], 7, 15, 1023, 0); /* BACKGROUND */
- CARL9170_FILL_QUEUE(ar->edcf[4], 2, 3, 7, 0); /* SPECIAL */
+ CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_VO], 2, 3, 7, 47);
+ CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_VI], 2, 7, 15, 94);
+ CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_BE], 3, 15, 1023, 0);
+ CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_BK], 7, 15, 1023, 0);
+ CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_SPECIAL], 2, 3, 7, 0);
ar->current_factor = ar->current_density = -1;
/* "The first key is unique." */
@@ -413,6 +413,9 @@ static int carl9170_op_start(struct ieee80211_hw *hw)
carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STARTED);
+ ieee80211_queue_delayed_work(ar->hw, &ar->stat_work,
+ round_jiffies(msecs_to_jiffies(CARL9170_STAT_WORK)));
+
ieee80211_wake_queues(ar->hw);
err = 0;
@@ -423,6 +426,7 @@ out:
static void carl9170_cancel_worker(struct ar9170 *ar)
{
+ cancel_delayed_work_sync(&ar->stat_work);
cancel_delayed_work_sync(&ar->tx_janitor);
#ifdef CONFIG_CARL9170_LEDS
cancel_delayed_work_sync(&ar->led_work);
@@ -794,6 +798,43 @@ static void carl9170_ps_work(struct work_struct *work)
mutex_unlock(&ar->mutex);
}
+static int carl9170_update_survey(struct ar9170 *ar, bool flush, bool noise)
+{
+ int err;
+
+ if (noise) {
+ err = carl9170_get_noisefloor(ar);
+ if (err)
+ return err;
+ }
+
+ if (ar->fw.hw_counters) {
+ err = carl9170_collect_tally(ar);
+ if (err)
+ return err;
+ }
+
+ if (flush)
+ memset(&ar->tally, 0, sizeof(ar->tally));
+
+ return 0;
+}
+
+static void carl9170_stat_work(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170, stat_work.work);
+ int err;
+
+ mutex_lock(&ar->mutex);
+ err = carl9170_update_survey(ar, false, true);
+ mutex_unlock(&ar->mutex);
+
+ if (err)
+ return;
+
+ ieee80211_queue_delayed_work(ar->hw, &ar->stat_work,
+ round_jiffies(msecs_to_jiffies(CARL9170_STAT_WORK)));
+}
static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed)
{
@@ -828,11 +869,19 @@ static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed)
if (err)
goto out;
+ err = carl9170_update_survey(ar, true, false);
+ if (err)
+ goto out;
+
err = carl9170_set_channel(ar, hw->conf.channel,
hw->conf.channel_type, CARL9170_RFI_NONE);
if (err)
goto out;
+ err = carl9170_update_survey(ar, false, true);
+ if (err)
+ goto out;
+
err = carl9170_set_dyn_sifs_ack(ar);
if (err)
goto out;
@@ -1029,7 +1078,8 @@ out:
mutex_unlock(&ar->mutex);
}
-static u64 carl9170_op_get_tsf(struct ieee80211_hw *hw)
+static u64 carl9170_op_get_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct ar9170 *ar = hw->priv;
struct carl9170_tsf_rsp tsf;
@@ -1066,8 +1116,10 @@ static int carl9170_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
* the high througput speed in 802.11n networks.
*/
- if (!is_main_vif(ar, vif))
+ if (!is_main_vif(ar, vif)) {
+ mutex_lock(&ar->mutex);
goto err_softw;
+ }
/*
* While the hardware supports *catch-all* key, for offloading
@@ -1253,7 +1305,8 @@ static int carl9170_op_sta_remove(struct ieee80211_hw *hw,
return 0;
}
-static int carl9170_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
+static int carl9170_op_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *param)
{
struct ar9170 *ar = hw->priv;
@@ -1419,24 +1472,159 @@ static int carl9170_register_wps_button(struct ar9170 *ar)
}
#endif /* CONFIG_CARL9170_WPC */
-static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx,
- struct survey_info *survey)
+#ifdef CONFIG_CARL9170_HWRNG
+static int carl9170_rng_get(struct ar9170 *ar)
{
- struct ar9170 *ar = hw->priv;
+
+#define RW (CARL9170_MAX_CMD_PAYLOAD_LEN / sizeof(u32))
+#define RB (CARL9170_MAX_CMD_PAYLOAD_LEN)
+
+ static const __le32 rng_load[RW] = {
+ [0 ... (RW - 1)] = cpu_to_le32(AR9170_RAND_REG_NUM)};
+
+ u32 buf[RW];
+
+ unsigned int i, off = 0, transfer, count;
int err;
- if (idx != 0)
- return -ENOENT;
+ BUILD_BUG_ON(RB > CARL9170_MAX_CMD_PAYLOAD_LEN);
+
+ if (!IS_ACCEPTING_CMD(ar) || !ar->rng.initialized)
+ return -EAGAIN;
+
+ count = ARRAY_SIZE(ar->rng.cache);
+ while (count) {
+ err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG,
+ RB, (u8 *) rng_load,
+ RB, (u8 *) buf);
+ if (err)
+ return err;
+
+ transfer = min_t(unsigned int, count, RW);
+ for (i = 0; i < transfer; i++)
+ ar->rng.cache[off + i] = buf[i];
+
+ off += transfer;
+ count -= transfer;
+ }
+
+ ar->rng.cache_idx = 0;
+
+#undef RW
+#undef RB
+ return 0;
+}
+
+static int carl9170_rng_read(struct hwrng *rng, u32 *data)
+{
+ struct ar9170 *ar = (struct ar9170 *)rng->priv;
+ int ret = -EIO;
mutex_lock(&ar->mutex);
- err = carl9170_get_noisefloor(ar);
+ if (ar->rng.cache_idx >= ARRAY_SIZE(ar->rng.cache)) {
+ ret = carl9170_rng_get(ar);
+ if (ret) {
+ mutex_unlock(&ar->mutex);
+ return ret;
+ }
+ }
+
+ *data = ar->rng.cache[ar->rng.cache_idx++];
mutex_unlock(&ar->mutex);
- if (err)
+
+ return sizeof(u16);
+}
+
+static void carl9170_unregister_hwrng(struct ar9170 *ar)
+{
+ if (ar->rng.initialized) {
+ hwrng_unregister(&ar->rng.rng);
+ ar->rng.initialized = false;
+ }
+}
+
+static int carl9170_register_hwrng(struct ar9170 *ar)
+{
+ int err;
+
+ snprintf(ar->rng.name, ARRAY_SIZE(ar->rng.name),
+ "%s_%s", KBUILD_MODNAME, wiphy_name(ar->hw->wiphy));
+ ar->rng.rng.name = ar->rng.name;
+ ar->rng.rng.data_read = carl9170_rng_read;
+ ar->rng.rng.priv = (unsigned long)ar;
+
+ if (WARN_ON(ar->rng.initialized))
+ return -EALREADY;
+
+ err = hwrng_register(&ar->rng.rng);
+ if (err) {
+ dev_err(&ar->udev->dev, "Failed to register the random "
+ "number generator (%d)\n", err);
+ return err;
+ }
+
+ ar->rng.initialized = true;
+
+ err = carl9170_rng_get(ar);
+ if (err) {
+ carl9170_unregister_hwrng(ar);
return err;
+ }
- survey->channel = ar->channel;
+ return 0;
+}
+#endif /* CONFIG_CARL9170_HWRNG */
+
+static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx,
+ struct survey_info *survey)
+{
+ struct ar9170 *ar = hw->priv;
+ struct ieee80211_channel *chan;
+ struct ieee80211_supported_band *band;
+ int err, b, i;
+
+ chan = ar->channel;
+ if (!chan)
+ return -ENODEV;
+
+ if (idx == chan->hw_value) {
+ mutex_lock(&ar->mutex);
+ err = carl9170_update_survey(ar, false, true);
+ mutex_unlock(&ar->mutex);
+ if (err)
+ return err;
+ }
+
+ for (b = 0; b < IEEE80211_NUM_BANDS; b++) {
+ band = ar->hw->wiphy->bands[b];
+
+ if (!band)
+ continue;
+
+ for (i = 0; i < band->n_channels; i++) {
+ if (band->channels[i].hw_value == idx) {
+ chan = &band->channels[i];
+ goto found;
+ }
+ }
+ }
+ return -ENOENT;
+
+found:
+ memcpy(survey, &ar->survey[idx], sizeof(*survey));
+
+ survey->channel = chan;
survey->filled = SURVEY_INFO_NOISE_DBM;
- survey->noise = ar->noise[0];
+
+ if (ar->channel == chan)
+ survey->filled |= SURVEY_INFO_IN_USE;
+
+ if (ar->fw.hw_counters) {
+ survey->filled |= SURVEY_INFO_CHANNEL_TIME |
+ SURVEY_INFO_CHANNEL_TIME_BUSY |
+ SURVEY_INFO_CHANNEL_TIME_TX;
+ }
+
return 0;
}
@@ -1484,6 +1672,13 @@ static void carl9170_op_sta_notify(struct ieee80211_hw *hw,
}
}
+static bool carl9170_tx_frames_pending(struct ieee80211_hw *hw)
+{
+ struct ar9170 *ar = hw->priv;
+
+ return !!atomic_read(&ar->tx_total_queued);
+}
+
static const struct ieee80211_ops carl9170_ops = {
.start = carl9170_op_start,
.stop = carl9170_op_stop,
@@ -1504,6 +1699,7 @@ static const struct ieee80211_ops carl9170_ops = {
.get_survey = carl9170_op_get_survey,
.get_stats = carl9170_op_get_stats,
.ampdu_action = carl9170_op_ampdu_action,
+ .tx_frames_pending = carl9170_tx_frames_pending,
};
void *carl9170_alloc(size_t priv_size)
@@ -1561,6 +1757,7 @@ void *carl9170_alloc(size_t priv_size)
INIT_WORK(&ar->ping_work, carl9170_ping_work);
INIT_WORK(&ar->restart_work, carl9170_restart_work);
INIT_WORK(&ar->ampdu_work, carl9170_ampdu_work);
+ INIT_DELAYED_WORK(&ar->stat_work, carl9170_stat_work);
INIT_DELAYED_WORK(&ar->tx_janitor, carl9170_tx_janitor);
INIT_LIST_HEAD(&ar->tx_ampdu_list);
rcu_assign_pointer(ar->tx_ampdu_iter,
@@ -1577,6 +1774,7 @@ void *carl9170_alloc(size_t priv_size)
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK |
+ IEEE80211_HW_NEED_DTIM_PERIOD |
IEEE80211_HW_SIGNAL_DBM;
if (!modparam_noht) {
@@ -1621,7 +1819,7 @@ static int carl9170_read_eeprom(struct ar9170 *ar)
BUILD_BUG_ON(sizeof(ar->eeprom) % RB);
#endif
- for (i = 0; i < sizeof(ar->eeprom)/RB; i++) {
+ for (i = 0; i < sizeof(ar->eeprom) / RB; i++) {
for (j = 0; j < RW; j++)
offsets[j] = cpu_to_le32(AR9170_EEPROM_START +
RB * i + 4 * j);
@@ -1643,6 +1841,7 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
struct ath_regulatory *regulatory = &ar->common.regulatory;
unsigned int rx_streams, tx_streams, tx_params = 0;
int bands = 0;
+ int chans = 0;
if (ar->eeprom.length == cpu_to_le16(0xffff))
return -ENODATA;
@@ -1666,14 +1865,24 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) {
ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
&carl9170_band_2GHz;
+ chans += carl9170_band_2GHz.n_channels;
bands++;
}
if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) {
ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
&carl9170_band_5GHz;
+ chans += carl9170_band_5GHz.n_channels;
bands++;
}
+ if (!bands)
+ return -EINVAL;
+
+ ar->survey = kzalloc(sizeof(struct survey_info) * chans, GFP_KERNEL);
+ if (!ar->survey)
+ return -ENOMEM;
+ ar->num_channels = chans;
+
/*
* I measured this, a bandswitch takes roughly
* 135 ms and a frequency switch about 80.
@@ -1687,12 +1896,11 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
ar->hw->channel_change_time = 80 * 1000;
regulatory->current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]);
- regulatory->current_rd_ext = le16_to_cpu(ar->eeprom.reg_domain[1]);
/* second part of wiphy init */
SET_IEEE80211_PERM_ADDR(ar->hw, ar->eeprom.mac_address);
- return bands ? 0 : -EINVAL;
+ return 0;
}
static int carl9170_reg_notifier(struct wiphy *wiphy,
@@ -1776,6 +1984,12 @@ int carl9170_register(struct ar9170 *ar)
goto err_unreg;
#endif /* CONFIG_CARL9170_WPC */
+#ifdef CONFIG_CARL9170_HWRNG
+ err = carl9170_register_hwrng(ar);
+ if (err)
+ goto err_unreg;
+#endif /* CONFIG_CARL9170_HWRNG */
+
dev_info(&ar->udev->dev, "Atheros AR9170 is registered as '%s'\n",
wiphy_name(ar->hw->wiphy));
@@ -1808,6 +2022,10 @@ void carl9170_unregister(struct ar9170 *ar)
}
#endif /* CONFIG_CARL9170_WPC */
+#ifdef CONFIG_CARL9170_HWRNG
+ carl9170_unregister_hwrng(ar);
+#endif /* CONFIG_CARL9170_HWRNG */
+
carl9170_cancel_worker(ar);
cancel_work_sync(&ar->restart_work);
@@ -1825,6 +2043,9 @@ void carl9170_free(struct ar9170 *ar)
kfree(ar->mem_bitmap);
ar->mem_bitmap = NULL;
+ kfree(ar->survey);
+ ar->survey = NULL;
+
mutex_destroy(&ar->mutex);
ieee80211_free_hw(ar->hw);
diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
index b6ae0e179c8d..472efc7e3402 100644
--- a/drivers/net/wireless/ath/carl9170/phy.c
+++ b/drivers/net/wireless/ath/carl9170/phy.c
@@ -578,11 +578,10 @@ static int carl9170_init_phy(struct ar9170 *ar, enum ieee80211_band band)
if (err)
return err;
- /* XXX: remove magic! */
- if (is_2ghz)
- err = carl9170_write_reg(ar, AR9170_PWR_REG_PLL_ADDAC, 0x5163);
- else
- err = carl9170_write_reg(ar, AR9170_PWR_REG_PLL_ADDAC, 0x5143);
+ if (!ar->fw.hw_counters) {
+ err = carl9170_write_reg(ar, AR9170_PWR_REG_PLL_ADDAC,
+ is_2ghz ? 0x5163 : 0x5143);
+ }
return err;
}
@@ -1098,7 +1097,7 @@ static u8 carl9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2)
* Isn't it just DIV_ROUND_UP(y, 1<<SHIFT)?
* Can we rely on the compiler to optimise away the div?
*/
- return (y >> SHIFT) + ((y & (1<<(SHIFT-1))) >> (SHIFT - 1));
+ return (y >> SHIFT) + ((y & (1 << (SHIFT - 1))) >> (SHIFT - 1));
#undef SHIFT
}
@@ -1379,7 +1378,7 @@ static void carl9170_calc_ctl(struct ar9170 *ar, u32 freq, enum carl9170_bw bw)
modes[i].max_power =
carl9170_get_max_edge_power(ar,
- freq+f_off, EDGES(ctl_idx, 1));
+ freq + f_off, EDGES(ctl_idx, 1));
/*
* TODO: check if the regulatory max. power is
@@ -1441,7 +1440,7 @@ static int carl9170_set_power_cal(struct ar9170 *ar, u32 freq,
if (freq < 3000)
f = freq - 2300;
else
- f = (freq - 4800)/5;
+ f = (freq - 4800) / 5;
/*
* cycle through the various modes
@@ -1574,6 +1573,9 @@ int carl9170_get_noisefloor(struct ar9170 *ar)
AR9170_PHY_EXT_CCA_MIN_PWR, phy_res[i + 2]), 8);
}
+ if (ar->channel)
+ ar->survey[ar->channel->hw_value].noise = ar->noise[0];
+
return 0;
}
@@ -1766,10 +1768,6 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
ar->chan_fail = 0;
}
- err = carl9170_get_noisefloor(ar);
- if (err)
- return err;
-
if (ar->heavy_clip) {
err = carl9170_write_reg(ar, AR9170_PHY_REG_HEAVY_CLIP_ENABLE,
0x200 | ar->heavy_clip);
@@ -1783,12 +1781,6 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
}
}
- /* FIXME: PSM does not work in 5GHz Band */
- if (channel->band == IEEE80211_BAND_5GHZ)
- ar->ps.off_override |= PS_OFF_5GHZ;
- else
- ar->ps.off_override &= ~PS_OFF_5GHZ;
-
ar->channel = channel;
ar->ht_settings = new_ht;
return 0;
diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
index ec21ea9fd8d5..dc99030ea8b6 100644
--- a/drivers/net/wireless/ath/carl9170/rx.c
+++ b/drivers/net/wireless/ath/carl9170/rx.c
@@ -472,7 +472,7 @@ static struct sk_buff *carl9170_rx_copy_data(u8 *buf, int len)
u8 *qc = ieee80211_get_qos_ctl(hdr);
reserved += NET_IP_ALIGN;
- if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
+ if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
reserved += NET_IP_ALIGN;
}
diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c
index e94084fcf6f5..59472e1605cd 100644
--- a/drivers/net/wireless/ath/carl9170/tx.c
+++ b/drivers/net/wireless/ath/carl9170/tx.c
@@ -296,7 +296,8 @@ static void carl9170_tx_release(struct kref *ref)
super = (void *)skb->data;
txinfo->status.ampdu_len = super->s.rix;
txinfo->status.ampdu_ack_len = super->s.cnt;
- } else if (txinfo->flags & IEEE80211_TX_STAT_ACK) {
+ } else if ((txinfo->flags & IEEE80211_TX_STAT_ACK) &&
+ !(txinfo->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) {
/*
* drop redundant tx_status reports:
*
@@ -308,15 +309,17 @@ static void carl9170_tx_release(struct kref *ref)
*
* 3. minstrel_ht is picky, it only accepts
* reports of frames with the TX_STATUS_AMPDU flag.
+ *
+ * 4. mac80211 is not particularly interested in
+ * feedback either [CTL_REQ_TX_STATUS not set]
*/
dev_kfree_skb_any(skb);
return;
} else {
/*
- * Frame has failed, but we want to keep it in
- * case it was lost due to a power-state
- * transition.
+ * Either the frame transmission has failed or
+ * mac80211 requested tx status.
*/
}
}
@@ -661,11 +664,67 @@ void carl9170_tx_process_status(struct ar9170 *ar,
}
}
+static void carl9170_tx_rate_tpc_chains(struct ar9170 *ar,
+ struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate,
+ unsigned int *phyrate, unsigned int *tpc, unsigned int *chains)
+{
+ struct ieee80211_rate *rate = NULL;
+ u8 *txpower;
+ unsigned int idx;
+
+ idx = txrate->idx;
+ *tpc = 0;
+ *phyrate = 0;
+
+ if (txrate->flags & IEEE80211_TX_RC_MCS) {
+ if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+ /* +1 dBm for HT40 */
+ *tpc += 2;
+
+ if (info->band == IEEE80211_BAND_2GHZ)
+ txpower = ar->power_2G_ht40;
+ else
+ txpower = ar->power_5G_ht40;
+ } else {
+ if (info->band == IEEE80211_BAND_2GHZ)
+ txpower = ar->power_2G_ht20;
+ else
+ txpower = ar->power_5G_ht20;
+ }
+
+ *phyrate = txrate->idx;
+ *tpc += txpower[idx & 7];
+ } else {
+ if (info->band == IEEE80211_BAND_2GHZ) {
+ if (idx < 4)
+ txpower = ar->power_2G_cck;
+ else
+ txpower = ar->power_2G_ofdm;
+ } else {
+ txpower = ar->power_5G_leg;
+ idx += 4;
+ }
+
+ rate = &__carl9170_ratetable[idx];
+ *tpc += txpower[(rate->hw_value & 0x30) >> 4];
+ *phyrate = rate->hw_value & 0xf;
+ }
+
+ if (ar->eeprom.tx_mask == 1) {
+ *chains = AR9170_TX_PHY_TXCHAIN_1;
+ } else {
+ if (!(txrate->flags & IEEE80211_TX_RC_MCS) &&
+ rate && rate->bitrate >= 360)
+ *chains = AR9170_TX_PHY_TXCHAIN_1;
+ else
+ *chains = AR9170_TX_PHY_TXCHAIN_2;
+ }
+}
+
static __le32 carl9170_tx_physet(struct ar9170 *ar,
struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate)
{
- struct ieee80211_rate *rate = NULL;
- u32 power, chains;
+ unsigned int power = 0, chains = 0, phyrate = 0;
__le32 tmp;
tmp = cpu_to_le32(0);
@@ -682,35 +741,12 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar,
tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI);
if (txrate->flags & IEEE80211_TX_RC_MCS) {
- u32 r = txrate->idx;
- u8 *txpower;
+ SET_VAL(AR9170_TX_PHY_MCS, phyrate, txrate->idx);
/* heavy clip control */
- tmp |= cpu_to_le32((r & 0x7) <<
+ tmp |= cpu_to_le32((txrate->idx & 0x7) <<
AR9170_TX_PHY_TX_HEAVY_CLIP_S);
- if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
- if (info->band == IEEE80211_BAND_5GHZ)
- txpower = ar->power_5G_ht40;
- else
- txpower = ar->power_2G_ht40;
- } else {
- if (info->band == IEEE80211_BAND_5GHZ)
- txpower = ar->power_5G_ht20;
- else
- txpower = ar->power_2G_ht20;
- }
-
- power = txpower[r & 7];
-
- /* +1 dBm for HT40 */
- if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
- power += 2;
-
- r <<= AR9170_TX_PHY_MCS_S;
- BUG_ON(r & ~AR9170_TX_PHY_MCS);
-
- tmp |= cpu_to_le32(r & AR9170_TX_PHY_MCS);
tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_HT);
/*
@@ -720,34 +756,15 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar,
* tmp |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD);
*/
} else {
- u8 *txpower;
- u32 mod;
- u32 phyrate;
- u8 idx = txrate->idx;
-
- if (info->band != IEEE80211_BAND_2GHZ) {
- idx += 4;
- txpower = ar->power_5G_leg;
- mod = AR9170_TX_PHY_MOD_OFDM;
+ if (info->band == IEEE80211_BAND_2GHZ) {
+ if (txrate->idx <= AR9170_TX_PHY_RATE_CCK_11M)
+ tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_CCK);
+ else
+ tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_OFDM);
} else {
- if (idx < 4) {
- txpower = ar->power_2G_cck;
- mod = AR9170_TX_PHY_MOD_CCK;
- } else {
- mod = AR9170_TX_PHY_MOD_OFDM;
- txpower = ar->power_2G_ofdm;
- }
+ tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_OFDM);
}
- rate = &__carl9170_ratetable[idx];
-
- phyrate = rate->hw_value & 0xF;
- power = txpower[(rate->hw_value & 0x30) >> 4];
- phyrate <<= AR9170_TX_PHY_MCS_S;
-
- tmp |= cpu_to_le32(mod);
- tmp |= cpu_to_le32(phyrate);
-
/*
* short preamble seems to be broken too.
*
@@ -755,23 +772,12 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar,
* tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE);
*/
}
- power <<= AR9170_TX_PHY_TX_PWR_S;
- power &= AR9170_TX_PHY_TX_PWR;
- tmp |= cpu_to_le32(power);
-
- /* set TX chains */
- if (ar->eeprom.tx_mask == 1) {
- chains = AR9170_TX_PHY_TXCHAIN_1;
- } else {
- chains = AR9170_TX_PHY_TXCHAIN_2;
-
- /* >= 36M legacy OFDM - use only one chain */
- if (rate && rate->bitrate >= 360 &&
- !(txrate->flags & IEEE80211_TX_RC_MCS))
- chains = AR9170_TX_PHY_TXCHAIN_1;
- }
- tmp |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_S);
+ carl9170_tx_rate_tpc_chains(ar, info, txrate,
+ &phyrate, &power, &chains);
+ tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_MCS, phyrate));
+ tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_TX_PWR, power));
+ tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_TXCHAIN, chains));
return tmp;
}
@@ -1438,3 +1444,154 @@ void carl9170_tx_scheduler(struct ar9170 *ar)
if (ar->tx_schedule)
carl9170_tx(ar);
}
+
+int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
+{
+ struct sk_buff *skb = NULL;
+ struct carl9170_vif_info *cvif;
+ struct ieee80211_tx_info *txinfo;
+ struct ieee80211_tx_rate *rate;
+ __le32 *data, *old = NULL;
+ unsigned int plcp, power, chains;
+ u32 word, ht1, off, addr, len;
+ int i = 0, err = 0;
+
+ rcu_read_lock();
+ cvif = rcu_dereference(ar->beacon_iter);
+retry:
+ if (ar->vifs == 0 || !cvif)
+ goto out_unlock;
+
+ list_for_each_entry_continue_rcu(cvif, &ar->vif_list, list) {
+ if (cvif->active && cvif->enable_beacon)
+ goto found;
+ }
+
+ if (!ar->beacon_enabled || i++)
+ goto out_unlock;
+
+ goto retry;
+
+found:
+ rcu_assign_pointer(ar->beacon_iter, cvif);
+
+ skb = ieee80211_beacon_get_tim(ar->hw, carl9170_get_vif(cvif),
+ NULL, NULL);
+
+ if (!skb) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ txinfo = IEEE80211_SKB_CB(skb);
+ spin_lock_bh(&ar->beacon_lock);
+ data = (__le32 *)skb->data;
+ if (cvif->beacon)
+ old = (__le32 *)cvif->beacon->data;
+
+ off = cvif->id * AR9170_MAC_BCN_LENGTH_MAX;
+ addr = ar->fw.beacon_addr + off;
+ len = roundup(skb->len + FCS_LEN, 4);
+
+ if ((off + len) > ar->fw.beacon_max_len) {
+ if (net_ratelimit()) {
+ wiphy_err(ar->hw->wiphy, "beacon does not "
+ "fit into device memory!\n");
+ }
+ err = -EINVAL;
+ goto err_unlock;
+ }
+
+ if (len > AR9170_MAC_BCN_LENGTH_MAX) {
+ if (net_ratelimit()) {
+ wiphy_err(ar->hw->wiphy, "no support for beacons "
+ "bigger than %d (yours:%d).\n",
+ AR9170_MAC_BCN_LENGTH_MAX, len);
+ }
+
+ err = -EMSGSIZE;
+ goto err_unlock;
+ }
+
+ ht1 = AR9170_MAC_BCN_HT1_TX_ANT0;
+ rate = &txinfo->control.rates[0];
+ carl9170_tx_rate_tpc_chains(ar, txinfo, rate, &plcp, &power, &chains);
+ if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+ if (plcp <= AR9170_TX_PHY_RATE_CCK_11M)
+ plcp |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400;
+ else
+ plcp |= ((skb->len + FCS_LEN) << 16) + 0x0010;
+ } else {
+ ht1 |= AR9170_MAC_BCN_HT1_HT_EN;
+ if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
+ plcp |= AR9170_MAC_BCN_HT2_SGI;
+
+ if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+ ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_SHARED;
+ plcp |= AR9170_MAC_BCN_HT2_BW40;
+ }
+ if (rate->flags & IEEE80211_TX_RC_DUP_DATA) {
+ ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_DUP;
+ plcp |= AR9170_MAC_BCN_HT2_BW40;
+ }
+
+ SET_VAL(AR9170_MAC_BCN_HT2_LEN, plcp, skb->len + FCS_LEN);
+ }
+
+ SET_VAL(AR9170_MAC_BCN_HT1_PWR_CTRL, ht1, 7);
+ SET_VAL(AR9170_MAC_BCN_HT1_TPC, ht1, power);
+ SET_VAL(AR9170_MAC_BCN_HT1_CHAIN_MASK, ht1, chains);
+ if (chains == AR9170_TX_PHY_TXCHAIN_2)
+ ht1 |= AR9170_MAC_BCN_HT1_TX_ANT1;
+
+ carl9170_async_regwrite_begin(ar);
+ carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT1, ht1);
+ if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS))
+ carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, plcp);
+ else
+ carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT2, plcp);
+
+ for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
+ /*
+ * XXX: This accesses beyond skb data for up
+ * to the last 3 bytes!!
+ */
+
+ if (old && (data[i] == old[i]))
+ continue;
+
+ word = le32_to_cpu(data[i]);
+ carl9170_async_regwrite(addr + 4 * i, word);
+ }
+ carl9170_async_regwrite_finish();
+
+ dev_kfree_skb_any(cvif->beacon);
+ cvif->beacon = NULL;
+
+ err = carl9170_async_regwrite_result();
+ if (!err)
+ cvif->beacon = skb;
+ spin_unlock_bh(&ar->beacon_lock);
+ if (err)
+ goto err_free;
+
+ if (submit) {
+ err = carl9170_bcn_ctrl(ar, cvif->id,
+ CARL9170_BCN_CTRL_CAB_TRIGGER,
+ addr, skb->len + FCS_LEN);
+
+ if (err)
+ goto err_free;
+ }
+out_unlock:
+ rcu_read_unlock();
+ return 0;
+
+err_unlock:
+ spin_unlock_bh(&ar->beacon_lock);
+
+err_free:
+ rcu_read_unlock();
+ dev_kfree_skb_any(skb);
+ return err;
+}
diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c
index 2fb53d067512..333b69ef2ae2 100644
--- a/drivers/net/wireless/ath/carl9170/usb.c
+++ b/drivers/net/wireless/ath/carl9170/usb.c
@@ -112,6 +112,8 @@ static struct usb_device_id carl9170_usb_ids[] = {
{ USB_DEVICE(0x04bb, 0x093f) },
/* NEC WL300NU-G */
{ USB_DEVICE(0x0409, 0x0249) },
+ /* NEC WL300NU-AG */
+ { USB_DEVICE(0x0409, 0x02b4) },
/* AVM FRITZ!WLAN USB Stick N */
{ USB_DEVICE(0x057c, 0x8401) },
/* AVM FRITZ!WLAN USB Stick N 2.4 */
diff --git a/drivers/net/wireless/ath/carl9170/version.h b/drivers/net/wireless/ath/carl9170/version.h
index 15095c035169..e651db856344 100644
--- a/drivers/net/wireless/ath/carl9170/version.h
+++ b/drivers/net/wireless/ath/carl9170/version.h
@@ -1,7 +1,7 @@
#ifndef __CARL9170_SHARED_VERSION_H
#define __CARL9170_SHARED_VERSION_H
#define CARL9170FW_VERSION_YEAR 11
-#define CARL9170FW_VERSION_MONTH 1
-#define CARL9170FW_VERSION_DAY 22
-#define CARL9170FW_VERSION_GIT "1.9.2"
+#define CARL9170FW_VERSION_MONTH 8
+#define CARL9170FW_VERSION_DAY 15
+#define CARL9170FW_VERSION_GIT "1.9.4"
#endif /* __CARL9170_SHARED_VERSION_H */
diff --git a/drivers/net/wireless/ath/carl9170/wlan.h b/drivers/net/wireless/ath/carl9170/wlan.h
index 9e1324b67e08..ea17995b32f4 100644
--- a/drivers/net/wireless/ath/carl9170/wlan.h
+++ b/drivers/net/wireless/ath/carl9170/wlan.h
@@ -4,7 +4,7 @@
* RX/TX meta descriptor format
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.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
@@ -278,7 +278,7 @@ struct ar9170_tx_frame {
struct carl9170_tx_superframe {
struct carl9170_tx_superdesc s;
struct ar9170_tx_frame f;
-} __packed;
+} __packed __aligned(4);
#endif /* __CARL9170FW__ */
@@ -328,7 +328,7 @@ struct _carl9170_tx_superframe {
struct _carl9170_tx_superdesc s;
struct _ar9170_tx_hwdesc f;
u8 frame_data[0];
-} __packed;
+} __packed __aligned(4);
#define CARL9170_TX_SUPERDESC_LEN 24
#define AR9170_TX_HWDESC_LEN 8
@@ -404,16 +404,6 @@ static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_macstatus *t)
(t->DAidx & 0xc0) >> 6;
}
-enum ar9170_txq {
- AR9170_TXQ_BE,
-
- AR9170_TXQ_VI,
- AR9170_TXQ_VO,
- AR9170_TXQ_BK,
-
- __AR9170_NUM_TXQ,
-};
-
/*
* This is an workaround for several undocumented bugs.
* Don't mess with the QoS/AC <-> HW Queue map, if you don't
@@ -431,7 +421,14 @@ enum ar9170_txq {
* result, this makes the device pretty much useless
* for any serious 802.11n setup.
*/
-static const u8 ar9170_qmap[__AR9170_NUM_TXQ] = { 2, 1, 0, 3 };
+enum ar9170_txq {
+ AR9170_TXQ_BK = 0, /* TXQ0 */
+ AR9170_TXQ_BE, /* TXQ1 */
+ AR9170_TXQ_VI, /* TXQ2 */
+ AR9170_TXQ_VO, /* TXQ3 */
+
+ __AR9170_NUM_TXQ,
+};
#define AR9170_TXQ_DEPTH 32
diff --git a/drivers/net/wireless/ath/debug.c b/drivers/net/wireless/ath/debug.c
index 5367b1086e09..508eccf5d982 100644
--- a/drivers/net/wireless/ath/debug.c
+++ b/drivers/net/wireless/ath/debug.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/export.h>
#include "ath.h"
const char *ath_opmode_to_string(enum nl80211_iftype opmode)
diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c
index 3f508e59f146..19befb331073 100644
--- a/drivers/net/wireless/ath/hw.c
+++ b/drivers/net/wireless/ath/hw.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/export.h>
#include <asm/unaligned.h>
#include "ath.h"
diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c
index a61ef3d6d89c..4cf7c5eb4813 100644
--- a/drivers/net/wireless/ath/key.c
+++ b/drivers/net/wireless/ath/key.c
@@ -15,6 +15,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/export.h>
#include <asm/unaligned.h>
#include <net/mac80211.h>
@@ -105,11 +106,8 @@ static bool ath_hw_keysetmac(struct ath_common *common,
if (mac[0] & 0x01)
unicast_flag = 0;
- macHi = (mac[5] << 8) | mac[4];
- macLo = (mac[3] << 24) |
- (mac[2] << 16) |
- (mac[1] << 8) |
- mac[0];
+ macLo = get_unaligned_le32(mac);
+ macHi = get_unaligned_le16(mac + 4);
macLo >>= 1;
macLo |= (macHi & 1) << 31;
macHi >>= 1;
diff --git a/drivers/net/wireless/ath/main.c b/drivers/net/wireless/ath/main.c
index c325202fdc5f..d9218fe02036 100644
--- a/drivers/net/wireless/ath/main.c
+++ b/drivers/net/wireless/ath/main.c
@@ -57,22 +57,18 @@ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
}
EXPORT_SYMBOL(ath_rxbuf_alloc);
-int ath_printk(const char *level, struct ath_common *common,
- const char *fmt, ...)
+void ath_printk(const char *level, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
- int rtn;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- rtn = printk("%sath: %pV", level, &vaf);
+ printk("%sath: %pV", level, &vaf);
va_end(args);
-
- return rtn;
}
EXPORT_SYMBOL(ath_printk);
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index 028310f263c8..65ecb5bab25a 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -15,6 +15,7 @@
*/
#include <linux/kernel.h>
+#include <linux/export.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include "regd.h"
@@ -253,6 +254,8 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy,
int r;
sband = wiphy->bands[IEEE80211_BAND_2GHZ];
+ if (!sband)
+ return;
/*
* If no country IE has been received always enable active scan
diff --git a/drivers/net/wireless/ath/regd.h b/drivers/net/wireless/ath/regd.h
index 172f63f671cf..03a8268ccf21 100644
--- a/drivers/net/wireless/ath/regd.h
+++ b/drivers/net/wireless/ath/regd.h
@@ -101,7 +101,7 @@ enum CountryCode {
CTRY_GERMANY = 276,
CTRY_GREECE = 300,
CTRY_GREENLAND = 304,
- CTRY_GRENEDA = 308,
+ CTRY_GRENADA = 308,
CTRY_GUAM = 316,
CTRY_GUATEMALA = 320,
CTRY_HAITI = 332,
diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h
index 24b53839fc3a..bdd2b4d61f2f 100644
--- a/drivers/net/wireless/ath/regd_common.h
+++ b/drivers/net/wireless/ath/regd_common.h
@@ -332,7 +332,7 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_GERMANY, ETSI1_WORLD, "DE"},
{CTRY_GREECE, ETSI1_WORLD, "GR"},
{CTRY_GREENLAND, ETSI1_WORLD, "GL"},
- {CTRY_GRENEDA, FCC3_FCCA, "GD"},
+ {CTRY_GRENADA, FCC3_FCCA, "GD"},
{CTRY_GUAM, FCC1_FCCA, "GU"},
{CTRY_GUATEMALA, FCC1_FCCA, "GT"},
{CTRY_HAITI, ETSI1_WORLD, "HT"},
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index 39a11e8af4fa..7e45ca2e78ef 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -40,6 +40,7 @@
******************************************************************************/
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig
index 480595f04411..b97a40ed5fff 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/b43/Kconfig
@@ -26,6 +26,16 @@ config B43
This driver can be built as a module (recommended) that will be called "b43".
If unsure, say M.
+config B43_BCMA
+ bool "Support for BCMA bus"
+ depends on B43 && BCMA
+ default y
+
+config B43_SSB
+ bool
+ depends on B43 && SSB
+ default y
+
# Auto-select SSB PCI-HOST support, if possible
config B43_PCI_AUTOSELECT
bool
@@ -80,6 +90,12 @@ config B43_SDIO
#Data transfers to the device via PIO. We want it as a fallback even
# if we can do DMA.
+config B43_BCMA_PIO
+ bool
+ depends on B43_BCMA
+ select BCMA_BLOCKIO
+ default y
+
config B43_PIO
bool
depends on B43
@@ -98,15 +114,31 @@ config B43_PHY_N
affect other devices support and may provide support for basic needs.
config B43_PHY_LP
- bool "Support for low-power (LP-PHY) devices (EXPERIMENTAL)"
- depends on B43 && EXPERIMENTAL
+ bool "Support for low-power (LP-PHY) devices"
+ depends on B43
default y
---help---
Support for the LP-PHY.
The LP-PHY is a low-power PHY built into some notebooks
- and embedded devices. It supports 802.11a/g
+ and embedded devices. It supports 802.11a/b/g
(802.11a support is optional, and currently disabled).
+config B43_PHY_HT
+ bool "Support for HT-PHY (high throughput) devices (EXPERIMENTAL)"
+ depends on B43 && EXPERIMENTAL
+ ---help---
+ Support for the HT-PHY.
+
+ Enables support for BCM4331 and possibly other chipsets with that PHY.
+
+config B43_PHY_LCN
+ bool "Support for LCN-PHY devices (BROKEN)"
+ depends on B43 && BROKEN
+ ---help---
+ Support for the LCN-PHY.
+
+ Say N, this is BROKEN and crashes driver.
+
# This config option automatically enables b43 LEDS support,
# if it's possible.
config B43_LEDS
@@ -137,13 +169,3 @@ config B43_DEBUG
Say N, if you are a distributor or user building a release kernel
for production use.
Only say Y, if you are debugging a problem in the b43 driver sourcecode.
-
-config B43_FORCE_PIO
- bool "Force usage of PIO instead of DMA"
- depends on B43 && B43_DEBUG
- ---help---
- This will disable DMA and always enable PIO instead.
-
- Say N!
- This is only for debugging the PIO engine code. You do
- _NOT_ want to enable this.
diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile
index cef334a8c669..4648bbf76abc 100644
--- a/drivers/net/wireless/b43/Makefile
+++ b/drivers/net/wireless/b43/Makefile
@@ -1,4 +1,5 @@
b43-y += main.o
+b43-y += bus.o
b43-y += tables.o
b43-$(CONFIG_B43_PHY_N) += tables_nphy.o
b43-$(CONFIG_B43_PHY_N) += radio_2055.o
@@ -9,6 +10,10 @@ b43-y += phy_a.o
b43-$(CONFIG_B43_PHY_N) += phy_n.o
b43-$(CONFIG_B43_PHY_LP) += phy_lp.o
b43-$(CONFIG_B43_PHY_LP) += tables_lpphy.o
+b43-$(CONFIG_B43_PHY_HT) += phy_ht.o
+b43-$(CONFIG_B43_PHY_HT) += tables_phy_ht.o
+b43-$(CONFIG_B43_PHY_HT) += radio_2059.o
+b43-$(CONFIG_B43_PHY_LCN) += phy_lcn.o tables_phy_lcn.o
b43-y += sysfs.o
b43-y += xmit.o
b43-y += lo.o
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h
index 25a78cfb7d15..37110dfd2c96 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -5,21 +5,18 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/hw_random.h>
+#include <linux/bcma/bcma.h>
#include <linux/ssb/ssb.h>
#include <net/mac80211.h>
#include "debugfs.h"
#include "leds.h"
#include "rfkill.h"
+#include "bus.h"
#include "lo.h"
#include "phy_common.h"
-/* The unique identifier of the firmware that's officially supported by
- * this driver version. */
-#define B43_SUPPORTED_FIRMWARE_ID "FW13"
-
-
#ifdef CONFIG_B43_DEBUG
# define B43_DEBUG 1
#else
@@ -90,6 +87,8 @@
#define B43_MMIO_PIO11_BASE4 0x300
#define B43_MMIO_PIO11_BASE5 0x340
+#define B43_MMIO_RADIO24_CONTROL 0x3D8 /* core rev >= 24 only */
+#define B43_MMIO_RADIO24_DATA 0x3DA /* core rev >= 24 only */
#define B43_MMIO_PHY_VER 0x3E0
#define B43_MMIO_PHY_RADIO 0x3E2
#define B43_MMIO_PHY0 0x3E6
@@ -108,21 +107,61 @@
#define B43_MMIO_RADIO_HWENABLED_LO 0x49A
#define B43_MMIO_GPIO_CONTROL 0x49C
#define B43_MMIO_GPIO_MASK 0x49E
+#define B43_MMIO_TXE0_CTL 0x500
+#define B43_MMIO_TXE0_AUX 0x502
+#define B43_MMIO_TXE0_TS_LOC 0x504
+#define B43_MMIO_TXE0_TIME_OUT 0x506
+#define B43_MMIO_TXE0_WM_0 0x508
+#define B43_MMIO_TXE0_WM_1 0x50A
+#define B43_MMIO_TXE0_PHYCTL 0x50C
+#define B43_MMIO_TXE0_STATUS 0x50E
+#define B43_MMIO_TXE0_MMPLCP0 0x510
+#define B43_MMIO_TXE0_MMPLCP1 0x512
+#define B43_MMIO_TXE0_PHYCTL1 0x514
+#define B43_MMIO_XMTFIFODEF 0x520
+#define B43_MMIO_XMTFIFO_FRAME_CNT 0x522 /* core rev>= 16 only */
+#define B43_MMIO_XMTFIFO_BYTE_CNT 0x524 /* core rev>= 16 only */
+#define B43_MMIO_XMTFIFO_HEAD 0x526 /* core rev>= 16 only */
+#define B43_MMIO_XMTFIFO_RD_PTR 0x528 /* core rev>= 16 only */
+#define B43_MMIO_XMTFIFO_WR_PTR 0x52A /* core rev>= 16 only */
+#define B43_MMIO_XMTFIFODEF1 0x52C /* core rev>= 16 only */
+#define B43_MMIO_XMTFIFOCMD 0x540
+#define B43_MMIO_XMTFIFOFLUSH 0x542
+#define B43_MMIO_XMTFIFOTHRESH 0x544
+#define B43_MMIO_XMTFIFORDY 0x546
+#define B43_MMIO_XMTFIFOPRIRDY 0x548
+#define B43_MMIO_XMTFIFORQPRI 0x54A
+#define B43_MMIO_XMTTPLATETXPTR 0x54C
+#define B43_MMIO_XMTTPLATEPTR 0x550
+#define B43_MMIO_SMPL_CLCT_STRPTR 0x552 /* core rev>= 22 only */
+#define B43_MMIO_SMPL_CLCT_STPPTR 0x554 /* core rev>= 22 only */
+#define B43_MMIO_SMPL_CLCT_CURPTR 0x556 /* core rev>= 22 only */
+#define B43_MMIO_XMTTPLATEDATALO 0x560
+#define B43_MMIO_XMTTPLATEDATAHI 0x562
+#define B43_MMIO_XMTSEL 0x568
+#define B43_MMIO_XMTTXCNT 0x56A
+#define B43_MMIO_XMTTXSHMADDR 0x56C
#define B43_MMIO_TSF_CFP_START_LOW 0x604
#define B43_MMIO_TSF_CFP_START_HIGH 0x606
#define B43_MMIO_TSF_CFP_PRETBTT 0x612
+#define B43_MMIO_TSF_CLK_FRAC_LOW 0x62E
+#define B43_MMIO_TSF_CLK_FRAC_HIGH 0x630
#define B43_MMIO_TSF_0 0x632 /* core rev < 3 only */
#define B43_MMIO_TSF_1 0x634 /* core rev < 3 only */
#define B43_MMIO_TSF_2 0x636 /* core rev < 3 only */
#define B43_MMIO_TSF_3 0x638 /* core rev < 3 only */
#define B43_MMIO_RNG 0x65A
#define B43_MMIO_IFSSLOT 0x684 /* Interframe slot time */
-#define B43_MMIO_IFSCTL 0x688 /* Interframe space control */
+#define B43_MMIO_IFSCTL 0x688 /* Interframe space control */
+#define B43_MMIO_IFSSTAT 0x690
+#define B43_MMIO_IFSMEDBUSYCTL 0x692
+#define B43_MMIO_IFTXDUR 0x694
#define B43_MMIO_IFSCTL_USE_EDCF 0x0004
#define B43_MMIO_POWERUP_DELAY 0x6A8
#define B43_MMIO_BTCOEX_CTL 0x6B4 /* Bluetooth Coexistence Control */
#define B43_MMIO_BTCOEX_STAT 0x6B6 /* Bluetooth Coexistence Status */
#define B43_MMIO_BTCOEX_TXCTL 0x6B8 /* Bluetooth Coexistence Transmit Control */
+#define B43_MMIO_WEPCTL 0x7C0
/* SPROM boardflags_lo values */
#define B43_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */
@@ -361,6 +400,10 @@ enum {
#define B43_PHYTYPE_G 0x02
#define B43_PHYTYPE_N 0x04
#define B43_PHYTYPE_LP 0x05
+#define B43_PHYTYPE_SSLPN 0x06
+#define B43_PHYTYPE_HT 0x07
+#define B43_PHYTYPE_LCN 0x08
+#define B43_PHYTYPE_LCNXN 0x09
/* PHYRegisters */
#define B43_PHY_ILT_A_CTRL 0x0072
@@ -414,6 +457,23 @@ enum {
#define B43_MACCMD_CCA 0x00000008 /* Clear channel assessment */
#define B43_MACCMD_BGNOISE 0x00000010 /* Background noise */
+/* BCMA 802.11 core specific IO Control (BCMA_IOCTL) flags */
+#define B43_BCMA_IOCTL_PHY_CLKEN 0x00000004 /* PHY Clock Enable */
+#define B43_BCMA_IOCTL_PHY_RESET 0x00000008 /* PHY Reset */
+#define B43_BCMA_IOCTL_MACPHYCLKEN 0x00000010 /* MAC PHY Clock Control Enable */
+#define B43_BCMA_IOCTL_PLLREFSEL 0x00000020 /* PLL Frequency Reference Select */
+#define B43_BCMA_IOCTL_PHY_BW 0x000000C0 /* PHY band width and clock speed mask (N-PHY+ only?) */
+#define B43_BCMA_IOCTL_PHY_BW_10MHZ 0x00000000 /* 10 MHz bandwidth, 40 MHz PHY */
+#define B43_BCMA_IOCTL_PHY_BW_20MHZ 0x00000040 /* 20 MHz bandwidth, 80 MHz PHY */
+#define B43_BCMA_IOCTL_PHY_BW_40MHZ 0x00000080 /* 40 MHz bandwidth, 160 MHz PHY */
+#define B43_BCMA_IOCTL_GMODE 0x00002000 /* G Mode Enable */
+
+/* BCMA 802.11 core specific IO status (BCMA_IOST) flags */
+#define B43_BCMA_IOST_2G_PHY 0x00000001 /* 2.4G capable phy */
+#define B43_BCMA_IOST_5G_PHY 0x00000002 /* 5G capable phy */
+#define B43_BCMA_IOST_FASTCLKA 0x00000004 /* Fast Clock Available */
+#define B43_BCMA_IOST_DUALB_PHY 0x00000008 /* Dualband phy */
+
/* 802.11 core specific TM State Low (SSB_TMSLOW) flags */
#define B43_TMSLOW_GMODE 0x20000000 /* G Mode Enable */
#define B43_TMSLOW_PHY_BANDWIDTH 0x00C00000 /* PHY band width and clock speed mask (N-PHY only) */
@@ -569,6 +629,8 @@ struct b43_dma {
struct b43_dmaring *rx_ring;
u32 translation; /* Routing bits */
+ bool translation_in_low; /* Should translation bit go into low addr? */
+ bool parity; /* Check for parity */
};
struct b43_pio_txqueue;
@@ -668,6 +730,12 @@ struct b43_firmware_file {
enum b43_firmware_file_type type;
};
+enum b43_firmware_hdr_format {
+ B43_FW_HDR_598,
+ B43_FW_HDR_410,
+ B43_FW_HDR_351,
+};
+
/* Pointers to the firmware data and meta information about it. */
struct b43_firmware {
/* Microcode */
@@ -684,6 +752,9 @@ struct b43_firmware {
/* Firmware patchlevel */
u16 patch;
+ /* Format of header used by firmware */
+ enum b43_firmware_hdr_format hdr_format;
+
/* Set to true, if we are using an opensource firmware.
* Use this to check for proprietary vs opensource. */
bool opensource;
@@ -707,7 +778,7 @@ enum {
/* Data structure for one wireless device (802.11 core) */
struct b43_wldev {
- struct ssb_device *sdev;
+ struct b43_bus_dev *dev;
struct b43_wl *wl;
/* The device initialization status.
@@ -849,7 +920,7 @@ struct b43_wl {
struct b43_leds leds;
/* Kmalloc'ed scratch space for PIO TX/RX. Protected by wl->mutex. */
- u8 pio_scratchspace[110] __attribute__((__aligned__(8)));
+ u8 pio_scratchspace[118] __attribute__((__aligned__(8)));
u8 pio_tailspace[4] __attribute__((__aligned__(8)));
};
@@ -879,36 +950,59 @@ static inline enum ieee80211_band b43_current_band(struct b43_wl *wl)
return wl->hw->conf.channel->band;
}
+static inline int b43_bus_may_powerdown(struct b43_wldev *wldev)
+{
+ return wldev->dev->bus_may_powerdown(wldev->dev);
+}
+static inline int b43_bus_powerup(struct b43_wldev *wldev, bool dynamic_pctl)
+{
+ return wldev->dev->bus_powerup(wldev->dev, dynamic_pctl);
+}
+static inline int b43_device_is_enabled(struct b43_wldev *wldev)
+{
+ return wldev->dev->device_is_enabled(wldev->dev);
+}
+static inline void b43_device_enable(struct b43_wldev *wldev,
+ u32 core_specific_flags)
+{
+ wldev->dev->device_enable(wldev->dev, core_specific_flags);
+}
+static inline void b43_device_disable(struct b43_wldev *wldev,
+ u32 core_specific_flags)
+{
+ wldev->dev->device_disable(wldev->dev, core_specific_flags);
+}
+
static inline u16 b43_read16(struct b43_wldev *dev, u16 offset)
{
- return ssb_read16(dev->sdev, offset);
+ return dev->dev->read16(dev->dev, offset);
}
static inline void b43_write16(struct b43_wldev *dev, u16 offset, u16 value)
{
- ssb_write16(dev->sdev, offset, value);
+ dev->dev->write16(dev->dev, offset, value);
}
static inline u32 b43_read32(struct b43_wldev *dev, u16 offset)
{
- return ssb_read32(dev->sdev, offset);
+ return dev->dev->read32(dev->dev, offset);
}
static inline void b43_write32(struct b43_wldev *dev, u16 offset, u32 value)
{
- ssb_write32(dev->sdev, offset, value);
+ dev->dev->write32(dev->dev, offset, value);
}
static inline void b43_block_read(struct b43_wldev *dev, void *buffer,
size_t count, u16 offset, u8 reg_width)
{
- ssb_block_read(dev->sdev, buffer, count, offset, reg_width);
+ dev->dev->block_read(dev->dev, buffer, count, offset, reg_width);
}
static inline void b43_block_write(struct b43_wldev *dev, const void *buffer,
size_t count, u16 offset, u8 reg_width)
{
- ssb_block_write(dev->sdev, buffer, count, offset, reg_width);
+ dev->dev->block_write(dev->dev, buffer, count, offset, reg_width);
}
static inline bool b43_using_pio_transfers(struct b43_wldev *dev)
@@ -916,21 +1010,11 @@ static inline bool b43_using_pio_transfers(struct b43_wldev *dev)
return dev->__using_pio_transfers;
}
-#ifdef CONFIG_B43_FORCE_PIO
-# define B43_PIO_DEFAULT 1
-#else
-# define B43_PIO_DEFAULT 0
-#endif
-
/* Message printing */
-void b43info(struct b43_wl *wl, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-void b43err(struct b43_wl *wl, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-void b43warn(struct b43_wl *wl, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-void b43dbg(struct b43_wl *wl, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
+__printf(2, 3) void b43info(struct b43_wl *wl, const char *fmt, ...);
+__printf(2, 3) void b43err(struct b43_wl *wl, const char *fmt, ...);
+__printf(2, 3) void b43warn(struct b43_wl *wl, const char *fmt, ...);
+__printf(2, 3) void b43dbg(struct b43_wl *wl, const char *fmt, ...);
/* A WARN_ON variant that vanishes when b43 debugging is disabled.
diff --git a/drivers/net/wireless/b43/bus.c b/drivers/net/wireless/b43/bus.c
new file mode 100644
index 000000000000..424692df239d
--- /dev/null
+++ b/drivers/net/wireless/b43/bus.c
@@ -0,0 +1,257 @@
+/*
+
+ Broadcom B43 wireless driver
+ Bus abstraction layer
+
+ Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "b43.h"
+#include "bus.h"
+
+/* BCMA */
+#ifdef CONFIG_B43_BCMA
+static int b43_bus_bcma_bus_may_powerdown(struct b43_bus_dev *dev)
+{
+ return 0; /* bcma_bus_may_powerdown(dev->bdev->bus); */
+}
+static int b43_bus_bcma_bus_powerup(struct b43_bus_dev *dev,
+ bool dynamic_pctl)
+{
+ return 0; /* bcma_bus_powerup(dev->sdev->bus, dynamic_pctl); */
+}
+static int b43_bus_bcma_device_is_enabled(struct b43_bus_dev *dev)
+{
+ return bcma_core_is_enabled(dev->bdev);
+}
+static void b43_bus_bcma_device_enable(struct b43_bus_dev *dev,
+ u32 core_specific_flags)
+{
+ bcma_core_enable(dev->bdev, core_specific_flags);
+}
+static void b43_bus_bcma_device_disable(struct b43_bus_dev *dev,
+ u32 core_specific_flags)
+{
+ bcma_core_disable(dev->bdev, core_specific_flags);
+}
+static u16 b43_bus_bcma_read16(struct b43_bus_dev *dev, u16 offset)
+{
+ return bcma_read16(dev->bdev, offset);
+}
+static u32 b43_bus_bcma_read32(struct b43_bus_dev *dev, u16 offset)
+{
+ return bcma_read32(dev->bdev, offset);
+}
+static
+void b43_bus_bcma_write16(struct b43_bus_dev *dev, u16 offset, u16 value)
+{
+ bcma_write16(dev->bdev, offset, value);
+}
+static
+void b43_bus_bcma_write32(struct b43_bus_dev *dev, u16 offset, u32 value)
+{
+ bcma_write32(dev->bdev, offset, value);
+}
+static
+void b43_bus_bcma_block_read(struct b43_bus_dev *dev, void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ bcma_block_read(dev->bdev, buffer, count, offset, reg_width);
+}
+static
+void b43_bus_bcma_block_write(struct b43_bus_dev *dev, const void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ bcma_block_write(dev->bdev, buffer, count, offset, reg_width);
+}
+
+struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core)
+{
+ struct b43_bus_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ dev->bus_type = B43_BUS_BCMA;
+ dev->bdev = core;
+
+ dev->bus_may_powerdown = b43_bus_bcma_bus_may_powerdown;
+ dev->bus_powerup = b43_bus_bcma_bus_powerup;
+ dev->device_is_enabled = b43_bus_bcma_device_is_enabled;
+ dev->device_enable = b43_bus_bcma_device_enable;
+ dev->device_disable = b43_bus_bcma_device_disable;
+
+ dev->read16 = b43_bus_bcma_read16;
+ dev->read32 = b43_bus_bcma_read32;
+ dev->write16 = b43_bus_bcma_write16;
+ dev->write32 = b43_bus_bcma_write32;
+ dev->block_read = b43_bus_bcma_block_read;
+ dev->block_write = b43_bus_bcma_block_write;
+
+ dev->dev = &core->dev;
+ dev->dma_dev = core->dma_dev;
+ dev->irq = core->irq;
+
+ /*
+ dev->board_vendor = core->bus->boardinfo.vendor;
+ dev->board_type = core->bus->boardinfo.type;
+ dev->board_rev = core->bus->boardinfo.rev;
+ */
+
+ dev->chip_id = core->bus->chipinfo.id;
+ dev->chip_rev = core->bus->chipinfo.rev;
+ dev->chip_pkg = core->bus->chipinfo.pkg;
+
+ dev->bus_sprom = &core->bus->sprom;
+
+ dev->core_id = core->id.id;
+ dev->core_rev = core->id.rev;
+
+ return dev;
+}
+#endif /* CONFIG_B43_BCMA */
+
+/* SSB */
+#ifdef CONFIG_B43_SSB
+static int b43_bus_ssb_bus_may_powerdown(struct b43_bus_dev *dev)
+{
+ return ssb_bus_may_powerdown(dev->sdev->bus);
+}
+static int b43_bus_ssb_bus_powerup(struct b43_bus_dev *dev,
+ bool dynamic_pctl)
+{
+ return ssb_bus_powerup(dev->sdev->bus, dynamic_pctl);
+}
+static int b43_bus_ssb_device_is_enabled(struct b43_bus_dev *dev)
+{
+ return ssb_device_is_enabled(dev->sdev);
+}
+static void b43_bus_ssb_device_enable(struct b43_bus_dev *dev,
+ u32 core_specific_flags)
+{
+ ssb_device_enable(dev->sdev, core_specific_flags);
+}
+static void b43_bus_ssb_device_disable(struct b43_bus_dev *dev,
+ u32 core_specific_flags)
+{
+ ssb_device_disable(dev->sdev, core_specific_flags);
+}
+
+static u16 b43_bus_ssb_read16(struct b43_bus_dev *dev, u16 offset)
+{
+ return ssb_read16(dev->sdev, offset);
+}
+static u32 b43_bus_ssb_read32(struct b43_bus_dev *dev, u16 offset)
+{
+ return ssb_read32(dev->sdev, offset);
+}
+static void b43_bus_ssb_write16(struct b43_bus_dev *dev, u16 offset, u16 value)
+{
+ ssb_write16(dev->sdev, offset, value);
+}
+static void b43_bus_ssb_write32(struct b43_bus_dev *dev, u16 offset, u32 value)
+{
+ ssb_write32(dev->sdev, offset, value);
+}
+static void b43_bus_ssb_block_read(struct b43_bus_dev *dev, void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ ssb_block_read(dev->sdev, buffer, count, offset, reg_width);
+}
+static
+void b43_bus_ssb_block_write(struct b43_bus_dev *dev, const void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ ssb_block_write(dev->sdev, buffer, count, offset, reg_width);
+}
+
+struct b43_bus_dev *b43_bus_dev_ssb_init(struct ssb_device *sdev)
+{
+ struct b43_bus_dev *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ dev->bus_type = B43_BUS_SSB;
+ dev->sdev = sdev;
+
+ dev->bus_may_powerdown = b43_bus_ssb_bus_may_powerdown;
+ dev->bus_powerup = b43_bus_ssb_bus_powerup;
+ dev->device_is_enabled = b43_bus_ssb_device_is_enabled;
+ dev->device_enable = b43_bus_ssb_device_enable;
+ dev->device_disable = b43_bus_ssb_device_disable;
+
+ dev->read16 = b43_bus_ssb_read16;
+ dev->read32 = b43_bus_ssb_read32;
+ dev->write16 = b43_bus_ssb_write16;
+ dev->write32 = b43_bus_ssb_write32;
+ dev->block_read = b43_bus_ssb_block_read;
+ dev->block_write = b43_bus_ssb_block_write;
+
+ dev->dev = sdev->dev;
+ dev->dma_dev = sdev->dma_dev;
+ dev->irq = sdev->irq;
+
+ dev->board_vendor = sdev->bus->boardinfo.vendor;
+ dev->board_type = sdev->bus->boardinfo.type;
+ dev->board_rev = sdev->bus->boardinfo.rev;
+
+ dev->chip_id = sdev->bus->chip_id;
+ dev->chip_rev = sdev->bus->chip_rev;
+ dev->chip_pkg = sdev->bus->chip_package;
+
+ dev->bus_sprom = &sdev->bus->sprom;
+
+ dev->core_id = sdev->id.coreid;
+ dev->core_rev = sdev->id.revision;
+
+ return dev;
+}
+#endif /* CONFIG_B43_SSB */
+
+void *b43_bus_get_wldev(struct b43_bus_dev *dev)
+{
+ switch (dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ return bcma_get_drvdata(dev->bdev);
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ return ssb_get_drvdata(dev->sdev);
+#endif
+ }
+ return NULL;
+}
+
+void b43_bus_set_wldev(struct b43_bus_dev *dev, void *wldev)
+{
+ switch (dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ bcma_set_drvdata(dev->bdev, wldev);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ ssb_set_drvdata(dev->sdev, wldev);
+ break;
+#endif
+ }
+}
diff --git a/drivers/net/wireless/b43/bus.h b/drivers/net/wireless/b43/bus.h
new file mode 100644
index 000000000000..184c95659279
--- /dev/null
+++ b/drivers/net/wireless/b43/bus.h
@@ -0,0 +1,70 @@
+#ifndef B43_BUS_H_
+#define B43_BUS_H_
+
+enum b43_bus_type {
+#ifdef CONFIG_B43_BCMA
+ B43_BUS_BCMA,
+#endif
+ B43_BUS_SSB,
+};
+
+struct b43_bus_dev {
+ enum b43_bus_type bus_type;
+ union {
+ struct bcma_device *bdev;
+ struct ssb_device *sdev;
+ };
+
+ int (*bus_may_powerdown)(struct b43_bus_dev *dev);
+ int (*bus_powerup)(struct b43_bus_dev *dev, bool dynamic_pctl);
+ int (*device_is_enabled)(struct b43_bus_dev *dev);
+ void (*device_enable)(struct b43_bus_dev *dev,
+ u32 core_specific_flags);
+ void (*device_disable)(struct b43_bus_dev *dev,
+ u32 core_specific_flags);
+
+ u16 (*read16)(struct b43_bus_dev *dev, u16 offset);
+ u32 (*read32)(struct b43_bus_dev *dev, u16 offset);
+ void (*write16)(struct b43_bus_dev *dev, u16 offset, u16 value);
+ void (*write32)(struct b43_bus_dev *dev, u16 offset, u32 value);
+ void (*block_read)(struct b43_bus_dev *dev, void *buffer,
+ size_t count, u16 offset, u8 reg_width);
+ void (*block_write)(struct b43_bus_dev *dev, const void *buffer,
+ size_t count, u16 offset, u8 reg_width);
+
+ struct device *dev;
+ struct device *dma_dev;
+ unsigned int irq;
+
+ u16 board_vendor;
+ u16 board_type;
+ u16 board_rev;
+
+ u16 chip_id;
+ u8 chip_rev;
+ u8 chip_pkg;
+
+ struct ssb_sprom *bus_sprom;
+
+ u16 core_id;
+ u8 core_rev;
+};
+
+static inline bool b43_bus_host_is_pcmcia(struct b43_bus_dev *dev)
+{
+ return (dev->bus_type == B43_BUS_SSB &&
+ dev->sdev->bus->bustype == SSB_BUSTYPE_PCMCIA);
+}
+static inline bool b43_bus_host_is_sdio(struct b43_bus_dev *dev)
+{
+ return (dev->bus_type == B43_BUS_SSB &&
+ dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO);
+}
+
+struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core);
+struct b43_bus_dev *b43_bus_dev_ssb_init(struct ssb_device *sdev);
+
+void *b43_bus_get_wldev(struct b43_bus_dev *dev);
+void b43_bus_set_wldev(struct b43_bus_dev *dev, void *data);
+
+#endif /* B43_BUS_H_ */
diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/b43/debugfs.c
index 59f59fa40334..e751fdee89b2 100644
--- a/drivers/net/wireless/b43/debugfs.c
+++ b/drivers/net/wireless/b43/debugfs.c
@@ -4,7 +4,7 @@
debugfs driver debugging code
- Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
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
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c
index 47d44bcff37d..5e45604f0f5d 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/b43/dma.c
@@ -4,7 +4,7 @@
DMA ringbuffer and descriptor allocation/management
- Copyright (c) 2005, 2006 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005, 2006 Michael Buesch <m@bues.ch>
Some code in this file is derived from the b44.c driver
Copyright (C) 2002 David S. Miller
@@ -47,6 +47,38 @@
* into separate slots. */
#define TX_SLOTS_PER_FRAME 2
+static u32 b43_dma_address(struct b43_dma *dma, dma_addr_t dmaaddr,
+ enum b43_addrtype addrtype)
+{
+ u32 uninitialized_var(addr);
+
+ switch (addrtype) {
+ case B43_DMA_ADDR_LOW:
+ addr = lower_32_bits(dmaaddr);
+ if (dma->translation_in_low) {
+ addr &= ~SSB_DMA_TRANSLATION_MASK;
+ addr |= dma->translation;
+ }
+ break;
+ case B43_DMA_ADDR_HIGH:
+ addr = upper_32_bits(dmaaddr);
+ if (!dma->translation_in_low) {
+ addr &= ~SSB_DMA_TRANSLATION_MASK;
+ addr |= dma->translation;
+ }
+ break;
+ case B43_DMA_ADDR_EXT:
+ if (dma->translation_in_low)
+ addr = lower_32_bits(dmaaddr);
+ else
+ addr = upper_32_bits(dmaaddr);
+ addr &= SSB_DMA_TRANSLATION_MASK;
+ addr >>= SSB_DMA_TRANSLATION_SHIFT;
+ break;
+ }
+
+ return addr;
+}
/* 32bit DMA ops. */
static
@@ -77,10 +109,9 @@ static void op32_fill_descriptor(struct b43_dmaring *ring,
slot = (int)(&(desc->dma32) - descbase);
B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
- addr = (u32) (dmaaddr & ~SSB_DMA_TRANSLATION_MASK);
- addrext = (u32) (dmaaddr & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
- addr |= ring->dev->dma.translation;
+ addr = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW);
+ addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT);
+
ctl = bufsize & B43_DMA32_DCTL_BYTECNT;
if (slot == ring->nr_slots - 1)
ctl |= B43_DMA32_DCTL_DTABLEEND;
@@ -170,11 +201,10 @@ static void op64_fill_descriptor(struct b43_dmaring *ring,
slot = (int)(&(desc->dma64) - descbase);
B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
- addrlo = (u32) (dmaaddr & 0xFFFFFFFF);
- addrhi = (((u64) dmaaddr >> 32) & ~SSB_DMA_TRANSLATION_MASK);
- addrext = (((u64) dmaaddr >> 32) & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
- addrhi |= (ring->dev->dma.translation << 1);
+ addrlo = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW);
+ addrhi = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_HIGH);
+ addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT);
+
if (slot == ring->nr_slots - 1)
ctl0 |= B43_DMA64_DCTL0_DTABLEEND;
if (start)
@@ -333,10 +363,10 @@ static inline
dma_addr_t dmaaddr;
if (tx) {
- dmaaddr = dma_map_single(ring->dev->sdev->dma_dev,
+ dmaaddr = dma_map_single(ring->dev->dev->dma_dev,
buf, len, DMA_TO_DEVICE);
} else {
- dmaaddr = dma_map_single(ring->dev->sdev->dma_dev,
+ dmaaddr = dma_map_single(ring->dev->dev->dma_dev,
buf, len, DMA_FROM_DEVICE);
}
@@ -348,10 +378,10 @@ static inline
dma_addr_t addr, size_t len, int tx)
{
if (tx) {
- dma_unmap_single(ring->dev->sdev->dma_dev,
+ dma_unmap_single(ring->dev->dev->dma_dev,
addr, len, DMA_TO_DEVICE);
} else {
- dma_unmap_single(ring->dev->sdev->dma_dev,
+ dma_unmap_single(ring->dev->dev->dma_dev,
addr, len, DMA_FROM_DEVICE);
}
}
@@ -361,7 +391,7 @@ static inline
dma_addr_t addr, size_t len)
{
B43_WARN_ON(ring->tx);
- dma_sync_single_for_cpu(ring->dev->sdev->dma_dev,
+ dma_sync_single_for_cpu(ring->dev->dev->dma_dev,
addr, len, DMA_FROM_DEVICE);
}
@@ -370,7 +400,7 @@ static inline
dma_addr_t addr, size_t len)
{
B43_WARN_ON(ring->tx);
- dma_sync_single_for_device(ring->dev->sdev->dma_dev,
+ dma_sync_single_for_device(ring->dev->dev->dma_dev,
addr, len, DMA_FROM_DEVICE);
}
@@ -389,33 +419,34 @@ static int alloc_ringmemory(struct b43_dmaring *ring)
gfp_t flags = GFP_KERNEL;
/* The specs call for 4K buffers for 30- and 32-bit DMA with 4K
- * alignment and 8K buffers for 64-bit DMA with 8K alignment. Testing
- * has shown that 4K is sufficient for the latter as long as the buffer
- * does not cross an 8K boundary.
- *
- * For unknown reasons - possibly a hardware error - the BCM4311 rev
- * 02, which uses 64-bit DMA, needs the ring buffer in very low memory,
- * which accounts for the GFP_DMA flag below.
- *
- * The flags here must match the flags in free_ringmemory below!
+ * alignment and 8K buffers for 64-bit DMA with 8K alignment.
+ * In practice we could use smaller buffers for the latter, but the
+ * alignment is really important because of the hardware bug. If bit
+ * 0x00001000 is used in DMA address, some hardware (like BCM4331)
+ * copies that bit into B43_DMA64_RXSTATUS and we get false values from
+ * B43_DMA64_RXSTATDPTR. Let's just use 8K buffers even if we don't use
+ * more than 256 slots for ring.
*/
- if (ring->type == B43_DMA_64BIT)
- flags |= GFP_DMA;
- ring->descbase = dma_alloc_coherent(ring->dev->sdev->dma_dev,
- B43_DMA_RINGMEMSIZE,
- &(ring->dmabase), flags);
+ u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
+ B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE;
+
+ ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev,
+ ring_mem_size, &(ring->dmabase),
+ flags);
if (!ring->descbase) {
b43err(ring->dev->wl, "DMA ringmemory allocation failed\n");
return -ENOMEM;
}
- memset(ring->descbase, 0, B43_DMA_RINGMEMSIZE);
+ memset(ring->descbase, 0, ring_mem_size);
return 0;
}
static void free_ringmemory(struct b43_dmaring *ring)
{
- dma_free_coherent(ring->dev->sdev->dma_dev, B43_DMA_RINGMEMSIZE,
+ u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
+ B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE;
+ dma_free_coherent(ring->dev->dev->dma_dev, ring_mem_size,
ring->descbase, ring->dmabase);
}
@@ -523,7 +554,7 @@ static bool b43_dma_mapping_error(struct b43_dmaring *ring,
dma_addr_t addr,
size_t buffersize, bool dma_to_device)
{
- if (unlikely(dma_mapping_error(ring->dev->sdev->dma_dev, addr)))
+ if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr)))
return 1;
switch (ring->type) {
@@ -658,36 +689,37 @@ static int dmacontroller_setup(struct b43_dmaring *ring)
int err = 0;
u32 value;
u32 addrext;
- u32 trans = ring->dev->dma.translation;
+ bool parity = ring->dev->dma.parity;
+ u32 addrlo;
+ u32 addrhi;
if (ring->tx) {
if (ring->type == B43_DMA_64BIT) {
u64 ringbase = (u64) (ring->dmabase);
+ addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT);
+ addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW);
+ addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH);
- addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
value = B43_DMA64_TXENABLE;
value |= (addrext << B43_DMA64_TXADDREXT_SHIFT)
& B43_DMA64_TXADDREXT_MASK;
+ if (!parity)
+ value |= B43_DMA64_TXPARITYDISABLE;
b43_dma_write(ring, B43_DMA64_TXCTL, value);
- b43_dma_write(ring, B43_DMA64_TXRINGLO,
- (ringbase & 0xFFFFFFFF));
- b43_dma_write(ring, B43_DMA64_TXRINGHI,
- ((ringbase >> 32) &
- ~SSB_DMA_TRANSLATION_MASK)
- | (trans << 1));
+ b43_dma_write(ring, B43_DMA64_TXRINGLO, addrlo);
+ b43_dma_write(ring, B43_DMA64_TXRINGHI, addrhi);
} else {
u32 ringbase = (u32) (ring->dmabase);
+ addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT);
+ addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW);
- addrext = (ringbase & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
value = B43_DMA32_TXENABLE;
value |= (addrext << B43_DMA32_TXADDREXT_SHIFT)
& B43_DMA32_TXADDREXT_MASK;
+ if (!parity)
+ value |= B43_DMA32_TXPARITYDISABLE;
b43_dma_write(ring, B43_DMA32_TXCTL, value);
- b43_dma_write(ring, B43_DMA32_TXRING,
- (ringbase & ~SSB_DMA_TRANSLATION_MASK)
- | trans);
+ b43_dma_write(ring, B43_DMA32_TXRING, addrlo);
}
} else {
err = alloc_initial_descbuffers(ring);
@@ -695,35 +727,34 @@ static int dmacontroller_setup(struct b43_dmaring *ring)
goto out;
if (ring->type == B43_DMA_64BIT) {
u64 ringbase = (u64) (ring->dmabase);
+ addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT);
+ addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW);
+ addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH);
- addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
value = (ring->frameoffset << B43_DMA64_RXFROFF_SHIFT);
value |= B43_DMA64_RXENABLE;
value |= (addrext << B43_DMA64_RXADDREXT_SHIFT)
& B43_DMA64_RXADDREXT_MASK;
+ if (!parity)
+ value |= B43_DMA64_RXPARITYDISABLE;
b43_dma_write(ring, B43_DMA64_RXCTL, value);
- b43_dma_write(ring, B43_DMA64_RXRINGLO,
- (ringbase & 0xFFFFFFFF));
- b43_dma_write(ring, B43_DMA64_RXRINGHI,
- ((ringbase >> 32) &
- ~SSB_DMA_TRANSLATION_MASK)
- | (trans << 1));
+ b43_dma_write(ring, B43_DMA64_RXRINGLO, addrlo);
+ b43_dma_write(ring, B43_DMA64_RXRINGHI, addrhi);
b43_dma_write(ring, B43_DMA64_RXINDEX, ring->nr_slots *
sizeof(struct b43_dmadesc64));
} else {
u32 ringbase = (u32) (ring->dmabase);
+ addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT);
+ addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW);
- addrext = (ringbase & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
value = (ring->frameoffset << B43_DMA32_RXFROFF_SHIFT);
value |= B43_DMA32_RXENABLE;
value |= (addrext << B43_DMA32_RXADDREXT_SHIFT)
& B43_DMA32_RXADDREXT_MASK;
+ if (!parity)
+ value |= B43_DMA32_RXPARITYDISABLE;
b43_dma_write(ring, B43_DMA32_RXCTL, value);
- b43_dma_write(ring, B43_DMA32_RXRING,
- (ringbase & ~SSB_DMA_TRANSLATION_MASK)
- | trans);
+ b43_dma_write(ring, B43_DMA32_RXRING, addrlo);
b43_dma_write(ring, B43_DMA32_RXINDEX, ring->nr_slots *
sizeof(struct b43_dmadesc32));
}
@@ -757,14 +788,14 @@ static void dmacontroller_cleanup(struct b43_dmaring *ring)
static void free_all_descbuffers(struct b43_dmaring *ring)
{
- struct b43_dmadesc_generic *desc;
struct b43_dmadesc_meta *meta;
int i;
if (!ring->used_slots)
return;
for (i = 0; i < ring->nr_slots; i++) {
- desc = ring->ops->idx2desc(ring, i, &meta);
+ /* get meta - ignore returned value */
+ ring->ops->idx2desc(ring, i, &meta);
if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) {
B43_WARN_ON(!ring->tx);
@@ -786,9 +817,23 @@ static u64 supported_dma_mask(struct b43_wldev *dev)
u32 tmp;
u16 mmio_base;
- tmp = b43_read32(dev, SSB_TMSHIGH);
- if (tmp & SSB_TMSHIGH_DMA64)
- return DMA_BIT_MASK(64);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST);
+ if (tmp & BCMA_IOST_DMA64)
+ return DMA_BIT_MASK(64);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
+ if (tmp & SSB_TMSHIGH_DMA64)
+ return DMA_BIT_MASK(64);
+ break;
+#endif
+ }
+
mmio_base = b43_dmacontroller_base(0, 0);
b43_write32(dev, mmio_base + B43_DMA32_TXCTL, B43_DMA32_TXADDREXT_MASK);
tmp = b43_read32(dev, mmio_base + B43_DMA32_TXCTL);
@@ -849,8 +894,17 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
ring->current_slot = -1;
} else {
if (ring->index == 0) {
- ring->rx_buffersize = B43_DMA0_RX_BUFFERSIZE;
- ring->frameoffset = B43_DMA0_RX_FRAMEOFFSET;
+ switch (dev->fw.hdr_format) {
+ case B43_FW_HDR_598:
+ ring->rx_buffersize = B43_DMA0_RX_FW598_BUFSIZE;
+ ring->frameoffset = B43_DMA0_RX_FW598_FO;
+ break;
+ case B43_FW_HDR_410:
+ case B43_FW_HDR_351:
+ ring->rx_buffersize = B43_DMA0_RX_FW351_BUFSIZE;
+ ring->frameoffset = B43_DMA0_RX_FW351_FO;
+ break;
+ }
} else
B43_WARN_ON(1);
}
@@ -869,7 +923,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
goto err_kfree_meta;
/* test for ability to dma to txhdr_cache */
- dma_test = dma_map_single(dev->sdev->dma_dev,
+ dma_test = dma_map_single(dev->dev->dma_dev,
ring->txhdr_cache,
b43_txhdr_size(dev),
DMA_TO_DEVICE);
@@ -884,7 +938,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
if (!ring->txhdr_cache)
goto err_kfree_meta;
- dma_test = dma_map_single(dev->sdev->dma_dev,
+ dma_test = dma_map_single(dev->dev->dma_dev,
ring->txhdr_cache,
b43_txhdr_size(dev),
DMA_TO_DEVICE);
@@ -898,7 +952,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
}
}
- dma_unmap_single(dev->sdev->dma_dev,
+ dma_unmap_single(dev->dev->dma_dev,
dma_test, b43_txhdr_size(dev),
DMA_TO_DEVICE);
}
@@ -1013,9 +1067,9 @@ static int b43_dma_set_mask(struct b43_wldev *dev, u64 mask)
/* Try to set the DMA mask. If it fails, try falling back to a
* lower mask, as we can always also support a lower one. */
while (1) {
- err = dma_set_mask(dev->sdev->dma_dev, mask);
+ err = dma_set_mask(dev->dev->dma_dev, mask);
if (!err) {
- err = dma_set_coherent_mask(dev->sdev->dma_dev, mask);
+ err = dma_set_coherent_mask(dev->dev->dma_dev, mask);
if (!err)
break;
}
@@ -1043,6 +1097,25 @@ static int b43_dma_set_mask(struct b43_wldev *dev, u64 mask)
return 0;
}
+/* Some hardware with 64-bit DMA seems to be bugged and looks for translation
+ * bit in low address word instead of high one.
+ */
+static bool b43_dma_translation_in_low_word(struct b43_wldev *dev,
+ enum b43_dmatype type)
+{
+ if (type != B43_DMA_64BIT)
+ return 1;
+
+#ifdef CONFIG_B43_SSB
+ if (dev->dev->bus_type == B43_BUS_SSB &&
+ dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
+ !(dev->dev->sdev->bus->host_pci->is_pcie &&
+ ssb_read32(dev->dev->sdev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64))
+ return 1;
+#endif
+ return 0;
+}
+
int b43_dma_init(struct b43_wldev *dev)
{
struct b43_dma *dma = &dev->dma;
@@ -1055,7 +1128,27 @@ int b43_dma_init(struct b43_wldev *dev)
err = b43_dma_set_mask(dev, dmamask);
if (err)
return err;
- dma->translation = ssb_dma_translation(dev->sdev);
+
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ dma->translation = bcma_core_dma_translation(dev->dev->bdev);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ dma->translation = ssb_dma_translation(dev->dev->sdev);
+ break;
+#endif
+ }
+ dma->translation_in_low = b43_dma_translation_in_low_word(dev, type);
+
+ dma->parity = true;
+#ifdef CONFIG_B43_BCMA
+ /* TODO: find out which SSB devices need disabling parity */
+ if (dev->dev->bus_type == B43_BUS_BCMA)
+ dma->parity = false;
+#endif
err = -ENOMEM;
/* setup TX DMA channels. */
@@ -1085,7 +1178,7 @@ int b43_dma_init(struct b43_wldev *dev)
goto err_destroy_mcast;
/* No support for the TX status DMA ring. */
- B43_WARN_ON(dev->sdev->id.revision < 5);
+ B43_WARN_ON(dev->dev->core_rev < 5);
b43dbg(dev->wl, "%u-bit DMA initialized\n",
(unsigned int)type);
@@ -1388,7 +1481,6 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
{
const struct b43_dma_ops *ops;
struct b43_dmaring *ring;
- struct b43_dmadesc_generic *desc;
struct b43_dmadesc_meta *meta;
int slot, firstused;
bool frame_succeed;
@@ -1416,7 +1508,8 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
ops = ring->ops;
while (1) {
B43_WARN_ON(slot < 0 || slot >= ring->nr_slots);
- desc = ops->idx2desc(ring, slot, &meta);
+ /* get meta - ignore returned value */
+ ops->idx2desc(ring, slot, &meta);
if (b43_dma_ptr_is_poisoned(meta->skb)) {
b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) "
@@ -1600,6 +1693,7 @@ void b43_dma_rx(struct b43_dmaring *ring)
dma_rx(ring, &slot);
update_max_used_slots(ring, ++used_slots);
}
+ wmb();
ops->set_current_rxslot(ring, slot);
ring->current_slot = slot;
}
diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h
index e8a80a1251bf..315b96ed1d90 100644
--- a/drivers/net/wireless/b43/dma.h
+++ b/drivers/net/wireless/b43/dma.h
@@ -20,6 +20,7 @@
#define B43_DMA32_TXSUSPEND 0x00000002
#define B43_DMA32_TXLOOPBACK 0x00000004
#define B43_DMA32_TXFLUSH 0x00000010
+#define B43_DMA32_TXPARITYDISABLE 0x00000800
#define B43_DMA32_TXADDREXT_MASK 0x00030000
#define B43_DMA32_TXADDREXT_SHIFT 16
#define B43_DMA32_TXRING 0x04
@@ -44,6 +45,7 @@
#define B43_DMA32_RXFROFF_MASK 0x000000FE
#define B43_DMA32_RXFROFF_SHIFT 1
#define B43_DMA32_RXDIRECTFIFO 0x00000100
+#define B43_DMA32_RXPARITYDISABLE 0x00000800
#define B43_DMA32_RXADDREXT_MASK 0x00030000
#define B43_DMA32_RXADDREXT_SHIFT 16
#define B43_DMA32_RXRING 0x14
@@ -84,6 +86,7 @@ struct b43_dmadesc32 {
#define B43_DMA64_TXSUSPEND 0x00000002
#define B43_DMA64_TXLOOPBACK 0x00000004
#define B43_DMA64_TXFLUSH 0x00000010
+#define B43_DMA64_TXPARITYDISABLE 0x00000800
#define B43_DMA64_TXADDREXT_MASK 0x00030000
#define B43_DMA64_TXADDREXT_SHIFT 16
#define B43_DMA64_TXINDEX 0x04
@@ -111,6 +114,7 @@ struct b43_dmadesc32 {
#define B43_DMA64_RXFROFF_MASK 0x000000FE
#define B43_DMA64_RXFROFF_SHIFT 1
#define B43_DMA64_RXDIRECTFIFO 0x00000100
+#define B43_DMA64_RXPARITYDISABLE 0x00000800
#define B43_DMA64_RXADDREXT_MASK 0x00030000
#define B43_DMA64_RXADDREXT_SHIFT 16
#define B43_DMA64_RXINDEX 0x24
@@ -157,13 +161,17 @@ struct b43_dmadesc_generic {
} __packed;
/* Misc DMA constants */
-#define B43_DMA_RINGMEMSIZE PAGE_SIZE
-#define B43_DMA0_RX_FRAMEOFFSET 30
+#define B43_DMA32_RINGMEMSIZE 4096
+#define B43_DMA64_RINGMEMSIZE 8192
+/* Offset of frame with actual data */
+#define B43_DMA0_RX_FW598_FO 38
+#define B43_DMA0_RX_FW351_FO 30
/* DMA engine tuning knobs */
#define B43_TXRING_SLOTS 256
#define B43_RXRING_SLOTS 64
-#define B43_DMA0_RX_BUFFERSIZE (B43_DMA0_RX_FRAMEOFFSET + IEEE80211_MAX_FRAME_LEN)
+#define B43_DMA0_RX_FW598_BUFSIZE (B43_DMA0_RX_FW598_FO + IEEE80211_MAX_FRAME_LEN)
+#define B43_DMA0_RX_FW351_BUFSIZE (B43_DMA0_RX_FW351_FO + IEEE80211_MAX_FRAME_LEN)
/* Pointer poison */
#define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM))
@@ -208,6 +216,12 @@ enum b43_dmatype {
B43_DMA_64BIT = 64,
};
+enum b43_addrtype {
+ B43_DMA_ADDR_LOW,
+ B43_DMA_ADDR_HIGH,
+ B43_DMA_ADDR_EXT,
+};
+
struct b43_dmaring {
/* Lowlevel DMA ops. */
const struct b43_dma_ops *ops;
diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c
index 0cafafe368af..a38c1c6446ad 100644
--- a/drivers/net/wireless/b43/leds.c
+++ b/drivers/net/wireless/b43/leds.c
@@ -5,7 +5,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Copyright (c) 2005 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
@@ -138,7 +138,7 @@ static int b43_register_led(struct b43_wldev *dev, struct b43_led *led,
led->led_dev.default_trigger = default_trigger;
led->led_dev.brightness_set = b43_led_brightness_set;
- err = led_classdev_register(dev->sdev->dev, &led->led_dev);
+ err = led_classdev_register(dev->dev->dev, &led->led_dev);
if (err) {
b43warn(dev->wl, "LEDs: Failed to register %s\n", name);
led->wl = NULL;
@@ -215,13 +215,12 @@ static void b43_led_get_sprominfo(struct b43_wldev *dev,
enum b43_led_behaviour *behaviour,
bool *activelow)
{
- struct ssb_bus *bus = dev->sdev->bus;
u8 sprom[4];
- sprom[0] = bus->sprom.gpio0;
- sprom[1] = bus->sprom.gpio1;
- sprom[2] = bus->sprom.gpio2;
- sprom[3] = bus->sprom.gpio3;
+ sprom[0] = dev->dev->bus_sprom->gpio0;
+ sprom[1] = dev->dev->bus_sprom->gpio1;
+ sprom[2] = dev->dev->bus_sprom->gpio2;
+ sprom[3] = dev->dev->bus_sprom->gpio3;
if (sprom[led_index] == 0xFF) {
/* There is no LED information in the SPROM
@@ -231,12 +230,12 @@ static void b43_led_get_sprominfo(struct b43_wldev *dev,
case 0:
*behaviour = B43_LED_ACTIVITY;
*activelow = 1;
- if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ)
+ if (dev->dev->board_vendor == PCI_VENDOR_ID_COMPAQ)
*behaviour = B43_LED_RADIO_ALL;
break;
case 1:
*behaviour = B43_LED_RADIO_B;
- if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK)
+ if (dev->dev->board_vendor == PCI_VENDOR_ID_ASUSTEK)
*behaviour = B43_LED_ASSOC;
break;
case 2:
diff --git a/drivers/net/wireless/b43/lo.c b/drivers/net/wireless/b43/lo.c
index 2ef7d4b38540..4c82d582a524 100644
--- a/drivers/net/wireless/b43/lo.c
+++ b/drivers/net/wireless/b43/lo.c
@@ -6,7 +6,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Copyright (c) 2005, 2006 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch>
@@ -98,7 +98,7 @@ static u16 lo_measure_feedthrough(struct b43_wldev *dev,
rfover |= pga;
rfover |= lna;
rfover |= trsw_rx;
- if ((dev->sdev->bus->sprom.boardflags_lo & B43_BFL_EXTLNA)
+ if ((dev->dev->bus_sprom->boardflags_lo & B43_BFL_EXTLNA)
&& phy->rev > 6)
rfover |= B43_PHY_RFOVERVAL_EXTLNA;
@@ -301,14 +301,12 @@ static void lo_measure_gain_values(struct b43_wldev *dev,
max_rx_gain = 0;
if (has_loopback_gain(phy)) {
- int trsw_rx = 0;
int trsw_rx_gain;
if (use_trsw_rx) {
trsw_rx_gain = gphy->trsw_rx_gain / 2;
if (max_rx_gain >= trsw_rx_gain) {
trsw_rx_gain = max_rx_gain - trsw_rx_gain;
- trsw_rx = 0x20;
}
} else
trsw_rx_gain = max_rx_gain;
@@ -387,7 +385,7 @@ struct lo_g_saved_values {
static void lo_measure_setup(struct b43_wldev *dev,
struct lo_g_saved_values *sav)
{
- struct ssb_sprom *sprom = &dev->sdev->bus->sprom;
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
struct b43_phy *phy = &dev->phy;
struct b43_phy_g *gphy = phy->g;
struct b43_txpower_lo_control *lo = gphy->lo_control;
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index eb4159686985..5634d9a9965b 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -4,9 +4,10 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>
Copyright (c) 2005 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (c) 2005-2009 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005-2009 Michael Buesch <m@bues.ch>
Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
+ Copyright (c) 2010-2011 Rafał Miłecki <zajec5@gmail.com>
SDIO support
Copyright (c) 2009 Albert Herranz <albert_herranz@yahoo.es>
@@ -33,11 +34,10 @@
#include <linux/delay.h>
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include <linux/firmware.h>
-#include <linux/wireless.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/io.h>
@@ -65,9 +65,9 @@ MODULE_AUTHOR("Martin Langer");
MODULE_AUTHOR("Stefano Brivio");
MODULE_AUTHOR("Michael Buesch");
MODULE_AUTHOR("Gábor Stefanik");
+MODULE_AUTHOR("Rafał Miłecki");
MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(B43_SUPPORTED_FIRMWARE_ID);
MODULE_FIRMWARE("b43/ucode11.fw");
MODULE_FIRMWARE("b43/ucode13.fw");
MODULE_FIRMWARE("b43/ucode14.fw");
@@ -109,10 +109,22 @@ int b43_modparam_verbose = B43_VERBOSITY_DEFAULT;
module_param_named(verbose, b43_modparam_verbose, int, 0644);
MODULE_PARM_DESC(verbose, "Log message verbosity: 0=error, 1=warn, 2=info(default), 3=debug");
-static int b43_modparam_pio = B43_PIO_DEFAULT;
+static int b43_modparam_pio = 0;
module_param_named(pio, b43_modparam_pio, int, 0644);
MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO");
+#ifdef CONFIG_B43_BCMA
+static const struct bcma_device_id b43_bcma_tbl[] = {
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS),
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS),
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS),
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS),
+ BCMA_CORETABLE_END
+};
+MODULE_DEVICE_TABLE(bcma, b43_bcma_tbl);
+#endif
+
+#ifdef CONFIG_B43_SSB
static const struct ssb_device_id b43_ssb_tbl[] = {
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5),
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 6),
@@ -126,8 +138,8 @@ static const struct ssb_device_id b43_ssb_tbl[] = {
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 16),
SSB_DEVTABLE_END
};
-
MODULE_DEVICE_TABLE(ssb, b43_ssb_tbl);
+#endif
/* Channel and ratetables are shared for all devices.
* They can't be const, because ieee80211 puts some precalculated
@@ -309,6 +321,10 @@ static void b43_wireless_core_exit(struct b43_wldev *dev);
static int b43_wireless_core_init(struct b43_wldev *dev);
static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev);
static int b43_wireless_core_start(struct b43_wldev *dev);
+static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf,
+ u32 changed);
static int b43_ratelimit(struct b43_wl *wl)
{
@@ -548,7 +564,7 @@ void b43_tsf_read(struct b43_wldev *dev, u64 *tsf)
{
u32 low, high;
- B43_WARN_ON(dev->sdev->id.revision < 3);
+ B43_WARN_ON(dev->dev->core_rev < 3);
/* The hardware guarantees us an atomic read, if we
* read the low register first. */
@@ -586,7 +602,7 @@ static void b43_tsf_write_locked(struct b43_wldev *dev, u64 tsf)
{
u32 low, high;
- B43_WARN_ON(dev->sdev->id.revision < 3);
+ B43_WARN_ON(dev->dev->core_rev < 3);
low = tsf;
high = (tsf >> 32);
@@ -713,52 +729,59 @@ void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on)
for (i = 0; i < 5; i++)
b43_ram_write(dev, i * 4, buffer[i]);
- b43_write16(dev, 0x0568, 0x0000);
- if (dev->sdev->id.revision < 11)
- b43_write16(dev, 0x07C0, 0x0000);
+ b43_write16(dev, B43_MMIO_XMTSEL, 0x0000);
+
+ if (dev->dev->core_rev < 11)
+ b43_write16(dev, B43_MMIO_WEPCTL, 0x0000);
else
- b43_write16(dev, 0x07C0, 0x0100);
+ b43_write16(dev, B43_MMIO_WEPCTL, 0x0100);
+
value = (ofdm ? 0x41 : 0x40);
- b43_write16(dev, 0x050C, value);
- if ((phy->type == B43_PHYTYPE_N) || (phy->type == B43_PHYTYPE_LP))
- b43_write16(dev, 0x0514, 0x1A02);
- b43_write16(dev, 0x0508, 0x0000);
- b43_write16(dev, 0x050A, 0x0000);
- b43_write16(dev, 0x054C, 0x0000);
- b43_write16(dev, 0x056A, 0x0014);
- b43_write16(dev, 0x0568, 0x0826);
- b43_write16(dev, 0x0500, 0x0000);
- if (!pa_on && (phy->type == B43_PHYTYPE_N)) {
- //SPEC TODO
- }
+ b43_write16(dev, B43_MMIO_TXE0_PHYCTL, value);
+ if (phy->type == B43_PHYTYPE_N || phy->type == B43_PHYTYPE_LP ||
+ phy->type == B43_PHYTYPE_LCN)
+ b43_write16(dev, B43_MMIO_TXE0_PHYCTL1, 0x1A02);
+
+ b43_write16(dev, B43_MMIO_TXE0_WM_0, 0x0000);
+ b43_write16(dev, B43_MMIO_TXE0_WM_1, 0x0000);
+
+ b43_write16(dev, B43_MMIO_XMTTPLATETXPTR, 0x0000);
+ b43_write16(dev, B43_MMIO_XMTTXCNT, 0x0014);
+ b43_write16(dev, B43_MMIO_XMTSEL, 0x0826);
+ b43_write16(dev, B43_MMIO_TXE0_CTL, 0x0000);
+
+ if (!pa_on && phy->type == B43_PHYTYPE_N)
+ ; /*b43_nphy_pa_override(dev, false) */
switch (phy->type) {
case B43_PHYTYPE_N:
- b43_write16(dev, 0x0502, 0x00D0);
+ case B43_PHYTYPE_LCN:
+ b43_write16(dev, B43_MMIO_TXE0_AUX, 0x00D0);
break;
case B43_PHYTYPE_LP:
- b43_write16(dev, 0x0502, 0x0050);
+ b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0050);
break;
default:
- b43_write16(dev, 0x0502, 0x0030);
+ b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0030);
}
+ b43_read16(dev, B43_MMIO_TXE0_AUX);
if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5)
b43_radio_write16(dev, 0x0051, 0x0017);
for (i = 0x00; i < max_loop; i++) {
- value = b43_read16(dev, 0x050E);
+ value = b43_read16(dev, B43_MMIO_TXE0_STATUS);
if (value & 0x0080)
break;
udelay(10);
}
for (i = 0x00; i < 0x0A; i++) {
- value = b43_read16(dev, 0x050E);
+ value = b43_read16(dev, B43_MMIO_TXE0_STATUS);
if (value & 0x0400)
break;
udelay(10);
}
for (i = 0x00; i < 0x19; i++) {
- value = b43_read16(dev, 0x0690);
+ value = b43_read16(dev, B43_MMIO_IFSSTAT);
if (!(value & 0x0100))
break;
udelay(10);
@@ -1132,7 +1155,7 @@ void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags)
b43_write32(dev, B43_MMIO_MACCTL, macctl);
/* Commit write */
b43_read32(dev, B43_MMIO_MACCTL);
- if (awake && dev->sdev->id.revision >= 5) {
+ if (awake && dev->dev->core_rev >= 5) {
/* Wait for the microcode to wake up. */
for (i = 0; i < 100; i++) {
ucstat = b43_shm_read16(dev, B43_SHM_SHARED,
@@ -1144,35 +1167,85 @@ void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags)
}
}
-static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, u32 flags)
+#ifdef CONFIG_B43_BCMA
+static void b43_bcma_phy_reset(struct b43_wldev *dev)
{
+ u32 flags;
+
+ /* Put PHY into reset */
+ flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+ flags |= B43_BCMA_IOCTL_PHY_RESET;
+ flags |= B43_BCMA_IOCTL_PHY_BW_20MHZ; /* Make 20 MHz def */
+ bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags);
+ udelay(2);
+
+ /* Take PHY out of reset */
+ flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+ flags &= ~B43_BCMA_IOCTL_PHY_RESET;
+ flags |= BCMA_IOCTL_FGC;
+ bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags);
+ udelay(1);
+
+ /* Do not force clock anymore */
+ flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+ flags &= ~BCMA_IOCTL_FGC;
+ bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags);
+ udelay(1);
+}
+
+static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode)
+{
+ b43_device_enable(dev, B43_BCMA_IOCTL_PHY_CLKEN);
+ bcma_core_set_clockmode(dev->dev->bdev, BCMA_CLKMODE_FAST);
+ b43_bcma_phy_reset(dev);
+ bcma_core_pll_ctl(dev->dev->bdev, 0x300, 0x3000000, true);
+}
+#endif
+
+static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode)
+{
+ struct ssb_device *sdev = dev->dev->sdev;
u32 tmslow;
+ u32 flags = 0;
+ if (gmode)
+ flags |= B43_TMSLOW_GMODE;
flags |= B43_TMSLOW_PHYCLKEN;
flags |= B43_TMSLOW_PHYRESET;
if (dev->phy.type == B43_PHYTYPE_N)
flags |= B43_TMSLOW_PHY_BANDWIDTH_20MHZ; /* Make 20 MHz def */
- ssb_device_enable(dev->sdev, flags);
+ b43_device_enable(dev, flags);
msleep(2); /* Wait for the PLL to turn on. */
/* Now take the PHY out of Reset again */
- tmslow = ssb_read32(dev->sdev, SSB_TMSLOW);
+ tmslow = ssb_read32(sdev, SSB_TMSLOW);
tmslow |= SSB_TMSLOW_FGC;
tmslow &= ~B43_TMSLOW_PHYRESET;
- ssb_write32(dev->sdev, SSB_TMSLOW, tmslow);
- ssb_read32(dev->sdev, SSB_TMSLOW); /* flush */
+ ssb_write32(sdev, SSB_TMSLOW, tmslow);
+ ssb_read32(sdev, SSB_TMSLOW); /* flush */
msleep(1);
tmslow &= ~SSB_TMSLOW_FGC;
- ssb_write32(dev->sdev, SSB_TMSLOW, tmslow);
- ssb_read32(dev->sdev, SSB_TMSLOW); /* flush */
+ ssb_write32(sdev, SSB_TMSLOW, tmslow);
+ ssb_read32(sdev, SSB_TMSLOW); /* flush */
msleep(1);
}
-void b43_wireless_core_reset(struct b43_wldev *dev, u32 flags)
+void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode)
{
u32 macctl;
- b43_ssb_wireless_core_reset(dev, flags);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ b43_bcma_wireless_core_reset(dev, gmode);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ b43_ssb_wireless_core_reset(dev, gmode);
+ break;
+#endif
+ }
/* Turn Analog ON, but only if we already know the PHY-type.
* This protects against very early setup where we don't know the
@@ -1183,7 +1256,7 @@ void b43_wireless_core_reset(struct b43_wldev *dev, u32 flags)
macctl = b43_read32(dev, B43_MMIO_MACCTL);
macctl &= ~B43_MACCTL_GMODE;
- if (flags & B43_TMSLOW_GMODE)
+ if (gmode)
macctl |= B43_MACCTL_GMODE;
macctl |= B43_MACCTL_IHR_ENABLED;
b43_write32(dev, B43_MMIO_MACCTL, macctl);
@@ -1221,7 +1294,7 @@ static void drain_txstatus_queue(struct b43_wldev *dev)
{
u32 dummy;
- if (dev->sdev->id.revision < 5)
+ if (dev->dev->core_rev < 5)
return;
/* Read all entries from the microcode TXstatus FIFO
* and throw them away.
@@ -1427,9 +1500,9 @@ u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev,
/* Get the mask of available antennas. */
if (dev->phy.gmode)
- antenna_mask = dev->sdev->bus->sprom.ant_available_bg;
+ antenna_mask = dev->dev->bus_sprom->ant_available_bg;
else
- antenna_mask = dev->sdev->bus->sprom.ant_available_a;
+ antenna_mask = dev->dev->bus_sprom->ant_available_a;
if (!(antenna_mask & (1 << (antenna_nr - 1)))) {
/* This antenna is not available. Fall back to default. */
@@ -1571,7 +1644,8 @@ static void handle_irq_beacon(struct b43_wldev *dev)
u32 cmd, beacon0_valid, beacon1_valid;
if (!b43_is_mode(wl, NL80211_IFTYPE_AP) &&
- !b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT))
+ !b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) &&
+ !b43_is_mode(wl, NL80211_IFTYPE_ADHOC))
return;
/* This is the bottom half of the asynchronous beacon update. */
@@ -1644,7 +1718,7 @@ static void b43_beacon_update_trigger_work(struct work_struct *work)
mutex_lock(&wl->mutex);
dev = wl->current_dev;
if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) {
- if (dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO) {
+ if (b43_bus_host_is_sdio(dev->dev)) {
/* wl->mutex is enough. */
b43_do_beacon_update_trigger_work(dev);
mmiowb();
@@ -1689,7 +1763,7 @@ static void b43_update_templates(struct b43_wl *wl)
static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int)
{
b43_time_lock(dev);
- if (dev->sdev->id.revision >= 3) {
+ if (dev->dev->core_rev >= 3) {
b43_write32(dev, B43_MMIO_TSF_CFP_REP, (beacon_int << 16));
b43_write32(dev, B43_MMIO_TSF_CFP_START, (beacon_int << 10));
} else {
@@ -1923,7 +1997,7 @@ static irqreturn_t b43_do_interrupt(struct b43_wldev *dev)
return IRQ_NONE;
reason &= dev->irq_mask;
if (!reason)
- return IRQ_HANDLED;
+ return IRQ_NONE;
dev->dma_reason[0] = b43_read32(dev, B43_MMIO_DMA0_REASON)
& 0x0001DC00;
@@ -2063,7 +2137,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,
B43_WARN_ON(1);
return -ENOSYS;
}
- err = request_firmware(&blob, ctx->fwname, ctx->dev->sdev->dev);
+ err = request_firmware(&blob, ctx->fwname, ctx->dev->dev->dev);
if (err == -ENOENT) {
snprintf(ctx->errors[ctx->req_type],
sizeof(ctx->errors[ctx->req_type]),
@@ -2113,26 +2187,48 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
{
struct b43_wldev *dev = ctx->dev;
struct b43_firmware *fw = &ctx->dev->fw;
- const u8 rev = ctx->dev->sdev->id.revision;
+ const u8 rev = ctx->dev->dev->core_rev;
const char *filename;
u32 tmshigh;
int err;
+ /* Files for HT and LCN were found by trying one by one */
+
/* Get microcode */
- if ((rev >= 5) && (rev <= 10))
+ if ((rev >= 5) && (rev <= 10)) {
filename = "ucode5";
- else if ((rev >= 11) && (rev <= 12))
+ } else if ((rev >= 11) && (rev <= 12)) {
filename = "ucode11";
- else if (rev == 13)
+ } else if (rev == 13) {
filename = "ucode13";
- else if (rev == 14)
+ } else if (rev == 14) {
filename = "ucode14";
- else if (rev == 15)
+ } else if (rev == 15) {
filename = "ucode15";
- else if ((rev >= 16) && (rev <= 20))
- filename = "ucode16_mimo";
- else
- goto err_no_ucode;
+ } else {
+ switch (dev->phy.type) {
+ case B43_PHYTYPE_N:
+ if (rev >= 16)
+ filename = "ucode16_mimo";
+ else
+ goto err_no_ucode;
+ break;
+ case B43_PHYTYPE_HT:
+ if (rev == 29)
+ filename = "ucode29_mimo";
+ else
+ goto err_no_ucode;
+ break;
+ case B43_PHYTYPE_LCN:
+ if (rev == 24)
+ filename = "ucode24_mimo";
+ else
+ goto err_no_ucode;
+ break;
+ default:
+ goto err_no_ucode;
+ }
+ }
err = b43_do_request_fw(ctx, filename, &fw->ucode);
if (err)
goto err_load;
@@ -2157,7 +2253,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
switch (dev->phy.type) {
case B43_PHYTYPE_A:
if ((rev >= 5) && (rev <= 10)) {
- tmshigh = ssb_read32(dev->sdev, SSB_TMSHIGH);
+ tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
filename = "a0g1initvals5";
else
@@ -2191,6 +2287,18 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
else
goto err_no_initvals;
break;
+ case B43_PHYTYPE_HT:
+ if (rev == 29)
+ filename = "ht0initvals29";
+ else
+ goto err_no_initvals;
+ break;
+ case B43_PHYTYPE_LCN:
+ if (rev == 24)
+ filename = "lcn0initvals24";
+ else
+ goto err_no_initvals;
+ break;
default:
goto err_no_initvals;
}
@@ -2202,7 +2310,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
switch (dev->phy.type) {
case B43_PHYTYPE_A:
if ((rev >= 5) && (rev <= 10)) {
- tmshigh = ssb_read32(dev->sdev, SSB_TMSHIGH);
+ tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
filename = "a0g1bsinitvals5";
else
@@ -2238,6 +2346,18 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
else
goto err_no_initvals;
break;
+ case B43_PHYTYPE_HT:
+ if (rev == 29)
+ filename = "ht0bsinitvals29";
+ else
+ goto err_no_initvals;
+ break;
+ case B43_PHYTYPE_LCN:
+ if (rev == 24)
+ filename = "lcn0bsinitvals24";
+ else
+ goto err_no_initvals;
+ break;
default:
goto err_no_initvals;
}
@@ -2403,6 +2523,12 @@ static int b43_upload_microcode(struct b43_wldev *dev)
}
dev->fw.rev = fwrev;
dev->fw.patch = fwpatch;
+ if (dev->fw.rev >= 598)
+ dev->fw.hdr_format = B43_FW_HDR_598;
+ else if (dev->fw.rev >= 410)
+ dev->fw.hdr_format = B43_FW_HDR_410;
+ else
+ dev->fw.hdr_format = B43_FW_HDR_351;
dev->fw.opensource = (fwdate == 0xFFFF);
/* Default to use-all-queues. */
@@ -2448,9 +2574,9 @@ static int b43_upload_microcode(struct b43_wldev *dev)
snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "%u.%u",
dev->fw.rev, dev->fw.patch);
- wiphy->hw_version = dev->sdev->id.coreid;
+ wiphy->hw_version = dev->dev->core_id;
- if (b43_is_old_txhdr_format(dev)) {
+ if (dev->fw.hdr_format == B43_FW_HDR_351) {
/* We're over the deadline, but we keep support for old fw
* until it turns out to be in major conflict with something new. */
b43warn(dev->wl, "You are using an old firmware image. "
@@ -2566,7 +2692,7 @@ out:
*/
static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_bus *bus = dev->dev->sdev->bus;
#ifdef CONFIG_SSB_DRIVER_PCICORE
return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev);
@@ -2588,7 +2714,7 @@ static int b43_gpio_init(struct b43_wldev *dev)
mask = 0x0000001F;
set = 0x0000000F;
- if (dev->sdev->bus->chip_id == 0x4301) {
+ if (dev->dev->chip_id == 0x4301) {
mask |= 0x0060;
set |= 0x0060;
}
@@ -2599,21 +2725,34 @@ static int b43_gpio_init(struct b43_wldev *dev)
mask |= 0x0180;
set |= 0x0180;
}
- if (dev->sdev->bus->sprom.boardflags_lo & B43_BFL_PACTRL) {
+ if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) {
b43_write16(dev, B43_MMIO_GPIO_MASK,
b43_read16(dev, B43_MMIO_GPIO_MASK)
| 0x0200);
mask |= 0x0200;
set |= 0x0200;
}
- if (dev->sdev->id.revision >= 2)
+ if (dev->dev->core_rev >= 2)
mask |= 0x0010; /* FIXME: This is redundant. */
- gpiodev = b43_ssb_gpio_dev(dev);
- if (gpiodev)
- ssb_write32(gpiodev, B43_GPIO_CONTROL,
- (ssb_read32(gpiodev, B43_GPIO_CONTROL)
- & mask) | set);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL,
+ (bcma_cc_read32(&dev->dev->bdev->bus->drv_cc,
+ BCMA_CC_GPIOCTL) & mask) | set);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ gpiodev = b43_ssb_gpio_dev(dev);
+ if (gpiodev)
+ ssb_write32(gpiodev, B43_GPIO_CONTROL,
+ (ssb_read32(gpiodev, B43_GPIO_CONTROL)
+ & mask) | set);
+ break;
+#endif
+ }
return 0;
}
@@ -2623,9 +2762,21 @@ static void b43_gpio_cleanup(struct b43_wldev *dev)
{
struct ssb_device *gpiodev;
- gpiodev = b43_ssb_gpio_dev(dev);
- if (gpiodev)
- ssb_write32(gpiodev, B43_GPIO_CONTROL, 0);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL,
+ 0);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ gpiodev = b43_ssb_gpio_dev(dev);
+ if (gpiodev)
+ ssb_write32(gpiodev, B43_GPIO_CONTROL, 0);
+ break;
+#endif
+ }
}
/* http://bcm-specs.sipsolutions.net/EnableMac */
@@ -2697,12 +2848,30 @@ out:
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/MacPhyClkSet */
void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on)
{
- u32 tmslow = ssb_read32(dev->sdev, SSB_TMSLOW);
- if (on)
- tmslow |= B43_TMSLOW_MACPHYCLKEN;
- else
- tmslow &= ~B43_TMSLOW_MACPHYCLKEN;
- ssb_write32(dev->sdev, SSB_TMSLOW, tmslow);
+ u32 tmp;
+
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+ if (on)
+ tmp |= B43_BCMA_IOCTL_MACPHYCLKEN;
+ else
+ tmp &= ~B43_BCMA_IOCTL_MACPHYCLKEN;
+ bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+ if (on)
+ tmp |= B43_TMSLOW_MACPHYCLKEN;
+ else
+ tmp &= ~B43_TMSLOW_MACPHYCLKEN;
+ ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+ break;
+#endif
+ }
}
static void b43_adjust_opmode(struct b43_wldev *dev)
@@ -2741,15 +2910,15 @@ static void b43_adjust_opmode(struct b43_wldev *dev)
/* Workaround: On old hardware the HW-MAC-address-filter
* doesn't work properly, so always run promisc in filter
* it in software. */
- if (dev->sdev->id.revision <= 4)
+ if (dev->dev->core_rev <= 4)
ctl |= B43_MACCTL_PROMISC;
b43_write32(dev, B43_MMIO_MACCTL, ctl);
cfp_pretbtt = 2;
if ((ctl & B43_MACCTL_INFRA) && !(ctl & B43_MACCTL_AP)) {
- if (dev->sdev->bus->chip_id == 0x4306 &&
- dev->sdev->bus->chip_rev == 3)
+ if (dev->dev->chip_id == 0x4306 &&
+ dev->dev->chip_rev == 3)
cfp_pretbtt = 100;
else
cfp_pretbtt = 50;
@@ -2793,6 +2962,8 @@ static void b43_rate_memory_init(struct b43_wldev *dev)
case B43_PHYTYPE_G:
case B43_PHYTYPE_N:
case B43_PHYTYPE_LP:
+ case B43_PHYTYPE_HT:
+ case B43_PHYTYPE_LCN:
b43_rate_memory_write(dev, B43_OFDM_RATE_6MB, 1);
b43_rate_memory_write(dev, B43_OFDM_RATE_12MB, 1);
b43_rate_memory_write(dev, B43_OFDM_RATE_18MB, 1);
@@ -2907,7 +3078,7 @@ static int b43_chip_init(struct b43_wldev *dev)
b43_write16(dev, 0x005E, value16);
}
b43_write32(dev, 0x0100, 0x01000000);
- if (dev->sdev->id.revision < 5)
+ if (dev->dev->core_rev < 5)
b43_write32(dev, 0x010C, 0x01000000);
b43_write32(dev, B43_MMIO_MACCTL, b43_read32(dev, B43_MMIO_MACCTL)
@@ -2922,7 +3093,7 @@ static int b43_chip_init(struct b43_wldev *dev)
/* Initially set the wireless operation mode. */
b43_adjust_opmode(dev);
- if (dev->sdev->id.revision < 3) {
+ if (dev->dev->core_rev < 3) {
b43_write16(dev, 0x060E, 0x0000);
b43_write16(dev, 0x0610, 0x8000);
b43_write16(dev, 0x0604, 0x0000);
@@ -2941,8 +3112,20 @@ static int b43_chip_init(struct b43_wldev *dev)
b43_mac_phy_clock_set(dev, true);
- b43_write16(dev, B43_MMIO_POWERUP_DELAY,
- dev->sdev->bus->chipco.fast_pwrup_delay);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ /* FIXME: 0xE74 is quite common, but should be read from CC */
+ b43_write16(dev, B43_MMIO_POWERUP_DELAY, 0xE74);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ b43_write16(dev, B43_MMIO_POWERUP_DELAY,
+ dev->dev->sdev->bus->chipco.fast_pwrup_delay);
+ break;
+#endif
+ }
err = 0;
b43dbg(dev->wl, "Chip initialized\n");
@@ -3105,7 +3288,7 @@ static int b43_validate_chipaccess(struct b43_wldev *dev)
b43_shm_write32(dev, B43_SHM_SHARED, 0, backup0);
b43_shm_write32(dev, B43_SHM_SHARED, 4, backup4);
- if ((dev->sdev->id.revision >= 3) && (dev->sdev->id.revision <= 10)) {
+ if ((dev->dev->core_rev >= 3) && (dev->dev->core_rev <= 10)) {
/* The 32bit register shadows the two 16bit registers
* with update sideeffects. Validate this. */
b43_write16(dev, B43_MMIO_TSF_CFP_START, 0xAAAA);
@@ -3376,7 +3559,8 @@ static void b43_qos_init(struct b43_wldev *dev)
b43dbg(dev->wl, "QoS enabled\n");
}
-static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue,
+static int b43_op_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 _queue,
const struct ieee80211_tx_queue_params *params)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
@@ -3423,7 +3607,7 @@ static int b43_op_get_stats(struct ieee80211_hw *hw,
return 0;
}
-static u64 b43_op_get_tsf(struct ieee80211_hw *hw)
+static u64 b43_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
@@ -3442,7 +3626,8 @@ static u64 b43_op_get_tsf(struct ieee80211_hw *hw)
return tsf;
}
-static void b43_op_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+static void b43_op_set_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u64 tsf)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
@@ -3458,21 +3643,33 @@ static void b43_op_set_tsf(struct ieee80211_hw *hw, u64 tsf)
static void b43_put_phy_into_reset(struct b43_wldev *dev)
{
- struct ssb_device *sdev = dev->sdev;
- u32 tmslow;
+ u32 tmp;
- tmslow = ssb_read32(sdev, SSB_TMSLOW);
- tmslow &= ~B43_TMSLOW_GMODE;
- tmslow |= B43_TMSLOW_PHYRESET;
- tmslow |= SSB_TMSLOW_FGC;
- ssb_write32(sdev, SSB_TMSLOW, tmslow);
- msleep(1);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ b43err(dev->wl,
+ "Putting PHY into reset not supported on BCMA\n");
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+ tmp &= ~B43_TMSLOW_GMODE;
+ tmp |= B43_TMSLOW_PHYRESET;
+ tmp |= SSB_TMSLOW_FGC;
+ ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+ msleep(1);
+
+ tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+ tmp &= ~SSB_TMSLOW_FGC;
+ tmp |= B43_TMSLOW_PHYRESET;
+ ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+ msleep(1);
- tmslow = ssb_read32(sdev, SSB_TMSLOW);
- tmslow &= ~SSB_TMSLOW_FGC;
- tmslow |= B43_TMSLOW_PHYRESET;
- ssb_write32(sdev, SSB_TMSLOW, tmslow);
- msleep(1);
+ break;
+#endif
+ }
}
static const char *band_to_string(enum ieee80211_band band)
@@ -3604,14 +3801,24 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
struct ieee80211_conf *conf = &hw->conf;
int antenna;
int err = 0;
+ bool reload_bss = false;
mutex_lock(&wl->mutex);
+ dev = wl->current_dev;
+
/* Switch the band (if necessary). This might change the active core. */
err = b43_switch_band(wl, conf->channel);
if (err)
goto out_unlock_mutex;
- dev = wl->current_dev;
+
+ /* Need to reload all settings if the core changed */
+ if (dev != wl->current_dev) {
+ dev = wl->current_dev;
+ changed = ~0;
+ reload_bss = true;
+ }
+
phy = &dev->phy;
if (conf_is_ht(conf))
@@ -3672,6 +3879,9 @@ out_mac_enable:
out_unlock_mutex:
mutex_unlock(&wl->mutex);
+ if (wl->vif && reload_bss)
+ b43_op_bss_info_changed(hw, wl->vif, &wl->vif->bss_conf, ~0);
+
return err;
}
@@ -3760,7 +3970,8 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_BEACON_INT &&
(b43_is_mode(wl, NL80211_IFTYPE_AP) ||
b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) ||
- b43_is_mode(wl, NL80211_IFTYPE_ADHOC)))
+ b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) &&
+ conf->beacon_int)
b43_set_beacon_int(dev, conf->beacon_int);
if (changed & BSS_CHANGED_BASIC_RATES)
@@ -3933,10 +4144,13 @@ out_unlock:
* because the core might be gone away while we unlocked the mutex. */
static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev)
{
- struct b43_wl *wl = dev->wl;
+ struct b43_wl *wl;
struct b43_wldev *orig_dev;
u32 mask;
+ if (!dev)
+ return NULL;
+ wl = dev->wl;
redo:
if (!dev || b43_status(dev) < B43_STAT_STARTED)
return dev;
@@ -3954,7 +4168,7 @@ redo:
/* Disable interrupts on the device. */
b43_set_status(dev, B43_STAT_INITIALIZED);
- if (dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO) {
+ if (b43_bus_host_is_sdio(dev->dev)) {
/* wl->mutex is locked. That is enough. */
b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0);
b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */
@@ -3967,11 +4181,11 @@ redo:
/* Synchronize and free the interrupt handlers. Unlock to avoid deadlocks. */
orig_dev = dev;
mutex_unlock(&wl->mutex);
- if (dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO) {
+ if (b43_bus_host_is_sdio(dev->dev)) {
b43_sdio_free_irq(dev);
} else {
- synchronize_irq(dev->sdev->irq);
- free_irq(dev->sdev->irq, dev);
+ synchronize_irq(dev->dev->irq);
+ free_irq(dev->dev->irq, dev);
}
mutex_lock(&wl->mutex);
dev = wl->current_dev;
@@ -4004,19 +4218,19 @@ static int b43_wireless_core_start(struct b43_wldev *dev)
B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED);
drain_txstatus_queue(dev);
- if (dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO) {
+ if (b43_bus_host_is_sdio(dev->dev)) {
err = b43_sdio_request_irq(dev, b43_sdio_interrupt_handler);
if (err) {
b43err(dev->wl, "Cannot request SDIO IRQ\n");
goto out;
}
} else {
- err = request_threaded_irq(dev->sdev->irq, b43_interrupt_handler,
+ err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler,
b43_interrupt_thread_handler,
IRQF_SHARED, KBUILD_MODNAME, dev);
if (err) {
b43err(dev->wl, "Cannot request IRQ-%d\n",
- dev->sdev->irq);
+ dev->dev->irq);
goto out;
}
}
@@ -4083,9 +4297,21 @@ static int b43_phy_versioning(struct b43_wldev *dev)
unsupported = 1;
break;
#endif
+#ifdef CONFIG_B43_PHY_HT
+ case B43_PHYTYPE_HT:
+ if (phy_rev > 1)
+ unsupported = 1;
+ break;
+#endif
+#ifdef CONFIG_B43_PHY_LCN
+ case B43_PHYTYPE_LCN:
+ if (phy_rev > 1)
+ unsupported = 1;
+ break;
+#endif
default:
unsupported = 1;
- };
+ }
if (unsupported) {
b43err(dev->wl, "FOUND UNSUPPORTED PHY "
"(Analog %u, Type %u, Revision %u)\n",
@@ -4096,22 +4322,42 @@ static int b43_phy_versioning(struct b43_wldev *dev)
analog_type, phy_type, phy_rev);
/* Get RADIO versioning */
- if (dev->sdev->bus->chip_id == 0x4317) {
- if (dev->sdev->bus->chip_rev == 0)
- tmp = 0x3205017F;
- else if (dev->sdev->bus->chip_rev == 1)
- tmp = 0x4205017F;
- else
- tmp = 0x5205017F;
+ if (dev->dev->core_rev >= 24) {
+ u16 radio24[3];
+
+ for (tmp = 0; tmp < 3; tmp++) {
+ b43_write16(dev, B43_MMIO_RADIO24_CONTROL, tmp);
+ radio24[tmp] = b43_read16(dev, B43_MMIO_RADIO24_DATA);
+ }
+
+ /* Broadcom uses "id" for our "ver" and has separated "ver" */
+ /* radio_ver = (radio24[0] & 0xF0) >> 4; */
+
+ radio_manuf = 0x17F;
+ radio_ver = (radio24[2] << 8) | radio24[1];
+ radio_rev = (radio24[0] & 0xF);
} else {
- b43_write16(dev, B43_MMIO_RADIO_CONTROL, B43_RADIOCTL_ID);
- tmp = b43_read16(dev, B43_MMIO_RADIO_DATA_LOW);
- b43_write16(dev, B43_MMIO_RADIO_CONTROL, B43_RADIOCTL_ID);
- tmp |= (u32)b43_read16(dev, B43_MMIO_RADIO_DATA_HIGH) << 16;
- }
- radio_manuf = (tmp & 0x00000FFF);
- radio_ver = (tmp & 0x0FFFF000) >> 12;
- radio_rev = (tmp & 0xF0000000) >> 28;
+ if (dev->dev->chip_id == 0x4317) {
+ if (dev->dev->chip_rev == 0)
+ tmp = 0x3205017F;
+ else if (dev->dev->chip_rev == 1)
+ tmp = 0x4205017F;
+ else
+ tmp = 0x5205017F;
+ } else {
+ b43_write16(dev, B43_MMIO_RADIO_CONTROL,
+ B43_RADIOCTL_ID);
+ tmp = b43_read16(dev, B43_MMIO_RADIO_DATA_LOW);
+ b43_write16(dev, B43_MMIO_RADIO_CONTROL,
+ B43_RADIOCTL_ID);
+ tmp |= (u32)b43_read16(dev, B43_MMIO_RADIO_DATA_HIGH)
+ << 16;
+ }
+ radio_manuf = (tmp & 0x00000FFF);
+ radio_ver = (tmp & 0x0FFFF000) >> 12;
+ radio_rev = (tmp & 0xF0000000) >> 28;
+ }
+
if (radio_manuf != 0x17F /* Broadcom */)
unsupported = 1;
switch (phy_type) {
@@ -4139,6 +4385,14 @@ static int b43_phy_versioning(struct b43_wldev *dev)
if (radio_ver != 0x2062 && radio_ver != 0x2063)
unsupported = 1;
break;
+ case B43_PHYTYPE_HT:
+ if (radio_ver != 0x2059)
+ unsupported = 1;
+ break;
+ case B43_PHYTYPE_LCN:
+ if (radio_ver != 0x2064)
+ unsupported = 1;
+ break;
default:
B43_WARN_ON(1);
}
@@ -4204,7 +4458,7 @@ static void setup_struct_wldev_for_init(struct b43_wldev *dev)
static void b43_bluetooth_coext_enable(struct b43_wldev *dev)
{
- struct ssb_sprom *sprom = &dev->sdev->bus->sprom;
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
u64 hf;
if (!modparam_btcoex)
@@ -4231,16 +4485,21 @@ static void b43_bluetooth_coext_disable(struct b43_wldev *dev)
static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_bus *bus;
u32 tmp;
+ if (dev->dev->bus_type != B43_BUS_SSB)
+ return;
+
+ bus = dev->dev->sdev->bus;
+
if ((bus->chip_id == 0x4311 && bus->chip_rev == 2) ||
(bus->chip_id == 0x4312)) {
- tmp = ssb_read32(dev->sdev, SSB_IMCFGLO);
+ tmp = ssb_read32(dev->dev->sdev, SSB_IMCFGLO);
tmp &= ~SSB_IMCFGLO_REQTO;
tmp &= ~SSB_IMCFGLO_SERTO;
tmp |= 0x3;
- ssb_write32(dev->sdev, SSB_IMCFGLO, tmp);
+ ssb_write32(dev->dev->sdev, SSB_IMCFGLO, tmp);
ssb_commit_settings(bus);
}
}
@@ -4310,36 +4569,45 @@ static void b43_wireless_core_exit(struct b43_wldev *dev)
dev->wl->current_beacon = NULL;
}
- ssb_device_disable(dev->sdev, 0);
- ssb_bus_may_powerdown(dev->sdev->bus);
+ b43_device_disable(dev, 0);
+ b43_bus_may_powerdown(dev);
}
/* Initialize a wireless core */
static int b43_wireless_core_init(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
- struct ssb_sprom *sprom = &bus->sprom;
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
struct b43_phy *phy = &dev->phy;
int err;
u64 hf;
- u32 tmp;
B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT);
- err = ssb_bus_powerup(bus, 0);
+ err = b43_bus_powerup(dev, 0);
if (err)
goto out;
- if (!ssb_device_is_enabled(dev->sdev)) {
- tmp = phy->gmode ? B43_TMSLOW_GMODE : 0;
- b43_wireless_core_reset(dev, tmp);
- }
+ if (!b43_device_is_enabled(dev))
+ b43_wireless_core_reset(dev, phy->gmode);
/* Reset all data structures. */
setup_struct_wldev_for_init(dev);
phy->ops->prepare_structs(dev);
/* Enable IRQ routing to this device. */
- ssb_pcicore_dev_irqvecs_enable(&bus->pcicore, dev->sdev);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci,
+ dev->dev->bdev, true);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ ssb_pcicore_dev_irqvecs_enable(&dev->dev->sdev->bus->pcicore,
+ dev->dev->sdev);
+ break;
+#endif
+ }
b43_imcfglo_timeouts_workaround(dev);
b43_bluetooth_coext_disable(dev);
@@ -4352,7 +4620,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
if (err)
goto err_busdown;
b43_shm_write16(dev, B43_SHM_SHARED,
- B43_SHM_SH_WLCOREREV, dev->sdev->id.revision);
+ B43_SHM_SH_WLCOREREV, dev->dev->core_rev);
hf = b43_hf_read(dev);
if (phy->type == B43_PHYTYPE_G) {
hf |= B43_HF_SYMW;
@@ -4370,8 +4638,9 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)
hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */
#ifdef CONFIG_SSB_DRIVER_PCICORE
- if ((bus->bustype == SSB_BUSTYPE_PCI) &&
- (bus->pcicore.dev->id.revision <= 10))
+ if (dev->dev->bus_type == B43_BUS_SSB &&
+ dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
+ dev->dev->sdev->bus->pcicore.dev->id.revision <= 10)
hf |= B43_HF_PCISCW; /* PCI slow clock workaround. */
#endif
hf &= ~B43_HF_SKCFPUP;
@@ -4399,9 +4668,14 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
/* Maximum Contention Window */
b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF);
- if ((dev->sdev->bus->bustype == SSB_BUSTYPE_PCMCIA) ||
- (dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO) ||
- dev->use_pio) {
+ if (b43_bus_host_is_pcmcia(dev->dev) ||
+ b43_bus_host_is_sdio(dev->dev)) {
+ dev->__using_pio_transfers = 1;
+ err = b43_pio_init(dev);
+ } else if (dev->use_pio) {
+ b43warn(dev->wl, "Forced PIO by use_pio module parameter. "
+ "This should not be needed and will result in lower "
+ "performance.\n");
dev->__using_pio_transfers = 1;
err = b43_pio_init(dev);
} else {
@@ -4414,7 +4688,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
b43_set_synth_pu_delay(dev, 1);
b43_bluetooth_coext_enable(dev);
- ssb_bus_powerup(bus, !(sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW));
+ b43_bus_powerup(dev, !(sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW));
b43_upload_card_macaddress(dev);
b43_security_init(dev);
@@ -4431,7 +4705,7 @@ out:
err_chip_exit:
b43_chip_exit(dev);
err_busdown:
- ssb_bus_may_powerdown(bus);
+ b43_bus_may_powerdown(dev);
B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT);
return err;
}
@@ -4473,6 +4747,9 @@ static int b43_op_add_interface(struct ieee80211_hw *hw,
out_mutex_unlock:
mutex_unlock(&wl->mutex);
+ if (err == 0)
+ b43_op_bss_info_changed(hw, vif, &vif->bss_conf, ~0);
+
return err;
}
@@ -4543,6 +4820,9 @@ static int b43_op_start(struct ieee80211_hw *hw)
out_mutex_unlock:
mutex_unlock(&wl->mutex);
+ /* reload configuration */
+ b43_op_config(hw, ~0);
+
return err;
}
@@ -4699,10 +4979,18 @@ out:
if (err)
wl->current_dev = NULL; /* Failed to init the dev. */
mutex_unlock(&wl->mutex);
- if (err)
+
+ if (err) {
b43err(wl, "Controller restart FAILED\n");
- else
- b43info(wl, "Controller restarted\n");
+ return;
+ }
+
+ /* reload configuration */
+ b43_op_config(wl->hw, ~0);
+ if (wl->vif)
+ b43_op_bss_info_changed(wl->hw, wl->vif, &wl->vif->bss_conf, ~0);
+
+ b43info(wl, "Controller restarted\n");
}
static int b43_setup_bands(struct b43_wldev *dev,
@@ -4737,11 +5025,10 @@ static void b43_wireless_core_detach(struct b43_wldev *dev)
static int b43_wireless_core_attach(struct b43_wldev *dev)
{
struct b43_wl *wl = dev->wl;
- struct ssb_bus *bus = dev->sdev->bus;
- struct pci_dev *pdev = (bus->bustype == SSB_BUSTYPE_PCI) ? bus->host_pci : NULL;
+ struct pci_dev *pdev = NULL;
int err;
- bool have_2ghz_phy = 0, have_5ghz_phy = 0;
u32 tmp;
+ bool have_2ghz_phy = 0, have_5ghz_phy = 0;
/* Do NOT do any device initialization here.
* Do it in wireless_core_init() instead.
@@ -4750,25 +5037,42 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
* that in core_init(), too.
*/
- err = ssb_bus_powerup(bus, 0);
+#ifdef CONFIG_B43_SSB
+ if (dev->dev->bus_type == B43_BUS_SSB &&
+ dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI)
+ pdev = dev->dev->sdev->bus->host_pci;
+#endif
+
+ err = b43_bus_powerup(dev, 0);
if (err) {
b43err(wl, "Bus powerup failed\n");
goto out;
}
- /* Get the PHY type. */
- if (dev->sdev->id.revision >= 5) {
- u32 tmshigh;
- tmshigh = ssb_read32(dev->sdev, SSB_TMSHIGH);
- have_2ghz_phy = !!(tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY);
- have_5ghz_phy = !!(tmshigh & B43_TMSHIGH_HAVE_5GHZ_PHY);
- } else
- B43_WARN_ON(1);
+ /* Get the PHY type. */
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST);
+ have_2ghz_phy = !!(tmp & B43_BCMA_IOST_2G_PHY);
+ have_5ghz_phy = !!(tmp & B43_BCMA_IOST_5G_PHY);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ if (dev->dev->core_rev >= 5) {
+ tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
+ have_2ghz_phy = !!(tmp & B43_TMSHIGH_HAVE_2GHZ_PHY);
+ have_5ghz_phy = !!(tmp & B43_TMSHIGH_HAVE_5GHZ_PHY);
+ } else
+ B43_WARN_ON(1);
+ break;
+#endif
+ }
dev->phy.gmode = have_2ghz_phy;
dev->phy.radio_on = 1;
- tmp = dev->phy.gmode ? B43_TMSLOW_GMODE : 0;
- b43_wireless_core_reset(dev, tmp);
+ b43_wireless_core_reset(dev, dev->phy.gmode);
err = b43_phy_versioning(dev);
if (err)
@@ -4790,6 +5094,8 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
#endif
case B43_PHYTYPE_G:
case B43_PHYTYPE_N:
+ case B43_PHYTYPE_HT:
+ case B43_PHYTYPE_LCN:
have_2ghz_phy = 1;
break;
default:
@@ -4816,8 +5122,7 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
goto err_powerdown;
dev->phy.gmode = have_2ghz_phy;
- tmp = dev->phy.gmode ? B43_TMSLOW_GMODE : 0;
- b43_wireless_core_reset(dev, tmp);
+ b43_wireless_core_reset(dev, dev->phy.gmode);
err = b43_validate_chipaccess(dev);
if (err)
@@ -4832,8 +5137,8 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
INIT_WORK(&dev->restart_work, b43_chip_reset);
dev->phy.ops->switch_analog(dev, 0);
- ssb_device_disable(dev->sdev, 0);
- ssb_bus_may_powerdown(bus);
+ b43_device_disable(dev, 0);
+ b43_bus_may_powerdown(dev);
out:
return err;
@@ -4841,11 +5146,11 @@ out:
err_phy_free:
b43_phy_free(dev);
err_powerdown:
- ssb_bus_may_powerdown(bus);
+ b43_bus_may_powerdown(dev);
return err;
}
-static void b43_one_core_detach(struct ssb_device *dev)
+static void b43_one_core_detach(struct b43_bus_dev *dev)
{
struct b43_wldev *wldev;
struct b43_wl *wl;
@@ -4853,17 +5158,17 @@ static void b43_one_core_detach(struct ssb_device *dev)
/* Do not cancel ieee80211-workqueue based work here.
* See comment in b43_remove(). */
- wldev = ssb_get_drvdata(dev);
+ wldev = b43_bus_get_wldev(dev);
wl = wldev->wl;
b43_debugfs_remove_device(wldev);
b43_wireless_core_detach(wldev);
list_del(&wldev->list);
wl->nr_devs--;
- ssb_set_drvdata(dev, NULL);
+ b43_bus_set_wldev(dev, NULL);
kfree(wldev);
}
-static int b43_one_core_attach(struct ssb_device *dev, struct b43_wl *wl)
+static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl)
{
struct b43_wldev *wldev;
int err = -ENOMEM;
@@ -4873,7 +5178,7 @@ static int b43_one_core_attach(struct ssb_device *dev, struct b43_wl *wl)
goto out;
wldev->use_pio = b43_modparam_pio;
- wldev->sdev = dev;
+ wldev->dev = dev;
wldev->wl = wl;
b43_set_status(wldev, B43_STAT_UNINIT);
wldev->bad_frames_preempt = modparam_bad_frames_preempt;
@@ -4885,7 +5190,7 @@ static int b43_one_core_attach(struct ssb_device *dev, struct b43_wl *wl)
list_add(&wldev->list, &wl->devlist);
wl->nr_devs++;
- ssb_set_drvdata(dev, wldev);
+ b43_bus_set_wldev(dev, wldev);
b43_debugfs_add_device(wldev);
out:
@@ -4926,19 +5231,20 @@ static void b43_sprom_fixup(struct ssb_bus *bus)
}
}
-static void b43_wireless_exit(struct ssb_device *dev, struct b43_wl *wl)
+static void b43_wireless_exit(struct b43_bus_dev *dev, struct b43_wl *wl)
{
struct ieee80211_hw *hw = wl->hw;
- ssb_set_devtypedata(dev, NULL);
+ ssb_set_devtypedata(dev->sdev, NULL);
ieee80211_free_hw(hw);
}
-static struct b43_wl *b43_wireless_init(struct ssb_device *dev)
+static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev)
{
- struct ssb_sprom *sprom = &dev->bus->sprom;
+ struct ssb_sprom *sprom = dev->bus_sprom;
struct ieee80211_hw *hw;
struct b43_wl *wl;
+ char chip_name[6];
hw = ieee80211_alloc_hw(sizeof(*wl), &b43_hw_ops);
if (!hw) {
@@ -4977,29 +5283,105 @@ static struct b43_wl *b43_wireless_init(struct ssb_device *dev)
INIT_WORK(&wl->tx_work, b43_tx_work);
skb_queue_head_init(&wl->tx_queue);
- b43info(wl, "Broadcom %04X WLAN found (core revision %u)\n",
- dev->bus->chip_id, dev->id.revision);
+ snprintf(chip_name, ARRAY_SIZE(chip_name),
+ (dev->chip_id > 0x9999) ? "%d" : "%04X", dev->chip_id);
+ b43info(wl, "Broadcom %s WLAN found (core revision %u)\n", chip_name,
+ dev->core_rev);
return wl;
}
-static int b43_ssb_probe(struct ssb_device *dev, const struct ssb_device_id *id)
+#ifdef CONFIG_B43_BCMA
+static int b43_bcma_probe(struct bcma_device *core)
+{
+ struct b43_bus_dev *dev;
+ struct b43_wl *wl;
+ int err;
+
+ dev = b43_bus_dev_bcma_init(core);
+ if (!dev)
+ return -ENODEV;
+
+ wl = b43_wireless_init(dev);
+ if (IS_ERR(wl)) {
+ err = PTR_ERR(wl);
+ goto bcma_out;
+ }
+
+ err = b43_one_core_attach(dev, wl);
+ if (err)
+ goto bcma_err_wireless_exit;
+
+ err = ieee80211_register_hw(wl->hw);
+ if (err)
+ goto bcma_err_one_core_detach;
+ b43_leds_register(wl->current_dev);
+
+bcma_out:
+ return err;
+
+bcma_err_one_core_detach:
+ b43_one_core_detach(dev);
+bcma_err_wireless_exit:
+ ieee80211_free_hw(wl->hw);
+ return err;
+}
+
+static void b43_bcma_remove(struct bcma_device *core)
+{
+ struct b43_wldev *wldev = bcma_get_drvdata(core);
+ struct b43_wl *wl = wldev->wl;
+
+ /* We must cancel any work here before unregistering from ieee80211,
+ * as the ieee80211 unreg will destroy the workqueue. */
+ cancel_work_sync(&wldev->restart_work);
+
+ /* Restore the queues count before unregistering, because firmware detect
+ * might have modified it. Restoring is important, so the networking
+ * stack can properly free resources. */
+ wl->hw->queues = wl->mac80211_initially_registered_queues;
+ b43_leds_stop(wldev);
+ ieee80211_unregister_hw(wl->hw);
+
+ b43_one_core_detach(wldev->dev);
+
+ b43_leds_unregister(wl);
+
+ ieee80211_free_hw(wl->hw);
+}
+
+static struct bcma_driver b43_bcma_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = b43_bcma_tbl,
+ .probe = b43_bcma_probe,
+ .remove = b43_bcma_remove,
+};
+#endif
+
+#ifdef CONFIG_B43_SSB
+static
+int b43_ssb_probe(struct ssb_device *sdev, const struct ssb_device_id *id)
{
+ struct b43_bus_dev *dev;
struct b43_wl *wl;
int err;
int first = 0;
- wl = ssb_get_devtypedata(dev);
+ dev = b43_bus_dev_ssb_init(sdev);
+ if (!dev)
+ return -ENOMEM;
+
+ wl = ssb_get_devtypedata(sdev);
if (!wl) {
/* Probing the first core. Must setup common struct b43_wl */
first = 1;
- b43_sprom_fixup(dev->bus);
+ b43_sprom_fixup(sdev->bus);
wl = b43_wireless_init(dev);
if (IS_ERR(wl)) {
err = PTR_ERR(wl);
goto out;
}
- ssb_set_devtypedata(dev, wl);
- B43_WARN_ON(ssb_get_devtypedata(dev) != wl);
+ ssb_set_devtypedata(sdev, wl);
+ B43_WARN_ON(ssb_get_devtypedata(sdev) != wl);
}
err = b43_one_core_attach(dev, wl);
if (err)
@@ -5023,10 +5405,11 @@ static int b43_ssb_probe(struct ssb_device *dev, const struct ssb_device_id *id)
return err;
}
-static void b43_ssb_remove(struct ssb_device *dev)
+static void b43_ssb_remove(struct ssb_device *sdev)
{
- struct b43_wl *wl = ssb_get_devtypedata(dev);
- struct b43_wldev *wldev = ssb_get_drvdata(dev);
+ struct b43_wl *wl = ssb_get_devtypedata(sdev);
+ struct b43_wldev *wldev = ssb_get_drvdata(sdev);
+ struct b43_bus_dev *dev = wldev->dev;
/* We must cancel any work here before unregistering from ieee80211,
* as the ieee80211 unreg will destroy the workqueue. */
@@ -5053,6 +5436,14 @@ static void b43_ssb_remove(struct ssb_device *dev)
}
}
+static struct ssb_driver b43_ssb_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = b43_ssb_tbl,
+ .probe = b43_ssb_probe,
+ .remove = b43_ssb_remove,
+};
+#endif /* CONFIG_B43_SSB */
+
/* Perform a hardware reset. This can be called from any context. */
void b43_controller_restart(struct b43_wldev *dev, const char *reason)
{
@@ -5063,13 +5454,6 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason)
ieee80211_queue_work(dev->wl->hw, &dev->restart_work);
}
-static struct ssb_driver b43_ssb_driver = {
- .name = KBUILD_MODNAME,
- .id_table = b43_ssb_tbl,
- .probe = b43_ssb_probe,
- .remove = b43_ssb_remove,
-};
-
static void b43_print_driverinfo(void)
{
const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "",
@@ -5091,8 +5475,7 @@ static void b43_print_driverinfo(void)
feat_sdio = "S";
#endif
printk(KERN_INFO "Broadcom 43xx driver loaded "
- "[ Features: %s%s%s%s%s, Firmware-ID: "
- B43_SUPPORTED_FIRMWARE_ID " ]\n",
+ "[ Features: %s%s%s%s%s ]\n",
feat_pci, feat_pcmcia, feat_nphy,
feat_leds, feat_sdio);
}
@@ -5108,14 +5491,27 @@ static int __init b43_init(void)
err = b43_sdio_init();
if (err)
goto err_pcmcia_exit;
- err = ssb_driver_register(&b43_ssb_driver);
+#ifdef CONFIG_B43_BCMA
+ err = bcma_driver_register(&b43_bcma_driver);
if (err)
goto err_sdio_exit;
+#endif
+#ifdef CONFIG_B43_SSB
+ err = ssb_driver_register(&b43_ssb_driver);
+ if (err)
+ goto err_bcma_driver_exit;
+#endif
b43_print_driverinfo();
return err;
+#ifdef CONFIG_B43_SSB
+err_bcma_driver_exit:
+#endif
+#ifdef CONFIG_B43_BCMA
+ bcma_driver_unregister(&b43_bcma_driver);
err_sdio_exit:
+#endif
b43_sdio_exit();
err_pcmcia_exit:
b43_pcmcia_exit();
@@ -5126,7 +5522,12 @@ err_dfs_exit:
static void __exit b43_exit(void)
{
+#ifdef CONFIG_B43_SSB
ssb_driver_unregister(&b43_ssb_driver);
+#endif
+#ifdef CONFIG_B43_BCMA
+ bcma_driver_unregister(&b43_bcma_driver);
+#endif
b43_sdio_exit();
b43_pcmcia_exit();
b43_debugfs_exit();
diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h
index a0d327f13183..8c684cd33529 100644
--- a/drivers/net/wireless/b43/main.h
+++ b/drivers/net/wireless/b43/main.h
@@ -4,7 +4,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <stefano.brivio@polimi.it>
- Michael Buesch <mb@bu3sch.de>
+ Michael Buesch <m@bues.ch>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
@@ -121,7 +121,7 @@ void b43_hf_write(struct b43_wldev *dev, u64 value);
void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on);
-void b43_wireless_core_reset(struct b43_wldev *dev, u32 flags);
+void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode);
void b43_controller_restart(struct b43_wldev *dev, const char *reason);
diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c
index 2c8461dcf1b0..714cad649c45 100644
--- a/drivers/net/wireless/b43/pcmcia.c
+++ b/drivers/net/wireless/b43/pcmcia.c
@@ -2,7 +2,7 @@
Broadcom B43 wireless driver
- Copyright (c) 2007 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2007 Michael Buesch <m@bues.ch>
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
@@ -25,6 +25,7 @@
#include <linux/ssb/ssb.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/b43/phy_a.c
index b01c8ced57c3..a6c38104693d 100644
--- a/drivers/net/wireless/b43/phy_a.c
+++ b/drivers/net/wireless/b43/phy_a.c
@@ -5,7 +5,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Copyright (c) 2005-2007 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (c) 2005-2008 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005-2008 Michael Buesch <m@bues.ch>
Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch>
@@ -265,7 +265,6 @@ static void hardware_pctl_init_aphy(struct b43_wldev *dev)
void b43_phy_inita(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
struct b43_phy *phy = &dev->phy;
/* This lowlevel A-PHY init is also called from G-PHY init.
@@ -296,9 +295,9 @@ void b43_phy_inita(struct b43_wldev *dev)
b43_radio_init2060(dev);
- if ((bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM) &&
- ((bus->boardinfo.type == SSB_BOARD_BU4306) ||
- (bus->boardinfo.type == SSB_BOARD_BU4309))) {
+ if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) &&
+ ((dev->dev->board_type == SSB_BOARD_BU4306) ||
+ (dev->dev->board_type == SSB_BOARD_BU4309))) {
; //TODO: A PHY LO
}
@@ -311,7 +310,7 @@ void b43_phy_inita(struct b43_wldev *dev)
}
if ((phy->type == B43_PHYTYPE_G) &&
- (dev->sdev->bus->sprom.boardflags_lo & B43_BFL_PACTRL)) {
+ (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL)) {
b43_phy_maskset(dev, B43_PHY_OFDM(0x6E), 0xE000, 0x3CF);
}
}
@@ -323,17 +322,17 @@ static int b43_aphy_init_tssi2dbm_table(struct b43_wldev *dev)
struct b43_phy_a *aphy = phy->a;
s16 pab0, pab1, pab2;
- pab0 = (s16) (dev->sdev->bus->sprom.pa1b0);
- pab1 = (s16) (dev->sdev->bus->sprom.pa1b1);
- pab2 = (s16) (dev->sdev->bus->sprom.pa1b2);
+ pab0 = (s16) (dev->dev->bus_sprom->pa1b0);
+ pab1 = (s16) (dev->dev->bus_sprom->pa1b1);
+ pab2 = (s16) (dev->dev->bus_sprom->pa1b2);
if (pab0 != 0 && pab1 != 0 && pab2 != 0 &&
pab0 != -1 && pab1 != -1 && pab2 != -1) {
/* The pabX values are set in SPROM. Use them. */
- if ((s8) dev->sdev->bus->sprom.itssi_a != 0 &&
- (s8) dev->sdev->bus->sprom.itssi_a != -1)
+ if ((s8) dev->dev->bus_sprom->itssi_a != 0 &&
+ (s8) dev->dev->bus_sprom->itssi_a != -1)
aphy->tgt_idle_tssi =
- (s8) (dev->sdev->bus->sprom.itssi_a);
+ (s8) (dev->dev->bus_sprom->itssi_a);
else
aphy->tgt_idle_tssi = 62;
aphy->tssi2dbm = b43_generate_dyn_tssi2dbm_tab(dev, pab0,
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c
index e46b2f4f0920..3ea44bb03684 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/b43/phy_common.c
@@ -5,7 +5,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Copyright (c) 2005-2007 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (c) 2005-2008 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005-2008 Michael Buesch <m@bues.ch>
Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch>
@@ -31,6 +31,8 @@
#include "phy_a.h"
#include "phy_n.h"
#include "phy_lp.h"
+#include "phy_ht.h"
+#include "phy_lcn.h"
#include "b43.h"
#include "main.h"
@@ -59,6 +61,16 @@ int b43_phy_allocate(struct b43_wldev *dev)
phy->ops = &b43_phyops_lp;
#endif
break;
+ case B43_PHYTYPE_HT:
+#ifdef CONFIG_B43_PHY_HT
+ phy->ops = &b43_phyops_ht;
+#endif
+ break;
+ case B43_PHYTYPE_LCN:
+#ifdef CONFIG_B43_PHY_LCN
+ phy->ops = &b43_phyops_lcn;
+#endif
+ break;
}
if (B43_WARN_ON(!phy->ops))
return -ENODEV;
@@ -168,7 +180,7 @@ void b43_phy_lock(struct b43_wldev *dev)
B43_WARN_ON(dev->phy.phy_locked);
dev->phy.phy_locked = 1;
#endif
- B43_WARN_ON(dev->sdev->id.revision < 3);
+ B43_WARN_ON(dev->dev->core_rev < 3);
if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP))
b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
@@ -180,7 +192,7 @@ void b43_phy_unlock(struct b43_wldev *dev)
B43_WARN_ON(!dev->phy.phy_locked);
dev->phy.phy_locked = 0;
#endif
- B43_WARN_ON(dev->sdev->id.revision < 3);
+ B43_WARN_ON(dev->dev->core_rev < 3);
if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP))
b43_power_saving_ctl_bits(dev, 0);
@@ -368,8 +380,8 @@ void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags)
/* The next check will be needed in two seconds, or later. */
phy->next_txpwr_check_time = round_jiffies(now + (HZ * 2));
- if ((dev->sdev->bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM) &&
- (dev->sdev->bus->boardinfo.type == SSB_BOARD_BU4306))
+ if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) &&
+ (dev->dev->board_type == SSB_BOARD_BU4306))
return; /* No software txpower adjustment needed */
result = phy->ops->recalc_txpower(dev, !!(flags & B43_TXPWR_IGNORE_TSSI));
@@ -436,6 +448,38 @@ bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type)
channel_type == NL80211_CHAN_HT40PLUS);
}
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */
+void b43_phy_force_clock(struct b43_wldev *dev, bool force)
+{
+ u32 tmp;
+
+ WARN_ON(dev->phy.type != B43_PHYTYPE_N &&
+ dev->phy.type != B43_PHYTYPE_HT);
+
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+ if (force)
+ tmp |= BCMA_IOCTL_FGC;
+ else
+ tmp &= ~BCMA_IOCTL_FGC;
+ bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+ if (force)
+ tmp |= SSB_TMSLOW_FGC;
+ else
+ tmp &= ~SSB_TMSLOW_FGC;
+ ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+ break;
+#endif
+ }
+}
+
/* http://bcm-v4.sipsolutions.net/802.11/PHY/Cordic */
struct b43_c32 b43_cordic(int theta)
{
diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h
index 2401bee8b081..9233b13fc16d 100644
--- a/drivers/net/wireless/b43/phy_common.h
+++ b/drivers/net/wireless/b43/phy_common.h
@@ -194,6 +194,8 @@ struct b43_phy_a;
struct b43_phy_g;
struct b43_phy_n;
struct b43_phy_lp;
+struct b43_phy_ht;
+struct b43_phy_lcn;
struct b43_phy {
/* Hardware operation callbacks. */
@@ -216,6 +218,10 @@ struct b43_phy {
struct b43_phy_n *n;
/* LP-PHY specific information */
struct b43_phy_lp *lp;
+ /* HT-PHY specific information */
+ struct b43_phy_ht *ht;
+ /* LCN-PHY specific information */
+ struct b43_phy_lcn *lcn;
};
/* Band support flags. */
@@ -438,6 +444,8 @@ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on);
bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type);
+void b43_phy_force_clock(struct b43_wldev *dev, bool force);
+
struct b43_c32 b43_cordic(int theta);
#endif /* LINUX_B43_PHY_COMMON_H_ */
diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c
index 1758a282f913..8e157bc213f3 100644
--- a/drivers/net/wireless/b43/phy_g.c
+++ b/drivers/net/wireless/b43/phy_g.c
@@ -5,7 +5,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Copyright (c) 2005-2007 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (c) 2005-2008 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005-2008 Michael Buesch <m@bues.ch>
Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch>
@@ -718,7 +718,7 @@ static void b43_calc_nrssi_threshold(struct b43_wldev *dev)
B43_WARN_ON(phy->type != B43_PHYTYPE_G);
if (!phy->gmode ||
- !(dev->sdev->bus->sprom.boardflags_lo & B43_BFL_RSSI)) {
+ !(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) {
tmp16 = b43_nrssi_hw_read(dev, 0x20);
if (tmp16 >= 0x20)
tmp16 -= 0x40;
@@ -1114,7 +1114,7 @@ static u16 radio2050_rfover_val(struct b43_wldev *dev,
{
struct b43_phy *phy = &dev->phy;
struct b43_phy_g *gphy = phy->g;
- struct ssb_sprom *sprom = &(dev->sdev->bus->sprom);
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
if (!phy->gmode)
return 0;
@@ -1491,7 +1491,6 @@ static u16 b43_radio_init2050(struct b43_wldev *dev)
static void b43_phy_initb5(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
struct b43_phy *phy = &dev->phy;
struct b43_phy_g *gphy = phy->g;
u16 offset, value;
@@ -1500,8 +1499,8 @@ static void b43_phy_initb5(struct b43_wldev *dev)
if (phy->analog == 1) {
b43_radio_set(dev, 0x007A, 0x0050);
}
- if ((bus->boardinfo.vendor != SSB_BOARDVENDOR_BCM) &&
- (bus->boardinfo.type != SSB_BOARD_BU4306)) {
+ if ((dev->dev->board_vendor != SSB_BOARDVENDOR_BCM) &&
+ (dev->dev->board_type != SSB_BOARD_BU4306)) {
value = 0x2120;
for (offset = 0x00A8; offset < 0x00C7; offset++) {
b43_phy_write(dev, offset, value);
@@ -1620,7 +1619,7 @@ static void b43_phy_initb6(struct b43_wldev *dev)
b43_radio_write16(dev, 0x5A, 0x88);
b43_radio_write16(dev, 0x5B, 0x6B);
b43_radio_write16(dev, 0x5C, 0x0F);
- if (dev->sdev->bus->sprom.boardflags_lo & B43_BFL_ALTIQ) {
+ if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_ALTIQ) {
b43_radio_write16(dev, 0x5D, 0xFA);
b43_radio_write16(dev, 0x5E, 0xD8);
} else {
@@ -1787,7 +1786,7 @@ static void b43_calc_loopback_gain(struct b43_wldev *dev)
b43_phy_set(dev, B43_PHY_RFOVER, 0x0100);
b43_phy_mask(dev, B43_PHY_RFOVERVAL, 0xCFFF);
- if (dev->sdev->bus->sprom.boardflags_lo & B43_BFL_EXTLNA) {
+ if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_EXTLNA) {
if (phy->rev >= 7) {
b43_phy_set(dev, B43_PHY_RFOVER, 0x0800);
b43_phy_set(dev, B43_PHY_RFOVERVAL, 0x8000);
@@ -1922,7 +1921,6 @@ static void b43_hardware_pctl_init_gphy(struct b43_wldev *dev)
/* Initialize B/G PHY power control */
static void b43_phy_init_pctl(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
struct b43_phy *phy = &dev->phy;
struct b43_phy_g *gphy = phy->g;
struct b43_rfatt old_rfatt;
@@ -1931,8 +1929,8 @@ static void b43_phy_init_pctl(struct b43_wldev *dev)
B43_WARN_ON(phy->type != B43_PHYTYPE_G);
- if ((bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM) &&
- (bus->boardinfo.type == SSB_BOARD_BU4306))
+ if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) &&
+ (dev->dev->board_type == SSB_BOARD_BU4306))
return;
b43_phy_write(dev, 0x0028, 0x8018);
@@ -2053,7 +2051,7 @@ static void b43_phy_initg(struct b43_wldev *dev)
if (phy->rev >= 6) {
b43_phy_maskset(dev, B43_PHY_CCK(0x36), 0x0FFF, (gphy->lo_control->tx_bias << 12));
}
- if (dev->sdev->bus->sprom.boardflags_lo & B43_BFL_PACTRL)
+ if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL)
b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8075);
else
b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x807F);
@@ -2066,7 +2064,7 @@ static void b43_phy_initg(struct b43_wldev *dev)
b43_phy_write(dev, B43_PHY_LO_MASK, 0x8078);
}
- if (!(dev->sdev->bus->sprom.boardflags_lo & B43_BFL_RSSI)) {
+ if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) {
/* The specs state to update the NRSSI LT with
* the value 0x7FFFFFFF here. I think that is some weird
* compiler optimization in the original driver.
@@ -2088,8 +2086,8 @@ static void b43_phy_initg(struct b43_wldev *dev)
/* FIXME: The spec says in the following if, the 0 should be replaced
'if OFDM may not be used in the current locale'
but OFDM is legal everywhere */
- if ((dev->sdev->bus->chip_id == 0x4306
- && dev->sdev->bus->chip_package == 2) || 0) {
+ if ((dev->dev->chip_id == 0x4306
+ && dev->dev->chip_pkg == 2) || 0) {
b43_phy_mask(dev, B43_PHY_CRS0, 0xBFFF);
b43_phy_mask(dev, B43_PHY_OFDM(0xC3), 0x7FFF);
}
@@ -2105,7 +2103,7 @@ void b43_gphy_channel_switch(struct b43_wldev *dev,
b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(channel));
if (channel == 14) {
- if (dev->sdev->bus->sprom.country_code ==
+ if (dev->dev->bus_sprom->country_code ==
SSB_SPROM1CCODE_JAPAN)
b43_hf_write(dev,
b43_hf_read(dev) & ~B43_HF_ACPR);
@@ -2136,17 +2134,17 @@ static void default_baseband_attenuation(struct b43_wldev *dev,
static void default_radio_attenuation(struct b43_wldev *dev,
struct b43_rfatt *rf)
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct b43_bus_dev *bdev = dev->dev;
struct b43_phy *phy = &dev->phy;
rf->with_padmix = 0;
- if (bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM &&
- bus->boardinfo.type == SSB_BOARD_BCM4309G) {
- if (bus->boardinfo.rev < 0x43) {
+ if (dev->dev->board_vendor == SSB_BOARDVENDOR_BCM &&
+ dev->dev->board_type == SSB_BOARD_BCM4309G) {
+ if (dev->dev->board_rev < 0x43) {
rf->att = 2;
return;
- } else if (bus->boardinfo.rev < 0x51) {
+ } else if (dev->dev->board_rev < 0x51) {
rf->att = 3;
return;
}
@@ -2172,21 +2170,21 @@ static void default_radio_attenuation(struct b43_wldev *dev,
return;
case 1:
if (phy->type == B43_PHYTYPE_G) {
- if (bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM
- && bus->boardinfo.type == SSB_BOARD_BCM4309G
- && bus->boardinfo.rev >= 30)
+ if (bdev->board_vendor == SSB_BOARDVENDOR_BCM
+ && bdev->board_type == SSB_BOARD_BCM4309G
+ && bdev->board_rev >= 30)
rf->att = 3;
- else if (bus->boardinfo.vendor ==
+ else if (bdev->board_vendor ==
SSB_BOARDVENDOR_BCM
- && bus->boardinfo.type ==
+ && bdev->board_type ==
SSB_BOARD_BU4306)
rf->att = 3;
else
rf->att = 1;
} else {
- if (bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM
- && bus->boardinfo.type == SSB_BOARD_BCM4309G
- && bus->boardinfo.rev >= 30)
+ if (bdev->board_vendor == SSB_BOARDVENDOR_BCM
+ && bdev->board_type == SSB_BOARD_BCM4309G
+ && bdev->board_rev >= 30)
rf->att = 7;
else
rf->att = 6;
@@ -2194,16 +2192,16 @@ static void default_radio_attenuation(struct b43_wldev *dev,
return;
case 2:
if (phy->type == B43_PHYTYPE_G) {
- if (bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM
- && bus->boardinfo.type == SSB_BOARD_BCM4309G
- && bus->boardinfo.rev >= 30)
+ if (bdev->board_vendor == SSB_BOARDVENDOR_BCM
+ && bdev->board_type == SSB_BOARD_BCM4309G
+ && bdev->board_rev >= 30)
rf->att = 3;
- else if (bus->boardinfo.vendor ==
+ else if (bdev->board_vendor ==
SSB_BOARDVENDOR_BCM
- && bus->boardinfo.type ==
+ && bdev->board_type ==
SSB_BOARD_BU4306)
rf->att = 5;
- else if (bus->chip_id == 0x4320)
+ else if (bdev->chip_id == 0x4320)
rf->att = 4;
else
rf->att = 3;
@@ -2384,11 +2382,11 @@ static int b43_gphy_init_tssi2dbm_table(struct b43_wldev *dev)
struct b43_phy_g *gphy = phy->g;
s16 pab0, pab1, pab2;
- pab0 = (s16) (dev->sdev->bus->sprom.pa0b0);
- pab1 = (s16) (dev->sdev->bus->sprom.pa0b1);
- pab2 = (s16) (dev->sdev->bus->sprom.pa0b2);
+ pab0 = (s16) (dev->dev->bus_sprom->pa0b0);
+ pab1 = (s16) (dev->dev->bus_sprom->pa0b1);
+ pab2 = (s16) (dev->dev->bus_sprom->pa0b2);
- B43_WARN_ON((dev->sdev->bus->chip_id == 0x4301) &&
+ B43_WARN_ON((dev->dev->chip_id == 0x4301) &&
(phy->radio_ver != 0x2050)); /* Not supported anymore */
gphy->dyn_tssi_tbl = 0;
@@ -2396,10 +2394,10 @@ static int b43_gphy_init_tssi2dbm_table(struct b43_wldev *dev)
if (pab0 != 0 && pab1 != 0 && pab2 != 0 &&
pab0 != -1 && pab1 != -1 && pab2 != -1) {
/* The pabX values are set in SPROM. Use them. */
- if ((s8) dev->sdev->bus->sprom.itssi_bg != 0 &&
- (s8) dev->sdev->bus->sprom.itssi_bg != -1) {
+ if ((s8) dev->dev->bus_sprom->itssi_bg != 0 &&
+ (s8) dev->dev->bus_sprom->itssi_bg != -1) {
gphy->tgt_idle_tssi =
- (s8) (dev->sdev->bus->sprom.itssi_bg);
+ (s8) (dev->dev->bus_sprom->itssi_bg);
} else
gphy->tgt_idle_tssi = 62;
gphy->tssi2dbm = b43_generate_dyn_tssi2dbm_tab(dev, pab0,
@@ -2537,7 +2535,7 @@ static int b43_gphy_op_prepare_hardware(struct b43_wldev *dev)
b43_wireless_core_reset(dev, 0);
b43_phy_initg(dev);
phy->gmode = 1;
- b43_wireless_core_reset(dev, B43_TMSLOW_GMODE);
+ b43_wireless_core_reset(dev, 1);
}
return 0;
@@ -2840,7 +2838,7 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev)
B43_TXCTL_TXMIX;
rfatt += 2;
bbatt += 2;
- } else if (dev->sdev->bus->sprom.
+ } else if (dev->dev->bus_sprom->
boardflags_lo &
B43_BFL_PACTRL) {
bbatt += 4 * (rfatt - 2);
@@ -2914,14 +2912,14 @@ static enum b43_txpwr_result b43_gphy_op_recalc_txpower(struct b43_wldev *dev,
estimated_pwr = b43_gphy_estimate_power_out(dev, average_tssi);
B43_WARN_ON(phy->type != B43_PHYTYPE_G);
- max_pwr = dev->sdev->bus->sprom.maxpwr_bg;
- if (dev->sdev->bus->sprom.boardflags_lo & B43_BFL_PACTRL)
+ max_pwr = dev->dev->bus_sprom->maxpwr_bg;
+ if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL)
max_pwr -= 3; /* minus 0.75 */
if (unlikely(max_pwr >= INT_TO_Q52(30/*dBm*/))) {
b43warn(dev->wl,
"Invalid max-TX-power value in SPROM.\n");
max_pwr = INT_TO_Q52(20); /* fake it */
- dev->sdev->bus->sprom.maxpwr_bg = max_pwr;
+ dev->dev->bus_sprom->maxpwr_bg = max_pwr;
}
/* Get desired power (in Q5.2) */
@@ -3014,7 +3012,7 @@ static void b43_gphy_op_pwork_60sec(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
- if (!(dev->sdev->bus->sprom.boardflags_lo & B43_BFL_RSSI))
+ if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI))
return;
b43_mac_suspend(dev);
diff --git a/drivers/net/wireless/b43/phy_ht.c b/drivers/net/wireless/b43/phy_ht.c
new file mode 100644
index 000000000000..7416c5e9154d
--- /dev/null
+++ b/drivers/net/wireless/b43/phy_ht.c
@@ -0,0 +1,618 @@
+/*
+
+ Broadcom B43 wireless driver
+ IEEE 802.11n HT-PHY support
+
+ Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include <linux/slab.h>
+
+#include "b43.h"
+#include "phy_ht.h"
+#include "tables_phy_ht.h"
+#include "radio_2059.h"
+#include "main.h"
+
+/**************************************************
+ * Radio 2059.
+ **************************************************/
+
+static void b43_radio_2059_channel_setup(struct b43_wldev *dev,
+ const struct b43_phy_ht_channeltab_e_radio2059 *e)
+{
+ u8 i;
+ u16 routing;
+
+ b43_radio_write(dev, 0x16, e->radio_syn16);
+ b43_radio_write(dev, 0x17, e->radio_syn17);
+ b43_radio_write(dev, 0x22, e->radio_syn22);
+ b43_radio_write(dev, 0x25, e->radio_syn25);
+ b43_radio_write(dev, 0x27, e->radio_syn27);
+ b43_radio_write(dev, 0x28, e->radio_syn28);
+ b43_radio_write(dev, 0x29, e->radio_syn29);
+ b43_radio_write(dev, 0x2c, e->radio_syn2c);
+ b43_radio_write(dev, 0x2d, e->radio_syn2d);
+ b43_radio_write(dev, 0x37, e->radio_syn37);
+ b43_radio_write(dev, 0x41, e->radio_syn41);
+ b43_radio_write(dev, 0x43, e->radio_syn43);
+ b43_radio_write(dev, 0x47, e->radio_syn47);
+ b43_radio_write(dev, 0x4a, e->radio_syn4a);
+ b43_radio_write(dev, 0x58, e->radio_syn58);
+ b43_radio_write(dev, 0x5a, e->radio_syn5a);
+ b43_radio_write(dev, 0x6a, e->radio_syn6a);
+ b43_radio_write(dev, 0x6d, e->radio_syn6d);
+ b43_radio_write(dev, 0x6e, e->radio_syn6e);
+ b43_radio_write(dev, 0x92, e->radio_syn92);
+ b43_radio_write(dev, 0x98, e->radio_syn98);
+
+ for (i = 0; i < 2; i++) {
+ routing = i ? R2059_RXRX1 : R2059_TXRX0;
+ b43_radio_write(dev, routing | 0x4a, e->radio_rxtx4a);
+ b43_radio_write(dev, routing | 0x58, e->radio_rxtx58);
+ b43_radio_write(dev, routing | 0x5a, e->radio_rxtx5a);
+ b43_radio_write(dev, routing | 0x6a, e->radio_rxtx6a);
+ b43_radio_write(dev, routing | 0x6d, e->radio_rxtx6d);
+ b43_radio_write(dev, routing | 0x6e, e->radio_rxtx6e);
+ b43_radio_write(dev, routing | 0x92, e->radio_rxtx92);
+ b43_radio_write(dev, routing | 0x98, e->radio_rxtx98);
+ }
+
+ udelay(50);
+
+ /* Calibration */
+ b43_radio_mask(dev, 0x2b, ~0x1);
+ b43_radio_mask(dev, 0x2e, ~0x4);
+ b43_radio_set(dev, 0x2e, 0x4);
+ b43_radio_set(dev, 0x2b, 0x1);
+
+ udelay(300);
+}
+
+static void b43_radio_2059_init(struct b43_wldev *dev)
+{
+ const u16 routing[] = { R2059_SYN, R2059_TXRX0, R2059_RXRX1 };
+ const u16 radio_values[3][2] = {
+ { 0x61, 0xE9 }, { 0x69, 0xD5 }, { 0x73, 0x99 },
+ };
+ u16 i, j;
+
+ b43_radio_write(dev, R2059_ALL | 0x51, 0x0070);
+ b43_radio_write(dev, R2059_ALL | 0x5a, 0x0003);
+
+ for (i = 0; i < ARRAY_SIZE(routing); i++)
+ b43_radio_set(dev, routing[i] | 0x146, 0x3);
+
+ b43_radio_set(dev, 0x2e, 0x0078);
+ b43_radio_set(dev, 0xc0, 0x0080);
+ msleep(2);
+ b43_radio_mask(dev, 0x2e, ~0x0078);
+ b43_radio_mask(dev, 0xc0, ~0x0080);
+
+ if (1) { /* FIXME */
+ b43_radio_set(dev, R2059_RXRX1 | 0x4, 0x1);
+ udelay(10);
+ b43_radio_set(dev, R2059_RXRX1 | 0x0BF, 0x1);
+ b43_radio_maskset(dev, R2059_RXRX1 | 0x19B, 0x3, 0x2);
+
+ b43_radio_set(dev, R2059_RXRX1 | 0x4, 0x2);
+ udelay(100);
+ b43_radio_mask(dev, R2059_RXRX1 | 0x4, ~0x2);
+
+ for (i = 0; i < 10000; i++) {
+ if (b43_radio_read(dev, R2059_RXRX1 | 0x145) & 1) {
+ i = 0;
+ break;
+ }
+ udelay(100);
+ }
+ if (i)
+ b43err(dev->wl, "radio 0x945 timeout\n");
+
+ b43_radio_mask(dev, R2059_RXRX1 | 0x4, ~0x1);
+ b43_radio_set(dev, 0xa, 0x60);
+
+ for (i = 0; i < 3; i++) {
+ b43_radio_write(dev, 0x17F, radio_values[i][0]);
+ b43_radio_write(dev, 0x13D, 0x6E);
+ b43_radio_write(dev, 0x13E, radio_values[i][1]);
+ b43_radio_write(dev, 0x13C, 0x55);
+
+ for (j = 0; j < 10000; j++) {
+ if (b43_radio_read(dev, 0x140) & 2) {
+ j = 0;
+ break;
+ }
+ udelay(500);
+ }
+ if (j)
+ b43err(dev->wl, "radio 0x140 timeout\n");
+
+ b43_radio_write(dev, 0x13C, 0x15);
+ }
+
+ b43_radio_mask(dev, 0x17F, ~0x1);
+ }
+
+ b43_radio_mask(dev, 0x11, ~0x0008);
+}
+
+/**************************************************
+ * Various PHY ops
+ **************************************************/
+
+static void b43_phy_ht_zero_extg(struct b43_wldev *dev)
+{
+ u8 i, j;
+ u16 base[] = { 0x40, 0x60, 0x80 };
+
+ for (i = 0; i < ARRAY_SIZE(base); i++) {
+ for (j = 0; j < 4; j++)
+ b43_phy_write(dev, B43_PHY_EXTG(base[i] + j), 0);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(base); i++)
+ b43_phy_write(dev, B43_PHY_EXTG(base[i] + 0xc), 0);
+}
+
+/* Some unknown AFE (Analog Frondned) op */
+static void b43_phy_ht_afe_unk1(struct b43_wldev *dev)
+{
+ u8 i;
+
+ const u16 ctl_regs[3][2] = {
+ { B43_PHY_HT_AFE_CTL1, B43_PHY_HT_AFE_CTL2 },
+ { B43_PHY_HT_AFE_CTL3, B43_PHY_HT_AFE_CTL4 },
+ { B43_PHY_HT_AFE_CTL5, B43_PHY_HT_AFE_CTL6},
+ };
+
+ for (i = 0; i < 3; i++) {
+ /* TODO: verify masks&sets */
+ b43_phy_set(dev, ctl_regs[i][1], 0x4);
+ b43_phy_set(dev, ctl_regs[i][0], 0x4);
+ b43_phy_mask(dev, ctl_regs[i][1], ~0x1);
+ b43_phy_set(dev, ctl_regs[i][0], 0x1);
+ b43_httab_write(dev, B43_HTTAB16(8, 5 + (i * 0x10)), 0);
+ b43_phy_mask(dev, ctl_regs[i][0], ~0x4);
+ }
+}
+
+static void b43_phy_ht_force_rf_sequence(struct b43_wldev *dev, u16 rf_seq)
+{
+ u8 i;
+
+ u16 save_seq_mode = b43_phy_read(dev, B43_PHY_HT_RF_SEQ_MODE);
+ b43_phy_set(dev, B43_PHY_HT_RF_SEQ_MODE, 0x3);
+
+ b43_phy_set(dev, B43_PHY_HT_RF_SEQ_TRIG, rf_seq);
+ for (i = 0; i < 200; i++) {
+ if (!(b43_phy_read(dev, B43_PHY_HT_RF_SEQ_STATUS) & rf_seq)) {
+ i = 0;
+ break;
+ }
+ msleep(1);
+ }
+ if (i)
+ b43err(dev->wl, "Forcing RF sequence timeout\n");
+
+ b43_phy_write(dev, B43_PHY_HT_RF_SEQ_MODE, save_seq_mode);
+}
+
+static void b43_phy_ht_read_clip_detection(struct b43_wldev *dev, u16 *clip_st)
+{
+ clip_st[0] = b43_phy_read(dev, B43_PHY_HT_C1_CLIP1THRES);
+ clip_st[1] = b43_phy_read(dev, B43_PHY_HT_C2_CLIP1THRES);
+ clip_st[2] = b43_phy_read(dev, B43_PHY_HT_C3_CLIP1THRES);
+}
+
+static void b43_phy_ht_bphy_init(struct b43_wldev *dev)
+{
+ unsigned int i;
+ u16 val;
+
+ val = 0x1E1F;
+ for (i = 0; i < 16; i++) {
+ b43_phy_write(dev, B43_PHY_N_BMODE(0x88 + i), val);
+ val -= 0x202;
+ }
+ val = 0x3E3F;
+ for (i = 0; i < 16; i++) {
+ b43_phy_write(dev, B43_PHY_N_BMODE(0x98 + i), val);
+ val -= 0x202;
+ }
+ b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668);
+}
+
+/**************************************************
+ * Channel switching ops.
+ **************************************************/
+
+static void b43_phy_ht_channel_setup(struct b43_wldev *dev,
+ const struct b43_phy_ht_channeltab_e_phy *e,
+ struct ieee80211_channel *new_channel)
+{
+ bool old_band_5ghz;
+ u8 i;
+
+ old_band_5ghz = b43_phy_read(dev, B43_PHY_HT_BANDCTL) & 0; /* FIXME */
+ if (new_channel->band == IEEE80211_BAND_5GHZ && !old_band_5ghz) {
+ /* TODO */
+ } else if (new_channel->band == IEEE80211_BAND_2GHZ && old_band_5ghz) {
+ /* TODO */
+ }
+
+ b43_phy_write(dev, B43_PHY_HT_BW1, e->bw1);
+ b43_phy_write(dev, B43_PHY_HT_BW2, e->bw2);
+ b43_phy_write(dev, B43_PHY_HT_BW3, e->bw3);
+ b43_phy_write(dev, B43_PHY_HT_BW4, e->bw4);
+ b43_phy_write(dev, B43_PHY_HT_BW5, e->bw5);
+ b43_phy_write(dev, B43_PHY_HT_BW6, e->bw6);
+
+ /* TODO: some ops on PHY regs 0x0B0 and 0xC0A */
+
+ /* TODO: separated function? */
+ for (i = 0; i < 3; i++) {
+ u16 mask;
+ u32 tmp = b43_httab_read(dev, B43_HTTAB32(26, 0xE8));
+
+ if (0) /* FIXME */
+ mask = 0x2 << (i * 4);
+ else
+ mask = 0;
+ b43_phy_mask(dev, B43_PHY_EXTG(0x108), mask);
+
+ b43_httab_write(dev, B43_HTTAB16(7, 0x110 + i), tmp >> 16);
+ b43_httab_write(dev, B43_HTTAB8(13, 0x63 + (i * 4)),
+ tmp & 0xFF);
+ b43_httab_write(dev, B43_HTTAB8(13, 0x73 + (i * 4)),
+ tmp & 0xFF);
+ }
+
+ b43_phy_write(dev, 0x017e, 0x3830);
+}
+
+static int b43_phy_ht_set_channel(struct b43_wldev *dev,
+ struct ieee80211_channel *channel,
+ enum nl80211_channel_type channel_type)
+{
+ struct b43_phy *phy = &dev->phy;
+
+ const struct b43_phy_ht_channeltab_e_radio2059 *chent_r2059 = NULL;
+
+ if (phy->radio_ver == 0x2059) {
+ chent_r2059 = b43_phy_ht_get_channeltab_e_r2059(dev,
+ channel->center_freq);
+ if (!chent_r2059)
+ return -ESRCH;
+ } else {
+ return -ESRCH;
+ }
+
+ /* TODO: In case of N-PHY some bandwidth switching goes here */
+
+ if (phy->radio_ver == 0x2059) {
+ b43_radio_2059_channel_setup(dev, chent_r2059);
+ b43_phy_ht_channel_setup(dev, &(chent_r2059->phy_regs),
+ channel);
+ } else {
+ return -ESRCH;
+ }
+
+ return 0;
+}
+
+/**************************************************
+ * Basic PHY ops.
+ **************************************************/
+
+static int b43_phy_ht_op_allocate(struct b43_wldev *dev)
+{
+ struct b43_phy_ht *phy_ht;
+
+ phy_ht = kzalloc(sizeof(*phy_ht), GFP_KERNEL);
+ if (!phy_ht)
+ return -ENOMEM;
+ dev->phy.ht = phy_ht;
+
+ return 0;
+}
+
+static void b43_phy_ht_op_prepare_structs(struct b43_wldev *dev)
+{
+ struct b43_phy *phy = &dev->phy;
+ struct b43_phy_ht *phy_ht = phy->ht;
+
+ memset(phy_ht, 0, sizeof(*phy_ht));
+}
+
+static int b43_phy_ht_op_init(struct b43_wldev *dev)
+{
+ u16 tmp;
+ u16 clip_state[3];
+
+ b43_phy_ht_tables_init(dev);
+
+ b43_phy_mask(dev, 0x0be, ~0x2);
+ b43_phy_set(dev, 0x23f, 0x7ff);
+ b43_phy_set(dev, 0x240, 0x7ff);
+ b43_phy_set(dev, 0x241, 0x7ff);
+
+ b43_phy_ht_zero_extg(dev);
+
+ b43_phy_mask(dev, B43_PHY_EXTG(0), ~0x3);
+
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL1, 0);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL3, 0);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL5, 0);
+
+ b43_phy_write(dev, B43_PHY_EXTG(0x103), 0x20);
+ b43_phy_write(dev, B43_PHY_EXTG(0x101), 0x20);
+ b43_phy_write(dev, 0x20d, 0xb8);
+ b43_phy_write(dev, B43_PHY_EXTG(0x14f), 0xc8);
+ b43_phy_write(dev, 0x70, 0x50);
+ b43_phy_write(dev, 0x1ff, 0x30);
+
+ if (0) /* TODO: condition */
+ ; /* TODO: PHY op on reg 0x217 */
+
+ b43_phy_read(dev, 0xb0); /* TODO: what for? */
+ b43_phy_set(dev, 0xb0, 0x1);
+
+ b43_phy_set(dev, 0xb1, 0x91);
+ b43_phy_write(dev, 0x32f, 0x0003);
+ b43_phy_write(dev, 0x077, 0x0010);
+ b43_phy_write(dev, 0x0b4, 0x0258);
+ b43_phy_mask(dev, 0x17e, ~0x4000);
+
+ b43_phy_write(dev, 0x0b9, 0x0072);
+
+ b43_httab_write_few(dev, B43_HTTAB16(7, 0x14e), 2, 0x010f, 0x010f);
+ b43_httab_write_few(dev, B43_HTTAB16(7, 0x15e), 2, 0x010f, 0x010f);
+ b43_httab_write_few(dev, B43_HTTAB16(7, 0x16e), 2, 0x010f, 0x010f);
+
+ b43_phy_ht_afe_unk1(dev);
+
+ b43_httab_write_few(dev, B43_HTTAB16(7, 0x130), 9, 0x777, 0x111, 0x111,
+ 0x777, 0x111, 0x111, 0x777, 0x111, 0x111);
+
+ b43_httab_write(dev, B43_HTTAB16(7, 0x120), 0x0777);
+ b43_httab_write(dev, B43_HTTAB16(7, 0x124), 0x0777);
+
+ b43_httab_write(dev, B43_HTTAB16(8, 0x00), 0x02);
+ b43_httab_write(dev, B43_HTTAB16(8, 0x10), 0x02);
+ b43_httab_write(dev, B43_HTTAB16(8, 0x20), 0x02);
+
+ b43_httab_write_few(dev, B43_HTTAB16(8, 0x08), 4,
+ 0x8e, 0x96, 0x96, 0x96);
+ b43_httab_write_few(dev, B43_HTTAB16(8, 0x18), 4,
+ 0x8f, 0x9f, 0x9f, 0x9f);
+ b43_httab_write_few(dev, B43_HTTAB16(8, 0x28), 4,
+ 0x8f, 0x9f, 0x9f, 0x9f);
+
+ b43_httab_write_few(dev, B43_HTTAB16(8, 0x0c), 4, 0x2, 0x2, 0x2, 0x2);
+ b43_httab_write_few(dev, B43_HTTAB16(8, 0x1c), 4, 0x2, 0x2, 0x2, 0x2);
+ b43_httab_write_few(dev, B43_HTTAB16(8, 0x2c), 4, 0x2, 0x2, 0x2, 0x2);
+
+ b43_phy_maskset(dev, 0x0280, 0xff00, 0x3e);
+ b43_phy_maskset(dev, 0x0283, 0xff00, 0x3e);
+ b43_phy_maskset(dev, B43_PHY_OFDM(0x0141), 0xff00, 0x46);
+ b43_phy_maskset(dev, 0x0283, 0xff00, 0x40);
+
+ b43_httab_write_few(dev, B43_HTTAB16(00, 0x8), 4,
+ 0x09, 0x0e, 0x13, 0x18);
+ b43_httab_write_few(dev, B43_HTTAB16(01, 0x8), 4,
+ 0x09, 0x0e, 0x13, 0x18);
+ /* TODO: Did wl mean 2 instead of 40? */
+ b43_httab_write_few(dev, B43_HTTAB16(40, 0x8), 4,
+ 0x09, 0x0e, 0x13, 0x18);
+
+ b43_phy_maskset(dev, B43_PHY_OFDM(0x24), 0x3f, 0xd);
+ b43_phy_maskset(dev, B43_PHY_OFDM(0x64), 0x3f, 0xd);
+ b43_phy_maskset(dev, B43_PHY_OFDM(0xa4), 0x3f, 0xd);
+
+ b43_phy_set(dev, B43_PHY_EXTG(0x060), 0x1);
+ b43_phy_set(dev, B43_PHY_EXTG(0x064), 0x1);
+ b43_phy_set(dev, B43_PHY_EXTG(0x080), 0x1);
+ b43_phy_set(dev, B43_PHY_EXTG(0x084), 0x1);
+
+ /* Copy some tables entries */
+ tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x144));
+ b43_httab_write(dev, B43_HTTAB16(7, 0x14a), tmp);
+ tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x154));
+ b43_httab_write(dev, B43_HTTAB16(7, 0x15a), tmp);
+ tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x164));
+ b43_httab_write(dev, B43_HTTAB16(7, 0x16a), tmp);
+
+ /* Reset CCA */
+ b43_phy_force_clock(dev, true);
+ tmp = b43_phy_read(dev, B43_PHY_HT_BBCFG);
+ b43_phy_write(dev, B43_PHY_HT_BBCFG, tmp | B43_PHY_HT_BBCFG_RSTCCA);
+ b43_phy_write(dev, B43_PHY_HT_BBCFG, tmp & ~B43_PHY_HT_BBCFG_RSTCCA);
+ b43_phy_force_clock(dev, false);
+
+ b43_mac_phy_clock_set(dev, true);
+
+ b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RX2TX);
+ b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RST2RX);
+
+ /* TODO: PHY op on reg 0xb0 */
+
+ /* TODO: Should we restore it? Or store it in global PHY info? */
+ b43_phy_ht_read_clip_detection(dev, clip_state);
+
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+ b43_phy_ht_bphy_init(dev);
+
+ b43_httab_write_bulk(dev, B43_HTTAB32(0x1a, 0xc0),
+ B43_HTTAB_1A_C0_LATE_SIZE, b43_httab_0x1a_0xc0_late);
+
+ return 0;
+}
+
+static void b43_phy_ht_op_free(struct b43_wldev *dev)
+{
+ struct b43_phy *phy = &dev->phy;
+ struct b43_phy_ht *phy_ht = phy->ht;
+
+ kfree(phy_ht);
+ phy->ht = NULL;
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/Radio/Switch%20Radio */
+static void b43_phy_ht_op_software_rfkill(struct b43_wldev *dev,
+ bool blocked)
+{
+ if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED)
+ b43err(dev->wl, "MAC not suspended\n");
+
+ /* In the following PHY ops we copy wl's dummy behaviour.
+ * TODO: Find out if reads (currently hidden in masks/masksets) are
+ * needed and replace following ops with just writes or w&r.
+ * Note: B43_PHY_HT_RF_CTL1 register is tricky, wrong operation can
+ * cause delayed (!) machine lock up. */
+ if (blocked) {
+ b43_phy_mask(dev, B43_PHY_HT_RF_CTL1, 0);
+ } else {
+ b43_phy_mask(dev, B43_PHY_HT_RF_CTL1, 0);
+ b43_phy_maskset(dev, B43_PHY_HT_RF_CTL1, 0, 0x1);
+ b43_phy_mask(dev, B43_PHY_HT_RF_CTL1, 0);
+ b43_phy_maskset(dev, B43_PHY_HT_RF_CTL1, 0, 0x2);
+
+ if (dev->phy.radio_ver == 0x2059)
+ b43_radio_2059_init(dev);
+ else
+ B43_WARN_ON(1);
+
+ b43_switch_channel(dev, dev->phy.channel);
+ }
+}
+
+static void b43_phy_ht_op_switch_analog(struct b43_wldev *dev, bool on)
+{
+ if (on) {
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL2, 0x00cd);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL1, 0x0000);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL4, 0x00cd);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL3, 0x0000);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL6, 0x00cd);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL5, 0x0000);
+ } else {
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL1, 0x07ff);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL2, 0x00fd);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL3, 0x07ff);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL4, 0x00fd);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL5, 0x07ff);
+ b43_phy_write(dev, B43_PHY_HT_AFE_CTL6, 0x00fd);
+ }
+}
+
+static int b43_phy_ht_op_switch_channel(struct b43_wldev *dev,
+ unsigned int new_channel)
+{
+ struct ieee80211_channel *channel = dev->wl->hw->conf.channel;
+ enum nl80211_channel_type channel_type = dev->wl->hw->conf.channel_type;
+
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+ if ((new_channel < 1) || (new_channel > 14))
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+
+ return b43_phy_ht_set_channel(dev, channel, channel_type);
+}
+
+static unsigned int b43_phy_ht_op_get_default_chan(struct b43_wldev *dev)
+{
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+ return 11;
+ return 36;
+}
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+static u16 b43_phy_ht_op_read(struct b43_wldev *dev, u16 reg)
+{
+ b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
+ return b43_read16(dev, B43_MMIO_PHY_DATA);
+}
+
+static void b43_phy_ht_op_write(struct b43_wldev *dev, u16 reg, u16 value)
+{
+ b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
+ b43_write16(dev, B43_MMIO_PHY_DATA, value);
+}
+
+static void b43_phy_ht_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
+ u16 set)
+{
+ b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
+ b43_write16(dev, B43_MMIO_PHY_DATA,
+ (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
+}
+
+static u16 b43_phy_ht_op_radio_read(struct b43_wldev *dev, u16 reg)
+{
+ /* HT-PHY needs 0x200 for read access */
+ reg |= 0x200;
+
+ b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
+ return b43_read16(dev, B43_MMIO_RADIO24_DATA);
+}
+
+static void b43_phy_ht_op_radio_write(struct b43_wldev *dev, u16 reg,
+ u16 value)
+{
+ b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
+ b43_write16(dev, B43_MMIO_RADIO24_DATA, value);
+}
+
+static enum b43_txpwr_result
+b43_phy_ht_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi)
+{
+ return B43_TXPWR_RES_DONE;
+}
+
+static void b43_phy_ht_op_adjust_txpower(struct b43_wldev *dev)
+{
+}
+
+/**************************************************
+ * PHY ops struct.
+ **************************************************/
+
+const struct b43_phy_operations b43_phyops_ht = {
+ .allocate = b43_phy_ht_op_allocate,
+ .free = b43_phy_ht_op_free,
+ .prepare_structs = b43_phy_ht_op_prepare_structs,
+ .init = b43_phy_ht_op_init,
+ .phy_read = b43_phy_ht_op_read,
+ .phy_write = b43_phy_ht_op_write,
+ .phy_maskset = b43_phy_ht_op_maskset,
+ .radio_read = b43_phy_ht_op_radio_read,
+ .radio_write = b43_phy_ht_op_radio_write,
+ .software_rfkill = b43_phy_ht_op_software_rfkill,
+ .switch_analog = b43_phy_ht_op_switch_analog,
+ .switch_channel = b43_phy_ht_op_switch_channel,
+ .get_default_chan = b43_phy_ht_op_get_default_chan,
+ .recalc_txpower = b43_phy_ht_op_recalc_txpower,
+ .adjust_txpower = b43_phy_ht_op_adjust_txpower,
+};
diff --git a/drivers/net/wireless/b43/phy_ht.h b/drivers/net/wireless/b43/phy_ht.h
new file mode 100644
index 000000000000..6544c4293b34
--- /dev/null
+++ b/drivers/net/wireless/b43/phy_ht.h
@@ -0,0 +1,65 @@
+#ifndef B43_PHY_HT_H_
+#define B43_PHY_HT_H_
+
+#include "phy_common.h"
+
+
+#define B43_PHY_HT_BBCFG 0x001 /* BB config */
+#define B43_PHY_HT_BBCFG_RSTCCA 0x4000 /* Reset CCA */
+#define B43_PHY_HT_BBCFG_RSTRX 0x8000 /* Reset RX */
+#define B43_PHY_HT_BANDCTL 0x009 /* Band control */
+#define B43_PHY_HT_BANDCTL_5GHZ 0x0001 /* Use the 5GHz band */
+#define B43_PHY_HT_TABLE_ADDR 0x072 /* Table address */
+#define B43_PHY_HT_TABLE_DATALO 0x073 /* Table data low */
+#define B43_PHY_HT_TABLE_DATAHI 0x074 /* Table data high */
+#define B43_PHY_HT_BW1 0x1CE
+#define B43_PHY_HT_BW2 0x1CF
+#define B43_PHY_HT_BW3 0x1D0
+#define B43_PHY_HT_BW4 0x1D1
+#define B43_PHY_HT_BW5 0x1D2
+#define B43_PHY_HT_BW6 0x1D3
+
+#define B43_PHY_HT_C1_CLIP1THRES B43_PHY_OFDM(0x00E)
+#define B43_PHY_HT_C2_CLIP1THRES B43_PHY_OFDM(0x04E)
+#define B43_PHY_HT_C3_CLIP1THRES B43_PHY_OFDM(0x08E)
+
+#define B43_PHY_HT_RF_SEQ_MODE B43_PHY_EXTG(0x000)
+#define B43_PHY_HT_RF_SEQ_TRIG B43_PHY_EXTG(0x003)
+#define B43_PHY_HT_RF_SEQ_TRIG_RX2TX 0x0001 /* RX2TX */
+#define B43_PHY_HT_RF_SEQ_TRIG_TX2RX 0x0002 /* TX2RX */
+#define B43_PHY_HT_RF_SEQ_TRIG_UPGH 0x0004 /* Update gain H */
+#define B43_PHY_HT_RF_SEQ_TRIG_UPGL 0x0008 /* Update gain L */
+#define B43_PHY_HT_RF_SEQ_TRIG_UPGU 0x0010 /* Update gain U */
+#define B43_PHY_HT_RF_SEQ_TRIG_RST2RX 0x0020 /* Reset to RX */
+#define B43_PHY_HT_RF_SEQ_STATUS B43_PHY_EXTG(0x004)
+/* Values for the status are the same as for the trigger */
+
+#define B43_PHY_HT_RF_CTL1 B43_PHY_EXTG(0x010)
+
+#define B43_PHY_HT_AFE_CTL1 B43_PHY_EXTG(0x110)
+#define B43_PHY_HT_AFE_CTL2 B43_PHY_EXTG(0x111)
+#define B43_PHY_HT_AFE_CTL3 B43_PHY_EXTG(0x114)
+#define B43_PHY_HT_AFE_CTL4 B43_PHY_EXTG(0x115)
+#define B43_PHY_HT_AFE_CTL5 B43_PHY_EXTG(0x118)
+#define B43_PHY_HT_AFE_CTL6 B43_PHY_EXTG(0x119)
+
+
+/* Values for PHY registers used on channel switching */
+struct b43_phy_ht_channeltab_e_phy {
+ u16 bw1;
+ u16 bw2;
+ u16 bw3;
+ u16 bw4;
+ u16 bw5;
+ u16 bw6;
+};
+
+
+struct b43_phy_ht {
+};
+
+
+struct b43_phy_operations;
+extern const struct b43_phy_operations b43_phyops_ht;
+
+#endif /* B43_PHY_HT_H_ */
diff --git a/drivers/net/wireless/b43/phy_lcn.c b/drivers/net/wireless/b43/phy_lcn.c
new file mode 100644
index 000000000000..a13e28ef6246
--- /dev/null
+++ b/drivers/net/wireless/b43/phy_lcn.c
@@ -0,0 +1,901 @@
+/*
+
+ Broadcom B43 wireless driver
+ IEEE 802.11n LCN-PHY support
+
+ Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This file incorporates work covered by the following copyright and
+ permission notice:
+
+ Copyright (c) 2010 Broadcom Corporation
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+*/
+
+#include <linux/slab.h>
+
+#include "b43.h"
+#include "phy_lcn.h"
+#include "tables_phy_lcn.h"
+#include "main.h"
+
+struct lcn_tx_gains {
+ u16 gm_gain;
+ u16 pga_gain;
+ u16 pad_gain;
+ u16 dac_gain;
+};
+
+struct lcn_tx_iir_filter {
+ u8 type;
+ u16 values[16];
+};
+
+enum lcn_sense_type {
+ B43_SENSE_TEMP,
+ B43_SENSE_VBAT,
+};
+
+/* In theory it's PHY common function, move if needed */
+/* brcms_b_switch_macfreq */
+static void b43_phy_switch_macfreq(struct b43_wldev *dev, u8 spurmode)
+{
+ if (dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) {
+ switch (spurmode) {
+ case 2: /* 126 Mhz */
+ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082);
+ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+ break;
+ case 1: /* 123 Mhz */
+ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341);
+ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+ break;
+ default: /* 120 Mhz */
+ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889);
+ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+ break;
+ }
+ } else if (dev->phy.type == B43_PHYTYPE_LCN) {
+ switch (spurmode) {
+ case 1: /* 82 Mhz */
+ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0);
+ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
+ break;
+ default: /* 80 Mhz */
+ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD);
+ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
+ break;
+ }
+ }
+}
+
+/**************************************************
+ * Radio 2064.
+ **************************************************/
+
+/* wlc_lcnphy_radio_2064_channel_tune_4313 */
+static void b43_radio_2064_channel_setup(struct b43_wldev *dev)
+{
+ u16 save[2];
+
+ b43_radio_set(dev, 0x09d, 0x4);
+ b43_radio_write(dev, 0x09e, 0xf);
+
+ /* Channel specific values in theory, in practice always the same */
+ b43_radio_write(dev, 0x02a, 0xb);
+ b43_radio_maskset(dev, 0x030, ~0x3, 0xa);
+ b43_radio_maskset(dev, 0x091, ~0x3, 0);
+ b43_radio_maskset(dev, 0x038, ~0xf, 0x7);
+ b43_radio_maskset(dev, 0x030, ~0xc, 0x8);
+ b43_radio_maskset(dev, 0x05e, ~0xf, 0x8);
+ b43_radio_maskset(dev, 0x05e, ~0xf0, 0x80);
+ b43_radio_write(dev, 0x06c, 0x80);
+
+ save[0] = b43_radio_read(dev, 0x044);
+ save[1] = b43_radio_read(dev, 0x12b);
+
+ b43_radio_set(dev, 0x044, 0x7);
+ b43_radio_set(dev, 0x12b, 0xe);
+
+ /* TODO */
+
+ b43_radio_write(dev, 0x040, 0xfb);
+
+ b43_radio_write(dev, 0x041, 0x9a);
+ b43_radio_write(dev, 0x042, 0xa3);
+ b43_radio_write(dev, 0x043, 0x0c);
+
+ /* TODO */
+
+ b43_radio_set(dev, 0x044, 0x0c);
+ udelay(1);
+
+ b43_radio_write(dev, 0x044, save[0]);
+ b43_radio_write(dev, 0x12b, save[1]);
+
+ if (dev->phy.rev == 1) {
+ /* brcmsmac uses outdated 0x3 for 0x038 */
+ b43_radio_write(dev, 0x038, 0x0);
+ b43_radio_write(dev, 0x091, 0x7);
+ }
+}
+
+/* wlc_radio_2064_init */
+static void b43_radio_2064_init(struct b43_wldev *dev)
+{
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+ b43_radio_write(dev, 0x09c, 0x0020);
+ b43_radio_write(dev, 0x105, 0x0008);
+ } else {
+ /* TODO */
+ }
+ b43_radio_write(dev, 0x032, 0x0062);
+ b43_radio_write(dev, 0x033, 0x0019);
+ b43_radio_write(dev, 0x090, 0x0010);
+ b43_radio_write(dev, 0x010, 0x0000);
+ if (dev->phy.rev == 1) {
+ b43_radio_write(dev, 0x060, 0x007f);
+ b43_radio_write(dev, 0x061, 0x0072);
+ b43_radio_write(dev, 0x062, 0x007f);
+ }
+ b43_radio_write(dev, 0x01d, 0x0002);
+ b43_radio_write(dev, 0x01e, 0x0006);
+
+ b43_phy_write(dev, 0x4ea, 0x4688);
+ b43_phy_maskset(dev, 0x4eb, ~0x7, 0x2);
+ b43_phy_mask(dev, 0x4eb, ~0x01c0);
+ b43_phy_maskset(dev, 0x46a, 0xff00, 0x19);
+
+ b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x55), 0);
+
+ b43_radio_mask(dev, 0x05b, (u16) ~0xff02);
+ b43_radio_set(dev, 0x004, 0x40);
+ b43_radio_set(dev, 0x120, 0x10);
+ b43_radio_set(dev, 0x078, 0x80);
+ b43_radio_set(dev, 0x129, 0x2);
+ b43_radio_set(dev, 0x057, 0x1);
+ b43_radio_set(dev, 0x05b, 0x2);
+
+ /* TODO: wait for some bit to be set */
+ b43_radio_read(dev, 0x05c);
+
+ b43_radio_mask(dev, 0x05b, (u16) ~0xff02);
+ b43_radio_mask(dev, 0x057, (u16) ~0xff01);
+
+ b43_phy_write(dev, 0x933, 0x2d6b);
+ b43_phy_write(dev, 0x934, 0x2d6b);
+ b43_phy_write(dev, 0x935, 0x2d6b);
+ b43_phy_write(dev, 0x936, 0x2d6b);
+ b43_phy_write(dev, 0x937, 0x016b);
+
+ b43_radio_mask(dev, 0x057, (u16) ~0xff02);
+ b43_radio_write(dev, 0x0c2, 0x006f);
+}
+
+/**************************************************
+ * Various PHY ops
+ **************************************************/
+
+/* wlc_lcnphy_toggle_afe_pwdn */
+static void b43_phy_lcn_afe_set_unset(struct b43_wldev *dev)
+{
+ u16 afe_ctl2 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL2);
+ u16 afe_ctl1 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL1);
+
+ b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 | 0x1);
+ b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 | 0x1);
+
+ b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 & ~0x1);
+ b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 & ~0x1);
+
+ b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2);
+ b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1);
+}
+
+/* wlc_lcnphy_get_pa_gain */
+static u16 b43_phy_lcn_get_pa_gain(struct b43_wldev *dev)
+{
+ return (b43_phy_read(dev, 0x4fb) & 0x7f00) >> 8;
+}
+
+/* wlc_lcnphy_set_dac_gain */
+static void b43_phy_lcn_set_dac_gain(struct b43_wldev *dev, u16 dac_gain)
+{
+ u16 dac_ctrl;
+
+ dac_ctrl = b43_phy_read(dev, 0x439);
+ dac_ctrl = dac_ctrl & 0xc7f;
+ dac_ctrl = dac_ctrl | (dac_gain << 7);
+ b43_phy_maskset(dev, 0x439, ~0xfff, dac_ctrl);
+}
+
+/* wlc_lcnphy_set_bbmult */
+static void b43_phy_lcn_set_bbmult(struct b43_wldev *dev, u8 m0)
+{
+ b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x57), m0 << 8);
+}
+
+/* wlc_lcnphy_clear_tx_power_offsets */
+static void b43_phy_lcn_clear_tx_power_offsets(struct b43_wldev *dev)
+{
+ u8 i;
+
+ if (1) { /* FIXME */
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x340);
+ for (i = 0; i < 30; i++) {
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0);
+ }
+ }
+
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x80);
+ for (i = 0; i < 64; i++) {
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0);
+ }
+}
+
+/* wlc_lcnphy_rev0_baseband_init */
+static void b43_phy_lcn_rev0_baseband_init(struct b43_wldev *dev)
+{
+ b43_radio_write(dev, 0x11c, 0);
+
+ b43_phy_write(dev, 0x43b, 0);
+ b43_phy_write(dev, 0x43c, 0);
+ b43_phy_write(dev, 0x44c, 0);
+ b43_phy_write(dev, 0x4e6, 0);
+ b43_phy_write(dev, 0x4f9, 0);
+ b43_phy_write(dev, 0x4b0, 0);
+ b43_phy_write(dev, 0x938, 0);
+ b43_phy_write(dev, 0x4b0, 0);
+ b43_phy_write(dev, 0x44e, 0);
+
+ b43_phy_set(dev, 0x567, 0x03);
+
+ b43_phy_set(dev, 0x44a, 0x44);
+ b43_phy_write(dev, 0x44a, 0x80);
+
+ if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM))
+ ; /* TODO */
+ b43_phy_maskset(dev, 0x634, ~0xff, 0xc);
+ if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) {
+ b43_phy_maskset(dev, 0x634, ~0xff, 0xa);
+ b43_phy_write(dev, 0x910, 0x1);
+ }
+
+ b43_phy_write(dev, 0x910, 0x1);
+
+ b43_phy_maskset(dev, 0x448, ~0x300, 0x100);
+ b43_phy_maskset(dev, 0x608, ~0xff, 0x17);
+ b43_phy_maskset(dev, 0x604, ~0x7ff, 0x3ea);
+}
+
+/* wlc_lcnphy_bu_tweaks */
+static void b43_phy_lcn_bu_tweaks(struct b43_wldev *dev)
+{
+ b43_phy_set(dev, 0x805, 0x1);
+
+ b43_phy_maskset(dev, 0x42f, ~0x7, 0x3);
+ b43_phy_maskset(dev, 0x030, ~0x7, 0x3);
+
+ b43_phy_write(dev, 0x414, 0x1e10);
+ b43_phy_write(dev, 0x415, 0x0640);
+
+ b43_phy_maskset(dev, 0x4df, (u16) ~0xff00, 0xf700);
+
+ b43_phy_set(dev, 0x44a, 0x44);
+ b43_phy_write(dev, 0x44a, 0x80);
+
+ b43_phy_maskset(dev, 0x434, ~0xff, 0xfd);
+ b43_phy_maskset(dev, 0x420, ~0xff, 0x10);
+
+ if (dev->dev->bus_sprom->board_rev >= 0x1204)
+ b43_radio_set(dev, 0x09b, 0xf0);
+
+ b43_phy_write(dev, 0x7d6, 0x0902);
+
+ b43_phy_maskset(dev, 0x429, ~0xf, 0x9);
+ b43_phy_maskset(dev, 0x429, ~(0x3f << 4), 0xe << 4);
+
+ if (dev->phy.rev == 1) {
+ b43_phy_maskset(dev, 0x423, ~0xff, 0x46);
+ b43_phy_maskset(dev, 0x411, ~0xff, 1);
+ b43_phy_set(dev, 0x434, 0xff); /* FIXME: update to wl */
+
+ /* TODO: wl operates on PHY 0x416, brcmsmac is outdated here */
+
+ b43_phy_maskset(dev, 0x656, ~0xf, 2);
+ b43_phy_set(dev, 0x44d, 4);
+
+ b43_radio_set(dev, 0x0f7, 0x4);
+ b43_radio_mask(dev, 0x0f1, ~0x3);
+ b43_radio_maskset(dev, 0x0f2, ~0xf8, 0x90);
+ b43_radio_maskset(dev, 0x0f3, ~0x3, 0x2);
+ b43_radio_maskset(dev, 0x0f3, ~0xf0, 0xa0);
+
+ b43_radio_set(dev, 0x11f, 0x2);
+
+ b43_phy_lcn_clear_tx_power_offsets(dev);
+
+ /* TODO: something more? */
+ }
+}
+
+/* wlc_lcnphy_vbat_temp_sense_setup */
+static void b43_phy_lcn_sense_setup(struct b43_wldev *dev,
+ enum lcn_sense_type sense_type)
+{
+ u8 auxpga_vmidcourse, auxpga_vmidfine, auxpga_gain;
+ u16 auxpga_vmid;
+ u8 tx_pwr_idx;
+ u8 i;
+
+ u16 save_radio_regs[6][2] = {
+ { 0x007, 0 }, { 0x0ff, 0 }, { 0x11f, 0 }, { 0x005, 0 },
+ { 0x025, 0 }, { 0x112, 0 },
+ };
+ u16 save_phy_regs[14][2] = {
+ { 0x503, 0 }, { 0x4a4, 0 }, { 0x4d0, 0 }, { 0x4d9, 0 },
+ { 0x4da, 0 }, { 0x4a6, 0 }, { 0x938, 0 }, { 0x939, 0 },
+ { 0x4d8, 0 }, { 0x4d0, 0 }, { 0x4d7, 0 }, { 0x4a5, 0 },
+ { 0x40d, 0 }, { 0x4a2, 0 },
+ };
+ u16 save_radio_4a4;
+
+ msleep(1);
+
+ /* Save */
+ for (i = 0; i < 6; i++)
+ save_radio_regs[i][1] = b43_radio_read(dev,
+ save_radio_regs[i][0]);
+ for (i = 0; i < 14; i++)
+ save_phy_regs[i][1] = b43_phy_read(dev, save_phy_regs[i][0]);
+ b43_mac_suspend(dev);
+ save_radio_4a4 = b43_radio_read(dev, 0x4a4);
+ /* wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); */
+ tx_pwr_idx = dev->phy.lcn->tx_pwr_curr_idx;
+
+ /* Setup */
+ /* TODO: wlc_lcnphy_set_tx_pwr_by_index(pi, 127); */
+ b43_radio_set(dev, 0x007, 0x1);
+ b43_radio_set(dev, 0x0ff, 0x10);
+ b43_radio_set(dev, 0x11f, 0x4);
+
+ b43_phy_mask(dev, 0x503, ~0x1);
+ b43_phy_mask(dev, 0x503, ~0x4);
+ b43_phy_mask(dev, 0x4a4, ~0x4000);
+ b43_phy_mask(dev, 0x4a4, (u16) ~0x8000);
+ b43_phy_mask(dev, 0x4d0, ~0x20);
+ b43_phy_set(dev, 0x4a5, 0xff);
+ b43_phy_maskset(dev, 0x4a5, ~0x7000, 0x5000);
+ b43_phy_mask(dev, 0x4a5, ~0x700);
+ b43_phy_maskset(dev, 0x40d, ~0xff, 64);
+ b43_phy_maskset(dev, 0x40d, ~0x700, 0x600);
+ b43_phy_maskset(dev, 0x4a2, ~0xff, 64);
+ b43_phy_maskset(dev, 0x4a2, ~0x700, 0x600);
+ b43_phy_maskset(dev, 0x4d9, ~0x70, 0x20);
+ b43_phy_maskset(dev, 0x4d9, ~0x700, 0x300);
+ b43_phy_maskset(dev, 0x4d9, ~0x7000, 0x1000);
+ b43_phy_mask(dev, 0x4da, ~0x1000);
+ b43_phy_set(dev, 0x4da, 0x2000);
+ b43_phy_set(dev, 0x4a6, 0x8000);
+
+ b43_radio_write(dev, 0x025, 0xc);
+ b43_radio_set(dev, 0x005, 0x8);
+ b43_phy_set(dev, 0x938, 0x4);
+ b43_phy_set(dev, 0x939, 0x4);
+ b43_phy_set(dev, 0x4a4, 0x1000);
+
+ /* FIXME: don't hardcode */
+ b43_lcntab_write(dev, B43_LCNTAB16(0x8, 0x6), 0x640);
+
+ switch (sense_type) {
+ case B43_SENSE_TEMP:
+ b43_phy_set(dev, 0x4d7, 0x8);
+ b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x1000);
+ auxpga_vmidcourse = 8;
+ auxpga_vmidfine = 0x4;
+ auxpga_gain = 2;
+ b43_radio_set(dev, 0x082, 0x20);
+ break;
+ case B43_SENSE_VBAT:
+ b43_phy_set(dev, 0x4d7, 0x8);
+ b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x3000);
+ auxpga_vmidcourse = 7;
+ auxpga_vmidfine = 0xa;
+ auxpga_gain = 2;
+ break;
+ }
+ auxpga_vmid = (0x200 | (auxpga_vmidcourse << 4) | auxpga_vmidfine);
+
+ b43_phy_set(dev, 0x4d8, 0x1);
+ b43_phy_maskset(dev, 0x4d8, ~(0x3ff << 2), auxpga_vmid << 2);
+ b43_phy_set(dev, 0x4d8, 0x2);
+ b43_phy_maskset(dev, 0x4d8, ~(0x7 << 12), auxpga_gain << 12);
+ b43_phy_set(dev, 0x4d0, 0x20);
+ b43_radio_write(dev, 0x112, 0x6);
+
+ b43_dummy_transmission(dev, true, false);
+ /* Wait if not done */
+ if (!(b43_phy_read(dev, 0x476) & 0x8000))
+ udelay(10);
+
+ /* Restore */
+ for (i = 0; i < 6; i++)
+ b43_radio_write(dev, save_radio_regs[i][0],
+ save_radio_regs[i][1]);
+ for (i = 0; i < 14; i++)
+ b43_phy_write(dev, save_phy_regs[i][0], save_phy_regs[i][1]);
+ /* TODO: wlc_lcnphy_set_tx_pwr_by_index(tx_pwr_idx) */
+ b43_radio_write(dev, 0x4a4, save_radio_4a4);
+
+ b43_mac_enable(dev);
+
+ msleep(1);
+}
+
+static bool b43_phy_lcn_load_tx_iir_cck_filter(struct b43_wldev *dev,
+ u8 filter_type)
+{
+ int i, j;
+ u16 phy_regs[] = { 0x910, 0x91e, 0x91f, 0x924, 0x925, 0x926, 0x920,
+ 0x921, 0x927, 0x928, 0x929, 0x922, 0x923, 0x930,
+ 0x931, 0x932 };
+ /* Table is from brcmsmac, values for type 25 were outdated, probably
+ * others need updating too */
+ struct lcn_tx_iir_filter tx_iir_filters_cck[] = {
+ { 0, { 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778,
+ 1582, 64, 128, 64 } },
+ { 1, { 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608,
+ 1863, 93, 167, 93 } },
+ { 2, { 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192,
+ 778, 1582, 64, 128, 64 } },
+ { 3, { 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205,
+ 754, 1760, 170, 340, 170 } },
+ { 20, { 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205,
+ 767, 1760, 256, 185, 256 } },
+ { 21, { 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205,
+ 767, 1760, 256, 273, 256 } },
+ { 22, { 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205,
+ 767, 1760, 256, 352, 256 } },
+ { 23, { 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205,
+ 767, 1760, 128, 233, 128 } },
+ { 24, { 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766,
+ 1760, 256, 1881, 256 } },
+ { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765,
+ 1760, 262, 1878, 262 } },
+ /* brcmsmac version { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720,
+ * 256, 471, 256, 765, 1760, 256, 1881, 256 } }, */
+ { 26, { 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614,
+ 1864, 128, 384, 288 } },
+ { 27, { 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576,
+ 613, 1864, 128, 384, 288 } },
+ { 30, { 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205,
+ 754, 1760, 170, 340, 170 } },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(tx_iir_filters_cck); i++) {
+ if (tx_iir_filters_cck[i].type == filter_type) {
+ for (j = 0; j < 16; j++)
+ b43_phy_write(dev, phy_regs[j],
+ tx_iir_filters_cck[i].values[j]);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool b43_phy_lcn_load_tx_iir_ofdm_filter(struct b43_wldev *dev,
+ u8 filter_type)
+{
+ int i, j;
+ u16 phy_regs[] = { 0x90f, 0x900, 0x901, 0x906, 0x907, 0x908, 0x902,
+ 0x903, 0x909, 0x90a, 0x90b, 0x904, 0x905, 0x90c,
+ 0x90d, 0x90e };
+ struct lcn_tx_iir_filter tx_iir_filters_ofdm[] = {
+ { 0, { 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0,
+ 0x0, 0x278, 0xfea0, 0x80, 0x100, 0x80 } },
+ { 1, { 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, 750,
+ 0xFE2B, 212, 0xFFCE, 212 } },
+ { 2, { 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748,
+ 0xFEF2, 128, 0xFFE2, 128 } },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(tx_iir_filters_ofdm); i++) {
+ if (tx_iir_filters_ofdm[i].type == filter_type) {
+ for (j = 0; j < 16; j++)
+ b43_phy_write(dev, phy_regs[j],
+ tx_iir_filters_ofdm[i].values[j]);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* wlc_lcnphy_set_tx_gain_override */
+static void b43_phy_lcn_set_tx_gain_override(struct b43_wldev *dev, bool enable)
+{
+ b43_phy_maskset(dev, 0x4b0, ~(0x1 << 7), enable << 7);
+ b43_phy_maskset(dev, 0x4b0, ~(0x1 << 14), enable << 14);
+ b43_phy_maskset(dev, 0x43b, ~(0x1 << 6), enable << 6);
+}
+
+/* wlc_lcnphy_set_tx_gain */
+static void b43_phy_lcn_set_tx_gain(struct b43_wldev *dev,
+ struct lcn_tx_gains *target_gains)
+{
+ u16 pa_gain = b43_phy_lcn_get_pa_gain(dev);
+
+ b43_phy_write(dev, 0x4b5,
+ (target_gains->gm_gain | (target_gains->pga_gain << 8)));
+ b43_phy_maskset(dev, 0x4fb, ~0x7fff,
+ (target_gains->pad_gain | (pa_gain << 8)));
+ b43_phy_write(dev, 0x4fc,
+ (target_gains->gm_gain | (target_gains->pga_gain << 8)));
+ b43_phy_maskset(dev, 0x4fd, ~0x7fff,
+ (target_gains->pad_gain | (pa_gain << 8)));
+
+ b43_phy_lcn_set_dac_gain(dev, target_gains->dac_gain);
+ b43_phy_lcn_set_tx_gain_override(dev, true);
+}
+
+/* wlc_lcnphy_tx_pwr_ctrl_init */
+static void b43_phy_lcn_tx_pwr_ctl_init(struct b43_wldev *dev)
+{
+ struct lcn_tx_gains tx_gains;
+ u8 bbmult;
+
+ b43_mac_suspend(dev);
+
+ if (!dev->phy.lcn->hw_pwr_ctl_capable) {
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+ tx_gains.gm_gain = 4;
+ tx_gains.pga_gain = 12;
+ tx_gains.pad_gain = 12;
+ tx_gains.dac_gain = 0;
+ bbmult = 150;
+ } else {
+ tx_gains.gm_gain = 7;
+ tx_gains.pga_gain = 15;
+ tx_gains.pad_gain = 14;
+ tx_gains.dac_gain = 0;
+ bbmult = 150;
+ }
+ b43_phy_lcn_set_tx_gain(dev, &tx_gains);
+ b43_phy_lcn_set_bbmult(dev, bbmult);
+ b43_phy_lcn_sense_setup(dev, B43_SENSE_TEMP);
+ } else {
+ b43err(dev->wl, "TX power control not supported for this HW\n");
+ }
+
+ b43_mac_enable(dev);
+}
+
+/* wlc_lcnphy_txrx_spur_avoidance_mode */
+static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev,
+ bool enable)
+{
+ if (enable) {
+ b43_phy_write(dev, 0x942, 0x7);
+ b43_phy_write(dev, 0x93b, ((1 << 13) + 23));
+ b43_phy_write(dev, 0x93c, ((1 << 13) + 1989));
+
+ b43_phy_write(dev, 0x44a, 0x084);
+ b43_phy_write(dev, 0x44a, 0x080);
+ b43_phy_write(dev, 0x6d3, 0x2222);
+ b43_phy_write(dev, 0x6d3, 0x2220);
+ } else {
+ b43_phy_write(dev, 0x942, 0x0);
+ b43_phy_write(dev, 0x93b, ((0 << 13) + 23));
+ b43_phy_write(dev, 0x93c, ((0 << 13) + 1989));
+ }
+ b43_phy_switch_macfreq(dev, enable);
+}
+
+/**************************************************
+ * Channel switching ops.
+ **************************************************/
+
+/* wlc_lcnphy_set_chanspec_tweaks */
+static void b43_phy_lcn_set_channel_tweaks(struct b43_wldev *dev, int channel)
+{
+ struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc;
+
+ b43_phy_maskset(dev, 0x448, ~0x300, (channel == 14) ? 0x200 : 0x100);
+
+ if (channel == 1 || channel == 2 || channel == 3 || channel == 4 ||
+ channel == 9 || channel == 10 || channel == 11 || channel == 12) {
+ bcma_chipco_pll_write(cc, 0x2, 0x03000c04);
+ bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x0);
+ bcma_chipco_pll_write(cc, 0x4, 0x200005c0);
+
+ bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400);
+
+ b43_phy_write(dev, 0x942, 0);
+
+ b43_phy_lcn_txrx_spur_avoidance_mode(dev, false);
+ b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1b00);
+ b43_phy_write(dev, 0x425, 0x5907);
+ } else {
+ bcma_chipco_pll_write(cc, 0x2, 0x03140c04);
+ bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x333333);
+ bcma_chipco_pll_write(cc, 0x4, 0x202c2820);
+
+ bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400);
+
+ b43_phy_write(dev, 0x942, 0);
+
+ b43_phy_lcn_txrx_spur_avoidance_mode(dev, true);
+ b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1f00);
+ b43_phy_write(dev, 0x425, 0x590a);
+ }
+
+ b43_phy_set(dev, 0x44a, 0x44);
+ b43_phy_write(dev, 0x44a, 0x80);
+}
+
+/* wlc_phy_chanspec_set_lcnphy */
+static int b43_phy_lcn_set_channel(struct b43_wldev *dev,
+ struct ieee80211_channel *channel,
+ enum nl80211_channel_type channel_type)
+{
+ static const u16 sfo_cfg[14][2] = {
+ {965, 1087}, {967, 1085}, {969, 1082}, {971, 1080}, {973, 1078},
+ {975, 1076}, {977, 1073}, {979, 1071}, {981, 1069}, {983, 1067},
+ {985, 1065}, {987, 1063}, {989, 1060}, {994, 1055},
+ };
+
+ b43_phy_lcn_set_channel_tweaks(dev, channel->hw_value);
+
+ b43_phy_set(dev, 0x44a, 0x44);
+ b43_phy_write(dev, 0x44a, 0x80);
+
+ b43_radio_2064_channel_setup(dev);
+ mdelay(1);
+
+ b43_phy_lcn_afe_set_unset(dev);
+
+ b43_phy_write(dev, 0x657, sfo_cfg[channel->hw_value - 1][0]);
+ b43_phy_write(dev, 0x658, sfo_cfg[channel->hw_value - 1][1]);
+
+ if (channel->hw_value == 14) {
+ b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (2) << 8);
+ b43_phy_lcn_load_tx_iir_cck_filter(dev, 3);
+ } else {
+ b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (1) << 8);
+ /* brcmsmac uses filter_type 2, we follow wl with 25 */
+ b43_phy_lcn_load_tx_iir_cck_filter(dev, 25);
+ }
+ /* brcmsmac uses filter_type 2, we follow wl with 0 */
+ b43_phy_lcn_load_tx_iir_ofdm_filter(dev, 0);
+
+ b43_phy_maskset(dev, 0x4eb, ~(0x7 << 3), 0x1 << 3);
+
+ return 0;
+}
+
+/**************************************************
+ * Basic PHY ops.
+ **************************************************/
+
+static int b43_phy_lcn_op_allocate(struct b43_wldev *dev)
+{
+ struct b43_phy_lcn *phy_lcn;
+
+ phy_lcn = kzalloc(sizeof(*phy_lcn), GFP_KERNEL);
+ if (!phy_lcn)
+ return -ENOMEM;
+ dev->phy.lcn = phy_lcn;
+
+ return 0;
+}
+
+static void b43_phy_lcn_op_free(struct b43_wldev *dev)
+{
+ struct b43_phy *phy = &dev->phy;
+ struct b43_phy_lcn *phy_lcn = phy->lcn;
+
+ kfree(phy_lcn);
+ phy->lcn = NULL;
+}
+
+static void b43_phy_lcn_op_prepare_structs(struct b43_wldev *dev)
+{
+ struct b43_phy *phy = &dev->phy;
+ struct b43_phy_lcn *phy_lcn = phy->lcn;
+
+ memset(phy_lcn, 0, sizeof(*phy_lcn));
+}
+
+/* wlc_phy_init_lcnphy */
+static int b43_phy_lcn_op_init(struct b43_wldev *dev)
+{
+ struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc;
+
+ b43_phy_set(dev, 0x44a, 0x80);
+ b43_phy_mask(dev, 0x44a, 0x7f);
+ b43_phy_set(dev, 0x6d1, 0x80);
+ b43_phy_write(dev, 0x6d0, 0x7);
+
+ b43_phy_lcn_afe_set_unset(dev);
+
+ b43_phy_write(dev, 0x60a, 0xa0);
+ b43_phy_write(dev, 0x46a, 0x19);
+ b43_phy_maskset(dev, 0x663, 0xFF00, 0x64);
+
+ b43_phy_lcn_tables_init(dev);
+
+ b43_phy_lcn_rev0_baseband_init(dev);
+ b43_phy_lcn_bu_tweaks(dev);
+
+ if (dev->phy.radio_ver == 0x2064)
+ b43_radio_2064_init(dev);
+ else
+ B43_WARN_ON(1);
+
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+ b43_phy_lcn_tx_pwr_ctl_init(dev);
+
+ b43_switch_channel(dev, dev->phy.channel);
+
+ bcma_chipco_regctl_maskset(cc, 0, 0xf, 0x9);
+ bcma_chipco_chipctl_maskset(cc, 0, 0, 0x03cddddd);
+
+ /* TODO */
+
+ b43_phy_set(dev, 0x448, 0x4000);
+ udelay(100);
+ b43_phy_mask(dev, 0x448, ~0x4000);
+
+ /* TODO */
+
+ return 0;
+}
+
+static void b43_phy_lcn_op_software_rfkill(struct b43_wldev *dev,
+ bool blocked)
+{
+ if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED)
+ b43err(dev->wl, "MAC not suspended\n");
+
+ if (blocked) {
+ b43_phy_mask(dev, B43_PHY_LCN_RF_CTL2, ~0x7c00);
+ b43_phy_set(dev, B43_PHY_LCN_RF_CTL1, 0x1f00);
+
+ b43_phy_mask(dev, B43_PHY_LCN_RF_CTL5, ~0x7f00);
+ b43_phy_mask(dev, B43_PHY_LCN_RF_CTL4, ~0x2);
+ b43_phy_set(dev, B43_PHY_LCN_RF_CTL3, 0x808);
+
+ b43_phy_mask(dev, B43_PHY_LCN_RF_CTL7, ~0x8);
+ b43_phy_set(dev, B43_PHY_LCN_RF_CTL6, 0x8);
+ } else {
+ b43_phy_mask(dev, B43_PHY_LCN_RF_CTL1, ~0x1f00);
+ b43_phy_mask(dev, B43_PHY_LCN_RF_CTL3, ~0x808);
+ b43_phy_mask(dev, B43_PHY_LCN_RF_CTL6, ~0x8);
+ }
+}
+
+static void b43_phy_lcn_op_switch_analog(struct b43_wldev *dev, bool on)
+{
+ if (on) {
+ b43_phy_mask(dev, B43_PHY_LCN_AFE_CTL1, ~0x7);
+ } else {
+ b43_phy_set(dev, B43_PHY_LCN_AFE_CTL2, 0x7);
+ b43_phy_set(dev, B43_PHY_LCN_AFE_CTL1, 0x7);
+ }
+}
+
+static int b43_phy_lcn_op_switch_channel(struct b43_wldev *dev,
+ unsigned int new_channel)
+{
+ struct ieee80211_channel *channel = dev->wl->hw->conf.channel;
+ enum nl80211_channel_type channel_type = dev->wl->hw->conf.channel_type;
+
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+ if ((new_channel < 1) || (new_channel > 14))
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+
+ return b43_phy_lcn_set_channel(dev, channel, channel_type);
+}
+
+static unsigned int b43_phy_lcn_op_get_default_chan(struct b43_wldev *dev)
+{
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+ return 1;
+ return 36;
+}
+
+static enum b43_txpwr_result
+b43_phy_lcn_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi)
+{
+ return B43_TXPWR_RES_DONE;
+}
+
+static void b43_phy_lcn_op_adjust_txpower(struct b43_wldev *dev)
+{
+}
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+static u16 b43_phy_lcn_op_read(struct b43_wldev *dev, u16 reg)
+{
+ b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
+ return b43_read16(dev, B43_MMIO_PHY_DATA);
+}
+
+static void b43_phy_lcn_op_write(struct b43_wldev *dev, u16 reg, u16 value)
+{
+ b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
+ b43_write16(dev, B43_MMIO_PHY_DATA, value);
+}
+
+static void b43_phy_lcn_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
+ u16 set)
+{
+ b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
+ b43_write16(dev, B43_MMIO_PHY_DATA,
+ (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
+}
+
+static u16 b43_phy_lcn_op_radio_read(struct b43_wldev *dev, u16 reg)
+{
+ /* LCN-PHY needs 0x200 for read access */
+ reg |= 0x200;
+
+ b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
+ return b43_read16(dev, B43_MMIO_RADIO24_DATA);
+}
+
+static void b43_phy_lcn_op_radio_write(struct b43_wldev *dev, u16 reg,
+ u16 value)
+{
+ b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
+ b43_write16(dev, B43_MMIO_RADIO24_DATA, value);
+}
+
+/**************************************************
+ * PHY ops struct.
+ **************************************************/
+
+const struct b43_phy_operations b43_phyops_lcn = {
+ .allocate = b43_phy_lcn_op_allocate,
+ .free = b43_phy_lcn_op_free,
+ .prepare_structs = b43_phy_lcn_op_prepare_structs,
+ .init = b43_phy_lcn_op_init,
+ .phy_read = b43_phy_lcn_op_read,
+ .phy_write = b43_phy_lcn_op_write,
+ .phy_maskset = b43_phy_lcn_op_maskset,
+ .radio_read = b43_phy_lcn_op_radio_read,
+ .radio_write = b43_phy_lcn_op_radio_write,
+ .software_rfkill = b43_phy_lcn_op_software_rfkill,
+ .switch_analog = b43_phy_lcn_op_switch_analog,
+ .switch_channel = b43_phy_lcn_op_switch_channel,
+ .get_default_chan = b43_phy_lcn_op_get_default_chan,
+ .recalc_txpower = b43_phy_lcn_op_recalc_txpower,
+ .adjust_txpower = b43_phy_lcn_op_adjust_txpower,
+};
diff --git a/drivers/net/wireless/b43/phy_lcn.h b/drivers/net/wireless/b43/phy_lcn.h
new file mode 100644
index 000000000000..6a7092e13fff
--- /dev/null
+++ b/drivers/net/wireless/b43/phy_lcn.h
@@ -0,0 +1,31 @@
+#ifndef B43_PHY_LCN_H_
+#define B43_PHY_LCN_H_
+
+#include "phy_common.h"
+
+
+#define B43_PHY_LCN_AFE_CTL1 B43_PHY_OFDM(0x03B)
+#define B43_PHY_LCN_AFE_CTL2 B43_PHY_OFDM(0x03C)
+#define B43_PHY_LCN_RF_CTL1 B43_PHY_OFDM(0x04C)
+#define B43_PHY_LCN_RF_CTL2 B43_PHY_OFDM(0x04D)
+#define B43_PHY_LCN_TABLE_ADDR B43_PHY_OFDM(0x055) /* Table address */
+#define B43_PHY_LCN_TABLE_DATALO B43_PHY_OFDM(0x056) /* Table data low */
+#define B43_PHY_LCN_TABLE_DATAHI B43_PHY_OFDM(0x057) /* Table data high */
+#define B43_PHY_LCN_RF_CTL3 B43_PHY_OFDM(0x0B0)
+#define B43_PHY_LCN_RF_CTL4 B43_PHY_OFDM(0x0B1)
+#define B43_PHY_LCN_RF_CTL5 B43_PHY_OFDM(0x0B7)
+#define B43_PHY_LCN_RF_CTL6 B43_PHY_OFDM(0x0F9)
+#define B43_PHY_LCN_RF_CTL7 B43_PHY_OFDM(0x0FA)
+
+
+struct b43_phy_lcn {
+ bool hw_pwr_ctl;
+ bool hw_pwr_ctl_capable;
+ u8 tx_pwr_curr_idx;
+};
+
+
+struct b43_phy_operations;
+extern const struct b43_phy_operations b43_phyops_lcn;
+
+#endif /* B43_PHY_LCN_H_ */
diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c
index 012c8da2f944..f93d66b1817b 100644
--- a/drivers/net/wireless/b43/phy_lp.c
+++ b/drivers/net/wireless/b43/phy_lp.c
@@ -3,7 +3,7 @@
Broadcom B43 wireless driver
IEEE 802.11a/g LP-PHY driver
- Copyright (c) 2008-2009 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2008-2009 Michael Buesch <m@bues.ch>
Copyright (c) 2009 Gábor Stefanik <netrolller.3d@gmail.com>
This program is free software; you can redistribute it and/or modify
@@ -85,39 +85,39 @@ static void b43_lpphy_op_free(struct b43_wldev *dev)
/* http://bcm-v4.sipsolutions.net/802.11/PHY/LP/ReadBandSrom */
static void lpphy_read_band_sprom(struct b43_wldev *dev)
{
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
struct b43_phy_lp *lpphy = dev->phy.lp;
- struct ssb_bus *bus = dev->sdev->bus;
u16 cckpo, maxpwr;
u32 ofdmpo;
int i;
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
- lpphy->tx_isolation_med_band = bus->sprom.tri2g;
- lpphy->bx_arch = bus->sprom.bxa2g;
- lpphy->rx_pwr_offset = bus->sprom.rxpo2g;
- lpphy->rssi_vf = bus->sprom.rssismf2g;
- lpphy->rssi_vc = bus->sprom.rssismc2g;
- lpphy->rssi_gs = bus->sprom.rssisav2g;
- lpphy->txpa[0] = bus->sprom.pa0b0;
- lpphy->txpa[1] = bus->sprom.pa0b1;
- lpphy->txpa[2] = bus->sprom.pa0b2;
- maxpwr = bus->sprom.maxpwr_bg;
+ lpphy->tx_isolation_med_band = sprom->tri2g;
+ lpphy->bx_arch = sprom->bxa2g;
+ lpphy->rx_pwr_offset = sprom->rxpo2g;
+ lpphy->rssi_vf = sprom->rssismf2g;
+ lpphy->rssi_vc = sprom->rssismc2g;
+ lpphy->rssi_gs = sprom->rssisav2g;
+ lpphy->txpa[0] = sprom->pa0b0;
+ lpphy->txpa[1] = sprom->pa0b1;
+ lpphy->txpa[2] = sprom->pa0b2;
+ maxpwr = sprom->maxpwr_bg;
lpphy->max_tx_pwr_med_band = maxpwr;
- cckpo = bus->sprom.cck2gpo;
+ cckpo = sprom->cck2gpo;
/*
* We don't read SPROM's opo as specs say. On rev8 SPROMs
* opo == ofdm2gpo and we don't know any SSB with LP-PHY
* and SPROM rev below 8.
*/
- B43_WARN_ON(bus->sprom.revision < 8);
- ofdmpo = bus->sprom.ofdm2gpo;
+ B43_WARN_ON(sprom->revision < 8);
+ ofdmpo = sprom->ofdm2gpo;
if (cckpo) {
for (i = 0; i < 4; i++) {
lpphy->tx_max_rate[i] =
maxpwr - (ofdmpo & 0xF) * 2;
ofdmpo >>= 4;
}
- ofdmpo = bus->sprom.ofdm2gpo;
+ ofdmpo = sprom->ofdm2gpo;
for (i = 4; i < 15; i++) {
lpphy->tx_max_rate[i] =
maxpwr - (ofdmpo & 0xF) * 2;
@@ -131,39 +131,39 @@ static void lpphy_read_band_sprom(struct b43_wldev *dev)
lpphy->tx_max_rate[i] = maxpwr - ofdmpo;
}
} else { /* 5GHz */
- lpphy->tx_isolation_low_band = bus->sprom.tri5gl;
- lpphy->tx_isolation_med_band = bus->sprom.tri5g;
- lpphy->tx_isolation_hi_band = bus->sprom.tri5gh;
- lpphy->bx_arch = bus->sprom.bxa5g;
- lpphy->rx_pwr_offset = bus->sprom.rxpo5g;
- lpphy->rssi_vf = bus->sprom.rssismf5g;
- lpphy->rssi_vc = bus->sprom.rssismc5g;
- lpphy->rssi_gs = bus->sprom.rssisav5g;
- lpphy->txpa[0] = bus->sprom.pa1b0;
- lpphy->txpa[1] = bus->sprom.pa1b1;
- lpphy->txpa[2] = bus->sprom.pa1b2;
- lpphy->txpal[0] = bus->sprom.pa1lob0;
- lpphy->txpal[1] = bus->sprom.pa1lob1;
- lpphy->txpal[2] = bus->sprom.pa1lob2;
- lpphy->txpah[0] = bus->sprom.pa1hib0;
- lpphy->txpah[1] = bus->sprom.pa1hib1;
- lpphy->txpah[2] = bus->sprom.pa1hib2;
- maxpwr = bus->sprom.maxpwr_al;
- ofdmpo = bus->sprom.ofdm5glpo;
+ lpphy->tx_isolation_low_band = sprom->tri5gl;
+ lpphy->tx_isolation_med_band = sprom->tri5g;
+ lpphy->tx_isolation_hi_band = sprom->tri5gh;
+ lpphy->bx_arch = sprom->bxa5g;
+ lpphy->rx_pwr_offset = sprom->rxpo5g;
+ lpphy->rssi_vf = sprom->rssismf5g;
+ lpphy->rssi_vc = sprom->rssismc5g;
+ lpphy->rssi_gs = sprom->rssisav5g;
+ lpphy->txpa[0] = sprom->pa1b0;
+ lpphy->txpa[1] = sprom->pa1b1;
+ lpphy->txpa[2] = sprom->pa1b2;
+ lpphy->txpal[0] = sprom->pa1lob0;
+ lpphy->txpal[1] = sprom->pa1lob1;
+ lpphy->txpal[2] = sprom->pa1lob2;
+ lpphy->txpah[0] = sprom->pa1hib0;
+ lpphy->txpah[1] = sprom->pa1hib1;
+ lpphy->txpah[2] = sprom->pa1hib2;
+ maxpwr = sprom->maxpwr_al;
+ ofdmpo = sprom->ofdm5glpo;
lpphy->max_tx_pwr_low_band = maxpwr;
for (i = 4; i < 12; i++) {
lpphy->tx_max_ratel[i] = maxpwr - (ofdmpo & 0xF) * 2;
ofdmpo >>= 4;
}
- maxpwr = bus->sprom.maxpwr_a;
- ofdmpo = bus->sprom.ofdm5gpo;
+ maxpwr = sprom->maxpwr_a;
+ ofdmpo = sprom->ofdm5gpo;
lpphy->max_tx_pwr_med_band = maxpwr;
for (i = 4; i < 12; i++) {
lpphy->tx_max_rate[i] = maxpwr - (ofdmpo & 0xF) * 2;
ofdmpo >>= 4;
}
- maxpwr = bus->sprom.maxpwr_ah;
- ofdmpo = bus->sprom.ofdm5ghpo;
+ maxpwr = sprom->maxpwr_ah;
+ ofdmpo = sprom->ofdm5ghpo;
lpphy->max_tx_pwr_hi_band = maxpwr;
for (i = 4; i < 12; i++) {
lpphy->tx_max_rateh[i] = maxpwr - (ofdmpo & 0xF) * 2;
@@ -214,7 +214,8 @@ static void lpphy_table_init(struct b43_wldev *dev)
static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_bus *bus = dev->dev->sdev->bus;
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
struct b43_phy_lp *lpphy = dev->phy.lp;
u16 tmp, tmp2;
@@ -242,9 +243,9 @@ static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00);
b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB,
0xFF00, lpphy->rx_pwr_offset);
- if ((bus->sprom.boardflags_lo & B43_BFL_FEM) &&
+ if ((sprom->boardflags_lo & B43_BFL_FEM) &&
((b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ||
- (bus->sprom.boardflags_hi & B43_BFH_PAREF))) {
+ (sprom->boardflags_hi & B43_BFH_PAREF))) {
ssb_pmu_set_ldo_voltage(&bus->chipco, LDO_PAREF, 0x28);
ssb_pmu_set_ldo_paref(&bus->chipco, true);
if (dev->phy.rev == 0) {
@@ -260,7 +261,7 @@ static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
}
tmp = lpphy->rssi_vf | lpphy->rssi_vc << 4 | 0xA000;
b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, tmp);
- if (bus->sprom.boardflags_hi & B43_BFH_RSSIINV)
+ if (sprom->boardflags_hi & B43_BFH_RSSIINV)
b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x0AAA);
else
b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x02AA);
@@ -268,7 +269,7 @@ static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_LPPHY_RX_RADIO_CTL,
0xFFF9, (lpphy->bx_arch << 1));
if (dev->phy.rev == 1 &&
- (bus->sprom.boardflags_hi & B43_BFH_FEM_BT)) {
+ (sprom->boardflags_hi & B43_BFH_FEM_BT)) {
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A);
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0x3F00, 0x0900);
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A);
@@ -286,8 +287,8 @@ static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xFFC0, 0x000A);
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xC0FF, 0x0B00);
} else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ ||
- (bus->boardinfo.type == 0x048A) || ((dev->phy.rev == 0) &&
- (bus->sprom.boardflags_lo & B43_BFL_FEM))) {
+ (dev->dev->board_type == 0x048A) || ((dev->phy.rev == 0) &&
+ (sprom->boardflags_lo & B43_BFL_FEM))) {
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0001);
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0400);
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0001);
@@ -297,7 +298,7 @@ static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002);
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0A00);
} else if (dev->phy.rev == 1 ||
- (bus->sprom.boardflags_lo & B43_BFL_FEM)) {
+ (sprom->boardflags_lo & B43_BFL_FEM)) {
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0004);
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0800);
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0004);
@@ -316,15 +317,15 @@ static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0006);
b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0700);
}
- if (dev->phy.rev == 1 && (bus->sprom.boardflags_hi & B43_BFH_PAREF)) {
+ if (dev->phy.rev == 1 && (sprom->boardflags_hi & B43_BFH_PAREF)) {
b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_5, B43_LPPHY_TR_LOOKUP_1);
b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_6, B43_LPPHY_TR_LOOKUP_2);
b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_7, B43_LPPHY_TR_LOOKUP_3);
b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_8, B43_LPPHY_TR_LOOKUP_4);
}
- if ((bus->sprom.boardflags_hi & B43_BFH_FEM_BT) &&
- (bus->chip_id == 0x5354) &&
- (bus->chip_package == SSB_CHIPPACK_BCM4712S)) {
+ if ((sprom->boardflags_hi & B43_BFH_FEM_BT) &&
+ (dev->dev->chip_id == 0x5354) &&
+ (dev->dev->chip_pkg == SSB_CHIPPACK_BCM4712S)) {
b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0006);
b43_phy_write(dev, B43_LPPHY_GPIO_SELECT, 0x0005);
b43_phy_write(dev, B43_LPPHY_GPIO_OUTEN, 0xFFFF);
@@ -412,7 +413,6 @@ static void lpphy_restore_dig_flt_state(struct b43_wldev *dev)
static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
struct b43_phy_lp *lpphy = dev->phy.lp;
b43_phy_write(dev, B43_LPPHY_AFE_DAC_CTL, 0x50);
@@ -432,7 +432,7 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x4000);
b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x2000);
b43_phy_set(dev, B43_PHY_OFDM(0x10A), 0x1);
- if (bus->boardinfo.rev >= 0x18) {
+ if (dev->dev->board_rev >= 0x18) {
b43_lptab_write(dev, B43_LPTAB32(17, 65), 0xEC);
b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x14);
} else {
@@ -449,7 +449,7 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0xA0);
b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xE0FF, 0x300);
b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2A00);
- if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) {
+ if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) {
b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100);
b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xA);
} else {
@@ -467,7 +467,7 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x12);
b43_phy_maskset(dev, B43_LPPHY_GAINMISMATCH, 0x0FFF, 0x9000);
- if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) {
+ if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) {
b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0);
b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40);
}
@@ -492,7 +492,7 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
0x2000 | ((u16)lpphy->rssi_gs << 10) |
((u16)lpphy->rssi_vc << 4) | lpphy->rssi_vf);
- if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) {
+ if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) {
b43_phy_set(dev, B43_LPPHY_AFE_ADC_CTL_0, 0x1C);
b43_phy_maskset(dev, B43_LPPHY_AFE_CTL, 0x00FF, 0x8800);
b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_1, 0xFC3C, 0x0400);
@@ -519,7 +519,7 @@ struct b2062_freqdata {
static void lpphy_2062_init(struct b43_wldev *dev)
{
struct b43_phy_lp *lpphy = dev->phy.lp;
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_bus *bus = dev->dev->sdev->bus;
u32 crystalfreq, tmp, ref;
unsigned int i;
const struct b2062_freqdata *fd = NULL;
@@ -697,7 +697,7 @@ static void lpphy_radio_init(struct b43_wldev *dev)
lpphy_sync_stx(dev);
b43_phy_write(dev, B43_PHY_OFDM(0xF0), 0x5F80);
b43_phy_write(dev, B43_PHY_OFDM(0xF1), 0);
- if (dev->sdev->bus->chip_id == 0x4325) {
+ if (dev->dev->chip_id == 0x4325) {
// TODO SSB PMU recalibration
}
}
@@ -1289,7 +1289,7 @@ finish:
static void lpphy_rev2plus_rc_calib(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_bus *bus = dev->dev->sdev->bus;
u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000;
u8 tmp = b43_radio_read(dev, B2063_RX_BB_SP8) & 0xFF;
int i;
@@ -1840,7 +1840,6 @@ static void lpphy_papd_cal(struct b43_wldev *dev, struct lpphy_tx_gains gains,
static void lpphy_papd_cal_txpwr(struct b43_wldev *dev)
{
struct b43_phy_lp *lpphy = dev->phy.lp;
- struct ssb_bus *bus = dev->sdev->bus;
struct lpphy_tx_gains gains, oldgains;
int old_txpctl, old_afe_ovr, old_rf, old_bbmult;
@@ -1854,7 +1853,7 @@ static void lpphy_papd_cal_txpwr(struct b43_wldev *dev)
lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
- if (bus->chip_id == 0x4325 && bus->chip_rev == 0)
+ if (dev->dev->chip_id == 0x4325 && dev->dev->chip_rev == 0)
lpphy_papd_cal(dev, gains, 0, 1, 30);
else
lpphy_papd_cal(dev, gains, 0, 1, 65);
@@ -1870,7 +1869,6 @@ static int lpphy_rx_iq_cal(struct b43_wldev *dev, bool noise, bool tx,
bool rx, bool pa, struct lpphy_tx_gains *gains)
{
struct b43_phy_lp *lpphy = dev->phy.lp;
- struct ssb_bus *bus = dev->sdev->bus;
const struct lpphy_rx_iq_comp *iqcomp = NULL;
struct lpphy_tx_gains nogains, oldgains;
u16 tmp;
@@ -1879,7 +1877,7 @@ static int lpphy_rx_iq_cal(struct b43_wldev *dev, bool noise, bool tx,
memset(&nogains, 0, sizeof(nogains));
memset(&oldgains, 0, sizeof(oldgains));
- if (bus->chip_id == 0x5354) {
+ if (dev->dev->chip_id == 0x5354) {
for (i = 0; i < ARRAY_SIZE(lpphy_5354_iq_table); i++) {
if (lpphy_5354_iq_table[i].chan == lpphy->channel) {
iqcomp = &lpphy_5354_iq_table[i];
@@ -2408,11 +2406,9 @@ static const struct b206x_channel b2063_chantbl[] = {
static void lpphy_b2062_reset_pll_bias(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
-
b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0xFF);
udelay(20);
- if (bus->chip_id == 0x5354) {
+ if (dev->dev->chip_id == 0x5354) {
b43_radio_write(dev, B2062_N_COMM1, 4);
b43_radio_write(dev, B2062_S_RFPLL_CTL2, 4);
} else {
@@ -2432,7 +2428,7 @@ static int lpphy_b2062_tune(struct b43_wldev *dev,
unsigned int channel)
{
struct b43_phy_lp *lpphy = dev->phy.lp;
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_bus *bus = dev->dev->sdev->bus;
const struct b206x_channel *chandata = NULL;
u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000;
u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9;
@@ -2522,7 +2518,7 @@ static void lpphy_b2063_vco_calib(struct b43_wldev *dev)
static int lpphy_b2063_tune(struct b43_wldev *dev,
unsigned int channel)
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_bus *bus = dev->dev->sdev->bus;
static const struct b206x_channel *chandata = NULL;
u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000;
@@ -2670,6 +2666,11 @@ static int b43_lpphy_op_init(struct b43_wldev *dev)
{
int err;
+ if (dev->dev->bus_type != B43_BUS_SSB) {
+ b43err(dev->wl, "LP-PHY is supported only on SSB!\n");
+ return -EOPNOTSUPP;
+ }
+
lpphy_read_band_sprom(dev); //FIXME should this be in prepare_structs?
lpphy_baseband_init(dev);
lpphy_radio_init(dev);
diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c
index 05960ddde24e..b17d9b6c33a5 100644
--- a/drivers/net/wireless/b43/phy_n.c
+++ b/drivers/net/wireless/b43/phy_n.c
@@ -3,7 +3,8 @@
Broadcom B43 wireless driver
IEEE 802.11n PHY support
- Copyright (c) 2008 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2008 Michael Buesch <m@bues.ch>
+ Copyright (c) 2010-2011 Rafał Miłecki <zajec5@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -77,6 +78,7 @@ enum b43_nphy_rssi_type {
B43_NPHY_RSSI_TBD,
};
+/* TODO: reorder functions */
static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev,
bool enable);
static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd,
@@ -87,6 +89,14 @@ static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field,
u16 value, u8 core, bool off);
static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field,
u16 value, u8 core);
+static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev);
+
+static inline bool b43_nphy_ipa(struct b43_wldev *dev)
+{
+ enum ieee80211_band band = b43_current_band(dev->wl);
+ return ((dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) ||
+ (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ));
+}
void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
{//TODO
@@ -248,15 +258,25 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
{
struct b43_phy_n *nphy = dev->phy.n;
u8 i;
- u16 tmp;
+ u16 bmask, val, tmp;
+ enum ieee80211_band band = b43_current_band(dev->wl);
if (nphy->hang_avoid)
b43_nphy_stay_in_carrier_search(dev, 1);
nphy->txpwrctrl = enable;
if (!enable) {
- if (dev->phy.rev >= 3)
- ; /* TODO */
+ if (dev->phy.rev >= 3 &&
+ (b43_phy_read(dev, B43_NPHY_TXPCTL_CMD) &
+ (B43_NPHY_TXPCTL_CMD_COEFF |
+ B43_NPHY_TXPCTL_CMD_HWPCTLEN |
+ B43_NPHY_TXPCTL_CMD_PCTLEN))) {
+ /* We disable enabled TX pwr ctl, save it's state */
+ nphy->tx_pwr_idx[0] = b43_phy_read(dev,
+ B43_NPHY_C1_TXPCTL_STAT) & 0x7f;
+ nphy->tx_pwr_idx[1] = b43_phy_read(dev,
+ B43_NPHY_C2_TXPCTL_STAT) & 0x7f;
+ }
b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x6840);
for (i = 0; i < 84; i++)
@@ -285,10 +305,67 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3,
~B43_NPHY_BPHY_CTL3_SCALE, 0x5A);
- if (dev->phy.rev < 2 && 0)
- ; /* TODO */
+ if (dev->phy.rev < 2 && dev->phy.is_40mhz)
+ b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW);
} else {
- b43err(dev->wl, "enabling tx pwr ctrl not implemented yet\n");
+ b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84,
+ nphy->adj_pwr_tbl);
+ b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84,
+ nphy->adj_pwr_tbl);
+
+ bmask = B43_NPHY_TXPCTL_CMD_COEFF |
+ B43_NPHY_TXPCTL_CMD_HWPCTLEN;
+ /* wl does useless check for "enable" param here */
+ val = B43_NPHY_TXPCTL_CMD_COEFF | B43_NPHY_TXPCTL_CMD_HWPCTLEN;
+ if (dev->phy.rev >= 3) {
+ bmask |= B43_NPHY_TXPCTL_CMD_PCTLEN;
+ if (val)
+ val |= B43_NPHY_TXPCTL_CMD_PCTLEN;
+ }
+ b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~(bmask), val);
+
+ if (band == IEEE80211_BAND_5GHZ) {
+ b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
+ ~B43_NPHY_TXPCTL_CMD_INIT, 0x64);
+ if (dev->phy.rev > 1)
+ b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT,
+ ~B43_NPHY_TXPCTL_INIT_PIDXI1,
+ 0x64);
+ }
+
+ if (dev->phy.rev >= 3) {
+ if (nphy->tx_pwr_idx[0] != 128 &&
+ nphy->tx_pwr_idx[1] != 128) {
+ /* Recover TX pwr ctl state */
+ b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
+ ~B43_NPHY_TXPCTL_CMD_INIT,
+ nphy->tx_pwr_idx[0]);
+ if (dev->phy.rev > 1)
+ b43_phy_maskset(dev,
+ B43_NPHY_TXPCTL_INIT,
+ ~0xff, nphy->tx_pwr_idx[1]);
+ }
+ }
+
+ if (dev->phy.rev >= 3) {
+ b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x100);
+ b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x100);
+ } else {
+ b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x4000);
+ }
+
+ if (dev->phy.rev == 2)
+ b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x3b);
+ else if (dev->phy.rev < 2)
+ b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x40);
+
+ if (dev->phy.rev < 2 && dev->phy.is_40mhz)
+ b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_TSSIRPSMW);
+
+ if (b43_nphy_ipa(dev)) {
+ b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x4);
+ b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x4);
+ }
}
if (nphy->hang_avoid)
@@ -299,7 +376,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
{
struct b43_phy_n *nphy = dev->phy.n;
- struct ssb_sprom *sprom = &(dev->sdev->bus->sprom);
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
u8 txpi[2], bbmult, i;
u16 tmp, radio_gain, dac_gain;
@@ -369,22 +446,23 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
else
b43_phy_write(dev, B43_NPHY_AFECTL_DACGAIN2, dac_gain);
- b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D10 + i);
- b43_phy_write(dev, B43_NPHY_TABLE_DATALO, radio_gain);
-
- b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x3C57);
- tmp = b43_phy_read(dev, B43_NPHY_TABLE_DATALO);
+ b43_ntab_write(dev, B43_NTAB16(0x7, 0x110 + i), radio_gain);
+ tmp = b43_ntab_read(dev, B43_NTAB16(0xF, 0x57));
if (i == 0)
tmp = (tmp & 0x00FF) | (bbmult << 8);
else
tmp = (tmp & 0xFF00) | bbmult;
-
- b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x3C57);
- b43_phy_write(dev, B43_NPHY_TABLE_DATALO, tmp);
-
- if (0)
- ; /* TODO */
+ b43_ntab_write(dev, B43_NTAB16(0xF, 0x57), tmp);
+
+ if (b43_nphy_ipa(dev)) {
+ u32 tmp32;
+ u16 reg = (i == 0) ?
+ B43_NPHY_PAPD_EN0 : B43_NPHY_PAPD_EN1;
+ tmp32 = b43_ntab_read(dev, B43_NTAB32(26 + i, txpi[i]));
+ b43_phy_maskset(dev, reg, 0xE00F, (u32) tmp32 << 4);
+ b43_phy_set(dev, reg, 0x4);
+ }
}
b43_phy_mask(dev, B43_NPHY_BPHY_CTL2, ~B43_NPHY_BPHY_CTL2_LUT);
@@ -393,6 +471,57 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
b43_nphy_stay_in_carrier_search(dev, 0);
}
+static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev)
+{
+ struct b43_phy *phy = &dev->phy;
+
+ const u32 *table = NULL;
+#if 0
+ TODO: b43_ntab_papd_pga_gain_delta_ipa_2*
+ u32 rfpwr_offset;
+ u8 pga_gain;
+ int i;
+#endif
+
+ if (phy->rev >= 3) {
+ if (b43_nphy_ipa(dev)) {
+ table = b43_nphy_get_ipa_gain_table(dev);
+ } else {
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+ if (phy->rev == 3)
+ table = b43_ntab_tx_gain_rev3_5ghz;
+ if (phy->rev == 4)
+ table = b43_ntab_tx_gain_rev4_5ghz;
+ else
+ table = b43_ntab_tx_gain_rev5plus_5ghz;
+ } else {
+ table = b43_ntab_tx_gain_rev3plus_2ghz;
+ }
+ }
+ } else {
+ table = b43_ntab_tx_gain_rev0_1_2;
+ }
+ b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table);
+ b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table);
+
+ if (phy->rev >= 3) {
+#if 0
+ nphy->gmval = (table[0] >> 16) & 0x7000;
+
+ for (i = 0; i < 128; i++) {
+ pga_gain = (table[i] >> 24) & 0xF;
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+ rfpwr_offset = b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain];
+ else
+ rfpwr_offset = b43_ntab_papd_pga_gain_delta_ipa_5g[pga_gain];
+ b43_ntab_write(dev, B43_NTAB32(26, 576 + i),
+ rfpwr_offset);
+ b43_ntab_write(dev, B43_NTAB32(27, 576 + i),
+ rfpwr_offset);
+ }
+#endif
+ }
+}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/Radio/2055Setup */
static void b43_radio_2055_setup(struct b43_wldev *dev,
@@ -423,16 +552,15 @@ static void b43_radio_init2055_pre(struct b43_wldev *dev)
static void b43_radio_init2055_post(struct b43_wldev *dev)
{
struct b43_phy_n *nphy = dev->phy.n;
- struct ssb_sprom *sprom = &(dev->sdev->bus->sprom);
- struct ssb_boardinfo *binfo = &(dev->sdev->bus->boardinfo);
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
int i;
u16 val;
bool workaround = false;
if (sprom->revision < 4)
- workaround = (binfo->vendor != PCI_VENDOR_ID_BROADCOM &&
- binfo->type == 0x46D &&
- binfo->rev >= 0x41);
+ workaround = (dev->dev->board_vendor != PCI_VENDOR_ID_BROADCOM
+ && dev->dev->board_type == 0x46D
+ && dev->dev->board_rev >= 0x41);
else
workaround =
!(sprom->boardflags2_lo & B43_BFL2_RXBB_INT_REG_DIS);
@@ -582,14 +710,10 @@ static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable)
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw */
static void b43_nphy_tx_lp_fbw(struct b43_wldev *dev)
{
- struct b43_phy_n *nphy = dev->phy.n;
u16 tmp;
- enum ieee80211_band band = b43_current_band(dev->wl);
- bool ipa = (nphy->ipa2g_on && band == IEEE80211_BAND_2GHZ) ||
- (nphy->ipa5g_on && band == IEEE80211_BAND_5GHZ);
if (dev->phy.rev >= 3) {
- if (ipa) {
+ if (b43_nphy_ipa(dev)) {
tmp = 4;
b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2,
(((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp);
@@ -601,33 +725,17 @@ static void b43_nphy_tx_lp_fbw(struct b43_wldev *dev)
}
}
-/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */
-static void b43_nphy_bmac_clock_fgc(struct b43_wldev *dev, bool force)
-{
- u32 tmslow;
-
- if (dev->phy.type != B43_PHYTYPE_N)
- return;
-
- tmslow = ssb_read32(dev->sdev, SSB_TMSLOW);
- if (force)
- tmslow |= SSB_TMSLOW_FGC;
- else
- tmslow &= ~SSB_TMSLOW_FGC;
- ssb_write32(dev->sdev, SSB_TMSLOW, tmslow);
-}
-
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CCA */
static void b43_nphy_reset_cca(struct b43_wldev *dev)
{
u16 bbcfg;
- b43_nphy_bmac_clock_fgc(dev, 1);
+ b43_phy_force_clock(dev, 1);
bbcfg = b43_phy_read(dev, B43_NPHY_BBCFG);
b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg | B43_NPHY_BBCFG_RSTCCA);
udelay(1);
b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg & ~B43_NPHY_BBCFG_RSTCCA);
- b43_nphy_bmac_clock_fgc(dev, 0);
+ b43_phy_force_clock(dev, 0);
b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
}
@@ -916,11 +1024,7 @@ static void b43_nphy_calc_rx_iq_comp(struct b43_wldev *dev, u8 mask)
static void b43_nphy_tx_iq_workaround(struct b43_wldev *dev)
{
u16 array[4];
- int i;
-
- b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x3C50);
- for (i = 0; i < 4; i++)
- array[i] = b43_phy_read(dev, B43_NPHY_TABLE_DATALO);
+ b43_ntab_read_bulk(dev, B43_NTAB16(0xF, 0x50), 4, array);
b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW0, array[0]);
b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW1, array[1]);
@@ -959,8 +1063,21 @@ static void b43_nphy_superswitch_init(struct b43_wldev *dev, bool init)
b43_phy_write(dev, B43_NPHY_GPIO_LOOEN, 0);
b43_phy_write(dev, B43_NPHY_GPIO_HIOEN, 0);
- ssb_chipco_gpio_control(&dev->sdev->bus->chipco, 0xFC00,
- 0xFC00);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ bcma_chipco_gpio_control(&dev->dev->bdev->bus->drv_cc,
+ 0xFC00, 0xFC00);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ ssb_chipco_gpio_control(&dev->dev->sdev->bus->chipco,
+ 0xFC00, 0xFC00);
+ break;
+#endif
+ }
+
b43_write32(dev, B43_MMIO_MACCTL,
b43_read32(dev, B43_MMIO_MACCTL) &
~B43_MACCTL_GPOUTSMSK);
@@ -983,7 +1100,7 @@ static u16 b43_nphy_classifier(struct b43_wldev *dev, u16 mask, u16 val)
{
u16 tmp;
- if (dev->sdev->id.revision == 16)
+ if (dev->dev->core_rev == 16)
b43_mac_suspend(dev);
tmp = b43_phy_read(dev, B43_NPHY_CLASSCTL);
@@ -993,7 +1110,7 @@ static u16 b43_nphy_classifier(struct b43_wldev *dev, u16 mask, u16 val)
tmp |= (val & mask);
b43_phy_maskset(dev, B43_NPHY_CLASSCTL, 0xFFF8, tmp);
- if (dev->sdev->id.revision == 16)
+ if (dev->dev->core_rev == 16)
b43_mac_enable(dev);
return tmp;
@@ -1168,7 +1285,7 @@ static void b43_nphy_adjust_lna_gain_table(struct b43_wldev *dev)
static void b43_nphy_gain_ctrl_workarounds(struct b43_wldev *dev)
{
struct b43_phy_n *nphy = dev->phy.n;
- struct ssb_sprom *sprom = &(dev->sdev->bus->sprom);
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
/* PHY rev 0, 1, 2 */
u8 i, j;
@@ -1370,181 +1487,221 @@ static void b43_nphy_gain_ctrl_workarounds(struct b43_wldev *dev)
}
}
-/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Workarounds */
-static void b43_nphy_workarounds(struct b43_wldev *dev)
+static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
- struct b43_phy *phy = &dev->phy;
- struct b43_phy_n *nphy = phy->n;
-
- u8 events1[7] = { 0x0, 0x1, 0x2, 0x8, 0x4, 0x5, 0x3 };
- u8 delays1[7] = { 0x8, 0x6, 0x6, 0x2, 0x4, 0x3C, 0x1 };
-
- u8 events2[7] = { 0x0, 0x3, 0x5, 0x4, 0x2, 0x1, 0x8 };
- u8 delays2[7] = { 0x8, 0x6, 0x2, 0x4, 0x4, 0x6, 0x1 };
+ struct b43_phy_n *nphy = dev->phy.n;
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
+
+ /* TX to RX */
+ u8 tx2rx_events[9] = { 0x4, 0x3, 0x6, 0x5, 0x2, 0x1, 0x8, 0x1F };
+ u8 tx2rx_delays[9] = { 8, 4, 2, 2, 4, 4, 6, 1 };
+ /* RX to TX */
+ u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3,
+ 0x1F };
+ u8 rx2tx_delays_ipa[9] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 };
+ u8 rx2tx_events[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0x3, 0x4, 0x1F };
+ u8 rx2tx_delays[9] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 };
u16 tmp16;
u32 tmp32;
- if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
- b43_nphy_classifier(dev, 1, 0);
- else
- b43_nphy_classifier(dev, 1, 1);
+ tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0));
+ tmp32 &= 0xffffff;
+ b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32);
+
+ b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125);
+ b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01B3);
+ b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105);
+ b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016E);
+ b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00CD);
+ b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020);
+
+ b43_phy_write(dev, B43_NPHY_C2_CLIP1_MEDGAIN, 0x000C);
+ b43_phy_write(dev, 0x2AE, 0x000C);
+
+ /* TX to RX */
+ b43_nphy_set_rf_sequence(dev, 1, tx2rx_events, tx2rx_delays, 9);
+
+ /* RX to TX */
+ if (b43_nphy_ipa(dev))
+ b43_nphy_set_rf_sequence(dev, 1, rx2tx_events_ipa,
+ rx2tx_delays_ipa, 9);
+ if (nphy->hw_phyrxchain != 3 &&
+ nphy->hw_phyrxchain != nphy->hw_phytxchain) {
+ if (b43_nphy_ipa(dev)) {
+ rx2tx_delays[5] = 59;
+ rx2tx_delays[6] = 1;
+ rx2tx_events[7] = 0x1F;
+ }
+ b43_nphy_set_rf_sequence(dev, 1, rx2tx_events, rx2tx_delays, 9);
+ }
- if (nphy->hang_avoid)
- b43_nphy_stay_in_carrier_search(dev, 1);
+ tmp16 = (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) ?
+ 0x2 : 0x9C40;
+ b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, tmp16);
- b43_phy_set(dev, B43_NPHY_IQFLIP,
- B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2);
+ b43_phy_maskset(dev, 0x294, 0xF0FF, 0x0700);
- if (dev->phy.rev >= 3) {
- tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0));
- tmp32 &= 0xffffff;
- b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32);
+ b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D);
+ b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D);
- b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125);
- b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01B3);
- b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105);
- b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016E);
- b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00CD);
- b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020);
+ b43_nphy_gain_ctrl_workarounds(dev);
- b43_phy_write(dev, B43_NPHY_C2_CLIP1_MEDGAIN, 0x000C);
- b43_phy_write(dev, 0x2AE, 0x000C);
+ b43_ntab_write(dev, B43_NTAB32(8, 0), 2);
+ b43_ntab_write(dev, B43_NTAB32(8, 16), 2);
- /* TODO */
-
- tmp16 = (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) ?
- 0x2 : 0x9C40;
- b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, tmp16);
+ /* TODO */
- b43_phy_maskset(dev, 0x294, 0xF0FF, 0x0700);
+ b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_MAST_BIAS, 0x00);
+ b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_MAST_BIAS, 0x00);
+ b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_MAIN, 0x06);
+ b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_MAIN, 0x06);
+ b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_AUX, 0x07);
+ b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_AUX, 0x07);
+ b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_LOB_BIAS, 0x88);
+ b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_LOB_BIAS, 0x88);
+ b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXG_CMFB_IDAC, 0x00);
+ b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXG_CMFB_IDAC, 0x00);
+
+ /* N PHY WAR TX Chain Update with hw_phytxchain as argument */
+
+ if ((sprom->boardflags2_lo & B43_BFL2_APLL_WAR &&
+ b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ||
+ (sprom->boardflags2_lo & B43_BFL2_GPLL_WAR &&
+ b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ))
+ tmp32 = 0x00088888;
+ else
+ tmp32 = 0x88888888;
+ b43_ntab_write(dev, B43_NTAB32(30, 1), tmp32);
+ b43_ntab_write(dev, B43_NTAB32(30, 2), tmp32);
+ b43_ntab_write(dev, B43_NTAB32(30, 3), tmp32);
+
+ if (dev->phy.rev == 4 &&
+ b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+ b43_radio_write(dev, B2056_TX0 | B2056_TX_GMBB_IDAC,
+ 0x70);
+ b43_radio_write(dev, B2056_TX1 | B2056_TX_GMBB_IDAC,
+ 0x70);
+ }
- b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D);
- b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D);
+ b43_phy_write(dev, 0x224, 0x039C);
+ b43_phy_write(dev, 0x225, 0x0357);
+ b43_phy_write(dev, 0x226, 0x0317);
+ b43_phy_write(dev, 0x227, 0x02D7);
+ b43_phy_write(dev, 0x228, 0x039C);
+ b43_phy_write(dev, 0x229, 0x0357);
+ b43_phy_write(dev, 0x22A, 0x0317);
+ b43_phy_write(dev, 0x22B, 0x02D7);
+ b43_phy_write(dev, 0x22C, 0x039C);
+ b43_phy_write(dev, 0x22D, 0x0357);
+ b43_phy_write(dev, 0x22E, 0x0317);
+ b43_phy_write(dev, 0x22F, 0x02D7);
+}
- b43_nphy_gain_ctrl_workarounds(dev);
+static void b43_nphy_workarounds_rev1_2(struct b43_wldev *dev)
+{
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
+ struct b43_phy *phy = &dev->phy;
+ struct b43_phy_n *nphy = phy->n;
- b43_ntab_write(dev, B43_NTAB32(8, 0), 2);
- b43_ntab_write(dev, B43_NTAB32(8, 16), 2);
+ u8 events1[7] = { 0x0, 0x1, 0x2, 0x8, 0x4, 0x5, 0x3 };
+ u8 delays1[7] = { 0x8, 0x6, 0x6, 0x2, 0x4, 0x3C, 0x1 };
- /* TODO */
+ u8 events2[7] = { 0x0, 0x3, 0x5, 0x4, 0x2, 0x1, 0x8 };
+ u8 delays2[7] = { 0x8, 0x6, 0x2, 0x4, 0x4, 0x6, 0x1 };
- b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_MAST_BIAS, 0x00);
- b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_MAST_BIAS, 0x00);
- b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_MAIN, 0x06);
- b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_MAIN, 0x06);
- b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_AUX, 0x07);
- b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_AUX, 0x07);
- b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_LOB_BIAS, 0x88);
- b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_LOB_BIAS, 0x88);
- b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXG_CMFB_IDAC, 0x00);
- b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXG_CMFB_IDAC, 0x00);
-
- /* N PHY WAR TX Chain Update with hw_phytxchain as argument */
-
- if ((bus->sprom.boardflags2_lo & B43_BFL2_APLL_WAR &&
- b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ||
- (bus->sprom.boardflags2_lo & B43_BFL2_GPLL_WAR &&
- b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ))
- tmp32 = 0x00088888;
- else
- tmp32 = 0x88888888;
- b43_ntab_write(dev, B43_NTAB32(30, 1), tmp32);
- b43_ntab_write(dev, B43_NTAB32(30, 2), tmp32);
- b43_ntab_write(dev, B43_NTAB32(30, 3), tmp32);
-
- if (dev->phy.rev == 4 &&
- b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
- b43_radio_write(dev, B2056_TX0 | B2056_TX_GMBB_IDAC,
- 0x70);
- b43_radio_write(dev, B2056_TX1 | B2056_TX_GMBB_IDAC,
- 0x70);
- }
-
- b43_phy_write(dev, 0x224, 0x039C);
- b43_phy_write(dev, 0x225, 0x0357);
- b43_phy_write(dev, 0x226, 0x0317);
- b43_phy_write(dev, 0x227, 0x02D7);
- b43_phy_write(dev, 0x228, 0x039C);
- b43_phy_write(dev, 0x229, 0x0357);
- b43_phy_write(dev, 0x22A, 0x0317);
- b43_phy_write(dev, 0x22B, 0x02D7);
- b43_phy_write(dev, 0x22C, 0x039C);
- b43_phy_write(dev, 0x22D, 0x0357);
- b43_phy_write(dev, 0x22E, 0x0317);
- b43_phy_write(dev, 0x22F, 0x02D7);
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ &&
+ nphy->band5g_pwrgain) {
+ b43_radio_mask(dev, B2055_C1_TX_RF_SPARE, ~0x8);
+ b43_radio_mask(dev, B2055_C2_TX_RF_SPARE, ~0x8);
} else {
- if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ &&
- nphy->band5g_pwrgain) {
- b43_radio_mask(dev, B2055_C1_TX_RF_SPARE, ~0x8);
- b43_radio_mask(dev, B2055_C2_TX_RF_SPARE, ~0x8);
- } else {
- b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8);
- b43_radio_set(dev, B2055_C2_TX_RF_SPARE, 0x8);
- }
-
- b43_ntab_write(dev, B43_NTAB16(8, 0x00), 0x000A);
- b43_ntab_write(dev, B43_NTAB16(8, 0x10), 0x000A);
- b43_ntab_write(dev, B43_NTAB16(8, 0x02), 0xCDAA);
- b43_ntab_write(dev, B43_NTAB16(8, 0x12), 0xCDAA);
+ b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8);
+ b43_radio_set(dev, B2055_C2_TX_RF_SPARE, 0x8);
+ }
- if (dev->phy.rev < 2) {
- b43_ntab_write(dev, B43_NTAB16(8, 0x08), 0x0000);
- b43_ntab_write(dev, B43_NTAB16(8, 0x18), 0x0000);
- b43_ntab_write(dev, B43_NTAB16(8, 0x07), 0x7AAB);
- b43_ntab_write(dev, B43_NTAB16(8, 0x17), 0x7AAB);
- b43_ntab_write(dev, B43_NTAB16(8, 0x06), 0x0800);
- b43_ntab_write(dev, B43_NTAB16(8, 0x16), 0x0800);
- }
+ b43_ntab_write(dev, B43_NTAB16(8, 0x00), 0x000A);
+ b43_ntab_write(dev, B43_NTAB16(8, 0x10), 0x000A);
+ b43_ntab_write(dev, B43_NTAB16(8, 0x02), 0xCDAA);
+ b43_ntab_write(dev, B43_NTAB16(8, 0x12), 0xCDAA);
- b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8);
- b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301);
- b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8);
- b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301);
+ if (dev->phy.rev < 2) {
+ b43_ntab_write(dev, B43_NTAB16(8, 0x08), 0x0000);
+ b43_ntab_write(dev, B43_NTAB16(8, 0x18), 0x0000);
+ b43_ntab_write(dev, B43_NTAB16(8, 0x07), 0x7AAB);
+ b43_ntab_write(dev, B43_NTAB16(8, 0x17), 0x7AAB);
+ b43_ntab_write(dev, B43_NTAB16(8, 0x06), 0x0800);
+ b43_ntab_write(dev, B43_NTAB16(8, 0x16), 0x0800);
+ }
- if (bus->sprom.boardflags2_lo & 0x100 &&
- bus->boardinfo.type == 0x8B) {
- delays1[0] = 0x1;
- delays1[5] = 0x14;
- }
- b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7);
- b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7);
+ b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8);
+ b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301);
+ b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8);
+ b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301);
- b43_nphy_gain_ctrl_workarounds(dev);
+ if (sprom->boardflags2_lo & B43_BFL2_SKWRKFEM_BRD &&
+ dev->dev->board_type == 0x8B) {
+ delays1[0] = 0x1;
+ delays1[5] = 0x14;
+ }
+ b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7);
+ b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7);
- if (dev->phy.rev < 2) {
- if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2)
- b43_hf_write(dev, b43_hf_read(dev) |
- B43_HF_MLADVW);
- } else if (dev->phy.rev == 2) {
- b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0);
- b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0);
- }
-
- if (dev->phy.rev < 2)
- b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL,
- ~B43_NPHY_SCRAM_SIGCTL_SCM);
-
- /* Set phase track alpha and beta */
- b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125);
- b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3);
- b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105);
- b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E);
- b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD);
- b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20);
-
- b43_phy_mask(dev, B43_NPHY_PIL_DW1,
- ~B43_NPHY_PIL_DW_64QAM & 0xFFFF);
- b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B1, 0xB5);
- b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B2, 0xA4);
- b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B3, 0x00);
+ b43_nphy_gain_ctrl_workarounds(dev);
- if (dev->phy.rev == 2)
- b43_phy_set(dev, B43_NPHY_FINERX2_CGC,
- B43_NPHY_FINERX2_CGC_DECGC);
+ if (dev->phy.rev < 2) {
+ if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2)
+ b43_hf_write(dev, b43_hf_read(dev) |
+ B43_HF_MLADVW);
+ } else if (dev->phy.rev == 2) {
+ b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0);
+ b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0);
}
+ if (dev->phy.rev < 2)
+ b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL,
+ ~B43_NPHY_SCRAM_SIGCTL_SCM);
+
+ /* Set phase track alpha and beta */
+ b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125);
+ b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3);
+ b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105);
+ b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E);
+ b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD);
+ b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20);
+
+ b43_phy_mask(dev, B43_NPHY_PIL_DW1,
+ ~B43_NPHY_PIL_DW_64QAM & 0xFFFF);
+ b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B1, 0xB5);
+ b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B2, 0xA4);
+ b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B3, 0x00);
+
+ if (dev->phy.rev == 2)
+ b43_phy_set(dev, B43_NPHY_FINERX2_CGC,
+ B43_NPHY_FINERX2_CGC_DECGC);
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Workarounds */
+static void b43_nphy_workarounds(struct b43_wldev *dev)
+{
+ struct b43_phy *phy = &dev->phy;
+ struct b43_phy_n *nphy = phy->n;
+
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
+ b43_nphy_classifier(dev, 1, 0);
+ else
+ b43_nphy_classifier(dev, 1, 1);
+
+ if (nphy->hang_avoid)
+ b43_nphy_stay_in_carrier_search(dev, 1);
+
+ b43_phy_set(dev, B43_NPHY_IQFLIP,
+ B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2);
+
+ if (dev->phy.rev >= 3)
+ b43_nphy_workarounds_rev3plus(dev);
+ else
+ b43_nphy_workarounds_rev1_2(dev);
+
if (nphy->hang_avoid)
b43_nphy_stay_in_carrier_search(dev, 0);
}
@@ -2139,7 +2296,6 @@ static void b43_nphy_rev2_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
{
- struct b43_phy_n *nphy = dev->phy.n;
u8 i;
u16 reg, val;
@@ -2203,10 +2359,7 @@ static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
enum ieee80211_band band =
b43_current_band(dev->wl);
- if ((nphy->ipa2g_on &&
- band == IEEE80211_BAND_2GHZ) ||
- (nphy->ipa5g_on &&
- band == IEEE80211_BAND_5GHZ))
+ if (b43_nphy_ipa(dev))
val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE;
else
val = 0x11;
@@ -2581,8 +2734,8 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev)
{
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
if (dev->phy.rev >= 6) {
- /* TODO If the chip is 47162
- return txpwrctrl_tx_gain_ipa_rev5 */
+ if (dev->dev->chip_id == 47162)
+ return txpwrctrl_tx_gain_ipa_rev5;
return txpwrctrl_tx_gain_ipa_rev6;
} else if (dev->phy.rev >= 5) {
return txpwrctrl_tx_gain_ipa_rev5;
@@ -2832,10 +2985,7 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
enum ieee80211_band band =
b43_current_band(dev->wl);
- if ((nphy->ipa2g_on &&
- band == IEEE80211_BAND_2GHZ) ||
- (nphy->ipa5g_on &&
- band == IEEE80211_BAND_5GHZ)) {
+ if (b43_nphy_ipa(dev)) {
table = b43_nphy_get_ipa_gain_table(dev);
} else {
if (band == IEEE80211_BAND_5GHZ) {
@@ -3586,7 +3736,7 @@ static void b43_nphy_set_rx_core_state(struct b43_wldev *dev, u8 mask)
*/
int b43_phy_initn(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
struct b43_phy *phy = &dev->phy;
struct b43_phy_n *nphy = phy->n;
u8 tx_pwr_state;
@@ -3599,9 +3749,22 @@ int b43_phy_initn(struct b43_wldev *dev)
bool do_cal = false;
if ((dev->phy.rev >= 3) &&
- (bus->sprom.boardflags_lo & B43_BFL_EXTLNA) &&
+ (sprom->boardflags_lo & B43_BFL_EXTLNA) &&
(b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) {
- chipco_set32(&dev->sdev->bus->chipco, SSB_CHIPCO_CHIPCTL, 0x40);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ bcma_cc_set32(&dev->dev->bdev->bus->drv_cc,
+ BCMA_CC_CHIPCTL, 0x40);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ chipco_set32(&dev->dev->sdev->bus->chipco,
+ SSB_CHIPCO_CHIPCTL, 0x40);
+ break;
+#endif
+ }
}
nphy->deaf_count = 0;
b43_nphy_tables_init(dev);
@@ -3639,9 +3802,9 @@ int b43_phy_initn(struct b43_wldev *dev)
b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_20M, 0x20);
b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_40M, 0x20);
- if (bus->sprom.boardflags2_lo & 0x100 ||
- (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE &&
- bus->boardinfo.type == 0x8B))
+ if (sprom->boardflags2_lo & B43_BFL2_SKWRKFEM_BRD ||
+ (dev->dev->board_vendor == PCI_VENDOR_ID_APPLE &&
+ dev->dev->board_type == 0x8B))
b43_phy_write(dev, B43_NPHY_TXREALFD, 0xA0);
else
b43_phy_write(dev, B43_NPHY_TXREALFD, 0xB8);
@@ -3658,8 +3821,7 @@ int b43_phy_initn(struct b43_wldev *dev)
}
tmp2 = b43_current_band(dev->wl);
- if ((nphy->ipa2g_on && tmp2 == IEEE80211_BAND_2GHZ) ||
- (nphy->ipa5g_on && tmp2 == IEEE80211_BAND_5GHZ)) {
+ if (b43_nphy_ipa(dev)) {
b43_phy_set(dev, B43_NPHY_PAPD_EN0, 0x1);
b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ0, 0x007F,
nphy->papd_epsilon_offset[0] << 7);
@@ -3674,11 +3836,11 @@ int b43_phy_initn(struct b43_wldev *dev)
b43_nphy_workarounds(dev);
/* Reset CCA, in init code it differs a little from standard way */
- b43_nphy_bmac_clock_fgc(dev, 1);
+ b43_phy_force_clock(dev, 1);
tmp = b43_phy_read(dev, B43_NPHY_BBCFG);
b43_phy_write(dev, B43_NPHY_BBCFG, tmp | B43_NPHY_BBCFG_RSTCCA);
b43_phy_write(dev, B43_NPHY_BBCFG, tmp & ~B43_NPHY_BBCFG_RSTCCA);
- b43_nphy_bmac_clock_fgc(dev, 0);
+ b43_phy_force_clock(dev, 0);
b43_mac_phy_clock_set(dev, true);
@@ -3697,15 +3859,7 @@ int b43_phy_initn(struct b43_wldev *dev)
b43_nphy_tx_power_fix(dev);
/* TODO N PHY TX Power Control Idle TSSI */
/* TODO N PHY TX Power Control Setup */
-
- if (phy->rev >= 3) {
- /* TODO */
- } else {
- b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128,
- b43_ntab_tx_gain_rev0_1_2);
- b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128,
- b43_ntab_tx_gain_rev0_1_2);
- }
+ b43_nphy_tx_gain_table_upload(dev);
if (nphy->phyrxchain != 3)
b43_nphy_set_rx_core_state(dev, nphy->phyrxchain);
@@ -3909,6 +4063,10 @@ static void b43_nphy_op_prepare_structs(struct b43_wldev *dev)
nphy->txrx_chain = 2; /* sth different than 0 and 1 for now */
nphy->phyrxchain = 3; /* to avoid b43_nphy_set_rx_core_state like wl */
nphy->perical = 2; /* avoid additional rssi cal on init (like wl) */
+ /* 128 can mean disabled-by-default state of TX pwr ctl. Max value is
+ * 0x7f == 127 and we check for 128 when restoring TX pwr ctl. */
+ nphy->tx_pwr_idx[0] = 128;
+ nphy->tx_pwr_idx[1] = 128;
}
static void b43_nphy_op_free(struct b43_wldev *dev)
@@ -4026,11 +4184,24 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
/* http://bcm-v4.sipsolutions.net/802.11/PHY/Anacore */
static void b43_nphy_op_switch_analog(struct b43_wldev *dev, bool on)
{
- u16 val = on ? 0 : 0x7FFF;
+ u16 override = on ? 0x0 : 0x7FFF;
+ u16 core = on ? 0xD : 0x00FD;
- if (dev->phy.rev >= 3)
- b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, val);
- b43_phy_write(dev, B43_NPHY_AFECTL_OVER, val);
+ if (dev->phy.rev >= 3) {
+ if (on) {
+ b43_phy_write(dev, B43_NPHY_AFECTL_C1, core);
+ b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override);
+ b43_phy_write(dev, B43_NPHY_AFECTL_C2, core);
+ b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override);
+ } else {
+ b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override);
+ b43_phy_write(dev, B43_NPHY_AFECTL_C1, core);
+ b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override);
+ b43_phy_write(dev, B43_NPHY_AFECTL_C2, core);
+ }
+ } else {
+ b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override);
+ }
}
static int b43_nphy_op_switch_channel(struct b43_wldev *dev,
diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/b43/phy_n.h
index e789a89f1047..fbf520285bd1 100644
--- a/drivers/net/wireless/b43/phy_n.h
+++ b/drivers/net/wireless/b43/phy_n.h
@@ -764,6 +764,8 @@ struct b43_phy_n {
u8 cal_orig_pwr_idx[2];
u8 measure_hold;
u8 phyrxchain;
+ u8 hw_phyrxchain;
+ u8 hw_phytxchain;
u8 perical;
u32 deaf_count;
u32 rxcalparams;
@@ -783,6 +785,8 @@ struct b43_phy_n {
u16 mphase_txcal_bestcoeffs[11];
bool txpwrctrl;
+ u8 tx_pwr_idx[2];
+ u16 adj_pwr_tbl[84];
u16 txcal_bbmult;
u16 txiqlocal_bestc[11];
bool txiqlocal_coeffsvalid;
diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c
index 72ab94df7569..fcff923b3c18 100644
--- a/drivers/net/wireless/b43/pio.c
+++ b/drivers/net/wireless/b43/pio.c
@@ -4,7 +4,7 @@
PIO data transfer
- Copyright (c) 2005-2008 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005-2008 Michael Buesch <m@bues.ch>
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
@@ -111,7 +111,7 @@ static u16 index_to_pioqueue_base(struct b43_wldev *dev,
B43_MMIO_PIO11_BASE5,
};
- if (dev->sdev->id.revision >= 11) {
+ if (dev->dev->core_rev >= 11) {
B43_WARN_ON(index >= ARRAY_SIZE(bases_rev11));
return bases_rev11[index];
}
@@ -121,14 +121,14 @@ static u16 index_to_pioqueue_base(struct b43_wldev *dev,
static u16 pio_txqueue_offset(struct b43_wldev *dev)
{
- if (dev->sdev->id.revision >= 11)
+ if (dev->dev->core_rev >= 11)
return 0x18;
return 0;
}
static u16 pio_rxqueue_offset(struct b43_wldev *dev)
{
- if (dev->sdev->id.revision >= 11)
+ if (dev->dev->core_rev >= 11)
return 0x38;
return 8;
}
@@ -144,7 +144,7 @@ static struct b43_pio_txqueue *b43_setup_pioqueue_tx(struct b43_wldev *dev,
if (!q)
return NULL;
q->dev = dev;
- q->rev = dev->sdev->id.revision;
+ q->rev = dev->dev->core_rev;
q->mmio_base = index_to_pioqueue_base(dev, index) +
pio_txqueue_offset(dev);
q->index = index;
@@ -178,7 +178,7 @@ static struct b43_pio_rxqueue *b43_setup_pioqueue_rx(struct b43_wldev *dev,
if (!q)
return NULL;
q->dev = dev;
- q->rev = dev->sdev->id.revision;
+ q->rev = dev->dev->core_rev;
q->mmio_base = index_to_pioqueue_base(dev, index) +
pio_rxqueue_offset(dev);
@@ -611,7 +611,7 @@ static bool pio_rx_frame(struct b43_pio_rxqueue *q)
struct b43_wldev *dev = q->dev;
struct b43_wl *wl = dev->wl;
u16 len;
- u32 macstat;
+ u32 macstat = 0;
unsigned int i, padding;
struct sk_buff *skb;
const char *err_msg = NULL;
@@ -676,7 +676,15 @@ data_ready:
goto rx_error;
}
- macstat = le32_to_cpu(rxhdr->mac_status);
+ switch (dev->fw.hdr_format) {
+ case B43_FW_HDR_598:
+ macstat = le32_to_cpu(rxhdr->format_598.mac_status);
+ break;
+ case B43_FW_HDR_410:
+ case B43_FW_HDR_351:
+ macstat = le32_to_cpu(rxhdr->format_351.mac_status);
+ break;
+ }
if (macstat & B43_RX_MAC_FCSERR) {
if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) {
/* Drop frames with failed FCS. */
diff --git a/drivers/net/wireless/b43/radio_2055.c b/drivers/net/wireless/b43/radio_2055.c
index 44c6dea66882..5289a18ddd8c 100644
--- a/drivers/net/wireless/b43/radio_2055.c
+++ b/drivers/net/wireless/b43/radio_2055.c
@@ -3,7 +3,8 @@
Broadcom B43 wireless driver
IEEE 802.11n PHY and radio device data tables
- Copyright (c) 2008 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2008 Michael Buesch <m@bues.ch>
+ Copyright (c) 2010 Rafał Miłecki <zajec5@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/drivers/net/wireless/b43/radio_2055.h b/drivers/net/wireless/b43/radio_2055.h
index d9bfa0f21b72..67f96122f8d8 100644
--- a/drivers/net/wireless/b43/radio_2055.h
+++ b/drivers/net/wireless/b43/radio_2055.h
@@ -251,4 +251,9 @@ struct b43_nphy_channeltab_entry_rev2 {
void b2055_upload_inittab(struct b43_wldev *dev,
bool ghz5, bool ignore_uploadflag);
+/* Get the NPHY Channel Switch Table entry for a channel.
+ * Returns NULL on failure to find an entry. */
+const struct b43_nphy_channeltab_entry_rev2 *
+b43_nphy_get_chantabent_rev2(struct b43_wldev *dev, u8 channel);
+
#endif /* B43_RADIO_2055_H_ */
diff --git a/drivers/net/wireless/b43/radio_2056.c b/drivers/net/wireless/b43/radio_2056.c
index 8890df067029..a01f776ca4de 100644
--- a/drivers/net/wireless/b43/radio_2056.c
+++ b/drivers/net/wireless/b43/radio_2056.c
@@ -3,6 +3,8 @@
Broadcom B43 wireless driver
IEEE 802.11n 2056 radio device data tables
+ Copyright (c) 2010 Rafał Miłecki <zajec5@gmail.com>
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
diff --git a/drivers/net/wireless/b43/radio_2056.h b/drivers/net/wireless/b43/radio_2056.h
index d601f6e7e313..a7159d8578be 100644
--- a/drivers/net/wireless/b43/radio_2056.h
+++ b/drivers/net/wireless/b43/radio_2056.h
@@ -1,29 +1,3 @@
-/*
-
- Broadcom B43 wireless driver
-
- Copyright (c) 2010 Rafał Miłecki <zajec5@gmail.com>
-
- Some parts of the code in this file are derived from the brcm80211
- driver Copyright (c) 2010 Broadcom Corporation
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This 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; see the file COPYING. If not, write to
- the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
- Boston, MA 02110-1301, USA.
-
-*/
-
#ifndef B43_RADIO_2056_H_
#define B43_RADIO_2056_H_
@@ -1117,4 +1091,9 @@ struct b43_nphy_channeltab_entry_rev3 {
void b2056_upload_inittabs(struct b43_wldev *dev,
bool ghz5, bool ignore_uploadflag);
+/* Get the NPHY Channel Switch Table entry for a channel.
+ * Returns NULL on failure to find an entry. */
+const struct b43_nphy_channeltab_entry_rev3 *
+b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq);
+
#endif /* B43_RADIO_2056_H_ */
diff --git a/drivers/net/wireless/b43/radio_2059.c b/drivers/net/wireless/b43/radio_2059.c
new file mode 100644
index 000000000000..d4ce8a12ff9a
--- /dev/null
+++ b/drivers/net/wireless/b43/radio_2059.c
@@ -0,0 +1,176 @@
+/*
+
+ Broadcom B43 wireless driver
+ IEEE 802.11n 2059 radio device data tables
+
+ Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "b43.h"
+#include "radio_2059.h"
+
+#define RADIOREGS(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \
+ r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \
+ r20, r21, r22, r23, r24, r25, r26, r27, r28) \
+ .radio_syn16 = r00, \
+ .radio_syn17 = r01, \
+ .radio_syn22 = r02, \
+ .radio_syn25 = r03, \
+ .radio_syn27 = r04, \
+ .radio_syn28 = r05, \
+ .radio_syn29 = r06, \
+ .radio_syn2c = r07, \
+ .radio_syn2d = r08, \
+ .radio_syn37 = r09, \
+ .radio_syn41 = r10, \
+ .radio_syn43 = r11, \
+ .radio_syn47 = r12, \
+ .radio_syn4a = r13, \
+ .radio_syn58 = r14, \
+ .radio_syn5a = r15, \
+ .radio_syn6a = r16, \
+ .radio_syn6d = r17, \
+ .radio_syn6e = r18, \
+ .radio_syn92 = r19, \
+ .radio_syn98 = r20, \
+ .radio_rxtx4a = r21, \
+ .radio_rxtx58 = r22, \
+ .radio_rxtx5a = r23, \
+ .radio_rxtx6a = r24, \
+ .radio_rxtx6d = r25, \
+ .radio_rxtx6e = r26, \
+ .radio_rxtx92 = r27, \
+ .radio_rxtx98 = r28
+
+#define PHYREGS(r0, r1, r2, r3, r4, r5) \
+ .phy_regs.bw1 = r0, \
+ .phy_regs.bw2 = r1, \
+ .phy_regs.bw3 = r2, \
+ .phy_regs.bw4 = r3, \
+ .phy_regs.bw5 = r4, \
+ .phy_regs.bw6 = r5
+
+static const struct b43_phy_ht_channeltab_e_radio2059 b43_phy_ht_channeltab_radio2059[] = {
+ { .freq = 2412,
+ RADIOREGS(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c,
+ 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443),
+ },
+ { .freq = 2417,
+ RADIOREGS(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71,
+ 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441),
+ },
+ { .freq = 2422,
+ RADIOREGS(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76,
+ 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f),
+ },
+ { .freq = 2427,
+ RADIOREGS(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b,
+ 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d),
+ },
+ { .freq = 2432,
+ RADIOREGS(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80,
+ 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a),
+ },
+ { .freq = 2437,
+ RADIOREGS(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85,
+ 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438),
+ },
+ { .freq = 2442,
+ RADIOREGS(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a,
+ 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436),
+ },
+ { .freq = 2447,
+ RADIOREGS(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f,
+ 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434),
+ },
+ { .freq = 2452,
+ RADIOREGS(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94,
+ 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431),
+ },
+ { .freq = 2457,
+ RADIOREGS(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99,
+ 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f),
+ },
+ { .freq = 2462,
+ RADIOREGS(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e,
+ 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d),
+ },
+ { .freq = 2467,
+ RADIOREGS(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3,
+ 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b),
+ },
+ { .freq = 2472,
+ RADIOREGS(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8,
+ 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03,
+ 0x00, 0x00, 0x00, 0xf0, 0x00),
+ PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429),
+ },
+};
+
+const struct b43_phy_ht_channeltab_e_radio2059
+*b43_phy_ht_get_channeltab_e_r2059(struct b43_wldev *dev, u16 freq)
+{
+ const struct b43_phy_ht_channeltab_e_radio2059 *e;
+ unsigned int i;
+
+ e = b43_phy_ht_channeltab_radio2059;
+ for (i = 0; i < ARRAY_SIZE(b43_phy_ht_channeltab_radio2059); i++, e++) {
+ if (e->freq == freq)
+ return e;
+ }
+
+ return NULL;
+}
diff --git a/drivers/net/wireless/b43/radio_2059.h b/drivers/net/wireless/b43/radio_2059.h
new file mode 100644
index 000000000000..e4d69e55e9fe
--- /dev/null
+++ b/drivers/net/wireless/b43/radio_2059.h
@@ -0,0 +1,54 @@
+#ifndef B43_RADIO_2059_H_
+#define B43_RADIO_2059_H_
+
+#include <linux/types.h>
+
+#include "phy_ht.h"
+
+#define R2059_SYN 0x000
+#define R2059_TXRX0 0x400
+#define R2059_RXRX1 0x800
+#define R2059_ALL 0xC00
+
+/* Values for various registers uploaded on channel switching */
+struct b43_phy_ht_channeltab_e_radio2059 {
+ /* The channel frequency in MHz */
+ u16 freq;
+ /* Values for radio registers */
+ u8 radio_syn16;
+ u8 radio_syn17;
+ u8 radio_syn22;
+ u8 radio_syn25;
+ u8 radio_syn27;
+ u8 radio_syn28;
+ u8 radio_syn29;
+ u8 radio_syn2c;
+ u8 radio_syn2d;
+ u8 radio_syn37;
+ u8 radio_syn41;
+ u8 radio_syn43;
+ u8 radio_syn47;
+ u8 radio_syn4a;
+ u8 radio_syn58;
+ u8 radio_syn5a;
+ u8 radio_syn6a;
+ u8 radio_syn6d;
+ u8 radio_syn6e;
+ u8 radio_syn92;
+ u8 radio_syn98;
+ u8 radio_rxtx4a;
+ u8 radio_rxtx58;
+ u8 radio_rxtx5a;
+ u8 radio_rxtx6a;
+ u8 radio_rxtx6d;
+ u8 radio_rxtx6e;
+ u8 radio_rxtx92;
+ u8 radio_rxtx98;
+ /* Values for PHY registers */
+ struct b43_phy_ht_channeltab_e_phy phy_regs;
+};
+
+const struct b43_phy_ht_channeltab_e_radio2059
+*b43_phy_ht_get_channeltab_e_r2059(struct b43_wldev *dev, u16 freq);
+
+#endif /* B43_RADIO_2059_H_ */
diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c
index a617efe38289..70c2fcedd1bb 100644
--- a/drivers/net/wireless/b43/rfkill.c
+++ b/drivers/net/wireless/b43/rfkill.c
@@ -3,7 +3,7 @@
Broadcom B43 wireless driver
RFKILL support
- Copyright (c) 2007 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2007 Michael Buesch <m@bues.ch>
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
@@ -37,17 +37,16 @@ void b43_rfkill_poll(struct ieee80211_hw *hw)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev = wl->current_dev;
- struct ssb_bus *bus = dev->sdev->bus;
bool enabled;
bool brought_up = false;
mutex_lock(&wl->mutex);
if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) {
- if (ssb_bus_powerup(bus, 0)) {
+ if (b43_bus_powerup(dev, 0)) {
mutex_unlock(&wl->mutex);
return;
}
- ssb_device_enable(dev->sdev, 0);
+ b43_device_enable(dev, 0);
brought_up = true;
}
@@ -63,8 +62,8 @@ void b43_rfkill_poll(struct ieee80211_hw *hw)
}
if (brought_up) {
- ssb_device_disable(dev->sdev, 0);
- ssb_bus_may_powerdown(bus);
+ b43_device_disable(dev, 0);
+ b43_bus_may_powerdown(dev);
}
mutex_unlock(&wl->mutex);
diff --git a/drivers/net/wireless/b43/sdio.c b/drivers/net/wireless/b43/sdio.c
index 808e25b79703..80b0755ed3af 100644
--- a/drivers/net/wireless/b43/sdio.c
+++ b/drivers/net/wireless/b43/sdio.c
@@ -4,7 +4,7 @@
* SDIO over Sonics Silicon Backplane bus glue for b43.
*
* Copyright (C) 2009 Albert Herranz
- * Copyright (C) 2009 Michael Buesch <mb@bu3sch.de>
+ * Copyright (C) 2009 Michael Buesch <m@bues.ch>
*
* 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
@@ -66,7 +66,7 @@ static void b43_sdio_interrupt_dispatcher(struct sdio_func *func)
int b43_sdio_request_irq(struct b43_wldev *dev,
void (*handler)(struct b43_wldev *dev))
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_bus *bus = dev->dev->sdev->bus;
struct sdio_func *func = bus->host_sdio;
struct b43_sdio *sdio = sdio_get_drvdata(func);
int err;
@@ -82,7 +82,7 @@ int b43_sdio_request_irq(struct b43_wldev *dev,
void b43_sdio_free_irq(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_bus *bus = dev->dev->sdev->bus;
struct sdio_func *func = bus->host_sdio;
struct b43_sdio *sdio = sdio_get_drvdata(func);
@@ -93,8 +93,8 @@ void b43_sdio_free_irq(struct b43_wldev *dev)
sdio->irq_handler = NULL;
}
-static int b43_sdio_probe(struct sdio_func *func,
- const struct sdio_device_id *id)
+static int __devinit b43_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
{
struct b43_sdio *sdio;
struct sdio_func_tuple *tuple;
@@ -171,7 +171,7 @@ out:
return error;
}
-static void b43_sdio_remove(struct sdio_func *func)
+static void __devexit b43_sdio_remove(struct sdio_func *func)
{
struct b43_sdio *sdio = sdio_get_drvdata(func);
diff --git a/drivers/net/wireless/b43/sysfs.c b/drivers/net/wireless/b43/sysfs.c
index 57af619725c3..8e8431d4eb0c 100644
--- a/drivers/net/wireless/b43/sysfs.c
+++ b/drivers/net/wireless/b43/sysfs.c
@@ -4,7 +4,7 @@
SYSFS support routines
- Copyright (c) 2006 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2006 Michael Buesch <m@bues.ch>
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
@@ -140,7 +140,7 @@ static DEVICE_ATTR(interference, 0644,
int b43_sysfs_register(struct b43_wldev *wldev)
{
- struct device *dev = wldev->sdev->dev;
+ struct device *dev = wldev->dev->dev;
B43_WARN_ON(b43_status(wldev) != B43_STAT_INITIALIZED);
@@ -149,7 +149,7 @@ int b43_sysfs_register(struct b43_wldev *wldev)
void b43_sysfs_unregister(struct b43_wldev *wldev)
{
- struct device *dev = wldev->sdev->dev;
+ struct device *dev = wldev->dev->dev;
device_remove_file(dev, &dev_attr_interference);
}
diff --git a/drivers/net/wireless/b43/tables.c b/drivers/net/wireless/b43/tables.c
index 1ef9a6463ec6..ea288df8aee9 100644
--- a/drivers/net/wireless/b43/tables.c
+++ b/drivers/net/wireless/b43/tables.c
@@ -4,7 +4,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Copyright (c) 2005-2007 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (c) 2006, 2006 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2006, 2006 Michael Buesch <m@bues.ch>
Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
diff --git a/drivers/net/wireless/b43/tables_lpphy.c b/drivers/net/wireless/b43/tables_lpphy.c
index 59df3c64af63..cff187c5616d 100644
--- a/drivers/net/wireless/b43/tables_lpphy.c
+++ b/drivers/net/wireless/b43/tables_lpphy.c
@@ -3,7 +3,7 @@
Broadcom B43 wireless driver
IEEE 802.11a/g LP-PHY and radio device data tables
- Copyright (c) 2009 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2009 Michael Buesch <m@bues.ch>
Copyright (c) 2009 Gábor Stefanik <netrolller.3d@gmail.com>
This program is free software; you can redistribute it and/or modify
@@ -2304,7 +2304,6 @@ void lpphy_rev0_1_table_init(struct b43_wldev *dev)
void lpphy_rev2plus_table_init(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
int i;
B43_WARN_ON(dev->phy.rev < 2);
@@ -2341,7 +2340,7 @@ void lpphy_rev2plus_table_init(struct b43_wldev *dev)
b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0),
ARRAY_SIZE(lpphy_papd_mult_table), lpphy_papd_mult_table);
- if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) {
+ if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) {
b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0),
ARRAY_SIZE(lpphy_a0_gain_idx_table), lpphy_a0_gain_idx_table);
b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0),
@@ -2416,12 +2415,12 @@ void lpphy_write_gain_table_bulk(struct b43_wldev *dev, int offset, int count,
void lpphy_init_tx_gain_table(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
switch (dev->phy.rev) {
case 0:
- if ((bus->sprom.boardflags_hi & B43_BFH_NOPA) ||
- (bus->sprom.boardflags_lo & B43_BFL_HGPA))
+ if ((sprom->boardflags_hi & B43_BFH_NOPA) ||
+ (sprom->boardflags_lo & B43_BFL_HGPA))
lpphy_write_gain_table_bulk(dev, 0, 128,
lpphy_rev0_nopa_tx_gain_table);
else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
@@ -2432,8 +2431,8 @@ void lpphy_init_tx_gain_table(struct b43_wldev *dev)
lpphy_rev0_5ghz_tx_gain_table);
break;
case 1:
- if ((bus->sprom.boardflags_hi & B43_BFH_NOPA) ||
- (bus->sprom.boardflags_lo & B43_BFL_HGPA))
+ if ((sprom->boardflags_hi & B43_BFH_NOPA) ||
+ (sprom->boardflags_lo & B43_BFL_HGPA))
lpphy_write_gain_table_bulk(dev, 0, 128,
lpphy_rev1_nopa_tx_gain_table);
else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
@@ -2444,7 +2443,7 @@ void lpphy_init_tx_gain_table(struct b43_wldev *dev)
lpphy_rev1_5ghz_tx_gain_table);
break;
default:
- if (bus->sprom.boardflags_hi & B43_BFH_NOPA)
+ if (sprom->boardflags_hi & B43_BFH_NOPA)
lpphy_write_gain_table_bulk(dev, 0, 128,
lpphy_rev2_nopa_tx_gain_table);
else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c
index 2de483b3d3ba..7b326f2efdc9 100644
--- a/drivers/net/wireless/b43/tables_nphy.c
+++ b/drivers/net/wireless/b43/tables_nphy.c
@@ -3,7 +3,8 @@
Broadcom B43 wireless driver
IEEE 802.11n PHY data tables
- Copyright (c) 2008 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2008 Michael Buesch <m@bues.ch>
+ Copyright (c) 2010 Rafał Miłecki <zajec5@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/b43/tables_nphy.h
index 18569367ce43..a81696bff0ed 100644
--- a/drivers/net/wireless/b43/tables_nphy.h
+++ b/drivers/net/wireless/b43/tables_nphy.h
@@ -60,16 +60,8 @@ struct nphy_gain_ctl_workaround_entry {
struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
struct b43_wldev *dev, bool ghz5, bool ext_lna);
-/* Get the NPHY Channel Switch Table entry for a channel.
- * Returns NULL on failure to find an entry. */
-const struct b43_nphy_channeltab_entry_rev2 *
-b43_nphy_get_chantabent_rev2(struct b43_wldev *dev, u8 channel);
-const struct b43_nphy_channeltab_entry_rev3 *
-b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq);
-
/* The N-PHY tables. */
-
#define B43_NTAB_TYPEMASK 0xF0000000
#define B43_NTAB_8BIT 0x10000000
#define B43_NTAB_16BIT 0x20000000
diff --git a/drivers/net/wireless/b43/tables_phy_ht.c b/drivers/net/wireless/b43/tables_phy_ht.c
new file mode 100644
index 000000000000..176c49d74ef4
--- /dev/null
+++ b/drivers/net/wireless/b43/tables_phy_ht.c
@@ -0,0 +1,836 @@
+/*
+
+ Broadcom B43 wireless driver
+ IEEE 802.11n HT-PHY data tables
+
+ Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "b43.h"
+#include "tables_phy_ht.h"
+#include "phy_common.h"
+#include "phy_ht.h"
+
+static const u16 b43_httab_0x12[] = {
+ 0x0000, 0x0008, 0x000a, 0x0010, 0x0012, 0x0019,
+ 0x001a, 0x001c, 0x0080, 0x0088, 0x008a, 0x0090,
+ 0x0092, 0x0099, 0x009a, 0x009c, 0x0100, 0x0108,
+ 0x010a, 0x0110, 0x0112, 0x0119, 0x011a, 0x011c,
+ 0x0180, 0x0188, 0x018a, 0x0190, 0x0192, 0x0199,
+ 0x019a, 0x019c, 0x0000, 0x0098, 0x00a0, 0x00a8,
+ 0x009a, 0x00a2, 0x00aa, 0x0120, 0x0128, 0x0128,
+ 0x0130, 0x0138, 0x0138, 0x0140, 0x0122, 0x012a,
+ 0x012a, 0x0132, 0x013a, 0x013a, 0x0142, 0x01a8,
+ 0x01b0, 0x01b8, 0x01b0, 0x01b8, 0x01c0, 0x01c8,
+ 0x01c0, 0x01c8, 0x01d0, 0x01d0, 0x01d8, 0x01aa,
+ 0x01b2, 0x01ba, 0x01b2, 0x01ba, 0x01c2, 0x01ca,
+ 0x01c2, 0x01ca, 0x01d2, 0x01d2, 0x01da, 0x0001,
+ 0x0002, 0x0004, 0x0009, 0x000c, 0x0011, 0x0014,
+ 0x0018, 0x0020, 0x0021, 0x0022, 0x0024, 0x0081,
+ 0x0082, 0x0084, 0x0089, 0x008c, 0x0091, 0x0094,
+ 0x0098, 0x00a0, 0x00a1, 0x00a2, 0x00a4, 0x0007,
+ 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007,
+ 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007,
+ 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007,
+ 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007,
+ 0x0007, 0x0007,
+};
+
+static const u16 b43_httab_0x27[] = {
+ 0x0009, 0x000e, 0x0011, 0x0014, 0x0017, 0x001a,
+ 0x001d, 0x0020, 0x0009, 0x000e, 0x0011, 0x0014,
+ 0x0017, 0x001a, 0x001d, 0x0020, 0x0009, 0x000e,
+ 0x0011, 0x0014, 0x0017, 0x001a, 0x001d, 0x0020,
+ 0x0009, 0x000e, 0x0011, 0x0014, 0x0017, 0x001a,
+ 0x001d, 0x0020,
+};
+
+static const u16 b43_httab_0x26[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000,
+};
+
+static const u32 b43_httab_0x25[] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+static const u32 b43_httab_0x2f[] = {
+ 0x00035700, 0x0002cc9a, 0x00026666, 0x0001581f,
+ 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f,
+ 0x0001581f, 0x0001581f, 0x0001581f, 0x00035700,
+ 0x0002cc9a, 0x00026666, 0x0001581f, 0x0001581f,
+ 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f,
+ 0x0001581f, 0x0001581f,
+};
+
+static const u16 b43_httab_0x1a[] = {
+ 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052,
+ 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e,
+ 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a,
+ 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046,
+ 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040,
+ 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a,
+ 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033,
+ 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b,
+ 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f,
+ 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e,
+ 0x000b, 0x0007, 0x0002, 0x00fd,
+};
+
+static const u16 b43_httab_0x1b[] = {
+ 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052,
+ 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e,
+ 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a,
+ 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046,
+ 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040,
+ 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a,
+ 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033,
+ 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b,
+ 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f,
+ 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e,
+ 0x000b, 0x0007, 0x0002, 0x00fd,
+};
+
+static const u16 b43_httab_0x1c[] = {
+ 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052,
+ 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e,
+ 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a,
+ 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046,
+ 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040,
+ 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a,
+ 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033,
+ 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b,
+ 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f,
+ 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e,
+ 0x000b, 0x0007, 0x0002, 0x00fd,
+};
+
+static const u32 b43_httab_0x1a_0xc0[] = {
+ 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e,
+ 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037,
+ 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031,
+ 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040,
+ 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039,
+ 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033,
+ 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e,
+ 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037,
+ 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031,
+ 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c,
+ 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e,
+ 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037,
+ 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031,
+ 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c,
+ 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042,
+ 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b,
+ 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034,
+ 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f,
+ 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e,
+ 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037,
+ 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031,
+ 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c,
+ 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027,
+ 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023,
+ 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e,
+ 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037,
+ 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031,
+ 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c,
+ 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027,
+ 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023,
+ 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f,
+ 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c,
+};
+
+static const u32 b43_httab_0x1a_0x140[] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+static const u32 b43_httab_0x1b_0x140[] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+static const u32 b43_httab_0x1c_0x140[] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+static const u16 b43_httab_0x1a_0x1c0[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000,
+};
+
+static const u16 b43_httab_0x1b_0x1c0[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000,
+};
+
+static const u16 b43_httab_0x1c_0x1c0[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000,
+};
+
+static const u16 b43_httab_0x1a_0x240[] = {
+ 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036,
+ 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036,
+ 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a,
+ 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a,
+ 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e,
+ 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e,
+ 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e,
+ 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e,
+ 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e,
+ 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc,
+ 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc,
+ 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6,
+};
+
+static const u16 b43_httab_0x1b_0x240[] = {
+ 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036,
+ 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036,
+ 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a,
+ 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a,
+ 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e,
+ 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e,
+ 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e,
+ 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e,
+ 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e,
+ 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc,
+ 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc,
+ 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6,
+};
+
+static const u16 b43_httab_0x1c_0x240[] = {
+ 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036,
+ 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036,
+ 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a,
+ 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a,
+ 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e,
+ 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e,
+ 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e,
+ 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e,
+ 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e,
+ 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc,
+ 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc,
+ 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6,
+ 0x01d6, 0x01d6,
+};
+
+static const u32 b43_httab_0x1f[] = {
+ 0x00000000, 0x00000000, 0x00016023, 0x00006028,
+ 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037,
+ 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d,
+ 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9,
+ 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9,
+ 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6,
+ 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c,
+ 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b,
+ 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15,
+ 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9,
+ 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87,
+ 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3,
+ 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa,
+ 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff,
+ 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff,
+ 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff,
+};
+
+static const u32 b43_httab_0x21[] = {
+ 0x00000000, 0x00000000, 0x00016023, 0x00006028,
+ 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037,
+ 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d,
+ 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9,
+ 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9,
+ 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6,
+ 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c,
+ 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b,
+ 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15,
+ 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9,
+ 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87,
+ 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3,
+ 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa,
+ 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff,
+ 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff,
+ 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff,
+};
+
+static const u32 b43_httab_0x23[] = {
+ 0x00000000, 0x00000000, 0x00016023, 0x00006028,
+ 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037,
+ 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d,
+ 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9,
+ 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9,
+ 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6,
+ 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c,
+ 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b,
+ 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15,
+ 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9,
+ 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87,
+ 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3,
+ 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa,
+ 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff,
+ 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff,
+ 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff,
+};
+
+static const u32 b43_httab_0x20[] = {
+ 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035,
+ 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043,
+ 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054,
+ 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a,
+ 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085,
+ 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8,
+ 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3,
+ 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a,
+ 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e,
+ 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5,
+ 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212,
+ 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b,
+ 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348,
+ 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422,
+ 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533,
+ 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c,
+};
+
+static const u32 b43_httab_0x22[] = {
+ 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035,
+ 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043,
+ 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054,
+ 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a,
+ 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085,
+ 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8,
+ 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3,
+ 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a,
+ 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e,
+ 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5,
+ 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212,
+ 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b,
+ 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348,
+ 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422,
+ 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533,
+ 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c,
+};
+
+static const u32 b43_httab_0x24[] = {
+ 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035,
+ 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043,
+ 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054,
+ 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a,
+ 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085,
+ 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8,
+ 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3,
+ 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a,
+ 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e,
+ 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5,
+ 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212,
+ 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b,
+ 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348,
+ 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422,
+ 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533,
+ 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c,
+};
+
+/* Some late-init table */
+const u32 b43_httab_0x1a_0xc0_late[] = {
+ 0x10f90040, 0x10e10040, 0x10e1003c, 0x10c9003d,
+ 0x10b9003c, 0x10a9003d, 0x10a1003c, 0x1099003b,
+ 0x1091003b, 0x1089003a, 0x1081003a, 0x10790039,
+ 0x10710039, 0x1069003a, 0x1061003b, 0x1059003d,
+ 0x1051003f, 0x10490042, 0x1049003e, 0x1049003b,
+ 0x1041003e, 0x1041003b, 0x1039003e, 0x1039003b,
+ 0x10390038, 0x10390035, 0x1031003a, 0x10310036,
+ 0x10310033, 0x1029003a, 0x10290037, 0x10290034,
+ 0x10290031, 0x10210039, 0x10210036, 0x10210033,
+ 0x10210030, 0x1019003c, 0x10190039, 0x10190036,
+ 0x10190033, 0x10190030, 0x1019002d, 0x1019002b,
+ 0x10190028, 0x1011003a, 0x10110036, 0x10110033,
+ 0x10110030, 0x1011002e, 0x1011002b, 0x10110029,
+ 0x10110027, 0x10110024, 0x10110022, 0x10110020,
+ 0x1011001f, 0x1011001d, 0x1009003a, 0x10090037,
+ 0x10090034, 0x10090031, 0x1009002e, 0x1009002c,
+ 0x10090029, 0x10090027, 0x10090025, 0x10090023,
+ 0x10090021, 0x1009001f, 0x1009001d, 0x1009001b,
+ 0x1009001a, 0x10090018, 0x10090017, 0x10090016,
+ 0x10090015, 0x10090013, 0x10090012, 0x10090011,
+ 0x10090010, 0x1009000f, 0x1009000f, 0x1009000e,
+ 0x1009000d, 0x1009000c, 0x1009000c, 0x1009000b,
+ 0x1009000a, 0x1009000a, 0x10090009, 0x10090009,
+ 0x10090008, 0x10090008, 0x10090007, 0x10090007,
+ 0x10090007, 0x10090006, 0x10090006, 0x10090005,
+ 0x10090005, 0x10090005, 0x10090005, 0x10090004,
+ 0x10090004, 0x10090004, 0x10090004, 0x10090003,
+ 0x10090003, 0x10090003, 0x10090003, 0x10090003,
+ 0x10090003, 0x10090002, 0x10090002, 0x10090002,
+ 0x10090002, 0x10090002, 0x10090002, 0x10090002,
+ 0x10090002, 0x10090002, 0x10090001, 0x10090001,
+ 0x10090001, 0x10090001, 0x10090001, 0x10090001,
+};
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+u32 b43_httab_read(struct b43_wldev *dev, u32 offset)
+{
+ u32 type, value;
+
+ type = offset & B43_HTTAB_TYPEMASK;
+ offset &= ~B43_HTTAB_TYPEMASK;
+ B43_WARN_ON(offset > 0xFFFF);
+
+ switch (type) {
+ case B43_HTTAB_8BIT:
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+ value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO) & 0xFF;
+ break;
+ case B43_HTTAB_16BIT:
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+ value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO);
+ break;
+ case B43_HTTAB_32BIT:
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+ value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATAHI);
+ value <<= 16;
+ value |= b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO);
+ break;
+ default:
+ B43_WARN_ON(1);
+ value = 0;
+ }
+
+ return value;
+}
+
+void b43_httab_read_bulk(struct b43_wldev *dev, u32 offset,
+ unsigned int nr_elements, void *_data)
+{
+ u32 type;
+ u8 *data = _data;
+ unsigned int i;
+
+ type = offset & B43_HTTAB_TYPEMASK;
+ offset &= ~B43_HTTAB_TYPEMASK;
+ B43_WARN_ON(offset > 0xFFFF);
+
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+
+ for (i = 0; i < nr_elements; i++) {
+ switch (type) {
+ case B43_HTTAB_8BIT:
+ *data = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO) & 0xFF;
+ data++;
+ break;
+ case B43_HTTAB_16BIT:
+ *((u16 *)data) = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO);
+ data += 2;
+ break;
+ case B43_HTTAB_32BIT:
+ *((u32 *)data) = b43_phy_read(dev, B43_PHY_HT_TABLE_DATAHI);
+ *((u32 *)data) <<= 16;
+ *((u32 *)data) |= b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO);
+ data += 4;
+ break;
+ default:
+ B43_WARN_ON(1);
+ }
+ }
+}
+
+void b43_httab_write(struct b43_wldev *dev, u32 offset, u32 value)
+{
+ u32 type;
+
+ type = offset & B43_HTTAB_TYPEMASK;
+ offset &= 0xFFFF;
+
+ switch (type) {
+ case B43_HTTAB_8BIT:
+ B43_WARN_ON(value & ~0xFF);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value);
+ break;
+ case B43_HTTAB_16BIT:
+ B43_WARN_ON(value & ~0xFFFF);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value);
+ break;
+ case B43_HTTAB_32BIT:
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, value >> 16);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value & 0xFFFF);
+ break;
+ default:
+ B43_WARN_ON(1);
+ }
+
+ return;
+}
+
+void b43_httab_write_few(struct b43_wldev *dev, u32 offset, size_t num, ...)
+{
+ va_list args;
+ u32 type, value;
+ unsigned int i;
+
+ type = offset & B43_HTTAB_TYPEMASK;
+ offset &= 0xFFFF;
+
+ va_start(args, num);
+ switch (type) {
+ case B43_HTTAB_8BIT:
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+ for (i = 0; i < num; i++) {
+ value = va_arg(args, int);
+ B43_WARN_ON(value & ~0xFF);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value);
+ }
+ break;
+ case B43_HTTAB_16BIT:
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+ for (i = 0; i < num; i++) {
+ value = va_arg(args, int);
+ B43_WARN_ON(value & ~0xFFFF);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value);
+ }
+ break;
+ case B43_HTTAB_32BIT:
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+ for (i = 0; i < num; i++) {
+ value = va_arg(args, int);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI,
+ value >> 16);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO,
+ value & 0xFFFF);
+ }
+ break;
+ default:
+ B43_WARN_ON(1);
+ }
+ va_end(args);
+
+ return;
+}
+
+void b43_httab_write_bulk(struct b43_wldev *dev, u32 offset,
+ unsigned int nr_elements, const void *_data)
+{
+ u32 type, value;
+ const u8 *data = _data;
+ unsigned int i;
+
+ type = offset & B43_HTTAB_TYPEMASK;
+ offset &= ~B43_HTTAB_TYPEMASK;
+ B43_WARN_ON(offset > 0xFFFF);
+
+ b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+
+ for (i = 0; i < nr_elements; i++) {
+ switch (type) {
+ case B43_HTTAB_8BIT:
+ value = *data;
+ data++;
+ B43_WARN_ON(value & ~0xFF);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value);
+ break;
+ case B43_HTTAB_16BIT:
+ value = *((u16 *)data);
+ data += 2;
+ B43_WARN_ON(value & ~0xFFFF);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value);
+ break;
+ case B43_HTTAB_32BIT:
+ value = *((u32 *)data);
+ data += 4;
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, value >> 16);
+ b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO,
+ value & 0xFFFF);
+ break;
+ default:
+ B43_WARN_ON(1);
+ }
+ }
+}
+
+/**************************************************
+ * Tables ops.
+ **************************************************/
+
+#define httab_upload(dev, offset, data) do { \
+ b43_httab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \
+ } while (0)
+void b43_phy_ht_tables_init(struct b43_wldev *dev)
+{
+ BUILD_BUG_ON(ARRAY_SIZE(b43_httab_0x1a_0xc0_late) !=
+ B43_HTTAB_1A_C0_LATE_SIZE);
+
+ httab_upload(dev, B43_HTTAB16(0x12, 0), b43_httab_0x12);
+ httab_upload(dev, B43_HTTAB16(0x27, 0), b43_httab_0x27);
+ httab_upload(dev, B43_HTTAB16(0x26, 0), b43_httab_0x26);
+ httab_upload(dev, B43_HTTAB32(0x25, 0), b43_httab_0x25);
+ httab_upload(dev, B43_HTTAB32(0x2f, 0), b43_httab_0x2f);
+ httab_upload(dev, B43_HTTAB16(0x1a, 0), b43_httab_0x1a);
+ httab_upload(dev, B43_HTTAB16(0x1b, 0), b43_httab_0x1b);
+ httab_upload(dev, B43_HTTAB16(0x1c, 0), b43_httab_0x1c);
+ httab_upload(dev, B43_HTTAB32(0x1a, 0x0c0), b43_httab_0x1a_0xc0);
+ httab_upload(dev, B43_HTTAB32(0x1a, 0x140), b43_httab_0x1a_0x140);
+ httab_upload(dev, B43_HTTAB32(0x1b, 0x140), b43_httab_0x1b_0x140);
+ httab_upload(dev, B43_HTTAB32(0x1c, 0x140), b43_httab_0x1c_0x140);
+ httab_upload(dev, B43_HTTAB16(0x1a, 0x1c0), b43_httab_0x1a_0x1c0);
+ httab_upload(dev, B43_HTTAB16(0x1b, 0x1c0), b43_httab_0x1b_0x1c0);
+ httab_upload(dev, B43_HTTAB16(0x1c, 0x1c0), b43_httab_0x1c_0x1c0);
+ httab_upload(dev, B43_HTTAB16(0x1a, 0x240), b43_httab_0x1a_0x240);
+ httab_upload(dev, B43_HTTAB16(0x1b, 0x240), b43_httab_0x1b_0x240);
+ httab_upload(dev, B43_HTTAB16(0x1c, 0x240), b43_httab_0x1c_0x240);
+ httab_upload(dev, B43_HTTAB32(0x1f, 0), b43_httab_0x1f);
+ httab_upload(dev, B43_HTTAB32(0x21, 0), b43_httab_0x21);
+ httab_upload(dev, B43_HTTAB32(0x23, 0), b43_httab_0x23);
+ httab_upload(dev, B43_HTTAB32(0x20, 0), b43_httab_0x20);
+ httab_upload(dev, B43_HTTAB32(0x22, 0), b43_httab_0x22);
+ httab_upload(dev, B43_HTTAB32(0x24, 0), b43_httab_0x24);
+}
diff --git a/drivers/net/wireless/b43/tables_phy_ht.h b/drivers/net/wireless/b43/tables_phy_ht.h
new file mode 100644
index 000000000000..1b5ef2bc770c
--- /dev/null
+++ b/drivers/net/wireless/b43/tables_phy_ht.h
@@ -0,0 +1,26 @@
+#ifndef B43_TABLES_PHY_HT_H_
+#define B43_TABLES_PHY_HT_H_
+
+/* The HT-PHY tables. */
+#define B43_HTTAB_TYPEMASK 0xF0000000
+#define B43_HTTAB_8BIT 0x10000000
+#define B43_HTTAB_16BIT 0x20000000
+#define B43_HTTAB_32BIT 0x30000000
+#define B43_HTTAB8(table, offset) (((table) << 10) | (offset) | B43_HTTAB_8BIT)
+#define B43_HTTAB16(table, offset) (((table) << 10) | (offset) | B43_HTTAB_16BIT)
+#define B43_HTTAB32(table, offset) (((table) << 10) | (offset) | B43_HTTAB_32BIT)
+
+u32 b43_httab_read(struct b43_wldev *dev, u32 offset);
+void b43_httab_read_bulk(struct b43_wldev *dev, u32 offset,
+ unsigned int nr_elements, void *_data);
+void b43_httab_write(struct b43_wldev *dev, u32 offset, u32 value);
+void b43_httab_write_few(struct b43_wldev *dev, u32 offset, size_t num, ...);
+void b43_httab_write_bulk(struct b43_wldev *dev, u32 offset,
+ unsigned int nr_elements, const void *_data);
+
+void b43_phy_ht_tables_init(struct b43_wldev *dev);
+
+#define B43_HTTAB_1A_C0_LATE_SIZE 128
+extern const u32 b43_httab_0x1a_0xc0_late[];
+
+#endif /* B43_TABLES_PHY_HT_H_ */
diff --git a/drivers/net/wireless/b43/tables_phy_lcn.c b/drivers/net/wireless/b43/tables_phy_lcn.c
new file mode 100644
index 000000000000..5176363cadf2
--- /dev/null
+++ b/drivers/net/wireless/b43/tables_phy_lcn.c
@@ -0,0 +1,724 @@
+/*
+
+ Broadcom B43 wireless driver
+ IEEE 802.11n LCN-PHY data tables
+
+ Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "b43.h"
+#include "tables_phy_lcn.h"
+#include "phy_common.h"
+#include "phy_lcn.h"
+
+struct b43_lcntab_tx_gain_tbl_entry {
+ u8 gm;
+ u8 pga;
+ u8 pad;
+ u8 dac;
+ u8 bb_mult;
+};
+
+/**************************************************
+ * Static tables.
+ **************************************************/
+
+static const u16 b43_lcntab_0x02[] = {
+ 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+ 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+ 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+ 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+ 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+ 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+ 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+ 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+ 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+ 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+ 0x014d, 0x014d, 0x014d, 0x014d,
+};
+
+static const u16 b43_lcntab_0x01[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const u32 b43_lcntab_0x0b[] = {
+ 0x000141f8, 0x000021f8, 0x000021fb, 0x000041fb,
+ 0x0001fedb, 0x0000217b, 0x00002133, 0x000040eb,
+ 0x0001fea3, 0x0000024b,
+};
+
+static const u32 b43_lcntab_0x0c[] = {
+ 0x00100001, 0x00200010, 0x00300001, 0x00400010,
+ 0x00500022, 0x00600122, 0x00700222, 0x00800322,
+ 0x00900422, 0x00a00522, 0x00b00622, 0x00c00722,
+ 0x00d00822, 0x00f00922, 0x00100a22, 0x00200b22,
+ 0x00300c22, 0x00400d22, 0x00500e22, 0x00600f22,
+};
+
+static const u32 b43_lcntab_0x0d[] = {
+ 0x00000000, 0x00000000, 0x10000000, 0x00000000,
+ 0x20000000, 0x00000000, 0x30000000, 0x00000000,
+ 0x40000000, 0x00000000, 0x50000000, 0x00000000,
+ 0x60000000, 0x00000000, 0x70000000, 0x00000000,
+ 0x80000000, 0x00000000, 0x90000000, 0x00000008,
+ 0xa0000000, 0x00000008, 0xb0000000, 0x00000008,
+ 0xc0000000, 0x00000008, 0xd0000000, 0x00000008,
+ 0xe0000000, 0x00000008, 0xf0000000, 0x00000008,
+ 0x00000000, 0x00000009, 0x10000000, 0x00000009,
+ 0x20000000, 0x00000019, 0x30000000, 0x00000019,
+ 0x40000000, 0x00000019, 0x50000000, 0x00000019,
+ 0x60000000, 0x00000019, 0x70000000, 0x00000019,
+ 0x80000000, 0x00000019, 0x90000000, 0x00000019,
+ 0xa0000000, 0x00000019, 0xb0000000, 0x00000019,
+ 0xc0000000, 0x00000019, 0xd0000000, 0x00000019,
+ 0xe0000000, 0x00000019, 0xf0000000, 0x00000019,
+ 0x00000000, 0x0000001a, 0x10000000, 0x0000001a,
+ 0x20000000, 0x0000001a, 0x30000000, 0x0000001a,
+ 0x40000000, 0x0000001a, 0x50000000, 0x00000002,
+ 0x60000000, 0x00000002, 0x70000000, 0x00000002,
+ 0x80000000, 0x00000002, 0x90000000, 0x00000002,
+ 0xa0000000, 0x00000002, 0xb0000000, 0x00000002,
+ 0xc0000000, 0x0000000a, 0xd0000000, 0x0000000a,
+ 0xe0000000, 0x0000000a, 0xf0000000, 0x0000000a,
+ 0x00000000, 0x0000000b, 0x10000000, 0x0000000b,
+ 0x20000000, 0x0000000b, 0x30000000, 0x0000000b,
+ 0x40000000, 0x0000000b, 0x50000000, 0x0000001b,
+ 0x60000000, 0x0000001b, 0x70000000, 0x0000001b,
+ 0x80000000, 0x0000001b, 0x90000000, 0x0000001b,
+ 0xa0000000, 0x0000001b, 0xb0000000, 0x0000001b,
+ 0xc0000000, 0x0000001b, 0xd0000000, 0x0000001b,
+ 0xe0000000, 0x0000001b, 0xf0000000, 0x0000001b,
+ 0x00000000, 0x0000001c, 0x10000000, 0x0000001c,
+ 0x20000000, 0x0000001c, 0x30000000, 0x0000001c,
+ 0x40000000, 0x0000001c, 0x50000000, 0x0000001c,
+ 0x60000000, 0x0000001c, 0x70000000, 0x0000001c,
+ 0x80000000, 0x0000001c, 0x90000000, 0x0000001c,
+};
+
+static const u16 b43_lcntab_0x0e[] = {
+ 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406,
+ 0x0407, 0x0408, 0x0409, 0x040a, 0x058b, 0x058c,
+ 0x058d, 0x058e, 0x058f, 0x0090, 0x0091, 0x0092,
+ 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 0x0198,
+ 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e,
+ 0x019f, 0x01a0, 0x01a1, 0x01a2, 0x01a3, 0x01a4,
+ 0x01a5, 0x0000,
+};
+
+static const u16 b43_lcntab_0x0f[] = {
+ 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009,
+ 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005,
+ 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009,
+ 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005,
+ 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009,
+ 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005,
+ 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009,
+ 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005,
+ 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009,
+ 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005,
+ 0x000a, 0x0009, 0x0006, 0x0005,
+};
+
+static const u16 b43_lcntab_0x10[] = {
+ 0x005f, 0x0036, 0x0029, 0x001f, 0x005f, 0x0036,
+ 0x0029, 0x001f, 0x005f, 0x0036, 0x0029, 0x001f,
+ 0x005f, 0x0036, 0x0029, 0x001f,
+};
+
+static const u16 b43_lcntab_0x11[] = {
+ 0x0009, 0x000f, 0x0014, 0x0018, 0x00fe, 0x0007,
+ 0x000b, 0x000f, 0x00fb, 0x00fe, 0x0001, 0x0005,
+ 0x0008, 0x000b, 0x000e, 0x0011, 0x0014, 0x0017,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0003, 0x0006, 0x0009, 0x000c, 0x000f,
+ 0x0012, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003,
+ 0x0006, 0x0009, 0x000c, 0x000f, 0x0012, 0x0015,
+ 0x0018, 0x001b, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0003, 0x00eb, 0x0000, 0x0000,
+};
+
+static const u32 b43_lcntab_0x12[] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000004, 0x00000000, 0x00000004, 0x00000008,
+ 0x00000001, 0x00000005, 0x00000009, 0x0000000d,
+ 0x0000004d, 0x0000008d, 0x0000000d, 0x0000004d,
+ 0x0000008d, 0x000000cd, 0x0000004f, 0x0000008f,
+ 0x000000cf, 0x000000d3, 0x00000113, 0x00000513,
+ 0x00000913, 0x00000953, 0x00000d53, 0x00001153,
+ 0x00001193, 0x00005193, 0x00009193, 0x0000d193,
+ 0x00011193, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000004,
+ 0x00000000, 0x00000004, 0x00000008, 0x00000001,
+ 0x00000005, 0x00000009, 0x0000000d, 0x0000004d,
+ 0x0000008d, 0x0000000d, 0x0000004d, 0x0000008d,
+ 0x000000cd, 0x0000004f, 0x0000008f, 0x000000cf,
+ 0x000000d3, 0x00000113, 0x00000513, 0x00000913,
+ 0x00000953, 0x00000d53, 0x00001153, 0x00005153,
+ 0x00009153, 0x0000d153, 0x00011153, 0x00015153,
+ 0x00019153, 0x0001d153, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+static const u16 b43_lcntab_0x14[] = {
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0002, 0x0003, 0x0001, 0x0003, 0x0002, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0003,
+ 0x0001, 0x0003, 0x0002, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001,
+};
+
+static const u16 b43_lcntab_0x17[] = {
+ 0x001a, 0x0034, 0x004e, 0x0068, 0x009c, 0x00d0,
+ 0x00ea, 0x0104, 0x0034, 0x0068, 0x009c, 0x00d0,
+ 0x0138, 0x01a0, 0x01d4, 0x0208, 0x004e, 0x009c,
+ 0x00ea, 0x0138, 0x01d4, 0x0270, 0x02be, 0x030c,
+ 0x0068, 0x00d0, 0x0138, 0x01a0, 0x0270, 0x0340,
+ 0x03a8, 0x0410, 0x0018, 0x009c, 0x00d0, 0x0104,
+ 0x00ea, 0x0138, 0x0186, 0x00d0, 0x0104, 0x0104,
+ 0x0138, 0x016c, 0x016c, 0x01a0, 0x0138, 0x0186,
+ 0x0186, 0x01d4, 0x0222, 0x0222, 0x0270, 0x0104,
+ 0x0138, 0x016c, 0x0138, 0x016c, 0x01a0, 0x01d4,
+ 0x01a0, 0x01d4, 0x0208, 0x0208, 0x023c, 0x0186,
+ 0x01d4, 0x0222, 0x01d4, 0x0222, 0x0270, 0x02be,
+ 0x0270, 0x02be, 0x030c, 0x030c, 0x035a, 0x0036,
+ 0x006c, 0x00a2, 0x00d8, 0x0144, 0x01b0, 0x01e6,
+ 0x021c, 0x006c, 0x00d8, 0x0144, 0x01b0, 0x0288,
+ 0x0360, 0x03cc, 0x0438, 0x00a2, 0x0144, 0x01e6,
+ 0x0288, 0x03cc, 0x0510, 0x05b2, 0x0654, 0x00d8,
+ 0x01b0, 0x0288, 0x0360, 0x0510, 0x06c0, 0x0798,
+ 0x0870, 0x0018, 0x0144, 0x01b0, 0x021c, 0x01e6,
+ 0x0288, 0x032a, 0x01b0, 0x021c, 0x021c, 0x0288,
+ 0x02f4, 0x02f4, 0x0360, 0x0288, 0x032a, 0x032a,
+ 0x03cc, 0x046e, 0x046e, 0x0510, 0x021c, 0x0288,
+ 0x02f4, 0x0288, 0x02f4, 0x0360, 0x03cc, 0x0360,
+ 0x03cc, 0x0438, 0x0438, 0x04a4, 0x032a, 0x03cc,
+ 0x046e, 0x03cc, 0x046e, 0x0510, 0x05b2, 0x0510,
+ 0x05b2, 0x0654, 0x0654, 0x06f6,
+};
+
+static const u16 b43_lcntab_0x00[] = {
+ 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00,
+ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005,
+ 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600,
+ 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003,
+ 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007,
+ 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const u32 b43_lcntab_0x18[] = {
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+};
+
+/**************************************************
+ * TX gain.
+ **************************************************/
+
+const struct b43_lcntab_tx_gain_tbl_entry
+ b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0[B43_LCNTAB_TX_GAIN_SIZE] = {
+ { 0x03, 0x00, 0x1f, 0x0, 0x48 },
+ { 0x03, 0x00, 0x1f, 0x0, 0x46 },
+ { 0x03, 0x00, 0x1f, 0x0, 0x44 },
+ { 0x03, 0x00, 0x1e, 0x0, 0x43 },
+ { 0x03, 0x00, 0x1d, 0x0, 0x44 },
+ { 0x03, 0x00, 0x1c, 0x0, 0x44 },
+ { 0x03, 0x00, 0x1b, 0x0, 0x45 },
+ { 0x03, 0x00, 0x1a, 0x0, 0x46 },
+ { 0x03, 0x00, 0x19, 0x0, 0x46 },
+ { 0x03, 0x00, 0x18, 0x0, 0x47 },
+ { 0x03, 0x00, 0x17, 0x0, 0x48 },
+ { 0x03, 0x00, 0x17, 0x0, 0x46 },
+ { 0x03, 0x00, 0x16, 0x0, 0x47 },
+ { 0x03, 0x00, 0x15, 0x0, 0x48 },
+ { 0x03, 0x00, 0x15, 0x0, 0x46 },
+ { 0x03, 0x00, 0x15, 0x0, 0x44 },
+ { 0x03, 0x00, 0x15, 0x0, 0x42 },
+ { 0x03, 0x00, 0x15, 0x0, 0x40 },
+ { 0x03, 0x00, 0x15, 0x0, 0x3f },
+ { 0x03, 0x00, 0x14, 0x0, 0x40 },
+ { 0x03, 0x00, 0x13, 0x0, 0x41 },
+ { 0x03, 0x00, 0x13, 0x0, 0x40 },
+ { 0x03, 0x00, 0x12, 0x0, 0x41 },
+ { 0x03, 0x00, 0x12, 0x0, 0x40 },
+ { 0x03, 0x00, 0x11, 0x0, 0x41 },
+ { 0x03, 0x00, 0x11, 0x0, 0x40 },
+ { 0x03, 0x00, 0x10, 0x0, 0x41 },
+ { 0x03, 0x00, 0x10, 0x0, 0x40 },
+ { 0x03, 0x00, 0x10, 0x0, 0x3e },
+ { 0x03, 0x00, 0x10, 0x0, 0x3c },
+ { 0x03, 0x00, 0x10, 0x0, 0x3a },
+ { 0x03, 0x00, 0x0f, 0x0, 0x3d },
+ { 0x03, 0x00, 0x0f, 0x0, 0x3b },
+ { 0x03, 0x00, 0x0e, 0x0, 0x3d },
+ { 0x03, 0x00, 0x0e, 0x0, 0x3c },
+ { 0x03, 0x00, 0x0e, 0x0, 0x3a },
+ { 0x03, 0x00, 0x0d, 0x0, 0x3c },
+ { 0x03, 0x00, 0x0d, 0x0, 0x3b },
+ { 0x03, 0x00, 0x0c, 0x0, 0x3e },
+ { 0x03, 0x00, 0x0c, 0x0, 0x3c },
+ { 0x03, 0x00, 0x0c, 0x0, 0x3a },
+ { 0x03, 0x00, 0x0b, 0x0, 0x3e },
+ { 0x03, 0x00, 0x0b, 0x0, 0x3c },
+ { 0x03, 0x00, 0x0b, 0x0, 0x3b },
+ { 0x03, 0x00, 0x0b, 0x0, 0x39 },
+ { 0x03, 0x00, 0x0a, 0x0, 0x3d },
+ { 0x03, 0x00, 0x0a, 0x0, 0x3b },
+ { 0x03, 0x00, 0x0a, 0x0, 0x39 },
+ { 0x03, 0x00, 0x09, 0x0, 0x3e },
+ { 0x03, 0x00, 0x09, 0x0, 0x3c },
+ { 0x03, 0x00, 0x09, 0x0, 0x3a },
+ { 0x03, 0x00, 0x09, 0x0, 0x39 },
+ { 0x03, 0x00, 0x08, 0x0, 0x3e },
+ { 0x03, 0x00, 0x08, 0x0, 0x3c },
+ { 0x03, 0x00, 0x08, 0x0, 0x3a },
+ { 0x03, 0x00, 0x08, 0x0, 0x39 },
+ { 0x03, 0x00, 0x08, 0x0, 0x37 },
+ { 0x03, 0x00, 0x07, 0x0, 0x3d },
+ { 0x03, 0x00, 0x07, 0x0, 0x3c },
+ { 0x03, 0x00, 0x07, 0x0, 0x3a },
+ { 0x03, 0x00, 0x07, 0x0, 0x38 },
+ { 0x03, 0x00, 0x07, 0x0, 0x37 },
+ { 0x03, 0x00, 0x06, 0x0, 0x3e },
+ { 0x03, 0x00, 0x06, 0x0, 0x3c },
+ { 0x03, 0x00, 0x06, 0x0, 0x3a },
+ { 0x03, 0x00, 0x06, 0x0, 0x39 },
+ { 0x03, 0x00, 0x06, 0x0, 0x37 },
+ { 0x03, 0x00, 0x06, 0x0, 0x36 },
+ { 0x03, 0x00, 0x06, 0x0, 0x34 },
+ { 0x03, 0x00, 0x05, 0x0, 0x3d },
+ { 0x03, 0x00, 0x05, 0x0, 0x3b },
+ { 0x03, 0x00, 0x05, 0x0, 0x39 },
+ { 0x03, 0x00, 0x05, 0x0, 0x38 },
+ { 0x03, 0x00, 0x05, 0x0, 0x36 },
+ { 0x03, 0x00, 0x05, 0x0, 0x35 },
+ { 0x03, 0x00, 0x05, 0x0, 0x33 },
+ { 0x03, 0x00, 0x04, 0x0, 0x3e },
+ { 0x03, 0x00, 0x04, 0x0, 0x3c },
+ { 0x03, 0x00, 0x04, 0x0, 0x3a },
+ { 0x03, 0x00, 0x04, 0x0, 0x39 },
+ { 0x03, 0x00, 0x04, 0x0, 0x37 },
+ { 0x03, 0x00, 0x04, 0x0, 0x36 },
+ { 0x03, 0x00, 0x04, 0x0, 0x34 },
+ { 0x03, 0x00, 0x04, 0x0, 0x33 },
+ { 0x03, 0x00, 0x04, 0x0, 0x31 },
+ { 0x03, 0x00, 0x04, 0x0, 0x30 },
+ { 0x03, 0x00, 0x04, 0x0, 0x2e },
+ { 0x03, 0x00, 0x03, 0x0, 0x3c },
+ { 0x03, 0x00, 0x03, 0x0, 0x3a },
+ { 0x03, 0x00, 0x03, 0x0, 0x39 },
+ { 0x03, 0x00, 0x03, 0x0, 0x37 },
+ { 0x03, 0x00, 0x03, 0x0, 0x36 },
+ { 0x03, 0x00, 0x03, 0x0, 0x34 },
+ { 0x03, 0x00, 0x03, 0x0, 0x33 },
+ { 0x03, 0x00, 0x03, 0x0, 0x31 },
+ { 0x03, 0x00, 0x03, 0x0, 0x30 },
+ { 0x03, 0x00, 0x03, 0x0, 0x2e },
+ { 0x03, 0x00, 0x03, 0x0, 0x2d },
+ { 0x03, 0x00, 0x03, 0x0, 0x2c },
+ { 0x03, 0x00, 0x03, 0x0, 0x2b },
+ { 0x03, 0x00, 0x03, 0x0, 0x29 },
+ { 0x03, 0x00, 0x02, 0x0, 0x3d },
+ { 0x03, 0x00, 0x02, 0x0, 0x3b },
+ { 0x03, 0x00, 0x02, 0x0, 0x39 },
+ { 0x03, 0x00, 0x02, 0x0, 0x38 },
+ { 0x03, 0x00, 0x02, 0x0, 0x36 },
+ { 0x03, 0x00, 0x02, 0x0, 0x35 },
+ { 0x03, 0x00, 0x02, 0x0, 0x33 },
+ { 0x03, 0x00, 0x02, 0x0, 0x32 },
+ { 0x03, 0x00, 0x02, 0x0, 0x30 },
+ { 0x03, 0x00, 0x02, 0x0, 0x2f },
+ { 0x03, 0x00, 0x02, 0x0, 0x2e },
+ { 0x03, 0x00, 0x02, 0x0, 0x2c },
+ { 0x03, 0x00, 0x02, 0x0, 0x2b },
+ { 0x03, 0x00, 0x02, 0x0, 0x2a },
+ { 0x03, 0x00, 0x02, 0x0, 0x29 },
+ { 0x03, 0x00, 0x02, 0x0, 0x27 },
+ { 0x03, 0x00, 0x02, 0x0, 0x26 },
+ { 0x03, 0x00, 0x02, 0x0, 0x25 },
+ { 0x03, 0x00, 0x02, 0x0, 0x24 },
+ { 0x03, 0x00, 0x02, 0x0, 0x23 },
+ { 0x03, 0x00, 0x02, 0x0, 0x22 },
+ { 0x03, 0x00, 0x02, 0x0, 0x21 },
+ { 0x03, 0x00, 0x02, 0x0, 0x20 },
+ { 0x03, 0x00, 0x01, 0x0, 0x3f },
+ { 0x03, 0x00, 0x01, 0x0, 0x3d },
+ { 0x03, 0x00, 0x01, 0x0, 0x3b },
+ { 0x03, 0x00, 0x01, 0x0, 0x39 },
+};
+
+/**************************************************
+ * SW control.
+ **************************************************/
+
+const u16 b43_lcntab_sw_ctl_4313_epa_rev0[] = {
+ 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
+ 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
+ 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
+ 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
+ 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
+ 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
+ 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
+ 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
+ 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
+ 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
+ 0x0002, 0x0008, 0x0004, 0x0001,
+};
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+u32 b43_lcntab_read(struct b43_wldev *dev, u32 offset)
+{
+ u32 type, value;
+
+ type = offset & B43_LCNTAB_TYPEMASK;
+ offset &= ~B43_LCNTAB_TYPEMASK;
+ B43_WARN_ON(offset > 0xFFFF);
+
+ switch (type) {
+ case B43_LCNTAB_8BIT:
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+ value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO) & 0xFF;
+ break;
+ case B43_LCNTAB_16BIT:
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+ value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO);
+ break;
+ case B43_LCNTAB_32BIT:
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+ value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO);
+ value |= (b43_phy_read(dev, B43_PHY_LCN_TABLE_DATAHI) << 16);
+ break;
+ default:
+ B43_WARN_ON(1);
+ value = 0;
+ }
+
+ return value;
+}
+
+void b43_lcntab_read_bulk(struct b43_wldev *dev, u32 offset,
+ unsigned int nr_elements, void *_data)
+{
+ u32 type;
+ u8 *data = _data;
+ unsigned int i;
+
+ type = offset & B43_LCNTAB_TYPEMASK;
+ offset &= ~B43_LCNTAB_TYPEMASK;
+ B43_WARN_ON(offset > 0xFFFF);
+
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+
+ for (i = 0; i < nr_elements; i++) {
+ switch (type) {
+ case B43_LCNTAB_8BIT:
+ *data = b43_phy_read(dev,
+ B43_PHY_LCN_TABLE_DATALO) & 0xFF;
+ data++;
+ break;
+ case B43_LCNTAB_16BIT:
+ *((u16 *)data) = b43_phy_read(dev,
+ B43_PHY_LCN_TABLE_DATALO);
+ data += 2;
+ break;
+ case B43_LCNTAB_32BIT:
+ *((u32 *)data) = b43_phy_read(dev,
+ B43_PHY_LCN_TABLE_DATALO);
+ *((u32 *)data) |= (b43_phy_read(dev,
+ B43_PHY_LCN_TABLE_DATAHI) << 16);
+ data += 4;
+ break;
+ default:
+ B43_WARN_ON(1);
+ }
+ }
+}
+
+void b43_lcntab_write(struct b43_wldev *dev, u32 offset, u32 value)
+{
+ u32 type;
+
+ type = offset & B43_LCNTAB_TYPEMASK;
+ offset &= 0xFFFF;
+
+ switch (type) {
+ case B43_LCNTAB_8BIT:
+ B43_WARN_ON(value & ~0xFF);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value);
+ break;
+ case B43_LCNTAB_16BIT:
+ B43_WARN_ON(value & ~0xFFFF);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value);
+ break;
+ case B43_LCNTAB_32BIT:
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, value >> 16);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value & 0xFFFF);
+ break;
+ default:
+ B43_WARN_ON(1);
+ }
+
+ return;
+}
+
+void b43_lcntab_write_bulk(struct b43_wldev *dev, u32 offset,
+ unsigned int nr_elements, const void *_data)
+{
+ u32 type, value;
+ const u8 *data = _data;
+ unsigned int i;
+
+ type = offset & B43_LCNTAB_TYPEMASK;
+ offset &= ~B43_LCNTAB_TYPEMASK;
+ B43_WARN_ON(offset > 0xFFFF);
+
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+
+ for (i = 0; i < nr_elements; i++) {
+ switch (type) {
+ case B43_LCNTAB_8BIT:
+ value = *data;
+ data++;
+ B43_WARN_ON(value & ~0xFF);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value);
+ break;
+ case B43_LCNTAB_16BIT:
+ value = *((u16 *)data);
+ data += 2;
+ B43_WARN_ON(value & ~0xFFFF);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value);
+ break;
+ case B43_LCNTAB_32BIT:
+ value = *((u32 *)data);
+ data += 4;
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI,
+ value >> 16);
+ b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO,
+ value & 0xFFFF);
+ break;
+ default:
+ B43_WARN_ON(1);
+ }
+ }
+}
+
+/**************************************************
+ * Tables ops.
+ **************************************************/
+
+#define lcntab_upload(dev, offset, data) do { \
+ b43_lcntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \
+ } while (0)
+static void b43_phy_lcn_upload_static_tables(struct b43_wldev *dev)
+{
+ lcntab_upload(dev, B43_LCNTAB16(0x02, 0), b43_lcntab_0x02);
+ lcntab_upload(dev, B43_LCNTAB16(0x01, 0), b43_lcntab_0x01);
+ lcntab_upload(dev, B43_LCNTAB32(0x0b, 0), b43_lcntab_0x0b);
+ lcntab_upload(dev, B43_LCNTAB32(0x0c, 0), b43_lcntab_0x0c);
+ lcntab_upload(dev, B43_LCNTAB32(0x0d, 0), b43_lcntab_0x0d);
+ lcntab_upload(dev, B43_LCNTAB16(0x0e, 0), b43_lcntab_0x0e);
+ lcntab_upload(dev, B43_LCNTAB16(0x0f, 0), b43_lcntab_0x0f);
+ lcntab_upload(dev, B43_LCNTAB16(0x10, 0), b43_lcntab_0x10);
+ lcntab_upload(dev, B43_LCNTAB16(0x11, 0), b43_lcntab_0x11);
+ lcntab_upload(dev, B43_LCNTAB32(0x12, 0), b43_lcntab_0x12);
+ lcntab_upload(dev, B43_LCNTAB16(0x14, 0), b43_lcntab_0x14);
+ lcntab_upload(dev, B43_LCNTAB16(0x17, 0), b43_lcntab_0x17);
+ lcntab_upload(dev, B43_LCNTAB16(0x00, 0), b43_lcntab_0x00);
+ lcntab_upload(dev, B43_LCNTAB32(0x18, 0), b43_lcntab_0x18);
+}
+
+void b43_phy_lcn_load_tx_gain_tab(struct b43_wldev *dev,
+ const struct b43_lcntab_tx_gain_tbl_entry *gain_table)
+{
+ u32 i;
+ u32 val;
+
+ u16 pa_gain = 0x70;
+ if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM)
+ pa_gain = 0x10;
+
+ for (i = 0; i < B43_LCNTAB_TX_GAIN_SIZE; i++) {
+ val = ((pa_gain << 24) |
+ (gain_table[i].pad << 16) |
+ (gain_table[i].pga << 8) |
+ gain_table[i].gm);
+ b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0xc0 + i), val);
+
+ /* brcmsmac doesn't maskset, we follow newer wl here */
+ val = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i));
+ val &= 0x000fffff;
+ val |= ((gain_table[i].dac << 28) |
+ (gain_table[i].bb_mult << 20));
+ b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x140 + i), val);
+ }
+}
+
+/* wlc_lcnphy_load_rfpower */
+static void b43_phy_lcn_load_rfpower(struct b43_wldev *dev)
+{
+ u32 bbmult, rfgain;
+ u8 i;
+
+ for (i = 0; i < 128; i++) {
+ bbmult = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i));
+ bbmult >>= 20;
+ rfgain = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0xc0 + i));
+
+ /* TODO: calculate value for 0x240 + i table offset
+ * b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), val);
+ */
+ }
+}
+
+/* Not implemented in brcmsmac, noticed in wl in MMIO dump */
+static void b43_phy_lcn_rewrite_rfpower_table(struct b43_wldev *dev)
+{
+ int i;
+ u32 tmp;
+ for (i = 0; i < 128; i++) {
+ tmp = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x240 + i));
+ b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), tmp);
+ }
+}
+
+/* wlc_lcnphy_clear_papd_comptable */
+static void b43_phy_lcn_clean_papd_comp_table(struct b43_wldev *dev)
+{
+ u8 i;
+
+ for (i = 0; i < 0x80; i++)
+ b43_lcntab_write(dev, B43_LCNTAB32(0x18, i), 0x80000);
+}
+
+/* wlc_lcnphy_tbl_init */
+void b43_phy_lcn_tables_init(struct b43_wldev *dev)
+{
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
+
+ b43_phy_lcn_upload_static_tables(dev);
+
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+ if (sprom->boardflags_lo & B43_BFL_FEM)
+ b43_phy_lcn_load_tx_gain_tab(dev,
+ b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0);
+ else
+ b43err(dev->wl,
+ "TX gain table unknown for this card\n");
+ }
+
+ if (sprom->boardflags_lo & B43_BFL_FEM &&
+ !(sprom->boardflags_hi & B43_BFH_FEM_BT))
+ b43_lcntab_write_bulk(dev, B43_LCNTAB16(0xf, 0),
+ ARRAY_SIZE(b43_lcntab_sw_ctl_4313_epa_rev0),
+ b43_lcntab_sw_ctl_4313_epa_rev0);
+ else
+ b43err(dev->wl, "SW ctl table is unknown for this card\n");
+
+ b43_phy_lcn_load_rfpower(dev);
+ b43_phy_lcn_rewrite_rfpower_table(dev);
+ b43_phy_lcn_clean_papd_comp_table(dev);
+}
diff --git a/drivers/net/wireless/b43/tables_phy_lcn.h b/drivers/net/wireless/b43/tables_phy_lcn.h
new file mode 100644
index 000000000000..caff9db6831f
--- /dev/null
+++ b/drivers/net/wireless/b43/tables_phy_lcn.h
@@ -0,0 +1,24 @@
+#ifndef B43_TABLES_PHY_LCN_H_
+#define B43_TABLES_PHY_LCN_H_
+
+/* The LCN-PHY tables. */
+#define B43_LCNTAB_TYPEMASK 0xF0000000
+#define B43_LCNTAB_8BIT 0x10000000
+#define B43_LCNTAB_16BIT 0x20000000
+#define B43_LCNTAB_32BIT 0x30000000
+#define B43_LCNTAB8(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_8BIT)
+#define B43_LCNTAB16(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_16BIT)
+#define B43_LCNTAB32(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_32BIT)
+
+#define B43_LCNTAB_TX_GAIN_SIZE 128
+
+u32 b43_lcntab_read(struct b43_wldev *dev, u32 offset);
+void b43_lcntab_read_bulk(struct b43_wldev *dev, u32 offset,
+ unsigned int nr_elements, void *_data);
+void b43_lcntab_write(struct b43_wldev *dev, u32 offset, u32 value);
+void b43_lcntab_write_bulk(struct b43_wldev *dev, u32 offset,
+ unsigned int nr_elements, const void *_data);
+
+void b43_phy_lcn_tables_init(struct b43_wldev *dev);
+
+#endif /* B43_TABLES_PHY_LCN_H_ */
diff --git a/drivers/net/wireless/b43/wa.c b/drivers/net/wireless/b43/wa.c
index 8f4db448ec33..9b1a038be08b 100644
--- a/drivers/net/wireless/b43/wa.c
+++ b/drivers/net/wireless/b43/wa.c
@@ -5,7 +5,7 @@
PHY workarounds.
Copyright (c) 2005-2007 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (c) 2005-2007 Michael Buesch <mbuesch@freenet.de>
+ Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
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
@@ -458,17 +458,15 @@ static void b43_wa_rssi_adc(struct b43_wldev *dev)
static void b43_wa_boards_a(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
-
- if (bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM &&
- bus->boardinfo.type == SSB_BOARD_BU4306 &&
- bus->boardinfo.rev < 0x30) {
+ if (dev->dev->board_vendor == SSB_BOARDVENDOR_BCM &&
+ dev->dev->board_type == SSB_BOARD_BU4306 &&
+ dev->dev->board_rev < 0x30) {
b43_phy_write(dev, 0x0010, 0xE000);
b43_phy_write(dev, 0x0013, 0x0140);
b43_phy_write(dev, 0x0014, 0x0280);
} else {
- if (bus->boardinfo.type == SSB_BOARD_MP4318 &&
- bus->boardinfo.rev < 0x20) {
+ if (dev->dev->board_type == SSB_BOARD_MP4318 &&
+ dev->dev->board_rev < 0x20) {
b43_phy_write(dev, 0x0013, 0x0210);
b43_phy_write(dev, 0x0014, 0x0840);
} else {
@@ -486,19 +484,19 @@ static void b43_wa_boards_a(struct b43_wldev *dev)
static void b43_wa_boards_g(struct b43_wldev *dev)
{
- struct ssb_bus *bus = dev->sdev->bus;
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
struct b43_phy *phy = &dev->phy;
- if (bus->boardinfo.vendor != SSB_BOARDVENDOR_BCM ||
- bus->boardinfo.type != SSB_BOARD_BU4306 ||
- bus->boardinfo.rev != 0x17) {
+ if (dev->dev->board_vendor != SSB_BOARDVENDOR_BCM ||
+ dev->dev->board_type != SSB_BOARD_BU4306 ||
+ dev->dev->board_rev != 0x17) {
if (phy->rev < 2) {
b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX_R1, 1, 0x0002);
b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX_R1, 2, 0x0001);
} else {
b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 1, 0x0002);
b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 2, 0x0001);
- if ((bus->sprom.boardflags_lo & B43_BFL_EXTLNA) &&
+ if ((sprom->boardflags_lo & B43_BFL_EXTLNA) &&
(phy->rev >= 7)) {
b43_phy_mask(dev, B43_PHY_EXTG(0x11), 0xF7FF);
b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0020, 0x0001);
@@ -510,7 +508,7 @@ static void b43_wa_boards_g(struct b43_wldev *dev)
}
}
}
- if (bus->sprom.boardflags_lo & B43_BFL_FEM) {
+ if (sprom->boardflags_lo & B43_BFL_FEM) {
b43_phy_write(dev, B43_PHY_GTABCTL, 0x3120);
b43_phy_write(dev, B43_PHY_GTABDATA, 0xC480);
}
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index c8f99aebe01f..5f77cbe0b6aa 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -6,7 +6,7 @@
Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
Copyright (C) 2005 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (C) 2005, 2006 Michael Buesch <mb@bu3sch.de>
+ Copyright (C) 2005, 2006 Michael Buesch <m@bues.ch>
Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
@@ -175,6 +175,7 @@ void b43_generate_plcp_hdr(struct b43_plcp_hdr4 *plcp,
}
}
+/* TODO: verify if needed for SSLPN or LCN */
static u16 b43_generate_tx_phy_ctl1(struct b43_wldev *dev, u8 bitrate)
{
const struct b43_phy *phy = &dev->phy;
@@ -256,6 +257,9 @@ int b43_generate_txhdr(struct b43_wldev *dev,
unsigned int plcp_fragment_len;
u32 mac_ctl = 0;
u16 phy_ctl = 0;
+ bool fill_phy_ctl1 = (phy->type == B43_PHYTYPE_LP ||
+ phy->type == B43_PHYTYPE_N ||
+ phy->type == B43_PHYTYPE_HT);
u8 extra_ft = 0;
struct ieee80211_rate *txrate;
struct ieee80211_tx_rate *rates;
@@ -323,8 +327,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
/* we give the phase1key and iv16 here, the key is stored in
* shm. With that the hardware can do phase 2 and encryption.
*/
- ieee80211_get_tkip_key(info->control.hw_key, skb_frag,
- IEEE80211_TKIP_P1_KEY, (u8*)phase1key);
+ ieee80211_get_tkip_p1k(info->control.hw_key, skb_frag, phase1key);
/* phase1key is in host endian. Copy to little-endian txhdr->iv. */
for (i = 0; i < 5; i++) {
txhdr->iv[i * 2 + 0] = phase1key[i];
@@ -338,12 +341,19 @@ int b43_generate_txhdr(struct b43_wldev *dev,
memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len);
}
}
- if (b43_is_old_txhdr_format(dev)) {
- b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->old_format.plcp),
+ switch (dev->fw.hdr_format) {
+ case B43_FW_HDR_598:
+ b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_598.plcp),
plcp_fragment_len, rate);
- } else {
- b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->new_format.plcp),
+ break;
+ case B43_FW_HDR_351:
+ b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_351.plcp),
+ plcp_fragment_len, rate);
+ break;
+ case B43_FW_HDR_410:
+ b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_410.plcp),
plcp_fragment_len, rate);
+ break;
}
b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->plcp_fb),
plcp_fragment_len, rate_fb);
@@ -416,10 +426,10 @@ int b43_generate_txhdr(struct b43_wldev *dev,
if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
(rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) {
unsigned int len;
- struct ieee80211_hdr *hdr;
+ struct ieee80211_hdr *uninitialized_var(hdr);
int rts_rate, rts_rate_fb;
int rts_rate_ofdm, rts_rate_fb_ofdm;
- struct b43_plcp_hdr6 *plcp;
+ struct b43_plcp_hdr6 *uninitialized_var(plcp);
struct ieee80211_rate *rts_cts_rate;
rts_cts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info);
@@ -430,14 +440,21 @@ int b43_generate_txhdr(struct b43_wldev *dev,
rts_rate_fb_ofdm = b43_is_ofdm_rate(rts_rate_fb);
if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
- struct ieee80211_cts *cts;
+ struct ieee80211_cts *uninitialized_var(cts);
- if (b43_is_old_txhdr_format(dev)) {
+ switch (dev->fw.hdr_format) {
+ case B43_FW_HDR_598:
cts = (struct ieee80211_cts *)
- (txhdr->old_format.rts_frame);
- } else {
+ (txhdr->format_598.rts_frame);
+ break;
+ case B43_FW_HDR_351:
cts = (struct ieee80211_cts *)
- (txhdr->new_format.rts_frame);
+ (txhdr->format_351.rts_frame);
+ break;
+ case B43_FW_HDR_410:
+ cts = (struct ieee80211_cts *)
+ (txhdr->format_410.rts_frame);
+ break;
}
ieee80211_ctstoself_get(dev->wl->hw, info->control.vif,
fragment_data, fragment_len,
@@ -445,14 +462,21 @@ int b43_generate_txhdr(struct b43_wldev *dev,
mac_ctl |= B43_TXH_MAC_SENDCTS;
len = sizeof(struct ieee80211_cts);
} else {
- struct ieee80211_rts *rts;
+ struct ieee80211_rts *uninitialized_var(rts);
- if (b43_is_old_txhdr_format(dev)) {
+ switch (dev->fw.hdr_format) {
+ case B43_FW_HDR_598:
rts = (struct ieee80211_rts *)
- (txhdr->old_format.rts_frame);
- } else {
+ (txhdr->format_598.rts_frame);
+ break;
+ case B43_FW_HDR_351:
rts = (struct ieee80211_rts *)
- (txhdr->new_format.rts_frame);
+ (txhdr->format_351.rts_frame);
+ break;
+ case B43_FW_HDR_410:
+ rts = (struct ieee80211_rts *)
+ (txhdr->format_410.rts_frame);
+ break;
}
ieee80211_rts_get(dev->wl->hw, info->control.vif,
fragment_data, fragment_len,
@@ -463,22 +487,36 @@ int b43_generate_txhdr(struct b43_wldev *dev,
len += FCS_LEN;
/* Generate the PLCP headers for the RTS/CTS frame */
- if (b43_is_old_txhdr_format(dev))
- plcp = &txhdr->old_format.rts_plcp;
- else
- plcp = &txhdr->new_format.rts_plcp;
+ switch (dev->fw.hdr_format) {
+ case B43_FW_HDR_598:
+ plcp = &txhdr->format_598.rts_plcp;
+ break;
+ case B43_FW_HDR_351:
+ plcp = &txhdr->format_351.rts_plcp;
+ break;
+ case B43_FW_HDR_410:
+ plcp = &txhdr->format_410.rts_plcp;
+ break;
+ }
b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)plcp,
len, rts_rate);
plcp = &txhdr->rts_plcp_fb;
b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)plcp,
len, rts_rate_fb);
- if (b43_is_old_txhdr_format(dev)) {
+ switch (dev->fw.hdr_format) {
+ case B43_FW_HDR_598:
hdr = (struct ieee80211_hdr *)
- (&txhdr->old_format.rts_frame);
- } else {
+ (&txhdr->format_598.rts_frame);
+ break;
+ case B43_FW_HDR_351:
hdr = (struct ieee80211_hdr *)
- (&txhdr->new_format.rts_frame);
+ (&txhdr->format_351.rts_frame);
+ break;
+ case B43_FW_HDR_410:
+ hdr = (struct ieee80211_hdr *)
+ (&txhdr->format_410.rts_frame);
+ break;
}
txhdr->rts_dur_fb = hdr->duration_id;
@@ -497,7 +535,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
extra_ft |= B43_TXH_EFT_RTSFB_CCK;
if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS &&
- phy->type == B43_PHYTYPE_N) {
+ fill_phy_ctl1) {
txhdr->phy_ctl1_rts = cpu_to_le16(
b43_generate_tx_phy_ctl1(dev, rts_rate));
txhdr->phy_ctl1_rts_fb = cpu_to_le16(
@@ -506,12 +544,19 @@ int b43_generate_txhdr(struct b43_wldev *dev,
}
/* Magic cookie */
- if (b43_is_old_txhdr_format(dev))
- txhdr->old_format.cookie = cpu_to_le16(cookie);
- else
- txhdr->new_format.cookie = cpu_to_le16(cookie);
+ switch (dev->fw.hdr_format) {
+ case B43_FW_HDR_598:
+ txhdr->format_598.cookie = cpu_to_le16(cookie);
+ break;
+ case B43_FW_HDR_351:
+ txhdr->format_351.cookie = cpu_to_le16(cookie);
+ break;
+ case B43_FW_HDR_410:
+ txhdr->format_410.cookie = cpu_to_le16(cookie);
+ break;
+ }
- if (phy->type == B43_PHYTYPE_N) {
+ if (fill_phy_ctl1) {
txhdr->phy_ctl1 =
cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate));
txhdr->phy_ctl1_fb =
@@ -547,7 +592,7 @@ static s8 b43_rssi_postprocess(struct b43_wldev *dev,
else
tmp -= 3;
} else {
- if (dev->sdev->bus->sprom.
+ if (dev->dev->bus_sprom->
boardflags_lo & B43_BFL_RSSI) {
if (in_rssi > 63)
in_rssi = 63;
@@ -612,8 +657,9 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
struct ieee80211_hdr *wlhdr;
const struct b43_rxhdr_fw4 *rxhdr = _rxhdr;
__le16 fctl;
- u16 phystat0, phystat3, chanstat, mactime;
- u32 macstat;
+ u16 phystat0, phystat3;
+ u16 uninitialized_var(chanstat), uninitialized_var(mactime);
+ u32 uninitialized_var(macstat);
u16 chanid;
u16 phytype;
int padding;
@@ -623,9 +669,19 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
/* Get metadata about the frame from the header. */
phystat0 = le16_to_cpu(rxhdr->phy_status0);
phystat3 = le16_to_cpu(rxhdr->phy_status3);
- macstat = le32_to_cpu(rxhdr->mac_status);
- mactime = le16_to_cpu(rxhdr->mac_time);
- chanstat = le16_to_cpu(rxhdr->channel);
+ switch (dev->fw.hdr_format) {
+ case B43_FW_HDR_598:
+ macstat = le32_to_cpu(rxhdr->format_598.mac_status);
+ mactime = le16_to_cpu(rxhdr->format_598.mac_time);
+ chanstat = le16_to_cpu(rxhdr->format_598.channel);
+ break;
+ case B43_FW_HDR_410:
+ case B43_FW_HDR_351:
+ macstat = le32_to_cpu(rxhdr->format_351.mac_status);
+ mactime = le16_to_cpu(rxhdr->format_351.mac_time);
+ chanstat = le16_to_cpu(rxhdr->format_351.channel);
+ break;
+ }
phytype = chanstat & B43_RX_CHAN_PHYTYPE;
if (unlikely(macstat & B43_RX_MAC_FCSERR)) {
@@ -683,16 +739,29 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
}
/* Link quality statistics */
- if ((chanstat & B43_RX_CHAN_PHYTYPE) == B43_PHYTYPE_N) {
-// s8 rssi = max(rxhdr->power0, rxhdr->power1);
- //TODO: Find out what the rssi value is (dBm or percentage?)
- // and also find out what the maximum possible value is.
- // Fill status.ssi and status.signal fields.
- } else {
+ switch (chanstat & B43_RX_CHAN_PHYTYPE) {
+ case B43_PHYTYPE_HT:
+ /* TODO: is max the right choice? */
+ status.signal = max_t(__s8,
+ max(rxhdr->phy_ht_power0, rxhdr->phy_ht_power1),
+ rxhdr->phy_ht_power2);
+ break;
+ case B43_PHYTYPE_N:
+ /* Broadcom has code for min and avg, but always uses max */
+ if (rxhdr->power0 == 16 || rxhdr->power0 == 32)
+ status.signal = max(rxhdr->power1, rxhdr->power2);
+ else
+ status.signal = max(rxhdr->power0, rxhdr->power1);
+ break;
+ case B43_PHYTYPE_A:
+ case B43_PHYTYPE_B:
+ case B43_PHYTYPE_G:
+ case B43_PHYTYPE_LP:
status.signal = b43_rssi_postprocess(dev, rxhdr->jssi,
(phystat0 & B43_RX_PHYST0_OFDM),
(phystat0 & B43_RX_PHYST0_GAINCTL),
(phystat3 & B43_RX_PHYST3_TRSTATE));
+ break;
}
if (phystat0 & B43_RX_PHYST0_OFDM)
@@ -745,6 +814,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
break;
case B43_PHYTYPE_N:
case B43_PHYTYPE_LP:
+ case B43_PHYTYPE_HT:
/* chanid is the SHM channel cookie. Which is the plain
* channel number in b43. */
if (chanstat & B43_RX_CHAN_5GHZ) {
@@ -768,7 +838,6 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
#endif
return;
drop:
- b43dbg(dev->wl, "RX: Packet dropped\n");
dev_kfree_skb_any(skb);
}
diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/b43/xmit.h
index 42debb5cd6fa..98d90747836a 100644
--- a/drivers/net/wireless/b43/xmit.h
+++ b/drivers/net/wireless/b43/xmit.h
@@ -46,7 +46,24 @@ struct b43_txhdr {
__le32 timeout; /* Timeout */
union {
- /* The new r410 format. */
+ /* Tested with 598.314, 644.1001 and 666.2 */
+ struct {
+ __le16 mimo_antenna; /* MIMO antenna select */
+ __le16 preload_size; /* Preload size */
+ PAD_BYTES(2);
+ __le16 cookie; /* TX frame cookie */
+ __le16 tx_status; /* TX status */
+ __le16 max_n_mpdus;
+ __le16 max_a_bytes_mrt;
+ __le16 max_a_bytes_fbr;
+ __le16 min_m_bytes;
+ struct b43_plcp_hdr6 rts_plcp; /* RTS PLCP header */
+ __u8 rts_frame[16]; /* The RTS frame (if used) */
+ PAD_BYTES(2);
+ struct b43_plcp_hdr6 plcp; /* Main PLCP header */
+ } format_598 __packed;
+
+ /* Tested with 410.2160, 478.104 and 508.* */
struct {
__le16 mimo_antenna; /* MIMO antenna select */
__le16 preload_size; /* Preload size */
@@ -57,9 +74,9 @@ struct b43_txhdr {
__u8 rts_frame[16]; /* The RTS frame (if used) */
PAD_BYTES(2);
struct b43_plcp_hdr6 plcp; /* Main PLCP header */
- } new_format __packed;
+ } format_410 __packed;
- /* The old r351 format. */
+ /* Tested with 351.126 */
struct {
PAD_BYTES(2);
__le16 cookie; /* TX frame cookie */
@@ -68,7 +85,7 @@ struct b43_txhdr {
__u8 rts_frame[16]; /* The RTS frame (if used) */
PAD_BYTES(2);
struct b43_plcp_hdr6 plcp; /* Main PLCP header */
- } old_format __packed;
+ } format_351 __packed;
} __packed;
} __packed;
@@ -166,19 +183,18 @@ struct b43_tx_legacy_rate_phy_ctl_entry {
#define B43_TXH_PHY1_MODUL_QAM256 0x2000 /* QAM256 */
-/* r351 firmware compatibility stuff. */
-static inline
-bool b43_is_old_txhdr_format(struct b43_wldev *dev)
-{
- return (dev->fw.rev <= 351);
-}
-
static inline
size_t b43_txhdr_size(struct b43_wldev *dev)
{
- if (b43_is_old_txhdr_format(dev))
+ switch (dev->fw.hdr_format) {
+ case B43_FW_HDR_598:
+ return 112 + sizeof(struct b43_plcp_hdr6);
+ case B43_FW_HDR_410:
+ return 104 + sizeof(struct b43_plcp_hdr6);
+ case B43_FW_HDR_351:
return 100 + sizeof(struct b43_plcp_hdr6);
- return 104 + sizeof(struct b43_plcp_hdr6);
+ }
+ return 0;
}
@@ -232,11 +248,47 @@ struct b43_rxhdr_fw4 {
__s8 power1; /* PHY RX Status 1: Power 1 */
} __packed;
} __packed;
- __le16 phy_status2; /* PHY RX Status 2 */
- __le16 phy_status3; /* PHY RX Status 3 */
- __le32 mac_status; /* MAC RX status */
- __le16 mac_time;
- __le16 channel;
+ union {
+ /* HT-PHY */
+ struct {
+ PAD_BYTES(1);
+ __s8 phy_ht_power0;
+ } __packed;
+
+ /* RSSI for N-PHYs */
+ struct {
+ __s8 power2;
+ PAD_BYTES(1);
+ } __packed;
+
+ __le16 phy_status2; /* PHY RX Status 2 */
+ } __packed;
+ union {
+ /* HT-PHY */
+ struct {
+ __s8 phy_ht_power1;
+ __s8 phy_ht_power2;
+ } __packed;
+
+ __le16 phy_status3; /* PHY RX Status 3 */
+ } __packed;
+ union {
+ /* Tested with 598.314, 644.1001 and 666.2 */
+ struct {
+ __le16 phy_status4; /* PHY RX Status 4 */
+ __le16 phy_status5; /* PHY RX Status 5 */
+ __le32 mac_status; /* MAC RX status */
+ __le16 mac_time;
+ __le16 channel;
+ } format_598 __packed;
+
+ /* Tested with 351.126, 410.2160, 478.104 and 508.* */
+ struct {
+ __le32 mac_status; /* MAC RX status */
+ __le16 mac_time;
+ __le16 channel;
+ } format_351 __packed;
+ } __packed;
} __packed;
/* PHY RX Status 0 */
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h
index 23583be1ee0b..1d4fc9db7f5e 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/b43legacy/b43legacy.h
@@ -8,13 +8,12 @@
#include <linux/stringify.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/io.h>
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_driver_chipcommon.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include "debugfs.h"
@@ -23,10 +22,6 @@
#include "phy.h"
-/* The unique identifier of the firmware that's officially supported by this
- * driver version. */
-#define B43legacy_SUPPORTED_FIRMWARE_ID "FW10"
-
#define B43legacy_IRQWAIT_MAX_RETRIES 20
/* MMIO offsets */
@@ -532,6 +527,8 @@ struct b43legacy_dma {
struct b43legacy_dmaring *rx_ring0;
struct b43legacy_dmaring *rx_ring3; /* only on core.rev < 5 */
+
+ u32 translation; /* Routing bits */
};
/* Data structures for PIO transmission, per 80211 core. */
@@ -813,15 +810,15 @@ struct b43legacy_lopair *b43legacy_get_lopair(struct b43legacy_phy *phy,
/* Message printing */
-void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
-void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
-void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
+__printf(2, 3)
+void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...);
+__printf(2, 3)
+void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...);
+__printf(2, 3)
+void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...);
#if B43legacy_DEBUG
-void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
+__printf(2, 3)
+void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...);
#else /* DEBUG */
# define b43legacydbg(wl, fmt...) do { /* nothing */ } while (0)
#endif /* DEBUG */
diff --git a/drivers/net/wireless/b43legacy/debugfs.c b/drivers/net/wireless/b43legacy/debugfs.c
index f232618f2cd1..5e28ad0d6d17 100644
--- a/drivers/net/wireless/b43legacy/debugfs.c
+++ b/drivers/net/wireless/b43legacy/debugfs.c
@@ -4,7 +4,7 @@
debugfs driver debugging code
- Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
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
diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c
index e03e01d0bc35..c5535adf6991 100644
--- a/drivers/net/wireless/b43legacy/dma.c
+++ b/drivers/net/wireless/b43legacy/dma.c
@@ -4,7 +4,7 @@
DMA ringbuffer and descriptor allocation/management
- Copyright (c) 2005, 2006 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005, 2006 Michael Buesch <m@bues.ch>
Some code in this file is derived from the b44.c driver
Copyright (C) 2002 David S. Miller
@@ -42,10 +42,9 @@
/* 32bit DMA ops. */
static
-struct b43legacy_dmadesc_generic *op32_idx2desc(
- struct b43legacy_dmaring *ring,
- int slot,
- struct b43legacy_dmadesc_meta **meta)
+struct b43legacy_dmadesc32 *op32_idx2desc(struct b43legacy_dmaring *ring,
+ int slot,
+ struct b43legacy_dmadesc_meta **meta)
{
struct b43legacy_dmadesc32 *desc;
@@ -53,11 +52,11 @@ struct b43legacy_dmadesc_generic *op32_idx2desc(
desc = ring->descbase;
desc = &(desc[slot]);
- return (struct b43legacy_dmadesc_generic *)desc;
+ return (struct b43legacy_dmadesc32 *)desc;
}
static void op32_fill_descriptor(struct b43legacy_dmaring *ring,
- struct b43legacy_dmadesc_generic *desc,
+ struct b43legacy_dmadesc32 *desc,
dma_addr_t dmaaddr, u16 bufsize,
int start, int end, int irq)
{
@@ -67,13 +66,13 @@ static void op32_fill_descriptor(struct b43legacy_dmaring *ring,
u32 addr;
u32 addrext;
- slot = (int)(&(desc->dma32) - descbase);
+ slot = (int)(desc - descbase);
B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
addr = (u32)(dmaaddr & ~SSB_DMA_TRANSLATION_MASK);
addrext = (u32)(dmaaddr & SSB_DMA_TRANSLATION_MASK)
>> SSB_DMA_TRANSLATION_SHIFT;
- addr |= ssb_dma_translation(ring->dev->dev);
+ addr |= ring->dev->dma.translation;
ctl = (bufsize - ring->frameoffset)
& B43legacy_DMA32_DCTL_BYTECNT;
if (slot == ring->nr_slots - 1)
@@ -87,8 +86,8 @@ static void op32_fill_descriptor(struct b43legacy_dmaring *ring,
ctl |= (addrext << B43legacy_DMA32_DCTL_ADDREXT_SHIFT)
& B43legacy_DMA32_DCTL_ADDREXT_MASK;
- desc->dma32.control = cpu_to_le32(ctl);
- desc->dma32.address = cpu_to_le32(addr);
+ desc->control = cpu_to_le32(ctl);
+ desc->address = cpu_to_le32(addr);
}
static void op32_poke_tx(struct b43legacy_dmaring *ring, int slot)
@@ -128,121 +127,6 @@ static void op32_set_current_rxslot(struct b43legacy_dmaring *ring,
(u32)(slot * sizeof(struct b43legacy_dmadesc32)));
}
-static const struct b43legacy_dma_ops dma32_ops = {
- .idx2desc = op32_idx2desc,
- .fill_descriptor = op32_fill_descriptor,
- .poke_tx = op32_poke_tx,
- .tx_suspend = op32_tx_suspend,
- .tx_resume = op32_tx_resume,
- .get_current_rxslot = op32_get_current_rxslot,
- .set_current_rxslot = op32_set_current_rxslot,
-};
-
-/* 64bit DMA ops. */
-static
-struct b43legacy_dmadesc_generic *op64_idx2desc(
- struct b43legacy_dmaring *ring,
- int slot,
- struct b43legacy_dmadesc_meta
- **meta)
-{
- struct b43legacy_dmadesc64 *desc;
-
- *meta = &(ring->meta[slot]);
- desc = ring->descbase;
- desc = &(desc[slot]);
-
- return (struct b43legacy_dmadesc_generic *)desc;
-}
-
-static void op64_fill_descriptor(struct b43legacy_dmaring *ring,
- struct b43legacy_dmadesc_generic *desc,
- dma_addr_t dmaaddr, u16 bufsize,
- int start, int end, int irq)
-{
- struct b43legacy_dmadesc64 *descbase = ring->descbase;
- int slot;
- u32 ctl0 = 0;
- u32 ctl1 = 0;
- u32 addrlo;
- u32 addrhi;
- u32 addrext;
-
- slot = (int)(&(desc->dma64) - descbase);
- B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
-
- addrlo = (u32)(dmaaddr & 0xFFFFFFFF);
- addrhi = (((u64)dmaaddr >> 32) & ~SSB_DMA_TRANSLATION_MASK);
- addrext = (((u64)dmaaddr >> 32) & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
- addrhi |= ssb_dma_translation(ring->dev->dev);
- if (slot == ring->nr_slots - 1)
- ctl0 |= B43legacy_DMA64_DCTL0_DTABLEEND;
- if (start)
- ctl0 |= B43legacy_DMA64_DCTL0_FRAMESTART;
- if (end)
- ctl0 |= B43legacy_DMA64_DCTL0_FRAMEEND;
- if (irq)
- ctl0 |= B43legacy_DMA64_DCTL0_IRQ;
- ctl1 |= (bufsize - ring->frameoffset)
- & B43legacy_DMA64_DCTL1_BYTECNT;
- ctl1 |= (addrext << B43legacy_DMA64_DCTL1_ADDREXT_SHIFT)
- & B43legacy_DMA64_DCTL1_ADDREXT_MASK;
-
- desc->dma64.control0 = cpu_to_le32(ctl0);
- desc->dma64.control1 = cpu_to_le32(ctl1);
- desc->dma64.address_low = cpu_to_le32(addrlo);
- desc->dma64.address_high = cpu_to_le32(addrhi);
-}
-
-static void op64_poke_tx(struct b43legacy_dmaring *ring, int slot)
-{
- b43legacy_dma_write(ring, B43legacy_DMA64_TXINDEX,
- (u32)(slot * sizeof(struct b43legacy_dmadesc64)));
-}
-
-static void op64_tx_suspend(struct b43legacy_dmaring *ring)
-{
- b43legacy_dma_write(ring, B43legacy_DMA64_TXCTL,
- b43legacy_dma_read(ring, B43legacy_DMA64_TXCTL)
- | B43legacy_DMA64_TXSUSPEND);
-}
-
-static void op64_tx_resume(struct b43legacy_dmaring *ring)
-{
- b43legacy_dma_write(ring, B43legacy_DMA64_TXCTL,
- b43legacy_dma_read(ring, B43legacy_DMA64_TXCTL)
- & ~B43legacy_DMA64_TXSUSPEND);
-}
-
-static int op64_get_current_rxslot(struct b43legacy_dmaring *ring)
-{
- u32 val;
-
- val = b43legacy_dma_read(ring, B43legacy_DMA64_RXSTATUS);
- val &= B43legacy_DMA64_RXSTATDPTR;
-
- return (val / sizeof(struct b43legacy_dmadesc64));
-}
-
-static void op64_set_current_rxslot(struct b43legacy_dmaring *ring,
- int slot)
-{
- b43legacy_dma_write(ring, B43legacy_DMA64_RXINDEX,
- (u32)(slot * sizeof(struct b43legacy_dmadesc64)));
-}
-
-static const struct b43legacy_dma_ops dma64_ops = {
- .idx2desc = op64_idx2desc,
- .fill_descriptor = op64_fill_descriptor,
- .poke_tx = op64_poke_tx,
- .tx_suspend = op64_tx_suspend,
- .tx_resume = op64_tx_resume,
- .get_current_rxslot = op64_get_current_rxslot,
- .set_current_rxslot = op64_set_current_rxslot,
-};
-
-
static inline int free_slots(struct b43legacy_dmaring *ring)
{
return (ring->nr_slots - ring->used_slots);
@@ -358,14 +242,6 @@ return 0;
static u16 b43legacy_dmacontroller_base(enum b43legacy_dmatype type,
int controller_idx)
{
- static const u16 map64[] = {
- B43legacy_MMIO_DMA64_BASE0,
- B43legacy_MMIO_DMA64_BASE1,
- B43legacy_MMIO_DMA64_BASE2,
- B43legacy_MMIO_DMA64_BASE3,
- B43legacy_MMIO_DMA64_BASE4,
- B43legacy_MMIO_DMA64_BASE5,
- };
static const u16 map32[] = {
B43legacy_MMIO_DMA32_BASE0,
B43legacy_MMIO_DMA32_BASE1,
@@ -375,11 +251,6 @@ static u16 b43legacy_dmacontroller_base(enum b43legacy_dmatype type,
B43legacy_MMIO_DMA32_BASE5,
};
- if (type == B43legacy_DMA_64BIT) {
- B43legacy_WARN_ON(!(controller_idx >= 0 &&
- controller_idx < ARRAY_SIZE(map64)));
- return map64[controller_idx];
- }
B43legacy_WARN_ON(!(controller_idx >= 0 &&
controller_idx < ARRAY_SIZE(map32)));
return map32[controller_idx];
@@ -491,25 +362,15 @@ static int b43legacy_dmacontroller_rx_reset(struct b43legacy_wldev *dev,
might_sleep();
- offset = (type == B43legacy_DMA_64BIT) ?
- B43legacy_DMA64_RXCTL : B43legacy_DMA32_RXCTL;
+ offset = B43legacy_DMA32_RXCTL;
b43legacy_write32(dev, mmio_base + offset, 0);
for (i = 0; i < 10; i++) {
- offset = (type == B43legacy_DMA_64BIT) ?
- B43legacy_DMA64_RXSTATUS : B43legacy_DMA32_RXSTATUS;
+ offset = B43legacy_DMA32_RXSTATUS;
value = b43legacy_read32(dev, mmio_base + offset);
- if (type == B43legacy_DMA_64BIT) {
- value &= B43legacy_DMA64_RXSTAT;
- if (value == B43legacy_DMA64_RXSTAT_DISABLED) {
- i = -1;
- break;
- }
- } else {
- value &= B43legacy_DMA32_RXSTATE;
- if (value == B43legacy_DMA32_RXSTAT_DISABLED) {
- i = -1;
- break;
- }
+ value &= B43legacy_DMA32_RXSTATE;
+ if (value == B43legacy_DMA32_RXSTAT_DISABLED) {
+ i = -1;
+ break;
}
msleep(1);
}
@@ -533,43 +394,24 @@ static int b43legacy_dmacontroller_tx_reset(struct b43legacy_wldev *dev,
might_sleep();
for (i = 0; i < 10; i++) {
- offset = (type == B43legacy_DMA_64BIT) ?
- B43legacy_DMA64_TXSTATUS : B43legacy_DMA32_TXSTATUS;
+ offset = B43legacy_DMA32_TXSTATUS;
value = b43legacy_read32(dev, mmio_base + offset);
- if (type == B43legacy_DMA_64BIT) {
- value &= B43legacy_DMA64_TXSTAT;
- if (value == B43legacy_DMA64_TXSTAT_DISABLED ||
- value == B43legacy_DMA64_TXSTAT_IDLEWAIT ||
- value == B43legacy_DMA64_TXSTAT_STOPPED)
- break;
- } else {
- value &= B43legacy_DMA32_TXSTATE;
- if (value == B43legacy_DMA32_TXSTAT_DISABLED ||
- value == B43legacy_DMA32_TXSTAT_IDLEWAIT ||
- value == B43legacy_DMA32_TXSTAT_STOPPED)
- break;
- }
+ value &= B43legacy_DMA32_TXSTATE;
+ if (value == B43legacy_DMA32_TXSTAT_DISABLED ||
+ value == B43legacy_DMA32_TXSTAT_IDLEWAIT ||
+ value == B43legacy_DMA32_TXSTAT_STOPPED)
+ break;
msleep(1);
}
- offset = (type == B43legacy_DMA_64BIT) ? B43legacy_DMA64_TXCTL :
- B43legacy_DMA32_TXCTL;
+ offset = B43legacy_DMA32_TXCTL;
b43legacy_write32(dev, mmio_base + offset, 0);
for (i = 0; i < 10; i++) {
- offset = (type == B43legacy_DMA_64BIT) ?
- B43legacy_DMA64_TXSTATUS : B43legacy_DMA32_TXSTATUS;
+ offset = B43legacy_DMA32_TXSTATUS;
value = b43legacy_read32(dev, mmio_base + offset);
- if (type == B43legacy_DMA_64BIT) {
- value &= B43legacy_DMA64_TXSTAT;
- if (value == B43legacy_DMA64_TXSTAT_DISABLED) {
- i = -1;
- break;
- }
- } else {
- value &= B43legacy_DMA32_TXSTATE;
- if (value == B43legacy_DMA32_TXSTAT_DISABLED) {
- i = -1;
- break;
- }
+ value &= B43legacy_DMA32_TXSTATE;
+ if (value == B43legacy_DMA32_TXSTAT_DISABLED) {
+ i = -1;
+ break;
}
msleep(1);
}
@@ -601,9 +443,6 @@ static bool b43legacy_dma_mapping_error(struct b43legacy_dmaring *ring,
if ((u64)addr + buffersize > (1ULL << 32))
goto address_error;
break;
- case B43legacy_DMA_64BIT:
- /* Currently we can't have addresses beyond 64 bits in the kernel. */
- break;
}
/* The address is OK. */
@@ -617,7 +456,7 @@ address_error:
}
static int setup_rx_descbuffer(struct b43legacy_dmaring *ring,
- struct b43legacy_dmadesc_generic *desc,
+ struct b43legacy_dmadesc32 *desc,
struct b43legacy_dmadesc_meta *meta,
gfp_t gfp_flags)
{
@@ -653,8 +492,7 @@ static int setup_rx_descbuffer(struct b43legacy_dmaring *ring,
meta->skb = skb;
meta->dmaaddr = dmaaddr;
- ring->ops->fill_descriptor(ring, desc, dmaaddr,
- ring->rx_buffersize, 0, 0, 0);
+ op32_fill_descriptor(ring, desc, dmaaddr, ring->rx_buffersize, 0, 0, 0);
rxhdr = (struct b43legacy_rxhdr_fw3 *)(skb->data);
rxhdr->frame_len = 0;
@@ -671,11 +509,11 @@ static int alloc_initial_descbuffers(struct b43legacy_dmaring *ring)
{
int i;
int err = -ENOMEM;
- struct b43legacy_dmadesc_generic *desc;
+ struct b43legacy_dmadesc32 *desc;
struct b43legacy_dmadesc_meta *meta;
for (i = 0; i < ring->nr_slots; i++) {
- desc = ring->ops->idx2desc(ring, i, &meta);
+ desc = op32_idx2desc(ring, i, &meta);
err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL);
if (err) {
@@ -692,7 +530,7 @@ out:
err_unwind:
for (i--; i >= 0; i--) {
- desc = ring->ops->idx2desc(ring, i, &meta);
+ desc = op32_idx2desc(ring, i, &meta);
unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0);
dev_kfree_skb(meta->skb);
@@ -709,84 +547,36 @@ static int dmacontroller_setup(struct b43legacy_dmaring *ring)
int err = 0;
u32 value;
u32 addrext;
- u32 trans = ssb_dma_translation(ring->dev->dev);
+ u32 trans = ring->dev->dma.translation;
+ u32 ringbase = (u32)(ring->dmabase);
if (ring->tx) {
- if (ring->type == B43legacy_DMA_64BIT) {
- u64 ringbase = (u64)(ring->dmabase);
-
- addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
- value = B43legacy_DMA64_TXENABLE;
- value |= (addrext << B43legacy_DMA64_TXADDREXT_SHIFT)
- & B43legacy_DMA64_TXADDREXT_MASK;
- b43legacy_dma_write(ring, B43legacy_DMA64_TXCTL,
- value);
- b43legacy_dma_write(ring, B43legacy_DMA64_TXRINGLO,
- (ringbase & 0xFFFFFFFF));
- b43legacy_dma_write(ring, B43legacy_DMA64_TXRINGHI,
- ((ringbase >> 32)
- & ~SSB_DMA_TRANSLATION_MASK)
- | trans);
- } else {
- u32 ringbase = (u32)(ring->dmabase);
-
- addrext = (ringbase & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
- value = B43legacy_DMA32_TXENABLE;
- value |= (addrext << B43legacy_DMA32_TXADDREXT_SHIFT)
- & B43legacy_DMA32_TXADDREXT_MASK;
- b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL,
- value);
- b43legacy_dma_write(ring, B43legacy_DMA32_TXRING,
- (ringbase &
- ~SSB_DMA_TRANSLATION_MASK)
- | trans);
- }
+ addrext = (ringbase & SSB_DMA_TRANSLATION_MASK)
+ >> SSB_DMA_TRANSLATION_SHIFT;
+ value = B43legacy_DMA32_TXENABLE;
+ value |= (addrext << B43legacy_DMA32_TXADDREXT_SHIFT)
+ & B43legacy_DMA32_TXADDREXT_MASK;
+ b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, value);
+ b43legacy_dma_write(ring, B43legacy_DMA32_TXRING,
+ (ringbase & ~SSB_DMA_TRANSLATION_MASK)
+ | trans);
} else {
err = alloc_initial_descbuffers(ring);
if (err)
goto out;
- if (ring->type == B43legacy_DMA_64BIT) {
- u64 ringbase = (u64)(ring->dmabase);
-
- addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
- value = (ring->frameoffset <<
- B43legacy_DMA64_RXFROFF_SHIFT);
- value |= B43legacy_DMA64_RXENABLE;
- value |= (addrext << B43legacy_DMA64_RXADDREXT_SHIFT)
- & B43legacy_DMA64_RXADDREXT_MASK;
- b43legacy_dma_write(ring, B43legacy_DMA64_RXCTL,
- value);
- b43legacy_dma_write(ring, B43legacy_DMA64_RXRINGLO,
- (ringbase & 0xFFFFFFFF));
- b43legacy_dma_write(ring, B43legacy_DMA64_RXRINGHI,
- ((ringbase >> 32) &
- ~SSB_DMA_TRANSLATION_MASK) |
- trans);
- b43legacy_dma_write(ring, B43legacy_DMA64_RXINDEX,
- 200);
- } else {
- u32 ringbase = (u32)(ring->dmabase);
-
- addrext = (ringbase & SSB_DMA_TRANSLATION_MASK)
- >> SSB_DMA_TRANSLATION_SHIFT;
- value = (ring->frameoffset <<
- B43legacy_DMA32_RXFROFF_SHIFT);
- value |= B43legacy_DMA32_RXENABLE;
- value |= (addrext <<
- B43legacy_DMA32_RXADDREXT_SHIFT)
- & B43legacy_DMA32_RXADDREXT_MASK;
- b43legacy_dma_write(ring, B43legacy_DMA32_RXCTL,
- value);
- b43legacy_dma_write(ring, B43legacy_DMA32_RXRING,
- (ringbase &
- ~SSB_DMA_TRANSLATION_MASK)
- | trans);
- b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX,
- 200);
- }
+
+ addrext = (ringbase & SSB_DMA_TRANSLATION_MASK)
+ >> SSB_DMA_TRANSLATION_SHIFT;
+ value = (ring->frameoffset <<
+ B43legacy_DMA32_RXFROFF_SHIFT);
+ value |= B43legacy_DMA32_RXENABLE;
+ value |= (addrext << B43legacy_DMA32_RXADDREXT_SHIFT)
+ & B43legacy_DMA32_RXADDREXT_MASK;
+ b43legacy_dma_write(ring, B43legacy_DMA32_RXCTL, value);
+ b43legacy_dma_write(ring, B43legacy_DMA32_RXRING,
+ (ringbase & ~SSB_DMA_TRANSLATION_MASK)
+ | trans);
+ b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, 200);
}
out:
@@ -799,32 +589,23 @@ static void dmacontroller_cleanup(struct b43legacy_dmaring *ring)
if (ring->tx) {
b43legacy_dmacontroller_tx_reset(ring->dev, ring->mmio_base,
ring->type);
- if (ring->type == B43legacy_DMA_64BIT) {
- b43legacy_dma_write(ring, B43legacy_DMA64_TXRINGLO, 0);
- b43legacy_dma_write(ring, B43legacy_DMA64_TXRINGHI, 0);
- } else
- b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, 0);
+ b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, 0);
} else {
b43legacy_dmacontroller_rx_reset(ring->dev, ring->mmio_base,
ring->type);
- if (ring->type == B43legacy_DMA_64BIT) {
- b43legacy_dma_write(ring, B43legacy_DMA64_RXRINGLO, 0);
- b43legacy_dma_write(ring, B43legacy_DMA64_RXRINGHI, 0);
- } else
- b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, 0);
+ b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, 0);
}
}
static void free_all_descbuffers(struct b43legacy_dmaring *ring)
{
- struct b43legacy_dmadesc_generic *desc;
struct b43legacy_dmadesc_meta *meta;
int i;
if (!ring->used_slots)
return;
for (i = 0; i < ring->nr_slots; i++) {
- desc = ring->ops->idx2desc(ring, i, &meta);
+ op32_idx2desc(ring, i, &meta);
if (!meta->skb) {
B43legacy_WARN_ON(!ring->tx);
@@ -845,9 +626,6 @@ static u64 supported_dma_mask(struct b43legacy_wldev *dev)
u32 tmp;
u16 mmio_base;
- tmp = b43legacy_read32(dev, SSB_TMSHIGH);
- if (tmp & SSB_TMSHIGH_DMA64)
- return DMA_BIT_MASK(64);
mmio_base = b43legacy_dmacontroller_base(0, 0);
b43legacy_write32(dev,
mmio_base + B43legacy_DMA32_TXCTL,
@@ -866,8 +644,6 @@ static enum b43legacy_dmatype dma_mask_to_engine_type(u64 dmamask)
return B43legacy_DMA_30BIT;
if (dmamask == DMA_BIT_MASK(32))
return B43legacy_DMA_32BIT;
- if (dmamask == DMA_BIT_MASK(64))
- return B43legacy_DMA_64BIT;
B43legacy_WARN_ON(1);
return B43legacy_DMA_30BIT;
}
@@ -938,10 +714,6 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev,
ring->nr_slots = nr_slots;
ring->mmio_base = b43legacy_dmacontroller_base(type, controller_index);
ring->index = controller_index;
- if (type == B43legacy_DMA_64BIT)
- ring->ops = &dma64_ops;
- else
- ring->ops = &dma32_ops;
if (for_tx) {
ring->tx = 1;
ring->current_slot = -1;
@@ -1094,6 +866,7 @@ int b43legacy_dma_init(struct b43legacy_wldev *dev)
return -EOPNOTSUPP;
#endif
}
+ dma->translation = ssb_dma_translation(dev->dev);
err = -ENOMEM;
/* setup TX DMA channels. */
@@ -1247,12 +1020,11 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring,
struct sk_buff **in_skb)
{
struct sk_buff *skb = *in_skb;
- const struct b43legacy_dma_ops *ops = ring->ops;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
u8 *header;
int slot, old_top_slot, old_used_slots;
int err;
- struct b43legacy_dmadesc_generic *desc;
+ struct b43legacy_dmadesc32 *desc;
struct b43legacy_dmadesc_meta *meta;
struct b43legacy_dmadesc_meta *meta_hdr;
struct sk_buff *bounce_skb;
@@ -1265,7 +1037,7 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring,
/* Get a slot for the header. */
slot = request_slot(ring);
- desc = ops->idx2desc(ring, slot, &meta_hdr);
+ desc = op32_idx2desc(ring, slot, &meta_hdr);
memset(meta_hdr, 0, sizeof(*meta_hdr));
header = &(ring->txhdr_cache[slot * sizeof(
@@ -1287,12 +1059,12 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring,
ring->used_slots = old_used_slots;
return -EIO;
}
- ops->fill_descriptor(ring, desc, meta_hdr->dmaaddr,
+ op32_fill_descriptor(ring, desc, meta_hdr->dmaaddr,
sizeof(struct b43legacy_txhdr_fw3), 1, 0, 0);
/* Get a slot for the payload. */
slot = request_slot(ring);
- desc = ops->idx2desc(ring, slot, &meta);
+ desc = op32_idx2desc(ring, slot, &meta);
memset(meta, 0, sizeof(*meta));
meta->skb = skb;
@@ -1328,12 +1100,12 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring,
}
}
- ops->fill_descriptor(ring, desc, meta->dmaaddr,
+ op32_fill_descriptor(ring, desc, meta->dmaaddr,
skb->len, 0, 1, 1);
wmb(); /* previous stuff MUST be done */
/* Now transfer the whole frame. */
- ops->poke_tx(ring, next_slot(ring, slot));
+ op32_poke_tx(ring, next_slot(ring, slot));
return 0;
out_free_bounce:
@@ -1371,10 +1143,8 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb)
{
struct b43legacy_dmaring *ring;
- struct ieee80211_hdr *hdr;
int err = 0;
unsigned long flags;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
ring = priority_to_txring(dev, skb_get_queue_mapping(skb));
spin_lock_irqsave(&ring->lock, flags);
@@ -1401,8 +1171,6 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev,
/* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing
* into the skb data or cb now. */
- hdr = NULL;
- info = NULL;
err = dma_tx_fragment(ring, &skb);
if (unlikely(err == -ENOKEY)) {
/* Drop this packet, as we don't have the encryption key
@@ -1433,9 +1201,7 @@ out_unlock:
void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status)
{
- const struct b43legacy_dma_ops *ops;
struct b43legacy_dmaring *ring;
- struct b43legacy_dmadesc_generic *desc;
struct b43legacy_dmadesc_meta *meta;
int retry_limit;
int slot;
@@ -1447,10 +1213,9 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
spin_lock(&ring->lock);
B43legacy_WARN_ON(!ring->tx);
- ops = ring->ops;
while (1) {
B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
- desc = ops->idx2desc(ring, slot, &meta);
+ op32_idx2desc(ring, slot, &meta);
if (meta->skb)
unmap_descbuffer(ring, meta->dmaaddr,
@@ -1533,8 +1298,7 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
static void dma_rx(struct b43legacy_dmaring *ring,
int *slot)
{
- const struct b43legacy_dma_ops *ops = ring->ops;
- struct b43legacy_dmadesc_generic *desc;
+ struct b43legacy_dmadesc32 *desc;
struct b43legacy_dmadesc_meta *meta;
struct b43legacy_rxhdr_fw3 *rxhdr;
struct sk_buff *skb;
@@ -1542,7 +1306,7 @@ static void dma_rx(struct b43legacy_dmaring *ring,
int err;
dma_addr_t dmaaddr;
- desc = ops->idx2desc(ring, *slot, &meta);
+ desc = op32_idx2desc(ring, *slot, &meta);
sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize);
skb = meta->skb;
@@ -1594,7 +1358,7 @@ static void dma_rx(struct b43legacy_dmaring *ring,
s32 tmp = len;
while (1) {
- desc = ops->idx2desc(ring, *slot, &meta);
+ desc = op32_idx2desc(ring, *slot, &meta);
/* recycle the descriptor buffer. */
sync_descbuffer_for_device(ring, meta->dmaaddr,
ring->rx_buffersize);
@@ -1631,13 +1395,12 @@ drop:
void b43legacy_dma_rx(struct b43legacy_dmaring *ring)
{
- const struct b43legacy_dma_ops *ops = ring->ops;
int slot;
int current_slot;
int used_slots = 0;
B43legacy_WARN_ON(ring->tx);
- current_slot = ops->get_current_rxslot(ring);
+ current_slot = op32_get_current_rxslot(ring);
B43legacy_WARN_ON(!(current_slot >= 0 && current_slot <
ring->nr_slots));
@@ -1646,7 +1409,7 @@ void b43legacy_dma_rx(struct b43legacy_dmaring *ring)
dma_rx(ring, &slot);
update_max_used_slots(ring, ++used_slots);
}
- ops->set_current_rxslot(ring, slot);
+ op32_set_current_rxslot(ring, slot);
ring->current_slot = slot;
}
@@ -1656,7 +1419,7 @@ static void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring)
spin_lock_irqsave(&ring->lock, flags);
B43legacy_WARN_ON(!ring->tx);
- ring->ops->tx_suspend(ring);
+ op32_tx_suspend(ring);
spin_unlock_irqrestore(&ring->lock, flags);
}
@@ -1666,7 +1429,7 @@ static void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring)
spin_lock_irqsave(&ring->lock, flags);
B43legacy_WARN_ON(!ring->tx);
- ring->ops->tx_resume(ring);
+ op32_tx_resume(ring);
spin_unlock_irqrestore(&ring->lock, flags);
}
diff --git a/drivers/net/wireless/b43legacy/dma.h b/drivers/net/wireless/b43legacy/dma.h
index f89c34226288..504a58767e95 100644
--- a/drivers/net/wireless/b43legacy/dma.h
+++ b/drivers/net/wireless/b43legacy/dma.h
@@ -5,7 +5,7 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/linkage.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "b43legacy.h"
@@ -82,90 +82,6 @@ struct b43legacy_dmadesc32 {
#define B43legacy_DMA32_DCTL_FRAMESTART 0x80000000
-
-/*** 64-bit DMA Engine. ***/
-
-/* 64-bit DMA controller registers. */
-#define B43legacy_DMA64_TXCTL 0x00
-#define B43legacy_DMA64_TXENABLE 0x00000001
-#define B43legacy_DMA64_TXSUSPEND 0x00000002
-#define B43legacy_DMA64_TXLOOPBACK 0x00000004
-#define B43legacy_DMA64_TXFLUSH 0x00000010
-#define B43legacy_DMA64_TXADDREXT_MASK 0x00030000
-#define B43legacy_DMA64_TXADDREXT_SHIFT 16
-#define B43legacy_DMA64_TXINDEX 0x04
-#define B43legacy_DMA64_TXRINGLO 0x08
-#define B43legacy_DMA64_TXRINGHI 0x0C
-#define B43legacy_DMA64_TXSTATUS 0x10
-#define B43legacy_DMA64_TXSTATDPTR 0x00001FFF
-#define B43legacy_DMA64_TXSTAT 0xF0000000
-#define B43legacy_DMA64_TXSTAT_DISABLED 0x00000000
-#define B43legacy_DMA64_TXSTAT_ACTIVE 0x10000000
-#define B43legacy_DMA64_TXSTAT_IDLEWAIT 0x20000000
-#define B43legacy_DMA64_TXSTAT_STOPPED 0x30000000
-#define B43legacy_DMA64_TXSTAT_SUSP 0x40000000
-#define B43legacy_DMA64_TXERROR 0x14
-#define B43legacy_DMA64_TXERRDPTR 0x0001FFFF
-#define B43legacy_DMA64_TXERR 0xF0000000
-#define B43legacy_DMA64_TXERR_NOERR 0x00000000
-#define B43legacy_DMA64_TXERR_PROT 0x10000000
-#define B43legacy_DMA64_TXERR_UNDERRUN 0x20000000
-#define B43legacy_DMA64_TXERR_TRANSFER 0x30000000
-#define B43legacy_DMA64_TXERR_DESCREAD 0x40000000
-#define B43legacy_DMA64_TXERR_CORE 0x50000000
-#define B43legacy_DMA64_RXCTL 0x20
-#define B43legacy_DMA64_RXENABLE 0x00000001
-#define B43legacy_DMA64_RXFROFF_MASK 0x000000FE
-#define B43legacy_DMA64_RXFROFF_SHIFT 1
-#define B43legacy_DMA64_RXDIRECTFIFO 0x00000100
-#define B43legacy_DMA64_RXADDREXT_MASK 0x00030000
-#define B43legacy_DMA64_RXADDREXT_SHIFT 16
-#define B43legacy_DMA64_RXINDEX 0x24
-#define B43legacy_DMA64_RXRINGLO 0x28
-#define B43legacy_DMA64_RXRINGHI 0x2C
-#define B43legacy_DMA64_RXSTATUS 0x30
-#define B43legacy_DMA64_RXSTATDPTR 0x00001FFF
-#define B43legacy_DMA64_RXSTAT 0xF0000000
-#define B43legacy_DMA64_RXSTAT_DISABLED 0x00000000
-#define B43legacy_DMA64_RXSTAT_ACTIVE 0x10000000
-#define B43legacy_DMA64_RXSTAT_IDLEWAIT 0x20000000
-#define B43legacy_DMA64_RXSTAT_STOPPED 0x30000000
-#define B43legacy_DMA64_RXSTAT_SUSP 0x40000000
-#define B43legacy_DMA64_RXERROR 0x34
-#define B43legacy_DMA64_RXERRDPTR 0x0001FFFF
-#define B43legacy_DMA64_RXERR 0xF0000000
-#define B43legacy_DMA64_RXERR_NOERR 0x00000000
-#define B43legacy_DMA64_RXERR_PROT 0x10000000
-#define B43legacy_DMA64_RXERR_UNDERRUN 0x20000000
-#define B43legacy_DMA64_RXERR_TRANSFER 0x30000000
-#define B43legacy_DMA64_RXERR_DESCREAD 0x40000000
-#define B43legacy_DMA64_RXERR_CORE 0x50000000
-
-/* 64-bit DMA descriptor. */
-struct b43legacy_dmadesc64 {
- __le32 control0;
- __le32 control1;
- __le32 address_low;
- __le32 address_high;
-} __packed;
-#define B43legacy_DMA64_DCTL0_DTABLEEND 0x10000000
-#define B43legacy_DMA64_DCTL0_IRQ 0x20000000
-#define B43legacy_DMA64_DCTL0_FRAMEEND 0x40000000
-#define B43legacy_DMA64_DCTL0_FRAMESTART 0x80000000
-#define B43legacy_DMA64_DCTL1_BYTECNT 0x00001FFF
-#define B43legacy_DMA64_DCTL1_ADDREXT_MASK 0x00030000
-#define B43legacy_DMA64_DCTL1_ADDREXT_SHIFT 16
-
-
-
-struct b43legacy_dmadesc_generic {
- union {
- struct b43legacy_dmadesc32 dma32;
- struct b43legacy_dmadesc64 dma64;
- } __packed;
-} __packed;
-
-
/* Misc DMA constants */
#define B43legacy_DMA_RINGMEMSIZE PAGE_SIZE
#define B43legacy_DMA0_RX_FRAMEOFFSET 30
@@ -197,35 +113,12 @@ struct b43legacy_dmadesc_meta {
bool is_last_fragment;
};
-struct b43legacy_dmaring;
-
-/* Lowlevel DMA operations that differ between 32bit and 64bit DMA. */
-struct b43legacy_dma_ops {
- struct b43legacy_dmadesc_generic * (*idx2desc)
- (struct b43legacy_dmaring *ring,
- int slot,
- struct b43legacy_dmadesc_meta
- **meta);
- void (*fill_descriptor)(struct b43legacy_dmaring *ring,
- struct b43legacy_dmadesc_generic *desc,
- dma_addr_t dmaaddr, u16 bufsize,
- int start, int end, int irq);
- void (*poke_tx)(struct b43legacy_dmaring *ring, int slot);
- void (*tx_suspend)(struct b43legacy_dmaring *ring);
- void (*tx_resume)(struct b43legacy_dmaring *ring);
- int (*get_current_rxslot)(struct b43legacy_dmaring *ring);
- void (*set_current_rxslot)(struct b43legacy_dmaring *ring, int slot);
-};
-
enum b43legacy_dmatype {
B43legacy_DMA_30BIT = 30,
B43legacy_DMA_32BIT = 32,
- B43legacy_DMA_64BIT = 64,
};
struct b43legacy_dmaring {
- /* Lowlevel DMA ops. */
- const struct b43legacy_dma_ops *ops;
/* Kernel virtual base address of the ring memory. */
void *descbase;
/* Meta data about all descriptors. */
diff --git a/drivers/net/wireless/b43legacy/ilt.c b/drivers/net/wireless/b43legacy/ilt.c
index a849078aea69..ee5682e54204 100644
--- a/drivers/net/wireless/b43legacy/ilt.c
+++ b/drivers/net/wireless/b43legacy/ilt.c
@@ -4,7 +4,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <stefano.brivio@polimi.it>
- Michael Buesch <mbuesch@freenet.de>
+ Michael Buesch <m@bues.ch>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
diff --git a/drivers/net/wireless/b43legacy/leds.c b/drivers/net/wireless/b43legacy/leds.c
index 37e9be893560..2f1bfdc44f94 100644
--- a/drivers/net/wireless/b43legacy/leds.c
+++ b/drivers/net/wireless/b43legacy/leds.c
@@ -5,7 +5,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Copyright (c) 2005 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 1ab8861dd43a..20f02437af8c 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -4,7 +4,7 @@
*
* Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>
* Copyright (c) 2005-2008 Stefano Brivio <stefano.brivio@polimi.it>
- * Copyright (c) 2005, 2006 Michael Buesch <mb@bu3sch.de>
+ * Copyright (c) 2005, 2006 Michael Buesch <m@bues.ch>
* Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
* Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
* Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
@@ -31,11 +31,10 @@
#include <linux/delay.h>
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include <linux/firmware.h>
-#include <linux/wireless.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
@@ -61,7 +60,6 @@ MODULE_AUTHOR("Stefano Brivio");
MODULE_AUTHOR("Michael Buesch");
MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(B43legacy_SUPPORTED_FIRMWARE_ID);
MODULE_FIRMWARE("b43legacy/ucode2.fw");
MODULE_FIRMWARE("b43legacy/ucode4.fw");
@@ -1564,10 +1562,10 @@ static int b43legacy_request_firmware(struct b43legacy_wldev *dev)
struct b43legacy_firmware *fw = &dev->fw;
const u8 rev = dev->dev->id.revision;
const char *filename;
- u32 tmshigh;
int err;
- tmshigh = ssb_read32(dev->dev, SSB_TMSHIGH);
+ /* do dummy read */
+ ssb_read32(dev->dev, SSB_TMSHIGH);
if (!fw->ucode) {
if (rev == 2)
filename = "ucode2";
@@ -2468,7 +2466,8 @@ out:
}
}
-static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
+static int b43legacy_op_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
return 0;
@@ -2634,11 +2633,9 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
unsigned long flags;
unsigned int new_phymode = 0xFFFF;
int antenna_tx;
- int antenna_rx;
int err = 0;
antenna_tx = B43legacy_ANTENNA_DEFAULT;
- antenna_rx = B43legacy_ANTENNA_DEFAULT;
mutex_lock(&wl->mutex);
dev = wl->current_dev;
@@ -2775,14 +2772,12 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw,
{
struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
struct b43legacy_wldev *dev;
- struct b43legacy_phy *phy;
unsigned long flags;
mutex_lock(&wl->mutex);
B43legacy_WARN_ON(wl->vif != vif);
dev = wl->current_dev;
- phy = &dev->phy;
/* Disable IRQs while reconfiguring the device.
* This makes it possible to drop the spinlock throughout
@@ -2974,7 +2969,7 @@ static int b43legacy_phy_versioning(struct b43legacy_wldev *dev)
break;
default:
unsupported = 1;
- };
+ }
if (unsupported) {
b43legacyerr(dev->wl, "FOUND UNSUPPORTED PHY "
"(Analog %u, Type %u, Revision %u)\n",
@@ -3789,7 +3784,8 @@ static int b43legacy_wireless_init(struct ssb_device *dev)
INIT_WORK(&wl->beacon_update_trigger, b43legacy_beacon_update_trigger_work);
ssb_set_devtypedata(dev, wl);
- b43legacyinfo(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id);
+ b43legacyinfo(wl, "Broadcom %04X WLAN found (core revision %u)\n",
+ dev->bus->chip_id, dev->id.revision);
err = 0;
out:
return err;
@@ -3951,8 +3947,7 @@ static void b43legacy_print_driverinfo(void)
feat_dma = "D";
#endif
printk(KERN_INFO "Broadcom 43xx-legacy driver loaded "
- "[ Features: %s%s%s%s, Firmware-ID: "
- B43legacy_SUPPORTED_FIRMWARE_ID " ]\n",
+ "[ Features: %s%s%s%s ]\n",
feat_pci, feat_leds, feat_pio, feat_dma);
}
diff --git a/drivers/net/wireless/b43legacy/main.h b/drivers/net/wireless/b43legacy/main.h
index 1f0e2e379b02..b74a058d7bac 100644
--- a/drivers/net/wireless/b43legacy/main.h
+++ b/drivers/net/wireless/b43legacy/main.h
@@ -4,7 +4,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Copyright (c) 2005 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (c) 2005, 2006 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005, 2006 Michael Buesch <m@bues.ch>
Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
diff --git a/drivers/net/wireless/b43legacy/phy.c b/drivers/net/wireless/b43legacy/phy.c
index 28e477d01587..96faaef3661b 100644
--- a/drivers/net/wireless/b43legacy/phy.c
+++ b/drivers/net/wireless/b43legacy/phy.c
@@ -4,7 +4,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <stefano.brivio@polimi.it>
- Michael Buesch <mbuesch@freenet.de>
+ Michael Buesch <m@bues.ch>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
diff --git a/drivers/net/wireless/b43legacy/phy.h b/drivers/net/wireless/b43legacy/phy.h
index ecbe409f9a94..831a7a4760e5 100644
--- a/drivers/net/wireless/b43legacy/phy.h
+++ b/drivers/net/wireless/b43legacy/phy.h
@@ -4,7 +4,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <stefano.brivio@polimi.it>
- Michael Buesch <mbuesch@freenet.de>
+ Michael Buesch <m@bues.ch>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
diff --git a/drivers/net/wireless/b43legacy/pio.c b/drivers/net/wireless/b43legacy/pio.c
index b033b0ed4ca0..192251adf986 100644
--- a/drivers/net/wireless/b43legacy/pio.c
+++ b/drivers/net/wireless/b43legacy/pio.c
@@ -4,7 +4,7 @@
PIO Transmission
- Copyright (c) 2005 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2005 Michael Buesch <m@bues.ch>
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
diff --git a/drivers/net/wireless/b43legacy/radio.c b/drivers/net/wireless/b43legacy/radio.c
index 2df545cfad14..475eb14e665b 100644
--- a/drivers/net/wireless/b43legacy/radio.c
+++ b/drivers/net/wireless/b43legacy/radio.c
@@ -4,7 +4,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <stefano.brivio@polimi.it>
- Michael Buesch <mbuesch@freenet.de>
+ Michael Buesch <m@bues.ch>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
diff --git a/drivers/net/wireless/b43legacy/radio.h b/drivers/net/wireless/b43legacy/radio.h
index ec4de2811c52..bccb3d7da682 100644
--- a/drivers/net/wireless/b43legacy/radio.h
+++ b/drivers/net/wireless/b43legacy/radio.h
@@ -4,7 +4,7 @@
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <stefano.brivio@polimi.it>
- Michael Buesch <mbuesch@freenet.de>
+ Michael Buesch <m@bues.ch>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
diff --git a/drivers/net/wireless/b43legacy/rfkill.c b/drivers/net/wireless/b43legacy/rfkill.c
index b90f223fb31c..c4559bcbc707 100644
--- a/drivers/net/wireless/b43legacy/rfkill.c
+++ b/drivers/net/wireless/b43legacy/rfkill.c
@@ -3,7 +3,7 @@
Broadcom B43 wireless driver
RFKILL support
- Copyright (c) 2007 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2007 Michael Buesch <m@bues.ch>
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
diff --git a/drivers/net/wireless/b43legacy/sysfs.c b/drivers/net/wireless/b43legacy/sysfs.c
index 56c384fa9b1f..57f8b089767c 100644
--- a/drivers/net/wireless/b43legacy/sysfs.c
+++ b/drivers/net/wireless/b43legacy/sysfs.c
@@ -4,7 +4,7 @@
SYSFS support routines
- Copyright (c) 2006 Michael Buesch <mb@bu3sch.de>
+ Copyright (c) 2006 Michael Buesch <m@bues.ch>
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
diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c
index 3a95541708a6..5188fab0b377 100644
--- a/drivers/net/wireless/b43legacy/xmit.c
+++ b/drivers/net/wireless/b43legacy/xmit.c
@@ -6,7 +6,7 @@
Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
Copyright (C) 2005 Stefano Brivio <stefano.brivio@polimi.it>
- Copyright (C) 2005, 2006 Michael Buesch <mb@bu3sch.de>
+ Copyright (C) 2005, 2006 Michael Buesch <m@bues.ch>
Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
Copyright (C) 2007 Larry Finger <Larry.Finger@lwfinger.net>
@@ -321,11 +321,9 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
struct ieee80211_hdr *hdr;
int rts_rate;
int rts_rate_fb;
- int rts_rate_ofdm;
int rts_rate_fb_ofdm;
rts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info)->hw_value;
- rts_rate_ofdm = b43legacy_is_ofdm_rate(rts_rate);
rts_rate_fb = b43legacy_calc_fallback_rate(rts_rate);
rts_rate_fb_ofdm = b43legacy_is_ofdm_rate(rts_rate_fb);
if (rts_rate_fb_ofdm)
diff --git a/drivers/staging/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig
index 379cf16e89f7..2069fc8f7ad1 100644
--- a/drivers/staging/brcm80211/Kconfig
+++ b/drivers/net/wireless/brcm80211/Kconfig
@@ -1,16 +1,16 @@
config BRCMUTIL
tristate
- default n
config BRCMSMAC
tristate "Broadcom IEEE802.11n PCIe SoftMAC WLAN driver"
- default n
depends on PCI
- depends on WLAN && MAC80211
- depends on X86 || MIPS
+ depends on MAC80211
+ depends on BCMA=n
select BRCMUTIL
select FW_LOADER
select CRC_CCITT
+ select CRC8
+ select CORDIC
---help---
This module adds support for PCIe wireless adapters based on Broadcom
IEEE802.11n SoftMAC chipsets. If you choose to build a module, it'll
@@ -18,14 +18,10 @@ config BRCMSMAC
config BRCMFMAC
tristate "Broadcom IEEE802.11n embedded FullMAC WLAN driver"
- default n
depends on MMC
- depends on WLAN && CFG80211
- depends on X86 || MIPS
+ depends on CFG80211
select BRCMUTIL
select FW_LOADER
- select WIRELESS_EXT
- select WEXT_PRIV
---help---
This module adds support for embedded wireless adapters based on
Broadcom IEEE802.11n FullMAC chipsets. This driver uses the kernel's
@@ -34,7 +30,6 @@ config BRCMFMAC
config BRCMDBG
bool "Broadcom driver debug functions"
- default n
depends on BRCMSMAC || BRCMFMAC
---help---
Selecting this enables additional code for debug purposes.
diff --git a/drivers/staging/brcm80211/Makefile b/drivers/net/wireless/brcm80211/Makefile
index e7b3f27847cf..f41c047eca82 100644
--- a/drivers/staging/brcm80211/Makefile
+++ b/drivers/net/wireless/brcm80211/Makefile
@@ -16,9 +16,8 @@
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# common flags
-subdir-ccflags-y := -DBCMDMA32
-subdir-ccflags-$(CONFIG_BRCMDBG) += -DBCMDBG -DBCMDBG_ASSERT
+subdir-ccflags-$(CONFIG_BRCMDBG) += -DBCMDBG
-obj-$(CONFIG_BRCMUTIL) += util/
+obj-$(CONFIG_BRCMUTIL) += brcmutil/
obj-$(CONFIG_BRCMFMAC) += brcmfmac/
obj-$(CONFIG_BRCMSMAC) += brcmsmac/
diff --git a/drivers/staging/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
index c5ec562c3645..b44e3094588a 100644
--- a/drivers/staging/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
@@ -15,42 +15,19 @@
# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-ccflags-y := \
- -DARP_OFFLOAD_SUPPORT \
- -DBCMLXSDMMC \
- -DBCMPLATFORM_BUS \
- -DBCMSDIO \
- -DBDC \
- -DBRCM_FULLMAC \
- -DDHD_FIRSTREAD=64 \
- -DDHD_SCHED \
- -DDHD_SDALIGN=64 \
- -DEMBEDDED_PLATFORM \
- -DMAX_HDR_READ=64 \
- -DMMC_SDIO_ABORT \
- -DPKT_FILTER_SUPPORT \
- -DSHOW_EVENTS \
- -DTOE
-
-ccflags-$(CONFIG_BRCMDBG) += -DDHD_DEBUG
-
ccflags-y += \
- -Idrivers/staging/brcm80211/brcmfmac \
- -Idrivers/staging/brcm80211/include
+ -Idrivers/net/wireless/brcm80211/brcmfmac \
+ -Idrivers/net/wireless/brcm80211/include
DHDOFILES = \
wl_cfg80211.o \
- wl_iw.o \
dhd_cdc.o \
dhd_common.o \
- dhd_custom_gpio.o \
dhd_sdio.o \
dhd_linux.o \
- dhd_linux_sched.o \
bcmsdh.o \
- bcmsdh_linux.o \
- bcmsdh_sdmmc.o \
- bcmsdh_sdmmc_linux.o
+ bcmsdh_sdmmc.o
obj-$(CONFIG_BRCMFMAC) += brcmfmac.o
brcmfmac-objs += $(DHDOFILES)
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/staging/brcm80211/brcmfmac/bcmchip.h b/drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h
index c0d4c3bf6d42..d7d3afd5a10f 100644
--- a/drivers/staging/brcm80211/brcmfmac/bcmchip.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h
@@ -17,12 +17,6 @@
#ifndef _bcmchip_h_
#define _bcmchip_h_
-/* Core reg address translation */
-#define CORE_CC_REG(base, field) (base + offsetof(chipcregs_t, field))
-#define CORE_BUS_REG(base, field) (base + offsetof(sdpcmd_regs_t, field))
-#define CORE_SB(base, field) \
- (base + SBCONFIGOFF + offsetof(sbconfig_t, field))
-
/* bcm4329 */
/* SDIO device core, ID 0x829 */
#define BCM4329_CORE_BUS_BASE 0x18011000
@@ -31,5 +25,8 @@
/* ARM Cortex M3 core, ID 0x82a */
#define BCM4329_CORE_ARM_BASE 0x18002000
#define BCM4329_RAMSIZE 0x48000
+/* firmware name */
+#define BCM4329_FW_NAME "brcm/bcm4329-fullmac-4.bin"
+#define BCM4329_NV_NAME "brcm/bcm4329-fullmac-4.txt"
#endif /* _bcmchip_h_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
new file mode 100644
index 000000000000..89ff94da556a
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* ****************** SDIO CARD Interface Functions **************************/
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/export.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+
+#include <defs.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include <soc.h>
+#include "dhd.h"
+#include "dhd_bus.h"
+#include "dhd_dbg.h"
+#include "sdio_host.h"
+
+#define SDIOH_API_ACCESS_RETRY_LIMIT 2
+
+static void brcmf_sdioh_irqhandler(struct sdio_func *func)
+{
+ struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev);
+
+ brcmf_dbg(TRACE, "***IRQHandler\n");
+
+ sdio_release_host(func);
+
+ brcmf_sdbrcm_isr(sdiodev->bus);
+
+ sdio_claim_host(func);
+}
+
+int brcmf_sdcard_intr_reg(struct brcmf_sdio_dev *sdiodev)
+{
+ brcmf_dbg(TRACE, "Entering\n");
+
+ sdio_claim_host(sdiodev->func[1]);
+ sdio_claim_irq(sdiodev->func[1], brcmf_sdioh_irqhandler);
+ sdio_release_host(sdiodev->func[1]);
+
+ return 0;
+}
+
+int brcmf_sdcard_intr_dereg(struct brcmf_sdio_dev *sdiodev)
+{
+ brcmf_dbg(TRACE, "Entering\n");
+
+ sdio_claim_host(sdiodev->func[1]);
+ sdio_release_irq(sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
+
+ return 0;
+}
+
+u8 brcmf_sdcard_cfg_read(struct brcmf_sdio_dev *sdiodev, uint fnc_num, u32 addr,
+ int *err)
+{
+ int status;
+ s32 retry = 0;
+ u8 data = 0;
+
+ do {
+ if (retry) /* wait for 1 ms till bus get settled down */
+ udelay(1000);
+ status = brcmf_sdioh_request_byte(sdiodev, SDIOH_READ, fnc_num,
+ addr, (u8 *) &data);
+ } while (status != 0
+ && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT));
+ if (err)
+ *err = status;
+
+ brcmf_dbg(INFO, "fun = %d, addr = 0x%x, u8data = 0x%x\n",
+ fnc_num, addr, data);
+
+ return data;
+}
+
+void
+brcmf_sdcard_cfg_write(struct brcmf_sdio_dev *sdiodev, uint fnc_num, u32 addr,
+ u8 data, int *err)
+{
+ int status;
+ s32 retry = 0;
+
+ do {
+ if (retry) /* wait for 1 ms till bus get settled down */
+ udelay(1000);
+ status = brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, fnc_num,
+ addr, (u8 *) &data);
+ } while (status != 0
+ && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT));
+ if (err)
+ *err = status;
+
+ brcmf_dbg(INFO, "fun = %d, addr = 0x%x, u8data = 0x%x\n",
+ fnc_num, addr, data);
+}
+
+int
+brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
+{
+ int err = 0;
+ brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
+ (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
+ if (!err)
+ brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_SBADDRMID,
+ (address >> 16) & SBSDIO_SBADDRMID_MASK,
+ &err);
+ if (!err)
+ brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_SBADDRHIGH,
+ (address >> 24) & SBSDIO_SBADDRHIGH_MASK,
+ &err);
+
+ return err;
+}
+
+u32 brcmf_sdcard_reg_read(struct brcmf_sdio_dev *sdiodev, u32 addr, uint size)
+{
+ int status;
+ u32 word = 0;
+ uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+
+ brcmf_dbg(INFO, "fun = 1, addr = 0x%x\n", addr);
+
+ if (bar0 != sdiodev->sbwad) {
+ if (brcmf_sdcard_set_sbaddr_window(sdiodev, bar0))
+ return 0xFFFFFFFF;
+
+ sdiodev->sbwad = bar0;
+ }
+
+ addr &= SBSDIO_SB_OFT_ADDR_MASK;
+ if (size == 4)
+ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ status = brcmf_sdioh_request_word(sdiodev, SDIOH_READ, SDIO_FUNC_1,
+ addr, &word, size);
+
+ sdiodev->regfail = (status != 0);
+
+ brcmf_dbg(INFO, "u32data = 0x%x\n", word);
+
+ /* if ok, return appropriately masked word */
+ if (status == 0) {
+ switch (size) {
+ case sizeof(u8):
+ return word & 0xff;
+ case sizeof(u16):
+ return word & 0xffff;
+ case sizeof(u32):
+ return word;
+ default:
+ sdiodev->regfail = true;
+
+ }
+ }
+
+ /* otherwise, bad sdio access or invalid size */
+ brcmf_dbg(ERROR, "error reading addr 0x%04x size %d\n", addr, size);
+ return 0xFFFFFFFF;
+}
+
+u32 brcmf_sdcard_reg_write(struct brcmf_sdio_dev *sdiodev, u32 addr, uint size,
+ u32 data)
+{
+ int status;
+ uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+ int err = 0;
+
+ brcmf_dbg(INFO, "fun = 1, addr = 0x%x, uint%ddata = 0x%x\n",
+ addr, size * 8, data);
+
+ if (bar0 != sdiodev->sbwad) {
+ err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
+ if (err)
+ return err;
+
+ sdiodev->sbwad = bar0;
+ }
+
+ addr &= SBSDIO_SB_OFT_ADDR_MASK;
+ if (size == 4)
+ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+ status =
+ brcmf_sdioh_request_word(sdiodev, SDIOH_WRITE, SDIO_FUNC_1,
+ addr, &data, size);
+ sdiodev->regfail = (status != 0);
+
+ if (status == 0)
+ return 0;
+
+ brcmf_dbg(ERROR, "error writing 0x%08x to addr 0x%04x size %d\n",
+ data, addr, size);
+ return 0xFFFFFFFF;
+}
+
+bool brcmf_sdcard_regfail(struct brcmf_sdio_dev *sdiodev)
+{
+ return sdiodev->regfail;
+}
+
+int
+brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
+ uint flags,
+ u8 *buf, uint nbytes, struct sk_buff *pkt)
+{
+ int status;
+ uint incr_fix;
+ uint width;
+ uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+ int err = 0;
+
+ brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, nbytes);
+
+ /* Async not implemented yet */
+ if (flags & SDIO_REQ_ASYNC)
+ return -ENOTSUPP;
+
+ if (bar0 != sdiodev->sbwad) {
+ err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
+ if (err)
+ return err;
+
+ sdiodev->sbwad = bar0;
+ }
+
+ addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+ incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
+ width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
+ if (width == 4)
+ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ status = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ,
+ fn, addr, width, nbytes, buf, pkt);
+
+ return status;
+}
+
+int
+brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
+ uint flags, u8 *buf, uint nbytes, struct sk_buff *pkt)
+{
+ uint incr_fix;
+ uint width;
+ uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+ int err = 0;
+
+ brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, nbytes);
+
+ /* Async not implemented yet */
+ if (flags & SDIO_REQ_ASYNC)
+ return -ENOTSUPP;
+
+ if (bar0 != sdiodev->sbwad) {
+ err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
+ if (err)
+ return err;
+
+ sdiodev->sbwad = bar0;
+ }
+
+ addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+ incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
+ width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
+ if (width == 4)
+ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ return brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn,
+ addr, width, nbytes, buf, pkt);
+}
+
+int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr,
+ u8 *buf, uint nbytes)
+{
+ addr &= SBSDIO_SB_OFT_ADDR_MASK;
+ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ return brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC,
+ (rw ? SDIOH_WRITE : SDIOH_READ), SDIO_FUNC_1,
+ addr, 4, nbytes, buf, NULL);
+}
+
+int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
+{
+ char t_func = (char)fn;
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* issue abort cmd52 command through F0 */
+ brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0,
+ SDIO_CCCR_ABORT, &t_func);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return 0;
+}
+
+int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
+{
+ u32 regs = 0;
+ int ret = 0;
+
+ ret = brcmf_sdioh_attach(sdiodev);
+ if (ret)
+ goto out;
+
+ regs = SI_ENUM_BASE;
+
+ /* Report the BAR, to fix if needed */
+ sdiodev->sbwad = SI_ENUM_BASE;
+
+ /* try to attach to the target device */
+ sdiodev->bus = brcmf_sdbrcm_probe(0, 0, 0, 0, regs, sdiodev);
+ if (!sdiodev->bus) {
+ brcmf_dbg(ERROR, "device attach failed\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+out:
+ if (ret)
+ brcmf_sdio_remove(sdiodev);
+
+ return ret;
+}
+EXPORT_SYMBOL(brcmf_sdio_probe);
+
+int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev)
+{
+ if (sdiodev->bus) {
+ brcmf_sdbrcm_disconnect(sdiodev->bus);
+ sdiodev->bus = NULL;
+ }
+
+ brcmf_sdioh_detach(sdiodev);
+
+ sdiodev->sbwad = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(brcmf_sdio_remove);
+
+void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable)
+{
+ if (enable)
+ brcmf_sdbrcm_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS);
+ else
+ brcmf_sdbrcm_wd_timer(sdiodev->bus, 0);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
new file mode 100644
index 000000000000..bbaeb2d5c93a
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/card.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/sched.h> /* request_irq() */
+#include <linux/module.h>
+#include <net/cfg80211.h>
+
+#include <defs.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include "sdio_host.h"
+#include "dhd.h"
+#include "dhd_dbg.h"
+#include "wl_cfg80211.h"
+
+#define SDIO_VENDOR_ID_BROADCOM 0x02d0
+
+#define DMA_ALIGN_MASK 0x03
+
+#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
+
+#define SDIO_FUNC1_BLOCKSIZE 64
+#define SDIO_FUNC2_BLOCKSIZE 512
+
+/* devices we support, null terminated */
+static const struct sdio_device_id brcmf_sdmmc_ids[] = {
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
+ { /* end: all zeroes */ },
+};
+MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
+
+static bool
+brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
+{
+ bool is_err = false;
+#ifdef CONFIG_PM_SLEEP
+ is_err = atomic_read(&sdiodev->suspend);
+#endif
+ return is_err;
+}
+
+static void
+brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq)
+{
+#ifdef CONFIG_PM_SLEEP
+ int retry = 0;
+ while (atomic_read(&sdiodev->suspend) && retry++ != 30)
+ wait_event_timeout(*wq, false, HZ/100);
+#endif
+}
+
+static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev,
+ uint regaddr, u8 *byte)
+{
+ struct sdio_func *sdfunc = sdiodev->func[0];
+ int err_ret;
+
+ /*
+ * Can only directly write to some F0 registers.
+ * Handle F2 enable/disable and Abort command
+ * as a special case.
+ */
+ if (regaddr == SDIO_CCCR_IOEx) {
+ sdfunc = sdiodev->func[2];
+ if (sdfunc) {
+ sdio_claim_host(sdfunc);
+ if (*byte & SDIO_FUNC_ENABLE_2) {
+ /* Enable Function 2 */
+ err_ret = sdio_enable_func(sdfunc);
+ if (err_ret)
+ brcmf_dbg(ERROR,
+ "enable F2 failed:%d\n",
+ err_ret);
+ } else {
+ /* Disable Function 2 */
+ err_ret = sdio_disable_func(sdfunc);
+ if (err_ret)
+ brcmf_dbg(ERROR,
+ "Disable F2 failed:%d\n",
+ err_ret);
+ }
+ sdio_release_host(sdfunc);
+ }
+ } else if (regaddr == SDIO_CCCR_ABORT) {
+ sdio_claim_host(sdfunc);
+ sdio_writeb(sdfunc, *byte, regaddr, &err_ret);
+ sdio_release_host(sdfunc);
+ } else if (regaddr < 0xF0) {
+ brcmf_dbg(ERROR, "F0 Wr:0x%02x: write disallowed\n", regaddr);
+ err_ret = -EPERM;
+ } else {
+ sdio_claim_host(sdfunc);
+ sdio_f0_writeb(sdfunc, *byte, regaddr, &err_ret);
+ sdio_release_host(sdfunc);
+ }
+
+ return err_ret;
+}
+
+int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint func,
+ uint regaddr, u8 *byte)
+{
+ int err_ret;
+
+ brcmf_dbg(INFO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr);
+
+ brcmf_pm_resume_wait(sdiodev, &sdiodev->request_byte_wait);
+ if (brcmf_pm_resume_error(sdiodev))
+ return -EIO;
+
+ if (rw && func == 0) {
+ /* handle F0 separately */
+ err_ret = brcmf_sdioh_f0_write_byte(sdiodev, regaddr, byte);
+ } else {
+ sdio_claim_host(sdiodev->func[func]);
+ if (rw) /* CMD52 Write */
+ sdio_writeb(sdiodev->func[func], *byte, regaddr,
+ &err_ret);
+ else if (func == 0) {
+ *byte = sdio_f0_readb(sdiodev->func[func], regaddr,
+ &err_ret);
+ } else {
+ *byte = sdio_readb(sdiodev->func[func], regaddr,
+ &err_ret);
+ }
+ sdio_release_host(sdiodev->func[func]);
+ }
+
+ if (err_ret)
+ brcmf_dbg(ERROR, "Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n",
+ rw ? "write" : "read", func, regaddr, *byte, err_ret);
+
+ return err_ret;
+}
+
+int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
+ uint rw, uint func, uint addr, u32 *word,
+ uint nbytes)
+{
+ int err_ret = -EIO;
+
+ if (func == 0) {
+ brcmf_dbg(ERROR, "Only CMD52 allowed to F0\n");
+ return -EINVAL;
+ }
+
+ brcmf_dbg(INFO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
+ rw, func, addr, nbytes);
+
+ brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait);
+ if (brcmf_pm_resume_error(sdiodev))
+ return -EIO;
+ /* Claim host controller */
+ sdio_claim_host(sdiodev->func[func]);
+
+ if (rw) { /* CMD52 Write */
+ if (nbytes == 4)
+ sdio_writel(sdiodev->func[func], *word, addr,
+ &err_ret);
+ else if (nbytes == 2)
+ sdio_writew(sdiodev->func[func], (*word & 0xFFFF),
+ addr, &err_ret);
+ else
+ brcmf_dbg(ERROR, "Invalid nbytes: %d\n", nbytes);
+ } else { /* CMD52 Read */
+ if (nbytes == 4)
+ *word = sdio_readl(sdiodev->func[func], addr, &err_ret);
+ else if (nbytes == 2)
+ *word = sdio_readw(sdiodev->func[func], addr,
+ &err_ret) & 0xFFFF;
+ else
+ brcmf_dbg(ERROR, "Invalid nbytes: %d\n", nbytes);
+ }
+
+ /* Release host controller */
+ sdio_release_host(sdiodev->func[func]);
+
+ if (err_ret)
+ brcmf_dbg(ERROR, "Failed to %s word, Err: 0x%08x\n",
+ rw ? "write" : "read", err_ret);
+
+ return err_ret;
+}
+
+static int
+brcmf_sdioh_request_packet(struct brcmf_sdio_dev *sdiodev, uint fix_inc,
+ uint write, uint func, uint addr,
+ struct sk_buff *pkt)
+{
+ bool fifo = (fix_inc == SDIOH_DATA_FIX);
+ u32 SGCount = 0;
+ int err_ret = 0;
+
+ struct sk_buff *pnext;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ brcmf_pm_resume_wait(sdiodev, &sdiodev->request_packet_wait);
+ if (brcmf_pm_resume_error(sdiodev))
+ return -EIO;
+
+ /* Claim host controller */
+ sdio_claim_host(sdiodev->func[func]);
+ for (pnext = pkt; pnext; pnext = pnext->next) {
+ uint pkt_len = pnext->len;
+ pkt_len += 3;
+ pkt_len &= 0xFFFFFFFC;
+
+ if ((write) && (!fifo)) {
+ err_ret = sdio_memcpy_toio(sdiodev->func[func], addr,
+ ((u8 *) (pnext->data)),
+ pkt_len);
+ } else if (write) {
+ err_ret = sdio_memcpy_toio(sdiodev->func[func], addr,
+ ((u8 *) (pnext->data)),
+ pkt_len);
+ } else if (fifo) {
+ err_ret = sdio_readsb(sdiodev->func[func],
+ ((u8 *) (pnext->data)),
+ addr, pkt_len);
+ } else {
+ err_ret = sdio_memcpy_fromio(sdiodev->func[func],
+ ((u8 *) (pnext->data)),
+ addr, pkt_len);
+ }
+
+ if (err_ret) {
+ brcmf_dbg(ERROR, "%s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
+ write ? "TX" : "RX", pnext, SGCount, addr,
+ pkt_len, err_ret);
+ } else {
+ brcmf_dbg(TRACE, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n",
+ write ? "TX" : "RX", pnext, SGCount, addr,
+ pkt_len);
+ }
+
+ if (!fifo)
+ addr += pkt_len;
+ SGCount++;
+
+ }
+
+ /* Release host controller */
+ sdio_release_host(sdiodev->func[func]);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err_ret;
+}
+
+/*
+ * This function takes a buffer or packet, and fixes everything up
+ * so that in the end, a DMA-able packet is created.
+ *
+ * A buffer does not have an associated packet pointer,
+ * and may or may not be aligned.
+ * A packet may consist of a single packet, or a packet chain.
+ * If it is a packet chain, then all the packets in the chain
+ * must be properly aligned.
+ *
+ * If the packet data is not aligned, then there may only be
+ * one packet, and in this case, it is copied to a new
+ * aligned packet.
+ *
+ */
+int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev,
+ uint fix_inc, uint write, uint func, uint addr,
+ uint reg_width, uint buflen_u, u8 *buffer,
+ struct sk_buff *pkt)
+{
+ int Status;
+ struct sk_buff *mypkt = NULL;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
+ if (brcmf_pm_resume_error(sdiodev))
+ return -EIO;
+ /* Case 1: we don't have a packet. */
+ if (pkt == NULL) {
+ brcmf_dbg(DATA, "Creating new %s Packet, len=%d\n",
+ write ? "TX" : "RX", buflen_u);
+ mypkt = brcmu_pkt_buf_get_skb(buflen_u);
+ if (!mypkt) {
+ brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n",
+ buflen_u);
+ return -EIO;
+ }
+
+ /* For a write, copy the buffer data into the packet. */
+ if (write)
+ memcpy(mypkt->data, buffer, buflen_u);
+
+ Status = brcmf_sdioh_request_packet(sdiodev, fix_inc, write,
+ func, addr, mypkt);
+
+ /* For a read, copy the packet data back to the buffer. */
+ if (!write)
+ memcpy(buffer, mypkt->data, buflen_u);
+
+ brcmu_pkt_buf_free_skb(mypkt);
+ } else if (((ulong) (pkt->data) & DMA_ALIGN_MASK) != 0) {
+ /*
+ * Case 2: We have a packet, but it is unaligned.
+ * In this case, we cannot have a chain (pkt->next == NULL)
+ */
+ brcmf_dbg(DATA, "Creating aligned %s Packet, len=%d\n",
+ write ? "TX" : "RX", pkt->len);
+ mypkt = brcmu_pkt_buf_get_skb(pkt->len);
+ if (!mypkt) {
+ brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n",
+ pkt->len);
+ return -EIO;
+ }
+
+ /* For a write, copy the buffer data into the packet. */
+ if (write)
+ memcpy(mypkt->data, pkt->data, pkt->len);
+
+ Status = brcmf_sdioh_request_packet(sdiodev, fix_inc, write,
+ func, addr, mypkt);
+
+ /* For a read, copy the packet data back to the buffer. */
+ if (!write)
+ memcpy(pkt->data, mypkt->data, mypkt->len);
+
+ brcmu_pkt_buf_free_skb(mypkt);
+ } else { /* case 3: We have a packet and
+ it is aligned. */
+ brcmf_dbg(DATA, "Aligned %s Packet, direct DMA\n",
+ write ? "Tx" : "Rx");
+ Status = brcmf_sdioh_request_packet(sdiodev, fix_inc, write,
+ func, addr, pkt);
+ }
+
+ return Status;
+}
+
+/* Read client card reg */
+static int
+brcmf_sdioh_card_regread(struct brcmf_sdio_dev *sdiodev, int func, u32 regaddr,
+ int regsize, u32 *data)
+{
+
+ if ((func == 0) || (regsize == 1)) {
+ u8 temp = 0;
+
+ brcmf_sdioh_request_byte(sdiodev, SDIOH_READ, func, regaddr,
+ &temp);
+ *data = temp;
+ *data &= 0xff;
+ brcmf_dbg(DATA, "byte read data=0x%02x\n", *data);
+ } else {
+ brcmf_sdioh_request_word(sdiodev, SDIOH_READ, func, regaddr,
+ data, regsize);
+ if (regsize == 2)
+ *data &= 0xffff;
+
+ brcmf_dbg(DATA, "word read data=0x%08x\n", *data);
+ }
+
+ return SUCCESS;
+}
+
+static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr)
+{
+ /* read 24 bits and return valid 17 bit addr */
+ int i;
+ u32 scratch, regdata;
+ __le32 scratch_le;
+ u8 *ptr = (u8 *)&scratch_le;
+
+ for (i = 0; i < 3; i++) {
+ if ((brcmf_sdioh_card_regread(sdiodev, 0, regaddr, 1,
+ &regdata)) != SUCCESS)
+ brcmf_dbg(ERROR, "Can't read!\n");
+
+ *ptr++ = (u8) regdata;
+ regaddr++;
+ }
+
+ /* Only the lower 17-bits are valid */
+ scratch = le32_to_cpu(scratch_le);
+ scratch &= 0x0001FFFF;
+ return scratch;
+}
+
+static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev)
+{
+ int err_ret;
+ u32 fbraddr;
+ u8 func;
+
+ brcmf_dbg(TRACE, "\n");
+
+ /* Get the Card's common CIS address */
+ sdiodev->func_cis_ptr[0] = brcmf_sdioh_get_cisaddr(sdiodev,
+ SDIO_CCCR_CIS);
+ brcmf_dbg(INFO, "Card's Common CIS Ptr = 0x%x\n",
+ sdiodev->func_cis_ptr[0]);
+
+ /* Get the Card's function CIS (for each function) */
+ for (fbraddr = SDIO_FBR_BASE(1), func = 1;
+ func <= sdiodev->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
+ sdiodev->func_cis_ptr[func] =
+ brcmf_sdioh_get_cisaddr(sdiodev, SDIO_FBR_CIS + fbraddr);
+ brcmf_dbg(INFO, "Function %d CIS Ptr = 0x%x\n",
+ func, sdiodev->func_cis_ptr[func]);
+ }
+
+ /* Enable Function 1 */
+ sdio_claim_host(sdiodev->func[1]);
+ err_ret = sdio_enable_func(sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
+ if (err_ret)
+ brcmf_dbg(ERROR, "Failed to enable F1 Err: 0x%08x\n", err_ret);
+
+ return false;
+}
+
+/*
+ * Public entry points & extern's
+ */
+int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev)
+{
+ int err_ret = 0;
+
+ brcmf_dbg(TRACE, "\n");
+
+ sdiodev->num_funcs = 2;
+
+ sdio_claim_host(sdiodev->func[1]);
+ err_ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE);
+ sdio_release_host(sdiodev->func[1]);
+ if (err_ret) {
+ brcmf_dbg(ERROR, "Failed to set F1 blocksize\n");
+ goto out;
+ }
+
+ sdio_claim_host(sdiodev->func[2]);
+ err_ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE);
+ sdio_release_host(sdiodev->func[2]);
+ if (err_ret) {
+ brcmf_dbg(ERROR, "Failed to set F2 blocksize\n");
+ goto out;
+ }
+
+ brcmf_sdioh_enablefuncs(sdiodev);
+
+out:
+ brcmf_dbg(TRACE, "Done\n");
+ return err_ret;
+}
+
+void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev)
+{
+ brcmf_dbg(TRACE, "\n");
+
+ /* Disable Function 2 */
+ sdio_claim_host(sdiodev->func[2]);
+ sdio_disable_func(sdiodev->func[2]);
+ sdio_release_host(sdiodev->func[2]);
+
+ /* Disable Function 1 */
+ sdio_claim_host(sdiodev->func[1]);
+ sdio_disable_func(sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
+
+}
+
+static int brcmf_ops_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int ret = 0;
+ struct brcmf_sdio_dev *sdiodev;
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(TRACE, "func->class=%x\n", func->class);
+ brcmf_dbg(TRACE, "sdio_vendor: 0x%04x\n", func->vendor);
+ brcmf_dbg(TRACE, "sdio_device: 0x%04x\n", func->device);
+ brcmf_dbg(TRACE, "Function#: 0x%04x\n", func->num);
+
+ if (func->num == 1) {
+ if (dev_get_drvdata(&func->card->dev)) {
+ brcmf_dbg(ERROR, "card private drvdata occupied\n");
+ return -ENXIO;
+ }
+ sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL);
+ if (!sdiodev)
+ return -ENOMEM;
+ sdiodev->func[0] = func->card->sdio_func[0];
+ sdiodev->func[1] = func;
+ dev_set_drvdata(&func->card->dev, sdiodev);
+
+ atomic_set(&sdiodev->suspend, false);
+ init_waitqueue_head(&sdiodev->request_byte_wait);
+ init_waitqueue_head(&sdiodev->request_word_wait);
+ init_waitqueue_head(&sdiodev->request_packet_wait);
+ init_waitqueue_head(&sdiodev->request_buffer_wait);
+ }
+
+ if (func->num == 2) {
+ sdiodev = dev_get_drvdata(&func->card->dev);
+ if ((!sdiodev) || (sdiodev->func[1]->card != func->card))
+ return -ENODEV;
+ sdiodev->func[2] = func;
+
+ brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_probe...\n");
+ ret = brcmf_sdio_probe(sdiodev);
+ }
+
+ return ret;
+}
+
+static void brcmf_ops_sdio_remove(struct sdio_func *func)
+{
+ struct brcmf_sdio_dev *sdiodev;
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(INFO, "func->class=%x\n", func->class);
+ brcmf_dbg(INFO, "sdio_vendor: 0x%04x\n", func->vendor);
+ brcmf_dbg(INFO, "sdio_device: 0x%04x\n", func->device);
+ brcmf_dbg(INFO, "Function#: 0x%04x\n", func->num);
+
+ if (func->num == 2) {
+ sdiodev = dev_get_drvdata(&func->card->dev);
+ brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_remove...\n");
+ brcmf_sdio_remove(sdiodev);
+ dev_set_drvdata(&func->card->dev, NULL);
+ kfree(sdiodev);
+ }
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int brcmf_sdio_suspend(struct device *dev)
+{
+ mmc_pm_flag_t sdio_flags;
+ struct brcmf_sdio_dev *sdiodev;
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ int ret = 0;
+
+ brcmf_dbg(TRACE, "\n");
+
+ sdiodev = dev_get_drvdata(&func->card->dev);
+
+ atomic_set(&sdiodev->suspend, true);
+
+ sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]);
+ if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
+ brcmf_dbg(ERROR, "Host can't keep power while suspended\n");
+ return -EINVAL;
+ }
+
+ ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER);
+ if (ret) {
+ brcmf_dbg(ERROR, "Failed to set pm_flags\n");
+ return ret;
+ }
+
+ brcmf_sdio_wdtmr_enable(sdiodev, false);
+
+ return ret;
+}
+
+static int brcmf_sdio_resume(struct device *dev)
+{
+ struct brcmf_sdio_dev *sdiodev;
+ struct sdio_func *func = dev_to_sdio_func(dev);
+
+ sdiodev = dev_get_drvdata(&func->card->dev);
+ brcmf_sdio_wdtmr_enable(sdiodev, true);
+ atomic_set(&sdiodev->suspend, false);
+ return 0;
+}
+
+static const struct dev_pm_ops brcmf_sdio_pm_ops = {
+ .suspend = brcmf_sdio_suspend,
+ .resume = brcmf_sdio_resume,
+};
+#endif /* CONFIG_PM_SLEEP */
+
+static struct sdio_driver brcmf_sdmmc_driver = {
+ .probe = brcmf_ops_sdio_probe,
+ .remove = brcmf_ops_sdio_remove,
+ .name = "brcmfmac",
+ .id_table = brcmf_sdmmc_ids,
+#ifdef CONFIG_PM_SLEEP
+ .drv = {
+ .pm = &brcmf_sdio_pm_ops,
+ },
+#endif /* CONFIG_PM_SLEEP */
+};
+
+/* bus register interface */
+int brcmf_bus_register(void)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ return sdio_register_driver(&brcmf_sdmmc_driver);
+}
+
+void brcmf_bus_unregister(void)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ sdio_unregister_driver(&brcmf_sdmmc_driver);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
new file mode 100644
index 000000000000..4645766b4070
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -0,0 +1,776 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/****************
+ * Common types *
+ */
+
+#ifndef _BRCMF_H_
+#define _BRCMF_H_
+
+#define BRCMF_VERSION_STR "4.218.248.5"
+
+/*******************************************************************************
+ * IO codes that are interpreted by dongle firmware
+ ******************************************************************************/
+#define BRCMF_C_UP 2
+#define BRCMF_C_SET_PROMISC 10
+#define BRCMF_C_GET_RATE 12
+#define BRCMF_C_GET_INFRA 19
+#define BRCMF_C_SET_INFRA 20
+#define BRCMF_C_GET_AUTH 21
+#define BRCMF_C_SET_AUTH 22
+#define BRCMF_C_GET_BSSID 23
+#define BRCMF_C_GET_SSID 25
+#define BRCMF_C_SET_SSID 26
+#define BRCMF_C_GET_CHANNEL 29
+#define BRCMF_C_GET_SRL 31
+#define BRCMF_C_GET_LRL 33
+#define BRCMF_C_GET_RADIO 37
+#define BRCMF_C_SET_RADIO 38
+#define BRCMF_C_GET_PHYTYPE 39
+#define BRCMF_C_SET_KEY 45
+#define BRCMF_C_SET_PASSIVE_SCAN 49
+#define BRCMF_C_SCAN 50
+#define BRCMF_C_SCAN_RESULTS 51
+#define BRCMF_C_DISASSOC 52
+#define BRCMF_C_REASSOC 53
+#define BRCMF_C_SET_ROAM_TRIGGER 55
+#define BRCMF_C_SET_ROAM_DELTA 57
+#define BRCMF_C_GET_DTIMPRD 77
+#define BRCMF_C_SET_COUNTRY 84
+#define BRCMF_C_GET_PM 85
+#define BRCMF_C_SET_PM 86
+#define BRCMF_C_GET_AP 117
+#define BRCMF_C_SET_AP 118
+#define BRCMF_C_GET_RSSI 127
+#define BRCMF_C_GET_WSEC 133
+#define BRCMF_C_SET_WSEC 134
+#define BRCMF_C_GET_PHY_NOISE 135
+#define BRCMF_C_GET_BSS_INFO 136
+#define BRCMF_C_SET_SCAN_CHANNEL_TIME 185
+#define BRCMF_C_SET_SCAN_UNASSOC_TIME 187
+#define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201
+#define BRCMF_C_GET_VALID_CHANNELS 217
+#define BRCMF_C_GET_KEY_PRIMARY 235
+#define BRCMF_C_SET_KEY_PRIMARY 236
+#define BRCMF_C_SET_SCAN_PASSIVE_TIME 258
+#define BRCMF_C_GET_VAR 262
+#define BRCMF_C_SET_VAR 263
+
+/* phy types (returned by WLC_GET_PHYTPE) */
+#define WLC_PHY_TYPE_A 0
+#define WLC_PHY_TYPE_B 1
+#define WLC_PHY_TYPE_G 2
+#define WLC_PHY_TYPE_N 4
+#define WLC_PHY_TYPE_LP 5
+#define WLC_PHY_TYPE_SSN 6
+#define WLC_PHY_TYPE_HT 7
+#define WLC_PHY_TYPE_LCN 8
+#define WLC_PHY_TYPE_NULL 0xf
+
+#define BRCMF_EVENTING_MASK_LEN 16
+
+#define TOE_TX_CSUM_OL 0x00000001
+#define TOE_RX_CSUM_OL 0x00000002
+
+#define BRCMF_BSS_INFO_VERSION 108 /* current ver of brcmf_bss_info struct */
+
+/* size of brcmf_scan_params not including variable length array */
+#define BRCMF_SCAN_PARAMS_FIXED_SIZE 64
+
+/* masks for channel and ssid count */
+#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff
+#define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16
+
+#define BRCMF_SCAN_ACTION_START 1
+#define BRCMF_SCAN_ACTION_CONTINUE 2
+#define WL_SCAN_ACTION_ABORT 3
+
+#define BRCMF_ISCAN_REQ_VERSION 1
+
+/* brcmf_iscan_results status values */
+#define BRCMF_SCAN_RESULTS_SUCCESS 0
+#define BRCMF_SCAN_RESULTS_PARTIAL 1
+#define BRCMF_SCAN_RESULTS_PENDING 2
+#define BRCMF_SCAN_RESULTS_ABORTED 3
+#define BRCMF_SCAN_RESULTS_NO_MEM 4
+
+/* Indicates this key is using soft encrypt */
+#define WL_SOFT_KEY (1 << 0)
+/* primary (ie tx) key */
+#define BRCMF_PRIMARY_KEY (1 << 1)
+/* Reserved for backward compat */
+#define WL_KF_RES_4 (1 << 4)
+/* Reserved for backward compat */
+#define WL_KF_RES_5 (1 << 5)
+/* Indicates a group key for a IBSS PEER */
+#define WL_IBSS_PEER_GROUP_KEY (1 << 6)
+
+/* For supporting multiple interfaces */
+#define BRCMF_MAX_IFS 16
+#define BRCMF_DEL_IF -0xe
+#define BRCMF_BAD_IF -0xf
+
+#define DOT11_BSSTYPE_ANY 2
+#define DOT11_MAX_DEFAULT_KEYS 4
+
+#define BRCMF_EVENT_MSG_LINK 0x01
+#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02
+#define BRCMF_EVENT_MSG_GROUP 0x04
+
+struct brcmf_event_msg {
+ __be16 version;
+ __be16 flags;
+ __be32 event_type;
+ __be32 status;
+ __be32 reason;
+ __be32 auth_type;
+ __be32 datalen;
+ u8 addr[ETH_ALEN];
+ char ifname[IFNAMSIZ];
+} __packed;
+
+struct brcm_ethhdr {
+ u16 subtype;
+ u16 length;
+ u8 version;
+ u8 oui[3];
+ u16 usr_subtype;
+} __packed;
+
+struct brcmf_event {
+ struct ethhdr eth;
+ struct brcm_ethhdr hdr;
+ struct brcmf_event_msg msg;
+} __packed;
+
+struct dngl_stats {
+ unsigned long rx_packets; /* total packets received */
+ unsigned long tx_packets; /* total packets transmitted */
+ unsigned long rx_bytes; /* total bytes received */
+ unsigned long tx_bytes; /* total bytes transmitted */
+ unsigned long rx_errors; /* bad packets received */
+ unsigned long tx_errors; /* packet transmit problems */
+ unsigned long rx_dropped; /* packets dropped by dongle */
+ unsigned long tx_dropped; /* packets dropped by dongle */
+ unsigned long multicast; /* multicast packets received */
+};
+
+/* event codes sent by the dongle to this driver */
+#define BRCMF_E_SET_SSID 0
+#define BRCMF_E_JOIN 1
+#define BRCMF_E_START 2
+#define BRCMF_E_AUTH 3
+#define BRCMF_E_AUTH_IND 4
+#define BRCMF_E_DEAUTH 5
+#define BRCMF_E_DEAUTH_IND 6
+#define BRCMF_E_ASSOC 7
+#define BRCMF_E_ASSOC_IND 8
+#define BRCMF_E_REASSOC 9
+#define BRCMF_E_REASSOC_IND 10
+#define BRCMF_E_DISASSOC 11
+#define BRCMF_E_DISASSOC_IND 12
+#define BRCMF_E_QUIET_START 13
+#define BRCMF_E_QUIET_END 14
+#define BRCMF_E_BEACON_RX 15
+#define BRCMF_E_LINK 16
+#define BRCMF_E_MIC_ERROR 17
+#define BRCMF_E_NDIS_LINK 18
+#define BRCMF_E_ROAM 19
+#define BRCMF_E_TXFAIL 20
+#define BRCMF_E_PMKID_CACHE 21
+#define BRCMF_E_RETROGRADE_TSF 22
+#define BRCMF_E_PRUNE 23
+#define BRCMF_E_AUTOAUTH 24
+#define BRCMF_E_EAPOL_MSG 25
+#define BRCMF_E_SCAN_COMPLETE 26
+#define BRCMF_E_ADDTS_IND 27
+#define BRCMF_E_DELTS_IND 28
+#define BRCMF_E_BCNSENT_IND 29
+#define BRCMF_E_BCNRX_MSG 30
+#define BRCMF_E_BCNLOST_MSG 31
+#define BRCMF_E_ROAM_PREP 32
+#define BRCMF_E_PFN_NET_FOUND 33
+#define BRCMF_E_PFN_NET_LOST 34
+#define BRCMF_E_RESET_COMPLETE 35
+#define BRCMF_E_JOIN_START 36
+#define BRCMF_E_ROAM_START 37
+#define BRCMF_E_ASSOC_START 38
+#define BRCMF_E_IBSS_ASSOC 39
+#define BRCMF_E_RADIO 40
+#define BRCMF_E_PSM_WATCHDOG 41
+#define BRCMF_E_PROBREQ_MSG 44
+#define BRCMF_E_SCAN_CONFIRM_IND 45
+#define BRCMF_E_PSK_SUP 46
+#define BRCMF_E_COUNTRY_CODE_CHANGED 47
+#define BRCMF_E_EXCEEDED_MEDIUM_TIME 48
+#define BRCMF_E_ICV_ERROR 49
+#define BRCMF_E_UNICAST_DECODE_ERROR 50
+#define BRCMF_E_MULTICAST_DECODE_ERROR 51
+#define BRCMF_E_TRACE 52
+#define BRCMF_E_IF 54
+#define BRCMF_E_RSSI 56
+#define BRCMF_E_PFN_SCAN_COMPLETE 57
+#define BRCMF_E_EXTLOG_MSG 58
+#define BRCMF_E_ACTION_FRAME 59
+#define BRCMF_E_ACTION_FRAME_COMPLETE 60
+#define BRCMF_E_PRE_ASSOC_IND 61
+#define BRCMF_E_PRE_REASSOC_IND 62
+#define BRCMF_E_CHANNEL_ADOPTED 63
+#define BRCMF_E_AP_STARTED 64
+#define BRCMF_E_DFS_AP_STOP 65
+#define BRCMF_E_DFS_AP_RESUME 66
+#define BRCMF_E_RESERVED1 67
+#define BRCMF_E_RESERVED2 68
+#define BRCMF_E_ESCAN_RESULT 69
+#define BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE 70
+#define BRCMF_E_DCS_REQUEST 73
+
+#define BRCMF_E_FIFO_CREDIT_MAP 74
+
+#define BRCMF_E_LAST 75
+
+#define BRCMF_E_STATUS_SUCCESS 0
+#define BRCMF_E_STATUS_FAIL 1
+#define BRCMF_E_STATUS_TIMEOUT 2
+#define BRCMF_E_STATUS_NO_NETWORKS 3
+#define BRCMF_E_STATUS_ABORT 4
+#define BRCMF_E_STATUS_NO_ACK 5
+#define BRCMF_E_STATUS_UNSOLICITED 6
+#define BRCMF_E_STATUS_ATTEMPT 7
+#define BRCMF_E_STATUS_PARTIAL 8
+#define BRCMF_E_STATUS_NEWSCAN 9
+#define BRCMF_E_STATUS_NEWASSOC 10
+#define BRCMF_E_STATUS_11HQUIET 11
+#define BRCMF_E_STATUS_SUPPRESS 12
+#define BRCMF_E_STATUS_NOCHANS 13
+#define BRCMF_E_STATUS_CS_ABORT 15
+#define BRCMF_E_STATUS_ERROR 16
+
+#define BRCMF_E_REASON_INITIAL_ASSOC 0
+#define BRCMF_E_REASON_LOW_RSSI 1
+#define BRCMF_E_REASON_DEAUTH 2
+#define BRCMF_E_REASON_DISASSOC 3
+#define BRCMF_E_REASON_BCNS_LOST 4
+#define BRCMF_E_REASON_MINTXRATE 9
+#define BRCMF_E_REASON_TXFAIL 10
+
+#define BRCMF_E_REASON_FAST_ROAM_FAILED 5
+#define BRCMF_E_REASON_DIRECTED_ROAM 6
+#define BRCMF_E_REASON_TSPEC_REJECTED 7
+#define BRCMF_E_REASON_BETTER_AP 8
+
+#define BRCMF_E_PRUNE_ENCR_MISMATCH 1
+#define BRCMF_E_PRUNE_BCAST_BSSID 2
+#define BRCMF_E_PRUNE_MAC_DENY 3
+#define BRCMF_E_PRUNE_MAC_NA 4
+#define BRCMF_E_PRUNE_REG_PASSV 5
+#define BRCMF_E_PRUNE_SPCT_MGMT 6
+#define BRCMF_E_PRUNE_RADAR 7
+#define BRCMF_E_RSN_MISMATCH 8
+#define BRCMF_E_PRUNE_NO_COMMON_RATES 9
+#define BRCMF_E_PRUNE_BASIC_RATES 10
+#define BRCMF_E_PRUNE_CIPHER_NA 12
+#define BRCMF_E_PRUNE_KNOWN_STA 13
+#define BRCMF_E_PRUNE_WDS_PEER 15
+#define BRCMF_E_PRUNE_QBSS_LOAD 16
+#define BRCMF_E_PRUNE_HOME_AP 17
+
+#define BRCMF_E_SUP_OTHER 0
+#define BRCMF_E_SUP_DECRYPT_KEY_DATA 1
+#define BRCMF_E_SUP_BAD_UCAST_WEP128 2
+#define BRCMF_E_SUP_BAD_UCAST_WEP40 3
+#define BRCMF_E_SUP_UNSUP_KEY_LEN 4
+#define BRCMF_E_SUP_PW_KEY_CIPHER 5
+#define BRCMF_E_SUP_MSG3_TOO_MANY_IE 6
+#define BRCMF_E_SUP_MSG3_IE_MISMATCH 7
+#define BRCMF_E_SUP_NO_INSTALL_FLAG 8
+#define BRCMF_E_SUP_MSG3_NO_GTK 9
+#define BRCMF_E_SUP_GRP_KEY_CIPHER 10
+#define BRCMF_E_SUP_GRP_MSG1_NO_GTK 11
+#define BRCMF_E_SUP_GTK_DECRYPT_FAIL 12
+#define BRCMF_E_SUP_SEND_FAIL 13
+#define BRCMF_E_SUP_DEAUTH 14
+
+#define BRCMF_E_IF_ADD 1
+#define BRCMF_E_IF_DEL 2
+#define BRCMF_E_IF_CHANGE 3
+
+#define BRCMF_E_IF_ROLE_STA 0
+#define BRCMF_E_IF_ROLE_AP 1
+#define BRCMF_E_IF_ROLE_WDS 2
+
+#define BRCMF_E_LINK_BCN_LOSS 1
+#define BRCMF_E_LINK_DISASSOC 2
+#define BRCMF_E_LINK_ASSOC_REC 3
+#define BRCMF_E_LINK_BSSCFG_DIS 4
+
+/* The level of bus communication with the dongle */
+enum brcmf_bus_state {
+ BRCMF_BUS_DOWN, /* Not ready for frame transfers */
+ BRCMF_BUS_LOAD, /* Download access only (CPU reset) */
+ BRCMF_BUS_DATA /* Ready for frame transfers */
+};
+
+/* Pattern matching filter. Specifies an offset within received packets to
+ * start matching, the pattern to match, the size of the pattern, and a bitmask
+ * that indicates which bits within the pattern should be matched.
+ */
+struct brcmf_pkt_filter_pattern_le {
+ /*
+ * Offset within received packet to start pattern matching.
+ * Offset '0' is the first byte of the ethernet header.
+ */
+ __le32 offset;
+ /* Size of the pattern. Bitmask must be the same size.*/
+ __le32 size_bytes;
+ /*
+ * Variable length mask and pattern data. mask starts at offset 0.
+ * Pattern immediately follows mask.
+ */
+ u8 mask_and_pattern[1];
+};
+
+/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */
+struct brcmf_pkt_filter_le {
+ __le32 id; /* Unique filter id, specified by app. */
+ __le32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */
+ __le32 negate_match; /* Negate the result of filter matches */
+ union { /* Filter definitions */
+ struct brcmf_pkt_filter_pattern_le pattern; /* Filter pattern */
+ } u;
+};
+
+/* IOVAR "pkt_filter_enable" parameter. */
+struct brcmf_pkt_filter_enable_le {
+ __le32 id; /* Unique filter id */
+ __le32 enable; /* Enable/disable bool */
+};
+
+/* BSS info structure
+ * Applications MUST CHECK ie_offset field and length field to access IEs and
+ * next bss_info structure in a vector (in struct brcmf_scan_results)
+ */
+struct brcmf_bss_info {
+ __le32 version; /* version field */
+ __le32 length; /* byte length of data in this record,
+ * starting at version and including IEs
+ */
+ u8 BSSID[ETH_ALEN];
+ __le16 beacon_period; /* units are Kusec */
+ __le16 capability; /* Capability information */
+ u8 SSID_len;
+ u8 SSID[32];
+ struct {
+ __le32 count; /* # rates in this set */
+ u8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */
+ } rateset; /* supported rates */
+ __le16 chanspec; /* chanspec for bss */
+ __le16 atim_window; /* units are Kusec */
+ u8 dtim_period; /* DTIM period */
+ __le16 RSSI; /* receive signal strength (in dBm) */
+ s8 phy_noise; /* noise (in dBm) */
+
+ u8 n_cap; /* BSS is 802.11N Capable */
+ /* 802.11N BSS Capabilities (based on HT_CAP_*): */
+ __le32 nbss_cap;
+ u8 ctl_ch; /* 802.11N BSS control channel number */
+ __le32 reserved32[1]; /* Reserved for expansion of BSS properties */
+ u8 flags; /* flags */
+ u8 reserved[3]; /* Reserved for expansion of BSS properties */
+ u8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */
+
+ __le16 ie_offset; /* offset at which IEs start, from beginning */
+ __le32 ie_length; /* byte length of Information Elements */
+ __le16 SNR; /* average SNR of during frame reception */
+ /* Add new fields here */
+ /* variable length Information Elements */
+};
+
+struct brcm_rateset_le {
+ /* # rates in this set */
+ __le32 count;
+ /* rates in 500kbps units w/hi bit set if basic */
+ u8 rates[WL_NUMRATES];
+};
+
+struct brcmf_ssid {
+ u32 SSID_len;
+ unsigned char SSID[32];
+};
+
+struct brcmf_ssid_le {
+ __le32 SSID_len;
+ unsigned char SSID[32];
+};
+
+struct brcmf_scan_params_le {
+ struct brcmf_ssid_le ssid_le; /* default: {0, ""} */
+ u8 bssid[ETH_ALEN]; /* default: bcast */
+ s8 bss_type; /* default: any,
+ * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT
+ */
+ u8 scan_type; /* flags, 0 use default */
+ __le32 nprobes; /* -1 use default, number of probes per channel */
+ __le32 active_time; /* -1 use default, dwell time per channel for
+ * active scanning
+ */
+ __le32 passive_time; /* -1 use default, dwell time per channel
+ * for passive scanning
+ */
+ __le32 home_time; /* -1 use default, dwell time for the
+ * home channel between channel scans
+ */
+ __le32 channel_num; /* count of channels and ssids that follow
+ *
+ * low half is count of channels in
+ * channel_list, 0 means default (use all
+ * available channels)
+ *
+ * high half is entries in struct brcmf_ssid
+ * array that follows channel_list, aligned for
+ * s32 (4 bytes) meaning an odd channel count
+ * implies a 2-byte pad between end of
+ * channel_list and first ssid
+ *
+ * if ssid count is zero, single ssid in the
+ * fixed parameter portion is assumed, otherwise
+ * ssid in the fixed portion is ignored
+ */
+ __le16 channel_list[1]; /* list of chanspecs */
+};
+
+/* incremental scan struct */
+struct brcmf_iscan_params_le {
+ __le32 version;
+ __le16 action;
+ __le16 scan_duration;
+ struct brcmf_scan_params_le params_le;
+};
+
+struct brcmf_scan_results {
+ u32 buflen;
+ u32 version;
+ u32 count;
+ struct brcmf_bss_info bss_info[1];
+};
+
+struct brcmf_scan_results_le {
+ __le32 buflen;
+ __le32 version;
+ __le32 count;
+ struct brcmf_bss_info bss_info[1];
+};
+
+/* used for association with a specific BSSID and chanspec list */
+struct brcmf_assoc_params_le {
+ /* 00:00:00:00:00:00: broadcast scan */
+ u8 bssid[ETH_ALEN];
+ /* 0: all available channels, otherwise count of chanspecs in
+ * chanspec_list */
+ __le32 chanspec_num;
+ /* list of chanspecs */
+ __le16 chanspec_list[1];
+};
+
+/* used for join with or without a specific bssid and channel list */
+struct brcmf_join_params {
+ struct brcmf_ssid_le ssid_le;
+ struct brcmf_assoc_params_le params_le;
+};
+
+/* size of brcmf_scan_results not including variable length array */
+#define BRCMF_SCAN_RESULTS_FIXED_SIZE \
+ (sizeof(struct brcmf_scan_results) - sizeof(struct brcmf_bss_info))
+
+/* incremental scan results struct */
+struct brcmf_iscan_results {
+ union {
+ u32 status;
+ __le32 status_le;
+ };
+ union {
+ struct brcmf_scan_results results;
+ struct brcmf_scan_results_le results_le;
+ };
+};
+
+/* size of brcmf_iscan_results not including variable length array */
+#define BRCMF_ISCAN_RESULTS_FIXED_SIZE \
+ (BRCMF_SCAN_RESULTS_FIXED_SIZE + \
+ offsetof(struct brcmf_iscan_results, results))
+
+struct brcmf_wsec_key {
+ u32 index; /* key index */
+ u32 len; /* key length */
+ u8 data[WLAN_MAX_KEY_LEN]; /* key data */
+ u32 pad_1[18];
+ u32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */
+ u32 flags; /* misc flags */
+ u32 pad_2[3];
+ u32 iv_initialized; /* has IV been initialized already? */
+ u32 pad_3;
+ /* Rx IV */
+ struct {
+ u32 hi; /* upper 32 bits of IV */
+ u16 lo; /* lower 16 bits of IV */
+ } rxiv;
+ u32 pad_4[2];
+ u8 ea[ETH_ALEN]; /* per station */
+};
+
+/*
+ * dongle requires same struct as above but with fields in little endian order
+ */
+struct brcmf_wsec_key_le {
+ __le32 index; /* key index */
+ __le32 len; /* key length */
+ u8 data[WLAN_MAX_KEY_LEN]; /* key data */
+ __le32 pad_1[18];
+ __le32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */
+ __le32 flags; /* misc flags */
+ __le32 pad_2[3];
+ __le32 iv_initialized; /* has IV been initialized already? */
+ __le32 pad_3;
+ /* Rx IV */
+ struct {
+ __le32 hi; /* upper 32 bits of IV */
+ __le16 lo; /* lower 16 bits of IV */
+ } rxiv;
+ __le32 pad_4[2];
+ u8 ea[ETH_ALEN]; /* per station */
+};
+
+/* Used to get specific STA parameters */
+struct brcmf_scb_val_le {
+ __le32 val;
+ u8 ea[ETH_ALEN];
+};
+
+/* channel encoding */
+struct brcmf_channel_info_le {
+ __le32 hw_channel;
+ __le32 target_channel;
+ __le32 scan_channel;
+};
+
+/* Bus independent dongle command */
+struct brcmf_dcmd {
+ uint cmd; /* common dongle cmd definition */
+ void *buf; /* pointer to user buffer */
+ uint len; /* length of user buffer */
+ u8 set; /* get or set request (optional) */
+ uint used; /* bytes read or written (optional) */
+ uint needed; /* bytes needed (optional) */
+};
+
+/* Forward decls for struct brcmf_pub (see below) */
+struct brcmf_bus; /* device bus info */
+struct brcmf_proto; /* device communication protocol info */
+struct brcmf_info; /* device driver info */
+struct brcmf_cfg80211_dev; /* cfg80211 device info */
+
+/* Common structure for module and instance linkage */
+struct brcmf_pub {
+ /* Linkage ponters */
+ struct brcmf_bus *bus;
+ struct brcmf_proto *prot;
+ struct brcmf_info *info;
+ struct brcmf_cfg80211_dev *config;
+
+ /* Internal brcmf items */
+ bool up; /* Driver up/down (to OS) */
+ bool txoff; /* Transmit flow-controlled */
+ enum brcmf_bus_state busstate;
+ uint hdrlen; /* Total BRCMF header length (proto + bus) */
+ uint maxctl; /* Max size rxctl request from proto to bus */
+ uint rxsz; /* Rx buffer size bus module should use */
+ u8 wme_dp; /* wme discard priority */
+
+ /* Dongle media info */
+ bool iswl; /* Dongle-resident driver is wl */
+ unsigned long drv_version; /* Version of dongle-resident driver */
+ u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */
+ struct dngl_stats dstats; /* Stats for dongle-based data */
+
+ /* Additional stats for the bus level */
+
+ /* Data packets sent to dongle */
+ unsigned long tx_packets;
+ /* Multicast data packets sent to dongle */
+ unsigned long tx_multicast;
+ /* Errors in sending data to dongle */
+ unsigned long tx_errors;
+ /* Control packets sent to dongle */
+ unsigned long tx_ctlpkts;
+ /* Errors sending control frames to dongle */
+ unsigned long tx_ctlerrs;
+ /* Packets sent up the network interface */
+ unsigned long rx_packets;
+ /* Multicast packets sent up the network interface */
+ unsigned long rx_multicast;
+ /* Errors processing rx data packets */
+ unsigned long rx_errors;
+ /* Control frames processed from dongle */
+ unsigned long rx_ctlpkts;
+
+ /* Errors in processing rx control frames */
+ unsigned long rx_ctlerrs;
+ /* Packets dropped locally (no memory) */
+ unsigned long rx_dropped;
+ /* Packets flushed due to unscheduled sendup thread */
+ unsigned long rx_flushed;
+ /* Number of times dpc scheduled by watchdog timer */
+ unsigned long wd_dpc_sched;
+
+ /* Number of packets where header read-ahead was used. */
+ unsigned long rx_readahead_cnt;
+ /* Number of tx packets we had to realloc for headroom */
+ unsigned long tx_realloc;
+ /* Number of flow control pkts recvd */
+ unsigned long fc_packets;
+
+ /* Last error return */
+ int bcmerror;
+ uint tickcnt;
+
+ /* Last error from dongle */
+ int dongle_error;
+
+ /* Suspend disable flag flag */
+ int suspend_disable_flag; /* "1" to disable all extra powersaving
+ during suspend */
+ int in_suspend; /* flag set to 1 when early suspend called */
+ int dtim_skip; /* dtim skip , default 0 means wake each dtim */
+
+ /* Pkt filter defination */
+ char *pktfilter[100];
+ int pktfilter_count;
+
+ u8 country_code[BRCM_CNTRY_BUF_SZ];
+ char eventmask[BRCMF_EVENTING_MASK_LEN];
+
+};
+
+struct brcmf_if_event {
+ u8 ifidx;
+ u8 action;
+ u8 flags;
+ u8 bssidx;
+};
+
+struct bcmevent_name {
+ uint event;
+ const char *name;
+};
+
+extern const struct bcmevent_name bcmevent_names[];
+
+extern uint brcmf_c_mkiovar(char *name, char *data, uint datalen,
+ char *buf, uint len);
+
+/* Indication from bus module regarding presence/insertion of dongle.
+ * Return struct brcmf_pub pointer, used as handle to OS module in later calls.
+ * Returned structure should have bus and prot pointers filled in.
+ * bus_hdrlen specifies required headroom for bus module header.
+ */
+extern struct brcmf_pub *brcmf_attach(struct brcmf_bus *bus,
+ uint bus_hdrlen);
+extern int brcmf_net_attach(struct brcmf_pub *drvr, int idx);
+extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev);
+
+extern s32 brcmf_exec_dcmd(struct net_device *dev, u32 cmd, void *arg, u32 len);
+
+/* Indication from bus module regarding removal/absence of dongle */
+extern void brcmf_detach(struct brcmf_pub *drvr);
+
+/* Indication from bus module to change flow-control state */
+extern void brcmf_txflowcontrol(struct brcmf_pub *drvr, int ifidx, bool on);
+
+extern bool brcmf_c_prec_enq(struct brcmf_pub *drvr, struct pktq *q,
+ struct sk_buff *pkt, int prec);
+
+/* Receive frame for delivery to OS. Callee disposes of rxp. */
+extern void brcmf_rx_frame(struct brcmf_pub *drvr, int ifidx,
+ struct sk_buff *rxp, int numpkt);
+
+/* Return pointer to interface name */
+extern char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
+
+/* Notify tx completion */
+extern void brcmf_txcomplete(struct brcmf_pub *drvr, struct sk_buff *txp,
+ bool success);
+
+/* Query dongle */
+extern int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len);
+
+/* OS independent layer functions */
+extern int brcmf_os_proto_block(struct brcmf_pub *drvr);
+extern int brcmf_os_proto_unblock(struct brcmf_pub *drvr);
+#ifdef BCMDBG
+extern int brcmf_write_to_file(struct brcmf_pub *drvr, const u8 *buf, int size);
+#endif /* BCMDBG */
+
+extern int brcmf_ifname2idx(struct brcmf_info *drvr_priv, char *name);
+extern int brcmf_c_host_event(struct brcmf_info *drvr_priv, int *idx,
+ void *pktdata, struct brcmf_event_msg *,
+ void **data_ptr);
+
+extern void brcmf_c_init(void);
+
+extern int brcmf_add_if(struct brcmf_info *drvr_priv, int ifidx,
+ struct net_device *ndev, char *name, u8 *mac_addr,
+ u32 flags, u8 bssidx);
+extern void brcmf_del_if(struct brcmf_info *drvr_priv, int ifidx);
+
+/* Send packet to dongle via data channel */
+extern int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx,\
+ struct sk_buff *pkt);
+
+extern int brcmf_bus_start(struct brcmf_pub *drvr);
+
+extern void brcmf_c_pktfilter_offload_set(struct brcmf_pub *drvr, char *arg);
+extern void brcmf_c_pktfilter_offload_enable(struct brcmf_pub *drvr, char *arg,
+ int enable, int master_mode);
+
+#define BRCMF_DCMD_SMLEN 256 /* "small" cmd buffer required */
+#define BRCMF_DCMD_MEDLEN 1536 /* "med" cmd buffer required */
+#define BRCMF_DCMD_MAXLEN 8192 /* max length cmd buffer required */
+
+/* message levels */
+#define BRCMF_ERROR_VAL 0x0001
+#define BRCMF_TRACE_VAL 0x0002
+#define BRCMF_INFO_VAL 0x0004
+#define BRCMF_DATA_VAL 0x0008
+#define BRCMF_CTL_VAL 0x0010
+#define BRCMF_TIMER_VAL 0x0020
+#define BRCMF_HDRS_VAL 0x0040
+#define BRCMF_BYTES_VAL 0x0080
+#define BRCMF_INTR_VAL 0x0100
+#define BRCMF_GLOM_VAL 0x0400
+#define BRCMF_EVENT_VAL 0x0800
+#define BRCMF_BTA_VAL 0x1000
+#define BRCMF_ISCAN_VAL 0x2000
+
+/* Enter idle immediately (no timeout) */
+#define BRCMF_IDLE_IMMEDIATE (-1)
+#define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change
+ when idle */
+#define BRCMF_IDLE_INTERVAL 1
+
+#endif /* _BRCMF_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
new file mode 100644
index 000000000000..a249407c9a1b
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMF_BUS_H_
+#define _BRCMF_BUS_H_
+
+/* Packet alignment for most efficient SDIO (can change based on platform) */
+#define BRCMF_SDALIGN (1 << 6)
+
+/* watchdog polling interval in ms */
+#define BRCMF_WD_POLL_MS 10
+
+/*
+ * Exported from brcmf bus module (brcmf_usb, brcmf_sdio)
+ */
+
+/* Indicate (dis)interest in finding dongles. */
+extern int brcmf_bus_register(void);
+extern void brcmf_bus_unregister(void);
+
+/* obtain linux device object providing bus function */
+extern struct device *brcmf_bus_get_device(struct brcmf_bus *bus);
+
+/* Stop bus module: clear pending frames, disable data flow */
+extern void brcmf_sdbrcm_bus_stop(struct brcmf_bus *bus);
+
+/* Initialize bus module: prepare for communication w/dongle */
+extern int brcmf_sdbrcm_bus_init(struct brcmf_pub *drvr);
+
+/* Send a data frame to the dongle. Callee disposes of txp. */
+extern int brcmf_sdbrcm_bus_txdata(struct brcmf_bus *bus, struct sk_buff *txp);
+
+/* Send/receive a control message to/from the dongle.
+ * Expects caller to enforce a single outstanding transaction.
+ */
+extern int
+brcmf_sdbrcm_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint msglen);
+
+extern int
+brcmf_sdbrcm_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint msglen);
+
+extern void brcmf_sdbrcm_wd_timer(struct brcmf_bus *bus, uint wdtick);
+
+#endif /* _BRCMF_BUS_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
new file mode 100644
index 000000000000..e34c5c3d1d55
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
@@ -0,0 +1,498 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*******************************************************************************
+ * Communicates with the dongle by using dcmd codes.
+ * For certain dcmd codes, the dongle interprets string data from the host.
+ ******************************************************************************/
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <defs.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+
+#include "dhd.h"
+#include "dhd_proto.h"
+#include "dhd_bus.h"
+#include "dhd_dbg.h"
+
+struct brcmf_proto_cdc_dcmd {
+ __le32 cmd; /* dongle command value */
+ __le32 len; /* lower 16: output buflen;
+ * upper 16: input buflen (excludes header) */
+ __le32 flags; /* flag defns given below */
+ __le32 status; /* status code returned from the device */
+};
+
+/* Max valid buffer size that can be sent to the dongle */
+#define CDC_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN)
+
+/* CDC flag definitions */
+#define CDC_DCMD_ERROR 0x01 /* 1=cmd failed */
+#define CDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */
+#define CDC_DCMD_IF_MASK 0xF000 /* I/F index */
+#define CDC_DCMD_IF_SHIFT 12
+#define CDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */
+#define CDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */
+#define CDC_DCMD_ID(flags) \
+ (((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT)
+
+/*
+ * BDC header - Broadcom specific extension of CDC.
+ * Used on data packets to convey priority across USB.
+ */
+#define BDC_HEADER_LEN 4
+#define BDC_PROTO_VER 1 /* Protocol version */
+#define BDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */
+#define BDC_FLAG_VER_SHIFT 4 /* Protocol version shift */
+#define BDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */
+#define BDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */
+#define BDC_PRIORITY_MASK 0x7
+#define BDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */
+#define BDC_FLAG2_IF_SHIFT 0
+
+#define BDC_GET_IF_IDX(hdr) \
+ ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT))
+#define BDC_SET_IF_IDX(hdr, idx) \
+ ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \
+ ((idx) << BDC_FLAG2_IF_SHIFT)))
+
+struct brcmf_proto_bdc_header {
+ u8 flags;
+ u8 priority; /* 802.1d Priority, 4:7 flow control info for usb */
+ u8 flags2;
+ u8 rssi;
+};
+
+
+#define RETRIES 2 /* # of retries to retrieve matching dcmd response */
+#define BUS_HEADER_LEN (16+BRCMF_SDALIGN) /* Must be atleast SDPCM_RESERVE
+ * (amount of header tha might be added)
+ * plus any space that might be needed
+ * for alignment padding.
+ */
+#define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for
+ * round off at the end of buffer
+ */
+
+struct brcmf_proto {
+ u16 reqid;
+ u8 pending;
+ u32 lastcmd;
+ u8 bus_header[BUS_HEADER_LEN];
+ struct brcmf_proto_cdc_dcmd msg;
+ unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN];
+};
+
+static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr)
+{
+ struct brcmf_proto *prot = drvr->prot;
+ int len = le32_to_cpu(prot->msg.len) +
+ sizeof(struct brcmf_proto_cdc_dcmd);
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* NOTE : cdc->msg.len holds the desired length of the buffer to be
+ * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
+ * is actually sent to the dongle
+ */
+ if (len > CDC_MAX_MSG_SIZE)
+ len = CDC_MAX_MSG_SIZE;
+
+ /* Send request */
+ return brcmf_sdbrcm_bus_txctl(drvr->bus, (unsigned char *)&prot->msg,
+ len);
+}
+
+static int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
+{
+ int ret;
+ struct brcmf_proto *prot = drvr->prot;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ do {
+ ret = brcmf_sdbrcm_bus_rxctl(drvr->bus,
+ (unsigned char *)&prot->msg,
+ len + sizeof(struct brcmf_proto_cdc_dcmd));
+ if (ret < 0)
+ break;
+ } while (CDC_DCMD_ID(le32_to_cpu(prot->msg.flags)) != id);
+
+ return ret;
+}
+
+int
+brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
+ void *buf, uint len)
+{
+ struct brcmf_proto *prot = drvr->prot;
+ struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
+ void *info;
+ int ret = 0, retries = 0;
+ u32 id, flags;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len);
+
+ /* Respond "bcmerror" and "bcmerrorstr" with local cache */
+ if (cmd == BRCMF_C_GET_VAR && buf) {
+ if (!strcmp((char *)buf, "bcmerrorstr")) {
+ strncpy((char *)buf, "bcm_error",
+ BCME_STRLEN);
+ goto done;
+ } else if (!strcmp((char *)buf, "bcmerror")) {
+ *(int *)buf = drvr->dongle_error;
+ goto done;
+ }
+ }
+
+ memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
+
+ msg->cmd = cpu_to_le32(cmd);
+ msg->len = cpu_to_le32(len);
+ flags = (++prot->reqid << CDC_DCMD_ID_SHIFT);
+ flags = (flags & ~CDC_DCMD_IF_MASK) |
+ (ifidx << CDC_DCMD_IF_SHIFT);
+ msg->flags = cpu_to_le32(flags);
+
+ if (buf)
+ memcpy(prot->buf, buf, len);
+
+ ret = brcmf_proto_cdc_msg(drvr);
+ if (ret < 0) {
+ brcmf_dbg(ERROR, "brcmf_proto_cdc_msg failed w/status %d\n",
+ ret);
+ goto done;
+ }
+
+retry:
+ /* wait for interrupt and get first fragment */
+ ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
+ if (ret < 0)
+ goto done;
+
+ flags = le32_to_cpu(msg->flags);
+ id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT;
+
+ if ((id < prot->reqid) && (++retries < RETRIES))
+ goto retry;
+ if (id != prot->reqid) {
+ brcmf_dbg(ERROR, "%s: unexpected request id %d (expected %d)\n",
+ brcmf_ifname(drvr, ifidx), id, prot->reqid);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Check info buffer */
+ info = (void *)&msg[1];
+
+ /* Copy info buffer */
+ if (buf) {
+ if (ret < (int)len)
+ len = ret;
+ memcpy(buf, info, len);
+ }
+
+ /* Check the ERROR flag */
+ if (flags & CDC_DCMD_ERROR) {
+ ret = le32_to_cpu(msg->status);
+ /* Cache error from dongle */
+ drvr->dongle_error = ret;
+ }
+
+done:
+ return ret;
+}
+
+int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
+ void *buf, uint len)
+{
+ struct brcmf_proto *prot = drvr->prot;
+ struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
+ int ret = 0;
+ u32 flags, id;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len);
+
+ memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
+
+ msg->cmd = cpu_to_le32(cmd);
+ msg->len = cpu_to_le32(len);
+ flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET;
+ flags = (flags & ~CDC_DCMD_IF_MASK) |
+ (ifidx << CDC_DCMD_IF_SHIFT);
+ msg->flags = cpu_to_le32(flags);
+
+ if (buf)
+ memcpy(prot->buf, buf, len);
+
+ ret = brcmf_proto_cdc_msg(drvr);
+ if (ret < 0)
+ goto done;
+
+ ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
+ if (ret < 0)
+ goto done;
+
+ flags = le32_to_cpu(msg->flags);
+ id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT;
+
+ if (id != prot->reqid) {
+ brcmf_dbg(ERROR, "%s: unexpected request id %d (expected %d)\n",
+ brcmf_ifname(drvr, ifidx), id, prot->reqid);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Check the ERROR flag */
+ if (flags & CDC_DCMD_ERROR) {
+ ret = le32_to_cpu(msg->status);
+ /* Cache error from dongle */
+ drvr->dongle_error = ret;
+ }
+
+done:
+ return ret;
+}
+
+int
+brcmf_proto_dcmd(struct brcmf_pub *drvr, int ifidx, struct brcmf_dcmd *dcmd,
+ int len)
+{
+ struct brcmf_proto *prot = drvr->prot;
+ int ret = -1;
+
+ if (drvr->busstate == BRCMF_BUS_DOWN) {
+ brcmf_dbg(ERROR, "bus is down. we have nothing to do.\n");
+ return ret;
+ }
+ brcmf_os_proto_block(drvr);
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (len > BRCMF_DCMD_MAXLEN)
+ goto done;
+
+ if (prot->pending == true) {
+ brcmf_dbg(TRACE, "CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
+ dcmd->cmd, (unsigned long)dcmd->cmd, prot->lastcmd,
+ (unsigned long)prot->lastcmd);
+ if (dcmd->cmd == BRCMF_C_SET_VAR ||
+ dcmd->cmd == BRCMF_C_GET_VAR)
+ brcmf_dbg(TRACE, "iovar cmd=%s\n", (char *)dcmd->buf);
+
+ goto done;
+ }
+
+ prot->pending = true;
+ prot->lastcmd = dcmd->cmd;
+ if (dcmd->set)
+ ret = brcmf_proto_cdc_set_dcmd(drvr, ifidx, dcmd->cmd,
+ dcmd->buf, len);
+ else {
+ ret = brcmf_proto_cdc_query_dcmd(drvr, ifidx, dcmd->cmd,
+ dcmd->buf, len);
+ if (ret > 0)
+ dcmd->used = ret -
+ sizeof(struct brcmf_proto_cdc_dcmd);
+ }
+
+ if (ret >= 0)
+ ret = 0;
+ else {
+ struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
+ /* len == needed when set/query fails from dongle */
+ dcmd->needed = le32_to_cpu(msg->len);
+ }
+
+ /* Intercept the wme_dp dongle cmd here */
+ if (!ret && dcmd->cmd == BRCMF_C_SET_VAR &&
+ !strcmp(dcmd->buf, "wme_dp")) {
+ int slen;
+ __le32 val = 0;
+
+ slen = strlen("wme_dp") + 1;
+ if (len >= (int)(slen + sizeof(int)))
+ memcpy(&val, (char *)dcmd->buf + slen, sizeof(int));
+ drvr->wme_dp = (u8) le32_to_cpu(val);
+ }
+
+ prot->pending = false;
+
+done:
+ brcmf_os_proto_unblock(drvr);
+
+ return ret;
+}
+
+static bool pkt_sum_needed(struct sk_buff *skb)
+{
+ return skb->ip_summed == CHECKSUM_PARTIAL;
+}
+
+static void pkt_set_sum_good(struct sk_buff *skb, bool x)
+{
+ skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE);
+}
+
+void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
+ struct sk_buff *pktbuf)
+{
+ struct brcmf_proto_bdc_header *h;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Push BDC header used to convey priority for buses that don't */
+
+ skb_push(pktbuf, BDC_HEADER_LEN);
+
+ h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
+
+ h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
+ if (pkt_sum_needed(pktbuf))
+ h->flags |= BDC_FLAG_SUM_NEEDED;
+
+ h->priority = (pktbuf->priority & BDC_PRIORITY_MASK);
+ h->flags2 = 0;
+ h->rssi = 0;
+ BDC_SET_IF_IDX(h, ifidx);
+}
+
+int brcmf_proto_hdrpull(struct brcmf_pub *drvr, int *ifidx,
+ struct sk_buff *pktbuf)
+{
+ struct brcmf_proto_bdc_header *h;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Pop BDC header used to convey priority for buses that don't */
+
+ if (pktbuf->len < BDC_HEADER_LEN) {
+ brcmf_dbg(ERROR, "rx data too short (%d < %d)\n",
+ pktbuf->len, BDC_HEADER_LEN);
+ return -EBADE;
+ }
+
+ h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
+
+ *ifidx = BDC_GET_IF_IDX(h);
+ if (*ifidx >= BRCMF_MAX_IFS) {
+ brcmf_dbg(ERROR, "rx data ifnum out of range (%d)\n", *ifidx);
+ return -EBADE;
+ }
+
+ if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) !=
+ BDC_PROTO_VER) {
+ brcmf_dbg(ERROR, "%s: non-BDC packet received, flags 0x%x\n",
+ brcmf_ifname(drvr, *ifidx), h->flags);
+ return -EBADE;
+ }
+
+ if (h->flags & BDC_FLAG_SUM_GOOD) {
+ brcmf_dbg(INFO, "%s: BDC packet received with good rx-csum, flags 0x%x\n",
+ brcmf_ifname(drvr, *ifidx), h->flags);
+ pkt_set_sum_good(pktbuf, true);
+ }
+
+ pktbuf->priority = h->priority & BDC_PRIORITY_MASK;
+
+ skb_pull(pktbuf, BDC_HEADER_LEN);
+
+ return 0;
+}
+
+int brcmf_proto_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_proto *cdc;
+
+ cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC);
+ if (!cdc)
+ goto fail;
+
+ /* ensure that the msg buf directly follows the cdc msg struct */
+ if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) {
+ brcmf_dbg(ERROR, "struct brcmf_proto is not correctly defined\n");
+ goto fail;
+ }
+
+ drvr->prot = cdc;
+ drvr->hdrlen += BDC_HEADER_LEN;
+ drvr->maxctl = BRCMF_DCMD_MAXLEN +
+ sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN;
+ return 0;
+
+fail:
+ kfree(cdc);
+ return -ENOMEM;
+}
+
+/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
+void brcmf_proto_detach(struct brcmf_pub *drvr)
+{
+ kfree(drvr->prot);
+ drvr->prot = NULL;
+}
+
+void brcmf_proto_dstats(struct brcmf_pub *drvr)
+{
+ /* No stats from dongle added yet, copy bus stats */
+ drvr->dstats.tx_packets = drvr->tx_packets;
+ drvr->dstats.tx_errors = drvr->tx_errors;
+ drvr->dstats.rx_packets = drvr->rx_packets;
+ drvr->dstats.rx_errors = drvr->rx_errors;
+ drvr->dstats.rx_dropped = drvr->rx_dropped;
+ drvr->dstats.multicast = drvr->rx_multicast;
+ return;
+}
+
+int brcmf_proto_init(struct brcmf_pub *drvr)
+{
+ int ret = 0;
+ char buf[128];
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ brcmf_os_proto_block(drvr);
+
+ /* Get the device MAC address */
+ strcpy(buf, "cur_etheraddr");
+ ret = brcmf_proto_cdc_query_dcmd(drvr, 0, BRCMF_C_GET_VAR,
+ buf, sizeof(buf));
+ if (ret < 0) {
+ brcmf_os_proto_unblock(drvr);
+ return ret;
+ }
+ memcpy(drvr->mac, buf, ETH_ALEN);
+
+ brcmf_os_proto_unblock(drvr);
+
+ ret = brcmf_c_preinit_dcmds(drvr);
+
+ /* Always assumes wl for now */
+ drvr->iswl = true;
+
+ return ret;
+}
+
+void brcmf_proto_stop(struct brcmf_pub *drvr)
+{
+ /* Nothing to do for CDC */
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
new file mode 100644
index 000000000000..891826197f96
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
@@ -0,0 +1,895 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <asm/unaligned.h>
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include "dhd.h"
+#include "dhd_bus.h"
+#include "dhd_proto.h"
+#include "dhd_dbg.h"
+
+#define BRCM_OUI "\x00\x10\x18"
+#define DOT11_OUI_LEN 3
+#define BCMILCP_BCM_SUBTYPE_EVENT 1
+#define PKTFILTER_BUF_SIZE 2048
+#define BRCMF_ARPOL_MODE 0xb /* agent|snoop|peer_autoreply */
+
+int brcmf_msg_level;
+
+#define MSGTRACE_VERSION 1
+
+#define BRCMF_PKT_FILTER_FIXED_LEN offsetof(struct brcmf_pkt_filter_le, u)
+#define BRCMF_PKT_FILTER_PATTERN_FIXED_LEN \
+ offsetof(struct brcmf_pkt_filter_pattern_le, mask_and_pattern)
+
+#ifdef BCMDBG
+static const char brcmf_version[] =
+ "Dongle Host Driver, version " BRCMF_VERSION_STR "\nCompiled on "
+ __DATE__ " at " __TIME__;
+#else
+static const char brcmf_version[] =
+ "Dongle Host Driver, version " BRCMF_VERSION_STR;
+#endif
+
+/* Message trace header */
+struct msgtrace_hdr {
+ u8 version;
+ u8 spare;
+ __be16 len; /* Len of the trace */
+ __be32 seqnum; /* Sequence number of message. Useful
+ * if the messsage has been lost
+ * because of DMA error or a bus reset
+ * (ex: SDIO Func2)
+ */
+ __be32 discarded_bytes; /* Number of discarded bytes because of
+ trace overflow */
+ __be32 discarded_printf; /* Number of discarded printf
+ because of trace overflow */
+} __packed;
+
+
+uint
+brcmf_c_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)
+{
+ uint len;
+
+ len = strlen(name) + 1;
+
+ if ((len + datalen) > buflen)
+ return 0;
+
+ strncpy(buf, name, buflen);
+
+ /* append data onto the end of the name string */
+ memcpy(&buf[len], data, datalen);
+ len += datalen;
+
+ return len;
+}
+
+void brcmf_c_init(void)
+{
+ /* Init global variables at run-time, not as part of the declaration.
+ * This is required to support init/de-init of the driver.
+ * Initialization
+ * of globals as part of the declaration results in non-deterministic
+ * behaviour since the value of the globals may be different on the
+ * first time that the driver is initialized vs subsequent
+ * initializations.
+ */
+ brcmf_msg_level = BRCMF_ERROR_VAL;
+}
+
+bool brcmf_c_prec_enq(struct brcmf_pub *drvr, struct pktq *q,
+ struct sk_buff *pkt, int prec)
+{
+ struct sk_buff *p;
+ int eprec = -1; /* precedence to evict from */
+ bool discard_oldest;
+
+ /* Fast case, precedence queue is not full and we are also not
+ * exceeding total queue length
+ */
+ if (!pktq_pfull(q, prec) && !pktq_full(q)) {
+ brcmu_pktq_penq(q, prec, pkt);
+ return true;
+ }
+
+ /* Determine precedence from which to evict packet, if any */
+ if (pktq_pfull(q, prec))
+ eprec = prec;
+ else if (pktq_full(q)) {
+ p = brcmu_pktq_peek_tail(q, &eprec);
+ if (eprec > prec)
+ return false;
+ }
+
+ /* Evict if needed */
+ if (eprec >= 0) {
+ /* Detect queueing to unconfigured precedence */
+ discard_oldest = ac_bitmap_tst(drvr->wme_dp, eprec);
+ if (eprec == prec && !discard_oldest)
+ return false; /* refuse newer (incoming) packet */
+ /* Evict packet according to discard policy */
+ p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) :
+ brcmu_pktq_pdeq_tail(q, eprec);
+ if (p == NULL)
+ brcmf_dbg(ERROR, "brcmu_pktq_penq() failed, oldest %d\n",
+ discard_oldest);
+
+ brcmu_pkt_buf_free_skb(p);
+ }
+
+ /* Enqueue */
+ p = brcmu_pktq_penq(q, prec, pkt);
+ if (p == NULL)
+ brcmf_dbg(ERROR, "brcmu_pktq_penq() failed\n");
+
+ return p != NULL;
+}
+
+#ifdef BCMDBG
+static void
+brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data)
+{
+ uint i, status, reason;
+ bool group = false, flush_txq = false, link = false;
+ char *auth_str, *event_name;
+ unsigned char *buf;
+ char err_msg[256], eabuf[ETHER_ADDR_STR_LEN];
+ static struct {
+ uint event;
+ char *event_name;
+ } event_names[] = {
+ {
+ BRCMF_E_SET_SSID, "SET_SSID"}, {
+ BRCMF_E_JOIN, "JOIN"}, {
+ BRCMF_E_START, "START"}, {
+ BRCMF_E_AUTH, "AUTH"}, {
+ BRCMF_E_AUTH_IND, "AUTH_IND"}, {
+ BRCMF_E_DEAUTH, "DEAUTH"}, {
+ BRCMF_E_DEAUTH_IND, "DEAUTH_IND"}, {
+ BRCMF_E_ASSOC, "ASSOC"}, {
+ BRCMF_E_ASSOC_IND, "ASSOC_IND"}, {
+ BRCMF_E_REASSOC, "REASSOC"}, {
+ BRCMF_E_REASSOC_IND, "REASSOC_IND"}, {
+ BRCMF_E_DISASSOC, "DISASSOC"}, {
+ BRCMF_E_DISASSOC_IND, "DISASSOC_IND"}, {
+ BRCMF_E_QUIET_START, "START_QUIET"}, {
+ BRCMF_E_QUIET_END, "END_QUIET"}, {
+ BRCMF_E_BEACON_RX, "BEACON_RX"}, {
+ BRCMF_E_LINK, "LINK"}, {
+ BRCMF_E_MIC_ERROR, "MIC_ERROR"}, {
+ BRCMF_E_NDIS_LINK, "NDIS_LINK"}, {
+ BRCMF_E_ROAM, "ROAM"}, {
+ BRCMF_E_TXFAIL, "TXFAIL"}, {
+ BRCMF_E_PMKID_CACHE, "PMKID_CACHE"}, {
+ BRCMF_E_RETROGRADE_TSF, "RETROGRADE_TSF"}, {
+ BRCMF_E_PRUNE, "PRUNE"}, {
+ BRCMF_E_AUTOAUTH, "AUTOAUTH"}, {
+ BRCMF_E_EAPOL_MSG, "EAPOL_MSG"}, {
+ BRCMF_E_SCAN_COMPLETE, "SCAN_COMPLETE"}, {
+ BRCMF_E_ADDTS_IND, "ADDTS_IND"}, {
+ BRCMF_E_DELTS_IND, "DELTS_IND"}, {
+ BRCMF_E_BCNSENT_IND, "BCNSENT_IND"}, {
+ BRCMF_E_BCNRX_MSG, "BCNRX_MSG"}, {
+ BRCMF_E_BCNLOST_MSG, "BCNLOST_MSG"}, {
+ BRCMF_E_ROAM_PREP, "ROAM_PREP"}, {
+ BRCMF_E_PFN_NET_FOUND, "PNO_NET_FOUND"}, {
+ BRCMF_E_PFN_NET_LOST, "PNO_NET_LOST"}, {
+ BRCMF_E_RESET_COMPLETE, "RESET_COMPLETE"}, {
+ BRCMF_E_JOIN_START, "JOIN_START"}, {
+ BRCMF_E_ROAM_START, "ROAM_START"}, {
+ BRCMF_E_ASSOC_START, "ASSOC_START"}, {
+ BRCMF_E_IBSS_ASSOC, "IBSS_ASSOC"}, {
+ BRCMF_E_RADIO, "RADIO"}, {
+ BRCMF_E_PSM_WATCHDOG, "PSM_WATCHDOG"}, {
+ BRCMF_E_PROBREQ_MSG, "PROBREQ_MSG"}, {
+ BRCMF_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND"}, {
+ BRCMF_E_PSK_SUP, "PSK_SUP"}, {
+ BRCMF_E_COUNTRY_CODE_CHANGED, "COUNTRY_CODE_CHANGED"}, {
+ BRCMF_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME"}, {
+ BRCMF_E_ICV_ERROR, "ICV_ERROR"}, {
+ BRCMF_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR"}, {
+ BRCMF_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR"}, {
+ BRCMF_E_TRACE, "TRACE"}, {
+ BRCMF_E_ACTION_FRAME, "ACTION FRAME"}, {
+ BRCMF_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"}, {
+ BRCMF_E_IF, "IF"}, {
+ BRCMF_E_RSSI, "RSSI"}, {
+ BRCMF_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}
+ };
+ uint event_type, flags, auth_type, datalen;
+ static u32 seqnum_prev;
+ struct msgtrace_hdr hdr;
+ u32 nblost;
+ char *s, *p;
+
+ event_type = be32_to_cpu(event->event_type);
+ flags = be16_to_cpu(event->flags);
+ status = be32_to_cpu(event->status);
+ reason = be32_to_cpu(event->reason);
+ auth_type = be32_to_cpu(event->auth_type);
+ datalen = be32_to_cpu(event->datalen);
+ /* debug dump of event messages */
+ sprintf(eabuf, "%pM", event->addr);
+
+ event_name = "UNKNOWN";
+ for (i = 0; i < ARRAY_SIZE(event_names); i++) {
+ if (event_names[i].event == event_type)
+ event_name = event_names[i].event_name;
+ }
+
+ brcmf_dbg(EVENT, "EVENT: %s, event ID = %d\n", event_name, event_type);
+ brcmf_dbg(EVENT, "flags 0x%04x, status %d, reason %d, auth_type %d MAC %s\n",
+ flags, status, reason, auth_type, eabuf);
+
+ if (flags & BRCMF_EVENT_MSG_LINK)
+ link = true;
+ if (flags & BRCMF_EVENT_MSG_GROUP)
+ group = true;
+ if (flags & BRCMF_EVENT_MSG_FLUSHTXQ)
+ flush_txq = true;
+
+ switch (event_type) {
+ case BRCMF_E_START:
+ case BRCMF_E_DEAUTH:
+ case BRCMF_E_DISASSOC:
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", event_name, eabuf);
+ break;
+
+ case BRCMF_E_ASSOC_IND:
+ case BRCMF_E_REASSOC_IND:
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", event_name, eabuf);
+ break;
+
+ case BRCMF_E_ASSOC:
+ case BRCMF_E_REASSOC:
+ if (status == BRCMF_E_STATUS_SUCCESS)
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, SUCCESS\n",
+ event_name, eabuf);
+ else if (status == BRCMF_E_STATUS_TIMEOUT)
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, TIMEOUT\n",
+ event_name, eabuf);
+ else if (status == BRCMF_E_STATUS_FAIL)
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, FAILURE, reason %d\n",
+ event_name, eabuf, (int)reason);
+ else
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, unexpected status %d\n",
+ event_name, eabuf, (int)status);
+ break;
+
+ case BRCMF_E_DEAUTH_IND:
+ case BRCMF_E_DISASSOC_IND:
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, reason %d\n",
+ event_name, eabuf, (int)reason);
+ break;
+
+ case BRCMF_E_AUTH:
+ case BRCMF_E_AUTH_IND:
+ if (auth_type == WLAN_AUTH_OPEN)
+ auth_str = "Open System";
+ else if (auth_type == WLAN_AUTH_SHARED_KEY)
+ auth_str = "Shared Key";
+ else {
+ sprintf(err_msg, "AUTH unknown: %d", (int)auth_type);
+ auth_str = err_msg;
+ }
+ if (event_type == BRCMF_E_AUTH_IND)
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s\n",
+ event_name, eabuf, auth_str);
+ else if (status == BRCMF_E_STATUS_SUCCESS)
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s, SUCCESS\n",
+ event_name, eabuf, auth_str);
+ else if (status == BRCMF_E_STATUS_TIMEOUT)
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s, TIMEOUT\n",
+ event_name, eabuf, auth_str);
+ else if (status == BRCMF_E_STATUS_FAIL) {
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s, FAILURE, reason %d\n",
+ event_name, eabuf, auth_str, (int)reason);
+ }
+
+ break;
+
+ case BRCMF_E_JOIN:
+ case BRCMF_E_ROAM:
+ case BRCMF_E_SET_SSID:
+ if (status == BRCMF_E_STATUS_SUCCESS)
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n",
+ event_name, eabuf);
+ else if (status == BRCMF_E_STATUS_FAIL)
+ brcmf_dbg(EVENT, "MACEVENT: %s, failed\n", event_name);
+ else if (status == BRCMF_E_STATUS_NO_NETWORKS)
+ brcmf_dbg(EVENT, "MACEVENT: %s, no networks found\n",
+ event_name);
+ else
+ brcmf_dbg(EVENT, "MACEVENT: %s, unexpected status %d\n",
+ event_name, (int)status);
+ break;
+
+ case BRCMF_E_BEACON_RX:
+ if (status == BRCMF_E_STATUS_SUCCESS)
+ brcmf_dbg(EVENT, "MACEVENT: %s, SUCCESS\n", event_name);
+ else if (status == BRCMF_E_STATUS_FAIL)
+ brcmf_dbg(EVENT, "MACEVENT: %s, FAIL\n", event_name);
+ else
+ brcmf_dbg(EVENT, "MACEVENT: %s, status %d\n",
+ event_name, status);
+ break;
+
+ case BRCMF_E_LINK:
+ brcmf_dbg(EVENT, "MACEVENT: %s %s\n",
+ event_name, link ? "UP" : "DOWN");
+ break;
+
+ case BRCMF_E_MIC_ERROR:
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, Group %d, Flush %d\n",
+ event_name, eabuf, group, flush_txq);
+ break;
+
+ case BRCMF_E_ICV_ERROR:
+ case BRCMF_E_UNICAST_DECODE_ERROR:
+ case BRCMF_E_MULTICAST_DECODE_ERROR:
+ brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", event_name, eabuf);
+ break;
+
+ case BRCMF_E_TXFAIL:
+ brcmf_dbg(EVENT, "MACEVENT: %s, RA %s\n", event_name, eabuf);
+ break;
+
+ case BRCMF_E_SCAN_COMPLETE:
+ case BRCMF_E_PMKID_CACHE:
+ brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name);
+ break;
+
+ case BRCMF_E_PFN_NET_FOUND:
+ case BRCMF_E_PFN_NET_LOST:
+ case BRCMF_E_PFN_SCAN_COMPLETE:
+ brcmf_dbg(EVENT, "PNOEVENT: %s\n", event_name);
+ break;
+
+ case BRCMF_E_PSK_SUP:
+ case BRCMF_E_PRUNE:
+ brcmf_dbg(EVENT, "MACEVENT: %s, status %d, reason %d\n",
+ event_name, (int)status, (int)reason);
+ break;
+
+ case BRCMF_E_TRACE:
+ buf = (unsigned char *) event_data;
+ memcpy(&hdr, buf, sizeof(struct msgtrace_hdr));
+
+ if (hdr.version != MSGTRACE_VERSION) {
+ brcmf_dbg(ERROR,
+ "MACEVENT: %s [unsupported version --> brcmf"
+ " version:%d dongle version:%d]\n",
+ event_name, MSGTRACE_VERSION, hdr.version);
+ /* Reset datalen to avoid display below */
+ datalen = 0;
+ break;
+ }
+
+ /* There are 2 bytes available at the end of data */
+ *(buf + sizeof(struct msgtrace_hdr)
+ + be16_to_cpu(hdr.len)) = '\0';
+
+ if (be32_to_cpu(hdr.discarded_bytes)
+ || be32_to_cpu(hdr.discarded_printf))
+ brcmf_dbg(ERROR,
+ "WLC_E_TRACE: [Discarded traces in dongle -->"
+ " discarded_bytes %d discarded_printf %d]\n",
+ be32_to_cpu(hdr.discarded_bytes),
+ be32_to_cpu(hdr.discarded_printf));
+
+ nblost = be32_to_cpu(hdr.seqnum) - seqnum_prev - 1;
+ if (nblost > 0)
+ brcmf_dbg(ERROR, "WLC_E_TRACE: [Event lost --> seqnum "
+ " %d nblost %d\n", be32_to_cpu(hdr.seqnum),
+ nblost);
+ seqnum_prev = be32_to_cpu(hdr.seqnum);
+
+ /* Display the trace buffer. Advance from \n to \n to
+ * avoid display big
+ * printf (issue with Linux printk )
+ */
+ p = (char *)&buf[sizeof(struct msgtrace_hdr)];
+ while ((s = strstr(p, "\n")) != NULL) {
+ *s = '\0';
+ printk(KERN_DEBUG"%s\n", p);
+ p = s + 1;
+ }
+ printk(KERN_DEBUG "%s\n", p);
+
+ /* Reset datalen to avoid display below */
+ datalen = 0;
+ break;
+
+ case BRCMF_E_RSSI:
+ brcmf_dbg(EVENT, "MACEVENT: %s %d\n",
+ event_name, be32_to_cpu(*((__be32 *)event_data)));
+ break;
+
+ default:
+ brcmf_dbg(EVENT,
+ "MACEVENT: %s %d, MAC %s, status %d, reason %d, "
+ "auth %d\n", event_name, event_type, eabuf,
+ (int)status, (int)reason, (int)auth_type);
+ break;
+ }
+
+ /* show any appended data */
+ if (datalen) {
+ buf = (unsigned char *) event_data;
+ brcmf_dbg(EVENT, " data (%d) : ", datalen);
+ for (i = 0; i < datalen; i++)
+ brcmf_dbg(EVENT, " 0x%02x ", *buf++);
+ brcmf_dbg(EVENT, "\n");
+ }
+}
+#endif /* BCMDBG */
+
+int
+brcmf_c_host_event(struct brcmf_info *drvr_priv, int *ifidx, void *pktdata,
+ struct brcmf_event_msg *event, void **data_ptr)
+{
+ /* check whether packet is a BRCM event pkt */
+ struct brcmf_event *pvt_data = (struct brcmf_event *) pktdata;
+ struct brcmf_if_event *ifevent;
+ char *event_data;
+ u32 type, status;
+ u16 flags;
+ int evlen;
+
+ if (memcmp(BRCM_OUI, &pvt_data->hdr.oui[0], DOT11_OUI_LEN)) {
+ brcmf_dbg(ERROR, "mismatched OUI, bailing\n");
+ return -EBADE;
+ }
+
+ /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */
+ if (get_unaligned_be16(&pvt_data->hdr.usr_subtype) !=
+ BCMILCP_BCM_SUBTYPE_EVENT) {
+ brcmf_dbg(ERROR, "mismatched subtype, bailing\n");
+ return -EBADE;
+ }
+
+ *data_ptr = &pvt_data[1];
+ event_data = *data_ptr;
+
+ /* memcpy since BRCM event pkt may be unaligned. */
+ memcpy(event, &pvt_data->msg, sizeof(struct brcmf_event_msg));
+
+ type = get_unaligned_be32(&event->event_type);
+ flags = get_unaligned_be16(&event->flags);
+ status = get_unaligned_be32(&event->status);
+ evlen = get_unaligned_be32(&event->datalen) +
+ sizeof(struct brcmf_event);
+
+ switch (type) {
+ case BRCMF_E_IF:
+ ifevent = (struct brcmf_if_event *) event_data;
+ brcmf_dbg(TRACE, "if event\n");
+
+ if (ifevent->ifidx > 0 && ifevent->ifidx < BRCMF_MAX_IFS) {
+ if (ifevent->action == BRCMF_E_IF_ADD)
+ brcmf_add_if(drvr_priv, ifevent->ifidx, NULL,
+ event->ifname,
+ pvt_data->eth.h_dest,
+ ifevent->flags, ifevent->bssidx);
+ else
+ brcmf_del_if(drvr_priv, ifevent->ifidx);
+ } else {
+ brcmf_dbg(ERROR, "Invalid ifidx %d for %s\n",
+ ifevent->ifidx, event->ifname);
+ }
+
+ /* send up the if event: btamp user needs it */
+ *ifidx = brcmf_ifname2idx(drvr_priv, event->ifname);
+ break;
+
+ /* These are what external supplicant/authenticator wants */
+ case BRCMF_E_LINK:
+ case BRCMF_E_ASSOC_IND:
+ case BRCMF_E_REASSOC_IND:
+ case BRCMF_E_DISASSOC_IND:
+ case BRCMF_E_MIC_ERROR:
+ default:
+ /* Fall through: this should get _everything_ */
+
+ *ifidx = brcmf_ifname2idx(drvr_priv, event->ifname);
+ brcmf_dbg(TRACE, "MAC event %d, flags %x, status %x\n",
+ type, flags, status);
+
+ /* put it back to BRCMF_E_NDIS_LINK */
+ if (type == BRCMF_E_NDIS_LINK) {
+ u32 temp1;
+ __be32 temp2;
+
+ temp1 = get_unaligned_be32(&event->event_type);
+ brcmf_dbg(TRACE, "Converted to WLC_E_LINK type %d\n",
+ temp1);
+
+ temp2 = cpu_to_be32(BRCMF_E_NDIS_LINK);
+ memcpy((void *)(&pvt_data->msg.event_type), &temp2,
+ sizeof(pvt_data->msg.event_type));
+ }
+ break;
+ }
+
+#ifdef BCMDBG
+ brcmf_c_show_host_event(event, event_data);
+#endif /* BCMDBG */
+
+ return 0;
+}
+
+/* Convert user's input in hex pattern to byte-size mask */
+static int brcmf_c_pattern_atoh(char *src, char *dst)
+{
+ int i;
+ if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) {
+ brcmf_dbg(ERROR, "Mask invalid format. Needs to start with 0x\n");
+ return -EINVAL;
+ }
+ src = src + 2; /* Skip past 0x */
+ if (strlen(src) % 2 != 0) {
+ brcmf_dbg(ERROR, "Mask invalid format. Length must be even.\n");
+ return -EINVAL;
+ }
+ for (i = 0; *src != '\0'; i++) {
+ unsigned long res;
+ char num[3];
+ strncpy(num, src, 2);
+ num[2] = '\0';
+ if (kstrtoul(num, 16, &res))
+ return -EINVAL;
+ dst[i] = (u8)res;
+ src += 2;
+ }
+ return i;
+}
+
+void
+brcmf_c_pktfilter_offload_enable(struct brcmf_pub *drvr, char *arg, int enable,
+ int master_mode)
+{
+ unsigned long res;
+ char *argv[8];
+ int i = 0;
+ const char *str;
+ int buf_len;
+ int str_len;
+ char *arg_save = NULL, *arg_org = NULL;
+ int rc;
+ char buf[128];
+ struct brcmf_pkt_filter_enable_le enable_parm;
+ struct brcmf_pkt_filter_enable_le *pkt_filterp;
+ __le32 mmode_le;
+
+ arg_save = kmalloc(strlen(arg) + 1, GFP_ATOMIC);
+ if (!arg_save)
+ goto fail;
+
+ arg_org = arg_save;
+ memcpy(arg_save, arg, strlen(arg) + 1);
+
+ argv[i] = strsep(&arg_save, " ");
+
+ i = 0;
+ if (NULL == argv[i]) {
+ brcmf_dbg(ERROR, "No args provided\n");
+ goto fail;
+ }
+
+ str = "pkt_filter_enable";
+ str_len = strlen(str);
+ strncpy(buf, str, str_len);
+ buf[str_len] = '\0';
+ buf_len = str_len + 1;
+
+ pkt_filterp = (struct brcmf_pkt_filter_enable_le *) (buf + str_len + 1);
+
+ /* Parse packet filter id. */
+ enable_parm.id = 0;
+ if (!kstrtoul(argv[i], 0, &res))
+ enable_parm.id = cpu_to_le32((u32)res);
+
+ /* Parse enable/disable value. */
+ enable_parm.enable = cpu_to_le32(enable);
+
+ buf_len += sizeof(enable_parm);
+ memcpy((char *)pkt_filterp, &enable_parm, sizeof(enable_parm));
+
+ /* Enable/disable the specified filter. */
+ rc = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, buf, buf_len);
+ rc = rc >= 0 ? 0 : rc;
+ if (rc)
+ brcmf_dbg(TRACE, "failed to add pktfilter %s, retcode = %d\n",
+ arg, rc);
+ else
+ brcmf_dbg(TRACE, "successfully added pktfilter %s\n", arg);
+
+ /* Contorl the master mode */
+ mmode_le = cpu_to_le32(master_mode);
+ brcmf_c_mkiovar("pkt_filter_mode", (char *)&mmode_le, 4, buf,
+ sizeof(buf));
+ rc = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, buf,
+ sizeof(buf));
+ rc = rc >= 0 ? 0 : rc;
+ if (rc)
+ brcmf_dbg(TRACE, "failed to add pktfilter %s, retcode = %d\n",
+ arg, rc);
+
+fail:
+ kfree(arg_org);
+}
+
+void brcmf_c_pktfilter_offload_set(struct brcmf_pub *drvr, char *arg)
+{
+ const char *str;
+ struct brcmf_pkt_filter_le pkt_filter;
+ struct brcmf_pkt_filter_le *pkt_filterp;
+ unsigned long res;
+ int buf_len;
+ int str_len;
+ int rc;
+ u32 mask_size;
+ u32 pattern_size;
+ char *argv[8], *buf = NULL;
+ int i = 0;
+ char *arg_save = NULL, *arg_org = NULL;
+
+ arg_save = kstrdup(arg, GFP_ATOMIC);
+ if (!arg_save)
+ goto fail;
+
+ arg_org = arg_save;
+
+ buf = kmalloc(PKTFILTER_BUF_SIZE, GFP_ATOMIC);
+ if (!buf)
+ goto fail;
+
+ argv[i] = strsep(&arg_save, " ");
+ while (argv[i++])
+ argv[i] = strsep(&arg_save, " ");
+
+ i = 0;
+ if (NULL == argv[i]) {
+ brcmf_dbg(ERROR, "No args provided\n");
+ goto fail;
+ }
+
+ str = "pkt_filter_add";
+ strcpy(buf, str);
+ str_len = strlen(str);
+ buf_len = str_len + 1;
+
+ pkt_filterp = (struct brcmf_pkt_filter_le *) (buf + str_len + 1);
+
+ /* Parse packet filter id. */
+ pkt_filter.id = 0;
+ if (!kstrtoul(argv[i], 0, &res))
+ pkt_filter.id = cpu_to_le32((u32)res);
+
+ if (NULL == argv[++i]) {
+ brcmf_dbg(ERROR, "Polarity not provided\n");
+ goto fail;
+ }
+
+ /* Parse filter polarity. */
+ pkt_filter.negate_match = 0;
+ if (!kstrtoul(argv[i], 0, &res))
+ pkt_filter.negate_match = cpu_to_le32((u32)res);
+
+ if (NULL == argv[++i]) {
+ brcmf_dbg(ERROR, "Filter type not provided\n");
+ goto fail;
+ }
+
+ /* Parse filter type. */
+ pkt_filter.type = 0;
+ if (!kstrtoul(argv[i], 0, &res))
+ pkt_filter.type = cpu_to_le32((u32)res);
+
+ if (NULL == argv[++i]) {
+ brcmf_dbg(ERROR, "Offset not provided\n");
+ goto fail;
+ }
+
+ /* Parse pattern filter offset. */
+ pkt_filter.u.pattern.offset = 0;
+ if (!kstrtoul(argv[i], 0, &res))
+ pkt_filter.u.pattern.offset = cpu_to_le32((u32)res);
+
+ if (NULL == argv[++i]) {
+ brcmf_dbg(ERROR, "Bitmask not provided\n");
+ goto fail;
+ }
+
+ /* Parse pattern filter mask. */
+ mask_size =
+ brcmf_c_pattern_atoh
+ (argv[i], (char *)pkt_filterp->u.pattern.mask_and_pattern);
+
+ if (NULL == argv[++i]) {
+ brcmf_dbg(ERROR, "Pattern not provided\n");
+ goto fail;
+ }
+
+ /* Parse pattern filter pattern. */
+ pattern_size =
+ brcmf_c_pattern_atoh(argv[i],
+ (char *)&pkt_filterp->u.pattern.
+ mask_and_pattern[mask_size]);
+
+ if (mask_size != pattern_size) {
+ brcmf_dbg(ERROR, "Mask and pattern not the same size\n");
+ goto fail;
+ }
+
+ pkt_filter.u.pattern.size_bytes = cpu_to_le32(mask_size);
+ buf_len += BRCMF_PKT_FILTER_FIXED_LEN;
+ buf_len += (BRCMF_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size);
+
+ /* Keep-alive attributes are set in local
+ * variable (keep_alive_pkt), and
+ ** then memcpy'ed into buffer (keep_alive_pktp) since there is no
+ ** guarantee that the buffer is properly aligned.
+ */
+ memcpy((char *)pkt_filterp,
+ &pkt_filter,
+ BRCMF_PKT_FILTER_FIXED_LEN + BRCMF_PKT_FILTER_PATTERN_FIXED_LEN);
+
+ rc = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, buf, buf_len);
+ rc = rc >= 0 ? 0 : rc;
+
+ if (rc)
+ brcmf_dbg(TRACE, "failed to add pktfilter %s, retcode = %d\n",
+ arg, rc);
+ else
+ brcmf_dbg(TRACE, "successfully added pktfilter %s\n", arg);
+
+fail:
+ kfree(arg_org);
+
+ kfree(buf);
+}
+
+static void brcmf_c_arp_offload_set(struct brcmf_pub *drvr, int arp_mode)
+{
+ char iovbuf[32];
+ int retcode;
+
+ brcmf_c_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
+ retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR,
+ iovbuf, sizeof(iovbuf));
+ retcode = retcode >= 0 ? 0 : retcode;
+ if (retcode)
+ brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, retcode = %d\n",
+ arp_mode, retcode);
+ else
+ brcmf_dbg(TRACE, "successfully set ARP offload mode to 0x%x\n",
+ arp_mode);
+}
+
+static void brcmf_c_arp_offload_enable(struct brcmf_pub *drvr, int arp_enable)
+{
+ char iovbuf[32];
+ int retcode;
+
+ brcmf_c_mkiovar("arpoe", (char *)&arp_enable, 4,
+ iovbuf, sizeof(iovbuf));
+ retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR,
+ iovbuf, sizeof(iovbuf));
+ retcode = retcode >= 0 ? 0 : retcode;
+ if (retcode)
+ brcmf_dbg(TRACE, "failed to enable ARP offload to %d, retcode = %d\n",
+ arp_enable, retcode);
+ else
+ brcmf_dbg(TRACE, "successfully enabled ARP offload to %d\n",
+ arp_enable);
+}
+
+int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
+{
+ char iovbuf[BRCMF_EVENTING_MASK_LEN + 12]; /* Room for
+ "event_msgs" + '\0' + bitvec */
+ uint up = 0;
+ char buf[128], *ptr;
+ u32 dongle_align = BRCMF_SDALIGN;
+ u32 glom = 0;
+ u32 roaming = 1;
+ uint bcn_timeout = 3;
+ int scan_assoc_time = 40;
+ int scan_unassoc_time = 40;
+ int i;
+
+ brcmf_os_proto_block(drvr);
+
+ /* Set Country code */
+ if (drvr->country_code[0] != 0) {
+ if (brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_COUNTRY,
+ drvr->country_code,
+ sizeof(drvr->country_code)) < 0)
+ brcmf_dbg(ERROR, "country code setting failed\n");
+ }
+
+ /* query for 'ver' to get version info from firmware */
+ memset(buf, 0, sizeof(buf));
+ ptr = buf;
+ brcmf_c_mkiovar("ver", NULL, 0, buf, sizeof(buf));
+ brcmf_proto_cdc_query_dcmd(drvr, 0, BRCMF_C_GET_VAR, buf, sizeof(buf));
+ strsep(&ptr, "\n");
+ /* Print fw version info */
+ brcmf_dbg(ERROR, "Firmware version = %s\n", buf);
+
+ /* Match Host and Dongle rx alignment */
+ brcmf_c_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf,
+ sizeof(iovbuf));
+ brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
+ sizeof(iovbuf));
+
+ /* disable glom option per default */
+ brcmf_c_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf));
+ brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
+ sizeof(iovbuf));
+
+ /* Setup timeout if Beacons are lost and roam is off to report
+ link down */
+ brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf,
+ sizeof(iovbuf));
+ brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
+ sizeof(iovbuf));
+
+ /* Enable/Disable build-in roaming to allowed ext supplicant to take
+ of romaing */
+ brcmf_c_mkiovar("roam_off", (char *)&roaming, 4,
+ iovbuf, sizeof(iovbuf));
+ brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
+ sizeof(iovbuf));
+
+ /* Force STA UP */
+ brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_UP, (char *)&up, sizeof(up));
+
+ /* Setup event_msgs */
+ brcmf_c_mkiovar("event_msgs", drvr->eventmask, BRCMF_EVENTING_MASK_LEN,
+ iovbuf, sizeof(iovbuf));
+ brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
+ sizeof(iovbuf));
+
+ brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_CHANNEL_TIME,
+ (char *)&scan_assoc_time, sizeof(scan_assoc_time));
+ brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_UNASSOC_TIME,
+ (char *)&scan_unassoc_time, sizeof(scan_unassoc_time));
+
+ /* Set and enable ARP offload feature */
+ brcmf_c_arp_offload_set(drvr, BRCMF_ARPOL_MODE);
+ brcmf_c_arp_offload_enable(drvr, true);
+
+ /* Set up pkt filter */
+ for (i = 0; i < drvr->pktfilter_count; i++) {
+ brcmf_c_pktfilter_offload_set(drvr, drvr->pktfilter[i]);
+ brcmf_c_pktfilter_offload_enable(drvr, drvr->pktfilter[i],
+ 0, true);
+ }
+
+ brcmf_os_proto_unblock(drvr);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
new file mode 100644
index 000000000000..7467922f0536
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMF_DBG_H_
+#define _BRCMF_DBG_H_
+
+#if defined(BCMDBG)
+
+#define brcmf_dbg(level, fmt, ...) \
+do { \
+ if (BRCMF_ERROR_VAL == BRCMF_##level##_VAL) { \
+ if (brcmf_msg_level & BRCMF_##level##_VAL) { \
+ if (net_ratelimit()) \
+ printk(KERN_DEBUG "%s: " fmt, \
+ __func__, ##__VA_ARGS__); \
+ } \
+ } else { \
+ if (brcmf_msg_level & BRCMF_##level##_VAL) { \
+ printk(KERN_DEBUG "%s: " fmt, \
+ __func__, ##__VA_ARGS__); \
+ } \
+ } \
+} while (0)
+
+#define BRCMF_DATA_ON() (brcmf_msg_level & BRCMF_DATA_VAL)
+#define BRCMF_CTL_ON() (brcmf_msg_level & BRCMF_CTL_VAL)
+#define BRCMF_HDRS_ON() (brcmf_msg_level & BRCMF_HDRS_VAL)
+#define BRCMF_BYTES_ON() (brcmf_msg_level & BRCMF_BYTES_VAL)
+#define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL)
+
+#else /* (defined BCMDBG) || (defined BCMDBG) */
+
+#define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
+
+#define BRCMF_DATA_ON() 0
+#define BRCMF_CTL_ON() 0
+#define BRCMF_HDRS_ON() 0
+#define BRCMF_BYTES_ON() 0
+#define BRCMF_GLOM_ON() 0
+
+#endif /* defined(BCMDBG) */
+
+extern int brcmf_msg_level;
+
+#endif /* _BRCMF_DBG_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
new file mode 100644
index 000000000000..4acbac5a74c6
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -0,0 +1,1356 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/hardirq.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <net/cfg80211.h>
+#include <net/rtnetlink.h>
+#include <defs.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+
+#include "dhd.h"
+#include "dhd_bus.h"
+#include "dhd_proto.h"
+#include "dhd_dbg.h"
+#include "wl_cfg80211.h"
+#include "bcmchip.h"
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN fullmac driver.");
+MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN fullmac cards");
+MODULE_LICENSE("Dual BSD/GPL");
+
+
+/* Interface control information */
+struct brcmf_if {
+ struct brcmf_info *info; /* back pointer to brcmf_info */
+ /* OS/stack specifics */
+ struct net_device *ndev;
+ struct net_device_stats stats;
+ int idx; /* iface idx in dongle */
+ int state; /* interface state */
+ u8 mac_addr[ETH_ALEN]; /* assigned MAC address */
+};
+
+/* Local private structure (extension of pub) */
+struct brcmf_info {
+ struct brcmf_pub pub;
+
+ /* OS/stack specifics */
+ struct brcmf_if *iflist[BRCMF_MAX_IFS];
+
+ struct mutex proto_block;
+
+ struct work_struct setmacaddr_work;
+ struct work_struct multicast_work;
+ u8 macvalue[ETH_ALEN];
+ atomic_t pend_8021x_cnt;
+};
+
+/* Error bits */
+module_param(brcmf_msg_level, int, 0);
+
+
+static int brcmf_net2idx(struct brcmf_info *drvr_priv, struct net_device *ndev)
+{
+ int i = 0;
+
+ while (i < BRCMF_MAX_IFS) {
+ if (drvr_priv->iflist[i] && drvr_priv->iflist[i]->ndev == ndev)
+ return i;
+ i++;
+ }
+
+ return BRCMF_BAD_IF;
+}
+
+int brcmf_ifname2idx(struct brcmf_info *drvr_priv, char *name)
+{
+ int i = BRCMF_MAX_IFS;
+ struct brcmf_if *ifp;
+
+ if (name == NULL || *name == '\0')
+ return 0;
+
+ while (--i > 0) {
+ ifp = drvr_priv->iflist[i];
+ if (ifp && !strncmp(ifp->ndev->name, name, IFNAMSIZ))
+ break;
+ }
+
+ brcmf_dbg(TRACE, "return idx %d for \"%s\"\n", i, name);
+
+ return i; /* default - the primary interface */
+}
+
+char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
+{
+ struct brcmf_info *drvr_priv = drvr->info;
+
+ if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
+ brcmf_dbg(ERROR, "ifidx %d out of range\n", ifidx);
+ return "<if_bad>";
+ }
+
+ if (drvr_priv->iflist[ifidx] == NULL) {
+ brcmf_dbg(ERROR, "null i/f %d\n", ifidx);
+ return "<if_null>";
+ }
+
+ if (drvr_priv->iflist[ifidx]->ndev)
+ return drvr_priv->iflist[ifidx]->ndev->name;
+
+ return "<if_none>";
+}
+
+static void _brcmf_set_multicast_list(struct work_struct *work)
+{
+ struct net_device *ndev;
+ struct netdev_hw_addr *ha;
+ u32 dcmd_value, cnt;
+ __le32 cnt_le;
+ __le32 dcmd_le_value;
+
+ struct brcmf_dcmd dcmd;
+ char *buf, *bufp;
+ uint buflen;
+ int ret;
+
+ struct brcmf_info *drvr_priv = container_of(work, struct brcmf_info,
+ multicast_work);
+
+ ndev = drvr_priv->iflist[0]->ndev;
+ cnt = netdev_mc_count(ndev);
+
+ /* Determine initial value of allmulti flag */
+ dcmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false;
+
+ /* Send down the multicast list first. */
+
+ buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETH_ALEN);
+ bufp = buf = kmalloc(buflen, GFP_ATOMIC);
+ if (!bufp)
+ return;
+
+ strcpy(bufp, "mcast_list");
+ bufp += strlen("mcast_list") + 1;
+
+ cnt_le = cpu_to_le32(cnt);
+ memcpy(bufp, &cnt_le, sizeof(cnt));
+ bufp += sizeof(cnt_le);
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ if (!cnt)
+ break;
+ memcpy(bufp, ha->addr, ETH_ALEN);
+ bufp += ETH_ALEN;
+ cnt--;
+ }
+
+ memset(&dcmd, 0, sizeof(dcmd));
+ dcmd.cmd = BRCMF_C_SET_VAR;
+ dcmd.buf = buf;
+ dcmd.len = buflen;
+ dcmd.set = true;
+
+ ret = brcmf_proto_dcmd(&drvr_priv->pub, 0, &dcmd, dcmd.len);
+ if (ret < 0) {
+ brcmf_dbg(ERROR, "%s: set mcast_list failed, cnt %d\n",
+ brcmf_ifname(&drvr_priv->pub, 0), cnt);
+ dcmd_value = cnt ? true : dcmd_value;
+ }
+
+ kfree(buf);
+
+ /* Now send the allmulti setting. This is based on the setting in the
+ * net_device flags, but might be modified above to be turned on if we
+ * were trying to set some addresses and dongle rejected it...
+ */
+
+ buflen = sizeof("allmulti") + sizeof(dcmd_value);
+ buf = kmalloc(buflen, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ dcmd_le_value = cpu_to_le32(dcmd_value);
+
+ if (!brcmf_c_mkiovar
+ ("allmulti", (void *)&dcmd_le_value,
+ sizeof(dcmd_le_value), buf, buflen)) {
+ brcmf_dbg(ERROR, "%s: mkiovar failed for allmulti, datalen %d buflen %u\n",
+ brcmf_ifname(&drvr_priv->pub, 0),
+ (int)sizeof(dcmd_value), buflen);
+ kfree(buf);
+ return;
+ }
+
+ memset(&dcmd, 0, sizeof(dcmd));
+ dcmd.cmd = BRCMF_C_SET_VAR;
+ dcmd.buf = buf;
+ dcmd.len = buflen;
+ dcmd.set = true;
+
+ ret = brcmf_proto_dcmd(&drvr_priv->pub, 0, &dcmd, dcmd.len);
+ if (ret < 0) {
+ brcmf_dbg(ERROR, "%s: set allmulti %d failed\n",
+ brcmf_ifname(&drvr_priv->pub, 0),
+ le32_to_cpu(dcmd_le_value));
+ }
+
+ kfree(buf);
+
+ /* Finally, pick up the PROMISC flag as well, like the NIC
+ driver does */
+
+ dcmd_value = (ndev->flags & IFF_PROMISC) ? true : false;
+ dcmd_le_value = cpu_to_le32(dcmd_value);
+
+ memset(&dcmd, 0, sizeof(dcmd));
+ dcmd.cmd = BRCMF_C_SET_PROMISC;
+ dcmd.buf = &dcmd_le_value;
+ dcmd.len = sizeof(dcmd_le_value);
+ dcmd.set = true;
+
+ ret = brcmf_proto_dcmd(&drvr_priv->pub, 0, &dcmd, dcmd.len);
+ if (ret < 0) {
+ brcmf_dbg(ERROR, "%s: set promisc %d failed\n",
+ brcmf_ifname(&drvr_priv->pub, 0),
+ le32_to_cpu(dcmd_le_value));
+ }
+}
+
+static void
+_brcmf_set_mac_address(struct work_struct *work)
+{
+ char buf[32];
+ struct brcmf_dcmd dcmd;
+ int ret;
+
+ struct brcmf_info *drvr_priv = container_of(work, struct brcmf_info,
+ setmacaddr_work);
+
+ brcmf_dbg(TRACE, "enter\n");
+ if (!brcmf_c_mkiovar("cur_etheraddr", (char *)drvr_priv->macvalue,
+ ETH_ALEN, buf, 32)) {
+ brcmf_dbg(ERROR, "%s: mkiovar failed for cur_etheraddr\n",
+ brcmf_ifname(&drvr_priv->pub, 0));
+ return;
+ }
+ memset(&dcmd, 0, sizeof(dcmd));
+ dcmd.cmd = BRCMF_C_SET_VAR;
+ dcmd.buf = buf;
+ dcmd.len = 32;
+ dcmd.set = true;
+
+ ret = brcmf_proto_dcmd(&drvr_priv->pub, 0, &dcmd, dcmd.len);
+ if (ret < 0)
+ brcmf_dbg(ERROR, "%s: set cur_etheraddr failed\n",
+ brcmf_ifname(&drvr_priv->pub, 0));
+ else
+ memcpy(drvr_priv->iflist[0]->ndev->dev_addr,
+ drvr_priv->macvalue, ETH_ALEN);
+
+ return;
+}
+
+static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
+{
+ struct brcmf_info *drvr_priv = *(struct brcmf_info **)
+ netdev_priv(ndev);
+ struct sockaddr *sa = (struct sockaddr *)addr;
+ int ifidx;
+
+ ifidx = brcmf_net2idx(drvr_priv, ndev);
+ if (ifidx == BRCMF_BAD_IF)
+ return -1;
+
+ memcpy(&drvr_priv->macvalue, sa->sa_data, ETH_ALEN);
+ schedule_work(&drvr_priv->setmacaddr_work);
+ return 0;
+}
+
+static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
+{
+ struct brcmf_info *drvr_priv = *(struct brcmf_info **)
+ netdev_priv(ndev);
+ int ifidx;
+
+ ifidx = brcmf_net2idx(drvr_priv, ndev);
+ if (ifidx == BRCMF_BAD_IF)
+ return;
+
+ schedule_work(&drvr_priv->multicast_work);
+}
+
+int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx, struct sk_buff *pktbuf)
+{
+ struct brcmf_info *drvr_priv = drvr->info;
+
+ /* Reject if down */
+ if (!drvr->up || (drvr->busstate == BRCMF_BUS_DOWN))
+ return -ENODEV;
+
+ /* Update multicast statistic */
+ if (pktbuf->len >= ETH_ALEN) {
+ u8 *pktdata = (u8 *) (pktbuf->data);
+ struct ethhdr *eh = (struct ethhdr *)pktdata;
+
+ if (is_multicast_ether_addr(eh->h_dest))
+ drvr->tx_multicast++;
+ if (ntohs(eh->h_proto) == ETH_P_PAE)
+ atomic_inc(&drvr_priv->pend_8021x_cnt);
+ }
+
+ /* If the protocol uses a data header, apply it */
+ brcmf_proto_hdrpush(drvr, ifidx, pktbuf);
+
+ /* Use bus module to send data frame */
+ return brcmf_sdbrcm_bus_txdata(drvr->bus, pktbuf);
+}
+
+static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ int ret;
+ struct brcmf_info *drvr_priv = *(struct brcmf_info **)
+ netdev_priv(ndev);
+ int ifidx;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Reject if down */
+ if (!drvr_priv->pub.up || (drvr_priv->pub.busstate == BRCMF_BUS_DOWN)) {
+ brcmf_dbg(ERROR, "xmit rejected pub.up=%d busstate=%d\n",
+ drvr_priv->pub.up, drvr_priv->pub.busstate);
+ netif_stop_queue(ndev);
+ return -ENODEV;
+ }
+
+ ifidx = brcmf_net2idx(drvr_priv, ndev);
+ if (ifidx == BRCMF_BAD_IF) {
+ brcmf_dbg(ERROR, "bad ifidx %d\n", ifidx);
+ netif_stop_queue(ndev);
+ return -ENODEV;
+ }
+
+ /* Make sure there's enough room for any header */
+ if (skb_headroom(skb) < drvr_priv->pub.hdrlen) {
+ struct sk_buff *skb2;
+
+ brcmf_dbg(INFO, "%s: insufficient headroom\n",
+ brcmf_ifname(&drvr_priv->pub, ifidx));
+ drvr_priv->pub.tx_realloc++;
+ skb2 = skb_realloc_headroom(skb, drvr_priv->pub.hdrlen);
+ dev_kfree_skb(skb);
+ skb = skb2;
+ if (skb == NULL) {
+ brcmf_dbg(ERROR, "%s: skb_realloc_headroom failed\n",
+ brcmf_ifname(&drvr_priv->pub, ifidx));
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = brcmf_sendpkt(&drvr_priv->pub, ifidx, skb);
+
+done:
+ if (ret)
+ drvr_priv->pub.dstats.tx_dropped++;
+ else
+ drvr_priv->pub.tx_packets++;
+
+ /* Return ok: we always eat the packet */
+ return 0;
+}
+
+void brcmf_txflowcontrol(struct brcmf_pub *drvr, int ifidx, bool state)
+{
+ struct net_device *ndev;
+ struct brcmf_info *drvr_priv = drvr->info;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ drvr->txoff = state;
+ ndev = drvr_priv->iflist[ifidx]->ndev;
+ if (state == ON)
+ netif_stop_queue(ndev);
+ else
+ netif_wake_queue(ndev);
+}
+
+static int brcmf_host_event(struct brcmf_info *drvr_priv, int *ifidx,
+ void *pktdata, struct brcmf_event_msg *event,
+ void **data)
+{
+ int bcmerror = 0;
+
+ bcmerror = brcmf_c_host_event(drvr_priv, ifidx, pktdata, event, data);
+ if (bcmerror != 0)
+ return bcmerror;
+
+ if (drvr_priv->iflist[*ifidx]->ndev)
+ brcmf_cfg80211_event(drvr_priv->iflist[*ifidx]->ndev,
+ event, *data);
+
+ return bcmerror;
+}
+
+void brcmf_rx_frame(struct brcmf_pub *drvr, int ifidx, struct sk_buff *skb,
+ int numpkt)
+{
+ struct brcmf_info *drvr_priv = drvr->info;
+ unsigned char *eth;
+ uint len;
+ void *data;
+ struct sk_buff *pnext, *save_pktbuf;
+ int i;
+ struct brcmf_if *ifp;
+ struct brcmf_event_msg event;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ save_pktbuf = skb;
+
+ for (i = 0; skb && i < numpkt; i++, skb = pnext) {
+
+ pnext = skb->next;
+ skb->next = NULL;
+
+ /* Get the protocol, maintain skb around eth_type_trans()
+ * The main reason for this hack is for the limitation of
+ * Linux 2.4 where 'eth_type_trans' uses the
+ * 'net->hard_header_len'
+ * to perform skb_pull inside vs ETH_HLEN. Since to avoid
+ * coping of the packet coming from the network stack to add
+ * BDC, Hardware header etc, during network interface
+ * registration
+ * we set the 'net->hard_header_len' to ETH_HLEN + extra space
+ * required
+ * for BDC, Hardware header etc. and not just the ETH_HLEN
+ */
+ eth = skb->data;
+ len = skb->len;
+
+ ifp = drvr_priv->iflist[ifidx];
+ if (ifp == NULL)
+ ifp = drvr_priv->iflist[0];
+
+ skb->dev = ifp->ndev;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ if (skb->pkt_type == PACKET_MULTICAST)
+ drvr_priv->pub.rx_multicast++;
+
+ skb->data = eth;
+ skb->len = len;
+
+ /* Strip header, count, deliver upward */
+ skb_pull(skb, ETH_HLEN);
+
+ /* Process special event packets and then discard them */
+ if (ntohs(skb->protocol) == ETH_P_LINK_CTL)
+ brcmf_host_event(drvr_priv, &ifidx,
+ skb_mac_header(skb),
+ &event, &data);
+
+ if (drvr_priv->iflist[ifidx] &&
+ !drvr_priv->iflist[ifidx]->state)
+ ifp = drvr_priv->iflist[ifidx];
+
+ if (ifp->ndev)
+ ifp->ndev->last_rx = jiffies;
+
+ drvr->dstats.rx_bytes += skb->len;
+ drvr->rx_packets++; /* Local count */
+
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ /* If the receive is not processed inside an ISR,
+ * the softirqd must be woken explicitly to service
+ * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled
+ * by netif_rx_ni(), but in earlier kernels, we need
+ * to do it manually.
+ */
+ netif_rx_ni(skb);
+ }
+}
+
+void brcmf_txcomplete(struct brcmf_pub *drvr, struct sk_buff *txp, bool success)
+{
+ uint ifidx;
+ struct brcmf_info *drvr_priv = drvr->info;
+ struct ethhdr *eh;
+ u16 type;
+
+ brcmf_proto_hdrpull(drvr, &ifidx, txp);
+
+ eh = (struct ethhdr *)(txp->data);
+ type = ntohs(eh->h_proto);
+
+ if (type == ETH_P_PAE)
+ atomic_dec(&drvr_priv->pend_8021x_cnt);
+
+}
+
+static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev)
+{
+ struct brcmf_info *drvr_priv = *(struct brcmf_info **)
+ netdev_priv(ndev);
+ struct brcmf_if *ifp;
+ int ifidx;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ ifidx = brcmf_net2idx(drvr_priv, ndev);
+ if (ifidx == BRCMF_BAD_IF)
+ return NULL;
+
+ ifp = drvr_priv->iflist[ifidx];
+
+ if (drvr_priv->pub.up)
+ /* Use the protocol to get dongle stats */
+ brcmf_proto_dstats(&drvr_priv->pub);
+
+ /* Copy dongle stats to net device stats */
+ ifp->stats.rx_packets = drvr_priv->pub.dstats.rx_packets;
+ ifp->stats.tx_packets = drvr_priv->pub.dstats.tx_packets;
+ ifp->stats.rx_bytes = drvr_priv->pub.dstats.rx_bytes;
+ ifp->stats.tx_bytes = drvr_priv->pub.dstats.tx_bytes;
+ ifp->stats.rx_errors = drvr_priv->pub.dstats.rx_errors;
+ ifp->stats.tx_errors = drvr_priv->pub.dstats.tx_errors;
+ ifp->stats.rx_dropped = drvr_priv->pub.dstats.rx_dropped;
+ ifp->stats.tx_dropped = drvr_priv->pub.dstats.tx_dropped;
+ ifp->stats.multicast = drvr_priv->pub.dstats.multicast;
+
+ return &ifp->stats;
+}
+
+/* Retrieve current toe component enables, which are kept
+ as a bitmap in toe_ol iovar */
+static int brcmf_toe_get(struct brcmf_info *drvr_priv, int ifidx, u32 *toe_ol)
+{
+ struct brcmf_dcmd dcmd;
+ __le32 toe_le;
+ char buf[32];
+ int ret;
+
+ memset(&dcmd, 0, sizeof(dcmd));
+
+ dcmd.cmd = BRCMF_C_GET_VAR;
+ dcmd.buf = buf;
+ dcmd.len = (uint) sizeof(buf);
+ dcmd.set = false;
+
+ strcpy(buf, "toe_ol");
+ ret = brcmf_proto_dcmd(&drvr_priv->pub, ifidx, &dcmd, dcmd.len);
+ if (ret < 0) {
+ /* Check for older dongle image that doesn't support toe_ol */
+ if (ret == -EIO) {
+ brcmf_dbg(ERROR, "%s: toe not supported by device\n",
+ brcmf_ifname(&drvr_priv->pub, ifidx));
+ return -EOPNOTSUPP;
+ }
+
+ brcmf_dbg(INFO, "%s: could not get toe_ol: ret=%d\n",
+ brcmf_ifname(&drvr_priv->pub, ifidx), ret);
+ return ret;
+ }
+
+ memcpy(&toe_le, buf, sizeof(u32));
+ *toe_ol = le32_to_cpu(toe_le);
+ return 0;
+}
+
+/* Set current toe component enables in toe_ol iovar,
+ and set toe global enable iovar */
+static int brcmf_toe_set(struct brcmf_info *drvr_priv, int ifidx, u32 toe_ol)
+{
+ struct brcmf_dcmd dcmd;
+ char buf[32];
+ int ret;
+ __le32 toe_le = cpu_to_le32(toe_ol);
+
+ memset(&dcmd, 0, sizeof(dcmd));
+
+ dcmd.cmd = BRCMF_C_SET_VAR;
+ dcmd.buf = buf;
+ dcmd.len = (uint) sizeof(buf);
+ dcmd.set = true;
+
+ /* Set toe_ol as requested */
+ strcpy(buf, "toe_ol");
+ memcpy(&buf[sizeof("toe_ol")], &toe_le, sizeof(u32));
+
+ ret = brcmf_proto_dcmd(&drvr_priv->pub, ifidx, &dcmd, dcmd.len);
+ if (ret < 0) {
+ brcmf_dbg(ERROR, "%s: could not set toe_ol: ret=%d\n",
+ brcmf_ifname(&drvr_priv->pub, ifidx), ret);
+ return ret;
+ }
+
+ /* Enable toe globally only if any components are enabled. */
+ toe_le = cpu_to_le32(toe_ol != 0);
+
+ strcpy(buf, "toe");
+ memcpy(&buf[sizeof("toe")], &toe_le, sizeof(u32));
+
+ ret = brcmf_proto_dcmd(&drvr_priv->pub, ifidx, &dcmd, dcmd.len);
+ if (ret < 0) {
+ brcmf_dbg(ERROR, "%s: could not set toe: ret=%d\n",
+ brcmf_ifname(&drvr_priv->pub, ifidx), ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void brcmf_ethtool_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *info)
+{
+ struct brcmf_info *drvr_priv = *(struct brcmf_info **)
+ netdev_priv(ndev);
+
+ sprintf(info->driver, KBUILD_MODNAME);
+ sprintf(info->version, "%lu", drvr_priv->pub.drv_version);
+ sprintf(info->fw_version, "%s", BCM4329_FW_NAME);
+ sprintf(info->bus_info, "%s",
+ dev_name(brcmf_bus_get_device(drvr_priv->pub.bus)));
+}
+
+static struct ethtool_ops brcmf_ethtool_ops = {
+ .get_drvinfo = brcmf_ethtool_get_drvinfo
+};
+
+static int brcmf_ethtool(struct brcmf_info *drvr_priv, void __user *uaddr)
+{
+ struct ethtool_drvinfo info;
+ char drvname[sizeof(info.driver)];
+ u32 cmd;
+ struct ethtool_value edata;
+ u32 toe_cmpnt, csum_dir;
+ int ret;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* all ethtool calls start with a cmd word */
+ if (copy_from_user(&cmd, uaddr, sizeof(u32)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case ETHTOOL_GDRVINFO:
+ /* Copy out any request driver name */
+ if (copy_from_user(&info, uaddr, sizeof(info)))
+ return -EFAULT;
+ strncpy(drvname, info.driver, sizeof(info.driver));
+ drvname[sizeof(info.driver) - 1] = '\0';
+
+ /* clear struct for return */
+ memset(&info, 0, sizeof(info));
+ info.cmd = cmd;
+
+ /* if requested, identify ourselves */
+ if (strcmp(drvname, "?dhd") == 0) {
+ sprintf(info.driver, "dhd");
+ strcpy(info.version, BRCMF_VERSION_STR);
+ }
+
+ /* otherwise, require dongle to be up */
+ else if (!drvr_priv->pub.up) {
+ brcmf_dbg(ERROR, "dongle is not up\n");
+ return -ENODEV;
+ }
+
+ /* finally, report dongle driver type */
+ else if (drvr_priv->pub.iswl)
+ sprintf(info.driver, "wl");
+ else
+ sprintf(info.driver, "xx");
+
+ sprintf(info.version, "%lu", drvr_priv->pub.drv_version);
+ if (copy_to_user(uaddr, &info, sizeof(info)))
+ return -EFAULT;
+ brcmf_dbg(CTL, "given %*s, returning %s\n",
+ (int)sizeof(drvname), drvname, info.driver);
+ break;
+
+ /* Get toe offload components from dongle */
+ case ETHTOOL_GRXCSUM:
+ case ETHTOOL_GTXCSUM:
+ ret = brcmf_toe_get(drvr_priv, 0, &toe_cmpnt);
+ if (ret < 0)
+ return ret;
+
+ csum_dir =
+ (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
+
+ edata.cmd = cmd;
+ edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
+
+ if (copy_to_user(uaddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ break;
+
+ /* Set toe offload components in dongle */
+ case ETHTOOL_SRXCSUM:
+ case ETHTOOL_STXCSUM:
+ if (copy_from_user(&edata, uaddr, sizeof(edata)))
+ return -EFAULT;
+
+ /* Read the current settings, update and write back */
+ ret = brcmf_toe_get(drvr_priv, 0, &toe_cmpnt);
+ if (ret < 0)
+ return ret;
+
+ csum_dir =
+ (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
+
+ if (edata.data != 0)
+ toe_cmpnt |= csum_dir;
+ else
+ toe_cmpnt &= ~csum_dir;
+
+ ret = brcmf_toe_set(drvr_priv, 0, toe_cmpnt);
+ if (ret < 0)
+ return ret;
+
+ /* If setting TX checksum mode, tell Linux the new mode */
+ if (cmd == ETHTOOL_STXCSUM) {
+ if (edata.data)
+ drvr_priv->iflist[0]->ndev->features |=
+ NETIF_F_IP_CSUM;
+ else
+ drvr_priv->iflist[0]->ndev->features &=
+ ~NETIF_F_IP_CSUM;
+ }
+
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int brcmf_netdev_ioctl_entry(struct net_device *ndev, struct ifreq *ifr,
+ int cmd)
+{
+ struct brcmf_info *drvr_priv = *(struct brcmf_info **)
+ netdev_priv(ndev);
+ int ifidx;
+
+ ifidx = brcmf_net2idx(drvr_priv, ndev);
+ brcmf_dbg(TRACE, "ifidx %d, cmd 0x%04x\n", ifidx, cmd);
+
+ if (ifidx == BRCMF_BAD_IF)
+ return -1;
+
+ if (cmd == SIOCETHTOOL)
+ return brcmf_ethtool(drvr_priv, ifr->ifr_data);
+
+ return -EOPNOTSUPP;
+}
+
+/* called only from within this driver. Sends a command to the dongle. */
+s32 brcmf_exec_dcmd(struct net_device *ndev, u32 cmd, void *arg, u32 len)
+{
+ struct brcmf_dcmd dcmd;
+ s32 err = 0;
+ int buflen = 0;
+ bool is_set_key_cmd;
+ struct brcmf_info *drvr_priv = *(struct brcmf_info **)
+ netdev_priv(ndev);
+ int ifidx;
+
+ memset(&dcmd, 0, sizeof(dcmd));
+ dcmd.cmd = cmd;
+ dcmd.buf = arg;
+ dcmd.len = len;
+
+ ifidx = brcmf_net2idx(drvr_priv, ndev);
+
+ if (dcmd.buf != NULL)
+ buflen = min_t(uint, dcmd.len, BRCMF_DCMD_MAXLEN);
+
+ /* send to dongle (must be up, and wl) */
+ if ((drvr_priv->pub.busstate != BRCMF_BUS_DATA)) {
+ brcmf_dbg(ERROR, "DONGLE_DOWN\n");
+ err = -EIO;
+ goto done;
+ }
+
+ if (!drvr_priv->pub.iswl) {
+ err = -EIO;
+ goto done;
+ }
+
+ /*
+ * Intercept BRCMF_C_SET_KEY CMD - serialize M4 send and
+ * set key CMD to prevent M4 encryption.
+ */
+ is_set_key_cmd = ((dcmd.cmd == BRCMF_C_SET_KEY) ||
+ ((dcmd.cmd == BRCMF_C_SET_VAR) &&
+ !(strncmp("wsec_key", dcmd.buf, 9))) ||
+ ((dcmd.cmd == BRCMF_C_SET_VAR) &&
+ !(strncmp("bsscfg:wsec_key", dcmd.buf, 15))));
+ if (is_set_key_cmd)
+ brcmf_netdev_wait_pend8021x(ndev);
+
+ err = brcmf_proto_dcmd(&drvr_priv->pub, ifidx, &dcmd, buflen);
+
+done:
+ if (err > 0)
+ err = 0;
+
+ return err;
+}
+
+static int brcmf_netdev_stop(struct net_device *ndev)
+{
+ struct brcmf_pub *drvr = *(struct brcmf_pub **) netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_cfg80211_down(drvr->config);
+ if (drvr->up == 0)
+ return 0;
+
+ /* Set state and stop OS transmissions */
+ drvr->up = 0;
+ netif_stop_queue(ndev);
+
+ return 0;
+}
+
+static int brcmf_netdev_open(struct net_device *ndev)
+{
+ struct brcmf_info *drvr_priv = *(struct brcmf_info **)
+ netdev_priv(ndev);
+ u32 toe_ol;
+ int ifidx = brcmf_net2idx(drvr_priv, ndev);
+ s32 ret = 0;
+
+ brcmf_dbg(TRACE, "ifidx %d\n", ifidx);
+
+ if (ifidx == 0) { /* do it only for primary eth0 */
+
+ /* try to bring up bus */
+ ret = brcmf_bus_start(&drvr_priv->pub);
+ if (ret != 0) {
+ brcmf_dbg(ERROR, "failed with code %d\n", ret);
+ return -1;
+ }
+ atomic_set(&drvr_priv->pend_8021x_cnt, 0);
+
+ memcpy(ndev->dev_addr, drvr_priv->pub.mac, ETH_ALEN);
+
+ /* Get current TOE mode from dongle */
+ if (brcmf_toe_get(drvr_priv, ifidx, &toe_ol) >= 0
+ && (toe_ol & TOE_TX_CSUM_OL) != 0)
+ drvr_priv->iflist[ifidx]->ndev->features |=
+ NETIF_F_IP_CSUM;
+ else
+ drvr_priv->iflist[ifidx]->ndev->features &=
+ ~NETIF_F_IP_CSUM;
+ }
+ /* Allow transmit calls */
+ netif_start_queue(ndev);
+ drvr_priv->pub.up = 1;
+ if (brcmf_cfg80211_up(drvr_priv->pub.config)) {
+ brcmf_dbg(ERROR, "failed to bring up cfg80211\n");
+ return -1;
+ }
+
+ return ret;
+}
+
+int
+brcmf_add_if(struct brcmf_info *drvr_priv, int ifidx, struct net_device *ndev,
+ char *name, u8 *mac_addr, u32 flags, u8 bssidx)
+{
+ struct brcmf_if *ifp;
+ int ret = 0, err = 0;
+
+ brcmf_dbg(TRACE, "idx %d, handle->%p\n", ifidx, ndev);
+
+ ifp = drvr_priv->iflist[ifidx];
+ if (!ifp) {
+ ifp = kmalloc(sizeof(struct brcmf_if), GFP_ATOMIC);
+ if (!ifp)
+ return -ENOMEM;
+ }
+
+ memset(ifp, 0, sizeof(struct brcmf_if));
+ ifp->info = drvr_priv;
+ drvr_priv->iflist[ifidx] = ifp;
+ if (mac_addr != NULL)
+ memcpy(&ifp->mac_addr, mac_addr, ETH_ALEN);
+
+ if (ndev == NULL) {
+ ifp->state = BRCMF_E_IF_ADD;
+ ifp->idx = ifidx;
+ /*
+ * Delete the existing interface before overwriting it
+ * in case we missed the BRCMF_E_IF_DEL event.
+ */
+ if (ifp->ndev != NULL) {
+ brcmf_dbg(ERROR, "ERROR: netdev:%s already exists, try free & unregister\n",
+ ifp->ndev->name);
+ netif_stop_queue(ifp->ndev);
+ unregister_netdev(ifp->ndev);
+ free_netdev(ifp->ndev);
+ }
+
+ /* Allocate netdev, including space for private structure */
+ ifp->ndev = alloc_netdev(sizeof(drvr_priv), "wlan%d",
+ ether_setup);
+ if (!ifp->ndev) {
+ brcmf_dbg(ERROR, "OOM - alloc_netdev\n");
+ ret = -ENOMEM;
+ }
+
+ if (ret == 0) {
+ memcpy(netdev_priv(ifp->ndev), &drvr_priv,
+ sizeof(drvr_priv));
+ err = brcmf_net_attach(&drvr_priv->pub, ifp->idx);
+ if (err != 0) {
+ brcmf_dbg(ERROR, "brcmf_net_attach failed, err %d\n",
+ err);
+ ret = -EOPNOTSUPP;
+ } else {
+ brcmf_dbg(TRACE, " ==== pid:%x, net_device for if:%s created ===\n",
+ current->pid, ifp->ndev->name);
+ ifp->state = 0;
+ }
+ }
+
+ if (ret < 0) {
+ if (ifp->ndev)
+ free_netdev(ifp->ndev);
+
+ drvr_priv->iflist[ifp->idx] = NULL;
+ kfree(ifp);
+ }
+ } else
+ ifp->ndev = ndev;
+
+ return 0;
+}
+
+void brcmf_del_if(struct brcmf_info *drvr_priv, int ifidx)
+{
+ struct brcmf_if *ifp;
+
+ brcmf_dbg(TRACE, "idx %d\n", ifidx);
+
+ ifp = drvr_priv->iflist[ifidx];
+ if (!ifp) {
+ brcmf_dbg(ERROR, "Null interface\n");
+ return;
+ }
+
+ ifp->state = BRCMF_E_IF_DEL;
+ ifp->idx = ifidx;
+ if (ifp->ndev != NULL) {
+ netif_stop_queue(ifp->ndev);
+ unregister_netdev(ifp->ndev);
+ free_netdev(ifp->ndev);
+ drvr_priv->iflist[ifidx] = NULL;
+ kfree(ifp);
+ }
+}
+
+struct brcmf_pub *brcmf_attach(struct brcmf_bus *bus, uint bus_hdrlen)
+{
+ struct brcmf_info *drvr_priv = NULL;
+ struct net_device *ndev;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Allocate netdev, including space for private structure */
+ ndev = alloc_netdev(sizeof(drvr_priv), "wlan%d", ether_setup);
+ if (!ndev) {
+ brcmf_dbg(ERROR, "OOM - alloc_netdev\n");
+ goto fail;
+ }
+
+ /* Allocate primary brcmf_info */
+ drvr_priv = kzalloc(sizeof(struct brcmf_info), GFP_ATOMIC);
+ if (!drvr_priv)
+ goto fail;
+
+ /*
+ * Save the brcmf_info into the priv
+ */
+ memcpy(netdev_priv(ndev), &drvr_priv, sizeof(drvr_priv));
+
+ if (brcmf_add_if(drvr_priv, 0, ndev, ndev->name, NULL, 0, 0) ==
+ BRCMF_BAD_IF)
+ goto fail;
+
+ ndev->netdev_ops = NULL;
+ mutex_init(&drvr_priv->proto_block);
+
+ /* Link to info module */
+ drvr_priv->pub.info = drvr_priv;
+
+ /* Link to bus module */
+ drvr_priv->pub.bus = bus;
+ drvr_priv->pub.hdrlen = bus_hdrlen;
+
+ /* Attach and link in the protocol */
+ if (brcmf_proto_attach(&drvr_priv->pub) != 0) {
+ brcmf_dbg(ERROR, "brcmf_prot_attach failed\n");
+ goto fail;
+ }
+
+ /* Attach and link in the cfg80211 */
+ drvr_priv->pub.config =
+ brcmf_cfg80211_attach(ndev,
+ brcmf_bus_get_device(bus),
+ &drvr_priv->pub);
+ if (drvr_priv->pub.config == NULL) {
+ brcmf_dbg(ERROR, "wl_cfg80211_attach failed\n");
+ goto fail;
+ }
+
+ INIT_WORK(&drvr_priv->setmacaddr_work, _brcmf_set_mac_address);
+ INIT_WORK(&drvr_priv->multicast_work, _brcmf_set_multicast_list);
+
+ /*
+ * Save the brcmf_info into the priv
+ */
+ memcpy(netdev_priv(ndev), &drvr_priv, sizeof(drvr_priv));
+
+ return &drvr_priv->pub;
+
+fail:
+ if (ndev)
+ free_netdev(ndev);
+ if (drvr_priv)
+ brcmf_detach(&drvr_priv->pub);
+
+ return NULL;
+}
+
+int brcmf_bus_start(struct brcmf_pub *drvr)
+{
+ int ret = -1;
+ struct brcmf_info *drvr_priv = drvr->info;
+ /* Room for "event_msgs" + '\0' + bitvec */
+ char iovbuf[BRCMF_EVENTING_MASK_LEN + 12];
+
+ brcmf_dbg(TRACE, "\n");
+
+ /* Bring up the bus */
+ ret = brcmf_sdbrcm_bus_init(&drvr_priv->pub);
+ if (ret != 0) {
+ brcmf_dbg(ERROR, "brcmf_sdbrcm_bus_init failed %d\n", ret);
+ return ret;
+ }
+
+ /* If bus is not ready, can't come up */
+ if (drvr_priv->pub.busstate != BRCMF_BUS_DATA) {
+ brcmf_dbg(ERROR, "failed bus is not ready\n");
+ return -ENODEV;
+ }
+
+ brcmf_c_mkiovar("event_msgs", drvr->eventmask, BRCMF_EVENTING_MASK_LEN,
+ iovbuf, sizeof(iovbuf));
+ brcmf_proto_cdc_query_dcmd(drvr, 0, BRCMF_C_GET_VAR, iovbuf,
+ sizeof(iovbuf));
+ memcpy(drvr->eventmask, iovbuf, BRCMF_EVENTING_MASK_LEN);
+
+ setbit(drvr->eventmask, BRCMF_E_SET_SSID);
+ setbit(drvr->eventmask, BRCMF_E_PRUNE);
+ setbit(drvr->eventmask, BRCMF_E_AUTH);
+ setbit(drvr->eventmask, BRCMF_E_REASSOC);
+ setbit(drvr->eventmask, BRCMF_E_REASSOC_IND);
+ setbit(drvr->eventmask, BRCMF_E_DEAUTH_IND);
+ setbit(drvr->eventmask, BRCMF_E_DISASSOC_IND);
+ setbit(drvr->eventmask, BRCMF_E_DISASSOC);
+ setbit(drvr->eventmask, BRCMF_E_JOIN);
+ setbit(drvr->eventmask, BRCMF_E_ASSOC_IND);
+ setbit(drvr->eventmask, BRCMF_E_PSK_SUP);
+ setbit(drvr->eventmask, BRCMF_E_LINK);
+ setbit(drvr->eventmask, BRCMF_E_NDIS_LINK);
+ setbit(drvr->eventmask, BRCMF_E_MIC_ERROR);
+ setbit(drvr->eventmask, BRCMF_E_PMKID_CACHE);
+ setbit(drvr->eventmask, BRCMF_E_TXFAIL);
+ setbit(drvr->eventmask, BRCMF_E_JOIN_START);
+ setbit(drvr->eventmask, BRCMF_E_SCAN_COMPLETE);
+
+/* enable dongle roaming event */
+
+ drvr->pktfilter_count = 1;
+ /* Setup filter to allow only unicast */
+ drvr->pktfilter[0] = "100 0 0 0 0x01 0x00";
+
+ /* Bus is ready, do any protocol initialization */
+ ret = brcmf_proto_init(&drvr_priv->pub);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct net_device_ops brcmf_netdev_ops_pri = {
+ .ndo_open = brcmf_netdev_open,
+ .ndo_stop = brcmf_netdev_stop,
+ .ndo_get_stats = brcmf_netdev_get_stats,
+ .ndo_do_ioctl = brcmf_netdev_ioctl_entry,
+ .ndo_start_xmit = brcmf_netdev_start_xmit,
+ .ndo_set_mac_address = brcmf_netdev_set_mac_address,
+ .ndo_set_rx_mode = brcmf_netdev_set_multicast_list
+};
+
+int brcmf_net_attach(struct brcmf_pub *drvr, int ifidx)
+{
+ struct brcmf_info *drvr_priv = drvr->info;
+ struct net_device *ndev;
+ u8 temp_addr[ETH_ALEN] = {
+ 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33};
+
+ brcmf_dbg(TRACE, "ifidx %d\n", ifidx);
+
+ ndev = drvr_priv->iflist[ifidx]->ndev;
+ ndev->netdev_ops = &brcmf_netdev_ops_pri;
+
+ /*
+ * We have to use the primary MAC for virtual interfaces
+ */
+ if (ifidx != 0) {
+ /* for virtual interfaces use the primary MAC */
+ memcpy(temp_addr, drvr_priv->pub.mac, ETH_ALEN);
+
+ }
+
+ if (ifidx == 1) {
+ brcmf_dbg(TRACE, "ACCESS POINT MAC:\n");
+ /* ACCESSPOINT INTERFACE CASE */
+ temp_addr[0] |= 0X02; /* set bit 2 ,
+ - Locally Administered address */
+
+ }
+ ndev->hard_header_len = ETH_HLEN + drvr_priv->pub.hdrlen;
+ ndev->ethtool_ops = &brcmf_ethtool_ops;
+
+ drvr_priv->pub.rxsz = ndev->mtu + ndev->hard_header_len +
+ drvr_priv->pub.hdrlen;
+
+ memcpy(ndev->dev_addr, temp_addr, ETH_ALEN);
+
+ if (register_netdev(ndev) != 0) {
+ brcmf_dbg(ERROR, "couldn't register the net device\n");
+ goto fail;
+ }
+
+ brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
+
+ return 0;
+
+fail:
+ ndev->netdev_ops = NULL;
+ return -EBADE;
+}
+
+static void brcmf_bus_detach(struct brcmf_pub *drvr)
+{
+ struct brcmf_info *drvr_priv;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (drvr) {
+ drvr_priv = drvr->info;
+ if (drvr_priv) {
+ /* Stop the protocol module */
+ brcmf_proto_stop(&drvr_priv->pub);
+
+ /* Stop the bus module */
+ brcmf_sdbrcm_bus_stop(drvr_priv->pub.bus);
+ }
+ }
+}
+
+void brcmf_detach(struct brcmf_pub *drvr)
+{
+ struct brcmf_info *drvr_priv;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (drvr) {
+ drvr_priv = drvr->info;
+ if (drvr_priv) {
+ struct brcmf_if *ifp;
+ int i;
+
+ for (i = 1; i < BRCMF_MAX_IFS; i++)
+ if (drvr_priv->iflist[i])
+ brcmf_del_if(drvr_priv, i);
+
+ ifp = drvr_priv->iflist[0];
+ if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
+ rtnl_lock();
+ brcmf_netdev_stop(ifp->ndev);
+ rtnl_unlock();
+ unregister_netdev(ifp->ndev);
+ }
+
+ cancel_work_sync(&drvr_priv->setmacaddr_work);
+ cancel_work_sync(&drvr_priv->multicast_work);
+
+ brcmf_bus_detach(drvr);
+
+ if (drvr->prot)
+ brcmf_proto_detach(drvr);
+
+ brcmf_cfg80211_detach(drvr->config);
+
+ free_netdev(ifp->ndev);
+ kfree(ifp);
+ kfree(drvr_priv);
+ }
+ }
+}
+
+static void __exit brcmf_module_cleanup(void)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ brcmf_bus_unregister();
+}
+
+static int __init brcmf_module_init(void)
+{
+ int error;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ error = brcmf_bus_register();
+
+ if (error) {
+ brcmf_dbg(ERROR, "brcmf_bus_register failed\n");
+ goto failed;
+ }
+ return 0;
+
+failed:
+ return -EINVAL;
+}
+
+module_init(brcmf_module_init);
+module_exit(brcmf_module_cleanup);
+
+int brcmf_os_proto_block(struct brcmf_pub *drvr)
+{
+ struct brcmf_info *drvr_priv = drvr->info;
+
+ if (drvr_priv) {
+ mutex_lock(&drvr_priv->proto_block);
+ return 1;
+ }
+ return 0;
+}
+
+int brcmf_os_proto_unblock(struct brcmf_pub *drvr)
+{
+ struct brcmf_info *drvr_priv = drvr->info;
+
+ if (drvr_priv) {
+ mutex_unlock(&drvr_priv->proto_block);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int brcmf_get_pend_8021x_cnt(struct brcmf_info *drvr_priv)
+{
+ return atomic_read(&drvr_priv->pend_8021x_cnt);
+}
+
+#define MAX_WAIT_FOR_8021X_TX 10
+
+int brcmf_netdev_wait_pend8021x(struct net_device *ndev)
+{
+ struct brcmf_info *drvr_priv = *(struct brcmf_info **)netdev_priv(ndev);
+ int timeout = 10 * HZ / 1000;
+ int ntimes = MAX_WAIT_FOR_8021X_TX;
+ int pend = brcmf_get_pend_8021x_cnt(drvr_priv);
+
+ while (ntimes && pend) {
+ if (pend) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(timeout);
+ set_current_state(TASK_RUNNING);
+ ntimes--;
+ }
+ pend = brcmf_get_pend_8021x_cnt(drvr_priv);
+ }
+ return pend;
+}
+
+#ifdef BCMDBG
+int brcmf_write_to_file(struct brcmf_pub *drvr, const u8 *buf, int size)
+{
+ int ret = 0;
+ struct file *fp;
+ mm_segment_t old_fs;
+ loff_t pos = 0;
+
+ /* change to KERNEL_DS address limit */
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ /* open file to write */
+ fp = filp_open("/tmp/mem_dump", O_WRONLY | O_CREAT, 0640);
+ if (!fp) {
+ brcmf_dbg(ERROR, "open file error\n");
+ ret = -1;
+ goto exit;
+ }
+
+ /* Write buf to file */
+ fp->f_op->write(fp, (char __user *)buf, size, &pos);
+
+exit:
+ /* free buf before return */
+ kfree(buf);
+ /* close file before return */
+ if (fp)
+ filp_close(fp, current->files);
+ /* restore previous address limit */
+ set_fs(old_fs);
+
+ return ret;
+}
+#endif /* BCMDBG */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h
new file mode 100644
index 000000000000..4ee1ea846f6d
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMF_PROTO_H_
+#define _BRCMF_PROTO_H_
+
+/*
+ * Exported from the brcmf protocol module (brcmf_cdc)
+ */
+
+/* Linkage, sets prot link and updates hdrlen in pub */
+extern int brcmf_proto_attach(struct brcmf_pub *drvr);
+
+/* Unlink, frees allocated protocol memory (including brcmf_proto) */
+extern void brcmf_proto_detach(struct brcmf_pub *drvr);
+
+/* Initialize protocol: sync w/dongle state.
+ * Sets dongle media info (iswl, drv_version, mac address).
+ */
+extern int brcmf_proto_init(struct brcmf_pub *drvr);
+
+/* Stop protocol: sync w/dongle state. */
+extern void brcmf_proto_stop(struct brcmf_pub *drvr);
+
+/* Add any protocol-specific data header.
+ * Caller must reserve prot_hdrlen prepend space.
+ */
+extern void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx,
+ struct sk_buff *txp);
+
+/* Remove any protocol-specific data header. */
+extern int brcmf_proto_hdrpull(struct brcmf_pub *, int *ifidx,
+ struct sk_buff *rxp);
+
+/* Use protocol to issue command to dongle */
+extern int brcmf_proto_dcmd(struct brcmf_pub *drvr, int ifidx,
+ struct brcmf_dcmd *dcmd, int len);
+
+/* Update local copy of dongle statistics */
+extern void brcmf_proto_dstats(struct brcmf_pub *drvr);
+
+extern int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr);
+
+extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len);
+
+#endif /* _BRCMF_PROTO_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
new file mode 100644
index 000000000000..313b8bf592d1
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -0,0 +1,4591 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/printk.h>
+#include <linux/pci_ids.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/semaphore.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <asm/unaligned.h>
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
+#include <soc.h>
+#include "sdio_host.h"
+
+#define DCMD_RESP_TIMEOUT 2000 /* In milli second */
+
+#ifdef BCMDBG
+
+#define BRCMF_TRAP_INFO_SIZE 80
+
+#define CBUF_LEN (128)
+
+struct rte_log_le {
+ __le32 buf; /* Can't be pointer on (64-bit) hosts */
+ __le32 buf_size;
+ __le32 idx;
+ char *_buf_compat; /* Redundant pointer for backward compat. */
+};
+
+struct rte_console {
+ /* Virtual UART
+ * When there is no UART (e.g. Quickturn),
+ * the host should write a complete
+ * input line directly into cbuf and then write
+ * the length into vcons_in.
+ * This may also be used when there is a real UART
+ * (at risk of conflicting with
+ * the real UART). vcons_out is currently unused.
+ */
+ uint vcons_in;
+ uint vcons_out;
+
+ /* Output (logging) buffer
+ * Console output is written to a ring buffer log_buf at index log_idx.
+ * The host may read the output when it sees log_idx advance.
+ * Output will be lost if the output wraps around faster than the host
+ * polls.
+ */
+ struct rte_log_le log_le;
+
+ /* Console input line buffer
+ * Characters are read one at a time into cbuf
+ * until <CR> is received, then
+ * the buffer is processed as a command line.
+ * Also used for virtual UART.
+ */
+ uint cbuf_idx;
+ char cbuf[CBUF_LEN];
+};
+
+#endif /* BCMDBG */
+#include <chipcommon.h>
+
+#include "dhd.h"
+#include "dhd_bus.h"
+#include "dhd_proto.h"
+#include "dhd_dbg.h"
+#include <bcmchip.h>
+
+#define TXQLEN 2048 /* bulk tx queue length */
+#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */
+#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */
+#define PRIOMASK 7
+
+#define TXRETRIES 2 /* # of retries for tx frames */
+
+#define BRCMF_RXBOUND 50 /* Default for max rx frames in
+ one scheduling */
+
+#define BRCMF_TXBOUND 20 /* Default for max tx frames in
+ one scheduling */
+
+#define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */
+
+#define MEMBLOCK 2048 /* Block size used for downloading
+ of dongle image */
+#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold
+ biggest possible glom */
+
+#define BRCMF_FIRSTREAD (1 << 6)
+
+
+/* SBSDIO_DEVICE_CTL */
+
+/* 1: device will assert busy signal when receiving CMD53 */
+#define SBSDIO_DEVCTL_SETBUSY 0x01
+/* 1: assertion of sdio interrupt is synchronous to the sdio clock */
+#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02
+/* 1: mask all interrupts to host except the chipActive (rev 8) */
+#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04
+/* 1: isolate internal sdio signals, put external pads in tri-state; requires
+ * sdio bus power cycle to clear (rev 9) */
+#define SBSDIO_DEVCTL_PADS_ISO 0x08
+/* Force SD->SB reset mapping (rev 11) */
+#define SBSDIO_DEVCTL_SB_RST_CTL 0x30
+/* Determined by CoreControl bit */
+#define SBSDIO_DEVCTL_RST_CORECTL 0x00
+/* Force backplane reset */
+#define SBSDIO_DEVCTL_RST_BPRESET 0x10
+/* Force no backplane reset */
+#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20
+
+/* SBSDIO_FUNC1_CHIPCLKCSR */
+
+/* Force ALP request to backplane */
+#define SBSDIO_FORCE_ALP 0x01
+/* Force HT request to backplane */
+#define SBSDIO_FORCE_HT 0x02
+/* Force ILP request to backplane */
+#define SBSDIO_FORCE_ILP 0x04
+/* Make ALP ready (power up xtal) */
+#define SBSDIO_ALP_AVAIL_REQ 0x08
+/* Make HT ready (power up PLL) */
+#define SBSDIO_HT_AVAIL_REQ 0x10
+/* Squelch clock requests from HW */
+#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20
+/* Status: ALP is ready */
+#define SBSDIO_ALP_AVAIL 0x40
+/* Status: HT is ready */
+#define SBSDIO_HT_AVAIL 0x80
+
+#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
+#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS)
+#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
+#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
+
+#define SBSDIO_CLKAV(regval, alponly) \
+ (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval)))
+
+/* direct(mapped) cis space */
+
+/* MAPPED common CIS address */
+#define SBSDIO_CIS_BASE_COMMON 0x1000
+/* maximum bytes in one CIS */
+#define SBSDIO_CIS_SIZE_LIMIT 0x200
+/* cis offset addr is < 17 bits */
+#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF
+
+/* manfid tuple length, include tuple, link bytes */
+#define SBSDIO_CIS_MANFID_TUPLE_LEN 6
+
+/* intstatus */
+#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */
+#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */
+#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */
+#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */
+#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */
+#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */
+#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */
+#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */
+#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */
+#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */
+#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */
+#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */
+#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */
+#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */
+#define I_PC (1 << 10) /* descriptor error */
+#define I_PD (1 << 11) /* data error */
+#define I_DE (1 << 12) /* Descriptor protocol Error */
+#define I_RU (1 << 13) /* Receive descriptor Underflow */
+#define I_RO (1 << 14) /* Receive fifo Overflow */
+#define I_XU (1 << 15) /* Transmit fifo Underflow */
+#define I_RI (1 << 16) /* Receive Interrupt */
+#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */
+#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */
+#define I_XI (1 << 24) /* Transmit Interrupt */
+#define I_RF_TERM (1 << 25) /* Read Frame Terminate */
+#define I_WF_TERM (1 << 26) /* Write Frame Terminate */
+#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */
+#define I_SBINT (1 << 28) /* sbintstatus Interrupt */
+#define I_CHIPACTIVE (1 << 29) /* chip from doze to active state */
+#define I_SRESET (1 << 30) /* CCCR RES interrupt */
+#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */
+#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU)
+#define I_DMA (I_RI | I_XI | I_ERRORS)
+
+/* corecontrol */
+#define CC_CISRDY (1 << 0) /* CIS Ready */
+#define CC_BPRESEN (1 << 1) /* CCCR RES signal */
+#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */
+#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation */
+#define CC_XMTDATAAVAIL_MODE (1 << 4)
+#define CC_XMTDATAAVAIL_CTRL (1 << 5)
+
+/* SDA_FRAMECTRL */
+#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */
+#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */
+#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */
+#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */
+
+/* HW frame tag */
+#define SDPCM_FRAMETAG_LEN 4 /* 2 bytes len, 2 bytes check val */
+
+/* Total length of frame header for dongle protocol */
+#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
+#define SDPCM_RESERVE (SDPCM_HDRLEN + BRCMF_SDALIGN)
+
+/*
+ * Software allocation of To SB Mailbox resources
+ */
+
+/* tosbmailbox bits corresponding to intstatus bits */
+#define SMB_NAK (1 << 0) /* Frame NAK */
+#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */
+#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */
+#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */
+
+/* tosbmailboxdata */
+#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */
+
+/*
+ * Software allocation of To Host Mailbox resources
+ */
+
+/* intstatus bits */
+#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */
+#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */
+#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */
+#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */
+
+/* tohostmailboxdata */
+#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */
+#define HMB_DATA_DEVREADY 2 /* talk to host after enable */
+#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */
+#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */
+
+#define HMB_DATA_FCDATA_MASK 0xff000000
+#define HMB_DATA_FCDATA_SHIFT 24
+
+#define HMB_DATA_VERSION_MASK 0x00ff0000
+#define HMB_DATA_VERSION_SHIFT 16
+
+/*
+ * Software-defined protocol header
+ */
+
+/* Current protocol version */
+#define SDPCM_PROT_VERSION 4
+
+/* SW frame header */
+#define SDPCM_PACKET_SEQUENCE(p) (((u8 *)p)[0] & 0xff)
+
+#define SDPCM_CHANNEL_MASK 0x00000f00
+#define SDPCM_CHANNEL_SHIFT 8
+#define SDPCM_PACKET_CHANNEL(p) (((u8 *)p)[1] & 0x0f)
+
+#define SDPCM_NEXTLEN_OFFSET 2
+
+/* Data Offset from SOF (HW Tag, SW Tag, Pad) */
+#define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */
+#define SDPCM_DOFFSET_VALUE(p) (((u8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff)
+#define SDPCM_DOFFSET_MASK 0xff000000
+#define SDPCM_DOFFSET_SHIFT 24
+#define SDPCM_FCMASK_OFFSET 4 /* Flow control */
+#define SDPCM_FCMASK_VALUE(p) (((u8 *)p)[SDPCM_FCMASK_OFFSET] & 0xff)
+#define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */
+#define SDPCM_WINDOW_VALUE(p) (((u8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff)
+
+#define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */
+
+/* logical channel numbers */
+#define SDPCM_CONTROL_CHANNEL 0 /* Control channel Id */
+#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */
+#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */
+#define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets */
+#define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */
+
+#define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for 8bit frame seq */
+
+#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80)
+
+/*
+ * Shared structure between dongle and the host.
+ * The structure contains pointers to trap or assert information.
+ */
+#define SDPCM_SHARED_VERSION 0x0002
+#define SDPCM_SHARED_VERSION_MASK 0x00FF
+#define SDPCM_SHARED_ASSERT_BUILT 0x0100
+#define SDPCM_SHARED_ASSERT 0x0200
+#define SDPCM_SHARED_TRAP 0x0400
+
+/* Space for header read, limit for data packets */
+#define MAX_HDR_READ (1 << 6)
+#define MAX_RX_DATASZ 2048
+
+/* Maximum milliseconds to wait for F2 to come up */
+#define BRCMF_WAIT_F2RDY 3000
+
+/* Bump up limit on waiting for HT to account for first startup;
+ * if the image is doing a CRC calculation before programming the PMU
+ * for HT availability, it could take a couple hundred ms more, so
+ * max out at a 1 second (1000000us).
+ */
+#undef PMU_MAX_TRANSITION_DLY
+#define PMU_MAX_TRANSITION_DLY 1000000
+
+/* Value for ChipClockCSR during initial setup */
+#define BRCMF_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \
+ SBSDIO_ALP_AVAIL_REQ)
+
+/* Flags for SDH calls */
+#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
+
+/* sbimstate */
+#define SBIM_IBE 0x20000 /* inbanderror */
+#define SBIM_TO 0x40000 /* timeout */
+#define SBIM_BY 0x01800000 /* busy (sonics >= 2.3) */
+#define SBIM_RJ 0x02000000 /* reject (sonics >= 2.3) */
+
+/* sbtmstatelow */
+
+/* reset */
+#define SBTML_RESET 0x0001
+/* reject field */
+#define SBTML_REJ_MASK 0x0006
+/* reject */
+#define SBTML_REJ 0x0002
+/* temporary reject, for error recovery */
+#define SBTML_TMPREJ 0x0004
+
+/* Shift to locate the SI control flags in sbtml */
+#define SBTML_SICF_SHIFT 16
+
+/* sbtmstatehigh */
+#define SBTMH_SERR 0x0001 /* serror */
+#define SBTMH_INT 0x0002 /* interrupt */
+#define SBTMH_BUSY 0x0004 /* busy */
+#define SBTMH_TO 0x0020 /* timeout (sonics >= 2.3) */
+
+/* Shift to locate the SI status flags in sbtmh */
+#define SBTMH_SISF_SHIFT 16
+
+/* sbidlow */
+#define SBIDL_INIT 0x80 /* initiator */
+
+/* sbidhigh */
+#define SBIDH_RC_MASK 0x000f /* revision code */
+#define SBIDH_RCE_MASK 0x7000 /* revision code extension field */
+#define SBIDH_RCE_SHIFT 8
+#define SBCOREREV(sbidh) \
+ ((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | \
+ ((sbidh) & SBIDH_RC_MASK))
+#define SBIDH_CC_MASK 0x8ff0 /* core code */
+#define SBIDH_CC_SHIFT 4
+#define SBIDH_VC_MASK 0xffff0000 /* vendor code */
+#define SBIDH_VC_SHIFT 16
+
+/*
+ * Conversion of 802.1D priority to precedence level
+ */
+static uint prio2prec(u32 prio)
+{
+ return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
+ (prio^2) : prio;
+}
+
+/*
+ * Core reg address translation.
+ * Both macro's returns a 32 bits byte address on the backplane bus.
+ */
+#define CORE_CC_REG(base, field) \
+ (base + offsetof(struct chipcregs, field))
+#define CORE_BUS_REG(base, field) \
+ (base + offsetof(struct sdpcmd_regs, field))
+#define CORE_SB(base, field) \
+ (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
+
+/* core registers */
+struct sdpcmd_regs {
+ u32 corecontrol; /* 0x00, rev8 */
+ u32 corestatus; /* rev8 */
+ u32 PAD[1];
+ u32 biststatus; /* rev8 */
+
+ /* PCMCIA access */
+ u16 pcmciamesportaladdr; /* 0x010, rev8 */
+ u16 PAD[1];
+ u16 pcmciamesportalmask; /* rev8 */
+ u16 PAD[1];
+ u16 pcmciawrframebc; /* rev8 */
+ u16 PAD[1];
+ u16 pcmciaunderflowtimer; /* rev8 */
+ u16 PAD[1];
+
+ /* interrupt */
+ u32 intstatus; /* 0x020, rev8 */
+ u32 hostintmask; /* rev8 */
+ u32 intmask; /* rev8 */
+ u32 sbintstatus; /* rev8 */
+ u32 sbintmask; /* rev8 */
+ u32 funcintmask; /* rev4 */
+ u32 PAD[2];
+ u32 tosbmailbox; /* 0x040, rev8 */
+ u32 tohostmailbox; /* rev8 */
+ u32 tosbmailboxdata; /* rev8 */
+ u32 tohostmailboxdata; /* rev8 */
+
+ /* synchronized access to registers in SDIO clock domain */
+ u32 sdioaccess; /* 0x050, rev8 */
+ u32 PAD[3];
+
+ /* PCMCIA frame control */
+ u8 pcmciaframectrl; /* 0x060, rev8 */
+ u8 PAD[3];
+ u8 pcmciawatermark; /* rev8 */
+ u8 PAD[155];
+
+ /* interrupt batching control */
+ u32 intrcvlazy; /* 0x100, rev8 */
+ u32 PAD[3];
+
+ /* counters */
+ u32 cmd52rd; /* 0x110, rev8 */
+ u32 cmd52wr; /* rev8 */
+ u32 cmd53rd; /* rev8 */
+ u32 cmd53wr; /* rev8 */
+ u32 abort; /* rev8 */
+ u32 datacrcerror; /* rev8 */
+ u32 rdoutofsync; /* rev8 */
+ u32 wroutofsync; /* rev8 */
+ u32 writebusy; /* rev8 */
+ u32 readwait; /* rev8 */
+ u32 readterm; /* rev8 */
+ u32 writeterm; /* rev8 */
+ u32 PAD[40];
+ u32 clockctlstatus; /* rev8 */
+ u32 PAD[7];
+
+ u32 PAD[128]; /* DMA engines */
+
+ /* SDIO/PCMCIA CIS region */
+ char cis[512]; /* 0x400-0x5ff, rev6 */
+
+ /* PCMCIA function control registers */
+ char pcmciafcr[256]; /* 0x600-6ff, rev6 */
+ u16 PAD[55];
+
+ /* PCMCIA backplane access */
+ u16 backplanecsr; /* 0x76E, rev6 */
+ u16 backplaneaddr0; /* rev6 */
+ u16 backplaneaddr1; /* rev6 */
+ u16 backplaneaddr2; /* rev6 */
+ u16 backplaneaddr3; /* rev6 */
+ u16 backplanedata0; /* rev6 */
+ u16 backplanedata1; /* rev6 */
+ u16 backplanedata2; /* rev6 */
+ u16 backplanedata3; /* rev6 */
+ u16 PAD[31];
+
+ /* sprom "size" & "blank" info */
+ u16 spromstatus; /* 0x7BE, rev2 */
+ u32 PAD[464];
+
+ u16 PAD[0x80];
+};
+
+#ifdef BCMDBG
+/* Device console log buffer state */
+struct brcmf_console {
+ uint count; /* Poll interval msec counter */
+ uint log_addr; /* Log struct address (fixed) */
+ struct rte_log_le log_le; /* Log struct (host copy) */
+ uint bufsize; /* Size of log buffer */
+ u8 *buf; /* Log buffer (host copy) */
+ uint last; /* Last buffer read index */
+};
+#endif /* BCMDBG */
+
+struct sdpcm_shared {
+ u32 flags;
+ u32 trap_addr;
+ u32 assert_exp_addr;
+ u32 assert_file_addr;
+ u32 assert_line;
+ u32 console_addr; /* Address of struct rte_console */
+ u32 msgtrace_addr;
+ u8 tag[32];
+};
+
+struct sdpcm_shared_le {
+ __le32 flags;
+ __le32 trap_addr;
+ __le32 assert_exp_addr;
+ __le32 assert_file_addr;
+ __le32 assert_line;
+ __le32 console_addr; /* Address of struct rte_console */
+ __le32 msgtrace_addr;
+ u8 tag[32];
+};
+
+
+/* misc chip info needed by some of the routines */
+struct chip_info {
+ u32 chip;
+ u32 chiprev;
+ u32 cccorebase;
+ u32 ccrev;
+ u32 cccaps;
+ u32 buscorebase; /* 32 bits backplane bus address */
+ u32 buscorerev;
+ u32 buscoretype;
+ u32 ramcorebase;
+ u32 armcorebase;
+ u32 pmurev;
+ u32 ramsize;
+};
+
+/* Private data for SDIO bus interaction */
+struct brcmf_bus {
+ struct brcmf_pub *drvr;
+
+ struct brcmf_sdio_dev *sdiodev; /* sdio device handler */
+ struct chip_info *ci; /* Chip info struct */
+ char *vars; /* Variables (from CIS and/or other) */
+ uint varsz; /* Size of variables buffer */
+
+ u32 ramsize; /* Size of RAM in SOCRAM (bytes) */
+
+ u32 hostintmask; /* Copy of Host Interrupt Mask */
+ u32 intstatus; /* Intstatus bits (events) pending */
+ bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */
+ bool fcstate; /* State of dongle flow-control */
+
+ uint blocksize; /* Block size of SDIO transfers */
+ uint roundup; /* Max roundup limit */
+
+ struct pktq txq; /* Queue length used for flow-control */
+ u8 flowcontrol; /* per prio flow control bitmask */
+ u8 tx_seq; /* Transmit sequence number (next) */
+ u8 tx_max; /* Maximum transmit sequence allowed */
+
+ u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN];
+ u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
+ u16 nextlen; /* Next Read Len from last header */
+ u8 rx_seq; /* Receive sequence number (expected) */
+ bool rxskip; /* Skip receive (awaiting NAK ACK) */
+
+ uint rxbound; /* Rx frames to read before resched */
+ uint txbound; /* Tx frames to send before resched */
+ uint txminmax;
+
+ struct sk_buff *glomd; /* Packet containing glomming descriptor */
+ struct sk_buff *glom; /* Packet chain for glommed superframe */
+ uint glomerr; /* Glom packet read errors */
+
+ u8 *rxbuf; /* Buffer for receiving control packets */
+ uint rxblen; /* Allocated length of rxbuf */
+ u8 *rxctl; /* Aligned pointer into rxbuf */
+ u8 *databuf; /* Buffer for receiving big glom packet */
+ u8 *dataptr; /* Aligned pointer into databuf */
+ uint rxlen; /* Length of valid data in buffer */
+
+ u8 sdpcm_ver; /* Bus protocol reported by dongle */
+
+ bool intr; /* Use interrupts */
+ bool poll; /* Use polling */
+ bool ipend; /* Device interrupt is pending */
+ uint intrcount; /* Count of device interrupt callbacks */
+ uint lastintrs; /* Count as of last watchdog timer */
+ uint spurious; /* Count of spurious interrupts */
+ uint pollrate; /* Ticks between device polls */
+ uint polltick; /* Tick counter */
+ uint pollcnt; /* Count of active polls */
+
+#ifdef BCMDBG
+ uint console_interval;
+ struct brcmf_console console; /* Console output polling support */
+ uint console_addr; /* Console address from shared struct */
+#endif /* BCMDBG */
+
+ uint regfails; /* Count of R_REG failures */
+
+ uint clkstate; /* State of sd and backplane clock(s) */
+ bool activity; /* Activity flag for clock down */
+ s32 idletime; /* Control for activity timeout */
+ s32 idlecount; /* Activity timeout counter */
+ s32 idleclock; /* How to set bus driver when idle */
+ s32 sd_rxchain;
+ bool use_rxchain; /* If brcmf should use PKT chains */
+ bool sleeping; /* Is SDIO bus sleeping? */
+ bool rxflow_mode; /* Rx flow control mode */
+ bool rxflow; /* Is rx flow control on */
+ bool alp_only; /* Don't use HT clock (ALP only) */
+/* Field to decide if rx of control frames happen in rxbuf or lb-pool */
+ bool usebufpool;
+
+ /* Some additional counters */
+ uint tx_sderrs; /* Count of tx attempts with sd errors */
+ uint fcqueued; /* Tx packets that got queued */
+ uint rxrtx; /* Count of rtx requests (NAK to dongle) */
+ uint rx_toolong; /* Receive frames too long to receive */
+ uint rxc_errors; /* SDIO errors when reading control frames */
+ uint rx_hdrfail; /* SDIO errors on header reads */
+ uint rx_badhdr; /* Bad received headers (roosync?) */
+ uint rx_badseq; /* Mismatched rx sequence number */
+ uint fc_rcvd; /* Number of flow-control events received */
+ uint fc_xoff; /* Number which turned on flow-control */
+ uint fc_xon; /* Number which turned off flow-control */
+ uint rxglomfail; /* Failed deglom attempts */
+ uint rxglomframes; /* Number of glom frames (superframes) */
+ uint rxglompkts; /* Number of packets from glom frames */
+ uint f2rxhdrs; /* Number of header reads */
+ uint f2rxdata; /* Number of frame data reads */
+ uint f2txdata; /* Number of f2 frame writes */
+ uint f1regdata; /* Number of f1 register accesses */
+
+ u8 *ctrl_frame_buf;
+ u32 ctrl_frame_len;
+ bool ctrl_frame_stat;
+
+ spinlock_t txqlock;
+ wait_queue_head_t ctrl_wait;
+ wait_queue_head_t dcmd_resp_wait;
+
+ struct timer_list timer;
+ struct completion watchdog_wait;
+ struct task_struct *watchdog_tsk;
+ bool wd_timer_valid;
+ uint save_ms;
+
+ struct task_struct *dpc_tsk;
+ struct completion dpc_wait;
+
+ struct semaphore sdsem;
+
+ const char *fw_name;
+ const struct firmware *firmware;
+ const char *nv_name;
+ u32 fw_ptr;
+};
+
+struct sbconfig {
+ u32 PAD[2];
+ u32 sbipsflag; /* initiator port ocp slave flag */
+ u32 PAD[3];
+ u32 sbtpsflag; /* target port ocp slave flag */
+ u32 PAD[11];
+ u32 sbtmerrloga; /* (sonics >= 2.3) */
+ u32 PAD;
+ u32 sbtmerrlog; /* (sonics >= 2.3) */
+ u32 PAD[3];
+ u32 sbadmatch3; /* address match3 */
+ u32 PAD;
+ u32 sbadmatch2; /* address match2 */
+ u32 PAD;
+ u32 sbadmatch1; /* address match1 */
+ u32 PAD[7];
+ u32 sbimstate; /* initiator agent state */
+ u32 sbintvec; /* interrupt mask */
+ u32 sbtmstatelow; /* target state */
+ u32 sbtmstatehigh; /* target state */
+ u32 sbbwa0; /* bandwidth allocation table0 */
+ u32 PAD;
+ u32 sbimconfiglow; /* initiator configuration */
+ u32 sbimconfighigh; /* initiator configuration */
+ u32 sbadmatch0; /* address match0 */
+ u32 PAD;
+ u32 sbtmconfiglow; /* target configuration */
+ u32 sbtmconfighigh; /* target configuration */
+ u32 sbbconfig; /* broadcast configuration */
+ u32 PAD;
+ u32 sbbstate; /* broadcast state */
+ u32 PAD[3];
+ u32 sbactcnfg; /* activate configuration */
+ u32 PAD[3];
+ u32 sbflagst; /* current sbflags */
+ u32 PAD[3];
+ u32 sbidlow; /* identification */
+ u32 sbidhigh; /* identification */
+};
+
+/* clkstate */
+#define CLK_NONE 0
+#define CLK_SDONLY 1
+#define CLK_PENDING 2 /* Not used yet */
+#define CLK_AVAIL 3
+
+#ifdef BCMDBG
+static int qcount[NUMPRIO];
+static int tx_packets[NUMPRIO];
+#endif /* BCMDBG */
+
+#define SDIO_DRIVE_STRENGTH 6 /* in milliamps */
+
+#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL)
+
+/* Retry count for register access failures */
+static const uint retry_limit = 2;
+
+/* Limit on rounding up frames */
+static const uint max_roundup = 512;
+
+#define ALIGNMENT 4
+
+static void pkt_align(struct sk_buff *p, int len, int align)
+{
+ uint datalign;
+ datalign = (unsigned long)(p->data);
+ datalign = roundup(datalign, (align)) - datalign;
+ if (datalign)
+ skb_pull(p, datalign);
+ __skb_trim(p, len);
+}
+
+/* To check if there's window offered */
+static bool data_ok(struct brcmf_bus *bus)
+{
+ return (u8)(bus->tx_max - bus->tx_seq) != 0 &&
+ ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0;
+}
+
+/*
+ * Reads a register in the SDIO hardware block. This block occupies a series of
+ * adresses on the 32 bit backplane bus.
+ */
+static void
+r_sdreg32(struct brcmf_bus *bus, u32 *regvar, u32 reg_offset, u32 *retryvar)
+{
+ *retryvar = 0;
+ do {
+ *regvar = brcmf_sdcard_reg_read(bus->sdiodev,
+ bus->ci->buscorebase + reg_offset, sizeof(u32));
+ } while (brcmf_sdcard_regfail(bus->sdiodev) &&
+ (++(*retryvar) <= retry_limit));
+ if (*retryvar) {
+ bus->regfails += (*retryvar-1);
+ if (*retryvar > retry_limit) {
+ brcmf_dbg(ERROR, "FAILED READ %Xh\n", reg_offset);
+ *regvar = 0;
+ }
+ }
+}
+
+static void
+w_sdreg32(struct brcmf_bus *bus, u32 regval, u32 reg_offset, u32 *retryvar)
+{
+ *retryvar = 0;
+ do {
+ brcmf_sdcard_reg_write(bus->sdiodev,
+ bus->ci->buscorebase + reg_offset,
+ sizeof(u32), regval);
+ } while (brcmf_sdcard_regfail(bus->sdiodev) &&
+ (++(*retryvar) <= retry_limit));
+ if (*retryvar) {
+ bus->regfails += (*retryvar-1);
+ if (*retryvar > retry_limit)
+ brcmf_dbg(ERROR, "FAILED REGISTER WRITE %Xh\n",
+ reg_offset);
+ }
+}
+
+#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND)
+
+#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
+
+/* Packet free applicable unconditionally for sdio and sdspi.
+ * Conditional if bufpool was present for gspi bus.
+ */
+static void brcmf_sdbrcm_pktfree2(struct brcmf_bus *bus, struct sk_buff *pkt)
+{
+ if (bus->usebufpool)
+ brcmu_pkt_buf_free_skb(pkt);
+}
+
+/* Turn backplane clock on or off */
+static int brcmf_sdbrcm_htclk(struct brcmf_bus *bus, bool on, bool pendok)
+{
+ int err;
+ u8 clkctl, clkreq, devctl;
+ unsigned long timeout;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ clkctl = 0;
+
+ if (on) {
+ /* Request HT Avail */
+ clkreq =
+ bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
+
+ if ((bus->ci->chip == BCM4329_CHIP_ID)
+ && (bus->ci->chiprev == 0))
+ clkreq |= SBSDIO_FORCE_ALP;
+
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
+ if (err) {
+ brcmf_dbg(ERROR, "HT Avail request error: %d\n", err);
+ return -EBADE;
+ }
+
+ if (pendok && ((bus->ci->buscoretype == PCMCIA_CORE_ID)
+ && (bus->ci->buscorerev == 9))) {
+ u32 dummy, retries;
+ r_sdreg32(bus, &dummy,
+ offsetof(struct sdpcmd_regs, clockctlstatus),
+ &retries);
+ }
+
+ /* Check current status */
+ clkctl = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (err) {
+ brcmf_dbg(ERROR, "HT Avail read error: %d\n", err);
+ return -EBADE;
+ }
+
+ /* Go to pending and await interrupt if appropriate */
+ if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
+ /* Allow only clock-available interrupt */
+ devctl = brcmf_sdcard_cfg_read(bus->sdiodev,
+ SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL, &err);
+ if (err) {
+ brcmf_dbg(ERROR, "Devctl error setting CA: %d\n",
+ err);
+ return -EBADE;
+ }
+
+ devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL, devctl, &err);
+ brcmf_dbg(INFO, "CLKCTL: set PENDING\n");
+ bus->clkstate = CLK_PENDING;
+
+ return 0;
+ } else if (bus->clkstate == CLK_PENDING) {
+ /* Cancel CA-only interrupt filter */
+ devctl =
+ brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL, &err);
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL, devctl, &err);
+ }
+
+ /* Otherwise, wait here (polling) for HT Avail */
+ timeout = jiffies +
+ msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000);
+ while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+ clkctl = brcmf_sdcard_cfg_read(bus->sdiodev,
+ SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ &err);
+ if (time_after(jiffies, timeout))
+ break;
+ else
+ usleep_range(5000, 10000);
+ }
+ if (err) {
+ brcmf_dbg(ERROR, "HT Avail request error: %d\n", err);
+ return -EBADE;
+ }
+ if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+ brcmf_dbg(ERROR, "HT Avail timeout (%d): clkctl 0x%02x\n",
+ PMU_MAX_TRANSITION_DLY, clkctl);
+ return -EBADE;
+ }
+
+ /* Mark clock available */
+ bus->clkstate = CLK_AVAIL;
+ brcmf_dbg(INFO, "CLKCTL: turned ON\n");
+
+#if defined(BCMDBG)
+ if (bus->alp_only != true) {
+ if (SBSDIO_ALPONLY(clkctl))
+ brcmf_dbg(ERROR, "HT Clock should be on\n");
+ }
+#endif /* defined (BCMDBG) */
+
+ bus->activity = true;
+ } else {
+ clkreq = 0;
+
+ if (bus->clkstate == CLK_PENDING) {
+ /* Cancel CA-only interrupt filter */
+ devctl = brcmf_sdcard_cfg_read(bus->sdiodev,
+ SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL, &err);
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL, devctl, &err);
+ }
+
+ bus->clkstate = CLK_SDONLY;
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
+ brcmf_dbg(INFO, "CLKCTL: turned OFF\n");
+ if (err) {
+ brcmf_dbg(ERROR, "Failed access turning clock off: %d\n",
+ err);
+ return -EBADE;
+ }
+ }
+ return 0;
+}
+
+/* Change idle/active SD state */
+static int brcmf_sdbrcm_sdclk(struct brcmf_bus *bus, bool on)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (on)
+ bus->clkstate = CLK_SDONLY;
+ else
+ bus->clkstate = CLK_NONE;
+
+ return 0;
+}
+
+/* Transition SD and backplane clock readiness */
+static int brcmf_sdbrcm_clkctl(struct brcmf_bus *bus, uint target, bool pendok)
+{
+#ifdef BCMDBG
+ uint oldstate = bus->clkstate;
+#endif /* BCMDBG */
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Early exit if we're already there */
+ if (bus->clkstate == target) {
+ if (target == CLK_AVAIL) {
+ brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
+ bus->activity = true;
+ }
+ return 0;
+ }
+
+ switch (target) {
+ case CLK_AVAIL:
+ /* Make sure SD clock is available */
+ if (bus->clkstate == CLK_NONE)
+ brcmf_sdbrcm_sdclk(bus, true);
+ /* Now request HT Avail on the backplane */
+ brcmf_sdbrcm_htclk(bus, true, pendok);
+ brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
+ bus->activity = true;
+ break;
+
+ case CLK_SDONLY:
+ /* Remove HT request, or bring up SD clock */
+ if (bus->clkstate == CLK_NONE)
+ brcmf_sdbrcm_sdclk(bus, true);
+ else if (bus->clkstate == CLK_AVAIL)
+ brcmf_sdbrcm_htclk(bus, false, false);
+ else
+ brcmf_dbg(ERROR, "request for %d -> %d\n",
+ bus->clkstate, target);
+ brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
+ break;
+
+ case CLK_NONE:
+ /* Make sure to remove HT request */
+ if (bus->clkstate == CLK_AVAIL)
+ brcmf_sdbrcm_htclk(bus, false, false);
+ /* Now remove the SD clock */
+ brcmf_sdbrcm_sdclk(bus, false);
+ brcmf_sdbrcm_wd_timer(bus, 0);
+ break;
+ }
+#ifdef BCMDBG
+ brcmf_dbg(INFO, "%d -> %d\n", oldstate, bus->clkstate);
+#endif /* BCMDBG */
+
+ return 0;
+}
+
+static int brcmf_sdbrcm_bussleep(struct brcmf_bus *bus, bool sleep)
+{
+ uint retries = 0;
+
+ brcmf_dbg(INFO, "request %s (currently %s)\n",
+ sleep ? "SLEEP" : "WAKE",
+ bus->sleeping ? "SLEEP" : "WAKE");
+
+ /* Done if we're already in the requested state */
+ if (sleep == bus->sleeping)
+ return 0;
+
+ /* Going to sleep: set the alarm and turn off the lights... */
+ if (sleep) {
+ /* Don't sleep if something is pending */
+ if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq))
+ return -EBUSY;
+
+ /* Make sure the controller has the bus up */
+ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+ /* Tell device to start using OOB wakeup */
+ w_sdreg32(bus, SMB_USE_OOB,
+ offsetof(struct sdpcmd_regs, tosbmailbox), &retries);
+ if (retries > retry_limit)
+ brcmf_dbg(ERROR, "CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n");
+
+ /* Turn off our contribution to the HT clock request */
+ brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
+
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
+
+ /* Isolate the bus */
+ if (bus->ci->chip != BCM4329_CHIP_ID) {
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL,
+ SBSDIO_DEVCTL_PADS_ISO, NULL);
+ }
+
+ /* Change state */
+ bus->sleeping = true;
+
+ } else {
+ /* Waking up: bus power up is ok, set local state */
+
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
+
+ /* Force pad isolation off if possible
+ (in case power never toggled) */
+ if ((bus->ci->buscoretype == PCMCIA_CORE_ID)
+ && (bus->ci->buscorerev >= 10))
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL, 0, NULL);
+
+ /* Make sure the controller has the bus up */
+ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+ /* Send misc interrupt to indicate OOB not needed */
+ w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, tosbmailboxdata),
+ &retries);
+ if (retries <= retry_limit)
+ w_sdreg32(bus, SMB_DEV_INT,
+ offsetof(struct sdpcmd_regs, tosbmailbox),
+ &retries);
+
+ if (retries > retry_limit)
+ brcmf_dbg(ERROR, "CANNOT SIGNAL CHIP TO CLEAR OOB!!\n");
+
+ /* Make sure we have SD bus access */
+ brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
+
+ /* Change state */
+ bus->sleeping = false;
+ }
+
+ return 0;
+}
+
+static void bus_wake(struct brcmf_bus *bus)
+{
+ if (bus->sleeping)
+ brcmf_sdbrcm_bussleep(bus, false);
+}
+
+static u32 brcmf_sdbrcm_hostmail(struct brcmf_bus *bus)
+{
+ u32 intstatus = 0;
+ u32 hmb_data;
+ u8 fcbits;
+ uint retries = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Read mailbox data and ack that we did so */
+ r_sdreg32(bus, &hmb_data,
+ offsetof(struct sdpcmd_regs, tohostmailboxdata), &retries);
+
+ if (retries <= retry_limit)
+ w_sdreg32(bus, SMB_INT_ACK,
+ offsetof(struct sdpcmd_regs, tosbmailbox), &retries);
+ bus->f1regdata += 2;
+
+ /* Dongle recomposed rx frames, accept them again */
+ if (hmb_data & HMB_DATA_NAKHANDLED) {
+ brcmf_dbg(INFO, "Dongle reports NAK handled, expect rtx of %d\n",
+ bus->rx_seq);
+ if (!bus->rxskip)
+ brcmf_dbg(ERROR, "unexpected NAKHANDLED!\n");
+
+ bus->rxskip = false;
+ intstatus |= I_HMB_FRAME_IND;
+ }
+
+ /*
+ * DEVREADY does not occur with gSPI.
+ */
+ if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
+ bus->sdpcm_ver =
+ (hmb_data & HMB_DATA_VERSION_MASK) >>
+ HMB_DATA_VERSION_SHIFT;
+ if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
+ brcmf_dbg(ERROR, "Version mismatch, dongle reports %d, "
+ "expecting %d\n",
+ bus->sdpcm_ver, SDPCM_PROT_VERSION);
+ else
+ brcmf_dbg(INFO, "Dongle ready, protocol version %d\n",
+ bus->sdpcm_ver);
+ }
+
+ /*
+ * Flow Control has been moved into the RX headers and this out of band
+ * method isn't used any more.
+ * remaining backward compatible with older dongles.
+ */
+ if (hmb_data & HMB_DATA_FC) {
+ fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >>
+ HMB_DATA_FCDATA_SHIFT;
+
+ if (fcbits & ~bus->flowcontrol)
+ bus->fc_xoff++;
+
+ if (bus->flowcontrol & ~fcbits)
+ bus->fc_xon++;
+
+ bus->fc_rcvd++;
+ bus->flowcontrol = fcbits;
+ }
+
+ /* Shouldn't be any others */
+ if (hmb_data & ~(HMB_DATA_DEVREADY |
+ HMB_DATA_NAKHANDLED |
+ HMB_DATA_FC |
+ HMB_DATA_FWREADY |
+ HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK))
+ brcmf_dbg(ERROR, "Unknown mailbox data content: 0x%02x\n",
+ hmb_data);
+
+ return intstatus;
+}
+
+static void brcmf_sdbrcm_rxfail(struct brcmf_bus *bus, bool abort, bool rtx)
+{
+ uint retries = 0;
+ u16 lastrbc;
+ u8 hi, lo;
+ int err;
+
+ brcmf_dbg(ERROR, "%sterminate frame%s\n",
+ abort ? "abort command, " : "",
+ rtx ? ", send NAK" : "");
+
+ if (abort)
+ brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
+
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_FRAMECTRL,
+ SFC_RF_TERM, &err);
+ bus->f1regdata++;
+
+ /* Wait until the packet has been flushed (device/FIFO stable) */
+ for (lastrbc = retries = 0xffff; retries > 0; retries--) {
+ hi = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_RFRAMEBCHI, NULL);
+ lo = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_RFRAMEBCLO, NULL);
+ bus->f1regdata += 2;
+
+ if ((hi == 0) && (lo == 0))
+ break;
+
+ if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
+ brcmf_dbg(ERROR, "count growing: last 0x%04x now 0x%04x\n",
+ lastrbc, (hi << 8) + lo);
+ }
+ lastrbc = (hi << 8) + lo;
+ }
+
+ if (!retries)
+ brcmf_dbg(ERROR, "count never zeroed: last 0x%04x\n", lastrbc);
+ else
+ brcmf_dbg(INFO, "flush took %d iterations\n", 0xffff - retries);
+
+ if (rtx) {
+ bus->rxrtx++;
+ w_sdreg32(bus, SMB_NAK,
+ offsetof(struct sdpcmd_regs, tosbmailbox), &retries);
+
+ bus->f1regdata++;
+ if (retries <= retry_limit)
+ bus->rxskip = true;
+ }
+
+ /* Clear partial in any case */
+ bus->nextlen = 0;
+
+ /* If we can't reach the device, signal failure */
+ if (err || brcmf_sdcard_regfail(bus->sdiodev))
+ bus->drvr->busstate = BRCMF_BUS_DOWN;
+}
+
+static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq)
+{
+ u16 dlen, totlen;
+ u8 *dptr, num = 0;
+
+ u16 sublen, check;
+ struct sk_buff *pfirst, *plast, *pnext, *save_pfirst;
+
+ int errcode;
+ u8 chan, seq, doff, sfdoff;
+ u8 txmax;
+
+ int ifidx = 0;
+ bool usechain = bus->use_rxchain;
+
+ /* If packets, issue read(s) and send up packet chain */
+ /* Return sequence numbers consumed? */
+
+ brcmf_dbg(TRACE, "start: glomd %p glom %p\n", bus->glomd, bus->glom);
+
+ /* If there's a descriptor, generate the packet chain */
+ if (bus->glomd) {
+ pfirst = plast = pnext = NULL;
+ dlen = (u16) (bus->glomd->len);
+ dptr = bus->glomd->data;
+ if (!dlen || (dlen & 1)) {
+ brcmf_dbg(ERROR, "bad glomd len(%d), ignore descriptor\n",
+ dlen);
+ dlen = 0;
+ }
+
+ for (totlen = num = 0; dlen; num++) {
+ /* Get (and move past) next length */
+ sublen = get_unaligned_le16(dptr);
+ dlen -= sizeof(u16);
+ dptr += sizeof(u16);
+ if ((sublen < SDPCM_HDRLEN) ||
+ ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
+ brcmf_dbg(ERROR, "descriptor len %d bad: %d\n",
+ num, sublen);
+ pnext = NULL;
+ break;
+ }
+ if (sublen % BRCMF_SDALIGN) {
+ brcmf_dbg(ERROR, "sublen %d not multiple of %d\n",
+ sublen, BRCMF_SDALIGN);
+ usechain = false;
+ }
+ totlen += sublen;
+
+ /* For last frame, adjust read len so total
+ is a block multiple */
+ if (!dlen) {
+ sublen +=
+ (roundup(totlen, bus->blocksize) - totlen);
+ totlen = roundup(totlen, bus->blocksize);
+ }
+
+ /* Allocate/chain packet for next subframe */
+ pnext = brcmu_pkt_buf_get_skb(sublen + BRCMF_SDALIGN);
+ if (pnext == NULL) {
+ brcmf_dbg(ERROR, "bcm_pkt_buf_get_skb failed, num %d len %d\n",
+ num, sublen);
+ break;
+ }
+ if (!pfirst) {
+ pfirst = plast = pnext;
+ } else {
+ plast->next = pnext;
+ plast = pnext;
+ }
+
+ /* Adhere to start alignment requirements */
+ pkt_align(pnext, sublen, BRCMF_SDALIGN);
+ }
+
+ /* If all allocations succeeded, save packet chain
+ in bus structure */
+ if (pnext) {
+ brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n",
+ totlen, num);
+ if (BRCMF_GLOM_ON() && bus->nextlen &&
+ totlen != bus->nextlen) {
+ brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n",
+ bus->nextlen, totlen, rxseq);
+ }
+ bus->glom = pfirst;
+ pfirst = pnext = NULL;
+ } else {
+ if (pfirst)
+ brcmu_pkt_buf_free_skb(pfirst);
+ bus->glom = NULL;
+ num = 0;
+ }
+
+ /* Done with descriptor packet */
+ brcmu_pkt_buf_free_skb(bus->glomd);
+ bus->glomd = NULL;
+ bus->nextlen = 0;
+ }
+
+ /* Ok -- either we just generated a packet chain,
+ or had one from before */
+ if (bus->glom) {
+ if (BRCMF_GLOM_ON()) {
+ brcmf_dbg(GLOM, "try superframe read, packet chain:\n");
+ for (pnext = bus->glom; pnext; pnext = pnext->next) {
+ brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n",
+ pnext, (u8 *) (pnext->data),
+ pnext->len, pnext->len);
+ }
+ }
+
+ pfirst = bus->glom;
+ dlen = (u16) brcmu_pkttotlen(pfirst);
+
+ /* Do an SDIO read for the superframe. Configurable iovar to
+ * read directly into the chained packet, or allocate a large
+ * packet and and copy into the chain.
+ */
+ if (usechain) {
+ errcode = brcmf_sdcard_recv_buf(bus->sdiodev,
+ bus->sdiodev->sbwad,
+ SDIO_FUNC_2,
+ F2SYNC, (u8 *) pfirst->data, dlen,
+ pfirst);
+ } else if (bus->dataptr) {
+ errcode = brcmf_sdcard_recv_buf(bus->sdiodev,
+ bus->sdiodev->sbwad,
+ SDIO_FUNC_2,
+ F2SYNC, bus->dataptr, dlen,
+ NULL);
+ sublen = (u16) brcmu_pktfrombuf(pfirst, 0, dlen,
+ bus->dataptr);
+ if (sublen != dlen) {
+ brcmf_dbg(ERROR, "FAILED TO COPY, dlen %d sublen %d\n",
+ dlen, sublen);
+ errcode = -1;
+ }
+ pnext = NULL;
+ } else {
+ brcmf_dbg(ERROR, "COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n",
+ dlen);
+ errcode = -1;
+ }
+ bus->f2rxdata++;
+
+ /* On failure, kill the superframe, allow a couple retries */
+ if (errcode < 0) {
+ brcmf_dbg(ERROR, "glom read of %d bytes failed: %d\n",
+ dlen, errcode);
+ bus->drvr->rx_errors++;
+
+ if (bus->glomerr++ < 3) {
+ brcmf_sdbrcm_rxfail(bus, true, true);
+ } else {
+ bus->glomerr = 0;
+ brcmf_sdbrcm_rxfail(bus, true, false);
+ brcmu_pkt_buf_free_skb(bus->glom);
+ bus->rxglomfail++;
+ bus->glom = NULL;
+ }
+ return 0;
+ }
+#ifdef BCMDBG
+ if (BRCMF_GLOM_ON()) {
+ printk(KERN_DEBUG "SUPERFRAME:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ pfirst->data, min_t(int, pfirst->len, 48));
+ }
+#endif
+
+ /* Validate the superframe header */
+ dptr = (u8 *) (pfirst->data);
+ sublen = get_unaligned_le16(dptr);
+ check = get_unaligned_le16(dptr + sizeof(u16));
+
+ chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+ seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
+ bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+ if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+ brcmf_dbg(INFO, "nextlen too large (%d) seq %d\n",
+ bus->nextlen, seq);
+ bus->nextlen = 0;
+ }
+ doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+ txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+
+ errcode = 0;
+ if ((u16)~(sublen ^ check)) {
+ brcmf_dbg(ERROR, "(superframe): HW hdr error: len/check 0x%04x/0x%04x\n",
+ sublen, check);
+ errcode = -1;
+ } else if (roundup(sublen, bus->blocksize) != dlen) {
+ brcmf_dbg(ERROR, "(superframe): len 0x%04x, rounded 0x%04x, expect 0x%04x\n",
+ sublen, roundup(sublen, bus->blocksize),
+ dlen);
+ errcode = -1;
+ } else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) !=
+ SDPCM_GLOM_CHANNEL) {
+ brcmf_dbg(ERROR, "(superframe): bad channel %d\n",
+ SDPCM_PACKET_CHANNEL(
+ &dptr[SDPCM_FRAMETAG_LEN]));
+ errcode = -1;
+ } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) {
+ brcmf_dbg(ERROR, "(superframe): got 2nd descriptor?\n");
+ errcode = -1;
+ } else if ((doff < SDPCM_HDRLEN) ||
+ (doff > (pfirst->len - SDPCM_HDRLEN))) {
+ brcmf_dbg(ERROR, "(superframe): Bad data offset %d: HW %d pkt %d min %d\n",
+ doff, sublen, pfirst->len, SDPCM_HDRLEN);
+ errcode = -1;
+ }
+
+ /* Check sequence number of superframe SW header */
+ if (rxseq != seq) {
+ brcmf_dbg(INFO, "(superframe) rx_seq %d, expected %d\n",
+ seq, rxseq);
+ bus->rx_badseq++;
+ rxseq = seq;
+ }
+
+ /* Check window for sanity */
+ if ((u8) (txmax - bus->tx_seq) > 0x40) {
+ brcmf_dbg(ERROR, "unlikely tx max %d with tx_seq %d\n",
+ txmax, bus->tx_seq);
+ txmax = bus->tx_seq + 2;
+ }
+ bus->tx_max = txmax;
+
+ /* Remove superframe header, remember offset */
+ skb_pull(pfirst, doff);
+ sfdoff = doff;
+
+ /* Validate all the subframe headers */
+ for (num = 0, pnext = pfirst; pnext && !errcode;
+ num++, pnext = pnext->next) {
+ dptr = (u8 *) (pnext->data);
+ dlen = (u16) (pnext->len);
+ sublen = get_unaligned_le16(dptr);
+ check = get_unaligned_le16(dptr + sizeof(u16));
+ chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+ doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+#ifdef BCMDBG
+ if (BRCMF_GLOM_ON()) {
+ printk(KERN_DEBUG "subframe:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ dptr, 32);
+ }
+#endif
+
+ if ((u16)~(sublen ^ check)) {
+ brcmf_dbg(ERROR, "(subframe %d): HW hdr error: len/check 0x%04x/0x%04x\n",
+ num, sublen, check);
+ errcode = -1;
+ } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) {
+ brcmf_dbg(ERROR, "(subframe %d): length mismatch: len 0x%04x, expect 0x%04x\n",
+ num, sublen, dlen);
+ errcode = -1;
+ } else if ((chan != SDPCM_DATA_CHANNEL) &&
+ (chan != SDPCM_EVENT_CHANNEL)) {
+ brcmf_dbg(ERROR, "(subframe %d): bad channel %d\n",
+ num, chan);
+ errcode = -1;
+ } else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) {
+ brcmf_dbg(ERROR, "(subframe %d): Bad data offset %d: HW %d min %d\n",
+ num, doff, sublen, SDPCM_HDRLEN);
+ errcode = -1;
+ }
+ }
+
+ if (errcode) {
+ /* Terminate frame on error, request
+ a couple retries */
+ if (bus->glomerr++ < 3) {
+ /* Restore superframe header space */
+ skb_push(pfirst, sfdoff);
+ brcmf_sdbrcm_rxfail(bus, true, true);
+ } else {
+ bus->glomerr = 0;
+ brcmf_sdbrcm_rxfail(bus, true, false);
+ brcmu_pkt_buf_free_skb(bus->glom);
+ bus->rxglomfail++;
+ bus->glom = NULL;
+ }
+ bus->nextlen = 0;
+ return 0;
+ }
+
+ /* Basic SD framing looks ok - process each packet (header) */
+ save_pfirst = pfirst;
+ bus->glom = NULL;
+ plast = NULL;
+
+ for (num = 0; pfirst; rxseq++, pfirst = pnext) {
+ pnext = pfirst->next;
+ pfirst->next = NULL;
+
+ dptr = (u8 *) (pfirst->data);
+ sublen = get_unaligned_le16(dptr);
+ chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+ seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
+ doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+
+ brcmf_dbg(GLOM, "Get subframe %d, %p(%p/%d), sublen %d chan %d seq %d\n",
+ num, pfirst, pfirst->data,
+ pfirst->len, sublen, chan, seq);
+
+ /* precondition: chan == SDPCM_DATA_CHANNEL ||
+ chan == SDPCM_EVENT_CHANNEL */
+
+ if (rxseq != seq) {
+ brcmf_dbg(GLOM, "rx_seq %d, expected %d\n",
+ seq, rxseq);
+ bus->rx_badseq++;
+ rxseq = seq;
+ }
+#ifdef BCMDBG
+ if (BRCMF_BYTES_ON() && BRCMF_DATA_ON()) {
+ printk(KERN_DEBUG "Rx Subframe Data:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ dptr, dlen);
+ }
+#endif
+
+ __skb_trim(pfirst, sublen);
+ skb_pull(pfirst, doff);
+
+ if (pfirst->len == 0) {
+ brcmu_pkt_buf_free_skb(pfirst);
+ if (plast)
+ plast->next = pnext;
+ else
+ save_pfirst = pnext;
+
+ continue;
+ } else if (brcmf_proto_hdrpull(bus->drvr, &ifidx,
+ pfirst) != 0) {
+ brcmf_dbg(ERROR, "rx protocol error\n");
+ bus->drvr->rx_errors++;
+ brcmu_pkt_buf_free_skb(pfirst);
+ if (plast)
+ plast->next = pnext;
+ else
+ save_pfirst = pnext;
+
+ continue;
+ }
+
+ /* this packet will go up, link back into
+ chain and count it */
+ pfirst->next = pnext;
+ plast = pfirst;
+ num++;
+
+#ifdef BCMDBG
+ if (BRCMF_GLOM_ON()) {
+ brcmf_dbg(GLOM, "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n",
+ num, pfirst, pfirst->data,
+ pfirst->len, pfirst->next,
+ pfirst->prev);
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ pfirst->data,
+ min_t(int, pfirst->len, 32));
+ }
+#endif /* BCMDBG */
+ }
+ if (num) {
+ up(&bus->sdsem);
+ brcmf_rx_frame(bus->drvr, ifidx, save_pfirst, num);
+ down(&bus->sdsem);
+ }
+
+ bus->rxglomframes++;
+ bus->rxglompkts += num;
+ }
+ return num;
+}
+
+static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_bus *bus, uint *condition,
+ bool *pending)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT);
+
+ /* Wait until control frame is available */
+ add_wait_queue(&bus->dcmd_resp_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (!(*condition) && (!signal_pending(current) && timeout))
+ timeout = schedule_timeout(timeout);
+
+ if (signal_pending(current))
+ *pending = true;
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&bus->dcmd_resp_wait, &wait);
+
+ return timeout;
+}
+
+static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_bus *bus)
+{
+ if (waitqueue_active(&bus->dcmd_resp_wait))
+ wake_up_interruptible(&bus->dcmd_resp_wait);
+
+ return 0;
+}
+static void
+brcmf_sdbrcm_read_control(struct brcmf_bus *bus, u8 *hdr, uint len, uint doff)
+{
+ uint rdlen, pad;
+
+ int sdret;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Set rxctl for frame (w/optional alignment) */
+ bus->rxctl = bus->rxbuf;
+ bus->rxctl += BRCMF_FIRSTREAD;
+ pad = ((unsigned long)bus->rxctl % BRCMF_SDALIGN);
+ if (pad)
+ bus->rxctl += (BRCMF_SDALIGN - pad);
+ bus->rxctl -= BRCMF_FIRSTREAD;
+
+ /* Copy the already-read portion over */
+ memcpy(bus->rxctl, hdr, BRCMF_FIRSTREAD);
+ if (len <= BRCMF_FIRSTREAD)
+ goto gotpkt;
+
+ /* Raise rdlen to next SDIO block to avoid tail command */
+ rdlen = len - BRCMF_FIRSTREAD;
+ if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
+ pad = bus->blocksize - (rdlen % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+ ((len + pad) < bus->drvr->maxctl))
+ rdlen += pad;
+ } else if (rdlen % BRCMF_SDALIGN) {
+ rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
+ }
+
+ /* Satisfy length-alignment requirements */
+ if (rdlen & (ALIGNMENT - 1))
+ rdlen = roundup(rdlen, ALIGNMENT);
+
+ /* Drop if the read is too big or it exceeds our maximum */
+ if ((rdlen + BRCMF_FIRSTREAD) > bus->drvr->maxctl) {
+ brcmf_dbg(ERROR, "%d-byte control read exceeds %d-byte buffer\n",
+ rdlen, bus->drvr->maxctl);
+ bus->drvr->rx_errors++;
+ brcmf_sdbrcm_rxfail(bus, false, false);
+ goto done;
+ }
+
+ if ((len - doff) > bus->drvr->maxctl) {
+ brcmf_dbg(ERROR, "%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
+ len, len - doff, bus->drvr->maxctl);
+ bus->drvr->rx_errors++;
+ bus->rx_toolong++;
+ brcmf_sdbrcm_rxfail(bus, false, false);
+ goto done;
+ }
+
+ /* Read remainder of frame body into the rxctl buffer */
+ sdret = brcmf_sdcard_recv_buf(bus->sdiodev,
+ bus->sdiodev->sbwad,
+ SDIO_FUNC_2,
+ F2SYNC, (bus->rxctl + BRCMF_FIRSTREAD), rdlen,
+ NULL);
+ bus->f2rxdata++;
+
+ /* Control frame failures need retransmission */
+ if (sdret < 0) {
+ brcmf_dbg(ERROR, "read %d control bytes failed: %d\n",
+ rdlen, sdret);
+ bus->rxc_errors++;
+ brcmf_sdbrcm_rxfail(bus, true, true);
+ goto done;
+ }
+
+gotpkt:
+
+#ifdef BCMDBG
+ if (BRCMF_BYTES_ON() && BRCMF_CTL_ON()) {
+ printk(KERN_DEBUG "RxCtrl:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, bus->rxctl, len);
+ }
+#endif
+
+ /* Point to valid data and indicate its length */
+ bus->rxctl += doff;
+ bus->rxlen = len - doff;
+
+done:
+ /* Awake any waiters */
+ brcmf_sdbrcm_dcmd_resp_wake(bus);
+}
+
+/* Pad read to blocksize for efficiency */
+static void brcmf_pad(struct brcmf_bus *bus, u16 *pad, u16 *rdlen)
+{
+ if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) {
+ *pad = bus->blocksize - (*rdlen % bus->blocksize);
+ if (*pad <= bus->roundup && *pad < bus->blocksize &&
+ *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ)
+ *rdlen += *pad;
+ } else if (*rdlen % BRCMF_SDALIGN) {
+ *rdlen += BRCMF_SDALIGN - (*rdlen % BRCMF_SDALIGN);
+ }
+}
+
+static void
+brcmf_alloc_pkt_and_read(struct brcmf_bus *bus, u16 rdlen,
+ struct sk_buff **pkt, u8 **rxbuf)
+{
+ int sdret; /* Return code from calls */
+
+ *pkt = brcmu_pkt_buf_get_skb(rdlen + BRCMF_SDALIGN);
+ if (*pkt == NULL)
+ return;
+
+ pkt_align(*pkt, rdlen, BRCMF_SDALIGN);
+ *rxbuf = (u8 *) ((*pkt)->data);
+ /* Read the entire frame */
+ sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad,
+ SDIO_FUNC_2, F2SYNC,
+ *rxbuf, rdlen, *pkt);
+ bus->f2rxdata++;
+
+ if (sdret < 0) {
+ brcmf_dbg(ERROR, "(nextlen): read %d bytes failed: %d\n",
+ rdlen, sdret);
+ brcmu_pkt_buf_free_skb(*pkt);
+ bus->drvr->rx_errors++;
+ /* Force retry w/normal header read.
+ * Don't attempt NAK for
+ * gSPI
+ */
+ brcmf_sdbrcm_rxfail(bus, true, true);
+ *pkt = NULL;
+ }
+}
+
+/* Checks the header */
+static int
+brcmf_check_rxbuf(struct brcmf_bus *bus, struct sk_buff *pkt, u8 *rxbuf,
+ u8 rxseq, u16 nextlen, u16 *len)
+{
+ u16 check;
+ bool len_consistent; /* Result of comparing readahead len and
+ len from hw-hdr */
+
+ memcpy(bus->rxhdr, rxbuf, SDPCM_HDRLEN);
+
+ /* Extract hardware header fields */
+ *len = get_unaligned_le16(bus->rxhdr);
+ check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
+
+ /* All zeros means readahead info was bad */
+ if (!(*len | check)) {
+ brcmf_dbg(INFO, "(nextlen): read zeros in HW header???\n");
+ goto fail;
+ }
+
+ /* Validate check bytes */
+ if ((u16)~(*len ^ check)) {
+ brcmf_dbg(ERROR, "(nextlen): HW hdr error: nextlen/len/check 0x%04x/0x%04x/0x%04x\n",
+ nextlen, *len, check);
+ bus->rx_badhdr++;
+ brcmf_sdbrcm_rxfail(bus, false, false);
+ goto fail;
+ }
+
+ /* Validate frame length */
+ if (*len < SDPCM_HDRLEN) {
+ brcmf_dbg(ERROR, "(nextlen): HW hdr length invalid: %d\n",
+ *len);
+ goto fail;
+ }
+
+ /* Check for consistency with readahead info */
+ len_consistent = (nextlen != (roundup(*len, 16) >> 4));
+ if (len_consistent) {
+ /* Mismatch, force retry w/normal
+ header (may be >4K) */
+ brcmf_dbg(ERROR, "(nextlen): mismatch, nextlen %d len %d rnd %d; expected rxseq %d\n",
+ nextlen, *len, roundup(*len, 16),
+ rxseq);
+ brcmf_sdbrcm_rxfail(bus, true, true);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ brcmf_sdbrcm_pktfree2(bus, pkt);
+ return -EINVAL;
+}
+
+/* Return true if there may be more frames to read */
+static uint
+brcmf_sdbrcm_readframes(struct brcmf_bus *bus, uint maxframes, bool *finished)
+{
+ u16 len, check; /* Extracted hardware header fields */
+ u8 chan, seq, doff; /* Extracted software header fields */
+ u8 fcbits; /* Extracted fcbits from software header */
+
+ struct sk_buff *pkt; /* Packet for event or data frames */
+ u16 pad; /* Number of pad bytes to read */
+ u16 rdlen; /* Total number of bytes to read */
+ u8 rxseq; /* Next sequence number to expect */
+ uint rxleft = 0; /* Remaining number of frames allowed */
+ int sdret; /* Return code from calls */
+ u8 txmax; /* Maximum tx sequence offered */
+ u8 *rxbuf;
+ int ifidx = 0;
+ uint rxcount = 0; /* Total frames read */
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Not finished unless we encounter no more frames indication */
+ *finished = false;
+
+ for (rxseq = bus->rx_seq, rxleft = maxframes;
+ !bus->rxskip && rxleft && bus->drvr->busstate != BRCMF_BUS_DOWN;
+ rxseq++, rxleft--) {
+
+ /* Handle glomming separately */
+ if (bus->glom || bus->glomd) {
+ u8 cnt;
+ brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n",
+ bus->glomd, bus->glom);
+ cnt = brcmf_sdbrcm_rxglom(bus, rxseq);
+ brcmf_dbg(GLOM, "rxglom returned %d\n", cnt);
+ rxseq += cnt - 1;
+ rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
+ continue;
+ }
+
+ /* Try doing single read if we can */
+ if (bus->nextlen) {
+ u16 nextlen = bus->nextlen;
+ bus->nextlen = 0;
+
+ rdlen = len = nextlen << 4;
+ brcmf_pad(bus, &pad, &rdlen);
+
+ /*
+ * After the frame is received we have to
+ * distinguish whether it is data
+ * or non-data frame.
+ */
+ brcmf_alloc_pkt_and_read(bus, rdlen, &pkt, &rxbuf);
+ if (pkt == NULL) {
+ /* Give up on data, request rtx of events */
+ brcmf_dbg(ERROR, "(nextlen): brcmf_alloc_pkt_and_read failed: len %d rdlen %d expected rxseq %d\n",
+ len, rdlen, rxseq);
+ continue;
+ }
+
+ if (brcmf_check_rxbuf(bus, pkt, rxbuf, rxseq, nextlen,
+ &len) < 0)
+ continue;
+
+ /* Extract software header fields */
+ chan = SDPCM_PACKET_CHANNEL(
+ &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ seq = SDPCM_PACKET_SEQUENCE(
+ &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ doff = SDPCM_DOFFSET_VALUE(
+ &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ txmax = SDPCM_WINDOW_VALUE(
+ &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+ bus->nextlen =
+ bus->rxhdr[SDPCM_FRAMETAG_LEN +
+ SDPCM_NEXTLEN_OFFSET];
+ if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+ brcmf_dbg(INFO, "(nextlen): got frame w/nextlen too large (%d), seq %d\n",
+ bus->nextlen, seq);
+ bus->nextlen = 0;
+ }
+
+ bus->drvr->rx_readahead_cnt++;
+
+ /* Handle Flow Control */
+ fcbits = SDPCM_FCMASK_VALUE(
+ &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+ if (bus->flowcontrol != fcbits) {
+ if (~bus->flowcontrol & fcbits)
+ bus->fc_xoff++;
+
+ if (bus->flowcontrol & ~fcbits)
+ bus->fc_xon++;
+
+ bus->fc_rcvd++;
+ bus->flowcontrol = fcbits;
+ }
+
+ /* Check and update sequence number */
+ if (rxseq != seq) {
+ brcmf_dbg(INFO, "(nextlen): rx_seq %d, expected %d\n",
+ seq, rxseq);
+ bus->rx_badseq++;
+ rxseq = seq;
+ }
+
+ /* Check window for sanity */
+ if ((u8) (txmax - bus->tx_seq) > 0x40) {
+ brcmf_dbg(ERROR, "got unlikely tx max %d with tx_seq %d\n",
+ txmax, bus->tx_seq);
+ txmax = bus->tx_seq + 2;
+ }
+ bus->tx_max = txmax;
+
+#ifdef BCMDBG
+ if (BRCMF_BYTES_ON() && BRCMF_DATA_ON()) {
+ printk(KERN_DEBUG "Rx Data:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ rxbuf, len);
+ } else if (BRCMF_HDRS_ON()) {
+ printk(KERN_DEBUG "RxHdr:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ bus->rxhdr, SDPCM_HDRLEN);
+ }
+#endif
+
+ if (chan == SDPCM_CONTROL_CHANNEL) {
+ brcmf_dbg(ERROR, "(nextlen): readahead on control packet %d?\n",
+ seq);
+ /* Force retry w/normal header read */
+ bus->nextlen = 0;
+ brcmf_sdbrcm_rxfail(bus, false, true);
+ brcmf_sdbrcm_pktfree2(bus, pkt);
+ continue;
+ }
+
+ /* Validate data offset */
+ if ((doff < SDPCM_HDRLEN) || (doff > len)) {
+ brcmf_dbg(ERROR, "(nextlen): bad data offset %d: HW len %d min %d\n",
+ doff, len, SDPCM_HDRLEN);
+ brcmf_sdbrcm_rxfail(bus, false, false);
+ brcmf_sdbrcm_pktfree2(bus, pkt);
+ continue;
+ }
+
+ /* All done with this one -- now deliver the packet */
+ goto deliver;
+ }
+
+ /* Read frame header (hardware and software) */
+ sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad,
+ SDIO_FUNC_2, F2SYNC, bus->rxhdr,
+ BRCMF_FIRSTREAD, NULL);
+ bus->f2rxhdrs++;
+
+ if (sdret < 0) {
+ brcmf_dbg(ERROR, "RXHEADER FAILED: %d\n", sdret);
+ bus->rx_hdrfail++;
+ brcmf_sdbrcm_rxfail(bus, true, true);
+ continue;
+ }
+#ifdef BCMDBG
+ if (BRCMF_BYTES_ON() || BRCMF_HDRS_ON()) {
+ printk(KERN_DEBUG "RxHdr:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ bus->rxhdr, SDPCM_HDRLEN);
+ }
+#endif
+
+ /* Extract hardware header fields */
+ len = get_unaligned_le16(bus->rxhdr);
+ check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
+
+ /* All zeros means no more frames */
+ if (!(len | check)) {
+ *finished = true;
+ break;
+ }
+
+ /* Validate check bytes */
+ if ((u16) ~(len ^ check)) {
+ brcmf_dbg(ERROR, "HW hdr err: len/check 0x%04x/0x%04x\n",
+ len, check);
+ bus->rx_badhdr++;
+ brcmf_sdbrcm_rxfail(bus, false, false);
+ continue;
+ }
+
+ /* Validate frame length */
+ if (len < SDPCM_HDRLEN) {
+ brcmf_dbg(ERROR, "HW hdr length invalid: %d\n", len);
+ continue;
+ }
+
+ /* Extract software header fields */
+ chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+ /* Validate data offset */
+ if ((doff < SDPCM_HDRLEN) || (doff > len)) {
+ brcmf_dbg(ERROR, "Bad data offset %d: HW len %d, min %d seq %d\n",
+ doff, len, SDPCM_HDRLEN, seq);
+ bus->rx_badhdr++;
+ brcmf_sdbrcm_rxfail(bus, false, false);
+ continue;
+ }
+
+ /* Save the readahead length if there is one */
+ bus->nextlen =
+ bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+ if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+ brcmf_dbg(INFO, "(nextlen): got frame w/nextlen too large (%d), seq %d\n",
+ bus->nextlen, seq);
+ bus->nextlen = 0;
+ }
+
+ /* Handle Flow Control */
+ fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+ if (bus->flowcontrol != fcbits) {
+ if (~bus->flowcontrol & fcbits)
+ bus->fc_xoff++;
+
+ if (bus->flowcontrol & ~fcbits)
+ bus->fc_xon++;
+
+ bus->fc_rcvd++;
+ bus->flowcontrol = fcbits;
+ }
+
+ /* Check and update sequence number */
+ if (rxseq != seq) {
+ brcmf_dbg(INFO, "rx_seq %d, expected %d\n", seq, rxseq);
+ bus->rx_badseq++;
+ rxseq = seq;
+ }
+
+ /* Check window for sanity */
+ if ((u8) (txmax - bus->tx_seq) > 0x40) {
+ brcmf_dbg(ERROR, "unlikely tx max %d with tx_seq %d\n",
+ txmax, bus->tx_seq);
+ txmax = bus->tx_seq + 2;
+ }
+ bus->tx_max = txmax;
+
+ /* Call a separate function for control frames */
+ if (chan == SDPCM_CONTROL_CHANNEL) {
+ brcmf_sdbrcm_read_control(bus, bus->rxhdr, len, doff);
+ continue;
+ }
+
+ /* precondition: chan is either SDPCM_DATA_CHANNEL,
+ SDPCM_EVENT_CHANNEL, SDPCM_TEST_CHANNEL or
+ SDPCM_GLOM_CHANNEL */
+
+ /* Length to read */
+ rdlen = (len > BRCMF_FIRSTREAD) ? (len - BRCMF_FIRSTREAD) : 0;
+
+ /* May pad read to blocksize for efficiency */
+ if (bus->roundup && bus->blocksize &&
+ (rdlen > bus->blocksize)) {
+ pad = bus->blocksize - (rdlen % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+ ((rdlen + pad + BRCMF_FIRSTREAD) < MAX_RX_DATASZ))
+ rdlen += pad;
+ } else if (rdlen % BRCMF_SDALIGN) {
+ rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
+ }
+
+ /* Satisfy length-alignment requirements */
+ if (rdlen & (ALIGNMENT - 1))
+ rdlen = roundup(rdlen, ALIGNMENT);
+
+ if ((rdlen + BRCMF_FIRSTREAD) > MAX_RX_DATASZ) {
+ /* Too long -- skip this frame */
+ brcmf_dbg(ERROR, "too long: len %d rdlen %d\n",
+ len, rdlen);
+ bus->drvr->rx_errors++;
+ bus->rx_toolong++;
+ brcmf_sdbrcm_rxfail(bus, false, false);
+ continue;
+ }
+
+ pkt = brcmu_pkt_buf_get_skb(rdlen +
+ BRCMF_FIRSTREAD + BRCMF_SDALIGN);
+ if (!pkt) {
+ /* Give up on data, request rtx of events */
+ brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: rdlen %d chan %d\n",
+ rdlen, chan);
+ bus->drvr->rx_dropped++;
+ brcmf_sdbrcm_rxfail(bus, false, RETRYCHAN(chan));
+ continue;
+ }
+
+ /* Leave room for what we already read, and align remainder */
+ skb_pull(pkt, BRCMF_FIRSTREAD);
+ pkt_align(pkt, rdlen, BRCMF_SDALIGN);
+
+ /* Read the remaining frame data */
+ sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad,
+ SDIO_FUNC_2, F2SYNC, ((u8 *) (pkt->data)),
+ rdlen, pkt);
+ bus->f2rxdata++;
+
+ if (sdret < 0) {
+ brcmf_dbg(ERROR, "read %d %s bytes failed: %d\n", rdlen,
+ ((chan == SDPCM_EVENT_CHANNEL) ? "event"
+ : ((chan == SDPCM_DATA_CHANNEL) ? "data"
+ : "test")), sdret);
+ brcmu_pkt_buf_free_skb(pkt);
+ bus->drvr->rx_errors++;
+ brcmf_sdbrcm_rxfail(bus, true, RETRYCHAN(chan));
+ continue;
+ }
+
+ /* Copy the already-read portion */
+ skb_push(pkt, BRCMF_FIRSTREAD);
+ memcpy(pkt->data, bus->rxhdr, BRCMF_FIRSTREAD);
+
+#ifdef BCMDBG
+ if (BRCMF_BYTES_ON() && BRCMF_DATA_ON()) {
+ printk(KERN_DEBUG "Rx Data:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ pkt->data, len);
+ }
+#endif
+
+deliver:
+ /* Save superframe descriptor and allocate packet frame */
+ if (chan == SDPCM_GLOM_CHANNEL) {
+ if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
+ brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
+ len);
+#ifdef BCMDBG
+ if (BRCMF_GLOM_ON()) {
+ printk(KERN_DEBUG "Glom Data:\n");
+ print_hex_dump_bytes("",
+ DUMP_PREFIX_OFFSET,
+ pkt->data, len);
+ }
+#endif
+ __skb_trim(pkt, len);
+ skb_pull(pkt, SDPCM_HDRLEN);
+ bus->glomd = pkt;
+ } else {
+ brcmf_dbg(ERROR, "%s: glom superframe w/o "
+ "descriptor!\n", __func__);
+ brcmf_sdbrcm_rxfail(bus, false, false);
+ }
+ continue;
+ }
+
+ /* Fill in packet len and prio, deliver upward */
+ __skb_trim(pkt, len);
+ skb_pull(pkt, doff);
+
+ if (pkt->len == 0) {
+ brcmu_pkt_buf_free_skb(pkt);
+ continue;
+ } else if (brcmf_proto_hdrpull(bus->drvr, &ifidx, pkt) != 0) {
+ brcmf_dbg(ERROR, "rx protocol error\n");
+ brcmu_pkt_buf_free_skb(pkt);
+ bus->drvr->rx_errors++;
+ continue;
+ }
+
+ /* Unlock during rx call */
+ up(&bus->sdsem);
+ brcmf_rx_frame(bus->drvr, ifidx, pkt, 1);
+ down(&bus->sdsem);
+ }
+ rxcount = maxframes - rxleft;
+#ifdef BCMDBG
+ /* Message if we hit the limit */
+ if (!rxleft)
+ brcmf_dbg(DATA, "hit rx limit of %d frames\n",
+ maxframes);
+ else
+#endif /* BCMDBG */
+ brcmf_dbg(DATA, "processed %d frames\n", rxcount);
+ /* Back off rxseq if awaiting rtx, update rx_seq */
+ if (bus->rxskip)
+ rxseq--;
+ bus->rx_seq = rxseq;
+
+ return rxcount;
+}
+
+static int
+brcmf_sdbrcm_send_buf(struct brcmf_bus *bus, u32 addr, uint fn, uint flags,
+ u8 *buf, uint nbytes, struct sk_buff *pkt)
+{
+ return brcmf_sdcard_send_buf
+ (bus->sdiodev, addr, fn, flags, buf, nbytes, pkt);
+}
+
+static void
+brcmf_sdbrcm_wait_for_event(struct brcmf_bus *bus, bool *lockvar)
+{
+ up(&bus->sdsem);
+ wait_event_interruptible_timeout(bus->ctrl_wait,
+ (*lockvar == false), HZ * 2);
+ down(&bus->sdsem);
+ return;
+}
+
+static void
+brcmf_sdbrcm_wait_event_wakeup(struct brcmf_bus *bus)
+{
+ if (waitqueue_active(&bus->ctrl_wait))
+ wake_up_interruptible(&bus->ctrl_wait);
+ return;
+}
+
+/* Writes a HW/SW header into the packet and sends it. */
+/* Assumes: (a) header space already there, (b) caller holds lock */
+static int brcmf_sdbrcm_txpkt(struct brcmf_bus *bus, struct sk_buff *pkt,
+ uint chan, bool free_pkt)
+{
+ int ret;
+ u8 *frame;
+ u16 len, pad = 0;
+ u32 swheader;
+ struct sk_buff *new;
+ int i;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ frame = (u8 *) (pkt->data);
+
+ /* Add alignment padding, allocate new packet if needed */
+ pad = ((unsigned long)frame % BRCMF_SDALIGN);
+ if (pad) {
+ if (skb_headroom(pkt) < pad) {
+ brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n",
+ skb_headroom(pkt), pad);
+ bus->drvr->tx_realloc++;
+ new = brcmu_pkt_buf_get_skb(pkt->len + BRCMF_SDALIGN);
+ if (!new) {
+ brcmf_dbg(ERROR, "couldn't allocate new %d-byte packet\n",
+ pkt->len + BRCMF_SDALIGN);
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ pkt_align(new, pkt->len, BRCMF_SDALIGN);
+ memcpy(new->data, pkt->data, pkt->len);
+ if (free_pkt)
+ brcmu_pkt_buf_free_skb(pkt);
+ /* free the pkt if canned one is not used */
+ free_pkt = true;
+ pkt = new;
+ frame = (u8 *) (pkt->data);
+ /* precondition: (frame % BRCMF_SDALIGN) == 0) */
+ pad = 0;
+ } else {
+ skb_push(pkt, pad);
+ frame = (u8 *) (pkt->data);
+ /* precondition: pad + SDPCM_HDRLEN <= pkt->len */
+ memset(frame, 0, pad + SDPCM_HDRLEN);
+ }
+ }
+ /* precondition: pad < BRCMF_SDALIGN */
+
+ /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
+ len = (u16) (pkt->len);
+ *(__le16 *) frame = cpu_to_le16(len);
+ *(((__le16 *) frame) + 1) = cpu_to_le16(~len);
+
+ /* Software tag: channel, sequence number, data offset */
+ swheader =
+ ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
+ (((pad +
+ SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
+
+ put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
+ put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+
+#ifdef BCMDBG
+ tx_packets[pkt->priority]++;
+ if (BRCMF_BYTES_ON() &&
+ (((BRCMF_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) ||
+ (BRCMF_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) {
+ printk(KERN_DEBUG "Tx Frame:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, frame, len);
+ } else if (BRCMF_HDRS_ON()) {
+ printk(KERN_DEBUG "TxHdr:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ frame, min_t(u16, len, 16));
+ }
+#endif
+
+ /* Raise len to next SDIO block to eliminate tail command */
+ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+ u16 pad = bus->blocksize - (len % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize))
+ len += pad;
+ } else if (len % BRCMF_SDALIGN) {
+ len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN);
+ }
+
+ /* Some controllers have trouble with odd bytes -- round to even */
+ if (len & (ALIGNMENT - 1))
+ len = roundup(len, ALIGNMENT);
+
+ ret = brcmf_sdbrcm_send_buf(bus, bus->sdiodev->sbwad,
+ SDIO_FUNC_2, F2SYNC, frame,
+ len, pkt);
+ bus->f2txdata++;
+
+ if (ret < 0) {
+ /* On failure, abort the command and terminate the frame */
+ brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
+ ret);
+ bus->tx_sderrs++;
+
+ brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
+ NULL);
+ bus->f1regdata++;
+
+ for (i = 0; i < 3; i++) {
+ u8 hi, lo;
+ hi = brcmf_sdcard_cfg_read(bus->sdiodev,
+ SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCHI,
+ NULL);
+ lo = brcmf_sdcard_cfg_read(bus->sdiodev,
+ SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCLO,
+ NULL);
+ bus->f1regdata += 2;
+ if ((hi == 0) && (lo == 0))
+ break;
+ }
+
+ }
+ if (ret == 0)
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+
+done:
+ /* restore pkt buffer pointer before calling tx complete routine */
+ skb_pull(pkt, SDPCM_HDRLEN + pad);
+ up(&bus->sdsem);
+ brcmf_txcomplete(bus->drvr, pkt, ret != 0);
+ down(&bus->sdsem);
+
+ if (free_pkt)
+ brcmu_pkt_buf_free_skb(pkt);
+
+ return ret;
+}
+
+static uint brcmf_sdbrcm_sendfromq(struct brcmf_bus *bus, uint maxframes)
+{
+ struct sk_buff *pkt;
+ u32 intstatus = 0;
+ uint retries = 0;
+ int ret = 0, prec_out;
+ uint cnt = 0;
+ uint datalen;
+ u8 tx_prec_map;
+
+ struct brcmf_pub *drvr = bus->drvr;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ tx_prec_map = ~bus->flowcontrol;
+
+ /* Send frames until the limit or some other event */
+ for (cnt = 0; (cnt < maxframes) && data_ok(bus); cnt++) {
+ spin_lock_bh(&bus->txqlock);
+ pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out);
+ if (pkt == NULL) {
+ spin_unlock_bh(&bus->txqlock);
+ break;
+ }
+ spin_unlock_bh(&bus->txqlock);
+ datalen = pkt->len - SDPCM_HDRLEN;
+
+ ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
+ if (ret)
+ bus->drvr->tx_errors++;
+ else
+ bus->drvr->dstats.tx_bytes += datalen;
+
+ /* In poll mode, need to check for other events */
+ if (!bus->intr && cnt) {
+ /* Check device status, signal pending interrupt */
+ r_sdreg32(bus, &intstatus,
+ offsetof(struct sdpcmd_regs, intstatus),
+ &retries);
+ bus->f2txdata++;
+ if (brcmf_sdcard_regfail(bus->sdiodev))
+ break;
+ if (intstatus & bus->hostintmask)
+ bus->ipend = true;
+ }
+ }
+
+ /* Deflow-control stack if needed */
+ if (drvr->up && (drvr->busstate == BRCMF_BUS_DATA) &&
+ drvr->txoff && (pktq_len(&bus->txq) < TXLOW))
+ brcmf_txflowcontrol(drvr, 0, OFF);
+
+ return cnt;
+}
+
+static bool brcmf_sdbrcm_dpc(struct brcmf_bus *bus)
+{
+ u32 intstatus, newstatus = 0;
+ uint retries = 0;
+ uint rxlimit = bus->rxbound; /* Rx frames to read before resched */
+ uint txlimit = bus->txbound; /* Tx frames to send before resched */
+ uint framecnt = 0; /* Temporary counter of tx/rx frames */
+ bool rxdone = true; /* Flag for no more read data */
+ bool resched = false; /* Flag indicating resched wanted */
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Start with leftover status bits */
+ intstatus = bus->intstatus;
+
+ down(&bus->sdsem);
+
+ /* If waiting for HTAVAIL, check status */
+ if (bus->clkstate == CLK_PENDING) {
+ int err;
+ u8 clkctl, devctl = 0;
+
+#ifdef BCMDBG
+ /* Check for inconsistent device control */
+ devctl = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL, &err);
+ if (err) {
+ brcmf_dbg(ERROR, "error reading DEVCTL: %d\n", err);
+ bus->drvr->busstate = BRCMF_BUS_DOWN;
+ }
+#endif /* BCMDBG */
+
+ /* Read CSR, if clock on switch to AVAIL, else ignore */
+ clkctl = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (err) {
+ brcmf_dbg(ERROR, "error reading CSR: %d\n",
+ err);
+ bus->drvr->busstate = BRCMF_BUS_DOWN;
+ }
+
+ brcmf_dbg(INFO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",
+ devctl, clkctl);
+
+ if (SBSDIO_HTAV(clkctl)) {
+ devctl = brcmf_sdcard_cfg_read(bus->sdiodev,
+ SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL, &err);
+ if (err) {
+ brcmf_dbg(ERROR, "error reading DEVCTL: %d\n",
+ err);
+ bus->drvr->busstate = BRCMF_BUS_DOWN;
+ }
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_DEVICE_CTL, devctl, &err);
+ if (err) {
+ brcmf_dbg(ERROR, "error writing DEVCTL: %d\n",
+ err);
+ bus->drvr->busstate = BRCMF_BUS_DOWN;
+ }
+ bus->clkstate = CLK_AVAIL;
+ } else {
+ goto clkwait;
+ }
+ }
+
+ bus_wake(bus);
+
+ /* Make sure backplane clock is on */
+ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, true);
+ if (bus->clkstate == CLK_PENDING)
+ goto clkwait;
+
+ /* Pending interrupt indicates new device status */
+ if (bus->ipend) {
+ bus->ipend = false;
+ r_sdreg32(bus, &newstatus,
+ offsetof(struct sdpcmd_regs, intstatus), &retries);
+ bus->f1regdata++;
+ if (brcmf_sdcard_regfail(bus->sdiodev))
+ newstatus = 0;
+ newstatus &= bus->hostintmask;
+ bus->fcstate = !!(newstatus & I_HMB_FC_STATE);
+ if (newstatus) {
+ w_sdreg32(bus, newstatus,
+ offsetof(struct sdpcmd_regs, intstatus),
+ &retries);
+ bus->f1regdata++;
+ }
+ }
+
+ /* Merge new bits with previous */
+ intstatus |= newstatus;
+ bus->intstatus = 0;
+
+ /* Handle flow-control change: read new state in case our ack
+ * crossed another change interrupt. If change still set, assume
+ * FC ON for safety, let next loop through do the debounce.
+ */
+ if (intstatus & I_HMB_FC_CHANGE) {
+ intstatus &= ~I_HMB_FC_CHANGE;
+ w_sdreg32(bus, I_HMB_FC_CHANGE,
+ offsetof(struct sdpcmd_regs, intstatus), &retries);
+
+ r_sdreg32(bus, &newstatus,
+ offsetof(struct sdpcmd_regs, intstatus), &retries);
+ bus->f1regdata += 2;
+ bus->fcstate =
+ !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
+ intstatus |= (newstatus & bus->hostintmask);
+ }
+
+ /* Handle host mailbox indication */
+ if (intstatus & I_HMB_HOST_INT) {
+ intstatus &= ~I_HMB_HOST_INT;
+ intstatus |= brcmf_sdbrcm_hostmail(bus);
+ }
+
+ /* Generally don't ask for these, can get CRC errors... */
+ if (intstatus & I_WR_OOSYNC) {
+ brcmf_dbg(ERROR, "Dongle reports WR_OOSYNC\n");
+ intstatus &= ~I_WR_OOSYNC;
+ }
+
+ if (intstatus & I_RD_OOSYNC) {
+ brcmf_dbg(ERROR, "Dongle reports RD_OOSYNC\n");
+ intstatus &= ~I_RD_OOSYNC;
+ }
+
+ if (intstatus & I_SBINT) {
+ brcmf_dbg(ERROR, "Dongle reports SBINT\n");
+ intstatus &= ~I_SBINT;
+ }
+
+ /* Would be active due to wake-wlan in gSPI */
+ if (intstatus & I_CHIPACTIVE) {
+ brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n");
+ intstatus &= ~I_CHIPACTIVE;
+ }
+
+ /* Ignore frame indications if rxskip is set */
+ if (bus->rxskip)
+ intstatus &= ~I_HMB_FRAME_IND;
+
+ /* On frame indication, read available frames */
+ if (PKT_AVAILABLE()) {
+ framecnt = brcmf_sdbrcm_readframes(bus, rxlimit, &rxdone);
+ if (rxdone || bus->rxskip)
+ intstatus &= ~I_HMB_FRAME_IND;
+ rxlimit -= min(framecnt, rxlimit);
+ }
+
+ /* Keep still-pending events for next scheduling */
+ bus->intstatus = intstatus;
+
+clkwait:
+ if (data_ok(bus) && bus->ctrl_frame_stat &&
+ (bus->clkstate == CLK_AVAIL)) {
+ int ret, i;
+
+ ret = brcmf_sdbrcm_send_buf(bus, bus->sdiodev->sbwad,
+ SDIO_FUNC_2, F2SYNC, (u8 *) bus->ctrl_frame_buf,
+ (u32) bus->ctrl_frame_len, NULL);
+
+ if (ret < 0) {
+ /* On failure, abort the command and
+ terminate the frame */
+ brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
+ ret);
+ bus->tx_sderrs++;
+
+ brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
+
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
+ NULL);
+ bus->f1regdata++;
+
+ for (i = 0; i < 3; i++) {
+ u8 hi, lo;
+ hi = brcmf_sdcard_cfg_read(bus->sdiodev,
+ SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCHI,
+ NULL);
+ lo = brcmf_sdcard_cfg_read(bus->sdiodev,
+ SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCLO,
+ NULL);
+ bus->f1regdata += 2;
+ if ((hi == 0) && (lo == 0))
+ break;
+ }
+
+ }
+ if (ret == 0)
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+
+ brcmf_dbg(INFO, "Return_dpc value is : %d\n", ret);
+ bus->ctrl_frame_stat = false;
+ brcmf_sdbrcm_wait_event_wakeup(bus);
+ }
+ /* Send queued frames (limit 1 if rx may still be pending) */
+ else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate &&
+ brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit
+ && data_ok(bus)) {
+ framecnt = rxdone ? txlimit : min(txlimit, bus->txminmax);
+ framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt);
+ txlimit -= framecnt;
+ }
+
+ /* Resched if events or tx frames are pending,
+ else await next interrupt */
+ /* On failed register access, all bets are off:
+ no resched or interrupts */
+ if ((bus->drvr->busstate == BRCMF_BUS_DOWN) ||
+ brcmf_sdcard_regfail(bus->sdiodev)) {
+ brcmf_dbg(ERROR, "failed backplane access over SDIO, halting operation %d\n",
+ brcmf_sdcard_regfail(bus->sdiodev));
+ bus->drvr->busstate = BRCMF_BUS_DOWN;
+ bus->intstatus = 0;
+ } else if (bus->clkstate == CLK_PENDING) {
+ brcmf_dbg(INFO, "rescheduled due to CLK_PENDING awaiting I_CHIPACTIVE interrupt\n");
+ resched = true;
+ } else if (bus->intstatus || bus->ipend ||
+ (!bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)
+ && data_ok(bus)) || PKT_AVAILABLE()) {
+ resched = true;
+ }
+
+ bus->dpc_sched = resched;
+
+ /* If we're done for now, turn off clock request. */
+ if ((bus->clkstate != CLK_PENDING)
+ && bus->idletime == BRCMF_IDLE_IMMEDIATE) {
+ bus->activity = false;
+ brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+ }
+
+ up(&bus->sdsem);
+
+ return resched;
+}
+
+static int brcmf_sdbrcm_dpc_thread(void *data)
+{
+ struct brcmf_bus *bus = (struct brcmf_bus *) data;
+
+ allow_signal(SIGTERM);
+ /* Run until signal received */
+ while (1) {
+ if (kthread_should_stop())
+ break;
+ if (!wait_for_completion_interruptible(&bus->dpc_wait)) {
+ /* Call bus dpc unless it indicated down
+ (then clean stop) */
+ if (bus->drvr->busstate != BRCMF_BUS_DOWN) {
+ if (brcmf_sdbrcm_dpc(bus))
+ complete(&bus->dpc_wait);
+ } else {
+ /* after stopping the bus, exit thread */
+ brcmf_sdbrcm_bus_stop(bus);
+ bus->dpc_tsk = NULL;
+ break;
+ }
+ } else
+ break;
+ }
+ return 0;
+}
+
+int brcmf_sdbrcm_bus_txdata(struct brcmf_bus *bus, struct sk_buff *pkt)
+{
+ int ret = -EBADE;
+ uint datalen, prec;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ datalen = pkt->len;
+
+ /* Add space for the header */
+ skb_push(pkt, SDPCM_HDRLEN);
+ /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
+
+ prec = prio2prec((pkt->priority & PRIOMASK));
+
+ /* Check for existing queue, current flow-control,
+ pending event, or pending clock */
+ brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq));
+ bus->fcqueued++;
+
+ /* Priority based enq */
+ spin_lock_bh(&bus->txqlock);
+ if (brcmf_c_prec_enq(bus->drvr, &bus->txq, pkt, prec) == false) {
+ skb_pull(pkt, SDPCM_HDRLEN);
+ brcmf_txcomplete(bus->drvr, pkt, false);
+ brcmu_pkt_buf_free_skb(pkt);
+ brcmf_dbg(ERROR, "out of bus->txq !!!\n");
+ ret = -ENOSR;
+ } else {
+ ret = 0;
+ }
+ spin_unlock_bh(&bus->txqlock);
+
+ if (pktq_len(&bus->txq) >= TXHI)
+ brcmf_txflowcontrol(bus->drvr, 0, ON);
+
+#ifdef BCMDBG
+ if (pktq_plen(&bus->txq, prec) > qcount[prec])
+ qcount[prec] = pktq_plen(&bus->txq, prec);
+#endif
+ /* Schedule DPC if needed to send queued packet(s) */
+ if (!bus->dpc_sched) {
+ bus->dpc_sched = true;
+ if (bus->dpc_tsk)
+ complete(&bus->dpc_wait);
+ }
+
+ return ret;
+}
+
+static int
+brcmf_sdbrcm_membytes(struct brcmf_bus *bus, bool write, u32 address, u8 *data,
+ uint size)
+{
+ int bcmerror = 0;
+ u32 sdaddr;
+ uint dsize;
+
+ /* Determine initial transfer parameters */
+ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
+ if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
+ dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
+ else
+ dsize = size;
+
+ /* Set the backplane window to include the start address */
+ bcmerror = brcmf_sdcard_set_sbaddr_window(bus->sdiodev, address);
+ if (bcmerror) {
+ brcmf_dbg(ERROR, "window change failed\n");
+ goto xfer_done;
+ }
+
+ /* Do the transfer(s) */
+ while (size) {
+ brcmf_dbg(INFO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
+ write ? "write" : "read", dsize,
+ sdaddr, address & SBSDIO_SBWINDOW_MASK);
+ bcmerror = brcmf_sdcard_rwdata(bus->sdiodev, write,
+ sdaddr, data, dsize);
+ if (bcmerror) {
+ brcmf_dbg(ERROR, "membytes transfer failed\n");
+ break;
+ }
+
+ /* Adjust for next transfer (if any) */
+ size -= dsize;
+ if (size) {
+ data += dsize;
+ address += dsize;
+ bcmerror = brcmf_sdcard_set_sbaddr_window(bus->sdiodev,
+ address);
+ if (bcmerror) {
+ brcmf_dbg(ERROR, "window change failed\n");
+ break;
+ }
+ sdaddr = 0;
+ dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
+ }
+ }
+
+xfer_done:
+ /* Return the window to backplane enumeration space for core access */
+ if (brcmf_sdcard_set_sbaddr_window(bus->sdiodev, bus->sdiodev->sbwad))
+ brcmf_dbg(ERROR, "FAILED to set window back to 0x%x\n",
+ bus->sdiodev->sbwad);
+
+ return bcmerror;
+}
+
+#ifdef BCMDBG
+#define CONSOLE_LINE_MAX 192
+
+static int brcmf_sdbrcm_readconsole(struct brcmf_bus *bus)
+{
+ struct brcmf_console *c = &bus->console;
+ u8 line[CONSOLE_LINE_MAX], ch;
+ u32 n, idx, addr;
+ int rv;
+
+ /* Don't do anything until FWREADY updates console address */
+ if (bus->console_addr == 0)
+ return 0;
+
+ /* Read console log struct */
+ addr = bus->console_addr + offsetof(struct rte_console, log_le);
+ rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&c->log_le,
+ sizeof(c->log_le));
+ if (rv < 0)
+ return rv;
+
+ /* Allocate console buffer (one time only) */
+ if (c->buf == NULL) {
+ c->bufsize = le32_to_cpu(c->log_le.buf_size);
+ c->buf = kmalloc(c->bufsize, GFP_ATOMIC);
+ if (c->buf == NULL)
+ return -ENOMEM;
+ }
+
+ idx = le32_to_cpu(c->log_le.idx);
+
+ /* Protect against corrupt value */
+ if (idx > c->bufsize)
+ return -EBADE;
+
+ /* Skip reading the console buffer if the index pointer
+ has not moved */
+ if (idx == c->last)
+ return 0;
+
+ /* Read the console buffer */
+ addr = le32_to_cpu(c->log_le.buf);
+ rv = brcmf_sdbrcm_membytes(bus, false, addr, c->buf, c->bufsize);
+ if (rv < 0)
+ return rv;
+
+ while (c->last != idx) {
+ for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
+ if (c->last == idx) {
+ /* This would output a partial line.
+ * Instead, back up
+ * the buffer pointer and output this
+ * line next time around.
+ */
+ if (c->last >= n)
+ c->last -= n;
+ else
+ c->last = c->bufsize - n;
+ goto break2;
+ }
+ ch = c->buf[c->last];
+ c->last = (c->last + 1) % c->bufsize;
+ if (ch == '\n')
+ break;
+ line[n] = ch;
+ }
+
+ if (n > 0) {
+ if (line[n - 1] == '\r')
+ n--;
+ line[n] = 0;
+ printk(KERN_DEBUG "CONSOLE: %s\n", line);
+ }
+ }
+break2:
+
+ return 0;
+}
+#endif /* BCMDBG */
+
+static int brcmf_tx_frame(struct brcmf_bus *bus, u8 *frame, u16 len)
+{
+ int i;
+ int ret;
+
+ bus->ctrl_frame_stat = false;
+ ret = brcmf_sdbrcm_send_buf(bus, bus->sdiodev->sbwad,
+ SDIO_FUNC_2, F2SYNC, frame, len, NULL);
+
+ if (ret < 0) {
+ /* On failure, abort the command and terminate the frame */
+ brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
+ ret);
+ bus->tx_sderrs++;
+
+ brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
+
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_FRAMECTRL,
+ SFC_WF_TERM, NULL);
+ bus->f1regdata++;
+
+ for (i = 0; i < 3; i++) {
+ u8 hi, lo;
+ hi = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCHI,
+ NULL);
+ lo = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCLO,
+ NULL);
+ bus->f1regdata += 2;
+ if (hi == 0 && lo == 0)
+ break;
+ }
+ return ret;
+ }
+
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+
+ return ret;
+}
+
+int
+brcmf_sdbrcm_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint msglen)
+{
+ u8 *frame;
+ u16 len;
+ u32 swheader;
+ uint retries = 0;
+ u8 doff = 0;
+ int ret = -1;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Back the pointer to make a room for bus header */
+ frame = msg - SDPCM_HDRLEN;
+ len = (msglen += SDPCM_HDRLEN);
+
+ /* Add alignment padding (optional for ctl frames) */
+ doff = ((unsigned long)frame % BRCMF_SDALIGN);
+ if (doff) {
+ frame -= doff;
+ len += doff;
+ msglen += doff;
+ memset(frame, 0, doff + SDPCM_HDRLEN);
+ }
+ /* precondition: doff < BRCMF_SDALIGN */
+ doff += SDPCM_HDRLEN;
+
+ /* Round send length to next SDIO block */
+ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+ u16 pad = bus->blocksize - (len % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize))
+ len += pad;
+ } else if (len % BRCMF_SDALIGN) {
+ len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN);
+ }
+
+ /* Satisfy length-alignment requirements */
+ if (len & (ALIGNMENT - 1))
+ len = roundup(len, ALIGNMENT);
+
+ /* precondition: IS_ALIGNED((unsigned long)frame, 2) */
+
+ /* Need to lock here to protect txseq and SDIO tx calls */
+ down(&bus->sdsem);
+
+ bus_wake(bus);
+
+ /* Make sure backplane clock is on */
+ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+ /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
+ *(__le16 *) frame = cpu_to_le16((u16) msglen);
+ *(((__le16 *) frame) + 1) = cpu_to_le16(~msglen);
+
+ /* Software tag: channel, sequence number, data offset */
+ swheader =
+ ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) &
+ SDPCM_CHANNEL_MASK)
+ | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) &
+ SDPCM_DOFFSET_MASK);
+ put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
+ put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+
+ if (!data_ok(bus)) {
+ brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n",
+ bus->tx_max, bus->tx_seq);
+ bus->ctrl_frame_stat = true;
+ /* Send from dpc */
+ bus->ctrl_frame_buf = frame;
+ bus->ctrl_frame_len = len;
+
+ brcmf_sdbrcm_wait_for_event(bus, &bus->ctrl_frame_stat);
+
+ if (bus->ctrl_frame_stat == false) {
+ brcmf_dbg(INFO, "ctrl_frame_stat == false\n");
+ ret = 0;
+ } else {
+ brcmf_dbg(INFO, "ctrl_frame_stat == true\n");
+ ret = -1;
+ }
+ }
+
+ if (ret == -1) {
+#ifdef BCMDBG
+ if (BRCMF_BYTES_ON() && BRCMF_CTL_ON()) {
+ printk(KERN_DEBUG "Tx Frame:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ frame, len);
+ } else if (BRCMF_HDRS_ON()) {
+ printk(KERN_DEBUG "TxHdr:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ frame, min_t(u16, len, 16));
+ }
+#endif
+
+ do {
+ ret = brcmf_tx_frame(bus, frame, len);
+ } while (ret < 0 && retries++ < TXRETRIES);
+ }
+
+ if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+ bus->activity = false;
+ brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
+ }
+
+ up(&bus->sdsem);
+
+ if (ret)
+ bus->drvr->tx_ctlerrs++;
+ else
+ bus->drvr->tx_ctlpkts++;
+
+ return ret ? -EIO : 0;
+}
+
+int
+brcmf_sdbrcm_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint msglen)
+{
+ int timeleft;
+ uint rxlen = 0;
+ bool pending;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Wait until control frame is available */
+ timeleft = brcmf_sdbrcm_dcmd_resp_wait(bus, &bus->rxlen, &pending);
+
+ down(&bus->sdsem);
+ rxlen = bus->rxlen;
+ memcpy(msg, bus->rxctl, min(msglen, rxlen));
+ bus->rxlen = 0;
+ up(&bus->sdsem);
+
+ if (rxlen) {
+ brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n",
+ rxlen, msglen);
+ } else if (timeleft == 0) {
+ brcmf_dbg(ERROR, "resumed on timeout\n");
+ } else if (pending == true) {
+ brcmf_dbg(CTL, "cancelled\n");
+ return -ERESTARTSYS;
+ } else {
+ brcmf_dbg(CTL, "resumed for unknown reason?\n");
+ }
+
+ if (rxlen)
+ bus->drvr->rx_ctlpkts++;
+ else
+ bus->drvr->rx_ctlerrs++;
+
+ return rxlen ? (int)rxlen : -ETIMEDOUT;
+}
+
+static int brcmf_sdbrcm_downloadvars(struct brcmf_bus *bus, void *arg, int len)
+{
+ int bcmerror = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Basic sanity checks */
+ if (bus->drvr->up) {
+ bcmerror = -EISCONN;
+ goto err;
+ }
+ if (!len) {
+ bcmerror = -EOVERFLOW;
+ goto err;
+ }
+
+ /* Free the old ones and replace with passed variables */
+ kfree(bus->vars);
+
+ bus->vars = kmalloc(len, GFP_ATOMIC);
+ bus->varsz = bus->vars ? len : 0;
+ if (bus->vars == NULL) {
+ bcmerror = -ENOMEM;
+ goto err;
+ }
+
+ /* Copy the passed variables, which should include the
+ terminating double-null */
+ memcpy(bus->vars, arg, bus->varsz);
+err:
+ return bcmerror;
+}
+
+static int brcmf_sdbrcm_write_vars(struct brcmf_bus *bus)
+{
+ int bcmerror = 0;
+ u32 varsize;
+ u32 varaddr;
+ u8 *vbuffer;
+ u32 varsizew;
+ __le32 varsizew_le;
+#ifdef BCMDBG
+ char *nvram_ularray;
+#endif /* BCMDBG */
+
+ /* Even if there are no vars are to be written, we still
+ need to set the ramsize. */
+ varsize = bus->varsz ? roundup(bus->varsz, 4) : 0;
+ varaddr = (bus->ramsize - 4) - varsize;
+
+ if (bus->vars) {
+ vbuffer = kzalloc(varsize, GFP_ATOMIC);
+ if (!vbuffer)
+ return -ENOMEM;
+
+ memcpy(vbuffer, bus->vars, bus->varsz);
+
+ /* Write the vars list */
+ bcmerror =
+ brcmf_sdbrcm_membytes(bus, true, varaddr, vbuffer, varsize);
+#ifdef BCMDBG
+ /* Verify NVRAM bytes */
+ brcmf_dbg(INFO, "Compare NVRAM dl & ul; varsize=%d\n", varsize);
+ nvram_ularray = kmalloc(varsize, GFP_ATOMIC);
+ if (!nvram_ularray)
+ return -ENOMEM;
+
+ /* Upload image to verify downloaded contents. */
+ memset(nvram_ularray, 0xaa, varsize);
+
+ /* Read the vars list to temp buffer for comparison */
+ bcmerror =
+ brcmf_sdbrcm_membytes(bus, false, varaddr, nvram_ularray,
+ varsize);
+ if (bcmerror) {
+ brcmf_dbg(ERROR, "error %d on reading %d nvram bytes at 0x%08x\n",
+ bcmerror, varsize, varaddr);
+ }
+ /* Compare the org NVRAM with the one read from RAM */
+ if (memcmp(vbuffer, nvram_ularray, varsize))
+ brcmf_dbg(ERROR, "Downloaded NVRAM image is corrupted\n");
+ else
+ brcmf_dbg(ERROR, "Download/Upload/Compare of NVRAM ok\n");
+
+ kfree(nvram_ularray);
+#endif /* BCMDBG */
+
+ kfree(vbuffer);
+ }
+
+ /* adjust to the user specified RAM */
+ brcmf_dbg(INFO, "Physical memory size: %d\n", bus->ramsize);
+ brcmf_dbg(INFO, "Vars are at %d, orig varsize is %d\n",
+ varaddr, varsize);
+ varsize = ((bus->ramsize - 4) - varaddr);
+
+ /*
+ * Determine the length token:
+ * Varsize, converted to words, in lower 16-bits, checksum
+ * in upper 16-bits.
+ */
+ if (bcmerror) {
+ varsizew = 0;
+ varsizew_le = cpu_to_le32(0);
+ } else {
+ varsizew = varsize / 4;
+ varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF);
+ varsizew_le = cpu_to_le32(varsizew);
+ }
+
+ brcmf_dbg(INFO, "New varsize is %d, length token=0x%08x\n",
+ varsize, varsizew);
+
+ /* Write the length token to the last word */
+ bcmerror = brcmf_sdbrcm_membytes(bus, true, (bus->ramsize - 4),
+ (u8 *)&varsizew_le, 4);
+
+ return bcmerror;
+}
+
+static void
+brcmf_sdbrcm_chip_disablecore(struct brcmf_sdio_dev *sdiodev, u32 corebase)
+{
+ u32 regdata;
+
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbtmstatelow), 4);
+ if (regdata & SBTML_RESET)
+ return;
+
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbtmstatelow), 4);
+ if ((regdata & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) != 0) {
+ /*
+ * set target reject and spin until busy is clear
+ * (preserve core-specific bits)
+ */
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbtmstatelow), 4);
+ brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow),
+ 4, regdata | SBTML_REJ);
+
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbtmstatelow), 4);
+ udelay(1);
+ SPINWAIT((brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbtmstatehigh), 4) &
+ SBTMH_BUSY), 100000);
+
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbtmstatehigh), 4);
+ if (regdata & SBTMH_BUSY)
+ brcmf_dbg(ERROR, "ARM core still busy\n");
+
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbidlow), 4);
+ if (regdata & SBIDL_INIT) {
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbimstate), 4) |
+ SBIM_RJ;
+ brcmf_sdcard_reg_write(sdiodev,
+ CORE_SB(corebase, sbimstate), 4,
+ regdata);
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbimstate), 4);
+ udelay(1);
+ SPINWAIT((brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbimstate), 4) &
+ SBIM_BY), 100000);
+ }
+
+ /* set reset and reject while enabling the clocks */
+ brcmf_sdcard_reg_write(sdiodev,
+ CORE_SB(corebase, sbtmstatelow), 4,
+ (((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
+ SBTML_REJ | SBTML_RESET));
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbtmstatelow), 4);
+ udelay(10);
+
+ /* clear the initiator reject bit */
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbidlow), 4);
+ if (regdata & SBIDL_INIT) {
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbimstate), 4) &
+ ~SBIM_RJ;
+ brcmf_sdcard_reg_write(sdiodev,
+ CORE_SB(corebase, sbimstate), 4,
+ regdata);
+ }
+ }
+
+ /* leave reset and reject asserted */
+ brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4,
+ (SBTML_REJ | SBTML_RESET));
+ udelay(1);
+}
+
+static void
+brcmf_sdbrcm_chip_resetcore(struct brcmf_sdio_dev *sdiodev, u32 corebase)
+{
+ u32 regdata;
+
+ /*
+ * Must do the disable sequence first to work for
+ * arbitrary current core state.
+ */
+ brcmf_sdbrcm_chip_disablecore(sdiodev, corebase);
+
+ /*
+ * Now do the initialization sequence.
+ * set reset while enabling the clock and
+ * forcing them on throughout the core
+ */
+ brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4,
+ ((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
+ SBTML_RESET);
+ udelay(1);
+
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbtmstatehigh), 4);
+ if (regdata & SBTMH_SERR)
+ brcmf_sdcard_reg_write(sdiodev,
+ CORE_SB(corebase, sbtmstatehigh), 4, 0);
+
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(corebase, sbimstate), 4);
+ if (regdata & (SBIM_IBE | SBIM_TO))
+ brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbimstate), 4,
+ regdata & ~(SBIM_IBE | SBIM_TO));
+
+ /* clear reset and allow it to propagate throughout the core */
+ brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4,
+ (SICF_FGC << SBTML_SICF_SHIFT) |
+ (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
+ udelay(1);
+
+ /* leave clock enabled */
+ brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4,
+ (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
+ udelay(1);
+}
+
+static int brcmf_sdbrcm_download_state(struct brcmf_bus *bus, bool enter)
+{
+ uint retries;
+ u32 regdata;
+ int bcmerror = 0;
+
+ /* To enter download state, disable ARM and reset SOCRAM.
+ * To exit download state, simply reset ARM (default is RAM boot).
+ */
+ if (enter) {
+ bus->alp_only = true;
+
+ brcmf_sdbrcm_chip_disablecore(bus->sdiodev,
+ bus->ci->armcorebase);
+
+ brcmf_sdbrcm_chip_resetcore(bus->sdiodev, bus->ci->ramcorebase);
+
+ /* Clear the top bit of memory */
+ if (bus->ramsize) {
+ u32 zeros = 0;
+ brcmf_sdbrcm_membytes(bus, true, bus->ramsize - 4,
+ (u8 *)&zeros, 4);
+ }
+ } else {
+ regdata = brcmf_sdcard_reg_read(bus->sdiodev,
+ CORE_SB(bus->ci->ramcorebase, sbtmstatelow), 4);
+ regdata &= (SBTML_RESET | SBTML_REJ_MASK |
+ (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
+ if ((SICF_CLOCK_EN << SBTML_SICF_SHIFT) != regdata) {
+ brcmf_dbg(ERROR, "SOCRAM core is down after reset?\n");
+ bcmerror = -EBADE;
+ goto fail;
+ }
+
+ bcmerror = brcmf_sdbrcm_write_vars(bus);
+ if (bcmerror) {
+ brcmf_dbg(ERROR, "no vars written to RAM\n");
+ bcmerror = 0;
+ }
+
+ w_sdreg32(bus, 0xFFFFFFFF,
+ offsetof(struct sdpcmd_regs, intstatus), &retries);
+
+ brcmf_sdbrcm_chip_resetcore(bus->sdiodev, bus->ci->armcorebase);
+
+ /* Allow HT Clock now that the ARM is running. */
+ bus->alp_only = false;
+
+ bus->drvr->busstate = BRCMF_BUS_LOAD;
+ }
+fail:
+ return bcmerror;
+}
+
+static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_bus *bus)
+{
+ if (bus->firmware->size < bus->fw_ptr + len)
+ len = bus->firmware->size - bus->fw_ptr;
+
+ memcpy(buf, &bus->firmware->data[bus->fw_ptr], len);
+ bus->fw_ptr += len;
+ return len;
+}
+
+MODULE_FIRMWARE(BCM4329_FW_NAME);
+MODULE_FIRMWARE(BCM4329_NV_NAME);
+
+static int brcmf_sdbrcm_download_code_file(struct brcmf_bus *bus)
+{
+ int offset = 0;
+ uint len;
+ u8 *memblock = NULL, *memptr;
+ int ret;
+
+ brcmf_dbg(INFO, "Enter\n");
+
+ bus->fw_name = BCM4329_FW_NAME;
+ ret = request_firmware(&bus->firmware, bus->fw_name,
+ &bus->sdiodev->func[2]->dev);
+ if (ret) {
+ brcmf_dbg(ERROR, "Fail to request firmware %d\n", ret);
+ return ret;
+ }
+ bus->fw_ptr = 0;
+
+ memptr = memblock = kmalloc(MEMBLOCK + BRCMF_SDALIGN, GFP_ATOMIC);
+ if (memblock == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ if ((u32)(unsigned long)memblock % BRCMF_SDALIGN)
+ memptr += (BRCMF_SDALIGN -
+ ((u32)(unsigned long)memblock % BRCMF_SDALIGN));
+
+ /* Download image */
+ while ((len =
+ brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus))) {
+ ret = brcmf_sdbrcm_membytes(bus, true, offset, memptr, len);
+ if (ret) {
+ brcmf_dbg(ERROR, "error %d on writing %d membytes at 0x%08x\n",
+ ret, MEMBLOCK, offset);
+ goto err;
+ }
+
+ offset += MEMBLOCK;
+ }
+
+err:
+ kfree(memblock);
+
+ release_firmware(bus->firmware);
+ bus->fw_ptr = 0;
+
+ return ret;
+}
+
+/*
+ * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file
+ * and ending in a NUL.
+ * Removes carriage returns, empty lines, comment lines, and converts
+ * newlines to NULs.
+ * Shortens buffer as needed and pads with NULs. End of buffer is marked
+ * by two NULs.
+*/
+
+static uint brcmf_process_nvram_vars(char *varbuf, uint len)
+{
+ char *dp;
+ bool findNewline;
+ int column;
+ uint buf_len, n;
+
+ dp = varbuf;
+
+ findNewline = false;
+ column = 0;
+
+ for (n = 0; n < len; n++) {
+ if (varbuf[n] == 0)
+ break;
+ if (varbuf[n] == '\r')
+ continue;
+ if (findNewline && varbuf[n] != '\n')
+ continue;
+ findNewline = false;
+ if (varbuf[n] == '#') {
+ findNewline = true;
+ continue;
+ }
+ if (varbuf[n] == '\n') {
+ if (column == 0)
+ continue;
+ *dp++ = 0;
+ column = 0;
+ continue;
+ }
+ *dp++ = varbuf[n];
+ column++;
+ }
+ buf_len = dp - varbuf;
+
+ while (dp < varbuf + n)
+ *dp++ = 0;
+
+ return buf_len;
+}
+
+static int brcmf_sdbrcm_download_nvram(struct brcmf_bus *bus)
+{
+ uint len;
+ char *memblock = NULL;
+ char *bufp;
+ int ret;
+
+ bus->nv_name = BCM4329_NV_NAME;
+ ret = request_firmware(&bus->firmware, bus->nv_name,
+ &bus->sdiodev->func[2]->dev);
+ if (ret) {
+ brcmf_dbg(ERROR, "Fail to request nvram %d\n", ret);
+ return ret;
+ }
+ bus->fw_ptr = 0;
+
+ memblock = kmalloc(MEMBLOCK, GFP_ATOMIC);
+ if (memblock == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ len = brcmf_sdbrcm_get_image(memblock, MEMBLOCK, bus);
+
+ if (len > 0 && len < MEMBLOCK) {
+ bufp = (char *)memblock;
+ bufp[len] = 0;
+ len = brcmf_process_nvram_vars(bufp, len);
+ bufp += len;
+ *bufp++ = 0;
+ if (len)
+ ret = brcmf_sdbrcm_downloadvars(bus, memblock, len + 1);
+ if (ret)
+ brcmf_dbg(ERROR, "error downloading vars: %d\n", ret);
+ } else {
+ brcmf_dbg(ERROR, "error reading nvram file: %d\n", len);
+ ret = -EIO;
+ }
+
+err:
+ kfree(memblock);
+
+ release_firmware(bus->firmware);
+ bus->fw_ptr = 0;
+
+ return ret;
+}
+
+static int _brcmf_sdbrcm_download_firmware(struct brcmf_bus *bus)
+{
+ int bcmerror = -1;
+
+ /* Keep arm in reset */
+ if (brcmf_sdbrcm_download_state(bus, true)) {
+ brcmf_dbg(ERROR, "error placing ARM core in reset\n");
+ goto err;
+ }
+
+ /* External image takes precedence if specified */
+ if (brcmf_sdbrcm_download_code_file(bus)) {
+ brcmf_dbg(ERROR, "dongle image file download failed\n");
+ goto err;
+ }
+
+ /* External nvram takes precedence if specified */
+ if (brcmf_sdbrcm_download_nvram(bus))
+ brcmf_dbg(ERROR, "dongle nvram file download failed\n");
+
+ /* Take arm out of reset */
+ if (brcmf_sdbrcm_download_state(bus, false)) {
+ brcmf_dbg(ERROR, "error getting out of ARM core reset\n");
+ goto err;
+ }
+
+ bcmerror = 0;
+
+err:
+ return bcmerror;
+}
+
+static bool
+brcmf_sdbrcm_download_firmware(struct brcmf_bus *bus)
+{
+ bool ret;
+
+ /* Download the firmware */
+ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+ ret = _brcmf_sdbrcm_download_firmware(bus) == 0;
+
+ brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
+
+ return ret;
+}
+
+void brcmf_sdbrcm_bus_stop(struct brcmf_bus *bus)
+{
+ u32 local_hostintmask;
+ u8 saveclk;
+ uint retries;
+ int err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus->watchdog_tsk) {
+ send_sig(SIGTERM, bus->watchdog_tsk, 1);
+ kthread_stop(bus->watchdog_tsk);
+ bus->watchdog_tsk = NULL;
+ }
+
+ if (bus->dpc_tsk && bus->dpc_tsk != current) {
+ send_sig(SIGTERM, bus->dpc_tsk, 1);
+ kthread_stop(bus->dpc_tsk);
+ bus->dpc_tsk = NULL;
+ }
+
+ down(&bus->sdsem);
+
+ bus_wake(bus);
+
+ /* Enable clock for device interrupts */
+ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+ /* Disable and clear interrupts at the chip level also */
+ w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask), &retries);
+ local_hostintmask = bus->hostintmask;
+ bus->hostintmask = 0;
+
+ /* Change our idea of bus state */
+ bus->drvr->busstate = BRCMF_BUS_DOWN;
+
+ /* Force clocks on backplane to be sure F2 interrupt propagates */
+ saveclk = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (!err) {
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_FORCE_HT), &err);
+ }
+ if (err)
+ brcmf_dbg(ERROR, "Failed to force clock for F2: err %d\n", err);
+
+ /* Turn off the bus (F2), free any pending packets */
+ brcmf_dbg(INTR, "disable SDIO interrupts\n");
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_0, SDIO_CCCR_IOEx,
+ SDIO_FUNC_ENABLE_1, NULL);
+
+ /* Clear any pending interrupts now that F2 is disabled */
+ w_sdreg32(bus, local_hostintmask,
+ offsetof(struct sdpcmd_regs, intstatus), &retries);
+
+ /* Turn off the backplane clock (only) */
+ brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
+
+ /* Clear the data packet queues */
+ brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
+
+ /* Clear any held glomming stuff */
+ if (bus->glomd)
+ brcmu_pkt_buf_free_skb(bus->glomd);
+
+ if (bus->glom)
+ brcmu_pkt_buf_free_skb(bus->glom);
+
+ bus->glom = bus->glomd = NULL;
+
+ /* Clear rx control and wake any waiters */
+ bus->rxlen = 0;
+ brcmf_sdbrcm_dcmd_resp_wake(bus);
+
+ /* Reset some F2 state stuff */
+ bus->rxskip = false;
+ bus->tx_seq = bus->rx_seq = 0;
+
+ up(&bus->sdsem);
+}
+
+int brcmf_sdbrcm_bus_init(struct brcmf_pub *drvr)
+{
+ struct brcmf_bus *bus = drvr->bus;
+ unsigned long timeout;
+ uint retries = 0;
+ u8 ready, enable;
+ int err, ret = 0;
+ u8 saveclk;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* try to download image and nvram to the dongle */
+ if (drvr->busstate == BRCMF_BUS_DOWN) {
+ if (!(brcmf_sdbrcm_download_firmware(bus)))
+ return -1;
+ }
+
+ if (!bus->drvr)
+ return 0;
+
+ /* Start the watchdog timer */
+ bus->drvr->tickcnt = 0;
+ brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
+
+ down(&bus->sdsem);
+
+ /* Make sure backplane clock is on, needed to generate F2 interrupt */
+ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+ if (bus->clkstate != CLK_AVAIL)
+ goto exit;
+
+ /* Force clocks on backplane to be sure F2 interrupt propagates */
+ saveclk =
+ brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (!err) {
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_FORCE_HT), &err);
+ }
+ if (err) {
+ brcmf_dbg(ERROR, "Failed to force clock for F2: err %d\n", err);
+ goto exit;
+ }
+
+ /* Enable function 2 (frame transfers) */
+ w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
+ offsetof(struct sdpcmd_regs, tosbmailboxdata), &retries);
+ enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2);
+
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_0, SDIO_CCCR_IOEx,
+ enable, NULL);
+
+ timeout = jiffies + msecs_to_jiffies(BRCMF_WAIT_F2RDY);
+ ready = 0;
+ while (enable != ready) {
+ ready = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_0,
+ SDIO_CCCR_IORx, NULL);
+ if (time_after(jiffies, timeout))
+ break;
+ else if (time_after(jiffies, timeout - BRCMF_WAIT_F2RDY + 50))
+ /* prevent busy waiting if it takes too long */
+ msleep_interruptible(20);
+ }
+
+ brcmf_dbg(INFO, "enable 0x%02x, ready 0x%02x\n", enable, ready);
+
+ /* If F2 successfully enabled, set core and enable interrupts */
+ if (ready == enable) {
+ /* Set up the interrupt mask and enable interrupts */
+ bus->hostintmask = HOSTINTMASK;
+ w_sdreg32(bus, bus->hostintmask,
+ offsetof(struct sdpcmd_regs, hostintmask), &retries);
+
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_WATERMARK, 8, &err);
+
+ /* Set bus state according to enable result */
+ drvr->busstate = BRCMF_BUS_DATA;
+ }
+
+ else {
+ /* Disable F2 again */
+ enable = SDIO_FUNC_ENABLE_1;
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_0,
+ SDIO_CCCR_IOEx, enable, NULL);
+ }
+
+ /* Restore previous clock setting */
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err);
+
+ /* If we didn't come up, turn off backplane clock */
+ if (drvr->busstate != BRCMF_BUS_DATA)
+ brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+
+exit:
+ up(&bus->sdsem);
+
+ return ret;
+}
+
+void brcmf_sdbrcm_isr(void *arg)
+{
+ struct brcmf_bus *bus = (struct brcmf_bus *) arg;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (!bus) {
+ brcmf_dbg(ERROR, "bus is null pointer, exiting\n");
+ return;
+ }
+
+ if (bus->drvr->busstate == BRCMF_BUS_DOWN) {
+ brcmf_dbg(ERROR, "bus is down. we have nothing to do\n");
+ return;
+ }
+ /* Count the interrupt call */
+ bus->intrcount++;
+ bus->ipend = true;
+
+ /* Shouldn't get this interrupt if we're sleeping? */
+ if (bus->sleeping) {
+ brcmf_dbg(ERROR, "INTERRUPT WHILE SLEEPING??\n");
+ return;
+ }
+
+ /* Disable additional interrupts (is this needed now)? */
+ if (!bus->intr)
+ brcmf_dbg(ERROR, "isr w/o interrupt configured!\n");
+
+ bus->dpc_sched = true;
+ if (bus->dpc_tsk)
+ complete(&bus->dpc_wait);
+}
+
+static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_pub *drvr)
+{
+ struct brcmf_bus *bus;
+
+ brcmf_dbg(TIMER, "Enter\n");
+
+ bus = drvr->bus;
+
+ /* Ignore the timer if simulating bus down */
+ if (bus->sleeping)
+ return false;
+
+ down(&bus->sdsem);
+
+ /* Poll period: check device if appropriate. */
+ if (bus->poll && (++bus->polltick >= bus->pollrate)) {
+ u32 intstatus = 0;
+
+ /* Reset poll tick */
+ bus->polltick = 0;
+
+ /* Check device if no interrupts */
+ if (!bus->intr || (bus->intrcount == bus->lastintrs)) {
+
+ if (!bus->dpc_sched) {
+ u8 devpend;
+ devpend = brcmf_sdcard_cfg_read(bus->sdiodev,
+ SDIO_FUNC_0, SDIO_CCCR_INTx,
+ NULL);
+ intstatus =
+ devpend & (INTR_STATUS_FUNC1 |
+ INTR_STATUS_FUNC2);
+ }
+
+ /* If there is something, make like the ISR and
+ schedule the DPC */
+ if (intstatus) {
+ bus->pollcnt++;
+ bus->ipend = true;
+
+ bus->dpc_sched = true;
+ if (bus->dpc_tsk)
+ complete(&bus->dpc_wait);
+ }
+ }
+
+ /* Update interrupt tracking */
+ bus->lastintrs = bus->intrcount;
+ }
+#ifdef BCMDBG
+ /* Poll for console output periodically */
+ if (drvr->busstate == BRCMF_BUS_DATA && bus->console_interval != 0) {
+ bus->console.count += BRCMF_WD_POLL_MS;
+ if (bus->console.count >= bus->console_interval) {
+ bus->console.count -= bus->console_interval;
+ /* Make sure backplane clock is on */
+ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+ if (brcmf_sdbrcm_readconsole(bus) < 0)
+ /* stop on error */
+ bus->console_interval = 0;
+ }
+ }
+#endif /* BCMDBG */
+
+ /* On idle timeout clear activity flag and/or turn off clock */
+ if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
+ if (++bus->idlecount >= bus->idletime) {
+ bus->idlecount = 0;
+ if (bus->activity) {
+ bus->activity = false;
+ brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
+ } else {
+ brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+ }
+ }
+ }
+
+ up(&bus->sdsem);
+
+ return bus->ipend;
+}
+
+static bool brcmf_sdbrcm_chipmatch(u16 chipid)
+{
+ if (chipid == BCM4329_CHIP_ID)
+ return true;
+ return false;
+}
+
+static void brcmf_sdbrcm_release_malloc(struct brcmf_bus *bus)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ kfree(bus->rxbuf);
+ bus->rxctl = bus->rxbuf = NULL;
+ bus->rxlen = 0;
+
+ kfree(bus->databuf);
+ bus->databuf = NULL;
+}
+
+static bool brcmf_sdbrcm_probe_malloc(struct brcmf_bus *bus)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus->drvr->maxctl) {
+ bus->rxblen =
+ roundup((bus->drvr->maxctl + SDPCM_HDRLEN),
+ ALIGNMENT) + BRCMF_SDALIGN;
+ bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
+ if (!(bus->rxbuf))
+ goto fail;
+ }
+
+ /* Allocate buffer to receive glomed packet */
+ bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC);
+ if (!(bus->databuf)) {
+ /* release rxbuf which was already located as above */
+ if (!bus->rxblen)
+ kfree(bus->rxbuf);
+ goto fail;
+ }
+
+ /* Align the buffer */
+ if ((unsigned long)bus->databuf % BRCMF_SDALIGN)
+ bus->dataptr = bus->databuf + (BRCMF_SDALIGN -
+ ((unsigned long)bus->databuf % BRCMF_SDALIGN));
+ else
+ bus->dataptr = bus->databuf;
+
+ return true;
+
+fail:
+ return false;
+}
+
+/* SDIO Pad drive strength to select value mappings */
+struct sdiod_drive_str {
+ u8 strength; /* Pad Drive Strength in mA */
+ u8 sel; /* Chip-specific select value */
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 1 */
+static const struct sdiod_drive_str sdiod_drive_strength_tab1[] = {
+ {
+ 4, 0x2}, {
+ 2, 0x3}, {
+ 1, 0x0}, {
+ 0, 0x0}
+ };
+
+/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */
+static const struct sdiod_drive_str sdiod_drive_strength_tab2[] = {
+ {
+ 12, 0x7}, {
+ 10, 0x6}, {
+ 8, 0x5}, {
+ 6, 0x4}, {
+ 4, 0x2}, {
+ 2, 0x1}, {
+ 0, 0x0}
+ };
+
+/* SDIO Drive Strength to sel value table for PMU Rev 8 (1.8V) */
+static const struct sdiod_drive_str sdiod_drive_strength_tab3[] = {
+ {
+ 32, 0x7}, {
+ 26, 0x6}, {
+ 22, 0x5}, {
+ 16, 0x4}, {
+ 12, 0x3}, {
+ 8, 0x2}, {
+ 4, 0x1}, {
+ 0, 0x0}
+ };
+
+#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
+
+static char *brcmf_chipname(uint chipid, char *buf, uint len)
+{
+ const char *fmt;
+
+ fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
+ snprintf(buf, len, fmt, chipid);
+ return buf;
+}
+
+static void brcmf_sdbrcm_sdiod_drive_strength_init(struct brcmf_bus *bus,
+ u32 drivestrength) {
+ struct sdiod_drive_str *str_tab = NULL;
+ u32 str_mask = 0;
+ u32 str_shift = 0;
+ char chn[8];
+
+ if (!(bus->ci->cccaps & CC_CAP_PMU))
+ return;
+
+ switch (SDIOD_DRVSTR_KEY(bus->ci->chip, bus->ci->pmurev)) {
+ case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1):
+ str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab1;
+ str_mask = 0x30000000;
+ str_shift = 28;
+ break;
+ case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2):
+ case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3):
+ str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab2;
+ str_mask = 0x00003800;
+ str_shift = 11;
+ break;
+ case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 8):
+ str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab3;
+ str_mask = 0x00003800;
+ str_shift = 11;
+ break;
+ default:
+ brcmf_dbg(ERROR, "No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
+ brcmf_chipname(bus->ci->chip, chn, 8),
+ bus->ci->chiprev, bus->ci->pmurev);
+ break;
+ }
+
+ if (str_tab != NULL) {
+ u32 drivestrength_sel = 0;
+ u32 cc_data_temp;
+ int i;
+
+ for (i = 0; str_tab[i].strength != 0; i++) {
+ if (drivestrength >= str_tab[i].strength) {
+ drivestrength_sel = str_tab[i].sel;
+ break;
+ }
+ }
+
+ brcmf_sdcard_reg_write(bus->sdiodev,
+ CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr),
+ 4, 1);
+ cc_data_temp = brcmf_sdcard_reg_read(bus->sdiodev,
+ CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr), 4);
+ cc_data_temp &= ~str_mask;
+ drivestrength_sel <<= str_shift;
+ cc_data_temp |= drivestrength_sel;
+ brcmf_sdcard_reg_write(bus->sdiodev,
+ CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr),
+ 4, cc_data_temp);
+
+ brcmf_dbg(INFO, "SDIO: %dmA drive strength selected, set to 0x%08x\n",
+ drivestrength, cc_data_temp);
+ }
+}
+
+static int
+brcmf_sdbrcm_chip_recognition(struct brcmf_sdio_dev *sdiodev,
+ struct chip_info *ci, u32 regs)
+{
+ u32 regdata;
+
+ /*
+ * Get CC core rev
+ * Chipid is assume to be at offset 0 from regs arg
+ * For different chiptypes or old sdio hosts w/o chipcommon,
+ * other ways of recognition should be added here.
+ */
+ ci->cccorebase = regs;
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_CC_REG(ci->cccorebase, chipid), 4);
+ ci->chip = regdata & CID_ID_MASK;
+ ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
+
+ brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev);
+
+ /* Address of cores for new chips should be added here */
+ switch (ci->chip) {
+ case BCM4329_CHIP_ID:
+ ci->buscorebase = BCM4329_CORE_BUS_BASE;
+ ci->ramcorebase = BCM4329_CORE_SOCRAM_BASE;
+ ci->armcorebase = BCM4329_CORE_ARM_BASE;
+ ci->ramsize = BCM4329_RAMSIZE;
+ break;
+ default:
+ brcmf_dbg(ERROR, "chipid 0x%x is not supported\n", ci->chip);
+ return -ENODEV;
+ }
+
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(ci->cccorebase, sbidhigh), 4);
+ ci->ccrev = SBCOREREV(regdata);
+
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_CC_REG(ci->cccorebase, pmucapabilities), 4);
+ ci->pmurev = regdata & PCAP_REV_MASK;
+
+ regdata = brcmf_sdcard_reg_read(sdiodev,
+ CORE_SB(ci->buscorebase, sbidhigh), 4);
+ ci->buscorerev = SBCOREREV(regdata);
+ ci->buscoretype = (regdata & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT;
+
+ brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n",
+ ci->ccrev, ci->pmurev, ci->buscorerev, ci->buscoretype);
+
+ /* get chipcommon capabilites */
+ ci->cccaps = brcmf_sdcard_reg_read(sdiodev,
+ CORE_CC_REG(ci->cccorebase, capabilities), 4);
+
+ return 0;
+}
+
+static int
+brcmf_sdbrcm_chip_attach(struct brcmf_bus *bus, u32 regs)
+{
+ struct chip_info *ci;
+ int err;
+ u8 clkval, clkset;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* alloc chip_info_t */
+ ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC);
+ if (NULL == ci)
+ return -ENOMEM;
+
+ /* bus/core/clk setup for register access */
+ /* Try forcing SDIO core to do ALPAvail request only */
+ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+ if (err) {
+ brcmf_dbg(ERROR, "error writing for HT off\n");
+ goto fail;
+ }
+
+ /* If register supported, wait for ALPAvail and then force ALP */
+ /* This may take up to 15 milliseconds */
+ clkval = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, NULL);
+ if ((clkval & ~SBSDIO_AVBITS) == clkset) {
+ SPINWAIT(((clkval =
+ brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ NULL)),
+ !SBSDIO_ALPAV(clkval)),
+ PMU_MAX_TRANSITION_DLY);
+ if (!SBSDIO_ALPAV(clkval)) {
+ brcmf_dbg(ERROR, "timeout on ALPAV wait, clkval 0x%02x\n",
+ clkval);
+ err = -EBUSY;
+ goto fail;
+ }
+ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF |
+ SBSDIO_FORCE_ALP;
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ clkset, &err);
+ udelay(65);
+ } else {
+ brcmf_dbg(ERROR, "ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
+ clkset, clkval);
+ err = -EACCES;
+ goto fail;
+ }
+
+ /* Also, disable the extra SDIO pull-ups */
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
+
+ err = brcmf_sdbrcm_chip_recognition(bus->sdiodev, ci, regs);
+ if (err)
+ goto fail;
+
+ /*
+ * Make sure any on-chip ARM is off (in case strapping is wrong),
+ * or downloaded code was already running.
+ */
+ brcmf_sdbrcm_chip_disablecore(bus->sdiodev, ci->armcorebase);
+
+ brcmf_sdcard_reg_write(bus->sdiodev,
+ CORE_CC_REG(ci->cccorebase, gpiopullup), 4, 0);
+ brcmf_sdcard_reg_write(bus->sdiodev,
+ CORE_CC_REG(ci->cccorebase, gpiopulldown), 4, 0);
+
+ /* Disable F2 to clear any intermediate frame state on the dongle */
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_0, SDIO_CCCR_IOEx,
+ SDIO_FUNC_ENABLE_1, NULL);
+
+ /* WAR: cmd52 backplane read so core HW will drop ALPReq */
+ clkval = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ 0, NULL);
+
+ /* Done with backplane-dependent accesses, can drop clock... */
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
+
+ bus->ci = ci;
+ return 0;
+fail:
+ bus->ci = NULL;
+ kfree(ci);
+ return err;
+}
+
+static bool
+brcmf_sdbrcm_probe_attach(struct brcmf_bus *bus, u32 regsva)
+{
+ u8 clkctl = 0;
+ int err = 0;
+ int reg_addr;
+ u32 reg_val;
+
+ bus->alp_only = true;
+
+ /* Return the window to backplane enumeration space for core access */
+ if (brcmf_sdcard_set_sbaddr_window(bus->sdiodev, SI_ENUM_BASE))
+ brcmf_dbg(ERROR, "FAILED to return to SI_ENUM_BASE\n");
+
+#ifdef BCMDBG
+ printk(KERN_DEBUG "F1 signature read @0x18000000=0x%4x\n",
+ brcmf_sdcard_reg_read(bus->sdiodev, SI_ENUM_BASE, 4));
+
+#endif /* BCMDBG */
+
+ /*
+ * Force PLL off until brcmf_sdbrcm_chip_attach()
+ * programs PLL control regs
+ */
+
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ BRCMF_INIT_CLKCTL1, &err);
+ if (!err)
+ clkctl =
+ brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+
+ if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
+ brcmf_dbg(ERROR, "ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n",
+ err, BRCMF_INIT_CLKCTL1, clkctl);
+ goto fail;
+ }
+
+ if (brcmf_sdbrcm_chip_attach(bus, regsva)) {
+ brcmf_dbg(ERROR, "brcmf_sdbrcm_chip_attach failed!\n");
+ goto fail;
+ }
+
+ if (!brcmf_sdbrcm_chipmatch((u16) bus->ci->chip)) {
+ brcmf_dbg(ERROR, "unsupported chip: 0x%04x\n", bus->ci->chip);
+ goto fail;
+ }
+
+ brcmf_sdbrcm_sdiod_drive_strength_init(bus, SDIO_DRIVE_STRENGTH);
+
+ /* Get info on the ARM and SOCRAM cores... */
+ brcmf_sdcard_reg_read(bus->sdiodev,
+ CORE_SB(bus->ci->armcorebase, sbidhigh), 4);
+ bus->ramsize = bus->ci->ramsize;
+ if (!(bus->ramsize)) {
+ brcmf_dbg(ERROR, "failed to find SOCRAM memory!\n");
+ goto fail;
+ }
+
+ /* Set core control so an SDIO reset does a backplane reset */
+ reg_addr = bus->ci->buscorebase +
+ offsetof(struct sdpcmd_regs, corecontrol);
+ reg_val = brcmf_sdcard_reg_read(bus->sdiodev, reg_addr, sizeof(u32));
+ brcmf_sdcard_reg_write(bus->sdiodev, reg_addr, sizeof(u32),
+ reg_val | CC_BPRESEN);
+
+ brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
+
+ /* Locate an appropriately-aligned portion of hdrbuf */
+ bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0],
+ BRCMF_SDALIGN);
+
+ /* Set the poll and/or interrupt flags */
+ bus->intr = true;
+ bus->poll = false;
+ if (bus->poll)
+ bus->pollrate = 1;
+
+ return true;
+
+fail:
+ return false;
+}
+
+static bool brcmf_sdbrcm_probe_init(struct brcmf_bus *bus)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Disable F2 to clear any intermediate frame state on the dongle */
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_0, SDIO_CCCR_IOEx,
+ SDIO_FUNC_ENABLE_1, NULL);
+
+ bus->drvr->busstate = BRCMF_BUS_DOWN;
+ bus->sleeping = false;
+ bus->rxflow = false;
+
+ /* Done with backplane-dependent accesses, can drop clock... */
+ brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
+
+ /* ...and initialize clock/power states */
+ bus->clkstate = CLK_SDONLY;
+ bus->idletime = BRCMF_IDLE_INTERVAL;
+ bus->idleclock = BRCMF_IDLE_ACTIVE;
+
+ /* Query the F2 block size, set roundup accordingly */
+ bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
+ bus->roundup = min(max_roundup, bus->blocksize);
+
+ /* bus module does not support packet chaining */
+ bus->use_rxchain = false;
+ bus->sd_rxchain = false;
+
+ return true;
+}
+
+static int
+brcmf_sdbrcm_watchdog_thread(void *data)
+{
+ struct brcmf_bus *bus = (struct brcmf_bus *)data;
+
+ allow_signal(SIGTERM);
+ /* Run until signal received */
+ while (1) {
+ if (kthread_should_stop())
+ break;
+ if (!wait_for_completion_interruptible(&bus->watchdog_wait)) {
+ brcmf_sdbrcm_bus_watchdog(bus->drvr);
+ /* Count the tick for reference */
+ bus->drvr->tickcnt++;
+ } else
+ break;
+ }
+ return 0;
+}
+
+static void
+brcmf_sdbrcm_watchdog(unsigned long data)
+{
+ struct brcmf_bus *bus = (struct brcmf_bus *)data;
+
+ if (bus->watchdog_tsk) {
+ complete(&bus->watchdog_wait);
+ /* Reschedule the watchdog */
+ if (bus->wd_timer_valid)
+ mod_timer(&bus->timer,
+ jiffies + BRCMF_WD_POLL_MS * HZ / 1000);
+ }
+}
+
+static void
+brcmf_sdbrcm_chip_detach(struct brcmf_bus *bus)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ kfree(bus->ci);
+ bus->ci = NULL;
+}
+
+static void brcmf_sdbrcm_release_dongle(struct brcmf_bus *bus)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus->ci) {
+ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+ brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+ brcmf_sdbrcm_chip_detach(bus);
+ if (bus->vars && bus->varsz)
+ kfree(bus->vars);
+ bus->vars = NULL;
+ }
+
+ brcmf_dbg(TRACE, "Disconnected\n");
+}
+
+/* Detach and free everything */
+static void brcmf_sdbrcm_release(struct brcmf_bus *bus)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus) {
+ /* De-register interrupt handler */
+ brcmf_sdcard_intr_dereg(bus->sdiodev);
+
+ if (bus->drvr) {
+ brcmf_detach(bus->drvr);
+ brcmf_sdbrcm_release_dongle(bus);
+ bus->drvr = NULL;
+ }
+
+ brcmf_sdbrcm_release_malloc(bus);
+
+ kfree(bus);
+ }
+
+ brcmf_dbg(TRACE, "Disconnected\n");
+}
+
+void *brcmf_sdbrcm_probe(u16 bus_no, u16 slot, u16 func, uint bustype,
+ u32 regsva, struct brcmf_sdio_dev *sdiodev)
+{
+ int ret;
+ struct brcmf_bus *bus;
+
+ /* Init global variables at run-time, not as part of the declaration.
+ * This is required to support init/de-init of the driver.
+ * Initialization
+ * of globals as part of the declaration results in non-deterministic
+ * behavior since the value of the globals may be different on the
+ * first time that the driver is initialized vs subsequent
+ * initializations.
+ */
+ brcmf_c_init();
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* We make an assumption about address window mappings:
+ * regsva == SI_ENUM_BASE*/
+
+ /* Allocate private bus interface state */
+ bus = kzalloc(sizeof(struct brcmf_bus), GFP_ATOMIC);
+ if (!bus)
+ goto fail;
+
+ bus->sdiodev = sdiodev;
+ sdiodev->bus = bus;
+ bus->txbound = BRCMF_TXBOUND;
+ bus->rxbound = BRCMF_RXBOUND;
+ bus->txminmax = BRCMF_TXMINMAX;
+ bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
+ bus->usebufpool = false; /* Use bufpool if allocated,
+ else use locally malloced rxbuf */
+
+ /* attempt to attach to the dongle */
+ if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) {
+ brcmf_dbg(ERROR, "brcmf_sdbrcm_probe_attach failed\n");
+ goto fail;
+ }
+
+ spin_lock_init(&bus->txqlock);
+ init_waitqueue_head(&bus->ctrl_wait);
+ init_waitqueue_head(&bus->dcmd_resp_wait);
+
+ /* Set up the watchdog timer */
+ init_timer(&bus->timer);
+ bus->timer.data = (unsigned long)bus;
+ bus->timer.function = brcmf_sdbrcm_watchdog;
+
+ /* Initialize thread based operation and lock */
+ sema_init(&bus->sdsem, 1);
+
+ /* Initialize watchdog thread */
+ init_completion(&bus->watchdog_wait);
+ bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread,
+ bus, "brcmf_watchdog");
+ if (IS_ERR(bus->watchdog_tsk)) {
+ printk(KERN_WARNING
+ "brcmf_watchdog thread failed to start\n");
+ bus->watchdog_tsk = NULL;
+ }
+ /* Initialize DPC thread */
+ init_completion(&bus->dpc_wait);
+ bus->dpc_tsk = kthread_run(brcmf_sdbrcm_dpc_thread,
+ bus, "brcmf_dpc");
+ if (IS_ERR(bus->dpc_tsk)) {
+ printk(KERN_WARNING
+ "brcmf_dpc thread failed to start\n");
+ bus->dpc_tsk = NULL;
+ }
+
+ /* Attach to the brcmf/OS/network interface */
+ bus->drvr = brcmf_attach(bus, SDPCM_RESERVE);
+ if (!bus->drvr) {
+ brcmf_dbg(ERROR, "brcmf_attach failed\n");
+ goto fail;
+ }
+
+ /* Allocate buffers */
+ if (!(brcmf_sdbrcm_probe_malloc(bus))) {
+ brcmf_dbg(ERROR, "brcmf_sdbrcm_probe_malloc failed\n");
+ goto fail;
+ }
+
+ if (!(brcmf_sdbrcm_probe_init(bus))) {
+ brcmf_dbg(ERROR, "brcmf_sdbrcm_probe_init failed\n");
+ goto fail;
+ }
+
+ /* Register interrupt callback, but mask it (not operational yet). */
+ brcmf_dbg(INTR, "disable SDIO interrupts (not interested yet)\n");
+ ret = brcmf_sdcard_intr_reg(bus->sdiodev);
+ if (ret != 0) {
+ brcmf_dbg(ERROR, "FAILED: sdcard_intr_reg returned %d\n", ret);
+ goto fail;
+ }
+ brcmf_dbg(INTR, "registered SDIO interrupt function ok\n");
+
+ brcmf_dbg(INFO, "completed!!\n");
+
+ /* if firmware path present try to download and bring up bus */
+ ret = brcmf_bus_start(bus->drvr);
+ if (ret != 0) {
+ if (ret == -ENOLINK) {
+ brcmf_dbg(ERROR, "dongle is not responding\n");
+ goto fail;
+ }
+ }
+ /* Ok, have the per-port tell the stack we're open for business */
+ if (brcmf_net_attach(bus->drvr, 0) != 0) {
+ brcmf_dbg(ERROR, "Net attach failed!!\n");
+ goto fail;
+ }
+
+ return bus;
+
+fail:
+ brcmf_sdbrcm_release(bus);
+ return NULL;
+}
+
+void brcmf_sdbrcm_disconnect(void *ptr)
+{
+ struct brcmf_bus *bus = (struct brcmf_bus *)ptr;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus)
+ brcmf_sdbrcm_release(bus);
+
+ brcmf_dbg(TRACE, "Disconnected\n");
+}
+
+struct device *brcmf_bus_get_device(struct brcmf_bus *bus)
+{
+ return &bus->sdiodev->func[2]->dev;
+}
+
+void
+brcmf_sdbrcm_wd_timer(struct brcmf_bus *bus, uint wdtick)
+{
+ /* don't start the wd until fw is loaded */
+ if (bus->drvr->busstate == BRCMF_BUS_DOWN)
+ return;
+
+ /* Totally stop the timer */
+ if (!wdtick && bus->wd_timer_valid == true) {
+ del_timer_sync(&bus->timer);
+ bus->wd_timer_valid = false;
+ bus->save_ms = wdtick;
+ return;
+ }
+
+ if (wdtick) {
+ if (bus->save_ms != BRCMF_WD_POLL_MS) {
+ if (bus->wd_timer_valid == true)
+ /* Stop timer and restart at new value */
+ del_timer_sync(&bus->timer);
+
+ /* Create timer again when watchdog period is
+ dynamically changed or in the first instance
+ */
+ bus->timer.expires =
+ jiffies + BRCMF_WD_POLL_MS * HZ / 1000;
+ add_timer(&bus->timer);
+
+ } else {
+ /* Re arm the timer, at last watchdog period */
+ mod_timer(&bus->timer,
+ jiffies + BRCMF_WD_POLL_MS * HZ / 1000);
+ }
+
+ bus->wd_timer_valid = true;
+ bus->save_ms = wdtick;
+ }
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
new file mode 100644
index 000000000000..726fa8981113
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_SDH_H_
+#define _BRCM_SDH_H_
+
+#include <linux/skbuff.h>
+
+#define SDIO_FUNC_0 0
+#define SDIO_FUNC_1 1
+#define SDIO_FUNC_2 2
+
+#define SDIOD_FBR_SIZE 0x100
+
+/* io_en */
+#define SDIO_FUNC_ENABLE_1 0x02
+#define SDIO_FUNC_ENABLE_2 0x04
+
+/* io_rdys */
+#define SDIO_FUNC_READY_1 0x02
+#define SDIO_FUNC_READY_2 0x04
+
+/* intr_status */
+#define INTR_STATUS_FUNC1 0x2
+#define INTR_STATUS_FUNC2 0x4
+
+/* Maximum number of I/O funcs */
+#define SDIOD_MAX_IOFUNCS 7
+
+/* as of sdiod rev 0, supports 3 functions */
+#define SBSDIO_NUM_FUNCTION 3
+
+/* function 1 miscellaneous registers */
+
+/* sprom command and status */
+#define SBSDIO_SPROM_CS 0x10000
+/* sprom info register */
+#define SBSDIO_SPROM_INFO 0x10001
+/* sprom indirect access data byte 0 */
+#define SBSDIO_SPROM_DATA_LOW 0x10002
+/* sprom indirect access data byte 1 */
+#define SBSDIO_SPROM_DATA_HIGH 0x10003
+/* sprom indirect access addr byte 0 */
+#define SBSDIO_SPROM_ADDR_LOW 0x10004
+/* sprom indirect access addr byte 0 */
+#define SBSDIO_SPROM_ADDR_HIGH 0x10005
+/* xtal_pu (gpio) output */
+#define SBSDIO_CHIP_CTRL_DATA 0x10006
+/* xtal_pu (gpio) enable */
+#define SBSDIO_CHIP_CTRL_EN 0x10007
+/* rev < 7, watermark for sdio device */
+#define SBSDIO_WATERMARK 0x10008
+/* control busy signal generation */
+#define SBSDIO_DEVICE_CTL 0x10009
+
+/* SB Address Window Low (b15) */
+#define SBSDIO_FUNC1_SBADDRLOW 0x1000A
+/* SB Address Window Mid (b23:b16) */
+#define SBSDIO_FUNC1_SBADDRMID 0x1000B
+/* SB Address Window High (b31:b24) */
+#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C
+/* Frame Control (frame term/abort) */
+#define SBSDIO_FUNC1_FRAMECTRL 0x1000D
+/* ChipClockCSR (ALP/HT ctl/status) */
+#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E
+/* SdioPullUp (on cmd, d0-d2) */
+#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F
+/* Write Frame Byte Count Low */
+#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019
+/* Write Frame Byte Count High */
+#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A
+/* Read Frame Byte Count Low */
+#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B
+/* Read Frame Byte Count High */
+#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C
+
+#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */
+#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001C /* f1 misc register end */
+
+/* function 1 OCP space */
+
+/* sb offset addr is <= 15 bits, 32k */
+#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF
+#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000
+/* with b15, maps to 32-bit SB access */
+#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000
+
+/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
+
+#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */
+#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */
+#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */
+/* Address bits from SBADDR regs */
+#define SBSDIO_SBWINDOW_MASK 0xffff8000
+
+#define SDIOH_READ 0 /* Read request */
+#define SDIOH_WRITE 1 /* Write request */
+
+#define SDIOH_DATA_FIX 0 /* Fixed addressing */
+#define SDIOH_DATA_INC 1 /* Incremental addressing */
+
+/* internal return code */
+#define SUCCESS 0
+#define ERROR 1
+
+struct brcmf_sdreg {
+ int func;
+ int offset;
+ int value;
+};
+
+struct brcmf_sdio_dev {
+ struct sdio_func *func[SDIO_MAX_FUNCS];
+ u8 num_funcs; /* Supported funcs on client */
+ u32 func_cis_ptr[SDIOD_MAX_IOFUNCS];
+ u32 sbwad; /* Save backplane window address */
+ bool regfail; /* status of last reg_r/w call */
+ void *bus;
+ atomic_t suspend; /* suspend flag */
+ wait_queue_head_t request_byte_wait;
+ wait_queue_head_t request_word_wait;
+ wait_queue_head_t request_packet_wait;
+ wait_queue_head_t request_buffer_wait;
+
+};
+
+/* Register/deregister device interrupt handler. */
+extern int
+brcmf_sdcard_intr_reg(struct brcmf_sdio_dev *sdiodev);
+
+extern int brcmf_sdcard_intr_dereg(struct brcmf_sdio_dev *sdiodev);
+
+/* Access SDIO address space (e.g. CCCR) using CMD52 (single-byte interface).
+ * fn: function number
+ * addr: unmodified SDIO-space address
+ * data: data byte to write
+ * err: pointer to error code (or NULL)
+ */
+extern u8 brcmf_sdcard_cfg_read(struct brcmf_sdio_dev *sdiodev, uint func,
+ u32 addr, int *err);
+extern void brcmf_sdcard_cfg_write(struct brcmf_sdio_dev *sdiodev, uint func,
+ u32 addr, u8 data, int *err);
+
+/* Synchronous access to device (client) core registers via CMD53 to F1.
+ * addr: backplane address (i.e. >= regsva from attach)
+ * size: register width in bytes (2 or 4)
+ * data: data for register write
+ */
+extern u32
+brcmf_sdcard_reg_read(struct brcmf_sdio_dev *sdiodev, u32 addr, uint size);
+
+extern u32
+brcmf_sdcard_reg_write(struct brcmf_sdio_dev *sdiodev, u32 addr, uint size,
+ u32 data);
+
+/* Indicate if last reg read/write failed */
+extern bool brcmf_sdcard_regfail(struct brcmf_sdio_dev *sdiodev);
+
+/* Buffer transfer to/from device (client) core via cmd53.
+ * fn: function number
+ * addr: backplane address (i.e. >= regsva from attach)
+ * flags: backplane width, address increment, sync/async
+ * buf: pointer to memory data buffer
+ * nbytes: number of bytes to transfer to/from buf
+ * pkt: pointer to packet associated with buf (if any)
+ * complete: callback function for command completion (async only)
+ * handle: handle for completion callback (first arg in callback)
+ * Returns 0 or error code.
+ * NOTE: Async operation is not currently supported.
+ */
+extern int
+brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
+ uint flags, u8 *buf, uint nbytes, struct sk_buff *pkt);
+extern int
+brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
+ uint flags, u8 *buf, uint nbytes, struct sk_buff *pkt);
+
+/* Flags bits */
+
+/* Four-byte target (backplane) width (vs. two-byte) */
+#define SDIO_REQ_4BYTE 0x1
+/* Fixed address (FIFO) (vs. incrementing address) */
+#define SDIO_REQ_FIXED 0x2
+/* Async request (vs. sync request) */
+#define SDIO_REQ_ASYNC 0x4
+
+/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only).
+ * rw: read or write (0/1)
+ * addr: direct SDIO address
+ * buf: pointer to memory data buffer
+ * nbytes: number of bytes to transfer to/from buf
+ * Returns 0 or error code.
+ */
+extern int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw,
+ u32 addr, u8 *buf, uint nbytes);
+
+/* Issue an abort to the specified function */
+extern int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
+
+/* platform specific/high level functions */
+extern int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
+extern int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev);
+
+extern int brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev,
+ u32 address);
+
+/* attach, return handler on success, NULL if failed.
+ * The handler shall be provided by all subsequent calls. No local cache
+ * cfghdl points to the starting address of pci device mapped memory
+ */
+extern int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev);
+extern void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev);
+
+/* read or write one byte using cmd52 */
+extern int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw,
+ uint fnc, uint addr, u8 *byte);
+
+/* read or write 2/4 bytes using cmd53 */
+extern int
+brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
+ uint rw, uint fnc, uint addr,
+ u32 *word, uint nbyte);
+
+/* read or write any buffer using cmd53 */
+extern int
+brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev,
+ uint fix_inc, uint rw, uint fnc_num,
+ u32 addr, uint regwidth,
+ u32 buflen, u8 *buffer, struct sk_buff *pkt);
+
+/* Watchdog timer interface for pm ops */
+extern void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev,
+ bool enable);
+
+extern void *brcmf_sdbrcm_probe(u16 bus_no, u16 slot, u16 func, uint bustype,
+ u32 regsva, struct brcmf_sdio_dev *sdiodev);
+extern void brcmf_sdbrcm_disconnect(void *ptr);
+extern void brcmf_sdbrcm_isr(void *arg);
+#endif /* _BRCM_SDH_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
new file mode 100644
index 000000000000..5eddabe5228a
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -0,0 +1,3868 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
+
+#include <linux/kernel.h>
+#include <linux/if_arp.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/netdevice.h>
+#include <linux/bitops.h>
+#include <linux/etherdevice.h>
+#include <linux/ieee80211.h>
+#include <linux/uaccess.h>
+#include <net/cfg80211.h>
+
+#include <brcmu_utils.h>
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include "dhd.h"
+#include "wl_cfg80211.h"
+
+#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
+ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
+
+static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255};
+
+static u32 brcmf_dbg_level = WL_DBG_ERR;
+
+static void brcmf_set_drvdata(struct brcmf_cfg80211_dev *dev, void *data)
+{
+ dev->driver_data = data;
+}
+
+static void *brcmf_get_drvdata(struct brcmf_cfg80211_dev *dev)
+{
+ void *data = NULL;
+
+ if (dev)
+ data = dev->driver_data;
+ return data;
+}
+
+static
+struct brcmf_cfg80211_priv *brcmf_priv_get(struct brcmf_cfg80211_dev *cfg_dev)
+{
+ struct brcmf_cfg80211_iface *ci = brcmf_get_drvdata(cfg_dev);
+ return ci->cfg_priv;
+}
+
+static bool check_sys_up(struct wiphy *wiphy)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ if (!test_bit(WL_STATUS_READY, &cfg_priv->status)) {
+ WL_INFO("device is not ready : status (%d)\n",
+ (int)cfg_priv->status);
+ return false;
+ }
+ return true;
+}
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_channel, _flags) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = 5000 + (5 * (_channel)), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
+#define RATETAB_ENT(_rateid, _flags) \
+ { \
+ .bitrate = RATE_TO_BASE100KBPS(_rateid), \
+ .hw_value = (_rateid), \
+ .flags = (_flags), \
+ }
+
+static struct ieee80211_rate __wl_rates[] = {
+ RATETAB_ENT(BRCM_RATE_1M, 0),
+ RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(BRCM_RATE_6M, 0),
+ RATETAB_ENT(BRCM_RATE_9M, 0),
+ RATETAB_ENT(BRCM_RATE_12M, 0),
+ RATETAB_ENT(BRCM_RATE_18M, 0),
+ RATETAB_ENT(BRCM_RATE_24M, 0),
+ RATETAB_ENT(BRCM_RATE_36M, 0),
+ RATETAB_ENT(BRCM_RATE_48M, 0),
+ RATETAB_ENT(BRCM_RATE_54M, 0),
+};
+
+#define wl_a_rates (__wl_rates + 4)
+#define wl_a_rates_size 8
+#define wl_g_rates (__wl_rates + 0)
+#define wl_g_rates_size 12
+
+static struct ieee80211_channel __wl_2ghz_channels[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel __wl_5ghz_a_channels[] = {
+ CHAN5G(34, 0), CHAN5G(36, 0),
+ CHAN5G(38, 0), CHAN5G(40, 0),
+ CHAN5G(42, 0), CHAN5G(44, 0),
+ CHAN5G(46, 0), CHAN5G(48, 0),
+ CHAN5G(52, 0), CHAN5G(56, 0),
+ CHAN5G(60, 0), CHAN5G(64, 0),
+ CHAN5G(100, 0), CHAN5G(104, 0),
+ CHAN5G(108, 0), CHAN5G(112, 0),
+ CHAN5G(116, 0), CHAN5G(120, 0),
+ CHAN5G(124, 0), CHAN5G(128, 0),
+ CHAN5G(132, 0), CHAN5G(136, 0),
+ CHAN5G(140, 0), CHAN5G(149, 0),
+ CHAN5G(153, 0), CHAN5G(157, 0),
+ CHAN5G(161, 0), CHAN5G(165, 0),
+ CHAN5G(184, 0), CHAN5G(188, 0),
+ CHAN5G(192, 0), CHAN5G(196, 0),
+ CHAN5G(200, 0), CHAN5G(204, 0),
+ CHAN5G(208, 0), CHAN5G(212, 0),
+ CHAN5G(216, 0),
+};
+
+static struct ieee80211_channel __wl_5ghz_n_channels[] = {
+ CHAN5G(32, 0), CHAN5G(34, 0),
+ CHAN5G(36, 0), CHAN5G(38, 0),
+ CHAN5G(40, 0), CHAN5G(42, 0),
+ CHAN5G(44, 0), CHAN5G(46, 0),
+ CHAN5G(48, 0), CHAN5G(50, 0),
+ CHAN5G(52, 0), CHAN5G(54, 0),
+ CHAN5G(56, 0), CHAN5G(58, 0),
+ CHAN5G(60, 0), CHAN5G(62, 0),
+ CHAN5G(64, 0), CHAN5G(66, 0),
+ CHAN5G(68, 0), CHAN5G(70, 0),
+ CHAN5G(72, 0), CHAN5G(74, 0),
+ CHAN5G(76, 0), CHAN5G(78, 0),
+ CHAN5G(80, 0), CHAN5G(82, 0),
+ CHAN5G(84, 0), CHAN5G(86, 0),
+ CHAN5G(88, 0), CHAN5G(90, 0),
+ CHAN5G(92, 0), CHAN5G(94, 0),
+ CHAN5G(96, 0), CHAN5G(98, 0),
+ CHAN5G(100, 0), CHAN5G(102, 0),
+ CHAN5G(104, 0), CHAN5G(106, 0),
+ CHAN5G(108, 0), CHAN5G(110, 0),
+ CHAN5G(112, 0), CHAN5G(114, 0),
+ CHAN5G(116, 0), CHAN5G(118, 0),
+ CHAN5G(120, 0), CHAN5G(122, 0),
+ CHAN5G(124, 0), CHAN5G(126, 0),
+ CHAN5G(128, 0), CHAN5G(130, 0),
+ CHAN5G(132, 0), CHAN5G(134, 0),
+ CHAN5G(136, 0), CHAN5G(138, 0),
+ CHAN5G(140, 0), CHAN5G(142, 0),
+ CHAN5G(144, 0), CHAN5G(145, 0),
+ CHAN5G(146, 0), CHAN5G(147, 0),
+ CHAN5G(148, 0), CHAN5G(149, 0),
+ CHAN5G(150, 0), CHAN5G(151, 0),
+ CHAN5G(152, 0), CHAN5G(153, 0),
+ CHAN5G(154, 0), CHAN5G(155, 0),
+ CHAN5G(156, 0), CHAN5G(157, 0),
+ CHAN5G(158, 0), CHAN5G(159, 0),
+ CHAN5G(160, 0), CHAN5G(161, 0),
+ CHAN5G(162, 0), CHAN5G(163, 0),
+ CHAN5G(164, 0), CHAN5G(165, 0),
+ CHAN5G(166, 0), CHAN5G(168, 0),
+ CHAN5G(170, 0), CHAN5G(172, 0),
+ CHAN5G(174, 0), CHAN5G(176, 0),
+ CHAN5G(178, 0), CHAN5G(180, 0),
+ CHAN5G(182, 0), CHAN5G(184, 0),
+ CHAN5G(186, 0), CHAN5G(188, 0),
+ CHAN5G(190, 0), CHAN5G(192, 0),
+ CHAN5G(194, 0), CHAN5G(196, 0),
+ CHAN5G(198, 0), CHAN5G(200, 0),
+ CHAN5G(202, 0), CHAN5G(204, 0),
+ CHAN5G(206, 0), CHAN5G(208, 0),
+ CHAN5G(210, 0), CHAN5G(212, 0),
+ CHAN5G(214, 0), CHAN5G(216, 0),
+ CHAN5G(218, 0), CHAN5G(220, 0),
+ CHAN5G(222, 0), CHAN5G(224, 0),
+ CHAN5G(226, 0), CHAN5G(228, 0),
+};
+
+static struct ieee80211_supported_band __wl_band_2ghz = {
+ .band = IEEE80211_BAND_2GHZ,
+ .channels = __wl_2ghz_channels,
+ .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
+ .bitrates = wl_g_rates,
+ .n_bitrates = wl_g_rates_size,
+};
+
+static struct ieee80211_supported_band __wl_band_5ghz_a = {
+ .band = IEEE80211_BAND_5GHZ,
+ .channels = __wl_5ghz_a_channels,
+ .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
+ .bitrates = wl_a_rates,
+ .n_bitrates = wl_a_rates_size,
+};
+
+static struct ieee80211_supported_band __wl_band_5ghz_n = {
+ .band = IEEE80211_BAND_5GHZ,
+ .channels = __wl_5ghz_n_channels,
+ .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels),
+ .bitrates = wl_a_rates,
+ .n_bitrates = wl_a_rates_size,
+};
+
+static const u32 __wl_cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_AES_CMAC,
+};
+
+/* tag_ID/length/value_buffer tuple */
+struct brcmf_tlv {
+ u8 id;
+ u8 len;
+ u8 data[1];
+};
+
+/* Quarter dBm units to mW
+ * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
+ * Table is offset so the last entry is largest mW value that fits in
+ * a u16.
+ */
+
+#define QDBM_OFFSET 153 /* Offset for first entry */
+#define QDBM_TABLE_LEN 40 /* Table size */
+
+/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
+ * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
+ */
+#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
+
+/* Largest mW value that will round down to the last table entry,
+ * QDBM_OFFSET + QDBM_TABLE_LEN-1.
+ * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
+ * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
+ */
+#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
+
+static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
+/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
+/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
+/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
+/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
+/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
+/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
+};
+
+static u16 brcmf_qdbm_to_mw(u8 qdbm)
+{
+ uint factor = 1;
+ int idx = qdbm - QDBM_OFFSET;
+
+ if (idx >= QDBM_TABLE_LEN)
+ /* clamp to max u16 mW value */
+ return 0xFFFF;
+
+ /* scale the qdBm index up to the range of the table 0-40
+ * where an offset of 40 qdBm equals a factor of 10 mW.
+ */
+ while (idx < 0) {
+ idx += 40;
+ factor *= 10;
+ }
+
+ /* return the mW value scaled down to the correct factor of 10,
+ * adding in factor/2 to get proper rounding.
+ */
+ return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
+}
+
+static u8 brcmf_mw_to_qdbm(u16 mw)
+{
+ u8 qdbm;
+ int offset;
+ uint mw_uint = mw;
+ uint boundary;
+
+ /* handle boundary case */
+ if (mw_uint <= 1)
+ return 0;
+
+ offset = QDBM_OFFSET;
+
+ /* move mw into the range of the table */
+ while (mw_uint < QDBM_TABLE_LOW_BOUND) {
+ mw_uint *= 10;
+ offset -= 40;
+ }
+
+ for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
+ boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
+ nqdBm_to_mW_map[qdbm]) / 2;
+ if (mw_uint < boundary)
+ break;
+ }
+
+ qdbm += (u8) offset;
+
+ return qdbm;
+}
+
+/* function for reading/writing a single u32 from/to the dongle */
+static int
+brcmf_exec_dcmd_u32(struct net_device *ndev, u32 cmd, u32 *par)
+{
+ int err;
+ __le32 par_le = cpu_to_le32(*par);
+
+ err = brcmf_exec_dcmd(ndev, cmd, &par_le, sizeof(__le32));
+ *par = le32_to_cpu(par_le);
+
+ return err;
+}
+
+static void convert_key_from_CPU(struct brcmf_wsec_key *key,
+ struct brcmf_wsec_key_le *key_le)
+{
+ key_le->index = cpu_to_le32(key->index);
+ key_le->len = cpu_to_le32(key->len);
+ key_le->algo = cpu_to_le32(key->algo);
+ key_le->flags = cpu_to_le32(key->flags);
+ key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
+ key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
+ key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
+ memcpy(key_le->data, key->data, sizeof(key->data));
+ memcpy(key_le->ea, key->ea, sizeof(key->ea));
+}
+
+static int send_key_to_dongle(struct net_device *ndev,
+ struct brcmf_wsec_key *key)
+{
+ int err;
+ struct brcmf_wsec_key_le key_le;
+
+ convert_key_from_CPU(key, &key_le);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_KEY, &key_le, sizeof(key_le));
+ if (err)
+ WL_ERR("WLC_SET_KEY error (%d)\n", err);
+ return err;
+}
+
+static s32
+brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct wireless_dev *wdev;
+ s32 infra = 0;
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_WDS:
+ WL_ERR("type (%d) : currently we do not support this type\n",
+ type);
+ return -EOPNOTSUPP;
+ case NL80211_IFTYPE_ADHOC:
+ cfg_priv->conf->mode = WL_MODE_IBSS;
+ infra = 0;
+ break;
+ case NL80211_IFTYPE_STATION:
+ cfg_priv->conf->mode = WL_MODE_BSS;
+ infra = 1;
+ break;
+ default:
+ err = -EINVAL;
+ goto done;
+ }
+
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &infra);
+ if (err) {
+ WL_ERR("WLC_SET_INFRA error (%d)\n", err);
+ err = -EAGAIN;
+ } else {
+ wdev = ndev->ieee80211_ptr;
+ wdev->iftype = type;
+ }
+
+ WL_INFO("IF Type = %s\n",
+ (cfg_priv->conf->mode == WL_MODE_IBSS) ? "Adhoc" : "Infra");
+
+done:
+ WL_TRACE("Exit\n");
+
+ return err;
+}
+
+static s32 brcmf_dev_intvar_set(struct net_device *ndev, s8 *name, s32 val)
+{
+ s8 buf[BRCMF_DCMD_SMLEN];
+ u32 len;
+ s32 err = 0;
+ __le32 val_le;
+
+ val_le = cpu_to_le32(val);
+ len = brcmf_c_mkiovar(name, (char *)(&val_le), sizeof(val_le), buf,
+ sizeof(buf));
+ BUG_ON(!len);
+
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, buf, len);
+ if (err)
+ WL_ERR("error (%d)\n", err);
+
+ return err;
+}
+
+static s32
+brcmf_dev_intvar_get(struct net_device *ndev, s8 *name, s32 *retval)
+{
+ union {
+ s8 buf[BRCMF_DCMD_SMLEN];
+ __le32 val;
+ } var;
+ u32 len;
+ u32 data_null;
+ s32 err = 0;
+
+ len =
+ brcmf_c_mkiovar(name, (char *)(&data_null), 0, (char *)(&var),
+ sizeof(var.buf));
+ BUG_ON(!len);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, &var, len);
+ if (err)
+ WL_ERR("error (%d)\n", err);
+
+ *retval = le32_to_cpu(var.val);
+
+ return err;
+}
+
+static void brcmf_set_mpc(struct net_device *ndev, int mpc)
+{
+ s32 err = 0;
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+
+ if (test_bit(WL_STATUS_READY, &cfg_priv->status)) {
+ err = brcmf_dev_intvar_set(ndev, "mpc", mpc);
+ if (err) {
+ WL_ERR("fail to set mpc\n");
+ return;
+ }
+ WL_INFO("MPC : %d\n", mpc);
+ }
+}
+
+static void wl_iscan_prep(struct brcmf_scan_params_le *params_le,
+ struct brcmf_ssid *ssid)
+{
+ memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
+ params_le->bss_type = DOT11_BSSTYPE_ANY;
+ params_le->scan_type = 0;
+ params_le->channel_num = 0;
+ params_le->nprobes = cpu_to_le32(-1);
+ params_le->active_time = cpu_to_le32(-1);
+ params_le->passive_time = cpu_to_le32(-1);
+ params_le->home_time = cpu_to_le32(-1);
+ if (ssid && ssid->SSID_len)
+ memcpy(&params_le->ssid_le, ssid, sizeof(struct brcmf_ssid));
+}
+
+static s32
+brcmf_dev_iovar_setbuf(struct net_device *ndev, s8 * iovar, void *param,
+ s32 paramlen, void *bufptr, s32 buflen)
+{
+ s32 iolen;
+
+ iolen = brcmf_c_mkiovar(iovar, param, paramlen, bufptr, buflen);
+ BUG_ON(!iolen);
+
+ return brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, bufptr, iolen);
+}
+
+static s32
+brcmf_dev_iovar_getbuf(struct net_device *ndev, s8 * iovar, void *param,
+ s32 paramlen, void *bufptr, s32 buflen)
+{
+ s32 iolen;
+
+ iolen = brcmf_c_mkiovar(iovar, param, paramlen, bufptr, buflen);
+ BUG_ON(!iolen);
+
+ return brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, bufptr, buflen);
+}
+
+static s32
+brcmf_run_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan,
+ struct brcmf_ssid *ssid, u16 action)
+{
+ s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
+ offsetof(struct brcmf_iscan_params_le, params_le);
+ struct brcmf_iscan_params_le *params;
+ s32 err = 0;
+
+ if (ssid && ssid->SSID_len)
+ params_size += sizeof(struct brcmf_ssid);
+ params = kzalloc(params_size, GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+ BUG_ON(params_size >= BRCMF_DCMD_SMLEN);
+
+ wl_iscan_prep(&params->params_le, ssid);
+
+ params->version = cpu_to_le32(BRCMF_ISCAN_REQ_VERSION);
+ params->action = cpu_to_le16(action);
+ params->scan_duration = cpu_to_le16(0);
+
+ err = brcmf_dev_iovar_setbuf(iscan->ndev, "iscan", params, params_size,
+ iscan->dcmd_buf, BRCMF_DCMD_SMLEN);
+ if (err) {
+ if (err == -EBUSY)
+ WL_INFO("system busy : iscan canceled\n");
+ else
+ WL_ERR("error (%d)\n", err);
+ }
+
+ kfree(params);
+ return err;
+}
+
+static s32 brcmf_do_iscan(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv);
+ struct net_device *ndev = cfg_to_ndev(cfg_priv);
+ struct brcmf_ssid ssid;
+ __le32 passive_scan;
+ s32 err = 0;
+
+ /* Broadcast scan by default */
+ memset(&ssid, 0, sizeof(ssid));
+
+ iscan->state = WL_ISCAN_STATE_SCANING;
+
+ passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1);
+ err = brcmf_exec_dcmd(cfg_to_ndev(cfg_priv), BRCMF_C_SET_PASSIVE_SCAN,
+ &passive_scan, sizeof(passive_scan));
+ if (err) {
+ WL_ERR("error (%d)\n", err);
+ return err;
+ }
+ brcmf_set_mpc(ndev, 0);
+ cfg_priv->iscan_kickstart = true;
+ err = brcmf_run_iscan(iscan, &ssid, BRCMF_SCAN_ACTION_START);
+ if (err) {
+ brcmf_set_mpc(ndev, 1);
+ cfg_priv->iscan_kickstart = false;
+ return err;
+ }
+ mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
+ iscan->timer_on = 1;
+ return err;
+}
+
+static s32
+__brcmf_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_scan_request *request,
+ struct cfg80211_ssid *this_ssid)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+ struct cfg80211_ssid *ssids;
+ struct brcmf_cfg80211_scan_req *sr = cfg_priv->scan_req_int;
+ __le32 passive_scan;
+ bool iscan_req;
+ bool spec_scan;
+ s32 err = 0;
+ u32 SSID_len;
+
+ if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
+ WL_ERR("Scanning already : status (%lu)\n", cfg_priv->status);
+ return -EAGAIN;
+ }
+ if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status)) {
+ WL_ERR("Scanning being aborted : status (%lu)\n",
+ cfg_priv->status);
+ return -EAGAIN;
+ }
+ if (test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) {
+ WL_ERR("Connecting : status (%lu)\n",
+ cfg_priv->status);
+ return -EAGAIN;
+ }
+
+ iscan_req = false;
+ spec_scan = false;
+ if (request) {
+ /* scan bss */
+ ssids = request->ssids;
+ if (cfg_priv->iscan_on && (!ssids || !ssids->ssid_len))
+ iscan_req = true;
+ } else {
+ /* scan in ibss */
+ /* we don't do iscan in ibss */
+ ssids = this_ssid;
+ }
+
+ cfg_priv->scan_request = request;
+ set_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+ if (iscan_req) {
+ err = brcmf_do_iscan(cfg_priv);
+ if (!err)
+ return err;
+ else
+ goto scan_out;
+ } else {
+ WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
+ ssids->ssid, ssids->ssid_len);
+ memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
+ SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
+ sr->ssid_le.SSID_len = cpu_to_le32(0);
+ if (SSID_len) {
+ memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
+ sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
+ spec_scan = true;
+ } else {
+ WL_SCAN("Broadcast scan\n");
+ }
+
+ passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
+ &passive_scan, sizeof(passive_scan));
+ if (err) {
+ WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
+ goto scan_out;
+ }
+ brcmf_set_mpc(ndev, 0);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le,
+ sizeof(sr->ssid_le));
+ if (err) {
+ if (err == -EBUSY)
+ WL_INFO("system busy : scan for \"%s\" "
+ "canceled\n", sr->ssid_le.SSID);
+ else
+ WL_ERR("WLC_SCAN error (%d)\n", err);
+
+ brcmf_set_mpc(ndev, 1);
+ goto scan_out;
+ }
+ }
+
+ return 0;
+
+scan_out:
+ clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+ cfg_priv->scan_request = NULL;
+ return err;
+}
+
+static s32
+brcmf_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_scan_request *request)
+{
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ err = __brcmf_cfg80211_scan(wiphy, ndev, request, NULL);
+ if (err)
+ WL_ERR("scan error (%d)\n", err);
+
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
+{
+ s32 err = 0;
+
+ err = brcmf_dev_intvar_set(ndev, "rtsthresh", rts_threshold);
+ if (err)
+ WL_ERR("Error (%d)\n", err);
+
+ return err;
+}
+
+static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
+{
+ s32 err = 0;
+
+ err = brcmf_dev_intvar_set(ndev, "fragthresh", frag_threshold);
+ if (err)
+ WL_ERR("Error (%d)\n", err);
+
+ return err;
+}
+
+static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
+{
+ s32 err = 0;
+ u32 cmd = (l ? BRCM_SET_LRL : BRCM_SET_SRL);
+
+ err = brcmf_exec_dcmd_u32(ndev, cmd, &retry);
+ if (err) {
+ WL_ERR("cmd (%d) , error (%d)\n", cmd, err);
+ return err;
+ }
+ return err;
+}
+
+static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg_priv);
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
+ (cfg_priv->conf->rts_threshold != wiphy->rts_threshold)) {
+ cfg_priv->conf->rts_threshold = wiphy->rts_threshold;
+ err = brcmf_set_rts(ndev, cfg_priv->conf->rts_threshold);
+ if (!err)
+ goto done;
+ }
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
+ (cfg_priv->conf->frag_threshold != wiphy->frag_threshold)) {
+ cfg_priv->conf->frag_threshold = wiphy->frag_threshold;
+ err = brcmf_set_frag(ndev, cfg_priv->conf->frag_threshold);
+ if (!err)
+ goto done;
+ }
+ if (changed & WIPHY_PARAM_RETRY_LONG
+ && (cfg_priv->conf->retry_long != wiphy->retry_long)) {
+ cfg_priv->conf->retry_long = wiphy->retry_long;
+ err = brcmf_set_retry(ndev, cfg_priv->conf->retry_long, true);
+ if (!err)
+ goto done;
+ }
+ if (changed & WIPHY_PARAM_RETRY_SHORT
+ && (cfg_priv->conf->retry_short != wiphy->retry_short)) {
+ cfg_priv->conf->retry_short = wiphy->retry_short;
+ err = brcmf_set_retry(ndev, cfg_priv->conf->retry_short, false);
+ if (!err)
+ goto done;
+ }
+
+done:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static void *brcmf_read_prof(struct brcmf_cfg80211_priv *cfg_priv, s32 item)
+{
+ switch (item) {
+ case WL_PROF_SEC:
+ return &cfg_priv->profile->sec;
+ case WL_PROF_BSSID:
+ return &cfg_priv->profile->bssid;
+ case WL_PROF_SSID:
+ return &cfg_priv->profile->ssid;
+ }
+ WL_ERR("invalid item (%d)\n", item);
+ return NULL;
+}
+
+static s32
+brcmf_update_prof(struct brcmf_cfg80211_priv *cfg_priv,
+ const struct brcmf_event_msg *e, void *data, s32 item)
+{
+ s32 err = 0;
+ struct brcmf_ssid *ssid;
+
+ switch (item) {
+ case WL_PROF_SSID:
+ ssid = (struct brcmf_ssid *) data;
+ memset(cfg_priv->profile->ssid.SSID, 0,
+ sizeof(cfg_priv->profile->ssid.SSID));
+ memcpy(cfg_priv->profile->ssid.SSID,
+ ssid->SSID, ssid->SSID_len);
+ cfg_priv->profile->ssid.SSID_len = ssid->SSID_len;
+ break;
+ case WL_PROF_BSSID:
+ if (data)
+ memcpy(cfg_priv->profile->bssid, data, ETH_ALEN);
+ else
+ memset(cfg_priv->profile->bssid, 0, ETH_ALEN);
+ break;
+ case WL_PROF_SEC:
+ memcpy(&cfg_priv->profile->sec, data,
+ sizeof(cfg_priv->profile->sec));
+ break;
+ case WL_PROF_BEACONINT:
+ cfg_priv->profile->beacon_interval = *(u16 *)data;
+ break;
+ case WL_PROF_DTIMPERIOD:
+ cfg_priv->profile->dtim_period = *(u8 *)data;
+ break;
+ default:
+ WL_ERR("unsupported item (%d)\n", item);
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
+{
+ memset(prof, 0, sizeof(*prof));
+}
+
+static void brcmf_ch_to_chanspec(int ch, struct brcmf_join_params *join_params,
+ size_t *join_params_size)
+{
+ u16 chanspec = 0;
+
+ if (ch != 0) {
+ if (ch <= CH_MAX_2G_CHANNEL)
+ chanspec |= WL_CHANSPEC_BAND_2G;
+ else
+ chanspec |= WL_CHANSPEC_BAND_5G;
+
+ chanspec |= WL_CHANSPEC_BW_20;
+ chanspec |= WL_CHANSPEC_CTL_SB_NONE;
+
+ *join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE +
+ sizeof(u16);
+
+ chanspec |= (ch & WL_CHANSPEC_CHAN_MASK);
+ join_params->params_le.chanspec_list[0] = cpu_to_le16(chanspec);
+ join_params->params_le.chanspec_num = cpu_to_le32(1);
+
+ WL_CONN("join_params->params.chanspec_list[0]= %#X,"
+ "channel %d, chanspec %#X\n",
+ chanspec, ch, chanspec);
+ }
+}
+
+static void brcmf_link_down(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct net_device *ndev = NULL;
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+
+ if (cfg_priv->link_up) {
+ ndev = cfg_to_ndev(cfg_priv);
+ WL_INFO("Call WLC_DISASSOC to stop excess roaming\n ");
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_DISASSOC, NULL, 0);
+ if (err)
+ WL_ERR("WLC_DISASSOC failed (%d)\n", err);
+ cfg_priv->link_up = false;
+ }
+ WL_TRACE("Exit\n");
+}
+
+static s32
+brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_ibss_params *params)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct brcmf_join_params join_params;
+ size_t join_params_size = 0;
+ s32 err = 0;
+ s32 wsec = 0;
+ s32 bcnprd;
+ struct brcmf_ssid ssid;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ if (params->ssid)
+ WL_CONN("SSID: %s\n", params->ssid);
+ else {
+ WL_CONN("SSID: NULL, Not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ set_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+
+ if (params->bssid)
+ WL_CONN("BSSID: %02X %02X %02X %02X %02X %02X\n",
+ params->bssid[0], params->bssid[1], params->bssid[2],
+ params->bssid[3], params->bssid[4], params->bssid[5]);
+ else
+ WL_CONN("No BSSID specified\n");
+
+ if (params->channel)
+ WL_CONN("channel: %d\n", params->channel->center_freq);
+ else
+ WL_CONN("no channel specified\n");
+
+ if (params->channel_fixed)
+ WL_CONN("fixed channel required\n");
+ else
+ WL_CONN("no fixed channel required\n");
+
+ if (params->ie && params->ie_len)
+ WL_CONN("ie len: %d\n", params->ie_len);
+ else
+ WL_CONN("no ie specified\n");
+
+ if (params->beacon_interval)
+ WL_CONN("beacon interval: %d\n", params->beacon_interval);
+ else
+ WL_CONN("no beacon interval specified\n");
+
+ if (params->basic_rates)
+ WL_CONN("basic rates: %08X\n", params->basic_rates);
+ else
+ WL_CONN("no basic rates specified\n");
+
+ if (params->privacy)
+ WL_CONN("privacy required\n");
+ else
+ WL_CONN("no privacy required\n");
+
+ /* Configure Privacy for starter */
+ if (params->privacy)
+ wsec |= WEP_ENABLED;
+
+ err = brcmf_dev_intvar_set(ndev, "wsec", wsec);
+ if (err) {
+ WL_ERR("wsec failed (%d)\n", err);
+ goto done;
+ }
+
+ /* Configure Beacon Interval for starter */
+ if (params->beacon_interval)
+ bcnprd = params->beacon_interval;
+ else
+ bcnprd = 100;
+
+ err = brcmf_exec_dcmd_u32(ndev, BRCM_SET_BCNPRD, &bcnprd);
+ if (err) {
+ WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err);
+ goto done;
+ }
+
+ /* Configure required join parameter */
+ memset(&join_params, 0, sizeof(struct brcmf_join_params));
+
+ /* SSID */
+ ssid.SSID_len = min_t(u32, params->ssid_len, 32);
+ memcpy(ssid.SSID, params->ssid, ssid.SSID_len);
+ memcpy(join_params.ssid_le.SSID, params->ssid, ssid.SSID_len);
+ join_params.ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len);
+ join_params_size = sizeof(join_params.ssid_le);
+ brcmf_update_prof(cfg_priv, NULL, &ssid, WL_PROF_SSID);
+
+ /* BSSID */
+ if (params->bssid) {
+ memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
+ join_params_size = sizeof(join_params.ssid_le) +
+ BRCMF_ASSOC_PARAMS_FIXED_SIZE;
+ } else {
+ memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN);
+ }
+
+ brcmf_update_prof(cfg_priv, NULL,
+ &join_params.params_le.bssid, WL_PROF_BSSID);
+
+ /* Channel */
+ if (params->channel) {
+ u32 target_channel;
+
+ cfg_priv->channel =
+ ieee80211_frequency_to_channel(
+ params->channel->center_freq);
+ if (params->channel_fixed) {
+ /* adding chanspec */
+ brcmf_ch_to_chanspec(cfg_priv->channel,
+ &join_params, &join_params_size);
+ }
+
+ /* set channel for starter */
+ target_channel = cfg_priv->channel;
+ err = brcmf_exec_dcmd_u32(ndev, BRCM_SET_CHANNEL,
+ &target_channel);
+ if (err) {
+ WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err);
+ goto done;
+ }
+ } else
+ cfg_priv->channel = 0;
+
+ cfg_priv->ibss_starter = false;
+
+
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID,
+ &join_params, join_params_size);
+ if (err) {
+ WL_ERR("WLC_SET_SSID failed (%d)\n", err);
+ goto done;
+ }
+
+done:
+ if (err)
+ clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ brcmf_link_down(cfg_priv);
+
+ WL_TRACE("Exit\n");
+
+ return err;
+}
+
+static s32 brcmf_set_wpa_version(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 val = 0;
+ s32 err = 0;
+
+ if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
+ else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+ else
+ val = WPA_AUTH_DISABLED;
+ WL_CONN("setting wpa_auth to 0x%0x\n", val);
+ err = brcmf_dev_intvar_set(ndev, "wpa_auth", val);
+ if (err) {
+ WL_ERR("set wpa_auth failed (%d)\n", err);
+ return err;
+ }
+ sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+ sec->wpa_versions = sme->crypto.wpa_versions;
+ return err;
+}
+
+static s32 brcmf_set_auth_type(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 val = 0;
+ s32 err = 0;
+
+ switch (sme->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ val = 0;
+ WL_CONN("open system\n");
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ val = 1;
+ WL_CONN("shared key\n");
+ break;
+ case NL80211_AUTHTYPE_AUTOMATIC:
+ val = 2;
+ WL_CONN("automatic\n");
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ WL_CONN("network eap\n");
+ default:
+ val = 2;
+ WL_ERR("invalid auth type (%d)\n", sme->auth_type);
+ break;
+ }
+
+ err = brcmf_dev_intvar_set(ndev, "auth", val);
+ if (err) {
+ WL_ERR("set auth failed (%d)\n", err);
+ return err;
+ }
+ sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+ sec->auth_type = sme->auth_type;
+ return err;
+}
+
+static s32
+brcmf_set_set_cipher(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 pval = 0;
+ s32 gval = 0;
+ s32 err = 0;
+
+ if (sme->crypto.n_ciphers_pairwise) {
+ switch (sme->crypto.ciphers_pairwise[0]) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ pval = WEP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ pval = TKIP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ pval = AES_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ pval = AES_ENABLED;
+ break;
+ default:
+ WL_ERR("invalid cipher pairwise (%d)\n",
+ sme->crypto.ciphers_pairwise[0]);
+ return -EINVAL;
+ }
+ }
+ if (sme->crypto.cipher_group) {
+ switch (sme->crypto.cipher_group) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ gval = WEP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ gval = TKIP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ gval = AES_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ gval = AES_ENABLED;
+ break;
+ default:
+ WL_ERR("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
+ }
+ }
+
+ WL_CONN("pval (%d) gval (%d)\n", pval, gval);
+ err = brcmf_dev_intvar_set(ndev, "wsec", pval | gval);
+ if (err) {
+ WL_ERR("error (%d)\n", err);
+ return err;
+ }
+
+ sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+ sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
+ sec->cipher_group = sme->crypto.cipher_group;
+
+ return err;
+}
+
+static s32
+brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 val = 0;
+ s32 err = 0;
+
+ if (sme->crypto.n_akm_suites) {
+ err = brcmf_dev_intvar_get(ndev, "wpa_auth", &val);
+ if (err) {
+ WL_ERR("could not get wpa_auth (%d)\n", err);
+ return err;
+ }
+ if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
+ switch (sme->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_8021X:
+ val = WPA_AUTH_UNSPECIFIED;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ val = WPA_AUTH_PSK;
+ break;
+ default:
+ WL_ERR("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
+ }
+ } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+ switch (sme->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_8021X:
+ val = WPA2_AUTH_UNSPECIFIED;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ val = WPA2_AUTH_PSK;
+ break;
+ default:
+ WL_ERR("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
+ }
+ }
+
+ WL_CONN("setting wpa_auth to %d\n", val);
+ err = brcmf_dev_intvar_set(ndev, "wpa_auth", val);
+ if (err) {
+ WL_ERR("could not set wpa_auth (%d)\n", err);
+ return err;
+ }
+ }
+ sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+ sec->wpa_auth = sme->crypto.akm_suites[0];
+
+ return err;
+}
+
+static s32
+brcmf_set_wep_sharedkey(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+ struct brcmf_cfg80211_security *sec;
+ struct brcmf_wsec_key key;
+ s32 val;
+ s32 err = 0;
+
+ WL_CONN("key len (%d)\n", sme->key_len);
+
+ if (sme->key_len == 0)
+ return 0;
+
+ sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+ WL_CONN("wpa_versions 0x%x cipher_pairwise 0x%x\n",
+ sec->wpa_versions, sec->cipher_pairwise);
+
+ if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
+ return 0;
+
+ if (sec->cipher_pairwise &
+ (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)) {
+ memset(&key, 0, sizeof(key));
+ key.len = (u32) sme->key_len;
+ key.index = (u32) sme->key_idx;
+ if (key.len > sizeof(key.data)) {
+ WL_ERR("Too long key length (%u)\n", key.len);
+ return -EINVAL;
+ }
+ memcpy(key.data, sme->key, key.len);
+ key.flags = BRCMF_PRIMARY_KEY;
+ switch (sec->cipher_pairwise) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ key.algo = CRYPTO_ALGO_WEP1;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key.algo = CRYPTO_ALGO_WEP128;
+ break;
+ default:
+ WL_ERR("Invalid algorithm (%d)\n",
+ sme->crypto.ciphers_pairwise[0]);
+ return -EINVAL;
+ }
+ /* Set the new key/index */
+ WL_CONN("key length (%d) key index (%d) algo (%d)\n",
+ key.len, key.index, key.algo);
+ WL_CONN("key \"%s\"\n", key.data);
+ err = send_key_to_dongle(ndev, &key);
+ if (err)
+ return err;
+
+ if (sec->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) {
+ WL_CONN("set auth_type to shared key\n");
+ val = 1; /* shared key */
+ err = brcmf_dev_intvar_set(ndev, "auth", val);
+ if (err) {
+ WL_ERR("set auth failed (%d)\n", err);
+ return err;
+ }
+ }
+ }
+ return err;
+}
+
+static s32
+brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct ieee80211_channel *chan = sme->channel;
+ struct brcmf_join_params join_params;
+ size_t join_params_size;
+ struct brcmf_ssid ssid;
+
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ if (!sme->ssid) {
+ WL_ERR("Invalid ssid\n");
+ return -EOPNOTSUPP;
+ }
+
+ set_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+
+ if (chan) {
+ cfg_priv->channel =
+ ieee80211_frequency_to_channel(chan->center_freq);
+ WL_CONN("channel (%d), center_req (%d)\n",
+ cfg_priv->channel, chan->center_freq);
+ } else
+ cfg_priv->channel = 0;
+
+ WL_INFO("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
+
+ err = brcmf_set_wpa_version(ndev, sme);
+ if (err) {
+ WL_ERR("wl_set_wpa_version failed (%d)\n", err);
+ goto done;
+ }
+
+ err = brcmf_set_auth_type(ndev, sme);
+ if (err) {
+ WL_ERR("wl_set_auth_type failed (%d)\n", err);
+ goto done;
+ }
+
+ err = brcmf_set_set_cipher(ndev, sme);
+ if (err) {
+ WL_ERR("wl_set_set_cipher failed (%d)\n", err);
+ goto done;
+ }
+
+ err = brcmf_set_key_mgmt(ndev, sme);
+ if (err) {
+ WL_ERR("wl_set_key_mgmt failed (%d)\n", err);
+ goto done;
+ }
+
+ err = brcmf_set_wep_sharedkey(ndev, sme);
+ if (err) {
+ WL_ERR("brcmf_set_wep_sharedkey failed (%d)\n", err);
+ goto done;
+ }
+
+ memset(&join_params, 0, sizeof(join_params));
+ join_params_size = sizeof(join_params.ssid_le);
+
+ ssid.SSID_len = min_t(u32, sizeof(ssid.SSID), sme->ssid_len);
+ memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid.SSID_len);
+ memcpy(&ssid.SSID, sme->ssid, ssid.SSID_len);
+ join_params.ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len);
+ brcmf_update_prof(cfg_priv, NULL, &ssid, WL_PROF_SSID);
+
+ memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN);
+
+ if (ssid.SSID_len < IEEE80211_MAX_SSID_LEN)
+ WL_CONN("ssid \"%s\", len (%d)\n",
+ ssid.SSID, ssid.SSID_len);
+
+ brcmf_ch_to_chanspec(cfg_priv->channel,
+ &join_params, &join_params_size);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID,
+ &join_params, join_params_size);
+ if (err)
+ WL_ERR("WLC_SET_SSID failed (%d)\n", err);
+
+done:
+ if (err)
+ clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
+ u16 reason_code)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct brcmf_scb_val_le scbval;
+ s32 err = 0;
+
+ WL_TRACE("Enter. Reason code = %d\n", reason_code);
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ clear_bit(WL_STATUS_CONNECTED, &cfg_priv->status);
+
+ memcpy(&scbval.ea, brcmf_read_prof(cfg_priv, WL_PROF_BSSID), ETH_ALEN);
+ scbval.val = cpu_to_le32(reason_code);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_DISASSOC, &scbval,
+ sizeof(struct brcmf_scb_val_le));
+ if (err)
+ WL_ERR("error (%d)\n", err);
+
+ cfg_priv->link_up = false;
+
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_set_tx_power(struct wiphy *wiphy,
+ enum nl80211_tx_power_setting type, s32 dbm)
+{
+
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg_priv);
+ u16 txpwrmw;
+ s32 err = 0;
+ s32 disable = 0;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ switch (type) {
+ case NL80211_TX_POWER_AUTOMATIC:
+ break;
+ case NL80211_TX_POWER_LIMITED:
+ if (dbm < 0) {
+ WL_ERR("TX_POWER_LIMITED - dbm is negative\n");
+ err = -EINVAL;
+ goto done;
+ }
+ break;
+ case NL80211_TX_POWER_FIXED:
+ if (dbm < 0) {
+ WL_ERR("TX_POWER_FIXED - dbm is negative\n");
+ err = -EINVAL;
+ goto done;
+ }
+ break;
+ }
+ /* Make sure radio is off or on as far as software is concerned */
+ disable = WL_RADIO_SW_DISABLE << 16;
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_RADIO, &disable);
+ if (err)
+ WL_ERR("WLC_SET_RADIO error (%d)\n", err);
+
+ if (dbm > 0xffff)
+ txpwrmw = 0xffff;
+ else
+ txpwrmw = (u16) dbm;
+ err = brcmf_dev_intvar_set(ndev, "qtxpower",
+ (s32) (brcmf_mw_to_qdbm(txpwrmw)));
+ if (err)
+ WL_ERR("qtxpower error (%d)\n", err);
+ cfg_priv->conf->tx_power = dbm;
+
+done:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg_priv);
+ s32 txpwrdbm;
+ u8 result;
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ err = brcmf_dev_intvar_get(ndev, "qtxpower", &txpwrdbm);
+ if (err) {
+ WL_ERR("error (%d)\n", err);
+ goto done;
+ }
+
+ result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
+ *dbm = (s32) brcmf_qdbm_to_mw(result);
+
+done:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool unicast, bool multicast)
+{
+ u32 index;
+ u32 wsec;
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+ WL_CONN("key index (%d)\n", key_idx);
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_GET_WSEC, &wsec);
+ if (err) {
+ WL_ERR("WLC_GET_WSEC error (%d)\n", err);
+ goto done;
+ }
+
+ if (wsec & WEP_ENABLED) {
+ /* Just select a new current key */
+ index = key_idx;
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_KEY_PRIMARY,
+ &index);
+ if (err)
+ WL_ERR("error (%d)\n", err);
+ }
+done:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, const u8 *mac_addr, struct key_params *params)
+{
+ struct brcmf_wsec_key key;
+ struct brcmf_wsec_key_le key_le;
+ s32 err = 0;
+
+ memset(&key, 0, sizeof(key));
+ key.index = (u32) key_idx;
+ /* Instead of bcast for ea address for default wep keys,
+ driver needs it to be Null */
+ if (!is_multicast_ether_addr(mac_addr))
+ memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
+ key.len = (u32) params->key_len;
+ /* check for key index change */
+ if (key.len == 0) {
+ /* key delete */
+ err = send_key_to_dongle(ndev, &key);
+ if (err)
+ return err;
+ } else {
+ if (key.len > sizeof(key.data)) {
+ WL_ERR("Invalid key length (%d)\n", key.len);
+ return -EINVAL;
+ }
+
+ WL_CONN("Setting the key index %d\n", key.index);
+ memcpy(key.data, params->key, key.len);
+
+ if (params->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ u8 keybuf[8];
+ memcpy(keybuf, &key.data[24], sizeof(keybuf));
+ memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
+ memcpy(&key.data[16], keybuf, sizeof(keybuf));
+ }
+
+ /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
+ if (params->seq && params->seq_len == 6) {
+ /* rx iv */
+ u8 *ivptr;
+ ivptr = (u8 *) params->seq;
+ key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
+ (ivptr[3] << 8) | ivptr[2];
+ key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
+ key.iv_initialized = true;
+ }
+
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ key.algo = CRYPTO_ALGO_WEP1;
+ WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key.algo = CRYPTO_ALGO_WEP128;
+ WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ key.algo = CRYPTO_ALGO_TKIP;
+ WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
+ break;
+ default:
+ WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
+ return -EINVAL;
+ }
+ convert_key_from_CPU(&key, &key_le);
+
+ brcmf_netdev_wait_pend8021x(ndev);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_KEY, &key_le,
+ sizeof(key_le));
+ if (err) {
+ WL_ERR("WLC_SET_KEY error (%d)\n", err);
+ return err;
+ }
+ }
+ return err;
+}
+
+static s32
+brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ struct brcmf_wsec_key key;
+ s32 val;
+ s32 wsec;
+ s32 err = 0;
+ u8 keybuf[8];
+
+ WL_TRACE("Enter\n");
+ WL_CONN("key index (%d)\n", key_idx);
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ if (mac_addr) {
+ WL_TRACE("Exit");
+ return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
+ }
+ memset(&key, 0, sizeof(key));
+
+ key.len = (u32) params->key_len;
+ key.index = (u32) key_idx;
+
+ if (key.len > sizeof(key.data)) {
+ WL_ERR("Too long key length (%u)\n", key.len);
+ err = -EINVAL;
+ goto done;
+ }
+ memcpy(key.data, params->key, key.len);
+
+ key.flags = BRCMF_PRIMARY_KEY;
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ key.algo = CRYPTO_ALGO_WEP1;
+ WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key.algo = CRYPTO_ALGO_WEP128;
+ WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ memcpy(keybuf, &key.data[24], sizeof(keybuf));
+ memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
+ memcpy(&key.data[16], keybuf, sizeof(keybuf));
+ key.algo = CRYPTO_ALGO_TKIP;
+ WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
+ break;
+ default:
+ WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
+ err = -EINVAL;
+ goto done;
+ }
+
+ err = send_key_to_dongle(ndev, &key); /* Set the new key/index */
+ if (err)
+ goto done;
+
+ val = WEP_ENABLED;
+ err = brcmf_dev_intvar_get(ndev, "wsec", &wsec);
+ if (err) {
+ WL_ERR("get wsec error (%d)\n", err);
+ goto done;
+ }
+ wsec &= ~(WEP_ENABLED);
+ wsec |= val;
+ err = brcmf_dev_intvar_set(ndev, "wsec", wsec);
+ if (err) {
+ WL_ERR("set wsec error (%d)\n", err);
+ goto done;
+ }
+
+ val = 1; /* assume shared key. otherwise 0 */
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_AUTH, &val);
+ if (err)
+ WL_ERR("WLC_SET_AUTH error (%d)\n", err);
+done:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr)
+{
+ struct brcmf_wsec_key key;
+ s32 err = 0;
+ s32 val;
+ s32 wsec;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ memset(&key, 0, sizeof(key));
+
+ key.index = (u32) key_idx;
+ key.flags = BRCMF_PRIMARY_KEY;
+ key.algo = CRYPTO_ALGO_OFF;
+
+ WL_CONN("key index (%d)\n", key_idx);
+
+ /* Set the new key/index */
+ err = send_key_to_dongle(ndev, &key);
+ if (err) {
+ if (err == -EINVAL) {
+ if (key.index >= DOT11_MAX_DEFAULT_KEYS)
+ /* we ignore this key index in this case */
+ WL_ERR("invalid key index (%d)\n", key_idx);
+ }
+ /* Ignore this error, may happen during DISASSOC */
+ err = -EAGAIN;
+ goto done;
+ }
+
+ val = 0;
+ err = brcmf_dev_intvar_get(ndev, "wsec", &wsec);
+ if (err) {
+ WL_ERR("get wsec error (%d)\n", err);
+ /* Ignore this error, may happen during DISASSOC */
+ err = -EAGAIN;
+ goto done;
+ }
+ wsec &= ~(WEP_ENABLED);
+ wsec |= val;
+ err = brcmf_dev_intvar_set(ndev, "wsec", wsec);
+ if (err) {
+ WL_ERR("set wsec error (%d)\n", err);
+ /* Ignore this error, may happen during DISASSOC */
+ err = -EAGAIN;
+ goto done;
+ }
+
+ val = 0; /* assume open key. otherwise 1 */
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_AUTH, &val);
+ if (err) {
+ WL_ERR("WLC_SET_AUTH error (%d)\n", err);
+ /* Ignore this error, may happen during DISASSOC */
+ err = -EAGAIN;
+ }
+done:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
+ void (*callback) (void *cookie, struct key_params * params))
+{
+ struct key_params params;
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct brcmf_cfg80211_security *sec;
+ s32 wsec;
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+ WL_CONN("key index (%d)\n", key_idx);
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ memset(&params, 0, sizeof(params));
+
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_GET_WSEC, &wsec);
+ if (err) {
+ WL_ERR("WLC_GET_WSEC error (%d)\n", err);
+ /* Ignore this error, may happen during DISASSOC */
+ err = -EAGAIN;
+ goto done;
+ }
+ switch (wsec) {
+ case WEP_ENABLED:
+ sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+ if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
+ params.cipher = WLAN_CIPHER_SUITE_WEP40;
+ WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
+ } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
+ params.cipher = WLAN_CIPHER_SUITE_WEP104;
+ WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
+ }
+ break;
+ case TKIP_ENABLED:
+ params.cipher = WLAN_CIPHER_SUITE_TKIP;
+ WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
+ break;
+ case AES_ENABLED:
+ params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
+ WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
+ break;
+ default:
+ WL_ERR("Invalid algo (0x%x)\n", wsec);
+ err = -EINVAL;
+ goto done;
+ }
+ callback(cookie, &params);
+
+done:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
+ struct net_device *ndev, u8 key_idx)
+{
+ WL_INFO("Not supported\n");
+
+ return -EOPNOTSUPP;
+}
+
+static s32
+brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
+ u8 *mac, struct station_info *sinfo)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct brcmf_scb_val_le scb_val;
+ int rssi;
+ s32 rate;
+ s32 err = 0;
+ u8 *bssid = brcmf_read_prof(cfg_priv, WL_PROF_BSSID);
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ if (memcmp(mac, bssid, ETH_ALEN)) {
+ WL_ERR("Wrong Mac address cfg_mac-%X:%X:%X:%X:%X:%X"
+ "wl_bssid-%X:%X:%X:%X:%X:%X\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ bssid[0], bssid[1], bssid[2], bssid[3],
+ bssid[4], bssid[5]);
+ err = -ENOENT;
+ goto done;
+ }
+
+ /* Report the current tx rate */
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_GET_RATE, &rate);
+ if (err) {
+ WL_ERR("Could not get rate (%d)\n", err);
+ } else {
+ sinfo->filled |= STATION_INFO_TX_BITRATE;
+ sinfo->txrate.legacy = rate * 5;
+ WL_CONN("Rate %d Mbps\n", rate / 2);
+ }
+
+ if (test_bit(WL_STATUS_CONNECTED, &cfg_priv->status)) {
+ scb_val.val = cpu_to_le32(0);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_RSSI, &scb_val,
+ sizeof(struct brcmf_scb_val_le));
+ if (err)
+ WL_ERR("Could not get rssi (%d)\n", err);
+
+ rssi = le32_to_cpu(scb_val.val);
+ sinfo->filled |= STATION_INFO_SIGNAL;
+ sinfo->signal = rssi;
+ WL_CONN("RSSI %d dBm\n", rssi);
+ }
+
+done:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
+ bool enabled, s32 timeout)
+{
+ s32 pm;
+ s32 err = 0;
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+
+ WL_TRACE("Enter\n");
+
+ /*
+ * Powersave enable/disable request is coming from the
+ * cfg80211 even before the interface is up. In that
+ * scenario, driver will be storing the power save
+ * preference in cfg_priv struct to apply this to
+ * FW later while initializing the dongle
+ */
+ cfg_priv->pwr_save = enabled;
+ if (!test_bit(WL_STATUS_READY, &cfg_priv->status)) {
+
+ WL_INFO("Device is not ready,"
+ "storing the value in cfg_priv struct\n");
+ goto done;
+ }
+
+ pm = enabled ? PM_FAST : PM_OFF;
+ WL_INFO("power save %s\n", (pm ? "enabled" : "disabled"));
+
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_PM, &pm);
+ if (err) {
+ if (err == -ENODEV)
+ WL_ERR("net_device is not ready yet\n");
+ else
+ WL_ERR("error (%d)\n", err);
+ }
+done:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev,
+ const u8 *addr,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct brcm_rateset_le rateset_le;
+ s32 rate;
+ s32 val;
+ s32 err_bg;
+ s32 err_a;
+ u32 legacy;
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ /* addr param is always NULL. ignore it */
+ /* Get current rateset */
+ err = brcmf_exec_dcmd(ndev, BRCM_GET_CURR_RATESET, &rateset_le,
+ sizeof(rateset_le));
+ if (err) {
+ WL_ERR("could not get current rateset (%d)\n", err);
+ goto done;
+ }
+
+ legacy = ffs(mask->control[IEEE80211_BAND_2GHZ].legacy & 0xFFFF);
+ if (!legacy)
+ legacy = ffs(mask->control[IEEE80211_BAND_5GHZ].legacy &
+ 0xFFFF);
+
+ val = wl_g_rates[legacy - 1].bitrate * 100000;
+
+ if (val < le32_to_cpu(rateset_le.count))
+ /* Select rate by rateset index */
+ rate = rateset_le.rates[val] & 0x7f;
+ else
+ /* Specified rate in bps */
+ rate = val / 500000;
+
+ WL_CONN("rate %d mbps\n", rate / 2);
+
+ /*
+ *
+ * Set rate override,
+ * Since the is a/b/g-blind, both a/bg_rate are enforced.
+ */
+ err_bg = brcmf_dev_intvar_set(ndev, "bg_rate", rate);
+ err_a = brcmf_dev_intvar_set(ndev, "a_rate", rate);
+ if (err_bg && err_a) {
+ WL_ERR("could not set fixed rate (%d) (%d)\n", err_bg, err_a);
+ err = err_bg | err_a;
+ }
+
+done:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_priv *cfg_priv,
+ struct brcmf_bss_info *bi)
+{
+ struct wiphy *wiphy = cfg_to_wiphy(cfg_priv);
+ struct ieee80211_channel *notify_channel;
+ struct cfg80211_bss *bss;
+ struct ieee80211_supported_band *band;
+ s32 err = 0;
+ u16 channel;
+ u32 freq;
+ u64 notify_timestamp;
+ u16 notify_capability;
+ u16 notify_interval;
+ u8 *notify_ie;
+ size_t notify_ielen;
+ s32 notify_signal;
+
+ if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
+ WL_ERR("Bss info is larger than buffer. Discarding\n");
+ return 0;
+ }
+
+ channel = bi->ctl_ch ? bi->ctl_ch :
+ CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
+
+ if (channel <= CH_MAX_2G_CHANNEL)
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
+ else
+ band = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+ freq = ieee80211_channel_to_frequency(channel, band->band);
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+ notify_timestamp = jiffies_to_msecs(jiffies)*1000; /* uSec */
+ notify_capability = le16_to_cpu(bi->capability);
+ notify_interval = le16_to_cpu(bi->beacon_period);
+ notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
+ notify_ielen = le32_to_cpu(bi->ie_length);
+ notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
+
+ WL_CONN("bssid: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+ bi->BSSID[0], bi->BSSID[1], bi->BSSID[2],
+ bi->BSSID[3], bi->BSSID[4], bi->BSSID[5]);
+ WL_CONN("Channel: %d(%d)\n", channel, freq);
+ WL_CONN("Capability: %X\n", notify_capability);
+ WL_CONN("Beacon interval: %d\n", notify_interval);
+ WL_CONN("Signal: %d\n", notify_signal);
+ WL_CONN("notify_timestamp: %#018llx\n", notify_timestamp);
+
+ bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID,
+ notify_timestamp, notify_capability, notify_interval, notify_ie,
+ notify_ielen, notify_signal, GFP_KERNEL);
+
+ if (!bss) {
+ WL_ERR("cfg80211_inform_bss_frame error\n");
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+static s32 brcmf_inform_bss(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_scan_results *bss_list;
+ struct brcmf_bss_info *bi = NULL; /* must be initialized */
+ s32 err = 0;
+ int i;
+
+ bss_list = cfg_priv->bss_list;
+ if (bss_list->version != BRCMF_BSS_INFO_VERSION) {
+ WL_ERR("Version %d != WL_BSS_INFO_VERSION\n",
+ bss_list->version);
+ return -EOPNOTSUPP;
+ }
+ WL_SCAN("scanned AP count (%d)\n", bss_list->count);
+ for (i = 0; i < bss_list->count && i < WL_AP_MAX; i++) {
+ bi = next_bss(bss_list, bi);
+ err = brcmf_inform_single_bss(cfg_priv, bi);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+static s32 wl_inform_ibss(struct brcmf_cfg80211_priv *cfg_priv,
+ struct net_device *ndev, const u8 *bssid)
+{
+ struct wiphy *wiphy = cfg_to_wiphy(cfg_priv);
+ struct ieee80211_channel *notify_channel;
+ struct brcmf_bss_info *bi = NULL;
+ struct ieee80211_supported_band *band;
+ u8 *buf = NULL;
+ s32 err = 0;
+ u16 channel;
+ u32 freq;
+ u64 notify_timestamp;
+ u16 notify_capability;
+ u16 notify_interval;
+ u8 *notify_ie;
+ size_t notify_ielen;
+ s32 notify_signal;
+
+ WL_TRACE("Enter\n");
+
+ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ goto CleanUp;
+ }
+
+ *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
+
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_BSS_INFO, buf, WL_BSS_INFO_MAX);
+ if (err) {
+ WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err);
+ goto CleanUp;
+ }
+
+ bi = (struct brcmf_bss_info *)(buf + 4);
+
+ channel = bi->ctl_ch ? bi->ctl_ch :
+ CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
+
+ if (channel <= CH_MAX_2G_CHANNEL)
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
+ else
+ band = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+ freq = ieee80211_channel_to_frequency(channel, band->band);
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+ notify_timestamp = jiffies_to_msecs(jiffies)*1000; /* uSec */
+ notify_capability = le16_to_cpu(bi->capability);
+ notify_interval = le16_to_cpu(bi->beacon_period);
+ notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
+ notify_ielen = le32_to_cpu(bi->ie_length);
+ notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
+
+ WL_CONN("channel: %d(%d)\n", channel, freq);
+ WL_CONN("capability: %X\n", notify_capability);
+ WL_CONN("beacon interval: %d\n", notify_interval);
+ WL_CONN("signal: %d\n", notify_signal);
+ WL_CONN("notify_timestamp: %#018llx\n", notify_timestamp);
+
+ cfg80211_inform_bss(wiphy, notify_channel, bssid,
+ notify_timestamp, notify_capability, notify_interval,
+ notify_ie, notify_ielen, notify_signal, GFP_KERNEL);
+
+CleanUp:
+
+ kfree(buf);
+
+ WL_TRACE("Exit\n");
+
+ return err;
+}
+
+static bool brcmf_is_ibssmode(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ return cfg_priv->conf->mode == WL_MODE_IBSS;
+}
+
+/*
+ * Traverse a string of 1-byte tag/1-byte length/variable-length value
+ * triples, returning a pointer to the substring whose first element
+ * matches tag
+ */
+static struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
+{
+ struct brcmf_tlv *elt;
+ int totlen;
+
+ elt = (struct brcmf_tlv *) buf;
+ totlen = buflen;
+
+ /* find tagged parameter */
+ while (totlen >= 2) {
+ int len = elt->len;
+
+ /* validate remaining totlen */
+ if ((elt->id == key) && (totlen >= (len + 2)))
+ return elt;
+
+ elt = (struct brcmf_tlv *) ((u8 *) elt + (len + 2));
+ totlen -= (len + 2);
+ }
+
+ return NULL;
+}
+
+static s32 brcmf_update_bss_info(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_bss_info *bi;
+ struct brcmf_ssid *ssid;
+ struct brcmf_tlv *tim;
+ u16 beacon_interval;
+ u8 dtim_period;
+ size_t ie_len;
+ u8 *ie;
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+ if (brcmf_is_ibssmode(cfg_priv))
+ return err;
+
+ ssid = (struct brcmf_ssid *)brcmf_read_prof(cfg_priv, WL_PROF_SSID);
+
+ *(__le32 *)cfg_priv->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
+ err = brcmf_exec_dcmd(cfg_to_ndev(cfg_priv), BRCMF_C_GET_BSS_INFO,
+ cfg_priv->extra_buf, WL_EXTRA_BUF_MAX);
+ if (err) {
+ WL_ERR("Could not get bss info %d\n", err);
+ goto update_bss_info_out;
+ }
+
+ bi = (struct brcmf_bss_info *)(cfg_priv->extra_buf + 4);
+ err = brcmf_inform_single_bss(cfg_priv, bi);
+ if (err)
+ goto update_bss_info_out;
+
+ ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
+ ie_len = le32_to_cpu(bi->ie_length);
+ beacon_interval = le16_to_cpu(bi->beacon_period);
+
+ tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
+ if (tim)
+ dtim_period = tim->data[1];
+ else {
+ /*
+ * active scan was done so we could not get dtim
+ * information out of probe response.
+ * so we speficially query dtim information to dongle.
+ */
+ u32 var;
+ err = brcmf_dev_intvar_get(cfg_to_ndev(cfg_priv),
+ "dtim_assoc", &var);
+ if (err) {
+ WL_ERR("wl dtim_assoc failed (%d)\n", err);
+ goto update_bss_info_out;
+ }
+ dtim_period = (u8)var;
+ }
+
+ brcmf_update_prof(cfg_priv, NULL, &beacon_interval, WL_PROF_BEACONINT);
+ brcmf_update_prof(cfg_priv, NULL, &dtim_period, WL_PROF_DTIMPERIOD);
+
+update_bss_info_out:
+ WL_TRACE("Exit");
+ return err;
+}
+
+static void brcmf_term_iscan(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv);
+ struct brcmf_ssid ssid;
+
+ if (cfg_priv->iscan_on) {
+ iscan->state = WL_ISCAN_STATE_IDLE;
+
+ if (iscan->timer_on) {
+ del_timer_sync(&iscan->timer);
+ iscan->timer_on = 0;
+ }
+
+ cancel_work_sync(&iscan->work);
+
+ /* Abort iscan running in FW */
+ memset(&ssid, 0, sizeof(ssid));
+ brcmf_run_iscan(iscan, &ssid, WL_SCAN_ACTION_ABORT);
+ }
+}
+
+static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan,
+ bool aborted)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = iscan_to_cfg(iscan);
+ struct net_device *ndev = cfg_to_ndev(cfg_priv);
+
+ if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
+ WL_ERR("Scan complete while device not scanning\n");
+ return;
+ }
+ if (cfg_priv->scan_request) {
+ WL_SCAN("ISCAN Completed scan: %s\n",
+ aborted ? "Aborted" : "Done");
+ cfg80211_scan_done(cfg_priv->scan_request, aborted);
+ brcmf_set_mpc(ndev, 1);
+ cfg_priv->scan_request = NULL;
+ }
+ cfg_priv->iscan_kickstart = false;
+}
+
+static s32 brcmf_wakeup_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan)
+{
+ if (iscan->state != WL_ISCAN_STATE_IDLE) {
+ WL_SCAN("wake up iscan\n");
+ schedule_work(&iscan->work);
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static s32
+brcmf_get_iscan_results(struct brcmf_cfg80211_iscan_ctrl *iscan, u32 *status,
+ struct brcmf_scan_results **bss_list)
+{
+ struct brcmf_iscan_results list;
+ struct brcmf_scan_results *results;
+ struct brcmf_scan_results_le *results_le;
+ struct brcmf_iscan_results *list_buf;
+ s32 err = 0;
+
+ memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX);
+ list_buf = (struct brcmf_iscan_results *)iscan->scan_buf;
+ results = &list_buf->results;
+ results_le = &list_buf->results_le;
+ results->buflen = BRCMF_ISCAN_RESULTS_FIXED_SIZE;
+ results->version = 0;
+ results->count = 0;
+
+ memset(&list, 0, sizeof(list));
+ list.results_le.buflen = cpu_to_le32(WL_ISCAN_BUF_MAX);
+ err = brcmf_dev_iovar_getbuf(iscan->ndev, "iscanresults", &list,
+ BRCMF_ISCAN_RESULTS_FIXED_SIZE,
+ iscan->scan_buf, WL_ISCAN_BUF_MAX);
+ if (err) {
+ WL_ERR("error (%d)\n", err);
+ return err;
+ }
+ results->buflen = le32_to_cpu(results_le->buflen);
+ results->version = le32_to_cpu(results_le->version);
+ results->count = le32_to_cpu(results_le->count);
+ WL_SCAN("results->count = %d\n", results_le->count);
+ WL_SCAN("results->buflen = %d\n", results_le->buflen);
+ *status = le32_to_cpu(list_buf->status_le);
+ WL_SCAN("status = %d\n", *status);
+ *bss_list = results;
+
+ return err;
+}
+
+static s32 brcmf_iscan_done(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan;
+ s32 err = 0;
+
+ iscan->state = WL_ISCAN_STATE_IDLE;
+ brcmf_inform_bss(cfg_priv);
+ brcmf_notify_iscan_complete(iscan, false);
+
+ return err;
+}
+
+static s32 brcmf_iscan_pending(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan;
+ s32 err = 0;
+
+ /* Reschedule the timer */
+ mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
+ iscan->timer_on = 1;
+
+ return err;
+}
+
+static s32 brcmf_iscan_inprogress(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan;
+ s32 err = 0;
+
+ brcmf_inform_bss(cfg_priv);
+ brcmf_run_iscan(iscan, NULL, BRCMF_SCAN_ACTION_CONTINUE);
+ /* Reschedule the timer */
+ mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
+ iscan->timer_on = 1;
+
+ return err;
+}
+
+static s32 brcmf_iscan_aborted(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan;
+ s32 err = 0;
+
+ iscan->state = WL_ISCAN_STATE_IDLE;
+ brcmf_notify_iscan_complete(iscan, true);
+
+ return err;
+}
+
+static void brcmf_cfg80211_iscan_handler(struct work_struct *work)
+{
+ struct brcmf_cfg80211_iscan_ctrl *iscan =
+ container_of(work, struct brcmf_cfg80211_iscan_ctrl,
+ work);
+ struct brcmf_cfg80211_priv *cfg_priv = iscan_to_cfg(iscan);
+ struct brcmf_cfg80211_iscan_eloop *el = &iscan->el;
+ u32 status = BRCMF_SCAN_RESULTS_PARTIAL;
+
+ if (iscan->timer_on) {
+ del_timer_sync(&iscan->timer);
+ iscan->timer_on = 0;
+ }
+
+ if (brcmf_get_iscan_results(iscan, &status, &cfg_priv->bss_list)) {
+ status = BRCMF_SCAN_RESULTS_ABORTED;
+ WL_ERR("Abort iscan\n");
+ }
+
+ el->handler[status](cfg_priv);
+}
+
+static void brcmf_iscan_timer(unsigned long data)
+{
+ struct brcmf_cfg80211_iscan_ctrl *iscan =
+ (struct brcmf_cfg80211_iscan_ctrl *)data;
+
+ if (iscan) {
+ iscan->timer_on = 0;
+ WL_SCAN("timer expired\n");
+ brcmf_wakeup_iscan(iscan);
+ }
+}
+
+static s32 brcmf_invoke_iscan(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv);
+
+ if (cfg_priv->iscan_on) {
+ iscan->state = WL_ISCAN_STATE_IDLE;
+ INIT_WORK(&iscan->work, brcmf_cfg80211_iscan_handler);
+ }
+
+ return 0;
+}
+
+static void brcmf_init_iscan_eloop(struct brcmf_cfg80211_iscan_eloop *el)
+{
+ memset(el, 0, sizeof(*el));
+ el->handler[BRCMF_SCAN_RESULTS_SUCCESS] = brcmf_iscan_done;
+ el->handler[BRCMF_SCAN_RESULTS_PARTIAL] = brcmf_iscan_inprogress;
+ el->handler[BRCMF_SCAN_RESULTS_PENDING] = brcmf_iscan_pending;
+ el->handler[BRCMF_SCAN_RESULTS_ABORTED] = brcmf_iscan_aborted;
+ el->handler[BRCMF_SCAN_RESULTS_NO_MEM] = brcmf_iscan_aborted;
+}
+
+static s32 brcmf_init_iscan(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv);
+ int err = 0;
+
+ if (cfg_priv->iscan_on) {
+ iscan->ndev = cfg_to_ndev(cfg_priv);
+ brcmf_init_iscan_eloop(&iscan->el);
+ iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS;
+ init_timer(&iscan->timer);
+ iscan->timer.data = (unsigned long) iscan;
+ iscan->timer.function = brcmf_iscan_timer;
+ err = brcmf_invoke_iscan(cfg_priv);
+ if (!err)
+ iscan->data = cfg_priv;
+ }
+
+ return err;
+}
+
+static void brcmf_delay(u32 ms)
+{
+ if (ms < 1000 / HZ) {
+ cond_resched();
+ mdelay(ms);
+ } else {
+ msleep(ms);
+ }
+}
+
+static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+
+ /*
+ * Check for WL_STATUS_READY before any function call which
+ * could result is bus access. Don't block the resume for
+ * any driver error conditions
+ */
+ WL_TRACE("Enter\n");
+
+ if (test_bit(WL_STATUS_READY, &cfg_priv->status))
+ brcmf_invoke_iscan(wiphy_to_cfg(wiphy));
+
+ WL_TRACE("Exit\n");
+ return 0;
+}
+
+static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
+ struct cfg80211_wowlan *wow)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg_priv);
+
+ WL_TRACE("Enter\n");
+
+ /*
+ * Check for WL_STATUS_READY before any function call which
+ * could result is bus access. Don't block the suspend for
+ * any driver error conditions
+ */
+
+ /*
+ * While going to suspend if associated with AP disassociate
+ * from AP to save power while system is in suspended state
+ */
+ if ((test_bit(WL_STATUS_CONNECTED, &cfg_priv->status) ||
+ test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) &&
+ test_bit(WL_STATUS_READY, &cfg_priv->status)) {
+ WL_INFO("Disassociating from AP"
+ " while entering suspend state\n");
+ brcmf_link_down(cfg_priv);
+
+ /*
+ * Make sure WPA_Supplicant receives all the event
+ * generated due to DISASSOC call to the fw to keep
+ * the state fw and WPA_Supplicant state consistent
+ */
+ brcmf_delay(500);
+ }
+
+ set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
+ if (test_bit(WL_STATUS_READY, &cfg_priv->status))
+ brcmf_term_iscan(cfg_priv);
+
+ if (cfg_priv->scan_request) {
+ /* Indidate scan abort to cfg80211 layer */
+ WL_INFO("Terminating scan in progress\n");
+ cfg80211_scan_done(cfg_priv->scan_request, true);
+ cfg_priv->scan_request = NULL;
+ }
+ clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+ clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
+
+ /* Turn off watchdog timer */
+ if (test_bit(WL_STATUS_READY, &cfg_priv->status)) {
+ WL_INFO("Enable MPC\n");
+ brcmf_set_mpc(ndev, 1);
+ }
+
+ WL_TRACE("Exit\n");
+
+ return 0;
+}
+
+static __used s32
+brcmf_dev_bufvar_set(struct net_device *ndev, s8 *name, s8 *buf, s32 len)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+ u32 buflen;
+
+ buflen = brcmf_c_mkiovar(name, buf, len, cfg_priv->dcmd_buf,
+ WL_DCMD_LEN_MAX);
+ BUG_ON(!buflen);
+
+ return brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, cfg_priv->dcmd_buf,
+ buflen);
+}
+
+static s32
+brcmf_dev_bufvar_get(struct net_device *ndev, s8 *name, s8 *buf,
+ s32 buf_len)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+ u32 len;
+ s32 err = 0;
+
+ len = brcmf_c_mkiovar(name, NULL, 0, cfg_priv->dcmd_buf,
+ WL_DCMD_LEN_MAX);
+ BUG_ON(!len);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, cfg_priv->dcmd_buf,
+ WL_DCMD_LEN_MAX);
+ if (err) {
+ WL_ERR("error (%d)\n", err);
+ return err;
+ }
+ memcpy(buf, cfg_priv->dcmd_buf, buf_len);
+
+ return err;
+}
+
+static __used s32
+brcmf_update_pmklist(struct net_device *ndev,
+ struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
+{
+ int i, j;
+ int pmkid_len;
+
+ pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
+
+ WL_CONN("No of elements %d\n", pmkid_len);
+ for (i = 0; i < pmkid_len; i++) {
+ WL_CONN("PMKID[%d]: %pM =\n", i,
+ &pmk_list->pmkids.pmkid[i].BSSID);
+ for (j = 0; j < WLAN_PMKID_LEN; j++)
+ WL_CONN("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j]);
+ }
+
+ if (!err)
+ brcmf_dev_bufvar_set(ndev, "pmkid_info", (char *)pmk_list,
+ sizeof(*pmk_list));
+
+ return err;
+}
+
+static s32
+brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct pmkid_list *pmkids = &cfg_priv->pmk_list->pmkids;
+ s32 err = 0;
+ int i;
+ int pmkid_len;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ pmkid_len = le32_to_cpu(pmkids->npmkid);
+ for (i = 0; i < pmkid_len; i++)
+ if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
+ break;
+ if (i < WL_NUM_PMKIDS_MAX) {
+ memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
+ memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
+ if (i == pmkid_len) {
+ pmkid_len++;
+ pmkids->npmkid = cpu_to_le32(pmkid_len);
+ }
+ } else
+ err = -EINVAL;
+
+ WL_CONN("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
+ pmkids->pmkid[pmkid_len].BSSID);
+ for (i = 0; i < WLAN_PMKID_LEN; i++)
+ WL_CONN("%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
+
+ err = brcmf_update_pmklist(ndev, cfg_priv->pmk_list, err);
+
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct pmkid_list pmkid;
+ s32 err = 0;
+ int i, pmkid_len;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
+ memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
+
+ WL_CONN("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
+ &pmkid.pmkid[0].BSSID);
+ for (i = 0; i < WLAN_PMKID_LEN; i++)
+ WL_CONN("%02x\n", pmkid.pmkid[0].PMKID[i]);
+
+ pmkid_len = le32_to_cpu(cfg_priv->pmk_list->pmkids.npmkid);
+ for (i = 0; i < pmkid_len; i++)
+ if (!memcmp
+ (pmksa->bssid, &cfg_priv->pmk_list->pmkids.pmkid[i].BSSID,
+ ETH_ALEN))
+ break;
+
+ if ((pmkid_len > 0)
+ && (i < pmkid_len)) {
+ memset(&cfg_priv->pmk_list->pmkids.pmkid[i], 0,
+ sizeof(struct pmkid));
+ for (; i < (pmkid_len - 1); i++) {
+ memcpy(&cfg_priv->pmk_list->pmkids.pmkid[i].BSSID,
+ &cfg_priv->pmk_list->pmkids.pmkid[i + 1].BSSID,
+ ETH_ALEN);
+ memcpy(&cfg_priv->pmk_list->pmkids.pmkid[i].PMKID,
+ &cfg_priv->pmk_list->pmkids.pmkid[i + 1].PMKID,
+ WLAN_PMKID_LEN);
+ }
+ cfg_priv->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
+ } else
+ err = -EINVAL;
+
+ err = brcmf_update_pmklist(ndev, cfg_priv->pmk_list, err);
+
+ WL_TRACE("Exit\n");
+ return err;
+
+}
+
+static s32
+brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+ if (!check_sys_up(wiphy))
+ return -EIO;
+
+ memset(cfg_priv->pmk_list, 0, sizeof(*cfg_priv->pmk_list));
+ err = brcmf_update_pmklist(ndev, cfg_priv->pmk_list, err);
+
+ WL_TRACE("Exit\n");
+ return err;
+
+}
+
+static struct cfg80211_ops wl_cfg80211_ops = {
+ .change_virtual_intf = brcmf_cfg80211_change_iface,
+ .scan = brcmf_cfg80211_scan,
+ .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
+ .join_ibss = brcmf_cfg80211_join_ibss,
+ .leave_ibss = brcmf_cfg80211_leave_ibss,
+ .get_station = brcmf_cfg80211_get_station,
+ .set_tx_power = brcmf_cfg80211_set_tx_power,
+ .get_tx_power = brcmf_cfg80211_get_tx_power,
+ .add_key = brcmf_cfg80211_add_key,
+ .del_key = brcmf_cfg80211_del_key,
+ .get_key = brcmf_cfg80211_get_key,
+ .set_default_key = brcmf_cfg80211_config_default_key,
+ .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
+ .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
+ .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask,
+ .connect = brcmf_cfg80211_connect,
+ .disconnect = brcmf_cfg80211_disconnect,
+ .suspend = brcmf_cfg80211_suspend,
+ .resume = brcmf_cfg80211_resume,
+ .set_pmksa = brcmf_cfg80211_set_pmksa,
+ .del_pmksa = brcmf_cfg80211_del_pmksa,
+ .flush_pmksa = brcmf_cfg80211_flush_pmksa
+};
+
+static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
+{
+ s32 err = 0;
+
+ switch (mode) {
+ case WL_MODE_BSS:
+ return NL80211_IFTYPE_STATION;
+ case WL_MODE_IBSS:
+ return NL80211_IFTYPE_ADHOC;
+ default:
+ return NL80211_IFTYPE_UNSPECIFIED;
+ }
+
+ return err;
+}
+
+static struct wireless_dev *brcmf_alloc_wdev(s32 sizeof_iface,
+ struct device *ndev)
+{
+ struct wireless_dev *wdev;
+ s32 err = 0;
+
+ wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
+ if (!wdev)
+ return ERR_PTR(-ENOMEM);
+
+ wdev->wiphy =
+ wiphy_new(&wl_cfg80211_ops,
+ sizeof(struct brcmf_cfg80211_priv) + sizeof_iface);
+ if (!wdev->wiphy) {
+ WL_ERR("Couldn not allocate wiphy device\n");
+ err = -ENOMEM;
+ goto wiphy_new_out;
+ }
+ set_wiphy_dev(wdev->wiphy, ndev);
+ wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
+ wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
+ wdev->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC);
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
+ wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set
+ * it as 11a by default.
+ * This will be updated with
+ * 11n phy tables in
+ * "ifconfig up"
+ * if phy has 11n capability
+ */
+ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wdev->wiphy->cipher_suites = __wl_cipher_suites;
+ wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
+ wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power
+ * save mode
+ * by default
+ */
+ err = wiphy_register(wdev->wiphy);
+ if (err < 0) {
+ WL_ERR("Couldn not register wiphy device (%d)\n", err);
+ goto wiphy_register_out;
+ }
+ return wdev;
+
+wiphy_register_out:
+ wiphy_free(wdev->wiphy);
+
+wiphy_new_out:
+ kfree(wdev);
+
+ return ERR_PTR(err);
+}
+
+static void brcmf_free_wdev(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct wireless_dev *wdev = cfg_priv->wdev;
+
+ if (!wdev) {
+ WL_ERR("wdev is invalid\n");
+ return;
+ }
+ wiphy_unregister(wdev->wiphy);
+ wiphy_free(wdev->wiphy);
+ kfree(wdev);
+ cfg_priv->wdev = NULL;
+}
+
+static bool brcmf_is_linkup(struct brcmf_cfg80211_priv *cfg_priv,
+ const struct brcmf_event_msg *e)
+{
+ u32 event = be32_to_cpu(e->event_type);
+ u32 status = be32_to_cpu(e->status);
+
+ if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
+ WL_CONN("Processing set ssid\n");
+ cfg_priv->link_up = true;
+ return true;
+ }
+
+ return false;
+}
+
+static bool brcmf_is_linkdown(struct brcmf_cfg80211_priv *cfg_priv,
+ const struct brcmf_event_msg *e)
+{
+ u32 event = be32_to_cpu(e->event_type);
+ u16 flags = be16_to_cpu(e->flags);
+
+ if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
+ WL_CONN("Processing link down\n");
+ return true;
+ }
+ return false;
+}
+
+static bool brcmf_is_nonetwork(struct brcmf_cfg80211_priv *cfg_priv,
+ const struct brcmf_event_msg *e)
+{
+ u32 event = be32_to_cpu(e->event_type);
+ u32 status = be32_to_cpu(e->status);
+
+ if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
+ WL_CONN("Processing Link %s & no network found\n",
+ be16_to_cpu(e->flags) & BRCMF_EVENT_MSG_LINK ?
+ "up" : "down");
+ return true;
+ }
+
+ if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
+ WL_CONN("Processing connecting & no network found\n");
+ return true;
+ }
+
+ return false;
+}
+
+static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv);
+
+ kfree(conn_info->req_ie);
+ conn_info->req_ie = NULL;
+ conn_info->req_ie_len = 0;
+ kfree(conn_info->resp_ie);
+ conn_info->resp_ie = NULL;
+ conn_info->resp_ie_len = 0;
+}
+
+static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct net_device *ndev = cfg_to_ndev(cfg_priv);
+ struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv);
+ u32 req_len;
+ u32 resp_len;
+ s32 err = 0;
+
+ brcmf_clear_assoc_ies(cfg_priv);
+
+ err = brcmf_dev_bufvar_get(ndev, "assoc_info", cfg_priv->extra_buf,
+ WL_ASSOC_INFO_MAX);
+ if (err) {
+ WL_ERR("could not get assoc info (%d)\n", err);
+ return err;
+ }
+ assoc_info =
+ (struct brcmf_cfg80211_assoc_ielen_le *)cfg_priv->extra_buf;
+ req_len = le32_to_cpu(assoc_info->req_len);
+ resp_len = le32_to_cpu(assoc_info->resp_len);
+ if (req_len) {
+ err = brcmf_dev_bufvar_get(ndev, "assoc_req_ies",
+ cfg_priv->extra_buf,
+ WL_ASSOC_INFO_MAX);
+ if (err) {
+ WL_ERR("could not get assoc req (%d)\n", err);
+ return err;
+ }
+ conn_info->req_ie_len = req_len;
+ conn_info->req_ie =
+ kmemdup(cfg_priv->extra_buf, conn_info->req_ie_len,
+ GFP_KERNEL);
+ } else {
+ conn_info->req_ie_len = 0;
+ conn_info->req_ie = NULL;
+ }
+ if (resp_len) {
+ err = brcmf_dev_bufvar_get(ndev, "assoc_resp_ies",
+ cfg_priv->extra_buf,
+ WL_ASSOC_INFO_MAX);
+ if (err) {
+ WL_ERR("could not get assoc resp (%d)\n", err);
+ return err;
+ }
+ conn_info->resp_ie_len = resp_len;
+ conn_info->resp_ie =
+ kmemdup(cfg_priv->extra_buf, conn_info->resp_ie_len,
+ GFP_KERNEL);
+ } else {
+ conn_info->resp_ie_len = 0;
+ conn_info->resp_ie = NULL;
+ }
+ WL_CONN("req len (%d) resp len (%d)\n",
+ conn_info->req_ie_len, conn_info->resp_ie_len);
+
+ return err;
+}
+
+static s32
+brcmf_bss_roaming_done(struct brcmf_cfg80211_priv *cfg_priv,
+ struct net_device *ndev,
+ const struct brcmf_event_msg *e)
+{
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv);
+ struct wiphy *wiphy = cfg_to_wiphy(cfg_priv);
+ struct brcmf_channel_info_le channel_le;
+ struct ieee80211_channel *notify_channel;
+ struct ieee80211_supported_band *band;
+ u32 freq;
+ s32 err = 0;
+ u32 target_channel;
+
+ WL_TRACE("Enter\n");
+
+ brcmf_get_assoc_ies(cfg_priv);
+ brcmf_update_prof(cfg_priv, NULL, &e->addr, WL_PROF_BSSID);
+ brcmf_update_bss_info(cfg_priv);
+
+ brcmf_exec_dcmd(ndev, BRCMF_C_GET_CHANNEL, &channel_le,
+ sizeof(channel_le));
+
+ target_channel = le32_to_cpu(channel_le.target_channel);
+ WL_CONN("Roamed to channel %d\n", target_channel);
+
+ if (target_channel <= CH_MAX_2G_CHANNEL)
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
+ else
+ band = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+ freq = ieee80211_channel_to_frequency(target_channel, band->band);
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+ cfg80211_roamed(ndev, notify_channel,
+ (u8 *)brcmf_read_prof(cfg_priv, WL_PROF_BSSID),
+ conn_info->req_ie, conn_info->req_ie_len,
+ conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
+ WL_CONN("Report roaming result\n");
+
+ set_bit(WL_STATUS_CONNECTED, &cfg_priv->status);
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_bss_connect_done(struct brcmf_cfg80211_priv *cfg_priv,
+ struct net_device *ndev, const struct brcmf_event_msg *e,
+ bool completed)
+{
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv);
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+
+ if (test_and_clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) {
+ if (completed) {
+ brcmf_get_assoc_ies(cfg_priv);
+ brcmf_update_prof(cfg_priv, NULL, &e->addr,
+ WL_PROF_BSSID);
+ brcmf_update_bss_info(cfg_priv);
+ }
+ cfg80211_connect_result(ndev,
+ (u8 *)brcmf_read_prof(cfg_priv,
+ WL_PROF_BSSID),
+ conn_info->req_ie,
+ conn_info->req_ie_len,
+ conn_info->resp_ie,
+ conn_info->resp_ie_len,
+ completed ? WLAN_STATUS_SUCCESS :
+ WLAN_STATUS_AUTH_TIMEOUT,
+ GFP_KERNEL);
+ if (completed)
+ set_bit(WL_STATUS_CONNECTED, &cfg_priv->status);
+ WL_CONN("Report connect result - connection %s\n",
+ completed ? "succeeded" : "failed");
+ }
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_notify_connect_status(struct brcmf_cfg80211_priv *cfg_priv,
+ struct net_device *ndev,
+ const struct brcmf_event_msg *e, void *data)
+{
+ s32 err = 0;
+
+ if (brcmf_is_linkup(cfg_priv, e)) {
+ WL_CONN("Linkup\n");
+ if (brcmf_is_ibssmode(cfg_priv)) {
+ brcmf_update_prof(cfg_priv, NULL, (void *)e->addr,
+ WL_PROF_BSSID);
+ wl_inform_ibss(cfg_priv, ndev, e->addr);
+ cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
+ clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+ set_bit(WL_STATUS_CONNECTED, &cfg_priv->status);
+ } else
+ brcmf_bss_connect_done(cfg_priv, ndev, e, true);
+ } else if (brcmf_is_linkdown(cfg_priv, e)) {
+ WL_CONN("Linkdown\n");
+ if (brcmf_is_ibssmode(cfg_priv)) {
+ clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+ if (test_and_clear_bit(WL_STATUS_CONNECTED,
+ &cfg_priv->status))
+ brcmf_link_down(cfg_priv);
+ } else {
+ brcmf_bss_connect_done(cfg_priv, ndev, e, false);
+ if (test_and_clear_bit(WL_STATUS_CONNECTED,
+ &cfg_priv->status)) {
+ cfg80211_disconnected(ndev, 0, NULL, 0,
+ GFP_KERNEL);
+ brcmf_link_down(cfg_priv);
+ }
+ }
+ brcmf_init_prof(cfg_priv->profile);
+ } else if (brcmf_is_nonetwork(cfg_priv, e)) {
+ if (brcmf_is_ibssmode(cfg_priv))
+ clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+ else
+ brcmf_bss_connect_done(cfg_priv, ndev, e, false);
+ }
+
+ return err;
+}
+
+static s32
+brcmf_notify_roaming_status(struct brcmf_cfg80211_priv *cfg_priv,
+ struct net_device *ndev,
+ const struct brcmf_event_msg *e, void *data)
+{
+ s32 err = 0;
+ u32 event = be32_to_cpu(e->event_type);
+ u32 status = be32_to_cpu(e->status);
+
+ if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
+ if (test_bit(WL_STATUS_CONNECTED, &cfg_priv->status))
+ brcmf_bss_roaming_done(cfg_priv, ndev, e);
+ else
+ brcmf_bss_connect_done(cfg_priv, ndev, e, true);
+ }
+
+ return err;
+}
+
+static s32
+brcmf_notify_mic_status(struct brcmf_cfg80211_priv *cfg_priv,
+ struct net_device *ndev,
+ const struct brcmf_event_msg *e, void *data)
+{
+ u16 flags = be16_to_cpu(e->flags);
+ enum nl80211_key_type key_type;
+
+ if (flags & BRCMF_EVENT_MSG_GROUP)
+ key_type = NL80211_KEYTYPE_GROUP;
+ else
+ key_type = NL80211_KEYTYPE_PAIRWISE;
+
+ cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1,
+ NULL, GFP_KERNEL);
+
+ return 0;
+}
+
+static s32
+brcmf_notify_scan_status(struct brcmf_cfg80211_priv *cfg_priv,
+ struct net_device *ndev,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_channel_info_le channel_inform_le;
+ struct brcmf_scan_results_le *bss_list_le;
+ u32 len = WL_SCAN_BUF_MAX;
+ s32 err = 0;
+ bool scan_abort = false;
+ u32 scan_channel;
+
+ WL_TRACE("Enter\n");
+
+ if (cfg_priv->iscan_on && cfg_priv->iscan_kickstart) {
+ WL_TRACE("Exit\n");
+ return brcmf_wakeup_iscan(cfg_to_iscan(cfg_priv));
+ }
+
+ if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
+ WL_ERR("Scan complete while device not scanning\n");
+ scan_abort = true;
+ err = -EINVAL;
+ goto scan_done_out;
+ }
+
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_CHANNEL, &channel_inform_le,
+ sizeof(channel_inform_le));
+ if (err) {
+ WL_ERR("scan busy (%d)\n", err);
+ scan_abort = true;
+ goto scan_done_out;
+ }
+ scan_channel = le32_to_cpu(channel_inform_le.scan_channel);
+ if (scan_channel)
+ WL_CONN("channel_inform.scan_channel (%d)\n", scan_channel);
+ cfg_priv->bss_list = cfg_priv->scan_results;
+ bss_list_le = (struct brcmf_scan_results_le *) cfg_priv->bss_list;
+
+ memset(cfg_priv->scan_results, 0, len);
+ bss_list_le->buflen = cpu_to_le32(len);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN_RESULTS,
+ cfg_priv->scan_results, len);
+ if (err) {
+ WL_ERR("%s Scan_results error (%d)\n", ndev->name, err);
+ err = -EINVAL;
+ scan_abort = true;
+ goto scan_done_out;
+ }
+ cfg_priv->scan_results->buflen = le32_to_cpu(bss_list_le->buflen);
+ cfg_priv->scan_results->version = le32_to_cpu(bss_list_le->version);
+ cfg_priv->scan_results->count = le32_to_cpu(bss_list_le->count);
+
+ err = brcmf_inform_bss(cfg_priv);
+ if (err) {
+ scan_abort = true;
+ goto scan_done_out;
+ }
+
+scan_done_out:
+ if (cfg_priv->scan_request) {
+ WL_SCAN("calling cfg80211_scan_done\n");
+ cfg80211_scan_done(cfg_priv->scan_request, scan_abort);
+ brcmf_set_mpc(ndev, 1);
+ cfg_priv->scan_request = NULL;
+ }
+
+ WL_TRACE("Exit\n");
+
+ return err;
+}
+
+static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
+{
+ conf->mode = (u32)-1;
+ conf->frag_threshold = (u32)-1;
+ conf->rts_threshold = (u32)-1;
+ conf->retry_short = (u32)-1;
+ conf->retry_long = (u32)-1;
+ conf->tx_power = -1;
+}
+
+static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el)
+{
+ memset(el, 0, sizeof(*el));
+ el->handler[BRCMF_E_SCAN_COMPLETE] = brcmf_notify_scan_status;
+ el->handler[BRCMF_E_LINK] = brcmf_notify_connect_status;
+ el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status;
+ el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status;
+ el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status;
+}
+
+static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ kfree(cfg_priv->scan_results);
+ cfg_priv->scan_results = NULL;
+ kfree(cfg_priv->bss_info);
+ cfg_priv->bss_info = NULL;
+ kfree(cfg_priv->conf);
+ cfg_priv->conf = NULL;
+ kfree(cfg_priv->profile);
+ cfg_priv->profile = NULL;
+ kfree(cfg_priv->scan_req_int);
+ cfg_priv->scan_req_int = NULL;
+ kfree(cfg_priv->dcmd_buf);
+ cfg_priv->dcmd_buf = NULL;
+ kfree(cfg_priv->extra_buf);
+ cfg_priv->extra_buf = NULL;
+ kfree(cfg_priv->iscan);
+ cfg_priv->iscan = NULL;
+ kfree(cfg_priv->pmk_list);
+ cfg_priv->pmk_list = NULL;
+}
+
+static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ cfg_priv->scan_results = kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL);
+ if (!cfg_priv->scan_results)
+ goto init_priv_mem_out;
+ cfg_priv->conf = kzalloc(sizeof(*cfg_priv->conf), GFP_KERNEL);
+ if (!cfg_priv->conf)
+ goto init_priv_mem_out;
+ cfg_priv->profile = kzalloc(sizeof(*cfg_priv->profile), GFP_KERNEL);
+ if (!cfg_priv->profile)
+ goto init_priv_mem_out;
+ cfg_priv->bss_info = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (!cfg_priv->bss_info)
+ goto init_priv_mem_out;
+ cfg_priv->scan_req_int = kzalloc(sizeof(*cfg_priv->scan_req_int),
+ GFP_KERNEL);
+ if (!cfg_priv->scan_req_int)
+ goto init_priv_mem_out;
+ cfg_priv->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL);
+ if (!cfg_priv->dcmd_buf)
+ goto init_priv_mem_out;
+ cfg_priv->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
+ if (!cfg_priv->extra_buf)
+ goto init_priv_mem_out;
+ cfg_priv->iscan = kzalloc(sizeof(*cfg_priv->iscan), GFP_KERNEL);
+ if (!cfg_priv->iscan)
+ goto init_priv_mem_out;
+ cfg_priv->pmk_list = kzalloc(sizeof(*cfg_priv->pmk_list), GFP_KERNEL);
+ if (!cfg_priv->pmk_list)
+ goto init_priv_mem_out;
+
+ return 0;
+
+init_priv_mem_out:
+ brcmf_deinit_priv_mem(cfg_priv);
+
+ return -ENOMEM;
+}
+
+/*
+* retrieve first queued event from head
+*/
+
+static struct brcmf_cfg80211_event_q *brcmf_deq_event(
+ struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_event_q *e = NULL;
+
+ spin_lock_irq(&cfg_priv->evt_q_lock);
+ if (!list_empty(&cfg_priv->evt_q_list)) {
+ e = list_first_entry(&cfg_priv->evt_q_list,
+ struct brcmf_cfg80211_event_q, evt_q_list);
+ list_del(&e->evt_q_list);
+ }
+ spin_unlock_irq(&cfg_priv->evt_q_lock);
+
+ return e;
+}
+
+/*
+** push event to tail of the queue
+*/
+
+static s32
+brcmf_enq_event(struct brcmf_cfg80211_priv *cfg_priv, u32 event,
+ const struct brcmf_event_msg *msg)
+{
+ struct brcmf_cfg80211_event_q *e;
+ s32 err = 0;
+
+ e = kzalloc(sizeof(struct brcmf_cfg80211_event_q), GFP_KERNEL);
+ if (!e)
+ return -ENOMEM;
+
+ e->etype = event;
+ memcpy(&e->emsg, msg, sizeof(struct brcmf_event_msg));
+
+ spin_lock_irq(&cfg_priv->evt_q_lock);
+ list_add_tail(&e->evt_q_list, &cfg_priv->evt_q_list);
+ spin_unlock_irq(&cfg_priv->evt_q_lock);
+
+ return err;
+}
+
+static void brcmf_put_event(struct brcmf_cfg80211_event_q *e)
+{
+ kfree(e);
+}
+
+static void brcmf_cfg80211_event_handler(struct work_struct *work)
+{
+ struct brcmf_cfg80211_priv *cfg_priv =
+ container_of(work, struct brcmf_cfg80211_priv,
+ event_work);
+ struct brcmf_cfg80211_event_q *e;
+
+ e = brcmf_deq_event(cfg_priv);
+ if (unlikely(!e)) {
+ WL_ERR("event queue empty...\n");
+ return;
+ }
+
+ do {
+ WL_INFO("event type (%d)\n", e->etype);
+ if (cfg_priv->el.handler[e->etype])
+ cfg_priv->el.handler[e->etype](cfg_priv,
+ cfg_to_ndev(cfg_priv),
+ &e->emsg, e->edata);
+ else
+ WL_INFO("Unknown Event (%d): ignoring\n", e->etype);
+ brcmf_put_event(e);
+ } while ((e = brcmf_deq_event(cfg_priv)));
+
+}
+
+static void brcmf_init_eq(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ spin_lock_init(&cfg_priv->evt_q_lock);
+ INIT_LIST_HEAD(&cfg_priv->evt_q_list);
+}
+
+static void brcmf_flush_eq(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct brcmf_cfg80211_event_q *e;
+
+ spin_lock_irq(&cfg_priv->evt_q_lock);
+ while (!list_empty(&cfg_priv->evt_q_list)) {
+ e = list_first_entry(&cfg_priv->evt_q_list,
+ struct brcmf_cfg80211_event_q, evt_q_list);
+ list_del(&e->evt_q_list);
+ kfree(e);
+ }
+ spin_unlock_irq(&cfg_priv->evt_q_lock);
+}
+
+static s32 wl_init_priv(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ s32 err = 0;
+
+ cfg_priv->scan_request = NULL;
+ cfg_priv->pwr_save = true;
+ cfg_priv->iscan_on = true; /* iscan on & off switch.
+ we enable iscan per default */
+ cfg_priv->roam_on = true; /* roam on & off switch.
+ we enable roam per default */
+
+ cfg_priv->iscan_kickstart = false;
+ cfg_priv->active_scan = true; /* we do active scan for
+ specific scan per default */
+ cfg_priv->dongle_up = false; /* dongle is not up yet */
+ brcmf_init_eq(cfg_priv);
+ err = brcmf_init_priv_mem(cfg_priv);
+ if (err)
+ return err;
+ INIT_WORK(&cfg_priv->event_work, brcmf_cfg80211_event_handler);
+ brcmf_init_eloop_handler(&cfg_priv->el);
+ mutex_init(&cfg_priv->usr_sync);
+ err = brcmf_init_iscan(cfg_priv);
+ if (err)
+ return err;
+ brcmf_init_conf(cfg_priv->conf);
+ brcmf_init_prof(cfg_priv->profile);
+ brcmf_link_down(cfg_priv);
+
+ return err;
+}
+
+static void wl_deinit_priv(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ cancel_work_sync(&cfg_priv->event_work);
+ cfg_priv->dongle_up = false; /* dongle down */
+ brcmf_flush_eq(cfg_priv);
+ brcmf_link_down(cfg_priv);
+ brcmf_term_iscan(cfg_priv);
+ brcmf_deinit_priv_mem(cfg_priv);
+}
+
+struct brcmf_cfg80211_dev *brcmf_cfg80211_attach(struct net_device *ndev,
+ struct device *busdev,
+ void *data)
+{
+ struct wireless_dev *wdev;
+ struct brcmf_cfg80211_priv *cfg_priv;
+ struct brcmf_cfg80211_iface *ci;
+ struct brcmf_cfg80211_dev *cfg_dev;
+ s32 err = 0;
+
+ if (!ndev) {
+ WL_ERR("ndev is invalid\n");
+ return NULL;
+ }
+ cfg_dev = kzalloc(sizeof(struct brcmf_cfg80211_dev), GFP_KERNEL);
+ if (!cfg_dev)
+ return NULL;
+
+ wdev = brcmf_alloc_wdev(sizeof(struct brcmf_cfg80211_iface), busdev);
+ if (IS_ERR(wdev)) {
+ kfree(cfg_dev);
+ return NULL;
+ }
+
+ wdev->iftype = brcmf_mode_to_nl80211_iftype(WL_MODE_BSS);
+ cfg_priv = wdev_to_cfg(wdev);
+ cfg_priv->wdev = wdev;
+ cfg_priv->pub = data;
+ ci = (struct brcmf_cfg80211_iface *)&cfg_priv->ci;
+ ci->cfg_priv = cfg_priv;
+ ndev->ieee80211_ptr = wdev;
+ SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
+ wdev->netdev = ndev;
+ err = wl_init_priv(cfg_priv);
+ if (err) {
+ WL_ERR("Failed to init iwm_priv (%d)\n", err);
+ goto cfg80211_attach_out;
+ }
+ brcmf_set_drvdata(cfg_dev, ci);
+
+ return cfg_dev;
+
+cfg80211_attach_out:
+ brcmf_free_wdev(cfg_priv);
+ kfree(cfg_dev);
+ return NULL;
+}
+
+void brcmf_cfg80211_detach(struct brcmf_cfg80211_dev *cfg_dev)
+{
+ struct brcmf_cfg80211_priv *cfg_priv;
+
+ cfg_priv = brcmf_priv_get(cfg_dev);
+
+ wl_deinit_priv(cfg_priv);
+ brcmf_free_wdev(cfg_priv);
+ brcmf_set_drvdata(cfg_dev, NULL);
+ kfree(cfg_dev);
+}
+
+void
+brcmf_cfg80211_event(struct net_device *ndev,
+ const struct brcmf_event_msg *e, void *data)
+{
+ u32 event_type = be32_to_cpu(e->event_type);
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+
+ if (!brcmf_enq_event(cfg_priv, event_type, e))
+ schedule_work(&cfg_priv->event_work);
+}
+
+static s32 brcmf_dongle_mode(struct net_device *ndev, s32 iftype)
+{
+ s32 infra = 0;
+ s32 err = 0;
+
+ switch (iftype) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_WDS:
+ WL_ERR("type (%d) : currently we do not support this mode\n",
+ iftype);
+ err = -EINVAL;
+ return err;
+ case NL80211_IFTYPE_ADHOC:
+ infra = 0;
+ break;
+ case NL80211_IFTYPE_STATION:
+ infra = 1;
+ break;
+ default:
+ err = -EINVAL;
+ WL_ERR("invalid type (%d)\n", iftype);
+ return err;
+ }
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &infra);
+ if (err) {
+ WL_ERR("WLC_SET_INFRA error (%d)\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
+{
+ /* Room for "event_msgs" + '\0' + bitvec */
+ s8 iovbuf[BRCMF_EVENTING_MASK_LEN + 12];
+ s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+ s32 err = 0;
+
+ WL_TRACE("Enter\n");
+
+ /* Setup event_msgs */
+ brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
+ iovbuf, sizeof(iovbuf));
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, iovbuf, sizeof(iovbuf));
+ if (err) {
+ WL_ERR("Get event_msgs error (%d)\n", err);
+ goto dongle_eventmsg_out;
+ }
+ memcpy(eventmask, iovbuf, BRCMF_EVENTING_MASK_LEN);
+
+ setbit(eventmask, BRCMF_E_SET_SSID);
+ setbit(eventmask, BRCMF_E_ROAM);
+ setbit(eventmask, BRCMF_E_PRUNE);
+ setbit(eventmask, BRCMF_E_AUTH);
+ setbit(eventmask, BRCMF_E_REASSOC);
+ setbit(eventmask, BRCMF_E_REASSOC_IND);
+ setbit(eventmask, BRCMF_E_DEAUTH_IND);
+ setbit(eventmask, BRCMF_E_DISASSOC_IND);
+ setbit(eventmask, BRCMF_E_DISASSOC);
+ setbit(eventmask, BRCMF_E_JOIN);
+ setbit(eventmask, BRCMF_E_ASSOC_IND);
+ setbit(eventmask, BRCMF_E_PSK_SUP);
+ setbit(eventmask, BRCMF_E_LINK);
+ setbit(eventmask, BRCMF_E_NDIS_LINK);
+ setbit(eventmask, BRCMF_E_MIC_ERROR);
+ setbit(eventmask, BRCMF_E_PMKID_CACHE);
+ setbit(eventmask, BRCMF_E_TXFAIL);
+ setbit(eventmask, BRCMF_E_JOIN_START);
+ setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
+
+ brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
+ iovbuf, sizeof(iovbuf));
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf));
+ if (err) {
+ WL_ERR("Set event_msgs error (%d)\n", err);
+ goto dongle_eventmsg_out;
+ }
+
+dongle_eventmsg_out:
+ WL_TRACE("Exit\n");
+ return err;
+}
+
+static s32
+brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
+{
+ s8 iovbuf[32];
+ s32 err = 0;
+ __le32 roamtrigger[2];
+ __le32 roam_delta[2];
+ __le32 bcn_to_le;
+ __le32 roamvar_le;
+
+ /*
+ * Setup timeout if Beacons are lost and roam is
+ * off to report link down
+ */
+ if (roamvar) {
+ bcn_to_le = cpu_to_le32(bcn_timeout);
+ brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_to_le,
+ sizeof(bcn_to_le), iovbuf, sizeof(iovbuf));
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR,
+ iovbuf, sizeof(iovbuf));
+ if (err) {
+ WL_ERR("bcn_timeout error (%d)\n", err);
+ goto dongle_rom_out;
+ }
+ }
+
+ /*
+ * Enable/Disable built-in roaming to allow supplicant
+ * to take care of roaming
+ */
+ WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On");
+ roamvar_le = cpu_to_le32(roamvar);
+ brcmf_c_mkiovar("roam_off", (char *)&roamvar_le,
+ sizeof(roamvar_le), iovbuf, sizeof(iovbuf));
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf));
+ if (err) {
+ WL_ERR("roam_off error (%d)\n", err);
+ goto dongle_rom_out;
+ }
+
+ roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
+ roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_ROAM_TRIGGER,
+ (void *)roamtrigger, sizeof(roamtrigger));
+ if (err) {
+ WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
+ goto dongle_rom_out;
+ }
+
+ roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
+ roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_ROAM_DELTA,
+ (void *)roam_delta, sizeof(roam_delta));
+ if (err) {
+ WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err);
+ goto dongle_rom_out;
+ }
+
+dongle_rom_out:
+ return err;
+}
+
+static s32
+brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
+ s32 scan_unassoc_time, s32 scan_passive_time)
+{
+ s32 err = 0;
+ __le32 scan_assoc_tm_le = cpu_to_le32(scan_assoc_time);
+ __le32 scan_unassoc_tm_le = cpu_to_le32(scan_unassoc_time);
+ __le32 scan_passive_tm_le = cpu_to_le32(scan_passive_time);
+
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_CHANNEL_TIME,
+ &scan_assoc_tm_le, sizeof(scan_assoc_tm_le));
+ if (err) {
+ if (err == -EOPNOTSUPP)
+ WL_INFO("Scan assoc time is not supported\n");
+ else
+ WL_ERR("Scan assoc time error (%d)\n", err);
+ goto dongle_scantime_out;
+ }
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_UNASSOC_TIME,
+ &scan_unassoc_tm_le, sizeof(scan_unassoc_tm_le));
+ if (err) {
+ if (err == -EOPNOTSUPP)
+ WL_INFO("Scan unassoc time is not supported\n");
+ else
+ WL_ERR("Scan unassoc time error (%d)\n", err);
+ goto dongle_scantime_out;
+ }
+
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_PASSIVE_TIME,
+ &scan_passive_tm_le, sizeof(scan_passive_tm_le));
+ if (err) {
+ if (err == -EOPNOTSUPP)
+ WL_INFO("Scan passive time is not supported\n");
+ else
+ WL_ERR("Scan passive time error (%d)\n", err);
+ goto dongle_scantime_out;
+ }
+
+dongle_scantime_out:
+ return err;
+}
+
+static s32 wl_update_wiphybands(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct wiphy *wiphy;
+ s32 phy_list;
+ s8 phy;
+ s32 err = 0;
+
+ err = brcmf_exec_dcmd(cfg_to_ndev(cfg_priv), BRCM_GET_PHYLIST,
+ &phy_list, sizeof(phy_list));
+ if (err) {
+ WL_ERR("error (%d)\n", err);
+ return err;
+ }
+
+ phy = ((char *)&phy_list)[1];
+ WL_INFO("%c phy\n", phy);
+ if (phy == 'n' || phy == 'a') {
+ wiphy = cfg_to_wiphy(cfg_priv);
+ wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
+ }
+
+ return err;
+}
+
+static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ return wl_update_wiphybands(cfg_priv);
+}
+
+static s32 brcmf_config_dongle(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ struct net_device *ndev;
+ struct wireless_dev *wdev;
+ s32 power_mode;
+ s32 err = 0;
+
+ if (cfg_priv->dongle_up)
+ return err;
+
+ ndev = cfg_to_ndev(cfg_priv);
+ wdev = ndev->ieee80211_ptr;
+
+ brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME,
+ WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
+
+ err = brcmf_dongle_eventmsg(ndev);
+ if (err)
+ goto default_conf_out;
+
+ power_mode = cfg_priv->pwr_save ? PM_FAST : PM_OFF;
+ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_PM, &power_mode);
+ if (err)
+ goto default_conf_out;
+ WL_INFO("power save set to %s\n",
+ (power_mode ? "enabled" : "disabled"));
+
+ err = brcmf_dongle_roam(ndev, (cfg_priv->roam_on ? 0 : 1),
+ WL_BEACON_TIMEOUT);
+ if (err)
+ goto default_conf_out;
+ err = brcmf_dongle_mode(ndev, wdev->iftype);
+ if (err && err != -EINPROGRESS)
+ goto default_conf_out;
+ err = brcmf_dongle_probecap(cfg_priv);
+ if (err)
+ goto default_conf_out;
+
+ /* -EINPROGRESS: Call commit handler */
+
+default_conf_out:
+
+ cfg_priv->dongle_up = true;
+
+ return err;
+
+}
+
+static int brcmf_debugfs_add_netdev_params(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ char buf[10+IFNAMSIZ];
+ struct dentry *fd;
+ s32 err = 0;
+
+ sprintf(buf, "netdev:%s", cfg_to_ndev(cfg_priv)->name);
+ cfg_priv->debugfsdir = debugfs_create_dir(buf,
+ cfg_to_wiphy(cfg_priv)->debugfsdir);
+
+ fd = debugfs_create_u16("beacon_int", S_IRUGO, cfg_priv->debugfsdir,
+ (u16 *)&cfg_priv->profile->beacon_interval);
+ if (!fd) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ fd = debugfs_create_u8("dtim_period", S_IRUGO, cfg_priv->debugfsdir,
+ (u8 *)&cfg_priv->profile->dtim_period);
+ if (!fd) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+err_out:
+ return err;
+}
+
+static void brcmf_debugfs_remove_netdev(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ debugfs_remove_recursive(cfg_priv->debugfsdir);
+ cfg_priv->debugfsdir = NULL;
+}
+
+static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ s32 err = 0;
+
+ set_bit(WL_STATUS_READY, &cfg_priv->status);
+
+ brcmf_debugfs_add_netdev_params(cfg_priv);
+
+ err = brcmf_config_dongle(cfg_priv);
+ if (err)
+ return err;
+
+ brcmf_invoke_iscan(cfg_priv);
+
+ return err;
+}
+
+static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_priv *cfg_priv)
+{
+ /*
+ * While going down, if associated with AP disassociate
+ * from AP to save power
+ */
+ if ((test_bit(WL_STATUS_CONNECTED, &cfg_priv->status) ||
+ test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) &&
+ test_bit(WL_STATUS_READY, &cfg_priv->status)) {
+ WL_INFO("Disassociating from AP");
+ brcmf_link_down(cfg_priv);
+
+ /* Make sure WPA_Supplicant receives all the event
+ generated due to DISASSOC call to the fw to keep
+ the state fw and WPA_Supplicant state consistent
+ */
+ brcmf_delay(500);
+ }
+
+ set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
+ brcmf_term_iscan(cfg_priv);
+ if (cfg_priv->scan_request) {
+ cfg80211_scan_done(cfg_priv->scan_request, true);
+ /* May need to perform this to cover rmmod */
+ /* wl_set_mpc(cfg_to_ndev(wl), 1); */
+ cfg_priv->scan_request = NULL;
+ }
+ clear_bit(WL_STATUS_READY, &cfg_priv->status);
+ clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+ clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
+
+ brcmf_debugfs_remove_netdev(cfg_priv);
+
+ return 0;
+}
+
+s32 brcmf_cfg80211_up(struct brcmf_cfg80211_dev *cfg_dev)
+{
+ struct brcmf_cfg80211_priv *cfg_priv;
+ s32 err = 0;
+
+ cfg_priv = brcmf_priv_get(cfg_dev);
+ mutex_lock(&cfg_priv->usr_sync);
+ err = __brcmf_cfg80211_up(cfg_priv);
+ mutex_unlock(&cfg_priv->usr_sync);
+
+ return err;
+}
+
+s32 brcmf_cfg80211_down(struct brcmf_cfg80211_dev *cfg_dev)
+{
+ struct brcmf_cfg80211_priv *cfg_priv;
+ s32 err = 0;
+
+ cfg_priv = brcmf_priv_get(cfg_dev);
+ mutex_lock(&cfg_priv->usr_sync);
+ err = __brcmf_cfg80211_down(cfg_priv);
+ mutex_unlock(&cfg_priv->usr_sync);
+
+ return err;
+}
+
+static __used s32 brcmf_add_ie(struct brcmf_cfg80211_priv *cfg_priv,
+ u8 t, u8 l, u8 *v)
+{
+ struct brcmf_cfg80211_ie *ie = &cfg_priv->ie;
+ s32 err = 0;
+
+ if (ie->offset + l + 2 > WL_TLV_INFO_MAX) {
+ WL_ERR("ei crosses buffer boundary\n");
+ return -ENOSPC;
+ }
+ ie->buf[ie->offset] = t;
+ ie->buf[ie->offset + 1] = l;
+ memcpy(&ie->buf[ie->offset + 2], v, l);
+ ie->offset += l + 2;
+
+ return err;
+}
diff --git a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
index 996033cf9b09..62dc46144ede 100644
--- a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
@@ -17,16 +17,11 @@
#ifndef _wl_cfg80211_h_
#define _wl_cfg80211_h_
-#include <linux/wireless.h>
-#include <linux/wireless.h>
-#include <net/cfg80211.h>
-#include <wlioctl.h>
-
-struct wl_conf;
-struct wl_iface;
-struct wl_priv;
-struct wl_security;
-struct wl_ibss;
+struct brcmf_cfg80211_conf;
+struct brcmf_cfg80211_iface;
+struct brcmf_cfg80211_priv;
+struct brcmf_cfg80211_security;
+struct brcmf_cfg80211_ibss;
#define WL_DBG_NONE 0
#define WL_DBG_CONN (1 << 5)
@@ -39,7 +34,7 @@ struct wl_ibss;
#define WL_ERR(fmt, args...) \
do { \
- if (wl_dbg_level & WL_DBG_ERR) { \
+ if (brcmf_dbg_level & WL_DBG_ERR) { \
if (net_ratelimit()) { \
printk(KERN_ERR "ERROR @%s : " fmt, \
__func__, ##args); \
@@ -50,7 +45,7 @@ do { \
#if (defined BCMDBG)
#define WL_INFO(fmt, args...) \
do { \
- if (wl_dbg_level & WL_DBG_INFO) { \
+ if (brcmf_dbg_level & WL_DBG_INFO) { \
if (net_ratelimit()) { \
printk(KERN_ERR "INFO @%s : " fmt, \
__func__, ##args); \
@@ -60,7 +55,7 @@ do { \
#define WL_TRACE(fmt, args...) \
do { \
- if (wl_dbg_level & WL_DBG_TRACE) { \
+ if (brcmf_dbg_level & WL_DBG_TRACE) { \
if (net_ratelimit()) { \
printk(KERN_ERR "TRACE @%s : " fmt, \
__func__, ##args); \
@@ -70,7 +65,7 @@ do { \
#define WL_SCAN(fmt, args...) \
do { \
- if (wl_dbg_level & WL_DBG_SCAN) { \
+ if (brcmf_dbg_level & WL_DBG_SCAN) { \
if (net_ratelimit()) { \
printk(KERN_ERR "SCAN @%s : " fmt, \
__func__, ##args); \
@@ -80,7 +75,7 @@ do { \
#define WL_CONN(fmt, args...) \
do { \
- if (wl_dbg_level & WL_DBG_CONN) { \
+ if (brcmf_dbg_level & WL_DBG_CONN) { \
if (net_ratelimit()) { \
printk(KERN_ERR "CONN @%s : " fmt, \
__func__, ##args); \
@@ -95,33 +90,30 @@ do { \
#define WL_CONN(fmt, args...)
#endif /* (defined BCMDBG) */
-
-#define WL_SCAN_RETRY_MAX 3 /* used for ibss scan */
#define WL_NUM_SCAN_MAX 1
#define WL_NUM_PMKIDS_MAX MAXPMKID /* will be used
* for 2.6.33 kernel
* or later
*/
-#define WL_SCAN_BUF_MAX (1024 * 8)
-#define WL_TLV_INFO_MAX 1024
+#define WL_SCAN_BUF_MAX (1024 * 8)
+#define WL_TLV_INFO_MAX 1024
#define WL_BSS_INFO_MAX 2048
#define WL_ASSOC_INFO_MAX 512 /*
* needs to grab assoc info from dongle to
* report it to cfg80211 through "connect"
* event
*/
-#define WL_IOCTL_LEN_MAX 1024
+#define WL_DCMD_LEN_MAX 1024
#define WL_EXTRA_BUF_MAX 2048
#define WL_ISCAN_BUF_MAX 2048 /*
- * the buf lengh can be WLC_IOCTL_MAXLEN (8K)
+ * the buf length can be BRCMF_DCMD_MAXLEN
* to reduce iteration
*/
#define WL_ISCAN_TIMER_INTERVAL_MS 3000
-#define WL_SCAN_ERSULTS_LAST (WL_SCAN_RESULTS_NO_MEM+1)
+#define WL_SCAN_ERSULTS_LAST (BRCMF_SCAN_RESULTS_NO_MEM+1)
#define WL_AP_MAX 256 /* virtually unlimitted as long
* as kernel memory allows
*/
-#define WL_FILE_NAME_MAX 256
#define WL_ROAM_TRIGGER_LEVEL -75
#define WL_ROAM_DELTA 20
@@ -166,22 +158,8 @@ enum wl_iscan_state {
WL_ISCAN_STATE_SCANING
};
-/* fw downloading status */
-enum wl_fw_status {
- WL_FW_LOADING_DONE,
- WL_NVRAM_LOADING_DONE
-};
-
-/* beacon / probe_response */
-struct beacon_proberesp {
- __le64 timestamp;
- __le16 beacon_int;
- __le16 capab_info;
- u8 variable[0];
-} __attribute__ ((packed));
-
/* dongle configuration */
-struct wl_conf {
+struct brcmf_cfg80211_conf {
u32 mode; /* adhoc , infrastructure or ap */
u32 frag_threshold;
u32 rts_threshold;
@@ -192,51 +170,43 @@ struct wl_conf {
};
/* cfg80211 main event loop */
-struct wl_event_loop {
- s32(*handler[WLC_E_LAST]) (struct wl_priv *wl,
+struct brcmf_cfg80211_event_loop {
+ s32(*handler[BRCMF_E_LAST]) (struct brcmf_cfg80211_priv *cfg_priv,
struct net_device *ndev,
- const wl_event_msg_t *e, void *data);
+ const struct brcmf_event_msg *e,
+ void *data);
};
/* representing interface of cfg80211 plane */
-struct wl_iface {
- struct wl_priv *wl;
+struct brcmf_cfg80211_iface {
+ struct brcmf_cfg80211_priv *cfg_priv;
};
-struct wl_dev {
+struct brcmf_cfg80211_dev {
void *driver_data; /* to store cfg80211 object information */
};
-/* bss inform structure for cfg80211 interface */
-struct wl_cfg80211_bss_info {
- u16 band;
- u16 channel;
- s16 rssi;
- u16 frame_len;
- u8 frame_buf[1];
-};
-
/* basic structure of scan request */
-struct wl_scan_req {
- struct wlc_ssid ssid;
+struct brcmf_cfg80211_scan_req {
+ struct brcmf_ssid_le ssid_le;
};
/* basic structure of information element */
-struct wl_ie {
+struct brcmf_cfg80211_ie {
u16 offset;
u8 buf[WL_TLV_INFO_MAX];
};
/* event queue for cfg80211 main event */
-struct wl_event_q {
- struct list_head eq_list;
+struct brcmf_cfg80211_event_q {
+ struct list_head evt_q_list;
u32 etype;
- wl_event_msg_t emsg;
+ struct brcmf_event_msg emsg;
s8 edata[1];
};
/* security information with currently associated ap */
-struct wl_security {
+struct brcmf_cfg80211_security {
u32 wpa_versions;
u32 auth_type;
u32 cipher_pairwise;
@@ -245,7 +215,7 @@ struct wl_security {
};
/* ibss information for currently joined ibss network */
-struct wl_ibss {
+struct brcmf_cfg80211_ibss {
u8 beacon_interval; /* in millisecond */
u8 atim; /* in millisecond */
s8 join_only;
@@ -254,95 +224,81 @@ struct wl_ibss {
};
/* dongle profile */
-struct wl_profile {
+struct brcmf_cfg80211_profile {
u32 mode;
- struct wlc_ssid ssid;
+ struct brcmf_ssid ssid;
u8 bssid[ETH_ALEN];
u16 beacon_interval;
u8 dtim_period;
- struct wl_security sec;
- struct wl_ibss ibss;
+ struct brcmf_cfg80211_security sec;
+ struct brcmf_cfg80211_ibss ibss;
s32 band;
};
/* dongle iscan event loop */
-struct wl_iscan_eloop {
- s32(*handler[WL_SCAN_ERSULTS_LAST]) (struct wl_priv *wl);
+struct brcmf_cfg80211_iscan_eloop {
+ s32 (*handler[WL_SCAN_ERSULTS_LAST])
+ (struct brcmf_cfg80211_priv *cfg_priv);
};
/* dongle iscan controller */
-struct wl_iscan_ctrl {
- struct net_device *dev;
+struct brcmf_cfg80211_iscan_ctrl {
+ struct net_device *ndev;
struct timer_list timer;
u32 timer_ms;
u32 timer_on;
s32 state;
- struct task_struct *tsk;
- struct semaphore sync;
- struct wl_iscan_eloop el;
+ struct work_struct work;
+ struct brcmf_cfg80211_iscan_eloop el;
void *data;
- s8 ioctl_buf[WLC_IOCTL_SMLEN];
+ s8 dcmd_buf[BRCMF_DCMD_SMLEN];
s8 scan_buf[WL_ISCAN_BUF_MAX];
};
/* association inform */
-struct wl_connect_info {
+struct brcmf_cfg80211_connect_info {
u8 *req_ie;
s32 req_ie_len;
u8 *resp_ie;
s32 resp_ie_len;
};
-/* firmware /nvram downloading controller */
-struct wl_fw_ctrl {
- const struct firmware *fw_entry;
- unsigned long status;
- u32 ptr;
- s8 fw_name[WL_FILE_NAME_MAX];
- s8 nvram_name[WL_FILE_NAME_MAX];
-};
-
/* assoc ie length */
-struct wl_assoc_ielen {
- u32 req_len;
- u32 resp_len;
+struct brcmf_cfg80211_assoc_ielen_le {
+ __le32 req_len;
+ __le32 resp_len;
};
/* wpa2 pmk list */
-struct wl_pmk_list {
- pmkid_list_t pmkids;
- pmkid_t foo[MAXPMKID - 1];
+struct brcmf_cfg80211_pmk_list {
+ struct pmkid_list pmkids;
+ struct pmkid foo[MAXPMKID - 1];
};
/* dongle private data of cfg80211 interface */
-struct wl_priv {
+struct brcmf_cfg80211_priv {
struct wireless_dev *wdev; /* representing wl cfg80211 device */
- struct wl_conf *conf; /* dongle configuration */
+ struct brcmf_cfg80211_conf *conf; /* dongle configuration */
struct cfg80211_scan_request *scan_request; /* scan request
object */
- struct wl_event_loop el; /* main event loop */
- struct list_head eq_list; /* used for event queue */
- spinlock_t eq_lock; /* for event queue synchronization */
+ struct brcmf_cfg80211_event_loop el; /* main event loop */
+ struct list_head evt_q_list; /* used for event queue */
+ spinlock_t evt_q_lock; /* for event queue synchronization */
struct mutex usr_sync; /* maily for dongle up/down synchronization */
- struct wl_scan_results *bss_list; /* bss_list holding scanned
+ struct brcmf_scan_results *bss_list; /* bss_list holding scanned
ap information */
- struct wl_scan_results *scan_results;
- struct wl_scan_req *scan_req_int; /* scan request object for
- internal purpose */
+ struct brcmf_scan_results *scan_results;
+ struct brcmf_cfg80211_scan_req *scan_req_int; /* scan request object
+ for internal purpose */
struct wl_cfg80211_bss_info *bss_info; /* bss information for
cfg80211 layer */
- struct wl_ie ie; /* information element object for
+ struct brcmf_cfg80211_ie ie; /* information element object for
internal purpose */
- struct semaphore event_sync; /* for synchronization of main event
- thread */
- struct wl_profile *profile; /* holding dongle profile */
- struct wl_iscan_ctrl *iscan; /* iscan controller */
- struct wl_connect_info conn_info; /* association information
- container */
- struct wl_fw_ctrl *fw; /* control firwmare / nvram paramter
- downloading */
- struct wl_pmk_list *pmk_list; /* wpa2 pmk list */
- struct task_struct *event_tsk; /* task of main event handler thread */
+ struct brcmf_cfg80211_profile *profile; /* holding dongle profile */
+ struct brcmf_cfg80211_iscan_ctrl *iscan; /* iscan controller */
+ struct brcmf_cfg80211_connect_info conn_info; /* association info */
+ struct brcmf_cfg80211_pmk_list *pmk_list; /* wpa2 pmk list */
+ struct work_struct event_work; /* event handler work struct */
unsigned long status; /* current dongle status */
void *pub;
u32 channel; /* current channel */
@@ -356,59 +312,64 @@ struct wl_priv {
bool dongle_up; /* indicate whether dongle up or not */
bool roam_on; /* on/off switch for dongle self-roaming */
bool scan_tried; /* indicates if first scan attempted */
- u8 *ioctl_buf; /* ioctl buffer */
- u8 *extra_buf; /* maily to grab assoc information */
+ u8 *dcmd_buf; /* dcmd buffer */
+ u8 *extra_buf; /* maily to grab assoc information */
struct dentry *debugfsdir;
- u8 ci[0] __attribute__ ((__aligned__(NETDEV_ALIGN)));
+ u8 ci[0] __aligned(NETDEV_ALIGN);
};
-#define wl_to_dev(w) (wiphy_dev(wl->wdev->wiphy))
-#define wl_to_wiphy(w) (w->wdev->wiphy)
-#define wiphy_to_wl(w) ((struct wl_priv *)(wiphy_priv(w)))
-#define wl_to_wdev(w) (w->wdev)
-#define wdev_to_wl(w) ((struct wl_priv *)(wdev_priv(w)))
-#define wl_to_ndev(w) (w->wdev->netdev)
-#define ndev_to_wl(n) (wdev_to_wl(n->ieee80211_ptr))
-#define ci_to_wl(c) (ci->wl)
-#define wl_to_ci(w) (&w->ci)
-#define wl_to_sr(w) (w->scan_req_int)
-#define wl_to_ie(w) (&w->ie)
-#define iscan_to_wl(i) ((struct wl_priv *)(i->data))
-#define wl_to_iscan(w) (w->iscan)
-#define wl_to_conn(w) (&w->conn_info)
-
-static inline struct wl_bss_info *next_bss(struct wl_scan_results *list,
- struct wl_bss_info *bss)
+static inline struct wiphy *cfg_to_wiphy(struct brcmf_cfg80211_priv *w)
+{
+ return w->wdev->wiphy;
+}
+
+static inline struct brcmf_cfg80211_priv *wiphy_to_cfg(struct wiphy *w)
+{
+ return (struct brcmf_cfg80211_priv *)(wiphy_priv(w));
+}
+
+static inline struct brcmf_cfg80211_priv *wdev_to_cfg(struct wireless_dev *wd)
+{
+ return (struct brcmf_cfg80211_priv *)(wdev_priv(wd));
+}
+
+static inline struct net_device *cfg_to_ndev(struct brcmf_cfg80211_priv *cfg)
+{
+ return cfg->wdev->netdev;
+}
+
+static inline struct brcmf_cfg80211_priv *ndev_to_cfg(struct net_device *ndev)
+{
+ return wdev_to_cfg(ndev->ieee80211_ptr);
+}
+
+#define iscan_to_cfg(i) ((struct brcmf_cfg80211_priv *)(i->data))
+#define cfg_to_iscan(w) (w->iscan)
+
+static inline struct
+brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_priv *cfg)
+{
+ return &cfg->conn_info;
+}
+
+static inline struct brcmf_bss_info *next_bss(struct brcmf_scan_results *list,
+ struct brcmf_bss_info *bss)
{
return bss = bss ?
- (struct wl_bss_info *)((unsigned long)bss +
+ (struct brcmf_bss_info *)((unsigned long)bss +
le32_to_cpu(bss->length)) :
list->bss_info;
}
-#define for_each_bss(list, bss, __i) \
- for (__i = 0; __i < list->count && __i < WL_AP_MAX; __i++, bss = next_bss(list, bss))
+extern struct brcmf_cfg80211_dev *brcmf_cfg80211_attach(struct net_device *ndev,
+ struct device *busdev,
+ void *data);
+extern void brcmf_cfg80211_detach(struct brcmf_cfg80211_dev *cfg);
-extern s32 wl_cfg80211_attach(struct net_device *ndev, void *data);
-extern void wl_cfg80211_detach(void);
/* event handler from dongle */
-extern void wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t *e,
- void *data);
-extern void wl_cfg80211_sdio_func(void *func); /* set sdio function info */
-extern struct sdio_func *wl_cfg80211_get_sdio_func(void); /* set sdio function info */
-extern s32 wl_cfg80211_up(void); /* dongle up */
-extern s32 wl_cfg80211_down(void); /* dongle down */
-extern void wl_cfg80211_dbg_level(u32 level); /* set dongle
- debugging level */
-extern void *wl_cfg80211_request_fw(s8 *file_name); /* request fw /nvram
- downloading */
-extern s32 wl_cfg80211_read_fw(s8 *buf, u32 size); /* read fw
- image */
-extern void wl_cfg80211_release_fw(void); /* release fw */
-extern s8 *wl_cfg80211_get_fwname(void); /* get firmware name for
- the dongle */
-extern s8 *wl_cfg80211_get_nvramname(void); /* get nvram name for
- the dongle */
-extern void wl_os_wd_timer(struct net_device *ndev, uint wdtick);
+extern void brcmf_cfg80211_event(struct net_device *ndev,
+ const struct brcmf_event_msg *e, void *data);
+extern s32 brcmf_cfg80211_up(struct brcmf_cfg80211_dev *cfg_dev);
+extern s32 brcmf_cfg80211_down(struct brcmf_cfg80211_dev *cfg_dev);
#endif /* _wl_cfg80211_h_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/Makefile b/drivers/net/wireless/brcm80211/brcmsmac/Makefile
index 8d75fe19ca9a..c2eb2d0af386 100644
--- a/drivers/staging/brcm80211/brcmsmac/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmsmac/Makefile
@@ -15,43 +15,35 @@
# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-ccflags-y := \
- -DWLC_HIGH \
- -DWLC_LOW \
- -DSTA \
- -DWME \
- -DWL11N \
- -DDBAND \
- -DBCMNVRAMR \
- -Idrivers/staging/brcm80211/brcmsmac \
- -Idrivers/staging/brcm80211/brcmsmac/phy \
- -Idrivers/staging/brcm80211/include
+ccflags-y := \
+ -D__CHECK_ENDIAN__ \
+ -Idrivers/net/wireless/brcm80211/brcmsmac \
+ -Idrivers/net/wireless/brcm80211/brcmsmac/phy \
+ -Idrivers/net/wireless/brcm80211/include
BRCMSMAC_OFILES := \
- wl_mac80211.o \
- wl_ucode_loader.o \
- wlc_alloc.o \
- wlc_ampdu.o \
- wlc_antsel.o \
- wlc_bmac.o \
- wlc_channel.o \
- wlc_main.o \
- wlc_phy_shim.o \
- wlc_pmu.o \
- wlc_rate.o \
- wlc_stf.o \
+ mac80211_if.o \
+ ucode_loader.o \
+ ampdu.o \
+ antsel.o \
+ channel.o \
+ main.o \
+ phy_shim.o \
+ pmu.o \
+ rate.o \
+ stf.o \
aiutils.o \
- phy/wlc_phy_cmn.o \
- phy/wlc_phy_lcn.o \
- phy/wlc_phy_n.o \
- phy/wlc_phytbl_lcn.o \
- phy/wlc_phytbl_n.o \
- phy/wlc_phy_qmath.o \
- bcmotp.o \
- bcmsrom.o \
- hnddma.o \
+ phy/phy_cmn.o \
+ phy/phy_lcn.o \
+ phy/phy_n.o \
+ phy/phytbl_lcn.o \
+ phy/phytbl_n.o \
+ phy/phy_qmath.o \
+ otp.o \
+ srom.o \
+ dma.o \
nicpci.o \
- nvram.o
+ brcms_trace_events.o
MODULEPFX := brcmsmac
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
new file mode 100644
index 000000000000..025fa0eb6f47
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
@@ -0,0 +1,2079 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * File contents: support functions for PCI/PCIe
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <defs.h>
+#include <chipcommon.h>
+#include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
+#include <soc.h>
+#include "types.h"
+#include "pub.h"
+#include "pmu.h"
+#include "srom.h"
+#include "nicpci.h"
+#include "aiutils.h"
+
+/* slow_clk_ctl */
+ /* slow clock source mask */
+#define SCC_SS_MASK 0x00000007
+ /* source of slow clock is LPO */
+#define SCC_SS_LPO 0x00000000
+ /* source of slow clock is crystal */
+#define SCC_SS_XTAL 0x00000001
+ /* source of slow clock is PCI */
+#define SCC_SS_PCI 0x00000002
+ /* LPOFreqSel, 1: 160Khz, 0: 32KHz */
+#define SCC_LF 0x00000200
+ /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */
+#define SCC_LP 0x00000400
+ /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */
+#define SCC_FS 0x00000800
+ /* IgnorePllOffReq, 1/0:
+ * power logic ignores/honors PLL clock disable requests from core
+ */
+#define SCC_IP 0x00001000
+ /* XtalControlEn, 1/0:
+ * power logic does/doesn't disable crystal when appropriate
+ */
+#define SCC_XC 0x00002000
+ /* XtalPU (RO), 1/0: crystal running/disabled */
+#define SCC_XP 0x00004000
+ /* ClockDivider (SlowClk = 1/(4+divisor)) */
+#define SCC_CD_MASK 0xffff0000
+#define SCC_CD_SHIFT 16
+
+/* system_clk_ctl */
+ /* ILPen: Enable Idle Low Power */
+#define SYCC_IE 0x00000001
+ /* ALPen: Enable Active Low Power */
+#define SYCC_AE 0x00000002
+ /* ForcePLLOn */
+#define SYCC_FP 0x00000004
+ /* Force ALP (or HT if ALPen is not set */
+#define SYCC_AR 0x00000008
+ /* Force HT */
+#define SYCC_HR 0x00000010
+ /* ClkDiv (ILP = 1/(4 * (divisor + 1)) */
+#define SYCC_CD_MASK 0xffff0000
+#define SYCC_CD_SHIFT 16
+
+#define CST4329_SPROM_OTP_SEL_MASK 0x00000003
+ /* OTP is powered up, use def. CIS, no SPROM */
+#define CST4329_DEFCIS_SEL 0
+ /* OTP is powered up, SPROM is present */
+#define CST4329_SPROM_SEL 1
+ /* OTP is powered up, no SPROM */
+#define CST4329_OTP_SEL 2
+ /* OTP is powered down, SPROM is present */
+#define CST4329_OTP_PWRDN 3
+
+#define CST4329_SPI_SDIO_MODE_MASK 0x00000004
+#define CST4329_SPI_SDIO_MODE_SHIFT 2
+
+/* 43224 chip-specific ChipControl register bits */
+#define CCTRL43224_GPIO_TOGGLE 0x8000
+ /* 12 mA drive strength */
+#define CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0
+ /* 12 mA drive strength for later 43224s */
+#define CCTRL_43224B0_12MA_LED_DRIVE 0xF0
+
+/* 43236 Chip specific ChipStatus register bits */
+#define CST43236_SFLASH_MASK 0x00000040
+#define CST43236_OTP_MASK 0x00000080
+#define CST43236_HSIC_MASK 0x00000100 /* USB/HSIC */
+#define CST43236_BP_CLK 0x00000200 /* 120/96Mbps */
+#define CST43236_BOOT_MASK 0x00001800
+#define CST43236_BOOT_SHIFT 11
+#define CST43236_BOOT_FROM_SRAM 0 /* boot from SRAM, ARM in reset */
+#define CST43236_BOOT_FROM_ROM 1 /* boot from ROM */
+#define CST43236_BOOT_FROM_FLASH 2 /* boot from FLASH */
+#define CST43236_BOOT_FROM_INVALID 3
+
+/* 4331 chip-specific ChipControl register bits */
+ /* 0 disable */
+#define CCTRL4331_BT_COEXIST (1<<0)
+ /* 0 SECI is disabled (JTAG functional) */
+#define CCTRL4331_SECI (1<<1)
+ /* 0 disable */
+#define CCTRL4331_EXT_LNA (1<<2)
+ /* sprom/gpio13-15 mux */
+#define CCTRL4331_SPROM_GPIO13_15 (1<<3)
+ /* 0 ext pa disable, 1 ext pa enabled */
+#define CCTRL4331_EXTPA_EN (1<<4)
+ /* set drive out GPIO_CLK on sprom_cs pin */
+#define CCTRL4331_GPIOCLK_ON_SPROMCS (1<<5)
+ /* use sprom_cs pin as PCIE mdio interface */
+#define CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6)
+ /* aband extpa will be at gpio2/5 and sprom_dout */
+#define CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7)
+ /* override core control on pipe_AuxClkEnable */
+#define CCTRL4331_OVR_PIPEAUXCLKEN (1<<8)
+ /* override core control on pipe_AuxPowerDown */
+#define CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9)
+ /* pcie_auxclkenable */
+#define CCTRL4331_PCIE_AUXCLKEN (1<<10)
+ /* pcie_pipe_pllpowerdown */
+#define CCTRL4331_PCIE_PIPE_PLLDOWN (1<<11)
+ /* enable bt_shd0 at gpio4 */
+#define CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16)
+ /* enable bt_shd1 at gpio5 */
+#define CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17)
+
+/* 4331 Chip specific ChipStatus register bits */
+ /* crystal frequency 20/40Mhz */
+#define CST4331_XTAL_FREQ 0x00000001
+#define CST4331_SPROM_PRESENT 0x00000002
+#define CST4331_OTP_PRESENT 0x00000004
+#define CST4331_LDO_RF 0x00000008
+#define CST4331_LDO_PAR 0x00000010
+
+/* 4319 chip-specific ChipStatus register bits */
+#define CST4319_SPI_CPULESSUSB 0x00000001
+#define CST4319_SPI_CLK_POL 0x00000002
+#define CST4319_SPI_CLK_PH 0x00000008
+ /* gpio [7:6], SDIO CIS selection */
+#define CST4319_SPROM_OTP_SEL_MASK 0x000000c0
+#define CST4319_SPROM_OTP_SEL_SHIFT 6
+ /* use default CIS, OTP is powered up */
+#define CST4319_DEFCIS_SEL 0x00000000
+ /* use SPROM, OTP is powered up */
+#define CST4319_SPROM_SEL 0x00000040
+ /* use OTP, OTP is powered up */
+#define CST4319_OTP_SEL 0x00000080
+ /* use SPROM, OTP is powered down */
+#define CST4319_OTP_PWRDN 0x000000c0
+ /* gpio [8], sdio/usb mode */
+#define CST4319_SDIO_USB_MODE 0x00000100
+#define CST4319_REMAP_SEL_MASK 0x00000600
+#define CST4319_ILPDIV_EN 0x00000800
+#define CST4319_XTAL_PD_POL 0x00001000
+#define CST4319_LPO_SEL 0x00002000
+#define CST4319_RES_INIT_MODE 0x0000c000
+ /* PALDO is configured with external PNP */
+#define CST4319_PALDO_EXTPNP 0x00010000
+#define CST4319_CBUCK_MODE_MASK 0x00060000
+#define CST4319_CBUCK_MODE_BURST 0x00020000
+#define CST4319_CBUCK_MODE_LPBURST 0x00060000
+#define CST4319_RCAL_VALID 0x01000000
+#define CST4319_RCAL_VALUE_MASK 0x3e000000
+#define CST4319_RCAL_VALUE_SHIFT 25
+
+/* 4336 chip-specific ChipStatus register bits */
+#define CST4336_SPI_MODE_MASK 0x00000001
+#define CST4336_SPROM_PRESENT 0x00000002
+#define CST4336_OTP_PRESENT 0x00000004
+#define CST4336_ARMREMAP_0 0x00000008
+#define CST4336_ILPDIV_EN_MASK 0x00000010
+#define CST4336_ILPDIV_EN_SHIFT 4
+#define CST4336_XTAL_PD_POL_MASK 0x00000020
+#define CST4336_XTAL_PD_POL_SHIFT 5
+#define CST4336_LPO_SEL_MASK 0x00000040
+#define CST4336_LPO_SEL_SHIFT 6
+#define CST4336_RES_INIT_MODE_MASK 0x00000180
+#define CST4336_RES_INIT_MODE_SHIFT 7
+#define CST4336_CBUCK_MODE_MASK 0x00000600
+#define CST4336_CBUCK_MODE_SHIFT 9
+
+/* 4313 chip-specific ChipStatus register bits */
+#define CST4313_SPROM_PRESENT 1
+#define CST4313_OTP_PRESENT 2
+#define CST4313_SPROM_OTP_SEL_MASK 0x00000002
+#define CST4313_SPROM_OTP_SEL_SHIFT 0
+
+/* 4313 Chip specific ChipControl register bits */
+ /* 12 mA drive strengh for later 4313 */
+#define CCTRL_4313_12MA_LED_DRIVE 0x00000007
+
+/* Manufacturer Ids */
+#define MFGID_ARM 0x43b
+#define MFGID_BRCM 0x4bf
+#define MFGID_MIPS 0x4a7
+
+/* Enumeration ROM registers */
+#define ER_EROMENTRY 0x000
+#define ER_REMAPCONTROL 0xe00
+#define ER_REMAPSELECT 0xe04
+#define ER_MASTERSELECT 0xe10
+#define ER_ITCR 0xf00
+#define ER_ITIP 0xf04
+
+/* Erom entries */
+#define ER_TAG 0xe
+#define ER_TAG1 0x6
+#define ER_VALID 1
+#define ER_CI 0
+#define ER_MP 2
+#define ER_ADD 4
+#define ER_END 0xe
+#define ER_BAD 0xffffffff
+
+/* EROM CompIdentA */
+#define CIA_MFG_MASK 0xfff00000
+#define CIA_MFG_SHIFT 20
+#define CIA_CID_MASK 0x000fff00
+#define CIA_CID_SHIFT 8
+#define CIA_CCL_MASK 0x000000f0
+#define CIA_CCL_SHIFT 4
+
+/* EROM CompIdentB */
+#define CIB_REV_MASK 0xff000000
+#define CIB_REV_SHIFT 24
+#define CIB_NSW_MASK 0x00f80000
+#define CIB_NSW_SHIFT 19
+#define CIB_NMW_MASK 0x0007c000
+#define CIB_NMW_SHIFT 14
+#define CIB_NSP_MASK 0x00003e00
+#define CIB_NSP_SHIFT 9
+#define CIB_NMP_MASK 0x000001f0
+#define CIB_NMP_SHIFT 4
+
+/* EROM AddrDesc */
+#define AD_ADDR_MASK 0xfffff000
+#define AD_SP_MASK 0x00000f00
+#define AD_SP_SHIFT 8
+#define AD_ST_MASK 0x000000c0
+#define AD_ST_SHIFT 6
+#define AD_ST_SLAVE 0x00000000
+#define AD_ST_BRIDGE 0x00000040
+#define AD_ST_SWRAP 0x00000080
+#define AD_ST_MWRAP 0x000000c0
+#define AD_SZ_MASK 0x00000030
+#define AD_SZ_SHIFT 4
+#define AD_SZ_4K 0x00000000
+#define AD_SZ_8K 0x00000010
+#define AD_SZ_16K 0x00000020
+#define AD_SZ_SZD 0x00000030
+#define AD_AG32 0x00000008
+#define AD_ADDR_ALIGN 0x00000fff
+#define AD_SZ_BASE 0x00001000 /* 4KB */
+
+/* EROM SizeDesc */
+#define SD_SZ_MASK 0xfffff000
+#define SD_SG32 0x00000008
+#define SD_SZ_ALIGN 0x00000fff
+
+/* PCI config space bit 4 for 4306c0 slow clock source */
+#define PCI_CFG_GPIO_SCS 0x10
+/* PCI config space GPIO 14 for Xtal power-up */
+#define PCI_CFG_GPIO_XTAL 0x40
+/* PCI config space GPIO 15 for PLL power-down */
+#define PCI_CFG_GPIO_PLL 0x80
+
+/* power control defines */
+#define PLL_DELAY 150 /* us pll on delay */
+#define FREF_DELAY 200 /* us fref change delay */
+#define XTAL_ON_DELAY 1000 /* us crystal power-on delay */
+
+/* resetctrl */
+#define AIRC_RESET 1
+
+#define NOREV -1 /* Invalid rev */
+
+/* GPIO Based LED powersave defines */
+#define DEFAULT_GPIO_ONTIME 10 /* Default: 10% on */
+#define DEFAULT_GPIO_OFFTIME 90 /* Default: 10% on */
+
+/* When Srom support present, fields in sromcontrol */
+#define SRC_START 0x80000000
+#define SRC_BUSY 0x80000000
+#define SRC_OPCODE 0x60000000
+#define SRC_OP_READ 0x00000000
+#define SRC_OP_WRITE 0x20000000
+#define SRC_OP_WRDIS 0x40000000
+#define SRC_OP_WREN 0x60000000
+#define SRC_OTPSEL 0x00000010
+#define SRC_LOCK 0x00000008
+#define SRC_SIZE_MASK 0x00000006
+#define SRC_SIZE_1K 0x00000000
+#define SRC_SIZE_4K 0x00000002
+#define SRC_SIZE_16K 0x00000004
+#define SRC_SIZE_SHIFT 1
+#define SRC_PRESENT 0x00000001
+
+/* External PA enable mask */
+#define GPIO_CTRL_EPA_EN_MASK 0x40
+
+#define DEFAULT_GPIOTIMERVAL \
+ ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME)
+
+#define BADIDX (SI_MAXCORES + 1)
+
+/* Newer chips can access PCI/PCIE and CC core without requiring to change
+ * PCI BAR0 WIN
+ */
+#define SI_FAST(si) (((si)->pub.buscoretype == PCIE_CORE_ID) || \
+ (((si)->pub.buscoretype == PCI_CORE_ID) && \
+ (si)->pub.buscorerev >= 13))
+
+#define CCREGS_FAST(si) (((char __iomem *)((si)->curmap) + \
+ PCI_16KB0_CCREGS_OFFSET))
+
+#define IS_SIM(chippkg) \
+ ((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID))
+
+/*
+ * Macros to disable/restore function core(D11, ENET, ILINE20, etc) interrupts
+ * before after core switching to avoid invalid register accesss inside ISR.
+ */
+#define INTR_OFF(si, intr_val) \
+ if ((si)->intrsoff_fn && \
+ (si)->coreid[(si)->curidx] == (si)->dev_coreid) \
+ intr_val = (*(si)->intrsoff_fn)((si)->intr_arg)
+
+#define INTR_RESTORE(si, intr_val) \
+ if ((si)->intrsrestore_fn && \
+ (si)->coreid[(si)->curidx] == (si)->dev_coreid) \
+ (*(si)->intrsrestore_fn)((si)->intr_arg, intr_val)
+
+#define PCI(si) ((si)->pub.buscoretype == PCI_CORE_ID)
+#define PCIE(si) ((si)->pub.buscoretype == PCIE_CORE_ID)
+
+#define PCI_FORCEHT(si) (PCIE(si) && (si->pub.chip == BCM4716_CHIP_ID))
+
+#ifdef BCMDBG
+#define SI_MSG(args) printk args
+#else
+#define SI_MSG(args)
+#endif /* BCMDBG */
+
+#define GOODCOREADDR(x, b) \
+ (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \
+ IS_ALIGNED((x), SI_CORE_SIZE))
+
+#define PCIEREGS(si) ((__iomem char *)((si)->curmap) + \
+ PCI_16KB0_PCIREGS_OFFSET)
+
+struct aidmp {
+ u32 oobselina30; /* 0x000 */
+ u32 oobselina74; /* 0x004 */
+ u32 PAD[6];
+ u32 oobselinb30; /* 0x020 */
+ u32 oobselinb74; /* 0x024 */
+ u32 PAD[6];
+ u32 oobselinc30; /* 0x040 */
+ u32 oobselinc74; /* 0x044 */
+ u32 PAD[6];
+ u32 oobselind30; /* 0x060 */
+ u32 oobselind74; /* 0x064 */
+ u32 PAD[38];
+ u32 oobselouta30; /* 0x100 */
+ u32 oobselouta74; /* 0x104 */
+ u32 PAD[6];
+ u32 oobseloutb30; /* 0x120 */
+ u32 oobseloutb74; /* 0x124 */
+ u32 PAD[6];
+ u32 oobseloutc30; /* 0x140 */
+ u32 oobseloutc74; /* 0x144 */
+ u32 PAD[6];
+ u32 oobseloutd30; /* 0x160 */
+ u32 oobseloutd74; /* 0x164 */
+ u32 PAD[38];
+ u32 oobsynca; /* 0x200 */
+ u32 oobseloutaen; /* 0x204 */
+ u32 PAD[6];
+ u32 oobsyncb; /* 0x220 */
+ u32 oobseloutben; /* 0x224 */
+ u32 PAD[6];
+ u32 oobsyncc; /* 0x240 */
+ u32 oobseloutcen; /* 0x244 */
+ u32 PAD[6];
+ u32 oobsyncd; /* 0x260 */
+ u32 oobseloutden; /* 0x264 */
+ u32 PAD[38];
+ u32 oobaextwidth; /* 0x300 */
+ u32 oobainwidth; /* 0x304 */
+ u32 oobaoutwidth; /* 0x308 */
+ u32 PAD[5];
+ u32 oobbextwidth; /* 0x320 */
+ u32 oobbinwidth; /* 0x324 */
+ u32 oobboutwidth; /* 0x328 */
+ u32 PAD[5];
+ u32 oobcextwidth; /* 0x340 */
+ u32 oobcinwidth; /* 0x344 */
+ u32 oobcoutwidth; /* 0x348 */
+ u32 PAD[5];
+ u32 oobdextwidth; /* 0x360 */
+ u32 oobdinwidth; /* 0x364 */
+ u32 oobdoutwidth; /* 0x368 */
+ u32 PAD[37];
+ u32 ioctrlset; /* 0x400 */
+ u32 ioctrlclear; /* 0x404 */
+ u32 ioctrl; /* 0x408 */
+ u32 PAD[61];
+ u32 iostatus; /* 0x500 */
+ u32 PAD[127];
+ u32 ioctrlwidth; /* 0x700 */
+ u32 iostatuswidth; /* 0x704 */
+ u32 PAD[62];
+ u32 resetctrl; /* 0x800 */
+ u32 resetstatus; /* 0x804 */
+ u32 resetreadid; /* 0x808 */
+ u32 resetwriteid; /* 0x80c */
+ u32 PAD[60];
+ u32 errlogctrl; /* 0x900 */
+ u32 errlogdone; /* 0x904 */
+ u32 errlogstatus; /* 0x908 */
+ u32 errlogaddrlo; /* 0x90c */
+ u32 errlogaddrhi; /* 0x910 */
+ u32 errlogid; /* 0x914 */
+ u32 errloguser; /* 0x918 */
+ u32 errlogflags; /* 0x91c */
+ u32 PAD[56];
+ u32 intstatus; /* 0xa00 */
+ u32 PAD[127];
+ u32 config; /* 0xe00 */
+ u32 PAD[63];
+ u32 itcr; /* 0xf00 */
+ u32 PAD[3];
+ u32 itipooba; /* 0xf10 */
+ u32 itipoobb; /* 0xf14 */
+ u32 itipoobc; /* 0xf18 */
+ u32 itipoobd; /* 0xf1c */
+ u32 PAD[4];
+ u32 itipoobaout; /* 0xf30 */
+ u32 itipoobbout; /* 0xf34 */
+ u32 itipoobcout; /* 0xf38 */
+ u32 itipoobdout; /* 0xf3c */
+ u32 PAD[4];
+ u32 itopooba; /* 0xf50 */
+ u32 itopoobb; /* 0xf54 */
+ u32 itopoobc; /* 0xf58 */
+ u32 itopoobd; /* 0xf5c */
+ u32 PAD[4];
+ u32 itopoobain; /* 0xf70 */
+ u32 itopoobbin; /* 0xf74 */
+ u32 itopoobcin; /* 0xf78 */
+ u32 itopoobdin; /* 0xf7c */
+ u32 PAD[4];
+ u32 itopreset; /* 0xf90 */
+ u32 PAD[15];
+ u32 peripherialid4; /* 0xfd0 */
+ u32 peripherialid5; /* 0xfd4 */
+ u32 peripherialid6; /* 0xfd8 */
+ u32 peripherialid7; /* 0xfdc */
+ u32 peripherialid0; /* 0xfe0 */
+ u32 peripherialid1; /* 0xfe4 */
+ u32 peripherialid2; /* 0xfe8 */
+ u32 peripherialid3; /* 0xfec */
+ u32 componentid0; /* 0xff0 */
+ u32 componentid1; /* 0xff4 */
+ u32 componentid2; /* 0xff8 */
+ u32 componentid3; /* 0xffc */
+};
+
+/* EROM parsing */
+
+static u32
+get_erom_ent(struct si_pub *sih, u32 __iomem **eromptr, u32 mask, u32 match)
+{
+ u32 ent;
+ uint inv = 0, nom = 0;
+
+ while (true) {
+ ent = R_REG(*eromptr);
+ (*eromptr)++;
+
+ if (mask == 0)
+ break;
+
+ if ((ent & ER_VALID) == 0) {
+ inv++;
+ continue;
+ }
+
+ if (ent == (ER_END | ER_VALID))
+ break;
+
+ if ((ent & mask) == match)
+ break;
+
+ nom++;
+ }
+
+ return ent;
+}
+
+static u32
+get_asd(struct si_pub *sih, u32 __iomem **eromptr, uint sp, uint ad, uint st,
+ u32 *addrl, u32 *addrh, u32 *sizel, u32 *sizeh)
+{
+ u32 asd, sz, szd;
+
+ asd = get_erom_ent(sih, eromptr, ER_VALID, ER_VALID);
+ if (((asd & ER_TAG1) != ER_ADD) ||
+ (((asd & AD_SP_MASK) >> AD_SP_SHIFT) != sp) ||
+ ((asd & AD_ST_MASK) != st)) {
+ /* This is not what we want, "push" it back */
+ (*eromptr)--;
+ return 0;
+ }
+ *addrl = asd & AD_ADDR_MASK;
+ if (asd & AD_AG32)
+ *addrh = get_erom_ent(sih, eromptr, 0, 0);
+ else
+ *addrh = 0;
+ *sizeh = 0;
+ sz = asd & AD_SZ_MASK;
+ if (sz == AD_SZ_SZD) {
+ szd = get_erom_ent(sih, eromptr, 0, 0);
+ *sizel = szd & SD_SZ_MASK;
+ if (szd & SD_SG32)
+ *sizeh = get_erom_ent(sih, eromptr, 0, 0);
+ } else
+ *sizel = AD_SZ_BASE << (sz >> AD_SZ_SHIFT);
+
+ return asd;
+}
+
+static void ai_hwfixup(struct si_info *sii)
+{
+}
+
+/* parse the enumeration rom to identify all cores */
+static void ai_scan(struct si_pub *sih, struct chipcregs __iomem *cc)
+{
+ struct si_info *sii = (struct si_info *)sih;
+
+ u32 erombase;
+ u32 __iomem *eromptr, *eromlim;
+ void __iomem *regs = cc;
+
+ erombase = R_REG(&cc->eromptr);
+
+ /* Set wrappers address */
+ sii->curwrap = (void *)((unsigned long)cc + SI_CORE_SIZE);
+
+ /* Now point the window at the erom */
+ pci_write_config_dword(sii->pbus, PCI_BAR0_WIN, erombase);
+ eromptr = regs;
+ eromlim = eromptr + (ER_REMAPCONTROL / sizeof(u32));
+
+ while (eromptr < eromlim) {
+ u32 cia, cib, cid, mfg, crev, nmw, nsw, nmp, nsp;
+ u32 mpd, asd, addrl, addrh, sizel, sizeh;
+ u32 __iomem *base;
+ uint i, j, idx;
+ bool br;
+
+ br = false;
+
+ /* Grok a component */
+ cia = get_erom_ent(sih, &eromptr, ER_TAG, ER_CI);
+ if (cia == (ER_END | ER_VALID)) {
+ /* Found END of erom */
+ ai_hwfixup(sii);
+ return;
+ }
+ base = eromptr - 1;
+ cib = get_erom_ent(sih, &eromptr, 0, 0);
+
+ if ((cib & ER_TAG) != ER_CI) {
+ /* CIA not followed by CIB */
+ goto error;
+ }
+
+ cid = (cia & CIA_CID_MASK) >> CIA_CID_SHIFT;
+ mfg = (cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT;
+ crev = (cib & CIB_REV_MASK) >> CIB_REV_SHIFT;
+ nmw = (cib & CIB_NMW_MASK) >> CIB_NMW_SHIFT;
+ nsw = (cib & CIB_NSW_MASK) >> CIB_NSW_SHIFT;
+ nmp = (cib & CIB_NMP_MASK) >> CIB_NMP_SHIFT;
+ nsp = (cib & CIB_NSP_MASK) >> CIB_NSP_SHIFT;
+
+ if (((mfg == MFGID_ARM) && (cid == DEF_AI_COMP)) || (nsp == 0))
+ continue;
+ if ((nmw + nsw == 0)) {
+ /* A component which is not a core */
+ if (cid == OOB_ROUTER_CORE_ID) {
+ asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE,
+ &addrl, &addrh, &sizel, &sizeh);
+ if (asd != 0)
+ sii->oob_router = addrl;
+ }
+ continue;
+ }
+
+ idx = sii->numcores;
+/* sii->eromptr[idx] = base; */
+ sii->cia[idx] = cia;
+ sii->cib[idx] = cib;
+ sii->coreid[idx] = cid;
+
+ for (i = 0; i < nmp; i++) {
+ mpd = get_erom_ent(sih, &eromptr, ER_VALID, ER_VALID);
+ if ((mpd & ER_TAG) != ER_MP) {
+ /* Not enough MP entries for component */
+ goto error;
+ }
+ }
+
+ /* First Slave Address Descriptor should be port 0:
+ * the main register space for the core
+ */
+ asd =
+ get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, &addrl, &addrh,
+ &sizel, &sizeh);
+ if (asd == 0) {
+ /* Try again to see if it is a bridge */
+ asd =
+ get_asd(sih, &eromptr, 0, 0, AD_ST_BRIDGE, &addrl,
+ &addrh, &sizel, &sizeh);
+ if (asd != 0)
+ br = true;
+ else if ((addrh != 0) || (sizeh != 0)
+ || (sizel != SI_CORE_SIZE)) {
+ /* First Slave ASD for core malformed */
+ goto error;
+ }
+ }
+ sii->coresba[idx] = addrl;
+ sii->coresba_size[idx] = sizel;
+ /* Get any more ASDs in port 0 */
+ j = 1;
+ do {
+ asd =
+ get_asd(sih, &eromptr, 0, j, AD_ST_SLAVE, &addrl,
+ &addrh, &sizel, &sizeh);
+ if ((asd != 0) && (j == 1) && (sizel == SI_CORE_SIZE)) {
+ sii->coresba2[idx] = addrl;
+ sii->coresba2_size[idx] = sizel;
+ }
+ j++;
+ } while (asd != 0);
+
+ /* Go through the ASDs for other slave ports */
+ for (i = 1; i < nsp; i++) {
+ j = 0;
+ do {
+ asd =
+ get_asd(sih, &eromptr, i, j++, AD_ST_SLAVE,
+ &addrl, &addrh, &sizel, &sizeh);
+ } while (asd != 0);
+ if (j == 0) {
+ /* SP has no address descriptors */
+ goto error;
+ }
+ }
+
+ /* Now get master wrappers */
+ for (i = 0; i < nmw; i++) {
+ asd =
+ get_asd(sih, &eromptr, i, 0, AD_ST_MWRAP, &addrl,
+ &addrh, &sizel, &sizeh);
+ if (asd == 0) {
+ /* Missing descriptor for MW */
+ goto error;
+ }
+ if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) {
+ /* Master wrapper %d is not 4KB */
+ goto error;
+ }
+ if (i == 0)
+ sii->wrapba[idx] = addrl;
+ }
+
+ /* And finally slave wrappers */
+ for (i = 0; i < nsw; i++) {
+ uint fwp = (nsp == 1) ? 0 : 1;
+ asd =
+ get_asd(sih, &eromptr, fwp + i, 0, AD_ST_SWRAP,
+ &addrl, &addrh, &sizel, &sizeh);
+ if (asd == 0) {
+ /* Missing descriptor for SW */
+ goto error;
+ }
+ if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) {
+ /* Slave wrapper is not 4KB */
+ goto error;
+ }
+ if ((nmw == 0) && (i == 0))
+ sii->wrapba[idx] = addrl;
+ }
+
+ /* Don't record bridges */
+ if (br)
+ continue;
+
+ /* Done with core */
+ sii->numcores++;
+ }
+
+ error:
+ /* Reached end of erom without finding END */
+ sii->numcores = 0;
+ return;
+}
+
+/*
+ * This function changes the logical "focus" to the indicated core.
+ * Return the current core's virtual address. Since each core starts with the
+ * same set of registers (BIST, clock control, etc), the returned address
+ * contains the first register of this 'common' register block (not to be
+ * confused with 'common core').
+ */
+void __iomem *ai_setcoreidx(struct si_pub *sih, uint coreidx)
+{
+ struct si_info *sii = (struct si_info *)sih;
+ u32 addr = sii->coresba[coreidx];
+ u32 wrap = sii->wrapba[coreidx];
+
+ if (coreidx >= sii->numcores)
+ return NULL;
+
+ /* point bar0 window */
+ pci_write_config_dword(sii->pbus, PCI_BAR0_WIN, addr);
+ /* point bar0 2nd 4KB window */
+ pci_write_config_dword(sii->pbus, PCI_BAR0_WIN2, wrap);
+ sii->curidx = coreidx;
+
+ return sii->curmap;
+}
+
+/* Return the number of address spaces in current core */
+int ai_numaddrspaces(struct si_pub *sih)
+{
+ return 2;
+}
+
+/* Return the address of the nth address space in the current core */
+u32 ai_addrspace(struct si_pub *sih, uint asidx)
+{
+ struct si_info *sii;
+ uint cidx;
+
+ sii = (struct si_info *)sih;
+ cidx = sii->curidx;
+
+ if (asidx == 0)
+ return sii->coresba[cidx];
+ else if (asidx == 1)
+ return sii->coresba2[cidx];
+ else {
+ /* Need to parse the erom again to find addr space */
+ return 0;
+ }
+}
+
+/* Return the size of the nth address space in the current core */
+u32 ai_addrspacesize(struct si_pub *sih, uint asidx)
+{
+ struct si_info *sii;
+ uint cidx;
+
+ sii = (struct si_info *)sih;
+ cidx = sii->curidx;
+
+ if (asidx == 0)
+ return sii->coresba_size[cidx];
+ else if (asidx == 1)
+ return sii->coresba2_size[cidx];
+ else {
+ /* Need to parse the erom again to find addr */
+ return 0;
+ }
+}
+
+uint ai_flag(struct si_pub *sih)
+{
+ struct si_info *sii;
+ struct aidmp *ai;
+
+ sii = (struct si_info *)sih;
+ ai = sii->curwrap;
+
+ return R_REG(&ai->oobselouta30) & 0x1f;
+}
+
+void ai_setint(struct si_pub *sih, int siflag)
+{
+}
+
+uint ai_corevendor(struct si_pub *sih)
+{
+ struct si_info *sii;
+ u32 cia;
+
+ sii = (struct si_info *)sih;
+ cia = sii->cia[sii->curidx];
+ return (cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT;
+}
+
+uint ai_corerev(struct si_pub *sih)
+{
+ struct si_info *sii;
+ u32 cib;
+
+ sii = (struct si_info *)sih;
+ cib = sii->cib[sii->curidx];
+ return (cib & CIB_REV_MASK) >> CIB_REV_SHIFT;
+}
+
+bool ai_iscoreup(struct si_pub *sih)
+{
+ struct si_info *sii;
+ struct aidmp *ai;
+
+ sii = (struct si_info *)sih;
+ ai = sii->curwrap;
+
+ return (((R_REG(&ai->ioctrl) & (SICF_FGC | SICF_CLOCK_EN)) ==
+ SICF_CLOCK_EN)
+ && ((R_REG(&ai->resetctrl) & AIRC_RESET) == 0));
+}
+
+void ai_core_cflags_wo(struct si_pub *sih, u32 mask, u32 val)
+{
+ struct si_info *sii;
+ struct aidmp *ai;
+ u32 w;
+
+ sii = (struct si_info *)sih;
+
+ ai = sii->curwrap;
+
+ if (mask || val) {
+ w = ((R_REG(&ai->ioctrl) & ~mask) | val);
+ W_REG(&ai->ioctrl, w);
+ }
+}
+
+u32 ai_core_cflags(struct si_pub *sih, u32 mask, u32 val)
+{
+ struct si_info *sii;
+ struct aidmp *ai;
+ u32 w;
+
+ sii = (struct si_info *)sih;
+ ai = sii->curwrap;
+
+ if (mask || val) {
+ w = ((R_REG(&ai->ioctrl) & ~mask) | val);
+ W_REG(&ai->ioctrl, w);
+ }
+
+ return R_REG(&ai->ioctrl);
+}
+
+/* return true if PCIE capability exists in the pci config space */
+static bool ai_ispcie(struct si_info *sii)
+{
+ u8 cap_ptr;
+
+ cap_ptr =
+ pcicore_find_pci_capability(sii->pbus, PCI_CAP_ID_EXP, NULL,
+ NULL);
+ if (!cap_ptr)
+ return false;
+
+ return true;
+}
+
+static bool ai_buscore_prep(struct si_info *sii)
+{
+ /* kludge to enable the clock on the 4306 which lacks a slowclock */
+ if (!ai_ispcie(sii))
+ ai_clkctl_xtal(&sii->pub, XTAL | PLL, ON);
+ return true;
+}
+
+u32 ai_core_sflags(struct si_pub *sih, u32 mask, u32 val)
+{
+ struct si_info *sii;
+ struct aidmp *ai;
+ u32 w;
+
+ sii = (struct si_info *)sih;
+ ai = sii->curwrap;
+
+ if (mask || val) {
+ w = ((R_REG(&ai->iostatus) & ~mask) | val);
+ W_REG(&ai->iostatus, w);
+ }
+
+ return R_REG(&ai->iostatus);
+}
+
+static bool
+ai_buscore_setup(struct si_info *sii, u32 savewin, uint *origidx)
+{
+ bool pci, pcie;
+ uint i;
+ uint pciidx, pcieidx, pcirev, pcierev;
+ struct chipcregs __iomem *cc;
+
+ cc = ai_setcoreidx(&sii->pub, SI_CC_IDX);
+
+ /* get chipcommon rev */
+ sii->pub.ccrev = (int)ai_corerev(&sii->pub);
+
+ /* get chipcommon chipstatus */
+ if (sii->pub.ccrev >= 11)
+ sii->pub.chipst = R_REG(&cc->chipstatus);
+
+ /* get chipcommon capabilites */
+ sii->pub.cccaps = R_REG(&cc->capabilities);
+ /* get chipcommon extended capabilities */
+
+ if (sii->pub.ccrev >= 35)
+ sii->pub.cccaps_ext = R_REG(&cc->capabilities_ext);
+
+ /* get pmu rev and caps */
+ if (sii->pub.cccaps & CC_CAP_PMU) {
+ sii->pub.pmucaps = R_REG(&cc->pmucapabilities);
+ sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK;
+ }
+
+ /* figure out bus/orignal core idx */
+ sii->pub.buscoretype = NODEV_CORE_ID;
+ sii->pub.buscorerev = NOREV;
+ sii->pub.buscoreidx = BADIDX;
+
+ pci = pcie = false;
+ pcirev = pcierev = NOREV;
+ pciidx = pcieidx = BADIDX;
+
+ for (i = 0; i < sii->numcores; i++) {
+ uint cid, crev;
+
+ ai_setcoreidx(&sii->pub, i);
+ cid = ai_coreid(&sii->pub);
+ crev = ai_corerev(&sii->pub);
+
+ if (cid == PCI_CORE_ID) {
+ pciidx = i;
+ pcirev = crev;
+ pci = true;
+ } else if (cid == PCIE_CORE_ID) {
+ pcieidx = i;
+ pcierev = crev;
+ pcie = true;
+ }
+
+ /* find the core idx before entering this func. */
+ if ((savewin && (savewin == sii->coresba[i])) ||
+ (cc == sii->regs[i]))
+ *origidx = i;
+ }
+
+ if (pci && pcie) {
+ if (ai_ispcie(sii))
+ pci = false;
+ else
+ pcie = false;
+ }
+ if (pci) {
+ sii->pub.buscoretype = PCI_CORE_ID;
+ sii->pub.buscorerev = pcirev;
+ sii->pub.buscoreidx = pciidx;
+ } else if (pcie) {
+ sii->pub.buscoretype = PCIE_CORE_ID;
+ sii->pub.buscorerev = pcierev;
+ sii->pub.buscoreidx = pcieidx;
+ }
+
+ /* fixup necessary chip/core configurations */
+ if (SI_FAST(sii)) {
+ if (!sii->pch) {
+ sii->pch = pcicore_init(&sii->pub, sii->pbus,
+ (__iomem void *)PCIEREGS(sii));
+ if (sii->pch == NULL)
+ return false;
+ }
+ }
+ if (ai_pci_fixcfg(&sii->pub)) {
+ /* si_doattach: si_pci_fixcfg failed */
+ return false;
+ }
+
+ /* return to the original core */
+ ai_setcoreidx(&sii->pub, *origidx);
+
+ return true;
+}
+
+/*
+ * get boardtype and boardrev
+ */
+static __used void ai_nvram_process(struct si_info *sii)
+{
+ uint w = 0;
+
+ /* do a pci config read to get subsystem id and subvendor id */
+ pci_read_config_dword(sii->pbus, PCI_SUBSYSTEM_VENDOR_ID, &w);
+
+ sii->pub.boardvendor = w & 0xffff;
+ sii->pub.boardtype = (w >> 16) & 0xffff;
+ sii->pub.boardflags = getintvar(&sii->pub, BRCMS_SROM_BOARDFLAGS);
+}
+
+static struct si_info *ai_doattach(struct si_info *sii,
+ void __iomem *regs, struct pci_dev *pbus)
+{
+ struct si_pub *sih = &sii->pub;
+ u32 w, savewin;
+ struct chipcregs __iomem *cc;
+ uint socitype;
+ uint origidx;
+
+ memset((unsigned char *) sii, 0, sizeof(struct si_info));
+
+ savewin = 0;
+
+ sih->buscoreidx = BADIDX;
+
+ sii->curmap = regs;
+ sii->pbus = pbus;
+
+ /* find Chipcommon address */
+ pci_read_config_dword(sii->pbus, PCI_BAR0_WIN, &savewin);
+ if (!GOODCOREADDR(savewin, SI_ENUM_BASE))
+ savewin = SI_ENUM_BASE;
+
+ pci_write_config_dword(sii->pbus, PCI_BAR0_WIN,
+ SI_ENUM_BASE);
+ cc = (struct chipcregs __iomem *) regs;
+
+ /* bus/core/clk setup for register access */
+ if (!ai_buscore_prep(sii))
+ return NULL;
+
+ /*
+ * ChipID recognition.
+ * We assume we can read chipid at offset 0 from the regs arg.
+ * If we add other chiptypes (or if we need to support old sdio
+ * hosts w/o chipcommon), some way of recognizing them needs to
+ * be added here.
+ */
+ w = R_REG(&cc->chipid);
+ socitype = (w & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
+ /* Might as wll fill in chip id rev & pkg */
+ sih->chip = w & CID_ID_MASK;
+ sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT;
+ sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT;
+
+ sih->issim = false;
+
+ /* scan for cores */
+ if (socitype == SOCI_AI) {
+ SI_MSG(("Found chip type AI (0x%08x)\n", w));
+ /* pass chipc address instead of original core base */
+ ai_scan(&sii->pub, cc);
+ } else {
+ /* Found chip of unknown type */
+ return NULL;
+ }
+ /* no cores found, bail out */
+ if (sii->numcores == 0)
+ return NULL;
+
+ /* bus/core/clk setup */
+ origidx = SI_CC_IDX;
+ if (!ai_buscore_setup(sii, savewin, &origidx))
+ goto exit;
+
+ /* Init nvram from sprom/otp if they exist */
+ if (srom_var_init(&sii->pub, cc))
+ goto exit;
+
+ ai_nvram_process(sii);
+
+ /* === NVRAM, clock is ready === */
+ cc = (struct chipcregs __iomem *) ai_setcore(sih, CC_CORE_ID, 0);
+ W_REG(&cc->gpiopullup, 0);
+ W_REG(&cc->gpiopulldown, 0);
+ ai_setcoreidx(sih, origidx);
+
+ /* PMU specific initializations */
+ if (sih->cccaps & CC_CAP_PMU) {
+ u32 xtalfreq;
+ si_pmu_init(sih);
+ si_pmu_chip_init(sih);
+
+ xtalfreq = si_pmu_measure_alpclk(sih);
+ si_pmu_pll_init(sih, xtalfreq);
+ si_pmu_res_init(sih);
+ si_pmu_swreg_init(sih);
+ }
+
+ /* setup the GPIO based LED powersave register */
+ w = getintvar(sih, BRCMS_SROM_LEDDC);
+ if (w == 0)
+ w = DEFAULT_GPIOTIMERVAL;
+ ai_corereg(sih, SI_CC_IDX, offsetof(struct chipcregs, gpiotimerval),
+ ~0, w);
+
+ if (PCIE(sii))
+ pcicore_attach(sii->pch, SI_DOATTACH);
+
+ if (sih->chip == BCM43224_CHIP_ID) {
+ /*
+ * enable 12 mA drive strenth for 43224 and
+ * set chipControl register bit 15
+ */
+ if (sih->chiprev == 0) {
+ SI_MSG(("Applying 43224A0 WARs\n"));
+ ai_corereg(sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol),
+ CCTRL43224_GPIO_TOGGLE,
+ CCTRL43224_GPIO_TOGGLE);
+ si_pmu_chipcontrol(sih, 0, CCTRL_43224A0_12MA_LED_DRIVE,
+ CCTRL_43224A0_12MA_LED_DRIVE);
+ }
+ if (sih->chiprev >= 1) {
+ SI_MSG(("Applying 43224B0+ WARs\n"));
+ si_pmu_chipcontrol(sih, 0, CCTRL_43224B0_12MA_LED_DRIVE,
+ CCTRL_43224B0_12MA_LED_DRIVE);
+ }
+ }
+
+ if (sih->chip == BCM4313_CHIP_ID) {
+ /*
+ * enable 12 mA drive strenth for 4313 and
+ * set chipControl register bit 1
+ */
+ SI_MSG(("Applying 4313 WARs\n"));
+ si_pmu_chipcontrol(sih, 0, CCTRL_4313_12MA_LED_DRIVE,
+ CCTRL_4313_12MA_LED_DRIVE);
+ }
+
+ return sii;
+
+ exit:
+ if (sii->pch)
+ pcicore_deinit(sii->pch);
+ sii->pch = NULL;
+
+ return NULL;
+}
+
+/*
+ * Allocate a si handle.
+ * devid - pci device id (used to determine chip#)
+ * osh - opaque OS handle
+ * regs - virtual address of initial core registers
+ */
+struct si_pub *
+ai_attach(void __iomem *regs, struct pci_dev *sdh)
+{
+ struct si_info *sii;
+
+ /* alloc struct si_info */
+ sii = kmalloc(sizeof(struct si_info), GFP_ATOMIC);
+ if (sii == NULL)
+ return NULL;
+
+ if (ai_doattach(sii, regs, sdh) == NULL) {
+ kfree(sii);
+ return NULL;
+ }
+
+ return (struct si_pub *) sii;
+}
+
+/* may be called with core in reset */
+void ai_detach(struct si_pub *sih)
+{
+ struct si_info *sii;
+
+ struct si_pub *si_local = NULL;
+ memcpy(&si_local, &sih, sizeof(struct si_pub **));
+
+ sii = (struct si_info *)sih;
+
+ if (sii == NULL)
+ return;
+
+ if (sii->pch)
+ pcicore_deinit(sii->pch);
+ sii->pch = NULL;
+
+ srom_free_vars(sih);
+ kfree(sii);
+}
+
+/* register driver interrupt disabling and restoring callback functions */
+void
+ai_register_intr_callback(struct si_pub *sih, void *intrsoff_fn,
+ void *intrsrestore_fn,
+ void *intrsenabled_fn, void *intr_arg)
+{
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+ sii->intr_arg = intr_arg;
+ sii->intrsoff_fn = (u32 (*)(void *)) intrsoff_fn;
+ sii->intrsrestore_fn = (void (*) (void *, u32)) intrsrestore_fn;
+ sii->intrsenabled_fn = (bool (*)(void *)) intrsenabled_fn;
+ /* save current core id. when this function called, the current core
+ * must be the core which provides driver functions(il, et, wl, etc.)
+ */
+ sii->dev_coreid = sii->coreid[sii->curidx];
+}
+
+void ai_deregister_intr_callback(struct si_pub *sih)
+{
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+ sii->intrsoff_fn = NULL;
+}
+
+uint ai_coreid(struct si_pub *sih)
+{
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+ return sii->coreid[sii->curidx];
+}
+
+uint ai_coreidx(struct si_pub *sih)
+{
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+ return sii->curidx;
+}
+
+bool ai_backplane64(struct si_pub *sih)
+{
+ return (sih->cccaps & CC_CAP_BKPLN64) != 0;
+}
+
+/* return index of coreid or BADIDX if not found */
+uint ai_findcoreidx(struct si_pub *sih, uint coreid, uint coreunit)
+{
+ struct si_info *sii;
+ uint found;
+ uint i;
+
+ sii = (struct si_info *)sih;
+
+ found = 0;
+
+ for (i = 0; i < sii->numcores; i++)
+ if (sii->coreid[i] == coreid) {
+ if (found == coreunit)
+ return i;
+ found++;
+ }
+
+ return BADIDX;
+}
+
+/*
+ * This function changes logical "focus" to the indicated core;
+ * must be called with interrupts off.
+ * Moreover, callers should keep interrupts off during switching
+ * out of and back to d11 core.
+ */
+void __iomem *ai_setcore(struct si_pub *sih, uint coreid, uint coreunit)
+{
+ uint idx;
+
+ idx = ai_findcoreidx(sih, coreid, coreunit);
+ if (idx >= SI_MAXCORES)
+ return NULL;
+
+ return ai_setcoreidx(sih, idx);
+}
+
+/* Turn off interrupt as required by ai_setcore, before switch core */
+void __iomem *ai_switch_core(struct si_pub *sih, uint coreid, uint *origidx,
+ uint *intr_val)
+{
+ void __iomem *cc;
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+
+ if (SI_FAST(sii)) {
+ /* Overloading the origidx variable to remember the coreid,
+ * this works because the core ids cannot be confused with
+ * core indices.
+ */
+ *origidx = coreid;
+ if (coreid == CC_CORE_ID)
+ return CCREGS_FAST(sii);
+ else if (coreid == sih->buscoretype)
+ return PCIEREGS(sii);
+ }
+ INTR_OFF(sii, *intr_val);
+ *origidx = sii->curidx;
+ cc = ai_setcore(sih, coreid, 0);
+ return cc;
+}
+
+/* restore coreidx and restore interrupt */
+void ai_restore_core(struct si_pub *sih, uint coreid, uint intr_val)
+{
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+ if (SI_FAST(sii)
+ && ((coreid == CC_CORE_ID) || (coreid == sih->buscoretype)))
+ return;
+
+ ai_setcoreidx(sih, coreid);
+ INTR_RESTORE(sii, intr_val);
+}
+
+void ai_write_wrapperreg(struct si_pub *sih, u32 offset, u32 val)
+{
+ struct si_info *sii = (struct si_info *)sih;
+ u32 *w = (u32 *) sii->curwrap;
+ W_REG(w + (offset / 4), val);
+ return;
+}
+
+/*
+ * Switch to 'coreidx', issue a single arbitrary 32bit register mask&set
+ * operation, switch back to the original core, and return the new value.
+ *
+ * When using the silicon backplane, no fiddling with interrupts or core
+ * switches is needed.
+ *
+ * Also, when using pci/pcie, we can optimize away the core switching for pci
+ * registers and (on newer pci cores) chipcommon registers.
+ */
+uint ai_corereg(struct si_pub *sih, uint coreidx, uint regoff, uint mask,
+ uint val)
+{
+ uint origidx = 0;
+ u32 __iomem *r = NULL;
+ uint w;
+ uint intr_val = 0;
+ bool fast = false;
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+
+ if (coreidx >= SI_MAXCORES)
+ return 0;
+
+ /*
+ * If pci/pcie, we can get at pci/pcie regs
+ * and on newer cores to chipc
+ */
+ if ((sii->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) {
+ /* Chipc registers are mapped at 12KB */
+ fast = true;
+ r = (u32 __iomem *)((__iomem char *)sii->curmap +
+ PCI_16KB0_CCREGS_OFFSET + regoff);
+ } else if (sii->pub.buscoreidx == coreidx) {
+ /*
+ * pci registers are at either in the last 2KB of
+ * an 8KB window or, in pcie and pci rev 13 at 8KB
+ */
+ fast = true;
+ if (SI_FAST(sii))
+ r = (u32 __iomem *)((__iomem char *)sii->curmap +
+ PCI_16KB0_PCIREGS_OFFSET + regoff);
+ else
+ r = (u32 __iomem *)((__iomem char *)sii->curmap +
+ ((regoff >= SBCONFIGOFF) ?
+ PCI_BAR0_PCISBR_OFFSET :
+ PCI_BAR0_PCIREGS_OFFSET) + regoff);
+ }
+
+ if (!fast) {
+ INTR_OFF(sii, intr_val);
+
+ /* save current core index */
+ origidx = ai_coreidx(&sii->pub);
+
+ /* switch core */
+ r = (u32 __iomem *) ((unsigned char __iomem *)
+ ai_setcoreidx(&sii->pub, coreidx) + regoff);
+ }
+
+ /* mask and set */
+ if (mask || val) {
+ w = (R_REG(r) & ~mask) | val;
+ W_REG(r, w);
+ }
+
+ /* readback */
+ w = R_REG(r);
+
+ if (!fast) {
+ /* restore core index */
+ if (origidx != coreidx)
+ ai_setcoreidx(&sii->pub, origidx);
+
+ INTR_RESTORE(sii, intr_val);
+ }
+
+ return w;
+}
+
+void ai_core_disable(struct si_pub *sih, u32 bits)
+{
+ struct si_info *sii;
+ u32 dummy;
+ struct aidmp *ai;
+
+ sii = (struct si_info *)sih;
+
+ ai = sii->curwrap;
+
+ /* if core is already in reset, just return */
+ if (R_REG(&ai->resetctrl) & AIRC_RESET)
+ return;
+
+ W_REG(&ai->ioctrl, bits);
+ dummy = R_REG(&ai->ioctrl);
+ udelay(10);
+
+ W_REG(&ai->resetctrl, AIRC_RESET);
+ udelay(1);
+}
+
+/* reset and re-enable a core
+ * inputs:
+ * bits - core specific bits that are set during and after reset sequence
+ * resetbits - core specific bits that are set only during reset sequence
+ */
+void ai_core_reset(struct si_pub *sih, u32 bits, u32 resetbits)
+{
+ struct si_info *sii;
+ struct aidmp *ai;
+ u32 dummy;
+
+ sii = (struct si_info *)sih;
+ ai = sii->curwrap;
+
+ /*
+ * Must do the disable sequence first to work
+ * for arbitrary current core state.
+ */
+ ai_core_disable(sih, (bits | resetbits));
+
+ /*
+ * Now do the initialization sequence.
+ */
+ W_REG(&ai->ioctrl, (bits | SICF_FGC | SICF_CLOCK_EN));
+ dummy = R_REG(&ai->ioctrl);
+ W_REG(&ai->resetctrl, 0);
+ udelay(1);
+
+ W_REG(&ai->ioctrl, (bits | SICF_CLOCK_EN));
+ dummy = R_REG(&ai->ioctrl);
+ udelay(1);
+}
+
+/* return the slow clock source - LPO, XTAL, or PCI */
+static uint ai_slowclk_src(struct si_info *sii)
+{
+ struct chipcregs __iomem *cc;
+ u32 val;
+
+ if (sii->pub.ccrev < 6) {
+ pci_read_config_dword(sii->pbus, PCI_GPIO_OUT,
+ &val);
+ if (val & PCI_CFG_GPIO_SCS)
+ return SCC_SS_PCI;
+ return SCC_SS_XTAL;
+ } else if (sii->pub.ccrev < 10) {
+ cc = (struct chipcregs __iomem *)
+ ai_setcoreidx(&sii->pub, sii->curidx);
+ return R_REG(&cc->slow_clk_ctl) & SCC_SS_MASK;
+ } else /* Insta-clock */
+ return SCC_SS_XTAL;
+}
+
+/*
+* return the ILP (slowclock) min or max frequency
+* precondition: we've established the chip has dynamic clk control
+*/
+static uint ai_slowclk_freq(struct si_info *sii, bool max_freq,
+ struct chipcregs __iomem *cc)
+{
+ u32 slowclk;
+ uint div;
+
+ slowclk = ai_slowclk_src(sii);
+ if (sii->pub.ccrev < 6) {
+ if (slowclk == SCC_SS_PCI)
+ return max_freq ? (PCIMAXFREQ / 64)
+ : (PCIMINFREQ / 64);
+ else
+ return max_freq ? (XTALMAXFREQ / 32)
+ : (XTALMINFREQ / 32);
+ } else if (sii->pub.ccrev < 10) {
+ div = 4 *
+ (((R_REG(&cc->slow_clk_ctl) & SCC_CD_MASK) >>
+ SCC_CD_SHIFT) + 1);
+ if (slowclk == SCC_SS_LPO)
+ return max_freq ? LPOMAXFREQ : LPOMINFREQ;
+ else if (slowclk == SCC_SS_XTAL)
+ return max_freq ? (XTALMAXFREQ / div)
+ : (XTALMINFREQ / div);
+ else if (slowclk == SCC_SS_PCI)
+ return max_freq ? (PCIMAXFREQ / div)
+ : (PCIMINFREQ / div);
+ } else {
+ /* Chipc rev 10 is InstaClock */
+ div = R_REG(&cc->system_clk_ctl) >> SYCC_CD_SHIFT;
+ div = 4 * (div + 1);
+ return max_freq ? XTALMAXFREQ : (XTALMINFREQ / div);
+ }
+ return 0;
+}
+
+static void
+ai_clkctl_setdelay(struct si_info *sii, struct chipcregs __iomem *cc)
+{
+ uint slowmaxfreq, pll_delay, slowclk;
+ uint pll_on_delay, fref_sel_delay;
+
+ pll_delay = PLL_DELAY;
+
+ /*
+ * If the slow clock is not sourced by the xtal then
+ * add the xtal_on_delay since the xtal will also be
+ * powered down by dynamic clk control logic.
+ */
+
+ slowclk = ai_slowclk_src(sii);
+ if (slowclk != SCC_SS_XTAL)
+ pll_delay += XTAL_ON_DELAY;
+
+ /* Starting with 4318 it is ILP that is used for the delays */
+ slowmaxfreq =
+ ai_slowclk_freq(sii, (sii->pub.ccrev >= 10) ? false : true, cc);
+
+ pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000;
+ fref_sel_delay = ((slowmaxfreq * FREF_DELAY) + 999999) / 1000000;
+
+ W_REG(&cc->pll_on_delay, pll_on_delay);
+ W_REG(&cc->fref_sel_delay, fref_sel_delay);
+}
+
+/* initialize power control delay registers */
+void ai_clkctl_init(struct si_pub *sih)
+{
+ struct si_info *sii;
+ uint origidx = 0;
+ struct chipcregs __iomem *cc;
+ bool fast;
+
+ if (!(sih->cccaps & CC_CAP_PWR_CTL))
+ return;
+
+ sii = (struct si_info *)sih;
+ fast = SI_FAST(sii);
+ if (!fast) {
+ origidx = sii->curidx;
+ cc = (struct chipcregs __iomem *)
+ ai_setcore(sih, CC_CORE_ID, 0);
+ if (cc == NULL)
+ return;
+ } else {
+ cc = (struct chipcregs __iomem *) CCREGS_FAST(sii);
+ if (cc == NULL)
+ return;
+ }
+
+ /* set all Instaclk chip ILP to 1 MHz */
+ if (sih->ccrev >= 10)
+ SET_REG(&cc->system_clk_ctl, SYCC_CD_MASK,
+ (ILP_DIV_1MHZ << SYCC_CD_SHIFT));
+
+ ai_clkctl_setdelay(sii, cc);
+
+ if (!fast)
+ ai_setcoreidx(sih, origidx);
+}
+
+/*
+ * return the value suitable for writing to the
+ * dot11 core FAST_PWRUP_DELAY register
+ */
+u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih)
+{
+ struct si_info *sii;
+ uint origidx = 0;
+ struct chipcregs __iomem *cc;
+ uint slowminfreq;
+ u16 fpdelay;
+ uint intr_val = 0;
+ bool fast;
+
+ sii = (struct si_info *)sih;
+ if (sih->cccaps & CC_CAP_PMU) {
+ INTR_OFF(sii, intr_val);
+ fpdelay = si_pmu_fast_pwrup_delay(sih);
+ INTR_RESTORE(sii, intr_val);
+ return fpdelay;
+ }
+
+ if (!(sih->cccaps & CC_CAP_PWR_CTL))
+ return 0;
+
+ fast = SI_FAST(sii);
+ fpdelay = 0;
+ if (!fast) {
+ origidx = sii->curidx;
+ INTR_OFF(sii, intr_val);
+ cc = (struct chipcregs __iomem *)
+ ai_setcore(sih, CC_CORE_ID, 0);
+ if (cc == NULL)
+ goto done;
+ } else {
+ cc = (struct chipcregs __iomem *) CCREGS_FAST(sii);
+ if (cc == NULL)
+ goto done;
+ }
+
+ slowminfreq = ai_slowclk_freq(sii, false, cc);
+ fpdelay = (((R_REG(&cc->pll_on_delay) + 2) * 1000000) +
+ (slowminfreq - 1)) / slowminfreq;
+
+ done:
+ if (!fast) {
+ ai_setcoreidx(sih, origidx);
+ INTR_RESTORE(sii, intr_val);
+ }
+ return fpdelay;
+}
+
+/* turn primary xtal and/or pll off/on */
+int ai_clkctl_xtal(struct si_pub *sih, uint what, bool on)
+{
+ struct si_info *sii;
+ u32 in, out, outen;
+
+ sii = (struct si_info *)sih;
+
+ /* pcie core doesn't have any mapping to control the xtal pu */
+ if (PCIE(sii))
+ return -1;
+
+ pci_read_config_dword(sii->pbus, PCI_GPIO_IN, &in);
+ pci_read_config_dword(sii->pbus, PCI_GPIO_OUT, &out);
+ pci_read_config_dword(sii->pbus, PCI_GPIO_OUTEN, &outen);
+
+ /*
+ * Avoid glitching the clock if GPRS is already using it.
+ * We can't actually read the state of the PLLPD so we infer it
+ * by the value of XTAL_PU which *is* readable via gpioin.
+ */
+ if (on && (in & PCI_CFG_GPIO_XTAL))
+ return 0;
+
+ if (what & XTAL)
+ outen |= PCI_CFG_GPIO_XTAL;
+ if (what & PLL)
+ outen |= PCI_CFG_GPIO_PLL;
+
+ if (on) {
+ /* turn primary xtal on */
+ if (what & XTAL) {
+ out |= PCI_CFG_GPIO_XTAL;
+ if (what & PLL)
+ out |= PCI_CFG_GPIO_PLL;
+ pci_write_config_dword(sii->pbus,
+ PCI_GPIO_OUT, out);
+ pci_write_config_dword(sii->pbus,
+ PCI_GPIO_OUTEN, outen);
+ udelay(XTAL_ON_DELAY);
+ }
+
+ /* turn pll on */
+ if (what & PLL) {
+ out &= ~PCI_CFG_GPIO_PLL;
+ pci_write_config_dword(sii->pbus,
+ PCI_GPIO_OUT, out);
+ mdelay(2);
+ }
+ } else {
+ if (what & XTAL)
+ out &= ~PCI_CFG_GPIO_XTAL;
+ if (what & PLL)
+ out |= PCI_CFG_GPIO_PLL;
+ pci_write_config_dword(sii->pbus,
+ PCI_GPIO_OUT, out);
+ pci_write_config_dword(sii->pbus,
+ PCI_GPIO_OUTEN, outen);
+ }
+
+ return 0;
+}
+
+/* clk control mechanism through chipcommon, no policy checking */
+static bool _ai_clkctl_cc(struct si_info *sii, uint mode)
+{
+ uint origidx = 0;
+ struct chipcregs __iomem *cc;
+ u32 scc;
+ uint intr_val = 0;
+ bool fast = SI_FAST(sii);
+
+ /* chipcommon cores prior to rev6 don't support dynamic clock control */
+ if (sii->pub.ccrev < 6)
+ return false;
+
+ if (!fast) {
+ INTR_OFF(sii, intr_val);
+ origidx = sii->curidx;
+ cc = (struct chipcregs __iomem *)
+ ai_setcore(&sii->pub, CC_CORE_ID, 0);
+ } else {
+ cc = (struct chipcregs __iomem *) CCREGS_FAST(sii);
+ if (cc == NULL)
+ goto done;
+ }
+
+ if (!(sii->pub.cccaps & CC_CAP_PWR_CTL) && (sii->pub.ccrev < 20))
+ goto done;
+
+ switch (mode) {
+ case CLK_FAST: /* FORCEHT, fast (pll) clock */
+ if (sii->pub.ccrev < 10) {
+ /*
+ * don't forget to force xtal back
+ * on before we clear SCC_DYN_XTAL..
+ */
+ ai_clkctl_xtal(&sii->pub, XTAL, ON);
+ SET_REG(&cc->slow_clk_ctl,
+ (SCC_XC | SCC_FS | SCC_IP), SCC_IP);
+ } else if (sii->pub.ccrev < 20) {
+ OR_REG(&cc->system_clk_ctl, SYCC_HR);
+ } else {
+ OR_REG(&cc->clk_ctl_st, CCS_FORCEHT);
+ }
+
+ /* wait for the PLL */
+ if (sii->pub.cccaps & CC_CAP_PMU) {
+ u32 htavail = CCS_HTAVAIL;
+ SPINWAIT(((R_REG(&cc->clk_ctl_st) & htavail)
+ == 0), PMU_MAX_TRANSITION_DLY);
+ } else {
+ udelay(PLL_DELAY);
+ }
+ break;
+
+ case CLK_DYNAMIC: /* enable dynamic clock control */
+ if (sii->pub.ccrev < 10) {
+ scc = R_REG(&cc->slow_clk_ctl);
+ scc &= ~(SCC_FS | SCC_IP | SCC_XC);
+ if ((scc & SCC_SS_MASK) != SCC_SS_XTAL)
+ scc |= SCC_XC;
+ W_REG(&cc->slow_clk_ctl, scc);
+
+ /*
+ * for dynamic control, we have to
+ * release our xtal_pu "force on"
+ */
+ if (scc & SCC_XC)
+ ai_clkctl_xtal(&sii->pub, XTAL, OFF);
+ } else if (sii->pub.ccrev < 20) {
+ /* Instaclock */
+ AND_REG(&cc->system_clk_ctl, ~SYCC_HR);
+ } else {
+ AND_REG(&cc->clk_ctl_st, ~CCS_FORCEHT);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ done:
+ if (!fast) {
+ ai_setcoreidx(&sii->pub, origidx);
+ INTR_RESTORE(sii, intr_val);
+ }
+ return mode == CLK_FAST;
+}
+
+/*
+ * clock control policy function throught chipcommon
+ *
+ * set dynamic clk control mode (forceslow, forcefast, dynamic)
+ * returns true if we are forcing fast clock
+ * this is a wrapper over the next internal function
+ * to allow flexible policy settings for outside caller
+ */
+bool ai_clkctl_cc(struct si_pub *sih, uint mode)
+{
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+
+ /* chipcommon cores prior to rev6 don't support dynamic clock control */
+ if (sih->ccrev < 6)
+ return false;
+
+ if (PCI_FORCEHT(sii))
+ return mode == CLK_FAST;
+
+ return _ai_clkctl_cc(sii, mode);
+}
+
+/* Build device path */
+int ai_devpath(struct si_pub *sih, char *path, int size)
+{
+ int slen;
+
+ if (!path || size <= 0)
+ return -1;
+
+ slen = snprintf(path, (size_t) size, "pci/%u/%u/",
+ ((struct si_info *)sih)->pbus->bus->number,
+ PCI_SLOT(((struct pci_dev *)
+ (((struct si_info *)(sih))->pbus))->devfn));
+
+ if (slen < 0 || slen >= size) {
+ path[0] = '\0';
+ return -1;
+ }
+
+ return 0;
+}
+
+void ai_pci_up(struct si_pub *sih)
+{
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+
+ if (PCI_FORCEHT(sii))
+ _ai_clkctl_cc(sii, CLK_FAST);
+
+ if (PCIE(sii))
+ pcicore_up(sii->pch, SI_PCIUP);
+
+}
+
+/* Unconfigure and/or apply various WARs when system is going to sleep mode */
+void ai_pci_sleep(struct si_pub *sih)
+{
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+
+ pcicore_sleep(sii->pch);
+}
+
+/* Unconfigure and/or apply various WARs when going down */
+void ai_pci_down(struct si_pub *sih)
+{
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+
+ /* release FORCEHT since chip is going to "down" state */
+ if (PCI_FORCEHT(sii))
+ _ai_clkctl_cc(sii, CLK_DYNAMIC);
+
+ pcicore_down(sii->pch, SI_PCIDOWN);
+}
+
+/*
+ * Configure the pci core for pci client (NIC) action
+ * coremask is the bitvec of cores by index to be enabled.
+ */
+void ai_pci_setup(struct si_pub *sih, uint coremask)
+{
+ struct si_info *sii;
+ struct sbpciregs __iomem *regs = NULL;
+ u32 siflag = 0, w;
+ uint idx = 0;
+
+ sii = (struct si_info *)sih;
+
+ if (PCI(sii)) {
+ /* get current core index */
+ idx = sii->curidx;
+
+ /* we interrupt on this backplane flag number */
+ siflag = ai_flag(sih);
+
+ /* switch over to pci core */
+ regs = ai_setcoreidx(sih, sii->pub.buscoreidx);
+ }
+
+ /*
+ * Enable sb->pci interrupts. Assume
+ * PCI rev 2.3 support was added in pci core rev 6 and things changed..
+ */
+ if (PCIE(sii) || (PCI(sii) && ((sii->pub.buscorerev) >= 6))) {
+ /* pci config write to set this core bit in PCIIntMask */
+ pci_read_config_dword(sii->pbus, PCI_INT_MASK, &w);
+ w |= (coremask << PCI_SBIM_SHIFT);
+ pci_write_config_dword(sii->pbus, PCI_INT_MASK, w);
+ } else {
+ /* set sbintvec bit for our flag number */
+ ai_setint(sih, siflag);
+ }
+
+ if (PCI(sii)) {
+ pcicore_pci_setup(sii->pch, regs);
+
+ /* switch back to previous core */
+ ai_setcoreidx(sih, idx);
+ }
+}
+
+/*
+ * Fixup SROMless PCI device's configuration.
+ * The current core may be changed upon return.
+ */
+int ai_pci_fixcfg(struct si_pub *sih)
+{
+ uint origidx;
+ void __iomem *regs = NULL;
+ struct si_info *sii = (struct si_info *)sih;
+
+ /* Fixup PI in SROM shadow area to enable the correct PCI core access */
+ /* save the current index */
+ origidx = ai_coreidx(&sii->pub);
+
+ /* check 'pi' is correct and fix it if not */
+ regs = ai_setcore(&sii->pub, sii->pub.buscoretype, 0);
+ if (sii->pub.buscoretype == PCIE_CORE_ID)
+ pcicore_fixcfg_pcie(sii->pch,
+ (struct sbpcieregs __iomem *)regs);
+ else if (sii->pub.buscoretype == PCI_CORE_ID)
+ pcicore_fixcfg_pci(sii->pch, (struct sbpciregs __iomem *)regs);
+
+ /* restore the original index */
+ ai_setcoreidx(&sii->pub, origidx);
+
+ pcicore_hwup(sii->pch);
+ return 0;
+}
+
+/* mask&set gpiocontrol bits */
+u32 ai_gpiocontrol(struct si_pub *sih, u32 mask, u32 val, u8 priority)
+{
+ uint regoff;
+
+ regoff = offsetof(struct chipcregs, gpiocontrol);
+ return ai_corereg(sih, SI_CC_IDX, regoff, mask, val);
+}
+
+void ai_chipcontrl_epa4331(struct si_pub *sih, bool on)
+{
+ struct si_info *sii;
+ struct chipcregs __iomem *cc;
+ uint origidx;
+ u32 val;
+
+ sii = (struct si_info *)sih;
+ origidx = ai_coreidx(sih);
+
+ cc = (struct chipcregs __iomem *) ai_setcore(sih, CC_CORE_ID, 0);
+
+ val = R_REG(&cc->chipcontrol);
+
+ if (on) {
+ if (sih->chippkg == 9 || sih->chippkg == 0xb)
+ /* Ext PA Controls for 4331 12x9 Package */
+ W_REG(&cc->chipcontrol, val |
+ CCTRL4331_EXTPA_EN |
+ CCTRL4331_EXTPA_ON_GPIO2_5);
+ else
+ /* Ext PA Controls for 4331 12x12 Package */
+ W_REG(&cc->chipcontrol,
+ val | CCTRL4331_EXTPA_EN);
+ } else {
+ val &= ~(CCTRL4331_EXTPA_EN | CCTRL4331_EXTPA_ON_GPIO2_5);
+ W_REG(&cc->chipcontrol, val);
+ }
+
+ ai_setcoreidx(sih, origidx);
+}
+
+/* Enable BT-COEX & Ex-PA for 4313 */
+void ai_epa_4313war(struct si_pub *sih)
+{
+ struct si_info *sii;
+ struct chipcregs __iomem *cc;
+ uint origidx;
+
+ sii = (struct si_info *)sih;
+ origidx = ai_coreidx(sih);
+
+ cc = ai_setcore(sih, CC_CORE_ID, 0);
+
+ /* EPA Fix */
+ W_REG(&cc->gpiocontrol,
+ R_REG(&cc->gpiocontrol) | GPIO_CTRL_EPA_EN_MASK);
+
+ ai_setcoreidx(sih, origidx);
+}
+
+/* check if the device is removed */
+bool ai_deviceremoved(struct si_pub *sih)
+{
+ u32 w;
+ struct si_info *sii;
+
+ sii = (struct si_info *)sih;
+
+ pci_read_config_dword(sii->pbus, PCI_VENDOR_ID, &w);
+ if ((w & 0xFFFF) != PCI_VENDOR_ID_BROADCOM)
+ return true;
+
+ return false;
+}
+
+bool ai_is_sprom_available(struct si_pub *sih)
+{
+ if (sih->ccrev >= 31) {
+ struct si_info *sii;
+ uint origidx;
+ struct chipcregs __iomem *cc;
+ u32 sromctrl;
+
+ if ((sih->cccaps & CC_CAP_SROM) == 0)
+ return false;
+
+ sii = (struct si_info *)sih;
+ origidx = sii->curidx;
+ cc = ai_setcoreidx(sih, SI_CC_IDX);
+ sromctrl = R_REG(&cc->sromcontrol);
+ ai_setcoreidx(sih, origidx);
+ return sromctrl & SRC_PRESENT;
+ }
+
+ switch (sih->chip) {
+ case BCM4313_CHIP_ID:
+ return (sih->chipst & CST4313_SPROM_PRESENT) != 0;
+ default:
+ return true;
+ }
+}
+
+bool ai_is_otp_disabled(struct si_pub *sih)
+{
+ switch (sih->chip) {
+ case BCM4313_CHIP_ID:
+ return (sih->chipst & CST4313_OTP_PRESENT) == 0;
+ /* These chips always have their OTP on */
+ case BCM43224_CHIP_ID:
+ case BCM43225_CHIP_ID:
+ default:
+ return false;
+ }
+}
diff --git a/drivers/staging/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
index b98099eaa621..106a7424a7cd 100644
--- a/drivers/staging/brcm80211/brcmsmac/aiutils.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
@@ -14,43 +14,15 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _aiutils_h_
-#define _aiutils_h_
+#ifndef _BRCM_AIUTILS_H_
+#define _BRCM_AIUTILS_H_
-/* cpp contortions to concatenate w/arg prescan */
-#ifndef PAD
-#define _PADLINE(line) pad ## line
-#define _XSTR(line) _PADLINE(line)
-#define PAD _XSTR(__LINE__)
-#endif
-
-/* Include the soci specific files */
-#include <aidmp.h>
+#include "types.h"
/*
* SOC Interconnect Address Map.
* All regions may not exist on all chips.
*/
-/* Physical SDRAM */
-#define SI_SDRAM_BASE 0x00000000
-/* Host Mode sb2pcitranslation0 (64 MB) */
-#define SI_PCI_MEM 0x08000000
-#define SI_PCI_MEM_SZ (64 * 1024 * 1024)
-/* Host Mode sb2pcitranslation1 (64 MB) */
-#define SI_PCI_CFG 0x0c000000
-/* Byteswapped Physical SDRAM */
-#define SI_SDRAM_SWAPPED 0x10000000
-/* Region 2 for sdram (512 MB) */
-#define SI_SDRAM_R2 0x80000000
-
-#ifdef SI_ENUM_BASE_VARIABLE
-#define SI_ENUM_BASE (sii->pub.si_enum_base)
-#else
-#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */
-#endif /* SI_ENUM_BASE_VARIABLE */
-
-/* Wrapper space base */
-#define SI_WRAP_BASE 0x18100000
/* each core gets 4Kbytes for registers */
#define SI_CORE_SIZE 0x1000
/*
@@ -60,39 +32,9 @@
*/
#define SI_MAXCORES 16
-/* On-chip RAM on chips that also have DDR */
-#define SI_FASTRAM 0x19000000
-#define SI_FASTRAM_SWAPPED 0x19800000
-
-/* Flash Region 2 (region 1 shadowed here) */
-#define SI_FLASH2 0x1c000000
-/* Size of Flash Region 2 */
-#define SI_FLASH2_SZ 0x02000000
-/* ARM Cortex-M3 ROM */
-#define SI_ARMCM3_ROM 0x1e000000
-/* MIPS Flash Region 1 */
-#define SI_FLASH1 0x1fc00000
-/* MIPS Size of Flash Region 1 */
-#define SI_FLASH1_SZ 0x00400000
-/* ARM7TDMI-S ROM */
-#define SI_ARM7S_ROM 0x20000000
-/* ARM Cortex-M3 SRAM Region 2 */
-#define SI_ARMCM3_SRAM2 0x60000000
-/* ARM7TDMI-S SRAM Region 2 */
-#define SI_ARM7S_SRAM2 0x80000000
-/* ARM Flash Region 1 */
-#define SI_ARM_FLASH1 0xffff0000
-/* ARM Size of Flash Region 1 */
-#define SI_ARM_FLASH1_SZ 0x00010000
-
-/* Client Mode sb2pcitranslation2 (1 GB) */
-#define SI_PCI_DMA 0x40000000
-/* Client Mode sb2pcitranslation2 (1 GB) */
-#define SI_PCI_DMA2 0x80000000
/* Client Mode sb2pcitranslation2 size in bytes */
#define SI_PCI_DMA_SZ 0x40000000
-/* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), low 32 bits */
-#define SI_PCIE_DMA_L32 0x00000000
+
/* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */
#define SI_PCIE_DMA_H32 0x80000000
@@ -158,9 +100,7 @@
* maps all unused address ranges
*/
-/* There are TWO constants on all HND chips: SI_ENUM_BASE above,
- * and chipcommon being the first core:
- */
+/* chipcommon being the first core: */
#define SI_CC_IDX 0
/* SOC Interconnect types (aka chip types) */
@@ -213,51 +153,24 @@
/* Minumum amount of flash we support */
#define FLASH_MIN 0x00020000 /* Minimum flash size */
-/* A boot/binary may have an embedded block that describes its size */
-#define BISZ_OFFSET 0x3e0 /* At this offset into the binary */
-#define BISZ_MAGIC 0x4249535a /* Marked with value: 'BISZ' */
-#define BISZ_MAGIC_IDX 0 /* Word 0: magic */
-#define BISZ_TXTST_IDX 1 /* 1: text start */
-#define BISZ_TXTEND_IDX 2 /* 2: text end */
-#define BISZ_DATAST_IDX 3 /* 3: data start */
-#define BISZ_DATAEND_IDX 4 /* 4: data end */
-#define BISZ_BSSST_IDX 5 /* 5: bss start */
-#define BISZ_BSSEND_IDX 6 /* 6: bss end */
-#define BISZ_SIZE 7 /* descriptor size in 32-bit integers */
-
-#define SI_INFO(sih) (si_info_t *)sih
-
-#define GOODCOREADDR(x, b) \
- (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \
- IS_ALIGNED((x), SI_CORE_SIZE))
-#define GOODREGS(regs) \
- ((regs) != NULL && IS_ALIGNED((unsigned long)(regs), SI_CORE_SIZE))
-#define BADCOREADDR 0
-#define GOODIDX(idx) (((uint)idx) < SI_MAXCORES)
-#define NOREV -1 /* Invalid rev */
-
-/* Newer chips can access PCI/PCIE and CC core without requiring to change
- * PCI BAR0 WIN
- */
-#define SI_FAST(si) (((si)->pub.buscoretype == PCIE_CORE_ID) || \
- (((si)->pub.buscoretype == PCI_CORE_ID) && \
- (si)->pub.buscorerev >= 13))
+#define CC_SROM_OTP 0x800 /* SROM/OTP address space */
-#define PCIEREGS(si) (((char *)((si)->curmap) + PCI_16KB0_PCIREGS_OFFSET))
-#define CCREGS_FAST(si) (((char *)((si)->curmap) + PCI_16KB0_CCREGS_OFFSET))
+/* gpiotimerval */
+#define GPIO_ONTIME_SHIFT 16
-/*
- * Macros to disable/restore function core(D11, ENET, ILINE20, etc) interrupts
- * before after core switching to avoid invalid register accesss inside ISR.
- */
-#define INTR_OFF(si, intr_val) \
- if ((si)->intrsoff_fn && \
- (si)->coreid[(si)->curidx] == (si)->dev_coreid) \
- intr_val = (*(si)->intrsoff_fn)((si)->intr_arg)
-#define INTR_RESTORE(si, intr_val) \
- if ((si)->intrsrestore_fn && \
- (si)->coreid[(si)->curidx] == (si)->dev_coreid) \
- (*(si)->intrsrestore_fn)((si)->intr_arg, intr_val)
+/* Fields in clkdiv */
+#define CLKD_OTP 0x000f0000
+#define CLKD_OTP_SHIFT 16
+
+/* Package IDs */
+#define BCM4717_PKG_ID 9 /* 4717 package id */
+#define BCM4718_PKG_ID 10 /* 4718 package id */
+#define BCM43224_FAB_SMIC 0xa /* the chip is manufactured by SMIC */
+
+/* these are router chips */
+#define BCM4716_CHIP_ID 0x4716 /* 4716 chipcommon chipid */
+#define BCM47162_CHIP_ID 47162 /* 47162 chipcommon chipid */
+#define BCM4748_CHIP_ID 0x4748 /* 4716 chipcommon chipid (OTP, RBBU) */
/* dynamic clock control defines */
#define LPOMINFREQ 25000 /* low power oscillator min */
@@ -270,67 +183,6 @@
#define ILP_DIV_5MHZ 0 /* ILP = 5 MHz */
#define ILP_DIV_1MHZ 4 /* ILP = 1 MHz */
-#define PCI(si) (((si)->pub.bustype == PCI_BUS) && \
- ((si)->pub.buscoretype == PCI_CORE_ID))
-#define PCIE(si) (((si)->pub.bustype == PCI_BUS) && \
- ((si)->pub.buscoretype == PCIE_CORE_ID))
-#define PCI_FORCEHT(si) \
- (PCIE(si) && (si->pub.chip == BCM4716_CHIP_ID))
-
-/* GPIO Based LED powersave defines */
-#define DEFAULT_GPIO_ONTIME 10 /* Default: 10% on */
-#define DEFAULT_GPIO_OFFTIME 90 /* Default: 10% on */
-
-#ifndef DEFAULT_GPIOTIMERVAL
-#define DEFAULT_GPIOTIMERVAL \
- ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME)
-#endif
-
-/*
- * Data structure to export all chip specific common variables
- * public (read-only) portion of aiutils handle returned by si_attach()
- */
-struct si_pub {
- uint bustype; /* SI_BUS, PCI_BUS */
- uint buscoretype; /* PCI_CORE_ID, PCIE_CORE_ID, PCMCIA_CORE_ID */
- uint buscorerev; /* buscore rev */
- uint buscoreidx; /* buscore index */
- int ccrev; /* chip common core rev */
- u32 cccaps; /* chip common capabilities */
- u32 cccaps_ext; /* chip common capabilities extension */
- int pmurev; /* pmu core rev */
- u32 pmucaps; /* pmu capabilities */
- uint boardtype; /* board type */
- uint boardvendor; /* board vendor */
- uint boardflags; /* board flags */
- uint boardflags2; /* board flags2 */
- uint chip; /* chip number */
- uint chiprev; /* chip revision */
- uint chippkg; /* chip package option */
- u32 chipst; /* chip status */
- bool issim; /* chip is in simulation or emulation */
- uint socirev; /* SOC interconnect rev */
- bool pci_pr32414;
-
-};
-
-/*
- * for HIGH_ONLY driver, the si_t must be writable to allow states sync from
- * BMAC to HIGH driver for monolithic driver, it is readonly to prevent accident
- * change
- */
-typedef const struct si_pub si_t;
-
-/*
- * Many of the routines below take an 'sih' handle as their first arg.
- * Allocate this by calling si_attach(). Free it by calling si_detach().
- * At any one time, the sih is logically focused on one particular si core
- * (the "current core").
- * Use si_setcore() or si_setcoreidx() to change the association to another core
- */
-
-#define BADIDX (SI_MAXCORES + 1)
-
/* clkctl xtal what flags */
#define XTAL 0x1 /* primary crystal oscillator (2050) */
#define PLL 0x2 /* main chip pll */
@@ -363,76 +215,61 @@ typedef const struct si_pub si_t;
#define SI_PCIDOWN 2
#define SI_PCIUP 3
-#define ISSIM_ENAB(sih) 0
-
-/* PMU clock/power control */
-#if defined(BCMPMUCTL)
-#define PMUCTL_ENAB(sih) (BCMPMUCTL)
-#else
-#define PMUCTL_ENAB(sih) ((sih)->cccaps & CC_CAP_PMU)
-#endif
-
-/* chipcommon clock/power control (exclusive with PMU's) */
-#if defined(BCMPMUCTL) && BCMPMUCTL
-#define CCCTL_ENAB(sih) (0)
-#define CCPLL_ENAB(sih) (0)
-#else
-#define CCCTL_ENAB(sih) ((sih)->cccaps & CC_CAP_PWR_CTL)
-#define CCPLL_ENAB(sih) ((sih)->cccaps & CC_CAP_PLL_MASK)
-#endif
-
-typedef void (*gpio_handler_t) (u32 stat, void *arg);
-
-/* External PA enable mask */
-#define GPIO_CTRL_EPA_EN_MASK 0x40
-
-#define SI_ERROR(args)
-
-#ifdef BCMDBG
-#define SI_MSG(args) printk args
-#else
-#define SI_MSG(args)
-#endif /* BCMDBG */
-
-/* Define SI_VMSG to printf for verbose debugging, but don't check it in */
-#define SI_VMSG(args)
+/*
+ * Data structure to export all chip specific common variables
+ * public (read-only) portion of aiutils handle returned by si_attach()
+ */
+struct si_pub {
+ uint buscoretype; /* PCI_CORE_ID, PCIE_CORE_ID, PCMCIA_CORE_ID */
+ uint buscorerev; /* buscore rev */
+ uint buscoreidx; /* buscore index */
+ int ccrev; /* chip common core rev */
+ u32 cccaps; /* chip common capabilities */
+ u32 cccaps_ext; /* chip common capabilities extension */
+ int pmurev; /* pmu core rev */
+ u32 pmucaps; /* pmu capabilities */
+ uint boardtype; /* board type */
+ uint boardvendor; /* board vendor */
+ uint boardflags; /* board flags */
+ uint boardflags2; /* board flags2 */
+ uint chip; /* chip number */
+ uint chiprev; /* chip revision */
+ uint chippkg; /* chip package option */
+ u32 chipst; /* chip status */
+ bool issim; /* chip is in simulation or emulation */
+ uint socirev; /* SOC interconnect rev */
+ bool pci_pr32414;
-#define IS_SIM(chippkg) \
- ((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID))
+};
-typedef u32(*si_intrsoff_t) (void *intr_arg);
-typedef void (*si_intrsrestore_t) (void *intr_arg, u32 arg);
-typedef bool(*si_intrsenabled_t) (void *intr_arg);
+struct pci_dev;
-typedef struct gpioh_item {
+struct gpioh_item {
void *arg;
bool level;
- gpio_handler_t handler;
+ void (*handler) (u32 stat, void *arg);
u32 event;
struct gpioh_item *next;
-} gpioh_item_t;
+};
/* misc si info needed by some of the routines */
-typedef struct si_info {
+struct si_info {
struct si_pub pub; /* back plane public state (must be first) */
- void *pbus; /* handle to bus (pci/sdio/..) */
+ struct pci_dev *pbus; /* handle to pci bus */
uint dev_coreid; /* the core provides driver functions */
void *intr_arg; /* interrupt callback function arg */
- si_intrsoff_t intrsoff_fn; /* turns chip interrupts off */
- si_intrsrestore_t intrsrestore_fn; /* restore chip interrupts */
- si_intrsenabled_t intrsenabled_fn; /* check if interrupts are enabled */
+ u32 (*intrsoff_fn) (void *intr_arg); /* turns chip interrupts off */
+ /* restore chip interrupts */
+ void (*intrsrestore_fn) (void *intr_arg, u32 arg);
+ /* check if interrupts are enabled */
+ bool (*intrsenabled_fn) (void *intr_arg);
- void *pch; /* PCI/E core handle */
+ struct pcicore_info *pch; /* PCI/E core handle */
- gpioh_item_t *gpioh_head; /* GPIO event handlers list */
+ struct list_head var_list; /* list of srom variables */
- bool memseg; /* flag to toggle MEM_SEG register */
-
- char *vars;
- uint varsz;
-
- void *curmap; /* current regs va */
- void *regs[SI_MAXCORES]; /* other regs va */
+ void __iomem *curmap; /* current regs va */
+ void __iomem *regs[SI_MAXCORES]; /* other regs va */
uint curidx; /* current core index */
uint numcores; /* # discovered cores */
@@ -450,97 +287,92 @@ typedef struct si_info {
u32 cia[SI_MAXCORES]; /* erom cia entry for each core */
u32 cib[SI_MAXCORES]; /* erom cia entry for each core */
u32 oob_router; /* oob router registers for axi */
-} si_info_t;
+};
+
+/*
+ * Many of the routines below take an 'sih' handle as their first arg.
+ * Allocate this by calling si_attach(). Free it by calling si_detach().
+ * At any one time, the sih is logically focused on one particular si core
+ * (the "current core").
+ * Use si_setcore() or si_setcoreidx() to change the association to another core
+ */
+
/* AMBA Interconnect exported externs */
-extern void ai_scan(si_t *sih, void *regs, uint devid);
-
-extern uint ai_flag(si_t *sih);
-extern void ai_setint(si_t *sih, int siflag);
-extern uint ai_coreidx(si_t *sih);
-extern uint ai_corevendor(si_t *sih);
-extern uint ai_corerev(si_t *sih);
-extern bool ai_iscoreup(si_t *sih);
-extern void *ai_setcoreidx(si_t *sih, uint coreidx);
-extern u32 ai_core_cflags(si_t *sih, u32 mask, u32 val);
-extern void ai_core_cflags_wo(si_t *sih, u32 mask, u32 val);
-extern u32 ai_core_sflags(si_t *sih, u32 mask, u32 val);
-extern uint ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask,
+extern uint ai_flag(struct si_pub *sih);
+extern void ai_setint(struct si_pub *sih, int siflag);
+extern uint ai_coreidx(struct si_pub *sih);
+extern uint ai_corevendor(struct si_pub *sih);
+extern uint ai_corerev(struct si_pub *sih);
+extern bool ai_iscoreup(struct si_pub *sih);
+extern u32 ai_core_cflags(struct si_pub *sih, u32 mask, u32 val);
+extern void ai_core_cflags_wo(struct si_pub *sih, u32 mask, u32 val);
+extern u32 ai_core_sflags(struct si_pub *sih, u32 mask, u32 val);
+extern uint ai_corereg(struct si_pub *sih, uint coreidx, uint regoff, uint mask,
uint val);
-extern void ai_core_reset(si_t *sih, u32 bits, u32 resetbits);
-extern void ai_core_disable(si_t *sih, u32 bits);
-extern int ai_numaddrspaces(si_t *sih);
-extern u32 ai_addrspace(si_t *sih, uint asidx);
-extern u32 ai_addrspacesize(si_t *sih, uint asidx);
-extern void ai_write_wrap_reg(si_t *sih, u32 offset, u32 val);
+extern void ai_core_reset(struct si_pub *sih, u32 bits, u32 resetbits);
+extern void ai_core_disable(struct si_pub *sih, u32 bits);
+extern int ai_numaddrspaces(struct si_pub *sih);
+extern u32 ai_addrspace(struct si_pub *sih, uint asidx);
+extern u32 ai_addrspacesize(struct si_pub *sih, uint asidx);
+extern void ai_write_wrap_reg(struct si_pub *sih, u32 offset, u32 val);
/* === exported functions === */
-extern si_t *ai_attach(uint pcidev, void *regs, uint bustype,
- void *sdh, char **vars, uint *varsz);
-
-extern void ai_detach(si_t *sih);
-extern bool ai_pci_war16165(si_t *sih);
-
-extern uint ai_coreid(si_t *sih);
-extern uint ai_corerev(si_t *sih);
-extern uint ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask,
+extern struct si_pub *ai_attach(void __iomem *regs, struct pci_dev *sdh);
+extern void ai_detach(struct si_pub *sih);
+extern uint ai_coreid(struct si_pub *sih);
+extern uint ai_corerev(struct si_pub *sih);
+extern uint ai_corereg(struct si_pub *sih, uint coreidx, uint regoff, uint mask,
uint val);
-extern void ai_write_wrapperreg(si_t *sih, u32 offset, u32 val);
-extern u32 ai_core_cflags(si_t *sih, u32 mask, u32 val);
-extern u32 ai_core_sflags(si_t *sih, u32 mask, u32 val);
-extern bool ai_iscoreup(si_t *sih);
-extern uint ai_findcoreidx(si_t *sih, uint coreid, uint coreunit);
-extern void *ai_setcoreidx(si_t *sih, uint coreidx);
-extern void *ai_setcore(si_t *sih, uint coreid, uint coreunit);
-extern void *ai_switch_core(si_t *sih, uint coreid, uint *origidx,
- uint *intr_val);
-extern void ai_restore_core(si_t *sih, uint coreid, uint intr_val);
-extern void ai_core_reset(si_t *sih, u32 bits, u32 resetbits);
-extern void ai_core_disable(si_t *sih, u32 bits);
-extern u32 ai_alp_clock(si_t *sih);
-extern u32 ai_ilp_clock(si_t *sih);
-extern void ai_pci_setup(si_t *sih, uint coremask);
-extern void ai_setint(si_t *sih, int siflag);
-extern bool ai_backplane64(si_t *sih);
-extern void ai_register_intr_callback(si_t *sih, void *intrsoff_fn,
+extern void ai_write_wrapperreg(struct si_pub *sih, u32 offset, u32 val);
+extern u32 ai_core_cflags(struct si_pub *sih, u32 mask, u32 val);
+extern u32 ai_core_sflags(struct si_pub *sih, u32 mask, u32 val);
+extern bool ai_iscoreup(struct si_pub *sih);
+extern uint ai_findcoreidx(struct si_pub *sih, uint coreid, uint coreunit);
+extern void __iomem *ai_setcoreidx(struct si_pub *sih, uint coreidx);
+extern void __iomem *ai_setcore(struct si_pub *sih, uint coreid, uint coreunit);
+extern void __iomem *ai_switch_core(struct si_pub *sih, uint coreid,
+ uint *origidx, uint *intr_val);
+extern void ai_restore_core(struct si_pub *sih, uint coreid, uint intr_val);
+extern void ai_core_reset(struct si_pub *sih, u32 bits, u32 resetbits);
+extern void ai_core_disable(struct si_pub *sih, u32 bits);
+extern u32 ai_alp_clock(struct si_pub *sih);
+extern u32 ai_ilp_clock(struct si_pub *sih);
+extern void ai_pci_setup(struct si_pub *sih, uint coremask);
+extern void ai_setint(struct si_pub *sih, int siflag);
+extern bool ai_backplane64(struct si_pub *sih);
+extern void ai_register_intr_callback(struct si_pub *sih, void *intrsoff_fn,
void *intrsrestore_fn,
void *intrsenabled_fn, void *intr_arg);
-extern void ai_deregister_intr_callback(si_t *sih);
-extern void ai_clkctl_init(si_t *sih);
-extern u16 ai_clkctl_fast_pwrup_delay(si_t *sih);
-extern bool ai_clkctl_cc(si_t *sih, uint mode);
-extern int ai_clkctl_xtal(si_t *sih, uint what, bool on);
-extern bool ai_deviceremoved(si_t *sih);
-extern u32 ai_gpiocontrol(si_t *sih, u32 mask, u32 val,
+extern void ai_deregister_intr_callback(struct si_pub *sih);
+extern void ai_clkctl_init(struct si_pub *sih);
+extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih);
+extern bool ai_clkctl_cc(struct si_pub *sih, uint mode);
+extern int ai_clkctl_xtal(struct si_pub *sih, uint what, bool on);
+extern bool ai_deviceremoved(struct si_pub *sih);
+extern u32 ai_gpiocontrol(struct si_pub *sih, u32 mask, u32 val,
u8 priority);
/* OTP status */
-extern bool ai_is_otp_disabled(si_t *sih);
-extern bool ai_is_otp_powered(si_t *sih);
-extern void ai_otp_power(si_t *sih, bool on);
+extern bool ai_is_otp_disabled(struct si_pub *sih);
/* SPROM availability */
-extern bool ai_is_sprom_available(si_t *sih);
+extern bool ai_is_sprom_available(struct si_pub *sih);
/*
* Build device path. Path size must be >= SI_DEVPATH_BUFSZ.
* The returned path is NULL terminated and has trailing '/'.
* Return 0 on success, nonzero otherwise.
*/
-extern int ai_devpath(si_t *sih, char *path, int size);
-/* Read variable with prepending the devpath to the name */
-extern char *ai_getdevpathvar(si_t *sih, const char *name);
-extern int ai_getdevpathintvar(si_t *sih, const char *name);
+extern int ai_devpath(struct si_pub *sih, char *path, int size);
-extern void ai_pci_sleep(si_t *sih);
-extern void ai_pci_down(si_t *sih);
-extern void ai_pci_up(si_t *sih);
-extern int ai_pci_fixcfg(si_t *sih);
+extern void ai_pci_sleep(struct si_pub *sih);
+extern void ai_pci_down(struct si_pub *sih);
+extern void ai_pci_up(struct si_pub *sih);
+extern int ai_pci_fixcfg(struct si_pub *sih);
-extern void ai_chipcontrl_epa4331(si_t *sih, bool on);
+extern void ai_chipcontrl_epa4331(struct si_pub *sih, bool on);
/* Enable Ex-PA for 4313 */
-extern void ai_epa_4313war(si_t *sih);
-
-char *ai_getnvramflvar(si_t *sih, const char *name);
+extern void ai_epa_4313war(struct si_pub *sih);
-#endif /* _aiutils_h_ */
+#endif /* _BRCM_AIUTILS_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/wlc_ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
index 85ad70096056..7f27dbdb6b60 100644
--- a/drivers/staging/brcm80211/brcmsmac/wlc_ampdu.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
@@ -13,44 +13,41 @@
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/kernel.h>
#include <net/mac80211.h>
-#include <bcmdefs.h>
-#include <bcmutils.h>
-#include <aiutils.h>
-#include <wlioctl.h>
-#include <sbhnddma.h>
-#include <hnddma.h>
-#include <d11.h>
-
-#include "wlc_types.h"
-#include "wlc_cfg.h"
-#include "wlc_rate.h"
-#include "wlc_scb.h"
-#include "wlc_pub.h"
-#include "wlc_key.h"
-#include "phy/wlc_phy_hal.h"
-#include "wlc_antsel.h"
-#include "wl_export.h"
-#include "wl_dbg.h"
-#include "wlc_channel.h"
-#include "wlc_main.h"
-#include "wlc_ampdu.h"
-
-#define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu */
-#define AMPDU_NUM_MPDU_LEGACY 16 /* max number of mpdus in an ampdu to a legacy */
-#define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
-#define AMPDU_TX_BA_DEF_WSIZE 64 /* default Tx ba window size (in pdu) */
-#define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */
-#define AMPDU_RX_BA_MAX_WSIZE 64 /* default Rx ba window size (in pdu) */
-#define AMPDU_MAX_DUR 5 /* max dur of tx ampdu (in msec) */
-#define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit */
-#define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default tx retry limit at reg rate */
-#define AMPDU_DEF_TXPKT_WEIGHT 2 /* default weight of ampdu in txfifo */
-#define AMPDU_DEF_FFPLD_RSVD 2048 /* default ffpld reserved bytes */
-#define AMPDU_INI_FREE 10 /* # of inis to be freed on detach */
-#define AMPDU_SCB_MAX_RELEASE 20 /* max # of mpdus released at a time */
+#include "rate.h"
+#include "scb.h"
+#include "phy/phy_hal.h"
+#include "antsel.h"
+#include "main.h"
+#include "ampdu.h"
+
+/* max number of mpdus in an ampdu */
+#define AMPDU_MAX_MPDU 32
+/* max number of mpdus in an ampdu to a legacy */
+#define AMPDU_NUM_MPDU_LEGACY 16
+/* max Tx ba window size (in pdu) */
+#define AMPDU_TX_BA_MAX_WSIZE 64
+/* default Tx ba window size (in pdu) */
+#define AMPDU_TX_BA_DEF_WSIZE 64
+/* default Rx ba window size (in pdu) */
+#define AMPDU_RX_BA_DEF_WSIZE 64
+/* max Rx ba window size (in pdu) */
+#define AMPDU_RX_BA_MAX_WSIZE 64
+/* max dur of tx ampdu (in msec) */
+#define AMPDU_MAX_DUR 5
+/* default tx retry limit */
+#define AMPDU_DEF_RETRY_LIMIT 5
+/* default tx retry limit at reg rate */
+#define AMPDU_DEF_RR_RETRY_LIMIT 2
+/* default weight of ampdu in txfifo */
+#define AMPDU_DEF_TXPKT_WEIGHT 2
+/* default ffpld reserved bytes */
+#define AMPDU_DEF_FFPLD_RSVD 2048
+/* # of inis to be freed on detach */
+#define AMPDU_INI_FREE 10
+/* max # of mpdus released at a time */
+#define AMPDU_SCB_MAX_RELEASE 20
#define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */
#define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu
@@ -63,6 +60,11 @@
* accumulate between resets.
*/
+#define AMPDU_DELIMITER_LEN 4
+
+/* max allowed number of mpdus in an ampdu (2 streams) */
+#define AMPDU_NUM_MPDU 16
+
#define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
@@ -70,48 +72,77 @@
AMPDU_DELIMITER_LEN + 3\
+ DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
+/* modulo add/sub, bound = 2^k */
+#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1))
+#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1))
+
/* structure to hold tx fifo information and pre-loading state
* counters specific to tx underflows of ampdus
* some counters might be redundant with the ones in wlc or ampdu structures.
* This allows to maintain a specific state independently of
* how often and/or when the wlc counters are updated.
+ *
+ * ampdu_pld_size: number of bytes to be pre-loaded
+ * mcs2ampdu_table: per-mcs max # of mpdus in an ampdu
+ * prev_txfunfl: num of underflows last read from the HW macstats counter
+ * accum_txfunfl: num of underflows since we modified pld params
+ * accum_txampdu: num of tx ampdu since we modified pld params
+ * prev_txampdu: previous reading of tx ampdu
+ * dmaxferrate: estimated dma avg xfer rate in kbits/sec
+ */
+struct brcms_fifo_info {
+ u16 ampdu_pld_size;
+ u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1];
+ u16 prev_txfunfl;
+ u32 accum_txfunfl;
+ u32 accum_txampdu;
+ u32 prev_txampdu;
+ u32 dmaxferrate;
+};
+
+/* AMPDU module specific state
+ *
+ * wlc: pointer to main wlc structure
+ * scb_handle: scb cubby handle to retrieve data from scb
+ * ini_enable: per-tid initiator enable/disable of ampdu
+ * ba_tx_wsize: Tx ba window size (in pdu)
+ * ba_rx_wsize: Rx ba window size (in pdu)
+ * retry_limit: mpdu transmit retry limit
+ * rr_retry_limit: mpdu transmit retry limit at regular rate
+ * retry_limit_tid: per-tid mpdu transmit retry limit
+ * rr_retry_limit_tid: per-tid mpdu transmit retry limit at regular rate
+ * mpdu_density: min mpdu spacing (0-7) ==> 2^(x-1)/8 usec
+ * max_pdu: max pdus allowed in ampdu
+ * dur: max duration of an ampdu (in msec)
+ * txpkt_weight: weight of ampdu in txfifo; reduces rate lag
+ * rx_factor: maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes
+ * ffpld_rsvd: number of bytes to reserve for preload
+ * max_txlen: max size of ampdu per mcs, bw and sgi
+ * mfbr: enable multiple fallback rate
+ * tx_max_funl: underflows should be kept such that
+ * (tx_max_funfl*underflows) < tx frames
+ * fifo_tb: table of fifo infos
*/
-typedef struct wlc_fifo_info {
- u16 ampdu_pld_size; /* number of bytes to be pre-loaded */
- u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
- u16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
- u32 accum_txfunfl; /* num of underflows since we modified pld params */
- u32 accum_txampdu; /* num of tx ampdu since we modified pld params */
- u32 prev_txampdu; /* previous reading of tx ampdu */
- u32 dmaxferrate; /* estimated dma avg xfer rate in kbits/sec */
-} wlc_fifo_info_t;
-
-/* AMPDU module specific state */
struct ampdu_info {
- struct wlc_info *wlc; /* pointer to main wlc structure */
- int scb_handle; /* scb cubby handle to retrieve data from scb */
- u8 ini_enable[AMPDU_MAX_SCB_TID]; /* per-tid initiator enable/disable of ampdu */
- u8 ba_tx_wsize; /* Tx ba window size (in pdu) */
- u8 ba_rx_wsize; /* Rx ba window size (in pdu) */
- u8 retry_limit; /* mpdu transmit retry limit */
- u8 rr_retry_limit; /* mpdu transmit retry limit at regular rate */
- u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; /* per-tid mpdu transmit retry limit */
- /* per-tid mpdu transmit retry limit at regular rate */
+ struct brcms_c_info *wlc;
+ int scb_handle;
+ u8 ini_enable[AMPDU_MAX_SCB_TID];
+ u8 ba_tx_wsize;
+ u8 ba_rx_wsize;
+ u8 retry_limit;
+ u8 rr_retry_limit;
+ u8 retry_limit_tid[AMPDU_MAX_SCB_TID];
u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
- u8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
- s8 max_pdu; /* max pdus allowed in ampdu */
- u8 dur; /* max duration of an ampdu (in msec) */
- u8 txpkt_weight; /* weight of ampdu in txfifo; reduces rate lag */
- u8 rx_factor; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
- u32 ffpld_rsvd; /* number of bytes to reserve for preload */
- u32 max_txlen[MCS_TABLE_SIZE][2][2]; /* max size of ampdu per mcs, bw and sgi */
- void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
- bool mfbr; /* enable multiple fallback rate */
- u32 tx_max_funl; /* underflows should be kept such that
- * (tx_max_funfl*underflows) < tx frames
- */
- wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
-
+ u8 mpdu_density;
+ s8 max_pdu;
+ u8 dur;
+ u8 txpkt_weight;
+ u8 rx_factor;
+ u32 ffpld_rsvd;
+ u32 max_txlen[MCS_TABLE_SIZE][2][2];
+ bool mfbr;
+ u32 tx_max_funl;
+ struct brcms_fifo_info fifo_tb[NUM_FFPLD_FIFO];
};
/* used for flushing ampdu packets */
@@ -120,43 +151,85 @@ struct cb_del_ampdu_pars {
u16 tid;
};
-#define AMPDU_CLEANUPFLAG_RX (0x1)
-#define AMPDU_CLEANUPFLAG_TX (0x2)
+static void brcms_c_scb_ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur)
+{
+ u32 rate, mcs;
+
+ for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
+ /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
+ /* 20MHz, No SGI */
+ rate = mcs_2_rate(mcs, false, false);
+ ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
+ /* 40 MHz, No SGI */
+ rate = mcs_2_rate(mcs, true, false);
+ ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
+ /* 20MHz, SGI */
+ rate = mcs_2_rate(mcs, false, true);
+ ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
+ /* 40 MHz, SGI */
+ rate = mcs_2_rate(mcs, true, true);
+ ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
+ }
+}
+
+static bool brcms_c_ampdu_cap(struct ampdu_info *ampdu)
+{
+ if (BRCMS_PHY_11N_CAP(ampdu->wlc->band))
+ return true;
+ else
+ return false;
+}
+
+static int brcms_c_ampdu_set(struct ampdu_info *ampdu, bool on)
+{
+ struct brcms_c_info *wlc = ampdu->wlc;
+
+ wlc->pub->_ampdu = false;
-#define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
-#define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
+ if (on) {
+ if (!(wlc->pub->_n_enab & SUPPORT_11N)) {
+ wiphy_err(ampdu->wlc->wiphy, "wl%d: driver not "
+ "nmode enabled\n", wlc->pub->unit);
+ return -ENOTSUPP;
+ }
+ if (!brcms_c_ampdu_cap(ampdu)) {
+ wiphy_err(ampdu->wlc->wiphy, "wl%d: device not "
+ "ampdu capable\n", wlc->pub->unit);
+ return -ENOTSUPP;
+ }
+ wlc->pub->_ampdu = on;
+ }
-static void wlc_ffpld_init(struct ampdu_info *ampdu);
-static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int f);
-static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f);
+ return 0;
+}
-static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
- scb_ampdu_t *scb_ampdu,
- u8 tid, bool override);
-static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur);
-static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb);
-static void scb_ampdu_update_config_all(struct ampdu_info *ampdu);
+static void brcms_c_ffpld_init(struct ampdu_info *ampdu)
+{
+ int i, j;
+ struct brcms_fifo_info *fifo;
-#define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
+ for (j = 0; j < NUM_FFPLD_FIFO; j++) {
+ fifo = (ampdu->fifo_tb + j);
+ fifo->ampdu_pld_size = 0;
+ for (i = 0; i <= FFPLD_MAX_MCS; i++)
+ fifo->mcs2ampdu_table[i] = 255;
+ fifo->dmaxferrate = 0;
+ fifo->accum_txampdu = 0;
+ fifo->prev_txfunfl = 0;
+ fifo->accum_txfunfl = 0;
-static void wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu,
- struct scb *scb,
- struct sk_buff *p, tx_status_t *txs,
- u32 frmtxstatus, u32 frmtxstatus2);
-static bool wlc_ampdu_cap(struct ampdu_info *ampdu);
-static int wlc_ampdu_set(struct ampdu_info *ampdu, bool on);
+ }
+}
-struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc)
+struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc)
{
struct ampdu_info *ampdu;
int i;
ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC);
- if (!ampdu) {
- wiphy_err(wlc->wiphy, "wl%d: wlc_ampdu_attach: out of mem\n",
- wlc->pub->unit);
+ if (!ampdu)
return NULL;
- }
+
ampdu->wlc = wlc;
for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
@@ -177,8 +250,11 @@ struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc)
ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
- /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
- if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
+ /*
+ * bump max ampdu rcv size to 64k for all 11n
+ * devices except 4321A0 and 4321A1
+ */
+ if (BRCMS_ISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_32K;
else
ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_64K;
@@ -190,39 +266,29 @@ struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc)
ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
}
- ampdu_update_max_txlen(ampdu, ampdu->dur);
+ brcms_c_scb_ampdu_update_max_txlen(ampdu, ampdu->dur);
ampdu->mfbr = false;
/* try to set ampdu to the default value */
- wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
+ brcms_c_ampdu_set(ampdu, wlc->pub->_ampdu);
ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
- wlc_ffpld_init(ampdu);
+ brcms_c_ffpld_init(ampdu);
return ampdu;
}
-void wlc_ampdu_detach(struct ampdu_info *ampdu)
+void brcms_c_ampdu_detach(struct ampdu_info *ampdu)
{
- int i;
-
- if (!ampdu)
- return;
-
- /* free all ini's which were to be freed on callbacks which were never called */
- for (i = 0; i < AMPDU_INI_FREE; i++) {
- kfree(ampdu->ini_free[i]);
- }
-
- wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
kfree(ampdu);
}
-static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb)
+static void brcms_c_scb_ampdu_update_config(struct ampdu_info *ampdu,
+ struct scb *scb)
{
- scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+ struct scb_ampdu *scb_ampdu = &scb->scb_ampdu;
int i;
- scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
+ scb_ampdu->max_pdu = AMPDU_NUM_MPDU;
/* go back to legacy size if some preloading is occurring */
for (i = 0; i < NUM_FFPLD_FIFO; i++) {
@@ -234,37 +300,52 @@ static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb)
if (ampdu->max_pdu != AUTO)
scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
- scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
+ scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu,
+ AMPDU_SCB_MAX_RELEASE);
- if (scb_ampdu->max_rxlen)
- scb_ampdu->release =
- min_t(u8, scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
+ if (scb_ampdu->max_rx_ampdu_bytes)
+ scb_ampdu->release = min_t(u8, scb_ampdu->release,
+ scb_ampdu->max_rx_ampdu_bytes / 1600);
scb_ampdu->release = min(scb_ampdu->release,
ampdu->fifo_tb[TX_AC_BE_FIFO].
mcs2ampdu_table[FFPLD_MAX_MCS]);
}
-static void scb_ampdu_update_config_all(struct ampdu_info *ampdu)
+static void brcms_c_scb_ampdu_update_config_all(struct ampdu_info *ampdu)
{
- scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
+ brcms_c_scb_ampdu_update_config(ampdu, &ampdu->wlc->pri_scb);
}
-static void wlc_ffpld_init(struct ampdu_info *ampdu)
+static void brcms_c_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f)
{
- int i, j;
- wlc_fifo_info_t *fifo;
+ int i;
+ u32 phy_rate, dma_rate, tmp;
+ u8 max_mpdu;
+ struct brcms_fifo_info *fifo = (ampdu->fifo_tb + f);
- for (j = 0; j < NUM_FFPLD_FIFO; j++) {
- fifo = (ampdu->fifo_tb + j);
- fifo->ampdu_pld_size = 0;
- for (i = 0; i <= FFPLD_MAX_MCS; i++)
- fifo->mcs2ampdu_table[i] = 255;
- fifo->dmaxferrate = 0;
- fifo->accum_txampdu = 0;
- fifo->prev_txfunfl = 0;
- fifo->accum_txfunfl = 0;
+ /* recompute the dma rate */
+ /* note : we divide/multiply by 100 to avoid integer overflows */
+ max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS],
+ AMPDU_NUM_MPDU_LEGACY);
+ phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false);
+ dma_rate =
+ (((phy_rate / 100) *
+ (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
+ / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
+ fifo->dmaxferrate = dma_rate;
+ /* fill up the mcs2ampdu table; do not recalc the last mcs */
+ dma_rate = dma_rate >> 7;
+ for (i = 0; i < FFPLD_MAX_MCS; i++) {
+ /* shifting to keep it within integer range */
+ phy_rate = mcs_2_rate(i, true, false) >> 7;
+ if (phy_rate > dma_rate) {
+ tmp = ((fifo->ampdu_pld_size * phy_rate) /
+ ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
+ tmp = min_t(u32, tmp, 255);
+ fifo->mcs2ampdu_table[i] = (u8) tmp;
+ }
}
}
@@ -274,23 +355,23 @@ static void wlc_ffpld_init(struct ampdu_info *ampdu)
* Return 1 if pre-loading not active, -1 if not an underflow event,
* 0 if pre-loading module took care of the event.
*/
-static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int fid)
+static int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid)
{
struct ampdu_info *ampdu = wlc->ampdu;
- u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
+ u32 phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false);
u32 txunfl_ratio;
u8 max_mpdu;
u32 current_ampdu_cnt = 0;
u16 max_pld_size;
u32 new_txunfl;
- wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
+ struct brcms_fifo_info *fifo = (ampdu->fifo_tb + fid);
uint xmtfifo_sz;
u16 cur_txunfl;
/* return if we got here for a different reason than underflows */
- cur_txunfl =
- wlc_read_shm(wlc,
- M_UCODE_MACSTAT + offsetof(macstat_t, txfunfl[fid]));
+ cur_txunfl = brcms_b_read_shm(wlc->hw,
+ M_UCODE_MACSTAT +
+ offsetof(struct macstat, txfunfl[fid]));
new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
if (new_txunfl == 0) {
BCMMSG(wlc->wiphy, "TX status FRAG set but no tx underflows\n");
@@ -302,9 +383,8 @@ static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int fid)
return 1;
/* check if fifo is big enough */
- if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
+ if (brcms_b_xmtfifo_sz_get(wlc->hw, fid, &xmtfifo_sz))
return -1;
- }
if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
return 1;
@@ -331,13 +411,13 @@ static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int fid)
txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
if (txunfl_ratio > ampdu->tx_max_funl) {
- if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
+ if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT)
fifo->accum_txfunfl = 0;
- }
+
return 0;
}
- max_mpdu =
- min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
+ max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS],
+ AMPDU_NUM_MPDU_LEGACY);
/* In case max value max_pdu is already lower than
the fifo depth, there is nothing more we can do.
@@ -356,14 +436,15 @@ static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int fid)
fifo->ampdu_pld_size = max_pld_size;
/* update scb release size */
- scb_ampdu_update_config_all(ampdu);
+ brcms_c_scb_ampdu_update_config_all(ampdu);
/*
- compute a new dma xfer rate for max_mpdu @ max mcs.
- This is the minimum dma rate that
- can achieve no underflow condition for the current mpdu size.
+ * compute a new dma xfer rate for max_mpdu @ max mcs.
+ * This is the minimum dma rate that can achieve no
+ * underflow condition for the current mpdu size.
+ *
+ * note : we divide/multiply by 100 to avoid integer overflows
*/
- /* note : we divide/multiply by 100 to avoid integer overflows */
fifo->dmaxferrate =
(((phy_rate / 100) *
(max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
@@ -383,97 +464,71 @@ static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int fid)
fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
/* recompute the table */
- wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
+ brcms_c_ffpld_calc_mcs2ampdu_table(ampdu, fid);
/* update scb release size */
- scb_ampdu_update_config_all(ampdu);
+ brcms_c_scb_ampdu_update_config_all(ampdu);
}
}
fifo->accum_txfunfl = 0;
return 0;
}
-static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f)
+void
+brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid,
+ u8 ba_wsize, /* negotiated ba window size (in pdu) */
+ uint max_rx_ampdu_bytes) /* from ht_cap in beacon */
{
- int i;
- u32 phy_rate, dma_rate, tmp;
- u8 max_mpdu;
- wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
-
- /* recompute the dma rate */
- /* note : we divide/multiply by 100 to avoid integer overflows */
- max_mpdu =
- min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
- phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
- dma_rate =
- (((phy_rate / 100) *
- (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
- / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
- fifo->dmaxferrate = dma_rate;
+ struct scb_ampdu *scb_ampdu;
+ struct scb_ampdu_tid_ini *ini;
+ struct ampdu_info *ampdu = wlc->ampdu;
+ struct scb *scb = &wlc->pri_scb;
+ scb_ampdu = &scb->scb_ampdu;
- /* fill up the mcs2ampdu table; do not recalc the last mcs */
- dma_rate = dma_rate >> 7;
- for (i = 0; i < FFPLD_MAX_MCS; i++) {
- /* shifting to keep it within integer range */
- phy_rate = MCS_RATE(i, true, false) >> 7;
- if (phy_rate > dma_rate) {
- tmp = ((fifo->ampdu_pld_size * phy_rate) /
- ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
- tmp = min_t(u32, tmp, 255);
- fifo->mcs2ampdu_table[i] = (u8) tmp;
- }
+ if (!ampdu->ini_enable[tid]) {
+ wiphy_err(ampdu->wlc->wiphy, "%s: Rejecting tid %d\n",
+ __func__, tid);
+ return;
}
-}
-
-static void
-wlc_ampdu_agg(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p,
- uint prec)
-{
- scb_ampdu_t *scb_ampdu;
- scb_ampdu_tid_ini_t *ini;
- u8 tid = (u8) (p->priority);
-
- scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
- /* initialize initiator on first packet; sends addba req */
- ini = SCB_AMPDU_INI(scb_ampdu, tid);
- if (ini->magic != INI_MAGIC) {
- ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, false);
- }
- return;
+ ini = &scb_ampdu->ini[tid];
+ ini->tid = tid;
+ ini->scb = scb_ampdu->scb;
+ ini->ba_wsize = ba_wsize;
+ scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes;
}
int
-wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
+brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
struct sk_buff **pdu, int prec)
{
- struct wlc_info *wlc;
+ struct brcms_c_info *wlc;
struct sk_buff *p, *pkt[AMPDU_MAX_MPDU];
u8 tid, ndelim;
int err = 0;
- u8 preamble_type = WLC_GF_PREAMBLE;
- u8 fbr_preamble_type = WLC_GF_PREAMBLE;
- u8 rts_preamble_type = WLC_LONG_PREAMBLE;
- u8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
+ u8 preamble_type = BRCMS_GF_PREAMBLE;
+ u8 fbr_preamble_type = BRCMS_GF_PREAMBLE;
+ u8 rts_preamble_type = BRCMS_LONG_PREAMBLE;
+ u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE;
bool rr = true, fbr = false;
uint i, count = 0, fifo, seg_cnt = 0;
u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
- u32 ampdu_len, maxlen = 0;
- d11txh_t *txh = NULL;
+ u32 ampdu_len, max_ampdu_bytes = 0;
+ struct d11txh *txh = NULL;
u8 *plcp;
struct ieee80211_hdr *h;
struct scb *scb;
- scb_ampdu_t *scb_ampdu;
- scb_ampdu_tid_ini_t *ini;
+ struct scb_ampdu *scb_ampdu;
+ struct scb_ampdu_tid_ini *ini;
u8 mcs = 0;
bool use_rts = false, use_cts = false;
- ratespec_t rspec = 0, rspec_fallback = 0;
- ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
+ u32 rspec = 0, rspec_fallback = 0;
+ u32 rts_rspec = 0, rts_rspec_fallback = 0;
u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
struct ieee80211_rts *rts;
u8 rr_retry_limit;
- wlc_fifo_info_t *f;
+ struct brcms_fifo_info *f;
bool fbr_iscck;
struct ieee80211_tx_info *tx_info;
u16 qlen;
@@ -487,22 +542,18 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
f = ampdu->fifo_tb + prio2fifo[tid];
- scb = wlc->pub->global_scb;
- scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+ scb = &wlc->pri_scb;
+ scb_ampdu = &scb->scb_ampdu;
ini = &scb_ampdu->ini[tid];
/* Let pressure continue to build ... */
qlen = pktq_plen(&qi->q, prec);
- if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
+ if (ini->tx_in_transit > 0 &&
+ qlen < min(scb_ampdu->max_pdu, ini->ba_wsize))
+ /* Collect multiple MPDU's to be sent in the next AMPDU */
return -EBUSY;
- }
- wlc_ampdu_agg(ampdu, scb, p, tid);
-
- if (wlc->block_datafifo) {
- wiphy_err(wiphy, "%s: Fifo blocked\n", __func__);
- return -EBUSY;
- }
+ /* at this point we intend to transmit an AMPDU */
rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
ampdu_len = 0;
dma_len = 0;
@@ -513,7 +564,7 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
txrate = tx_info->status.rates;
if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
- err = wlc_prep_pdu(wlc, p, &fifo);
+ err = brcms_c_prep_pdu(wlc, p, &fifo);
} else {
wiphy_err(wiphy, "%s: AMPDU flag is off!\n", __func__);
*pdu = NULL;
@@ -523,7 +574,7 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
if (err) {
if (err == -EBUSY) {
- wiphy_err(wiphy, "wl%d: wlc_sendampdu: "
+ wiphy_err(wiphy, "wl%d: sendampdu: "
"prep_xdu retry; seq 0x%x\n",
wlc->pub->unit, seq);
*pdu = p;
@@ -531,14 +582,14 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
}
/* error in the packet; reject it */
- wiphy_err(wiphy, "wl%d: wlc_sendampdu: prep_xdu "
+ wiphy_err(wiphy, "wl%d: sendampdu: prep_xdu "
"rejected; seq 0x%x\n", wlc->pub->unit, seq);
*pdu = NULL;
break;
}
/* pkt is good to be aggregated */
- txh = (d11txh_t *) p->data;
+ txh = (struct d11txh *) p->data;
plcp = (u8 *) (txh + 1);
h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT;
@@ -562,8 +613,8 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
}
/* extract the length info */
- len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
- : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
+ len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
+ : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
/* retrieve null delimiter count */
ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
@@ -598,7 +649,7 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
len = roundup(len, 4);
ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
- dma_len += (u16) bcm_pkttotlen(p);
+ dma_len += (u16) brcmu_pkttotlen(p);
BCMMSG(wlc->wiphy, "wl%d: ampdu_len %d"
" seg_cnt %d null delim %d\n",
@@ -625,21 +676,17 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
}
is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
- sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
+ sgi = plcp3_issgi(plcp3) ? 1 : 0;
mcs = plcp0 & ~MIMO_PLCP_40MHZ;
- maxlen =
- min(scb_ampdu->max_rxlen,
+ max_ampdu_bytes =
+ min(scb_ampdu->max_rx_ampdu_bytes,
ampdu->max_txlen[mcs][is40][sgi]);
- /* XXX Fix me to honor real max_rxlen */
- /* can fix this as soon as ampdu_action() in mac80211.h
- * gets extra u8buf_size par */
- maxlen = 64 * 1024;
-
if (is40)
mimo_ctlchbw =
- CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
- ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
+ CHSPEC_SB_UPPER(wlc_phy_chanspec_get(
+ wlc->band->pi))
+ ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
/* rebuild the rspec and rspec_fallback */
rspec = RSPEC_MIMORATE;
@@ -648,9 +695,8 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
if (fbr_iscck) /* CCK */
- rspec_fallback =
- CCK_RSPEC(CCK_PHY2MAC_RATE
- (txh->FragPLCPFallback[0]));
+ rspec_fallback = cck_rspec(cck_phy2mac_rate
+ (txh->FragPLCPFallback[0]));
else { /* MIMO */
rspec_fallback = RSPEC_MIMORATE;
rspec_fallback |=
@@ -663,17 +709,17 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
if (use_rts || use_cts) {
rts_rspec =
- wlc_rspec_to_rts_rspec(wlc, rspec, false,
- mimo_ctlchbw);
+ brcms_c_rspec_to_rts_rspec(wlc,
+ rspec, false, mimo_ctlchbw);
rts_rspec_fallback =
- wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
- false, mimo_ctlchbw);
+ brcms_c_rspec_to_rts_rspec(wlc,
+ rspec_fallback, false, mimo_ctlchbw);
}
}
/* if (first mpdu for host agg) */
/* test whether to add more */
- if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
+ if ((mcs_2_rate(mcs, true, false) >= f->dmaxferrate) &&
(count == f->mcs2ampdu_table[mcs])) {
BCMMSG(wlc->wiphy, "wl%d: PR 37644: stopping"
" ampdu at %d for mcs %d\n",
@@ -681,37 +727,41 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
break;
}
- if (count == scb_ampdu->max_pdu) {
+ if (count == scb_ampdu->max_pdu)
break;
- }
- /* check to see if the next pkt is a candidate for aggregation */
+ /*
+ * check to see if the next pkt is
+ * a candidate for aggregation
+ */
p = pktq_ppeek(&qi->q, prec);
- tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
+ /* tx_info must be checked with current p */
+ tx_info = IEEE80211_SKB_CB(p);
if (p) {
if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
((u8) (p->priority) == tid)) {
- plen =
- bcm_pkttotlen(p) + AMPDU_MAX_MPDU_OVERHEAD;
+ plen = brcmu_pkttotlen(p) +
+ AMPDU_MAX_MPDU_OVERHEAD;
plen = max(scb_ampdu->min_len, plen);
- if ((plen + ampdu_len) > maxlen) {
+ if ((plen + ampdu_len) > max_ampdu_bytes) {
p = NULL;
- wiphy_err(wiphy, "%s: Bogus plen #1\n",
- __func__);
continue;
}
- /* check if there are enough descriptors available */
- if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
+ /*
+ * check if there are enough
+ * descriptors available
+ */
+ if (*wlc->core->txavail[fifo] <= seg_cnt + 1) {
wiphy_err(wiphy, "%s: No fifo space "
"!!\n", __func__);
p = NULL;
continue;
}
- p = bcm_pktq_pdeq(&qi->q, prec);
+ p = brcmu_pktq_pdeq(&qi->q, prec);
} else {
p = NULL;
}
@@ -722,7 +772,7 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
if (count) {
/* patch up the last txh */
- txh = (d11txh_t *) pkt[count - 1]->data;
+ txh = (struct d11txh *) pkt[count - 1]->data;
mcl = le16_to_cpu(txh->MacTxControlLow);
mcl &= ~TXC_AMPDU_MASK;
mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
@@ -735,34 +785,35 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
/* remove the pad len from last mpdu */
fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0);
- len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
- : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
+ len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
+ : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
ampdu_len -= roundup(len, 4) - len;
/* patch up the first txh & plcp */
- txh = (d11txh_t *) pkt[0]->data;
+ txh = (struct d11txh *) pkt[0]->data;
plcp = (u8 *) (txh + 1);
- WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
+ BRCMS_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
/* mark plcp to indicate ampdu */
- WLC_SET_MIMO_PLCP_AMPDU(plcp);
+ BRCMS_SET_MIMO_PLCP_AMPDU(plcp);
/* reset the mixed mode header durations */
if (txh->MModeLen) {
u16 mmodelen =
- wlc_calc_lsig_len(wlc, rspec, ampdu_len);
+ brcms_c_calc_lsig_len(wlc, rspec, ampdu_len);
txh->MModeLen = cpu_to_le16(mmodelen);
- preamble_type = WLC_MM_PREAMBLE;
+ preamble_type = BRCMS_MM_PREAMBLE;
}
if (txh->MModeFbrLen) {
u16 mmfbrlen =
- wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
+ brcms_c_calc_lsig_len(wlc, rspec_fallback,
+ ampdu_len);
txh->MModeFbrLen = cpu_to_le16(mmfbrlen);
- fbr_preamble_type = WLC_MM_PREAMBLE;
+ fbr_preamble_type = BRCMS_MM_PREAMBLE;
}
/* set the preload length */
- if (MCS_RATE(mcs, true, false) >= f->dmaxferrate) {
+ if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) {
dma_len = min(dma_len, f->ampdu_pld_size);
txh->PreloadSize = cpu_to_le16(dma_len);
} else
@@ -776,19 +827,19 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
rts = (struct ieee80211_rts *)&txh->rts_frame;
if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
TXC_PREAMBLE_RTS_MAIN_SHORT)
- rts_preamble_type = WLC_SHORT_PREAMBLE;
+ rts_preamble_type = BRCMS_SHORT_PREAMBLE;
if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
TXC_PREAMBLE_RTS_FB_SHORT)
- rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
+ rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE;
durid =
- wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
+ brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec,
rspec, rts_preamble_type,
preamble_type, ampdu_len,
true);
rts->duration = cpu_to_le16(durid);
- durid = wlc_compute_rtscts_dur(wlc, use_cts,
+ durid = brcms_c_compute_rtscts_dur(wlc, use_cts,
rts_rspec_fallback,
rspec_fallback,
rts_fbr_preamble_type,
@@ -805,8 +856,8 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
if (fbr) {
mch |= TXC_AMPDU_FBR;
txh->MacTxControlHigh = cpu_to_le16(mch);
- WLC_SET_MIMO_PLCP_AMPDU(plcp);
- WLC_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
+ BRCMS_SET_MIMO_PLCP_AMPDU(plcp);
+ BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
}
BCMMSG(wlc->wiphy, "wl%d: count %d ampdu_len %d\n",
@@ -814,12 +865,12 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
/* inform rate_sel if it this is a rate probe pkt */
frameid = le16_to_cpu(txh->TxFrameID);
- if (frameid & TXFID_RATE_PROBE_MASK) {
+ if (frameid & TXFID_RATE_PROBE_MASK)
wiphy_err(wiphy, "%s: XXX what to do with "
"TXFID_RATE_PROBE_MASK!?\n", __func__);
- }
+
for (i = 0; i < count; i++)
- wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
+ brcms_c_txfifo(wlc, fifo, pkt[i], i == (count - 1),
ampdu->txpkt_weight);
}
@@ -827,65 +878,10 @@ wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
return err;
}
-void
-wlc_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
- struct sk_buff *p, tx_status_t *txs)
-{
- scb_ampdu_t *scb_ampdu;
- struct wlc_info *wlc = ampdu->wlc;
- scb_ampdu_tid_ini_t *ini;
- u32 s1 = 0, s2 = 0;
- struct ieee80211_tx_info *tx_info;
-
- tx_info = IEEE80211_SKB_CB(p);
-
- /* BMAC_NOTE: For the split driver, second level txstatus comes later
- * So if the ACK was received then wait for the second level else just
- * call the first one
- */
- if (txs->status & TX_STATUS_ACK_RCV) {
- u8 status_delay = 0;
-
- /* wait till the next 8 bytes of txstatus is available */
- while (((s1 = R_REG(&wlc->regs->frmtxstatus)) & TXS_V) == 0) {
- udelay(1);
- status_delay++;
- if (status_delay > 10) {
- return; /* error condition */
- }
- }
-
- s2 = R_REG(&wlc->regs->frmtxstatus2);
- }
-
- if (likely(scb)) {
- scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
- ini = SCB_AMPDU_INI(scb_ampdu, p->priority);
- wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
- } else {
- /* loop through all pkts and free */
- u8 queue = txs->frameid & TXFID_QUEUE_MASK;
- d11txh_t *txh;
- u16 mcl;
- while (p) {
- tx_info = IEEE80211_SKB_CB(p);
- txh = (d11txh_t *) p->data;
- mcl = le16_to_cpu(txh->MacTxControlLow);
- bcm_pkt_buf_free_skb(p);
- /* break out if last packet of ampdu */
- if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
- TXC_AMPDU_LAST)
- break;
- p = GETNEXTTXP(wlc, queue);
- }
- wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
- }
- wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
-}
-
static void
-rate_status(struct wlc_info *wlc, struct ieee80211_tx_info *tx_info,
- tx_status_t *txs, u8 mcs)
+brcms_c_ampdu_rate_status(struct brcms_c_info *wlc,
+ struct ieee80211_tx_info *tx_info,
+ struct tx_status *txs, u8 mcs)
{
struct ieee80211_tx_rate *txrate = tx_info->status.rates;
int i;
@@ -897,18 +893,16 @@ rate_status(struct wlc_info *wlc, struct ieee80211_tx_info *tx_info,
}
}
-#define SHORTNAME "AMPDU status"
-
static void
-wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
- struct sk_buff *p, tx_status_t *txs,
+brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
+ struct sk_buff *p, struct tx_status *txs,
u32 s1, u32 s2)
{
- scb_ampdu_t *scb_ampdu;
- struct wlc_info *wlc = ampdu->wlc;
- scb_ampdu_tid_ini_t *ini;
+ struct scb_ampdu *scb_ampdu;
+ struct brcms_c_info *wlc = ampdu->wlc;
+ struct scb_ampdu_tid_ini *ini;
u8 bitmap[8], queue, tid;
- d11txh_t *txh;
+ struct d11txh *txh;
u8 *plcp;
struct ieee80211_hdr *h;
u16 seq, start_seq = 0, bindex, index, mcl;
@@ -928,10 +922,10 @@ wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
memset(hole, 0, sizeof(hole));
#endif
- scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+ scb_ampdu = &scb->scb_ampdu;
tid = (u8) (p->priority);
- ini = SCB_AMPDU_INI(scb_ampdu, tid);
+ ini = &scb_ampdu->ini[tid];
retry_limit = ampdu->retry_limit_tid[tid];
rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
memset(bitmap, 0, sizeof(bitmap));
@@ -939,9 +933,8 @@ wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
supr_status = txs->status & TX_STATUS_SUPR_MASK;
if (txs->status & TX_STATUS_ACK_RCV) {
- if (TX_STATUS_SUPR_UF == supr_status) {
+ if (TX_STATUS_SUPR_UF == supr_status)
update_rate = false;
- }
WARN_ON(!(txs->status & TX_STATUS_INTERMEDIATE));
start_seq = txs->sequence >> SEQNUM_SHIFT;
@@ -974,43 +967,45 @@ wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
wlc->default_bss->chanspec));
} else {
if (supr_status != TX_STATUS_SUPR_FRAG)
- wiphy_err(wiphy, "%s: wlc_ampdu_dotx"
- "status:supr_status 0x%x\n",
- __func__, supr_status);
+ wiphy_err(wiphy, "%s:"
+ "supr_status 0x%x\n",
+ __func__, supr_status);
}
/* no need to retry for badch; will fail again */
if (supr_status == TX_STATUS_SUPR_BADCH ||
supr_status == TX_STATUS_SUPR_EXPTIME) {
retry = false;
} else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
- /* TX underflow : try tuning pre-loading or ampdu size */
+ /* TX underflow:
+ * try tuning pre-loading or ampdu size
+ */
} else if (supr_status == TX_STATUS_SUPR_FRAG) {
- /* if there were underflows, but pre-loading is not active,
- notify rate adaptation.
+ /*
+ * if there were underflows, but pre-loading
+ * is not active, notify rate adaptation.
*/
- if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
- > 0) {
+ if (brcms_c_ffpld_check_txfunfl(wlc,
+ prio2fifo[tid]) > 0)
tx_error = true;
- }
}
} else if (txs->phyerr) {
update_rate = false;
- wiphy_err(wiphy, "wl%d: wlc_ampdu_dotxstatus: tx phy "
+ wiphy_err(wiphy, "wl%d: ampdu tx phy "
"error (0x%x)\n", wlc->pub->unit,
txs->phyerr);
- if (WL_ERROR_ON()) {
- bcm_prpkt("txpkt (AMPDU)", p);
- wlc_print_txdesc((d11txh_t *) p->data);
+ if (brcm_msg_level & LOG_ERROR_VAL) {
+ brcmu_prpkt("txpkt (AMPDU)", p);
+ brcms_c_print_txdesc((struct d11txh *) p->data);
}
- wlc_print_txstatus(txs);
+ brcms_c_print_txstatus(txs);
}
}
/* loop through all pkts and retry if not acked */
while (p) {
tx_info = IEEE80211_SKB_CB(p);
- txh = (d11txh_t *) p->data;
+ txh = (struct d11txh *) p->data;
mcl = le16_to_cpu(txh->MacTxControlLow);
plcp = (u8 *) (txh + 1);
h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
@@ -1035,9 +1030,13 @@ wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
ini->tx_in_transit--;
ini->txretry[index] = 0;
- /* ampdu_ack_len: number of acked aggregated frames */
+ /*
+ * ampdu_ack_len:
+ * number of acked aggregated frames
+ */
/* ampdu_len: number of aggregated frames */
- rate_status(wlc, tx_info, txs, mcs);
+ brcms_c_ampdu_rate_status(wlc, tx_info, txs,
+ mcs);
tx_info->flags |= IEEE80211_TX_STAT_ACK;
tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
tx_info->status.ampdu_ack_len =
@@ -1059,10 +1058,14 @@ wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
if (retry && (txrate[0].count < (int)retry_limit)) {
ini->txretry[index]++;
ini->tx_in_transit--;
- /* Use high prededence for retransmit to give some punch */
- /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
- wlc_txq_enq(wlc, scb, p,
- WLC_PRIO_TO_HI_PREC(tid));
+ /*
+ * Use high prededence for retransmit to
+ * give some punch
+ */
+ /* brcms_c_txq_enq(wlc, scb, p,
+ * BRCMS_PRIO_TO_PREC(tid)); */
+ brcms_c_txq_enq(wlc, scb, p,
+ BRCMS_PRIO_TO_HI_PREC(tid));
} else {
/* Retry timeout */
ini->tx_in_transit--;
@@ -1074,7 +1077,7 @@ wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
skb_pull(p, D11_PHY_HDR_LEN);
skb_pull(p, D11_TXH_LEN);
wiphy_err(wiphy, "%s: BA Timeout, seq %d, in_"
- "transit %d\n", SHORTNAME, seq,
+ "transit %d\n", "AMPDU status", seq,
ini->tx_in_transit);
ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
p);
@@ -1087,117 +1090,103 @@ wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
TXC_AMPDU_LAST)
break;
- p = GETNEXTTXP(wlc, queue);
+ p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED);
}
- wlc_send_q(wlc);
+ brcms_c_send_q(wlc);
/* update rate state */
- antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
+ antselid = brcms_c_antsel_antsel2id(wlc->asi, mimoantsel);
- wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
+ brcms_c_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
}
-/* initialize the initiator code for tid */
-static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
- scb_ampdu_t *scb_ampdu,
- u8 tid, bool override)
+void
+brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
+ struct sk_buff *p, struct tx_status *txs)
{
- scb_ampdu_tid_ini_t *ini;
-
- /* check for per-tid control of ampdu */
- if (!ampdu->ini_enable[tid]) {
- wiphy_err(ampdu->wlc->wiphy, "%s: Rejecting tid %d\n",
- __func__, tid);
- return NULL;
- }
-
- ini = SCB_AMPDU_INI(scb_ampdu, tid);
- ini->tid = tid;
- ini->scb = scb_ampdu->scb;
- ini->magic = INI_MAGIC;
- return ini;
-}
+ struct scb_ampdu *scb_ampdu;
+ struct brcms_c_info *wlc = ampdu->wlc;
+ struct scb_ampdu_tid_ini *ini;
+ u32 s1 = 0, s2 = 0;
+ struct ieee80211_tx_info *tx_info;
-static int wlc_ampdu_set(struct ampdu_info *ampdu, bool on)
-{
- struct wlc_info *wlc = ampdu->wlc;
+ tx_info = IEEE80211_SKB_CB(p);
- wlc->pub->_ampdu = false;
+ /* BMAC_NOTE: For the split driver, second level txstatus comes later
+ * So if the ACK was received then wait for the second level else just
+ * call the first one
+ */
+ if (txs->status & TX_STATUS_ACK_RCV) {
+ u8 status_delay = 0;
- if (on) {
- if (!N_ENAB(wlc->pub)) {
- wiphy_err(ampdu->wlc->wiphy, "wl%d: driver not "
- "nmode enabled\n", wlc->pub->unit);
- return -ENOTSUPP;
- }
- if (!wlc_ampdu_cap(ampdu)) {
- wiphy_err(ampdu->wlc->wiphy, "wl%d: device not "
- "ampdu capable\n", wlc->pub->unit);
- return -ENOTSUPP;
+ /* wait till the next 8 bytes of txstatus is available */
+ while (((s1 = R_REG(&wlc->regs->frmtxstatus)) & TXS_V) == 0) {
+ udelay(1);
+ status_delay++;
+ if (status_delay > 10)
+ return; /* error condition */
}
- wlc->pub->_ampdu = on;
- }
- return 0;
-}
-
-static bool wlc_ampdu_cap(struct ampdu_info *ampdu)
-{
- if (WLC_PHY_11N_CAP(ampdu->wlc->band))
- return true;
- else
- return false;
-}
-
-static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur)
-{
- u32 rate, mcs;
+ s2 = R_REG(&wlc->regs->frmtxstatus2);
+ }
- for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
- /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
- /* 20MHz, No SGI */
- rate = MCS_RATE(mcs, false, false);
- ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
- /* 40 MHz, No SGI */
- rate = MCS_RATE(mcs, true, false);
- ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
- /* 20MHz, SGI */
- rate = MCS_RATE(mcs, false, true);
- ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
- /* 40 MHz, SGI */
- rate = MCS_RATE(mcs, true, true);
- ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
+ if (scb) {
+ scb_ampdu = &scb->scb_ampdu;
+ ini = &scb_ampdu->ini[p->priority];
+ brcms_c_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
+ } else {
+ /* loop through all pkts and free */
+ u8 queue = txs->frameid & TXFID_QUEUE_MASK;
+ struct d11txh *txh;
+ u16 mcl;
+ while (p) {
+ tx_info = IEEE80211_SKB_CB(p);
+ txh = (struct d11txh *) p->data;
+ mcl = le16_to_cpu(txh->MacTxControlLow);
+ brcmu_pkt_buf_free_skb(p);
+ /* break out if last packet of ampdu */
+ if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
+ TXC_AMPDU_LAST)
+ break;
+ p = dma_getnexttxp(wlc->hw->di[queue],
+ DMA_RANGE_TRANSMITTED);
+ }
+ brcms_c_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
}
}
-void wlc_ampdu_macaddr_upd(struct wlc_info *wlc)
+void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc)
{
char template[T_RAM_ACCESS_SZ * 2];
/* driver needs to write the ta in the template; ta is at offset 16 */
memset(template, 0, sizeof(template));
memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN);
- wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
- template);
+ brcms_b_write_template_ram(wlc->hw, (T_BA_TPL_BASE + 16),
+ (T_RAM_ACCESS_SZ * 2),
+ template);
}
-bool wlc_aggregatable(struct wlc_info *wlc, u8 tid)
+bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid)
{
return wlc->ampdu->ini_enable[tid];
}
-void wlc_ampdu_shm_upd(struct ampdu_info *ampdu)
+void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu)
{
- struct wlc_info *wlc = ampdu->wlc;
+ struct brcms_c_info *wlc = ampdu->wlc;
- /* Extend ucode internal watchdog timer to match larger received frames */
+ /*
+ * Extend ucode internal watchdog timer to
+ * match larger received frames
+ */
if ((ampdu->rx_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) ==
IEEE80211_HT_MAX_AMPDU_64K) {
- wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
- wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
+ brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
+ brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
} else {
- wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
- wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);
+ brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
+ brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);
}
}
@@ -1235,19 +1224,18 @@ static void dma_cb_fn_ampdu(void *txi, void *arg_a)
* When a remote party is no longer available for ampdu communication, any
* pending tx ampdu packets in the driver have to be flushed.
*/
-void wlc_ampdu_flush(struct wlc_info *wlc,
+void brcms_c_ampdu_flush(struct brcms_c_info *wlc,
struct ieee80211_sta *sta, u16 tid)
{
- struct wlc_txq_info *qi = wlc->pkt_queue;
+ struct brcms_txq_info *qi = wlc->pkt_queue;
struct pktq *pq = &qi->q;
int prec;
struct cb_del_ampdu_pars ampdu_pars;
ampdu_pars.sta = sta;
ampdu_pars.tid = tid;
- for (prec = 0; prec < pq->num_prec; prec++) {
- bcm_pktq_pflush(pq, prec, true, cb_del_ampdu_pkt,
+ for (prec = 0; prec < pq->num_prec; prec++)
+ brcmu_pktq_pflush(pq, prec, true, cb_del_ampdu_pkt,
(void *)&ampdu_pars);
- }
- wlc_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu);
+ brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu);
}
diff --git a/drivers/staging/brcm80211/brcmfmac/sdiovar.h b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h
index d1cfa5f0a982..421f4ba7c63c 100644
--- a/drivers/staging/brcm80211/brcmfmac/sdiovar.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h
@@ -14,25 +14,17 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _sdiovar_h_
-#define _sdiovar_h_
+#ifndef _BRCM_AMPDU_H_
+#define _BRCM_AMPDU_H_
-typedef struct sdreg {
- int func;
- int offset;
- int value;
-} sdreg_t;
+extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc);
+extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu);
+extern int brcms_c_sendampdu(struct ampdu_info *ampdu,
+ struct brcms_txq_info *qi,
+ struct sk_buff **aggp, int prec);
+extern void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
+ struct sk_buff *p, struct tx_status *txs);
+extern void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc);
+extern void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu);
-/* Common msglevel constants */
-#define SDH_ERROR_VAL 0x0001 /* Error */
-#define SDH_TRACE_VAL 0x0002 /* Trace */
-#define SDH_INFO_VAL 0x0004 /* Info */
-#define SDH_DEBUG_VAL 0x0008 /* Debug */
-#define SDH_DATA_VAL 0x0010 /* Data */
-#define SDH_CTRL_VAL 0x0020 /* Control Regs */
-#define SDH_LOG_VAL 0x0040 /* Enable bcmlog */
-#define SDH_DMA_VAL 0x0080 /* DMA */
-
-#define NUM_PREV_TRANSACTIONS 16
-
-#endif /* _sdiovar_h_ */
+#endif /* _BRCM_AMPDU_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/wlc_antsel.c b/drivers/net/wireless/brcm80211/brcmsmac/antsel.c
index 111ef32b7ac4..a47ce25cb9a2 100644
--- a/drivers/staging/brcm80211/brcmsmac/wlc_antsel.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/antsel.c
@@ -14,40 +14,28 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <wlc_cfg.h>
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-
-#include <bcmdefs.h>
-#include <bcmutils.h>
-#include <bcmnvram.h>
-#include <aiutils.h>
-#include <bcmdevs.h>
-#include <sbhnddma.h>
-#include <wlioctl.h>
-
-#include "d11.h"
-#include "wlc_rate.h"
-#include "wlc_key.h"
-#include "wlc_scb.h"
-#include "wlc_pub.h"
-#include "wl_dbg.h"
-#include "phy/wlc_phy_hal.h"
-#include "wlc_bmac.h"
-#include "wlc_channel.h"
-#include "wlc_main.h"
-#include "wl_export.h"
-#include "wlc_phy_shim.h"
-#include "wlc_antsel.h"
+#include <linux/slab.h>
+#include <net/mac80211.h>
+
+#include "types.h"
+#include "main.h"
+#include "phy_shim.h"
+#include "antsel.h"
+
+#define ANT_SELCFG_AUTO 0x80 /* bit indicates antenna sel AUTO */
+#define ANT_SELCFG_MASK 0x33 /* antenna configuration mask */
+#define ANT_SELCFG_TX_UNICAST 0 /* unicast tx antenna configuration */
+#define ANT_SELCFG_RX_UNICAST 1 /* unicast rx antenna configuration */
+#define ANT_SELCFG_TX_DEF 2 /* default tx antenna configuration */
+#define ANT_SELCFG_RX_DEF 3 /* default rx antenna configuration */
/* useful macros */
-#define WLC_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf)
-#define WLC_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf)
-#define WLC_ANTIDX_11N(ant) (((WLC_ANTSEL_11N_0(ant)) << 2) + (WLC_ANTSEL_11N_1(ant)))
-#define WLC_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO)
-#define WLC_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK)
+#define BRCMS_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf)
+#define BRCMS_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf)
+#define BRCMS_ANTIDX_11N(ant) (((BRCMS_ANTSEL_11N_0(ant)) << 2) +\
+ (BRCMS_ANTSEL_11N_1(ant)))
+#define BRCMS_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO)
+#define BRCMS_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK)
/* antenna switch */
/* defines for no boardlevel antenna diversity */
@@ -61,54 +49,76 @@
#define ANT_SELCFG_NUM_2x4 4
#define ANT_SELCFG_DEF_2x4 0x02 /* default antenna configuration */
-/* static functions */
-static int wlc_antsel_cfgupd(struct antsel_info *asi, wlc_antselcfg_t *antsel);
-static u8 wlc_antsel_id2antcfg(struct antsel_info *asi, u8 id);
-static u16 wlc_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg);
-static void wlc_antsel_init_cfg(struct antsel_info *asi,
- wlc_antselcfg_t *antsel,
- bool auto_sel);
-
-const u16 mimo_2x4_div_antselpat_tbl[] = {
+static const u16 mimo_2x4_div_antselpat_tbl[] = {
0, 0, 0x9, 0xa, /* ant0: 0 ant1: 2,3 */
0, 0, 0x5, 0x6, /* ant0: 1 ant1: 2,3 */
0, 0, 0, 0, /* n.a. */
0, 0, 0, 0 /* n.a. */
};
-const u8 mimo_2x4_div_antselid_tbl[16] = {
+static const u8 mimo_2x4_div_antselid_tbl[16] = {
0, 0, 0, 0, 0, 2, 3, 0,
0, 0, 1, 0, 0, 0, 0, 0 /* pat to antselid */
};
-const u16 mimo_2x3_div_antselpat_tbl[] = {
+static const u16 mimo_2x3_div_antselpat_tbl[] = {
16, 0, 1, 16, /* ant0: 0 ant1: 1,2 */
16, 16, 16, 16, /* n.a. */
16, 2, 16, 16, /* ant0: 2 ant1: 1 */
16, 16, 16, 16 /* n.a. */
};
-const u8 mimo_2x3_div_antselid_tbl[16] = {
+static const u8 mimo_2x3_div_antselid_tbl[16] = {
0, 1, 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 /* pat to antselid */
};
-struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc)
+/* boardlevel antenna selection: init antenna selection structure */
+static void
+brcms_c_antsel_init_cfg(struct antsel_info *asi, struct brcms_antselcfg *antsel,
+ bool auto_sel)
+{
+ if (asi->antsel_type == ANTSEL_2x3) {
+ u8 antcfg_def = ANT_SELCFG_DEF_2x3 |
+ ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0);
+ antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def;
+ antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def;
+ antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def;
+ antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def;
+ antsel->num_antcfg = ANT_SELCFG_NUM_2x3;
+
+ } else if (asi->antsel_type == ANTSEL_2x4) {
+
+ antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4;
+ antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4;
+ antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4;
+ antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4;
+ antsel->num_antcfg = ANT_SELCFG_NUM_2x4;
+
+ } else { /* no antenna selection available */
+
+ antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2;
+ antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2;
+ antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2;
+ antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2;
+ antsel->num_antcfg = 0;
+ }
+}
+
+struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc)
{
struct antsel_info *asi;
+ struct si_pub *sih = wlc->hw->sih;
asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC);
- if (!asi) {
- wiphy_err(wlc->wiphy, "wl%d: wlc_antsel_attach: out of mem\n",
- wlc->pub->unit);
+ if (!asi)
return NULL;
- }
asi->wlc = wlc;
asi->pub = wlc->pub;
asi->antsel_type = ANTSEL_NA;
asi->antsel_avail = false;
- asi->antsel_antswitch = (u8) getintvar(asi->pub->vars, "antswitch");
+ asi->antsel_antswitch = (u8) getintvar(sih, BRCMS_SROM_ANTSWITCH);
if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) {
switch (asi->antsel_antswitch) {
@@ -118,27 +128,26 @@ struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc)
/* 4321/2 board with 2x3 switch logic */
asi->antsel_type = ANTSEL_2x3;
/* Antenna selection availability */
- if (((u16) getintvar(asi->pub->vars, "aa2g") == 7) ||
- ((u16) getintvar(asi->pub->vars, "aa5g") == 7)) {
+ if (((u16) getintvar(sih, BRCMS_SROM_AA2G) == 7) ||
+ ((u16) getintvar(sih, BRCMS_SROM_AA5G) == 7)) {
asi->antsel_avail = true;
- } else
- if (((u16) getintvar(asi->pub->vars, "aa2g") ==
- 3)
- || ((u16) getintvar(asi->pub->vars, "aa5g")
- == 3)) {
+ } else if (
+ (u16) getintvar(sih, BRCMS_SROM_AA2G) == 3 ||
+ (u16) getintvar(sih, BRCMS_SROM_AA5G) == 3) {
asi->antsel_avail = false;
} else {
asi->antsel_avail = false;
- wiphy_err(wlc->wiphy, "wlc_antsel_attach: 2o3 "
+ wiphy_err(wlc->wiphy, "antsel_attach: 2o3 "
"board cfg invalid\n");
}
+
break;
default:
break;
}
} else if ((asi->pub->sromrev == 4) &&
- ((u16) getintvar(asi->pub->vars, "aa2g") == 7) &&
- ((u16) getintvar(asi->pub->vars, "aa5g") == 0)) {
+ ((u16) getintvar(sih, BRCMS_SROM_AA2G) == 7) &&
+ ((u16) getintvar(sih, BRCMS_SROM_AA5G) == 0)) {
/* hack to match old 4321CB2 cards with 2of3 antenna switch */
asi->antsel_type = ANTSEL_2x3;
asi->antsel_avail = true;
@@ -148,61 +157,106 @@ struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc)
}
/* Set the antenna selection type for the low driver */
- wlc_bmac_antsel_type_set(wlc->hw, asi->antsel_type);
+ brcms_b_antsel_type_set(wlc->hw, asi->antsel_type);
/* Init (auto/manual) antenna selection */
- wlc_antsel_init_cfg(asi, &asi->antcfg_11n, true);
- wlc_antsel_init_cfg(asi, &asi->antcfg_cur, true);
+ brcms_c_antsel_init_cfg(asi, &asi->antcfg_11n, true);
+ brcms_c_antsel_init_cfg(asi, &asi->antcfg_cur, true);
return asi;
}
-void wlc_antsel_detach(struct antsel_info *asi)
+void brcms_c_antsel_detach(struct antsel_info *asi)
{
kfree(asi);
}
-void wlc_antsel_init(struct antsel_info *asi)
+/*
+ * boardlevel antenna selection:
+ * convert ant_cfg to mimo_antsel (ucode interface)
+ */
+static u16 brcms_c_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg)
{
- if ((asi->antsel_type == ANTSEL_2x3) ||
- (asi->antsel_type == ANTSEL_2x4))
- wlc_antsel_cfgupd(asi, &asi->antcfg_11n);
+ u8 idx = BRCMS_ANTIDX_11N(BRCMS_ANTSEL_11N(ant_cfg));
+ u16 mimo_antsel = 0;
+
+ if (asi->antsel_type == ANTSEL_2x4) {
+ /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
+ mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf);
+ return mimo_antsel;
+
+ } else if (asi->antsel_type == ANTSEL_2x3) {
+ /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
+ mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf);
+ return mimo_antsel;
+ }
+
+ return mimo_antsel;
}
-/* boardlevel antenna selection: init antenna selection structure */
-static void
-wlc_antsel_init_cfg(struct antsel_info *asi, wlc_antselcfg_t *antsel,
- bool auto_sel)
+/* boardlevel antenna selection: ucode interface control */
+static int brcms_c_antsel_cfgupd(struct antsel_info *asi,
+ struct brcms_antselcfg *antsel)
{
- if (asi->antsel_type == ANTSEL_2x3) {
- u8 antcfg_def = ANT_SELCFG_DEF_2x3 |
- ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0);
- antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def;
- antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def;
- antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def;
- antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def;
- antsel->num_antcfg = ANT_SELCFG_NUM_2x3;
+ struct brcms_c_info *wlc = asi->wlc;
+ u8 ant_cfg;
+ u16 mimo_antsel;
- } else if (asi->antsel_type == ANTSEL_2x4) {
+ /* 1) Update TX antconfig for all frames that are not unicast data
+ * (aka default TX)
+ */
+ ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];
+ mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);
+ brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);
+ /*
+ * Update driver stats for currently selected
+ * default tx/rx antenna config
+ */
+ asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;
- antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4;
- antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4;
- antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4;
- antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4;
- antsel->num_antcfg = ANT_SELCFG_NUM_2x4;
+ /* 2) Update RX antconfig for all frames that are not unicast data
+ * (aka default RX)
+ */
+ ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];
+ mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);
+ brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);
+ /*
+ * Update driver stats for currently selected
+ * default tx/rx antenna config
+ */
+ asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;
- } else { /* no antenna selection available */
+ return 0;
+}
- antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2;
- antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2;
- antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2;
- antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2;
- antsel->num_antcfg = 0;
+void brcms_c_antsel_init(struct antsel_info *asi)
+{
+ if ((asi->antsel_type == ANTSEL_2x3) ||
+ (asi->antsel_type == ANTSEL_2x4))
+ brcms_c_antsel_cfgupd(asi, &asi->antcfg_11n);
+}
+
+/* boardlevel antenna selection: convert id to ant_cfg */
+static u8 brcms_c_antsel_id2antcfg(struct antsel_info *asi, u8 id)
+{
+ u8 antcfg = ANT_SELCFG_DEF_2x2;
+
+ if (asi->antsel_type == ANTSEL_2x4) {
+ /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
+ antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2));
+ return antcfg;
+
+ } else if (asi->antsel_type == ANTSEL_2x3) {
+ /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
+ antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1));
+ return antcfg;
}
+
+ return antcfg;
}
void
-wlc_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
+brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
u8 antselid, u8 fbantselid, u8 *antcfg,
u8 *fbantcfg)
{
@@ -222,8 +276,8 @@ wlc_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
} else {
ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) {
- *antcfg = wlc_antsel_id2antcfg(asi, antselid);
- *fbantcfg = wlc_antsel_id2antcfg(asi, fbantselid);
+ *antcfg = brcms_c_antsel_id2antcfg(asi, antselid);
+ *fbantcfg = brcms_c_antsel_id2antcfg(asi, fbantselid);
} else {
*antcfg =
asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
@@ -234,7 +288,7 @@ wlc_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
}
/* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */
-u8 wlc_antsel_antsel2id(struct antsel_info *asi, u16 antsel)
+u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel)
{
u8 antselid = 0;
@@ -251,70 +305,3 @@ u8 wlc_antsel_antsel2id(struct antsel_info *asi, u16 antsel)
return antselid;
}
-
-/* boardlevel antenna selection: convert id to ant_cfg */
-static u8 wlc_antsel_id2antcfg(struct antsel_info *asi, u8 id)
-{
- u8 antcfg = ANT_SELCFG_DEF_2x2;
-
- if (asi->antsel_type == ANTSEL_2x4) {
- /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
- antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2));
- return antcfg;
-
- } else if (asi->antsel_type == ANTSEL_2x3) {
- /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
- antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1));
- return antcfg;
- }
-
- return antcfg;
-}
-
-/* boardlevel antenna selection: convert ant_cfg to mimo_antsel (ucode interface) */
-static u16 wlc_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg)
-{
- u8 idx = WLC_ANTIDX_11N(WLC_ANTSEL_11N(ant_cfg));
- u16 mimo_antsel = 0;
-
- if (asi->antsel_type == ANTSEL_2x4) {
- /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
- mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf);
- return mimo_antsel;
-
- } else if (asi->antsel_type == ANTSEL_2x3) {
- /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
- mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf);
- return mimo_antsel;
- }
-
- return mimo_antsel;
-}
-
-/* boardlevel antenna selection: ucode interface control */
-static int wlc_antsel_cfgupd(struct antsel_info *asi, wlc_antselcfg_t *antsel)
-{
- struct wlc_info *wlc = asi->wlc;
- u8 ant_cfg;
- u16 mimo_antsel;
-
- /* 1) Update TX antconfig for all frames that are not unicast data
- * (aka default TX)
- */
- ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];
- mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg);
- wlc_write_shm(wlc, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);
- /* Update driver stats for currently selected default tx/rx antenna config */
- asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;
-
- /* 2) Update RX antconfig for all frames that are not unicast data
- * (aka default RX)
- */
- ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];
- mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg);
- wlc_write_shm(wlc, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);
- /* Update driver stats for currently selected default tx/rx antenna config */
- asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;
-
- return 0;
-}
diff --git a/drivers/staging/brcm80211/brcmsmac/wlc_antsel.h b/drivers/net/wireless/brcm80211/brcmsmac/antsel.h
index 2470c73fc4ed..97ea3881a8ec 100644
--- a/drivers/staging/brcm80211/brcmsmac/wlc_antsel.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/antsel.h
@@ -14,16 +14,16 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _wlc_antsel_h_
-#define _wlc_antsel_h_
+#ifndef _BRCM_ANTSEL_H_
+#define _BRCM_ANTSEL_H_
-extern struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc);
-extern void wlc_antsel_detach(struct antsel_info *asi);
-extern void wlc_antsel_init(struct antsel_info *asi);
-extern void wlc_antsel_antcfg_get(struct antsel_info *asi, bool usedef,
+extern struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc);
+extern void brcms_c_antsel_detach(struct antsel_info *asi);
+extern void brcms_c_antsel_init(struct antsel_info *asi);
+extern void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef,
bool sel,
u8 id, u8 fbid, u8 *antcfg,
u8 *fbantcfg);
-extern u8 wlc_antsel_antsel2id(struct antsel_info *asi, u16 antsel);
+extern u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel);
-#endif /* _wlc_antsel_h_ */
+#endif /* _BRCM_ANTSEL_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/wlc_alloc.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c
index 95f951eb2b2f..52fc9eeb5fa5 100644
--- a/drivers/staging/brcm80211/brcmsmac/wlc_alloc.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Broadcom Corporation
+ * Copyright (c) 2011 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,5 +14,10 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-extern struct wlc_info *wlc_attach_malloc(uint unit, uint *err, uint devid);
-extern void wlc_detach_mfree(struct wlc_info *wlc);
+#include <linux/module.h> /* bug in tracepoint.h, it should include this */
+
+#ifndef __CHECKER__
+#include "mac80211_if.h"
+#define CREATE_TRACE_POINTS
+#include "brcms_trace_events.h"
+#endif
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h
new file mode 100644
index 000000000000..27dd73eef56d
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM brcmsmac
+
+#if !defined(__TRACE_BRCMSMAC_H) || defined(TRACE_HEADER_MULTI_READ)
+
+#define __TRACE_BRCMSMAC_H
+
+#include <linux/tracepoint.h>
+#include "mac80211_if.h"
+
+#ifndef CONFIG_BRCMDBG
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif
+
+/*
+ * We define a tracepoint, its arguments, its printk format and its
+ * 'fast binary record' layout.
+ */
+TRACE_EVENT(brcms_timer,
+ /* TPPROTO is the prototype of the function called by this tracepoint */
+ TP_PROTO(struct brcms_timer *t),
+ /*
+ * TPARGS(firstarg, p) are the parameters names, same as found in the
+ * prototype.
+ */
+ TP_ARGS(t),
+ /*
+ * Fast binary tracing: define the trace record via TP_STRUCT__entry().
+ * You can think about it like a regular C structure local variable
+ * definition.
+ */
+ TP_STRUCT__entry(
+ __field(uint, ms)
+ __field(uint, set)
+ __field(uint, periodic)
+ ),
+ TP_fast_assign(
+ __entry->ms = t->ms;
+ __entry->set = t->set;
+ __entry->periodic = t->periodic;
+ ),
+ TP_printk(
+ "ms=%u set=%u periodic=%u",
+ __entry->ms, __entry->set, __entry->periodic
+ )
+);
+
+TRACE_EVENT(brcms_dpc,
+ TP_PROTO(unsigned long data),
+ TP_ARGS(data),
+ TP_STRUCT__entry(
+ __field(unsigned long, data)
+ ),
+ TP_fast_assign(
+ __entry->data = data;
+ ),
+ TP_printk(
+ "data=%p",
+ (void *)__entry->data
+ )
+);
+
+#endif /* __TRACE_BRCMSMAC_H */
+
+#ifdef CONFIG_BRCMDBG
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE brcms_trace_events
+
+#include <trace/define_trace.h>
+
+#endif /* CONFIG_BRCMDBG */
diff --git a/drivers/staging/brcm80211/brcmsmac/wlc_channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
index a3a2bf9b4f12..89ad1b7dab8f 100644
--- a/drivers/staging/brcm80211/brcmsmac/wlc_channel.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
@@ -14,109 +14,173 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/kernel.h>
#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-
-#include <bcmdefs.h>
-#include <bcmutils.h>
-#include <bcmnvram.h>
-#include <aiutils.h>
-#include <sbhnddma.h>
-#include <wlioctl.h>
-
-#include "wlc_types.h"
-#include "d11.h"
-#include "wlc_cfg.h"
-#include "wlc_scb.h"
-#include "wlc_pub.h"
-#include "wlc_key.h"
-#include "phy/wlc_phy_hal.h"
-#include "wlc_bmac.h"
-#include "wlc_rate.h"
-#include "wlc_channel.h"
-#include "wlc_main.h"
-#include "wlc_stf.h"
-#include "wl_dbg.h"
-
-#define VALID_CHANNEL20_DB(wlc, val) wlc_valid_channel20_db((wlc)->cmi, val)
-#define VALID_CHANNEL20_IN_BAND(wlc, bandunit, val) \
- wlc_valid_channel20_in_band((wlc)->cmi, bandunit, val)
-#define VALID_CHANNEL20(wlc, val) wlc_valid_channel20((wlc)->cmi, val)
-
-typedef struct wlc_cm_band {
- u8 locale_flags; /* locale_info_t flags */
- chanvec_t valid_channels; /* List of valid channels in the country */
- const chanvec_t *restricted_channels; /* List of restricted use channels */
- const chanvec_t *radar_channels; /* List of radar sensitive channels */
+#include <net/mac80211.h>
+
+#include <defs.h>
+#include "pub.h"
+#include "phy/phy_hal.h"
+#include "main.h"
+#include "stf.h"
+#include "channel.h"
+
+/* QDB() macro takes a dB value and converts to a quarter dB value */
+#define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR)
+
+#define LOCALE_CHAN_01_11 (1<<0)
+#define LOCALE_CHAN_12_13 (1<<1)
+#define LOCALE_CHAN_14 (1<<2)
+#define LOCALE_SET_5G_LOW_JP1 (1<<3) /* 34-48, step 2 */
+#define LOCALE_SET_5G_LOW_JP2 (1<<4) /* 34-46, step 4 */
+#define LOCALE_SET_5G_LOW1 (1<<5) /* 36-48, step 4 */
+#define LOCALE_SET_5G_LOW2 (1<<6) /* 52 */
+#define LOCALE_SET_5G_LOW3 (1<<7) /* 56-64, step 4 */
+#define LOCALE_SET_5G_MID1 (1<<8) /* 100-116, step 4 */
+#define LOCALE_SET_5G_MID2 (1<<9) /* 120-124, step 4 */
+#define LOCALE_SET_5G_MID3 (1<<10) /* 128 */
+#define LOCALE_SET_5G_HIGH1 (1<<11) /* 132-140, step 4 */
+#define LOCALE_SET_5G_HIGH2 (1<<12) /* 149-161, step 4 */
+#define LOCALE_SET_5G_HIGH3 (1<<13) /* 165 */
+#define LOCALE_CHAN_52_140_ALL (1<<14)
+#define LOCALE_SET_5G_HIGH4 (1<<15) /* 184-216 */
+
+#define LOCALE_CHAN_36_64 (LOCALE_SET_5G_LOW1 | \
+ LOCALE_SET_5G_LOW2 | \
+ LOCALE_SET_5G_LOW3)
+#define LOCALE_CHAN_52_64 (LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3)
+#define LOCALE_CHAN_100_124 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2)
+#define LOCALE_CHAN_100_140 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2 | \
+ LOCALE_SET_5G_MID3 | LOCALE_SET_5G_HIGH1)
+#define LOCALE_CHAN_149_165 (LOCALE_SET_5G_HIGH2 | LOCALE_SET_5G_HIGH3)
+#define LOCALE_CHAN_184_216 LOCALE_SET_5G_HIGH4
+
+#define LOCALE_CHAN_01_14 (LOCALE_CHAN_01_11 | \
+ LOCALE_CHAN_12_13 | \
+ LOCALE_CHAN_14)
+
+#define LOCALE_RADAR_SET_NONE 0
+#define LOCALE_RADAR_SET_1 1
+
+#define LOCALE_RESTRICTED_NONE 0
+#define LOCALE_RESTRICTED_SET_2G_SHORT 1
+#define LOCALE_RESTRICTED_CHAN_165 2
+#define LOCALE_CHAN_ALL_5G 3
+#define LOCALE_RESTRICTED_JAPAN_LEGACY 4
+#define LOCALE_RESTRICTED_11D_2G 5
+#define LOCALE_RESTRICTED_11D_5G 6
+#define LOCALE_RESTRICTED_LOW_HI 7
+#define LOCALE_RESTRICTED_12_13_14 8
+
+#define LOCALE_2G_IDX_i 0
+#define LOCALE_5G_IDX_11 0
+#define LOCALE_MIMO_IDX_bn 0
+#define LOCALE_MIMO_IDX_11n 0
+
+/* max of BAND_5G_PWR_LVLS and 6 for 2.4 GHz */
+#define BRCMS_MAXPWR_TBL_SIZE 6
+/* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */
+#define BRCMS_MAXPWR_MIMO_TBL_SIZE 14
+
+/* power level in group of 2.4GHz band channels:
+ * maxpwr[0] - CCK channels [1]
+ * maxpwr[1] - CCK channels [2-10]
+ * maxpwr[2] - CCK channels [11-14]
+ * maxpwr[3] - OFDM channels [1]
+ * maxpwr[4] - OFDM channels [2-10]
+ * maxpwr[5] - OFDM channels [11-14]
+ */
+
+/* maxpwr mapping to 5GHz band channels:
+ * maxpwr[0] - channels [34-48]
+ * maxpwr[1] - channels [52-60]
+ * maxpwr[2] - channels [62-64]
+ * maxpwr[3] - channels [100-140]
+ * maxpwr[4] - channels [149-165]
+ */
+#define BAND_5G_PWR_LVLS 5 /* 5 power levels for 5G */
+
+#define LC(id) LOCALE_MIMO_IDX_ ## id
+
+#define LC_2G(id) LOCALE_2G_IDX_ ## id
+
+#define LC_5G(id) LOCALE_5G_IDX_ ## id
+
+#define LOCALES(band2, band5, mimo2, mimo5) \
+ {LC_2G(band2), LC_5G(band5), LC(mimo2), LC(mimo5)}
+
+/* macro to get 2.4 GHz channel group index for tx power */
+#define CHANNEL_POWER_IDX_2G_CCK(c) (((c) < 2) ? 0 : (((c) < 11) ? 1 : 2))
+#define CHANNEL_POWER_IDX_2G_OFDM(c) (((c) < 2) ? 3 : (((c) < 11) ? 4 : 5))
+
+/* macro to get 5 GHz channel group index for tx power */
+#define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \
+ (((c) < 62) ? 1 : \
+ (((c) < 100) ? 2 : \
+ (((c) < 149) ? 3 : 4))))
+
+#define ISDFS_EU(fl) (((fl) & BRCMS_DFS_EU) == BRCMS_DFS_EU)
+
+struct brcms_cm_band {
+ /* struct locale_info flags */
+ u8 locale_flags;
+ /* List of valid channels in the country */
+ struct brcms_chanvec valid_channels;
+ /* List of restricted use channels */
+ const struct brcms_chanvec *restricted_channels;
+ /* List of radar sensitive channels */
+ const struct brcms_chanvec *radar_channels;
u8 PAD[8];
-} wlc_cm_band_t;
+};
+
+ /* locale per-channel tx power limits for MIMO frames
+ * maxpwr arrays are index by channel for 2.4 GHz limits, and
+ * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel)
+ */
+struct locale_mimo_info {
+ /* tx 20 MHz power limits, qdBm units */
+ s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE];
+ /* tx 40 MHz power limits, qdBm units */
+ s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE];
+ u8 flags;
+};
-struct wlc_cm_info {
- struct wlc_pub *pub;
- struct wlc_info *wlc;
- char srom_ccode[WLC_CNTRY_BUF_SZ]; /* Country Code in SROM */
+/* Country names and abbreviations with locale defined from ISO 3166 */
+struct country_info {
+ const u8 locale_2G; /* 2.4G band locale */
+ const u8 locale_5G; /* 5G band locale */
+ const u8 locale_mimo_2G; /* 2.4G mimo info */
+ const u8 locale_mimo_5G; /* 5G mimo info */
+};
+
+struct brcms_cm_info {
+ struct brcms_pub *pub;
+ struct brcms_c_info *wlc;
+ char srom_ccode[BRCM_CNTRY_BUF_SZ]; /* Country Code in SROM */
uint srom_regrev; /* Regulatory Rev for the SROM ccode */
- const country_info_t *country; /* current country def */
- char ccode[WLC_CNTRY_BUF_SZ]; /* current internal Country Code */
+ const struct country_info *country; /* current country def */
+ char ccode[BRCM_CNTRY_BUF_SZ]; /* current internal Country Code */
uint regrev; /* current Regulatory Revision */
- char country_abbrev[WLC_CNTRY_BUF_SZ]; /* current advertised ccode */
- wlc_cm_band_t bandstate[MAXBANDS]; /* per-band state (one per phy/radio) */
+ char country_abbrev[BRCM_CNTRY_BUF_SZ]; /* current advertised ccode */
+ /* per-band state (one per phy/radio) */
+ struct brcms_cm_band bandstate[MAXBANDS];
/* quiet channels currently for radar sensitivity or 11h support */
- chanvec_t quiet_channels; /* channels on which we cannot transmit */
+ /* channels on which we cannot transmit */
+ struct brcms_chanvec quiet_channels;
};
-static int wlc_channels_init(wlc_cm_info_t *wlc_cm,
- const country_info_t *country);
-static void wlc_set_country_common(wlc_cm_info_t *wlc_cm,
- const char *country_abbrev,
- const char *ccode, uint regrev,
- const country_info_t *country);
-static int wlc_set_countrycode(wlc_cm_info_t *wlc_cm, const char *ccode);
-static int wlc_set_countrycode_rev(wlc_cm_info_t *wlc_cm,
- const char *country_abbrev,
- const char *ccode, int regrev);
-static int wlc_country_aggregate_map(wlc_cm_info_t *wlc_cm, const char *ccode,
- char *mapped_ccode, uint *mapped_regrev);
-static const country_info_t *wlc_country_lookup_direct(const char *ccode,
- uint regrev);
-static const country_info_t *wlc_countrycode_map(wlc_cm_info_t *wlc_cm,
- const char *ccode,
- char *mapped_ccode,
- uint *mapped_regrev);
-static void wlc_channels_commit(wlc_cm_info_t *wlc_cm);
-static void wlc_quiet_channels_reset(wlc_cm_info_t *wlc_cm);
-static bool wlc_quiet_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chspec);
-static bool wlc_valid_channel20_db(wlc_cm_info_t *wlc_cm, uint val);
-static bool wlc_valid_channel20_in_band(wlc_cm_info_t *wlc_cm, uint bandunit,
- uint val);
-static bool wlc_valid_channel20(wlc_cm_info_t *wlc_cm, uint val);
-static const country_info_t *wlc_country_lookup(struct wlc_info *wlc,
- const char *ccode);
-static void wlc_locale_get_channels(const locale_info_t *locale,
- chanvec_t *valid_channels);
-static const locale_info_t *wlc_get_locale_2g(u8 locale_idx);
-static const locale_info_t *wlc_get_locale_5g(u8 locale_idx);
-static bool wlc_japan(struct wlc_info *wlc);
-static bool wlc_japan_ccode(const char *ccode);
-static void wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm_info_t *
- wlc_cm,
- struct
- txpwr_limits
- *txpwr,
- u8
- local_constraint_qdbm);
-static void wlc_locale_add_channels(chanvec_t *target,
- const chanvec_t *channels);
-static const locale_mimo_info_t *wlc_get_mimo_2g(u8 locale_idx);
-static const locale_mimo_info_t *wlc_get_mimo_5g(u8 locale_idx);
-
-/* QDB() macro takes a dB value and converts to a quarter dB value */
-#ifdef QDB
-#undef QDB
-#endif
-#define QDB(n) ((n) * WLC_TXPWR_DB_FACTOR)
+/* locale channel and power info. */
+struct locale_info {
+ u32 valid_channels;
+ /* List of radar sensitive channels */
+ u8 radar_channels;
+ /* List of channels used only if APs are detected */
+ u8 restricted_channels;
+ /* Max tx pwr in qdBm for each sub-band */
+ s8 maxpwr[BRCMS_MAXPWR_TBL_SIZE];
+ /* Country IE advertised max tx pwr in dBm per sub-band */
+ s8 pub_maxpwr[BAND_5G_PWR_LVLS];
+ u8 flags;
+};
/* Regulatory Matrix Spreadsheet (CLM) MIMO v3.7.9 */
@@ -125,7 +189,7 @@ static const locale_mimo_info_t *wlc_get_mimo_5g(u8 locale_idx);
*/
/* No channels */
-static const chanvec_t chanvec_none = {
+static const struct brcms_chanvec chanvec_none = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -133,7 +197,7 @@ static const chanvec_t chanvec_none = {
};
/* All 2.4 GHz HW channels */
-const chanvec_t chanvec_all_2G = {
+static const struct brcms_chanvec chanvec_all_2G = {
{0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -141,7 +205,7 @@ const chanvec_t chanvec_all_2G = {
};
/* All 5 GHz HW channels */
-const chanvec_t chanvec_all_5G = {
+static const struct brcms_chanvec chanvec_all_5G = {
{0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x11, 0x11,
0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11,
0x11, 0x11, 0x20, 0x22, 0x22, 0x00, 0x00, 0x11,
@@ -152,13 +216,11 @@ const chanvec_t chanvec_all_5G = {
* Radar channel sets
*/
-/* No radar */
-#define radar_set_none chanvec_none
-
-static const chanvec_t radar_set1 = { /* Channels 52 - 64, 100 - 140 */
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, /* 52 - 60 */
- 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11, /* 64, 100 - 124 */
- 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128 - 140 */
+/* Channels 52 - 64, 100 - 140 */
+static const struct brcms_chanvec radar_set1 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, /* 52 - 60 */
+ 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11, /* 64, 100 - 124 */
+ 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128 - 140 */
0x00, 0x00, 0x00, 0x00}
};
@@ -166,10 +228,8 @@ static const chanvec_t radar_set1 = { /* Channels 52 - 64, 100 - 140 */
* Restricted channel sets
*/
-#define restricted_set_none chanvec_none
-
/* Channels 34, 38, 42, 46 */
-static const chanvec_t restricted_set_japan_legacy = {
+static const struct brcms_chanvec restricted_set_japan_legacy = {
{0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -177,7 +237,7 @@ static const chanvec_t restricted_set_japan_legacy = {
};
/* Channels 12, 13 */
-static const chanvec_t restricted_set_2g_short = {
+static const struct brcms_chanvec restricted_set_2g_short = {
{0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -185,7 +245,7 @@ static const chanvec_t restricted_set_2g_short = {
};
/* Channel 165 */
-static const chanvec_t restricted_chan_165 = {
+static const struct brcms_chanvec restricted_chan_165 = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
@@ -193,7 +253,7 @@ static const chanvec_t restricted_chan_165 = {
};
/* Channels 36 - 48 & 149 - 165 */
-static const chanvec_t restricted_low_hi = {
+static const struct brcms_chanvec restricted_low_hi = {
{0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x22, 0x22, 0x00, 0x00, 0x00,
@@ -201,61 +261,21 @@ static const chanvec_t restricted_low_hi = {
};
/* Channels 12 - 14 */
-static const chanvec_t restricted_set_12_13_14 = {
+static const struct brcms_chanvec restricted_set_12_13_14 = {
{0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-#define LOCALE_CHAN_01_11 (1<<0)
-#define LOCALE_CHAN_12_13 (1<<1)
-#define LOCALE_CHAN_14 (1<<2)
-#define LOCALE_SET_5G_LOW_JP1 (1<<3) /* 34-48, step 2 */
-#define LOCALE_SET_5G_LOW_JP2 (1<<4) /* 34-46, step 4 */
-#define LOCALE_SET_5G_LOW1 (1<<5) /* 36-48, step 4 */
-#define LOCALE_SET_5G_LOW2 (1<<6) /* 52 */
-#define LOCALE_SET_5G_LOW3 (1<<7) /* 56-64, step 4 */
-#define LOCALE_SET_5G_MID1 (1<<8) /* 100-116, step 4 */
-#define LOCALE_SET_5G_MID2 (1<<9) /* 120-124, step 4 */
-#define LOCALE_SET_5G_MID3 (1<<10) /* 128 */
-#define LOCALE_SET_5G_HIGH1 (1<<11) /* 132-140, step 4 */
-#define LOCALE_SET_5G_HIGH2 (1<<12) /* 149-161, step 4 */
-#define LOCALE_SET_5G_HIGH3 (1<<13) /* 165 */
-#define LOCALE_CHAN_52_140_ALL (1<<14)
-#define LOCALE_SET_5G_HIGH4 (1<<15) /* 184-216 */
-
-#define LOCALE_CHAN_36_64 (LOCALE_SET_5G_LOW1 | LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3)
-#define LOCALE_CHAN_52_64 (LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3)
-#define LOCALE_CHAN_100_124 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2)
-#define LOCALE_CHAN_100_140 \
- (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2 | LOCALE_SET_5G_MID3 | LOCALE_SET_5G_HIGH1)
-#define LOCALE_CHAN_149_165 (LOCALE_SET_5G_HIGH2 | LOCALE_SET_5G_HIGH3)
-#define LOCALE_CHAN_184_216 LOCALE_SET_5G_HIGH4
-
-#define LOCALE_CHAN_01_14 (LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13 | LOCALE_CHAN_14)
-
-#define LOCALE_RADAR_SET_NONE 0
-#define LOCALE_RADAR_SET_1 1
-
-#define LOCALE_RESTRICTED_NONE 0
-#define LOCALE_RESTRICTED_SET_2G_SHORT 1
-#define LOCALE_RESTRICTED_CHAN_165 2
-#define LOCALE_CHAN_ALL_5G 3
-#define LOCALE_RESTRICTED_JAPAN_LEGACY 4
-#define LOCALE_RESTRICTED_11D_2G 5
-#define LOCALE_RESTRICTED_11D_5G 6
-#define LOCALE_RESTRICTED_LOW_HI 7
-#define LOCALE_RESTRICTED_12_13_14 8
-
/* global memory to provide working buffer for expanded locale */
-static const chanvec_t *g_table_radar_set[] = {
+static const struct brcms_chanvec *g_table_radar_set[] = {
&chanvec_none,
&radar_set1
};
-static const chanvec_t *g_table_restricted_chan[] = {
+static const struct brcms_chanvec *g_table_restricted_chan[] = {
&chanvec_none, /* restricted_set_none */
&restricted_set_2g_short,
&restricted_chan_165,
@@ -267,119 +287,119 @@ static const chanvec_t *g_table_restricted_chan[] = {
&restricted_set_12_13_14
};
-static const chanvec_t locale_2g_01_11 = {
+static const struct brcms_chanvec locale_2g_01_11 = {
{0xfe, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_2g_12_13 = {
+static const struct brcms_chanvec locale_2g_12_13 = {
{0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_2g_14 = {
+static const struct brcms_chanvec locale_2g_14 = {
{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_LOW_JP1 = {
+static const struct brcms_chanvec locale_5g_LOW_JP1 = {
{0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_LOW_JP2 = {
+static const struct brcms_chanvec locale_5g_LOW_JP2 = {
{0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_LOW1 = {
+static const struct brcms_chanvec locale_5g_LOW1 = {
{0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_LOW2 = {
+static const struct brcms_chanvec locale_5g_LOW2 = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_LOW3 = {
+static const struct brcms_chanvec locale_5g_LOW3 = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_MID1 = {
+static const struct brcms_chanvec locale_5g_MID1 = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_MID2 = {
+static const struct brcms_chanvec locale_5g_MID2 = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_MID3 = {
+static const struct brcms_chanvec locale_5g_MID3 = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_HIGH1 = {
+static const struct brcms_chanvec locale_5g_HIGH1 = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_HIGH2 = {
+static const struct brcms_chanvec locale_5g_HIGH2 = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x22, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_HIGH3 = {
+static const struct brcms_chanvec locale_5g_HIGH3 = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_52_140_ALL = {
+static const struct brcms_chanvec locale_5g_52_140_ALL = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
};
-static const chanvec_t locale_5g_HIGH4 = {
+static const struct brcms_chanvec locale_5g_HIGH4 = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
0x11, 0x11, 0x11, 0x11}
};
-static const chanvec_t *g_table_locale_base[] = {
+static const struct brcms_chanvec *g_table_locale_base[] = {
&locale_2g_01_11,
&locale_2g_12_13,
&locale_2g_14,
@@ -398,70 +418,66 @@ static const chanvec_t *g_table_locale_base[] = {
&locale_5g_HIGH4
};
-static void wlc_locale_add_channels(chanvec_t *target,
- const chanvec_t *channels)
+static void brcms_c_locale_add_channels(struct brcms_chanvec *target,
+ const struct brcms_chanvec *channels)
{
u8 i;
- for (i = 0; i < sizeof(chanvec_t); i++) {
+ for (i = 0; i < sizeof(struct brcms_chanvec); i++)
target->vec[i] |= channels->vec[i];
- }
}
-static void wlc_locale_get_channels(const locale_info_t *locale,
- chanvec_t *channels)
+static void brcms_c_locale_get_channels(const struct locale_info *locale,
+ struct brcms_chanvec *channels)
{
u8 i;
- memset(channels, 0, sizeof(chanvec_t));
+ memset(channels, 0, sizeof(struct brcms_chanvec));
for (i = 0; i < ARRAY_SIZE(g_table_locale_base); i++) {
- if (locale->valid_channels & (1 << i)) {
- wlc_locale_add_channels(channels,
+ if (locale->valid_channels & (1 << i))
+ brcms_c_locale_add_channels(channels,
g_table_locale_base[i]);
- }
}
}
/*
* Locale Definitions - 2.4 GHz
*/
-static const locale_info_t locale_i = { /* locale i. channel 1 - 13 */
+static const struct locale_info locale_i = { /* locale i. channel 1 - 13 */
LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13,
LOCALE_RADAR_SET_NONE,
LOCALE_RESTRICTED_SET_2G_SHORT,
{QDB(19), QDB(19), QDB(19),
QDB(19), QDB(19), QDB(19)},
{20, 20, 20, 0},
- WLC_EIRP
+ BRCMS_EIRP
};
/*
* Locale Definitions - 5 GHz
*/
-static const locale_info_t locale_11 = {
+static const struct locale_info locale_11 = {
/* locale 11. channel 36 - 48, 52 - 64, 100 - 140, 149 - 165 */
LOCALE_CHAN_36_64 | LOCALE_CHAN_100_140 | LOCALE_CHAN_149_165,
LOCALE_RADAR_SET_1,
LOCALE_RESTRICTED_NONE,
{QDB(21), QDB(21), QDB(21), QDB(21), QDB(21)},
{23, 23, 23, 30, 30},
- WLC_EIRP | WLC_DFS_EU
+ BRCMS_EIRP | BRCMS_DFS_EU
};
-#define LOCALE_2G_IDX_i 0
-static const locale_info_t *g_locale_2g_table[] = {
+static const struct locale_info *g_locale_2g_table[] = {
&locale_i
};
-#define LOCALE_5G_IDX_11 0
-static const locale_info_t *g_locale_5g_table[] = {
+static const struct locale_info *g_locale_5g_table[] = {
&locale_11
};
/*
* MIMO Locale Definitions - 2.4 GHz
*/
-static const locale_mimo_info_t locale_bn = {
+static const struct locale_mimo_info locale_bn = {
{QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
QDB(13), QDB(13), QDB(13)},
@@ -471,47 +487,26 @@ static const locale_mimo_info_t locale_bn = {
0
};
-/* locale mimo 2g indexes */
-#define LOCALE_MIMO_IDX_bn 0
-
-static const locale_mimo_info_t *g_mimo_2g_table[] = {
+static const struct locale_mimo_info *g_mimo_2g_table[] = {
&locale_bn
};
/*
* MIMO Locale Definitions - 5 GHz
*/
-static const locale_mimo_info_t locale_11n = {
+static const struct locale_mimo_info locale_11n = {
{ /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)},
{QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)},
0
};
-#define LOCALE_MIMO_IDX_11n 0
-static const locale_mimo_info_t *g_mimo_5g_table[] = {
+static const struct locale_mimo_info *g_mimo_5g_table[] = {
&locale_11n
};
-#ifdef LC
-#undef LC
-#endif
-#define LC(id) LOCALE_MIMO_IDX_ ## id
-
-#ifdef LC_2G
-#undef LC_2G
-#endif
-#define LC_2G(id) LOCALE_2G_IDX_ ## id
-
-#ifdef LC_5G
-#undef LC_5G
-#endif
-#define LC_5G(id) LOCALE_5G_IDX_ ## id
-
-#define LOCALES(band2, band5, mimo2, mimo5) {LC_2G(band2), LC_5G(band5), LC(mimo2), LC(mimo5)}
-
static const struct {
- char abbrev[WLC_CNTRY_BUF_SZ]; /* country abbreviation */
- country_info_t country;
+ char abbrev[BRCM_CNTRY_BUF_SZ]; /* country abbreviation */
+ struct country_info country;
} cntry_locales[] = {
{
"X2", LOCALES(i, 11, bn, 11n)}, /* Worldwide RoW 2 */
@@ -594,222 +589,91 @@ struct chan20_info chan20_info[] = {
};
#endif /* SUPPORT_40MHZ */
-static const locale_info_t *wlc_get_locale_2g(u8 locale_idx)
+static const struct locale_info *brcms_c_get_locale_2g(u8 locale_idx)
{
- if (locale_idx >= ARRAY_SIZE(g_locale_2g_table)) {
+ if (locale_idx >= ARRAY_SIZE(g_locale_2g_table))
return NULL; /* error condition */
- }
+
return g_locale_2g_table[locale_idx];
}
-static const locale_info_t *wlc_get_locale_5g(u8 locale_idx)
+static const struct locale_info *brcms_c_get_locale_5g(u8 locale_idx)
{
- if (locale_idx >= ARRAY_SIZE(g_locale_5g_table)) {
+ if (locale_idx >= ARRAY_SIZE(g_locale_5g_table))
return NULL; /* error condition */
- }
+
return g_locale_5g_table[locale_idx];
}
-static const locale_mimo_info_t *wlc_get_mimo_2g(u8 locale_idx)
+static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx)
{
- if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table)) {
+ if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table))
return NULL;
- }
- return g_mimo_2g_table[locale_idx];
-}
-static const locale_mimo_info_t *wlc_get_mimo_5g(u8 locale_idx)
-{
- if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table)) {
- return NULL;
- }
- return g_mimo_5g_table[locale_idx];
+ return g_mimo_2g_table[locale_idx];
}
-wlc_cm_info_t *wlc_channel_mgr_attach(struct wlc_info *wlc)
+static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx)
{
- wlc_cm_info_t *wlc_cm;
- char country_abbrev[WLC_CNTRY_BUF_SZ];
- const country_info_t *country;
- struct wlc_pub *pub = wlc->pub;
- char *ccode;
-
- BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
-
- wlc_cm = kzalloc(sizeof(wlc_cm_info_t), GFP_ATOMIC);
- if (wlc_cm == NULL) {
- wiphy_err(wlc->wiphy, "wl%d: %s: out of memory", pub->unit,
- __func__);
+ if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table))
return NULL;
- }
- wlc_cm->pub = pub;
- wlc_cm->wlc = wlc;
- wlc->cmi = wlc_cm;
-
- /* store the country code for passing up as a regulatory hint */
- ccode = getvar(wlc->pub->vars, "ccode");
- if (ccode) {
- strncpy(wlc->pub->srom_ccode, ccode, WLC_CNTRY_BUF_SZ - 1);
- }
-
- /* internal country information which must match regulatory constraints in firmware */
- memset(country_abbrev, 0, WLC_CNTRY_BUF_SZ);
- strncpy(country_abbrev, "X2", sizeof(country_abbrev) - 1);
- country = wlc_country_lookup(wlc, country_abbrev);
-
- /* save default country for exiting 11d regulatory mode */
- strncpy(wlc->country_default, country_abbrev, WLC_CNTRY_BUF_SZ - 1);
- /* initialize autocountry_default to driver default */
- strncpy(wlc->autocountry_default, "X2", WLC_CNTRY_BUF_SZ - 1);
-
- wlc_set_countrycode(wlc_cm, country_abbrev);
-
- return wlc_cm;
-}
-
-void wlc_channel_mgr_detach(wlc_cm_info_t *wlc_cm)
-{
- kfree(wlc_cm);
-}
-
-u8 wlc_channel_locale_flags_in_band(wlc_cm_info_t *wlc_cm, uint bandunit)
-{
- return wlc_cm->bandstate[bandunit].locale_flags;
-}
-
-/* set the driver's current country and regulatory information using a country code
- * as the source. Lookup built in country information found with the country code.
- */
-static int wlc_set_countrycode(wlc_cm_info_t *wlc_cm, const char *ccode)
-{
- char country_abbrev[WLC_CNTRY_BUF_SZ];
- strncpy(country_abbrev, ccode, WLC_CNTRY_BUF_SZ);
- return wlc_set_countrycode_rev(wlc_cm, country_abbrev, ccode, -1);
+ return g_mimo_5g_table[locale_idx];
}
static int
-wlc_set_countrycode_rev(wlc_cm_info_t *wlc_cm,
- const char *country_abbrev,
- const char *ccode, int regrev)
+brcms_c_country_aggregate_map(struct brcms_cm_info *wlc_cm, const char *ccode,
+ char *mapped_ccode, uint *mapped_regrev)
{
- const country_info_t *country;
- char mapped_ccode[WLC_CNTRY_BUF_SZ];
- uint mapped_regrev;
-
- /* if regrev is -1, lookup the mapped country code,
- * otherwise use the ccode and regrev directly
- */
- if (regrev == -1) {
- /* map the country code to a built-in country code, regrev, and country_info */
- country =
- wlc_countrycode_map(wlc_cm, ccode, mapped_ccode,
- &mapped_regrev);
- } else {
- /* find the matching built-in country definition */
- country = wlc_country_lookup_direct(ccode, regrev);
- strncpy(mapped_ccode, ccode, WLC_CNTRY_BUF_SZ);
- mapped_regrev = regrev;
- }
-
- if (country == NULL)
- return -EINVAL;
-
- /* set the driver state for the country */
- wlc_set_country_common(wlc_cm, country_abbrev, mapped_ccode,
- mapped_regrev, country);
-
- return 0;
+ return false;
}
-/* set the driver's current country and regulatory information using a country code
- * as the source. Look up built in country information found with the country code.
+/* Lookup a country info structure from a null terminated country
+ * abbreviation and regrev directly with no translation.
*/
-static void
-wlc_set_country_common(wlc_cm_info_t *wlc_cm,
- const char *country_abbrev,
- const char *ccode, uint regrev,
- const country_info_t *country)
+static const struct country_info *
+brcms_c_country_lookup_direct(const char *ccode, uint regrev)
{
- const locale_mimo_info_t *li_mimo;
- const locale_info_t *locale;
- struct wlc_info *wlc = wlc_cm->wlc;
- char prev_country_abbrev[WLC_CNTRY_BUF_SZ];
-
- /* save current country state */
- wlc_cm->country = country;
-
- memset(&prev_country_abbrev, 0, WLC_CNTRY_BUF_SZ);
- strncpy(prev_country_abbrev, wlc_cm->country_abbrev,
- WLC_CNTRY_BUF_SZ - 1);
+ uint size, i;
- strncpy(wlc_cm->country_abbrev, country_abbrev, WLC_CNTRY_BUF_SZ - 1);
- strncpy(wlc_cm->ccode, ccode, WLC_CNTRY_BUF_SZ - 1);
- wlc_cm->regrev = regrev;
+ /* Should just return 0 for single locale driver. */
+ /* Keep it this way in case we add more locales. (for now anyway) */
- /* disable/restore nmode based on country regulations */
- li_mimo = wlc_get_mimo_2g(country->locale_mimo_2G);
- if (li_mimo && (li_mimo->flags & WLC_NO_MIMO)) {
- wlc_set_nmode(wlc, OFF);
- wlc->stf->no_cddstbc = true;
- } else {
- wlc->stf->no_cddstbc = false;
- if (N_ENAB(wlc->pub) != wlc->protection->nmode_user)
- wlc_set_nmode(wlc, wlc->protection->nmode_user);
- }
+ /*
+ * all other country def arrays are for regrev == 0, so if
+ * regrev is non-zero, fail
+ */
+ if (regrev > 0)
+ return NULL;
- wlc_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
- wlc_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
- /* set or restore gmode as required by regulatory */
- locale = wlc_get_locale_2g(country->locale_2G);
- if (locale && (locale->flags & WLC_NO_OFDM)) {
- wlc_set_gmode(wlc, GMODE_LEGACY_B, false);
- } else {
- wlc_set_gmode(wlc, wlc->protection->gmode_user, false);
+ /* find matched table entry from country code */
+ size = ARRAY_SIZE(cntry_locales);
+ for (i = 0; i < size; i++) {
+ if (strcmp(ccode, cntry_locales[i].abbrev) == 0)
+ return &cntry_locales[i].country;
}
-
- wlc_channels_init(wlc_cm, country);
-
- return;
-}
-
-/* Lookup a country info structure from a null terminated country code
- * The lookup is case sensitive.
- */
-static const country_info_t *wlc_country_lookup(struct wlc_info *wlc,
- const char *ccode)
-{
- const country_info_t *country;
- char mapped_ccode[WLC_CNTRY_BUF_SZ];
- uint mapped_regrev;
-
- /* map the country code to a built-in country code, regrev, and country_info struct */
- country =
- wlc_countrycode_map(wlc->cmi, ccode, mapped_ccode, &mapped_regrev);
-
- return country;
+ return NULL;
}
-static const country_info_t *wlc_countrycode_map(wlc_cm_info_t *wlc_cm,
- const char *ccode,
- char *mapped_ccode,
- uint *mapped_regrev)
+static const struct country_info *
+brcms_c_countrycode_map(struct brcms_cm_info *wlc_cm, const char *ccode,
+ char *mapped_ccode, uint *mapped_regrev)
{
- struct wlc_info *wlc = wlc_cm->wlc;
- const country_info_t *country;
+ struct brcms_c_info *wlc = wlc_cm->wlc;
+ const struct country_info *country;
uint srom_regrev = wlc_cm->srom_regrev;
const char *srom_ccode = wlc_cm->srom_ccode;
int mapped;
/* check for currently supported ccode size */
- if (strlen(ccode) > (WLC_CNTRY_BUF_SZ - 1)) {
+ if (strlen(ccode) > (BRCM_CNTRY_BUF_SZ - 1)) {
wiphy_err(wlc->wiphy, "wl%d: %s: ccode \"%s\" too long for "
"match\n", wlc->pub->unit, __func__, ccode);
return NULL;
}
/* default mapping is the given ccode and regrev 0 */
- strncpy(mapped_ccode, ccode, WLC_CNTRY_BUF_SZ);
+ strncpy(mapped_ccode, ccode, BRCM_CNTRY_BUF_SZ);
*mapped_regrev = 0;
/* If the desired country code matches the srom country code,
@@ -822,301 +686,179 @@ static const country_info_t *wlc_countrycode_map(wlc_cm_info_t *wlc_cm,
wiphy_err(wlc->wiphy, "srom_code == ccode %s\n", __func__);
} else {
mapped =
- wlc_country_aggregate_map(wlc_cm, ccode, mapped_ccode,
+ brcms_c_country_aggregate_map(wlc_cm, ccode, mapped_ccode,
mapped_regrev);
}
/* find the matching built-in country definition */
- country = wlc_country_lookup_direct(mapped_ccode, *mapped_regrev);
+ country = brcms_c_country_lookup_direct(mapped_ccode, *mapped_regrev);
/* if there is not an exact rev match, default to rev zero */
if (country == NULL && *mapped_regrev != 0) {
*mapped_regrev = 0;
country =
- wlc_country_lookup_direct(mapped_ccode, *mapped_regrev);
+ brcms_c_country_lookup_direct(mapped_ccode, *mapped_regrev);
}
return country;
}
-static int
-wlc_country_aggregate_map(wlc_cm_info_t *wlc_cm, const char *ccode,
- char *mapped_ccode, uint *mapped_regrev)
-{
- return false;
-}
-
-/* Lookup a country info structure from a null terminated country
- * abbreviation and regrev directly with no translation.
- */
-static const country_info_t *wlc_country_lookup_direct(const char *ccode,
- uint regrev)
-{
- uint size, i;
-
- /* Should just return 0 for single locale driver. */
- /* Keep it this way in case we add more locales. (for now anyway) */
-
- /* all other country def arrays are for regrev == 0, so if regrev is non-zero, fail */
- if (regrev > 0)
- return NULL;
-
- /* find matched table entry from country code */
- size = ARRAY_SIZE(cntry_locales);
- for (i = 0; i < size; i++) {
- if (strcmp(ccode, cntry_locales[i].abbrev) == 0) {
- return &cntry_locales[i].country;
- }
- }
- return NULL;
-}
-
-static int
-wlc_channels_init(wlc_cm_info_t *wlc_cm, const country_info_t *country)
-{
- struct wlc_info *wlc = wlc_cm->wlc;
- uint i, j;
- struct wlcband *band;
- const locale_info_t *li;
- chanvec_t sup_chan;
- const locale_mimo_info_t *li_mimo;
-
- band = wlc->band;
- for (i = 0; i < NBANDS(wlc);
- i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) {
-
- li = BAND_5G(band->bandtype) ?
- wlc_get_locale_5g(country->locale_5G) :
- wlc_get_locale_2g(country->locale_2G);
- wlc_cm->bandstate[band->bandunit].locale_flags = li->flags;
- li_mimo = BAND_5G(band->bandtype) ?
- wlc_get_mimo_5g(country->locale_mimo_5G) :
- wlc_get_mimo_2g(country->locale_mimo_2G);
-
- /* merge the mimo non-mimo locale flags */
- wlc_cm->bandstate[band->bandunit].locale_flags |=
- li_mimo->flags;
-
- wlc_cm->bandstate[band->bandunit].restricted_channels =
- g_table_restricted_chan[li->restricted_channels];
- wlc_cm->bandstate[band->bandunit].radar_channels =
- g_table_radar_set[li->radar_channels];
-
- /* set the channel availability,
- * masking out the channels that may not be supported on this phy
- */
- wlc_phy_chanspec_band_validch(band->pi, band->bandtype,
- &sup_chan);
- wlc_locale_get_channels(li,
- &wlc_cm->bandstate[band->bandunit].
- valid_channels);
- for (j = 0; j < sizeof(chanvec_t); j++)
- wlc_cm->bandstate[band->bandunit].valid_channels.
- vec[j] &= sup_chan.vec[j];
- }
-
- wlc_quiet_channels_reset(wlc_cm);
- wlc_channels_commit(wlc_cm);
-
- return 0;
-}
-
-/* Update the radio state (enable/disable) and tx power targets
- * based on a new set of channel/regulatory information
+/* Lookup a country info structure from a null terminated country code
+ * The lookup is case sensitive.
*/
-static void wlc_channels_commit(wlc_cm_info_t *wlc_cm)
+static const struct country_info *
+brcms_c_country_lookup(struct brcms_c_info *wlc, const char *ccode)
{
- struct wlc_info *wlc = wlc_cm->wlc;
- uint chan;
- struct txpwr_limits txpwr;
-
- /* search for the existence of any valid channel */
- for (chan = 0; chan < MAXCHANNEL; chan++) {
- if (VALID_CHANNEL20_DB(wlc, chan)) {
- break;
- }
- }
- if (chan == MAXCHANNEL)
- chan = INVCHANNEL;
-
- /* based on the channel search above, set or clear WL_RADIO_COUNTRY_DISABLE */
- if (chan == INVCHANNEL) {
- /* country/locale with no valid channels, set the radio disable bit */
- mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
- wiphy_err(wlc->wiphy, "wl%d: %s: no valid channel for \"%s\" "
- "nbands %d bandlocked %d\n", wlc->pub->unit,
- __func__, wlc_cm->country_abbrev, NBANDS(wlc),
- wlc->bandlocked);
- } else
- if (mboolisset(wlc->pub->radio_disabled,
- WL_RADIO_COUNTRY_DISABLE)) {
- /* country/locale with valid channel, clear the radio disable bit */
- mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
- }
+ const struct country_info *country;
+ char mapped_ccode[BRCM_CNTRY_BUF_SZ];
+ uint mapped_regrev;
- /* Now that the country abbreviation is set, if the radio supports 2G, then
- * set channel 14 restrictions based on the new locale.
+ /*
+ * map the country code to a built-in country code, regrev, and
+ * country_info struct
*/
- if (NBANDS(wlc) > 1 || BAND_2G(wlc->band->bandtype)) {
- wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi,
- wlc_japan(wlc) ? true :
- false);
- }
+ country = brcms_c_countrycode_map(wlc->cmi, ccode, mapped_ccode,
+ &mapped_regrev);
- if (wlc->pub->up && chan != INVCHANNEL) {
- wlc_channel_reg_limits(wlc_cm, wlc->chanspec, &txpwr);
- wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm,
- &txpwr,
- WLC_TXPWR_MAX);
- wlc_phy_txpower_limit_set(wlc->band->pi, &txpwr, wlc->chanspec);
- }
+ return country;
}
-/* reset the quiet channels vector to the union of the restricted and radar channel sets */
-static void wlc_quiet_channels_reset(wlc_cm_info_t *wlc_cm)
+/*
+ * reset the quiet channels vector to the union
+ * of the restricted and radar channel sets
+ */
+static void brcms_c_quiet_channels_reset(struct brcms_cm_info *wlc_cm)
{
- struct wlc_info *wlc = wlc_cm->wlc;
+ struct brcms_c_info *wlc = wlc_cm->wlc;
uint i, j;
- struct wlcband *band;
- const chanvec_t *chanvec;
+ struct brcms_band *band;
+ const struct brcms_chanvec *chanvec;
- memset(&wlc_cm->quiet_channels, 0, sizeof(chanvec_t));
+ memset(&wlc_cm->quiet_channels, 0, sizeof(struct brcms_chanvec));
band = wlc->band;
- for (i = 0; i < NBANDS(wlc);
+ for (i = 0; i < wlc->pub->_nbands;
i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) {
/* initialize quiet channels for restricted channels */
chanvec = wlc_cm->bandstate[band->bandunit].restricted_channels;
- for (j = 0; j < sizeof(chanvec_t); j++)
+ for (j = 0; j < sizeof(struct brcms_chanvec); j++)
wlc_cm->quiet_channels.vec[j] |= chanvec->vec[j];
}
}
-static bool wlc_quiet_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chspec)
+/* Is the channel valid for the current locale and current band? */
+static bool brcms_c_valid_channel20(struct brcms_cm_info *wlc_cm, uint val)
+{
+ struct brcms_c_info *wlc = wlc_cm->wlc;
+
+ return ((val < MAXCHANNEL) &&
+ isset(wlc_cm->bandstate[wlc->band->bandunit].valid_channels.vec,
+ val));
+}
+
+/* Is the channel valid for the current locale and specified band? */
+static bool brcms_c_valid_channel20_in_band(struct brcms_cm_info *wlc_cm,
+ uint bandunit, uint val)
{
- return N_ENAB(wlc_cm->wlc->pub) && CHSPEC_IS40(chspec) ?
- (isset
- (wlc_cm->quiet_channels.vec,
- LOWER_20_SB(CHSPEC_CHANNEL(chspec)))
- || isset(wlc_cm->quiet_channels.vec,
- UPPER_20_SB(CHSPEC_CHANNEL(chspec)))) : isset(wlc_cm->
- quiet_channels.
- vec,
- CHSPEC_CHANNEL
- (chspec));
+ return ((val < MAXCHANNEL)
+ && isset(wlc_cm->bandstate[bandunit].valid_channels.vec, val));
}
/* Is the channel valid for the current locale? (but don't consider channels not
* available due to bandlocking)
*/
-static bool wlc_valid_channel20_db(wlc_cm_info_t *wlc_cm, uint val)
+static bool brcms_c_valid_channel20_db(struct brcms_cm_info *wlc_cm, uint val)
{
- struct wlc_info *wlc = wlc_cm->wlc;
+ struct brcms_c_info *wlc = wlc_cm->wlc;
- return VALID_CHANNEL20(wlc, val) ||
+ return brcms_c_valid_channel20(wlc->cmi, val) ||
(!wlc->bandlocked
- && VALID_CHANNEL20_IN_BAND(wlc, OTHERBANDUNIT(wlc), val));
+ && brcms_c_valid_channel20_in_band(wlc->cmi,
+ OTHERBANDUNIT(wlc), val));
}
-/* Is the channel valid for the current locale and specified band? */
-static bool
-wlc_valid_channel20_in_band(wlc_cm_info_t *wlc_cm, uint bandunit, uint val)
+/* JP, J1 - J10 are Japan ccodes */
+static bool brcms_c_japan_ccode(const char *ccode)
{
- return ((val < MAXCHANNEL)
- && isset(wlc_cm->bandstate[bandunit].valid_channels.vec, val));
+ return (ccode[0] == 'J' &&
+ (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9')));
}
-/* Is the channel valid for the current locale and current band? */
-static bool wlc_valid_channel20(wlc_cm_info_t *wlc_cm, uint val)
+/* Returns true if currently set country is Japan or variant */
+static bool brcms_c_japan(struct brcms_c_info *wlc)
{
- struct wlc_info *wlc = wlc_cm->wlc;
-
- return ((val < MAXCHANNEL) &&
- isset(wlc_cm->bandstate[wlc->band->bandunit].valid_channels.vec,
- val));
+ return brcms_c_japan_ccode(wlc->cmi->country_abbrev);
}
static void
-wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm_info_t *wlc_cm,
- struct txpwr_limits *txpwr,
- u8
- local_constraint_qdbm)
+brcms_c_channel_min_txpower_limits_with_local_constraint(
+ struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr,
+ u8 local_constraint_qdbm)
{
int j;
/* CCK Rates */
- for (j = 0; j < WL_TX_POWER_CCK_NUM; j++) {
+ for (j = 0; j < WL_TX_POWER_CCK_NUM; j++)
txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm);
- }
/* 20 MHz Legacy OFDM SISO */
- for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++) {
+ for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++)
txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm);
- }
/* 20 MHz Legacy OFDM CDD */
- for (j = 0; j < WLC_NUM_RATES_OFDM; j++) {
+ for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
txpwr->ofdm_cdd[j] =
min(txpwr->ofdm_cdd[j], local_constraint_qdbm);
- }
/* 40 MHz Legacy OFDM SISO */
- for (j = 0; j < WLC_NUM_RATES_OFDM; j++) {
+ for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
txpwr->ofdm_40_siso[j] =
min(txpwr->ofdm_40_siso[j], local_constraint_qdbm);
- }
/* 40 MHz Legacy OFDM CDD */
- for (j = 0; j < WLC_NUM_RATES_OFDM; j++) {
+ for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
txpwr->ofdm_40_cdd[j] =
min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm);
- }
/* 20MHz MCS 0-7 SISO */
- for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
txpwr->mcs_20_siso[j] =
min(txpwr->mcs_20_siso[j], local_constraint_qdbm);
- }
/* 20MHz MCS 0-7 CDD */
- for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
txpwr->mcs_20_cdd[j] =
min(txpwr->mcs_20_cdd[j], local_constraint_qdbm);
- }
/* 20MHz MCS 0-7 STBC */
- for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
txpwr->mcs_20_stbc[j] =
min(txpwr->mcs_20_stbc[j], local_constraint_qdbm);
- }
/* 20MHz MCS 8-15 MIMO */
- for (j = 0; j < WLC_NUM_RATES_MCS_2_STREAM; j++)
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++)
txpwr->mcs_20_mimo[j] =
min(txpwr->mcs_20_mimo[j], local_constraint_qdbm);
/* 40MHz MCS 0-7 SISO */
- for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
txpwr->mcs_40_siso[j] =
min(txpwr->mcs_40_siso[j], local_constraint_qdbm);
- }
/* 40MHz MCS 0-7 CDD */
- for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
txpwr->mcs_40_cdd[j] =
min(txpwr->mcs_40_cdd[j], local_constraint_qdbm);
- }
/* 40MHz MCS 0-7 STBC */
- for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
txpwr->mcs_40_stbc[j] =
min(txpwr->mcs_40_stbc[j], local_constraint_qdbm);
- }
/* 40MHz MCS 8-15 MIMO */
- for (j = 0; j < WLC_NUM_RATES_MCS_2_STREAM; j++)
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++)
txpwr->mcs_40_mimo[j] =
min(txpwr->mcs_40_mimo[j], local_constraint_qdbm);
@@ -1125,163 +867,430 @@ wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm_info_t *wlc_cm,
}
+/* Update the radio state (enable/disable) and tx power targets
+ * based on a new set of channel/regulatory information
+ */
+static void brcms_c_channels_commit(struct brcms_cm_info *wlc_cm)
+{
+ struct brcms_c_info *wlc = wlc_cm->wlc;
+ uint chan;
+ struct txpwr_limits txpwr;
+
+ /* search for the existence of any valid channel */
+ for (chan = 0; chan < MAXCHANNEL; chan++) {
+ if (brcms_c_valid_channel20_db(wlc->cmi, chan))
+ break;
+ }
+ if (chan == MAXCHANNEL)
+ chan = INVCHANNEL;
+
+ /*
+ * based on the channel search above, set or
+ * clear WL_RADIO_COUNTRY_DISABLE.
+ */
+ if (chan == INVCHANNEL) {
+ /*
+ * country/locale with no valid channels, set
+ * the radio disable bit
+ */
+ mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
+ wiphy_err(wlc->wiphy, "wl%d: %s: no valid channel for \"%s\" "
+ "nbands %d bandlocked %d\n", wlc->pub->unit,
+ __func__, wlc_cm->country_abbrev, wlc->pub->_nbands,
+ wlc->bandlocked);
+ } else if (mboolisset(wlc->pub->radio_disabled,
+ WL_RADIO_COUNTRY_DISABLE)) {
+ /*
+ * country/locale with valid channel, clear
+ * the radio disable bit
+ */
+ mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
+ }
+
+ /*
+ * Now that the country abbreviation is set, if the radio supports 2G,
+ * then set channel 14 restrictions based on the new locale.
+ */
+ if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G)
+ wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi,
+ brcms_c_japan(wlc) ? true :
+ false);
+
+ if (wlc->pub->up && chan != INVCHANNEL) {
+ brcms_c_channel_reg_limits(wlc_cm, wlc->chanspec, &txpwr);
+ brcms_c_channel_min_txpower_limits_with_local_constraint(wlc_cm,
+ &txpwr, BRCMS_TXPWR_MAX);
+ wlc_phy_txpower_limit_set(wlc->band->pi, &txpwr, wlc->chanspec);
+ }
+}
+
+static int
+brcms_c_channels_init(struct brcms_cm_info *wlc_cm,
+ const struct country_info *country)
+{
+ struct brcms_c_info *wlc = wlc_cm->wlc;
+ uint i, j;
+ struct brcms_band *band;
+ const struct locale_info *li;
+ struct brcms_chanvec sup_chan;
+ const struct locale_mimo_info *li_mimo;
+
+ band = wlc->band;
+ for (i = 0; i < wlc->pub->_nbands;
+ i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) {
+
+ li = (band->bandtype == BRCM_BAND_5G) ?
+ brcms_c_get_locale_5g(country->locale_5G) :
+ brcms_c_get_locale_2g(country->locale_2G);
+ wlc_cm->bandstate[band->bandunit].locale_flags = li->flags;
+ li_mimo = (band->bandtype == BRCM_BAND_5G) ?
+ brcms_c_get_mimo_5g(country->locale_mimo_5G) :
+ brcms_c_get_mimo_2g(country->locale_mimo_2G);
+
+ /* merge the mimo non-mimo locale flags */
+ wlc_cm->bandstate[band->bandunit].locale_flags |=
+ li_mimo->flags;
+
+ wlc_cm->bandstate[band->bandunit].restricted_channels =
+ g_table_restricted_chan[li->restricted_channels];
+ wlc_cm->bandstate[band->bandunit].radar_channels =
+ g_table_radar_set[li->radar_channels];
+
+ /*
+ * set the channel availability, masking out the channels
+ * that may not be supported on this phy.
+ */
+ wlc_phy_chanspec_band_validch(band->pi, band->bandtype,
+ &sup_chan);
+ brcms_c_locale_get_channels(li,
+ &wlc_cm->bandstate[band->bandunit].
+ valid_channels);
+ for (j = 0; j < sizeof(struct brcms_chanvec); j++)
+ wlc_cm->bandstate[band->bandunit].valid_channels.
+ vec[j] &= sup_chan.vec[j];
+ }
+
+ brcms_c_quiet_channels_reset(wlc_cm);
+ brcms_c_channels_commit(wlc_cm);
+
+ return 0;
+}
+
+/*
+ * set the driver's current country and regulatory information
+ * using a country code as the source. Look up built in country
+ * information found with the country code.
+ */
+static void
+brcms_c_set_country_common(struct brcms_cm_info *wlc_cm,
+ const char *country_abbrev,
+ const char *ccode, uint regrev,
+ const struct country_info *country)
+{
+ const struct locale_info *locale;
+ struct brcms_c_info *wlc = wlc_cm->wlc;
+ char prev_country_abbrev[BRCM_CNTRY_BUF_SZ];
+
+ /* save current country state */
+ wlc_cm->country = country;
+
+ memset(&prev_country_abbrev, 0, BRCM_CNTRY_BUF_SZ);
+ strncpy(prev_country_abbrev, wlc_cm->country_abbrev,
+ BRCM_CNTRY_BUF_SZ - 1);
+
+ strncpy(wlc_cm->country_abbrev, country_abbrev, BRCM_CNTRY_BUF_SZ - 1);
+ strncpy(wlc_cm->ccode, ccode, BRCM_CNTRY_BUF_SZ - 1);
+ wlc_cm->regrev = regrev;
+
+ if ((wlc->pub->_n_enab & SUPPORT_11N) !=
+ wlc->protection->nmode_user)
+ brcms_c_set_nmode(wlc);
+
+ brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
+ brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
+ /* set or restore gmode as required by regulatory */
+ locale = brcms_c_get_locale_2g(country->locale_2G);
+ if (locale && (locale->flags & BRCMS_NO_OFDM))
+ brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false);
+ else
+ brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
+
+ brcms_c_channels_init(wlc_cm, country);
+
+ return;
+}
+
+static int
+brcms_c_set_countrycode_rev(struct brcms_cm_info *wlc_cm,
+ const char *country_abbrev,
+ const char *ccode, int regrev)
+{
+ const struct country_info *country;
+ char mapped_ccode[BRCM_CNTRY_BUF_SZ];
+ uint mapped_regrev;
+
+ /* if regrev is -1, lookup the mapped country code,
+ * otherwise use the ccode and regrev directly
+ */
+ if (regrev == -1) {
+ /*
+ * map the country code to a built-in country
+ * code, regrev, and country_info
+ */
+ country =
+ brcms_c_countrycode_map(wlc_cm, ccode, mapped_ccode,
+ &mapped_regrev);
+ } else {
+ /* find the matching built-in country definition */
+ country = brcms_c_country_lookup_direct(ccode, regrev);
+ strncpy(mapped_ccode, ccode, BRCM_CNTRY_BUF_SZ);
+ mapped_regrev = regrev;
+ }
+
+ if (country == NULL)
+ return -EINVAL;
+
+ /* set the driver state for the country */
+ brcms_c_set_country_common(wlc_cm, country_abbrev, mapped_ccode,
+ mapped_regrev, country);
+
+ return 0;
+}
+
+/*
+ * set the driver's current country and regulatory information using
+ * a country code as the source. Lookup built in country information
+ * found with the country code.
+ */
+static int
+brcms_c_set_countrycode(struct brcms_cm_info *wlc_cm, const char *ccode)
+{
+ char country_abbrev[BRCM_CNTRY_BUF_SZ];
+ strncpy(country_abbrev, ccode, BRCM_CNTRY_BUF_SZ);
+ return brcms_c_set_countrycode_rev(wlc_cm, country_abbrev, ccode, -1);
+}
+
+struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc)
+{
+ struct brcms_cm_info *wlc_cm;
+ char country_abbrev[BRCM_CNTRY_BUF_SZ];
+ const struct country_info *country;
+ struct brcms_pub *pub = wlc->pub;
+ char *ccode;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+ wlc_cm = kzalloc(sizeof(struct brcms_cm_info), GFP_ATOMIC);
+ if (wlc_cm == NULL)
+ return NULL;
+ wlc_cm->pub = pub;
+ wlc_cm->wlc = wlc;
+ wlc->cmi = wlc_cm;
+
+ /* store the country code for passing up as a regulatory hint */
+ ccode = getvar(wlc->hw->sih, BRCMS_SROM_CCODE);
+ if (ccode)
+ strncpy(wlc->pub->srom_ccode, ccode, BRCM_CNTRY_BUF_SZ - 1);
+
+ /*
+ * internal country information which must match
+ * regulatory constraints in firmware
+ */
+ memset(country_abbrev, 0, BRCM_CNTRY_BUF_SZ);
+ strncpy(country_abbrev, "X2", sizeof(country_abbrev) - 1);
+ country = brcms_c_country_lookup(wlc, country_abbrev);
+
+ /* save default country for exiting 11d regulatory mode */
+ strncpy(wlc->country_default, country_abbrev, BRCM_CNTRY_BUF_SZ - 1);
+
+ /* initialize autocountry_default to driver default */
+ strncpy(wlc->autocountry_default, "X2", BRCM_CNTRY_BUF_SZ - 1);
+
+ brcms_c_set_countrycode(wlc_cm, country_abbrev);
+
+ return wlc_cm;
+}
+
+void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm)
+{
+ kfree(wlc_cm);
+}
+
+u8
+brcms_c_channel_locale_flags_in_band(struct brcms_cm_info *wlc_cm,
+ uint bandunit)
+{
+ return wlc_cm->bandstate[bandunit].locale_flags;
+}
+
+static bool
+brcms_c_quiet_chanspec(struct brcms_cm_info *wlc_cm, u16 chspec)
+{
+ return (wlc_cm->wlc->pub->_n_enab & SUPPORT_11N) &&
+ CHSPEC_IS40(chspec) ?
+ (isset(wlc_cm->quiet_channels.vec,
+ lower_20_sb(CHSPEC_CHANNEL(chspec))) ||
+ isset(wlc_cm->quiet_channels.vec,
+ upper_20_sb(CHSPEC_CHANNEL(chspec)))) :
+ isset(wlc_cm->quiet_channels.vec, CHSPEC_CHANNEL(chspec));
+}
+
void
-wlc_channel_set_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
+brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
u8 local_constraint_qdbm)
{
- struct wlc_info *wlc = wlc_cm->wlc;
+ struct brcms_c_info *wlc = wlc_cm->wlc;
struct txpwr_limits txpwr;
- wlc_channel_reg_limits(wlc_cm, chanspec, &txpwr);
+ brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr);
- wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm, &txpwr,
- local_constraint_qdbm);
+ brcms_c_channel_min_txpower_limits_with_local_constraint(
+ wlc_cm, &txpwr, local_constraint_qdbm
+ );
- wlc_bmac_set_chanspec(wlc->hw, chanspec,
- (wlc_quiet_chanspec(wlc_cm, chanspec) != 0),
+ brcms_b_set_chanspec(wlc->hw, chanspec,
+ (brcms_c_quiet_chanspec(wlc_cm, chanspec) != 0),
&txpwr);
}
#ifdef POWER_DBG
-static void wlc_phy_txpower_limits_dump(txpwr_limits_t *txpwr)
+static void wlc_phy_txpower_limits_dump(struct txpwr_limits *txpwr)
{
int i;
char buf[80];
char fraction[4][4] = { " ", ".25", ".5 ", ".75" };
sprintf(buf, "CCK ");
- for (i = 0; i < WLC_NUM_RATES_CCK; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_CCK; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->cck[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->cck[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->cck[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->cck[i] % BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "20 MHz OFDM SISO ");
- for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->ofdm[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->ofdm[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->ofdm[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->ofdm[i] % BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "20 MHz OFDM CDD ");
- for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->ofdm_cdd[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->ofdm_cdd[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->ofdm_cdd[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->ofdm_cdd[i] % BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "40 MHz OFDM SISO ");
- for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->ofdm_40_siso[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->ofdm_40_siso[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->ofdm_40_siso[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->ofdm_40_siso[i] %
+ BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "40 MHz OFDM CDD ");
- for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->ofdm_40_cdd[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->ofdm_40_cdd[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->ofdm_40_cdd[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->ofdm_40_cdd[i] %
+ BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "20 MHz MCS0-7 SISO ");
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->mcs_20_siso[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->mcs_20_siso[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->mcs_20_siso[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_20_siso[i] %
+ BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "20 MHz MCS0-7 CDD ");
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->mcs_20_cdd[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->mcs_20_cdd[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->mcs_20_cdd[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_20_cdd[i] %
+ BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "20 MHz MCS0-7 STBC ");
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->mcs_20_stbc[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->mcs_20_stbc[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->mcs_20_stbc[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_20_stbc[i] %
+ BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "20 MHz MCS8-15 SDM ");
- for (i = 0; i < WLC_NUM_RATES_MCS_2_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->mcs_20_mimo[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->mcs_20_mimo[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->mcs_20_mimo[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_20_mimo[i] %
+ BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "40 MHz MCS0-7 SISO ");
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->mcs_40_siso[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->mcs_40_siso[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->mcs_40_siso[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_40_siso[i] %
+ BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "40 MHz MCS0-7 CDD ");
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->mcs_40_cdd[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->mcs_40_cdd[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->mcs_40_cdd[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_40_cdd[i] %
+ BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "40 MHz MCS0-7 STBC ");
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->mcs_40_stbc[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->mcs_40_stbc[i] % WLC_TXPWR_DB_FACTOR]);
- }
+ txpwr->mcs_40_stbc[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_40_stbc[i] %
+ BRCMS_TXPWR_DB_FACTOR]);
printk(KERN_DEBUG "%s\n", buf);
sprintf(buf, "40 MHz MCS8-15 SDM ");
- for (i = 0; i < WLC_NUM_RATES_MCS_2_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++)
sprintf(buf[strlen(buf)], " %2d%s",
- txpwr->mcs_40_mimo[i] / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->mcs_40_mimo[i] % WLC_TXPWR_DB_FACTOR]);
+ txpwr->mcs_40_mimo[i] / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_40_mimo[i] %
+ BRCMS_TXPWR_DB_FACTOR]);
}
printk(KERN_DEBUG "%s\n", buf);
printk(KERN_DEBUG "MCS32 %2d%s\n",
- txpwr->mcs32 / WLC_TXPWR_DB_FACTOR,
- fraction[txpwr->mcs32 % WLC_TXPWR_DB_FACTOR]);
+ txpwr->mcs32 / BRCMS_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs32 % BRCMS_TXPWR_DB_FACTOR]);
}
#endif /* POWER_DBG */
void
-wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
- txpwr_limits_t *txpwr)
+brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec,
+ struct txpwr_limits *txpwr)
{
- struct wlc_info *wlc = wlc_cm->wlc;
+ struct brcms_c_info *wlc = wlc_cm->wlc;
uint i;
uint chan;
int maxpwr;
int delta;
- const country_info_t *country;
- struct wlcband *band;
- const locale_info_t *li;
- int conducted_max;
- int conducted_ofdm_max;
- const locale_mimo_info_t *li_mimo;
+ const struct country_info *country;
+ struct brcms_band *band;
+ const struct locale_info *li;
+ int conducted_max = BRCMS_TXPWR_MAX;
+ int conducted_ofdm_max = BRCMS_TXPWR_MAX;
+ const struct locale_mimo_info *li_mimo;
int maxpwr20, maxpwr40;
int maxpwr_idx;
uint j;
- memset(txpwr, 0, sizeof(txpwr_limits_t));
+ memset(txpwr, 0, sizeof(struct txpwr_limits));
- if (!wlc_valid_chanspec_db(wlc_cm, chanspec)) {
- country = wlc_country_lookup(wlc, wlc->autocountry_default);
+ if (!brcms_c_valid_chanspec_db(wlc_cm, chanspec)) {
+ country = brcms_c_country_lookup(wlc, wlc->autocountry_default);
if (country == NULL)
return;
} else {
@@ -1289,16 +1298,16 @@ wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
}
chan = CHSPEC_CHANNEL(chanspec);
- band = wlc->bandstate[CHSPEC_WLCBANDUNIT(chanspec)];
- li = BAND_5G(band->bandtype) ?
- wlc_get_locale_5g(country->locale_5G) :
- wlc_get_locale_2g(country->locale_2G);
+ band = wlc->bandstate[chspec_bandunit(chanspec)];
+ li = (band->bandtype == BRCM_BAND_5G) ?
+ brcms_c_get_locale_5g(country->locale_5G) :
+ brcms_c_get_locale_2g(country->locale_2G);
- li_mimo = BAND_5G(band->bandtype) ?
- wlc_get_mimo_5g(country->locale_mimo_5G) :
- wlc_get_mimo_2g(country->locale_mimo_2G);
+ li_mimo = (band->bandtype == BRCM_BAND_5G) ?
+ brcms_c_get_mimo_5g(country->locale_mimo_5G) :
+ brcms_c_get_mimo_2g(country->locale_mimo_2G);
- if (li->flags & WLC_EIRP) {
+ if (li->flags & BRCMS_EIRP) {
delta = band->antgain;
} else {
delta = 0;
@@ -1312,42 +1321,41 @@ wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
}
/* CCK txpwr limits for 2.4G band */
- if (BAND_2G(band->bandtype)) {
+ if (band->bandtype == BRCM_BAND_2G) {
maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_CCK(chan)];
maxpwr = maxpwr - delta;
maxpwr = max(maxpwr, 0);
maxpwr = min(maxpwr, conducted_max);
- for (i = 0; i < WLC_NUM_RATES_CCK; i++)
+ for (i = 0; i < BRCMS_NUM_RATES_CCK; i++)
txpwr->cck[i] = (u8) maxpwr;
}
/* OFDM txpwr limits for 2.4G or 5G bands */
- if (BAND_2G(band->bandtype)) {
+ if (band->bandtype == BRCM_BAND_2G)
maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_OFDM(chan)];
-
- } else {
+ else
maxpwr = li->maxpwr[CHANNEL_POWER_IDX_5G(chan)];
- }
maxpwr = maxpwr - delta;
maxpwr = max(maxpwr, 0);
maxpwr = min(maxpwr, conducted_ofdm_max);
/* Keep OFDM lmit below CCK limit */
- if (BAND_2G(band->bandtype))
+ if (band->bandtype == BRCM_BAND_2G)
maxpwr = min_t(int, maxpwr, txpwr->cck[0]);
- for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++)
txpwr->ofdm[i] = (u8) maxpwr;
- }
- for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
- /* OFDM 40 MHz SISO has the same power as the corresponding MCS0-7 rate unless
- * overriden by the locale specific code. We set this value to 0 as a
- * flag (presumably 0 dBm isn't a possibility) and then copy the MCS0-7 value
- * to the 40 MHz value if it wasn't explicitly set.
+ for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) {
+ /*
+ * OFDM 40 MHz SISO has the same power as the corresponding
+ * MCS0-7 rate unless overriden by the locale specific code.
+ * We set this value to 0 as a flag (presumably 0 dBm isn't
+ * a possibility) and then copy the MCS0-7 value to the 40 MHz
+ * value if it wasn't explicitly set.
*/
txpwr->ofdm_40_siso[i] = 0;
@@ -1357,7 +1365,7 @@ wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
}
/* MIMO/HT specific limits */
- if (li_mimo->flags & WLC_EIRP) {
+ if (li_mimo->flags & BRCMS_EIRP) {
delta = band->antgain;
} else {
delta = 0;
@@ -1365,7 +1373,7 @@ wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
delta = band->antgain - QDB(6); /* Excess over 6 dB */
}
- if (BAND_2G(band->bandtype))
+ if (band->bandtype == BRCM_BAND_2G)
maxpwr_idx = (chan - 1);
else
maxpwr_idx = CHANNEL_POWER_IDX_5G(chan);
@@ -1379,46 +1387,49 @@ wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
maxpwr40 = max(maxpwr40, 0);
/* Fill in the MCS 0-7 (SISO) rates */
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
- /* 20 MHz has the same power as the corresponding OFDM rate unless
- * overriden by the locale specific code.
+ /*
+ * 20 MHz has the same power as the corresponding OFDM rate
+ * unless overriden by the locale specific code.
*/
txpwr->mcs_20_siso[i] = txpwr->ofdm[i];
txpwr->mcs_40_siso[i] = 0;
}
/* Fill in the MCS 0-7 CDD rates */
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
txpwr->mcs_20_cdd[i] = (u8) maxpwr20;
txpwr->mcs_40_cdd[i] = (u8) maxpwr40;
}
- /* These locales have SISO expressed in the table and override CDD later */
+ /*
+ * These locales have SISO expressed in the
+ * table and override CDD later
+ */
if (li_mimo == &locale_bn) {
if (li_mimo == &locale_bn) {
maxpwr20 = QDB(16);
maxpwr40 = 0;
- if (chan >= 3 && chan <= 11) {
+ if (chan >= 3 && chan <= 11)
maxpwr40 = QDB(16);
- }
}
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
txpwr->mcs_20_siso[i] = (u8) maxpwr20;
txpwr->mcs_40_siso[i] = (u8) maxpwr40;
}
}
/* Fill in the MCS 0-7 STBC rates */
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
txpwr->mcs_20_stbc[i] = 0;
txpwr->mcs_40_stbc[i] = 0;
}
/* Fill in the MCS 8-15 SDM rates */
- for (i = 0; i < WLC_NUM_RATES_MCS_2_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) {
txpwr->mcs_20_mimo[i] = (u8) maxpwr20;
txpwr->mcs_40_mimo[i] = (u8) maxpwr40;
}
@@ -1426,7 +1437,7 @@ wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
/* Fill in MCS32 */
txpwr->mcs32 = (u8) maxpwr40;
- for (i = 0, j = 0; i < WLC_NUM_RATES_OFDM; i++, j++) {
+ for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) {
if (txpwr->ofdm_40_cdd[i] == 0)
txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
if (i == 0) {
@@ -1436,16 +1447,16 @@ wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
}
}
- /* Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO value if it wasn't
- * provided explicitly.
+ /*
+ * Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO
+ * value if it wasn't provided explicitly.
*/
-
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
if (txpwr->mcs_40_siso[i] == 0)
txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i];
}
- for (i = 0, j = 0; i < WLC_NUM_RATES_OFDM; i++, j++) {
+ for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) {
if (txpwr->ofdm_40_siso[i] == 0)
txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
if (i == 0) {
@@ -1455,10 +1466,11 @@ wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
}
}
- /* Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding STBC values if they weren't
- * provided explicitly.
+ /*
+ * Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding
+ * STBC values if they weren't provided explicitly.
*/
- for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
if (txpwr->mcs_20_stbc[i] == 0)
txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i];
@@ -1472,72 +1484,94 @@ wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
return;
}
-/* Returns true if currently set country is Japan or variant */
-static bool wlc_japan(struct wlc_info *wlc)
+/*
+ * Verify the chanspec is using a legal set of parameters, i.e. that the
+ * chanspec specified a band, bw, ctl_sb and channel and that the
+ * combination could be legal given any set of circumstances.
+ * RETURNS: true is the chanspec is malformed, false if it looks good.
+ */
+static bool brcms_c_chspec_malformed(u16 chanspec)
{
- return wlc_japan_ccode(wlc->cmi->country_abbrev);
-}
+ /* must be 2G or 5G band */
+ if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec))
+ return true;
+ /* must be 20 or 40 bandwidth */
+ if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec))
+ return true;
+
+ /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */
+ if (CHSPEC_IS20(chanspec)) {
+ if (!CHSPEC_SB_NONE(chanspec))
+ return true;
+ } else if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) {
+ return true;
+ }
-/* JP, J1 - J10 are Japan ccodes */
-static bool wlc_japan_ccode(const char *ccode)
-{
- return (ccode[0] == 'J' &&
- (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9')));
+ return false;
}
/*
- * Validate the chanspec for this locale, for 40MHZ we need to also check that the sidebands
- * are valid 20MZH channels in this locale and they are also a legal HT combination
+ * Validate the chanspec for this locale, for 40MHZ we need to also
+ * check that the sidebands are valid 20MZH channels in this locale
+ * and they are also a legal HT combination
*/
static bool
-wlc_valid_chanspec_ext(wlc_cm_info_t *wlc_cm, chanspec_t chspec, bool dualband)
+brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec,
+ bool dualband)
{
- struct wlc_info *wlc = wlc_cm->wlc;
+ struct brcms_c_info *wlc = wlc_cm->wlc;
u8 channel = CHSPEC_CHANNEL(chspec);
/* check the chanspec */
- if (bcm_chspec_malformed(chspec)) {
+ if (brcms_c_chspec_malformed(chspec)) {
wiphy_err(wlc->wiphy, "wl%d: malformed chanspec 0x%x\n",
wlc->pub->unit, chspec);
return false;
}
if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) !=
- CHSPEC_WLCBANDUNIT(chspec))
+ chspec_bandunit(chspec))
return false;
/* Check a 20Mhz channel */
if (CHSPEC_IS20(chspec)) {
if (dualband)
- return VALID_CHANNEL20_DB(wlc_cm->wlc, channel);
+ return brcms_c_valid_channel20_db(wlc_cm->wlc->cmi,
+ channel);
else
- return VALID_CHANNEL20(wlc_cm->wlc, channel);
+ return brcms_c_valid_channel20(wlc_cm->wlc->cmi,
+ channel);
}
#ifdef SUPPORT_40MHZ
- /* We know we are now checking a 40MHZ channel, so we should only be here
- * for NPHYS
+ /*
+ * We know we are now checking a 40MHZ channel, so we should
+ * only be here for NPHYS
*/
- if (WLCISNPHY(wlc->band) || WLCISSSLPNPHY(wlc->band)) {
+ if (BRCMS_ISNPHY(wlc->band) || BRCMS_ISSSLPNPHY(wlc->band)) {
u8 upper_sideband = 0, idx;
u8 num_ch20_entries =
sizeof(chan20_info) / sizeof(struct chan20_info);
- if (!VALID_40CHANSPEC_IN_BAND(wlc, CHSPEC_WLCBANDUNIT(chspec)))
+ if (!VALID_40CHANSPEC_IN_BAND(wlc, chspec_bandunit(chspec)))
return false;
if (dualband) {
- if (!VALID_CHANNEL20_DB(wlc, LOWER_20_SB(channel)) ||
- !VALID_CHANNEL20_DB(wlc, UPPER_20_SB(channel)))
+ if (!brcms_c_valid_channel20_db(wlc->cmi,
+ lower_20_sb(channel)) ||
+ !brcms_c_valid_channel20_db(wlc->cmi,
+ upper_20_sb(channel)))
return false;
} else {
- if (!VALID_CHANNEL20(wlc, LOWER_20_SB(channel)) ||
- !VALID_CHANNEL20(wlc, UPPER_20_SB(channel)))
+ if (!brcms_c_valid_channel20(wlc->cmi,
+ lower_20_sb(channel)) ||
+ !brcms_c_valid_channel20(wlc->cmi,
+ upper_20_sb(channel)))
return false;
}
/* find the lower sideband info in the sideband array */
for (idx = 0; idx < num_ch20_entries; idx++) {
- if (chan20_info[idx].sb == LOWER_20_SB(channel))
+ if (chan20_info[idx].sb == lower_20_sb(channel))
upper_sideband = chan20_info[idx].adj_sbs;
}
/* check that the lower sideband allows an upper sideband */
@@ -1551,7 +1585,7 @@ wlc_valid_chanspec_ext(wlc_cm_info_t *wlc_cm, chanspec_t chspec, bool dualband)
return false;
}
-bool wlc_valid_chanspec_db(wlc_cm_info_t *wlc_cm, chanspec_t chspec)
+bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec)
{
- return wlc_valid_chanspec_ext(wlc_cm, chspec, true);
+ return brcms_c_valid_chanspec_ext(wlc_cm, chspec, true);
}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.h b/drivers/net/wireless/brcm80211/brcmsmac/channel.h
new file mode 100644
index 000000000000..808cb4fbfbe7
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_CHANNEL_H_
+#define _BRCM_CHANNEL_H_
+
+/* conversion for phy txpwr calculations that use .25 dB units */
+#define BRCMS_TXPWR_DB_FACTOR 4
+
+/* bits for locale_info flags */
+#define BRCMS_PEAK_CONDUCTED 0x00 /* Peak for locals */
+#define BRCMS_EIRP 0x01 /* Flag for EIRP */
+#define BRCMS_DFS_TPC 0x02 /* Flag for DFS TPC */
+#define BRCMS_NO_OFDM 0x04 /* Flag for No OFDM */
+#define BRCMS_NO_40MHZ 0x08 /* Flag for No MIMO 40MHz */
+#define BRCMS_NO_MIMO 0x10 /* Flag for No MIMO, 20 or 40 MHz */
+#define BRCMS_RADAR_TYPE_EU 0x20 /* Flag for EU */
+#define BRCMS_DFS_FCC BRCMS_DFS_TPC /* Flag for DFS FCC */
+
+#define BRCMS_DFS_EU (BRCMS_DFS_TPC | BRCMS_RADAR_TYPE_EU) /* Flag for DFS EU */
+
+extern struct brcms_cm_info *
+brcms_c_channel_mgr_attach(struct brcms_c_info *wlc);
+
+extern void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm);
+
+extern u8 brcms_c_channel_locale_flags_in_band(struct brcms_cm_info *wlc_cm,
+ uint bandunit);
+
+extern bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm,
+ u16 chspec);
+
+extern void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm,
+ u16 chanspec,
+ struct txpwr_limits *txpwr);
+extern void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm,
+ u16 chanspec,
+ u8 local_constraint_qdbm);
+
+#endif /* _WLC_CHANNEL_H */
diff --git a/drivers/staging/brcm80211/brcmsmac/d11.h b/drivers/net/wireless/brcm80211/brcmsmac/d11.h
index d91e4189a3e8..ed51616abc85 100644
--- a/drivers/staging/brcm80211/brcmsmac/d11.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/d11.h
@@ -14,35 +14,24 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _D11_H
-#define _D11_H
+#ifndef _BRCM_D11_H_
+#define _BRCM_D11_H_
-#include <sbconfig.h>
+#include <linux/ieee80211.h>
-#ifndef WL_RSSI_ANT_MAX
-#define WL_RSSI_ANT_MAX 4 /* max possible rx antennas */
-#elif WL_RSSI_ANT_MAX != 4
-#error "WL_RSSI_ANT_MAX does not match"
-#endif
-
-/* cpp contortions to concatenate w/arg prescan */
-#ifndef PAD
-#define _PADLINE(line) pad ## line
-#define _XSTR(line) _PADLINE(line)
-#define PAD _XSTR(__LINE__)
-#endif
-
-#define BCN_TMPL_LEN 512 /* length of the BCN template area */
+#include <defs.h>
+#include "pub.h"
+#include "dma.h"
/* RX FIFO numbers */
#define RX_FIFO 0 /* data and ctl frames */
#define RX_TXSTATUS_FIFO 3 /* RX fifo for tx status packages */
-/* TX FIFO numbers using WME Access Classes */
-#define TX_AC_BK_FIFO 0 /* Access Category Background TX FIFO */
-#define TX_AC_BE_FIFO 1 /* Access Category Best-Effort TX FIFO */
-#define TX_AC_VI_FIFO 2 /* Access Class Video TX FIFO */
-#define TX_AC_VO_FIFO 3 /* Access Class Voice TX FIFO */
+/* TX FIFO numbers using WME Access Category */
+#define TX_AC_BK_FIFO 0 /* Background TX FIFO */
+#define TX_AC_BE_FIFO 1 /* Best-Effort TX FIFO */
+#define TX_AC_VI_FIFO 2 /* Video TX FIFO */
+#define TX_AC_VO_FIFO 3 /* Voice TX FIFO */
#define TX_BCMC_FIFO 4 /* Broadcast/Multicast TX FIFO */
#define TX_ATIM_FIFO 5 /* TX fifo for ATIM window info */
@@ -56,10 +45,12 @@
#define TX_DATA_FIFO TX_AC_BE_FIFO
#define TX_CTL_FIFO TX_AC_VO_FIFO
-typedef volatile struct {
+#define WL_RSSI_ANT_MAX 4 /* max possible rx antennas */
+
+struct intctrlregs {
u32 intstatus;
u32 intmask;
-} intctrlregs_t;
+};
/* PIO structure,
* support two PIO format: 2 bytes access and 4 bytes access
@@ -67,55 +58,53 @@ typedef volatile struct {
* a pair of channels is defined for convenience
*/
/* 2byte-wide pio register set per channel(xmt or rcv) */
-typedef volatile struct {
+struct pio2regs {
u16 fifocontrol;
u16 fifodata;
u16 fifofree; /* only valid in xmt channel, not in rcv channel */
u16 PAD;
-} pio2regs_t;
+};
/* a pair of pio channels(tx and rx) */
-typedef volatile struct {
- pio2regs_t tx;
- pio2regs_t rx;
-} pio2regp_t;
+struct pio2regp {
+ struct pio2regs tx;
+ struct pio2regs rx;
+};
/* 4byte-wide pio register set per channel(xmt or rcv) */
-typedef volatile struct {
+struct pio4regs {
u32 fifocontrol;
u32 fifodata;
-} pio4regs_t;
+};
/* a pair of pio channels(tx and rx) */
-typedef volatile struct {
- pio4regs_t tx;
- pio4regs_t rx;
-} pio4regp_t;
+struct pio4regp {
+ struct pio4regs tx;
+ struct pio4regs rx;
+};
/* read: 32-bit register that can be read as 32-bit or as 2 16-bit
* write: only low 16b-it half can be written
*/
-typedef volatile union {
+union pmqreg {
u32 pmqhostdata; /* read only! */
struct {
u16 pmqctrlstatus; /* read/write */
u16 PAD;
} w;
-} pmqreg_t;
+};
-typedef volatile struct {
- dma64regs_t dmaxmt; /* dma tx */
- pio4regs_t piotx; /* pio tx */
- dma64regs_t dmarcv; /* dma rx */
- pio4regs_t piorx; /* pio rx */
-} fifo64_t;
+struct fifo64 {
+ struct dma64regs dmaxmt; /* dma tx */
+ struct pio4regs piotx; /* pio tx */
+ struct dma64regs dmarcv; /* dma rx */
+ struct pio4regs piorx; /* pio rx */
+};
/*
* Host Interface Registers
- * - primed from hnd_cores/dot11mac/systemC/registers/ihr.h
- * - but definitely not complete
*/
-typedef volatile struct _d11regs {
+struct d11regs {
/* Device Control ("semi-standard host registers") */
u32 PAD[3]; /* 0x0 - 0x8 */
u32 biststatus; /* 0xC */
@@ -125,7 +114,7 @@ typedef volatile struct _d11regs {
u32 usectimer; /* 0x1c *//* for corerev >= 26 */
/* Interrupt Control *//* 0x20 */
- intctrlregs_t intctrlregs[8];
+ struct intctrlregs intctrlregs[8];
u32 PAD[40]; /* 0x60 - 0xFC */
@@ -144,7 +133,7 @@ typedef volatile struct _d11regs {
u32 PAD[2]; /* 0x138 - 0x13C */
/* PMQ registers */
- pmqreg_t pmqreg; /* 0x140 */
+ union pmqreg pmqreg; /* 0x140 */
u32 pmqpatl; /* 0x144 */
u32 pmqpath; /* 0x148 */
u32 PAD; /* 0x14C */
@@ -184,10 +173,10 @@ typedef volatile struct _d11regs {
u32 PAD[5]; /* 0x1ec - 0x1fc */
/* 0x200-0x37F dma/pio registers */
- fifo64_t fifo64regs[6];
+ struct fifo64 fifo64regs[6];
/* FIFO diagnostic port access */
- dma32diag_t dmafifo; /* 0x380 - 0x38C */
+ struct dma32diag dmafifo; /* 0x380 - 0x38C */
u32 aggfifocnt; /* 0x390 */
u32 aggfifodata; /* 0x394 */
@@ -439,10 +428,7 @@ typedef volatile struct _d11regs {
/* SHM *//* 0x800 - 0xEFE */
u16 PAD[0x380]; /* 0x800 - 0xEFE */
-
- /* SB configuration registers: 0xF00 */
- sbconfig_t sbconfig; /* sb config regs occupy top 256 bytes */
-} d11regs_t;
+};
#define PIHR_BASE 0x0400 /* byte address of packed IHR region */
@@ -465,7 +451,7 @@ typedef volatile struct _d11regs {
#define IRL_FC_MASK 0xff000000 /* frame count */
#define IRL_FC_SHIFT 24 /* frame count */
-/* maccontrol register */
+/*== maccontrol register ==*/
#define MCTL_GMODE (1U << 31)
#define MCTL_DISCARD_PMQ (1 << 30)
#define MCTL_WAKE (1 << 26)
@@ -489,75 +475,119 @@ typedef volatile struct _d11regs {
#define MCTL_PSM_RUN (1 << 1)
#define MCTL_EN_MAC (1 << 0)
-/* maccommand register */
+/*== maccommand register ==*/
#define MCMD_BCN0VLD (1 << 0)
#define MCMD_BCN1VLD (1 << 1)
#define MCMD_DIRFRMQVAL (1 << 2)
#define MCMD_CCA (1 << 3)
#define MCMD_BG_NOISE (1 << 4)
#define MCMD_SKIP_SHMINIT (1 << 5) /* only used for simulation */
-#define MCMD_SAMPLECOLL MCMD_SKIP_SHMINIT /* reuse for sample collect */
-
-/* macintstatus/macintmask */
-#define MI_MACSSPNDD (1 << 0) /* MAC has gracefully suspended */
-#define MI_BCNTPL (1 << 1) /* beacon template available */
-#define MI_TBTT (1 << 2) /* TBTT indication */
-#define MI_BCNSUCCESS (1 << 3) /* beacon successfully tx'd */
-#define MI_BCNCANCLD (1 << 4) /* beacon canceled (IBSS) */
-#define MI_ATIMWINEND (1 << 5) /* end of ATIM-window (IBSS) */
-#define MI_PMQ (1 << 6) /* PMQ entries available */
-#define MI_NSPECGEN_0 (1 << 7) /* non-specific gen-stat bits that are set by PSM */
-#define MI_NSPECGEN_1 (1 << 8) /* non-specific gen-stat bits that are set by PSM */
-#define MI_MACTXERR (1 << 9) /* MAC level Tx error */
-#define MI_NSPECGEN_3 (1 << 10) /* non-specific gen-stat bits that are set by PSM */
-#define MI_PHYTXERR (1 << 11) /* PHY Tx error */
-#define MI_PME (1 << 12) /* Power Management Event */
-#define MI_GP0 (1 << 13) /* General-purpose timer0 */
-#define MI_GP1 (1 << 14) /* General-purpose timer1 */
-#define MI_DMAINT (1 << 15) /* (ORed) DMA-interrupts */
-#define MI_TXSTOP (1 << 16) /* MAC has completed a TX FIFO Suspend/Flush */
-#define MI_CCA (1 << 17) /* MAC has completed a CCA measurement */
-#define MI_BG_NOISE (1 << 18) /* MAC has collected background noise samples */
-#define MI_DTIM_TBTT (1 << 19) /* MBSS DTIM TBTT indication */
-#define MI_PRQ (1 << 20) /* Probe response queue needs attention */
-#define MI_PWRUP (1 << 21) /* Radio/PHY has been powered back up. */
+#define MCMD_SAMPLECOLL MCMD_SKIP_SHMINIT /* reuse for sample collect */
+
+/*== macintstatus/macintmask ==*/
+/* gracefully suspended */
+#define MI_MACSSPNDD (1 << 0)
+/* beacon template available */
+#define MI_BCNTPL (1 << 1)
+/* TBTT indication */
+#define MI_TBTT (1 << 2)
+/* beacon successfully tx'd */
+#define MI_BCNSUCCESS (1 << 3)
+/* beacon canceled (IBSS) */
+#define MI_BCNCANCLD (1 << 4)
+/* end of ATIM-window (IBSS) */
+#define MI_ATIMWINEND (1 << 5)
+/* PMQ entries available */
+#define MI_PMQ (1 << 6)
+/* non-specific gen-stat bits that are set by PSM */
+#define MI_NSPECGEN_0 (1 << 7)
+/* non-specific gen-stat bits that are set by PSM */
+#define MI_NSPECGEN_1 (1 << 8)
+/* MAC level Tx error */
+#define MI_MACTXERR (1 << 9)
+/* non-specific gen-stat bits that are set by PSM */
+#define MI_NSPECGEN_3 (1 << 10)
+/* PHY Tx error */
+#define MI_PHYTXERR (1 << 11)
+/* Power Management Event */
+#define MI_PME (1 << 12)
+/* General-purpose timer0 */
+#define MI_GP0 (1 << 13)
+/* General-purpose timer1 */
+#define MI_GP1 (1 << 14)
+/* (ORed) DMA-interrupts */
+#define MI_DMAINT (1 << 15)
+/* MAC has completed a TX FIFO Suspend/Flush */
+#define MI_TXSTOP (1 << 16)
+/* MAC has completed a CCA measurement */
+#define MI_CCA (1 << 17)
+/* MAC has collected background noise samples */
+#define MI_BG_NOISE (1 << 18)
+/* MBSS DTIM TBTT indication */
+#define MI_DTIM_TBTT (1 << 19)
+/* Probe response queue needs attention */
+#define MI_PRQ (1 << 20)
+/* Radio/PHY has been powered back up. */
+#define MI_PWRUP (1 << 21)
#define MI_RESERVED3 (1 << 22)
#define MI_RESERVED2 (1 << 23)
#define MI_RESERVED1 (1 << 25)
/* MAC detected change on RF Disable input*/
#define MI_RFDISABLE (1 << 28)
-#define MI_TFS (1 << 29) /* MAC has completed a TX */
-#define MI_PHYCHANGED (1 << 30) /* A phy status change wrt G mode */
-#define MI_TO (1U << 31) /* general purpose timeout */
+/* MAC has completed a TX */
+#define MI_TFS (1 << 29)
+/* A phy status change wrt G mode */
+#define MI_PHYCHANGED (1 << 30)
+/* general purpose timeout */
+#define MI_TO (1U << 31)
/* Mac capabilities registers */
-/* machwcap */
+/*== machwcap ==*/
#define MCAP_TKIPMIC 0x80000000 /* TKIP MIC hardware present */
-/* pmqhost data */
-#define PMQH_DATA_MASK 0xffff0000 /* data entry of head pmq entry */
-#define PMQH_BSSCFG 0x00100000 /* PM entry for BSS config */
-#define PMQH_PMOFF 0x00010000 /* PM Mode OFF: power save off */
-#define PMQH_PMON 0x00020000 /* PM Mode ON: power save on */
-#define PMQH_DASAT 0x00040000 /* Dis-associated or De-authenticated */
-#define PMQH_ATIMFAIL 0x00080000 /* ATIM not acknowledged */
-#define PMQH_DEL_ENTRY 0x00000001 /* delete head entry */
-#define PMQH_DEL_MULT 0x00000002 /* delete head entry to cur read pointer -1 */
-#define PMQH_OFLO 0x00000004 /* pmq overflow indication */
-#define PMQH_NOT_EMPTY 0x00000008 /* entries are present in pmq */
-
-/* phydebug */
-#define PDBG_CRS (1 << 0) /* phy is asserting carrier sense */
-#define PDBG_TXA (1 << 1) /* phy is taking xmit byte from mac this cycle */
-#define PDBG_TXF (1 << 2) /* mac is instructing the phy to transmit a frame */
-#define PDBG_TXE (1 << 3) /* phy is signalling a transmit Error to the mac */
-#define PDBG_RXF (1 << 4) /* phy detected the end of a valid frame preamble */
-#define PDBG_RXS (1 << 5) /* phy detected the end of a valid PLCP header */
-#define PDBG_RXFRG (1 << 6) /* rx start not asserted */
-#define PDBG_RXV (1 << 7) /* mac is taking receive byte from phy this cycle */
-#define PDBG_RFD (1 << 16) /* RF portion of the radio is disabled */
-
-/* objaddr register */
+/*== pmqhost data ==*/
+/* data entry of head pmq entry */
+#define PMQH_DATA_MASK 0xffff0000
+/* PM entry for BSS config */
+#define PMQH_BSSCFG 0x00100000
+/* PM Mode OFF: power save off */
+#define PMQH_PMOFF 0x00010000
+/* PM Mode ON: power save on */
+#define PMQH_PMON 0x00020000
+/* Dis-associated or De-authenticated */
+#define PMQH_DASAT 0x00040000
+/* ATIM not acknowledged */
+#define PMQH_ATIMFAIL 0x00080000
+/* delete head entry */
+#define PMQH_DEL_ENTRY 0x00000001
+/* delete head entry to cur read pointer -1 */
+#define PMQH_DEL_MULT 0x00000002
+/* pmq overflow indication */
+#define PMQH_OFLO 0x00000004
+/* entries are present in pmq */
+#define PMQH_NOT_EMPTY 0x00000008
+
+/*== phydebug ==*/
+/* phy is asserting carrier sense */
+#define PDBG_CRS (1 << 0)
+/* phy is taking xmit byte from mac this cycle */
+#define PDBG_TXA (1 << 1)
+/* mac is instructing the phy to transmit a frame */
+#define PDBG_TXF (1 << 2)
+/* phy is signalling a transmit Error to the mac */
+#define PDBG_TXE (1 << 3)
+/* phy detected the end of a valid frame preamble */
+#define PDBG_RXF (1 << 4)
+/* phy detected the end of a valid PLCP header */
+#define PDBG_RXS (1 << 5)
+/* rx start not asserted */
+#define PDBG_RXFRG (1 << 6)
+/* mac is taking receive byte from phy this cycle */
+#define PDBG_RXV (1 << 7)
+/* RF portion of the radio is disabled */
+#define PDBG_RFD (1 << 16)
+
+/*== objaddr register ==*/
#define OBJADDR_SEL_MASK 0x000F0000
#define OBJADDR_UCM_SEL 0x00000000
#define OBJADDR_SHM_SEL 0x00010000
@@ -572,20 +602,20 @@ typedef volatile struct _d11regs {
#define WEP_PCMADDR 0x07d4
#define WEP_PCMDATA 0x07d6
-/* frmtxstatus */
+/*== frmtxstatus ==*/
#define TXS_V (1 << 0) /* valid bit */
#define TXS_STATUS_MASK 0xffff
#define TXS_FID_MASK 0xffff0000
#define TXS_FID_SHIFT 16
-/* frmtxstatus2 */
+/*== frmtxstatus2 ==*/
#define TXS_SEQ_MASK 0xffff
#define TXS_PTX_MASK 0xff0000
#define TXS_PTX_SHIFT 16
#define TXS_MU_MASK 0x01000000
#define TXS_MU_SHIFT 24
-/* clk_ctl_st */
+/*== clk_ctl_st ==*/
#define CCS_ERSRC_REQ_D11PLL 0x00000100 /* d11 core pll request */
#define CCS_ERSRC_REQ_PHYPLL 0x00000200 /* PHY pll request */
#define CCS_ERSRC_AVAIL_D11PLL 0x01000000 /* d11 core pll available */
@@ -610,31 +640,35 @@ typedef volatile struct _d11regs {
#define TXFIFO_SIZE_UNIT 256 /* one unit corresponds to 256 bytes */
#define MBSS16_TEMPLMEM_MINBLKS 65 /* one unit corresponds to 256 bytes */
-/* phy versions, PhyVersion:Revision field */
-#define PV_AV_MASK 0xf000 /* analog block version */
-#define PV_AV_SHIFT 12 /* analog block version bitfield offset */
-#define PV_PT_MASK 0x0f00 /* phy type */
-#define PV_PT_SHIFT 8 /* phy type bitfield offset */
-#define PV_PV_MASK 0x000f /* phy version */
+/*== phy versions (PhyVersion:Revision field) ==*/
+/* analog block version */
+#define PV_AV_MASK 0xf000
+/* analog block version bitfield offset */
+#define PV_AV_SHIFT 12
+/* phy type */
+#define PV_PT_MASK 0x0f00
+/* phy type bitfield offset */
+#define PV_PT_SHIFT 8
+/* phy version */
+#define PV_PV_MASK 0x000f
#define PHY_TYPE(v) ((v & PV_PT_MASK) >> PV_PT_SHIFT)
-/* phy types, PhyVersion:PhyType field */
+/*== phy types (PhyVersion:PhyType field) ==*/
#define PHY_TYPE_N 4 /* N-Phy value */
#define PHY_TYPE_SSN 6 /* SSLPN-Phy value */
#define PHY_TYPE_LCN 8 /* LCN-Phy value */
#define PHY_TYPE_LCNXN 9 /* LCNXN-Phy value */
#define PHY_TYPE_NULL 0xf /* Invalid Phy value */
-/* analog types, PhyVersion:AnalogType field */
+/*== analog types (PhyVersion:AnalogType field) ==*/
#define ANA_11N_013 5
/* 802.11a PLCP header def */
-typedef struct ofdm_phy_hdr ofdm_phy_hdr_t;
struct ofdm_phy_hdr {
u8 rlpt[3]; /* rate, length, parity, tail */
u16 service;
u8 pad;
-} __attribute__((packed));
+} __packed;
#define D11A_PHY_HDR_GRATE(phdr) ((phdr)->rlpt[0] & 0x0f)
#define D11A_PHY_HDR_GRES(phdr) (((phdr)->rlpt[0] >> 4) & 0x01)
@@ -664,13 +698,12 @@ struct ofdm_phy_hdr {
#define D11A_PHY_PREHDR_TIME (D11A_PHY_PRE_TIME + D11A_PHY_HDR_TIME)
/* 802.11b PLCP header def */
-typedef struct cck_phy_hdr cck_phy_hdr_t;
struct cck_phy_hdr {
u8 signal;
u8 service;
u16 length;
u16 crc;
-} __attribute__((packed));
+} __packed;
#define D11B_PHY_HDR_LEN 6
@@ -691,62 +724,62 @@ struct cck_phy_hdr {
#define MIMO_PLCP_40MHZ 0x80 /* 40 Hz frame */
#define MIMO_PLCP_AMPDU 0x08 /* ampdu */
-#define WLC_GET_CCK_PLCP_LEN(plcp) (plcp[4] + (plcp[5] << 8))
-#define WLC_GET_MIMO_PLCP_LEN(plcp) (plcp[1] + (plcp[2] << 8))
-#define WLC_SET_MIMO_PLCP_LEN(plcp, len) \
+#define BRCMS_GET_CCK_PLCP_LEN(plcp) (plcp[4] + (plcp[5] << 8))
+#define BRCMS_GET_MIMO_PLCP_LEN(plcp) (plcp[1] + (plcp[2] << 8))
+#define BRCMS_SET_MIMO_PLCP_LEN(plcp, len) \
do { \
plcp[1] = len & 0xff; \
plcp[2] = ((len >> 8) & 0xff); \
} while (0);
-#define WLC_SET_MIMO_PLCP_AMPDU(plcp) (plcp[3] |= MIMO_PLCP_AMPDU)
-#define WLC_CLR_MIMO_PLCP_AMPDU(plcp) (plcp[3] &= ~MIMO_PLCP_AMPDU)
-#define WLC_IS_MIMO_PLCP_AMPDU(plcp) (plcp[3] & MIMO_PLCP_AMPDU)
+#define BRCMS_SET_MIMO_PLCP_AMPDU(plcp) (plcp[3] |= MIMO_PLCP_AMPDU)
+#define BRCMS_CLR_MIMO_PLCP_AMPDU(plcp) (plcp[3] &= ~MIMO_PLCP_AMPDU)
+#define BRCMS_IS_MIMO_PLCP_AMPDU(plcp) (plcp[3] & MIMO_PLCP_AMPDU)
-/* The dot11a PLCP header is 5 bytes. To simplify the software (so that we
- * don't need e.g. different tx DMA headers for 11a and 11b), the PLCP header has
- * padding added in the ucode.
+/*
+ * The dot11a PLCP header is 5 bytes. To simplify the software (so that we
+ * don't need e.g. different tx DMA headers for 11a and 11b), the PLCP header
+ * has padding added in the ucode.
*/
#define D11_PHY_HDR_LEN 6
/* TX DMA buffer header */
-typedef struct d11txh d11txh_t;
struct d11txh {
- u16 MacTxControlLow; /* 0x0 */
- u16 MacTxControlHigh; /* 0x1 */
- u16 MacFrameControl; /* 0x2 */
- u16 TxFesTimeNormal; /* 0x3 */
- u16 PhyTxControlWord; /* 0x4 */
- u16 PhyTxControlWord_1; /* 0x5 */
- u16 PhyTxControlWord_1_Fbr; /* 0x6 */
- u16 PhyTxControlWord_1_Rts; /* 0x7 */
- u16 PhyTxControlWord_1_FbrRts; /* 0x8 */
- u16 MainRates; /* 0x9 */
- u16 XtraFrameTypes; /* 0xa */
+ __le16 MacTxControlLow; /* 0x0 */
+ __le16 MacTxControlHigh; /* 0x1 */
+ __le16 MacFrameControl; /* 0x2 */
+ __le16 TxFesTimeNormal; /* 0x3 */
+ __le16 PhyTxControlWord; /* 0x4 */
+ __le16 PhyTxControlWord_1; /* 0x5 */
+ __le16 PhyTxControlWord_1_Fbr; /* 0x6 */
+ __le16 PhyTxControlWord_1_Rts; /* 0x7 */
+ __le16 PhyTxControlWord_1_FbrRts; /* 0x8 */
+ __le16 MainRates; /* 0x9 */
+ __le16 XtraFrameTypes; /* 0xa */
u8 IV[16]; /* 0x0b - 0x12 */
u8 TxFrameRA[6]; /* 0x13 - 0x15 */
- u16 TxFesTimeFallback; /* 0x16 */
+ __le16 TxFesTimeFallback; /* 0x16 */
u8 RTSPLCPFallback[6]; /* 0x17 - 0x19 */
- u16 RTSDurFallback; /* 0x1a */
+ __le16 RTSDurFallback; /* 0x1a */
u8 FragPLCPFallback[6]; /* 0x1b - 1d */
- u16 FragDurFallback; /* 0x1e */
- u16 MModeLen; /* 0x1f */
- u16 MModeFbrLen; /* 0x20 */
- u16 TstampLow; /* 0x21 */
- u16 TstampHigh; /* 0x22 */
- u16 ABI_MimoAntSel; /* 0x23 */
- u16 PreloadSize; /* 0x24 */
- u16 AmpduSeqCtl; /* 0x25 */
- u16 TxFrameID; /* 0x26 */
- u16 TxStatus; /* 0x27 */
- u16 MaxNMpdus; /* 0x28 */
- u16 MaxABytes_MRT; /* 0x29 */
- u16 MaxABytes_FBR; /* 0x2a */
- u16 MinMBytes; /* 0x2b */
+ __le16 FragDurFallback; /* 0x1e */
+ __le16 MModeLen; /* 0x1f */
+ __le16 MModeFbrLen; /* 0x20 */
+ __le16 TstampLow; /* 0x21 */
+ __le16 TstampHigh; /* 0x22 */
+ __le16 ABI_MimoAntSel; /* 0x23 */
+ __le16 PreloadSize; /* 0x24 */
+ __le16 AmpduSeqCtl; /* 0x25 */
+ __le16 TxFrameID; /* 0x26 */
+ __le16 TxStatus; /* 0x27 */
+ __le16 MaxNMpdus; /* 0x28 */
+ __le16 MaxABytes_MRT; /* 0x29 */
+ __le16 MaxABytes_FBR; /* 0x2a */
+ __le16 MinMBytes; /* 0x2b */
u8 RTSPhyHeader[D11_PHY_HDR_LEN]; /* 0x2c - 0x2e */
struct ieee80211_rts rts_frame; /* 0x2f - 0x36 */
u16 PAD; /* 0x37 */
-} __attribute__((packed));
+} __packed;
#define D11_TXH_LEN 112 /* bytes */
@@ -756,14 +789,17 @@ struct d11txh {
#define FT_HT 2
#define FT_N 3
-/* Position of MPDU inside A-MPDU; indicated with bits 10:9 of MacTxControlLow */
+/*
+ * Position of MPDU inside A-MPDU; indicated with bits 10:9
+ * of MacTxControlLow
+ */
#define TXC_AMPDU_SHIFT 9 /* shift for ampdu settings */
#define TXC_AMPDU_NONE 0 /* Regular MPDU, not an A-MPDU */
#define TXC_AMPDU_FIRST 1 /* first MPDU of an A-MPDU */
#define TXC_AMPDU_MIDDLE 2 /* intermediate MPDU of an A-MPDU */
#define TXC_AMPDU_LAST 3 /* last (or single) MPDU of an A-MPDU */
-/* MacTxControlLow */
+/*== MacTxControlLow ==*/
#define TXC_AMIC 0x8000
#define TXC_SENDCTS 0x0800
#define TXC_AMPDU_MASK 0x0600
@@ -777,18 +813,25 @@ struct d11txh {
#define TXC_LONGFRAME 0x0002
#define TXC_IMMEDACK 0x0001
-/* MacTxControlHigh */
-#define TXC_PREAMBLE_RTS_FB_SHORT 0x8000 /* RTS fallback preamble type 1 = SHORT 0 = LONG */
-#define TXC_PREAMBLE_RTS_MAIN_SHORT 0x4000 /* RTS main rate preamble type 1 = SHORT 0 = LONG */
-#define TXC_PREAMBLE_DATA_FB_SHORT 0x2000 /* Main fallback rate preamble type
- * 1 = SHORT for OFDM/GF for MIMO
- * 0 = LONG for CCK/MM for MIMO
- */
+/*== MacTxControlHigh ==*/
+/* RTS fallback preamble type 1 = SHORT 0 = LONG */
+#define TXC_PREAMBLE_RTS_FB_SHORT 0x8000
+/* RTS main rate preamble type 1 = SHORT 0 = LONG */
+#define TXC_PREAMBLE_RTS_MAIN_SHORT 0x4000
+/*
+ * Main fallback rate preamble type
+ * 1 = SHORT for OFDM/GF for MIMO
+ * 0 = LONG for CCK/MM for MIMO
+ */
+#define TXC_PREAMBLE_DATA_FB_SHORT 0x2000
+
/* TXC_PREAMBLE_DATA_MAIN is in PhyTxControl bit 5 */
-#define TXC_AMPDU_FBR 0x1000 /* use fallback rate for this AMPDU */
+/* use fallback rate for this AMPDU */
+#define TXC_AMPDU_FBR 0x1000
#define TXC_SECKEY_MASK 0x0FF0
#define TXC_SECKEY_SHIFT 4
-#define TXC_ALT_TXPWR 0x0008 /* Use alternate txpwr defined at loc. M_ALT_TXPWR_IDX */
+/* Use alternate txpwr defined at loc. M_ALT_TXPWR_IDX */
+#define TXC_ALT_TXPWR 0x0008
#define TXC_SECTYPE_MASK 0x0007
#define TXC_SECTYPE_SHIFT 0
@@ -828,7 +871,7 @@ struct d11txh {
#define PHY_TXC1_MODE_SDM 3
/* PhyTxControl for HTphy that are different from Mimophy */
-#define PHY_TXC_HTANT_MASK 0x3fC0 /* bit 6, 7, 8, 9, 10, 11, 12, 13 */
+#define PHY_TXC_HTANT_MASK 0x3fC0 /* bits 6-13 */
/* XtraFrameTypes */
#define XFTS_RTS_FT_SHIFT 2
@@ -854,7 +897,6 @@ struct d11txh {
#define ABI_MAS_MRT_ANT_PTN_MASK 0x000f
/* tx status packet */
-typedef struct tx_status tx_status_t;
struct tx_status {
u16 framelen;
u16 PAD;
@@ -864,7 +906,7 @@ struct tx_status {
u16 sequence;
u16 phyerr;
u16 ackphyrxsh;
-} __attribute__((packed));
+} __packed;
#define TXSTATUS_LEN 16
@@ -874,23 +916,23 @@ struct tx_status {
#define TX_STATUS_RTS_RTX_MASK 0x0F00
#define TX_STATUS_RTS_RTX_SHIFT 8
#define TX_STATUS_MASK 0x00FE
-#define TX_STATUS_PMINDCTD (1 << 7) /* PM mode indicated to AP */
-#define TX_STATUS_INTERMEDIATE (1 << 6) /* intermediate or 1st ampdu pkg */
-#define TX_STATUS_AMPDU (1 << 5) /* AMPDU status */
-#define TX_STATUS_SUPR_MASK 0x1C /* suppress status bits (4:2) */
+#define TX_STATUS_PMINDCTD (1 << 7) /* PM mode indicated to AP */
+#define TX_STATUS_INTERMEDIATE (1 << 6) /* intermediate or 1st ampdu pkg */
+#define TX_STATUS_AMPDU (1 << 5) /* AMPDU status */
+#define TX_STATUS_SUPR_MASK 0x1C /* suppress status bits (4:2) */
#define TX_STATUS_SUPR_SHIFT 2
-#define TX_STATUS_ACK_RCV (1 << 1) /* ACK received */
-#define TX_STATUS_VALID (1 << 0) /* Tx status valid */
+#define TX_STATUS_ACK_RCV (1 << 1) /* ACK received */
+#define TX_STATUS_VALID (1 << 0) /* Tx status valid */
#define TX_STATUS_NO_ACK 0
/* suppress status reason codes */
-#define TX_STATUS_SUPR_PMQ (1 << 2) /* PMQ entry */
-#define TX_STATUS_SUPR_FLUSH (2 << 2) /* flush request */
-#define TX_STATUS_SUPR_FRAG (3 << 2) /* previous frag failure */
-#define TX_STATUS_SUPR_TBTT (3 << 2) /* SHARED: Probe response supr for TBTT */
-#define TX_STATUS_SUPR_BADCH (4 << 2) /* channel mismatch */
-#define TX_STATUS_SUPR_EXPTIME (5 << 2) /* lifetime expiry */
-#define TX_STATUS_SUPR_UF (6 << 2) /* underflow */
+#define TX_STATUS_SUPR_PMQ (1 << 2) /* PMQ entry */
+#define TX_STATUS_SUPR_FLUSH (2 << 2) /* flush request */
+#define TX_STATUS_SUPR_FRAG (3 << 2) /* previous frag failure */
+#define TX_STATUS_SUPR_TBTT (3 << 2) /* SHARED: Probe resp supr for TBTT */
+#define TX_STATUS_SUPR_BADCH (4 << 2) /* channel mismatch */
+#define TX_STATUS_SUPR_EXPTIME (5 << 2) /* lifetime expiry */
+#define TX_STATUS_SUPR_UF (6 << 2) /* underflow */
/* Unexpected tx status for rate update */
#define TX_STATUS_UNEXP(status) \
@@ -951,8 +993,8 @@ struct tx_status {
#define ADDR_BMP_RA (1 << 0) /* Receiver Address (RA) */
#define ADDR_BMP_TA (1 << 1) /* Transmitter Address (TA) */
#define ADDR_BMP_BSSID (1 << 2) /* BSSID */
-#define ADDR_BMP_AP (1 << 3) /* Infra-BSS Access Point (AP) */
-#define ADDR_BMP_STA (1 << 4) /* Infra-BSS Station (STA) */
+#define ADDR_BMP_AP (1 << 3) /* Infra-BSS Access Point */
+#define ADDR_BMP_STA (1 << 4) /* Infra-BSS Station */
#define ADDR_BMP_RESERVED1 (1 << 5)
#define ADDR_BMP_RESERVED2 (1 << 6)
#define ADDR_BMP_RESERVED3 (1 << 7)
@@ -1024,9 +1066,10 @@ struct tx_status {
#define T_BCN0_TPL_BASE (0x34 * 2)
#define T_PRS_TPL_BASE (0x134 * 2)
#define T_BCN1_TPL_BASE (0x234 * 2)
-#define T_TX_FIFO_TXRAM_BASE (T_ACTS_TPL_BASE + (TXFIFO_START_BLK * TXFIFO_SIZE_UNIT))
+#define T_TX_FIFO_TXRAM_BASE (T_ACTS_TPL_BASE + \
+ (TXFIFO_START_BLK * TXFIFO_SIZE_UNIT))
-#define T_BA_TPL_BASE T_QNULL_TPL_BASE /* template area for BA */
+#define T_BA_TPL_BASE T_QNULL_TPL_BASE /* template area for BA */
#define T_RAM_ACCESS_SZ 4 /* template ram is 4 byte access only */
@@ -1160,25 +1203,25 @@ struct tx_status {
#define M_TX_IDLE_BUSY_RATIO_X_16_OFDM (0x5A * 2)
/* CW RSSI for LCNPHY */
-#define M_LCN_RSSI_0 0x1332
-#define M_LCN_RSSI_1 0x1338
-#define M_LCN_RSSI_2 0x133e
-#define M_LCN_RSSI_3 0x1344
+#define M_LCN_RSSI_0 0x1332
+#define M_LCN_RSSI_1 0x1338
+#define M_LCN_RSSI_2 0x133e
+#define M_LCN_RSSI_3 0x1344
/* SNR for LCNPHY */
-#define M_LCN_SNR_A_0 0x1334
-#define M_LCN_SNR_B_0 0x1336
+#define M_LCN_SNR_A_0 0x1334
+#define M_LCN_SNR_B_0 0x1336
-#define M_LCN_SNR_A_1 0x133a
-#define M_LCN_SNR_B_1 0x133c
+#define M_LCN_SNR_A_1 0x133a
+#define M_LCN_SNR_B_1 0x133c
-#define M_LCN_SNR_A_2 0x1340
-#define M_LCN_SNR_B_2 0x1342
+#define M_LCN_SNR_A_2 0x1340
+#define M_LCN_SNR_B_2 0x1342
-#define M_LCN_SNR_A_3 0x1346
-#define M_LCN_SNR_B_3 0x1348
+#define M_LCN_SNR_A_3 0x1346
+#define M_LCN_SNR_B_3 0x1348
-#define M_LCN_LAST_RESET (81*2)
+#define M_LCN_LAST_RESET (81*2)
#define M_LCN_LAST_LOC (63*2)
#define M_LCNPHY_RESET_STATUS (4902)
#define M_LCNPHY_DSC_TIME (0x98d*2)
@@ -1219,14 +1262,18 @@ struct tx_status {
#define WATCHDOG_8TU_MAX 10
/* Manufacturing Test Variables */
-#define M_PKTENG_CTRL (0x6c * 2) /* PER test mode */
-#define M_PKTENG_IFS (0x6d * 2) /* IFS for TX mode */
-#define M_PKTENG_FRMCNT_LO (0x6e * 2) /* Lower word of tx frmcnt/rx lostcnt */
-#define M_PKTENG_FRMCNT_HI (0x6f * 2) /* Upper word of tx frmcnt/rx lostcnt */
+/* PER test mode */
+#define M_PKTENG_CTRL (0x6c * 2)
+/* IFS for TX mode */
+#define M_PKTENG_IFS (0x6d * 2)
+/* Lower word of tx frmcnt/rx lostcnt */
+#define M_PKTENG_FRMCNT_LO (0x6e * 2)
+/* Upper word of tx frmcnt/rx lostcnt */
+#define M_PKTENG_FRMCNT_HI (0x6f * 2)
/* Index variation in vbat ripple */
-#define M_LCN_PWR_IDX_MAX (0x67 * 2) /* highest index read by ucode */
-#define M_LCN_PWR_IDX_MIN (0x66 * 2) /* lowest index read by ucode */
+#define M_LCN_PWR_IDX_MAX (0x67 * 2) /* highest index read by ucode */
+#define M_LCN_PWR_IDX_MIN (0x66 * 2) /* lowest index read by ucode */
/* M_PKTENG_CTRL bit definitions */
#define M_PKTENG_MODE_TX 0x0001
@@ -1235,11 +1282,14 @@ struct tx_status {
#define M_PKTENG_MODE_RX 0x0002
#define M_PKTENG_MODE_RX_WITH_ACK 0x0402
#define M_PKTENG_MODE_MASK 0x0003
-#define M_PKTENG_FRMCNT_VLD 0x0100 /* TX frames indicated in the frmcnt reg */
+/* TX frames indicated in the frmcnt reg */
+#define M_PKTENG_FRMCNT_VLD 0x0100
/* Sample Collect parameters (bitmap and type) */
-#define M_SMPL_COL_BMP (0x37d * 2) /* Trigger bitmap for sample collect */
-#define M_SMPL_COL_CTL (0x3b2 * 2) /* Sample collect type */
+/* Trigger bitmap for sample collect */
+#define M_SMPL_COL_BMP (0x37d * 2)
+/* Sample collect type */
+#define M_SMPL_COL_CTL (0x3b2 * 2)
#define ANTSEL_CLKDIV_4MHZ 6
#define MIMO_ANTSEL_BUSY 0x4000 /* bit 14 (busy) */
@@ -1247,7 +1297,6 @@ struct tx_status {
#define MIMO_ANTSEL_WAIT 50 /* 50us wait */
#define MIMO_ANTSEL_OVERRIDE 0x8000 /* flag */
-typedef struct shm_acparams shm_acparams_t;
struct shm_acparams {
u16 txop;
u16 cwmin;
@@ -1258,7 +1307,7 @@ struct shm_acparams {
u16 reggap;
u16 status;
u16 rsvd[8];
-} __attribute__((packed));
+} __packed;
#define M_EDCF_QLEN (16 * 2)
#define WME_STATUS_NEWAC (1 << 8)
@@ -1272,27 +1321,36 @@ struct shm_acparams {
#define MHF5 4 /* Hostflag 5 index */
/* Flags in M_HOST_FLAGS */
-#define MHF1_ANTDIV 0x0001 /* Enable ucode antenna diversity help */
-#define MHF1_EDCF 0x0100 /* Enable EDCF access control */
+/* Enable ucode antenna diversity help */
+#define MHF1_ANTDIV 0x0001
+/* Enable EDCF access control */
+#define MHF1_EDCF 0x0100
#define MHF1_IQSWAP_WAR 0x0200
-#define MHF1_FORCEFASTCLK 0x0400 /* Disable Slow clock request, for corerev < 11 */
+/* Disable Slow clock request, for corerev < 11 */
+#define MHF1_FORCEFASTCLK 0x0400
/* Flags in M_HOST_FLAGS2 */
-#define MHF2_PCISLOWCLKWAR 0x0008 /* PR16165WAR : Enable ucode PCI slow clock WAR */
-#define MHF2_TXBCMC_NOW 0x0040 /* Flush BCMC FIFO immediately */
-#define MHF2_HWPWRCTL 0x0080 /* Enable ucode/hw power control */
+
+/* Flush BCMC FIFO immediately */
+#define MHF2_TXBCMC_NOW 0x0040
+/* Enable ucode/hw power control */
+#define MHF2_HWPWRCTL 0x0080
#define MHF2_NPHY40MHZ_WAR 0x0800
/* Flags in M_HOST_FLAGS3 */
-#define MHF3_ANTSEL_EN 0x0001 /* enabled mimo antenna selection */
-#define MHF3_ANTSEL_MODE 0x0002 /* antenna selection mode: 0: 2x3, 1: 2x4 */
+/* enabled mimo antenna selection */
+#define MHF3_ANTSEL_EN 0x0001
+/* antenna selection mode: 0: 2x3, 1: 2x4 */
+#define MHF3_ANTSEL_MODE 0x0002
#define MHF3_RESERVED1 0x0004
#define MHF3_RESERVED2 0x0008
#define MHF3_NPHY_MLADV_WAR 0x0010
/* Flags in M_HOST_FLAGS4 */
-#define MHF4_BPHY_TXCORE0 0x0080 /* force bphy Tx on core 0 (board level WAR) */
-#define MHF4_EXTPA_ENABLE 0x4000 /* for 4313A0 FEM boards */
+/* force bphy Tx on core 0 (board level WAR) */
+#define MHF4_BPHY_TXCORE0 0x0080
+/* for 4313A0 FEM boards */
+#define MHF4_EXTPA_ENABLE 0x4000
/* Flags in M_HOST_FLAGS5 */
#define MHF5_4313_GPIOCTRL 0x0001
@@ -1305,54 +1363,80 @@ struct shm_acparams {
#define M_PHY_NOISE (0x037 * 2)
#define PHY_NOISE_MASK 0x00ff
-/* Receive Frame Data Header for 802.11b DCF-only frames */
-typedef struct d11rxhdr d11rxhdr_t;
+/*
+ * Receive Frame Data Header for 802.11b DCF-only frames
+ *
+ * RxFrameSize: Actual byte length of the frame data received
+ * PAD: padding (not used)
+ * PhyRxStatus_0: PhyRxStatus 15:0
+ * PhyRxStatus_1: PhyRxStatus 31:16
+ * PhyRxStatus_2: PhyRxStatus 47:32
+ * PhyRxStatus_3: PhyRxStatus 63:48
+ * PhyRxStatus_4: PhyRxStatus 79:64
+ * PhyRxStatus_5: PhyRxStatus 95:80
+ * RxStatus1: MAC Rx Status
+ * RxStatus2: extended MAC Rx status
+ * RxTSFTime: RxTSFTime time of first MAC symbol + M_PHY_PLCPRX_DLY
+ * RxChan: gain code, channel radio code, and phy type
+ */
+struct d11rxhdr_le {
+ __le16 RxFrameSize;
+ u16 PAD;
+ __le16 PhyRxStatus_0;
+ __le16 PhyRxStatus_1;
+ __le16 PhyRxStatus_2;
+ __le16 PhyRxStatus_3;
+ __le16 PhyRxStatus_4;
+ __le16 PhyRxStatus_5;
+ __le16 RxStatus1;
+ __le16 RxStatus2;
+ __le16 RxTSFTime;
+ __le16 RxChan;
+} __packed;
+
struct d11rxhdr {
- u16 RxFrameSize; /* Actual byte length of the frame data received */
+ u16 RxFrameSize;
u16 PAD;
- u16 PhyRxStatus_0; /* PhyRxStatus 15:0 */
- u16 PhyRxStatus_1; /* PhyRxStatus 31:16 */
- u16 PhyRxStatus_2; /* PhyRxStatus 47:32 */
- u16 PhyRxStatus_3; /* PhyRxStatus 63:48 */
- u16 PhyRxStatus_4; /* PhyRxStatus 79:64 */
- u16 PhyRxStatus_5; /* PhyRxStatus 95:80 */
- u16 RxStatus1; /* MAC Rx Status */
- u16 RxStatus2; /* extended MAC Rx status */
- u16 RxTSFTime; /* RxTSFTime time of first MAC symbol + M_PHY_PLCPRX_DLY */
- u16 RxChan; /* gain code, channel radio code, and phy type */
-} __attribute__((packed));
-
-#define RXHDR_LEN 24 /* sizeof d11rxhdr_t */
-#define FRAMELEN(h) ((h)->RxFrameSize)
-
-typedef struct wlc_d11rxhdr wlc_d11rxhdr_t;
-struct wlc_d11rxhdr {
- d11rxhdr_t rxhdr;
- u32 tsf_l; /* TSF_L reading */
- s8 rssi; /* computed instanteneous rssi in BMAC */
- s8 rxpwr0; /* obsoleted, place holder for legacy ROM code. use rxpwr[] */
- s8 rxpwr1; /* obsoleted, place holder for legacy ROM code. use rxpwr[] */
- s8 do_rssi_ma; /* do per-pkt sampling for per-antenna ma in HIGH */
- s8 rxpwr[WL_RSSI_ANT_MAX]; /* rssi for supported antennas */
-} __attribute__((packed));
+ u16 PhyRxStatus_0;
+ u16 PhyRxStatus_1;
+ u16 PhyRxStatus_2;
+ u16 PhyRxStatus_3;
+ u16 PhyRxStatus_4;
+ u16 PhyRxStatus_5;
+ u16 RxStatus1;
+ u16 RxStatus2;
+ u16 RxTSFTime;
+ u16 RxChan;
+} __packed;
/* PhyRxStatus_0: */
-#define PRXS0_FT_MASK 0x0003 /* NPHY only: CCK, OFDM, preN, N */
-#define PRXS0_CLIP_MASK 0x000C /* NPHY only: clip count adjustment steps by AGC */
+/* NPHY only: CCK, OFDM, preN, N */
+#define PRXS0_FT_MASK 0x0003
+/* NPHY only: clip count adjustment steps by AGC */
+#define PRXS0_CLIP_MASK 0x000C
#define PRXS0_CLIP_SHIFT 2
-#define PRXS0_UNSRATE 0x0010 /* PHY received a frame with unsupported rate */
-#define PRXS0_RXANT_UPSUBBAND 0x0020 /* GPHY: rx ant, NPHY: upper sideband */
-#define PRXS0_LCRS 0x0040 /* CCK frame only: lost crs during cck frame reception */
-#define PRXS0_SHORTH 0x0080 /* Short Preamble */
-#define PRXS0_PLCPFV 0x0100 /* PLCP violation */
-#define PRXS0_PLCPHCF 0x0200 /* PLCP header integrity check failed */
-#define PRXS0_GAIN_CTL 0x4000 /* legacy PHY gain control */
-#define PRXS0_ANTSEL_MASK 0xF000 /* NPHY: Antennas used for received frame, bitmask */
+/* PHY received a frame with unsupported rate */
+#define PRXS0_UNSRATE 0x0010
+/* GPHY: rx ant, NPHY: upper sideband */
+#define PRXS0_RXANT_UPSUBBAND 0x0020
+/* CCK frame only: lost crs during cck frame reception */
+#define PRXS0_LCRS 0x0040
+/* Short Preamble */
+#define PRXS0_SHORTH 0x0080
+/* PLCP violation */
+#define PRXS0_PLCPFV 0x0100
+/* PLCP header integrity check failed */
+#define PRXS0_PLCPHCF 0x0200
+/* legacy PHY gain control */
+#define PRXS0_GAIN_CTL 0x4000
+/* NPHY: Antennas used for received frame, bitmask */
+#define PRXS0_ANTSEL_MASK 0xF000
#define PRXS0_ANTSEL_SHIFT 0x12
/* subfield PRXS0_FT_MASK */
#define PRXS0_CCK 0x0000
-#define PRXS0_OFDM 0x0001 /* valid only for G phy, use rxh->RxChan for A phy */
+/* valid only for G phy, use rxh->RxChan for A phy */
+#define PRXS0_OFDM 0x0001
#define PRXS0_PREN 0x0002
#define PRXS0_STDN 0x0003
@@ -1379,35 +1463,51 @@ struct wlc_d11rxhdr {
#define PRXS0_UNUSED 0xF000 /* unused and not defined; set to 0 */
/* htphy PhyRxStatus_1: */
-#define PRXS1_HTPHY_CORE_MASK 0x000F /* core enables for {3..0}, 0=disabled, 1=enabled */
-#define PRXS1_HTPHY_ANTCFG_MASK 0x00F0 /* antenna configation */
-#define PRXS1_HTPHY_MMPLCPLenL_MASK 0xFF00 /* Mixmode PLCP Length low byte mask */
+/* core enables for {3..0}, 0=disabled, 1=enabled */
+#define PRXS1_HTPHY_CORE_MASK 0x000F
+/* antenna configation */
+#define PRXS1_HTPHY_ANTCFG_MASK 0x00F0
+/* Mixmode PLCP Length low byte mask */
+#define PRXS1_HTPHY_MMPLCPLenL_MASK 0xFF00
/* htphy PhyRxStatus_2: */
-#define PRXS2_HTPHY_MMPLCPLenH_MASK 0x000F /* Mixmode PLCP Length high byte maskw */
-#define PRXS2_HTPHY_MMPLCH_RATE_MASK 0x00F0 /* Mixmode PLCP rate mask */
-#define PRXS2_HTPHY_RXPWR_ANT0 0xFF00 /* Rx power on core 0 */
+/* Mixmode PLCP Length high byte maskw */
+#define PRXS2_HTPHY_MMPLCPLenH_MASK 0x000F
+/* Mixmode PLCP rate mask */
+#define PRXS2_HTPHY_MMPLCH_RATE_MASK 0x00F0
+/* Rx power on core 0 */
+#define PRXS2_HTPHY_RXPWR_ANT0 0xFF00
/* htphy PhyRxStatus_3: */
-#define PRXS3_HTPHY_RXPWR_ANT1 0x00FF /* Rx power on core 1 */
-#define PRXS3_HTPHY_RXPWR_ANT2 0xFF00 /* Rx power on core 2 */
+/* Rx power on core 1 */
+#define PRXS3_HTPHY_RXPWR_ANT1 0x00FF
+/* Rx power on core 2 */
+#define PRXS3_HTPHY_RXPWR_ANT2 0xFF00
/* htphy PhyRxStatus_4: */
-#define PRXS4_HTPHY_RXPWR_ANT3 0x00FF /* Rx power on core 3 */
-#define PRXS4_HTPHY_CFO 0xFF00 /* Coarse frequency offset */
+/* Rx power on core 3 */
+#define PRXS4_HTPHY_RXPWR_ANT3 0x00FF
+/* Coarse frequency offset */
+#define PRXS4_HTPHY_CFO 0xFF00
/* htphy PhyRxStatus_5: */
-#define PRXS5_HTPHY_FFO 0x00FF /* Fine frequency offset */
-#define PRXS5_HTPHY_AR 0xFF00 /* Advance Retard */
+/* Fine frequency offset */
+#define PRXS5_HTPHY_FFO 0x00FF
+/* Advance Retard */
+#define PRXS5_HTPHY_AR 0xFF00
-#define HTPHY_MMPLCPLen(rxs) ((((rxs)->PhyRxStatus_1 & PRXS1_HTPHY_MMPLCPLenL_MASK) >> 8) | \
+#define HTPHY_MMPLCPLen(rxs) \
+ ((((rxs)->PhyRxStatus_1 & PRXS1_HTPHY_MMPLCPLenL_MASK) >> 8) | \
(((rxs)->PhyRxStatus_2 & PRXS2_HTPHY_MMPLCPLenH_MASK) << 8))
/* Get Rx power on core 0 */
-#define HTPHY_RXPWR_ANT0(rxs) ((((rxs)->PhyRxStatus_2) & PRXS2_HTPHY_RXPWR_ANT0) >> 8)
+#define HTPHY_RXPWR_ANT0(rxs) \
+ ((((rxs)->PhyRxStatus_2) & PRXS2_HTPHY_RXPWR_ANT0) >> 8)
/* Get Rx power on core 1 */
-#define HTPHY_RXPWR_ANT1(rxs) (((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT1)
+#define HTPHY_RXPWR_ANT1(rxs) \
+ (((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT1)
/* Get Rx power on core 2 */
-#define HTPHY_RXPWR_ANT2(rxs) ((((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT2) >> 8)
+#define HTPHY_RXPWR_ANT2(rxs) \
+ ((((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT2) >> 8)
/* ucode RxStatus1: */
#define RXS_BCNSENT 0x8000
@@ -1415,7 +1515,8 @@ struct wlc_d11rxhdr {
#define RXS_SECKINDX_SHIFT 5
#define RXS_DECERR (1 << 4)
#define RXS_DECATMPT (1 << 3)
-#define RXS_PBPRES (1 << 2) /* PAD bytes to make IP data 4 bytes aligned */
+/* PAD bytes to make IP data 4 bytes aligned */
+#define RXS_PBPRES (1 << 2)
#define RXS_RESPFRAMETX (1 << 1)
#define RXS_FCSERR (1 << 0)
@@ -1448,16 +1549,17 @@ struct wlc_d11rxhdr {
#define M_PSM_SOFT_REGS 0x0
#define M_BOM_REV_MAJOR (M_PSM_SOFT_REGS + 0x0)
#define M_BOM_REV_MINOR (M_PSM_SOFT_REGS + 0x2)
-#define M_UCODE_DBGST (M_PSM_SOFT_REGS + 0x40) /* ucode debug status code */
-#define M_UCODE_MACSTAT (M_PSM_SOFT_REGS + 0xE0) /* macstat counters */
+#define M_UCODE_DBGST (M_PSM_SOFT_REGS + 0x40) /* ucode debug status code */
+#define M_UCODE_MACSTAT (M_PSM_SOFT_REGS + 0xE0) /* macstat counters */
-#define M_AGING_THRSH (0x3e * 2) /* max time waiting for medium before tx */
-#define M_MBURST_SIZE (0x40 * 2) /* max frames in a frameburst */
-#define M_MBURST_TXOP (0x41 * 2) /* max frameburst TXOP in unit of us */
-#define M_SYNTHPU_DLY (0x4a * 2) /* pre-wakeup for synthpu, default: 500 */
+#define M_AGING_THRSH (0x3e * 2) /* max time waiting for medium before tx */
+#define M_MBURST_SIZE (0x40 * 2) /* max frames in a frameburst */
+#define M_MBURST_TXOP (0x41 * 2) /* max frameburst TXOP in unit of us */
+#define M_SYNTHPU_DLY (0x4a * 2) /* pre-wakeup for synthpu, default: 500 */
#define M_PRETBTT (0x4b * 2)
-#define M_ALT_TXPWR_IDX (M_PSM_SOFT_REGS + (0x3b * 2)) /* offset to the target txpwr */
+/* offset to the target txpwr */
+#define M_ALT_TXPWR_IDX (M_PSM_SOFT_REGS + (0x3b * 2))
#define M_PHY_TX_FLT_PTR (M_PSM_SOFT_REGS + (0x3d * 2))
#define M_CTS_DURATION (M_PSM_SOFT_REGS + (0x5c * 2))
#define M_LP_RCCAL_OVR (M_PSM_SOFT_REGS + (0x6b * 2))
@@ -1466,78 +1568,83 @@ struct wlc_d11rxhdr {
#define M_RXSTATS_BLK_PTR (M_PSM_SOFT_REGS + (0x65 * 2))
/* ucode debug status codes */
-#define DBGST_INACTIVE 0 /* not valid really */
-#define DBGST_INIT 1 /* after zeroing SHM, before suspending at init */
-#define DBGST_ACTIVE 2 /* "normal" state */
-#define DBGST_SUSPENDED 3 /* suspended */
-#define DBGST_ASLEEP 4 /* asleep (PS mode) */
+/* not valid really */
+#define DBGST_INACTIVE 0
+/* after zeroing SHM, before suspending at init */
+#define DBGST_INIT 1
+/* "normal" state */
+#define DBGST_ACTIVE 2
+/* suspended */
+#define DBGST_SUSPENDED 3
+/* asleep (PS mode) */
+#define DBGST_ASLEEP 4
/* Scratch Reg defs */
-typedef enum {
+enum _ePsmScratchPadRegDefinitions {
S_RSV0 = 0,
S_RSV1,
S_RSV2,
- /* scratch registers for Dot11-contants */
- S_DOT11_CWMIN, /* CW-minimum 0x03 */
- S_DOT11_CWMAX, /* CW-maximum 0x04 */
- S_DOT11_CWCUR, /* CW-current 0x05 */
- S_DOT11_SRC_LMT, /* short retry count limit 0x06 */
- S_DOT11_LRC_LMT, /* long retry count limit 0x07 */
- S_DOT11_DTIMCOUNT, /* DTIM-count 0x08 */
-
- /* Tx-side scratch registers */
- S_SEQ_NUM, /* hardware sequence number reg 0x09 */
- S_SEQ_NUM_FRAG, /* seq-num for frags (Set at the start os MSDU 0x0A */
- S_FRMRETX_CNT, /* frame retx count 0x0B */
- S_SSRC, /* Station short retry count 0x0C */
- S_SLRC, /* Station long retry count 0x0D */
- S_EXP_RSP, /* Expected response frame 0x0E */
- S_OLD_BREM, /* Remaining backoff ctr 0x0F */
- S_OLD_CWWIN, /* saved-off CW-cur 0x10 */
- S_TXECTL, /* TXE-Ctl word constructed in scr-pad 0x11 */
- S_CTXTST, /* frm type-subtype as read from Tx-descr 0x12 */
-
- /* Rx-side scratch registers */
- S_RXTST, /* Type and subtype in Rxframe 0x13 */
+ /* offset 0x03: scratch registers for Dot11-contants */
+ S_DOT11_CWMIN, /* CW-minimum */
+ S_DOT11_CWMAX, /* CW-maximum */
+ S_DOT11_CWCUR, /* CW-current */
+ S_DOT11_SRC_LMT, /* short retry count limit */
+ S_DOT11_LRC_LMT, /* long retry count limit */
+ S_DOT11_DTIMCOUNT, /* DTIM-count */
+
+ /* offset 0x09: Tx-side scratch registers */
+ S_SEQ_NUM, /* hardware sequence number reg */
+ S_SEQ_NUM_FRAG, /* seq num for frags (at the start of MSDU) */
+ S_FRMRETX_CNT, /* frame retx count */
+ S_SSRC, /* Station short retry count */
+ S_SLRC, /* Station long retry count */
+ S_EXP_RSP, /* Expected response frame */
+ S_OLD_BREM, /* Remaining backoff ctr */
+ S_OLD_CWWIN, /* saved-off CW-cur */
+ S_TXECTL, /* TXE-Ctl word constructed in scr-pad */
+ S_CTXTST, /* frm type-subtype as read from Tx-descr */
+
+ /* offset 0x13: Rx-side scratch registers */
+ S_RXTST, /* Type and subtype in Rxframe */
/* Global state register */
- S_STREG, /* state storage actual bit maps below 0x14 */
-
- S_TXPWR_SUM, /* Tx power control: accumulator 0x15 */
- S_TXPWR_ITER, /* Tx power control: iteration 0x16 */
- S_RX_FRMTYPE, /* Rate and PHY type for frames 0x17 */
- S_THIS_AGG, /* Size of this AGG (A-MSDU) 0x18 */
-
- S_KEYINDX, /* 0x19 */
- S_RXFRMLEN, /* Receive MPDU length in bytes 0x1A */
-
- /* Receive TSF time stored in SCR */
- S_RXTSFTMRVAL_WD3, /* TSF value at the start of rx 0x1B */
- S_RXTSFTMRVAL_WD2, /* TSF value at the start of rx 0x1C */
- S_RXTSFTMRVAL_WD1, /* TSF value at the start of rx 0x1D */
- S_RXTSFTMRVAL_WD0, /* TSF value at the start of rx 0x1E */
- S_RXSSN, /* Received start seq number for A-MPDU BA 0x1F */
- S_RXQOSFLD, /* Rx-QoS field (if present) 0x20 */
-
- /* Scratch pad regs used in microcode as temp storage */
- S_TMP0, /* stmp0 0x21 */
- S_TMP1, /* stmp1 0x22 */
- S_TMP2, /* stmp2 0x23 */
- S_TMP3, /* stmp3 0x24 */
- S_TMP4, /* stmp4 0x25 */
- S_TMP5, /* stmp5 0x26 */
- S_PRQPENALTY_CTR, /* Probe response queue penalty counter 0x27 */
- S_ANTCNT, /* unsuccessful attempts on current ant. 0x28 */
- S_SYMBOL, /* flag for possible symbol ctl frames 0x29 */
- S_RXTP, /* rx frame type 0x2A */
- S_STREG2, /* extra state storage 0x2B */
- S_STREG3, /* even more extra state storage 0x2C */
- S_STREG4, /* ... 0x2D */
- S_STREG5, /* remember to initialize it to zero 0x2E */
+ S_STREG, /* state storage actual bit maps below */
+
+ S_TXPWR_SUM, /* Tx power control: accumulator */
+ S_TXPWR_ITER, /* Tx power control: iteration */
+ S_RX_FRMTYPE, /* Rate and PHY type for frames */
+ S_THIS_AGG, /* Size of this AGG (A-MSDU) */
+
+ S_KEYINDX,
+ S_RXFRMLEN, /* Receive MPDU length in bytes */
+
+ /* offset 0x1B: Receive TSF time stored in SCR */
+ S_RXTSFTMRVAL_WD3, /* TSF value at the start of rx */
+ S_RXTSFTMRVAL_WD2, /* TSF value at the start of rx */
+ S_RXTSFTMRVAL_WD1, /* TSF value at the start of rx */
+ S_RXTSFTMRVAL_WD0, /* TSF value at the start of rx */
+ S_RXSSN, /* Received start seq number for A-MPDU BA */
+ S_RXQOSFLD, /* Rx-QoS field (if present) */
+
+ /* offset 0x21: Scratch pad regs used in microcode as temp storage */
+ S_TMP0, /* stmp0 */
+ S_TMP1, /* stmp1 */
+ S_TMP2, /* stmp2 */
+ S_TMP3, /* stmp3 */
+ S_TMP4, /* stmp4 */
+ S_TMP5, /* stmp5 */
+ S_PRQPENALTY_CTR, /* Probe response queue penalty counter */
+ S_ANTCNT, /* unsuccessful attempts on current ant. */
+ S_SYMBOL, /* flag for possible symbol ctl frames */
+ S_RXTP, /* rx frame type */
+ S_STREG2, /* extra state storage */
+ S_STREG3, /* even more extra state storage */
+ S_STREG4, /* ... */
+ S_STREG5, /* remember to initialize it to zero */
S_ADJPWR_IDX,
- S_CUR_PTR, /* Temp pointer for A-MPDU re-Tx SHM table 0x32 */
+ S_CUR_PTR, /* Temp pointer for A-MPDU re-Tx SHM table */
S_REVID4, /* 0x33 */
S_INDX, /* 0x34 */
S_ADDR0, /* 0x35 */
@@ -1547,11 +1654,11 @@ typedef enum {
S_ADDR4, /* 0x39 */
S_ADDR5, /* 0x3A */
S_TMP6, /* 0x3B */
- S_KEYINDX_BU, /* Backup for Key index 0x3C */
- S_MFGTEST_TMP0, /* Temp register used for RX test calculations 0x3D */
- S_RXESN, /* Received end sequence number for A-MPDU BA 0x3E */
+ S_KEYINDX_BU, /* Backup for Key index */
+ S_MFGTEST_TMP0, /* Temp regs used for RX test calculations */
+ S_RXESN, /* Received end sequence number for A-MPDU BA */
S_STREG6, /* 0x3F */
-} ePsmScratchPadRegDefinitions;
+};
#define S_BEACON_INDX S_OLD_BREM
#define S_PRS_INDX S_OLD_CWWIN
@@ -1563,7 +1670,7 @@ typedef enum {
#define SLOW_CTRL_FD (1 << 8)
/* ucode mac statistic counters in shared memory */
-typedef struct macstat {
+struct macstat {
u16 txallfrm; /* 0x80 */
u16 txrtsfrm; /* 0x82 */
u16 txctsfrm; /* 0x84 */
@@ -1621,7 +1728,7 @@ typedef struct macstat {
u16 phywatchdog; /* 0xfa # of phy watchdog events */
u16 PAD;
u16 bphy_badplcp; /* bphy bad plcp */
-} macstat_t;
+};
/* dot11 core-specific control flags */
#define SICF_PCLKE 0x0004 /* PHY clock enable */
@@ -1643,7 +1750,8 @@ typedef struct macstat {
#define SISF_FCLKA 0x0004 /* FastClkAvailable */
#define SISF_DB_PHY 0x0008 /* Dualband phy */
-/* === End of MAC reg, Beginning of PHY(b/a/g/n) reg, radio and LPPHY regs are separated === */
+/* === End of MAC reg, Beginning of PHY(b/a/g/n) reg === */
+/* radio and LPPHY regs are separated */
#define BPHY_REG_OFT_BASE 0x0
/* offsets for indirect access to bphy registers */
@@ -1688,7 +1796,7 @@ typedef struct macstat {
#define BPHY_PEAK_ENERGY_HI 0x34
#define BPHY_SYNC_CTL 0x35
#define BPHY_TX_PWR_CTRL 0x36
-#define BPHY_TX_EST_PWR 0x37
+#define BPHY_TX_EST_PWR 0x37
#define BPHY_STEP 0x38
#define BPHY_WARMUP 0x39
#define BPHY_LMS_CFF_READ 0x3a
@@ -1770,4 +1878,21 @@ typedef struct macstat {
#define SHM_BYT_CNT 0x2 /* IHR location */
#define MAX_BYT_CNT 0x600 /* Maximum frame len */
-#endif /* _D11_H */
+struct d11cnt {
+ u32 txfrag;
+ u32 txmulti;
+ u32 txfail;
+ u32 txretry;
+ u32 txretrie;
+ u32 rxdup;
+ u32 txrts;
+ u32 txnocts;
+ u32 txnoack;
+ u32 rxfrag;
+ u32 rxmulti;
+ u32 rxcrc;
+ u32 txfrmsnt;
+ u32 rxundec;
+};
+
+#endif /* _BRCM_D11_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c
new file mode 100644
index 000000000000..6ebec8f42846
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c
@@ -0,0 +1,1426 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <brcmu_utils.h>
+#include <aiutils.h>
+#include "types.h"
+#include "dma.h"
+
+/*
+ * DMA hardware requires each descriptor ring to be 8kB aligned, and fit within
+ * a contiguous 8kB physical address.
+ */
+#define D64RINGALIGN_BITS 13
+#define D64MAXRINGSZ (1 << D64RINGALIGN_BITS)
+#define D64RINGALIGN (1 << D64RINGALIGN_BITS)
+
+#define D64MAXDD (D64MAXRINGSZ / sizeof(struct dma64desc))
+
+/* transmit channel control */
+#define D64_XC_XE 0x00000001 /* transmit enable */
+#define D64_XC_SE 0x00000002 /* transmit suspend request */
+#define D64_XC_LE 0x00000004 /* loopback enable */
+#define D64_XC_FL 0x00000010 /* flush request */
+#define D64_XC_PD 0x00000800 /* parity check disable */
+#define D64_XC_AE 0x00030000 /* address extension bits */
+#define D64_XC_AE_SHIFT 16
+
+/* transmit descriptor table pointer */
+#define D64_XP_LD_MASK 0x00000fff /* last valid descriptor */
+
+/* transmit channel status */
+#define D64_XS0_CD_MASK 0x00001fff /* current descriptor pointer */
+#define D64_XS0_XS_MASK 0xf0000000 /* transmit state */
+#define D64_XS0_XS_SHIFT 28
+#define D64_XS0_XS_DISABLED 0x00000000 /* disabled */
+#define D64_XS0_XS_ACTIVE 0x10000000 /* active */
+#define D64_XS0_XS_IDLE 0x20000000 /* idle wait */
+#define D64_XS0_XS_STOPPED 0x30000000 /* stopped */
+#define D64_XS0_XS_SUSP 0x40000000 /* suspend pending */
+
+#define D64_XS1_AD_MASK 0x00001fff /* active descriptor */
+#define D64_XS1_XE_MASK 0xf0000000 /* transmit errors */
+#define D64_XS1_XE_SHIFT 28
+#define D64_XS1_XE_NOERR 0x00000000 /* no error */
+#define D64_XS1_XE_DPE 0x10000000 /* descriptor protocol error */
+#define D64_XS1_XE_DFU 0x20000000 /* data fifo underrun */
+#define D64_XS1_XE_DTE 0x30000000 /* data transfer error */
+#define D64_XS1_XE_DESRE 0x40000000 /* descriptor read error */
+#define D64_XS1_XE_COREE 0x50000000 /* core error */
+
+/* receive channel control */
+/* receive enable */
+#define D64_RC_RE 0x00000001
+/* receive frame offset */
+#define D64_RC_RO_MASK 0x000000fe
+#define D64_RC_RO_SHIFT 1
+/* direct fifo receive (pio) mode */
+#define D64_RC_FM 0x00000100
+/* separate rx header descriptor enable */
+#define D64_RC_SH 0x00000200
+/* overflow continue */
+#define D64_RC_OC 0x00000400
+/* parity check disable */
+#define D64_RC_PD 0x00000800
+/* address extension bits */
+#define D64_RC_AE 0x00030000
+#define D64_RC_AE_SHIFT 16
+
+/* flags for dma controller */
+/* partity enable */
+#define DMA_CTRL_PEN (1 << 0)
+/* rx overflow continue */
+#define DMA_CTRL_ROC (1 << 1)
+/* allow rx scatter to multiple descriptors */
+#define DMA_CTRL_RXMULTI (1 << 2)
+/* Unframed Rx/Tx data */
+#define DMA_CTRL_UNFRAMED (1 << 3)
+
+/* receive descriptor table pointer */
+#define D64_RP_LD_MASK 0x00000fff /* last valid descriptor */
+
+/* receive channel status */
+#define D64_RS0_CD_MASK 0x00001fff /* current descriptor pointer */
+#define D64_RS0_RS_MASK 0xf0000000 /* receive state */
+#define D64_RS0_RS_SHIFT 28
+#define D64_RS0_RS_DISABLED 0x00000000 /* disabled */
+#define D64_RS0_RS_ACTIVE 0x10000000 /* active */
+#define D64_RS0_RS_IDLE 0x20000000 /* idle wait */
+#define D64_RS0_RS_STOPPED 0x30000000 /* stopped */
+#define D64_RS0_RS_SUSP 0x40000000 /* suspend pending */
+
+#define D64_RS1_AD_MASK 0x0001ffff /* active descriptor */
+#define D64_RS1_RE_MASK 0xf0000000 /* receive errors */
+#define D64_RS1_RE_SHIFT 28
+#define D64_RS1_RE_NOERR 0x00000000 /* no error */
+#define D64_RS1_RE_DPO 0x10000000 /* descriptor protocol error */
+#define D64_RS1_RE_DFU 0x20000000 /* data fifo overflow */
+#define D64_RS1_RE_DTE 0x30000000 /* data transfer error */
+#define D64_RS1_RE_DESRE 0x40000000 /* descriptor read error */
+#define D64_RS1_RE_COREE 0x50000000 /* core error */
+
+/* fifoaddr */
+#define D64_FA_OFF_MASK 0xffff /* offset */
+#define D64_FA_SEL_MASK 0xf0000 /* select */
+#define D64_FA_SEL_SHIFT 16
+#define D64_FA_SEL_XDD 0x00000 /* transmit dma data */
+#define D64_FA_SEL_XDP 0x10000 /* transmit dma pointers */
+#define D64_FA_SEL_RDD 0x40000 /* receive dma data */
+#define D64_FA_SEL_RDP 0x50000 /* receive dma pointers */
+#define D64_FA_SEL_XFD 0x80000 /* transmit fifo data */
+#define D64_FA_SEL_XFP 0x90000 /* transmit fifo pointers */
+#define D64_FA_SEL_RFD 0xc0000 /* receive fifo data */
+#define D64_FA_SEL_RFP 0xd0000 /* receive fifo pointers */
+#define D64_FA_SEL_RSD 0xe0000 /* receive frame status data */
+#define D64_FA_SEL_RSP 0xf0000 /* receive frame status pointers */
+
+/* descriptor control flags 1 */
+#define D64_CTRL_COREFLAGS 0x0ff00000 /* core specific flags */
+#define D64_CTRL1_EOT ((u32)1 << 28) /* end of descriptor table */
+#define D64_CTRL1_IOC ((u32)1 << 29) /* interrupt on completion */
+#define D64_CTRL1_EOF ((u32)1 << 30) /* end of frame */
+#define D64_CTRL1_SOF ((u32)1 << 31) /* start of frame */
+
+/* descriptor control flags 2 */
+/* buffer byte count. real data len must <= 16KB */
+#define D64_CTRL2_BC_MASK 0x00007fff
+/* address extension bits */
+#define D64_CTRL2_AE 0x00030000
+#define D64_CTRL2_AE_SHIFT 16
+/* parity bit */
+#define D64_CTRL2_PARITY 0x00040000
+
+/* control flags in the range [27:20] are core-specific and not defined here */
+#define D64_CTRL_CORE_MASK 0x0ff00000
+
+#define D64_RX_FRM_STS_LEN 0x0000ffff /* frame length mask */
+#define D64_RX_FRM_STS_OVFL 0x00800000 /* RxOverFlow */
+#define D64_RX_FRM_STS_DSCRCNT 0x0f000000 /* no. of descriptors used - 1 */
+#define D64_RX_FRM_STS_DATATYPE 0xf0000000 /* core-dependent data type */
+
+/*
+ * packet headroom necessary to accommodate the largest header
+ * in the system, (i.e TXOFF). By doing, we avoid the need to
+ * allocate an extra buffer for the header when bridging to WL.
+ * There is a compile time check in wlc.c which ensure that this
+ * value is at least as big as TXOFF. This value is used in
+ * dma_rxfill().
+ */
+
+#define BCMEXTRAHDROOM 172
+
+/* debug/trace */
+#ifdef BCMDBG
+#define DMA_ERROR(args) \
+ do { \
+ if (!(*di->msg_level & 1)) \
+ ; \
+ else \
+ printk args; \
+ } while (0)
+#define DMA_TRACE(args) \
+ do { \
+ if (!(*di->msg_level & 2)) \
+ ; \
+ else \
+ printk args; \
+ } while (0)
+#else
+#define DMA_ERROR(args)
+#define DMA_TRACE(args)
+#endif /* BCMDBG */
+
+#define DMA_NONE(args)
+
+#define MAXNAMEL 8 /* 8 char names */
+
+/* macros to convert between byte offsets and indexes */
+#define B2I(bytes, type) ((bytes) / sizeof(type))
+#define I2B(index, type) ((index) * sizeof(type))
+
+#define PCI32ADDR_HIGH 0xc0000000 /* address[31:30] */
+#define PCI32ADDR_HIGH_SHIFT 30 /* address[31:30] */
+
+#define PCI64ADDR_HIGH 0x80000000 /* address[63] */
+#define PCI64ADDR_HIGH_SHIFT 31 /* address[63] */
+
+/*
+ * DMA Descriptor
+ * Descriptors are only read by the hardware, never written back.
+ */
+struct dma64desc {
+ __le32 ctrl1; /* misc control bits & bufcount */
+ __le32 ctrl2; /* buffer count and address extension */
+ __le32 addrlow; /* memory address of the date buffer, bits 31:0 */
+ __le32 addrhigh; /* memory address of the date buffer, bits 63:32 */
+};
+
+/* dma engine software state */
+struct dma_info {
+ struct dma_pub dma; /* exported structure */
+ uint *msg_level; /* message level pointer */
+ char name[MAXNAMEL]; /* callers name for diag msgs */
+
+ struct pci_dev *pbus; /* bus handle */
+
+ bool dma64; /* this dma engine is operating in 64-bit mode */
+ bool addrext; /* this dma engine supports DmaExtendedAddrChanges */
+
+ /* 64-bit dma tx engine registers */
+ struct dma64regs __iomem *d64txregs;
+ /* 64-bit dma rx engine registers */
+ struct dma64regs __iomem *d64rxregs;
+ /* pointer to dma64 tx descriptor ring */
+ struct dma64desc *txd64;
+ /* pointer to dma64 rx descriptor ring */
+ struct dma64desc *rxd64;
+
+ u16 dmadesc_align; /* alignment requirement for dma descriptors */
+
+ u16 ntxd; /* # tx descriptors tunable */
+ u16 txin; /* index of next descriptor to reclaim */
+ u16 txout; /* index of next descriptor to post */
+ /* pointer to parallel array of pointers to packets */
+ struct sk_buff **txp;
+ /* Aligned physical address of descriptor ring */
+ dma_addr_t txdpa;
+ /* Original physical address of descriptor ring */
+ dma_addr_t txdpaorig;
+ u16 txdalign; /* #bytes added to alloc'd mem to align txd */
+ u32 txdalloc; /* #bytes allocated for the ring */
+ u32 xmtptrbase; /* When using unaligned descriptors, the ptr register
+ * is not just an index, it needs all 13 bits to be
+ * an offset from the addr register.
+ */
+
+ u16 nrxd; /* # rx descriptors tunable */
+ u16 rxin; /* index of next descriptor to reclaim */
+ u16 rxout; /* index of next descriptor to post */
+ /* pointer to parallel array of pointers to packets */
+ struct sk_buff **rxp;
+ /* Aligned physical address of descriptor ring */
+ dma_addr_t rxdpa;
+ /* Original physical address of descriptor ring */
+ dma_addr_t rxdpaorig;
+ u16 rxdalign; /* #bytes added to alloc'd mem to align rxd */
+ u32 rxdalloc; /* #bytes allocated for the ring */
+ u32 rcvptrbase; /* Base for ptr reg when using unaligned descriptors */
+
+ /* tunables */
+ unsigned int rxbufsize; /* rx buffer size in bytes, not including
+ * the extra headroom
+ */
+ uint rxextrahdrroom; /* extra rx headroom, reverseved to assist upper
+ * stack, e.g. some rx pkt buffers will be
+ * bridged to tx side without byte copying.
+ * The extra headroom needs to be large enough
+ * to fit txheader needs. Some dongle driver may
+ * not need it.
+ */
+ uint nrxpost; /* # rx buffers to keep posted */
+ unsigned int rxoffset; /* rxcontrol offset */
+ /* add to get dma address of descriptor ring, low 32 bits */
+ uint ddoffsetlow;
+ /* high 32 bits */
+ uint ddoffsethigh;
+ /* add to get dma address of data buffer, low 32 bits */
+ uint dataoffsetlow;
+ /* high 32 bits */
+ uint dataoffsethigh;
+ /* descriptor base need to be aligned or not */
+ bool aligndesc_4k;
+};
+
+/*
+ * default dma message level (if input msg_level
+ * pointer is null in dma_attach())
+ */
+static uint dma_msg_level;
+
+/* Check for odd number of 1's */
+static u32 parity32(__le32 data)
+{
+ /* no swap needed for counting 1's */
+ u32 par_data = *(u32 *)&data;
+
+ par_data ^= par_data >> 16;
+ par_data ^= par_data >> 8;
+ par_data ^= par_data >> 4;
+ par_data ^= par_data >> 2;
+ par_data ^= par_data >> 1;
+
+ return par_data & 1;
+}
+
+static bool dma64_dd_parity(struct dma64desc *dd)
+{
+ return parity32(dd->addrlow ^ dd->addrhigh ^ dd->ctrl1 ^ dd->ctrl2);
+}
+
+/* descriptor bumping functions */
+
+static uint xxd(uint x, uint n)
+{
+ return x & (n - 1); /* faster than %, but n must be power of 2 */
+}
+
+static uint txd(struct dma_info *di, uint x)
+{
+ return xxd(x, di->ntxd);
+}
+
+static uint rxd(struct dma_info *di, uint x)
+{
+ return xxd(x, di->nrxd);
+}
+
+static uint nexttxd(struct dma_info *di, uint i)
+{
+ return txd(di, i + 1);
+}
+
+static uint prevtxd(struct dma_info *di, uint i)
+{
+ return txd(di, i - 1);
+}
+
+static uint nextrxd(struct dma_info *di, uint i)
+{
+ return txd(di, i + 1);
+}
+
+static uint ntxdactive(struct dma_info *di, uint h, uint t)
+{
+ return txd(di, t-h);
+}
+
+static uint nrxdactive(struct dma_info *di, uint h, uint t)
+{
+ return rxd(di, t-h);
+}
+
+static uint _dma_ctrlflags(struct dma_info *di, uint mask, uint flags)
+{
+ uint dmactrlflags;
+
+ if (di == NULL) {
+ DMA_ERROR(("_dma_ctrlflags: NULL dma handle\n"));
+ return 0;
+ }
+
+ dmactrlflags = di->dma.dmactrlflags;
+ dmactrlflags &= ~mask;
+ dmactrlflags |= flags;
+
+ /* If trying to enable parity, check if parity is actually supported */
+ if (dmactrlflags & DMA_CTRL_PEN) {
+ u32 control;
+
+ control = R_REG(&di->d64txregs->control);
+ W_REG(&di->d64txregs->control,
+ control | D64_XC_PD);
+ if (R_REG(&di->d64txregs->control) & D64_XC_PD)
+ /* We *can* disable it so it is supported,
+ * restore control register
+ */
+ W_REG(&di->d64txregs->control,
+ control);
+ else
+ /* Not supported, don't allow it to be enabled */
+ dmactrlflags &= ~DMA_CTRL_PEN;
+ }
+
+ di->dma.dmactrlflags = dmactrlflags;
+
+ return dmactrlflags;
+}
+
+static bool _dma64_addrext(struct dma64regs __iomem *dma64regs)
+{
+ u32 w;
+ OR_REG(&dma64regs->control, D64_XC_AE);
+ w = R_REG(&dma64regs->control);
+ AND_REG(&dma64regs->control, ~D64_XC_AE);
+ return (w & D64_XC_AE) == D64_XC_AE;
+}
+
+/*
+ * return true if this dma engine supports DmaExtendedAddrChanges,
+ * otherwise false
+ */
+static bool _dma_isaddrext(struct dma_info *di)
+{
+ /* DMA64 supports full 32- or 64-bit operation. AE is always valid */
+
+ /* not all tx or rx channel are available */
+ if (di->d64txregs != NULL) {
+ if (!_dma64_addrext(di->d64txregs))
+ DMA_ERROR(("%s: _dma_isaddrext: DMA64 tx doesn't have "
+ "AE set\n", di->name));
+ return true;
+ } else if (di->d64rxregs != NULL) {
+ if (!_dma64_addrext(di->d64rxregs))
+ DMA_ERROR(("%s: _dma_isaddrext: DMA64 rx doesn't have "
+ "AE set\n", di->name));
+ return true;
+ }
+
+ return false;
+}
+
+static bool _dma_descriptor_align(struct dma_info *di)
+{
+ u32 addrl;
+
+ /* Check to see if the descriptors need to be aligned on 4K/8K or not */
+ if (di->d64txregs != NULL) {
+ W_REG(&di->d64txregs->addrlow, 0xff0);
+ addrl = R_REG(&di->d64txregs->addrlow);
+ if (addrl != 0)
+ return false;
+ } else if (di->d64rxregs != NULL) {
+ W_REG(&di->d64rxregs->addrlow, 0xff0);
+ addrl = R_REG(&di->d64rxregs->addrlow);
+ if (addrl != 0)
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Descriptor table must start at the DMA hardware dictated alignment, so
+ * allocated memory must be large enough to support this requirement.
+ */
+static void *dma_alloc_consistent(struct pci_dev *pdev, uint size,
+ u16 align_bits, uint *alloced,
+ dma_addr_t *pap)
+{
+ if (align_bits) {
+ u16 align = (1 << align_bits);
+ if (!IS_ALIGNED(PAGE_SIZE, align))
+ size += align;
+ *alloced = size;
+ }
+ return pci_alloc_consistent(pdev, size, pap);
+}
+
+static
+u8 dma_align_sizetobits(uint size)
+{
+ u8 bitpos = 0;
+ while (size >>= 1)
+ bitpos++;
+ return bitpos;
+}
+
+/* This function ensures that the DMA descriptor ring will not get allocated
+ * across Page boundary. If the allocation is done across the page boundary
+ * at the first time, then it is freed and the allocation is done at
+ * descriptor ring size aligned location. This will ensure that the ring will
+ * not cross page boundary
+ */
+static void *dma_ringalloc(struct dma_info *di, u32 boundary, uint size,
+ u16 *alignbits, uint *alloced,
+ dma_addr_t *descpa)
+{
+ void *va;
+ u32 desc_strtaddr;
+ u32 alignbytes = 1 << *alignbits;
+
+ va = dma_alloc_consistent(di->pbus, size, *alignbits, alloced, descpa);
+
+ if (NULL == va)
+ return NULL;
+
+ desc_strtaddr = (u32) roundup((unsigned long)va, alignbytes);
+ if (((desc_strtaddr + size - 1) & boundary) != (desc_strtaddr
+ & boundary)) {
+ *alignbits = dma_align_sizetobits(size);
+ pci_free_consistent(di->pbus, size, va, *descpa);
+ va = dma_alloc_consistent(di->pbus, size, *alignbits,
+ alloced, descpa);
+ }
+ return va;
+}
+
+static bool dma64_alloc(struct dma_info *di, uint direction)
+{
+ u16 size;
+ uint ddlen;
+ void *va;
+ uint alloced = 0;
+ u16 align;
+ u16 align_bits;
+
+ ddlen = sizeof(struct dma64desc);
+
+ size = (direction == DMA_TX) ? (di->ntxd * ddlen) : (di->nrxd * ddlen);
+ align_bits = di->dmadesc_align;
+ align = (1 << align_bits);
+
+ if (direction == DMA_TX) {
+ va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits,
+ &alloced, &di->txdpaorig);
+ if (va == NULL) {
+ DMA_ERROR(("%s: dma64_alloc: DMA_ALLOC_CONSISTENT(ntxd)"
+ " failed\n", di->name));
+ return false;
+ }
+ align = (1 << align_bits);
+ di->txd64 = (struct dma64desc *)
+ roundup((unsigned long)va, align);
+ di->txdalign = (uint) ((s8 *)di->txd64 - (s8 *) va);
+ di->txdpa = di->txdpaorig + di->txdalign;
+ di->txdalloc = alloced;
+ } else {
+ va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits,
+ &alloced, &di->rxdpaorig);
+ if (va == NULL) {
+ DMA_ERROR(("%s: dma64_alloc: DMA_ALLOC_CONSISTENT(nrxd)"
+ " failed\n", di->name));
+ return false;
+ }
+ align = (1 << align_bits);
+ di->rxd64 = (struct dma64desc *)
+ roundup((unsigned long)va, align);
+ di->rxdalign = (uint) ((s8 *)di->rxd64 - (s8 *) va);
+ di->rxdpa = di->rxdpaorig + di->rxdalign;
+ di->rxdalloc = alloced;
+ }
+
+ return true;
+}
+
+static bool _dma_alloc(struct dma_info *di, uint direction)
+{
+ return dma64_alloc(di, direction);
+}
+
+struct dma_pub *dma_attach(char *name, struct si_pub *sih,
+ void __iomem *dmaregstx, void __iomem *dmaregsrx,
+ uint ntxd, uint nrxd,
+ uint rxbufsize, int rxextheadroom,
+ uint nrxpost, uint rxoffset, uint *msg_level)
+{
+ struct dma_info *di;
+ uint size;
+
+ /* allocate private info structure */
+ di = kzalloc(sizeof(struct dma_info), GFP_ATOMIC);
+ if (di == NULL)
+ return NULL;
+
+ di->msg_level = msg_level ? msg_level : &dma_msg_level;
+
+
+ di->dma64 = ((ai_core_sflags(sih, 0, 0) & SISF_DMA64) == SISF_DMA64);
+
+ /* init dma reg pointer */
+ di->d64txregs = (struct dma64regs __iomem *) dmaregstx;
+ di->d64rxregs = (struct dma64regs __iomem *) dmaregsrx;
+
+ /*
+ * Default flags (which can be changed by the driver calling
+ * dma_ctrlflags before enable): For backwards compatibility
+ * both Rx Overflow Continue and Parity are DISABLED.
+ */
+ _dma_ctrlflags(di, DMA_CTRL_ROC | DMA_CTRL_PEN, 0);
+
+ DMA_TRACE(("%s: dma_attach: %s flags 0x%x ntxd %d nrxd %d "
+ "rxbufsize %d rxextheadroom %d nrxpost %d rxoffset %d "
+ "dmaregstx %p dmaregsrx %p\n", name, "DMA64",
+ di->dma.dmactrlflags, ntxd, nrxd, rxbufsize,
+ rxextheadroom, nrxpost, rxoffset, dmaregstx, dmaregsrx));
+
+ /* make a private copy of our callers name */
+ strncpy(di->name, name, MAXNAMEL);
+ di->name[MAXNAMEL - 1] = '\0';
+
+ di->pbus = ((struct si_info *)sih)->pbus;
+
+ /* save tunables */
+ di->ntxd = (u16) ntxd;
+ di->nrxd = (u16) nrxd;
+
+ /* the actual dma size doesn't include the extra headroom */
+ di->rxextrahdrroom =
+ (rxextheadroom == -1) ? BCMEXTRAHDROOM : rxextheadroom;
+ if (rxbufsize > BCMEXTRAHDROOM)
+ di->rxbufsize = (u16) (rxbufsize - di->rxextrahdrroom);
+ else
+ di->rxbufsize = (u16) rxbufsize;
+
+ di->nrxpost = (u16) nrxpost;
+ di->rxoffset = (u8) rxoffset;
+
+ /*
+ * figure out the DMA physical address offset for dd and data
+ * PCI/PCIE: they map silicon backplace address to zero
+ * based memory, need offset
+ * Other bus: use zero SI_BUS BIGENDIAN kludge: use sdram
+ * swapped region for data buffer, not descriptor
+ */
+ di->ddoffsetlow = 0;
+ di->dataoffsetlow = 0;
+ /* add offset for pcie with DMA64 bus */
+ di->ddoffsetlow = 0;
+ di->ddoffsethigh = SI_PCIE_DMA_H32;
+ di->dataoffsetlow = di->ddoffsetlow;
+ di->dataoffsethigh = di->ddoffsethigh;
+ /* WAR64450 : DMACtl.Addr ext fields are not supported in SDIOD core. */
+ if ((ai_coreid(sih) == SDIOD_CORE_ID)
+ && ((ai_corerev(sih) > 0) && (ai_corerev(sih) <= 2)))
+ di->addrext = 0;
+ else if ((ai_coreid(sih) == I2S_CORE_ID) &&
+ ((ai_corerev(sih) == 0) || (ai_corerev(sih) == 1)))
+ di->addrext = 0;
+ else
+ di->addrext = _dma_isaddrext(di);
+
+ /* does the descriptor need to be aligned and if yes, on 4K/8K or not */
+ di->aligndesc_4k = _dma_descriptor_align(di);
+ if (di->aligndesc_4k) {
+ di->dmadesc_align = D64RINGALIGN_BITS;
+ if ((ntxd < D64MAXDD / 2) && (nrxd < D64MAXDD / 2))
+ /* for smaller dd table, HW relax alignment reqmnt */
+ di->dmadesc_align = D64RINGALIGN_BITS - 1;
+ } else {
+ di->dmadesc_align = 4; /* 16 byte alignment */
+ }
+
+ DMA_NONE(("DMA descriptor align_needed %d, align %d\n",
+ di->aligndesc_4k, di->dmadesc_align));
+
+ /* allocate tx packet pointer vector */
+ if (ntxd) {
+ size = ntxd * sizeof(void *);
+ di->txp = kzalloc(size, GFP_ATOMIC);
+ if (di->txp == NULL)
+ goto fail;
+ }
+
+ /* allocate rx packet pointer vector */
+ if (nrxd) {
+ size = nrxd * sizeof(void *);
+ di->rxp = kzalloc(size, GFP_ATOMIC);
+ if (di->rxp == NULL)
+ goto fail;
+ }
+
+ /*
+ * allocate transmit descriptor ring, only need ntxd descriptors
+ * but it must be aligned
+ */
+ if (ntxd) {
+ if (!_dma_alloc(di, DMA_TX))
+ goto fail;
+ }
+
+ /*
+ * allocate receive descriptor ring, only need nrxd descriptors
+ * but it must be aligned
+ */
+ if (nrxd) {
+ if (!_dma_alloc(di, DMA_RX))
+ goto fail;
+ }
+
+ if ((di->ddoffsetlow != 0) && !di->addrext) {
+ if (di->txdpa > SI_PCI_DMA_SZ) {
+ DMA_ERROR(("%s: dma_attach: txdpa 0x%x: addrext not "
+ "supported\n", di->name, (u32)di->txdpa));
+ goto fail;
+ }
+ if (di->rxdpa > SI_PCI_DMA_SZ) {
+ DMA_ERROR(("%s: dma_attach: rxdpa 0x%x: addrext not "
+ "supported\n", di->name, (u32)di->rxdpa));
+ goto fail;
+ }
+ }
+
+ DMA_TRACE(("ddoffsetlow 0x%x ddoffsethigh 0x%x dataoffsetlow 0x%x "
+ "dataoffsethigh " "0x%x addrext %d\n", di->ddoffsetlow,
+ di->ddoffsethigh, di->dataoffsetlow, di->dataoffsethigh,
+ di->addrext));
+
+ return (struct dma_pub *) di;
+
+ fail:
+ dma_detach((struct dma_pub *)di);
+ return NULL;
+}
+
+static inline void
+dma64_dd_upd(struct dma_info *di, struct dma64desc *ddring,
+ dma_addr_t pa, uint outidx, u32 *flags, u32 bufcount)
+{
+ u32 ctrl2 = bufcount & D64_CTRL2_BC_MASK;
+
+ /* PCI bus with big(>1G) physical address, use address extension */
+ if ((di->dataoffsetlow == 0) || !(pa & PCI32ADDR_HIGH)) {
+ ddring[outidx].addrlow = cpu_to_le32(pa + di->dataoffsetlow);
+ ddring[outidx].addrhigh = cpu_to_le32(di->dataoffsethigh);
+ ddring[outidx].ctrl1 = cpu_to_le32(*flags);
+ ddring[outidx].ctrl2 = cpu_to_le32(ctrl2);
+ } else {
+ /* address extension for 32-bit PCI */
+ u32 ae;
+
+ ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT;
+ pa &= ~PCI32ADDR_HIGH;
+
+ ctrl2 |= (ae << D64_CTRL2_AE_SHIFT) & D64_CTRL2_AE;
+ ddring[outidx].addrlow = cpu_to_le32(pa + di->dataoffsetlow);
+ ddring[outidx].addrhigh = cpu_to_le32(di->dataoffsethigh);
+ ddring[outidx].ctrl1 = cpu_to_le32(*flags);
+ ddring[outidx].ctrl2 = cpu_to_le32(ctrl2);
+ }
+ if (di->dma.dmactrlflags & DMA_CTRL_PEN) {
+ if (dma64_dd_parity(&ddring[outidx]))
+ ddring[outidx].ctrl2 =
+ cpu_to_le32(ctrl2 | D64_CTRL2_PARITY);
+ }
+}
+
+/* !! may be called with core in reset */
+void dma_detach(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+
+ DMA_TRACE(("%s: dma_detach\n", di->name));
+
+ /* free dma descriptor rings */
+ if (di->txd64)
+ pci_free_consistent(di->pbus, di->txdalloc,
+ ((s8 *)di->txd64 - di->txdalign),
+ (di->txdpaorig));
+ if (di->rxd64)
+ pci_free_consistent(di->pbus, di->rxdalloc,
+ ((s8 *)di->rxd64 - di->rxdalign),
+ (di->rxdpaorig));
+
+ /* free packet pointer vectors */
+ kfree(di->txp);
+ kfree(di->rxp);
+
+ /* free our private info structure */
+ kfree(di);
+
+}
+
+/* initialize descriptor table base address */
+static void
+_dma_ddtable_init(struct dma_info *di, uint direction, dma_addr_t pa)
+{
+ if (!di->aligndesc_4k) {
+ if (direction == DMA_TX)
+ di->xmtptrbase = pa;
+ else
+ di->rcvptrbase = pa;
+ }
+
+ if ((di->ddoffsetlow == 0)
+ || !(pa & PCI32ADDR_HIGH)) {
+ if (direction == DMA_TX) {
+ W_REG(&di->d64txregs->addrlow, pa + di->ddoffsetlow);
+ W_REG(&di->d64txregs->addrhigh, di->ddoffsethigh);
+ } else {
+ W_REG(&di->d64rxregs->addrlow, pa + di->ddoffsetlow);
+ W_REG(&di->d64rxregs->addrhigh, di->ddoffsethigh);
+ }
+ } else {
+ /* DMA64 32bits address extension */
+ u32 ae;
+
+ /* shift the high bit(s) from pa to ae */
+ ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT;
+ pa &= ~PCI32ADDR_HIGH;
+
+ if (direction == DMA_TX) {
+ W_REG(&di->d64txregs->addrlow, pa + di->ddoffsetlow);
+ W_REG(&di->d64txregs->addrhigh, di->ddoffsethigh);
+ SET_REG(&di->d64txregs->control,
+ D64_XC_AE, (ae << D64_XC_AE_SHIFT));
+ } else {
+ W_REG(&di->d64rxregs->addrlow, pa + di->ddoffsetlow);
+ W_REG(&di->d64rxregs->addrhigh, di->ddoffsethigh);
+ SET_REG(&di->d64rxregs->control,
+ D64_RC_AE, (ae << D64_RC_AE_SHIFT));
+ }
+ }
+}
+
+static void _dma_rxenable(struct dma_info *di)
+{
+ uint dmactrlflags = di->dma.dmactrlflags;
+ u32 control;
+
+ DMA_TRACE(("%s: dma_rxenable\n", di->name));
+
+ control =
+ (R_REG(&di->d64rxregs->control) & D64_RC_AE) |
+ D64_RC_RE;
+
+ if ((dmactrlflags & DMA_CTRL_PEN) == 0)
+ control |= D64_RC_PD;
+
+ if (dmactrlflags & DMA_CTRL_ROC)
+ control |= D64_RC_OC;
+
+ W_REG(&di->d64rxregs->control,
+ ((di->rxoffset << D64_RC_RO_SHIFT) | control));
+}
+
+void dma_rxinit(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+
+ DMA_TRACE(("%s: dma_rxinit\n", di->name));
+
+ if (di->nrxd == 0)
+ return;
+
+ di->rxin = di->rxout = 0;
+
+ /* clear rx descriptor ring */
+ memset(di->rxd64, '\0', di->nrxd * sizeof(struct dma64desc));
+
+ /* DMA engine with out alignment requirement requires table to be inited
+ * before enabling the engine
+ */
+ if (!di->aligndesc_4k)
+ _dma_ddtable_init(di, DMA_RX, di->rxdpa);
+
+ _dma_rxenable(di);
+
+ if (di->aligndesc_4k)
+ _dma_ddtable_init(di, DMA_RX, di->rxdpa);
+}
+
+static struct sk_buff *dma64_getnextrxp(struct dma_info *di, bool forceall)
+{
+ uint i, curr;
+ struct sk_buff *rxp;
+ dma_addr_t pa;
+
+ i = di->rxin;
+
+ /* return if no packets posted */
+ if (i == di->rxout)
+ return NULL;
+
+ curr =
+ B2I(((R_REG(&di->d64rxregs->status0) & D64_RS0_CD_MASK) -
+ di->rcvptrbase) & D64_RS0_CD_MASK, struct dma64desc);
+
+ /* ignore curr if forceall */
+ if (!forceall && (i == curr))
+ return NULL;
+
+ /* get the packet pointer that corresponds to the rx descriptor */
+ rxp = di->rxp[i];
+ di->rxp[i] = NULL;
+
+ pa = le32_to_cpu(di->rxd64[i].addrlow) - di->dataoffsetlow;
+
+ /* clear this packet from the descriptor ring */
+ pci_unmap_single(di->pbus, pa, di->rxbufsize, PCI_DMA_FROMDEVICE);
+
+ di->rxd64[i].addrlow = cpu_to_le32(0xdeadbeef);
+ di->rxd64[i].addrhigh = cpu_to_le32(0xdeadbeef);
+
+ di->rxin = nextrxd(di, i);
+
+ return rxp;
+}
+
+static struct sk_buff *_dma_getnextrxp(struct dma_info *di, bool forceall)
+{
+ if (di->nrxd == 0)
+ return NULL;
+
+ return dma64_getnextrxp(di, forceall);
+}
+
+/*
+ * !! rx entry routine
+ * returns a pointer to the next frame received, or NULL if there are no more
+ * if DMA_CTRL_RXMULTI is defined, DMA scattering(multiple buffers) is
+ * supported with pkts chain
+ * otherwise, it's treated as giant pkt and will be tossed.
+ * The DMA scattering starts with normal DMA header, followed by first
+ * buffer data. After it reaches the max size of buffer, the data continues
+ * in next DMA descriptor buffer WITHOUT DMA header
+ */
+struct sk_buff *dma_rx(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+ struct sk_buff *p, *head, *tail;
+ uint len;
+ uint pkt_len;
+ int resid = 0;
+
+ next_frame:
+ head = _dma_getnextrxp(di, false);
+ if (head == NULL)
+ return NULL;
+
+ len = le16_to_cpu(*(__le16 *) (head->data));
+ DMA_TRACE(("%s: dma_rx len %d\n", di->name, len));
+ dma_spin_for_len(len, head);
+
+ /* set actual length */
+ pkt_len = min((di->rxoffset + len), di->rxbufsize);
+ __skb_trim(head, pkt_len);
+ resid = len - (di->rxbufsize - di->rxoffset);
+
+ /* check for single or multi-buffer rx */
+ if (resid > 0) {
+ tail = head;
+ while ((resid > 0) && (p = _dma_getnextrxp(di, false))) {
+ tail->next = p;
+ pkt_len = min_t(uint, resid, di->rxbufsize);
+ __skb_trim(p, pkt_len);
+
+ tail = p;
+ resid -= di->rxbufsize;
+ }
+
+#ifdef BCMDBG
+ if (resid > 0) {
+ uint cur;
+ cur =
+ B2I(((R_REG(&di->d64rxregs->status0) &
+ D64_RS0_CD_MASK) -
+ di->rcvptrbase) & D64_RS0_CD_MASK,
+ struct dma64desc);
+ DMA_ERROR(("dma_rx, rxin %d rxout %d, hw_curr %d\n",
+ di->rxin, di->rxout, cur));
+ }
+#endif /* BCMDBG */
+
+ if ((di->dma.dmactrlflags & DMA_CTRL_RXMULTI) == 0) {
+ DMA_ERROR(("%s: dma_rx: bad frame length (%d)\n",
+ di->name, len));
+ brcmu_pkt_buf_free_skb(head);
+ di->dma.rxgiants++;
+ goto next_frame;
+ }
+ }
+
+ return head;
+}
+
+static bool dma64_rxidle(struct dma_info *di)
+{
+ DMA_TRACE(("%s: dma_rxidle\n", di->name));
+
+ if (di->nrxd == 0)
+ return true;
+
+ return ((R_REG(&di->d64rxregs->status0) & D64_RS0_CD_MASK) ==
+ (R_REG(&di->d64rxregs->ptr) & D64_RS0_CD_MASK));
+}
+
+/*
+ * post receive buffers
+ * return false is refill failed completely and ring is empty this will stall
+ * the rx dma and user might want to call rxfill again asap. This unlikely
+ * happens on memory-rich NIC, but often on memory-constrained dongle
+ */
+bool dma_rxfill(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+ struct sk_buff *p;
+ u16 rxin, rxout;
+ u32 flags = 0;
+ uint n;
+ uint i;
+ dma_addr_t pa;
+ uint extra_offset = 0;
+ bool ring_empty;
+
+ ring_empty = false;
+
+ /*
+ * Determine how many receive buffers we're lacking
+ * from the full complement, allocate, initialize,
+ * and post them, then update the chip rx lastdscr.
+ */
+
+ rxin = di->rxin;
+ rxout = di->rxout;
+
+ n = di->nrxpost - nrxdactive(di, rxin, rxout);
+
+ DMA_TRACE(("%s: dma_rxfill: post %d\n", di->name, n));
+
+ if (di->rxbufsize > BCMEXTRAHDROOM)
+ extra_offset = di->rxextrahdrroom;
+
+ for (i = 0; i < n; i++) {
+ /*
+ * the di->rxbufsize doesn't include the extra headroom,
+ * we need to add it to the size to be allocated
+ */
+ p = brcmu_pkt_buf_get_skb(di->rxbufsize + extra_offset);
+
+ if (p == NULL) {
+ DMA_ERROR(("%s: dma_rxfill: out of rxbufs\n",
+ di->name));
+ if (i == 0 && dma64_rxidle(di)) {
+ DMA_ERROR(("%s: rxfill64: ring is empty !\n",
+ di->name));
+ ring_empty = true;
+ }
+ di->dma.rxnobuf++;
+ break;
+ }
+ /* reserve an extra headroom, if applicable */
+ if (extra_offset)
+ skb_pull(p, extra_offset);
+
+ /* Do a cached write instead of uncached write since DMA_MAP
+ * will flush the cache.
+ */
+ *(u32 *) (p->data) = 0;
+
+ pa = pci_map_single(di->pbus, p->data,
+ di->rxbufsize, PCI_DMA_FROMDEVICE);
+
+ /* save the free packet pointer */
+ di->rxp[rxout] = p;
+
+ /* reset flags for each descriptor */
+ flags = 0;
+ if (rxout == (di->nrxd - 1))
+ flags = D64_CTRL1_EOT;
+
+ dma64_dd_upd(di, di->rxd64, pa, rxout, &flags,
+ di->rxbufsize);
+ rxout = nextrxd(di, rxout);
+ }
+
+ di->rxout = rxout;
+
+ /* update the chip lastdscr pointer */
+ W_REG(&di->d64rxregs->ptr,
+ di->rcvptrbase + I2B(rxout, struct dma64desc));
+
+ return ring_empty;
+}
+
+void dma_rxreclaim(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+ struct sk_buff *p;
+
+ DMA_TRACE(("%s: dma_rxreclaim\n", di->name));
+
+ while ((p = _dma_getnextrxp(di, true)))
+ brcmu_pkt_buf_free_skb(p);
+}
+
+void dma_counterreset(struct dma_pub *pub)
+{
+ /* reset all software counters */
+ pub->rxgiants = 0;
+ pub->rxnobuf = 0;
+ pub->txnobuf = 0;
+}
+
+/* get the address of the var in order to change later */
+unsigned long dma_getvar(struct dma_pub *pub, const char *name)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+
+ if (!strcmp(name, "&txavail"))
+ return (unsigned long)&(di->dma.txavail);
+ return 0;
+}
+
+/* 64-bit DMA functions */
+
+void dma_txinit(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+ u32 control = D64_XC_XE;
+
+ DMA_TRACE(("%s: dma_txinit\n", di->name));
+
+ if (di->ntxd == 0)
+ return;
+
+ di->txin = di->txout = 0;
+ di->dma.txavail = di->ntxd - 1;
+
+ /* clear tx descriptor ring */
+ memset(di->txd64, '\0', (di->ntxd * sizeof(struct dma64desc)));
+
+ /* DMA engine with out alignment requirement requires table to be inited
+ * before enabling the engine
+ */
+ if (!di->aligndesc_4k)
+ _dma_ddtable_init(di, DMA_TX, di->txdpa);
+
+ if ((di->dma.dmactrlflags & DMA_CTRL_PEN) == 0)
+ control |= D64_XC_PD;
+ OR_REG(&di->d64txregs->control, control);
+
+ /* DMA engine with alignment requirement requires table to be inited
+ * before enabling the engine
+ */
+ if (di->aligndesc_4k)
+ _dma_ddtable_init(di, DMA_TX, di->txdpa);
+}
+
+void dma_txsuspend(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+
+ DMA_TRACE(("%s: dma_txsuspend\n", di->name));
+
+ if (di->ntxd == 0)
+ return;
+
+ OR_REG(&di->d64txregs->control, D64_XC_SE);
+}
+
+void dma_txresume(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+
+ DMA_TRACE(("%s: dma_txresume\n", di->name));
+
+ if (di->ntxd == 0)
+ return;
+
+ AND_REG(&di->d64txregs->control, ~D64_XC_SE);
+}
+
+bool dma_txsuspended(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+
+ return (di->ntxd == 0) ||
+ ((R_REG(&di->d64txregs->control) & D64_XC_SE) ==
+ D64_XC_SE);
+}
+
+void dma_txreclaim(struct dma_pub *pub, enum txd_range range)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+ struct sk_buff *p;
+
+ DMA_TRACE(("%s: dma_txreclaim %s\n", di->name,
+ (range == DMA_RANGE_ALL) ? "all" :
+ ((range ==
+ DMA_RANGE_TRANSMITTED) ? "transmitted" :
+ "transferred")));
+
+ if (di->txin == di->txout)
+ return;
+
+ while ((p = dma_getnexttxp(pub, range))) {
+ /* For unframed data, we don't have any packets to free */
+ if (!(di->dma.dmactrlflags & DMA_CTRL_UNFRAMED))
+ brcmu_pkt_buf_free_skb(p);
+ }
+}
+
+bool dma_txreset(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+ u32 status;
+
+ if (di->ntxd == 0)
+ return true;
+
+ /* suspend tx DMA first */
+ W_REG(&di->d64txregs->control, D64_XC_SE);
+ SPINWAIT(((status =
+ (R_REG(&di->d64txregs->status0) & D64_XS0_XS_MASK))
+ != D64_XS0_XS_DISABLED) && (status != D64_XS0_XS_IDLE)
+ && (status != D64_XS0_XS_STOPPED), 10000);
+
+ W_REG(&di->d64txregs->control, 0);
+ SPINWAIT(((status =
+ (R_REG(&di->d64txregs->status0) & D64_XS0_XS_MASK))
+ != D64_XS0_XS_DISABLED), 10000);
+
+ /* wait for the last transaction to complete */
+ udelay(300);
+
+ return status == D64_XS0_XS_DISABLED;
+}
+
+bool dma_rxreset(struct dma_pub *pub)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+ u32 status;
+
+ if (di->nrxd == 0)
+ return true;
+
+ W_REG(&di->d64rxregs->control, 0);
+ SPINWAIT(((status =
+ (R_REG(&di->d64rxregs->status0) & D64_RS0_RS_MASK))
+ != D64_RS0_RS_DISABLED), 10000);
+
+ return status == D64_RS0_RS_DISABLED;
+}
+
+/*
+ * !! tx entry routine
+ * WARNING: call must check the return value for error.
+ * the error(toss frames) could be fatal and cause many subsequent hard
+ * to debug problems
+ */
+int dma_txfast(struct dma_pub *pub, struct sk_buff *p0, bool commit)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+ struct sk_buff *p, *next;
+ unsigned char *data;
+ uint len;
+ u16 txout;
+ u32 flags = 0;
+ dma_addr_t pa;
+
+ DMA_TRACE(("%s: dma_txfast\n", di->name));
+
+ txout = di->txout;
+
+ /*
+ * Walk the chain of packet buffers
+ * allocating and initializing transmit descriptor entries.
+ */
+ for (p = p0; p; p = next) {
+ data = p->data;
+ len = p->len;
+ next = p->next;
+
+ /* return nonzero if out of tx descriptors */
+ if (nexttxd(di, txout) == di->txin)
+ goto outoftxd;
+
+ if (len == 0)
+ continue;
+
+ /* get physical address of buffer start */
+ pa = pci_map_single(di->pbus, data, len, PCI_DMA_TODEVICE);
+
+ flags = 0;
+ if (p == p0)
+ flags |= D64_CTRL1_SOF;
+
+ /* With a DMA segment list, Descriptor table is filled
+ * using the segment list instead of looping over
+ * buffers in multi-chain DMA. Therefore, EOF for SGLIST
+ * is when end of segment list is reached.
+ */
+ if (next == NULL)
+ flags |= (D64_CTRL1_IOC | D64_CTRL1_EOF);
+ if (txout == (di->ntxd - 1))
+ flags |= D64_CTRL1_EOT;
+
+ dma64_dd_upd(di, di->txd64, pa, txout, &flags, len);
+
+ txout = nexttxd(di, txout);
+ }
+
+ /* if last txd eof not set, fix it */
+ if (!(flags & D64_CTRL1_EOF))
+ di->txd64[prevtxd(di, txout)].ctrl1 =
+ cpu_to_le32(flags | D64_CTRL1_IOC | D64_CTRL1_EOF);
+
+ /* save the packet */
+ di->txp[prevtxd(di, txout)] = p0;
+
+ /* bump the tx descriptor index */
+ di->txout = txout;
+
+ /* kick the chip */
+ if (commit)
+ W_REG(&di->d64txregs->ptr,
+ di->xmtptrbase + I2B(txout, struct dma64desc));
+
+ /* tx flow control */
+ di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) - 1;
+
+ return 0;
+
+ outoftxd:
+ DMA_ERROR(("%s: dma_txfast: out of txds !!!\n", di->name));
+ brcmu_pkt_buf_free_skb(p0);
+ di->dma.txavail = 0;
+ di->dma.txnobuf++;
+ return -1;
+}
+
+/*
+ * Reclaim next completed txd (txds if using chained buffers) in the range
+ * specified and return associated packet.
+ * If range is DMA_RANGE_TRANSMITTED, reclaim descriptors that have be
+ * transmitted as noted by the hardware "CurrDescr" pointer.
+ * If range is DMA_RANGE_TRANSFERED, reclaim descriptors that have be
+ * transferred by the DMA as noted by the hardware "ActiveDescr" pointer.
+ * If range is DMA_RANGE_ALL, reclaim all txd(s) posted to the ring and
+ * return associated packet regardless of the value of hardware pointers.
+ */
+struct sk_buff *dma_getnexttxp(struct dma_pub *pub, enum txd_range range)
+{
+ struct dma_info *di = (struct dma_info *)pub;
+ u16 start, end, i;
+ u16 active_desc;
+ struct sk_buff *txp;
+
+ DMA_TRACE(("%s: dma_getnexttxp %s\n", di->name,
+ (range == DMA_RANGE_ALL) ? "all" :
+ ((range ==
+ DMA_RANGE_TRANSMITTED) ? "transmitted" :
+ "transferred")));
+
+ if (di->ntxd == 0)
+ return NULL;
+
+ txp = NULL;
+
+ start = di->txin;
+ if (range == DMA_RANGE_ALL)
+ end = di->txout;
+ else {
+ struct dma64regs __iomem *dregs = di->d64txregs;
+
+ end = (u16) (B2I(((R_REG(&dregs->status0) &
+ D64_XS0_CD_MASK) -
+ di->xmtptrbase) & D64_XS0_CD_MASK,
+ struct dma64desc));
+
+ if (range == DMA_RANGE_TRANSFERED) {
+ active_desc =
+ (u16) (R_REG(&dregs->status1) &
+ D64_XS1_AD_MASK);
+ active_desc =
+ (active_desc - di->xmtptrbase) & D64_XS0_CD_MASK;
+ active_desc = B2I(active_desc, struct dma64desc);
+ if (end != active_desc)
+ end = prevtxd(di, active_desc);
+ }
+ }
+
+ if ((start == 0) && (end > di->txout))
+ goto bogus;
+
+ for (i = start; i != end && !txp; i = nexttxd(di, i)) {
+ dma_addr_t pa;
+ uint size;
+
+ pa = le32_to_cpu(di->txd64[i].addrlow) - di->dataoffsetlow;
+
+ size =
+ (le32_to_cpu(di->txd64[i].ctrl2) &
+ D64_CTRL2_BC_MASK);
+
+ di->txd64[i].addrlow = cpu_to_le32(0xdeadbeef);
+ di->txd64[i].addrhigh = cpu_to_le32(0xdeadbeef);
+
+ txp = di->txp[i];
+ di->txp[i] = NULL;
+
+ pci_unmap_single(di->pbus, pa, size, PCI_DMA_TODEVICE);
+ }
+
+ di->txin = i;
+
+ /* tx flow control */
+ di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) - 1;
+
+ return txp;
+
+ bogus:
+ DMA_NONE(("dma_getnexttxp: bogus curr: start %d end %d txout %d "
+ "force %d\n", start, end, di->txout, forceall));
+ return NULL;
+}
+
+/*
+ * Mac80211 initiated actions sometimes require packets in the DMA queue to be
+ * modified. The modified portion of the packet is not under control of the DMA
+ * engine. This function calls a caller-supplied function for each packet in
+ * the caller specified dma chain.
+ */
+void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc)
+ (void *pkt, void *arg_a), void *arg_a)
+{
+ struct dma_info *di = (struct dma_info *) dmah;
+ uint i = di->txin;
+ uint end = di->txout;
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *tx_info;
+
+ while (i != end) {
+ skb = (struct sk_buff *)di->txp[i];
+ if (skb != NULL) {
+ tx_info = (struct ieee80211_tx_info *)skb->cb;
+ (callback_fnc)(tx_info, arg_a);
+ }
+ i = nexttxd(di, i);
+ }
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.h b/drivers/net/wireless/brcm80211/brcmsmac/dma.h
new file mode 100644
index 000000000000..ebc5bc546f3b
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_DMA_H_
+#define _BRCM_DMA_H_
+
+#include <linux/delay.h>
+#include "types.h" /* forward structure declarations */
+
+/* map/unmap direction */
+#define DMA_TX 1 /* TX direction for DMA */
+#define DMA_RX 2 /* RX direction for DMA */
+
+/* DMA structure:
+ * support two DMA engines: 32 bits address or 64 bit addressing
+ * basic DMA register set is per channel(transmit or receive)
+ * a pair of channels is defined for convenience
+ */
+
+/* 32 bits addressing */
+
+struct dma32diag { /* diag access */
+ u32 fifoaddr; /* diag address */
+ u32 fifodatalow; /* low 32bits of data */
+ u32 fifodatahigh; /* high 32bits of data */
+ u32 pad; /* reserved */
+};
+
+/* 64 bits addressing */
+
+/* dma registers per channel(xmt or rcv) */
+struct dma64regs {
+ u32 control; /* enable, et al */
+ u32 ptr; /* last descriptor posted to chip */
+ u32 addrlow; /* desc ring base address low 32-bits (8K aligned) */
+ u32 addrhigh; /* desc ring base address bits 63:32 (8K aligned) */
+ u32 status0; /* current descriptor, xmt state */
+ u32 status1; /* active descriptor, xmt error */
+};
+
+/* range param for dma_getnexttxp() and dma_txreclaim */
+enum txd_range {
+ DMA_RANGE_ALL = 1,
+ DMA_RANGE_TRANSMITTED,
+ DMA_RANGE_TRANSFERED
+};
+
+/*
+ * Exported data structure (read-only)
+ */
+/* export structure */
+struct dma_pub {
+ uint txavail; /* # free tx descriptors */
+ uint dmactrlflags; /* dma control flags */
+
+ /* rx error counters */
+ uint rxgiants; /* rx giant frames */
+ uint rxnobuf; /* rx out of dma descriptors */
+ /* tx error counters */
+ uint txnobuf; /* tx out of dma descriptors */
+};
+
+extern struct dma_pub *dma_attach(char *name, struct si_pub *sih,
+ void __iomem *dmaregstx, void __iomem *dmaregsrx,
+ uint ntxd, uint nrxd,
+ uint rxbufsize, int rxextheadroom,
+ uint nrxpost, uint rxoffset, uint *msg_level);
+
+void dma_rxinit(struct dma_pub *pub);
+struct sk_buff *dma_rx(struct dma_pub *pub);
+bool dma_rxfill(struct dma_pub *pub);
+bool dma_rxreset(struct dma_pub *pub);
+bool dma_txreset(struct dma_pub *pub);
+void dma_txinit(struct dma_pub *pub);
+int dma_txfast(struct dma_pub *pub, struct sk_buff *p0, bool commit);
+void dma_txsuspend(struct dma_pub *pub);
+bool dma_txsuspended(struct dma_pub *pub);
+void dma_txresume(struct dma_pub *pub);
+void dma_txreclaim(struct dma_pub *pub, enum txd_range range);
+void dma_rxreclaim(struct dma_pub *pub);
+void dma_detach(struct dma_pub *pub);
+unsigned long dma_getvar(struct dma_pub *pub, const char *name);
+struct sk_buff *dma_getnexttxp(struct dma_pub *pub, enum txd_range range);
+void dma_counterreset(struct dma_pub *pub);
+
+void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc)
+ (void *pkt, void *arg_a), void *arg_a);
+
+/*
+ * DMA(Bug) on bcm47xx chips seems to declare that the packet is ready, but
+ * the packet length is not updated yet (by DMA) on the expected time.
+ * Workaround is to hold processor till DMA updates the length, and stay off
+ * the bus to allow DMA update the length in buffer
+ */
+static inline void dma_spin_for_len(uint len, struct sk_buff *head)
+{
+#if defined(CONFIG_BCM47XX)
+ if (!len) {
+ while (!(len = *(u16 *) KSEG1ADDR(head->data)))
+ udelay(1);
+
+ *(u16 *) (head->data) = cpu_to_le16((u16) len);
+ }
+#endif /* defined(CONFIG_BCM47XX) */
+}
+
+#endif /* _BRCM_DMA_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/wl_mac80211.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index 6c6236c969b7..0d8a9cdf897a 100644
--- a/drivers/staging/brcm80211/brcmsmac/wl_mac80211.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -16,50 +16,26 @@
#define __UNDEF_NO_VERSION__
-#include <linux/kernel.h>
#include <linux/etherdevice.h>
-#include <linux/types.h>
-#include <linux/pci_ids.h>
-#include <linux/module.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
#include <net/mac80211.h>
-
-#include <proto/802.11.h>
-#include <bcmdefs.h>
-#include <bcmwifi.h>
-#include <bcmutils.h>
-#include <bcmnvram.h>
-#include <pcicfg.h>
-#include <wlioctl.h>
-#include <sbhnddma.h>
-
-#include "phy/wlc_phy_int.h"
+#include <defs.h>
+#include "nicpci.h"
+#include "phy/phy_int.h"
#include "d11.h"
-#include "wlc_types.h"
-#include "wlc_cfg.h"
-#include "phy/phy_version.h"
-#include "wlc_key.h"
-#include "wlc_channel.h"
-#include "wlc_scb.h"
-#include "wlc_pub.h"
-#include "wl_dbg.h"
-#include "wl_export.h"
-#include "wl_ucode.h"
-#include "wl_mac80211.h"
+#include "channel.h"
+#include "scb.h"
+#include "pub.h"
+#include "ucode_loader.h"
+#include "mac80211_if.h"
+#include "main.h"
#define N_TX_QUEUES 4 /* #tx queues on mac80211<->driver interface */
-static void wl_timer(unsigned long data);
-static void _wl_timer(struct wl_timer *t);
-
-
-static int ieee_hw_init(struct ieee80211_hw *hw);
-static int ieee_hw_rate_init(struct ieee80211_hw *hw);
-
-static int wl_linux_watchdog(void *ctx);
-
/* Flags we support */
#define MAC_FILTERS (FIF_PROMISC_IN_BSS | \
FIF_ALLMULTI | \
@@ -69,22 +45,42 @@ static int wl_linux_watchdog(void *ctx);
FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC)
-static int wl_found;
+#define CHAN2GHZ(channel, freqency, chflags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (freqency), \
+ .hw_value = (channel), \
+ .flags = chflags, \
+ .max_antenna_gain = 0, \
+ .max_power = 19, \
+}
+
+#define CHAN5GHZ(channel, chflags) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = 5000 + 5*(channel), \
+ .hw_value = (channel), \
+ .flags = chflags, \
+ .max_antenna_gain = 0, \
+ .max_power = 21, \
+}
+
+#define RATE(rate100m, _flags) { \
+ .bitrate = (rate100m), \
+ .flags = (_flags), \
+ .hw_value = (rate100m / 5), \
+}
-#define WL_DEV_IF(dev) ((struct wl_if *)netdev_priv(dev))
-#define WL_INFO(dev) ((struct wl_info *)(WL_DEV_IF(dev)->wl))
-static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev);
-static void wl_release_fw(struct wl_info *wl);
+struct firmware_hdr {
+ __le32 offset;
+ __le32 len;
+ __le32 idx;
+};
-/* local prototypes */
-static void wl_dpc(unsigned long data);
-static irqreturn_t wl_isr(int irq, void *dev_id);
+static const char * const brcms_firmwares[MAX_FW_IMAGES] = {
+ "brcm/bcm43xx",
+ NULL
+};
-static int __devinit wl_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent);
-static void wl_remove(struct pci_dev *pdev);
-static void wl_free(struct wl_info *wl);
-static void wl_set_basic_rate(struct wl_rateset *rs, u16 rate, bool is_br);
+static int n_adapters_found;
MODULE_AUTHOR("Broadcom Corporation");
MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver.");
@@ -92,108 +88,225 @@ MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
MODULE_LICENSE("Dual BSD/GPL");
/* recognized PCI IDs */
-static struct pci_device_id wl_id_table[] = {
- {PCI_VENDOR_ID_BROADCOM, 0x4357, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 43225 2G */
- {PCI_VENDOR_ID_BROADCOM, 0x4353, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 43224 DUAL */
- {PCI_VENDOR_ID_BROADCOM, 0x4727, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 4313 DUAL */
+static DEFINE_PCI_DEVICE_TABLE(brcms_pci_id_table) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) }, /* 43225 2G */
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) }, /* 43224 DUAL */
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) }, /* 4313 DUAL */
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) }, /* 43224 Ven */
{0}
};
-MODULE_DEVICE_TABLE(pci, wl_id_table);
+MODULE_DEVICE_TABLE(pci, brcms_pci_id_table);
#ifdef BCMDBG
static int msglevel = 0xdeadbeef;
module_param(msglevel, int, 0);
-static int phymsglevel = 0xdeadbeef;
-module_param(phymsglevel, int, 0);
#endif /* BCMDBG */
-#define HW_TO_WL(hw) (hw->priv)
-#define WL_TO_HW(wl) (wl->pub->ieee_hw)
-
-/* MAC80211 callback functions */
-static int wl_ops_start(struct ieee80211_hw *hw);
-static void wl_ops_stop(struct ieee80211_hw *hw);
-static int wl_ops_add_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif);
-static void wl_ops_remove_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif);
-static int wl_ops_config(struct ieee80211_hw *hw, u32 changed);
-static void wl_ops_bss_info_changed(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *info,
- u32 changed);
-static void wl_ops_configure_filter(struct ieee80211_hw *hw,
- unsigned int changed_flags,
- unsigned int *total_flags, u64 multicast);
-static int wl_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
- bool set);
-static void wl_ops_sw_scan_start(struct ieee80211_hw *hw);
-static void wl_ops_sw_scan_complete(struct ieee80211_hw *hw);
-static void wl_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf);
-static int wl_ops_get_stats(struct ieee80211_hw *hw,
- struct ieee80211_low_level_stats *stats);
-static void wl_ops_sta_notify(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- enum sta_notify_cmd cmd,
- struct ieee80211_sta *sta);
-static int wl_ops_conf_tx(struct ieee80211_hw *hw, u16 queue,
- const struct ieee80211_tx_queue_params *params);
-static u64 wl_ops_get_tsf(struct ieee80211_hw *hw);
-static int wl_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
-static int wl_ops_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
-static int wl_ops_ampdu_action(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid, u16 *ssn,
- u8 buf_size);
-static void wl_ops_rfkill_poll(struct ieee80211_hw *hw);
-static void wl_ops_flush(struct ieee80211_hw *hw, bool drop);
-
-static void wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+static struct ieee80211_channel brcms_2ghz_chantable[] = {
+ CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN2GHZ(5, 2432, 0),
+ CHAN2GHZ(6, 2437, 0),
+ CHAN2GHZ(7, 2442, 0),
+ CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(12, 2467,
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(13, 2472,
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(14, 2484,
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+};
+
+static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = {
+ /* UNII-1 */
+ CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS),
+ /* UNII-2 */
+ CHAN5GHZ(52,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(56,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(60,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(64,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ /* MID */
+ CHAN5GHZ(100,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(104,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(108,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(112,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(116,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(120,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(124,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(128,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(132,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(136,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(140,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS |
+ IEEE80211_CHAN_NO_HT40MINUS),
+ /* UNII-3 */
+ CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+};
+
+/*
+ * The rate table is used for both 2.4G and 5G rates. The
+ * latter being a subset as it does not support CCK rates.
+ */
+static struct ieee80211_rate legacy_ratetable[] = {
+ RATE(10, 0),
+ RATE(20, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(55, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(110, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(60, 0),
+ RATE(90, 0),
+ RATE(120, 0),
+ RATE(180, 0),
+ RATE(240, 0),
+ RATE(360, 0),
+ RATE(480, 0),
+ RATE(540, 0),
+};
+
+static const struct ieee80211_supported_band brcms_band_2GHz_nphy_template = {
+ .band = IEEE80211_BAND_2GHZ,
+ .channels = brcms_2ghz_chantable,
+ .n_channels = ARRAY_SIZE(brcms_2ghz_chantable),
+ .bitrates = legacy_ratetable,
+ .n_bitrates = ARRAY_SIZE(legacy_ratetable),
+ .ht_cap = {
+ /* from include/linux/ieee80211.h */
+ .cap = IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT,
+ .ht_supported = true,
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
+ .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
+ .mcs = {
+ /* placeholders for now */
+ .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
+ .rx_highest = cpu_to_le16(500),
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
+ }
+};
+
+static const struct ieee80211_supported_band brcms_band_5GHz_nphy_template = {
+ .band = IEEE80211_BAND_5GHZ,
+ .channels = brcms_5ghz_nphy_chantable,
+ .n_channels = ARRAY_SIZE(brcms_5ghz_nphy_chantable),
+ .bitrates = legacy_ratetable + BRCMS_LEGACY_5G_RATE_OFFSET,
+ .n_bitrates = ARRAY_SIZE(legacy_ratetable) -
+ BRCMS_LEGACY_5G_RATE_OFFSET,
+ .ht_cap = {
+ .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_40MHZ_INTOLERANT, /* No 40 mhz yet */
+ .ht_supported = true,
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
+ .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
+ .mcs = {
+ /* placeholders for now */
+ .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
+ .rx_highest = cpu_to_le16(500),
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
+ }
+};
+
+/* flags the given rate in rateset as requested */
+static void brcms_set_basic_rate(struct brcm_rateset *rs, u16 rate, bool is_br)
+{
+ u32 i;
+
+ for (i = 0; i < rs->count; i++) {
+ if (rate != (rs->rates[i] & 0x7f))
+ continue;
+
+ if (is_br)
+ rs->rates[i] |= BRCMS_RATE_FLAG;
+ else
+ rs->rates[i] &= BRCMS_RATE_MASK;
+ return;
+ }
+}
+
+static void brcms_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
- struct wl_info *wl = hw->priv;
+ struct brcms_info *wl = hw->priv;
- WL_LOCK(wl);
+ spin_lock_bh(&wl->lock);
if (!wl->pub->up) {
wiphy_err(wl->wiphy, "ops->tx called while down\n");
kfree_skb(skb);
goto done;
}
- wlc_sendpkt_mac80211(wl->wlc, skb, hw);
+ brcms_c_sendpkt_mac80211(wl->wlc, skb, hw);
done:
- WL_UNLOCK(wl);
+ spin_unlock_bh(&wl->lock);
}
-static int wl_ops_start(struct ieee80211_hw *hw)
+static int brcms_ops_start(struct ieee80211_hw *hw)
{
- struct wl_info *wl = hw->priv;
+ struct brcms_info *wl = hw->priv;
bool blocked;
- /*
- struct ieee80211_channel *curchan = hw->conf.channel;
- */
ieee80211_wake_queues(hw);
- WL_LOCK(wl);
- blocked = wl_rfkill_set_hw_state(wl);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ blocked = brcms_rfkill_set_hw_state(wl);
+ spin_unlock_bh(&wl->lock);
if (!blocked)
wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
return 0;
}
-static void wl_ops_stop(struct ieee80211_hw *hw)
+static void brcms_ops_stop(struct ieee80211_hw *hw)
{
ieee80211_stop_queues(hw);
}
static int
-wl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
- struct wl_info *wl;
+ struct brcms_info *wl;
int err;
/* Just STA for now */
@@ -207,77 +320,46 @@ wl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
return -EOPNOTSUPP;
}
- wl = HW_TO_WL(hw);
- WL_LOCK(wl);
- err = wl_up(wl);
- WL_UNLOCK(wl);
+ wl = hw->priv;
+ spin_lock_bh(&wl->lock);
+ if (!wl->pub->up)
+ err = brcms_up(wl);
+ else
+ err = -ENODEV;
+ spin_unlock_bh(&wl->lock);
- if (err != 0) {
- wiphy_err(hw->wiphy, "%s: wl_up() returned %d\n", __func__,
+ if (err != 0)
+ wiphy_err(hw->wiphy, "%s: brcms_up() returned %d\n", __func__,
err);
- }
+
return err;
}
static void
-wl_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+brcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
- struct wl_info *wl;
+ struct brcms_info *wl;
- wl = HW_TO_WL(hw);
+ wl = hw->priv;
/* put driver in down state */
- WL_LOCK(wl);
- wl_down(wl);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ brcms_down(wl);
+ spin_unlock_bh(&wl->lock);
}
-/*
- * precondition: perimeter lock has been acquired
- */
-static int
-ieee_set_channel(struct ieee80211_hw *hw, struct ieee80211_channel *chan,
- enum nl80211_channel_type type)
-{
- struct wl_info *wl = HW_TO_WL(hw);
- int err = 0;
-
- switch (type) {
- case NL80211_CHAN_HT20:
- case NL80211_CHAN_NO_HT:
- err = wlc_set(wl->wlc, WLC_SET_CHANNEL, chan->hw_value);
- break;
- case NL80211_CHAN_HT40MINUS:
- case NL80211_CHAN_HT40PLUS:
- wiphy_err(hw->wiphy,
- "%s: Need to implement 40 Mhz Channels!\n", __func__);
- err = 1;
- break;
- }
-
- if (err)
- return -EIO;
- return err;
-}
-
-static int wl_ops_config(struct ieee80211_hw *hw, u32 changed)
+static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed)
{
struct ieee80211_conf *conf = &hw->conf;
- struct wl_info *wl = HW_TO_WL(hw);
+ struct brcms_info *wl = hw->priv;
int err = 0;
int new_int;
struct wiphy *wiphy = hw->wiphy;
- WL_LOCK(wl);
+ spin_lock_bh(&wl->lock);
if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
- if (wlc_iovar_setint
- (wl->wlc, "bcn_li_bcn", conf->listen_interval)) {
- wiphy_err(wiphy, "%s: Error setting listen_interval\n",
- __func__);
- err = -EIO;
- goto config_out;
- }
- wlc_iovar_getint(wl->wlc, "bcn_li_bcn", &new_int);
+ brcms_c_set_beacon_listen_interval(wl->wlc,
+ conf->listen_interval);
}
if (changed & IEEE80211_CONF_CHANGE_MONITOR)
wiphy_err(wiphy, "%s: change monitor mode: %s (implement)\n",
@@ -289,51 +371,43 @@ static int wl_ops_config(struct ieee80211_hw *hw, u32 changed)
"true" : "false");
if (changed & IEEE80211_CONF_CHANGE_POWER) {
- if (wlc_iovar_setint
- (wl->wlc, "qtxpower", conf->power_level * 4)) {
+ err = brcms_c_set_tx_power(wl->wlc, conf->power_level);
+ if (err < 0) {
wiphy_err(wiphy, "%s: Error setting power_level\n",
__func__);
- err = -EIO;
goto config_out;
}
- wlc_iovar_getint(wl->wlc, "qtxpower", &new_int);
- if (new_int != (conf->power_level * 4))
+ new_int = brcms_c_get_tx_power(wl->wlc);
+ if (new_int != conf->power_level)
wiphy_err(wiphy, "%s: Power level req != actual, %d %d"
- "\n", __func__, conf->power_level * 4,
+ "\n", __func__, conf->power_level,
new_int);
}
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- err = ieee_set_channel(hw, conf->channel, conf->channel_type);
- }
- if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
- if (wlc_set
- (wl->wlc, WLC_SET_SRL,
- conf->short_frame_max_tx_count) < 0) {
- wiphy_err(wiphy, "%s: Error setting srl\n", __func__);
- err = -EIO;
- goto config_out;
- }
- if (wlc_set(wl->wlc, WLC_SET_LRL, conf->long_frame_max_tx_count)
- < 0) {
- wiphy_err(wiphy, "%s: Error setting lrl\n", __func__);
- err = -EIO;
- goto config_out;
- }
+ if (conf->channel_type == NL80211_CHAN_HT20 ||
+ conf->channel_type == NL80211_CHAN_NO_HT)
+ err = brcms_c_set_channel(wl->wlc,
+ conf->channel->hw_value);
+ else
+ err = -ENOTSUPP;
}
+ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
+ err = brcms_c_set_rate_limit(wl->wlc,
+ conf->short_frame_max_tx_count,
+ conf->long_frame_max_tx_count);
config_out:
- WL_UNLOCK(wl);
+ spin_unlock_bh(&wl->lock);
return err;
}
static void
-wl_ops_bss_info_changed(struct ieee80211_hw *hw,
+brcms_ops_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info, u32 changed)
{
- struct wl_info *wl = HW_TO_WL(hw);
+ struct brcms_info *wl = hw->priv;
struct wiphy *wiphy = hw->wiphy;
- int val;
if (changed & BSS_CHANGED_ASSOC) {
/* association status changed (associated/disassociated)
@@ -341,106 +415,109 @@ wl_ops_bss_info_changed(struct ieee80211_hw *hw,
*/
wiphy_err(wiphy, "%s: %s: %sassociated\n", KBUILD_MODNAME,
__func__, info->assoc ? "" : "dis");
- WL_LOCK(wl);
- wlc_associate_upd(wl->wlc, info->assoc);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ brcms_c_associate_upd(wl->wlc, info->assoc);
+ spin_unlock_bh(&wl->lock);
}
if (changed & BSS_CHANGED_ERP_SLOT) {
+ s8 val;
+
/* slot timing changed */
if (info->use_short_slot)
val = 1;
else
val = 0;
- WL_LOCK(wl);
- wlc_set(wl->wlc, WLC_SET_SHORTSLOT_OVERRIDE, val);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ brcms_c_set_shortslot_override(wl->wlc, val);
+ spin_unlock_bh(&wl->lock);
}
if (changed & BSS_CHANGED_HT) {
/* 802.11n parameters changed */
u16 mode = info->ht_operation_mode;
- WL_LOCK(wl);
- wlc_protection_upd(wl->wlc, WLC_PROT_N_CFG,
+ spin_lock_bh(&wl->lock);
+ brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_CFG,
mode & IEEE80211_HT_OP_MODE_PROTECTION);
- wlc_protection_upd(wl->wlc, WLC_PROT_N_NONGF,
+ brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_NONGF,
mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
- wlc_protection_upd(wl->wlc, WLC_PROT_N_OBSS,
+ brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_OBSS,
mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT);
- WL_UNLOCK(wl);
+ spin_unlock_bh(&wl->lock);
}
if (changed & BSS_CHANGED_BASIC_RATES) {
struct ieee80211_supported_band *bi;
u32 br_mask, i;
u16 rate;
- struct wl_rateset rs;
+ struct brcm_rateset rs;
int error;
/* retrieve the current rates */
- WL_LOCK(wl);
- error = wlc_ioctl(wl->wlc, WLC_GET_CURR_RATESET,
- &rs, sizeof(rs), NULL);
- WL_UNLOCK(wl);
- if (error) {
- wiphy_err(wiphy, "%s: retrieve rateset failed: %d\n",
- __func__, error);
- return;
- }
+ spin_lock_bh(&wl->lock);
+ brcms_c_get_current_rateset(wl->wlc, &rs);
+ spin_unlock_bh(&wl->lock);
+
br_mask = info->basic_rates;
- bi = hw->wiphy->bands[wlc_get_curband(wl->wlc)];
+ bi = hw->wiphy->bands[brcms_c_get_curband(wl->wlc)];
for (i = 0; i < bi->n_bitrates; i++) {
/* convert to internal rate value */
rate = (bi->bitrates[i].bitrate << 1) / 10;
/* set/clear basic rate flag */
- wl_set_basic_rate(&rs, rate, br_mask & 1);
+ brcms_set_basic_rate(&rs, rate, br_mask & 1);
br_mask >>= 1;
}
/* update the rate set */
- WL_LOCK(wl);
- wlc_ioctl(wl->wlc, WLC_SET_RATESET, &rs, sizeof(rs), NULL);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ error = brcms_c_set_rateset(wl->wlc, &rs);
+ spin_unlock_bh(&wl->lock);
+ if (error)
+ wiphy_err(wiphy, "changing basic rates failed: %d\n",
+ error);
}
if (changed & BSS_CHANGED_BEACON_INT) {
/* Beacon interval changed */
- WL_LOCK(wl);
- wlc_set(wl->wlc, WLC_SET_BCNPRD, info->beacon_int);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ brcms_c_set_beacon_period(wl->wlc, info->beacon_int);
+ spin_unlock_bh(&wl->lock);
}
if (changed & BSS_CHANGED_BSSID) {
/* BSSID changed, for whatever reason (IBSS and managed mode) */
- WL_LOCK(wl);
- wlc_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET,
- info->bssid);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ brcms_c_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET, info->bssid);
+ spin_unlock_bh(&wl->lock);
}
- if (changed & BSS_CHANGED_BEACON) {
+ if (changed & BSS_CHANGED_BEACON)
/* Beacon data changed, retrieve new beacon (beaconing modes) */
wiphy_err(wiphy, "%s: beacon changed\n", __func__);
- }
+
if (changed & BSS_CHANGED_BEACON_ENABLED) {
/* Beaconing should be enabled/disabled (beaconing modes) */
wiphy_err(wiphy, "%s: Beacon enabled: %s\n", __func__,
info->enable_beacon ? "true" : "false");
}
+
if (changed & BSS_CHANGED_CQM) {
/* Connection quality monitor config changed */
wiphy_err(wiphy, "%s: cqm change: threshold %d, hys %d "
" (implement)\n", __func__, info->cqm_rssi_thold,
info->cqm_rssi_hyst);
}
+
if (changed & BSS_CHANGED_IBSS) {
/* IBSS join status changed */
wiphy_err(wiphy, "%s: IBSS joined: %s (implement)\n", __func__,
info->ibss_joined ? "true" : "false");
}
+
if (changed & BSS_CHANGED_ARP_FILTER) {
/* Hardware ARP filter address list or state changed */
wiphy_err(wiphy, "%s: arp filtering: enabled %s, count %d"
" (implement)\n", __func__, info->arp_filter_enabled ?
"true" : "false", info->arp_addr_cnt);
}
+
if (changed & BSS_CHANGED_QOS) {
/*
* QoS for this association was enabled/disabled.
@@ -449,20 +526,15 @@ wl_ops_bss_info_changed(struct ieee80211_hw *hw,
wiphy_err(wiphy, "%s: qos enabled: %s (implement)\n", __func__,
info->qos ? "true" : "false");
}
- if (changed & BSS_CHANGED_IDLE) {
- /* Idle changed for this BSS/interface */
- wiphy_err(wiphy, "%s: BSS idle: %s (implement)\n", __func__,
- info->idle ? "true" : "false");
- }
return;
}
static void
-wl_ops_configure_filter(struct ieee80211_hw *hw,
+brcms_ops_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags, u64 multicast)
{
- struct wl_info *wl = hw->priv;
+ struct brcms_info *wl = hw->priv;
struct wiphy *wiphy = hw->wiphy;
changed_flags &= MAC_FILTERS;
@@ -480,121 +552,62 @@ wl_ops_configure_filter(struct ieee80211_hw *hw,
if (changed_flags & FIF_OTHER_BSS)
wiphy_err(wiphy, "FIF_OTHER_BSS\n");
if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
- WL_LOCK(wl);
+ spin_lock_bh(&wl->lock);
if (*total_flags & FIF_BCN_PRBRESP_PROMISC) {
wl->pub->mac80211_state |= MAC80211_PROMISC_BCNS;
- wlc_mac_bcn_promisc_change(wl->wlc, 1);
+ brcms_c_mac_bcn_promisc_change(wl->wlc, 1);
} else {
- wlc_mac_bcn_promisc_change(wl->wlc, 0);
+ brcms_c_mac_bcn_promisc_change(wl->wlc, 0);
wl->pub->mac80211_state &= ~MAC80211_PROMISC_BCNS;
}
- WL_UNLOCK(wl);
+ spin_unlock_bh(&wl->lock);
}
return;
}
-static int
-wl_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
+static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw)
{
- return 0;
-}
-
-static void wl_ops_sw_scan_start(struct ieee80211_hw *hw)
-{
- struct wl_info *wl = hw->priv;
- WL_LOCK(wl);
- wlc_scan_start(wl->wlc);
- WL_UNLOCK(wl);
- return;
-}
-
-static void wl_ops_sw_scan_complete(struct ieee80211_hw *hw)
-{
- struct wl_info *wl = hw->priv;
- WL_LOCK(wl);
- wlc_scan_stop(wl->wlc);
- WL_UNLOCK(wl);
- return;
-}
-
-static void wl_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf)
-{
- wiphy_err(hw->wiphy, "%s: Enter\n", __func__);
+ struct brcms_info *wl = hw->priv;
+ spin_lock_bh(&wl->lock);
+ brcms_c_scan_start(wl->wlc);
+ spin_unlock_bh(&wl->lock);
return;
}
-static int
-wl_ops_get_stats(struct ieee80211_hw *hw,
- struct ieee80211_low_level_stats *stats)
-{
- struct wl_info *wl = hw->priv;
- struct wl_cnt *cnt;
-
- WL_LOCK(wl);
- cnt = wl->pub->_cnt;
- stats->dot11ACKFailureCount = 0;
- stats->dot11RTSFailureCount = 0;
- stats->dot11FCSErrorCount = 0;
- stats->dot11RTSSuccessCount = 0;
- WL_UNLOCK(wl);
- return 0;
-}
-
-static void
-wl_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
+static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw)
{
- switch (cmd) {
- default:
- wiphy_err(hw->wiphy, "%s: Unknown cmd = %d\n", __func__,
- cmd);
- break;
- }
+ struct brcms_info *wl = hw->priv;
+ spin_lock_bh(&wl->lock);
+ brcms_c_scan_stop(wl->wlc);
+ spin_unlock_bh(&wl->lock);
return;
}
static int
-wl_ops_conf_tx(struct ieee80211_hw *hw, u16 queue,
- const struct ieee80211_tx_queue_params *params)
+brcms_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
{
- struct wl_info *wl = hw->priv;
+ struct brcms_info *wl = hw->priv;
- WL_LOCK(wl);
- wlc_wme_setparams(wl->wlc, queue, params, true);
- WL_UNLOCK(wl);
-
- return 0;
-}
+ spin_lock_bh(&wl->lock);
+ brcms_c_wme_setparams(wl->wlc, queue, params, true);
+ spin_unlock_bh(&wl->lock);
-static u64 wl_ops_get_tsf(struct ieee80211_hw *hw)
-{
- wiphy_err(hw->wiphy, "%s: Enter\n", __func__);
return 0;
}
static int
-wl_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
- struct scb *scb;
-
- int i;
- struct wl_info *wl = hw->priv;
+ struct brcms_info *wl = hw->priv;
+ struct scb *scb = &wl->wlc->pri_scb;
- /* Init the scb */
- scb = (struct scb *)sta->drv_priv;
- memset(scb, 0, sizeof(struct scb));
- for (i = 0; i < NUMPRIO; i++)
- scb->seqctl[i] = 0xFFFF;
- scb->seqctl_nonqos = 0xFFFF;
- scb->magic = SCB_MAGIC;
+ brcms_c_init_scb(scb);
- wl->pub->global_scb = scb;
wl->pub->global_ampdu = &(scb->scb_ampdu);
wl->pub->global_ampdu->scb = scb;
wl->pub->global_ampdu->max_pdu = 16;
- bcm_pktq_init(&scb->scb_ampdu.txq, AMPDU_MAX_SCB_TID,
- AMPDU_MAX_SCB_TID * PKTQ_LEN_DEFAULT);
sta->ht_cap.ht_supported = true;
sta->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
@@ -603,26 +616,22 @@ wl_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT;
- /* minstrel_ht initiates addBA on our behalf by calling ieee80211_start_tx_ba_session() */
- return 0;
-}
-
-static int
-wl_ops_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
+ /*
+ * minstrel_ht initiates addBA on our behalf by calling
+ * ieee80211_start_tx_ba_session()
+ */
return 0;
}
static int
-wl_ops_ampdu_action(struct ieee80211_hw *hw,
+brcms_ops_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn,
u8 buf_size)
{
- struct scb *scb = (struct scb *)sta->drv_priv;
- struct wl_info *wl = hw->priv;
+ struct brcms_info *wl = hw->priv;
+ struct scb *scb = &wl->wlc->pri_scb;
int status;
if (WARN_ON(scb->magic != SCB_MAGIC))
@@ -633,27 +642,35 @@ wl_ops_ampdu_action(struct ieee80211_hw *hw,
case IEEE80211_AMPDU_RX_STOP:
break;
case IEEE80211_AMPDU_TX_START:
- WL_LOCK(wl);
- status = wlc_aggregatable(wl->wlc, tid);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ status = brcms_c_aggregatable(wl->wlc, tid);
+ spin_unlock_bh(&wl->lock);
if (!status) {
wiphy_err(wl->wiphy, "START: tid %d is not agg\'able\n",
tid);
return -EINVAL;
}
- /* XXX: Use the starting sequence number provided ... */
- *ssn = 0;
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_STOP:
- WL_LOCK(wl);
- wlc_ampdu_flush(wl->wlc, sta, tid);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ brcms_c_ampdu_flush(wl->wlc, sta, tid);
+ spin_unlock_bh(&wl->lock);
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
- /* Not sure what to do here */
+ /*
+ * BA window size from ADDBA response ('buf_size') defines how
+ * many outstanding MPDUs are allowed for the BA stream by
+ * recipient and traffic class. 'ampdu_factor' gives maximum
+ * AMPDU size.
+ */
+ spin_lock_bh(&wl->lock);
+ brcms_c_ampdu_tx_operational(wl->wlc, tid, buf_size,
+ (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+ sta->ht_cap.ampdu_factor)) - 1);
+ spin_unlock_bh(&wl->lock);
/* Power save wakeup */
break;
default:
@@ -664,92 +681,362 @@ wl_ops_ampdu_action(struct ieee80211_hw *hw,
return 0;
}
-static void wl_ops_rfkill_poll(struct ieee80211_hw *hw)
+static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw)
{
- struct wl_info *wl = HW_TO_WL(hw);
+ struct brcms_info *wl = hw->priv;
bool blocked;
- WL_LOCK(wl);
- blocked = wlc_check_radio_disabled(wl->wlc);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ blocked = brcms_c_check_radio_disabled(wl->wlc);
+ spin_unlock_bh(&wl->lock);
wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
}
-static void wl_ops_flush(struct ieee80211_hw *hw, bool drop)
+static void brcms_ops_flush(struct ieee80211_hw *hw, bool drop)
{
- struct wl_info *wl = HW_TO_WL(hw);
+ struct brcms_info *wl = hw->priv;
no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false");
/* wait for packet queue and dma fifos to run empty */
- WL_LOCK(wl);
- wlc_wait_for_tx_completion(wl->wlc, drop);
- WL_UNLOCK(wl);
+ spin_lock_bh(&wl->lock);
+ brcms_c_wait_for_tx_completion(wl->wlc, drop);
+ spin_unlock_bh(&wl->lock);
}
-static const struct ieee80211_ops wl_ops = {
- .tx = wl_ops_tx,
- .start = wl_ops_start,
- .stop = wl_ops_stop,
- .add_interface = wl_ops_add_interface,
- .remove_interface = wl_ops_remove_interface,
- .config = wl_ops_config,
- .bss_info_changed = wl_ops_bss_info_changed,
- .configure_filter = wl_ops_configure_filter,
- .set_tim = wl_ops_set_tim,
- .sw_scan_start = wl_ops_sw_scan_start,
- .sw_scan_complete = wl_ops_sw_scan_complete,
- .set_tsf = wl_ops_set_tsf,
- .get_stats = wl_ops_get_stats,
- .sta_notify = wl_ops_sta_notify,
- .conf_tx = wl_ops_conf_tx,
- .get_tsf = wl_ops_get_tsf,
- .sta_add = wl_ops_sta_add,
- .sta_remove = wl_ops_sta_remove,
- .ampdu_action = wl_ops_ampdu_action,
- .rfkill_poll = wl_ops_rfkill_poll,
- .flush = wl_ops_flush,
+static const struct ieee80211_ops brcms_ops = {
+ .tx = brcms_ops_tx,
+ .start = brcms_ops_start,
+ .stop = brcms_ops_stop,
+ .add_interface = brcms_ops_add_interface,
+ .remove_interface = brcms_ops_remove_interface,
+ .config = brcms_ops_config,
+ .bss_info_changed = brcms_ops_bss_info_changed,
+ .configure_filter = brcms_ops_configure_filter,
+ .sw_scan_start = brcms_ops_sw_scan_start,
+ .sw_scan_complete = brcms_ops_sw_scan_complete,
+ .conf_tx = brcms_ops_conf_tx,
+ .sta_add = brcms_ops_sta_add,
+ .ampdu_action = brcms_ops_ampdu_action,
+ .rfkill_poll = brcms_ops_rfkill_poll,
+ .flush = brcms_ops_flush,
};
/*
- * is called in wl_pci_probe() context, therefore no locking required.
+ * is called in brcms_pci_probe() context, therefore no locking required.
*/
-static int wl_set_hint(struct wl_info *wl, char *abbrev)
+static int brcms_set_hint(struct brcms_info *wl, char *abbrev)
{
return regulatory_hint(wl->pub->ieee_hw->wiphy, abbrev);
}
+void brcms_dpc(unsigned long data)
+{
+ struct brcms_info *wl;
+
+ wl = (struct brcms_info *) data;
+
+ spin_lock_bh(&wl->lock);
+
+ /* call the common second level interrupt handler */
+ if (wl->pub->up) {
+ if (wl->resched) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&wl->isr_lock, flags);
+ brcms_c_intrsupd(wl->wlc);
+ spin_unlock_irqrestore(&wl->isr_lock, flags);
+ }
+
+ wl->resched = brcms_c_dpc(wl->wlc, true);
+ }
+
+ /* brcms_c_dpc() may bring the driver down */
+ if (!wl->pub->up)
+ goto done;
+
+ /* re-schedule dpc */
+ if (wl->resched)
+ tasklet_schedule(&wl->tasklet);
+ else
+ /* re-enable interrupts */
+ brcms_intrson(wl);
+
+ done:
+ spin_unlock_bh(&wl->lock);
+}
+
+/*
+ * Precondition: Since this function is called in brcms_pci_probe() context,
+ * no locking is required.
+ */
+static int brcms_request_fw(struct brcms_info *wl, struct pci_dev *pdev)
+{
+ int status;
+ struct device *device = &pdev->dev;
+ char fw_name[100];
+ int i;
+
+ memset(&wl->fw, 0, sizeof(struct brcms_firmware));
+ for (i = 0; i < MAX_FW_IMAGES; i++) {
+ if (brcms_firmwares[i] == NULL)
+ break;
+ sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i],
+ UCODE_LOADER_API_VER);
+ status = request_firmware(&wl->fw.fw_bin[i], fw_name, device);
+ if (status) {
+ wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
+ KBUILD_MODNAME, fw_name);
+ return status;
+ }
+ sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i],
+ UCODE_LOADER_API_VER);
+ status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device);
+ if (status) {
+ wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
+ KBUILD_MODNAME, fw_name);
+ return status;
+ }
+ wl->fw.hdr_num_entries[i] =
+ wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr));
+ }
+ wl->fw.fw_cnt = i;
+ return brcms_ucode_data_init(wl, &wl->ucode);
+}
+
+/*
+ * Precondition: Since this function is called in brcms_pci_probe() context,
+ * no locking is required.
+ */
+static void brcms_release_fw(struct brcms_info *wl)
+{
+ int i;
+ for (i = 0; i < MAX_FW_IMAGES; i++) {
+ release_firmware(wl->fw.fw_bin[i]);
+ release_firmware(wl->fw.fw_hdr[i]);
+ }
+}
+
+/**
+ * This function frees the WL per-device resources.
+ *
+ * This function frees resources owned by the WL device pointed to
+ * by the wl parameter.
+ *
+ * precondition: can both be called locked and unlocked
+ *
+ */
+static void brcms_free(struct brcms_info *wl)
+{
+ struct brcms_timer *t, *next;
+
+ /* free ucode data */
+ if (wl->fw.fw_cnt)
+ brcms_ucode_data_free(&wl->ucode);
+ if (wl->irq)
+ free_irq(wl->irq, wl);
+
+ /* kill dpc */
+ tasklet_kill(&wl->tasklet);
+
+ if (wl->pub)
+ brcms_c_module_unregister(wl->pub, "linux", wl);
+
+ /* free common resources */
+ if (wl->wlc) {
+ brcms_c_detach(wl->wlc);
+ wl->wlc = NULL;
+ wl->pub = NULL;
+ }
+
+ /* virtual interface deletion is deferred so we cannot spinwait */
+
+ /* wait for all pending callbacks to complete */
+ while (atomic_read(&wl->callbacks) > 0)
+ schedule();
+
+ /* free timers */
+ for (t = wl->timers; t; t = next) {
+ next = t->next;
+#ifdef BCMDBG
+ kfree(t->name);
+#endif
+ kfree(t);
+ }
+
+ /*
+ * unregister_netdev() calls get_stats() which may read chip
+ * registers so we cannot unmap the chip registers until
+ * after calling unregister_netdev() .
+ */
+ if (wl->regsva)
+ iounmap(wl->regsva);
+
+ wl->regsva = NULL;
+}
+
+/*
+* called from both kernel as from this kernel module.
+* precondition: perimeter lock is not acquired.
+*/
+static void brcms_remove(struct pci_dev *pdev)
+{
+ struct brcms_info *wl;
+ struct ieee80211_hw *hw;
+ int status;
+
+ hw = pci_get_drvdata(pdev);
+ wl = hw->priv;
+ if (!wl) {
+ pr_err("wl: brcms_remove: pci_get_drvdata failed\n");
+ return;
+ }
+
+ spin_lock_bh(&wl->lock);
+ status = brcms_c_chipmatch(pdev->vendor, pdev->device);
+ spin_unlock_bh(&wl->lock);
+ if (!status) {
+ wiphy_err(wl->wiphy, "wl: brcms_remove: chipmatch "
+ "failed\n");
+ return;
+ }
+ if (wl->wlc) {
+ wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
+ wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
+ ieee80211_unregister_hw(hw);
+ spin_lock_bh(&wl->lock);
+ brcms_down(wl);
+ spin_unlock_bh(&wl->lock);
+ }
+ pci_disable_device(pdev);
+
+ brcms_free(wl);
+
+ pci_set_drvdata(pdev, NULL);
+ ieee80211_free_hw(hw);
+}
+
+static irqreturn_t brcms_isr(int irq, void *dev_id)
+{
+ struct brcms_info *wl;
+ bool ours, wantdpc;
+
+ wl = (struct brcms_info *) dev_id;
+
+ spin_lock(&wl->isr_lock);
+
+ /* call common first level interrupt handler */
+ ours = brcms_c_isr(wl->wlc, &wantdpc);
+ if (ours) {
+ /* if more to do... */
+ if (wantdpc) {
+
+ /* ...and call the second level interrupt handler */
+ /* schedule dpc */
+ tasklet_schedule(&wl->tasklet);
+ }
+ }
+
+ spin_unlock(&wl->isr_lock);
+
+ return IRQ_RETVAL(ours);
+}
+
+/*
+ * is called in brcms_pci_probe() context, therefore no locking required.
+ */
+static int ieee_hw_rate_init(struct ieee80211_hw *hw)
+{
+ struct brcms_info *wl = hw->priv;
+ struct brcms_c_info *wlc = wl->wlc;
+ struct ieee80211_supported_band *band;
+ int has_5g = 0;
+ u16 phy_type;
+
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
+
+ phy_type = brcms_c_get_phy_type(wl->wlc, 0);
+ if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) {
+ band = &wlc->bandstate[BAND_2G_INDEX]->band;
+ *band = brcms_band_2GHz_nphy_template;
+ if (phy_type == PHY_TYPE_LCN) {
+ /* Single stream */
+ band->ht_cap.mcs.rx_mask[1] = 0;
+ band->ht_cap.mcs.rx_highest = cpu_to_le16(72);
+ }
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+ } else {
+ return -EPERM;
+ }
+
+ /* Assume all bands use the same phy. True for 11n devices. */
+ if (wl->pub->_nbands > 1) {
+ has_5g++;
+ if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) {
+ band = &wlc->bandstate[BAND_5G_INDEX]->band;
+ *band = brcms_band_5GHz_nphy_template;
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+ } else {
+ return -EPERM;
+ }
+ }
+ return 0;
+}
+
+/*
+ * is called in brcms_pci_probe() context, therefore no locking required.
+ */
+static int ieee_hw_init(struct ieee80211_hw *hw)
+{
+ hw->flags = IEEE80211_HW_SIGNAL_DBM
+ /* | IEEE80211_HW_CONNECTION_MONITOR What is this? */
+ | IEEE80211_HW_REPORTS_TX_ACK_STATUS
+ | IEEE80211_HW_AMPDU_AGGREGATION;
+
+ hw->extra_tx_headroom = brcms_c_get_header_len();
+ hw->queues = N_TX_QUEUES;
+ hw->max_rates = 2; /* Primary rate and 1 fallback rate */
+
+ /* channel change time is dependent on chip and band */
+ hw->channel_change_time = 7 * 1000;
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
+ hw->rate_control_algorithm = "minstrel_ht";
+
+ hw->sta_data_size = 0;
+ return ieee_hw_rate_init(hw);
+}
+
/**
* attach to the WL device.
*
* Attach to the WL device identified by vendor and device parameters.
* regs is a host accessible memory address pointing to WL device registers.
*
- * wl_attach is not defined as static because in the case where no bus
+ * brcms_attach is not defined as static because in the case where no bus
* is defined, wl_attach will never be called, and thus, gcc will issue
* a warning that this function is defined but not used if we declare
* it as static.
*
*
- * is called in wl_pci_probe() context, therefore no locking required.
+ * is called in brcms_pci_probe() context, therefore no locking required.
*/
-static struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
- uint bustype, void *btparam, uint irq)
+static struct brcms_info *brcms_attach(u16 vendor, u16 device,
+ resource_size_t regs,
+ struct pci_dev *btparam, uint irq)
{
- struct wl_info *wl = NULL;
+ struct brcms_info *wl = NULL;
int unit, err;
-
- unsigned long base_addr;
struct ieee80211_hw *hw;
u8 perm[ETH_ALEN];
- unit = wl_found;
+ unit = n_adapters_found;
err = 0;
- if (unit < 0) {
+ if (unit < 0)
return NULL;
- }
/* allocate private info */
hw = pci_get_drvdata(btparam); /* btparam == pdev */
@@ -762,23 +1049,9 @@ static struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
atomic_set(&wl->callbacks, 0);
/* setup the bottom half handler */
- tasklet_init(&wl->tasklet, wl_dpc, (unsigned long) wl);
-
-
-
- base_addr = regs;
-
- if (bustype == PCI_BUS) {
- wl->piomode = false;
- } else if (bustype == RPC_BUS) {
- /* Do nothing */
- } else {
- bustype = PCI_BUS;
- BCMMSG(wl->wiphy, "force to PCI\n");
- }
- wl->bcm_bustype = bustype;
+ tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl);
- wl->regsva = ioremap_nocache(base_addr, PCI_BAR0_WINSZ);
+ wl->regsva = ioremap_nocache(regs, PCI_BAR0_WINSZ);
if (wl->regsva == NULL) {
wiphy_err(wl->wiphy, "wl%d: ioremap() failed\n", unit);
goto fail;
@@ -787,42 +1060,39 @@ static struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
spin_lock_init(&wl->isr_lock);
/* prepare ucode */
- if (wl_request_fw(wl, (struct pci_dev *)btparam) < 0) {
+ if (brcms_request_fw(wl, btparam) < 0) {
wiphy_err(wl->wiphy, "%s: Failed to find firmware usually in "
"%s\n", KBUILD_MODNAME, "/lib/firmware/brcm");
- wl_release_fw(wl);
- wl_remove((struct pci_dev *)btparam);
+ brcms_release_fw(wl);
+ brcms_remove(btparam);
return NULL;
}
/* common load-time initialization */
- wl->wlc = wlc_attach((void *)wl, vendor, device, unit, wl->piomode,
- wl->regsva, wl->bcm_bustype, btparam, &err);
- wl_release_fw(wl);
+ wl->wlc = brcms_c_attach(wl, vendor, device, unit, false,
+ wl->regsva, btparam, &err);
+ brcms_release_fw(wl);
if (!wl->wlc) {
- wiphy_err(wl->wiphy, "%s: wlc_attach() failed with code %d\n",
+ wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n",
KBUILD_MODNAME, err);
goto fail;
}
- wl->pub = wlc_pub(wl->wlc);
+ wl->pub = brcms_c_pub(wl->wlc);
wl->pub->ieee_hw = hw;
- if (wlc_iovar_setint(wl->wlc, "mpc", 0)) {
- wiphy_err(wl->wiphy, "wl%d: Error setting MPC variable to 0\n",
- unit);
- }
+ /* disable mpc */
+ brcms_c_set_radio_mpc(wl->wlc, false);
/* register our interrupt handler */
- if (request_irq(irq, wl_isr, IRQF_SHARED, KBUILD_MODNAME, wl)) {
+ if (request_irq(irq, brcms_isr, IRQF_SHARED, KBUILD_MODNAME, wl)) {
wiphy_err(wl->wiphy, "wl%d: request_irq() failed\n", unit);
goto fail;
}
wl->irq = irq;
/* register module */
- wlc_module_register(wl->pub, NULL, "linux", wl, NULL, wl_linux_watchdog,
- NULL);
+ brcms_c_module_register(wl->pub, "linux", wl, NULL);
if (ieee_hw_init(hw)) {
wiphy_err(wl->wiphy, "wl%d: %s: ieee_hw_init failed!\n", unit,
@@ -836,279 +1106,41 @@ static struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
SET_IEEE80211_PERM_ADDR(hw, perm);
err = ieee80211_register_hw(hw);
- if (err) {
+ if (err)
wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status"
"%d\n", __func__, err);
- }
if (wl->pub->srom_ccode[0])
- err = wl_set_hint(wl, wl->pub->srom_ccode);
+ err = brcms_set_hint(wl, wl->pub->srom_ccode);
else
- err = wl_set_hint(wl, "US");
- if (err) {
+ err = brcms_set_hint(wl, "US");
+ if (err)
wiphy_err(wl->wiphy, "%s: regulatory_hint failed, status %d\n",
__func__, err);
- }
- wl_found++;
+ n_adapters_found++;
return wl;
fail:
- wl_free(wl);
+ brcms_free(wl);
return NULL;
}
-#define CHAN2GHZ(channel, freqency, chflags) { \
- .band = IEEE80211_BAND_2GHZ, \
- .center_freq = (freqency), \
- .hw_value = (channel), \
- .flags = chflags, \
- .max_antenna_gain = 0, \
- .max_power = 19, \
-}
-
-static struct ieee80211_channel wl_2ghz_chantable[] = {
- CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS),
- CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS),
- CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS),
- CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS),
- CHAN2GHZ(5, 2432, 0),
- CHAN2GHZ(6, 2437, 0),
- CHAN2GHZ(7, 2442, 0),
- CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS),
- CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS),
- CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS),
- CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS),
- CHAN2GHZ(12, 2467,
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_NO_HT40PLUS),
- CHAN2GHZ(13, 2472,
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_NO_HT40PLUS),
- CHAN2GHZ(14, 2484,
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
-};
-
-#define CHAN5GHZ(channel, chflags) { \
- .band = IEEE80211_BAND_5GHZ, \
- .center_freq = 5000 + 5*(channel), \
- .hw_value = (channel), \
- .flags = chflags, \
- .max_antenna_gain = 0, \
- .max_power = 21, \
-}
-
-static struct ieee80211_channel wl_5ghz_nphy_chantable[] = {
- /* UNII-1 */
- CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS),
- CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS),
- /* UNII-2 */
- CHAN5GHZ(52,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(56,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
- CHAN5GHZ(60,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(64,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
- /* MID */
- CHAN5GHZ(100,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(104,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
- CHAN5GHZ(108,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(112,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
- CHAN5GHZ(116,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(120,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
- CHAN5GHZ(124,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(128,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
- CHAN5GHZ(132,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(136,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
- CHAN5GHZ(140,
- IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS |
- IEEE80211_CHAN_NO_HT40MINUS),
- /* UNII-3 */
- CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS),
- CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS),
- CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS),
- CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
-};
-
-#define RATE(rate100m, _flags) { \
- .bitrate = (rate100m), \
- .flags = (_flags), \
- .hw_value = (rate100m / 5), \
-}
-
-static struct ieee80211_rate wl_legacy_ratetable[] = {
- RATE(10, 0),
- RATE(20, IEEE80211_RATE_SHORT_PREAMBLE),
- RATE(55, IEEE80211_RATE_SHORT_PREAMBLE),
- RATE(110, IEEE80211_RATE_SHORT_PREAMBLE),
- RATE(60, 0),
- RATE(90, 0),
- RATE(120, 0),
- RATE(180, 0),
- RATE(240, 0),
- RATE(360, 0),
- RATE(480, 0),
- RATE(540, 0),
-};
-
-static struct ieee80211_supported_band wl_band_2GHz_nphy = {
- .band = IEEE80211_BAND_2GHZ,
- .channels = wl_2ghz_chantable,
- .n_channels = ARRAY_SIZE(wl_2ghz_chantable),
- .bitrates = wl_legacy_ratetable,
- .n_bitrates = ARRAY_SIZE(wl_legacy_ratetable),
- .ht_cap = {
- /* from include/linux/ieee80211.h */
- .cap = IEEE80211_HT_CAP_GRN_FLD |
- IEEE80211_HT_CAP_SGI_20 |
- IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT,
- .ht_supported = true,
- .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
- .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
- .mcs = {
- /* placeholders for now */
- .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
- .rx_highest = 500,
- .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
- }
-};
-
-static struct ieee80211_supported_band wl_band_5GHz_nphy = {
- .band = IEEE80211_BAND_5GHZ,
- .channels = wl_5ghz_nphy_chantable,
- .n_channels = ARRAY_SIZE(wl_5ghz_nphy_chantable),
- .bitrates = wl_legacy_ratetable + 4,
- .n_bitrates = ARRAY_SIZE(wl_legacy_ratetable) - 4,
- .ht_cap = {
- /* use IEEE80211_HT_CAP_* from include/linux/ieee80211.h */
- .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT, /* No 40 mhz yet */
- .ht_supported = true,
- .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
- .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
- .mcs = {
- /* placeholders for now */
- .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
- .rx_highest = 500,
- .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
- }
-};
-
-/*
- * is called in wl_pci_probe() context, therefore no locking required.
- */
-static int ieee_hw_rate_init(struct ieee80211_hw *hw)
-{
- struct wl_info *wl = HW_TO_WL(hw);
- int has_5g;
- char phy_list[4];
-
- has_5g = 0;
-
- hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
- hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
-
- if (wlc_get(wl->wlc, WLC_GET_PHYLIST, (int *)&phy_list) < 0) {
- wiphy_err(hw->wiphy, "Phy list failed\n");
- }
-
- if (phy_list[0] == 'n' || phy_list[0] == 'c') {
- if (phy_list[0] == 'c') {
- /* Single stream */
- wl_band_2GHz_nphy.ht_cap.mcs.rx_mask[1] = 0;
- wl_band_2GHz_nphy.ht_cap.mcs.rx_highest = 72;
- }
- hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl_band_2GHz_nphy;
- } else {
- return -EPERM;
- }
-
- /* Assume all bands use the same phy. True for 11n devices. */
- if (NBANDS_PUB(wl->pub) > 1) {
- has_5g++;
- if (phy_list[0] == 'n' || phy_list[0] == 'c') {
- hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
- &wl_band_5GHz_nphy;
- } else {
- return -EPERM;
- }
- }
- return 0;
-}
-
-/*
- * is called in wl_pci_probe() context, therefore no locking required.
- */
-static int ieee_hw_init(struct ieee80211_hw *hw)
-{
- hw->flags = IEEE80211_HW_SIGNAL_DBM
- /* | IEEE80211_HW_CONNECTION_MONITOR What is this? */
- | IEEE80211_HW_REPORTS_TX_ACK_STATUS
- | IEEE80211_HW_AMPDU_AGGREGATION;
-
- hw->extra_tx_headroom = wlc_get_header_len();
- hw->queues = N_TX_QUEUES;
- /* FIXME: this doesn't seem to be used properly in minstrel_ht.
- * mac80211/status.c:ieee80211_tx_status() checks this value,
- * but mac80211/rc80211_minstrel_ht.c:minstrel_ht_get_rate()
- * appears to always set 3 rates
- */
- hw->max_rates = 2; /* Primary rate and 1 fallback rate */
-
- hw->channel_change_time = 7 * 1000; /* channel change time is dependent on chip and band */
- hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
-
- hw->rate_control_algorithm = "minstrel_ht";
-
- hw->sta_data_size = sizeof(struct scb);
- return ieee_hw_rate_init(hw);
-}
-
/**
* determines if a device is a WL device, and if so, attaches it.
*
* This function determines if a device pointed to by pdev is a WL device,
- * and if so, performs a wl_attach() on it.
+ * and if so, performs a brcms_attach() on it.
*
* Perimeter lock is initialized in the course of this function.
*/
static int __devinit
-wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+brcms_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int rc;
- struct wl_info *wl;
+ struct brcms_info *wl;
struct ieee80211_hw *hw;
u32 val;
@@ -1117,7 +1149,8 @@ wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
PCI_FUNC(pdev->devfn), pdev->irq);
if ((pdev->vendor != PCI_VENDOR_ID_BROADCOM) ||
- (((pdev->device & 0xff00) != 0x4300) &&
+ ((pdev->device != 0x0576) &&
+ ((pdev->device & 0xff00) != 0x4300) &&
((pdev->device & 0xff00) != 0x4700) &&
((pdev->device < 43000) || (pdev->device > 43999))))
return -ENODEV;
@@ -1135,7 +1168,7 @@ wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
- hw = ieee80211_alloc_hw(sizeof(struct wl_info), &wl_ops);
+ hw = ieee80211_alloc_hw(sizeof(struct brcms_info), &brcms_ops);
if (!hw) {
pr_err("%s: ieee80211_alloc_hw failed\n", __func__);
return -ENOMEM;
@@ -1147,52 +1180,53 @@ wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
memset(hw->priv, 0, sizeof(*wl));
- wl = wl_attach(pdev->vendor, pdev->device, pci_resource_start(pdev, 0),
- PCI_BUS, pdev, pdev->irq);
+ wl = brcms_attach(pdev->vendor, pdev->device,
+ pci_resource_start(pdev, 0), pdev,
+ pdev->irq);
if (!wl) {
- pr_err("%s: %s: wl_attach failed!\n", KBUILD_MODNAME,
+ pr_err("%s: %s: brcms_attach failed!\n", KBUILD_MODNAME,
__func__);
return -ENODEV;
}
return 0;
}
-static int wl_suspend(struct pci_dev *pdev, pm_message_t state)
+static int brcms_suspend(struct pci_dev *pdev, pm_message_t state)
{
- struct wl_info *wl;
+ struct brcms_info *wl;
struct ieee80211_hw *hw;
hw = pci_get_drvdata(pdev);
- wl = HW_TO_WL(hw);
+ wl = hw->priv;
if (!wl) {
wiphy_err(wl->wiphy,
- "wl_suspend: pci_get_drvdata failed\n");
+ "brcms_suspend: pci_get_drvdata failed\n");
return -ENODEV;
}
/* only need to flag hw is down for proper resume */
- WL_LOCK(wl);
+ spin_lock_bh(&wl->lock);
wl->pub->hw_up = false;
- WL_UNLOCK(wl);
+ spin_unlock_bh(&wl->lock);
pci_save_state(pdev);
pci_disable_device(pdev);
return pci_set_power_state(pdev, PCI_D3hot);
}
-static int wl_resume(struct pci_dev *pdev)
+static int brcms_resume(struct pci_dev *pdev)
{
- struct wl_info *wl;
+ struct brcms_info *wl;
struct ieee80211_hw *hw;
int err = 0;
u32 val;
hw = pci_get_drvdata(pdev);
- wl = HW_TO_WL(hw);
+ wl = hw->priv;
if (!wl) {
wiphy_err(wl->wiphy,
- "wl: wl_resume: pci_get_drvdata failed\n");
+ "wl: brcms_resume: pci_get_drvdata failed\n");
return -ENODEV;
}
@@ -1214,97 +1248,37 @@ static int wl_resume(struct pci_dev *pdev)
/*
* done. driver will be put in up state
- * in wl_ops_add_interface() call.
+ * in brcms_ops_add_interface() call.
*/
return err;
}
-/*
-* called from both kernel as from wl_*()
-* precondition: perimeter lock is not acquired.
-*/
-static void wl_remove(struct pci_dev *pdev)
-{
- struct wl_info *wl;
- struct ieee80211_hw *hw;
- int status;
-
- hw = pci_get_drvdata(pdev);
- wl = HW_TO_WL(hw);
- if (!wl) {
- pr_err("wl: wl_remove: pci_get_drvdata failed\n");
- return;
- }
-
- WL_LOCK(wl);
- status = wlc_chipmatch(pdev->vendor, pdev->device);
- WL_UNLOCK(wl);
- if (!status) {
- wiphy_err(wl->wiphy, "wl: wl_remove: wlc_chipmatch failed\n");
- return;
- }
- if (wl->wlc) {
- wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
- wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
- ieee80211_unregister_hw(hw);
- WL_LOCK(wl);
- wl_down(wl);
- WL_UNLOCK(wl);
- }
- pci_disable_device(pdev);
-
- wl_free(wl);
-
- pci_set_drvdata(pdev, NULL);
- ieee80211_free_hw(hw);
-}
-
-static struct pci_driver wl_pci_driver = {
+static struct pci_driver brcms_pci_driver = {
.name = KBUILD_MODNAME,
- .probe = wl_pci_probe,
- .suspend = wl_suspend,
- .resume = wl_resume,
- .remove = __devexit_p(wl_remove),
- .id_table = wl_id_table,
+ .probe = brcms_pci_probe,
+ .suspend = brcms_suspend,
+ .resume = brcms_resume,
+ .remove = __devexit_p(brcms_remove),
+ .id_table = brcms_pci_id_table,
};
/**
* This is the main entry point for the WL driver.
*
* This function determines if a device pointed to by pdev is a WL device,
- * and if so, performs a wl_attach() on it.
+ * and if so, performs a brcms_attach() on it.
*
*/
-static int __init wl_module_init(void)
+static int __init brcms_module_init(void)
{
int error = -ENODEV;
#ifdef BCMDBG
if (msglevel != 0xdeadbeef)
- wl_msg_level = msglevel;
- else {
- char *var = getvar(NULL, "wl_msglevel");
- if (var) {
- unsigned long value;
-
- (void)strict_strtoul(var, 0, &value);
- wl_msg_level = value;
- }
- }
- if (phymsglevel != 0xdeadbeef)
- phyhal_msg_level = phymsglevel;
- else {
- char *var = getvar(NULL, "phy_msglevel");
- if (var) {
- unsigned long value;
-
- (void)strict_strtoul(var, 0, &value);
- phyhal_msg_level = value;
- }
- }
+ brcm_msg_level = msglevel;
#endif /* BCMDBG */
- error = pci_register_driver(&wl_pci_driver);
+ error = pci_register_driver(&brcms_pci_driver);
if (!error)
return 0;
@@ -1320,96 +1294,20 @@ static int __init wl_module_init(void)
* system.
*
*/
-static void __exit wl_module_exit(void)
+static void __exit brcms_module_exit(void)
{
- pci_unregister_driver(&wl_pci_driver);
-
-}
-
-module_init(wl_module_init);
-module_exit(wl_module_exit);
-
-/**
- * This function frees the WL per-device resources.
- *
- * This function frees resources owned by the WL device pointed to
- * by the wl parameter.
- *
- * precondition: can both be called locked and unlocked
- *
- */
-static void wl_free(struct wl_info *wl)
-{
- struct wl_timer *t, *next;
-
- /* free ucode data */
- if (wl->fw.fw_cnt)
- wl_ucode_data_free();
- if (wl->irq)
- free_irq(wl->irq, wl);
-
- /* kill dpc */
- tasklet_kill(&wl->tasklet);
-
- if (wl->pub) {
- wlc_module_unregister(wl->pub, "linux", wl);
- }
-
- /* free common resources */
- if (wl->wlc) {
- wlc_detach(wl->wlc);
- wl->wlc = NULL;
- wl->pub = NULL;
- }
-
- /* virtual interface deletion is deferred so we cannot spinwait */
-
- /* wait for all pending callbacks to complete */
- while (atomic_read(&wl->callbacks) > 0)
- schedule();
-
- /* free timers */
- for (t = wl->timers; t; t = next) {
- next = t->next;
-#ifdef BCMDBG
- kfree(t->name);
-#endif
- kfree(t);
- }
+ pci_unregister_driver(&brcms_pci_driver);
- /*
- * unregister_netdev() calls get_stats() which may read chip registers
- * so we cannot unmap the chip registers until after calling unregister_netdev() .
- */
- if (wl->regsva && wl->bcm_bustype != SDIO_BUS &&
- wl->bcm_bustype != JTAG_BUS) {
- iounmap((void *)wl->regsva);
- }
- wl->regsva = NULL;
}
-/* flags the given rate in rateset as requested */
-static void wl_set_basic_rate(struct wl_rateset *rs, u16 rate, bool is_br)
-{
- u32 i;
-
- for (i = 0; i < rs->count; i++) {
- if (rate != (rs->rates[i] & 0x7f))
- continue;
-
- if (is_br)
- rs->rates[i] |= WLC_RATE_FLAG;
- else
- rs->rates[i] &= WLC_RATE_MASK;
- return;
- }
-}
+module_init(brcms_module_init);
+module_exit(brcms_module_exit);
/*
* precondition: perimeter lock has been acquired
*/
-void wl_txflowcontrol(struct wl_info *wl, struct wl_if *wlif, bool state,
- int prio)
+void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif,
+ bool state, int prio)
{
wiphy_err(wl->wiphy, "Shouldn't be here %s\n", __func__);
}
@@ -1417,21 +1315,21 @@ void wl_txflowcontrol(struct wl_info *wl, struct wl_if *wlif, bool state,
/*
* precondition: perimeter lock has been acquired
*/
-void wl_init(struct wl_info *wl)
+void brcms_init(struct brcms_info *wl)
{
- BCMMSG(WL_TO_HW(wl)->wiphy, "wl%d\n", wl->pub->unit);
- wl_reset(wl);
+ BCMMSG(wl->pub->ieee_hw->wiphy, "wl%d\n", wl->pub->unit);
+ brcms_reset(wl);
- wlc_init(wl->wlc);
+ brcms_c_init(wl->wlc);
}
/*
* precondition: perimeter lock has been acquired
*/
-uint wl_reset(struct wl_info *wl)
+uint brcms_reset(struct brcms_info *wl)
{
- BCMMSG(WL_TO_HW(wl)->wiphy, "wl%d\n", wl->pub->unit);
- wlc_reset(wl->wlc);
+ BCMMSG(wl->pub->ieee_hw->wiphy, "wl%d\n", wl->pub->unit);
+ brcms_c_reset(wl->wlc);
/* dpc will not be rescheduled */
wl->resched = 0;
@@ -1443,54 +1341,46 @@ uint wl_reset(struct wl_info *wl)
* These are interrupt on/off entry points. Disable interrupts
* during interrupt state transition.
*/
-void wl_intrson(struct wl_info *wl)
+void brcms_intrson(struct brcms_info *wl)
{
unsigned long flags;
- INT_LOCK(wl, flags);
- wlc_intrson(wl->wlc);
- INT_UNLOCK(wl, flags);
+ spin_lock_irqsave(&wl->isr_lock, flags);
+ brcms_c_intrson(wl->wlc);
+ spin_unlock_irqrestore(&wl->isr_lock, flags);
}
-/*
- * precondition: perimeter lock has been acquired
- */
-bool wl_alloc_dma_resources(struct wl_info *wl, uint addrwidth)
-{
- return true;
-}
-
-u32 wl_intrsoff(struct wl_info *wl)
+u32 brcms_intrsoff(struct brcms_info *wl)
{
unsigned long flags;
u32 status;
- INT_LOCK(wl, flags);
- status = wlc_intrsoff(wl->wlc);
- INT_UNLOCK(wl, flags);
+ spin_lock_irqsave(&wl->isr_lock, flags);
+ status = brcms_c_intrsoff(wl->wlc);
+ spin_unlock_irqrestore(&wl->isr_lock, flags);
return status;
}
-void wl_intrsrestore(struct wl_info *wl, u32 macintmask)
+void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask)
{
unsigned long flags;
- INT_LOCK(wl, flags);
- wlc_intrsrestore(wl->wlc, macintmask);
- INT_UNLOCK(wl, flags);
+ spin_lock_irqsave(&wl->isr_lock, flags);
+ brcms_c_intrsrestore(wl->wlc, macintmask);
+ spin_unlock_irqrestore(&wl->isr_lock, flags);
}
/*
* precondition: perimeter lock has been acquired
*/
-int wl_up(struct wl_info *wl)
+int brcms_up(struct brcms_info *wl)
{
int error = 0;
if (wl->pub->up)
return 0;
- error = wlc_up(wl->wlc);
+ error = brcms_c_up(wl->wlc);
return error;
}
@@ -1498,119 +1388,51 @@ int wl_up(struct wl_info *wl)
/*
* precondition: perimeter lock has been acquired
*/
-void wl_down(struct wl_info *wl)
+void brcms_down(struct brcms_info *wl)
{
uint callbacks, ret_val = 0;
/* call common down function */
- ret_val = wlc_down(wl->wlc);
+ ret_val = brcms_c_down(wl->wlc);
callbacks = atomic_read(&wl->callbacks) - ret_val;
/* wait for down callbacks to complete */
- WL_UNLOCK(wl);
+ spin_unlock_bh(&wl->lock);
/* For HIGH_only driver, it's important to actually schedule other work,
* not just spin wait since everything runs at schedule level
*/
SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000);
- WL_LOCK(wl);
-}
-
-static irqreturn_t wl_isr(int irq, void *dev_id)
-{
- struct wl_info *wl;
- bool ours, wantdpc;
- unsigned long flags;
-
- wl = (struct wl_info *) dev_id;
-
- WL_ISRLOCK(wl, flags);
-
- /* call common first level interrupt handler */
- ours = wlc_isr(wl->wlc, &wantdpc);
- if (ours) {
- /* if more to do... */
- if (wantdpc) {
-
- /* ...and call the second level interrupt handler */
- /* schedule dpc */
- tasklet_schedule(&wl->tasklet);
- }
- }
-
- WL_ISRUNLOCK(wl, flags);
-
- return IRQ_RETVAL(ours);
-}
-
-static void wl_dpc(unsigned long data)
-{
- struct wl_info *wl;
-
- wl = (struct wl_info *) data;
-
- WL_LOCK(wl);
-
- /* call the common second level interrupt handler */
- if (wl->pub->up) {
- if (wl->resched) {
- unsigned long flags;
-
- INT_LOCK(wl, flags);
- wlc_intrsupd(wl->wlc);
- INT_UNLOCK(wl, flags);
- }
-
- wl->resched = wlc_dpc(wl->wlc, true);
- }
-
- /* wlc_dpc() may bring the driver down */
- if (!wl->pub->up)
- goto done;
-
- /* re-schedule dpc */
- if (wl->resched)
- tasklet_schedule(&wl->tasklet);
- else {
- /* re-enable interrupts */
- wl_intrson(wl);
- }
-
- done:
- WL_UNLOCK(wl);
-}
-
-/*
- * is called by the kernel from software irq context
- */
-static void wl_timer(unsigned long data)
-{
- _wl_timer((struct wl_timer *) data);
+ spin_lock_bh(&wl->lock);
}
/*
* precondition: perimeter lock is not acquired
*/
-static void _wl_timer(struct wl_timer *t)
+static void _brcms_timer(struct work_struct *work)
{
- WL_LOCK(t->wl);
+ struct brcms_timer *t = container_of(work, struct brcms_timer,
+ dly_wrk.work);
+
+ spin_lock_bh(&t->wl->lock);
if (t->set) {
if (t->periodic) {
- t->timer.expires = jiffies + t->ms * HZ / 1000;
atomic_inc(&t->wl->callbacks);
- add_timer(&t->timer);
- t->set = true;
- } else
+ ieee80211_queue_delayed_work(t->wl->pub->ieee_hw,
+ &t->dly_wrk,
+ msecs_to_jiffies(t->ms));
+ } else {
t->set = false;
+ }
t->fn(t->arg);
}
atomic_dec(&t->wl->callbacks);
- WL_UNLOCK(t->wl);
+ spin_unlock_bh(&t->wl->lock);
}
/*
@@ -1619,21 +1441,17 @@ static void _wl_timer(struct wl_timer *t)
*
* precondition: perimeter lock has been acquired
*/
-struct wl_timer *wl_init_timer(struct wl_info *wl, void (*fn) (void *arg),
- void *arg, const char *name)
+struct brcms_timer *brcms_init_timer(struct brcms_info *wl,
+ void (*fn) (void *arg),
+ void *arg, const char *name)
{
- struct wl_timer *t;
+ struct brcms_timer *t;
- t = kzalloc(sizeof(struct wl_timer), GFP_ATOMIC);
- if (!t) {
- wiphy_err(wl->wiphy, "wl%d: wl_init_timer: out of memory\n",
- wl->pub->unit);
- return 0;
- }
+ t = kzalloc(sizeof(struct brcms_timer), GFP_ATOMIC);
+ if (!t)
+ return NULL;
- init_timer(&t->timer);
- t->timer.data = (unsigned long) t;
- t->timer.function = wl_timer;
+ INIT_DELAYED_WORK(&t->dly_wrk, _brcms_timer);
t->wl = wl;
t->fn = fn;
t->arg = arg;
@@ -1649,26 +1467,28 @@ struct wl_timer *wl_init_timer(struct wl_info *wl, void (*fn) (void *arg),
return t;
}
-/* BMAC_NOTE: Add timer adds only the kernel timer since it's going to be more accurate
+/*
+ * adds only the kernel timer since it's going to be more accurate
* as well as it's easier to make it periodic
*
* precondition: perimeter lock has been acquired
*/
-void wl_add_timer(struct wl_info *wl, struct wl_timer *t, uint ms, int periodic)
+void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic)
{
+ struct ieee80211_hw *hw = t->wl->pub->ieee_hw;
+
#ifdef BCMDBG
- if (t->set) {
- wiphy_err(wl->wiphy, "%s: Already set. Name: %s, per %d\n",
+ if (t->set)
+ wiphy_err(hw->wiphy, "%s: Already set. Name: %s, per %d\n",
__func__, t->name, periodic);
- }
#endif
t->ms = ms;
t->periodic = (bool) periodic;
t->set = true;
- t->timer.expires = jiffies + ms * HZ / 1000;
- atomic_inc(&wl->callbacks);
- add_timer(&t->timer);
+ atomic_inc(&t->wl->callbacks);
+
+ ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms));
}
/*
@@ -1676,14 +1496,14 @@ void wl_add_timer(struct wl_info *wl, struct wl_timer *t, uint ms, int periodic)
*
* precondition: perimeter lock has been acquired
*/
-bool wl_del_timer(struct wl_info *wl, struct wl_timer *t)
+bool brcms_del_timer(struct brcms_timer *t)
{
if (t->set) {
t->set = false;
- if (!del_timer(&t->timer)) {
+ if (!cancel_delayed_work(&t->dly_wrk))
return false;
- }
- atomic_dec(&wl->callbacks);
+
+ atomic_dec(&t->wl->callbacks);
}
return true;
@@ -1692,12 +1512,13 @@ bool wl_del_timer(struct wl_info *wl, struct wl_timer *t)
/*
* precondition: perimeter lock has been acquired
*/
-void wl_free_timer(struct wl_info *wl, struct wl_timer *t)
+void brcms_free_timer(struct brcms_timer *t)
{
- struct wl_timer *tmp;
+ struct brcms_info *wl = t->wl;
+ struct brcms_timer *tmp;
/* delete the timer in case it is active */
- wl_del_timer(wl, t);
+ brcms_del_timer(t);
if (wl->timers == t) {
wl->timers = wl->timers->next;
@@ -1725,47 +1546,26 @@ void wl_free_timer(struct wl_info *wl, struct wl_timer *t)
}
/*
- * runs in software irq context
- *
- * precondition: perimeter lock is not acquired
- */
-static int wl_linux_watchdog(void *ctx)
-{
- return 0;
-}
-
-struct wl_fw_hdr {
- u32 offset;
- u32 len;
- u32 idx;
-};
-
-char *wl_firmwares[WL_MAX_FW] = {
- "brcm/bcm43xx",
- NULL
-};
-
-/*
* precondition: perimeter lock has been acquired
*/
-int wl_ucode_init_buf(struct wl_info *wl, void **pbuf, u32 idx)
+int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx)
{
int i, entry;
const u8 *pdata;
- struct wl_fw_hdr *hdr;
+ struct firmware_hdr *hdr;
for (i = 0; i < wl->fw.fw_cnt; i++) {
- hdr = (struct wl_fw_hdr *)wl->fw.fw_hdr[i]->data;
+ hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data;
for (entry = 0; entry < wl->fw.hdr_num_entries[i];
entry++, hdr++) {
- if (hdr->idx == idx) {
- pdata = wl->fw.fw_bin[i]->data + hdr->offset;
- *pbuf = kmalloc(hdr->len, GFP_ATOMIC);
- if (*pbuf == NULL) {
- wiphy_err(wl->wiphy, "fail to alloc %d"
- " bytes\n", hdr->len);
+ u32 len = le32_to_cpu(hdr->len);
+ if (le32_to_cpu(hdr->idx) == idx) {
+ pdata = wl->fw.fw_bin[i]->data +
+ le32_to_cpu(hdr->offset);
+ *pbuf = kmalloc(len, GFP_ATOMIC);
+ if (*pbuf == NULL)
goto fail;
- }
- memcpy(*pbuf, pdata, hdr->len);
+
+ memcpy(*pbuf, pdata, len);
return 0;
}
}
@@ -1778,26 +1578,27 @@ fail:
}
/*
- * Precondition: Since this function is called in wl_pci_probe() context,
+ * Precondition: Since this function is called in brcms_pci_probe() context,
* no locking is required.
*/
-int wl_ucode_init_uint(struct wl_info *wl, u32 *data, u32 idx)
+int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx)
{
int i, entry;
const u8 *pdata;
- struct wl_fw_hdr *hdr;
+ struct firmware_hdr *hdr;
for (i = 0; i < wl->fw.fw_cnt; i++) {
- hdr = (struct wl_fw_hdr *)wl->fw.fw_hdr[i]->data;
+ hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data;
for (entry = 0; entry < wl->fw.hdr_num_entries[i];
entry++, hdr++) {
- if (hdr->idx == idx) {
- pdata = wl->fw.fw_bin[i]->data + hdr->offset;
- if (hdr->len != 4) {
+ if (le32_to_cpu(hdr->idx) == idx) {
+ pdata = wl->fw.fw_bin[i]->data +
+ le32_to_cpu(hdr->offset);
+ if (le32_to_cpu(hdr->len) != 4) {
wiphy_err(wl->wiphy,
"ERROR: fw hdr len\n");
return -ENOMSG;
}
- *data = *((u32 *) pdata);
+ *n_bytes = le32_to_cpu(*((__le32 *) pdata));
return 0;
}
}
@@ -1807,80 +1608,28 @@ int wl_ucode_init_uint(struct wl_info *wl, u32 *data, u32 idx)
}
/*
- * Precondition: Since this function is called in wl_pci_probe() context,
- * no locking is required.
- */
-static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev)
-{
- int status;
- struct device *device = &pdev->dev;
- char fw_name[100];
- int i;
-
- memset((void *)&wl->fw, 0, sizeof(struct wl_firmware));
- for (i = 0; i < WL_MAX_FW; i++) {
- if (wl_firmwares[i] == NULL)
- break;
- sprintf(fw_name, "%s-%d.fw", wl_firmwares[i],
- UCODE_LOADER_API_VER);
- status = request_firmware(&wl->fw.fw_bin[i], fw_name, device);
- if (status) {
- wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
- KBUILD_MODNAME, fw_name);
- return status;
- }
- sprintf(fw_name, "%s_hdr-%d.fw", wl_firmwares[i],
- UCODE_LOADER_API_VER);
- status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device);
- if (status) {
- wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
- KBUILD_MODNAME, fw_name);
- return status;
- }
- wl->fw.hdr_num_entries[i] =
- wl->fw.fw_hdr[i]->size / (sizeof(struct wl_fw_hdr));
- }
- wl->fw.fw_cnt = i;
- return wl_ucode_data_init(wl);
-}
-
-/*
* precondition: can both be called locked and unlocked
*/
-void wl_ucode_free_buf(void *p)
+void brcms_ucode_free_buf(void *p)
{
kfree(p);
}
/*
- * Precondition: Since this function is called in wl_pci_probe() context,
- * no locking is required.
- */
-static void wl_release_fw(struct wl_info *wl)
-{
- int i;
- for (i = 0; i < WL_MAX_FW; i++) {
- release_firmware(wl->fw.fw_bin[i]);
- release_firmware(wl->fw.fw_hdr[i]);
- }
-}
-
-
-/*
* checks validity of all firmware images loaded from user space
*
- * Precondition: Since this function is called in wl_pci_probe() context,
+ * Precondition: Since this function is called in brcms_pci_probe() context,
* no locking is required.
*/
-int wl_check_firmwares(struct wl_info *wl)
+int brcms_check_firmwares(struct brcms_info *wl)
{
int i;
int entry;
int rc = 0;
const struct firmware *fw;
const struct firmware *fw_hdr;
- struct wl_fw_hdr *ucode_hdr;
- for (i = 0; i < WL_MAX_FW && rc == 0; i++) {
+ struct firmware_hdr *ucode_hdr;
+ for (i = 0; i < MAX_FW_IMAGES && rc == 0; i++) {
fw = wl->fw.fw_bin[i];
fw_hdr = wl->fw.fw_hdr[i];
if (fw == NULL && fw_hdr == NULL) {
@@ -1889,10 +1638,10 @@ int wl_check_firmwares(struct wl_info *wl)
wiphy_err(wl->wiphy, "%s: invalid bin/hdr fw\n",
__func__);
rc = -EBADF;
- } else if (fw_hdr->size % sizeof(struct wl_fw_hdr)) {
+ } else if (fw_hdr->size % sizeof(struct firmware_hdr)) {
wiphy_err(wl->wiphy, "%s: non integral fw hdr file "
"size %zu/%zu\n", __func__, fw_hdr->size,
- sizeof(struct wl_fw_hdr));
+ sizeof(struct firmware_hdr));
rc = -EBADF;
} else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) {
wiphy_err(wl->wiphy, "%s: out of bounds fw file size "
@@ -1900,10 +1649,11 @@ int wl_check_firmwares(struct wl_info *wl)
rc = -EBADF;
} else {
/* check if ucode section overruns firmware image */
- ucode_hdr = (struct wl_fw_hdr *)fw_hdr->data;
+ ucode_hdr = (struct firmware_hdr *)fw_hdr->data;
for (entry = 0; entry < wl->fw.hdr_num_entries[i] &&
!rc; entry++, ucode_hdr++) {
- if (ucode_hdr->offset + ucode_hdr->len >
+ if (le32_to_cpu(ucode_hdr->offset) +
+ le32_to_cpu(ucode_hdr->len) >
fw->size) {
wiphy_err(wl->wiphy,
"%s: conflicting bin/hdr\n",
@@ -1924,24 +1674,24 @@ int wl_check_firmwares(struct wl_info *wl)
/*
* precondition: perimeter lock has been acquired
*/
-bool wl_rfkill_set_hw_state(struct wl_info *wl)
+bool brcms_rfkill_set_hw_state(struct brcms_info *wl)
{
- bool blocked = wlc_check_radio_disabled(wl->wlc);
+ bool blocked = brcms_c_check_radio_disabled(wl->wlc);
- WL_UNLOCK(wl);
+ spin_unlock_bh(&wl->lock);
wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
if (blocked)
wiphy_rfkill_start_polling(wl->pub->ieee_hw->wiphy);
- WL_LOCK(wl);
+ spin_lock_bh(&wl->lock);
return blocked;
}
/*
* precondition: perimeter lock has been acquired
*/
-void wl_msleep(struct wl_info *wl, uint ms)
+void brcms_msleep(struct brcms_info *wl, uint ms)
{
- WL_UNLOCK(wl);
+ spin_unlock_bh(&wl->lock);
msleep(ms);
- WL_LOCK(wl);
+ spin_lock_bh(&wl->lock);
}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h
new file mode 100644
index 000000000000..177f0e44e4b6
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_MAC80211_IF_H_
+#define _BRCM_MAC80211_IF_H_
+
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+
+#include "ucode_loader.h"
+/*
+ * Starting index for 5G rates in the
+ * legacy rate table.
+ */
+#define BRCMS_LEGACY_5G_RATE_OFFSET 4
+
+/* softmac ioctl definitions */
+#define BRCMS_SET_SHORTSLOT_OVERRIDE 146
+
+struct brcms_timer {
+ struct delayed_work dly_wrk;
+ struct brcms_info *wl;
+ void (*fn) (void *); /* function called upon expiration */
+ void *arg; /* fixed argument provided to called function */
+ uint ms;
+ bool periodic;
+ bool set; /* indicates if timer is active */
+ struct brcms_timer *next; /* for freeing on unload */
+#ifdef BCMDBG
+ char *name; /* Description of the timer */
+#endif
+};
+
+struct brcms_if {
+ uint subunit; /* WDS/BSS unit */
+ struct pci_dev *pci_dev;
+};
+
+#define MAX_FW_IMAGES 4
+struct brcms_firmware {
+ u32 fw_cnt;
+ const struct firmware *fw_bin[MAX_FW_IMAGES];
+ const struct firmware *fw_hdr[MAX_FW_IMAGES];
+ u32 hdr_num_entries[MAX_FW_IMAGES];
+};
+
+struct brcms_info {
+ struct brcms_pub *pub; /* pointer to public wlc state */
+ struct brcms_c_info *wlc; /* pointer to private common data */
+ u32 magic;
+
+ int irq;
+
+ spinlock_t lock; /* per-device perimeter lock */
+ spinlock_t isr_lock; /* per-device ISR synchronization lock */
+
+ /* regsva for unmap in brcms_free() */
+ void __iomem *regsva; /* opaque chip registers virtual address */
+
+ /* timer related fields */
+ atomic_t callbacks; /* # outstanding callback functions */
+ struct brcms_timer *timers; /* timer cleanup queue */
+
+ struct tasklet_struct tasklet; /* dpc tasklet */
+ bool resched; /* dpc needs to be and is rescheduled */
+ struct brcms_firmware fw;
+ struct wiphy *wiphy;
+ struct brcms_ucode ucode;
+};
+
+/* misc callbacks */
+extern void brcms_init(struct brcms_info *wl);
+extern uint brcms_reset(struct brcms_info *wl);
+extern void brcms_intrson(struct brcms_info *wl);
+extern u32 brcms_intrsoff(struct brcms_info *wl);
+extern void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask);
+extern int brcms_up(struct brcms_info *wl);
+extern void brcms_down(struct brcms_info *wl);
+extern void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif,
+ bool state, int prio);
+extern bool brcms_rfkill_set_hw_state(struct brcms_info *wl);
+
+/* timer functions */
+extern struct brcms_timer *brcms_init_timer(struct brcms_info *wl,
+ void (*fn) (void *arg), void *arg,
+ const char *name);
+extern void brcms_free_timer(struct brcms_timer *timer);
+extern void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic);
+extern bool brcms_del_timer(struct brcms_timer *timer);
+extern void brcms_msleep(struct brcms_info *wl, uint ms);
+extern void brcms_dpc(unsigned long data);
+extern void brcms_timer(struct brcms_timer *t);
+
+#endif /* _BRCM_MAC80211_IF_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c
new file mode 100644
index 000000000000..510e9bb52287
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c
@@ -0,0 +1,8775 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/pci_ids.h>
+#include <linux/if_ether.h>
+#include <net/mac80211.h>
+#include <brcm_hw_ids.h>
+#include <aiutils.h>
+#include <chipcommon.h>
+#include "rate.h"
+#include "scb.h"
+#include "phy/phy_hal.h"
+#include "channel.h"
+#include "antsel.h"
+#include "stf.h"
+#include "ampdu.h"
+#include "mac80211_if.h"
+#include "ucode_loader.h"
+#include "main.h"
+
+/*
+ * Indication for txflowcontrol that all priority bits in
+ * TXQ_STOP_FOR_PRIOFC_MASK are to be considered.
+ */
+#define ALLPRIO -1
+
+/*
+ * 32 SSID chars, max of 4 chars for each SSID char "\xFF", plus NULL.
+ */
+#define SSID_FMT_BUF_LEN ((4 * IEEE80211_MAX_SSID_LEN) + 1)
+
+/* watchdog timer, in unit of ms */
+#define TIMER_INTERVAL_WATCHDOG 1000
+/* radio monitor timer, in unit of ms */
+#define TIMER_INTERVAL_RADIOCHK 800
+
+/* Max MPC timeout, in unit of watchdog */
+#ifndef BRCMS_MPC_MAX_DELAYCNT
+#define BRCMS_MPC_MAX_DELAYCNT 10
+#endif
+
+/* Min MPC timeout, in unit of watchdog */
+#define BRCMS_MPC_MIN_DELAYCNT 1
+#define BRCMS_MPC_THRESHOLD 3 /* MPC count threshold level */
+
+/* beacon interval, in unit of 1024TU */
+#define BEACON_INTERVAL_DEFAULT 100
+/* DTIM interval, in unit of beacon interval */
+#define DTIM_INTERVAL_DEFAULT 3
+
+/* Scale down delays to accommodate QT slow speed */
+/* beacon interval, in unit of 1024TU */
+#define BEACON_INTERVAL_DEF_QT 20
+/* DTIM interval, in unit of beacon interval */
+#define DTIM_INTERVAL_DEF_QT 1
+
+#define TBTT_ALIGN_LEEWAY_US 100 /* min leeway before first TBTT in us */
+
+/* n-mode support capability */
+/* 2x2 includes both 1x1 & 2x2 devices
+ * reserved #define 2 for future when we want to separate 1x1 & 2x2 and
+ * control it independently
+ */
+#define WL_11N_2x2 1
+#define WL_11N_3x3 3
+#define WL_11N_4x4 4
+
+/* define 11n feature disable flags */
+#define WLFEATURE_DISABLE_11N 0x00000001
+#define WLFEATURE_DISABLE_11N_STBC_TX 0x00000002
+#define WLFEATURE_DISABLE_11N_STBC_RX 0x00000004
+#define WLFEATURE_DISABLE_11N_SGI_TX 0x00000008
+#define WLFEATURE_DISABLE_11N_SGI_RX 0x00000010
+#define WLFEATURE_DISABLE_11N_AMPDU_TX 0x00000020
+#define WLFEATURE_DISABLE_11N_AMPDU_RX 0x00000040
+#define WLFEATURE_DISABLE_11N_GF 0x00000080
+
+#define EDCF_ACI_MASK 0x60
+#define EDCF_ACI_SHIFT 5
+#define EDCF_ECWMIN_MASK 0x0f
+#define EDCF_ECWMAX_SHIFT 4
+#define EDCF_AIFSN_MASK 0x0f
+#define EDCF_AIFSN_MAX 15
+#define EDCF_ECWMAX_MASK 0xf0
+
+#define EDCF_AC_BE_TXOP_STA 0x0000
+#define EDCF_AC_BK_TXOP_STA 0x0000
+#define EDCF_AC_VO_ACI_STA 0x62
+#define EDCF_AC_VO_ECW_STA 0x32
+#define EDCF_AC_VI_ACI_STA 0x42
+#define EDCF_AC_VI_ECW_STA 0x43
+#define EDCF_AC_BK_ECW_STA 0xA4
+#define EDCF_AC_VI_TXOP_STA 0x005e
+#define EDCF_AC_VO_TXOP_STA 0x002f
+#define EDCF_AC_BE_ACI_STA 0x03
+#define EDCF_AC_BE_ECW_STA 0xA4
+#define EDCF_AC_BK_ACI_STA 0x27
+#define EDCF_AC_VO_TXOP_AP 0x002f
+
+#define EDCF_TXOP2USEC(txop) ((txop) << 5)
+#define EDCF_ECW2CW(exp) ((1 << (exp)) - 1)
+
+#define APHY_SYMBOL_TIME 4
+#define APHY_PREAMBLE_TIME 16
+#define APHY_SIGNAL_TIME 4
+#define APHY_SIFS_TIME 16
+#define APHY_SERVICE_NBITS 16
+#define APHY_TAIL_NBITS 6
+#define BPHY_SIFS_TIME 10
+#define BPHY_PLCP_SHORT_TIME 96
+
+#define PREN_PREAMBLE 24
+#define PREN_MM_EXT 12
+#define PREN_PREAMBLE_EXT 4
+
+#define DOT11_MAC_HDR_LEN 24
+#define DOT11_ACK_LEN 10
+#define DOT11_BA_LEN 4
+#define DOT11_OFDM_SIGNAL_EXTENSION 6
+#define DOT11_MIN_FRAG_LEN 256
+#define DOT11_RTS_LEN 16
+#define DOT11_CTS_LEN 10
+#define DOT11_BA_BITMAP_LEN 128
+#define DOT11_MIN_BEACON_PERIOD 1
+#define DOT11_MAX_BEACON_PERIOD 0xFFFF
+#define DOT11_MAXNUMFRAGS 16
+#define DOT11_MAX_FRAG_LEN 2346
+
+#define BPHY_PLCP_TIME 192
+#define RIFS_11N_TIME 2
+
+#define WME_VER 1
+#define WME_SUBTYPE_PARAM_IE 1
+#define WME_TYPE 2
+#define WME_OUI "\x00\x50\xf2"
+
+#define AC_BE 0
+#define AC_BK 1
+#define AC_VI 2
+#define AC_VO 3
+
+#define BCN_TMPL_LEN 512 /* length of the BCN template area */
+
+/* brcms_bss_info flag bit values */
+#define BRCMS_BSS_HT 0x0020 /* BSS is HT (MIMO) capable */
+
+/* Flags used in brcms_c_txq_info.stopped */
+/* per prio flow control bits */
+#define TXQ_STOP_FOR_PRIOFC_MASK 0x000000FF
+/* stop txq enqueue for packet drain */
+#define TXQ_STOP_FOR_PKT_DRAIN 0x00000100
+/* stop txq enqueue for ampdu flow control */
+#define TXQ_STOP_FOR_AMPDU_FLOW_CNTRL 0x00000200
+
+#define BRCMS_HWRXOFF 38 /* chip rx buffer offset */
+
+/* Find basic rate for a given rate */
+static u8 brcms_basic_rate(struct brcms_c_info *wlc, u32 rspec)
+{
+ if (is_mcs_rate(rspec))
+ return wlc->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK]
+ .leg_ofdm];
+ return wlc->band->basic_rate[rspec & RSPEC_RATE_MASK];
+}
+
+static u16 frametype(u32 rspec, u8 mimoframe)
+{
+ if (is_mcs_rate(rspec))
+ return mimoframe;
+ return is_cck_rate(rspec) ? FT_CCK : FT_OFDM;
+}
+
+/* rfdisable delay timer 500 ms, runs of ALP clock */
+#define RFDISABLE_DEFAULT 10000000
+
+#define BRCMS_TEMPSENSE_PERIOD 10 /* 10 second timeout */
+
+/* precedences numbers for wlc queues. These are twice as may levels as
+ * 802.1D priorities.
+ * Odd numbers are used for HI priority traffic at same precedence levels
+ * These constants are used ONLY by wlc_prio2prec_map. Do not use them
+ * elsewhere.
+ */
+#define _BRCMS_PREC_NONE 0 /* None = - */
+#define _BRCMS_PREC_BK 2 /* BK - Background */
+#define _BRCMS_PREC_BE 4 /* BE - Best-effort */
+#define _BRCMS_PREC_EE 6 /* EE - Excellent-effort */
+#define _BRCMS_PREC_CL 8 /* CL - Controlled Load */
+#define _BRCMS_PREC_VI 10 /* Vi - Video */
+#define _BRCMS_PREC_VO 12 /* Vo - Voice */
+#define _BRCMS_PREC_NC 14 /* NC - Network Control */
+
+/* The BSS is generating beacons in HW */
+#define BRCMS_BSSCFG_HW_BCN 0x20
+
+#define SYNTHPU_DLY_APHY_US 3700 /* a phy synthpu_dly time in us */
+#define SYNTHPU_DLY_BPHY_US 1050 /* b/g phy synthpu_dly time in us */
+#define SYNTHPU_DLY_NPHY_US 2048 /* n phy REV3 synthpu_dly time in us */
+#define SYNTHPU_DLY_LPPHY_US 300 /* lpphy synthpu_dly time in us */
+
+#define SYNTHPU_DLY_PHY_US_QT 100 /* QT synthpu_dly time in us */
+
+#define ANTCNT 10 /* vanilla M_MAX_ANTCNT value */
+
+/* Per-AC retry limit register definitions; uses defs.h bitfield macros */
+#define EDCF_SHORT_S 0
+#define EDCF_SFB_S 4
+#define EDCF_LONG_S 8
+#define EDCF_LFB_S 12
+#define EDCF_SHORT_M BITFIELD_MASK(4)
+#define EDCF_SFB_M BITFIELD_MASK(4)
+#define EDCF_LONG_M BITFIELD_MASK(4)
+#define EDCF_LFB_M BITFIELD_MASK(4)
+
+#define RETRY_SHORT_DEF 7 /* Default Short retry Limit */
+#define RETRY_SHORT_MAX 255 /* Maximum Short retry Limit */
+#define RETRY_LONG_DEF 4 /* Default Long retry count */
+#define RETRY_SHORT_FB 3 /* Short count for fallback rate */
+#define RETRY_LONG_FB 2 /* Long count for fallback rate */
+
+#define APHY_CWMIN 15
+#define PHY_CWMAX 1023
+
+#define EDCF_AIFSN_MIN 1
+
+#define FRAGNUM_MASK 0xF
+
+#define APHY_SLOT_TIME 9
+#define BPHY_SLOT_TIME 20
+
+#define WL_SPURAVOID_OFF 0
+#define WL_SPURAVOID_ON1 1
+#define WL_SPURAVOID_ON2 2
+
+/* invalid core flags, use the saved coreflags */
+#define BRCMS_USE_COREFLAGS 0xffffffff
+
+/* values for PLCPHdr_override */
+#define BRCMS_PLCP_AUTO -1
+#define BRCMS_PLCP_SHORT 0
+#define BRCMS_PLCP_LONG 1
+
+/* values for g_protection_override and n_protection_override */
+#define BRCMS_PROTECTION_AUTO -1
+#define BRCMS_PROTECTION_OFF 0
+#define BRCMS_PROTECTION_ON 1
+#define BRCMS_PROTECTION_MMHDR_ONLY 2
+#define BRCMS_PROTECTION_CTS_ONLY 3
+
+/* values for g_protection_control and n_protection_control */
+#define BRCMS_PROTECTION_CTL_OFF 0
+#define BRCMS_PROTECTION_CTL_LOCAL 1
+#define BRCMS_PROTECTION_CTL_OVERLAP 2
+
+/* values for n_protection */
+#define BRCMS_N_PROTECTION_OFF 0
+#define BRCMS_N_PROTECTION_OPTIONAL 1
+#define BRCMS_N_PROTECTION_20IN40 2
+#define BRCMS_N_PROTECTION_MIXEDMODE 3
+
+/* values for band specific 40MHz capabilities */
+#define BRCMS_N_BW_20ALL 0
+#define BRCMS_N_BW_40ALL 1
+#define BRCMS_N_BW_20IN2G_40IN5G 2
+
+/* bitflags for SGI support (sgi_rx iovar) */
+#define BRCMS_N_SGI_20 0x01
+#define BRCMS_N_SGI_40 0x02
+
+/* defines used by the nrate iovar */
+/* MSC in use,indicates b0-6 holds an mcs */
+#define NRATE_MCS_INUSE 0x00000080
+/* rate/mcs value */
+#define NRATE_RATE_MASK 0x0000007f
+/* stf mode mask: siso, cdd, stbc, sdm */
+#define NRATE_STF_MASK 0x0000ff00
+/* stf mode shift */
+#define NRATE_STF_SHIFT 8
+/* bit indicates override both rate & mode */
+#define NRATE_OVERRIDE 0x80000000
+/* bit indicate to override mcs only */
+#define NRATE_OVERRIDE_MCS_ONLY 0x40000000
+#define NRATE_SGI_MASK 0x00800000 /* sgi mode */
+#define NRATE_SGI_SHIFT 23 /* sgi mode */
+#define NRATE_LDPC_CODING 0x00400000 /* bit indicates adv coding in use */
+#define NRATE_LDPC_SHIFT 22 /* ldpc shift */
+
+#define NRATE_STF_SISO 0 /* stf mode SISO */
+#define NRATE_STF_CDD 1 /* stf mode CDD */
+#define NRATE_STF_STBC 2 /* stf mode STBC */
+#define NRATE_STF_SDM 3 /* stf mode SDM */
+
+#define MAX_DMA_SEGS 4
+
+/* Max # of entries in Tx FIFO based on 4kb page size */
+#define NTXD 256
+/* Max # of entries in Rx FIFO based on 4kb page size */
+#define NRXD 256
+
+/* try to keep this # rbufs posted to the chip */
+#define NRXBUFPOST 32
+
+/* data msg txq hiwat mark */
+#define BRCMS_DATAHIWAT 50
+
+/* bounded rx loops */
+#define RXBND 8 /* max # frames to process in brcms_c_recv() */
+#define TXSBND 8 /* max # tx status to process in wlc_txstatus() */
+
+/*
+ * 32 SSID chars, max of 4 chars for each SSID char "\xFF", plus NULL.
+ */
+#define SSID_FMT_BUF_LEN ((4 * IEEE80211_MAX_SSID_LEN) + 1)
+
+/* brcmu_format_flags() bit description structure */
+struct brcms_c_bit_desc {
+ u32 bit;
+ const char *name;
+};
+
+/*
+ * The following table lists the buffer memory allocated to xmt fifos in HW.
+ * the size is in units of 256bytes(one block), total size is HW dependent
+ * ucode has default fifo partition, sw can overwrite if necessary
+ *
+ * This is documented in twiki under the topic UcodeTxFifo. Please ensure
+ * the twiki is updated before making changes.
+ */
+
+/* Starting corerev for the fifo size table */
+#define XMTFIFOTBL_STARTREV 20
+
+struct d11init {
+ __le16 addr;
+ __le16 size;
+ __le32 value;
+};
+
+struct edcf_acparam {
+ u8 ACI;
+ u8 ECW;
+ u16 TXOP;
+} __packed;
+
+const u8 prio2fifo[NUMPRIO] = {
+ TX_AC_BE_FIFO, /* 0 BE AC_BE Best Effort */
+ TX_AC_BK_FIFO, /* 1 BK AC_BK Background */
+ TX_AC_BK_FIFO, /* 2 -- AC_BK Background */
+ TX_AC_BE_FIFO, /* 3 EE AC_BE Best Effort */
+ TX_AC_VI_FIFO, /* 4 CL AC_VI Video */
+ TX_AC_VI_FIFO, /* 5 VI AC_VI Video */
+ TX_AC_VO_FIFO, /* 6 VO AC_VO Voice */
+ TX_AC_VO_FIFO /* 7 NC AC_VO Voice */
+};
+
+/* debug/trace */
+uint brcm_msg_level =
+#if defined(BCMDBG)
+ LOG_ERROR_VAL;
+#else
+ 0;
+#endif /* BCMDBG */
+
+/* TX FIFO number to WME/802.1E Access Category */
+static const u8 wme_fifo2ac[] = { AC_BK, AC_BE, AC_VI, AC_VO, AC_BE, AC_BE };
+
+/* WME/802.1E Access Category to TX FIFO number */
+static const u8 wme_ac2fifo[] = { 1, 0, 2, 3 };
+
+/* 802.1D Priority to precedence queue mapping */
+const u8 wlc_prio2prec_map[] = {
+ _BRCMS_PREC_BE, /* 0 BE - Best-effort */
+ _BRCMS_PREC_BK, /* 1 BK - Background */
+ _BRCMS_PREC_NONE, /* 2 None = - */
+ _BRCMS_PREC_EE, /* 3 EE - Excellent-effort */
+ _BRCMS_PREC_CL, /* 4 CL - Controlled Load */
+ _BRCMS_PREC_VI, /* 5 Vi - Video */
+ _BRCMS_PREC_VO, /* 6 Vo - Voice */
+ _BRCMS_PREC_NC, /* 7 NC - Network Control */
+};
+
+static const u16 xmtfifo_sz[][NFIFO] = {
+ /* corerev 20: 5120, 49152, 49152, 5376, 4352, 1280 */
+ {20, 192, 192, 21, 17, 5},
+ /* corerev 21: 2304, 14848, 5632, 3584, 3584, 1280 */
+ {9, 58, 22, 14, 14, 5},
+ /* corerev 22: 5120, 49152, 49152, 5376, 4352, 1280 */
+ {20, 192, 192, 21, 17, 5},
+ /* corerev 23: 5120, 49152, 49152, 5376, 4352, 1280 */
+ {20, 192, 192, 21, 17, 5},
+ /* corerev 24: 2304, 14848, 5632, 3584, 3584, 1280 */
+ {9, 58, 22, 14, 14, 5},
+};
+
+static const u8 acbitmap2maxprio[] = {
+ PRIO_8021D_BE, PRIO_8021D_BE, PRIO_8021D_BK, PRIO_8021D_BK,
+ PRIO_8021D_VI, PRIO_8021D_VI, PRIO_8021D_VI, PRIO_8021D_VI,
+ PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO,
+ PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO
+};
+
+#ifdef BCMDBG
+static const char * const fifo_names[] = {
+ "AC_BK", "AC_BE", "AC_VI", "AC_VO", "BCMC", "ATIM" };
+#else
+static const char fifo_names[6][0];
+#endif
+
+#ifdef BCMDBG
+/* pointer to most recently allocated wl/wlc */
+static struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL);
+#endif
+
+/* currently the best mechanism for determining SIFS is the band in use */
+static u16 get_sifs(struct brcms_band *band)
+{
+ return band->bandtype == BRCM_BAND_5G ? APHY_SIFS_TIME :
+ BPHY_SIFS_TIME;
+}
+
+/*
+ * Detect Card removed.
+ * Even checking an sbconfig register read will not false trigger when the core
+ * is in reset it breaks CF address mechanism. Accessing gphy phyversion will
+ * cause SB error if aphy is in reset on 4306B0-DB. Need a simple accessible
+ * reg with fixed 0/1 pattern (some platforms return all 0).
+ * If clocks are present, call the sb routine which will figure out if the
+ * device is removed.
+ */
+static bool brcms_deviceremoved(struct brcms_c_info *wlc)
+{
+ if (!wlc->hw->clk)
+ return ai_deviceremoved(wlc->hw->sih);
+ return (R_REG(&wlc->hw->regs->maccontrol) &
+ (MCTL_PSM_JMP_0 | MCTL_IHR_EN)) != MCTL_IHR_EN;
+}
+
+/* sum the individual fifo tx pending packet counts */
+static s16 brcms_txpktpendtot(struct brcms_c_info *wlc)
+{
+ return wlc->core->txpktpend[0] + wlc->core->txpktpend[1] +
+ wlc->core->txpktpend[2] + wlc->core->txpktpend[3];
+}
+
+static bool brcms_is_mband_unlocked(struct brcms_c_info *wlc)
+{
+ return wlc->pub->_nbands > 1 && !wlc->bandlocked;
+}
+
+static int brcms_chspec_bw(u16 chanspec)
+{
+ if (CHSPEC_IS40(chanspec))
+ return BRCMS_40_MHZ;
+ if (CHSPEC_IS20(chanspec))
+ return BRCMS_20_MHZ;
+
+ return BRCMS_10_MHZ;
+}
+
+/*
+ * return true if Minimum Power Consumption should
+ * be entered, false otherwise
+ */
+static bool brcms_c_is_non_delay_mpc(struct brcms_c_info *wlc)
+{
+ return false;
+}
+
+static bool brcms_c_ismpc(struct brcms_c_info *wlc)
+{
+ return (wlc->mpc_delay_off == 0) && (brcms_c_is_non_delay_mpc(wlc));
+}
+
+static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg)
+{
+ if (cfg == NULL)
+ return;
+
+ kfree(cfg->current_bss);
+ kfree(cfg);
+}
+
+static void brcms_c_detach_mfree(struct brcms_c_info *wlc)
+{
+ if (wlc == NULL)
+ return;
+
+ brcms_c_bsscfg_mfree(wlc->bsscfg);
+ kfree(wlc->pub);
+ kfree(wlc->modulecb);
+ kfree(wlc->default_bss);
+ kfree(wlc->protection);
+ kfree(wlc->stf);
+ kfree(wlc->bandstate[0]);
+ kfree(wlc->corestate->macstat_snapshot);
+ kfree(wlc->corestate);
+ kfree(wlc->hw->bandstate[0]);
+ kfree(wlc->hw);
+
+ /* free the wlc */
+ kfree(wlc);
+ wlc = NULL;
+}
+
+static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit)
+{
+ struct brcms_bss_cfg *cfg;
+
+ cfg = kzalloc(sizeof(struct brcms_bss_cfg), GFP_ATOMIC);
+ if (cfg == NULL)
+ goto fail;
+
+ cfg->current_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC);
+ if (cfg->current_bss == NULL)
+ goto fail;
+
+ return cfg;
+
+ fail:
+ brcms_c_bsscfg_mfree(cfg);
+ return NULL;
+}
+
+static struct brcms_c_info *
+brcms_c_attach_malloc(uint unit, uint *err, uint devid)
+{
+ struct brcms_c_info *wlc;
+
+ wlc = kzalloc(sizeof(struct brcms_c_info), GFP_ATOMIC);
+ if (wlc == NULL) {
+ *err = 1002;
+ goto fail;
+ }
+
+ /* allocate struct brcms_c_pub state structure */
+ wlc->pub = kzalloc(sizeof(struct brcms_pub), GFP_ATOMIC);
+ if (wlc->pub == NULL) {
+ *err = 1003;
+ goto fail;
+ }
+ wlc->pub->wlc = wlc;
+
+ /* allocate struct brcms_hardware state structure */
+
+ wlc->hw = kzalloc(sizeof(struct brcms_hardware), GFP_ATOMIC);
+ if (wlc->hw == NULL) {
+ *err = 1005;
+ goto fail;
+ }
+ wlc->hw->wlc = wlc;
+
+ wlc->hw->bandstate[0] =
+ kzalloc(sizeof(struct brcms_hw_band) * MAXBANDS, GFP_ATOMIC);
+ if (wlc->hw->bandstate[0] == NULL) {
+ *err = 1006;
+ goto fail;
+ } else {
+ int i;
+
+ for (i = 1; i < MAXBANDS; i++)
+ wlc->hw->bandstate[i] = (struct brcms_hw_band *)
+ ((unsigned long)wlc->hw->bandstate[0] +
+ (sizeof(struct brcms_hw_band) * i));
+ }
+
+ wlc->modulecb =
+ kzalloc(sizeof(struct modulecb) * BRCMS_MAXMODULES, GFP_ATOMIC);
+ if (wlc->modulecb == NULL) {
+ *err = 1009;
+ goto fail;
+ }
+
+ wlc->default_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC);
+ if (wlc->default_bss == NULL) {
+ *err = 1010;
+ goto fail;
+ }
+
+ wlc->bsscfg = brcms_c_bsscfg_malloc(unit);
+ if (wlc->bsscfg == NULL) {
+ *err = 1011;
+ goto fail;
+ }
+
+ wlc->protection = kzalloc(sizeof(struct brcms_protection),
+ GFP_ATOMIC);
+ if (wlc->protection == NULL) {
+ *err = 1016;
+ goto fail;
+ }
+
+ wlc->stf = kzalloc(sizeof(struct brcms_stf), GFP_ATOMIC);
+ if (wlc->stf == NULL) {
+ *err = 1017;
+ goto fail;
+ }
+
+ wlc->bandstate[0] =
+ kzalloc(sizeof(struct brcms_band)*MAXBANDS, GFP_ATOMIC);
+ if (wlc->bandstate[0] == NULL) {
+ *err = 1025;
+ goto fail;
+ } else {
+ int i;
+
+ for (i = 1; i < MAXBANDS; i++)
+ wlc->bandstate[i] = (struct brcms_band *)
+ ((unsigned long)wlc->bandstate[0]
+ + (sizeof(struct brcms_band)*i));
+ }
+
+ wlc->corestate = kzalloc(sizeof(struct brcms_core), GFP_ATOMIC);
+ if (wlc->corestate == NULL) {
+ *err = 1026;
+ goto fail;
+ }
+
+ wlc->corestate->macstat_snapshot =
+ kzalloc(sizeof(struct macstat), GFP_ATOMIC);
+ if (wlc->corestate->macstat_snapshot == NULL) {
+ *err = 1027;
+ goto fail;
+ }
+
+ return wlc;
+
+ fail:
+ brcms_c_detach_mfree(wlc);
+ return NULL;
+}
+
+/*
+ * Update the slot timing for standard 11b/g (20us slots)
+ * or shortslot 11g (9us slots)
+ * The PSM needs to be suspended for this call.
+ */
+static void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw,
+ bool shortslot)
+{
+ struct d11regs __iomem *regs;
+
+ regs = wlc_hw->regs;
+
+ if (shortslot) {
+ /* 11g short slot: 11a timing */
+ W_REG(&regs->ifs_slot, 0x0207); /* APHY_SLOT_TIME */
+ brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, APHY_SLOT_TIME);
+ } else {
+ /* 11g long slot: 11b timing */
+ W_REG(&regs->ifs_slot, 0x0212); /* BPHY_SLOT_TIME */
+ brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, BPHY_SLOT_TIME);
+ }
+}
+
+/*
+ * calculate frame duration of a given rate and length, return
+ * time in usec unit
+ */
+uint
+brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec,
+ u8 preamble_type, uint mac_len)
+{
+ uint nsyms, dur = 0, Ndps, kNdps;
+ uint rate = rspec2rate(ratespec);
+
+ if (rate == 0) {
+ wiphy_err(wlc->wiphy, "wl%d: WAR: using rate of 1 mbps\n",
+ wlc->pub->unit);
+ rate = BRCM_RATE_1M;
+ }
+
+ BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, len%d\n",
+ wlc->pub->unit, ratespec, preamble_type, mac_len);
+
+ if (is_mcs_rate(ratespec)) {
+ uint mcs = ratespec & RSPEC_RATE_MASK;
+ int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec);
+
+ dur = PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT);
+ if (preamble_type == BRCMS_MM_PREAMBLE)
+ dur += PREN_MM_EXT;
+ /* 1000Ndbps = kbps * 4 */
+ kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec),
+ rspec_issgi(ratespec)) * 4;
+
+ if (rspec_stc(ratespec) == 0)
+ nsyms =
+ CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
+ APHY_TAIL_NBITS) * 1000, kNdps);
+ else
+ /* STBC needs to have even number of symbols */
+ nsyms =
+ 2 *
+ CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
+ APHY_TAIL_NBITS) * 1000, 2 * kNdps);
+
+ dur += APHY_SYMBOL_TIME * nsyms;
+ if (wlc->band->bandtype == BRCM_BAND_2G)
+ dur += DOT11_OFDM_SIGNAL_EXTENSION;
+ } else if (is_ofdm_rate(rate)) {
+ dur = APHY_PREAMBLE_TIME;
+ dur += APHY_SIGNAL_TIME;
+ /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */
+ Ndps = rate * 2;
+ /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */
+ nsyms =
+ CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS),
+ Ndps);
+ dur += APHY_SYMBOL_TIME * nsyms;
+ if (wlc->band->bandtype == BRCM_BAND_2G)
+ dur += DOT11_OFDM_SIGNAL_EXTENSION;
+ } else {
+ /*
+ * calc # bits * 2 so factor of 2 in rate (1/2 mbps)
+ * will divide out
+ */
+ mac_len = mac_len * 8 * 2;
+ /* calc ceiling of bits/rate = microseconds of air time */
+ dur = (mac_len + rate - 1) / rate;
+ if (preamble_type & BRCMS_SHORT_PREAMBLE)
+ dur += BPHY_PLCP_SHORT_TIME;
+ else
+ dur += BPHY_PLCP_TIME;
+ }
+ return dur;
+}
+
+static void brcms_c_write_inits(struct brcms_hardware *wlc_hw,
+ const struct d11init *inits)
+{
+ int i;
+ u8 __iomem *base;
+ u8 __iomem *addr;
+ u16 size;
+ u32 value;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ base = (u8 __iomem *)wlc_hw->regs;
+
+ for (i = 0; inits[i].addr != cpu_to_le16(0xffff); i++) {
+ size = le16_to_cpu(inits[i].size);
+ addr = base + le16_to_cpu(inits[i].addr);
+ value = le32_to_cpu(inits[i].value);
+ if (size == 2)
+ W_REG((u16 __iomem *)addr, value);
+ else if (size == 4)
+ W_REG((u32 __iomem *)addr, value);
+ else
+ break;
+ }
+}
+
+static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs)
+{
+ u8 idx;
+ u16 addr[] = {
+ M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4,
+ M_HOST_FLAGS5
+ };
+
+ for (idx = 0; idx < MHFMAX; idx++)
+ brcms_b_write_shm(wlc_hw, addr[idx], mhfs[idx]);
+}
+
+static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw)
+{
+ struct wiphy *wiphy = wlc_hw->wlc->wiphy;
+ struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode;
+
+ /* init microcode host flags */
+ brcms_c_write_mhf(wlc_hw, wlc_hw->band->mhfs);
+
+ /* do band-specific ucode IHR, SHM, and SCR inits */
+ if (D11REV_IS(wlc_hw->corerev, 23)) {
+ if (BRCMS_ISNPHY(wlc_hw->band))
+ brcms_c_write_inits(wlc_hw, ucode->d11n0bsinitvals16);
+ else
+ wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev"
+ " %d\n", __func__, wlc_hw->unit,
+ wlc_hw->corerev);
+ } else {
+ if (D11REV_IS(wlc_hw->corerev, 24)) {
+ if (BRCMS_ISLCNPHY(wlc_hw->band))
+ brcms_c_write_inits(wlc_hw,
+ ucode->d11lcn0bsinitvals24);
+ else
+ wiphy_err(wiphy, "%s: wl%d: unsupported phy in"
+ " core rev %d\n", __func__,
+ wlc_hw->unit, wlc_hw->corerev);
+ } else {
+ wiphy_err(wiphy, "%s: wl%d: unsupported corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev);
+ }
+ }
+}
+
+static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk)
+{
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d: clk %d\n", wlc_hw->unit, clk);
+
+ wlc_hw->phyclk = clk;
+
+ if (OFF == clk) { /* clear gmode bit, put phy into reset */
+
+ ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC | SICF_GMODE),
+ (SICF_PRST | SICF_FGC));
+ udelay(1);
+ ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC), SICF_PRST);
+ udelay(1);
+
+ } else { /* take phy out of reset */
+
+ ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC), SICF_FGC);
+ udelay(1);
+ ai_core_cflags(wlc_hw->sih, (SICF_FGC), 0);
+ udelay(1);
+
+ }
+}
+
+/* low-level band switch utility routine */
+static void brcms_c_setxband(struct brcms_hardware *wlc_hw, uint bandunit)
+{
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit,
+ bandunit);
+
+ wlc_hw->band = wlc_hw->bandstate[bandunit];
+
+ /*
+ * BMAC_NOTE:
+ * until we eliminate need for wlc->band refs in low level code
+ */
+ wlc_hw->wlc->band = wlc_hw->wlc->bandstate[bandunit];
+
+ /* set gmode core flag */
+ if (wlc_hw->sbclk && !wlc_hw->noreset)
+ ai_core_cflags(wlc_hw->sih, SICF_GMODE,
+ ((bandunit == 0) ? SICF_GMODE : 0));
+}
+
+/* switch to new band but leave it inactive */
+static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ u32 macintmask;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ WARN_ON((R_REG(&wlc_hw->regs->maccontrol) & MCTL_EN_MAC) != 0);
+
+ /* disable interrupts */
+ macintmask = brcms_intrsoff(wlc->wl);
+
+ /* radio off */
+ wlc_phy_switch_radio(wlc_hw->band->pi, OFF);
+
+ brcms_b_core_phy_clk(wlc_hw, OFF);
+
+ brcms_c_setxband(wlc_hw, bandunit);
+
+ return macintmask;
+}
+
+/* process an individual struct tx_status */
+static bool
+brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs)
+{
+ struct sk_buff *p;
+ uint queue;
+ struct d11txh *txh;
+ struct scb *scb = NULL;
+ bool free_pdu;
+ int tx_rts, tx_frame_count, tx_rts_count;
+ uint totlen, supr_status;
+ bool lastframe;
+ struct ieee80211_hdr *h;
+ u16 mcl;
+ struct ieee80211_tx_info *tx_info;
+ struct ieee80211_tx_rate *txrate;
+ int i;
+
+ /* discard intermediate indications for ucode with one legitimate case:
+ * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange,
+ * but the subsequent tx of DATA failed. so it will start rts/cts
+ * from the beginning (resetting the rts transmission count)
+ */
+ if (!(txs->status & TX_STATUS_AMPDU)
+ && (txs->status & TX_STATUS_INTERMEDIATE)) {
+ wiphy_err(wlc->wiphy, "%s: INTERMEDIATE but not AMPDU\n",
+ __func__);
+ return false;
+ }
+
+ queue = txs->frameid & TXFID_QUEUE_MASK;
+ if (queue >= NFIFO) {
+ p = NULL;
+ goto fatal;
+ }
+
+ p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED);
+ if (p == NULL)
+ goto fatal;
+
+ txh = (struct d11txh *) (p->data);
+ mcl = le16_to_cpu(txh->MacTxControlLow);
+
+ if (txs->phyerr) {
+ if (brcm_msg_level & LOG_ERROR_VAL) {
+ wiphy_err(wlc->wiphy, "phyerr 0x%x, rate 0x%x\n",
+ txs->phyerr, txh->MainRates);
+ brcms_c_print_txdesc(txh);
+ }
+ brcms_c_print_txstatus(txs);
+ }
+
+ if (txs->frameid != le16_to_cpu(txh->TxFrameID))
+ goto fatal;
+ tx_info = IEEE80211_SKB_CB(p);
+ h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
+
+ if (tx_info->control.sta)
+ scb = &wlc->pri_scb;
+
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+ brcms_c_ampdu_dotxstatus(wlc->ampdu, scb, p, txs);
+ return false;
+ }
+
+ supr_status = txs->status & TX_STATUS_SUPR_MASK;
+ if (supr_status == TX_STATUS_SUPR_BADCH)
+ BCMMSG(wlc->wiphy,
+ "%s: Pkt tx suppressed, possibly channel %d\n",
+ __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec));
+
+ tx_rts = le16_to_cpu(txh->MacTxControlLow) & TXC_SENDRTS;
+ tx_frame_count =
+ (txs->status & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT;
+ tx_rts_count =
+ (txs->status & TX_STATUS_RTS_RTX_MASK) >> TX_STATUS_RTS_RTX_SHIFT;
+
+ lastframe = !ieee80211_has_morefrags(h->frame_control);
+
+ if (!lastframe) {
+ wiphy_err(wlc->wiphy, "Not last frame!\n");
+ } else {
+ /*
+ * Set information to be consumed by Minstrel ht.
+ *
+ * The "fallback limit" is the number of tx attempts a given
+ * MPDU is sent at the "primary" rate. Tx attempts beyond that
+ * limit are sent at the "secondary" rate.
+ * A 'short frame' does not exceed RTS treshold.
+ */
+ u16 sfbl, /* Short Frame Rate Fallback Limit */
+ lfbl, /* Long Frame Rate Fallback Limit */
+ fbl;
+
+ if (queue < AC_COUNT) {
+ sfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]],
+ EDCF_SFB);
+ lfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]],
+ EDCF_LFB);
+ } else {
+ sfbl = wlc->SFBL;
+ lfbl = wlc->LFBL;
+ }
+
+ txrate = tx_info->status.rates;
+ if (txrate[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
+ fbl = lfbl;
+ else
+ fbl = sfbl;
+
+ ieee80211_tx_info_clear_status(tx_info);
+
+ if ((tx_frame_count > fbl) && (txrate[1].idx >= 0)) {
+ /*
+ * rate selection requested a fallback rate
+ * and we used it
+ */
+ txrate[0].count = fbl;
+ txrate[1].count = tx_frame_count - fbl;
+ } else {
+ /*
+ * rate selection did not request fallback rate, or
+ * we didn't need it
+ */
+ txrate[0].count = tx_frame_count;
+ /*
+ * rc80211_minstrel.c:minstrel_tx_status() expects
+ * unused rates to be marked with idx = -1
+ */
+ txrate[1].idx = -1;
+ txrate[1].count = 0;
+ }
+
+ /* clear the rest of the rates */
+ for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
+ txrate[i].idx = -1;
+ txrate[i].count = 0;
+ }
+
+ if (txs->status & TX_STATUS_ACK_RCV)
+ tx_info->flags |= IEEE80211_TX_STAT_ACK;
+ }
+
+ totlen = brcmu_pkttotlen(p);
+ free_pdu = true;
+
+ brcms_c_txfifo_complete(wlc, queue, 1);
+
+ if (lastframe) {
+ p->next = NULL;
+ p->prev = NULL;
+ /* remove PLCP & Broadcom tx descriptor header */
+ skb_pull(p, D11_PHY_HDR_LEN);
+ skb_pull(p, D11_TXH_LEN);
+ ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p);
+ } else {
+ wiphy_err(wlc->wiphy, "%s: Not last frame => not calling "
+ "tx_status\n", __func__);
+ }
+
+ return false;
+
+ fatal:
+ if (p)
+ brcmu_pkt_buf_free_skb(p);
+
+ return true;
+
+}
+
+/* process tx completion events in BMAC
+ * Return true if more tx status need to be processed. false otherwise.
+ */
+static bool
+brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal)
+{
+ bool morepending = false;
+ struct brcms_c_info *wlc = wlc_hw->wlc;
+ struct d11regs __iomem *regs;
+ struct tx_status txstatus, *txs;
+ u32 s1, s2;
+ uint n = 0;
+ /*
+ * Param 'max_tx_num' indicates max. # tx status to process before
+ * break out.
+ */
+ uint max_tx_num = bound ? TXSBND : -1;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ txs = &txstatus;
+ regs = wlc_hw->regs;
+ *fatal = false;
+ while (!(*fatal)
+ && (s1 = R_REG(&regs->frmtxstatus)) & TXS_V) {
+
+ if (s1 == 0xffffffff) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n",
+ wlc_hw->unit, __func__);
+ return morepending;
+ }
+
+ s2 = R_REG(&regs->frmtxstatus2);
+
+ txs->status = s1 & TXS_STATUS_MASK;
+ txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT;
+ txs->sequence = s2 & TXS_SEQ_MASK;
+ txs->phyerr = (s2 & TXS_PTX_MASK) >> TXS_PTX_SHIFT;
+ txs->lasttxtime = 0;
+
+ *fatal = brcms_c_dotxstatus(wlc_hw->wlc, txs);
+
+ /* !give others some time to run! */
+ if (++n >= max_tx_num)
+ break;
+ }
+
+ if (*fatal)
+ return 0;
+
+ if (n >= max_tx_num)
+ morepending = true;
+
+ if (!pktq_empty(&wlc->pkt_queue->q))
+ brcms_c_send_q(wlc);
+
+ return morepending;
+}
+
+static void brcms_c_tbtt(struct brcms_c_info *wlc)
+{
+ if (!wlc->bsscfg->BSS)
+ /*
+ * DirFrmQ is now valid...defer setting until end
+ * of ATIM window
+ */
+ wlc->qvalid |= MCMD_DIRFRMQVAL;
+}
+
+/* set initial host flags value */
+static void
+brcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+
+ memset(mhfs, 0, MHFMAX * sizeof(u16));
+
+ mhfs[MHF2] |= mhf2_init;
+
+ /* prohibit use of slowclock on multifunction boards */
+ if (wlc_hw->boardflags & BFL_NOPLLDOWN)
+ mhfs[MHF1] |= MHF1_FORCEFASTCLK;
+
+ if (BRCMS_ISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 2)) {
+ mhfs[MHF2] |= MHF2_NPHY40MHZ_WAR;
+ mhfs[MHF1] |= MHF1_IQSWAP_WAR;
+ }
+}
+
+static struct dma64regs __iomem *
+dmareg(struct brcms_hardware *hw, uint direction, uint fifonum)
+{
+ if (direction == DMA_TX)
+ return &(hw->regs->fifo64regs[fifonum].dmaxmt);
+ return &(hw->regs->fifo64regs[fifonum].dmarcv);
+}
+
+static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme)
+{
+ uint i;
+ char name[8];
+ /*
+ * ucode host flag 2 needed for pio mode, independent of band and fifo
+ */
+ u16 pio_mhf2 = 0;
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ uint unit = wlc_hw->unit;
+ struct wiphy *wiphy = wlc->wiphy;
+
+ /* name and offsets for dma_attach */
+ snprintf(name, sizeof(name), "wl%d", unit);
+
+ if (wlc_hw->di[0] == NULL) { /* Init FIFOs */
+ int dma_attach_err = 0;
+
+ /*
+ * FIFO 0
+ * TX: TX_AC_BK_FIFO (TX AC Background data packets)
+ * RX: RX_FIFO (RX data packets)
+ */
+ wlc_hw->di[0] = dma_attach(name, wlc_hw->sih,
+ (wme ? dmareg(wlc_hw, DMA_TX, 0) :
+ NULL), dmareg(wlc_hw, DMA_RX, 0),
+ (wme ? NTXD : 0), NRXD,
+ RXBUFSZ, -1, NRXBUFPOST,
+ BRCMS_HWRXOFF, &brcm_msg_level);
+ dma_attach_err |= (NULL == wlc_hw->di[0]);
+
+ /*
+ * FIFO 1
+ * TX: TX_AC_BE_FIFO (TX AC Best-Effort data packets)
+ * (legacy) TX_DATA_FIFO (TX data packets)
+ * RX: UNUSED
+ */
+ wlc_hw->di[1] = dma_attach(name, wlc_hw->sih,
+ dmareg(wlc_hw, DMA_TX, 1), NULL,
+ NTXD, 0, 0, -1, 0, 0,
+ &brcm_msg_level);
+ dma_attach_err |= (NULL == wlc_hw->di[1]);
+
+ /*
+ * FIFO 2
+ * TX: TX_AC_VI_FIFO (TX AC Video data packets)
+ * RX: UNUSED
+ */
+ wlc_hw->di[2] = dma_attach(name, wlc_hw->sih,
+ dmareg(wlc_hw, DMA_TX, 2), NULL,
+ NTXD, 0, 0, -1, 0, 0,
+ &brcm_msg_level);
+ dma_attach_err |= (NULL == wlc_hw->di[2]);
+ /*
+ * FIFO 3
+ * TX: TX_AC_VO_FIFO (TX AC Voice data packets)
+ * (legacy) TX_CTL_FIFO (TX control & mgmt packets)
+ */
+ wlc_hw->di[3] = dma_attach(name, wlc_hw->sih,
+ dmareg(wlc_hw, DMA_TX, 3),
+ NULL, NTXD, 0, 0, -1,
+ 0, 0, &brcm_msg_level);
+ dma_attach_err |= (NULL == wlc_hw->di[3]);
+/* Cleaner to leave this as if with AP defined */
+
+ if (dma_attach_err) {
+ wiphy_err(wiphy, "wl%d: wlc_attach: dma_attach failed"
+ "\n", unit);
+ return false;
+ }
+
+ /* get pointer to dma engine tx flow control variable */
+ for (i = 0; i < NFIFO; i++)
+ if (wlc_hw->di[i])
+ wlc_hw->txavail[i] =
+ (uint *) dma_getvar(wlc_hw->di[i],
+ "&txavail");
+ }
+
+ /* initial ucode host flags */
+ brcms_c_mhfdef(wlc, wlc_hw->band->mhfs, pio_mhf2);
+
+ return true;
+}
+
+static void brcms_b_detach_dmapio(struct brcms_hardware *wlc_hw)
+{
+ uint j;
+
+ for (j = 0; j < NFIFO; j++) {
+ if (wlc_hw->di[j]) {
+ dma_detach(wlc_hw->di[j]);
+ wlc_hw->di[j] = NULL;
+ }
+ }
+}
+
+/*
+ * Initialize brcms_c_info default values ...
+ * may get overrides later in this function
+ * BMAC_NOTES, move low out and resolve the dangling ones
+ */
+static void brcms_b_info_init(struct brcms_hardware *wlc_hw)
+{
+ struct brcms_c_info *wlc = wlc_hw->wlc;
+
+ /* set default sw macintmask value */
+ wlc->defmacintmask = DEF_MACINTMASK;
+
+ /* various 802.11g modes */
+ wlc_hw->shortslot = false;
+
+ wlc_hw->SFBL = RETRY_SHORT_FB;
+ wlc_hw->LFBL = RETRY_LONG_FB;
+
+ /* default mac retry limits */
+ wlc_hw->SRL = RETRY_SHORT_DEF;
+ wlc_hw->LRL = RETRY_LONG_DEF;
+ wlc_hw->chanspec = ch20mhz_chspec(1);
+}
+
+static void brcms_b_wait_for_wake(struct brcms_hardware *wlc_hw)
+{
+ /* delay before first read of ucode state */
+ udelay(40);
+
+ /* wait until ucode is no longer asleep */
+ SPINWAIT((brcms_b_read_shm(wlc_hw, M_UCODE_DBGST) ==
+ DBGST_ASLEEP), wlc_hw->wlc->fastpwrup_dly);
+}
+
+/* control chip clock to save power, enable dynamic clock or force fast clock */
+static void brcms_b_clkctl_clk(struct brcms_hardware *wlc_hw, uint mode)
+{
+ if (wlc_hw->sih->cccaps & CC_CAP_PMU) {
+ /* new chips with PMU, CCS_FORCEHT will distribute the HT clock
+ * on backplane, but mac core will still run on ALP(not HT) when
+ * it enters powersave mode, which means the FCA bit may not be
+ * set. Should wakeup mac if driver wants it to run on HT.
+ */
+
+ if (wlc_hw->clk) {
+ if (mode == CLK_FAST) {
+ OR_REG(&wlc_hw->regs->clk_ctl_st,
+ CCS_FORCEHT);
+
+ udelay(64);
+
+ SPINWAIT(((R_REG
+ (&wlc_hw->regs->
+ clk_ctl_st) & CCS_HTAVAIL) == 0),
+ PMU_MAX_TRANSITION_DLY);
+ WARN_ON(!(R_REG
+ (&wlc_hw->regs->
+ clk_ctl_st) & CCS_HTAVAIL));
+ } else {
+ if ((wlc_hw->sih->pmurev == 0) &&
+ (R_REG
+ (&wlc_hw->regs->
+ clk_ctl_st) & (CCS_FORCEHT | CCS_HTAREQ)))
+ SPINWAIT(((R_REG
+ (&wlc_hw->regs->
+ clk_ctl_st) & CCS_HTAVAIL)
+ == 0),
+ PMU_MAX_TRANSITION_DLY);
+ AND_REG(&wlc_hw->regs->clk_ctl_st,
+ ~CCS_FORCEHT);
+ }
+ }
+ wlc_hw->forcefastclk = (mode == CLK_FAST);
+ } else {
+
+ /* old chips w/o PMU, force HT through cc,
+ * then use FCA to verify mac is running fast clock
+ */
+
+ wlc_hw->forcefastclk = ai_clkctl_cc(wlc_hw->sih, mode);
+
+ /* check fast clock is available (if core is not in reset) */
+ if (wlc_hw->forcefastclk && wlc_hw->clk)
+ WARN_ON(!(ai_core_sflags(wlc_hw->sih, 0, 0) &
+ SISF_FCLKA));
+
+ /*
+ * keep the ucode wake bit on if forcefastclk is on since we
+ * do not want ucode to put us back to slow clock when it dozes
+ * for PM mode. Code below matches the wake override bit with
+ * current forcefastclk state. Only setting bit in wake_override
+ * instead of waking ucode immediately since old code had this
+ * behavior. Older code set wlc->forcefastclk but only had the
+ * wake happen if the wakup_ucode work (protected by an up
+ * check) was executed just below.
+ */
+ if (wlc_hw->forcefastclk)
+ mboolset(wlc_hw->wake_override,
+ BRCMS_WAKE_OVERRIDE_FORCEFAST);
+ else
+ mboolclr(wlc_hw->wake_override,
+ BRCMS_WAKE_OVERRIDE_FORCEFAST);
+ }
+}
+
+/* set or clear ucode host flag bits
+ * it has an optimization for no-change write
+ * it only writes through shared memory when the core has clock;
+ * pre-CLK changes should use wlc_write_mhf to get around the optimization
+ *
+ *
+ * bands values are: BRCM_BAND_AUTO <--- Current band only
+ * BRCM_BAND_5G <--- 5G band only
+ * BRCM_BAND_2G <--- 2G band only
+ * BRCM_BAND_ALL <--- All bands
+ */
+void
+brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val,
+ int bands)
+{
+ u16 save;
+ u16 addr[MHFMAX] = {
+ M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4,
+ M_HOST_FLAGS5
+ };
+ struct brcms_hw_band *band;
+
+ if ((val & ~mask) || idx >= MHFMAX)
+ return; /* error condition */
+
+ switch (bands) {
+ /* Current band only or all bands,
+ * then set the band to current band
+ */
+ case BRCM_BAND_AUTO:
+ case BRCM_BAND_ALL:
+ band = wlc_hw->band;
+ break;
+ case BRCM_BAND_5G:
+ band = wlc_hw->bandstate[BAND_5G_INDEX];
+ break;
+ case BRCM_BAND_2G:
+ band = wlc_hw->bandstate[BAND_2G_INDEX];
+ break;
+ default:
+ band = NULL; /* error condition */
+ }
+
+ if (band) {
+ save = band->mhfs[idx];
+ band->mhfs[idx] = (band->mhfs[idx] & ~mask) | val;
+
+ /* optimization: only write through if changed, and
+ * changed band is the current band
+ */
+ if (wlc_hw->clk && (band->mhfs[idx] != save)
+ && (band == wlc_hw->band))
+ brcms_b_write_shm(wlc_hw, addr[idx],
+ (u16) band->mhfs[idx]);
+ }
+
+ if (bands == BRCM_BAND_ALL) {
+ wlc_hw->bandstate[0]->mhfs[idx] =
+ (wlc_hw->bandstate[0]->mhfs[idx] & ~mask) | val;
+ wlc_hw->bandstate[1]->mhfs[idx] =
+ (wlc_hw->bandstate[1]->mhfs[idx] & ~mask) | val;
+ }
+}
+
+/* set the maccontrol register to desired reset state and
+ * initialize the sw cache of the register
+ */
+static void brcms_c_mctrl_reset(struct brcms_hardware *wlc_hw)
+{
+ /* IHR accesses are always enabled, PSM disabled, HPS off and WAKE on */
+ wlc_hw->maccontrol = 0;
+ wlc_hw->suspended_fifos = 0;
+ wlc_hw->wake_override = 0;
+ wlc_hw->mute_override = 0;
+ brcms_b_mctrl(wlc_hw, ~0, MCTL_IHR_EN | MCTL_WAKE);
+}
+
+/*
+ * write the software state of maccontrol and
+ * overrides to the maccontrol register
+ */
+static void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw)
+{
+ u32 maccontrol = wlc_hw->maccontrol;
+
+ /* OR in the wake bit if overridden */
+ if (wlc_hw->wake_override)
+ maccontrol |= MCTL_WAKE;
+
+ /* set AP and INFRA bits for mute if needed */
+ if (wlc_hw->mute_override) {
+ maccontrol &= ~(MCTL_AP);
+ maccontrol |= MCTL_INFRA;
+ }
+
+ W_REG(&wlc_hw->regs->maccontrol, maccontrol);
+}
+
+/* set or clear maccontrol bits */
+void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val)
+{
+ u32 maccontrol;
+ u32 new_maccontrol;
+
+ if (val & ~mask)
+ return; /* error condition */
+ maccontrol = wlc_hw->maccontrol;
+ new_maccontrol = (maccontrol & ~mask) | val;
+
+ /* if the new maccontrol value is the same as the old, nothing to do */
+ if (new_maccontrol == maccontrol)
+ return;
+
+ /* something changed, cache the new value */
+ wlc_hw->maccontrol = new_maccontrol;
+
+ /* write the new values with overrides applied */
+ brcms_c_mctrl_write(wlc_hw);
+}
+
+void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw,
+ u32 override_bit)
+{
+ if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) {
+ mboolset(wlc_hw->wake_override, override_bit);
+ return;
+ }
+
+ mboolset(wlc_hw->wake_override, override_bit);
+
+ brcms_c_mctrl_write(wlc_hw);
+ brcms_b_wait_for_wake(wlc_hw);
+}
+
+void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw,
+ u32 override_bit)
+{
+ mboolclr(wlc_hw->wake_override, override_bit);
+
+ if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE))
+ return;
+
+ brcms_c_mctrl_write(wlc_hw);
+}
+
+/* When driver needs ucode to stop beaconing, it has to make sure that
+ * MCTL_AP is clear and MCTL_INFRA is set
+ * Mode MCTL_AP MCTL_INFRA
+ * AP 1 1
+ * STA 0 1 <--- This will ensure no beacons
+ * IBSS 0 0
+ */
+static void brcms_c_ucode_mute_override_set(struct brcms_hardware *wlc_hw)
+{
+ wlc_hw->mute_override = 1;
+
+ /* if maccontrol already has AP == 0 and INFRA == 1 without this
+ * override, then there is no change to write
+ */
+ if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA)
+ return;
+
+ brcms_c_mctrl_write(wlc_hw);
+}
+
+/* Clear the override on AP and INFRA bits */
+static void brcms_c_ucode_mute_override_clear(struct brcms_hardware *wlc_hw)
+{
+ if (wlc_hw->mute_override == 0)
+ return;
+
+ wlc_hw->mute_override = 0;
+
+ /* if maccontrol already has AP == 0 and INFRA == 1 without this
+ * override, then there is no change to write
+ */
+ if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA)
+ return;
+
+ brcms_c_mctrl_write(wlc_hw);
+}
+
+/*
+ * Write a MAC address to the given match reg offset in the RXE match engine.
+ */
+static void
+brcms_b_set_addrmatch(struct brcms_hardware *wlc_hw, int match_reg_offset,
+ const u8 *addr)
+{
+ struct d11regs __iomem *regs;
+ u16 mac_l;
+ u16 mac_m;
+ u16 mac_h;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d: brcms_b_set_addrmatch\n",
+ wlc_hw->unit);
+
+ regs = wlc_hw->regs;
+ mac_l = addr[0] | (addr[1] << 8);
+ mac_m = addr[2] | (addr[3] << 8);
+ mac_h = addr[4] | (addr[5] << 8);
+
+ /* enter the MAC addr into the RXE match registers */
+ W_REG(&regs->rcm_ctl, RCM_INC_DATA | match_reg_offset);
+ W_REG(&regs->rcm_mat_data, mac_l);
+ W_REG(&regs->rcm_mat_data, mac_m);
+ W_REG(&regs->rcm_mat_data, mac_h);
+
+}
+
+void
+brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, int len,
+ void *buf)
+{
+ struct d11regs __iomem *regs;
+ u32 word;
+ __le32 word_le;
+ __be32 word_be;
+ bool be_bit;
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ regs = wlc_hw->regs;
+ W_REG(&regs->tplatewrptr, offset);
+
+ /* if MCTL_BIGEND bit set in mac control register,
+ * the chip swaps data in fifo, as well as data in
+ * template ram
+ */
+ be_bit = (R_REG(&regs->maccontrol) & MCTL_BIGEND) != 0;
+
+ while (len > 0) {
+ memcpy(&word, buf, sizeof(u32));
+
+ if (be_bit) {
+ word_be = cpu_to_be32(word);
+ word = *(u32 *)&word_be;
+ } else {
+ word_le = cpu_to_le32(word);
+ word = *(u32 *)&word_le;
+ }
+
+ W_REG(&regs->tplatewrdata, word);
+
+ buf = (u8 *) buf + sizeof(u32);
+ len -= sizeof(u32);
+ }
+}
+
+static void brcms_b_set_cwmin(struct brcms_hardware *wlc_hw, u16 newmin)
+{
+ wlc_hw->band->CWmin = newmin;
+
+ W_REG(&wlc_hw->regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_CWMIN);
+ (void)R_REG(&wlc_hw->regs->objaddr);
+ W_REG(&wlc_hw->regs->objdata, newmin);
+}
+
+static void brcms_b_set_cwmax(struct brcms_hardware *wlc_hw, u16 newmax)
+{
+ wlc_hw->band->CWmax = newmax;
+
+ W_REG(&wlc_hw->regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_CWMAX);
+ (void)R_REG(&wlc_hw->regs->objaddr);
+ W_REG(&wlc_hw->regs->objdata, newmax);
+}
+
+void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw)
+{
+ bool fastclk;
+
+ /* request FAST clock if not on */
+ fastclk = wlc_hw->forcefastclk;
+ if (!fastclk)
+ brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+ wlc_phy_bw_state_set(wlc_hw->band->pi, bw);
+
+ brcms_b_phy_reset(wlc_hw);
+ wlc_phy_init(wlc_hw->band->pi, wlc_phy_chanspec_get(wlc_hw->band->pi));
+
+ /* restore the clk */
+ if (!fastclk)
+ brcms_b_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+}
+
+static void brcms_b_upd_synthpu(struct brcms_hardware *wlc_hw)
+{
+ u16 v;
+ struct brcms_c_info *wlc = wlc_hw->wlc;
+ /* update SYNTHPU_DLY */
+
+ if (BRCMS_ISLCNPHY(wlc->band))
+ v = SYNTHPU_DLY_LPPHY_US;
+ else if (BRCMS_ISNPHY(wlc->band) && (NREV_GE(wlc->band->phyrev, 3)))
+ v = SYNTHPU_DLY_NPHY_US;
+ else
+ v = SYNTHPU_DLY_BPHY_US;
+
+ brcms_b_write_shm(wlc_hw, M_SYNTHPU_DLY, v);
+}
+
+static void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw)
+{
+ u16 phyctl;
+ u16 phytxant = wlc_hw->bmac_phytxant;
+ u16 mask = PHY_TXC_ANT_MASK;
+
+ /* set the Probe Response frame phy control word */
+ phyctl = brcms_b_read_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS);
+ phyctl = (phyctl & ~mask) | phytxant;
+ brcms_b_write_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS, phyctl);
+
+ /* set the Response (ACK/CTS) frame phy control word */
+ phyctl = brcms_b_read_shm(wlc_hw, M_RSP_PCTLWD);
+ phyctl = (phyctl & ~mask) | phytxant;
+ brcms_b_write_shm(wlc_hw, M_RSP_PCTLWD, phyctl);
+}
+
+static u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw,
+ u8 rate)
+{
+ uint i;
+ u8 plcp_rate = 0;
+ struct plcp_signal_rate_lookup {
+ u8 rate;
+ u8 signal_rate;
+ };
+ /* OFDM RATE sub-field of PLCP SIGNAL field, per 802.11 sec 17.3.4.1 */
+ const struct plcp_signal_rate_lookup rate_lookup[] = {
+ {BRCM_RATE_6M, 0xB},
+ {BRCM_RATE_9M, 0xF},
+ {BRCM_RATE_12M, 0xA},
+ {BRCM_RATE_18M, 0xE},
+ {BRCM_RATE_24M, 0x9},
+ {BRCM_RATE_36M, 0xD},
+ {BRCM_RATE_48M, 0x8},
+ {BRCM_RATE_54M, 0xC}
+ };
+
+ for (i = 0; i < ARRAY_SIZE(rate_lookup); i++) {
+ if (rate == rate_lookup[i].rate) {
+ plcp_rate = rate_lookup[i].signal_rate;
+ break;
+ }
+ }
+
+ /* Find the SHM pointer to the rate table entry by looking in the
+ * Direct-map Table
+ */
+ return 2 * brcms_b_read_shm(wlc_hw, M_RT_DIRMAP_A + (plcp_rate * 2));
+}
+
+static void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw)
+{
+ u8 rate;
+ u8 rates[8] = {
+ BRCM_RATE_6M, BRCM_RATE_9M, BRCM_RATE_12M, BRCM_RATE_18M,
+ BRCM_RATE_24M, BRCM_RATE_36M, BRCM_RATE_48M, BRCM_RATE_54M
+ };
+ u16 entry_ptr;
+ u16 pctl1;
+ uint i;
+
+ if (!BRCMS_PHY_11N_CAP(wlc_hw->band))
+ return;
+
+ /* walk the phy rate table and update the entries */
+ for (i = 0; i < ARRAY_SIZE(rates); i++) {
+ rate = rates[i];
+
+ entry_ptr = brcms_b_ofdm_ratetable_offset(wlc_hw, rate);
+
+ /* read the SHM Rate Table entry OFDM PCTL1 values */
+ pctl1 =
+ brcms_b_read_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS);
+
+ /* modify the value */
+ pctl1 &= ~PHY_TXC1_MODE_MASK;
+ pctl1 |= (wlc_hw->hw_stf_ss_opmode << PHY_TXC1_MODE_SHIFT);
+
+ /* Update the SHM Rate Table entry OFDM PCTL1 values */
+ brcms_b_write_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS,
+ pctl1);
+ }
+}
+
+/* band-specific init */
+static void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+
+ BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit,
+ wlc_hw->band->bandunit);
+
+ brcms_c_ucode_bsinit(wlc_hw);
+
+ wlc_phy_init(wlc_hw->band->pi, chanspec);
+
+ brcms_c_ucode_txant_set(wlc_hw);
+
+ /*
+ * cwmin is band-specific, update hardware
+ * with value for current band
+ */
+ brcms_b_set_cwmin(wlc_hw, wlc_hw->band->CWmin);
+ brcms_b_set_cwmax(wlc_hw, wlc_hw->band->CWmax);
+
+ brcms_b_update_slot_timing(wlc_hw,
+ wlc_hw->band->bandtype == BRCM_BAND_5G ?
+ true : wlc_hw->shortslot);
+
+ /* write phytype and phyvers */
+ brcms_b_write_shm(wlc_hw, M_PHYTYPE, (u16) wlc_hw->band->phytype);
+ brcms_b_write_shm(wlc_hw, M_PHYVER, (u16) wlc_hw->band->phyrev);
+
+ /*
+ * initialize the txphyctl1 rate table since
+ * shmem is shared between bands
+ */
+ brcms_upd_ofdm_pctl1_table(wlc_hw);
+
+ brcms_b_upd_synthpu(wlc_hw);
+}
+
+/* Perform a soft reset of the PHY PLL */
+void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw)
+{
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ ai_corereg(wlc_hw->sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol_addr), ~0, 0);
+ udelay(1);
+ ai_corereg(wlc_hw->sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol_data), 0x4, 0);
+ udelay(1);
+ ai_corereg(wlc_hw->sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol_data), 0x4, 4);
+ udelay(1);
+ ai_corereg(wlc_hw->sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol_data), 0x4, 0);
+ udelay(1);
+}
+
+/* light way to turn on phy clock without reset for NPHY only
+ * refer to brcms_b_core_phy_clk for full version
+ */
+void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk)
+{
+ /* support(necessary for NPHY and HYPHY) only */
+ if (!BRCMS_ISNPHY(wlc_hw->band))
+ return;
+
+ if (ON == clk)
+ ai_core_cflags(wlc_hw->sih, SICF_FGC, SICF_FGC);
+ else
+ ai_core_cflags(wlc_hw->sih, SICF_FGC, 0);
+
+}
+
+void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk)
+{
+ if (ON == clk)
+ ai_core_cflags(wlc_hw->sih, SICF_MPCLKE, SICF_MPCLKE);
+ else
+ ai_core_cflags(wlc_hw->sih, SICF_MPCLKE, 0);
+}
+
+void brcms_b_phy_reset(struct brcms_hardware *wlc_hw)
+{
+ struct brcms_phy_pub *pih = wlc_hw->band->pi;
+ u32 phy_bw_clkbits;
+ bool phy_in_reset = false;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ if (pih == NULL)
+ return;
+
+ phy_bw_clkbits = wlc_phy_clk_bwbits(wlc_hw->band->pi);
+
+ /* Specific reset sequence required for NPHY rev 3 and 4 */
+ if (BRCMS_ISNPHY(wlc_hw->band) && NREV_GE(wlc_hw->band->phyrev, 3) &&
+ NREV_LE(wlc_hw->band->phyrev, 4)) {
+ /* Set the PHY bandwidth */
+ ai_core_cflags(wlc_hw->sih, SICF_BWMASK, phy_bw_clkbits);
+
+ udelay(1);
+
+ /* Perform a soft reset of the PHY PLL */
+ brcms_b_core_phypll_reset(wlc_hw);
+
+ /* reset the PHY */
+ ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_PCLKE),
+ (SICF_PRST | SICF_PCLKE));
+ phy_in_reset = true;
+ } else {
+ ai_core_cflags(wlc_hw->sih,
+ (SICF_PRST | SICF_PCLKE | SICF_BWMASK),
+ (SICF_PRST | SICF_PCLKE | phy_bw_clkbits));
+ }
+
+ udelay(2);
+ brcms_b_core_phy_clk(wlc_hw, ON);
+
+ if (pih)
+ wlc_phy_anacore(pih, ON);
+}
+
+/* switch to and initialize new band */
+static void brcms_b_setband(struct brcms_hardware *wlc_hw, uint bandunit,
+ u16 chanspec) {
+ struct brcms_c_info *wlc = wlc_hw->wlc;
+ u32 macintmask;
+
+ /* Enable the d11 core before accessing it */
+ if (!ai_iscoreup(wlc_hw->sih)) {
+ ai_core_reset(wlc_hw->sih, 0, 0);
+ brcms_c_mctrl_reset(wlc_hw);
+ }
+
+ macintmask = brcms_c_setband_inact(wlc, bandunit);
+
+ if (!wlc_hw->up)
+ return;
+
+ brcms_b_core_phy_clk(wlc_hw, ON);
+
+ /* band-specific initializations */
+ brcms_b_bsinit(wlc, chanspec);
+
+ /*
+ * If there are any pending software interrupt bits,
+ * then replace these with a harmless nonzero value
+ * so brcms_c_dpc() will re-enable interrupts when done.
+ */
+ if (wlc->macintstatus)
+ wlc->macintstatus = MI_DMAINT;
+
+ /* restore macintmask */
+ brcms_intrsrestore(wlc->wl, macintmask);
+
+ /* ucode should still be suspended.. */
+ WARN_ON((R_REG(&wlc_hw->regs->maccontrol) & MCTL_EN_MAC) != 0);
+}
+
+static bool brcms_c_isgoodchip(struct brcms_hardware *wlc_hw)
+{
+
+ /* reject unsupported corerev */
+ if (!CONF_HAS(D11CONF, wlc_hw->corerev)) {
+ wiphy_err(wlc_hw->wlc->wiphy, "unsupported core rev %d\n",
+ wlc_hw->corerev);
+ return false;
+ }
+
+ return true;
+}
+
+/* Validate some board info parameters */
+static bool brcms_c_validboardtype(struct brcms_hardware *wlc_hw)
+{
+ uint boardrev = wlc_hw->boardrev;
+
+ /* 4 bits each for board type, major, minor, and tiny version */
+ uint brt = (boardrev & 0xf000) >> 12;
+ uint b0 = (boardrev & 0xf00) >> 8;
+ uint b1 = (boardrev & 0xf0) >> 4;
+ uint b2 = boardrev & 0xf;
+
+ /* voards from other vendors are always considered valid */
+ if (wlc_hw->sih->boardvendor != PCI_VENDOR_ID_BROADCOM)
+ return true;
+
+ /* do some boardrev sanity checks when boardvendor is Broadcom */
+ if (boardrev == 0)
+ return false;
+
+ if (boardrev <= 0xff)
+ return true;
+
+ if ((brt > 2) || (brt == 0) || (b0 > 9) || (b0 == 0) || (b1 > 9)
+ || (b2 > 9))
+ return false;
+
+ return true;
+}
+
+static char *brcms_c_get_macaddr(struct brcms_hardware *wlc_hw)
+{
+ enum brcms_srom_id var_id = BRCMS_SROM_MACADDR;
+ char *macaddr;
+
+ /* If macaddr exists, use it (Sromrev4, CIS, ...). */
+ macaddr = getvar(wlc_hw->sih, var_id);
+ if (macaddr != NULL)
+ return macaddr;
+
+ if (wlc_hw->_nbands > 1)
+ var_id = BRCMS_SROM_ET1MACADDR;
+ else
+ var_id = BRCMS_SROM_IL0MACADDR;
+
+ macaddr = getvar(wlc_hw->sih, var_id);
+ if (macaddr == NULL)
+ wiphy_err(wlc_hw->wlc->wiphy, "wl%d: wlc_get_macaddr: macaddr "
+ "getvar(%d) not found\n", wlc_hw->unit, var_id);
+
+ return macaddr;
+}
+
+/* power both the pll and external oscillator on/off */
+static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want)
+{
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d: want %d\n", wlc_hw->unit, want);
+
+ /*
+ * dont power down if plldown is false or
+ * we must poll hw radio disable
+ */
+ if (!want && wlc_hw->pllreq)
+ return;
+
+ if (wlc_hw->sih)
+ ai_clkctl_xtal(wlc_hw->sih, XTAL | PLL, want);
+
+ wlc_hw->sbclk = want;
+ if (!wlc_hw->sbclk) {
+ wlc_hw->clk = false;
+ if (wlc_hw->band && wlc_hw->band->pi)
+ wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
+ }
+}
+
+/*
+ * Return true if radio is disabled, otherwise false.
+ * hw radio disable signal is an external pin, users activate it asynchronously
+ * this function could be called when driver is down and w/o clock
+ * it operates on different registers depending on corerev and boardflag.
+ */
+static bool brcms_b_radio_read_hwdisabled(struct brcms_hardware *wlc_hw)
+{
+ bool v, clk, xtal;
+ u32 resetbits = 0, flags = 0;
+
+ xtal = wlc_hw->sbclk;
+ if (!xtal)
+ brcms_b_xtal(wlc_hw, ON);
+
+ /* may need to take core out of reset first */
+ clk = wlc_hw->clk;
+ if (!clk) {
+ /*
+ * mac no longer enables phyclk automatically when driver
+ * accesses phyreg throughput mac. This can be skipped since
+ * only mac reg is accessed below
+ */
+ flags |= SICF_PCLKE;
+
+ /*
+ * AI chip doesn't restore bar0win2 on
+ * hibernation/resume, need sw fixup
+ */
+ if ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
+ (wlc_hw->sih->chip == BCM43225_CHIP_ID))
+ wlc_hw->regs = (struct d11regs __iomem *)
+ ai_setcore(wlc_hw->sih, D11_CORE_ID, 0);
+ ai_core_reset(wlc_hw->sih, flags, resetbits);
+ brcms_c_mctrl_reset(wlc_hw);
+ }
+
+ v = ((R_REG(&wlc_hw->regs->phydebug) & PDBG_RFD) != 0);
+
+ /* put core back into reset */
+ if (!clk)
+ ai_core_disable(wlc_hw->sih, 0);
+
+ if (!xtal)
+ brcms_b_xtal(wlc_hw, OFF);
+
+ return v;
+}
+
+static bool wlc_dma_rxreset(struct brcms_hardware *wlc_hw, uint fifo)
+{
+ struct dma_pub *di = wlc_hw->di[fifo];
+ return dma_rxreset(di);
+}
+
+/* d11 core reset
+ * ensure fask clock during reset
+ * reset dma
+ * reset d11(out of reset)
+ * reset phy(out of reset)
+ * clear software macintstatus for fresh new start
+ * one testing hack wlc_hw->noreset will bypass the d11/phy reset
+ */
+void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags)
+{
+ struct d11regs __iomem *regs;
+ uint i;
+ bool fastclk;
+ u32 resetbits = 0;
+
+ if (flags == BRCMS_USE_COREFLAGS)
+ flags = (wlc_hw->band->pi ? wlc_hw->band->core_flags : 0);
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ regs = wlc_hw->regs;
+
+ /* request FAST clock if not on */
+ fastclk = wlc_hw->forcefastclk;
+ if (!fastclk)
+ brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+ /* reset the dma engines except first time thru */
+ if (ai_iscoreup(wlc_hw->sih)) {
+ for (i = 0; i < NFIFO; i++)
+ if ((wlc_hw->di[i]) && (!dma_txreset(wlc_hw->di[i])))
+ wiphy_err(wlc_hw->wlc->wiphy, "wl%d: %s: "
+ "dma_txreset[%d]: cannot stop dma\n",
+ wlc_hw->unit, __func__, i);
+
+ if ((wlc_hw->di[RX_FIFO])
+ && (!wlc_dma_rxreset(wlc_hw, RX_FIFO)))
+ wiphy_err(wlc_hw->wlc->wiphy, "wl%d: %s: dma_rxreset"
+ "[%d]: cannot stop dma\n",
+ wlc_hw->unit, __func__, RX_FIFO);
+ }
+ /* if noreset, just stop the psm and return */
+ if (wlc_hw->noreset) {
+ wlc_hw->wlc->macintstatus = 0; /* skip wl_dpc after down */
+ brcms_b_mctrl(wlc_hw, MCTL_PSM_RUN | MCTL_EN_MAC, 0);
+ return;
+ }
+
+ /*
+ * mac no longer enables phyclk automatically when driver accesses
+ * phyreg throughput mac, AND phy_reset is skipped at early stage when
+ * band->pi is invalid. need to enable PHY CLK
+ */
+ flags |= SICF_PCLKE;
+
+ /*
+ * reset the core
+ * In chips with PMU, the fastclk request goes through d11 core
+ * reg 0x1e0, which is cleared by the core_reset. have to re-request it.
+ *
+ * This adds some delay and we can optimize it by also requesting
+ * fastclk through chipcommon during this period if necessary. But
+ * that has to work coordinate with other driver like mips/arm since
+ * they may touch chipcommon as well.
+ */
+ wlc_hw->clk = false;
+ ai_core_reset(wlc_hw->sih, flags, resetbits);
+ wlc_hw->clk = true;
+ if (wlc_hw->band && wlc_hw->band->pi)
+ wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, true);
+
+ brcms_c_mctrl_reset(wlc_hw);
+
+ if (wlc_hw->sih->cccaps & CC_CAP_PMU)
+ brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+ brcms_b_phy_reset(wlc_hw);
+
+ /* turn on PHY_PLL */
+ brcms_b_core_phypll_ctl(wlc_hw, true);
+
+ /* clear sw intstatus */
+ wlc_hw->wlc->macintstatus = 0;
+
+ /* restore the clk setting */
+ if (!fastclk)
+ brcms_b_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+}
+
+/* txfifo sizes needs to be modified(increased) since the newer cores
+ * have more memory.
+ */
+static void brcms_b_corerev_fifofixup(struct brcms_hardware *wlc_hw)
+{
+ struct d11regs __iomem *regs = wlc_hw->regs;
+ u16 fifo_nu;
+ u16 txfifo_startblk = TXFIFO_START_BLK, txfifo_endblk;
+ u16 txfifo_def, txfifo_def1;
+ u16 txfifo_cmd;
+
+ /* tx fifos start at TXFIFO_START_BLK from the Base address */
+ txfifo_startblk = TXFIFO_START_BLK;
+
+ /* sequence of operations: reset fifo, set fifo size, reset fifo */
+ for (fifo_nu = 0; fifo_nu < NFIFO; fifo_nu++) {
+
+ txfifo_endblk = txfifo_startblk + wlc_hw->xmtfifo_sz[fifo_nu];
+ txfifo_def = (txfifo_startblk & 0xff) |
+ (((txfifo_endblk - 1) & 0xff) << TXFIFO_FIFOTOP_SHIFT);
+ txfifo_def1 = ((txfifo_startblk >> 8) & 0x1) |
+ ((((txfifo_endblk -
+ 1) >> 8) & 0x1) << TXFIFO_FIFOTOP_SHIFT);
+ txfifo_cmd =
+ TXFIFOCMD_RESET_MASK | (fifo_nu << TXFIFOCMD_FIFOSEL_SHIFT);
+
+ W_REG(&regs->xmtfifocmd, txfifo_cmd);
+ W_REG(&regs->xmtfifodef, txfifo_def);
+ W_REG(&regs->xmtfifodef1, txfifo_def1);
+
+ W_REG(&regs->xmtfifocmd, txfifo_cmd);
+
+ txfifo_startblk += wlc_hw->xmtfifo_sz[fifo_nu];
+ }
+ /*
+ * need to propagate to shm location to be in sync since ucode/hw won't
+ * do this
+ */
+ brcms_b_write_shm(wlc_hw, M_FIFOSIZE0,
+ wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]);
+ brcms_b_write_shm(wlc_hw, M_FIFOSIZE1,
+ wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]);
+ brcms_b_write_shm(wlc_hw, M_FIFOSIZE2,
+ ((wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO] << 8) | wlc_hw->
+ xmtfifo_sz[TX_AC_BK_FIFO]));
+ brcms_b_write_shm(wlc_hw, M_FIFOSIZE3,
+ ((wlc_hw->xmtfifo_sz[TX_ATIM_FIFO] << 8) | wlc_hw->
+ xmtfifo_sz[TX_BCMC_FIFO]));
+}
+
+/* This function is used for changing the tsf frac register
+ * If spur avoidance mode is off, the mac freq will be 80/120/160Mhz
+ * If spur avoidance mode is on1, the mac freq will be 82/123/164Mhz
+ * If spur avoidance mode is on2, the mac freq will be 84/126/168Mhz
+ * HTPHY Formula is 2^26/freq(MHz) e.g.
+ * For spuron2 - 126MHz -> 2^26/126 = 532610.0
+ * - 532610 = 0x82082 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x2082
+ * For spuron: 123MHz -> 2^26/123 = 545600.5
+ * - 545601 = 0x85341 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x5341
+ * For spur off: 120MHz -> 2^26/120 = 559240.5
+ * - 559241 = 0x88889 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x8889
+ */
+
+void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode)
+{
+ struct d11regs __iomem *regs = wlc_hw->regs;
+
+ if ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
+ (wlc_hw->sih->chip == BCM43225_CHIP_ID)) {
+ if (spurmode == WL_SPURAVOID_ON2) { /* 126Mhz */
+ W_REG(&regs->tsf_clk_frac_l, 0x2082);
+ W_REG(&regs->tsf_clk_frac_h, 0x8);
+ } else if (spurmode == WL_SPURAVOID_ON1) { /* 123Mhz */
+ W_REG(&regs->tsf_clk_frac_l, 0x5341);
+ W_REG(&regs->tsf_clk_frac_h, 0x8);
+ } else { /* 120Mhz */
+ W_REG(&regs->tsf_clk_frac_l, 0x8889);
+ W_REG(&regs->tsf_clk_frac_h, 0x8);
+ }
+ } else if (BRCMS_ISLCNPHY(wlc_hw->band)) {
+ if (spurmode == WL_SPURAVOID_ON1) { /* 82Mhz */
+ W_REG(&regs->tsf_clk_frac_l, 0x7CE0);
+ W_REG(&regs->tsf_clk_frac_h, 0xC);
+ } else { /* 80Mhz */
+ W_REG(&regs->tsf_clk_frac_l, 0xCCCD);
+ W_REG(&regs->tsf_clk_frac_h, 0xC);
+ }
+ }
+}
+
+/* Initialize GPIOs that are controlled by D11 core */
+static void brcms_c_gpio_init(struct brcms_c_info *wlc)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ struct d11regs __iomem *regs;
+ u32 gc, gm;
+
+ regs = wlc_hw->regs;
+
+ /* use GPIO select 0 to get all gpio signals from the gpio out reg */
+ brcms_b_mctrl(wlc_hw, MCTL_GPOUT_SEL_MASK, 0);
+
+ /*
+ * Common GPIO setup:
+ * G0 = LED 0 = WLAN Activity
+ * G1 = LED 1 = WLAN 2.4 GHz Radio State
+ * G2 = LED 2 = WLAN 5 GHz Radio State
+ * G4 = radio disable input (HI enabled, LO disabled)
+ */
+
+ gc = gm = 0;
+
+ /* Allocate GPIOs for mimo antenna diversity feature */
+ if (wlc_hw->antsel_type == ANTSEL_2x3) {
+ /* Enable antenna diversity, use 2x3 mode */
+ brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN,
+ MHF3_ANTSEL_EN, BRCM_BAND_ALL);
+ brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE,
+ MHF3_ANTSEL_MODE, BRCM_BAND_ALL);
+
+ /* init superswitch control */
+ wlc_phy_antsel_init(wlc_hw->band->pi, false);
+
+ } else if (wlc_hw->antsel_type == ANTSEL_2x4) {
+ gm |= gc |= (BOARD_GPIO_12 | BOARD_GPIO_13);
+ /*
+ * The board itself is powered by these GPIOs
+ * (when not sending pattern) so set them high
+ */
+ OR_REG(&regs->psm_gpio_oe,
+ (BOARD_GPIO_12 | BOARD_GPIO_13));
+ OR_REG(&regs->psm_gpio_out,
+ (BOARD_GPIO_12 | BOARD_GPIO_13));
+
+ /* Enable antenna diversity, use 2x4 mode */
+ brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN,
+ MHF3_ANTSEL_EN, BRCM_BAND_ALL);
+ brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, 0,
+ BRCM_BAND_ALL);
+
+ /* Configure the desired clock to be 4Mhz */
+ brcms_b_write_shm(wlc_hw, M_ANTSEL_CLKDIV,
+ ANTSEL_CLKDIV_4MHZ);
+ }
+
+ /*
+ * gpio 9 controls the PA. ucode is responsible
+ * for wiggling out and oe
+ */
+ if (wlc_hw->boardflags & BFL_PACTRL)
+ gm |= gc |= BOARD_GPIO_PACTRL;
+
+ /* apply to gpiocontrol register */
+ ai_gpiocontrol(wlc_hw->sih, gm, gc, GPIO_DRV_PRIORITY);
+}
+
+static void brcms_ucode_write(struct brcms_hardware *wlc_hw,
+ const __le32 ucode[], const size_t nbytes)
+{
+ struct d11regs __iomem *regs = wlc_hw->regs;
+ uint i;
+ uint count;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ count = (nbytes / sizeof(u32));
+
+ W_REG(&regs->objaddr, (OBJADDR_AUTO_INC | OBJADDR_UCM_SEL));
+ (void)R_REG(&regs->objaddr);
+ for (i = 0; i < count; i++)
+ W_REG(&regs->objdata, le32_to_cpu(ucode[i]));
+
+}
+
+static void brcms_ucode_download(struct brcms_hardware *wlc_hw)
+{
+ struct brcms_c_info *wlc;
+ struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode;
+
+ wlc = wlc_hw->wlc;
+
+ if (wlc_hw->ucode_loaded)
+ return;
+
+ if (D11REV_IS(wlc_hw->corerev, 23)) {
+ if (BRCMS_ISNPHY(wlc_hw->band)) {
+ brcms_ucode_write(wlc_hw, ucode->bcm43xx_16_mimo,
+ ucode->bcm43xx_16_mimosz);
+ wlc_hw->ucode_loaded = true;
+ } else
+ wiphy_err(wlc->wiphy, "%s: wl%d: unsupported phy in "
+ "corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev);
+ } else if (D11REV_IS(wlc_hw->corerev, 24)) {
+ if (BRCMS_ISLCNPHY(wlc_hw->band)) {
+ brcms_ucode_write(wlc_hw, ucode->bcm43xx_24_lcn,
+ ucode->bcm43xx_24_lcnsz);
+ wlc_hw->ucode_loaded = true;
+ } else {
+ wiphy_err(wlc->wiphy, "%s: wl%d: unsupported phy in "
+ "corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev);
+ }
+ }
+}
+
+void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant)
+{
+ /* update sw state */
+ wlc_hw->bmac_phytxant = phytxant;
+
+ /* push to ucode if up */
+ if (!wlc_hw->up)
+ return;
+ brcms_c_ucode_txant_set(wlc_hw);
+
+}
+
+u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw)
+{
+ return (u16) wlc_hw->wlc->stf->txant;
+}
+
+void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type)
+{
+ wlc_hw->antsel_type = antsel_type;
+
+ /* Update the antsel type for phy module to use */
+ wlc_phy_antsel_type_set(wlc_hw->band->pi, antsel_type);
+}
+
+static void brcms_c_fatal_error(struct brcms_c_info *wlc)
+{
+ wiphy_err(wlc->wiphy, "wl%d: fatal error, reinitializing\n",
+ wlc->pub->unit);
+ brcms_init(wlc->wl);
+}
+
+static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw)
+{
+ bool fatal = false;
+ uint unit;
+ uint intstatus, idx;
+ struct d11regs __iomem *regs = wlc_hw->regs;
+ struct wiphy *wiphy = wlc_hw->wlc->wiphy;
+
+ unit = wlc_hw->unit;
+
+ for (idx = 0; idx < NFIFO; idx++) {
+ /* read intstatus register and ignore any non-error bits */
+ intstatus =
+ R_REG(&regs->intctrlregs[idx].intstatus) & I_ERRORS;
+ if (!intstatus)
+ continue;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d: intstatus%d 0x%x\n",
+ unit, idx, intstatus);
+
+ if (intstatus & I_RO) {
+ wiphy_err(wiphy, "wl%d: fifo %d: receive fifo "
+ "overflow\n", unit, idx);
+ fatal = true;
+ }
+
+ if (intstatus & I_PC) {
+ wiphy_err(wiphy, "wl%d: fifo %d: descriptor error\n",
+ unit, idx);
+ fatal = true;
+ }
+
+ if (intstatus & I_PD) {
+ wiphy_err(wiphy, "wl%d: fifo %d: data error\n", unit,
+ idx);
+ fatal = true;
+ }
+
+ if (intstatus & I_DE) {
+ wiphy_err(wiphy, "wl%d: fifo %d: descriptor protocol "
+ "error\n", unit, idx);
+ fatal = true;
+ }
+
+ if (intstatus & I_RU)
+ wiphy_err(wiphy, "wl%d: fifo %d: receive descriptor "
+ "underflow\n", idx, unit);
+
+ if (intstatus & I_XU) {
+ wiphy_err(wiphy, "wl%d: fifo %d: transmit fifo "
+ "underflow\n", idx, unit);
+ fatal = true;
+ }
+
+ if (fatal) {
+ brcms_c_fatal_error(wlc_hw->wlc); /* big hammer */
+ break;
+ } else
+ W_REG(&regs->intctrlregs[idx].intstatus,
+ intstatus);
+ }
+}
+
+void brcms_c_intrson(struct brcms_c_info *wlc)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ wlc->macintmask = wlc->defmacintmask;
+ W_REG(&wlc_hw->regs->macintmask, wlc->macintmask);
+}
+
+/*
+ * callback for siutils.c, which has only wlc handler, no wl they both check
+ * up, not only because there is no need to off/restore d11 interrupt but also
+ * because per-port code may require sync with valid interrupt.
+ */
+static u32 brcms_c_wlintrsoff(struct brcms_c_info *wlc)
+{
+ if (!wlc->hw->up)
+ return 0;
+
+ return brcms_intrsoff(wlc->wl);
+}
+
+static void brcms_c_wlintrsrestore(struct brcms_c_info *wlc, u32 macintmask)
+{
+ if (!wlc->hw->up)
+ return;
+
+ brcms_intrsrestore(wlc->wl, macintmask);
+}
+
+u32 brcms_c_intrsoff(struct brcms_c_info *wlc)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ u32 macintmask;
+
+ if (!wlc_hw->clk)
+ return 0;
+
+ macintmask = wlc->macintmask; /* isr can still happen */
+
+ W_REG(&wlc_hw->regs->macintmask, 0);
+ (void)R_REG(&wlc_hw->regs->macintmask); /* sync readback */
+ udelay(1); /* ensure int line is no longer driven */
+ wlc->macintmask = 0;
+
+ /* return previous macintmask; resolve race between us and our isr */
+ return wlc->macintstatus ? 0 : macintmask;
+}
+
+void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ if (!wlc_hw->clk)
+ return;
+
+ wlc->macintmask = macintmask;
+ W_REG(&wlc_hw->regs->macintmask, wlc->macintmask);
+}
+
+static void brcms_b_tx_fifo_suspend(struct brcms_hardware *wlc_hw,
+ uint tx_fifo)
+{
+ u8 fifo = 1 << tx_fifo;
+
+ /* Two clients of this code, 11h Quiet period and scanning. */
+
+ /* only suspend if not already suspended */
+ if ((wlc_hw->suspended_fifos & fifo) == fifo)
+ return;
+
+ /* force the core awake only if not already */
+ if (wlc_hw->suspended_fifos == 0)
+ brcms_c_ucode_wake_override_set(wlc_hw,
+ BRCMS_WAKE_OVERRIDE_TXFIFO);
+
+ wlc_hw->suspended_fifos |= fifo;
+
+ if (wlc_hw->di[tx_fifo]) {
+ /*
+ * Suspending AMPDU transmissions in the middle can cause
+ * underflow which may result in mismatch between ucode and
+ * driver so suspend the mac before suspending the FIFO
+ */
+ if (BRCMS_PHY_11N_CAP(wlc_hw->band))
+ brcms_c_suspend_mac_and_wait(wlc_hw->wlc);
+
+ dma_txsuspend(wlc_hw->di[tx_fifo]);
+
+ if (BRCMS_PHY_11N_CAP(wlc_hw->band))
+ brcms_c_enable_mac(wlc_hw->wlc);
+ }
+}
+
+static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw,
+ uint tx_fifo)
+{
+ /* BMAC_NOTE: BRCMS_TX_FIFO_ENAB is done in brcms_c_dpc() for DMA case
+ * but need to be done here for PIO otherwise the watchdog will catch
+ * the inconsistency and fire
+ */
+ /* Two clients of this code, 11h Quiet period and scanning. */
+ if (wlc_hw->di[tx_fifo])
+ dma_txresume(wlc_hw->di[tx_fifo]);
+
+ /* allow core to sleep again */
+ if (wlc_hw->suspended_fifos == 0)
+ return;
+ else {
+ wlc_hw->suspended_fifos &= ~(1 << tx_fifo);
+ if (wlc_hw->suspended_fifos == 0)
+ brcms_c_ucode_wake_override_clear(wlc_hw,
+ BRCMS_WAKE_OVERRIDE_TXFIFO);
+ }
+}
+
+static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool on, u32 flags)
+{
+ static const u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
+
+ if (on) {
+ /* suspend tx fifos */
+ brcms_b_tx_fifo_suspend(wlc_hw, TX_DATA_FIFO);
+ brcms_b_tx_fifo_suspend(wlc_hw, TX_CTL_FIFO);
+ brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_BK_FIFO);
+ brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO);
+
+ /* zero the address match register so we do not send ACKs */
+ brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET,
+ null_ether_addr);
+ } else {
+ /* resume tx fifos */
+ brcms_b_tx_fifo_resume(wlc_hw, TX_DATA_FIFO);
+ brcms_b_tx_fifo_resume(wlc_hw, TX_CTL_FIFO);
+ brcms_b_tx_fifo_resume(wlc_hw, TX_AC_BK_FIFO);
+ brcms_b_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO);
+
+ /* Restore address */
+ brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET,
+ wlc_hw->etheraddr);
+ }
+
+ wlc_phy_mute_upd(wlc_hw->band->pi, on, flags);
+
+ if (on)
+ brcms_c_ucode_mute_override_set(wlc_hw);
+ else
+ brcms_c_ucode_mute_override_clear(wlc_hw);
+}
+
+/*
+ * Read and clear macintmask and macintstatus and intstatus registers.
+ * This routine should be called with interrupts off
+ * Return:
+ * -1 if brcms_deviceremoved(wlc) evaluates to true;
+ * 0 if the interrupt is not for us, or we are in some special cases;
+ * device interrupt status bits otherwise.
+ */
+static inline u32 wlc_intstatus(struct brcms_c_info *wlc, bool in_isr)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ struct d11regs __iomem *regs = wlc_hw->regs;
+ u32 macintstatus;
+
+ /* macintstatus includes a DMA interrupt summary bit */
+ macintstatus = R_REG(&regs->macintstatus);
+
+ BCMMSG(wlc->wiphy, "wl%d: macintstatus: 0x%x\n", wlc_hw->unit,
+ macintstatus);
+
+ /* detect cardbus removed, in power down(suspend) and in reset */
+ if (brcms_deviceremoved(wlc))
+ return -1;
+
+ /* brcms_deviceremoved() succeeds even when the core is still resetting,
+ * handle that case here.
+ */
+ if (macintstatus == 0xffffffff)
+ return 0;
+
+ /* defer unsolicited interrupts */
+ macintstatus &= (in_isr ? wlc->macintmask : wlc->defmacintmask);
+
+ /* if not for us */
+ if (macintstatus == 0)
+ return 0;
+
+ /* interrupts are already turned off for CFE build
+ * Caution: For CFE Turning off the interrupts again has some undesired
+ * consequences
+ */
+ /* turn off the interrupts */
+ W_REG(&regs->macintmask, 0);
+ (void)R_REG(&regs->macintmask); /* sync readback */
+ wlc->macintmask = 0;
+
+ /* clear device interrupts */
+ W_REG(&regs->macintstatus, macintstatus);
+
+ /* MI_DMAINT is indication of non-zero intstatus */
+ if (macintstatus & MI_DMAINT)
+ /*
+ * only fifo interrupt enabled is I_RI in
+ * RX_FIFO. If MI_DMAINT is set, assume it
+ * is set and clear the interrupt.
+ */
+ W_REG(&regs->intctrlregs[RX_FIFO].intstatus,
+ DEF_RXINTMASK);
+
+ return macintstatus;
+}
+
+/* Update wlc->macintstatus and wlc->intstatus[]. */
+/* Return true if they are updated successfully. false otherwise */
+bool brcms_c_intrsupd(struct brcms_c_info *wlc)
+{
+ u32 macintstatus;
+
+ /* read and clear macintstatus and intstatus registers */
+ macintstatus = wlc_intstatus(wlc, false);
+
+ /* device is removed */
+ if (macintstatus == 0xffffffff)
+ return false;
+
+ /* update interrupt status in software */
+ wlc->macintstatus |= macintstatus;
+
+ return true;
+}
+
+/*
+ * First-level interrupt processing.
+ * Return true if this was our interrupt, false otherwise.
+ * *wantdpc will be set to true if further brcms_c_dpc() processing is required,
+ * false otherwise.
+ */
+bool brcms_c_isr(struct brcms_c_info *wlc, bool *wantdpc)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ u32 macintstatus;
+
+ *wantdpc = false;
+
+ if (!wlc_hw->up || !wlc->macintmask)
+ return false;
+
+ /* read and clear macintstatus and intstatus registers */
+ macintstatus = wlc_intstatus(wlc, true);
+
+ if (macintstatus == 0xffffffff)
+ wiphy_err(wlc->wiphy, "DEVICEREMOVED detected in the ISR code"
+ " path\n");
+
+ /* it is not for us */
+ if (macintstatus == 0)
+ return false;
+
+ *wantdpc = true;
+
+ /* save interrupt status bits */
+ wlc->macintstatus = macintstatus;
+
+ return true;
+
+}
+
+void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ struct d11regs __iomem *regs = wlc_hw->regs;
+ u32 mc, mi;
+ struct wiphy *wiphy = wlc->wiphy;
+
+ BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit,
+ wlc_hw->band->bandunit);
+
+ /*
+ * Track overlapping suspend requests
+ */
+ wlc_hw->mac_suspend_depth++;
+ if (wlc_hw->mac_suspend_depth > 1)
+ return;
+
+ /* force the core awake */
+ brcms_c_ucode_wake_override_set(wlc_hw, BRCMS_WAKE_OVERRIDE_MACSUSPEND);
+
+ mc = R_REG(&regs->maccontrol);
+
+ if (mc == 0xffffffff) {
+ wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit,
+ __func__);
+ brcms_down(wlc->wl);
+ return;
+ }
+ WARN_ON(mc & MCTL_PSM_JMP_0);
+ WARN_ON(!(mc & MCTL_PSM_RUN));
+ WARN_ON(!(mc & MCTL_EN_MAC));
+
+ mi = R_REG(&regs->macintstatus);
+ if (mi == 0xffffffff) {
+ wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit,
+ __func__);
+ brcms_down(wlc->wl);
+ return;
+ }
+ WARN_ON(mi & MI_MACSSPNDD);
+
+ brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, 0);
+
+ SPINWAIT(!(R_REG(&regs->macintstatus) & MI_MACSSPNDD),
+ BRCMS_MAX_MAC_SUSPEND);
+
+ if (!(R_REG(&regs->macintstatus) & MI_MACSSPNDD)) {
+ wiphy_err(wiphy, "wl%d: wlc_suspend_mac_and_wait: waited %d uS"
+ " and MI_MACSSPNDD is still not on.\n",
+ wlc_hw->unit, BRCMS_MAX_MAC_SUSPEND);
+ wiphy_err(wiphy, "wl%d: psmdebug 0x%08x, phydebug 0x%08x, "
+ "psm_brc 0x%04x\n", wlc_hw->unit,
+ R_REG(&regs->psmdebug),
+ R_REG(&regs->phydebug),
+ R_REG(&regs->psm_brc));
+ }
+
+ mc = R_REG(&regs->maccontrol);
+ if (mc == 0xffffffff) {
+ wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit,
+ __func__);
+ brcms_down(wlc->wl);
+ return;
+ }
+ WARN_ON(mc & MCTL_PSM_JMP_0);
+ WARN_ON(!(mc & MCTL_PSM_RUN));
+ WARN_ON(mc & MCTL_EN_MAC);
+}
+
+void brcms_c_enable_mac(struct brcms_c_info *wlc)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ struct d11regs __iomem *regs = wlc_hw->regs;
+ u32 mc, mi;
+
+ BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit,
+ wlc->band->bandunit);
+
+ /*
+ * Track overlapping suspend requests
+ */
+ wlc_hw->mac_suspend_depth--;
+ if (wlc_hw->mac_suspend_depth > 0)
+ return;
+
+ mc = R_REG(&regs->maccontrol);
+ WARN_ON(mc & MCTL_PSM_JMP_0);
+ WARN_ON(mc & MCTL_EN_MAC);
+ WARN_ON(!(mc & MCTL_PSM_RUN));
+
+ brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, MCTL_EN_MAC);
+ W_REG(&regs->macintstatus, MI_MACSSPNDD);
+
+ mc = R_REG(&regs->maccontrol);
+ WARN_ON(mc & MCTL_PSM_JMP_0);
+ WARN_ON(!(mc & MCTL_EN_MAC));
+ WARN_ON(!(mc & MCTL_PSM_RUN));
+
+ mi = R_REG(&regs->macintstatus);
+ WARN_ON(mi & MI_MACSSPNDD);
+
+ brcms_c_ucode_wake_override_clear(wlc_hw,
+ BRCMS_WAKE_OVERRIDE_MACSUSPEND);
+}
+
+void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode)
+{
+ wlc_hw->hw_stf_ss_opmode = stf_mode;
+
+ if (wlc_hw->clk)
+ brcms_upd_ofdm_pctl1_table(wlc_hw);
+}
+
+static bool brcms_b_validate_chip_access(struct brcms_hardware *wlc_hw)
+{
+ struct d11regs __iomem *regs;
+ u32 w, val;
+ struct wiphy *wiphy = wlc_hw->wlc->wiphy;
+
+ BCMMSG(wiphy, "wl%d\n", wlc_hw->unit);
+
+ regs = wlc_hw->regs;
+
+ /* Validate dchip register access */
+
+ W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(&regs->objaddr);
+ w = R_REG(&regs->objdata);
+
+ /* Can we write and read back a 32bit register? */
+ W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(&regs->objaddr);
+ W_REG(&regs->objdata, (u32) 0xaa5555aa);
+
+ W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(&regs->objaddr);
+ val = R_REG(&regs->objdata);
+ if (val != (u32) 0xaa5555aa) {
+ wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, "
+ "expected 0xaa5555aa\n", wlc_hw->unit, val);
+ return false;
+ }
+
+ W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(&regs->objaddr);
+ W_REG(&regs->objdata, (u32) 0x55aaaa55);
+
+ W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(&regs->objaddr);
+ val = R_REG(&regs->objdata);
+ if (val != (u32) 0x55aaaa55) {
+ wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, "
+ "expected 0x55aaaa55\n", wlc_hw->unit, val);
+ return false;
+ }
+
+ W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(&regs->objaddr);
+ W_REG(&regs->objdata, w);
+
+ /* clear CFPStart */
+ W_REG(&regs->tsf_cfpstart, 0);
+
+ w = R_REG(&regs->maccontrol);
+ if ((w != (MCTL_IHR_EN | MCTL_WAKE)) &&
+ (w != (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE))) {
+ wiphy_err(wiphy, "wl%d: validate_chip_access: maccontrol = "
+ "0x%x, expected 0x%x or 0x%x\n", wlc_hw->unit, w,
+ (MCTL_IHR_EN | MCTL_WAKE),
+ (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE));
+ return false;
+ }
+
+ return true;
+}
+
+#define PHYPLL_WAIT_US 100000
+
+void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on)
+{
+ struct d11regs __iomem *regs;
+ u32 tmp;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ tmp = 0;
+ regs = wlc_hw->regs;
+
+ if (on) {
+ if ((wlc_hw->sih->chip == BCM4313_CHIP_ID)) {
+ OR_REG(&regs->clk_ctl_st,
+ (CCS_ERSRC_REQ_HT | CCS_ERSRC_REQ_D11PLL |
+ CCS_ERSRC_REQ_PHYPLL));
+ SPINWAIT((R_REG(&regs->clk_ctl_st) &
+ (CCS_ERSRC_AVAIL_HT)) != (CCS_ERSRC_AVAIL_HT),
+ PHYPLL_WAIT_US);
+
+ tmp = R_REG(&regs->clk_ctl_st);
+ if ((tmp & (CCS_ERSRC_AVAIL_HT)) !=
+ (CCS_ERSRC_AVAIL_HT))
+ wiphy_err(wlc_hw->wlc->wiphy, "%s: turn on PHY"
+ " PLL failed\n", __func__);
+ } else {
+ OR_REG(&regs->clk_ctl_st,
+ (CCS_ERSRC_REQ_D11PLL | CCS_ERSRC_REQ_PHYPLL));
+ SPINWAIT((R_REG(&regs->clk_ctl_st) &
+ (CCS_ERSRC_AVAIL_D11PLL |
+ CCS_ERSRC_AVAIL_PHYPLL)) !=
+ (CCS_ERSRC_AVAIL_D11PLL |
+ CCS_ERSRC_AVAIL_PHYPLL), PHYPLL_WAIT_US);
+
+ tmp = R_REG(&regs->clk_ctl_st);
+ if ((tmp &
+ (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL))
+ !=
+ (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL))
+ wiphy_err(wlc_hw->wlc->wiphy, "%s: turn on "
+ "PHY PLL failed\n", __func__);
+ }
+ } else {
+ /*
+ * Since the PLL may be shared, other cores can still
+ * be requesting it; so we'll deassert the request but
+ * not wait for status to comply.
+ */
+ AND_REG(&regs->clk_ctl_st, ~CCS_ERSRC_REQ_PHYPLL);
+ tmp = R_REG(&regs->clk_ctl_st);
+ }
+}
+
+static void brcms_c_coredisable(struct brcms_hardware *wlc_hw)
+{
+ bool dev_gone;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ dev_gone = brcms_deviceremoved(wlc_hw->wlc);
+
+ if (dev_gone)
+ return;
+
+ if (wlc_hw->noreset)
+ return;
+
+ /* radio off */
+ wlc_phy_switch_radio(wlc_hw->band->pi, OFF);
+
+ /* turn off analog core */
+ wlc_phy_anacore(wlc_hw->band->pi, OFF);
+
+ /* turn off PHYPLL to save power */
+ brcms_b_core_phypll_ctl(wlc_hw, false);
+
+ wlc_hw->clk = false;
+ ai_core_disable(wlc_hw->sih, 0);
+ wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
+}
+
+static void brcms_c_flushqueues(struct brcms_c_info *wlc)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ uint i;
+
+ /* free any posted tx packets */
+ for (i = 0; i < NFIFO; i++)
+ if (wlc_hw->di[i]) {
+ dma_txreclaim(wlc_hw->di[i], DMA_RANGE_ALL);
+ wlc->core->txpktpend[i] = 0;
+ BCMMSG(wlc->wiphy, "pktpend fifo %d clrd\n", i);
+ }
+
+ /* free any posted rx packets */
+ dma_rxreclaim(wlc_hw->di[RX_FIFO]);
+}
+
+static u16
+brcms_b_read_objmem(struct brcms_hardware *wlc_hw, uint offset, u32 sel)
+{
+ struct d11regs __iomem *regs = wlc_hw->regs;
+ u16 __iomem *objdata_lo = (u16 __iomem *)&regs->objdata;
+ u16 __iomem *objdata_hi = objdata_lo + 1;
+ u16 v;
+
+ W_REG(&regs->objaddr, sel | (offset >> 2));
+ (void)R_REG(&regs->objaddr);
+ if (offset & 2)
+ v = R_REG(objdata_hi);
+ else
+ v = R_REG(objdata_lo);
+
+ return v;
+}
+
+static void
+brcms_b_write_objmem(struct brcms_hardware *wlc_hw, uint offset, u16 v,
+ u32 sel)
+{
+ struct d11regs __iomem *regs = wlc_hw->regs;
+ u16 __iomem *objdata_lo = (u16 __iomem *)&regs->objdata;
+ u16 __iomem *objdata_hi = objdata_lo + 1;
+
+ W_REG(&regs->objaddr, sel | (offset >> 2));
+ (void)R_REG(&regs->objaddr);
+ if (offset & 2)
+ W_REG(objdata_hi, v);
+ else
+ W_REG(objdata_lo, v);
+}
+
+/*
+ * Read a single u16 from shared memory.
+ * SHM 'offset' needs to be an even address
+ */
+u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset)
+{
+ return brcms_b_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL);
+}
+
+/*
+ * Write a single u16 to shared memory.
+ * SHM 'offset' needs to be an even address
+ */
+void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v)
+{
+ brcms_b_write_objmem(wlc_hw, offset, v, OBJADDR_SHM_SEL);
+}
+
+/*
+ * Copy a buffer to shared memory of specified type .
+ * SHM 'offset' needs to be an even address and
+ * Buffer length 'len' must be an even number of bytes
+ * 'sel' selects the type of memory
+ */
+void
+brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset,
+ const void *buf, int len, u32 sel)
+{
+ u16 v;
+ const u8 *p = (const u8 *)buf;
+ int i;
+
+ if (len <= 0 || (offset & 1) || (len & 1))
+ return;
+
+ for (i = 0; i < len; i += 2) {
+ v = p[i] | (p[i + 1] << 8);
+ brcms_b_write_objmem(wlc_hw, offset + i, v, sel);
+ }
+}
+
+/*
+ * Copy a piece of shared memory of specified type to a buffer .
+ * SHM 'offset' needs to be an even address and
+ * Buffer length 'len' must be an even number of bytes
+ * 'sel' selects the type of memory
+ */
+void
+brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, void *buf,
+ int len, u32 sel)
+{
+ u16 v;
+ u8 *p = (u8 *) buf;
+ int i;
+
+ if (len <= 0 || (offset & 1) || (len & 1))
+ return;
+
+ for (i = 0; i < len; i += 2) {
+ v = brcms_b_read_objmem(wlc_hw, offset + i, sel);
+ p[i] = v & 0xFF;
+ p[i + 1] = (v >> 8) & 0xFF;
+ }
+}
+
+/* Copy a buffer to shared memory.
+ * SHM 'offset' needs to be an even address and
+ * Buffer length 'len' must be an even number of bytes
+ */
+static void brcms_c_copyto_shm(struct brcms_c_info *wlc, uint offset,
+ const void *buf, int len)
+{
+ brcms_b_copyto_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL);
+}
+
+static void brcms_b_retrylimit_upd(struct brcms_hardware *wlc_hw,
+ u16 SRL, u16 LRL)
+{
+ wlc_hw->SRL = SRL;
+ wlc_hw->LRL = LRL;
+
+ /* write retry limit to SCR, shouldn't need to suspend */
+ if (wlc_hw->up) {
+ W_REG(&wlc_hw->regs->objaddr,
+ OBJADDR_SCR_SEL | S_DOT11_SRC_LMT);
+ (void)R_REG(&wlc_hw->regs->objaddr);
+ W_REG(&wlc_hw->regs->objdata, wlc_hw->SRL);
+ W_REG(&wlc_hw->regs->objaddr,
+ OBJADDR_SCR_SEL | S_DOT11_LRC_LMT);
+ (void)R_REG(&wlc_hw->regs->objaddr);
+ W_REG(&wlc_hw->regs->objdata, wlc_hw->LRL);
+ }
+}
+
+static void brcms_b_pllreq(struct brcms_hardware *wlc_hw, bool set, u32 req_bit)
+{
+ if (set) {
+ if (mboolisset(wlc_hw->pllreq, req_bit))
+ return;
+
+ mboolset(wlc_hw->pllreq, req_bit);
+
+ if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) {
+ if (!wlc_hw->sbclk)
+ brcms_b_xtal(wlc_hw, ON);
+ }
+ } else {
+ if (!mboolisset(wlc_hw->pllreq, req_bit))
+ return;
+
+ mboolclr(wlc_hw->pllreq, req_bit);
+
+ if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) {
+ if (wlc_hw->sbclk)
+ brcms_b_xtal(wlc_hw, OFF);
+ }
+ }
+}
+
+static void brcms_b_antsel_set(struct brcms_hardware *wlc_hw, u32 antsel_avail)
+{
+ wlc_hw->antsel_avail = antsel_avail;
+}
+
+/*
+ * conditions under which the PM bit should be set in outgoing frames
+ * and STAY_AWAKE is meaningful
+ */
+static bool brcms_c_ps_allowed(struct brcms_c_info *wlc)
+{
+ struct brcms_bss_cfg *cfg = wlc->bsscfg;
+
+ /* disallow PS when one of the following global conditions meets */
+ if (!wlc->pub->associated)
+ return false;
+
+ /* disallow PS when one of these meets when not scanning */
+ if (wlc->monitor)
+ return false;
+
+ if (cfg->associated) {
+ /*
+ * disallow PS when one of the following
+ * bsscfg specific conditions meets
+ */
+ if (!cfg->BSS)
+ return false;
+
+ return false;
+ }
+
+ return true;
+}
+
+static void brcms_c_statsupd(struct brcms_c_info *wlc)
+{
+ int i;
+ struct macstat macstats;
+#ifdef BCMDBG
+ u16 delta;
+ u16 rxf0ovfl;
+ u16 txfunfl[NFIFO];
+#endif /* BCMDBG */
+
+ /* if driver down, make no sense to update stats */
+ if (!wlc->pub->up)
+ return;
+
+#ifdef BCMDBG
+ /* save last rx fifo 0 overflow count */
+ rxf0ovfl = wlc->core->macstat_snapshot->rxf0ovfl;
+
+ /* save last tx fifo underflow count */
+ for (i = 0; i < NFIFO; i++)
+ txfunfl[i] = wlc->core->macstat_snapshot->txfunfl[i];
+#endif /* BCMDBG */
+
+ /* Read mac stats from contiguous shared memory */
+ brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, &macstats,
+ sizeof(struct macstat), OBJADDR_SHM_SEL);
+
+#ifdef BCMDBG
+ /* check for rx fifo 0 overflow */
+ delta = (u16) (wlc->core->macstat_snapshot->rxf0ovfl - rxf0ovfl);
+ if (delta)
+ wiphy_err(wlc->wiphy, "wl%d: %u rx fifo 0 overflows!\n",
+ wlc->pub->unit, delta);
+
+ /* check for tx fifo underflows */
+ for (i = 0; i < NFIFO; i++) {
+ delta =
+ (u16) (wlc->core->macstat_snapshot->txfunfl[i] -
+ txfunfl[i]);
+ if (delta)
+ wiphy_err(wlc->wiphy, "wl%d: %u tx fifo %d underflows!"
+ "\n", wlc->pub->unit, delta, i);
+ }
+#endif /* BCMDBG */
+
+ /* merge counters from dma module */
+ for (i = 0; i < NFIFO; i++) {
+ if (wlc->hw->di[i])
+ dma_counterreset(wlc->hw->di[i]);
+ }
+}
+
+static void brcms_b_reset(struct brcms_hardware *wlc_hw)
+{
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ /* reset the core */
+ if (!brcms_deviceremoved(wlc_hw->wlc))
+ brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS);
+
+ /* purge the dma rings */
+ brcms_c_flushqueues(wlc_hw->wlc);
+}
+
+void brcms_c_reset(struct brcms_c_info *wlc)
+{
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+ /* slurp up hw mac counters before core reset */
+ brcms_c_statsupd(wlc);
+
+ /* reset our snapshot of macstat counters */
+ memset((char *)wlc->core->macstat_snapshot, 0,
+ sizeof(struct macstat));
+
+ brcms_b_reset(wlc->hw);
+}
+
+/* Return the channel the driver should initialize during brcms_c_init.
+ * the channel may have to be changed from the currently configured channel
+ * if other configurations are in conflict (bandlocked, 11n mode disabled,
+ * invalid channel for current country, etc.)
+ */
+static u16 brcms_c_init_chanspec(struct brcms_c_info *wlc)
+{
+ u16 chanspec =
+ 1 | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE |
+ WL_CHANSPEC_BAND_2G;
+
+ return chanspec;
+}
+
+void brcms_c_init_scb(struct scb *scb)
+{
+ int i;
+
+ memset(scb, 0, sizeof(struct scb));
+ scb->flags = SCB_WMECAP | SCB_HTCAP;
+ for (i = 0; i < NUMPRIO; i++) {
+ scb->seqnum[i] = 0;
+ scb->seqctl[i] = 0xFFFF;
+ }
+
+ scb->seqctl_nonqos = 0xFFFF;
+ scb->magic = SCB_MAGIC;
+}
+
+/* d11 core init
+ * reset PSM
+ * download ucode/PCM
+ * let ucode run to suspended
+ * download ucode inits
+ * config other core registers
+ * init dma
+ */
+static void brcms_b_coreinit(struct brcms_c_info *wlc)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ struct d11regs __iomem *regs;
+ u32 sflags;
+ uint bcnint_us;
+ uint i = 0;
+ bool fifosz_fixup = false;
+ int err = 0;
+ u16 buf[NFIFO];
+ struct wiphy *wiphy = wlc->wiphy;
+ struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode;
+
+ regs = wlc_hw->regs;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ /* reset PSM */
+ brcms_b_mctrl(wlc_hw, ~0, (MCTL_IHR_EN | MCTL_PSM_JMP_0 | MCTL_WAKE));
+
+ brcms_ucode_download(wlc_hw);
+ /*
+ * FIFOSZ fixup. driver wants to controls the fifo allocation.
+ */
+ fifosz_fixup = true;
+
+ /* let the PSM run to the suspended state, set mode to BSS STA */
+ W_REG(&regs->macintstatus, -1);
+ brcms_b_mctrl(wlc_hw, ~0,
+ (MCTL_IHR_EN | MCTL_INFRA | MCTL_PSM_RUN | MCTL_WAKE));
+
+ /* wait for ucode to self-suspend after auto-init */
+ SPINWAIT(((R_REG(&regs->macintstatus) & MI_MACSSPNDD) == 0),
+ 1000 * 1000);
+ if ((R_REG(&regs->macintstatus) & MI_MACSSPNDD) == 0)
+ wiphy_err(wiphy, "wl%d: wlc_coreinit: ucode did not self-"
+ "suspend!\n", wlc_hw->unit);
+
+ brcms_c_gpio_init(wlc);
+
+ sflags = ai_core_sflags(wlc_hw->sih, 0, 0);
+
+ if (D11REV_IS(wlc_hw->corerev, 23)) {
+ if (BRCMS_ISNPHY(wlc_hw->band))
+ brcms_c_write_inits(wlc_hw, ucode->d11n0initvals16);
+ else
+ wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev"
+ " %d\n", __func__, wlc_hw->unit,
+ wlc_hw->corerev);
+ } else if (D11REV_IS(wlc_hw->corerev, 24)) {
+ if (BRCMS_ISLCNPHY(wlc_hw->band))
+ brcms_c_write_inits(wlc_hw, ucode->d11lcn0initvals24);
+ else
+ wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev"
+ " %d\n", __func__, wlc_hw->unit,
+ wlc_hw->corerev);
+ } else {
+ wiphy_err(wiphy, "%s: wl%d: unsupported corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev);
+ }
+
+ /* For old ucode, txfifo sizes needs to be modified(increased) */
+ if (fifosz_fixup == true)
+ brcms_b_corerev_fifofixup(wlc_hw);
+
+ /* check txfifo allocations match between ucode and driver */
+ buf[TX_AC_BE_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE0);
+ if (buf[TX_AC_BE_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]) {
+ i = TX_AC_BE_FIFO;
+ err = -1;
+ }
+ buf[TX_AC_VI_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE1);
+ if (buf[TX_AC_VI_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]) {
+ i = TX_AC_VI_FIFO;
+ err = -1;
+ }
+ buf[TX_AC_BK_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE2);
+ buf[TX_AC_VO_FIFO] = (buf[TX_AC_BK_FIFO] >> 8) & 0xff;
+ buf[TX_AC_BK_FIFO] &= 0xff;
+ if (buf[TX_AC_BK_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BK_FIFO]) {
+ i = TX_AC_BK_FIFO;
+ err = -1;
+ }
+ if (buf[TX_AC_VO_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO]) {
+ i = TX_AC_VO_FIFO;
+ err = -1;
+ }
+ buf[TX_BCMC_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE3);
+ buf[TX_ATIM_FIFO] = (buf[TX_BCMC_FIFO] >> 8) & 0xff;
+ buf[TX_BCMC_FIFO] &= 0xff;
+ if (buf[TX_BCMC_FIFO] != wlc_hw->xmtfifo_sz[TX_BCMC_FIFO]) {
+ i = TX_BCMC_FIFO;
+ err = -1;
+ }
+ if (buf[TX_ATIM_FIFO] != wlc_hw->xmtfifo_sz[TX_ATIM_FIFO]) {
+ i = TX_ATIM_FIFO;
+ err = -1;
+ }
+ if (err != 0)
+ wiphy_err(wiphy, "wlc_coreinit: txfifo mismatch: ucode size %d"
+ " driver size %d index %d\n", buf[i],
+ wlc_hw->xmtfifo_sz[i], i);
+
+ /* make sure we can still talk to the mac */
+ WARN_ON(R_REG(&regs->maccontrol) == 0xffffffff);
+
+ /* band-specific inits done by wlc_bsinit() */
+
+ /* Set up frame burst size and antenna swap threshold init values */
+ brcms_b_write_shm(wlc_hw, M_MBURST_SIZE, MAXTXFRAMEBURST);
+ brcms_b_write_shm(wlc_hw, M_MAX_ANTCNT, ANTCNT);
+
+ /* enable one rx interrupt per received frame */
+ W_REG(&regs->intrcvlazy[0], (1 << IRL_FC_SHIFT));
+
+ /* set the station mode (BSS STA) */
+ brcms_b_mctrl(wlc_hw,
+ (MCTL_INFRA | MCTL_DISCARD_PMQ | MCTL_AP),
+ (MCTL_INFRA | MCTL_DISCARD_PMQ));
+
+ /* set up Beacon interval */
+ bcnint_us = 0x8000 << 10;
+ W_REG(&regs->tsf_cfprep, (bcnint_us << CFPREP_CBI_SHIFT));
+ W_REG(&regs->tsf_cfpstart, bcnint_us);
+ W_REG(&regs->macintstatus, MI_GP1);
+
+ /* write interrupt mask */
+ W_REG(&regs->intctrlregs[RX_FIFO].intmask, DEF_RXINTMASK);
+
+ /* allow the MAC to control the PHY clock (dynamic on/off) */
+ brcms_b_macphyclk_set(wlc_hw, ON);
+
+ /* program dynamic clock control fast powerup delay register */
+ wlc->fastpwrup_dly = ai_clkctl_fast_pwrup_delay(wlc_hw->sih);
+ W_REG(&regs->scc_fastpwrup_dly, wlc->fastpwrup_dly);
+
+ /* tell the ucode the corerev */
+ brcms_b_write_shm(wlc_hw, M_MACHW_VER, (u16) wlc_hw->corerev);
+
+ /* tell the ucode MAC capabilities */
+ brcms_b_write_shm(wlc_hw, M_MACHW_CAP_L,
+ (u16) (wlc_hw->machwcap & 0xffff));
+ brcms_b_write_shm(wlc_hw, M_MACHW_CAP_H,
+ (u16) ((wlc_hw->
+ machwcap >> 16) & 0xffff));
+
+ /* write retry limits to SCR, this done after PSM init */
+ W_REG(&regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_SRC_LMT);
+ (void)R_REG(&regs->objaddr);
+ W_REG(&regs->objdata, wlc_hw->SRL);
+ W_REG(&regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_LRC_LMT);
+ (void)R_REG(&regs->objaddr);
+ W_REG(&regs->objdata, wlc_hw->LRL);
+
+ /* write rate fallback retry limits */
+ brcms_b_write_shm(wlc_hw, M_SFRMTXCNTFBRTHSD, wlc_hw->SFBL);
+ brcms_b_write_shm(wlc_hw, M_LFRMTXCNTFBRTHSD, wlc_hw->LFBL);
+
+ AND_REG(&regs->ifs_ctl, 0x0FFF);
+ W_REG(&regs->ifs_aifsn, EDCF_AIFSN_MIN);
+
+ /* init the tx dma engines */
+ for (i = 0; i < NFIFO; i++) {
+ if (wlc_hw->di[i])
+ dma_txinit(wlc_hw->di[i]);
+ }
+
+ /* init the rx dma engine(s) and post receive buffers */
+ dma_rxinit(wlc_hw->di[RX_FIFO]);
+ dma_rxfill(wlc_hw->di[RX_FIFO]);
+}
+
+void
+static brcms_b_init(struct brcms_hardware *wlc_hw, u16 chanspec,
+ bool mute) {
+ u32 macintmask;
+ bool fastclk;
+ struct brcms_c_info *wlc = wlc_hw->wlc;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ /* request FAST clock if not on */
+ fastclk = wlc_hw->forcefastclk;
+ if (!fastclk)
+ brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+ /* disable interrupts */
+ macintmask = brcms_intrsoff(wlc->wl);
+
+ /* set up the specified band and chanspec */
+ brcms_c_setxband(wlc_hw, chspec_bandunit(chanspec));
+ wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec);
+
+ /* do one-time phy inits and calibration */
+ wlc_phy_cal_init(wlc_hw->band->pi);
+
+ /* core-specific initialization */
+ brcms_b_coreinit(wlc);
+
+ /* suspend the tx fifos and mute the phy for preism cac time */
+ if (mute)
+ brcms_b_mute(wlc_hw, ON, PHY_MUTE_FOR_PREISM);
+
+ /* band-specific inits */
+ brcms_b_bsinit(wlc, chanspec);
+
+ /* restore macintmask */
+ brcms_intrsrestore(wlc->wl, macintmask);
+
+ /* seed wake_override with BRCMS_WAKE_OVERRIDE_MACSUSPEND since the mac
+ * is suspended and brcms_c_enable_mac() will clear this override bit.
+ */
+ mboolset(wlc_hw->wake_override, BRCMS_WAKE_OVERRIDE_MACSUSPEND);
+
+ /*
+ * initialize mac_suspend_depth to 1 to match ucode
+ * initial suspended state
+ */
+ wlc_hw->mac_suspend_depth = 1;
+
+ /* restore the clk */
+ if (!fastclk)
+ brcms_b_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+}
+
+static void brcms_c_set_phy_chanspec(struct brcms_c_info *wlc,
+ u16 chanspec)
+{
+ /* Save our copy of the chanspec */
+ wlc->chanspec = chanspec;
+
+ /* Set the chanspec and power limits for this locale */
+ brcms_c_channel_set_chanspec(wlc->cmi, chanspec, BRCMS_TXPWR_MAX);
+
+ if (wlc->stf->ss_algosel_auto)
+ brcms_c_stf_ss_algo_channel_get(wlc, &wlc->stf->ss_algo_channel,
+ chanspec);
+
+ brcms_c_stf_ss_update(wlc, wlc->band);
+}
+
+static void
+brcms_default_rateset(struct brcms_c_info *wlc, struct brcms_c_rateset *rs)
+{
+ brcms_c_rateset_default(rs, NULL, wlc->band->phytype,
+ wlc->band->bandtype, false, BRCMS_RATE_MASK_FULL,
+ (bool) (wlc->pub->_n_enab & SUPPORT_11N),
+ brcms_chspec_bw(wlc->default_bss->chanspec),
+ wlc->stf->txstreams);
+}
+
+/* derive wlc->band->basic_rate[] table from 'rateset' */
+static void brcms_c_rate_lookup_init(struct brcms_c_info *wlc,
+ struct brcms_c_rateset *rateset)
+{
+ u8 rate;
+ u8 mandatory;
+ u8 cck_basic = 0;
+ u8 ofdm_basic = 0;
+ u8 *br = wlc->band->basic_rate;
+ uint i;
+
+ /* incoming rates are in 500kbps units as in 802.11 Supported Rates */
+ memset(br, 0, BRCM_MAXRATE + 1);
+
+ /* For each basic rate in the rates list, make an entry in the
+ * best basic lookup.
+ */
+ for (i = 0; i < rateset->count; i++) {
+ /* only make an entry for a basic rate */
+ if (!(rateset->rates[i] & BRCMS_RATE_FLAG))
+ continue;
+
+ /* mask off basic bit */
+ rate = (rateset->rates[i] & BRCMS_RATE_MASK);
+
+ if (rate > BRCM_MAXRATE) {
+ wiphy_err(wlc->wiphy, "brcms_c_rate_lookup_init: "
+ "invalid rate 0x%X in rate set\n",
+ rateset->rates[i]);
+ continue;
+ }
+
+ br[rate] = rate;
+ }
+
+ /* The rate lookup table now has non-zero entries for each
+ * basic rate, equal to the basic rate: br[basicN] = basicN
+ *
+ * To look up the best basic rate corresponding to any
+ * particular rate, code can use the basic_rate table
+ * like this
+ *
+ * basic_rate = wlc->band->basic_rate[tx_rate]
+ *
+ * Make sure there is a best basic rate entry for
+ * every rate by walking up the table from low rates
+ * to high, filling in holes in the lookup table
+ */
+
+ for (i = 0; i < wlc->band->hw_rateset.count; i++) {
+ rate = wlc->band->hw_rateset.rates[i];
+
+ if (br[rate] != 0) {
+ /* This rate is a basic rate.
+ * Keep track of the best basic rate so far by
+ * modulation type.
+ */
+ if (is_ofdm_rate(rate))
+ ofdm_basic = rate;
+ else
+ cck_basic = rate;
+
+ continue;
+ }
+
+ /* This rate is not a basic rate so figure out the
+ * best basic rate less than this rate and fill in
+ * the hole in the table
+ */
+
+ br[rate] = is_ofdm_rate(rate) ? ofdm_basic : cck_basic;
+
+ if (br[rate] != 0)
+ continue;
+
+ if (is_ofdm_rate(rate)) {
+ /*
+ * In 11g and 11a, the OFDM mandatory rates
+ * are 6, 12, and 24 Mbps
+ */
+ if (rate >= BRCM_RATE_24M)
+ mandatory = BRCM_RATE_24M;
+ else if (rate >= BRCM_RATE_12M)
+ mandatory = BRCM_RATE_12M;
+ else
+ mandatory = BRCM_RATE_6M;
+ } else {
+ /* In 11b, all CCK rates are mandatory 1 - 11 Mbps */
+ mandatory = rate;
+ }
+
+ br[rate] = mandatory;
+ }
+}
+
+static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc,
+ u16 chanspec)
+{
+ struct brcms_c_rateset default_rateset;
+ uint parkband;
+ uint i, band_order[2];
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+ /*
+ * We might have been bandlocked during down and the chip
+ * power-cycled (hibernate). Figure out the right band to park on
+ */
+ if (wlc->bandlocked || wlc->pub->_nbands == 1) {
+ /* updated in brcms_c_bandlock() */
+ parkband = wlc->band->bandunit;
+ band_order[0] = band_order[1] = parkband;
+ } else {
+ /* park on the band of the specified chanspec */
+ parkband = chspec_bandunit(chanspec);
+
+ /* order so that parkband initialize last */
+ band_order[0] = parkband ^ 1;
+ band_order[1] = parkband;
+ }
+
+ /* make each band operational, software state init */
+ for (i = 0; i < wlc->pub->_nbands; i++) {
+ uint j = band_order[i];
+
+ wlc->band = wlc->bandstate[j];
+
+ brcms_default_rateset(wlc, &default_rateset);
+
+ /* fill in hw_rate */
+ brcms_c_rateset_filter(&default_rateset, &wlc->band->hw_rateset,
+ false, BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK,
+ (bool) (wlc->pub->_n_enab & SUPPORT_11N));
+
+ /* init basic rate lookup */
+ brcms_c_rate_lookup_init(wlc, &default_rateset);
+ }
+
+ /* sync up phy/radio chanspec */
+ brcms_c_set_phy_chanspec(wlc, chanspec);
+}
+
+static void brcms_c_mac_bcn_promisc(struct brcms_c_info *wlc)
+{
+ if (wlc->bcnmisc_monitor)
+ brcms_b_mctrl(wlc->hw, MCTL_BCNS_PROMISC, MCTL_BCNS_PROMISC);
+ else
+ brcms_b_mctrl(wlc->hw, MCTL_BCNS_PROMISC, 0);
+}
+
+void brcms_c_mac_bcn_promisc_change(struct brcms_c_info *wlc, bool promisc)
+{
+ wlc->bcnmisc_monitor = promisc;
+ brcms_c_mac_bcn_promisc(wlc);
+}
+
+/* set or clear maccontrol bits MCTL_PROMISC and MCTL_KEEPCONTROL */
+static void brcms_c_mac_promisc(struct brcms_c_info *wlc)
+{
+ u32 promisc_bits = 0;
+
+ /*
+ * promiscuous mode just sets MCTL_PROMISC
+ * Note: APs get all BSS traffic without the need to set
+ * the MCTL_PROMISC bit since all BSS data traffic is
+ * directed at the AP
+ */
+ if (wlc->pub->promisc)
+ promisc_bits |= MCTL_PROMISC;
+
+ /* monitor mode needs both MCTL_PROMISC and MCTL_KEEPCONTROL
+ * Note: monitor mode also needs MCTL_BCNS_PROMISC, but that is
+ * handled in brcms_c_mac_bcn_promisc()
+ */
+ if (wlc->monitor)
+ promisc_bits |= MCTL_PROMISC | MCTL_KEEPCONTROL;
+
+ brcms_b_mctrl(wlc->hw, MCTL_PROMISC | MCTL_KEEPCONTROL, promisc_bits);
+}
+
+/*
+ * ucode, hwmac update
+ * Channel dependent updates for ucode and hw
+ */
+static void brcms_c_ucode_mac_upd(struct brcms_c_info *wlc)
+{
+ /* enable or disable any active IBSSs depending on whether or not
+ * we are on the home channel
+ */
+ if (wlc->home_chanspec == wlc_phy_chanspec_get(wlc->band->pi)) {
+ if (wlc->pub->associated) {
+ /*
+ * BMAC_NOTE: This is something that should be fixed
+ * in ucode inits. I think that the ucode inits set
+ * up the bcn templates and shm values with a bogus
+ * beacon. This should not be done in the inits. If
+ * ucode needs to set up a beacon for testing, the
+ * test routines should write it down, not expect the
+ * inits to populate a bogus beacon.
+ */
+ if (BRCMS_PHY_11N_CAP(wlc->band))
+ brcms_b_write_shm(wlc->hw,
+ M_BCN_TXTSF_OFFSET, 0);
+ }
+ } else {
+ /* disable an active IBSS if we are not on the home channel */
+ }
+
+ /* update the various promisc bits */
+ brcms_c_mac_bcn_promisc(wlc);
+ brcms_c_mac_promisc(wlc);
+}
+
+static void brcms_c_write_rate_shm(struct brcms_c_info *wlc, u8 rate,
+ u8 basic_rate)
+{
+ u8 phy_rate, index;
+ u8 basic_phy_rate, basic_index;
+ u16 dir_table, basic_table;
+ u16 basic_ptr;
+
+ /* Shared memory address for the table we are reading */
+ dir_table = is_ofdm_rate(basic_rate) ? M_RT_DIRMAP_A : M_RT_DIRMAP_B;
+
+ /* Shared memory address for the table we are writing */
+ basic_table = is_ofdm_rate(rate) ? M_RT_BBRSMAP_A : M_RT_BBRSMAP_B;
+
+ /*
+ * for a given rate, the LS-nibble of the PLCP SIGNAL field is
+ * the index into the rate table.
+ */
+ phy_rate = rate_info[rate] & BRCMS_RATE_MASK;
+ basic_phy_rate = rate_info[basic_rate] & BRCMS_RATE_MASK;
+ index = phy_rate & 0xf;
+ basic_index = basic_phy_rate & 0xf;
+
+ /* Find the SHM pointer to the ACK rate entry by looking in the
+ * Direct-map Table
+ */
+ basic_ptr = brcms_b_read_shm(wlc->hw, (dir_table + basic_index * 2));
+
+ /* Update the SHM BSS-basic-rate-set mapping table with the pointer
+ * to the correct basic rate for the given incoming rate
+ */
+ brcms_b_write_shm(wlc->hw, (basic_table + index * 2), basic_ptr);
+}
+
+static const struct brcms_c_rateset *
+brcms_c_rateset_get_hwrs(struct brcms_c_info *wlc)
+{
+ const struct brcms_c_rateset *rs_dflt;
+
+ if (BRCMS_PHY_11N_CAP(wlc->band)) {
+ if (wlc->band->bandtype == BRCM_BAND_5G)
+ rs_dflt = &ofdm_mimo_rates;
+ else
+ rs_dflt = &cck_ofdm_mimo_rates;
+ } else if (wlc->band->gmode)
+ rs_dflt = &cck_ofdm_rates;
+ else
+ rs_dflt = &cck_rates;
+
+ return rs_dflt;
+}
+
+static void brcms_c_set_ratetable(struct brcms_c_info *wlc)
+{
+ const struct brcms_c_rateset *rs_dflt;
+ struct brcms_c_rateset rs;
+ u8 rate, basic_rate;
+ uint i;
+
+ rs_dflt = brcms_c_rateset_get_hwrs(wlc);
+
+ brcms_c_rateset_copy(rs_dflt, &rs);
+ brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams);
+
+ /* walk the phy rate table and update SHM basic rate lookup table */
+ for (i = 0; i < rs.count; i++) {
+ rate = rs.rates[i] & BRCMS_RATE_MASK;
+
+ /* for a given rate brcms_basic_rate returns the rate at
+ * which a response ACK/CTS should be sent.
+ */
+ basic_rate = brcms_basic_rate(wlc, rate);
+ if (basic_rate == 0)
+ /* This should only happen if we are using a
+ * restricted rateset.
+ */
+ basic_rate = rs.rates[0] & BRCMS_RATE_MASK;
+
+ brcms_c_write_rate_shm(wlc, rate, basic_rate);
+ }
+}
+
+/* band-specific init */
+static void brcms_c_bsinit(struct brcms_c_info *wlc)
+{
+ BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n",
+ wlc->pub->unit, wlc->band->bandunit);
+
+ /* write ucode ACK/CTS rate table */
+ brcms_c_set_ratetable(wlc);
+
+ /* update some band specific mac configuration */
+ brcms_c_ucode_mac_upd(wlc);
+
+ /* init antenna selection */
+ brcms_c_antsel_init(wlc->asi);
+
+}
+
+/* formula: IDLE_BUSY_RATIO_X_16 = (100-duty_cycle)/duty_cycle*16 */
+static int
+brcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle, bool isOFDM,
+ bool writeToShm)
+{
+ int idle_busy_ratio_x_16 = 0;
+ uint offset =
+ isOFDM ? M_TX_IDLE_BUSY_RATIO_X_16_OFDM :
+ M_TX_IDLE_BUSY_RATIO_X_16_CCK;
+ if (duty_cycle > 100 || duty_cycle < 0) {
+ wiphy_err(wlc->wiphy, "wl%d: duty cycle value off limit\n",
+ wlc->pub->unit);
+ return -EINVAL;
+ }
+ if (duty_cycle)
+ idle_busy_ratio_x_16 = (100 - duty_cycle) * 16 / duty_cycle;
+ /* Only write to shared memory when wl is up */
+ if (writeToShm)
+ brcms_b_write_shm(wlc->hw, offset, (u16) idle_busy_ratio_x_16);
+
+ if (isOFDM)
+ wlc->tx_duty_cycle_ofdm = (u16) duty_cycle;
+ else
+ wlc->tx_duty_cycle_cck = (u16) duty_cycle;
+
+ return 0;
+}
+
+/*
+ * Initialize the base precedence map for dequeueing
+ * from txq based on WME settings
+ */
+static void brcms_c_tx_prec_map_init(struct brcms_c_info *wlc)
+{
+ wlc->tx_prec_map = BRCMS_PREC_BMP_ALL;
+ memset(wlc->fifo2prec_map, 0, NFIFO * sizeof(u16));
+
+ wlc->fifo2prec_map[TX_AC_BK_FIFO] = BRCMS_PREC_BMP_AC_BK;
+ wlc->fifo2prec_map[TX_AC_BE_FIFO] = BRCMS_PREC_BMP_AC_BE;
+ wlc->fifo2prec_map[TX_AC_VI_FIFO] = BRCMS_PREC_BMP_AC_VI;
+ wlc->fifo2prec_map[TX_AC_VO_FIFO] = BRCMS_PREC_BMP_AC_VO;
+}
+
+static void
+brcms_c_txflowcontrol_signal(struct brcms_c_info *wlc,
+ struct brcms_txq_info *qi, bool on, int prio)
+{
+ /* transmit flowcontrol is not yet implemented */
+}
+
+static void brcms_c_txflowcontrol_reset(struct brcms_c_info *wlc)
+{
+ struct brcms_txq_info *qi;
+
+ for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) {
+ if (qi->stopped) {
+ brcms_c_txflowcontrol_signal(wlc, qi, OFF, ALLPRIO);
+ qi->stopped = 0;
+ }
+ }
+}
+
+/* push sw hps and wake state through hardware */
+static void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc)
+{
+ u32 v1, v2;
+ bool hps;
+ bool awake_before;
+
+ hps = brcms_c_ps_allowed(wlc);
+
+ BCMMSG(wlc->wiphy, "wl%d: hps %d\n", wlc->pub->unit, hps);
+
+ v1 = R_REG(&wlc->regs->maccontrol);
+ v2 = MCTL_WAKE;
+ if (hps)
+ v2 |= MCTL_HPS;
+
+ brcms_b_mctrl(wlc->hw, MCTL_WAKE | MCTL_HPS, v2);
+
+ awake_before = ((v1 & MCTL_WAKE) || ((v1 & MCTL_HPS) == 0));
+
+ if (!awake_before)
+ brcms_b_wait_for_wake(wlc->hw);
+}
+
+/*
+ * Write this BSS config's MAC address to core.
+ * Updates RXE match engine.
+ */
+static int brcms_c_set_mac(struct brcms_bss_cfg *bsscfg)
+{
+ int err = 0;
+ struct brcms_c_info *wlc = bsscfg->wlc;
+
+ /* enter the MAC addr into the RXE match registers */
+ brcms_c_set_addrmatch(wlc, RCM_MAC_OFFSET, bsscfg->cur_etheraddr);
+
+ brcms_c_ampdu_macaddr_upd(wlc);
+
+ return err;
+}
+
+/* Write the BSS config's BSSID address to core (set_bssid in d11procs.tcl).
+ * Updates RXE match engine.
+ */
+static void brcms_c_set_bssid(struct brcms_bss_cfg *bsscfg)
+{
+ /* we need to update BSSID in RXE match registers */
+ brcms_c_set_addrmatch(bsscfg->wlc, RCM_BSSID_OFFSET, bsscfg->BSSID);
+}
+
+static void brcms_b_set_shortslot(struct brcms_hardware *wlc_hw, bool shortslot)
+{
+ wlc_hw->shortslot = shortslot;
+
+ if (wlc_hw->band->bandtype == BRCM_BAND_2G && wlc_hw->up) {
+ brcms_c_suspend_mac_and_wait(wlc_hw->wlc);
+ brcms_b_update_slot_timing(wlc_hw, shortslot);
+ brcms_c_enable_mac(wlc_hw->wlc);
+ }
+}
+
+/*
+ * Suspend the the MAC and update the slot timing
+ * for standard 11b/g (20us slots) or shortslot 11g (9us slots).
+ */
+static void brcms_c_switch_shortslot(struct brcms_c_info *wlc, bool shortslot)
+{
+ /* use the override if it is set */
+ if (wlc->shortslot_override != BRCMS_SHORTSLOT_AUTO)
+ shortslot = (wlc->shortslot_override == BRCMS_SHORTSLOT_ON);
+
+ if (wlc->shortslot == shortslot)
+ return;
+
+ wlc->shortslot = shortslot;
+
+ brcms_b_set_shortslot(wlc->hw, shortslot);
+}
+
+static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec)
+{
+ if (wlc->home_chanspec != chanspec) {
+ wlc->home_chanspec = chanspec;
+
+ if (wlc->bsscfg->associated)
+ wlc->bsscfg->current_bss->chanspec = chanspec;
+ }
+}
+
+void
+brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec,
+ bool mute, struct txpwr_limits *txpwr)
+{
+ uint bandunit;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d: 0x%x\n", wlc_hw->unit, chanspec);
+
+ wlc_hw->chanspec = chanspec;
+
+ /* Switch bands if necessary */
+ if (wlc_hw->_nbands > 1) {
+ bandunit = chspec_bandunit(chanspec);
+ if (wlc_hw->band->bandunit != bandunit) {
+ /* brcms_b_setband disables other bandunit,
+ * use light band switch if not up yet
+ */
+ if (wlc_hw->up) {
+ wlc_phy_chanspec_radio_set(wlc_hw->
+ bandstate[bandunit]->
+ pi, chanspec);
+ brcms_b_setband(wlc_hw, bandunit, chanspec);
+ } else {
+ brcms_c_setxband(wlc_hw, bandunit);
+ }
+ }
+ }
+
+ wlc_phy_initcal_enable(wlc_hw->band->pi, !mute);
+
+ if (!wlc_hw->up) {
+ if (wlc_hw->clk)
+ wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr,
+ chanspec);
+ wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec);
+ } else {
+ wlc_phy_chanspec_set(wlc_hw->band->pi, chanspec);
+ wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, chanspec);
+
+ /* Update muting of the channel */
+ brcms_b_mute(wlc_hw, mute, 0);
+ }
+}
+
+/* switch to and initialize new band */
+static void brcms_c_setband(struct brcms_c_info *wlc,
+ uint bandunit)
+{
+ wlc->band = wlc->bandstate[bandunit];
+
+ if (!wlc->pub->up)
+ return;
+
+ /* wait for at least one beacon before entering sleeping state */
+ brcms_c_set_ps_ctrl(wlc);
+
+ /* band-specific initializations */
+ brcms_c_bsinit(wlc);
+}
+
+static void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec)
+{
+ uint bandunit;
+ bool switchband = false;
+ u16 old_chanspec = wlc->chanspec;
+
+ if (!brcms_c_valid_chanspec_db(wlc->cmi, chanspec)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Bad channel %d\n",
+ wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec));
+ return;
+ }
+
+ /* Switch bands if necessary */
+ if (wlc->pub->_nbands > 1) {
+ bandunit = chspec_bandunit(chanspec);
+ if (wlc->band->bandunit != bandunit || wlc->bandinit_pending) {
+ switchband = true;
+ if (wlc->bandlocked) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: chspec %d "
+ "band is locked!\n",
+ wlc->pub->unit, __func__,
+ CHSPEC_CHANNEL(chanspec));
+ return;
+ }
+ /*
+ * should the setband call come after the
+ * brcms_b_chanspec() ? if the setband updates
+ * (brcms_c_bsinit) use low level calls to inspect and
+ * set state, the state inspected may be from the wrong
+ * band, or the following brcms_b_set_chanspec() may
+ * undo the work.
+ */
+ brcms_c_setband(wlc, bandunit);
+ }
+ }
+
+ /* sync up phy/radio chanspec */
+ brcms_c_set_phy_chanspec(wlc, chanspec);
+
+ /* init antenna selection */
+ if (brcms_chspec_bw(old_chanspec) != brcms_chspec_bw(chanspec)) {
+ brcms_c_antsel_init(wlc->asi);
+
+ /* Fix the hardware rateset based on bw.
+ * Mainly add MCS32 for 40Mhz, remove MCS 32 for 20Mhz
+ */
+ brcms_c_rateset_bw_mcs_filter(&wlc->band->hw_rateset,
+ wlc->band->mimo_cap_40 ? brcms_chspec_bw(chanspec) : 0);
+ }
+
+ /* update some mac configuration since chanspec changed */
+ brcms_c_ucode_mac_upd(wlc);
+}
+
+/*
+ * This function changes the phytxctl for beacon based on current
+ * beacon ratespec AND txant setting as per this table:
+ * ratespec CCK ant = wlc->stf->txant
+ * OFDM ant = 3
+ */
+void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc,
+ u32 bcn_rspec)
+{
+ u16 phyctl;
+ u16 phytxant = wlc->stf->phytxant;
+ u16 mask = PHY_TXC_ANT_MASK;
+
+ /* for non-siso rates or default setting, use the available chains */
+ if (BRCMS_PHY_11N_CAP(wlc->band))
+ phytxant = brcms_c_stf_phytxchain_sel(wlc, bcn_rspec);
+
+ phyctl = brcms_b_read_shm(wlc->hw, M_BCN_PCTLWD);
+ phyctl = (phyctl & ~mask) | phytxant;
+ brcms_b_write_shm(wlc->hw, M_BCN_PCTLWD, phyctl);
+}
+
+/*
+ * centralized protection config change function to simplify debugging, no
+ * consistency checking this should be called only on changes to avoid overhead
+ * in periodic function
+ */
+void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val)
+{
+ BCMMSG(wlc->wiphy, "idx %d, val %d\n", idx, val);
+
+ switch (idx) {
+ case BRCMS_PROT_G_SPEC:
+ wlc->protection->_g = (bool) val;
+ break;
+ case BRCMS_PROT_G_OVR:
+ wlc->protection->g_override = (s8) val;
+ break;
+ case BRCMS_PROT_G_USER:
+ wlc->protection->gmode_user = (u8) val;
+ break;
+ case BRCMS_PROT_OVERLAP:
+ wlc->protection->overlap = (s8) val;
+ break;
+ case BRCMS_PROT_N_USER:
+ wlc->protection->nmode_user = (s8) val;
+ break;
+ case BRCMS_PROT_N_CFG:
+ wlc->protection->n_cfg = (s8) val;
+ break;
+ case BRCMS_PROT_N_CFG_OVR:
+ wlc->protection->n_cfg_override = (s8) val;
+ break;
+ case BRCMS_PROT_N_NONGF:
+ wlc->protection->nongf = (bool) val;
+ break;
+ case BRCMS_PROT_N_NONGF_OVR:
+ wlc->protection->nongf_override = (s8) val;
+ break;
+ case BRCMS_PROT_N_PAM_OVR:
+ wlc->protection->n_pam_override = (s8) val;
+ break;
+ case BRCMS_PROT_N_OBSS:
+ wlc->protection->n_obss = (bool) val;
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+static void brcms_c_ht_update_sgi_rx(struct brcms_c_info *wlc, int val)
+{
+ if (wlc->pub->up) {
+ brcms_c_update_beacon(wlc);
+ brcms_c_update_probe_resp(wlc, true);
+ }
+}
+
+static void brcms_c_ht_update_ldpc(struct brcms_c_info *wlc, s8 val)
+{
+ wlc->stf->ldpc = val;
+
+ if (wlc->pub->up) {
+ brcms_c_update_beacon(wlc);
+ brcms_c_update_probe_resp(wlc, true);
+ wlc_phy_ldpc_override_set(wlc->band->pi, (val ? true : false));
+ }
+}
+
+void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci,
+ const struct ieee80211_tx_queue_params *params,
+ bool suspend)
+{
+ int i;
+ struct shm_acparams acp_shm;
+ u16 *shm_entry;
+
+ /* Only apply params if the core is out of reset and has clocks */
+ if (!wlc->clk) {
+ wiphy_err(wlc->wiphy, "wl%d: %s : no-clock\n", wlc->pub->unit,
+ __func__);
+ return;
+ }
+
+ memset((char *)&acp_shm, 0, sizeof(struct shm_acparams));
+ /* fill in shm ac params struct */
+ acp_shm.txop = params->txop;
+ /* convert from units of 32us to us for ucode */
+ wlc->edcf_txop[aci & 0x3] = acp_shm.txop =
+ EDCF_TXOP2USEC(acp_shm.txop);
+ acp_shm.aifs = (params->aifs & EDCF_AIFSN_MASK);
+
+ if (aci == AC_VI && acp_shm.txop == 0
+ && acp_shm.aifs < EDCF_AIFSN_MAX)
+ acp_shm.aifs++;
+
+ if (acp_shm.aifs < EDCF_AIFSN_MIN
+ || acp_shm.aifs > EDCF_AIFSN_MAX) {
+ wiphy_err(wlc->wiphy, "wl%d: edcf_setparams: bad "
+ "aifs %d\n", wlc->pub->unit, acp_shm.aifs);
+ } else {
+ acp_shm.cwmin = params->cw_min;
+ acp_shm.cwmax = params->cw_max;
+ acp_shm.cwcur = acp_shm.cwmin;
+ acp_shm.bslots =
+ R_REG(&wlc->regs->tsf_random) & acp_shm.cwcur;
+ acp_shm.reggap = acp_shm.bslots + acp_shm.aifs;
+ /* Indicate the new params to the ucode */
+ acp_shm.status = brcms_b_read_shm(wlc->hw, (M_EDCF_QINFO +
+ wme_ac2fifo[aci] *
+ M_EDCF_QLEN +
+ M_EDCF_STATUS_OFF));
+ acp_shm.status |= WME_STATUS_NEWAC;
+
+ /* Fill in shm acparam table */
+ shm_entry = (u16 *) &acp_shm;
+ for (i = 0; i < (int)sizeof(struct shm_acparams); i += 2)
+ brcms_b_write_shm(wlc->hw,
+ M_EDCF_QINFO +
+ wme_ac2fifo[aci] * M_EDCF_QLEN + i,
+ *shm_entry++);
+ }
+
+ if (suspend) {
+ brcms_c_suspend_mac_and_wait(wlc);
+ brcms_c_enable_mac(wlc);
+ }
+}
+
+void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend)
+{
+ u16 aci;
+ int i_ac;
+ struct ieee80211_tx_queue_params txq_pars;
+ static const struct edcf_acparam default_edcf_acparams[] = {
+ {EDCF_AC_BE_ACI_STA, EDCF_AC_BE_ECW_STA, EDCF_AC_BE_TXOP_STA},
+ {EDCF_AC_BK_ACI_STA, EDCF_AC_BK_ECW_STA, EDCF_AC_BK_TXOP_STA},
+ {EDCF_AC_VI_ACI_STA, EDCF_AC_VI_ECW_STA, EDCF_AC_VI_TXOP_STA},
+ {EDCF_AC_VO_ACI_STA, EDCF_AC_VO_ECW_STA, EDCF_AC_VO_TXOP_STA}
+ }; /* ucode needs these parameters during its initialization */
+ const struct edcf_acparam *edcf_acp = &default_edcf_acparams[0];
+
+ for (i_ac = 0; i_ac < AC_COUNT; i_ac++, edcf_acp++) {
+ /* find out which ac this set of params applies to */
+ aci = (edcf_acp->ACI & EDCF_ACI_MASK) >> EDCF_ACI_SHIFT;
+
+ /* fill in shm ac params struct */
+ txq_pars.txop = edcf_acp->TXOP;
+ txq_pars.aifs = edcf_acp->ACI;
+
+ /* CWmin = 2^(ECWmin) - 1 */
+ txq_pars.cw_min = EDCF_ECW2CW(edcf_acp->ECW & EDCF_ECWMIN_MASK);
+ /* CWmax = 2^(ECWmax) - 1 */
+ txq_pars.cw_max = EDCF_ECW2CW((edcf_acp->ECW & EDCF_ECWMAX_MASK)
+ >> EDCF_ECWMAX_SHIFT);
+ brcms_c_wme_setparams(wlc, aci, &txq_pars, suspend);
+ }
+
+ if (suspend) {
+ brcms_c_suspend_mac_and_wait(wlc);
+ brcms_c_enable_mac(wlc);
+ }
+}
+
+/* maintain LED behavior in down state */
+static void brcms_c_down_led_upd(struct brcms_c_info *wlc)
+{
+ /*
+ * maintain LEDs while in down state, turn on sbclk if
+ * not available yet. Turn on sbclk if necessary
+ */
+ brcms_b_pllreq(wlc->hw, true, BRCMS_PLLREQ_FLIP);
+ brcms_b_pllreq(wlc->hw, false, BRCMS_PLLREQ_FLIP);
+}
+
+static void brcms_c_radio_monitor_start(struct brcms_c_info *wlc)
+{
+ /* Don't start the timer if HWRADIO feature is disabled */
+ if (wlc->radio_monitor)
+ return;
+
+ wlc->radio_monitor = true;
+ brcms_b_pllreq(wlc->hw, true, BRCMS_PLLREQ_RADIO_MON);
+ brcms_add_timer(wlc->radio_timer, TIMER_INTERVAL_RADIOCHK, true);
+}
+
+static void brcms_c_radio_disable(struct brcms_c_info *wlc)
+{
+ if (!wlc->pub->up) {
+ brcms_c_down_led_upd(wlc);
+ return;
+ }
+
+ brcms_c_radio_monitor_start(wlc);
+ brcms_down(wlc->wl);
+}
+
+static void brcms_c_radio_enable(struct brcms_c_info *wlc)
+{
+ if (wlc->pub->up)
+ return;
+
+ if (brcms_deviceremoved(wlc))
+ return;
+
+ brcms_up(wlc->wl);
+}
+
+static bool brcms_c_radio_monitor_stop(struct brcms_c_info *wlc)
+{
+ if (!wlc->radio_monitor)
+ return true;
+
+ wlc->radio_monitor = false;
+ brcms_b_pllreq(wlc->hw, false, BRCMS_PLLREQ_RADIO_MON);
+ return brcms_del_timer(wlc->radio_timer);
+}
+
+/* read hwdisable state and propagate to wlc flag */
+static void brcms_c_radio_hwdisable_upd(struct brcms_c_info *wlc)
+{
+ if (wlc->pub->hw_off)
+ return;
+
+ if (brcms_b_radio_read_hwdisabled(wlc->hw))
+ mboolset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE);
+ else
+ mboolclr(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE);
+}
+
+/*
+ * centralized radio disable/enable function,
+ * invoke radio enable/disable after updating hwradio status
+ */
+static void brcms_c_radio_upd(struct brcms_c_info *wlc)
+{
+ if (wlc->pub->radio_disabled)
+ brcms_c_radio_disable(wlc);
+ else
+ brcms_c_radio_enable(wlc);
+}
+
+/* update hwradio status and return it */
+bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc)
+{
+ brcms_c_radio_hwdisable_upd(wlc);
+
+ return mboolisset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE) ?
+ true : false;
+}
+
+/* periodical query hw radio button while driver is "down" */
+static void brcms_c_radio_timer(void *arg)
+{
+ struct brcms_c_info *wlc = (struct brcms_c_info *) arg;
+
+ if (brcms_deviceremoved(wlc)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc->pub->unit,
+ __func__);
+ brcms_down(wlc->wl);
+ return;
+ }
+
+ /* cap mpc off count */
+ if (wlc->mpc_offcnt < BRCMS_MPC_MAX_DELAYCNT)
+ wlc->mpc_offcnt++;
+
+ brcms_c_radio_hwdisable_upd(wlc);
+ brcms_c_radio_upd(wlc);
+}
+
+/* common low-level watchdog code */
+static void brcms_b_watchdog(void *arg)
+{
+ struct brcms_c_info *wlc = (struct brcms_c_info *) arg;
+ struct brcms_hardware *wlc_hw = wlc->hw;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ if (!wlc_hw->up)
+ return;
+
+ /* increment second count */
+ wlc_hw->now++;
+
+ /* Check for FIFO error interrupts */
+ brcms_b_fifoerrors(wlc_hw);
+
+ /* make sure RX dma has buffers */
+ dma_rxfill(wlc->hw->di[RX_FIFO]);
+
+ wlc_phy_watchdog(wlc_hw->band->pi);
+}
+
+static void brcms_c_radio_mpc_upd(struct brcms_c_info *wlc)
+{
+ bool mpc_radio, radio_state;
+
+ /*
+ * Clear the WL_RADIO_MPC_DISABLE bit when mpc feature is disabled
+ * in case the WL_RADIO_MPC_DISABLE bit was set. Stop the radio
+ * monitor also when WL_RADIO_MPC_DISABLE is the only reason that
+ * the radio is going down.
+ */
+ if (!wlc->mpc) {
+ if (!wlc->pub->radio_disabled)
+ return;
+ mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE);
+ brcms_c_radio_upd(wlc);
+ if (!wlc->pub->radio_disabled)
+ brcms_c_radio_monitor_stop(wlc);
+ return;
+ }
+
+ /*
+ * sync ismpc logic with WL_RADIO_MPC_DISABLE bit in
+ * wlc->pub->radio_disabled to go ON, always call radio_upd
+ * synchronously to go OFF, postpone radio_upd to later when
+ * context is safe(e.g. watchdog)
+ */
+ radio_state =
+ (mboolisset(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE) ? OFF :
+ ON);
+ mpc_radio = (brcms_c_ismpc(wlc) == true) ? OFF : ON;
+
+ if (radio_state == ON && mpc_radio == OFF)
+ wlc->mpc_delay_off = wlc->mpc_dlycnt;
+ else if (radio_state == OFF && mpc_radio == ON) {
+ mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE);
+ brcms_c_radio_upd(wlc);
+ if (wlc->mpc_offcnt < BRCMS_MPC_THRESHOLD)
+ wlc->mpc_dlycnt = BRCMS_MPC_MAX_DELAYCNT;
+ else
+ wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT;
+ }
+ /*
+ * Below logic is meant to capture the transition from mpc off
+ * to mpc on for reasons other than wlc->mpc_delay_off keeping
+ * the mpc off. In that case reset wlc->mpc_delay_off to
+ * wlc->mpc_dlycnt, so that we restart the countdown of mpc_delay_off
+ */
+ if ((wlc->prev_non_delay_mpc == false) &&
+ (brcms_c_is_non_delay_mpc(wlc) == true) && wlc->mpc_delay_off)
+ wlc->mpc_delay_off = wlc->mpc_dlycnt;
+
+ wlc->prev_non_delay_mpc = brcms_c_is_non_delay_mpc(wlc);
+}
+
+/* common watchdog code */
+static void brcms_c_watchdog(void *arg)
+{
+ struct brcms_c_info *wlc = (struct brcms_c_info *) arg;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+ if (!wlc->pub->up)
+ return;
+
+ if (brcms_deviceremoved(wlc)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc->pub->unit,
+ __func__);
+ brcms_down(wlc->wl);
+ return;
+ }
+
+ /* increment second count */
+ wlc->pub->now++;
+
+ /* delay radio disable */
+ if (wlc->mpc_delay_off) {
+ if (--wlc->mpc_delay_off == 0) {
+ mboolset(wlc->pub->radio_disabled,
+ WL_RADIO_MPC_DISABLE);
+ if (wlc->mpc && brcms_c_ismpc(wlc))
+ wlc->mpc_offcnt = 0;
+ }
+ }
+
+ /* mpc sync */
+ brcms_c_radio_mpc_upd(wlc);
+ /* radio sync: sw/hw/mpc --> radio_disable/radio_enable */
+ brcms_c_radio_hwdisable_upd(wlc);
+ brcms_c_radio_upd(wlc);
+ /* if radio is disable, driver may be down, quit here */
+ if (wlc->pub->radio_disabled)
+ return;
+
+ brcms_b_watchdog(wlc);
+
+ /*
+ * occasionally sample mac stat counters to
+ * detect 16-bit counter wrap
+ */
+ if ((wlc->pub->now % SW_TIMER_MAC_STAT_UPD) == 0)
+ brcms_c_statsupd(wlc);
+
+ if (BRCMS_ISNPHY(wlc->band) &&
+ ((wlc->pub->now - wlc->tempsense_lasttime) >=
+ BRCMS_TEMPSENSE_PERIOD)) {
+ wlc->tempsense_lasttime = wlc->pub->now;
+ brcms_c_tempsense_upd(wlc);
+ }
+}
+
+static void brcms_c_watchdog_by_timer(void *arg)
+{
+ brcms_c_watchdog(arg);
+}
+
+static bool brcms_c_timers_init(struct brcms_c_info *wlc, int unit)
+{
+ wlc->wdtimer = brcms_init_timer(wlc->wl, brcms_c_watchdog_by_timer,
+ wlc, "watchdog");
+ if (!wlc->wdtimer) {
+ wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for wdtimer "
+ "failed\n", unit);
+ goto fail;
+ }
+
+ wlc->radio_timer = brcms_init_timer(wlc->wl, brcms_c_radio_timer,
+ wlc, "radio");
+ if (!wlc->radio_timer) {
+ wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for radio_timer "
+ "failed\n", unit);
+ goto fail;
+ }
+
+ return true;
+
+ fail:
+ return false;
+}
+
+/*
+ * Initialize brcms_c_info default values ...
+ * may get overrides later in this function
+ */
+static void brcms_c_info_init(struct brcms_c_info *wlc, int unit)
+{
+ int i;
+
+ /* Save our copy of the chanspec */
+ wlc->chanspec = ch20mhz_chspec(1);
+
+ /* various 802.11g modes */
+ wlc->shortslot = false;
+ wlc->shortslot_override = BRCMS_SHORTSLOT_AUTO;
+
+ brcms_c_protection_upd(wlc, BRCMS_PROT_G_OVR, BRCMS_PROTECTION_AUTO);
+ brcms_c_protection_upd(wlc, BRCMS_PROT_G_SPEC, false);
+
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG_OVR,
+ BRCMS_PROTECTION_AUTO);
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG, BRCMS_N_PROTECTION_OFF);
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF_OVR,
+ BRCMS_PROTECTION_AUTO);
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF, false);
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, AUTO);
+
+ brcms_c_protection_upd(wlc, BRCMS_PROT_OVERLAP,
+ BRCMS_PROTECTION_CTL_OVERLAP);
+
+ /* 802.11g draft 4.0 NonERP elt advertisement */
+ wlc->include_legacy_erp = true;
+
+ wlc->stf->ant_rx_ovr = ANT_RX_DIV_DEF;
+ wlc->stf->txant = ANT_TX_DEF;
+
+ wlc->prb_resp_timeout = BRCMS_PRB_RESP_TIMEOUT;
+
+ wlc->usr_fragthresh = DOT11_DEFAULT_FRAG_LEN;
+ for (i = 0; i < NFIFO; i++)
+ wlc->fragthresh[i] = DOT11_DEFAULT_FRAG_LEN;
+ wlc->RTSThresh = DOT11_DEFAULT_RTS_LEN;
+
+ /* default rate fallback retry limits */
+ wlc->SFBL = RETRY_SHORT_FB;
+ wlc->LFBL = RETRY_LONG_FB;
+
+ /* default mac retry limits */
+ wlc->SRL = RETRY_SHORT_DEF;
+ wlc->LRL = RETRY_LONG_DEF;
+
+ /* WME QoS mode is Auto by default */
+ wlc->pub->_ampdu = AMPDU_AGG_HOST;
+ wlc->pub->bcmerror = 0;
+
+ /* initialize mpc delay */
+ wlc->mpc_delay_off = wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT;
+}
+
+static uint brcms_c_attach_module(struct brcms_c_info *wlc)
+{
+ uint err = 0;
+ uint unit;
+ unit = wlc->pub->unit;
+
+ wlc->asi = brcms_c_antsel_attach(wlc);
+ if (wlc->asi == NULL) {
+ wiphy_err(wlc->wiphy, "wl%d: attach: antsel_attach "
+ "failed\n", unit);
+ err = 44;
+ goto fail;
+ }
+
+ wlc->ampdu = brcms_c_ampdu_attach(wlc);
+ if (wlc->ampdu == NULL) {
+ wiphy_err(wlc->wiphy, "wl%d: attach: ampdu_attach "
+ "failed\n", unit);
+ err = 50;
+ goto fail;
+ }
+
+ if ((brcms_c_stf_attach(wlc) != 0)) {
+ wiphy_err(wlc->wiphy, "wl%d: attach: stf_attach "
+ "failed\n", unit);
+ err = 68;
+ goto fail;
+ }
+ fail:
+ return err;
+}
+
+struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc)
+{
+ return wlc->pub;
+}
+
+/* low level attach
+ * run backplane attach, init nvram
+ * run phy attach
+ * initialize software state for each core and band
+ * put the whole chip in reset(driver down state), no clock
+ */
+static int brcms_b_attach(struct brcms_c_info *wlc, u16 vendor, u16 device,
+ uint unit, bool piomode, void __iomem *regsva,
+ struct pci_dev *btparam)
+{
+ struct brcms_hardware *wlc_hw;
+ struct d11regs __iomem *regs;
+ char *macaddr = NULL;
+ uint err = 0;
+ uint j;
+ bool wme = false;
+ struct shared_phy_params sha_params;
+ struct wiphy *wiphy = wlc->wiphy;
+
+ BCMMSG(wlc->wiphy, "wl%d: vendor 0x%x device 0x%x\n", unit, vendor,
+ device);
+
+ wme = true;
+
+ wlc_hw = wlc->hw;
+ wlc_hw->wlc = wlc;
+ wlc_hw->unit = unit;
+ wlc_hw->band = wlc_hw->bandstate[0];
+ wlc_hw->_piomode = piomode;
+
+ /* populate struct brcms_hardware with default values */
+ brcms_b_info_init(wlc_hw);
+
+ /*
+ * Do the hardware portion of the attach. Also initialize software
+ * state that depends on the particular hardware we are running.
+ */
+ wlc_hw->sih = ai_attach(regsva, btparam);
+ if (wlc_hw->sih == NULL) {
+ wiphy_err(wiphy, "wl%d: brcms_b_attach: si_attach failed\n",
+ unit);
+ err = 11;
+ goto fail;
+ }
+
+ /* verify again the device is supported */
+ if (!brcms_c_chipmatch(vendor, device)) {
+ wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported "
+ "vendor/device (0x%x/0x%x)\n",
+ unit, vendor, device);
+ err = 12;
+ goto fail;
+ }
+
+ wlc_hw->vendorid = vendor;
+ wlc_hw->deviceid = device;
+
+ /* set bar0 window to point at D11 core */
+ wlc_hw->regs = (struct d11regs __iomem *)
+ ai_setcore(wlc_hw->sih, D11_CORE_ID, 0);
+ wlc_hw->corerev = ai_corerev(wlc_hw->sih);
+
+ regs = wlc_hw->regs;
+
+ wlc->regs = wlc_hw->regs;
+
+ /* validate chip, chiprev and corerev */
+ if (!brcms_c_isgoodchip(wlc_hw)) {
+ err = 13;
+ goto fail;
+ }
+
+ /* initialize power control registers */
+ ai_clkctl_init(wlc_hw->sih);
+
+ /* request fastclock and force fastclock for the rest of attach
+ * bring the d11 core out of reset.
+ * For PMU chips, the first wlc_clkctl_clk is no-op since core-clk
+ * is still false; But it will be called again inside wlc_corereset,
+ * after d11 is out of reset.
+ */
+ brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+ brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS);
+
+ if (!brcms_b_validate_chip_access(wlc_hw)) {
+ wiphy_err(wiphy, "wl%d: brcms_b_attach: validate_chip_access "
+ "failed\n", unit);
+ err = 14;
+ goto fail;
+ }
+
+ /* get the board rev, used just below */
+ j = getintvar(wlc_hw->sih, BRCMS_SROM_BOARDREV);
+ /* promote srom boardrev of 0xFF to 1 */
+ if (j == BOARDREV_PROMOTABLE)
+ j = BOARDREV_PROMOTED;
+ wlc_hw->boardrev = (u16) j;
+ if (!brcms_c_validboardtype(wlc_hw)) {
+ wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported Broadcom "
+ "board type (0x%x)" " or revision level (0x%x)\n",
+ unit, wlc_hw->sih->boardtype, wlc_hw->boardrev);
+ err = 15;
+ goto fail;
+ }
+ wlc_hw->sromrev = (u8) getintvar(wlc_hw->sih, BRCMS_SROM_REV);
+ wlc_hw->boardflags = (u32) getintvar(wlc_hw->sih,
+ BRCMS_SROM_BOARDFLAGS);
+ wlc_hw->boardflags2 = (u32) getintvar(wlc_hw->sih,
+ BRCMS_SROM_BOARDFLAGS2);
+
+ if (wlc_hw->boardflags & BFL_NOPLLDOWN)
+ brcms_b_pllreq(wlc_hw, true, BRCMS_PLLREQ_SHARED);
+
+ /* check device id(srom, nvram etc.) to set bands */
+ if (wlc_hw->deviceid == BCM43224_D11N_ID ||
+ wlc_hw->deviceid == BCM43224_D11N_ID_VEN1)
+ /* Dualband boards */
+ wlc_hw->_nbands = 2;
+ else
+ wlc_hw->_nbands = 1;
+
+ if ((wlc_hw->sih->chip == BCM43225_CHIP_ID))
+ wlc_hw->_nbands = 1;
+
+ /* BMAC_NOTE: remove init of pub values when brcms_c_attach()
+ * unconditionally does the init of these values
+ */
+ wlc->vendorid = wlc_hw->vendorid;
+ wlc->deviceid = wlc_hw->deviceid;
+ wlc->pub->sih = wlc_hw->sih;
+ wlc->pub->corerev = wlc_hw->corerev;
+ wlc->pub->sromrev = wlc_hw->sromrev;
+ wlc->pub->boardrev = wlc_hw->boardrev;
+ wlc->pub->boardflags = wlc_hw->boardflags;
+ wlc->pub->boardflags2 = wlc_hw->boardflags2;
+ wlc->pub->_nbands = wlc_hw->_nbands;
+
+ wlc_hw->physhim = wlc_phy_shim_attach(wlc_hw, wlc->wl, wlc);
+
+ if (wlc_hw->physhim == NULL) {
+ wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_shim_attach "
+ "failed\n", unit);
+ err = 25;
+ goto fail;
+ }
+
+ /* pass all the parameters to wlc_phy_shared_attach in one struct */
+ sha_params.sih = wlc_hw->sih;
+ sha_params.physhim = wlc_hw->physhim;
+ sha_params.unit = unit;
+ sha_params.corerev = wlc_hw->corerev;
+ sha_params.vid = wlc_hw->vendorid;
+ sha_params.did = wlc_hw->deviceid;
+ sha_params.chip = wlc_hw->sih->chip;
+ sha_params.chiprev = wlc_hw->sih->chiprev;
+ sha_params.chippkg = wlc_hw->sih->chippkg;
+ sha_params.sromrev = wlc_hw->sromrev;
+ sha_params.boardtype = wlc_hw->sih->boardtype;
+ sha_params.boardrev = wlc_hw->boardrev;
+ sha_params.boardvendor = wlc_hw->sih->boardvendor;
+ sha_params.boardflags = wlc_hw->boardflags;
+ sha_params.boardflags2 = wlc_hw->boardflags2;
+ sha_params.buscorerev = wlc_hw->sih->buscorerev;
+
+ /* alloc and save pointer to shared phy state area */
+ wlc_hw->phy_sh = wlc_phy_shared_attach(&sha_params);
+ if (!wlc_hw->phy_sh) {
+ err = 16;
+ goto fail;
+ }
+
+ /* initialize software state for each core and band */
+ for (j = 0; j < wlc_hw->_nbands; j++) {
+ /*
+ * band0 is always 2.4Ghz
+ * band1, if present, is 5Ghz
+ */
+
+ brcms_c_setxband(wlc_hw, j);
+
+ wlc_hw->band->bandunit = j;
+ wlc_hw->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G;
+ wlc->band->bandunit = j;
+ wlc->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G;
+ wlc->core->coreidx = ai_coreidx(wlc_hw->sih);
+
+ wlc_hw->machwcap = R_REG(&regs->machwcap);
+ wlc_hw->machwcap_backup = wlc_hw->machwcap;
+
+ /* init tx fifo size */
+ wlc_hw->xmtfifo_sz =
+ xmtfifo_sz[(wlc_hw->corerev - XMTFIFOTBL_STARTREV)];
+
+ /* Get a phy for this band */
+ wlc_hw->band->pi =
+ wlc_phy_attach(wlc_hw->phy_sh, regs,
+ wlc_hw->band->bandtype,
+ wlc->wiphy);
+ if (wlc_hw->band->pi == NULL) {
+ wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_"
+ "attach failed\n", unit);
+ err = 17;
+ goto fail;
+ }
+
+ wlc_phy_machwcap_set(wlc_hw->band->pi, wlc_hw->machwcap);
+
+ wlc_phy_get_phyversion(wlc_hw->band->pi, &wlc_hw->band->phytype,
+ &wlc_hw->band->phyrev,
+ &wlc_hw->band->radioid,
+ &wlc_hw->band->radiorev);
+ wlc_hw->band->abgphy_encore =
+ wlc_phy_get_encore(wlc_hw->band->pi);
+ wlc->band->abgphy_encore = wlc_phy_get_encore(wlc_hw->band->pi);
+ wlc_hw->band->core_flags =
+ wlc_phy_get_coreflags(wlc_hw->band->pi);
+
+ /* verify good phy_type & supported phy revision */
+ if (BRCMS_ISNPHY(wlc_hw->band)) {
+ if (NCONF_HAS(wlc_hw->band->phyrev))
+ goto good_phy;
+ else
+ goto bad_phy;
+ } else if (BRCMS_ISLCNPHY(wlc_hw->band)) {
+ if (LCNCONF_HAS(wlc_hw->band->phyrev))
+ goto good_phy;
+ else
+ goto bad_phy;
+ } else {
+ bad_phy:
+ wiphy_err(wiphy, "wl%d: brcms_b_attach: unsupported "
+ "phy type/rev (%d/%d)\n", unit,
+ wlc_hw->band->phytype, wlc_hw->band->phyrev);
+ err = 18;
+ goto fail;
+ }
+
+ good_phy:
+ /*
+ * BMAC_NOTE: wlc->band->pi should not be set below and should
+ * be done in the high level attach. However we can not make
+ * that change until all low level access is changed to
+ * wlc_hw->band->pi. Instead do the wlc->band->pi init below,
+ * keeping wlc_hw->band->pi as well for incremental update of
+ * low level fns, and cut over low only init when all fns
+ * updated.
+ */
+ wlc->band->pi = wlc_hw->band->pi;
+ wlc->band->phytype = wlc_hw->band->phytype;
+ wlc->band->phyrev = wlc_hw->band->phyrev;
+ wlc->band->radioid = wlc_hw->band->radioid;
+ wlc->band->radiorev = wlc_hw->band->radiorev;
+
+ /* default contention windows size limits */
+ wlc_hw->band->CWmin = APHY_CWMIN;
+ wlc_hw->band->CWmax = PHY_CWMAX;
+
+ if (!brcms_b_attach_dmapio(wlc, j, wme)) {
+ err = 19;
+ goto fail;
+ }
+ }
+
+ /* disable core to match driver "down" state */
+ brcms_c_coredisable(wlc_hw);
+
+ /* Match driver "down" state */
+ ai_pci_down(wlc_hw->sih);
+
+ /* register sb interrupt callback functions */
+ ai_register_intr_callback(wlc_hw->sih, (void *)brcms_c_wlintrsoff,
+ (void *)brcms_c_wlintrsrestore, NULL, wlc);
+
+ /* turn off pll and xtal to match driver "down" state */
+ brcms_b_xtal(wlc_hw, OFF);
+
+ /* *******************************************************************
+ * The hardware is in the DOWN state at this point. D11 core
+ * or cores are in reset with clocks off, and the board PLLs
+ * are off if possible.
+ *
+ * Beyond this point, wlc->sbclk == false and chip registers
+ * should not be touched.
+ *********************************************************************
+ */
+
+ /* init etheraddr state variables */
+ macaddr = brcms_c_get_macaddr(wlc_hw);
+ if (macaddr == NULL) {
+ wiphy_err(wiphy, "wl%d: brcms_b_attach: macaddr not found\n",
+ unit);
+ err = 21;
+ goto fail;
+ }
+ if (!mac_pton(macaddr, wlc_hw->etheraddr) ||
+ is_broadcast_ether_addr(wlc_hw->etheraddr) ||
+ is_zero_ether_addr(wlc_hw->etheraddr)) {
+ wiphy_err(wiphy, "wl%d: brcms_b_attach: bad macaddr %s\n",
+ unit, macaddr);
+ err = 22;
+ goto fail;
+ }
+
+ BCMMSG(wlc->wiphy,
+ "deviceid 0x%x nbands %d board 0x%x macaddr: %s\n",
+ wlc_hw->deviceid, wlc_hw->_nbands,
+ wlc_hw->sih->boardtype, macaddr);
+
+ return err;
+
+ fail:
+ wiphy_err(wiphy, "wl%d: brcms_b_attach: failed with err %d\n", unit,
+ err);
+ return err;
+}
+
+static void brcms_c_attach_antgain_init(struct brcms_c_info *wlc)
+{
+ uint unit;
+ unit = wlc->pub->unit;
+
+ if ((wlc->band->antgain == -1) && (wlc->pub->sromrev == 1)) {
+ /* default antenna gain for srom rev 1 is 2 dBm (8 qdbm) */
+ wlc->band->antgain = 8;
+ } else if (wlc->band->antgain == -1) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in"
+ " srom, using 2dB\n", unit, __func__);
+ wlc->band->antgain = 8;
+ } else {
+ s8 gain, fract;
+ /* Older sroms specified gain in whole dbm only. In order
+ * be able to specify qdbm granularity and remain backward
+ * compatible the whole dbms are now encoded in only
+ * low 6 bits and remaining qdbms are encoded in the hi 2 bits.
+ * 6 bit signed number ranges from -32 - 31.
+ *
+ * Examples:
+ * 0x1 = 1 db,
+ * 0xc1 = 1.75 db (1 + 3 quarters),
+ * 0x3f = -1 (-1 + 0 quarters),
+ * 0x7f = -.75 (-1 + 1 quarters) = -3 qdbm.
+ * 0xbf = -.50 (-1 + 2 quarters) = -2 qdbm.
+ */
+ gain = wlc->band->antgain & 0x3f;
+ gain <<= 2; /* Sign extend */
+ gain >>= 2;
+ fract = (wlc->band->antgain & 0xc0) >> 6;
+ wlc->band->antgain = 4 * gain + fract;
+ }
+}
+
+static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc)
+{
+ int aa;
+ uint unit;
+ int bandtype;
+ struct si_pub *sih = wlc->hw->sih;
+
+ unit = wlc->pub->unit;
+ bandtype = wlc->band->bandtype;
+
+ /* get antennas available */
+ if (bandtype == BRCM_BAND_5G)
+ aa = (s8) getintvar(sih, BRCMS_SROM_AA5G);
+ else
+ aa = (s8) getintvar(sih, BRCMS_SROM_AA2G);
+
+ if ((aa < 1) || (aa > 15)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in"
+ " srom (0x%x), using 3\n", unit, __func__, aa);
+ aa = 3;
+ }
+
+ /* reset the defaults if we have a single antenna */
+ if (aa == 1) {
+ wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_0;
+ wlc->stf->txant = ANT_TX_FORCE_0;
+ } else if (aa == 2) {
+ wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_1;
+ wlc->stf->txant = ANT_TX_FORCE_1;
+ } else {
+ }
+
+ /* Compute Antenna Gain */
+ if (bandtype == BRCM_BAND_5G)
+ wlc->band->antgain = (s8) getintvar(sih, BRCMS_SROM_AG1);
+ else
+ wlc->band->antgain = (s8) getintvar(sih, BRCMS_SROM_AG0);
+
+ brcms_c_attach_antgain_init(wlc);
+
+ return true;
+}
+
+static void brcms_c_bss_default_init(struct brcms_c_info *wlc)
+{
+ u16 chanspec;
+ struct brcms_band *band;
+ struct brcms_bss_info *bi = wlc->default_bss;
+
+ /* init default and target BSS with some sane initial values */
+ memset((char *)(bi), 0, sizeof(struct brcms_bss_info));
+ bi->beacon_period = BEACON_INTERVAL_DEFAULT;
+
+ /* fill the default channel as the first valid channel
+ * starting from the 2G channels
+ */
+ chanspec = ch20mhz_chspec(1);
+ wlc->home_chanspec = bi->chanspec = chanspec;
+
+ /* find the band of our default channel */
+ band = wlc->band;
+ if (wlc->pub->_nbands > 1 &&
+ band->bandunit != chspec_bandunit(chanspec))
+ band = wlc->bandstate[OTHERBANDUNIT(wlc)];
+
+ /* init bss rates to the band specific default rate set */
+ brcms_c_rateset_default(&bi->rateset, NULL, band->phytype,
+ band->bandtype, false, BRCMS_RATE_MASK_FULL,
+ (bool) (wlc->pub->_n_enab & SUPPORT_11N),
+ brcms_chspec_bw(chanspec), wlc->stf->txstreams);
+
+ if (wlc->pub->_n_enab & SUPPORT_11N)
+ bi->flags |= BRCMS_BSS_HT;
+}
+
+static struct brcms_txq_info *brcms_c_txq_alloc(struct brcms_c_info *wlc)
+{
+ struct brcms_txq_info *qi, *p;
+
+ qi = kzalloc(sizeof(struct brcms_txq_info), GFP_ATOMIC);
+ if (qi != NULL) {
+ /*
+ * Have enough room for control packets along with HI watermark
+ * Also, add room to txq for total psq packets if all the SCBs
+ * leave PS mode. The watermark for flowcontrol to OS packets
+ * will remain the same
+ */
+ brcmu_pktq_init(&qi->q, BRCMS_PREC_COUNT,
+ 2 * BRCMS_DATAHIWAT + PKTQ_LEN_DEFAULT);
+
+ /* add this queue to the the global list */
+ p = wlc->tx_queues;
+ if (p == NULL) {
+ wlc->tx_queues = qi;
+ } else {
+ while (p->next != NULL)
+ p = p->next;
+ p->next = qi;
+ }
+ }
+ return qi;
+}
+
+static void brcms_c_txq_free(struct brcms_c_info *wlc,
+ struct brcms_txq_info *qi)
+{
+ struct brcms_txq_info *p;
+
+ if (qi == NULL)
+ return;
+
+ /* remove the queue from the linked list */
+ p = wlc->tx_queues;
+ if (p == qi)
+ wlc->tx_queues = p->next;
+ else {
+ while (p != NULL && p->next != qi)
+ p = p->next;
+ if (p != NULL)
+ p->next = p->next->next;
+ }
+
+ kfree(qi);
+}
+
+static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap)
+{
+ uint i;
+ struct brcms_band *band;
+
+ for (i = 0; i < wlc->pub->_nbands; i++) {
+ band = wlc->bandstate[i];
+ if (band->bandtype == BRCM_BAND_5G) {
+ if ((bwcap == BRCMS_N_BW_40ALL)
+ || (bwcap == BRCMS_N_BW_20IN2G_40IN5G))
+ band->mimo_cap_40 = true;
+ else
+ band->mimo_cap_40 = false;
+ } else {
+ if (bwcap == BRCMS_N_BW_40ALL)
+ band->mimo_cap_40 = true;
+ else
+ band->mimo_cap_40 = false;
+ }
+ }
+}
+
+static void brcms_c_timers_deinit(struct brcms_c_info *wlc)
+{
+ /* free timer state */
+ if (wlc->wdtimer) {
+ brcms_free_timer(wlc->wdtimer);
+ wlc->wdtimer = NULL;
+ }
+ if (wlc->radio_timer) {
+ brcms_free_timer(wlc->radio_timer);
+ wlc->radio_timer = NULL;
+ }
+}
+
+static void brcms_c_detach_module(struct brcms_c_info *wlc)
+{
+ if (wlc->asi) {
+ brcms_c_antsel_detach(wlc->asi);
+ wlc->asi = NULL;
+ }
+
+ if (wlc->ampdu) {
+ brcms_c_ampdu_detach(wlc->ampdu);
+ wlc->ampdu = NULL;
+ }
+
+ brcms_c_stf_detach(wlc);
+}
+
+/*
+ * low level detach
+ */
+static int brcms_b_detach(struct brcms_c_info *wlc)
+{
+ uint i;
+ struct brcms_hw_band *band;
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ int callbacks;
+
+ callbacks = 0;
+
+ if (wlc_hw->sih) {
+ /*
+ * detach interrupt sync mechanism since interrupt is disabled
+ * and per-port interrupt object may has been freed. this must
+ * be done before sb core switch
+ */
+ ai_deregister_intr_callback(wlc_hw->sih);
+ ai_pci_sleep(wlc_hw->sih);
+ }
+
+ brcms_b_detach_dmapio(wlc_hw);
+
+ band = wlc_hw->band;
+ for (i = 0; i < wlc_hw->_nbands; i++) {
+ if (band->pi) {
+ /* Detach this band's phy */
+ wlc_phy_detach(band->pi);
+ band->pi = NULL;
+ }
+ band = wlc_hw->bandstate[OTHERBANDUNIT(wlc)];
+ }
+
+ /* Free shared phy state */
+ kfree(wlc_hw->phy_sh);
+
+ wlc_phy_shim_detach(wlc_hw->physhim);
+
+ if (wlc_hw->sih) {
+ ai_detach(wlc_hw->sih);
+ wlc_hw->sih = NULL;
+ }
+
+ return callbacks;
+
+}
+
+/*
+ * Return a count of the number of driver callbacks still pending.
+ *
+ * General policy is that brcms_c_detach can only dealloc/free software states.
+ * It can NOT touch hardware registers since the d11core may be in reset and
+ * clock may not be available.
+ * One exception is sb register access, which is possible if crystal is turned
+ * on after "down" state, driver should avoid software timer with the exception
+ * of radio_monitor.
+ */
+uint brcms_c_detach(struct brcms_c_info *wlc)
+{
+ uint callbacks = 0;
+
+ if (wlc == NULL)
+ return 0;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+ callbacks += brcms_b_detach(wlc);
+
+ /* delete software timers */
+ if (!brcms_c_radio_monitor_stop(wlc))
+ callbacks++;
+
+ brcms_c_channel_mgr_detach(wlc->cmi);
+
+ brcms_c_timers_deinit(wlc);
+
+ brcms_c_detach_module(wlc);
+
+
+ while (wlc->tx_queues != NULL)
+ brcms_c_txq_free(wlc, wlc->tx_queues);
+
+ brcms_c_detach_mfree(wlc);
+ return callbacks;
+}
+
+/* update state that depends on the current value of "ap" */
+static void brcms_c_ap_upd(struct brcms_c_info *wlc)
+{
+ /* STA-BSS; short capable */
+ wlc->PLCPHdr_override = BRCMS_PLCP_SHORT;
+
+ /* fixup mpc */
+ wlc->mpc = true;
+}
+
+/* Initialize just the hardware when coming out of POR or S3/S5 system states */
+static void brcms_b_hw_up(struct brcms_hardware *wlc_hw)
+{
+ if (wlc_hw->wlc->pub->hw_up)
+ return;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ /*
+ * Enable pll and xtal, initialize the power control registers,
+ * and force fastclock for the remainder of brcms_c_up().
+ */
+ brcms_b_xtal(wlc_hw, ON);
+ ai_clkctl_init(wlc_hw->sih);
+ brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+ ai_pci_fixcfg(wlc_hw->sih);
+
+ /*
+ * AI chip doesn't restore bar0win2 on
+ * hibernation/resume, need sw fixup
+ */
+ if ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
+ (wlc_hw->sih->chip == BCM43225_CHIP_ID))
+ wlc_hw->regs = (struct d11regs __iomem *)
+ ai_setcore(wlc_hw->sih, D11_CORE_ID, 0);
+
+ /*
+ * Inform phy that a POR reset has occurred so
+ * it does a complete phy init
+ */
+ wlc_phy_por_inform(wlc_hw->band->pi);
+
+ wlc_hw->ucode_loaded = false;
+ wlc_hw->wlc->pub->hw_up = true;
+
+ if ((wlc_hw->boardflags & BFL_FEM)
+ && (wlc_hw->sih->chip == BCM4313_CHIP_ID)) {
+ if (!
+ (wlc_hw->boardrev >= 0x1250
+ && (wlc_hw->boardflags & BFL_FEM_BT)))
+ ai_epa_4313war(wlc_hw->sih);
+ }
+}
+
+static int brcms_b_up_prep(struct brcms_hardware *wlc_hw)
+{
+ uint coremask;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ /*
+ * Enable pll and xtal, initialize the power control registers,
+ * and force fastclock for the remainder of brcms_c_up().
+ */
+ brcms_b_xtal(wlc_hw, ON);
+ ai_clkctl_init(wlc_hw->sih);
+ brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+ /*
+ * Configure pci/pcmcia here instead of in brcms_c_attach()
+ * to allow mfg hotswap: down, hotswap (chip power cycle), up.
+ */
+ coremask = (1 << wlc_hw->wlc->core->coreidx);
+
+ ai_pci_setup(wlc_hw->sih, coremask);
+
+ /*
+ * Need to read the hwradio status here to cover the case where the
+ * system is loaded with the hw radio disabled. We do not want to
+ * bring the driver up in this case.
+ */
+ if (brcms_b_radio_read_hwdisabled(wlc_hw)) {
+ /* put SB PCI in down state again */
+ ai_pci_down(wlc_hw->sih);
+ brcms_b_xtal(wlc_hw, OFF);
+ return -ENOMEDIUM;
+ }
+
+ ai_pci_up(wlc_hw->sih);
+
+ /* reset the d11 core */
+ brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS);
+
+ return 0;
+}
+
+static int brcms_b_up_finish(struct brcms_hardware *wlc_hw)
+{
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ wlc_hw->up = true;
+ wlc_phy_hw_state_upd(wlc_hw->band->pi, true);
+
+ /* FULLY enable dynamic power control and d11 core interrupt */
+ brcms_b_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+ brcms_intrson(wlc_hw->wlc->wl);
+ return 0;
+}
+
+/*
+ * Write WME tunable parameters for retransmit/max rate
+ * from wlc struct to ucode
+ */
+static void brcms_c_wme_retries_write(struct brcms_c_info *wlc)
+{
+ int ac;
+
+ /* Need clock to do this */
+ if (!wlc->clk)
+ return;
+
+ for (ac = 0; ac < AC_COUNT; ac++)
+ brcms_b_write_shm(wlc->hw, M_AC_TXLMT_ADDR(ac),
+ wlc->wme_retries[ac]);
+}
+
+/* make interface operational */
+int brcms_c_up(struct brcms_c_info *wlc)
+{
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+ /* HW is turned off so don't try to access it */
+ if (wlc->pub->hw_off || brcms_deviceremoved(wlc))
+ return -ENOMEDIUM;
+
+ if (!wlc->pub->hw_up) {
+ brcms_b_hw_up(wlc->hw);
+ wlc->pub->hw_up = true;
+ }
+
+ if ((wlc->pub->boardflags & BFL_FEM)
+ && (wlc->pub->sih->chip == BCM4313_CHIP_ID)) {
+ if (wlc->pub->boardrev >= 0x1250
+ && (wlc->pub->boardflags & BFL_FEM_BT))
+ brcms_b_mhf(wlc->hw, MHF5, MHF5_4313_GPIOCTRL,
+ MHF5_4313_GPIOCTRL, BRCM_BAND_ALL);
+ else
+ brcms_b_mhf(wlc->hw, MHF4, MHF4_EXTPA_ENABLE,
+ MHF4_EXTPA_ENABLE, BRCM_BAND_ALL);
+ }
+
+ /*
+ * Need to read the hwradio status here to cover the case where the
+ * system is loaded with the hw radio disabled. We do not want to bring
+ * the driver up in this case. If radio is disabled, abort up, lower
+ * power, start radio timer and return 0(for NDIS) don't call
+ * radio_update to avoid looping brcms_c_up.
+ *
+ * brcms_b_up_prep() returns either 0 or -BCME_RADIOOFF only
+ */
+ if (!wlc->pub->radio_disabled) {
+ int status = brcms_b_up_prep(wlc->hw);
+ if (status == -ENOMEDIUM) {
+ if (!mboolisset
+ (wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE)) {
+ struct brcms_bss_cfg *bsscfg = wlc->bsscfg;
+ mboolset(wlc->pub->radio_disabled,
+ WL_RADIO_HW_DISABLE);
+
+ if (bsscfg->enable && bsscfg->BSS)
+ wiphy_err(wlc->wiphy, "wl%d: up"
+ ": rfdisable -> "
+ "bsscfg_disable()\n",
+ wlc->pub->unit);
+ }
+ }
+ }
+
+ if (wlc->pub->radio_disabled) {
+ brcms_c_radio_monitor_start(wlc);
+ return 0;
+ }
+
+ /* brcms_b_up_prep has done brcms_c_corereset(). so clk is on, set it */
+ wlc->clk = true;
+
+ brcms_c_radio_monitor_stop(wlc);
+
+ /* Set EDCF hostflags */
+ brcms_b_mhf(wlc->hw, MHF1, MHF1_EDCF, MHF1_EDCF, BRCM_BAND_ALL);
+
+ brcms_init(wlc->wl);
+ wlc->pub->up = true;
+
+ if (wlc->bandinit_pending) {
+ brcms_c_suspend_mac_and_wait(wlc);
+ brcms_c_set_chanspec(wlc, wlc->default_bss->chanspec);
+ wlc->bandinit_pending = false;
+ brcms_c_enable_mac(wlc);
+ }
+
+ brcms_b_up_finish(wlc->hw);
+
+ /* Program the TX wme params with the current settings */
+ brcms_c_wme_retries_write(wlc);
+
+ /* start one second watchdog timer */
+ brcms_add_timer(wlc->wdtimer, TIMER_INTERVAL_WATCHDOG, true);
+ wlc->WDarmed = true;
+
+ /* ensure antenna config is up to date */
+ brcms_c_stf_phy_txant_upd(wlc);
+ /* ensure LDPC config is in sync */
+ brcms_c_ht_update_ldpc(wlc, wlc->stf->ldpc);
+
+ return 0;
+}
+
+static uint brcms_c_down_del_timer(struct brcms_c_info *wlc)
+{
+ uint callbacks = 0;
+
+ return callbacks;
+}
+
+static int brcms_b_bmac_down_prep(struct brcms_hardware *wlc_hw)
+{
+ bool dev_gone;
+ uint callbacks = 0;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ if (!wlc_hw->up)
+ return callbacks;
+
+ dev_gone = brcms_deviceremoved(wlc_hw->wlc);
+
+ /* disable interrupts */
+ if (dev_gone)
+ wlc_hw->wlc->macintmask = 0;
+ else {
+ /* now disable interrupts */
+ brcms_intrsoff(wlc_hw->wlc->wl);
+
+ /* ensure we're running on the pll clock again */
+ brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+ }
+ /* down phy at the last of this stage */
+ callbacks += wlc_phy_down(wlc_hw->band->pi);
+
+ return callbacks;
+}
+
+static int brcms_b_down_finish(struct brcms_hardware *wlc_hw)
+{
+ uint callbacks = 0;
+ bool dev_gone;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ if (!wlc_hw->up)
+ return callbacks;
+
+ wlc_hw->up = false;
+ wlc_phy_hw_state_upd(wlc_hw->band->pi, false);
+
+ dev_gone = brcms_deviceremoved(wlc_hw->wlc);
+
+ if (dev_gone) {
+ wlc_hw->sbclk = false;
+ wlc_hw->clk = false;
+ wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
+
+ /* reclaim any posted packets */
+ brcms_c_flushqueues(wlc_hw->wlc);
+ } else {
+
+ /* Reset and disable the core */
+ if (ai_iscoreup(wlc_hw->sih)) {
+ if (R_REG(&wlc_hw->regs->maccontrol) &
+ MCTL_EN_MAC)
+ brcms_c_suspend_mac_and_wait(wlc_hw->wlc);
+ callbacks += brcms_reset(wlc_hw->wlc->wl);
+ brcms_c_coredisable(wlc_hw);
+ }
+
+ /* turn off primary xtal and pll */
+ if (!wlc_hw->noreset) {
+ ai_pci_down(wlc_hw->sih);
+ brcms_b_xtal(wlc_hw, OFF);
+ }
+ }
+
+ return callbacks;
+}
+
+/*
+ * Mark the interface nonoperational, stop the software mechanisms,
+ * disable the hardware, free any transient buffer state.
+ * Return a count of the number of driver callbacks still pending.
+ */
+uint brcms_c_down(struct brcms_c_info *wlc)
+{
+
+ uint callbacks = 0;
+ int i;
+ bool dev_gone = false;
+ struct brcms_txq_info *qi;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+ /* check if we are already in the going down path */
+ if (wlc->going_down) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Driver going down so return"
+ "\n", wlc->pub->unit, __func__);
+ return 0;
+ }
+ if (!wlc->pub->up)
+ return callbacks;
+
+ /* in between, mpc could try to bring down again.. */
+ wlc->going_down = true;
+
+ callbacks += brcms_b_bmac_down_prep(wlc->hw);
+
+ dev_gone = brcms_deviceremoved(wlc);
+
+ /* Call any registered down handlers */
+ for (i = 0; i < BRCMS_MAXMODULES; i++) {
+ if (wlc->modulecb[i].down_fn)
+ callbacks +=
+ wlc->modulecb[i].down_fn(wlc->modulecb[i].hdl);
+ }
+
+ /* cancel the watchdog timer */
+ if (wlc->WDarmed) {
+ if (!brcms_del_timer(wlc->wdtimer))
+ callbacks++;
+ wlc->WDarmed = false;
+ }
+ /* cancel all other timers */
+ callbacks += brcms_c_down_del_timer(wlc);
+
+ wlc->pub->up = false;
+
+ wlc_phy_mute_upd(wlc->band->pi, false, PHY_MUTE_ALL);
+
+ /* clear txq flow control */
+ brcms_c_txflowcontrol_reset(wlc);
+
+ /* flush tx queues */
+ for (qi = wlc->tx_queues; qi != NULL; qi = qi->next)
+ brcmu_pktq_flush(&qi->q, true, NULL, NULL);
+
+ callbacks += brcms_b_down_finish(wlc->hw);
+
+ /* brcms_b_down_finish has done brcms_c_coredisable(). so clk is off */
+ wlc->clk = false;
+
+ wlc->going_down = false;
+ return callbacks;
+}
+
+/* Set the current gmode configuration */
+int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config)
+{
+ int ret = 0;
+ uint i;
+ struct brcms_c_rateset rs;
+ /* Default to 54g Auto */
+ /* Advertise and use shortslot (-1/0/1 Auto/Off/On) */
+ s8 shortslot = BRCMS_SHORTSLOT_AUTO;
+ bool shortslot_restrict = false; /* Restrict association to stations
+ * that support shortslot
+ */
+ bool ofdm_basic = false; /* Make 6, 12, and 24 basic rates */
+ /* Advertise and use short preambles (-1/0/1 Auto/Off/On) */
+ int preamble = BRCMS_PLCP_LONG;
+ bool preamble_restrict = false; /* Restrict association to stations
+ * that support short preambles
+ */
+ struct brcms_band *band;
+
+ /* if N-support is enabled, allow Gmode set as long as requested
+ * Gmode is not GMODE_LEGACY_B
+ */
+ if ((wlc->pub->_n_enab & SUPPORT_11N) && gmode == GMODE_LEGACY_B)
+ return -ENOTSUPP;
+
+ /* verify that we are dealing with 2G band and grab the band pointer */
+ if (wlc->band->bandtype == BRCM_BAND_2G)
+ band = wlc->band;
+ else if ((wlc->pub->_nbands > 1) &&
+ (wlc->bandstate[OTHERBANDUNIT(wlc)]->bandtype == BRCM_BAND_2G))
+ band = wlc->bandstate[OTHERBANDUNIT(wlc)];
+ else
+ return -EINVAL;
+
+ /* Legacy or bust when no OFDM is supported by regulatory */
+ if ((brcms_c_channel_locale_flags_in_band(wlc->cmi, band->bandunit) &
+ BRCMS_NO_OFDM) && (gmode != GMODE_LEGACY_B))
+ return -EINVAL;
+
+ /* update configuration value */
+ if (config == true)
+ brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, gmode);
+
+ /* Clear rateset override */
+ memset(&rs, 0, sizeof(struct brcms_c_rateset));
+
+ switch (gmode) {
+ case GMODE_LEGACY_B:
+ shortslot = BRCMS_SHORTSLOT_OFF;
+ brcms_c_rateset_copy(&gphy_legacy_rates, &rs);
+
+ break;
+
+ case GMODE_LRS:
+ break;
+
+ case GMODE_AUTO:
+ /* Accept defaults */
+ break;
+
+ case GMODE_ONLY:
+ ofdm_basic = true;
+ preamble = BRCMS_PLCP_SHORT;
+ preamble_restrict = true;
+ break;
+
+ case GMODE_PERFORMANCE:
+ shortslot = BRCMS_SHORTSLOT_ON;
+ shortslot_restrict = true;
+ ofdm_basic = true;
+ preamble = BRCMS_PLCP_SHORT;
+ preamble_restrict = true;
+ break;
+
+ default:
+ /* Error */
+ wiphy_err(wlc->wiphy, "wl%d: %s: invalid gmode %d\n",
+ wlc->pub->unit, __func__, gmode);
+ return -ENOTSUPP;
+ }
+
+ band->gmode = gmode;
+
+ wlc->shortslot_override = shortslot;
+
+ /* Use the default 11g rateset */
+ if (!rs.count)
+ brcms_c_rateset_copy(&cck_ofdm_rates, &rs);
+
+ if (ofdm_basic) {
+ for (i = 0; i < rs.count; i++) {
+ if (rs.rates[i] == BRCM_RATE_6M
+ || rs.rates[i] == BRCM_RATE_12M
+ || rs.rates[i] == BRCM_RATE_24M)
+ rs.rates[i] |= BRCMS_RATE_FLAG;
+ }
+ }
+
+ /* Set default bss rateset */
+ wlc->default_bss->rateset.count = rs.count;
+ memcpy(wlc->default_bss->rateset.rates, rs.rates,
+ sizeof(wlc->default_bss->rateset.rates));
+
+ return ret;
+}
+
+int brcms_c_set_nmode(struct brcms_c_info *wlc)
+{
+ uint i;
+ s32 nmode = AUTO;
+
+ if (wlc->stf->txstreams == WL_11N_3x3)
+ nmode = WL_11N_3x3;
+ else
+ nmode = WL_11N_2x2;
+
+ /* force GMODE_AUTO if NMODE is ON */
+ brcms_c_set_gmode(wlc, GMODE_AUTO, true);
+ if (nmode == WL_11N_3x3)
+ wlc->pub->_n_enab = SUPPORT_HT;
+ else
+ wlc->pub->_n_enab = SUPPORT_11N;
+ wlc->default_bss->flags |= BRCMS_BSS_HT;
+ /* add the mcs rates to the default and hw ratesets */
+ brcms_c_rateset_mcs_build(&wlc->default_bss->rateset,
+ wlc->stf->txstreams);
+ for (i = 0; i < wlc->pub->_nbands; i++)
+ memcpy(wlc->bandstate[i]->hw_rateset.mcs,
+ wlc->default_bss->rateset.mcs, MCSSET_LEN);
+
+ return 0;
+}
+
+static int
+brcms_c_set_internal_rateset(struct brcms_c_info *wlc,
+ struct brcms_c_rateset *rs_arg)
+{
+ struct brcms_c_rateset rs, new;
+ uint bandunit;
+
+ memcpy(&rs, rs_arg, sizeof(struct brcms_c_rateset));
+
+ /* check for bad count value */
+ if ((rs.count == 0) || (rs.count > BRCMS_NUMRATES))
+ return -EINVAL;
+
+ /* try the current band */
+ bandunit = wlc->band->bandunit;
+ memcpy(&new, &rs, sizeof(struct brcms_c_rateset));
+ if (brcms_c_rate_hwrs_filter_sort_validate
+ (&new, &wlc->bandstate[bandunit]->hw_rateset, true,
+ wlc->stf->txstreams))
+ goto good;
+
+ /* try the other band */
+ if (brcms_is_mband_unlocked(wlc)) {
+ bandunit = OTHERBANDUNIT(wlc);
+ memcpy(&new, &rs, sizeof(struct brcms_c_rateset));
+ if (brcms_c_rate_hwrs_filter_sort_validate(&new,
+ &wlc->
+ bandstate[bandunit]->
+ hw_rateset, true,
+ wlc->stf->txstreams))
+ goto good;
+ }
+
+ return -EBADE;
+
+ good:
+ /* apply new rateset */
+ memcpy(&wlc->default_bss->rateset, &new,
+ sizeof(struct brcms_c_rateset));
+ memcpy(&wlc->bandstate[bandunit]->defrateset, &new,
+ sizeof(struct brcms_c_rateset));
+ return 0;
+}
+
+static void brcms_c_ofdm_rateset_war(struct brcms_c_info *wlc)
+{
+ u8 r;
+ bool war = false;
+
+ if (wlc->bsscfg->associated)
+ r = wlc->bsscfg->current_bss->rateset.rates[0];
+ else
+ r = wlc->default_bss->rateset.rates[0];
+
+ wlc_phy_ofdm_rateset_war(wlc->band->pi, war);
+}
+
+int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel)
+{
+ u16 chspec = ch20mhz_chspec(channel);
+
+ if (channel < 0 || channel > MAXCHANNEL)
+ return -EINVAL;
+
+ if (!brcms_c_valid_chanspec_db(wlc->cmi, chspec))
+ return -EINVAL;
+
+
+ if (!wlc->pub->up && brcms_is_mband_unlocked(wlc)) {
+ if (wlc->band->bandunit != chspec_bandunit(chspec))
+ wlc->bandinit_pending = true;
+ else
+ wlc->bandinit_pending = false;
+ }
+
+ wlc->default_bss->chanspec = chspec;
+ /* brcms_c_BSSinit() will sanitize the rateset before
+ * using it.. */
+ if (wlc->pub->up && (wlc_phy_chanspec_get(wlc->band->pi) != chspec)) {
+ brcms_c_set_home_chanspec(wlc, chspec);
+ brcms_c_suspend_mac_and_wait(wlc);
+ brcms_c_set_chanspec(wlc, chspec);
+ brcms_c_enable_mac(wlc);
+ }
+ return 0;
+}
+
+int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl)
+{
+ int ac;
+
+ if (srl < 1 || srl > RETRY_SHORT_MAX ||
+ lrl < 1 || lrl > RETRY_SHORT_MAX)
+ return -EINVAL;
+
+ wlc->SRL = srl;
+ wlc->LRL = lrl;
+
+ brcms_b_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL);
+
+ for (ac = 0; ac < AC_COUNT; ac++) {
+ wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac],
+ EDCF_SHORT, wlc->SRL);
+ wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac],
+ EDCF_LONG, wlc->LRL);
+ }
+ brcms_c_wme_retries_write(wlc);
+
+ return 0;
+}
+
+void brcms_c_get_current_rateset(struct brcms_c_info *wlc,
+ struct brcm_rateset *currs)
+{
+ struct brcms_c_rateset *rs;
+
+ if (wlc->pub->associated)
+ rs = &wlc->bsscfg->current_bss->rateset;
+ else
+ rs = &wlc->default_bss->rateset;
+
+ /* Copy only legacy rateset section */
+ currs->count = rs->count;
+ memcpy(&currs->rates, &rs->rates, rs->count);
+}
+
+int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs)
+{
+ struct brcms_c_rateset internal_rs;
+ int bcmerror;
+
+ if (rs->count > BRCMS_NUMRATES)
+ return -ENOBUFS;
+
+ memset(&internal_rs, 0, sizeof(struct brcms_c_rateset));
+
+ /* Copy only legacy rateset section */
+ internal_rs.count = rs->count;
+ memcpy(&internal_rs.rates, &rs->rates, internal_rs.count);
+
+ /* merge rateset coming in with the current mcsset */
+ if (wlc->pub->_n_enab & SUPPORT_11N) {
+ struct brcms_bss_info *mcsset_bss;
+ if (wlc->bsscfg->associated)
+ mcsset_bss = wlc->bsscfg->current_bss;
+ else
+ mcsset_bss = wlc->default_bss;
+ memcpy(internal_rs.mcs, &mcsset_bss->rateset.mcs[0],
+ MCSSET_LEN);
+ }
+
+ bcmerror = brcms_c_set_internal_rateset(wlc, &internal_rs);
+ if (!bcmerror)
+ brcms_c_ofdm_rateset_war(wlc);
+
+ return bcmerror;
+}
+
+int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period)
+{
+ if (period < DOT11_MIN_BEACON_PERIOD ||
+ period > DOT11_MAX_BEACON_PERIOD)
+ return -EINVAL;
+
+ wlc->default_bss->beacon_period = period;
+ return 0;
+}
+
+u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx)
+{
+ return wlc->band->phytype;
+}
+
+void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, s8 sslot_override)
+{
+ wlc->shortslot_override = sslot_override;
+
+ /*
+ * shortslot is an 11g feature, so no more work if we are
+ * currently on the 5G band
+ */
+ if (wlc->band->bandtype == BRCM_BAND_5G)
+ return;
+
+ if (wlc->pub->up && wlc->pub->associated) {
+ /* let watchdog or beacon processing update shortslot */
+ } else if (wlc->pub->up) {
+ /* unassociated shortslot is off */
+ brcms_c_switch_shortslot(wlc, false);
+ } else {
+ /* driver is down, so just update the brcms_c_info
+ * value */
+ if (wlc->shortslot_override == BRCMS_SHORTSLOT_AUTO)
+ wlc->shortslot = false;
+ else
+ wlc->shortslot =
+ (wlc->shortslot_override ==
+ BRCMS_SHORTSLOT_ON);
+ }
+}
+
+/*
+ * register watchdog and down handlers.
+ */
+int brcms_c_module_register(struct brcms_pub *pub,
+ const char *name, struct brcms_info *hdl,
+ int (*d_fn)(void *handle))
+{
+ struct brcms_c_info *wlc = (struct brcms_c_info *) pub->wlc;
+ int i;
+
+ /* find an empty entry and just add, no duplication check! */
+ for (i = 0; i < BRCMS_MAXMODULES; i++) {
+ if (wlc->modulecb[i].name[0] == '\0') {
+ strncpy(wlc->modulecb[i].name, name,
+ sizeof(wlc->modulecb[i].name) - 1);
+ wlc->modulecb[i].hdl = hdl;
+ wlc->modulecb[i].down_fn = d_fn;
+ return 0;
+ }
+ }
+
+ return -ENOSR;
+}
+
+/* unregister module callbacks */
+int brcms_c_module_unregister(struct brcms_pub *pub, const char *name,
+ struct brcms_info *hdl)
+{
+ struct brcms_c_info *wlc = (struct brcms_c_info *) pub->wlc;
+ int i;
+
+ if (wlc == NULL)
+ return -ENODATA;
+
+ for (i = 0; i < BRCMS_MAXMODULES; i++) {
+ if (!strcmp(wlc->modulecb[i].name, name) &&
+ (wlc->modulecb[i].hdl == hdl)) {
+ memset(&wlc->modulecb[i], 0, sizeof(struct modulecb));
+ return 0;
+ }
+ }
+
+ /* table not found! */
+ return -ENODATA;
+}
+
+#ifdef BCMDBG
+static const char * const supr_reason[] = {
+ "None", "PMQ Entry", "Flush request",
+ "Previous frag failure", "Channel mismatch",
+ "Lifetime Expiry", "Underflow"
+};
+
+static void brcms_c_print_txs_status(u16 s)
+{
+ printk(KERN_DEBUG "[15:12] %d frame attempts\n",
+ (s & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT);
+ printk(KERN_DEBUG " [11:8] %d rts attempts\n",
+ (s & TX_STATUS_RTS_RTX_MASK) >> TX_STATUS_RTS_RTX_SHIFT);
+ printk(KERN_DEBUG " [7] %d PM mode indicated\n",
+ ((s & TX_STATUS_PMINDCTD) ? 1 : 0));
+ printk(KERN_DEBUG " [6] %d intermediate status\n",
+ ((s & TX_STATUS_INTERMEDIATE) ? 1 : 0));
+ printk(KERN_DEBUG " [5] %d AMPDU\n",
+ (s & TX_STATUS_AMPDU) ? 1 : 0);
+ printk(KERN_DEBUG " [4:2] %d Frame Suppressed Reason (%s)\n",
+ ((s & TX_STATUS_SUPR_MASK) >> TX_STATUS_SUPR_SHIFT),
+ supr_reason[(s & TX_STATUS_SUPR_MASK) >> TX_STATUS_SUPR_SHIFT]);
+ printk(KERN_DEBUG " [1] %d acked\n",
+ ((s & TX_STATUS_ACK_RCV) ? 1 : 0));
+}
+#endif /* BCMDBG */
+
+void brcms_c_print_txstatus(struct tx_status *txs)
+{
+#if defined(BCMDBG)
+ u16 s = txs->status;
+ u16 ackphyrxsh = txs->ackphyrxsh;
+
+ printk(KERN_DEBUG "\ntxpkt (MPDU) Complete\n");
+
+ printk(KERN_DEBUG "FrameID: %04x ", txs->frameid);
+ printk(KERN_DEBUG "TxStatus: %04x", s);
+ printk(KERN_DEBUG "\n");
+
+ brcms_c_print_txs_status(s);
+
+ printk(KERN_DEBUG "LastTxTime: %04x ", txs->lasttxtime);
+ printk(KERN_DEBUG "Seq: %04x ", txs->sequence);
+ printk(KERN_DEBUG "PHYTxStatus: %04x ", txs->phyerr);
+ printk(KERN_DEBUG "RxAckRSSI: %04x ",
+ (ackphyrxsh & PRXS1_JSSI_MASK) >> PRXS1_JSSI_SHIFT);
+ printk(KERN_DEBUG "RxAckSQ: %04x",
+ (ackphyrxsh & PRXS1_SQ_MASK) >> PRXS1_SQ_SHIFT);
+ printk(KERN_DEBUG "\n");
+#endif /* defined(BCMDBG) */
+}
+
+bool brcms_c_chipmatch(u16 vendor, u16 device)
+{
+ if (vendor != PCI_VENDOR_ID_BROADCOM) {
+ pr_err("chipmatch: unknown vendor id %04x\n", vendor);
+ return false;
+ }
+
+ if (device == BCM43224_D11N_ID_VEN1)
+ return true;
+ if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID))
+ return true;
+ if (device == BCM4313_D11N2G_ID)
+ return true;
+ if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID))
+ return true;
+
+ pr_err("chipmatch: unknown device id %04x\n", device);
+ return false;
+}
+
+#if defined(BCMDBG)
+void brcms_c_print_txdesc(struct d11txh *txh)
+{
+ u16 mtcl = le16_to_cpu(txh->MacTxControlLow);
+ u16 mtch = le16_to_cpu(txh->MacTxControlHigh);
+ u16 mfc = le16_to_cpu(txh->MacFrameControl);
+ u16 tfest = le16_to_cpu(txh->TxFesTimeNormal);
+ u16 ptcw = le16_to_cpu(txh->PhyTxControlWord);
+ u16 ptcw_1 = le16_to_cpu(txh->PhyTxControlWord_1);
+ u16 ptcw_1_Fbr = le16_to_cpu(txh->PhyTxControlWord_1_Fbr);
+ u16 ptcw_1_Rts = le16_to_cpu(txh->PhyTxControlWord_1_Rts);
+ u16 ptcw_1_FbrRts = le16_to_cpu(txh->PhyTxControlWord_1_FbrRts);
+ u16 mainrates = le16_to_cpu(txh->MainRates);
+ u16 xtraft = le16_to_cpu(txh->XtraFrameTypes);
+ u8 *iv = txh->IV;
+ u8 *ra = txh->TxFrameRA;
+ u16 tfestfb = le16_to_cpu(txh->TxFesTimeFallback);
+ u8 *rtspfb = txh->RTSPLCPFallback;
+ u16 rtsdfb = le16_to_cpu(txh->RTSDurFallback);
+ u8 *fragpfb = txh->FragPLCPFallback;
+ u16 fragdfb = le16_to_cpu(txh->FragDurFallback);
+ u16 mmodelen = le16_to_cpu(txh->MModeLen);
+ u16 mmodefbrlen = le16_to_cpu(txh->MModeFbrLen);
+ u16 tfid = le16_to_cpu(txh->TxFrameID);
+ u16 txs = le16_to_cpu(txh->TxStatus);
+ u16 mnmpdu = le16_to_cpu(txh->MaxNMpdus);
+ u16 mabyte = le16_to_cpu(txh->MaxABytes_MRT);
+ u16 mabyte_f = le16_to_cpu(txh->MaxABytes_FBR);
+ u16 mmbyte = le16_to_cpu(txh->MinMBytes);
+
+ u8 *rtsph = txh->RTSPhyHeader;
+ struct ieee80211_rts rts = txh->rts_frame;
+ char hexbuf[256];
+
+ /* add plcp header along with txh descriptor */
+ printk(KERN_DEBUG "Raw TxDesc + plcp header:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ txh, sizeof(struct d11txh) + 48);
+
+ printk(KERN_DEBUG "TxCtlLow: %04x ", mtcl);
+ printk(KERN_DEBUG "TxCtlHigh: %04x ", mtch);
+ printk(KERN_DEBUG "FC: %04x ", mfc);
+ printk(KERN_DEBUG "FES Time: %04x\n", tfest);
+ printk(KERN_DEBUG "PhyCtl: %04x%s ", ptcw,
+ (ptcw & PHY_TXC_SHORT_HDR) ? " short" : "");
+ printk(KERN_DEBUG "PhyCtl_1: %04x ", ptcw_1);
+ printk(KERN_DEBUG "PhyCtl_1_Fbr: %04x\n", ptcw_1_Fbr);
+ printk(KERN_DEBUG "PhyCtl_1_Rts: %04x ", ptcw_1_Rts);
+ printk(KERN_DEBUG "PhyCtl_1_Fbr_Rts: %04x\n", ptcw_1_FbrRts);
+ printk(KERN_DEBUG "MainRates: %04x ", mainrates);
+ printk(KERN_DEBUG "XtraFrameTypes: %04x ", xtraft);
+ printk(KERN_DEBUG "\n");
+
+ brcmu_format_hex(hexbuf, iv, sizeof(txh->IV));
+ printk(KERN_DEBUG "SecIV: %s\n", hexbuf);
+ brcmu_format_hex(hexbuf, ra, sizeof(txh->TxFrameRA));
+ printk(KERN_DEBUG "RA: %s\n", hexbuf);
+
+ printk(KERN_DEBUG "Fb FES Time: %04x ", tfestfb);
+ brcmu_format_hex(hexbuf, rtspfb, sizeof(txh->RTSPLCPFallback));
+ printk(KERN_DEBUG "RTS PLCP: %s ", hexbuf);
+ printk(KERN_DEBUG "RTS DUR: %04x ", rtsdfb);
+ brcmu_format_hex(hexbuf, fragpfb, sizeof(txh->FragPLCPFallback));
+ printk(KERN_DEBUG "PLCP: %s ", hexbuf);
+ printk(KERN_DEBUG "DUR: %04x", fragdfb);
+ printk(KERN_DEBUG "\n");
+
+ printk(KERN_DEBUG "MModeLen: %04x ", mmodelen);
+ printk(KERN_DEBUG "MModeFbrLen: %04x\n", mmodefbrlen);
+
+ printk(KERN_DEBUG "FrameID: %04x\n", tfid);
+ printk(KERN_DEBUG "TxStatus: %04x\n", txs);
+
+ printk(KERN_DEBUG "MaxNumMpdu: %04x\n", mnmpdu);
+ printk(KERN_DEBUG "MaxAggbyte: %04x\n", mabyte);
+ printk(KERN_DEBUG "MaxAggbyte_fb: %04x\n", mabyte_f);
+ printk(KERN_DEBUG "MinByte: %04x\n", mmbyte);
+
+ brcmu_format_hex(hexbuf, rtsph, sizeof(txh->RTSPhyHeader));
+ printk(KERN_DEBUG "RTS PLCP: %s ", hexbuf);
+ brcmu_format_hex(hexbuf, (u8 *) &rts, sizeof(txh->rts_frame));
+ printk(KERN_DEBUG "RTS Frame: %s", hexbuf);
+ printk(KERN_DEBUG "\n");
+}
+#endif /* defined(BCMDBG) */
+
+#if defined(BCMDBG)
+int
+brcms_c_format_flags(const struct brcms_c_bit_desc *bd, u32 flags, char *buf,
+ int len)
+{
+ int i;
+ char *p = buf;
+ char hexstr[16];
+ int slen = 0, nlen = 0;
+ u32 bit;
+ const char *name;
+
+ if (len < 2 || !buf)
+ return 0;
+
+ buf[0] = '\0';
+
+ for (i = 0; flags != 0; i++) {
+ bit = bd[i].bit;
+ name = bd[i].name;
+ if (bit == 0 && flags != 0) {
+ /* print any unnamed bits */
+ snprintf(hexstr, 16, "0x%X", flags);
+ name = hexstr;
+ flags = 0; /* exit loop */
+ } else if ((flags & bit) == 0)
+ continue;
+ flags &= ~bit;
+ nlen = strlen(name);
+ slen += nlen;
+ /* count btwn flag space */
+ if (flags != 0)
+ slen += 1;
+ /* need NULL char as well */
+ if (len <= slen)
+ break;
+ /* copy NULL char but don't count it */
+ strncpy(p, name, nlen + 1);
+ p += nlen;
+ /* copy btwn flag space and NULL char */
+ if (flags != 0)
+ p += snprintf(p, 2, " ");
+ len -= slen;
+ }
+
+ /* indicate the str was too short */
+ if (flags != 0) {
+ if (len < 2)
+ p -= 2 - len; /* overwrite last char */
+ p += snprintf(p, 2, ">");
+ }
+
+ return (int)(p - buf);
+}
+#endif /* defined(BCMDBG) */
+
+#if defined(BCMDBG)
+void brcms_c_print_rxh(struct d11rxhdr *rxh)
+{
+ u16 len = rxh->RxFrameSize;
+ u16 phystatus_0 = rxh->PhyRxStatus_0;
+ u16 phystatus_1 = rxh->PhyRxStatus_1;
+ u16 phystatus_2 = rxh->PhyRxStatus_2;
+ u16 phystatus_3 = rxh->PhyRxStatus_3;
+ u16 macstatus1 = rxh->RxStatus1;
+ u16 macstatus2 = rxh->RxStatus2;
+ char flagstr[64];
+ char lenbuf[20];
+ static const struct brcms_c_bit_desc macstat_flags[] = {
+ {RXS_FCSERR, "FCSErr"},
+ {RXS_RESPFRAMETX, "Reply"},
+ {RXS_PBPRES, "PADDING"},
+ {RXS_DECATMPT, "DeCr"},
+ {RXS_DECERR, "DeCrErr"},
+ {RXS_BCNSENT, "Bcn"},
+ {0, NULL}
+ };
+
+ printk(KERN_DEBUG "Raw RxDesc:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, rxh,
+ sizeof(struct d11rxhdr));
+
+ brcms_c_format_flags(macstat_flags, macstatus1, flagstr, 64);
+
+ snprintf(lenbuf, sizeof(lenbuf), "0x%x", len);
+
+ printk(KERN_DEBUG "RxFrameSize: %6s (%d)%s\n", lenbuf, len,
+ (rxh->PhyRxStatus_0 & PRXS0_SHORTH) ? " short preamble" : "");
+ printk(KERN_DEBUG "RxPHYStatus: %04x %04x %04x %04x\n",
+ phystatus_0, phystatus_1, phystatus_2, phystatus_3);
+ printk(KERN_DEBUG "RxMACStatus: %x %s\n", macstatus1, flagstr);
+ printk(KERN_DEBUG "RXMACaggtype: %x\n",
+ (macstatus2 & RXS_AGGTYPE_MASK));
+ printk(KERN_DEBUG "RxTSFTime: %04x\n", rxh->RxTSFTime);
+}
+#endif /* defined(BCMDBG) */
+
+u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate)
+{
+ u16 table_ptr;
+ u8 phy_rate, index;
+
+ /* get the phy specific rate encoding for the PLCP SIGNAL field */
+ if (is_ofdm_rate(rate))
+ table_ptr = M_RT_DIRMAP_A;
+ else
+ table_ptr = M_RT_DIRMAP_B;
+
+ /* for a given rate, the LS-nibble of the PLCP SIGNAL field is
+ * the index into the rate table.
+ */
+ phy_rate = rate_info[rate] & BRCMS_RATE_MASK;
+ index = phy_rate & 0xf;
+
+ /* Find the SHM pointer to the rate table entry by looking in the
+ * Direct-map Table
+ */
+ return 2 * brcms_b_read_shm(wlc_hw, table_ptr + (index * 2));
+}
+
+static bool
+brcms_c_prec_enq_head(struct brcms_c_info *wlc, struct pktq *q,
+ struct sk_buff *pkt, int prec, bool head)
+{
+ struct sk_buff *p;
+ int eprec = -1; /* precedence to evict from */
+
+ /* Determine precedence from which to evict packet, if any */
+ if (pktq_pfull(q, prec))
+ eprec = prec;
+ else if (pktq_full(q)) {
+ p = brcmu_pktq_peek_tail(q, &eprec);
+ if (eprec > prec) {
+ wiphy_err(wlc->wiphy, "%s: Failing: eprec %d > prec %d"
+ "\n", __func__, eprec, prec);
+ return false;
+ }
+ }
+
+ /* Evict if needed */
+ if (eprec >= 0) {
+ bool discard_oldest;
+
+ discard_oldest = ac_bitmap_tst(0, eprec);
+
+ /* Refuse newer packet unless configured to discard oldest */
+ if (eprec == prec && !discard_oldest) {
+ wiphy_err(wlc->wiphy, "%s: No where to go, prec == %d"
+ "\n", __func__, prec);
+ return false;
+ }
+
+ /* Evict packet according to discard policy */
+ p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) :
+ brcmu_pktq_pdeq_tail(q, eprec);
+ brcmu_pkt_buf_free_skb(p);
+ }
+
+ /* Enqueue */
+ if (head)
+ p = brcmu_pktq_penq_head(q, prec, pkt);
+ else
+ p = brcmu_pktq_penq(q, prec, pkt);
+
+ return true;
+}
+
+/*
+ * Attempts to queue a packet onto a multiple-precedence queue,
+ * if necessary evicting a lower precedence packet from the queue.
+ *
+ * 'prec' is the precedence number that has already been mapped
+ * from the packet priority.
+ *
+ * Returns true if packet consumed (queued), false if not.
+ */
+static bool brcms_c_prec_enq(struct brcms_c_info *wlc, struct pktq *q,
+ struct sk_buff *pkt, int prec)
+{
+ return brcms_c_prec_enq_head(wlc, q, pkt, prec, false);
+}
+
+void brcms_c_txq_enq(struct brcms_c_info *wlc, struct scb *scb,
+ struct sk_buff *sdu, uint prec)
+{
+ struct brcms_txq_info *qi = wlc->pkt_queue; /* Check me */
+ struct pktq *q = &qi->q;
+ int prio;
+
+ prio = sdu->priority;
+
+ if (!brcms_c_prec_enq(wlc, q, sdu, prec)) {
+ /*
+ * we might hit this condtion in case
+ * packet flooding from mac80211 stack
+ */
+ brcmu_pkt_buf_free_skb(sdu);
+ }
+}
+
+/*
+ * bcmc_fid_generate:
+ * Generate frame ID for a BCMC packet. The frag field is not used
+ * for MC frames so is used as part of the sequence number.
+ */
+static inline u16
+bcmc_fid_generate(struct brcms_c_info *wlc, struct brcms_bss_cfg *bsscfg,
+ struct d11txh *txh)
+{
+ u16 frameid;
+
+ frameid = le16_to_cpu(txh->TxFrameID) & ~(TXFID_SEQ_MASK |
+ TXFID_QUEUE_MASK);
+ frameid |=
+ (((wlc->
+ mc_fid_counter++) << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) |
+ TX_BCMC_FIFO;
+
+ return frameid;
+}
+
+static uint
+brcms_c_calc_ack_time(struct brcms_c_info *wlc, u32 rspec,
+ u8 preamble_type)
+{
+ uint dur = 0;
+
+ BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d\n",
+ wlc->pub->unit, rspec, preamble_type);
+ /*
+ * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that
+ * is less than or equal to the rate of the immediately previous
+ * frame in the FES
+ */
+ rspec = brcms_basic_rate(wlc, rspec);
+ /* ACK frame len == 14 == 2(fc) + 2(dur) + 6(ra) + 4(fcs) */
+ dur =
+ brcms_c_calc_frame_time(wlc, rspec, preamble_type,
+ (DOT11_ACK_LEN + FCS_LEN));
+ return dur;
+}
+
+static uint
+brcms_c_calc_cts_time(struct brcms_c_info *wlc, u32 rspec,
+ u8 preamble_type)
+{
+ BCMMSG(wlc->wiphy, "wl%d: ratespec 0x%x, preamble_type %d\n",
+ wlc->pub->unit, rspec, preamble_type);
+ return brcms_c_calc_ack_time(wlc, rspec, preamble_type);
+}
+
+static uint
+brcms_c_calc_ba_time(struct brcms_c_info *wlc, u32 rspec,
+ u8 preamble_type)
+{
+ BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, "
+ "preamble_type %d\n", wlc->pub->unit, rspec, preamble_type);
+ /*
+ * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that
+ * is less than or equal to the rate of the immediately previous
+ * frame in the FES
+ */
+ rspec = brcms_basic_rate(wlc, rspec);
+ /* BA len == 32 == 16(ctl hdr) + 4(ba len) + 8(bitmap) + 4(fcs) */
+ return brcms_c_calc_frame_time(wlc, rspec, preamble_type,
+ (DOT11_BA_LEN + DOT11_BA_BITMAP_LEN +
+ FCS_LEN));
+}
+
+/* brcms_c_compute_frame_dur()
+ *
+ * Calculate the 802.11 MAC header DUR field for MPDU
+ * DUR for a single frame = 1 SIFS + 1 ACK
+ * DUR for a frame with following frags = 3 SIFS + 2 ACK + next frag time
+ *
+ * rate MPDU rate in unit of 500kbps
+ * next_frag_len next MPDU length in bytes
+ * preamble_type use short/GF or long/MM PLCP header
+ */
+static u16
+brcms_c_compute_frame_dur(struct brcms_c_info *wlc, u32 rate,
+ u8 preamble_type, uint next_frag_len)
+{
+ u16 dur, sifs;
+
+ sifs = get_sifs(wlc->band);
+
+ dur = sifs;
+ dur += (u16) brcms_c_calc_ack_time(wlc, rate, preamble_type);
+
+ if (next_frag_len) {
+ /* Double the current DUR to get 2 SIFS + 2 ACKs */
+ dur *= 2;
+ /* add another SIFS and the frag time */
+ dur += sifs;
+ dur +=
+ (u16) brcms_c_calc_frame_time(wlc, rate, preamble_type,
+ next_frag_len);
+ }
+ return dur;
+}
+
+/* The opposite of brcms_c_calc_frame_time */
+static uint
+brcms_c_calc_frame_len(struct brcms_c_info *wlc, u32 ratespec,
+ u8 preamble_type, uint dur)
+{
+ uint nsyms, mac_len, Ndps, kNdps;
+ uint rate = rspec2rate(ratespec);
+
+ BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, dur %d\n",
+ wlc->pub->unit, ratespec, preamble_type, dur);
+
+ if (is_mcs_rate(ratespec)) {
+ uint mcs = ratespec & RSPEC_RATE_MASK;
+ int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec);
+ dur -= PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT);
+ /* payload calculation matches that of regular ofdm */
+ if (wlc->band->bandtype == BRCM_BAND_2G)
+ dur -= DOT11_OFDM_SIGNAL_EXTENSION;
+ /* kNdbps = kbps * 4 */
+ kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec),
+ rspec_issgi(ratespec)) * 4;
+ nsyms = dur / APHY_SYMBOL_TIME;
+ mac_len =
+ ((nsyms * kNdps) -
+ ((APHY_SERVICE_NBITS + APHY_TAIL_NBITS) * 1000)) / 8000;
+ } else if (is_ofdm_rate(ratespec)) {
+ dur -= APHY_PREAMBLE_TIME;
+ dur -= APHY_SIGNAL_TIME;
+ /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */
+ Ndps = rate * 2;
+ nsyms = dur / APHY_SYMBOL_TIME;
+ mac_len =
+ ((nsyms * Ndps) -
+ (APHY_SERVICE_NBITS + APHY_TAIL_NBITS)) / 8;
+ } else {
+ if (preamble_type & BRCMS_SHORT_PREAMBLE)
+ dur -= BPHY_PLCP_SHORT_TIME;
+ else
+ dur -= BPHY_PLCP_TIME;
+ mac_len = dur * rate;
+ /* divide out factor of 2 in rate (1/2 mbps) */
+ mac_len = mac_len / 8 / 2;
+ }
+ return mac_len;
+}
+
+/*
+ * Return true if the specified rate is supported by the specified band.
+ * BRCM_BAND_AUTO indicates the current band.
+ */
+static bool brcms_c_valid_rate(struct brcms_c_info *wlc, u32 rspec, int band,
+ bool verbose)
+{
+ struct brcms_c_rateset *hw_rateset;
+ uint i;
+
+ if ((band == BRCM_BAND_AUTO) || (band == wlc->band->bandtype))
+ hw_rateset = &wlc->band->hw_rateset;
+ else if (wlc->pub->_nbands > 1)
+ hw_rateset = &wlc->bandstate[OTHERBANDUNIT(wlc)]->hw_rateset;
+ else
+ /* other band specified and we are a single band device */
+ return false;
+
+ /* check if this is a mimo rate */
+ if (is_mcs_rate(rspec)) {
+ if ((rspec & RSPEC_RATE_MASK) >= MCS_TABLE_SIZE)
+ goto error;
+
+ return isset(hw_rateset->mcs, (rspec & RSPEC_RATE_MASK));
+ }
+
+ for (i = 0; i < hw_rateset->count; i++)
+ if (hw_rateset->rates[i] == rspec2rate(rspec))
+ return true;
+ error:
+ if (verbose)
+ wiphy_err(wlc->wiphy, "wl%d: valid_rate: rate spec 0x%x "
+ "not in hw_rateset\n", wlc->pub->unit, rspec);
+
+ return false;
+}
+
+static u32
+mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band,
+ u32 int_val)
+{
+ u8 stf = (int_val & NRATE_STF_MASK) >> NRATE_STF_SHIFT;
+ u8 rate = int_val & NRATE_RATE_MASK;
+ u32 rspec;
+ bool ismcs = ((int_val & NRATE_MCS_INUSE) == NRATE_MCS_INUSE);
+ bool issgi = ((int_val & NRATE_SGI_MASK) >> NRATE_SGI_SHIFT);
+ bool override_mcs_only = ((int_val & NRATE_OVERRIDE_MCS_ONLY)
+ == NRATE_OVERRIDE_MCS_ONLY);
+ int bcmerror = 0;
+
+ if (!ismcs)
+ return (u32) rate;
+
+ /* validate the combination of rate/mcs/stf is allowed */
+ if ((wlc->pub->_n_enab & SUPPORT_11N) && ismcs) {
+ /* mcs only allowed when nmode */
+ if (stf > PHY_TXC1_MODE_SDM) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid stf\n",
+ wlc->pub->unit, __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+
+ /* mcs 32 is a special case, DUP mode 40 only */
+ if (rate == 32) {
+ if (!CHSPEC_IS40(wlc->home_chanspec) ||
+ ((stf != PHY_TXC1_MODE_SISO)
+ && (stf != PHY_TXC1_MODE_CDD))) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid mcs "
+ "32\n", wlc->pub->unit, __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+ /* mcs > 7 must use stf SDM */
+ } else if (rate > HIGHEST_SINGLE_STREAM_MCS) {
+ /* mcs > 7 must use stf SDM */
+ if (stf != PHY_TXC1_MODE_SDM) {
+ BCMMSG(wlc->wiphy, "wl%d: enabling "
+ "SDM mode for mcs %d\n",
+ wlc->pub->unit, rate);
+ stf = PHY_TXC1_MODE_SDM;
+ }
+ } else {
+ /*
+ * MCS 0-7 may use SISO, CDD, and for
+ * phy_rev >= 3 STBC
+ */
+ if ((stf > PHY_TXC1_MODE_STBC) ||
+ (!BRCMS_STBC_CAP_PHY(wlc)
+ && (stf == PHY_TXC1_MODE_STBC))) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid STBC"
+ "\n", wlc->pub->unit, __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+ }
+ } else if (is_ofdm_rate(rate)) {
+ if ((stf != PHY_TXC1_MODE_CDD) && (stf != PHY_TXC1_MODE_SISO)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid OFDM\n",
+ wlc->pub->unit, __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+ } else if (is_cck_rate(rate)) {
+ if ((cur_band->bandtype != BRCM_BAND_2G)
+ || (stf != PHY_TXC1_MODE_SISO)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid CCK\n",
+ wlc->pub->unit, __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+ } else {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Unknown rate type\n",
+ wlc->pub->unit, __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+ /* make sure multiple antennae are available for non-siso rates */
+ if ((stf != PHY_TXC1_MODE_SISO) && (wlc->stf->txstreams == 1)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: SISO antenna but !SISO "
+ "request\n", wlc->pub->unit, __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+
+ rspec = rate;
+ if (ismcs) {
+ rspec |= RSPEC_MIMORATE;
+ /* For STBC populate the STC field of the ratespec */
+ if (stf == PHY_TXC1_MODE_STBC) {
+ u8 stc;
+ stc = 1; /* Nss for single stream is always 1 */
+ rspec |= (stc << RSPEC_STC_SHIFT);
+ }
+ }
+
+ rspec |= (stf << RSPEC_STF_SHIFT);
+
+ if (override_mcs_only)
+ rspec |= RSPEC_OVERRIDE_MCS_ONLY;
+
+ if (issgi)
+ rspec |= RSPEC_SHORT_GI;
+
+ if ((rate != 0)
+ && !brcms_c_valid_rate(wlc, rspec, cur_band->bandtype, true))
+ return rate;
+
+ return rspec;
+done:
+ return rate;
+}
+
+/*
+ * Compute PLCP, but only requires actual rate and length of pkt.
+ * Rate is given in the driver standard multiple of 500 kbps.
+ * le is set for 11 Mbps rate if necessary.
+ * Broken out for PRQ.
+ */
+
+static void brcms_c_cck_plcp_set(struct brcms_c_info *wlc, int rate_500,
+ uint length, u8 *plcp)
+{
+ u16 usec = 0;
+ u8 le = 0;
+
+ switch (rate_500) {
+ case BRCM_RATE_1M:
+ usec = length << 3;
+ break;
+ case BRCM_RATE_2M:
+ usec = length << 2;
+ break;
+ case BRCM_RATE_5M5:
+ usec = (length << 4) / 11;
+ if ((length << 4) - (usec * 11) > 0)
+ usec++;
+ break;
+ case BRCM_RATE_11M:
+ usec = (length << 3) / 11;
+ if ((length << 3) - (usec * 11) > 0) {
+ usec++;
+ if ((usec * 11) - (length << 3) >= 8)
+ le = D11B_PLCP_SIGNAL_LE;
+ }
+ break;
+
+ default:
+ wiphy_err(wlc->wiphy,
+ "brcms_c_cck_plcp_set: unsupported rate %d\n",
+ rate_500);
+ rate_500 = BRCM_RATE_1M;
+ usec = length << 3;
+ break;
+ }
+ /* PLCP signal byte */
+ plcp[0] = rate_500 * 5; /* r (500kbps) * 5 == r (100kbps) */
+ /* PLCP service byte */
+ plcp[1] = (u8) (le | D11B_PLCP_SIGNAL_LOCKED);
+ /* PLCP length u16, little endian */
+ plcp[2] = usec & 0xff;
+ plcp[3] = (usec >> 8) & 0xff;
+ /* PLCP CRC16 */
+ plcp[4] = 0;
+ plcp[5] = 0;
+}
+
+/* Rate: 802.11 rate code, length: PSDU length in octets */
+static void brcms_c_compute_mimo_plcp(u32 rspec, uint length, u8 *plcp)
+{
+ u8 mcs = (u8) (rspec & RSPEC_RATE_MASK);
+ plcp[0] = mcs;
+ if (rspec_is40mhz(rspec) || (mcs == 32))
+ plcp[0] |= MIMO_PLCP_40MHZ;
+ BRCMS_SET_MIMO_PLCP_LEN(plcp, length);
+ plcp[3] = rspec_mimoplcp3(rspec); /* rspec already holds this byte */
+ plcp[3] |= 0x7; /* set smoothing, not sounding ppdu & reserved */
+ plcp[4] = 0; /* number of extension spatial streams bit 0 & 1 */
+ plcp[5] = 0;
+}
+
+/* Rate: 802.11 rate code, length: PSDU length in octets */
+static void
+brcms_c_compute_ofdm_plcp(u32 rspec, u32 length, u8 *plcp)
+{
+ u8 rate_signal;
+ u32 tmp = 0;
+ int rate = rspec2rate(rspec);
+
+ /*
+ * encode rate per 802.11a-1999 sec 17.3.4.1, with lsb
+ * transmitted first
+ */
+ rate_signal = rate_info[rate] & BRCMS_RATE_MASK;
+ memset(plcp, 0, D11_PHY_HDR_LEN);
+ D11A_PHY_HDR_SRATE((struct ofdm_phy_hdr *) plcp, rate_signal);
+
+ tmp = (length & 0xfff) << 5;
+ plcp[2] |= (tmp >> 16) & 0xff;
+ plcp[1] |= (tmp >> 8) & 0xff;
+ plcp[0] |= tmp & 0xff;
+}
+
+/* Rate: 802.11 rate code, length: PSDU length in octets */
+static void brcms_c_compute_cck_plcp(struct brcms_c_info *wlc, u32 rspec,
+ uint length, u8 *plcp)
+{
+ int rate = rspec2rate(rspec);
+
+ brcms_c_cck_plcp_set(wlc, rate, length, plcp);
+}
+
+static void
+brcms_c_compute_plcp(struct brcms_c_info *wlc, u32 rspec,
+ uint length, u8 *plcp)
+{
+ if (is_mcs_rate(rspec))
+ brcms_c_compute_mimo_plcp(rspec, length, plcp);
+ else if (is_ofdm_rate(rspec))
+ brcms_c_compute_ofdm_plcp(rspec, length, plcp);
+ else
+ brcms_c_compute_cck_plcp(wlc, rspec, length, plcp);
+}
+
+/* brcms_c_compute_rtscts_dur()
+ *
+ * Calculate the 802.11 MAC header DUR field for an RTS or CTS frame
+ * DUR for normal RTS/CTS w/ frame = 3 SIFS + 1 CTS + next frame time + 1 ACK
+ * DUR for CTS-TO-SELF w/ frame = 2 SIFS + next frame time + 1 ACK
+ *
+ * cts cts-to-self or rts/cts
+ * rts_rate rts or cts rate in unit of 500kbps
+ * rate next MPDU rate in unit of 500kbps
+ * frame_len next MPDU frame length in bytes
+ */
+u16
+brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only,
+ u32 rts_rate,
+ u32 frame_rate, u8 rts_preamble_type,
+ u8 frame_preamble_type, uint frame_len, bool ba)
+{
+ u16 dur, sifs;
+
+ sifs = get_sifs(wlc->band);
+
+ if (!cts_only) {
+ /* RTS/CTS */
+ dur = 3 * sifs;
+ dur +=
+ (u16) brcms_c_calc_cts_time(wlc, rts_rate,
+ rts_preamble_type);
+ } else {
+ /* CTS-TO-SELF */
+ dur = 2 * sifs;
+ }
+
+ dur +=
+ (u16) brcms_c_calc_frame_time(wlc, frame_rate, frame_preamble_type,
+ frame_len);
+ if (ba)
+ dur +=
+ (u16) brcms_c_calc_ba_time(wlc, frame_rate,
+ BRCMS_SHORT_PREAMBLE);
+ else
+ dur +=
+ (u16) brcms_c_calc_ack_time(wlc, frame_rate,
+ frame_preamble_type);
+ return dur;
+}
+
+static u16 brcms_c_phytxctl1_calc(struct brcms_c_info *wlc, u32 rspec)
+{
+ u16 phyctl1 = 0;
+ u16 bw;
+
+ if (BRCMS_ISLCNPHY(wlc->band)) {
+ bw = PHY_TXC1_BW_20MHZ;
+ } else {
+ bw = rspec_get_bw(rspec);
+ /* 10Mhz is not supported yet */
+ if (bw < PHY_TXC1_BW_20MHZ) {
+ wiphy_err(wlc->wiphy, "phytxctl1_calc: bw %d is "
+ "not supported yet, set to 20L\n", bw);
+ bw = PHY_TXC1_BW_20MHZ;
+ }
+ }
+
+ if (is_mcs_rate(rspec)) {
+ uint mcs = rspec & RSPEC_RATE_MASK;
+
+ /* bw, stf, coding-type is part of rspec_phytxbyte2 returns */
+ phyctl1 = rspec_phytxbyte2(rspec);
+ /* set the upper byte of phyctl1 */
+ phyctl1 |= (mcs_table[mcs].tx_phy_ctl3 << 8);
+ } else if (is_cck_rate(rspec) && !BRCMS_ISLCNPHY(wlc->band)
+ && !BRCMS_ISSSLPNPHY(wlc->band)) {
+ /*
+ * In CCK mode LPPHY overloads OFDM Modulation bits with CCK
+ * Data Rate. Eventually MIMOPHY would also be converted to
+ * this format
+ */
+ /* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */
+ phyctl1 = (bw | (rspec_stf(rspec) << PHY_TXC1_MODE_SHIFT));
+ } else { /* legacy OFDM/CCK */
+ s16 phycfg;
+ /* get the phyctl byte from rate phycfg table */
+ phycfg = brcms_c_rate_legacy_phyctl(rspec2rate(rspec));
+ if (phycfg == -1) {
+ wiphy_err(wlc->wiphy, "phytxctl1_calc: wrong "
+ "legacy OFDM/CCK rate\n");
+ phycfg = 0;
+ }
+ /* set the upper byte of phyctl1 */
+ phyctl1 =
+ (bw | (phycfg << 8) |
+ (rspec_stf(rspec) << PHY_TXC1_MODE_SHIFT));
+ }
+ return phyctl1;
+}
+
+/*
+ * Add struct d11txh, struct cck_phy_hdr.
+ *
+ * 'p' data must start with 802.11 MAC header
+ * 'p' must allow enough bytes of local headers to be "pushed" onto the packet
+ *
+ * headroom == D11_PHY_HDR_LEN + D11_TXH_LEN (D11_TXH_LEN is now 104 bytes)
+ *
+ */
+static u16
+brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw,
+ struct sk_buff *p, struct scb *scb, uint frag,
+ uint nfrags, uint queue, uint next_frag_len)
+{
+ struct ieee80211_hdr *h;
+ struct d11txh *txh;
+ u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN];
+ int len, phylen, rts_phylen;
+ u16 mch, phyctl, xfts, mainrates;
+ u16 seq = 0, mcl = 0, status = 0, frameid = 0;
+ u32 rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M };
+ u32 rts_rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M };
+ bool use_rts = false;
+ bool use_cts = false;
+ bool use_rifs = false;
+ bool short_preamble[2] = { false, false };
+ u8 preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE };
+ u8 rts_preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE };
+ u8 *rts_plcp, rts_plcp_fallback[D11_PHY_HDR_LEN];
+ struct ieee80211_rts *rts = NULL;
+ bool qos;
+ uint ac;
+ bool hwtkmic = false;
+ u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
+#define ANTCFG_NONE 0xFF
+ u8 antcfg = ANTCFG_NONE;
+ u8 fbantcfg = ANTCFG_NONE;
+ uint phyctl1_stf = 0;
+ u16 durid = 0;
+ struct ieee80211_tx_rate *txrate[2];
+ int k;
+ struct ieee80211_tx_info *tx_info;
+ bool is_mcs;
+ u16 mimo_txbw;
+ u8 mimo_preamble_type;
+
+ /* locate 802.11 MAC header */
+ h = (struct ieee80211_hdr *)(p->data);
+ qos = ieee80211_is_data_qos(h->frame_control);
+
+ /* compute length of frame in bytes for use in PLCP computations */
+ len = brcmu_pkttotlen(p);
+ phylen = len + FCS_LEN;
+
+ /* Get tx_info */
+ tx_info = IEEE80211_SKB_CB(p);
+
+ /* add PLCP */
+ plcp = skb_push(p, D11_PHY_HDR_LEN);
+
+ /* add Broadcom tx descriptor header */
+ txh = (struct d11txh *) skb_push(p, D11_TXH_LEN);
+ memset(txh, 0, D11_TXH_LEN);
+
+ /* setup frameid */
+ if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+ /* non-AP STA should never use BCMC queue */
+ if (queue == TX_BCMC_FIFO) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: ASSERT queue == "
+ "TX_BCMC!\n", wlc->pub->unit, __func__);
+ frameid = bcmc_fid_generate(wlc, NULL, txh);
+ } else {
+ /* Increment the counter for first fragment */
+ if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+ scb->seqnum[p->priority]++;
+
+ /* extract fragment number from frame first */
+ seq = le16_to_cpu(h->seq_ctrl) & FRAGNUM_MASK;
+ seq |= (scb->seqnum[p->priority] << SEQNUM_SHIFT);
+ h->seq_ctrl = cpu_to_le16(seq);
+
+ frameid = ((seq << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) |
+ (queue & TXFID_QUEUE_MASK);
+ }
+ }
+ frameid |= queue & TXFID_QUEUE_MASK;
+
+ /* set the ignpmq bit for all pkts tx'd in PS mode and for beacons */
+ if (ieee80211_is_beacon(h->frame_control))
+ mcl |= TXC_IGNOREPMQ;
+
+ txrate[0] = tx_info->control.rates;
+ txrate[1] = txrate[0] + 1;
+
+ /*
+ * if rate control algorithm didn't give us a fallback
+ * rate, use the primary rate
+ */
+ if (txrate[1]->idx < 0)
+ txrate[1] = txrate[0];
+
+ for (k = 0; k < hw->max_rates; k++) {
+ is_mcs = txrate[k]->flags & IEEE80211_TX_RC_MCS ? true : false;
+ if (!is_mcs) {
+ if ((txrate[k]->idx >= 0)
+ && (txrate[k]->idx <
+ hw->wiphy->bands[tx_info->band]->n_bitrates)) {
+ rspec[k] =
+ hw->wiphy->bands[tx_info->band]->
+ bitrates[txrate[k]->idx].hw_value;
+ short_preamble[k] =
+ txrate[k]->
+ flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ?
+ true : false;
+ } else {
+ rspec[k] = BRCM_RATE_1M;
+ }
+ } else {
+ rspec[k] = mac80211_wlc_set_nrate(wlc, wlc->band,
+ NRATE_MCS_INUSE | txrate[k]->idx);
+ }
+
+ /*
+ * Currently only support same setting for primay and
+ * fallback rates. Unify flags for each rate into a
+ * single value for the frame
+ */
+ use_rts |=
+ txrate[k]->
+ flags & IEEE80211_TX_RC_USE_RTS_CTS ? true : false;
+ use_cts |=
+ txrate[k]->
+ flags & IEEE80211_TX_RC_USE_CTS_PROTECT ? true : false;
+
+
+ /*
+ * (1) RATE:
+ * determine and validate primary rate
+ * and fallback rates
+ */
+ if (!rspec_active(rspec[k])) {
+ rspec[k] = BRCM_RATE_1M;
+ } else {
+ if (!is_multicast_ether_addr(h->addr1)) {
+ /* set tx antenna config */
+ brcms_c_antsel_antcfg_get(wlc->asi, false,
+ false, 0, 0, &antcfg, &fbantcfg);
+ }
+ }
+ }
+
+ phyctl1_stf = wlc->stf->ss_opmode;
+
+ if (wlc->pub->_n_enab & SUPPORT_11N) {
+ for (k = 0; k < hw->max_rates; k++) {
+ /*
+ * apply siso/cdd to single stream mcs's or ofdm
+ * if rspec is auto selected
+ */
+ if (((is_mcs_rate(rspec[k]) &&
+ is_single_stream(rspec[k] & RSPEC_RATE_MASK)) ||
+ is_ofdm_rate(rspec[k]))
+ && ((rspec[k] & RSPEC_OVERRIDE_MCS_ONLY)
+ || !(rspec[k] & RSPEC_OVERRIDE))) {
+ rspec[k] &= ~(RSPEC_STF_MASK | RSPEC_STC_MASK);
+
+ /* For SISO MCS use STBC if possible */
+ if (is_mcs_rate(rspec[k])
+ && BRCMS_STF_SS_STBC_TX(wlc, scb)) {
+ u8 stc;
+
+ /* Nss for single stream is always 1 */
+ stc = 1;
+ rspec[k] |= (PHY_TXC1_MODE_STBC <<
+ RSPEC_STF_SHIFT) |
+ (stc << RSPEC_STC_SHIFT);
+ } else
+ rspec[k] |=
+ (phyctl1_stf << RSPEC_STF_SHIFT);
+ }
+
+ /*
+ * Is the phy configured to use 40MHZ frames? If
+ * so then pick the desired txbw
+ */
+ if (brcms_chspec_bw(wlc->chanspec) == BRCMS_40_MHZ) {
+ /* default txbw is 20in40 SB */
+ mimo_ctlchbw = mimo_txbw =
+ CHSPEC_SB_UPPER(wlc_phy_chanspec_get(
+ wlc->band->pi))
+ ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
+
+ if (is_mcs_rate(rspec[k])) {
+ /* mcs 32 must be 40b/w DUP */
+ if ((rspec[k] & RSPEC_RATE_MASK)
+ == 32) {
+ mimo_txbw =
+ PHY_TXC1_BW_40MHZ_DUP;
+ /* use override */
+ } else if (wlc->mimo_40txbw != AUTO)
+ mimo_txbw = wlc->mimo_40txbw;
+ /* else check if dst is using 40 Mhz */
+ else if (scb->flags & SCB_IS40)
+ mimo_txbw = PHY_TXC1_BW_40MHZ;
+ } else if (is_ofdm_rate(rspec[k])) {
+ if (wlc->ofdm_40txbw != AUTO)
+ mimo_txbw = wlc->ofdm_40txbw;
+ } else if (wlc->cck_40txbw != AUTO) {
+ mimo_txbw = wlc->cck_40txbw;
+ }
+ } else {
+ /*
+ * mcs32 is 40 b/w only.
+ * This is possible for probe packets on
+ * a STA during SCAN
+ */
+ if ((rspec[k] & RSPEC_RATE_MASK) == 32)
+ /* mcs 0 */
+ rspec[k] = RSPEC_MIMORATE;
+
+ mimo_txbw = PHY_TXC1_BW_20MHZ;
+ }
+
+ /* Set channel width */
+ rspec[k] &= ~RSPEC_BW_MASK;
+ if ((k == 0) || ((k > 0) && is_mcs_rate(rspec[k])))
+ rspec[k] |= (mimo_txbw << RSPEC_BW_SHIFT);
+ else
+ rspec[k] |= (mimo_ctlchbw << RSPEC_BW_SHIFT);
+
+ /* Disable short GI, not supported yet */
+ rspec[k] &= ~RSPEC_SHORT_GI;
+
+ mimo_preamble_type = BRCMS_MM_PREAMBLE;
+ if (txrate[k]->flags & IEEE80211_TX_RC_GREEN_FIELD)
+ mimo_preamble_type = BRCMS_GF_PREAMBLE;
+
+ if ((txrate[k]->flags & IEEE80211_TX_RC_MCS)
+ && (!is_mcs_rate(rspec[k]))) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: IEEE80211_TX_"
+ "RC_MCS != is_mcs_rate(rspec)\n",
+ wlc->pub->unit, __func__);
+ }
+
+ if (is_mcs_rate(rspec[k])) {
+ preamble_type[k] = mimo_preamble_type;
+
+ /*
+ * if SGI is selected, then forced mm
+ * for single stream
+ */
+ if ((rspec[k] & RSPEC_SHORT_GI)
+ && is_single_stream(rspec[k] &
+ RSPEC_RATE_MASK))
+ preamble_type[k] = BRCMS_MM_PREAMBLE;
+ }
+
+ /* should be better conditionalized */
+ if (!is_mcs_rate(rspec[0])
+ && (tx_info->control.rates[0].
+ flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE))
+ preamble_type[k] = BRCMS_SHORT_PREAMBLE;
+ }
+ } else {
+ for (k = 0; k < hw->max_rates; k++) {
+ /* Set ctrlchbw as 20Mhz */
+ rspec[k] &= ~RSPEC_BW_MASK;
+ rspec[k] |= (PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT);
+
+ /* for nphy, stf of ofdm frames must follow policies */
+ if (BRCMS_ISNPHY(wlc->band) && is_ofdm_rate(rspec[k])) {
+ rspec[k] &= ~RSPEC_STF_MASK;
+ rspec[k] |= phyctl1_stf << RSPEC_STF_SHIFT;
+ }
+ }
+ }
+
+ /* Reset these for use with AMPDU's */
+ txrate[0]->count = 0;
+ txrate[1]->count = 0;
+
+ /* (2) PROTECTION, may change rspec */
+ if ((ieee80211_is_data(h->frame_control) ||
+ ieee80211_is_mgmt(h->frame_control)) &&
+ (phylen > wlc->RTSThresh) && !is_multicast_ether_addr(h->addr1))
+ use_rts = true;
+
+ /* (3) PLCP: determine PLCP header and MAC duration,
+ * fill struct d11txh */
+ brcms_c_compute_plcp(wlc, rspec[0], phylen, plcp);
+ brcms_c_compute_plcp(wlc, rspec[1], phylen, plcp_fallback);
+ memcpy(&txh->FragPLCPFallback,
+ plcp_fallback, sizeof(txh->FragPLCPFallback));
+
+ /* Length field now put in CCK FBR CRC field */
+ if (is_cck_rate(rspec[1])) {
+ txh->FragPLCPFallback[4] = phylen & 0xff;
+ txh->FragPLCPFallback[5] = (phylen & 0xff00) >> 8;
+ }
+
+ /* MIMO-RATE: need validation ?? */
+ mainrates = is_ofdm_rate(rspec[0]) ?
+ D11A_PHY_HDR_GRATE((struct ofdm_phy_hdr *) plcp) :
+ plcp[0];
+
+ /* DUR field for main rate */
+ if (!ieee80211_is_pspoll(h->frame_control) &&
+ !is_multicast_ether_addr(h->addr1) && !use_rifs) {
+ durid =
+ brcms_c_compute_frame_dur(wlc, rspec[0], preamble_type[0],
+ next_frag_len);
+ h->duration_id = cpu_to_le16(durid);
+ } else if (use_rifs) {
+ /* NAV protect to end of next max packet size */
+ durid =
+ (u16) brcms_c_calc_frame_time(wlc, rspec[0],
+ preamble_type[0],
+ DOT11_MAX_FRAG_LEN);
+ durid += RIFS_11N_TIME;
+ h->duration_id = cpu_to_le16(durid);
+ }
+
+ /* DUR field for fallback rate */
+ if (ieee80211_is_pspoll(h->frame_control))
+ txh->FragDurFallback = h->duration_id;
+ else if (is_multicast_ether_addr(h->addr1) || use_rifs)
+ txh->FragDurFallback = 0;
+ else {
+ durid = brcms_c_compute_frame_dur(wlc, rspec[1],
+ preamble_type[1], next_frag_len);
+ txh->FragDurFallback = cpu_to_le16(durid);
+ }
+
+ /* (4) MAC-HDR: MacTxControlLow */
+ if (frag == 0)
+ mcl |= TXC_STARTMSDU;
+
+ if (!is_multicast_ether_addr(h->addr1))
+ mcl |= TXC_IMMEDACK;
+
+ if (wlc->band->bandtype == BRCM_BAND_5G)
+ mcl |= TXC_FREQBAND_5G;
+
+ if (CHSPEC_IS40(wlc_phy_chanspec_get(wlc->band->pi)))
+ mcl |= TXC_BW_40;
+
+ /* set AMIC bit if using hardware TKIP MIC */
+ if (hwtkmic)
+ mcl |= TXC_AMIC;
+
+ txh->MacTxControlLow = cpu_to_le16(mcl);
+
+ /* MacTxControlHigh */
+ mch = 0;
+
+ /* Set fallback rate preamble type */
+ if ((preamble_type[1] == BRCMS_SHORT_PREAMBLE) ||
+ (preamble_type[1] == BRCMS_GF_PREAMBLE)) {
+ if (rspec2rate(rspec[1]) != BRCM_RATE_1M)
+ mch |= TXC_PREAMBLE_DATA_FB_SHORT;
+ }
+
+ /* MacFrameControl */
+ memcpy(&txh->MacFrameControl, &h->frame_control, sizeof(u16));
+ txh->TxFesTimeNormal = cpu_to_le16(0);
+
+ txh->TxFesTimeFallback = cpu_to_le16(0);
+
+ /* TxFrameRA */
+ memcpy(&txh->TxFrameRA, &h->addr1, ETH_ALEN);
+
+ /* TxFrameID */
+ txh->TxFrameID = cpu_to_le16(frameid);
+
+ /*
+ * TxStatus, Note the case of recreating the first frag of a suppressed
+ * frame then we may need to reset the retry cnt's via the status reg
+ */
+ txh->TxStatus = cpu_to_le16(status);
+
+ /*
+ * extra fields for ucode AMPDU aggregation, the new fields are added to
+ * the END of previous structure so that it's compatible in driver.
+ */
+ txh->MaxNMpdus = cpu_to_le16(0);
+ txh->MaxABytes_MRT = cpu_to_le16(0);
+ txh->MaxABytes_FBR = cpu_to_le16(0);
+ txh->MinMBytes = cpu_to_le16(0);
+
+ /* (5) RTS/CTS: determine RTS/CTS PLCP header and MAC duration,
+ * furnish struct d11txh */
+ /* RTS PLCP header and RTS frame */
+ if (use_rts || use_cts) {
+ if (use_rts && use_cts)
+ use_cts = false;
+
+ for (k = 0; k < 2; k++) {
+ rts_rspec[k] = brcms_c_rspec_to_rts_rspec(wlc, rspec[k],
+ false,
+ mimo_ctlchbw);
+ }
+
+ if (!is_ofdm_rate(rts_rspec[0]) &&
+ !((rspec2rate(rts_rspec[0]) == BRCM_RATE_1M) ||
+ (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) {
+ rts_preamble_type[0] = BRCMS_SHORT_PREAMBLE;
+ mch |= TXC_PREAMBLE_RTS_MAIN_SHORT;
+ }
+
+ if (!is_ofdm_rate(rts_rspec[1]) &&
+ !((rspec2rate(rts_rspec[1]) == BRCM_RATE_1M) ||
+ (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) {
+ rts_preamble_type[1] = BRCMS_SHORT_PREAMBLE;
+ mch |= TXC_PREAMBLE_RTS_FB_SHORT;
+ }
+
+ /* RTS/CTS additions to MacTxControlLow */
+ if (use_cts) {
+ txh->MacTxControlLow |= cpu_to_le16(TXC_SENDCTS);
+ } else {
+ txh->MacTxControlLow |= cpu_to_le16(TXC_SENDRTS);
+ txh->MacTxControlLow |= cpu_to_le16(TXC_LONGFRAME);
+ }
+
+ /* RTS PLCP header */
+ rts_plcp = txh->RTSPhyHeader;
+ if (use_cts)
+ rts_phylen = DOT11_CTS_LEN + FCS_LEN;
+ else
+ rts_phylen = DOT11_RTS_LEN + FCS_LEN;
+
+ brcms_c_compute_plcp(wlc, rts_rspec[0], rts_phylen, rts_plcp);
+
+ /* fallback rate version of RTS PLCP header */
+ brcms_c_compute_plcp(wlc, rts_rspec[1], rts_phylen,
+ rts_plcp_fallback);
+ memcpy(&txh->RTSPLCPFallback, rts_plcp_fallback,
+ sizeof(txh->RTSPLCPFallback));
+
+ /* RTS frame fields... */
+ rts = (struct ieee80211_rts *)&txh->rts_frame;
+
+ durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec[0],
+ rspec[0], rts_preamble_type[0],
+ preamble_type[0], phylen, false);
+ rts->duration = cpu_to_le16(durid);
+ /* fallback rate version of RTS DUR field */
+ durid = brcms_c_compute_rtscts_dur(wlc, use_cts,
+ rts_rspec[1], rspec[1],
+ rts_preamble_type[1],
+ preamble_type[1], phylen, false);
+ txh->RTSDurFallback = cpu_to_le16(durid);
+
+ if (use_cts) {
+ rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_CTS);
+
+ memcpy(&rts->ra, &h->addr2, ETH_ALEN);
+ } else {
+ rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_RTS);
+
+ memcpy(&rts->ra, &h->addr1, 2 * ETH_ALEN);
+ }
+
+ /* mainrate
+ * low 8 bits: main frag rate/mcs,
+ * high 8 bits: rts/cts rate/mcs
+ */
+ mainrates |= (is_ofdm_rate(rts_rspec[0]) ?
+ D11A_PHY_HDR_GRATE(
+ (struct ofdm_phy_hdr *) rts_plcp) :
+ rts_plcp[0]) << 8;
+ } else {
+ memset((char *)txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN);
+ memset((char *)&txh->rts_frame, 0,
+ sizeof(struct ieee80211_rts));
+ memset((char *)txh->RTSPLCPFallback, 0,
+ sizeof(txh->RTSPLCPFallback));
+ txh->RTSDurFallback = 0;
+ }
+
+#ifdef SUPPORT_40MHZ
+ /* add null delimiter count */
+ if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && is_mcs_rate(rspec))
+ txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] =
+ brcm_c_ampdu_null_delim_cnt(wlc->ampdu, scb, rspec, phylen);
+
+#endif
+
+ /*
+ * Now that RTS/RTS FB preamble types are updated, write
+ * the final value
+ */
+ txh->MacTxControlHigh = cpu_to_le16(mch);
+
+ /*
+ * MainRates (both the rts and frag plcp rates have
+ * been calculated now)
+ */
+ txh->MainRates = cpu_to_le16(mainrates);
+
+ /* XtraFrameTypes */
+ xfts = frametype(rspec[1], wlc->mimoft);
+ xfts |= (frametype(rts_rspec[0], wlc->mimoft) << XFTS_RTS_FT_SHIFT);
+ xfts |= (frametype(rts_rspec[1], wlc->mimoft) << XFTS_FBRRTS_FT_SHIFT);
+ xfts |= CHSPEC_CHANNEL(wlc_phy_chanspec_get(wlc->band->pi)) <<
+ XFTS_CHANNEL_SHIFT;
+ txh->XtraFrameTypes = cpu_to_le16(xfts);
+
+ /* PhyTxControlWord */
+ phyctl = frametype(rspec[0], wlc->mimoft);
+ if ((preamble_type[0] == BRCMS_SHORT_PREAMBLE) ||
+ (preamble_type[0] == BRCMS_GF_PREAMBLE)) {
+ if (rspec2rate(rspec[0]) != BRCM_RATE_1M)
+ phyctl |= PHY_TXC_SHORT_HDR;
+ }
+
+ /* phytxant is properly bit shifted */
+ phyctl |= brcms_c_stf_d11hdrs_phyctl_txant(wlc, rspec[0]);
+ txh->PhyTxControlWord = cpu_to_le16(phyctl);
+
+ /* PhyTxControlWord_1 */
+ if (BRCMS_PHY_11N_CAP(wlc->band)) {
+ u16 phyctl1 = 0;
+
+ phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[0]);
+ txh->PhyTxControlWord_1 = cpu_to_le16(phyctl1);
+ phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[1]);
+ txh->PhyTxControlWord_1_Fbr = cpu_to_le16(phyctl1);
+
+ if (use_rts || use_cts) {
+ phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[0]);
+ txh->PhyTxControlWord_1_Rts = cpu_to_le16(phyctl1);
+ phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[1]);
+ txh->PhyTxControlWord_1_FbrRts = cpu_to_le16(phyctl1);
+ }
+
+ /*
+ * For mcs frames, if mixedmode(overloaded with long preamble)
+ * is going to be set, fill in non-zero MModeLen and/or
+ * MModeFbrLen it will be unnecessary if they are separated
+ */
+ if (is_mcs_rate(rspec[0]) &&
+ (preamble_type[0] == BRCMS_MM_PREAMBLE)) {
+ u16 mmodelen =
+ brcms_c_calc_lsig_len(wlc, rspec[0], phylen);
+ txh->MModeLen = cpu_to_le16(mmodelen);
+ }
+
+ if (is_mcs_rate(rspec[1]) &&
+ (preamble_type[1] == BRCMS_MM_PREAMBLE)) {
+ u16 mmodefbrlen =
+ brcms_c_calc_lsig_len(wlc, rspec[1], phylen);
+ txh->MModeFbrLen = cpu_to_le16(mmodefbrlen);
+ }
+ }
+
+ ac = skb_get_queue_mapping(p);
+ if ((scb->flags & SCB_WMECAP) && qos && wlc->edcf_txop[ac]) {
+ uint frag_dur, dur, dur_fallback;
+
+ /* WME: Update TXOP threshold */
+ if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU) && frag == 0) {
+ frag_dur =
+ brcms_c_calc_frame_time(wlc, rspec[0],
+ preamble_type[0], phylen);
+
+ if (rts) {
+ /* 1 RTS or CTS-to-self frame */
+ dur =
+ brcms_c_calc_cts_time(wlc, rts_rspec[0],
+ rts_preamble_type[0]);
+ dur_fallback =
+ brcms_c_calc_cts_time(wlc, rts_rspec[1],
+ rts_preamble_type[1]);
+ /* (SIFS + CTS) + SIFS + frame + SIFS + ACK */
+ dur += le16_to_cpu(rts->duration);
+ dur_fallback +=
+ le16_to_cpu(txh->RTSDurFallback);
+ } else if (use_rifs) {
+ dur = frag_dur;
+ dur_fallback = 0;
+ } else {
+ /* frame + SIFS + ACK */
+ dur = frag_dur;
+ dur +=
+ brcms_c_compute_frame_dur(wlc, rspec[0],
+ preamble_type[0], 0);
+
+ dur_fallback =
+ brcms_c_calc_frame_time(wlc, rspec[1],
+ preamble_type[1],
+ phylen);
+ dur_fallback +=
+ brcms_c_compute_frame_dur(wlc, rspec[1],
+ preamble_type[1], 0);
+ }
+ /* NEED to set TxFesTimeNormal (hard) */
+ txh->TxFesTimeNormal = cpu_to_le16((u16) dur);
+ /*
+ * NEED to set fallback rate version of
+ * TxFesTimeNormal (hard)
+ */
+ txh->TxFesTimeFallback =
+ cpu_to_le16((u16) dur_fallback);
+
+ /*
+ * update txop byte threshold (txop minus intraframe
+ * overhead)
+ */
+ if (wlc->edcf_txop[ac] >= (dur - frag_dur)) {
+ uint newfragthresh;
+
+ newfragthresh =
+ brcms_c_calc_frame_len(wlc,
+ rspec[0], preamble_type[0],
+ (wlc->edcf_txop[ac] -
+ (dur - frag_dur)));
+ /* range bound the fragthreshold */
+ if (newfragthresh < DOT11_MIN_FRAG_LEN)
+ newfragthresh =
+ DOT11_MIN_FRAG_LEN;
+ else if (newfragthresh >
+ wlc->usr_fragthresh)
+ newfragthresh =
+ wlc->usr_fragthresh;
+ /* update the fragthresh and do txc update */
+ if (wlc->fragthresh[queue] !=
+ (u16) newfragthresh)
+ wlc->fragthresh[queue] =
+ (u16) newfragthresh;
+ } else {
+ wiphy_err(wlc->wiphy, "wl%d: %s txop invalid "
+ "for rate %d\n",
+ wlc->pub->unit, fifo_names[queue],
+ rspec2rate(rspec[0]));
+ }
+
+ if (dur > wlc->edcf_txop[ac])
+ wiphy_err(wlc->wiphy, "wl%d: %s: %s txop "
+ "exceeded phylen %d/%d dur %d/%d\n",
+ wlc->pub->unit, __func__,
+ fifo_names[queue],
+ phylen, wlc->fragthresh[queue],
+ dur, wlc->edcf_txop[ac]);
+ }
+ }
+
+ return 0;
+}
+
+void brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu,
+ struct ieee80211_hw *hw)
+{
+ u8 prio;
+ uint fifo;
+ struct scb *scb = &wlc->pri_scb;
+ struct ieee80211_hdr *d11_header = (struct ieee80211_hdr *)(sdu->data);
+
+ /*
+ * 802.11 standard requires management traffic
+ * to go at highest priority
+ */
+ prio = ieee80211_is_data(d11_header->frame_control) ? sdu->priority :
+ MAXPRIO;
+ fifo = prio2fifo[prio];
+ if (brcms_c_d11hdrs_mac80211(wlc, hw, sdu, scb, 0, 1, fifo, 0))
+ return;
+ brcms_c_txq_enq(wlc, scb, sdu, BRCMS_PRIO_TO_PREC(prio));
+ brcms_c_send_q(wlc);
+}
+
+void brcms_c_send_q(struct brcms_c_info *wlc)
+{
+ struct sk_buff *pkt[DOT11_MAXNUMFRAGS];
+ int prec;
+ u16 prec_map;
+ int err = 0, i, count;
+ uint fifo;
+ struct brcms_txq_info *qi = wlc->pkt_queue;
+ struct pktq *q = &qi->q;
+ struct ieee80211_tx_info *tx_info;
+
+ prec_map = wlc->tx_prec_map;
+
+ /* Send all the enq'd pkts that we can.
+ * Dequeue packets with precedence with empty HW fifo only
+ */
+ while (prec_map && (pkt[0] = brcmu_pktq_mdeq(q, prec_map, &prec))) {
+ tx_info = IEEE80211_SKB_CB(pkt[0]);
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+ err = brcms_c_sendampdu(wlc->ampdu, qi, pkt, prec);
+ } else {
+ count = 1;
+ err = brcms_c_prep_pdu(wlc, pkt[0], &fifo);
+ if (!err) {
+ for (i = 0; i < count; i++)
+ brcms_c_txfifo(wlc, fifo, pkt[i], true,
+ 1);
+ }
+ }
+
+ if (err == -EBUSY) {
+ brcmu_pktq_penq_head(q, prec, pkt[0]);
+ /*
+ * If send failed due to any other reason than a
+ * change in HW FIFO condition, quit. Otherwise,
+ * read the new prec_map!
+ */
+ if (prec_map == wlc->tx_prec_map)
+ break;
+ prec_map = wlc->tx_prec_map;
+ }
+ }
+}
+
+void
+brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p,
+ bool commit, s8 txpktpend)
+{
+ u16 frameid = INVALIDFID;
+ struct d11txh *txh;
+
+ txh = (struct d11txh *) (p->data);
+
+ /* When a BC/MC frame is being committed to the BCMC fifo
+ * via DMA (NOT PIO), update ucode or BSS info as appropriate.
+ */
+ if (fifo == TX_BCMC_FIFO)
+ frameid = le16_to_cpu(txh->TxFrameID);
+
+ /*
+ * Bump up pending count for if not using rpc. If rpc is
+ * used, this will be handled in brcms_b_txfifo()
+ */
+ if (commit) {
+ wlc->core->txpktpend[fifo] += txpktpend;
+ BCMMSG(wlc->wiphy, "pktpend inc %d to %d\n",
+ txpktpend, wlc->core->txpktpend[fifo]);
+ }
+
+ /* Commit BCMC sequence number in the SHM frame ID location */
+ if (frameid != INVALIDFID) {
+ /*
+ * To inform the ucode of the last mcast frame posted
+ * so that it can clear moredata bit
+ */
+ brcms_b_write_shm(wlc->hw, M_BCMC_FID, frameid);
+ }
+
+ if (dma_txfast(wlc->hw->di[fifo], p, commit) < 0)
+ wiphy_err(wlc->wiphy, "txfifo: fatal, toss frames !!!\n");
+}
+
+u32
+brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec,
+ bool use_rspec, u16 mimo_ctlchbw)
+{
+ u32 rts_rspec = 0;
+
+ if (use_rspec)
+ /* use frame rate as rts rate */
+ rts_rspec = rspec;
+ else if (wlc->band->gmode && wlc->protection->_g && !is_cck_rate(rspec))
+ /* Use 11Mbps as the g protection RTS target rate and fallback.
+ * Use the brcms_basic_rate() lookup to find the best basic rate
+ * under the target in case 11 Mbps is not Basic.
+ * 6 and 9 Mbps are not usually selected by rate selection, but
+ * even if the OFDM rate we are protecting is 6 or 9 Mbps, 11
+ * is more robust.
+ */
+ rts_rspec = brcms_basic_rate(wlc, BRCM_RATE_11M);
+ else
+ /* calculate RTS rate and fallback rate based on the frame rate
+ * RTS must be sent at a basic rate since it is a
+ * control frame, sec 9.6 of 802.11 spec
+ */
+ rts_rspec = brcms_basic_rate(wlc, rspec);
+
+ if (BRCMS_PHY_11N_CAP(wlc->band)) {
+ /* set rts txbw to correct side band */
+ rts_rspec &= ~RSPEC_BW_MASK;
+
+ /*
+ * if rspec/rspec_fallback is 40MHz, then send RTS on both
+ * 20MHz channel (DUP), otherwise send RTS on control channel
+ */
+ if (rspec_is40mhz(rspec) && !is_cck_rate(rts_rspec))
+ rts_rspec |= (PHY_TXC1_BW_40MHZ_DUP << RSPEC_BW_SHIFT);
+ else
+ rts_rspec |= (mimo_ctlchbw << RSPEC_BW_SHIFT);
+
+ /* pick siso/cdd as default for ofdm */
+ if (is_ofdm_rate(rts_rspec)) {
+ rts_rspec &= ~RSPEC_STF_MASK;
+ rts_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT);
+ }
+ }
+ return rts_rspec;
+}
+
+void
+brcms_c_txfifo_complete(struct brcms_c_info *wlc, uint fifo, s8 txpktpend)
+{
+ wlc->core->txpktpend[fifo] -= txpktpend;
+ BCMMSG(wlc->wiphy, "pktpend dec %d to %d\n", txpktpend,
+ wlc->core->txpktpend[fifo]);
+
+ /* There is more room; mark precedences related to this FIFO sendable */
+ wlc->tx_prec_map |= wlc->fifo2prec_map[fifo];
+
+ /* figure out which bsscfg is being worked on... */
+}
+
+/* Update beacon listen interval in shared memory */
+static void brcms_c_bcn_li_upd(struct brcms_c_info *wlc)
+{
+ /* wake up every DTIM is the default */
+ if (wlc->bcn_li_dtim == 1)
+ brcms_b_write_shm(wlc->hw, M_BCN_LI, 0);
+ else
+ brcms_b_write_shm(wlc->hw, M_BCN_LI,
+ (wlc->bcn_li_dtim << 8) | wlc->bcn_li_bcn);
+}
+
+static void
+brcms_b_read_tsf(struct brcms_hardware *wlc_hw, u32 *tsf_l_ptr,
+ u32 *tsf_h_ptr)
+{
+ struct d11regs __iomem *regs = wlc_hw->regs;
+
+ /* read the tsf timer low, then high to get an atomic read */
+ *tsf_l_ptr = R_REG(&regs->tsf_timerlow);
+ *tsf_h_ptr = R_REG(&regs->tsf_timerhigh);
+}
+
+/*
+ * recover 64bit TSF value from the 16bit TSF value in the rx header
+ * given the assumption that the TSF passed in header is within 65ms
+ * of the current tsf.
+ *
+ * 6 5 4 4 3 2 1
+ * 3.......6.......8.......0.......2.......4.......6.......8......0
+ * |<---------- tsf_h ----------->||<--- tsf_l -->||<-RxTSFTime ->|
+ *
+ * The RxTSFTime are the lowest 16 bits and provided by the ucode. The
+ * tsf_l is filled in by brcms_b_recv, which is done earlier in the
+ * receive call sequence after rx interrupt. Only the higher 16 bits
+ * are used. Finally, the tsf_h is read from the tsf register.
+ */
+static u64 brcms_c_recover_tsf64(struct brcms_c_info *wlc,
+ struct d11rxhdr *rxh)
+{
+ u32 tsf_h, tsf_l;
+ u16 rx_tsf_0_15, rx_tsf_16_31;
+
+ brcms_b_read_tsf(wlc->hw, &tsf_l, &tsf_h);
+
+ rx_tsf_16_31 = (u16)(tsf_l >> 16);
+ rx_tsf_0_15 = rxh->RxTSFTime;
+
+ /*
+ * a greater tsf time indicates the low 16 bits of
+ * tsf_l wrapped, so decrement the high 16 bits.
+ */
+ if ((u16)tsf_l < rx_tsf_0_15) {
+ rx_tsf_16_31 -= 1;
+ if (rx_tsf_16_31 == 0xffff)
+ tsf_h -= 1;
+ }
+
+ return ((u64)tsf_h << 32) | (((u32)rx_tsf_16_31 << 16) + rx_tsf_0_15);
+}
+
+static void
+prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh,
+ struct sk_buff *p,
+ struct ieee80211_rx_status *rx_status)
+{
+ int preamble;
+ int channel;
+ u32 rspec;
+ unsigned char *plcp;
+
+ /* fill in TSF and flag its presence */
+ rx_status->mactime = brcms_c_recover_tsf64(wlc, rxh);
+ rx_status->flag |= RX_FLAG_MACTIME_MPDU;
+
+ channel = BRCMS_CHAN_CHANNEL(rxh->RxChan);
+
+ if (channel > 14) {
+ rx_status->band = IEEE80211_BAND_5GHZ;
+ rx_status->freq = ieee80211_ofdm_chan_to_freq(
+ WF_CHAN_FACTOR_5_G/2, channel);
+
+ } else {
+ rx_status->band = IEEE80211_BAND_2GHZ;
+ rx_status->freq = ieee80211_dsss_chan_to_freq(channel);
+ }
+
+ rx_status->signal = wlc_phy_rssi_compute(wlc->hw->band->pi, rxh);
+
+ /* noise */
+ /* qual */
+ rx_status->antenna =
+ (rxh->PhyRxStatus_0 & PRXS0_RXANT_UPSUBBAND) ? 1 : 0;
+
+ plcp = p->data;
+
+ rspec = brcms_c_compute_rspec(rxh, plcp);
+ if (is_mcs_rate(rspec)) {
+ rx_status->rate_idx = rspec & RSPEC_RATE_MASK;
+ rx_status->flag |= RX_FLAG_HT;
+ if (rspec_is40mhz(rspec))
+ rx_status->flag |= RX_FLAG_40MHZ;
+ } else {
+ switch (rspec2rate(rspec)) {
+ case BRCM_RATE_1M:
+ rx_status->rate_idx = 0;
+ break;
+ case BRCM_RATE_2M:
+ rx_status->rate_idx = 1;
+ break;
+ case BRCM_RATE_5M5:
+ rx_status->rate_idx = 2;
+ break;
+ case BRCM_RATE_11M:
+ rx_status->rate_idx = 3;
+ break;
+ case BRCM_RATE_6M:
+ rx_status->rate_idx = 4;
+ break;
+ case BRCM_RATE_9M:
+ rx_status->rate_idx = 5;
+ break;
+ case BRCM_RATE_12M:
+ rx_status->rate_idx = 6;
+ break;
+ case BRCM_RATE_18M:
+ rx_status->rate_idx = 7;
+ break;
+ case BRCM_RATE_24M:
+ rx_status->rate_idx = 8;
+ break;
+ case BRCM_RATE_36M:
+ rx_status->rate_idx = 9;
+ break;
+ case BRCM_RATE_48M:
+ rx_status->rate_idx = 10;
+ break;
+ case BRCM_RATE_54M:
+ rx_status->rate_idx = 11;
+ break;
+ default:
+ wiphy_err(wlc->wiphy, "%s: Unknown rate\n", __func__);
+ }
+
+ /*
+ * For 5GHz, we should decrease the index as it is
+ * a subset of the 2.4G rates. See bitrates field
+ * of brcms_band_5GHz_nphy (in mac80211_if.c).
+ */
+ if (rx_status->band == IEEE80211_BAND_5GHZ)
+ rx_status->rate_idx -= BRCMS_LEGACY_5G_RATE_OFFSET;
+
+ /* Determine short preamble and rate_idx */
+ preamble = 0;
+ if (is_cck_rate(rspec)) {
+ if (rxh->PhyRxStatus_0 & PRXS0_SHORTH)
+ rx_status->flag |= RX_FLAG_SHORTPRE;
+ } else if (is_ofdm_rate(rspec)) {
+ rx_status->flag |= RX_FLAG_SHORTPRE;
+ } else {
+ wiphy_err(wlc->wiphy, "%s: Unknown modulation\n",
+ __func__);
+ }
+ }
+
+ if (plcp3_issgi(plcp[3]))
+ rx_status->flag |= RX_FLAG_SHORT_GI;
+
+ if (rxh->RxStatus1 & RXS_DECERR) {
+ rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+ wiphy_err(wlc->wiphy, "%s: RX_FLAG_FAILED_PLCP_CRC\n",
+ __func__);
+ }
+ if (rxh->RxStatus1 & RXS_FCSERR) {
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ wiphy_err(wlc->wiphy, "%s: RX_FLAG_FAILED_FCS_CRC\n",
+ __func__);
+ }
+}
+
+static void
+brcms_c_recvctl(struct brcms_c_info *wlc, struct d11rxhdr *rxh,
+ struct sk_buff *p)
+{
+ int len_mpdu;
+ struct ieee80211_rx_status rx_status;
+
+ memset(&rx_status, 0, sizeof(rx_status));
+ prep_mac80211_status(wlc, rxh, p, &rx_status);
+
+ /* mac header+body length, exclude CRC and plcp header */
+ len_mpdu = p->len - D11_PHY_HDR_LEN - FCS_LEN;
+ skb_pull(p, D11_PHY_HDR_LEN);
+ __skb_trim(p, len_mpdu);
+
+ memcpy(IEEE80211_SKB_RXCB(p), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(wlc->pub->ieee_hw, p);
+}
+
+/* calculate frame duration for Mixed-mode L-SIG spoofing, return
+ * number of bytes goes in the length field
+ *
+ * Formula given by HT PHY Spec v 1.13
+ * len = 3(nsyms + nstream + 3) - 3
+ */
+u16
+brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec,
+ uint mac_len)
+{
+ uint nsyms, len = 0, kNdps;
+
+ BCMMSG(wlc->wiphy, "wl%d: rate %d, len%d\n",
+ wlc->pub->unit, rspec2rate(ratespec), mac_len);
+
+ if (is_mcs_rate(ratespec)) {
+ uint mcs = ratespec & RSPEC_RATE_MASK;
+ int tot_streams = (mcs_2_txstreams(mcs) + 1) +
+ rspec_stc(ratespec);
+
+ /*
+ * the payload duration calculation matches that
+ * of regular ofdm
+ */
+ /* 1000Ndbps = kbps * 4 */
+ kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec),
+ rspec_issgi(ratespec)) * 4;
+
+ if (rspec_stc(ratespec) == 0)
+ nsyms =
+ CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
+ APHY_TAIL_NBITS) * 1000, kNdps);
+ else
+ /* STBC needs to have even number of symbols */
+ nsyms =
+ 2 *
+ CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
+ APHY_TAIL_NBITS) * 1000, 2 * kNdps);
+
+ /* (+3) account for HT-SIG(2) and HT-STF(1) */
+ nsyms += (tot_streams + 3);
+ /*
+ * 3 bytes/symbol @ legacy 6Mbps rate
+ * (-3) excluding service bits and tail bits
+ */
+ len = (3 * nsyms) - 3;
+ }
+
+ return (u16) len;
+}
+
+static void
+brcms_c_mod_prb_rsp_rate_table(struct brcms_c_info *wlc, uint frame_len)
+{
+ const struct brcms_c_rateset *rs_dflt;
+ struct brcms_c_rateset rs;
+ u8 rate;
+ u16 entry_ptr;
+ u8 plcp[D11_PHY_HDR_LEN];
+ u16 dur, sifs;
+ uint i;
+
+ sifs = get_sifs(wlc->band);
+
+ rs_dflt = brcms_c_rateset_get_hwrs(wlc);
+
+ brcms_c_rateset_copy(rs_dflt, &rs);
+ brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams);
+
+ /*
+ * walk the phy rate table and update MAC core SHM
+ * basic rate table entries
+ */
+ for (i = 0; i < rs.count; i++) {
+ rate = rs.rates[i] & BRCMS_RATE_MASK;
+
+ entry_ptr = brcms_b_rate_shm_offset(wlc->hw, rate);
+
+ /* Calculate the Probe Response PLCP for the given rate */
+ brcms_c_compute_plcp(wlc, rate, frame_len, plcp);
+
+ /*
+ * Calculate the duration of the Probe Response
+ * frame plus SIFS for the MAC
+ */
+ dur = (u16) brcms_c_calc_frame_time(wlc, rate,
+ BRCMS_LONG_PREAMBLE, frame_len);
+ dur += sifs;
+
+ /* Update the SHM Rate Table entry Probe Response values */
+ brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS,
+ (u16) (plcp[0] + (plcp[1] << 8)));
+ brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS + 2,
+ (u16) (plcp[2] + (plcp[3] << 8)));
+ brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_DUR_POS, dur);
+ }
+}
+
+/* Max buffering needed for beacon template/prb resp template is 142 bytes.
+ *
+ * PLCP header is 6 bytes.
+ * 802.11 A3 header is 24 bytes.
+ * Max beacon frame body template length is 112 bytes.
+ * Max probe resp frame body template length is 110 bytes.
+ *
+ * *len on input contains the max length of the packet available.
+ *
+ * The *len value is set to the number of bytes in buf used, and starts
+ * with the PLCP and included up to, but not including, the 4 byte FCS.
+ */
+static void
+brcms_c_bcn_prb_template(struct brcms_c_info *wlc, u16 type,
+ u32 bcn_rspec,
+ struct brcms_bss_cfg *cfg, u16 *buf, int *len)
+{
+ static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255};
+ struct cck_phy_hdr *plcp;
+ struct ieee80211_mgmt *h;
+ int hdr_len, body_len;
+
+ hdr_len = D11_PHY_HDR_LEN + DOT11_MAC_HDR_LEN;
+
+ /* calc buffer size provided for frame body */
+ body_len = *len - hdr_len;
+ /* return actual size */
+ *len = hdr_len + body_len;
+
+ /* format PHY and MAC headers */
+ memset((char *)buf, 0, hdr_len);
+
+ plcp = (struct cck_phy_hdr *) buf;
+
+ /*
+ * PLCP for Probe Response frames are filled in from
+ * core's rate table
+ */
+ if (type == IEEE80211_STYPE_BEACON)
+ /* fill in PLCP */
+ brcms_c_compute_plcp(wlc, bcn_rspec,
+ (DOT11_MAC_HDR_LEN + body_len + FCS_LEN),
+ (u8 *) plcp);
+
+ /* "Regular" and 16 MBSS but not for 4 MBSS */
+ /* Update the phytxctl for the beacon based on the rspec */
+ brcms_c_beacon_phytxctl_txant_upd(wlc, bcn_rspec);
+
+ h = (struct ieee80211_mgmt *)&plcp[1];
+
+ /* fill in 802.11 header */
+ h->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | type);
+
+ /* DUR is 0 for multicast bcn, or filled in by MAC for prb resp */
+ /* A1 filled in by MAC for prb resp, broadcast for bcn */
+ if (type == IEEE80211_STYPE_BEACON)
+ memcpy(&h->da, &ether_bcast, ETH_ALEN);
+ memcpy(&h->sa, &cfg->cur_etheraddr, ETH_ALEN);
+ memcpy(&h->bssid, &cfg->BSSID, ETH_ALEN);
+
+ /* SEQ filled in by MAC */
+}
+
+int brcms_c_get_header_len(void)
+{
+ return TXOFF;
+}
+
+/*
+ * Update all beacons for the system.
+ */
+void brcms_c_update_beacon(struct brcms_c_info *wlc)
+{
+ struct brcms_bss_cfg *bsscfg = wlc->bsscfg;
+
+ if (bsscfg->up && !bsscfg->BSS)
+ /* Clear the soft intmask */
+ wlc->defmacintmask &= ~MI_BCNTPL;
+}
+
+/* Write ssid into shared memory */
+static void
+brcms_c_shm_ssid_upd(struct brcms_c_info *wlc, struct brcms_bss_cfg *cfg)
+{
+ u8 *ssidptr = cfg->SSID;
+ u16 base = M_SSID;
+ u8 ssidbuf[IEEE80211_MAX_SSID_LEN];
+
+ /* padding the ssid with zero and copy it into shm */
+ memset(ssidbuf, 0, IEEE80211_MAX_SSID_LEN);
+ memcpy(ssidbuf, ssidptr, cfg->SSID_len);
+
+ brcms_c_copyto_shm(wlc, base, ssidbuf, IEEE80211_MAX_SSID_LEN);
+ brcms_b_write_shm(wlc->hw, M_SSIDLEN, (u16) cfg->SSID_len);
+}
+
+static void
+brcms_c_bss_update_probe_resp(struct brcms_c_info *wlc,
+ struct brcms_bss_cfg *cfg,
+ bool suspend)
+{
+ u16 prb_resp[BCN_TMPL_LEN / 2];
+ int len = BCN_TMPL_LEN;
+
+ /*
+ * write the probe response to hardware, or save in
+ * the config structure
+ */
+
+ /* create the probe response template */
+ brcms_c_bcn_prb_template(wlc, IEEE80211_STYPE_PROBE_RESP, 0,
+ cfg, prb_resp, &len);
+
+ if (suspend)
+ brcms_c_suspend_mac_and_wait(wlc);
+
+ /* write the probe response into the template region */
+ brcms_b_write_template_ram(wlc->hw, T_PRS_TPL_BASE,
+ (len + 3) & ~3, prb_resp);
+
+ /* write the length of the probe response frame (+PLCP/-FCS) */
+ brcms_b_write_shm(wlc->hw, M_PRB_RESP_FRM_LEN, (u16) len);
+
+ /* write the SSID and SSID length */
+ brcms_c_shm_ssid_upd(wlc, cfg);
+
+ /*
+ * Write PLCP headers and durations for probe response frames
+ * at all rates. Use the actual frame length covered by the
+ * PLCP header for the call to brcms_c_mod_prb_rsp_rate_table()
+ * by subtracting the PLCP len and adding the FCS.
+ */
+ len += (-D11_PHY_HDR_LEN + FCS_LEN);
+ brcms_c_mod_prb_rsp_rate_table(wlc, (u16) len);
+
+ if (suspend)
+ brcms_c_enable_mac(wlc);
+}
+
+void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend)
+{
+ struct brcms_bss_cfg *bsscfg = wlc->bsscfg;
+
+ /* update AP or IBSS probe responses */
+ if (bsscfg->up && !bsscfg->BSS)
+ brcms_c_bss_update_probe_resp(wlc, bsscfg, suspend);
+}
+
+/* prepares pdu for transmission. returns BCM error codes */
+int brcms_c_prep_pdu(struct brcms_c_info *wlc, struct sk_buff *pdu, uint *fifop)
+{
+ uint fifo;
+ struct d11txh *txh;
+ struct ieee80211_hdr *h;
+ struct scb *scb;
+
+ txh = (struct d11txh *) (pdu->data);
+ h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
+
+ /* get the pkt queue info. This was put at brcms_c_sendctl or
+ * brcms_c_send for PDU */
+ fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK;
+
+ scb = NULL;
+
+ *fifop = fifo;
+
+ /* return if insufficient dma resources */
+ if (*wlc->core->txavail[fifo] < MAX_DMA_SEGS) {
+ /* Mark precedences related to this FIFO, unsendable */
+ /* A fifo is full. Clear precedences related to that FIFO */
+ wlc->tx_prec_map &= ~(wlc->fifo2prec_map[fifo]);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo,
+ uint *blocks)
+{
+ if (fifo >= NFIFO)
+ return -EINVAL;
+
+ *blocks = wlc_hw->xmtfifo_sz[fifo];
+
+ return 0;
+}
+
+void
+brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset,
+ const u8 *addr)
+{
+ brcms_b_set_addrmatch(wlc->hw, match_reg_offset, addr);
+ if (match_reg_offset == RCM_BSSID_OFFSET)
+ memcpy(wlc->bsscfg->BSSID, addr, ETH_ALEN);
+}
+
+/*
+ * Flag 'scan in progress' to withhold dynamic phy calibration
+ */
+void brcms_c_scan_start(struct brcms_c_info *wlc)
+{
+ wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, true);
+}
+
+void brcms_c_scan_stop(struct brcms_c_info *wlc)
+{
+ wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, false);
+}
+
+void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state)
+{
+ wlc->pub->associated = state;
+ wlc->bsscfg->associated = state;
+}
+
+/*
+ * When a remote STA/AP is removed by Mac80211, or when it can no longer accept
+ * AMPDU traffic, packets pending in hardware have to be invalidated so that
+ * when later on hardware releases them, they can be handled appropriately.
+ */
+void brcms_c_inval_dma_pkts(struct brcms_hardware *hw,
+ struct ieee80211_sta *sta,
+ void (*dma_callback_fn))
+{
+ struct dma_pub *dmah;
+ int i;
+ for (i = 0; i < NFIFO; i++) {
+ dmah = hw->di[i];
+ if (dmah != NULL)
+ dma_walk_packets(dmah, dma_callback_fn, sta);
+ }
+}
+
+int brcms_c_get_curband(struct brcms_c_info *wlc)
+{
+ return wlc->band->bandunit;
+}
+
+void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, bool drop)
+{
+ /* flush packet queue when requested */
+ if (drop)
+ brcmu_pktq_flush(&wlc->pkt_queue->q, false, NULL, NULL);
+
+ /* wait for queue and DMA fifos to run dry */
+ while (!pktq_empty(&wlc->pkt_queue->q) || brcms_txpktpendtot(wlc) > 0)
+ brcms_msleep(wlc->wl, 1);
+}
+
+void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval)
+{
+ wlc->bcn_li_bcn = interval;
+ if (wlc->pub->up)
+ brcms_c_bcn_li_upd(wlc);
+}
+
+int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr)
+{
+ uint qdbm;
+
+ /* Remove override bit and clip to max qdbm value */
+ qdbm = min_t(uint, txpwr * BRCMS_TXPWR_DB_FACTOR, 0xff);
+ return wlc_phy_txpower_set(wlc->band->pi, qdbm, false);
+}
+
+int brcms_c_get_tx_power(struct brcms_c_info *wlc)
+{
+ uint qdbm;
+ bool override;
+
+ wlc_phy_txpower_get(wlc->band->pi, &qdbm, &override);
+
+ /* Return qdbm units */
+ return (int)(qdbm / BRCMS_TXPWR_DB_FACTOR);
+}
+
+void brcms_c_set_radio_mpc(struct brcms_c_info *wlc, bool mpc)
+{
+ wlc->mpc = mpc;
+ brcms_c_radio_mpc_upd(wlc);
+}
+
+/* Process received frames */
+/*
+ * Return true if more frames need to be processed. false otherwise.
+ * Param 'bound' indicates max. # frames to process before break out.
+ */
+static void brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p)
+{
+ struct d11rxhdr *rxh;
+ struct ieee80211_hdr *h;
+ uint len;
+ bool is_amsdu;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+ /* frame starts with rxhdr */
+ rxh = (struct d11rxhdr *) (p->data);
+
+ /* strip off rxhdr */
+ skb_pull(p, BRCMS_HWRXOFF);
+
+ /* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU subframes */
+ if (rxh->RxStatus1 & RXS_PBPRES) {
+ if (p->len < 2) {
+ wiphy_err(wlc->wiphy, "wl%d: recv: rcvd runt of "
+ "len %d\n", wlc->pub->unit, p->len);
+ goto toss;
+ }
+ skb_pull(p, 2);
+ }
+
+ h = (struct ieee80211_hdr *)(p->data + D11_PHY_HDR_LEN);
+ len = p->len;
+
+ if (rxh->RxStatus1 & RXS_FCSERR) {
+ if (wlc->pub->mac80211_state & MAC80211_PROMISC_BCNS) {
+ wiphy_err(wlc->wiphy, "FCSERR while scanning******* -"
+ " tossing\n");
+ goto toss;
+ } else {
+ wiphy_err(wlc->wiphy, "RCSERR!!!\n");
+ goto toss;
+ }
+ }
+
+ /* check received pkt has at least frame control field */
+ if (len < D11_PHY_HDR_LEN + sizeof(h->frame_control))
+ goto toss;
+
+ /* not supporting A-MSDU */
+ is_amsdu = rxh->RxStatus2 & RXS_AMSDU_MASK;
+ if (is_amsdu)
+ goto toss;
+
+ brcms_c_recvctl(wlc, rxh, p);
+ return;
+
+ toss:
+ brcmu_pkt_buf_free_skb(p);
+}
+
+/* Process received frames */
+/*
+ * Return true if more frames need to be processed. false otherwise.
+ * Param 'bound' indicates max. # frames to process before break out.
+ */
+static bool
+brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound)
+{
+ struct sk_buff *p;
+ struct sk_buff *head = NULL;
+ struct sk_buff *tail = NULL;
+ uint n = 0;
+ uint bound_limit = bound ? RXBND : -1;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+ /* gather received frames */
+ while ((p = dma_rx(wlc_hw->di[fifo]))) {
+
+ if (!tail)
+ head = tail = p;
+ else {
+ tail->prev = p;
+ tail = p;
+ }
+
+ /* !give others some time to run! */
+ if (++n >= bound_limit)
+ break;
+ }
+
+ /* post more rbufs */
+ dma_rxfill(wlc_hw->di[fifo]);
+
+ /* process each frame */
+ while ((p = head) != NULL) {
+ struct d11rxhdr_le *rxh_le;
+ struct d11rxhdr *rxh;
+ head = head->prev;
+ p->prev = NULL;
+
+ rxh_le = (struct d11rxhdr_le *)p->data;
+ rxh = (struct d11rxhdr *)p->data;
+
+ /* fixup rx header endianness */
+ rxh->RxFrameSize = le16_to_cpu(rxh_le->RxFrameSize);
+ rxh->PhyRxStatus_0 = le16_to_cpu(rxh_le->PhyRxStatus_0);
+ rxh->PhyRxStatus_1 = le16_to_cpu(rxh_le->PhyRxStatus_1);
+ rxh->PhyRxStatus_2 = le16_to_cpu(rxh_le->PhyRxStatus_2);
+ rxh->PhyRxStatus_3 = le16_to_cpu(rxh_le->PhyRxStatus_3);
+ rxh->PhyRxStatus_4 = le16_to_cpu(rxh_le->PhyRxStatus_4);
+ rxh->PhyRxStatus_5 = le16_to_cpu(rxh_le->PhyRxStatus_5);
+ rxh->RxStatus1 = le16_to_cpu(rxh_le->RxStatus1);
+ rxh->RxStatus2 = le16_to_cpu(rxh_le->RxStatus2);
+ rxh->RxTSFTime = le16_to_cpu(rxh_le->RxTSFTime);
+ rxh->RxChan = le16_to_cpu(rxh_le->RxChan);
+
+ brcms_c_recv(wlc_hw->wlc, p);
+ }
+
+ return n >= bound_limit;
+}
+
+/* second-level interrupt processing
+ * Return true if another dpc needs to be re-scheduled. false otherwise.
+ * Param 'bounded' indicates if applicable loops should be bounded.
+ */
+bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded)
+{
+ u32 macintstatus;
+ struct brcms_hardware *wlc_hw = wlc->hw;
+ struct d11regs __iomem *regs = wlc_hw->regs;
+ struct wiphy *wiphy = wlc->wiphy;
+
+ if (brcms_deviceremoved(wlc)) {
+ wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit,
+ __func__);
+ brcms_down(wlc->wl);
+ return false;
+ }
+
+ /* grab and clear the saved software intstatus bits */
+ macintstatus = wlc->macintstatus;
+ wlc->macintstatus = 0;
+
+ BCMMSG(wlc->wiphy, "wl%d: macintstatus 0x%x\n",
+ wlc_hw->unit, macintstatus);
+
+ WARN_ON(macintstatus & MI_PRQ); /* PRQ Interrupt in non-MBSS */
+
+ /* tx status */
+ if (macintstatus & MI_TFS) {
+ bool fatal;
+ if (brcms_b_txstatus(wlc->hw, bounded, &fatal))
+ wlc->macintstatus |= MI_TFS;
+ if (fatal) {
+ wiphy_err(wiphy, "MI_TFS: fatal\n");
+ goto fatal;
+ }
+ }
+
+ if (macintstatus & (MI_TBTT | MI_DTIM_TBTT))
+ brcms_c_tbtt(wlc);
+
+ /* ATIM window end */
+ if (macintstatus & MI_ATIMWINEND) {
+ BCMMSG(wlc->wiphy, "end of ATIM window\n");
+ OR_REG(&regs->maccommand, wlc->qvalid);
+ wlc->qvalid = 0;
+ }
+
+ /*
+ * received data or control frame, MI_DMAINT is
+ * indication of RX_FIFO interrupt
+ */
+ if (macintstatus & MI_DMAINT)
+ if (brcms_b_recv(wlc_hw, RX_FIFO, bounded))
+ wlc->macintstatus |= MI_DMAINT;
+
+ /* noise sample collected */
+ if (macintstatus & MI_BG_NOISE)
+ wlc_phy_noise_sample_intr(wlc_hw->band->pi);
+
+ if (macintstatus & MI_GP0) {
+ wiphy_err(wiphy, "wl%d: PSM microcode watchdog fired at %d "
+ "(seconds). Resetting.\n", wlc_hw->unit, wlc_hw->now);
+
+ printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n",
+ __func__, wlc_hw->sih->chip,
+ wlc_hw->sih->chiprev);
+ /* big hammer */
+ brcms_init(wlc->wl);
+ }
+
+ /* gptimer timeout */
+ if (macintstatus & MI_TO)
+ W_REG(&regs->gptimer, 0);
+
+ if (macintstatus & MI_RFDISABLE) {
+ BCMMSG(wlc->wiphy, "wl%d: BMAC Detected a change on the"
+ " RF Disable Input\n", wlc_hw->unit);
+ brcms_rfkill_set_hw_state(wlc->wl);
+ }
+
+ /* send any enq'd tx packets. Just makes sure to jump start tx */
+ if (!pktq_empty(&wlc->pkt_queue->q))
+ brcms_c_send_q(wlc);
+
+ /* it isn't done and needs to be resched if macintstatus is non-zero */
+ return wlc->macintstatus != 0;
+
+ fatal:
+ brcms_init(wlc->wl);
+ return wlc->macintstatus != 0;
+}
+
+void brcms_c_init(struct brcms_c_info *wlc)
+{
+ struct d11regs __iomem *regs;
+ u16 chanspec;
+ bool mute = false;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+ regs = wlc->regs;
+
+ /*
+ * This will happen if a big-hammer was executed. In
+ * that case, we want to go back to the channel that
+ * we were on and not new channel
+ */
+ if (wlc->pub->associated)
+ chanspec = wlc->home_chanspec;
+ else
+ chanspec = brcms_c_init_chanspec(wlc);
+
+ brcms_b_init(wlc->hw, chanspec, mute);
+
+ /* update beacon listen interval */
+ brcms_c_bcn_li_upd(wlc);
+
+ /* write ethernet address to core */
+ brcms_c_set_mac(wlc->bsscfg);
+ brcms_c_set_bssid(wlc->bsscfg);
+
+ /* Update tsf_cfprep if associated and up */
+ if (wlc->pub->associated && wlc->bsscfg->up) {
+ u32 bi;
+
+ /* get beacon period and convert to uS */
+ bi = wlc->bsscfg->current_bss->beacon_period << 10;
+ /*
+ * update since init path would reset
+ * to default value
+ */
+ W_REG(&regs->tsf_cfprep,
+ (bi << CFPREP_CBI_SHIFT));
+
+ /* Update maccontrol PM related bits */
+ brcms_c_set_ps_ctrl(wlc);
+ }
+
+ brcms_c_bandinit_ordered(wlc, chanspec);
+
+ /* init probe response timeout */
+ brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout);
+
+ /* init max burst txop (framebursting) */
+ brcms_b_write_shm(wlc->hw, M_MBURST_TXOP,
+ (wlc->
+ _rifs ? (EDCF_AC_VO_TXOP_AP << 5) : MAXFRAMEBURST_TXOP));
+
+ /* initialize maximum allowed duty cycle */
+ brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_ofdm, true, true);
+ brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_cck, false, true);
+
+ /*
+ * Update some shared memory locations related to
+ * max AMPDU size allowed to received
+ */
+ brcms_c_ampdu_shm_upd(wlc->ampdu);
+
+ /* band-specific inits */
+ brcms_c_bsinit(wlc);
+
+ /* Enable EDCF mode (while the MAC is suspended) */
+ OR_REG(&regs->ifs_ctl, IFS_USEEDCF);
+ brcms_c_edcf_setparams(wlc, false);
+
+ /* Init precedence maps for empty FIFOs */
+ brcms_c_tx_prec_map_init(wlc);
+
+ /* read the ucode version if we have not yet done so */
+ if (wlc->ucode_rev == 0) {
+ wlc->ucode_rev =
+ brcms_b_read_shm(wlc->hw, M_BOM_REV_MAJOR) << NBITS(u16);
+ wlc->ucode_rev |= brcms_b_read_shm(wlc->hw, M_BOM_REV_MINOR);
+ }
+
+ /* ..now really unleash hell (allow the MAC out of suspend) */
+ brcms_c_enable_mac(wlc);
+
+ /* clear tx flow control */
+ brcms_c_txflowcontrol_reset(wlc);
+
+ /* enable the RF Disable Delay timer */
+ W_REG(&wlc->regs->rfdisabledly, RFDISABLE_DEFAULT);
+
+ /* initialize mpc delay */
+ wlc->mpc_delay_off = wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT;
+
+ /*
+ * Initialize WME parameters; if they haven't been set by some other
+ * mechanism (IOVar, etc) then read them from the hardware.
+ */
+ if (GFIELD(wlc->wme_retries[0], EDCF_SHORT) == 0) {
+ /* Uninitialized; read from HW */
+ int ac;
+
+ for (ac = 0; ac < AC_COUNT; ac++)
+ wlc->wme_retries[ac] =
+ brcms_b_read_shm(wlc->hw, M_AC_TXLMT_ADDR(ac));
+ }
+}
+
+/*
+ * The common driver entry routine. Error codes should be unique
+ */
+struct brcms_c_info *
+brcms_c_attach(struct brcms_info *wl, u16 vendor, u16 device, uint unit,
+ bool piomode, void __iomem *regsva, struct pci_dev *btparam,
+ uint *perr)
+{
+ struct brcms_c_info *wlc;
+ uint err = 0;
+ uint i, j;
+ struct brcms_pub *pub;
+
+ /* allocate struct brcms_c_info state and its substructures */
+ wlc = (struct brcms_c_info *) brcms_c_attach_malloc(unit, &err, device);
+ if (wlc == NULL)
+ goto fail;
+ wlc->wiphy = wl->wiphy;
+ pub = wlc->pub;
+
+#if defined(BCMDBG)
+ wlc_info_dbg = wlc;
+#endif
+
+ wlc->band = wlc->bandstate[0];
+ wlc->core = wlc->corestate;
+ wlc->wl = wl;
+ pub->unit = unit;
+ pub->_piomode = piomode;
+ wlc->bandinit_pending = false;
+
+ /* populate struct brcms_c_info with default values */
+ brcms_c_info_init(wlc, unit);
+
+ /* update sta/ap related parameters */
+ brcms_c_ap_upd(wlc);
+
+ /*
+ * low level attach steps(all hw accesses go
+ * inside, no more in rest of the attach)
+ */
+ err = brcms_b_attach(wlc, vendor, device, unit, piomode, regsva,
+ btparam);
+ if (err)
+ goto fail;
+
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, OFF);
+
+ pub->phy_11ncapable = BRCMS_PHY_11N_CAP(wlc->band);
+
+ /* disable allowed duty cycle */
+ wlc->tx_duty_cycle_ofdm = 0;
+ wlc->tx_duty_cycle_cck = 0;
+
+ brcms_c_stf_phy_chain_calc(wlc);
+
+ /* txchain 1: txant 0, txchain 2: txant 1 */
+ if (BRCMS_ISNPHY(wlc->band) && (wlc->stf->txstreams == 1))
+ wlc->stf->txant = wlc->stf->hw_txchain - 1;
+
+ /* push to BMAC driver */
+ wlc_phy_stf_chain_init(wlc->band->pi, wlc->stf->hw_txchain,
+ wlc->stf->hw_rxchain);
+
+ /* pull up some info resulting from the low attach */
+ for (i = 0; i < NFIFO; i++)
+ wlc->core->txavail[i] = wlc->hw->txavail[i];
+
+ memcpy(&wlc->perm_etheraddr, &wlc->hw->etheraddr, ETH_ALEN);
+ memcpy(&pub->cur_etheraddr, &wlc->hw->etheraddr, ETH_ALEN);
+
+ for (j = 0; j < wlc->pub->_nbands; j++) {
+ wlc->band = wlc->bandstate[j];
+
+ if (!brcms_c_attach_stf_ant_init(wlc)) {
+ err = 24;
+ goto fail;
+ }
+
+ /* default contention windows size limits */
+ wlc->band->CWmin = APHY_CWMIN;
+ wlc->band->CWmax = PHY_CWMAX;
+
+ /* init gmode value */
+ if (wlc->band->bandtype == BRCM_BAND_2G) {
+ wlc->band->gmode = GMODE_AUTO;
+ brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER,
+ wlc->band->gmode);
+ }
+
+ /* init _n_enab supported mode */
+ if (BRCMS_PHY_11N_CAP(wlc->band)) {
+ pub->_n_enab = SUPPORT_11N;
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_USER,
+ ((pub->_n_enab ==
+ SUPPORT_11N) ? WL_11N_2x2 :
+ WL_11N_3x3));
+ }
+
+ /* init per-band default rateset, depend on band->gmode */
+ brcms_default_rateset(wlc, &wlc->band->defrateset);
+
+ /* fill in hw_rateset */
+ brcms_c_rateset_filter(&wlc->band->defrateset,
+ &wlc->band->hw_rateset, false,
+ BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK,
+ (bool) (wlc->pub->_n_enab & SUPPORT_11N));
+ }
+
+ /*
+ * update antenna config due to
+ * wlc->stf->txant/txchain/ant_rx_ovr change
+ */
+ brcms_c_stf_phy_txant_upd(wlc);
+
+ /* attach each modules */
+ err = brcms_c_attach_module(wlc);
+ if (err != 0)
+ goto fail;
+
+ if (!brcms_c_timers_init(wlc, unit)) {
+ wiphy_err(wl->wiphy, "wl%d: %s: init_timer failed\n", unit,
+ __func__);
+ err = 32;
+ goto fail;
+ }
+
+ /* depend on rateset, gmode */
+ wlc->cmi = brcms_c_channel_mgr_attach(wlc);
+ if (!wlc->cmi) {
+ wiphy_err(wl->wiphy, "wl%d: %s: channel_mgr_attach failed"
+ "\n", unit, __func__);
+ err = 33;
+ goto fail;
+ }
+
+ /* init default when all parameters are ready, i.e. ->rateset */
+ brcms_c_bss_default_init(wlc);
+
+ /*
+ * Complete the wlc default state initializations..
+ */
+
+ /* allocate our initial queue */
+ wlc->pkt_queue = brcms_c_txq_alloc(wlc);
+ if (wlc->pkt_queue == NULL) {
+ wiphy_err(wl->wiphy, "wl%d: %s: failed to malloc tx queue\n",
+ unit, __func__);
+ err = 100;
+ goto fail;
+ }
+
+ wlc->bsscfg->wlc = wlc;
+
+ wlc->mimoft = FT_HT;
+ wlc->mimo_40txbw = AUTO;
+ wlc->ofdm_40txbw = AUTO;
+ wlc->cck_40txbw = AUTO;
+ brcms_c_update_mimo_band_bwcap(wlc, BRCMS_N_BW_20IN2G_40IN5G);
+
+ /* Set default values of SGI */
+ if (BRCMS_SGI_CAP_PHY(wlc)) {
+ brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 |
+ BRCMS_N_SGI_40));
+ } else if (BRCMS_ISSSLPNPHY(wlc->band)) {
+ brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 |
+ BRCMS_N_SGI_40));
+ } else {
+ brcms_c_ht_update_sgi_rx(wlc, 0);
+ }
+
+ /* initialize radio_mpc_disable according to wlc->mpc */
+ brcms_c_radio_mpc_upd(wlc);
+ brcms_b_antsel_set(wlc->hw, wlc->asi->antsel_avail);
+
+ if (perr)
+ *perr = 0;
+
+ return wlc;
+
+ fail:
+ wiphy_err(wl->wiphy, "wl%d: %s: failed with err %d\n",
+ unit, __func__, err);
+ if (wlc)
+ brcms_c_detach(wlc);
+
+ if (perr)
+ *perr = err;
+ return NULL;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.h b/drivers/net/wireless/brcm80211/brcmsmac/main.h
new file mode 100644
index 000000000000..c0e0fcfdfaf8
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/main.h
@@ -0,0 +1,735 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_MAIN_H_
+#define _BRCM_MAIN_H_
+
+#include <linux/etherdevice.h>
+
+#include <brcmu_utils.h>
+#include "types.h"
+#include "d11.h"
+#include "scb.h"
+
+#define INVCHANNEL 255 /* invalid channel */
+
+/* max # brcms_c_module_register() calls */
+#define BRCMS_MAXMODULES 22
+
+#define SEQNUM_SHIFT 4
+#define SEQNUM_MAX 0x1000
+
+#define NTXRATE 64 /* # tx MPDUs rate is reported for */
+
+/* Maximum wait time for a MAC suspend */
+/* uS: 83mS is max packet time (64KB ampdu @ 6Mbps) */
+#define BRCMS_MAX_MAC_SUSPEND 83000
+
+/* responses for probe requests older that this are tossed, zero to disable */
+#define BRCMS_PRB_RESP_TIMEOUT 0 /* Disable probe response timeout */
+
+/* transmit buffer max headroom for protocol headers */
+#define TXOFF (D11_TXH_LEN + D11_PHY_HDR_LEN)
+
+#define AC_COUNT 4
+
+/* Macros for doing definition and get/set of bitfields
+ * Usage example, e.g. a three-bit field (bits 4-6):
+ * #define <NAME>_M BITFIELD_MASK(3)
+ * #define <NAME>_S 4
+ * ...
+ * regval = R_REG(osh, &regs->regfoo);
+ * field = GFIELD(regval, <NAME>);
+ * regval = SFIELD(regval, <NAME>, 1);
+ * W_REG(osh, &regs->regfoo, regval);
+ */
+#define BITFIELD_MASK(width) \
+ (((unsigned)1 << (width)) - 1)
+#define GFIELD(val, field) \
+ (((val) >> field ## _S) & field ## _M)
+#define SFIELD(val, field, bits) \
+ (((val) & (~(field ## _M << field ## _S))) | \
+ ((unsigned)(bits) << field ## _S))
+
+#define SW_TIMER_MAC_STAT_UPD 30 /* periodic MAC stats update */
+
+/* max # supported core revisions (0 .. MAXCOREREV - 1) */
+#define MAXCOREREV 28
+
+/* Double check that unsupported cores are not enabled */
+#if CONF_MSK(D11CONF, 0x4f) || CONF_GE(D11CONF, MAXCOREREV)
+#error "Configuration for D11CONF includes unsupported versions."
+#endif /* Bad versions */
+
+/* values for shortslot_override */
+#define BRCMS_SHORTSLOT_AUTO -1 /* Driver will manage Shortslot setting */
+#define BRCMS_SHORTSLOT_OFF 0 /* Turn off short slot */
+#define BRCMS_SHORTSLOT_ON 1 /* Turn on short slot */
+
+/* value for short/long and mixmode/greenfield preamble */
+#define BRCMS_LONG_PREAMBLE (0)
+#define BRCMS_SHORT_PREAMBLE (1 << 0)
+#define BRCMS_GF_PREAMBLE (1 << 1)
+#define BRCMS_MM_PREAMBLE (1 << 2)
+#define BRCMS_IS_MIMO_PREAMBLE(_pre) (((_pre) == BRCMS_GF_PREAMBLE) || \
+ ((_pre) == BRCMS_MM_PREAMBLE))
+
+/* TxFrameID */
+/* seq and frag bits: SEQNUM_SHIFT, FRAGNUM_MASK (802.11.h) */
+/* rate epoch bits: TXFID_RATE_SHIFT, TXFID_RATE_MASK ((wlc_rate.c) */
+#define TXFID_QUEUE_MASK 0x0007 /* Bits 0-2 */
+#define TXFID_SEQ_MASK 0x7FE0 /* Bits 5-15 */
+#define TXFID_SEQ_SHIFT 5 /* Number of bit shifts */
+#define TXFID_RATE_PROBE_MASK 0x8000 /* Bit 15 for rate probe */
+#define TXFID_RATE_MASK 0x0018 /* Mask for bits 3 and 4 */
+#define TXFID_RATE_SHIFT 3 /* Shift 3 bits for rate mask */
+
+/* promote boardrev */
+#define BOARDREV_PROMOTABLE 0xFF /* from */
+#define BOARDREV_PROMOTED 1 /* to */
+
+#define DATA_BLOCK_TX_SUPR (1 << 4)
+
+/* 802.1D Priority to TX FIFO number for wme */
+extern const u8 prio2fifo[];
+
+/* Ucode MCTL_WAKE override bits */
+#define BRCMS_WAKE_OVERRIDE_CLKCTL 0x01
+#define BRCMS_WAKE_OVERRIDE_PHYREG 0x02
+#define BRCMS_WAKE_OVERRIDE_MACSUSPEND 0x04
+#define BRCMS_WAKE_OVERRIDE_TXFIFO 0x08
+#define BRCMS_WAKE_OVERRIDE_FORCEFAST 0x10
+
+/* stuff pulled in from wlc.c */
+
+/* Interrupt bit error summary. Don't include I_RU: we refill DMA at other
+ * times; and if we run out, constant I_RU interrupts may cause lockup. We
+ * will still get error counts from rx0ovfl.
+ */
+#define I_ERRORS (I_PC | I_PD | I_DE | I_RO | I_XU)
+/* default software intmasks */
+#define DEF_RXINTMASK (I_RI) /* enable rx int on rxfifo only */
+#define DEF_MACINTMASK (MI_TXSTOP | MI_TBTT | MI_ATIMWINEND | MI_PMQ | \
+ MI_PHYTXERR | MI_DMAINT | MI_TFS | MI_BG_NOISE | \
+ MI_CCA | MI_TO | MI_GP0 | MI_RFDISABLE | MI_PWRUP)
+
+#define MAXTXPKTS 6 /* max # pkts pending */
+
+/* frameburst */
+#define MAXTXFRAMEBURST 8 /* vanilla xpress mode: max frames/burst */
+#define MAXFRAMEBURST_TXOP 10000 /* Frameburst TXOP in usec */
+
+#define NFIFO 6 /* # tx/rx fifopairs */
+
+/* PLL requests */
+
+/* pll is shared on old chips */
+#define BRCMS_PLLREQ_SHARED 0x1
+/* hold pll for radio monitor register checking */
+#define BRCMS_PLLREQ_RADIO_MON 0x2
+/* hold/release pll for some short operation */
+#define BRCMS_PLLREQ_FLIP 0x4
+
+#define CHANNEL_BANDUNIT(wlc, ch) \
+ (((ch) <= CH_MAX_2G_CHANNEL) ? BAND_2G_INDEX : BAND_5G_INDEX)
+
+#define OTHERBANDUNIT(wlc) \
+ ((uint)((wlc)->band->bandunit ? BAND_2G_INDEX : BAND_5G_INDEX))
+
+/*
+ * 802.11 protection information
+ *
+ * _g: use g spec protection, driver internal.
+ * g_override: override for use of g spec protection.
+ * gmode_user: user config gmode, operating band->gmode is different.
+ * overlap: Overlap BSS/IBSS protection for both 11g and 11n.
+ * nmode_user: user config nmode, operating pub->nmode is different.
+ * n_cfg: use OFDM protection on MIMO frames.
+ * n_cfg_override: override for use of N protection.
+ * nongf: non-GF present protection.
+ * nongf_override: override for use of GF protection.
+ * n_pam_override: override for preamble: MM or GF.
+ * n_obss: indicated OBSS Non-HT STA present.
+*/
+struct brcms_protection {
+ bool _g;
+ s8 g_override;
+ u8 gmode_user;
+ s8 overlap;
+ s8 nmode_user;
+ s8 n_cfg;
+ s8 n_cfg_override;
+ bool nongf;
+ s8 nongf_override;
+ s8 n_pam_override;
+ bool n_obss;
+};
+
+/*
+ * anything affecting the single/dual streams/antenna operation
+ *
+ * hw_txchain: HW txchain bitmap cfg.
+ * txchain: txchain bitmap being used.
+ * txstreams: number of txchains being used.
+ * hw_rxchain: HW rxchain bitmap cfg.
+ * rxchain: rxchain bitmap being used.
+ * rxstreams: number of rxchains being used.
+ * ant_rx_ovr: rx antenna override.
+ * txant: userTx antenna setting.
+ * phytxant: phyTx antenna setting in txheader.
+ * ss_opmode: singlestream Operational mode, 0:siso; 1:cdd.
+ * ss_algosel_auto: if true, use wlc->stf->ss_algo_channel;
+ * else use wlc->band->stf->ss_mode_band.
+ * ss_algo_channel: ss based on per-channel algo: 0: SISO, 1: CDD 2: STBC.
+ * rxchain_restore_delay: delay time to restore default rxchain.
+ * ldpc: AUTO/ON/OFF ldpc cap supported.
+ * txcore[MAX_STREAMS_SUPPORTED + 1]: bitmap of selected core for each Nsts.
+ * spatial_policy:
+ */
+struct brcms_stf {
+ u8 hw_txchain;
+ u8 txchain;
+ u8 txstreams;
+ u8 hw_rxchain;
+ u8 rxchain;
+ u8 rxstreams;
+ u8 ant_rx_ovr;
+ s8 txant;
+ u16 phytxant;
+ u8 ss_opmode;
+ bool ss_algosel_auto;
+ u16 ss_algo_channel;
+ u8 rxchain_restore_delay;
+ s8 ldpc;
+ u8 txcore[MAX_STREAMS_SUPPORTED + 1];
+ s8 spatial_policy;
+};
+
+#define BRCMS_STF_SS_STBC_TX(wlc, scb) \
+ (((wlc)->stf->txstreams > 1) && (((wlc)->band->band_stf_stbc_tx == ON) \
+ || (((scb)->flags & SCB_STBCCAP) && \
+ (wlc)->band->band_stf_stbc_tx == AUTO && \
+ isset(&((wlc)->stf->ss_algo_channel), PHY_TXC1_MODE_STBC))))
+
+#define BRCMS_STBC_CAP_PHY(wlc) (BRCMS_ISNPHY(wlc->band) && \
+ NREV_GE(wlc->band->phyrev, 3))
+
+#define BRCMS_SGI_CAP_PHY(wlc) ((BRCMS_ISNPHY(wlc->band) && \
+ NREV_GE(wlc->band->phyrev, 3)) || \
+ BRCMS_ISLCNPHY(wlc->band))
+
+#define BRCMS_CHAN_PHYTYPE(x) (((x) & RXS_CHAN_PHYTYPE_MASK) \
+ >> RXS_CHAN_PHYTYPE_SHIFT)
+#define BRCMS_CHAN_CHANNEL(x) (((x) & RXS_CHAN_ID_MASK) \
+ >> RXS_CHAN_ID_SHIFT)
+
+/*
+ * core state (mac)
+ */
+struct brcms_core {
+ uint coreidx; /* # sb enumerated core */
+
+ /* fifo */
+ uint *txavail[NFIFO]; /* # tx descriptors available */
+ s16 txpktpend[NFIFO]; /* tx admission control */
+
+ struct macstat *macstat_snapshot; /* mac hw prev read values */
+};
+
+/*
+ * band state (phy+ana+radio)
+ */
+struct brcms_band {
+ int bandtype; /* BRCM_BAND_2G, BRCM_BAND_5G */
+ uint bandunit; /* bandstate[] index */
+
+ u16 phytype; /* phytype */
+ u16 phyrev;
+ u16 radioid;
+ u16 radiorev;
+ struct brcms_phy_pub *pi; /* pointer to phy specific information */
+ bool abgphy_encore;
+
+ u8 gmode; /* currently active gmode */
+
+ struct scb *hwrs_scb; /* permanent scb for hw rateset */
+
+ /* band-specific copy of default_bss.rateset */
+ struct brcms_c_rateset defrateset;
+
+ u8 band_stf_ss_mode; /* Configured STF type, 0:siso; 1:cdd */
+ s8 band_stf_stbc_tx; /* STBC TX 0:off; 1:force on; -1:auto */
+ /* rates supported by chip (phy-specific) */
+ struct brcms_c_rateset hw_rateset;
+ u8 basic_rate[BRCM_MAXRATE + 1]; /* basic rates indexed by rate */
+ bool mimo_cap_40; /* 40 MHz cap enabled on this band */
+ s8 antgain; /* antenna gain from srom */
+
+ u16 CWmin; /* minimum size of contention window, in unit of aSlotTime */
+ u16 CWmax; /* maximum size of contention window, in unit of aSlotTime */
+ struct ieee80211_supported_band band;
+};
+
+/* module control blocks */
+struct modulecb {
+ /* module name : NULL indicates empty array member */
+ char name[32];
+ /* handle passed when handler 'doiovar' is called */
+ struct brcms_info *hdl;
+
+ int (*down_fn)(void *handle); /* down handler. Note: the int returned
+ * by the down function is a count of the
+ * number of timers that could not be
+ * freed.
+ */
+
+};
+
+struct brcms_hw_band {
+ int bandtype; /* BRCM_BAND_2G, BRCM_BAND_5G */
+ uint bandunit; /* bandstate[] index */
+ u16 mhfs[MHFMAX]; /* MHF array shadow */
+ u8 bandhw_stf_ss_mode; /* HW configured STF type, 0:siso; 1:cdd */
+ u16 CWmin;
+ u16 CWmax;
+ u32 core_flags;
+
+ u16 phytype; /* phytype */
+ u16 phyrev;
+ u16 radioid;
+ u16 radiorev;
+ struct brcms_phy_pub *pi; /* pointer to phy specific information */
+ bool abgphy_encore;
+};
+
+struct brcms_hardware {
+ bool _piomode; /* true if pio mode */
+ struct brcms_c_info *wlc;
+
+ /* fifo */
+ struct dma_pub *di[NFIFO]; /* dma handles, per fifo */
+
+ uint unit; /* device instance number */
+
+ /* version info */
+ u16 vendorid; /* PCI vendor id */
+ u16 deviceid; /* PCI device id */
+ uint corerev; /* core revision */
+ u8 sromrev; /* version # of the srom */
+ u16 boardrev; /* version # of particular board */
+ u32 boardflags; /* Board specific flags from srom */
+ u32 boardflags2; /* More board flags if sromrev >= 4 */
+ u32 machwcap; /* MAC capabilities */
+ u32 machwcap_backup; /* backup of machwcap */
+
+ struct si_pub *sih; /* SI handle (cookie for siutils calls) */
+ struct d11regs __iomem *regs; /* pointer to device registers */
+ struct phy_shim_info *physhim; /* phy shim layer handler */
+ struct shared_phy *phy_sh; /* pointer to shared phy state */
+ struct brcms_hw_band *band;/* pointer to active per-band state */
+ /* band state per phy/radio */
+ struct brcms_hw_band *bandstate[MAXBANDS];
+ u16 bmac_phytxant; /* cache of high phytxant state */
+ bool shortslot; /* currently using 11g ShortSlot timing */
+ u16 SRL; /* 802.11 dot11ShortRetryLimit */
+ u16 LRL; /* 802.11 dot11LongRetryLimit */
+ u16 SFBL; /* Short Frame Rate Fallback Limit */
+ u16 LFBL; /* Long Frame Rate Fallback Limit */
+
+ bool up; /* d11 hardware up and running */
+ uint now; /* # elapsed seconds */
+ uint _nbands; /* # bands supported */
+ u16 chanspec; /* bmac chanspec shadow */
+
+ uint *txavail[NFIFO]; /* # tx descriptors available */
+ const u16 *xmtfifo_sz; /* fifo size in 256B for each xmt fifo */
+
+ u32 pllreq; /* pll requests to keep PLL on */
+
+ u8 suspended_fifos; /* Which TX fifo to remain awake for */
+ u32 maccontrol; /* Cached value of maccontrol */
+ uint mac_suspend_depth; /* current depth of mac_suspend levels */
+ u32 wake_override; /* bit flags to force MAC to WAKE mode */
+ u32 mute_override; /* Prevent ucode from sending beacons */
+ u8 etheraddr[ETH_ALEN]; /* currently configured ethernet address */
+ bool noreset; /* true= do not reset hw, used by WLC_OUT */
+ bool forcefastclk; /* true if h/w is forcing to use fast clk */
+ bool clk; /* core is out of reset and has clock */
+ bool sbclk; /* sb has clock */
+ bool phyclk; /* phy is out of reset and has clock */
+
+ bool ucode_loaded; /* true after ucode downloaded */
+
+
+ u8 hw_stf_ss_opmode; /* STF single stream operation mode */
+ u8 antsel_type; /* Type of boardlevel mimo antenna switch-logic
+ * 0 = N/A, 1 = 2x4 board, 2 = 2x3 CB2 board
+ */
+ u32 antsel_avail; /*
+ * put struct antsel_info here if more info is
+ * needed
+ */
+};
+
+/* TX Queue information
+ *
+ * Each flow of traffic out of the device has a TX Queue with independent
+ * flow control. Several interfaces may be associated with a single TX Queue
+ * if they belong to the same flow of traffic from the device. For multi-channel
+ * operation there are independent TX Queues for each channel.
+ */
+struct brcms_txq_info {
+ struct brcms_txq_info *next;
+ struct pktq q;
+ uint stopped; /* tx flow control bits */
+};
+
+/*
+ * Principal common driver data structure.
+ *
+ * pub: pointer to driver public state.
+ * wl: pointer to specific private state.
+ * regs: pointer to device registers.
+ * hw: HW related state.
+ * clkreq_override: setting for clkreq for PCIE : Auto, 0, 1.
+ * fastpwrup_dly: time in us needed to bring up d11 fast clock.
+ * macintstatus: bit channel between isr and dpc.
+ * macintmask: sw runtime master macintmask value.
+ * defmacintmask: default "on" macintmask value.
+ * clk: core is out of reset and has clock.
+ * core: pointer to active io core.
+ * band: pointer to active per-band state.
+ * corestate: per-core state (one per hw core).
+ * bandstate: per-band state (one per phy/radio).
+ * qvalid: DirFrmQValid and BcMcFrmQValid.
+ * ampdu: ampdu module handler.
+ * asi: antsel module handler.
+ * cmi: channel manager module handler.
+ * vendorid: PCI vendor id.
+ * deviceid: PCI device id.
+ * ucode_rev: microcode revision.
+ * machwcap: MAC capabilities, BMAC shadow.
+ * perm_etheraddr: original sprom local ethernet address.
+ * bandlocked: disable auto multi-band switching.
+ * bandinit_pending: track band init in auto band.
+ * radio_monitor: radio timer is running.
+ * going_down: down path intermediate variable.
+ * mpc: enable minimum power consumption.
+ * mpc_dlycnt: # of watchdog cnt before turn disable radio.
+ * mpc_offcnt: # of watchdog cnt that radio is disabled.
+ * mpc_delay_off: delay radio disable by # of watchdog cnt.
+ * prev_non_delay_mpc: prev state brcms_c_is_non_delay_mpc.
+ * wdtimer: timer for watchdog routine.
+ * radio_timer: timer for hw radio button monitor routine.
+ * monitor: monitor (MPDU sniffing) mode.
+ * bcnmisc_monitor: bcns promisc mode override for monitor.
+ * _rifs: enable per-packet rifs.
+ * bcn_li_bcn: beacon listen interval in # beacons.
+ * bcn_li_dtim: beacon listen interval in # dtims.
+ * WDarmed: watchdog timer is armed.
+ * WDlast: last time wlc_watchdog() was called.
+ * edcf_txop[AC_COUNT]: current txop for each ac.
+ * wme_retries: per-AC retry limits.
+ * tx_prec_map: Precedence map based on HW FIFO space.
+ * fifo2prec_map[NFIFO]: pointer to fifo2_prec map based on WME.
+ * bsscfg: set of BSS configurations, idx 0 is default and always valid.
+ * cfg: the primary bsscfg (can be AP or STA).
+ * tx_queues: common TX Queue list.
+ * modulecb:
+ * mimoft: SIGN or 11N.
+ * cck_40txbw: 11N, cck tx b/w override when in 40MHZ mode.
+ * ofdm_40txbw: 11N, ofdm tx b/w override when in 40MHZ mode.
+ * mimo_40txbw: 11N, mimo tx b/w override when in 40MHZ mode.
+ * default_bss: configured BSS parameters.
+ * mc_fid_counter: BC/MC FIFO frame ID counter.
+ * country_default: saved country for leaving 802.11d auto-country mode.
+ * autocountry_default: initial country for 802.11d auto-country mode.
+ * prb_resp_timeout: do not send prb resp if request older
+ * than this, 0 = disable.
+ * home_chanspec: shared home chanspec.
+ * chanspec: target operational channel.
+ * usr_fragthresh: user configured fragmentation threshold.
+ * fragthresh[NFIFO]: per-fifo fragmentation thresholds.
+ * RTSThresh: 802.11 dot11RTSThreshold.
+ * SRL: 802.11 dot11ShortRetryLimit.
+ * LRL: 802.11 dot11LongRetryLimit.
+ * SFBL: Short Frame Rate Fallback Limit.
+ * LFBL: Long Frame Rate Fallback Limit.
+ * shortslot: currently using 11g ShortSlot timing.
+ * shortslot_override: 11g ShortSlot override.
+ * include_legacy_erp: include Legacy ERP info elt ID 47 as well as g ID 42.
+ * PLCPHdr_override: 802.11b Preamble Type override.
+ * stf:
+ * bcn_rspec: save bcn ratespec purpose.
+ * tempsense_lasttime;
+ * tx_duty_cycle_ofdm: maximum allowed duty cycle for OFDM.
+ * tx_duty_cycle_cck: maximum allowed duty cycle for CCK.
+ * pkt_queue: txq for transmit packets.
+ * wiphy:
+ * pri_scb: primary Station Control Block
+ */
+struct brcms_c_info {
+ struct brcms_pub *pub;
+ struct brcms_info *wl;
+ struct d11regs __iomem *regs;
+ struct brcms_hardware *hw;
+
+ /* clock */
+ u16 fastpwrup_dly;
+
+ /* interrupt */
+ u32 macintstatus;
+ u32 macintmask;
+ u32 defmacintmask;
+
+ bool clk;
+
+ /* multiband */
+ struct brcms_core *core;
+ struct brcms_band *band;
+ struct brcms_core *corestate;
+ struct brcms_band *bandstate[MAXBANDS];
+
+ /* packet queue */
+ uint qvalid;
+
+ struct ampdu_info *ampdu;
+ struct antsel_info *asi;
+ struct brcms_cm_info *cmi;
+
+ u16 vendorid;
+ u16 deviceid;
+ uint ucode_rev;
+
+ u8 perm_etheraddr[ETH_ALEN];
+
+ bool bandlocked;
+ bool bandinit_pending;
+
+ bool radio_monitor;
+ bool going_down;
+
+ bool mpc;
+ u8 mpc_dlycnt;
+ u8 mpc_offcnt;
+ u8 mpc_delay_off;
+ u8 prev_non_delay_mpc;
+
+ struct brcms_timer *wdtimer;
+ struct brcms_timer *radio_timer;
+
+ /* promiscuous */
+ bool monitor;
+ bool bcnmisc_monitor;
+
+ /* driver feature */
+ bool _rifs;
+
+ /* AP-STA synchronization, power save */
+ u8 bcn_li_bcn;
+ u8 bcn_li_dtim;
+
+ bool WDarmed;
+ u32 WDlast;
+
+ /* WME */
+ u16 edcf_txop[AC_COUNT];
+
+ u16 wme_retries[AC_COUNT];
+ u16 tx_prec_map;
+ u16 fifo2prec_map[NFIFO];
+
+ struct brcms_bss_cfg *bsscfg;
+
+ /* tx queue */
+ struct brcms_txq_info *tx_queues;
+
+ struct modulecb *modulecb;
+
+ u8 mimoft;
+ s8 cck_40txbw;
+ s8 ofdm_40txbw;
+ s8 mimo_40txbw;
+
+ struct brcms_bss_info *default_bss;
+
+ u16 mc_fid_counter;
+
+ char country_default[BRCM_CNTRY_BUF_SZ];
+ char autocountry_default[BRCM_CNTRY_BUF_SZ];
+ u16 prb_resp_timeout;
+
+ u16 home_chanspec;
+
+ /* PHY parameters */
+ u16 chanspec;
+ u16 usr_fragthresh;
+ u16 fragthresh[NFIFO];
+ u16 RTSThresh;
+ u16 SRL;
+ u16 LRL;
+ u16 SFBL;
+ u16 LFBL;
+
+ /* network config */
+ bool shortslot;
+ s8 shortslot_override;
+ bool include_legacy_erp;
+
+ struct brcms_protection *protection;
+ s8 PLCPHdr_override;
+
+ struct brcms_stf *stf;
+
+ u32 bcn_rspec;
+
+ uint tempsense_lasttime;
+
+ u16 tx_duty_cycle_ofdm;
+ u16 tx_duty_cycle_cck;
+
+ struct brcms_txq_info *pkt_queue;
+ struct wiphy *wiphy;
+ struct scb pri_scb;
+};
+
+/* antsel module specific state */
+struct antsel_info {
+ struct brcms_c_info *wlc; /* pointer to main wlc structure */
+ struct brcms_pub *pub; /* pointer to public fn */
+ u8 antsel_type; /* Type of boardlevel mimo antenna switch-logic
+ * 0 = N/A, 1 = 2x4 board, 2 = 2x3 CB2 board
+ */
+ u8 antsel_antswitch; /* board level antenna switch type */
+ bool antsel_avail; /* Ant selection availability (SROM based) */
+ struct brcms_antselcfg antcfg_11n; /* antenna configuration */
+ struct brcms_antselcfg antcfg_cur; /* current antenna config (auto) */
+};
+
+/*
+ * BSS configuration state
+ *
+ * wlc: wlc to which this bsscfg belongs to.
+ * up: is this configuration up operational
+ * enable: is this configuration enabled
+ * associated: is BSS in ASSOCIATED state
+ * BSS: infraustructure or adhoc
+ * SSID_len: the length of SSID
+ * SSID: SSID string
+ *
+ *
+ * BSSID: BSSID (associated)
+ * cur_etheraddr: h/w address
+ * flags: BSSCFG flags; see below
+ *
+ * current_bss: BSS parms in ASSOCIATED state
+ *
+ *
+ * ID: 'unique' ID of this bsscfg, assigned at bsscfg allocation
+ */
+struct brcms_bss_cfg {
+ struct brcms_c_info *wlc;
+ bool up;
+ bool enable;
+ bool associated;
+ bool BSS;
+ u8 SSID_len;
+ u8 SSID[IEEE80211_MAX_SSID_LEN];
+ u8 BSSID[ETH_ALEN];
+ u8 cur_etheraddr[ETH_ALEN];
+ struct brcms_bss_info *current_bss;
+};
+
+extern void brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo,
+ struct sk_buff *p,
+ bool commit, s8 txpktpend);
+extern void brcms_c_txfifo_complete(struct brcms_c_info *wlc, uint fifo,
+ s8 txpktpend);
+extern void brcms_c_txq_enq(struct brcms_c_info *wlc, struct scb *scb,
+ struct sk_buff *sdu, uint prec);
+extern void brcms_c_print_txstatus(struct tx_status *txs);
+extern int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo,
+ uint *blocks);
+
+#if defined(BCMDBG)
+extern void brcms_c_print_txdesc(struct d11txh *txh);
+#else
+#define brcms_c_print_txdesc(a)
+#endif
+
+extern int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config);
+extern void brcms_c_mac_bcn_promisc_change(struct brcms_c_info *wlc,
+ bool promisc);
+extern void brcms_c_send_q(struct brcms_c_info *wlc);
+extern int brcms_c_prep_pdu(struct brcms_c_info *wlc, struct sk_buff *pdu,
+ uint *fifo);
+extern u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec,
+ uint mac_len);
+extern u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc,
+ u32 rspec,
+ bool use_rspec, u16 mimo_ctlchbw);
+extern u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only,
+ u32 rts_rate,
+ u32 frame_rate,
+ u8 rts_preamble_type,
+ u8 frame_preamble_type, uint frame_len,
+ bool ba);
+extern void brcms_c_inval_dma_pkts(struct brcms_hardware *hw,
+ struct ieee80211_sta *sta,
+ void (*dma_callback_fn));
+extern void brcms_c_update_beacon(struct brcms_c_info *wlc);
+extern void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend);
+extern int brcms_c_set_nmode(struct brcms_c_info *wlc);
+extern void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc,
+ u32 bcn_rate);
+extern void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw,
+ u8 antsel_type);
+extern void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw,
+ u16 chanspec,
+ bool mute, struct txpwr_limits *txpwr);
+extern void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset,
+ u16 v);
+extern u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset);
+extern void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask,
+ u16 val, int bands);
+extern void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags);
+extern void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val);
+extern void brcms_b_phy_reset(struct brcms_hardware *wlc_hw);
+extern void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw);
+extern void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw);
+extern void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw,
+ u32 override_bit);
+extern void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw,
+ u32 override_bit);
+extern void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw,
+ int offset, int len, void *buf);
+extern u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate);
+extern void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw,
+ uint offset, const void *buf, int len,
+ u32 sel);
+extern void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset,
+ void *buf, int len, u32 sel);
+extern void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode);
+extern u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw);
+extern void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk);
+extern void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk);
+extern void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on);
+extern void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant);
+extern void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw,
+ u8 stf_mode);
+extern void brcms_c_init_scb(struct scb *scb);
+
+#endif /* _BRCM_MAIN_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/nicpci.c b/drivers/net/wireless/brcm80211/brcmsmac/nicpci.c
new file mode 100644
index 000000000000..0bcb26792046
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/nicpci.c
@@ -0,0 +1,835 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <defs.h>
+#include <soc.h>
+#include <chipcommon.h>
+#include "aiutils.h"
+#include "pub.h"
+#include "nicpci.h"
+
+/* SPROM offsets */
+#define SRSH_ASPM_OFFSET 4 /* word 4 */
+#define SRSH_ASPM_ENB 0x18 /* bit 3, 4 */
+#define SRSH_ASPM_L1_ENB 0x10 /* bit 4 */
+#define SRSH_ASPM_L0s_ENB 0x8 /* bit 3 */
+
+#define SRSH_PCIE_MISC_CONFIG 5 /* word 5 */
+#define SRSH_L23READY_EXIT_NOPERST 0x8000 /* bit 15 */
+#define SRSH_CLKREQ_OFFSET_REV5 20 /* word 20 for srom rev <= 5 */
+#define SRSH_CLKREQ_ENB 0x0800 /* bit 11 */
+#define SRSH_BD_OFFSET 6 /* word 6 */
+
+/* chipcontrol */
+#define CHIPCTRL_4321_PLL_DOWN 0x800000/* serdes PLL down override */
+
+/* MDIO control */
+#define MDIOCTL_DIVISOR_MASK 0x7f /* clock to be used on MDIO */
+#define MDIOCTL_DIVISOR_VAL 0x2
+#define MDIOCTL_PREAM_EN 0x80 /* Enable preamble sequnce */
+#define MDIOCTL_ACCESS_DONE 0x100 /* Transaction complete */
+
+/* MDIO Data */
+#define MDIODATA_MASK 0x0000ffff /* data 2 bytes */
+#define MDIODATA_TA 0x00020000 /* Turnaround */
+
+#define MDIODATA_REGADDR_SHF 18 /* Regaddr shift */
+#define MDIODATA_REGADDR_MASK 0x007c0000 /* Regaddr Mask */
+#define MDIODATA_DEVADDR_SHF 23 /* Physmedia devaddr shift */
+#define MDIODATA_DEVADDR_MASK 0x0f800000
+ /* Physmedia devaddr Mask */
+
+/* MDIO Data for older revisions < 10 */
+#define MDIODATA_REGADDR_SHF_OLD 18 /* Regaddr shift */
+#define MDIODATA_REGADDR_MASK_OLD 0x003c0000
+ /* Regaddr Mask */
+#define MDIODATA_DEVADDR_SHF_OLD 22 /* Physmedia devaddr shift */
+#define MDIODATA_DEVADDR_MASK_OLD 0x0fc00000
+ /* Physmedia devaddr Mask */
+
+/* Transactions flags */
+#define MDIODATA_WRITE 0x10000000
+#define MDIODATA_READ 0x20000000
+#define MDIODATA_START 0x40000000
+
+#define MDIODATA_DEV_ADDR 0x0 /* dev address for serdes */
+#define MDIODATA_BLK_ADDR 0x1F /* blk address for serdes */
+
+/* serdes regs (rev < 10) */
+#define MDIODATA_DEV_PLL 0x1d /* SERDES PLL Dev */
+#define MDIODATA_DEV_TX 0x1e /* SERDES TX Dev */
+#define MDIODATA_DEV_RX 0x1f /* SERDES RX Dev */
+
+/* SERDES RX registers */
+#define SERDES_RX_CTRL 1 /* Rx cntrl */
+#define SERDES_RX_TIMER1 2 /* Rx Timer1 */
+#define SERDES_RX_CDR 6 /* CDR */
+#define SERDES_RX_CDRBW 7 /* CDR BW */
+/* SERDES RX control register */
+#define SERDES_RX_CTRL_FORCE 0x80 /* rxpolarity_force */
+#define SERDES_RX_CTRL_POLARITY 0x40 /* rxpolarity_value */
+
+/* SERDES PLL registers */
+#define SERDES_PLL_CTRL 1 /* PLL control reg */
+#define PLL_CTRL_FREQDET_EN 0x4000 /* bit 14 is FREQDET on */
+
+/* Linkcontrol reg offset in PCIE Cap */
+#define PCIE_CAP_LINKCTRL_OFFSET 16 /* offset in pcie cap */
+#define PCIE_CAP_LCREG_ASPML0s 0x01 /* ASPM L0s in linkctrl */
+#define PCIE_CAP_LCREG_ASPML1 0x02 /* ASPM L1 in linkctrl */
+#define PCIE_CLKREQ_ENAB 0x100 /* CLKREQ Enab in linkctrl */
+
+#define PCIE_ASPM_ENAB 3 /* ASPM L0s & L1 in linkctrl */
+#define PCIE_ASPM_L1_ENAB 2 /* ASPM L0s & L1 in linkctrl */
+#define PCIE_ASPM_L0s_ENAB 1 /* ASPM L0s & L1 in linkctrl */
+#define PCIE_ASPM_DISAB 0 /* ASPM L0s & L1 in linkctrl */
+
+/* Power management threshold */
+#define PCIE_L1THRESHOLDTIME_MASK 0xFF00 /* bits 8 - 15 */
+#define PCIE_L1THRESHOLDTIME_SHIFT 8 /* PCIE_L1THRESHOLDTIME_SHIFT */
+#define PCIE_L1THRESHOLD_WARVAL 0x72 /* WAR value */
+#define PCIE_ASPMTIMER_EXTEND 0x01000000
+ /* > rev7:
+ * enable extend ASPM timer
+ */
+
+/* different register spaces to access thru pcie indirect access */
+#define PCIE_CONFIGREGS 1 /* Access to config space */
+#define PCIE_PCIEREGS 2 /* Access to pcie registers */
+
+/* PCIE protocol PHY diagnostic registers */
+#define PCIE_PLP_STATUSREG 0x204 /* Status */
+
+/* Status reg PCIE_PLP_STATUSREG */
+#define PCIE_PLP_POLARITYINV_STAT 0x10
+
+/* PCIE protocol DLLP diagnostic registers */
+#define PCIE_DLLP_LCREG 0x100 /* Link Control */
+#define PCIE_DLLP_PMTHRESHREG 0x128 /* Power Management Threshold */
+
+/* PCIE protocol TLP diagnostic registers */
+#define PCIE_TLP_WORKAROUNDSREG 0x004 /* TLP Workarounds */
+
+/* Sonics to PCI translation types */
+#define SBTOPCI_PREF 0x4 /* prefetch enable */
+#define SBTOPCI_BURST 0x8 /* burst enable */
+#define SBTOPCI_RC_READMULTI 0x20 /* memory read multiple */
+
+#define PCI_CLKRUN_DSBL 0x8000 /* Bit 15 forceClkrun */
+
+/* PCI core index in SROM shadow area */
+#define SRSH_PI_OFFSET 0 /* first word */
+#define SRSH_PI_MASK 0xf000 /* bit 15:12 */
+#define SRSH_PI_SHIFT 12 /* bit 15:12 */
+
+/* Sonics side: PCI core and host control registers */
+struct sbpciregs {
+ u32 control; /* PCI control */
+ u32 PAD[3];
+ u32 arbcontrol; /* PCI arbiter control */
+ u32 clkrun; /* Clkrun Control (>=rev11) */
+ u32 PAD[2];
+ u32 intstatus; /* Interrupt status */
+ u32 intmask; /* Interrupt mask */
+ u32 sbtopcimailbox; /* Sonics to PCI mailbox */
+ u32 PAD[9];
+ u32 bcastaddr; /* Sonics broadcast address */
+ u32 bcastdata; /* Sonics broadcast data */
+ u32 PAD[2];
+ u32 gpioin; /* ro: gpio input (>=rev2) */
+ u32 gpioout; /* rw: gpio output (>=rev2) */
+ u32 gpioouten; /* rw: gpio output enable (>= rev2) */
+ u32 gpiocontrol; /* rw: gpio control (>= rev2) */
+ u32 PAD[36];
+ u32 sbtopci0; /* Sonics to PCI translation 0 */
+ u32 sbtopci1; /* Sonics to PCI translation 1 */
+ u32 sbtopci2; /* Sonics to PCI translation 2 */
+ u32 PAD[189];
+ u32 pcicfg[4][64]; /* 0x400 - 0x7FF, PCI Cfg Space (>=rev8) */
+ u16 sprom[36]; /* SPROM shadow Area */
+ u32 PAD[46];
+};
+
+/* SB side: PCIE core and host control registers */
+struct sbpcieregs {
+ u32 control; /* host mode only */
+ u32 PAD[2];
+ u32 biststatus; /* bist Status: 0x00C */
+ u32 gpiosel; /* PCIE gpio sel: 0x010 */
+ u32 gpioouten; /* PCIE gpio outen: 0x14 */
+ u32 PAD[2];
+ u32 intstatus; /* Interrupt status: 0x20 */
+ u32 intmask; /* Interrupt mask: 0x24 */
+ u32 sbtopcimailbox; /* sb to pcie mailbox: 0x028 */
+ u32 PAD[53];
+ u32 sbtopcie0; /* sb to pcie translation 0: 0x100 */
+ u32 sbtopcie1; /* sb to pcie translation 1: 0x104 */
+ u32 sbtopcie2; /* sb to pcie translation 2: 0x108 */
+ u32 PAD[5];
+
+ /* pcie core supports in direct access to config space */
+ u32 configaddr; /* pcie config space access: Address field: 0x120 */
+ u32 configdata; /* pcie config space access: Data field: 0x124 */
+
+ /* mdio access to serdes */
+ u32 mdiocontrol; /* controls the mdio access: 0x128 */
+ u32 mdiodata; /* Data to the mdio access: 0x12c */
+
+ /* pcie protocol phy/dllp/tlp register indirect access mechanism */
+ u32 pcieindaddr; /* indirect access to
+ * the internal register: 0x130
+ */
+ u32 pcieinddata; /* Data to/from the internal regsiter: 0x134 */
+
+ u32 clkreqenctrl; /* >= rev 6, Clkreq rdma control : 0x138 */
+ u32 PAD[177];
+ u32 pciecfg[4][64]; /* 0x400 - 0x7FF, PCIE Cfg Space */
+ u16 sprom[64]; /* SPROM shadow Area */
+};
+
+struct pcicore_info {
+ union {
+ struct sbpcieregs __iomem *pcieregs;
+ struct sbpciregs __iomem *pciregs;
+ } regs; /* Memory mapped register to the core */
+
+ struct si_pub *sih; /* System interconnect handle */
+ struct pci_dev *dev;
+ u8 pciecap_lcreg_offset;/* PCIE capability LCreg offset
+ * in the config space
+ */
+ bool pcie_pr42767;
+ u8 pcie_polarity;
+ u8 pcie_war_aspm_ovr; /* Override ASPM/Clkreq settings */
+
+ u8 pmecap_offset; /* PM Capability offset in the config space */
+ bool pmecap; /* Capable of generating PME */
+};
+
+#define PCIE_ASPM(sih) \
+ (((sih)->buscoretype == PCIE_CORE_ID) && \
+ (((sih)->buscorerev >= 3) && \
+ ((sih)->buscorerev <= 5)))
+
+
+/* delay needed between the mdio control/ mdiodata register data access */
+static void pr28829_delay(void)
+{
+ udelay(10);
+}
+
+/* Initialize the PCI core.
+ * It's caller's responsibility to make sure that this is done only once
+ */
+struct pcicore_info *pcicore_init(struct si_pub *sih, struct pci_dev *pdev,
+ void __iomem *regs)
+{
+ struct pcicore_info *pi;
+
+ /* alloc struct pcicore_info */
+ pi = kzalloc(sizeof(struct pcicore_info), GFP_ATOMIC);
+ if (pi == NULL)
+ return NULL;
+
+ pi->sih = sih;
+ pi->dev = pdev;
+
+ if (sih->buscoretype == PCIE_CORE_ID) {
+ u8 cap_ptr;
+ pi->regs.pcieregs = regs;
+ cap_ptr = pcicore_find_pci_capability(pi->dev, PCI_CAP_ID_EXP,
+ NULL, NULL);
+ pi->pciecap_lcreg_offset = cap_ptr + PCIE_CAP_LINKCTRL_OFFSET;
+ } else
+ pi->regs.pciregs = regs;
+
+ return pi;
+}
+
+void pcicore_deinit(struct pcicore_info *pch)
+{
+ kfree(pch);
+}
+
+/* return cap_offset if requested capability exists in the PCI config space */
+/* Note that it's caller's responsibility to make sure it's a pci bus */
+u8
+pcicore_find_pci_capability(struct pci_dev *dev, u8 req_cap_id,
+ unsigned char *buf, u32 *buflen)
+{
+ u8 cap_id;
+ u8 cap_ptr = 0;
+ u32 bufsize;
+ u8 byte_val;
+
+ /* check for Header type 0 */
+ pci_read_config_byte(dev, PCI_HEADER_TYPE, &byte_val);
+ if ((byte_val & 0x7f) != PCI_HEADER_TYPE_NORMAL)
+ goto end;
+
+ /* check if the capability pointer field exists */
+ pci_read_config_byte(dev, PCI_STATUS, &byte_val);
+ if (!(byte_val & PCI_STATUS_CAP_LIST))
+ goto end;
+
+ pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &cap_ptr);
+ /* check if the capability pointer is 0x00 */
+ if (cap_ptr == 0x00)
+ goto end;
+
+ /* loop thru the capability list
+ * and see if the pcie capability exists
+ */
+
+ pci_read_config_byte(dev, cap_ptr, &cap_id);
+
+ while (cap_id != req_cap_id) {
+ pci_read_config_byte(dev, cap_ptr + 1, &cap_ptr);
+ if (cap_ptr == 0x00)
+ break;
+ pci_read_config_byte(dev, cap_ptr, &cap_id);
+ }
+ if (cap_id != req_cap_id)
+ goto end;
+
+ /* found the caller requested capability */
+ if (buf != NULL && buflen != NULL) {
+ u8 cap_data;
+
+ bufsize = *buflen;
+ if (!bufsize)
+ goto end;
+ *buflen = 0;
+ /* copy the capability data excluding cap ID and next ptr */
+ cap_data = cap_ptr + 2;
+ if ((bufsize + cap_data) > PCI_SZPCR)
+ bufsize = PCI_SZPCR - cap_data;
+ *buflen = bufsize;
+ while (bufsize--) {
+ pci_read_config_byte(dev, cap_data, buf);
+ cap_data++;
+ buf++;
+ }
+ }
+end:
+ return cap_ptr;
+}
+
+/* ***** Register Access API */
+static uint
+pcie_readreg(struct sbpcieregs __iomem *pcieregs, uint addrtype, uint offset)
+{
+ uint retval = 0xFFFFFFFF;
+
+ switch (addrtype) {
+ case PCIE_CONFIGREGS:
+ W_REG(&pcieregs->configaddr, offset);
+ (void)R_REG((&pcieregs->configaddr));
+ retval = R_REG(&pcieregs->configdata);
+ break;
+ case PCIE_PCIEREGS:
+ W_REG(&pcieregs->pcieindaddr, offset);
+ (void)R_REG(&pcieregs->pcieindaddr);
+ retval = R_REG(&pcieregs->pcieinddata);
+ break;
+ }
+
+ return retval;
+}
+
+static uint pcie_writereg(struct sbpcieregs __iomem *pcieregs, uint addrtype,
+ uint offset, uint val)
+{
+ switch (addrtype) {
+ case PCIE_CONFIGREGS:
+ W_REG((&pcieregs->configaddr), offset);
+ W_REG((&pcieregs->configdata), val);
+ break;
+ case PCIE_PCIEREGS:
+ W_REG((&pcieregs->pcieindaddr), offset);
+ W_REG((&pcieregs->pcieinddata), val);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static bool pcie_mdiosetblock(struct pcicore_info *pi, uint blk)
+{
+ struct sbpcieregs __iomem *pcieregs = pi->regs.pcieregs;
+ uint mdiodata, i = 0;
+ uint pcie_serdes_spinwait = 200;
+
+ mdiodata = (MDIODATA_START | MDIODATA_WRITE | MDIODATA_TA |
+ (MDIODATA_DEV_ADDR << MDIODATA_DEVADDR_SHF) |
+ (MDIODATA_BLK_ADDR << MDIODATA_REGADDR_SHF) |
+ (blk << 4));
+ W_REG(&pcieregs->mdiodata, mdiodata);
+
+ pr28829_delay();
+ /* retry till the transaction is complete */
+ while (i < pcie_serdes_spinwait) {
+ if (R_REG(&pcieregs->mdiocontrol) & MDIOCTL_ACCESS_DONE)
+ break;
+
+ udelay(1000);
+ i++;
+ }
+
+ if (i >= pcie_serdes_spinwait)
+ return false;
+
+ return true;
+}
+
+static int
+pcie_mdioop(struct pcicore_info *pi, uint physmedia, uint regaddr, bool write,
+ uint *val)
+{
+ struct sbpcieregs __iomem *pcieregs = pi->regs.pcieregs;
+ uint mdiodata;
+ uint i = 0;
+ uint pcie_serdes_spinwait = 10;
+
+ /* enable mdio access to SERDES */
+ W_REG(&pcieregs->mdiocontrol, MDIOCTL_PREAM_EN | MDIOCTL_DIVISOR_VAL);
+
+ if (pi->sih->buscorerev >= 10) {
+ /* new serdes is slower in rw,
+ * using two layers of reg address mapping
+ */
+ if (!pcie_mdiosetblock(pi, physmedia))
+ return 1;
+ mdiodata = ((MDIODATA_DEV_ADDR << MDIODATA_DEVADDR_SHF) |
+ (regaddr << MDIODATA_REGADDR_SHF));
+ pcie_serdes_spinwait *= 20;
+ } else {
+ mdiodata = ((physmedia << MDIODATA_DEVADDR_SHF_OLD) |
+ (regaddr << MDIODATA_REGADDR_SHF_OLD));
+ }
+
+ if (!write)
+ mdiodata |= (MDIODATA_START | MDIODATA_READ | MDIODATA_TA);
+ else
+ mdiodata |= (MDIODATA_START | MDIODATA_WRITE | MDIODATA_TA |
+ *val);
+
+ W_REG(&pcieregs->mdiodata, mdiodata);
+
+ pr28829_delay();
+
+ /* retry till the transaction is complete */
+ while (i < pcie_serdes_spinwait) {
+ if (R_REG(&pcieregs->mdiocontrol) & MDIOCTL_ACCESS_DONE) {
+ if (!write) {
+ pr28829_delay();
+ *val = (R_REG(&pcieregs->mdiodata) &
+ MDIODATA_MASK);
+ }
+ /* Disable mdio access to SERDES */
+ W_REG(&pcieregs->mdiocontrol, 0);
+ return 0;
+ }
+ udelay(1000);
+ i++;
+ }
+
+ /* Timed out. Disable mdio access to SERDES. */
+ W_REG(&pcieregs->mdiocontrol, 0);
+ return 1;
+}
+
+/* use the mdio interface to read from mdio slaves */
+static int
+pcie_mdioread(struct pcicore_info *pi, uint physmedia, uint regaddr,
+ uint *regval)
+{
+ return pcie_mdioop(pi, physmedia, regaddr, false, regval);
+}
+
+/* use the mdio interface to write to mdio slaves */
+static int
+pcie_mdiowrite(struct pcicore_info *pi, uint physmedia, uint regaddr, uint val)
+{
+ return pcie_mdioop(pi, physmedia, regaddr, true, &val);
+}
+
+/* ***** Support functions ***** */
+static u8 pcie_clkreq(struct pcicore_info *pi, u32 mask, u32 val)
+{
+ u32 reg_val;
+ u8 offset;
+
+ offset = pi->pciecap_lcreg_offset;
+ if (!offset)
+ return 0;
+
+ pci_read_config_dword(pi->dev, offset, &reg_val);
+ /* set operation */
+ if (mask) {
+ if (val)
+ reg_val |= PCIE_CLKREQ_ENAB;
+ else
+ reg_val &= ~PCIE_CLKREQ_ENAB;
+ pci_write_config_dword(pi->dev, offset, reg_val);
+ pci_read_config_dword(pi->dev, offset, &reg_val);
+ }
+ if (reg_val & PCIE_CLKREQ_ENAB)
+ return 1;
+ else
+ return 0;
+}
+
+static void pcie_extendL1timer(struct pcicore_info *pi, bool extend)
+{
+ u32 w;
+ struct si_pub *sih = pi->sih;
+ struct sbpcieregs __iomem *pcieregs = pi->regs.pcieregs;
+
+ if (sih->buscoretype != PCIE_CORE_ID || sih->buscorerev < 7)
+ return;
+
+ w = pcie_readreg(pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG);
+ if (extend)
+ w |= PCIE_ASPMTIMER_EXTEND;
+ else
+ w &= ~PCIE_ASPMTIMER_EXTEND;
+ pcie_writereg(pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG, w);
+ w = pcie_readreg(pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG);
+}
+
+/* centralized clkreq control policy */
+static void pcie_clkreq_upd(struct pcicore_info *pi, uint state)
+{
+ struct si_pub *sih = pi->sih;
+
+ switch (state) {
+ case SI_DOATTACH:
+ if (PCIE_ASPM(sih))
+ pcie_clkreq(pi, 1, 0);
+ break;
+ case SI_PCIDOWN:
+ if (sih->buscorerev == 6) { /* turn on serdes PLL down */
+ ai_corereg(sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol_addr),
+ ~0, 0);
+ ai_corereg(sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol_data),
+ ~0x40, 0);
+ } else if (pi->pcie_pr42767) {
+ pcie_clkreq(pi, 1, 1);
+ }
+ break;
+ case SI_PCIUP:
+ if (sih->buscorerev == 6) { /* turn off serdes PLL down */
+ ai_corereg(sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol_addr),
+ ~0, 0);
+ ai_corereg(sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol_data),
+ ~0x40, 0x40);
+ } else if (PCIE_ASPM(sih)) { /* disable clkreq */
+ pcie_clkreq(pi, 1, 0);
+ }
+ break;
+ }
+}
+
+/* ***** PCI core WARs ***** */
+/* Done only once at attach time */
+static void pcie_war_polarity(struct pcicore_info *pi)
+{
+ u32 w;
+
+ if (pi->pcie_polarity != 0)
+ return;
+
+ w = pcie_readreg(pi->regs.pcieregs, PCIE_PCIEREGS, PCIE_PLP_STATUSREG);
+
+ /* Detect the current polarity at attach and force that polarity and
+ * disable changing the polarity
+ */
+ if ((w & PCIE_PLP_POLARITYINV_STAT) == 0)
+ pi->pcie_polarity = SERDES_RX_CTRL_FORCE;
+ else
+ pi->pcie_polarity = (SERDES_RX_CTRL_FORCE |
+ SERDES_RX_CTRL_POLARITY);
+}
+
+/* enable ASPM and CLKREQ if srom doesn't have it */
+/* Needs to happen when update to shadow SROM is needed
+ * : Coming out of 'standby'/'hibernate'
+ * : If pcie_war_aspm_ovr state changed
+ */
+static void pcie_war_aspm_clkreq(struct pcicore_info *pi)
+{
+ struct sbpcieregs __iomem *pcieregs = pi->regs.pcieregs;
+ struct si_pub *sih = pi->sih;
+ u16 val16;
+ u16 __iomem *reg16;
+ u32 w;
+
+ if (!PCIE_ASPM(sih))
+ return;
+
+ /* bypass this on QT or VSIM */
+ reg16 = &pcieregs->sprom[SRSH_ASPM_OFFSET];
+ val16 = R_REG(reg16);
+
+ val16 &= ~SRSH_ASPM_ENB;
+ if (pi->pcie_war_aspm_ovr == PCIE_ASPM_ENAB)
+ val16 |= SRSH_ASPM_ENB;
+ else if (pi->pcie_war_aspm_ovr == PCIE_ASPM_L1_ENAB)
+ val16 |= SRSH_ASPM_L1_ENB;
+ else if (pi->pcie_war_aspm_ovr == PCIE_ASPM_L0s_ENAB)
+ val16 |= SRSH_ASPM_L0s_ENB;
+
+ W_REG(reg16, val16);
+
+ pci_read_config_dword(pi->dev, pi->pciecap_lcreg_offset, &w);
+ w &= ~PCIE_ASPM_ENAB;
+ w |= pi->pcie_war_aspm_ovr;
+ pci_write_config_dword(pi->dev, pi->pciecap_lcreg_offset, w);
+
+ reg16 = &pcieregs->sprom[SRSH_CLKREQ_OFFSET_REV5];
+ val16 = R_REG(reg16);
+
+ if (pi->pcie_war_aspm_ovr != PCIE_ASPM_DISAB) {
+ val16 |= SRSH_CLKREQ_ENB;
+ pi->pcie_pr42767 = true;
+ } else
+ val16 &= ~SRSH_CLKREQ_ENB;
+
+ W_REG(reg16, val16);
+}
+
+/* Apply the polarity determined at the start */
+/* Needs to happen when coming out of 'standby'/'hibernate' */
+static void pcie_war_serdes(struct pcicore_info *pi)
+{
+ u32 w = 0;
+
+ if (pi->pcie_polarity != 0)
+ pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CTRL,
+ pi->pcie_polarity);
+
+ pcie_mdioread(pi, MDIODATA_DEV_PLL, SERDES_PLL_CTRL, &w);
+ if (w & PLL_CTRL_FREQDET_EN) {
+ w &= ~PLL_CTRL_FREQDET_EN;
+ pcie_mdiowrite(pi, MDIODATA_DEV_PLL, SERDES_PLL_CTRL, w);
+ }
+}
+
+/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
+/* Needs to happen when coming out of 'standby'/'hibernate' */
+static void pcie_misc_config_fixup(struct pcicore_info *pi)
+{
+ struct sbpcieregs __iomem *pcieregs = pi->regs.pcieregs;
+ u16 val16;
+ u16 __iomem *reg16;
+
+ reg16 = &pcieregs->sprom[SRSH_PCIE_MISC_CONFIG];
+ val16 = R_REG(reg16);
+
+ if ((val16 & SRSH_L23READY_EXIT_NOPERST) == 0) {
+ val16 |= SRSH_L23READY_EXIT_NOPERST;
+ W_REG(reg16, val16);
+ }
+}
+
+/* quick hack for testing */
+/* Needs to happen when coming out of 'standby'/'hibernate' */
+static void pcie_war_noplldown(struct pcicore_info *pi)
+{
+ struct sbpcieregs __iomem *pcieregs = pi->regs.pcieregs;
+ u16 __iomem *reg16;
+
+ /* turn off serdes PLL down */
+ ai_corereg(pi->sih, SI_CC_IDX, offsetof(struct chipcregs, chipcontrol),
+ CHIPCTRL_4321_PLL_DOWN, CHIPCTRL_4321_PLL_DOWN);
+
+ /* clear srom shadow backdoor */
+ reg16 = &pcieregs->sprom[SRSH_BD_OFFSET];
+ W_REG(reg16, 0);
+}
+
+/* Needs to happen when coming out of 'standby'/'hibernate' */
+static void pcie_war_pci_setup(struct pcicore_info *pi)
+{
+ struct si_pub *sih = pi->sih;
+ struct sbpcieregs __iomem *pcieregs = pi->regs.pcieregs;
+ u32 w;
+
+ if (sih->buscorerev == 0 || sih->buscorerev == 1) {
+ w = pcie_readreg(pcieregs, PCIE_PCIEREGS,
+ PCIE_TLP_WORKAROUNDSREG);
+ w |= 0x8;
+ pcie_writereg(pcieregs, PCIE_PCIEREGS,
+ PCIE_TLP_WORKAROUNDSREG, w);
+ }
+
+ if (sih->buscorerev == 1) {
+ w = pcie_readreg(pcieregs, PCIE_PCIEREGS, PCIE_DLLP_LCREG);
+ w |= 0x40;
+ pcie_writereg(pcieregs, PCIE_PCIEREGS, PCIE_DLLP_LCREG, w);
+ }
+
+ if (sih->buscorerev == 0) {
+ pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_TIMER1, 0x8128);
+ pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CDR, 0x0100);
+ pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CDRBW, 0x1466);
+ } else if (PCIE_ASPM(sih)) {
+ /* Change the L1 threshold for better performance */
+ w = pcie_readreg(pcieregs, PCIE_PCIEREGS,
+ PCIE_DLLP_PMTHRESHREG);
+ w &= ~PCIE_L1THRESHOLDTIME_MASK;
+ w |= PCIE_L1THRESHOLD_WARVAL << PCIE_L1THRESHOLDTIME_SHIFT;
+ pcie_writereg(pcieregs, PCIE_PCIEREGS,
+ PCIE_DLLP_PMTHRESHREG, w);
+
+ pcie_war_serdes(pi);
+
+ pcie_war_aspm_clkreq(pi);
+ } else if (pi->sih->buscorerev == 7)
+ pcie_war_noplldown(pi);
+
+ /* Note that the fix is actually in the SROM,
+ * that's why this is open-ended
+ */
+ if (pi->sih->buscorerev >= 6)
+ pcie_misc_config_fixup(pi);
+}
+
+/* ***** Functions called during driver state changes ***** */
+void pcicore_attach(struct pcicore_info *pi, int state)
+{
+ struct si_pub *sih = pi->sih;
+ u32 bfl2 = (u32)getintvar(sih, BRCMS_SROM_BOARDFLAGS2);
+
+ /* Determine if this board needs override */
+ if (PCIE_ASPM(sih)) {
+ if (bfl2 & BFL2_PCIEWAR_OVR)
+ pi->pcie_war_aspm_ovr = PCIE_ASPM_DISAB;
+ else
+ pi->pcie_war_aspm_ovr = PCIE_ASPM_ENAB;
+ }
+
+ /* These need to happen in this order only */
+ pcie_war_polarity(pi);
+
+ pcie_war_serdes(pi);
+
+ pcie_war_aspm_clkreq(pi);
+
+ pcie_clkreq_upd(pi, state);
+
+}
+
+void pcicore_hwup(struct pcicore_info *pi)
+{
+ if (!pi || pi->sih->buscoretype != PCIE_CORE_ID)
+ return;
+
+ pcie_war_pci_setup(pi);
+}
+
+void pcicore_up(struct pcicore_info *pi, int state)
+{
+ if (!pi || pi->sih->buscoretype != PCIE_CORE_ID)
+ return;
+
+ /* Restore L1 timer for better performance */
+ pcie_extendL1timer(pi, true);
+
+ pcie_clkreq_upd(pi, state);
+}
+
+/* When the device is going to enter D3 state
+ * (or the system is going to enter S3/S4 states)
+ */
+void pcicore_sleep(struct pcicore_info *pi)
+{
+ u32 w;
+
+ if (!pi || !PCIE_ASPM(pi->sih))
+ return;
+
+ pci_read_config_dword(pi->dev, pi->pciecap_lcreg_offset, &w);
+ w &= ~PCIE_CAP_LCREG_ASPML1;
+ pci_write_config_dword(pi->dev, pi->pciecap_lcreg_offset, w);
+
+ pi->pcie_pr42767 = false;
+}
+
+void pcicore_down(struct pcicore_info *pi, int state)
+{
+ if (!pi || pi->sih->buscoretype != PCIE_CORE_ID)
+ return;
+
+ pcie_clkreq_upd(pi, state);
+
+ /* Reduce L1 timer for better power savings */
+ pcie_extendL1timer(pi, false);
+}
+
+/* precondition: current core is sii->buscoretype */
+static void pcicore_fixcfg(struct pcicore_info *pi, u16 __iomem *reg16)
+{
+ struct si_info *sii = (struct si_info *)(pi->sih);
+ u16 val16;
+ uint pciidx;
+
+ pciidx = ai_coreidx(&sii->pub);
+ val16 = R_REG(reg16);
+ if (((val16 & SRSH_PI_MASK) >> SRSH_PI_SHIFT) != (u16)pciidx) {
+ val16 = (u16)(pciidx << SRSH_PI_SHIFT) |
+ (val16 & ~SRSH_PI_MASK);
+ W_REG(reg16, val16);
+ }
+}
+
+void
+pcicore_fixcfg_pci(struct pcicore_info *pi, struct sbpciregs __iomem *pciregs)
+{
+ pcicore_fixcfg(pi, &pciregs->sprom[SRSH_PI_OFFSET]);
+}
+
+void pcicore_fixcfg_pcie(struct pcicore_info *pi,
+ struct sbpcieregs __iomem *pcieregs)
+{
+ pcicore_fixcfg(pi, &pcieregs->sprom[SRSH_PI_OFFSET]);
+}
+
+/* precondition: current core is pci core */
+void
+pcicore_pci_setup(struct pcicore_info *pi, struct sbpciregs __iomem *pciregs)
+{
+ u32 w;
+
+ OR_REG(&pciregs->sbtopci2, SBTOPCI_PREF | SBTOPCI_BURST);
+
+ if (((struct si_info *)(pi->sih))->pub.buscorerev >= 11) {
+ OR_REG(&pciregs->sbtopci2, SBTOPCI_RC_READMULTI);
+ w = R_REG(&pciregs->clkrun);
+ W_REG(&pciregs->clkrun, w | PCI_CLKRUN_DSBL);
+ w = R_REG(&pciregs->clkrun);
+ }
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/nicpci.h b/drivers/net/wireless/brcm80211/brcmsmac/nicpci.h
new file mode 100644
index 000000000000..58aa80dc3329
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/nicpci.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_NICPCI_H_
+#define _BRCM_NICPCI_H_
+
+#include "types.h"
+
+/* PCI configuration address space size */
+#define PCI_SZPCR 256
+
+/* Brcm PCI configuration registers */
+/* backplane address space accessed by BAR0 */
+#define PCI_BAR0_WIN 0x80
+/* sprom property control */
+#define PCI_SPROM_CONTROL 0x88
+/* mask of PCI and other cores interrupts */
+#define PCI_INT_MASK 0x94
+/* backplane core interrupt mask bits offset */
+#define PCI_SBIM_SHIFT 8
+/* backplane address space accessed by second 4KB of BAR0 */
+#define PCI_BAR0_WIN2 0xac
+/* pci config space gpio input (>=rev3) */
+#define PCI_GPIO_IN 0xb0
+/* pci config space gpio output (>=rev3) */
+#define PCI_GPIO_OUT 0xb4
+/* pci config space gpio output enable (>=rev3) */
+#define PCI_GPIO_OUTEN 0xb8
+
+/* bar0 + 4K accesses external sprom */
+#define PCI_BAR0_SPROM_OFFSET (4 * 1024)
+/* bar0 + 6K accesses pci core registers */
+#define PCI_BAR0_PCIREGS_OFFSET (6 * 1024)
+/*
+ * pci core SB registers are at the end of the
+ * 8KB window, so their address is the "regular"
+ * address plus 4K
+ */
+#define PCI_BAR0_PCISBR_OFFSET (4 * 1024)
+/* bar0 window size Match with corerev 13 */
+#define PCI_BAR0_WINSZ (16 * 1024)
+/* On pci corerev >= 13 and all pcie, the bar0 is now 16KB and it maps: */
+/* bar0 + 8K accesses pci/pcie core registers */
+#define PCI_16KB0_PCIREGS_OFFSET (8 * 1024)
+/* bar0 + 12K accesses chipc core registers */
+#define PCI_16KB0_CCREGS_OFFSET (12 * 1024)
+
+struct sbpciregs;
+struct sbpcieregs;
+
+extern struct pcicore_info *pcicore_init(struct si_pub *sih,
+ struct pci_dev *pdev,
+ void __iomem *regs);
+extern void pcicore_deinit(struct pcicore_info *pch);
+extern void pcicore_attach(struct pcicore_info *pch, int state);
+extern void pcicore_hwup(struct pcicore_info *pch);
+extern void pcicore_up(struct pcicore_info *pch, int state);
+extern void pcicore_sleep(struct pcicore_info *pch);
+extern void pcicore_down(struct pcicore_info *pch, int state);
+extern u8 pcicore_find_pci_capability(struct pci_dev *dev, u8 req_cap_id,
+ unsigned char *buf, u32 *buflen);
+extern void pcicore_fixcfg_pci(struct pcicore_info *pch,
+ struct sbpciregs __iomem *pciregs);
+extern void pcicore_fixcfg_pcie(struct pcicore_info *pch,
+ struct sbpcieregs __iomem *pciregs);
+extern void pcicore_pci_setup(struct pcicore_info *pch,
+ struct sbpciregs __iomem *pciregs);
+
+#endif /* _BRCM_NICPCI_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/otp.c b/drivers/net/wireless/brcm80211/brcmsmac/otp.c
new file mode 100644
index 000000000000..edf551561fd8
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/otp.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include <brcm_hw_ids.h>
+#include <chipcommon.h>
+#include "aiutils.h"
+#include "otp.h"
+
+#define OTPS_GUP_MASK 0x00000f00
+#define OTPS_GUP_SHIFT 8
+/* h/w subregion is programmed */
+#define OTPS_GUP_HW 0x00000100
+/* s/w subregion is programmed */
+#define OTPS_GUP_SW 0x00000200
+/* chipid/pkgopt subregion is programmed */
+#define OTPS_GUP_CI 0x00000400
+/* fuse subregion is programmed */
+#define OTPS_GUP_FUSE 0x00000800
+
+/* Fields in otpprog in rev >= 21 */
+#define OTPP_COL_MASK 0x000000ff
+#define OTPP_COL_SHIFT 0
+#define OTPP_ROW_MASK 0x0000ff00
+#define OTPP_ROW_SHIFT 8
+#define OTPP_OC_MASK 0x0f000000
+#define OTPP_OC_SHIFT 24
+#define OTPP_READERR 0x10000000
+#define OTPP_VALUE_MASK 0x20000000
+#define OTPP_VALUE_SHIFT 29
+#define OTPP_START_BUSY 0x80000000
+#define OTPP_READ 0x40000000
+
+/* Opcodes for OTPP_OC field */
+#define OTPPOC_READ 0
+#define OTPPOC_BIT_PROG 1
+#define OTPPOC_VERIFY 3
+#define OTPPOC_INIT 4
+#define OTPPOC_SET 5
+#define OTPPOC_RESET 6
+#define OTPPOC_OCST 7
+#define OTPPOC_ROW_LOCK 8
+#define OTPPOC_PRESCN_TEST 9
+
+#define OTPTYPE_IPX(ccrev) ((ccrev) == 21 || (ccrev) >= 23)
+
+#define OTPP_TRIES 10000000 /* # of tries for OTPP */
+
+#define MAXNUMRDES 9 /* Maximum OTP redundancy entries */
+
+/* Fixed size subregions sizes in words */
+#define OTPGU_CI_SZ 2
+
+struct otpinfo;
+
+/* OTP function struct */
+struct otp_fn_s {
+ int (*init)(struct si_pub *sih, struct otpinfo *oi);
+ int (*read_region)(struct otpinfo *oi, int region, u16 *data,
+ uint *wlen);
+};
+
+struct otpinfo {
+ uint ccrev; /* chipc revision */
+ const struct otp_fn_s *fn; /* OTP functions */
+ struct si_pub *sih; /* Saved sb handle */
+
+ /* IPX OTP section */
+ u16 wsize; /* Size of otp in words */
+ u16 rows; /* Geometry */
+ u16 cols; /* Geometry */
+ u32 status; /* Flag bits (lock/prog/rv).
+ * (Reflected only when OTP is power cycled)
+ */
+ u16 hwbase; /* hardware subregion offset */
+ u16 hwlim; /* hardware subregion boundary */
+ u16 swbase; /* software subregion offset */
+ u16 swlim; /* software subregion boundary */
+ u16 fbase; /* fuse subregion offset */
+ u16 flim; /* fuse subregion boundary */
+ int otpgu_base; /* offset to General Use Region */
+};
+
+/* OTP layout */
+/* CC revs 21, 24 and 27 OTP General Use Region word offset */
+#define REVA4_OTPGU_BASE 12
+
+/* CC revs 23, 25, 26, 28 and above OTP General Use Region word offset */
+#define REVB8_OTPGU_BASE 20
+
+/* CC rev 36 OTP General Use Region word offset */
+#define REV36_OTPGU_BASE 12
+
+/* Subregion word offsets in General Use region */
+#define OTPGU_HSB_OFF 0
+#define OTPGU_SFB_OFF 1
+#define OTPGU_CI_OFF 2
+#define OTPGU_P_OFF 3
+#define OTPGU_SROM_OFF 4
+
+/* Flag bit offsets in General Use region */
+#define OTPGU_HWP_OFF 60
+#define OTPGU_SWP_OFF 61
+#define OTPGU_CIP_OFF 62
+#define OTPGU_FUSEP_OFF 63
+#define OTPGU_CIP_MSK 0x4000
+#define OTPGU_P_MSK 0xf000
+#define OTPGU_P_SHIFT (OTPGU_HWP_OFF % 16)
+
+/* OTP Size */
+#define OTP_SZ_FU_324 ((roundup(324, 8))/8) /* 324 bits */
+#define OTP_SZ_FU_288 (288/8) /* 288 bits */
+#define OTP_SZ_FU_216 (216/8) /* 216 bits */
+#define OTP_SZ_FU_72 (72/8) /* 72 bits */
+#define OTP_SZ_CHECKSUM (16/8) /* 16 bits */
+#define OTP4315_SWREG_SZ 178 /* 178 bytes */
+#define OTP_SZ_FU_144 (144/8) /* 144 bits */
+
+static u16
+ipxotp_otpr(struct otpinfo *oi, struct chipcregs __iomem *cc, uint wn)
+{
+ return R_REG(&cc->sromotp[wn]);
+}
+
+/*
+ * Calculate max HW/SW region byte size by subtracting fuse region
+ * and checksum size, osizew is oi->wsize (OTP size - GU size) in words
+ */
+static int ipxotp_max_rgnsz(struct si_pub *sih, int osizew)
+{
+ int ret = 0;
+
+ switch (sih->chip) {
+ case BCM43224_CHIP_ID:
+ case BCM43225_CHIP_ID:
+ ret = osizew * 2 - OTP_SZ_FU_72 - OTP_SZ_CHECKSUM;
+ break;
+ case BCM4313_CHIP_ID:
+ ret = osizew * 2 - OTP_SZ_FU_72 - OTP_SZ_CHECKSUM;
+ break;
+ default:
+ break; /* Don't know about this chip */
+ }
+
+ return ret;
+}
+
+static void _ipxotp_init(struct otpinfo *oi, struct chipcregs __iomem *cc)
+{
+ uint k;
+ u32 otpp, st;
+
+ /*
+ * record word offset of General Use Region
+ * for various chipcommon revs
+ */
+ if (oi->sih->ccrev == 21 || oi->sih->ccrev == 24
+ || oi->sih->ccrev == 27) {
+ oi->otpgu_base = REVA4_OTPGU_BASE;
+ } else if (oi->sih->ccrev == 36) {
+ /*
+ * OTP size greater than equal to 2KB (128 words),
+ * otpgu_base is similar to rev23
+ */
+ if (oi->wsize >= 128)
+ oi->otpgu_base = REVB8_OTPGU_BASE;
+ else
+ oi->otpgu_base = REV36_OTPGU_BASE;
+ } else if (oi->sih->ccrev == 23 || oi->sih->ccrev >= 25) {
+ oi->otpgu_base = REVB8_OTPGU_BASE;
+ }
+
+ /* First issue an init command so the status is up to date */
+ otpp =
+ OTPP_START_BUSY | ((OTPPOC_INIT << OTPP_OC_SHIFT) & OTPP_OC_MASK);
+
+ W_REG(&cc->otpprog, otpp);
+ for (k = 0;
+ ((st = R_REG(&cc->otpprog)) & OTPP_START_BUSY)
+ && (k < OTPP_TRIES); k++)
+ ;
+ if (k >= OTPP_TRIES)
+ return;
+
+ /* Read OTP lock bits and subregion programmed indication bits */
+ oi->status = R_REG(&cc->otpstatus);
+
+ if ((oi->sih->chip == BCM43224_CHIP_ID)
+ || (oi->sih->chip == BCM43225_CHIP_ID)) {
+ u32 p_bits;
+ p_bits =
+ (ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_P_OFF) &
+ OTPGU_P_MSK)
+ >> OTPGU_P_SHIFT;
+ oi->status |= (p_bits << OTPS_GUP_SHIFT);
+ }
+
+ /*
+ * h/w region base and fuse region limit are fixed to
+ * the top and the bottom of the general use region.
+ * Everything else can be flexible.
+ */
+ oi->hwbase = oi->otpgu_base + OTPGU_SROM_OFF;
+ oi->hwlim = oi->wsize;
+ if (oi->status & OTPS_GUP_HW) {
+ oi->hwlim =
+ ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_HSB_OFF) / 16;
+ oi->swbase = oi->hwlim;
+ } else
+ oi->swbase = oi->hwbase;
+
+ /* subtract fuse and checksum from beginning */
+ oi->swlim = ipxotp_max_rgnsz(oi->sih, oi->wsize) / 2;
+
+ if (oi->status & OTPS_GUP_SW) {
+ oi->swlim =
+ ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_SFB_OFF) / 16;
+ oi->fbase = oi->swlim;
+ } else
+ oi->fbase = oi->swbase;
+
+ oi->flim = oi->wsize;
+}
+
+static int ipxotp_init(struct si_pub *sih, struct otpinfo *oi)
+{
+ uint idx;
+ struct chipcregs __iomem *cc;
+
+ /* Make sure we're running IPX OTP */
+ if (!OTPTYPE_IPX(sih->ccrev))
+ return -EBADE;
+
+ /* Make sure OTP is not disabled */
+ if (ai_is_otp_disabled(sih))
+ return -EBADE;
+
+ /* Check for otp size */
+ switch ((sih->cccaps & CC_CAP_OTPSIZE) >> CC_CAP_OTPSIZE_SHIFT) {
+ case 0:
+ /* Nothing there */
+ return -EBADE;
+ case 1: /* 32x64 */
+ oi->rows = 32;
+ oi->cols = 64;
+ oi->wsize = 128;
+ break;
+ case 2: /* 64x64 */
+ oi->rows = 64;
+ oi->cols = 64;
+ oi->wsize = 256;
+ break;
+ case 5: /* 96x64 */
+ oi->rows = 96;
+ oi->cols = 64;
+ oi->wsize = 384;
+ break;
+ case 7: /* 16x64 *//* 1024 bits */
+ oi->rows = 16;
+ oi->cols = 64;
+ oi->wsize = 64;
+ break;
+ default:
+ /* Don't know the geometry */
+ return -EBADE;
+ }
+
+ /* Retrieve OTP region info */
+ idx = ai_coreidx(sih);
+ cc = ai_setcoreidx(sih, SI_CC_IDX);
+
+ _ipxotp_init(oi, cc);
+
+ ai_setcoreidx(sih, idx);
+
+ return 0;
+}
+
+static int
+ipxotp_read_region(struct otpinfo *oi, int region, u16 *data, uint *wlen)
+{
+ uint idx;
+ struct chipcregs __iomem *cc;
+ uint base, i, sz;
+
+ /* Validate region selection */
+ switch (region) {
+ case OTP_HW_RGN:
+ sz = (uint) oi->hwlim - oi->hwbase;
+ if (!(oi->status & OTPS_GUP_HW)) {
+ *wlen = sz;
+ return -ENODATA;
+ }
+ if (*wlen < sz) {
+ *wlen = sz;
+ return -EOVERFLOW;
+ }
+ base = oi->hwbase;
+ break;
+ case OTP_SW_RGN:
+ sz = ((uint) oi->swlim - oi->swbase);
+ if (!(oi->status & OTPS_GUP_SW)) {
+ *wlen = sz;
+ return -ENODATA;
+ }
+ if (*wlen < sz) {
+ *wlen = sz;
+ return -EOVERFLOW;
+ }
+ base = oi->swbase;
+ break;
+ case OTP_CI_RGN:
+ sz = OTPGU_CI_SZ;
+ if (!(oi->status & OTPS_GUP_CI)) {
+ *wlen = sz;
+ return -ENODATA;
+ }
+ if (*wlen < sz) {
+ *wlen = sz;
+ return -EOVERFLOW;
+ }
+ base = oi->otpgu_base + OTPGU_CI_OFF;
+ break;
+ case OTP_FUSE_RGN:
+ sz = (uint) oi->flim - oi->fbase;
+ if (!(oi->status & OTPS_GUP_FUSE)) {
+ *wlen = sz;
+ return -ENODATA;
+ }
+ if (*wlen < sz) {
+ *wlen = sz;
+ return -EOVERFLOW;
+ }
+ base = oi->fbase;
+ break;
+ case OTP_ALL_RGN:
+ sz = ((uint) oi->flim - oi->hwbase);
+ if (!(oi->status & (OTPS_GUP_HW | OTPS_GUP_SW))) {
+ *wlen = sz;
+ return -ENODATA;
+ }
+ if (*wlen < sz) {
+ *wlen = sz;
+ return -EOVERFLOW;
+ }
+ base = oi->hwbase;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ idx = ai_coreidx(oi->sih);
+ cc = ai_setcoreidx(oi->sih, SI_CC_IDX);
+
+ /* Read the data */
+ for (i = 0; i < sz; i++)
+ data[i] = ipxotp_otpr(oi, cc, base + i);
+
+ ai_setcoreidx(oi->sih, idx);
+ *wlen = sz;
+ return 0;
+}
+
+static const struct otp_fn_s ipxotp_fn = {
+ (int (*)(struct si_pub *, struct otpinfo *)) ipxotp_init,
+ (int (*)(struct otpinfo *, int, u16 *, uint *)) ipxotp_read_region,
+};
+
+static int otp_init(struct si_pub *sih, struct otpinfo *oi)
+{
+
+ int ret;
+
+ memset(oi, 0, sizeof(struct otpinfo));
+
+ oi->ccrev = sih->ccrev;
+
+ if (OTPTYPE_IPX(oi->ccrev))
+ oi->fn = &ipxotp_fn;
+
+ if (oi->fn == NULL)
+ return -EBADE;
+
+ oi->sih = sih;
+
+ ret = (oi->fn->init) (sih, oi);
+
+ return ret;
+}
+
+int
+otp_read_region(struct si_pub *sih, int region, u16 *data, uint *wlen) {
+ struct otpinfo otpinfo;
+ struct otpinfo *oi = &otpinfo;
+ int err = 0;
+
+ if (ai_is_otp_disabled(sih)) {
+ err = -EPERM;
+ goto out;
+ }
+
+ err = otp_init(sih, oi);
+ if (err)
+ goto out;
+
+ err = ((oi)->fn->read_region)(oi, region, data, wlen);
+
+ out:
+ return err;
+}
diff --git a/drivers/staging/brcm80211/brcmsmac/wlc_types.h b/drivers/net/wireless/brcm80211/brcmsmac/otp.h
index df6e04c6ac58..6b6d31cf9569 100644
--- a/drivers/staging/brcm80211/brcmsmac/wlc_types.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/otp.h
@@ -14,24 +14,23 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _wlc_types_h_
-#define _wlc_types_h_
+#ifndef _BRCM_OTP_H_
+#define _BRCM_OTP_H_
-/* forward declarations */
+#include "types.h"
-struct wlc_info;
-struct wlc_hw_info;
-struct wlc_if;
-struct wl_if;
-struct ampdu_info;
-struct antsel_info;
-struct bmac_pmq;
+/* OTP regions */
+#define OTP_HW_RGN 1
+#define OTP_SW_RGN 2
+#define OTP_CI_RGN 4
+#define OTP_FUSE_RGN 8
+/* From h/w region to end of OTP including checksum */
+#define OTP_ALL_RGN 0xf
-struct d11init;
+/* OTP Size */
+#define OTP_SZ_MAX (6144/8) /* maximum bytes in one CIS */
-#ifndef _hnddma_pub_
-#define _hnddma_pub_
-struct hnddma_pub;
-#endif /* _hnddma_pub_ */
+extern int otp_read_region(struct si_pub *sih, int region, u16 *data,
+ uint *wlen);
-#endif /* _wlc_types_h_ */
+#endif /* _BRCM_OTP_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_cmn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
index 6cba4dfbc3dd..a3149254cbcd 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_cmn.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
@@ -13,36 +13,47 @@
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-
-#include <wlc_cfg.h>
-
#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/bitops.h>
#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/pci.h>
+#include <linux/bitops.h>
+
+#include <brcm_hw_ids.h>
+#include <chipcommon.h>
+#include <aiutils.h>
+#include <d11.h>
+#include <phy_shim.h>
+#include "phy_hal.h"
+#include "phy_int.h"
+#include "phy_radio.h"
+#include "phy_lcn.h"
+#include "phyreg_n.h"
-#include <bcmdefs.h>
-#include <bcmnvram.h>
-#include <sbchipc.h>
-#include <bcmdevs.h>
-#include <sbhnddma.h>
+#define VALID_N_RADIO(radioid) ((radioid == BCM2055_ID) || \
+ (radioid == BCM2056_ID) || \
+ (radioid == BCM2057_ID))
-#include <wlc_phy_int.h>
-#include <wlc_phyreg_n.h>
-#include <wlc_phy_radio.h>
-#include <wlc_phy_lcn.h>
+#define VALID_LCN_RADIO(radioid) (radioid == BCM2064_ID)
-u32 phyhal_msg_level = PHYHAL_ERROR;
+#define VALID_RADIO(pi, radioid) ( \
+ (ISNPHY(pi) ? VALID_N_RADIO(radioid) : false) || \
+ (ISLCNPHY(pi) ? VALID_LCN_RADIO(radioid) : false))
-typedef struct _chan_info_basic {
+/* basic mux operation - can be optimized on several architectures */
+#define MUX(pred, true, false) ((pred) ? (true) : (false))
+
+/* modulo inc/dec - assumes x E [0, bound - 1] */
+#define MODINC(x, bound) MUX((x) == (bound) - 1, 0, (x) + 1)
+
+/* modulo inc/dec, bound = 2^k */
+#define MODDEC_POW2(x, bound) (((x) - 1) & ((bound) - 1))
+#define MODINC_POW2(x, bound) (((x) + 1) & ((bound) - 1))
+
+struct chan_info_basic {
u16 chan;
u16 freq;
-} chan_info_basic_t;
-
-static chan_info_basic_t chan_info_all[] = {
+};
+static const struct chan_info_basic chan_info_all[] = {
{1, 2412},
{2, 2417},
{3, 2422},
@@ -101,134 +112,61 @@ static chan_info_basic_t chan_info_all[] = {
{216, 50800}
};
-u16 ltrn_list[PHY_LTRN_LIST_LEN] = {
- 0x18f9, 0x0d01, 0x00e4, 0xdef4, 0x06f1, 0x0ffc,
- 0xfa27, 0x1dff, 0x10f0, 0x0918, 0xf20a, 0xe010,
- 0x1417, 0x1104, 0xf114, 0xf2fa, 0xf7db, 0xe2fc,
- 0xe1fb, 0x13ee, 0xff0d, 0xe91c, 0x171a, 0x0318,
- 0xda00, 0x03e8, 0x17e6, 0xe9e4, 0xfff3, 0x1312,
- 0xe105, 0xe204, 0xf725, 0xf206, 0xf1ec, 0x11fc,
- 0x14e9, 0xe0f0, 0xf2f6, 0x09e8, 0x1010, 0x1d01,
- 0xfad9, 0x0f04, 0x060f, 0xde0c, 0x001c, 0x0dff,
- 0x1807, 0xf61a, 0xe40e, 0x0f16, 0x05f9, 0x18ec,
- 0x0a1b, 0xff1e, 0x2600, 0xffe2, 0x0ae5, 0x1814,
- 0x0507, 0x0fea, 0xe4f2, 0xf6e6
-};
-
const u8 ofdm_rate_lookup[] = {
- WLC_RATE_48M,
- WLC_RATE_24M,
- WLC_RATE_12M,
- WLC_RATE_6M,
- WLC_RATE_54M,
- WLC_RATE_36M,
- WLC_RATE_18M,
- WLC_RATE_9M
+ BRCM_RATE_48M,
+ BRCM_RATE_24M,
+ BRCM_RATE_12M,
+ BRCM_RATE_6M,
+ BRCM_RATE_54M,
+ BRCM_RATE_36M,
+ BRCM_RATE_18M,
+ BRCM_RATE_9M
};
-#define PHY_WREG_LIMIT 24
-
-static void wlc_set_phy_uninitted(phy_info_t *pi);
-static u32 wlc_phy_get_radio_ver(phy_info_t *pi);
-static void wlc_phy_timercb_phycal(void *arg);
-
-static bool wlc_phy_noise_calc_phy(phy_info_t *pi, u32 *cmplx_pwr,
- s8 *pwr_ant);
-
-static void wlc_phy_cal_perical_mphase_schedule(phy_info_t *pi, uint delay);
-static void wlc_phy_noise_cb(phy_info_t *pi, u8 channel, s8 noise_dbm);
-static void wlc_phy_noise_sample_request(wlc_phy_t *pih, u8 reason,
- u8 ch);
-
-static void wlc_phy_txpower_reg_limit_calc(phy_info_t *pi,
- struct txpwr_limits *tp, chanspec_t);
-static bool wlc_phy_cal_txpower_recalc_sw(phy_info_t *pi);
-
-static s8 wlc_user_txpwr_antport_to_rfport(phy_info_t *pi, uint chan,
- u32 band, u8 rate);
-static void wlc_phy_upd_env_txpwr_rate_limits(phy_info_t *pi, u32 band);
-static s8 wlc_phy_env_measure_vbat(phy_info_t *pi);
-static s8 wlc_phy_env_measure_temperature(phy_info_t *pi);
-
-char *phy_getvar(phy_info_t *pi, const char *name)
-{
- char *vars = pi->vars;
- char *s;
- int len;
-
- if (!name)
- return NULL;
-
- len = strlen(name);
- if (len == 0)
- return NULL;
-
- for (s = vars; s && *s;) {
- if ((memcmp(s, name, len) == 0) && (s[len] == '='))
- return &s[len + 1];
-
- while (*s++)
- ;
- }
-
- return nvram_get(name);
-}
+#define PHY_WREG_LIMIT 24
-int phy_getintvar(phy_info_t *pi, const char *name)
+void wlc_phyreg_enter(struct brcms_phy_pub *pih)
{
- char *val;
-
- val = PHY_GETVAR(pi, name);
- if (val == NULL)
- return 0;
-
- return simple_strtoul(val, NULL, 0);
-}
-
-void wlc_phyreg_enter(wlc_phy_t *pih)
-{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
wlapi_bmac_ucode_wake_override_phyreg_set(pi->sh->physhim);
}
-void wlc_phyreg_exit(wlc_phy_t *pih)
+void wlc_phyreg_exit(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
wlapi_bmac_ucode_wake_override_phyreg_clear(pi->sh->physhim);
}
-void wlc_radioreg_enter(wlc_phy_t *pih)
+void wlc_radioreg_enter(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
wlapi_bmac_mctrl(pi->sh->physhim, MCTL_LOCK_RADIO, MCTL_LOCK_RADIO);
udelay(10);
}
-void wlc_radioreg_exit(wlc_phy_t *pih)
+void wlc_radioreg_exit(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
- volatile u16 dummy;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
+ u16 dummy;
dummy = R_REG(&pi->regs->phyversion);
pi->phy_wreg = 0;
wlapi_bmac_mctrl(pi->sh->physhim, MCTL_LOCK_RADIO, 0);
}
-u16 read_radio_reg(phy_info_t *pi, u16 addr)
+u16 read_radio_reg(struct brcms_phy *pi, u16 addr)
{
u16 data;
if ((addr == RADIO_IDCODE))
return 0xffff;
- if (NORADIO_ENAB(pi->pubpi))
- return NORADIO_IDCODE & 0xffff;
-
switch (pi->pubpi.phy_type) {
case PHY_TYPE_N:
- CASECHECK(PHYTYPE, PHY_TYPE_N);
+ if (!CONF_HAS(PHYTYPE, PHY_TYPE_N))
+ break;
if (NREV_GE(pi->pubpi.phy_rev, 7))
addr |= RADIO_2057_READ_OFF;
else
@@ -236,7 +174,8 @@ u16 read_radio_reg(phy_info_t *pi, u16 addr)
break;
case PHY_TYPE_LCN:
- CASECHECK(PHYTYPE, PHY_TYPE_LCN);
+ if (!CONF_HAS(PHYTYPE, PHY_TYPE_LCN))
+ break;
addr |= RADIO_2064_READ_OFF;
break;
@@ -247,16 +186,10 @@ u16 read_radio_reg(phy_info_t *pi, u16 addr)
if ((D11REV_GE(pi->sh->corerev, 24)) ||
(D11REV_IS(pi->sh->corerev, 22)
&& (pi->pubpi.phy_type != PHY_TYPE_SSN))) {
- W_REG(&pi->regs->radioregaddr, addr);
-#ifdef __mips__
- (void)R_REG(&pi->regs->radioregaddr);
-#endif
+ W_REG_FLUSH(&pi->regs->radioregaddr, addr);
data = R_REG(&pi->regs->radioregdata);
} else {
- W_REG(&pi->regs->phy4waddr, addr);
-#ifdef __mips__
- (void)R_REG(&pi->regs->phy4waddr);
-#endif
+ W_REG_FLUSH(&pi->regs->phy4waddr, addr);
#ifdef __ARM_ARCH_4T__
__asm__(" .align 4 ");
@@ -272,69 +205,43 @@ u16 read_radio_reg(phy_info_t *pi, u16 addr)
return data;
}
-void write_radio_reg(phy_info_t *pi, u16 addr, u16 val)
+void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val)
{
- if (NORADIO_ENAB(pi->pubpi))
- return;
-
if ((D11REV_GE(pi->sh->corerev, 24)) ||
(D11REV_IS(pi->sh->corerev, 22)
&& (pi->pubpi.phy_type != PHY_TYPE_SSN))) {
- W_REG(&pi->regs->radioregaddr, addr);
-#ifdef __mips__
- (void)R_REG(&pi->regs->radioregaddr);
-#endif
+ W_REG_FLUSH(&pi->regs->radioregaddr, addr);
W_REG(&pi->regs->radioregdata, val);
} else {
- W_REG(&pi->regs->phy4waddr, addr);
-#ifdef __mips__
- (void)R_REG(&pi->regs->phy4waddr);
-#endif
+ W_REG_FLUSH(&pi->regs->phy4waddr, addr);
W_REG(&pi->regs->phy4wdatalo, val);
}
- if (pi->sh->bustype == PCI_BUS) {
- if (++pi->phy_wreg >= pi->phy_wreg_limit) {
- (void)R_REG(&pi->regs->maccontrol);
- pi->phy_wreg = 0;
- }
+ if (++pi->phy_wreg >= pi->phy_wreg_limit) {
+ (void)R_REG(&pi->regs->maccontrol);
+ pi->phy_wreg = 0;
}
}
-static u32 read_radio_id(phy_info_t *pi)
+static u32 read_radio_id(struct brcms_phy *pi)
{
u32 id;
- if (NORADIO_ENAB(pi->pubpi))
- return NORADIO_IDCODE;
-
if (D11REV_GE(pi->sh->corerev, 24)) {
u32 b0, b1, b2;
- W_REG(&pi->regs->radioregaddr, 0);
-#ifdef __mips__
- (void)R_REG(&pi->regs->radioregaddr);
-#endif
+ W_REG_FLUSH(&pi->regs->radioregaddr, 0);
b0 = (u32) R_REG(&pi->regs->radioregdata);
- W_REG(&pi->regs->radioregaddr, 1);
-#ifdef __mips__
- (void)R_REG(&pi->regs->radioregaddr);
-#endif
+ W_REG_FLUSH(&pi->regs->radioregaddr, 1);
b1 = (u32) R_REG(&pi->regs->radioregdata);
- W_REG(&pi->regs->radioregaddr, 2);
-#ifdef __mips__
- (void)R_REG(&pi->regs->radioregaddr);
-#endif
+ W_REG_FLUSH(&pi->regs->radioregaddr, 2);
b2 = (u32) R_REG(&pi->regs->radioregdata);
id = ((b0 & 0xf) << 28) | (((b2 << 8) | b1) << 12) | ((b0 >> 4)
& 0xf);
} else {
- W_REG(&pi->regs->phy4waddr, RADIO_IDCODE);
-#ifdef __mips__
- (void)R_REG(&pi->regs->phy4waddr);
-#endif
+ W_REG_FLUSH(&pi->regs->phy4waddr, RADIO_IDCODE);
id = (u32) R_REG(&pi->regs->phy4wdatalo);
id |= (u32) R_REG(&pi->regs->phy4wdatahi) << 16;
}
@@ -342,141 +249,113 @@ static u32 read_radio_id(phy_info_t *pi)
return id;
}
-void and_radio_reg(phy_info_t *pi, u16 addr, u16 val)
+void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val)
{
u16 rval;
- if (NORADIO_ENAB(pi->pubpi))
- return;
-
rval = read_radio_reg(pi, addr);
write_radio_reg(pi, addr, (rval & val));
}
-void or_radio_reg(phy_info_t *pi, u16 addr, u16 val)
+void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val)
{
u16 rval;
- if (NORADIO_ENAB(pi->pubpi))
- return;
-
rval = read_radio_reg(pi, addr);
write_radio_reg(pi, addr, (rval | val));
}
-void xor_radio_reg(phy_info_t *pi, u16 addr, u16 mask)
+void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask)
{
u16 rval;
- if (NORADIO_ENAB(pi->pubpi))
- return;
-
rval = read_radio_reg(pi, addr);
write_radio_reg(pi, addr, (rval ^ mask));
}
-void mod_radio_reg(phy_info_t *pi, u16 addr, u16 mask, u16 val)
+void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val)
{
u16 rval;
- if (NORADIO_ENAB(pi->pubpi))
- return;
-
rval = read_radio_reg(pi, addr);
write_radio_reg(pi, addr, (rval & ~mask) | (val & mask));
}
-void write_phy_channel_reg(phy_info_t *pi, uint val)
+void write_phy_channel_reg(struct brcms_phy *pi, uint val)
{
W_REG(&pi->regs->phychannel, val);
}
-u16 read_phy_reg(phy_info_t *pi, u16 addr)
+u16 read_phy_reg(struct brcms_phy *pi, u16 addr)
{
- d11regs_t *regs;
+ struct d11regs __iomem *regs;
regs = pi->regs;
- W_REG(&regs->phyregaddr, addr);
-#ifdef __mips__
- (void)R_REG(&regs->phyregaddr);
-#endif
+ W_REG_FLUSH(&regs->phyregaddr, addr);
pi->phy_wreg = 0;
return R_REG(&regs->phyregdata);
}
-void write_phy_reg(phy_info_t *pi, u16 addr, u16 val)
+void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val)
{
- d11regs_t *regs;
+ struct d11regs __iomem *regs;
regs = pi->regs;
-#ifdef __mips__
- W_REG(&regs->phyregaddr, addr);
- (void)R_REG(&regs->phyregaddr);
+#ifdef CONFIG_BCM47XX
+ W_REG_FLUSH(&regs->phyregaddr, addr);
W_REG(&regs->phyregdata, val);
if (addr == 0x72)
(void)R_REG(&regs->phyregdata);
#else
- W_REG((u32 *)(&regs->phyregaddr),
- addr | (val << 16));
- if (pi->sh->bustype == PCI_BUS) {
- if (++pi->phy_wreg >= pi->phy_wreg_limit) {
- pi->phy_wreg = 0;
- (void)R_REG(&regs->phyversion);
- }
+ W_REG((u32 __iomem *)(&regs->phyregaddr), addr | (val << 16));
+ if (++pi->phy_wreg >= pi->phy_wreg_limit) {
+ pi->phy_wreg = 0;
+ (void)R_REG(&regs->phyversion);
}
#endif
}
-void and_phy_reg(phy_info_t *pi, u16 addr, u16 val)
+void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val)
{
- d11regs_t *regs;
+ struct d11regs __iomem *regs;
regs = pi->regs;
- W_REG(&regs->phyregaddr, addr);
-#ifdef __mips__
- (void)R_REG(&regs->phyregaddr);
-#endif
+ W_REG_FLUSH(&regs->phyregaddr, addr);
W_REG(&regs->phyregdata, (R_REG(&regs->phyregdata) & val));
pi->phy_wreg = 0;
}
-void or_phy_reg(phy_info_t *pi, u16 addr, u16 val)
+void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val)
{
- d11regs_t *regs;
+ struct d11regs __iomem *regs;
regs = pi->regs;
- W_REG(&regs->phyregaddr, addr);
-#ifdef __mips__
- (void)R_REG(&regs->phyregaddr);
-#endif
+ W_REG_FLUSH(&regs->phyregaddr, addr);
W_REG(&regs->phyregdata, (R_REG(&regs->phyregdata) | val));
pi->phy_wreg = 0;
}
-void mod_phy_reg(phy_info_t *pi, u16 addr, u16 mask, u16 val)
+void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val)
{
- d11regs_t *regs;
+ struct d11regs __iomem *regs;
regs = pi->regs;
- W_REG(&regs->phyregaddr, addr);
-#ifdef __mips__
- (void)R_REG(&regs->phyregaddr);
-#endif
+ W_REG_FLUSH(&regs->phyregaddr, addr);
W_REG(&regs->phyregdata,
((R_REG(&regs->phyregdata) & ~mask) | (val & mask)));
pi->phy_wreg = 0;
}
-static void WLBANDINITFN(wlc_set_phy_uninitted) (phy_info_t *pi)
+static void wlc_set_phy_uninitted(struct brcms_phy *pi)
{
int i, j;
@@ -507,20 +386,18 @@ static void WLBANDINITFN(wlc_set_phy_uninitted) (phy_info_t *pi)
}
pi->radiopwr = 0xffff;
for (i = 0; i < STATIC_NUM_RF; i++) {
- for (j = 0; j < STATIC_NUM_BB; j++) {
+ for (j = 0; j < STATIC_NUM_BB; j++)
pi->stats_11b_txpower[i][j] = -1;
- }
}
}
-shared_phy_t *wlc_phy_shared_attach(shared_phy_params_t *shp)
+struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp)
{
- shared_phy_t *sh;
+ struct shared_phy *sh;
- sh = kzalloc(sizeof(shared_phy_t), GFP_ATOMIC);
- if (sh == NULL) {
+ sh = kzalloc(sizeof(struct shared_phy), GFP_ATOMIC);
+ if (sh == NULL)
return NULL;
- }
sh->sih = shp->sih;
sh->physhim = shp->physhim;
@@ -538,7 +415,6 @@ shared_phy_t *wlc_phy_shared_attach(shared_phy_params_t *shp)
sh->boardvendor = shp->boardvendor;
sh->boardflags = shp->boardflags;
sh->boardflags2 = shp->boardflags2;
- sh->bustype = shp->bustype;
sh->buscorerev = shp->buscorerev;
sh->fast_timer = PHY_SW_TIMER_FAST;
@@ -550,19 +426,45 @@ shared_phy_t *wlc_phy_shared_attach(shared_phy_params_t *shp)
return sh;
}
-void wlc_phy_shared_detach(shared_phy_t *phy_sh)
+static void wlc_phy_timercb_phycal(struct brcms_phy *pi)
{
- if (phy_sh) {
- kfree(phy_sh);
+ uint delay = 5;
+
+ if (PHY_PERICAL_MPHASE_PENDING(pi)) {
+ if (!pi->sh->up) {
+ wlc_phy_cal_perical_mphase_reset(pi);
+ return;
+ }
+
+ if (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi)) {
+
+ delay = 1000;
+ wlc_phy_cal_perical_mphase_restart(pi);
+ } else
+ wlc_phy_cal_perical_nphy_run(pi, PHY_PERICAL_AUTO);
+ wlapi_add_timer(pi->phycal_timer, delay, 0);
+ return;
}
+
+}
+
+static u32 wlc_phy_get_radio_ver(struct brcms_phy *pi)
+{
+ u32 ver;
+
+ ver = read_radio_id(pi);
+
+ return ver;
}
-wlc_phy_t *wlc_phy_attach(shared_phy_t *sh, void *regs, int bandtype,
- char *vars, struct wiphy *wiphy)
+struct brcms_phy_pub *
+wlc_phy_attach(struct shared_phy *sh, struct d11regs __iomem *regs,
+ int bandtype, struct wiphy *wiphy)
{
- phy_info_t *pi;
+ struct brcms_phy *pi;
u32 sflags = 0;
uint phyversion;
+ u32 idcode;
int i;
if (D11REV_IS(sh->corerev, 4))
@@ -570,42 +472,35 @@ wlc_phy_t *wlc_phy_attach(shared_phy_t *sh, void *regs, int bandtype,
else
sflags = ai_core_sflags(sh->sih, 0, 0);
- if (BAND_5G(bandtype)) {
- if ((sflags & (SISF_5G_PHY | SISF_DB_PHY)) == 0) {
+ if (bandtype == BRCM_BAND_5G) {
+ if ((sflags & (SISF_5G_PHY | SISF_DB_PHY)) == 0)
return NULL;
- }
}
pi = sh->phy_head;
if ((sflags & SISF_DB_PHY) && pi) {
-
wlapi_bmac_corereset(pi->sh->physhim, pi->pubpi.coreflags);
pi->refcnt++;
return &pi->pubpi_ro;
}
- pi = kzalloc(sizeof(phy_info_t), GFP_ATOMIC);
- if (pi == NULL) {
+ pi = kzalloc(sizeof(struct brcms_phy), GFP_ATOMIC);
+ if (pi == NULL)
return NULL;
- }
pi->wiphy = wiphy;
- pi->regs = (d11regs_t *) regs;
+ pi->regs = regs;
pi->sh = sh;
pi->phy_init_por = true;
pi->phy_wreg_limit = PHY_WREG_LIMIT;
- pi->vars = vars;
-
pi->txpwr_percent = 100;
pi->do_initcal = true;
pi->phycal_tempdelta = 0;
- if (BAND_2G(bandtype) && (sflags & SISF_2G_PHY)) {
-
+ if (bandtype == BRCM_BAND_2G && (sflags & SISF_2G_PHY))
pi->pubpi.coreflags = SICF_GMODE;
- }
wlapi_bmac_corereset(pi->sh->physhim, pi->pubpi.coreflags);
phyversion = R_REG(&pi->regs->phyversion);
@@ -620,46 +515,36 @@ wlc_phy_t *wlc_phy_attach(shared_phy_t *sh, void *regs, int bandtype,
pi->pubpi.phy_corenum = PHY_CORE_NUM_2;
pi->pubpi.ana_rev = (phyversion & PV_AV_MASK) >> PV_AV_SHIFT;
- if (!VALID_PHYTYPE(pi->pubpi.phy_type)) {
+ if (!pi->pubpi.phy_type == PHY_TYPE_N &&
+ !pi->pubpi.phy_type == PHY_TYPE_LCN)
goto err;
- }
- if (BAND_5G(bandtype)) {
- if (!ISNPHY(pi)) {
- goto err;
- }
- } else {
- if (!ISNPHY(pi) && !ISLCNPHY(pi)) {
+
+ if (bandtype == BRCM_BAND_5G) {
+ if (!ISNPHY(pi))
goto err;
- }
+ } else if (!ISNPHY(pi) && !ISLCNPHY(pi)) {
+ goto err;
}
- if (ISSIM_ENAB(pi->sh->sih)) {
- pi->pubpi.radioid = NORADIO_ID;
- pi->pubpi.radiorev = 5;
- } else {
- u32 idcode;
-
- wlc_phy_anacore((wlc_phy_t *) pi, ON);
-
- idcode = wlc_phy_get_radio_ver(pi);
- pi->pubpi.radioid =
- (idcode & IDCODE_ID_MASK) >> IDCODE_ID_SHIFT;
- pi->pubpi.radiorev =
- (idcode & IDCODE_REV_MASK) >> IDCODE_REV_SHIFT;
- pi->pubpi.radiover =
- (idcode & IDCODE_VER_MASK) >> IDCODE_VER_SHIFT;
- if (!VALID_RADIO(pi, pi->pubpi.radioid)) {
- goto err;
- }
+ wlc_phy_anacore((struct brcms_phy_pub *) pi, ON);
- wlc_phy_switch_radio((wlc_phy_t *) pi, OFF);
- }
+ idcode = wlc_phy_get_radio_ver(pi);
+ pi->pubpi.radioid =
+ (idcode & IDCODE_ID_MASK) >> IDCODE_ID_SHIFT;
+ pi->pubpi.radiorev =
+ (idcode & IDCODE_REV_MASK) >> IDCODE_REV_SHIFT;
+ pi->pubpi.radiover =
+ (idcode & IDCODE_VER_MASK) >> IDCODE_VER_SHIFT;
+ if (!VALID_RADIO(pi, pi->pubpi.radioid))
+ goto err;
+
+ wlc_phy_switch_radio((struct brcms_phy_pub *) pi, OFF);
wlc_set_phy_uninitted(pi);
pi->bw = WL_CHANSPEC_BW_20;
- pi->radio_chanspec =
- BAND_2G(bandtype) ? CH20MHZ_CHSPEC(1) : CH20MHZ_CHSPEC(36);
+ pi->radio_chanspec = (bandtype == BRCM_BAND_2G) ?
+ ch20mhz_chspec(1) : ch20mhz_chspec(36);
pi->rxiq_samps = PHY_NOISE_SAMPLE_LOG_NUM_NPHY;
pi->rxiq_antsel = ANT_RX_DIV_DEF;
@@ -681,7 +566,7 @@ wlc_phy_t *wlc_phy_attach(shared_phy_t *sh, void *regs, int bandtype,
pi->phy_txcore_disable_temp = PHY_CHAIN_TX_DISABLE_TEMP;
pi->phy_txcore_enable_temp =
- PHY_CHAIN_TX_DISABLE_TEMP - PHY_HYSTERESIS_DELTATEMP;
+ PHY_CHAIN_TX_DISABLE_TEMP - PHY_HYSTERESIS_DELTATEMP;
pi->phy_tempsense_offset = 0;
pi->phy_txcore_heatedup = false;
@@ -692,9 +577,9 @@ wlc_phy_t *wlc_phy_attach(shared_phy_t *sh, void *regs, int bandtype,
pi->phynoise_polling = false;
for (i = 0; i < TXP_NUM_RATES; i++) {
- pi->txpwr_limit[i] = WLC_TXPWR_MAX;
- pi->txpwr_env_limit[i] = WLC_TXPWR_MAX;
- pi->tx_user_target[i] = WLC_TXPWR_MAX;
+ pi->txpwr_limit[i] = BRCMS_TXPWR_MAX;
+ pi->txpwr_env_limit[i] = BRCMS_TXPWR_MAX;
+ pi->tx_user_target[i] = BRCMS_TXPWR_MAX;
}
pi->radiopwr_override = RADIOPWR_OVERRIDE_DEF;
@@ -704,11 +589,10 @@ wlc_phy_t *wlc_phy_attach(shared_phy_t *sh, void *regs, int bandtype,
if (ISNPHY(pi)) {
pi->phycal_timer = wlapi_init_timer(pi->sh->physhim,
- wlc_phy_timercb_phycal,
- pi, "phycal");
- if (!pi->phycal_timer) {
+ wlc_phy_timercb_phycal,
+ pi, "phycal");
+ if (!pi->phycal_timer)
goto err;
- }
if (!wlc_phy_attach_nphy(pi))
goto err;
@@ -717,36 +601,31 @@ wlc_phy_t *wlc_phy_attach(shared_phy_t *sh, void *regs, int bandtype,
if (!wlc_phy_attach_lcnphy(pi))
goto err;
- } else {
-
}
pi->refcnt++;
pi->next = pi->sh->phy_head;
sh->phy_head = pi;
- pi->vars = (char *)&pi->vars;
-
- memcpy(&pi->pubpi_ro, &pi->pubpi, sizeof(wlc_phy_t));
+ memcpy(&pi->pubpi_ro, &pi->pubpi, sizeof(struct brcms_phy_pub));
return &pi->pubpi_ro;
- err:
+err:
kfree(pi);
return NULL;
}
-void wlc_phy_detach(wlc_phy_t *pih)
+void wlc_phy_detach(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
if (pih) {
- if (--pi->refcnt) {
+ if (--pi->refcnt)
return;
- }
if (pi->phycal_timer) {
- wlapi_free_timer(pi->sh->physhim, pi->phycal_timer);
+ wlapi_free_timer(pi->phycal_timer);
pi->phycal_timer = NULL;
}
@@ -756,17 +635,17 @@ void wlc_phy_detach(wlc_phy_t *pih)
pi->sh->phy_head->next = NULL;
if (pi->pi_fptr.detach)
- (pi->pi_fptr.detach) (pi);
+ (pi->pi_fptr.detach)(pi);
kfree(pi);
}
}
bool
-wlc_phy_get_phyversion(wlc_phy_t *pih, u16 *phytype, u16 *phyrev,
+wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, u16 *phyrev,
u16 *radioid, u16 *radiover)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
*phytype = (u16) pi->pubpi.phy_type;
*phyrev = (u16) pi->pubpi.phy_rev;
*radioid = pi->pubpi.radioid;
@@ -775,44 +654,21 @@ wlc_phy_get_phyversion(wlc_phy_t *pih, u16 *phytype, u16 *phyrev,
return true;
}
-bool wlc_phy_get_encore(wlc_phy_t *pih)
+bool wlc_phy_get_encore(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
return pi->pubpi.abgphy_encore;
}
-u32 wlc_phy_get_coreflags(wlc_phy_t *pih)
+u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
return pi->pubpi.coreflags;
}
-static void wlc_phy_timercb_phycal(void *arg)
+void wlc_phy_anacore(struct brcms_phy_pub *pih, bool on)
{
- phy_info_t *pi = (phy_info_t *) arg;
- uint delay = 5;
-
- if (PHY_PERICAL_MPHASE_PENDING(pi)) {
- if (!pi->sh->up) {
- wlc_phy_cal_perical_mphase_reset(pi);
- return;
- }
-
- if (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi)) {
-
- delay = 1000;
- wlc_phy_cal_perical_mphase_restart(pi);
- } else
- wlc_phy_cal_perical_nphy_run(pi, PHY_PERICAL_AUTO);
- wlapi_add_timer(pi->sh->physhim, pi->phycal_timer, delay, 0);
- return;
- }
-
-}
-
-void wlc_phy_anacore(wlc_phy_t *pih, bool on)
-{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
if (ISNPHY(pi)) {
if (on) {
@@ -847,9 +703,9 @@ void wlc_phy_anacore(wlc_phy_t *pih, bool on)
}
}
-u32 wlc_phy_clk_bwbits(wlc_phy_t *pih)
+u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
u32 phy_bw_clkbits = 0;
@@ -872,16 +728,16 @@ u32 wlc_phy_clk_bwbits(wlc_phy_t *pih)
return phy_bw_clkbits;
}
-void WLBANDINITFN(wlc_phy_por_inform) (wlc_phy_t *ppi)
+void wlc_phy_por_inform(struct brcms_phy_pub *ppi)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
pi->phy_init_por = true;
}
-void wlc_phy_edcrs_lock(wlc_phy_t *pih, bool lock)
+void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
pi->edcrs_threshold_lock = lock;
@@ -891,16 +747,16 @@ void wlc_phy_edcrs_lock(wlc_phy_t *pih, bool lock)
write_phy_reg(pi, 0x22f, 0x3c0);
}
-void wlc_phy_initcal_enable(wlc_phy_t *pih, bool initcal)
+void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
pi->do_initcal = initcal;
}
-void wlc_phy_hw_clk_state_upd(wlc_phy_t *pih, bool newstate)
+void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *pih, bool newstate)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
if (!pi || !pi->sh)
return;
@@ -908,9 +764,9 @@ void wlc_phy_hw_clk_state_upd(wlc_phy_t *pih, bool newstate)
pi->sh->clk = newstate;
}
-void wlc_phy_hw_state_upd(wlc_phy_t *pih, bool newstate)
+void wlc_phy_hw_state_upd(struct brcms_phy_pub *pih, bool newstate)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
if (!pi || !pi->sh)
return;
@@ -918,11 +774,11 @@ void wlc_phy_hw_state_upd(wlc_phy_t *pih, bool newstate)
pi->sh->up = newstate;
}
-void WLBANDINITFN(wlc_phy_init) (wlc_phy_t *pih, chanspec_t chanspec)
+void wlc_phy_init(struct brcms_phy_pub *pih, u16 chanspec)
{
u32 mc;
- initfn_t phy_init = NULL;
- phy_info_t *pi = (phy_info_t *) pih;
+ void (*phy_init)(struct brcms_phy *) = NULL;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
if (pi->init_in_progress)
return;
@@ -935,9 +791,8 @@ void WLBANDINITFN(wlc_phy_init) (wlc_phy_t *pih, chanspec_t chanspec)
if (WARN(mc & MCTL_EN_MAC, "HW error MAC running on init"))
return;
- if (!(pi->measure_hold & PHY_HOLD_FOR_SCAN)) {
+ if (!(pi->measure_hold & PHY_HOLD_FOR_SCAN))
pi->measure_hold |= PHY_HOLD_FOR_NOT_ASSOC;
- }
if (WARN(!(ai_core_sflags(pi->sh->sih, 0, 0) & SISF_FCLKA),
"HW error SISF_FCLKA\n"))
@@ -945,9 +800,8 @@ void WLBANDINITFN(wlc_phy_init) (wlc_phy_t *pih, chanspec_t chanspec)
phy_init = pi->pi_fptr.init;
- if (phy_init == NULL) {
+ if (phy_init == NULL)
return;
- }
wlc_phy_anacore(pih, ON);
@@ -957,9 +811,9 @@ void WLBANDINITFN(wlc_phy_init) (wlc_phy_t *pih, chanspec_t chanspec)
pi->nphy_gain_boost = true;
- wlc_phy_switch_radio((wlc_phy_t *) pi, ON);
+ wlc_phy_switch_radio((struct brcms_phy_pub *) pi, ON);
- (*phy_init) (pi);
+ (*phy_init)(pi);
pi->phy_init_por = false;
@@ -969,15 +823,15 @@ void WLBANDINITFN(wlc_phy_init) (wlc_phy_t *pih, chanspec_t chanspec)
if (!(ISNPHY(pi)))
wlc_phy_txpower_update_shm(pi);
- wlc_phy_ant_rxdiv_set((wlc_phy_t *) pi, pi->sh->rx_antdiv);
+ wlc_phy_ant_rxdiv_set((struct brcms_phy_pub *) pi, pi->sh->rx_antdiv);
pi->init_in_progress = false;
}
-void wlc_phy_cal_init(wlc_phy_t *pih)
+void wlc_phy_cal_init(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
- initfn_t cal_init = NULL;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
+ void (*cal_init)(struct brcms_phy *) = NULL;
if (WARN((R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC) != 0,
"HW error: MAC enabled during phy cal\n"))
@@ -986,19 +840,19 @@ void wlc_phy_cal_init(wlc_phy_t *pih)
if (!pi->initialized) {
cal_init = pi->pi_fptr.calinit;
if (cal_init)
- (*cal_init) (pi);
+ (*cal_init)(pi);
pi->initialized = true;
}
}
-int wlc_phy_down(wlc_phy_t *pih)
+int wlc_phy_down(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
int callbacks = 0;
if (pi->phycal_timer
- && !wlapi_del_timer(pi->sh->physhim, pi->phycal_timer))
+ && !wlapi_del_timer(pi->phycal_timer))
callbacks++;
pi->nphy_iqcal_chanspec_2G = 0;
@@ -1007,17 +861,8 @@ int wlc_phy_down(wlc_phy_t *pih)
return callbacks;
}
-static u32 wlc_phy_get_radio_ver(phy_info_t *pi)
-{
- u32 ver;
-
- ver = read_radio_id(pi);
-
- return ver;
-}
-
void
-wlc_phy_table_addr(phy_info_t *pi, uint tbl_id, uint tbl_offset,
+wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, uint tbl_offset,
u16 tblAddr, u16 tblDataHi, u16 tblDataLo)
{
write_phy_reg(pi, tblAddr, (tbl_id << 10) | tbl_offset);
@@ -1025,19 +870,17 @@ wlc_phy_table_addr(phy_info_t *pi, uint tbl_id, uint tbl_offset,
pi->tbl_data_hi = tblDataHi;
pi->tbl_data_lo = tblDataLo;
- if ((pi->sh->chip == BCM43224_CHIP_ID ||
- pi->sh->chip == BCM43421_CHIP_ID) &&
- (pi->sh->chiprev == 1)) {
+ if (pi->sh->chip == BCM43224_CHIP_ID &&
+ pi->sh->chiprev == 1) {
pi->tbl_addr = tblAddr;
pi->tbl_save_id = tbl_id;
pi->tbl_save_offset = tbl_offset;
}
}
-void wlc_phy_table_data_write(phy_info_t *pi, uint width, u32 val)
+void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val)
{
- if ((pi->sh->chip == BCM43224_CHIP_ID ||
- pi->sh->chip == BCM43421_CHIP_ID) &&
+ if ((pi->sh->chip == BCM43224_CHIP_ID) &&
(pi->sh->chiprev == 1) &&
(pi->tbl_save_id == NPHY_TBL_ID_ANTSWCTRLLUT)) {
read_phy_reg(pi, pi->tbl_data_lo);
@@ -1048,17 +891,15 @@ void wlc_phy_table_data_write(phy_info_t *pi, uint width, u32 val)
}
if (width == 32) {
-
write_phy_reg(pi, pi->tbl_data_hi, (u16) (val >> 16));
write_phy_reg(pi, pi->tbl_data_lo, (u16) val);
} else {
-
write_phy_reg(pi, pi->tbl_data_lo, (u16) val);
}
}
void
-wlc_phy_write_table(phy_info_t *pi, const phytbl_info_t *ptbl_info,
+wlc_phy_write_table(struct brcms_phy *pi, const struct phytbl_info *ptbl_info,
u16 tblAddr, u16 tblDataHi, u16 tblDataLo)
{
uint idx;
@@ -1073,8 +914,7 @@ wlc_phy_write_table(phy_info_t *pi, const phytbl_info_t *ptbl_info,
for (idx = 0; idx < ptbl_info->tbl_len; idx++) {
- if ((pi->sh->chip == BCM43224_CHIP_ID ||
- pi->sh->chip == BCM43421_CHIP_ID) &&
+ if ((pi->sh->chip == BCM43224_CHIP_ID) &&
(pi->sh->chiprev == 1) &&
(tbl_id == NPHY_TBL_ID_ANTSWCTRLLUT)) {
read_phy_reg(pi, tblDataLo);
@@ -1084,22 +924,19 @@ wlc_phy_write_table(phy_info_t *pi, const phytbl_info_t *ptbl_info,
}
if (tbl_width == 32) {
-
write_phy_reg(pi, tblDataHi,
(u16) (ptbl_32b[idx] >> 16));
write_phy_reg(pi, tblDataLo, (u16) ptbl_32b[idx]);
} else if (tbl_width == 16) {
-
write_phy_reg(pi, tblDataLo, ptbl_16b[idx]);
} else {
-
write_phy_reg(pi, tblDataLo, ptbl_8b[idx]);
}
}
}
void
-wlc_phy_read_table(phy_info_t *pi, const phytbl_info_t *ptbl_info,
+wlc_phy_read_table(struct brcms_phy *pi, const struct phytbl_info *ptbl_info,
u16 tblAddr, u16 tblDataHi, u16 tblDataLo)
{
uint idx;
@@ -1114,8 +951,7 @@ wlc_phy_read_table(phy_info_t *pi, const phytbl_info_t *ptbl_info,
for (idx = 0; idx < ptbl_info->tbl_len; idx++) {
- if ((pi->sh->chip == BCM43224_CHIP_ID ||
- pi->sh->chip == BCM43421_CHIP_ID) &&
+ if ((pi->sh->chip == BCM43224_CHIP_ID) &&
(pi->sh->chiprev == 1)) {
(void)read_phy_reg(pi, tblDataLo);
@@ -1124,29 +960,26 @@ wlc_phy_read_table(phy_info_t *pi, const phytbl_info_t *ptbl_info,
}
if (tbl_width == 32) {
-
ptbl_32b[idx] = read_phy_reg(pi, tblDataLo);
ptbl_32b[idx] |= (read_phy_reg(pi, tblDataHi) << 16);
} else if (tbl_width == 16) {
-
ptbl_16b[idx] = read_phy_reg(pi, tblDataLo);
} else {
-
ptbl_8b[idx] = (u8) read_phy_reg(pi, tblDataLo);
}
}
}
uint
-wlc_phy_init_radio_regs_allbands(phy_info_t *pi, radio_20xx_regs_t *radioregs)
+wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi,
+ struct radio_20xx_regs *radioregs)
{
uint i = 0;
do {
- if (radioregs[i].do_init) {
+ if (radioregs[i].do_init)
write_radio_reg(pi, radioregs[i].address,
(u16) radioregs[i].init);
- }
i++;
} while (radioregs[i].address != 0xffff);
@@ -1155,7 +988,8 @@ wlc_phy_init_radio_regs_allbands(phy_info_t *pi, radio_20xx_regs_t *radioregs)
}
uint
-wlc_phy_init_radio_regs(phy_info_t *pi, radio_regs_t *radioregs,
+wlc_phy_init_radio_regs(struct brcms_phy *pi,
+ const struct radio_regs *radioregs,
u16 core_offset)
{
uint i = 0;
@@ -1169,7 +1003,7 @@ wlc_phy_init_radio_regs(phy_info_t *pi, radio_regs_t *radioregs,
address | core_offset,
(u16) radioregs[i].init_a);
if (ISNPHY(pi) && (++count % 4 == 0))
- WLC_PHY_WAR_PR51571(pi);
+ BRCMS_PHY_WAR_PR51571(pi);
}
} else {
if (radioregs[i].do_init_g) {
@@ -1178,7 +1012,7 @@ wlc_phy_init_radio_regs(phy_info_t *pi, radio_regs_t *radioregs,
address | core_offset,
(u16) radioregs[i].init_g);
if (ISNPHY(pi) && (++count % 4 == 0))
- WLC_PHY_WAR_PR51571(pi);
+ BRCMS_PHY_WAR_PR51571(pi);
}
}
@@ -1188,10 +1022,10 @@ wlc_phy_init_radio_regs(phy_info_t *pi, radio_regs_t *radioregs,
return i;
}
-void wlc_phy_do_dummy_tx(phy_info_t *pi, bool ofdm, bool pa_on)
+void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on)
{
-#define DUMMY_PKT_LEN 20
- d11regs_t *regs = pi->regs;
+#define DUMMY_PKT_LEN 20
+ struct d11regs __iomem *regs = pi->regs;
int i, count;
u8 ofdmpkt[DUMMY_PKT_LEN] = {
0xcc, 0x01, 0x02, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00,
@@ -1215,9 +1049,8 @@ void wlc_phy_do_dummy_tx(phy_info_t *pi, bool ofdm, bool pa_on)
W_REG(&regs->wepctl, 0);
W_REG(&regs->txe_phyctl, (ofdm ? 1 : 0) | PHY_TXC_ANT_0);
- if (ISNPHY(pi) || ISLCNPHY(pi)) {
+ if (ISNPHY(pi) || ISLCNPHY(pi))
W_REG(&regs->txe_phyctl1, 0x1A02);
- }
W_REG(&regs->txe_wm_0, 0);
W_REG(&regs->txe_wm_1, 0);
@@ -1243,22 +1076,15 @@ void wlc_phy_do_dummy_tx(phy_info_t *pi, bool ofdm, bool pa_on)
i = 0;
count = ofdm ? 30 : 250;
-
- if (ISSIM_ENAB(pi->sh->sih)) {
- count *= 100;
- }
-
while ((i++ < count)
- && (R_REG(&regs->txe_status) & (1 << 7))) {
+ && (R_REG(&regs->txe_status) & (1 << 7)))
udelay(10);
- }
i = 0;
while ((i++ < 10)
- && ((R_REG(&regs->txe_status) & (1 << 10)) == 0)) {
+ && ((R_REG(&regs->txe_status) & (1 << 10)) == 0))
udelay(10);
- }
i = 0;
@@ -1271,37 +1097,35 @@ void wlc_phy_do_dummy_tx(phy_info_t *pi, bool ofdm, bool pa_on)
}
}
-void wlc_phy_hold_upd(wlc_phy_t *pih, mbool id, bool set)
+void wlc_phy_hold_upd(struct brcms_phy_pub *pih, u32 id, bool set)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
- if (set) {
+ if (set)
mboolset(pi->measure_hold, id);
- } else {
+ else
mboolclr(pi->measure_hold, id);
- }
return;
}
-void wlc_phy_mute_upd(wlc_phy_t *pih, bool mute, mbool flags)
+void wlc_phy_mute_upd(struct brcms_phy_pub *pih, bool mute, u32 flags)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
- if (mute) {
+ if (mute)
mboolset(pi->measure_hold, PHY_HOLD_FOR_MUTE);
- } else {
+ else
mboolclr(pi->measure_hold, PHY_HOLD_FOR_MUTE);
- }
if (!mute && (flags & PHY_MUTE_FOR_PREISM))
pi->nphy_perical_last = pi->sh->now - pi->sh->glacial_timer;
return;
}
-void wlc_phy_clear_tssi(wlc_phy_t *pih)
+void wlc_phy_clear_tssi(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
if (ISNPHY(pi)) {
return;
@@ -1313,27 +1137,18 @@ void wlc_phy_clear_tssi(wlc_phy_t *pih)
}
}
-static bool wlc_phy_cal_txpower_recalc_sw(phy_info_t *pi)
+static bool wlc_phy_cal_txpower_recalc_sw(struct brcms_phy *pi)
{
return false;
}
-void wlc_phy_switch_radio(wlc_phy_t *pih, bool on)
+void wlc_phy_switch_radio(struct brcms_phy_pub *pih, bool on)
{
- phy_info_t *pi = (phy_info_t *) pih;
-
- if (NORADIO_ENAB(pi->pubpi))
- return;
-
- {
- uint mc;
-
- mc = R_REG(&pi->regs->maccontrol);
- }
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
+ (void)R_REG(&pi->regs->maccontrol);
if (ISNPHY(pi)) {
wlc_phy_switch_radio_nphy(pi, on);
-
} else if (ISLCNPHY(pi)) {
if (on) {
and_phy_reg(pi, 0x44c,
@@ -1361,40 +1176,39 @@ void wlc_phy_switch_radio(wlc_phy_t *pih, bool on)
}
}
-u16 wlc_phy_bw_state_get(wlc_phy_t *ppi)
+u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
return pi->bw;
}
-void wlc_phy_bw_state_set(wlc_phy_t *ppi, u16 bw)
+void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
pi->bw = bw;
}
-void wlc_phy_chanspec_radio_set(wlc_phy_t *ppi, chanspec_t newch)
+void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, u16 newch)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
pi->radio_chanspec = newch;
}
-chanspec_t wlc_phy_chanspec_get(wlc_phy_t *ppi)
+u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
return pi->radio_chanspec;
}
-void wlc_phy_chanspec_set(wlc_phy_t *ppi, chanspec_t chanspec)
+void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, u16 chanspec)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
u16 m_cur_channel;
- chansetfn_t chanspec_set = NULL;
-
+ void (*chanspec_set)(struct brcms_phy *, u16) = NULL;
m_cur_channel = CHSPEC_CHANNEL(chanspec);
if (CHSPEC_IS5G(chanspec))
m_cur_channel |= D11_CURCHANNEL_5G;
@@ -1404,7 +1218,7 @@ void wlc_phy_chanspec_set(wlc_phy_t *ppi, chanspec_t chanspec)
chanspec_set = pi->pi_fptr.chanset;
if (chanspec_set)
- (*chanspec_set) (pi, chanspec);
+ (*chanspec_set)(pi, chanspec);
}
@@ -1424,24 +1238,24 @@ int wlc_phy_chanspec_freq2bandrange_lpssn(uint freq)
return range;
}
-int wlc_phy_chanspec_bandrange_get(phy_info_t *pi, chanspec_t chanspec)
+int wlc_phy_chanspec_bandrange_get(struct brcms_phy *pi, u16 chanspec)
{
int range = -1;
uint channel = CHSPEC_CHANNEL(chanspec);
uint freq = wlc_phy_channel2freq(channel);
- if (ISNPHY(pi)) {
+ if (ISNPHY(pi))
range = wlc_phy_get_chan_freq_range_nphy(pi, channel);
- } else if (ISLCNPHY(pi)) {
+ else if (ISLCNPHY(pi))
range = wlc_phy_chanspec_freq2bandrange_lpssn(freq);
- }
return range;
}
-void wlc_phy_chanspec_ch14_widefilter_set(wlc_phy_t *ppi, bool wide_filter)
+void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi,
+ bool wide_filter)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
pi->channel_14_wide_filter = wide_filter;
@@ -1458,13 +1272,14 @@ int wlc_phy_channel2freq(uint channel)
}
void
-wlc_phy_chanspec_band_validch(wlc_phy_t *ppi, uint band, chanvec_t *channels)
+wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band,
+ struct brcms_chanvec *channels)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
uint i;
uint channel;
- memset(channels, 0, sizeof(chanvec_t));
+ memset(channels, 0, sizeof(struct brcms_chanvec));
for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) {
channel = chan_info_all[i].chan;
@@ -1473,23 +1288,23 @@ wlc_phy_chanspec_band_validch(wlc_phy_t *ppi, uint band, chanvec_t *channels)
&& (channel <= LAST_REF5_CHANNUM))
continue;
- if (((band == WLC_BAND_2G) && (channel <= CH_MAX_2G_CHANNEL)) ||
- ((band == WLC_BAND_5G) && (channel > CH_MAX_2G_CHANNEL)))
+ if ((band == BRCM_BAND_2G && channel <= CH_MAX_2G_CHANNEL) ||
+ (band == BRCM_BAND_5G && channel > CH_MAX_2G_CHANNEL))
setbit(channels->vec, channel);
}
}
-chanspec_t wlc_phy_chanspec_band_firstch(wlc_phy_t *ppi, uint band)
+u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, uint band)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
uint i;
uint channel;
- chanspec_t chspec;
+ u16 chspec;
for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) {
channel = chan_info_all[i].chan;
- if (ISNPHY(pi) && IS40MHZ(pi)) {
+ if (ISNPHY(pi) && pi->bw == WL_CHANSPEC_BW_40) {
uint j;
for (j = 0; j < ARRAY_SIZE(chan_info_all); j++) {
@@ -1501,32 +1316,31 @@ chanspec_t wlc_phy_chanspec_band_firstch(wlc_phy_t *ppi, uint band)
if (j == ARRAY_SIZE(chan_info_all))
continue;
- channel = UPPER_20_SB(channel);
- chspec =
- channel | WL_CHANSPEC_BW_40 |
- WL_CHANSPEC_CTL_SB_LOWER;
- if (band == WLC_BAND_2G)
+ channel = upper_20_sb(channel);
+ chspec = channel | WL_CHANSPEC_BW_40 |
+ WL_CHANSPEC_CTL_SB_LOWER;
+ if (band == BRCM_BAND_2G)
chspec |= WL_CHANSPEC_BAND_2G;
else
chspec |= WL_CHANSPEC_BAND_5G;
} else
- chspec = CH20MHZ_CHSPEC(channel);
+ chspec = ch20mhz_chspec(channel);
if ((pi->a_band_high_disable) && (channel >= FIRST_REF5_CHANNUM)
&& (channel <= LAST_REF5_CHANNUM))
continue;
- if (((band == WLC_BAND_2G) && (channel <= CH_MAX_2G_CHANNEL)) ||
- ((band == WLC_BAND_5G) && (channel > CH_MAX_2G_CHANNEL)))
+ if ((band == BRCM_BAND_2G && channel <= CH_MAX_2G_CHANNEL) ||
+ (band == BRCM_BAND_5G && channel > CH_MAX_2G_CHANNEL))
return chspec;
}
- return (chanspec_t) INVCHANSPEC;
+ return (u16) INVCHANSPEC;
}
-int wlc_phy_txpower_get(wlc_phy_t *ppi, uint *qdbm, bool *override)
+int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, bool *override)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
*qdbm = pi->tx_user_target[0];
if (override != NULL)
@@ -1534,41 +1348,42 @@ int wlc_phy_txpower_get(wlc_phy_t *ppi, uint *qdbm, bool *override)
return 0;
}
-void wlc_phy_txpower_target_set(wlc_phy_t *ppi, struct txpwr_limits *txpwr)
+void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi,
+ struct txpwr_limits *txpwr)
{
bool mac_enabled = false;
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
memcpy(&pi->tx_user_target[TXP_FIRST_CCK],
- &txpwr->cck[0], WLC_NUM_RATES_CCK);
+ &txpwr->cck[0], BRCMS_NUM_RATES_CCK);
memcpy(&pi->tx_user_target[TXP_FIRST_OFDM],
- &txpwr->ofdm[0], WLC_NUM_RATES_OFDM);
+ &txpwr->ofdm[0], BRCMS_NUM_RATES_OFDM);
memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_20_CDD],
- &txpwr->ofdm_cdd[0], WLC_NUM_RATES_OFDM);
+ &txpwr->ofdm_cdd[0], BRCMS_NUM_RATES_OFDM);
memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_40_SISO],
- &txpwr->ofdm_40_siso[0], WLC_NUM_RATES_OFDM);
+ &txpwr->ofdm_40_siso[0], BRCMS_NUM_RATES_OFDM);
memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_40_CDD],
- &txpwr->ofdm_40_cdd[0], WLC_NUM_RATES_OFDM);
+ &txpwr->ofdm_40_cdd[0], BRCMS_NUM_RATES_OFDM);
memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_SISO],
- &txpwr->mcs_20_siso[0], WLC_NUM_RATES_MCS_1_STREAM);
+ &txpwr->mcs_20_siso[0], BRCMS_NUM_RATES_MCS_1_STREAM);
memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_CDD],
- &txpwr->mcs_20_cdd[0], WLC_NUM_RATES_MCS_1_STREAM);
+ &txpwr->mcs_20_cdd[0], BRCMS_NUM_RATES_MCS_1_STREAM);
memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_STBC],
- &txpwr->mcs_20_stbc[0], WLC_NUM_RATES_MCS_1_STREAM);
+ &txpwr->mcs_20_stbc[0], BRCMS_NUM_RATES_MCS_1_STREAM);
memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_SDM],
- &txpwr->mcs_20_mimo[0], WLC_NUM_RATES_MCS_2_STREAM);
+ &txpwr->mcs_20_mimo[0], BRCMS_NUM_RATES_MCS_2_STREAM);
memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_SISO],
- &txpwr->mcs_40_siso[0], WLC_NUM_RATES_MCS_1_STREAM);
+ &txpwr->mcs_40_siso[0], BRCMS_NUM_RATES_MCS_1_STREAM);
memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_CDD],
- &txpwr->mcs_40_cdd[0], WLC_NUM_RATES_MCS_1_STREAM);
+ &txpwr->mcs_40_cdd[0], BRCMS_NUM_RATES_MCS_1_STREAM);
memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_STBC],
- &txpwr->mcs_40_stbc[0], WLC_NUM_RATES_MCS_1_STREAM);
+ &txpwr->mcs_40_stbc[0], BRCMS_NUM_RATES_MCS_1_STREAM);
memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_SDM],
- &txpwr->mcs_40_mimo[0], WLC_NUM_RATES_MCS_2_STREAM);
+ &txpwr->mcs_40_mimo[0], BRCMS_NUM_RATES_MCS_2_STREAM);
if (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC)
mac_enabled = true;
@@ -1583,13 +1398,13 @@ void wlc_phy_txpower_target_set(wlc_phy_t *ppi, struct txpwr_limits *txpwr)
wlapi_enable_mac(pi->sh->physhim);
}
-int wlc_phy_txpower_set(wlc_phy_t *ppi, uint qdbm, bool override)
+int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, bool override)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
int i;
if (qdbm > 127)
- return 5;
+ return -EINVAL;
for (i = 0; i < TXP_NUM_RATES; i++)
pi->tx_user_target[i] = (u8) qdbm;
@@ -1600,10 +1415,8 @@ int wlc_phy_txpower_set(wlc_phy_t *ppi, uint qdbm, bool override)
if (!SCAN_INPROG_PHY(pi)) {
bool suspend;
- suspend =
- (0 ==
- (R_REG(&pi->regs->maccontrol) &
- MCTL_EN_MAC));
+ suspend = (0 == (R_REG(&pi->regs->maccontrol) &
+ MCTL_EN_MAC));
if (!suspend)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
@@ -1619,13 +1432,13 @@ int wlc_phy_txpower_set(wlc_phy_t *ppi, uint qdbm, bool override)
}
void
-wlc_phy_txpower_sromlimit(wlc_phy_t *ppi, uint channel, u8 *min_pwr,
+wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint channel, u8 *min_pwr,
u8 *max_pwr, int txp_rate_idx)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
uint i;
- *min_pwr = pi->min_txpower * WLC_TXPWR_DB_FACTOR;
+ *min_pwr = pi->min_txpower * BRCMS_TXPWR_DB_FACTOR;
if (ISNPHY(pi)) {
if (txp_rate_idx < 0)
@@ -1639,15 +1452,14 @@ wlc_phy_txpower_sromlimit(wlc_phy_t *ppi, uint channel, u8 *min_pwr,
*max_pwr = pi->tx_srom_max_rate_2g[txp_rate_idx];
} else {
- *max_pwr = WLC_TXPWR_MAX;
+ *max_pwr = BRCMS_TXPWR_MAX;
if (txp_rate_idx < 0)
txp_rate_idx = TXP_FIRST_OFDM;
for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) {
- if (channel == chan_info_all[i].chan) {
+ if (channel == chan_info_all[i].chan)
break;
- }
}
if (pi->hwtxpwr) {
@@ -1669,10 +1481,10 @@ wlc_phy_txpower_sromlimit(wlc_phy_t *ppi, uint channel, u8 *min_pwr,
}
void
-wlc_phy_txpower_sromlimit_max_get(wlc_phy_t *ppi, uint chan, u8 *max_txpwr,
- u8 *min_txpwr)
+wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, uint chan,
+ u8 *max_txpwr, u8 *min_txpwr)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
u8 tx_pwr_max = 0;
u8 tx_pwr_min = 255;
u8 max_num_rate;
@@ -1681,7 +1493,8 @@ wlc_phy_txpower_sromlimit_max_get(wlc_phy_t *ppi, uint chan, u8 *max_txpwr,
pactrl = 0;
max_num_rate = ISNPHY(pi) ? TXP_NUM_RATES :
- ISLCNPHY(pi) ? (TXP_LAST_SISO_MCS_20 + 1) : (TXP_LAST_OFDM + 1);
+ ISLCNPHY(pi) ? (TXP_LAST_SISO_MCS_20 +
+ 1) : (TXP_LAST_OFDM + 1);
for (rate = 0; rate < max_num_rate; rate++) {
@@ -1700,27 +1513,67 @@ wlc_phy_txpower_sromlimit_max_get(wlc_phy_t *ppi, uint chan, u8 *max_txpwr,
}
void
-wlc_phy_txpower_boardlimit_band(wlc_phy_t *ppi, uint bandunit, s32 *max_pwr,
- s32 *min_pwr, u32 *step_pwr)
+wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, uint bandunit,
+ s32 *max_pwr, s32 *min_pwr, u32 *step_pwr)
{
return;
}
-u8 wlc_phy_txpower_get_target_min(wlc_phy_t *ppi)
+u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
return pi->tx_power_min;
}
-u8 wlc_phy_txpower_get_target_max(wlc_phy_t *ppi)
+u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
return pi->tx_power_max;
}
-void wlc_phy_txpower_recalc_target(phy_info_t *pi)
+static s8 wlc_phy_env_measure_vbat(struct brcms_phy *pi)
+{
+ if (ISLCNPHY(pi))
+ return wlc_lcnphy_vbatsense(pi, 0);
+ else
+ return 0;
+}
+
+static s8 wlc_phy_env_measure_temperature(struct brcms_phy *pi)
+{
+ if (ISLCNPHY(pi))
+ return wlc_lcnphy_tempsense_degree(pi, 0);
+ else
+ return 0;
+}
+
+static void wlc_phy_upd_env_txpwr_rate_limits(struct brcms_phy *pi, u32 band)
+{
+ u8 i;
+ s8 temp, vbat;
+
+ for (i = 0; i < TXP_NUM_RATES; i++)
+ pi->txpwr_env_limit[i] = BRCMS_TXPWR_MAX;
+
+ vbat = wlc_phy_env_measure_vbat(pi);
+ temp = wlc_phy_env_measure_temperature(pi);
+
+}
+
+static s8
+wlc_user_txpwr_antport_to_rfport(struct brcms_phy *pi, uint chan, u32 band,
+ u8 rate)
+{
+ s8 offset = 0;
+
+ if (!pi->user_txpwr_at_rfport)
+ return offset;
+ return offset;
+}
+
+void wlc_phy_txpower_recalc_target(struct brcms_phy *pi)
{
u8 maxtxpwr, mintxpwr, rate, pactrl;
uint target_chan;
@@ -1730,17 +1583,17 @@ void wlc_phy_txpower_recalc_target(phy_info_t *pi)
u8 tx_pwr_max_rate_ind = 0;
u8 max_num_rate;
u8 start_rate = 0;
- chanspec_t chspec;
- u32 band = CHSPEC2WLC_BAND(pi->radio_chanspec);
- initfn_t txpwr_recalc_fn = NULL;
+ u16 chspec;
+ u32 band = CHSPEC2BAND(pi->radio_chanspec);
+ void (*txpwr_recalc_fn)(struct brcms_phy *) = NULL;
chspec = pi->radio_chanspec;
if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE)
target_chan = CHSPEC_CHANNEL(chspec);
else if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER)
- target_chan = UPPER_20_SB(CHSPEC_CHANNEL(chspec));
+ target_chan = upper_20_sb(CHSPEC_CHANNEL(chspec));
else
- target_chan = LOWER_20_SB(CHSPEC_CHANNEL(chspec));
+ target_chan = lower_20_sb(CHSPEC_CHANNEL(chspec));
pactrl = 0;
if (ISLCNPHY(pi)) {
@@ -1751,8 +1604,8 @@ void wlc_phy_txpower_recalc_target(phy_info_t *pi)
for (i = TXP_FIRST_SISO_MCS_20;
i <= TXP_LAST_SISO_MCS_20; i++) {
pi->tx_srom_max_rate_2g[i - 8] =
- pi->tx_srom_max_2g -
- ((offset_mcs & 0xf) * 2);
+ pi->tx_srom_max_2g -
+ ((offset_mcs & 0xf) * 2);
offset_mcs >>= 4;
}
} else {
@@ -1760,19 +1613,16 @@ void wlc_phy_txpower_recalc_target(phy_info_t *pi)
for (i = TXP_FIRST_SISO_MCS_20;
i <= TXP_LAST_SISO_MCS_20; i++) {
pi->tx_srom_max_rate_2g[i - 8] =
- pi->tx_srom_max_2g -
- ((offset_mcs & 0xf) * 2);
+ pi->tx_srom_max_2g -
+ ((offset_mcs & 0xf) * 2);
offset_mcs >>= 4;
}
}
}
-#if WL11N
+
max_num_rate = ((ISNPHY(pi)) ? (TXP_NUM_RATES) :
((ISLCNPHY(pi)) ?
(TXP_LAST_SISO_MCS_20 + 1) : (TXP_LAST_OFDM + 1)));
-#else
- max_num_rate = ((ISNPHY(pi)) ? (TXP_NUM_RATES) : (TXP_LAST_OFDM + 1));
-#endif
wlc_phy_upd_env_txpwr_rate_limits(pi, band);
@@ -1780,34 +1630,32 @@ void wlc_phy_txpower_recalc_target(phy_info_t *pi)
tx_pwr_target[rate] = pi->tx_user_target[rate];
- if (pi->user_txpwr_at_rfport) {
+ if (pi->user_txpwr_at_rfport)
tx_pwr_target[rate] +=
- wlc_user_txpwr_antport_to_rfport(pi, target_chan,
- band, rate);
- }
+ wlc_user_txpwr_antport_to_rfport(pi,
+ target_chan,
+ band,
+ rate);
- {
+ wlc_phy_txpower_sromlimit((struct brcms_phy_pub *) pi,
+ target_chan,
+ &mintxpwr, &maxtxpwr, rate);
- wlc_phy_txpower_sromlimit((wlc_phy_t *) pi, target_chan,
- &mintxpwr, &maxtxpwr, rate);
+ maxtxpwr = min(maxtxpwr, pi->txpwr_limit[rate]);
- maxtxpwr = min(maxtxpwr, pi->txpwr_limit[rate]);
+ maxtxpwr = (maxtxpwr > pactrl) ? (maxtxpwr - pactrl) : 0;
- maxtxpwr =
- (maxtxpwr > pactrl) ? (maxtxpwr - pactrl) : 0;
+ maxtxpwr = (maxtxpwr > 6) ? (maxtxpwr - 6) : 0;
- maxtxpwr = (maxtxpwr > 6) ? (maxtxpwr - 6) : 0;
+ maxtxpwr = min(maxtxpwr, tx_pwr_target[rate]);
- maxtxpwr = min(maxtxpwr, tx_pwr_target[rate]);
+ if (pi->txpwr_percent <= 100)
+ maxtxpwr = (maxtxpwr * pi->txpwr_percent) / 100;
- if (pi->txpwr_percent <= 100)
- maxtxpwr = (maxtxpwr * pi->txpwr_percent) / 100;
-
- tx_pwr_target[rate] = max(maxtxpwr, mintxpwr);
- }
+ tx_pwr_target[rate] = max(maxtxpwr, mintxpwr);
tx_pwr_target[rate] =
- min(tx_pwr_target[rate], pi->txpwr_env_limit[rate]);
+ min(tx_pwr_target[rate], pi->txpwr_env_limit[rate]);
if (tx_pwr_target[rate] > tx_pwr_max)
tx_pwr_max_rate_ind = rate;
@@ -1824,25 +1672,24 @@ void wlc_phy_txpower_recalc_target(phy_info_t *pi)
pi->tx_power_target[rate] = tx_pwr_target[rate];
- if (!pi->hwpwrctrl || ISNPHY(pi)) {
+ if (!pi->hwpwrctrl || ISNPHY(pi))
pi->tx_power_offset[rate] =
- pi->tx_power_max - pi->tx_power_target[rate];
- } else {
+ pi->tx_power_max - pi->tx_power_target[rate];
+ else
pi->tx_power_offset[rate] =
- pi->tx_power_target[rate] - pi->tx_power_min;
- }
+ pi->tx_power_target[rate] - pi->tx_power_min;
}
txpwr_recalc_fn = pi->pi_fptr.txpwrrecalc;
if (txpwr_recalc_fn)
- (*txpwr_recalc_fn) (pi);
+ (*txpwr_recalc_fn)(pi);
}
-void
-wlc_phy_txpower_reg_limit_calc(phy_info_t *pi, struct txpwr_limits *txpwr,
- chanspec_t chanspec)
+static void
+wlc_phy_txpower_reg_limit_calc(struct brcms_phy *pi, struct txpwr_limits *txpwr,
+ u16 chanspec)
{
- u8 tmp_txpwr_limit[2 * WLC_NUM_RATES_OFDM];
+ u8 tmp_txpwr_limit[2 * BRCMS_NUM_RATES_OFDM];
u8 *txpwr_ptr1 = NULL, *txpwr_ptr2 = NULL;
int rate_start_index = 0, rate1, rate2, k;
@@ -1875,7 +1722,7 @@ wlc_phy_txpower_reg_limit_calc(phy_info_t *pi, struct txpwr_limits *txpwr,
txpwr_ptr1 = txpwr->mcs_40_siso;
txpwr_ptr2 = txpwr->ofdm_40_siso;
rate_start_index =
- WL_TX_POWER_OFDM40_SISO_FIRST;
+ WL_TX_POWER_OFDM40_SISO_FIRST;
break;
case 3:
@@ -1885,19 +1732,21 @@ wlc_phy_txpower_reg_limit_calc(phy_info_t *pi, struct txpwr_limits *txpwr,
break;
}
- for (rate2 = 0; rate2 < WLC_NUM_RATES_OFDM; rate2++) {
+ for (rate2 = 0; rate2 < BRCMS_NUM_RATES_OFDM;
+ rate2++) {
tmp_txpwr_limit[rate2] = 0;
- tmp_txpwr_limit[WLC_NUM_RATES_OFDM + rate2] =
- txpwr_ptr1[rate2];
+ tmp_txpwr_limit[BRCMS_NUM_RATES_OFDM + rate2] =
+ txpwr_ptr1[rate2];
}
- wlc_phy_mcs_to_ofdm_powers_nphy(tmp_txpwr_limit, 0,
- WLC_NUM_RATES_OFDM - 1,
- WLC_NUM_RATES_OFDM);
+ wlc_phy_mcs_to_ofdm_powers_nphy(
+ tmp_txpwr_limit, 0,
+ BRCMS_NUM_RATES_OFDM -
+ 1, BRCMS_NUM_RATES_OFDM);
for (rate1 = rate_start_index, rate2 = 0;
- rate2 < WLC_NUM_RATES_OFDM; rate1++, rate2++)
+ rate2 < BRCMS_NUM_RATES_OFDM; rate1++, rate2++)
pi->txpwr_limit[rate1] =
- min(txpwr_ptr2[rate2],
- tmp_txpwr_limit[rate2]);
+ min(txpwr_ptr2[rate2],
+ tmp_txpwr_limit[rate2]);
}
for (k = 0; k < 4; k++) {
@@ -1927,20 +1776,22 @@ wlc_phy_txpower_reg_limit_calc(phy_info_t *pi, struct txpwr_limits *txpwr,
rate_start_index = WL_TX_POWER_MCS40_CDD_FIRST;
break;
}
- for (rate2 = 0; rate2 < WLC_NUM_RATES_OFDM; rate2++) {
+ for (rate2 = 0; rate2 < BRCMS_NUM_RATES_OFDM;
+ rate2++) {
tmp_txpwr_limit[rate2] = 0;
- tmp_txpwr_limit[WLC_NUM_RATES_OFDM + rate2] =
- txpwr_ptr1[rate2];
+ tmp_txpwr_limit[BRCMS_NUM_RATES_OFDM + rate2] =
+ txpwr_ptr1[rate2];
}
- wlc_phy_ofdm_to_mcs_powers_nphy(tmp_txpwr_limit, 0,
- WLC_NUM_RATES_OFDM - 1,
- WLC_NUM_RATES_OFDM);
+ wlc_phy_ofdm_to_mcs_powers_nphy(
+ tmp_txpwr_limit, 0,
+ BRCMS_NUM_RATES_OFDM -
+ 1, BRCMS_NUM_RATES_OFDM);
for (rate1 = rate_start_index, rate2 = 0;
- rate2 < WLC_NUM_RATES_MCS_1_STREAM;
+ rate2 < BRCMS_NUM_RATES_MCS_1_STREAM;
rate1++, rate2++)
pi->txpwr_limit[rate1] =
- min(txpwr_ptr2[rate2],
- tmp_txpwr_limit[rate2]);
+ min(txpwr_ptr2[rate2],
+ tmp_txpwr_limit[rate2]);
}
for (k = 0; k < 2; k++) {
@@ -1957,7 +1808,7 @@ wlc_phy_txpower_reg_limit_calc(phy_info_t *pi, struct txpwr_limits *txpwr,
break;
}
for (rate1 = rate_start_index, rate2 = 0;
- rate2 < WLC_NUM_RATES_MCS_1_STREAM;
+ rate2 < BRCMS_NUM_RATES_MCS_1_STREAM;
rate1++, rate2++)
pi->txpwr_limit[rate1] = txpwr_ptr1[rate2];
}
@@ -1976,7 +1827,7 @@ wlc_phy_txpower_reg_limit_calc(phy_info_t *pi, struct txpwr_limits *txpwr,
break;
}
for (rate1 = rate_start_index, rate2 = 0;
- rate2 < WLC_NUM_RATES_MCS_2_STREAM;
+ rate2 < BRCMS_NUM_RATES_MCS_2_STREAM;
rate1++, rate2++)
pi->txpwr_limit[rate1] = txpwr_ptr1[rate2];
}
@@ -1984,30 +1835,30 @@ wlc_phy_txpower_reg_limit_calc(phy_info_t *pi, struct txpwr_limits *txpwr,
pi->txpwr_limit[WL_TX_POWER_MCS_32] = txpwr->mcs32;
pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST] =
- min(pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST],
- pi->txpwr_limit[WL_TX_POWER_MCS_32]);
+ min(pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST],
+ pi->txpwr_limit[WL_TX_POWER_MCS_32]);
pi->txpwr_limit[WL_TX_POWER_MCS_32] =
- pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST];
+ pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST];
}
}
-void wlc_phy_txpwr_percent_set(wlc_phy_t *ppi, u8 txpwr_percent)
+void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, u8 txpwr_percent)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
pi->txpwr_percent = txpwr_percent;
}
-void wlc_phy_machwcap_set(wlc_phy_t *ppi, u32 machwcap)
+void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
pi->sh->machwcap = machwcap;
}
-void wlc_phy_runbist_config(wlc_phy_t *ppi, bool start_end)
+void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
u16 rxc;
rxc = 0;
@@ -2036,17 +1887,17 @@ void wlc_phy_runbist_config(wlc_phy_t *ppi, bool start_end)
}
void
-wlc_phy_txpower_limit_set(wlc_phy_t *ppi, struct txpwr_limits *txpwr,
- chanspec_t chanspec)
+wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, struct txpwr_limits *txpwr,
+ u16 chanspec)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
wlc_phy_txpower_reg_limit_calc(pi, txpwr, chanspec);
if (ISLCNPHY(pi)) {
int i, j;
for (i = TXP_FIRST_OFDM_20_CDD, j = 0;
- j < WLC_NUM_RATES_MCS_1_STREAM; i++, j++) {
+ j < BRCMS_NUM_RATES_MCS_1_STREAM; i++, j++) {
if (txpwr->mcs_20_siso[j])
pi->txpwr_limit[i] = txpwr->mcs_20_siso[j];
else
@@ -2061,26 +1912,25 @@ wlc_phy_txpower_limit_set(wlc_phy_t *ppi, struct txpwr_limits *txpwr,
wlapi_enable_mac(pi->sh->physhim);
}
-void wlc_phy_ofdm_rateset_war(wlc_phy_t *pih, bool war)
+void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
pi->ofdm_rateset_war = war;
}
-void wlc_phy_bf_preempt_enable(wlc_phy_t *pih, bool bf_preempt)
+void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, bool bf_preempt)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
pi->bf_preempt_4306 = bf_preempt;
}
-void wlc_phy_txpower_update_shm(phy_info_t *pi)
+void wlc_phy_txpower_update_shm(struct brcms_phy *pi)
{
int j;
- if (ISNPHY(pi)) {
+ if (ISNPHY(pi))
return;
- }
if (!pi->sh->clk)
return;
@@ -2102,9 +1952,9 @@ void wlc_phy_txpower_update_shm(phy_info_t *pi)
const u8 ucode_ofdm_rates[] = {
0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c
};
- offset = wlapi_bmac_rate_shm_offset(pi->sh->physhim,
- ucode_ofdm_rates[j -
- TXP_FIRST_OFDM]);
+ offset = wlapi_bmac_rate_shm_offset(
+ pi->sh->physhim,
+ ucode_ofdm_rates[j - TXP_FIRST_OFDM]);
wlapi_bmac_write_shm(pi->sh->physhim, offset + 6,
pi->tx_power_offset[j]);
wlapi_bmac_write_shm(pi->sh->physhim, offset + 14,
@@ -2112,70 +1962,60 @@ void wlc_phy_txpower_update_shm(phy_info_t *pi)
}
wlapi_bmac_mhf(pi->sh->physhim, MHF2, MHF2_HWPWRCTL,
- MHF2_HWPWRCTL, WLC_BAND_ALL);
+ MHF2_HWPWRCTL, BRCM_BAND_ALL);
} else {
int i;
for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++)
pi->tx_power_offset[i] =
- (u8) roundup(pi->tx_power_offset[i], 8);
+ (u8) roundup(pi->tx_power_offset[i], 8);
wlapi_bmac_write_shm(pi->sh->physhim, M_OFDM_OFFSET,
- (u16) ((pi->
- tx_power_offset[TXP_FIRST_OFDM]
- + 7) >> 3));
+ (u16)
+ ((pi->tx_power_offset[TXP_FIRST_OFDM]
+ + 7) >> 3));
}
}
-bool wlc_phy_txpower_hw_ctrl_get(wlc_phy_t *ppi)
+bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
- if (ISNPHY(pi)) {
+ if (ISNPHY(pi))
return pi->nphy_txpwrctrl;
- } else {
+ else
return pi->hwpwrctrl;
- }
}
-void wlc_phy_txpower_hw_ctrl_set(wlc_phy_t *ppi, bool hwpwrctrl)
+void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, bool hwpwrctrl)
{
- phy_info_t *pi = (phy_info_t *) ppi;
- bool cur_hwpwrctrl = pi->hwpwrctrl;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
bool suspend;
- if (!pi->hwpwrctrl_capable) {
+ if (!pi->hwpwrctrl_capable)
return;
- }
pi->hwpwrctrl = hwpwrctrl;
pi->nphy_txpwrctrl = hwpwrctrl;
pi->txpwrctrl = hwpwrctrl;
if (ISNPHY(pi)) {
- suspend =
- (0 ==
- (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ suspend = (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
if (!suspend)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
wlc_phy_txpwrctrl_enable_nphy(pi, pi->nphy_txpwrctrl);
- if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) {
+ if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF)
wlc_phy_txpwr_fixpower_nphy(pi);
- } else {
-
+ else
mod_phy_reg(pi, 0x1e7, (0x7f << 0),
pi->saved_txpwr_idx);
- }
if (!suspend)
wlapi_enable_mac(pi->sh->physhim);
- } else if (hwpwrctrl != cur_hwpwrctrl) {
-
- return;
}
}
-void wlc_phy_txpower_ipa_upd(phy_info_t *pi)
+void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi)
{
if (NREV_GE(pi->pubpi.phy_rev, 3)) {
@@ -2187,9 +2027,7 @@ void wlc_phy_txpower_ipa_upd(phy_info_t *pi)
}
}
-static u32 wlc_phy_txpower_est_power_nphy(phy_info_t *pi);
-
-static u32 wlc_phy_txpower_est_power_nphy(phy_info_t *pi)
+static u32 wlc_phy_txpower_est_power_nphy(struct brcms_phy *pi)
{
s16 tx0_status, tx1_status;
u16 estPower1, estPower2;
@@ -2199,54 +2037,44 @@ static u32 wlc_phy_txpower_est_power_nphy(phy_info_t *pi)
estPower1 = read_phy_reg(pi, 0x118);
estPower2 = read_phy_reg(pi, 0x119);
- if ((estPower1 & (0x1 << 8))
- == (0x1 << 8)) {
- pwr0 = (u8) (estPower1 & (0xff << 0))
- >> 0;
- } else {
+ if ((estPower1 & (0x1 << 8)) == (0x1 << 8))
+ pwr0 = (u8) (estPower1 & (0xff << 0)) >> 0;
+ else
pwr0 = 0x80;
- }
- if ((estPower2 & (0x1 << 8))
- == (0x1 << 8)) {
- pwr1 = (u8) (estPower2 & (0xff << 0))
- >> 0;
- } else {
+ if ((estPower2 & (0x1 << 8)) == (0x1 << 8))
+ pwr1 = (u8) (estPower2 & (0xff << 0)) >> 0;
+ else
pwr1 = 0x80;
- }
tx0_status = read_phy_reg(pi, 0x1ed);
tx1_status = read_phy_reg(pi, 0x1ee);
- if ((tx0_status & (0x1 << 15))
- == (0x1 << 15)) {
- adj_pwr0 = (u8) (tx0_status & (0xff << 0))
- >> 0;
- } else {
+ if ((tx0_status & (0x1 << 15)) == (0x1 << 15))
+ adj_pwr0 = (u8) (tx0_status & (0xff << 0)) >> 0;
+ else
adj_pwr0 = 0x80;
- }
- if ((tx1_status & (0x1 << 15))
- == (0x1 << 15)) {
- adj_pwr1 = (u8) (tx1_status & (0xff << 0))
- >> 0;
- } else {
+ if ((tx1_status & (0x1 << 15)) == (0x1 << 15))
+ adj_pwr1 = (u8) (tx1_status & (0xff << 0)) >> 0;
+ else
adj_pwr1 = 0x80;
- }
- est_pwr =
- (u32) ((pwr0 << 24) | (pwr1 << 16) | (adj_pwr0 << 8) | adj_pwr1);
+ est_pwr = (u32) ((pwr0 << 24) | (pwr1 << 16) | (adj_pwr0 << 8) |
+ adj_pwr1);
+
return est_pwr;
}
void
-wlc_phy_txpower_get_current(wlc_phy_t *ppi, tx_power_t *power, uint channel)
+wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, struct tx_power *power,
+ uint channel)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
uint rate, num_rates;
u8 min_pwr, max_pwr;
#if WL_TX_POWER_RATES != TXP_NUM_RATES
-#error "tx_power_t struct out of sync with this fn"
+#error "struct tx_power out of sync with this fn"
#endif
if (ISNPHY(pi)) {
@@ -2254,7 +2082,7 @@ wlc_phy_txpower_get_current(wlc_phy_t *ppi, tx_power_t *power, uint channel)
power->flags |= (WL_TX_POWER_F_MIMO);
if (pi->nphy_txpwrctrl == PHY_TPC_HW_ON)
power->flags |=
- (WL_TX_POWER_F_ENABLED | WL_TX_POWER_F_HW);
+ (WL_TX_POWER_F_ENABLED | WL_TX_POWER_F_HW);
} else if (ISLCNPHY(pi)) {
power->rf_cores = 1;
power->flags |= (WL_TX_POWER_F_SISO);
@@ -2280,9 +2108,9 @@ wlc_phy_txpower_get_current(wlc_phy_t *ppi, tx_power_t *power, uint channel)
u32 est_pout;
wlapi_suspend_mac_and_wait(pi->sh->physhim);
- wlc_phyreg_enter((wlc_phy_t *) pi);
+ wlc_phyreg_enter((struct brcms_phy_pub *) pi);
est_pout = wlc_phy_txpower_est_power_nphy(pi);
- wlc_phyreg_exit((wlc_phy_t *) pi);
+ wlc_phyreg_exit((struct brcms_phy_pub *) pi);
wlapi_enable_mac(pi->sh->physhim);
power->est_Pout[0] = (est_pout >> 8) & 0xff;
@@ -2308,8 +2136,7 @@ wlc_phy_txpower_get_current(wlc_phy_t *ppi, tx_power_t *power, uint channel)
power->tx_power_max_rate_ind[0] = pi->tx_power_max_rate_ind;
power->tx_power_max_rate_ind[1] = pi->tx_power_max_rate_ind;
- } else if (!pi->hwpwrctrl) {
- } else if (pi->sh->up) {
+ } else if (pi->hwpwrctrl && pi->sh->up) {
wlc_phyreg_enter(ppi);
if (ISLCNPHY(pi)) {
@@ -2318,16 +2145,18 @@ wlc_phy_txpower_get_current(wlc_phy_t *ppi, tx_power_t *power, uint channel)
power->tx_power_max[1] = pi->tx_power_max;
power->tx_power_max_rate_ind[0] =
- pi->tx_power_max_rate_ind;
+ pi->tx_power_max_rate_ind;
power->tx_power_max_rate_ind[1] =
- pi->tx_power_max_rate_ind;
+ pi->tx_power_max_rate_ind;
if (wlc_phy_tpc_isenabled_lcnphy(pi))
power->flags |=
- (WL_TX_POWER_F_HW | WL_TX_POWER_F_ENABLED);
+ (WL_TX_POWER_F_HW |
+ WL_TX_POWER_F_ENABLED);
else
power->flags &=
- ~(WL_TX_POWER_F_HW | WL_TX_POWER_F_ENABLED);
+ ~(WL_TX_POWER_F_HW |
+ WL_TX_POWER_F_ENABLED);
wlc_lcnphy_get_tssi(pi, (s8 *) &power->est_Pout[0],
(s8 *) &power->est_Pout_cck);
@@ -2336,44 +2165,23 @@ wlc_phy_txpower_get_current(wlc_phy_t *ppi, tx_power_t *power, uint channel)
}
}
-void wlc_phy_antsel_type_set(wlc_phy_t *ppi, u8 antsel_type)
+void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
pi->antsel_type = antsel_type;
}
-bool wlc_phy_test_ison(wlc_phy_t *ppi)
+bool wlc_phy_test_ison(struct brcms_phy_pub *ppi)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
return pi->phytest_on;
}
-bool wlc_phy_ant_rxdiv_get(wlc_phy_t *ppi, u8 *pval)
-{
- phy_info_t *pi = (phy_info_t *) ppi;
- bool ret = true;
-
- wlc_phyreg_enter(ppi);
-
- if (ISNPHY(pi)) {
-
- ret = false;
- } else if (ISLCNPHY(pi)) {
- u16 crsctrl = read_phy_reg(pi, 0x410);
- u16 div = crsctrl & (0x1 << 1);
- *pval = (div | ((crsctrl & (0x1 << 0)) ^ (div >> 1)));
- }
-
- wlc_phyreg_exit(ppi);
-
- return ret;
-}
-
-void wlc_phy_ant_rxdiv_set(wlc_phy_t *ppi, u8 val)
+void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
bool suspend;
pi->sh->rx_antdiv = val;
@@ -2381,22 +2189,19 @@ void wlc_phy_ant_rxdiv_set(wlc_phy_t *ppi, u8 val)
if (!(ISNPHY(pi) && D11REV_IS(pi->sh->corerev, 16))) {
if (val > ANT_RX_DIV_FORCE_1)
wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_ANTDIV,
- MHF1_ANTDIV, WLC_BAND_ALL);
+ MHF1_ANTDIV, BRCM_BAND_ALL);
else
wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_ANTDIV, 0,
- WLC_BAND_ALL);
+ BRCM_BAND_ALL);
}
- if (ISNPHY(pi)) {
-
+ if (ISNPHY(pi))
return;
- }
if (!pi->sh->clk)
return;
- suspend =
- (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ suspend = (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
if (!suspend)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
@@ -2419,7 +2224,7 @@ void wlc_phy_ant_rxdiv_set(wlc_phy_t *ppi, u8 val)
}
static bool
-wlc_phy_noise_calc_phy(phy_info_t *pi, u32 *cmplx_pwr, s8 *pwr_ant)
+wlc_phy_noise_calc_phy(struct brcms_phy *pi, u32 *cmplx_pwr, s8 *pwr_ant)
{
s8 cmplx_pwr_dbm[PHY_CORE_MAX];
u8 i;
@@ -2440,32 +2245,142 @@ wlc_phy_noise_calc_phy(phy_info_t *pi, u32 *cmplx_pwr, s8 *pwr_ant)
pwr_ant[i] = cmplx_pwr_dbm[i];
}
pi->nphy_noise_index =
- MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ);
+ MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ);
return true;
}
+static void wlc_phy_noise_cb(struct brcms_phy *pi, u8 channel, s8 noise_dbm)
+{
+ if (!pi->phynoise_state)
+ return;
+
+ if (pi->phynoise_state & PHY_NOISE_STATE_MON) {
+ if (pi->phynoise_chan_watchdog == channel) {
+ pi->sh->phy_noise_window[pi->sh->phy_noise_index] =
+ noise_dbm;
+ pi->sh->phy_noise_index =
+ MODINC(pi->sh->phy_noise_index, MA_WINDOW_SZ);
+ }
+ pi->phynoise_state &= ~PHY_NOISE_STATE_MON;
+ }
+
+ if (pi->phynoise_state & PHY_NOISE_STATE_EXTERNAL)
+ pi->phynoise_state &= ~PHY_NOISE_STATE_EXTERNAL;
+
+}
+
+static s8 wlc_phy_noise_read_shmem(struct brcms_phy *pi)
+{
+ u32 cmplx_pwr[PHY_CORE_MAX];
+ s8 noise_dbm_ant[PHY_CORE_MAX];
+ u16 lo, hi;
+ u32 cmplx_pwr_tot = 0;
+ s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY;
+ u8 idx, core;
+
+ memset((u8 *) cmplx_pwr, 0, sizeof(cmplx_pwr));
+ memset((u8 *) noise_dbm_ant, 0, sizeof(noise_dbm_ant));
+
+ for (idx = 0, core = 0; core < pi->pubpi.phy_corenum; idx += 2,
+ core++) {
+ lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP(idx));
+ hi = wlapi_bmac_read_shm(pi->sh->physhim,
+ M_PWRIND_MAP(idx + 1));
+ cmplx_pwr[core] = (hi << 16) + lo;
+ cmplx_pwr_tot += cmplx_pwr[core];
+ if (cmplx_pwr[core] == 0)
+ noise_dbm_ant[core] = PHY_NOISE_FIXED_VAL_NPHY;
+ else
+ cmplx_pwr[core] >>= PHY_NOISE_SAMPLE_LOG_NUM_UCODE;
+ }
+
+ if (cmplx_pwr_tot != 0)
+ wlc_phy_noise_calc_phy(pi, cmplx_pwr, noise_dbm_ant);
+
+ for (core = 0; core < pi->pubpi.phy_corenum; core++) {
+ pi->nphy_noise_win[core][pi->nphy_noise_index] =
+ noise_dbm_ant[core];
+
+ if (noise_dbm_ant[core] > noise_dbm)
+ noise_dbm = noise_dbm_ant[core];
+ }
+ pi->nphy_noise_index =
+ MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ);
+
+ return noise_dbm;
+
+}
+
+void wlc_phy_noise_sample_intr(struct brcms_phy_pub *pih)
+{
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
+ u16 jssi_aux;
+ u8 channel = 0;
+ s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY;
+
+ if (ISLCNPHY(pi)) {
+ u32 cmplx_pwr, cmplx_pwr0, cmplx_pwr1;
+ u16 lo, hi;
+ s32 pwr_offset_dB, gain_dB;
+ u16 status_0, status_1;
+
+ jssi_aux = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_AUX);
+ channel = jssi_aux & D11_CURCHANNEL_MAX;
+
+ lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP0);
+ hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP1);
+ cmplx_pwr0 = (hi << 16) + lo;
+
+ lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP2);
+ hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP3);
+ cmplx_pwr1 = (hi << 16) + lo;
+ cmplx_pwr = (cmplx_pwr0 + cmplx_pwr1) >> 6;
+
+ status_0 = 0x44;
+ status_1 = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_0);
+ if ((cmplx_pwr > 0 && cmplx_pwr < 500)
+ && ((status_1 & 0xc000) == 0x4000)) {
+
+ wlc_phy_compute_dB(&cmplx_pwr, &noise_dbm,
+ pi->pubpi.phy_corenum);
+ pwr_offset_dB = (read_phy_reg(pi, 0x434) & 0xFF);
+ if (pwr_offset_dB > 127)
+ pwr_offset_dB -= 256;
+
+ noise_dbm += (s8) (pwr_offset_dB - 30);
+
+ gain_dB = (status_0 & 0x1ff);
+ noise_dbm -= (s8) (gain_dB);
+ } else {
+ noise_dbm = PHY_NOISE_FIXED_VAL_LCNPHY;
+ }
+ } else if (ISNPHY(pi)) {
+
+ jssi_aux = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_AUX);
+ channel = jssi_aux & D11_CURCHANNEL_MAX;
+
+ noise_dbm = wlc_phy_noise_read_shmem(pi);
+ }
+
+ wlc_phy_noise_cb(pi, channel, noise_dbm);
+
+}
+
static void
-wlc_phy_noise_sample_request(wlc_phy_t *pih, u8 reason, u8 ch)
+wlc_phy_noise_sample_request(struct brcms_phy_pub *pih, u8 reason, u8 ch)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY;
bool sampling_in_progress = (pi->phynoise_state != 0);
bool wait_for_intr = true;
- if (NORADIO_ENAB(pi->pubpi)) {
- return;
- }
-
switch (reason) {
case PHY_NOISE_SAMPLE_MON:
-
pi->phynoise_chan_watchdog = ch;
pi->phynoise_state |= PHY_NOISE_STATE_MON;
-
break;
case PHY_NOISE_SAMPLE_EXTERNAL:
-
pi->phynoise_state |= PHY_NOISE_STATE_EXTERNAL;
break;
@@ -2481,15 +2396,13 @@ wlc_phy_noise_sample_request(wlc_phy_t *pih, u8 reason, u8 ch)
if (pi->phy_fixed_noise) {
if (ISNPHY(pi)) {
pi->nphy_noise_win[WL_ANT_IDX_1][pi->nphy_noise_index] =
- PHY_NOISE_FIXED_VAL_NPHY;
+ PHY_NOISE_FIXED_VAL_NPHY;
pi->nphy_noise_win[WL_ANT_IDX_2][pi->nphy_noise_index] =
- PHY_NOISE_FIXED_VAL_NPHY;
+ PHY_NOISE_FIXED_VAL_NPHY;
pi->nphy_noise_index = MODINC_POW2(pi->nphy_noise_index,
PHY_NOISE_WINDOW_SZ);
-
noise_dbm = PHY_NOISE_FIXED_VAL_NPHY;
} else {
-
noise_dbm = PHY_NOISE_FIXED_VAL;
}
@@ -2528,7 +2441,7 @@ wlc_phy_noise_sample_request(wlc_phy_t *pih, u8 reason, u8 ch)
OR_REG(&pi->regs->maccommand,
MCMD_BG_NOISE);
} else {
- phy_iq_est_t est[PHY_CORE_MAX];
+ struct phy_iq_est est[PHY_CORE_MAX];
u32 cmplx_pwr[PHY_CORE_MAX];
s8 noise_dbm_ant[PHY_CORE_MAX];
u16 log_num_samps, num_samps, classif_state = 0;
@@ -2552,15 +2465,14 @@ wlc_phy_noise_sample_request(wlc_phy_t *pih, u8 reason, u8 ch)
wlapi_enable_mac(pi->sh->physhim);
for (i = 0; i < pi->pubpi.phy_corenum; i++)
- cmplx_pwr[i] =
- (est[i].i_pwr +
- est[i].q_pwr) >> log_num_samps;
+ cmplx_pwr[i] = (est[i].i_pwr + est[i].q_pwr) >>
+ log_num_samps;
wlc_phy_noise_calc_phy(pi, cmplx_pwr, noise_dbm_ant);
for (i = 0; i < pi->pubpi.phy_corenum; i++) {
pi->nphy_noise_win[i][pi->nphy_noise_index] =
- noise_dbm_ant[i];
+ noise_dbm_ant[i];
if (noise_dbm_ant[i] > noise_dbm)
noise_dbm = noise_dbm_ant[i];
@@ -2572,14 +2484,14 @@ wlc_phy_noise_sample_request(wlc_phy_t *pih, u8 reason, u8 ch)
}
}
- done:
+done:
if (!wait_for_intr)
wlc_phy_noise_cb(pi, ch, noise_dbm);
}
-void wlc_phy_noise_sample_request_external(wlc_phy_t *pih)
+void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *pih)
{
u8 channel;
@@ -2588,124 +2500,7 @@ void wlc_phy_noise_sample_request_external(wlc_phy_t *pih)
wlc_phy_noise_sample_request(pih, PHY_NOISE_SAMPLE_EXTERNAL, channel);
}
-static void wlc_phy_noise_cb(phy_info_t *pi, u8 channel, s8 noise_dbm)
-{
- if (!pi->phynoise_state)
- return;
-
- if (pi->phynoise_state & PHY_NOISE_STATE_MON) {
- if (pi->phynoise_chan_watchdog == channel) {
- pi->sh->phy_noise_window[pi->sh->phy_noise_index] =
- noise_dbm;
- pi->sh->phy_noise_index =
- MODINC(pi->sh->phy_noise_index, MA_WINDOW_SZ);
- }
- pi->phynoise_state &= ~PHY_NOISE_STATE_MON;
- }
-
- if (pi->phynoise_state & PHY_NOISE_STATE_EXTERNAL) {
- pi->phynoise_state &= ~PHY_NOISE_STATE_EXTERNAL;
- }
-
-}
-
-static s8 wlc_phy_noise_read_shmem(phy_info_t *pi)
-{
- u32 cmplx_pwr[PHY_CORE_MAX];
- s8 noise_dbm_ant[PHY_CORE_MAX];
- u16 lo, hi;
- u32 cmplx_pwr_tot = 0;
- s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY;
- u8 idx, core;
-
- memset((u8 *) cmplx_pwr, 0, sizeof(cmplx_pwr));
- memset((u8 *) noise_dbm_ant, 0, sizeof(noise_dbm_ant));
-
- for (idx = 0, core = 0; core < pi->pubpi.phy_corenum; idx += 2, core++) {
- lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP(idx));
- hi = wlapi_bmac_read_shm(pi->sh->physhim,
- M_PWRIND_MAP(idx + 1));
- cmplx_pwr[core] = (hi << 16) + lo;
- cmplx_pwr_tot += cmplx_pwr[core];
- if (cmplx_pwr[core] == 0) {
- noise_dbm_ant[core] = PHY_NOISE_FIXED_VAL_NPHY;
- } else
- cmplx_pwr[core] >>= PHY_NOISE_SAMPLE_LOG_NUM_UCODE;
- }
-
- if (cmplx_pwr_tot != 0)
- wlc_phy_noise_calc_phy(pi, cmplx_pwr, noise_dbm_ant);
-
- for (core = 0; core < pi->pubpi.phy_corenum; core++) {
- pi->nphy_noise_win[core][pi->nphy_noise_index] =
- noise_dbm_ant[core];
-
- if (noise_dbm_ant[core] > noise_dbm)
- noise_dbm = noise_dbm_ant[core];
- }
- pi->nphy_noise_index =
- MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ);
-
- return noise_dbm;
-
-}
-
-void wlc_phy_noise_sample_intr(wlc_phy_t *pih)
-{
- phy_info_t *pi = (phy_info_t *) pih;
- u16 jssi_aux;
- u8 channel = 0;
- s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY;
-
- if (ISLCNPHY(pi)) {
- u32 cmplx_pwr, cmplx_pwr0, cmplx_pwr1;
- u16 lo, hi;
- s32 pwr_offset_dB, gain_dB;
- u16 status_0, status_1;
-
- jssi_aux = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_AUX);
- channel = jssi_aux & D11_CURCHANNEL_MAX;
-
- lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP0);
- hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP1);
- cmplx_pwr0 = (hi << 16) + lo;
-
- lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP2);
- hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP3);
- cmplx_pwr1 = (hi << 16) + lo;
- cmplx_pwr = (cmplx_pwr0 + cmplx_pwr1) >> 6;
-
- status_0 = 0x44;
- status_1 = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_0);
- if ((cmplx_pwr > 0 && cmplx_pwr < 500)
- && ((status_1 & 0xc000) == 0x4000)) {
-
- wlc_phy_compute_dB(&cmplx_pwr, &noise_dbm,
- pi->pubpi.phy_corenum);
- pwr_offset_dB = (read_phy_reg(pi, 0x434) & 0xFF);
- if (pwr_offset_dB > 127)
- pwr_offset_dB -= 256;
-
- noise_dbm += (s8) (pwr_offset_dB - 30);
-
- gain_dB = (status_0 & 0x1ff);
- noise_dbm -= (s8) (gain_dB);
- } else {
- noise_dbm = PHY_NOISE_FIXED_VAL_LCNPHY;
- }
- } else if (ISNPHY(pi)) {
-
- jssi_aux = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_AUX);
- channel = jssi_aux & D11_CURCHANNEL_MAX;
-
- noise_dbm = wlc_phy_noise_read_shmem(pi);
- }
-
- wlc_phy_noise_cb(pi, channel, noise_dbm);
-
-}
-
-s8 lcnphy_gain_index_offset_for_pkt_rssi[] = {
+static const s8 lcnphy_gain_index_offset_for_pkt_rssi[] = {
8,
8,
8,
@@ -2761,28 +2556,22 @@ void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_cmplx_pwr_dB, u8 core)
}
}
-void wlc_phy_rssi_compute(wlc_phy_t *pih, void *ctx)
+int wlc_phy_rssi_compute(struct brcms_phy_pub *pih,
+ struct d11rxhdr *rxh)
{
- wlc_d11rxhdr_t *wlc_rxhdr = (wlc_d11rxhdr_t *) ctx;
- d11rxhdr_t *rxh = &wlc_rxhdr->rxhdr;
- int rssi = le16_to_cpu(rxh->PhyRxStatus_1) & PRXS1_JSSI_MASK;
+ int rssi = rxh->PhyRxStatus_1 & PRXS1_JSSI_MASK;
uint radioid = pih->radioid;
- phy_info_t *pi = (phy_info_t *) pih;
-
- if (NORADIO_ENAB(pi->pubpi)) {
- rssi = WLC_RSSI_INVALID;
- goto end;
- }
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
if ((pi->sh->corerev >= 11)
- && !(le16_to_cpu(rxh->RxStatus2) & RXS_PHYRXST_VALID)) {
- rssi = WLC_RSSI_INVALID;
+ && !(rxh->RxStatus2 & RXS_PHYRXST_VALID)) {
+ rssi = BRCMS_RSSI_INVALID;
goto end;
}
if (ISLCNPHY(pi)) {
- u8 gidx = (le16_to_cpu(rxh->PhyRxStatus_2) & 0xFC00) >> 10;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ u8 gidx = (rxh->PhyRxStatus_2 & 0xFC00) >> 10;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
if (rssi > 127)
rssi -= 256;
@@ -2798,32 +2587,31 @@ void wlc_phy_rssi_compute(wlc_phy_t *pih, void *ctx)
}
if (ISLCNPHY(pi)) {
-
if (rssi > 127)
rssi -= 256;
} else if (radioid == BCM2055_ID || radioid == BCM2056_ID
|| radioid == BCM2057_ID) {
- rssi = wlc_phy_rssi_compute_nphy(pi, wlc_rxhdr);
+ rssi = wlc_phy_rssi_compute_nphy(pi, rxh);
}
- end:
- wlc_rxhdr->rssi = (s8) rssi;
+end:
+ return rssi;
}
-void wlc_phy_freqtrack_start(wlc_phy_t *pih)
+void wlc_phy_freqtrack_start(struct brcms_phy_pub *pih)
{
return;
}
-void wlc_phy_freqtrack_end(wlc_phy_t *pih)
+void wlc_phy_freqtrack_end(struct brcms_phy_pub *pih)
{
return;
}
-void wlc_phy_set_deaf(wlc_phy_t *ppi, bool user_flag)
+void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag)
{
- phy_info_t *pi;
- pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi;
+ pi = (struct brcms_phy *) ppi;
if (ISLCNPHY(pi))
wlc_lcnphy_deaf_mode(pi, true);
@@ -2831,37 +2619,31 @@ void wlc_phy_set_deaf(wlc_phy_t *ppi, bool user_flag)
wlc_nphy_deaf_mode(pi, true);
}
-void wlc_phy_watchdog(wlc_phy_t *pih)
+void wlc_phy_watchdog(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
bool delay_phy_cal = false;
pi->sh->now++;
if (!pi->watchdog_override)
return;
- if (!(SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi))) {
- wlc_phy_noise_sample_request((wlc_phy_t *) pi,
+ if (!(SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi)))
+ wlc_phy_noise_sample_request((struct brcms_phy_pub *) pi,
PHY_NOISE_SAMPLE_MON,
CHSPEC_CHANNEL(pi->
radio_chanspec));
- }
- if (pi->phynoise_state && (pi->sh->now - pi->phynoise_now) > 5) {
+ if (pi->phynoise_state && (pi->sh->now - pi->phynoise_now) > 5)
pi->phynoise_state = 0;
- }
if ((!pi->phycal_txpower) ||
((pi->sh->now - pi->phycal_txpower) >= pi->sh->fast_timer)) {
- if (!SCAN_INPROG_PHY(pi) && wlc_phy_cal_txpower_recalc_sw(pi)) {
+ if (!SCAN_INPROG_PHY(pi) && wlc_phy_cal_txpower_recalc_sw(pi))
pi->phycal_txpower = pi->sh->now;
- }
}
- if (NORADIO_ENAB(pi->pubpi))
- return;
-
if ((SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi)
|| ASSOC_INPROG_PHY(pi)))
return;
@@ -2872,7 +2654,7 @@ void wlc_phy_watchdog(wlc_phy_t *pih)
(pi->nphy_perical != PHY_PERICAL_MANUAL) &&
((pi->sh->now - pi->nphy_perical_last) >=
pi->sh->glacial_timer))
- wlc_phy_cal_perical((wlc_phy_t *) pi,
+ wlc_phy_cal_perical((struct brcms_phy_pub *) pi,
PHY_PERICAL_WATCHDOG);
wlc_phy_txpwr_papd_cal_nphy(pi);
@@ -2883,8 +2665,9 @@ void wlc_phy_watchdog(wlc_phy_t *pih)
((pi->sh->now - pi->phy_lastcal) >=
pi->sh->glacial_timer)) {
if (!(SCAN_RM_IN_PROGRESS(pi) || ASSOC_INPROG_PHY(pi)))
- wlc_lcnphy_calib_modes(pi,
- LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL);
+ wlc_lcnphy_calib_modes(
+ pi,
+ LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL);
if (!
(SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi)
|| ASSOC_INPROG_PHY(pi)
@@ -2896,19 +2679,18 @@ void wlc_phy_watchdog(wlc_phy_t *pih)
}
}
-void wlc_phy_BSSinit(wlc_phy_t *pih, bool bonlyap, int rssi)
+void wlc_phy_BSSinit(struct brcms_phy_pub *pih, bool bonlyap, int rssi)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
uint i;
uint k;
- for (i = 0; i < MA_WINDOW_SZ; i++) {
+ for (i = 0; i < MA_WINDOW_SZ; i++)
pi->sh->phy_noise_window[i] = (s8) (rssi & 0xff);
- }
if (ISLCNPHY(pi)) {
for (i = 0; i < MA_WINDOW_SZ; i++)
pi->sh->phy_noise_window[i] =
- PHY_NOISE_FIXED_VAL_LCNPHY;
+ PHY_NOISE_FIXED_VAL_LCNPHY;
}
pi->sh->phy_noise_index = 0;
@@ -2931,97 +2713,35 @@ wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, s32 *eps_imag)
*eps_real -= 0x2000;
}
-static const fixed AtanTbl[] = {
- 2949120,
- 1740967,
- 919879,
- 466945,
- 234379,
- 117304,
- 58666,
- 29335,
- 14668,
- 7334,
- 3667,
- 1833,
- 917,
- 458,
- 229,
- 115,
- 57,
- 29
-};
-
-void wlc_phy_cordic(fixed theta, cs32 *val)
-{
- fixed angle, valtmp;
- unsigned iter;
- int signx = 1;
- int signtheta;
-
- val[0].i = CORDIC_AG;
- val[0].q = 0;
- angle = 0;
-
- signtheta = (theta < 0) ? -1 : 1;
- theta =
- ((theta + FIXED(180) * signtheta) % FIXED(360)) -
- FIXED(180) * signtheta;
-
- if (FLOAT(theta) > 90) {
- theta -= FIXED(180);
- signx = -1;
- } else if (FLOAT(theta) < -90) {
- theta += FIXED(180);
- signx = -1;
- }
-
- for (iter = 0; iter < CORDIC_NI; iter++) {
- if (theta > angle) {
- valtmp = val[0].i - (val[0].q >> iter);
- val[0].q = (val[0].i >> iter) + val[0].q;
- val[0].i = valtmp;
- angle += AtanTbl[iter];
- } else {
- valtmp = val[0].i + (val[0].q >> iter);
- val[0].q = -(val[0].i >> iter) + val[0].q;
- val[0].i = valtmp;
- angle -= AtanTbl[iter];
- }
- }
-
- val[0].i = val[0].i * signx;
- val[0].q = val[0].q * signx;
-}
-
-void wlc_phy_cal_perical_mphase_reset(phy_info_t *pi)
+void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi)
{
- wlapi_del_timer(pi->sh->physhim, pi->phycal_timer);
+ wlapi_del_timer(pi->phycal_timer);
pi->cal_type_override = PHY_PERICAL_AUTO;
pi->mphase_cal_phase_id = MPHASE_CAL_STATE_IDLE;
pi->mphase_txcal_cmdidx = 0;
}
-static void wlc_phy_cal_perical_mphase_schedule(phy_info_t *pi, uint delay)
+static void
+wlc_phy_cal_perical_mphase_schedule(struct brcms_phy *pi, uint delay)
{
if ((pi->nphy_perical != PHY_PERICAL_MPHASE) &&
(pi->nphy_perical != PHY_PERICAL_MANUAL))
return;
- wlapi_del_timer(pi->sh->physhim, pi->phycal_timer);
+ wlapi_del_timer(pi->phycal_timer);
pi->mphase_cal_phase_id = MPHASE_CAL_STATE_INIT;
- wlapi_add_timer(pi->sh->physhim, pi->phycal_timer, delay, 0);
+ wlapi_add_timer(pi->phycal_timer, delay, 0);
}
-void wlc_phy_cal_perical(wlc_phy_t *pih, u8 reason)
+void wlc_phy_cal_perical(struct brcms_phy_pub *pih, u8 reason)
{
s16 nphy_currtemp = 0;
s16 delta_temp = 0;
bool do_periodic_cal = true;
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
if (!ISNPHY(pi))
return;
@@ -3036,11 +2756,12 @@ void wlc_phy_cal_perical(wlc_phy_t *pih, u8 reason)
case PHY_PERICAL_PHYINIT:
if (pi->nphy_perical == PHY_PERICAL_MPHASE) {
- if (PHY_PERICAL_MPHASE_PENDING(pi)) {
+ if (PHY_PERICAL_MPHASE_PENDING(pi))
wlc_phy_cal_perical_mphase_reset(pi);
- }
- wlc_phy_cal_perical_mphase_schedule(pi,
- PHY_PERICAL_INIT_DELAY);
+
+ wlc_phy_cal_perical_mphase_schedule(
+ pi,
+ PHY_PERICAL_INIT_DELAY);
}
break;
@@ -3048,17 +2769,16 @@ void wlc_phy_cal_perical(wlc_phy_t *pih, u8 reason)
case PHY_PERICAL_START_IBSS:
case PHY_PERICAL_UP_BSS:
if ((pi->nphy_perical == PHY_PERICAL_MPHASE) &&
- PHY_PERICAL_MPHASE_PENDING(pi)) {
+ PHY_PERICAL_MPHASE_PENDING(pi))
wlc_phy_cal_perical_mphase_reset(pi);
- }
pi->first_cal_after_assoc = true;
pi->cal_type_override = PHY_PERICAL_FULL;
- if (pi->phycal_tempdelta) {
+ if (pi->phycal_tempdelta)
pi->nphy_lastcal_temp = wlc_phy_tempsense_nphy(pi);
- }
+
wlc_phy_cal_perical_nphy_run(pi, PHY_PERICAL_FULL);
break;
@@ -3066,26 +2786,24 @@ void wlc_phy_cal_perical(wlc_phy_t *pih, u8 reason)
if (pi->phycal_tempdelta) {
nphy_currtemp = wlc_phy_tempsense_nphy(pi);
delta_temp =
- (nphy_currtemp > pi->nphy_lastcal_temp) ?
- nphy_currtemp - pi->nphy_lastcal_temp :
- pi->nphy_lastcal_temp - nphy_currtemp;
+ (nphy_currtemp > pi->nphy_lastcal_temp) ?
+ nphy_currtemp - pi->nphy_lastcal_temp :
+ pi->nphy_lastcal_temp - nphy_currtemp;
if ((delta_temp < (s16) pi->phycal_tempdelta) &&
(pi->nphy_txiqlocal_chanspec ==
- pi->radio_chanspec)) {
+ pi->radio_chanspec))
do_periodic_cal = false;
- } else {
+ else
pi->nphy_lastcal_temp = nphy_currtemp;
- }
}
if (do_periodic_cal) {
-
if (pi->nphy_perical == PHY_PERICAL_MPHASE) {
-
if (!PHY_PERICAL_MPHASE_PENDING(pi))
- wlc_phy_cal_perical_mphase_schedule(pi,
- PHY_PERICAL_WDOG_DELAY);
+ wlc_phy_cal_perical_mphase_schedule(
+ pi,
+ PHY_PERICAL_WDOG_DELAY);
} else if (pi->nphy_perical == PHY_PERICAL_SPHASE)
wlc_phy_cal_perical_nphy_run(pi,
PHY_PERICAL_AUTO);
@@ -3096,7 +2814,7 @@ void wlc_phy_cal_perical(wlc_phy_t *pih, u8 reason)
}
}
-void wlc_phy_cal_perical_mphase_restart(phy_info_t *pi)
+void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi)
{
pi->mphase_cal_phase_id = MPHASE_CAL_STATE_INIT;
pi->mphase_txcal_cmdidx = 0;
@@ -3107,49 +2825,49 @@ u8 wlc_phy_nbits(s32 value)
s32 abs_val;
u8 nbits = 0;
- abs_val = ABS(value);
+ abs_val = abs(value);
while ((abs_val >> nbits) > 0)
nbits++;
return nbits;
}
-void wlc_phy_stf_chain_init(wlc_phy_t *pih, u8 txchain, u8 rxchain)
+void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
pi->sh->hw_phytxchain = txchain;
pi->sh->hw_phyrxchain = rxchain;
pi->sh->phytxchain = txchain;
pi->sh->phyrxchain = rxchain;
- pi->pubpi.phy_corenum = (u8) PHY_BITSCNT(pi->sh->phyrxchain);
+ pi->pubpi.phy_corenum = (u8)hweight8(pi->sh->phyrxchain);
}
-void wlc_phy_stf_chain_set(wlc_phy_t *pih, u8 txchain, u8 rxchain)
+void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
pi->sh->phytxchain = txchain;
- if (ISNPHY(pi)) {
+ if (ISNPHY(pi))
wlc_phy_rxcore_setstate_nphy(pih, rxchain);
- }
- pi->pubpi.phy_corenum = (u8) PHY_BITSCNT(pi->sh->phyrxchain);
+
+ pi->pubpi.phy_corenum = (u8)hweight8(pi->sh->phyrxchain);
}
-void wlc_phy_stf_chain_get(wlc_phy_t *pih, u8 *txchain, u8 *rxchain)
+void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, u8 *rxchain)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
*txchain = pi->sh->phytxchain;
*rxchain = pi->sh->phyrxchain;
}
-u8 wlc_phy_stf_chain_active_get(wlc_phy_t *pih)
+u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih)
{
s16 nphy_currtemp;
u8 active_bitmap;
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
active_bitmap = (pi->phy_txcore_heatedup) ? 0x31 : 0x33;
@@ -3177,17 +2895,17 @@ u8 wlc_phy_stf_chain_active_get(wlc_phy_t *pih)
return active_bitmap;
}
-s8 wlc_phy_stf_ssmode_get(wlc_phy_t *pih, chanspec_t chanspec)
+s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, u16 chanspec)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
u8 siso_mcs_id, cdd_mcs_id;
siso_mcs_id =
- (CHSPEC_IS40(chanspec)) ? TXP_FIRST_MCS_40_SISO :
- TXP_FIRST_MCS_20_SISO;
+ (CHSPEC_IS40(chanspec)) ? TXP_FIRST_MCS_40_SISO :
+ TXP_FIRST_MCS_20_SISO;
cdd_mcs_id =
- (CHSPEC_IS40(chanspec)) ? TXP_FIRST_MCS_40_CDD :
- TXP_FIRST_MCS_20_CDD;
+ (CHSPEC_IS40(chanspec)) ? TXP_FIRST_MCS_40_CDD :
+ TXP_FIRST_MCS_20_CDD;
if (pi->tx_power_target[siso_mcs_id] >
(pi->tx_power_target[cdd_mcs_id] + 12))
@@ -3201,7 +2919,7 @@ const u8 *wlc_phy_get_ofdm_rate_lookup(void)
return ofdm_rate_lookup;
}
-void wlc_lcnphy_epa_switch(phy_info_t *pi, bool mode)
+void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode)
{
if ((pi->sh->chip == BCM4313_CHIP_ID) &&
(pi->sh->boardflags & BFL_FEM)) {
@@ -3215,12 +2933,13 @@ void wlc_lcnphy_epa_switch(phy_info_t *pi, bool mode)
}
ai_corereg(pi->sh->sih, SI_CC_IDX,
- offsetof(chipcregs_t, gpiocontrol), ~0x0,
- 0x0);
+ offsetof(struct chipcregs, gpiocontrol),
+ ~0x0, 0x0);
ai_corereg(pi->sh->sih, SI_CC_IDX,
- offsetof(chipcregs_t, gpioout), 0x40, 0x40);
+ offsetof(struct chipcregs, gpioout), 0x40,
+ 0x40);
ai_corereg(pi->sh->sih, SI_CC_IDX,
- offsetof(chipcregs_t, gpioouten), 0x40,
+ offsetof(struct chipcregs, gpioouten), 0x40,
0x40);
} else {
mod_phy_reg(pi, 0x44c, (0x1 << 2), (0) << 2);
@@ -3228,77 +2947,39 @@ void wlc_lcnphy_epa_switch(phy_info_t *pi, bool mode)
mod_phy_reg(pi, 0x44d, (0x1 << 2), (0) << 2);
ai_corereg(pi->sh->sih, SI_CC_IDX,
- offsetof(chipcregs_t, gpioout), 0x40, 0x00);
+ offsetof(struct chipcregs, gpioout), 0x40,
+ 0x00);
ai_corereg(pi->sh->sih, SI_CC_IDX,
- offsetof(chipcregs_t, gpioouten), 0x40, 0x0);
+ offsetof(struct chipcregs, gpioouten), 0x40,
+ 0x0);
ai_corereg(pi->sh->sih, SI_CC_IDX,
- offsetof(chipcregs_t, gpiocontrol), ~0x0,
- 0x40);
+ offsetof(struct chipcregs, gpiocontrol),
+ ~0x0, 0x40);
}
}
}
-static s8
-wlc_user_txpwr_antport_to_rfport(phy_info_t *pi, uint chan, u32 band,
- u8 rate)
-{
- s8 offset = 0;
-
- if (!pi->user_txpwr_at_rfport)
- return offset;
- return offset;
-}
-
-static s8 wlc_phy_env_measure_vbat(phy_info_t *pi)
-{
- if (ISLCNPHY(pi))
- return wlc_lcnphy_vbatsense(pi, 0);
- else
- return 0;
-}
-
-static s8 wlc_phy_env_measure_temperature(phy_info_t *pi)
-{
- if (ISLCNPHY(pi))
- return wlc_lcnphy_tempsense_degree(pi, 0);
- else
- return 0;
-}
-
-static void wlc_phy_upd_env_txpwr_rate_limits(phy_info_t *pi, u32 band)
-{
- u8 i;
- s8 temp, vbat;
-
- for (i = 0; i < TXP_NUM_RATES; i++)
- pi->txpwr_env_limit[i] = WLC_TXPWR_MAX;
-
- vbat = wlc_phy_env_measure_vbat(pi);
- temp = wlc_phy_env_measure_temperature(pi);
-
-}
-
-void wlc_phy_ldpc_override_set(wlc_phy_t *ppi, bool ldpc)
+void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool ldpc)
{
return;
}
void
-wlc_phy_get_pwrdet_offsets(phy_info_t *pi, s8 *cckoffset, s8 *ofdmoffset)
+wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset, s8 *ofdmoffset)
{
*cckoffset = 0;
*ofdmoffset = 0;
}
-s8 wlc_phy_upd_rssi_offset(phy_info_t *pi, s8 rssi, chanspec_t chanspec)
+s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, u16 chanspec)
{
return rssi;
}
-bool wlc_phy_txpower_ipa_ison(wlc_phy_t *ppi)
+bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *ppi)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
if (ISNPHY(pi))
return wlc_phy_n_txpower_ipa_ison(pi);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h
new file mode 100644
index 000000000000..96e15163222b
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * phy_hal.h: functionality exported from the phy to higher layers
+ */
+
+#ifndef _BRCM_PHY_HAL_H_
+#define _BRCM_PHY_HAL_H_
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include <phy_shim.h>
+
+#define IDCODE_VER_MASK 0x0000000f
+#define IDCODE_VER_SHIFT 0
+#define IDCODE_MFG_MASK 0x00000fff
+#define IDCODE_MFG_SHIFT 0
+#define IDCODE_ID_MASK 0x0ffff000
+#define IDCODE_ID_SHIFT 12
+#define IDCODE_REV_MASK 0xf0000000
+#define IDCODE_REV_SHIFT 28
+
+#define NORADIO_ID 0xe4f5
+#define NORADIO_IDCODE 0x4e4f5246
+
+#define BCM2055_ID 0x2055
+#define BCM2055_IDCODE 0x02055000
+#define BCM2055A0_IDCODE 0x1205517f
+
+#define BCM2056_ID 0x2056
+#define BCM2056_IDCODE 0x02056000
+#define BCM2056A0_IDCODE 0x1205617f
+
+#define BCM2057_ID 0x2057
+#define BCM2057_IDCODE 0x02057000
+#define BCM2057A0_IDCODE 0x1205717f
+
+#define BCM2064_ID 0x2064
+#define BCM2064_IDCODE 0x02064000
+#define BCM2064A0_IDCODE 0x0206417f
+
+#define PHY_TPC_HW_OFF false
+#define PHY_TPC_HW_ON true
+
+#define PHY_PERICAL_DRIVERUP 1
+#define PHY_PERICAL_WATCHDOG 2
+#define PHY_PERICAL_PHYINIT 3
+#define PHY_PERICAL_JOIN_BSS 4
+#define PHY_PERICAL_START_IBSS 5
+#define PHY_PERICAL_UP_BSS 6
+#define PHY_PERICAL_CHAN 7
+#define PHY_FULLCAL 8
+
+#define PHY_PERICAL_DISABLE 0
+#define PHY_PERICAL_SPHASE 1
+#define PHY_PERICAL_MPHASE 2
+#define PHY_PERICAL_MANUAL 3
+
+#define PHY_HOLD_FOR_ASSOC 1
+#define PHY_HOLD_FOR_SCAN 2
+#define PHY_HOLD_FOR_RM 4
+#define PHY_HOLD_FOR_PLT 8
+#define PHY_HOLD_FOR_MUTE 16
+#define PHY_HOLD_FOR_NOT_ASSOC 0x20
+
+#define PHY_MUTE_FOR_PREISM 1
+#define PHY_MUTE_ALL 0xffffffff
+
+#define PHY_NOISE_FIXED_VAL (-95)
+#define PHY_NOISE_FIXED_VAL_NPHY (-92)
+#define PHY_NOISE_FIXED_VAL_LCNPHY (-92)
+
+#define PHY_MODE_CAL 0x0002
+#define PHY_MODE_NOISEM 0x0004
+
+#define BRCMS_TXPWR_DB_FACTOR 4
+
+/* a large TX Power as an init value to factor out of min() calculations,
+ * keep low enough to fit in an s8, units are .25 dBm
+ */
+#define BRCMS_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */
+
+#define BRCMS_NUM_RATES_CCK 4
+#define BRCMS_NUM_RATES_OFDM 8
+#define BRCMS_NUM_RATES_MCS_1_STREAM 8
+#define BRCMS_NUM_RATES_MCS_2_STREAM 8
+#define BRCMS_NUM_RATES_MCS_3_STREAM 8
+#define BRCMS_NUM_RATES_MCS_4_STREAM 8
+
+#define BRCMS_RSSI_INVALID 0 /* invalid RSSI value */
+
+struct d11regs;
+struct phy_shim_info;
+
+struct txpwr_limits {
+ u8 cck[BRCMS_NUM_RATES_CCK];
+ u8 ofdm[BRCMS_NUM_RATES_OFDM];
+
+ u8 ofdm_cdd[BRCMS_NUM_RATES_OFDM];
+
+ u8 ofdm_40_siso[BRCMS_NUM_RATES_OFDM];
+ u8 ofdm_40_cdd[BRCMS_NUM_RATES_OFDM];
+
+ u8 mcs_20_siso[BRCMS_NUM_RATES_MCS_1_STREAM];
+ u8 mcs_20_cdd[BRCMS_NUM_RATES_MCS_1_STREAM];
+ u8 mcs_20_stbc[BRCMS_NUM_RATES_MCS_1_STREAM];
+ u8 mcs_20_mimo[BRCMS_NUM_RATES_MCS_2_STREAM];
+
+ u8 mcs_40_siso[BRCMS_NUM_RATES_MCS_1_STREAM];
+ u8 mcs_40_cdd[BRCMS_NUM_RATES_MCS_1_STREAM];
+ u8 mcs_40_stbc[BRCMS_NUM_RATES_MCS_1_STREAM];
+ u8 mcs_40_mimo[BRCMS_NUM_RATES_MCS_2_STREAM];
+ u8 mcs32;
+};
+
+struct tx_power {
+ u32 flags;
+ u16 chanspec; /* txpwr report for this channel */
+ u16 local_chanspec; /* channel on which we are associated */
+ u8 local_max; /* local max according to the AP */
+ u8 local_constraint; /* local constraint according to the AP */
+ s8 antgain[2]; /* Ant gain for each band - from SROM */
+ u8 rf_cores; /* count of RF Cores being reported */
+ u8 est_Pout[4]; /* Latest tx power out estimate per RF chain */
+ u8 est_Pout_act[4]; /* Latest tx power out estimate per RF chain
+ * without adjustment */
+ u8 est_Pout_cck; /* Latest CCK tx power out estimate */
+ u8 tx_power_max[4]; /* Maximum target power among all rates */
+ /* Index of the rate with the max target power */
+ u8 tx_power_max_rate_ind[4];
+ /* User limit */
+ u8 user_limit[WL_TX_POWER_RATES];
+ /* Regulatory power limit */
+ u8 reg_limit[WL_TX_POWER_RATES];
+ /* Max power board can support (SROM) */
+ u8 board_limit[WL_TX_POWER_RATES];
+ /* Latest target power */
+ u8 target[WL_TX_POWER_RATES];
+};
+
+struct tx_inst_power {
+ u8 txpwr_est_Pout[2]; /* Latest estimate for 2.4 and 5 Ghz */
+ u8 txpwr_est_Pout_gofdm; /* Pwr estimate for 2.4 OFDM */
+};
+
+struct brcms_chanvec {
+ u8 vec[MAXCHANNEL / NBBY];
+};
+
+struct shared_phy_params {
+ struct si_pub *sih;
+ struct phy_shim_info *physhim;
+ uint unit;
+ uint corerev;
+ uint buscorerev;
+ u16 vid;
+ u16 did;
+ uint chip;
+ uint chiprev;
+ uint chippkg;
+ uint sromrev;
+ uint boardtype;
+ uint boardrev;
+ uint boardvendor;
+ u32 boardflags;
+ u32 boardflags2;
+};
+
+
+extern struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp);
+extern struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh,
+ struct d11regs __iomem *regs,
+ int bandtype, struct wiphy *wiphy);
+extern void wlc_phy_detach(struct brcms_phy_pub *ppi);
+
+extern bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype,
+ u16 *phyrev, u16 *radioid,
+ u16 *radiover);
+extern bool wlc_phy_get_encore(struct brcms_phy_pub *pih);
+extern u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih);
+
+extern void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *ppi, bool newstate);
+extern void wlc_phy_hw_state_upd(struct brcms_phy_pub *ppi, bool newstate);
+extern void wlc_phy_init(struct brcms_phy_pub *ppi, u16 chanspec);
+extern void wlc_phy_watchdog(struct brcms_phy_pub *ppi);
+extern int wlc_phy_down(struct brcms_phy_pub *ppi);
+extern u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih);
+extern void wlc_phy_cal_init(struct brcms_phy_pub *ppi);
+extern void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init);
+
+extern void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi,
+ u16 chanspec);
+extern u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi);
+extern void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi,
+ u16 newch);
+extern u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi);
+extern void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw);
+
+extern int wlc_phy_rssi_compute(struct brcms_phy_pub *pih,
+ struct d11rxhdr *rxh);
+extern void wlc_phy_por_inform(struct brcms_phy_pub *ppi);
+extern void wlc_phy_noise_sample_intr(struct brcms_phy_pub *ppi);
+extern bool wlc_phy_bist_check_phy(struct brcms_phy_pub *ppi);
+
+extern void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag);
+
+extern void wlc_phy_switch_radio(struct brcms_phy_pub *ppi, bool on);
+extern void wlc_phy_anacore(struct brcms_phy_pub *ppi, bool on);
+
+
+extern void wlc_phy_BSSinit(struct brcms_phy_pub *ppi, bool bonlyap, int rssi);
+
+extern void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi,
+ bool wide_filter);
+extern void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band,
+ struct brcms_chanvec *channels);
+extern u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi,
+ uint band);
+
+extern void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint chan,
+ u8 *_min_, u8 *_max_, int rate);
+extern void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi,
+ uint chan, u8 *_max_, u8 *_min_);
+extern void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi,
+ uint band, s32 *, s32 *, u32 *);
+extern void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi,
+ struct txpwr_limits *,
+ u16 chanspec);
+extern int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm,
+ bool *override);
+extern int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm,
+ bool override);
+extern void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi,
+ struct txpwr_limits *);
+extern bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi);
+extern void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi,
+ bool hwpwrctrl);
+extern u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi);
+extern u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi);
+extern bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *pih);
+
+extern void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain,
+ u8 rxchain);
+extern void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain,
+ u8 rxchain);
+extern void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain,
+ u8 *rxchain);
+extern u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih);
+extern s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih,
+ u16 chanspec);
+extern void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool val);
+
+extern void wlc_phy_cal_perical(struct brcms_phy_pub *ppi, u8 reason);
+extern void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *ppi);
+extern void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock);
+extern void wlc_phy_cal_papd_recal(struct brcms_phy_pub *ppi);
+
+extern void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val);
+extern void wlc_phy_clear_tssi(struct brcms_phy_pub *ppi);
+extern void wlc_phy_hold_upd(struct brcms_phy_pub *ppi, u32 id, bool val);
+extern void wlc_phy_mute_upd(struct brcms_phy_pub *ppi, bool val, u32 flags);
+
+extern void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type);
+
+extern void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi,
+ struct tx_power *power, uint channel);
+
+extern void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal);
+extern bool wlc_phy_test_ison(struct brcms_phy_pub *ppi);
+extern void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi,
+ u8 txpwr_percent);
+extern void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war);
+extern void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih,
+ bool bf_preempt);
+extern void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap);
+
+extern void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end);
+
+extern void wlc_phy_freqtrack_start(struct brcms_phy_pub *ppi);
+extern void wlc_phy_freqtrack_end(struct brcms_phy_pub *ppi);
+
+extern const u8 *wlc_phy_get_ofdm_rate_lookup(void);
+
+extern s8 wlc_phy_get_tx_power_offset_by_mcs(struct brcms_phy_pub *ppi,
+ u8 mcs_offset);
+extern s8 wlc_phy_get_tx_power_offset(struct brcms_phy_pub *ppi, u8 tbl_offset);
+#endif /* _BRCM_PHY_HAL_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_int.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h
index 10cbf520474f..bea85241a244 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_int.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h
@@ -14,56 +14,34 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _wlc_phy_int_h_
-#define _wlc_phy_int_h_
+#ifndef _BRCM_PHY_INT_H_
+#define _BRCM_PHY_INT_H_
-#include <linux/kernel.h>
-#include <bcmdefs.h>
-#include <bcmutils.h>
+#include <types.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
-#include <bcmsrom_fmt.h>
-#include <wlc_phy_hal.h>
-
-#define PHYHAL_ERROR 0x0001
-#define PHYHAL_TRACE 0x0002
-#define PHYHAL_INFORM 0x0004
-
-extern u32 phyhal_msg_level;
-
-#define PHY_INFORM_ON() (phyhal_msg_level & PHYHAL_INFORM)
-#define PHY_THERMAL_ON() (phyhal_msg_level & PHYHAL_THERMAL)
-#define PHY_CAL_ON() (phyhal_msg_level & PHYHAL_CAL)
-
-#ifdef BOARD_TYPE
-#define BOARDTYPE(_type) BOARD_TYPE
-#else
-#define BOARDTYPE(_type) _type
-#endif
+#define PHY_VERSION { 1, 82, 8, 0 }
#define LCNXN_BASEREV 16
-struct wlc_hw_info;
-typedef struct phy_info phy_info_t;
-typedef void (*initfn_t) (phy_info_t *);
-typedef void (*chansetfn_t) (phy_info_t *, chanspec_t);
-typedef int (*longtrnfn_t) (phy_info_t *, int);
-typedef void (*txiqccgetfn_t) (phy_info_t *, u16 *, u16 *);
-typedef void (*txiqccsetfn_t) (phy_info_t *, u16, u16);
-typedef u16(*txloccgetfn_t) (phy_info_t *);
-typedef void (*radioloftgetfn_t) (phy_info_t *, u8 *, u8 *, u8 *,
- u8 *);
-typedef s32(*rxsigpwrfn_t) (phy_info_t *, s32);
-typedef void (*detachfn_t) (phy_info_t *);
-
-#undef ISNPHY
-#undef ISLCNPHY
-#define ISNPHY(pi) PHYTYPE_IS((pi)->pubpi.phy_type, PHY_TYPE_N)
-#define ISLCNPHY(pi) PHYTYPE_IS((pi)->pubpi.phy_type, PHY_TYPE_LCN)
-
-#define ISPHY_11N_CAP(pi) (ISNPHY(pi) || ISLCNPHY(pi))
+struct phy_shim_info;
+
+struct brcms_phy_srom_fem {
+ /* TSSI positive slope, 1: positive, 0: negative */
+ u8 tssipos;
+ /* Ext PA gain-type: full-gain: 0, pa-lite: 1, no_pa: 2 */
+ u8 extpagain;
+ /* support 32 combinations of different Pdet dynamic ranges */
+ u8 pdetrange;
+ /* TR switch isolation */
+ u8 triso;
+ /* antswctrl lookup table configuration: 32 possible choices */
+ u8 antswctrllut;
+};
-#define IS20MHZ(pi) ((pi)->bw == WL_CHANSPEC_BW_20)
-#define IS40MHZ(pi) ((pi)->bw == WL_CHANSPEC_BW_40)
+#define ISNPHY(pi) PHYTYPE_IS((pi)->pubpi.phy_type, PHY_TYPE_N)
+#define ISLCNPHY(pi) PHYTYPE_IS((pi)->pubpi.phy_type, PHY_TYPE_LCN)
#define PHY_GET_RFATTN(rfgain) ((rfgain) & 0x0f)
#define PHY_GET_PADMIX(rfgain) (((rfgain) & 0x10) >> 4)
@@ -198,7 +176,9 @@ typedef void (*detachfn_t) (phy_info_t *);
#define PHY_PERICAL_WDOG_DELAY 5
#define MPHASE_TXCAL_NUMCMDS 2
-#define PHY_PERICAL_MPHASE_PENDING(pi) (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_IDLE)
+
+#define PHY_PERICAL_MPHASE_PENDING(pi) \
+ (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_IDLE)
enum {
MPHASE_CAL_STATE_IDLE = 0,
@@ -215,7 +195,7 @@ enum {
MPHASE_CAL_STATE_IDLETSSI
};
-typedef enum {
+enum phy_cal_mode {
CAL_FULL,
CAL_RECAL,
CAL_CURRECAL,
@@ -223,7 +203,7 @@ typedef enum {
CAL_GCTRL,
CAL_SOFT,
CAL_DIGLO
-} phy_cal_mode_t;
+};
#define RDR_NTIERS 1
#define RDR_TIER_SIZE 64
@@ -243,63 +223,45 @@ typedef enum {
#define CORDIC_AG 39797
#define CORDIC_NI 18
#define FIXED(X) ((s32)((X) << 16))
-#define FLOAT(X) (((X) >= 0) ? ((((X) >> 15) + 1) >> 1) : -((((-(X)) >> 15) + 1) >> 1))
+
+#define FLOAT(X) \
+ (((X) >= 0) ? ((((X) >> 15) + 1) >> 1) : -((((-(X)) >> 15) + 1) >> 1))
#define PHY_CHAIN_TX_DISABLE_TEMP 115
#define PHY_HYSTERESIS_DELTATEMP 5
-#define PHY_BITSCNT(x) bcm_bitcount((u8 *)&(x), sizeof(u8))
+#define SCAN_INPROG_PHY(pi) \
+ (mboolisset(pi->measure_hold, PHY_HOLD_FOR_SCAN))
-#define MOD_PHY_REG(pi, phy_type, reg_name, field, value) \
- mod_phy_reg(pi, phy_type##_##reg_name, phy_type##_##reg_name##_##field##_MASK, \
- (value) << phy_type##_##reg_name##_##field##_##SHIFT);
-#define READ_PHY_REG(pi, phy_type, reg_name, field) \
- ((read_phy_reg(pi, phy_type##_##reg_name) & phy_type##_##reg_name##_##field##_##MASK)\
- >> phy_type##_##reg_name##_##field##_##SHIFT)
+#define PLT_INPROG_PHY(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_PLT))
-#define VALID_PHYTYPE(phytype) (((uint)phytype == PHY_TYPE_N) || \
- ((uint)phytype == PHY_TYPE_LCN))
+#define ASSOC_INPROG_PHY(pi) \
+ (mboolisset(pi->measure_hold, PHY_HOLD_FOR_ASSOC))
-#define VALID_N_RADIO(radioid) ((radioid == BCM2055_ID) || (radioid == BCM2056_ID) || \
- (radioid == BCM2057_ID))
-#define VALID_LCN_RADIO(radioid) (radioid == BCM2064_ID)
+#define SCAN_RM_IN_PROGRESS(pi) \
+ (mboolisset(pi->measure_hold, PHY_HOLD_FOR_SCAN | PHY_HOLD_FOR_RM))
-#define VALID_RADIO(pi, radioid) (\
- (ISNPHY(pi) ? VALID_N_RADIO(radioid) : false) || \
- (ISLCNPHY(pi) ? VALID_LCN_RADIO(radioid) : false))
+#define PHY_MUTED(pi) \
+ (mboolisset(pi->measure_hold, PHY_HOLD_FOR_MUTE))
-#define SCAN_INPROG_PHY(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_SCAN))
-#define RM_INPROG_PHY(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_RM))
-#define PLT_INPROG_PHY(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_PLT))
-#define ASSOC_INPROG_PHY(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_ASSOC))
-#define SCAN_RM_IN_PROGRESS(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_SCAN | PHY_HOLD_FOR_RM))
-#define PHY_MUTED(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_MUTE))
-#define PUB_NOT_ASSOC(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_NOT_ASSOC))
+#define PUB_NOT_ASSOC(pi) \
+ (mboolisset(pi->measure_hold, PHY_HOLD_FOR_NOT_ASSOC))
-#if defined(EXT_CBALL)
-#define NORADIO_ENAB(pub) ((pub).radioid == NORADIO_ID)
-#else
-#define NORADIO_ENAB(pub) 0
-#endif
-
-#define PHY_LTRN_LIST_LEN 64
-extern u16 ltrn_list[PHY_LTRN_LIST_LEN];
-
-typedef struct _phy_table_info {
+struct phy_table_info {
uint table;
int q;
uint max;
-} phy_table_info_t;
+};
-typedef struct phytbl_info {
+struct phytbl_info {
const void *tbl_ptr;
u32 tbl_len;
u32 tbl_id;
u32 tbl_offset;
u32 tbl_width;
-} phytbl_info_t;
+};
-typedef struct {
+struct interference_info {
u8 curr_home_channel;
u16 crsminpwrthld_40_stored;
u16 crsminpwrthld_20L_stored;
@@ -369,10 +331,9 @@ typedef struct {
u16 radio_2057_core2_rssi_wb2_gc_stored;
u16 radio_2057_core1_rssi_nb_gc_stored;
u16 radio_2057_core2_rssi_nb_gc_stored;
+};
-} interference_info_t;
-
-typedef struct {
+struct aci_save_gphy {
u16 rc_cal_ovr;
u16 phycrsth1;
u16 phycrsth2;
@@ -406,21 +367,21 @@ typedef struct {
u16 div_srch_gn_back;
u16 ant_dwell;
u16 ant_wr_settle;
-} aci_save_gphy_t;
+};
-typedef struct _lo_complex_t {
+struct lo_complex_abgphy_info {
s8 i;
s8 q;
-} lo_complex_abgphy_info_t;
+};
-typedef struct _nphy_iq_comp {
+struct nphy_iq_comp {
s16 a0;
s16 b0;
s16 a1;
s16 b1;
-} nphy_iq_comp_t;
+};
-typedef struct _nphy_txpwrindex {
+struct nphy_txpwrindex {
s8 index;
s8 index_internal;
s8 index_internal_save;
@@ -431,20 +392,20 @@ typedef struct _nphy_txpwrindex {
u16 iqcomp_a;
u16 iqcomp_b;
u16 locomp;
-} phy_txpwrindex_t;
+};
-typedef struct {
+struct txiqcal_cache {
u16 txcal_coeffs_2G[8];
u16 txcal_radio_regs_2G[8];
- nphy_iq_comp_t rxcal_coeffs_2G;
+ struct nphy_iq_comp rxcal_coeffs_2G;
u16 txcal_coeffs_5G[8];
u16 txcal_radio_regs_5G[8];
- nphy_iq_comp_t rxcal_coeffs_5G;
-} txiqcal_cache_t;
+ struct nphy_iq_comp rxcal_coeffs_5G;
+};
-typedef struct _nphy_pwrctrl {
+struct nphy_pwrctrl {
s8 max_pwr_2g;
s8 idle_targ_2g;
s16 pwrdet_2g_a1;
@@ -471,34 +432,34 @@ typedef struct _nphy_pwrctrl {
s16 a1;
s16 b0;
s16 b1;
-} phy_pwrctrl_t;
+};
-typedef struct _nphy_txgains {
+struct nphy_txgains {
u16 txlpf[2];
u16 txgm[2];
u16 pga[2];
u16 pad[2];
u16 ipa[2];
-} nphy_txgains_t;
+};
#define PHY_NOISEVAR_BUFSIZE 10
-typedef struct _nphy_noisevar_buf {
+struct nphy_noisevar_buf {
int bufcount;
int tone_id[PHY_NOISEVAR_BUFSIZE];
u32 noise_vars[PHY_NOISEVAR_BUFSIZE];
u32 min_noise_vars[PHY_NOISEVAR_BUFSIZE];
-} phy_noisevar_buf_t;
+};
-typedef struct {
+struct rssical_cache {
u16 rssical_radio_regs_2G[2];
u16 rssical_phyregs_2G[12];
u16 rssical_radio_regs_5G[2];
u16 rssical_phyregs_5G[12];
-} rssical_cache_t;
+};
-typedef struct {
+struct lcnphy_cal_results {
u16 txiqlocal_a;
u16 txiqlocal_b;
@@ -522,13 +483,13 @@ typedef struct {
u16 rxiqcal_coeff_a0;
u16 rxiqcal_coeff_b0;
-} lcnphy_cal_results_t;
+};
struct shared_phy {
- struct phy_info *phy_head;
+ struct brcms_phy *phy_head;
uint unit;
- si_t *sih;
- void *physhim;
+ struct si_pub *sih;
+ struct phy_shim_info *physhim;
uint corerev;
u32 machwcap;
bool up;
@@ -545,7 +506,6 @@ struct shared_phy {
uint boardvendor;
u32 boardflags;
u32 boardflags2;
- uint bustype;
uint buscorerev;
uint fast_timer;
uint slow_timer;
@@ -561,7 +521,7 @@ struct shared_phy {
bool _rifs_phy;
};
-struct phy_pub {
+struct brcms_phy_pub {
uint phy_type;
uint phy_rev;
u8 phy_corenum;
@@ -574,49 +534,40 @@ struct phy_pub {
bool abgphy_encore;
};
-struct phy_info_nphy;
-typedef struct phy_info_nphy phy_info_nphy_t;
-
-struct phy_info_lcnphy;
-typedef struct phy_info_lcnphy phy_info_lcnphy_t;
-
struct phy_func_ptr {
- initfn_t init;
- initfn_t calinit;
- chansetfn_t chanset;
- initfn_t txpwrrecalc;
- longtrnfn_t longtrn;
- txiqccgetfn_t txiqccget;
- txiqccsetfn_t txiqccset;
- txloccgetfn_t txloccget;
- radioloftgetfn_t radioloftget;
- initfn_t carrsuppr;
- rxsigpwrfn_t rxsigpwr;
- detachfn_t detach;
+ void (*init)(struct brcms_phy *);
+ void (*calinit)(struct brcms_phy *);
+ void (*chanset)(struct brcms_phy *, u16 chanspec);
+ void (*txpwrrecalc)(struct brcms_phy *);
+ int (*longtrn)(struct brcms_phy *, int);
+ void (*txiqccget)(struct brcms_phy *, u16 *, u16 *);
+ void (*txiqccset)(struct brcms_phy *, u16, u16);
+ u16 (*txloccget)(struct brcms_phy *);
+ void (*radioloftget)(struct brcms_phy *, u8 *, u8 *, u8 *, u8 *);
+ void (*carrsuppr)(struct brcms_phy *);
+ s32 (*rxsigpwr)(struct brcms_phy *, s32);
+ void (*detach)(struct brcms_phy *);
};
-typedef struct phy_func_ptr phy_func_ptr_t;
-struct phy_info {
- wlc_phy_t pubpi_ro;
- shared_phy_t *sh;
- phy_func_ptr_t pi_fptr;
- void *pi_ptr;
+struct brcms_phy {
+ struct brcms_phy_pub pubpi_ro;
+ struct shared_phy *sh;
+ struct phy_func_ptr pi_fptr;
union {
- phy_info_lcnphy_t *pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcnphy;
} u;
bool user_txpwr_at_rfport;
- d11regs_t *regs;
- struct phy_info *next;
- char *vars;
- wlc_phy_t pubpi;
+ struct d11regs __iomem *regs;
+ struct brcms_phy *next;
+ struct brcms_phy_pub pubpi;
bool do_initcal;
bool phytest_on;
bool ofdm_rateset_war;
bool bf_preempt_4306;
- chanspec_t radio_chanspec;
+ u16 radio_chanspec;
u8 antsel_type;
u16 bw;
u8 txpwr_percent;
@@ -632,7 +583,7 @@ struct phy_info {
int phynoise_chan_watchdog;
bool phynoise_polling;
bool disable_percal;
- mbool measure_hold;
+ u32 measure_hold;
s16 txpa_2g[PWRTBL_NUM_COEFF];
s16 txpa_2g_low_temp[PWRTBL_NUM_COEFF];
@@ -653,8 +604,8 @@ struct phy_info {
s8 tx_power_offset[TXP_NUM_RATES];
u8 tx_power_target[TXP_NUM_RATES];
- srom_fem_t srom_fem2g;
- srom_fem_t srom_fem5g;
+ struct brcms_phy_srom_fem srom_fem2g;
+ struct brcms_phy_srom_fem srom_fem5g;
u8 tx_power_max;
u8 tx_power_max_rate_ind;
@@ -725,7 +676,8 @@ struct phy_info {
u16 mintxbias;
u16 mintxmag;
- lo_complex_abgphy_info_t gphy_locomp_iq[STATIC_NUM_RF][STATIC_NUM_BB];
+ struct lo_complex_abgphy_info gphy_locomp_iq
+ [STATIC_NUM_RF][STATIC_NUM_BB];
s8 stats_11b_txpower[STATIC_NUM_RF][STATIC_NUM_BB];
u16 gain_table[TX_GAIN_TABLE_LENGTH];
bool loopback_gain;
@@ -783,8 +735,8 @@ struct phy_info {
u32 nphy_bb_mult_save;
u16 nphy_txiqlocal_bestc[11];
bool nphy_txiqlocal_coeffsvalid;
- phy_txpwrindex_t nphy_txpwrindex[PHY_CORE_NUM_2];
- phy_pwrctrl_t nphy_pwrctrl_info[PHY_CORE_NUM_2];
+ struct nphy_txpwrindex nphy_txpwrindex[PHY_CORE_NUM_2];
+ struct nphy_pwrctrl nphy_pwrctrl_info[PHY_CORE_NUM_2];
u16 cck2gpo;
u32 ofdm2gpo;
u32 ofdm5gpo;
@@ -842,18 +794,18 @@ struct phy_info {
u8 mphase_txcal_cmdidx;
u8 mphase_txcal_numcmds;
u16 mphase_txcal_bestcoeffs[11];
- chanspec_t nphy_txiqlocal_chanspec;
- chanspec_t nphy_iqcal_chanspec_2G;
- chanspec_t nphy_iqcal_chanspec_5G;
- chanspec_t nphy_rssical_chanspec_2G;
- chanspec_t nphy_rssical_chanspec_5G;
+ u16 nphy_txiqlocal_chanspec;
+ u16 nphy_iqcal_chanspec_2G;
+ u16 nphy_iqcal_chanspec_5G;
+ u16 nphy_rssical_chanspec_2G;
+ u16 nphy_rssical_chanspec_5G;
struct wlapi_timer *phycal_timer;
bool use_int_tx_iqlo_cal_nphy;
bool internal_tx_iqlo_cal_tapoff_intpa_nphy;
s16 nphy_lastcal_temp;
- txiqcal_cache_t calibration_cache;
- rssical_cache_t rssical_cache;
+ struct txiqcal_cache calibration_cache;
+ struct rssical_cache rssical_cache;
u8 nphy_txpwr_idx[2];
u8 nphy_papd_cal_type;
@@ -884,7 +836,7 @@ struct phy_info {
u8 nphy_txcal_pwr_idx[2];
u8 nphy_rxcal_pwr_idx[2];
u16 nphy_cal_orig_tx_gain[2];
- nphy_txgains_t nphy_cal_target_gain;
+ struct nphy_txgains nphy_cal_target_gain;
u16 nphy_txcal_bbmult;
u16 nphy_gmval;
@@ -895,7 +847,7 @@ struct phy_info {
bool nphy_aband_spurwar_en;
u16 nphy_rccal_value;
u16 nphy_crsminpwr[3];
- phy_noisevar_buf_t nphy_saved_noisevars;
+ struct nphy_noisevar_buf nphy_saved_noisevars;
bool nphy_anarxlpf_adjusted;
bool nphy_crsminpwr_adjusted;
bool nphy_noisevars_adjusted;
@@ -939,141 +891,121 @@ struct phy_info {
struct wiphy *wiphy;
};
-typedef s32 fixed;
-
-typedef struct _cs32 {
- fixed q;
- fixed i;
-} cs32;
+struct cs32 {
+ s32 q;
+ s32 i;
+};
-typedef struct radio_regs {
+struct radio_regs {
u16 address;
u32 init_a;
u32 init_g;
u8 do_init_a;
u8 do_init_g;
-} radio_regs_t;
+};
-typedef struct radio_20xx_regs {
+struct radio_20xx_regs {
u16 address;
u8 init;
u8 do_init;
-} radio_20xx_regs_t;
+};
-typedef struct lcnphy_radio_regs {
+struct lcnphy_radio_regs {
u16 address;
u8 init_a;
u8 init_g;
u8 do_init_a;
u8 do_init_g;
-} lcnphy_radio_regs_t;
-
-extern lcnphy_radio_regs_t lcnphy_radio_regs_2064[];
-extern lcnphy_radio_regs_t lcnphy_radio_regs_2066[];
-extern radio_regs_t regs_2055[], regs_SYN_2056[], regs_TX_2056[],
- regs_RX_2056[];
-extern radio_regs_t regs_SYN_2056_A1[], regs_TX_2056_A1[], regs_RX_2056_A1[];
-extern radio_regs_t regs_SYN_2056_rev5[], regs_TX_2056_rev5[],
- regs_RX_2056_rev5[];
-extern radio_regs_t regs_SYN_2056_rev6[], regs_TX_2056_rev6[],
- regs_RX_2056_rev6[];
-extern radio_regs_t regs_SYN_2056_rev7[], regs_TX_2056_rev7[],
- regs_RX_2056_rev7[];
-extern radio_regs_t regs_SYN_2056_rev8[], regs_TX_2056_rev8[],
- regs_RX_2056_rev8[];
-extern radio_20xx_regs_t regs_2057_rev4[], regs_2057_rev5[], regs_2057_rev5v1[];
-extern radio_20xx_regs_t regs_2057_rev7[], regs_2057_rev8[];
-
-extern char *phy_getvar(phy_info_t *pi, const char *name);
-extern int phy_getintvar(phy_info_t *pi, const char *name);
-#define PHY_GETVAR(pi, name) phy_getvar(pi, name)
-#define PHY_GETINTVAR(pi, name) phy_getintvar(pi, name)
-
-extern u16 read_phy_reg(phy_info_t *pi, u16 addr);
-extern void write_phy_reg(phy_info_t *pi, u16 addr, u16 val);
-extern void and_phy_reg(phy_info_t *pi, u16 addr, u16 val);
-extern void or_phy_reg(phy_info_t *pi, u16 addr, u16 val);
-extern void mod_phy_reg(phy_info_t *pi, u16 addr, u16 mask, u16 val);
-
-extern u16 read_radio_reg(phy_info_t *pi, u16 addr);
-extern void or_radio_reg(phy_info_t *pi, u16 addr, u16 val);
-extern void and_radio_reg(phy_info_t *pi, u16 addr, u16 val);
-extern void mod_radio_reg(phy_info_t *pi, u16 addr, u16 mask,
+};
+
+extern u16 read_phy_reg(struct brcms_phy *pi, u16 addr);
+extern void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val);
+extern void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val);
+extern void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val);
+extern void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val);
+
+extern u16 read_radio_reg(struct brcms_phy *pi, u16 addr);
+extern void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val);
+extern void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val);
+extern void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask,
u16 val);
-extern void xor_radio_reg(phy_info_t *pi, u16 addr, u16 mask);
+extern void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask);
-extern void write_radio_reg(phy_info_t *pi, u16 addr, u16 val);
+extern void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val);
-extern void wlc_phyreg_enter(wlc_phy_t *pih);
-extern void wlc_phyreg_exit(wlc_phy_t *pih);
-extern void wlc_radioreg_enter(wlc_phy_t *pih);
-extern void wlc_radioreg_exit(wlc_phy_t *pih);
+extern void wlc_phyreg_enter(struct brcms_phy_pub *pih);
+extern void wlc_phyreg_exit(struct brcms_phy_pub *pih);
+extern void wlc_radioreg_enter(struct brcms_phy_pub *pih);
+extern void wlc_radioreg_exit(struct brcms_phy_pub *pih);
-extern void wlc_phy_read_table(phy_info_t *pi, const phytbl_info_t *ptbl_info,
+extern void wlc_phy_read_table(struct brcms_phy *pi,
+ const struct phytbl_info *ptbl_info,
u16 tblAddr, u16 tblDataHi,
u16 tblDatalo);
-extern void wlc_phy_write_table(phy_info_t *pi,
- const phytbl_info_t *ptbl_info, u16 tblAddr,
- u16 tblDataHi, u16 tblDatalo);
-extern void wlc_phy_table_addr(phy_info_t *pi, uint tbl_id, uint tbl_offset,
- u16 tblAddr, u16 tblDataHi,
+extern void wlc_phy_write_table(struct brcms_phy *pi,
+ const struct phytbl_info *ptbl_info,
+ u16 tblAddr, u16 tblDataHi, u16 tblDatalo);
+extern void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id,
+ uint tbl_offset, u16 tblAddr, u16 tblDataHi,
u16 tblDataLo);
-extern void wlc_phy_table_data_write(phy_info_t *pi, uint width, u32 val);
+extern void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val);
-extern void write_phy_channel_reg(phy_info_t *pi, uint val);
-extern void wlc_phy_txpower_update_shm(phy_info_t *pi);
+extern void write_phy_channel_reg(struct brcms_phy *pi, uint val);
+extern void wlc_phy_txpower_update_shm(struct brcms_phy *pi);
-extern void wlc_phy_cordic(fixed theta, cs32 *val);
extern u8 wlc_phy_nbits(s32 value);
extern void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_dB, u8 core);
-extern uint wlc_phy_init_radio_regs_allbands(phy_info_t *pi,
- radio_20xx_regs_t *radioregs);
-extern uint wlc_phy_init_radio_regs(phy_info_t *pi, radio_regs_t *radioregs,
+extern uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi,
+ struct radio_20xx_regs *radioregs);
+extern uint wlc_phy_init_radio_regs(struct brcms_phy *pi,
+ const struct radio_regs *radioregs,
u16 core_offset);
-extern void wlc_phy_txpower_ipa_upd(phy_info_t *pi);
+extern void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi);
-extern void wlc_phy_do_dummy_tx(phy_info_t *pi, bool ofdm, bool pa_on);
+extern void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on);
extern void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real,
s32 *eps_imag);
-extern void wlc_phy_cal_perical_mphase_reset(phy_info_t *pi);
-extern void wlc_phy_cal_perical_mphase_restart(phy_info_t *pi);
+extern void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi);
+extern void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi);
-extern bool wlc_phy_attach_nphy(phy_info_t *pi);
-extern bool wlc_phy_attach_lcnphy(phy_info_t *pi);
+extern bool wlc_phy_attach_nphy(struct brcms_phy *pi);
+extern bool wlc_phy_attach_lcnphy(struct brcms_phy *pi);
-extern void wlc_phy_detach_lcnphy(phy_info_t *pi);
+extern void wlc_phy_detach_lcnphy(struct brcms_phy *pi);
-extern void wlc_phy_init_nphy(phy_info_t *pi);
-extern void wlc_phy_init_lcnphy(phy_info_t *pi);
+extern void wlc_phy_init_nphy(struct brcms_phy *pi);
+extern void wlc_phy_init_lcnphy(struct brcms_phy *pi);
-extern void wlc_phy_cal_init_nphy(phy_info_t *pi);
-extern void wlc_phy_cal_init_lcnphy(phy_info_t *pi);
+extern void wlc_phy_cal_init_nphy(struct brcms_phy *pi);
+extern void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi);
-extern void wlc_phy_chanspec_set_nphy(phy_info_t *pi, chanspec_t chanspec);
-extern void wlc_phy_chanspec_set_lcnphy(phy_info_t *pi, chanspec_t chanspec);
-extern void wlc_phy_chanspec_set_fixup_lcnphy(phy_info_t *pi,
- chanspec_t chanspec);
+extern void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi,
+ u16 chanspec);
+extern void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi,
+ u16 chanspec);
+extern void wlc_phy_chanspec_set_fixup_lcnphy(struct brcms_phy *pi,
+ u16 chanspec);
extern int wlc_phy_channel2freq(uint channel);
extern int wlc_phy_chanspec_freq2bandrange_lpssn(uint);
-extern int wlc_phy_chanspec_bandrange_get(phy_info_t *, chanspec_t);
+extern int wlc_phy_chanspec_bandrange_get(struct brcms_phy *, u16 chanspec);
-extern void wlc_lcnphy_set_tx_pwr_ctrl(phy_info_t *pi, u16 mode);
-extern s8 wlc_lcnphy_get_current_tx_pwr_idx(phy_info_t *pi);
+extern void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode);
+extern s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi);
-extern void wlc_phy_txpower_recalc_target_nphy(phy_info_t *pi);
-extern void wlc_lcnphy_txpower_recalc_target(phy_info_t *pi);
-extern void wlc_phy_txpower_recalc_target_lcnphy(phy_info_t *pi);
+extern void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi);
+extern void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi);
+extern void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi);
-extern void wlc_lcnphy_set_tx_pwr_by_index(phy_info_t *pi, int index);
-extern void wlc_lcnphy_tx_pu(phy_info_t *pi, bool bEnable);
-extern void wlc_lcnphy_stop_tx_tone(phy_info_t *pi);
-extern void wlc_lcnphy_start_tx_tone(phy_info_t *pi, s32 f_kHz,
+extern void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index);
+extern void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable);
+extern void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi);
+extern void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz,
u16 max_val, bool iqcalmode);
-extern void wlc_phy_txpower_sromlimit_get_nphy(phy_info_t *pi, uint chan,
+extern void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan,
u8 *max_pwr, u8 rate_id);
extern void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start,
u8 rate_mcs_end,
@@ -1083,21 +1015,21 @@ extern void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power,
u8 rate_ofdm_end,
u8 rate_mcs_start);
-extern u16 wlc_lcnphy_tempsense(phy_info_t *pi, bool mode);
-extern s16 wlc_lcnphy_tempsense_new(phy_info_t *pi, bool mode);
-extern s8 wlc_lcnphy_tempsense_degree(phy_info_t *pi, bool mode);
-extern s8 wlc_lcnphy_vbatsense(phy_info_t *pi, bool mode);
-extern void wlc_phy_carrier_suppress_lcnphy(phy_info_t *pi);
-extern void wlc_lcnphy_crsuprs(phy_info_t *pi, int channel);
-extern void wlc_lcnphy_epa_switch(phy_info_t *pi, bool mode);
-extern void wlc_2064_vco_cal(phy_info_t *pi);
+extern u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode);
+extern s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode);
+extern s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode);
+extern s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode);
+extern void wlc_phy_carrier_suppress_lcnphy(struct brcms_phy *pi);
+extern void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel);
+extern void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode);
+extern void wlc_2064_vco_cal(struct brcms_phy *pi);
-extern void wlc_phy_txpower_recalc_target(phy_info_t *pi);
+extern void wlc_phy_txpower_recalc_target(struct brcms_phy *pi);
#define LCNPHY_TBL_ID_PAPDCOMPDELTATBL 0x18
#define LCNPHY_TX_POWER_TABLE_SIZE 128
#define LCNPHY_MAX_TX_POWER_INDEX (LCNPHY_TX_POWER_TABLE_SIZE - 1)
-#define LCNPHY_TBL_ID_TXPWRCTL 0x07
+#define LCNPHY_TBL_ID_TXPWRCTL 0x07
#define LCNPHY_TX_PWR_CTRL_OFF 0
#define LCNPHY_TX_PWR_CTRL_SW (0x1 << 15)
#define LCNPHY_TX_PWR_CTRL_HW ((0x1 << 15) | \
@@ -1106,121 +1038,132 @@ extern void wlc_phy_txpower_recalc_target(phy_info_t *pi);
#define LCNPHY_TX_PWR_CTRL_TEMPBASED 0xE001
-extern void wlc_lcnphy_write_table(phy_info_t *pi, const phytbl_info_t *pti);
-extern void wlc_lcnphy_read_table(phy_info_t *pi, phytbl_info_t *pti);
-extern void wlc_lcnphy_set_tx_iqcc(phy_info_t *pi, u16 a, u16 b);
-extern void wlc_lcnphy_set_tx_locc(phy_info_t *pi, u16 didq);
-extern void wlc_lcnphy_get_tx_iqcc(phy_info_t *pi, u16 *a, u16 *b);
-extern u16 wlc_lcnphy_get_tx_locc(phy_info_t *pi);
-extern void wlc_lcnphy_get_radio_loft(phy_info_t *pi, u8 *ei0,
+extern void wlc_lcnphy_write_table(struct brcms_phy *pi,
+ const struct phytbl_info *pti);
+extern void wlc_lcnphy_read_table(struct brcms_phy *pi,
+ struct phytbl_info *pti);
+extern void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b);
+extern void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq);
+extern void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b);
+extern u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi);
+extern void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0,
u8 *eq0, u8 *fi0, u8 *fq0);
-extern void wlc_lcnphy_calib_modes(phy_info_t *pi, uint mode);
-extern void wlc_lcnphy_deaf_mode(phy_info_t *pi, bool mode);
-extern bool wlc_phy_tpc_isenabled_lcnphy(phy_info_t *pi);
-extern void wlc_lcnphy_tx_pwr_update_npt(phy_info_t *pi);
+extern void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode);
+extern void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode);
+extern bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi);
+extern void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi);
extern s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1);
-extern void wlc_lcnphy_get_tssi(phy_info_t *pi, s8 *ofdm_pwr,
+extern void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr,
s8 *cck_pwr);
-extern void wlc_lcnphy_tx_power_adjustment(wlc_phy_t *ppi);
+extern void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi);
-extern s32 wlc_lcnphy_rx_signal_power(phy_info_t *pi, s32 gain_index);
+extern s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index);
#define NPHY_MAX_HPVGA1_INDEX 10
#define NPHY_DEF_HPVGA1_INDEXLIMIT 7
-typedef struct _phy_iq_est {
+struct phy_iq_est {
s32 iq_prod;
u32 i_pwr;
u32 q_pwr;
-} phy_iq_est_t;
+};
+
+extern void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi,
+ bool enable);
+extern void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode);
+
+#define wlc_phy_write_table_nphy(pi, pti) \
+ wlc_phy_write_table(pi, pti, 0x72, 0x74, 0x73)
+
+#define wlc_phy_read_table_nphy(pi, pti) \
+ wlc_phy_read_table(pi, pti, 0x72, 0x74, 0x73)
-extern void wlc_phy_stay_in_carriersearch_nphy(phy_info_t *pi, bool enable);
-extern void wlc_nphy_deaf_mode(phy_info_t *pi, bool mode);
+#define wlc_nphy_table_addr(pi, id, off) \
+ wlc_phy_table_addr((pi), (id), (off), 0x72, 0x74, 0x73)
-#define wlc_phy_write_table_nphy(pi, pti) wlc_phy_write_table(pi, pti, 0x72, \
- 0x74, 0x73)
-#define wlc_phy_read_table_nphy(pi, pti) wlc_phy_read_table(pi, pti, 0x72, \
- 0x74, 0x73)
-#define wlc_nphy_table_addr(pi, id, off) wlc_phy_table_addr((pi), (id), (off), \
- 0x72, 0x74, 0x73)
-#define wlc_nphy_table_data_write(pi, w, v) wlc_phy_table_data_write((pi), (w), (v))
+#define wlc_nphy_table_data_write(pi, w, v) \
+ wlc_phy_table_data_write((pi), (w), (v))
-extern void wlc_phy_table_read_nphy(phy_info_t *pi, u32, u32 l, u32 o,
+extern void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32, u32 l, u32 o,
u32 w, void *d);
-extern void wlc_phy_table_write_nphy(phy_info_t *pi, u32, u32, u32,
+extern void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32,
u32, const void *);
#define PHY_IPA(pi) \
((pi->ipa2g_on && CHSPEC_IS2G(pi->radio_chanspec)) || \
(pi->ipa5g_on && CHSPEC_IS5G(pi->radio_chanspec)))
-#define WLC_PHY_WAR_PR51571(pi) \
- if (((pi)->sh->bustype == PCI_BUS) && NREV_LT((pi)->pubpi.phy_rev, 3)) \
+#define BRCMS_PHY_WAR_PR51571(pi) \
+ if (NREV_LT((pi)->pubpi.phy_rev, 3)) \
(void)R_REG(&(pi)->regs->maccontrol)
-extern void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype);
-extern void wlc_phy_aci_reset_nphy(phy_info_t *pi);
-extern void wlc_phy_pa_override_nphy(phy_info_t *pi, bool en);
+extern void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype);
+extern void wlc_phy_aci_reset_nphy(struct brcms_phy *pi);
+extern void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en);
-extern u8 wlc_phy_get_chan_freq_range_nphy(phy_info_t *pi, uint chan);
-extern void wlc_phy_switch_radio_nphy(phy_info_t *pi, bool on);
+extern u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint chan);
+extern void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on);
-extern void wlc_phy_stf_chain_upd_nphy(phy_info_t *pi);
+extern void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi);
-extern void wlc_phy_force_rfseq_nphy(phy_info_t *pi, u8 cmd);
-extern s16 wlc_phy_tempsense_nphy(phy_info_t *pi);
+extern void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd);
+extern s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi);
-extern u16 wlc_phy_classifier_nphy(phy_info_t *pi, u16 mask, u16 val);
+extern u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val);
-extern void wlc_phy_rx_iq_est_nphy(phy_info_t *pi, phy_iq_est_t *est,
+extern void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est,
u16 num_samps, u8 wait_time,
u8 wait_for_crs);
-extern void wlc_phy_rx_iq_coeffs_nphy(phy_info_t *pi, u8 write,
- nphy_iq_comp_t *comp);
-extern void wlc_phy_aci_and_noise_reduction_nphy(phy_info_t *pi);
+extern void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write,
+ struct nphy_iq_comp *comp);
+extern void wlc_phy_aci_and_noise_reduction_nphy(struct brcms_phy *pi);
-extern void wlc_phy_rxcore_setstate_nphy(wlc_phy_t *pih, u8 rxcore_bitmask);
-extern u8 wlc_phy_rxcore_getstate_nphy(wlc_phy_t *pih);
+extern void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih,
+ u8 rxcore_bitmask);
+extern u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih);
-extern void wlc_phy_txpwrctrl_enable_nphy(phy_info_t *pi, u8 ctrl_type);
-extern void wlc_phy_txpwr_fixpower_nphy(phy_info_t *pi);
-extern void wlc_phy_txpwr_apply_nphy(phy_info_t *pi);
-extern void wlc_phy_txpwr_papd_cal_nphy(phy_info_t *pi);
-extern u16 wlc_phy_txpwr_idx_get_nphy(phy_info_t *pi);
+extern void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type);
+extern void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi);
+extern void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi);
+extern void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi);
+extern u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi);
-extern nphy_txgains_t wlc_phy_get_tx_gain_nphy(phy_info_t *pi);
-extern int wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
+extern struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi);
+extern int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi,
+ struct nphy_txgains target_gain,
bool full, bool m);
-extern int wlc_phy_cal_rxiq_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
+extern int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi,
+ struct nphy_txgains target_gain,
u8 type, bool d);
-extern void wlc_phy_txpwr_index_nphy(phy_info_t *pi, u8 core_mask,
+extern void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask,
s8 txpwrindex, bool res);
-extern void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core, u8 rssi_type);
-extern int wlc_phy_poll_rssi_nphy(phy_info_t *pi, u8 rssi_type,
+extern void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core, u8 rssi_type);
+extern int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type,
s32 *rssi_buf, u8 nsamps);
-extern void wlc_phy_rssi_cal_nphy(phy_info_t *pi);
-extern int wlc_phy_aci_scan_nphy(phy_info_t *pi);
-extern void wlc_phy_cal_txgainctrl_nphy(phy_info_t *pi, s32 dBm_targetpower,
- bool debug);
-extern int wlc_phy_tx_tone_nphy(phy_info_t *pi, u32 f_kHz, u16 max_val,
+extern void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi);
+extern int wlc_phy_aci_scan_nphy(struct brcms_phy *pi);
+extern void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi,
+ s32 dBm_targetpower, bool debug);
+extern int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val,
u8 mode, u8, bool);
-extern void wlc_phy_stopplayback_nphy(phy_info_t *pi);
-extern void wlc_phy_est_tonepwr_nphy(phy_info_t *pi, s32 *qdBm_pwrbuf,
+extern void wlc_phy_stopplayback_nphy(struct brcms_phy *pi);
+extern void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf,
u8 num_samps);
-extern void wlc_phy_radio205x_vcocal_nphy(phy_info_t *pi);
+extern void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi);
-extern int wlc_phy_rssi_compute_nphy(phy_info_t *pi, wlc_d11rxhdr_t *wlc_rxh);
+extern int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi,
+ struct d11rxhdr *rxh);
#define NPHY_TESTPATTERN_BPHY_EVM 0
#define NPHY_TESTPATTERN_BPHY_RFCS 1
-extern void wlc_phy_nphy_tkip_rifs_war(phy_info_t *pi, u8 rifs);
+extern void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs);
-void wlc_phy_get_pwrdet_offsets(phy_info_t *pi, s8 *cckoffset,
+void wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset,
s8 *ofdmoffset);
-extern s8 wlc_phy_upd_rssi_offset(phy_info_t *pi, s8 rssi,
- chanspec_t chanspec);
+extern s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi,
+ u16 chanspec);
-extern bool wlc_phy_n_txpower_ipa_ison(phy_info_t *pih);
-#endif /* _wlc_phy_int_h_ */
+extern bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pih);
+#endif /* _BRCM_PHY_INT_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
index b8864c5b7a19..a63aa99d9810 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_lcn.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
@@ -15,27 +15,21 @@
*/
#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/bitops.h>
#include <linux/delay.h>
-#include <wlc_cfg.h>
-#include <linux/pci.h>
-#include <aiutils.h>
-#include <wlc_pmu.h>
-#include <bcmnvram.h>
+#include <linux/cordic.h>
-#include <bcmdevs.h>
-#include <sbhnddma.h>
-
-#include "wlc_phy_radio.h"
-#include "wlc_phy_int.h"
-#include "wlc_phy_qmath.h"
-#include "wlc_phy_lcn.h"
-#include "wlc_phytbl_lcn.h"
+#include <pmu.h>
+#include <d11.h>
+#include <phy_shim.h>
+#include "phy_qmath.h"
+#include "phy_hal.h"
+#include "phy_radio.h"
+#include "phytbl_lcn.h"
+#include "phy_lcn.h"
#define PLL_2064_NDIV 90
-#define PLL_2064_LOW_END_VCO 3000
-#define PLL_2064_LOW_END_KVCO 27
+#define PLL_2064_LOW_END_VCO 3000
+#define PLL_2064_LOW_END_KVCO 27
#define PLL_2064_HIGH_END_VCO 4200
#define PLL_2064_HIGH_END_KVCO 68
#define PLL_2064_LOOP_BW_DOUBLER 200
@@ -46,7 +40,7 @@
#define PLL_2064_MHZ 1000000
#define PLL_2064_OPEN_LOOP_DELAY 5
-#define TEMPSENSE 1
+#define TEMPSENSE 1
#define VBATSENSE 2
#define NOISE_IF_UPD_CHK_INTERVAL 1
@@ -58,10 +52,10 @@
#define NOISE_IF_CHK 1
#define NOISE_IF_ON 2
-#define PAPD_BLANKING_PROFILE 3
+#define PAPD_BLANKING_PROFILE 3
#define PAPD2LUT 0
-#define PAPD_CORR_NORM 0
-#define PAPD_BLANKING_THRESHOLD 0
+#define PAPD_CORR_NORM 0
+#define PAPD_BLANKING_THRESHOLD 0
#define PAPD_STOP_AFTER_LAST_UPDATE 0
#define LCN_TARGET_PWR 60
@@ -75,6 +69,14 @@
#define LCN_TEMPSENSE_OFFSET 80812
#define LCN_TEMPSENSE_DEN 2647
+#define LCN_BW_LMT 200
+#define LCN_CUR_LMT 1250
+#define LCN_MULT 1
+#define LCN_VCO_DIV 30
+#define LCN_OFFSET 680
+#define LCN_FACT 490
+#define LCN_CUR_DIV 2640
+
#define LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT \
(0 + 8)
#define LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK \
@@ -87,15 +89,15 @@
#define wlc_lcnphy_enable_tx_gain_override(pi) \
wlc_lcnphy_set_tx_gain_override(pi, true)
-#define wlc_lcnphy_disable_tx_gain_override(pi) \
+#define wlc_lcnphy_disable_tx_gain_override(pi) \
wlc_lcnphy_set_tx_gain_override(pi, false)
#define wlc_lcnphy_iqcal_active(pi) \
(read_phy_reg((pi), 0x451) & \
- ((0x1 << 15) | (0x1 << 14)))
+ ((0x1 << 15) | (0x1 << 14)))
#define txpwrctrl_off(pi) (0x7 != ((read_phy_reg(pi, 0x4a4) & 0xE000) >> 13))
-#define wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) \
+#define wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) \
(pi->temppwrctrl_capable)
#define wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) \
(pi->hwpwrctrl_capable)
@@ -116,9 +118,9 @@
#define LCNPHY_TBL_ID_SAMPLEPLAY 0x15
#define LCNPHY_TBL_ID_SAMPLEPLAY1 0x16
-#define LCNPHY_TX_PWR_CTRL_RATE_OFFSET 832
-#define LCNPHY_TX_PWR_CTRL_MAC_OFFSET 128
-#define LCNPHY_TX_PWR_CTRL_GAIN_OFFSET 192
+#define LCNPHY_TX_PWR_CTRL_RATE_OFFSET 832
+#define LCNPHY_TX_PWR_CTRL_MAC_OFFSET 128
+#define LCNPHY_TX_PWR_CTRL_GAIN_OFFSET 192
#define LCNPHY_TX_PWR_CTRL_IQ_OFFSET 320
#define LCNPHY_TX_PWR_CTRL_LO_OFFSET 448
#define LCNPHY_TX_PWR_CTRL_PWR_OFFSET 576
@@ -140,64 +142,59 @@
#define LCNPHY_ACI_DETECT_TIMEOUT 2
#define LCNPHY_ACI_START_DELAY 0
-#define wlc_lcnphy_tx_gain_override_enabled(pi) \
+#define wlc_lcnphy_tx_gain_override_enabled(pi) \
(0 != (read_phy_reg((pi), 0x43b) & (0x1 << 6)))
#define wlc_lcnphy_total_tx_frames(pi) \
- wlapi_bmac_read_shm((pi)->sh->physhim, M_UCODE_MACSTAT + offsetof(macstat_t, txallfrm))
+ wlapi_bmac_read_shm((pi)->sh->physhim, M_UCODE_MACSTAT + \
+ offsetof(struct macstat, txallfrm))
-typedef struct {
+struct lcnphy_txgains {
u16 gm_gain;
u16 pga_gain;
u16 pad_gain;
u16 dac_gain;
-} lcnphy_txgains_t;
+};
-typedef enum {
+enum lcnphy_cal_mode {
LCNPHY_CAL_FULL,
LCNPHY_CAL_RECAL,
LCNPHY_CAL_CURRECAL,
LCNPHY_CAL_DIGCAL,
LCNPHY_CAL_GCTRL
-} lcnphy_cal_mode_t;
-
-typedef struct {
- lcnphy_txgains_t gains;
- bool useindex;
- u8 index;
-} lcnphy_txcalgains_t;
+};
-typedef struct {
+struct lcnphy_rx_iqcomp {
u8 chan;
s16 a;
s16 b;
-} lcnphy_rx_iqcomp_t;
+};
-typedef struct {
+struct lcnphy_spb_tone {
s16 re;
s16 im;
-} lcnphy_spb_tone_t;
+};
-typedef struct {
+struct lcnphy_unsign16_struct {
u16 re;
u16 im;
-} lcnphy_unsign16_struct;
+};
-typedef struct {
+struct lcnphy_iq_est {
u32 iq_prod;
u32 i_pwr;
u32 q_pwr;
-} lcnphy_iq_est_t;
+};
-typedef struct {
+struct lcnphy_sfo_cfg {
u16 ptcentreTs20;
u16 ptcentreFactor;
-} lcnphy_sfo_cfg_t;
+};
-typedef enum {
+enum lcnphy_papd_cal_type {
LCNPHY_PAPD_CAL_CW,
LCNPHY_PAPD_CAL_OFDM
-} lcnphy_papd_cal_type_t;
+};
typedef u16 iqcal_gain_params_lcnphy[9];
@@ -211,10 +208,10 @@ static const iqcal_gain_params_lcnphy *tbl_iqcal_gainparams_lcnphy[1] = {
static const u16 iqcal_gainparams_numgains_lcnphy[1] = {
sizeof(tbl_iqcal_gainparams_lcnphy_2G) /
- sizeof(*tbl_iqcal_gainparams_lcnphy_2G),
+ sizeof(*tbl_iqcal_gainparams_lcnphy_2G),
};
-static const lcnphy_sfo_cfg_t lcnphy_sfo_cfg[] = {
+static const struct lcnphy_sfo_cfg lcnphy_sfo_cfg[] = {
{965, 1087},
{967, 1085},
{969, 1082},
@@ -280,7 +277,7 @@ u16 lcnphy_iqcal_ir_gainladder[] = {
};
static const
-lcnphy_spb_tone_t lcnphy_spb_tone_3750[] = {
+struct lcnphy_spb_tone lcnphy_spb_tone_3750[] = {
{88, 0},
{73, 49},
{34, 81},
@@ -373,7 +370,7 @@ u16 rxiq_cal_rf_reg[11] = {
};
static const
-lcnphy_rx_iqcomp_t lcnphy_rx_iqcomp_table_rev0[] = {
+struct lcnphy_rx_iqcomp lcnphy_rx_iqcomp_table_rev0[] = {
{1, 0, 0},
{2, 0, 0},
{3, 0, 0},
@@ -548,13 +545,7 @@ static const s8 lcnphy_gain_index_offset_for_rssi[] = {
-2
};
-extern const u8 spur_tbl_rev0[];
-extern const u32 dot11lcnphytbl_rx_gain_info_sz_rev1;
-extern const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_rev1[];
-extern const dot11lcnphytbl_info_t dot11lcn_sw_ctrl_tbl_info_4313_bt_epa;
-extern const dot11lcnphytbl_info_t dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250;
-
-typedef struct _chan_info_2064_lcnphy {
+struct chan_info_2064_lcnphy {
uint chan;
uint freq;
u8 logen_buftune;
@@ -565,9 +556,9 @@ typedef struct _chan_info_2064_lcnphy {
u8 pa_rxrf_lna1_freq_tune;
u8 pa_rxrf_lna2_freq_tune;
u8 rxrf_rxrf_spare1;
-} chan_info_2064_lcnphy_t;
+};
-static chan_info_2064_lcnphy_t chan_info_2064_lcnphy[] = {
+static const struct chan_info_2064_lcnphy chan_info_2064_lcnphy[] = {
{1, 2412, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80},
{2, 2417, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80},
{3, 2422, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80},
@@ -584,7 +575,7 @@ static chan_info_2064_lcnphy_t chan_info_2064_lcnphy[] = {
{14, 2484, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80},
};
-lcnphy_radio_regs_t lcnphy_radio_regs_2064[] = {
+static const struct lcnphy_radio_regs lcnphy_radio_regs_2064[] = {
{0x00, 0, 0, 0, 0},
{0x01, 0x64, 0x64, 0, 0},
{0x02, 0x20, 0x20, 0, 0},
@@ -896,9 +887,8 @@ lcnphy_radio_regs_t lcnphy_radio_regs_2064[] = {
#define LCNPHY_NUM_DIG_FILT_COEFFS 16
#define LCNPHY_NUM_TX_DIG_FILTERS_CCK 13
-u16
- LCNPHY_txdigfiltcoeffs_cck[LCNPHY_NUM_TX_DIG_FILTERS_CCK]
- [LCNPHY_NUM_DIG_FILT_COEFFS + 1] = {
+static const u16 LCNPHY_txdigfiltcoeffs_cck[LCNPHY_NUM_TX_DIG_FILTERS_CCK]
+ [LCNPHY_NUM_DIG_FILT_COEFFS + 1] = {
{0, 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778, 1582, 64,
128, 64,},
{1, 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608, 1863, 93,
@@ -928,9 +918,8 @@ u16
};
#define LCNPHY_NUM_TX_DIG_FILTERS_OFDM 3
-u16
- LCNPHY_txdigfiltcoeffs_ofdm[LCNPHY_NUM_TX_DIG_FILTERS_OFDM]
- [LCNPHY_NUM_DIG_FILT_COEFFS + 1] = {
+static const u16 LCNPHY_txdigfiltcoeffs_ofdm[LCNPHY_NUM_TX_DIG_FILTERS_OFDM]
+ [LCNPHY_NUM_DIG_FILT_COEFFS + 1] = {
{0, 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0,
0x278, 0xfea0, 0x80, 0x100, 0x80,},
{1, 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50,
@@ -941,119 +930,66 @@ u16
#define wlc_lcnphy_set_start_tx_pwr_idx(pi, idx) \
mod_phy_reg(pi, 0x4a4, \
- (0x1ff << 0), \
- (u16)(idx) << 0)
+ (0x1ff << 0), \
+ (u16)(idx) << 0)
#define wlc_lcnphy_set_tx_pwr_npt(pi, npt) \
mod_phy_reg(pi, 0x4a5, \
- (0x7 << 8), \
- (u16)(npt) << 8)
+ (0x7 << 8), \
+ (u16)(npt) << 8)
#define wlc_lcnphy_get_tx_pwr_ctrl(pi) \
(read_phy_reg((pi), 0x4a4) & \
- ((0x1 << 15) | \
- (0x1 << 14) | \
- (0x1 << 13)))
+ ((0x1 << 15) | \
+ (0x1 << 14) | \
+ (0x1 << 13)))
#define wlc_lcnphy_get_tx_pwr_npt(pi) \
((read_phy_reg(pi, 0x4a5) & \
- (0x7 << 8)) >> \
- 8)
+ (0x7 << 8)) >> \
+ 8)
#define wlc_lcnphy_get_current_tx_pwr_idx_if_pwrctrl_on(pi) \
(read_phy_reg(pi, 0x473) & 0x1ff)
#define wlc_lcnphy_get_target_tx_pwr(pi) \
((read_phy_reg(pi, 0x4a7) & \
- (0xff << 0)) >> \
- 0)
+ (0xff << 0)) >> \
+ 0)
#define wlc_lcnphy_set_target_tx_pwr(pi, target) \
mod_phy_reg(pi, 0x4a7, \
- (0xff << 0), \
- (u16)(target) << 0)
+ (0xff << 0), \
+ (u16)(target) << 0)
-#define wlc_radio_2064_rcal_done(pi) (0 != (read_radio_reg(pi, RADIO_2064_REG05C) & 0x20))
-#define tempsense_done(pi) (0x8000 == (read_phy_reg(pi, 0x476) & 0x8000))
+#define wlc_radio_2064_rcal_done(pi) \
+ (0 != (read_radio_reg(pi, RADIO_2064_REG05C) & 0x20))
+
+#define tempsense_done(pi) \
+ (0x8000 == (read_phy_reg(pi, 0x476) & 0x8000))
+
+#define LCNPHY_IQLOCC_READ(val) \
+ ((u8)(-(s8)(((val) & 0xf0) >> 4) + (s8)((val) & 0x0f)))
-#define LCNPHY_IQLOCC_READ(val) ((u8)(-(s8)(((val) & 0xf0) >> 4) + (s8)((val) & 0x0f)))
#define FIXED_TXPWR 78
#define LCNPHY_TEMPSENSE(val) ((s16)((val > 255) ? (val - 512) : val))
-static u32 wlc_lcnphy_qdiv_roundup(u32 divident, u32 divisor,
- u8 precision);
-static void wlc_lcnphy_set_rx_gain_by_distribution(phy_info_t *pi,
- u16 ext_lna, u16 trsw,
- u16 biq2, u16 biq1,
- u16 tia, u16 lna2,
- u16 lna1);
-static void wlc_lcnphy_clear_tx_power_offsets(phy_info_t *pi);
-static void wlc_lcnphy_set_pa_gain(phy_info_t *pi, u16 gain);
-static void wlc_lcnphy_set_trsw_override(phy_info_t *pi, bool tx, bool rx);
-static void wlc_lcnphy_set_bbmult(phy_info_t *pi, u8 m0);
-static u8 wlc_lcnphy_get_bbmult(phy_info_t *pi);
-static void wlc_lcnphy_get_tx_gain(phy_info_t *pi, lcnphy_txgains_t *gains);
-static void wlc_lcnphy_set_tx_gain_override(phy_info_t *pi, bool bEnable);
-static void wlc_lcnphy_toggle_afe_pwdn(phy_info_t *pi);
-static void wlc_lcnphy_rx_gain_override_enable(phy_info_t *pi, bool enable);
-static void wlc_lcnphy_set_tx_gain(phy_info_t *pi,
- lcnphy_txgains_t *target_gains);
-static bool wlc_lcnphy_rx_iq_est(phy_info_t *pi, u16 num_samps,
- u8 wait_time, lcnphy_iq_est_t *iq_est);
-static bool wlc_lcnphy_calc_rx_iq_comp(phy_info_t *pi, u16 num_samps);
-static u16 wlc_lcnphy_get_pa_gain(phy_info_t *pi);
-static void wlc_lcnphy_afe_clk_init(phy_info_t *pi, u8 mode);
-extern void wlc_lcnphy_tx_pwr_ctrl_init(wlc_phy_t *ppi);
-static void wlc_lcnphy_radio_2064_channel_tune_4313(phy_info_t *pi,
- u8 channel);
-
-static void wlc_lcnphy_load_tx_gain_table(phy_info_t *pi,
- const lcnphy_tx_gain_tbl_entry *g);
-
-static void wlc_lcnphy_samp_cap(phy_info_t *pi, int clip_detect_algo,
- u16 thresh, s16 *ptr, int mode);
-static int wlc_lcnphy_calc_floor(s16 coeff, int type);
-static void wlc_lcnphy_tx_iqlo_loopback(phy_info_t *pi,
- u16 *values_to_save);
-static void wlc_lcnphy_tx_iqlo_loopback_cleanup(phy_info_t *pi,
- u16 *values_to_save);
-static void wlc_lcnphy_set_cc(phy_info_t *pi, int cal_type, s16 coeff_x,
- s16 coeff_y);
-static lcnphy_unsign16_struct wlc_lcnphy_get_cc(phy_info_t *pi, int cal_type);
-static void wlc_lcnphy_a1(phy_info_t *pi, int cal_type,
- int num_levels, int step_size_lg2);
-static void wlc_lcnphy_tx_iqlo_soft_cal_full(phy_info_t *pi);
-
-static void wlc_lcnphy_set_chanspec_tweaks(phy_info_t *pi,
- chanspec_t chanspec);
-static void wlc_lcnphy_agc_temp_init(phy_info_t *pi);
-static void wlc_lcnphy_temp_adj(phy_info_t *pi);
-static void wlc_lcnphy_clear_papd_comptable(phy_info_t *pi);
-static void wlc_lcnphy_baseband_init(phy_info_t *pi);
-static void wlc_lcnphy_radio_init(phy_info_t *pi);
-static void wlc_lcnphy_rc_cal(phy_info_t *pi);
-static void wlc_lcnphy_rcal(phy_info_t *pi);
-static void wlc_lcnphy_txrx_spur_avoidance_mode(phy_info_t *pi, bool enable);
-static int wlc_lcnphy_load_tx_iir_filter(phy_info_t *pi, bool is_ofdm,
- s16 filt_type);
-static void wlc_lcnphy_set_rx_iq_comp(phy_info_t *pi, u16 a, u16 b);
-
-void wlc_lcnphy_write_table(phy_info_t *pi, const phytbl_info_t *pti)
+void wlc_lcnphy_write_table(struct brcms_phy *pi, const struct phytbl_info *pti)
{
wlc_phy_write_table(pi, pti, 0x455, 0x457, 0x456);
}
-void wlc_lcnphy_read_table(phy_info_t *pi, phytbl_info_t *pti)
+void wlc_lcnphy_read_table(struct brcms_phy *pi, struct phytbl_info *pti)
{
wlc_phy_read_table(pi, pti, 0x455, 0x457, 0x456);
}
static void
-wlc_lcnphy_common_read_table(phy_info_t *pi, u32 tbl_id,
- const void *tbl_ptr, u32 tbl_len,
+wlc_lcnphy_common_read_table(struct brcms_phy *pi, u32 tbl_id,
+ const u16 *tbl_ptr, u32 tbl_len,
u32 tbl_width, u32 tbl_offset)
{
- phytbl_info_t tab;
+ struct phytbl_info tab;
tab.tbl_id = tbl_id;
tab.tbl_ptr = tbl_ptr;
tab.tbl_len = tbl_len;
@@ -1063,12 +999,12 @@ wlc_lcnphy_common_read_table(phy_info_t *pi, u32 tbl_id,
}
static void
-wlc_lcnphy_common_write_table(phy_info_t *pi, u32 tbl_id,
- const void *tbl_ptr, u32 tbl_len,
+wlc_lcnphy_common_write_table(struct brcms_phy *pi, u32 tbl_id,
+ const u16 *tbl_ptr, u32 tbl_len,
u32 tbl_width, u32 tbl_offset)
{
- phytbl_info_t tab;
+ struct phytbl_info tab;
tab.tbl_id = tbl_id;
tab.tbl_ptr = tbl_ptr;
tab.tbl_len = tbl_len;
@@ -1108,12 +1044,12 @@ static int wlc_lcnphy_calc_floor(s16 coeff_x, int type)
int k;
k = 0;
if (type == 0) {
- if (coeff_x < 0) {
+ if (coeff_x < 0)
k = (coeff_x - 1) / 2;
- } else {
+ else
k = coeff_x / 2;
- }
}
+
if (type == 1) {
if ((coeff_x + 1) < 0)
k = (coeff_x) / 2;
@@ -1123,32 +1059,458 @@ static int wlc_lcnphy_calc_floor(s16 coeff_x, int type)
return k;
}
-s8 wlc_lcnphy_get_current_tx_pwr_idx(phy_info_t *pi)
+static void
+wlc_lcnphy_get_tx_gain(struct brcms_phy *pi, struct lcnphy_txgains *gains)
{
- s8 index;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ u16 dac_gain, rfgain0, rfgain1;
+
+ dac_gain = read_phy_reg(pi, 0x439) >> 0;
+ gains->dac_gain = (dac_gain & 0x380) >> 7;
+
+ rfgain0 = (read_phy_reg(pi, 0x4b5) & (0xffff << 0)) >> 0;
+ rfgain1 = (read_phy_reg(pi, 0x4fb) & (0x7fff << 0)) >> 0;
+
+ gains->gm_gain = rfgain0 & 0xff;
+ gains->pga_gain = (rfgain0 >> 8) & 0xff;
+ gains->pad_gain = rfgain1 & 0xff;
+}
+
+
+static void wlc_lcnphy_set_dac_gain(struct brcms_phy *pi, u16 dac_gain)
+{
+ u16 dac_ctrl;
+
+ dac_ctrl = (read_phy_reg(pi, 0x439) >> 0);
+ dac_ctrl = dac_ctrl & 0xc7f;
+ dac_ctrl = dac_ctrl | (dac_gain << 7);
+ mod_phy_reg(pi, 0x439, (0xfff << 0), (dac_ctrl) << 0);
- if (txpwrctrl_off(pi))
- index = pi_lcn->lcnphy_current_index;
- else if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi))
- index =
- (s8) (wlc_lcnphy_get_current_tx_pwr_idx_if_pwrctrl_on(pi)
- / 2);
- else
- index = pi_lcn->lcnphy_current_index;
- return index;
}
-static u32 wlc_lcnphy_measure_digital_power(phy_info_t *pi, u16 nsamples)
+static void wlc_lcnphy_set_tx_gain_override(struct brcms_phy *pi, bool bEnable)
{
- lcnphy_iq_est_t iq_est = { 0, 0, 0 };
+ u16 bit = bEnable ? 1 : 0;
+
+ mod_phy_reg(pi, 0x4b0, (0x1 << 7), bit << 7);
+
+ mod_phy_reg(pi, 0x4b0, (0x1 << 14), bit << 14);
+
+ mod_phy_reg(pi, 0x43b, (0x1 << 6), bit << 6);
+}
+
+static void
+wlc_lcnphy_rx_gain_override_enable(struct brcms_phy *pi, bool enable)
+{
+ u16 ebit = enable ? 1 : 0;
+
+ mod_phy_reg(pi, 0x4b0, (0x1 << 8), ebit << 8);
+
+ mod_phy_reg(pi, 0x44c, (0x1 << 0), ebit << 0);
+
+ if (LCNREV_LT(pi->pubpi.phy_rev, 2)) {
+ mod_phy_reg(pi, 0x44c, (0x1 << 4), ebit << 4);
+ mod_phy_reg(pi, 0x44c, (0x1 << 6), ebit << 6);
+ mod_phy_reg(pi, 0x4b0, (0x1 << 5), ebit << 5);
+ mod_phy_reg(pi, 0x4b0, (0x1 << 6), ebit << 6);
+ } else {
+ mod_phy_reg(pi, 0x4b0, (0x1 << 12), ebit << 12);
+ mod_phy_reg(pi, 0x4b0, (0x1 << 13), ebit << 13);
+ mod_phy_reg(pi, 0x4b0, (0x1 << 5), ebit << 5);
+ }
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ mod_phy_reg(pi, 0x4b0, (0x1 << 10), ebit << 10);
+ mod_phy_reg(pi, 0x4e5, (0x1 << 3), ebit << 3);
+ }
+}
+
+static void
+wlc_lcnphy_set_rx_gain_by_distribution(struct brcms_phy *pi,
+ u16 trsw,
+ u16 ext_lna,
+ u16 biq2,
+ u16 biq1,
+ u16 tia, u16 lna2, u16 lna1)
+{
+ u16 gain0_15, gain16_19;
+
+ gain16_19 = biq2 & 0xf;
+ gain0_15 = ((biq1 & 0xf) << 12) |
+ ((tia & 0xf) << 8) |
+ ((lna2 & 0x3) << 6) |
+ ((lna2 &
+ 0x3) << 4) | ((lna1 & 0x3) << 2) | ((lna1 & 0x3) << 0);
+
+ mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0);
+ mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0);
+ mod_phy_reg(pi, 0x4b1, (0x3 << 11), lna1 << 11);
+
+ if (LCNREV_LT(pi->pubpi.phy_rev, 2)) {
+ mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9);
+ mod_phy_reg(pi, 0x4b1, (0x1 << 10), ext_lna << 10);
+ } else {
+ mod_phy_reg(pi, 0x4b1, (0x1 << 10), 0 << 10);
+
+ mod_phy_reg(pi, 0x4b1, (0x1 << 15), 0 << 15);
+
+ mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9);
+ }
+
+ mod_phy_reg(pi, 0x44d, (0x1 << 0), (!trsw) << 0);
+
+}
+
+static void wlc_lcnphy_set_trsw_override(struct brcms_phy *pi, bool tx, bool rx)
+{
+
+ mod_phy_reg(pi, 0x44d,
+ (0x1 << 1) |
+ (0x1 << 0), (tx ? (0x1 << 1) : 0) | (rx ? (0x1 << 0) : 0));
+
+ or_phy_reg(pi, 0x44c, (0x1 << 1) | (0x1 << 0));
+}
+
+static void wlc_lcnphy_clear_trsw_override(struct brcms_phy *pi)
+{
+
+ and_phy_reg(pi, 0x44c, (u16) ~((0x1 << 1) | (0x1 << 0)));
+}
+
+static void wlc_lcnphy_set_rx_iq_comp(struct brcms_phy *pi, u16 a, u16 b)
+{
+ mod_phy_reg(pi, 0x645, (0x3ff << 0), (a) << 0);
+
+ mod_phy_reg(pi, 0x646, (0x3ff << 0), (b) << 0);
+
+ mod_phy_reg(pi, 0x647, (0x3ff << 0), (a) << 0);
+
+ mod_phy_reg(pi, 0x648, (0x3ff << 0), (b) << 0);
+
+ mod_phy_reg(pi, 0x649, (0x3ff << 0), (a) << 0);
+
+ mod_phy_reg(pi, 0x64a, (0x3ff << 0), (b) << 0);
+
+}
+
+static bool
+wlc_lcnphy_rx_iq_est(struct brcms_phy *pi,
+ u16 num_samps,
+ u8 wait_time, struct lcnphy_iq_est *iq_est)
+{
+ int wait_count = 0;
+ bool result = true;
+ u8 phybw40;
+ phybw40 = CHSPEC_IS40(pi->radio_chanspec);
+
+ mod_phy_reg(pi, 0x6da, (0x1 << 5), (1) << 5);
+
+ mod_phy_reg(pi, 0x410, (0x1 << 3), (0) << 3);
+
+ mod_phy_reg(pi, 0x482, (0xffff << 0), (num_samps) << 0);
+
+ mod_phy_reg(pi, 0x481, (0xff << 0), ((u16) wait_time) << 0);
+
+ mod_phy_reg(pi, 0x481, (0x1 << 8), (0) << 8);
+
+ mod_phy_reg(pi, 0x481, (0x1 << 9), (1) << 9);
+
+ while (read_phy_reg(pi, 0x481) & (0x1 << 9)) {
+
+ if (wait_count > (10 * 500)) {
+ result = false;
+ goto cleanup;
+ }
+ udelay(100);
+ wait_count++;
+ }
+
+ iq_est->iq_prod = ((u32) read_phy_reg(pi, 0x483) << 16) |
+ (u32) read_phy_reg(pi, 0x484);
+ iq_est->i_pwr = ((u32) read_phy_reg(pi, 0x485) << 16) |
+ (u32) read_phy_reg(pi, 0x486);
+ iq_est->q_pwr = ((u32) read_phy_reg(pi, 0x487) << 16) |
+ (u32) read_phy_reg(pi, 0x488);
+
+cleanup:
+ mod_phy_reg(pi, 0x410, (0x1 << 3), (1) << 3);
+
+ mod_phy_reg(pi, 0x6da, (0x1 << 5), (0) << 5);
+
+ return result;
+}
+
+static bool wlc_lcnphy_calc_rx_iq_comp(struct brcms_phy *pi, u16 num_samps)
+{
+#define LCNPHY_MIN_RXIQ_PWR 2
+ bool result;
+ u16 a0_new, b0_new;
+ struct lcnphy_iq_est iq_est = { 0, 0, 0 };
+ s32 a, b, temp;
+ s16 iq_nbits, qq_nbits, arsh, brsh;
+ s32 iq;
+ u32 ii, qq;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
+
+ a0_new = ((read_phy_reg(pi, 0x645) & (0x3ff << 0)) >> 0);
+ b0_new = ((read_phy_reg(pi, 0x646) & (0x3ff << 0)) >> 0);
+ mod_phy_reg(pi, 0x6d1, (0x1 << 2), (0) << 2);
+
+ mod_phy_reg(pi, 0x64b, (0x1 << 6), (1) << 6);
+
+ wlc_lcnphy_set_rx_iq_comp(pi, 0, 0);
+
+ result = wlc_lcnphy_rx_iq_est(pi, num_samps, 32, &iq_est);
+ if (!result)
+ goto cleanup;
+
+ iq = (s32) iq_est.iq_prod;
+ ii = iq_est.i_pwr;
+ qq = iq_est.q_pwr;
+
+ if ((ii + qq) < LCNPHY_MIN_RXIQ_PWR) {
+ result = false;
+ goto cleanup;
+ }
+
+ iq_nbits = wlc_phy_nbits(iq);
+ qq_nbits = wlc_phy_nbits(qq);
+
+ arsh = 10 - (30 - iq_nbits);
+ if (arsh >= 0) {
+ a = (-(iq << (30 - iq_nbits)) + (ii >> (1 + arsh)));
+ temp = (s32) (ii >> arsh);
+ if (temp == 0)
+ return false;
+ } else {
+ a = (-(iq << (30 - iq_nbits)) + (ii << (-1 - arsh)));
+ temp = (s32) (ii << -arsh);
+ if (temp == 0)
+ return false;
+ }
+ a /= temp;
+ brsh = qq_nbits - 31 + 20;
+ if (brsh >= 0) {
+ b = (qq << (31 - qq_nbits));
+ temp = (s32) (ii >> brsh);
+ if (temp == 0)
+ return false;
+ } else {
+ b = (qq << (31 - qq_nbits));
+ temp = (s32) (ii << -brsh);
+ if (temp == 0)
+ return false;
+ }
+ b /= temp;
+ b -= a * a;
+ b = (s32) int_sqrt((unsigned long) b);
+ b -= (1 << 10);
+ a0_new = (u16) (a & 0x3ff);
+ b0_new = (u16) (b & 0x3ff);
+cleanup:
+
+ wlc_lcnphy_set_rx_iq_comp(pi, a0_new, b0_new);
+
+ mod_phy_reg(pi, 0x64b, (0x1 << 0), (1) << 0);
+
+ mod_phy_reg(pi, 0x64b, (0x1 << 3), (1) << 3);
+
+ pi_lcn->lcnphy_cal_results.rxiqcal_coeff_a0 = a0_new;
+ pi_lcn->lcnphy_cal_results.rxiqcal_coeff_b0 = b0_new;
+
+ return result;
+}
+
+static u32 wlc_lcnphy_measure_digital_power(struct brcms_phy *pi, u16 nsamples)
+{
+ struct lcnphy_iq_est iq_est = { 0, 0, 0 };
if (!wlc_lcnphy_rx_iq_est(pi, nsamples, 32, &iq_est))
return 0;
return (iq_est.i_pwr + iq_est.q_pwr) / nsamples;
}
-void wlc_lcnphy_crsuprs(phy_info_t *pi, int channel)
+static bool
+wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
+ const struct lcnphy_rx_iqcomp *iqcomp,
+ int iqcomp_sz, bool tx_switch, bool rx_switch, int module,
+ int tx_gain_idx)
+{
+ struct lcnphy_txgains old_gains;
+ u16 tx_pwr_ctrl;
+ u8 tx_gain_index_old = 0;
+ bool result = false, tx_gain_override_old = false;
+ u16 i, Core1TxControl_old, RFOverride0_old,
+ RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old,
+ rfoverride3_old, rfoverride3val_old, rfoverride4_old,
+ rfoverride4val_old, afectrlovr_old, afectrlovrval_old;
+ int tia_gain;
+ u32 received_power, rx_pwr_threshold;
+ u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl;
+ u16 values_to_save[11];
+ s16 *ptr;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
+
+ ptr = kmalloc(sizeof(s16) * 131, GFP_ATOMIC);
+ if (NULL == ptr)
+ return false;
+ if (module == 2) {
+ while (iqcomp_sz--) {
+ if (iqcomp[iqcomp_sz].chan ==
+ CHSPEC_CHANNEL(pi->radio_chanspec)) {
+ wlc_lcnphy_set_rx_iq_comp(pi,
+ (u16)
+ iqcomp[iqcomp_sz].a,
+ (u16)
+ iqcomp[iqcomp_sz].b);
+ result = true;
+ break;
+ }
+ }
+ goto cal_done;
+ }
+
+ if (module == 1) {
+
+ tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
+ wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
+
+ for (i = 0; i < 11; i++)
+ values_to_save[i] =
+ read_radio_reg(pi, rxiq_cal_rf_reg[i]);
+ Core1TxControl_old = read_phy_reg(pi, 0x631);
+
+ or_phy_reg(pi, 0x631, 0x0015);
+
+ RFOverride0_old = read_phy_reg(pi, 0x44c);
+ RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
+ rfoverride2_old = read_phy_reg(pi, 0x4b0);
+ rfoverride2val_old = read_phy_reg(pi, 0x4b1);
+ rfoverride3_old = read_phy_reg(pi, 0x4f9);
+ rfoverride3val_old = read_phy_reg(pi, 0x4fa);
+ rfoverride4_old = read_phy_reg(pi, 0x938);
+ rfoverride4val_old = read_phy_reg(pi, 0x939);
+ afectrlovr_old = read_phy_reg(pi, 0x43b);
+ afectrlovrval_old = read_phy_reg(pi, 0x43c);
+ old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
+ old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
+
+ tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
+ if (tx_gain_override_old) {
+ wlc_lcnphy_get_tx_gain(pi, &old_gains);
+ tx_gain_index_old = pi_lcn->lcnphy_current_index;
+ }
+
+ wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
+
+ mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
+ mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
+
+ mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
+ mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
+
+ write_radio_reg(pi, RADIO_2064_REG116, 0x06);
+ write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
+ write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
+ write_radio_reg(pi, RADIO_2064_REG098, 0x03);
+ write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
+ mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
+ write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
+ write_radio_reg(pi, RADIO_2064_REG114, 0x01);
+ write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
+ write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
+
+ mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
+ mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
+ mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
+ mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
+ mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
+ mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
+ mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
+ mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
+ mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
+ mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
+
+ mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
+ mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
+
+ wlc_lcnphy_start_tx_tone(pi, 2000, 120, 0);
+ write_phy_reg(pi, 0x6da, 0xffff);
+ or_phy_reg(pi, 0x6db, 0x3);
+ wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
+ wlc_lcnphy_rx_gain_override_enable(pi, true);
+
+ tia_gain = 8;
+ rx_pwr_threshold = 950;
+ while (tia_gain > 0) {
+ tia_gain -= 1;
+ wlc_lcnphy_set_rx_gain_by_distribution(pi,
+ 0, 0, 2, 2,
+ (u16)
+ tia_gain, 1, 0);
+ udelay(500);
+
+ received_power =
+ wlc_lcnphy_measure_digital_power(pi, 2000);
+ if (received_power < rx_pwr_threshold)
+ break;
+ }
+ result = wlc_lcnphy_calc_rx_iq_comp(pi, 0xffff);
+
+ wlc_lcnphy_stop_tx_tone(pi);
+
+ write_phy_reg(pi, 0x631, Core1TxControl_old);
+
+ write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
+ write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
+ write_phy_reg(pi, 0x4b0, rfoverride2_old);
+ write_phy_reg(pi, 0x4b1, rfoverride2val_old);
+ write_phy_reg(pi, 0x4f9, rfoverride3_old);
+ write_phy_reg(pi, 0x4fa, rfoverride3val_old);
+ write_phy_reg(pi, 0x938, rfoverride4_old);
+ write_phy_reg(pi, 0x939, rfoverride4val_old);
+ write_phy_reg(pi, 0x43b, afectrlovr_old);
+ write_phy_reg(pi, 0x43c, afectrlovrval_old);
+ write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
+ write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
+
+ wlc_lcnphy_clear_trsw_override(pi);
+
+ mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
+
+ for (i = 0; i < 11; i++)
+ write_radio_reg(pi, rxiq_cal_rf_reg[i],
+ values_to_save[i]);
+
+ if (tx_gain_override_old)
+ wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
+ else
+ wlc_lcnphy_disable_tx_gain_override(pi);
+
+ wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
+ wlc_lcnphy_rx_gain_override_enable(pi, false);
+ }
+
+cal_done:
+ kfree(ptr);
+ return result;
+}
+
+s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi)
+{
+ s8 index;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
+
+ if (txpwrctrl_off(pi))
+ index = pi_lcn->lcnphy_current_index;
+ else if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi))
+ index = (s8) (wlc_lcnphy_get_current_tx_pwr_idx_if_pwrctrl_on(
+ pi) / 2);
+ else
+ index = pi_lcn->lcnphy_current_index;
+ return index;
+}
+
+void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel)
{
u16 afectrlovr, afectrlovrval;
afectrlovr = read_phy_reg(pi, 0x43b);
@@ -1179,7 +1541,7 @@ void wlc_lcnphy_crsuprs(phy_info_t *pi, int channel)
}
}
-static void wlc_lcnphy_toggle_afe_pwdn(phy_info_t *pi)
+static void wlc_lcnphy_toggle_afe_pwdn(struct brcms_phy *pi)
{
u16 save_AfeCtrlOvrVal, save_AfeCtrlOvr;
@@ -1196,7 +1558,8 @@ static void wlc_lcnphy_toggle_afe_pwdn(phy_info_t *pi)
write_phy_reg(pi, 0x43b, save_AfeCtrlOvr);
}
-static void wlc_lcnphy_txrx_spur_avoidance_mode(phy_info_t *pi, bool enable)
+static void
+wlc_lcnphy_txrx_spur_avoidance_mode(struct brcms_phy *pi, bool enable)
{
if (enable) {
write_phy_reg(pi, 0x942, 0x7);
@@ -1215,93 +1578,356 @@ static void wlc_lcnphy_txrx_spur_avoidance_mode(phy_info_t *pi, bool enable)
wlapi_switch_macfreq(pi->sh->physhim, enable);
}
-void wlc_phy_chanspec_set_lcnphy(phy_info_t *pi, chanspec_t chanspec)
+static void
+wlc_lcnphy_set_chanspec_tweaks(struct brcms_phy *pi, u16 chanspec)
{
u8 channel = CHSPEC_CHANNEL(chanspec);
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
+
+ if (channel == 14)
+ mod_phy_reg(pi, 0x448, (0x3 << 8), (2) << 8);
+ else
+ mod_phy_reg(pi, 0x448, (0x3 << 8), (1) << 8);
- wlc_phy_chanspec_radio_set((wlc_phy_t *) pi, chanspec);
+ pi_lcn->lcnphy_bandedge_corr = 2;
+ if (channel == 1)
+ pi_lcn->lcnphy_bandedge_corr = 4;
- wlc_lcnphy_set_chanspec_tweaks(pi, pi->radio_chanspec);
+ if (channel == 1 || channel == 2 || channel == 3 ||
+ channel == 4 || channel == 9 ||
+ channel == 10 || channel == 11 || channel == 12) {
+ si_pmu_pllcontrol(pi->sh->sih, 0x2, 0xffffffff, 0x03000c04);
+ si_pmu_pllcontrol(pi->sh->sih, 0x3, 0xffffff, 0x0);
+ si_pmu_pllcontrol(pi->sh->sih, 0x4, 0xffffffff, 0x200005c0);
+
+ si_pmu_pllupd(pi->sh->sih);
+ write_phy_reg(pi, 0x942, 0);
+ wlc_lcnphy_txrx_spur_avoidance_mode(pi, false);
+ pi_lcn->lcnphy_spurmod = 0;
+ mod_phy_reg(pi, 0x424, (0xff << 8), (0x1b) << 8);
+
+ write_phy_reg(pi, 0x425, 0x5907);
+ } else {
+ si_pmu_pllcontrol(pi->sh->sih, 0x2, 0xffffffff, 0x03140c04);
+ si_pmu_pllcontrol(pi->sh->sih, 0x3, 0xffffff, 0x333333);
+ si_pmu_pllcontrol(pi->sh->sih, 0x4, 0xffffffff, 0x202c2820);
+
+ si_pmu_pllupd(pi->sh->sih);
+ write_phy_reg(pi, 0x942, 0);
+ wlc_lcnphy_txrx_spur_avoidance_mode(pi, true);
+
+ pi_lcn->lcnphy_spurmod = 0;
+ mod_phy_reg(pi, 0x424, (0xff << 8), (0x1f) << 8);
+
+ write_phy_reg(pi, 0x425, 0x590a);
+ }
or_phy_reg(pi, 0x44a, 0x44);
write_phy_reg(pi, 0x44a, 0x80);
+}
- if (!NORADIO_ENAB(pi->pubpi)) {
- wlc_lcnphy_radio_2064_channel_tune_4313(pi, channel);
- udelay(1000);
- }
+static void
+wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel)
+{
+ uint i;
+ const struct chan_info_2064_lcnphy *ci;
+ u8 rfpll_doubler = 0;
+ u8 pll_pwrup, pll_pwrup_ovr;
+ s32 qFxtal, qFref, qFvco, qFcal;
+ u8 d15, d16, f16, e44, e45;
+ u32 div_int, div_frac, fvco3, fpfd, fref3, fcal_div;
+ u16 loop_bw, d30, setCount;
- wlc_lcnphy_toggle_afe_pwdn(pi);
+ u8 h29, h28_ten, e30, h30_ten, cp_current;
+ u16 g30, d28;
- write_phy_reg(pi, 0x657, lcnphy_sfo_cfg[channel - 1].ptcentreTs20);
- write_phy_reg(pi, 0x658, lcnphy_sfo_cfg[channel - 1].ptcentreFactor);
+ ci = &chan_info_2064_lcnphy[0];
+ rfpll_doubler = 1;
- if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) {
- mod_phy_reg(pi, 0x448, (0x3 << 8), (2) << 8);
+ mod_radio_reg(pi, RADIO_2064_REG09D, 0x4, 0x1 << 2);
- wlc_lcnphy_load_tx_iir_filter(pi, false, 3);
+ write_radio_reg(pi, RADIO_2064_REG09E, 0xf);
+ if (!rfpll_doubler) {
+ loop_bw = PLL_2064_LOOP_BW;
+ d30 = PLL_2064_D30;
} else {
- mod_phy_reg(pi, 0x448, (0x3 << 8), (1) << 8);
+ loop_bw = PLL_2064_LOOP_BW_DOUBLER;
+ d30 = PLL_2064_D30_DOUBLER;
+ }
- wlc_lcnphy_load_tx_iir_filter(pi, false, 2);
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ for (i = 0; i < ARRAY_SIZE(chan_info_2064_lcnphy); i++)
+ if (chan_info_2064_lcnphy[i].chan == channel)
+ break;
+
+ if (i >= ARRAY_SIZE(chan_info_2064_lcnphy))
+ return;
+
+ ci = &chan_info_2064_lcnphy[i];
}
- wlc_lcnphy_load_tx_iir_filter(pi, true, 0);
+ write_radio_reg(pi, RADIO_2064_REG02A, ci->logen_buftune);
- mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3);
+ mod_radio_reg(pi, RADIO_2064_REG030, 0x3, ci->logen_rccr_tx);
+
+ mod_radio_reg(pi, RADIO_2064_REG091, 0x3, ci->txrf_mix_tune_ctrl);
+
+ mod_radio_reg(pi, RADIO_2064_REG038, 0xf, ci->pa_input_tune_g);
+
+ mod_radio_reg(pi, RADIO_2064_REG030, 0x3 << 2,
+ (ci->logen_rccr_rx) << 2);
+
+ mod_radio_reg(pi, RADIO_2064_REG05E, 0xf, ci->pa_rxrf_lna1_freq_tune);
+ mod_radio_reg(pi, RADIO_2064_REG05E, (0xf) << 4,
+ (ci->pa_rxrf_lna2_freq_tune) << 4);
+
+ write_radio_reg(pi, RADIO_2064_REG06C, ci->rxrf_rxrf_spare1);
+
+ pll_pwrup = (u8) read_radio_reg(pi, RADIO_2064_REG044);
+ pll_pwrup_ovr = (u8) read_radio_reg(pi, RADIO_2064_REG12B);
+
+ or_radio_reg(pi, RADIO_2064_REG044, 0x07);
+
+ or_radio_reg(pi, RADIO_2064_REG12B, (0x07) << 1);
+ e44 = 0;
+ e45 = 0;
+
+ fpfd = rfpll_doubler ? (pi->xtalfreq << 1) : (pi->xtalfreq);
+ if (pi->xtalfreq > 26000000)
+ e44 = 1;
+ if (pi->xtalfreq > 52000000)
+ e45 = 1;
+ if (e44 == 0)
+ fcal_div = 1;
+ else if (e45 == 0)
+ fcal_div = 2;
+ else
+ fcal_div = 4;
+ fvco3 = (ci->freq * 3);
+ fref3 = 2 * fpfd;
+
+ qFxtal = wlc_lcnphy_qdiv_roundup(pi->xtalfreq, PLL_2064_MHZ, 16);
+ qFref = wlc_lcnphy_qdiv_roundup(fpfd, PLL_2064_MHZ, 16);
+ qFcal = pi->xtalfreq * fcal_div / PLL_2064_MHZ;
+ qFvco = wlc_lcnphy_qdiv_roundup(fvco3, 2, 16);
+
+ write_radio_reg(pi, RADIO_2064_REG04F, 0x02);
+
+ d15 = (pi->xtalfreq * fcal_div * 4 / 5) / PLL_2064_MHZ - 1;
+ write_radio_reg(pi, RADIO_2064_REG052, (0x07 & (d15 >> 2)));
+ write_radio_reg(pi, RADIO_2064_REG053, (d15 & 0x3) << 5);
+
+ d16 = (qFcal * 8 / (d15 + 1)) - 1;
+ write_radio_reg(pi, RADIO_2064_REG051, d16);
+
+ f16 = ((d16 + 1) * (d15 + 1)) / qFcal;
+ setCount = f16 * 3 * (ci->freq) / 32 - 1;
+ mod_radio_reg(pi, RADIO_2064_REG053, (0x0f << 0),
+ (u8) (setCount >> 8));
+
+ or_radio_reg(pi, RADIO_2064_REG053, 0x10);
+ write_radio_reg(pi, RADIO_2064_REG054, (u8) (setCount & 0xff));
+
+ div_int = ((fvco3 * (PLL_2064_MHZ >> 4)) / fref3) << 4;
+
+ div_frac = ((fvco3 * (PLL_2064_MHZ >> 4)) % fref3) << 4;
+ while (div_frac >= fref3) {
+ div_int++;
+ div_frac -= fref3;
+ }
+ div_frac = wlc_lcnphy_qdiv_roundup(div_frac, fref3, 20);
+
+ mod_radio_reg(pi, RADIO_2064_REG045, (0x1f << 0),
+ (u8) (div_int >> 4));
+ mod_radio_reg(pi, RADIO_2064_REG046, (0x1f << 4),
+ (u8) (div_int << 4));
+ mod_radio_reg(pi, RADIO_2064_REG046, (0x0f << 0),
+ (u8) (div_frac >> 16));
+ write_radio_reg(pi, RADIO_2064_REG047, (u8) (div_frac >> 8) & 0xff);
+ write_radio_reg(pi, RADIO_2064_REG048, (u8) div_frac & 0xff);
+
+ write_radio_reg(pi, RADIO_2064_REG040, 0xfb);
+
+ write_radio_reg(pi, RADIO_2064_REG041, 0x9A);
+ write_radio_reg(pi, RADIO_2064_REG042, 0xA3);
+ write_radio_reg(pi, RADIO_2064_REG043, 0x0C);
+
+ h29 = LCN_BW_LMT / loop_bw;
+ d28 = (((PLL_2064_HIGH_END_KVCO - PLL_2064_LOW_END_KVCO) *
+ (fvco3 / 2 - PLL_2064_LOW_END_VCO)) /
+ (PLL_2064_HIGH_END_VCO - PLL_2064_LOW_END_VCO))
+ + PLL_2064_LOW_END_KVCO;
+ h28_ten = (d28 * 10) / LCN_VCO_DIV;
+ e30 = (d30 - LCN_OFFSET) / LCN_FACT;
+ g30 = LCN_OFFSET + (e30 * LCN_FACT);
+ h30_ten = (g30 * 10) / LCN_CUR_DIV;
+ cp_current = ((LCN_CUR_LMT * h29 * LCN_MULT * 100) / h28_ten) / h30_ten;
+ mod_radio_reg(pi, RADIO_2064_REG03C, 0x3f, cp_current);
+
+ if (channel >= 1 && channel <= 5)
+ write_radio_reg(pi, RADIO_2064_REG03C, 0x8);
+ else
+ write_radio_reg(pi, RADIO_2064_REG03C, 0x7);
+ write_radio_reg(pi, RADIO_2064_REG03D, 0x3);
+
+ mod_radio_reg(pi, RADIO_2064_REG044, 0x0c, 0x0c);
+ udelay(1);
+
+ wlc_2064_vco_cal(pi);
+
+ write_radio_reg(pi, RADIO_2064_REG044, pll_pwrup);
+ write_radio_reg(pi, RADIO_2064_REG12B, pll_pwrup_ovr);
+ if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
+ write_radio_reg(pi, RADIO_2064_REG038, 3);
+ write_radio_reg(pi, RADIO_2064_REG091, 7);
+ }
}
-static void wlc_lcnphy_set_dac_gain(phy_info_t *pi, u16 dac_gain)
+static int
+wlc_lcnphy_load_tx_iir_filter(struct brcms_phy *pi, bool is_ofdm, s16 filt_type)
{
- u16 dac_ctrl;
+ s16 filt_index = -1;
+ int j;
- dac_ctrl = (read_phy_reg(pi, 0x439) >> 0);
- dac_ctrl = dac_ctrl & 0xc7f;
- dac_ctrl = dac_ctrl | (dac_gain << 7);
- mod_phy_reg(pi, 0x439, (0xfff << 0), (dac_ctrl) << 0);
+ u16 addr[] = {
+ 0x910,
+ 0x91e,
+ 0x91f,
+ 0x924,
+ 0x925,
+ 0x926,
+ 0x920,
+ 0x921,
+ 0x927,
+ 0x928,
+ 0x929,
+ 0x922,
+ 0x923,
+ 0x930,
+ 0x931,
+ 0x932
+ };
+
+ u16 addr_ofdm[] = {
+ 0x90f,
+ 0x900,
+ 0x901,
+ 0x906,
+ 0x907,
+ 0x908,
+ 0x902,
+ 0x903,
+ 0x909,
+ 0x90a,
+ 0x90b,
+ 0x904,
+ 0x905,
+ 0x90c,
+ 0x90d,
+ 0x90e
+ };
+
+ if (!is_ofdm) {
+ for (j = 0; j < LCNPHY_NUM_TX_DIG_FILTERS_CCK; j++) {
+ if (filt_type == LCNPHY_txdigfiltcoeffs_cck[j][0]) {
+ filt_index = (s16) j;
+ break;
+ }
+ }
+
+ if (filt_index != -1) {
+ for (j = 0; j < LCNPHY_NUM_DIG_FILT_COEFFS; j++)
+ write_phy_reg(pi, addr[j],
+ LCNPHY_txdigfiltcoeffs_cck
+ [filt_index][j + 1]);
+ }
+ } else {
+ for (j = 0; j < LCNPHY_NUM_TX_DIG_FILTERS_OFDM; j++) {
+ if (filt_type == LCNPHY_txdigfiltcoeffs_ofdm[j][0]) {
+ filt_index = (s16) j;
+ break;
+ }
+ }
+ if (filt_index != -1) {
+ for (j = 0; j < LCNPHY_NUM_DIG_FILT_COEFFS; j++)
+ write_phy_reg(pi, addr_ofdm[j],
+ LCNPHY_txdigfiltcoeffs_ofdm
+ [filt_index][j + 1]);
+ }
+ }
+
+ return (filt_index != -1) ? 0 : -1;
}
-static void wlc_lcnphy_set_tx_gain_override(phy_info_t *pi, bool bEnable)
+void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec)
{
- u16 bit = bEnable ? 1 : 0;
+ u8 channel = CHSPEC_CHANNEL(chanspec);
- mod_phy_reg(pi, 0x4b0, (0x1 << 7), bit << 7);
+ wlc_phy_chanspec_radio_set((struct brcms_phy_pub *) pi, chanspec);
- mod_phy_reg(pi, 0x4b0, (0x1 << 14), bit << 14);
+ wlc_lcnphy_set_chanspec_tweaks(pi, pi->radio_chanspec);
+
+ or_phy_reg(pi, 0x44a, 0x44);
+ write_phy_reg(pi, 0x44a, 0x80);
+
+ wlc_lcnphy_radio_2064_channel_tune_4313(pi, channel);
+ udelay(1000);
+
+ wlc_lcnphy_toggle_afe_pwdn(pi);
+
+ write_phy_reg(pi, 0x657, lcnphy_sfo_cfg[channel - 1].ptcentreTs20);
+ write_phy_reg(pi, 0x658, lcnphy_sfo_cfg[channel - 1].ptcentreFactor);
+
+ if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) {
+ mod_phy_reg(pi, 0x448, (0x3 << 8), (2) << 8);
+
+ wlc_lcnphy_load_tx_iir_filter(pi, false, 3);
+ } else {
+ mod_phy_reg(pi, 0x448, (0x3 << 8), (1) << 8);
+
+ wlc_lcnphy_load_tx_iir_filter(pi, false, 2);
+ }
+
+ wlc_lcnphy_load_tx_iir_filter(pi, true, 0);
+
+ mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3);
- mod_phy_reg(pi, 0x43b, (0x1 << 6), bit << 6);
}
-static u16 wlc_lcnphy_get_pa_gain(phy_info_t *pi)
+static u16 wlc_lcnphy_get_pa_gain(struct brcms_phy *pi)
{
u16 pa_gain;
pa_gain = (read_phy_reg(pi, 0x4fb) &
LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK) >>
- LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT;
+ LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT;
return pa_gain;
}
-static void
-wlc_lcnphy_set_tx_gain(phy_info_t *pi, lcnphy_txgains_t *target_gains)
+static void wlc_lcnphy_set_tx_gain(struct brcms_phy *pi,
+ struct lcnphy_txgains *target_gains)
{
u16 pa_gain = wlc_lcnphy_get_pa_gain(pi);
- mod_phy_reg(pi, 0x4b5,
- (0xffff << 0),
- ((target_gains->gm_gain) | (target_gains->pga_gain << 8)) <<
- 0);
+ mod_phy_reg(
+ pi, 0x4b5,
+ (0xffff << 0),
+ ((target_gains->gm_gain) |
+ (target_gains->pga_gain << 8)) <<
+ 0);
mod_phy_reg(pi, 0x4fb,
(0x7fff << 0),
((target_gains->pad_gain) | (pa_gain << 8)) << 0);
- mod_phy_reg(pi, 0x4fc,
- (0xffff << 0),
- ((target_gains->gm_gain) | (target_gains->pga_gain << 8)) <<
- 0);
+ mod_phy_reg(
+ pi, 0x4fc,
+ (0xffff << 0),
+ ((target_gains->gm_gain) |
+ (target_gains->pga_gain << 8)) <<
+ 0);
mod_phy_reg(pi, 0x4fd,
(0x7fff << 0),
((target_gains->pad_gain) | (pa_gain << 8)) << 0);
@@ -1311,10 +1937,10 @@ wlc_lcnphy_set_tx_gain(phy_info_t *pi, lcnphy_txgains_t *target_gains)
wlc_lcnphy_enable_tx_gain_override(pi);
}
-static void wlc_lcnphy_set_bbmult(phy_info_t *pi, u8 m0)
+static void wlc_lcnphy_set_bbmult(struct brcms_phy *pi, u8 m0)
{
u16 m0m1 = (u16) m0 << 8;
- phytbl_info_t tab;
+ struct phytbl_info tab;
tab.tbl_ptr = &m0m1;
tab.tbl_len = 1;
@@ -1324,10 +1950,10 @@ static void wlc_lcnphy_set_bbmult(phy_info_t *pi, u8 m0)
wlc_lcnphy_write_table(pi, &tab);
}
-static void wlc_lcnphy_clear_tx_power_offsets(phy_info_t *pi)
+static void wlc_lcnphy_clear_tx_power_offsets(struct brcms_phy *pi)
{
u32 data_buf[64];
- phytbl_info_t tab;
+ struct phytbl_info tab;
memset(data_buf, 0, sizeof(data_buf));
@@ -1347,13 +1973,14 @@ static void wlc_lcnphy_clear_tx_power_offsets(phy_info_t *pi)
wlc_lcnphy_write_table(pi, &tab);
}
-typedef enum {
+enum lcnphy_tssi_mode {
LCNPHY_TSSI_PRE_PA,
LCNPHY_TSSI_POST_PA,
LCNPHY_TSSI_EXT
-} lcnphy_tssi_mode_t;
+};
-static void wlc_lcnphy_set_tssi_mux(phy_info_t *pi, lcnphy_tssi_mode_t pos)
+static void
+wlc_lcnphy_set_tssi_mux(struct brcms_phy *pi, enum lcnphy_tssi_mode pos)
{
mod_phy_reg(pi, 0x4d7, (0x1 << 0), (0x1) << 0);
@@ -1392,7 +2019,7 @@ static void wlc_lcnphy_set_tssi_mux(phy_info_t *pi, lcnphy_tssi_mode_t pos)
}
}
-static u16 wlc_lcnphy_rfseq_tbl_adc_pwrup(phy_info_t *pi)
+static u16 wlc_lcnphy_rfseq_tbl_adc_pwrup(struct brcms_phy *pi)
{
u16 N1, N2, N3, N4, N5, N6, N;
N1 = ((read_phy_reg(pi, 0x4a5) & (0xff << 0))
@@ -1413,13 +2040,13 @@ static u16 wlc_lcnphy_rfseq_tbl_adc_pwrup(phy_info_t *pi)
return N;
}
-static void wlc_lcnphy_pwrctrl_rssiparams(phy_info_t *pi)
+static void wlc_lcnphy_pwrctrl_rssiparams(struct brcms_phy *pi)
{
u16 auxpga_vmid, auxpga_vmid_temp, auxpga_gain_temp;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
- auxpga_vmid =
- (2 << 8) | (pi_lcn->lcnphy_rssi_vc << 4) | pi_lcn->lcnphy_rssi_vf;
+ auxpga_vmid = (2 << 8) |
+ (pi_lcn->lcnphy_rssi_vc << 4) | pi_lcn->lcnphy_rssi_vf;
auxpga_vmid_temp = (2 << 8) | (8 << 4) | 4;
auxpga_gain_temp = 2;
@@ -1457,9 +2084,9 @@ static void wlc_lcnphy_pwrctrl_rssiparams(phy_info_t *pi)
mod_radio_reg(pi, RADIO_2064_REG082, (1 << 5), (1 << 5));
}
-static void wlc_lcnphy_tssi_setup(phy_info_t *pi)
+static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
{
- phytbl_info_t tab;
+ struct phytbl_info tab;
u32 rfseq, ind;
tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
@@ -1545,10 +2172,9 @@ static void wlc_lcnphy_tssi_setup(phy_info_t *pi)
mod_radio_reg(pi, RADIO_2064_REG005, 0x8, 1 << 3);
- if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) {
+ if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
mod_phy_reg(pi, 0x4d7,
(0x1 << 3) | (0x7 << 12), 0 << 3 | 2 << 12);
- }
rfseq = wlc_lcnphy_rfseq_tbl_adc_pwrup(pi);
tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
@@ -1571,10 +2197,10 @@ static void wlc_lcnphy_tssi_setup(phy_info_t *pi)
wlc_lcnphy_pwrctrl_rssiparams(pi);
}
-void wlc_lcnphy_tx_pwr_update_npt(phy_info_t *pi)
+void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi)
{
u16 tx_cnt, tx_total, npt;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
tx_total = wlc_lcnphy_total_tx_frames(pi);
tx_cnt = tx_total - pi_lcn->lcnphy_tssi_tx_cnt;
@@ -1601,9 +2227,9 @@ s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1)
return p;
}
-static void wlc_lcnphy_txpower_reset_npt(phy_info_t *pi)
+static void wlc_lcnphy_txpower_reset_npt(struct brcms_phy *pi)
{
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
return;
@@ -1611,18 +2237,18 @@ static void wlc_lcnphy_txpower_reset_npt(phy_info_t *pi)
pi_lcn->lcnphy_tssi_npt = LCNPHY_TX_PWR_CTRL_START_NPT;
}
-void wlc_lcnphy_txpower_recalc_target(phy_info_t *pi)
+void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi)
{
- phytbl_info_t tab;
- u32 rate_table[WLC_NUM_RATES_CCK + WLC_NUM_RATES_OFDM +
- WLC_NUM_RATES_MCS_1_STREAM];
+ struct phytbl_info tab;
+ u32 rate_table[BRCMS_NUM_RATES_CCK + BRCMS_NUM_RATES_OFDM +
+ BRCMS_NUM_RATES_MCS_1_STREAM];
uint i, j;
if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
return;
for (i = 0, j = 0; i < ARRAY_SIZE(rate_table); i++, j++) {
- if (i == WLC_NUM_RATES_CCK + WLC_NUM_RATES_OFDM)
+ if (i == BRCMS_NUM_RATES_CCK + BRCMS_NUM_RATES_OFDM)
j = TXP_FIRST_MCS_20_SISO;
rate_table[i] = (u32) ((s32) (-pi->tx_power_offset[j]));
@@ -1642,13 +2268,13 @@ void wlc_lcnphy_txpower_recalc_target(phy_info_t *pi)
}
}
-static void wlc_lcnphy_set_tx_pwr_soft_ctrl(phy_info_t *pi, s8 index)
+static void wlc_lcnphy_set_tx_pwr_soft_ctrl(struct brcms_phy *pi, s8 index)
{
u32 cck_offset[4] = { 22, 22, 22, 22 };
u32 ofdm_offset, reg_offset_cck;
int i;
u16 index2;
- phytbl_info_t tab;
+ struct phytbl_info tab;
if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi))
return;
@@ -1695,47 +2321,41 @@ static void wlc_lcnphy_set_tx_pwr_soft_ctrl(phy_info_t *pi, s8 index)
}
-static s8 wlc_lcnphy_tempcompensated_txpwrctrl(phy_info_t *pi)
+static s8 wlc_lcnphy_tempcompensated_txpwrctrl(struct brcms_phy *pi)
{
s8 index, delta_brd, delta_temp, new_index, tempcorrx;
s16 manp, meas_temp, temp_diff;
bool neg = 0;
u16 temp;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi))
return pi_lcn->lcnphy_current_index;
index = FIXED_TXPWR;
- if (NORADIO_ENAB(pi->pubpi))
+ if (pi_lcn->lcnphy_tempsense_slope == 0)
return index;
- if (pi_lcn->lcnphy_tempsense_slope == 0) {
- return index;
- }
temp = (u16) wlc_lcnphy_tempsense(pi, 0);
meas_temp = LCNPHY_TEMPSENSE(temp);
- if (pi->tx_power_min != 0) {
+ if (pi->tx_power_min != 0)
delta_brd = (pi_lcn->lcnphy_measPower - pi->tx_power_min);
- } else {
+ else
delta_brd = 0;
- }
manp = LCNPHY_TEMPSENSE(pi_lcn->lcnphy_rawtempsense);
temp_diff = manp - meas_temp;
if (temp_diff < 0) {
-
neg = 1;
-
temp_diff = -temp_diff;
}
delta_temp = (s8) wlc_lcnphy_qdiv_roundup((u32) (temp_diff * 192),
- (u32) (pi_lcn->
- lcnphy_tempsense_slope
- * 10), 0);
+ (u32) (pi_lcn->
+ lcnphy_tempsense_slope
+ * 10), 0);
if (neg)
delta_temp = -delta_temp;
@@ -1749,18 +2369,19 @@ static s8 wlc_lcnphy_tempcompensated_txpwrctrl(phy_info_t *pi)
if (LCNREV_IS(pi->pubpi.phy_rev, 1))
tempcorrx = 4;
new_index =
- index + delta_brd + delta_temp - pi_lcn->lcnphy_bandedge_corr;
+ index + delta_brd + delta_temp - pi_lcn->lcnphy_bandedge_corr;
new_index += tempcorrx;
if (LCNREV_IS(pi->pubpi.phy_rev, 1))
index = 127;
- if (new_index < 0 || new_index > 126) {
+
+ if (new_index < 0 || new_index > 126)
return index;
- }
+
return new_index;
}
-static u16 wlc_lcnphy_set_tx_pwr_ctrl_mode(phy_info_t *pi, u16 mode)
+static u16 wlc_lcnphy_set_tx_pwr_ctrl_mode(struct brcms_phy *pi, u16 mode)
{
u16 current_mode = mode;
@@ -1773,11 +2394,11 @@ static u16 wlc_lcnphy_set_tx_pwr_ctrl_mode(phy_info_t *pi, u16 mode)
return current_mode;
}
-void wlc_lcnphy_set_tx_pwr_ctrl(phy_info_t *pi, u16 mode)
+void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode)
{
u16 old_mode = wlc_lcnphy_get_tx_pwr_ctrl(pi);
s8 index;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
mode = wlc_lcnphy_set_tx_pwr_ctrl_mode(pi, mode);
old_mode = wlc_lcnphy_set_tx_pwr_ctrl_mode(pi, old_mode);
@@ -1806,7 +2427,7 @@ void wlc_lcnphy_set_tx_pwr_ctrl(phy_info_t *pi, u16 mode)
mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 0);
pi_lcn->lcnphy_tssi_tx_cnt =
- wlc_lcnphy_total_tx_frames(pi);
+ wlc_lcnphy_total_tx_frames(pi);
wlc_lcnphy_disable_tx_gain_override(pi);
pi_lcn->lcnphy_tx_power_idx_override = -1;
@@ -1819,12 +2440,106 @@ void wlc_lcnphy_set_tx_pwr_ctrl(phy_info_t *pi, u16 mode)
index = wlc_lcnphy_tempcompensated_txpwrctrl(pi);
wlc_lcnphy_set_tx_pwr_soft_ctrl(pi, index);
pi_lcn->lcnphy_current_index = (s8)
- ((read_phy_reg(pi, 0x4a9) & 0xFF) / 2);
+ ((read_phy_reg(pi,
+ 0x4a9) &
+ 0xFF) / 2);
}
}
}
-static bool wlc_lcnphy_iqcal_wait(phy_info_t *pi)
+static void
+wlc_lcnphy_tx_iqlo_loopback(struct brcms_phy *pi, u16 *values_to_save)
+{
+ u16 vmid;
+ int i;
+ for (i = 0; i < 20; i++)
+ values_to_save[i] =
+ read_radio_reg(pi, iqlo_loopback_rf_regs[i]);
+
+ mod_phy_reg(pi, 0x44c, (0x1 << 12), 1 << 12);
+ mod_phy_reg(pi, 0x44d, (0x1 << 14), 1 << 14);
+
+ mod_phy_reg(pi, 0x44c, (0x1 << 11), 1 << 11);
+ mod_phy_reg(pi, 0x44d, (0x1 << 13), 0 << 13);
+
+ mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
+ mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
+
+ mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
+ mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
+
+ if (LCNREV_IS(pi->pubpi.phy_rev, 2))
+ and_radio_reg(pi, RADIO_2064_REG03A, 0xFD);
+ else
+ and_radio_reg(pi, RADIO_2064_REG03A, 0xF9);
+ or_radio_reg(pi, RADIO_2064_REG11A, 0x1);
+
+ or_radio_reg(pi, RADIO_2064_REG036, 0x01);
+ or_radio_reg(pi, RADIO_2064_REG11A, 0x18);
+ udelay(20);
+
+ if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
+ if (CHSPEC_IS5G(pi->radio_chanspec))
+ mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0);
+ else
+ or_radio_reg(pi, RADIO_2064_REG03A, 1);
+ } else {
+ if (CHSPEC_IS5G(pi->radio_chanspec))
+ mod_radio_reg(pi, RADIO_2064_REG03A, 3, 1);
+ else
+ or_radio_reg(pi, RADIO_2064_REG03A, 0x3);
+ }
+
+ udelay(20);
+
+ write_radio_reg(pi, RADIO_2064_REG025, 0xF);
+ if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
+ if (CHSPEC_IS5G(pi->radio_chanspec))
+ mod_radio_reg(pi, RADIO_2064_REG028, 0xF, 0x4);
+ else
+ mod_radio_reg(pi, RADIO_2064_REG028, 0xF, 0x6);
+ } else {
+ if (CHSPEC_IS5G(pi->radio_chanspec))
+ mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0x4 << 1);
+ else
+ mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0x6 << 1);
+ }
+
+ udelay(20);
+
+ write_radio_reg(pi, RADIO_2064_REG005, 0x8);
+ or_radio_reg(pi, RADIO_2064_REG112, 0x80);
+ udelay(20);
+
+ or_radio_reg(pi, RADIO_2064_REG0FF, 0x10);
+ or_radio_reg(pi, RADIO_2064_REG11F, 0x44);
+ udelay(20);
+
+ or_radio_reg(pi, RADIO_2064_REG00B, 0x7);
+ or_radio_reg(pi, RADIO_2064_REG113, 0x10);
+ udelay(20);
+
+ write_radio_reg(pi, RADIO_2064_REG007, 0x1);
+ udelay(20);
+
+ vmid = 0x2A6;
+ mod_radio_reg(pi, RADIO_2064_REG0FC, 0x3 << 0, (vmid >> 8) & 0x3);
+ write_radio_reg(pi, RADIO_2064_REG0FD, (vmid & 0xff));
+ or_radio_reg(pi, RADIO_2064_REG11F, 0x44);
+ udelay(20);
+
+ or_radio_reg(pi, RADIO_2064_REG0FF, 0x10);
+ udelay(20);
+ write_radio_reg(pi, RADIO_2064_REG012, 0x02);
+ or_radio_reg(pi, RADIO_2064_REG112, 0x06);
+ write_radio_reg(pi, RADIO_2064_REG036, 0x11);
+ write_radio_reg(pi, RADIO_2064_REG059, 0xcc);
+ write_radio_reg(pi, RADIO_2064_REG05C, 0x2e);
+ write_radio_reg(pi, RADIO_2064_REG078, 0xd7);
+ write_radio_reg(pi, RADIO_2064_REG092, 0x15);
+}
+
+static bool wlc_lcnphy_iqcal_wait(struct brcms_phy *pi)
{
uint delay_count = 0;
@@ -1840,49 +2555,62 @@ static bool wlc_lcnphy_iqcal_wait(phy_info_t *pi)
}
static void
-wlc_lcnphy_tx_iqlo_cal(phy_info_t *pi,
- lcnphy_txgains_t *target_gains,
- lcnphy_cal_mode_t cal_mode, bool keep_tone)
+wlc_lcnphy_tx_iqlo_loopback_cleanup(struct brcms_phy *pi, u16 *values_to_save)
{
+ int i;
- lcnphy_txgains_t cal_gains, temp_gains;
+ and_phy_reg(pi, 0x44c, 0x0 >> 11);
+
+ and_phy_reg(pi, 0x43b, 0xC);
+
+ for (i = 0; i < 20; i++)
+ write_radio_reg(pi, iqlo_loopback_rf_regs[i],
+ values_to_save[i]);
+}
+
+static void
+wlc_lcnphy_tx_iqlo_cal(struct brcms_phy *pi,
+ struct lcnphy_txgains *target_gains,
+ enum lcnphy_cal_mode cal_mode, bool keep_tone)
+{
+
+ struct lcnphy_txgains cal_gains, temp_gains;
u16 hash;
u8 band_idx;
int j;
u16 ncorr_override[5];
u16 syst_coeffs[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
- };
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};
u16 commands_fullcal[] = {
- 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234 };
+ 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234
+ };
u16 commands_recal[] = {
- 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234 };
+ 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234
+ };
u16 command_nums_fullcal[] = {
- 0x7a97, 0x7a97, 0x7a97, 0x7a87, 0x7a87, 0x7b97 };
+ 0x7a97, 0x7a97, 0x7a97, 0x7a87, 0x7a87, 0x7b97
+ };
u16 command_nums_recal[] = {
- 0x7a97, 0x7a97, 0x7a97, 0x7a87, 0x7a87, 0x7b97 };
+ 0x7a97, 0x7a97, 0x7a97, 0x7a87, 0x7a87, 0x7b97
+ };
u16 *command_nums = command_nums_fullcal;
u16 *start_coeffs = NULL, *cal_cmds = NULL, cal_type, diq_start;
u16 tx_pwr_ctrl_old, save_txpwrctrlrfctrl2;
u16 save_sslpnCalibClkEnCtrl, save_sslpnRxFeClkEnCtrl;
bool tx_gain_override_old;
- lcnphy_txgains_t old_gains;
+ struct lcnphy_txgains old_gains;
uint i, n_cal_cmds = 0, n_cal_start = 0;
u16 *values_to_save;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
-
- if (NORADIO_ENAB(pi->pubpi))
- return;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
values_to_save = kmalloc(sizeof(u16) * 20, GFP_ATOMIC);
- if (NULL == values_to_save) {
+ if (NULL == values_to_save)
return;
- }
save_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
save_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
@@ -1941,7 +2669,7 @@ wlc_lcnphy_tx_iqlo_cal(phy_info_t *pi,
}
hash = (target_gains->gm_gain << 8) |
- (target_gains->pga_gain << 4) | (target_gains->pad_gain);
+ (target_gains->pga_gain << 4) | (target_gains->pad_gain);
band_idx = (CHSPEC_IS5G(pi->radio_chanspec) ? 1 : 0);
@@ -1950,11 +2678,11 @@ wlc_lcnphy_tx_iqlo_cal(phy_info_t *pi,
for (j = 0; j < iqcal_gainparams_numgains_lcnphy[band_idx]; j++) {
if (hash == tbl_iqcal_gainparams_lcnphy[band_idx][j][0]) {
cal_gains.gm_gain =
- tbl_iqcal_gainparams_lcnphy[band_idx][j][1];
+ tbl_iqcal_gainparams_lcnphy[band_idx][j][1];
cal_gains.pga_gain =
- tbl_iqcal_gainparams_lcnphy[band_idx][j][2];
+ tbl_iqcal_gainparams_lcnphy[band_idx][j][2];
cal_gains.pad_gain =
- tbl_iqcal_gainparams_lcnphy[band_idx][j][3];
+ tbl_iqcal_gainparams_lcnphy[band_idx][j][3];
memcpy(ncorr_override,
&tbl_iqcal_gainparams_lcnphy[band_idx][j][3],
sizeof(ncorr_override));
@@ -1968,14 +2696,14 @@ wlc_lcnphy_tx_iqlo_cal(phy_info_t *pi,
write_phy_reg(pi, 0x93d, 0xc0);
wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL,
- (const void *)
lcnphy_iqcal_loft_gainladder,
ARRAY_SIZE(lcnphy_iqcal_loft_gainladder),
16, 0);
wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL,
- (const void *)lcnphy_iqcal_ir_gainladder,
- ARRAY_SIZE(lcnphy_iqcal_ir_gainladder), 16,
+ lcnphy_iqcal_ir_gainladder,
+ ARRAY_SIZE(
+ lcnphy_iqcal_ir_gainladder), 16,
32);
if (pi->phy_tx_tone_freq) {
@@ -1999,13 +2727,12 @@ wlc_lcnphy_tx_iqlo_cal(phy_info_t *pi,
command_num = command_nums[i];
if (ncorr_override[cal_type])
command_num =
- ncorr_override[cal_type] << 8 | (command_num &
- 0xff);
+ ncorr_override[cal_type] << 8 | (command_num &
+ 0xff);
write_phy_reg(pi, 0x452, command_num);
if ((cal_type == 3) || (cal_type == 4)) {
-
wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL,
&diq_start, 1, 16, 69);
@@ -2015,10 +2742,8 @@ wlc_lcnphy_tx_iqlo_cal(phy_info_t *pi,
write_phy_reg(pi, 0x451, cal_cmds[i]);
- if (!wlc_lcnphy_iqcal_wait(pi)) {
-
+ if (!wlc_lcnphy_iqcal_wait(pi))
goto cleanup;
- }
wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL,
best_coeffs,
@@ -2027,16 +2752,15 @@ wlc_lcnphy_tx_iqlo_cal(phy_info_t *pi,
best_coeffs,
ARRAY_SIZE(best_coeffs), 16, 64);
- if ((cal_type == 3) || (cal_type == 4)) {
+ if ((cal_type == 3) || (cal_type == 4))
wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL,
&diq_start, 1, 16, 69);
- }
wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL,
pi_lcn->lcnphy_cal_results.
txiqlocal_bestcoeffs,
ARRAY_SIZE(pi_lcn->
- lcnphy_cal_results.
- txiqlocal_bestcoeffs),
+ lcnphy_cal_results.
+ txiqlocal_bestcoeffs),
16, 96);
}
@@ -2044,7 +2768,7 @@ wlc_lcnphy_tx_iqlo_cal(phy_info_t *pi,
pi_lcn->lcnphy_cal_results.
txiqlocal_bestcoeffs,
ARRAY_SIZE(pi_lcn->lcnphy_cal_results.
- txiqlocal_bestcoeffs), 16, 96);
+ txiqlocal_bestcoeffs), 16, 96);
pi_lcn->lcnphy_cal_results.txiqlocal_bestcoeffs_valid = true;
wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL,
@@ -2055,7 +2779,7 @@ wlc_lcnphy_tx_iqlo_cal(phy_info_t *pi,
&pi_lcn->lcnphy_cal_results.
txiqlocal_bestcoeffs[5], 2, 16, 85);
- cleanup:
+cleanup:
wlc_lcnphy_tx_iqlo_loopback_cleanup(pi, values_to_save);
kfree(values_to_save);
@@ -2075,24 +2799,24 @@ wlc_lcnphy_tx_iqlo_cal(phy_info_t *pi,
}
-static void wlc_lcnphy_idle_tssi_est(wlc_phy_t *ppi)
+static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
{
bool suspend, tx_gain_override_old;
- lcnphy_txgains_t old_gains;
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct lcnphy_txgains old_gains;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
u16 idleTssi, idleTssi0_2C, idleTssi0_OB, idleTssi0_regvalue_OB,
idleTssi0_regvalue_2C;
u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
u16 SAVE_lpfgain = read_radio_reg(pi, RADIO_2064_REG112);
u16 SAVE_jtag_bb_afe_switch =
- read_radio_reg(pi, RADIO_2064_REG007) & 1;
+ read_radio_reg(pi, RADIO_2064_REG007) & 1;
u16 SAVE_jtag_auxpga = read_radio_reg(pi, RADIO_2064_REG0FF) & 0x10;
u16 SAVE_iqadc_aux_en = read_radio_reg(pi, RADIO_2064_REG11F) & 4;
idleTssi = read_phy_reg(pi, 0x4ab);
suspend =
- (0 ==
- (R_REG(&((phy_info_t *) pi)->regs->maccontrol) &
- MCTL_EN_MAC));
+ (0 ==
+ (R_REG(&((struct brcms_phy *) pi)->regs->maccontrol) &
+ MCTL_EN_MAC));
if (!suspend)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
@@ -2141,20 +2865,20 @@ static void wlc_lcnphy_idle_tssi_est(wlc_phy_t *ppi)
wlapi_enable_mac(pi->sh->physhim);
}
-static void wlc_lcnphy_vbat_temp_sense_setup(phy_info_t *pi, u8 mode)
+static void wlc_lcnphy_vbat_temp_sense_setup(struct brcms_phy *pi, u8 mode)
{
bool suspend;
u16 save_txpwrCtrlEn;
u8 auxpga_vmidcourse, auxpga_vmidfine, auxpga_gain;
u16 auxpga_vmid;
- phytbl_info_t tab;
+ struct phytbl_info tab;
u32 val;
u8 save_reg007, save_reg0FF, save_reg11F, save_reg005, save_reg025,
- save_reg112;
+ save_reg112;
u16 values_to_save[14];
s8 index;
int i;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
udelay(999);
save_reg007 = (u8) read_radio_reg(pi, RADIO_2064_REG007);
@@ -2166,8 +2890,7 @@ static void wlc_lcnphy_vbat_temp_sense_setup(phy_info_t *pi, u8 mode)
for (i = 0; i < 14; i++)
values_to_save[i] = read_phy_reg(pi, tempsense_phy_regs[i]);
- suspend =
- (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ suspend = (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
if (!suspend)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
save_txpwrCtrlEn = read_radio_reg(pi, 0x4a4);
@@ -2250,7 +2973,7 @@ static void wlc_lcnphy_vbat_temp_sense_setup(phy_info_t *pi, u8 mode)
auxpga_gain = 2;
}
auxpga_vmid =
- (u16) ((2 << 8) | (auxpga_vmidcourse << 4) | auxpga_vmidfine);
+ (u16) ((2 << 8) | (auxpga_vmidcourse << 4) | auxpga_vmidfine);
mod_phy_reg(pi, 0x4d8, (0x1 << 0), (1) << 0);
mod_phy_reg(pi, 0x4d8, (0x3ff << 2), (auxpga_vmid) << 2);
@@ -2283,28 +3006,21 @@ static void wlc_lcnphy_vbat_temp_sense_setup(phy_info_t *pi, u8 mode)
udelay(999);
}
-void WLBANDINITFN(wlc_lcnphy_tx_pwr_ctrl_init) (wlc_phy_t *ppi)
+static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi)
{
- lcnphy_txgains_t tx_gains;
+ struct lcnphy_txgains tx_gains;
u8 bbmult;
- phytbl_info_t tab;
+ struct phytbl_info tab;
s32 a1, b0, b1;
s32 tssi, pwr, maxtargetpwr, mintargetpwr;
bool suspend;
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
suspend =
- (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
if (!suspend)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
- if (NORADIO_ENAB(pi->pubpi)) {
- wlc_lcnphy_set_bbmult(pi, 0x30);
- if (!suspend)
- wlapi_enable_mac(pi->sh->physhim);
- return;
- }
-
if (!pi->hwpwrctrl_capable) {
if (CHSPEC_IS2G(pi->radio_chanspec)) {
tx_gains.gm_gain = 4;
@@ -2361,10 +3077,10 @@ void WLBANDINITFN(wlc_lcnphy_tx_pwr_ctrl_init) (wlc_phy_t *ppi)
wlapi_enable_mac(pi->sh->physhim);
}
-static u8 wlc_lcnphy_get_bbmult(phy_info_t *pi)
+static u8 wlc_lcnphy_get_bbmult(struct brcms_phy *pi)
{
u16 m0m1;
- phytbl_info_t tab;
+ struct phytbl_info tab;
tab.tbl_ptr = &m0m1;
tab.tbl_len = 1;
@@ -2376,7 +3092,7 @@ static u8 wlc_lcnphy_get_bbmult(phy_info_t *pi)
return (u8) ((m0m1 & 0xff00) >> 8);
}
-static void wlc_lcnphy_set_pa_gain(phy_info_t *pi, u16 gain)
+static void wlc_lcnphy_set_pa_gain(struct brcms_phy *pi, u16 gain)
{
mod_phy_reg(pi, 0x4fb,
LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK,
@@ -2387,7 +3103,7 @@ static void wlc_lcnphy_set_pa_gain(phy_info_t *pi, u16 gain)
}
void
-wlc_lcnphy_get_radio_loft(phy_info_t *pi,
+wlc_lcnphy_get_radio_loft(struct brcms_phy *pi,
u8 *ei0, u8 *eq0, u8 *fi0, u8 *fq0)
{
*ei0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG089));
@@ -2396,28 +3112,9 @@ wlc_lcnphy_get_radio_loft(phy_info_t *pi,
*fq0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG08C));
}
-static void wlc_lcnphy_get_tx_gain(phy_info_t *pi, lcnphy_txgains_t *gains)
+void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b)
{
- u16 dac_gain;
-
- dac_gain = read_phy_reg(pi, 0x439) >> 0;
- gains->dac_gain = (dac_gain & 0x380) >> 7;
-
- {
- u16 rfgain0, rfgain1;
-
- rfgain0 = (read_phy_reg(pi, 0x4b5) & (0xffff << 0)) >> 0;
- rfgain1 = (read_phy_reg(pi, 0x4fb) & (0x7fff << 0)) >> 0;
-
- gains->gm_gain = rfgain0 & 0xff;
- gains->pga_gain = (rfgain0 >> 8) & 0xff;
- gains->pad_gain = rfgain1 & 0xff;
- }
-}
-
-void wlc_lcnphy_set_tx_iqcc(phy_info_t *pi, u16 a, u16 b)
-{
- phytbl_info_t tab;
+ struct phytbl_info tab;
u16 iqcc[2];
iqcc[0] = a;
@@ -2431,9 +3128,9 @@ void wlc_lcnphy_set_tx_iqcc(phy_info_t *pi, u16 a, u16 b)
wlc_lcnphy_write_table(pi, &tab);
}
-void wlc_lcnphy_set_tx_locc(phy_info_t *pi, u16 didq)
+void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq)
{
- phytbl_info_t tab;
+ struct phytbl_info tab;
tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL;
tab.tbl_width = 16;
@@ -2443,14 +3140,14 @@ void wlc_lcnphy_set_tx_locc(phy_info_t *pi, u16 didq)
wlc_lcnphy_write_table(pi, &tab);
}
-void wlc_lcnphy_set_tx_pwr_by_index(phy_info_t *pi, int index)
+void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index)
{
- phytbl_info_t tab;
+ struct phytbl_info tab;
u16 a, b;
u8 bb_mult;
u32 bbmultiqcomp, txgain, locoeffs, rfpower;
- lcnphy_txgains_t gains;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct lcnphy_txgains gains;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
pi_lcn->lcnphy_tx_power_idx_override = (s8) index;
pi_lcn->lcnphy_current_index = (u8) index;
@@ -2502,20 +3199,10 @@ void wlc_lcnphy_set_tx_pwr_by_index(phy_info_t *pi, int index)
}
}
-static void wlc_lcnphy_set_trsw_override(phy_info_t *pi, bool tx, bool rx)
-{
-
- mod_phy_reg(pi, 0x44d,
- (0x1 << 1) |
- (0x1 << 0), (tx ? (0x1 << 1) : 0) | (rx ? (0x1 << 0) : 0));
-
- or_phy_reg(pi, 0x44c, (0x1 << 1) | (0x1 << 0));
-}
-
-static void wlc_lcnphy_clear_papd_comptable(phy_info_t *pi)
+static void wlc_lcnphy_clear_papd_comptable(struct brcms_phy *pi)
{
u32 j;
- phytbl_info_t tab;
+ struct phytbl_info tab;
u32 temp_offset[128];
tab.tbl_ptr = temp_offset;
tab.tbl_len = 128;
@@ -2531,67 +3218,7 @@ static void wlc_lcnphy_clear_papd_comptable(phy_info_t *pi)
return;
}
-static void
-wlc_lcnphy_set_rx_gain_by_distribution(phy_info_t *pi,
- u16 trsw,
- u16 ext_lna,
- u16 biq2,
- u16 biq1,
- u16 tia, u16 lna2, u16 lna1)
-{
- u16 gain0_15, gain16_19;
-
- gain16_19 = biq2 & 0xf;
- gain0_15 = ((biq1 & 0xf) << 12) |
- ((tia & 0xf) << 8) |
- ((lna2 & 0x3) << 6) |
- ((lna2 & 0x3) << 4) | ((lna1 & 0x3) << 2) | ((lna1 & 0x3) << 0);
-
- mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0);
- mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0);
- mod_phy_reg(pi, 0x4b1, (0x3 << 11), lna1 << 11);
-
- if (LCNREV_LT(pi->pubpi.phy_rev, 2)) {
- mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9);
- mod_phy_reg(pi, 0x4b1, (0x1 << 10), ext_lna << 10);
- } else {
- mod_phy_reg(pi, 0x4b1, (0x1 << 10), 0 << 10);
-
- mod_phy_reg(pi, 0x4b1, (0x1 << 15), 0 << 15);
-
- mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9);
- }
-
- mod_phy_reg(pi, 0x44d, (0x1 << 0), (!trsw) << 0);
-
-}
-
-static void wlc_lcnphy_rx_gain_override_enable(phy_info_t *pi, bool enable)
-{
- u16 ebit = enable ? 1 : 0;
-
- mod_phy_reg(pi, 0x4b0, (0x1 << 8), ebit << 8);
-
- mod_phy_reg(pi, 0x44c, (0x1 << 0), ebit << 0);
-
- if (LCNREV_LT(pi->pubpi.phy_rev, 2)) {
- mod_phy_reg(pi, 0x44c, (0x1 << 4), ebit << 4);
- mod_phy_reg(pi, 0x44c, (0x1 << 6), ebit << 6);
- mod_phy_reg(pi, 0x4b0, (0x1 << 5), ebit << 5);
- mod_phy_reg(pi, 0x4b0, (0x1 << 6), ebit << 6);
- } else {
- mod_phy_reg(pi, 0x4b0, (0x1 << 12), ebit << 12);
- mod_phy_reg(pi, 0x4b0, (0x1 << 13), ebit << 13);
- mod_phy_reg(pi, 0x4b0, (0x1 << 5), ebit << 5);
- }
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- mod_phy_reg(pi, 0x4b0, (0x1 << 10), ebit << 10);
- mod_phy_reg(pi, 0x4e5, (0x1 << 3), ebit << 3);
- }
-}
-
-void wlc_lcnphy_tx_pu(phy_info_t *pi, bool bEnable)
+void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable)
{
if (!bEnable) {
@@ -2601,9 +3228,9 @@ void wlc_lcnphy_tx_pu(phy_info_t *pi, bool bEnable)
and_phy_reg(pi, 0x44c,
~(u16) ((0x1 << 3) |
- (0x1 << 5) |
- (0x1 << 12) |
- (0x1 << 0) | (0x1 << 1) | (0x1 << 2)));
+ (0x1 << 5) |
+ (0x1 << 12) |
+ (0x1 << 0) | (0x1 << 1) | (0x1 << 2)));
and_phy_reg(pi, 0x44d,
~(u16) ((0x1 << 3) | (0x1 << 5) | (0x1 << 14)));
@@ -2669,7 +3296,7 @@ void wlc_lcnphy_tx_pu(phy_info_t *pi, bool bEnable)
}
static void
-wlc_lcnphy_run_samples(phy_info_t *pi,
+wlc_lcnphy_run_samples(struct brcms_phy *pi,
u16 num_samps,
u16 num_loops, u16 wait, bool iqcalmode)
{
@@ -2695,7 +3322,7 @@ wlc_lcnphy_run_samples(phy_info_t *pi,
or_radio_reg(pi, RADIO_2064_REG112, 0x6);
}
-void wlc_lcnphy_deaf_mode(phy_info_t *pi, bool mode)
+void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode)
{
u8 phybw40;
@@ -2713,25 +3340,26 @@ void wlc_lcnphy_deaf_mode(phy_info_t *pi, bool mode)
mod_phy_reg((pi), 0x410,
(0x1 << 6) |
(0x1 << 5),
- ((CHSPEC_IS2G(pi->radio_chanspec)) ? (!mode) : 0) <<
+ ((CHSPEC_IS2G(
+ pi->radio_chanspec)) ? (!mode) : 0) <<
6 | (!mode) << 5);
mod_phy_reg(pi, 0x410, (0x1 << 7), (mode) << 7);
}
}
void
-wlc_lcnphy_start_tx_tone(phy_info_t *pi, s32 f_kHz, u16 max_val,
+wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val,
bool iqcalmode)
{
u8 phy_bw;
u16 num_samps, t, k;
u32 bw;
- fixed theta = 0, rot = 0;
- cs32 tone_samp;
+ s32 theta = 0, rot = 0;
+ struct cordic_iq tone_samp;
u32 data_buf[64];
u16 i_samp, q_samp;
- phytbl_info_t tab;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct phytbl_info tab;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
pi->phy_tx_tone_freq = f_kHz;
@@ -2749,18 +3377,18 @@ wlc_lcnphy_start_tx_tone(phy_info_t *pi, s32 f_kHz, u16 max_val,
k = 1;
do {
bw = phy_bw * 1000 * k;
- num_samps = bw / ABS(f_kHz);
+ num_samps = bw / abs(f_kHz);
k++;
- } while ((num_samps * (u32) (ABS(f_kHz))) != bw);
+ } while ((num_samps * (u32) (abs(f_kHz))) != bw);
} else
num_samps = 2;
- rot = FIXED((f_kHz * 36) / phy_bw) / 100;
+ rot = ((f_kHz * 36) / phy_bw) / 100;
theta = 0;
for (t = 0; t < num_samps; t++) {
- wlc_phy_cordic(theta, &tone_samp);
+ tone_samp = cordic_calc_iq(theta);
theta += rot;
@@ -2783,10 +3411,10 @@ wlc_lcnphy_start_tx_tone(phy_info_t *pi, s32 f_kHz, u16 max_val,
wlc_lcnphy_run_samples(pi, num_samps, 0xffff, 0, iqcalmode);
}
-void wlc_lcnphy_stop_tx_tone(phy_info_t *pi)
+void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi)
{
s16 playback_status;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
pi->phy_tx_tone_freq = 0;
if (pi_lcn->lcnphy_spurmod) {
@@ -2814,16 +3442,365 @@ void wlc_lcnphy_stop_tx_tone(phy_info_t *pi)
wlc_lcnphy_deaf_mode(pi, false);
}
-static void wlc_lcnphy_clear_trsw_override(phy_info_t *pi)
+static void
+wlc_lcnphy_set_cc(struct brcms_phy *pi, int cal_type, s16 coeff_x, s16 coeff_y)
{
+ u16 di0dq0;
+ u16 x, y, data_rf;
+ int k;
+ switch (cal_type) {
+ case 0:
+ wlc_lcnphy_set_tx_iqcc(pi, coeff_x, coeff_y);
+ break;
+ case 2:
+ di0dq0 = (coeff_x & 0xff) << 8 | (coeff_y & 0xff);
+ wlc_lcnphy_set_tx_locc(pi, di0dq0);
+ break;
+ case 3:
+ k = wlc_lcnphy_calc_floor(coeff_x, 0);
+ y = 8 + k;
+ k = wlc_lcnphy_calc_floor(coeff_x, 1);
+ x = 8 - k;
+ data_rf = (x * 16 + y);
+ write_radio_reg(pi, RADIO_2064_REG089, data_rf);
+ k = wlc_lcnphy_calc_floor(coeff_y, 0);
+ y = 8 + k;
+ k = wlc_lcnphy_calc_floor(coeff_y, 1);
+ x = 8 - k;
+ data_rf = (x * 16 + y);
+ write_radio_reg(pi, RADIO_2064_REG08A, data_rf);
+ break;
+ case 4:
+ k = wlc_lcnphy_calc_floor(coeff_x, 0);
+ y = 8 + k;
+ k = wlc_lcnphy_calc_floor(coeff_x, 1);
+ x = 8 - k;
+ data_rf = (x * 16 + y);
+ write_radio_reg(pi, RADIO_2064_REG08B, data_rf);
+ k = wlc_lcnphy_calc_floor(coeff_y, 0);
+ y = 8 + k;
+ k = wlc_lcnphy_calc_floor(coeff_y, 1);
+ x = 8 - k;
+ data_rf = (x * 16 + y);
+ write_radio_reg(pi, RADIO_2064_REG08C, data_rf);
+ break;
+ }
+}
- and_phy_reg(pi, 0x44c, (u16) ~((0x1 << 1) | (0x1 << 0)));
+static struct lcnphy_unsign16_struct
+wlc_lcnphy_get_cc(struct brcms_phy *pi, int cal_type)
+{
+ u16 a, b, didq;
+ u8 di0, dq0, ei, eq, fi, fq;
+ struct lcnphy_unsign16_struct cc;
+ cc.re = 0;
+ cc.im = 0;
+ switch (cal_type) {
+ case 0:
+ wlc_lcnphy_get_tx_iqcc(pi, &a, &b);
+ cc.re = a;
+ cc.im = b;
+ break;
+ case 2:
+ didq = wlc_lcnphy_get_tx_locc(pi);
+ di0 = (((didq & 0xff00) << 16) >> 24);
+ dq0 = (((didq & 0x00ff) << 24) >> 24);
+ cc.re = (u16) di0;
+ cc.im = (u16) dq0;
+ break;
+ case 3:
+ wlc_lcnphy_get_radio_loft(pi, &ei, &eq, &fi, &fq);
+ cc.re = (u16) ei;
+ cc.im = (u16) eq;
+ break;
+ case 4:
+ wlc_lcnphy_get_radio_loft(pi, &ei, &eq, &fi, &fq);
+ cc.re = (u16) fi;
+ cc.im = (u16) fq;
+ break;
+ }
+ return cc;
}
-void wlc_lcnphy_get_tx_iqcc(phy_info_t *pi, u16 *a, u16 *b)
+static void
+wlc_lcnphy_samp_cap(struct brcms_phy *pi, int clip_detect_algo, u16 thresh,
+ s16 *ptr, int mode)
+{
+ u32 curval1, curval2, stpptr, curptr, strptr, val;
+ u16 sslpnCalibClkEnCtrl, timer;
+ u16 old_sslpnCalibClkEnCtrl;
+ s16 imag, real;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
+
+ timer = 0;
+ old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
+
+ curval1 = R_REG(&pi->regs->psm_corectlsts);
+ ptr[130] = 0;
+ W_REG(&pi->regs->psm_corectlsts, ((1 << 6) | curval1));
+
+ W_REG(&pi->regs->smpl_clct_strptr, 0x7E00);
+ W_REG(&pi->regs->smpl_clct_stpptr, 0x8000);
+ udelay(20);
+ curval2 = R_REG(&pi->regs->psm_phy_hdr_param);
+ W_REG(&pi->regs->psm_phy_hdr_param, curval2 | 0x30);
+
+ write_phy_reg(pi, 0x555, 0x0);
+ write_phy_reg(pi, 0x5a6, 0x5);
+
+ write_phy_reg(pi, 0x5a2, (u16) (mode | mode << 6));
+ write_phy_reg(pi, 0x5cf, 3);
+ write_phy_reg(pi, 0x5a5, 0x3);
+ write_phy_reg(pi, 0x583, 0x0);
+ write_phy_reg(pi, 0x584, 0x0);
+ write_phy_reg(pi, 0x585, 0x0fff);
+ write_phy_reg(pi, 0x586, 0x0000);
+
+ write_phy_reg(pi, 0x580, 0x4501);
+
+ sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
+ write_phy_reg(pi, 0x6da, (u32) (sslpnCalibClkEnCtrl | 0x2008));
+ stpptr = R_REG(&pi->regs->smpl_clct_stpptr);
+ curptr = R_REG(&pi->regs->smpl_clct_curptr);
+ do {
+ udelay(10);
+ curptr = R_REG(&pi->regs->smpl_clct_curptr);
+ timer++;
+ } while ((curptr != stpptr) && (timer < 500));
+
+ W_REG(&pi->regs->psm_phy_hdr_param, 0x2);
+ strptr = 0x7E00;
+ W_REG(&pi->regs->tplatewrptr, strptr);
+ while (strptr < 0x8000) {
+ val = R_REG(&pi->regs->tplatewrdata);
+ imag = ((val >> 16) & 0x3ff);
+ real = ((val) & 0x3ff);
+ if (imag > 511)
+ imag -= 1024;
+
+ if (real > 511)
+ real -= 1024;
+
+ if (pi_lcn->lcnphy_iqcal_swp_dis)
+ ptr[(strptr - 0x7E00) / 4] = real;
+ else
+ ptr[(strptr - 0x7E00) / 4] = imag;
+
+ if (clip_detect_algo) {
+ if (imag > thresh || imag < -thresh) {
+ strptr = 0x8000;
+ ptr[130] = 1;
+ }
+ }
+
+ strptr += 4;
+ }
+
+ write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
+ W_REG(&pi->regs->psm_phy_hdr_param, curval2);
+ W_REG(&pi->regs->psm_corectlsts, curval1);
+}
+
+static void
+wlc_lcnphy_a1(struct brcms_phy *pi, int cal_type, int num_levels,
+ int step_size_lg2)
+{
+ const struct lcnphy_spb_tone *phy_c1;
+ struct lcnphy_spb_tone phy_c2;
+ struct lcnphy_unsign16_struct phy_c3;
+ int phy_c4, phy_c5, k, l, j, phy_c6;
+ u16 phy_c7, phy_c8, phy_c9;
+ s16 phy_c10, phy_c11, phy_c12, phy_c13, phy_c14, phy_c15, phy_c16;
+ s16 *ptr, phy_c17;
+ s32 phy_c18, phy_c19;
+ u32 phy_c20, phy_c21;
+ bool phy_c22, phy_c23, phy_c24, phy_c25;
+ u16 phy_c26, phy_c27;
+ u16 phy_c28, phy_c29, phy_c30;
+ u16 phy_c31;
+ u16 *phy_c32;
+ phy_c21 = 0;
+ phy_c10 = phy_c13 = phy_c14 = phy_c8 = 0;
+ ptr = kmalloc(sizeof(s16) * 131, GFP_ATOMIC);
+ if (NULL == ptr)
+ return;
+
+ phy_c32 = kmalloc(sizeof(u16) * 20, GFP_ATOMIC);
+ if (NULL == phy_c32) {
+ kfree(ptr);
+ return;
+ }
+ phy_c26 = read_phy_reg(pi, 0x6da);
+ phy_c27 = read_phy_reg(pi, 0x6db);
+ phy_c31 = read_radio_reg(pi, RADIO_2064_REG026);
+ write_phy_reg(pi, 0x93d, 0xC0);
+
+ wlc_lcnphy_start_tx_tone(pi, 3750, 88, 0);
+ write_phy_reg(pi, 0x6da, 0xffff);
+ or_phy_reg(pi, 0x6db, 0x3);
+
+ wlc_lcnphy_tx_iqlo_loopback(pi, phy_c32);
+ udelay(500);
+ phy_c28 = read_phy_reg(pi, 0x938);
+ phy_c29 = read_phy_reg(pi, 0x4d7);
+ phy_c30 = read_phy_reg(pi, 0x4d8);
+ or_phy_reg(pi, 0x938, 0x1 << 2);
+ or_phy_reg(pi, 0x4d7, 0x1 << 2);
+ or_phy_reg(pi, 0x4d7, 0x1 << 3);
+ mod_phy_reg(pi, 0x4d7, (0x7 << 12), 0x2 << 12);
+ or_phy_reg(pi, 0x4d8, 1 << 0);
+ or_phy_reg(pi, 0x4d8, 1 << 1);
+ mod_phy_reg(pi, 0x4d8, (0x3ff << 2), 0x23A << 2);
+ mod_phy_reg(pi, 0x4d8, (0x7 << 12), 0x7 << 12);
+ phy_c1 = &lcnphy_spb_tone_3750[0];
+ phy_c4 = 32;
+
+ if (num_levels == 0) {
+ if (cal_type != 0)
+ num_levels = 4;
+ else
+ num_levels = 9;
+ }
+ if (step_size_lg2 == 0) {
+ if (cal_type != 0)
+ step_size_lg2 = 3;
+ else
+ step_size_lg2 = 8;
+ }
+
+ phy_c7 = (1 << step_size_lg2);
+ phy_c3 = wlc_lcnphy_get_cc(pi, cal_type);
+ phy_c15 = (s16) phy_c3.re;
+ phy_c16 = (s16) phy_c3.im;
+ if (cal_type == 2) {
+ if (phy_c3.re > 127)
+ phy_c15 = phy_c3.re - 256;
+ if (phy_c3.im > 127)
+ phy_c16 = phy_c3.im - 256;
+ }
+ wlc_lcnphy_set_cc(pi, cal_type, phy_c15, phy_c16);
+ udelay(20);
+ for (phy_c8 = 0; phy_c7 != 0 && phy_c8 < num_levels; phy_c8++) {
+ phy_c23 = 1;
+ phy_c22 = 0;
+ switch (cal_type) {
+ case 0:
+ phy_c10 = 511;
+ break;
+ case 2:
+ phy_c10 = 127;
+ break;
+ case 3:
+ phy_c10 = 15;
+ break;
+ case 4:
+ phy_c10 = 15;
+ break;
+ }
+
+ phy_c9 = read_phy_reg(pi, 0x93d);
+ phy_c9 = 2 * phy_c9;
+ phy_c24 = 0;
+ phy_c5 = 7;
+ phy_c25 = 1;
+ while (1) {
+ write_radio_reg(pi, RADIO_2064_REG026,
+ (phy_c5 & 0x7) | ((phy_c5 & 0x7) << 4));
+ udelay(50);
+ phy_c22 = 0;
+ ptr[130] = 0;
+ wlc_lcnphy_samp_cap(pi, 1, phy_c9, &ptr[0], 2);
+ if (ptr[130] == 1)
+ phy_c22 = 1;
+ if (phy_c22)
+ phy_c5 -= 1;
+ if ((phy_c22 != phy_c24) && (!phy_c25))
+ break;
+ if (!phy_c22)
+ phy_c5 += 1;
+ if (phy_c5 <= 0 || phy_c5 >= 7)
+ break;
+ phy_c24 = phy_c22;
+ phy_c25 = 0;
+ }
+
+ if (phy_c5 < 0)
+ phy_c5 = 0;
+ else if (phy_c5 > 7)
+ phy_c5 = 7;
+
+ for (k = -phy_c7; k <= phy_c7; k += phy_c7) {
+ for (l = -phy_c7; l <= phy_c7; l += phy_c7) {
+ phy_c11 = phy_c15 + k;
+ phy_c12 = phy_c16 + l;
+
+ if (phy_c11 < -phy_c10)
+ phy_c11 = -phy_c10;
+ else if (phy_c11 > phy_c10)
+ phy_c11 = phy_c10;
+ if (phy_c12 < -phy_c10)
+ phy_c12 = -phy_c10;
+ else if (phy_c12 > phy_c10)
+ phy_c12 = phy_c10;
+ wlc_lcnphy_set_cc(pi, cal_type, phy_c11,
+ phy_c12);
+ udelay(20);
+ wlc_lcnphy_samp_cap(pi, 0, 0, ptr, 2);
+
+ phy_c18 = 0;
+ phy_c19 = 0;
+ for (j = 0; j < 128; j++) {
+ if (cal_type != 0)
+ phy_c6 = j % phy_c4;
+ else
+ phy_c6 = (2 * j) % phy_c4;
+
+ phy_c2.re = phy_c1[phy_c6].re;
+ phy_c2.im = phy_c1[phy_c6].im;
+ phy_c17 = ptr[j];
+ phy_c18 = phy_c18 + phy_c17 * phy_c2.re;
+ phy_c19 = phy_c19 + phy_c17 * phy_c2.im;
+ }
+
+ phy_c18 = phy_c18 >> 10;
+ phy_c19 = phy_c19 >> 10;
+ phy_c20 = ((phy_c18 * phy_c18) +
+ (phy_c19 * phy_c19));
+
+ if (phy_c23 || phy_c20 < phy_c21) {
+ phy_c21 = phy_c20;
+ phy_c13 = phy_c11;
+ phy_c14 = phy_c12;
+ }
+ phy_c23 = 0;
+ }
+ }
+ phy_c23 = 1;
+ phy_c15 = phy_c13;
+ phy_c16 = phy_c14;
+ phy_c7 = phy_c7 >> 1;
+ wlc_lcnphy_set_cc(pi, cal_type, phy_c15, phy_c16);
+ udelay(20);
+ }
+ goto cleanup;
+cleanup:
+ wlc_lcnphy_tx_iqlo_loopback_cleanup(pi, phy_c32);
+ wlc_lcnphy_stop_tx_tone(pi);
+ write_phy_reg(pi, 0x6da, phy_c26);
+ write_phy_reg(pi, 0x6db, phy_c27);
+ write_phy_reg(pi, 0x938, phy_c28);
+ write_phy_reg(pi, 0x4d7, phy_c29);
+ write_phy_reg(pi, 0x4d8, phy_c30);
+ write_radio_reg(pi, RADIO_2064_REG026, phy_c31);
+
+ kfree(phy_c32);
+ kfree(ptr);
+}
+
+void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b)
{
u16 iqcc[2];
- phytbl_info_t tab;
+ struct phytbl_info tab;
tab.tbl_ptr = iqcc;
tab.tbl_len = 2;
@@ -2836,9 +3813,31 @@ void wlc_lcnphy_get_tx_iqcc(phy_info_t *pi, u16 *a, u16 *b)
*b = iqcc[1];
}
-u16 wlc_lcnphy_get_tx_locc(phy_info_t *pi)
+static void wlc_lcnphy_tx_iqlo_soft_cal_full(struct brcms_phy *pi)
+{
+ struct lcnphy_unsign16_struct iqcc0, locc2, locc3, locc4;
+
+ wlc_lcnphy_set_cc(pi, 0, 0, 0);
+ wlc_lcnphy_set_cc(pi, 2, 0, 0);
+ wlc_lcnphy_set_cc(pi, 3, 0, 0);
+ wlc_lcnphy_set_cc(pi, 4, 0, 0);
+
+ wlc_lcnphy_a1(pi, 4, 0, 0);
+ wlc_lcnphy_a1(pi, 3, 0, 0);
+ wlc_lcnphy_a1(pi, 2, 3, 2);
+ wlc_lcnphy_a1(pi, 0, 5, 8);
+ wlc_lcnphy_a1(pi, 2, 2, 1);
+ wlc_lcnphy_a1(pi, 0, 4, 3);
+
+ iqcc0 = wlc_lcnphy_get_cc(pi, 0);
+ locc2 = wlc_lcnphy_get_cc(pi, 2);
+ locc3 = wlc_lcnphy_get_cc(pi, 3);
+ locc4 = wlc_lcnphy_get_cc(pi, 4);
+}
+
+u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi)
{
- phytbl_info_t tab;
+ struct phytbl_info tab;
u16 didq;
tab.tbl_id = 0;
@@ -2851,18 +3850,18 @@ u16 wlc_lcnphy_get_tx_locc(phy_info_t *pi)
return didq;
}
-static void wlc_lcnphy_txpwrtbl_iqlo_cal(phy_info_t *pi)
+static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi)
{
- lcnphy_txgains_t target_gains, old_gains;
+ struct lcnphy_txgains target_gains, old_gains;
u8 save_bb_mult;
u16 a, b, didq, save_pa_gain = 0;
uint idx, SAVE_txpwrindex = 0xFF;
u32 val;
u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
- phytbl_info_t tab;
+ struct phytbl_info tab;
u8 ei0, eq0, fi0, fq0;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
wlc_lcnphy_get_tx_gain(pi, &old_gains);
save_pa_gain = wlc_lcnphy_get_pa_gain(pi);
@@ -2890,12 +3889,11 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(phy_info_t *pi)
lcnphy_recal ? LCNPHY_CAL_RECAL :
LCNPHY_CAL_FULL), false);
} else {
-
wlc_lcnphy_tx_iqlo_soft_cal_full(pi);
}
wlc_lcnphy_get_radio_loft(pi, &ei0, &eq0, &fi0, &fq0);
- if ((ABS((s8) fi0) == 15) && (ABS((s8) fq0) == 15)) {
+ if ((abs((s8) fi0) == 15) && (abs((s8) fq0) == 15)) {
if (CHSPEC_IS5G(pi->radio_chanspec)) {
target_gains.gm_gain = 255;
target_gains.pga_gain = 255;
@@ -2917,10 +3915,8 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(phy_info_t *pi)
wlc_lcnphy_tx_iqlo_cal(pi, &target_gains,
LCNPHY_CAL_FULL, false);
} else {
-
wlc_lcnphy_tx_iqlo_soft_cal_full(pi);
}
-
}
wlc_lcnphy_get_tx_iqcc(pi, &a, &b);
@@ -2939,7 +3935,7 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(phy_info_t *pi)
wlc_lcnphy_read_table(pi, &tab);
val = (val & 0xfff00000) |
- ((u32) (a & 0x3FF) << 10) | (b & 0x3ff);
+ ((u32) (a & 0x3FF) << 10) | (b & 0x3ff);
wlc_lcnphy_write_table(pi, &tab);
val = didq;
@@ -2965,19 +3961,16 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(phy_info_t *pi)
wlc_lcnphy_set_tx_pwr_by_index(pi, SAVE_txpwrindex);
}
-s16 wlc_lcnphy_tempsense_new(phy_info_t *pi, bool mode)
+s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode)
{
u16 tempsenseval1, tempsenseval2;
s16 avg = 0;
bool suspend = 0;
- if (NORADIO_ENAB(pi->pubpi))
- return -1;
-
if (mode == 1) {
suspend =
- (0 ==
- (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ (0 ==
+ (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
if (!suspend)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE);
@@ -3010,21 +4003,18 @@ s16 wlc_lcnphy_tempsense_new(phy_info_t *pi, bool mode)
return avg;
}
-u16 wlc_lcnphy_tempsense(phy_info_t *pi, bool mode)
+u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode)
{
u16 tempsenseval1, tempsenseval2;
s32 avg = 0;
bool suspend = 0;
u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
-
- if (NORADIO_ENAB(pi->pubpi))
- return -1;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
if (mode == 1) {
suspend =
- (0 ==
- (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ (0 ==
+ (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
if (!suspend)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE);
@@ -3071,28 +4061,26 @@ u16 wlc_lcnphy_tempsense(phy_info_t *pi, bool mode)
return (u16) avg;
}
-s8 wlc_lcnphy_tempsense_degree(phy_info_t *pi, bool mode)
+s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode)
{
s32 degree = wlc_lcnphy_tempsense_new(pi, mode);
degree =
- ((degree << 10) + LCN_TEMPSENSE_OFFSET + (LCN_TEMPSENSE_DEN >> 1))
- / LCN_TEMPSENSE_DEN;
+ ((degree <<
+ 10) + LCN_TEMPSENSE_OFFSET + (LCN_TEMPSENSE_DEN >> 1))
+ / LCN_TEMPSENSE_DEN;
return (s8) degree;
}
-s8 wlc_lcnphy_vbatsense(phy_info_t *pi, bool mode)
+s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode)
{
u16 vbatsenseval;
s32 avg = 0;
bool suspend = 0;
- if (NORADIO_ENAB(pi->pubpi))
- return -1;
-
if (mode == 1) {
suspend =
- (0 ==
- (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ (0 ==
+ (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
if (!suspend)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
wlc_lcnphy_vbat_temp_sense_setup(pi, VBATSENSE);
@@ -3105,9 +4093,8 @@ s8 wlc_lcnphy_vbatsense(phy_info_t *pi, bool mode)
else
avg = (s32) vbatsenseval;
- avg =
- (avg * LCN_VBAT_SCALE_NOM +
- (LCN_VBAT_SCALE_DEN >> 1)) / LCN_VBAT_SCALE_DEN;
+ avg = (avg * LCN_VBAT_SCALE_NOM +
+ (LCN_VBAT_SCALE_DEN >> 1)) / LCN_VBAT_SCALE_DEN;
if (mode == 1) {
if (!suspend)
@@ -3116,7 +4103,7 @@ s8 wlc_lcnphy_vbatsense(phy_info_t *pi, bool mode)
return (s8) avg;
}
-static void wlc_lcnphy_afe_clk_init(phy_info_t *pi, u8 mode)
+static void wlc_lcnphy_afe_clk_init(struct brcms_phy *pi, u8 mode)
{
u8 phybw40;
phybw40 = CHSPEC_IS40(pi->radio_chanspec);
@@ -3130,321 +4117,18 @@ static void wlc_lcnphy_afe_clk_init(phy_info_t *pi, u8 mode)
wlc_lcnphy_toggle_afe_pwdn(pi);
}
-static bool
-wlc_lcnphy_rx_iq_est(phy_info_t *pi,
- u16 num_samps,
- u8 wait_time, lcnphy_iq_est_t *iq_est)
-{
- int wait_count = 0;
- bool result = true;
- u8 phybw40;
- phybw40 = CHSPEC_IS40(pi->radio_chanspec);
-
- mod_phy_reg(pi, 0x6da, (0x1 << 5), (1) << 5);
-
- mod_phy_reg(pi, 0x410, (0x1 << 3), (0) << 3);
-
- mod_phy_reg(pi, 0x482, (0xffff << 0), (num_samps) << 0);
-
- mod_phy_reg(pi, 0x481, (0xff << 0), ((u16) wait_time) << 0);
-
- mod_phy_reg(pi, 0x481, (0x1 << 8), (0) << 8);
-
- mod_phy_reg(pi, 0x481, (0x1 << 9), (1) << 9);
-
- while (read_phy_reg(pi, 0x481) & (0x1 << 9)) {
-
- if (wait_count > (10 * 500)) {
- result = false;
- goto cleanup;
- }
- udelay(100);
- wait_count++;
- }
-
- iq_est->iq_prod = ((u32) read_phy_reg(pi, 0x483) << 16) |
- (u32) read_phy_reg(pi, 0x484);
- iq_est->i_pwr = ((u32) read_phy_reg(pi, 0x485) << 16) |
- (u32) read_phy_reg(pi, 0x486);
- iq_est->q_pwr = ((u32) read_phy_reg(pi, 0x487) << 16) |
- (u32) read_phy_reg(pi, 0x488);
-
- cleanup:
- mod_phy_reg(pi, 0x410, (0x1 << 3), (1) << 3);
-
- mod_phy_reg(pi, 0x6da, (0x1 << 5), (0) << 5);
-
- return result;
-}
-
-static bool wlc_lcnphy_calc_rx_iq_comp(phy_info_t *pi, u16 num_samps)
-{
-#define LCNPHY_MIN_RXIQ_PWR 2
- bool result;
- u16 a0_new, b0_new;
- lcnphy_iq_est_t iq_est = { 0, 0, 0 };
- s32 a, b, temp;
- s16 iq_nbits, qq_nbits, arsh, brsh;
- s32 iq;
- u32 ii, qq;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
-
- a0_new = ((read_phy_reg(pi, 0x645) & (0x3ff << 0)) >> 0);
- b0_new = ((read_phy_reg(pi, 0x646) & (0x3ff << 0)) >> 0);
- mod_phy_reg(pi, 0x6d1, (0x1 << 2), (0) << 2);
-
- mod_phy_reg(pi, 0x64b, (0x1 << 6), (1) << 6);
-
- wlc_lcnphy_set_rx_iq_comp(pi, 0, 0);
-
- result = wlc_lcnphy_rx_iq_est(pi, num_samps, 32, &iq_est);
- if (!result)
- goto cleanup;
-
- iq = (s32) iq_est.iq_prod;
- ii = iq_est.i_pwr;
- qq = iq_est.q_pwr;
-
- if ((ii + qq) < LCNPHY_MIN_RXIQ_PWR) {
- result = false;
- goto cleanup;
- }
-
- iq_nbits = wlc_phy_nbits(iq);
- qq_nbits = wlc_phy_nbits(qq);
-
- arsh = 10 - (30 - iq_nbits);
- if (arsh >= 0) {
- a = (-(iq << (30 - iq_nbits)) + (ii >> (1 + arsh)));
- temp = (s32) (ii >> arsh);
- if (temp == 0) {
- return false;
- }
- } else {
- a = (-(iq << (30 - iq_nbits)) + (ii << (-1 - arsh)));
- temp = (s32) (ii << -arsh);
- if (temp == 0) {
- return false;
- }
- }
- a /= temp;
- brsh = qq_nbits - 31 + 20;
- if (brsh >= 0) {
- b = (qq << (31 - qq_nbits));
- temp = (s32) (ii >> brsh);
- if (temp == 0) {
- return false;
- }
- } else {
- b = (qq << (31 - qq_nbits));
- temp = (s32) (ii << -brsh);
- if (temp == 0) {
- return false;
- }
- }
- b /= temp;
- b -= a * a;
- b = (s32) int_sqrt((unsigned long) b);
- b -= (1 << 10);
- a0_new = (u16) (a & 0x3ff);
- b0_new = (u16) (b & 0x3ff);
- cleanup:
-
- wlc_lcnphy_set_rx_iq_comp(pi, a0_new, b0_new);
-
- mod_phy_reg(pi, 0x64b, (0x1 << 0), (1) << 0);
-
- mod_phy_reg(pi, 0x64b, (0x1 << 3), (1) << 3);
-
- pi_lcn->lcnphy_cal_results.rxiqcal_coeff_a0 = a0_new;
- pi_lcn->lcnphy_cal_results.rxiqcal_coeff_b0 = b0_new;
-
- return result;
-}
-
-static bool
-wlc_lcnphy_rx_iq_cal(phy_info_t *pi, const lcnphy_rx_iqcomp_t *iqcomp,
- int iqcomp_sz, bool tx_switch, bool rx_switch, int module,
- int tx_gain_idx)
+static void wlc_lcnphy_temp_adj(struct brcms_phy *pi)
{
- lcnphy_txgains_t old_gains;
- u16 tx_pwr_ctrl;
- u8 tx_gain_index_old = 0;
- bool result = false, tx_gain_override_old = false;
- u16 i, Core1TxControl_old, RFOverride0_old,
- RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old,
- rfoverride3_old, rfoverride3val_old, rfoverride4_old,
- rfoverride4val_old, afectrlovr_old, afectrlovrval_old;
- int tia_gain;
- u32 received_power, rx_pwr_threshold;
- u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl;
- u16 values_to_save[11];
- s16 *ptr;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
-
- ptr = kmalloc(sizeof(s16) * 131, GFP_ATOMIC);
- if (NULL == ptr) {
- return false;
- }
- if (module == 2) {
- while (iqcomp_sz--) {
- if (iqcomp[iqcomp_sz].chan ==
- CHSPEC_CHANNEL(pi->radio_chanspec)) {
-
- wlc_lcnphy_set_rx_iq_comp(pi,
- (u16)
- iqcomp[iqcomp_sz].a,
- (u16)
- iqcomp[iqcomp_sz].b);
- result = true;
- break;
- }
- }
- goto cal_done;
- }
-
- if (module == 1) {
-
- tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
- wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
-
- for (i = 0; i < 11; i++) {
- values_to_save[i] =
- read_radio_reg(pi, rxiq_cal_rf_reg[i]);
- }
- Core1TxControl_old = read_phy_reg(pi, 0x631);
-
- or_phy_reg(pi, 0x631, 0x0015);
-
- RFOverride0_old = read_phy_reg(pi, 0x44c);
- RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
- rfoverride2_old = read_phy_reg(pi, 0x4b0);
- rfoverride2val_old = read_phy_reg(pi, 0x4b1);
- rfoverride3_old = read_phy_reg(pi, 0x4f9);
- rfoverride3val_old = read_phy_reg(pi, 0x4fa);
- rfoverride4_old = read_phy_reg(pi, 0x938);
- rfoverride4val_old = read_phy_reg(pi, 0x939);
- afectrlovr_old = read_phy_reg(pi, 0x43b);
- afectrlovrval_old = read_phy_reg(pi, 0x43c);
- old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
- old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
-
- tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
- if (tx_gain_override_old) {
- wlc_lcnphy_get_tx_gain(pi, &old_gains);
- tx_gain_index_old = pi_lcn->lcnphy_current_index;
- }
-
- wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
-
- mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
- mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
-
- mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
- mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
-
- write_radio_reg(pi, RADIO_2064_REG116, 0x06);
- write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
- write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
- write_radio_reg(pi, RADIO_2064_REG098, 0x03);
- write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
- mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
- write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
- write_radio_reg(pi, RADIO_2064_REG114, 0x01);
- write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
- write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
-
- mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
- mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
- mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
- mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
- mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
- mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
- mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
- mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
- mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
- mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
-
- mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
- mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
-
- wlc_lcnphy_start_tx_tone(pi, 2000, 120, 0);
- write_phy_reg(pi, 0x6da, 0xffff);
- or_phy_reg(pi, 0x6db, 0x3);
- wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
- wlc_lcnphy_rx_gain_override_enable(pi, true);
-
- tia_gain = 8;
- rx_pwr_threshold = 950;
- while (tia_gain > 0) {
- tia_gain -= 1;
- wlc_lcnphy_set_rx_gain_by_distribution(pi,
- 0, 0, 2, 2,
- (u16)
- tia_gain, 1, 0);
- udelay(500);
-
- received_power =
- wlc_lcnphy_measure_digital_power(pi, 2000);
- if (received_power < rx_pwr_threshold)
- break;
- }
- result = wlc_lcnphy_calc_rx_iq_comp(pi, 0xffff);
-
- wlc_lcnphy_stop_tx_tone(pi);
-
- write_phy_reg(pi, 0x631, Core1TxControl_old);
-
- write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
- write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
- write_phy_reg(pi, 0x4b0, rfoverride2_old);
- write_phy_reg(pi, 0x4b1, rfoverride2val_old);
- write_phy_reg(pi, 0x4f9, rfoverride3_old);
- write_phy_reg(pi, 0x4fa, rfoverride3val_old);
- write_phy_reg(pi, 0x938, rfoverride4_old);
- write_phy_reg(pi, 0x939, rfoverride4val_old);
- write_phy_reg(pi, 0x43b, afectrlovr_old);
- write_phy_reg(pi, 0x43c, afectrlovrval_old);
- write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
- write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
-
- wlc_lcnphy_clear_trsw_override(pi);
-
- mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
-
- for (i = 0; i < 11; i++) {
- write_radio_reg(pi, rxiq_cal_rf_reg[i],
- values_to_save[i]);
- }
-
- if (tx_gain_override_old) {
- wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
- } else
- wlc_lcnphy_disable_tx_gain_override(pi);
- wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
-
- wlc_lcnphy_rx_gain_override_enable(pi, false);
- }
-
- cal_done:
- kfree(ptr);
- return result;
-}
-
-static void wlc_lcnphy_temp_adj(phy_info_t *pi)
-{
- if (NORADIO_ENAB(pi->pubpi))
- return;
}
-static void wlc_lcnphy_glacial_timer_based_cal(phy_info_t *pi)
+static void wlc_lcnphy_glacial_timer_based_cal(struct brcms_phy *pi)
{
bool suspend;
s8 index;
u16 SAVE_pwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
suspend =
- (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
if (!suspend)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
wlc_lcnphy_deaf_mode(pi, true);
@@ -3462,36 +4146,33 @@ static void wlc_lcnphy_glacial_timer_based_cal(phy_info_t *pi)
}
-static void wlc_lcnphy_periodic_cal(phy_info_t *pi)
+static void wlc_lcnphy_periodic_cal(struct brcms_phy *pi)
{
bool suspend, full_cal;
- const lcnphy_rx_iqcomp_t *rx_iqcomp;
+ const struct lcnphy_rx_iqcomp *rx_iqcomp;
int rx_iqcomp_sz;
u16 SAVE_pwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
s8 index;
- phytbl_info_t tab;
+ struct phytbl_info tab;
s32 a1, b0, b1;
s32 tssi, pwr, maxtargetpwr, mintargetpwr;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
-
- if (NORADIO_ENAB(pi->pubpi))
- return;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
pi->phy_lastcal = pi->sh->now;
pi->phy_forcecal = false;
full_cal =
- (pi_lcn->lcnphy_full_cal_channel !=
- CHSPEC_CHANNEL(pi->radio_chanspec));
+ (pi_lcn->lcnphy_full_cal_channel !=
+ CHSPEC_CHANNEL(pi->radio_chanspec));
pi_lcn->lcnphy_full_cal_channel = CHSPEC_CHANNEL(pi->radio_chanspec);
index = pi_lcn->lcnphy_current_index;
suspend =
- (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
if (!suspend) {
-
wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, 10000);
wlapi_suspend_mac_and_wait(pi->sh->physhim);
}
+
wlc_lcnphy_deaf_mode(pi, true);
wlc_lcnphy_txpwrtbl_iqlo_cal(pi);
@@ -3506,7 +4187,7 @@ static void wlc_lcnphy_periodic_cal(phy_info_t *pi)
if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) {
- wlc_lcnphy_idle_tssi_est((wlc_phy_t *) pi);
+ wlc_lcnphy_idle_tssi_est((struct brcms_phy_pub *) pi);
b0 = pi->txpa_2g[0];
b1 = pi->txpa_2g[1];
@@ -3534,15 +4215,14 @@ static void wlc_lcnphy_periodic_cal(phy_info_t *pi)
wlapi_enable_mac(pi->sh->physhim);
}
-void wlc_lcnphy_calib_modes(phy_info_t *pi, uint mode)
+void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode)
{
u16 temp_new;
int temp1, temp2, temp_diff;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
switch (mode) {
case PHY_PERICAL_CHAN:
-
break;
case PHY_FULLCAL:
wlc_lcnphy_periodic_cal(pi);
@@ -3568,12 +4248,13 @@ void wlc_lcnphy_calib_modes(phy_info_t *pi, uint mode)
break;
case LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL:
if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
- wlc_lcnphy_tx_power_adjustment((wlc_phy_t *) pi);
+ wlc_lcnphy_tx_power_adjustment(
+ (struct brcms_phy_pub *) pi);
break;
}
}
-void wlc_lcnphy_get_tssi(phy_info_t *pi, s8 *ofdm_pwr, s8 *cck_pwr)
+void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, s8 *cck_pwr)
{
s8 cck_offset;
u16 status;
@@ -3581,7 +4262,7 @@ void wlc_lcnphy_get_tssi(phy_info_t *pi, s8 *ofdm_pwr, s8 *cck_pwr)
if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) &&
(status & (0x1 << 15))) {
*ofdm_pwr = (s8) (((read_phy_reg(pi, 0x4ab) & (0x1ff << 0))
- >> 0) >> 1);
+ >> 0) >> 1);
if (wlc_phy_tpc_isenabled_lcnphy(pi))
cck_offset = pi->tx_power_offset[TXP_FIRST_CCK];
@@ -3595,728 +4276,293 @@ void wlc_lcnphy_get_tssi(phy_info_t *pi, s8 *ofdm_pwr, s8 *cck_pwr)
}
}
-void WLBANDINITFN(wlc_phy_cal_init_lcnphy) (phy_info_t *pi)
+void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi)
{
return;
}
-static void wlc_lcnphy_set_chanspec_tweaks(phy_info_t *pi, chanspec_t chanspec)
-{
- u8 channel = CHSPEC_CHANNEL(chanspec);
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
-
- if (NORADIO_ENAB(pi->pubpi))
- return;
-
- if (channel == 14) {
- mod_phy_reg(pi, 0x448, (0x3 << 8), (2) << 8);
-
- } else {
- mod_phy_reg(pi, 0x448, (0x3 << 8), (1) << 8);
-
- }
- pi_lcn->lcnphy_bandedge_corr = 2;
- if (channel == 1)
- pi_lcn->lcnphy_bandedge_corr = 4;
-
- if (channel == 1 || channel == 2 || channel == 3 ||
- channel == 4 || channel == 9 ||
- channel == 10 || channel == 11 || channel == 12) {
- si_pmu_pllcontrol(pi->sh->sih, 0x2, 0xffffffff, 0x03000c04);
- si_pmu_pllcontrol(pi->sh->sih, 0x3, 0xffffff, 0x0);
- si_pmu_pllcontrol(pi->sh->sih, 0x4, 0xffffffff, 0x200005c0);
-
- si_pmu_pllupd(pi->sh->sih);
- write_phy_reg(pi, 0x942, 0);
- wlc_lcnphy_txrx_spur_avoidance_mode(pi, false);
- pi_lcn->lcnphy_spurmod = 0;
- mod_phy_reg(pi, 0x424, (0xff << 8), (0x1b) << 8);
-
- write_phy_reg(pi, 0x425, 0x5907);
- } else {
- si_pmu_pllcontrol(pi->sh->sih, 0x2, 0xffffffff, 0x03140c04);
- si_pmu_pllcontrol(pi->sh->sih, 0x3, 0xffffff, 0x333333);
- si_pmu_pllcontrol(pi->sh->sih, 0x4, 0xffffffff, 0x202c2820);
-
- si_pmu_pllupd(pi->sh->sih);
- write_phy_reg(pi, 0x942, 0);
- wlc_lcnphy_txrx_spur_avoidance_mode(pi, true);
-
- pi_lcn->lcnphy_spurmod = 0;
- mod_phy_reg(pi, 0x424, (0xff << 8), (0x1f) << 8);
-
- write_phy_reg(pi, 0x425, 0x590a);
- }
-
- or_phy_reg(pi, 0x44a, 0x44);
- write_phy_reg(pi, 0x44a, 0x80);
-}
-
-void wlc_lcnphy_tx_power_adjustment(wlc_phy_t *ppi)
+void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi)
{
s8 index;
u16 index2;
- phy_info_t *pi = (phy_info_t *) ppi;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
- if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) && SAVE_txpwrctrl) {
+ if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) &&
+ SAVE_txpwrctrl) {
index = wlc_lcnphy_tempcompensated_txpwrctrl(pi);
index2 = (u16) (index * 2);
mod_phy_reg(pi, 0x4a9, (0x1ff << 0), (index2) << 0);
- pi_lcn->lcnphy_current_index = (s8)
- ((read_phy_reg(pi, 0x4a9) & 0xFF) / 2);
+ pi_lcn->lcnphy_current_index =
+ (s8)((read_phy_reg(pi, 0x4a9) & 0xFF) / 2);
}
}
-static void wlc_lcnphy_set_rx_iq_comp(phy_info_t *pi, u16 a, u16 b)
+static void
+wlc_lcnphy_load_tx_gain_table(struct brcms_phy *pi,
+ const struct lcnphy_tx_gain_tbl_entry *gain_table)
{
- mod_phy_reg(pi, 0x645, (0x3ff << 0), (a) << 0);
-
- mod_phy_reg(pi, 0x646, (0x3ff << 0), (b) << 0);
+ u32 j;
+ struct phytbl_info tab;
+ u32 val;
+ u16 pa_gain;
+ u16 gm_gain;
- mod_phy_reg(pi, 0x647, (0x3ff << 0), (a) << 0);
+ if (CHSPEC_IS5G(pi->radio_chanspec))
+ pa_gain = 0x70;
+ else
+ pa_gain = 0x70;
- mod_phy_reg(pi, 0x648, (0x3ff << 0), (b) << 0);
+ if (pi->sh->boardflags & BFL_FEM)
+ pa_gain = 0x10;
+ tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
+ tab.tbl_width = 32;
+ tab.tbl_len = 1;
+ tab.tbl_ptr = &val;
- mod_phy_reg(pi, 0x649, (0x3ff << 0), (a) << 0);
+ for (j = 0; j < 128; j++) {
+ gm_gain = gain_table[j].gm;
+ val = (((u32) pa_gain << 24) |
+ (gain_table[j].pad << 16) |
+ (gain_table[j].pga << 8) | gm_gain);
- mod_phy_reg(pi, 0x64a, (0x3ff << 0), (b) << 0);
+ tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + j;
+ wlc_lcnphy_write_table(pi, &tab);
+ val = (gain_table[j].dac << 28) | (gain_table[j].bb_mult << 20);
+ tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + j;
+ wlc_lcnphy_write_table(pi, &tab);
+ }
}
-void WLBANDINITFN(wlc_phy_init_lcnphy) (phy_info_t *pi)
+static void wlc_lcnphy_load_rfpower(struct brcms_phy *pi)
{
- u8 phybw40;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
- phybw40 = CHSPEC_IS40(pi->radio_chanspec);
+ struct phytbl_info tab;
+ u32 val, bbmult, rfgain;
+ u8 index;
+ u8 scale_factor = 1;
+ s16 temp, temp1, temp2, qQ, qQ1, qQ2, shift;
- pi_lcn->lcnphy_cal_counter = 0;
- pi_lcn->lcnphy_cal_temper = pi_lcn->lcnphy_rawtempsense;
+ tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
+ tab.tbl_width = 32;
+ tab.tbl_len = 1;
- or_phy_reg(pi, 0x44a, 0x80);
- and_phy_reg(pi, 0x44a, 0x7f);
+ for (index = 0; index < 128; index++) {
+ tab.tbl_ptr = &bbmult;
+ tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + index;
+ wlc_lcnphy_read_table(pi, &tab);
+ bbmult = bbmult >> 20;
- wlc_lcnphy_afe_clk_init(pi, AFE_CLK_INIT_MODE_TXRX2X);
+ tab.tbl_ptr = &rfgain;
+ tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + index;
+ wlc_lcnphy_read_table(pi, &tab);
- write_phy_reg(pi, 0x60a, 160);
+ qm_log10((s32) (bbmult), 0, &temp1, &qQ1);
+ qm_log10((s32) (1 << 6), 0, &temp2, &qQ2);
- write_phy_reg(pi, 0x46a, 25);
+ if (qQ1 < qQ2) {
+ temp2 = qm_shr16(temp2, qQ2 - qQ1);
+ qQ = qQ1;
+ } else {
+ temp1 = qm_shr16(temp1, qQ1 - qQ2);
+ qQ = qQ2;
+ }
+ temp = qm_sub16(temp1, temp2);
- wlc_lcnphy_baseband_init(pi);
+ if (qQ >= 4)
+ shift = qQ - 4;
+ else
+ shift = 4 - qQ;
- wlc_lcnphy_radio_init(pi);
+ val = (((index << shift) + (5 * temp) +
+ (1 << (scale_factor + shift - 3))) >> (scale_factor +
+ shift - 2));
- if (CHSPEC_IS2G(pi->radio_chanspec))
- wlc_lcnphy_tx_pwr_ctrl_init((wlc_phy_t *) pi);
+ tab.tbl_ptr = &val;
+ tab.tbl_offset = LCNPHY_TX_PWR_CTRL_PWR_OFFSET + index;
+ wlc_lcnphy_write_table(pi, &tab);
+ }
+}
- wlc_phy_chanspec_set((wlc_phy_t *) pi, pi->radio_chanspec);
+static void wlc_lcnphy_bu_tweaks(struct brcms_phy *pi)
+{
+ or_phy_reg(pi, 0x805, 0x1);
- si_pmu_regcontrol(pi->sh->sih, 0, 0xf, 0x9);
+ mod_phy_reg(pi, 0x42f, (0x7 << 0), (0x3) << 0);
- si_pmu_chipcontrol(pi->sh->sih, 0, 0xffffffff, 0x03CDDDDD);
+ mod_phy_reg(pi, 0x030, (0x7 << 0), (0x3) << 0);
- if ((pi->sh->boardflags & BFL_FEM)
- && wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
- wlc_lcnphy_set_tx_pwr_by_index(pi, FIXED_TXPWR);
+ write_phy_reg(pi, 0x414, 0x1e10);
+ write_phy_reg(pi, 0x415, 0x0640);
- wlc_lcnphy_agc_temp_init(pi);
+ mod_phy_reg(pi, 0x4df, (0xff << 8), -9 << 8);
- wlc_lcnphy_temp_adj(pi);
+ or_phy_reg(pi, 0x44a, 0x44);
+ write_phy_reg(pi, 0x44a, 0x80);
+ mod_phy_reg(pi, 0x434, (0xff << 0), (0xFD) << 0);
- mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14);
+ mod_phy_reg(pi, 0x420, (0xff << 0), (16) << 0);
- udelay(100);
- mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14);
+ if (!(pi->sh->boardrev < 0x1204))
+ mod_radio_reg(pi, RADIO_2064_REG09B, 0xF0, 0xF0);
- wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_HW);
- pi_lcn->lcnphy_noise_samples = LCNPHY_NOISE_SAMPLES_DEFAULT;
- wlc_lcnphy_calib_modes(pi, PHY_PERICAL_PHYINIT);
-}
+ write_phy_reg(pi, 0x7d6, 0x0902);
+ mod_phy_reg(pi, 0x429, (0xf << 0), (0x9) << 0);
-static void
-wlc_lcnphy_tx_iqlo_loopback(phy_info_t *pi, u16 *values_to_save)
-{
- u16 vmid;
- int i;
- for (i = 0; i < 20; i++) {
- values_to_save[i] =
- read_radio_reg(pi, iqlo_loopback_rf_regs[i]);
- }
+ mod_phy_reg(pi, 0x429, (0x3f << 4), (0xe) << 4);
- mod_phy_reg(pi, 0x44c, (0x1 << 12), 1 << 12);
- mod_phy_reg(pi, 0x44d, (0x1 << 14), 1 << 14);
+ if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
+ mod_phy_reg(pi, 0x423, (0xff << 0), (0x46) << 0);
- mod_phy_reg(pi, 0x44c, (0x1 << 11), 1 << 11);
- mod_phy_reg(pi, 0x44d, (0x1 << 13), 0 << 13);
+ mod_phy_reg(pi, 0x411, (0xff << 0), (1) << 0);
- mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
- mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
+ mod_phy_reg(pi, 0x434, (0xff << 0), (0xFF) << 0);
- mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
- mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
+ mod_phy_reg(pi, 0x656, (0xf << 0), (2) << 0);
- if (LCNREV_IS(pi->pubpi.phy_rev, 2))
- and_radio_reg(pi, RADIO_2064_REG03A, 0xFD);
- else
- and_radio_reg(pi, RADIO_2064_REG03A, 0xF9);
- or_radio_reg(pi, RADIO_2064_REG11A, 0x1);
+ mod_phy_reg(pi, 0x44d, (0x1 << 2), (1) << 2);
- or_radio_reg(pi, RADIO_2064_REG036, 0x01);
- or_radio_reg(pi, RADIO_2064_REG11A, 0x18);
- udelay(20);
+ mod_radio_reg(pi, RADIO_2064_REG0F7, 0x4, 0x4);
+ mod_radio_reg(pi, RADIO_2064_REG0F1, 0x3, 0);
+ mod_radio_reg(pi, RADIO_2064_REG0F2, 0xF8, 0x90);
+ mod_radio_reg(pi, RADIO_2064_REG0F3, 0x3, 0x2);
+ mod_radio_reg(pi, RADIO_2064_REG0F3, 0xf0, 0xa0);
- if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
- if (CHSPEC_IS5G(pi->radio_chanspec))
- mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0);
- else
- or_radio_reg(pi, RADIO_2064_REG03A, 1);
- } else {
- if (CHSPEC_IS5G(pi->radio_chanspec))
- mod_radio_reg(pi, RADIO_2064_REG03A, 3, 1);
- else
- or_radio_reg(pi, RADIO_2064_REG03A, 0x3);
- }
+ mod_radio_reg(pi, RADIO_2064_REG11F, 0x2, 0x2);
- udelay(20);
+ wlc_lcnphy_clear_tx_power_offsets(pi);
+ mod_phy_reg(pi, 0x4d0, (0x1ff << 6), (10) << 6);
- write_radio_reg(pi, RADIO_2064_REG025, 0xF);
- if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
- if (CHSPEC_IS5G(pi->radio_chanspec))
- mod_radio_reg(pi, RADIO_2064_REG028, 0xF, 0x4);
- else
- mod_radio_reg(pi, RADIO_2064_REG028, 0xF, 0x6);
- } else {
- if (CHSPEC_IS5G(pi->radio_chanspec))
- mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0x4 << 1);
- else
- mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0x6 << 1);
}
-
- udelay(20);
-
- write_radio_reg(pi, RADIO_2064_REG005, 0x8);
- or_radio_reg(pi, RADIO_2064_REG112, 0x80);
- udelay(20);
-
- or_radio_reg(pi, RADIO_2064_REG0FF, 0x10);
- or_radio_reg(pi, RADIO_2064_REG11F, 0x44);
- udelay(20);
-
- or_radio_reg(pi, RADIO_2064_REG00B, 0x7);
- or_radio_reg(pi, RADIO_2064_REG113, 0x10);
- udelay(20);
-
- write_radio_reg(pi, RADIO_2064_REG007, 0x1);
- udelay(20);
-
- vmid = 0x2A6;
- mod_radio_reg(pi, RADIO_2064_REG0FC, 0x3 << 0, (vmid >> 8) & 0x3);
- write_radio_reg(pi, RADIO_2064_REG0FD, (vmid & 0xff));
- or_radio_reg(pi, RADIO_2064_REG11F, 0x44);
- udelay(20);
-
- or_radio_reg(pi, RADIO_2064_REG0FF, 0x10);
- udelay(20);
- write_radio_reg(pi, RADIO_2064_REG012, 0x02);
- or_radio_reg(pi, RADIO_2064_REG112, 0x06);
- write_radio_reg(pi, RADIO_2064_REG036, 0x11);
- write_radio_reg(pi, RADIO_2064_REG059, 0xcc);
- write_radio_reg(pi, RADIO_2064_REG05C, 0x2e);
- write_radio_reg(pi, RADIO_2064_REG078, 0xd7);
- write_radio_reg(pi, RADIO_2064_REG092, 0x15);
}
-static void
-wlc_lcnphy_samp_cap(phy_info_t *pi, int clip_detect_algo, u16 thresh,
- s16 *ptr, int mode)
+static void wlc_lcnphy_rcal(struct brcms_phy *pi)
{
- u32 curval1, curval2, stpptr, curptr, strptr, val;
- u16 sslpnCalibClkEnCtrl, timer;
- u16 old_sslpnCalibClkEnCtrl;
- s16 imag, real;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
-
- timer = 0;
- old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
-
- curval1 = R_REG(&pi->regs->psm_corectlsts);
- ptr[130] = 0;
- W_REG(&pi->regs->psm_corectlsts, ((1 << 6) | curval1));
+ u8 rcal_value;
- W_REG(&pi->regs->smpl_clct_strptr, 0x7E00);
- W_REG(&pi->regs->smpl_clct_stpptr, 0x8000);
- udelay(20);
- curval2 = R_REG(&pi->regs->psm_phy_hdr_param);
- W_REG(&pi->regs->psm_phy_hdr_param, curval2 | 0x30);
+ and_radio_reg(pi, RADIO_2064_REG05B, 0xfD);
- write_phy_reg(pi, 0x555, 0x0);
- write_phy_reg(pi, 0x5a6, 0x5);
+ or_radio_reg(pi, RADIO_2064_REG004, 0x40);
+ or_radio_reg(pi, RADIO_2064_REG120, 0x10);
- write_phy_reg(pi, 0x5a2, (u16) (mode | mode << 6));
- write_phy_reg(pi, 0x5cf, 3);
- write_phy_reg(pi, 0x5a5, 0x3);
- write_phy_reg(pi, 0x583, 0x0);
- write_phy_reg(pi, 0x584, 0x0);
- write_phy_reg(pi, 0x585, 0x0fff);
- write_phy_reg(pi, 0x586, 0x0000);
+ or_radio_reg(pi, RADIO_2064_REG078, 0x80);
+ or_radio_reg(pi, RADIO_2064_REG129, 0x02);
- write_phy_reg(pi, 0x580, 0x4501);
+ or_radio_reg(pi, RADIO_2064_REG057, 0x01);
- sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
- write_phy_reg(pi, 0x6da, (u32) (sslpnCalibClkEnCtrl | 0x2008));
- stpptr = R_REG(&pi->regs->smpl_clct_stpptr);
- curptr = R_REG(&pi->regs->smpl_clct_curptr);
- do {
- udelay(10);
- curptr = R_REG(&pi->regs->smpl_clct_curptr);
- timer++;
- } while ((curptr != stpptr) && (timer < 500));
+ or_radio_reg(pi, RADIO_2064_REG05B, 0x02);
+ mdelay(5);
+ SPINWAIT(!wlc_radio_2064_rcal_done(pi), 10 * 1000 * 1000);
- W_REG(&pi->regs->psm_phy_hdr_param, 0x2);
- strptr = 0x7E00;
- W_REG(&pi->regs->tplatewrptr, strptr);
- while (strptr < 0x8000) {
- val = R_REG(&pi->regs->tplatewrdata);
- imag = ((val >> 16) & 0x3ff);
- real = ((val) & 0x3ff);
- if (imag > 511) {
- imag -= 1024;
- }
- if (real > 511) {
- real -= 1024;
- }
- if (pi_lcn->lcnphy_iqcal_swp_dis)
- ptr[(strptr - 0x7E00) / 4] = real;
- else
- ptr[(strptr - 0x7E00) / 4] = imag;
- if (clip_detect_algo) {
- if (imag > thresh || imag < -thresh) {
- strptr = 0x8000;
- ptr[130] = 1;
- }
- }
- strptr += 4;
+ if (wlc_radio_2064_rcal_done(pi)) {
+ rcal_value = (u8) read_radio_reg(pi, RADIO_2064_REG05C);
+ rcal_value = rcal_value & 0x1f;
}
- write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
- W_REG(&pi->regs->psm_phy_hdr_param, curval2);
- W_REG(&pi->regs->psm_corectlsts, curval1);
+ and_radio_reg(pi, RADIO_2064_REG05B, 0xfD);
+
+ and_radio_reg(pi, RADIO_2064_REG057, 0xFE);
}
-static void wlc_lcnphy_tx_iqlo_soft_cal_full(phy_info_t *pi)
+static void wlc_lcnphy_rc_cal(struct brcms_phy *pi)
{
- lcnphy_unsign16_struct iqcc0, locc2, locc3, locc4;
-
- wlc_lcnphy_set_cc(pi, 0, 0, 0);
- wlc_lcnphy_set_cc(pi, 2, 0, 0);
- wlc_lcnphy_set_cc(pi, 3, 0, 0);
- wlc_lcnphy_set_cc(pi, 4, 0, 0);
+ u8 dflt_rc_cal_val;
+ u16 flt_val;
- wlc_lcnphy_a1(pi, 4, 0, 0);
- wlc_lcnphy_a1(pi, 3, 0, 0);
- wlc_lcnphy_a1(pi, 2, 3, 2);
- wlc_lcnphy_a1(pi, 0, 5, 8);
- wlc_lcnphy_a1(pi, 2, 2, 1);
- wlc_lcnphy_a1(pi, 0, 4, 3);
+ dflt_rc_cal_val = 7;
+ if (LCNREV_IS(pi->pubpi.phy_rev, 1))
+ dflt_rc_cal_val = 11;
+ flt_val =
+ (dflt_rc_cal_val << 10) | (dflt_rc_cal_val << 5) |
+ (dflt_rc_cal_val);
+ write_phy_reg(pi, 0x933, flt_val);
+ write_phy_reg(pi, 0x934, flt_val);
+ write_phy_reg(pi, 0x935, flt_val);
+ write_phy_reg(pi, 0x936, flt_val);
+ write_phy_reg(pi, 0x937, (flt_val & 0x1FF));
- iqcc0 = wlc_lcnphy_get_cc(pi, 0);
- locc2 = wlc_lcnphy_get_cc(pi, 2);
- locc3 = wlc_lcnphy_get_cc(pi, 3);
- locc4 = wlc_lcnphy_get_cc(pi, 4);
+ return;
}
-static void
-wlc_lcnphy_set_cc(phy_info_t *pi, int cal_type, s16 coeff_x, s16 coeff_y)
+static void wlc_radio_2064_init(struct brcms_phy *pi)
{
- u16 di0dq0;
- u16 x, y, data_rf;
- int k;
- switch (cal_type) {
- case 0:
- wlc_lcnphy_set_tx_iqcc(pi, coeff_x, coeff_y);
- break;
- case 2:
- di0dq0 = (coeff_x & 0xff) << 8 | (coeff_y & 0xff);
- wlc_lcnphy_set_tx_locc(pi, di0dq0);
- break;
- case 3:
- k = wlc_lcnphy_calc_floor(coeff_x, 0);
- y = 8 + k;
- k = wlc_lcnphy_calc_floor(coeff_x, 1);
- x = 8 - k;
- data_rf = (x * 16 + y);
- write_radio_reg(pi, RADIO_2064_REG089, data_rf);
- k = wlc_lcnphy_calc_floor(coeff_y, 0);
- y = 8 + k;
- k = wlc_lcnphy_calc_floor(coeff_y, 1);
- x = 8 - k;
- data_rf = (x * 16 + y);
- write_radio_reg(pi, RADIO_2064_REG08A, data_rf);
- break;
- case 4:
- k = wlc_lcnphy_calc_floor(coeff_x, 0);
- y = 8 + k;
- k = wlc_lcnphy_calc_floor(coeff_x, 1);
- x = 8 - k;
- data_rf = (x * 16 + y);
- write_radio_reg(pi, RADIO_2064_REG08B, data_rf);
- k = wlc_lcnphy_calc_floor(coeff_y, 0);
- y = 8 + k;
- k = wlc_lcnphy_calc_floor(coeff_y, 1);
- x = 8 - k;
- data_rf = (x * 16 + y);
- write_radio_reg(pi, RADIO_2064_REG08C, data_rf);
- break;
- }
-}
+ u32 i;
+ const struct lcnphy_radio_regs *lcnphyregs = NULL;
-static lcnphy_unsign16_struct wlc_lcnphy_get_cc(phy_info_t *pi, int cal_type)
-{
- u16 a, b, didq;
- u8 di0, dq0, ei, eq, fi, fq;
- lcnphy_unsign16_struct cc;
- cc.re = 0;
- cc.im = 0;
- switch (cal_type) {
- case 0:
- wlc_lcnphy_get_tx_iqcc(pi, &a, &b);
- cc.re = a;
- cc.im = b;
- break;
- case 2:
- didq = wlc_lcnphy_get_tx_locc(pi);
- di0 = (((didq & 0xff00) << 16) >> 24);
- dq0 = (((didq & 0x00ff) << 24) >> 24);
- cc.re = (u16) di0;
- cc.im = (u16) dq0;
- break;
- case 3:
- wlc_lcnphy_get_radio_loft(pi, &ei, &eq, &fi, &fq);
- cc.re = (u16) ei;
- cc.im = (u16) eq;
- break;
- case 4:
- wlc_lcnphy_get_radio_loft(pi, &ei, &eq, &fi, &fq);
- cc.re = (u16) fi;
- cc.im = (u16) fq;
- break;
- }
- return cc;
-}
+ lcnphyregs = lcnphy_radio_regs_2064;
-static void
-wlc_lcnphy_a1(phy_info_t *pi, int cal_type, int num_levels, int step_size_lg2)
-{
- const lcnphy_spb_tone_t *phy_c1;
- lcnphy_spb_tone_t phy_c2;
- lcnphy_unsign16_struct phy_c3;
- int phy_c4, phy_c5, k, l, j, phy_c6;
- u16 phy_c7, phy_c8, phy_c9;
- s16 phy_c10, phy_c11, phy_c12, phy_c13, phy_c14, phy_c15, phy_c16;
- s16 *ptr, phy_c17;
- s32 phy_c18, phy_c19;
- u32 phy_c20, phy_c21;
- bool phy_c22, phy_c23, phy_c24, phy_c25;
- u16 phy_c26, phy_c27;
- u16 phy_c28, phy_c29, phy_c30;
- u16 phy_c31;
- u16 *phy_c32;
- phy_c21 = 0;
- phy_c10 = phy_c13 = phy_c14 = phy_c8 = 0;
- ptr = kmalloc(sizeof(s16) * 131, GFP_ATOMIC);
- if (NULL == ptr) {
- return;
- }
+ for (i = 0; lcnphyregs[i].address != 0xffff; i++)
+ if (CHSPEC_IS5G(pi->radio_chanspec) && lcnphyregs[i].do_init_a)
+ write_radio_reg(pi,
+ ((lcnphyregs[i].address & 0x3fff) |
+ RADIO_DEFAULT_CORE),
+ (u16) lcnphyregs[i].init_a);
+ else if (lcnphyregs[i].do_init_g)
+ write_radio_reg(pi,
+ ((lcnphyregs[i].address & 0x3fff) |
+ RADIO_DEFAULT_CORE),
+ (u16) lcnphyregs[i].init_g);
- phy_c32 = kmalloc(sizeof(u16) * 20, GFP_ATOMIC);
- if (NULL == phy_c32) {
- kfree(ptr);
- return;
- }
- phy_c26 = read_phy_reg(pi, 0x6da);
- phy_c27 = read_phy_reg(pi, 0x6db);
- phy_c31 = read_radio_reg(pi, RADIO_2064_REG026);
- write_phy_reg(pi, 0x93d, 0xC0);
+ write_radio_reg(pi, RADIO_2064_REG032, 0x62);
+ write_radio_reg(pi, RADIO_2064_REG033, 0x19);
- wlc_lcnphy_start_tx_tone(pi, 3750, 88, 0);
- write_phy_reg(pi, 0x6da, 0xffff);
- or_phy_reg(pi, 0x6db, 0x3);
+ write_radio_reg(pi, RADIO_2064_REG090, 0x10);
- wlc_lcnphy_tx_iqlo_loopback(pi, phy_c32);
- udelay(500);
- phy_c28 = read_phy_reg(pi, 0x938);
- phy_c29 = read_phy_reg(pi, 0x4d7);
- phy_c30 = read_phy_reg(pi, 0x4d8);
- or_phy_reg(pi, 0x938, 0x1 << 2);
- or_phy_reg(pi, 0x4d7, 0x1 << 2);
- or_phy_reg(pi, 0x4d7, 0x1 << 3);
- mod_phy_reg(pi, 0x4d7, (0x7 << 12), 0x2 << 12);
- or_phy_reg(pi, 0x4d8, 1 << 0);
- or_phy_reg(pi, 0x4d8, 1 << 1);
- mod_phy_reg(pi, 0x4d8, (0x3ff << 2), 0x23A << 2);
- mod_phy_reg(pi, 0x4d8, (0x7 << 12), 0x7 << 12);
- phy_c1 = &lcnphy_spb_tone_3750[0];
- phy_c4 = 32;
+ write_radio_reg(pi, RADIO_2064_REG010, 0x00);
- if (num_levels == 0) {
- if (cal_type != 0) {
- num_levels = 4;
- } else {
- num_levels = 9;
- }
- }
- if (step_size_lg2 == 0) {
- if (cal_type != 0) {
- step_size_lg2 = 3;
- } else {
- step_size_lg2 = 8;
- }
- }
+ if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
- phy_c7 = (1 << step_size_lg2);
- phy_c3 = wlc_lcnphy_get_cc(pi, cal_type);
- phy_c15 = (s16) phy_c3.re;
- phy_c16 = (s16) phy_c3.im;
- if (cal_type == 2) {
- if (phy_c3.re > 127)
- phy_c15 = phy_c3.re - 256;
- if (phy_c3.im > 127)
- phy_c16 = phy_c3.im - 256;
+ write_radio_reg(pi, RADIO_2064_REG060, 0x7f);
+ write_radio_reg(pi, RADIO_2064_REG061, 0x72);
+ write_radio_reg(pi, RADIO_2064_REG062, 0x7f);
}
- wlc_lcnphy_set_cc(pi, cal_type, phy_c15, phy_c16);
- udelay(20);
- for (phy_c8 = 0; phy_c7 != 0 && phy_c8 < num_levels; phy_c8++) {
- phy_c23 = 1;
- phy_c22 = 0;
- switch (cal_type) {
- case 0:
- phy_c10 = 511;
- break;
- case 2:
- phy_c10 = 127;
- break;
- case 3:
- phy_c10 = 15;
- break;
- case 4:
- phy_c10 = 15;
- break;
- }
- phy_c9 = read_phy_reg(pi, 0x93d);
- phy_c9 = 2 * phy_c9;
- phy_c24 = 0;
- phy_c5 = 7;
- phy_c25 = 1;
- while (1) {
- write_radio_reg(pi, RADIO_2064_REG026,
- (phy_c5 & 0x7) | ((phy_c5 & 0x7) << 4));
- udelay(50);
- phy_c22 = 0;
- ptr[130] = 0;
- wlc_lcnphy_samp_cap(pi, 1, phy_c9, &ptr[0], 2);
- if (ptr[130] == 1)
- phy_c22 = 1;
- if (phy_c22)
- phy_c5 -= 1;
- if ((phy_c22 != phy_c24) && (!phy_c25))
- break;
- if (!phy_c22)
- phy_c5 += 1;
- if (phy_c5 <= 0 || phy_c5 >= 7)
- break;
- phy_c24 = phy_c22;
- phy_c25 = 0;
- }
-
- if (phy_c5 < 0)
- phy_c5 = 0;
- else if (phy_c5 > 7)
- phy_c5 = 7;
-
- for (k = -phy_c7; k <= phy_c7; k += phy_c7) {
- for (l = -phy_c7; l <= phy_c7; l += phy_c7) {
- phy_c11 = phy_c15 + k;
- phy_c12 = phy_c16 + l;
-
- if (phy_c11 < -phy_c10)
- phy_c11 = -phy_c10;
- else if (phy_c11 > phy_c10)
- phy_c11 = phy_c10;
- if (phy_c12 < -phy_c10)
- phy_c12 = -phy_c10;
- else if (phy_c12 > phy_c10)
- phy_c12 = phy_c10;
- wlc_lcnphy_set_cc(pi, cal_type, phy_c11,
- phy_c12);
- udelay(20);
- wlc_lcnphy_samp_cap(pi, 0, 0, ptr, 2);
-
- phy_c18 = 0;
- phy_c19 = 0;
- for (j = 0; j < 128; j++) {
- if (cal_type != 0) {
- phy_c6 = j % phy_c4;
- } else {
- phy_c6 = (2 * j) % phy_c4;
- }
- phy_c2.re = phy_c1[phy_c6].re;
- phy_c2.im = phy_c1[phy_c6].im;
- phy_c17 = ptr[j];
- phy_c18 = phy_c18 + phy_c17 * phy_c2.re;
- phy_c19 = phy_c19 + phy_c17 * phy_c2.im;
- }
-
- phy_c18 = phy_c18 >> 10;
- phy_c19 = phy_c19 >> 10;
- phy_c20 =
- ((phy_c18 * phy_c18) + (phy_c19 * phy_c19));
+ write_radio_reg(pi, RADIO_2064_REG01D, 0x02);
+ write_radio_reg(pi, RADIO_2064_REG01E, 0x06);
- if (phy_c23 || phy_c20 < phy_c21) {
- phy_c21 = phy_c20;
- phy_c13 = phy_c11;
- phy_c14 = phy_c12;
- }
- phy_c23 = 0;
- }
- }
- phy_c23 = 1;
- phy_c15 = phy_c13;
- phy_c16 = phy_c14;
- phy_c7 = phy_c7 >> 1;
- wlc_lcnphy_set_cc(pi, cal_type, phy_c15, phy_c16);
- udelay(20);
- }
- goto cleanup;
- cleanup:
- wlc_lcnphy_tx_iqlo_loopback_cleanup(pi, phy_c32);
- wlc_lcnphy_stop_tx_tone(pi);
- write_phy_reg(pi, 0x6da, phy_c26);
- write_phy_reg(pi, 0x6db, phy_c27);
- write_phy_reg(pi, 0x938, phy_c28);
- write_phy_reg(pi, 0x4d7, phy_c29);
- write_phy_reg(pi, 0x4d8, phy_c30);
- write_radio_reg(pi, RADIO_2064_REG026, phy_c31);
+ mod_phy_reg(pi, 0x4ea, (0x7 << 0), 0 << 0);
- kfree(phy_c32);
- kfree(ptr);
-}
+ mod_phy_reg(pi, 0x4ea, (0x7 << 3), 1 << 3);
-static void
-wlc_lcnphy_tx_iqlo_loopback_cleanup(phy_info_t *pi, u16 *values_to_save)
-{
- int i;
+ mod_phy_reg(pi, 0x4ea, (0x7 << 6), 2 << 6);
- and_phy_reg(pi, 0x44c, 0x0 >> 11);
+ mod_phy_reg(pi, 0x4ea, (0x7 << 9), 3 << 9);
- and_phy_reg(pi, 0x43b, 0xC);
+ mod_phy_reg(pi, 0x4ea, (0x7 << 12), 4 << 12);
- for (i = 0; i < 20; i++) {
- write_radio_reg(pi, iqlo_loopback_rf_regs[i],
- values_to_save[i]);
- }
-}
+ write_phy_reg(pi, 0x4ea, 0x4688);
-static void
-WLBANDINITFN(wlc_lcnphy_load_tx_gain_table) (phy_info_t *pi,
- const lcnphy_tx_gain_tbl_entry *
- gain_table) {
- u32 j;
- phytbl_info_t tab;
- u32 val;
- u16 pa_gain;
- u16 gm_gain;
+ mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
- if (CHSPEC_IS5G(pi->radio_chanspec))
- pa_gain = 0x70;
- else
- pa_gain = 0x70;
+ mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6);
- if (pi->sh->boardflags & BFL_FEM)
- pa_gain = 0x10;
- tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
- tab.tbl_width = 32;
- tab.tbl_len = 1;
- tab.tbl_ptr = &val;
+ mod_phy_reg(pi, 0x46a, (0xffff << 0), 25 << 0);
- for (j = 0; j < 128; j++) {
- gm_gain = gain_table[j].gm;
- val = (((u32) pa_gain << 24) |
- (gain_table[j].pad << 16) |
- (gain_table[j].pga << 8) | gm_gain);
+ wlc_lcnphy_set_tx_locc(pi, 0);
- tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + j;
- wlc_lcnphy_write_table(pi, &tab);
+ wlc_lcnphy_rcal(pi);
- val = (gain_table[j].dac << 28) | (gain_table[j].bb_mult << 20);
- tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + j;
- wlc_lcnphy_write_table(pi, &tab);
- }
+ wlc_lcnphy_rc_cal(pi);
}
-static void wlc_lcnphy_load_rfpower(phy_info_t *pi)
+static void wlc_lcnphy_radio_init(struct brcms_phy *pi)
{
- phytbl_info_t tab;
- u32 val, bbmult, rfgain;
- u8 index;
- u8 scale_factor = 1;
- s16 temp, temp1, temp2, qQ, qQ1, qQ2, shift;
-
- tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
- tab.tbl_width = 32;
- tab.tbl_len = 1;
-
- for (index = 0; index < 128; index++) {
- tab.tbl_ptr = &bbmult;
- tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + index;
- wlc_lcnphy_read_table(pi, &tab);
- bbmult = bbmult >> 20;
-
- tab.tbl_ptr = &rfgain;
- tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + index;
- wlc_lcnphy_read_table(pi, &tab);
-
- qm_log10((s32) (bbmult), 0, &temp1, &qQ1);
- qm_log10((s32) (1 << 6), 0, &temp2, &qQ2);
-
- if (qQ1 < qQ2) {
- temp2 = qm_shr16(temp2, qQ2 - qQ1);
- qQ = qQ1;
- } else {
- temp1 = qm_shr16(temp1, qQ1 - qQ2);
- qQ = qQ2;
- }
- temp = qm_sub16(temp1, temp2);
-
- if (qQ >= 4)
- shift = qQ - 4;
- else
- shift = 4 - qQ;
-
- val = (((index << shift) + (5 * temp) +
- (1 << (scale_factor + shift - 3))) >> (scale_factor +
- shift - 2));
-
- tab.tbl_ptr = &val;
- tab.tbl_offset = LCNPHY_TX_PWR_CTRL_PWR_OFFSET + index;
- wlc_lcnphy_write_table(pi, &tab);
- }
+ wlc_radio_2064_init(pi);
}
-static void WLBANDINITFN(wlc_lcnphy_tbl_init) (phy_info_t *pi)
+static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
{
uint idx;
u8 phybw40;
- phytbl_info_t tab;
+ struct phytbl_info tab;
u32 val;
phybw40 = CHSPEC_IS40(pi->radio_chanspec);
- for (idx = 0; idx < dot11lcnphytbl_info_sz_rev0; idx++) {
+ for (idx = 0; idx < dot11lcnphytbl_info_sz_rev0; idx++)
wlc_lcnphy_write_table(pi, &dot11lcnphytbl_info_rev0[idx]);
- }
if (pi->sh->boardflags & BFL_FEM_BT) {
tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
@@ -4347,39 +4593,35 @@ static void WLBANDINITFN(wlc_lcnphy_tbl_init) (phy_info_t *pi)
if (CHSPEC_IS2G(pi->radio_chanspec)) {
if (pi->sh->boardflags & BFL_FEM)
- wlc_lcnphy_load_tx_gain_table(pi,
- dot11lcnphy_2GHz_extPA_gaintable_rev0);
+ wlc_lcnphy_load_tx_gain_table(
+ pi,
+ dot11lcnphy_2GHz_extPA_gaintable_rev0);
else
- wlc_lcnphy_load_tx_gain_table(pi,
- dot11lcnphy_2GHz_gaintable_rev0);
+ wlc_lcnphy_load_tx_gain_table(
+ pi,
+ dot11lcnphy_2GHz_gaintable_rev0);
}
if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
+ const struct phytbl_info *tb;
+ int l;
+
if (CHSPEC_IS2G(pi->radio_chanspec)) {
- for (idx = 0;
- idx < dot11lcnphytbl_rx_gain_info_2G_rev2_sz;
- idx++)
- if (pi->sh->boardflags & BFL_EXTLNA)
- wlc_lcnphy_write_table(pi,
- &dot11lcnphytbl_rx_gain_info_extlna_2G_rev2
- [idx]);
- else
- wlc_lcnphy_write_table(pi,
- &dot11lcnphytbl_rx_gain_info_2G_rev2
- [idx]);
+ l = dot11lcnphytbl_rx_gain_info_2G_rev2_sz;
+ if (pi->sh->boardflags & BFL_EXTLNA)
+ tb = dot11lcnphytbl_rx_gain_info_extlna_2G_rev2;
+ else
+ tb = dot11lcnphytbl_rx_gain_info_2G_rev2;
} else {
- for (idx = 0;
- idx < dot11lcnphytbl_rx_gain_info_5G_rev2_sz;
- idx++)
- if (pi->sh->boardflags & BFL_EXTLNA_5GHz)
- wlc_lcnphy_write_table(pi,
- &dot11lcnphytbl_rx_gain_info_extlna_5G_rev2
- [idx]);
- else
- wlc_lcnphy_write_table(pi,
- &dot11lcnphytbl_rx_gain_info_5G_rev2
- [idx]);
+ l = dot11lcnphytbl_rx_gain_info_5G_rev2_sz;
+ if (pi->sh->boardflags & BFL_EXTLNA_5GHz)
+ tb = dot11lcnphytbl_rx_gain_info_extlna_5G_rev2;
+ else
+ tb = dot11lcnphytbl_rx_gain_info_5G_rev2;
}
+
+ for (idx = 0; idx < l; idx++)
+ wlc_lcnphy_write_table(pi, &tb[idx]);
}
if ((pi->sh->boardflags & BFL_FEM)
@@ -4387,11 +4629,13 @@ static void WLBANDINITFN(wlc_lcnphy_tbl_init) (phy_info_t *pi)
wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313_epa);
else if (pi->sh->boardflags & BFL_FEM_BT) {
if (pi->sh->boardrev < 0x1250)
- wlc_lcnphy_write_table(pi,
- &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa);
+ wlc_lcnphy_write_table(
+ pi,
+ &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa);
else
- wlc_lcnphy_write_table(pi,
- &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250);
+ wlc_lcnphy_write_table(
+ pi,
+ &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250);
} else
wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313);
@@ -4400,10 +4644,10 @@ static void WLBANDINITFN(wlc_lcnphy_tbl_init) (phy_info_t *pi)
wlc_lcnphy_clear_papd_comptable(pi);
}
-static void WLBANDINITFN(wlc_lcnphy_rev0_baseband_init) (phy_info_t *pi)
+static void wlc_lcnphy_rev0_baseband_init(struct brcms_phy *pi)
{
u16 afectrl1;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
write_radio_reg(pi, RADIO_2064_REG11C, 0x0);
@@ -4428,9 +4672,8 @@ static void WLBANDINITFN(wlc_lcnphy_rev0_baseband_init) (phy_info_t *pi)
if (0) {
afectrl1 = 0;
afectrl1 = (u16) ((pi_lcn->lcnphy_rssi_vf) |
- (pi_lcn->lcnphy_rssi_vc << 4) | (pi_lcn->
- lcnphy_rssi_gs
- << 10));
+ (pi_lcn->lcnphy_rssi_vc << 4) |
+ (pi_lcn->lcnphy_rssi_gs << 10));
write_phy_reg(pi, 0x43e, afectrl1);
}
@@ -4447,24 +4690,20 @@ static void WLBANDINITFN(wlc_lcnphy_rev0_baseband_init) (phy_info_t *pi)
}
-static void WLBANDINITFN(wlc_lcnphy_rev2_baseband_init) (phy_info_t *pi)
+static void wlc_lcnphy_rev2_baseband_init(struct brcms_phy *pi)
{
if (CHSPEC_IS5G(pi->radio_chanspec)) {
mod_phy_reg(pi, 0x416, (0xff << 0), 80 << 0);
-
mod_phy_reg(pi, 0x416, (0xff << 8), 80 << 8);
}
}
-static void wlc_lcnphy_agc_temp_init(phy_info_t *pi)
+static void wlc_lcnphy_agc_temp_init(struct brcms_phy *pi)
{
s16 temp;
- phytbl_info_t tab;
+ struct phytbl_info tab;
u32 tableBuffer[2];
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
-
- if (NORADIO_ENAB(pi->pubpi))
- return;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
temp = (s16) read_phy_reg(pi, 0x4df);
pi_lcn->lcnphy_ofdmgainidxtableoffset = (temp & (0xff << 0)) >> 0;
@@ -4492,18 +4731,15 @@ static void wlc_lcnphy_agc_temp_init(phy_info_t *pi)
tableBuffer[1] -= 128;
pi_lcn->lcnphy_tr_T_gain_val = tableBuffer[1];
- temp = (s16) (read_phy_reg(pi, 0x434)
- & (0xff << 0));
+ temp = (s16) (read_phy_reg(pi, 0x434) & (0xff << 0));
if (temp > 127)
temp -= 256;
pi_lcn->lcnphy_input_pwr_offset_db = (s8) temp;
- pi_lcn->lcnphy_Med_Low_Gain_db = (read_phy_reg(pi, 0x424)
- & (0xff << 8))
- >> 8;
- pi_lcn->lcnphy_Very_Low_Gain_db = (read_phy_reg(pi, 0x425)
- & (0xff << 0))
- >> 0;
+ pi_lcn->lcnphy_Med_Low_Gain_db =
+ (read_phy_reg(pi, 0x424) & (0xff << 8)) >> 8;
+ pi_lcn->lcnphy_Very_Low_Gain_db =
+ (read_phy_reg(pi, 0x425) & (0xff << 0)) >> 0;
tab.tbl_ptr = tableBuffer;
tab.tbl_len = 2;
@@ -4517,62 +4753,7 @@ static void wlc_lcnphy_agc_temp_init(phy_info_t *pi)
}
-static void WLBANDINITFN(wlc_lcnphy_bu_tweaks) (phy_info_t *pi)
-{
- if (NORADIO_ENAB(pi->pubpi))
- return;
-
- or_phy_reg(pi, 0x805, 0x1);
-
- mod_phy_reg(pi, 0x42f, (0x7 << 0), (0x3) << 0);
-
- mod_phy_reg(pi, 0x030, (0x7 << 0), (0x3) << 0);
-
- write_phy_reg(pi, 0x414, 0x1e10);
- write_phy_reg(pi, 0x415, 0x0640);
-
- mod_phy_reg(pi, 0x4df, (0xff << 8), -9 << 8);
-
- or_phy_reg(pi, 0x44a, 0x44);
- write_phy_reg(pi, 0x44a, 0x80);
- mod_phy_reg(pi, 0x434, (0xff << 0), (0xFD) << 0);
-
- mod_phy_reg(pi, 0x420, (0xff << 0), (16) << 0);
-
- if (!(pi->sh->boardrev < 0x1204))
- mod_radio_reg(pi, RADIO_2064_REG09B, 0xF0, 0xF0);
-
- write_phy_reg(pi, 0x7d6, 0x0902);
- mod_phy_reg(pi, 0x429, (0xf << 0), (0x9) << 0);
-
- mod_phy_reg(pi, 0x429, (0x3f << 4), (0xe) << 4);
-
- if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
- mod_phy_reg(pi, 0x423, (0xff << 0), (0x46) << 0);
-
- mod_phy_reg(pi, 0x411, (0xff << 0), (1) << 0);
-
- mod_phy_reg(pi, 0x434, (0xff << 0), (0xFF) << 0);
-
- mod_phy_reg(pi, 0x656, (0xf << 0), (2) << 0);
-
- mod_phy_reg(pi, 0x44d, (0x1 << 2), (1) << 2);
-
- mod_radio_reg(pi, RADIO_2064_REG0F7, 0x4, 0x4);
- mod_radio_reg(pi, RADIO_2064_REG0F1, 0x3, 0);
- mod_radio_reg(pi, RADIO_2064_REG0F2, 0xF8, 0x90);
- mod_radio_reg(pi, RADIO_2064_REG0F3, 0x3, 0x2);
- mod_radio_reg(pi, RADIO_2064_REG0F3, 0xf0, 0xa0);
-
- mod_radio_reg(pi, RADIO_2064_REG11F, 0x2, 0x2);
-
- wlc_lcnphy_clear_tx_power_offsets(pi);
- mod_phy_reg(pi, 0x4d0, (0x1ff << 6), (10) << 6);
-
- }
-}
-
-static void WLBANDINITFN(wlc_lcnphy_baseband_init) (phy_info_t *pi)
+static void wlc_lcnphy_baseband_init(struct brcms_phy *pi)
{
wlc_lcnphy_tbl_init(pi);
@@ -4582,167 +4763,92 @@ static void WLBANDINITFN(wlc_lcnphy_baseband_init) (phy_info_t *pi)
wlc_lcnphy_bu_tweaks(pi);
}
-static void WLBANDINITFN(wlc_radio_2064_init) (phy_info_t *pi)
+void wlc_phy_init_lcnphy(struct brcms_phy *pi)
{
- u32 i;
- lcnphy_radio_regs_t *lcnphyregs = NULL;
-
- lcnphyregs = lcnphy_radio_regs_2064;
-
- for (i = 0; lcnphyregs[i].address != 0xffff; i++)
- if (CHSPEC_IS5G(pi->radio_chanspec) && lcnphyregs[i].do_init_a)
- write_radio_reg(pi,
- ((lcnphyregs[i].address & 0x3fff) |
- RADIO_DEFAULT_CORE),
- (u16) lcnphyregs[i].init_a);
- else if (lcnphyregs[i].do_init_g)
- write_radio_reg(pi,
- ((lcnphyregs[i].address & 0x3fff) |
- RADIO_DEFAULT_CORE),
- (u16) lcnphyregs[i].init_g);
-
- write_radio_reg(pi, RADIO_2064_REG032, 0x62);
- write_radio_reg(pi, RADIO_2064_REG033, 0x19);
-
- write_radio_reg(pi, RADIO_2064_REG090, 0x10);
-
- write_radio_reg(pi, RADIO_2064_REG010, 0x00);
-
- if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
-
- write_radio_reg(pi, RADIO_2064_REG060, 0x7f);
- write_radio_reg(pi, RADIO_2064_REG061, 0x72);
- write_radio_reg(pi, RADIO_2064_REG062, 0x7f);
- }
-
- write_radio_reg(pi, RADIO_2064_REG01D, 0x02);
- write_radio_reg(pi, RADIO_2064_REG01E, 0x06);
-
- mod_phy_reg(pi, 0x4ea, (0x7 << 0), 0 << 0);
-
- mod_phy_reg(pi, 0x4ea, (0x7 << 3), 1 << 3);
-
- mod_phy_reg(pi, 0x4ea, (0x7 << 6), 2 << 6);
-
- mod_phy_reg(pi, 0x4ea, (0x7 << 9), 3 << 9);
-
- mod_phy_reg(pi, 0x4ea, (0x7 << 12), 4 << 12);
-
- write_phy_reg(pi, 0x4ea, 0x4688);
-
- mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
-
- mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6);
-
- mod_phy_reg(pi, 0x46a, (0xffff << 0), 25 << 0);
-
- wlc_lcnphy_set_tx_locc(pi, 0);
-
- wlc_lcnphy_rcal(pi);
+ u8 phybw40;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
+ phybw40 = CHSPEC_IS40(pi->radio_chanspec);
- wlc_lcnphy_rc_cal(pi);
-}
+ pi_lcn->lcnphy_cal_counter = 0;
+ pi_lcn->lcnphy_cal_temper = pi_lcn->lcnphy_rawtempsense;
-static void WLBANDINITFN(wlc_lcnphy_radio_init) (phy_info_t *pi)
-{
- if (NORADIO_ENAB(pi->pubpi))
- return;
+ or_phy_reg(pi, 0x44a, 0x80);
+ and_phy_reg(pi, 0x44a, 0x7f);
- wlc_radio_2064_init(pi);
-}
+ wlc_lcnphy_afe_clk_init(pi, AFE_CLK_INIT_MODE_TXRX2X);
-static void wlc_lcnphy_rcal(phy_info_t *pi)
-{
- u8 rcal_value;
+ write_phy_reg(pi, 0x60a, 160);
- if (NORADIO_ENAB(pi->pubpi))
- return;
+ write_phy_reg(pi, 0x46a, 25);
- and_radio_reg(pi, RADIO_2064_REG05B, 0xfD);
+ wlc_lcnphy_baseband_init(pi);
- or_radio_reg(pi, RADIO_2064_REG004, 0x40);
- or_radio_reg(pi, RADIO_2064_REG120, 0x10);
+ wlc_lcnphy_radio_init(pi);
- or_radio_reg(pi, RADIO_2064_REG078, 0x80);
- or_radio_reg(pi, RADIO_2064_REG129, 0x02);
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ wlc_lcnphy_tx_pwr_ctrl_init((struct brcms_phy_pub *) pi);
- or_radio_reg(pi, RADIO_2064_REG057, 0x01);
+ wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec);
- or_radio_reg(pi, RADIO_2064_REG05B, 0x02);
- mdelay(5);
- SPINWAIT(!wlc_radio_2064_rcal_done(pi), 10 * 1000 * 1000);
+ si_pmu_regcontrol(pi->sh->sih, 0, 0xf, 0x9);
- if (wlc_radio_2064_rcal_done(pi)) {
- rcal_value = (u8) read_radio_reg(pi, RADIO_2064_REG05C);
- rcal_value = rcal_value & 0x1f;
- }
+ si_pmu_chipcontrol(pi->sh->sih, 0, 0xffffffff, 0x03CDDDDD);
- and_radio_reg(pi, RADIO_2064_REG05B, 0xfD);
+ if ((pi->sh->boardflags & BFL_FEM)
+ && wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
+ wlc_lcnphy_set_tx_pwr_by_index(pi, FIXED_TXPWR);
- and_radio_reg(pi, RADIO_2064_REG057, 0xFE);
-}
+ wlc_lcnphy_agc_temp_init(pi);
-static void wlc_lcnphy_rc_cal(phy_info_t *pi)
-{
- u8 dflt_rc_cal_val;
- u16 flt_val;
+ wlc_lcnphy_temp_adj(pi);
- if (NORADIO_ENAB(pi->pubpi))
- return;
+ mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14);
- dflt_rc_cal_val = 7;
- if (LCNREV_IS(pi->pubpi.phy_rev, 1))
- dflt_rc_cal_val = 11;
- flt_val =
- (dflt_rc_cal_val << 10) | (dflt_rc_cal_val << 5) |
- (dflt_rc_cal_val);
- write_phy_reg(pi, 0x933, flt_val);
- write_phy_reg(pi, 0x934, flt_val);
- write_phy_reg(pi, 0x935, flt_val);
- write_phy_reg(pi, 0x936, flt_val);
- write_phy_reg(pi, 0x937, (flt_val & 0x1FF));
+ udelay(100);
+ mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14);
- return;
+ wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_HW);
+ pi_lcn->lcnphy_noise_samples = LCNPHY_NOISE_SAMPLES_DEFAULT;
+ wlc_lcnphy_calib_modes(pi, PHY_PERICAL_PHYINIT);
}
-static bool wlc_phy_txpwr_srom_read_lcnphy(phy_info_t *pi)
+static bool wlc_phy_txpwr_srom_read_lcnphy(struct brcms_phy *pi)
{
s8 txpwr = 0;
int i;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
+ struct phy_shim_info *shim = pi->sh->physhim;
if (CHSPEC_IS2G(pi->radio_chanspec)) {
u16 cckpo = 0;
u32 offset_ofdm, offset_mcs;
pi_lcn->lcnphy_tr_isolation_mid =
- (u8) PHY_GETINTVAR(pi, "triso2g");
+ (u8)wlapi_getintvar(shim, BRCMS_SROM_TRISO2G);
pi_lcn->lcnphy_rx_power_offset =
- (u8) PHY_GETINTVAR(pi, "rxpo2g");
-
- pi->txpa_2g[0] = (s16) PHY_GETINTVAR(pi, "pa0b0");
- pi->txpa_2g[1] = (s16) PHY_GETINTVAR(pi, "pa0b1");
- pi->txpa_2g[2] = (s16) PHY_GETINTVAR(pi, "pa0b2");
-
- pi_lcn->lcnphy_rssi_vf = (u8) PHY_GETINTVAR(pi, "rssismf2g");
- pi_lcn->lcnphy_rssi_vc = (u8) PHY_GETINTVAR(pi, "rssismc2g");
- pi_lcn->lcnphy_rssi_gs = (u8) PHY_GETINTVAR(pi, "rssisav2g");
-
- {
- pi_lcn->lcnphy_rssi_vf_lowtemp = pi_lcn->lcnphy_rssi_vf;
- pi_lcn->lcnphy_rssi_vc_lowtemp = pi_lcn->lcnphy_rssi_vc;
- pi_lcn->lcnphy_rssi_gs_lowtemp = pi_lcn->lcnphy_rssi_gs;
-
- pi_lcn->lcnphy_rssi_vf_hightemp =
- pi_lcn->lcnphy_rssi_vf;
- pi_lcn->lcnphy_rssi_vc_hightemp =
- pi_lcn->lcnphy_rssi_vc;
- pi_lcn->lcnphy_rssi_gs_hightemp =
- pi_lcn->lcnphy_rssi_gs;
- }
+ (u8)wlapi_getintvar(shim, BRCMS_SROM_RXPO2G);
- txpwr = (s8) PHY_GETINTVAR(pi, "maxp2ga0");
+ pi->txpa_2g[0] = (s16)wlapi_getintvar(shim, BRCMS_SROM_PA0B0);
+ pi->txpa_2g[1] = (s16)wlapi_getintvar(shim, BRCMS_SROM_PA0B1);
+ pi->txpa_2g[2] = (s16)wlapi_getintvar(shim, BRCMS_SROM_PA0B2);
+
+ pi_lcn->lcnphy_rssi_vf =
+ (u8)wlapi_getintvar(shim, BRCMS_SROM_RSSISMF2G);
+ pi_lcn->lcnphy_rssi_vc =
+ (u8)wlapi_getintvar(shim, BRCMS_SROM_RSSISMC2G);
+ pi_lcn->lcnphy_rssi_gs =
+ (u8)wlapi_getintvar(shim, BRCMS_SROM_RSSISAV2G);
+
+ pi_lcn->lcnphy_rssi_vf_lowtemp = pi_lcn->lcnphy_rssi_vf;
+ pi_lcn->lcnphy_rssi_vc_lowtemp = pi_lcn->lcnphy_rssi_vc;
+ pi_lcn->lcnphy_rssi_gs_lowtemp = pi_lcn->lcnphy_rssi_gs;
+
+ pi_lcn->lcnphy_rssi_vf_hightemp = pi_lcn->lcnphy_rssi_vf;
+ pi_lcn->lcnphy_rssi_vc_hightemp = pi_lcn->lcnphy_rssi_vc;
+ pi_lcn->lcnphy_rssi_gs_hightemp = pi_lcn->lcnphy_rssi_gs;
+
+ txpwr = (s8)wlapi_getintvar(shim, BRCMS_SROM_MAXP2GA0);
pi->tx_srom_max_2g = txpwr;
for (i = 0; i < PWRTBL_NUM_COEFF; i++) {
@@ -4750,84 +4856,77 @@ static bool wlc_phy_txpwr_srom_read_lcnphy(phy_info_t *pi)
pi->txpa_2g_high_temp[i] = pi->txpa_2g[i];
}
- cckpo = (u16) PHY_GETINTVAR(pi, "cck2gpo");
+ cckpo = (u16)wlapi_getintvar(shim, BRCMS_SROM_CCK2GPO);
+ offset_ofdm = (u32)wlapi_getintvar(shim, BRCMS_SROM_OFDM2GPO);
if (cckpo) {
uint max_pwr_chan = txpwr;
for (i = TXP_FIRST_CCK; i <= TXP_LAST_CCK; i++) {
- pi->tx_srom_max_rate_2g[i] = max_pwr_chan -
- ((cckpo & 0xf) * 2);
+ pi->tx_srom_max_rate_2g[i] =
+ max_pwr_chan - ((cckpo & 0xf) * 2);
cckpo >>= 4;
}
- offset_ofdm = (u32) PHY_GETINTVAR(pi, "ofdm2gpo");
for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) {
- pi->tx_srom_max_rate_2g[i] = max_pwr_chan -
- ((offset_ofdm & 0xf) * 2);
+ pi->tx_srom_max_rate_2g[i] =
+ max_pwr_chan -
+ ((offset_ofdm & 0xf) * 2);
offset_ofdm >>= 4;
}
} else {
u8 opo = 0;
- opo = (u8) PHY_GETINTVAR(pi, "opo");
+ opo = (u8)wlapi_getintvar(shim, BRCMS_SROM_OPO);
- for (i = TXP_FIRST_CCK; i <= TXP_LAST_CCK; i++) {
+ for (i = TXP_FIRST_CCK; i <= TXP_LAST_CCK; i++)
pi->tx_srom_max_rate_2g[i] = txpwr;
- }
-
- offset_ofdm = (u32) PHY_GETINTVAR(pi, "ofdm2gpo");
for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) {
pi->tx_srom_max_rate_2g[i] = txpwr -
- ((offset_ofdm & 0xf) * 2);
+ ((offset_ofdm & 0xf) * 2);
offset_ofdm >>= 4;
}
offset_mcs =
- ((u16) PHY_GETINTVAR(pi, "mcs2gpo1") << 16) |
- (u16) PHY_GETINTVAR(pi, "mcs2gpo0");
+ wlapi_getintvar(shim,
+ BRCMS_SROM_MCS2GPO1) << 16;
+ offset_mcs |=
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS2GPO0);
pi_lcn->lcnphy_mcs20_po = offset_mcs;
for (i = TXP_FIRST_SISO_MCS_20;
i <= TXP_LAST_SISO_MCS_20; i++) {
pi->tx_srom_max_rate_2g[i] =
- txpwr - ((offset_mcs & 0xf) * 2);
+ txpwr - ((offset_mcs & 0xf) * 2);
offset_mcs >>= 4;
}
}
pi_lcn->lcnphy_rawtempsense =
- (u16) PHY_GETINTVAR(pi, "rawtempsense");
+ (u16)wlapi_getintvar(shim, BRCMS_SROM_RAWTEMPSENSE);
pi_lcn->lcnphy_measPower =
- (u8) PHY_GETINTVAR(pi, "measpower");
+ (u8)wlapi_getintvar(shim, BRCMS_SROM_MEASPOWER);
pi_lcn->lcnphy_tempsense_slope =
- (u8) PHY_GETINTVAR(pi, "tempsense_slope");
+ (u8)wlapi_getintvar(shim, BRCMS_SROM_TEMPSENSE_SLOPE);
pi_lcn->lcnphy_hw_iqcal_en =
- (bool) PHY_GETINTVAR(pi, "hw_iqcal_en");
+ (bool)wlapi_getintvar(shim, BRCMS_SROM_HW_IQCAL_EN);
pi_lcn->lcnphy_iqcal_swp_dis =
- (bool) PHY_GETINTVAR(pi, "iqcal_swp_dis");
+ (bool)wlapi_getintvar(shim, BRCMS_SROM_IQCAL_SWP_DIS);
pi_lcn->lcnphy_tempcorrx =
- (u8) PHY_GETINTVAR(pi, "tempcorrx");
+ (u8)wlapi_getintvar(shim, BRCMS_SROM_TEMPCORRX);
pi_lcn->lcnphy_tempsense_option =
- (u8) PHY_GETINTVAR(pi, "tempsense_option");
+ (u8)wlapi_getintvar(shim, BRCMS_SROM_TEMPSENSE_OPTION);
pi_lcn->lcnphy_freqoffset_corr =
- (u8) PHY_GETINTVAR(pi, "freqoffset_corr");
- if ((u8) getintvar(pi->vars, "aa2g") > 1)
- wlc_phy_ant_rxdiv_set((wlc_phy_t *) pi,
- (u8) getintvar(pi->vars,
- "aa2g"));
+ (u8)wlapi_getintvar(shim, BRCMS_SROM_FREQOFFSET_CORR);
+ if ((u8)wlapi_getintvar(shim, BRCMS_SROM_AA2G) > 1)
+ wlc_phy_ant_rxdiv_set((struct brcms_phy_pub *) pi,
+ (u8) wlapi_getintvar(shim, BRCMS_SROM_AA2G));
}
pi_lcn->lcnphy_cck_dig_filt_type = -1;
- if (PHY_GETVAR(pi, "cckdigfilttype")) {
- s16 temp;
- temp = (s16) PHY_GETINTVAR(pi, "cckdigfilttype");
- if (temp >= 0) {
- pi_lcn->lcnphy_cck_dig_filt_type = temp;
- }
- }
return true;
}
-void wlc_2064_vco_cal(phy_info_t *pi)
+void wlc_2064_vco_cal(struct brcms_phy *pi)
{
u8 calnrst;
@@ -4842,173 +4941,7 @@ void wlc_2064_vco_cal(phy_info_t *pi)
mod_radio_reg(pi, RADIO_2064_REG057, 1 << 3, 0);
}
-static void
-wlc_lcnphy_radio_2064_channel_tune_4313(phy_info_t *pi, u8 channel)
-{
- uint i;
- const chan_info_2064_lcnphy_t *ci;
- u8 rfpll_doubler = 0;
- u8 pll_pwrup, pll_pwrup_ovr;
- fixed qFxtal, qFref, qFvco, qFcal;
- u8 d15, d16, f16, e44, e45;
- u32 div_int, div_frac, fvco3, fpfd, fref3, fcal_div;
- u16 loop_bw, d30, setCount;
- if (NORADIO_ENAB(pi->pubpi))
- return;
- ci = &chan_info_2064_lcnphy[0];
- rfpll_doubler = 1;
-
- mod_radio_reg(pi, RADIO_2064_REG09D, 0x4, 0x1 << 2);
-
- write_radio_reg(pi, RADIO_2064_REG09E, 0xf);
- if (!rfpll_doubler) {
- loop_bw = PLL_2064_LOOP_BW;
- d30 = PLL_2064_D30;
- } else {
- loop_bw = PLL_2064_LOOP_BW_DOUBLER;
- d30 = PLL_2064_D30_DOUBLER;
- }
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- for (i = 0; i < ARRAY_SIZE(chan_info_2064_lcnphy); i++)
- if (chan_info_2064_lcnphy[i].chan == channel)
- break;
-
- if (i >= ARRAY_SIZE(chan_info_2064_lcnphy)) {
- return;
- }
-
- ci = &chan_info_2064_lcnphy[i];
- }
-
- write_radio_reg(pi, RADIO_2064_REG02A, ci->logen_buftune);
-
- mod_radio_reg(pi, RADIO_2064_REG030, 0x3, ci->logen_rccr_tx);
-
- mod_radio_reg(pi, RADIO_2064_REG091, 0x3, ci->txrf_mix_tune_ctrl);
-
- mod_radio_reg(pi, RADIO_2064_REG038, 0xf, ci->pa_input_tune_g);
-
- mod_radio_reg(pi, RADIO_2064_REG030, 0x3 << 2,
- (ci->logen_rccr_rx) << 2);
-
- mod_radio_reg(pi, RADIO_2064_REG05E, 0xf, ci->pa_rxrf_lna1_freq_tune);
-
- mod_radio_reg(pi, RADIO_2064_REG05E, (0xf) << 4,
- (ci->pa_rxrf_lna2_freq_tune) << 4);
-
- write_radio_reg(pi, RADIO_2064_REG06C, ci->rxrf_rxrf_spare1);
-
- pll_pwrup = (u8) read_radio_reg(pi, RADIO_2064_REG044);
- pll_pwrup_ovr = (u8) read_radio_reg(pi, RADIO_2064_REG12B);
-
- or_radio_reg(pi, RADIO_2064_REG044, 0x07);
-
- or_radio_reg(pi, RADIO_2064_REG12B, (0x07) << 1);
- e44 = 0;
- e45 = 0;
-
- fpfd = rfpll_doubler ? (pi->xtalfreq << 1) : (pi->xtalfreq);
- if (pi->xtalfreq > 26000000)
- e44 = 1;
- if (pi->xtalfreq > 52000000)
- e45 = 1;
- if (e44 == 0)
- fcal_div = 1;
- else if (e45 == 0)
- fcal_div = 2;
- else
- fcal_div = 4;
- fvco3 = (ci->freq * 3);
- fref3 = 2 * fpfd;
-
- qFxtal = wlc_lcnphy_qdiv_roundup(pi->xtalfreq, PLL_2064_MHZ, 16);
- qFref = wlc_lcnphy_qdiv_roundup(fpfd, PLL_2064_MHZ, 16);
- qFcal = pi->xtalfreq * fcal_div / PLL_2064_MHZ;
- qFvco = wlc_lcnphy_qdiv_roundup(fvco3, 2, 16);
-
- write_radio_reg(pi, RADIO_2064_REG04F, 0x02);
-
- d15 = (pi->xtalfreq * fcal_div * 4 / 5) / PLL_2064_MHZ - 1;
- write_radio_reg(pi, RADIO_2064_REG052, (0x07 & (d15 >> 2)));
- write_radio_reg(pi, RADIO_2064_REG053, (d15 & 0x3) << 5);
-
- d16 = (qFcal * 8 / (d15 + 1)) - 1;
- write_radio_reg(pi, RADIO_2064_REG051, d16);
-
- f16 = ((d16 + 1) * (d15 + 1)) / qFcal;
- setCount = f16 * 3 * (ci->freq) / 32 - 1;
- mod_radio_reg(pi, RADIO_2064_REG053, (0x0f << 0),
- (u8) (setCount >> 8));
-
- or_radio_reg(pi, RADIO_2064_REG053, 0x10);
- write_radio_reg(pi, RADIO_2064_REG054, (u8) (setCount & 0xff));
-
- div_int = ((fvco3 * (PLL_2064_MHZ >> 4)) / fref3) << 4;
-
- div_frac = ((fvco3 * (PLL_2064_MHZ >> 4)) % fref3) << 4;
- while (div_frac >= fref3) {
- div_int++;
- div_frac -= fref3;
- }
- div_frac = wlc_lcnphy_qdiv_roundup(div_frac, fref3, 20);
-
- mod_radio_reg(pi, RADIO_2064_REG045, (0x1f << 0),
- (u8) (div_int >> 4));
- mod_radio_reg(pi, RADIO_2064_REG046, (0x1f << 4),
- (u8) (div_int << 4));
- mod_radio_reg(pi, RADIO_2064_REG046, (0x0f << 0),
- (u8) (div_frac >> 16));
- write_radio_reg(pi, RADIO_2064_REG047, (u8) (div_frac >> 8) & 0xff);
- write_radio_reg(pi, RADIO_2064_REG048, (u8) div_frac & 0xff);
-
- write_radio_reg(pi, RADIO_2064_REG040, 0xfb);
-
- write_radio_reg(pi, RADIO_2064_REG041, 0x9A);
- write_radio_reg(pi, RADIO_2064_REG042, 0xA3);
- write_radio_reg(pi, RADIO_2064_REG043, 0x0C);
-
- {
- u8 h29, h23, c28, d29, h28_ten, e30, h30_ten, cp_current;
- u16 c29, c38, c30, g30, d28;
- c29 = loop_bw;
- d29 = 200;
- c38 = 1250;
- h29 = d29 / c29;
- h23 = 1;
- c28 = 30;
- d28 = (((PLL_2064_HIGH_END_KVCO - PLL_2064_LOW_END_KVCO) *
- (fvco3 / 2 - PLL_2064_LOW_END_VCO)) /
- (PLL_2064_HIGH_END_VCO - PLL_2064_LOW_END_VCO))
- + PLL_2064_LOW_END_KVCO;
- h28_ten = (d28 * 10) / c28;
- c30 = 2640;
- e30 = (d30 - 680) / 490;
- g30 = 680 + (e30 * 490);
- h30_ten = (g30 * 10) / c30;
- cp_current = ((c38 * h29 * h23 * 100) / h28_ten) / h30_ten;
- mod_radio_reg(pi, RADIO_2064_REG03C, 0x3f, cp_current);
- }
- if (channel >= 1 && channel <= 5)
- write_radio_reg(pi, RADIO_2064_REG03C, 0x8);
- else
- write_radio_reg(pi, RADIO_2064_REG03C, 0x7);
- write_radio_reg(pi, RADIO_2064_REG03D, 0x3);
-
- mod_radio_reg(pi, RADIO_2064_REG044, 0x0c, 0x0c);
- udelay(1);
-
- wlc_2064_vco_cal(pi);
-
- write_radio_reg(pi, RADIO_2064_REG044, pll_pwrup);
- write_radio_reg(pi, RADIO_2064_REG12B, pll_pwrup_ovr);
- if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
- write_radio_reg(pi, RADIO_2064_REG038, 3);
- write_radio_reg(pi, RADIO_2064_REG091, 7);
- }
-}
-
-bool wlc_phy_tpc_isenabled_lcnphy(phy_info_t *pi)
+bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi)
{
if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
return 0;
@@ -5017,39 +4950,35 @@ bool wlc_phy_tpc_isenabled_lcnphy(phy_info_t *pi)
wlc_lcnphy_get_tx_pwr_ctrl((pi)));
}
-void wlc_phy_txpower_recalc_target_lcnphy(phy_info_t *pi)
+void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi)
{
u16 pwr_ctrl;
if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) {
wlc_lcnphy_calib_modes(pi, LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL);
} else if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) {
-
pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
wlc_lcnphy_txpower_recalc_target(pi);
-
wlc_lcnphy_set_tx_pwr_ctrl(pi, pwr_ctrl);
- } else
- return;
+ }
}
-void wlc_phy_detach_lcnphy(phy_info_t *pi)
+void wlc_phy_detach_lcnphy(struct brcms_phy *pi)
{
kfree(pi->u.pi_lcnphy);
}
-bool wlc_phy_attach_lcnphy(phy_info_t *pi)
+bool wlc_phy_attach_lcnphy(struct brcms_phy *pi)
{
- phy_info_lcnphy_t *pi_lcn;
+ struct brcms_phy_lcnphy *pi_lcn;
- pi->u.pi_lcnphy = kzalloc(sizeof(phy_info_lcnphy_t), GFP_ATOMIC);
- if (pi->u.pi_lcnphy == NULL) {
+ pi->u.pi_lcnphy = kzalloc(sizeof(struct brcms_phy_lcnphy), GFP_ATOMIC);
+ if (pi->u.pi_lcnphy == NULL)
return false;
- }
pi_lcn = pi->u.pi_lcnphy;
- if ((0 == (pi->sh->boardflags & BFL_NOPA)) && !NORADIO_ENAB(pi->pubpi)) {
+ if (0 == (pi->sh->boardflags & BFL_NOPA)) {
pi->hwpwrctrl = true;
pi->hwpwrctrl_capable = true;
}
@@ -5070,7 +4999,8 @@ bool wlc_phy_attach_lcnphy(phy_info_t *pi)
if (!wlc_phy_txpwr_srom_read_lcnphy(pi))
return false;
- if ((pi->sh->boardflags & BFL_FEM) && (LCNREV_IS(pi->pubpi.phy_rev, 1))) {
+ if ((pi->sh->boardflags & BFL_FEM) &&
+ (LCNREV_IS(pi->pubpi.phy_rev, 1))) {
if (pi_lcn->lcnphy_tempsense_option == 3) {
pi->hwpwrctrl = true;
pi->hwpwrctrl_capable = true;
@@ -5085,7 +5015,7 @@ bool wlc_phy_attach_lcnphy(phy_info_t *pi)
return true;
}
-static void wlc_lcnphy_set_rx_gain(phy_info_t *pi, u32 gain)
+static void wlc_lcnphy_set_rx_gain(struct brcms_phy *pi, u32 gain)
{
u16 trsw, ext_lna, lna1, lna2, tia, biq0, biq1, gain0_15, gain16_19;
@@ -5098,8 +5028,8 @@ static void wlc_lcnphy_set_rx_gain(phy_info_t *pi, u32 gain)
biq1 = (u16) (gain >> 16) & 0xf;
gain0_15 = (u16) ((lna1 & 0x3) | ((lna1 & 0x3) << 2) |
- ((lna2 & 0x3) << 4) | ((lna2 & 0x3) << 6) |
- ((tia & 0xf) << 8) | ((biq0 & 0xf) << 12));
+ ((lna2 & 0x3) << 4) | ((lna2 & 0x3) << 6) |
+ ((tia & 0xf) << 8) | ((biq0 & 0xf) << 12));
gain16_19 = biq1;
mod_phy_reg(pi, 0x44d, (0x1 << 0), trsw << 0);
@@ -5115,12 +5045,12 @@ static void wlc_lcnphy_set_rx_gain(phy_info_t *pi, u32 gain)
wlc_lcnphy_rx_gain_override_enable(pi, true);
}
-static u32 wlc_lcnphy_get_receive_power(phy_info_t *pi, s32 *gain_index)
+static u32 wlc_lcnphy_get_receive_power(struct brcms_phy *pi, s32 *gain_index)
{
u32 received_power = 0;
s32 max_index = 0;
u32 gain_code = 0;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
max_index = 36;
if (*gain_index >= 0)
@@ -5134,32 +5064,35 @@ static u32 wlc_lcnphy_get_receive_power(phy_info_t *pi, s32 *gain_index)
lcnphy_23bitgaincode_table
[*gain_index]);
received_power =
- wlc_lcnphy_measure_digital_power(pi,
- pi_lcn->
- lcnphy_noise_samples);
+ wlc_lcnphy_measure_digital_power(
+ pi,
+ pi_lcn->
+ lcnphy_noise_samples);
(*gain_index)++;
}
(*gain_index)--;
} else {
wlc_lcnphy_set_rx_gain(pi, gain_code);
received_power =
- wlc_lcnphy_measure_digital_power(pi,
- pi_lcn->
- lcnphy_noise_samples);
+ wlc_lcnphy_measure_digital_power(pi,
+ pi_lcn->
+ lcnphy_noise_samples);
}
return received_power;
}
-s32 wlc_lcnphy_rx_signal_power(phy_info_t *pi, s32 gain_index)
+s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index)
{
s32 gain = 0;
s32 nominal_power_db;
s32 log_val, gain_mismatch, desired_gain, input_power_offset_db,
input_power_db;
s32 received_power, temperature;
+ u32 power;
+ u32 msb1, msb2, val1, val2, diff1, diff2;
uint freq;
- phy_info_lcnphy_t *pi_lcn = pi->u.pi_lcnphy;
+ struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
received_power = wlc_lcnphy_get_receive_power(pi, &gain_index);
@@ -5167,20 +5100,17 @@ s32 wlc_lcnphy_rx_signal_power(phy_info_t *pi, s32 gain_index)
nominal_power_db = read_phy_reg(pi, 0x425) >> 8;
- {
- u32 power = (received_power * 16);
- u32 msb1, msb2, val1, val2, diff1, diff2;
- msb1 = ffs(power) - 1;
- msb2 = msb1 + 1;
- val1 = 1 << msb1;
- val2 = 1 << msb2;
- diff1 = (power - val1);
- diff2 = (val2 - power);
- if (diff1 < diff2)
- log_val = msb1;
- else
- log_val = msb2;
- }
+ power = (received_power * 16);
+ msb1 = ffs(power) - 1;
+ msb2 = msb1 + 1;
+ val1 = 1 << msb1;
+ val2 = 1 << msb2;
+ diff1 = (power - val1);
+ diff2 = (val2 - power);
+ if (diff1 < diff2)
+ log_val = msb1;
+ else
+ log_val = msb2;
log_val = log_val * 3;
@@ -5196,7 +5126,7 @@ s32 wlc_lcnphy_rx_signal_power(phy_info_t *pi, s32 gain_index)
input_power_db = input_power_offset_db - desired_gain;
input_power_db =
- input_power_db + lcnphy_gain_index_offset_for_rssi[gain_index];
+ input_power_db + lcnphy_gain_index_offset_for_rssi[gain_index];
freq = wlc_phy_channel2freq(CHSPEC_CHANNEL(pi->radio_chanspec));
if ((freq > 2427) && (freq <= 2467))
@@ -5204,99 +5134,21 @@ s32 wlc_lcnphy_rx_signal_power(phy_info_t *pi, s32 gain_index)
temperature = pi_lcn->lcnphy_lastsensed_temperature;
- if ((temperature - 15) < -30) {
- input_power_db =
- input_power_db + (((temperature - 10 - 25) * 286) >> 12) -
- 7;
- } else if ((temperature - 15) < 4) {
+ if ((temperature - 15) < -30)
input_power_db =
- input_power_db + (((temperature - 10 - 25) * 286) >> 12) -
- 3;
- } else {
+ input_power_db +
+ (((temperature - 10 - 25) * 286) >> 12) -
+ 7;
+ else if ((temperature - 15) < 4)
input_power_db =
- input_power_db + (((temperature - 10 - 25) * 286) >> 12);
- }
+ input_power_db +
+ (((temperature - 10 - 25) * 286) >> 12) -
+ 3;
+ else
+ input_power_db = input_power_db +
+ (((temperature - 10 - 25) * 286) >> 12);
wlc_lcnphy_rx_gain_override_enable(pi, 0);
return input_power_db;
}
-
-static int
-wlc_lcnphy_load_tx_iir_filter(phy_info_t *pi, bool is_ofdm, s16 filt_type)
-{
- s16 filt_index = -1;
- int j;
-
- u16 addr[] = {
- 0x910,
- 0x91e,
- 0x91f,
- 0x924,
- 0x925,
- 0x926,
- 0x920,
- 0x921,
- 0x927,
- 0x928,
- 0x929,
- 0x922,
- 0x923,
- 0x930,
- 0x931,
- 0x932
- };
-
- u16 addr_ofdm[] = {
- 0x90f,
- 0x900,
- 0x901,
- 0x906,
- 0x907,
- 0x908,
- 0x902,
- 0x903,
- 0x909,
- 0x90a,
- 0x90b,
- 0x904,
- 0x905,
- 0x90c,
- 0x90d,
- 0x90e
- };
-
- if (!is_ofdm) {
- for (j = 0; j < LCNPHY_NUM_TX_DIG_FILTERS_CCK; j++) {
- if (filt_type == LCNPHY_txdigfiltcoeffs_cck[j][0]) {
- filt_index = (s16) j;
- break;
- }
- }
-
- if (filt_index != -1) {
- for (j = 0; j < LCNPHY_NUM_DIG_FILT_COEFFS; j++) {
- write_phy_reg(pi, addr[j],
- LCNPHY_txdigfiltcoeffs_cck
- [filt_index][j + 1]);
- }
- }
- } else {
- for (j = 0; j < LCNPHY_NUM_TX_DIG_FILTERS_OFDM; j++) {
- if (filt_type == LCNPHY_txdigfiltcoeffs_ofdm[j][0]) {
- filt_index = (s16) j;
- break;
- }
- }
-
- if (filt_index != -1) {
- for (j = 0; j < LCNPHY_NUM_DIG_FILT_COEFFS; j++) {
- write_phy_reg(pi, addr_ofdm[j],
- LCNPHY_txdigfiltcoeffs_ofdm
- [filt_index][j + 1]);
- }
- }
- }
-
- return (filt_index != -1) ? 0 : -1;
-}
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_lcn.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h
index b7bfc7230dfc..f4a8ab09da43 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_lcn.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h
@@ -14,10 +14,12 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _wlc_phy_lcn_h_
-#define _wlc_phy_lcn_h_
+#ifndef _BRCM_PHY_LCN_H_
+#define _BRCM_PHY_LCN_H_
-struct phy_info_lcnphy {
+#include <types.h>
+
+struct brcms_phy_lcnphy {
int lcnphy_txrf_sp_9_override;
u8 lcnphy_full_cal_channel;
u8 lcnphy_cal_counter;
@@ -98,7 +100,7 @@ struct phy_info_lcnphy {
u16 lcnphy_extstxctrl1;
s16 lcnphy_cck_dig_filt_type;
s16 lcnphy_ofdm_dig_filt_type;
- lcnphy_cal_results_t lcnphy_cal_results;
+ struct lcnphy_cal_results lcnphy_cal_results;
u8 lcnphy_psat_pwr;
u8 lcnphy_psat_indx;
@@ -116,4 +118,4 @@ struct phy_info_lcnphy {
uint lcnphy_aci_start_time;
s8 lcnphy_tx_power_offset[TXP_NUM_RATES];
};
-#endif /* _wlc_phy_lcn_h_ */
+#endif /* _BRCM_PHY_LCN_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_n.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c
index 71275094e810..cd19c2f7a347 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_n.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c
@@ -15,44 +15,57 @@
*/
#include <linux/kernel.h>
-#include <linux/string.h>
-#include <bcmdefs.h>
-#include <wlc_cfg.h>
#include <linux/delay.h>
-#include <linux/pci.h>
-#include <aiutils.h>
-#include <sbchipc.h>
-#include <wlc_pmu.h>
-
-#include <bcmdevs.h>
-#include <sbhnddma.h>
-
-#include <wlc_phy_radio.h>
-#include <wlc_phy_int.h>
-#include <wlc_phyreg_n.h>
-#include <wlc_phytbl_n.h>
+#include <linux/cordic.h>
-#define READ_RADIO_REG2(pi, radio_type, jspace, core, reg_name) \
- read_radio_reg(pi, radio_type##_##jspace##_##reg_name | \
- ((core == PHY_CORE_0) ? radio_type##_##jspace##0 : radio_type##_##jspace##1))
-#define WRITE_RADIO_REG2(pi, radio_type, jspace, core, reg_name, value) \
+#include <brcm_hw_ids.h>
+#include <aiutils.h>
+#include <chipcommon.h>
+#include <pmu.h>
+#include <d11.h>
+#include <phy_shim.h>
+#include "phy_int.h"
+#include "phy_hal.h"
+#include "phy_radio.h"
+#include "phyreg_n.h"
+#include "phytbl_n.h"
+
+#define READ_RADIO_REG2(pi, radio_type, jspace, core, reg_name) \
+ read_radio_reg(pi, radio_type##_##jspace##_##reg_name | \
+ ((core == PHY_CORE_0) ? \
+ radio_type##_##jspace##0 : \
+ radio_type##_##jspace##1))
+
+#define WRITE_RADIO_REG2(pi, radio_type, jspace, core, reg_name, value) \
write_radio_reg(pi, radio_type##_##jspace##_##reg_name | \
- ((core == PHY_CORE_0) ? radio_type##_##jspace##0 : radio_type##_##jspace##1), value);
-#define WRITE_RADIO_SYN(pi, radio_type, reg_name, value) \
- write_radio_reg(pi, radio_type##_##SYN##_##reg_name, value);
-
-#define READ_RADIO_REG3(pi, radio_type, jspace, core, reg_name) \
- read_radio_reg(pi, ((core == PHY_CORE_0) ? radio_type##_##jspace##0##_##reg_name : \
- radio_type##_##jspace##1##_##reg_name));
-#define WRITE_RADIO_REG3(pi, radio_type, jspace, core, reg_name, value) \
- write_radio_reg(pi, ((core == PHY_CORE_0) ? radio_type##_##jspace##0##_##reg_name : \
- radio_type##_##jspace##1##_##reg_name), value);
-#define READ_RADIO_REG4(pi, radio_type, jspace, core, reg_name) \
- read_radio_reg(pi, ((core == PHY_CORE_0) ? radio_type##_##reg_name##_##jspace##0 : \
- radio_type##_##reg_name##_##jspace##1));
-#define WRITE_RADIO_REG4(pi, radio_type, jspace, core, reg_name, value) \
- write_radio_reg(pi, ((core == PHY_CORE_0) ? radio_type##_##reg_name##_##jspace##0 : \
- radio_type##_##reg_name##_##jspace##1), value);
+ ((core == PHY_CORE_0) ? \
+ radio_type##_##jspace##0 : \
+ radio_type##_##jspace##1), value)
+
+#define WRITE_RADIO_SYN(pi, radio_type, reg_name, value) \
+ write_radio_reg(pi, radio_type##_##SYN##_##reg_name, value)
+
+#define READ_RADIO_REG3(pi, radio_type, jspace, core, reg_name) \
+ read_radio_reg(pi, ((core == PHY_CORE_0) ? \
+ radio_type##_##jspace##0##_##reg_name : \
+ radio_type##_##jspace##1##_##reg_name))
+
+#define WRITE_RADIO_REG3(pi, radio_type, jspace, core, reg_name, value) \
+ write_radio_reg(pi, ((core == PHY_CORE_0) ? \
+ radio_type##_##jspace##0##_##reg_name : \
+ radio_type##_##jspace##1##_##reg_name), \
+ value)
+
+#define READ_RADIO_REG4(pi, radio_type, jspace, core, reg_name) \
+ read_radio_reg(pi, ((core == PHY_CORE_0) ? \
+ radio_type##_##reg_name##_##jspace##0 : \
+ radio_type##_##reg_name##_##jspace##1))
+
+#define WRITE_RADIO_REG4(pi, radio_type, jspace, core, reg_name, value) \
+ write_radio_reg(pi, ((core == PHY_CORE_0) ? \
+ radio_type##_##reg_name##_##jspace##0 : \
+ radio_type##_##reg_name##_##jspace##1), \
+ value)
#define NPHY_ACI_MAX_UNDETECT_WINDOW_SZ 40
#define NPHY_ACI_CHANNEL_DELTA 5
@@ -107,9 +120,11 @@
#define NPHY_CALSANITY_RSSI_NB_MAX_POS 9
#define NPHY_CALSANITY_RSSI_NB_MAX_NEG -9
#define NPHY_CALSANITY_RSSI_W1_MAX_POS 12
-#define NPHY_CALSANITY_RSSI_W1_MAX_NEG (NPHY_RSSICAL_W1_TARGET - NPHY_RSSICAL_MAXREAD)
+#define NPHY_CALSANITY_RSSI_W1_MAX_NEG (NPHY_RSSICAL_W1_TARGET - \
+ NPHY_RSSICAL_MAXREAD)
#define NPHY_CALSANITY_RSSI_W2_MAX_POS NPHY_CALSANITY_RSSI_W1_MAX_POS
-#define NPHY_CALSANITY_RSSI_W2_MAX_NEG (NPHY_RSSICAL_W2_TARGET - NPHY_RSSICAL_MAXREAD)
+#define NPHY_CALSANITY_RSSI_W2_MAX_NEG (NPHY_RSSICAL_W2_TARGET - \
+ NPHY_RSSICAL_MAXREAD)
#define NPHY_RSSI_SXT(x) ((s8) (-((x) & 0x20) + ((x) & 0x1f)))
#define NPHY_RSSI_NB_VIOL(x) (((x) > NPHY_CALSANITY_RSSI_NB_MAX_POS) || \
((x) < NPHY_CALSANITY_RSSI_NB_MAX_NEG))
@@ -128,11 +143,11 @@
#define NPHY_PAPD_COMP_OFF 0
#define NPHY_PAPD_COMP_ON 1
-#define NPHY_SROM_TEMPSHIFT 32
-#define NPHY_SROM_MAXTEMPOFFSET 16
-#define NPHY_SROM_MINTEMPOFFSET -16
+#define NPHY_SROM_TEMPSHIFT 32
+#define NPHY_SROM_MAXTEMPOFFSET 16
+#define NPHY_SROM_MINTEMPOFFSET -16
-#define NPHY_CAL_MAXTEMPDELTA 64
+#define NPHY_CAL_MAXTEMPDELTA 64
#define NPHY_NOISEVAR_TBLLEN40 256
#define NPHY_NOISEVAR_TBLLEN20 128
@@ -141,7 +156,27 @@
#define NPHY_ADJUSTED_MINCRSPOWER 0x1e
-typedef struct _nphy_iqcal_params {
+/* 5357 Chip specific ChipControl register bits */
+#define CCTRL5357_EXTPA (1<<14) /* extPA in ChipControl 1, bit 14 */
+#define CCTRL5357_ANT_MUX_2o3 (1<<15) /* 2o3 in ChipControl 1, bit 15 */
+
+#define NPHY_CAL_TSSISAMPS 64
+#define NPHY_TEST_TONE_FREQ_40MHz 4000
+#define NPHY_TEST_TONE_FREQ_20MHz 2500
+
+#define MAX_205x_RCAL_WAITLOOPS 10000
+
+#define NPHY_RXCAL_TONEAMP 181
+#define NPHY_RXCAL_TONEFREQ_40MHz 4000
+#define NPHY_RXCAL_TONEFREQ_20MHz 2000
+
+#define TXFILT_SHAPING_OFDM20 0
+#define TXFILT_SHAPING_OFDM40 1
+#define TXFILT_SHAPING_CCK 2
+#define TXFILT_DEFAULT_OFDM20 3
+#define TXFILT_DEFAULT_OFDM40 4
+
+struct nphy_iqcal_params {
u16 txlpf;
u16 txgm;
u16 pga;
@@ -149,20 +184,20 @@ typedef struct _nphy_iqcal_params {
u16 ipa;
u16 cal_gain;
u16 ncorr[5];
-} nphy_iqcal_params_t;
+};
-typedef struct _nphy_txiqcal_ladder {
+struct nphy_txiqcal_ladder {
u8 percent;
u8 g_env;
-} nphy_txiqcal_ladder_t;
+};
-typedef struct {
- nphy_txgains_t gains;
+struct nphy_ipa_txcalgains {
+ struct nphy_txgains gains;
bool useindex;
u8 index;
-} nphy_ipa_txcalgains_t;
+};
-typedef struct nphy_papd_restore_state_t {
+struct nphy_papd_restore_state {
u16 fbmix[2];
u16 vga_master[2];
u16 intpa_master[2];
@@ -171,55 +206,55 @@ typedef struct nphy_papd_restore_state_t {
u16 pwrup[2];
u16 atten[2];
u16 mm;
-} nphy_papd_restore_state;
+};
-typedef struct _nphy_ipa_txrxgain {
+struct nphy_ipa_txrxgain {
u16 hpvga;
u16 lpf_biq1;
u16 lpf_biq0;
u16 lna2;
u16 lna1;
s8 txpwrindex;
-} nphy_ipa_txrxgain_t;
+};
#define NPHY_IPA_RXCAL_MAXGAININDEX (6 - 1)
-nphy_ipa_txrxgain_t nphy_ipa_rxcal_gaintbl_5GHz[] = { {0, 0, 0, 0, 0, 100},
-{0, 0, 0, 0, 0, 50},
-{0, 0, 0, 0, 0, -1},
-{0, 0, 0, 3, 0, -1},
-{0, 0, 3, 3, 0, -1},
-{0, 2, 3, 3, 0, -1}
+static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_5GHz[] = {
+ {0, 0, 0, 0, 0, 100},
+ {0, 0, 0, 0, 0, 50},
+ {0, 0, 0, 0, 0, -1},
+ {0, 0, 0, 3, 0, -1},
+ {0, 0, 3, 3, 0, -1},
+ {0, 2, 3, 3, 0, -1}
};
-nphy_ipa_txrxgain_t nphy_ipa_rxcal_gaintbl_2GHz[] = { {0, 0, 0, 0, 0, 128},
-{0, 0, 0, 0, 0, 70},
-{0, 0, 0, 0, 0, 20},
-{0, 0, 0, 3, 0, 20},
-{0, 0, 3, 3, 0, 20},
-{0, 2, 3, 3, 0, 20}
+static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_2GHz[] = {
+ {0, 0, 0, 0, 0, 128},
+ {0, 0, 0, 0, 0, 70},
+ {0, 0, 0, 0, 0, 20},
+ {0, 0, 0, 3, 0, 20},
+ {0, 0, 3, 3, 0, 20},
+ {0, 2, 3, 3, 0, 20}
};
-nphy_ipa_txrxgain_t nphy_ipa_rxcal_gaintbl_5GHz_rev7[] = { {0, 0, 0, 0, 0, 100},
-{0, 0, 0, 0, 0, 50},
-{0, 0, 0, 0, 0, -1},
-{0, 0, 0, 3, 0, -1},
-{0, 0, 3, 3, 0, -1},
-{0, 0, 5, 3, 0, -1}
+static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_5GHz_rev7[] = {
+ {0, 0, 0, 0, 0, 100},
+ {0, 0, 0, 0, 0, 50},
+ {0, 0, 0, 0, 0, -1},
+ {0, 0, 0, 3, 0, -1},
+ {0, 0, 3, 3, 0, -1},
+ {0, 0, 5, 3, 0, -1}
};
-nphy_ipa_txrxgain_t nphy_ipa_rxcal_gaintbl_2GHz_rev7[] = { {0, 0, 0, 0, 0, 10},
-{0, 0, 0, 1, 0, 10},
-{0, 0, 1, 2, 0, 10},
-{0, 0, 1, 3, 0, 10},
-{0, 0, 4, 3, 0, 10},
-{0, 0, 6, 3, 0, 10}
+static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_2GHz_rev7[] = {
+ {0, 0, 0, 0, 0, 10},
+ {0, 0, 0, 1, 0, 10},
+ {0, 0, 1, 2, 0, 10},
+ {0, 0, 1, 3, 0, 10},
+ {0, 0, 4, 3, 0, 10},
+ {0, 0, 6, 3, 0, 10}
};
-#define NPHY_RXCAL_TONEAMP 181
-#define NPHY_RXCAL_TONEFREQ_40MHz 4000
-#define NPHY_RXCAL_TONEFREQ_20MHz 2000
-
enum {
NPHY_RXCAL_GAIN_INIT = 0,
NPHY_RXCAL_GAIN_UP,
@@ -228,17 +263,11 @@ enum {
#define wlc_phy_get_papd_nphy(pi) \
(read_phy_reg((pi), 0x1e7) & \
- ((0x1 << 15) | \
- (0x1 << 14) | \
- (0x1 << 13)))
+ ((0x1 << 15) | \
+ (0x1 << 14) | \
+ (0x1 << 13)))
-#define TXFILT_SHAPING_OFDM20 0
-#define TXFILT_SHAPING_OFDM40 1
-#define TXFILT_SHAPING_CCK 2
-#define TXFILT_DEFAULT_OFDM20 3
-#define TXFILT_DEFAULT_OFDM40 4
-
-u16 NPHY_IPA_REV4_txdigi_filtcoeffs[][NPHY_NUM_DIG_FILT_COEFFS] = {
+static const u16 NPHY_IPA_REV4_txdigi_filtcoeffs[][NPHY_NUM_DIG_FILT_COEFFS] = {
{-377, 137, -407, 208, -1527, 956, 93, 186, 93,
230, -44, 230, 201, -191, 201},
{-77, 20, -98, 49, -93, 60, 56, 111, 56, 26, -5,
@@ -255,7 +284,7 @@ u16 NPHY_IPA_REV4_txdigi_filtcoeffs[][NPHY_NUM_DIG_FILT_COEFFS] = {
0x97, 0x12d, 0x97, 0x25a, 0xd10, 0x25a}
};
-typedef struct _chan_info_nphy_2055 {
+struct chan_info_nphy_2055 {
u16 chan;
u16 freq;
uint unknown;
@@ -287,9 +316,9 @@ typedef struct _chan_info_nphy_2055 {
u16 PHY_BW4;
u16 PHY_BW5;
u16 PHY_BW6;
-} chan_info_nphy_2055_t;
+};
-typedef struct _chan_info_nphy_radio205x {
+struct chan_info_nphy_radio205x {
u16 chan;
u16 freq;
u8 RF_SYN_pll_vcocal1;
@@ -335,9 +364,9 @@ typedef struct _chan_info_nphy_radio205x {
u16 PHY_BW4;
u16 PHY_BW5;
u16 PHY_BW6;
-} chan_info_nphy_radio205x_t;
+};
-typedef struct _chan_info_nphy_radio2057 {
+struct chan_info_nphy_radio2057 {
u16 chan;
u16 freq;
u8 RF_vcocal_countval0;
@@ -374,9 +403,9 @@ typedef struct _chan_info_nphy_radio2057 {
u16 PHY_BW4;
u16 PHY_BW5;
u16 PHY_BW6;
-} chan_info_nphy_radio2057_t;
+};
-typedef struct _chan_info_nphy_radio2057_rev5 {
+struct chan_info_nphy_radio2057_rev5 {
u16 chan;
u16 freq;
u8 RF_vcocal_countval0;
@@ -403,18 +432,18 @@ typedef struct _chan_info_nphy_radio2057_rev5 {
u16 PHY_BW4;
u16 PHY_BW5;
u16 PHY_BW6;
-} chan_info_nphy_radio2057_rev5_t;
+};
-typedef struct nphy_sfo_cfg {
+struct nphy_sfo_cfg {
u16 PHY_BW1a;
u16 PHY_BW2;
u16 PHY_BW3;
u16 PHY_BW4;
u16 PHY_BW5;
u16 PHY_BW6;
-} nphy_sfo_cfg_t;
+};
-static chan_info_nphy_2055_t chan_info_nphy_2055[] = {
+static const struct chan_info_nphy_2055 chan_info_nphy_2055[] = {
{
184, 4920, 3280, 0x71, 0x01, 0xEC, 0x0F, 0xFF, 0x01, 0x04, 0x0A,
0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F,
@@ -913,7 +942,7 @@ static chan_info_nphy_2055_t chan_info_nphy_2055[] = {
0x01, 0x80, 0x3E6, 0x3E2, 0x3DE, 0x41B, 0x41F, 0x424}
};
-static chan_info_nphy_radio205x_t chan_info_nphyrev3_2056[] = {
+static const struct chan_info_nphy_radio205x chan_info_nphyrev3_2056[] = {
{
184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01,
0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f,
@@ -1536,7 +1565,7 @@ static chan_info_nphy_radio205x_t chan_info_nphyrev3_2056[] = {
0x0f, 0x00, 0x0d, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424}
};
-static chan_info_nphy_radio205x_t chan_info_nphyrev4_2056_A1[] = {
+static const struct chan_info_nphy_radio205x chan_info_nphyrev4_2056_A1[] = {
{
184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01,
0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f,
@@ -2159,7 +2188,7 @@ static chan_info_nphy_radio205x_t chan_info_nphyrev4_2056_A1[] = {
0x0f, 0x00, 0x0e, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424}
};
-static chan_info_nphy_radio205x_t chan_info_nphyrev5_2056v5[] = {
+static const struct chan_info_nphy_radio205x chan_info_nphyrev5_2056v5[] = {
{
184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01,
0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70,
@@ -2782,7 +2811,7 @@ static chan_info_nphy_radio205x_t chan_info_nphyrev5_2056v5[] = {
0x0d, 0x00, 0x08, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424}
};
-static chan_info_nphy_radio205x_t chan_info_nphyrev6_2056v6[] = {
+static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v6[] = {
{
184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01,
0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77,
@@ -3405,7 +3434,7 @@ static chan_info_nphy_radio205x_t chan_info_nphyrev6_2056v6[] = {
0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424}
};
-static chan_info_nphy_radio205x_t chan_info_nphyrev5n6_2056v7[] = {
+static const struct chan_info_nphy_radio205x chan_info_nphyrev5n6_2056v7[] = {
{
184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01,
0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70,
@@ -4028,7 +4057,7 @@ static chan_info_nphy_radio205x_t chan_info_nphyrev5n6_2056v7[] = {
0x0d, 0x00, 0x08, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424}
};
-static chan_info_nphy_radio205x_t chan_info_nphyrev6_2056v8[] = {
+static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v8[] = {
{
184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01,
0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77,
@@ -4651,7 +4680,7 @@ static chan_info_nphy_radio205x_t chan_info_nphyrev6_2056v8[] = {
0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424}
};
-static chan_info_nphy_radio205x_t chan_info_nphyrev6_2056v11[] = {
+static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v11[] = {
{
184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x02, 0x0c, 0x01,
0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77,
@@ -5274,7 +5303,7 @@ static chan_info_nphy_radio205x_t chan_info_nphyrev6_2056v11[] = {
0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424}
};
-static chan_info_nphy_radio2057_t chan_info_nphyrev7_2057_rev4[] = {
+static const struct chan_info_nphy_radio2057 chan_info_nphyrev7_2057_rev4[] = {
{
184, 4920, 0x68, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xec, 0x01, 0x0f,
0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00,
@@ -6137,7 +6166,8 @@ static chan_info_nphy_radio2057_t chan_info_nphyrev7_2057_rev4[] = {
0x0424}
};
-static chan_info_nphy_radio2057_rev5_t chan_info_nphyrev8_2057_rev5[] = {
+static const struct chan_info_nphy_radio2057_rev5
+chan_info_nphyrev8_2057_rev5[] = {
{
1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0d,
0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03c9, 0x03c5, 0x03c1,
@@ -6196,7 +6226,8 @@ static chan_info_nphy_radio2057_rev5_t chan_info_nphyrev8_2057_rev5[] = {
0x041b, 0x041f, 0x0424}
};
-static chan_info_nphy_radio2057_rev5_t chan_info_nphyrev9_2057_rev5v1[] = {
+static const struct chan_info_nphy_radio2057_rev5
+chan_info_nphyrev9_2057_rev5v1[] = {
{
1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0d,
0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03c9, 0x03c5, 0x03c1,
@@ -6255,7 +6286,7 @@ static chan_info_nphy_radio2057_rev5_t chan_info_nphyrev9_2057_rev5v1[] = {
0x041b, 0x041f, 0x0424}
};
-static chan_info_nphy_radio2057_t chan_info_nphyrev8_2057_rev7[] = {
+static const struct chan_info_nphy_radio2057 chan_info_nphyrev8_2057_rev7[] = {
{
184, 4920, 0x68, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xec, 0x01, 0x0f,
0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00,
@@ -6996,7 +7027,7 @@ static chan_info_nphy_radio2057_t chan_info_nphyrev8_2057_rev7[] = {
0x0424}
};
-static chan_info_nphy_radio2057_t chan_info_nphyrev8_2057_rev8[] = {
+static const struct chan_info_nphy_radio2057 chan_info_nphyrev8_2057_rev8[] = {
{
186, 4930, 0x6b, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xed, 0x01, 0x0f,
0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00,
@@ -7731,7 +7762,7 @@ static chan_info_nphy_radio2057_t chan_info_nphyrev8_2057_rev8[] = {
0x0424}
};
-radio_regs_t regs_2055[] = {
+static struct radio_regs regs_2055[] = {
{0x02, 0x80, 0x80, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0x27, 0x27, 0, 0},
@@ -7960,7 +7991,7 @@ radio_regs_t regs_2055[] = {
{0xFFFF, 0, 0, 0, 0},
};
-radio_regs_t regs_SYN_2056[] = {
+static struct radio_regs regs_SYN_2056[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -8145,7 +8176,7 @@ radio_regs_t regs_SYN_2056[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_TX_2056[] = {
+static struct radio_regs regs_TX_2056[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -8294,7 +8325,7 @@ radio_regs_t regs_TX_2056[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_RX_2056[] = {
+static struct radio_regs regs_RX_2056[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -8445,7 +8476,7 @@ radio_regs_t regs_RX_2056[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_SYN_2056_A1[] = {
+static struct radio_regs regs_SYN_2056_A1[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -8630,7 +8661,7 @@ radio_regs_t regs_SYN_2056_A1[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_TX_2056_A1[] = {
+static struct radio_regs regs_TX_2056_A1[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -8779,7 +8810,7 @@ radio_regs_t regs_TX_2056_A1[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_RX_2056_A1[] = {
+static struct radio_regs regs_RX_2056_A1[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -8930,7 +8961,7 @@ radio_regs_t regs_RX_2056_A1[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_SYN_2056_rev5[] = {
+static struct radio_regs regs_SYN_2056_rev5[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -9115,7 +9146,7 @@ radio_regs_t regs_SYN_2056_rev5[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_TX_2056_rev5[] = {
+static struct radio_regs regs_TX_2056_rev5[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -9272,7 +9303,7 @@ radio_regs_t regs_TX_2056_rev5[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_RX_2056_rev5[] = {
+static struct radio_regs regs_RX_2056_rev5[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -9423,7 +9454,7 @@ radio_regs_t regs_RX_2056_rev5[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_SYN_2056_rev6[] = {
+static struct radio_regs regs_SYN_2056_rev6[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -9608,7 +9639,7 @@ radio_regs_t regs_SYN_2056_rev6[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_TX_2056_rev6[] = {
+static struct radio_regs regs_TX_2056_rev6[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -9765,7 +9796,7 @@ radio_regs_t regs_TX_2056_rev6[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_RX_2056_rev6[] = {
+static struct radio_regs regs_RX_2056_rev6[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -9916,7 +9947,7 @@ radio_regs_t regs_RX_2056_rev6[] = {
{0xFFFF, 0, 0, 0, 0}
};
-radio_regs_t regs_SYN_2056_rev7[] = {
+static struct radio_regs regs_SYN_2056_rev7[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -10101,7 +10132,7 @@ radio_regs_t regs_SYN_2056_rev7[] = {
{0xFFFF, 0, 0, 0, 0},
};
-radio_regs_t regs_TX_2056_rev7[] = {
+static struct radio_regs regs_TX_2056_rev7[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -10258,7 +10289,7 @@ radio_regs_t regs_TX_2056_rev7[] = {
{0xFFFF, 0, 0, 0, 0},
};
-radio_regs_t regs_RX_2056_rev7[] = {
+static struct radio_regs regs_RX_2056_rev7[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -10409,7 +10440,7 @@ radio_regs_t regs_RX_2056_rev7[] = {
{0xFFFF, 0, 0, 0, 0},
};
-radio_regs_t regs_SYN_2056_rev8[] = {
+static struct radio_regs regs_SYN_2056_rev8[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -10594,7 +10625,7 @@ radio_regs_t regs_SYN_2056_rev8[] = {
{0xFFFF, 0, 0, 0, 0},
};
-radio_regs_t regs_TX_2056_rev8[] = {
+static struct radio_regs regs_TX_2056_rev8[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -10751,7 +10782,7 @@ radio_regs_t regs_TX_2056_rev8[] = {
{0xFFFF, 0, 0, 0, 0},
};
-radio_regs_t regs_RX_2056_rev8[] = {
+static struct radio_regs regs_RX_2056_rev8[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -10902,7 +10933,7 @@ radio_regs_t regs_RX_2056_rev8[] = {
{0xFFFF, 0, 0, 0, 0},
};
-radio_regs_t regs_SYN_2056_rev11[] = {
+static const struct radio_regs regs_SYN_2056_rev11[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -11087,7 +11118,7 @@ radio_regs_t regs_SYN_2056_rev11[] = {
{0xFFFF, 0, 0, 0, 0},
};
-radio_regs_t regs_TX_2056_rev11[] = {
+static const struct radio_regs regs_TX_2056_rev11[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -11244,7 +11275,7 @@ radio_regs_t regs_TX_2056_rev11[] = {
{0xFFFF, 0, 0, 0, 0},
};
-radio_regs_t regs_RX_2056_rev11[] = {
+static const struct radio_regs regs_RX_2056_rev11[] = {
{0x02, 0, 0, 0, 0},
{0x03, 0, 0, 0, 0},
{0x04, 0, 0, 0, 0},
@@ -11395,7 +11426,7 @@ radio_regs_t regs_RX_2056_rev11[] = {
{0xFFFF, 0, 0, 0, 0},
};
-radio_20xx_regs_t regs_2057_rev4[] = {
+static struct radio_20xx_regs regs_2057_rev4[] = {
{0x00, 0x84, 0},
{0x01, 0, 0},
{0x02, 0x60, 0},
@@ -11785,7 +11816,7 @@ radio_20xx_regs_t regs_2057_rev4[] = {
{0xFFFF, 0, 0},
};
-radio_20xx_regs_t regs_2057_rev5[] = {
+static struct radio_20xx_regs regs_2057_rev5[] = {
{0x00, 0, 1},
{0x01, 0x57, 1},
{0x02, 0x20, 1},
@@ -12117,7 +12148,7 @@ radio_20xx_regs_t regs_2057_rev5[] = {
{0xFFFF, 0, 0}
};
-radio_20xx_regs_t regs_2057_rev5v1[] = {
+static struct radio_20xx_regs regs_2057_rev5v1[] = {
{0x00, 0x15, 1},
{0x01, 0x57, 1},
{0x02, 0x20, 1},
@@ -12449,7 +12480,7 @@ radio_20xx_regs_t regs_2057_rev5v1[] = {
{0xFFFF, 0, 0}
};
-radio_20xx_regs_t regs_2057_rev7[] = {
+static struct radio_20xx_regs regs_2057_rev7[] = {
{0x00, 0, 1},
{0x01, 0x57, 1},
{0x02, 0x20, 1},
@@ -12865,7 +12896,7 @@ radio_20xx_regs_t regs_2057_rev7[] = {
{0xFFFF, 0, 0}
};
-radio_20xx_regs_t regs_2057_rev8[] = {
+static struct radio_20xx_regs regs_2057_rev8[] = {
{0x00, 0x8, 1},
{0x01, 0x57, 1},
{0x02, 0x20, 1},
@@ -13288,27 +13319,27 @@ static s32 nphy_lnagain_est1[] = { -224, 23242 };
static const u16 tbl_iqcal_gainparams_nphy[2][NPHY_IQCAL_NUMGAINS][8] = {
{
- {0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69},
- {0x700, 7, 0, 0, 0x69, 0x69, 0x69, 0x69},
- {0x710, 7, 1, 0, 0x68, 0x68, 0x68, 0x68},
- {0x720, 7, 2, 0, 0x67, 0x67, 0x67, 0x67},
- {0x730, 7, 3, 0, 0x66, 0x66, 0x66, 0x66},
- {0x740, 7, 4, 0, 0x65, 0x65, 0x65, 0x65},
- {0x741, 7, 4, 1, 0x65, 0x65, 0x65, 0x65},
- {0x742, 7, 4, 2, 0x65, 0x65, 0x65, 0x65},
- {0x743, 7, 4, 3, 0x65, 0x65, 0x65, 0x65}
- },
- {
- {0x000, 7, 0, 0, 0x79, 0x79, 0x79, 0x79},
- {0x700, 7, 0, 0, 0x79, 0x79, 0x79, 0x79},
- {0x710, 7, 1, 0, 0x79, 0x79, 0x79, 0x79},
- {0x720, 7, 2, 0, 0x78, 0x78, 0x78, 0x78},
- {0x730, 7, 3, 0, 0x78, 0x78, 0x78, 0x78},
- {0x740, 7, 4, 0, 0x78, 0x78, 0x78, 0x78},
- {0x741, 7, 4, 1, 0x78, 0x78, 0x78, 0x78},
- {0x742, 7, 4, 2, 0x78, 0x78, 0x78, 0x78},
- {0x743, 7, 4, 3, 0x78, 0x78, 0x78, 0x78}
- }
+ {0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69},
+ {0x700, 7, 0, 0, 0x69, 0x69, 0x69, 0x69},
+ {0x710, 7, 1, 0, 0x68, 0x68, 0x68, 0x68},
+ {0x720, 7, 2, 0, 0x67, 0x67, 0x67, 0x67},
+ {0x730, 7, 3, 0, 0x66, 0x66, 0x66, 0x66},
+ {0x740, 7, 4, 0, 0x65, 0x65, 0x65, 0x65},
+ {0x741, 7, 4, 1, 0x65, 0x65, 0x65, 0x65},
+ {0x742, 7, 4, 2, 0x65, 0x65, 0x65, 0x65},
+ {0x743, 7, 4, 3, 0x65, 0x65, 0x65, 0x65}
+ },
+ {
+ {0x000, 7, 0, 0, 0x79, 0x79, 0x79, 0x79},
+ {0x700, 7, 0, 0, 0x79, 0x79, 0x79, 0x79},
+ {0x710, 7, 1, 0, 0x79, 0x79, 0x79, 0x79},
+ {0x720, 7, 2, 0, 0x78, 0x78, 0x78, 0x78},
+ {0x730, 7, 3, 0, 0x78, 0x78, 0x78, 0x78},
+ {0x740, 7, 4, 0, 0x78, 0x78, 0x78, 0x78},
+ {0x741, 7, 4, 1, 0x78, 0x78, 0x78, 0x78},
+ {0x742, 7, 4, 2, 0x78, 0x78, 0x78, 0x78},
+ {0x743, 7, 4, 3, 0x78, 0x78, 0x78, 0x78}
+ }
};
static const u32 nphy_tpc_txgain[] = {
@@ -14079,122 +14110,15 @@ static u32 nphy_tpc_5GHz_txgain_HiPwrEPA[] = {
static u8 ant_sw_ctrl_tbl_rev8_2o3[] = { 0x14, 0x18 };
static u8 ant_sw_ctrl_tbl_rev8[] = { 0x4, 0x8, 0x4, 0x8, 0x11, 0x12 };
static u8 ant_sw_ctrl_tbl_rev8_2057v7_core0[] = {
- 0x09, 0x0a, 0x15, 0x16, 0x09, 0x0a };
+ 0x09, 0x0a, 0x15, 0x16, 0x09, 0x0a
+};
static u8 ant_sw_ctrl_tbl_rev8_2057v7_core1[] = {
- 0x09, 0x0a, 0x09, 0x0a, 0x15, 0x16 };
-
-static bool wlc_phy_chan2freq_nphy(phy_info_t *pi, uint channel, int *f,
- chan_info_nphy_radio2057_t **t0,
- chan_info_nphy_radio205x_t **t1,
- chan_info_nphy_radio2057_rev5_t **t2,
- chan_info_nphy_2055_t **t3);
-static void wlc_phy_chanspec_nphy_setup(phy_info_t *pi, chanspec_t chans,
- const nphy_sfo_cfg_t *c);
-
-static void wlc_phy_adjust_rx_analpfbw_nphy(phy_info_t *pi,
- u16 reduction_factr);
-static void wlc_phy_adjust_min_noisevar_nphy(phy_info_t *pi, int ntones, int *,
- u32 *buf);
-static void wlc_phy_adjust_crsminpwr_nphy(phy_info_t *pi, u8 minpwr);
-static void wlc_phy_txlpfbw_nphy(phy_info_t *pi);
-static void wlc_phy_spurwar_nphy(phy_info_t *pi);
-
-static void wlc_phy_radio_preinit_2055(phy_info_t *pi);
-static void wlc_phy_radio_init_2055(phy_info_t *pi);
-static void wlc_phy_radio_postinit_2055(phy_info_t *pi);
-static void wlc_phy_radio_preinit_205x(phy_info_t *pi);
-static void wlc_phy_radio_init_2056(phy_info_t *pi);
-static void wlc_phy_radio_postinit_2056(phy_info_t *pi);
-static void wlc_phy_radio_init_2057(phy_info_t *pi);
-static void wlc_phy_radio_postinit_2057(phy_info_t *pi);
-static void wlc_phy_workarounds_nphy(phy_info_t *pi);
-static void wlc_phy_workarounds_nphy_gainctrl(phy_info_t *pi);
-static void wlc_phy_workarounds_nphy_gainctrl_2057_rev5(phy_info_t *pi);
-static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(phy_info_t *pi);
-static void wlc_phy_adjust_lnagaintbl_nphy(phy_info_t *pi);
-
-static void wlc_phy_restore_rssical_nphy(phy_info_t *pi);
-static void wlc_phy_reapply_txcal_coeffs_nphy(phy_info_t *pi);
-static void wlc_phy_tx_iq_war_nphy(phy_info_t *pi);
-static int wlc_phy_cal_rxiq_nphy_rev3(phy_info_t *pi, nphy_txgains_t tg,
- u8 type, bool d);
-static void wlc_phy_rxcal_gainctrl_nphy_rev5(phy_info_t *pi, u8 rxcore,
- u16 *rg, u8 type);
-static void wlc_phy_update_mimoconfig_nphy(phy_info_t *pi, s32 preamble);
-static void wlc_phy_savecal_nphy(phy_info_t *pi);
-static void wlc_phy_restorecal_nphy(phy_info_t *pi);
-static void wlc_phy_resetcca_nphy(phy_info_t *pi);
-
-static void wlc_phy_txpwrctrl_config_nphy(phy_info_t *pi);
-static void wlc_phy_internal_cal_txgain_nphy(phy_info_t *pi);
-static void wlc_phy_precal_txgain_nphy(phy_info_t *pi);
-static void wlc_phy_update_txcal_ladder_nphy(phy_info_t *pi, u16 core);
-
-static void wlc_phy_extpa_set_tx_digi_filts_nphy(phy_info_t *pi);
-static void wlc_phy_ipa_set_tx_digi_filts_nphy(phy_info_t *pi);
-static void wlc_phy_ipa_restore_tx_digi_filts_nphy(phy_info_t *pi);
-static u16 wlc_phy_ipa_get_bbmult_nphy(phy_info_t *pi);
-static void wlc_phy_ipa_set_bbmult_nphy(phy_info_t *pi, u8 m0, u8 m1);
-static u32 *wlc_phy_get_ipa_gaintbl_nphy(phy_info_t *pi);
-
-static void wlc_phy_a1_nphy(phy_info_t *pi, u8 core, u32 winsz, u32,
- u32 e);
-static u8 wlc_phy_a3_nphy(phy_info_t *pi, u8 start_gain, u8 core);
-static void wlc_phy_a2_nphy(phy_info_t *pi, nphy_ipa_txcalgains_t *,
- phy_cal_mode_t, u8);
-static void wlc_phy_papd_cal_cleanup_nphy(phy_info_t *pi,
- nphy_papd_restore_state *state);
-static void wlc_phy_papd_cal_setup_nphy(phy_info_t *pi,
- nphy_papd_restore_state *state, u8);
-
-static void wlc_phy_clip_det_nphy(phy_info_t *pi, u8 write, u16 *vals);
-
-static void wlc_phy_set_rfseq_nphy(phy_info_t *pi, u8 cmd, u8 *evts,
- u8 *dlys, u8 len);
-
-static u16 wlc_phy_read_lpf_bw_ctl_nphy(phy_info_t *pi, u16 offset);
+ 0x09, 0x0a, 0x09, 0x0a, 0x15, 0x16
+};
-static void
-wlc_phy_rfctrl_override_nphy_rev7(phy_info_t *pi, u16 field, u16 value,
- u8 core_mask, u8 off,
- u8 override_id);
-
-static void wlc_phy_rssi_cal_nphy_rev2(phy_info_t *pi, u8 rssi_type);
-static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi);
-
-static bool wlc_phy_txpwr_srom_read_nphy(phy_info_t *pi);
-static void wlc_phy_txpwr_nphy_srom_convert(u8 *srom_max,
- u16 *pwr_offset,
- u8 tmp_max_pwr, u8 rate_start,
- u8 rate_end);
-
-static void wlc_phy_txpwr_limit_to_tbl_nphy(phy_info_t *pi);
-static void wlc_phy_txpwrctrl_coeff_setup_nphy(phy_info_t *pi);
-static void wlc_phy_txpwrctrl_idle_tssi_nphy(phy_info_t *pi);
-static void wlc_phy_txpwrctrl_pwr_setup_nphy(phy_info_t *pi);
-
-static bool wlc_phy_txpwr_ison_nphy(phy_info_t *pi);
-static u8 wlc_phy_txpwr_idx_cur_get_nphy(phy_info_t *pi, u8 core);
-static void wlc_phy_txpwr_idx_cur_set_nphy(phy_info_t *pi, u8 idx0,
- u8 idx1);
-static void wlc_phy_a4(phy_info_t *pi, bool full_cal);
-
-static u16 wlc_phy_radio205x_rcal(phy_info_t *pi);
-
-static u16 wlc_phy_radio2057_rccal(phy_info_t *pi);
-
-static u16 wlc_phy_gen_load_samples_nphy(phy_info_t *pi, u32 f_kHz,
- u16 max_val,
- u8 dac_test_mode);
-static void wlc_phy_loadsampletable_nphy(phy_info_t *pi, cs32 *tone_buf,
- u16 num_samps);
-static void wlc_phy_runsamples_nphy(phy_info_t *pi, u16 n, u16 lps,
- u16 wait, u8 iq, u8 dac_test_mode,
- bool modify_bbmult);
-
-bool wlc_phy_bist_check_phy(wlc_phy_t *pih)
+bool wlc_phy_bist_check_phy(struct brcms_phy_pub *pih)
{
- phy_info_t *pi = (phy_info_t *) pih;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
u32 phybist0, phybist1, phybist2, phybist3, phybist4;
if (NREV_GE(pi->pubpi.phy_rev, 16))
@@ -14207,14 +14131,13 @@ bool wlc_phy_bist_check_phy(wlc_phy_t *pih)
phybist4 = read_phy_reg(pi, 0x156);
if ((phybist0 == 0) && (phybist1 == 0x4000) && (phybist2 == 0x1fe0) &&
- (phybist3 == 0) && (phybist4 == 0)) {
+ (phybist3 == 0) && (phybist4 == 0))
return true;
- }
return false;
}
-static void WLBANDINITFN(wlc_phy_bphy_init_nphy) (phy_info_t *pi)
+static void wlc_phy_bphy_init_nphy(struct brcms_phy *pi)
{
u16 addr, val;
@@ -14228,25 +14151,14 @@ static void WLBANDINITFN(wlc_phy_bphy_init_nphy) (phy_info_t *pi)
val -= 0x0202;
}
- if (NORADIO_ENAB(pi->pubpi)) {
-
- write_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_PHYCRSTH, 0x3206);
-
- write_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_RSSI_TRESH, 0x281e);
-
- or_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_LNA_GAIN_RANGE, 0x1a);
-
- } else {
-
- write_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_STEP, 0x668);
- }
+ write_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_STEP, 0x668);
}
void
-wlc_phy_table_write_nphy(phy_info_t *pi, u32 id, u32 len, u32 offset,
+wlc_phy_table_write_nphy(struct brcms_phy *pi, u32 id, u32 len, u32 offset,
u32 width, const void *data)
{
- mimophytbl_info_t tbl;
+ struct phytbl_info tbl;
tbl.tbl_id = id;
tbl.tbl_len = len;
@@ -14257,10 +14169,10 @@ wlc_phy_table_write_nphy(phy_info_t *pi, u32 id, u32 len, u32 offset,
}
void
-wlc_phy_table_read_nphy(phy_info_t *pi, u32 id, u32 len, u32 offset,
+wlc_phy_table_read_nphy(struct brcms_phy *pi, u32 id, u32 len, u32 offset,
u32 width, void *data)
{
- mimophytbl_info_t tbl;
+ struct phytbl_info tbl;
tbl.tbl_id = id;
tbl.tbl_len = len;
@@ -14270,7 +14182,8 @@ wlc_phy_table_read_nphy(phy_info_t *pi, u32 id, u32 len, u32 offset,
wlc_phy_read_table_nphy(pi, &tbl);
}
-static void WLBANDINITFN(wlc_phy_static_table_download_nphy) (phy_info_t *pi)
+static void
+wlc_phy_static_table_download_nphy(struct brcms_phy *pi)
{
uint idx;
@@ -14293,7 +14206,7 @@ static void WLBANDINITFN(wlc_phy_static_table_download_nphy) (phy_info_t *pi)
}
}
-static void WLBANDINITFN(wlc_phy_tbl_init_nphy) (phy_info_t *pi)
+static void wlc_phy_tbl_init_nphy(struct brcms_phy *pi)
{
uint idx = 0;
u8 antswctrllut;
@@ -14304,7 +14217,8 @@ static void WLBANDINITFN(wlc_phy_tbl_init_nphy) (phy_info_t *pi)
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
antswctrllut = CHSPEC_IS2G(pi->radio_chanspec) ?
- pi->srom_fem2g.antswctrllut : pi->srom_fem5g.antswctrllut;
+ pi->srom_fem2g.antswctrllut : pi->srom_fem5g.
+ antswctrllut;
switch (antswctrllut) {
case 0:
@@ -14313,20 +14227,20 @@ static void WLBANDINITFN(wlc_phy_tbl_init_nphy) (phy_info_t *pi)
case 1:
- if (pi->aa2g == 7) {
+ if (pi->aa2g == 7)
+ wlc_phy_table_write_nphy(
+ pi,
+ NPHY_TBL_ID_ANTSWCTRLLUT,
+ 2, 0x21, 8,
+ &ant_sw_ctrl_tbl_rev8_2o3[0]);
+ else
+ wlc_phy_table_write_nphy(
+ pi,
+ NPHY_TBL_ID_ANTSWCTRLLUT,
+ 2, 0x21, 8,
+ &ant_sw_ctrl_tbl_rev8
+ [0]);
- wlc_phy_table_write_nphy(pi,
- NPHY_TBL_ID_ANTSWCTRLLUT,
- 2, 0x21, 8,
- &ant_sw_ctrl_tbl_rev8_2o3
- [0]);
- } else {
- wlc_phy_table_write_nphy(pi,
- NPHY_TBL_ID_ANTSWCTRLLUT,
- 2, 0x21, 8,
- &ant_sw_ctrl_tbl_rev8
- [0]);
- }
wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT,
2, 0x25, 8,
&ant_sw_ctrl_tbl_rev8[2]);
@@ -14337,31 +14251,31 @@ static void WLBANDINITFN(wlc_phy_tbl_init_nphy) (phy_info_t *pi)
case 2:
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT,
- 2, 0x1, 8,
- &ant_sw_ctrl_tbl_rev8_2057v7_core0
- [0]);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT,
- 2, 0x5, 8,
- &ant_sw_ctrl_tbl_rev8_2057v7_core0
- [2]);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT,
- 2, 0x9, 8,
- &ant_sw_ctrl_tbl_rev8_2057v7_core0
- [4]);
-
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT,
- 2, 0x21, 8,
- &ant_sw_ctrl_tbl_rev8_2057v7_core1
- [0]);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT,
- 2, 0x25, 8,
- &ant_sw_ctrl_tbl_rev8_2057v7_core1
- [2]);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT,
- 2, 0x29, 8,
- &ant_sw_ctrl_tbl_rev8_2057v7_core1
- [4]);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_ANTSWCTRLLUT,
+ 2, 0x1, 8,
+ &ant_sw_ctrl_tbl_rev8_2057v7_core0[0]);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_ANTSWCTRLLUT,
+ 2, 0x5, 8,
+ &ant_sw_ctrl_tbl_rev8_2057v7_core0[2]);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_ANTSWCTRLLUT,
+ 2, 0x9, 8,
+ &ant_sw_ctrl_tbl_rev8_2057v7_core0[4]);
+
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_ANTSWCTRLLUT,
+ 2, 0x21, 8,
+ &ant_sw_ctrl_tbl_rev8_2057v7_core1[0]);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_ANTSWCTRLLUT,
+ 2, 0x25, 8,
+ &ant_sw_ctrl_tbl_rev8_2057v7_core1[2]);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_ANTSWCTRLLUT,
+ 2, 0x29, 8,
+ &ant_sw_ctrl_tbl_rev8_2057v7_core1[4]);
break;
default:
@@ -14372,56 +14286,60 @@ static void WLBANDINITFN(wlc_phy_tbl_init_nphy) (phy_info_t *pi)
for (idx = 0; idx < mimophytbl_info_sz_rev3_volatile; idx++) {
if (idx == ANT_SWCTRL_TBL_REV3_IDX) {
- antswctrllut = CHSPEC_IS2G(pi->radio_chanspec) ?
- pi->srom_fem2g.antswctrllut : pi->
- srom_fem5g.antswctrllut;
+ antswctrllut =
+ CHSPEC_IS2G(pi->radio_chanspec) ?
+ pi->srom_fem2g.antswctrllut :
+ pi->srom_fem5g.antswctrllut;
switch (antswctrllut) {
case 0:
- wlc_phy_write_table_nphy(pi,
- &mimophytbl_info_rev3_volatile
- [idx]);
+ wlc_phy_write_table_nphy(
+ pi,
+ &mimophytbl_info_rev3_volatile
+ [idx]);
break;
case 1:
- wlc_phy_write_table_nphy(pi,
- &mimophytbl_info_rev3_volatile1
- [idx]);
+ wlc_phy_write_table_nphy(
+ pi,
+ &mimophytbl_info_rev3_volatile1
+ [idx]);
break;
case 2:
- wlc_phy_write_table_nphy(pi,
- &mimophytbl_info_rev3_volatile2
- [idx]);
+ wlc_phy_write_table_nphy(
+ pi,
+ &mimophytbl_info_rev3_volatile2
+ [idx]);
break;
case 3:
- wlc_phy_write_table_nphy(pi,
- &mimophytbl_info_rev3_volatile3
- [idx]);
+ wlc_phy_write_table_nphy(
+ pi,
+ &mimophytbl_info_rev3_volatile3
+ [idx]);
break;
default:
break;
}
} else {
- wlc_phy_write_table_nphy(pi,
- &mimophytbl_info_rev3_volatile
- [idx]);
+ wlc_phy_write_table_nphy(
+ pi,
+ &mimophytbl_info_rev3_volatile[idx]);
}
}
} else {
- for (idx = 0; idx < mimophytbl_info_sz_rev0_volatile; idx++) {
+ for (idx = 0; idx < mimophytbl_info_sz_rev0_volatile; idx++)
wlc_phy_write_table_nphy(pi,
&mimophytbl_info_rev0_volatile
[idx]);
- }
}
}
static void
-wlc_phy_write_txmacreg_nphy(phy_info_t *pi, u16 holdoff, u16 delay)
+wlc_phy_write_txmacreg_nphy(struct brcms_phy *pi, u16 holdoff, u16 delay)
{
write_phy_reg(pi, 0x77, holdoff);
write_phy_reg(pi, 0xb4, delay);
}
-void wlc_phy_nphy_tkip_rifs_war(phy_info_t *pi, u8 rifs)
+void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs)
{
u16 holdoff, delay;
@@ -14437,37 +14355,419 @@ void wlc_phy_nphy_tkip_rifs_war(phy_info_t *pi, u8 rifs)
wlc_phy_write_txmacreg_nphy(pi, holdoff, delay);
- if (pi && pi->sh && (pi->sh->_rifs_phy != rifs)) {
+ if (pi && pi->sh && (pi->sh->_rifs_phy != rifs))
pi->sh->_rifs_phy = rifs;
+}
+
+static void wlc_phy_txpwrctrl_config_nphy(struct brcms_phy *pi)
+{
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ pi->nphy_txpwrctrl = PHY_TPC_HW_ON;
+ pi->phy_5g_pwrgain = true;
+ return;
+ }
+
+ pi->nphy_txpwrctrl = PHY_TPC_HW_OFF;
+ pi->phy_5g_pwrgain = false;
+
+ if ((pi->sh->boardflags2 & BFL2_TXPWRCTRL_EN) &&
+ NREV_GE(pi->pubpi.phy_rev, 2) && (pi->sh->sromrev >= 4))
+ pi->nphy_txpwrctrl = PHY_TPC_HW_ON;
+ else if ((pi->sh->sromrev >= 4)
+ && (pi->sh->boardflags2 & BFL2_5G_PWRGAIN))
+ pi->phy_5g_pwrgain = true;
+}
+
+static void wlc_phy_txpwr_srom_read_ppr_nphy(struct brcms_phy *pi)
+{
+ u16 bw40po, cddpo, stbcpo, bwduppo;
+ uint band_num;
+ struct phy_shim_info *shim = pi->sh->physhim;
+
+ if (pi->sh->sromrev >= 9)
+ return;
+
+ bw40po = (u16) wlapi_getintvar(shim, BRCMS_SROM_BW40PO);
+ pi->bw402gpo = bw40po & 0xf;
+ pi->bw405gpo = (bw40po & 0xf0) >> 4;
+ pi->bw405glpo = (bw40po & 0xf00) >> 8;
+ pi->bw405ghpo = (bw40po & 0xf000) >> 12;
+
+ cddpo = (u16) wlapi_getintvar(shim, BRCMS_SROM_CDDPO);
+ pi->cdd2gpo = cddpo & 0xf;
+ pi->cdd5gpo = (cddpo & 0xf0) >> 4;
+ pi->cdd5glpo = (cddpo & 0xf00) >> 8;
+ pi->cdd5ghpo = (cddpo & 0xf000) >> 12;
+
+ stbcpo = (u16) wlapi_getintvar(shim, BRCMS_SROM_STBCPO);
+ pi->stbc2gpo = stbcpo & 0xf;
+ pi->stbc5gpo = (stbcpo & 0xf0) >> 4;
+ pi->stbc5glpo = (stbcpo & 0xf00) >> 8;
+ pi->stbc5ghpo = (stbcpo & 0xf000) >> 12;
+
+ bwduppo = (u16) wlapi_getintvar(shim, BRCMS_SROM_BWDUPPO);
+ pi->bwdup2gpo = bwduppo & 0xf;
+ pi->bwdup5gpo = (bwduppo & 0xf0) >> 4;
+ pi->bwdup5glpo = (bwduppo & 0xf00) >> 8;
+ pi->bwdup5ghpo = (bwduppo & 0xf000) >> 12;
+
+ for (band_num = 0; band_num < (CH_2G_GROUP + CH_5G_GROUP);
+ band_num++) {
+ switch (band_num) {
+ case 0:
+
+ pi->nphy_txpid2g[PHY_CORE_0] =
+ (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_TXPID2GA0);
+ pi->nphy_txpid2g[PHY_CORE_1] =
+ (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_TXPID2GA1);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_2g =
+ (s8) wlapi_getintvar(shim,
+ BRCMS_SROM_MAXP2GA0);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_2g =
+ (s8) wlapi_getintvar(shim,
+ BRCMS_SROM_MAXP2GA1);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_a1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA2GW0A0);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_a1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA2GW0A1);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_b0 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA2GW1A0);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_b0 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA2GW1A1);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_b1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA2GW2A0);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_b1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA2GW2A1);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].idle_targ_2g =
+ (s8) wlapi_getintvar(shim, BRCMS_SROM_ITT2GA0);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].idle_targ_2g =
+ (s8) wlapi_getintvar(shim, BRCMS_SROM_ITT2GA1);
+
+ pi->cck2gpo = (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_CCK2GPO);
+
+ pi->ofdm2gpo =
+ (u32) wlapi_getintvar(shim,
+ BRCMS_SROM_OFDM2GPO);
+
+ pi->mcs2gpo[0] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS2GPO0);
+ pi->mcs2gpo[1] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS2GPO1);
+ pi->mcs2gpo[2] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS2GPO2);
+ pi->mcs2gpo[3] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS2GPO3);
+ pi->mcs2gpo[4] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS2GPO4);
+ pi->mcs2gpo[5] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS2GPO5);
+ pi->mcs2gpo[6] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS2GPO6);
+ pi->mcs2gpo[7] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS2GPO7);
+ break;
+ case 1:
+
+ pi->nphy_txpid5g[PHY_CORE_0] =
+ (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_TXPID5GA0);
+ pi->nphy_txpid5g[PHY_CORE_1] =
+ (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_TXPID5GA1);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_5gm =
+ (s8) wlapi_getintvar(shim, BRCMS_SROM_MAXP5GA0);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_5gm =
+ (s8) wlapi_getintvar(shim,
+ BRCMS_SROM_MAXP5GA1);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_a1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GW0A0);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_a1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GW0A1);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_b0 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GW1A0);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_b0 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GW1A1);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_b1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GW2A0);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_b1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GW2A1);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].idle_targ_5gm =
+ (s8) wlapi_getintvar(shim, BRCMS_SROM_ITT5GA0);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].idle_targ_5gm =
+ (s8) wlapi_getintvar(shim, BRCMS_SROM_ITT5GA1);
+
+ pi->ofdm5gpo =
+ (u32) wlapi_getintvar(shim,
+ BRCMS_SROM_OFDM5GPO);
+
+ pi->mcs5gpo[0] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GPO0);
+ pi->mcs5gpo[1] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GPO1);
+ pi->mcs5gpo[2] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GPO2);
+ pi->mcs5gpo[3] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GPO3);
+ pi->mcs5gpo[4] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GPO4);
+ pi->mcs5gpo[5] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GPO5);
+ pi->mcs5gpo[6] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GPO6);
+ pi->mcs5gpo[7] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GPO7);
+ break;
+ case 2:
+
+ pi->nphy_txpid5gl[0] =
+ (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_TXPID5GLA0);
+ pi->nphy_txpid5gl[1] =
+ (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_TXPID5GLA1);
+ pi->nphy_pwrctrl_info[0].max_pwr_5gl =
+ (s8) wlapi_getintvar(shim,
+ BRCMS_SROM_MAXP5GLA0);
+ pi->nphy_pwrctrl_info[1].max_pwr_5gl =
+ (s8) wlapi_getintvar(shim,
+ BRCMS_SROM_MAXP5GLA1);
+ pi->nphy_pwrctrl_info[0].pwrdet_5gl_a1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GLW0A0);
+ pi->nphy_pwrctrl_info[1].pwrdet_5gl_a1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GLW0A1);
+ pi->nphy_pwrctrl_info[0].pwrdet_5gl_b0 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GLW1A0);
+ pi->nphy_pwrctrl_info[1].pwrdet_5gl_b0 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GLW1A1);
+ pi->nphy_pwrctrl_info[0].pwrdet_5gl_b1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GLW2A0);
+ pi->nphy_pwrctrl_info[1].pwrdet_5gl_b1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GLW2A1);
+ pi->nphy_pwrctrl_info[0].idle_targ_5gl = 0;
+ pi->nphy_pwrctrl_info[1].idle_targ_5gl = 0;
+
+ pi->ofdm5glpo =
+ (u32) wlapi_getintvar(shim,
+ BRCMS_SROM_OFDM5GLPO);
+
+ pi->mcs5glpo[0] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GLPO0);
+ pi->mcs5glpo[1] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GLPO1);
+ pi->mcs5glpo[2] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GLPO2);
+ pi->mcs5glpo[3] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GLPO3);
+ pi->mcs5glpo[4] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GLPO4);
+ pi->mcs5glpo[5] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GLPO5);
+ pi->mcs5glpo[6] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GLPO6);
+ pi->mcs5glpo[7] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GLPO7);
+ break;
+ case 3:
+
+ pi->nphy_txpid5gh[0] =
+ (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_TXPID5GHA0);
+ pi->nphy_txpid5gh[1] =
+ (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_TXPID5GHA1);
+ pi->nphy_pwrctrl_info[0].max_pwr_5gh =
+ (s8) wlapi_getintvar(shim,
+ BRCMS_SROM_MAXP5GHA0);
+ pi->nphy_pwrctrl_info[1].max_pwr_5gh =
+ (s8) wlapi_getintvar(shim,
+ BRCMS_SROM_MAXP5GHA1);
+ pi->nphy_pwrctrl_info[0].pwrdet_5gh_a1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GHW0A0);
+ pi->nphy_pwrctrl_info[1].pwrdet_5gh_a1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GHW0A1);
+ pi->nphy_pwrctrl_info[0].pwrdet_5gh_b0 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GHW1A0);
+ pi->nphy_pwrctrl_info[1].pwrdet_5gh_b0 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GHW1A1);
+ pi->nphy_pwrctrl_info[0].pwrdet_5gh_b1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GHW2A0);
+ pi->nphy_pwrctrl_info[1].pwrdet_5gh_b1 =
+ (s16) wlapi_getintvar(shim,
+ BRCMS_SROM_PA5GHW2A1);
+ pi->nphy_pwrctrl_info[0].idle_targ_5gh = 0;
+ pi->nphy_pwrctrl_info[1].idle_targ_5gh = 0;
+
+ pi->ofdm5ghpo =
+ (u32) wlapi_getintvar(shim,
+ BRCMS_SROM_OFDM5GHPO);
+
+ pi->mcs5ghpo[0] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GHPO0);
+ pi->mcs5ghpo[1] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GHPO1);
+ pi->mcs5ghpo[2] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GHPO2);
+ pi->mcs5ghpo[3] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GHPO3);
+ pi->mcs5ghpo[4] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GHPO4);
+ pi->mcs5ghpo[5] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GHPO5);
+ pi->mcs5ghpo[6] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GHPO6);
+ pi->mcs5ghpo[7] =
+ (u16) wlapi_getintvar(shim,
+ BRCMS_SROM_MCS5GHPO7);
+ break;
+ }
+ }
+
+ wlc_phy_txpwr_apply_nphy(pi);
+}
+
+static bool wlc_phy_txpwr_srom_read_nphy(struct brcms_phy *pi)
+{
+ struct phy_shim_info *shim = pi->sh->physhim;
+
+ pi->antswitch = (u8) wlapi_getintvar(shim, BRCMS_SROM_ANTSWITCH);
+ pi->aa2g = (u8) wlapi_getintvar(shim, BRCMS_SROM_AA2G);
+ pi->aa5g = (u8) wlapi_getintvar(shim, BRCMS_SROM_AA5G);
+
+ pi->srom_fem2g.tssipos = (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_TSSIPOS2G);
+ pi->srom_fem2g.extpagain = (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_EXTPAGAIN2G);
+ pi->srom_fem2g.pdetrange = (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_PDETRANGE2G);
+ pi->srom_fem2g.triso = (u8) wlapi_getintvar(shim, BRCMS_SROM_TRISO2G);
+ pi->srom_fem2g.antswctrllut =
+ (u8) wlapi_getintvar(shim, BRCMS_SROM_ANTSWCTL2G);
+
+ pi->srom_fem5g.tssipos = (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_TSSIPOS5G);
+ pi->srom_fem5g.extpagain = (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_EXTPAGAIN5G);
+ pi->srom_fem5g.pdetrange = (u8) wlapi_getintvar(shim,
+ BRCMS_SROM_PDETRANGE5G);
+ pi->srom_fem5g.triso = (u8) wlapi_getintvar(shim, BRCMS_SROM_TRISO5G);
+ if (wlapi_getvar(shim, BRCMS_SROM_ANTSWCTL5G))
+ pi->srom_fem5g.antswctrllut =
+ (u8) wlapi_getintvar(shim, BRCMS_SROM_ANTSWCTL5G);
+ else
+ pi->srom_fem5g.antswctrllut =
+ (u8) wlapi_getintvar(shim, BRCMS_SROM_ANTSWCTL2G);
+
+ wlc_phy_txpower_ipa_upd(pi);
+
+ pi->phy_txcore_disable_temp =
+ (s16) wlapi_getintvar(shim, BRCMS_SROM_TEMPTHRESH);
+ if (pi->phy_txcore_disable_temp == 0)
+ pi->phy_txcore_disable_temp = PHY_CHAIN_TX_DISABLE_TEMP;
+
+ pi->phy_tempsense_offset = (s8) wlapi_getintvar(shim,
+ BRCMS_SROM_TEMPOFFSET);
+ if (pi->phy_tempsense_offset != 0) {
+ if (pi->phy_tempsense_offset >
+ (NPHY_SROM_TEMPSHIFT + NPHY_SROM_MAXTEMPOFFSET))
+ pi->phy_tempsense_offset = NPHY_SROM_MAXTEMPOFFSET;
+ else if (pi->phy_tempsense_offset < (NPHY_SROM_TEMPSHIFT +
+ NPHY_SROM_MINTEMPOFFSET))
+ pi->phy_tempsense_offset = NPHY_SROM_MINTEMPOFFSET;
+ else
+ pi->phy_tempsense_offset -= NPHY_SROM_TEMPSHIFT;
}
+
+ pi->phy_txcore_enable_temp =
+ pi->phy_txcore_disable_temp - PHY_HYSTERESIS_DELTATEMP;
+
+ pi->phycal_tempdelta =
+ (u8) wlapi_getintvar(shim, BRCMS_SROM_PHYCAL_TEMPDELTA);
+ if (pi->phycal_tempdelta > NPHY_CAL_MAXTEMPDELTA)
+ pi->phycal_tempdelta = 0;
+
+ wlc_phy_txpwr_srom_read_ppr_nphy(pi);
+
+ return true;
}
-bool wlc_phy_attach_nphy(phy_info_t *pi)
+bool wlc_phy_attach_nphy(struct brcms_phy *pi)
{
uint i;
- if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 6)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 6))
pi->phyhang_avoid = true;
- }
if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) {
-
pi->nphy_gband_spurwar_en = true;
-
- if (pi->sh->boardflags2 & BFL2_SPUR_WAR) {
+ if (pi->sh->boardflags2 & BFL2_SPUR_WAR)
pi->nphy_aband_spurwar_en = true;
- }
}
if (NREV_GE(pi->pubpi.phy_rev, 6) && NREV_LT(pi->pubpi.phy_rev, 7)) {
-
- if (pi->sh->boardflags2 & BFL2_2G_SPUR_WAR) {
+ if (pi->sh->boardflags2 & BFL2_2G_SPUR_WAR)
pi->nphy_gband_spurwar2_en = true;
- }
}
pi->n_preamble_override = AUTO;
if (NREV_IS(pi->pubpi.phy_rev, 3) || NREV_IS(pi->pubpi.phy_rev, 4))
- pi->n_preamble_override = WLC_N_PREAMBLE_MIXEDMODE;
+ pi->n_preamble_override = BRCMS_N_PREAMBLE_MIXEDMODE;
pi->nphy_txrx_chain = AUTO;
pi->phy_scraminit = AUTO;
@@ -14482,9 +14782,8 @@ bool wlc_phy_attach_nphy(phy_info_t *pi)
pi->nphy_elna_gain_config = false;
pi->radio_is_on = false;
- for (i = 0; i < pi->pubpi.phy_corenum; i++) {
+ for (i = 0; i < pi->pubpi.phy_corenum; i++)
pi->nphy_txpwrindex[i].index = AUTO;
- }
wlc_phy_txpwrctrl_config_nphy(pi);
if (pi->nphy_txpwrctrl == PHY_TPC_HW_ON)
@@ -14501,761 +14800,1409 @@ bool wlc_phy_attach_nphy(phy_info_t *pi)
return true;
}
-static void wlc_phy_txpwrctrl_config_nphy(phy_info_t *pi)
+static s32 get_rf_pwr_offset(struct brcms_phy *pi, s16 pga_gn, s16 pad_gn)
{
+ s32 rfpwr_offset = 0;
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- pi->nphy_txpwrctrl = PHY_TPC_HW_ON;
- pi->phy_5g_pwrgain = true;
- return;
- }
-
- pi->nphy_txpwrctrl = PHY_TPC_HW_OFF;
- pi->phy_5g_pwrgain = false;
-
- if ((pi->sh->boardflags2 & BFL2_TXPWRCTRL_EN) &&
- NREV_GE(pi->pubpi.phy_rev, 2) && (pi->sh->sromrev >= 4))
- pi->nphy_txpwrctrl = PHY_TPC_HW_ON;
- else if ((pi->sh->sromrev >= 4)
- && (pi->sh->boardflags2 & BFL2_5G_PWRGAIN))
- pi->phy_5g_pwrgain = true;
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if ((pi->pubpi.radiorev == 3) ||
+ (pi->pubpi.radiorev == 4) ||
+ (pi->pubpi.radiorev == 6))
+ rfpwr_offset = (s16)
+ nphy_papd_padgain_dlt_2g_2057rev3n4
+ [pad_gn];
+ else if (pi->pubpi.radiorev == 5)
+ rfpwr_offset = (s16)
+ nphy_papd_padgain_dlt_2g_2057rev5
+ [pad_gn];
+ else if ((pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev ==
+ 8))
+ rfpwr_offset = (s16)
+ nphy_papd_padgain_dlt_2g_2057rev7
+ [pad_gn];
+ } else {
+ if ((pi->pubpi.radiorev == 3) ||
+ (pi->pubpi.radiorev == 4) ||
+ (pi->pubpi.radiorev == 6))
+ rfpwr_offset = (s16)
+ nphy_papd_pgagain_dlt_5g_2057
+ [pga_gn];
+ else if ((pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev ==
+ 8))
+ rfpwr_offset = (s16)
+ nphy_papd_pgagain_dlt_5g_2057rev7
+ [pga_gn];
+ }
+ return rfpwr_offset;
}
-void WLBANDINITFN(wlc_phy_init_nphy) (phy_info_t *pi)
+static void wlc_phy_update_mimoconfig_nphy(struct brcms_phy *pi, s32 preamble)
{
+ bool gf_preamble = false;
u16 val;
- u16 clip1_ths[2];
- nphy_txgains_t target_gain;
- u8 tx_pwr_ctrl_state;
- bool do_nphy_cal = false;
- uint core;
- uint origidx, intr_val;
- d11regs_t *regs;
- u32 d11_clk_ctl_st;
- core = 0;
+ if (preamble == BRCMS_N_PREAMBLE_GF)
+ gf_preamble = true;
- if (!(pi->measure_hold & PHY_HOLD_FOR_SCAN)) {
- pi->measure_hold |= PHY_HOLD_FOR_NOT_ASSOC;
- }
+ val = read_phy_reg(pi, 0xed);
- if ((ISNPHY(pi)) && (NREV_GE(pi->pubpi.phy_rev, 5)) &&
- ((pi->sh->chippkg == BCM4717_PKG_ID) ||
- (pi->sh->chippkg == BCM4718_PKG_ID))) {
- if ((pi->sh->boardflags & BFL_EXTLNA) &&
- (CHSPEC_IS2G(pi->radio_chanspec))) {
- ai_corereg(pi->sh->sih, SI_CC_IDX,
- offsetof(chipcregs_t, chipcontrol), 0x40,
- 0x40);
- }
- }
+ val |= RX_GF_MM_AUTO;
+ val &= ~RX_GF_OR_MM;
+ if (gf_preamble)
+ val |= RX_GF_OR_MM;
- if ((!PHY_IPA(pi)) && (pi->sh->chip == BCM5357_CHIP_ID)) {
- si_pmu_chipcontrol(pi->sh->sih, 1, CCTRL5357_EXTPA,
- CCTRL5357_EXTPA);
- }
+ write_phy_reg(pi, 0xed, val);
+}
- if ((pi->nphy_gband_spurwar2_en) && CHSPEC_IS2G(pi->radio_chanspec) &&
- CHSPEC_IS40(pi->radio_chanspec)) {
+static void wlc_phy_ipa_set_tx_digi_filts_nphy(struct brcms_phy *pi)
+{
+ int j, type;
+ u16 addr_offset[] = { 0x186, 0x195, 0x2c5};
- regs = (d11regs_t *) ai_switch_core(pi->sh->sih, D11_CORE_ID,
- &origidx, &intr_val);
- d11_clk_ctl_st = R_REG(&regs->clk_ctl_st);
- AND_REG(&regs->clk_ctl_st,
- ~(CCS_FORCEHT | CCS_HTAREQ));
+ for (type = 0; type < 3; type++) {
+ for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++)
+ write_phy_reg(pi, addr_offset[type] + j,
+ NPHY_IPA_REV4_txdigi_filtcoeffs[type][j]);
+ }
- W_REG(&regs->clk_ctl_st, d11_clk_ctl_st);
+ if (pi->bw == WL_CHANSPEC_BW_40) {
+ for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++)
+ write_phy_reg(pi, 0x186 + j,
+ NPHY_IPA_REV4_txdigi_filtcoeffs[3][j]);
+ } else {
+ if (CHSPEC_IS5G(pi->radio_chanspec)) {
+ for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++)
+ write_phy_reg(pi, 0x186 + j,
+ NPHY_IPA_REV4_txdigi_filtcoeffs[5][j]);
+ }
- ai_restore_core(pi->sh->sih, origidx, intr_val);
+ if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) {
+ for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++)
+ write_phy_reg(pi, 0x2c5 + j,
+ NPHY_IPA_REV4_txdigi_filtcoeffs[6][j]);
+ }
}
+}
- pi->use_int_tx_iqlo_cal_nphy =
- (PHY_IPA(pi) ||
- (NREV_GE(pi->pubpi.phy_rev, 7) ||
- (NREV_GE(pi->pubpi.phy_rev, 5)
- && pi->sh->boardflags2 & BFL2_INTERNDET_TXIQCAL)));
+static void wlc_phy_ipa_restore_tx_digi_filts_nphy(struct brcms_phy *pi)
+{
+ int j;
- pi->internal_tx_iqlo_cal_tapoff_intpa_nphy = false;
+ if (pi->bw == WL_CHANSPEC_BW_40) {
+ for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++)
+ write_phy_reg(pi, 0x195 + j,
+ NPHY_IPA_REV4_txdigi_filtcoeffs[4][j]);
+ } else {
+ for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++)
+ write_phy_reg(pi, 0x186 + j,
+ NPHY_IPA_REV4_txdigi_filtcoeffs[3][j]);
+ }
+}
- pi->nphy_deaf_count = 0;
+static void
+wlc_phy_set_rfseq_nphy(struct brcms_phy *pi, u8 cmd, u8 *events, u8 *dlys,
+ u8 len)
+{
+ u32 t1_offset, t2_offset;
+ u8 ctr;
+ u8 end_event =
+ NREV_GE(pi->pubpi.phy_rev,
+ 3) ? NPHY_REV3_RFSEQ_CMD_END : NPHY_RFSEQ_CMD_END;
+ u8 end_dly = 1;
- wlc_phy_tbl_init_nphy(pi);
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, true);
- pi->nphy_crsminpwr_adjusted = false;
- pi->nphy_noisevars_adjusted = false;
+ t1_offset = cmd << 4;
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, len, t1_offset, 8,
+ events);
+ t2_offset = t1_offset + 0x080;
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, len, t2_offset, 8,
+ dlys);
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- write_phy_reg(pi, 0xe7, 0);
- write_phy_reg(pi, 0xec, 0);
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- write_phy_reg(pi, 0x342, 0);
- write_phy_reg(pi, 0x343, 0);
- write_phy_reg(pi, 0x346, 0);
- write_phy_reg(pi, 0x347, 0);
- }
- write_phy_reg(pi, 0xe5, 0);
- write_phy_reg(pi, 0xe6, 0);
- } else {
- write_phy_reg(pi, 0xec, 0);
+ for (ctr = len; ctr < 16; ctr++) {
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1,
+ t1_offset + ctr, 8, &end_event);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1,
+ t2_offset + ctr, 8, &end_dly);
}
- write_phy_reg(pi, 0x91, 0);
- write_phy_reg(pi, 0x92, 0);
- if (NREV_LT(pi->pubpi.phy_rev, 6)) {
- write_phy_reg(pi, 0x93, 0);
- write_phy_reg(pi, 0x94, 0);
- }
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, false);
+}
- and_phy_reg(pi, 0xa1, ~3);
+static u16 wlc_phy_read_lpf_bw_ctl_nphy(struct brcms_phy *pi, u16 offset)
+{
+ u16 lpf_bw_ctl_val = 0;
+ u16 rx2tx_lpf_rc_lut_offset = 0;
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- write_phy_reg(pi, 0x8f, 0);
- write_phy_reg(pi, 0xa5, 0);
+ if (offset == 0) {
+ if (CHSPEC_IS40(pi->radio_chanspec))
+ rx2tx_lpf_rc_lut_offset = 0x159;
+ else
+ rx2tx_lpf_rc_lut_offset = 0x154;
} else {
- write_phy_reg(pi, 0xa5, 0);
+ rx2tx_lpf_rc_lut_offset = offset;
}
+ wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 1,
+ (u32) rx2tx_lpf_rc_lut_offset, 16,
+ &lpf_bw_ctl_val);
- if (NREV_IS(pi->pubpi.phy_rev, 2))
- mod_phy_reg(pi, 0xdc, 0x00ff, 0x3b);
- else if (NREV_LT(pi->pubpi.phy_rev, 2))
- mod_phy_reg(pi, 0xdc, 0x00ff, 0x40);
+ lpf_bw_ctl_val = lpf_bw_ctl_val & 0x7;
- write_phy_reg(pi, 0x203, 32);
- write_phy_reg(pi, 0x201, 32);
+ return lpf_bw_ctl_val;
+}
- if (pi->sh->boardflags2 & BFL2_SKWRKFEM_BRD)
- write_phy_reg(pi, 0x20d, 160);
- else
- write_phy_reg(pi, 0x20d, 184);
+static void
+wlc_phy_rfctrl_override_nphy_rev7(struct brcms_phy *pi, u16 field, u16 value,
+ u8 core_mask, u8 off, u8 override_id)
+{
+ u8 core_num;
+ u16 addr = 0, en_addr = 0, val_addr = 0, en_mask = 0, val_mask = 0;
+ u8 val_shift = 0;
- write_phy_reg(pi, 0x13a, 200);
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ en_mask = field;
+ for (core_num = 0; core_num < 2; core_num++) {
+ if (override_id == NPHY_REV7_RFCTRLOVERRIDE_ID0) {
- write_phy_reg(pi, 0x70, 80);
+ switch (field) {
+ case (0x1 << 2):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a :
+ 0x7d;
+ val_mask = (0x1 << 1);
+ val_shift = 1;
+ break;
+ case (0x1 << 3):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a :
+ 0x7d;
+ val_mask = (0x1 << 2);
+ val_shift = 2;
+ break;
+ case (0x1 << 4):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a :
+ 0x7d;
+ val_mask = (0x1 << 4);
+ val_shift = 4;
+ break;
+ case (0x1 << 5):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a :
+ 0x7d;
+ val_mask = (0x1 << 5);
+ val_shift = 5;
+ break;
+ case (0x1 << 6):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a :
+ 0x7d;
+ val_mask = (0x1 << 6);
+ val_shift = 6;
+ break;
+ case (0x1 << 7):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a :
+ 0x7d;
+ val_mask = (0x1 << 7);
+ val_shift = 7;
+ break;
+ case (0x1 << 10):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0xf8 :
+ 0xfa;
+ val_mask = (0x7 << 4);
+ val_shift = 4;
+ break;
+ case (0x1 << 11):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7b :
+ 0x7e;
+ val_mask = (0xffff << 0);
+ val_shift = 0;
+ break;
+ case (0x1 << 12):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7c :
+ 0x7f;
+ val_mask = (0xffff << 0);
+ val_shift = 0;
+ break;
+ case (0x3 << 13):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x348 :
+ 0x349;
+ val_mask = (0xff << 0);
+ val_shift = 0;
+ break;
+ case (0x1 << 13):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x348 :
+ 0x349;
+ val_mask = (0xf << 0);
+ val_shift = 0;
+ break;
+ default:
+ addr = 0xffff;
+ break;
+ }
+ } else if (override_id ==
+ NPHY_REV7_RFCTRLOVERRIDE_ID1) {
- write_phy_reg(pi, 0x1ff, 48);
+ switch (field) {
+ case (0x1 << 1):
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 1);
+ val_shift = 1;
+ break;
+ case (0x1 << 3):
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 3);
+ val_shift = 3;
+ break;
+ case (0x1 << 5):
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 5);
+ val_shift = 5;
+ break;
+ case (0x1 << 4):
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 4);
+ val_shift = 4;
+ break;
+ case (0x1 << 2):
- if (NREV_LT(pi->pubpi.phy_rev, 8)) {
- wlc_phy_update_mimoconfig_nphy(pi, pi->n_preamble_override);
- }
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 2);
+ val_shift = 2;
+ break;
+ case (0x1 << 7):
- wlc_phy_stf_chain_upd_nphy(pi);
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x7 << 8);
+ val_shift = 8;
+ break;
+ case (0x1 << 11):
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 14);
+ val_shift = 14;
+ break;
+ case (0x1 << 10):
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 13);
+ val_shift = 13;
+ break;
+ case (0x1 << 9):
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 12);
+ val_shift = 12;
+ break;
+ case (0x1 << 8):
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 11);
+ val_shift = 11;
+ break;
+ case (0x1 << 6):
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 6);
+ val_shift = 6;
+ break;
+ case (0x1 << 0):
+ en_addr = (core_num == 0) ? 0x342 :
+ 0x343;
+ val_addr = (core_num == 0) ? 0x340 :
+ 0x341;
+ val_mask = (0x1 << 0);
+ val_shift = 0;
+ break;
+ default:
+ addr = 0xffff;
+ break;
+ }
+ } else if (override_id ==
+ NPHY_REV7_RFCTRLOVERRIDE_ID2) {
- if (NREV_LT(pi->pubpi.phy_rev, 2)) {
- write_phy_reg(pi, 0x180, 0xaa8);
- write_phy_reg(pi, 0x181, 0x9a4);
+ switch (field) {
+ case (0x1 << 3):
+ en_addr = (core_num == 0) ? 0x346 :
+ 0x347;
+ val_addr = (core_num == 0) ? 0x344 :
+ 0x345;
+ val_mask = (0x1 << 3);
+ val_shift = 3;
+ break;
+ case (0x1 << 1):
+ en_addr = (core_num == 0) ? 0x346 :
+ 0x347;
+ val_addr = (core_num == 0) ? 0x344 :
+ 0x345;
+ val_mask = (0x1 << 1);
+ val_shift = 1;
+ break;
+ case (0x1 << 0):
+ en_addr = (core_num == 0) ? 0x346 :
+ 0x347;
+ val_addr = (core_num == 0) ? 0x344 :
+ 0x345;
+ val_mask = (0x1 << 0);
+ val_shift = 0;
+ break;
+ case (0x1 << 2):
+ en_addr = (core_num == 0) ? 0x346 :
+ 0x347;
+ val_addr = (core_num == 0) ? 0x344 :
+ 0x345;
+ val_mask = (0x1 << 2);
+ val_shift = 2;
+ break;
+ case (0x1 << 4):
+ en_addr = (core_num == 0) ? 0x346 :
+ 0x347;
+ val_addr = (core_num == 0) ? 0x344 :
+ 0x345;
+ val_mask = (0x1 << 4);
+ val_shift = 4;
+ break;
+ default:
+ addr = 0xffff;
+ break;
+ }
+ }
+
+ if (off) {
+ and_phy_reg(pi, en_addr, ~en_mask);
+ and_phy_reg(pi, val_addr, ~val_mask);
+ } else {
+
+ if ((core_mask == 0)
+ || (core_mask & (1 << core_num))) {
+ or_phy_reg(pi, en_addr, en_mask);
+
+ if (addr != 0xffff)
+ mod_phy_reg(pi, val_addr,
+ val_mask,
+ (value <<
+ val_shift));
+ }
+ }
+ }
}
+}
- if (PHY_IPA(pi)) {
- for (core = 0; core < pi->pubpi.phy_corenum; core++) {
+static void wlc_phy_adjust_lnagaintbl_nphy(struct brcms_phy *pi)
+{
+ uint core;
+ int ctr;
+ s16 gain_delta[2];
+ u8 curr_channel;
+ u16 minmax_gain[2];
+ u16 regval[4];
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (1) << 0);
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, true);
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x298 :
- 0x29c, (0x1ff << 7),
- (pi->nphy_papd_epsilon_offset[core]) << 7);
+ if (pi->nphy_gain_boost) {
+ if ((CHSPEC_IS2G(pi->radio_chanspec))) {
- }
+ gain_delta[0] = 6;
+ gain_delta[1] = 6;
+ } else {
- wlc_phy_ipa_set_tx_digi_filts_nphy(pi);
+ curr_channel = CHSPEC_CHANNEL(pi->radio_chanspec);
+ gain_delta[0] =
+ (s16)
+ PHY_HW_ROUND(((nphy_lnagain_est0[0] *
+ curr_channel) +
+ nphy_lnagain_est0[1]), 13);
+ gain_delta[1] =
+ (s16)
+ PHY_HW_ROUND(((nphy_lnagain_est1[0] *
+ curr_channel) +
+ nphy_lnagain_est1[1]), 13);
+ }
} else {
- if (NREV_GE(pi->pubpi.phy_rev, 5)) {
- wlc_phy_extpa_set_tx_digi_filts_nphy(pi);
- }
+ gain_delta[0] = 0;
+ gain_delta[1] = 0;
}
- wlc_phy_workarounds_nphy(pi);
-
- wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON);
-
- val = read_phy_reg(pi, 0x01);
- write_phy_reg(pi, 0x01, val | BBCFG_RESETCCA);
- write_phy_reg(pi, 0x01, val & (~BBCFG_RESETCCA));
- wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF);
+ for (core = 0; core < pi->pubpi.phy_corenum; core++) {
+ if (pi->nphy_elna_gain_config) {
- wlapi_bmac_macphyclk_set(pi->sh->physhim, ON);
+ regval[0] = nphy_def_lnagains[2] + gain_delta[core];
+ regval[1] = nphy_def_lnagains[3] + gain_delta[core];
+ regval[2] = nphy_def_lnagains[3] + gain_delta[core];
+ regval[3] = nphy_def_lnagains[3] + gain_delta[core];
+ } else {
+ for (ctr = 0; ctr < 4; ctr++)
+ regval[ctr] =
+ nphy_def_lnagains[ctr] +
+ gain_delta[core];
+ }
+ wlc_phy_table_write_nphy(pi, core, 4, 8, 16, regval);
- wlc_phy_pa_override_nphy(pi, OFF);
- wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX);
- wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX);
- wlc_phy_pa_override_nphy(pi, ON);
+ minmax_gain[core] =
+ (u16) (nphy_def_lnagains[2] + gain_delta[core] + 4);
+ }
- wlc_phy_classifier_nphy(pi, 0, 0);
- wlc_phy_clip_det_nphy(pi, 0, clip1_ths);
+ mod_phy_reg(pi, 0x1e, (0xff << 0), (minmax_gain[0] << 0));
+ mod_phy_reg(pi, 0x34, (0xff << 0), (minmax_gain[1] << 0));
- if (CHSPEC_IS2G(pi->radio_chanspec))
- wlc_phy_bphy_init_nphy(pi);
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, false);
+}
- tx_pwr_ctrl_state = pi->nphy_txpwrctrl;
- wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF);
+static void
+wlc_phy_war_force_trsw_to_R_cliplo_nphy(struct brcms_phy *pi, u8 core)
+{
+ if (core == PHY_CORE_0) {
+ write_phy_reg(pi, 0x38, 0x4);
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ write_phy_reg(pi, 0x37, 0x0060);
+ else
+ write_phy_reg(pi, 0x37, 0x1080);
+ } else if (core == PHY_CORE_1) {
+ write_phy_reg(pi, 0x2ae, 0x4);
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ write_phy_reg(pi, 0x2ad, 0x0060);
+ else
+ write_phy_reg(pi, 0x2ad, 0x1080);
+ }
+}
- wlc_phy_txpwr_fixpower_nphy(pi);
+static void wlc_phy_war_txchain_upd_nphy(struct brcms_phy *pi, u8 txchain)
+{
+ u8 txchain0, txchain1;
- wlc_phy_txpwrctrl_idle_tssi_nphy(pi);
+ txchain0 = txchain & 0x1;
+ txchain1 = (txchain & 0x2) >> 1;
+ if (!txchain0)
+ wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_0);
- wlc_phy_txpwrctrl_pwr_setup_nphy(pi);
+ if (!txchain1)
+ wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_1);
+}
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- u32 *tx_pwrctrl_tbl = NULL;
- u16 idx;
- s16 pga_gn = 0;
- s16 pad_gn = 0;
- s32 rfpwr_offset = 0;
+static void wlc_phy_workarounds_nphy_gainctrl_2057_rev5(struct brcms_phy *pi)
+{
+ s8 lna1_gain_db[] = { 8, 13, 17, 22 };
+ s8 lna2_gain_db[] = { -2, 7, 11, 15 };
+ s8 tia_gain_db[] = { -4, -1, 2, 5, 5, 5, 5, 5, 5, 5 };
+ s8 tia_gainbits[] = {
+ 0x0, 0x01, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 };
- if (PHY_IPA(pi)) {
- tx_pwrctrl_tbl = wlc_phy_get_ipa_gaintbl_nphy(pi);
- } else {
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
- if NREV_IS
- (pi->pubpi.phy_rev, 3) {
- tx_pwrctrl_tbl =
- nphy_tpc_5GHz_txgain_rev3;
- } else if NREV_IS
- (pi->pubpi.phy_rev, 4) {
- tx_pwrctrl_tbl =
- (pi->srom_fem5g.extpagain == 3) ?
- nphy_tpc_5GHz_txgain_HiPwrEPA :
- nphy_tpc_5GHz_txgain_rev4;
- } else {
- tx_pwrctrl_tbl =
- nphy_tpc_5GHz_txgain_rev5;
- }
+ mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13));
+ mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13));
- } else {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- if (pi->pubpi.radiorev == 5) {
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_epa_2057rev5;
- } else if (pi->pubpi.radiorev == 3) {
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_epa_2057rev3;
- }
+ mod_phy_reg(pi, 0x289, (0xff << 0), (0x46 << 0));
- } else {
- if (NREV_GE(pi->pubpi.phy_rev, 5) &&
- (pi->srom_fem2g.extpagain == 3)) {
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_HiPwrEPA;
- } else {
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_rev3;
- }
- }
- }
- }
+ mod_phy_reg(pi, 0x283, (0xff << 0), (0x3c << 0));
+ mod_phy_reg(pi, 0x280, (0xff << 0), (0x3c << 0));
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 128,
- 192, 32, tx_pwrctrl_tbl);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 128,
- 192, 32, tx_pwrctrl_tbl);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x8, 8,
+ lna1_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x8, 8,
+ lna1_gain_db);
- pi->nphy_gmval = (u16) ((*tx_pwrctrl_tbl >> 16) & 0x7000);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, 8,
+ lna2_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, 8,
+ lna2_gain_db);
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8,
+ tia_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8,
+ tia_gain_db);
- for (idx = 0; idx < 128; idx++) {
- pga_gn = (tx_pwrctrl_tbl[idx] >> 24) & 0xf;
- pad_gn = (tx_pwrctrl_tbl[idx] >> 19) & 0x1f;
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8,
+ tia_gainbits);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8,
+ tia_gainbits);
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if ((pi->pubpi.radiorev == 3) ||
- (pi->pubpi.radiorev == 4) ||
- (pi->pubpi.radiorev == 6)) {
- rfpwr_offset = (s16)
- nphy_papd_padgain_dlt_2g_2057rev3n4
- [pad_gn];
- } else if (pi->pubpi.radiorev == 5) {
- rfpwr_offset = (s16)
- nphy_papd_padgain_dlt_2g_2057rev5
- [pad_gn];
- } else if ((pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev ==
- 8)) {
- rfpwr_offset = (s16)
- nphy_papd_padgain_dlt_2g_2057rev7
- [pad_gn];
- }
- } else {
- if ((pi->pubpi.radiorev == 3) ||
- (pi->pubpi.radiorev == 4) ||
- (pi->pubpi.radiorev == 6)) {
- rfpwr_offset = (s16)
- nphy_papd_pgagain_dlt_5g_2057
- [pga_gn];
- } else if ((pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev ==
- 8)) {
- rfpwr_offset = (s16)
- nphy_papd_pgagain_dlt_5g_2057rev7
- [pga_gn];
- }
- }
- wlc_phy_table_write_nphy(pi,
- NPHY_TBL_ID_CORE1TXPWRCTL,
- 1, 576 + idx, 32,
- &rfpwr_offset);
- wlc_phy_table_write_nphy(pi,
- NPHY_TBL_ID_CORE2TXPWRCTL,
- 1, 576 + idx, 32,
- &rfpwr_offset);
- }
- } else {
+ write_phy_reg(pi, 0x37, 0x74);
+ write_phy_reg(pi, 0x2ad, 0x74);
+ write_phy_reg(pi, 0x38, 0x18);
+ write_phy_reg(pi, 0x2ae, 0x18);
- for (idx = 0; idx < 128; idx++) {
- pga_gn = (tx_pwrctrl_tbl[idx] >> 24) & 0xf;
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- rfpwr_offset = (s16)
- nphy_papd_pga_gain_delta_ipa_2g
- [pga_gn];
- } else {
- rfpwr_offset = (s16)
- nphy_papd_pga_gain_delta_ipa_5g
- [pga_gn];
- }
+ write_phy_reg(pi, 0x2b, 0xe8);
+ write_phy_reg(pi, 0x41, 0xe8);
- wlc_phy_table_write_nphy(pi,
- NPHY_TBL_ID_CORE1TXPWRCTL,
- 1, 576 + idx, 32,
- &rfpwr_offset);
- wlc_phy_table_write_nphy(pi,
- NPHY_TBL_ID_CORE2TXPWRCTL,
- 1, 576 + idx, 32,
- &rfpwr_offset);
- }
+ if (CHSPEC_IS20(pi->radio_chanspec)) {
- }
+ mod_phy_reg(pi, 0x300, (0x3f << 0), (0x12 << 0));
+ mod_phy_reg(pi, 0x301, (0x3f << 0), (0x12 << 0));
} else {
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 128,
- 192, 32, nphy_tpc_txgain);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 128,
- 192, 32, nphy_tpc_txgain);
+ mod_phy_reg(pi, 0x300, (0x3f << 0), (0x10 << 0));
+ mod_phy_reg(pi, 0x301, (0x3f << 0), (0x10 << 0));
}
+}
- if (pi->sh->phyrxchain != 0x3) {
- wlc_phy_rxcore_setstate_nphy((wlc_phy_t *) pi,
- pi->sh->phyrxchain);
- }
+static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(struct brcms_phy *pi)
+{
+ u16 currband;
+ s8 lna1G_gain_db_rev7[] = { 9, 14, 19, 24 };
+ s8 *lna1_gain_db = NULL;
+ s8 *lna1_gain_db_2 = NULL;
+ s8 *lna2_gain_db = NULL;
+ s8 tiaA_gain_db_rev7[] = { -9, -6, -3, 0, 3, 3, 3, 3, 3, 3 };
+ s8 *tia_gain_db;
+ s8 tiaA_gainbits_rev7[] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4 };
+ s8 *tia_gainbits;
+ u16 rfseqA_init_gain_rev7[] = { 0x624f, 0x624f };
+ u16 *rfseq_init_gain;
+ u16 init_gaincode;
+ u16 clip1hi_gaincode;
+ u16 clip1md_gaincode = 0;
+ u16 clip1md_gaincode_B;
+ u16 clip1lo_gaincode;
+ u16 clip1lo_gaincode_B;
+ u8 crsminl_th = 0;
+ u8 crsminu_th;
+ u16 nbclip_th = 0;
+ u8 w1clip_th;
+ u16 freq;
+ s8 nvar_baseline_offset0 = 0, nvar_baseline_offset1 = 0;
+ u8 chg_nbclip_th = 0;
- if (PHY_PERICAL_MPHASE_PENDING(pi)) {
- wlc_phy_cal_perical_mphase_restart(pi);
- }
+ mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13));
+ mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13));
- if (!NORADIO_ENAB(pi->pubpi)) {
- bool do_rssi_cal = false;
+ currband = read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand;
+ if (currband == 0) {
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- do_rssi_cal = (CHSPEC_IS2G(pi->radio_chanspec)) ?
- (pi->nphy_rssical_chanspec_2G == 0) :
- (pi->nphy_rssical_chanspec_5G == 0);
+ lna1_gain_db = lna1G_gain_db_rev7;
- if (do_rssi_cal) {
- wlc_phy_rssi_cal_nphy(pi);
- } else {
- wlc_phy_restore_rssical_nphy(pi);
- }
- } else {
- wlc_phy_rssi_cal_nphy(pi);
- }
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8,
+ lna1_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8,
+ lna1_gain_db);
+
+ mod_phy_reg(pi, 0x283, (0xff << 0), (0x40 << 0));
- if (!SCAN_RM_IN_PROGRESS(pi)) {
- do_nphy_cal = (CHSPEC_IS2G(pi->radio_chanspec)) ?
- (pi->nphy_iqcal_chanspec_2G == 0) :
- (pi->nphy_iqcal_chanspec_5G == 0);
+ if (CHSPEC_IS40(pi->radio_chanspec)) {
+ mod_phy_reg(pi, 0x280, (0xff << 0), (0x3e << 0));
+ mod_phy_reg(pi, 0x283, (0xff << 0), (0x3e << 0));
}
- if (!pi->do_initcal)
- do_nphy_cal = false;
+ mod_phy_reg(pi, 0x289, (0xff << 0), (0x46 << 0));
- if (do_nphy_cal) {
+ if (CHSPEC_IS20(pi->radio_chanspec)) {
+ mod_phy_reg(pi, 0x300, (0x3f << 0), (13 << 0));
+ mod_phy_reg(pi, 0x301, (0x3f << 0), (13 << 0));
+ }
+ } else {
- target_gain = wlc_phy_get_tx_gain_nphy(pi);
+ init_gaincode = 0x9e;
+ clip1hi_gaincode = 0x9e;
+ clip1md_gaincode_B = 0x24;
+ clip1lo_gaincode = 0x8a;
+ clip1lo_gaincode_B = 8;
+ rfseq_init_gain = rfseqA_init_gain_rev7;
- if (pi->antsel_type == ANTSEL_2x3)
- wlc_phy_antsel_init((wlc_phy_t *) pi, true);
+ tia_gain_db = tiaA_gain_db_rev7;
+ tia_gainbits = tiaA_gainbits_rev7;
- if (pi->nphy_perical != PHY_PERICAL_MPHASE) {
- wlc_phy_rssi_cal_nphy(pi);
+ freq = CHAN5G_FREQ(CHSPEC_CHANNEL(pi->radio_chanspec));
+ if (CHSPEC_IS20(pi->radio_chanspec)) {
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- pi->nphy_cal_orig_pwr_idx[0] =
- pi->nphy_txpwrindex[PHY_CORE_0].
- index_internal;
- pi->nphy_cal_orig_pwr_idx[1] =
- pi->nphy_txpwrindex[PHY_CORE_1].
- index_internal;
-
- wlc_phy_precal_txgain_nphy(pi);
- target_gain =
- wlc_phy_get_tx_gain_nphy(pi);
- }
+ w1clip_th = 25;
+ clip1md_gaincode = 0x82;
- if (wlc_phy_cal_txiqlo_nphy
- (pi, target_gain, true, false) == 0) {
- if (wlc_phy_cal_rxiq_nphy
- (pi, target_gain, 2,
- false) == 0) {
- wlc_phy_savecal_nphy(pi);
+ if ((freq <= 5080) || (freq == 5825)) {
- }
- }
- } else if (pi->mphase_cal_phase_id ==
- MPHASE_CAL_STATE_IDLE) {
+ s8 lna1A_gain_db_rev7[] = { 11, 16, 20, 24 };
+ s8 lna1A_gain_db_2_rev7[] = {
+ 11, 17, 22, 25};
+ s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 };
- wlc_phy_cal_perical((wlc_phy_t *) pi,
- PHY_PERICAL_PHYINIT);
- }
- } else {
- wlc_phy_restorecal_nphy(pi);
- }
- }
+ crsminu_th = 0x3e;
+ lna1_gain_db = lna1A_gain_db_rev7;
+ lna1_gain_db_2 = lna1A_gain_db_2_rev7;
+ lna2_gain_db = lna2A_gain_db_rev7;
+ } else if ((freq >= 5500) && (freq <= 5700)) {
- wlc_phy_txpwrctrl_coeff_setup_nphy(pi);
+ s8 lna1A_gain_db_rev7[] = { 11, 17, 21, 25 };
+ s8 lna1A_gain_db_2_rev7[] = {
+ 12, 18, 22, 26};
+ s8 lna2A_gain_db_rev7[] = { 1, 8, 12, 16 };
- wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state);
+ crsminu_th = 0x45;
+ clip1md_gaincode_B = 0x14;
+ nbclip_th = 0xff;
+ chg_nbclip_th = 1;
+ lna1_gain_db = lna1A_gain_db_rev7;
+ lna1_gain_db_2 = lna1A_gain_db_2_rev7;
+ lna2_gain_db = lna2A_gain_db_rev7;
+ } else {
- wlc_phy_nphy_tkip_rifs_war(pi, pi->sh->_rifs_phy);
+ s8 lna1A_gain_db_rev7[] = { 12, 18, 22, 26 };
+ s8 lna1A_gain_db_2_rev7[] = {
+ 12, 18, 22, 26};
+ s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 };
- if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LE(pi->pubpi.phy_rev, 6))
+ crsminu_th = 0x41;
+ lna1_gain_db = lna1A_gain_db_rev7;
+ lna1_gain_db_2 = lna1A_gain_db_2_rev7;
+ lna2_gain_db = lna2A_gain_db_rev7;
+ }
- write_phy_reg(pi, 0x70, 50);
+ if (freq <= 4920) {
+ nvar_baseline_offset0 = 5;
+ nvar_baseline_offset1 = 5;
+ } else if ((freq > 4920) && (freq <= 5320)) {
+ nvar_baseline_offset0 = 3;
+ nvar_baseline_offset1 = 5;
+ } else if ((freq > 5320) && (freq <= 5700)) {
+ nvar_baseline_offset0 = 3;
+ nvar_baseline_offset1 = 2;
+ } else {
+ nvar_baseline_offset0 = 4;
+ nvar_baseline_offset1 = 0;
+ }
+ } else {
- wlc_phy_txlpfbw_nphy(pi);
+ crsminu_th = 0x3a;
+ crsminl_th = 0x3a;
+ w1clip_th = 20;
- wlc_phy_spurwar_nphy(pi);
+ if ((freq >= 4920) && (freq <= 5320)) {
+ nvar_baseline_offset0 = 4;
+ nvar_baseline_offset1 = 5;
+ } else if ((freq > 5320) && (freq <= 5550)) {
+ nvar_baseline_offset0 = 4;
+ nvar_baseline_offset1 = 2;
+ } else {
+ nvar_baseline_offset0 = 5;
+ nvar_baseline_offset1 = 3;
+ }
+ }
-}
+ write_phy_reg(pi, 0x20, init_gaincode);
+ write_phy_reg(pi, 0x2a7, init_gaincode);
-static void wlc_phy_update_mimoconfig_nphy(phy_info_t *pi, s32 preamble)
-{
- bool gf_preamble = false;
- u16 val;
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
+ pi->pubpi.phy_corenum, 0x106, 16,
+ rfseq_init_gain);
- if (preamble == WLC_N_PREAMBLE_GF) {
- gf_preamble = true;
- }
+ write_phy_reg(pi, 0x22, clip1hi_gaincode);
+ write_phy_reg(pi, 0x2a9, clip1hi_gaincode);
- val = read_phy_reg(pi, 0xed);
+ write_phy_reg(pi, 0x36, clip1md_gaincode_B);
+ write_phy_reg(pi, 0x2ac, clip1md_gaincode_B);
- val |= RX_GF_MM_AUTO;
- val &= ~RX_GF_OR_MM;
- if (gf_preamble)
- val |= RX_GF_OR_MM;
+ write_phy_reg(pi, 0x37, clip1lo_gaincode);
+ write_phy_reg(pi, 0x2ad, clip1lo_gaincode);
+ write_phy_reg(pi, 0x38, clip1lo_gaincode_B);
+ write_phy_reg(pi, 0x2ae, clip1lo_gaincode_B);
- write_phy_reg(pi, 0xed, val);
-}
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8,
+ tia_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8,
+ tia_gain_db);
-static void wlc_phy_resetcca_nphy(phy_info_t *pi)
-{
- u16 val;
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8,
+ tia_gainbits);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8,
+ tia_gainbits);
- wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON);
+ mod_phy_reg(pi, 0x283, (0xff << 0), (crsminu_th << 0));
- val = read_phy_reg(pi, 0x01);
- write_phy_reg(pi, 0x01, val | BBCFG_RESETCCA);
- udelay(1);
- write_phy_reg(pi, 0x01, val & (~BBCFG_RESETCCA));
+ if (chg_nbclip_th == 1) {
+ write_phy_reg(pi, 0x2b, nbclip_th);
+ write_phy_reg(pi, 0x41, nbclip_th);
+ }
- wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF);
+ mod_phy_reg(pi, 0x300, (0x3f << 0), (w1clip_th << 0));
+ mod_phy_reg(pi, 0x301, (0x3f << 0), (w1clip_th << 0));
- wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX);
-}
+ mod_phy_reg(pi, 0x2e4,
+ (0x3f << 0), (nvar_baseline_offset0 << 0));
-void wlc_phy_pa_override_nphy(phy_info_t *pi, bool en)
-{
- u16 rfctrlintc_override_val;
+ mod_phy_reg(pi, 0x2e4,
+ (0x3f << 6), (nvar_baseline_offset1 << 6));
- if (!en) {
+ if (CHSPEC_IS20(pi->radio_chanspec)) {
- pi->rfctrlIntc1_save = read_phy_reg(pi, 0x91);
- pi->rfctrlIntc2_save = read_phy_reg(pi, 0x92);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8,
+ lna1_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8,
+ lna1_gain_db_2);
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- rfctrlintc_override_val = 0x1480;
- } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- rfctrlintc_override_val =
- CHSPEC_IS5G(pi->radio_chanspec) ? 0x600 : 0x480;
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10,
+ 8, lna2_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10,
+ 8, lna2_gain_db);
+
+ write_phy_reg(pi, 0x24, clip1md_gaincode);
+ write_phy_reg(pi, 0x2ab, clip1md_gaincode);
} else {
- rfctrlintc_override_val =
- CHSPEC_IS5G(pi->radio_chanspec) ? 0x180 : 0x120;
+ mod_phy_reg(pi, 0x280, (0xff << 0), (crsminl_th << 0));
}
-
- write_phy_reg(pi, 0x91, rfctrlintc_override_val);
- write_phy_reg(pi, 0x92, rfctrlintc_override_val);
- } else {
-
- write_phy_reg(pi, 0x91, pi->rfctrlIntc1_save);
- write_phy_reg(pi, 0x92, pi->rfctrlIntc2_save);
}
-
}
-void wlc_phy_stf_chain_upd_nphy(phy_info_t *pi)
+static void wlc_phy_workarounds_nphy_gainctrl(struct brcms_phy *pi)
{
+ u16 w1th, hpf_code, currband;
+ int ctr;
+ u8 rfseq_updategainu_events[] = {
+ NPHY_RFSEQ_CMD_RX_GAIN,
+ NPHY_RFSEQ_CMD_CLR_HIQ_DIS,
+ NPHY_RFSEQ_CMD_SET_HPF_BW
+ };
+ u8 rfseq_updategainu_dlys[] = { 10, 30, 1 };
+ s8 lna1G_gain_db[] = { 7, 11, 16, 23 };
+ s8 lna1G_gain_db_rev4[] = { 8, 12, 17, 25 };
+ s8 lna1G_gain_db_rev5[] = { 9, 13, 18, 26 };
+ s8 lna1G_gain_db_rev6[] = { 8, 13, 18, 25 };
+ s8 lna1G_gain_db_rev6_224B0[] = { 10, 14, 19, 27 };
+ s8 lna1A_gain_db[] = { 7, 11, 17, 23 };
+ s8 lna1A_gain_db_rev4[] = { 8, 12, 18, 23 };
+ s8 lna1A_gain_db_rev5[] = { 6, 10, 16, 21 };
+ s8 lna1A_gain_db_rev6[] = { 6, 10, 16, 21 };
+ s8 *lna1_gain_db = NULL;
+ s8 lna2G_gain_db[] = { -5, 6, 10, 14 };
+ s8 lna2G_gain_db_rev5[] = { -3, 7, 11, 16 };
+ s8 lna2G_gain_db_rev6[] = { -5, 6, 10, 14 };
+ s8 lna2G_gain_db_rev6_224B0[] = { -5, 6, 10, 15 };
+ s8 lna2A_gain_db[] = { -6, 2, 6, 10 };
+ s8 lna2A_gain_db_rev4[] = { -5, 2, 6, 10 };
+ s8 lna2A_gain_db_rev5[] = { -7, 0, 4, 8 };
+ s8 lna2A_gain_db_rev6[] = { -7, 0, 4, 8 };
+ s8 *lna2_gain_db = NULL;
+ s8 tiaG_gain_db[] = {
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A };
+ s8 tiaA_gain_db[] = {
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13 };
+ s8 tiaA_gain_db_rev4[] = {
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d };
+ s8 tiaA_gain_db_rev5[] = {
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d };
+ s8 tiaA_gain_db_rev6[] = {
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d };
+ s8 *tia_gain_db;
+ s8 tiaG_gainbits[] = {
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 };
+ s8 tiaA_gainbits[] = {
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 };
+ s8 tiaA_gainbits_rev4[] = {
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 };
+ s8 tiaA_gainbits_rev5[] = {
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 };
+ s8 tiaA_gainbits_rev6[] = {
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 };
+ s8 *tia_gainbits;
+ s8 lpf_gain_db[] = { 0x00, 0x06, 0x0c, 0x12, 0x12, 0x12 };
+ s8 lpf_gainbits[] = { 0x00, 0x01, 0x02, 0x03, 0x03, 0x03 };
+ u16 rfseqG_init_gain[] = { 0x613f, 0x613f, 0x613f, 0x613f };
+ u16 rfseqG_init_gain_rev4[] = { 0x513f, 0x513f, 0x513f, 0x513f };
+ u16 rfseqG_init_gain_rev5[] = { 0x413f, 0x413f, 0x413f, 0x413f };
+ u16 rfseqG_init_gain_rev5_elna[] = {
+ 0x013f, 0x013f, 0x013f, 0x013f };
+ u16 rfseqG_init_gain_rev6[] = { 0x513f, 0x513f };
+ u16 rfseqG_init_gain_rev6_224B0[] = { 0x413f, 0x413f };
+ u16 rfseqG_init_gain_rev6_elna[] = { 0x113f, 0x113f };
+ u16 rfseqA_init_gain[] = { 0x516f, 0x516f, 0x516f, 0x516f };
+ u16 rfseqA_init_gain_rev4[] = { 0x614f, 0x614f, 0x614f, 0x614f };
+ u16 rfseqA_init_gain_rev4_elna[] = {
+ 0x314f, 0x314f, 0x314f, 0x314f };
+ u16 rfseqA_init_gain_rev5[] = { 0x714f, 0x714f, 0x714f, 0x714f };
+ u16 rfseqA_init_gain_rev6[] = { 0x714f, 0x714f };
+ u16 *rfseq_init_gain;
+ u16 initG_gaincode = 0x627e;
+ u16 initG_gaincode_rev4 = 0x527e;
+ u16 initG_gaincode_rev5 = 0x427e;
+ u16 initG_gaincode_rev5_elna = 0x027e;
+ u16 initG_gaincode_rev6 = 0x527e;
+ u16 initG_gaincode_rev6_224B0 = 0x427e;
+ u16 initG_gaincode_rev6_elna = 0x127e;
+ u16 initA_gaincode = 0x52de;
+ u16 initA_gaincode_rev4 = 0x629e;
+ u16 initA_gaincode_rev4_elna = 0x329e;
+ u16 initA_gaincode_rev5 = 0x729e;
+ u16 initA_gaincode_rev6 = 0x729e;
+ u16 init_gaincode;
+ u16 clip1hiG_gaincode = 0x107e;
+ u16 clip1hiG_gaincode_rev4 = 0x007e;
+ u16 clip1hiG_gaincode_rev5 = 0x1076;
+ u16 clip1hiG_gaincode_rev6 = 0x007e;
+ u16 clip1hiA_gaincode = 0x00de;
+ u16 clip1hiA_gaincode_rev4 = 0x029e;
+ u16 clip1hiA_gaincode_rev5 = 0x029e;
+ u16 clip1hiA_gaincode_rev6 = 0x029e;
+ u16 clip1hi_gaincode;
+ u16 clip1mdG_gaincode = 0x0066;
+ u16 clip1mdA_gaincode = 0x00ca;
+ u16 clip1mdA_gaincode_rev4 = 0x1084;
+ u16 clip1mdA_gaincode_rev5 = 0x2084;
+ u16 clip1mdA_gaincode_rev6 = 0x2084;
+ u16 clip1md_gaincode = 0;
+ u16 clip1loG_gaincode = 0x0074;
+ u16 clip1loG_gaincode_rev5[] = {
+ 0x0062, 0x0064, 0x006a, 0x106a, 0x106c, 0x1074, 0x107c, 0x207c
+ };
+ u16 clip1loG_gaincode_rev6[] = {
+ 0x106a, 0x106c, 0x1074, 0x107c, 0x007e, 0x107e, 0x207e, 0x307e
+ };
+ u16 clip1loG_gaincode_rev6_224B0 = 0x1074;
+ u16 clip1loA_gaincode = 0x00cc;
+ u16 clip1loA_gaincode_rev4 = 0x0086;
+ u16 clip1loA_gaincode_rev5 = 0x2086;
+ u16 clip1loA_gaincode_rev6 = 0x2086;
+ u16 clip1lo_gaincode;
+ u8 crsminG_th = 0x18;
+ u8 crsminG_th_rev5 = 0x18;
+ u8 crsminG_th_rev6 = 0x18;
+ u8 crsminA_th = 0x1e;
+ u8 crsminA_th_rev4 = 0x24;
+ u8 crsminA_th_rev5 = 0x24;
+ u8 crsminA_th_rev6 = 0x24;
+ u8 crsmin_th;
+ u8 crsminlG_th = 0x18;
+ u8 crsminlG_th_rev5 = 0x18;
+ u8 crsminlG_th_rev6 = 0x18;
+ u8 crsminlA_th = 0x1e;
+ u8 crsminlA_th_rev4 = 0x24;
+ u8 crsminlA_th_rev5 = 0x24;
+ u8 crsminlA_th_rev6 = 0x24;
+ u8 crsminl_th = 0;
+ u8 crsminuG_th = 0x18;
+ u8 crsminuG_th_rev5 = 0x18;
+ u8 crsminuG_th_rev6 = 0x18;
+ u8 crsminuA_th = 0x1e;
+ u8 crsminuA_th_rev4 = 0x24;
+ u8 crsminuA_th_rev5 = 0x24;
+ u8 crsminuA_th_rev6 = 0x24;
+ u8 crsminuA_th_rev6_224B0 = 0x2d;
+ u8 crsminu_th;
+ u16 nbclipG_th = 0x20d;
+ u16 nbclipG_th_rev4 = 0x1a1;
+ u16 nbclipG_th_rev5 = 0x1d0;
+ u16 nbclipG_th_rev6 = 0x1d0;
+ u16 nbclipA_th = 0x1a1;
+ u16 nbclipA_th_rev4 = 0x107;
+ u16 nbclipA_th_rev5 = 0x0a9;
+ u16 nbclipA_th_rev6 = 0x0f0;
+ u16 nbclip_th = 0;
+ u8 w1clipG_th = 5;
+ u8 w1clipG_th_rev5 = 9;
+ u8 w1clipG_th_rev6 = 5;
+ u8 w1clipA_th = 25, w1clip_th;
+ u8 rssi_gain_default = 0x50;
+ u8 rssiG_gain_rev6_224B0 = 0x50;
+ u8 rssiA_gain_rev5 = 0x90;
+ u8 rssiA_gain_rev6 = 0x90;
+ u8 rssi_gain;
+ u16 regval[21];
+ u8 triso;
- u16 txrx_chain =
- (NPHY_RfseqCoreActv_TxRxChain0 | NPHY_RfseqCoreActv_TxRxChain1);
- bool CoreActv_override = false;
+ triso = (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.triso :
+ pi->srom_fem2g.triso;
- if (pi->nphy_txrx_chain == WLC_N_TXRX_CHAIN0) {
- txrx_chain = NPHY_RfseqCoreActv_TxRxChain0;
- CoreActv_override = true;
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (pi->pubpi.radiorev == 5) {
+ wlc_phy_workarounds_nphy_gainctrl_2057_rev5(pi);
+ } else if (pi->pubpi.radiorev == 7) {
+ wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi);
- if (NREV_LE(pi->pubpi.phy_rev, 2)) {
- and_phy_reg(pi, 0xa0, ~0x20);
- }
- } else if (pi->nphy_txrx_chain == WLC_N_TXRX_CHAIN1) {
- txrx_chain = NPHY_RfseqCoreActv_TxRxChain1;
- CoreActv_override = true;
+ mod_phy_reg(pi, 0x283, (0xff << 0), (0x44 << 0));
+ mod_phy_reg(pi, 0x280, (0xff << 0), (0x44 << 0));
- if (NREV_LE(pi->pubpi.phy_rev, 2)) {
- or_phy_reg(pi, 0xa0, 0x20);
+ } else if ((pi->pubpi.radiorev == 3)
+ || (pi->pubpi.radiorev == 8)) {
+ wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi);
+
+ if (pi->pubpi.radiorev == 8) {
+ mod_phy_reg(pi, 0x283,
+ (0xff << 0), (0x44 << 0));
+ mod_phy_reg(pi, 0x280,
+ (0xff << 0), (0x44 << 0));
+ }
+ } else {
+ wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi);
}
- }
+ } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- mod_phy_reg(pi, 0xa2, ((0xf << 0) | (0xf << 4)), txrx_chain);
+ mod_phy_reg(pi, 0xa0, (0x1 << 6), (1 << 6));
- if (CoreActv_override) {
+ mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13));
+ mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13));
- pi->nphy_perical = PHY_PERICAL_DISABLE;
- or_phy_reg(pi, 0xa1, NPHY_RfseqMode_CoreActv_override);
- } else {
- pi->nphy_perical = PHY_PERICAL_MPHASE;
- and_phy_reg(pi, 0xa1, ~NPHY_RfseqMode_CoreActv_override);
- }
-}
+ currband =
+ read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand;
+ if (currband == 0) {
+ if (NREV_GE(pi->pubpi.phy_rev, 6)) {
+ if (pi->pubpi.radiorev == 11) {
+ lna1_gain_db = lna1G_gain_db_rev6_224B0;
+ lna2_gain_db = lna2G_gain_db_rev6_224B0;
+ rfseq_init_gain =
+ rfseqG_init_gain_rev6_224B0;
+ init_gaincode =
+ initG_gaincode_rev6_224B0;
+ clip1hi_gaincode =
+ clip1hiG_gaincode_rev6;
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev6_224B0;
+ nbclip_th = nbclipG_th_rev6;
+ w1clip_th = w1clipG_th_rev6;
+ crsmin_th = crsminG_th_rev6;
+ crsminl_th = crsminlG_th_rev6;
+ crsminu_th = crsminuG_th_rev6;
+ rssi_gain = rssiG_gain_rev6_224B0;
+ } else {
+ lna1_gain_db = lna1G_gain_db_rev6;
+ lna2_gain_db = lna2G_gain_db_rev6;
+ if (pi->sh->boardflags & BFL_EXTLNA) {
-void wlc_phy_rxcore_setstate_nphy(wlc_phy_t *pih, u8 rxcore_bitmask)
-{
- u16 regval;
- u16 tbl_buf[16];
- uint i;
- phy_info_t *pi = (phy_info_t *) pih;
- u16 tbl_opcode;
- bool suspend;
+ rfseq_init_gain =
+ rfseqG_init_gain_rev6_elna;
+ init_gaincode =
+ initG_gaincode_rev6_elna;
+ } else {
+ rfseq_init_gain =
+ rfseqG_init_gain_rev6;
+ init_gaincode =
+ initG_gaincode_rev6;
+ }
+ clip1hi_gaincode =
+ clip1hiG_gaincode_rev6;
+ switch (triso) {
+ case 0:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev6
+ [0];
+ break;
+ case 1:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev6
+ [1];
+ break;
+ case 2:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev6
+ [2];
+ break;
+ case 3:
+ default:
- pi->sh->phyrxchain = rxcore_bitmask;
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev6
+ [3];
+ break;
+ case 4:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev6
+ [4];
+ break;
+ case 5:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev6
+ [5];
+ break;
+ case 6:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev6
+ [6];
+ break;
+ case 7:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev6
+ [7];
+ break;
+ }
+ nbclip_th = nbclipG_th_rev6;
+ w1clip_th = w1clipG_th_rev6;
+ crsmin_th = crsminG_th_rev6;
+ crsminl_th = crsminlG_th_rev6;
+ crsminu_th = crsminuG_th_rev6;
+ rssi_gain = rssi_gain_default;
+ }
+ } else if (NREV_IS(pi->pubpi.phy_rev, 5)) {
+ lna1_gain_db = lna1G_gain_db_rev5;
+ lna2_gain_db = lna2G_gain_db_rev5;
+ if (pi->sh->boardflags & BFL_EXTLNA) {
- if (!pi->sh->clk)
- return;
+ rfseq_init_gain =
+ rfseqG_init_gain_rev5_elna;
+ init_gaincode =
+ initG_gaincode_rev5_elna;
+ } else {
+ rfseq_init_gain = rfseqG_init_gain_rev5;
+ init_gaincode = initG_gaincode_rev5;
+ }
+ clip1hi_gaincode = clip1hiG_gaincode_rev5;
+ switch (triso) {
+ case 0:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev5[0];
+ break;
+ case 1:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev5[1];
+ break;
+ case 2:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev5[2];
+ break;
+ case 3:
- suspend =
- (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
- if (!suspend)
- wlapi_suspend_mac_and_wait(pi->sh->physhim);
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev5[3];
+ break;
+ case 4:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev5[4];
+ break;
+ case 5:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev5[5];
+ break;
+ case 6:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev5[6];
+ break;
+ case 7:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev5[7];
+ break;
+ default:
+ clip1lo_gaincode =
+ clip1loG_gaincode_rev5[3];
+ break;
+ }
+ nbclip_th = nbclipG_th_rev5;
+ w1clip_th = w1clipG_th_rev5;
+ crsmin_th = crsminG_th_rev5;
+ crsminl_th = crsminlG_th_rev5;
+ crsminu_th = crsminuG_th_rev5;
+ rssi_gain = rssi_gain_default;
+ } else if (NREV_IS(pi->pubpi.phy_rev, 4)) {
+ lna1_gain_db = lna1G_gain_db_rev4;
+ lna2_gain_db = lna2G_gain_db;
+ rfseq_init_gain = rfseqG_init_gain_rev4;
+ init_gaincode = initG_gaincode_rev4;
+ clip1hi_gaincode = clip1hiG_gaincode_rev4;
+ clip1lo_gaincode = clip1loG_gaincode;
+ nbclip_th = nbclipG_th_rev4;
+ w1clip_th = w1clipG_th;
+ crsmin_th = crsminG_th;
+ crsminl_th = crsminlG_th;
+ crsminu_th = crsminuG_th;
+ rssi_gain = rssi_gain_default;
+ } else {
+ lna1_gain_db = lna1G_gain_db;
+ lna2_gain_db = lna2G_gain_db;
+ rfseq_init_gain = rfseqG_init_gain;
+ init_gaincode = initG_gaincode;
+ clip1hi_gaincode = clip1hiG_gaincode;
+ clip1lo_gaincode = clip1loG_gaincode;
+ nbclip_th = nbclipG_th;
+ w1clip_th = w1clipG_th;
+ crsmin_th = crsminG_th;
+ crsminl_th = crsminlG_th;
+ crsminu_th = crsminuG_th;
+ rssi_gain = rssi_gain_default;
+ }
+ tia_gain_db = tiaG_gain_db;
+ tia_gainbits = tiaG_gainbits;
+ clip1md_gaincode = clip1mdG_gaincode;
+ } else {
+ if (NREV_GE(pi->pubpi.phy_rev, 6)) {
+ lna1_gain_db = lna1A_gain_db_rev6;
+ lna2_gain_db = lna2A_gain_db_rev6;
+ tia_gain_db = tiaA_gain_db_rev6;
+ tia_gainbits = tiaA_gainbits_rev6;
+ rfseq_init_gain = rfseqA_init_gain_rev6;
+ init_gaincode = initA_gaincode_rev6;
+ clip1hi_gaincode = clip1hiA_gaincode_rev6;
+ clip1md_gaincode = clip1mdA_gaincode_rev6;
+ clip1lo_gaincode = clip1loA_gaincode_rev6;
+ crsmin_th = crsminA_th_rev6;
+ crsminl_th = crsminlA_th_rev6;
+ if ((pi->pubpi.radiorev == 11) &&
+ (CHSPEC_IS40(pi->radio_chanspec) == 0))
+ crsminu_th = crsminuA_th_rev6_224B0;
+ else
+ crsminu_th = crsminuA_th_rev6;
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, true);
+ nbclip_th = nbclipA_th_rev6;
+ rssi_gain = rssiA_gain_rev6;
+ } else if (NREV_IS(pi->pubpi.phy_rev, 5)) {
+ lna1_gain_db = lna1A_gain_db_rev5;
+ lna2_gain_db = lna2A_gain_db_rev5;
+ tia_gain_db = tiaA_gain_db_rev5;
+ tia_gainbits = tiaA_gainbits_rev5;
+ rfseq_init_gain = rfseqA_init_gain_rev5;
+ init_gaincode = initA_gaincode_rev5;
+ clip1hi_gaincode = clip1hiA_gaincode_rev5;
+ clip1md_gaincode = clip1mdA_gaincode_rev5;
+ clip1lo_gaincode = clip1loA_gaincode_rev5;
+ crsmin_th = crsminA_th_rev5;
+ crsminl_th = crsminlA_th_rev5;
+ crsminu_th = crsminuA_th_rev5;
+ nbclip_th = nbclipA_th_rev5;
+ rssi_gain = rssiA_gain_rev5;
+ } else if (NREV_IS(pi->pubpi.phy_rev, 4)) {
+ lna1_gain_db = lna1A_gain_db_rev4;
+ lna2_gain_db = lna2A_gain_db_rev4;
+ tia_gain_db = tiaA_gain_db_rev4;
+ tia_gainbits = tiaA_gainbits_rev4;
+ if (pi->sh->boardflags & BFL_EXTLNA_5GHz) {
- regval = read_phy_reg(pi, 0xa2);
- regval &= ~(0xf << 4);
- regval |= ((u16) (rxcore_bitmask & 0x3)) << 4;
- write_phy_reg(pi, 0xa2, regval);
+ rfseq_init_gain =
+ rfseqA_init_gain_rev4_elna;
+ init_gaincode =
+ initA_gaincode_rev4_elna;
+ } else {
+ rfseq_init_gain = rfseqA_init_gain_rev4;
+ init_gaincode = initA_gaincode_rev4;
+ }
+ clip1hi_gaincode = clip1hiA_gaincode_rev4;
+ clip1md_gaincode = clip1mdA_gaincode_rev4;
+ clip1lo_gaincode = clip1loA_gaincode_rev4;
+ crsmin_th = crsminA_th_rev4;
+ crsminl_th = crsminlA_th_rev4;
+ crsminu_th = crsminuA_th_rev4;
+ nbclip_th = nbclipA_th_rev4;
+ rssi_gain = rssi_gain_default;
+ } else {
+ lna1_gain_db = lna1A_gain_db;
+ lna2_gain_db = lna2A_gain_db;
+ tia_gain_db = tiaA_gain_db;
+ tia_gainbits = tiaA_gainbits;
+ rfseq_init_gain = rfseqA_init_gain;
+ init_gaincode = initA_gaincode;
+ clip1hi_gaincode = clip1hiA_gaincode;
+ clip1md_gaincode = clip1mdA_gaincode;
+ clip1lo_gaincode = clip1loA_gaincode;
+ crsmin_th = crsminA_th;
+ crsminl_th = crsminlA_th;
+ crsminu_th = crsminuA_th;
+ nbclip_th = nbclipA_th;
+ rssi_gain = rssi_gain_default;
+ }
+ w1clip_th = w1clipA_th;
+ }
- if ((rxcore_bitmask & 0x3) != 0x3) {
+ write_radio_reg(pi,
+ (RADIO_2056_RX_BIASPOLE_LNAG1_IDAC |
+ RADIO_2056_RX0), 0x17);
+ write_radio_reg(pi,
+ (RADIO_2056_RX_BIASPOLE_LNAG1_IDAC |
+ RADIO_2056_RX1), 0x17);
- write_phy_reg(pi, 0x20e, 1);
+ write_radio_reg(pi, (RADIO_2056_RX_LNAG2_IDAC | RADIO_2056_RX0),
+ 0xf0);
+ write_radio_reg(pi, (RADIO_2056_RX_LNAG2_IDAC | RADIO_2056_RX1),
+ 0xf0);
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- if (pi->rx2tx_biasentry == -1) {
- wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ,
- ARRAY_SIZE(tbl_buf), 80,
- 16, tbl_buf);
+ write_radio_reg(pi, (RADIO_2056_RX_RSSI_POLE | RADIO_2056_RX0),
+ 0x0);
+ write_radio_reg(pi, (RADIO_2056_RX_RSSI_POLE | RADIO_2056_RX1),
+ 0x0);
- for (i = 0; i < ARRAY_SIZE(tbl_buf); i++) {
- if (tbl_buf[i] ==
- NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS) {
+ write_radio_reg(pi, (RADIO_2056_RX_RSSI_GAIN | RADIO_2056_RX0),
+ rssi_gain);
+ write_radio_reg(pi, (RADIO_2056_RX_RSSI_GAIN | RADIO_2056_RX1),
+ rssi_gain);
- pi->rx2tx_biasentry = (u8) i;
- tbl_opcode =
- NPHY_REV3_RFSEQ_CMD_NOP;
- wlc_phy_table_write_nphy(pi,
- NPHY_TBL_ID_RFSEQ,
- 1, i,
- 16,
- &tbl_opcode);
- break;
- } else if (tbl_buf[i] ==
- NPHY_REV3_RFSEQ_CMD_END) {
- break;
- }
- }
- }
- }
- } else {
+ write_radio_reg(pi,
+ (RADIO_2056_RX_BIASPOLE_LNAA1_IDAC |
+ RADIO_2056_RX0), 0x17);
+ write_radio_reg(pi,
+ (RADIO_2056_RX_BIASPOLE_LNAA1_IDAC |
+ RADIO_2056_RX1), 0x17);
- write_phy_reg(pi, 0x20e, 30);
+ write_radio_reg(pi, (RADIO_2056_RX_LNAA2_IDAC | RADIO_2056_RX0),
+ 0xFF);
+ write_radio_reg(pi, (RADIO_2056_RX_LNAA2_IDAC | RADIO_2056_RX1),
+ 0xFF);
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- if (pi->rx2tx_biasentry != -1) {
- tbl_opcode = NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS;
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- 1, pi->rx2tx_biasentry,
- 16, &tbl_opcode);
- pi->rx2tx_biasentry = -1;
- }
- }
- }
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8,
+ 8, lna1_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8,
+ 8, lna1_gain_db);
- wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10,
+ 8, lna2_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10,
+ 8, lna2_gain_db);
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, false);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20,
+ 8, tia_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20,
+ 8, tia_gain_db);
- if (!suspend)
- wlapi_enable_mac(pi->sh->physhim);
-}
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20,
+ 8, tia_gainbits);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20,
+ 8, tia_gainbits);
-u8 wlc_phy_rxcore_getstate_nphy(wlc_phy_t *pih)
-{
- u16 regval, rxen_bits;
- phy_info_t *pi = (phy_info_t *) pih;
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 6, 0x40,
+ 8, &lpf_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 6, 0x40,
+ 8, &lpf_gain_db);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 6, 0x40,
+ 8, &lpf_gainbits);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 6, 0x40,
+ 8, &lpf_gainbits);
- regval = read_phy_reg(pi, 0xa2);
- rxen_bits = (regval >> 4) & 0xf;
+ write_phy_reg(pi, 0x20, init_gaincode);
+ write_phy_reg(pi, 0x2a7, init_gaincode);
- return (u8) rxen_bits;
-}
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
+ pi->pubpi.phy_corenum, 0x106, 16,
+ rfseq_init_gain);
-bool wlc_phy_n_txpower_ipa_ison(phy_info_t *pi)
-{
- return PHY_IPA(pi);
-}
+ write_phy_reg(pi, 0x22, clip1hi_gaincode);
+ write_phy_reg(pi, 0x2a9, clip1hi_gaincode);
-static void wlc_phy_txpwr_limit_to_tbl_nphy(phy_info_t *pi)
-{
- u8 idx, idx2, i, delta_ind;
+ write_phy_reg(pi, 0x24, clip1md_gaincode);
+ write_phy_reg(pi, 0x2ab, clip1md_gaincode);
- for (idx = TXP_FIRST_CCK; idx <= TXP_LAST_CCK; idx++) {
- pi->adj_pwr_tbl_nphy[idx] = pi->tx_power_offset[idx];
- }
+ write_phy_reg(pi, 0x37, clip1lo_gaincode);
+ write_phy_reg(pi, 0x2ad, clip1lo_gaincode);
- for (i = 0; i < 4; i++) {
- idx2 = 0;
+ mod_phy_reg(pi, 0x27d, (0xff << 0), (crsmin_th << 0));
+ mod_phy_reg(pi, 0x280, (0xff << 0), (crsminl_th << 0));
+ mod_phy_reg(pi, 0x283, (0xff << 0), (crsminu_th << 0));
- delta_ind = 0;
+ write_phy_reg(pi, 0x2b, nbclip_th);
+ write_phy_reg(pi, 0x41, nbclip_th);
- switch (i) {
- case 0:
+ mod_phy_reg(pi, 0x27, (0x3f << 0), (w1clip_th << 0));
+ mod_phy_reg(pi, 0x3d, (0x3f << 0), (w1clip_th << 0));
- if (CHSPEC_IS40(pi->radio_chanspec)
- && NPHY_IS_SROM_REINTERPRET) {
- idx = TXP_FIRST_MCS_40_SISO;
- } else {
- idx = (CHSPEC_IS40(pi->radio_chanspec)) ?
- TXP_FIRST_OFDM_40_SISO : TXP_FIRST_OFDM;
- delta_ind = 1;
- }
- break;
+ write_phy_reg(pi, 0x150, 0x809c);
- case 1:
+ } else {
- idx = (CHSPEC_IS40(pi->radio_chanspec)) ?
- TXP_FIRST_MCS_40_CDD : TXP_FIRST_MCS_20_CDD;
- break;
+ mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13));
+ mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13));
- case 2:
+ write_phy_reg(pi, 0x2b, 0x84);
+ write_phy_reg(pi, 0x41, 0x84);
- idx = (CHSPEC_IS40(pi->radio_chanspec)) ?
- TXP_FIRST_MCS_40_STBC : TXP_FIRST_MCS_20_STBC;
- break;
+ if (CHSPEC_IS20(pi->radio_chanspec)) {
+ write_phy_reg(pi, 0x6b, 0x2b);
+ write_phy_reg(pi, 0x6c, 0x2b);
+ write_phy_reg(pi, 0x6d, 0x9);
+ write_phy_reg(pi, 0x6e, 0x9);
+ }
- case 3:
+ w1th = NPHY_RSSICAL_W1_TARGET - 4;
+ mod_phy_reg(pi, 0x27, (0x3f << 0), (w1th << 0));
+ mod_phy_reg(pi, 0x3d, (0x3f << 0), (w1th << 0));
- idx = (CHSPEC_IS40(pi->radio_chanspec)) ?
- TXP_FIRST_MCS_40_SDM : TXP_FIRST_MCS_20_SDM;
- break;
+ if (CHSPEC_IS20(pi->radio_chanspec)) {
+ mod_phy_reg(pi, 0x1c, (0x1f << 0), (0x1 << 0));
+ mod_phy_reg(pi, 0x32, (0x1f << 0), (0x1 << 0));
+
+ mod_phy_reg(pi, 0x1d, (0x1f << 0), (0x1 << 0));
+ mod_phy_reg(pi, 0x33, (0x1f << 0), (0x1 << 0));
}
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- idx = idx + delta_ind;
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx++];
+ write_phy_reg(pi, 0x150, 0x809c);
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx++];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx++];
+ if (pi->nphy_gain_boost)
+ if ((CHSPEC_IS2G(pi->radio_chanspec)) &&
+ (CHSPEC_IS40(pi->radio_chanspec)))
+ hpf_code = 4;
+ else
+ hpf_code = 5;
+ else if (CHSPEC_IS40(pi->radio_chanspec))
+ hpf_code = 6;
+ else
+ hpf_code = 7;
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx++];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx++];
+ mod_phy_reg(pi, 0x20, (0x1f << 7), (hpf_code << 7));
+ mod_phy_reg(pi, 0x36, (0x1f << 7), (hpf_code << 7));
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx++];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- idx = idx + 1 - delta_ind;
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
+ for (ctr = 0; ctr < 4; ctr++)
+ regval[ctr] = (hpf_code << 8) | 0x7c;
+ wlc_phy_table_write_nphy(pi, 7, 4, 0x106, 16, regval);
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
- pi->tx_power_offset[idx];
- }
-}
+ wlc_phy_adjust_lnagaintbl_nphy(pi);
-void wlc_phy_cal_init_nphy(phy_info_t *pi)
-{
-}
+ if (pi->nphy_elna_gain_config) {
+ regval[0] = 0;
+ regval[1] = 1;
+ regval[2] = 1;
+ regval[3] = 1;
+ wlc_phy_table_write_nphy(pi, 2, 4, 8, 16, regval);
+ wlc_phy_table_write_nphy(pi, 3, 4, 8, 16, regval);
-static void wlc_phy_war_force_trsw_to_R_cliplo_nphy(phy_info_t *pi, u8 core)
-{
- if (core == PHY_CORE_0) {
- write_phy_reg(pi, 0x38, 0x4);
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- write_phy_reg(pi, 0x37, 0x0060);
- } else {
- write_phy_reg(pi, 0x37, 0x1080);
+ for (ctr = 0; ctr < 4; ctr++)
+ regval[ctr] = (hpf_code << 8) | 0x74;
+ wlc_phy_table_write_nphy(pi, 7, 4, 0x106, 16, regval);
}
- } else if (core == PHY_CORE_1) {
- write_phy_reg(pi, 0x2ae, 0x4);
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- write_phy_reg(pi, 0x2ad, 0x0060);
- } else {
- write_phy_reg(pi, 0x2ad, 0x1080);
+
+ if (NREV_IS(pi->pubpi.phy_rev, 2)) {
+ for (ctr = 0; ctr < 21; ctr++)
+ regval[ctr] = 3 * ctr;
+ wlc_phy_table_write_nphy(pi, 0, 21, 32, 16, regval);
+ wlc_phy_table_write_nphy(pi, 1, 21, 32, 16, regval);
+
+ for (ctr = 0; ctr < 21; ctr++)
+ regval[ctr] = (u16) ctr;
+ wlc_phy_table_write_nphy(pi, 2, 21, 32, 16, regval);
+ wlc_phy_table_write_nphy(pi, 3, 21, 32, 16, regval);
}
- }
-}
-static void wlc_phy_war_txchain_upd_nphy(phy_info_t *pi, u8 txchain)
-{
- u8 txchain0, txchain1;
+ wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_UPDATEGAINU,
+ rfseq_updategainu_events,
+ rfseq_updategainu_dlys,
+ sizeof(rfseq_updategainu_events) /
+ sizeof(rfseq_updategainu_events[0]));
- txchain0 = txchain & 0x1;
- txchain1 = (txchain & 0x2) >> 1;
- if (!txchain0) {
- wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_0);
- }
+ mod_phy_reg(pi, 0x153, (0xff << 8), (90 << 8));
- if (!txchain1) {
- wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_1);
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ mod_phy_reg(pi,
+ (NPHY_TO_BPHY_OFF + BPHY_OPTIONAL_MODES),
+ 0x7f, 0x4);
}
}
-static void wlc_phy_workarounds_nphy(phy_info_t *pi)
+static void wlc_phy_workarounds_nphy(struct brcms_phy *pi)
{
u8 rfseq_rx2tx_events[] = {
NPHY_RFSEQ_CMD_NOP,
@@ -15365,18 +16312,15 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
u16 freq;
int coreNum;
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
+ if (CHSPEC_IS5G(pi->radio_chanspec))
wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_cck_en, 0);
- } else {
+ else
wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_cck_en, 1);
- }
if (pi->phyhang_avoid)
wlc_phy_stay_in_carriersearch_nphy(pi, true);
- if (!ISSIM_ENAB(pi->sh->sih)) {
- or_phy_reg(pi, 0xb1, NPHY_IQFlip_ADC1 | NPHY_IQFlip_ADC2);
- }
+ or_phy_reg(pi, 0xb1, NPHY_IQFlip_ADC1 | NPHY_IQFlip_ADC2);
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
@@ -15406,9 +16350,8 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
write_phy_reg(pi, 0x240, 0x1b0);
}
- if (NREV_GE(pi->pubpi.phy_rev, 8)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 8))
mod_phy_reg(pi, 0xbd, (0xff << 0), (114 << 0));
- }
wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x00, 16,
&dac_control);
@@ -15427,7 +16370,7 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x16e, 16,
rfseq_rx2tx_dacbufpu_rev7);
- if (PHY_IPA(pi)) {
+ if (PHY_IPA(pi))
wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX,
rfseq_rx2tx_events_rev3_ipa,
rfseq_rx2tx_dlys_rev3_ipa,
@@ -15436,7 +16379,6 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
sizeof
(rfseq_rx2tx_events_rev3_ipa
[0]));
- }
mod_phy_reg(pi, 0x299, (0x3 << 14), (0x1 << 14));
mod_phy_reg(pi, 0x29d, (0x3 << 14), (0x1 << 14));
@@ -15453,11 +16395,13 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
|| (pi->pubpi.radiorev == 8)) {
rccal_bcap_val =
- read_radio_reg(pi,
- RADIO_2057_RCCAL_BCAP_VAL);
+ read_radio_reg(
+ pi,
+ RADIO_2057_RCCAL_BCAP_VAL);
rccal_scap_val =
- read_radio_reg(pi,
- RADIO_2057_RCCAL_SCAP_VAL);
+ read_radio_reg(
+ pi,
+ RADIO_2057_RCCAL_SCAP_VAL);
rccal_tx20_11b_bcap = rccal_bcap_val;
rccal_tx20_11b_scap = rccal_scap_val;
@@ -15502,11 +16446,13 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
tx_lpf_bw_ofdm_40mhz = 3;
rccal_bcap_val =
- read_radio_reg(pi,
- RADIO_2057_RCCAL_BCAP_VAL);
+ read_radio_reg(
+ pi,
+ RADIO_2057_RCCAL_BCAP_VAL);
rccal_scap_val =
- read_radio_reg(pi,
- RADIO_2057_RCCAL_SCAP_VAL);
+ read_radio_reg(
+ pi,
+ RADIO_2057_RCCAL_SCAP_VAL);
rccal_tx20_11b_bcap = rccal_bcap_val;
rccal_tx20_11b_scap = rccal_scap_val;
@@ -15522,70 +16468,83 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
if (rccal_ovrd) {
- rx2tx_lpf_rc_lut_tx20_11b = (rccal_tx20_11b_bcap << 8) |
- (rccal_tx20_11b_scap << 3) | tx_lpf_bw_11b;
- rx2tx_lpf_rc_lut_tx20_11n = (rccal_tx20_11n_bcap << 8) |
- (rccal_tx20_11n_scap << 3) | tx_lpf_bw_ofdm_20mhz;
- rx2tx_lpf_rc_lut_tx40_11n = (rccal_tx40_11n_bcap << 8) |
- (rccal_tx40_11n_scap << 3) | tx_lpf_bw_ofdm_40mhz;
+ rx2tx_lpf_rc_lut_tx20_11b =
+ (rccal_tx20_11b_bcap << 8) |
+ (rccal_tx20_11b_scap << 3) |
+ tx_lpf_bw_11b;
+ rx2tx_lpf_rc_lut_tx20_11n =
+ (rccal_tx20_11n_bcap << 8) |
+ (rccal_tx20_11n_scap << 3) |
+ tx_lpf_bw_ofdm_20mhz;
+ rx2tx_lpf_rc_lut_tx40_11n =
+ (rccal_tx40_11n_bcap << 8) |
+ (rccal_tx40_11n_scap << 3) |
+ tx_lpf_bw_ofdm_40mhz;
for (coreNum = 0; coreNum <= 1; coreNum++) {
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- 1,
- 0x152 + coreNum * 0x10,
- 16,
- &rx2tx_lpf_rc_lut_tx20_11b);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- 1,
- 0x153 + coreNum * 0x10,
- 16,
- &rx2tx_lpf_rc_lut_tx20_11n);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- 1,
- 0x154 + coreNum * 0x10,
- 16,
- &rx2tx_lpf_rc_lut_tx20_11n);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- 1,
- 0x155 + coreNum * 0x10,
- 16,
- &rx2tx_lpf_rc_lut_tx40_11n);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- 1,
- 0x156 + coreNum * 0x10,
- 16,
- &rx2tx_lpf_rc_lut_tx40_11n);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- 1,
- 0x157 + coreNum * 0x10,
- 16,
- &rx2tx_lpf_rc_lut_tx40_11n);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- 1,
- 0x158 + coreNum * 0x10,
- 16,
- &rx2tx_lpf_rc_lut_tx40_11n);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- 1,
- 0x159 + coreNum * 0x10,
- 16,
- &rx2tx_lpf_rc_lut_tx40_11n);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_RFSEQ,
+ 1,
+ 0x152 + coreNum * 0x10,
+ 16,
+ &rx2tx_lpf_rc_lut_tx20_11b);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_RFSEQ,
+ 1,
+ 0x153 + coreNum * 0x10,
+ 16,
+ &rx2tx_lpf_rc_lut_tx20_11n);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_RFSEQ,
+ 1,
+ 0x154 + coreNum * 0x10,
+ 16,
+ &rx2tx_lpf_rc_lut_tx20_11n);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_RFSEQ,
+ 1,
+ 0x155 + coreNum * 0x10,
+ 16,
+ &rx2tx_lpf_rc_lut_tx40_11n);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_RFSEQ,
+ 1,
+ 0x156 + coreNum * 0x10,
+ 16,
+ &rx2tx_lpf_rc_lut_tx40_11n);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_RFSEQ,
+ 1,
+ 0x157 + coreNum * 0x10,
+ 16,
+ &rx2tx_lpf_rc_lut_tx40_11n);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_RFSEQ,
+ 1,
+ 0x158 + coreNum * 0x10,
+ 16,
+ &rx2tx_lpf_rc_lut_tx40_11n);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_RFSEQ,
+ 1,
+ 0x159 + coreNum * 0x10,
+ 16,
+ &rx2tx_lpf_rc_lut_tx40_11n);
}
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4),
- 1, 0x3, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID2);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 4),
+ 1, 0x3, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID2);
}
- if (!NORADIO_ENAB(pi->pubpi)) {
- write_phy_reg(pi, 0x32f, 0x3);
- }
+ write_phy_reg(pi, 0x32f, 0x3);
- if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2),
- 1, 0x3, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- }
+ if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6))
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 2),
+ 1, 0x3, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) ||
(pi->pubpi.radiorev == 6)) {
@@ -15640,17 +16599,16 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
if (CHSPEC_IS2G(pi->radio_chanspec)) {
if ((pi->pubpi.radiorev == 3)
|| (pi->pubpi.radiorev == 4)
- || (pi->pubpi.radiorev == 6)) {
-
+ || (pi->pubpi.radiorev == 6))
txgm_idac_bleed = 0x7f;
- }
for (coreNum = 0; coreNum <= 1; coreNum++) {
if (txgm_idac_bleed != 0)
- WRITE_RADIO_REG4(pi, RADIO_2057,
- CORE, coreNum,
- TXGM_IDAC_BLEED,
- txgm_idac_bleed);
+ WRITE_RADIO_REG4(
+ pi, RADIO_2057,
+ CORE, coreNum,
+ TXGM_IDAC_BLEED,
+ txgm_idac_bleed);
}
if (pi->pubpi.radiorev == 5) {
@@ -15665,18 +16623,20 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
CORE, coreNum,
IPA2G_IMAIN,
0x1f);
- WRITE_RADIO_REG4(pi, RADIO_2057,
- CORE, coreNum,
- IPA2G_BIAS_FILTER,
- 0xee);
+ WRITE_RADIO_REG4(
+ pi, RADIO_2057,
+ CORE, coreNum,
+ IPA2G_BIAS_FILTER,
+ 0xee);
WRITE_RADIO_REG4(pi, RADIO_2057,
CORE, coreNum,
PAD2G_IDACS,
0x8a);
- WRITE_RADIO_REG4(pi, RADIO_2057,
- CORE, coreNum,
- PAD_BIAS_FILTER_BWS,
- 0x3e);
+ WRITE_RADIO_REG4(
+ pi, RADIO_2057,
+ CORE, coreNum,
+ PAD_BIAS_FILTER_BWS,
+ 0x3e);
}
} else if ((pi->pubpi.radiorev == 7)
@@ -15705,9 +16665,8 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
}
} else {
- freq =
- CHAN5G_FREQ(CHSPEC_CHANNEL
- (pi->radio_chanspec));
+ freq = CHAN5G_FREQ(CHSPEC_CHANNEL(
+ pi->radio_chanspec));
if (((freq >= 5180) && (freq <= 5230))
|| ((freq >= 5745) && (freq <= 5805))) {
WRITE_RADIO_REG4(pi, RADIO_2057, CORE,
@@ -15814,12 +16773,12 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
wlc_phy_workarounds_nphy_gainctrl(pi);
pdetrange =
- (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.
- pdetrange : pi->srom_fem2g.pdetrange;
+ (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.
+ pdetrange : pi->srom_fem2g.pdetrange;
if (pdetrange == 0) {
chan_freq_range =
- wlc_phy_get_chan_freq_range_nphy(pi, 0);
+ wlc_phy_get_chan_freq_range_nphy(pi, 0);
if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) {
aux_adc_vmid_rev7_core0[3] = 0x70;
aux_adc_vmid_rev7_core1[3] = 0x70;
@@ -15847,15 +16806,15 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
if (chan_freq_range ==
WL_CHAN_FREQ_RANGE_2G) {
aux_adc_vmid_rev7_core0[3] =
- 0x8c;
+ 0x8c;
aux_adc_vmid_rev7_core1[3] =
- 0x8c;
+ 0x8c;
aux_adc_gain_rev7[3] = 0;
} else {
aux_adc_vmid_rev7_core0[3] =
- 0x96;
+ 0x96;
aux_adc_vmid_rev7_core1[3] =
- 0x96;
+ 0x96;
aux_adc_gain_rev7[3] = 0;
}
}
@@ -15923,7 +16882,7 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
sizeof(rfseq_tx2rx_events_rev3) /
sizeof(rfseq_tx2rx_events_rev3[0]));
- if (PHY_IPA(pi)) {
+ if (PHY_IPA(pi))
wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX,
rfseq_rx2tx_events_rev3_ipa,
rfseq_rx2tx_dlys_rev3_ipa,
@@ -15932,7 +16891,6 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
sizeof
(rfseq_rx2tx_events_rev3_ipa
[0]));
- }
if ((pi->sh->hw_phyrxchain != 0x3) &&
(pi->sh->hw_phyrxchain != pi->sh->hw_phytxchain)) {
@@ -15941,22 +16899,21 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
rfseq_rx2tx_dlys_rev3[5] = 59;
rfseq_rx2tx_dlys_rev3[6] = 1;
rfseq_rx2tx_events_rev3[7] =
- NPHY_REV3_RFSEQ_CMD_END;
+ NPHY_REV3_RFSEQ_CMD_END;
}
- wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX,
- rfseq_rx2tx_events_rev3,
- rfseq_rx2tx_dlys_rev3,
- sizeof(rfseq_rx2tx_events_rev3) /
- sizeof(rfseq_rx2tx_events_rev3
- [0]));
+ wlc_phy_set_rfseq_nphy(
+ pi, NPHY_RFSEQ_RX2TX,
+ rfseq_rx2tx_events_rev3,
+ rfseq_rx2tx_dlys_rev3,
+ sizeof(rfseq_rx2tx_events_rev3) /
+ sizeof(rfseq_rx2tx_events_rev3[0]));
}
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if (CHSPEC_IS2G(pi->radio_chanspec))
write_phy_reg(pi, 0x6a, 0x2);
- } else {
+ else
write_phy_reg(pi, 0x6a, 0x9c40);
- }
mod_phy_reg(pi, 0x294, (0xf << 8), (7 << 8));
@@ -15983,8 +16940,8 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
&dac_control);
pdetrange =
- (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.
- pdetrange : pi->srom_fem2g.pdetrange;
+ (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.
+ pdetrange : pi->srom_fem2g.pdetrange;
if (pdetrange == 0) {
if (NREV_GE(pi->pubpi.phy_rev, 4)) {
@@ -15995,7 +16952,7 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
aux_adc_gain = aux_adc_gain_rev3;
}
chan_freq_range =
- wlc_phy_get_chan_freq_range_nphy(pi, 0);
+ wlc_phy_get_chan_freq_range_nphy(pi, 0);
if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) {
switch (chan_freq_range) {
case WL_CHAN_FREQ_RANGE_5GL:
@@ -16038,7 +16995,7 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
if (NREV_GE(pi->pubpi.phy_rev, 6)) {
chan_freq_range =
- wlc_phy_get_chan_freq_range_nphy(pi, 0);
+ wlc_phy_get_chan_freq_range_nphy(pi, 0);
if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) {
bcm_adc_vmid[3] = 0x8e;
bcm_adc_gain[3] = 0x03;
@@ -16061,14 +17018,16 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
0x1c, 16, bcm_adc_gain);
} else if (pdetrange == 3) {
chan_freq_range =
- wlc_phy_get_chan_freq_range_nphy(pi, 0);
+ wlc_phy_get_chan_freq_range_nphy(pi, 0);
if ((NREV_GE(pi->pubpi.phy_rev, 4))
&& (chan_freq_range == WL_CHAN_FREQ_RANGE_2G)) {
u16 auxadc_vmid[] = {
- 0xa2, 0xb4, 0xb4, 0x270 };
+ 0xa2, 0xb4, 0xb4, 0x270
+ };
u16 auxadc_gain[] = {
- 0x02, 0x02, 0x02, 0x00 };
+ 0x02, 0x02, 0x02, 0x00
+ };
wlc_phy_table_write_nphy(pi,
NPHY_TBL_ID_AFECTRL, 4,
@@ -16089,7 +17048,7 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
u16 Vmid[2], Av[2];
chan_freq_range =
- wlc_phy_get_chan_freq_range_nphy(pi, 0);
+ wlc_phy_get_chan_freq_range_nphy(pi, 0);
if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) {
Vmid[0] = (pdetrange == 4) ? 0x8e : 0x89;
Vmid[1] = (pdetrange == 4) ? 0x96 : 0x89;
@@ -16160,8 +17119,8 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
0x0);
triso =
- (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.
- triso : pi->srom_fem2g.triso;
+ (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.
+ triso : pi->srom_fem2g.triso;
if (triso == 7) {
wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_0);
wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_1);
@@ -16217,11 +17176,10 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
if (NREV_GE(pi->pubpi.phy_rev, 6)) {
- if (pi->sh->boardflags2 & BFL2_SINGLEANT_CCK) {
+ if (pi->sh->boardflags2 & BFL2_SINGLEANT_CCK)
wlapi_bmac_mhf(pi->sh->physhim, MHF4,
- MHF4_BPHY_TXCORE0,
- MHF4_BPHY_TXCORE0, WLC_BAND_ALL);
- }
+ MHF4_BPHY_TXCORE0,
+ MHF4_BPHY_TXCORE0, BRCM_BAND_ALL);
}
} else {
@@ -16288,7 +17246,7 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
wlapi_bmac_mhf(pi->sh->physhim, MHF3,
MHF3_NPHY_MLADV_WAR,
MHF3_NPHY_MLADV_WAR,
- WLC_BAND_ALL);
+ BRCM_BAND_ALL);
} else if (NREV_IS(pi->pubpi.phy_rev, 2)) {
write_phy_reg(pi, 0x1e3, 0x0);
@@ -16319,1124 +17277,2723 @@ static void wlc_phy_workarounds_nphy(phy_info_t *pi)
write_phy_reg(pi, 0x194, 0x0);
}
- if (NREV_IS(pi->pubpi.phy_rev, 2)) {
+ if (NREV_IS(pi->pubpi.phy_rev, 2))
mod_phy_reg(pi, 0x221,
NPHY_FORCESIG_DECODEGATEDCLKS,
NPHY_FORCESIG_DECODEGATEDCLKS);
- }
}
if (pi->phyhang_avoid)
wlc_phy_stay_in_carriersearch_nphy(pi, false);
}
-static void wlc_phy_workarounds_nphy_gainctrl(phy_info_t *pi)
+static void wlc_phy_extpa_set_tx_digi_filts_nphy(struct brcms_phy *pi)
{
- u16 w1th, hpf_code, currband;
- int ctr;
- u8 rfseq_updategainu_events[] = {
- NPHY_RFSEQ_CMD_RX_GAIN,
- NPHY_RFSEQ_CMD_CLR_HIQ_DIS,
- NPHY_RFSEQ_CMD_SET_HPF_BW
- };
- u8 rfseq_updategainu_dlys[] = { 10, 30, 1 };
- s8 lna1G_gain_db[] = { 7, 11, 16, 23 };
- s8 lna1G_gain_db_rev4[] = { 8, 12, 17, 25 };
- s8 lna1G_gain_db_rev5[] = { 9, 13, 18, 26 };
- s8 lna1G_gain_db_rev6[] = { 8, 13, 18, 25 };
- s8 lna1G_gain_db_rev6_224B0[] = { 10, 14, 19, 27 };
- s8 lna1A_gain_db[] = { 7, 11, 17, 23 };
- s8 lna1A_gain_db_rev4[] = { 8, 12, 18, 23 };
- s8 lna1A_gain_db_rev5[] = { 6, 10, 16, 21 };
- s8 lna1A_gain_db_rev6[] = { 6, 10, 16, 21 };
- s8 *lna1_gain_db = NULL;
- s8 lna2G_gain_db[] = { -5, 6, 10, 14 };
- s8 lna2G_gain_db_rev5[] = { -3, 7, 11, 16 };
- s8 lna2G_gain_db_rev6[] = { -5, 6, 10, 14 };
- s8 lna2G_gain_db_rev6_224B0[] = { -5, 6, 10, 15 };
- s8 lna2A_gain_db[] = { -6, 2, 6, 10 };
- s8 lna2A_gain_db_rev4[] = { -5, 2, 6, 10 };
- s8 lna2A_gain_db_rev5[] = { -7, 0, 4, 8 };
- s8 lna2A_gain_db_rev6[] = { -7, 0, 4, 8 };
- s8 *lna2_gain_db = NULL;
- s8 tiaG_gain_db[] = {
- 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A };
- s8 tiaA_gain_db[] = {
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13 };
- s8 tiaA_gain_db_rev4[] = {
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d };
- s8 tiaA_gain_db_rev5[] = {
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d };
- s8 tiaA_gain_db_rev6[] = {
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d };
- s8 *tia_gain_db;
- s8 tiaG_gainbits[] = {
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 };
- s8 tiaA_gainbits[] = {
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 };
- s8 tiaA_gainbits_rev4[] = {
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 };
- s8 tiaA_gainbits_rev5[] = {
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 };
- s8 tiaA_gainbits_rev6[] = {
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 };
- s8 *tia_gainbits;
- s8 lpf_gain_db[] = { 0x00, 0x06, 0x0c, 0x12, 0x12, 0x12 };
- s8 lpf_gainbits[] = { 0x00, 0x01, 0x02, 0x03, 0x03, 0x03 };
- u16 rfseqG_init_gain[] = { 0x613f, 0x613f, 0x613f, 0x613f };
- u16 rfseqG_init_gain_rev4[] = { 0x513f, 0x513f, 0x513f, 0x513f };
- u16 rfseqG_init_gain_rev5[] = { 0x413f, 0x413f, 0x413f, 0x413f };
- u16 rfseqG_init_gain_rev5_elna[] = {
- 0x013f, 0x013f, 0x013f, 0x013f };
- u16 rfseqG_init_gain_rev6[] = { 0x513f, 0x513f };
- u16 rfseqG_init_gain_rev6_224B0[] = { 0x413f, 0x413f };
- u16 rfseqG_init_gain_rev6_elna[] = { 0x113f, 0x113f };
- u16 rfseqA_init_gain[] = { 0x516f, 0x516f, 0x516f, 0x516f };
- u16 rfseqA_init_gain_rev4[] = { 0x614f, 0x614f, 0x614f, 0x614f };
- u16 rfseqA_init_gain_rev4_elna[] = {
- 0x314f, 0x314f, 0x314f, 0x314f };
- u16 rfseqA_init_gain_rev5[] = { 0x714f, 0x714f, 0x714f, 0x714f };
- u16 rfseqA_init_gain_rev6[] = { 0x714f, 0x714f };
- u16 *rfseq_init_gain;
- u16 initG_gaincode = 0x627e;
- u16 initG_gaincode_rev4 = 0x527e;
- u16 initG_gaincode_rev5 = 0x427e;
- u16 initG_gaincode_rev5_elna = 0x027e;
- u16 initG_gaincode_rev6 = 0x527e;
- u16 initG_gaincode_rev6_224B0 = 0x427e;
- u16 initG_gaincode_rev6_elna = 0x127e;
- u16 initA_gaincode = 0x52de;
- u16 initA_gaincode_rev4 = 0x629e;
- u16 initA_gaincode_rev4_elna = 0x329e;
- u16 initA_gaincode_rev5 = 0x729e;
- u16 initA_gaincode_rev6 = 0x729e;
- u16 init_gaincode;
- u16 clip1hiG_gaincode = 0x107e;
- u16 clip1hiG_gaincode_rev4 = 0x007e;
- u16 clip1hiG_gaincode_rev5 = 0x1076;
- u16 clip1hiG_gaincode_rev6 = 0x007e;
- u16 clip1hiA_gaincode = 0x00de;
- u16 clip1hiA_gaincode_rev4 = 0x029e;
- u16 clip1hiA_gaincode_rev5 = 0x029e;
- u16 clip1hiA_gaincode_rev6 = 0x029e;
- u16 clip1hi_gaincode;
- u16 clip1mdG_gaincode = 0x0066;
- u16 clip1mdA_gaincode = 0x00ca;
- u16 clip1mdA_gaincode_rev4 = 0x1084;
- u16 clip1mdA_gaincode_rev5 = 0x2084;
- u16 clip1mdA_gaincode_rev6 = 0x2084;
- u16 clip1md_gaincode = 0;
- u16 clip1loG_gaincode = 0x0074;
- u16 clip1loG_gaincode_rev5[] = {
- 0x0062, 0x0064, 0x006a, 0x106a, 0x106c, 0x1074, 0x107c, 0x207c
- };
- u16 clip1loG_gaincode_rev6[] = {
- 0x106a, 0x106c, 0x1074, 0x107c, 0x007e, 0x107e, 0x207e, 0x307e
- };
- u16 clip1loG_gaincode_rev6_224B0 = 0x1074;
- u16 clip1loA_gaincode = 0x00cc;
- u16 clip1loA_gaincode_rev4 = 0x0086;
- u16 clip1loA_gaincode_rev5 = 0x2086;
- u16 clip1loA_gaincode_rev6 = 0x2086;
- u16 clip1lo_gaincode;
- u8 crsminG_th = 0x18;
- u8 crsminG_th_rev5 = 0x18;
- u8 crsminG_th_rev6 = 0x18;
- u8 crsminA_th = 0x1e;
- u8 crsminA_th_rev4 = 0x24;
- u8 crsminA_th_rev5 = 0x24;
- u8 crsminA_th_rev6 = 0x24;
- u8 crsmin_th;
- u8 crsminlG_th = 0x18;
- u8 crsminlG_th_rev5 = 0x18;
- u8 crsminlG_th_rev6 = 0x18;
- u8 crsminlA_th = 0x1e;
- u8 crsminlA_th_rev4 = 0x24;
- u8 crsminlA_th_rev5 = 0x24;
- u8 crsminlA_th_rev6 = 0x24;
- u8 crsminl_th = 0;
- u8 crsminuG_th = 0x18;
- u8 crsminuG_th_rev5 = 0x18;
- u8 crsminuG_th_rev6 = 0x18;
- u8 crsminuA_th = 0x1e;
- u8 crsminuA_th_rev4 = 0x24;
- u8 crsminuA_th_rev5 = 0x24;
- u8 crsminuA_th_rev6 = 0x24;
- u8 crsminuA_th_rev6_224B0 = 0x2d;
- u8 crsminu_th;
- u16 nbclipG_th = 0x20d;
- u16 nbclipG_th_rev4 = 0x1a1;
- u16 nbclipG_th_rev5 = 0x1d0;
- u16 nbclipG_th_rev6 = 0x1d0;
- u16 nbclipA_th = 0x1a1;
- u16 nbclipA_th_rev4 = 0x107;
- u16 nbclipA_th_rev5 = 0x0a9;
- u16 nbclipA_th_rev6 = 0x0f0;
- u16 nbclip_th = 0;
- u8 w1clipG_th = 5;
- u8 w1clipG_th_rev5 = 9;
- u8 w1clipG_th_rev6 = 5;
- u8 w1clipA_th = 25, w1clip_th;
- u8 rssi_gain_default = 0x50;
- u8 rssiG_gain_rev6_224B0 = 0x50;
- u8 rssiA_gain_rev5 = 0x90;
- u8 rssiA_gain_rev6 = 0x90;
- u8 rssi_gain;
- u16 regval[21];
- u8 triso;
+ int j, type = 2;
+ u16 addr_offset = 0x2c5;
- triso = (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.triso :
- pi->srom_fem2g.triso;
+ for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++)
+ write_phy_reg(pi, addr_offset + j,
+ NPHY_IPA_REV4_txdigi_filtcoeffs[type][j]);
+}
+
+static void wlc_phy_clip_det_nphy(struct brcms_phy *pi, u8 write, u16 *vals)
+{
+
+ if (write == 0) {
+ vals[0] = read_phy_reg(pi, 0x2c);
+ vals[1] = read_phy_reg(pi, 0x42);
+ } else {
+ write_phy_reg(pi, 0x2c, vals[0]);
+ write_phy_reg(pi, 0x42, vals[1]);
+ }
+}
+
+static void wlc_phy_ipa_internal_tssi_setup_nphy(struct brcms_phy *pi)
+{
+ u8 core;
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- if (pi->pubpi.radiorev == 5) {
+ for (core = 0; core < pi->pubpi.phy_corenum; core++) {
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TX_SSI_MASTER, 0x5);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TX_SSI_MUX, 0xe);
- wlc_phy_workarounds_nphy_gainctrl_2057_rev5(pi);
- } else if (pi->pubpi.radiorev == 7) {
- wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi);
+ if (pi->pubpi.radiorev != 5)
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX,
+ core, TSSIA, 0);
- mod_phy_reg(pi, 0x283, (0xff << 0), (0x44 << 0));
- mod_phy_reg(pi, 0x280, (0xff << 0), (0x44 << 0));
+ if (!NREV_IS(pi->pubpi.phy_rev, 7))
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX,
+ core, TSSIG, 0x1);
+ else
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX,
+ core, TSSIG, 0x31);
+ } else {
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TX_SSI_MASTER, 0x9);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TX_SSI_MUX, 0xc);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TSSIG, 0);
- } else if ((pi->pubpi.radiorev == 3)
- || (pi->pubpi.radiorev == 8)) {
- wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi);
+ if (pi->pubpi.radiorev != 5) {
+ if (!NREV_IS(pi->pubpi.phy_rev, 7))
+ WRITE_RADIO_REG3(pi, RADIO_2057,
+ TX, core,
+ TSSIA, 0x1);
+ else
+ WRITE_RADIO_REG3(pi, RADIO_2057,
+ TX, core,
+ TSSIA, 0x31);
+ }
+ }
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG,
+ 0);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC,
+ 0);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM,
+ 0x3);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1,
+ 0x0);
+ }
+ } else {
+ WRITE_RADIO_SYN(pi, RADIO_2056, RESERVED_ADDR31,
+ (CHSPEC_IS2G(pi->radio_chanspec)) ? 0x128 :
+ 0x80);
+ WRITE_RADIO_SYN(pi, RADIO_2056, RESERVED_ADDR30, 0x0);
+ WRITE_RADIO_SYN(pi, RADIO_2056, GPIO_MASTER1, 0x29);
- if (pi->pubpi.radiorev == 8) {
- mod_phy_reg(pi, 0x283,
- (0xff << 0), (0x44 << 0));
- mod_phy_reg(pi, 0x280,
- (0xff << 0), (0x44 << 0));
+ for (core = 0; core < pi->pubpi.phy_corenum; core++) {
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, IQCAL_VCM_HG,
+ 0x0);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, IQCAL_IDAC,
+ 0x0);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_VCM,
+ 0x3);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TX_AMP_DET,
+ 0x0);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC1,
+ 0x8);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC2,
+ 0x0);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC3,
+ 0x0);
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
+ TX_SSI_MASTER, 0x5);
+
+ if (pi->pubpi.radiorev != 5)
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX,
+ core, TSSIA, 0x0);
+ if (NREV_GE(pi->pubpi.phy_rev, 5))
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX,
+ core, TSSIG, 0x31);
+ else
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX,
+ core, TSSIG, 0x11);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
+ TX_SSI_MUX, 0xe);
+ } else {
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
+ TX_SSI_MASTER, 0x9);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
+ TSSIA, 0x31);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
+ TSSIG, 0x0);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
+ TX_SSI_MUX, 0xc);
}
- } else {
- wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi);
}
- } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ }
+}
- mod_phy_reg(pi, 0xa0, (0x1 << 6), (1 << 6));
+static void
+wlc_phy_rfctrl_override_nphy(struct brcms_phy *pi, u16 field, u16 value,
+ u8 core_mask, u8 off)
+{
+ u8 core_num;
+ u16 addr = 0, mask = 0, en_addr = 0, val_addr = 0, en_mask =
+ 0, val_mask = 0;
+ u8 shift = 0, val_shift = 0;
- mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13));
- mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13));
+ if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) {
- currband =
- read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand;
- if (currband == 0) {
- if (NREV_GE(pi->pubpi.phy_rev, 6)) {
- if (pi->pubpi.radiorev == 11) {
- lna1_gain_db = lna1G_gain_db_rev6_224B0;
- lna2_gain_db = lna2G_gain_db_rev6_224B0;
- rfseq_init_gain =
- rfseqG_init_gain_rev6_224B0;
- init_gaincode =
- initG_gaincode_rev6_224B0;
- clip1hi_gaincode =
- clip1hiG_gaincode_rev6;
- clip1lo_gaincode =
- clip1loG_gaincode_rev6_224B0;
- nbclip_th = nbclipG_th_rev6;
- w1clip_th = w1clipG_th_rev6;
- crsmin_th = crsminG_th_rev6;
- crsminl_th = crsminlG_th_rev6;
- crsminu_th = crsminuG_th_rev6;
- rssi_gain = rssiG_gain_rev6_224B0;
- } else {
- lna1_gain_db = lna1G_gain_db_rev6;
- lna2_gain_db = lna2G_gain_db_rev6;
- if (pi->sh->boardflags & BFL_EXTLNA) {
+ en_mask = field;
+ for (core_num = 0; core_num < 2; core_num++) {
- rfseq_init_gain =
- rfseqG_init_gain_rev6_elna;
- init_gaincode =
- initG_gaincode_rev6_elna;
- } else {
- rfseq_init_gain =
- rfseqG_init_gain_rev6;
- init_gaincode =
- initG_gaincode_rev6;
- }
- clip1hi_gaincode =
- clip1hiG_gaincode_rev6;
- switch (triso) {
- case 0:
- clip1lo_gaincode =
- clip1loG_gaincode_rev6[0];
- break;
- case 1:
- clip1lo_gaincode =
- clip1loG_gaincode_rev6[1];
- break;
- case 2:
- clip1lo_gaincode =
- clip1loG_gaincode_rev6[2];
- break;
- case 3:
- default:
+ switch (field) {
+ case (0x1 << 1):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a : 0x7d;
+ val_mask = (0x1 << 0);
+ val_shift = 0;
+ break;
+ case (0x1 << 2):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a : 0x7d;
+ val_mask = (0x1 << 1);
+ val_shift = 1;
+ break;
+ case (0x1 << 3):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a : 0x7d;
+ val_mask = (0x1 << 2);
+ val_shift = 2;
+ break;
+ case (0x1 << 4):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a : 0x7d;
+ val_mask = (0x1 << 4);
+ val_shift = 4;
+ break;
+ case (0x1 << 5):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a : 0x7d;
+ val_mask = (0x1 << 5);
+ val_shift = 5;
+ break;
+ case (0x1 << 6):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a : 0x7d;
+ val_mask = (0x1 << 6);
+ val_shift = 6;
+ break;
+ case (0x1 << 7):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a : 0x7d;
+ val_mask = (0x1 << 7);
+ val_shift = 7;
+ break;
+ case (0x1 << 8):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a : 0x7d;
+ val_mask = (0x7 << 8);
+ val_shift = 8;
+ break;
+ case (0x1 << 11):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7a : 0x7d;
+ val_mask = (0x7 << 13);
+ val_shift = 13;
+ break;
- clip1lo_gaincode =
- clip1loG_gaincode_rev6[3];
- break;
- case 4:
- clip1lo_gaincode =
- clip1loG_gaincode_rev6[4];
- break;
- case 5:
- clip1lo_gaincode =
- clip1loG_gaincode_rev6[5];
- break;
- case 6:
- clip1lo_gaincode =
- clip1loG_gaincode_rev6[6];
- break;
- case 7:
- clip1lo_gaincode =
- clip1loG_gaincode_rev6[7];
- break;
- }
- nbclip_th = nbclipG_th_rev6;
- w1clip_th = w1clipG_th_rev6;
- crsmin_th = crsminG_th_rev6;
- crsminl_th = crsminlG_th_rev6;
- crsminu_th = crsminuG_th_rev6;
- rssi_gain = rssi_gain_default;
- }
- } else if (NREV_IS(pi->pubpi.phy_rev, 5)) {
- lna1_gain_db = lna1G_gain_db_rev5;
- lna2_gain_db = lna2G_gain_db_rev5;
- if (pi->sh->boardflags & BFL_EXTLNA) {
+ case (0x1 << 9):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0xf8 : 0xfa;
+ val_mask = (0x7 << 0);
+ val_shift = 0;
+ break;
- rfseq_init_gain =
- rfseqG_init_gain_rev5_elna;
- init_gaincode =
- initG_gaincode_rev5_elna;
- } else {
- rfseq_init_gain = rfseqG_init_gain_rev5;
- init_gaincode = initG_gaincode_rev5;
- }
- clip1hi_gaincode = clip1hiG_gaincode_rev5;
- switch (triso) {
- case 0:
- clip1lo_gaincode =
- clip1loG_gaincode_rev5[0];
- break;
- case 1:
- clip1lo_gaincode =
- clip1loG_gaincode_rev5[1];
- break;
- case 2:
- clip1lo_gaincode =
- clip1loG_gaincode_rev5[2];
- break;
- case 3:
+ case (0x1 << 10):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0xf8 : 0xfa;
+ val_mask = (0x7 << 4);
+ val_shift = 4;
+ break;
- clip1lo_gaincode =
- clip1loG_gaincode_rev5[3];
- break;
- case 4:
- clip1lo_gaincode =
- clip1loG_gaincode_rev5[4];
- break;
- case 5:
- clip1lo_gaincode =
- clip1loG_gaincode_rev5[5];
- break;
- case 6:
- clip1lo_gaincode =
- clip1loG_gaincode_rev5[6];
- break;
- case 7:
- clip1lo_gaincode =
- clip1loG_gaincode_rev5[7];
- break;
- default:
- clip1lo_gaincode =
- clip1loG_gaincode_rev5[3];
- break;
- }
- nbclip_th = nbclipG_th_rev5;
- w1clip_th = w1clipG_th_rev5;
- crsmin_th = crsminG_th_rev5;
- crsminl_th = crsminlG_th_rev5;
- crsminu_th = crsminuG_th_rev5;
- rssi_gain = rssi_gain_default;
- } else if (NREV_IS(pi->pubpi.phy_rev, 4)) {
- lna1_gain_db = lna1G_gain_db_rev4;
- lna2_gain_db = lna2G_gain_db;
- rfseq_init_gain = rfseqG_init_gain_rev4;
- init_gaincode = initG_gaincode_rev4;
- clip1hi_gaincode = clip1hiG_gaincode_rev4;
- clip1lo_gaincode = clip1loG_gaincode;
- nbclip_th = nbclipG_th_rev4;
- w1clip_th = w1clipG_th;
- crsmin_th = crsminG_th;
- crsminl_th = crsminlG_th;
- crsminu_th = crsminuG_th;
- rssi_gain = rssi_gain_default;
- } else {
- lna1_gain_db = lna1G_gain_db;
- lna2_gain_db = lna2G_gain_db;
- rfseq_init_gain = rfseqG_init_gain;
- init_gaincode = initG_gaincode;
- clip1hi_gaincode = clip1hiG_gaincode;
- clip1lo_gaincode = clip1loG_gaincode;
- nbclip_th = nbclipG_th;
- w1clip_th = w1clipG_th;
- crsmin_th = crsminG_th;
- crsminl_th = crsminlG_th;
- crsminu_th = crsminuG_th;
- rssi_gain = rssi_gain_default;
+ case (0x1 << 12):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7b : 0x7e;
+ val_mask = (0xffff << 0);
+ val_shift = 0;
+ break;
+ case (0x1 << 13):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0x7c : 0x7f;
+ val_mask = (0xffff << 0);
+ val_shift = 0;
+ break;
+ case (0x1 << 14):
+ en_addr = (core_num == 0) ? 0xe7 : 0xec;
+ val_addr = (core_num == 0) ? 0xf9 : 0xfb;
+ val_mask = (0x3 << 6);
+ val_shift = 6;
+ break;
+ case (0x1 << 0):
+ en_addr = (core_num == 0) ? 0xe5 : 0xe6;
+ val_addr = (core_num == 0) ? 0xf9 : 0xfb;
+ val_mask = (0x1 << 15);
+ val_shift = 15;
+ break;
+ default:
+ addr = 0xffff;
+ break;
}
- tia_gain_db = tiaG_gain_db;
- tia_gainbits = tiaG_gainbits;
- clip1md_gaincode = clip1mdG_gaincode;
- } else {
- if (NREV_GE(pi->pubpi.phy_rev, 6)) {
- lna1_gain_db = lna1A_gain_db_rev6;
- lna2_gain_db = lna2A_gain_db_rev6;
- tia_gain_db = tiaA_gain_db_rev6;
- tia_gainbits = tiaA_gainbits_rev6;
- rfseq_init_gain = rfseqA_init_gain_rev6;
- init_gaincode = initA_gaincode_rev6;
- clip1hi_gaincode = clip1hiA_gaincode_rev6;
- clip1md_gaincode = clip1mdA_gaincode_rev6;
- clip1lo_gaincode = clip1loA_gaincode_rev6;
- crsmin_th = crsminA_th_rev6;
- crsminl_th = crsminlA_th_rev6;
- if ((pi->pubpi.radiorev == 11) &&
- (CHSPEC_IS40(pi->radio_chanspec) == 0)) {
- crsminu_th = crsminuA_th_rev6_224B0;
- } else {
- crsminu_th = crsminuA_th_rev6;
- }
- nbclip_th = nbclipA_th_rev6;
- rssi_gain = rssiA_gain_rev6;
- } else if (NREV_IS(pi->pubpi.phy_rev, 5)) {
- lna1_gain_db = lna1A_gain_db_rev5;
- lna2_gain_db = lna2A_gain_db_rev5;
- tia_gain_db = tiaA_gain_db_rev5;
- tia_gainbits = tiaA_gainbits_rev5;
- rfseq_init_gain = rfseqA_init_gain_rev5;
- init_gaincode = initA_gaincode_rev5;
- clip1hi_gaincode = clip1hiA_gaincode_rev5;
- clip1md_gaincode = clip1mdA_gaincode_rev5;
- clip1lo_gaincode = clip1loA_gaincode_rev5;
- crsmin_th = crsminA_th_rev5;
- crsminl_th = crsminlA_th_rev5;
- crsminu_th = crsminuA_th_rev5;
- nbclip_th = nbclipA_th_rev5;
- rssi_gain = rssiA_gain_rev5;
- } else if (NREV_IS(pi->pubpi.phy_rev, 4)) {
- lna1_gain_db = lna1A_gain_db_rev4;
- lna2_gain_db = lna2A_gain_db_rev4;
- tia_gain_db = tiaA_gain_db_rev4;
- tia_gainbits = tiaA_gainbits_rev4;
- if (pi->sh->boardflags & BFL_EXTLNA_5GHz) {
- rfseq_init_gain =
- rfseqA_init_gain_rev4_elna;
- init_gaincode =
- initA_gaincode_rev4_elna;
- } else {
- rfseq_init_gain = rfseqA_init_gain_rev4;
- init_gaincode = initA_gaincode_rev4;
- }
- clip1hi_gaincode = clip1hiA_gaincode_rev4;
- clip1md_gaincode = clip1mdA_gaincode_rev4;
- clip1lo_gaincode = clip1loA_gaincode_rev4;
- crsmin_th = crsminA_th_rev4;
- crsminl_th = crsminlA_th_rev4;
- crsminu_th = crsminuA_th_rev4;
- nbclip_th = nbclipA_th_rev4;
- rssi_gain = rssi_gain_default;
+ if (off) {
+ and_phy_reg(pi, en_addr, ~en_mask);
+ and_phy_reg(pi, val_addr, ~val_mask);
} else {
- lna1_gain_db = lna1A_gain_db;
- lna2_gain_db = lna2A_gain_db;
- tia_gain_db = tiaA_gain_db;
- tia_gainbits = tiaA_gainbits;
- rfseq_init_gain = rfseqA_init_gain;
- init_gaincode = initA_gaincode;
- clip1hi_gaincode = clip1hiA_gaincode;
- clip1md_gaincode = clip1mdA_gaincode;
- clip1lo_gaincode = clip1loA_gaincode;
- crsmin_th = crsminA_th;
- crsminl_th = crsminlA_th;
- crsminu_th = crsminuA_th;
- nbclip_th = nbclipA_th;
- rssi_gain = rssi_gain_default;
+
+ if ((core_mask == 0)
+ || (core_mask & (1 << core_num))) {
+ or_phy_reg(pi, en_addr, en_mask);
+
+ if (addr != 0xffff)
+ mod_phy_reg(pi, val_addr,
+ val_mask,
+ (value <<
+ val_shift));
+ }
}
- w1clip_th = w1clipA_th;
}
+ } else {
- write_radio_reg(pi,
- (RADIO_2056_RX_BIASPOLE_LNAG1_IDAC |
- RADIO_2056_RX0), 0x17);
- write_radio_reg(pi,
- (RADIO_2056_RX_BIASPOLE_LNAG1_IDAC |
- RADIO_2056_RX1), 0x17);
+ if (off) {
+ and_phy_reg(pi, 0xec, ~field);
+ value = 0x0;
+ } else {
+ or_phy_reg(pi, 0xec, field);
+ }
- write_radio_reg(pi, (RADIO_2056_RX_LNAG2_IDAC | RADIO_2056_RX0),
- 0xf0);
- write_radio_reg(pi, (RADIO_2056_RX_LNAG2_IDAC | RADIO_2056_RX1),
- 0xf0);
+ for (core_num = 0; core_num < 2; core_num++) {
- write_radio_reg(pi, (RADIO_2056_RX_RSSI_POLE | RADIO_2056_RX0),
- 0x0);
- write_radio_reg(pi, (RADIO_2056_RX_RSSI_POLE | RADIO_2056_RX1),
- 0x0);
+ switch (field) {
+ case (0x1 << 1):
+ case (0x1 << 9):
+ case (0x1 << 12):
+ case (0x1 << 13):
+ case (0x1 << 14):
+ addr = 0x78;
- write_radio_reg(pi, (RADIO_2056_RX_RSSI_GAIN | RADIO_2056_RX0),
- rssi_gain);
- write_radio_reg(pi, (RADIO_2056_RX_RSSI_GAIN | RADIO_2056_RX1),
- rssi_gain);
+ core_mask = 0x1;
+ break;
+ case (0x1 << 2):
+ case (0x1 << 3):
+ case (0x1 << 4):
+ case (0x1 << 5):
+ case (0x1 << 6):
+ case (0x1 << 7):
+ case (0x1 << 8):
+ addr = (core_num == 0) ? 0x7a : 0x7d;
+ break;
+ case (0x1 << 10):
+ addr = (core_num == 0) ? 0x7b : 0x7e;
+ break;
+ case (0x1 << 11):
+ addr = (core_num == 0) ? 0x7c : 0x7f;
+ break;
+ default:
+ addr = 0xffff;
+ }
- write_radio_reg(pi,
- (RADIO_2056_RX_BIASPOLE_LNAA1_IDAC |
- RADIO_2056_RX0), 0x17);
- write_radio_reg(pi,
- (RADIO_2056_RX_BIASPOLE_LNAA1_IDAC |
- RADIO_2056_RX1), 0x17);
+ switch (field) {
+ case (0x1 << 1):
+ mask = (0x7 << 3);
+ shift = 3;
+ break;
+ case (0x1 << 9):
+ mask = (0x1 << 2);
+ shift = 2;
+ break;
+ case (0x1 << 12):
+ mask = (0x1 << 8);
+ shift = 8;
+ break;
+ case (0x1 << 13):
+ mask = (0x1 << 9);
+ shift = 9;
+ break;
+ case (0x1 << 14):
+ mask = (0xf << 12);
+ shift = 12;
+ break;
+ case (0x1 << 2):
+ mask = (0x1 << 0);
+ shift = 0;
+ break;
+ case (0x1 << 3):
+ mask = (0x1 << 1);
+ shift = 1;
+ break;
+ case (0x1 << 4):
+ mask = (0x1 << 2);
+ shift = 2;
+ break;
+ case (0x1 << 5):
+ mask = (0x3 << 4);
+ shift = 4;
+ break;
+ case (0x1 << 6):
+ mask = (0x3 << 6);
+ shift = 6;
+ break;
+ case (0x1 << 7):
+ mask = (0x1 << 8);
+ shift = 8;
+ break;
+ case (0x1 << 8):
+ mask = (0x1 << 9);
+ shift = 9;
+ break;
+ case (0x1 << 10):
+ mask = 0x1fff;
+ shift = 0x0;
+ break;
+ case (0x1 << 11):
+ mask = 0x1fff;
+ shift = 0x0;
+ break;
+ default:
+ mask = 0x0;
+ shift = 0x0;
+ break;
+ }
- write_radio_reg(pi, (RADIO_2056_RX_LNAA2_IDAC | RADIO_2056_RX0),
- 0xFF);
- write_radio_reg(pi, (RADIO_2056_RX_LNAA2_IDAC | RADIO_2056_RX1),
- 0xFF);
+ if ((addr != 0xffff) && (core_mask & (1 << core_num)))
+ mod_phy_reg(pi, addr, mask, (value << shift));
+ }
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8,
- 8, lna1_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8,
- 8, lna1_gain_db);
+ or_phy_reg(pi, 0xec, (0x1 << 0));
+ or_phy_reg(pi, 0x78, (0x1 << 0));
+ udelay(1);
+ and_phy_reg(pi, 0xec, ~(0x1 << 0));
+ }
+}
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10,
- 8, lna2_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10,
- 8, lna2_gain_db);
+static void wlc_phy_txpwrctrl_idle_tssi_nphy(struct brcms_phy *pi)
+{
+ s32 rssi_buf[4];
+ s32 int_val;
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20,
- 8, tia_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20,
- 8, tia_gain_db);
+ if (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) || PHY_MUTED(pi))
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20,
- 8, tia_gainbits);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20,
- 8, tia_gainbits);
+ return;
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 6, 0x40,
- 8, &lpf_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 6, 0x40,
- 8, &lpf_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 6, 0x40,
- 8, &lpf_gainbits);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 6, 0x40,
- 8, &lpf_gainbits);
+ if (PHY_IPA(pi))
+ wlc_phy_ipa_internal_tssi_setup_nphy(pi);
- write_phy_reg(pi, 0x20, init_gaincode);
- write_phy_reg(pi, 0x2a7, init_gaincode);
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12),
+ 0, 0x3, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ else if (NREV_GE(pi->pubpi.phy_rev, 3))
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 3, 0);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- pi->pubpi.phy_corenum, 0x106, 16,
- rfseq_init_gain);
+ wlc_phy_stopplayback_nphy(pi);
- write_phy_reg(pi, 0x22, clip1hi_gaincode);
- write_phy_reg(pi, 0x2a9, clip1hi_gaincode);
+ wlc_phy_tx_tone_nphy(pi, 4000, 0, 0, 0, false);
- write_phy_reg(pi, 0x24, clip1md_gaincode);
- write_phy_reg(pi, 0x2ab, clip1md_gaincode);
+ udelay(20);
+ int_val =
+ wlc_phy_poll_rssi_nphy(pi, (u8) NPHY_RSSI_SEL_TSSI_2G, rssi_buf,
+ 1);
+ wlc_phy_stopplayback_nphy(pi);
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, 0);
- write_phy_reg(pi, 0x37, clip1lo_gaincode);
- write_phy_reg(pi, 0x2ad, clip1lo_gaincode);
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12),
+ 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ else if (NREV_GE(pi->pubpi.phy_rev, 3))
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 3, 1);
- mod_phy_reg(pi, 0x27d, (0xff << 0), (crsmin_th << 0));
- mod_phy_reg(pi, 0x280, (0xff << 0), (crsminl_th << 0));
- mod_phy_reg(pi, 0x283, (0xff << 0), (crsminu_th << 0));
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- write_phy_reg(pi, 0x2b, nbclip_th);
- write_phy_reg(pi, 0x41, nbclip_th);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_2g =
+ (u8) ((int_val >> 24) & 0xff);
+ pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_5g =
+ (u8) ((int_val >> 24) & 0xff);
- mod_phy_reg(pi, 0x27, (0x3f << 0), (w1clip_th << 0));
- mod_phy_reg(pi, 0x3d, (0x3f << 0), (w1clip_th << 0));
+ pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_2g =
+ (u8) ((int_val >> 8) & 0xff);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_5g =
+ (u8) ((int_val >> 8) & 0xff);
+ } else {
+ pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_2g =
+ (u8) ((int_val >> 24) & 0xff);
- write_phy_reg(pi, 0x150, 0x809c);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_2g =
+ (u8) ((int_val >> 8) & 0xff);
- } else {
+ pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_5g =
+ (u8) ((int_val >> 16) & 0xff);
+ pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_5g =
+ (u8) ((int_val) & 0xff);
+ }
- mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13));
- mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13));
+}
- write_phy_reg(pi, 0x2b, 0x84);
- write_phy_reg(pi, 0x41, 0x84);
+static void wlc_phy_txpwr_limit_to_tbl_nphy(struct brcms_phy *pi)
+{
+ u8 idx, idx2, i, delta_ind;
- if (CHSPEC_IS20(pi->radio_chanspec)) {
- write_phy_reg(pi, 0x6b, 0x2b);
- write_phy_reg(pi, 0x6c, 0x2b);
- write_phy_reg(pi, 0x6d, 0x9);
- write_phy_reg(pi, 0x6e, 0x9);
- }
+ for (idx = TXP_FIRST_CCK; idx <= TXP_LAST_CCK; idx++)
+ pi->adj_pwr_tbl_nphy[idx] = pi->tx_power_offset[idx];
- w1th = NPHY_RSSICAL_W1_TARGET - 4;
- mod_phy_reg(pi, 0x27, (0x3f << 0), (w1th << 0));
- mod_phy_reg(pi, 0x3d, (0x3f << 0), (w1th << 0));
+ for (i = 0; i < 4; i++) {
+ idx2 = 0;
- if (CHSPEC_IS20(pi->radio_chanspec)) {
- mod_phy_reg(pi, 0x1c, (0x1f << 0), (0x1 << 0));
- mod_phy_reg(pi, 0x32, (0x1f << 0), (0x1 << 0));
+ delta_ind = 0;
- mod_phy_reg(pi, 0x1d, (0x1f << 0), (0x1 << 0));
- mod_phy_reg(pi, 0x33, (0x1f << 0), (0x1 << 0));
- }
+ switch (i) {
+ case 0:
- write_phy_reg(pi, 0x150, 0x809c);
+ if (CHSPEC_IS40(pi->radio_chanspec)
+ && NPHY_IS_SROM_REINTERPRET) {
+ idx = TXP_FIRST_MCS_40_SISO;
+ } else {
+ idx = (CHSPEC_IS40(pi->radio_chanspec)) ?
+ TXP_FIRST_OFDM_40_SISO : TXP_FIRST_OFDM;
+ delta_ind = 1;
+ }
+ break;
- if (pi->nphy_gain_boost)
- if ((CHSPEC_IS2G(pi->radio_chanspec)) &&
- (CHSPEC_IS40(pi->radio_chanspec)))
- hpf_code = 4;
- else
- hpf_code = 5;
- else if (CHSPEC_IS40(pi->radio_chanspec))
- hpf_code = 6;
- else
- hpf_code = 7;
+ case 1:
- mod_phy_reg(pi, 0x20, (0x1f << 7), (hpf_code << 7));
- mod_phy_reg(pi, 0x36, (0x1f << 7), (hpf_code << 7));
+ idx = (CHSPEC_IS40(pi->radio_chanspec)) ?
+ TXP_FIRST_MCS_40_CDD : TXP_FIRST_MCS_20_CDD;
+ break;
- for (ctr = 0; ctr < 4; ctr++) {
- regval[ctr] = (hpf_code << 8) | 0x7c;
- }
- wlc_phy_table_write_nphy(pi, 7, 4, 0x106, 16, regval);
+ case 2:
- wlc_phy_adjust_lnagaintbl_nphy(pi);
+ idx = (CHSPEC_IS40(pi->radio_chanspec)) ?
+ TXP_FIRST_MCS_40_STBC : TXP_FIRST_MCS_20_STBC;
+ break;
- if (pi->nphy_elna_gain_config) {
- regval[0] = 0;
- regval[1] = 1;
- regval[2] = 1;
- regval[3] = 1;
- wlc_phy_table_write_nphy(pi, 2, 4, 8, 16, regval);
- wlc_phy_table_write_nphy(pi, 3, 4, 8, 16, regval);
+ case 3:
- for (ctr = 0; ctr < 4; ctr++) {
- regval[ctr] = (hpf_code << 8) | 0x74;
- }
- wlc_phy_table_write_nphy(pi, 7, 4, 0x106, 16, regval);
+ idx = (CHSPEC_IS40(pi->radio_chanspec)) ?
+ TXP_FIRST_MCS_40_SDM : TXP_FIRST_MCS_20_SDM;
+ break;
}
- if (NREV_IS(pi->pubpi.phy_rev, 2)) {
- for (ctr = 0; ctr < 21; ctr++) {
- regval[ctr] = 3 * ctr;
- }
- wlc_phy_table_write_nphy(pi, 0, 21, 32, 16, regval);
- wlc_phy_table_write_nphy(pi, 1, 21, 32, 16, regval);
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ idx = idx + delta_ind;
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx++];
- for (ctr = 0; ctr < 21; ctr++) {
- regval[ctr] = (u16) ctr;
- }
- wlc_phy_table_write_nphy(pi, 2, 21, 32, 16, regval);
- wlc_phy_table_write_nphy(pi, 3, 21, 32, 16, regval);
- }
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx++];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx++];
- wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_UPDATEGAINU,
- rfseq_updategainu_events,
- rfseq_updategainu_dlys,
- sizeof(rfseq_updategainu_events) /
- sizeof(rfseq_updategainu_events[0]));
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx++];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx++];
- mod_phy_reg(pi, 0x153, (0xff << 8), (90 << 8));
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx++];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ idx = idx + 1 - delta_ind;
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
- if (CHSPEC_IS2G(pi->radio_chanspec))
- mod_phy_reg(pi,
- (NPHY_TO_BPHY_OFF + BPHY_OPTIONAL_MODES),
- 0x7f, 0x4);
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
+ pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] =
+ pi->tx_power_offset[idx];
}
}
-static void wlc_phy_workarounds_nphy_gainctrl_2057_rev5(phy_info_t *pi)
+static void wlc_phy_txpwrctrl_pwr_setup_nphy(struct brcms_phy *pi)
{
- s8 lna1_gain_db[] = { 8, 13, 17, 22 };
- s8 lna2_gain_db[] = { -2, 7, 11, 15 };
- s8 tia_gain_db[] = { -4, -1, 2, 5, 5, 5, 5, 5, 5, 5 };
- s8 tia_gainbits[] = {
- 0x0, 0x01, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 };
+ u32 idx;
+ s16 a1[2], b0[2], b1[2];
+ s8 target_pwr_qtrdbm[2];
+ s32 num, den, pwr_est;
+ u8 chan_freq_range;
+ u8 idle_tssi[2];
+ u32 tbl_id, tbl_len, tbl_offset;
+ u32 regval[64];
+ u8 core;
- mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13));
- mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13));
+ if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) {
+ wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK);
+ (void)R_REG(&pi->regs->maccontrol);
+ udelay(1);
+ }
- mod_phy_reg(pi, 0x289, (0xff << 0), (0x46 << 0));
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, true);
- mod_phy_reg(pi, 0x283, (0xff << 0), (0x3c << 0));
- mod_phy_reg(pi, 0x280, (0xff << 0), (0x3c << 0));
+ or_phy_reg(pi, 0x122, (0x1 << 0));
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x8, 8,
- lna1_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x8, 8,
- lna1_gain_db);
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
+ and_phy_reg(pi, 0x1e7, (u16) (~(0x1 << 15)));
+ else
+ or_phy_reg(pi, 0x1e7, (0x1 << 15));
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, 8,
- lna2_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, 8,
- lna2_gain_db);
+ if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12))
+ wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8,
- tia_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8,
- tia_gain_db);
+ if (pi->sh->sromrev < 4) {
+ idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g;
+ idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g;
+ target_pwr_qtrdbm[0] = 13 * 4;
+ target_pwr_qtrdbm[1] = 13 * 4;
+ a1[0] = -424;
+ a1[1] = -424;
+ b0[0] = 5612;
+ b0[1] = 5612;
+ b1[1] = -1393;
+ b1[0] = -1393;
+ } else {
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8,
- tia_gainbits);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8,
- tia_gainbits);
+ chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0);
+ switch (chan_freq_range) {
+ case WL_CHAN_FREQ_RANGE_2G:
+ idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g;
+ idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g;
+ target_pwr_qtrdbm[0] =
+ pi->nphy_pwrctrl_info[0].max_pwr_2g;
+ target_pwr_qtrdbm[1] =
+ pi->nphy_pwrctrl_info[1].max_pwr_2g;
+ a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_a1;
+ a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_a1;
+ b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_b0;
+ b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_b0;
+ b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_b1;
+ b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_b1;
+ break;
+ case WL_CHAN_FREQ_RANGE_5GL:
+ idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g;
+ idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g;
+ target_pwr_qtrdbm[0] =
+ pi->nphy_pwrctrl_info[0].max_pwr_5gl;
+ target_pwr_qtrdbm[1] =
+ pi->nphy_pwrctrl_info[1].max_pwr_5gl;
+ a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_a1;
+ a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_a1;
+ b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_b0;
+ b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_b0;
+ b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_b1;
+ b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_b1;
+ break;
+ case WL_CHAN_FREQ_RANGE_5GM:
+ idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g;
+ idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g;
+ target_pwr_qtrdbm[0] =
+ pi->nphy_pwrctrl_info[0].max_pwr_5gm;
+ target_pwr_qtrdbm[1] =
+ pi->nphy_pwrctrl_info[1].max_pwr_5gm;
+ a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_a1;
+ a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_a1;
+ b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_b0;
+ b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_b0;
+ b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_b1;
+ b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_b1;
+ break;
+ case WL_CHAN_FREQ_RANGE_5GH:
+ idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g;
+ idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g;
+ target_pwr_qtrdbm[0] =
+ pi->nphy_pwrctrl_info[0].max_pwr_5gh;
+ target_pwr_qtrdbm[1] =
+ pi->nphy_pwrctrl_info[1].max_pwr_5gh;
+ a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_a1;
+ a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_a1;
+ b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_b0;
+ b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_b0;
+ b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_b1;
+ b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_b1;
+ break;
+ default:
+ idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g;
+ idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g;
+ target_pwr_qtrdbm[0] = 13 * 4;
+ target_pwr_qtrdbm[1] = 13 * 4;
+ a1[0] = -424;
+ a1[1] = -424;
+ b0[0] = 5612;
+ b0[1] = 5612;
+ b1[1] = -1393;
+ b1[0] = -1393;
+ break;
+ }
+ }
- write_phy_reg(pi, 0x37, 0x74);
- write_phy_reg(pi, 0x2ad, 0x74);
- write_phy_reg(pi, 0x38, 0x18);
- write_phy_reg(pi, 0x2ae, 0x18);
+ target_pwr_qtrdbm[0] = (s8) pi->tx_power_max;
+ target_pwr_qtrdbm[1] = (s8) pi->tx_power_max;
- write_phy_reg(pi, 0x2b, 0xe8);
- write_phy_reg(pi, 0x41, 0xe8);
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (pi->srom_fem2g.tssipos)
+ or_phy_reg(pi, 0x1e9, (0x1 << 14));
- if (CHSPEC_IS20(pi->radio_chanspec)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ for (core = 0; core <= 1; core++) {
+ if (PHY_IPA(pi)) {
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ WRITE_RADIO_REG3(pi, RADIO_2057,
+ TX, core,
+ TX_SSI_MUX,
+ 0xe);
+ else
+ WRITE_RADIO_REG3(pi, RADIO_2057,
+ TX, core,
+ TX_SSI_MUX,
+ 0xc);
+ }
+ }
+ } else {
+ if (PHY_IPA(pi)) {
- mod_phy_reg(pi, 0x300, (0x3f << 0), (0x12 << 0));
- mod_phy_reg(pi, 0x301, (0x3f << 0), (0x12 << 0));
- } else {
+ write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX |
+ RADIO_2056_TX0,
+ (CHSPEC_IS5G
+ (pi->radio_chanspec)) ?
+ 0xc : 0xe);
+ write_radio_reg(pi,
+ RADIO_2056_TX_TX_SSI_MUX |
+ RADIO_2056_TX1,
+ (CHSPEC_IS5G
+ (pi->radio_chanspec)) ?
+ 0xc : 0xe);
+ } else {
- mod_phy_reg(pi, 0x300, (0x3f << 0), (0x10 << 0));
- mod_phy_reg(pi, 0x301, (0x3f << 0), (0x10 << 0));
+ write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX |
+ RADIO_2056_TX0, 0x11);
+ write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX |
+ RADIO_2056_TX1, 0x11);
+ }
+ }
}
-}
-static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(phy_info_t *pi)
-{
- u16 currband;
- s8 lna1G_gain_db_rev7[] = { 9, 14, 19, 24 };
- s8 *lna1_gain_db = NULL;
- s8 *lna1_gain_db_2 = NULL;
- s8 *lna2_gain_db = NULL;
- s8 tiaA_gain_db_rev7[] = { -9, -6, -3, 0, 3, 3, 3, 3, 3, 3 };
- s8 *tia_gain_db;
- s8 tiaA_gainbits_rev7[] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4 };
- s8 *tia_gainbits;
- u16 rfseqA_init_gain_rev7[] = { 0x624f, 0x624f };
- u16 *rfseq_init_gain;
- u16 init_gaincode;
- u16 clip1hi_gaincode;
- u16 clip1md_gaincode = 0;
- u16 clip1md_gaincode_B;
- u16 clip1lo_gaincode;
- u16 clip1lo_gaincode_B;
- u8 crsminl_th = 0;
- u8 crsminu_th;
- u16 nbclip_th = 0;
- u8 w1clip_th;
- u16 freq;
- s8 nvar_baseline_offset0 = 0, nvar_baseline_offset1 = 0;
- u8 chg_nbclip_th = 0;
+ if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) {
+ wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK);
+ (void)R_REG(&pi->regs->maccontrol);
+ udelay(1);
+ }
- mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13));
- mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13));
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ mod_phy_reg(pi, 0x1e7, (0x7f << 0),
+ (NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 << 0));
+ else
+ mod_phy_reg(pi, 0x1e7, (0x7f << 0),
+ (NPHY_TxPwrCtrlCmd_pwrIndex_init << 0));
- currband = read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand;
- if (currband == 0) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ mod_phy_reg(pi, 0x222, (0xff << 0),
+ (NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 << 0));
+ else if (NREV_GT(pi->pubpi.phy_rev, 1))
+ mod_phy_reg(pi, 0x222, (0xff << 0),
+ (NPHY_TxPwrCtrlCmd_pwrIndex_init << 0));
- lna1_gain_db = lna1G_gain_db_rev7;
+ if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12))
+ wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8,
- lna1_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8,
- lna1_gain_db);
+ write_phy_reg(pi, 0x1e8, (0x3 << 8) | (240 << 0));
- mod_phy_reg(pi, 0x283, (0xff << 0), (0x40 << 0));
+ write_phy_reg(pi, 0x1e9,
+ (1 << 15) | (idle_tssi[0] << 0) | (idle_tssi[1] << 8));
- if (CHSPEC_IS40(pi->radio_chanspec)) {
- mod_phy_reg(pi, 0x280, (0xff << 0), (0x3e << 0));
- mod_phy_reg(pi, 0x283, (0xff << 0), (0x3e << 0));
+ write_phy_reg(pi, 0x1ea,
+ (target_pwr_qtrdbm[0] << 0) |
+ (target_pwr_qtrdbm[1] << 8));
+
+ tbl_len = 64;
+ tbl_offset = 0;
+ for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL;
+ tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) {
+
+ for (idx = 0; idx < tbl_len; idx++) {
+ num = 8 *
+ (16 * b0[tbl_id - 26] + b1[tbl_id - 26] * idx);
+ den = 32768 + a1[tbl_id - 26] * idx;
+ pwr_est = max(((4 * num + den / 2) / den), -8);
+ if (NREV_LT(pi->pubpi.phy_rev, 3)) {
+ if (idx <=
+ (uint) (31 - idle_tssi[tbl_id - 26] + 1))
+ pwr_est =
+ max(pwr_est,
+ target_pwr_qtrdbm
+ [tbl_id - 26] + 1);
+ }
+ regval[idx] = (u32) pwr_est;
}
+ wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32,
+ regval);
+ }
- mod_phy_reg(pi, 0x289, (0xff << 0), (0x46 << 0));
+ wlc_phy_txpwr_limit_to_tbl_nphy(pi);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 84, 64, 8,
+ pi->adj_pwr_tbl_nphy);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 84, 64, 8,
+ pi->adj_pwr_tbl_nphy);
- if (CHSPEC_IS20(pi->radio_chanspec)) {
- mod_phy_reg(pi, 0x300, (0x3f << 0), (13 << 0));
- mod_phy_reg(pi, 0x301, (0x3f << 0), (13 << 0));
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, false);
+}
+
+static u32 *wlc_phy_get_ipa_gaintbl_nphy(struct brcms_phy *pi)
+{
+ u32 *tx_pwrctrl_tbl = NULL;
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if ((pi->pubpi.radiorev == 4)
+ || (pi->pubpi.radiorev == 6))
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_ipa_2g_2057rev4n6;
+ else if (pi->pubpi.radiorev == 3)
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_ipa_2g_2057rev3;
+ else if (pi->pubpi.radiorev == 5)
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_ipa_2g_2057rev5;
+ else if ((pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev == 8))
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_ipa_2g_2057rev7;
+ } else if (NREV_IS(pi->pubpi.phy_rev, 6)) {
+ tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev6;
+ } else if (NREV_IS(pi->pubpi.phy_rev, 5)) {
+ tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev5;
+ } else {
+ tx_pwrctrl_tbl = nphy_tpc_txgain_ipa;
}
} else {
- init_gaincode = 0x9e;
- clip1hi_gaincode = 0x9e;
- clip1md_gaincode_B = 0x24;
- clip1lo_gaincode = 0x8a;
- clip1lo_gaincode_B = 8;
- rfseq_init_gain = rfseqA_init_gain_rev7;
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if ((pi->pubpi.radiorev == 3) ||
+ (pi->pubpi.radiorev == 4) ||
+ (pi->pubpi.radiorev == 6))
+ tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g_2057;
+ else if ((pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev == 8))
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_ipa_5g_2057rev7;
+ } else {
+ tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g;
+ }
+ }
- tia_gain_db = tiaA_gain_db_rev7;
- tia_gainbits = tiaA_gainbits_rev7;
+ return tx_pwrctrl_tbl;
+}
- freq = CHAN5G_FREQ(CHSPEC_CHANNEL(pi->radio_chanspec));
- if (CHSPEC_IS20(pi->radio_chanspec)) {
+static void wlc_phy_restore_rssical_nphy(struct brcms_phy *pi)
+{
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if (pi->nphy_rssical_chanspec_2G == 0)
+ return;
- w1clip_th = 25;
- clip1md_gaincode = 0x82;
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0,
+ RADIO_2057_VCM_MASK,
+ pi->rssical_cache.
+ rssical_radio_regs_2G[0]);
+ mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1,
+ RADIO_2057_VCM_MASK,
+ pi->rssical_cache.
+ rssical_radio_regs_2G[1]);
+ } else {
+ mod_radio_reg(pi,
+ RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0,
+ RADIO_2056_VCM_MASK,
+ pi->rssical_cache.
+ rssical_radio_regs_2G[0]);
+ mod_radio_reg(pi,
+ RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1,
+ RADIO_2056_VCM_MASK,
+ pi->rssical_cache.
+ rssical_radio_regs_2G[1]);
+ }
- if ((freq <= 5080) || (freq == 5825)) {
+ write_phy_reg(pi, 0x1a6,
+ pi->rssical_cache.rssical_phyregs_2G[0]);
+ write_phy_reg(pi, 0x1ac,
+ pi->rssical_cache.rssical_phyregs_2G[1]);
+ write_phy_reg(pi, 0x1b2,
+ pi->rssical_cache.rssical_phyregs_2G[2]);
+ write_phy_reg(pi, 0x1b8,
+ pi->rssical_cache.rssical_phyregs_2G[3]);
+ write_phy_reg(pi, 0x1a4,
+ pi->rssical_cache.rssical_phyregs_2G[4]);
+ write_phy_reg(pi, 0x1aa,
+ pi->rssical_cache.rssical_phyregs_2G[5]);
+ write_phy_reg(pi, 0x1b0,
+ pi->rssical_cache.rssical_phyregs_2G[6]);
+ write_phy_reg(pi, 0x1b6,
+ pi->rssical_cache.rssical_phyregs_2G[7]);
+ write_phy_reg(pi, 0x1a5,
+ pi->rssical_cache.rssical_phyregs_2G[8]);
+ write_phy_reg(pi, 0x1ab,
+ pi->rssical_cache.rssical_phyregs_2G[9]);
+ write_phy_reg(pi, 0x1b1,
+ pi->rssical_cache.rssical_phyregs_2G[10]);
+ write_phy_reg(pi, 0x1b7,
+ pi->rssical_cache.rssical_phyregs_2G[11]);
- s8 lna1A_gain_db_rev7[] = { 11, 16, 20, 24 };
- s8 lna1A_gain_db_2_rev7[] = {
- 11, 17, 22, 25 };
- s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 };
+ } else {
+ if (pi->nphy_rssical_chanspec_5G == 0)
+ return;
- crsminu_th = 0x3e;
- lna1_gain_db = lna1A_gain_db_rev7;
- lna1_gain_db_2 = lna1A_gain_db_2_rev7;
- lna2_gain_db = lna2A_gain_db_rev7;
- } else if ((freq >= 5500) && (freq <= 5700)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0,
+ RADIO_2057_VCM_MASK,
+ pi->rssical_cache.
+ rssical_radio_regs_5G[0]);
+ mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1,
+ RADIO_2057_VCM_MASK,
+ pi->rssical_cache.
+ rssical_radio_regs_5G[1]);
+ } else {
+ mod_radio_reg(pi,
+ RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0,
+ RADIO_2056_VCM_MASK,
+ pi->rssical_cache.
+ rssical_radio_regs_5G[0]);
+ mod_radio_reg(pi,
+ RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1,
+ RADIO_2056_VCM_MASK,
+ pi->rssical_cache.
+ rssical_radio_regs_5G[1]);
+ }
- s8 lna1A_gain_db_rev7[] = { 11, 17, 21, 25 };
- s8 lna1A_gain_db_2_rev7[] = {
- 12, 18, 22, 26 };
- s8 lna2A_gain_db_rev7[] = { 1, 8, 12, 16 };
+ write_phy_reg(pi, 0x1a6,
+ pi->rssical_cache.rssical_phyregs_5G[0]);
+ write_phy_reg(pi, 0x1ac,
+ pi->rssical_cache.rssical_phyregs_5G[1]);
+ write_phy_reg(pi, 0x1b2,
+ pi->rssical_cache.rssical_phyregs_5G[2]);
+ write_phy_reg(pi, 0x1b8,
+ pi->rssical_cache.rssical_phyregs_5G[3]);
+ write_phy_reg(pi, 0x1a4,
+ pi->rssical_cache.rssical_phyregs_5G[4]);
+ write_phy_reg(pi, 0x1aa,
+ pi->rssical_cache.rssical_phyregs_5G[5]);
+ write_phy_reg(pi, 0x1b0,
+ pi->rssical_cache.rssical_phyregs_5G[6]);
+ write_phy_reg(pi, 0x1b6,
+ pi->rssical_cache.rssical_phyregs_5G[7]);
+ write_phy_reg(pi, 0x1a5,
+ pi->rssical_cache.rssical_phyregs_5G[8]);
+ write_phy_reg(pi, 0x1ab,
+ pi->rssical_cache.rssical_phyregs_5G[9]);
+ write_phy_reg(pi, 0x1b1,
+ pi->rssical_cache.rssical_phyregs_5G[10]);
+ write_phy_reg(pi, 0x1b7,
+ pi->rssical_cache.rssical_phyregs_5G[11]);
+ }
+}
- crsminu_th = 0x45;
- clip1md_gaincode_B = 0x14;
- nbclip_th = 0xff;
- chg_nbclip_th = 1;
- lna1_gain_db = lna1A_gain_db_rev7;
- lna1_gain_db_2 = lna1A_gain_db_2_rev7;
- lna2_gain_db = lna2A_gain_db_rev7;
+static void wlc_phy_internal_cal_txgain_nphy(struct brcms_phy *pi)
+{
+ u16 txcal_gain[2];
+
+ pi->nphy_txcal_pwr_idx[0] = pi->nphy_cal_orig_pwr_idx[0];
+ pi->nphy_txcal_pwr_idx[1] = pi->nphy_cal_orig_pwr_idx[0];
+ wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_cal_orig_pwr_idx[0], true);
+ wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_cal_orig_pwr_idx[1], true);
+
+ wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16,
+ txcal_gain);
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ txcal_gain[0] = (txcal_gain[0] & 0xF000) | 0x0F40;
+ txcal_gain[1] = (txcal_gain[1] & 0xF000) | 0x0F40;
+ } else {
+ txcal_gain[0] = (txcal_gain[0] & 0xF000) | 0x0F60;
+ txcal_gain[1] = (txcal_gain[1] & 0xF000) | 0x0F60;
+ }
+
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16,
+ txcal_gain);
+}
+
+static void wlc_phy_precal_txgain_nphy(struct brcms_phy *pi)
+{
+ bool save_bbmult = false;
+ u8 txcal_index_2057_rev5n7 = 0;
+ u8 txcal_index_2057_rev3n4n6 = 10;
+
+ if (pi->use_int_tx_iqlo_cal_nphy) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if ((pi->pubpi.radiorev == 3) ||
+ (pi->pubpi.radiorev == 4) ||
+ (pi->pubpi.radiorev == 6)) {
+
+ pi->nphy_txcal_pwr_idx[0] =
+ txcal_index_2057_rev3n4n6;
+ pi->nphy_txcal_pwr_idx[1] =
+ txcal_index_2057_rev3n4n6;
+ wlc_phy_txpwr_index_nphy(
+ pi, 3,
+ txcal_index_2057_rev3n4n6,
+ false);
} else {
- s8 lna1A_gain_db_rev7[] = { 12, 18, 22, 26 };
- s8 lna1A_gain_db_2_rev7[] = {
- 12, 18, 22, 26 };
- s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 };
+ pi->nphy_txcal_pwr_idx[0] =
+ txcal_index_2057_rev5n7;
+ pi->nphy_txcal_pwr_idx[1] =
+ txcal_index_2057_rev5n7;
+ wlc_phy_txpwr_index_nphy(
+ pi, 3,
+ txcal_index_2057_rev5n7,
+ false);
+ }
+ save_bbmult = true;
- crsminu_th = 0x41;
- lna1_gain_db = lna1A_gain_db_rev7;
- lna1_gain_db_2 = lna1A_gain_db_2_rev7;
- lna2_gain_db = lna2A_gain_db_rev7;
+ } else if (NREV_LT(pi->pubpi.phy_rev, 5)) {
+ wlc_phy_cal_txgainctrl_nphy(pi, 11, false);
+ if (pi->sh->hw_phytxchain != 3) {
+ pi->nphy_txcal_pwr_idx[1] =
+ pi->nphy_txcal_pwr_idx[0];
+ wlc_phy_txpwr_index_nphy(pi, 3,
+ pi->
+ nphy_txcal_pwr_idx[0],
+ true);
+ save_bbmult = true;
}
- if (freq <= 4920) {
- nvar_baseline_offset0 = 5;
- nvar_baseline_offset1 = 5;
- } else if ((freq > 4920) && (freq <= 5320)) {
- nvar_baseline_offset0 = 3;
- nvar_baseline_offset1 = 5;
- } else if ((freq > 5320) && (freq <= 5700)) {
- nvar_baseline_offset0 = 3;
- nvar_baseline_offset1 = 2;
+ } else if (NREV_IS(pi->pubpi.phy_rev, 5)) {
+ if (PHY_IPA(pi)) {
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ wlc_phy_cal_txgainctrl_nphy(pi, 12,
+ false);
+ } else {
+ pi->nphy_txcal_pwr_idx[0] = 80;
+ pi->nphy_txcal_pwr_idx[1] = 80;
+ wlc_phy_txpwr_index_nphy(pi, 3, 80,
+ false);
+ save_bbmult = true;
+ }
} else {
- nvar_baseline_offset0 = 4;
- nvar_baseline_offset1 = 0;
+ wlc_phy_internal_cal_txgain_nphy(pi);
+ save_bbmult = true;
}
- } else {
- crsminu_th = 0x3a;
- crsminl_th = 0x3a;
- w1clip_th = 20;
-
- if ((freq >= 4920) && (freq <= 5320)) {
- nvar_baseline_offset0 = 4;
- nvar_baseline_offset1 = 5;
- } else if ((freq > 5320) && (freq <= 5550)) {
- nvar_baseline_offset0 = 4;
- nvar_baseline_offset1 = 2;
+ } else if (NREV_IS(pi->pubpi.phy_rev, 6)) {
+ if (PHY_IPA(pi)) {
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ wlc_phy_cal_txgainctrl_nphy(pi, 12,
+ false);
+ else
+ wlc_phy_cal_txgainctrl_nphy(pi, 14,
+ false);
} else {
- nvar_baseline_offset0 = 5;
- nvar_baseline_offset1 = 3;
+ wlc_phy_internal_cal_txgain_nphy(pi);
+ save_bbmult = true;
}
}
- write_phy_reg(pi, 0x20, init_gaincode);
- write_phy_reg(pi, 0x2a7, init_gaincode);
+ } else {
+ wlc_phy_cal_txgainctrl_nphy(pi, 10, false);
+ }
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
- pi->pubpi.phy_corenum, 0x106, 16,
- rfseq_init_gain);
+ if (save_bbmult)
+ wlc_phy_table_read_nphy(pi, 15, 1, 87, 16,
+ &pi->nphy_txcal_bbmult);
+}
- write_phy_reg(pi, 0x22, clip1hi_gaincode);
- write_phy_reg(pi, 0x2a9, clip1hi_gaincode);
+static void
+wlc_phy_rfctrlintc_override_nphy(struct brcms_phy *pi, u8 field, u16 value,
+ u8 core_code)
+{
+ u16 mask;
+ u16 val;
+ u8 core;
- write_phy_reg(pi, 0x36, clip1md_gaincode_B);
- write_phy_reg(pi, 0x2ac, clip1md_gaincode_B);
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ for (core = 0; core < pi->pubpi.phy_corenum; core++) {
+ if (core_code == RADIO_MIMO_CORESEL_CORE1
+ && core == PHY_CORE_1)
+ continue;
+ else if (core_code == RADIO_MIMO_CORESEL_CORE2
+ && core == PHY_CORE_0)
+ continue;
- write_phy_reg(pi, 0x37, clip1lo_gaincode);
- write_phy_reg(pi, 0x2ad, clip1lo_gaincode);
- write_phy_reg(pi, 0x38, clip1lo_gaincode_B);
- write_phy_reg(pi, 0x2ae, clip1lo_gaincode_B);
+ if (NREV_LT(pi->pubpi.phy_rev, 7)) {
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8,
- tia_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8,
- tia_gain_db);
+ mask = (0x1 << 10);
+ val = 1 << 10;
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 :
+ 0x92, mask, val);
+ }
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8,
- tia_gainbits);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8,
- tia_gainbits);
+ if (field == NPHY_RfctrlIntc_override_OFF) {
- mod_phy_reg(pi, 0x283, (0xff << 0), (crsminu_th << 0));
+ write_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 :
+ 0x92, 0);
- if (chg_nbclip_th == 1) {
- write_phy_reg(pi, 0x2b, nbclip_th);
- write_phy_reg(pi, 0x41, nbclip_th);
- }
+ wlc_phy_force_rfseq_nphy(pi,
+ NPHY_RFSEQ_RESET2RX);
+ } else if (field == NPHY_RfctrlIntc_override_TRSW) {
- mod_phy_reg(pi, 0x300, (0x3f << 0), (w1clip_th << 0));
- mod_phy_reg(pi, 0x301, (0x3f << 0), (w1clip_th << 0));
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- mod_phy_reg(pi, 0x2e4,
- (0x3f << 0), (nvar_baseline_offset0 << 0));
+ mask = (0x1 << 6) | (0x1 << 7);
- mod_phy_reg(pi, 0x2e4,
- (0x3f << 6), (nvar_baseline_offset1 << 6));
+ val = value << 6;
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91 : 0x92,
+ mask, val);
- if (CHSPEC_IS20(pi->radio_chanspec)) {
+ or_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91 : 0x92,
+ (0x1 << 10));
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8,
- lna1_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8,
- lna1_gain_db_2);
+ and_phy_reg(pi, 0x2ff, (u16)
+ ~(0x3 << 14));
+ or_phy_reg(pi, 0x2ff, (0x1 << 13));
+ or_phy_reg(pi, 0x2ff, (0x1 << 0));
+ } else {
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10,
- 8, lna2_gain_db);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10,
- 8, lna2_gain_db);
+ mask = (0x1 << 6) |
+ (0x1 << 7) |
+ (0x1 << 8) | (0x1 << 9);
+ val = value << 6;
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91 : 0x92,
+ mask, val);
- write_phy_reg(pi, 0x24, clip1md_gaincode);
- write_phy_reg(pi, 0x2ab, clip1md_gaincode);
- } else {
- mod_phy_reg(pi, 0x280, (0xff << 0), (crsminl_th << 0));
- }
+ mask = (0x1 << 0);
+ val = 1 << 0;
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0xe7 : 0xec,
+ mask, val);
- }
+ mask = (core == PHY_CORE_0) ?
+ (0x1 << 0) : (0x1 << 1);
+ val = 1 << ((core == PHY_CORE_0) ?
+ 0 : 1);
+ mod_phy_reg(pi, 0x78, mask, val);
+ SPINWAIT(((read_phy_reg(pi, 0x78) & val)
+ != 0), 10000);
+ if (WARN(read_phy_reg(pi, 0x78) & val,
+ "HW error: override failed"))
+ return;
+
+ mask = (0x1 << 0);
+ val = 0 << 0;
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0xe7 : 0xec,
+ mask, val);
+ }
+ } else if (field == NPHY_RfctrlIntc_override_PA) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+
+ mask = (0x1 << 4) | (0x1 << 5);
+
+ if (CHSPEC_IS5G(pi->radio_chanspec))
+ val = value << 5;
+ else
+ val = value << 4;
+
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91 : 0x92,
+ mask, val);
+
+ or_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91 : 0x92,
+ (0x1 << 12));
+ } else {
+
+ if (CHSPEC_IS5G(pi->radio_chanspec)) {
+ mask = (0x1 << 5);
+ val = value << 5;
+ } else {
+ mask = (0x1 << 4);
+ val = value << 4;
+ }
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91 : 0x92,
+ mask, val);
+ }
+ } else if (field ==
+ NPHY_RfctrlIntc_override_EXT_LNA_PU) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (CHSPEC_IS5G(pi->radio_chanspec)) {
+
+ mask = (0x1 << 0);
+ val = value << 0;
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91
+ : 0x92, mask, val);
+
+ mask = (0x1 << 2);
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91
+ : 0x92, mask, 0);
+ } else {
+
+ mask = (0x1 << 2);
+ val = value << 2;
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91
+ : 0x92, mask, val);
+
+ mask = (0x1 << 0);
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91
+ : 0x92, mask, 0);
+ }
+
+ mask = (0x1 << 11);
+ val = 1 << 11;
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91 : 0x92,
+ mask, val);
+ } else {
+
+ if (CHSPEC_IS5G(pi->radio_chanspec)) {
+ mask = (0x1 << 0);
+ val = value << 0;
+ } else {
+ mask = (0x1 << 2);
+ val = value << 2;
+ }
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91 : 0x92,
+ mask, val);
+ }
+ } else if (field ==
+ NPHY_RfctrlIntc_override_EXT_LNA_GAIN) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (CHSPEC_IS5G(pi->radio_chanspec)) {
+
+ mask = (0x1 << 1);
+ val = value << 1;
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91
+ : 0x92, mask, val);
+
+ mask = (0x1 << 3);
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91
+ : 0x92, mask, 0);
+ } else {
+
+ mask = (0x1 << 3);
+ val = value << 3;
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91
+ : 0x92, mask, val);
+
+ mask = (0x1 << 1);
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91
+ : 0x92, mask, 0);
+ }
+
+ mask = (0x1 << 11);
+ val = 1 << 11;
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91 : 0x92,
+ mask, val);
+ } else {
+
+ if (CHSPEC_IS5G(pi->radio_chanspec)) {
+ mask = (0x1 << 1);
+ val = value << 1;
+ } else {
+ mask = (0x1 << 3);
+ val = value << 3;
+ }
+ mod_phy_reg(pi,
+ (core ==
+ PHY_CORE_0) ? 0x91 : 0x92,
+ mask, val);
+ }
+ }
+ }
+ }
}
-static void wlc_phy_adjust_lnagaintbl_nphy(phy_info_t *pi)
+void
+wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, s32 dBm_targetpower,
+ bool debug)
{
+ int gainctrl_loopidx;
uint core;
- int ctr;
- s16 gain_delta[2];
- u8 curr_channel;
- u16 minmax_gain[2];
- u16 regval[4];
+ u16 m0m1, curr_m0m1;
+ s32 delta_power;
+ s32 txpwrindex;
+ s32 qdBm_power[2];
+ u16 orig_BBConfig;
+ u16 phy_saveregs[4];
+ u32 freq_test;
+ u16 ampl_test = 250;
+ uint stepsize;
+ bool phyhang_avoid_state = false;
+
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ stepsize = 2;
+ else
+ stepsize = 1;
+
+ if (CHSPEC_IS40(pi->radio_chanspec))
+ freq_test = 5000;
+ else
+ freq_test = 2500;
+
+ wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_cal_orig_pwr_idx[0], true);
+ wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_cal_orig_pwr_idx[1], true);
if (pi->phyhang_avoid)
wlc_phy_stay_in_carriersearch_nphy(pi, true);
- if (pi->nphy_gain_boost) {
- if ((CHSPEC_IS2G(pi->radio_chanspec))) {
+ phyhang_avoid_state = pi->phyhang_avoid;
+ pi->phyhang_avoid = false;
- gain_delta[0] = 6;
- gain_delta[1] = 6;
- } else {
+ phy_saveregs[0] = read_phy_reg(pi, 0x91);
+ phy_saveregs[1] = read_phy_reg(pi, 0x92);
+ phy_saveregs[2] = read_phy_reg(pi, 0xe7);
+ phy_saveregs[3] = read_phy_reg(pi, 0xec);
+ wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_PA, 1,
+ RADIO_MIMO_CORESEL_CORE1 |
+ RADIO_MIMO_CORESEL_CORE2);
- curr_channel = CHSPEC_CHANNEL(pi->radio_chanspec);
- gain_delta[0] =
- (s16)
- PHY_HW_ROUND(((nphy_lnagain_est0[0] *
- curr_channel) +
- nphy_lnagain_est0[1]), 13);
- gain_delta[1] =
- (s16)
- PHY_HW_ROUND(((nphy_lnagain_est1[0] *
- curr_channel) +
- nphy_lnagain_est1[1]), 13);
- }
+ if (!debug) {
+ wlc_phy_rfctrlintc_override_nphy(pi,
+ NPHY_RfctrlIntc_override_TRSW,
+ 0x2, RADIO_MIMO_CORESEL_CORE1);
+ wlc_phy_rfctrlintc_override_nphy(pi,
+ NPHY_RfctrlIntc_override_TRSW,
+ 0x8, RADIO_MIMO_CORESEL_CORE2);
} else {
-
- gain_delta[0] = 0;
- gain_delta[1] = 0;
+ wlc_phy_rfctrlintc_override_nphy(pi,
+ NPHY_RfctrlIntc_override_TRSW,
+ 0x1, RADIO_MIMO_CORESEL_CORE1);
+ wlc_phy_rfctrlintc_override_nphy(pi,
+ NPHY_RfctrlIntc_override_TRSW,
+ 0x7, RADIO_MIMO_CORESEL_CORE2);
}
+ orig_BBConfig = read_phy_reg(pi, 0x01);
+ mod_phy_reg(pi, 0x01, (0x1 << 15), 0);
+
+ wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m0m1);
+
for (core = 0; core < pi->pubpi.phy_corenum; core++) {
- if (pi->nphy_elna_gain_config) {
+ txpwrindex = (s32) pi->nphy_cal_orig_pwr_idx[core];
- regval[0] = nphy_def_lnagains[2] + gain_delta[core];
- regval[1] = nphy_def_lnagains[3] + gain_delta[core];
- regval[2] = nphy_def_lnagains[3] + gain_delta[core];
- regval[3] = nphy_def_lnagains[3] + gain_delta[core];
- } else {
- for (ctr = 0; ctr < 4; ctr++) {
- regval[ctr] =
- nphy_def_lnagains[ctr] + gain_delta[core];
+ for (gainctrl_loopidx = 0; gainctrl_loopidx < 2;
+ gainctrl_loopidx++) {
+ wlc_phy_tx_tone_nphy(pi, freq_test, ampl_test, 0, 0,
+ false);
+
+ if (core == PHY_CORE_0)
+ curr_m0m1 = m0m1 & 0xff00;
+ else
+ curr_m0m1 = m0m1 & 0x00ff;
+
+ wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &curr_m0m1);
+ wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &curr_m0m1);
+
+ udelay(50);
+
+ wlc_phy_est_tonepwr_nphy(pi, qdBm_power,
+ NPHY_CAL_TSSISAMPS);
+
+ pi->nphy_bb_mult_save = 0;
+ wlc_phy_stopplayback_nphy(pi);
+
+ delta_power = (dBm_targetpower * 4) - qdBm_power[core];
+
+ txpwrindex -= stepsize * delta_power;
+ if (txpwrindex < 0)
+ txpwrindex = 0;
+ else if (txpwrindex > 127)
+ txpwrindex = 127;
+
+ if (CHSPEC_IS5G(pi->radio_chanspec)) {
+ if (NREV_IS(pi->pubpi.phy_rev, 4) &&
+ (pi->srom_fem5g.extpagain == 3)) {
+ if (txpwrindex < 30)
+ txpwrindex = 30;
+ }
+ } else {
+ if (NREV_GE(pi->pubpi.phy_rev, 5) &&
+ (pi->srom_fem2g.extpagain == 3)) {
+ if (txpwrindex < 50)
+ txpwrindex = 50;
+ }
}
+
+ wlc_phy_txpwr_index_nphy(pi, (1 << core),
+ (u8) txpwrindex, true);
}
- wlc_phy_table_write_nphy(pi, core, 4, 8, 16, regval);
- minmax_gain[core] =
- (u16) (nphy_def_lnagains[2] + gain_delta[core] + 4);
+ pi->nphy_txcal_pwr_idx[core] = (u8) txpwrindex;
+
+ if (debug) {
+ u16 radio_gain;
+ u16 dbg_m0m1;
+
+ wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &dbg_m0m1);
+
+ wlc_phy_tx_tone_nphy(pi, freq_test, ampl_test, 0, 0,
+ false);
+
+ wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &dbg_m0m1);
+ wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &dbg_m0m1);
+
+ udelay(100);
+
+ wlc_phy_est_tonepwr_nphy(pi, qdBm_power,
+ NPHY_CAL_TSSISAMPS);
+
+ wlc_phy_table_read_nphy(pi, 7, 1, (0x110 + core), 16,
+ &radio_gain);
+
+ mdelay(4000);
+ pi->nphy_bb_mult_save = 0;
+ wlc_phy_stopplayback_nphy(pi);
+ }
}
- mod_phy_reg(pi, 0x1e, (0xff << 0), (minmax_gain[0] << 0));
- mod_phy_reg(pi, 0x34, (0xff << 0), (minmax_gain[1] << 0));
+ wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_txcal_pwr_idx[0], true);
+ wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_txcal_pwr_idx[1], true);
+
+ wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &pi->nphy_txcal_bbmult);
+
+ write_phy_reg(pi, 0x01, orig_BBConfig);
+
+ write_phy_reg(pi, 0x91, phy_saveregs[0]);
+ write_phy_reg(pi, 0x92, phy_saveregs[1]);
+ write_phy_reg(pi, 0xe7, phy_saveregs[2]);
+ write_phy_reg(pi, 0xec, phy_saveregs[3]);
+
+ pi->phyhang_avoid = phyhang_avoid_state;
if (pi->phyhang_avoid)
wlc_phy_stay_in_carriersearch_nphy(pi, false);
}
-void wlc_phy_switch_radio_nphy(phy_info_t *pi, bool on)
+static void wlc_phy_savecal_nphy(struct brcms_phy *pi)
{
- if (on) {
+ void *tbl_ptr;
+ int coreNum;
+ u16 *txcal_radio_regs = NULL;
+
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, true);
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+
+ wlc_phy_rx_iq_coeffs_nphy(pi, 0,
+ &pi->calibration_cache.
+ rxcal_coeffs_2G);
+
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- if (!pi->radio_is_on) {
- wlc_phy_radio_preinit_205x(pi);
- wlc_phy_radio_init_2057(pi);
- wlc_phy_radio_postinit_2057(pi);
- }
+ txcal_radio_regs =
+ pi->calibration_cache.txcal_radio_regs_2G;
+ } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- wlc_phy_chanspec_set((wlc_phy_t *) pi,
- pi->radio_chanspec);
+ pi->calibration_cache.txcal_radio_regs_2G[0] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_FINE_I |
+ RADIO_2056_TX0);
+ pi->calibration_cache.txcal_radio_regs_2G[1] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_FINE_Q |
+ RADIO_2056_TX0);
+ pi->calibration_cache.txcal_radio_regs_2G[2] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_FINE_I |
+ RADIO_2056_TX1);
+ pi->calibration_cache.txcal_radio_regs_2G[3] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_FINE_Q |
+ RADIO_2056_TX1);
+
+ pi->calibration_cache.txcal_radio_regs_2G[4] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_I |
+ RADIO_2056_TX0);
+ pi->calibration_cache.txcal_radio_regs_2G[5] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_Q |
+ RADIO_2056_TX0);
+ pi->calibration_cache.txcal_radio_regs_2G[6] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_I |
+ RADIO_2056_TX1);
+ pi->calibration_cache.txcal_radio_regs_2G[7] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_Q |
+ RADIO_2056_TX1);
+ } else {
+ pi->calibration_cache.txcal_radio_regs_2G[0] =
+ read_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL);
+ pi->calibration_cache.txcal_radio_regs_2G[1] =
+ read_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL);
+ pi->calibration_cache.txcal_radio_regs_2G[2] =
+ read_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM);
+ pi->calibration_cache.txcal_radio_regs_2G[3] =
+ read_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM);
+ }
+
+ pi->nphy_iqcal_chanspec_2G = pi->radio_chanspec;
+ tbl_ptr = pi->calibration_cache.txcal_coeffs_2G;
+ } else {
+
+ wlc_phy_rx_iq_coeffs_nphy(pi, 0,
+ &pi->calibration_cache.
+ rxcal_coeffs_5G);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ txcal_radio_regs =
+ pi->calibration_cache.txcal_radio_regs_5G;
} else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- wlc_phy_radio_preinit_205x(pi);
- wlc_phy_radio_init_2056(pi);
- wlc_phy_radio_postinit_2056(pi);
- wlc_phy_chanspec_set((wlc_phy_t *) pi,
- pi->radio_chanspec);
+ pi->calibration_cache.txcal_radio_regs_5G[0] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_FINE_I |
+ RADIO_2056_TX0);
+ pi->calibration_cache.txcal_radio_regs_5G[1] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_FINE_Q |
+ RADIO_2056_TX0);
+ pi->calibration_cache.txcal_radio_regs_5G[2] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_FINE_I |
+ RADIO_2056_TX1);
+ pi->calibration_cache.txcal_radio_regs_5G[3] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_FINE_Q |
+ RADIO_2056_TX1);
+
+ pi->calibration_cache.txcal_radio_regs_5G[4] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_I |
+ RADIO_2056_TX0);
+ pi->calibration_cache.txcal_radio_regs_5G[5] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_Q |
+ RADIO_2056_TX0);
+ pi->calibration_cache.txcal_radio_regs_5G[6] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_I |
+ RADIO_2056_TX1);
+ pi->calibration_cache.txcal_radio_regs_5G[7] =
+ read_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_Q |
+ RADIO_2056_TX1);
} else {
- wlc_phy_radio_preinit_2055(pi);
- wlc_phy_radio_init_2055(pi);
- wlc_phy_radio_postinit_2055(pi);
+ pi->calibration_cache.txcal_radio_regs_5G[0] =
+ read_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL);
+ pi->calibration_cache.txcal_radio_regs_5G[1] =
+ read_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL);
+ pi->calibration_cache.txcal_radio_regs_5G[2] =
+ read_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM);
+ pi->calibration_cache.txcal_radio_regs_5G[3] =
+ read_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM);
}
- pi->radio_is_on = true;
+ pi->nphy_iqcal_chanspec_5G = pi->radio_chanspec;
+ tbl_ptr = pi->calibration_cache.txcal_coeffs_5G;
+ }
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ for (coreNum = 0; coreNum <= 1; coreNum++) {
+
+ txcal_radio_regs[2 * coreNum] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
+ LOFT_FINE_I);
+ txcal_radio_regs[2 * coreNum + 1] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
+ LOFT_FINE_Q);
+
+ txcal_radio_regs[2 * coreNum + 4] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
+ LOFT_COARSE_I);
+ txcal_radio_regs[2 * coreNum + 5] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
+ LOFT_COARSE_Q);
+ }
+ }
+
+ wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 8, 80, 16, tbl_ptr);
+
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, false);
+}
+
+static void wlc_phy_tx_iq_war_nphy(struct brcms_phy *pi)
+{
+ struct nphy_iq_comp tx_comp;
+
+ wlc_phy_table_read_nphy(pi, 15, 4, 0x50, 16, &tx_comp);
+
+ wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ, tx_comp.a0);
+ wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 2, tx_comp.b0);
+ wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 4, tx_comp.a1);
+ wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 6, tx_comp.b1);
+}
+
+static void wlc_phy_restorecal_nphy(struct brcms_phy *pi)
+{
+ u16 *loft_comp;
+ u16 txcal_coeffs_bphy[4];
+ u16 *tbl_ptr;
+ int coreNum;
+ u16 *txcal_radio_regs = NULL;
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if (pi->nphy_iqcal_chanspec_2G == 0)
+ return;
+ tbl_ptr = pi->calibration_cache.txcal_coeffs_2G;
+ loft_comp = &pi->calibration_cache.txcal_coeffs_2G[5];
} else {
+ if (pi->nphy_iqcal_chanspec_5G == 0)
+ return;
- if (NREV_GE(pi->pubpi.phy_rev, 3)
- && NREV_LT(pi->pubpi.phy_rev, 7)) {
- and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU);
- mod_radio_reg(pi, RADIO_2056_SYN_COM_PU, 0x2, 0x0);
+ tbl_ptr = pi->calibration_cache.txcal_coeffs_5G;
+ loft_comp = &pi->calibration_cache.txcal_coeffs_5G[5];
+ }
+
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, 16, tbl_ptr);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ txcal_coeffs_bphy[0] = tbl_ptr[0];
+ txcal_coeffs_bphy[1] = tbl_ptr[1];
+ txcal_coeffs_bphy[2] = tbl_ptr[2];
+ txcal_coeffs_bphy[3] = tbl_ptr[3];
+ } else {
+ txcal_coeffs_bphy[0] = 0;
+ txcal_coeffs_bphy[1] = 0;
+ txcal_coeffs_bphy[2] = 0;
+ txcal_coeffs_bphy[3] = 0;
+ }
+
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, 16,
+ txcal_coeffs_bphy);
+
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, 16, loft_comp);
+
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, 16, loft_comp);
+
+ if (NREV_LT(pi->pubpi.phy_rev, 2))
+ wlc_phy_tx_iq_war_nphy(pi);
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ txcal_radio_regs =
+ pi->calibration_cache.txcal_radio_regs_2G;
+ } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
write_radio_reg(pi,
- RADIO_2056_TX_PADA_BOOST_TUNE |
- RADIO_2056_TX0, 0);
+ RADIO_2056_TX_LOFT_FINE_I |
+ RADIO_2056_TX0,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[0]);
write_radio_reg(pi,
- RADIO_2056_TX_PADG_BOOST_TUNE |
- RADIO_2056_TX0, 0);
+ RADIO_2056_TX_LOFT_FINE_Q |
+ RADIO_2056_TX0,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[1]);
write_radio_reg(pi,
- RADIO_2056_TX_PGAA_BOOST_TUNE |
- RADIO_2056_TX0, 0);
+ RADIO_2056_TX_LOFT_FINE_I |
+ RADIO_2056_TX1,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[2]);
write_radio_reg(pi,
- RADIO_2056_TX_PGAG_BOOST_TUNE |
- RADIO_2056_TX0, 0);
- mod_radio_reg(pi,
- RADIO_2056_TX_MIXA_BOOST_TUNE |
- RADIO_2056_TX0, 0xf0, 0);
+ RADIO_2056_TX_LOFT_FINE_Q |
+ RADIO_2056_TX1,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[3]);
+
write_radio_reg(pi,
- RADIO_2056_TX_MIXG_BOOST_TUNE |
- RADIO_2056_TX0, 0);
+ RADIO_2056_TX_LOFT_COARSE_I |
+ RADIO_2056_TX0,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[4]);
+ write_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_Q |
+ RADIO_2056_TX0,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[5]);
+ write_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_I |
+ RADIO_2056_TX1,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[6]);
+ write_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_Q |
+ RADIO_2056_TX1,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[7]);
+ } else {
+ write_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[0]);
+ write_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[1]);
+ write_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[2]);
+ write_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM,
+ pi->calibration_cache.
+ txcal_radio_regs_2G[3]);
+ }
+
+ wlc_phy_rx_iq_coeffs_nphy(pi, 1,
+ &pi->calibration_cache.
+ rxcal_coeffs_2G);
+ } else {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ txcal_radio_regs =
+ pi->calibration_cache.txcal_radio_regs_5G;
+ } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
write_radio_reg(pi,
- RADIO_2056_TX_PADA_BOOST_TUNE |
- RADIO_2056_TX1, 0);
+ RADIO_2056_TX_LOFT_FINE_I |
+ RADIO_2056_TX0,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[0]);
write_radio_reg(pi,
- RADIO_2056_TX_PADG_BOOST_TUNE |
- RADIO_2056_TX1, 0);
+ RADIO_2056_TX_LOFT_FINE_Q |
+ RADIO_2056_TX0,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[1]);
write_radio_reg(pi,
- RADIO_2056_TX_PGAA_BOOST_TUNE |
- RADIO_2056_TX1, 0);
+ RADIO_2056_TX_LOFT_FINE_I |
+ RADIO_2056_TX1,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[2]);
write_radio_reg(pi,
- RADIO_2056_TX_PGAG_BOOST_TUNE |
- RADIO_2056_TX1, 0);
- mod_radio_reg(pi,
- RADIO_2056_TX_MIXA_BOOST_TUNE |
- RADIO_2056_TX1, 0xf0, 0);
+ RADIO_2056_TX_LOFT_FINE_Q |
+ RADIO_2056_TX1,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[3]);
+
write_radio_reg(pi,
- RADIO_2056_TX_MIXG_BOOST_TUNE |
- RADIO_2056_TX1, 0);
+ RADIO_2056_TX_LOFT_COARSE_I |
+ RADIO_2056_TX0,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[4]);
+ write_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_Q |
+ RADIO_2056_TX0,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[5]);
+ write_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_I |
+ RADIO_2056_TX1,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[6]);
+ write_radio_reg(pi,
+ RADIO_2056_TX_LOFT_COARSE_Q |
+ RADIO_2056_TX1,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[7]);
+ } else {
+ write_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[0]);
+ write_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[1]);
+ write_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[2]);
+ write_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM,
+ pi->calibration_cache.
+ txcal_radio_regs_5G[3]);
+ }
- pi->radio_is_on = false;
+ wlc_phy_rx_iq_coeffs_nphy(pi, 1,
+ &pi->calibration_cache.
+ rxcal_coeffs_5G);
+ }
+
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ for (coreNum = 0; coreNum <= 1; coreNum++) {
+
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
+ LOFT_FINE_I,
+ txcal_radio_regs[2 * coreNum]);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
+ LOFT_FINE_Q,
+ txcal_radio_regs[2 * coreNum + 1]);
+
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
+ LOFT_COARSE_I,
+ txcal_radio_regs[2 * coreNum + 4]);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
+ LOFT_COARSE_Q,
+ txcal_radio_regs[2 * coreNum + 5]);
}
+ }
+}
- if (NREV_GE(pi->pubpi.phy_rev, 8)) {
- and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU);
- pi->radio_is_on = false;
+static void wlc_phy_txpwrctrl_coeff_setup_nphy(struct brcms_phy *pi)
+{
+ u32 idx;
+ u16 iqloCalbuf[7];
+ u32 iqcomp, locomp, curr_locomp;
+ s8 locomp_i, locomp_q;
+ s8 curr_locomp_i, curr_locomp_q;
+ u32 tbl_id, tbl_len, tbl_offset;
+ u32 regval[128];
+
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, true);
+
+ wlc_phy_table_read_nphy(pi, 15, 7, 80, 16, iqloCalbuf);
+
+ tbl_len = 128;
+ tbl_offset = 320;
+ for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL;
+ tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) {
+ iqcomp =
+ (tbl_id ==
+ 26) ? (((u32) (iqloCalbuf[0] & 0x3ff)) << 10) |
+ (iqloCalbuf[1] & 0x3ff)
+ : (((u32) (iqloCalbuf[2] & 0x3ff)) << 10) |
+ (iqloCalbuf[3] & 0x3ff);
+
+ for (idx = 0; idx < tbl_len; idx++)
+ regval[idx] = iqcomp;
+ wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32,
+ regval);
+ }
+
+ tbl_offset = 448;
+ for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL;
+ tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) {
+
+ locomp =
+ (u32) ((tbl_id == 26) ? iqloCalbuf[5] : iqloCalbuf[6]);
+ locomp_i = (s8) ((locomp >> 8) & 0xff);
+ locomp_q = (s8) ((locomp) & 0xff);
+ for (idx = 0; idx < tbl_len; idx++) {
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ curr_locomp_i = locomp_i;
+ curr_locomp_q = locomp_q;
+ } else {
+ curr_locomp_i = (s8) ((locomp_i *
+ nphy_tpc_loscale[idx] +
+ 128) >> 8);
+ curr_locomp_q =
+ (s8) ((locomp_q *
+ nphy_tpc_loscale[idx] +
+ 128) >> 8);
+ }
+ curr_locomp = (u32) ((curr_locomp_i & 0xff) << 8);
+ curr_locomp |= (u32) (curr_locomp_q & 0xff);
+ regval[idx] = curr_locomp;
}
+ wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32,
+ regval);
+ }
+ if (NREV_LT(pi->pubpi.phy_rev, 2)) {
+
+ wlapi_bmac_write_shm(pi->sh->physhim, M_CURR_IDX1, 0xFFFF);
+ wlapi_bmac_write_shm(pi->sh->physhim, M_CURR_IDX2, 0xFFFF);
}
+
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, false);
}
-static void wlc_phy_radio_preinit_2055(phy_info_t *pi)
+static void wlc_phy_txlpfbw_nphy(struct brcms_phy *pi)
{
+ u8 tx_lpf_bw = 0;
- and_phy_reg(pi, 0x78, ~RFCC_POR_FORCE);
- or_phy_reg(pi, 0x78, RFCC_CHIP0_PU | RFCC_OE_POR_FORCE);
+ if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) {
+ if (CHSPEC_IS40(pi->radio_chanspec))
+ tx_lpf_bw = 3;
+ else
+ tx_lpf_bw = 1;
- or_phy_reg(pi, 0x78, RFCC_POR_FORCE);
+ if (PHY_IPA(pi)) {
+ if (CHSPEC_IS40(pi->radio_chanspec))
+ tx_lpf_bw = 5;
+ else
+ tx_lpf_bw = 4;
+ }
+
+ write_phy_reg(pi, 0xe8,
+ (tx_lpf_bw << 0) |
+ (tx_lpf_bw << 3) |
+ (tx_lpf_bw << 6) | (tx_lpf_bw << 9));
+
+ if (PHY_IPA(pi)) {
+
+ if (CHSPEC_IS40(pi->radio_chanspec))
+ tx_lpf_bw = 4;
+ else
+ tx_lpf_bw = 1;
+
+ write_phy_reg(pi, 0xe9,
+ (tx_lpf_bw << 0) |
+ (tx_lpf_bw << 3) |
+ (tx_lpf_bw << 6) | (tx_lpf_bw << 9));
+ }
+ }
}
-static void wlc_phy_radio_init_2055(phy_info_t *pi)
+static void
+wlc_phy_adjust_rx_analpfbw_nphy(struct brcms_phy *pi, u16 reduction_factr)
{
- wlc_phy_init_radio_regs(pi, regs_2055, RADIO_DEFAULT_CORE);
+ if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) {
+ if ((CHSPEC_CHANNEL(pi->radio_chanspec) == 11) &&
+ CHSPEC_IS40(pi->radio_chanspec)) {
+ if (!pi->nphy_anarxlpf_adjusted) {
+ write_radio_reg(pi,
+ (RADIO_2056_RX_RXLPF_RCCAL_LPC |
+ RADIO_2056_RX0),
+ ((pi->nphy_rccal_value +
+ reduction_factr) | 0x80));
+
+ pi->nphy_anarxlpf_adjusted = true;
+ }
+ } else {
+ if (pi->nphy_anarxlpf_adjusted) {
+ write_radio_reg(pi,
+ (RADIO_2056_RX_RXLPF_RCCAL_LPC |
+ RADIO_2056_RX0),
+ (pi->nphy_rccal_value | 0x80));
+
+ pi->nphy_anarxlpf_adjusted = false;
+ }
+ }
+ }
}
-static void wlc_phy_radio_postinit_2055(phy_info_t *pi)
+static void
+wlc_phy_adjust_min_noisevar_nphy(struct brcms_phy *pi, int ntones,
+ int *tone_id_buf, u32 *noise_var_buf)
{
+ int i;
+ u32 offset;
+ int tone_id;
+ int tbllen =
+ CHSPEC_IS40(pi->radio_chanspec) ?
+ NPHY_NOISEVAR_TBLLEN40 : NPHY_NOISEVAR_TBLLEN20;
- and_radio_reg(pi, RADIO_2055_MASTER_CNTRL1,
- ~(RADIO_2055_JTAGCTRL_MASK | RADIO_2055_JTAGSYNC_MASK));
+ if (pi->nphy_noisevars_adjusted) {
+ for (i = 0; i < pi->nphy_saved_noisevars.bufcount; i++) {
+ tone_id = pi->nphy_saved_noisevars.tone_id[i];
+ offset = (tone_id >= 0) ?
+ ((tone_id *
+ 2) + 1) : (tbllen + (tone_id * 2) + 1);
+ wlc_phy_table_write_nphy(
+ pi, NPHY_TBL_ID_NOISEVAR, 1,
+ offset, 32,
+ &pi->nphy_saved_noisevars.min_noise_vars[i]);
+ }
- if (((pi->sh->sromrev >= 4)
- && !(pi->sh->boardflags2 & BFL2_RXBB_INT_REG_DIS))
- || ((pi->sh->sromrev < 4))) {
- and_radio_reg(pi, RADIO_2055_CORE1_RXBB_REGULATOR, 0x7F);
- and_radio_reg(pi, RADIO_2055_CORE2_RXBB_REGULATOR, 0x7F);
+ pi->nphy_saved_noisevars.bufcount = 0;
+ pi->nphy_noisevars_adjusted = false;
}
- mod_radio_reg(pi, RADIO_2055_RRCCAL_N_OPT_SEL, 0x3F, 0x2C);
- write_radio_reg(pi, RADIO_2055_CAL_MISC, 0x3C);
+ if ((noise_var_buf != NULL) && (tone_id_buf != NULL)) {
+ pi->nphy_saved_noisevars.bufcount = 0;
- and_radio_reg(pi, RADIO_2055_CAL_MISC,
- ~(RADIO_2055_RRCAL_START | RADIO_2055_RRCAL_RST_N));
+ for (i = 0; i < ntones; i++) {
+ tone_id = tone_id_buf[i];
+ offset = (tone_id >= 0) ?
+ ((tone_id * 2) + 1) :
+ (tbllen + (tone_id * 2) + 1);
+ pi->nphy_saved_noisevars.tone_id[i] = tone_id;
+ wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1,
+ offset, 32,
+ &pi->nphy_saved_noisevars.
+ min_noise_vars[i]);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1,
+ offset, 32, &noise_var_buf[i]);
+ pi->nphy_saved_noisevars.bufcount++;
+ }
- or_radio_reg(pi, RADIO_2055_CAL_LPO_CNTRL, RADIO_2055_CAL_LPO_ENABLE);
+ pi->nphy_noisevars_adjusted = true;
+ }
+}
- or_radio_reg(pi, RADIO_2055_CAL_MISC, RADIO_2055_RRCAL_RST_N);
+static void wlc_phy_adjust_crsminpwr_nphy(struct brcms_phy *pi, u8 minpwr)
+{
+ u16 regval;
- udelay(1000);
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if ((CHSPEC_CHANNEL(pi->radio_chanspec) == 11) &&
+ CHSPEC_IS40(pi->radio_chanspec)) {
+ if (!pi->nphy_crsminpwr_adjusted) {
+ regval = read_phy_reg(pi, 0x27d);
+ pi->nphy_crsminpwr[0] = regval & 0xff;
+ regval &= 0xff00;
+ regval |= (u16) minpwr;
+ write_phy_reg(pi, 0x27d, regval);
- or_radio_reg(pi, RADIO_2055_CAL_MISC, RADIO_2055_RRCAL_START);
+ regval = read_phy_reg(pi, 0x280);
+ pi->nphy_crsminpwr[1] = regval & 0xff;
+ regval &= 0xff00;
+ regval |= (u16) minpwr;
+ write_phy_reg(pi, 0x280, regval);
- SPINWAIT(((read_radio_reg(pi, RADIO_2055_CAL_COUNTER_OUT2) &
- RADIO_2055_RCAL_DONE) != RADIO_2055_RCAL_DONE), 2000);
+ regval = read_phy_reg(pi, 0x283);
+ pi->nphy_crsminpwr[2] = regval & 0xff;
+ regval &= 0xff00;
+ regval |= (u16) minpwr;
+ write_phy_reg(pi, 0x283, regval);
- if (WARN((read_radio_reg(pi, RADIO_2055_CAL_COUNTER_OUT2) &
- RADIO_2055_RCAL_DONE) != RADIO_2055_RCAL_DONE,
- "HW error: radio calibration1\n"))
+ pi->nphy_crsminpwr_adjusted = true;
+ }
+ } else {
+ if (pi->nphy_crsminpwr_adjusted) {
+ regval = read_phy_reg(pi, 0x27d);
+ regval &= 0xff00;
+ regval |= pi->nphy_crsminpwr[0];
+ write_phy_reg(pi, 0x27d, regval);
+
+ regval = read_phy_reg(pi, 0x280);
+ regval &= 0xff00;
+ regval |= pi->nphy_crsminpwr[1];
+ write_phy_reg(pi, 0x280, regval);
+
+ regval = read_phy_reg(pi, 0x283);
+ regval &= 0xff00;
+ regval |= pi->nphy_crsminpwr[2];
+ write_phy_reg(pi, 0x283, regval);
+
+ pi->nphy_crsminpwr_adjusted = false;
+ }
+ }
+ }
+}
+
+static void wlc_phy_spurwar_nphy(struct brcms_phy *pi)
+{
+ u16 cur_channel = 0;
+ int nphy_adj_tone_id_buf[] = { 57, 58 };
+ u32 nphy_adj_noise_var_buf[] = { 0x3ff, 0x3ff };
+ bool isAdjustNoiseVar = false;
+ uint numTonesAdjust = 0;
+ u32 tempval = 0;
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, true);
+
+ cur_channel = CHSPEC_CHANNEL(pi->radio_chanspec);
+
+ if (pi->nphy_gband_spurwar_en) {
+
+ wlc_phy_adjust_rx_analpfbw_nphy(
+ pi,
+ NPHY_ANARXLPFBW_REDUCTIONFACT);
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if ((cur_channel == 11)
+ && CHSPEC_IS40(pi->radio_chanspec))
+ wlc_phy_adjust_min_noisevar_nphy(
+ pi, 2,
+ nphy_adj_tone_id_buf,
+ nphy_adj_noise_var_buf);
+ else
+ wlc_phy_adjust_min_noisevar_nphy(pi, 0,
+ NULL,
+ NULL);
+ }
+
+ wlc_phy_adjust_crsminpwr_nphy(pi,
+ NPHY_ADJUSTED_MINCRSPOWER);
+ }
+
+ if ((pi->nphy_gband_spurwar2_en)
+ && CHSPEC_IS2G(pi->radio_chanspec)) {
+
+ if (CHSPEC_IS40(pi->radio_chanspec)) {
+ switch (cur_channel) {
+ case 3:
+ nphy_adj_tone_id_buf[0] = 57;
+ nphy_adj_tone_id_buf[1] = 58;
+ nphy_adj_noise_var_buf[0] = 0x22f;
+ nphy_adj_noise_var_buf[1] = 0x25f;
+ isAdjustNoiseVar = true;
+ break;
+ case 4:
+ nphy_adj_tone_id_buf[0] = 41;
+ nphy_adj_tone_id_buf[1] = 42;
+ nphy_adj_noise_var_buf[0] = 0x22f;
+ nphy_adj_noise_var_buf[1] = 0x25f;
+ isAdjustNoiseVar = true;
+ break;
+ case 5:
+ nphy_adj_tone_id_buf[0] = 25;
+ nphy_adj_tone_id_buf[1] = 26;
+ nphy_adj_noise_var_buf[0] = 0x24f;
+ nphy_adj_noise_var_buf[1] = 0x25f;
+ isAdjustNoiseVar = true;
+ break;
+ case 6:
+ nphy_adj_tone_id_buf[0] = 9;
+ nphy_adj_tone_id_buf[1] = 10;
+ nphy_adj_noise_var_buf[0] = 0x22f;
+ nphy_adj_noise_var_buf[1] = 0x24f;
+ isAdjustNoiseVar = true;
+ break;
+ case 7:
+ nphy_adj_tone_id_buf[0] = 121;
+ nphy_adj_tone_id_buf[1] = 122;
+ nphy_adj_noise_var_buf[0] = 0x18f;
+ nphy_adj_noise_var_buf[1] = 0x24f;
+ isAdjustNoiseVar = true;
+ break;
+ case 8:
+ nphy_adj_tone_id_buf[0] = 105;
+ nphy_adj_tone_id_buf[1] = 106;
+ nphy_adj_noise_var_buf[0] = 0x22f;
+ nphy_adj_noise_var_buf[1] = 0x25f;
+ isAdjustNoiseVar = true;
+ break;
+ case 9:
+ nphy_adj_tone_id_buf[0] = 89;
+ nphy_adj_tone_id_buf[1] = 90;
+ nphy_adj_noise_var_buf[0] = 0x22f;
+ nphy_adj_noise_var_buf[1] = 0x24f;
+ isAdjustNoiseVar = true;
+ break;
+ case 10:
+ nphy_adj_tone_id_buf[0] = 73;
+ nphy_adj_tone_id_buf[1] = 74;
+ nphy_adj_noise_var_buf[0] = 0x22f;
+ nphy_adj_noise_var_buf[1] = 0x24f;
+ isAdjustNoiseVar = true;
+ break;
+ default:
+ isAdjustNoiseVar = false;
+ break;
+ }
+ }
+
+ if (isAdjustNoiseVar) {
+ numTonesAdjust = sizeof(nphy_adj_tone_id_buf) /
+ sizeof(nphy_adj_tone_id_buf[0]);
+
+ wlc_phy_adjust_min_noisevar_nphy(
+ pi,
+ numTonesAdjust,
+ nphy_adj_tone_id_buf,
+ nphy_adj_noise_var_buf);
+
+ tempval = 0;
+
+ } else {
+ wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL,
+ NULL);
+ }
+ }
+
+ if ((pi->nphy_aband_spurwar_en) &&
+ (CHSPEC_IS5G(pi->radio_chanspec))) {
+ switch (cur_channel) {
+ case 54:
+ nphy_adj_tone_id_buf[0] = 32;
+ nphy_adj_noise_var_buf[0] = 0x25f;
+ break;
+ case 38:
+ case 102:
+ case 118:
+ nphy_adj_tone_id_buf[0] = 0;
+ nphy_adj_noise_var_buf[0] = 0x0;
+ break;
+ case 134:
+ nphy_adj_tone_id_buf[0] = 32;
+ nphy_adj_noise_var_buf[0] = 0x21f;
+ break;
+ case 151:
+ nphy_adj_tone_id_buf[0] = 16;
+ nphy_adj_noise_var_buf[0] = 0x23f;
+ break;
+ case 153:
+ case 161:
+ nphy_adj_tone_id_buf[0] = 48;
+ nphy_adj_noise_var_buf[0] = 0x23f;
+ break;
+ default:
+ nphy_adj_tone_id_buf[0] = 0;
+ nphy_adj_noise_var_buf[0] = 0x0;
+ break;
+ }
+
+ if (nphy_adj_tone_id_buf[0]
+ && nphy_adj_noise_var_buf[0])
+ wlc_phy_adjust_min_noisevar_nphy(
+ pi, 1,
+ nphy_adj_tone_id_buf,
+ nphy_adj_noise_var_buf);
+ else
+ wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL,
+ NULL);
+ }
+
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, false);
+ }
+}
+
+void wlc_phy_init_nphy(struct brcms_phy *pi)
+{
+ u16 val;
+ u16 clip1_ths[2];
+ struct nphy_txgains target_gain;
+ u8 tx_pwr_ctrl_state;
+ bool do_nphy_cal = false;
+ uint core;
+ uint origidx, intr_val;
+ struct d11regs __iomem *regs;
+ u32 d11_clk_ctl_st;
+ bool do_rssi_cal = false;
+
+ core = 0;
+
+ if (!(pi->measure_hold & PHY_HOLD_FOR_SCAN))
+ pi->measure_hold |= PHY_HOLD_FOR_NOT_ASSOC;
+
+ if ((ISNPHY(pi)) && (NREV_GE(pi->pubpi.phy_rev, 5)) &&
+ ((pi->sh->chippkg == BCM4717_PKG_ID) ||
+ (pi->sh->chippkg == BCM4718_PKG_ID))) {
+ if ((pi->sh->boardflags & BFL_EXTLNA) &&
+ (CHSPEC_IS2G(pi->radio_chanspec)))
+ ai_corereg(pi->sh->sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol),
+ 0x40, 0x40);
+ }
+
+ if ((pi->nphy_gband_spurwar2_en) && CHSPEC_IS2G(pi->radio_chanspec) &&
+ CHSPEC_IS40(pi->radio_chanspec)) {
+
+ regs = (struct d11regs __iomem *)
+ ai_switch_core(pi->sh->sih,
+ D11_CORE_ID, &origidx,
+ &intr_val);
+ d11_clk_ctl_st = R_REG(&regs->clk_ctl_st);
+ AND_REG(&regs->clk_ctl_st,
+ ~(CCS_FORCEHT | CCS_HTAREQ));
+
+ W_REG(&regs->clk_ctl_st, d11_clk_ctl_st);
+
+ ai_restore_core(pi->sh->sih, origidx, intr_val);
+ }
+
+ pi->use_int_tx_iqlo_cal_nphy =
+ (PHY_IPA(pi) ||
+ (NREV_GE(pi->pubpi.phy_rev, 7) ||
+ (NREV_GE(pi->pubpi.phy_rev, 5)
+ && pi->sh->boardflags2 & BFL2_INTERNDET_TXIQCAL)));
+
+ pi->internal_tx_iqlo_cal_tapoff_intpa_nphy = false;
+
+ pi->nphy_deaf_count = 0;
+
+ wlc_phy_tbl_init_nphy(pi);
+
+ pi->nphy_crsminpwr_adjusted = false;
+ pi->nphy_noisevars_adjusted = false;
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ write_phy_reg(pi, 0xe7, 0);
+ write_phy_reg(pi, 0xec, 0);
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ write_phy_reg(pi, 0x342, 0);
+ write_phy_reg(pi, 0x343, 0);
+ write_phy_reg(pi, 0x346, 0);
+ write_phy_reg(pi, 0x347, 0);
+ }
+ write_phy_reg(pi, 0xe5, 0);
+ write_phy_reg(pi, 0xe6, 0);
+ } else {
+ write_phy_reg(pi, 0xec, 0);
+ }
+
+ write_phy_reg(pi, 0x91, 0);
+ write_phy_reg(pi, 0x92, 0);
+ if (NREV_LT(pi->pubpi.phy_rev, 6)) {
+ write_phy_reg(pi, 0x93, 0);
+ write_phy_reg(pi, 0x94, 0);
+ }
+
+ and_phy_reg(pi, 0xa1, ~3);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ write_phy_reg(pi, 0x8f, 0);
+ write_phy_reg(pi, 0xa5, 0);
+ } else {
+ write_phy_reg(pi, 0xa5, 0);
+ }
+
+ if (NREV_IS(pi->pubpi.phy_rev, 2))
+ mod_phy_reg(pi, 0xdc, 0x00ff, 0x3b);
+ else if (NREV_LT(pi->pubpi.phy_rev, 2))
+ mod_phy_reg(pi, 0xdc, 0x00ff, 0x40);
+
+ write_phy_reg(pi, 0x203, 32);
+ write_phy_reg(pi, 0x201, 32);
+
+ if (pi->sh->boardflags2 & BFL2_SKWRKFEM_BRD)
+ write_phy_reg(pi, 0x20d, 160);
+ else
+ write_phy_reg(pi, 0x20d, 184);
+
+ write_phy_reg(pi, 0x13a, 200);
+
+ write_phy_reg(pi, 0x70, 80);
+
+ write_phy_reg(pi, 0x1ff, 48);
+
+ if (NREV_LT(pi->pubpi.phy_rev, 8))
+ wlc_phy_update_mimoconfig_nphy(pi, pi->n_preamble_override);
+
+ wlc_phy_stf_chain_upd_nphy(pi);
+
+ if (NREV_LT(pi->pubpi.phy_rev, 2)) {
+ write_phy_reg(pi, 0x180, 0xaa8);
+ write_phy_reg(pi, 0x181, 0x9a4);
+ }
+
+ if (PHY_IPA(pi)) {
+ for (core = 0; core < pi->pubpi.phy_corenum; core++) {
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (1) << 0);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x298 :
+ 0x29c, (0x1ff << 7),
+ (pi->nphy_papd_epsilon_offset[core]) << 7);
+
+ }
+
+ wlc_phy_ipa_set_tx_digi_filts_nphy(pi);
+ } else if (NREV_GE(pi->pubpi.phy_rev, 5)) {
+ wlc_phy_extpa_set_tx_digi_filts_nphy(pi);
+ }
+
+ wlc_phy_workarounds_nphy(pi);
+
+ wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON);
+
+ val = read_phy_reg(pi, 0x01);
+ write_phy_reg(pi, 0x01, val | BBCFG_RESETCCA);
+ write_phy_reg(pi, 0x01, val & (~BBCFG_RESETCCA));
+ wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF);
+
+ wlapi_bmac_macphyclk_set(pi->sh->physhim, ON);
+
+ wlc_phy_pa_override_nphy(pi, OFF);
+ wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX);
+ wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX);
+ wlc_phy_pa_override_nphy(pi, ON);
+
+ wlc_phy_classifier_nphy(pi, 0, 0);
+ wlc_phy_clip_det_nphy(pi, 0, clip1_ths);
+
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ wlc_phy_bphy_init_nphy(pi);
+
+ tx_pwr_ctrl_state = pi->nphy_txpwrctrl;
+ wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF);
+
+ wlc_phy_txpwr_fixpower_nphy(pi);
+
+ wlc_phy_txpwrctrl_idle_tssi_nphy(pi);
+
+ wlc_phy_txpwrctrl_pwr_setup_nphy(pi);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ u32 *tx_pwrctrl_tbl = NULL;
+ u16 idx;
+ s16 pga_gn = 0;
+ s16 pad_gn = 0;
+ s32 rfpwr_offset;
+
+ if (PHY_IPA(pi)) {
+ tx_pwrctrl_tbl = wlc_phy_get_ipa_gaintbl_nphy(pi);
+ } else {
+ if (CHSPEC_IS5G(pi->radio_chanspec)) {
+ if (NREV_IS(pi->pubpi.phy_rev, 3))
+ tx_pwrctrl_tbl =
+ nphy_tpc_5GHz_txgain_rev3;
+ else if (NREV_IS(pi->pubpi.phy_rev, 4))
+ tx_pwrctrl_tbl =
+ (pi->srom_fem5g.extpagain ==
+ 3) ?
+ nphy_tpc_5GHz_txgain_HiPwrEPA :
+ nphy_tpc_5GHz_txgain_rev4;
+ else
+ tx_pwrctrl_tbl =
+ nphy_tpc_5GHz_txgain_rev5;
+ } else {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (pi->pubpi.radiorev == 5)
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_epa_2057rev5;
+ else if (pi->pubpi.radiorev == 3)
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_epa_2057rev3;
+ } else {
+ if (NREV_GE(pi->pubpi.phy_rev, 5) &&
+ (pi->srom_fem2g.extpagain == 3))
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_HiPwrEPA;
+ else
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_rev3;
+ }
+ }
+ }
+
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 128,
+ 192, 32, tx_pwrctrl_tbl);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 128,
+ 192, 32, tx_pwrctrl_tbl);
+
+ pi->nphy_gmval = (u16) ((*tx_pwrctrl_tbl >> 16) & 0x7000);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+
+ for (idx = 0; idx < 128; idx++) {
+ pga_gn = (tx_pwrctrl_tbl[idx] >> 24) & 0xf;
+ pad_gn = (tx_pwrctrl_tbl[idx] >> 19) & 0x1f;
+ rfpwr_offset = get_rf_pwr_offset(pi, pga_gn,
+ pad_gn);
+ wlc_phy_table_write_nphy(
+ pi,
+ NPHY_TBL_ID_CORE1TXPWRCTL,
+ 1, 576 + idx, 32,
+ &rfpwr_offset);
+ wlc_phy_table_write_nphy(
+ pi,
+ NPHY_TBL_ID_CORE2TXPWRCTL,
+ 1, 576 + idx, 32,
+ &rfpwr_offset);
+ }
+ } else {
+
+ for (idx = 0; idx < 128; idx++) {
+ pga_gn = (tx_pwrctrl_tbl[idx] >> 24) & 0xf;
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ rfpwr_offset = (s16)
+ nphy_papd_pga_gain_delta_ipa_2g
+ [pga_gn];
+ else
+ rfpwr_offset = (s16)
+ nphy_papd_pga_gain_delta_ipa_5g
+ [pga_gn];
+
+ wlc_phy_table_write_nphy(
+ pi,
+ NPHY_TBL_ID_CORE1TXPWRCTL,
+ 1, 576 + idx, 32,
+ &rfpwr_offset);
+ wlc_phy_table_write_nphy(
+ pi,
+ NPHY_TBL_ID_CORE2TXPWRCTL,
+ 1, 576 + idx, 32,
+ &rfpwr_offset);
+ }
+
+ }
+ } else {
+
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 128,
+ 192, 32, nphy_tpc_txgain);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 128,
+ 192, 32, nphy_tpc_txgain);
+ }
+
+ if (pi->sh->phyrxchain != 0x3)
+ wlc_phy_rxcore_setstate_nphy((struct brcms_phy_pub *) pi,
+ pi->sh->phyrxchain);
+
+ if (PHY_PERICAL_MPHASE_PENDING(pi))
+ wlc_phy_cal_perical_mphase_restart(pi);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ do_rssi_cal = (CHSPEC_IS2G(pi->radio_chanspec)) ?
+ (pi->nphy_rssical_chanspec_2G == 0) :
+ (pi->nphy_rssical_chanspec_5G == 0);
+
+ if (do_rssi_cal)
+ wlc_phy_rssi_cal_nphy(pi);
+ else
+ wlc_phy_restore_rssical_nphy(pi);
+ } else {
+ wlc_phy_rssi_cal_nphy(pi);
+ }
+
+ if (!SCAN_RM_IN_PROGRESS(pi))
+ do_nphy_cal = (CHSPEC_IS2G(pi->radio_chanspec)) ?
+ (pi->nphy_iqcal_chanspec_2G == 0) :
+ (pi->nphy_iqcal_chanspec_5G == 0);
+
+ if (!pi->do_initcal)
+ do_nphy_cal = false;
+
+ if (do_nphy_cal) {
+
+ target_gain = wlc_phy_get_tx_gain_nphy(pi);
+
+ if (pi->antsel_type == ANTSEL_2x3)
+ wlc_phy_antsel_init((struct brcms_phy_pub *) pi,
+ true);
+
+ if (pi->nphy_perical != PHY_PERICAL_MPHASE) {
+ wlc_phy_rssi_cal_nphy(pi);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ pi->nphy_cal_orig_pwr_idx[0] =
+ pi->nphy_txpwrindex[PHY_CORE_0]
+ .
+ index_internal;
+ pi->nphy_cal_orig_pwr_idx[1] =
+ pi->nphy_txpwrindex[PHY_CORE_1]
+ .
+ index_internal;
+
+ wlc_phy_precal_txgain_nphy(pi);
+ target_gain =
+ wlc_phy_get_tx_gain_nphy(pi);
+ }
+
+ if (wlc_phy_cal_txiqlo_nphy
+ (pi, target_gain, true,
+ false) == 0) {
+ if (wlc_phy_cal_rxiq_nphy
+ (pi, target_gain, 2,
+ false) == 0)
+ wlc_phy_savecal_nphy(pi);
+
+ }
+ } else if (pi->mphase_cal_phase_id ==
+ MPHASE_CAL_STATE_IDLE) {
+ wlc_phy_cal_perical((struct brcms_phy_pub *) pi,
+ PHY_PERICAL_PHYINIT);
+ }
+ } else {
+ wlc_phy_restorecal_nphy(pi);
+ }
+
+ wlc_phy_txpwrctrl_coeff_setup_nphy(pi);
+
+ wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state);
+
+ wlc_phy_nphy_tkip_rifs_war(pi, pi->sh->_rifs_phy);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LE(pi->pubpi.phy_rev, 6))
+
+ write_phy_reg(pi, 0x70, 50);
+
+ wlc_phy_txlpfbw_nphy(pi);
+
+ wlc_phy_spurwar_nphy(pi);
+
+}
+
+static void wlc_phy_resetcca_nphy(struct brcms_phy *pi)
+{
+ u16 val;
+
+ wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON);
+
+ val = read_phy_reg(pi, 0x01);
+ write_phy_reg(pi, 0x01, val | BBCFG_RESETCCA);
+ udelay(1);
+ write_phy_reg(pi, 0x01, val & (~BBCFG_RESETCCA));
+
+ wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF);
+
+ wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX);
+}
+
+void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en)
+{
+ u16 rfctrlintc_override_val;
+
+ if (!en) {
+
+ pi->rfctrlIntc1_save = read_phy_reg(pi, 0x91);
+ pi->rfctrlIntc2_save = read_phy_reg(pi, 0x92);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ rfctrlintc_override_val = 0x1480;
+ else if (NREV_GE(pi->pubpi.phy_rev, 3))
+ rfctrlintc_override_val =
+ CHSPEC_IS5G(pi->radio_chanspec) ? 0x600 : 0x480;
+ else
+ rfctrlintc_override_val =
+ CHSPEC_IS5G(pi->radio_chanspec) ? 0x180 : 0x120;
+
+ write_phy_reg(pi, 0x91, rfctrlintc_override_val);
+ write_phy_reg(pi, 0x92, rfctrlintc_override_val);
+ } else {
+ write_phy_reg(pi, 0x91, pi->rfctrlIntc1_save);
+ write_phy_reg(pi, 0x92, pi->rfctrlIntc2_save);
+ }
+
+}
+
+void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi)
+{
+
+ u16 txrx_chain =
+ (NPHY_RfseqCoreActv_TxRxChain0 | NPHY_RfseqCoreActv_TxRxChain1);
+ bool CoreActv_override = false;
+
+ if (pi->nphy_txrx_chain == BRCMS_N_TXRX_CHAIN0) {
+ txrx_chain = NPHY_RfseqCoreActv_TxRxChain0;
+ CoreActv_override = true;
+
+ if (NREV_LE(pi->pubpi.phy_rev, 2))
+ and_phy_reg(pi, 0xa0, ~0x20);
+ } else if (pi->nphy_txrx_chain == BRCMS_N_TXRX_CHAIN1) {
+ txrx_chain = NPHY_RfseqCoreActv_TxRxChain1;
+ CoreActv_override = true;
+
+ if (NREV_LE(pi->pubpi.phy_rev, 2))
+ or_phy_reg(pi, 0xa0, 0x20);
+ }
+
+ mod_phy_reg(pi, 0xa2, ((0xf << 0) | (0xf << 4)), txrx_chain);
+
+ if (CoreActv_override) {
+ pi->nphy_perical = PHY_PERICAL_DISABLE;
+ or_phy_reg(pi, 0xa1, NPHY_RfseqMode_CoreActv_override);
+ } else {
+ pi->nphy_perical = PHY_PERICAL_MPHASE;
+ and_phy_reg(pi, 0xa1, ~NPHY_RfseqMode_CoreActv_override);
+ }
+}
+
+void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, u8 rxcore_bitmask)
+{
+ u16 regval;
+ u16 tbl_buf[16];
+ uint i;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
+ u16 tbl_opcode;
+ bool suspend;
+
+ pi->sh->phyrxchain = rxcore_bitmask;
+
+ if (!pi->sh->clk)
return;
- and_radio_reg(pi, RADIO_2055_CAL_LPO_CNTRL,
- ~(RADIO_2055_CAL_LPO_ENABLE));
+ suspend = (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ if (!suspend)
+ wlapi_suspend_mac_and_wait(pi->sh->physhim);
- wlc_phy_chanspec_set((wlc_phy_t *) pi, pi->radio_chanspec);
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, true);
- write_radio_reg(pi, RADIO_2055_CORE1_RXBB_LPF, 9);
- write_radio_reg(pi, RADIO_2055_CORE2_RXBB_LPF, 9);
+ regval = read_phy_reg(pi, 0xa2);
+ regval &= ~(0xf << 4);
+ regval |= ((u16) (rxcore_bitmask & 0x3)) << 4;
+ write_phy_reg(pi, 0xa2, regval);
- write_radio_reg(pi, RADIO_2055_CORE1_RXBB_MIDAC_HIPAS, 0x83);
- write_radio_reg(pi, RADIO_2055_CORE2_RXBB_MIDAC_HIPAS, 0x83);
+ if ((rxcore_bitmask & 0x3) != 0x3) {
- mod_radio_reg(pi, RADIO_2055_CORE1_LNA_GAINBST,
- RADIO_2055_GAINBST_VAL_MASK, RADIO_2055_GAINBST_CODE);
- mod_radio_reg(pi, RADIO_2055_CORE2_LNA_GAINBST,
- RADIO_2055_GAINBST_VAL_MASK, RADIO_2055_GAINBST_CODE);
- if (pi->nphy_gain_boost) {
- and_radio_reg(pi, RADIO_2055_CORE1_RXRF_SPC1,
- ~(RADIO_2055_GAINBST_DISABLE));
- and_radio_reg(pi, RADIO_2055_CORE2_RXRF_SPC1,
- ~(RADIO_2055_GAINBST_DISABLE));
+ write_phy_reg(pi, 0x20e, 1);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (pi->rx2tx_biasentry == -1) {
+ wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ,
+ ARRAY_SIZE(tbl_buf), 80,
+ 16, tbl_buf);
+
+ for (i = 0; i < ARRAY_SIZE(tbl_buf); i++) {
+ if (tbl_buf[i] ==
+ NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS) {
+ pi->rx2tx_biasentry = (u8) i;
+ tbl_opcode =
+ NPHY_REV3_RFSEQ_CMD_NOP;
+ wlc_phy_table_write_nphy(
+ pi,
+ NPHY_TBL_ID_RFSEQ,
+ 1, i,
+ 16,
+ &tbl_opcode);
+ break;
+ } else if (tbl_buf[i] ==
+ NPHY_REV3_RFSEQ_CMD_END)
+ break;
+ }
+ }
+ }
} else {
- or_radio_reg(pi, RADIO_2055_CORE1_RXRF_SPC1,
- RADIO_2055_GAINBST_DISABLE);
- or_radio_reg(pi, RADIO_2055_CORE2_RXRF_SPC1,
- RADIO_2055_GAINBST_DISABLE);
+
+ write_phy_reg(pi, 0x20e, 30);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (pi->rx2tx_biasentry != -1) {
+ tbl_opcode = NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS;
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ,
+ 1, pi->rx2tx_biasentry,
+ 16, &tbl_opcode);
+ pi->rx2tx_biasentry = -1;
+ }
+ }
}
- udelay(2);
+ wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX);
+
+ if (pi->phyhang_avoid)
+ wlc_phy_stay_in_carriersearch_nphy(pi, false);
+
+ if (!suspend)
+ wlapi_enable_mac(pi->sh->physhim);
+}
+
+u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih)
+{
+ u16 regval, rxen_bits;
+ struct brcms_phy *pi = (struct brcms_phy *) pih;
+
+ regval = read_phy_reg(pi, 0xa2);
+ rxen_bits = (regval >> 4) & 0xf;
+
+ return (u8) rxen_bits;
+}
+
+bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pi)
+{
+ return PHY_IPA(pi);
}
-static void wlc_phy_radio_preinit_205x(phy_info_t *pi)
+void wlc_phy_cal_init_nphy(struct brcms_phy *pi)
+{
+}
+
+static void wlc_phy_radio_preinit_205x(struct brcms_phy *pi)
{
and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU);
@@ -17447,11 +20004,257 @@ static void wlc_phy_radio_preinit_205x(phy_info_t *pi)
}
-static void wlc_phy_radio_init_2056(phy_info_t *pi)
+static void wlc_phy_radio_init_2057(struct brcms_phy *pi)
+{
+ struct radio_20xx_regs *regs_2057_ptr = NULL;
+
+ if (NREV_IS(pi->pubpi.phy_rev, 7)) {
+ regs_2057_ptr = regs_2057_rev4;
+ } else if (NREV_IS(pi->pubpi.phy_rev, 8)
+ || NREV_IS(pi->pubpi.phy_rev, 9)) {
+ switch (pi->pubpi.radiorev) {
+ case 5:
+
+ if (pi->pubpi.radiover == 0x0)
+ regs_2057_ptr = regs_2057_rev5;
+ else if (pi->pubpi.radiover == 0x1)
+ regs_2057_ptr = regs_2057_rev5v1;
+ else
+ break;
+
+ case 7:
+
+ regs_2057_ptr = regs_2057_rev7;
+ break;
+
+ case 8:
+
+ regs_2057_ptr = regs_2057_rev8;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ wlc_phy_init_radio_regs_allbands(pi, regs_2057_ptr);
+}
+
+static u16 wlc_phy_radio205x_rcal(struct brcms_phy *pi)
+{
+ u16 rcal_reg = 0;
+ int i;
+
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+
+ if (pi->pubpi.radiorev == 5) {
+
+ and_phy_reg(pi, 0x342, ~(0x1 << 1));
+
+ udelay(10);
+
+ mod_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1, 0x1);
+ mod_radio_reg(pi, RADIO_2057v7_IQTEST_SEL_PU2, 0x2,
+ 0x1);
+ }
+ mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x1, 0x1);
+
+ udelay(10);
+
+ mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x3, 0x3);
+
+ for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) {
+ rcal_reg = read_radio_reg(pi, RADIO_2057_RCAL_STATUS);
+ if (rcal_reg & 0x1)
+ break;
+
+ udelay(100);
+ }
+
+ if (WARN(i == MAX_205x_RCAL_WAITLOOPS,
+ "HW error: radio calib2"))
+ return 0;
+
+ mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x2, 0x0);
+
+ rcal_reg = read_radio_reg(pi, RADIO_2057_RCAL_STATUS) & 0x3e;
+
+ mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x1, 0x0);
+ if (pi->pubpi.radiorev == 5) {
+
+ mod_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1, 0x0);
+ mod_radio_reg(pi, RADIO_2057v7_IQTEST_SEL_PU2, 0x2,
+ 0x0);
+ }
+
+ if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) {
+
+ mod_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, 0x3c,
+ rcal_reg);
+ mod_radio_reg(pi, RADIO_2057_BANDGAP_RCAL_TRIM, 0xf0,
+ rcal_reg << 2);
+ }
+
+ } else if (NREV_IS(pi->pubpi.phy_rev, 3)) {
+ u16 savereg;
+
+ savereg =
+ read_radio_reg(
+ pi,
+ RADIO_2056_SYN_PLL_MAST2 |
+ RADIO_2056_SYN);
+ write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN,
+ savereg | 0x7);
+ udelay(10);
+
+ write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN,
+ 0x1);
+ udelay(10);
+
+ write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN,
+ 0x9);
+
+ for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) {
+ rcal_reg = read_radio_reg(
+ pi,
+ RADIO_2056_SYN_RCAL_CODE_OUT |
+ RADIO_2056_SYN);
+ if (rcal_reg & 0x80)
+ break;
+
+ udelay(100);
+ }
+
+ if (WARN(i == MAX_205x_RCAL_WAITLOOPS,
+ "HW error: radio calib3"))
+ return 0;
+
+ write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN,
+ 0x1);
+
+ rcal_reg =
+ read_radio_reg(pi,
+ RADIO_2056_SYN_RCAL_CODE_OUT |
+ RADIO_2056_SYN);
+
+ write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN,
+ 0x0);
+
+ write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN,
+ savereg);
+
+ return rcal_reg & 0x1f;
+ }
+ return rcal_reg & 0x3e;
+}
+
+static u16 wlc_phy_radio2057_rccal(struct brcms_phy *pi)
+{
+ u16 rccal_valid;
+ int i;
+ bool chip43226_6362A0;
+
+ chip43226_6362A0 = ((pi->pubpi.radiorev == 3)
+ || (pi->pubpi.radiorev == 4)
+ || (pi->pubpi.radiorev == 6));
+
+ rccal_valid = 0;
+ if (chip43226_6362A0) {
+ write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x61);
+ write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xc0);
+ } else {
+ write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x61);
+
+ write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xe9);
+ }
+ write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e);
+ write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55);
+
+ for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) {
+ rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP);
+ if (rccal_valid & 0x2)
+ break;
+
+ udelay(500);
+ }
+
+ write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15);
+
+ rccal_valid = 0;
+ if (chip43226_6362A0) {
+ write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x69);
+ write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xb0);
+ } else {
+ write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x69);
+
+ write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xd5);
+ }
+ write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e);
+ write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55);
+
+ for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) {
+ rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP);
+ if (rccal_valid & 0x2)
+ break;
+
+ udelay(500);
+ }
+
+ write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15);
+
+ rccal_valid = 0;
+ if (chip43226_6362A0) {
+ write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x73);
+
+ write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x28);
+ write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xb0);
+ } else {
+ write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x73);
+ write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e);
+ write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0x99);
+ }
+ write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55);
+
+ for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) {
+ rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP);
+ if (rccal_valid & 0x2)
+ break;
+
+ udelay(500);
+ }
+
+ if (WARN(!(rccal_valid & 0x2), "HW error: radio calib4"))
+ return 0;
+
+ write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15);
+
+ return rccal_valid;
+}
+
+static void wlc_phy_radio_postinit_2057(struct brcms_phy *pi)
+{
+
+ mod_radio_reg(pi, RADIO_2057_XTALPUOVR_PINCTRL, 0x1, 0x1);
+
+ mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x78, 0x78);
+ mod_radio_reg(pi, RADIO_2057_XTAL_CONFIG2, 0x80, 0x80);
+ mdelay(2);
+ mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x78, 0x0);
+ mod_radio_reg(pi, RADIO_2057_XTAL_CONFIG2, 0x80, 0x0);
+
+ if (pi->phy_init_por) {
+ wlc_phy_radio205x_rcal(pi);
+ wlc_phy_radio2057_rccal(pi);
+ }
+
+ mod_radio_reg(pi, RADIO_2057_RFPLL_MASTER, 0x8, 0x0);
+}
+
+static void wlc_phy_radio_init_2056(struct brcms_phy *pi)
{
- radio_regs_t *regs_SYN_2056_ptr = NULL;
- radio_regs_t *regs_TX_2056_ptr = NULL;
- radio_regs_t *regs_RX_2056_ptr = NULL;
+ const struct radio_regs *regs_SYN_2056_ptr = NULL;
+ const struct radio_regs *regs_TX_2056_ptr = NULL;
+ const struct radio_regs *regs_RX_2056_ptr = NULL;
if (NREV_IS(pi->pubpi.phy_rev, 3)) {
regs_SYN_2056_ptr = regs_SYN_2056;
@@ -17510,7 +20313,7 @@ static void wlc_phy_radio_init_2056(phy_info_t *pi)
wlc_phy_init_radio_regs(pi, regs_RX_2056_ptr, (u16) RADIO_2056_RX1);
}
-static void wlc_phy_radio_postinit_2056(phy_info_t *pi)
+static void wlc_phy_radio_postinit_2056(struct brcms_phy *pi)
{
mod_radio_reg(pi, RADIO_2056_SYN_COM_CTRL, 0xb, 0xb);
@@ -17520,97 +20323,190 @@ static void wlc_phy_radio_postinit_2056(phy_info_t *pi)
mod_radio_reg(pi, RADIO_2056_SYN_COM_RESET, 0x2, 0x0);
if ((pi->sh->boardflags2 & BFL2_LEGACY)
- || (pi->sh->boardflags2 & BFL2_XTALBUFOUTEN)) {
-
+ || (pi->sh->boardflags2 & BFL2_XTALBUFOUTEN))
mod_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2, 0xf4, 0x0);
- } else {
-
+ else
mod_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2, 0xfc, 0x0);
- }
mod_radio_reg(pi, RADIO_2056_SYN_RCCAL_CTRL0, 0x1, 0x0);
- if (pi->phy_init_por) {
+ if (pi->phy_init_por)
wlc_phy_radio205x_rcal(pi);
- }
}
-static void wlc_phy_radio_init_2057(phy_info_t *pi)
+static void wlc_phy_radio_preinit_2055(struct brcms_phy *pi)
{
- radio_20xx_regs_t *regs_2057_ptr = NULL;
- if (NREV_IS(pi->pubpi.phy_rev, 7)) {
+ and_phy_reg(pi, 0x78, ~RFCC_POR_FORCE);
+ or_phy_reg(pi, 0x78, RFCC_CHIP0_PU | RFCC_OE_POR_FORCE);
- regs_2057_ptr = regs_2057_rev4;
- } else if (NREV_IS(pi->pubpi.phy_rev, 8)
- || NREV_IS(pi->pubpi.phy_rev, 9)) {
- switch (pi->pubpi.radiorev) {
- case 5:
+ or_phy_reg(pi, 0x78, RFCC_POR_FORCE);
+}
- if (pi->pubpi.radiover == 0x0) {
+static void wlc_phy_radio_init_2055(struct brcms_phy *pi)
+{
+ wlc_phy_init_radio_regs(pi, regs_2055, RADIO_DEFAULT_CORE);
+}
- regs_2057_ptr = regs_2057_rev5;
+static void wlc_phy_radio_postinit_2055(struct brcms_phy *pi)
+{
- } else if (pi->pubpi.radiover == 0x1) {
+ and_radio_reg(pi, RADIO_2055_MASTER_CNTRL1,
+ ~(RADIO_2055_JTAGCTRL_MASK | RADIO_2055_JTAGSYNC_MASK));
- regs_2057_ptr = regs_2057_rev5v1;
- } else {
- break;
- }
+ if (((pi->sh->sromrev >= 4)
+ && !(pi->sh->boardflags2 & BFL2_RXBB_INT_REG_DIS))
+ || ((pi->sh->sromrev < 4))) {
+ and_radio_reg(pi, RADIO_2055_CORE1_RXBB_REGULATOR, 0x7F);
+ and_radio_reg(pi, RADIO_2055_CORE2_RXBB_REGULATOR, 0x7F);
+ }
- case 7:
+ mod_radio_reg(pi, RADIO_2055_RRCCAL_N_OPT_SEL, 0x3F, 0x2C);
+ write_radio_reg(pi, RADIO_2055_CAL_MISC, 0x3C);
- regs_2057_ptr = regs_2057_rev7;
- break;
+ and_radio_reg(pi, RADIO_2055_CAL_MISC,
+ ~(RADIO_2055_RRCAL_START | RADIO_2055_RRCAL_RST_N));
- case 8:
+ or_radio_reg(pi, RADIO_2055_CAL_LPO_CNTRL, RADIO_2055_CAL_LPO_ENABLE);
- regs_2057_ptr = regs_2057_rev8;
- break;
+ or_radio_reg(pi, RADIO_2055_CAL_MISC, RADIO_2055_RRCAL_RST_N);
- default:
- break;
- }
+ udelay(1000);
+
+ or_radio_reg(pi, RADIO_2055_CAL_MISC, RADIO_2055_RRCAL_START);
+
+ SPINWAIT(((read_radio_reg(pi, RADIO_2055_CAL_COUNTER_OUT2) &
+ RADIO_2055_RCAL_DONE) != RADIO_2055_RCAL_DONE), 2000);
+
+ if (WARN((read_radio_reg(pi, RADIO_2055_CAL_COUNTER_OUT2) &
+ RADIO_2055_RCAL_DONE) != RADIO_2055_RCAL_DONE,
+ "HW error: radio calibration1\n"))
+ return;
+
+ and_radio_reg(pi, RADIO_2055_CAL_LPO_CNTRL,
+ ~(RADIO_2055_CAL_LPO_ENABLE));
+
+ wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec);
+
+ write_radio_reg(pi, RADIO_2055_CORE1_RXBB_LPF, 9);
+ write_radio_reg(pi, RADIO_2055_CORE2_RXBB_LPF, 9);
+
+ write_radio_reg(pi, RADIO_2055_CORE1_RXBB_MIDAC_HIPAS, 0x83);
+ write_radio_reg(pi, RADIO_2055_CORE2_RXBB_MIDAC_HIPAS, 0x83);
+
+ mod_radio_reg(pi, RADIO_2055_CORE1_LNA_GAINBST,
+ RADIO_2055_GAINBST_VAL_MASK, RADIO_2055_GAINBST_CODE);
+ mod_radio_reg(pi, RADIO_2055_CORE2_LNA_GAINBST,
+ RADIO_2055_GAINBST_VAL_MASK, RADIO_2055_GAINBST_CODE);
+ if (pi->nphy_gain_boost) {
+ and_radio_reg(pi, RADIO_2055_CORE1_RXRF_SPC1,
+ ~(RADIO_2055_GAINBST_DISABLE));
+ and_radio_reg(pi, RADIO_2055_CORE2_RXRF_SPC1,
+ ~(RADIO_2055_GAINBST_DISABLE));
+ } else {
+ or_radio_reg(pi, RADIO_2055_CORE1_RXRF_SPC1,
+ RADIO_2055_GAINBST_DISABLE);
+ or_radio_reg(pi, RADIO_2055_CORE2_RXRF_SPC1,
+ RADIO_2055_GAINBST_DISABLE);
}
- wlc_phy_init_radio_regs_allbands(pi, regs_2057_ptr);
+ udelay(2);
}
-static void wlc_phy_radio_postinit_2057(phy_info_t *pi)
+void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on)
{
+ if (on) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (!pi->radio_is_on) {
+ wlc_phy_radio_preinit_205x(pi);
+ wlc_phy_radio_init_2057(pi);
+ wlc_phy_radio_postinit_2057(pi);
+ }
- mod_radio_reg(pi, RADIO_2057_XTALPUOVR_PINCTRL, 0x1, 0x1);
+ wlc_phy_chanspec_set((struct brcms_phy_pub *) pi,
+ pi->radio_chanspec);
+ } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ wlc_phy_radio_preinit_205x(pi);
+ wlc_phy_radio_init_2056(pi);
+ wlc_phy_radio_postinit_2056(pi);
- if (pi->sh->chip == !BCM6362_CHIP_ID) {
+ wlc_phy_chanspec_set((struct brcms_phy_pub *) pi,
+ pi->radio_chanspec);
+ } else {
+ wlc_phy_radio_preinit_2055(pi);
+ wlc_phy_radio_init_2055(pi);
+ wlc_phy_radio_postinit_2055(pi);
+ }
- mod_radio_reg(pi, RADIO_2057_XTALPUOVR_PINCTRL, 0x2, 0x2);
- }
+ pi->radio_is_on = true;
- mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x78, 0x78);
- mod_radio_reg(pi, RADIO_2057_XTAL_CONFIG2, 0x80, 0x80);
- mdelay(2);
- mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x78, 0x0);
- mod_radio_reg(pi, RADIO_2057_XTAL_CONFIG2, 0x80, 0x0);
+ } else {
- if (pi->phy_init_por) {
- wlc_phy_radio205x_rcal(pi);
- wlc_phy_radio2057_rccal(pi);
- }
+ if (NREV_GE(pi->pubpi.phy_rev, 3)
+ && NREV_LT(pi->pubpi.phy_rev, 7)) {
+ and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU);
+ mod_radio_reg(pi, RADIO_2056_SYN_COM_PU, 0x2, 0x0);
- mod_radio_reg(pi, RADIO_2057_RFPLL_MASTER, 0x8, 0x0);
+ write_radio_reg(pi,
+ RADIO_2056_TX_PADA_BOOST_TUNE |
+ RADIO_2056_TX0, 0);
+ write_radio_reg(pi,
+ RADIO_2056_TX_PADG_BOOST_TUNE |
+ RADIO_2056_TX0, 0);
+ write_radio_reg(pi,
+ RADIO_2056_TX_PGAA_BOOST_TUNE |
+ RADIO_2056_TX0, 0);
+ write_radio_reg(pi,
+ RADIO_2056_TX_PGAG_BOOST_TUNE |
+ RADIO_2056_TX0, 0);
+ mod_radio_reg(pi,
+ RADIO_2056_TX_MIXA_BOOST_TUNE |
+ RADIO_2056_TX0, 0xf0, 0);
+ write_radio_reg(pi,
+ RADIO_2056_TX_MIXG_BOOST_TUNE |
+ RADIO_2056_TX0, 0);
+
+ write_radio_reg(pi,
+ RADIO_2056_TX_PADA_BOOST_TUNE |
+ RADIO_2056_TX1, 0);
+ write_radio_reg(pi,
+ RADIO_2056_TX_PADG_BOOST_TUNE |
+ RADIO_2056_TX1, 0);
+ write_radio_reg(pi,
+ RADIO_2056_TX_PGAA_BOOST_TUNE |
+ RADIO_2056_TX1, 0);
+ write_radio_reg(pi,
+ RADIO_2056_TX_PGAG_BOOST_TUNE |
+ RADIO_2056_TX1, 0);
+ mod_radio_reg(pi,
+ RADIO_2056_TX_MIXA_BOOST_TUNE |
+ RADIO_2056_TX1, 0xf0, 0);
+ write_radio_reg(pi,
+ RADIO_2056_TX_MIXG_BOOST_TUNE |
+ RADIO_2056_TX1, 0);
+
+ pi->radio_is_on = false;
+ }
+
+ if (NREV_GE(pi->pubpi.phy_rev, 8)) {
+ and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU);
+ pi->radio_is_on = false;
+ }
+
+ }
}
static bool
-wlc_phy_chan2freq_nphy(phy_info_t *pi, uint channel, int *f,
- chan_info_nphy_radio2057_t **t0,
- chan_info_nphy_radio205x_t **t1,
- chan_info_nphy_radio2057_rev5_t **t2,
- chan_info_nphy_2055_t **t3)
+wlc_phy_chan2freq_nphy(struct brcms_phy *pi, uint channel, int *f,
+ const struct chan_info_nphy_radio2057 **t0,
+ const struct chan_info_nphy_radio205x **t1,
+ const struct chan_info_nphy_radio2057_rev5 **t2,
+ const struct chan_info_nphy_2055 **t3)
{
uint i;
- chan_info_nphy_radio2057_t *chan_info_tbl_p_0 = NULL;
- chan_info_nphy_radio205x_t *chan_info_tbl_p_1 = NULL;
- chan_info_nphy_radio2057_rev5_t *chan_info_tbl_p_2 = NULL;
+ const struct chan_info_nphy_radio2057 *chan_info_tbl_p_0 = NULL;
+ const struct chan_info_nphy_radio205x *chan_info_tbl_p_1 = NULL;
+ const struct chan_info_nphy_radio2057_rev5 *chan_info_tbl_p_2 = NULL;
u32 tbl_len = 0;
int freq = 0;
@@ -17631,40 +20527,35 @@ wlc_phy_chan2freq_nphy(phy_info_t *pi, uint channel, int *f,
if (pi->pubpi.radiover == 0x0) {
chan_info_tbl_p_2 =
- chan_info_nphyrev8_2057_rev5;
- tbl_len =
- ARRAY_SIZE
- (chan_info_nphyrev8_2057_rev5);
+ chan_info_nphyrev8_2057_rev5;
+ tbl_len = ARRAY_SIZE(
+ chan_info_nphyrev8_2057_rev5);
} else if (pi->pubpi.radiover == 0x1) {
chan_info_tbl_p_2 =
- chan_info_nphyrev9_2057_rev5v1;
- tbl_len =
- ARRAY_SIZE
- (chan_info_nphyrev9_2057_rev5v1);
+ chan_info_nphyrev9_2057_rev5v1;
+ tbl_len = ARRAY_SIZE(
+ chan_info_nphyrev9_2057_rev5v1);
}
break;
case 7:
chan_info_tbl_p_0 =
- chan_info_nphyrev8_2057_rev7;
- tbl_len =
- ARRAY_SIZE(chan_info_nphyrev8_2057_rev7);
+ chan_info_nphyrev8_2057_rev7;
+ tbl_len = ARRAY_SIZE(
+ chan_info_nphyrev8_2057_rev7);
break;
case 8:
chan_info_tbl_p_0 =
- chan_info_nphyrev8_2057_rev8;
- tbl_len =
- ARRAY_SIZE(chan_info_nphyrev8_2057_rev8);
+ chan_info_nphyrev8_2057_rev8;
+ tbl_len = ARRAY_SIZE(
+ chan_info_nphyrev8_2057_rev8);
break;
default:
- if (NORADIO_ENAB(pi->pubpi)) {
- goto fail;
- }
break;
}
} else if (NREV_IS(pi->pubpi.phy_rev, 16)) {
@@ -17687,9 +20578,9 @@ wlc_phy_chan2freq_nphy(phy_info_t *pi, uint channel, int *f,
}
}
- if (i >= tbl_len) {
+ if (i >= tbl_len)
goto fail;
- }
+
if (pi->pubpi.radiorev == 5) {
*t2 = &chan_info_tbl_p_2[i];
freq = chan_info_tbl_p_2[i].freq;
@@ -17720,7 +20611,7 @@ wlc_phy_chan2freq_nphy(phy_info_t *pi, uint channel, int *f,
case 9:
chan_info_tbl_p_1 = chan_info_nphyrev5n6_2056v7;
tbl_len =
- ARRAY_SIZE(chan_info_nphyrev5n6_2056v7);
+ ARRAY_SIZE(chan_info_nphyrev5n6_2056v7);
break;
case 8:
chan_info_tbl_p_1 = chan_info_nphyrev6_2056v8;
@@ -17728,12 +20619,10 @@ wlc_phy_chan2freq_nphy(phy_info_t *pi, uint channel, int *f,
break;
case 11:
chan_info_tbl_p_1 = chan_info_nphyrev6_2056v11;
- tbl_len = ARRAY_SIZE(chan_info_nphyrev6_2056v11);
+ tbl_len = ARRAY_SIZE(
+ chan_info_nphyrev6_2056v11);
break;
default:
- if (NORADIO_ENAB(pi->pubpi)) {
- goto fail;
- }
break;
}
}
@@ -17743,9 +20632,9 @@ wlc_phy_chan2freq_nphy(phy_info_t *pi, uint channel, int *f,
break;
}
- if (i >= tbl_len) {
+ if (i >= tbl_len)
goto fail;
- }
+
*t1 = &chan_info_tbl_p_1[i];
freq = chan_info_tbl_p_1[i].freq;
@@ -17754,9 +20643,9 @@ wlc_phy_chan2freq_nphy(phy_info_t *pi, uint channel, int *f,
if (chan_info_nphy_2055[i].chan == channel)
break;
- if (i >= ARRAY_SIZE(chan_info_nphy_2055)) {
+ if (i >= ARRAY_SIZE(chan_info_nphy_2055))
goto fail;
- }
+
*t3 = &chan_info_nphy_2055[i];
freq = chan_info_nphy_2055[i].freq;
}
@@ -17764,21 +20653,18 @@ wlc_phy_chan2freq_nphy(phy_info_t *pi, uint channel, int *f,
*f = freq;
return true;
- fail:
+fail:
*f = WL_CHAN_FREQ_RANGE_2G;
return false;
}
-u8 wlc_phy_get_chan_freq_range_nphy(phy_info_t *pi, uint channel)
+u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint channel)
{
int freq;
- chan_info_nphy_radio2057_t *t0 = NULL;
- chan_info_nphy_radio205x_t *t1 = NULL;
- chan_info_nphy_radio2057_rev5_t *t2 = NULL;
- chan_info_nphy_2055_t *t3 = NULL;
-
- if (NORADIO_ENAB(pi->pubpi))
- return WL_CHAN_FREQ_RANGE_2G;
+ const struct chan_info_nphy_radio2057 *t0 = NULL;
+ const struct chan_info_nphy_radio205x *t1 = NULL;
+ const struct chan_info_nphy_radio2057_rev5 *t2 = NULL;
+ const struct chan_info_nphy_2055 *t3 = NULL;
if (channel == 0)
channel = CHSPEC_CHANNEL(pi->radio_chanspec);
@@ -17788,17 +20674,17 @@ u8 wlc_phy_get_chan_freq_range_nphy(phy_info_t *pi, uint channel)
if (CHSPEC_IS2G(pi->radio_chanspec))
return WL_CHAN_FREQ_RANGE_2G;
- if ((freq >= BASE_LOW_5G_CHAN) && (freq < BASE_MID_5G_CHAN)) {
+ if ((freq >= BASE_LOW_5G_CHAN) && (freq < BASE_MID_5G_CHAN))
return WL_CHAN_FREQ_RANGE_5GL;
- } else if ((freq >= BASE_MID_5G_CHAN) && (freq < BASE_HIGH_5G_CHAN)) {
+ else if ((freq >= BASE_MID_5G_CHAN) && (freq < BASE_HIGH_5G_CHAN))
return WL_CHAN_FREQ_RANGE_5GM;
- } else {
+ else
return WL_CHAN_FREQ_RANGE_5GH;
- }
}
static void
-wlc_phy_chanspec_radio2055_setup(phy_info_t *pi, chan_info_nphy_2055_t *ci)
+wlc_phy_chanspec_radio2055_setup(struct brcms_phy *pi,
+ const struct chan_info_nphy_2055 *ci)
{
write_radio_reg(pi, RADIO_2055_PLL_REF, ci->RF_pll_ref);
@@ -17806,21 +20692,21 @@ wlc_phy_chanspec_radio2055_setup(phy_info_t *pi, chan_info_nphy_2055_t *ci)
write_radio_reg(pi, RADIO_2055_RF_PLL_MOD1, ci->RF_rf_pll_mod1);
write_radio_reg(pi, RADIO_2055_VCO_CAP_TAIL, ci->RF_vco_cap_tail);
- WLC_PHY_WAR_PR51571(pi);
+ BRCMS_PHY_WAR_PR51571(pi);
write_radio_reg(pi, RADIO_2055_VCO_CAL1, ci->RF_vco_cal1);
write_radio_reg(pi, RADIO_2055_VCO_CAL2, ci->RF_vco_cal2);
write_radio_reg(pi, RADIO_2055_PLL_LF_C1, ci->RF_pll_lf_c1);
write_radio_reg(pi, RADIO_2055_PLL_LF_R1, ci->RF_pll_lf_r1);
- WLC_PHY_WAR_PR51571(pi);
+ BRCMS_PHY_WAR_PR51571(pi);
write_radio_reg(pi, RADIO_2055_PLL_LF_C2, ci->RF_pll_lf_c2);
write_radio_reg(pi, RADIO_2055_LGBUF_CEN_BUF, ci->RF_lgbuf_cen_buf);
write_radio_reg(pi, RADIO_2055_LGEN_TUNE1, ci->RF_lgen_tune1);
write_radio_reg(pi, RADIO_2055_LGEN_TUNE2, ci->RF_lgen_tune2);
- WLC_PHY_WAR_PR51571(pi);
+ BRCMS_PHY_WAR_PR51571(pi);
write_radio_reg(pi, RADIO_2055_CORE1_LGBUF_A_TUNE,
ci->RF_core1_lgbuf_a_tune);
@@ -17830,7 +20716,7 @@ wlc_phy_chanspec_radio2055_setup(phy_info_t *pi, chan_info_nphy_2055_t *ci)
write_radio_reg(pi, RADIO_2055_CORE1_TX_PGA_PAD_TN,
ci->RF_core1_tx_pga_pad_tn);
- WLC_PHY_WAR_PR51571(pi);
+ BRCMS_PHY_WAR_PR51571(pi);
write_radio_reg(pi, RADIO_2055_CORE1_TX_MX_BGTRIM,
ci->RF_core1_tx_mx_bgtrim);
@@ -17840,7 +20726,7 @@ wlc_phy_chanspec_radio2055_setup(phy_info_t *pi, chan_info_nphy_2055_t *ci)
ci->RF_core2_lgbuf_g_tune);
write_radio_reg(pi, RADIO_2055_CORE2_RXRF_REG1, ci->RF_core2_rxrf_reg1);
- WLC_PHY_WAR_PR51571(pi);
+ BRCMS_PHY_WAR_PR51571(pi);
write_radio_reg(pi, RADIO_2055_CORE2_TX_PGA_PAD_TN,
ci->RF_core2_tx_pga_pad_tn);
@@ -17852,7 +20738,7 @@ wlc_phy_chanspec_radio2055_setup(phy_info_t *pi, chan_info_nphy_2055_t *ci)
write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x05);
write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x45);
- WLC_PHY_WAR_PR51571(pi);
+ BRCMS_PHY_WAR_PR51571(pi);
write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x65);
@@ -17860,10 +20746,10 @@ wlc_phy_chanspec_radio2055_setup(phy_info_t *pi, chan_info_nphy_2055_t *ci)
}
static void
-wlc_phy_chanspec_radio2056_setup(phy_info_t *pi,
- const chan_info_nphy_radio205x_t *ci)
+wlc_phy_chanspec_radio2056_setup(struct brcms_phy *pi,
+ const struct chan_info_nphy_radio205x *ci)
{
- radio_regs_t *regs_SYN_2056_ptr = NULL;
+ const struct radio_regs *regs_SYN_2056_ptr = NULL;
write_radio_reg(pi,
RADIO_2056_SYN_PLL_VCOCAL1 | RADIO_2056_SYN,
@@ -17969,15 +20855,14 @@ wlc_phy_chanspec_radio2056_setup(phy_info_t *pi,
break;
}
}
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if (CHSPEC_IS2G(pi->radio_chanspec))
write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 |
RADIO_2056_SYN,
(u16) regs_SYN_2056_ptr[0x49 - 2].init_g);
- } else {
+ else
write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 |
RADIO_2056_SYN,
(u16) regs_SYN_2056_ptr[0x49 - 2].init_a);
- }
if (pi->sh->boardflags2 & BFL2_GPLL_WAR) {
if (CHSPEC_IS2G(pi->radio_chanspec)) {
@@ -17986,23 +20871,12 @@ wlc_phy_chanspec_radio2056_setup(phy_info_t *pi,
write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 |
RADIO_2056_SYN, 0x1f);
- if ((pi->sh->chip == BCM4716_CHIP_ID) ||
- (pi->sh->chip == BCM47162_CHIP_ID)) {
-
- write_radio_reg(pi,
- RADIO_2056_SYN_PLL_LOOPFILTER4 |
- RADIO_2056_SYN, 0x14);
- write_radio_reg(pi,
- RADIO_2056_SYN_PLL_CP2 |
- RADIO_2056_SYN, 0x00);
- } else {
- write_radio_reg(pi,
- RADIO_2056_SYN_PLL_LOOPFILTER4 |
- RADIO_2056_SYN, 0xb);
- write_radio_reg(pi,
- RADIO_2056_SYN_PLL_CP2 |
- RADIO_2056_SYN, 0x14);
- }
+ write_radio_reg(pi,
+ RADIO_2056_SYN_PLL_LOOPFILTER4 |
+ RADIO_2056_SYN, 0xb);
+ write_radio_reg(pi,
+ RADIO_2056_SYN_PLL_CP2 |
+ RADIO_2056_SYN, 0x14);
}
}
@@ -18049,38 +20923,25 @@ wlc_phy_chanspec_radio2056_setup(phy_info_t *pi,
WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
PADG_IDAC, 0xcc);
- if ((pi->sh->chip == BCM4716_CHIP_ID) ||
- (pi->sh->chip ==
- BCM47162_CHIP_ID)) {
- bias = 0x40;
- cascbias = 0x45;
- pag_boost_tune = 0x5;
- pgag_boost_tune = 0x33;
- padg_boost_tune = 0x77;
- mixg_boost_tune = 0x55;
- } else {
- bias = 0x25;
- cascbias = 0x20;
-
- if ((pi->sh->chip ==
- BCM43224_CHIP_ID)
- || (pi->sh->chip ==
- BCM43225_CHIP_ID)
- || (pi->sh->chip ==
- BCM43421_CHIP_ID)) {
- if (pi->sh->chippkg ==
- BCM43224_FAB_SMIC) {
- bias = 0x2a;
- cascbias = 0x38;
- }
+ bias = 0x25;
+ cascbias = 0x20;
+
+ if ((pi->sh->chip ==
+ BCM43224_CHIP_ID)
+ || (pi->sh->chip ==
+ BCM43225_CHIP_ID)) {
+ if (pi->sh->chippkg ==
+ BCM43224_FAB_SMIC) {
+ bias = 0x2a;
+ cascbias = 0x38;
}
-
- pag_boost_tune = 0x4;
- pgag_boost_tune = 0x03;
- padg_boost_tune = 0x77;
- mixg_boost_tune = 0x65;
}
+ pag_boost_tune = 0x4;
+ pgag_boost_tune = 0x03;
+ padg_boost_tune = 0x77;
+ mixg_boost_tune = 0x65;
+
WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
INTPAG_IMAIN_STAT, bias);
WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
@@ -18102,7 +20963,8 @@ wlc_phy_chanspec_radio2056_setup(phy_info_t *pi,
mixg_boost_tune);
} else {
- bias = IS40MHZ(pi) ? 0x40 : 0x20;
+ bias = (pi->bw == WL_CHANSPEC_BW_40) ?
+ 0x40 : 0x20;
WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
INTPAG_IMAIN_STAT, bias);
@@ -18149,11 +21011,11 @@ wlc_phy_chanspec_radio2056_setup(phy_info_t *pi,
paa_boost_tune = 0x0;
pada_boost_tune = 0x77;
- if (freq != 5825) {
+ if (freq != 5825)
pgaa_boost_tune = -(int)(freq - 18) / 36 + 168;
- } else {
+ else
pgaa_boost_tune = 6;
- }
+
mixa_boost_tune = 0xf;
}
@@ -18178,11 +21040,9 @@ wlc_phy_chanspec_radio2056_setup(phy_info_t *pi,
cascbias = 0x30;
if ((pi->sh->chip == BCM43224_CHIP_ID) ||
- (pi->sh->chip == BCM43225_CHIP_ID) ||
- (pi->sh->chip == BCM43421_CHIP_ID)) {
- if (pi->sh->chippkg == BCM43224_FAB_SMIC) {
+ (pi->sh->chip == BCM43225_CHIP_ID)) {
+ if (pi->sh->chippkg == BCM43224_FAB_SMIC)
cascbias = 0x35;
- }
}
pabias = (pi->phy_pabias == 0) ? 0x30 : pi->phy_pabias;
@@ -18201,7 +21061,7 @@ wlc_phy_chanspec_radio2056_setup(phy_info_t *pi,
wlc_phy_radio205x_vcocal_nphy(pi);
}
-void wlc_phy_radio205x_vcocal_nphy(phy_info_t *pi)
+void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi)
{
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_EN, 0x01, 0x0);
@@ -18220,117 +21080,12 @@ void wlc_phy_radio205x_vcocal_nphy(phy_info_t *pi)
udelay(300);
}
-#define MAX_205x_RCAL_WAITLOOPS 10000
-
-static u16 wlc_phy_radio205x_rcal(phy_info_t *pi)
-{
- u16 rcal_reg = 0;
- int i;
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
- if (pi->pubpi.radiorev == 5) {
-
- and_phy_reg(pi, 0x342, ~(0x1 << 1));
-
- udelay(10);
-
- mod_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1, 0x1);
- mod_radio_reg(pi, RADIO_2057v7_IQTEST_SEL_PU2, 0x2,
- 0x1);
- }
- mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x1, 0x1);
-
- udelay(10);
-
- mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x3, 0x3);
-
- for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) {
- rcal_reg = read_radio_reg(pi, RADIO_2057_RCAL_STATUS);
- if (rcal_reg & 0x1) {
- break;
- }
- udelay(100);
- }
-
- if (WARN(i == MAX_205x_RCAL_WAITLOOPS,
- "HW error: radio calib2"))
- return 0;
-
- mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x2, 0x0);
-
- rcal_reg = read_radio_reg(pi, RADIO_2057_RCAL_STATUS) & 0x3e;
-
- mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x1, 0x0);
- if (pi->pubpi.radiorev == 5) {
-
- mod_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1, 0x0);
- mod_radio_reg(pi, RADIO_2057v7_IQTEST_SEL_PU2, 0x2,
- 0x0);
- }
-
- if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) {
-
- mod_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, 0x3c,
- rcal_reg);
- mod_radio_reg(pi, RADIO_2057_BANDGAP_RCAL_TRIM, 0xf0,
- rcal_reg << 2);
- }
-
- } else if (NREV_IS(pi->pubpi.phy_rev, 3)) {
- u16 savereg;
-
- savereg =
- read_radio_reg(pi,
- RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN);
- write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN,
- savereg | 0x7);
- udelay(10);
-
- write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN,
- 0x1);
- udelay(10);
-
- write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN,
- 0x9);
-
- for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) {
- rcal_reg = read_radio_reg(pi,
- RADIO_2056_SYN_RCAL_CODE_OUT |
- RADIO_2056_SYN);
- if (rcal_reg & 0x80) {
- break;
- }
- udelay(100);
- }
-
- if (WARN(i == MAX_205x_RCAL_WAITLOOPS,
- "HW error: radio calib3"))
- return 0;
-
- write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN,
- 0x1);
-
- rcal_reg =
- read_radio_reg(pi,
- RADIO_2056_SYN_RCAL_CODE_OUT |
- RADIO_2056_SYN);
-
- write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN,
- 0x0);
-
- write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN,
- savereg);
-
- return rcal_reg & 0x1f;
- }
- return rcal_reg & 0x3e;
-}
-
static void
-wlc_phy_chanspec_radio2057_setup(phy_info_t *pi,
- const chan_info_nphy_radio2057_t *ci,
- const chan_info_nphy_radio2057_rev5_t *ci2)
+wlc_phy_chanspec_radio2057_setup(
+ struct brcms_phy *pi,
+ const struct chan_info_nphy_radio2057 *ci,
+ const struct chan_info_nphy_radio2057_rev5 *
+ ci2)
{
int coreNum;
u16 txmix2g_tune_boost_pu = 0;
@@ -18489,9 +21244,8 @@ wlc_phy_chanspec_radio2057_setup(phy_info_t *pi,
if (CHSPEC_IS2G(pi->radio_chanspec)) {
if (PHY_IPA(pi)) {
- if (pi->pubpi.radiorev == 3) {
+ if (pi->pubpi.radiorev == 3)
txmix2g_tune_boost_pu = 0x6b;
- }
if (pi->pubpi.radiorev == 5)
pad2g_tune_pus = 0x73;
@@ -18523,437 +21277,9 @@ wlc_phy_chanspec_radio2057_setup(phy_info_t *pi,
wlc_phy_radio205x_vcocal_nphy(pi);
}
-static u16 wlc_phy_radio2057_rccal(phy_info_t *pi)
-{
- u16 rccal_valid;
- int i;
- bool chip43226_6362A0;
-
- chip43226_6362A0 = ((pi->pubpi.radiorev == 3)
- || (pi->pubpi.radiorev == 4)
- || (pi->pubpi.radiorev == 6));
-
- rccal_valid = 0;
- if (chip43226_6362A0) {
- write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x61);
- write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xc0);
- } else {
- write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x61);
-
- write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xe9);
- }
- write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e);
- write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55);
-
- for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) {
- rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP);
- if (rccal_valid & 0x2) {
- break;
- }
- udelay(500);
- }
-
- write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15);
-
- rccal_valid = 0;
- if (chip43226_6362A0) {
- write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x69);
- write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xb0);
- } else {
- write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x69);
-
- write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xd5);
- }
- write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e);
- write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55);
-
- for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) {
- rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP);
- if (rccal_valid & 0x2) {
- break;
- }
- udelay(500);
- }
-
- write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15);
-
- rccal_valid = 0;
- if (chip43226_6362A0) {
- write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x73);
-
- write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x28);
- write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xb0);
- } else {
- write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x73);
- write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e);
- write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0x99);
- }
- write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55);
-
- for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) {
- rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP);
- if (rccal_valid & 0x2) {
- break;
- }
- udelay(500);
- }
-
- if (WARN(!(rccal_valid & 0x2), "HW error: radio calib4"))
- return 0;
-
- write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15);
-
- return rccal_valid;
-}
-
-static void
-wlc_phy_adjust_rx_analpfbw_nphy(phy_info_t *pi, u16 reduction_factr)
-{
- if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) {
- if ((CHSPEC_CHANNEL(pi->radio_chanspec) == 11) &&
- CHSPEC_IS40(pi->radio_chanspec)) {
- if (!pi->nphy_anarxlpf_adjusted) {
- write_radio_reg(pi,
- (RADIO_2056_RX_RXLPF_RCCAL_LPC |
- RADIO_2056_RX0),
- ((pi->nphy_rccal_value +
- reduction_factr) | 0x80));
-
- pi->nphy_anarxlpf_adjusted = true;
- }
- } else {
- if (pi->nphy_anarxlpf_adjusted) {
- write_radio_reg(pi,
- (RADIO_2056_RX_RXLPF_RCCAL_LPC |
- RADIO_2056_RX0),
- (pi->nphy_rccal_value | 0x80));
-
- pi->nphy_anarxlpf_adjusted = false;
- }
- }
- }
-}
-
-static void
-wlc_phy_adjust_min_noisevar_nphy(phy_info_t *pi, int ntones, int *tone_id_buf,
- u32 *noise_var_buf)
-{
- int i;
- u32 offset;
- int tone_id;
- int tbllen =
- CHSPEC_IS40(pi->
- radio_chanspec) ? NPHY_NOISEVAR_TBLLEN40 :
- NPHY_NOISEVAR_TBLLEN20;
-
- if (pi->nphy_noisevars_adjusted) {
- for (i = 0; i < pi->nphy_saved_noisevars.bufcount; i++) {
- tone_id = pi->nphy_saved_noisevars.tone_id[i];
- offset = (tone_id >= 0) ?
- ((tone_id * 2) + 1) : (tbllen + (tone_id * 2) + 1);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1,
- offset, 32,
- (void *)&pi->
- nphy_saved_noisevars.
- min_noise_vars[i]);
- }
-
- pi->nphy_saved_noisevars.bufcount = 0;
- pi->nphy_noisevars_adjusted = false;
- }
-
- if ((noise_var_buf != NULL) && (tone_id_buf != NULL)) {
- pi->nphy_saved_noisevars.bufcount = 0;
-
- for (i = 0; i < ntones; i++) {
- tone_id = tone_id_buf[i];
- offset = (tone_id >= 0) ?
- ((tone_id * 2) + 1) : (tbllen + (tone_id * 2) + 1);
- pi->nphy_saved_noisevars.tone_id[i] = tone_id;
- wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1,
- offset, 32,
- &pi->nphy_saved_noisevars.
- min_noise_vars[i]);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1,
- offset, 32,
- (void *)&noise_var_buf[i]);
- pi->nphy_saved_noisevars.bufcount++;
- }
-
- pi->nphy_noisevars_adjusted = true;
- }
-}
-
-static void wlc_phy_adjust_crsminpwr_nphy(phy_info_t *pi, u8 minpwr)
-{
- u16 regval;
-
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- if ((CHSPEC_CHANNEL(pi->radio_chanspec) == 11) &&
- CHSPEC_IS40(pi->radio_chanspec)) {
- if (!pi->nphy_crsminpwr_adjusted) {
- regval = read_phy_reg(pi, 0x27d);
- pi->nphy_crsminpwr[0] = regval & 0xff;
- regval &= 0xff00;
- regval |= (u16) minpwr;
- write_phy_reg(pi, 0x27d, regval);
-
- regval = read_phy_reg(pi, 0x280);
- pi->nphy_crsminpwr[1] = regval & 0xff;
- regval &= 0xff00;
- regval |= (u16) minpwr;
- write_phy_reg(pi, 0x280, regval);
-
- regval = read_phy_reg(pi, 0x283);
- pi->nphy_crsminpwr[2] = regval & 0xff;
- regval &= 0xff00;
- regval |= (u16) minpwr;
- write_phy_reg(pi, 0x283, regval);
-
- pi->nphy_crsminpwr_adjusted = true;
- }
- } else {
- if (pi->nphy_crsminpwr_adjusted) {
- regval = read_phy_reg(pi, 0x27d);
- regval &= 0xff00;
- regval |= pi->nphy_crsminpwr[0];
- write_phy_reg(pi, 0x27d, regval);
-
- regval = read_phy_reg(pi, 0x280);
- regval &= 0xff00;
- regval |= pi->nphy_crsminpwr[1];
- write_phy_reg(pi, 0x280, regval);
-
- regval = read_phy_reg(pi, 0x283);
- regval &= 0xff00;
- regval |= pi->nphy_crsminpwr[2];
- write_phy_reg(pi, 0x283, regval);
-
- pi->nphy_crsminpwr_adjusted = false;
- }
- }
- }
-}
-
-static void wlc_phy_txlpfbw_nphy(phy_info_t *pi)
-{
- u8 tx_lpf_bw = 0;
-
- if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) {
- if (CHSPEC_IS40(pi->radio_chanspec)) {
- tx_lpf_bw = 3;
- } else {
- tx_lpf_bw = 1;
- }
-
- if (PHY_IPA(pi)) {
- if (CHSPEC_IS40(pi->radio_chanspec)) {
- tx_lpf_bw = 5;
- } else {
- tx_lpf_bw = 4;
- }
- }
- write_phy_reg(pi, 0xe8,
- (tx_lpf_bw << 0) |
- (tx_lpf_bw << 3) |
- (tx_lpf_bw << 6) | (tx_lpf_bw << 9));
-
- if (PHY_IPA(pi)) {
-
- if (CHSPEC_IS40(pi->radio_chanspec)) {
- tx_lpf_bw = 4;
- } else {
- tx_lpf_bw = 1;
- }
-
- write_phy_reg(pi, 0xe9,
- (tx_lpf_bw << 0) |
- (tx_lpf_bw << 3) |
- (tx_lpf_bw << 6) | (tx_lpf_bw << 9));
- }
- }
-}
-
-static void wlc_phy_spurwar_nphy(phy_info_t *pi)
-{
- u16 cur_channel = 0;
- int nphy_adj_tone_id_buf[] = { 57, 58 };
- u32 nphy_adj_noise_var_buf[] = { 0x3ff, 0x3ff };
- bool isAdjustNoiseVar = false;
- uint numTonesAdjust = 0;
- u32 tempval = 0;
-
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, true);
-
- cur_channel = CHSPEC_CHANNEL(pi->radio_chanspec);
-
- if (pi->nphy_gband_spurwar_en) {
-
- wlc_phy_adjust_rx_analpfbw_nphy(pi,
- NPHY_ANARXLPFBW_REDUCTIONFACT);
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if ((cur_channel == 11)
- && CHSPEC_IS40(pi->radio_chanspec)) {
-
- wlc_phy_adjust_min_noisevar_nphy(pi, 2,
- nphy_adj_tone_id_buf,
- nphy_adj_noise_var_buf);
- } else {
-
- wlc_phy_adjust_min_noisevar_nphy(pi, 0,
- NULL,
- NULL);
- }
- }
- wlc_phy_adjust_crsminpwr_nphy(pi,
- NPHY_ADJUSTED_MINCRSPOWER);
- }
-
- if ((pi->nphy_gband_spurwar2_en)
- && CHSPEC_IS2G(pi->radio_chanspec)) {
-
- if (CHSPEC_IS40(pi->radio_chanspec)) {
- switch (cur_channel) {
- case 3:
- nphy_adj_tone_id_buf[0] = 57;
- nphy_adj_tone_id_buf[1] = 58;
- nphy_adj_noise_var_buf[0] = 0x22f;
- nphy_adj_noise_var_buf[1] = 0x25f;
- isAdjustNoiseVar = true;
- break;
- case 4:
- nphy_adj_tone_id_buf[0] = 41;
- nphy_adj_tone_id_buf[1] = 42;
- nphy_adj_noise_var_buf[0] = 0x22f;
- nphy_adj_noise_var_buf[1] = 0x25f;
- isAdjustNoiseVar = true;
- break;
- case 5:
- nphy_adj_tone_id_buf[0] = 25;
- nphy_adj_tone_id_buf[1] = 26;
- nphy_adj_noise_var_buf[0] = 0x24f;
- nphy_adj_noise_var_buf[1] = 0x25f;
- isAdjustNoiseVar = true;
- break;
- case 6:
- nphy_adj_tone_id_buf[0] = 9;
- nphy_adj_tone_id_buf[1] = 10;
- nphy_adj_noise_var_buf[0] = 0x22f;
- nphy_adj_noise_var_buf[1] = 0x24f;
- isAdjustNoiseVar = true;
- break;
- case 7:
- nphy_adj_tone_id_buf[0] = 121;
- nphy_adj_tone_id_buf[1] = 122;
- nphy_adj_noise_var_buf[0] = 0x18f;
- nphy_adj_noise_var_buf[1] = 0x24f;
- isAdjustNoiseVar = true;
- break;
- case 8:
- nphy_adj_tone_id_buf[0] = 105;
- nphy_adj_tone_id_buf[1] = 106;
- nphy_adj_noise_var_buf[0] = 0x22f;
- nphy_adj_noise_var_buf[1] = 0x25f;
- isAdjustNoiseVar = true;
- break;
- case 9:
- nphy_adj_tone_id_buf[0] = 89;
- nphy_adj_tone_id_buf[1] = 90;
- nphy_adj_noise_var_buf[0] = 0x22f;
- nphy_adj_noise_var_buf[1] = 0x24f;
- isAdjustNoiseVar = true;
- break;
- case 10:
- nphy_adj_tone_id_buf[0] = 73;
- nphy_adj_tone_id_buf[1] = 74;
- nphy_adj_noise_var_buf[0] = 0x22f;
- nphy_adj_noise_var_buf[1] = 0x24f;
- isAdjustNoiseVar = true;
- break;
- default:
- isAdjustNoiseVar = false;
- break;
- }
- }
-
- if (isAdjustNoiseVar) {
- numTonesAdjust = sizeof(nphy_adj_tone_id_buf) /
- sizeof(nphy_adj_tone_id_buf[0]);
-
- wlc_phy_adjust_min_noisevar_nphy(pi,
- numTonesAdjust,
- nphy_adj_tone_id_buf,
- nphy_adj_noise_var_buf);
-
- tempval = 0;
-
- } else {
-
- wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL,
- NULL);
- }
- }
-
- if ((pi->nphy_aband_spurwar_en) &&
- (CHSPEC_IS5G(pi->radio_chanspec))) {
- switch (cur_channel) {
- case 54:
- nphy_adj_tone_id_buf[0] = 32;
- nphy_adj_noise_var_buf[0] = 0x25f;
- break;
- case 38:
- case 102:
- case 118:
- if ((pi->sh->chip == BCM4716_CHIP_ID) &&
- (pi->sh->chippkg == BCM4717_PKG_ID)) {
- nphy_adj_tone_id_buf[0] = 32;
- nphy_adj_noise_var_buf[0] = 0x21f;
- } else {
- nphy_adj_tone_id_buf[0] = 0;
- nphy_adj_noise_var_buf[0] = 0x0;
- }
- break;
- case 134:
- nphy_adj_tone_id_buf[0] = 32;
- nphy_adj_noise_var_buf[0] = 0x21f;
- break;
- case 151:
- nphy_adj_tone_id_buf[0] = 16;
- nphy_adj_noise_var_buf[0] = 0x23f;
- break;
- case 153:
- case 161:
- nphy_adj_tone_id_buf[0] = 48;
- nphy_adj_noise_var_buf[0] = 0x23f;
- break;
- default:
- nphy_adj_tone_id_buf[0] = 0;
- nphy_adj_noise_var_buf[0] = 0x0;
- break;
- }
-
- if (nphy_adj_tone_id_buf[0]
- && nphy_adj_noise_var_buf[0]) {
- wlc_phy_adjust_min_noisevar_nphy(pi, 1,
- nphy_adj_tone_id_buf,
- nphy_adj_noise_var_buf);
- } else {
- wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL,
- NULL);
- }
- }
-
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, false);
- }
-}
-
static void
-wlc_phy_chanspec_nphy_setup(phy_info_t *pi, chanspec_t chanspec,
- const nphy_sfo_cfg_t *ci)
+wlc_phy_chanspec_nphy_setup(struct brcms_phy *pi, u16 chanspec,
+ const struct nphy_sfo_cfg *ci)
{
u16 val;
@@ -19004,14 +21330,11 @@ wlc_phy_chanspec_nphy_setup(phy_info_t *pi, chanspec_t chanspec,
and_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_TEST, ~0x840);
}
- if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) {
+ if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF)
wlc_phy_txpwr_fixpower_nphy(pi);
- }
-
- if (NREV_LT(pi->pubpi.phy_rev, 3)) {
+ if (NREV_LT(pi->pubpi.phy_rev, 3))
wlc_phy_adjust_lnagaintbl_nphy(pi);
- }
wlc_phy_txlpfbw_nphy(pi);
@@ -19022,53 +21345,31 @@ wlc_phy_chanspec_nphy_setup(phy_info_t *pi, chanspec_t chanspec,
val = CHSPEC_CHANNEL(chanspec);
if (!CHSPEC_IS40(pi->radio_chanspec)) {
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- if ((val == 13) || (val == 14) || (val == 153)) {
- spuravoid = 1;
- }
- } else {
-
- if (((val >= 5) && (val <= 8)) || (val == 13)
- || (val == 14)) {
+ if ((val == 13) || (val == 14) || (val == 153))
spuravoid = 1;
- }
+ } else if (((val >= 5) && (val <= 8)) || (val == 13)
+ || (val == 14)) {
+ spuravoid = 1;
}
+ } else if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (val == 54)
+ spuravoid = 1;
} else {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- if (val == 54) {
- spuravoid = 1;
- }
- } else {
-
- if (pi->nphy_aband_spurwar_en &&
- ((val == 38) || (val == 102)
- || (val == 118))) {
- if ((pi->sh->chip ==
- BCM4716_CHIP_ID)
- && (pi->sh->chippkg ==
- BCM4717_PKG_ID)) {
- spuravoid = 0;
- } else {
- spuravoid = 1;
- }
- }
- }
+ if (pi->nphy_aband_spurwar_en &&
+ ((val == 38) || (val == 102)
+ || (val == 118)))
+ spuravoid = 1;
}
if (pi->phy_spuravoid == SPURAVOID_FORCEON)
spuravoid = 1;
- if ((pi->sh->chip == BCM4716_CHIP_ID) ||
- (pi->sh->chip == BCM47162_CHIP_ID)) {
- si_pmu_spuravoid(pi->sh->sih, spuravoid);
- } else {
- wlapi_bmac_core_phypll_ctl(pi->sh->physhim, false);
- si_pmu_spuravoid(pi->sh->sih, spuravoid);
- wlapi_bmac_core_phypll_ctl(pi->sh->physhim, true);
- }
+ wlapi_bmac_core_phypll_ctl(pi->sh->physhim, false);
+ si_pmu_spuravoid(pi->sh->sih, spuravoid);
+ wlapi_bmac_core_phypll_ctl(pi->sh->physhim, true);
if ((pi->sh->chip == BCM43224_CHIP_ID) ||
- (pi->sh->chip == BCM43225_CHIP_ID) ||
- (pi->sh->chip == BCM43421_CHIP_ID)) {
+ (pi->sh->chip == BCM43225_CHIP_ID)) {
if (spuravoid == 1) {
@@ -19085,10 +21386,7 @@ wlc_phy_chanspec_nphy_setup(phy_info_t *pi, chanspec_t chanspec,
}
}
- if (!((pi->sh->chip == BCM4716_CHIP_ID) ||
- (pi->sh->chip == BCM47162_CHIP_ID))) {
- wlapi_bmac_core_phypll_reset(pi->sh->physhim);
- }
+ wlapi_bmac_core_phypll_reset(pi->sh->physhim);
mod_phy_reg(pi, 0x01, (0x1 << 15),
((spuravoid > 0) ? (0x1 << 15) : 0));
@@ -19104,23 +21402,19 @@ wlc_phy_chanspec_nphy_setup(phy_info_t *pi, chanspec_t chanspec,
wlc_phy_spurwar_nphy(pi);
}
-void wlc_phy_chanspec_set_nphy(phy_info_t *pi, chanspec_t chanspec)
+void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, u16 chanspec)
{
int freq;
- chan_info_nphy_radio2057_t *t0 = NULL;
- chan_info_nphy_radio205x_t *t1 = NULL;
- chan_info_nphy_radio2057_rev5_t *t2 = NULL;
- chan_info_nphy_2055_t *t3 = NULL;
-
- if (NORADIO_ENAB(pi->pubpi)) {
- return;
- }
+ const struct chan_info_nphy_radio2057 *t0 = NULL;
+ const struct chan_info_nphy_radio205x *t1 = NULL;
+ const struct chan_info_nphy_radio2057_rev5 *t2 = NULL;
+ const struct chan_info_nphy_2055 *t3 = NULL;
if (!wlc_phy_chan2freq_nphy
- (pi, CHSPEC_CHANNEL(chanspec), &freq, &t0, &t1, &t2, &t3))
+ (pi, CHSPEC_CHANNEL(chanspec), &freq, &t0, &t1, &t2, &t3))
return;
- wlc_phy_chanspec_radio_set((wlc_phy_t *) pi, chanspec);
+ wlc_phy_chanspec_radio_set((struct brcms_phy_pub *) pi, chanspec);
if (CHSPEC_BW(chanspec) != pi->bw)
wlapi_bmac_bw_set(pi->sh->physhim, CHSPEC_BW(chanspec));
@@ -19128,15 +21422,13 @@ void wlc_phy_chanspec_set_nphy(phy_info_t *pi, chanspec_t chanspec)
if (CHSPEC_IS40(chanspec)) {
if (CHSPEC_SB_UPPER(chanspec)) {
or_phy_reg(pi, 0xa0, BPHY_BAND_SEL_UP20);
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
or_phy_reg(pi, 0x310, PRIM_SEL_UP20);
- }
} else {
and_phy_reg(pi, 0xa0, ~BPHY_BAND_SEL_UP20);
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
and_phy_reg(pi, 0x310,
(~PRIM_SEL_UP20 & 0xffff));
- }
}
}
@@ -19157,12 +21449,9 @@ void wlc_phy_chanspec_set_nphy(phy_info_t *pi, chanspec_t chanspec)
wlc_phy_chanspec_radio2057_setup(pi, t0, t2);
wlc_phy_chanspec_nphy_setup(pi, chanspec,
- (pi->pubpi.radiorev ==
- 5) ? (const nphy_sfo_cfg_t
- *)&(t2->
- PHY_BW1a)
- : (const nphy_sfo_cfg_t *)
- &(t0->PHY_BW1a));
+ (pi->pubpi.radiorev == 5) ?
+ (const struct nphy_sfo_cfg *)&(t2->PHY_BW1a) :
+ (const struct nphy_sfo_cfg *)&(t0->PHY_BW1a));
} else {
@@ -19173,8 +21462,7 @@ void wlc_phy_chanspec_set_nphy(phy_info_t *pi, chanspec_t chanspec)
wlc_phy_chanspec_radio2056_setup(pi, t1);
wlc_phy_chanspec_nphy_setup(pi, chanspec,
- (const nphy_sfo_cfg_t *)
- &(t1->PHY_BW1a));
+ (const struct nphy_sfo_cfg *) &(t1->PHY_BW1a));
}
} else {
@@ -19185,363 +21473,15 @@ void wlc_phy_chanspec_set_nphy(phy_info_t *pi, chanspec_t chanspec)
wlc_phy_chanspec_radio2055_setup(pi, t3);
wlc_phy_chanspec_nphy_setup(pi, chanspec,
- (const nphy_sfo_cfg_t *)&(t3->
- PHY_BW1a));
- }
-
-}
-
-static void wlc_phy_savecal_nphy(phy_info_t *pi)
-{
- void *tbl_ptr;
- int coreNum;
- u16 *txcal_radio_regs = NULL;
-
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, true);
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
-
- wlc_phy_rx_iq_coeffs_nphy(pi, 0,
- &pi->calibration_cache.
- rxcal_coeffs_2G);
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- txcal_radio_regs =
- pi->calibration_cache.txcal_radio_regs_2G;
- } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
-
- pi->calibration_cache.txcal_radio_regs_2G[0] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_I |
- RADIO_2056_TX0);
- pi->calibration_cache.txcal_radio_regs_2G[1] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_Q |
- RADIO_2056_TX0);
- pi->calibration_cache.txcal_radio_regs_2G[2] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_I |
- RADIO_2056_TX1);
- pi->calibration_cache.txcal_radio_regs_2G[3] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_Q |
- RADIO_2056_TX1);
-
- pi->calibration_cache.txcal_radio_regs_2G[4] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_I |
- RADIO_2056_TX0);
- pi->calibration_cache.txcal_radio_regs_2G[5] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_Q |
- RADIO_2056_TX0);
- pi->calibration_cache.txcal_radio_regs_2G[6] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_I |
- RADIO_2056_TX1);
- pi->calibration_cache.txcal_radio_regs_2G[7] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_Q |
- RADIO_2056_TX1);
- } else {
- pi->calibration_cache.txcal_radio_regs_2G[0] =
- read_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL);
- pi->calibration_cache.txcal_radio_regs_2G[1] =
- read_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL);
- pi->calibration_cache.txcal_radio_regs_2G[2] =
- read_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM);
- pi->calibration_cache.txcal_radio_regs_2G[3] =
- read_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM);
- }
-
- pi->nphy_iqcal_chanspec_2G = pi->radio_chanspec;
- tbl_ptr = pi->calibration_cache.txcal_coeffs_2G;
- } else {
-
- wlc_phy_rx_iq_coeffs_nphy(pi, 0,
- &pi->calibration_cache.
- rxcal_coeffs_5G);
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- txcal_radio_regs =
- pi->calibration_cache.txcal_radio_regs_5G;
- } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
-
- pi->calibration_cache.txcal_radio_regs_5G[0] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_I |
- RADIO_2056_TX0);
- pi->calibration_cache.txcal_radio_regs_5G[1] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_Q |
- RADIO_2056_TX0);
- pi->calibration_cache.txcal_radio_regs_5G[2] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_I |
- RADIO_2056_TX1);
- pi->calibration_cache.txcal_radio_regs_5G[3] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_Q |
- RADIO_2056_TX1);
-
- pi->calibration_cache.txcal_radio_regs_5G[4] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_I |
- RADIO_2056_TX0);
- pi->calibration_cache.txcal_radio_regs_5G[5] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_Q |
- RADIO_2056_TX0);
- pi->calibration_cache.txcal_radio_regs_5G[6] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_I |
- RADIO_2056_TX1);
- pi->calibration_cache.txcal_radio_regs_5G[7] =
- read_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_Q |
- RADIO_2056_TX1);
- } else {
- pi->calibration_cache.txcal_radio_regs_5G[0] =
- read_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL);
- pi->calibration_cache.txcal_radio_regs_5G[1] =
- read_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL);
- pi->calibration_cache.txcal_radio_regs_5G[2] =
- read_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM);
- pi->calibration_cache.txcal_radio_regs_5G[3] =
- read_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM);
- }
-
- pi->nphy_iqcal_chanspec_5G = pi->radio_chanspec;
- tbl_ptr = pi->calibration_cache.txcal_coeffs_5G;
- }
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- for (coreNum = 0; coreNum <= 1; coreNum++) {
-
- txcal_radio_regs[2 * coreNum] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
- LOFT_FINE_I);
- txcal_radio_regs[2 * coreNum + 1] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
- LOFT_FINE_Q);
-
- txcal_radio_regs[2 * coreNum + 4] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
- LOFT_COARSE_I);
- txcal_radio_regs[2 * coreNum + 5] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
- LOFT_COARSE_Q);
- }
- }
-
- wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 8, 80, 16, tbl_ptr);
-
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, false);
-}
-
-static void wlc_phy_restorecal_nphy(phy_info_t *pi)
-{
- u16 *loft_comp;
- u16 txcal_coeffs_bphy[4];
- u16 *tbl_ptr;
- int coreNum;
- u16 *txcal_radio_regs = NULL;
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if (pi->nphy_iqcal_chanspec_2G == 0)
- return;
-
- tbl_ptr = pi->calibration_cache.txcal_coeffs_2G;
- loft_comp = &pi->calibration_cache.txcal_coeffs_2G[5];
- } else {
- if (pi->nphy_iqcal_chanspec_5G == 0)
- return;
-
- tbl_ptr = pi->calibration_cache.txcal_coeffs_5G;
- loft_comp = &pi->calibration_cache.txcal_coeffs_5G[5];
- }
-
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, 16,
- (void *)tbl_ptr);
-
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- txcal_coeffs_bphy[0] = tbl_ptr[0];
- txcal_coeffs_bphy[1] = tbl_ptr[1];
- txcal_coeffs_bphy[2] = tbl_ptr[2];
- txcal_coeffs_bphy[3] = tbl_ptr[3];
- } else {
- txcal_coeffs_bphy[0] = 0;
- txcal_coeffs_bphy[1] = 0;
- txcal_coeffs_bphy[2] = 0;
- txcal_coeffs_bphy[3] = 0;
- }
-
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, 16,
- txcal_coeffs_bphy);
-
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, 16, loft_comp);
-
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, 16, loft_comp);
-
- if (NREV_LT(pi->pubpi.phy_rev, 2))
- wlc_phy_tx_iq_war_nphy(pi);
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- txcal_radio_regs =
- pi->calibration_cache.txcal_radio_regs_2G;
- } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
-
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_I |
- RADIO_2056_TX0,
- pi->calibration_cache.
- txcal_radio_regs_2G[0]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_Q |
- RADIO_2056_TX0,
- pi->calibration_cache.
- txcal_radio_regs_2G[1]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_I |
- RADIO_2056_TX1,
- pi->calibration_cache.
- txcal_radio_regs_2G[2]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_Q |
- RADIO_2056_TX1,
- pi->calibration_cache.
- txcal_radio_regs_2G[3]);
-
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_I |
- RADIO_2056_TX0,
- pi->calibration_cache.
- txcal_radio_regs_2G[4]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_Q |
- RADIO_2056_TX0,
- pi->calibration_cache.
- txcal_radio_regs_2G[5]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_I |
- RADIO_2056_TX1,
- pi->calibration_cache.
- txcal_radio_regs_2G[6]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_Q |
- RADIO_2056_TX1,
- pi->calibration_cache.
- txcal_radio_regs_2G[7]);
- } else {
- write_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL,
- pi->calibration_cache.
- txcal_radio_regs_2G[0]);
- write_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL,
- pi->calibration_cache.
- txcal_radio_regs_2G[1]);
- write_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM,
- pi->calibration_cache.
- txcal_radio_regs_2G[2]);
- write_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM,
- pi->calibration_cache.
- txcal_radio_regs_2G[3]);
- }
-
- wlc_phy_rx_iq_coeffs_nphy(pi, 1,
- &pi->calibration_cache.
- rxcal_coeffs_2G);
- } else {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- txcal_radio_regs =
- pi->calibration_cache.txcal_radio_regs_5G;
- } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
-
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_I |
- RADIO_2056_TX0,
- pi->calibration_cache.
- txcal_radio_regs_5G[0]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_Q |
- RADIO_2056_TX0,
- pi->calibration_cache.
- txcal_radio_regs_5G[1]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_I |
- RADIO_2056_TX1,
- pi->calibration_cache.
- txcal_radio_regs_5G[2]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_FINE_Q |
- RADIO_2056_TX1,
- pi->calibration_cache.
- txcal_radio_regs_5G[3]);
-
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_I |
- RADIO_2056_TX0,
- pi->calibration_cache.
- txcal_radio_regs_5G[4]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_Q |
- RADIO_2056_TX0,
- pi->calibration_cache.
- txcal_radio_regs_5G[5]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_I |
- RADIO_2056_TX1,
- pi->calibration_cache.
- txcal_radio_regs_5G[6]);
- write_radio_reg(pi,
- RADIO_2056_TX_LOFT_COARSE_Q |
- RADIO_2056_TX1,
- pi->calibration_cache.
- txcal_radio_regs_5G[7]);
- } else {
- write_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL,
- pi->calibration_cache.
- txcal_radio_regs_5G[0]);
- write_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL,
- pi->calibration_cache.
- txcal_radio_regs_5G[1]);
- write_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM,
- pi->calibration_cache.
- txcal_radio_regs_5G[2]);
- write_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM,
- pi->calibration_cache.
- txcal_radio_regs_5G[3]);
- }
-
- wlc_phy_rx_iq_coeffs_nphy(pi, 1,
- &pi->calibration_cache.
- rxcal_coeffs_5G);
+ (const struct nphy_sfo_cfg *)
+ &(t3->PHY_BW1a));
}
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- for (coreNum = 0; coreNum <= 1; coreNum++) {
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
- LOFT_FINE_I,
- txcal_radio_regs[2 * coreNum]);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
- LOFT_FINE_Q,
- txcal_radio_regs[2 * coreNum + 1]);
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
- LOFT_COARSE_I,
- txcal_radio_regs[2 * coreNum + 4]);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum,
- LOFT_COARSE_Q,
- txcal_radio_regs[2 * coreNum + 5]);
- }
- }
}
-void wlc_phy_antsel_init(wlc_phy_t *ppi, bool lut_init)
+void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init)
{
- phy_info_t *pi = (phy_info_t *) ppi;
+ struct brcms_phy *pi = (struct brcms_phy *) ppi;
u16 mask = 0xfc00;
u32 mc = 0;
@@ -19599,15 +21539,15 @@ void wlc_phy_antsel_init(wlc_phy_t *ppi, bool lut_init)
}
}
-u16 wlc_phy_classifier_nphy(phy_info_t *pi, u16 mask, u16 val)
+u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val)
{
u16 curr_ctl, new_ctl;
bool suspended = false;
if (D11REV_IS(pi->sh->corerev, 16)) {
suspended =
- (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC) ?
- false : true;
+ (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC) ?
+ false : true;
if (!suspended)
wlapi_suspend_mac_and_wait(pi->sh->physhim);
}
@@ -19624,19 +21564,7 @@ u16 wlc_phy_classifier_nphy(phy_info_t *pi, u16 mask, u16 val)
return new_ctl;
}
-static void wlc_phy_clip_det_nphy(phy_info_t *pi, u8 write, u16 *vals)
-{
-
- if (write == 0) {
- vals[0] = read_phy_reg(pi, 0x2c);
- vals[1] = read_phy_reg(pi, 0x42);
- } else {
- write_phy_reg(pi, 0x2c, vals[0]);
- write_phy_reg(pi, 0x42, vals[1]);
- }
-}
-
-void wlc_phy_force_rfseq_nphy(phy_info_t *pi, u8 cmd)
+void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd)
{
u16 trigger_mask, status_mask;
u16 orig_RfseqCoreActv;
@@ -19681,578 +21609,7 @@ void wlc_phy_force_rfseq_nphy(phy_info_t *pi, u8 cmd)
}
static void
-wlc_phy_set_rfseq_nphy(phy_info_t *pi, u8 cmd, u8 *events, u8 *dlys,
- u8 len)
-{
- u32 t1_offset, t2_offset;
- u8 ctr;
- u8 end_event =
- NREV_GE(pi->pubpi.phy_rev,
- 3) ? NPHY_REV3_RFSEQ_CMD_END : NPHY_RFSEQ_CMD_END;
- u8 end_dly = 1;
-
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, true);
-
- t1_offset = cmd << 4;
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, len, t1_offset, 8,
- events);
- t2_offset = t1_offset + 0x080;
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, len, t2_offset, 8,
- dlys);
-
- for (ctr = len; ctr < 16; ctr++) {
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1,
- t1_offset + ctr, 8, &end_event);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1,
- t2_offset + ctr, 8, &end_dly);
- }
-
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, false);
-}
-
-static u16 wlc_phy_read_lpf_bw_ctl_nphy(phy_info_t *pi, u16 offset)
-{
- u16 lpf_bw_ctl_val = 0;
- u16 rx2tx_lpf_rc_lut_offset = 0;
-
- if (offset == 0) {
- if (CHSPEC_IS40(pi->radio_chanspec)) {
- rx2tx_lpf_rc_lut_offset = 0x159;
- } else {
- rx2tx_lpf_rc_lut_offset = 0x154;
- }
- } else {
- rx2tx_lpf_rc_lut_offset = offset;
- }
- wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 1,
- (u32) rx2tx_lpf_rc_lut_offset, 16,
- &lpf_bw_ctl_val);
-
- lpf_bw_ctl_val = lpf_bw_ctl_val & 0x7;
-
- return lpf_bw_ctl_val;
-}
-
-static void
-wlc_phy_rfctrl_override_nphy_rev7(phy_info_t *pi, u16 field, u16 value,
- u8 core_mask, u8 off, u8 override_id)
-{
- u8 core_num;
- u16 addr = 0, en_addr = 0, val_addr = 0, en_mask = 0, val_mask = 0;
- u8 val_shift = 0;
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- en_mask = field;
- for (core_num = 0; core_num < 2; core_num++) {
- if (override_id == NPHY_REV7_RFCTRLOVERRIDE_ID0) {
-
- switch (field) {
- case (0x1 << 2):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a :
- 0x7d;
- val_mask = (0x1 << 1);
- val_shift = 1;
- break;
- case (0x1 << 3):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a :
- 0x7d;
- val_mask = (0x1 << 2);
- val_shift = 2;
- break;
- case (0x1 << 4):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a :
- 0x7d;
- val_mask = (0x1 << 4);
- val_shift = 4;
- break;
- case (0x1 << 5):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a :
- 0x7d;
- val_mask = (0x1 << 5);
- val_shift = 5;
- break;
- case (0x1 << 6):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a :
- 0x7d;
- val_mask = (0x1 << 6);
- val_shift = 6;
- break;
- case (0x1 << 7):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a :
- 0x7d;
- val_mask = (0x1 << 7);
- val_shift = 7;
- break;
- case (0x1 << 10):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0xf8 :
- 0xfa;
- val_mask = (0x7 << 4);
- val_shift = 4;
- break;
- case (0x1 << 11):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7b :
- 0x7e;
- val_mask = (0xffff << 0);
- val_shift = 0;
- break;
- case (0x1 << 12):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7c :
- 0x7f;
- val_mask = (0xffff << 0);
- val_shift = 0;
- break;
- case (0x3 << 13):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x348 :
- 0x349;
- val_mask = (0xff << 0);
- val_shift = 0;
- break;
- case (0x1 << 13):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x348 :
- 0x349;
- val_mask = (0xf << 0);
- val_shift = 0;
- break;
- default:
- addr = 0xffff;
- break;
- }
- } else if (override_id == NPHY_REV7_RFCTRLOVERRIDE_ID1) {
-
- switch (field) {
- case (0x1 << 1):
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 1);
- val_shift = 1;
- break;
- case (0x1 << 3):
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 3);
- val_shift = 3;
- break;
- case (0x1 << 5):
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 5);
- val_shift = 5;
- break;
- case (0x1 << 4):
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 4);
- val_shift = 4;
- break;
- case (0x1 << 2):
-
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 2);
- val_shift = 2;
- break;
- case (0x1 << 7):
-
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x7 << 8);
- val_shift = 8;
- break;
- case (0x1 << 11):
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 14);
- val_shift = 14;
- break;
- case (0x1 << 10):
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 13);
- val_shift = 13;
- break;
- case (0x1 << 9):
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 12);
- val_shift = 12;
- break;
- case (0x1 << 8):
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 11);
- val_shift = 11;
- break;
- case (0x1 << 6):
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 6);
- val_shift = 6;
- break;
- case (0x1 << 0):
- en_addr = (core_num == 0) ? 0x342 :
- 0x343;
- val_addr = (core_num == 0) ? 0x340 :
- 0x341;
- val_mask = (0x1 << 0);
- val_shift = 0;
- break;
- default:
- addr = 0xffff;
- break;
- }
- } else if (override_id == NPHY_REV7_RFCTRLOVERRIDE_ID2) {
-
- switch (field) {
- case (0x1 << 3):
- en_addr = (core_num == 0) ? 0x346 :
- 0x347;
- val_addr = (core_num == 0) ? 0x344 :
- 0x345;
- val_mask = (0x1 << 3);
- val_shift = 3;
- break;
- case (0x1 << 1):
- en_addr = (core_num == 0) ? 0x346 :
- 0x347;
- val_addr = (core_num == 0) ? 0x344 :
- 0x345;
- val_mask = (0x1 << 1);
- val_shift = 1;
- break;
- case (0x1 << 0):
- en_addr = (core_num == 0) ? 0x346 :
- 0x347;
- val_addr = (core_num == 0) ? 0x344 :
- 0x345;
- val_mask = (0x1 << 0);
- val_shift = 0;
- break;
- case (0x1 << 2):
- en_addr = (core_num == 0) ? 0x346 :
- 0x347;
- val_addr = (core_num == 0) ? 0x344 :
- 0x345;
- val_mask = (0x1 << 2);
- val_shift = 2;
- break;
- case (0x1 << 4):
- en_addr = (core_num == 0) ? 0x346 :
- 0x347;
- val_addr = (core_num == 0) ? 0x344 :
- 0x345;
- val_mask = (0x1 << 4);
- val_shift = 4;
- break;
- default:
- addr = 0xffff;
- break;
- }
- }
-
- if (off) {
- and_phy_reg(pi, en_addr, ~en_mask);
- and_phy_reg(pi, val_addr, ~val_mask);
- } else {
-
- if ((core_mask == 0)
- || (core_mask & (1 << core_num))) {
- or_phy_reg(pi, en_addr, en_mask);
-
- if (addr != 0xffff) {
- mod_phy_reg(pi, val_addr,
- val_mask,
- (value <<
- val_shift));
- }
- }
- }
- }
- }
-}
-
-static void
-wlc_phy_rfctrl_override_nphy(phy_info_t *pi, u16 field, u16 value,
- u8 core_mask, u8 off)
-{
- u8 core_num;
- u16 addr = 0, mask = 0, en_addr = 0, val_addr = 0, en_mask =
- 0, val_mask = 0;
- u8 shift = 0, val_shift = 0;
-
- if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) {
-
- en_mask = field;
- for (core_num = 0; core_num < 2; core_num++) {
-
- switch (field) {
- case (0x1 << 1):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a : 0x7d;
- val_mask = (0x1 << 0);
- val_shift = 0;
- break;
- case (0x1 << 2):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a : 0x7d;
- val_mask = (0x1 << 1);
- val_shift = 1;
- break;
- case (0x1 << 3):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a : 0x7d;
- val_mask = (0x1 << 2);
- val_shift = 2;
- break;
- case (0x1 << 4):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a : 0x7d;
- val_mask = (0x1 << 4);
- val_shift = 4;
- break;
- case (0x1 << 5):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a : 0x7d;
- val_mask = (0x1 << 5);
- val_shift = 5;
- break;
- case (0x1 << 6):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a : 0x7d;
- val_mask = (0x1 << 6);
- val_shift = 6;
- break;
- case (0x1 << 7):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a : 0x7d;
- val_mask = (0x1 << 7);
- val_shift = 7;
- break;
- case (0x1 << 8):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a : 0x7d;
- val_mask = (0x7 << 8);
- val_shift = 8;
- break;
- case (0x1 << 11):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7a : 0x7d;
- val_mask = (0x7 << 13);
- val_shift = 13;
- break;
-
- case (0x1 << 9):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0xf8 : 0xfa;
- val_mask = (0x7 << 0);
- val_shift = 0;
- break;
-
- case (0x1 << 10):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0xf8 : 0xfa;
- val_mask = (0x7 << 4);
- val_shift = 4;
- break;
-
- case (0x1 << 12):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7b : 0x7e;
- val_mask = (0xffff << 0);
- val_shift = 0;
- break;
- case (0x1 << 13):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0x7c : 0x7f;
- val_mask = (0xffff << 0);
- val_shift = 0;
- break;
- case (0x1 << 14):
- en_addr = (core_num == 0) ? 0xe7 : 0xec;
- val_addr = (core_num == 0) ? 0xf9 : 0xfb;
- val_mask = (0x3 << 6);
- val_shift = 6;
- break;
- case (0x1 << 0):
- en_addr = (core_num == 0) ? 0xe5 : 0xe6;
- val_addr = (core_num == 0) ? 0xf9 : 0xfb;
- val_mask = (0x1 << 15);
- val_shift = 15;
- break;
- default:
- addr = 0xffff;
- break;
- }
-
- if (off) {
- and_phy_reg(pi, en_addr, ~en_mask);
- and_phy_reg(pi, val_addr, ~val_mask);
- } else {
-
- if ((core_mask == 0)
- || (core_mask & (1 << core_num))) {
- or_phy_reg(pi, en_addr, en_mask);
-
- if (addr != 0xffff) {
- mod_phy_reg(pi, val_addr,
- val_mask,
- (value <<
- val_shift));
- }
- }
- }
- }
- } else {
-
- if (off) {
- and_phy_reg(pi, 0xec, ~field);
- value = 0x0;
- } else {
- or_phy_reg(pi, 0xec, field);
- }
-
- for (core_num = 0; core_num < 2; core_num++) {
-
- switch (field) {
- case (0x1 << 1):
- case (0x1 << 9):
- case (0x1 << 12):
- case (0x1 << 13):
- case (0x1 << 14):
- addr = 0x78;
-
- core_mask = 0x1;
- break;
- case (0x1 << 2):
- case (0x1 << 3):
- case (0x1 << 4):
- case (0x1 << 5):
- case (0x1 << 6):
- case (0x1 << 7):
- case (0x1 << 8):
- addr = (core_num == 0) ? 0x7a : 0x7d;
- break;
- case (0x1 << 10):
- addr = (core_num == 0) ? 0x7b : 0x7e;
- break;
- case (0x1 << 11):
- addr = (core_num == 0) ? 0x7c : 0x7f;
- break;
- default:
- addr = 0xffff;
- }
-
- switch (field) {
- case (0x1 << 1):
- mask = (0x7 << 3);
- shift = 3;
- break;
- case (0x1 << 9):
- mask = (0x1 << 2);
- shift = 2;
- break;
- case (0x1 << 12):
- mask = (0x1 << 8);
- shift = 8;
- break;
- case (0x1 << 13):
- mask = (0x1 << 9);
- shift = 9;
- break;
- case (0x1 << 14):
- mask = (0xf << 12);
- shift = 12;
- break;
- case (0x1 << 2):
- mask = (0x1 << 0);
- shift = 0;
- break;
- case (0x1 << 3):
- mask = (0x1 << 1);
- shift = 1;
- break;
- case (0x1 << 4):
- mask = (0x1 << 2);
- shift = 2;
- break;
- case (0x1 << 5):
- mask = (0x3 << 4);
- shift = 4;
- break;
- case (0x1 << 6):
- mask = (0x3 << 6);
- shift = 6;
- break;
- case (0x1 << 7):
- mask = (0x1 << 8);
- shift = 8;
- break;
- case (0x1 << 8):
- mask = (0x1 << 9);
- shift = 9;
- break;
- case (0x1 << 10):
- mask = 0x1fff;
- shift = 0x0;
- break;
- case (0x1 << 11):
- mask = 0x1fff;
- shift = 0x0;
- break;
- default:
- mask = 0x0;
- shift = 0x0;
- break;
- }
-
- if ((addr != 0xffff) && (core_mask & (1 << core_num))) {
- mod_phy_reg(pi, addr, mask, (value << shift));
- }
- }
-
- or_phy_reg(pi, 0xec, (0x1 << 0));
- or_phy_reg(pi, 0x78, (0x1 << 0));
- udelay(1);
- and_phy_reg(pi, 0xec, ~(0x1 << 0));
- }
-}
-
-static void
-wlc_phy_rfctrl_override_1tomany_nphy(phy_info_t *pi, u16 cmd, u16 value,
+wlc_phy_rfctrl_override_1tomany_nphy(struct brcms_phy *pi, u16 cmd, u16 value,
u8 core_mask, u8 off)
{
u16 rfmxgain = 0, lpfgain = 0;
@@ -20262,223 +21619,267 @@ wlc_phy_rfctrl_override_1tomany_nphy(phy_info_t *pi, u16 cmd, u16 value,
switch (cmd) {
case NPHY_REV7_RfctrlOverride_cmd_rxrf_pu:
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5),
- value, core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), value,
- core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), value,
- core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 5),
+ value, core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 4), value,
+ core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 3), value,
+ core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
break;
case NPHY_REV7_RfctrlOverride_cmd_rx_pu:
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2),
- value, core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), value,
- core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), value,
- core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), value,
- core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID2);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 0,
- core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 2),
+ value, core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 1), value,
+ core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 0), value,
+ core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 1), value,
+ core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID2);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 11), 0,
+ core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
break;
case NPHY_REV7_RfctrlOverride_cmd_tx_pu:
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2),
- value, core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), value,
- core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), value,
- core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID2);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), value,
- core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID2);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 1,
- core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 2),
+ value, core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 1), value,
+ core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 0), value,
+ core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID2);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 2), value,
+ core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID2);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 11), 1,
+ core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
break;
case NPHY_REV7_RfctrlOverride_cmd_rxgain:
rfmxgain = value & 0x000ff;
lpfgain = value & 0x0ff00;
lpfgain = lpfgain >> 8;
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11),
- rfmxgain, core_mask,
- off,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x3 << 13),
- lpfgain, core_mask,
- off,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 11),
+ rfmxgain, core_mask,
+ off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x3 << 13),
+ lpfgain, core_mask,
+ off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
break;
case NPHY_REV7_RfctrlOverride_cmd_txgain:
tgain = value & 0x7fff;
lpfgain = value & 0x8000;
lpfgain = lpfgain >> 14;
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12),
- tgain, core_mask, off,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 13),
- lpfgain, core_mask,
- off,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 12),
+ tgain, core_mask, off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 13),
+ lpfgain, core_mask,
+ off,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
break;
}
}
}
static void
-wlc_phy_scale_offset_rssi_nphy(phy_info_t *pi, u16 scale, s8 offset,
+wlc_phy_scale_offset_rssi_nphy(struct brcms_phy *pi, u16 scale, s8 offset,
u8 coresel, u8 rail, u8 rssi_type)
{
u16 valuetostuff;
offset = (offset > NPHY_RSSICAL_MAXREAD) ?
- NPHY_RSSICAL_MAXREAD : offset;
+ NPHY_RSSICAL_MAXREAD : offset;
offset = (offset < (-NPHY_RSSICAL_MAXREAD - 1)) ?
- -NPHY_RSSICAL_MAXREAD - 1 : offset;
+ -NPHY_RSSICAL_MAXREAD - 1 : offset;
valuetostuff = ((scale & 0x3f) << 8) | (offset & 0x3f);
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_NB)) {
+ (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_NB))
write_phy_reg(pi, 0x1a6, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_NB)) {
+ (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_NB))
write_phy_reg(pi, 0x1ac, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_NB)) {
+ (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_NB))
write_phy_reg(pi, 0x1b2, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_NB)) {
+ (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_NB))
write_phy_reg(pi, 0x1b8, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W1)) {
+ (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W1))
write_phy_reg(pi, 0x1a4, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W1)) {
+ (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W1))
write_phy_reg(pi, 0x1aa, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W1)) {
+ (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W1))
write_phy_reg(pi, 0x1b0, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W1)) {
+ (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W1))
write_phy_reg(pi, 0x1b6, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W2)) {
+ (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W2))
write_phy_reg(pi, 0x1a5, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W2)) {
+ (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W2))
write_phy_reg(pi, 0x1ab, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W2)) {
+ (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W2))
write_phy_reg(pi, 0x1b1, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W2)) {
+ (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W2))
write_phy_reg(pi, 0x1b7, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_TBD)) {
+ (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_TBD))
write_phy_reg(pi, 0x1a7, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_TBD)) {
+ (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_TBD))
write_phy_reg(pi, 0x1ad, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_TBD)) {
+ (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_TBD))
write_phy_reg(pi, 0x1b3, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_TBD)) {
+ (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_TBD))
write_phy_reg(pi, 0x1b9, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_IQ)) {
+ (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_IQ))
write_phy_reg(pi, 0x1a8, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_IQ)) {
+ (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_IQ))
write_phy_reg(pi, 0x1ae, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_IQ)) {
+ (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_IQ))
write_phy_reg(pi, 0x1b4, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_IQ)) {
+ (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_IQ))
write_phy_reg(pi, 0x1ba, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rssi_type == NPHY_RSSI_SEL_TSSI_2G)) {
+ (rssi_type == NPHY_RSSI_SEL_TSSI_2G))
write_phy_reg(pi, 0x1a9, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rssi_type == NPHY_RSSI_SEL_TSSI_2G)) {
+ (rssi_type == NPHY_RSSI_SEL_TSSI_2G))
write_phy_reg(pi, 0x1b5, valuetostuff);
- }
if (((coresel == RADIO_MIMO_CORESEL_CORE1) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rssi_type == NPHY_RSSI_SEL_TSSI_5G)) {
+ (rssi_type == NPHY_RSSI_SEL_TSSI_5G))
write_phy_reg(pi, 0x1af, valuetostuff);
- }
+
if (((coresel == RADIO_MIMO_CORESEL_CORE2) ||
(coresel == RADIO_MIMO_CORESEL_ALLRX)) &&
- (rssi_type == NPHY_RSSI_SEL_TSSI_5G)) {
+ (rssi_type == NPHY_RSSI_SEL_TSSI_5G))
write_phy_reg(pi, 0x1bb, valuetostuff);
+}
+
+static void brcms_phy_wr_tx_mux(struct brcms_phy *pi, u8 core)
+{
+ if (PHY_IPA(pi)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ write_radio_reg(pi,
+ ((core == PHY_CORE_0) ?
+ RADIO_2057_TX0_TX_SSI_MUX :
+ RADIO_2057_TX1_TX_SSI_MUX),
+ (CHSPEC_IS5G(pi->radio_chanspec) ?
+ 0xc : 0xe));
+ else
+ write_radio_reg(pi,
+ RADIO_2056_TX_TX_SSI_MUX |
+ ((core == PHY_CORE_0) ?
+ RADIO_2056_TX0 : RADIO_2056_TX1),
+ (CHSPEC_IS5G(pi->radio_chanspec) ?
+ 0xc : 0xe));
+ } else {
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ write_radio_reg(pi,
+ ((core == PHY_CORE_0) ?
+ RADIO_2057_TX0_TX_SSI_MUX :
+ RADIO_2057_TX1_TX_SSI_MUX),
+ 0x11);
+
+ if (pi->pubpi.radioid == BCM2057_ID)
+ write_radio_reg(pi,
+ RADIO_2057_IQTEST_SEL_PU, 0x1);
+
+ } else {
+ write_radio_reg(pi,
+ RADIO_2056_TX_TX_SSI_MUX |
+ ((core == PHY_CORE_0) ?
+ RADIO_2056_TX0 : RADIO_2056_TX1),
+ 0x11);
+ }
}
}
-void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
+void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core_code, u8 rssi_type)
{
u16 mask, val;
u16 afectrlovr_rssi_val, rfctrlcmd_rxen_val, rfctrlcmd_coresel_val,
@@ -20501,7 +21902,7 @@ void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
mod_phy_reg(pi, 0xe6, (0x1 << 5), 0);
mask = (0x1 << 2) |
- (0x1 << 3) | (0x1 << 4) | (0x1 << 5);
+ (0x1 << 3) | (0x1 << 4) | (0x1 << 5);
mod_phy_reg(pi, 0xf9, mask, 0);
mod_phy_reg(pi, 0xfb, mask, 0);
@@ -20520,23 +21921,22 @@ void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
if (rssi_type == NPHY_RSSI_SEL_W1 ||
rssi_type == NPHY_RSSI_SEL_W2 ||
rssi_type == NPHY_RSSI_SEL_NB) {
-
mod_phy_reg(pi,
(core ==
PHY_CORE_0) ? 0xa6 : 0xa7,
(0x3 << 8), 0);
mask = (0x1 << 2) |
- (0x1 << 3) |
- (0x1 << 4) | (0x1 << 5);
+ (0x1 << 3) |
+ (0x1 << 4) | (0x1 << 5);
mod_phy_reg(pi,
(core ==
PHY_CORE_0) ? 0xf9 : 0xfb,
mask, 0);
if (rssi_type == NPHY_RSSI_SEL_W1) {
- if (CHSPEC_IS5G
- (pi->radio_chanspec)) {
+ if (CHSPEC_IS5G(
+ pi->radio_chanspec)) {
mask = (0x1 << 2);
val = 1 << 2;
} else {
@@ -20562,7 +21962,6 @@ void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
0xe5 : 0xe6, mask, val);
} else {
if (rssi_type == NPHY_RSSI_SEL_TBD) {
-
mask = (0x3 << 8);
val = 1 << 8;
mod_phy_reg(pi,
@@ -20577,7 +21976,6 @@ void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
: 0xa7, mask, val);
} else if (rssi_type ==
NPHY_RSSI_SEL_IQ) {
-
mask = (0x3 << 8);
val = 2 << 8;
mod_phy_reg(pi,
@@ -20591,7 +21989,6 @@ void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
PHY_CORE_0) ? 0xa6
: 0xa7, mask, val);
} else {
-
mask = (0x3 << 8);
val = 3 << 8;
mod_phy_reg(pi,
@@ -20604,86 +22001,13 @@ void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
(core ==
PHY_CORE_0) ? 0xa6
: 0xa7, mask, val);
-
- if (PHY_IPA(pi)) {
- if (NREV_GE
- (pi->pubpi.phy_rev,
- 7)) {
-
- write_radio_reg
- (pi,
- ((core ==
- PHY_CORE_0)
- ?
- RADIO_2057_TX0_TX_SSI_MUX
- :
- RADIO_2057_TX1_TX_SSI_MUX),
- (CHSPEC_IS5G
- (pi->
- radio_chanspec)
- ? 0xc :
- 0xe));
- } else {
- write_radio_reg
- (pi,
- RADIO_2056_TX_TX_SSI_MUX
- |
- ((core ==
- PHY_CORE_0)
- ?
- RADIO_2056_TX0
- :
- RADIO_2056_TX1),
- (CHSPEC_IS5G
- (pi->
- radio_chanspec)
- ? 0xc :
- 0xe));
- }
- } else {
-
- if (NREV_GE
- (pi->pubpi.phy_rev,
- 7)) {
- write_radio_reg
- (pi,
- ((core ==
- PHY_CORE_0)
- ?
- RADIO_2057_TX0_TX_SSI_MUX
- :
- RADIO_2057_TX1_TX_SSI_MUX),
- 0x11);
-
- if (pi->pubpi.
- radioid ==
- BCM2057_ID)
- write_radio_reg
- (pi,
- RADIO_2057_IQTEST_SEL_PU,
- 0x1);
-
- } else {
- write_radio_reg
- (pi,
- RADIO_2056_TX_TX_SSI_MUX
- |
- ((core ==
- PHY_CORE_0)
- ?
- RADIO_2056_TX0
- :
- RADIO_2056_TX1),
- 0x11);
- }
- }
-
+ brcms_phy_wr_tx_mux(pi, core);
afectrlovr_rssi_val = 1 << 9;
mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x8f
- : 0xa5, (0x1 << 9),
- afectrlovr_rssi_val);
+ (core ==
+ PHY_CORE_0) ? 0x8f
+ : 0xa5, (0x1 << 9),
+ afectrlovr_rssi_val);
}
}
}
@@ -20692,19 +22016,15 @@ void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
if ((rssi_type == NPHY_RSSI_SEL_W1) ||
(rssi_type == NPHY_RSSI_SEL_W2) ||
- (rssi_type == NPHY_RSSI_SEL_NB)) {
-
+ (rssi_type == NPHY_RSSI_SEL_NB))
val = 0x0;
- } else if (rssi_type == NPHY_RSSI_SEL_TBD) {
-
+ else if (rssi_type == NPHY_RSSI_SEL_TBD)
val = 0x1;
- } else if (rssi_type == NPHY_RSSI_SEL_IQ) {
-
+ else if (rssi_type == NPHY_RSSI_SEL_IQ)
val = 0x2;
- } else {
-
+ else
val = 0x3;
- }
+
mask = ((0x3 << 12) | (0x3 << 14));
val = (val << 12) | (val << 14);
mod_phy_reg(pi, 0xa6, mask, val);
@@ -20713,15 +22033,13 @@ void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
if ((rssi_type == NPHY_RSSI_SEL_W1) ||
(rssi_type == NPHY_RSSI_SEL_W2) ||
(rssi_type == NPHY_RSSI_SEL_NB)) {
- if (rssi_type == NPHY_RSSI_SEL_W1) {
+ if (rssi_type == NPHY_RSSI_SEL_W1)
val = 0x1;
- }
- if (rssi_type == NPHY_RSSI_SEL_W2) {
+ if (rssi_type == NPHY_RSSI_SEL_W2)
val = 0x2;
- }
- if (rssi_type == NPHY_RSSI_SEL_NB) {
+ if (rssi_type == NPHY_RSSI_SEL_NB)
val = 0x3;
- }
+
mask = (0x3 << 4);
val = (val << 4);
mod_phy_reg(pi, 0x7a, mask, val);
@@ -20759,16 +22077,16 @@ void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
(rssi_type == NPHY_RSSI_SEL_NB)) {
rfctrlcmd_mask = ((0x1 << 8) | (0x7 << 3));
rfctrlcmd_val = (rfctrlcmd_rxen_val << 8) |
- (rfctrlcmd_coresel_val << 3);
+ (rfctrlcmd_coresel_val << 3);
rfctrlovr_mask = ((0x1 << 5) |
(0x1 << 12) |
(0x1 << 1) | (0x1 << 0));
rfctrlovr_val = (rfctrlovr_rssi_val <<
5) |
- (rfctrlovr_rxen_val << 12) |
- (rfctrlovr_coresel_val << 1) |
- (rfctrlovr_trigger_val << 0);
+ (rfctrlovr_rxen_val << 12) |
+ (rfctrlovr_coresel_val << 1) |
+ (rfctrlovr_trigger_val << 0);
mod_phy_reg(pi, 0x78, rfctrlcmd_mask, rfctrlcmd_val);
mod_phy_reg(pi, 0xec, rfctrlovr_mask, rfctrlovr_val);
@@ -20782,7 +22100,7 @@ void wlc_phy_rssisel_nphy(phy_info_t *pi, u8 core_code, u8 rssi_type)
}
int
-wlc_phy_poll_rssi_nphy(phy_info_t *pi, u8 rssi_type, s32 *rssi_buf,
+wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, s32 *rssi_buf,
u8 nsamps)
{
s16 rssi0, rssi1;
@@ -20823,13 +22141,11 @@ wlc_phy_poll_rssi_nphy(phy_info_t *pi, u8 rssi_type, s32 *rssi_buf,
wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_ALLRX, rssi_type);
gpiosel_orig = read_phy_reg(pi, 0xca);
- if (NREV_LT(pi->pubpi.phy_rev, 2)) {
+ if (NREV_LT(pi->pubpi.phy_rev, 2))
write_phy_reg(pi, 0xca, 5);
- }
- for (ctr = 0; ctr < 4; ctr++) {
+ for (ctr = 0; ctr < 4; ctr++)
rssi_buf[ctr] = 0;
- }
for (samp = 0; samp < nsamps; samp++) {
if (NREV_LT(pi->pubpi.phy_rev, 2)) {
@@ -20846,9 +22162,8 @@ wlc_phy_poll_rssi_nphy(phy_info_t *pi, u8 rssi_type, s32 *rssi_buf,
tmp_buf[ctr++] = ((s8) ((rssi1 & 0x3f) << 2)) >> 2;
tmp_buf[ctr++] = ((s8) (((rssi1 >> 8) & 0x3f) << 2)) >> 2;
- for (ctr = 0; ctr < 4; ctr++) {
+ for (ctr = 0; ctr < 4; ctr++)
rssi_buf[ctr] += tmp_buf[ctr];
- }
}
@@ -20857,9 +22172,8 @@ wlc_phy_poll_rssi_nphy(phy_info_t *pi, u8 rssi_type, s32 *rssi_buf,
rssi_out_val |= (rssi_buf[1] & 0xff) << 16;
rssi_out_val |= (rssi_buf[0] & 0xff) << 24;
- if (NREV_LT(pi->pubpi.phy_rev, 2)) {
+ if (NREV_LT(pi->pubpi.phy_rev, 2))
write_phy_reg(pi, 0xca, gpiosel_orig);
- }
write_phy_reg(pi, 0xa6, afectrlCore1_save);
write_phy_reg(pi, 0xa7, afectrlCore2_save);
@@ -20881,7 +22195,7 @@ wlc_phy_poll_rssi_nphy(phy_info_t *pi, u8 rssi_type, s32 *rssi_buf,
return rssi_out_val;
}
-s16 wlc_phy_tempsense_nphy(phy_info_t *pi)
+s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi)
{
u16 core1_txrf_iqcal1_save, core1_txrf_iqcal2_save;
u16 core2_txrf_iqcal1_save, core2_txrf_iqcal2_save;
@@ -20909,7 +22223,7 @@ s16 wlc_phy_tempsense_nphy(phy_info_t *pi)
u16 tempsense_Rcal;
syn_tempprocsense_save =
- read_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG);
+ read_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG);
afectrlCore1_save = read_phy_reg(pi, 0xa6);
afectrlCore2_save = read_phy_reg(pi, 0xa7);
@@ -21035,25 +22349,15 @@ s16 wlc_phy_tempsense_nphy(phy_info_t *pi)
wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x03, 16,
&auxADC_rssi_ctrlH_save);
- if (pi->sh->chip == BCM5357_CHIP_ID) {
- radio_temp[0] = (193 * (radio_temp[1] + radio_temp2[1])
- + 88 * (auxADC_Vl) - 27111 +
- 128) / 256;
- } else if (pi->sh->chip == BCM43236_CHIP_ID) {
- radio_temp[0] = (198 * (radio_temp[1] + radio_temp2[1])
- + 91 * (auxADC_Vl) - 27243 +
- 128) / 256;
- } else {
- radio_temp[0] = (179 * (radio_temp[1] + radio_temp2[1])
- + 82 * (auxADC_Vl) - 28861 +
- 128) / 256;
- }
+ radio_temp[0] = (179 * (radio_temp[1] + radio_temp2[1])
+ + 82 * (auxADC_Vl) - 28861 +
+ 128) / 256;
offset = (s16) pi->phy_tempsense_offset;
} else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
syn_tempprocsense_save =
- read_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE);
+ read_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE);
afectrlCore1_save = read_phy_reg(pi, 0xa6);
afectrlCore2_save = read_phy_reg(pi, 0xa7);
@@ -21064,20 +22368,17 @@ s16 wlc_phy_tempsense_nphy(phy_info_t *pi)
write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x01);
wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1);
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- } else {
+ if (NREV_LT(pi->pubpi.phy_rev, 7))
write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x05);
- }
wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1);
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, 0x01);
- } else {
+ else
write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x01);
- }
radio_temp[0] =
- (126 * (radio_temp[1] + radio_temp2[1]) + 3987) / 64;
+ (126 * (radio_temp[1] + radio_temp2[1]) + 3987) / 64;
write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE,
syn_tempprocsense_save);
@@ -21092,17 +22393,17 @@ s16 wlc_phy_tempsense_nphy(phy_info_t *pi)
} else {
pwrdet_rxtx_core1_save =
- read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1);
+ read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1);
pwrdet_rxtx_core2_save =
- read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2);
+ read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2);
core1_txrf_iqcal1_save =
- read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1);
+ read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1);
core1_txrf_iqcal2_save =
- read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2);
+ read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2);
core2_txrf_iqcal1_save =
- read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1);
+ read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1);
core2_txrf_iqcal2_save =
- read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2);
+ read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2);
pd_pll_ts_save = read_radio_reg(pi, RADIO_2055_PD_PLL_TS);
afectrlCore1_save = read_phy_reg(pi, 0xa6);
@@ -21133,11 +22434,12 @@ s16 wlc_phy_tempsense_nphy(phy_info_t *pi)
radio_temp[3] = (radio_temp[3] + radio_temp2[3]);
radio_temp[0] =
- (radio_temp[0] + radio_temp[1] + radio_temp[2] +
- radio_temp[3]);
+ (radio_temp[0] + radio_temp[1] + radio_temp[2] +
+ radio_temp[3]);
radio_temp[0] =
- (radio_temp[0] + (8 * 32)) * (950 - 350) / 63 + (350 * 8);
+ (radio_temp[0] +
+ (8 * 32)) * (950 - 350) / 63 + (350 * 8);
radio_temp[0] = (radio_temp[0] - (8 * 420)) / 38;
@@ -21165,7 +22467,7 @@ s16 wlc_phy_tempsense_nphy(phy_info_t *pi)
}
static void
-wlc_phy_set_rssi_2055_vcm(phy_info_t *pi, u8 rssi_type, u8 *vcm_buf)
+wlc_phy_set_rssi_2055_vcm(struct brcms_phy *pi, u8 rssi_type, u8 *vcm_buf)
{
u8 core;
@@ -21199,522 +22501,25 @@ wlc_phy_set_rssi_2055_vcm(phy_info_t *pi, u8 rssi_type, u8 *vcm_buf)
RADIO_2055_NBRSSI_VCM_Q_SHIFT);
}
} else {
-
- if (core == PHY_CORE_0) {
+ if (core == PHY_CORE_0)
mod_radio_reg(pi,
RADIO_2055_CORE1_RXBB_RSSI_CTRL5,
RADIO_2055_WBRSSI_VCM_IQ_MASK,
vcm_buf[2 *
core] <<
RADIO_2055_WBRSSI_VCM_IQ_SHIFT);
- } else {
+ else
mod_radio_reg(pi,
RADIO_2055_CORE2_RXBB_RSSI_CTRL5,
RADIO_2055_WBRSSI_VCM_IQ_MASK,
vcm_buf[2 *
core] <<
RADIO_2055_WBRSSI_VCM_IQ_SHIFT);
- }
- }
- }
-}
-
-void wlc_phy_rssi_cal_nphy(phy_info_t *pi)
-{
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
-
- wlc_phy_rssi_cal_nphy_rev3(pi);
- } else {
- wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_NB);
- wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_W1);
- wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_W2);
- }
-}
-
-static void wlc_phy_rssi_cal_nphy_rev2(phy_info_t *pi, u8 rssi_type)
-{
- s32 target_code;
- u16 classif_state;
- u16 clip_state[2];
- u16 rssi_ctrl_state[2], pd_state[2];
- u16 rfctrlintc_state[2], rfpdcorerxtx_state[2];
- u16 rfctrlintc_override_val;
- u16 clip_off[] = { 0xffff, 0xffff };
- u16 rf_pd_val, pd_mask, rssi_ctrl_mask;
- u8 vcm, min_vcm, vcm_tmp[4];
- u8 vcm_final[4] = { 0, 0, 0, 0 };
- u8 result_idx, ctr;
- s32 poll_results[4][4] = {
- {0, 0, 0, 0},
- {0, 0, 0, 0},
- {0, 0, 0, 0},
- {0, 0, 0, 0}
- };
- s32 poll_miniq[4][2] = {
- {0, 0},
- {0, 0},
- {0, 0},
- {0, 0}
- };
- s32 min_d, curr_d;
- s32 fine_digital_offset[4];
- s32 poll_results_min[4] = { 0, 0, 0, 0 };
- s32 min_poll;
-
- switch (rssi_type) {
- case NPHY_RSSI_SEL_NB:
- target_code = NPHY_RSSICAL_NB_TARGET;
- break;
- case NPHY_RSSI_SEL_W1:
- target_code = NPHY_RSSICAL_W1_TARGET;
- break;
- case NPHY_RSSI_SEL_W2:
- target_code = NPHY_RSSICAL_W2_TARGET;
- break;
- default:
- return;
- break;
- }
-
- classif_state = wlc_phy_classifier_nphy(pi, 0, 0);
- wlc_phy_classifier_nphy(pi, (0x7 << 0), 4);
- wlc_phy_clip_det_nphy(pi, 0, clip_state);
- wlc_phy_clip_det_nphy(pi, 1, clip_off);
-
- rf_pd_val = (rssi_type == NPHY_RSSI_SEL_NB) ? 0x6 : 0x4;
- rfctrlintc_override_val =
- CHSPEC_IS5G(pi->radio_chanspec) ? 0x140 : 0x110;
-
- rfctrlintc_state[0] = read_phy_reg(pi, 0x91);
- rfpdcorerxtx_state[0] = read_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX);
- write_phy_reg(pi, 0x91, rfctrlintc_override_val);
- write_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX, rf_pd_val);
-
- rfctrlintc_state[1] = read_phy_reg(pi, 0x92);
- rfpdcorerxtx_state[1] = read_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX);
- write_phy_reg(pi, 0x92, rfctrlintc_override_val);
- write_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX, rf_pd_val);
-
- pd_mask = RADIO_2055_NBRSSI_PD | RADIO_2055_WBRSSI_G1_PD |
- RADIO_2055_WBRSSI_G2_PD;
- pd_state[0] =
- read_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC) & pd_mask;
- pd_state[1] =
- read_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC) & pd_mask;
- mod_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC, pd_mask, 0);
- mod_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC, pd_mask, 0);
- rssi_ctrl_mask = RADIO_2055_NBRSSI_SEL | RADIO_2055_WBRSSI_G1_SEL |
- RADIO_2055_WBRSSI_G2_SEL;
- rssi_ctrl_state[0] =
- read_radio_reg(pi, RADIO_2055_SP_RSSI_CORE1) & rssi_ctrl_mask;
- rssi_ctrl_state[1] =
- read_radio_reg(pi, RADIO_2055_SP_RSSI_CORE2) & rssi_ctrl_mask;
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_ALLRX, rssi_type);
-
- wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, RADIO_MIMO_CORESEL_ALLRX,
- NPHY_RAIL_I, rssi_type);
- wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, RADIO_MIMO_CORESEL_ALLRX,
- NPHY_RAIL_Q, rssi_type);
-
- for (vcm = 0; vcm < 4; vcm++) {
-
- vcm_tmp[0] = vcm_tmp[1] = vcm_tmp[2] = vcm_tmp[3] = vcm;
- if (rssi_type != NPHY_RSSI_SEL_W2) {
- wlc_phy_set_rssi_2055_vcm(pi, rssi_type, vcm_tmp);
- }
-
- wlc_phy_poll_rssi_nphy(pi, rssi_type, &poll_results[vcm][0],
- NPHY_RSSICAL_NPOLL);
-
- if ((rssi_type == NPHY_RSSI_SEL_W1)
- || (rssi_type == NPHY_RSSI_SEL_W2)) {
- for (ctr = 0; ctr < 2; ctr++) {
- poll_miniq[vcm][ctr] =
- min(poll_results[vcm][ctr * 2 + 0],
- poll_results[vcm][ctr * 2 + 1]);
- }
- }
- }
-
- for (result_idx = 0; result_idx < 4; result_idx++) {
- min_d = NPHY_RSSICAL_MAXD;
- min_vcm = 0;
- min_poll = NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL + 1;
- for (vcm = 0; vcm < 4; vcm++) {
- curr_d = ABS(((rssi_type == NPHY_RSSI_SEL_NB) ?
- poll_results[vcm][result_idx] :
- poll_miniq[vcm][result_idx / 2]) -
- (target_code * NPHY_RSSICAL_NPOLL));
- if (curr_d < min_d) {
- min_d = curr_d;
- min_vcm = vcm;
- }
- if (poll_results[vcm][result_idx] < min_poll) {
- min_poll = poll_results[vcm][result_idx];
- }
- }
- vcm_final[result_idx] = min_vcm;
- poll_results_min[result_idx] = min_poll;
- }
-
- if (rssi_type != NPHY_RSSI_SEL_W2) {
- wlc_phy_set_rssi_2055_vcm(pi, rssi_type, vcm_final);
- }
-
- for (result_idx = 0; result_idx < 4; result_idx++) {
- fine_digital_offset[result_idx] =
- (target_code * NPHY_RSSICAL_NPOLL) -
- poll_results[vcm_final[result_idx]][result_idx];
- if (fine_digital_offset[result_idx] < 0) {
- fine_digital_offset[result_idx] =
- ABS(fine_digital_offset[result_idx]);
- fine_digital_offset[result_idx] +=
- (NPHY_RSSICAL_NPOLL / 2);
- fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL;
- fine_digital_offset[result_idx] =
- -fine_digital_offset[result_idx];
- } else {
- fine_digital_offset[result_idx] +=
- (NPHY_RSSICAL_NPOLL / 2);
- fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL;
- }
-
- if (poll_results_min[result_idx] ==
- NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL) {
- fine_digital_offset[result_idx] =
- (target_code - NPHY_RSSICAL_MAXREAD - 1);
- }
-
- wlc_phy_scale_offset_rssi_nphy(pi, 0x0,
- (s8)
- fine_digital_offset[result_idx],
- (result_idx / 2 ==
- 0) ? RADIO_MIMO_CORESEL_CORE1 :
- RADIO_MIMO_CORESEL_CORE2,
- (result_idx % 2 ==
- 0) ? NPHY_RAIL_I : NPHY_RAIL_Q,
- rssi_type);
- }
-
- mod_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC, pd_mask, pd_state[0]);
- mod_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC, pd_mask, pd_state[1]);
- if (rssi_ctrl_state[0] == RADIO_2055_NBRSSI_SEL) {
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
- NPHY_RSSI_SEL_NB);
- } else if (rssi_ctrl_state[0] == RADIO_2055_WBRSSI_G1_SEL) {
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
- NPHY_RSSI_SEL_W1);
- } else if (rssi_ctrl_state[0] == RADIO_2055_WBRSSI_G2_SEL) {
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
- NPHY_RSSI_SEL_W2);
- } else {
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
- NPHY_RSSI_SEL_W2);
- }
- if (rssi_ctrl_state[1] == RADIO_2055_NBRSSI_SEL) {
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
- NPHY_RSSI_SEL_NB);
- } else if (rssi_ctrl_state[1] == RADIO_2055_WBRSSI_G1_SEL) {
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
- NPHY_RSSI_SEL_W1);
- } else if (rssi_ctrl_state[1] == RADIO_2055_WBRSSI_G2_SEL) {
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
- NPHY_RSSI_SEL_W2);
- } else {
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
- NPHY_RSSI_SEL_W2);
- }
-
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, rssi_type);
-
- write_phy_reg(pi, 0x91, rfctrlintc_state[0]);
- write_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX, rfpdcorerxtx_state[0]);
- write_phy_reg(pi, 0x92, rfctrlintc_state[1]);
- write_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX, rfpdcorerxtx_state[1]);
-
- wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state);
- wlc_phy_clip_det_nphy(pi, 1, clip_state);
-
- wlc_phy_resetcca_nphy(pi);
-}
-
-int
-wlc_phy_rssi_compute_nphy(phy_info_t *pi, wlc_d11rxhdr_t *wlc_rxh)
-{
- d11rxhdr_t *rxh = &wlc_rxh->rxhdr;
- s16 rxpwr, rxpwr0, rxpwr1;
- s16 phyRx0_l, phyRx2_l;
-
- rxpwr = 0;
- rxpwr0 = le16_to_cpu(rxh->PhyRxStatus_1) & PRXS1_nphy_PWR0_MASK;
- rxpwr1 = (le16_to_cpu(rxh->PhyRxStatus_1) & PRXS1_nphy_PWR1_MASK) >> 8;
-
- if (rxpwr0 > 127)
- rxpwr0 -= 256;
- if (rxpwr1 > 127)
- rxpwr1 -= 256;
-
- phyRx0_l = le16_to_cpu(rxh->PhyRxStatus_0) & 0x00ff;
- phyRx2_l = le16_to_cpu(rxh->PhyRxStatus_2) & 0x00ff;
- if (phyRx2_l > 127)
- phyRx2_l -= 256;
-
- if (((rxpwr0 == 16) || (rxpwr0 == 32))) {
- rxpwr0 = rxpwr1;
- rxpwr1 = phyRx2_l;
- }
-
- wlc_rxh->rxpwr[0] = (s8) rxpwr0;
- wlc_rxh->rxpwr[1] = (s8) rxpwr1;
- wlc_rxh->do_rssi_ma = 0;
-
- if (pi->sh->rssi_mode == RSSI_ANT_MERGE_MAX)
- rxpwr = (rxpwr0 > rxpwr1) ? rxpwr0 : rxpwr1;
- else if (pi->sh->rssi_mode == RSSI_ANT_MERGE_MIN)
- rxpwr = (rxpwr0 < rxpwr1) ? rxpwr0 : rxpwr1;
- else if (pi->sh->rssi_mode == RSSI_ANT_MERGE_AVG)
- rxpwr = (rxpwr0 + rxpwr1) >> 1;
-
- return rxpwr;
-}
-
-static void
-wlc_phy_rfctrlintc_override_nphy(phy_info_t *pi, u8 field, u16 value,
- u8 core_code)
-{
- u16 mask;
- u16 val;
- u8 core;
-
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- for (core = 0; core < pi->pubpi.phy_corenum; core++) {
- if (core_code == RADIO_MIMO_CORESEL_CORE1
- && core == PHY_CORE_1)
- continue;
- else if (core_code == RADIO_MIMO_CORESEL_CORE2
- && core == PHY_CORE_0)
- continue;
-
- if (NREV_LT(pi->pubpi.phy_rev, 7)) {
-
- mask = (0x1 << 10);
- val = 1 << 10;
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 :
- 0x92, mask, val);
- }
-
- if (field == NPHY_RfctrlIntc_override_OFF) {
-
- write_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 :
- 0x92, 0);
-
- wlc_phy_force_rfseq_nphy(pi,
- NPHY_RFSEQ_RESET2RX);
- } else if (field == NPHY_RfctrlIntc_override_TRSW) {
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
- mask = (0x1 << 6) | (0x1 << 7);
-
- val = value << 6;
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91 : 0x92,
- mask, val);
-
- or_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91 : 0x92,
- (0x1 << 10));
-
- and_phy_reg(pi, 0x2ff, (u16)
- ~(0x3 << 14));
- or_phy_reg(pi, 0x2ff, (0x1 << 13));
- or_phy_reg(pi, 0x2ff, (0x1 << 0));
- } else {
-
- mask = (0x1 << 6) |
- (0x1 << 7) |
- (0x1 << 8) | (0x1 << 9);
- val = value << 6;
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91 : 0x92,
- mask, val);
-
- mask = (0x1 << 0);
- val = 1 << 0;
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0xe7 : 0xec,
- mask, val);
-
- mask = (core == PHY_CORE_0) ? (0x1 << 0)
- : (0x1 << 1);
- val = 1 << ((core == PHY_CORE_0) ?
- 0 : 1);
- mod_phy_reg(pi, 0x78, mask, val);
-
- SPINWAIT(((read_phy_reg(pi, 0x78) & val)
- != 0), 10000);
- if (WARN(read_phy_reg(pi, 0x78) & val,
- "HW error: override failed"))
- return;
-
- mask = (0x1 << 0);
- val = 0 << 0;
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0xe7 : 0xec,
- mask, val);
- }
- } else if (field == NPHY_RfctrlIntc_override_PA) {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
- mask = (0x1 << 4) | (0x1 << 5);
-
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
- val = value << 5;
- } else {
- val = value << 4;
- }
-
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91 : 0x92,
- mask, val);
-
- or_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91 : 0x92,
- (0x1 << 12));
- } else {
-
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
- mask = (0x1 << 5);
- val = value << 5;
- } else {
- mask = (0x1 << 4);
- val = value << 4;
- }
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91 : 0x92,
- mask, val);
- }
- } else if (field == NPHY_RfctrlIntc_override_EXT_LNA_PU) {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
-
- mask = (0x1 << 0);
- val = value << 0;
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91
- : 0x92, mask, val);
-
- mask = (0x1 << 2);
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91
- : 0x92, mask, 0);
- } else {
-
- mask = (0x1 << 2);
- val = value << 2;
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91
- : 0x92, mask, val);
-
- mask = (0x1 << 0);
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91
- : 0x92, mask, 0);
- }
-
- mask = (0x1 << 11);
- val = 1 << 11;
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91 : 0x92,
- mask, val);
- } else {
-
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
- mask = (0x1 << 0);
- val = value << 0;
- } else {
- mask = (0x1 << 2);
- val = value << 2;
- }
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91 : 0x92,
- mask, val);
- }
- } else if (field ==
- NPHY_RfctrlIntc_override_EXT_LNA_GAIN) {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
-
- mask = (0x1 << 1);
- val = value << 1;
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91
- : 0x92, mask, val);
-
- mask = (0x1 << 3);
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91
- : 0x92, mask, 0);
- } else {
-
- mask = (0x1 << 3);
- val = value << 3;
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91
- : 0x92, mask, val);
-
- mask = (0x1 << 1);
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91
- : 0x92, mask, 0);
- }
-
- mask = (0x1 << 11);
- val = 1 << 11;
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91 : 0x92,
- mask, val);
- } else {
-
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
- mask = (0x1 << 1);
- val = value << 1;
- } else {
- mask = (0x1 << 3);
- val = value << 3;
- }
- mod_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0x91 : 0x92,
- mask, val);
- }
- }
}
- } else {
- return;
}
}
-static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi)
+static void wlc_phy_rssi_cal_nphy_rev3(struct brcms_phy *pi)
{
u16 classif_state;
u16 clip_state[2];
@@ -21756,10 +22561,14 @@ static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi)
u16 NPHY_REV7_RfctrlMiscReg3_save, NPHY_REV7_RfctrlMiscReg4_save;
u16 NPHY_REV7_RfctrlMiscReg5_save, NPHY_REV7_RfctrlMiscReg6_save;
- NPHY_REV7_RfctrlOverride3_save = NPHY_REV7_RfctrlOverride4_save =
- NPHY_REV7_RfctrlOverride5_save = NPHY_REV7_RfctrlOverride6_save =
- NPHY_REV7_RfctrlMiscReg3_save = NPHY_REV7_RfctrlMiscReg4_save =
- NPHY_REV7_RfctrlMiscReg5_save = NPHY_REV7_RfctrlMiscReg6_save = 0;
+ NPHY_REV7_RfctrlOverride3_save =
+ NPHY_REV7_RfctrlOverride4_save =
+ NPHY_REV7_RfctrlOverride5_save =
+ NPHY_REV7_RfctrlOverride6_save =
+ NPHY_REV7_RfctrlMiscReg3_save =
+ NPHY_REV7_RfctrlMiscReg4_save =
+ NPHY_REV7_RfctrlMiscReg5_save =
+ NPHY_REV7_RfctrlMiscReg6_save = 0;
classif_state = wlc_phy_classifier_nphy(pi, 0, 0);
wlc_phy_classifier_nphy(pi, (0x7 << 0), 4);
@@ -21799,21 +22608,21 @@ static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi)
wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 1,
RADIO_MIMO_CORESEL_ALLRXTX);
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- wlc_phy_rfctrl_override_1tomany_nphy(pi,
- NPHY_REV7_RfctrlOverride_cmd_rxrf_pu,
- 0, 0, 0);
- } else {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ wlc_phy_rfctrl_override_1tomany_nphy(
+ pi,
+ NPHY_REV7_RfctrlOverride_cmd_rxrf_pu,
+ 0, 0, 0);
+ else
wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0, 0);
- }
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- wlc_phy_rfctrl_override_1tomany_nphy(pi,
- NPHY_REV7_RfctrlOverride_cmd_rx_pu,
- 1, 0, 0);
- } else {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ wlc_phy_rfctrl_override_1tomany_nphy(
+ pi,
+ NPHY_REV7_RfctrlOverride_cmd_rx_pu,
+ 1, 0, 0);
+ else
wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 1, 0, 0);
- }
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7),
@@ -21828,12 +22637,14 @@ static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi)
if (CHSPEC_IS5G(pi->radio_chanspec)) {
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5),
- 0, 0, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 1, 0,
- 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 5),
+ 0, 0, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 4), 1, 0,
+ 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
} else {
wlc_phy_rfctrl_override_nphy(pi, (0x1 << 5), 0, 0, 0);
wlc_phy_rfctrl_override_nphy(pi, (0x1 << 4), 1, 0, 0);
@@ -21841,19 +22652,22 @@ static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi)
} else {
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4),
- 0, 0, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), 1, 0,
- 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 4),
+ 0, 0, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 5), 1, 0,
+ 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
} else {
wlc_phy_rfctrl_override_nphy(pi, (0x1 << 4), 0, 0, 0);
wlc_phy_rfctrl_override_nphy(pi, (0x1 << 5), 1, 0, 0);
}
}
- rxcore_state = wlc_phy_rxcore_getstate_nphy((wlc_phy_t *) pi);
+ rxcore_state = wlc_phy_rxcore_getstate_nphy(
+ (struct brcms_phy_pub *) pi);
vcm_level_max = 8;
@@ -21876,21 +22690,18 @@ static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi)
NPHY_RAIL_Q, NPHY_RSSI_SEL_NB);
for (vcm = 0; vcm < vcm_level_max; vcm++) {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
mod_radio_reg(pi, (core == PHY_CORE_0) ?
RADIO_2057_NB_MASTER_CORE0 :
RADIO_2057_NB_MASTER_CORE1,
RADIO_2057_VCM_MASK, vcm);
- } else {
-
+ else
mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC |
((core ==
PHY_CORE_0) ? RADIO_2056_RX0 :
RADIO_2056_RX1),
RADIO_2056_VCM_MASK,
vcm << RADIO_2056_RSSI_VCM_SHIFT);
- }
wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_NB,
&poll_results[vcm][0],
@@ -21898,92 +22709,90 @@ static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi)
}
for (result_idx = 0; result_idx < 4; result_idx++) {
- if ((core == result_idx / 2) && (result_idx % 2 == 0)) {
+ if ((core == result_idx / 2) &&
+ (result_idx % 2 == 0)) {
min_d = NPHY_RSSICAL_MAXD;
min_vcm = 0;
min_poll =
- NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL +
- 1;
+ NPHY_RSSICAL_MAXREAD *
+ NPHY_RSSICAL_NPOLL + 1;
for (vcm = 0; vcm < vcm_level_max; vcm++) {
- curr_d = poll_results[vcm][result_idx] *
- poll_results[vcm][result_idx] +
- poll_results[vcm][result_idx + 1] *
- poll_results[vcm][result_idx + 1];
+ curr_d =
+ poll_results[vcm][result_idx] *
+ poll_results[vcm][result_idx] +
+ poll_results[vcm][result_idx +
+ 1] *
+ poll_results[vcm][result_idx +
+ 1];
if (curr_d < min_d) {
min_d = curr_d;
min_vcm = vcm;
}
if (poll_results[vcm][result_idx] <
- min_poll) {
+ min_poll)
min_poll =
- poll_results[vcm]
- [result_idx];
- }
+ poll_results[vcm]
+ [result_idx];
}
vcm_final = min_vcm;
poll_results_min[result_idx] = min_poll;
}
}
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
mod_radio_reg(pi, (core == PHY_CORE_0) ?
RADIO_2057_NB_MASTER_CORE0 :
RADIO_2057_NB_MASTER_CORE1,
RADIO_2057_VCM_MASK, vcm_final);
- } else {
+ else
mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC |
((core ==
PHY_CORE_0) ? RADIO_2056_RX0 :
RADIO_2056_RX1), RADIO_2056_VCM_MASK,
vcm_final << RADIO_2056_RSSI_VCM_SHIFT);
- }
for (result_idx = 0; result_idx < 4; result_idx++) {
if (core == result_idx / 2) {
fine_digital_offset[result_idx] =
- (NPHY_RSSICAL_NB_TARGET *
- NPHY_RSSICAL_NPOLL) -
- poll_results[vcm_final][result_idx];
+ (NPHY_RSSICAL_NB_TARGET *
+ NPHY_RSSICAL_NPOLL) -
+ poll_results[vcm_final][result_idx];
if (fine_digital_offset[result_idx] < 0) {
fine_digital_offset[result_idx] =
- ABS(fine_digital_offset
- [result_idx]);
+ abs(fine_digital_offset
+ [result_idx]);
fine_digital_offset[result_idx] +=
- (NPHY_RSSICAL_NPOLL / 2);
+ (NPHY_RSSICAL_NPOLL / 2);
fine_digital_offset[result_idx] /=
- NPHY_RSSICAL_NPOLL;
+ NPHY_RSSICAL_NPOLL;
fine_digital_offset[result_idx] =
- -fine_digital_offset[result_idx];
+ -fine_digital_offset[
+ result_idx];
} else {
fine_digital_offset[result_idx] +=
- (NPHY_RSSICAL_NPOLL / 2);
+ (NPHY_RSSICAL_NPOLL / 2);
fine_digital_offset[result_idx] /=
- NPHY_RSSICAL_NPOLL;
+ NPHY_RSSICAL_NPOLL;
}
if (poll_results_min[result_idx] ==
- NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL) {
+ NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL)
fine_digital_offset[result_idx] =
- (NPHY_RSSICAL_NB_TARGET -
- NPHY_RSSICAL_MAXREAD - 1);
- }
-
- wlc_phy_scale_offset_rssi_nphy(pi, 0x0,
- (s8)
- fine_digital_offset
- [result_idx],
- (result_idx /
- 2 ==
- 0) ?
- RADIO_MIMO_CORESEL_CORE1
- :
- RADIO_MIMO_CORESEL_CORE2,
- (result_idx %
- 2 ==
- 0) ? NPHY_RAIL_I
- : NPHY_RAIL_Q,
- NPHY_RSSI_SEL_NB);
+ (NPHY_RSSICAL_NB_TARGET -
+ NPHY_RSSICAL_MAXREAD - 1);
+
+ wlc_phy_scale_offset_rssi_nphy(
+ pi, 0x0,
+ (s8)
+ fine_digital_offset
+ [result_idx],
+ (result_idx / 2 == 0) ?
+ RADIO_MIMO_CORESEL_CORE1 :
+ RADIO_MIMO_CORESEL_CORE2,
+ (result_idx % 2 == 0) ?
+ NPHY_RAIL_I : NPHY_RAIL_Q,
+ NPHY_RSSI_SEL_NB);
}
}
@@ -22024,46 +22833,44 @@ static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi)
for (result_idx = 0; result_idx < 4; result_idx++) {
if (core == result_idx / 2) {
fine_digital_offset[result_idx] =
- (target_code * NPHY_RSSICAL_NPOLL) -
- poll_result_core[result_idx];
- if (fine_digital_offset[result_idx] < 0) {
+ (target_code *
+ NPHY_RSSICAL_NPOLL) -
+ poll_result_core[result_idx];
+ if (fine_digital_offset[result_idx] <
+ 0) {
fine_digital_offset[result_idx]
- =
- ABS(fine_digital_offset
- [result_idx]);
+ = abs(
+ fine_digital_offset
+ [result_idx]);
fine_digital_offset[result_idx]
- += (NPHY_RSSICAL_NPOLL / 2);
+ += (NPHY_RSSICAL_NPOLL
+ / 2);
fine_digital_offset[result_idx]
- /= NPHY_RSSICAL_NPOLL;
+ /= NPHY_RSSICAL_NPOLL;
fine_digital_offset[result_idx]
- =
- -fine_digital_offset
- [result_idx];
+ = -fine_digital_offset
+ [result_idx];
} else {
fine_digital_offset[result_idx]
- += (NPHY_RSSICAL_NPOLL / 2);
+ += (NPHY_RSSICAL_NPOLL
+ / 2);
fine_digital_offset[result_idx]
- /= NPHY_RSSICAL_NPOLL;
+ /= NPHY_RSSICAL_NPOLL;
}
- wlc_phy_scale_offset_rssi_nphy(pi, 0x0,
- (s8)
- fine_digital_offset
- [core *
- 2],
- (core ==
- PHY_CORE_0)
- ?
- RADIO_MIMO_CORESEL_CORE1
- :
- RADIO_MIMO_CORESEL_CORE2,
- (result_idx
- % 2 ==
- 0) ?
- NPHY_RAIL_I
- :
- NPHY_RAIL_Q,
- rssi_type);
+ wlc_phy_scale_offset_rssi_nphy(
+ pi, 0x0,
+ (s8)
+ fine_digital_offset
+ [core *
+ 2],
+ (core == PHY_CORE_0) ?
+ RADIO_MIMO_CORESEL_CORE1 :
+ RADIO_MIMO_CORESEL_CORE2,
+ (result_idx % 2 == 0) ?
+ NPHY_RAIL_I :
+ NPHY_RAIL_Q,
+ rssi_type);
}
}
@@ -22112,87 +22919,87 @@ static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi)
if (CHSPEC_IS2G(pi->radio_chanspec)) {
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
pi->rssical_cache.rssical_radio_regs_2G[0] =
- read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0);
+ read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0);
pi->rssical_cache.rssical_radio_regs_2G[1] =
- read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1);
+ read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1);
} else {
pi->rssical_cache.rssical_radio_regs_2G[0] =
- read_radio_reg(pi,
- RADIO_2056_RX_RSSI_MISC |
- RADIO_2056_RX0);
+ read_radio_reg(pi,
+ RADIO_2056_RX_RSSI_MISC |
+ RADIO_2056_RX0);
pi->rssical_cache.rssical_radio_regs_2G[1] =
- read_radio_reg(pi,
- RADIO_2056_RX_RSSI_MISC |
- RADIO_2056_RX1);
+ read_radio_reg(pi,
+ RADIO_2056_RX_RSSI_MISC |
+ RADIO_2056_RX1);
}
pi->rssical_cache.rssical_phyregs_2G[0] =
- read_phy_reg(pi, 0x1a6);
+ read_phy_reg(pi, 0x1a6);
pi->rssical_cache.rssical_phyregs_2G[1] =
- read_phy_reg(pi, 0x1ac);
+ read_phy_reg(pi, 0x1ac);
pi->rssical_cache.rssical_phyregs_2G[2] =
- read_phy_reg(pi, 0x1b2);
+ read_phy_reg(pi, 0x1b2);
pi->rssical_cache.rssical_phyregs_2G[3] =
- read_phy_reg(pi, 0x1b8);
+ read_phy_reg(pi, 0x1b8);
pi->rssical_cache.rssical_phyregs_2G[4] =
- read_phy_reg(pi, 0x1a4);
+ read_phy_reg(pi, 0x1a4);
pi->rssical_cache.rssical_phyregs_2G[5] =
- read_phy_reg(pi, 0x1aa);
+ read_phy_reg(pi, 0x1aa);
pi->rssical_cache.rssical_phyregs_2G[6] =
- read_phy_reg(pi, 0x1b0);
+ read_phy_reg(pi, 0x1b0);
pi->rssical_cache.rssical_phyregs_2G[7] =
- read_phy_reg(pi, 0x1b6);
+ read_phy_reg(pi, 0x1b6);
pi->rssical_cache.rssical_phyregs_2G[8] =
- read_phy_reg(pi, 0x1a5);
+ read_phy_reg(pi, 0x1a5);
pi->rssical_cache.rssical_phyregs_2G[9] =
- read_phy_reg(pi, 0x1ab);
+ read_phy_reg(pi, 0x1ab);
pi->rssical_cache.rssical_phyregs_2G[10] =
- read_phy_reg(pi, 0x1b1);
+ read_phy_reg(pi, 0x1b1);
pi->rssical_cache.rssical_phyregs_2G[11] =
- read_phy_reg(pi, 0x1b7);
+ read_phy_reg(pi, 0x1b7);
pi->nphy_rssical_chanspec_2G = pi->radio_chanspec;
} else {
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
pi->rssical_cache.rssical_radio_regs_5G[0] =
- read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0);
+ read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0);
pi->rssical_cache.rssical_radio_regs_5G[1] =
- read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1);
+ read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1);
} else {
pi->rssical_cache.rssical_radio_regs_5G[0] =
- read_radio_reg(pi,
- RADIO_2056_RX_RSSI_MISC |
- RADIO_2056_RX0);
+ read_radio_reg(pi,
+ RADIO_2056_RX_RSSI_MISC |
+ RADIO_2056_RX0);
pi->rssical_cache.rssical_radio_regs_5G[1] =
- read_radio_reg(pi,
- RADIO_2056_RX_RSSI_MISC |
- RADIO_2056_RX1);
+ read_radio_reg(pi,
+ RADIO_2056_RX_RSSI_MISC |
+ RADIO_2056_RX1);
}
pi->rssical_cache.rssical_phyregs_5G[0] =
- read_phy_reg(pi, 0x1a6);
+ read_phy_reg(pi, 0x1a6);
pi->rssical_cache.rssical_phyregs_5G[1] =
- read_phy_reg(pi, 0x1ac);
+ read_phy_reg(pi, 0x1ac);
pi->rssical_cache.rssical_phyregs_5G[2] =
- read_phy_reg(pi, 0x1b2);
+ read_phy_reg(pi, 0x1b2);
pi->rssical_cache.rssical_phyregs_5G[3] =
- read_phy_reg(pi, 0x1b8);
+ read_phy_reg(pi, 0x1b8);
pi->rssical_cache.rssical_phyregs_5G[4] =
- read_phy_reg(pi, 0x1a4);
+ read_phy_reg(pi, 0x1a4);
pi->rssical_cache.rssical_phyregs_5G[5] =
- read_phy_reg(pi, 0x1aa);
+ read_phy_reg(pi, 0x1aa);
pi->rssical_cache.rssical_phyregs_5G[6] =
- read_phy_reg(pi, 0x1b0);
+ read_phy_reg(pi, 0x1b0);
pi->rssical_cache.rssical_phyregs_5G[7] =
- read_phy_reg(pi, 0x1b6);
+ read_phy_reg(pi, 0x1b6);
pi->rssical_cache.rssical_phyregs_5G[8] =
- read_phy_reg(pi, 0x1a5);
+ read_phy_reg(pi, 0x1a5);
pi->rssical_cache.rssical_phyregs_5G[9] =
- read_phy_reg(pi, 0x1ab);
+ read_phy_reg(pi, 0x1ab);
pi->rssical_cache.rssical_phyregs_5G[10] =
- read_phy_reg(pi, 0x1b1);
+ read_phy_reg(pi, 0x1b1);
pi->rssical_cache.rssical_phyregs_5G[11] =
- read_phy_reg(pi, 0x1b7);
+ read_phy_reg(pi, 0x1b7);
pi->nphy_rssical_chanspec_5G = pi->radio_chanspec;
}
@@ -22201,200 +23008,269 @@ static void wlc_phy_rssi_cal_nphy_rev3(phy_info_t *pi)
wlc_phy_clip_det_nphy(pi, 1, clip_state);
}
-static void wlc_phy_restore_rssical_nphy(phy_info_t *pi)
+static void wlc_phy_rssi_cal_nphy_rev2(struct brcms_phy *pi, u8 rssi_type)
{
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if (pi->nphy_rssical_chanspec_2G == 0)
- return;
+ s32 target_code;
+ u16 classif_state;
+ u16 clip_state[2];
+ u16 rssi_ctrl_state[2], pd_state[2];
+ u16 rfctrlintc_state[2], rfpdcorerxtx_state[2];
+ u16 rfctrlintc_override_val;
+ u16 clip_off[] = { 0xffff, 0xffff };
+ u16 rf_pd_val, pd_mask, rssi_ctrl_mask;
+ u8 vcm, min_vcm, vcm_tmp[4];
+ u8 vcm_final[4] = { 0, 0, 0, 0 };
+ u8 result_idx, ctr;
+ s32 poll_results[4][4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+ };
+ s32 poll_miniq[4][2] = {
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0}
+ };
+ s32 min_d, curr_d;
+ s32 fine_digital_offset[4];
+ s32 poll_results_min[4] = { 0, 0, 0, 0 };
+ s32 min_poll;
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0,
- RADIO_2057_VCM_MASK,
- pi->rssical_cache.
- rssical_radio_regs_2G[0]);
- mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1,
- RADIO_2057_VCM_MASK,
- pi->rssical_cache.
- rssical_radio_regs_2G[1]);
- } else {
- mod_radio_reg(pi,
- RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0,
- RADIO_2056_VCM_MASK,
- pi->rssical_cache.
- rssical_radio_regs_2G[0]);
- mod_radio_reg(pi,
- RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1,
- RADIO_2056_VCM_MASK,
- pi->rssical_cache.
- rssical_radio_regs_2G[1]);
- }
+ switch (rssi_type) {
+ case NPHY_RSSI_SEL_NB:
+ target_code = NPHY_RSSICAL_NB_TARGET;
+ break;
+ case NPHY_RSSI_SEL_W1:
+ target_code = NPHY_RSSICAL_W1_TARGET;
+ break;
+ case NPHY_RSSI_SEL_W2:
+ target_code = NPHY_RSSICAL_W2_TARGET;
+ break;
+ default:
+ return;
+ break;
+ }
- write_phy_reg(pi, 0x1a6,
- pi->rssical_cache.rssical_phyregs_2G[0]);
- write_phy_reg(pi, 0x1ac,
- pi->rssical_cache.rssical_phyregs_2G[1]);
- write_phy_reg(pi, 0x1b2,
- pi->rssical_cache.rssical_phyregs_2G[2]);
- write_phy_reg(pi, 0x1b8,
- pi->rssical_cache.rssical_phyregs_2G[3]);
- write_phy_reg(pi, 0x1a4,
- pi->rssical_cache.rssical_phyregs_2G[4]);
- write_phy_reg(pi, 0x1aa,
- pi->rssical_cache.rssical_phyregs_2G[5]);
- write_phy_reg(pi, 0x1b0,
- pi->rssical_cache.rssical_phyregs_2G[6]);
- write_phy_reg(pi, 0x1b6,
- pi->rssical_cache.rssical_phyregs_2G[7]);
- write_phy_reg(pi, 0x1a5,
- pi->rssical_cache.rssical_phyregs_2G[8]);
- write_phy_reg(pi, 0x1ab,
- pi->rssical_cache.rssical_phyregs_2G[9]);
- write_phy_reg(pi, 0x1b1,
- pi->rssical_cache.rssical_phyregs_2G[10]);
- write_phy_reg(pi, 0x1b7,
- pi->rssical_cache.rssical_phyregs_2G[11]);
+ classif_state = wlc_phy_classifier_nphy(pi, 0, 0);
+ wlc_phy_classifier_nphy(pi, (0x7 << 0), 4);
+ wlc_phy_clip_det_nphy(pi, 0, clip_state);
+ wlc_phy_clip_det_nphy(pi, 1, clip_off);
- } else {
- if (pi->nphy_rssical_chanspec_5G == 0)
- return;
+ rf_pd_val = (rssi_type == NPHY_RSSI_SEL_NB) ? 0x6 : 0x4;
+ rfctrlintc_override_val =
+ CHSPEC_IS5G(pi->radio_chanspec) ? 0x140 : 0x110;
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0,
- RADIO_2057_VCM_MASK,
- pi->rssical_cache.
- rssical_radio_regs_5G[0]);
- mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1,
- RADIO_2057_VCM_MASK,
- pi->rssical_cache.
- rssical_radio_regs_5G[1]);
- } else {
- mod_radio_reg(pi,
- RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0,
- RADIO_2056_VCM_MASK,
- pi->rssical_cache.
- rssical_radio_regs_5G[0]);
- mod_radio_reg(pi,
- RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1,
- RADIO_2056_VCM_MASK,
- pi->rssical_cache.
- rssical_radio_regs_5G[1]);
- }
+ rfctrlintc_state[0] = read_phy_reg(pi, 0x91);
+ rfpdcorerxtx_state[0] = read_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX);
+ write_phy_reg(pi, 0x91, rfctrlintc_override_val);
+ write_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX, rf_pd_val);
- write_phy_reg(pi, 0x1a6,
- pi->rssical_cache.rssical_phyregs_5G[0]);
- write_phy_reg(pi, 0x1ac,
- pi->rssical_cache.rssical_phyregs_5G[1]);
- write_phy_reg(pi, 0x1b2,
- pi->rssical_cache.rssical_phyregs_5G[2]);
- write_phy_reg(pi, 0x1b8,
- pi->rssical_cache.rssical_phyregs_5G[3]);
- write_phy_reg(pi, 0x1a4,
- pi->rssical_cache.rssical_phyregs_5G[4]);
- write_phy_reg(pi, 0x1aa,
- pi->rssical_cache.rssical_phyregs_5G[5]);
- write_phy_reg(pi, 0x1b0,
- pi->rssical_cache.rssical_phyregs_5G[6]);
- write_phy_reg(pi, 0x1b6,
- pi->rssical_cache.rssical_phyregs_5G[7]);
- write_phy_reg(pi, 0x1a5,
- pi->rssical_cache.rssical_phyregs_5G[8]);
- write_phy_reg(pi, 0x1ab,
- pi->rssical_cache.rssical_phyregs_5G[9]);
- write_phy_reg(pi, 0x1b1,
- pi->rssical_cache.rssical_phyregs_5G[10]);
- write_phy_reg(pi, 0x1b7,
- pi->rssical_cache.rssical_phyregs_5G[11]);
- }
-}
+ rfctrlintc_state[1] = read_phy_reg(pi, 0x92);
+ rfpdcorerxtx_state[1] = read_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX);
+ write_phy_reg(pi, 0x92, rfctrlintc_override_val);
+ write_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX, rf_pd_val);
-static u16
-wlc_phy_gen_load_samples_nphy(phy_info_t *pi, u32 f_kHz, u16 max_val,
- u8 dac_test_mode)
-{
- u8 phy_bw, is_phybw40;
- u16 num_samps, t, spur;
- fixed theta = 0, rot = 0;
- u32 tbl_len;
- cs32 *tone_buf = NULL;
+ pd_mask = RADIO_2055_NBRSSI_PD | RADIO_2055_WBRSSI_G1_PD |
+ RADIO_2055_WBRSSI_G2_PD;
+ pd_state[0] =
+ read_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC) & pd_mask;
+ pd_state[1] =
+ read_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC) & pd_mask;
+ mod_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC, pd_mask, 0);
+ mod_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC, pd_mask, 0);
+ rssi_ctrl_mask = RADIO_2055_NBRSSI_SEL | RADIO_2055_WBRSSI_G1_SEL |
+ RADIO_2055_WBRSSI_G2_SEL;
+ rssi_ctrl_state[0] =
+ read_radio_reg(pi, RADIO_2055_SP_RSSI_CORE1) & rssi_ctrl_mask;
+ rssi_ctrl_state[1] =
+ read_radio_reg(pi, RADIO_2055_SP_RSSI_CORE2) & rssi_ctrl_mask;
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_ALLRX, rssi_type);
- is_phybw40 = CHSPEC_IS40(pi->radio_chanspec);
- phy_bw = (is_phybw40 == 1) ? 40 : 20;
- tbl_len = (phy_bw << 3);
+ wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, RADIO_MIMO_CORESEL_ALLRX,
+ NPHY_RAIL_I, rssi_type);
+ wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, RADIO_MIMO_CORESEL_ALLRX,
+ NPHY_RAIL_Q, rssi_type);
- if (dac_test_mode == 1) {
- spur = read_phy_reg(pi, 0x01);
- spur = (spur >> 15) & 1;
- phy_bw = (spur == 1) ? 82 : 80;
- phy_bw = (is_phybw40 == 1) ? (phy_bw << 1) : phy_bw;
+ for (vcm = 0; vcm < 4; vcm++) {
- tbl_len = (phy_bw << 1);
- }
+ vcm_tmp[0] = vcm_tmp[1] = vcm_tmp[2] = vcm_tmp[3] = vcm;
+ if (rssi_type != NPHY_RSSI_SEL_W2)
+ wlc_phy_set_rssi_2055_vcm(pi, rssi_type, vcm_tmp);
- tone_buf = kmalloc(sizeof(cs32) * tbl_len, GFP_ATOMIC);
- if (tone_buf == NULL) {
- return 0;
+ wlc_phy_poll_rssi_nphy(pi, rssi_type, &poll_results[vcm][0],
+ NPHY_RSSICAL_NPOLL);
+
+ if ((rssi_type == NPHY_RSSI_SEL_W1)
+ || (rssi_type == NPHY_RSSI_SEL_W2)) {
+ for (ctr = 0; ctr < 2; ctr++)
+ poll_miniq[vcm][ctr] =
+ min(poll_results[vcm][ctr * 2 + 0],
+ poll_results[vcm][ctr * 2 + 1]);
+ }
}
- num_samps = (u16) tbl_len;
- rot = FIXED((f_kHz * 36) / phy_bw) / 100;
- theta = 0;
+ for (result_idx = 0; result_idx < 4; result_idx++) {
+ min_d = NPHY_RSSICAL_MAXD;
+ min_vcm = 0;
+ min_poll = NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL + 1;
+ for (vcm = 0; vcm < 4; vcm++) {
+ curr_d = abs(((rssi_type == NPHY_RSSI_SEL_NB) ?
+ poll_results[vcm][result_idx] :
+ poll_miniq[vcm][result_idx / 2]) -
+ (target_code * NPHY_RSSICAL_NPOLL));
+ if (curr_d < min_d) {
+ min_d = curr_d;
+ min_vcm = vcm;
+ }
+ if (poll_results[vcm][result_idx] < min_poll)
+ min_poll = poll_results[vcm][result_idx];
+ }
+ vcm_final[result_idx] = min_vcm;
+ poll_results_min[result_idx] = min_poll;
+ }
- for (t = 0; t < num_samps; t++) {
+ if (rssi_type != NPHY_RSSI_SEL_W2)
+ wlc_phy_set_rssi_2055_vcm(pi, rssi_type, vcm_final);
- wlc_phy_cordic(theta, &tone_buf[t]);
+ for (result_idx = 0; result_idx < 4; result_idx++) {
+ fine_digital_offset[result_idx] =
+ (target_code * NPHY_RSSICAL_NPOLL) -
+ poll_results[vcm_final[result_idx]][result_idx];
+ if (fine_digital_offset[result_idx] < 0) {
+ fine_digital_offset[result_idx] =
+ abs(fine_digital_offset[result_idx]);
+ fine_digital_offset[result_idx] +=
+ (NPHY_RSSICAL_NPOLL / 2);
+ fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL;
+ fine_digital_offset[result_idx] =
+ -fine_digital_offset[result_idx];
+ } else {
+ fine_digital_offset[result_idx] +=
+ (NPHY_RSSICAL_NPOLL / 2);
+ fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL;
+ }
- theta += rot;
+ if (poll_results_min[result_idx] ==
+ NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL)
+ fine_digital_offset[result_idx] =
+ (target_code - NPHY_RSSICAL_MAXREAD - 1);
- tone_buf[t].q = (s32) FLOAT(tone_buf[t].q * max_val);
- tone_buf[t].i = (s32) FLOAT(tone_buf[t].i * max_val);
+ wlc_phy_scale_offset_rssi_nphy(pi, 0x0,
+ (s8)
+ fine_digital_offset[result_idx],
+ (result_idx / 2 ==
+ 0) ? RADIO_MIMO_CORESEL_CORE1 :
+ RADIO_MIMO_CORESEL_CORE2,
+ (result_idx % 2 ==
+ 0) ? NPHY_RAIL_I : NPHY_RAIL_Q,
+ rssi_type);
}
- wlc_phy_loadsampletable_nphy(pi, tone_buf, num_samps);
+ mod_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC, pd_mask, pd_state[0]);
+ mod_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC, pd_mask, pd_state[1]);
+ if (rssi_ctrl_state[0] == RADIO_2055_NBRSSI_SEL)
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
+ NPHY_RSSI_SEL_NB);
+ else if (rssi_ctrl_state[0] == RADIO_2055_WBRSSI_G1_SEL)
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
+ NPHY_RSSI_SEL_W1);
+ else if (rssi_ctrl_state[0] == RADIO_2055_WBRSSI_G2_SEL)
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
+ NPHY_RSSI_SEL_W2);
+ else
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
+ NPHY_RSSI_SEL_W2);
+ if (rssi_ctrl_state[1] == RADIO_2055_NBRSSI_SEL)
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
+ NPHY_RSSI_SEL_NB);
+ else if (rssi_ctrl_state[1] == RADIO_2055_WBRSSI_G1_SEL)
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
+ NPHY_RSSI_SEL_W1);
+ else if (rssi_ctrl_state[1] == RADIO_2055_WBRSSI_G2_SEL)
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
+ NPHY_RSSI_SEL_W2);
+ else
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
+ NPHY_RSSI_SEL_W2);
- kfree(tone_buf);
+ wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, rssi_type);
- return num_samps;
+ write_phy_reg(pi, 0x91, rfctrlintc_state[0]);
+ write_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX, rfpdcorerxtx_state[0]);
+ write_phy_reg(pi, 0x92, rfctrlintc_state[1]);
+ write_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX, rfpdcorerxtx_state[1]);
+
+ wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state);
+ wlc_phy_clip_det_nphy(pi, 1, clip_state);
+
+ wlc_phy_resetcca_nphy(pi);
+}
+
+void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi)
+{
+ if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ wlc_phy_rssi_cal_nphy_rev3(pi);
+ } else {
+ wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_NB);
+ wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_W1);
+ wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_W2);
+ }
}
int
-wlc_phy_tx_tone_nphy(phy_info_t *pi, u32 f_kHz, u16 max_val,
- u8 iqmode, u8 dac_test_mode, bool modify_bbmult)
+wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, struct d11rxhdr *rxh)
{
- u16 num_samps;
- u16 loops = 0xffff;
- u16 wait = 0;
+ s16 rxpwr, rxpwr0, rxpwr1;
+ s16 phyRx0_l, phyRx2_l;
- num_samps =
- wlc_phy_gen_load_samples_nphy(pi, f_kHz, max_val, dac_test_mode);
- if (num_samps == 0) {
- return -EBADE;
+ rxpwr = 0;
+ rxpwr0 = rxh->PhyRxStatus_1 & PRXS1_nphy_PWR0_MASK;
+ rxpwr1 = (rxh->PhyRxStatus_1 & PRXS1_nphy_PWR1_MASK) >> 8;
+
+ if (rxpwr0 > 127)
+ rxpwr0 -= 256;
+ if (rxpwr1 > 127)
+ rxpwr1 -= 256;
+
+ phyRx0_l = rxh->PhyRxStatus_0 & 0x00ff;
+ phyRx2_l = rxh->PhyRxStatus_2 & 0x00ff;
+ if (phyRx2_l > 127)
+ phyRx2_l -= 256;
+
+ if (((rxpwr0 == 16) || (rxpwr0 == 32))) {
+ rxpwr0 = rxpwr1;
+ rxpwr1 = phyRx2_l;
}
- wlc_phy_runsamples_nphy(pi, num_samps, loops, wait, iqmode,
- dac_test_mode, modify_bbmult);
+ if (pi->sh->rssi_mode == RSSI_ANT_MERGE_MAX)
+ rxpwr = (rxpwr0 > rxpwr1) ? rxpwr0 : rxpwr1;
+ else if (pi->sh->rssi_mode == RSSI_ANT_MERGE_MIN)
+ rxpwr = (rxpwr0 < rxpwr1) ? rxpwr0 : rxpwr1;
+ else if (pi->sh->rssi_mode == RSSI_ANT_MERGE_AVG)
+ rxpwr = (rxpwr0 + rxpwr1) >> 1;
- return 0;
+ return rxpwr;
}
static void
-wlc_phy_loadsampletable_nphy(phy_info_t *pi, cs32 *tone_buf,
+wlc_phy_loadsampletable_nphy(struct brcms_phy *pi, struct cordic_iq *tone_buf,
u16 num_samps)
{
u16 t;
u32 *data_buf = NULL;
data_buf = kmalloc(sizeof(u32) * num_samps, GFP_ATOMIC);
- if (data_buf == NULL) {
+ if (data_buf == NULL)
return;
- }
if (pi->phyhang_avoid)
wlc_phy_stay_in_carriersearch_nphy(pi, true);
- for (t = 0; t < num_samps; t++) {
+ for (t = 0; t < num_samps; t++)
data_buf[t] = ((((unsigned int)tone_buf[t].i) & 0x3ff) << 10) |
- (((unsigned int)tone_buf[t].q) & 0x3ff);
- }
+ (((unsigned int)tone_buf[t].q) & 0x3ff);
wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SAMPLEPLAY, num_samps, 0, 32,
data_buf);
@@ -22404,8 +23280,56 @@ wlc_phy_loadsampletable_nphy(phy_info_t *pi, cs32 *tone_buf,
wlc_phy_stay_in_carriersearch_nphy(pi, false);
}
+static u16
+wlc_phy_gen_load_samples_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val,
+ u8 dac_test_mode)
+{
+ u8 phy_bw, is_phybw40;
+ u16 num_samps, t, spur;
+ s32 theta = 0, rot = 0;
+ u32 tbl_len;
+ struct cordic_iq *tone_buf = NULL;
+
+ is_phybw40 = CHSPEC_IS40(pi->radio_chanspec);
+ phy_bw = (is_phybw40 == 1) ? 40 : 20;
+ tbl_len = (phy_bw << 3);
+
+ if (dac_test_mode == 1) {
+ spur = read_phy_reg(pi, 0x01);
+ spur = (spur >> 15) & 1;
+ phy_bw = (spur == 1) ? 82 : 80;
+ phy_bw = (is_phybw40 == 1) ? (phy_bw << 1) : phy_bw;
+
+ tbl_len = (phy_bw << 1);
+ }
+
+ tone_buf = kmalloc(sizeof(struct cordic_iq) * tbl_len, GFP_ATOMIC);
+ if (tone_buf == NULL)
+ return 0;
+
+ num_samps = (u16) tbl_len;
+ rot = ((f_kHz * 36) / phy_bw) / 100;
+ theta = 0;
+
+ for (t = 0; t < num_samps; t++) {
+
+ tone_buf[t] = cordic_calc_iq(theta);
+
+ theta += rot;
+
+ tone_buf[t].q = (s32) FLOAT(tone_buf[t].q * max_val);
+ tone_buf[t].i = (s32) FLOAT(tone_buf[t].i * max_val);
+ }
+
+ wlc_phy_loadsampletable_nphy(pi, tone_buf, num_samps);
+
+ kfree(tone_buf);
+
+ return num_samps;
+}
+
static void
-wlc_phy_runsamples_nphy(phy_info_t *pi, u16 num_samps, u16 loops,
+wlc_phy_runsamples_nphy(struct brcms_phy *pi, u16 num_samps, u16 loops,
u16 wait, u8 iqmode, u8 dac_test_mode,
bool modify_bbmult)
{
@@ -22428,22 +23352,24 @@ wlc_phy_runsamples_nphy(phy_info_t *pi, u16 num_samps, u16 loops,
lpf_bw_ctl_override4 = read_phy_reg(pi, 0x343) & (0x1 << 7);
if (lpf_bw_ctl_override3 | lpf_bw_ctl_override4) {
lpf_bw_ctl_miscreg3 = read_phy_reg(pi, 0x340) &
- (0x7 << 8);
+ (0x7 << 8);
lpf_bw_ctl_miscreg4 = read_phy_reg(pi, 0x341) &
- (0x7 << 8);
+ (0x7 << 8);
} else {
- wlc_phy_rfctrl_override_nphy_rev7(pi,
- (0x1 << 7),
- wlc_phy_read_lpf_bw_ctl_nphy
- (pi, 0), 0, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi,
+ (0x1 << 7),
+ wlc_phy_read_lpf_bw_ctl_nphy
+ (pi,
+ 0), 0, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
pi->nphy_sample_play_lpf_bw_ctl_ovr = true;
lpf_bw_ctl_miscreg3 = read_phy_reg(pi, 0x340) &
- (0x7 << 8);
+ (0x7 << 8);
lpf_bw_ctl_miscreg4 = read_phy_reg(pi, 0x341) &
- (0x7 << 8);
+ (0x7 << 8);
}
}
@@ -22452,7 +23378,7 @@ wlc_phy_runsamples_nphy(phy_info_t *pi, u16 num_samps, u16 loops,
wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 87, 16,
&bb_mult);
pi->nphy_bb_mult_save =
- BB_MULT_VALID_MASK | (bb_mult & BB_MULT_MASK);
+ BB_MULT_VALID_MASK | (bb_mult & BB_MULT_MASK);
}
if (modify_bbmult) {
@@ -22467,11 +23393,11 @@ wlc_phy_runsamples_nphy(phy_info_t *pi, u16 num_samps, u16 loops,
write_phy_reg(pi, 0xc6, num_samps - 1);
- if (loops != 0xffff) {
+ if (loops != 0xffff)
write_phy_reg(pi, 0xc4, loops - 1);
- } else {
+ else
write_phy_reg(pi, 0xc4, loops);
- }
+
write_phy_reg(pi, 0xc5, wait);
orig_RfseqCoreActv = read_phy_reg(pi, 0xa1);
@@ -22492,7 +23418,26 @@ wlc_phy_runsamples_nphy(phy_info_t *pi, u16 num_samps, u16 loops,
write_phy_reg(pi, 0xa1, orig_RfseqCoreActv);
}
-void wlc_phy_stopplayback_nphy(phy_info_t *pi)
+int
+wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val,
+ u8 iqmode, u8 dac_test_mode, bool modify_bbmult)
+{
+ u16 num_samps;
+ u16 loops = 0xffff;
+ u16 wait = 0;
+
+ num_samps = wlc_phy_gen_load_samples_nphy(pi, f_kHz, max_val,
+ dac_test_mode);
+ if (num_samps == 0)
+ return -EBADE;
+
+ wlc_phy_runsamples_nphy(pi, num_samps, loops, wait, iqmode,
+ dac_test_mode, modify_bbmult);
+
+ return 0;
+}
+
+void wlc_phy_stopplayback_nphy(struct brcms_phy *pi)
{
u16 playback_status;
u16 bb_mult;
@@ -22501,13 +23446,11 @@ void wlc_phy_stopplayback_nphy(phy_info_t *pi)
wlc_phy_stay_in_carriersearch_nphy(pi, true);
playback_status = read_phy_reg(pi, 0xc7);
- if (playback_status & 0x1) {
+ if (playback_status & 0x1)
or_phy_reg(pi, 0xc3, NPHY_sampleCmd_STOP);
- } else if (playback_status & 0x2) {
-
+ else if (playback_status & 0x2)
and_phy_reg(pi, 0xc2,
(u16) ~NPHY_iqloCalCmdGctl_IQLO_CAL_EN);
- }
and_phy_reg(pi, 0xc3, (u16) ~(0x1 << 2));
@@ -22522,10 +23465,11 @@ void wlc_phy_stopplayback_nphy(phy_info_t *pi)
if (NREV_IS(pi->pubpi.phy_rev, 7) || NREV_GE(pi->pubpi.phy_rev, 8)) {
if (pi->nphy_sample_play_lpf_bw_ctl_ovr) {
- wlc_phy_rfctrl_override_nphy_rev7(pi,
- (0x1 << 7),
- 0, 0, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi,
+ (0x1 << 7),
+ 0, 0, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
pi->nphy_sample_play_lpf_bw_ctl_ovr = false;
}
}
@@ -22534,11 +23478,52 @@ void wlc_phy_stopplayback_nphy(phy_info_t *pi)
wlc_phy_stay_in_carriersearch_nphy(pi, false);
}
-nphy_txgains_t wlc_phy_get_tx_gain_nphy(phy_info_t *pi)
+static u32 *brcms_phy_get_tx_pwrctrl_tbl(struct brcms_phy *pi)
+{
+ u32 *tx_pwrctrl_tbl = NULL;
+ uint phyrev = pi->pubpi.phy_rev;
+
+ if (PHY_IPA(pi)) {
+ tx_pwrctrl_tbl =
+ wlc_phy_get_ipa_gaintbl_nphy(pi);
+ } else {
+ if (CHSPEC_IS5G(pi->radio_chanspec)) {
+ if (NREV_IS(phyrev, 3))
+ tx_pwrctrl_tbl = nphy_tpc_5GHz_txgain_rev3;
+ else if (NREV_IS(phyrev, 4))
+ tx_pwrctrl_tbl =
+ (pi->srom_fem5g.extpagain == 3) ?
+ nphy_tpc_5GHz_txgain_HiPwrEPA :
+ nphy_tpc_5GHz_txgain_rev4;
+ else
+ tx_pwrctrl_tbl = nphy_tpc_5GHz_txgain_rev5;
+ } else {
+ if (NREV_GE(phyrev, 7)) {
+ if (pi->pubpi.radiorev == 3)
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_epa_2057rev3;
+ else if (pi->pubpi.radiorev == 5)
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_epa_2057rev5;
+ } else {
+ if (NREV_GE(phyrev, 5) &&
+ (pi->srom_fem2g.extpagain == 3))
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_HiPwrEPA;
+ else
+ tx_pwrctrl_tbl =
+ nphy_tpc_txgain_rev3;
+ }
+ }
+ }
+ return tx_pwrctrl_tbl;
+}
+
+struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi)
{
u16 base_idx[2], curr_gain[2];
u8 core_no;
- nphy_txgains_t target_gain;
+ struct nphy_txgains target_gain;
u32 *tx_pwrctrl_tbl = NULL;
if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) {
@@ -22554,136 +23539,96 @@ nphy_txgains_t wlc_phy_get_tx_gain_nphy(phy_info_t *pi)
for (core_no = 0; core_no < 2; core_no++) {
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
target_gain.ipa[core_no] =
- curr_gain[core_no] & 0x0007;
+ curr_gain[core_no] & 0x0007;
target_gain.pad[core_no] =
- ((curr_gain[core_no] & 0x00F8) >> 3);
+ ((curr_gain[core_no] & 0x00F8) >> 3);
target_gain.pga[core_no] =
- ((curr_gain[core_no] & 0x0F00) >> 8);
+ ((curr_gain[core_no] & 0x0F00) >> 8);
target_gain.txgm[core_no] =
- ((curr_gain[core_no] & 0x7000) >> 12);
+ ((curr_gain[core_no] & 0x7000) >> 12);
target_gain.txlpf[core_no] =
- ((curr_gain[core_no] & 0x8000) >> 15);
+ ((curr_gain[core_no] & 0x8000) >> 15);
} else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
target_gain.ipa[core_no] =
- curr_gain[core_no] & 0x000F;
+ curr_gain[core_no] & 0x000F;
target_gain.pad[core_no] =
- ((curr_gain[core_no] & 0x00F0) >> 4);
+ ((curr_gain[core_no] & 0x00F0) >> 4);
target_gain.pga[core_no] =
- ((curr_gain[core_no] & 0x0F00) >> 8);
+ ((curr_gain[core_no] & 0x0F00) >> 8);
target_gain.txgm[core_no] =
- ((curr_gain[core_no] & 0x7000) >> 12);
+ ((curr_gain[core_no] & 0x7000) >> 12);
} else {
target_gain.ipa[core_no] =
- curr_gain[core_no] & 0x0003;
+ curr_gain[core_no] & 0x0003;
target_gain.pad[core_no] =
- ((curr_gain[core_no] & 0x000C) >> 2);
+ ((curr_gain[core_no] & 0x000C) >> 2);
target_gain.pga[core_no] =
- ((curr_gain[core_no] & 0x0070) >> 4);
+ ((curr_gain[core_no] & 0x0070) >> 4);
target_gain.txgm[core_no] =
- ((curr_gain[core_no] & 0x0380) >> 7);
+ ((curr_gain[core_no] & 0x0380) >> 7);
}
}
} else {
+ uint phyrev = pi->pubpi.phy_rev;
+
base_idx[0] = (read_phy_reg(pi, 0x1ed) >> 8) & 0x7f;
base_idx[1] = (read_phy_reg(pi, 0x1ee) >> 8) & 0x7f;
for (core_no = 0; core_no < 2; core_no++) {
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- if (PHY_IPA(pi)) {
- tx_pwrctrl_tbl =
- wlc_phy_get_ipa_gaintbl_nphy(pi);
- } else {
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
- if NREV_IS
- (pi->pubpi.phy_rev, 3) {
- tx_pwrctrl_tbl =
- nphy_tpc_5GHz_txgain_rev3;
- } else if NREV_IS
- (pi->pubpi.phy_rev, 4) {
- tx_pwrctrl_tbl =
- (pi->srom_fem5g.
- extpagain ==
- 3) ?
- nphy_tpc_5GHz_txgain_HiPwrEPA
- :
- nphy_tpc_5GHz_txgain_rev4;
- } else {
- tx_pwrctrl_tbl =
- nphy_tpc_5GHz_txgain_rev5;
- }
- } else {
- if (NREV_GE
- (pi->pubpi.phy_rev, 7)) {
- if (pi->pubpi.
- radiorev == 3) {
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_epa_2057rev3;
- } else if (pi->pubpi.
- radiorev ==
- 5) {
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_epa_2057rev5;
- }
-
- } else {
- if (NREV_GE
- (pi->pubpi.phy_rev,
- 5)
- && (pi->srom_fem2g.
- extpagain ==
- 3)) {
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_HiPwrEPA;
- } else {
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_rev3;
- }
- }
- }
- }
- if NREV_GE
- (pi->pubpi.phy_rev, 7) {
+ if (NREV_GE(phyrev, 3)) {
+ tx_pwrctrl_tbl =
+ brcms_phy_get_tx_pwrctrl_tbl(pi);
+ if (NREV_GE(phyrev, 7)) {
target_gain.ipa[core_no] =
- (tx_pwrctrl_tbl[base_idx[core_no]]
- >> 16) & 0x7;
+ (tx_pwrctrl_tbl
+ [base_idx[core_no]]
+ >> 16) & 0x7;
target_gain.pad[core_no] =
- (tx_pwrctrl_tbl[base_idx[core_no]]
- >> 19) & 0x1f;
+ (tx_pwrctrl_tbl
+ [base_idx[core_no]]
+ >> 19) & 0x1f;
target_gain.pga[core_no] =
- (tx_pwrctrl_tbl[base_idx[core_no]]
- >> 24) & 0xf;
+ (tx_pwrctrl_tbl
+ [base_idx[core_no]]
+ >> 24) & 0xf;
target_gain.txgm[core_no] =
- (tx_pwrctrl_tbl[base_idx[core_no]]
- >> 28) & 0x7;
+ (tx_pwrctrl_tbl
+ [base_idx[core_no]]
+ >> 28) & 0x7;
target_gain.txlpf[core_no] =
- (tx_pwrctrl_tbl[base_idx[core_no]]
- >> 31) & 0x1;
+ (tx_pwrctrl_tbl
+ [base_idx[core_no]]
+ >> 31) & 0x1;
} else {
target_gain.ipa[core_no] =
- (tx_pwrctrl_tbl[base_idx[core_no]]
- >> 16) & 0xf;
+ (tx_pwrctrl_tbl
+ [base_idx[core_no]]
+ >> 16) & 0xf;
target_gain.pad[core_no] =
- (tx_pwrctrl_tbl[base_idx[core_no]]
- >> 20) & 0xf;
+ (tx_pwrctrl_tbl
+ [base_idx[core_no]]
+ >> 20) & 0xf;
target_gain.pga[core_no] =
- (tx_pwrctrl_tbl[base_idx[core_no]]
- >> 24) & 0xf;
+ (tx_pwrctrl_tbl
+ [base_idx[core_no]]
+ >> 24) & 0xf;
target_gain.txgm[core_no] =
- (tx_pwrctrl_tbl[base_idx[core_no]]
- >> 28) & 0x7;
+ (tx_pwrctrl_tbl
+ [base_idx[core_no]]
+ >> 28) & 0x7;
}
} else {
target_gain.ipa[core_no] =
- (nphy_tpc_txgain[base_idx[core_no]] >> 16) &
- 0x3;
+ (nphy_tpc_txgain[base_idx[core_no]] >>
+ 16) & 0x3;
target_gain.pad[core_no] =
- (nphy_tpc_txgain[base_idx[core_no]] >> 18) &
- 0x3;
+ (nphy_tpc_txgain[base_idx[core_no]] >>
+ 18) & 0x3;
target_gain.pga[core_no] =
- (nphy_tpc_txgain[base_idx[core_no]] >> 20) &
- 0x7;
+ (nphy_tpc_txgain[base_idx[core_no]] >>
+ 20) & 0x7;
target_gain.txgm[core_no] =
- (nphy_tpc_txgain[base_idx[core_no]] >> 23) &
- 0x7;
+ (nphy_tpc_txgain[base_idx[core_no]] >>
+ 23) & 0x7;
}
}
}
@@ -22692,9 +23637,9 @@ nphy_txgains_t wlc_phy_get_tx_gain_nphy(phy_info_t *pi)
}
static void
-wlc_phy_iqcal_gainparams_nphy(phy_info_t *pi, u16 core_no,
- nphy_txgains_t target_gain,
- nphy_iqcal_params_t *params)
+wlc_phy_iqcal_gainparams_nphy(struct brcms_phy *pi, u16 core_no,
+ struct nphy_txgains target_gain,
+ struct nphy_iqcal_params *params)
{
u8 k;
int idx;
@@ -22702,26 +23647,23 @@ wlc_phy_iqcal_gainparams_nphy(phy_info_t *pi, u16 core_no,
u8 band_idx = (CHSPEC_IS5G(pi->radio_chanspec) ? 1 : 0);
if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
params->txlpf = target_gain.txlpf[core_no];
- }
+
params->txgm = target_gain.txgm[core_no];
params->pga = target_gain.pga[core_no];
params->pad = target_gain.pad[core_no];
params->ipa = target_gain.ipa[core_no];
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
params->cal_gain =
- ((params->txlpf << 15) | (params->
- txgm << 12) | (params->
- pga << 8) |
- (params->pad << 3) | (params->ipa));
- } else {
+ ((params->txlpf << 15) | (params->txgm << 12) |
+ (params->pga << 8) |
+ (params->pad << 3) | (params->ipa));
+ else
params->cal_gain =
- ((params->txgm << 12) | (params->
- pga << 8) | (params->
- pad << 4) |
- (params->ipa));
- }
+ ((params->txgm << 12) | (params->pga << 8) |
+ (params->pad << 4) | (params->ipa));
+
params->ncorr[0] = 0x79;
params->ncorr[1] = 0x79;
params->ncorr[2] = 0x79;
@@ -22730,9 +23672,8 @@ wlc_phy_iqcal_gainparams_nphy(phy_info_t *pi, u16 core_no,
} else {
gain_index = ((target_gain.pad[core_no] << 0) |
- (target_gain.pga[core_no] << 4) | (target_gain.
- txgm[core_no]
- << 8));
+ (target_gain.pga[core_no] << 4) |
+ (target_gain.txgm[core_no] << 8));
idx = -1;
for (k = 0; k < NPHY_IQCAL_NUMGAINS; k++) {
@@ -22755,7 +23696,7 @@ wlc_phy_iqcal_gainparams_nphy(phy_info_t *pi, u16 core_no,
}
}
-static void wlc_phy_txcal_radio_setup_nphy(phy_info_t *pi)
+static void wlc_phy_txcal_radio_setup_nphy(struct brcms_phy *pi)
{
u16 jtag_core, core;
@@ -22764,37 +23705,39 @@ static void wlc_phy_txcal_radio_setup_nphy(phy_info_t *pi)
for (core = 0; core <= 1; core++) {
pi->tx_rx_cal_radio_saveregs[(core * 11) + 0] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core,
- TX_SSI_MASTER);
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TX_SSI_MASTER);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 1] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core,
- IQCAL_VCM_HG);
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core,
+ IQCAL_VCM_HG);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 2] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core,
- IQCAL_IDAC);
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core,
+ IQCAL_IDAC);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 3] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM);
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TSSI_VCM);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 4] = 0;
pi->tx_rx_cal_radio_saveregs[(core * 11) + 5] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core,
- TX_SSI_MUX);
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TX_SSI_MUX);
if (pi->pubpi.radiorev != 5)
pi->tx_rx_cal_radio_saveregs[(core * 11) + 6] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core,
- TSSIA);
+ READ_RADIO_REG3(pi, RADIO_2057, TX,
+ core,
+ TSSIA);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 7] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG);
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 8] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core,
- TSSI_MISC1);
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TSSI_MISC1);
if (CHSPEC_IS5G(pi->radio_chanspec)) {
WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
@@ -22810,19 +23753,15 @@ static void wlc_phy_txcal_radio_setup_nphy(phy_info_t *pi)
if (pi->use_int_tx_iqlo_cal_nphy) {
WRITE_RADIO_REG3(pi, RADIO_2057, TX,
core, TX_SSI_MUX, 0x4);
- if (!
- (pi->
- internal_tx_iqlo_cal_tapoff_intpa_nphy)) {
-
+ if (!(pi->
+ internal_tx_iqlo_cal_tapoff_intpa_nphy))
WRITE_RADIO_REG3(pi, RADIO_2057,
TX, core,
TSSIA, 0x31);
- } else {
-
+ else
WRITE_RADIO_REG3(pi, RADIO_2057,
TX, core,
TSSIA, 0x21);
- }
}
WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
TSSI_MISC1, 0x00);
@@ -22843,19 +23782,15 @@ static void wlc_phy_txcal_radio_setup_nphy(phy_info_t *pi)
WRITE_RADIO_REG3(pi, RADIO_2057, TX,
core, TX_SSI_MUX,
0x06);
- if (!
- (pi->
- internal_tx_iqlo_cal_tapoff_intpa_nphy)) {
-
+ if (!(pi->
+ internal_tx_iqlo_cal_tapoff_intpa_nphy))
WRITE_RADIO_REG3(pi, RADIO_2057,
TX, core,
TSSIG, 0x31);
- } else {
-
+ else
WRITE_RADIO_REG3(pi, RADIO_2057,
TX, core,
TSSIG, 0x21);
- }
}
WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
TSSI_MISC1, 0x00);
@@ -22865,58 +23800,62 @@ static void wlc_phy_txcal_radio_setup_nphy(phy_info_t *pi)
for (core = 0; core <= 1; core++) {
jtag_core =
- (core ==
- PHY_CORE_0) ? RADIO_2056_TX0 : RADIO_2056_TX1;
+ (core ==
+ PHY_CORE_0) ? RADIO_2056_TX0 : RADIO_2056_TX1;
pi->tx_rx_cal_radio_saveregs[(core * 11) + 0] =
- read_radio_reg(pi,
- RADIO_2056_TX_TX_SSI_MASTER |
- jtag_core);
+ read_radio_reg(pi,
+ RADIO_2056_TX_TX_SSI_MASTER |
+ jtag_core);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 1] =
- read_radio_reg(pi,
- RADIO_2056_TX_IQCAL_VCM_HG |
- jtag_core);
+ read_radio_reg(pi,
+ RADIO_2056_TX_IQCAL_VCM_HG |
+ jtag_core);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 2] =
- read_radio_reg(pi,
- RADIO_2056_TX_IQCAL_IDAC |
- jtag_core);
+ read_radio_reg(pi,
+ RADIO_2056_TX_IQCAL_IDAC |
+ jtag_core);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 3] =
- read_radio_reg(pi,
- RADIO_2056_TX_TSSI_VCM | jtag_core);
+ read_radio_reg(
+ pi,
+ RADIO_2056_TX_TSSI_VCM |
+ jtag_core);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 4] =
- read_radio_reg(pi,
- RADIO_2056_TX_TX_AMP_DET |
- jtag_core);
+ read_radio_reg(pi,
+ RADIO_2056_TX_TX_AMP_DET |
+ jtag_core);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 5] =
- read_radio_reg(pi,
- RADIO_2056_TX_TX_SSI_MUX |
- jtag_core);
+ read_radio_reg(pi,
+ RADIO_2056_TX_TX_SSI_MUX |
+ jtag_core);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 6] =
- read_radio_reg(pi, RADIO_2056_TX_TSSIA | jtag_core);
+ read_radio_reg(pi,
+ RADIO_2056_TX_TSSIA | jtag_core);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 7] =
- read_radio_reg(pi, RADIO_2056_TX_TSSIG | jtag_core);
+ read_radio_reg(pi,
+ RADIO_2056_TX_TSSIG | jtag_core);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 8] =
- read_radio_reg(pi,
- RADIO_2056_TX_TSSI_MISC1 |
- jtag_core);
+ read_radio_reg(pi,
+ RADIO_2056_TX_TSSI_MISC1 |
+ jtag_core);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 9] =
- read_radio_reg(pi,
- RADIO_2056_TX_TSSI_MISC2 |
- jtag_core);
+ read_radio_reg(pi,
+ RADIO_2056_TX_TSSI_MISC2 |
+ jtag_core);
pi->tx_rx_cal_radio_saveregs[(core * 11) + 10] =
- read_radio_reg(pi,
- RADIO_2056_TX_TSSI_MISC3 |
- jtag_core);
+ read_radio_reg(pi,
+ RADIO_2056_TX_TSSI_MISC3 |
+ jtag_core);
if (CHSPEC_IS5G(pi->radio_chanspec)) {
write_radio_reg(pi,
@@ -22936,16 +23875,18 @@ static void wlc_phy_txcal_radio_setup_nphy(phy_info_t *pi)
jtag_core, 0x00);
if (PHY_IPA(pi)) {
- write_radio_reg(pi,
- RADIO_2056_TX_TX_SSI_MUX
- | jtag_core, 0x4);
+ write_radio_reg(
+ pi,
+ RADIO_2056_TX_TX_SSI_MUX
+ | jtag_core, 0x4);
write_radio_reg(pi,
RADIO_2056_TX_TSSIA |
jtag_core, 0x1);
} else {
- write_radio_reg(pi,
- RADIO_2056_TX_TX_SSI_MUX
- | jtag_core, 0x00);
+ write_radio_reg(
+ pi,
+ RADIO_2056_TX_TX_SSI_MUX
+ | jtag_core, 0x00);
write_radio_reg(pi,
RADIO_2056_TX_TSSIA |
jtag_core, 0x2f);
@@ -22985,26 +23926,27 @@ static void wlc_phy_txcal_radio_setup_nphy(phy_info_t *pi)
if (PHY_IPA(pi)) {
- write_radio_reg(pi,
- RADIO_2056_TX_TX_SSI_MUX
- | jtag_core, 0x06);
- if (NREV_LT(pi->pubpi.phy_rev, 5)) {
-
- write_radio_reg(pi,
- RADIO_2056_TX_TSSIG
- | jtag_core,
- 0x11);
- } else {
-
- write_radio_reg(pi,
- RADIO_2056_TX_TSSIG
- | jtag_core,
- 0x1);
- }
+ write_radio_reg(
+ pi,
+ RADIO_2056_TX_TX_SSI_MUX
+ | jtag_core, 0x06);
+ if (NREV_LT(pi->pubpi.phy_rev, 5))
+ write_radio_reg(
+ pi,
+ RADIO_2056_TX_TSSIG
+ | jtag_core,
+ 0x11);
+ else
+ write_radio_reg(
+ pi,
+ RADIO_2056_TX_TSSIG
+ | jtag_core,
+ 0x1);
} else {
- write_radio_reg(pi,
- RADIO_2056_TX_TX_SSI_MUX
- | jtag_core, 0x00);
+ write_radio_reg(
+ pi,
+ RADIO_2056_TX_TX_SSI_MUX
+ | jtag_core, 0x00);
write_radio_reg(pi,
RADIO_2056_TX_TSSIG |
jtag_core, 0x20);
@@ -23024,23 +23966,23 @@ static void wlc_phy_txcal_radio_setup_nphy(phy_info_t *pi)
} else {
pi->tx_rx_cal_radio_saveregs[0] =
- read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1);
+ read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1);
write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, 0x29);
pi->tx_rx_cal_radio_saveregs[1] =
- read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2);
+ read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2);
write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, 0x54);
pi->tx_rx_cal_radio_saveregs[2] =
- read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1);
+ read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1);
write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, 0x29);
pi->tx_rx_cal_radio_saveregs[3] =
- read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2);
+ read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2);
write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, 0x54);
pi->tx_rx_cal_radio_saveregs[4] =
- read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1);
+ read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1);
pi->tx_rx_cal_radio_saveregs[5] =
- read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2);
+ read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2);
if ((read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand) ==
0) {
@@ -23065,7 +24007,7 @@ static void wlc_phy_txcal_radio_setup_nphy(phy_info_t *pi)
}
}
-static void wlc_phy_txcal_radio_cleanup_nphy(phy_info_t *pi)
+static void wlc_phy_txcal_radio_cleanup_nphy(struct brcms_phy *pi)
{
u16 jtag_core, core;
@@ -23101,11 +24043,8 @@ static void wlc_phy_txcal_radio_cleanup_nphy(phy_info_t *pi)
if (pi->pubpi.radiorev != 5)
WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
TSSIA,
- pi->
- tx_rx_cal_radio_saveregs[(core
- *
- 11) +
- 6]);
+ pi->tx_rx_cal_radio_saveregs
+ [(core * 11) + 6]);
WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG,
pi->
@@ -23119,9 +24058,8 @@ static void wlc_phy_txcal_radio_cleanup_nphy(phy_info_t *pi)
}
} else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
for (core = 0; core <= 1; core++) {
- jtag_core =
- (core ==
- PHY_CORE_0) ? RADIO_2056_TX0 : RADIO_2056_TX1;
+ jtag_core = (core == PHY_CORE_0) ?
+ RADIO_2056_TX0 : RADIO_2056_TX1;
write_radio_reg(pi,
RADIO_2056_TX_TX_SSI_MASTER | jtag_core,
@@ -23203,7 +24141,7 @@ static void wlc_phy_txcal_radio_cleanup_nphy(phy_info_t *pi)
}
}
-static void wlc_phy_txcal_physetup_nphy(phy_info_t *pi)
+static void wlc_phy_txcal_physetup_nphy(struct brcms_phy *pi)
{
u16 val, mask;
@@ -23247,23 +24185,22 @@ static void wlc_phy_txcal_physetup_nphy(phy_info_t *pi)
pi->tx_rx_cal_phy_saveregs[7] = read_phy_reg(pi, 0x91);
pi->tx_rx_cal_phy_saveregs[8] = read_phy_reg(pi, 0x92);
- if (!(pi->use_int_tx_iqlo_cal_nphy)) {
-
- wlc_phy_rfctrlintc_override_nphy(pi,
- NPHY_RfctrlIntc_override_PA,
- 1,
- RADIO_MIMO_CORESEL_CORE1
- |
- RADIO_MIMO_CORESEL_CORE2);
- } else {
-
- wlc_phy_rfctrlintc_override_nphy(pi,
- NPHY_RfctrlIntc_override_PA,
- 0,
- RADIO_MIMO_CORESEL_CORE1
- |
- RADIO_MIMO_CORESEL_CORE2);
- }
+ if (!(pi->use_int_tx_iqlo_cal_nphy))
+ wlc_phy_rfctrlintc_override_nphy(
+ pi,
+ NPHY_RfctrlIntc_override_PA,
+ 1,
+ RADIO_MIMO_CORESEL_CORE1
+ |
+ RADIO_MIMO_CORESEL_CORE2);
+ else
+ wlc_phy_rfctrlintc_override_nphy(
+ pi,
+ NPHY_RfctrlIntc_override_PA,
+ 0,
+ RADIO_MIMO_CORESEL_CORE1
+ |
+ RADIO_MIMO_CORESEL_CORE2);
wlc_phy_rfctrlintc_override_nphy(pi,
NPHY_RfctrlIntc_override_TRSW,
@@ -23281,12 +24218,13 @@ static void wlc_phy_txcal_physetup_nphy(phy_info_t *pi)
0x29b, (0x1 << 0), (0) << 0);
if (NREV_IS(pi->pubpi.phy_rev, 7)
- || NREV_GE(pi->pubpi.phy_rev, 8)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7),
- wlc_phy_read_lpf_bw_ctl_nphy
- (pi, 0), 0, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- }
+ || NREV_GE(pi->pubpi.phy_rev, 8))
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 7),
+ wlc_phy_read_lpf_bw_ctl_nphy
+ (pi,
+ 0), 0, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
if (pi->use_int_tx_iqlo_cal_nphy
&& !(pi->internal_tx_iqlo_cal_tapoff_intpa_nphy)) {
@@ -23297,25 +24235,30 @@ static void wlc_phy_txcal_physetup_nphy(phy_info_t *pi)
1 << 4);
if (CHSPEC_IS2G(pi->radio_chanspec)) {
- mod_radio_reg(pi,
- RADIO_2057_PAD2G_TUNE_PUS_CORE0,
- 1, 0);
- mod_radio_reg(pi,
- RADIO_2057_PAD2G_TUNE_PUS_CORE1,
- 1, 0);
+ mod_radio_reg(
+ pi,
+ RADIO_2057_PAD2G_TUNE_PUS_CORE0,
+ 1, 0);
+ mod_radio_reg(
+ pi,
+ RADIO_2057_PAD2G_TUNE_PUS_CORE1,
+ 1, 0);
} else {
- mod_radio_reg(pi,
- RADIO_2057_IPA5G_CASCOFFV_PU_CORE0,
- 1, 0);
- mod_radio_reg(pi,
- RADIO_2057_IPA5G_CASCOFFV_PU_CORE1,
- 1, 0);
+ mod_radio_reg(
+ pi,
+ RADIO_2057_IPA5G_CASCOFFV_PU_CORE0,
+ 1, 0);
+ mod_radio_reg(
+ pi,
+ RADIO_2057_IPA5G_CASCOFFV_PU_CORE1,
+ 1, 0);
}
} else if (NREV_GE(pi->pubpi.phy_rev, 8)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi,
- (0x1 << 3), 0,
- 0x3, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi,
+ (0x1 << 3), 0,
+ 0x3, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
}
}
} else {
@@ -23355,7 +24298,7 @@ static void wlc_phy_txcal_physetup_nphy(phy_info_t *pi)
}
}
-static void wlc_phy_txcal_phycleanup_nphy(phy_info_t *pi)
+static void wlc_phy_txcal_phycleanup_nphy(struct brcms_phy *pi)
{
u16 mask;
@@ -23378,11 +24321,11 @@ static void wlc_phy_txcal_phycleanup_nphy(phy_info_t *pi)
write_phy_reg(pi, 0x29b, pi->tx_rx_cal_phy_saveregs[10]);
if (NREV_IS(pi->pubpi.phy_rev, 7)
- || NREV_GE(pi->pubpi.phy_rev, 8)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7), 0, 0,
- 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- }
+ || NREV_GE(pi->pubpi.phy_rev, 8))
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 7), 0, 0,
+ 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
wlc_phy_resetcca_nphy(pi);
@@ -23391,28 +24334,33 @@ static void wlc_phy_txcal_phycleanup_nphy(phy_info_t *pi)
if (NREV_IS(pi->pubpi.phy_rev, 7)) {
if (CHSPEC_IS2G(pi->radio_chanspec)) {
- mod_radio_reg(pi,
- RADIO_2057_PAD2G_TUNE_PUS_CORE0,
- 1, 1);
- mod_radio_reg(pi,
- RADIO_2057_PAD2G_TUNE_PUS_CORE1,
- 1, 1);
+ mod_radio_reg(
+ pi,
+ RADIO_2057_PAD2G_TUNE_PUS_CORE0,
+ 1, 1);
+ mod_radio_reg(
+ pi,
+ RADIO_2057_PAD2G_TUNE_PUS_CORE1,
+ 1, 1);
} else {
- mod_radio_reg(pi,
- RADIO_2057_IPA5G_CASCOFFV_PU_CORE0,
- 1, 1);
- mod_radio_reg(pi,
- RADIO_2057_IPA5G_CASCOFFV_PU_CORE1,
- 1, 1);
+ mod_radio_reg(
+ pi,
+ RADIO_2057_IPA5G_CASCOFFV_PU_CORE0,
+ 1, 1);
+ mod_radio_reg(
+ pi,
+ RADIO_2057_IPA5G_CASCOFFV_PU_CORE1,
+ 1, 1);
}
mod_radio_reg(pi, RADIO_2057_OVR_REG0, 1 << 4,
0);
} else if (NREV_GE(pi->pubpi.phy_rev, 8)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi,
- (0x1 << 3), 0,
- 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi,
+ (0x1 << 3), 0,
+ 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
}
}
} else {
@@ -23432,12 +24380,8 @@ static void wlc_phy_txcal_phycleanup_nphy(phy_info_t *pi)
}
}
-#define NPHY_CAL_TSSISAMPS 64
-#define NPHY_TEST_TONE_FREQ_40MHz 4000
-#define NPHY_TEST_TONE_FREQ_20MHz 2500
-
void
-wlc_phy_est_tonepwr_nphy(phy_info_t *pi, s32 *qdBm_pwrbuf, u8 num_samps)
+wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, u8 num_samps)
{
u16 tssi_reg;
s32 temp, pwrindex[2];
@@ -23455,8 +24399,8 @@ wlc_phy_est_tonepwr_nphy(phy_info_t *pi, s32 *qdBm_pwrbuf, u8 num_samps)
idle_tssi[1] = (temp <= 31) ? temp : (temp - 64);
tssi_type =
- CHSPEC_IS5G(pi->radio_chanspec) ?
- (u8)NPHY_RSSI_SEL_TSSI_5G:(u8)NPHY_RSSI_SEL_TSSI_2G;
+ CHSPEC_IS5G(pi->radio_chanspec) ?
+ (u8)NPHY_RSSI_SEL_TSSI_5G : (u8)NPHY_RSSI_SEL_TSSI_2G;
wlc_phy_poll_rssi_nphy(pi, tssi_type, rssi_buf, num_samps);
@@ -23466,17 +24410,15 @@ wlc_phy_est_tonepwr_nphy(phy_info_t *pi, s32 *qdBm_pwrbuf, u8 num_samps)
pwrindex[0] = idle_tssi[0] - tssival[0] + 64;
pwrindex[1] = idle_tssi[1] - tssival[1] + 64;
- if (pwrindex[0] < 0) {
+ if (pwrindex[0] < 0)
pwrindex[0] = 0;
- } else if (pwrindex[0] > 63) {
+ else if (pwrindex[0] > 63)
pwrindex[0] = 63;
- }
- if (pwrindex[1] < 0) {
+ if (pwrindex[1] < 0)
pwrindex[1] = 0;
- } else if (pwrindex[1] > 63) {
+ else if (pwrindex[1] > 63)
pwrindex[1] = 63;
- }
wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 1,
(u32) pwrindex[0], 32, &qdBm_pwrbuf[0]);
@@ -23484,340 +24426,1251 @@ wlc_phy_est_tonepwr_nphy(phy_info_t *pi, s32 *qdBm_pwrbuf, u8 num_samps)
(u32) pwrindex[1], 32, &qdBm_pwrbuf[1]);
}
-static void wlc_phy_internal_cal_txgain_nphy(phy_info_t *pi)
+static void wlc_phy_update_txcal_ladder_nphy(struct brcms_phy *pi, u16 core)
{
- u16 txcal_gain[2];
+ int index;
+ u32 bbmult_scale;
+ u16 bbmult;
+ u16 tblentry;
- pi->nphy_txcal_pwr_idx[0] = pi->nphy_cal_orig_pwr_idx[0];
- pi->nphy_txcal_pwr_idx[1] = pi->nphy_cal_orig_pwr_idx[0];
- wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_cal_orig_pwr_idx[0], true);
- wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_cal_orig_pwr_idx[1], true);
+ struct nphy_txiqcal_ladder ladder_lo[] = {
+ {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0},
+ {25, 0}, {25, 1}, {25, 2}, {25, 3}, {25, 4}, {25, 5},
+ {25, 6}, {25, 7}, {35, 7}, {50, 7}, {71, 7}, {100, 7}
+ };
- wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16,
- txcal_gain);
+ struct nphy_txiqcal_ladder ladder_iq[] = {
+ {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0},
+ {25, 0}, {35, 0}, {50, 0}, {71, 0}, {100, 0}, {100, 1},
+ {100, 2}, {100, 3}, {100, 4}, {100, 5}, {100, 6}, {100, 7}
+ };
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- txcal_gain[0] = (txcal_gain[0] & 0xF000) | 0x0F40;
- txcal_gain[1] = (txcal_gain[1] & 0xF000) | 0x0F40;
- } else {
- txcal_gain[0] = (txcal_gain[0] & 0xF000) | 0x0F60;
- txcal_gain[1] = (txcal_gain[1] & 0xF000) | 0x0F60;
+ bbmult = (core == PHY_CORE_0) ?
+ ((pi->nphy_txcal_bbmult >> 8) & 0xff) :
+ (pi->nphy_txcal_bbmult & 0xff);
+
+ for (index = 0; index < 18; index++) {
+ bbmult_scale = ladder_lo[index].percent * bbmult;
+ bbmult_scale /= 100;
+
+ tblentry =
+ ((bbmult_scale & 0xff) << 8) | ladder_lo[index].g_env;
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, index, 16,
+ &tblentry);
+
+ bbmult_scale = ladder_iq[index].percent * bbmult;
+ bbmult_scale /= 100;
+
+ tblentry =
+ ((bbmult_scale & 0xff) << 8) | ladder_iq[index].g_env;
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, index + 32,
+ 16, &tblentry);
}
+}
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16,
- txcal_gain);
+static u8 wlc_phy_txpwr_idx_cur_get_nphy(struct brcms_phy *pi, u8 core)
+{
+ u16 tmp;
+ tmp = read_phy_reg(pi, ((core == PHY_CORE_0) ? 0x1ed : 0x1ee));
+
+ tmp = (tmp & (0x7f << 8)) >> 8;
+ return (u8) tmp;
}
-static void wlc_phy_precal_txgain_nphy(phy_info_t *pi)
+static void
+wlc_phy_txpwr_idx_cur_set_nphy(struct brcms_phy *pi, u8 idx0, u8 idx1)
{
- bool save_bbmult = false;
- u8 txcal_index_2057_rev5n7 = 0;
- u8 txcal_index_2057_rev3n4n6 = 10;
+ mod_phy_reg(pi, 0x1e7, (0x7f << 0), idx0);
+
+ if (NREV_GT(pi->pubpi.phy_rev, 1))
+ mod_phy_reg(pi, 0x222, (0xff << 0), idx1);
+}
+
+static u16 wlc_phy_ipa_get_bbmult_nphy(struct brcms_phy *pi)
+{
+ u16 m0m1;
+
+ wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m0m1);
+
+ return m0m1;
+}
+
+static void wlc_phy_ipa_set_bbmult_nphy(struct brcms_phy *pi, u8 m0, u8 m1)
+{
+ u16 m0m1 = (u16) ((m0 << 8) | m1);
+
+ wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m0m1);
+ wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &m0m1);
+}
+
+static void
+wlc_phy_papd_cal_setup_nphy(struct brcms_phy *pi,
+ struct nphy_papd_restore_state *state, u8 core)
+{
+ s32 tone_freq;
+ u8 off_core;
+ u16 mixgain = 0;
+
+ off_core = core ^ 0x1;
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+
+ if (NREV_IS(pi->pubpi.phy_rev, 7)
+ || NREV_GE(pi->pubpi.phy_rev, 8))
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 7),
+ wlc_phy_read_lpf_bw_ctl_nphy
+ (pi,
+ 0), 0, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if (pi->pubpi.radiorev == 5)
+ mixgain = (core == 0) ? 0x20 : 0x00;
+ else if ((pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev == 8))
+ mixgain = 0x00;
+ else if ((pi->pubpi.radiorev <= 4)
+ || (pi->pubpi.radiorev == 6))
+ mixgain = 0x00;
+ } else {
+ if ((pi->pubpi.radiorev == 4) ||
+ (pi->pubpi.radiorev == 6))
+ mixgain = 0x50;
+ else if ((pi->pubpi.radiorev == 3)
+ || (pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev == 8))
+ mixgain = 0x0;
+ }
+
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11),
+ mixgain, (1 << core), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+
+ wlc_phy_rfctrl_override_1tomany_nphy(
+ pi,
+ NPHY_REV7_RfctrlOverride_cmd_tx_pu,
+ 1, (1 << core), 0);
+ wlc_phy_rfctrl_override_1tomany_nphy(
+ pi,
+ NPHY_REV7_RfctrlOverride_cmd_tx_pu,
+ 0, (1 << off_core), 0);
+
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3),
+ 0, 0x3, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 1,
+ (1 << core), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0,
+ (1 << core), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1,
+ (1 << core), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID2);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 8), 0,
+ (1 << core), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 1,
+ (1 << core), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 0,
+ (1 << core), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1,
+ (1 << core), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5),
+ 0, (1 << core), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 0,
+ (1 << core), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+
+ state->afectrl[core] = read_phy_reg(pi, (core == PHY_CORE_0) ?
+ 0xa6 : 0xa7);
+ state->afeoverride[core] =
+ read_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5);
+ state->afectrl[off_core] =
+ read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa7 : 0xa6);
+ state->afeoverride[off_core] =
+ read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa5 : 0x8f);
+
+ mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa6 : 0xa7),
+ (0x1 << 2), 0);
+ mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f :
+ 0xa5), (0x1 << 2), (0x1 << 2));
+
+ mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa7 : 0xa6),
+ (0x1 << 2), (0x1 << 2));
+ mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa5 :
+ 0x8f), (0x1 << 2), (0x1 << 2));
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ state->pwrup[core] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_2G_PWRUP);
+ state->atten[core] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_2G_ATTEN);
+ state->pwrup[off_core] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, off_core,
+ TXRXCOUPLE_2G_PWRUP);
+ state->atten[off_core] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, off_core,
+ TXRXCOUPLE_2G_ATTEN);
+
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_2G_PWRUP, 0xc);
- if (pi->use_int_tx_iqlo_cal_nphy) {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
if ((pi->pubpi.radiorev == 3) ||
(pi->pubpi.radiorev == 4) ||
- (pi->pubpi.radiorev == 6)) {
+ (pi->pubpi.radiorev == 6))
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_2G_ATTEN, 0xf0);
+ else if (pi->pubpi.radiorev == 5)
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_2G_ATTEN,
+ (core == 0) ? 0xf7 : 0xf2);
+ else if ((pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev == 8))
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_2G_ATTEN, 0xf0);
- pi->nphy_txcal_pwr_idx[0] =
- txcal_index_2057_rev3n4n6;
- pi->nphy_txcal_pwr_idx[1] =
- txcal_index_2057_rev3n4n6;
- wlc_phy_txpwr_index_nphy(pi, 3,
- txcal_index_2057_rev3n4n6,
- false);
- } else {
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core,
+ TXRXCOUPLE_2G_PWRUP, 0x0);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core,
+ TXRXCOUPLE_2G_ATTEN, 0xff);
+ } else {
+ state->pwrup[core] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_5G_PWRUP);
+ state->atten[core] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_5G_ATTEN);
+ state->pwrup[off_core] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, off_core,
+ TXRXCOUPLE_5G_PWRUP);
+ state->atten[off_core] =
+ READ_RADIO_REG3(pi, RADIO_2057, TX, off_core,
+ TXRXCOUPLE_5G_ATTEN);
- pi->nphy_txcal_pwr_idx[0] =
- txcal_index_2057_rev5n7;
- pi->nphy_txcal_pwr_idx[1] =
- txcal_index_2057_rev5n7;
- wlc_phy_txpwr_index_nphy(pi, 3,
- txcal_index_2057_rev5n7,
- false);
- }
- save_bbmult = true;
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_5G_PWRUP, 0xc);
- } else if (NREV_LT(pi->pubpi.phy_rev, 5)) {
- wlc_phy_cal_txgainctrl_nphy(pi, 11, false);
- if (pi->sh->hw_phytxchain != 3) {
- pi->nphy_txcal_pwr_idx[1] =
- pi->nphy_txcal_pwr_idx[0];
- wlc_phy_txpwr_index_nphy(pi, 3,
- pi->
- nphy_txcal_pwr_idx[0],
- true);
- save_bbmult = true;
+ if ((pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev == 8))
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_5G_ATTEN, 0xf4);
+
+ else
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_5G_ATTEN, 0xf0);
+
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core,
+ TXRXCOUPLE_5G_PWRUP, 0x0);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core,
+ TXRXCOUPLE_5G_ATTEN, 0xff);
+ }
+
+ tone_freq = 4000;
+
+ wlc_phy_tx_tone_nphy(pi, tone_freq, 181, 0, 0, false);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 13), (1) << 13);
+
+ mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_OFF) << 0);
+
+ mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 13), (0) << 13);
+
+ } else {
+
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 0);
+
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 1, 0, 0);
+
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0x3, 0);
+
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 2), 1, 0x3, 0);
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 1, 0x3, 0);
+
+ state->afectrl[core] = read_phy_reg(pi, (core == PHY_CORE_0) ?
+ 0xa6 : 0xa7);
+ state->afeoverride[core] =
+ read_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5);
+
+ mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa6 : 0xa7),
+ (0x1 << 0) | (0x1 << 1) | (0x1 << 2), 0);
+ mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f :
+ 0xa5),
+ (0x1 << 0) |
+ (0x1 << 1) |
+ (0x1 << 2), (0x1 << 0) | (0x1 << 1) | (0x1 << 2));
+
+ state->vga_master[core] =
+ READ_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER);
+ WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER, 0x2b);
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ state->fbmix[core] =
+ READ_RADIO_REG2(pi, RADIO_2056, RX, core,
+ TXFBMIX_G);
+ state->intpa_master[core] =
+ READ_RADIO_REG2(pi, RADIO_2056, TX, core,
+ INTPAG_MASTER);
+
+ WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_G,
+ 0x03);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
+ INTPAG_MASTER, 0x04);
+ } else {
+ state->fbmix[core] =
+ READ_RADIO_REG2(pi, RADIO_2056, RX, core,
+ TXFBMIX_A);
+ state->intpa_master[core] =
+ READ_RADIO_REG2(pi, RADIO_2056, TX, core,
+ INTPAA_MASTER);
+
+ WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_A,
+ 0x03);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
+ INTPAA_MASTER, 0x04);
+
+ }
+
+ tone_freq = 4000;
+
+ wlc_phy_tx_tone_nphy(pi, tone_freq, 181, 0, 0, false);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (1) << 0);
+
+ mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (0) << 0);
+
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 0);
+ }
+}
+
+static void
+wlc_phy_papd_cal_cleanup_nphy(struct brcms_phy *pi,
+ struct nphy_papd_restore_state *state)
+{
+ u8 core;
+
+ wlc_phy_stopplayback_nphy(pi);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+
+ for (core = 0; core < pi->pubpi.phy_corenum; core++) {
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_2G_PWRUP, 0);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_2G_ATTEN,
+ state->atten[core]);
+ } else {
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_5G_PWRUP, 0);
+ WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
+ TXRXCOUPLE_5G_ATTEN,
+ state->atten[core]);
}
+ }
- } else if (NREV_IS(pi->pubpi.phy_rev, 5)) {
- if (PHY_IPA(pi)) {
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- wlc_phy_cal_txgainctrl_nphy(pi, 12,
- false);
- } else {
- pi->nphy_txcal_pwr_idx[0] = 80;
- pi->nphy_txcal_pwr_idx[1] = 80;
- wlc_phy_txpwr_index_nphy(pi, 3, 80,
- false);
- save_bbmult = true;
- }
+ if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6))
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 2),
+ 1, 0x3, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ else
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 2),
+ 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1),
+ 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID2);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID2);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 1, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 1, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID2);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 8), 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 1, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 0, 0x3, 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+
+ for (core = 0; core < pi->pubpi.phy_corenum; core++) {
+
+ write_phy_reg(pi, (core == PHY_CORE_0) ?
+ 0xa6 : 0xa7, state->afectrl[core]);
+ write_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f :
+ 0xa5, state->afeoverride[core]);
+ }
+
+ wlc_phy_ipa_set_bbmult_nphy(pi, (state->mm >> 8) & 0xff,
+ (state->mm & 0xff));
+
+ if (NREV_IS(pi->pubpi.phy_rev, 7)
+ || NREV_GE(pi->pubpi.phy_rev, 8))
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi, (0x1 << 7), 0, 0,
+ 1,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ } else {
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 1);
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 0x3, 1);
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0x3, 1);
+
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 2), 0, 0x3, 1);
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 0, 0x3, 1);
+
+ for (core = 0; core < pi->pubpi.phy_corenum; core++) {
+
+ WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER,
+ state->vga_master[core]);
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ WRITE_RADIO_REG2(pi, RADIO_2056, RX, core,
+ TXFBMIX_G, state->fbmix[core]);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
+ INTPAG_MASTER,
+ state->intpa_master[core]);
} else {
+ WRITE_RADIO_REG2(pi, RADIO_2056, RX, core,
+ TXFBMIX_A, state->fbmix[core]);
+ WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
+ INTPAA_MASTER,
+ state->intpa_master[core]);
+ }
- wlc_phy_internal_cal_txgain_nphy(pi);
- save_bbmult = true;
+ write_phy_reg(pi, (core == PHY_CORE_0) ?
+ 0xa6 : 0xa7, state->afectrl[core]);
+ write_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f :
+ 0xa5, state->afeoverride[core]);
+ }
+
+ wlc_phy_ipa_set_bbmult_nphy(pi, (state->mm >> 8) & 0xff,
+ (state->mm & 0xff));
+
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 1);
+ }
+}
+
+static void
+wlc_phy_a1_nphy(struct brcms_phy *pi, u8 core, u32 winsz, u32 start,
+ u32 end)
+{
+ u32 *buf, *src, *dst, sz;
+
+ sz = end - start + 1;
+
+ buf = kmalloc(2 * sizeof(u32) * NPHY_PAPD_EPS_TBL_SIZE, GFP_ATOMIC);
+ if (NULL == buf)
+ return;
+
+ src = buf;
+ dst = buf + NPHY_PAPD_EPS_TBL_SIZE;
+
+ wlc_phy_table_read_nphy(pi,
+ (core ==
+ PHY_CORE_0 ? NPHY_TBL_ID_EPSILONTBL0 :
+ NPHY_TBL_ID_EPSILONTBL1),
+ NPHY_PAPD_EPS_TBL_SIZE, 0, 32, src);
+
+ do {
+ u32 phy_a1, phy_a2;
+ s32 phy_a3, phy_a4, phy_a5, phy_a6, phy_a7;
+
+ phy_a1 = end - min(end, (winsz >> 1));
+ phy_a2 = min_t(u32, NPHY_PAPD_EPS_TBL_SIZE - 1,
+ end + (winsz >> 1));
+ phy_a3 = phy_a2 - phy_a1 + 1;
+ phy_a6 = 0;
+ phy_a7 = 0;
+
+ do {
+ wlc_phy_papd_decode_epsilon(src[phy_a2], &phy_a4,
+ &phy_a5);
+ phy_a6 += phy_a4;
+ phy_a7 += phy_a5;
+ } while (phy_a2-- != phy_a1);
+
+ phy_a6 /= phy_a3;
+ phy_a7 /= phy_a3;
+ dst[end] = ((u32) phy_a7 << 13) | ((u32) phy_a6 & 0x1fff);
+ } while (end-- != start);
+
+ wlc_phy_table_write_nphy(pi,
+ (core ==
+ PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 :
+ NPHY_TBL_ID_EPSILONTBL1, sz, start, 32, dst);
+
+ kfree(buf);
+}
+
+static void
+wlc_phy_a2_nphy(struct brcms_phy *pi, struct nphy_ipa_txcalgains *txgains,
+ enum phy_cal_mode cal_mode, u8 core)
+{
+ u16 phy_a1, phy_a2, phy_a3;
+ u16 phy_a4, phy_a5;
+ bool phy_a6;
+ u8 phy_a7, m[2];
+ u32 phy_a8 = 0;
+ struct nphy_txgains phy_a9;
+
+ if (NREV_LT(pi->pubpi.phy_rev, 3))
+ return;
+
+ phy_a7 = (core == PHY_CORE_0) ? 1 : 0;
+
+ phy_a6 = ((cal_mode == CAL_GCTRL)
+ || (cal_mode == CAL_SOFT)) ? true : false;
+
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+
+ phy_a9 = wlc_phy_get_tx_gain_nphy(pi);
+
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ phy_a5 = ((phy_a9.txlpf[core] << 15) |
+ (phy_a9.txgm[core] << 12) |
+ (phy_a9.pga[core] << 8) |
+ (txgains->gains.pad[core] << 3) |
+ (phy_a9.ipa[core]));
+ else
+ phy_a5 = ((phy_a9.txlpf[core] << 15) |
+ (phy_a9.txgm[core] << 12) |
+ (txgains->gains.pga[core] << 8) |
+ (phy_a9.pad[core] << 3) | (phy_a9.ipa[core]));
+
+ wlc_phy_rfctrl_override_1tomany_nphy(
+ pi,
+ NPHY_REV7_RfctrlOverride_cmd_txgain,
+ phy_a5, (1 << core), 0);
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if ((pi->pubpi.radiorev <= 4)
+ || (pi->pubpi.radiorev == 6))
+ m[core] = (pi->bw == WL_CHANSPEC_BW_40) ?
+ 60 : 79;
+ else
+ m[core] = (pi->bw == WL_CHANSPEC_BW_40) ?
+ 45 : 64;
+ } else {
+ m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 75 : 107;
+ }
+
+ m[phy_a7] = 0;
+ wlc_phy_ipa_set_bbmult_nphy(pi, m[0], m[1]);
+
+ phy_a2 = 63;
+
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if ((pi->pubpi.radiorev == 4)
+ || (pi->pubpi.radiorev == 6)) {
+ phy_a1 = 30;
+ phy_a3 = 30;
+ } else {
+ phy_a1 = 25;
+ phy_a3 = 25;
}
+ } else {
+ if ((pi->pubpi.radiorev == 5)
+ || (pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev == 8)) {
+ phy_a1 = 25;
+ phy_a3 = 25;
+ } else {
+ phy_a1 = 35;
+ phy_a3 = 35;
+ }
+ }
- } else if (NREV_IS(pi->pubpi.phy_rev, 6)) {
- if (PHY_IPA(pi)) {
+ if (cal_mode == CAL_GCTRL) {
+ if ((pi->pubpi.radiorev == 5)
+ && (CHSPEC_IS2G(pi->radio_chanspec)))
+ phy_a1 = 55;
+ else if (((pi->pubpi.radiorev == 7) &&
+ (CHSPEC_IS2G(pi->radio_chanspec))) ||
+ ((pi->pubpi.radiorev == 8) &&
+ (CHSPEC_IS2G(pi->radio_chanspec))))
+ phy_a1 = 60;
+ else
+ phy_a1 = 63;
+
+ } else if ((cal_mode != CAL_FULL) && (cal_mode != CAL_SOFT)) {
+
+ phy_a1 = 35;
+ phy_a3 = 35;
+ }
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (1) << 0);
+
+ mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (0) << 0);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 13), (1) << 13);
+
+ mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 13), (0) << 13);
+
+ write_phy_reg(pi, 0x2a1, 0x80);
+ write_phy_reg(pi, 0x2a2, 0x100);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x7 << 4), (11) << 4);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x7 << 8), (11) << 8);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x7 << 0), (0x3) << 0);
+
+ write_phy_reg(pi, 0x2e5, 0x20);
+
+ mod_phy_reg(pi, 0x2a0, (0x3f << 0), (phy_a3) << 0);
+
+ mod_phy_reg(pi, 0x29f, (0x3f << 0), (phy_a1) << 0);
+
+ mod_phy_reg(pi, 0x29f, (0x3f << 8), (phy_a2) << 8);
+
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3),
+ 1, ((core == 0) ? 1 : 2), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3),
+ 0, ((core == 0) ? 2 : 1), 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+
+ write_phy_reg(pi, 0x2be, 1);
+ SPINWAIT(read_phy_reg(pi, 0x2be), 10 * 1000 * 1000);
+
+ wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3),
+ 0, 0x3, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID0);
+
+ wlc_phy_table_write_nphy(pi,
+ (core ==
+ PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0
+ : NPHY_TBL_ID_EPSILONTBL1, 1, phy_a3,
+ 32, &phy_a8);
+
+ if (cal_mode != CAL_GCTRL) {
+ if (CHSPEC_IS5G(pi->radio_chanspec))
+ wlc_phy_a1_nphy(pi, core, 5, 0, 35);
+ }
+
+ wlc_phy_rfctrl_override_1tomany_nphy(
+ pi,
+ NPHY_REV7_RfctrlOverride_cmd_txgain,
+ phy_a5, (1 << core), 1);
+
+ } else {
+
+ if (txgains) {
+ if (txgains->useindex) {
+ phy_a4 = 15 - ((txgains->index) >> 3);
if (CHSPEC_IS2G(pi->radio_chanspec)) {
- wlc_phy_cal_txgainctrl_nphy(pi, 12,
- false);
+ if (NREV_GE(pi->pubpi.phy_rev, 6))
+ phy_a5 = 0x00f7 | (phy_a4 << 8);
+
+ else
+ if (NREV_IS(pi->pubpi.phy_rev, 5))
+ phy_a5 = 0x10f7 | (phy_a4 << 8);
+ else
+ phy_a5 = 0x50f7 | (phy_a4 << 8);
} else {
- wlc_phy_cal_txgainctrl_nphy(pi, 14,
- false);
+ phy_a5 = 0x70f7 | (phy_a4 << 8);
}
+ wlc_phy_rfctrl_override_nphy(pi,
+ (0x1 << 13),
+ phy_a5,
+ (1 << core), 0);
} else {
-
- wlc_phy_internal_cal_txgain_nphy(pi);
- save_bbmult = true;
+ wlc_phy_rfctrl_override_nphy(pi,
+ (0x1 << 13),
+ 0x5bf7,
+ (1 << core), 0);
}
}
- } else {
- wlc_phy_cal_txgainctrl_nphy(pi, 10, false);
- }
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 45 : 64;
+ else
+ m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 75 : 107;
- if (save_bbmult) {
- wlc_phy_table_read_nphy(pi, 15, 1, 87, 16,
- &pi->nphy_txcal_bbmult);
+ m[phy_a7] = 0;
+ wlc_phy_ipa_set_bbmult_nphy(pi, m[0], m[1]);
+
+ phy_a2 = 63;
+
+ if (cal_mode == CAL_FULL) {
+ phy_a1 = 25;
+ phy_a3 = 25;
+ } else if (cal_mode == CAL_SOFT) {
+ phy_a1 = 25;
+ phy_a3 = 25;
+ } else if (cal_mode == CAL_GCTRL) {
+ phy_a1 = 63;
+ phy_a3 = 25;
+ } else {
+
+ phy_a1 = 25;
+ phy_a3 = 25;
+ }
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (1) << 0);
+
+ mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (0) << 0);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 6)) {
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 13), (1) << 13);
+
+ mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 13), (0) << 13);
+
+ write_phy_reg(pi, 0x2a1, 0x20);
+ write_phy_reg(pi, 0x2a2, 0x60);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0xf << 4), (9) << 4);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0xf << 8), (9) << 8);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0xf << 0), (0x2) << 0);
+
+ write_phy_reg(pi, 0x2e5, 0x20);
+ } else {
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 11), (1) << 11);
+
+ mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 11), (0) << 11);
+
+ write_phy_reg(pi, 0x2a1, 0x80);
+ write_phy_reg(pi, 0x2a2, 0x600);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x7 << 4), (0) << 4);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x7 << 8), (0) << 8);
+
+ mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x7 << 0), (0x3) << 0);
+
+ mod_phy_reg(pi, 0x2a0, (0x3f << 8), (0x20) << 8);
+
+ }
+
+ mod_phy_reg(pi, 0x2a0, (0x3f << 0), (phy_a3) << 0);
+
+ mod_phy_reg(pi, 0x29f, (0x3f << 0), (phy_a1) << 0);
+
+ mod_phy_reg(pi, 0x29f, (0x3f << 8), (phy_a2) << 8);
+
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 1, 0x3, 0);
+
+ write_phy_reg(pi, 0x2be, 1);
+ SPINWAIT(read_phy_reg(pi, 0x2be), 10 * 1000 * 1000);
+
+ wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 0);
+
+ wlc_phy_table_write_nphy(pi,
+ (core ==
+ PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0
+ : NPHY_TBL_ID_EPSILONTBL1, 1, phy_a3,
+ 32, &phy_a8);
+
+ if (cal_mode != CAL_GCTRL)
+ wlc_phy_a1_nphy(pi, core, 5, 0, 40);
}
}
-void
-wlc_phy_cal_txgainctrl_nphy(phy_info_t *pi, s32 dBm_targetpower, bool debug)
+static u8 wlc_phy_a3_nphy(struct brcms_phy *pi, u8 start_gain, u8 core)
{
- int gainctrl_loopidx;
- uint core;
- u16 m0m1, curr_m0m1;
- s32 delta_power;
- s32 txpwrindex;
- s32 qdBm_power[2];
- u16 orig_BBConfig;
- u16 phy_saveregs[4];
- u32 freq_test;
- u16 ampl_test = 250;
- uint stepsize;
- bool phyhang_avoid_state = false;
+ int phy_a1;
+ int phy_a2;
+ bool phy_a3;
+ struct nphy_ipa_txcalgains phy_a4;
+ bool phy_a5 = false;
+ bool phy_a6 = true;
+ s32 phy_a7, phy_a8;
+ u32 phy_a9;
+ int phy_a10;
+ bool phy_a11 = false;
+ int phy_a12;
+ u8 phy_a13 = 0;
+ u8 phy_a14;
+ u8 *phy_a15 = NULL;
+
+ phy_a4.useindex = true;
+ phy_a12 = start_gain;
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- stepsize = 2;
- } else {
+ phy_a2 = 20;
+ phy_a1 = 1;
- stepsize = 1;
- }
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if (pi->pubpi.radiorev == 5) {
- if (CHSPEC_IS40(pi->radio_chanspec)) {
- freq_test = 5000;
- } else {
- freq_test = 2500;
- }
+ phy_a15 = pad_gain_codes_used_2057rev5;
+ phy_a13 =
+ sizeof(pad_gain_codes_used_2057rev5) /
+ sizeof(pad_gain_codes_used_2057rev5
+ [0]) - 1;
- wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_cal_orig_pwr_idx[0], true);
- wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_cal_orig_pwr_idx[1], true);
+ } else if ((pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev == 8)) {
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, true);
+ phy_a15 = pad_gain_codes_used_2057rev7;
+ phy_a13 =
+ sizeof(pad_gain_codes_used_2057rev7) /
+ sizeof(pad_gain_codes_used_2057rev7
+ [0]) - 1;
- phyhang_avoid_state = pi->phyhang_avoid;
- pi->phyhang_avoid = false;
+ } else {
- phy_saveregs[0] = read_phy_reg(pi, 0x91);
- phy_saveregs[1] = read_phy_reg(pi, 0x92);
- phy_saveregs[2] = read_phy_reg(pi, 0xe7);
- phy_saveregs[3] = read_phy_reg(pi, 0xec);
- wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_PA, 1,
- RADIO_MIMO_CORESEL_CORE1 |
- RADIO_MIMO_CORESEL_CORE2);
+ phy_a15 = pad_all_gain_codes_2057;
+ phy_a13 = sizeof(pad_all_gain_codes_2057) /
+ sizeof(pad_all_gain_codes_2057[0]) -
+ 1;
+ }
- if (!debug) {
- wlc_phy_rfctrlintc_override_nphy(pi,
- NPHY_RfctrlIntc_override_TRSW,
- 0x2, RADIO_MIMO_CORESEL_CORE1);
- wlc_phy_rfctrlintc_override_nphy(pi,
- NPHY_RfctrlIntc_override_TRSW,
- 0x8, RADIO_MIMO_CORESEL_CORE2);
- } else {
- wlc_phy_rfctrlintc_override_nphy(pi,
- NPHY_RfctrlIntc_override_TRSW,
- 0x1, RADIO_MIMO_CORESEL_CORE1);
- wlc_phy_rfctrlintc_override_nphy(pi,
- NPHY_RfctrlIntc_override_TRSW,
- 0x7, RADIO_MIMO_CORESEL_CORE2);
- }
+ } else {
- orig_BBConfig = read_phy_reg(pi, 0x01);
- mod_phy_reg(pi, 0x01, (0x1 << 15), 0);
+ phy_a15 = pga_all_gain_codes_2057;
+ phy_a13 = sizeof(pga_all_gain_codes_2057) /
+ sizeof(pga_all_gain_codes_2057[0]) - 1;
+ }
- wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m0m1);
+ phy_a14 = 0;
- for (core = 0; core < pi->pubpi.phy_corenum; core++) {
- txpwrindex = (s32) pi->nphy_cal_orig_pwr_idx[core];
+ for (phy_a10 = 0; phy_a10 < phy_a2; phy_a10++) {
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ phy_a4.gains.pad[core] =
+ (u16) phy_a15[phy_a12];
+ else
+ phy_a4.gains.pga[core] =
+ (u16) phy_a15[phy_a12];
- for (gainctrl_loopidx = 0; gainctrl_loopidx < 2;
- gainctrl_loopidx++) {
- wlc_phy_tx_tone_nphy(pi, freq_test, ampl_test, 0, 0,
- false);
+ wlc_phy_a2_nphy(pi, &phy_a4, CAL_GCTRL, core);
- if (core == PHY_CORE_0) {
- curr_m0m1 = m0m1 & 0xff00;
- } else {
- curr_m0m1 = m0m1 & 0x00ff;
+ wlc_phy_table_read_nphy(pi,
+ (core ==
+ PHY_CORE_0 ?
+ NPHY_TBL_ID_EPSILONTBL0 :
+ NPHY_TBL_ID_EPSILONTBL1), 1,
+ 63, 32, &phy_a9);
+
+ wlc_phy_papd_decode_epsilon(phy_a9, &phy_a7, &phy_a8);
+
+ phy_a3 = ((phy_a7 == 4095) || (phy_a7 == -4096) ||
+ (phy_a8 == 4095) || (phy_a8 == -4096));
+
+ if (!phy_a6 && (phy_a3 != phy_a5)) {
+ if (!phy_a3)
+ phy_a12 -= (u8) phy_a1;
+
+ phy_a11 = true;
+ break;
}
- wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &curr_m0m1);
- wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &curr_m0m1);
+ if (phy_a3)
+ phy_a12 += (u8) phy_a1;
+ else
+ phy_a12 -= (u8) phy_a1;
- udelay(50);
+ if ((phy_a12 < phy_a14) || (phy_a12 > phy_a13)) {
+ if (phy_a12 < phy_a14)
+ phy_a12 = phy_a14;
+ else
+ phy_a12 = phy_a13;
- wlc_phy_est_tonepwr_nphy(pi, qdBm_power,
- NPHY_CAL_TSSISAMPS);
+ phy_a11 = true;
+ break;
+ }
- pi->nphy_bb_mult_save = 0;
- wlc_phy_stopplayback_nphy(pi);
+ phy_a6 = false;
+ phy_a5 = phy_a3;
+ }
- delta_power = (dBm_targetpower * 4) - qdBm_power[core];
+ } else {
+ phy_a2 = 10;
+ phy_a1 = 8;
+ for (phy_a10 = 0; phy_a10 < phy_a2; phy_a10++) {
+ phy_a4.index = (u8) phy_a12;
+ wlc_phy_a2_nphy(pi, &phy_a4, CAL_GCTRL, core);
- txpwrindex -= stepsize * delta_power;
- if (txpwrindex < 0) {
- txpwrindex = 0;
- } else if (txpwrindex > 127) {
- txpwrindex = 127;
+ wlc_phy_table_read_nphy(pi,
+ (core ==
+ PHY_CORE_0 ?
+ NPHY_TBL_ID_EPSILONTBL0 :
+ NPHY_TBL_ID_EPSILONTBL1), 1,
+ 63, 32, &phy_a9);
+
+ wlc_phy_papd_decode_epsilon(phy_a9, &phy_a7, &phy_a8);
+
+ phy_a3 = ((phy_a7 == 4095) || (phy_a7 == -4096) ||
+ (phy_a8 == 4095) || (phy_a8 == -4096));
+
+ if (!phy_a6 && (phy_a3 != phy_a5)) {
+ if (!phy_a3)
+ phy_a12 -= (u8) phy_a1;
+
+ phy_a11 = true;
+ break;
}
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
- if (NREV_IS(pi->pubpi.phy_rev, 4) &&
- (pi->srom_fem5g.extpagain == 3)) {
- if (txpwrindex < 30) {
- txpwrindex = 30;
- }
- }
- } else {
- if (NREV_GE(pi->pubpi.phy_rev, 5) &&
- (pi->srom_fem2g.extpagain == 3)) {
- if (txpwrindex < 50) {
- txpwrindex = 50;
- }
- }
+ if (phy_a3)
+ phy_a12 += (u8) phy_a1;
+ else
+ phy_a12 -= (u8) phy_a1;
+
+ if ((phy_a12 < 0) || (phy_a12 > 127)) {
+ if (phy_a12 < 0)
+ phy_a12 = 0;
+ else
+ phy_a12 = 127;
+
+ phy_a11 = true;
+ break;
}
- wlc_phy_txpwr_index_nphy(pi, (1 << core),
- (u8) txpwrindex, true);
+ phy_a6 = false;
+ phy_a5 = phy_a3;
}
- pi->nphy_txcal_pwr_idx[core] = (u8) txpwrindex;
+ }
- if (debug) {
- u16 radio_gain;
- u16 dbg_m0m1;
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ return (u8) phy_a15[phy_a12];
+ else
+ return (u8) phy_a12;
- wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &dbg_m0m1);
+}
- wlc_phy_tx_tone_nphy(pi, freq_test, ampl_test, 0, 0,
- false);
+static void wlc_phy_a4(struct brcms_phy *pi, bool full_cal)
+{
+ struct nphy_ipa_txcalgains phy_b1[2];
+ struct nphy_papd_restore_state phy_b2;
+ bool phy_b3;
+ u8 phy_b4;
+ u8 phy_b5;
+ s16 phy_b6, phy_b7, phy_b8;
+ u16 phy_b9;
+ s16 phy_b10, phy_b11, phy_b12;
- wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &dbg_m0m1);
- wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &dbg_m0m1);
+ phy_b11 = 0;
+ phy_b12 = 0;
+ phy_b7 = 0;
+ phy_b8 = 0;
+ phy_b6 = 0;
- udelay(100);
+ if (pi->nphy_papd_skip == 1)
+ return;
- wlc_phy_est_tonepwr_nphy(pi, qdBm_power,
- NPHY_CAL_TSSISAMPS);
+ phy_b3 = (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
+ if (!phy_b3)
+ wlapi_suspend_mac_and_wait(pi->sh->physhim);
- wlc_phy_table_read_nphy(pi, 7, 1, (0x110 + core), 16,
- &radio_gain);
+ wlc_phy_stay_in_carriersearch_nphy(pi, true);
- mdelay(4000);
- pi->nphy_bb_mult_save = 0;
- wlc_phy_stopplayback_nphy(pi);
+ pi->nphy_force_papd_cal = false;
+
+ for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++)
+ pi->nphy_papd_tx_gain_at_last_cal[phy_b5] =
+ wlc_phy_txpwr_idx_cur_get_nphy(pi, phy_b5);
+
+ pi->nphy_papd_last_cal = pi->sh->now;
+ pi->nphy_papd_recal_counter++;
+
+ phy_b4 = pi->nphy_txpwrctrl;
+ wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF);
+
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SCALARTBL0, 64, 0, 32,
+ nphy_papd_scaltbl);
+ wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SCALARTBL1, 64, 0, 32,
+ nphy_papd_scaltbl);
+
+ phy_b9 = read_phy_reg(pi, 0x01);
+ mod_phy_reg(pi, 0x01, (0x1 << 15), 0);
+
+ for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) {
+ s32 i, val = 0;
+ for (i = 0; i < 64; i++)
+ wlc_phy_table_write_nphy(pi,
+ ((phy_b5 ==
+ PHY_CORE_0) ?
+ NPHY_TBL_ID_EPSILONTBL0 :
+ NPHY_TBL_ID_EPSILONTBL1), 1,
+ i, 32, &val);
+ }
+
+ wlc_phy_ipa_restore_tx_digi_filts_nphy(pi);
+
+ phy_b2.mm = wlc_phy_ipa_get_bbmult_nphy(pi);
+ for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) {
+ wlc_phy_papd_cal_setup_nphy(pi, &phy_b2, phy_b5);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if ((pi->pubpi.radiorev == 3)
+ || (pi->pubpi.radiorev == 4)
+ || (pi->pubpi.radiorev == 6)) {
+ pi->nphy_papd_cal_gain_index[phy_b5] =
+ 23;
+ } else if (pi->pubpi.radiorev == 5) {
+ pi->nphy_papd_cal_gain_index[phy_b5] =
+ 0;
+ pi->nphy_papd_cal_gain_index[phy_b5] =
+ wlc_phy_a3_nphy(
+ pi,
+ pi->
+ nphy_papd_cal_gain_index
+ [phy_b5],
+ phy_b5);
+
+ } else if ((pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev == 8)) {
+
+ pi->nphy_papd_cal_gain_index[phy_b5] =
+ 0;
+ pi->nphy_papd_cal_gain_index[phy_b5] =
+ wlc_phy_a3_nphy(
+ pi,
+ pi->
+ nphy_papd_cal_gain_index
+ [phy_b5],
+ phy_b5);
+
+ }
+
+ phy_b1[phy_b5].gains.pad[phy_b5] =
+ pi->nphy_papd_cal_gain_index[phy_b5];
+
+ } else {
+ pi->nphy_papd_cal_gain_index[phy_b5] = 0;
+ pi->nphy_papd_cal_gain_index[phy_b5] =
+ wlc_phy_a3_nphy(
+ pi,
+ pi->
+ nphy_papd_cal_gain_index
+ [phy_b5], phy_b5);
+ phy_b1[phy_b5].gains.pga[phy_b5] =
+ pi->nphy_papd_cal_gain_index[phy_b5];
+ }
+ } else {
+ phy_b1[phy_b5].useindex = true;
+ phy_b1[phy_b5].index = 16;
+ phy_b1[phy_b5].index =
+ wlc_phy_a3_nphy(pi, phy_b1[phy_b5].index,
+ phy_b5);
+
+ pi->nphy_papd_cal_gain_index[phy_b5] =
+ 15 - ((phy_b1[phy_b5].index) >> 3);
+ }
+
+ switch (pi->nphy_papd_cal_type) {
+ case 0:
+ wlc_phy_a2_nphy(pi, &phy_b1[phy_b5], CAL_FULL, phy_b5);
+ break;
+ case 1:
+ wlc_phy_a2_nphy(pi, &phy_b1[phy_b5], CAL_SOFT, phy_b5);
+ break;
}
+
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ wlc_phy_papd_cal_cleanup_nphy(pi, &phy_b2);
}
- wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_txcal_pwr_idx[0], true);
- wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_txcal_pwr_idx[1], true);
+ if (NREV_LT(pi->pubpi.phy_rev, 7))
+ wlc_phy_papd_cal_cleanup_nphy(pi, &phy_b2);
- wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &pi->nphy_txcal_bbmult);
+ for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) {
+ int eps_offset = 0;
- write_phy_reg(pi, 0x01, orig_BBConfig);
+ if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ if (pi->pubpi.radiorev == 3)
+ eps_offset = -2;
+ else if (pi->pubpi.radiorev == 5)
+ eps_offset = 3;
+ else
+ eps_offset = -1;
+ } else {
+ eps_offset = 2;
+ }
- write_phy_reg(pi, 0x91, phy_saveregs[0]);
- write_phy_reg(pi, 0x92, phy_saveregs[1]);
- write_phy_reg(pi, 0xe7, phy_saveregs[2]);
- write_phy_reg(pi, 0xec, phy_saveregs[3]);
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ phy_b8 = phy_b1[phy_b5].gains.pad[phy_b5];
+ phy_b10 = 0;
+ if ((pi->pubpi.radiorev == 3) ||
+ (pi->pubpi.radiorev == 4) ||
+ (pi->pubpi.radiorev == 6)) {
+ phy_b12 = -(
+ nphy_papd_padgain_dlt_2g_2057rev3n4
+ [phy_b8] + 1) / 2;
+ phy_b10 = -1;
+ } else if (pi->pubpi.radiorev == 5) {
+ phy_b12 = -(
+ nphy_papd_padgain_dlt_2g_2057rev5
+ [phy_b8] + 1) / 2;
+ } else if ((pi->pubpi.radiorev == 7) ||
+ (pi->pubpi.radiorev == 8)) {
+ phy_b12 = -(
+ nphy_papd_padgain_dlt_2g_2057rev7
+ [phy_b8] + 1) / 2;
+ }
+ } else {
+ phy_b7 = phy_b1[phy_b5].gains.pga[phy_b5];
+ if ((pi->pubpi.radiorev == 3) ||
+ (pi->pubpi.radiorev == 4) ||
+ (pi->pubpi.radiorev == 6))
+ phy_b11 =
+ -(nphy_papd_pgagain_dlt_5g_2057
+ [phy_b7]
+ + 1) / 2;
+ else if ((pi->pubpi.radiorev == 7)
+ || (pi->pubpi.radiorev == 8))
+ phy_b11 = -(
+ nphy_papd_pgagain_dlt_5g_2057rev7
+ [phy_b7] + 1) / 2;
- pi->phyhang_avoid = phyhang_avoid_state;
+ phy_b10 = -9;
+ }
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, false);
-}
+ if (CHSPEC_IS2G(pi->radio_chanspec))
+ phy_b6 =
+ -60 + 27 + eps_offset + phy_b12 +
+ phy_b10;
+ else
+ phy_b6 =
+ -60 + 27 + eps_offset + phy_b11 +
+ phy_b10;
-static void wlc_phy_update_txcal_ladder_nphy(phy_info_t *pi, u16 core)
-{
- int index;
- u32 bbmult_scale;
- u16 bbmult;
- u16 tblentry;
+ mod_phy_reg(pi, (phy_b5 == PHY_CORE_0) ? 0x298 :
+ 0x29c, (0x1ff << 7), (phy_b6) << 7);
- nphy_txiqcal_ladder_t ladder_lo[] = {
- {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0},
- {25, 0}, {25, 1}, {25, 2}, {25, 3}, {25, 4}, {25, 5},
- {25, 6}, {25, 7}, {35, 7}, {50, 7}, {71, 7}, {100, 7}
- };
+ pi->nphy_papd_epsilon_offset[phy_b5] = phy_b6;
+ } else {
+ if (NREV_LT(pi->pubpi.phy_rev, 5))
+ eps_offset = 4;
+ else
+ eps_offset = 2;
- nphy_txiqcal_ladder_t ladder_iq[] = {
- {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0},
- {25, 0}, {35, 0}, {50, 0}, {71, 0}, {100, 0}, {100, 1},
- {100, 2}, {100, 3}, {100, 4}, {100, 5}, {100, 6}, {100, 7}
- };
+ phy_b7 = 15 - ((phy_b1[phy_b5].index) >> 3);
- bbmult = (core == PHY_CORE_0) ?
- ((pi->nphy_txcal_bbmult >> 8) & 0xff) : (pi->
- nphy_txcal_bbmult & 0xff);
+ if (CHSPEC_IS2G(pi->radio_chanspec)) {
+ phy_b11 =
+ -(nphy_papd_pga_gain_delta_ipa_2g[
+ phy_b7] +
+ 1) / 2;
+ phy_b10 = 0;
+ } else {
+ phy_b11 =
+ -(nphy_papd_pga_gain_delta_ipa_5g[
+ phy_b7] +
+ 1) / 2;
+ phy_b10 = -9;
+ }
- for (index = 0; index < 18; index++) {
- bbmult_scale = ladder_lo[index].percent * bbmult;
- bbmult_scale /= 100;
+ phy_b6 = -60 + 27 + eps_offset + phy_b11 + phy_b10;
- tblentry =
- ((bbmult_scale & 0xff) << 8) | ladder_lo[index].g_env;
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, index, 16,
- &tblentry);
+ mod_phy_reg(pi, (phy_b5 == PHY_CORE_0) ? 0x298 :
+ 0x29c, (0x1ff << 7), (phy_b6) << 7);
- bbmult_scale = ladder_iq[index].percent * bbmult;
- bbmult_scale /= 100;
+ pi->nphy_papd_epsilon_offset[phy_b5] = phy_b6;
+ }
+ }
+
+ mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0);
+
+ mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 :
+ 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0);
+
+ if (NREV_GE(pi->pubpi.phy_rev, 6)) {
+ mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 13), (0) << 13);
+
+ mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 13), (0) << 13);
+
+ } else {
+ mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 11), (0) << 11);
+
+ mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x2a3 :
+ 0x2a4, (0x1 << 11), (0) << 11);
- tblentry =
- ((bbmult_scale & 0xff) << 8) | ladder_iq[index].g_env;
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, index + 32,
- 16, &tblentry);
}
+ pi->nphy_papdcomp = NPHY_PAPD_COMP_ON;
+
+ write_phy_reg(pi, 0x01, phy_b9);
+
+ wlc_phy_ipa_set_tx_digi_filts_nphy(pi);
+
+ wlc_phy_txpwrctrl_enable_nphy(pi, phy_b4);
+ if (phy_b4 == PHY_TPC_HW_OFF) {
+ wlc_phy_txpwr_index_nphy(pi, (1 << 0),
+ (s8) (pi->nphy_txpwrindex[0].
+ index_internal), false);
+ wlc_phy_txpwr_index_nphy(pi, (1 << 1),
+ (s8) (pi->nphy_txpwrindex[1].
+ index_internal), false);
+ }
+
+ wlc_phy_stay_in_carriersearch_nphy(pi, false);
+
+ if (!phy_b3)
+ wlapi_enable_mac(pi->sh->physhim);
}
-void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
+void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype)
{
- nphy_txgains_t target_gain;
+ struct nphy_txgains target_gain;
u8 tx_pwr_ctrl_state;
bool fullcal = true;
bool restore_tx_gain = false;
bool mphase;
- if (NORADIO_ENAB(pi->pubpi)) {
- wlc_phy_cal_perical_mphase_reset(pi);
- return;
- }
-
if (PHY_MUTED(pi))
return;
@@ -23826,30 +25679,29 @@ void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
else if (caltype == PHY_PERICAL_PARTIAL)
fullcal = false;
- if (pi->cal_type_override != PHY_PERICAL_AUTO) {
+ if (pi->cal_type_override != PHY_PERICAL_AUTO)
fullcal =
- (pi->cal_type_override == PHY_PERICAL_FULL) ? true : false;
- }
+ (pi->cal_type_override ==
+ PHY_PERICAL_FULL) ? true : false;
if ((pi->mphase_cal_phase_id > MPHASE_CAL_STATE_INIT)) {
if (pi->nphy_txiqlocal_chanspec != pi->radio_chanspec)
wlc_phy_cal_perical_mphase_restart(pi);
}
- if ((pi->mphase_cal_phase_id == MPHASE_CAL_STATE_RXCAL)) {
+ if ((pi->mphase_cal_phase_id == MPHASE_CAL_STATE_RXCAL))
wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, 10000);
- }
wlapi_suspend_mac_and_wait(pi->sh->physhim);
- wlc_phyreg_enter((wlc_phy_t *) pi);
+ wlc_phyreg_enter((struct brcms_phy_pub *) pi);
if ((pi->mphase_cal_phase_id == MPHASE_CAL_STATE_IDLE) ||
(pi->mphase_cal_phase_id == MPHASE_CAL_STATE_INIT)) {
pi->nphy_cal_orig_pwr_idx[0] =
- (u8) ((read_phy_reg(pi, 0x1ed) >> 8) & 0x7f);
+ (u8) ((read_phy_reg(pi, 0x1ed) >> 8) & 0x7f);
pi->nphy_cal_orig_pwr_idx[1] =
- (u8) ((read_phy_reg(pi, 0x1ee) >> 8) & 0x7f);
+ (u8) ((read_phy_reg(pi, 0x1ee) >> 8) & 0x7f);
if (pi->nphy_txpwrctrl != PHY_TPC_HW_OFF) {
wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2,
@@ -23865,7 +25717,7 @@ void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF);
if (pi->antsel_type == ANTSEL_2x3)
- wlc_phy_antsel_init((wlc_phy_t *) pi, true);
+ wlc_phy_antsel_init((struct brcms_phy_pub *) pi, true);
mphase = (pi->mphase_cal_phase_id != MPHASE_CAL_STATE_IDLE);
if (!mphase) {
@@ -23878,25 +25730,22 @@ void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
target_gain = pi->nphy_cal_target_gain;
}
if (0 ==
- wlc_phy_cal_txiqlo_nphy(pi, target_gain, fullcal, mphase)) {
+ wlc_phy_cal_txiqlo_nphy(pi, target_gain, fullcal,
+ mphase)) {
if (PHY_IPA(pi))
wlc_phy_a4(pi, true);
- wlc_phyreg_exit((wlc_phy_t *) pi);
+ wlc_phyreg_exit((struct brcms_phy_pub *) pi);
wlapi_enable_mac(pi->sh->physhim);
wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION,
10000);
wlapi_suspend_mac_and_wait(pi->sh->physhim);
- wlc_phyreg_enter((wlc_phy_t *) pi);
+ wlc_phyreg_enter((struct brcms_phy_pub *) pi);
if (0 == wlc_phy_cal_rxiq_nphy(pi, target_gain,
- (pi->
- first_cal_after_assoc
- || (pi->
- cal_type_override
- ==
- PHY_PERICAL_FULL))
- ? 2 : 0, false)) {
+ (pi->first_cal_after_assoc ||
+ (pi->cal_type_override ==
+ PHY_PERICAL_FULL)) ? 2 : 0, false)) {
wlc_phy_savecal_nphy(pi);
wlc_phy_txpwrctrl_coeff_setup_nphy(pi);
@@ -23904,9 +25753,8 @@ void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
pi->nphy_perical_last = pi->sh->now;
}
}
- if (caltype != PHY_PERICAL_AUTO) {
+ if (caltype != PHY_PERICAL_AUTO)
wlc_phy_rssi_cal_nphy(pi);
- }
if (pi->first_cal_after_assoc
|| (pi->cal_type_override == PHY_PERICAL_FULL)) {
@@ -23915,18 +25763,17 @@ void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
wlc_phy_txpwrctrl_pwr_setup_nphy(pi);
}
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
wlc_phy_radio205x_vcocal_nphy(pi);
- }
} else {
switch (pi->mphase_cal_phase_id) {
case MPHASE_CAL_STATE_INIT:
pi->nphy_perical_last = pi->sh->now;
pi->nphy_txiqlocal_chanspec = pi->radio_chanspec;
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
wlc_phy_precal_txgain_nphy(pi);
- }
+
pi->nphy_cal_target_gain = wlc_phy_get_tx_gain_nphy(pi);
pi->mphase_cal_phase_id++;
break;
@@ -23941,8 +25788,8 @@ void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
pi->nphy_rxcal_active = true;
if (wlc_phy_cal_txiqlo_nphy
- (pi, pi->nphy_cal_target_gain, fullcal,
- true) != 0) {
+ (pi, pi->nphy_cal_target_gain, fullcal,
+ true) != 0) {
wlc_phy_cal_perical_mphase_reset(pi);
break;
@@ -23950,20 +25797,19 @@ void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
if (NREV_LE(pi->pubpi.phy_rev, 2) &&
(pi->mphase_cal_phase_id ==
- MPHASE_CAL_STATE_TXPHASE4)) {
+ MPHASE_CAL_STATE_TXPHASE4))
pi->mphase_cal_phase_id += 2;
- } else {
+ else
pi->mphase_cal_phase_id++;
- }
break;
case MPHASE_CAL_STATE_PAPDCAL:
if ((pi->radar_percal_mask & 0x2) != 0)
pi->nphy_rxcal_active = true;
- if (PHY_IPA(pi)) {
+ if (PHY_IPA(pi))
wlc_phy_a4(pi, true);
- }
+
pi->mphase_cal_phase_id++;
break;
@@ -23974,9 +25820,8 @@ void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
(pi->first_cal_after_assoc ||
(pi->cal_type_override ==
PHY_PERICAL_FULL)) ? 2 : 0,
- false) == 0) {
+ false) == 0)
wlc_phy_savecal_nphy(pi);
- }
pi->mphase_cal_phase_id++;
break;
@@ -23987,16 +25832,15 @@ void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
wlc_phy_txpwrctrl_coeff_setup_nphy(pi);
wlc_phy_rssi_cal_nphy(pi);
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
wlc_phy_radio205x_vcocal_nphy(pi);
- }
+
restore_tx_gain = true;
- if (pi->first_cal_after_assoc) {
+ if (pi->first_cal_after_assoc)
pi->mphase_cal_phase_id++;
- } else {
+ else
wlc_phy_cal_perical_mphase_reset(pi);
- }
break;
@@ -24037,27 +25881,27 @@ void wlc_phy_cal_perical_nphy_run(phy_info_t *pi, u8 caltype)
} else {
wlc_phy_txpwr_index_nphy(pi, (1 << 0),
(s8) (pi->
- nphy_txpwrindex
- [0].
- index_internal),
+ nphy_txpwrindex
+ [0].
+ index_internal),
false);
wlc_phy_txpwr_index_nphy(pi, (1 << 1),
(s8) (pi->
- nphy_txpwrindex
- [1].
- index_internal),
+ nphy_txpwrindex
+ [1].
+ index_internal),
false);
}
}
}
wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state);
- wlc_phyreg_exit((wlc_phy_t *) pi);
+ wlc_phyreg_exit((struct brcms_phy_pub *) pi);
wlapi_enable_mac(pi->sh->physhim);
}
int
-wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
+wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain,
bool fullcal, bool mphase)
{
u16 val;
@@ -24072,7 +25916,7 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
u16 tone_freq;
u16 gain_save[2];
u16 cal_gain[2];
- nphy_iqcal_params_t cal_params[2];
+ struct nphy_iqcal_params cal_params[2];
u32 tbl_len;
void *tbl_ptr;
bool ladder_updated[2];
@@ -24082,35 +25926,35 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
u16 tbl_tx_iqlo_cal_loft_ladder_20[] = {
0x0300, 0x0500, 0x0700, 0x0900, 0x0d00, 0x1100, 0x1900, 0x1901,
- 0x1902,
+ 0x1902,
0x1903, 0x1904, 0x1905, 0x1906, 0x1907, 0x2407, 0x3207, 0x4607,
- 0x6407
+ 0x6407
};
u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = {
0x0200, 0x0300, 0x0600, 0x0900, 0x0d00, 0x1100, 0x1900, 0x2400,
- 0x3200,
+ 0x3200,
0x4600, 0x6400, 0x6401, 0x6402, 0x6403, 0x6404, 0x6405, 0x6406,
- 0x6407
+ 0x6407
};
u16 tbl_tx_iqlo_cal_loft_ladder_40[] = {
0x0200, 0x0300, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1201,
- 0x1202,
+ 0x1202,
0x1203, 0x1204, 0x1205, 0x1206, 0x1207, 0x1907, 0x2307, 0x3207,
- 0x4707
+ 0x4707
};
u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = {
0x0100, 0x0200, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1900,
- 0x2300,
+ 0x2300,
0x3200, 0x4700, 0x4701, 0x4702, 0x4703, 0x4704, 0x4705, 0x4706,
- 0x4707
+ 0x4707
};
u16 tbl_tx_iqlo_cal_startcoefs[] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000
+ 0x0000
};
u16 tbl_tx_iqlo_cal_cmds_fullcal[] = {
@@ -24146,11 +25990,10 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
pi->phyhang_avoid = false;
}
- if (CHSPEC_IS40(pi->radio_chanspec)) {
+ if (CHSPEC_IS40(pi->radio_chanspec))
phy_bw = 40;
- } else {
+ else
phy_bw = 20;
- }
wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save);
@@ -24192,11 +26035,10 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
16, tbl_ptr);
}
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
write_phy_reg(pi, 0xc2, 0x8ad9);
- } else {
+ else
write_phy_reg(pi, 0xc2, 0x8aa9);
- }
max_val = 250;
tone_freq = (phy_bw == 20) ? 2500 : 5000;
@@ -24206,7 +26048,8 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
bcmerror = 0;
} else {
bcmerror =
- wlc_phy_tx_tone_nphy(pi, tone_freq, max_val, 1, 0, false);
+ wlc_phy_tx_tone_nphy(pi, tone_freq, max_val, 1, 0,
+ false);
}
if (bcmerror == 0) {
@@ -24214,19 +26057,15 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
if (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_TXPHASE0) {
tbl_ptr = pi->mphase_txcal_bestcoeffs;
tbl_len = ARRAY_SIZE(pi->mphase_txcal_bestcoeffs);
- if (NREV_LT(pi->pubpi.phy_rev, 3)) {
-
+ if (NREV_LT(pi->pubpi.phy_rev, 3))
tbl_len -= 2;
- }
} else {
if ((!fullcal) && (pi->nphy_txiqlocal_coeffsvalid)) {
tbl_ptr = pi->nphy_txiqlocal_bestc;
tbl_len = ARRAY_SIZE(pi->nphy_txiqlocal_bestc);
- if (NREV_LT(pi->pubpi.phy_rev, 3)) {
-
+ if (NREV_LT(pi->pubpi.phy_rev, 3))
tbl_len -= 2;
- }
} else {
fullcal = true;
@@ -24234,14 +26073,12 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
if (NREV_GE(pi->pubpi.phy_rev, 3)) {
tbl_ptr =
tbl_tx_iqlo_cal_startcoefs_nphyrev3;
- tbl_len =
- ARRAY_SIZE
- (tbl_tx_iqlo_cal_startcoefs_nphyrev3);
+ tbl_len = ARRAY_SIZE(
+ tbl_tx_iqlo_cal_startcoefs_nphyrev3);
} else {
tbl_ptr = tbl_tx_iqlo_cal_startcoefs;
- tbl_len =
- ARRAY_SIZE
- (tbl_tx_iqlo_cal_startcoefs);
+ tbl_len = ARRAY_SIZE(
+ tbl_tx_iqlo_cal_startcoefs);
}
}
}
@@ -24250,21 +26087,22 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
if (fullcal) {
max_cal_cmds = (NREV_GE(pi->pubpi.phy_rev, 3)) ?
- ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3) :
- ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_fullcal);
+ ARRAY_SIZE(
+ tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3) :
+ ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_fullcal);
} else {
max_cal_cmds = (NREV_GE(pi->pubpi.phy_rev, 3)) ?
- ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_recal_nphyrev3) :
- ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_recal);
+ ARRAY_SIZE(
+ tbl_tx_iqlo_cal_cmds_recal_nphyrev3) :
+ ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_recal);
}
if (mphase) {
cal_cnt = pi->mphase_txcal_cmdidx;
- if ((cal_cnt + pi->mphase_txcal_numcmds) < max_cal_cmds) {
+ if ((cal_cnt + pi->mphase_txcal_numcmds) < max_cal_cmds)
num_cals = cal_cnt + pi->mphase_txcal_numcmds;
- } else {
+ else
num_cals = max_cal_cmds;
- }
} else {
cal_cnt = 0;
num_cals = max_cal_cmds;
@@ -24274,13 +26112,14 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
if (fullcal) {
cal_cmd = (NREV_GE(pi->pubpi.phy_rev, 3)) ?
- tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3
- [cal_cnt] :
- tbl_tx_iqlo_cal_cmds_fullcal[cal_cnt];
+ tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3
+ [cal_cnt] :
+ tbl_tx_iqlo_cal_cmds_fullcal[cal_cnt];
} else {
cal_cmd = (NREV_GE(pi->pubpi.phy_rev, 3)) ?
- tbl_tx_iqlo_cal_cmds_recal_nphyrev3[cal_cnt]
- : tbl_tx_iqlo_cal_cmds_recal[cal_cnt];
+ tbl_tx_iqlo_cal_cmds_recal_nphyrev3[
+ cal_cnt]
+ : tbl_tx_iqlo_cal_cmds_recal[cal_cnt];
}
core_no = ((cal_cmd & 0x3000) >> 12);
@@ -24291,15 +26130,16 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
PHY_IPA(pi)
&& (CHSPEC_IS2G(pi->radio_chanspec)))) {
if (!ladder_updated[core_no]) {
- wlc_phy_update_txcal_ladder_nphy(pi,
- core_no);
+ wlc_phy_update_txcal_ladder_nphy(
+ pi,
+ core_no);
ladder_updated[core_no] = true;
}
}
val =
- (cal_params[core_no].
- ncorr[cal_type] << 8) | NPHY_N_GCTL;
+ (cal_params[core_no].
+ ncorr[cal_type] << 8) | NPHY_N_GCTL;
write_phy_reg(pi, 0xc1, val);
if ((cal_type == 1) || (cal_type == 3)
@@ -24347,8 +26187,8 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
}
mphase_cal_lastphase =
- (NREV_LE(pi->pubpi.phy_rev, 2)) ?
- MPHASE_CAL_STATE_TXPHASE4 : MPHASE_CAL_STATE_TXPHASE5;
+ (NREV_LE(pi->pubpi.phy_rev, 2)) ?
+ MPHASE_CAL_STATE_TXPHASE4 : MPHASE_CAL_STATE_TXPHASE5;
if (!mphase
|| (pi->mphase_cal_phase_id == mphase_cal_lastphase)) {
@@ -24378,10 +26218,9 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
16, tbl_buf);
tbl_len = ARRAY_SIZE(pi->nphy_txiqlocal_bestc);
- if (NREV_LT(pi->pubpi.phy_rev, 3)) {
-
+ if (NREV_LT(pi->pubpi.phy_rev, 3))
tbl_len -= 2;
- }
+
wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL,
tbl_len, 96, 16,
pi->nphy_txiqlocal_bestc);
@@ -24390,10 +26229,9 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
pi->nphy_txiqlocal_chanspec = pi->radio_chanspec;
} else {
tbl_len = ARRAY_SIZE(pi->mphase_txcal_bestcoeffs);
- if (NREV_LT(pi->pubpi.phy_rev, 3)) {
-
+ if (NREV_LT(pi->pubpi.phy_rev, 3))
tbl_len -= 2;
- }
+
wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL,
tbl_len, 96, 16,
pi->mphase_txcal_bestcoeffs);
@@ -24418,16 +26256,15 @@ wlc_phy_cal_txiqlo_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
wlc_phy_tx_iq_war_nphy(pi);
}
- if (NREV_GE(pi->pubpi.phy_rev, 4)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 4))
pi->phyhang_avoid = phyhang_avoid_state;
- }
wlc_phy_stay_in_carriersearch_nphy(pi, false);
return bcmerror;
}
-static void wlc_phy_reapply_txcal_coeffs_nphy(phy_info_t *pi)
+static void wlc_phy_reapply_txcal_coeffs_nphy(struct brcms_phy *pi)
{
u16 tbl_buf[7];
@@ -24462,20 +26299,9 @@ static void wlc_phy_reapply_txcal_coeffs_nphy(phy_info_t *pi)
}
}
-static void wlc_phy_tx_iq_war_nphy(phy_info_t *pi)
-{
- nphy_iq_comp_t tx_comp;
-
- wlc_phy_table_read_nphy(pi, 15, 4, 0x50, 16, (void *)&tx_comp);
-
- wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ, tx_comp.a0);
- wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 2, tx_comp.b0);
- wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 4, tx_comp.a1);
- wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 6, tx_comp.b1);
-}
-
void
-wlc_phy_rx_iq_coeffs_nphy(phy_info_t *pi, u8 write, nphy_iq_comp_t *pcomp)
+wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write,
+ struct nphy_iq_comp *pcomp)
{
if (write) {
write_phy_reg(pi, 0x9a, pcomp->a0);
@@ -24491,8 +26317,8 @@ wlc_phy_rx_iq_coeffs_nphy(phy_info_t *pi, u8 write, nphy_iq_comp_t *pcomp)
}
void
-wlc_phy_rx_iq_est_nphy(phy_info_t *pi, phy_iq_est_t *est, u16 num_samps,
- u8 wait_time, u8 wait_for_crs)
+wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est,
+ u16 num_samps, u8 wait_time, u8 wait_for_crs)
{
u8 core;
@@ -24512,24 +26338,27 @@ wlc_phy_rx_iq_est_nphy(phy_info_t *pi, phy_iq_est_t *est, u16 num_samps,
if ((read_phy_reg(pi, 0x129) & NPHY_IqestCmd_iqstart) == 0) {
for (core = 0; core < pi->pubpi.phy_corenum; core++) {
est[core].i_pwr =
- (read_phy_reg(pi, NPHY_IqestipwrAccHi(core)) << 16)
- | read_phy_reg(pi, NPHY_IqestipwrAccLo(core));
+ (read_phy_reg(pi,
+ NPHY_IqestipwrAccHi(core)) << 16)
+ | read_phy_reg(pi, NPHY_IqestipwrAccLo(core));
est[core].q_pwr =
- (read_phy_reg(pi, NPHY_IqestqpwrAccHi(core)) << 16)
- | read_phy_reg(pi, NPHY_IqestqpwrAccLo(core));
+ (read_phy_reg(pi,
+ NPHY_IqestqpwrAccHi(core)) << 16)
+ | read_phy_reg(pi, NPHY_IqestqpwrAccLo(core));
est[core].iq_prod =
- (read_phy_reg(pi, NPHY_IqestIqAccHi(core)) << 16) |
- read_phy_reg(pi, NPHY_IqestIqAccLo(core));
+ (read_phy_reg(pi,
+ NPHY_IqestIqAccHi(core)) << 16) |
+ read_phy_reg(pi, NPHY_IqestIqAccLo(core));
}
}
}
#define CAL_RETRY_CNT 2
-static void wlc_phy_calc_rx_iq_comp_nphy(phy_info_t *pi, u8 core_mask)
+static void wlc_phy_calc_rx_iq_comp_nphy(struct brcms_phy *pi, u8 core_mask)
{
u8 curr_core;
- phy_iq_est_t est[PHY_CORE_MAX];
- nphy_iq_comp_t old_comp, new_comp;
+ struct phy_iq_est est[PHY_CORE_MAX];
+ struct nphy_iq_comp old_comp, new_comp;
s32 iq = 0;
u32 ii = 0, qq = 0;
s16 iq_nbits, qq_nbits, brsh, arsh;
@@ -24544,7 +26373,7 @@ static void wlc_phy_calc_rx_iq_comp_nphy(phy_info_t *pi, u8 core_mask)
new_comp.a0 = new_comp.b0 = new_comp.a1 = new_comp.b1 = 0x0;
wlc_phy_rx_iq_coeffs_nphy(pi, 1, &new_comp);
- cal_try:
+cal_try:
wlc_phy_rx_iq_est_nphy(pi, est, 0x4000, 32, 0);
new_comp = old_comp;
@@ -24634,7 +26463,8 @@ static void wlc_phy_calc_rx_iq_comp_nphy(phy_info_t *pi, u8 core_mask)
}
if (bcmerror != 0) {
- printk("%s: Failed, cnt = %d\n", __func__, cal_retry);
+ printk(KERN_DEBUG "%s: Failed, cnt = %d\n", __func__,
+ cal_retry);
if (cal_retry < CAL_RETRY_CNT) {
cal_retry++;
@@ -24642,13 +26472,12 @@ static void wlc_phy_calc_rx_iq_comp_nphy(phy_info_t *pi, u8 core_mask)
}
new_comp = old_comp;
- } else if (cal_retry > 0) {
}
wlc_phy_rx_iq_coeffs_nphy(pi, 1, &new_comp);
}
-static void wlc_phy_rxcal_radio_setup_nphy(phy_info_t *pi, u8 rx_core)
+static void wlc_phy_rxcal_radio_setup_nphy(struct brcms_phy *pi, u8 rx_core)
{
u16 offtune_val;
u16 bias_g = 0;
@@ -24658,121 +26487,125 @@ static void wlc_phy_rxcal_radio_setup_nphy(phy_info_t *pi, u8 rx_core)
if (rx_core == PHY_CORE_0) {
if (CHSPEC_IS5G(pi->radio_chanspec)) {
pi->tx_rx_cal_radio_saveregs[0] =
- read_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP);
+ read_radio_reg(pi,
+ RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP);
pi->tx_rx_cal_radio_saveregs[1] =
- read_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN);
+ read_radio_reg(pi,
+ RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN);
write_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP,
- 0x3);
+ RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP,
+ 0x3);
write_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN,
- 0xaf);
+ RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN,
+ 0xaf);
} else {
pi->tx_rx_cal_radio_saveregs[0] =
- read_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP);
+ read_radio_reg(pi,
+ RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP);
pi->tx_rx_cal_radio_saveregs[1] =
- read_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN);
-
- write_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP,
- 0x3);
- write_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN,
- 0x7f);
+ read_radio_reg(pi,
+ RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN);
+
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP,
+ 0x3);
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN,
+ 0x7f);
}
} else {
if (CHSPEC_IS5G(pi->radio_chanspec)) {
pi->tx_rx_cal_radio_saveregs[0] =
- read_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP);
+ read_radio_reg(pi,
+ RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP);
pi->tx_rx_cal_radio_saveregs[1] =
- read_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN);
-
- write_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP,
- 0x3);
- write_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN,
- 0xaf);
+ read_radio_reg(pi,
+ RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN);
+
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP,
+ 0x3);
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN,
+ 0xaf);
} else {
pi->tx_rx_cal_radio_saveregs[0] =
- read_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP);
+ read_radio_reg(pi,
+ RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP);
pi->tx_rx_cal_radio_saveregs[1] =
- read_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN);
+ read_radio_reg(pi,
+ RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN);
write_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP,
- 0x3);
+ RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP,
+ 0x3);
write_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN,
- 0x7f);
+ RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN,
+ 0x7f);
}
}
} else {
if (rx_core == PHY_CORE_0) {
pi->tx_rx_cal_radio_saveregs[0] =
- read_radio_reg(pi,
- RADIO_2056_TX_RXIQCAL_TXMUX |
- RADIO_2056_TX1);
+ read_radio_reg(pi,
+ RADIO_2056_TX_RXIQCAL_TXMUX |
+ RADIO_2056_TX1);
pi->tx_rx_cal_radio_saveregs[1] =
- read_radio_reg(pi,
- RADIO_2056_RX_RXIQCAL_RXMUX |
- RADIO_2056_RX0);
+ read_radio_reg(pi,
+ RADIO_2056_RX_RXIQCAL_RXMUX |
+ RADIO_2056_RX0);
if (pi->pubpi.radiorev >= 5) {
pi->tx_rx_cal_radio_saveregs[2] =
- read_radio_reg(pi,
- RADIO_2056_RX_RXSPARE2 |
- RADIO_2056_RX0);
+ read_radio_reg(pi,
+ RADIO_2056_RX_RXSPARE2 |
+ RADIO_2056_RX0);
pi->tx_rx_cal_radio_saveregs[3] =
- read_radio_reg(pi,
- RADIO_2056_TX_TXSPARE2 |
- RADIO_2056_TX1);
+ read_radio_reg(pi,
+ RADIO_2056_TX_TXSPARE2 |
+ RADIO_2056_TX1);
}
if (CHSPEC_IS5G(pi->radio_chanspec)) {
if (pi->pubpi.radiorev >= 5) {
pi->tx_rx_cal_radio_saveregs[4] =
- read_radio_reg(pi,
- RADIO_2056_RX_LNAA_MASTER
- | RADIO_2056_RX0);
+ read_radio_reg(pi,
+ RADIO_2056_RX_LNAA_MASTER
+ | RADIO_2056_RX0);
- write_radio_reg(pi,
- RADIO_2056_RX_LNAA_MASTER
- | RADIO_2056_RX0, 0x40);
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAA_MASTER
+ | RADIO_2056_RX0, 0x40);
write_radio_reg(pi,
- RADIO_2056_TX_TXSPARE2 |
- RADIO_2056_TX1, bias_a);
+ RADIO_2056_TX_TXSPARE2 |
+ RADIO_2056_TX1, bias_a);
write_radio_reg(pi,
- RADIO_2056_RX_RXSPARE2 |
- RADIO_2056_RX0, bias_a);
+ RADIO_2056_RX_RXSPARE2 |
+ RADIO_2056_RX0, bias_a);
} else {
pi->tx_rx_cal_radio_saveregs[4] =
- read_radio_reg(pi,
- RADIO_2056_RX_LNAA_TUNE
- | RADIO_2056_RX0);
+ read_radio_reg(pi,
+ RADIO_2056_RX_LNAA_TUNE
+ | RADIO_2056_RX0);
offtune_val =
- (pi->
- tx_rx_cal_radio_saveregs[2] & 0xF0)
- >> 8;
+ (pi->tx_rx_cal_radio_saveregs
+ [2] & 0xF0) >> 8;
offtune_val =
- (offtune_val <= 0x7) ? 0xF : 0;
+ (offtune_val <= 0x7) ? 0xF : 0;
mod_radio_reg(pi,
RADIO_2056_RX_LNAA_TUNE |
@@ -24789,34 +26622,41 @@ static void wlc_phy_rxcal_radio_setup_nphy(phy_info_t *pi, u8 rx_core)
} else {
if (pi->pubpi.radiorev >= 5) {
pi->tx_rx_cal_radio_saveregs[4] =
- read_radio_reg(pi,
- RADIO_2056_RX_LNAG_MASTER
- | RADIO_2056_RX0);
-
- write_radio_reg(pi,
- RADIO_2056_RX_LNAG_MASTER
- | RADIO_2056_RX0, 0x40);
-
- write_radio_reg(pi,
- RADIO_2056_TX_TXSPARE2 |
- RADIO_2056_TX1, bias_g);
-
- write_radio_reg(pi,
- RADIO_2056_RX_RXSPARE2 |
- RADIO_2056_RX0, bias_g);
+ read_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAG_MASTER
+ | RADIO_2056_RX0);
+
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAG_MASTER
+ | RADIO_2056_RX0, 0x40);
+
+ write_radio_reg(
+ pi,
+ RADIO_2056_TX_TXSPARE2
+ |
+ RADIO_2056_TX1, bias_g);
+
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_RXSPARE2
+ |
+ RADIO_2056_RX0, bias_g);
} else {
pi->tx_rx_cal_radio_saveregs[4] =
- read_radio_reg(pi,
- RADIO_2056_RX_LNAG_TUNE
- | RADIO_2056_RX0);
+ read_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAG_TUNE
+ | RADIO_2056_RX0);
offtune_val =
- (pi->
- tx_rx_cal_radio_saveregs[2] & 0xF0)
- >> 8;
+ (pi->
+ tx_rx_cal_radio_saveregs[2] &
+ 0xF0) >> 8;
offtune_val =
- (offtune_val <= 0x7) ? 0xF : 0;
+ (offtune_val <= 0x7) ? 0xF : 0;
mod_radio_reg(pi,
RADIO_2056_RX_LNAG_TUNE |
@@ -24834,56 +26674,63 @@ static void wlc_phy_rxcal_radio_setup_nphy(phy_info_t *pi, u8 rx_core)
} else {
pi->tx_rx_cal_radio_saveregs[0] =
- read_radio_reg(pi,
- RADIO_2056_TX_RXIQCAL_TXMUX |
- RADIO_2056_TX0);
+ read_radio_reg(pi,
+ RADIO_2056_TX_RXIQCAL_TXMUX |
+ RADIO_2056_TX0);
pi->tx_rx_cal_radio_saveregs[1] =
- read_radio_reg(pi,
- RADIO_2056_RX_RXIQCAL_RXMUX |
- RADIO_2056_RX1);
+ read_radio_reg(pi,
+ RADIO_2056_RX_RXIQCAL_RXMUX |
+ RADIO_2056_RX1);
if (pi->pubpi.radiorev >= 5) {
pi->tx_rx_cal_radio_saveregs[2] =
- read_radio_reg(pi,
- RADIO_2056_RX_RXSPARE2 |
- RADIO_2056_RX1);
+ read_radio_reg(pi,
+ RADIO_2056_RX_RXSPARE2 |
+ RADIO_2056_RX1);
pi->tx_rx_cal_radio_saveregs[3] =
- read_radio_reg(pi,
- RADIO_2056_TX_TXSPARE2 |
- RADIO_2056_TX0);
+ read_radio_reg(pi,
+ RADIO_2056_TX_TXSPARE2 |
+ RADIO_2056_TX0);
}
if (CHSPEC_IS5G(pi->radio_chanspec)) {
if (pi->pubpi.radiorev >= 5) {
pi->tx_rx_cal_radio_saveregs[4] =
- read_radio_reg(pi,
- RADIO_2056_RX_LNAA_MASTER
- | RADIO_2056_RX1);
-
- write_radio_reg(pi,
- RADIO_2056_RX_LNAA_MASTER
- | RADIO_2056_RX1, 0x40);
-
- write_radio_reg(pi,
- RADIO_2056_TX_TXSPARE2 |
- RADIO_2056_TX0, bias_a);
-
- write_radio_reg(pi,
- RADIO_2056_RX_RXSPARE2 |
- RADIO_2056_RX1, bias_a);
+ read_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAA_MASTER
+ | RADIO_2056_RX1);
+
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAA_MASTER |
+ RADIO_2056_RX1, 0x40);
+
+ write_radio_reg(
+ pi,
+ RADIO_2056_TX_TXSPARE2
+ |
+ RADIO_2056_TX0, bias_a);
+
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_RXSPARE2
+ |
+ RADIO_2056_RX1, bias_a);
} else {
pi->tx_rx_cal_radio_saveregs[4] =
- read_radio_reg(pi,
- RADIO_2056_RX_LNAA_TUNE
- | RADIO_2056_RX1);
+ read_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAA_TUNE
+ | RADIO_2056_RX1);
offtune_val =
- (pi->
- tx_rx_cal_radio_saveregs[2] & 0xF0)
- >> 8;
+ (pi->
+ tx_rx_cal_radio_saveregs[2] &
+ 0xF0) >> 8;
offtune_val =
- (offtune_val <= 0x7) ? 0xF : 0;
+ (offtune_val <= 0x7) ? 0xF : 0;
mod_radio_reg(pi,
RADIO_2056_RX_LNAA_TUNE |
@@ -24900,33 +26747,40 @@ static void wlc_phy_rxcal_radio_setup_nphy(phy_info_t *pi, u8 rx_core)
} else {
if (pi->pubpi.radiorev >= 5) {
pi->tx_rx_cal_radio_saveregs[4] =
- read_radio_reg(pi,
- RADIO_2056_RX_LNAG_MASTER
- | RADIO_2056_RX1);
-
- write_radio_reg(pi,
- RADIO_2056_RX_LNAG_MASTER
- | RADIO_2056_RX1, 0x40);
-
- write_radio_reg(pi,
- RADIO_2056_TX_TXSPARE2 |
- RADIO_2056_TX0, bias_g);
-
- write_radio_reg(pi,
- RADIO_2056_RX_RXSPARE2 |
- RADIO_2056_RX1, bias_g);
+ read_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAG_MASTER
+ | RADIO_2056_RX1);
+
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAG_MASTER
+ | RADIO_2056_RX1, 0x40);
+
+ write_radio_reg(
+ pi,
+ RADIO_2056_TX_TXSPARE2
+ |
+ RADIO_2056_TX0, bias_g);
+
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_RXSPARE2
+ |
+ RADIO_2056_RX1, bias_g);
} else {
pi->tx_rx_cal_radio_saveregs[4] =
- read_radio_reg(pi,
- RADIO_2056_RX_LNAG_TUNE
- | RADIO_2056_RX1);
+ read_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAG_TUNE
+ | RADIO_2056_RX1);
offtune_val =
- (pi->
- tx_rx_cal_radio_saveregs[2] & 0xF0)
- >> 8;
+ (pi->
+ tx_rx_cal_radio_saveregs[2] &
+ 0xF0) >> 8;
offtune_val =
- (offtune_val <= 0x7) ? 0xF : 0;
+ (offtune_val <= 0x7) ? 0xF : 0;
mod_radio_reg(pi,
RADIO_2056_RX_LNAG_TUNE |
@@ -24945,51 +26799,59 @@ static void wlc_phy_rxcal_radio_setup_nphy(phy_info_t *pi, u8 rx_core)
}
}
-static void wlc_phy_rxcal_radio_cleanup_nphy(phy_info_t *pi, u8 rx_core)
+static void wlc_phy_rxcal_radio_cleanup_nphy(struct brcms_phy *pi, u8 rx_core)
{
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
if (rx_core == PHY_CORE_0) {
if (CHSPEC_IS5G(pi->radio_chanspec)) {
- write_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP,
- pi->
- tx_rx_cal_radio_saveregs[0]);
- write_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN,
- pi->
- tx_rx_cal_radio_saveregs[1]);
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP,
+ pi->
+ tx_rx_cal_radio_saveregs[0]);
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN,
+ pi->
+ tx_rx_cal_radio_saveregs[1]);
} else {
- write_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP,
- pi->
- tx_rx_cal_radio_saveregs[0]);
- write_radio_reg(pi,
- RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN,
- pi->
- tx_rx_cal_radio_saveregs[1]);
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP,
+ pi->
+ tx_rx_cal_radio_saveregs[0]);
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN,
+ pi->
+ tx_rx_cal_radio_saveregs[1]);
}
} else {
if (CHSPEC_IS5G(pi->radio_chanspec)) {
- write_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP,
- pi->
- tx_rx_cal_radio_saveregs[0]);
- write_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN,
- pi->
- tx_rx_cal_radio_saveregs[1]);
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP,
+ pi->
+ tx_rx_cal_radio_saveregs[0]);
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN,
+ pi->
+ tx_rx_cal_radio_saveregs[1]);
} else {
- write_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP,
- pi->
- tx_rx_cal_radio_saveregs[0]);
- write_radio_reg(pi,
- RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN,
- pi->
- tx_rx_cal_radio_saveregs[1]);
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP,
+ pi->
+ tx_rx_cal_radio_saveregs[0]);
+ write_radio_reg(
+ pi,
+ RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN,
+ pi->
+ tx_rx_cal_radio_saveregs[1]);
}
}
@@ -25020,37 +26882,39 @@ static void wlc_phy_rxcal_radio_cleanup_nphy(phy_info_t *pi, u8 rx_core)
}
if (CHSPEC_IS5G(pi->radio_chanspec)) {
- if (pi->pubpi.radiorev >= 5) {
- write_radio_reg(pi,
- RADIO_2056_RX_LNAA_MASTER
- | RADIO_2056_RX0,
- pi->
- tx_rx_cal_radio_saveregs
- [4]);
- } else {
- write_radio_reg(pi,
- RADIO_2056_RX_LNAA_TUNE
- | RADIO_2056_RX0,
- pi->
- tx_rx_cal_radio_saveregs
- [4]);
- }
+ if (pi->pubpi.radiorev >= 5)
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAA_MASTER
+ | RADIO_2056_RX0,
+ pi->
+ tx_rx_cal_radio_saveregs
+ [4]);
+ else
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAA_TUNE
+ | RADIO_2056_RX0,
+ pi->
+ tx_rx_cal_radio_saveregs
+ [4]);
} else {
- if (pi->pubpi.radiorev >= 5) {
- write_radio_reg(pi,
- RADIO_2056_RX_LNAG_MASTER
- | RADIO_2056_RX0,
- pi->
- tx_rx_cal_radio_saveregs
- [4]);
- } else {
- write_radio_reg(pi,
- RADIO_2056_RX_LNAG_TUNE
- | RADIO_2056_RX0,
- pi->
- tx_rx_cal_radio_saveregs
- [4]);
- }
+ if (pi->pubpi.radiorev >= 5)
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAG_MASTER
+ | RADIO_2056_RX0,
+ pi->
+ tx_rx_cal_radio_saveregs
+ [4]);
+ else
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAG_TUNE
+ | RADIO_2056_RX0,
+ pi->
+ tx_rx_cal_radio_saveregs
+ [4]);
}
} else {
@@ -25079,59 +26943,59 @@ static void wlc_phy_rxcal_radio_cleanup_nphy(phy_info_t *pi, u8 rx_core)
}
if (CHSPEC_IS5G(pi->radio_chanspec)) {
- if (pi->pubpi.radiorev >= 5) {
- write_radio_reg(pi,
- RADIO_2056_RX_LNAA_MASTER
- | RADIO_2056_RX1,
- pi->
- tx_rx_cal_radio_saveregs
- [4]);
- } else {
- write_radio_reg(pi,
- RADIO_2056_RX_LNAA_TUNE
- | RADIO_2056_RX1,
- pi->
- tx_rx_cal_radio_saveregs
- [4]);
- }
+ if (pi->pubpi.radiorev >= 5)
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAA_MASTER
+ | RADIO_2056_RX1,
+ pi->
+ tx_rx_cal_radio_saveregs
+ [4]);
+ else
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAA_TUNE
+ | RADIO_2056_RX1,
+ pi->
+ tx_rx_cal_radio_saveregs
+ [4]);
} else {
- if (pi->pubpi.radiorev >= 5) {
- write_radio_reg(pi,
- RADIO_2056_RX_LNAG_MASTER
- | RADIO_2056_RX1,
- pi->
- tx_rx_cal_radio_saveregs
- [4]);
- } else {
- write_radio_reg(pi,
- RADIO_2056_RX_LNAG_TUNE
- | RADIO_2056_RX1,
- pi->
- tx_rx_cal_radio_saveregs
- [4]);
- }
+ if (pi->pubpi.radiorev >= 5)
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAG_MASTER
+ | RADIO_2056_RX1,
+ pi->
+ tx_rx_cal_radio_saveregs
+ [4]);
+ else
+ write_radio_reg(
+ pi,
+ RADIO_2056_RX_LNAG_TUNE
+ | RADIO_2056_RX1,
+ pi->
+ tx_rx_cal_radio_saveregs
+ [4]);
}
}
}
}
-static void wlc_phy_rxcal_physetup_nphy(phy_info_t *pi, u8 rx_core)
+static void wlc_phy_rxcal_physetup_nphy(struct brcms_phy *pi, u8 rx_core)
{
u8 tx_core;
u16 rx_antval, tx_antval;
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
tx_core = rx_core;
- } else {
+ else
tx_core = (rx_core == PHY_CORE_0) ? 1 : 0;
- }
pi->tx_rx_cal_phy_saveregs[0] = read_phy_reg(pi, 0xa2);
pi->tx_rx_cal_phy_saveregs[1] =
- read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : 0xa7);
+ read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : 0xa7);
pi->tx_rx_cal_phy_saveregs[2] =
- read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5);
+ read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5);
pi->tx_rx_cal_phy_saveregs[3] = read_phy_reg(pi, 0x91);
pi->tx_rx_cal_phy_saveregs[4] = read_phy_reg(pi, 0x92);
pi->tx_rx_cal_phy_saveregs[5] = read_phy_reg(pi, 0x7a);
@@ -25196,17 +27060,19 @@ static void wlc_phy_rxcal_physetup_nphy(phy_info_t *pi, u8 rx_core)
NPHY_REV7_RFCTRLOVERRIDE_ID2);
wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 0, 0, 0,
NPHY_REV7_RFCTRLOVERRIDE_ID1);
- if (CHSPEC_IS40(pi->radio_chanspec)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi,
- (0x1 << 7),
- 2, 0, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- } else {
- wlc_phy_rfctrl_override_nphy_rev7(pi,
- (0x1 << 7),
- 0, 0, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- }
+ if (CHSPEC_IS40(pi->radio_chanspec))
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi,
+ (0x1 << 7),
+ 2, 0, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+ else
+ wlc_phy_rfctrl_override_nphy_rev7(
+ pi,
+ (0x1 << 7),
+ 0, 0, 0,
+ NPHY_REV7_RFCTRLOVERRIDE_ID1);
+
wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7),
0, 0, 0,
NPHY_REV7_RFCTRLOVERRIDE_ID1);
@@ -25242,7 +27108,7 @@ static void wlc_phy_rxcal_physetup_nphy(phy_info_t *pi, u8 rx_core)
}
}
-static void wlc_phy_rxcal_phycleanup_nphy(phy_info_t *pi, u8 rx_core)
+static void wlc_phy_rxcal_phycleanup_nphy(struct brcms_phy *pi, u8 rx_core)
{
write_phy_reg(pi, 0xa2, pi->tx_rx_cal_phy_saveregs[0]);
@@ -25269,34 +27135,32 @@ static void wlc_phy_rxcal_phycleanup_nphy(phy_info_t *pi, u8 rx_core)
}
static void
-wlc_phy_rxcal_gainctrl_nphy_rev5(phy_info_t *pi, u8 rx_core,
+wlc_phy_rxcal_gainctrl_nphy_rev5(struct brcms_phy *pi, u8 rx_core,
u16 *rxgain, u8 cal_type)
{
u16 num_samps;
- phy_iq_est_t est[PHY_CORE_MAX];
+ struct phy_iq_est est[PHY_CORE_MAX];
u8 tx_core;
- nphy_iq_comp_t save_comp, zero_comp;
- u32 i_pwr, q_pwr, curr_pwr, optim_pwr = 0, prev_pwr = 0, thresh_pwr =
- 10000;
+ struct nphy_iq_comp save_comp, zero_comp;
+ u32 i_pwr, q_pwr, curr_pwr, optim_pwr = 0, prev_pwr = 0,
+ thresh_pwr = 10000;
s16 desired_log2_pwr, actual_log2_pwr, delta_pwr;
bool gainctrl_done = false;
u8 mix_tia_gain = 3;
s8 optim_gaintbl_index = 0, prev_gaintbl_index = 0;
s8 curr_gaintbl_index = 3;
u8 gainctrl_dirn = NPHY_RXCAL_GAIN_INIT;
- nphy_ipa_txrxgain_t *nphy_rxcal_gaintbl;
+ const struct nphy_ipa_txrxgain *nphy_rxcal_gaintbl;
u16 hpvga, lpf_biq1, lpf_biq0, lna2, lna1;
int fine_gain_idx;
s8 txpwrindex;
u16 nphy_rxcal_txgain[2];
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
tx_core = rx_core;
- } else {
+ else
tx_core = 1 - rx_core;
- }
num_samps = 1024;
desired_log2_pwr = (cal_type == 0) ? 13 : 13;
@@ -25306,45 +27170,42 @@ wlc_phy_rxcal_gainctrl_nphy_rev5(phy_info_t *pi, u8 rx_core,
wlc_phy_rx_iq_coeffs_nphy(pi, 1, &zero_comp);
if (CHSPEC_IS5G(pi->radio_chanspec)) {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
mix_tia_gain = 3;
- } else if (NREV_GE(pi->pubpi.phy_rev, 4)) {
+ else if (NREV_GE(pi->pubpi.phy_rev, 4))
mix_tia_gain = 4;
- } else {
+ else
mix_tia_gain = 6;
- }
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_5GHz_rev7;
- } else {
+ else
nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_5GHz;
- }
} else {
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_2GHz_rev7;
- } else {
+ else
nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_2GHz;
- }
}
do {
hpvga = (NREV_GE(pi->pubpi.phy_rev, 7)) ?
- 0 : nphy_rxcal_gaintbl[curr_gaintbl_index].hpvga;
+ 0 : nphy_rxcal_gaintbl[curr_gaintbl_index].hpvga;
lpf_biq1 = nphy_rxcal_gaintbl[curr_gaintbl_index].lpf_biq1;
lpf_biq0 = nphy_rxcal_gaintbl[curr_gaintbl_index].lpf_biq0;
lna2 = nphy_rxcal_gaintbl[curr_gaintbl_index].lna2;
lna1 = nphy_rxcal_gaintbl[curr_gaintbl_index].lna1;
txpwrindex = nphy_rxcal_gaintbl[curr_gaintbl_index].txpwrindex;
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- wlc_phy_rfctrl_override_1tomany_nphy(pi,
- NPHY_REV7_RfctrlOverride_cmd_rxgain,
- ((lpf_biq1 << 12) |
- (lpf_biq0 << 8) |
- (mix_tia_gain <<
- 4) | (lna2 << 2)
- | lna1), 0x3, 0);
- } else {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ wlc_phy_rfctrl_override_1tomany_nphy(
+ pi,
+ NPHY_REV7_RfctrlOverride_cmd_rxgain,
+ ((lpf_biq1 << 12) |
+ (lpf_biq0 << 8) |
+ (mix_tia_gain << 4) | (lna2 << 2)
+ | lna1), 0x3, 0);
+ else
wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12),
((hpvga << 12) |
(lpf_biq1 << 10) |
@@ -25352,7 +27213,6 @@ wlc_phy_rxcal_gainctrl_nphy_rev5(phy_info_t *pi, u8 rx_core,
(mix_tia_gain << 4) |
(lna2 << 2) | lna1), 0x3,
0);
- }
pi->nphy_rxcal_pwr_idx[tx_core] = txpwrindex;
@@ -25441,27 +27301,28 @@ wlc_phy_rxcal_gainctrl_nphy_rev5(phy_info_t *pi, u8 rx_core,
if (NREV_GE(pi->pubpi.phy_rev, 7)) {
fine_gain_idx = (int)lpf_biq1 + delta_pwr;
- if (fine_gain_idx + (int)lpf_biq0 > 10) {
+ if (fine_gain_idx + (int)lpf_biq0 > 10)
lpf_biq1 = 10 - lpf_biq0;
- } else {
+ else
lpf_biq1 = (u16) max(fine_gain_idx, 0);
- }
- wlc_phy_rfctrl_override_1tomany_nphy(pi,
- NPHY_REV7_RfctrlOverride_cmd_rxgain,
- ((lpf_biq1 << 12) |
- (lpf_biq0 << 8) |
- (mix_tia_gain << 4) |
- (lna2 << 2) | lna1), 0x3,
- 0);
+
+ wlc_phy_rfctrl_override_1tomany_nphy(
+ pi,
+ NPHY_REV7_RfctrlOverride_cmd_rxgain,
+ ((lpf_biq1 << 12) |
+ (lpf_biq0 << 8) |
+ (mix_tia_gain << 4) |
+ (lna2 << 2) | lna1), 0x3,
+ 0);
} else {
hpvga = (u16) max(min(((int)hpvga) + delta_pwr, 10), 0);
wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12),
- ((hpvga << 12) | (lpf_biq1 << 10) |
- (lpf_biq0 << 8) | (mix_tia_gain <<
- 4) | (lna2 <<
- 2) |
+ ((hpvga << 12) |
+ (lpf_biq1 << 10) |
+ (lpf_biq0 << 8) |
+ (mix_tia_gain << 4) |
+ (lna2 << 2) |
lna1), 0x3, 0);
-
}
if (rxgain != NULL) {
@@ -25477,14 +27338,14 @@ wlc_phy_rxcal_gainctrl_nphy_rev5(phy_info_t *pi, u8 rx_core,
}
static void
-wlc_phy_rxcal_gainctrl_nphy(phy_info_t *pi, u8 rx_core, u16 *rxgain,
+wlc_phy_rxcal_gainctrl_nphy(struct brcms_phy *pi, u8 rx_core, u16 *rxgain,
u8 cal_type)
{
wlc_phy_rxcal_gainctrl_nphy_rev5(pi, rx_core, rxgain, cal_type);
}
static u8
-wlc_phy_rc_sweep_nphy(phy_info_t *pi, u8 core_idx, u8 loopback_type)
+wlc_phy_rc_sweep_nphy(struct brcms_phy *pi, u8 core_idx, u8 loopback_type)
{
u32 target_bws[2] = { 9500, 21000 };
u32 ref_tones[2] = { 3000, 6000 };
@@ -25516,11 +27377,10 @@ wlc_phy_rc_sweep_nphy(phy_info_t *pi, u8 core_idx, u8 loopback_type)
u16 rccal_val, last_rccal_val = 0, best_rccal_val = 0;
u32 ref_iq_vals = 0, target_iq_vals = 0;
u16 num_samps, log_num_samps = 10;
- phy_iq_est_t est[PHY_CORE_MAX];
+ struct phy_iq_est est[PHY_CORE_MAX];
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
return 0;
- }
num_samps = (1 << log_num_samps);
@@ -25539,20 +27399,21 @@ wlc_phy_rc_sweep_nphy(phy_info_t *pi, u8 core_idx, u8 loopback_type)
if (core_idx == 0) {
radio_addr_offset_rx = RADIO_2056_RX0;
radio_addr_offset_tx =
- (loopback_type == 0) ? RADIO_2056_TX0 : RADIO_2056_TX1;
+ (loopback_type == 0) ? RADIO_2056_TX0 : RADIO_2056_TX1;
} else {
radio_addr_offset_rx = RADIO_2056_RX1;
radio_addr_offset_tx =
- (loopback_type == 0) ? RADIO_2056_TX1 : RADIO_2056_TX0;
+ (loopback_type == 0) ? RADIO_2056_TX1 : RADIO_2056_TX0;
}
orig_txlpf_rccal_lpc_ovr_val =
- read_radio_reg(pi,
- (RADIO_2056_TX_TXLPF_RCCAL | radio_addr_offset_tx));
+ read_radio_reg(pi,
+ (RADIO_2056_TX_TXLPF_RCCAL |
+ radio_addr_offset_tx));
orig_rxlpf_rccal_hpc_ovr_val =
- read_radio_reg(pi,
- (RADIO_2056_RX_RXLPF_RCCAL_HPC |
- radio_addr_offset_rx));
+ read_radio_reg(pi,
+ (RADIO_2056_RX_RXLPF_RCCAL_HPC |
+ radio_addr_offset_rx));
orig_dcBypass = ((read_phy_reg(pi, 0x48) >> 8) & 1);
@@ -25623,17 +27484,18 @@ wlc_phy_rc_sweep_nphy(phy_info_t *pi, u8 core_idx, u8 loopback_type)
wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0);
- if (core_idx == 0) {
+ if (core_idx == 0)
ref_iq_vals =
- max_t(u32, (est[0].i_pwr +
- est[0].q_pwr) >> (log_num_samps + 1),
- 1);
- } else {
+ max_t(u32, (est[0].i_pwr +
+ est[0].q_pwr) >>
+ (log_num_samps + 1),
+ 1);
+ else
ref_iq_vals =
- max_t(u32, (est[1].i_pwr +
- est[1].q_pwr) >> (log_num_samps + 1),
- 1);
- }
+ max_t(u32, (est[1].i_pwr +
+ est[1].q_pwr) >>
+ (log_num_samps + 1),
+ 1);
wlc_phy_tx_tone_nphy(pi, target_bw, NPHY_RXCAL_TONEAMP,
0, 1, false);
@@ -25642,20 +27504,19 @@ wlc_phy_rc_sweep_nphy(phy_info_t *pi, u8 core_idx, u8 loopback_type)
wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0);
- if (core_idx == 0) {
- target_iq_vals =
- (est[0].i_pwr + est[0].q_pwr) >> (log_num_samps +
- 1);
- } else {
+ if (core_idx == 0)
+ target_iq_vals = (est[0].i_pwr + est[0].q_pwr) >>
+ (log_num_samps + 1);
+ else
target_iq_vals =
- (est[1].i_pwr + est[1].q_pwr) >> (log_num_samps +
- 1);
- }
+ (est[1].i_pwr +
+ est[1].q_pwr) >> (log_num_samps + 1);
+
pwr_ratio = (uint) ((target_iq_vals << 16) / ref_iq_vals);
- if (rccal_stepsize == 0) {
+ if (rccal_stepsize == 0)
rccal_stepsize--;
- } else if (rccal_stepsize == 1) {
+ else if (rccal_stepsize == 1) {
last_rccal_val = rccal_val;
rccal_val += (pwr_ratio > target_pwr_ratio) ? 1 : -1;
last_pwr_ratio = pwr_ratio;
@@ -25668,21 +27529,20 @@ wlc_phy_rc_sweep_nphy(phy_info_t *pi, u8 core_idx, u8 loopback_type)
if (rccal_stepsize == -1) {
best_rccal_val =
- (ABS((int)last_pwr_ratio - (int)target_pwr_ratio) <
- ABS((int)pwr_ratio -
- (int)target_pwr_ratio)) ? last_rccal_val :
- rccal_val;
+ (abs((int)last_pwr_ratio -
+ (int)target_pwr_ratio) <
+ abs((int)pwr_ratio -
+ (int)target_pwr_ratio)) ? last_rccal_val :
+ rccal_val;
if (CHSPEC_IS40(pi->radio_chanspec)) {
if ((best_rccal_val > 140)
- || (best_rccal_val < 135)) {
+ || (best_rccal_val < 135))
best_rccal_val = 138;
- }
} else {
if ((best_rccal_val > 142)
- || (best_rccal_val < 137)) {
+ || (best_rccal_val < 137))
best_rccal_val = 140;
- }
}
write_radio_reg(pi,
@@ -25723,17 +27583,17 @@ wlc_phy_rc_sweep_nphy(phy_info_t *pi, u8 core_idx, u8 loopback_type)
return best_rccal_val - 0x80;
}
-#define WAIT_FOR_SCOPE 4000
-static int
-wlc_phy_cal_rxiq_nphy_rev3(phy_info_t *pi, nphy_txgains_t target_gain,
- u8 cal_type, bool debug)
+#define WAIT_FOR_SCOPE 4000
+static int wlc_phy_cal_rxiq_nphy_rev3(struct brcms_phy *pi,
+ struct nphy_txgains target_gain,
+ u8 cal_type, bool debug)
{
u16 orig_BBConfig;
u8 core_no, rx_core;
u8 best_rccal[2];
u16 gain_save[2];
u16 cal_gain[2];
- nphy_iqcal_params_t cal_params[2];
+ struct nphy_iqcal_params cal_params[2];
u8 rxcore_state;
s8 rxlpf_rccal_hpc, txlpf_rccal_lpc;
s8 txlpf_idac;
@@ -25760,12 +27620,13 @@ wlc_phy_cal_rxiq_nphy_rev3(phy_info_t *pi, nphy_txgains_t target_gain,
wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, cal_gain);
- rxcore_state = wlc_phy_rxcore_getstate_nphy((wlc_phy_t *) pi);
+ rxcore_state = wlc_phy_rxcore_getstate_nphy(
+ (struct brcms_phy_pub *) pi);
for (rx_core = 0; rx_core < pi->pubpi.phy_corenum; rx_core++) {
skip_rxiqcal =
- ((rxcore_state & (1 << rx_core)) == 0) ? true : false;
+ ((rxcore_state & (1 << rx_core)) == 0) ? true : false;
wlc_phy_rxcal_physetup_nphy(pi, rx_core);
@@ -25776,7 +27637,8 @@ wlc_phy_cal_rxiq_nphy_rev3(phy_info_t *pi, nphy_txgains_t target_gain,
wlc_phy_rxcal_gainctrl_nphy(pi, rx_core, NULL, 0);
wlc_phy_tx_tone_nphy(pi,
- (CHSPEC_IS40(pi->radio_chanspec)) ?
+ (CHSPEC_IS40(
+ pi->radio_chanspec)) ?
NPHY_RXCAL_TONEFREQ_40MHz :
NPHY_RXCAL_TONEFREQ_20MHz,
NPHY_RXCAL_TONEAMP, 0, cal_type,
@@ -25794,23 +27656,21 @@ wlc_phy_cal_rxiq_nphy_rev3(phy_info_t *pi, nphy_txgains_t target_gain,
if (rx_core == PHY_CORE_1) {
- if (rxcore_state == 1) {
- wlc_phy_rxcore_setstate_nphy((wlc_phy_t
- *) pi, 3);
- }
+ if (rxcore_state == 1)
+ wlc_phy_rxcore_setstate_nphy(
+ (struct brcms_phy_pub *) pi, 3);
wlc_phy_rxcal_gainctrl_nphy(pi, rx_core, NULL,
1);
best_rccal[rx_core] =
- wlc_phy_rc_sweep_nphy(pi, rx_core, 1);
+ wlc_phy_rc_sweep_nphy(pi, rx_core, 1);
pi->nphy_rccal_value = best_rccal[rx_core];
- if (rxcore_state == 1) {
- wlc_phy_rxcore_setstate_nphy((wlc_phy_t
- *) pi,
- rxcore_state);
- }
+ if (rxcore_state == 1)
+ wlc_phy_rxcore_setstate_nphy(
+ (struct brcms_phy_pub *) pi,
+ rxcore_state);
}
}
@@ -25829,18 +27689,22 @@ wlc_phy_cal_rxiq_nphy_rev3(phy_info_t *pi, nphy_txgains_t target_gain,
for (rx_core = 0; rx_core < pi->pubpi.phy_corenum; rx_core++) {
rxlpf_rccal_hpc =
- (((int)best_rccal[rx_core] - 12) >> 1) + 10;
+ (((int)best_rccal[rx_core] - 12) >> 1) + 10;
txlpf_rccal_lpc = ((int)best_rccal[rx_core] - 12) + 10;
if (PHY_IPA(pi)) {
- txlpf_rccal_lpc += IS40MHZ(pi) ? 24 : 12;
- txlpf_idac = IS40MHZ(pi) ? 0x0e : 0x13;
+ txlpf_rccal_lpc +=
+ (pi->bw == WL_CHANSPEC_BW_40) ? 24 : 12;
+ txlpf_idac = (pi->bw == WL_CHANSPEC_BW_40) ?
+ 0x0e : 0x13;
WRITE_RADIO_REG2(pi, RADIO_2056, TX, rx_core,
TXLPF_IDAC_4, txlpf_idac);
}
- rxlpf_rccal_hpc = max(min_t(u8, rxlpf_rccal_hpc, 31), 0);
- txlpf_rccal_lpc = max(min_t(u8, txlpf_rccal_lpc, 31), 0);
+ rxlpf_rccal_hpc = max(min_t(u8, rxlpf_rccal_hpc, 31),
+ 0);
+ txlpf_rccal_lpc = max(min_t(u8, txlpf_rccal_lpc, 31),
+ 0);
write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_HPC |
((rx_core ==
@@ -25860,21 +27724,21 @@ wlc_phy_cal_rxiq_nphy_rev3(phy_info_t *pi, nphy_txgains_t target_gain,
wlc_phy_resetcca_nphy(pi);
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- wlc_phy_rfctrl_override_1tomany_nphy(pi,
- NPHY_REV7_RfctrlOverride_cmd_rxgain,
- 0, 0x3, 1);
- } else {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
+ wlc_phy_rfctrl_override_1tomany_nphy(
+ pi,
+ NPHY_REV7_RfctrlOverride_cmd_rxgain,
+ 0, 0x3, 1);
+ else
wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 1);
- }
+
wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX);
wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16,
gain_save);
- if (NREV_GE(pi->pubpi.phy_rev, 4)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 4))
pi->phyhang_avoid = phyhang_avoid_state;
- }
wlc_phy_stay_in_carriersearch_nphy(pi, false);
@@ -25882,10 +27746,10 @@ wlc_phy_cal_rxiq_nphy_rev3(phy_info_t *pi, nphy_txgains_t target_gain,
}
static int
-wlc_phy_cal_rxiq_nphy_rev2(phy_info_t *pi, nphy_txgains_t target_gain,
- bool debug)
+wlc_phy_cal_rxiq_nphy_rev2(struct brcms_phy *pi,
+ struct nphy_txgains target_gain, bool debug)
{
- phy_iq_est_t est[PHY_CORE_MAX];
+ struct phy_iq_est est[PHY_CORE_MAX];
u8 core_num, rx_core, tx_core;
u16 lna_vals[] = { 0x3, 0x3, 0x1 };
u16 hpf1_vals[] = { 0x7, 0x2, 0x0 };
@@ -25901,17 +27765,15 @@ wlc_phy_cal_rxiq_nphy_rev2(phy_info_t *pi, nphy_txgains_t target_gain,
u16 core_no;
u16 gain_save[2];
u16 cal_gain[2];
- nphy_iqcal_params_t cal_params[2];
+ struct nphy_iqcal_params cal_params[2];
u8 phy_bw;
int bcmerror = 0;
bool first_playtone = true;
wlc_phy_stay_in_carriersearch_nphy(pi, true);
- if (NREV_LT(pi->pubpi.phy_rev, 2)) {
-
+ if (NREV_LT(pi->pubpi.phy_rev, 2))
wlc_phy_reapply_txcal_coeffs_nphy(pi);
- }
wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save);
@@ -25947,19 +27809,16 @@ wlc_phy_cal_rxiq_nphy_rev2(phy_info_t *pi, nphy_txgains_t target_gain,
((0x1 << 1) | (0x1 << 2)));
or_phy_reg(pi, 0xa5, ((0x1 << 1) | (0x1 << 2)));
- if (((pi->nphy_rxcalparams) & 0xff000000)) {
-
+ if (((pi->nphy_rxcalparams) & 0xff000000))
write_phy_reg(pi,
(rx_core == PHY_CORE_0) ? 0x91 : 0x92,
- (CHSPEC_IS5G(pi->radio_chanspec) ? 0x140 :
- 0x110));
- } else {
-
+ (CHSPEC_IS5G(pi->radio_chanspec) ?
+ 0x140 : 0x110));
+ else
write_phy_reg(pi,
(rx_core == PHY_CORE_0) ? 0x91 : 0x92,
- (CHSPEC_IS5G(pi->radio_chanspec) ? 0x180 :
- 0x120));
- }
+ (CHSPEC_IS5G(pi->radio_chanspec) ?
+ 0x180 : 0x120));
write_phy_reg(pi, (tx_core == PHY_CORE_0) ? 0x91 : 0x92,
(CHSPEC_IS5G(pi->radio_chanspec) ? 0x148 :
@@ -25999,7 +27858,7 @@ wlc_phy_cal_rxiq_nphy_rev2(phy_info_t *pi, nphy_txgains_t target_gain,
use_hpf_num = 1;
curr_hpf = curr_hpf1;
actual_log2_pwr =
- wlc_phy_nbits(tot_pwr[2]);
+ wlc_phy_nbits(tot_pwr[2]);
} else {
if (tot_pwr[0] > 10000) {
curr_lna = lna_vals[1];
@@ -26008,7 +27867,8 @@ wlc_phy_cal_rxiq_nphy_rev2(phy_info_t *pi, nphy_txgains_t target_gain,
use_hpf_num = 1;
curr_hpf = curr_hpf1;
actual_log2_pwr =
- wlc_phy_nbits(tot_pwr[1]);
+ wlc_phy_nbits(
+ tot_pwr[1]);
} else {
curr_lna = lna_vals[0];
curr_hpf1 = hpf1_vals[0];
@@ -26016,18 +27876,18 @@ wlc_phy_cal_rxiq_nphy_rev2(phy_info_t *pi, nphy_txgains_t target_gain,
use_hpf_num = 2;
curr_hpf = curr_hpf2;
actual_log2_pwr =
- wlc_phy_nbits(tot_pwr[0]);
+ wlc_phy_nbits(
+ tot_pwr[0]);
}
}
hpf_change = desired_log2_pwr - actual_log2_pwr;
curr_hpf += hpf_change;
curr_hpf = max(min_t(u16, curr_hpf, 10), 0);
- if (use_hpf_num == 1) {
+ if (use_hpf_num == 1)
curr_hpf1 = curr_hpf;
- } else {
+ else
curr_hpf2 = curr_hpf;
- }
}
wlc_phy_rfctrl_override_nphy(pi, (0x1 << 10),
@@ -26040,15 +27900,12 @@ wlc_phy_cal_rxiq_nphy_rev2(phy_info_t *pi, nphy_txgains_t target_gain,
if (first_playtone) {
bcmerror = wlc_phy_tx_tone_nphy(pi, 4000,
- (u16) (pi->
- nphy_rxcalparams
- &
- 0xffff),
- 0, 0, true);
+ (u16) (pi->nphy_rxcalparams &
+ 0xffff), 0, 0, true);
first_playtone = false;
} else {
- phy_bw =
- (CHSPEC_IS40(pi->radio_chanspec)) ? 40 : 20;
+ phy_bw = (CHSPEC_IS40(pi->radio_chanspec)) ?
+ 40 : 20;
wlc_phy_runsamples_nphy(pi, phy_bw * 8, 0xffff,
0, 0, 0, true);
}
@@ -26059,12 +27916,10 @@ wlc_phy_cal_rxiq_nphy_rev2(phy_info_t *pi, nphy_txgains_t target_gain,
wlc_phy_rx_iq_est_nphy(pi, est,
num_samps, 32,
0);
- i_pwr =
- (est[rx_core].i_pwr +
- num_samps / 2) / num_samps;
- q_pwr =
- (est[rx_core].q_pwr +
- num_samps / 2) / num_samps;
+ i_pwr = (est[rx_core].i_pwr +
+ num_samps / 2) / num_samps;
+ q_pwr = (est[rx_core].q_pwr +
+ num_samps / 2) / num_samps;
tot_pwr[gain_pass] = i_pwr + q_pwr;
} else {
@@ -26108,1369 +27963,20 @@ wlc_phy_cal_rxiq_nphy_rev2(phy_info_t *pi, nphy_txgains_t target_gain,
}
int
-wlc_phy_cal_rxiq_nphy(phy_info_t *pi, nphy_txgains_t target_gain,
+wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain,
u8 cal_type, bool debug)
{
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
cal_type = 0;
- }
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
return wlc_phy_cal_rxiq_nphy_rev3(pi, target_gain, cal_type,
debug);
- } else {
+ else
return wlc_phy_cal_rxiq_nphy_rev2(pi, target_gain, debug);
- }
-}
-
-static void wlc_phy_extpa_set_tx_digi_filts_nphy(phy_info_t *pi)
-{
- int j, type = 2;
- u16 addr_offset = 0x2c5;
-
- for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) {
- write_phy_reg(pi, addr_offset + j,
- NPHY_IPA_REV4_txdigi_filtcoeffs[type][j]);
- }
-}
-
-static void wlc_phy_ipa_set_tx_digi_filts_nphy(phy_info_t *pi)
-{
- int j, type;
- u16 addr_offset[] = { 0x186, 0x195,
- 0x2c5
- };
-
- for (type = 0; type < 3; type++) {
- for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) {
- write_phy_reg(pi, addr_offset[type] + j,
- NPHY_IPA_REV4_txdigi_filtcoeffs[type][j]);
- }
- }
-
- if (IS40MHZ(pi)) {
- for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) {
- write_phy_reg(pi, 0x186 + j,
- NPHY_IPA_REV4_txdigi_filtcoeffs[3][j]);
- }
- } else {
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
- for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) {
- write_phy_reg(pi, 0x186 + j,
- NPHY_IPA_REV4_txdigi_filtcoeffs[5]
- [j]);
- }
- }
-
- if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) {
- for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) {
- write_phy_reg(pi, 0x2c5 + j,
- NPHY_IPA_REV4_txdigi_filtcoeffs[6]
- [j]);
- }
- }
- }
-}
-
-static void wlc_phy_ipa_restore_tx_digi_filts_nphy(phy_info_t *pi)
-{
- int j;
-
- if (IS40MHZ(pi)) {
- for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) {
- write_phy_reg(pi, 0x195 + j,
- NPHY_IPA_REV4_txdigi_filtcoeffs[4][j]);
- }
- } else {
- for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) {
- write_phy_reg(pi, 0x186 + j,
- NPHY_IPA_REV4_txdigi_filtcoeffs[3][j]);
- }
- }
-}
-
-static u16 wlc_phy_ipa_get_bbmult_nphy(phy_info_t *pi)
-{
- u16 m0m1;
-
- wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m0m1);
-
- return m0m1;
-}
-
-static void wlc_phy_ipa_set_bbmult_nphy(phy_info_t *pi, u8 m0, u8 m1)
-{
- u16 m0m1 = (u16) ((m0 << 8) | m1);
-
- wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m0m1);
- wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &m0m1);
-}
-
-static u32 *wlc_phy_get_ipa_gaintbl_nphy(phy_info_t *pi)
-{
- u32 *tx_pwrctrl_tbl = NULL;
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
- if ((pi->pubpi.radiorev == 4)
- || (pi->pubpi.radiorev == 6)) {
-
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_ipa_2g_2057rev4n6;
- } else if (pi->pubpi.radiorev == 3) {
-
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_ipa_2g_2057rev3;
- } else if (pi->pubpi.radiorev == 5) {
-
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_ipa_2g_2057rev5;
- } else if ((pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev == 8)) {
-
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_ipa_2g_2057rev7;
- }
-
- } else if (NREV_IS(pi->pubpi.phy_rev, 6)) {
-
- tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev6;
- if (pi->sh->chip == BCM47162_CHIP_ID) {
-
- tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev5;
- }
-
- } else if (NREV_IS(pi->pubpi.phy_rev, 5)) {
-
- tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev5;
- } else {
-
- tx_pwrctrl_tbl = nphy_tpc_txgain_ipa;
- }
-
- } else {
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- if ((pi->pubpi.radiorev == 3) ||
- (pi->pubpi.radiorev == 4) ||
- (pi->pubpi.radiorev == 6)) {
-
- tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g_2057;
- } else if ((pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev == 8)) {
-
- tx_pwrctrl_tbl =
- nphy_tpc_txgain_ipa_5g_2057rev7;
- }
-
- } else {
- tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g;
- }
- }
-
- return tx_pwrctrl_tbl;
-}
-
-static void
-wlc_phy_papd_cal_setup_nphy(phy_info_t *pi, nphy_papd_restore_state *state,
- u8 core)
-{
- s32 tone_freq;
- u8 off_core;
- u16 mixgain = 0;
-
- off_core = core ^ 0x1;
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
- if (NREV_IS(pi->pubpi.phy_rev, 7)
- || NREV_GE(pi->pubpi.phy_rev, 8)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7),
- wlc_phy_read_lpf_bw_ctl_nphy
- (pi, 0), 0, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- }
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if (pi->pubpi.radiorev == 5) {
- mixgain = (core == 0) ? 0x20 : 0x00;
-
- } else if ((pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev == 8)) {
-
- mixgain = 0x00;
-
- } else if ((pi->pubpi.radiorev <= 4)
- || (pi->pubpi.radiorev == 6)) {
-
- mixgain = 0x00;
- }
-
- } else {
- if ((pi->pubpi.radiorev == 4) ||
- (pi->pubpi.radiorev == 6)) {
-
- mixgain = 0x50;
- } else if ((pi->pubpi.radiorev == 3)
- || (pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev == 8)) {
-
- mixgain = 0x0;
- }
- }
-
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11),
- mixgain, (1 << core), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
-
- wlc_phy_rfctrl_override_1tomany_nphy(pi,
- NPHY_REV7_RfctrlOverride_cmd_tx_pu,
- 1, (1 << core), 0);
- wlc_phy_rfctrl_override_1tomany_nphy(pi,
- NPHY_REV7_RfctrlOverride_cmd_tx_pu,
- 0, (1 << off_core), 0);
-
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3),
- 0, 0x3, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 1,
- (1 << core), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0,
- (1 << core), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1,
- (1 << core), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID2);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 8), 0,
- (1 << core), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 1,
- (1 << core), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 0,
- (1 << core), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1,
- (1 << core), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
-
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5),
- 0, (1 << core), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 0,
- (1 << core), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
-
- state->afectrl[core] = read_phy_reg(pi, (core == PHY_CORE_0) ?
- 0xa6 : 0xa7);
- state->afeoverride[core] =
- read_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5);
- state->afectrl[off_core] =
- read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa7 : 0xa6);
- state->afeoverride[off_core] =
- read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa5 : 0x8f);
-
- mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa6 : 0xa7),
- (0x1 << 2), 0);
- mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f :
- 0xa5), (0x1 << 2), (0x1 << 2));
-
- mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa7 : 0xa6),
- (0x1 << 2), (0x1 << 2));
- mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa5 :
- 0x8f), (0x1 << 2), (0x1 << 2));
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- state->pwrup[core] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_2G_PWRUP);
- state->atten[core] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_2G_ATTEN);
- state->pwrup[off_core] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, off_core,
- TXRXCOUPLE_2G_PWRUP);
- state->atten[off_core] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, off_core,
- TXRXCOUPLE_2G_ATTEN);
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_2G_PWRUP, 0xc);
-
- if ((pi->pubpi.radiorev == 3) ||
- (pi->pubpi.radiorev == 4) ||
- (pi->pubpi.radiorev == 6)) {
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_2G_ATTEN, 0xf0);
-
- } else if (pi->pubpi.radiorev == 5) {
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_2G_ATTEN,
- (core == 0) ? 0xf7 : 0xf2);
-
- } else if ((pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev == 8)) {
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_2G_ATTEN, 0xf0);
-
- }
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core,
- TXRXCOUPLE_2G_PWRUP, 0x0);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core,
- TXRXCOUPLE_2G_ATTEN, 0xff);
-
- } else {
- state->pwrup[core] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_5G_PWRUP);
- state->atten[core] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_5G_ATTEN);
- state->pwrup[off_core] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, off_core,
- TXRXCOUPLE_5G_PWRUP);
- state->atten[off_core] =
- READ_RADIO_REG3(pi, RADIO_2057, TX, off_core,
- TXRXCOUPLE_5G_ATTEN);
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_5G_PWRUP, 0xc);
-
- if ((pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev == 8)) {
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_5G_ATTEN, 0xf4);
-
- } else {
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_5G_ATTEN, 0xf0);
- }
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core,
- TXRXCOUPLE_5G_PWRUP, 0x0);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core,
- TXRXCOUPLE_5G_ATTEN, 0xff);
- }
-
- tone_freq = 4000;
-
- wlc_phy_tx_tone_nphy(pi, tone_freq, 181, 0, 0, false);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 13), (1) << 13);
-
- mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_OFF) << 0);
-
- mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 13), (0) << 13);
-
- } else {
-
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 0);
-
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 1, 0, 0);
-
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0x3, 0);
-
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 2), 1, 0x3, 0);
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 1, 0x3, 0);
-
- state->afectrl[core] = read_phy_reg(pi, (core == PHY_CORE_0) ?
- 0xa6 : 0xa7);
- state->afeoverride[core] =
- read_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5);
-
- mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa6 : 0xa7),
- (0x1 << 0) | (0x1 << 1) | (0x1 << 2), 0);
- mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f :
- 0xa5),
- (0x1 << 0) |
- (0x1 << 1) |
- (0x1 << 2), (0x1 << 0) | (0x1 << 1) | (0x1 << 2));
-
- state->vga_master[core] =
- READ_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER);
- WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER, 0x2b);
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- state->fbmix[core] =
- READ_RADIO_REG2(pi, RADIO_2056, RX, core,
- TXFBMIX_G);
- state->intpa_master[core] =
- READ_RADIO_REG2(pi, RADIO_2056, TX, core,
- INTPAG_MASTER);
-
- WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_G,
- 0x03);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
- INTPAG_MASTER, 0x04);
- } else {
- state->fbmix[core] =
- READ_RADIO_REG2(pi, RADIO_2056, RX, core,
- TXFBMIX_A);
- state->intpa_master[core] =
- READ_RADIO_REG2(pi, RADIO_2056, TX, core,
- INTPAA_MASTER);
-
- WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_A,
- 0x03);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
- INTPAA_MASTER, 0x04);
-
- }
-
- tone_freq = 4000;
-
- wlc_phy_tx_tone_nphy(pi, tone_freq, 181, 0, 0, false);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (1) << 0);
-
- mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (0) << 0);
-
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 0);
- }
-}
-
-static void
-wlc_phy_papd_cal_cleanup_nphy(phy_info_t *pi, nphy_papd_restore_state *state)
-{
- u8 core;
-
- wlc_phy_stopplayback_nphy(pi);
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
- for (core = 0; core < pi->pubpi.phy_corenum; core++) {
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_2G_PWRUP, 0);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_2G_ATTEN,
- state->atten[core]);
- } else {
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_5G_PWRUP, 0);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TXRXCOUPLE_5G_ATTEN,
- state->atten[core]);
- }
- }
-
- if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2),
- 1, 0x3, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- } else {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2),
- 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- }
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1),
- 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID2);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID2);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 1, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 1, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID2);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 8), 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 1, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
-
- for (core = 0; core < pi->pubpi.phy_corenum; core++) {
-
- write_phy_reg(pi, (core == PHY_CORE_0) ?
- 0xa6 : 0xa7, state->afectrl[core]);
- write_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f :
- 0xa5, state->afeoverride[core]);
- }
-
- wlc_phy_ipa_set_bbmult_nphy(pi, (state->mm >> 8) & 0xff,
- (state->mm & 0xff));
-
- if (NREV_IS(pi->pubpi.phy_rev, 7)
- || NREV_GE(pi->pubpi.phy_rev, 8)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7), 0, 0,
- 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID1);
- }
- } else {
-
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 1);
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 0x3, 1);
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0x3, 1);
-
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 2), 0, 0x3, 1);
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 0, 0x3, 1);
-
- for (core = 0; core < pi->pubpi.phy_corenum; core++) {
-
- WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER,
- state->vga_master[core]);
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- WRITE_RADIO_REG2(pi, RADIO_2056, RX, core,
- TXFBMIX_G, state->fbmix[core]);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
- INTPAG_MASTER,
- state->intpa_master[core]);
- } else {
- WRITE_RADIO_REG2(pi, RADIO_2056, RX, core,
- TXFBMIX_A, state->fbmix[core]);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
- INTPAA_MASTER,
- state->intpa_master[core]);
- }
-
- write_phy_reg(pi, (core == PHY_CORE_0) ?
- 0xa6 : 0xa7, state->afectrl[core]);
- write_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f :
- 0xa5, state->afeoverride[core]);
- }
-
- wlc_phy_ipa_set_bbmult_nphy(pi, (state->mm >> 8) & 0xff,
- (state->mm & 0xff));
-
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 1);
- }
-}
-
-static void
-wlc_phy_a1_nphy(phy_info_t *pi, u8 core, u32 winsz, u32 start,
- u32 end)
-{
- u32 *buf, *src, *dst, sz;
-
- sz = end - start + 1;
-
- buf = kmalloc(2 * sizeof(u32) * NPHY_PAPD_EPS_TBL_SIZE, GFP_ATOMIC);
- if (NULL == buf) {
- return;
- }
-
- src = buf;
- dst = buf + NPHY_PAPD_EPS_TBL_SIZE;
-
- wlc_phy_table_read_nphy(pi,
- (core ==
- PHY_CORE_0 ? NPHY_TBL_ID_EPSILONTBL0 :
- NPHY_TBL_ID_EPSILONTBL1),
- NPHY_PAPD_EPS_TBL_SIZE, 0, 32, src);
-
- do {
- u32 phy_a1, phy_a2;
- s32 phy_a3, phy_a4, phy_a5, phy_a6, phy_a7;
-
- phy_a1 = end - min(end, (winsz >> 1));
- phy_a2 = min_t(u32, NPHY_PAPD_EPS_TBL_SIZE - 1, end + (winsz >> 1));
- phy_a3 = phy_a2 - phy_a1 + 1;
- phy_a6 = 0;
- phy_a7 = 0;
-
- do {
- wlc_phy_papd_decode_epsilon(src[phy_a2], &phy_a4,
- &phy_a5);
- phy_a6 += phy_a4;
- phy_a7 += phy_a5;
- } while (phy_a2-- != phy_a1);
-
- phy_a6 /= phy_a3;
- phy_a7 /= phy_a3;
- dst[end] = ((u32) phy_a7 << 13) | ((u32) phy_a6 & 0x1fff);
- } while (end-- != start);
-
- wlc_phy_table_write_nphy(pi,
- (core ==
- PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 :
- NPHY_TBL_ID_EPSILONTBL1, sz, start, 32, dst);
-
- kfree(buf);
-}
-
-static void
-wlc_phy_a2_nphy(phy_info_t *pi, nphy_ipa_txcalgains_t *txgains,
- phy_cal_mode_t cal_mode, u8 core)
-{
- u16 phy_a1, phy_a2, phy_a3;
- u16 phy_a4, phy_a5;
- bool phy_a6;
- u8 phy_a7, m[2];
- u32 phy_a8 = 0;
- nphy_txgains_t phy_a9;
-
- if (NREV_LT(pi->pubpi.phy_rev, 3))
- return;
-
- phy_a7 = (core == PHY_CORE_0) ? 1 : 0;
-
- phy_a6 = ((cal_mode == CAL_GCTRL)
- || (cal_mode == CAL_SOFT)) ? true : false;
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
- phy_a9 = wlc_phy_get_tx_gain_nphy(pi);
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- phy_a5 = ((phy_a9.txlpf[core] << 15) |
- (phy_a9.txgm[core] << 12) |
- (phy_a9.pga[core] << 8) |
- (txgains->gains.pad[core] << 3) |
- (phy_a9.ipa[core]));
- } else {
- phy_a5 = ((phy_a9.txlpf[core] << 15) |
- (phy_a9.txgm[core] << 12) |
- (txgains->gains.pga[core] << 8) |
- (phy_a9.pad[core] << 3) | (phy_a9.ipa[core]));
- }
-
- wlc_phy_rfctrl_override_1tomany_nphy(pi,
- NPHY_REV7_RfctrlOverride_cmd_txgain,
- phy_a5, (1 << core), 0);
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if ((pi->pubpi.radiorev <= 4)
- || (pi->pubpi.radiorev == 6)) {
-
- m[core] = IS40MHZ(pi) ? 60 : 79;
- } else {
-
- m[core] = IS40MHZ(pi) ? 45 : 64;
- }
-
- } else {
- m[core] = IS40MHZ(pi) ? 75 : 107;
- }
-
- m[phy_a7] = 0;
- wlc_phy_ipa_set_bbmult_nphy(pi, m[0], m[1]);
-
- phy_a2 = 63;
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if (pi->sh->chip == BCM6362_CHIP_ID) {
- phy_a1 = 35;
- phy_a3 = 35;
- } else if ((pi->pubpi.radiorev == 4)
- || (pi->pubpi.radiorev == 6)) {
- phy_a1 = 30;
- phy_a3 = 30;
- } else {
- phy_a1 = 25;
- phy_a3 = 25;
- }
- } else {
- if ((pi->pubpi.radiorev == 5)
- || (pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev == 8)) {
- phy_a1 = 25;
- phy_a3 = 25;
- } else {
- phy_a1 = 35;
- phy_a3 = 35;
- }
- }
-
- if (cal_mode == CAL_GCTRL) {
- if ((pi->pubpi.radiorev == 5)
- && (CHSPEC_IS2G(pi->radio_chanspec))) {
- phy_a1 = 55;
- } else if (((pi->pubpi.radiorev == 7) &&
- (CHSPEC_IS2G(pi->radio_chanspec))) ||
- ((pi->pubpi.radiorev == 8) &&
- (CHSPEC_IS2G(pi->radio_chanspec)))) {
- phy_a1 = 60;
- } else {
- phy_a1 = 63;
- }
-
- } else if ((cal_mode != CAL_FULL) && (cal_mode != CAL_SOFT)) {
-
- phy_a1 = 35;
- phy_a3 = 35;
- }
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (1) << 0);
-
- mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (0) << 0);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 13), (1) << 13);
-
- mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 13), (0) << 13);
-
- write_phy_reg(pi, 0x2a1, 0x80);
- write_phy_reg(pi, 0x2a2, 0x100);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x7 << 4), (11) << 4);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x7 << 8), (11) << 8);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x7 << 0), (0x3) << 0);
-
- write_phy_reg(pi, 0x2e5, 0x20);
-
- mod_phy_reg(pi, 0x2a0, (0x3f << 0), (phy_a3) << 0);
-
- mod_phy_reg(pi, 0x29f, (0x3f << 0), (phy_a1) << 0);
-
- mod_phy_reg(pi, 0x29f, (0x3f << 8), (phy_a2) << 8);
-
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3),
- 1, ((core == 0) ? 1 : 2), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3),
- 0, ((core == 0) ? 2 : 1), 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
-
- write_phy_reg(pi, 0x2be, 1);
- SPINWAIT(read_phy_reg(pi, 0x2be), 10 * 1000 * 1000);
-
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3),
- 0, 0x3, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
-
- wlc_phy_table_write_nphy(pi,
- (core ==
- PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0
- : NPHY_TBL_ID_EPSILONTBL1, 1, phy_a3,
- 32, &phy_a8);
-
- if (cal_mode != CAL_GCTRL) {
- if (CHSPEC_IS5G(pi->radio_chanspec)) {
- wlc_phy_a1_nphy(pi, core, 5, 0, 35);
- }
- }
-
- wlc_phy_rfctrl_override_1tomany_nphy(pi,
- NPHY_REV7_RfctrlOverride_cmd_txgain,
- phy_a5, (1 << core), 1);
-
- } else {
-
- if (txgains) {
- if (txgains->useindex) {
- phy_a4 = 15 - ((txgains->index) >> 3);
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if (NREV_GE(pi->pubpi.phy_rev, 6)) {
- phy_a5 = 0x00f7 | (phy_a4 << 8);
-
- if (pi->sh->chip ==
- BCM47162_CHIP_ID) {
- phy_a5 =
- 0x10f7 | (phy_a4 <<
- 8);
- }
- } else
- if (NREV_IS(pi->pubpi.phy_rev, 5))
- phy_a5 = 0x10f7 | (phy_a4 << 8);
- else
- phy_a5 = 0x50f7 | (phy_a4 << 8);
- } else {
- phy_a5 = 0x70f7 | (phy_a4 << 8);
- }
- wlc_phy_rfctrl_override_nphy(pi,
- (0x1 << 13),
- phy_a5,
- (1 << core), 0);
- } else {
- wlc_phy_rfctrl_override_nphy(pi,
- (0x1 << 13),
- 0x5bf7,
- (1 << core), 0);
- }
- }
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- m[core] = IS40MHZ(pi) ? 45 : 64;
- } else {
- m[core] = IS40MHZ(pi) ? 75 : 107;
- }
-
- m[phy_a7] = 0;
- wlc_phy_ipa_set_bbmult_nphy(pi, m[0], m[1]);
-
- phy_a2 = 63;
-
- if (cal_mode == CAL_FULL) {
- phy_a1 = 25;
- phy_a3 = 25;
- } else if (cal_mode == CAL_SOFT) {
- phy_a1 = 25;
- phy_a3 = 25;
- } else if (cal_mode == CAL_GCTRL) {
- phy_a1 = 63;
- phy_a3 = 25;
- } else {
-
- phy_a1 = 25;
- phy_a3 = 25;
- }
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (1) << 0);
-
- mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (0) << 0);
-
- if (NREV_GE(pi->pubpi.phy_rev, 6)) {
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 13), (1) << 13);
-
- mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 13), (0) << 13);
-
- write_phy_reg(pi, 0x2a1, 0x20);
- write_phy_reg(pi, 0x2a2, 0x60);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0xf << 4), (9) << 4);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0xf << 8), (9) << 8);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0xf << 0), (0x2) << 0);
-
- write_phy_reg(pi, 0x2e5, 0x20);
- } else {
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 11), (1) << 11);
-
- mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 11), (0) << 11);
-
- write_phy_reg(pi, 0x2a1, 0x80);
- write_phy_reg(pi, 0x2a2, 0x600);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x7 << 4), (0) << 4);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x7 << 8), (0) << 8);
-
- mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x7 << 0), (0x3) << 0);
-
- mod_phy_reg(pi, 0x2a0, (0x3f << 8), (0x20) << 8);
-
- }
-
- mod_phy_reg(pi, 0x2a0, (0x3f << 0), (phy_a3) << 0);
-
- mod_phy_reg(pi, 0x29f, (0x3f << 0), (phy_a1) << 0);
-
- mod_phy_reg(pi, 0x29f, (0x3f << 8), (phy_a2) << 8);
-
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 1, 0x3, 0);
-
- write_phy_reg(pi, 0x2be, 1);
- SPINWAIT(read_phy_reg(pi, 0x2be), 10 * 1000 * 1000);
-
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 0);
-
- wlc_phy_table_write_nphy(pi,
- (core ==
- PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0
- : NPHY_TBL_ID_EPSILONTBL1, 1, phy_a3,
- 32, &phy_a8);
-
- if (cal_mode != CAL_GCTRL) {
- wlc_phy_a1_nphy(pi, core, 5, 0, 40);
- }
- }
-}
-
-static u8 wlc_phy_a3_nphy(phy_info_t *pi, u8 start_gain, u8 core)
-{
- int phy_a1;
- int phy_a2;
- bool phy_a3;
- nphy_ipa_txcalgains_t phy_a4;
- bool phy_a5 = false;
- bool phy_a6 = true;
- s32 phy_a7, phy_a8;
- u32 phy_a9;
- int phy_a10;
- bool phy_a11 = false;
- int phy_a12;
- u8 phy_a13 = 0;
- u8 phy_a14;
- u8 *phy_a15 = NULL;
-
- phy_a4.useindex = true;
- phy_a12 = start_gain;
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
-
- phy_a2 = 20;
- phy_a1 = 1;
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if (pi->pubpi.radiorev == 5) {
-
- phy_a15 = pad_gain_codes_used_2057rev5;
- phy_a13 = sizeof(pad_gain_codes_used_2057rev5) /
- sizeof(pad_gain_codes_used_2057rev5[0]) - 1;
-
- } else if ((pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev == 8)) {
-
- phy_a15 = pad_gain_codes_used_2057rev7;
- phy_a13 = sizeof(pad_gain_codes_used_2057rev7) /
- sizeof(pad_gain_codes_used_2057rev7[0]) - 1;
-
- } else {
-
- phy_a15 = pad_all_gain_codes_2057;
- phy_a13 = sizeof(pad_all_gain_codes_2057) /
- sizeof(pad_all_gain_codes_2057[0]) - 1;
- }
-
- } else {
-
- phy_a15 = pga_all_gain_codes_2057;
- phy_a13 = sizeof(pga_all_gain_codes_2057) /
- sizeof(pga_all_gain_codes_2057[0]) - 1;
- }
-
- phy_a14 = 0;
-
- for (phy_a10 = 0; phy_a10 < phy_a2; phy_a10++) {
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- phy_a4.gains.pad[core] =
- (u16) phy_a15[phy_a12];
- } else {
- phy_a4.gains.pga[core] =
- (u16) phy_a15[phy_a12];
- }
-
- wlc_phy_a2_nphy(pi, &phy_a4, CAL_GCTRL, core);
-
- wlc_phy_table_read_nphy(pi,
- (core ==
- PHY_CORE_0 ?
- NPHY_TBL_ID_EPSILONTBL0 :
- NPHY_TBL_ID_EPSILONTBL1), 1,
- 63, 32, &phy_a9);
-
- wlc_phy_papd_decode_epsilon(phy_a9, &phy_a7, &phy_a8);
-
- phy_a3 = ((phy_a7 == 4095) || (phy_a7 == -4096) ||
- (phy_a8 == 4095) || (phy_a8 == -4096));
-
- if (!phy_a6 && (phy_a3 != phy_a5)) {
- if (!phy_a3) {
- phy_a12 -= (u8) phy_a1;
- }
- phy_a11 = true;
- break;
- }
-
- if (phy_a3)
- phy_a12 += (u8) phy_a1;
- else
- phy_a12 -= (u8) phy_a1;
-
- if ((phy_a12 < phy_a14) || (phy_a12 > phy_a13)) {
- if (phy_a12 < phy_a14) {
- phy_a12 = phy_a14;
- } else {
- phy_a12 = phy_a13;
- }
- phy_a11 = true;
- break;
- }
-
- phy_a6 = false;
- phy_a5 = phy_a3;
- }
-
- } else {
- phy_a2 = 10;
- phy_a1 = 8;
- for (phy_a10 = 0; phy_a10 < phy_a2; phy_a10++) {
- phy_a4.index = (u8) phy_a12;
- wlc_phy_a2_nphy(pi, &phy_a4, CAL_GCTRL, core);
-
- wlc_phy_table_read_nphy(pi,
- (core ==
- PHY_CORE_0 ?
- NPHY_TBL_ID_EPSILONTBL0 :
- NPHY_TBL_ID_EPSILONTBL1), 1,
- 63, 32, &phy_a9);
-
- wlc_phy_papd_decode_epsilon(phy_a9, &phy_a7, &phy_a8);
-
- phy_a3 = ((phy_a7 == 4095) || (phy_a7 == -4096) ||
- (phy_a8 == 4095) || (phy_a8 == -4096));
-
- if (!phy_a6 && (phy_a3 != phy_a5)) {
- if (!phy_a3) {
- phy_a12 -= (u8) phy_a1;
- }
- phy_a11 = true;
- break;
- }
-
- if (phy_a3)
- phy_a12 += (u8) phy_a1;
- else
- phy_a12 -= (u8) phy_a1;
-
- if ((phy_a12 < 0) || (phy_a12 > 127)) {
- if (phy_a12 < 0) {
- phy_a12 = 0;
- } else {
- phy_a12 = 127;
- }
- phy_a11 = true;
- break;
- }
-
- phy_a6 = false;
- phy_a5 = phy_a3;
- }
-
- }
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- return (u8) phy_a15[phy_a12];
- } else {
- return (u8) phy_a12;
- }
-
-}
-
-static void wlc_phy_a4(phy_info_t *pi, bool full_cal)
-{
- nphy_ipa_txcalgains_t phy_b1[2];
- nphy_papd_restore_state phy_b2;
- bool phy_b3;
- u8 phy_b4;
- u8 phy_b5;
- s16 phy_b6, phy_b7, phy_b8;
- u16 phy_b9;
- s16 phy_b10, phy_b11, phy_b12;
-
- phy_b11 = 0;
- phy_b12 = 0;
- phy_b7 = 0;
- phy_b8 = 0;
- phy_b6 = 0;
-
- if (pi->nphy_papd_skip == 1)
- return;
-
- phy_b3 =
- (0 == (R_REG(&pi->regs->maccontrol) & MCTL_EN_MAC));
- if (!phy_b3) {
- wlapi_suspend_mac_and_wait(pi->sh->physhim);
- }
-
- wlc_phy_stay_in_carriersearch_nphy(pi, true);
-
- pi->nphy_force_papd_cal = false;
-
- for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++)
- pi->nphy_papd_tx_gain_at_last_cal[phy_b5] =
- wlc_phy_txpwr_idx_cur_get_nphy(pi, phy_b5);
-
- pi->nphy_papd_last_cal = pi->sh->now;
- pi->nphy_papd_recal_counter++;
-
- if (NORADIO_ENAB(pi->pubpi))
- return;
-
- phy_b4 = pi->nphy_txpwrctrl;
- wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF);
-
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SCALARTBL0, 64, 0, 32,
- nphy_papd_scaltbl);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SCALARTBL1, 64, 0, 32,
- nphy_papd_scaltbl);
-
- phy_b9 = read_phy_reg(pi, 0x01);
- mod_phy_reg(pi, 0x01, (0x1 << 15), 0);
-
- for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) {
- s32 i, val = 0;
- for (i = 0; i < 64; i++) {
- wlc_phy_table_write_nphy(pi,
- ((phy_b5 ==
- PHY_CORE_0) ?
- NPHY_TBL_ID_EPSILONTBL0 :
- NPHY_TBL_ID_EPSILONTBL1), 1,
- i, 32, &val);
- }
- }
-
- wlc_phy_ipa_restore_tx_digi_filts_nphy(pi);
-
- phy_b2.mm = wlc_phy_ipa_get_bbmult_nphy(pi);
- for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) {
- wlc_phy_papd_cal_setup_nphy(pi, &phy_b2, phy_b5);
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
-
- if ((pi->pubpi.radiorev == 3)
- || (pi->pubpi.radiorev == 4)
- || (pi->pubpi.radiorev == 6)) {
-
- pi->nphy_papd_cal_gain_index[phy_b5] =
- 23;
-
- } else if (pi->pubpi.radiorev == 5) {
-
- pi->nphy_papd_cal_gain_index[phy_b5] =
- 0;
- pi->nphy_papd_cal_gain_index[phy_b5] =
- wlc_phy_a3_nphy(pi,
- pi->
- nphy_papd_cal_gain_index
- [phy_b5], phy_b5);
-
- } else if ((pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev == 8)) {
-
- pi->nphy_papd_cal_gain_index[phy_b5] =
- 0;
- pi->nphy_papd_cal_gain_index[phy_b5] =
- wlc_phy_a3_nphy(pi,
- pi->
- nphy_papd_cal_gain_index
- [phy_b5], phy_b5);
-
- }
-
- phy_b1[phy_b5].gains.pad[phy_b5] =
- pi->nphy_papd_cal_gain_index[phy_b5];
-
- } else {
- pi->nphy_papd_cal_gain_index[phy_b5] = 0;
- pi->nphy_papd_cal_gain_index[phy_b5] =
- wlc_phy_a3_nphy(pi,
- pi->
- nphy_papd_cal_gain_index
- [phy_b5], phy_b5);
- phy_b1[phy_b5].gains.pga[phy_b5] =
- pi->nphy_papd_cal_gain_index[phy_b5];
- }
- } else {
- phy_b1[phy_b5].useindex = true;
- phy_b1[phy_b5].index = 16;
- phy_b1[phy_b5].index =
- wlc_phy_a3_nphy(pi, phy_b1[phy_b5].index, phy_b5);
-
- pi->nphy_papd_cal_gain_index[phy_b5] =
- 15 - ((phy_b1[phy_b5].index) >> 3);
- }
-
- switch (pi->nphy_papd_cal_type) {
- case 0:
- wlc_phy_a2_nphy(pi, &phy_b1[phy_b5], CAL_FULL, phy_b5);
- break;
- case 1:
- wlc_phy_a2_nphy(pi, &phy_b1[phy_b5], CAL_SOFT, phy_b5);
- break;
- }
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- wlc_phy_papd_cal_cleanup_nphy(pi, &phy_b2);
- }
- }
-
- if (NREV_LT(pi->pubpi.phy_rev, 7)) {
- wlc_phy_papd_cal_cleanup_nphy(pi, &phy_b2);
- }
-
- for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) {
- int eps_offset = 0;
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- if (pi->pubpi.radiorev == 3) {
- eps_offset = -2;
- } else if (pi->pubpi.radiorev == 5) {
- eps_offset = 3;
- } else {
- eps_offset = -1;
- }
- } else {
- eps_offset = 2;
- }
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- phy_b8 = phy_b1[phy_b5].gains.pad[phy_b5];
- phy_b10 = 0;
- if ((pi->pubpi.radiorev == 3) ||
- (pi->pubpi.radiorev == 4) ||
- (pi->pubpi.radiorev == 6)) {
- phy_b12 =
- -
- (nphy_papd_padgain_dlt_2g_2057rev3n4
- [phy_b8]
- + 1) / 2;
- phy_b10 = -1;
- } else if (pi->pubpi.radiorev == 5) {
- phy_b12 =
- -(nphy_papd_padgain_dlt_2g_2057rev5
- [phy_b8]
- + 1) / 2;
- } else if ((pi->pubpi.radiorev == 7) ||
- (pi->pubpi.radiorev == 8)) {
- phy_b12 =
- -(nphy_papd_padgain_dlt_2g_2057rev7
- [phy_b8]
- + 1) / 2;
- }
- } else {
- phy_b7 = phy_b1[phy_b5].gains.pga[phy_b5];
- if ((pi->pubpi.radiorev == 3) ||
- (pi->pubpi.radiorev == 4) ||
- (pi->pubpi.radiorev == 6)) {
- phy_b11 =
- -(nphy_papd_pgagain_dlt_5g_2057
- [phy_b7]
- + 1) / 2;
- } else if ((pi->pubpi.radiorev == 7)
- || (pi->pubpi.radiorev == 8)) {
- phy_b11 =
- -(nphy_papd_pgagain_dlt_5g_2057rev7
- [phy_b7]
- + 1) / 2;
- }
-
- phy_b10 = -9;
- }
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- phy_b6 =
- -60 + 27 + eps_offset + phy_b12 + phy_b10;
- } else {
- phy_b6 =
- -60 + 27 + eps_offset + phy_b11 + phy_b10;
- }
-
- mod_phy_reg(pi, (phy_b5 == PHY_CORE_0) ? 0x298 :
- 0x29c, (0x1ff << 7), (phy_b6) << 7);
-
- pi->nphy_papd_epsilon_offset[phy_b5] = phy_b6;
- } else {
- if (NREV_LT(pi->pubpi.phy_rev, 5)) {
- eps_offset = 4;
- } else {
- eps_offset = 2;
- }
-
- phy_b7 = 15 - ((phy_b1[phy_b5].index) >> 3);
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- phy_b11 =
- -(nphy_papd_pga_gain_delta_ipa_2g[phy_b7] +
- 1) / 2;
- phy_b10 = 0;
- } else {
- phy_b11 =
- -(nphy_papd_pga_gain_delta_ipa_5g[phy_b7] +
- 1) / 2;
- phy_b10 = -9;
- }
-
- phy_b6 = -60 + 27 + eps_offset + phy_b11 + phy_b10;
-
- mod_phy_reg(pi, (phy_b5 == PHY_CORE_0) ? 0x298 :
- 0x29c, (0x1ff << 7), (phy_b6) << 7);
-
- pi->nphy_papd_epsilon_offset[phy_b5] = phy_b6;
- }
- }
-
- mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0);
-
- mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 :
- 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0);
-
- if (NREV_GE(pi->pubpi.phy_rev, 6)) {
- mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 13), (0) << 13);
-
- mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 13), (0) << 13);
-
- } else {
- mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 11), (0) << 11);
-
- mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x2a3 :
- 0x2a4, (0x1 << 11), (0) << 11);
-
- }
- pi->nphy_papdcomp = NPHY_PAPD_COMP_ON;
-
- write_phy_reg(pi, 0x01, phy_b9);
-
- wlc_phy_ipa_set_tx_digi_filts_nphy(pi);
-
- wlc_phy_txpwrctrl_enable_nphy(pi, phy_b4);
- if (phy_b4 == PHY_TPC_HW_OFF) {
- wlc_phy_txpwr_index_nphy(pi, (1 << 0),
- (s8) (pi->nphy_txpwrindex[0].
- index_internal), false);
- wlc_phy_txpwr_index_nphy(pi, (1 << 1),
- (s8) (pi->nphy_txpwrindex[1].
- index_internal), false);
- }
-
- wlc_phy_stay_in_carriersearch_nphy(pi, false);
-
- if (!phy_b3) {
- wlapi_enable_mac(pi->sh->physhim);
- }
}
-void wlc_phy_txpwr_fixpower_nphy(phy_info_t *pi)
+void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi)
{
uint core;
u32 txgain;
@@ -27509,11 +28015,10 @@ void wlc_phy_txpwr_fixpower_nphy(phy_info_t *pi)
}
}
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 7))
txpi[0] = txpi[1] = 30;
- } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ else if (NREV_GE(pi->pubpi.phy_rev, 3))
txpi[0] = txpi[1] = 40;
- }
if (NREV_LT(pi->pubpi.phy_rev, 7)) {
@@ -27528,42 +28033,41 @@ void wlc_phy_txpwr_fixpower_nphy(phy_info_t *pi)
pi->nphy_txpwrindex[PHY_CORE_1].index_internal_save = txpi[1];
for (core = 0; core < pi->pubpi.phy_corenum; core++) {
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ uint phyrev = pi->pubpi.phy_rev;
+
+ if (NREV_GE(phyrev, 3)) {
if (PHY_IPA(pi)) {
u32 *tx_gaintbl =
- wlc_phy_get_ipa_gaintbl_nphy(pi);
+ wlc_phy_get_ipa_gaintbl_nphy(pi);
txgain = tx_gaintbl[txpi[core]];
} else {
if (CHSPEC_IS5G(pi->radio_chanspec)) {
- if NREV_IS
- (pi->pubpi.phy_rev, 3) {
- txgain =
- nphy_tpc_5GHz_txgain_rev3
- [txpi[core]];
- } else if NREV_IS
- (pi->pubpi.phy_rev, 4) {
+ if (NREV_IS(phyrev, 3)) {
txgain =
- (pi->srom_fem5g.extpagain ==
- 3) ?
- nphy_tpc_5GHz_txgain_HiPwrEPA
- [txpi[core]] :
- nphy_tpc_5GHz_txgain_rev4
- [txpi[core]];
+ nphy_tpc_5GHz_txgain_rev3
+ [txpi[core]];
+ } else if (NREV_IS(phyrev, 4)) {
+ txgain = (
+ pi->srom_fem5g.extpagain ==
+ 3) ?
+ nphy_tpc_5GHz_txgain_HiPwrEPA
+ [txpi[core]] :
+ nphy_tpc_5GHz_txgain_rev4
+ [txpi[core]];
} else {
txgain =
- nphy_tpc_5GHz_txgain_rev5
- [txpi[core]];
+ nphy_tpc_5GHz_txgain_rev5
+ [txpi[core]];
}
} else {
- if (NREV_GE(pi->pubpi.phy_rev, 5) &&
+ if (NREV_GE(phyrev, 5) &&
(pi->srom_fem2g.extpagain == 3)) {
txgain =
- nphy_tpc_txgain_HiPwrEPA
- [txpi[core]];
+ nphy_tpc_txgain_HiPwrEPA
+ [txpi[core]];
} else {
- txgain =
- nphy_tpc_txgain_rev3[txpi
- [core]];
+ txgain = nphy_tpc_txgain_rev3
+ [txpi[core]];
}
}
}
@@ -27571,25 +28075,24 @@ void wlc_phy_txpwr_fixpower_nphy(phy_info_t *pi)
txgain = nphy_tpc_txgain[txpi[core]];
}
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (NREV_GE(phyrev, 3))
rad_gain = (txgain >> 16) & ((1 << (32 - 16 + 1)) - 1);
- } else {
+ else
rad_gain = (txgain >> 16) & ((1 << (28 - 16 + 1)) - 1);
- }
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
+ if (NREV_GE(phyrev, 7))
dac_gain = (txgain >> 8) & ((1 << (10 - 8 + 1)) - 1);
- } else {
+ else
dac_gain = (txgain >> 8) & ((1 << (13 - 8 + 1)) - 1);
- }
+
bbmult = (txgain >> 0) & ((1 << (7 - 0 + 1)) - 1);
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (NREV_GE(phyrev, 3))
mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f :
0xa5), (0x1 << 8), (0x1 << 8));
- } else {
+ else
mod_phy_reg(pi, 0xa5, (0x1 << 14), (0x1 << 14));
- }
+
write_phy_reg(pi, (core == PHY_CORE_0) ? 0xaa : 0xab, dac_gain);
wlc_phy_table_write_nphy(pi, 7, 1, (0x110 + core), 16,
@@ -27649,9 +28152,8 @@ wlc_phy_txpwr_nphy_po_apply(u8 *srom_max, u8 pwr_offset,
{
u8 rate;
- for (rate = rate_start; rate <= rate_end; rate++) {
+ for (rate = rate_start; rate <= rate_end; rate++)
srom_max[rate] -= 2 * pwr_offset;
- }
}
void
@@ -27682,7 +28184,7 @@ wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, u8 rate_ofdm_start,
}
}
-void wlc_phy_txpwr_apply_nphy(phy_info_t *pi)
+void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi)
{
uint rate1, rate2, band_num;
u8 tmp_bw40po = 0, tmp_cddpo = 0, tmp_stbcpo = 0;
@@ -27690,7 +28192,8 @@ void wlc_phy_txpwr_apply_nphy(phy_info_t *pi)
u16 pwr_offsets1[2], *pwr_offsets2 = NULL;
u8 *tx_srom_max_rate = NULL;
- for (band_num = 0; band_num < (CH_2G_GROUP + CH_5G_GROUP); band_num++) {
+ for (band_num = 0; band_num < (CH_2G_GROUP + CH_5G_GROUP);
+ band_num++) {
switch (band_num) {
case 0:
@@ -27706,7 +28209,7 @@ void wlc_phy_txpwr_apply_nphy(phy_info_t *pi)
pwr_offsets1[0] = (u16) (pi->ofdm2gpo & 0xffff);
pwr_offsets1[1] =
- (u16) (pi->ofdm2gpo >> 16) & 0xffff;
+ (u16) (pi->ofdm2gpo >> 16) & 0xffff;
pwr_offsets2 = pi->mcs2gpo;
@@ -27723,7 +28226,7 @@ void wlc_phy_txpwr_apply_nphy(phy_info_t *pi)
pwr_offsets1[0] = (u16) (pi->ofdm5gpo & 0xffff);
pwr_offsets1[1] =
- (u16) (pi->ofdm5gpo >> 16) & 0xffff;
+ (u16) (pi->ofdm5gpo >> 16) & 0xffff;
pwr_offsets2 = pi->mcs5gpo;
@@ -27740,7 +28243,7 @@ void wlc_phy_txpwr_apply_nphy(phy_info_t *pi)
pwr_offsets1[0] = (u16) (pi->ofdm5glpo & 0xffff);
pwr_offsets1[1] =
- (u16) (pi->ofdm5glpo >> 16) & 0xffff;
+ (u16) (pi->ofdm5glpo >> 16) & 0xffff;
pwr_offsets2 = pi->mcs5glpo;
@@ -27757,7 +28260,7 @@ void wlc_phy_txpwr_apply_nphy(phy_info_t *pi)
pwr_offsets1[0] = (u16) (pi->ofdm5ghpo & 0xffff);
pwr_offsets1[1] =
- (u16) (pi->ofdm5ghpo >> 16) & 0xffff;
+ (u16) (pi->ofdm5ghpo >> 16) & 0xffff;
pwr_offsets2 = pi->mcs5ghpo;
@@ -27783,12 +28286,10 @@ void wlc_phy_txpwr_apply_nphy(phy_info_t *pi)
TXP_FIRST_MCS_20_CDD,
TXP_LAST_MCS_20_CDD);
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
-
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, tmp_cddpo,
TXP_FIRST_MCS_20_CDD,
TXP_LAST_MCS_20_CDD);
- }
wlc_phy_mcs_to_ofdm_powers_nphy(tx_srom_max_rate,
TXP_FIRST_OFDM_20_CDD,
@@ -27800,13 +28301,11 @@ void wlc_phy_txpwr_apply_nphy(phy_info_t *pi)
TXP_FIRST_MCS_20_STBC,
TXP_LAST_MCS_20_STBC);
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
-
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate,
tmp_stbcpo,
TXP_FIRST_MCS_20_STBC,
TXP_LAST_MCS_20_STBC);
- }
wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate,
&pwr_offsets2[2], tmp_max_pwr,
@@ -27860,295 +28359,27 @@ void wlc_phy_txpwr_apply_nphy(phy_info_t *pi)
} else {
for (rate1 = TXP_FIRST_OFDM_40_SISO, rate2 =
- TXP_FIRST_OFDM; rate1 <= TXP_LAST_MCS_40_SDM;
+ TXP_FIRST_OFDM;
+ rate1 <= TXP_LAST_MCS_40_SDM;
rate1++, rate2++)
tx_srom_max_rate[rate1] =
- tx_srom_max_rate[rate2];
+ tx_srom_max_rate[rate2];
}
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate,
tmp_bw40po,
TXP_FIRST_OFDM_40_SISO,
TXP_LAST_MCS_40_SDM);
- }
tx_srom_max_rate[TXP_MCS_32] =
- tx_srom_max_rate[TXP_FIRST_MCS_40_CDD];
+ tx_srom_max_rate[TXP_FIRST_MCS_40_CDD];
}
return;
}
-static void wlc_phy_txpwr_srom_read_ppr_nphy(phy_info_t *pi)
-{
- u16 bw40po, cddpo, stbcpo, bwduppo;
- uint band_num;
-
- if (pi->sh->sromrev >= 9) {
-
- return;
- }
-
- bw40po = (u16) PHY_GETINTVAR(pi, "bw40po");
- pi->bw402gpo = bw40po & 0xf;
- pi->bw405gpo = (bw40po & 0xf0) >> 4;
- pi->bw405glpo = (bw40po & 0xf00) >> 8;
- pi->bw405ghpo = (bw40po & 0xf000) >> 12;
-
- cddpo = (u16) PHY_GETINTVAR(pi, "cddpo");
- pi->cdd2gpo = cddpo & 0xf;
- pi->cdd5gpo = (cddpo & 0xf0) >> 4;
- pi->cdd5glpo = (cddpo & 0xf00) >> 8;
- pi->cdd5ghpo = (cddpo & 0xf000) >> 12;
-
- stbcpo = (u16) PHY_GETINTVAR(pi, "stbcpo");
- pi->stbc2gpo = stbcpo & 0xf;
- pi->stbc5gpo = (stbcpo & 0xf0) >> 4;
- pi->stbc5glpo = (stbcpo & 0xf00) >> 8;
- pi->stbc5ghpo = (stbcpo & 0xf000) >> 12;
-
- bwduppo = (u16) PHY_GETINTVAR(pi, "bwduppo");
- pi->bwdup2gpo = bwduppo & 0xf;
- pi->bwdup5gpo = (bwduppo & 0xf0) >> 4;
- pi->bwdup5glpo = (bwduppo & 0xf00) >> 8;
- pi->bwdup5ghpo = (bwduppo & 0xf000) >> 12;
-
- for (band_num = 0; band_num < (CH_2G_GROUP + CH_5G_GROUP); band_num++) {
- switch (band_num) {
- case 0:
-
- pi->nphy_txpid2g[PHY_CORE_0] =
- (u8) PHY_GETINTVAR(pi, "txpid2ga0");
- pi->nphy_txpid2g[PHY_CORE_1] =
- (u8) PHY_GETINTVAR(pi, "txpid2ga1");
- pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_2g =
- (s8) PHY_GETINTVAR(pi, "maxp2ga0");
- pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_2g =
- (s8) PHY_GETINTVAR(pi, "maxp2ga1");
- pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_a1 =
- (s16) PHY_GETINTVAR(pi, "pa2gw0a0");
- pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_a1 =
- (s16) PHY_GETINTVAR(pi, "pa2gw0a1");
- pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_b0 =
- (s16) PHY_GETINTVAR(pi, "pa2gw1a0");
- pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_b0 =
- (s16) PHY_GETINTVAR(pi, "pa2gw1a1");
- pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_b1 =
- (s16) PHY_GETINTVAR(pi, "pa2gw2a0");
- pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_b1 =
- (s16) PHY_GETINTVAR(pi, "pa2gw2a1");
- pi->nphy_pwrctrl_info[PHY_CORE_0].idle_targ_2g =
- (s8) PHY_GETINTVAR(pi, "itt2ga0");
- pi->nphy_pwrctrl_info[PHY_CORE_1].idle_targ_2g =
- (s8) PHY_GETINTVAR(pi, "itt2ga1");
-
- pi->cck2gpo = (u16) PHY_GETINTVAR(pi, "cck2gpo");
-
- pi->ofdm2gpo = (u32) PHY_GETINTVAR(pi, "ofdm2gpo");
-
- pi->mcs2gpo[0] = (u16) PHY_GETINTVAR(pi, "mcs2gpo0");
- pi->mcs2gpo[1] = (u16) PHY_GETINTVAR(pi, "mcs2gpo1");
- pi->mcs2gpo[2] = (u16) PHY_GETINTVAR(pi, "mcs2gpo2");
- pi->mcs2gpo[3] = (u16) PHY_GETINTVAR(pi, "mcs2gpo3");
- pi->mcs2gpo[4] = (u16) PHY_GETINTVAR(pi, "mcs2gpo4");
- pi->mcs2gpo[5] = (u16) PHY_GETINTVAR(pi, "mcs2gpo5");
- pi->mcs2gpo[6] = (u16) PHY_GETINTVAR(pi, "mcs2gpo6");
- pi->mcs2gpo[7] = (u16) PHY_GETINTVAR(pi, "mcs2gpo7");
- break;
- case 1:
-
- pi->nphy_txpid5g[PHY_CORE_0] =
- (u8) PHY_GETINTVAR(pi, "txpid5ga0");
- pi->nphy_txpid5g[PHY_CORE_1] =
- (u8) PHY_GETINTVAR(pi, "txpid5ga1");
- pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_5gm =
- (s8) PHY_GETINTVAR(pi, "maxp5ga0");
- pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_5gm =
- (s8) PHY_GETINTVAR(pi, "maxp5ga1");
- pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_a1 =
- (s16) PHY_GETINTVAR(pi, "pa5gw0a0");
- pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_a1 =
- (s16) PHY_GETINTVAR(pi, "pa5gw0a1");
- pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_b0 =
- (s16) PHY_GETINTVAR(pi, "pa5gw1a0");
- pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_b0 =
- (s16) PHY_GETINTVAR(pi, "pa5gw1a1");
- pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_b1 =
- (s16) PHY_GETINTVAR(pi, "pa5gw2a0");
- pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_b1 =
- (s16) PHY_GETINTVAR(pi, "pa5gw2a1");
- pi->nphy_pwrctrl_info[PHY_CORE_0].idle_targ_5gm =
- (s8) PHY_GETINTVAR(pi, "itt5ga0");
- pi->nphy_pwrctrl_info[PHY_CORE_1].idle_targ_5gm =
- (s8) PHY_GETINTVAR(pi, "itt5ga1");
-
- pi->ofdm5gpo = (u32) PHY_GETINTVAR(pi, "ofdm5gpo");
-
- pi->mcs5gpo[0] = (u16) PHY_GETINTVAR(pi, "mcs5gpo0");
- pi->mcs5gpo[1] = (u16) PHY_GETINTVAR(pi, "mcs5gpo1");
- pi->mcs5gpo[2] = (u16) PHY_GETINTVAR(pi, "mcs5gpo2");
- pi->mcs5gpo[3] = (u16) PHY_GETINTVAR(pi, "mcs5gpo3");
- pi->mcs5gpo[4] = (u16) PHY_GETINTVAR(pi, "mcs5gpo4");
- pi->mcs5gpo[5] = (u16) PHY_GETINTVAR(pi, "mcs5gpo5");
- pi->mcs5gpo[6] = (u16) PHY_GETINTVAR(pi, "mcs5gpo6");
- pi->mcs5gpo[7] = (u16) PHY_GETINTVAR(pi, "mcs5gpo7");
- break;
- case 2:
-
- pi->nphy_txpid5gl[0] =
- (u8) PHY_GETINTVAR(pi, "txpid5gla0");
- pi->nphy_txpid5gl[1] =
- (u8) PHY_GETINTVAR(pi, "txpid5gla1");
- pi->nphy_pwrctrl_info[0].max_pwr_5gl =
- (s8) PHY_GETINTVAR(pi, "maxp5gla0");
- pi->nphy_pwrctrl_info[1].max_pwr_5gl =
- (s8) PHY_GETINTVAR(pi, "maxp5gla1");
- pi->nphy_pwrctrl_info[0].pwrdet_5gl_a1 =
- (s16) PHY_GETINTVAR(pi, "pa5glw0a0");
- pi->nphy_pwrctrl_info[1].pwrdet_5gl_a1 =
- (s16) PHY_GETINTVAR(pi, "pa5glw0a1");
- pi->nphy_pwrctrl_info[0].pwrdet_5gl_b0 =
- (s16) PHY_GETINTVAR(pi, "pa5glw1a0");
- pi->nphy_pwrctrl_info[1].pwrdet_5gl_b0 =
- (s16) PHY_GETINTVAR(pi, "pa5glw1a1");
- pi->nphy_pwrctrl_info[0].pwrdet_5gl_b1 =
- (s16) PHY_GETINTVAR(pi, "pa5glw2a0");
- pi->nphy_pwrctrl_info[1].pwrdet_5gl_b1 =
- (s16) PHY_GETINTVAR(pi, "pa5glw2a1");
- pi->nphy_pwrctrl_info[0].idle_targ_5gl = 0;
- pi->nphy_pwrctrl_info[1].idle_targ_5gl = 0;
-
- pi->ofdm5glpo = (u32) PHY_GETINTVAR(pi, "ofdm5glpo");
-
- pi->mcs5glpo[0] =
- (u16) PHY_GETINTVAR(pi, "mcs5glpo0");
- pi->mcs5glpo[1] =
- (u16) PHY_GETINTVAR(pi, "mcs5glpo1");
- pi->mcs5glpo[2] =
- (u16) PHY_GETINTVAR(pi, "mcs5glpo2");
- pi->mcs5glpo[3] =
- (u16) PHY_GETINTVAR(pi, "mcs5glpo3");
- pi->mcs5glpo[4] =
- (u16) PHY_GETINTVAR(pi, "mcs5glpo4");
- pi->mcs5glpo[5] =
- (u16) PHY_GETINTVAR(pi, "mcs5glpo5");
- pi->mcs5glpo[6] =
- (u16) PHY_GETINTVAR(pi, "mcs5glpo6");
- pi->mcs5glpo[7] =
- (u16) PHY_GETINTVAR(pi, "mcs5glpo7");
- break;
- case 3:
-
- pi->nphy_txpid5gh[0] =
- (u8) PHY_GETINTVAR(pi, "txpid5gha0");
- pi->nphy_txpid5gh[1] =
- (u8) PHY_GETINTVAR(pi, "txpid5gha1");
- pi->nphy_pwrctrl_info[0].max_pwr_5gh =
- (s8) PHY_GETINTVAR(pi, "maxp5gha0");
- pi->nphy_pwrctrl_info[1].max_pwr_5gh =
- (s8) PHY_GETINTVAR(pi, "maxp5gha1");
- pi->nphy_pwrctrl_info[0].pwrdet_5gh_a1 =
- (s16) PHY_GETINTVAR(pi, "pa5ghw0a0");
- pi->nphy_pwrctrl_info[1].pwrdet_5gh_a1 =
- (s16) PHY_GETINTVAR(pi, "pa5ghw0a1");
- pi->nphy_pwrctrl_info[0].pwrdet_5gh_b0 =
- (s16) PHY_GETINTVAR(pi, "pa5ghw1a0");
- pi->nphy_pwrctrl_info[1].pwrdet_5gh_b0 =
- (s16) PHY_GETINTVAR(pi, "pa5ghw1a1");
- pi->nphy_pwrctrl_info[0].pwrdet_5gh_b1 =
- (s16) PHY_GETINTVAR(pi, "pa5ghw2a0");
- pi->nphy_pwrctrl_info[1].pwrdet_5gh_b1 =
- (s16) PHY_GETINTVAR(pi, "pa5ghw2a1");
- pi->nphy_pwrctrl_info[0].idle_targ_5gh = 0;
- pi->nphy_pwrctrl_info[1].idle_targ_5gh = 0;
-
- pi->ofdm5ghpo = (u32) PHY_GETINTVAR(pi, "ofdm5ghpo");
-
- pi->mcs5ghpo[0] =
- (u16) PHY_GETINTVAR(pi, "mcs5ghpo0");
- pi->mcs5ghpo[1] =
- (u16) PHY_GETINTVAR(pi, "mcs5ghpo1");
- pi->mcs5ghpo[2] =
- (u16) PHY_GETINTVAR(pi, "mcs5ghpo2");
- pi->mcs5ghpo[3] =
- (u16) PHY_GETINTVAR(pi, "mcs5ghpo3");
- pi->mcs5ghpo[4] =
- (u16) PHY_GETINTVAR(pi, "mcs5ghpo4");
- pi->mcs5ghpo[5] =
- (u16) PHY_GETINTVAR(pi, "mcs5ghpo5");
- pi->mcs5ghpo[6] =
- (u16) PHY_GETINTVAR(pi, "mcs5ghpo6");
- pi->mcs5ghpo[7] =
- (u16) PHY_GETINTVAR(pi, "mcs5ghpo7");
- break;
- }
- }
-
- wlc_phy_txpwr_apply_nphy(pi);
-}
-
-static bool wlc_phy_txpwr_srom_read_nphy(phy_info_t *pi)
-{
-
- pi->antswitch = (u8) PHY_GETINTVAR(pi, "antswitch");
- pi->aa2g = (u8) PHY_GETINTVAR(pi, "aa2g");
- pi->aa5g = (u8) PHY_GETINTVAR(pi, "aa5g");
-
- pi->srom_fem2g.tssipos = (u8) PHY_GETINTVAR(pi, "tssipos2g");
- pi->srom_fem2g.extpagain = (u8) PHY_GETINTVAR(pi, "extpagain2g");
- pi->srom_fem2g.pdetrange = (u8) PHY_GETINTVAR(pi, "pdetrange2g");
- pi->srom_fem2g.triso = (u8) PHY_GETINTVAR(pi, "triso2g");
- pi->srom_fem2g.antswctrllut = (u8) PHY_GETINTVAR(pi, "antswctl2g");
-
- pi->srom_fem5g.tssipos = (u8) PHY_GETINTVAR(pi, "tssipos5g");
- pi->srom_fem5g.extpagain = (u8) PHY_GETINTVAR(pi, "extpagain5g");
- pi->srom_fem5g.pdetrange = (u8) PHY_GETINTVAR(pi, "pdetrange5g");
- pi->srom_fem5g.triso = (u8) PHY_GETINTVAR(pi, "triso5g");
- if (PHY_GETVAR(pi, "antswctl5g")) {
-
- pi->srom_fem5g.antswctrllut =
- (u8) PHY_GETINTVAR(pi, "antswctl5g");
- } else {
-
- pi->srom_fem5g.antswctrllut =
- (u8) PHY_GETINTVAR(pi, "antswctl2g");
- }
-
- wlc_phy_txpower_ipa_upd(pi);
-
- pi->phy_txcore_disable_temp = (s16) PHY_GETINTVAR(pi, "tempthresh");
- if (pi->phy_txcore_disable_temp == 0) {
- pi->phy_txcore_disable_temp = PHY_CHAIN_TX_DISABLE_TEMP;
- }
-
- pi->phy_tempsense_offset = (s8) PHY_GETINTVAR(pi, "tempoffset");
- if (pi->phy_tempsense_offset != 0) {
- if (pi->phy_tempsense_offset >
- (NPHY_SROM_TEMPSHIFT + NPHY_SROM_MAXTEMPOFFSET)) {
- pi->phy_tempsense_offset = NPHY_SROM_MAXTEMPOFFSET;
- } else if (pi->phy_tempsense_offset < (NPHY_SROM_TEMPSHIFT +
- NPHY_SROM_MINTEMPOFFSET)) {
- pi->phy_tempsense_offset = NPHY_SROM_MINTEMPOFFSET;
- } else {
- pi->phy_tempsense_offset -= NPHY_SROM_TEMPSHIFT;
- }
- }
-
- pi->phy_txcore_enable_temp =
- pi->phy_txcore_disable_temp - PHY_HYSTERESIS_DELTATEMP;
-
- pi->phycal_tempdelta = (u8) PHY_GETINTVAR(pi, "phycal_tempdelta");
- if (pi->phycal_tempdelta > NPHY_CAL_MAXTEMPDELTA) {
- pi->phycal_tempdelta = 0;
- }
-
- wlc_phy_txpwr_srom_read_ppr_nphy(pi);
-
- return true;
-}
-
-void wlc_phy_txpower_recalc_target_nphy(phy_info_t *pi)
+void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi)
{
u8 tx_pwr_ctrl_state;
wlc_phy_txpwr_limit_to_tbl_nphy(pi);
@@ -28168,515 +28399,13 @@ void wlc_phy_txpower_recalc_target_nphy(phy_info_t *pi)
wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0);
}
-static void wlc_phy_txpwrctrl_coeff_setup_nphy(phy_info_t *pi)
-{
- u32 idx;
- u16 iqloCalbuf[7];
- u32 iqcomp, locomp, curr_locomp;
- s8 locomp_i, locomp_q;
- s8 curr_locomp_i, curr_locomp_q;
- u32 tbl_id, tbl_len, tbl_offset;
- u32 regval[128];
-
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, true);
-
- wlc_phy_table_read_nphy(pi, 15, 7, 80, 16, iqloCalbuf);
-
- tbl_len = 128;
- tbl_offset = 320;
- for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL;
- tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) {
- iqcomp =
- (tbl_id ==
- 26) ? (((u32) (iqloCalbuf[0] & 0x3ff)) << 10) |
- (iqloCalbuf[1] & 0x3ff)
- : (((u32) (iqloCalbuf[2] & 0x3ff)) << 10) |
- (iqloCalbuf[3] & 0x3ff);
-
- for (idx = 0; idx < tbl_len; idx++) {
- regval[idx] = iqcomp;
- }
- wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32,
- regval);
- }
-
- tbl_offset = 448;
- for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL;
- tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) {
-
- locomp =
- (u32) ((tbl_id == 26) ? iqloCalbuf[5] : iqloCalbuf[6]);
- locomp_i = (s8) ((locomp >> 8) & 0xff);
- locomp_q = (s8) ((locomp) & 0xff);
- for (idx = 0; idx < tbl_len; idx++) {
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- curr_locomp_i = locomp_i;
- curr_locomp_q = locomp_q;
- } else {
- curr_locomp_i = (s8) ((locomp_i *
- nphy_tpc_loscale[idx] +
- 128) >> 8);
- curr_locomp_q =
- (s8) ((locomp_q * nphy_tpc_loscale[idx] +
- 128) >> 8);
- }
- curr_locomp = (u32) ((curr_locomp_i & 0xff) << 8);
- curr_locomp |= (u32) (curr_locomp_q & 0xff);
- regval[idx] = curr_locomp;
- }
- wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32,
- regval);
- }
-
- if (NREV_LT(pi->pubpi.phy_rev, 2)) {
-
- wlapi_bmac_write_shm(pi->sh->physhim, M_CURR_IDX1, 0xFFFF);
- wlapi_bmac_write_shm(pi->sh->physhim, M_CURR_IDX2, 0xFFFF);
- }
-
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, false);
-}
-
-static void wlc_phy_ipa_internal_tssi_setup_nphy(phy_info_t *pi)
-{
- u8 core;
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- for (core = 0; core < pi->pubpi.phy_corenum; core++) {
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TX_SSI_MASTER, 0x5);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TX_SSI_MUX, 0xe);
-
- if (pi->pubpi.radiorev != 5)
- WRITE_RADIO_REG3(pi, RADIO_2057, TX,
- core, TSSIA, 0);
-
- if (!NREV_IS(pi->pubpi.phy_rev, 7)) {
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX,
- core, TSSIG, 0x1);
- } else {
-
- WRITE_RADIO_REG3(pi, RADIO_2057, TX,
- core, TSSIG, 0x31);
- }
- } else {
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TX_SSI_MASTER, 0x9);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TX_SSI_MUX, 0xc);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core,
- TSSIG, 0);
-
- if (pi->pubpi.radiorev != 5) {
- if (!NREV_IS(pi->pubpi.phy_rev, 7)) {
-
- WRITE_RADIO_REG3(pi, RADIO_2057,
- TX, core,
- TSSIA, 0x1);
- } else {
-
- WRITE_RADIO_REG3(pi, RADIO_2057,
- TX, core,
- TSSIA, 0x31);
- }
- }
- }
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG,
- 0);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC,
- 0);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM,
- 0x3);
- WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1,
- 0x0);
- }
- } else {
- WRITE_RADIO_SYN(pi, RADIO_2056, RESERVED_ADDR31,
- (CHSPEC_IS2G(pi->radio_chanspec)) ? 0x128 :
- 0x80);
- WRITE_RADIO_SYN(pi, RADIO_2056, RESERVED_ADDR30, 0x0);
- WRITE_RADIO_SYN(pi, RADIO_2056, GPIO_MASTER1, 0x29);
-
- for (core = 0; core < pi->pubpi.phy_corenum; core++) {
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, IQCAL_VCM_HG,
- 0x0);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, IQCAL_IDAC,
- 0x0);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_VCM,
- 0x3);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TX_AMP_DET,
- 0x0);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC1,
- 0x8);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC2,
- 0x0);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC3,
- 0x0);
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
- TX_SSI_MASTER, 0x5);
-
- if (pi->pubpi.radiorev != 5)
- WRITE_RADIO_REG2(pi, RADIO_2056, TX,
- core, TSSIA, 0x0);
- if (NREV_GE(pi->pubpi.phy_rev, 5)) {
-
- WRITE_RADIO_REG2(pi, RADIO_2056, TX,
- core, TSSIG, 0x31);
- } else {
- WRITE_RADIO_REG2(pi, RADIO_2056, TX,
- core, TSSIG, 0x11);
- }
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
- TX_SSI_MUX, 0xe);
- } else {
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
- TX_SSI_MASTER, 0x9);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
- TSSIA, 0x31);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
- TSSIG, 0x0);
- WRITE_RADIO_REG2(pi, RADIO_2056, TX, core,
- TX_SSI_MUX, 0xc);
- }
- }
- }
-}
-
-static void wlc_phy_txpwrctrl_idle_tssi_nphy(phy_info_t *pi)
-{
- s32 rssi_buf[4];
- s32 int_val;
-
- if (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) || PHY_MUTED(pi))
-
- return;
-
- if (PHY_IPA(pi)) {
- wlc_phy_ipa_internal_tssi_setup_nphy(pi);
- }
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12),
- 0, 0x3, 0,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 3, 0);
- }
-
- wlc_phy_stopplayback_nphy(pi);
-
- wlc_phy_tx_tone_nphy(pi, 4000, 0, 0, 0, false);
-
- udelay(20);
- int_val =
- wlc_phy_poll_rssi_nphy(pi, (u8) NPHY_RSSI_SEL_TSSI_2G, rssi_buf,
- 1);
- wlc_phy_stopplayback_nphy(pi);
- wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, 0);
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12),
- 0, 0x3, 1,
- NPHY_REV7_RFCTRLOVERRIDE_ID0);
- } else if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 3, 1);
- }
-
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
-
- pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_2g =
- (u8) ((int_val >> 24) & 0xff);
- pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_5g =
- (u8) ((int_val >> 24) & 0xff);
-
- pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_2g =
- (u8) ((int_val >> 8) & 0xff);
- pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_5g =
- (u8) ((int_val >> 8) & 0xff);
- } else {
- pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_2g =
- (u8) ((int_val >> 24) & 0xff);
-
- pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_2g =
- (u8) ((int_val >> 8) & 0xff);
-
- pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_5g =
- (u8) ((int_val >> 16) & 0xff);
- pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_5g =
- (u8) ((int_val) & 0xff);
- }
-
-}
-
-static void wlc_phy_txpwrctrl_pwr_setup_nphy(phy_info_t *pi)
-{
- u32 idx;
- s16 a1[2], b0[2], b1[2];
- s8 target_pwr_qtrdbm[2];
- s32 num, den, pwr_est;
- u8 chan_freq_range;
- u8 idle_tssi[2];
- u32 tbl_id, tbl_len, tbl_offset;
- u32 regval[64];
- u8 core;
-
- if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) {
- wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK);
- (void)R_REG(&pi->regs->maccontrol);
- udelay(1);
- }
-
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, true);
-
- or_phy_reg(pi, 0x122, (0x1 << 0));
-
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- and_phy_reg(pi, 0x1e7, (u16) (~(0x1 << 15)));
- } else {
-
- or_phy_reg(pi, 0x1e7, (0x1 << 15));
- }
-
- if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12))
- wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0);
-
- if (pi->sh->sromrev < 4) {
- idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g;
- idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g;
- target_pwr_qtrdbm[0] = 13 * 4;
- target_pwr_qtrdbm[1] = 13 * 4;
- a1[0] = -424;
- a1[1] = -424;
- b0[0] = 5612;
- b0[1] = 5612;
- b1[1] = -1393;
- b1[0] = -1393;
- } else {
-
- chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0);
- switch (chan_freq_range) {
- case WL_CHAN_FREQ_RANGE_2G:
- idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g;
- idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g;
- target_pwr_qtrdbm[0] =
- pi->nphy_pwrctrl_info[0].max_pwr_2g;
- target_pwr_qtrdbm[1] =
- pi->nphy_pwrctrl_info[1].max_pwr_2g;
- a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_a1;
- a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_a1;
- b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_b0;
- b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_b0;
- b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_b1;
- b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_b1;
- break;
- case WL_CHAN_FREQ_RANGE_5GL:
- idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g;
- idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g;
- target_pwr_qtrdbm[0] =
- pi->nphy_pwrctrl_info[0].max_pwr_5gl;
- target_pwr_qtrdbm[1] =
- pi->nphy_pwrctrl_info[1].max_pwr_5gl;
- a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_a1;
- a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_a1;
- b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_b0;
- b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_b0;
- b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_b1;
- b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_b1;
- break;
- case WL_CHAN_FREQ_RANGE_5GM:
- idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g;
- idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g;
- target_pwr_qtrdbm[0] =
- pi->nphy_pwrctrl_info[0].max_pwr_5gm;
- target_pwr_qtrdbm[1] =
- pi->nphy_pwrctrl_info[1].max_pwr_5gm;
- a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_a1;
- a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_a1;
- b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_b0;
- b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_b0;
- b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_b1;
- b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_b1;
- break;
- case WL_CHAN_FREQ_RANGE_5GH:
- idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g;
- idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g;
- target_pwr_qtrdbm[0] =
- pi->nphy_pwrctrl_info[0].max_pwr_5gh;
- target_pwr_qtrdbm[1] =
- pi->nphy_pwrctrl_info[1].max_pwr_5gh;
- a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_a1;
- a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_a1;
- b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_b0;
- b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_b0;
- b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_b1;
- b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_b1;
- break;
- default:
- idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g;
- idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g;
- target_pwr_qtrdbm[0] = 13 * 4;
- target_pwr_qtrdbm[1] = 13 * 4;
- a1[0] = -424;
- a1[1] = -424;
- b0[0] = 5612;
- b0[1] = 5612;
- b1[1] = -1393;
- b1[0] = -1393;
- break;
- }
- }
-
- target_pwr_qtrdbm[0] = (s8) pi->tx_power_max;
- target_pwr_qtrdbm[1] = (s8) pi->tx_power_max;
-
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- if (pi->srom_fem2g.tssipos) {
- or_phy_reg(pi, 0x1e9, (0x1 << 14));
- }
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- for (core = 0; core <= 1; core++) {
- if (PHY_IPA(pi)) {
-
- if (CHSPEC_IS2G(pi->radio_chanspec)) {
- WRITE_RADIO_REG3(pi, RADIO_2057,
- TX, core,
- TX_SSI_MUX,
- 0xe);
- } else {
- WRITE_RADIO_REG3(pi, RADIO_2057,
- TX, core,
- TX_SSI_MUX,
- 0xc);
- }
- } else {
- }
- }
- } else {
- if (PHY_IPA(pi)) {
-
- write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX |
- RADIO_2056_TX0,
- (CHSPEC_IS5G
- (pi->
- radio_chanspec)) ? 0xc : 0xe);
- write_radio_reg(pi,
- RADIO_2056_TX_TX_SSI_MUX |
- RADIO_2056_TX1,
- (CHSPEC_IS5G
- (pi->
- radio_chanspec)) ? 0xc : 0xe);
- } else {
-
- write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX |
- RADIO_2056_TX0, 0x11);
- write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX |
- RADIO_2056_TX1, 0x11);
- }
- }
- }
-
- if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) {
- wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK);
- (void)R_REG(&pi->regs->maccontrol);
- udelay(1);
- }
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- mod_phy_reg(pi, 0x1e7, (0x7f << 0),
- (NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 << 0));
- } else {
- mod_phy_reg(pi, 0x1e7, (0x7f << 0),
- (NPHY_TxPwrCtrlCmd_pwrIndex_init << 0));
- }
-
- if (NREV_GE(pi->pubpi.phy_rev, 7)) {
- mod_phy_reg(pi, 0x222, (0xff << 0),
- (NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 << 0));
- } else if (NREV_GT(pi->pubpi.phy_rev, 1)) {
- mod_phy_reg(pi, 0x222, (0xff << 0),
- (NPHY_TxPwrCtrlCmd_pwrIndex_init << 0));
- }
-
- if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12))
- wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0);
-
- write_phy_reg(pi, 0x1e8, (0x3 << 8) | (240 << 0));
-
- write_phy_reg(pi, 0x1e9,
- (1 << 15) | (idle_tssi[0] << 0) | (idle_tssi[1] << 8));
-
- write_phy_reg(pi, 0x1ea,
- (target_pwr_qtrdbm[0] << 0) |
- (target_pwr_qtrdbm[1] << 8));
-
- tbl_len = 64;
- tbl_offset = 0;
- for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL;
- tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) {
-
- for (idx = 0; idx < tbl_len; idx++) {
- num =
- 8 * (16 * b0[tbl_id - 26] + b1[tbl_id - 26] * idx);
- den = 32768 + a1[tbl_id - 26] * idx;
- pwr_est = max(((4 * num + den / 2) / den), -8);
- if (NREV_LT(pi->pubpi.phy_rev, 3)) {
- if (idx <=
- (uint) (31 - idle_tssi[tbl_id - 26] + 1))
- pwr_est =
- max(pwr_est,
- target_pwr_qtrdbm[tbl_id - 26] +
- 1);
- }
- regval[idx] = (u32) pwr_est;
- }
- wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32,
- regval);
- }
-
- wlc_phy_txpwr_limit_to_tbl_nphy(pi);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 84, 64, 8,
- pi->adj_pwr_tbl_nphy);
- wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 84, 64, 8,
- pi->adj_pwr_tbl_nphy);
-
- if (pi->phyhang_avoid)
- wlc_phy_stay_in_carriersearch_nphy(pi, false);
-}
-
-static bool wlc_phy_txpwr_ison_nphy(phy_info_t *pi)
+static bool wlc_phy_txpwr_ison_nphy(struct brcms_phy *pi)
{
return read_phy_reg((pi), 0x1e7) & ((0x1 << 15) |
- (0x1 << 14) | (0x1 << 13));
+ (0x1 << 14) | (0x1 << 13));
}
-static u8 wlc_phy_txpwr_idx_cur_get_nphy(phy_info_t *pi, u8 core)
-{
- u16 tmp;
- tmp = read_phy_reg(pi, ((core == PHY_CORE_0) ? 0x1ed : 0x1ee));
-
- tmp = (tmp & (0x7f << 8)) >> 8;
- return (u8) tmp;
-}
-
-static void
-wlc_phy_txpwr_idx_cur_set_nphy(phy_info_t *pi, u8 idx0, u8 idx1)
-{
- mod_phy_reg(pi, 0x1e7, (0x7f << 0), idx0);
-
- if (NREV_GT(pi->pubpi.phy_rev, 1))
- mod_phy_reg(pi, 0x222, (0xff << 0), idx1);
-}
-
-u16 wlc_phy_txpwr_idx_get_nphy(phy_info_t *pi)
+u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi)
{
u16 tmp;
u16 pwr_idx[2];
@@ -28687,34 +28416,30 @@ u16 wlc_phy_txpwr_idx_get_nphy(phy_info_t *pi)
tmp = (pwr_idx[0] << 8) | pwr_idx[1];
} else {
- tmp =
- ((pi->nphy_txpwrindex[PHY_CORE_0].
- index_internal & 0xff) << 8) | (pi->
- nphy_txpwrindex
- [PHY_CORE_1].
- index_internal & 0xff);
+ tmp = ((pi->nphy_txpwrindex[PHY_CORE_0].index_internal & 0xff)
+ << 8) |
+ (pi->nphy_txpwrindex[PHY_CORE_1].index_internal & 0xff);
}
return tmp;
}
-void wlc_phy_txpwr_papd_cal_nphy(phy_info_t *pi)
+void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi)
{
if (PHY_IPA(pi)
&& (pi->nphy_force_papd_cal
|| (wlc_phy_txpwr_ison_nphy(pi)
&&
(((u32)
- ABS(wlc_phy_txpwr_idx_cur_get_nphy(pi, 0) -
+ abs(wlc_phy_txpwr_idx_cur_get_nphy(pi, 0) -
pi->nphy_papd_tx_gain_at_last_cal[0]) >= 4)
|| ((u32)
- ABS(wlc_phy_txpwr_idx_cur_get_nphy(pi, 1) -
- pi->nphy_papd_tx_gain_at_last_cal[1]) >= 4))))) {
+ abs(wlc_phy_txpwr_idx_cur_get_nphy(pi, 1) -
+ pi->nphy_papd_tx_gain_at_last_cal[1]) >= 4)))))
wlc_phy_a4(pi, true);
- }
}
-void wlc_phy_txpwrctrl_enable_nphy(phy_info_t *pi, u8 ctrl_type)
+void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type)
{
u16 mask = 0, val = 0, ishw = 0;
u8 ctr;
@@ -28742,32 +28467,29 @@ void wlc_phy_txpwrctrl_enable_nphy(phy_info_t *pi, u8 ctrl_type)
for (core = 0; core < pi->pubpi.phy_corenum;
core++)
pi->nphy_txpwr_idx[core] =
- wlc_phy_txpwr_idx_cur_get_nphy(pi,
- (u8)
- core);
+ wlc_phy_txpwr_idx_cur_get_nphy(
+ pi,
+ (u8) core);
}
}
tbl_len = 84;
tbl_offset = 64;
- for (ctr = 0; ctr < tbl_len; ctr++) {
+ for (ctr = 0; ctr < tbl_len; ctr++)
regval[ctr] = 0;
- }
wlc_phy_table_write_nphy(pi, 26, tbl_len, tbl_offset, 16,
regval);
wlc_phy_table_write_nphy(pi, 27, tbl_len, tbl_offset, 16,
regval);
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
-
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
and_phy_reg(pi, 0x1e7,
(u16) (~((0x1 << 15) |
- (0x1 << 14) | (0x1 << 13))));
- } else {
+ (0x1 << 14) | (0x1 << 13))));
+ else
and_phy_reg(pi, 0x1e7,
(u16) (~((0x1 << 14) | (0x1 << 13))));
- }
if (NREV_GE(pi->pubpi.phy_rev, 3)) {
or_phy_reg(pi, 0x8f, (0x1 << 8));
@@ -28781,9 +28503,10 @@ void wlc_phy_txpwrctrl_enable_nphy(phy_info_t *pi, u8 ctrl_type)
else if (NREV_LT(pi->pubpi.phy_rev, 2))
mod_phy_reg(pi, 0xdc, 0x00ff, 0x5a);
- if (NREV_LT(pi->pubpi.phy_rev, 2) && IS40MHZ(pi))
+ if (NREV_LT(pi->pubpi.phy_rev, 2) &&
+ pi->bw == WL_CHANSPEC_BW_40)
wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_IQSWAP_WAR,
- MHF1_IQSWAP_WAR, WLC_BAND_ALL);
+ MHF1_IQSWAP_WAR, BRCM_BAND_ALL);
} else {
@@ -28817,7 +28540,7 @@ void wlc_phy_txpwrctrl_enable_nphy(phy_info_t *pi, u8 ctrl_type)
if (NREV_GE(pi->pubpi.phy_rev, 3)) {
if ((pi->nphy_txpwr_idx[0] != 128)
- && (pi->nphy_txpwr_idx[1] != 128)) {
+ && (pi->nphy_txpwr_idx[1] != 128))
wlc_phy_txpwr_idx_cur_set_nphy(pi,
pi->
nphy_txpwr_idx
@@ -28825,7 +28548,6 @@ void wlc_phy_txpwrctrl_enable_nphy(phy_info_t *pi, u8 ctrl_type)
pi->
nphy_txpwr_idx
[1]);
- }
}
if (NREV_GE(pi->pubpi.phy_rev, 3)) {
@@ -28840,9 +28562,10 @@ void wlc_phy_txpwrctrl_enable_nphy(phy_info_t *pi, u8 ctrl_type)
else if (NREV_LT(pi->pubpi.phy_rev, 2))
mod_phy_reg(pi, 0xdc, 0x00ff, 0x40);
- if (NREV_LT(pi->pubpi.phy_rev, 2) && IS40MHZ(pi))
+ if (NREV_LT(pi->pubpi.phy_rev, 2) &&
+ pi->bw == WL_CHANSPEC_BW_40)
wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_IQSWAP_WAR,
- 0x0, WLC_BAND_ALL);
+ 0x0, BRCM_BAND_ALL);
if (PHY_IPA(pi)) {
mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 :
@@ -28860,7 +28583,7 @@ void wlc_phy_txpwrctrl_enable_nphy(phy_info_t *pi, u8 ctrl_type)
}
void
-wlc_phy_txpwr_index_nphy(phy_info_t *pi, u8 core_mask, s8 txpwrindex,
+wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, s8 txpwrindex,
bool restore_cals)
{
u8 core, txpwrctl_tbl;
@@ -28886,17 +28609,14 @@ wlc_phy_txpwr_index_nphy(phy_info_t *pi, u8 core_mask, s8 txpwrindex,
for (core = 0; core < pi->pubpi.phy_corenum; core++) {
- if ((core_mask & (1 << core)) == 0) {
+ if ((core_mask & (1 << core)) == 0)
continue;
- }
txpwrctl_tbl = (core == PHY_CORE_0) ? 26 : 27;
if (txpwrindex < 0) {
- if (pi->nphy_txpwrindex[core].index < 0) {
-
+ if (pi->nphy_txpwrindex[core].index < 0)
continue;
- }
if (NREV_GE(pi->pubpi.phy_rev, 3)) {
mod_phy_reg(pi, 0x8f,
@@ -28929,29 +28649,21 @@ wlc_phy_txpwr_index_nphy(phy_info_t *pi, u8 core_mask, s8 txpwrindex,
wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2);
if (restore_cals) {
-
- wlc_phy_table_write_nphy(pi, 15, 2,
- (80 + 2 * core), 16,
- (void *)&pi->
- nphy_txpwrindex[core].
- iqcomp_a);
-
- wlc_phy_table_write_nphy(pi, 15, 1, (85 + core),
- 16,
- &pi->
- nphy_txpwrindex[core].
- locomp);
- wlc_phy_table_write_nphy(pi, 15, 1, (93 + core),
- 16,
- (void *)&pi->
- nphy_txpwrindex[core].
- locomp);
+ wlc_phy_table_write_nphy(
+ pi, 15, 2, (80 + 2 * core), 16,
+ &pi->nphy_txpwrindex[core].iqcomp_a);
+ wlc_phy_table_write_nphy(
+ pi, 15, 1, (85 + core), 16,
+ &pi->nphy_txpwrindex[core].locomp);
+ wlc_phy_table_write_nphy(
+ pi, 15, 1, (93 + core), 16,
+ &pi->nphy_txpwrindex[core].locomp);
}
wlc_phy_txpwrctrl_enable_nphy(pi, pi->nphy_txpwrctrl);
pi->nphy_txpwrindex[core].index_internal =
- pi->nphy_txpwrindex[core].index_internal_save;
+ pi->nphy_txpwrindex[core].index_internal_save;
} else {
if (pi->nphy_txpwrindex[core].index < 0) {
@@ -28966,14 +28678,13 @@ wlc_phy_txpwr_index_nphy(phy_info_t *pi, u8 core_mask, s8 txpwrindex,
AfectrlOverride);
} else {
pi->nphy_txpwrindex[core].
- AfectrlOverride =
- read_phy_reg(pi, 0xa5);
+ AfectrlOverride =
+ read_phy_reg(pi, 0xa5);
}
pi->nphy_txpwrindex[core].AfeCtrlDacGain =
- read_phy_reg(pi,
- (core ==
- PHY_CORE_0) ? 0xaa : 0xab);
+ read_phy_reg(pi, (core == PHY_CORE_0) ?
+ 0xaa : 0xab);
wlc_phy_table_read_nphy(pi, 7, 1,
(0x110 + core), 16,
@@ -28985,23 +28696,23 @@ wlc_phy_txpwr_index_nphy(phy_info_t *pi, u8 core_mask, s8 txpwrindex,
&tmpval);
tmpval >>= ((core == PHY_CORE_0) ? 8 : 0);
tmpval &= 0xff;
- pi->nphy_txpwrindex[core].bbmult =
- (u8) tmpval;
+ pi->nphy_txpwrindex[core].bbmult = (u8) tmpval;
wlc_phy_table_read_nphy(pi, 15, 2,
(80 + 2 * core), 16,
- (void *)&pi->
+ &pi->
nphy_txpwrindex[core].
iqcomp_a);
wlc_phy_table_read_nphy(pi, 15, 1, (85 + core),
16,
- (void *)&pi->
+ &pi->
nphy_txpwrindex[core].
locomp);
pi->nphy_txpwrindex[core].index_internal_save =
- pi->nphy_txpwrindex[core].index_internal;
+ pi->nphy_txpwrindex[core].
+ index_internal;
}
tx_pwr_ctrl_state = pi->nphy_txpwrctrl;
@@ -29014,22 +28725,22 @@ wlc_phy_txpwr_index_nphy(phy_info_t *pi, u8 core_mask, s8 txpwrindex,
(tx_ind0 + txpwrindex), 32,
&txgain);
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
- rad_gain =
- (txgain >> 16) & ((1 << (32 - 16 + 1)) - 1);
- } else {
- rad_gain =
- (txgain >> 16) & ((1 << (28 - 16 + 1)) - 1);
- }
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
+ rad_gain = (txgain >> 16) &
+ ((1 << (32 - 16 + 1)) - 1);
+ else
+ rad_gain = (txgain >> 16) &
+ ((1 << (28 - 16 + 1)) - 1);
+
dac_gain = (txgain >> 8) & ((1 << (13 - 8 + 1)) - 1);
bbmult = (txgain >> 0) & ((1 << (7 - 0 + 1)) - 1);
- if (NREV_GE(pi->pubpi.phy_rev, 3)) {
+ if (NREV_GE(pi->pubpi.phy_rev, 3))
mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f :
0xa5), (0x1 << 8), (0x1 << 8));
- } else {
+ else
mod_phy_reg(pi, 0xa5, (0x1 << 14), (0x1 << 14));
- }
+
write_phy_reg(pi, (core == PHY_CORE_0) ?
0xaa : 0xab, dac_gain);
@@ -29038,9 +28749,8 @@ wlc_phy_txpwr_index_nphy(phy_info_t *pi, u8 core_mask, s8 txpwrindex,
wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m1m2);
m1m2 &= ((core == PHY_CORE_0) ? 0x00ff : 0xff00);
- m1m2 |=
- ((core ==
- PHY_CORE_0) ? (bbmult << 8) : (bbmult << 0));
+ m1m2 |= ((core == PHY_CORE_0) ?
+ (bbmult << 8) : (bbmult << 0));
wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2);
@@ -29061,23 +28771,20 @@ wlc_phy_txpwr_index_nphy(phy_info_t *pi, u8 core_mask, s8 txpwrindex,
wlc_phy_table_read_nphy(pi, txpwrctl_tbl, 1,
(lo_ind0 + txpwrindex), 32,
&locomp);
- if (restore_cals) {
+ if (restore_cals)
wlc_phy_table_write_nphy(pi, 15, 1, (85 + core),
16, &locomp);
- }
if (NREV_IS(pi->pubpi.phy_rev, 1))
wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF);
if (PHY_IPA(pi)) {
wlc_phy_table_read_nphy(pi,
- (core ==
- PHY_CORE_0 ?
- NPHY_TBL_ID_CORE1TXPWRCTL
- :
- NPHY_TBL_ID_CORE2TXPWRCTL),
- 1, 576 + txpwrindex, 32,
- &rfpwr_offset);
+ (core == PHY_CORE_0 ?
+ NPHY_TBL_ID_CORE1TXPWRCTL :
+ NPHY_TBL_ID_CORE2TXPWRCTL),
+ 1, 576 + txpwrindex, 32,
+ &rfpwr_offset);
mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 :
0x29b, (0x1ff << 4),
@@ -29099,7 +28806,7 @@ wlc_phy_txpwr_index_nphy(phy_info_t *pi, u8 core_mask, s8 txpwrindex,
}
void
-wlc_phy_txpower_sromlimit_get_nphy(phy_info_t *pi, uint chan, u8 *max_pwr,
+wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, u8 *max_pwr,
u8 txp_rate_idx)
{
u8 chan_freq_range;
@@ -29126,14 +28833,14 @@ wlc_phy_txpower_sromlimit_get_nphy(phy_info_t *pi, uint chan, u8 *max_pwr,
return;
}
-void wlc_phy_stay_in_carriersearch_nphy(phy_info_t *pi, bool enable)
+void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, bool enable)
{
u16 clip_off[] = { 0xffff, 0xffff };
if (enable) {
if (pi->nphy_deaf_count == 0) {
pi->classifier_state =
- wlc_phy_classifier_nphy(pi, 0, 0);
+ wlc_phy_classifier_nphy(pi, 0, 0);
wlc_phy_classifier_nphy(pi, (0x7 << 0), 4);
wlc_phy_clip_det_nphy(pi, 0, pi->clip_state);
wlc_phy_clip_det_nphy(pi, 1, clip_off);
@@ -29154,16 +28861,16 @@ void wlc_phy_stay_in_carriersearch_nphy(phy_info_t *pi, bool enable)
}
}
-void wlc_nphy_deaf_mode(phy_info_t *pi, bool mode)
+void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode)
{
wlapi_suspend_mac_and_wait(pi->sh->physhim);
if (mode) {
if (pi->nphy_deaf_count == 0)
wlc_phy_stay_in_carriersearch_nphy(pi, true);
- } else {
- if (pi->nphy_deaf_count > 0)
- wlc_phy_stay_in_carriersearch_nphy(pi, false);
+ } else if (pi->nphy_deaf_count > 0) {
+ wlc_phy_stay_in_carriersearch_nphy(pi, false);
}
+
wlapi_enable_mac(pi->sh->physhim);
}
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_qmath.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c
index c98176fd0aae..faf1ebe76068 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_qmath.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c
@@ -14,94 +14,98 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/types.h>
-
-#include "wlc_phy_qmath.h"
+#include "phy_qmath.h"
/*
-Description: This function make 16 bit unsigned multiplication. To fit the output into
-16 bits the 32 bit multiplication result is right shifted by 16 bits.
-*/
+ * Description: This function make 16 bit unsigned multiplication.
+ * To fit the output into 16 bits the 32 bit multiplication result is right
+ * shifted by 16 bits.
+ */
u16 qm_mulu16(u16 op1, u16 op2)
{
return (u16) (((u32) op1 * (u32) op2) >> 16);
}
/*
-Description: This function make 16 bit multiplication and return the result in 16 bits.
-To fit the multiplication result into 16 bits the multiplication result is right shifted by
-15 bits. Right shifting 15 bits instead of 16 bits is done to remove the extra sign bit formed
-due to the multiplication.
-When both the 16bit inputs are 0x8000 then the output is saturated to 0x7fffffff.
-*/
+ * Description: This function make 16 bit multiplication and return the result
+ * in 16 bits. To fit the multiplication result into 16 bits the multiplication
+ * result is right shifted by 15 bits. Right shifting 15 bits instead of 16 bits
+ * is done to remove the extra sign bit formed due to the multiplication.
+ * When both the 16bit inputs are 0x8000 then the output is saturated to
+ * 0x7fffffff.
+ */
s16 qm_muls16(s16 op1, s16 op2)
{
s32 result;
- if (op1 == (s16) 0x8000 && op2 == (s16) 0x8000) {
+ if (op1 == (s16) 0x8000 && op2 == (s16) 0x8000)
result = 0x7fffffff;
- } else {
+ else
result = ((s32) (op1) * (s32) (op2));
- }
+
return (s16) (result >> 15);
}
/*
-Description: This function add two 32 bit numbers and return the 32bit result.
-If the result overflow 32 bits, the output will be saturated to 32bits.
-*/
+ * Description: This function add two 32 bit numbers and return the 32bit
+ * result. If the result overflow 32 bits, the output will be saturated to
+ * 32bits.
+ */
s32 qm_add32(s32 op1, s32 op2)
{
s32 result;
result = op1 + op2;
- if (op1 < 0 && op2 < 0 && result > 0) {
+ if (op1 < 0 && op2 < 0 && result > 0)
result = 0x80000000;
- } else if (op1 > 0 && op2 > 0 && result < 0) {
+ else if (op1 > 0 && op2 > 0 && result < 0)
result = 0x7fffffff;
- }
+
return result;
}
/*
-Description: This function add two 16 bit numbers and return the 16bit result.
-If the result overflow 16 bits, the output will be saturated to 16bits.
-*/
+ * Description: This function add two 16 bit numbers and return the 16bit
+ * result. If the result overflow 16 bits, the output will be saturated to
+ * 16bits.
+ */
s16 qm_add16(s16 op1, s16 op2)
{
s16 result;
s32 temp = (s32) op1 + (s32) op2;
- if (temp > (s32) 0x7fff) {
+ if (temp > (s32) 0x7fff)
result = (s16) 0x7fff;
- } else if (temp < (s32) 0xffff8000) {
+ else if (temp < (s32) 0xffff8000)
result = (s16) 0xffff8000;
- } else {
+ else
result = (s16) temp;
- }
+
return result;
}
/*
-Description: This function make 16 bit subtraction and return the 16bit result.
-If the result overflow 16 bits, the output will be saturated to 16bits.
-*/
+ * Description: This function make 16 bit subtraction and return the 16bit
+ * result. If the result overflow 16 bits, the output will be saturated to
+ * 16bits.
+ */
s16 qm_sub16(s16 op1, s16 op2)
{
s16 result;
s32 temp = (s32) op1 - (s32) op2;
- if (temp > (s32) 0x7fff) {
+ if (temp > (s32) 0x7fff)
result = (s16) 0x7fff;
- } else if (temp < (s32) 0xffff8000) {
+ else if (temp < (s32) 0xffff8000)
result = (s16) 0xffff8000;
- } else {
+ else
result = (s16) temp;
- }
+
return result;
}
/*
-Description: This function make a 32 bit saturated left shift when the specified shift
-is +ve. This function will make a 32 bit right shift when the specified shift is -ve.
-This function return the result after shifting operation.
-*/
+ * Description: This function make a 32 bit saturated left shift when the
+ * specified shift is +ve. This function will make a 32 bit right shift when
+ * the specified shift is -ve. This function return the result after shifting
+ * operation.
+ */
s32 qm_shl32(s32 op, int shift)
{
int i;
@@ -112,20 +116,21 @@ s32 qm_shl32(s32 op, int shift)
else if (shift < -31)
shift = -31;
if (shift >= 0) {
- for (i = 0; i < shift; i++) {
+ for (i = 0; i < shift; i++)
result = qm_add32(result, result);
- }
} else {
result = result >> (-shift);
}
+
return result;
}
/*
-Description: This function make a 16 bit saturated left shift when the specified shift
-is +ve. This function will make a 16 bit right shift when the specified shift is -ve.
-This function return the result after shifting operation.
-*/
+ * Description: This function make a 16 bit saturated left shift when the
+ * specified shift is +ve. This function will make a 16 bit right shift when
+ * the specified shift is -ve. This function return the result after shifting
+ * operation.
+ */
s16 qm_shl16(s16 op, int shift)
{
int i;
@@ -136,29 +141,29 @@ s16 qm_shl16(s16 op, int shift)
else if (shift < -15)
shift = -15;
if (shift > 0) {
- for (i = 0; i < shift; i++) {
+ for (i = 0; i < shift; i++)
result = qm_add16(result, result);
- }
} else {
result = result >> (-shift);
}
+
return result;
}
/*
-Description: This function make a 16 bit right shift when shift is +ve.
-This function make a 16 bit saturated left shift when shift is -ve. This function
-return the result of the shift operation.
-*/
+ * Description: This function make a 16 bit right shift when shift is +ve.
+ * This function make a 16 bit saturated left shift when shift is -ve. This
+ * function return the result of the shift operation.
+ */
s16 qm_shr16(s16 op, int shift)
{
return qm_shl16(op, -shift);
}
/*
-Description: This function return the number of redundant sign bits in a 32 bit number.
-Example: qm_norm32(0x00000080) = 23
-*/
+ * Description: This function return the number of redundant sign bits in a
+ * 32 bit number. Example: qm_norm32(0x00000080) = 23
+ */
s16 qm_norm32(s32 op)
{
u16 u16extraSignBits;
@@ -210,28 +215,30 @@ static const s16 log_table[] = {
32024
};
-#define LOG_TABLE_SIZE 32 /* log_table size */
-#define LOG2_LOG_TABLE_SIZE 5 /* log2(log_table size) */
-#define Q_LOG_TABLE 15 /* qformat of log_table */
-#define LOG10_2 19728 /* log10(2) in q.16 */
+#define LOG_TABLE_SIZE 32 /* log_table size */
+#define LOG2_LOG_TABLE_SIZE 5 /* log2(log_table size) */
+#define Q_LOG_TABLE 15 /* qformat of log_table */
+#define LOG10_2 19728 /* log10(2) in q.16 */
/*
-Description:
-This routine takes the input number N and its q format qN and compute
-the log10(N). This routine first normalizes the input no N. Then N is in mag*(2^x) format.
-mag is any number in the range 2^30-(2^31 - 1). Then log2(mag * 2^x) = log2(mag) + x is computed.
-From that log10(mag * 2^x) = log2(mag * 2^x) * log10(2) is computed.
-This routine looks the log2 value in the table considering LOG2_LOG_TABLE_SIZE+1 MSBs.
-As the MSB is always 1, only next LOG2_OF_LOG_TABLE_SIZE MSBs are used for table lookup.
-Next 16 MSBs are used for interpolation.
-Inputs:
-N - number to which log10 has to be found.
-qN - q format of N
-log10N - address where log10(N) will be written.
-qLog10N - address where log10N qformat will be written.
-Note/Problem:
-For accurate results input should be in normalized or near normalized form.
-*/
+ * Description:
+ * This routine takes the input number N and its q format qN and compute
+ * the log10(N). This routine first normalizes the input no N. Then N is in
+ * mag*(2^x) format. mag is any number in the range 2^30-(2^31 - 1).
+ * Then log2(mag * 2^x) = log2(mag) + x is computed. From that
+ * log10(mag * 2^x) = log2(mag * 2^x) * log10(2) is computed.
+ * This routine looks the log2 value in the table considering
+ * LOG2_LOG_TABLE_SIZE+1 MSBs. As the MSB is always 1, only next
+ * LOG2_OF_LOG_TABLE_SIZE MSBs are used for table lookup. Next 16 MSBs are used
+ * for interpolation.
+ * Inputs:
+ * N - number to which log10 has to be found.
+ * qN - q format of N
+ * log10N - address where log10(N) will be written.
+ * qLog10N - address where log10N qformat will be written.
+ * Note/Problem:
+ * For accurate results input should be in normalized or near normalized form.
+ */
void qm_log10(s32 N, s16 qN, s16 *log10N, s16 *qLog10N)
{
s16 s16norm, s16tableIndex, s16errorApproximation;
@@ -250,12 +257,13 @@ void qm_log10(s32 N, s16 qN, s16 *log10N, s16 *qLog10N)
*/
qN = qN + s16norm - 30;
- /* take the table index as the LOG2_OF_LOG_TABLE_SIZE bits right of the MSB */
+ /* take the table index as the LOG2_OF_LOG_TABLE_SIZE bits right of the
+ * MSB */
s16tableIndex = (s16) (N >> (32 - (2 + LOG2_LOG_TABLE_SIZE)));
/* remove the MSB. the MSB is always 1 after normalization. */
s16tableIndex =
- s16tableIndex & (s16) ((1 << LOG2_LOG_TABLE_SIZE) - 1);
+ s16tableIndex & (s16) ((1 << LOG2_LOG_TABLE_SIZE) - 1);
/* remove the (1+LOG2_OF_LOG_TABLE_SIZE) MSBs in the N. */
N = N & ((1 << (32 - (2 + LOG2_LOG_TABLE_SIZE))) - 1);
@@ -265,23 +273,27 @@ void qm_log10(s32 N, s16 qN, s16 *log10N, s16 *qLog10N)
u16offset = (u16) (N >> (32 - (2 + LOG2_LOG_TABLE_SIZE + 16)));
/* look the log value in the table. */
- s32log = log_table[s16tableIndex]; /* q.15 format */
+ s32log = log_table[s16tableIndex]; /* q.15 format */
- /* interpolate using the offset. */
- s16errorApproximation = (s16) qm_mulu16(u16offset, (u16) (log_table[s16tableIndex + 1] - log_table[s16tableIndex])); /* q.15 */
+ /* interpolate using the offset. q.15 format. */
+ s16errorApproximation = (s16) qm_mulu16(u16offset,
+ (u16) (log_table[s16tableIndex + 1] -
+ log_table[s16tableIndex]));
- s32log = qm_add16((s16) s32log, s16errorApproximation); /* q.15 format */
+ /* q.15 format */
+ s32log = qm_add16((s16) s32log, s16errorApproximation);
/* adjust for the qformat of the N as
* log2(mag * 2^x) = log2(mag) + x
*/
- s32log = qm_add32(s32log, ((s32) -qN) << 15); /* q.15 format */
+ s32log = qm_add32(s32log, ((s32) -qN) << 15); /* q.15 format */
/* normalize the result. */
s16norm = qm_norm32(s32log);
/* bring all the important bits into lower 16 bits */
- s32log = qm_shl32(s32log, s16norm - 16); /* q.15+s16norm-16 format */
+ /* q.15+s16norm-16 format */
+ s32log = qm_shl32(s32log, s16norm - 16);
/* compute the log10(N) by multiplying log2(N) with log10(2).
* as log10(mag * 2^x) = log2(mag * 2^x) * log10(2)
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_qmath.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h
index 3dcee1c4aa65..20e3783f921b 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_qmath.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h
@@ -14,8 +14,10 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef __QMATH_H__
-#define __QMATH_H__
+#ifndef _BRCM_QMATH_H_
+#define _BRCM_QMATH_H_
+
+#include <types.h>
u16 qm_mulu16(u16 op1, u16 op2);
@@ -37,4 +39,4 @@ s16 qm_norm32(s32 op);
void qm_log10(s32 N, s16 qN, s16 *log10N, s16 *qLog10N);
-#endif /* #ifndef __QMATH_H__ */
+#endif /* #ifndef _BRCM_QMATH_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_radio.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h
index 72176ae2882c..c3a675455ff5 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phy_radio.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h
@@ -14,8 +14,8 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _BCM20XX_H
-#define _BCM20XX_H
+#ifndef _BRCM_PHY_RADIO_H_
+#define _BRCM_PHY_RADIO_H_
#define RADIO_IDCODE 0x01
@@ -1530,4 +1530,4 @@
#define RADIO_2057_VCM_MASK 0x7
-#endif /* _BCM20XX_H */
+#endif /* _BRCM_PHY_RADIO_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phyreg_n.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h
index 211bc3a842af..a97c3a799479 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phyreg_n.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h
@@ -123,13 +123,13 @@
#define NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS 0xf
#define NPHY_REV3_RFSEQ_CMD_END 0x1f
-#define NPHY_RSSI_SEL_W1 0x0
-#define NPHY_RSSI_SEL_W2 0x1
-#define NPHY_RSSI_SEL_NB 0x2
-#define NPHY_RSSI_SEL_IQ 0x3
-#define NPHY_RSSI_SEL_TSSI_2G 0x4
-#define NPHY_RSSI_SEL_TSSI_5G 0x5
-#define NPHY_RSSI_SEL_TBD 0x6
+#define NPHY_RSSI_SEL_W1 0x0
+#define NPHY_RSSI_SEL_W2 0x1
+#define NPHY_RSSI_SEL_NB 0x2
+#define NPHY_RSSI_SEL_IQ 0x3
+#define NPHY_RSSI_SEL_TSSI_2G 0x4
+#define NPHY_RSSI_SEL_TSSI_5G 0x5
+#define NPHY_RSSI_SEL_TBD 0x6
#define NPHY_RAIL_I 0x0
#define NPHY_RAIL_Q 0x1
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
index 81c59b05482a..622c01ca72c5 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_lcn.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
@@ -14,12 +14,10 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/types.h>
-#include <sbhnddma.h>
-#include <wlc_phy_int.h>
-#include <wlc_phytbl_lcn.h>
+#include <types.h>
+#include "phytbl_lcn.h"
-const u32 dot11lcn_gain_tbl_rev0[] = {
+static const u32 dot11lcn_gain_tbl_rev0[] = {
0x00000000,
0x00000000,
0x00000000,
@@ -118,7 +116,7 @@ const u32 dot11lcn_gain_tbl_rev0[] = {
0x00000000,
};
-const u32 dot11lcn_gain_tbl_rev1[] = {
+static const u32 dot11lcn_gain_tbl_rev1[] = {
0x00000000,
0x00000000,
0x00000000,
@@ -217,7 +215,7 @@ const u32 dot11lcn_gain_tbl_rev1[] = {
0x00000000,
};
-const u16 dot11lcn_aux_gain_idx_tbl_rev0[] = {
+static const u16 dot11lcn_aux_gain_idx_tbl_rev0[] = {
0x0401,
0x0402,
0x0403,
@@ -258,7 +256,7 @@ const u16 dot11lcn_aux_gain_idx_tbl_rev0[] = {
0x0000,
};
-const u32 dot11lcn_gain_idx_tbl_rev0[] = {
+static const u32 dot11lcn_gain_idx_tbl_rev0[] = {
0x00000000,
0x00000000,
0x10000000,
@@ -409,7 +407,7 @@ const u32 dot11lcn_gain_idx_tbl_rev0[] = {
0x0000001c,
};
-const u16 dot11lcn_aux_gain_idx_tbl_2G[] = {
+static const u16 dot11lcn_aux_gain_idx_tbl_2G[] = {
0x0000,
0x0000,
0x0000,
@@ -450,7 +448,7 @@ const u16 dot11lcn_aux_gain_idx_tbl_2G[] = {
0x0000
};
-const u8 dot11lcn_gain_val_tbl_2G[] = {
+static const u8 dot11lcn_gain_val_tbl_2G[] = {
0xfc,
0x02,
0x08,
@@ -521,7 +519,7 @@ const u8 dot11lcn_gain_val_tbl_2G[] = {
0x00
};
-const u32 dot11lcn_gain_idx_tbl_2G[] = {
+static const u32 dot11lcn_gain_idx_tbl_2G[] = {
0x00000000,
0x00000000,
0x00000000,
@@ -676,7 +674,7 @@ const u32 dot11lcn_gain_idx_tbl_2G[] = {
0x00000000
};
-const u32 dot11lcn_gain_tbl_2G[] = {
+static const u32 dot11lcn_gain_tbl_2G[] = {
0x00000000,
0x00000004,
0x00000008,
@@ -775,7 +773,7 @@ const u32 dot11lcn_gain_tbl_2G[] = {
0x00000000
};
-const u32 dot11lcn_gain_tbl_extlna_2G[] = {
+static const u32 dot11lcn_gain_tbl_extlna_2G[] = {
0x00000000,
0x00000004,
0x00000008,
@@ -874,7 +872,7 @@ const u32 dot11lcn_gain_tbl_extlna_2G[] = {
0x00000000
};
-const u16 dot11lcn_aux_gain_idx_tbl_extlna_2G[] = {
+static const u16 dot11lcn_aux_gain_idx_tbl_extlna_2G[] = {
0x0400,
0x0400,
0x0400,
@@ -915,7 +913,7 @@ const u16 dot11lcn_aux_gain_idx_tbl_extlna_2G[] = {
0x0000
};
-const u8 dot11lcn_gain_val_tbl_extlna_2G[] = {
+static const u8 dot11lcn_gain_val_tbl_extlna_2G[] = {
0xfc,
0x02,
0x08,
@@ -986,7 +984,7 @@ const u8 dot11lcn_gain_val_tbl_extlna_2G[] = {
0x00
};
-const u32 dot11lcn_gain_idx_tbl_extlna_2G[] = {
+static const u32 dot11lcn_gain_idx_tbl_extlna_2G[] = {
0x00000000,
0x00000040,
0x00000000,
@@ -1141,7 +1139,7 @@ const u32 dot11lcn_gain_idx_tbl_extlna_2G[] = {
0x00000000
};
-const u32 dot11lcn_aux_gain_idx_tbl_5G[] = {
+static const u32 dot11lcn_aux_gain_idx_tbl_5G[] = {
0x0000,
0x0000,
0x0000,
@@ -1182,7 +1180,7 @@ const u32 dot11lcn_aux_gain_idx_tbl_5G[] = {
0x0000
};
-const u32 dot11lcn_gain_val_tbl_5G[] = {
+static const u32 dot11lcn_gain_val_tbl_5G[] = {
0xf7,
0xfd,
0x00,
@@ -1253,7 +1251,7 @@ const u32 dot11lcn_gain_val_tbl_5G[] = {
0x00
};
-const u32 dot11lcn_gain_idx_tbl_5G[] = {
+static const u32 dot11lcn_gain_idx_tbl_5G[] = {
0x00000000,
0x00000000,
0x00000000,
@@ -1408,7 +1406,7 @@ const u32 dot11lcn_gain_idx_tbl_5G[] = {
0x00000000
};
-const u32 dot11lcn_gain_tbl_5G[] = {
+static const u32 dot11lcn_gain_tbl_5G[] = {
0x00000000,
0x00000040,
0x00000080,
@@ -1507,7 +1505,7 @@ const u32 dot11lcn_gain_tbl_5G[] = {
0x00000000
};
-const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_rev0[] = {
+const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[] = {
{&dot11lcn_gain_tbl_rev0,
sizeof(dot11lcn_gain_tbl_rev0) / sizeof(dot11lcn_gain_tbl_rev0[0]), 18,
0, 32}
@@ -1522,7 +1520,7 @@ const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_rev0[] = {
,
};
-const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_rev1[] = {
+static const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev1[] = {
{&dot11lcn_gain_tbl_rev1,
sizeof(dot11lcn_gain_tbl_rev1) / sizeof(dot11lcn_gain_tbl_rev1[0]), 18,
0, 32}
@@ -1537,7 +1535,7 @@ const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_rev1[] = {
,
};
-const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_2G_rev2[] = {
+const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[] = {
{&dot11lcn_gain_tbl_2G,
sizeof(dot11lcn_gain_tbl_2G) / sizeof(dot11lcn_gain_tbl_2G[0]), 18, 0,
32}
@@ -1555,7 +1553,7 @@ const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_2G_rev2[] = {
17, 0, 8}
};
-const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_5G_rev2[] = {
+const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[] = {
{&dot11lcn_gain_tbl_5G,
sizeof(dot11lcn_gain_tbl_5G) / sizeof(dot11lcn_gain_tbl_5G[0]), 18, 0,
32}
@@ -1573,7 +1571,7 @@ const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_5G_rev2[] = {
17, 0, 8}
};
-const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[] = {
+const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[] = {
{&dot11lcn_gain_tbl_extlna_2G,
sizeof(dot11lcn_gain_tbl_extlna_2G) /
sizeof(dot11lcn_gain_tbl_extlna_2G[0]), 18, 0, 32}
@@ -1591,7 +1589,7 @@ const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[] = {
sizeof(dot11lcn_gain_val_tbl_extlna_2G[0]), 17, 0, 8}
};
-const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[] = {
+const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[] = {
{&dot11lcn_gain_tbl_5G,
sizeof(dot11lcn_gain_tbl_5G) / sizeof(dot11lcn_gain_tbl_5G[0]), 18, 0,
32}
@@ -1610,22 +1608,18 @@ const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[] = {
};
const u32 dot11lcnphytbl_rx_gain_info_sz_rev0 =
- sizeof(dot11lcnphytbl_rx_gain_info_rev0) /
- sizeof(dot11lcnphytbl_rx_gain_info_rev0[0]);
-
-const u32 dot11lcnphytbl_rx_gain_info_sz_rev1 =
- sizeof(dot11lcnphytbl_rx_gain_info_rev1) /
- sizeof(dot11lcnphytbl_rx_gain_info_rev1[0]);
+ sizeof(dot11lcnphytbl_rx_gain_info_rev0) /
+ sizeof(dot11lcnphytbl_rx_gain_info_rev0[0]);
const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz =
- sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2) /
- sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2[0]);
+ sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2) /
+ sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2[0]);
const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz =
- sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2) /
- sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2[0]);
+ sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2) /
+ sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2[0]);
-const u16 dot11lcn_min_sig_sq_tbl_rev0[] = {
+static const u16 dot11lcn_min_sig_sq_tbl_rev0[] = {
0x014d,
0x014d,
0x014d,
@@ -1692,7 +1686,7 @@ const u16 dot11lcn_min_sig_sq_tbl_rev0[] = {
0x014d,
};
-const u16 dot11lcn_noise_scale_tbl_rev0[] = {
+static const u16 dot11lcn_noise_scale_tbl_rev0[] = {
0x0000,
0x0000,
0x0000,
@@ -1759,7 +1753,7 @@ const u16 dot11lcn_noise_scale_tbl_rev0[] = {
0x0000,
};
-const u32 dot11lcn_fltr_ctrl_tbl_rev0[] = {
+static const u32 dot11lcn_fltr_ctrl_tbl_rev0[] = {
0x000141f8,
0x000021f8,
0x000021fb,
@@ -1772,7 +1766,7 @@ const u32 dot11lcn_fltr_ctrl_tbl_rev0[] = {
0x0000024b,
};
-const u32 dot11lcn_ps_ctrl_tbl_rev0[] = {
+static const u32 dot11lcn_ps_ctrl_tbl_rev0[] = {
0x00100001,
0x00200010,
0x00300001,
@@ -1795,7 +1789,7 @@ const u32 dot11lcn_ps_ctrl_tbl_rev0[] = {
0x00600f22,
};
-const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[] = {
+static const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[] = {
0x0007,
0x0005,
0x0006,
@@ -1863,7 +1857,7 @@ const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[] = {
};
-const u16 dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[] = {
+static const u16 dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[] = {
0x0007,
0x0005,
0x0002,
@@ -1930,7 +1924,7 @@ const u16 dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[] = {
0x0002,
};
-const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0[] = {
+static const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0[] = {
0x0002,
0x0008,
0x0004,
@@ -1997,7 +1991,7 @@ const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0[] = {
0x0001,
};
-const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = {
+static const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = {
0x000a,
0x0009,
0x0006,
@@ -2064,7 +2058,7 @@ const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = {
0x0005,
};
-const u16 dot11lcn_sw_ctrl_tbl_rev0[] = {
+static const u16 dot11lcn_sw_ctrl_tbl_rev0[] = {
0x0004,
0x0004,
0x0002,
@@ -2131,7 +2125,7 @@ const u16 dot11lcn_sw_ctrl_tbl_rev0[] = {
0x0002,
};
-const u8 dot11lcn_nf_table_rev0[] = {
+static const u8 dot11lcn_nf_table_rev0[] = {
0x5f,
0x36,
0x29,
@@ -2150,7 +2144,7 @@ const u8 dot11lcn_nf_table_rev0[] = {
0x1f,
};
-const u8 dot11lcn_gain_val_tbl_rev0[] = {
+static const u8 dot11lcn_gain_val_tbl_rev0[] = {
0x09,
0x0f,
0x14,
@@ -2213,7 +2207,7 @@ const u8 dot11lcn_gain_val_tbl_rev0[] = {
0x00,
};
-const u8 dot11lcn_spur_tbl_rev0[] = {
+static const u8 dot11lcn_spur_tbl_rev0[] = {
0x01,
0x01,
0x01,
@@ -2344,7 +2338,7 @@ const u8 dot11lcn_spur_tbl_rev0[] = {
0x01,
};
-const u16 dot11lcn_unsup_mcs_tbl_rev0[] = {
+static const u16 dot11lcn_unsup_mcs_tbl_rev0[] = {
0x001a,
0x0034,
0x004e,
@@ -2501,7 +2495,7 @@ const u16 dot11lcn_unsup_mcs_tbl_rev0[] = {
0x06f6,
};
-const u16 dot11lcn_iq_local_tbl_rev0[] = {
+static const u16 dot11lcn_iq_local_tbl_rev0[] = {
0x0200,
0x0300,
0x0400,
@@ -2612,7 +2606,7 @@ const u16 dot11lcn_iq_local_tbl_rev0[] = {
0x0000,
};
-const u32 dot11lcn_papd_compdelta_tbl_rev0[] = {
+static const u32 dot11lcn_papd_compdelta_tbl_rev0[] = {
0x00080000,
0x00080000,
0x00080000,
@@ -2775,7 +2769,7 @@ const u32 dot11lcn_papd_compdelta_tbl_rev0[] = {
0x00080000,
};
-const dot11lcnphytbl_info_t dot11lcnphytbl_info_rev0[] = {
+const struct phytbl_info dot11lcnphytbl_info_rev0[] = {
{&dot11lcn_min_sig_sq_tbl_rev0,
sizeof(dot11lcn_min_sig_sq_tbl_rev0) /
sizeof(dot11lcn_min_sig_sq_tbl_rev0[0]), 2, 0, 16}
@@ -2834,806 +2828,423 @@ const dot11lcnphytbl_info_t dot11lcnphytbl_info_rev0[] = {
,
};
-const dot11lcnphytbl_info_t dot11lcn_sw_ctrl_tbl_info_4313 = {
+const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313 = {
&dot11lcn_sw_ctrl_tbl_4313_rev0,
- sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0) /
- sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0[0]), 15, 0, 16
+ sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0) /
+ sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0[0]), 15, 0, 16
};
-const dot11lcnphytbl_info_t dot11lcn_sw_ctrl_tbl_info_4313_epa = {
+const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa = {
&dot11lcn_sw_ctrl_tbl_4313_epa_rev0,
- sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0) /
- sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0[0]), 15, 0, 16
+ sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0) /
+ sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0[0]), 15, 0, 16
};
-const dot11lcnphytbl_info_t dot11lcn_sw_ctrl_tbl_info_4313_bt_epa = {
+const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa = {
&dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo,
- sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo) /
- sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[0]), 15, 0, 16
+ sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo) /
+ sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[0]), 15, 0, 16
};
-const dot11lcnphytbl_info_t dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250 = {
+const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250 = {
&dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0,
- sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0) /
- sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[0]), 15, 0, 16
+ sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0) /
+ sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[0]), 15, 0, 16
};
const u32 dot11lcnphytbl_info_sz_rev0 =
- sizeof(dot11lcnphytbl_info_rev0) / sizeof(dot11lcnphytbl_info_rev0[0]);
+ sizeof(dot11lcnphytbl_info_rev0) / sizeof(dot11lcnphytbl_info_rev0[0]);
-const lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = {
- {3, 0, 31, 0, 72,}
- ,
- {3, 0, 31, 0, 70,}
- ,
- {3, 0, 31, 0, 68,}
- ,
- {3, 0, 30, 0, 67,}
- ,
- {3, 0, 29, 0, 68,}
- ,
- {3, 0, 28, 0, 68,}
- ,
- {3, 0, 27, 0, 69,}
- ,
- {3, 0, 26, 0, 70,}
- ,
- {3, 0, 25, 0, 70,}
- ,
- {3, 0, 24, 0, 71,}
- ,
- {3, 0, 23, 0, 72,}
- ,
- {3, 0, 23, 0, 70,}
- ,
- {3, 0, 22, 0, 71,}
- ,
- {3, 0, 21, 0, 72,}
- ,
- {3, 0, 21, 0, 70,}
- ,
- {3, 0, 21, 0, 68,}
- ,
- {3, 0, 21, 0, 66,}
- ,
- {3, 0, 21, 0, 64,}
- ,
- {3, 0, 21, 0, 63,}
- ,
- {3, 0, 20, 0, 64,}
- ,
- {3, 0, 19, 0, 65,}
- ,
- {3, 0, 19, 0, 64,}
- ,
- {3, 0, 18, 0, 65,}
- ,
- {3, 0, 18, 0, 64,}
- ,
- {3, 0, 17, 0, 65,}
- ,
- {3, 0, 17, 0, 64,}
- ,
- {3, 0, 16, 0, 65,}
- ,
- {3, 0, 16, 0, 64,}
- ,
- {3, 0, 16, 0, 62,}
- ,
- {3, 0, 16, 0, 60,}
- ,
- {3, 0, 16, 0, 58,}
- ,
- {3, 0, 15, 0, 61,}
- ,
- {3, 0, 15, 0, 59,}
- ,
- {3, 0, 14, 0, 61,}
- ,
- {3, 0, 14, 0, 60,}
- ,
- {3, 0, 14, 0, 58,}
- ,
- {3, 0, 13, 0, 60,}
- ,
- {3, 0, 13, 0, 59,}
- ,
- {3, 0, 12, 0, 62,}
- ,
- {3, 0, 12, 0, 60,}
- ,
- {3, 0, 12, 0, 58,}
- ,
- {3, 0, 11, 0, 62,}
- ,
- {3, 0, 11, 0, 60,}
- ,
- {3, 0, 11, 0, 59,}
- ,
- {3, 0, 11, 0, 57,}
- ,
- {3, 0, 10, 0, 61,}
- ,
- {3, 0, 10, 0, 59,}
- ,
- {3, 0, 10, 0, 57,}
- ,
- {3, 0, 9, 0, 62,}
- ,
- {3, 0, 9, 0, 60,}
- ,
- {3, 0, 9, 0, 58,}
- ,
- {3, 0, 9, 0, 57,}
- ,
- {3, 0, 8, 0, 62,}
- ,
- {3, 0, 8, 0, 60,}
- ,
- {3, 0, 8, 0, 58,}
- ,
- {3, 0, 8, 0, 57,}
- ,
- {3, 0, 8, 0, 55,}
- ,
- {3, 0, 7, 0, 61,}
- ,
- {3, 0, 7, 0, 60,}
- ,
- {3, 0, 7, 0, 58,}
- ,
- {3, 0, 7, 0, 56,}
- ,
- {3, 0, 7, 0, 55,}
- ,
- {3, 0, 6, 0, 62,}
- ,
- {3, 0, 6, 0, 60,}
- ,
- {3, 0, 6, 0, 58,}
- ,
- {3, 0, 6, 0, 57,}
- ,
- {3, 0, 6, 0, 55,}
- ,
- {3, 0, 6, 0, 54,}
- ,
- {3, 0, 6, 0, 52,}
- ,
- {3, 0, 5, 0, 61,}
- ,
- {3, 0, 5, 0, 59,}
- ,
- {3, 0, 5, 0, 57,}
- ,
- {3, 0, 5, 0, 56,}
- ,
- {3, 0, 5, 0, 54,}
- ,
- {3, 0, 5, 0, 53,}
- ,
- {3, 0, 5, 0, 51,}
- ,
- {3, 0, 4, 0, 62,}
- ,
- {3, 0, 4, 0, 60,}
- ,
- {3, 0, 4, 0, 58,}
- ,
- {3, 0, 4, 0, 57,}
- ,
- {3, 0, 4, 0, 55,}
- ,
- {3, 0, 4, 0, 54,}
- ,
- {3, 0, 4, 0, 52,}
- ,
- {3, 0, 4, 0, 51,}
- ,
- {3, 0, 4, 0, 49,}
- ,
- {3, 0, 4, 0, 48,}
- ,
- {3, 0, 4, 0, 46,}
- ,
- {3, 0, 3, 0, 60,}
- ,
- {3, 0, 3, 0, 58,}
- ,
- {3, 0, 3, 0, 57,}
- ,
- {3, 0, 3, 0, 55,}
- ,
- {3, 0, 3, 0, 54,}
- ,
- {3, 0, 3, 0, 52,}
- ,
- {3, 0, 3, 0, 51,}
- ,
- {3, 0, 3, 0, 49,}
- ,
- {3, 0, 3, 0, 48,}
- ,
- {3, 0, 3, 0, 46,}
- ,
- {3, 0, 3, 0, 45,}
- ,
- {3, 0, 3, 0, 44,}
- ,
- {3, 0, 3, 0, 43,}
- ,
- {3, 0, 3, 0, 41,}
- ,
- {3, 0, 2, 0, 61,}
- ,
- {3, 0, 2, 0, 59,}
- ,
- {3, 0, 2, 0, 57,}
- ,
- {3, 0, 2, 0, 56,}
- ,
- {3, 0, 2, 0, 54,}
- ,
- {3, 0, 2, 0, 53,}
- ,
- {3, 0, 2, 0, 51,}
- ,
- {3, 0, 2, 0, 50,}
- ,
- {3, 0, 2, 0, 48,}
- ,
- {3, 0, 2, 0, 47,}
- ,
- {3, 0, 2, 0, 46,}
- ,
- {3, 0, 2, 0, 44,}
- ,
- {3, 0, 2, 0, 43,}
- ,
- {3, 0, 2, 0, 42,}
- ,
- {3, 0, 2, 0, 41,}
- ,
- {3, 0, 2, 0, 39,}
- ,
- {3, 0, 2, 0, 38,}
- ,
- {3, 0, 2, 0, 37,}
- ,
- {3, 0, 2, 0, 36,}
- ,
- {3, 0, 2, 0, 35,}
- ,
- {3, 0, 2, 0, 34,}
- ,
- {3, 0, 2, 0, 33,}
- ,
- {3, 0, 2, 0, 32,}
- ,
- {3, 0, 1, 0, 63,}
- ,
- {3, 0, 1, 0, 61,}
- ,
- {3, 0, 1, 0, 59,}
- ,
- {3, 0, 1, 0, 57,}
- ,
+const struct lcnphy_tx_gain_tbl_entry
+dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = {
+ {3, 0, 31, 0, 72},
+ {3, 0, 31, 0, 70},
+ {3, 0, 31, 0, 68},
+ {3, 0, 30, 0, 67},
+ {3, 0, 29, 0, 68},
+ {3, 0, 28, 0, 68},
+ {3, 0, 27, 0, 69},
+ {3, 0, 26, 0, 70},
+ {3, 0, 25, 0, 70},
+ {3, 0, 24, 0, 71},
+ {3, 0, 23, 0, 72},
+ {3, 0, 23, 0, 70},
+ {3, 0, 22, 0, 71},
+ {3, 0, 21, 0, 72},
+ {3, 0, 21, 0, 70},
+ {3, 0, 21, 0, 68},
+ {3, 0, 21, 0, 66},
+ {3, 0, 21, 0, 64},
+ {3, 0, 21, 0, 63},
+ {3, 0, 20, 0, 64},
+ {3, 0, 19, 0, 65},
+ {3, 0, 19, 0, 64},
+ {3, 0, 18, 0, 65},
+ {3, 0, 18, 0, 64},
+ {3, 0, 17, 0, 65},
+ {3, 0, 17, 0, 64},
+ {3, 0, 16, 0, 65},
+ {3, 0, 16, 0, 64},
+ {3, 0, 16, 0, 62},
+ {3, 0, 16, 0, 60},
+ {3, 0, 16, 0, 58},
+ {3, 0, 15, 0, 61},
+ {3, 0, 15, 0, 59},
+ {3, 0, 14, 0, 61},
+ {3, 0, 14, 0, 60},
+ {3, 0, 14, 0, 58},
+ {3, 0, 13, 0, 60},
+ {3, 0, 13, 0, 59},
+ {3, 0, 12, 0, 62},
+ {3, 0, 12, 0, 60},
+ {3, 0, 12, 0, 58},
+ {3, 0, 11, 0, 62},
+ {3, 0, 11, 0, 60},
+ {3, 0, 11, 0, 59},
+ {3, 0, 11, 0, 57},
+ {3, 0, 10, 0, 61},
+ {3, 0, 10, 0, 59},
+ {3, 0, 10, 0, 57},
+ {3, 0, 9, 0, 62},
+ {3, 0, 9, 0, 60},
+ {3, 0, 9, 0, 58},
+ {3, 0, 9, 0, 57},
+ {3, 0, 8, 0, 62},
+ {3, 0, 8, 0, 60},
+ {3, 0, 8, 0, 58},
+ {3, 0, 8, 0, 57},
+ {3, 0, 8, 0, 55},
+ {3, 0, 7, 0, 61},
+ {3, 0, 7, 0, 60},
+ {3, 0, 7, 0, 58},
+ {3, 0, 7, 0, 56},
+ {3, 0, 7, 0, 55},
+ {3, 0, 6, 0, 62},
+ {3, 0, 6, 0, 60},
+ {3, 0, 6, 0, 58},
+ {3, 0, 6, 0, 57},
+ {3, 0, 6, 0, 55},
+ {3, 0, 6, 0, 54},
+ {3, 0, 6, 0, 52},
+ {3, 0, 5, 0, 61},
+ {3, 0, 5, 0, 59},
+ {3, 0, 5, 0, 57},
+ {3, 0, 5, 0, 56},
+ {3, 0, 5, 0, 54},
+ {3, 0, 5, 0, 53},
+ {3, 0, 5, 0, 51},
+ {3, 0, 4, 0, 62},
+ {3, 0, 4, 0, 60},
+ {3, 0, 4, 0, 58},
+ {3, 0, 4, 0, 57},
+ {3, 0, 4, 0, 55},
+ {3, 0, 4, 0, 54},
+ {3, 0, 4, 0, 52},
+ {3, 0, 4, 0, 51},
+ {3, 0, 4, 0, 49},
+ {3, 0, 4, 0, 48},
+ {3, 0, 4, 0, 46},
+ {3, 0, 3, 0, 60},
+ {3, 0, 3, 0, 58},
+ {3, 0, 3, 0, 57},
+ {3, 0, 3, 0, 55},
+ {3, 0, 3, 0, 54},
+ {3, 0, 3, 0, 52},
+ {3, 0, 3, 0, 51},
+ {3, 0, 3, 0, 49},
+ {3, 0, 3, 0, 48},
+ {3, 0, 3, 0, 46},
+ {3, 0, 3, 0, 45},
+ {3, 0, 3, 0, 44},
+ {3, 0, 3, 0, 43},
+ {3, 0, 3, 0, 41},
+ {3, 0, 2, 0, 61},
+ {3, 0, 2, 0, 59},
+ {3, 0, 2, 0, 57},
+ {3, 0, 2, 0, 56},
+ {3, 0, 2, 0, 54},
+ {3, 0, 2, 0, 53},
+ {3, 0, 2, 0, 51},
+ {3, 0, 2, 0, 50},
+ {3, 0, 2, 0, 48},
+ {3, 0, 2, 0, 47},
+ {3, 0, 2, 0, 46},
+ {3, 0, 2, 0, 44},
+ {3, 0, 2, 0, 43},
+ {3, 0, 2, 0, 42},
+ {3, 0, 2, 0, 41},
+ {3, 0, 2, 0, 39},
+ {3, 0, 2, 0, 38},
+ {3, 0, 2, 0, 37},
+ {3, 0, 2, 0, 36},
+ {3, 0, 2, 0, 35},
+ {3, 0, 2, 0, 34},
+ {3, 0, 2, 0, 33},
+ {3, 0, 2, 0, 32},
+ {3, 0, 1, 0, 63},
+ {3, 0, 1, 0, 61},
+ {3, 0, 1, 0, 59},
+ {3, 0, 1, 0, 57},
};
-const lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[128] = {
- {7, 0, 31, 0, 72,}
- ,
- {7, 0, 31, 0, 70,}
- ,
- {7, 0, 31, 0, 68,}
- ,
- {7, 0, 30, 0, 67,}
- ,
- {7, 0, 29, 0, 68,}
- ,
- {7, 0, 28, 0, 68,}
- ,
- {7, 0, 27, 0, 69,}
- ,
- {7, 0, 26, 0, 70,}
- ,
- {7, 0, 25, 0, 70,}
- ,
- {7, 0, 24, 0, 71,}
- ,
- {7, 0, 23, 0, 72,}
- ,
- {7, 0, 23, 0, 70,}
- ,
- {7, 0, 22, 0, 71,}
- ,
- {7, 0, 21, 0, 72,}
- ,
- {7, 0, 21, 0, 70,}
- ,
- {7, 0, 21, 0, 68,}
- ,
- {7, 0, 21, 0, 66,}
- ,
- {7, 0, 21, 0, 64,}
- ,
- {7, 0, 21, 0, 63,}
- ,
- {7, 0, 20, 0, 64,}
- ,
- {7, 0, 19, 0, 65,}
- ,
- {7, 0, 19, 0, 64,}
- ,
- {7, 0, 18, 0, 65,}
- ,
- {7, 0, 18, 0, 64,}
- ,
- {7, 0, 17, 0, 65,}
- ,
- {7, 0, 17, 0, 64,}
- ,
- {7, 0, 16, 0, 65,}
- ,
- {7, 0, 16, 0, 64,}
- ,
- {7, 0, 16, 0, 62,}
- ,
- {7, 0, 16, 0, 60,}
- ,
- {7, 0, 16, 0, 58,}
- ,
- {7, 0, 15, 0, 61,}
- ,
- {7, 0, 15, 0, 59,}
- ,
- {7, 0, 14, 0, 61,}
- ,
- {7, 0, 14, 0, 60,}
- ,
- {7, 0, 14, 0, 58,}
- ,
- {7, 0, 13, 0, 60,}
- ,
- {7, 0, 13, 0, 59,}
- ,
- {7, 0, 12, 0, 62,}
- ,
- {7, 0, 12, 0, 60,}
- ,
- {7, 0, 12, 0, 58,}
- ,
- {7, 0, 11, 0, 62,}
- ,
- {7, 0, 11, 0, 60,}
- ,
- {7, 0, 11, 0, 59,}
- ,
- {7, 0, 11, 0, 57,}
- ,
- {7, 0, 10, 0, 61,}
- ,
- {7, 0, 10, 0, 59,}
- ,
- {7, 0, 10, 0, 57,}
- ,
- {7, 0, 9, 0, 62,}
- ,
- {7, 0, 9, 0, 60,}
- ,
- {7, 0, 9, 0, 58,}
- ,
- {7, 0, 9, 0, 57,}
- ,
- {7, 0, 8, 0, 62,}
- ,
- {7, 0, 8, 0, 60,}
- ,
- {7, 0, 8, 0, 58,}
- ,
- {7, 0, 8, 0, 57,}
- ,
- {7, 0, 8, 0, 55,}
- ,
- {7, 0, 7, 0, 61,}
- ,
- {7, 0, 7, 0, 60,}
- ,
- {7, 0, 7, 0, 58,}
- ,
- {7, 0, 7, 0, 56,}
- ,
- {7, 0, 7, 0, 55,}
- ,
- {7, 0, 6, 0, 62,}
- ,
- {7, 0, 6, 0, 60,}
- ,
- {7, 0, 6, 0, 58,}
- ,
- {7, 0, 6, 0, 57,}
- ,
- {7, 0, 6, 0, 55,}
- ,
- {7, 0, 6, 0, 54,}
- ,
- {7, 0, 6, 0, 52,}
- ,
- {7, 0, 5, 0, 61,}
- ,
- {7, 0, 5, 0, 59,}
- ,
- {7, 0, 5, 0, 57,}
- ,
- {7, 0, 5, 0, 56,}
- ,
- {7, 0, 5, 0, 54,}
- ,
- {7, 0, 5, 0, 53,}
- ,
- {7, 0, 5, 0, 51,}
- ,
- {7, 0, 4, 0, 62,}
- ,
- {7, 0, 4, 0, 60,}
- ,
- {7, 0, 4, 0, 58,}
- ,
- {7, 0, 4, 0, 57,}
- ,
- {7, 0, 4, 0, 55,}
- ,
- {7, 0, 4, 0, 54,}
- ,
- {7, 0, 4, 0, 52,}
- ,
- {7, 0, 4, 0, 51,}
- ,
- {7, 0, 4, 0, 49,}
- ,
- {7, 0, 4, 0, 48,}
- ,
- {7, 0, 4, 0, 46,}
- ,
- {7, 0, 3, 0, 60,}
- ,
- {7, 0, 3, 0, 58,}
- ,
- {7, 0, 3, 0, 57,}
- ,
- {7, 0, 3, 0, 55,}
- ,
- {7, 0, 3, 0, 54,}
- ,
- {7, 0, 3, 0, 52,}
- ,
- {7, 0, 3, 0, 51,}
- ,
- {7, 0, 3, 0, 49,}
- ,
- {7, 0, 3, 0, 48,}
- ,
- {7, 0, 3, 0, 46,}
- ,
- {7, 0, 3, 0, 45,}
- ,
- {7, 0, 3, 0, 44,}
- ,
- {7, 0, 3, 0, 43,}
- ,
- {7, 0, 3, 0, 41,}
- ,
- {7, 0, 2, 0, 61,}
- ,
- {7, 0, 2, 0, 59,}
- ,
- {7, 0, 2, 0, 57,}
- ,
- {7, 0, 2, 0, 56,}
- ,
- {7, 0, 2, 0, 54,}
- ,
- {7, 0, 2, 0, 53,}
- ,
- {7, 0, 2, 0, 51,}
- ,
- {7, 0, 2, 0, 50,}
- ,
- {7, 0, 2, 0, 48,}
- ,
- {7, 0, 2, 0, 47,}
- ,
- {7, 0, 2, 0, 46,}
- ,
- {7, 0, 2, 0, 44,}
- ,
- {7, 0, 2, 0, 43,}
- ,
- {7, 0, 2, 0, 42,}
- ,
- {7, 0, 2, 0, 41,}
- ,
- {7, 0, 2, 0, 39,}
- ,
- {7, 0, 2, 0, 38,}
- ,
- {7, 0, 2, 0, 37,}
- ,
- {7, 0, 2, 0, 36,}
- ,
- {7, 0, 2, 0, 35,}
- ,
- {7, 0, 2, 0, 34,}
- ,
- {7, 0, 2, 0, 33,}
- ,
- {7, 0, 2, 0, 32,}
- ,
- {7, 0, 1, 0, 63,}
- ,
- {7, 0, 1, 0, 61,}
- ,
- {7, 0, 1, 0, 59,}
- ,
- {7, 0, 1, 0, 57,}
- ,
+const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[128] = {
+ {7, 0, 31, 0, 72},
+ {7, 0, 31, 0, 70},
+ {7, 0, 31, 0, 68},
+ {7, 0, 30, 0, 67},
+ {7, 0, 29, 0, 68},
+ {7, 0, 28, 0, 68},
+ {7, 0, 27, 0, 69},
+ {7, 0, 26, 0, 70},
+ {7, 0, 25, 0, 70},
+ {7, 0, 24, 0, 71},
+ {7, 0, 23, 0, 72},
+ {7, 0, 23, 0, 70},
+ {7, 0, 22, 0, 71},
+ {7, 0, 21, 0, 72},
+ {7, 0, 21, 0, 70},
+ {7, 0, 21, 0, 68},
+ {7, 0, 21, 0, 66},
+ {7, 0, 21, 0, 64},
+ {7, 0, 21, 0, 63},
+ {7, 0, 20, 0, 64},
+ {7, 0, 19, 0, 65},
+ {7, 0, 19, 0, 64},
+ {7, 0, 18, 0, 65},
+ {7, 0, 18, 0, 64},
+ {7, 0, 17, 0, 65},
+ {7, 0, 17, 0, 64},
+ {7, 0, 16, 0, 65},
+ {7, 0, 16, 0, 64},
+ {7, 0, 16, 0, 62},
+ {7, 0, 16, 0, 60},
+ {7, 0, 16, 0, 58},
+ {7, 0, 15, 0, 61},
+ {7, 0, 15, 0, 59},
+ {7, 0, 14, 0, 61},
+ {7, 0, 14, 0, 60},
+ {7, 0, 14, 0, 58},
+ {7, 0, 13, 0, 60},
+ {7, 0, 13, 0, 59},
+ {7, 0, 12, 0, 62},
+ {7, 0, 12, 0, 60},
+ {7, 0, 12, 0, 58},
+ {7, 0, 11, 0, 62},
+ {7, 0, 11, 0, 60},
+ {7, 0, 11, 0, 59},
+ {7, 0, 11, 0, 57},
+ {7, 0, 10, 0, 61},
+ {7, 0, 10, 0, 59},
+ {7, 0, 10, 0, 57},
+ {7, 0, 9, 0, 62},
+ {7, 0, 9, 0, 60},
+ {7, 0, 9, 0, 58},
+ {7, 0, 9, 0, 57},
+ {7, 0, 8, 0, 62},
+ {7, 0, 8, 0, 60},
+ {7, 0, 8, 0, 58},
+ {7, 0, 8, 0, 57},
+ {7, 0, 8, 0, 55},
+ {7, 0, 7, 0, 61},
+ {7, 0, 7, 0, 60},
+ {7, 0, 7, 0, 58},
+ {7, 0, 7, 0, 56},
+ {7, 0, 7, 0, 55},
+ {7, 0, 6, 0, 62},
+ {7, 0, 6, 0, 60},
+ {7, 0, 6, 0, 58},
+ {7, 0, 6, 0, 57},
+ {7, 0, 6, 0, 55},
+ {7, 0, 6, 0, 54},
+ {7, 0, 6, 0, 52},
+ {7, 0, 5, 0, 61},
+ {7, 0, 5, 0, 59},
+ {7, 0, 5, 0, 57},
+ {7, 0, 5, 0, 56},
+ {7, 0, 5, 0, 54},
+ {7, 0, 5, 0, 53},
+ {7, 0, 5, 0, 51},
+ {7, 0, 4, 0, 62},
+ {7, 0, 4, 0, 60},
+ {7, 0, 4, 0, 58},
+ {7, 0, 4, 0, 57},
+ {7, 0, 4, 0, 55},
+ {7, 0, 4, 0, 54},
+ {7, 0, 4, 0, 52},
+ {7, 0, 4, 0, 51},
+ {7, 0, 4, 0, 49},
+ {7, 0, 4, 0, 48},
+ {7, 0, 4, 0, 46},
+ {7, 0, 3, 0, 60},
+ {7, 0, 3, 0, 58},
+ {7, 0, 3, 0, 57},
+ {7, 0, 3, 0, 55},
+ {7, 0, 3, 0, 54},
+ {7, 0, 3, 0, 52},
+ {7, 0, 3, 0, 51},
+ {7, 0, 3, 0, 49},
+ {7, 0, 3, 0, 48},
+ {7, 0, 3, 0, 46},
+ {7, 0, 3, 0, 45},
+ {7, 0, 3, 0, 44},
+ {7, 0, 3, 0, 43},
+ {7, 0, 3, 0, 41},
+ {7, 0, 2, 0, 61},
+ {7, 0, 2, 0, 59},
+ {7, 0, 2, 0, 57},
+ {7, 0, 2, 0, 56},
+ {7, 0, 2, 0, 54},
+ {7, 0, 2, 0, 53},
+ {7, 0, 2, 0, 51},
+ {7, 0, 2, 0, 50},
+ {7, 0, 2, 0, 48},
+ {7, 0, 2, 0, 47},
+ {7, 0, 2, 0, 46},
+ {7, 0, 2, 0, 44},
+ {7, 0, 2, 0, 43},
+ {7, 0, 2, 0, 42},
+ {7, 0, 2, 0, 41},
+ {7, 0, 2, 0, 39},
+ {7, 0, 2, 0, 38},
+ {7, 0, 2, 0, 37},
+ {7, 0, 2, 0, 36},
+ {7, 0, 2, 0, 35},
+ {7, 0, 2, 0, 34},
+ {7, 0, 2, 0, 33},
+ {7, 0, 2, 0, 32},
+ {7, 0, 1, 0, 63},
+ {7, 0, 1, 0, 61},
+ {7, 0, 1, 0, 59},
+ {7, 0, 1, 0, 57},
};
-const lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[128] = {
- {255, 255, 0xf0, 0, 152,}
- ,
- {255, 255, 0xf0, 0, 147,}
- ,
- {255, 255, 0xf0, 0, 143,}
- ,
- {255, 255, 0xf0, 0, 139,}
- ,
- {255, 255, 0xf0, 0, 135,}
- ,
- {255, 255, 0xf0, 0, 131,}
- ,
- {255, 255, 0xf0, 0, 128,}
- ,
- {255, 255, 0xf0, 0, 124,}
- ,
- {255, 255, 0xf0, 0, 121,}
- ,
- {255, 255, 0xf0, 0, 117,}
- ,
- {255, 255, 0xf0, 0, 114,}
- ,
- {255, 255, 0xf0, 0, 111,}
- ,
- {255, 255, 0xf0, 0, 107,}
- ,
- {255, 255, 0xf0, 0, 104,}
- ,
- {255, 255, 0xf0, 0, 101,}
- ,
- {255, 255, 0xf0, 0, 99,}
- ,
- {255, 255, 0xf0, 0, 96,}
- ,
- {255, 255, 0xf0, 0, 93,}
- ,
- {255, 255, 0xf0, 0, 90,}
- ,
- {255, 255, 0xf0, 0, 88,}
- ,
- {255, 255, 0xf0, 0, 85,}
- ,
- {255, 255, 0xf0, 0, 83,}
- ,
- {255, 255, 0xf0, 0, 81,}
- ,
- {255, 255, 0xf0, 0, 78,}
- ,
- {255, 255, 0xf0, 0, 76,}
- ,
- {255, 255, 0xf0, 0, 74,}
- ,
- {255, 255, 0xf0, 0, 72,}
- ,
- {255, 255, 0xf0, 0, 70,}
- ,
- {255, 255, 0xf0, 0, 68,}
- ,
- {255, 255, 0xf0, 0, 66,}
- ,
- {255, 255, 0xf0, 0, 64,}
- ,
- {255, 248, 0xf0, 0, 64,}
- ,
- {255, 241, 0xf0, 0, 64,}
- ,
- {255, 251, 0xe0, 0, 64,}
- ,
- {255, 244, 0xe0, 0, 64,}
- ,
- {255, 254, 0xd0, 0, 64,}
- ,
- {255, 246, 0xd0, 0, 64,}
- ,
- {255, 239, 0xd0, 0, 64,}
- ,
- {255, 249, 0xc0, 0, 64,}
- ,
- {255, 242, 0xc0, 0, 64,}
- ,
- {255, 255, 0xb0, 0, 64,}
- ,
- {255, 248, 0xb0, 0, 64,}
- ,
- {255, 241, 0xb0, 0, 64,}
- ,
- {255, 254, 0xa0, 0, 64,}
- ,
- {255, 246, 0xa0, 0, 64,}
- ,
- {255, 239, 0xa0, 0, 64,}
- ,
- {255, 255, 0x90, 0, 64,}
- ,
- {255, 248, 0x90, 0, 64,}
- ,
- {255, 241, 0x90, 0, 64,}
- ,
- {255, 234, 0x90, 0, 64,}
- ,
- {255, 255, 0x80, 0, 64,}
- ,
- {255, 248, 0x80, 0, 64,}
- ,
- {255, 241, 0x80, 0, 64,}
- ,
- {255, 234, 0x80, 0, 64,}
- ,
- {255, 255, 0x70, 0, 64,}
- ,
- {255, 248, 0x70, 0, 64,}
- ,
- {255, 241, 0x70, 0, 64,}
- ,
- {255, 234, 0x70, 0, 64,}
- ,
- {255, 227, 0x70, 0, 64,}
- ,
- {255, 221, 0x70, 0, 64,}
- ,
- {255, 215, 0x70, 0, 64,}
- ,
- {255, 208, 0x70, 0, 64,}
- ,
- {255, 203, 0x70, 0, 64,}
- ,
- {255, 197, 0x70, 0, 64,}
- ,
- {255, 255, 0x60, 0, 64,}
- ,
- {255, 248, 0x60, 0, 64,}
- ,
- {255, 241, 0x60, 0, 64,}
- ,
- {255, 234, 0x60, 0, 64,}
- ,
- {255, 227, 0x60, 0, 64,}
- ,
- {255, 221, 0x60, 0, 64,}
- ,
- {255, 255, 0x50, 0, 64,}
- ,
- {255, 248, 0x50, 0, 64,}
- ,
- {255, 241, 0x50, 0, 64,}
- ,
- {255, 234, 0x50, 0, 64,}
- ,
- {255, 227, 0x50, 0, 64,}
- ,
- {255, 221, 0x50, 0, 64,}
- ,
- {255, 215, 0x50, 0, 64,}
- ,
- {255, 208, 0x50, 0, 64,}
- ,
- {255, 255, 0x40, 0, 64,}
- ,
- {255, 248, 0x40, 0, 64,}
- ,
- {255, 241, 0x40, 0, 64,}
- ,
- {255, 234, 0x40, 0, 64,}
- ,
- {255, 227, 0x40, 0, 64,}
- ,
- {255, 221, 0x40, 0, 64,}
- ,
- {255, 215, 0x40, 0, 64,}
- ,
- {255, 208, 0x40, 0, 64,}
- ,
- {255, 203, 0x40, 0, 64,}
- ,
- {255, 197, 0x40, 0, 64,}
- ,
- {255, 255, 0x30, 0, 64,}
- ,
- {255, 248, 0x30, 0, 64,}
- ,
- {255, 241, 0x30, 0, 64,}
- ,
- {255, 234, 0x30, 0, 64,}
- ,
- {255, 227, 0x30, 0, 64,}
- ,
- {255, 221, 0x30, 0, 64,}
- ,
- {255, 215, 0x30, 0, 64,}
- ,
- {255, 208, 0x30, 0, 64,}
- ,
- {255, 203, 0x30, 0, 64,}
- ,
- {255, 197, 0x30, 0, 64,}
- ,
- {255, 191, 0x30, 0, 64,}
- ,
- {255, 186, 0x30, 0, 64,}
- ,
- {255, 181, 0x30, 0, 64,}
- ,
- {255, 175, 0x30, 0, 64,}
- ,
- {255, 255, 0x20, 0, 64,}
- ,
- {255, 248, 0x20, 0, 64,}
- ,
- {255, 241, 0x20, 0, 64,}
- ,
- {255, 234, 0x20, 0, 64,}
- ,
- {255, 227, 0x20, 0, 64,}
- ,
- {255, 221, 0x20, 0, 64,}
- ,
- {255, 215, 0x20, 0, 64,}
- ,
- {255, 208, 0x20, 0, 64,}
- ,
- {255, 203, 0x20, 0, 64,}
- ,
- {255, 197, 0x20, 0, 64,}
- ,
- {255, 191, 0x20, 0, 64,}
- ,
- {255, 186, 0x20, 0, 64,}
- ,
- {255, 181, 0x20, 0, 64,}
- ,
- {255, 175, 0x20, 0, 64,}
- ,
- {255, 170, 0x20, 0, 64,}
- ,
- {255, 166, 0x20, 0, 64,}
- ,
- {255, 161, 0x20, 0, 64,}
- ,
- {255, 156, 0x20, 0, 64,}
- ,
- {255, 152, 0x20, 0, 64,}
- ,
- {255, 148, 0x20, 0, 64,}
- ,
- {255, 143, 0x20, 0, 64,}
- ,
- {255, 139, 0x20, 0, 64,}
- ,
- {255, 135, 0x20, 0, 64,}
- ,
- {255, 132, 0x20, 0, 64,}
- ,
- {255, 255, 0x10, 0, 64,}
- ,
- {255, 248, 0x10, 0, 64,}
- ,
+const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[128] = {
+ {255, 255, 0xf0, 0, 152},
+ {255, 255, 0xf0, 0, 147},
+ {255, 255, 0xf0, 0, 143},
+ {255, 255, 0xf0, 0, 139},
+ {255, 255, 0xf0, 0, 135},
+ {255, 255, 0xf0, 0, 131},
+ {255, 255, 0xf0, 0, 128},
+ {255, 255, 0xf0, 0, 124},
+ {255, 255, 0xf0, 0, 121},
+ {255, 255, 0xf0, 0, 117},
+ {255, 255, 0xf0, 0, 114},
+ {255, 255, 0xf0, 0, 111},
+ {255, 255, 0xf0, 0, 107},
+ {255, 255, 0xf0, 0, 104},
+ {255, 255, 0xf0, 0, 101},
+ {255, 255, 0xf0, 0, 99},
+ {255, 255, 0xf0, 0, 96},
+ {255, 255, 0xf0, 0, 93},
+ {255, 255, 0xf0, 0, 90},
+ {255, 255, 0xf0, 0, 88},
+ {255, 255, 0xf0, 0, 85},
+ {255, 255, 0xf0, 0, 83},
+ {255, 255, 0xf0, 0, 81},
+ {255, 255, 0xf0, 0, 78},
+ {255, 255, 0xf0, 0, 76},
+ {255, 255, 0xf0, 0, 74},
+ {255, 255, 0xf0, 0, 72},
+ {255, 255, 0xf0, 0, 70},
+ {255, 255, 0xf0, 0, 68},
+ {255, 255, 0xf0, 0, 66},
+ {255, 255, 0xf0, 0, 64},
+ {255, 248, 0xf0, 0, 64},
+ {255, 241, 0xf0, 0, 64},
+ {255, 251, 0xe0, 0, 64},
+ {255, 244, 0xe0, 0, 64},
+ {255, 254, 0xd0, 0, 64},
+ {255, 246, 0xd0, 0, 64},
+ {255, 239, 0xd0, 0, 64},
+ {255, 249, 0xc0, 0, 64},
+ {255, 242, 0xc0, 0, 64},
+ {255, 255, 0xb0, 0, 64},
+ {255, 248, 0xb0, 0, 64},
+ {255, 241, 0xb0, 0, 64},
+ {255, 254, 0xa0, 0, 64},
+ {255, 246, 0xa0, 0, 64},
+ {255, 239, 0xa0, 0, 64},
+ {255, 255, 0x90, 0, 64},
+ {255, 248, 0x90, 0, 64},
+ {255, 241, 0x90, 0, 64},
+ {255, 234, 0x90, 0, 64},
+ {255, 255, 0x80, 0, 64},
+ {255, 248, 0x80, 0, 64},
+ {255, 241, 0x80, 0, 64},
+ {255, 234, 0x80, 0, 64},
+ {255, 255, 0x70, 0, 64},
+ {255, 248, 0x70, 0, 64},
+ {255, 241, 0x70, 0, 64},
+ {255, 234, 0x70, 0, 64},
+ {255, 227, 0x70, 0, 64},
+ {255, 221, 0x70, 0, 64},
+ {255, 215, 0x70, 0, 64},
+ {255, 208, 0x70, 0, 64},
+ {255, 203, 0x70, 0, 64},
+ {255, 197, 0x70, 0, 64},
+ {255, 255, 0x60, 0, 64},
+ {255, 248, 0x60, 0, 64},
+ {255, 241, 0x60, 0, 64},
+ {255, 234, 0x60, 0, 64},
+ {255, 227, 0x60, 0, 64},
+ {255, 221, 0x60, 0, 64},
+ {255, 255, 0x50, 0, 64},
+ {255, 248, 0x50, 0, 64},
+ {255, 241, 0x50, 0, 64},
+ {255, 234, 0x50, 0, 64},
+ {255, 227, 0x50, 0, 64},
+ {255, 221, 0x50, 0, 64},
+ {255, 215, 0x50, 0, 64},
+ {255, 208, 0x50, 0, 64},
+ {255, 255, 0x40, 0, 64},
+ {255, 248, 0x40, 0, 64},
+ {255, 241, 0x40, 0, 64},
+ {255, 234, 0x40, 0, 64},
+ {255, 227, 0x40, 0, 64},
+ {255, 221, 0x40, 0, 64},
+ {255, 215, 0x40, 0, 64},
+ {255, 208, 0x40, 0, 64},
+ {255, 203, 0x40, 0, 64},
+ {255, 197, 0x40, 0, 64},
+ {255, 255, 0x30, 0, 64},
+ {255, 248, 0x30, 0, 64},
+ {255, 241, 0x30, 0, 64},
+ {255, 234, 0x30, 0, 64},
+ {255, 227, 0x30, 0, 64},
+ {255, 221, 0x30, 0, 64},
+ {255, 215, 0x30, 0, 64},
+ {255, 208, 0x30, 0, 64},
+ {255, 203, 0x30, 0, 64},
+ {255, 197, 0x30, 0, 64},
+ {255, 191, 0x30, 0, 64},
+ {255, 186, 0x30, 0, 64},
+ {255, 181, 0x30, 0, 64},
+ {255, 175, 0x30, 0, 64},
+ {255, 255, 0x20, 0, 64},
+ {255, 248, 0x20, 0, 64},
+ {255, 241, 0x20, 0, 64},
+ {255, 234, 0x20, 0, 64},
+ {255, 227, 0x20, 0, 64},
+ {255, 221, 0x20, 0, 64},
+ {255, 215, 0x20, 0, 64},
+ {255, 208, 0x20, 0, 64},
+ {255, 203, 0x20, 0, 64},
+ {255, 197, 0x20, 0, 64},
+ {255, 191, 0x20, 0, 64},
+ {255, 186, 0x20, 0, 64},
+ {255, 181, 0x20, 0, 64},
+ {255, 175, 0x20, 0, 64},
+ {255, 170, 0x20, 0, 64},
+ {255, 166, 0x20, 0, 64},
+ {255, 161, 0x20, 0, 64},
+ {255, 156, 0x20, 0, 64},
+ {255, 152, 0x20, 0, 64},
+ {255, 148, 0x20, 0, 64},
+ {255, 143, 0x20, 0, 64},
+ {255, 139, 0x20, 0, 64},
+ {255, 135, 0x20, 0, 64},
+ {255, 132, 0x20, 0, 64},
+ {255, 255, 0x10, 0, 64},
+ {255, 248, 0x10, 0, 64},
};
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_lcn.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h
index 5a64a988d107..5f75e16bf5a7 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_lcn.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h
@@ -14,36 +14,41 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-typedef phytbl_info_t dot11lcnphytbl_info_t;
+#include <types.h>
+#include "phy_int.h"
-extern const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_rev0[];
+extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[];
extern const u32 dot11lcnphytbl_rx_gain_info_sz_rev0;
-extern const dot11lcnphytbl_info_t dot11lcn_sw_ctrl_tbl_info_4313;
-extern const dot11lcnphytbl_info_t dot11lcn_sw_ctrl_tbl_info_4313_epa;
-extern const dot11lcnphytbl_info_t dot11lcn_sw_ctrl_tbl_info_4313_epa_combo;
+extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313;
+extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa;
+extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa_combo;
+extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa;
+extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250;
-extern const dot11lcnphytbl_info_t dot11lcnphytbl_info_rev0[];
+extern const struct phytbl_info dot11lcnphytbl_info_rev0[];
extern const u32 dot11lcnphytbl_info_sz_rev0;
-extern const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_2G_rev2[];
+extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[];
extern const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz;
-extern const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_5G_rev2[];
+extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[];
extern const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz;
-extern const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[];
+extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[];
-extern const dot11lcnphytbl_info_t dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[];
+extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[];
-typedef struct {
+struct lcnphy_tx_gain_tbl_entry {
unsigned char gm;
unsigned char pga;
unsigned char pad;
unsigned char dac;
unsigned char bb_mult;
-} lcnphy_tx_gain_tbl_entry;
+};
-extern const lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[];
-extern const lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_extPA_gaintable_rev0[];
+extern const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[];
-extern const lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[];
+extern const struct
+lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_extPA_gaintable_rev0[];
+
+extern const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[];
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_n.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c
index 742df997a3b1..dbf50ef6cd75 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_n.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c
@@ -14,13 +14,10 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/kernel.h>
+#include <types.h>
+#include "phytbl_n.h"
-#include <sbhnddma.h>
-#include <wlc_phy_int.h>
-#include <wlc_phytbl_n.h>
-
-const u32 frame_struct_rev0[] = {
+static const u32 frame_struct_rev0[] = {
0x08004a04,
0x00100000,
0x01000a05,
@@ -855,7 +852,7 @@ const u32 frame_struct_rev0[] = {
0x00000000,
};
-const u8 frame_lut_rev0[] = {
+static const u8 frame_lut_rev0[] = {
0x02,
0x04,
0x14,
@@ -890,7 +887,7 @@ const u8 frame_lut_rev0[] = {
0x2a,
};
-const u32 tmap_tbl_rev0[] = {
+static const u32 tmap_tbl_rev0[] = {
0x8a88aa80,
0x8aaaaa8a,
0x8a8a8aa8,
@@ -1341,7 +1338,7 @@ const u32 tmap_tbl_rev0[] = {
0x00000000,
};
-const u32 tdtrn_tbl_rev0[] = {
+static const u32 tdtrn_tbl_rev0[] = {
0x061c061c,
0x0050ee68,
0xf592fe36,
@@ -2048,7 +2045,7 @@ const u32 tdtrn_tbl_rev0[] = {
0x00f006be,
};
-const u32 intlv_tbl_rev0[] = {
+static const u32 intlv_tbl_rev0[] = {
0x00802070,
0x0671188d,
0x0a60192c,
@@ -2058,7 +2055,7 @@ const u32 intlv_tbl_rev0[] = {
0x00000070,
};
-const u16 pilot_tbl_rev0[] = {
+static const u16 pilot_tbl_rev0[] = {
0xff08,
0xff08,
0xff08,
@@ -2149,7 +2146,7 @@ const u16 pilot_tbl_rev0[] = {
0xffff,
};
-const u32 pltlut_tbl_rev0[] = {
+static const u32 pltlut_tbl_rev0[] = {
0x76540123,
0x62407351,
0x76543201,
@@ -2158,7 +2155,7 @@ const u32 pltlut_tbl_rev0[] = {
0x76430521,
};
-const u32 tdi_tbl20_ant0_rev0[] = {
+static const u32 tdi_tbl20_ant0_rev0[] = {
0x00091226,
0x000a1429,
0x000b56ad,
@@ -2216,7 +2213,7 @@ const u32 tdi_tbl20_ant0_rev0[] = {
0x00000000,
};
-const u32 tdi_tbl20_ant1_rev0[] = {
+static const u32 tdi_tbl20_ant1_rev0[] = {
0x00014b26,
0x00028d29,
0x000393ad,
@@ -2274,7 +2271,7 @@ const u32 tdi_tbl20_ant1_rev0[] = {
0x00000000,
};
-const u32 tdi_tbl40_ant0_rev0[] = {
+static const u32 tdi_tbl40_ant0_rev0[] = {
0x0011a346,
0x00136ccf,
0x0014f5d9,
@@ -2387,7 +2384,7 @@ const u32 tdi_tbl40_ant0_rev0[] = {
0x00000000,
};
-const u32 tdi_tbl40_ant1_rev0[] = {
+static const u32 tdi_tbl40_ant1_rev0[] = {
0x001edb36,
0x000129ca,
0x0002b353,
@@ -2500,7 +2497,7 @@ const u32 tdi_tbl40_ant1_rev0[] = {
0x00000000,
};
-const u16 bdi_tbl_rev0[] = {
+static const u16 bdi_tbl_rev0[] = {
0x0070,
0x0126,
0x012c,
@@ -2509,7 +2506,7 @@ const u16 bdi_tbl_rev0[] = {
0x04d2,
};
-const u32 chanest_tbl_rev0[] = {
+static const u32 chanest_tbl_rev0[] = {
0x44444444,
0x44444444,
0x44444444,
@@ -2608,7 +2605,7 @@ const u32 chanest_tbl_rev0[] = {
0x10101010,
};
-const u8 mcs_tbl_rev0[] = {
+static const u8 mcs_tbl_rev0[] = {
0x00,
0x08,
0x0a,
@@ -2739,7 +2736,7 @@ const u8 mcs_tbl_rev0[] = {
0x00,
};
-const u32 noise_var_tbl0_rev0[] = {
+static const u32 noise_var_tbl0_rev0[] = {
0x020c020c,
0x0000014d,
0x020c020c,
@@ -2998,7 +2995,7 @@ const u32 noise_var_tbl0_rev0[] = {
0x0000014d,
};
-const u32 noise_var_tbl1_rev0[] = {
+static const u32 noise_var_tbl1_rev0[] = {
0x020c020c,
0x0000014d,
0x020c020c,
@@ -3257,7 +3254,7 @@ const u32 noise_var_tbl1_rev0[] = {
0x0000014d,
};
-const u8 est_pwr_lut_core0_rev0[] = {
+static const u8 est_pwr_lut_core0_rev0[] = {
0x50,
0x4f,
0x4e,
@@ -3324,7 +3321,7 @@ const u8 est_pwr_lut_core0_rev0[] = {
0x11,
};
-const u8 est_pwr_lut_core1_rev0[] = {
+static const u8 est_pwr_lut_core1_rev0[] = {
0x50,
0x4f,
0x4e,
@@ -3391,7 +3388,7 @@ const u8 est_pwr_lut_core1_rev0[] = {
0x11,
};
-const u8 adj_pwr_lut_core0_rev0[] = {
+static const u8 adj_pwr_lut_core0_rev0[] = {
0x00,
0x00,
0x00,
@@ -3522,7 +3519,7 @@ const u8 adj_pwr_lut_core0_rev0[] = {
0x00,
};
-const u8 adj_pwr_lut_core1_rev0[] = {
+static const u8 adj_pwr_lut_core1_rev0[] = {
0x00,
0x00,
0x00,
@@ -3653,7 +3650,7 @@ const u8 adj_pwr_lut_core1_rev0[] = {
0x00,
};
-const u32 gainctrl_lut_core0_rev0[] = {
+static const u32 gainctrl_lut_core0_rev0[] = {
0x03cc2b44,
0x03cc2b42,
0x03cc2b40,
@@ -3784,7 +3781,7 @@ const u32 gainctrl_lut_core0_rev0[] = {
0x00002b00,
};
-const u32 gainctrl_lut_core1_rev0[] = {
+static const u32 gainctrl_lut_core1_rev0[] = {
0x03cc2b44,
0x03cc2b42,
0x03cc2b40,
@@ -3915,7 +3912,7 @@ const u32 gainctrl_lut_core1_rev0[] = {
0x00002b00,
};
-const u32 iq_lut_core0_rev0[] = {
+static const u32 iq_lut_core0_rev0[] = {
0x0000007f,
0x0000007f,
0x0000007f,
@@ -4046,7 +4043,7 @@ const u32 iq_lut_core0_rev0[] = {
0x0000007f,
};
-const u32 iq_lut_core1_rev0[] = {
+static const u32 iq_lut_core1_rev0[] = {
0x0000007f,
0x0000007f,
0x0000007f,
@@ -4177,7 +4174,7 @@ const u32 iq_lut_core1_rev0[] = {
0x0000007f,
};
-const u16 loft_lut_core0_rev0[] = {
+static const u16 loft_lut_core0_rev0[] = {
0x0000,
0x0101,
0x0002,
@@ -4308,7 +4305,7 @@ const u16 loft_lut_core0_rev0[] = {
0x0103,
};
-const u16 loft_lut_core1_rev0[] = {
+static const u16 loft_lut_core1_rev0[] = {
0x0000,
0x0101,
0x0002,
@@ -4439,7 +4436,7 @@ const u16 loft_lut_core1_rev0[] = {
0x0103,
};
-const mimophytbl_info_t mimophytbl_info_rev0_volatile[] = {
+const struct phytbl_info mimophytbl_info_rev0_volatile[] = {
{&bdi_tbl_rev0, sizeof(bdi_tbl_rev0) / sizeof(bdi_tbl_rev0[0]), 21, 0,
16}
,
@@ -4487,7 +4484,7 @@ const mimophytbl_info_t mimophytbl_info_rev0_volatile[] = {
,
};
-const mimophytbl_info_t mimophytbl_info_rev0[] = {
+const struct phytbl_info mimophytbl_info_rev0[] = {
{&frame_struct_rev0,
sizeof(frame_struct_rev0) / sizeof(frame_struct_rev0[0]), 10, 0, 32}
,
@@ -4525,7 +4522,8 @@ const mimophytbl_info_t mimophytbl_info_rev0[] = {
{&chanest_tbl_rev0,
sizeof(chanest_tbl_rev0) / sizeof(chanest_tbl_rev0[0]), 22, 0, 32}
,
- {&mcs_tbl_rev0, sizeof(mcs_tbl_rev0) / sizeof(mcs_tbl_rev0[0]), 18, 0, 8}
+ {&mcs_tbl_rev0, sizeof(mcs_tbl_rev0) / sizeof(mcs_tbl_rev0[0]), 18, 0,
+ 8}
,
{&noise_var_tbl0_rev0,
sizeof(noise_var_tbl0_rev0) / sizeof(noise_var_tbl0_rev0[0]), 16, 0,
@@ -4538,12 +4536,12 @@ const mimophytbl_info_t mimophytbl_info_rev0[] = {
};
const u32 mimophytbl_info_sz_rev0 =
- sizeof(mimophytbl_info_rev0) / sizeof(mimophytbl_info_rev0[0]);
+ sizeof(mimophytbl_info_rev0) / sizeof(mimophytbl_info_rev0[0]);
const u32 mimophytbl_info_sz_rev0_volatile =
- sizeof(mimophytbl_info_rev0_volatile) /
- sizeof(mimophytbl_info_rev0_volatile[0]);
+ sizeof(mimophytbl_info_rev0_volatile) /
+ sizeof(mimophytbl_info_rev0_volatile[0]);
-const u16 ant_swctrl_tbl_rev3[] = {
+static const u16 ant_swctrl_tbl_rev3[] = {
0x0082,
0x0082,
0x0211,
@@ -4578,7 +4576,7 @@ const u16 ant_swctrl_tbl_rev3[] = {
0x0000,
};
-const u16 ant_swctrl_tbl_rev3_1[] = {
+static const u16 ant_swctrl_tbl_rev3_1[] = {
0x0022,
0x0022,
0x0011,
@@ -4613,7 +4611,7 @@ const u16 ant_swctrl_tbl_rev3_1[] = {
0x0000,
};
-const u16 ant_swctrl_tbl_rev3_2[] = {
+static const u16 ant_swctrl_tbl_rev3_2[] = {
0x0088,
0x0088,
0x0044,
@@ -4648,7 +4646,7 @@ const u16 ant_swctrl_tbl_rev3_2[] = {
0x0000,
};
-const u16 ant_swctrl_tbl_rev3_3[] = {
+static const u16 ant_swctrl_tbl_rev3_3[] = {
0x022,
0x022,
0x011,
@@ -4683,7 +4681,7 @@ const u16 ant_swctrl_tbl_rev3_3[] = {
0x3cc
};
-const u32 frame_struct_rev3[] = {
+static const u32 frame_struct_rev3[] = {
0x08004a04,
0x00100000,
0x01000a05,
@@ -5518,7 +5516,7 @@ const u32 frame_struct_rev3[] = {
0x00000000,
};
-const u16 pilot_tbl_rev3[] = {
+static const u16 pilot_tbl_rev3[] = {
0xff08,
0xff08,
0xff08,
@@ -5609,7 +5607,7 @@ const u16 pilot_tbl_rev3[] = {
0xffff,
};
-const u32 tmap_tbl_rev3[] = {
+static const u32 tmap_tbl_rev3[] = {
0x8a88aa80,
0x8aaaaa8a,
0x8a8a8aa8,
@@ -6060,7 +6058,7 @@ const u32 tmap_tbl_rev3[] = {
0x00000000,
};
-const u32 intlv_tbl_rev3[] = {
+static const u32 intlv_tbl_rev3[] = {
0x00802070,
0x0671188d,
0x0a60192c,
@@ -6070,7 +6068,7 @@ const u32 intlv_tbl_rev3[] = {
0x00000070,
};
-const u32 tdtrn_tbl_rev3[] = {
+static const u32 tdtrn_tbl_rev3[] = {
0x061c061c,
0x0050ee68,
0xf592fe36,
@@ -7036,7 +7034,7 @@ const u32 noise_var_tbl_rev3[] = {
0x0000014d,
};
-const u16 mcs_tbl_rev3[] = {
+static const u16 mcs_tbl_rev3[] = {
0x0000,
0x0008,
0x000a,
@@ -7167,7 +7165,7 @@ const u16 mcs_tbl_rev3[] = {
0x0007,
};
-const u32 tdi_tbl20_ant0_rev3[] = {
+static const u32 tdi_tbl20_ant0_rev3[] = {
0x00091226,
0x000a1429,
0x000b56ad,
@@ -7225,7 +7223,7 @@ const u32 tdi_tbl20_ant0_rev3[] = {
0x00000000,
};
-const u32 tdi_tbl20_ant1_rev3[] = {
+static const u32 tdi_tbl20_ant1_rev3[] = {
0x00014b26,
0x00028d29,
0x000393ad,
@@ -7283,7 +7281,7 @@ const u32 tdi_tbl20_ant1_rev3[] = {
0x00000000,
};
-const u32 tdi_tbl40_ant0_rev3[] = {
+static const u32 tdi_tbl40_ant0_rev3[] = {
0x0011a346,
0x00136ccf,
0x0014f5d9,
@@ -7396,7 +7394,7 @@ const u32 tdi_tbl40_ant0_rev3[] = {
0x00000000,
};
-const u32 tdi_tbl40_ant1_rev3[] = {
+static const u32 tdi_tbl40_ant1_rev3[] = {
0x001edb36,
0x000129ca,
0x0002b353,
@@ -7509,7 +7507,7 @@ const u32 tdi_tbl40_ant1_rev3[] = {
0x00000000,
};
-const u32 pltlut_tbl_rev3[] = {
+static const u32 pltlut_tbl_rev3[] = {
0x76540213,
0x62407351,
0x76543210,
@@ -7518,7 +7516,7 @@ const u32 pltlut_tbl_rev3[] = {
0x76430521,
};
-const u32 chanest_tbl_rev3[] = {
+static const u32 chanest_tbl_rev3[] = {
0x44444444,
0x44444444,
0x44444444,
@@ -7617,7 +7615,7 @@ const u32 chanest_tbl_rev3[] = {
0x10101010,
};
-const u8 frame_lut_rev3[] = {
+static const u8 frame_lut_rev3[] = {
0x02,
0x04,
0x14,
@@ -7652,7 +7650,7 @@ const u8 frame_lut_rev3[] = {
0x2a,
};
-const u8 est_pwr_lut_core0_rev3[] = {
+static const u8 est_pwr_lut_core0_rev3[] = {
0x55,
0x54,
0x54,
@@ -7719,7 +7717,7 @@ const u8 est_pwr_lut_core0_rev3[] = {
0xfd,
};
-const u8 est_pwr_lut_core1_rev3[] = {
+static const u8 est_pwr_lut_core1_rev3[] = {
0x55,
0x54,
0x54,
@@ -7786,7 +7784,7 @@ const u8 est_pwr_lut_core1_rev3[] = {
0xfd,
};
-const u8 adj_pwr_lut_core0_rev3[] = {
+static const u8 adj_pwr_lut_core0_rev3[] = {
0x00,
0x00,
0x00,
@@ -7917,7 +7915,7 @@ const u8 adj_pwr_lut_core0_rev3[] = {
0x00,
};
-const u8 adj_pwr_lut_core1_rev3[] = {
+static const u8 adj_pwr_lut_core1_rev3[] = {
0x00,
0x00,
0x00,
@@ -8048,7 +8046,7 @@ const u8 adj_pwr_lut_core1_rev3[] = {
0x00,
};
-const u32 gainctrl_lut_core0_rev3[] = {
+static const u32 gainctrl_lut_core0_rev3[] = {
0x5bf70044,
0x5bf70042,
0x5bf70040,
@@ -8179,7 +8177,7 @@ const u32 gainctrl_lut_core0_rev3[] = {
0x5b07001c,
};
-const u32 gainctrl_lut_core1_rev3[] = {
+static const u32 gainctrl_lut_core1_rev3[] = {
0x5bf70044,
0x5bf70042,
0x5bf70040,
@@ -8310,7 +8308,7 @@ const u32 gainctrl_lut_core1_rev3[] = {
0x5b07001c,
};
-const u32 iq_lut_core0_rev3[] = {
+static const u32 iq_lut_core0_rev3[] = {
0x00000000,
0x00000000,
0x00000000,
@@ -8441,7 +8439,7 @@ const u32 iq_lut_core0_rev3[] = {
0x00000000,
};
-const u32 iq_lut_core1_rev3[] = {
+static const u32 iq_lut_core1_rev3[] = {
0x00000000,
0x00000000,
0x00000000,
@@ -8572,7 +8570,7 @@ const u32 iq_lut_core1_rev3[] = {
0x00000000,
};
-const u16 loft_lut_core0_rev3[] = {
+static const u16 loft_lut_core0_rev3[] = {
0x0000,
0x0000,
0x0000,
@@ -8703,7 +8701,7 @@ const u16 loft_lut_core0_rev3[] = {
0x0000,
};
-const u16 loft_lut_core1_rev3[] = {
+static const u16 loft_lut_core1_rev3[] = {
0x0000,
0x0000,
0x0000,
@@ -8834,7 +8832,7 @@ const u16 loft_lut_core1_rev3[] = {
0x0000,
};
-const u16 papd_comp_rfpwr_tbl_core0_rev3[] = {
+static const u16 papd_comp_rfpwr_tbl_core0_rev3[] = {
0x0036,
0x0036,
0x0036,
@@ -8965,7 +8963,7 @@ const u16 papd_comp_rfpwr_tbl_core0_rev3[] = {
0x01d6,
};
-const u16 papd_comp_rfpwr_tbl_core1_rev3[] = {
+static const u16 papd_comp_rfpwr_tbl_core1_rev3[] = {
0x0036,
0x0036,
0x0036,
@@ -9096,7 +9094,7 @@ const u16 papd_comp_rfpwr_tbl_core1_rev3[] = {
0x01d6,
};
-const u32 papd_comp_epsilon_tbl_core0_rev3[] = {
+static const u32 papd_comp_epsilon_tbl_core0_rev3[] = {
0x00000000,
0x00001fa0,
0x00019f78,
@@ -9163,7 +9161,7 @@ const u32 papd_comp_epsilon_tbl_core0_rev3[] = {
0x03e38ffe,
};
-const u32 papd_cal_scalars_tbl_core0_rev3[] = {
+static const u32 papd_cal_scalars_tbl_core0_rev3[] = {
0x05af005a,
0x0571005e,
0x05040066,
@@ -9230,7 +9228,7 @@ const u32 papd_cal_scalars_tbl_core0_rev3[] = {
0x002606a4,
};
-const u32 papd_comp_epsilon_tbl_core1_rev3[] = {
+static const u32 papd_comp_epsilon_tbl_core1_rev3[] = {
0x00000000,
0x00001fa0,
0x00019f78,
@@ -9297,7 +9295,7 @@ const u32 papd_comp_epsilon_tbl_core1_rev3[] = {
0x03e38ffe,
};
-const u32 papd_cal_scalars_tbl_core1_rev3[] = {
+static const u32 papd_cal_scalars_tbl_core1_rev3[] = {
0x05af005a,
0x0571005e,
0x05040066,
@@ -9364,34 +9362,34 @@ const u32 papd_cal_scalars_tbl_core1_rev3[] = {
0x002606a4,
};
-const mimophytbl_info_t mimophytbl_info_rev3_volatile[] = {
+const struct phytbl_info mimophytbl_info_rev3_volatile[] = {
{&ant_swctrl_tbl_rev3,
sizeof(ant_swctrl_tbl_rev3) / sizeof(ant_swctrl_tbl_rev3[0]), 9, 0, 16}
,
};
-const mimophytbl_info_t mimophytbl_info_rev3_volatile1[] = {
+const struct phytbl_info mimophytbl_info_rev3_volatile1[] = {
{&ant_swctrl_tbl_rev3_1,
sizeof(ant_swctrl_tbl_rev3_1) / sizeof(ant_swctrl_tbl_rev3_1[0]), 9, 0,
16}
,
};
-const mimophytbl_info_t mimophytbl_info_rev3_volatile2[] = {
+const struct phytbl_info mimophytbl_info_rev3_volatile2[] = {
{&ant_swctrl_tbl_rev3_2,
sizeof(ant_swctrl_tbl_rev3_2) / sizeof(ant_swctrl_tbl_rev3_2[0]), 9, 0,
16}
,
};
-const mimophytbl_info_t mimophytbl_info_rev3_volatile3[] = {
+const struct phytbl_info mimophytbl_info_rev3_volatile3[] = {
{&ant_swctrl_tbl_rev3_3,
sizeof(ant_swctrl_tbl_rev3_3) / sizeof(ant_swctrl_tbl_rev3_3[0]), 9, 0,
16}
,
};
-const mimophytbl_info_t mimophytbl_info_rev3[] = {
+const struct phytbl_info mimophytbl_info_rev3[] = {
{&frame_struct_rev3,
sizeof(frame_struct_rev3) / sizeof(frame_struct_rev3[0]), 10, 0, 32}
,
@@ -9478,21 +9476,21 @@ const mimophytbl_info_t mimophytbl_info_rev3[] = {
};
const u32 mimophytbl_info_sz_rev3 =
- sizeof(mimophytbl_info_rev3) / sizeof(mimophytbl_info_rev3[0]);
+ sizeof(mimophytbl_info_rev3) / sizeof(mimophytbl_info_rev3[0]);
const u32 mimophytbl_info_sz_rev3_volatile =
- sizeof(mimophytbl_info_rev3_volatile) /
- sizeof(mimophytbl_info_rev3_volatile[0]);
+ sizeof(mimophytbl_info_rev3_volatile) /
+ sizeof(mimophytbl_info_rev3_volatile[0]);
const u32 mimophytbl_info_sz_rev3_volatile1 =
- sizeof(mimophytbl_info_rev3_volatile1) /
- sizeof(mimophytbl_info_rev3_volatile1[0]);
+ sizeof(mimophytbl_info_rev3_volatile1) /
+ sizeof(mimophytbl_info_rev3_volatile1[0]);
const u32 mimophytbl_info_sz_rev3_volatile2 =
- sizeof(mimophytbl_info_rev3_volatile2) /
- sizeof(mimophytbl_info_rev3_volatile2[0]);
+ sizeof(mimophytbl_info_rev3_volatile2) /
+ sizeof(mimophytbl_info_rev3_volatile2[0]);
const u32 mimophytbl_info_sz_rev3_volatile3 =
- sizeof(mimophytbl_info_rev3_volatile3) /
- sizeof(mimophytbl_info_rev3_volatile3[0]);
+ sizeof(mimophytbl_info_rev3_volatile3) /
+ sizeof(mimophytbl_info_rev3_volatile3[0]);
-const u32 tmap_tbl_rev7[] = {
+static const u32 tmap_tbl_rev7[] = {
0x8a88aa80,
0x8aaaaa8a,
0x8a8a8aa8,
@@ -10202,7 +10200,7 @@ const u32 noise_var_tbl_rev7[] = {
0x0000014d,
};
-const u32 papd_comp_epsilon_tbl_core0_rev7[] = {
+static const u32 papd_comp_epsilon_tbl_core0_rev7[] = {
0x00000000,
0x00000000,
0x00016023,
@@ -10269,7 +10267,7 @@ const u32 papd_comp_epsilon_tbl_core0_rev7[] = {
0x0156cfff,
};
-const u32 papd_cal_scalars_tbl_core0_rev7[] = {
+static const u32 papd_cal_scalars_tbl_core0_rev7[] = {
0x0b5e002d,
0x0ae2002f,
0x0a3b0032,
@@ -10336,7 +10334,7 @@ const u32 papd_cal_scalars_tbl_core0_rev7[] = {
0x004e068c,
};
-const u32 papd_comp_epsilon_tbl_core1_rev7[] = {
+static const u32 papd_comp_epsilon_tbl_core1_rev7[] = {
0x00000000,
0x00000000,
0x00016023,
@@ -10403,7 +10401,7 @@ const u32 papd_comp_epsilon_tbl_core1_rev7[] = {
0x0156cfff,
};
-const u32 papd_cal_scalars_tbl_core1_rev7[] = {
+static const u32 papd_cal_scalars_tbl_core1_rev7[] = {
0x0b5e002d,
0x0ae2002f,
0x0a3b0032,
@@ -10470,7 +10468,7 @@ const u32 papd_cal_scalars_tbl_core1_rev7[] = {
0x004e068c,
};
-const mimophytbl_info_t mimophytbl_info_rev7[] = {
+const struct phytbl_info mimophytbl_info_rev7[] = {
{&frame_struct_rev3,
sizeof(frame_struct_rev3) / sizeof(frame_struct_rev3[0]), 10, 0, 32}
,
@@ -10582,9 +10580,9 @@ const mimophytbl_info_t mimophytbl_info_rev7[] = {
};
const u32 mimophytbl_info_sz_rev7 =
- sizeof(mimophytbl_info_rev7) / sizeof(mimophytbl_info_rev7[0]);
+ sizeof(mimophytbl_info_rev7) / sizeof(mimophytbl_info_rev7[0]);
-const mimophytbl_info_t mimophytbl_info_rev16[] = {
+const struct phytbl_info mimophytbl_info_rev16[] = {
{&noise_var_tbl_rev7,
sizeof(noise_var_tbl_rev7) / sizeof(noise_var_tbl_rev7[0]), 16, 0, 32}
,
@@ -10629,4 +10627,4 @@ const mimophytbl_info_t mimophytbl_info_rev16[] = {
};
const u32 mimophytbl_info_sz_rev16 =
- sizeof(mimophytbl_info_rev16) / sizeof(mimophytbl_info_rev16[0]);
+ sizeof(mimophytbl_info_rev16) / sizeof(mimophytbl_info_rev16[0]);
diff --git a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_n.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h
index 396122f5e50b..dc8a84e85117 100644
--- a/drivers/staging/brcm80211/brcmsmac/phy/wlc_phytbl_n.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h
@@ -16,24 +16,35 @@
#define ANT_SWCTRL_TBL_REV3_IDX (0)
-typedef phytbl_info_t mimophytbl_info_t;
+#include <types.h>
+#include "phy_int.h"
-extern const mimophytbl_info_t mimophytbl_info_rev0[],
- mimophytbl_info_rev0_volatile[];
-extern const u32 mimophytbl_info_sz_rev0, mimophytbl_info_sz_rev0_volatile;
+extern const struct phytbl_info mimophytbl_info_rev0[],
+ mimophytbl_info_rev0_volatile[];
-extern const mimophytbl_info_t mimophytbl_info_rev3[],
- mimophytbl_info_rev3_volatile[], mimophytbl_info_rev3_volatile1[],
- mimophytbl_info_rev3_volatile2[], mimophytbl_info_rev3_volatile3[];
-extern const u32 mimophytbl_info_sz_rev3, mimophytbl_info_sz_rev3_volatile,
- mimophytbl_info_sz_rev3_volatile1, mimophytbl_info_sz_rev3_volatile2,
- mimophytbl_info_sz_rev3_volatile3;
+extern const u32 mimophytbl_info_sz_rev0,
+ mimophytbl_info_sz_rev0_volatile;
+
+extern const struct phytbl_info mimophytbl_info_rev3[],
+ mimophytbl_info_rev3_volatile[],
+ mimophytbl_info_rev3_volatile1[],
+ mimophytbl_info_rev3_volatile2[],
+ mimophytbl_info_rev3_volatile3[];
+
+extern const u32 mimophytbl_info_sz_rev3,
+ mimophytbl_info_sz_rev3_volatile,
+ mimophytbl_info_sz_rev3_volatile1,
+ mimophytbl_info_sz_rev3_volatile2,
+ mimophytbl_info_sz_rev3_volatile3;
extern const u32 noise_var_tbl_rev3[];
-extern const mimophytbl_info_t mimophytbl_info_rev7[];
+extern const struct phytbl_info mimophytbl_info_rev7[];
+
extern const u32 mimophytbl_info_sz_rev7;
+
extern const u32 noise_var_tbl_rev7[];
-extern const mimophytbl_info_t mimophytbl_info_rev16[];
+extern const struct phytbl_info mimophytbl_info_rev16[];
+
extern const u32 mimophytbl_info_sz_rev16;
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c b/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c
new file mode 100644
index 000000000000..5926854f62e2
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This is "two-way" interface, acting as the SHIM layer between driver
+ * and PHY layer. The driver can optionally call this translation layer
+ * to do some preprocessing, then reach PHY. On the PHY->driver direction,
+ * all calls go through this layer since PHY doesn't have access to the
+ * driver's brcms_hardware pointer.
+ */
+#include <linux/slab.h>
+#include <net/mac80211.h>
+
+#include "main.h"
+#include "mac80211_if.h"
+#include "phy_shim.h"
+
+/* PHY SHIM module specific state */
+struct phy_shim_info {
+ struct brcms_hardware *wlc_hw; /* pointer to main wlc_hw structure */
+ struct brcms_c_info *wlc; /* pointer to main wlc structure */
+ struct brcms_info *wl; /* pointer to os-specific private state */
+};
+
+struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw,
+ struct brcms_info *wl,
+ struct brcms_c_info *wlc) {
+ struct phy_shim_info *physhim = NULL;
+
+ physhim = kzalloc(sizeof(struct phy_shim_info), GFP_ATOMIC);
+ if (!physhim)
+ return NULL;
+
+ physhim->wlc_hw = wlc_hw;
+ physhim->wlc = wlc;
+ physhim->wl = wl;
+
+ return physhim;
+}
+
+void wlc_phy_shim_detach(struct phy_shim_info *physhim)
+{
+ kfree(physhim);
+}
+
+struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim,
+ void (*fn)(struct brcms_phy *pi),
+ void *arg, const char *name)
+{
+ return (struct wlapi_timer *)
+ brcms_init_timer(physhim->wl, (void (*)(void *))fn,
+ arg, name);
+}
+
+void wlapi_free_timer(struct wlapi_timer *t)
+{
+ brcms_free_timer((struct brcms_timer *)t);
+}
+
+void
+wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic)
+{
+ brcms_add_timer((struct brcms_timer *)t, ms, periodic);
+}
+
+bool wlapi_del_timer(struct wlapi_timer *t)
+{
+ return brcms_del_timer((struct brcms_timer *)t);
+}
+
+void wlapi_intrson(struct phy_shim_info *physhim)
+{
+ brcms_intrson(physhim->wl);
+}
+
+u32 wlapi_intrsoff(struct phy_shim_info *physhim)
+{
+ return brcms_intrsoff(physhim->wl);
+}
+
+void wlapi_intrsrestore(struct phy_shim_info *physhim, u32 macintmask)
+{
+ brcms_intrsrestore(physhim->wl, macintmask);
+}
+
+void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, u16 v)
+{
+ brcms_b_write_shm(physhim->wlc_hw, offset, v);
+}
+
+u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset)
+{
+ return brcms_b_read_shm(physhim->wlc_hw, offset);
+}
+
+void
+wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, u16 mask,
+ u16 val, int bands)
+{
+ brcms_b_mhf(physhim->wlc_hw, idx, mask, val, bands);
+}
+
+void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags)
+{
+ brcms_b_corereset(physhim->wlc_hw, flags);
+}
+
+void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim)
+{
+ brcms_c_suspend_mac_and_wait(physhim->wlc);
+}
+
+void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode)
+{
+ brcms_b_switch_macfreq(physhim->wlc_hw, spurmode);
+}
+
+void wlapi_enable_mac(struct phy_shim_info *physhim)
+{
+ brcms_c_enable_mac(physhim->wlc);
+}
+
+void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, u32 val)
+{
+ brcms_b_mctrl(physhim->wlc_hw, mask, val);
+}
+
+void wlapi_bmac_phy_reset(struct phy_shim_info *physhim)
+{
+ brcms_b_phy_reset(physhim->wlc_hw);
+}
+
+void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw)
+{
+ brcms_b_bw_set(physhim->wlc_hw, bw);
+}
+
+u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim)
+{
+ return brcms_b_get_txant(physhim->wlc_hw);
+}
+
+void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk)
+{
+ brcms_b_phyclk_fgc(physhim->wlc_hw, clk);
+}
+
+void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk)
+{
+ brcms_b_macphyclk_set(physhim->wlc_hw, clk);
+}
+
+void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on)
+{
+ brcms_b_core_phypll_ctl(physhim->wlc_hw, on);
+}
+
+void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim)
+{
+ brcms_b_core_phypll_reset(physhim->wlc_hw);
+}
+
+void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *physhim)
+{
+ brcms_c_ucode_wake_override_set(physhim->wlc_hw,
+ BRCMS_WAKE_OVERRIDE_PHYREG);
+}
+
+void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *physhim)
+{
+ brcms_c_ucode_wake_override_clear(physhim->wlc_hw,
+ BRCMS_WAKE_OVERRIDE_PHYREG);
+}
+
+void
+wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int offset,
+ int len, void *buf)
+{
+ brcms_b_write_template_ram(physhim->wlc_hw, offset, len, buf);
+}
+
+u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, u8 rate)
+{
+ return brcms_b_rate_shm_offset(physhim->wlc_hw, rate);
+}
+
+void wlapi_ucode_sample_init(struct phy_shim_info *physhim)
+{
+}
+
+void
+wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint offset, void *buf,
+ int len, u32 sel)
+{
+ brcms_b_copyfrom_objmem(physhim->wlc_hw, offset, buf, len, sel);
+}
+
+void
+wlapi_copyto_objmem(struct phy_shim_info *physhim, uint offset, const void *buf,
+ int l, u32 sel)
+{
+ brcms_b_copyto_objmem(physhim->wlc_hw, offset, buf, l, sel);
+}
+
+char *wlapi_getvar(struct phy_shim_info *physhim, enum brcms_srom_id id)
+{
+ return getvar(physhim->wlc_hw->sih, id);
+}
+int wlapi_getintvar(struct phy_shim_info *physhim, enum brcms_srom_id id)
+{
+ return getintvar(physhim->wlc_hw->sih, id);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h b/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h
new file mode 100644
index 000000000000..9168c459b185
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * phy_shim.h: stuff defined in phy_shim.c and included only by the phy
+ */
+
+#ifndef _BRCM_PHY_SHIM_H_
+#define _BRCM_PHY_SHIM_H_
+
+#include "types.h"
+
+#define RADAR_TYPE_NONE 0 /* Radar type None */
+#define RADAR_TYPE_ETSI_1 1 /* ETSI 1 Radar type */
+#define RADAR_TYPE_ETSI_2 2 /* ETSI 2 Radar type */
+#define RADAR_TYPE_ETSI_3 3 /* ETSI 3 Radar type */
+#define RADAR_TYPE_ITU_E 4 /* ITU E Radar type */
+#define RADAR_TYPE_ITU_K 5 /* ITU K Radar type */
+#define RADAR_TYPE_UNCLASSIFIED 6 /* Unclassified Radar type */
+#define RADAR_TYPE_BIN5 7 /* long pulse radar type */
+#define RADAR_TYPE_STG2 8 /* staggered-2 radar */
+#define RADAR_TYPE_STG3 9 /* staggered-3 radar */
+#define RADAR_TYPE_FRA 10 /* French radar */
+
+/* French radar pulse widths */
+#define FRA_T1_20MHZ 52770
+#define FRA_T2_20MHZ 61538
+#define FRA_T3_20MHZ 66002
+#define FRA_T1_40MHZ 105541
+#define FRA_T2_40MHZ 123077
+#define FRA_T3_40MHZ 132004
+#define FRA_ERR_20MHZ 60
+#define FRA_ERR_40MHZ 120
+
+#define ANTSEL_NA 0 /* No boardlevel selection available */
+#define ANTSEL_2x4 1 /* 2x4 boardlevel selection available */
+#define ANTSEL_2x3 2 /* 2x3 CB2 boardlevel selection available */
+
+/* Rx Antenna diversity control values */
+#define ANT_RX_DIV_FORCE_0 0 /* Use antenna 0 */
+#define ANT_RX_DIV_FORCE_1 1 /* Use antenna 1 */
+#define ANT_RX_DIV_START_1 2 /* Choose starting with 1 */
+#define ANT_RX_DIV_START_0 3 /* Choose starting with 0 */
+#define ANT_RX_DIV_ENABLE 3 /* APHY bbConfig Enable RX Diversity */
+#define ANT_RX_DIV_DEF ANT_RX_DIV_START_0 /* default antdiv setting */
+
+#define WL_ANT_RX_MAX 2 /* max 2 receive antennas */
+#define WL_ANT_HT_RX_MAX 3 /* max 3 receive antennas/cores */
+#define WL_ANT_IDX_1 0 /* antenna index 1 */
+#define WL_ANT_IDX_2 1 /* antenna index 2 */
+
+/* values for n_preamble_type */
+#define BRCMS_N_PREAMBLE_MIXEDMODE 0
+#define BRCMS_N_PREAMBLE_GF 1
+#define BRCMS_N_PREAMBLE_GF_BRCM 2
+
+#define WL_TX_POWER_RATES_LEGACY 45
+#define WL_TX_POWER_MCS20_FIRST 12
+#define WL_TX_POWER_MCS20_NUM 16
+#define WL_TX_POWER_MCS40_FIRST 28
+#define WL_TX_POWER_MCS40_NUM 17
+
+
+#define WL_TX_POWER_RATES 101
+#define WL_TX_POWER_CCK_FIRST 0
+#define WL_TX_POWER_CCK_NUM 4
+/* Index for first 20MHz OFDM SISO rate */
+#define WL_TX_POWER_OFDM_FIRST 4
+/* Index for first 20MHz OFDM CDD rate */
+#define WL_TX_POWER_OFDM20_CDD_FIRST 12
+/* Index for first 40MHz OFDM SISO rate */
+#define WL_TX_POWER_OFDM40_SISO_FIRST 52
+/* Index for first 40MHz OFDM CDD rate */
+#define WL_TX_POWER_OFDM40_CDD_FIRST 60
+#define WL_TX_POWER_OFDM_NUM 8
+/* Index for first 20MHz MCS SISO rate */
+#define WL_TX_POWER_MCS20_SISO_FIRST 20
+/* Index for first 20MHz MCS CDD rate */
+#define WL_TX_POWER_MCS20_CDD_FIRST 28
+/* Index for first 20MHz MCS STBC rate */
+#define WL_TX_POWER_MCS20_STBC_FIRST 36
+/* Index for first 20MHz MCS SDM rate */
+#define WL_TX_POWER_MCS20_SDM_FIRST 44
+/* Index for first 40MHz MCS SISO rate */
+#define WL_TX_POWER_MCS40_SISO_FIRST 68
+/* Index for first 40MHz MCS CDD rate */
+#define WL_TX_POWER_MCS40_CDD_FIRST 76
+/* Index for first 40MHz MCS STBC rate */
+#define WL_TX_POWER_MCS40_STBC_FIRST 84
+/* Index for first 40MHz MCS SDM rate */
+#define WL_TX_POWER_MCS40_SDM_FIRST 92
+#define WL_TX_POWER_MCS_1_STREAM_NUM 8
+#define WL_TX_POWER_MCS_2_STREAM_NUM 8
+/* Index for 40MHz rate MCS 32 */
+#define WL_TX_POWER_MCS_32 100
+#define WL_TX_POWER_MCS_32_NUM 1
+
+/* sslpnphy specifics */
+/* Index for first 20MHz MCS SISO rate */
+#define WL_TX_POWER_MCS20_SISO_FIRST_SSN 12
+
+/* struct tx_power::flags bits */
+#define WL_TX_POWER_F_ENABLED 1
+#define WL_TX_POWER_F_HW 2
+#define WL_TX_POWER_F_MIMO 4
+#define WL_TX_POWER_F_SISO 8
+
+/* values to force tx/rx chain */
+#define BRCMS_N_TXRX_CHAIN0 0
+#define BRCMS_N_TXRX_CHAIN1 1
+
+struct brcms_phy;
+
+extern struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw,
+ struct brcms_info *wl,
+ struct brcms_c_info *wlc);
+extern void wlc_phy_shim_detach(struct phy_shim_info *physhim);
+
+/* PHY to WL utility functions */
+extern struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim,
+ void (*fn) (struct brcms_phy *pi),
+ void *arg, const char *name);
+extern void wlapi_free_timer(struct wlapi_timer *t);
+extern void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic);
+extern bool wlapi_del_timer(struct wlapi_timer *t);
+extern void wlapi_intrson(struct phy_shim_info *physhim);
+extern u32 wlapi_intrsoff(struct phy_shim_info *physhim);
+extern void wlapi_intrsrestore(struct phy_shim_info *physhim,
+ u32 macintmask);
+
+extern void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset,
+ u16 v);
+extern u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset);
+extern void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx,
+ u16 mask, u16 val, int bands);
+extern void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags);
+extern void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim);
+extern void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode);
+extern void wlapi_enable_mac(struct phy_shim_info *physhim);
+extern void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask,
+ u32 val);
+extern void wlapi_bmac_phy_reset(struct phy_shim_info *physhim);
+extern void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw);
+extern void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk);
+extern void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk);
+extern void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on);
+extern void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim);
+extern void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *
+ physhim);
+extern void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *
+ physhim);
+extern void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int o,
+ int len, void *buf);
+extern u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim,
+ u8 rate);
+extern void wlapi_ucode_sample_init(struct phy_shim_info *physhim);
+extern void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint,
+ void *buf, int, u32 sel);
+extern void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint,
+ const void *buf, int, u32);
+
+extern void wlapi_high_update_phy_mode(struct phy_shim_info *physhim,
+ u32 phy_mode);
+extern u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim);
+extern char *wlapi_getvar(struct phy_shim_info *physhim, enum brcms_srom_id id);
+extern int wlapi_getintvar(struct phy_shim_info *physhim,
+ enum brcms_srom_id id);
+
+#endif /* _BRCM_PHY_SHIM_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.c b/drivers/net/wireless/brcm80211/brcmsmac/pmu.c
new file mode 100644
index 000000000000..3b36e3acfd74
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/pmu.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <brcm_hw_ids.h>
+#include <chipcommon.h>
+#include <brcmu_utils.h>
+#include "pub.h"
+#include "aiutils.h"
+#include "pmu.h"
+
+/*
+ * external LPO crystal frequency
+ */
+#define EXT_ILP_HZ 32768
+
+/*
+ * Duration for ILP clock frequency measurment in milliseconds
+ *
+ * remark: 1000 must be an integer multiple of this duration
+ */
+#define ILP_CALC_DUR 10
+
+/* Fields in pmucontrol */
+#define PCTL_ILP_DIV_MASK 0xffff0000
+#define PCTL_ILP_DIV_SHIFT 16
+#define PCTL_PLL_PLLCTL_UPD 0x00000400 /* rev 2 */
+#define PCTL_NOILP_ON_WAIT 0x00000200 /* rev 1 */
+#define PCTL_HT_REQ_EN 0x00000100
+#define PCTL_ALP_REQ_EN 0x00000080
+#define PCTL_XTALFREQ_MASK 0x0000007c
+#define PCTL_XTALFREQ_SHIFT 2
+#define PCTL_ILP_DIV_EN 0x00000002
+#define PCTL_LPO_SEL 0x00000001
+
+/* ILP clock */
+#define ILP_CLOCK 32000
+
+/* ALP clock on pre-PMU chips */
+#define ALP_CLOCK 20000000
+
+/* pmustatus */
+#define PST_EXTLPOAVAIL 0x0100
+#define PST_WDRESET 0x0080
+#define PST_INTPEND 0x0040
+#define PST_SBCLKST 0x0030
+#define PST_SBCLKST_ILP 0x0010
+#define PST_SBCLKST_ALP 0x0020
+#define PST_SBCLKST_HT 0x0030
+#define PST_ALPAVAIL 0x0008
+#define PST_HTAVAIL 0x0004
+#define PST_RESINIT 0x0003
+
+/* PMU resource bit position */
+#define PMURES_BIT(bit) (1 << (bit))
+
+/* PMU corerev and chip specific PLL controls.
+ * PMU<rev>_PLL<num>_XX where <rev> is PMU corerev and <num> is an arbitrary
+ * number to differentiate different PLLs controlled by the same PMU rev.
+ */
+/* pllcontrol registers:
+ * ndiv_pwrdn, pwrdn_ch<x>, refcomp_pwrdn, dly_ch<x>,
+ * p1div, p2div, _bypass_sdmod
+ */
+#define PMU1_PLL0_PLLCTL0 0
+#define PMU1_PLL0_PLLCTL1 1
+#define PMU1_PLL0_PLLCTL2 2
+#define PMU1_PLL0_PLLCTL3 3
+#define PMU1_PLL0_PLLCTL4 4
+#define PMU1_PLL0_PLLCTL5 5
+
+/* pmu XtalFreqRatio */
+#define PMU_XTALFREQ_REG_ILPCTR_MASK 0x00001FFF
+#define PMU_XTALFREQ_REG_MEASURE_MASK 0x80000000
+#define PMU_XTALFREQ_REG_MEASURE_SHIFT 31
+
+/* 4313 resources */
+#define RES4313_BB_PU_RSRC 0
+#define RES4313_ILP_REQ_RSRC 1
+#define RES4313_XTAL_PU_RSRC 2
+#define RES4313_ALP_AVAIL_RSRC 3
+#define RES4313_RADIO_PU_RSRC 4
+#define RES4313_BG_PU_RSRC 5
+#define RES4313_VREG1P4_PU_RSRC 6
+#define RES4313_AFE_PWRSW_RSRC 7
+#define RES4313_RX_PWRSW_RSRC 8
+#define RES4313_TX_PWRSW_RSRC 9
+#define RES4313_BB_PWRSW_RSRC 10
+#define RES4313_SYNTH_PWRSW_RSRC 11
+#define RES4313_MISC_PWRSW_RSRC 12
+#define RES4313_BB_PLL_PWRSW_RSRC 13
+#define RES4313_HT_AVAIL_RSRC 14
+#define RES4313_MACPHY_CLK_AVAIL_RSRC 15
+
+/* Determine min/max rsrc masks. Value 0 leaves hardware at default. */
+static void si_pmu_res_masks(struct si_pub *sih, u32 * pmin, u32 * pmax)
+{
+ u32 min_mask = 0, max_mask = 0;
+ uint rsrcs;
+
+ /* # resources */
+ rsrcs = (sih->pmucaps & PCAP_RC_MASK) >> PCAP_RC_SHIFT;
+
+ /* determine min/max rsrc masks */
+ switch (sih->chip) {
+ case BCM43224_CHIP_ID:
+ case BCM43225_CHIP_ID:
+ /* ??? */
+ break;
+
+ case BCM4313_CHIP_ID:
+ min_mask = PMURES_BIT(RES4313_BB_PU_RSRC) |
+ PMURES_BIT(RES4313_XTAL_PU_RSRC) |
+ PMURES_BIT(RES4313_ALP_AVAIL_RSRC) |
+ PMURES_BIT(RES4313_BB_PLL_PWRSW_RSRC);
+ max_mask = 0xffff;
+ break;
+ default:
+ break;
+ }
+
+ *pmin = min_mask;
+ *pmax = max_mask;
+}
+
+static void
+si_pmu_spuravoid_pllupdate(struct si_pub *sih, struct chipcregs __iomem *cc,
+ u8 spuravoid)
+{
+ u32 tmp = 0;
+
+ switch (sih->chip) {
+ case BCM43224_CHIP_ID:
+ case BCM43225_CHIP_ID:
+ if (spuravoid == 1) {
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL0);
+ W_REG(&cc->pllcontrol_data, 0x11500010);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL1);
+ W_REG(&cc->pllcontrol_data, 0x000C0C06);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL2);
+ W_REG(&cc->pllcontrol_data, 0x0F600a08);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL3);
+ W_REG(&cc->pllcontrol_data, 0x00000000);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL4);
+ W_REG(&cc->pllcontrol_data, 0x2001E920);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL5);
+ W_REG(&cc->pllcontrol_data, 0x88888815);
+ } else {
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL0);
+ W_REG(&cc->pllcontrol_data, 0x11100010);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL1);
+ W_REG(&cc->pllcontrol_data, 0x000c0c06);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL2);
+ W_REG(&cc->pllcontrol_data, 0x03000a08);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL3);
+ W_REG(&cc->pllcontrol_data, 0x00000000);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL4);
+ W_REG(&cc->pllcontrol_data, 0x200005c0);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL5);
+ W_REG(&cc->pllcontrol_data, 0x88888815);
+ }
+ tmp = 1 << 10;
+ break;
+
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL0);
+ W_REG(&cc->pllcontrol_data, 0x11100008);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL1);
+ W_REG(&cc->pllcontrol_data, 0x0c000c06);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL2);
+ W_REG(&cc->pllcontrol_data, 0x03000a08);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL3);
+ W_REG(&cc->pllcontrol_data, 0x00000000);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL4);
+ W_REG(&cc->pllcontrol_data, 0x200005c0);
+ W_REG(&cc->pllcontrol_addr, PMU1_PLL0_PLLCTL5);
+ W_REG(&cc->pllcontrol_data, 0x88888855);
+
+ tmp = 1 << 10;
+ break;
+
+ default:
+ /* bail out */
+ return;
+ }
+
+ tmp |= R_REG(&cc->pmucontrol);
+ W_REG(&cc->pmucontrol, tmp);
+}
+
+u16 si_pmu_fast_pwrup_delay(struct si_pub *sih)
+{
+ uint delay = PMU_MAX_TRANSITION_DLY;
+
+ switch (sih->chip) {
+ case BCM43224_CHIP_ID:
+ case BCM43225_CHIP_ID:
+ case BCM4313_CHIP_ID:
+ delay = 3700;
+ break;
+ default:
+ break;
+ }
+
+ return (u16) delay;
+}
+
+void si_pmu_sprom_enable(struct si_pub *sih, bool enable)
+{
+ struct chipcregs __iomem *cc;
+ uint origidx;
+
+ /* Remember original core before switch to chipc */
+ origidx = ai_coreidx(sih);
+ cc = ai_setcoreidx(sih, SI_CC_IDX);
+
+ /* Return to original core */
+ ai_setcoreidx(sih, origidx);
+}
+
+/* Read/write a chipcontrol reg */
+u32 si_pmu_chipcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val)
+{
+ ai_corereg(sih, SI_CC_IDX, offsetof(struct chipcregs, chipcontrol_addr),
+ ~0, reg);
+ return ai_corereg(sih, SI_CC_IDX,
+ offsetof(struct chipcregs, chipcontrol_data), mask,
+ val);
+}
+
+/* Read/write a regcontrol reg */
+u32 si_pmu_regcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val)
+{
+ ai_corereg(sih, SI_CC_IDX, offsetof(struct chipcregs, regcontrol_addr),
+ ~0, reg);
+ return ai_corereg(sih, SI_CC_IDX,
+ offsetof(struct chipcregs, regcontrol_data), mask,
+ val);
+}
+
+/* Read/write a pllcontrol reg */
+u32 si_pmu_pllcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val)
+{
+ ai_corereg(sih, SI_CC_IDX, offsetof(struct chipcregs, pllcontrol_addr),
+ ~0, reg);
+ return ai_corereg(sih, SI_CC_IDX,
+ offsetof(struct chipcregs, pllcontrol_data), mask,
+ val);
+}
+
+/* PMU PLL update */
+void si_pmu_pllupd(struct si_pub *sih)
+{
+ ai_corereg(sih, SI_CC_IDX, offsetof(struct chipcregs, pmucontrol),
+ PCTL_PLL_PLLCTL_UPD, PCTL_PLL_PLLCTL_UPD);
+}
+
+/* query alp/xtal clock frequency */
+u32 si_pmu_alp_clock(struct si_pub *sih)
+{
+ u32 clock = ALP_CLOCK;
+
+ /* bail out with default */
+ if (!(sih->cccaps & CC_CAP_PMU))
+ return clock;
+
+ switch (sih->chip) {
+ case BCM43224_CHIP_ID:
+ case BCM43225_CHIP_ID:
+ case BCM4313_CHIP_ID:
+ /* always 20Mhz */
+ clock = 20000 * 1000;
+ break;
+ default:
+ break;
+ }
+
+ return clock;
+}
+
+void si_pmu_spuravoid(struct si_pub *sih, u8 spuravoid)
+{
+ struct chipcregs __iomem *cc;
+ uint origidx, intr_val;
+
+ /* Remember original core before switch to chipc */
+ cc = (struct chipcregs __iomem *)
+ ai_switch_core(sih, CC_CORE_ID, &origidx, &intr_val);
+
+ /* update the pll changes */
+ si_pmu_spuravoid_pllupdate(sih, cc, spuravoid);
+
+ /* Return to original core */
+ ai_restore_core(sih, origidx, intr_val);
+}
+
+/* initialize PMU */
+void si_pmu_init(struct si_pub *sih)
+{
+ struct chipcregs __iomem *cc;
+ uint origidx;
+
+ /* Remember original core before switch to chipc */
+ origidx = ai_coreidx(sih);
+ cc = ai_setcoreidx(sih, SI_CC_IDX);
+
+ if (sih->pmurev == 1)
+ AND_REG(&cc->pmucontrol, ~PCTL_NOILP_ON_WAIT);
+ else if (sih->pmurev >= 2)
+ OR_REG(&cc->pmucontrol, PCTL_NOILP_ON_WAIT);
+
+ /* Return to original core */
+ ai_setcoreidx(sih, origidx);
+}
+
+/* initialize PMU chip controls and other chip level stuff */
+void si_pmu_chip_init(struct si_pub *sih)
+{
+ uint origidx;
+
+ /* Gate off SPROM clock and chip select signals */
+ si_pmu_sprom_enable(sih, false);
+
+ /* Remember original core */
+ origidx = ai_coreidx(sih);
+
+ /* Return to original core */
+ ai_setcoreidx(sih, origidx);
+}
+
+/* initialize PMU switch/regulators */
+void si_pmu_swreg_init(struct si_pub *sih)
+{
+}
+
+/* initialize PLL */
+void si_pmu_pll_init(struct si_pub *sih, uint xtalfreq)
+{
+ struct chipcregs __iomem *cc;
+ uint origidx;
+
+ /* Remember original core before switch to chipc */
+ origidx = ai_coreidx(sih);
+ cc = ai_setcoreidx(sih, SI_CC_IDX);
+
+ switch (sih->chip) {
+ case BCM4313_CHIP_ID:
+ case BCM43224_CHIP_ID:
+ case BCM43225_CHIP_ID:
+ /* ??? */
+ break;
+ default:
+ break;
+ }
+
+ /* Return to original core */
+ ai_setcoreidx(sih, origidx);
+}
+
+/* initialize PMU resources */
+void si_pmu_res_init(struct si_pub *sih)
+{
+ struct chipcregs __iomem *cc;
+ uint origidx;
+ u32 min_mask = 0, max_mask = 0;
+
+ /* Remember original core before switch to chipc */
+ origidx = ai_coreidx(sih);
+ cc = ai_setcoreidx(sih, SI_CC_IDX);
+
+ /* Determine min/max rsrc masks */
+ si_pmu_res_masks(sih, &min_mask, &max_mask);
+
+ /* It is required to program max_mask first and then min_mask */
+
+ /* Program max resource mask */
+
+ if (max_mask)
+ W_REG(&cc->max_res_mask, max_mask);
+
+ /* Program min resource mask */
+
+ if (min_mask)
+ W_REG(&cc->min_res_mask, min_mask);
+
+ /* Add some delay; allow resources to come up and settle. */
+ mdelay(2);
+
+ /* Return to original core */
+ ai_setcoreidx(sih, origidx);
+}
+
+u32 si_pmu_measure_alpclk(struct si_pub *sih)
+{
+ struct chipcregs __iomem *cc;
+ uint origidx;
+ u32 alp_khz;
+
+ if (sih->pmurev < 10)
+ return 0;
+
+ /* Remember original core before switch to chipc */
+ origidx = ai_coreidx(sih);
+ cc = ai_setcoreidx(sih, SI_CC_IDX);
+
+ if (R_REG(&cc->pmustatus) & PST_EXTLPOAVAIL) {
+ u32 ilp_ctr, alp_hz;
+
+ /*
+ * Enable the reg to measure the freq,
+ * in case it was disabled before
+ */
+ W_REG(&cc->pmu_xtalfreq,
+ 1U << PMU_XTALFREQ_REG_MEASURE_SHIFT);
+
+ /* Delay for well over 4 ILP clocks */
+ udelay(1000);
+
+ /* Read the latched number of ALP ticks per 4 ILP ticks */
+ ilp_ctr =
+ R_REG(&cc->pmu_xtalfreq) & PMU_XTALFREQ_REG_ILPCTR_MASK;
+
+ /*
+ * Turn off the PMU_XTALFREQ_REG_MEASURE_SHIFT
+ * bit to save power
+ */
+ W_REG(&cc->pmu_xtalfreq, 0);
+
+ /* Calculate ALP frequency */
+ alp_hz = (ilp_ctr * EXT_ILP_HZ) / 4;
+
+ /*
+ * Round to nearest 100KHz, and at
+ * the same time convert to KHz
+ */
+ alp_khz = (alp_hz + 50000) / 100000 * 100;
+ } else
+ alp_khz = 0;
+
+ /* Return to original core */
+ ai_setcoreidx(sih, origidx);
+
+ return alp_khz;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h b/drivers/net/wireless/brcm80211/brcmsmac/pmu.h
new file mode 100644
index 000000000000..3a08c620640e
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/pmu.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef _BRCM_PMU_H_
+#define _BRCM_PMU_H_
+
+#include "types.h"
+
+extern u16 si_pmu_fast_pwrup_delay(struct si_pub *sih);
+extern void si_pmu_sprom_enable(struct si_pub *sih, bool enable);
+extern u32 si_pmu_chipcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val);
+extern u32 si_pmu_regcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val);
+extern u32 si_pmu_alp_clock(struct si_pub *sih);
+extern void si_pmu_pllupd(struct si_pub *sih);
+extern void si_pmu_spuravoid(struct si_pub *sih, u8 spuravoid);
+extern u32 si_pmu_pllcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val);
+extern void si_pmu_init(struct si_pub *sih);
+extern void si_pmu_chip_init(struct si_pub *sih);
+extern void si_pmu_pll_init(struct si_pub *sih, u32 xtalfreq);
+extern void si_pmu_res_init(struct si_pub *sih);
+extern void si_pmu_swreg_init(struct si_pub *sih);
+extern u32 si_pmu_measure_alpclk(struct si_pub *sih);
+
+#endif /* _BRCM_PMU_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pub.h b/drivers/net/wireless/brcm80211/brcmsmac/pub.h
new file mode 100644
index 000000000000..37bb2dcc113f
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/pub.h
@@ -0,0 +1,634 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_PUB_H_
+#define _BRCM_PUB_H_
+
+#include <brcmu_wifi.h>
+#include "types.h"
+#include "defs.h"
+
+enum brcms_srom_id {
+ BRCMS_SROM_NULL,
+ BRCMS_SROM_CONT,
+ BRCMS_SROM_AA2G,
+ BRCMS_SROM_AA5G,
+ BRCMS_SROM_AG0,
+ BRCMS_SROM_AG1,
+ BRCMS_SROM_AG2,
+ BRCMS_SROM_AG3,
+ BRCMS_SROM_ANTSWCTL2G,
+ BRCMS_SROM_ANTSWCTL5G,
+ BRCMS_SROM_ANTSWITCH,
+ BRCMS_SROM_BOARDFLAGS2,
+ BRCMS_SROM_BOARDFLAGS,
+ BRCMS_SROM_BOARDNUM,
+ BRCMS_SROM_BOARDREV,
+ BRCMS_SROM_BOARDTYPE,
+ BRCMS_SROM_BW40PO,
+ BRCMS_SROM_BWDUPPO,
+ BRCMS_SROM_BXA2G,
+ BRCMS_SROM_BXA5G,
+ BRCMS_SROM_CC,
+ BRCMS_SROM_CCK2GPO,
+ BRCMS_SROM_CCKBW202GPO,
+ BRCMS_SROM_CCKBW20UL2GPO,
+ BRCMS_SROM_CCODE,
+ BRCMS_SROM_CDDPO,
+ BRCMS_SROM_DEVID,
+ BRCMS_SROM_ET1MACADDR,
+ BRCMS_SROM_EXTPAGAIN2G,
+ BRCMS_SROM_EXTPAGAIN5G,
+ BRCMS_SROM_FREQOFFSET_CORR,
+ BRCMS_SROM_HW_IQCAL_EN,
+ BRCMS_SROM_IL0MACADDR,
+ BRCMS_SROM_IQCAL_SWP_DIS,
+ BRCMS_SROM_LEDBH0,
+ BRCMS_SROM_LEDBH1,
+ BRCMS_SROM_LEDBH2,
+ BRCMS_SROM_LEDBH3,
+ BRCMS_SROM_LEDDC,
+ BRCMS_SROM_LEGOFDM40DUPPO,
+ BRCMS_SROM_LEGOFDMBW202GPO,
+ BRCMS_SROM_LEGOFDMBW205GHPO,
+ BRCMS_SROM_LEGOFDMBW205GLPO,
+ BRCMS_SROM_LEGOFDMBW205GMPO,
+ BRCMS_SROM_LEGOFDMBW20UL2GPO,
+ BRCMS_SROM_LEGOFDMBW20UL5GHPO,
+ BRCMS_SROM_LEGOFDMBW20UL5GLPO,
+ BRCMS_SROM_LEGOFDMBW20UL5GMPO,
+ BRCMS_SROM_MACADDR,
+ BRCMS_SROM_MCS2GPO0,
+ BRCMS_SROM_MCS2GPO1,
+ BRCMS_SROM_MCS2GPO2,
+ BRCMS_SROM_MCS2GPO3,
+ BRCMS_SROM_MCS2GPO4,
+ BRCMS_SROM_MCS2GPO5,
+ BRCMS_SROM_MCS2GPO6,
+ BRCMS_SROM_MCS2GPO7,
+ BRCMS_SROM_MCS32PO,
+ BRCMS_SROM_MCS5GHPO0,
+ BRCMS_SROM_MCS5GHPO1,
+ BRCMS_SROM_MCS5GHPO2,
+ BRCMS_SROM_MCS5GHPO3,
+ BRCMS_SROM_MCS5GHPO4,
+ BRCMS_SROM_MCS5GHPO5,
+ BRCMS_SROM_MCS5GHPO6,
+ BRCMS_SROM_MCS5GHPO7,
+ BRCMS_SROM_MCS5GLPO0,
+ BRCMS_SROM_MCS5GLPO1,
+ BRCMS_SROM_MCS5GLPO2,
+ BRCMS_SROM_MCS5GLPO3,
+ BRCMS_SROM_MCS5GLPO4,
+ BRCMS_SROM_MCS5GLPO5,
+ BRCMS_SROM_MCS5GLPO6,
+ BRCMS_SROM_MCS5GLPO7,
+ BRCMS_SROM_MCS5GPO0,
+ BRCMS_SROM_MCS5GPO1,
+ BRCMS_SROM_MCS5GPO2,
+ BRCMS_SROM_MCS5GPO3,
+ BRCMS_SROM_MCS5GPO4,
+ BRCMS_SROM_MCS5GPO5,
+ BRCMS_SROM_MCS5GPO6,
+ BRCMS_SROM_MCS5GPO7,
+ BRCMS_SROM_MCSBW202GPO,
+ BRCMS_SROM_MCSBW205GHPO,
+ BRCMS_SROM_MCSBW205GLPO,
+ BRCMS_SROM_MCSBW205GMPO,
+ BRCMS_SROM_MCSBW20UL2GPO,
+ BRCMS_SROM_MCSBW20UL5GHPO,
+ BRCMS_SROM_MCSBW20UL5GLPO,
+ BRCMS_SROM_MCSBW20UL5GMPO,
+ BRCMS_SROM_MCSBW402GPO,
+ BRCMS_SROM_MCSBW405GHPO,
+ BRCMS_SROM_MCSBW405GLPO,
+ BRCMS_SROM_MCSBW405GMPO,
+ BRCMS_SROM_MEASPOWER,
+ BRCMS_SROM_OFDM2GPO,
+ BRCMS_SROM_OFDM5GHPO,
+ BRCMS_SROM_OFDM5GLPO,
+ BRCMS_SROM_OFDM5GPO,
+ BRCMS_SROM_OPO,
+ BRCMS_SROM_PA0B0,
+ BRCMS_SROM_PA0B1,
+ BRCMS_SROM_PA0B2,
+ BRCMS_SROM_PA0ITSSIT,
+ BRCMS_SROM_PA0MAXPWR,
+ BRCMS_SROM_PA1B0,
+ BRCMS_SROM_PA1B1,
+ BRCMS_SROM_PA1B2,
+ BRCMS_SROM_PA1HIB0,
+ BRCMS_SROM_PA1HIB1,
+ BRCMS_SROM_PA1HIB2,
+ BRCMS_SROM_PA1HIMAXPWR,
+ BRCMS_SROM_PA1ITSSIT,
+ BRCMS_SROM_PA1LOB0,
+ BRCMS_SROM_PA1LOB1,
+ BRCMS_SROM_PA1LOB2,
+ BRCMS_SROM_PA1LOMAXPWR,
+ BRCMS_SROM_PA1MAXPWR,
+ BRCMS_SROM_PDETRANGE2G,
+ BRCMS_SROM_PDETRANGE5G,
+ BRCMS_SROM_PHYCAL_TEMPDELTA,
+ BRCMS_SROM_RAWTEMPSENSE,
+ BRCMS_SROM_REGREV,
+ BRCMS_SROM_REV,
+ BRCMS_SROM_RSSISAV2G,
+ BRCMS_SROM_RSSISAV5G,
+ BRCMS_SROM_RSSISMC2G,
+ BRCMS_SROM_RSSISMC5G,
+ BRCMS_SROM_RSSISMF2G,
+ BRCMS_SROM_RSSISMF5G,
+ BRCMS_SROM_RXCHAIN,
+ BRCMS_SROM_RXPO2G,
+ BRCMS_SROM_RXPO5G,
+ BRCMS_SROM_STBCPO,
+ BRCMS_SROM_TEMPCORRX,
+ BRCMS_SROM_TEMPOFFSET,
+ BRCMS_SROM_TEMPSENSE_OPTION,
+ BRCMS_SROM_TEMPSENSE_SLOPE,
+ BRCMS_SROM_TEMPTHRESH,
+ BRCMS_SROM_TRI2G,
+ BRCMS_SROM_TRI5GH,
+ BRCMS_SROM_TRI5GL,
+ BRCMS_SROM_TRI5G,
+ BRCMS_SROM_TRISO2G,
+ BRCMS_SROM_TRISO5G,
+ BRCMS_SROM_TSSIPOS2G,
+ BRCMS_SROM_TSSIPOS5G,
+ BRCMS_SROM_TXCHAIN,
+ BRCMS_SROM_TXPID2GA0,
+ BRCMS_SROM_TXPID2GA1,
+ BRCMS_SROM_TXPID2GA2,
+ BRCMS_SROM_TXPID2GA3,
+ BRCMS_SROM_TXPID5GA0,
+ BRCMS_SROM_TXPID5GA1,
+ BRCMS_SROM_TXPID5GA2,
+ BRCMS_SROM_TXPID5GA3,
+ BRCMS_SROM_TXPID5GHA0,
+ BRCMS_SROM_TXPID5GHA1,
+ BRCMS_SROM_TXPID5GHA2,
+ BRCMS_SROM_TXPID5GHA3,
+ BRCMS_SROM_TXPID5GLA0,
+ BRCMS_SROM_TXPID5GLA1,
+ BRCMS_SROM_TXPID5GLA2,
+ BRCMS_SROM_TXPID5GLA3,
+ /*
+ * per-path identifiers (see srom.c)
+ */
+ BRCMS_SROM_ITT2GA0,
+ BRCMS_SROM_ITT2GA1,
+ BRCMS_SROM_ITT2GA2,
+ BRCMS_SROM_ITT2GA3,
+ BRCMS_SROM_ITT5GA0,
+ BRCMS_SROM_ITT5GA1,
+ BRCMS_SROM_ITT5GA2,
+ BRCMS_SROM_ITT5GA3,
+ BRCMS_SROM_MAXP2GA0,
+ BRCMS_SROM_MAXP2GA1,
+ BRCMS_SROM_MAXP2GA2,
+ BRCMS_SROM_MAXP2GA3,
+ BRCMS_SROM_MAXP5GA0,
+ BRCMS_SROM_MAXP5GA1,
+ BRCMS_SROM_MAXP5GA2,
+ BRCMS_SROM_MAXP5GA3,
+ BRCMS_SROM_MAXP5GHA0,
+ BRCMS_SROM_MAXP5GHA1,
+ BRCMS_SROM_MAXP5GHA2,
+ BRCMS_SROM_MAXP5GHA3,
+ BRCMS_SROM_MAXP5GLA0,
+ BRCMS_SROM_MAXP5GLA1,
+ BRCMS_SROM_MAXP5GLA2,
+ BRCMS_SROM_MAXP5GLA3,
+ BRCMS_SROM_PA2GW0A0,
+ BRCMS_SROM_PA2GW0A1,
+ BRCMS_SROM_PA2GW0A2,
+ BRCMS_SROM_PA2GW0A3,
+ BRCMS_SROM_PA2GW1A0,
+ BRCMS_SROM_PA2GW1A1,
+ BRCMS_SROM_PA2GW1A2,
+ BRCMS_SROM_PA2GW1A3,
+ BRCMS_SROM_PA2GW2A0,
+ BRCMS_SROM_PA2GW2A1,
+ BRCMS_SROM_PA2GW2A2,
+ BRCMS_SROM_PA2GW2A3,
+ BRCMS_SROM_PA2GW3A0,
+ BRCMS_SROM_PA2GW3A1,
+ BRCMS_SROM_PA2GW3A2,
+ BRCMS_SROM_PA2GW3A3,
+ BRCMS_SROM_PA5GHW0A0,
+ BRCMS_SROM_PA5GHW0A1,
+ BRCMS_SROM_PA5GHW0A2,
+ BRCMS_SROM_PA5GHW0A3,
+ BRCMS_SROM_PA5GHW1A0,
+ BRCMS_SROM_PA5GHW1A1,
+ BRCMS_SROM_PA5GHW1A2,
+ BRCMS_SROM_PA5GHW1A3,
+ BRCMS_SROM_PA5GHW2A0,
+ BRCMS_SROM_PA5GHW2A1,
+ BRCMS_SROM_PA5GHW2A2,
+ BRCMS_SROM_PA5GHW2A3,
+ BRCMS_SROM_PA5GHW3A0,
+ BRCMS_SROM_PA5GHW3A1,
+ BRCMS_SROM_PA5GHW3A2,
+ BRCMS_SROM_PA5GHW3A3,
+ BRCMS_SROM_PA5GLW0A0,
+ BRCMS_SROM_PA5GLW0A1,
+ BRCMS_SROM_PA5GLW0A2,
+ BRCMS_SROM_PA5GLW0A3,
+ BRCMS_SROM_PA5GLW1A0,
+ BRCMS_SROM_PA5GLW1A1,
+ BRCMS_SROM_PA5GLW1A2,
+ BRCMS_SROM_PA5GLW1A3,
+ BRCMS_SROM_PA5GLW2A0,
+ BRCMS_SROM_PA5GLW2A1,
+ BRCMS_SROM_PA5GLW2A2,
+ BRCMS_SROM_PA5GLW2A3,
+ BRCMS_SROM_PA5GLW3A0,
+ BRCMS_SROM_PA5GLW3A1,
+ BRCMS_SROM_PA5GLW3A2,
+ BRCMS_SROM_PA5GLW3A3,
+ BRCMS_SROM_PA5GW0A0,
+ BRCMS_SROM_PA5GW0A1,
+ BRCMS_SROM_PA5GW0A2,
+ BRCMS_SROM_PA5GW0A3,
+ BRCMS_SROM_PA5GW1A0,
+ BRCMS_SROM_PA5GW1A1,
+ BRCMS_SROM_PA5GW1A2,
+ BRCMS_SROM_PA5GW1A3,
+ BRCMS_SROM_PA5GW2A0,
+ BRCMS_SROM_PA5GW2A1,
+ BRCMS_SROM_PA5GW2A2,
+ BRCMS_SROM_PA5GW2A3,
+ BRCMS_SROM_PA5GW3A0,
+ BRCMS_SROM_PA5GW3A1,
+ BRCMS_SROM_PA5GW3A2,
+ BRCMS_SROM_PA5GW3A3,
+};
+
+#define BRCMS_NUMRATES 16 /* max # of rates in a rateset */
+#define D11_PHY_HDR_LEN 6 /* Phy header length - 6 bytes */
+
+/* phy types */
+#define PHY_TYPE_A 0 /* Phy type A */
+#define PHY_TYPE_G 2 /* Phy type G */
+#define PHY_TYPE_N 4 /* Phy type N */
+#define PHY_TYPE_LP 5 /* Phy type Low Power A/B/G */
+#define PHY_TYPE_SSN 6 /* Phy type Single Stream N */
+#define PHY_TYPE_LCN 8 /* Phy type Single Stream N */
+#define PHY_TYPE_LCNXN 9 /* Phy type 2-stream N */
+#define PHY_TYPE_HT 7 /* Phy type 3-Stream N */
+
+/* bw */
+#define BRCMS_10_MHZ 10 /* 10Mhz nphy channel bandwidth */
+#define BRCMS_20_MHZ 20 /* 20Mhz nphy channel bandwidth */
+#define BRCMS_40_MHZ 40 /* 40Mhz nphy channel bandwidth */
+
+#define BRCMS_RSSI_MINVAL -200 /* Low value, e.g. for forcing roam */
+#define BRCMS_RSSI_NO_SIGNAL -91 /* NDIS RSSI link quality cutoffs */
+#define BRCMS_RSSI_VERY_LOW -80 /* Very low quality cutoffs */
+#define BRCMS_RSSI_LOW -70 /* Low quality cutoffs */
+#define BRCMS_RSSI_GOOD -68 /* Good quality cutoffs */
+#define BRCMS_RSSI_VERY_GOOD -58 /* Very good quality cutoffs */
+#define BRCMS_RSSI_EXCELLENT -57 /* Excellent quality cutoffs */
+
+/* a large TX Power as an init value to factor out of min() calculations,
+ * keep low enough to fit in an s8, units are .25 dBm
+ */
+#define BRCMS_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */
+
+/* rate related definitions */
+#define BRCMS_RATE_FLAG 0x80 /* Flag to indicate it is a basic rate */
+#define BRCMS_RATE_MASK 0x7f /* Rate value mask w/o basic rate flag */
+
+/* legacy rx Antenna diversity for SISO rates */
+#define ANT_RX_DIV_FORCE_0 0 /* Use antenna 0 */
+#define ANT_RX_DIV_FORCE_1 1 /* Use antenna 1 */
+#define ANT_RX_DIV_START_1 2 /* Choose starting with 1 */
+#define ANT_RX_DIV_START_0 3 /* Choose starting with 0 */
+#define ANT_RX_DIV_ENABLE 3 /* APHY bbConfig Enable RX Diversity */
+/* default antdiv setting */
+#define ANT_RX_DIV_DEF ANT_RX_DIV_START_0
+
+/* legacy rx Antenna diversity for SISO rates */
+/* Tx on antenna 0, "legacy term Main" */
+#define ANT_TX_FORCE_0 0
+/* Tx on antenna 1, "legacy term Aux" */
+#define ANT_TX_FORCE_1 1
+/* Tx on phy's last good Rx antenna */
+#define ANT_TX_LAST_RX 3
+/* driver's default tx antenna setting */
+#define ANT_TX_DEF 3
+
+/* Tx Chain values */
+/* def bitmap of txchain */
+#define TXCHAIN_DEF 0x1
+/* default bitmap of tx chains for nphy */
+#define TXCHAIN_DEF_NPHY 0x3
+/* default bitmap of tx chains for nphy */
+#define TXCHAIN_DEF_HTPHY 0x7
+/* def bitmap of rxchain */
+#define RXCHAIN_DEF 0x1
+/* default bitmap of rx chains for nphy */
+#define RXCHAIN_DEF_NPHY 0x3
+/* default bitmap of rx chains for nphy */
+#define RXCHAIN_DEF_HTPHY 0x7
+/* no antenna switch */
+#define ANTSWITCH_NONE 0
+/* antenna switch on 4321CB2, 2of3 */
+#define ANTSWITCH_TYPE_1 1
+/* antenna switch on 4321MPCI, 2of3 */
+#define ANTSWITCH_TYPE_2 2
+/* antenna switch on 4322, 2of3 */
+#define ANTSWITCH_TYPE_3 3
+
+#define RXBUFSZ PKTBUFSZ
+
+#define MAX_STREAMS_SUPPORTED 4 /* max number of streams supported */
+
+struct brcm_rateset {
+ /* # rates in this set */
+ u32 count;
+ /* rates in 500kbps units w/hi bit set if basic */
+ u8 rates[WL_NUMRATES];
+};
+
+struct brcms_c_rateset {
+ uint count; /* number of rates in rates[] */
+ /* rates in 500kbps units w/hi bit set if basic */
+ u8 rates[BRCMS_NUMRATES];
+ u8 htphy_membership; /* HT PHY Membership */
+ u8 mcs[MCSSET_LEN]; /* supported mcs index bit map */
+};
+
+/* All the HT-specific default advertised capabilities (including AMPDU)
+ * should be grouped here at one place
+ */
+#define AMPDU_DEF_MPDU_DENSITY 6 /* default mpdu density (110 ==> 4us) */
+
+/* wlc internal bss_info */
+struct brcms_bss_info {
+ u8 BSSID[ETH_ALEN]; /* network BSSID */
+ u16 flags; /* flags for internal attributes */
+ u8 SSID_len; /* the length of SSID */
+ u8 SSID[32]; /* SSID string */
+ s16 RSSI; /* receive signal strength (in dBm) */
+ s16 SNR; /* receive signal SNR in dB */
+ u16 beacon_period; /* units are Kusec */
+ u16 chanspec; /* Channel num, bw, ctrl_sb and band */
+ struct brcms_c_rateset rateset; /* supported rates */
+};
+
+#define MAC80211_PROMISC_BCNS (1 << 0)
+#define MAC80211_SCAN (1 << 1)
+
+/*
+ * Public portion of common driver state structure.
+ * The wlc handle points at this.
+ */
+struct brcms_pub {
+ struct brcms_c_info *wlc;
+ struct ieee80211_hw *ieee_hw;
+ struct scb_ampdu *global_ampdu;
+ uint mac80211_state;
+ uint unit; /* device instance number */
+ uint corerev; /* core revision */
+ struct si_pub *sih; /* SI handle (cookie for siutils calls) */
+ bool up; /* interface up and running */
+ bool hw_off; /* HW is off */
+ bool hw_up; /* one time hw up/down */
+ bool _piomode; /* true if pio mode */
+ uint _nbands; /* # bands supported */
+ uint now; /* # elapsed seconds */
+
+ bool promisc; /* promiscuous destination address */
+ bool delayed_down; /* down delayed */
+ bool associated; /* true:part of [I]BSS, false: not */
+ /* (union of stas_associated, aps_associated) */
+ bool _ampdu; /* ampdu enabled or not */
+ u8 _n_enab; /* bitmap of 11N + HT support */
+
+ u8 cur_etheraddr[ETH_ALEN]; /* our local ethernet address */
+
+ int bcmerror; /* last bcm error */
+
+ u32 radio_disabled; /* bit vector for radio disabled reasons */
+
+ u16 boardrev; /* version # of particular board */
+ u8 sromrev; /* version # of the srom */
+ char srom_ccode[BRCM_CNTRY_BUF_SZ]; /* Country Code in SROM */
+ u32 boardflags; /* Board specific flags from srom */
+ u32 boardflags2; /* More board flags if sromrev >= 4 */
+ bool phy_11ncapable; /* the PHY/HW is capable of 802.11N */
+
+ struct wl_cnt *_cnt; /* low-level counters in driver */
+};
+
+enum wlc_par_id {
+ IOV_MPC = 1,
+ IOV_RTSTHRESH,
+ IOV_QTXPOWER,
+ IOV_BCN_LI_BCN /* Beacon listen interval in # of beacons */
+};
+
+/***********************************************
+ * Feature-related macros to optimize out code *
+ * *********************************************
+ */
+
+#define ENAB_1x1 0x01
+#define ENAB_2x2 0x02
+#define ENAB_3x3 0x04
+#define ENAB_4x4 0x08
+#define SUPPORT_11N (ENAB_1x1|ENAB_2x2)
+#define SUPPORT_HT (ENAB_1x1|ENAB_2x2|ENAB_3x3)
+
+/* WL11N Support */
+#define AMPDU_AGG_HOST 1
+
+/* pri is priority encoded in the packet. This maps the Packet priority to
+ * enqueue precedence as defined in wlc_prec_map
+ */
+extern const u8 wlc_prio2prec_map[];
+#define BRCMS_PRIO_TO_PREC(pri) wlc_prio2prec_map[(pri) & 7]
+
+#define BRCMS_PREC_COUNT 16 /* Max precedence level implemented */
+
+/* Mask to describe all precedence levels */
+#define BRCMS_PREC_BMP_ALL MAXBITVAL(BRCMS_PREC_COUNT)
+
+/*
+ * This maps priority to one precedence higher - Used by PS-Poll response
+ * packets to simulate enqueue-at-head operation, but still maintain the
+ * order on the queue
+ */
+#define BRCMS_PRIO_TO_HI_PREC(pri) min(BRCMS_PRIO_TO_PREC(pri) + 1,\
+ BRCMS_PREC_COUNT - 1)
+
+/* Define a bitmap of precedences comprised by each AC */
+#define BRCMS_PREC_BMP_AC_BE (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_BE)) | \
+ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_BE)) | \
+ NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_EE)) | \
+ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_EE)))
+#define BRCMS_PREC_BMP_AC_BK (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_BK)) | \
+ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_BK)) | \
+ NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_NONE)) | \
+ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_NONE)))
+#define BRCMS_PREC_BMP_AC_VI (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_CL)) | \
+ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_CL)) | \
+ NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_VI)) | \
+ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_VI)))
+#define BRCMS_PREC_BMP_AC_VO (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_VO)) | \
+ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_VO)) | \
+ NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_NC)) | \
+ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_NC)))
+
+/* network protection config */
+#define BRCMS_PROT_G_SPEC 1 /* SPEC g protection */
+#define BRCMS_PROT_G_OVR 2 /* SPEC g prot override */
+#define BRCMS_PROT_G_USER 3 /* gmode specified by user */
+#define BRCMS_PROT_OVERLAP 4 /* overlap */
+#define BRCMS_PROT_N_USER 10 /* nmode specified by user */
+#define BRCMS_PROT_N_CFG 11 /* n protection */
+#define BRCMS_PROT_N_CFG_OVR 12 /* n protection override */
+#define BRCMS_PROT_N_NONGF 13 /* non-GF protection */
+#define BRCMS_PROT_N_NONGF_OVR 14 /* non-GF protection override */
+#define BRCMS_PROT_N_PAM_OVR 15 /* n preamble override */
+#define BRCMS_PROT_N_OBSS 16 /* non-HT OBSS present */
+
+/*
+ * 54g modes (basic bits may still be overridden)
+ *
+ * GMODE_LEGACY_B
+ * Rateset: 1b, 2b, 5.5, 11
+ * Preamble: Long
+ * Shortslot: Off
+ * GMODE_AUTO
+ * Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54
+ * Extended Rateset: 6, 9, 12, 48
+ * Preamble: Long
+ * Shortslot: Auto
+ * GMODE_ONLY
+ * Rateset: 1b, 2b, 5.5b, 11b, 18, 24b, 36, 54
+ * Extended Rateset: 6b, 9, 12b, 48
+ * Preamble: Short required
+ * Shortslot: Auto
+ * GMODE_B_DEFERRED
+ * Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54
+ * Extended Rateset: 6, 9, 12, 48
+ * Preamble: Long
+ * Shortslot: On
+ * GMODE_PERFORMANCE
+ * Rateset: 1b, 2b, 5.5b, 6b, 9, 11b, 12b, 18, 24b, 36, 48, 54
+ * Preamble: Short required
+ * Shortslot: On and required
+ * GMODE_LRS
+ * Rateset: 1b, 2b, 5.5b, 11b
+ * Extended Rateset: 6, 9, 12, 18, 24, 36, 48, 54
+ * Preamble: Long
+ * Shortslot: Auto
+ */
+#define GMODE_LEGACY_B 0
+#define GMODE_AUTO 1
+#define GMODE_ONLY 2
+#define GMODE_B_DEFERRED 3
+#define GMODE_PERFORMANCE 4
+#define GMODE_LRS 5
+#define GMODE_MAX 6
+
+/* MCS values greater than this enable multiple streams */
+#define HIGHEST_SINGLE_STREAM_MCS 7
+
+#define MAXBANDS 2 /* Maximum #of bands */
+
+/* max number of antenna configurations */
+#define ANT_SELCFG_MAX 4
+
+struct brcms_antselcfg {
+ u8 ant_config[ANT_SELCFG_MAX]; /* antenna configuration */
+ u8 num_antcfg; /* number of available antenna configurations */
+};
+
+/* common functions for every port */
+extern struct brcms_c_info *
+brcms_c_attach(struct brcms_info *wl, u16 vendor, u16 device, uint unit,
+ bool piomode, void __iomem *regsva, struct pci_dev *btparam,
+ uint *perr);
+extern uint brcms_c_detach(struct brcms_c_info *wlc);
+extern int brcms_c_up(struct brcms_c_info *wlc);
+extern uint brcms_c_down(struct brcms_c_info *wlc);
+
+extern bool brcms_c_chipmatch(u16 vendor, u16 device);
+extern void brcms_c_init(struct brcms_c_info *wlc);
+extern void brcms_c_reset(struct brcms_c_info *wlc);
+
+extern void brcms_c_intrson(struct brcms_c_info *wlc);
+extern u32 brcms_c_intrsoff(struct brcms_c_info *wlc);
+extern void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask);
+extern bool brcms_c_intrsupd(struct brcms_c_info *wlc);
+extern bool brcms_c_isr(struct brcms_c_info *wlc, bool *wantdpc);
+extern bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded);
+extern void brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc,
+ struct sk_buff *sdu,
+ struct ieee80211_hw *hw);
+extern bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid);
+extern void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx,
+ int val);
+extern int brcms_c_get_header_len(void);
+extern void brcms_c_set_addrmatch(struct brcms_c_info *wlc,
+ int match_reg_offset,
+ const u8 *addr);
+extern void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci,
+ const struct ieee80211_tx_queue_params *arg,
+ bool suspend);
+extern struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc);
+extern void brcms_c_ampdu_flush(struct brcms_c_info *wlc,
+ struct ieee80211_sta *sta, u16 tid);
+extern void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid,
+ u8 ba_wsize, uint max_rx_ampdu_bytes);
+extern char *getvar(struct si_pub *sih, enum brcms_srom_id id);
+extern int getintvar(struct si_pub *sih, enum brcms_srom_id id);
+extern int brcms_c_module_register(struct brcms_pub *pub,
+ const char *name, struct brcms_info *hdl,
+ int (*down_fn)(void *handle));
+extern int brcms_c_module_unregister(struct brcms_pub *pub, const char *name,
+ struct brcms_info *hdl);
+extern void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc);
+extern void brcms_c_enable_mac(struct brcms_c_info *wlc);
+extern void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state);
+extern void brcms_c_scan_start(struct brcms_c_info *wlc);
+extern void brcms_c_scan_stop(struct brcms_c_info *wlc);
+extern int brcms_c_get_curband(struct brcms_c_info *wlc);
+extern void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc,
+ bool drop);
+extern int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel);
+extern int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl);
+extern void brcms_c_get_current_rateset(struct brcms_c_info *wlc,
+ struct brcm_rateset *currs);
+extern int brcms_c_set_rateset(struct brcms_c_info *wlc,
+ struct brcm_rateset *rs);
+extern int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period);
+extern u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx);
+extern void brcms_c_set_shortslot_override(struct brcms_c_info *wlc,
+ s8 sslot_override);
+extern void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc,
+ u8 interval);
+extern int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr);
+extern int brcms_c_get_tx_power(struct brcms_c_info *wlc);
+extern void brcms_c_set_radio_mpc(struct brcms_c_info *wlc, bool mpc);
+extern bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc);
+
+#endif /* _BRCM_PUB_H_ */
diff --git a/drivers/staging/brcm80211/brcmsmac/wlc_rate.c b/drivers/net/wireless/brcm80211/brcmsmac/rate.c
index 87b252d6a7f5..0a0c0ad4f96f 100644
--- a/drivers/staging/brcm80211/brcmsmac/wlc_rate.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/rate.c
@@ -13,26 +13,19 @@
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <proto/802.11.h>
-#include <bcmdefs.h>
-#include <bcmutils.h>
-#include <aiutils.h>
-#include <wlioctl.h>
-#include <sbhnddma.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
-#include "wlc_types.h"
#include "d11.h"
-#include "wl_dbg.h"
-#include "wlc_cfg.h"
-#include "wlc_scb.h"
-#include "wlc_pub.h"
-#include "wlc_rate.h"
-
-/* Rate info per rate: It tells whether a rate is ofdm or not and its phy_rate value */
-const u8 rate_info[WLC_MAXRATE + 1] = {
+#include "pub.h"
+#include "rate.h"
+
+/*
+ * Rate info per rate: It tells whether a rate is ofdm or not and its phy_rate
+ * value
+ */
+const u8 rate_info[BRCM_MAXRATE + 1] = {
/* 0 1 2 3 4 5 6 7 8 9 */
/* 0 */ 0x00, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 10 */ 0x00, 0x37, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x00,
@@ -48,220 +41,235 @@ const u8 rate_info[WLC_MAXRATE + 1] = {
};
/* rates are in units of Kbps */
-const mcs_info_t mcs_table[MCS_TABLE_SIZE] = {
+const struct brcms_mcs_info mcs_table[MCS_TABLE_SIZE] = {
/* MCS 0: SS 1, MOD: BPSK, CR 1/2 */
{6500, 13500, CEIL(6500 * 10, 9), CEIL(13500 * 10, 9), 0x00,
- WLC_RATE_6M},
+ BRCM_RATE_6M},
/* MCS 1: SS 1, MOD: QPSK, CR 1/2 */
{13000, 27000, CEIL(13000 * 10, 9), CEIL(27000 * 10, 9), 0x08,
- WLC_RATE_12M},
+ BRCM_RATE_12M},
/* MCS 2: SS 1, MOD: QPSK, CR 3/4 */
{19500, 40500, CEIL(19500 * 10, 9), CEIL(40500 * 10, 9), 0x0A,
- WLC_RATE_18M},
+ BRCM_RATE_18M},
/* MCS 3: SS 1, MOD: 16QAM, CR 1/2 */
{26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0x10,
- WLC_RATE_24M},
+ BRCM_RATE_24M},
/* MCS 4: SS 1, MOD: 16QAM, CR 3/4 */
{39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x12,
- WLC_RATE_36M},
+ BRCM_RATE_36M},
/* MCS 5: SS 1, MOD: 64QAM, CR 2/3 */
{52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0x19,
- WLC_RATE_48M},
+ BRCM_RATE_48M},
/* MCS 6: SS 1, MOD: 64QAM, CR 3/4 */
{58500, 121500, CEIL(58500 * 10, 9), CEIL(121500 * 10, 9), 0x1A,
- WLC_RATE_54M},
+ BRCM_RATE_54M},
/* MCS 7: SS 1, MOD: 64QAM, CR 5/6 */
{65000, 135000, CEIL(65000 * 10, 9), CEIL(135000 * 10, 9), 0x1C,
- WLC_RATE_54M},
+ BRCM_RATE_54M},
/* MCS 8: SS 2, MOD: BPSK, CR 1/2 */
{13000, 27000, CEIL(13000 * 10, 9), CEIL(27000 * 10, 9), 0x40,
- WLC_RATE_6M},
+ BRCM_RATE_6M},
/* MCS 9: SS 2, MOD: QPSK, CR 1/2 */
{26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0x48,
- WLC_RATE_12M},
+ BRCM_RATE_12M},
/* MCS 10: SS 2, MOD: QPSK, CR 3/4 */
{39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x4A,
- WLC_RATE_18M},
+ BRCM_RATE_18M},
/* MCS 11: SS 2, MOD: 16QAM, CR 1/2 */
{52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0x50,
- WLC_RATE_24M},
+ BRCM_RATE_24M},
/* MCS 12: SS 2, MOD: 16QAM, CR 3/4 */
{78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0x52,
- WLC_RATE_36M},
+ BRCM_RATE_36M},
/* MCS 13: SS 2, MOD: 64QAM, CR 2/3 */
{104000, 216000, CEIL(104000 * 10, 9), CEIL(216000 * 10, 9), 0x59,
- WLC_RATE_48M},
+ BRCM_RATE_48M},
/* MCS 14: SS 2, MOD: 64QAM, CR 3/4 */
{117000, 243000, CEIL(117000 * 10, 9), CEIL(243000 * 10, 9), 0x5A,
- WLC_RATE_54M},
+ BRCM_RATE_54M},
/* MCS 15: SS 2, MOD: 64QAM, CR 5/6 */
{130000, 270000, CEIL(130000 * 10, 9), CEIL(270000 * 10, 9), 0x5C,
- WLC_RATE_54M},
+ BRCM_RATE_54M},
/* MCS 16: SS 3, MOD: BPSK, CR 1/2 */
{19500, 40500, CEIL(19500 * 10, 9), CEIL(40500 * 10, 9), 0x80,
- WLC_RATE_6M},
+ BRCM_RATE_6M},
/* MCS 17: SS 3, MOD: QPSK, CR 1/2 */
{39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x88,
- WLC_RATE_12M},
+ BRCM_RATE_12M},
/* MCS 18: SS 3, MOD: QPSK, CR 3/4 */
{58500, 121500, CEIL(58500 * 10, 9), CEIL(121500 * 10, 9), 0x8A,
- WLC_RATE_18M},
+ BRCM_RATE_18M},
/* MCS 19: SS 3, MOD: 16QAM, CR 1/2 */
{78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0x90,
- WLC_RATE_24M},
+ BRCM_RATE_24M},
/* MCS 20: SS 3, MOD: 16QAM, CR 3/4 */
{117000, 243000, CEIL(117000 * 10, 9), CEIL(243000 * 10, 9), 0x92,
- WLC_RATE_36M},
+ BRCM_RATE_36M},
/* MCS 21: SS 3, MOD: 64QAM, CR 2/3 */
{156000, 324000, CEIL(156000 * 10, 9), CEIL(324000 * 10, 9), 0x99,
- WLC_RATE_48M},
+ BRCM_RATE_48M},
/* MCS 22: SS 3, MOD: 64QAM, CR 3/4 */
{175500, 364500, CEIL(175500 * 10, 9), CEIL(364500 * 10, 9), 0x9A,
- WLC_RATE_54M},
+ BRCM_RATE_54M},
/* MCS 23: SS 3, MOD: 64QAM, CR 5/6 */
{195000, 405000, CEIL(195000 * 10, 9), CEIL(405000 * 10, 9), 0x9B,
- WLC_RATE_54M},
+ BRCM_RATE_54M},
/* MCS 24: SS 4, MOD: BPSK, CR 1/2 */
{26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0xC0,
- WLC_RATE_6M},
+ BRCM_RATE_6M},
/* MCS 25: SS 4, MOD: QPSK, CR 1/2 */
{52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0xC8,
- WLC_RATE_12M},
+ BRCM_RATE_12M},
/* MCS 26: SS 4, MOD: QPSK, CR 3/4 */
{78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0xCA,
- WLC_RATE_18M},
+ BRCM_RATE_18M},
/* MCS 27: SS 4, MOD: 16QAM, CR 1/2 */
{104000, 216000, CEIL(104000 * 10, 9), CEIL(216000 * 10, 9), 0xD0,
- WLC_RATE_24M},
+ BRCM_RATE_24M},
/* MCS 28: SS 4, MOD: 16QAM, CR 3/4 */
{156000, 324000, CEIL(156000 * 10, 9), CEIL(324000 * 10, 9), 0xD2,
- WLC_RATE_36M},
+ BRCM_RATE_36M},
/* MCS 29: SS 4, MOD: 64QAM, CR 2/3 */
{208000, 432000, CEIL(208000 * 10, 9), CEIL(432000 * 10, 9), 0xD9,
- WLC_RATE_48M},
+ BRCM_RATE_48M},
/* MCS 30: SS 4, MOD: 64QAM, CR 3/4 */
{234000, 486000, CEIL(234000 * 10, 9), CEIL(486000 * 10, 9), 0xDA,
- WLC_RATE_54M},
+ BRCM_RATE_54M},
/* MCS 31: SS 4, MOD: 64QAM, CR 5/6 */
{260000, 540000, CEIL(260000 * 10, 9), CEIL(540000 * 10, 9), 0xDB,
- WLC_RATE_54M},
+ BRCM_RATE_54M},
/* MCS 32: SS 1, MOD: BPSK, CR 1/2 */
- {0, 6000, 0, CEIL(6000 * 10, 9), 0x00, WLC_RATE_6M},
+ {0, 6000, 0, CEIL(6000 * 10, 9), 0x00, BRCM_RATE_6M},
};
-/* phycfg for legacy OFDM frames: code rate, modulation scheme, spatial streams
- * Number of spatial streams: always 1
- * other fields: refer to table 78 of section 17.3.2.2 of the original .11a standard
+/*
+ * phycfg for legacy OFDM frames: code rate, modulation scheme, spatial streams
+ * Number of spatial streams: always 1 other fields: refer to table 78 of
+ * section 17.3.2.2 of the original .11a standard
*/
-typedef struct legacy_phycfg {
+struct legacy_phycfg {
u32 rate_ofdm; /* ofdm mac rate */
- u8 tx_phy_ctl3; /* phy ctl byte 3, code rate, modulation type, # of streams */
-} legacy_phycfg_t;
-
-#define LEGACY_PHYCFG_TABLE_SIZE 12 /* Number of legacy_rate_cfg entries in the table */
-
-/* In CCK mode LPPHY overloads OFDM Modulation bits with CCK Data Rate */
-/* Eventually MIMOPHY would also be converted to this format */
-/* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */
-static const legacy_phycfg_t legacy_phycfg_table[LEGACY_PHYCFG_TABLE_SIZE] = {
- {WLC_RATE_1M, 0x00}, /* CCK 1Mbps, data rate 0 */
- {WLC_RATE_2M, 0x08}, /* CCK 2Mbps, data rate 1 */
- {WLC_RATE_5M5, 0x10}, /* CCK 5.5Mbps, data rate 2 */
- {WLC_RATE_11M, 0x18}, /* CCK 11Mbps, data rate 3 */
- {WLC_RATE_6M, 0x00}, /* OFDM 6Mbps, code rate 1/2, BPSK, 1 spatial stream */
- {WLC_RATE_9M, 0x02}, /* OFDM 9Mbps, code rate 3/4, BPSK, 1 spatial stream */
- {WLC_RATE_12M, 0x08}, /* OFDM 12Mbps, code rate 1/2, QPSK, 1 spatial stream */
- {WLC_RATE_18M, 0x0A}, /* OFDM 18Mbps, code rate 3/4, QPSK, 1 spatial stream */
- {WLC_RATE_24M, 0x10}, /* OFDM 24Mbps, code rate 1/2, 16-QAM, 1 spatial stream */
- {WLC_RATE_36M, 0x12}, /* OFDM 36Mbps, code rate 3/4, 16-QAM, 1 spatial stream */
- {WLC_RATE_48M, 0x19}, /* OFDM 48Mbps, code rate 2/3, 64-QAM, 1 spatial stream */
- {WLC_RATE_54M, 0x1A}, /* OFDM 54Mbps, code rate 3/4, 64-QAM, 1 spatial stream */
+ /* phy ctl byte 3, code rate, modulation type, # of streams */
+ u8 tx_phy_ctl3;
+};
+
+/* Number of legacy_rate_cfg entries in the table */
+#define LEGACY_PHYCFG_TABLE_SIZE 12
+
+/*
+ * In CCK mode LPPHY overloads OFDM Modulation bits with CCK Data Rate
+ * Eventually MIMOPHY would also be converted to this format
+ * 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps
+ */
+static const struct
+legacy_phycfg legacy_phycfg_table[LEGACY_PHYCFG_TABLE_SIZE] = {
+ {BRCM_RATE_1M, 0x00}, /* CCK 1Mbps, data rate 0 */
+ {BRCM_RATE_2M, 0x08}, /* CCK 2Mbps, data rate 1 */
+ {BRCM_RATE_5M5, 0x10}, /* CCK 5.5Mbps, data rate 2 */
+ {BRCM_RATE_11M, 0x18}, /* CCK 11Mbps, data rate 3 */
+ /* OFDM 6Mbps, code rate 1/2, BPSK, 1 spatial stream */
+ {BRCM_RATE_6M, 0x00},
+ /* OFDM 9Mbps, code rate 3/4, BPSK, 1 spatial stream */
+ {BRCM_RATE_9M, 0x02},
+ /* OFDM 12Mbps, code rate 1/2, QPSK, 1 spatial stream */
+ {BRCM_RATE_12M, 0x08},
+ /* OFDM 18Mbps, code rate 3/4, QPSK, 1 spatial stream */
+ {BRCM_RATE_18M, 0x0A},
+ /* OFDM 24Mbps, code rate 1/2, 16-QAM, 1 spatial stream */
+ {BRCM_RATE_24M, 0x10},
+ /* OFDM 36Mbps, code rate 3/4, 16-QAM, 1 spatial stream */
+ {BRCM_RATE_36M, 0x12},
+ /* OFDM 48Mbps, code rate 2/3, 64-QAM, 1 spatial stream */
+ {BRCM_RATE_48M, 0x19},
+ /* OFDM 54Mbps, code rate 3/4, 64-QAM, 1 spatial stream */
+ {BRCM_RATE_54M, 0x1A},
};
/* Hardware rates (also encodes default basic rates) */
-const wlc_rateset_t cck_ofdm_mimo_rates = {
+const struct brcms_c_rateset cck_ofdm_mimo_rates = {
12,
- { /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48, 54 Mbps */
- 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60,
- 0x6c},
+ /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48, */
+ { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60,
+ /* 54 Mbps */
+ 0x6c},
0x00,
- {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00}
+ { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00}
};
-const wlc_rateset_t ofdm_mimo_rates = {
+const struct brcms_c_rateset ofdm_mimo_rates = {
8,
- { /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */
- 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c},
+ /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */
+ { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c},
0x00,
- {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00}
+ { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00}
};
/* Default ratesets that include MCS32 for 40BW channels */
-const wlc_rateset_t cck_ofdm_40bw_mimo_rates = {
+static const struct brcms_c_rateset cck_ofdm_40bw_mimo_rates = {
12,
- { /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48, 54 Mbps */
- 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60,
- 0x6c},
+ /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48 */
+ { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60,
+ /* 54 Mbps */
+ 0x6c},
0x00,
- {0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00}
+ { 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00}
};
-const wlc_rateset_t ofdm_40bw_mimo_rates = {
+static const struct brcms_c_rateset ofdm_40bw_mimo_rates = {
8,
- { /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */
- 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c},
+ /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */
+ { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c},
0x00,
- {0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00}
+ { 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00}
};
-const wlc_rateset_t cck_ofdm_rates = {
+const struct brcms_c_rateset cck_ofdm_rates = {
12,
- { /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48, 54 Mbps */
- 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60,
- 0x6c},
+ /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48,*/
+ { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60,
+ /*54 Mbps */
+ 0x6c},
0x00,
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00}
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00}
};
-const wlc_rateset_t gphy_legacy_rates = {
+const struct brcms_c_rateset gphy_legacy_rates = {
4,
- { /* 1b, 2b, 5.5b, 11b Mbps */
- 0x82, 0x84, 0x8b, 0x96},
+ /* 1b, 2b, 5.5b, 11b Mbps */
+ { 0x82, 0x84, 0x8b, 0x96},
0x00,
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00}
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00}
};
-const wlc_rateset_t ofdm_rates = {
+const struct brcms_c_rateset ofdm_rates = {
8,
- { /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */
- 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c},
+ /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */
+ { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c},
0x00,
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00}
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00}
};
-const wlc_rateset_t cck_rates = {
+const struct brcms_c_rateset cck_rates = {
4,
- { /* 1b, 2b, 5.5, 11 Mbps */
- 0x82, 0x84, 0x0b, 0x16},
+ /* 1b, 2b, 5.5, 11 Mbps */
+ { 0x82, 0x84, 0x0b, 0x16},
0x00,
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00}
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00}
};
-static bool wlc_rateset_valid(wlc_rateset_t *rs, bool check_brate);
-
/* check if rateset is valid.
* if check_brate is true, rateset without a basic rate is considered NOT valid.
*/
-static bool wlc_rateset_valid(wlc_rateset_t *rs, bool check_brate)
+static bool brcms_c_rateset_valid(struct brcms_c_rateset *rs, bool check_brate)
{
uint idx;
@@ -273,28 +281,29 @@ static bool wlc_rateset_valid(wlc_rateset_t *rs, bool check_brate)
/* error if no basic rates */
for (idx = 0; idx < rs->count; idx++) {
- if (rs->rates[idx] & WLC_RATE_FLAG)
+ if (rs->rates[idx] & BRCMS_RATE_FLAG)
return true;
}
return false;
}
-void wlc_rateset_mcs_upd(wlc_rateset_t *rs, u8 txstreams)
+void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams)
{
int i;
for (i = txstreams; i < MAX_STREAMS_SUPPORTED; i++)
rs->mcs[i] = 0;
}
-/* filter based on hardware rateset, and sort filtered rateset with basic bit(s) preserved,
- * and check if resulting rateset is valid.
+/*
+ * filter based on hardware rateset, and sort filtered rateset with basic
+ * bit(s) preserved, and check if resulting rateset is valid.
*/
bool
-wlc_rate_hwrs_filter_sort_validate(wlc_rateset_t *rs,
- const wlc_rateset_t *hw_rs,
+brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs,
+ const struct brcms_c_rateset *hw_rs,
bool check_brate, u8 txstreams)
{
- u8 rateset[WLC_MAXRATE + 1];
+ u8 rateset[BRCM_MAXRATE + 1];
u8 r;
uint count;
uint i;
@@ -303,18 +312,17 @@ wlc_rate_hwrs_filter_sort_validate(wlc_rateset_t *rs,
count = rs->count;
for (i = 0; i < count; i++) {
- /* mask off "basic rate" bit, WLC_RATE_FLAG */
- r = (int)rs->rates[i] & WLC_RATE_MASK;
- if ((r > WLC_MAXRATE) || (rate_info[r] == 0)) {
+ /* mask off "basic rate" bit, BRCMS_RATE_FLAG */
+ r = (int)rs->rates[i] & BRCMS_RATE_MASK;
+ if ((r > BRCM_MAXRATE) || (rate_info[r] == 0))
continue;
- }
rateset[r] = rs->rates[i]; /* preserve basic bit! */
}
/* fill out the rates in order, looking at only supported rates */
count = 0;
for (i = 0; i < hw_rs->count; i++) {
- r = hw_rs->rates[i] & WLC_RATE_MASK;
+ r = hw_rs->rates[i] & BRCMS_RATE_MASK;
if (rateset[r])
rs->rates[count++] = rateset[r];
}
@@ -325,17 +333,17 @@ wlc_rate_hwrs_filter_sort_validate(wlc_rateset_t *rs,
for (i = 0; i < MCSSET_LEN; i++)
rs->mcs[i] = (rs->mcs[i] & hw_rs->mcs[i]);
- if (wlc_rateset_valid(rs, check_brate))
+ if (brcms_c_rateset_valid(rs, check_brate))
return true;
else
return false;
}
/* calculate the rate of a rx'd frame and return it as a ratespec */
-ratespec_t wlc_compute_rspec(d11rxhdr_t *rxh, u8 *plcp)
+u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp)
{
int phy_type;
- ratespec_t rspec = PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT;
+ u32 rspec = PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT;
phy_type =
((rxh->RxChan & RXS_CHAN_PHYTYPE_MASK) >> RXS_CHAN_PHYTYPE_SHIFT);
@@ -345,12 +353,13 @@ ratespec_t wlc_compute_rspec(d11rxhdr_t *rxh, u8 *plcp)
switch (rxh->PhyRxStatus_0 & PRXS0_FT_MASK) {
case PRXS0_CCK:
rspec =
- CCK_PHY2MAC_RATE(((cck_phy_hdr_t *) plcp)->signal);
+ cck_phy2mac_rate(
+ ((struct cck_phy_hdr *) plcp)->signal);
break;
case PRXS0_OFDM:
rspec =
- OFDM_PHY2MAC_RATE(((ofdm_phy_hdr_t *) plcp)->
- rlpt[0]);
+ ofdm_phy2mac_rate(
+ ((struct ofdm_phy_hdr *) plcp)->rlpt[0]);
break;
case PRXS0_PREN:
rspec = (plcp[0] & MIMO_PLCP_MCS_MASK) | RSPEC_MIMORATE;
@@ -366,21 +375,24 @@ ratespec_t wlc_compute_rspec(d11rxhdr_t *rxh, u8 *plcp)
/* not supported, error condition */
break;
}
- if (PLCP3_ISSGI(plcp[3]))
+ if (plcp3_issgi(plcp[3]))
rspec |= RSPEC_SHORT_GI;
} else
if ((phy_type == PHY_TYPE_A) || (rxh->PhyRxStatus_0 & PRXS0_OFDM))
- rspec = OFDM_PHY2MAC_RATE(((ofdm_phy_hdr_t *) plcp)->rlpt[0]);
+ rspec = ofdm_phy2mac_rate(
+ ((struct ofdm_phy_hdr *) plcp)->rlpt[0]);
else
- rspec = CCK_PHY2MAC_RATE(((cck_phy_hdr_t *) plcp)->signal);
+ rspec = cck_phy2mac_rate(
+ ((struct cck_phy_hdr *) plcp)->signal);
return rspec;
}
/* copy rateset src to dst as-is (no masking or sorting) */
-void wlc_rateset_copy(const wlc_rateset_t *src, wlc_rateset_t *dst)
+void brcms_c_rateset_copy(const struct brcms_c_rateset *src,
+ struct brcms_c_rateset *dst)
{
- memcpy(dst, src, sizeof(wlc_rateset_t));
+ memcpy(dst, src, sizeof(struct brcms_c_rateset));
}
/*
@@ -393,8 +405,8 @@ void wlc_rateset_copy(const wlc_rateset_t *src, wlc_rateset_t *dst)
* 'xmask' is the copy mask (typically 0x7f or 0xff).
*/
void
-wlc_rateset_filter(wlc_rateset_t *src, wlc_rateset_t *dst, bool basic_only,
- u8 rates, uint xmask, bool mcsallow)
+brcms_c_rateset_filter(struct brcms_c_rateset *src, struct brcms_c_rateset *dst,
+ bool basic_only, u8 rates, uint xmask, bool mcsallow)
{
uint i;
uint r;
@@ -403,47 +415,50 @@ wlc_rateset_filter(wlc_rateset_t *src, wlc_rateset_t *dst, bool basic_only,
count = 0;
for (i = 0; i < src->count; i++) {
r = src->rates[i];
- if (basic_only && !(r & WLC_RATE_FLAG))
+ if (basic_only && !(r & BRCMS_RATE_FLAG))
continue;
- if ((rates == WLC_RATES_CCK) && IS_OFDM((r & WLC_RATE_MASK)))
+ if (rates == BRCMS_RATES_CCK &&
+ is_ofdm_rate((r & BRCMS_RATE_MASK)))
continue;
- if ((rates == WLC_RATES_OFDM) && IS_CCK((r & WLC_RATE_MASK)))
+ if (rates == BRCMS_RATES_OFDM &&
+ is_cck_rate((r & BRCMS_RATE_MASK)))
continue;
dst->rates[count++] = r & xmask;
}
dst->count = count;
dst->htphy_membership = src->htphy_membership;
- if (mcsallow && rates != WLC_RATES_CCK)
+ if (mcsallow && rates != BRCMS_RATES_CCK)
memcpy(&dst->mcs[0], &src->mcs[0], MCSSET_LEN);
else
- wlc_rateset_mcs_clear(dst);
+ brcms_c_rateset_mcs_clear(dst);
}
/* select rateset for a given phy_type and bandtype and filter it, sort it
* and fill rs_tgt with result
*/
void
-wlc_rateset_default(wlc_rateset_t *rs_tgt, const wlc_rateset_t *rs_hw,
- uint phy_type, int bandtype, bool cck_only, uint rate_mask,
- bool mcsallow, u8 bw, u8 txstreams)
+brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt,
+ const struct brcms_c_rateset *rs_hw,
+ uint phy_type, int bandtype, bool cck_only,
+ uint rate_mask, bool mcsallow, u8 bw, u8 txstreams)
{
- const wlc_rateset_t *rs_dflt;
- wlc_rateset_t rs_sel;
+ const struct brcms_c_rateset *rs_dflt;
+ struct brcms_c_rateset rs_sel;
if ((PHYTYPE_IS(phy_type, PHY_TYPE_HT)) ||
(PHYTYPE_IS(phy_type, PHY_TYPE_N)) ||
(PHYTYPE_IS(phy_type, PHY_TYPE_LCN)) ||
(PHYTYPE_IS(phy_type, PHY_TYPE_SSN))) {
- if (BAND_5G(bandtype)) {
- rs_dflt = (bw == WLC_20_MHZ ?
+ if (bandtype == BRCM_BAND_5G)
+ rs_dflt = (bw == BRCMS_20_MHZ ?
&ofdm_mimo_rates : &ofdm_40bw_mimo_rates);
- } else {
- rs_dflt = (bw == WLC_20_MHZ ?
+ else
+ rs_dflt = (bw == BRCMS_20_MHZ ?
&cck_ofdm_mimo_rates :
&cck_ofdm_40bw_mimo_rates);
- }
} else if (PHYTYPE_IS(phy_type, PHY_TYPE_LP)) {
- rs_dflt = (BAND_5G(bandtype)) ? &ofdm_rates : &cck_ofdm_rates;
+ rs_dflt = (bandtype == BRCM_BAND_5G) ?
+ &ofdm_rates : &cck_ofdm_rates;
} else if (PHYTYPE_IS(phy_type, PHY_TYPE_A)) {
rs_dflt = &ofdm_rates;
} else if (PHYTYPE_IS(phy_type, PHY_TYPE_G)) {
@@ -457,16 +472,16 @@ wlc_rateset_default(wlc_rateset_t *rs_tgt, const wlc_rateset_t *rs_hw,
if (!rs_hw)
rs_hw = rs_dflt;
- wlc_rateset_copy(rs_dflt, &rs_sel);
- wlc_rateset_mcs_upd(&rs_sel, txstreams);
- wlc_rateset_filter(&rs_sel, rs_tgt, false,
- cck_only ? WLC_RATES_CCK : WLC_RATES_CCK_OFDM,
+ brcms_c_rateset_copy(rs_dflt, &rs_sel);
+ brcms_c_rateset_mcs_upd(&rs_sel, txstreams);
+ brcms_c_rateset_filter(&rs_sel, rs_tgt, false,
+ cck_only ? BRCMS_RATES_CCK : BRCMS_RATES_CCK_OFDM,
rate_mask, mcsallow);
- wlc_rate_hwrs_filter_sort_validate(rs_tgt, rs_hw, false,
+ brcms_c_rate_hwrs_filter_sort_validate(rs_tgt, rs_hw, false,
mcsallow ? txstreams : 1);
}
-s16 wlc_rate_legacy_phyctl(uint rate)
+s16 brcms_c_rate_legacy_phyctl(uint rate)
{
uint i;
for (i = 0; i < LEGACY_PHYCFG_TABLE_SIZE; i++)
@@ -476,23 +491,23 @@ s16 wlc_rate_legacy_phyctl(uint rate)
return -1;
}
-void wlc_rateset_mcs_clear(wlc_rateset_t *rateset)
+void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset)
{
uint i;
for (i = 0; i < MCSSET_LEN; i++)
rateset->mcs[i] = 0;
}
-void wlc_rateset_mcs_build(wlc_rateset_t *rateset, u8 txstreams)
+void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, u8 txstreams)
{
memcpy(&rateset->mcs[0], &cck_ofdm_mimo_rates.mcs[0], MCSSET_LEN);
- wlc_rateset_mcs_upd(rateset, txstreams);
+ brcms_c_rateset_mcs_upd(rateset, txstreams);
}
/* Based on bandwidth passed, allow/disallow MCS 32 in the rateset */
-void wlc_rateset_bw_mcs_filter(wlc_rateset_t *rateset, u8 bw)
+void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, u8 bw)
{
- if (bw == WLC_40_MHZ)
+ if (bw == BRCMS_40_MHZ)
setbit(rateset->mcs, 32);
else
clrbit(rateset->mcs, 32);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/rate.h b/drivers/net/wireless/brcm80211/brcmsmac/rate.h
new file mode 100644
index 000000000000..e7b9dc2f2731
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/rate.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_RATE_H_
+#define _BRCM_RATE_H_
+
+#include "types.h"
+#include "d11.h"
+
+extern const u8 rate_info[];
+extern const struct brcms_c_rateset cck_ofdm_mimo_rates;
+extern const struct brcms_c_rateset ofdm_mimo_rates;
+extern const struct brcms_c_rateset cck_ofdm_rates;
+extern const struct brcms_c_rateset ofdm_rates;
+extern const struct brcms_c_rateset cck_rates;
+extern const struct brcms_c_rateset gphy_legacy_rates;
+extern const struct brcms_c_rateset rate_limit_1_2;
+
+struct brcms_mcs_info {
+ /* phy rate in kbps [20Mhz] */
+ u32 phy_rate_20;
+ /* phy rate in kbps [40Mhz] */
+ u32 phy_rate_40;
+ /* phy rate in kbps [20Mhz] with SGI */
+ u32 phy_rate_20_sgi;
+ /* phy rate in kbps [40Mhz] with SGI */
+ u32 phy_rate_40_sgi;
+ /* phy ctl byte 3, code rate, modulation type, # of streams */
+ u8 tx_phy_ctl3;
+ /* matching legacy ofdm rate in 500bkps */
+ u8 leg_ofdm;
+};
+
+#define BRCMS_MAXMCS 32 /* max valid mcs index */
+#define MCS_TABLE_SIZE 33 /* Number of mcs entries in the table */
+extern const struct brcms_mcs_info mcs_table[];
+
+#define MCS_TXS_MASK 0xc0 /* num tx streams - 1 bit mask */
+#define MCS_TXS_SHIFT 6 /* num tx streams - 1 bit shift */
+
+/* returns num tx streams - 1 */
+static inline u8 mcs_2_txstreams(u8 mcs)
+{
+ return (mcs_table[mcs].tx_phy_ctl3 & MCS_TXS_MASK) >> MCS_TXS_SHIFT;
+}
+
+static inline uint mcs_2_rate(u8 mcs, bool is40, bool sgi)
+{
+ if (sgi) {
+ if (is40)
+ return mcs_table[mcs].phy_rate_40_sgi;
+ return mcs_table[mcs].phy_rate_20_sgi;
+ }
+ if (is40)
+ return mcs_table[mcs].phy_rate_40;
+
+ return mcs_table[mcs].phy_rate_20;
+}
+
+/* Macro to use the rate_info table */
+#define BRCMS_RATE_MASK_FULL 0xff /* Rate value mask with basic rate flag */
+
+/*
+ * rate spec : holds rate and mode specific information required to generate a
+ * tx frame. Legacy CCK and OFDM information is held in the same manner as was
+ * done in the past (in the lower byte) the upper 3 bytes primarily hold MIMO
+ * specific information
+ */
+
+/* rate spec bit fields */
+
+/* Either 500Kbps units or MIMO MCS idx */
+#define RSPEC_RATE_MASK 0x0000007F
+/* mimo MCS is stored in RSPEC_RATE_MASK */
+#define RSPEC_MIMORATE 0x08000000
+/* mimo bw mask */
+#define RSPEC_BW_MASK 0x00000700
+/* mimo bw shift */
+#define RSPEC_BW_SHIFT 8
+/* mimo Space/Time/Frequency mode mask */
+#define RSPEC_STF_MASK 0x00003800
+/* mimo Space/Time/Frequency mode shift */
+#define RSPEC_STF_SHIFT 11
+/* mimo coding type mask */
+#define RSPEC_CT_MASK 0x0000C000
+/* mimo coding type shift */
+#define RSPEC_CT_SHIFT 14
+/* mimo num STC streams per PLCP defn. */
+#define RSPEC_STC_MASK 0x00300000
+/* mimo num STC streams per PLCP defn. */
+#define RSPEC_STC_SHIFT 20
+/* mimo bit indicates adv coding in use */
+#define RSPEC_LDPC_CODING 0x00400000
+/* mimo bit indicates short GI in use */
+#define RSPEC_SHORT_GI 0x00800000
+/* bit indicates override both rate & mode */
+#define RSPEC_OVERRIDE 0x80000000
+/* bit indicates override rate only */
+#define RSPEC_OVERRIDE_MCS_ONLY 0x40000000
+
+static inline bool rspec_active(u32 rspec)
+{
+ return rspec & (RSPEC_RATE_MASK | RSPEC_MIMORATE);
+}
+
+static inline u8 rspec_phytxbyte2(u32 rspec)
+{
+ return (rspec & 0xff00) >> 8;
+}
+
+static inline u32 rspec_get_bw(u32 rspec)
+{
+ return (rspec & RSPEC_BW_MASK) >> RSPEC_BW_SHIFT;
+}
+
+static inline bool rspec_issgi(u32 rspec)
+{
+ return (rspec & RSPEC_SHORT_GI) == RSPEC_SHORT_GI;
+}
+
+static inline bool rspec_is40mhz(u32 rspec)
+{
+ u32 bw = rspec_get_bw(rspec);
+
+ return bw == PHY_TXC1_BW_40MHZ || bw == PHY_TXC1_BW_40MHZ_DUP;
+}
+
+static inline uint rspec2rate(u32 rspec)
+{
+ if (rspec & RSPEC_MIMORATE)
+ return mcs_2_rate(rspec & RSPEC_RATE_MASK, rspec_is40mhz(rspec),
+ rspec_issgi(rspec));
+ return rspec & RSPEC_RATE_MASK;
+}
+
+static inline u8 rspec_mimoplcp3(u32 rspec)
+{
+ return (rspec & 0xf00000) >> 16;
+}
+
+static inline bool plcp3_issgi(u8 plcp)
+{
+ return (plcp & (RSPEC_SHORT_GI >> 16)) != 0;
+}
+
+static inline uint rspec_stc(u32 rspec)
+{
+ return (rspec & RSPEC_STC_MASK) >> RSPEC_STC_SHIFT;
+}
+
+static inline uint rspec_stf(u32 rspec)
+{
+ return (rspec & RSPEC_STF_MASK) >> RSPEC_STF_SHIFT;
+}
+
+static inline bool is_mcs_rate(u32 ratespec)
+{
+ return (ratespec & RSPEC_MIMORATE) != 0;
+}
+
+static inline bool is_ofdm_rate(u32 ratespec)
+{
+ return !is_mcs_rate(ratespec) &&
+ (rate_info[ratespec & RSPEC_RATE_MASK] & BRCMS_RATE_FLAG);
+}
+
+static inline bool is_cck_rate(u32 ratespec)
+{
+ u32 rate = (ratespec & BRCMS_RATE_MASK);
+
+ return !is_mcs_rate(ratespec) && (
+ rate == BRCM_RATE_1M || rate == BRCM_RATE_2M ||
+ rate == BRCM_RATE_5M5 || rate == BRCM_RATE_11M);
+}
+
+static inline bool is_single_stream(u8 mcs)
+{
+ return mcs <= HIGHEST_SINGLE_STREAM_MCS || mcs == 32;
+}
+
+static inline u8 cck_rspec(u8 cck)
+{
+ return cck & RSPEC_RATE_MASK;
+}
+
+/* Convert encoded rate value in plcp header to numerical rates in 500 KHz
+ * increments */
+extern const u8 ofdm_rate_lookup[];
+
+static inline u8 ofdm_phy2mac_rate(u8 rlpt)
+{
+ return ofdm_rate_lookup[rlpt & 0x7];
+}
+
+static inline u8 cck_phy2mac_rate(u8 signal)
+{
+ return signal/5;
+}
+
+/* Rates specified in brcms_c_rateset_filter() */
+#define BRCMS_RATES_CCK_OFDM 0
+#define BRCMS_RATES_CCK 1
+#define BRCMS_RATES_OFDM 2
+
+/* sanitize, and sort a rateset with the basic bit(s) preserved, validate
+ * rateset */
+extern bool
+brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs,
+ const struct brcms_c_rateset *hw_rs,
+ bool check_brate, u8 txstreams);
+/* copy rateset src to dst as-is (no masking or sorting) */
+extern void brcms_c_rateset_copy(const struct brcms_c_rateset *src,
+ struct brcms_c_rateset *dst);
+
+/* would be nice to have these documented ... */
+extern u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp);
+
+extern void brcms_c_rateset_filter(struct brcms_c_rateset *src,
+ struct brcms_c_rateset *dst, bool basic_only, u8 rates, uint xmask,
+ bool mcsallow);
+
+extern void
+brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt,
+ const struct brcms_c_rateset *rs_hw, uint phy_type,
+ int bandtype, bool cck_only, uint rate_mask,
+ bool mcsallow, u8 bw, u8 txstreams);
+
+extern s16 brcms_c_rate_legacy_phyctl(uint rate);
+
+extern void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams);
+extern void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset);
+extern void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset,
+ u8 txstreams);
+extern void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset,
+ u8 bw);
+
+#endif /* _BRCM_RATE_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/scb.h b/drivers/net/wireless/brcm80211/brcmsmac/scb.h
new file mode 100644
index 000000000000..51c79c7239b7
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/scb.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_SCB_H_
+#define _BRCM_SCB_H_
+
+#include <linux/if_ether.h>
+#include <brcmu_utils.h>
+#include <defs.h>
+#include "types.h"
+
+#define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
+
+#define AMPDU_MAX_SCB_TID NUMPRIO
+
+/* scb flags */
+#define SCB_WMECAP 0x0040
+#define SCB_HTCAP 0x10000 /* HT (MIMO) capable device */
+#define SCB_IS40 0x80000 /* 40MHz capable */
+#define SCB_STBCCAP 0x40000000 /* STBC Capable */
+
+#define SCB_MAGIC 0xbeefcafe
+
+/* structure to store per-tid state for the ampdu initiator */
+struct scb_ampdu_tid_ini {
+ u8 tx_in_transit; /* number of pending mpdus in transit in driver */
+ u8 tid; /* initiator tid for easy lookup */
+ /* tx retry count; indexed by seq modulo */
+ u8 txretry[AMPDU_TX_BA_MAX_WSIZE];
+ struct scb *scb; /* backptr for easy lookup */
+ u8 ba_wsize; /* negotiated ba window size (in pdu) */
+};
+
+struct scb_ampdu {
+ struct scb *scb; /* back pointer for easy reference */
+ u8 mpdu_density; /* mpdu density */
+ u8 max_pdu; /* max pdus allowed in ampdu */
+ u8 release; /* # of mpdus released at a time */
+ u16 min_len; /* min mpdu len to support the density */
+ u32 max_rx_ampdu_bytes; /* max ampdu rcv length; 8k, 16k, 32k, 64k */
+
+ /*
+ * This could easily be a ini[] pointer and we keep this info in wl
+ * itself instead of having mac80211 hold it for us. Also could be made
+ * dynamic per tid instead of static.
+ */
+ /* initiator info - per tid (NUMPRIO): */
+ struct scb_ampdu_tid_ini ini[AMPDU_MAX_SCB_TID];
+};
+
+/* station control block - one per remote MAC address */
+struct scb {
+ u32 magic;
+ u32 flags; /* various bit flags as defined below */
+ u32 flags2; /* various bit flags2 as defined below */
+ u8 state; /* current state bitfield of auth/assoc process */
+ u8 ea[ETH_ALEN]; /* station address */
+ uint fragresid[NUMPRIO];/* #bytes unused in frag buffer per prio */
+
+ u16 seqctl[NUMPRIO]; /* seqctl of last received frame (for dups) */
+ /* seqctl of last received frame (for dups) for non-QoS data and
+ * management */
+ u16 seqctl_nonqos;
+ u16 seqnum[NUMPRIO];/* WME: driver maintained sw seqnum per priority */
+
+ struct scb_ampdu scb_ampdu; /* AMPDU state including per tid info */
+};
+
+#endif /* _BRCM_SCB_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/srom.c b/drivers/net/wireless/brcm80211/brcmsmac/srom.c
new file mode 100644
index 000000000000..99f791048e84
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/srom.c
@@ -0,0 +1,1298 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/etherdevice.h>
+#include <linux/crc8.h>
+#include <stdarg.h>
+
+#include <chipcommon.h>
+#include <brcmu_utils.h>
+#include "pub.h"
+#include "nicpci.h"
+#include "aiutils.h"
+#include "otp.h"
+#include "srom.h"
+
+/*
+ * SROM CRC8 polynomial value:
+ *
+ * x^8 + x^7 +x^6 + x^4 + x^2 + 1
+ */
+#define SROM_CRC8_POLY 0xAB
+
+/* Maximum srom: 6 Kilobits == 768 bytes */
+#define SROM_MAX 768
+
+/* PCI fields */
+#define PCI_F0DEVID 48
+
+#define SROM_WORDS 64
+
+#define SROM_SSID 2
+
+#define SROM_WL1LHMAXP 29
+
+#define SROM_WL1LPAB0 30
+#define SROM_WL1LPAB1 31
+#define SROM_WL1LPAB2 32
+
+#define SROM_WL1HPAB0 33
+#define SROM_WL1HPAB1 34
+#define SROM_WL1HPAB2 35
+
+#define SROM_MACHI_IL0 36
+#define SROM_MACMID_IL0 37
+#define SROM_MACLO_IL0 38
+#define SROM_MACHI_ET1 42
+#define SROM_MACMID_ET1 43
+#define SROM_MACLO_ET1 44
+#define SROM3_MACHI 37
+#define SROM3_MACMID 38
+#define SROM3_MACLO 39
+
+#define SROM_BXARSSI2G 40
+#define SROM_BXARSSI5G 41
+
+#define SROM_TRI52G 42
+#define SROM_TRI5GHL 43
+
+#define SROM_RXPO52G 45
+
+#define SROM_AABREV 46
+/* Fields in AABREV */
+#define SROM_BR_MASK 0x00ff
+#define SROM_CC_MASK 0x0f00
+#define SROM_CC_SHIFT 8
+#define SROM_AA0_MASK 0x3000
+#define SROM_AA0_SHIFT 12
+#define SROM_AA1_MASK 0xc000
+#define SROM_AA1_SHIFT 14
+
+#define SROM_WL0PAB0 47
+#define SROM_WL0PAB1 48
+#define SROM_WL0PAB2 49
+
+#define SROM_LEDBH10 50
+#define SROM_LEDBH32 51
+
+#define SROM_WL10MAXP 52
+
+#define SROM_WL1PAB0 53
+#define SROM_WL1PAB1 54
+#define SROM_WL1PAB2 55
+
+#define SROM_ITT 56
+
+#define SROM_BFL 57
+#define SROM_BFL2 28
+#define SROM3_BFL2 61
+
+#define SROM_AG10 58
+
+#define SROM_CCODE 59
+
+#define SROM_OPO 60
+
+#define SROM3_LEDDC 62
+
+#define SROM_CRCREV 63
+
+/* SROM Rev 4: Reallocate the software part of the srom to accommodate
+ * MIMO features. It assumes up to two PCIE functions and 440 bytes
+ * of usable srom i.e. the usable storage in chips with OTP that
+ * implements hardware redundancy.
+ */
+
+#define SROM4_WORDS 220
+
+#define SROM4_SIGN 32
+#define SROM4_SIGNATURE 0x5372
+
+#define SROM4_BREV 33
+
+#define SROM4_BFL0 34
+#define SROM4_BFL1 35
+#define SROM4_BFL2 36
+#define SROM4_BFL3 37
+#define SROM5_BFL0 37
+#define SROM5_BFL1 38
+#define SROM5_BFL2 39
+#define SROM5_BFL3 40
+
+#define SROM4_MACHI 38
+#define SROM4_MACMID 39
+#define SROM4_MACLO 40
+#define SROM5_MACHI 41
+#define SROM5_MACMID 42
+#define SROM5_MACLO 43
+
+#define SROM4_CCODE 41
+#define SROM4_REGREV 42
+#define SROM5_CCODE 34
+#define SROM5_REGREV 35
+
+#define SROM4_LEDBH10 43
+#define SROM4_LEDBH32 44
+#define SROM5_LEDBH10 59
+#define SROM5_LEDBH32 60
+
+#define SROM4_LEDDC 45
+#define SROM5_LEDDC 45
+
+#define SROM4_AA 46
+
+#define SROM4_AG10 47
+#define SROM4_AG32 48
+
+#define SROM4_TXPID2G 49
+#define SROM4_TXPID5G 51
+#define SROM4_TXPID5GL 53
+#define SROM4_TXPID5GH 55
+
+#define SROM4_TXRXC 61
+#define SROM4_TXCHAIN_MASK 0x000f
+#define SROM4_TXCHAIN_SHIFT 0
+#define SROM4_RXCHAIN_MASK 0x00f0
+#define SROM4_RXCHAIN_SHIFT 4
+#define SROM4_SWITCH_MASK 0xff00
+#define SROM4_SWITCH_SHIFT 8
+
+/* Per-path fields */
+#define MAX_PATH_SROM 4
+#define SROM4_PATH0 64
+#define SROM4_PATH1 87
+#define SROM4_PATH2 110
+#define SROM4_PATH3 133
+
+#define SROM4_2G_ITT_MAXP 0
+#define SROM4_2G_PA 1
+#define SROM4_5G_ITT_MAXP 5
+#define SROM4_5GLH_MAXP 6
+#define SROM4_5G_PA 7
+#define SROM4_5GL_PA 11
+#define SROM4_5GH_PA 15
+
+/* All the miriad power offsets */
+#define SROM4_2G_CCKPO 156
+#define SROM4_2G_OFDMPO 157
+#define SROM4_5G_OFDMPO 159
+#define SROM4_5GL_OFDMPO 161
+#define SROM4_5GH_OFDMPO 163
+#define SROM4_2G_MCSPO 165
+#define SROM4_5G_MCSPO 173
+#define SROM4_5GL_MCSPO 181
+#define SROM4_5GH_MCSPO 189
+#define SROM4_CDDPO 197
+#define SROM4_STBCPO 198
+#define SROM4_BW40PO 199
+#define SROM4_BWDUPPO 200
+
+#define SROM4_CRCREV 219
+
+/* SROM Rev 8: Make space for a 48word hardware header for PCIe rev >= 6.
+ * This is acombined srom for both MIMO and SISO boards, usable in
+ * the .130 4Kilobit OTP with hardware redundancy.
+ */
+#define SROM8_BREV 65
+
+#define SROM8_BFL0 66
+#define SROM8_BFL1 67
+#define SROM8_BFL2 68
+#define SROM8_BFL3 69
+
+#define SROM8_MACHI 70
+#define SROM8_MACMID 71
+#define SROM8_MACLO 72
+
+#define SROM8_CCODE 73
+#define SROM8_REGREV 74
+
+#define SROM8_LEDBH10 75
+#define SROM8_LEDBH32 76
+
+#define SROM8_LEDDC 77
+
+#define SROM8_AA 78
+
+#define SROM8_AG10 79
+#define SROM8_AG32 80
+
+#define SROM8_TXRXC 81
+
+#define SROM8_BXARSSI2G 82
+#define SROM8_BXARSSI5G 83
+#define SROM8_TRI52G 84
+#define SROM8_TRI5GHL 85
+#define SROM8_RXPO52G 86
+
+#define SROM8_FEM2G 87
+#define SROM8_FEM5G 88
+#define SROM8_FEM_ANTSWLUT_MASK 0xf800
+#define SROM8_FEM_ANTSWLUT_SHIFT 11
+#define SROM8_FEM_TR_ISO_MASK 0x0700
+#define SROM8_FEM_TR_ISO_SHIFT 8
+#define SROM8_FEM_PDET_RANGE_MASK 0x00f8
+#define SROM8_FEM_PDET_RANGE_SHIFT 3
+#define SROM8_FEM_EXTPA_GAIN_MASK 0x0006
+#define SROM8_FEM_EXTPA_GAIN_SHIFT 1
+#define SROM8_FEM_TSSIPOS_MASK 0x0001
+#define SROM8_FEM_TSSIPOS_SHIFT 0
+
+#define SROM8_THERMAL 89
+
+/* Temp sense related entries */
+#define SROM8_MPWR_RAWTS 90
+#define SROM8_TS_SLP_OPT_CORRX 91
+/* FOC: freiquency offset correction, HWIQ: H/W IOCAL enable,
+ * IQSWP: IQ CAL swap disable */
+#define SROM8_FOC_HWIQ_IQSWP 92
+
+/* Temperature delta for PHY calibration */
+#define SROM8_PHYCAL_TEMPDELTA 93
+
+/* Per-path offsets & fields */
+#define SROM8_PATH0 96
+#define SROM8_PATH1 112
+#define SROM8_PATH2 128
+#define SROM8_PATH3 144
+
+#define SROM8_2G_ITT_MAXP 0
+#define SROM8_2G_PA 1
+#define SROM8_5G_ITT_MAXP 4
+#define SROM8_5GLH_MAXP 5
+#define SROM8_5G_PA 6
+#define SROM8_5GL_PA 9
+#define SROM8_5GH_PA 12
+
+/* All the miriad power offsets */
+#define SROM8_2G_CCKPO 160
+
+#define SROM8_2G_OFDMPO 161
+#define SROM8_5G_OFDMPO 163
+#define SROM8_5GL_OFDMPO 165
+#define SROM8_5GH_OFDMPO 167
+
+#define SROM8_2G_MCSPO 169
+#define SROM8_5G_MCSPO 177
+#define SROM8_5GL_MCSPO 185
+#define SROM8_5GH_MCSPO 193
+
+#define SROM8_CDDPO 201
+#define SROM8_STBCPO 202
+#define SROM8_BW40PO 203
+#define SROM8_BWDUPPO 204
+
+/* SISO PA parameters are in the path0 spaces */
+#define SROM8_SISO 96
+
+/* Legacy names for SISO PA paramters */
+#define SROM8_W0_ITTMAXP (SROM8_SISO + SROM8_2G_ITT_MAXP)
+#define SROM8_W0_PAB0 (SROM8_SISO + SROM8_2G_PA)
+#define SROM8_W0_PAB1 (SROM8_SISO + SROM8_2G_PA + 1)
+#define SROM8_W0_PAB2 (SROM8_SISO + SROM8_2G_PA + 2)
+#define SROM8_W1_ITTMAXP (SROM8_SISO + SROM8_5G_ITT_MAXP)
+#define SROM8_W1_MAXP_LCHC (SROM8_SISO + SROM8_5GLH_MAXP)
+#define SROM8_W1_PAB0 (SROM8_SISO + SROM8_5G_PA)
+#define SROM8_W1_PAB1 (SROM8_SISO + SROM8_5G_PA + 1)
+#define SROM8_W1_PAB2 (SROM8_SISO + SROM8_5G_PA + 2)
+#define SROM8_W1_PAB0_LC (SROM8_SISO + SROM8_5GL_PA)
+#define SROM8_W1_PAB1_LC (SROM8_SISO + SROM8_5GL_PA + 1)
+#define SROM8_W1_PAB2_LC (SROM8_SISO + SROM8_5GL_PA + 2)
+#define SROM8_W1_PAB0_HC (SROM8_SISO + SROM8_5GH_PA)
+#define SROM8_W1_PAB1_HC (SROM8_SISO + SROM8_5GH_PA + 1)
+#define SROM8_W1_PAB2_HC (SROM8_SISO + SROM8_5GH_PA + 2)
+
+/* SROM REV 9 */
+#define SROM9_2GPO_CCKBW20 160
+#define SROM9_2GPO_CCKBW20UL 161
+#define SROM9_2GPO_LOFDMBW20 162
+#define SROM9_2GPO_LOFDMBW20UL 164
+
+#define SROM9_5GLPO_LOFDMBW20 166
+#define SROM9_5GLPO_LOFDMBW20UL 168
+#define SROM9_5GMPO_LOFDMBW20 170
+#define SROM9_5GMPO_LOFDMBW20UL 172
+#define SROM9_5GHPO_LOFDMBW20 174
+#define SROM9_5GHPO_LOFDMBW20UL 176
+
+#define SROM9_2GPO_MCSBW20 178
+#define SROM9_2GPO_MCSBW20UL 180
+#define SROM9_2GPO_MCSBW40 182
+
+#define SROM9_5GLPO_MCSBW20 184
+#define SROM9_5GLPO_MCSBW20UL 186
+#define SROM9_5GLPO_MCSBW40 188
+#define SROM9_5GMPO_MCSBW20 190
+#define SROM9_5GMPO_MCSBW20UL 192
+#define SROM9_5GMPO_MCSBW40 194
+#define SROM9_5GHPO_MCSBW20 196
+#define SROM9_5GHPO_MCSBW20UL 198
+#define SROM9_5GHPO_MCSBW40 200
+
+#define SROM9_PO_MCS32 202
+#define SROM9_PO_LOFDM40DUP 203
+
+/* SROM flags (see sromvar_t) */
+
+/* value continues as described by the next entry */
+#define SRFL_MORE 1
+#define SRFL_NOFFS 2 /* value bits can't be all one's */
+#define SRFL_PRHEX 4 /* value is in hexdecimal format */
+#define SRFL_PRSIGN 8 /* value is in signed decimal format */
+#define SRFL_CCODE 0x10 /* value is in country code format */
+#define SRFL_ETHADDR 0x20 /* value is an Ethernet address */
+#define SRFL_LEDDC 0x40 /* value is an LED duty cycle */
+/* do not generate a nvram param, entry is for mfgc */
+#define SRFL_NOVAR 0x80
+
+/* Max. nvram variable table size */
+#define MAXSZ_NVRAM_VARS 4096
+
+/*
+ * indicates type of value.
+ */
+enum brcms_srom_var_type {
+ BRCMS_SROM_STRING,
+ BRCMS_SROM_SNUMBER,
+ BRCMS_SROM_UNUMBER
+};
+
+/*
+ * storage type for srom variable.
+ *
+ * var_list: for linked list operations.
+ * varid: identifier of the variable.
+ * var_type: type of variable.
+ * buf: variable value when var_type == BRCMS_SROM_STRING.
+ * uval: unsigned variable value when var_type == BRCMS_SROM_UNUMBER.
+ * sval: signed variable value when var_type == BRCMS_SROM_SNUMBER.
+ */
+struct brcms_srom_list_head {
+ struct list_head var_list;
+ enum brcms_srom_id varid;
+ enum brcms_srom_var_type var_type;
+ union {
+ char buf[0];
+ u32 uval;
+ s32 sval;
+ };
+};
+
+struct brcms_sromvar {
+ enum brcms_srom_id varid;
+ u32 revmask;
+ u32 flags;
+ u16 off;
+ u16 mask;
+};
+
+struct brcms_varbuf {
+ char *base; /* pointer to buffer base */
+ char *buf; /* pointer to current position */
+ unsigned int size; /* current (residual) size in bytes */
+};
+
+/*
+ * Assumptions:
+ * - Ethernet address spans across 3 consecutive words
+ *
+ * Table rules:
+ * - Add multiple entries next to each other if a value spans across multiple
+ * words (even multiple fields in the same word) with each entry except the
+ * last having it's SRFL_MORE bit set.
+ * - Ethernet address entry does not follow above rule and must not have
+ * SRFL_MORE bit set. Its SRFL_ETHADDR bit implies it takes multiple words.
+ * - The last entry's name field must be NULL to indicate the end of the table.
+ * Other entries must have non-NULL name.
+ */
+static const struct brcms_sromvar pci_sromvars[] = {
+ {BRCMS_SROM_DEVID, 0xffffff00, SRFL_PRHEX | SRFL_NOVAR, PCI_F0DEVID,
+ 0xffff},
+ {BRCMS_SROM_BOARDREV, 0x0000000e, SRFL_PRHEX, SROM_AABREV,
+ SROM_BR_MASK},
+ {BRCMS_SROM_BOARDREV, 0x000000f0, SRFL_PRHEX, SROM4_BREV, 0xffff},
+ {BRCMS_SROM_BOARDREV, 0xffffff00, SRFL_PRHEX, SROM8_BREV, 0xffff},
+ {BRCMS_SROM_BOARDFLAGS, 0x00000002, SRFL_PRHEX, SROM_BFL, 0xffff},
+ {BRCMS_SROM_BOARDFLAGS, 0x00000004, SRFL_PRHEX | SRFL_MORE, SROM_BFL,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM_BFL2, 0xffff},
+ {BRCMS_SROM_BOARDFLAGS, 0x00000008, SRFL_PRHEX | SRFL_MORE, SROM_BFL,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM3_BFL2, 0xffff},
+ {BRCMS_SROM_BOARDFLAGS, 0x00000010, SRFL_PRHEX | SRFL_MORE, SROM4_BFL0,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM4_BFL1, 0xffff},
+ {BRCMS_SROM_BOARDFLAGS, 0x000000e0, SRFL_PRHEX | SRFL_MORE, SROM5_BFL0,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM5_BFL1, 0xffff},
+ {BRCMS_SROM_BOARDFLAGS, 0xffffff00, SRFL_PRHEX | SRFL_MORE, SROM8_BFL0,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM8_BFL1, 0xffff},
+ {BRCMS_SROM_BOARDFLAGS2, 0x00000010, SRFL_PRHEX | SRFL_MORE, SROM4_BFL2,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM4_BFL3, 0xffff},
+ {BRCMS_SROM_BOARDFLAGS2, 0x000000e0, SRFL_PRHEX | SRFL_MORE, SROM5_BFL2,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM5_BFL3, 0xffff},
+ {BRCMS_SROM_BOARDFLAGS2, 0xffffff00, SRFL_PRHEX | SRFL_MORE, SROM8_BFL2,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM8_BFL3, 0xffff},
+ {BRCMS_SROM_BOARDTYPE, 0xfffffffc, SRFL_PRHEX, SROM_SSID, 0xffff},
+ {BRCMS_SROM_BOARDNUM, 0x00000006, 0, SROM_MACLO_IL0, 0xffff},
+ {BRCMS_SROM_BOARDNUM, 0x00000008, 0, SROM3_MACLO, 0xffff},
+ {BRCMS_SROM_BOARDNUM, 0x00000010, 0, SROM4_MACLO, 0xffff},
+ {BRCMS_SROM_BOARDNUM, 0x000000e0, 0, SROM5_MACLO, 0xffff},
+ {BRCMS_SROM_BOARDNUM, 0xffffff00, 0, SROM8_MACLO, 0xffff},
+ {BRCMS_SROM_CC, 0x00000002, 0, SROM_AABREV, SROM_CC_MASK},
+ {BRCMS_SROM_REGREV, 0x00000008, 0, SROM_OPO, 0xff00},
+ {BRCMS_SROM_REGREV, 0x00000010, 0, SROM4_REGREV, 0x00ff},
+ {BRCMS_SROM_REGREV, 0x000000e0, 0, SROM5_REGREV, 0x00ff},
+ {BRCMS_SROM_REGREV, 0xffffff00, 0, SROM8_REGREV, 0x00ff},
+ {BRCMS_SROM_LEDBH0, 0x0000000e, SRFL_NOFFS, SROM_LEDBH10, 0x00ff},
+ {BRCMS_SROM_LEDBH1, 0x0000000e, SRFL_NOFFS, SROM_LEDBH10, 0xff00},
+ {BRCMS_SROM_LEDBH2, 0x0000000e, SRFL_NOFFS, SROM_LEDBH32, 0x00ff},
+ {BRCMS_SROM_LEDBH3, 0x0000000e, SRFL_NOFFS, SROM_LEDBH32, 0xff00},
+ {BRCMS_SROM_LEDBH0, 0x00000010, SRFL_NOFFS, SROM4_LEDBH10, 0x00ff},
+ {BRCMS_SROM_LEDBH1, 0x00000010, SRFL_NOFFS, SROM4_LEDBH10, 0xff00},
+ {BRCMS_SROM_LEDBH2, 0x00000010, SRFL_NOFFS, SROM4_LEDBH32, 0x00ff},
+ {BRCMS_SROM_LEDBH3, 0x00000010, SRFL_NOFFS, SROM4_LEDBH32, 0xff00},
+ {BRCMS_SROM_LEDBH0, 0x000000e0, SRFL_NOFFS, SROM5_LEDBH10, 0x00ff},
+ {BRCMS_SROM_LEDBH1, 0x000000e0, SRFL_NOFFS, SROM5_LEDBH10, 0xff00},
+ {BRCMS_SROM_LEDBH2, 0x000000e0, SRFL_NOFFS, SROM5_LEDBH32, 0x00ff},
+ {BRCMS_SROM_LEDBH3, 0x000000e0, SRFL_NOFFS, SROM5_LEDBH32, 0xff00},
+ {BRCMS_SROM_LEDBH0, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH10, 0x00ff},
+ {BRCMS_SROM_LEDBH1, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH10, 0xff00},
+ {BRCMS_SROM_LEDBH2, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH32, 0x00ff},
+ {BRCMS_SROM_LEDBH3, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH32, 0xff00},
+ {BRCMS_SROM_PA0B0, 0x0000000e, SRFL_PRHEX, SROM_WL0PAB0, 0xffff},
+ {BRCMS_SROM_PA0B1, 0x0000000e, SRFL_PRHEX, SROM_WL0PAB1, 0xffff},
+ {BRCMS_SROM_PA0B2, 0x0000000e, SRFL_PRHEX, SROM_WL0PAB2, 0xffff},
+ {BRCMS_SROM_PA0ITSSIT, 0x0000000e, 0, SROM_ITT, 0x00ff},
+ {BRCMS_SROM_PA0MAXPWR, 0x0000000e, 0, SROM_WL10MAXP, 0x00ff},
+ {BRCMS_SROM_PA0B0, 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB0, 0xffff},
+ {BRCMS_SROM_PA0B1, 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB1, 0xffff},
+ {BRCMS_SROM_PA0B2, 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB2, 0xffff},
+ {BRCMS_SROM_PA0ITSSIT, 0xffffff00, 0, SROM8_W0_ITTMAXP, 0xff00},
+ {BRCMS_SROM_PA0MAXPWR, 0xffffff00, 0, SROM8_W0_ITTMAXP, 0x00ff},
+ {BRCMS_SROM_OPO, 0x0000000c, 0, SROM_OPO, 0x00ff},
+ {BRCMS_SROM_OPO, 0xffffff00, 0, SROM8_2G_OFDMPO, 0x00ff},
+ {BRCMS_SROM_AA2G, 0x0000000e, 0, SROM_AABREV, SROM_AA0_MASK},
+ {BRCMS_SROM_AA2G, 0x000000f0, 0, SROM4_AA, 0x00ff},
+ {BRCMS_SROM_AA2G, 0xffffff00, 0, SROM8_AA, 0x00ff},
+ {BRCMS_SROM_AA5G, 0x0000000e, 0, SROM_AABREV, SROM_AA1_MASK},
+ {BRCMS_SROM_AA5G, 0x000000f0, 0, SROM4_AA, 0xff00},
+ {BRCMS_SROM_AA5G, 0xffffff00, 0, SROM8_AA, 0xff00},
+ {BRCMS_SROM_AG0, 0x0000000e, 0, SROM_AG10, 0x00ff},
+ {BRCMS_SROM_AG1, 0x0000000e, 0, SROM_AG10, 0xff00},
+ {BRCMS_SROM_AG0, 0x000000f0, 0, SROM4_AG10, 0x00ff},
+ {BRCMS_SROM_AG1, 0x000000f0, 0, SROM4_AG10, 0xff00},
+ {BRCMS_SROM_AG2, 0x000000f0, 0, SROM4_AG32, 0x00ff},
+ {BRCMS_SROM_AG3, 0x000000f0, 0, SROM4_AG32, 0xff00},
+ {BRCMS_SROM_AG0, 0xffffff00, 0, SROM8_AG10, 0x00ff},
+ {BRCMS_SROM_AG1, 0xffffff00, 0, SROM8_AG10, 0xff00},
+ {BRCMS_SROM_AG2, 0xffffff00, 0, SROM8_AG32, 0x00ff},
+ {BRCMS_SROM_AG3, 0xffffff00, 0, SROM8_AG32, 0xff00},
+ {BRCMS_SROM_PA1B0, 0x0000000e, SRFL_PRHEX, SROM_WL1PAB0, 0xffff},
+ {BRCMS_SROM_PA1B1, 0x0000000e, SRFL_PRHEX, SROM_WL1PAB1, 0xffff},
+ {BRCMS_SROM_PA1B2, 0x0000000e, SRFL_PRHEX, SROM_WL1PAB2, 0xffff},
+ {BRCMS_SROM_PA1LOB0, 0x0000000c, SRFL_PRHEX, SROM_WL1LPAB0, 0xffff},
+ {BRCMS_SROM_PA1LOB1, 0x0000000c, SRFL_PRHEX, SROM_WL1LPAB1, 0xffff},
+ {BRCMS_SROM_PA1LOB2, 0x0000000c, SRFL_PRHEX, SROM_WL1LPAB2, 0xffff},
+ {BRCMS_SROM_PA1HIB0, 0x0000000c, SRFL_PRHEX, SROM_WL1HPAB0, 0xffff},
+ {BRCMS_SROM_PA1HIB1, 0x0000000c, SRFL_PRHEX, SROM_WL1HPAB1, 0xffff},
+ {BRCMS_SROM_PA1HIB2, 0x0000000c, SRFL_PRHEX, SROM_WL1HPAB2, 0xffff},
+ {BRCMS_SROM_PA1ITSSIT, 0x0000000e, 0, SROM_ITT, 0xff00},
+ {BRCMS_SROM_PA1MAXPWR, 0x0000000e, 0, SROM_WL10MAXP, 0xff00},
+ {BRCMS_SROM_PA1LOMAXPWR, 0x0000000c, 0, SROM_WL1LHMAXP, 0xff00},
+ {BRCMS_SROM_PA1HIMAXPWR, 0x0000000c, 0, SROM_WL1LHMAXP, 0x00ff},
+ {BRCMS_SROM_PA1B0, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB0, 0xffff},
+ {BRCMS_SROM_PA1B1, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB1, 0xffff},
+ {BRCMS_SROM_PA1B2, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB2, 0xffff},
+ {BRCMS_SROM_PA1LOB0, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB0_LC, 0xffff},
+ {BRCMS_SROM_PA1LOB1, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB1_LC, 0xffff},
+ {BRCMS_SROM_PA1LOB2, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB2_LC, 0xffff},
+ {BRCMS_SROM_PA1HIB0, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB0_HC, 0xffff},
+ {BRCMS_SROM_PA1HIB1, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB1_HC, 0xffff},
+ {BRCMS_SROM_PA1HIB2, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB2_HC, 0xffff},
+ {BRCMS_SROM_PA1ITSSIT, 0xffffff00, 0, SROM8_W1_ITTMAXP, 0xff00},
+ {BRCMS_SROM_PA1MAXPWR, 0xffffff00, 0, SROM8_W1_ITTMAXP, 0x00ff},
+ {BRCMS_SROM_PA1LOMAXPWR, 0xffffff00, 0, SROM8_W1_MAXP_LCHC, 0xff00},
+ {BRCMS_SROM_PA1HIMAXPWR, 0xffffff00, 0, SROM8_W1_MAXP_LCHC, 0x00ff},
+ {BRCMS_SROM_BXA2G, 0x00000008, 0, SROM_BXARSSI2G, 0x1800},
+ {BRCMS_SROM_RSSISAV2G, 0x00000008, 0, SROM_BXARSSI2G, 0x0700},
+ {BRCMS_SROM_RSSISMC2G, 0x00000008, 0, SROM_BXARSSI2G, 0x00f0},
+ {BRCMS_SROM_RSSISMF2G, 0x00000008, 0, SROM_BXARSSI2G, 0x000f},
+ {BRCMS_SROM_BXA2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x1800},
+ {BRCMS_SROM_RSSISAV2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x0700},
+ {BRCMS_SROM_RSSISMC2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x00f0},
+ {BRCMS_SROM_RSSISMF2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x000f},
+ {BRCMS_SROM_BXA5G, 0x00000008, 0, SROM_BXARSSI5G, 0x1800},
+ {BRCMS_SROM_RSSISAV5G, 0x00000008, 0, SROM_BXARSSI5G, 0x0700},
+ {BRCMS_SROM_RSSISMC5G, 0x00000008, 0, SROM_BXARSSI5G, 0x00f0},
+ {BRCMS_SROM_RSSISMF5G, 0x00000008, 0, SROM_BXARSSI5G, 0x000f},
+ {BRCMS_SROM_BXA5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x1800},
+ {BRCMS_SROM_RSSISAV5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x0700},
+ {BRCMS_SROM_RSSISMC5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x00f0},
+ {BRCMS_SROM_RSSISMF5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x000f},
+ {BRCMS_SROM_TRI2G, 0x00000008, 0, SROM_TRI52G, 0x00ff},
+ {BRCMS_SROM_TRI5G, 0x00000008, 0, SROM_TRI52G, 0xff00},
+ {BRCMS_SROM_TRI5GL, 0x00000008, 0, SROM_TRI5GHL, 0x00ff},
+ {BRCMS_SROM_TRI5GH, 0x00000008, 0, SROM_TRI5GHL, 0xff00},
+ {BRCMS_SROM_TRI2G, 0xffffff00, 0, SROM8_TRI52G, 0x00ff},
+ {BRCMS_SROM_TRI5G, 0xffffff00, 0, SROM8_TRI52G, 0xff00},
+ {BRCMS_SROM_TRI5GL, 0xffffff00, 0, SROM8_TRI5GHL, 0x00ff},
+ {BRCMS_SROM_TRI5GH, 0xffffff00, 0, SROM8_TRI5GHL, 0xff00},
+ {BRCMS_SROM_RXPO2G, 0x00000008, SRFL_PRSIGN, SROM_RXPO52G, 0x00ff},
+ {BRCMS_SROM_RXPO5G, 0x00000008, SRFL_PRSIGN, SROM_RXPO52G, 0xff00},
+ {BRCMS_SROM_RXPO2G, 0xffffff00, SRFL_PRSIGN, SROM8_RXPO52G, 0x00ff},
+ {BRCMS_SROM_RXPO5G, 0xffffff00, SRFL_PRSIGN, SROM8_RXPO52G, 0xff00},
+ {BRCMS_SROM_TXCHAIN, 0x000000f0, SRFL_NOFFS, SROM4_TXRXC,
+ SROM4_TXCHAIN_MASK},
+ {BRCMS_SROM_RXCHAIN, 0x000000f0, SRFL_NOFFS, SROM4_TXRXC,
+ SROM4_RXCHAIN_MASK},
+ {BRCMS_SROM_ANTSWITCH, 0x000000f0, SRFL_NOFFS, SROM4_TXRXC,
+ SROM4_SWITCH_MASK},
+ {BRCMS_SROM_TXCHAIN, 0xffffff00, SRFL_NOFFS, SROM8_TXRXC,
+ SROM4_TXCHAIN_MASK},
+ {BRCMS_SROM_RXCHAIN, 0xffffff00, SRFL_NOFFS, SROM8_TXRXC,
+ SROM4_RXCHAIN_MASK},
+ {BRCMS_SROM_ANTSWITCH, 0xffffff00, SRFL_NOFFS, SROM8_TXRXC,
+ SROM4_SWITCH_MASK},
+ {BRCMS_SROM_TSSIPOS2G, 0xffffff00, 0, SROM8_FEM2G,
+ SROM8_FEM_TSSIPOS_MASK},
+ {BRCMS_SROM_EXTPAGAIN2G, 0xffffff00, 0, SROM8_FEM2G,
+ SROM8_FEM_EXTPA_GAIN_MASK},
+ {BRCMS_SROM_PDETRANGE2G, 0xffffff00, 0, SROM8_FEM2G,
+ SROM8_FEM_PDET_RANGE_MASK},
+ {BRCMS_SROM_TRISO2G, 0xffffff00, 0, SROM8_FEM2G, SROM8_FEM_TR_ISO_MASK},
+ {BRCMS_SROM_ANTSWCTL2G, 0xffffff00, 0, SROM8_FEM2G,
+ SROM8_FEM_ANTSWLUT_MASK},
+ {BRCMS_SROM_TSSIPOS5G, 0xffffff00, 0, SROM8_FEM5G,
+ SROM8_FEM_TSSIPOS_MASK},
+ {BRCMS_SROM_EXTPAGAIN5G, 0xffffff00, 0, SROM8_FEM5G,
+ SROM8_FEM_EXTPA_GAIN_MASK},
+ {BRCMS_SROM_PDETRANGE5G, 0xffffff00, 0, SROM8_FEM5G,
+ SROM8_FEM_PDET_RANGE_MASK},
+ {BRCMS_SROM_TRISO5G, 0xffffff00, 0, SROM8_FEM5G, SROM8_FEM_TR_ISO_MASK},
+ {BRCMS_SROM_ANTSWCTL5G, 0xffffff00, 0, SROM8_FEM5G,
+ SROM8_FEM_ANTSWLUT_MASK},
+ {BRCMS_SROM_TEMPTHRESH, 0xffffff00, 0, SROM8_THERMAL, 0xff00},
+ {BRCMS_SROM_TEMPOFFSET, 0xffffff00, 0, SROM8_THERMAL, 0x00ff},
+ {BRCMS_SROM_TXPID2GA0, 0x000000f0, 0, SROM4_TXPID2G, 0x00ff},
+ {BRCMS_SROM_TXPID2GA1, 0x000000f0, 0, SROM4_TXPID2G, 0xff00},
+ {BRCMS_SROM_TXPID2GA2, 0x000000f0, 0, SROM4_TXPID2G + 1, 0x00ff},
+ {BRCMS_SROM_TXPID2GA3, 0x000000f0, 0, SROM4_TXPID2G + 1, 0xff00},
+ {BRCMS_SROM_TXPID5GA0, 0x000000f0, 0, SROM4_TXPID5G, 0x00ff},
+ {BRCMS_SROM_TXPID5GA1, 0x000000f0, 0, SROM4_TXPID5G, 0xff00},
+ {BRCMS_SROM_TXPID5GA2, 0x000000f0, 0, SROM4_TXPID5G + 1, 0x00ff},
+ {BRCMS_SROM_TXPID5GA3, 0x000000f0, 0, SROM4_TXPID5G + 1, 0xff00},
+ {BRCMS_SROM_TXPID5GLA0, 0x000000f0, 0, SROM4_TXPID5GL, 0x00ff},
+ {BRCMS_SROM_TXPID5GLA1, 0x000000f0, 0, SROM4_TXPID5GL, 0xff00},
+ {BRCMS_SROM_TXPID5GLA2, 0x000000f0, 0, SROM4_TXPID5GL + 1, 0x00ff},
+ {BRCMS_SROM_TXPID5GLA3, 0x000000f0, 0, SROM4_TXPID5GL + 1, 0xff00},
+ {BRCMS_SROM_TXPID5GHA0, 0x000000f0, 0, SROM4_TXPID5GH, 0x00ff},
+ {BRCMS_SROM_TXPID5GHA1, 0x000000f0, 0, SROM4_TXPID5GH, 0xff00},
+ {BRCMS_SROM_TXPID5GHA2, 0x000000f0, 0, SROM4_TXPID5GH + 1, 0x00ff},
+ {BRCMS_SROM_TXPID5GHA3, 0x000000f0, 0, SROM4_TXPID5GH + 1, 0xff00},
+
+ {BRCMS_SROM_CCODE, 0x0000000f, SRFL_CCODE, SROM_CCODE, 0xffff},
+ {BRCMS_SROM_CCODE, 0x00000010, SRFL_CCODE, SROM4_CCODE, 0xffff},
+ {BRCMS_SROM_CCODE, 0x000000e0, SRFL_CCODE, SROM5_CCODE, 0xffff},
+ {BRCMS_SROM_CCODE, 0xffffff00, SRFL_CCODE, SROM8_CCODE, 0xffff},
+ {BRCMS_SROM_MACADDR, 0xffffff00, SRFL_ETHADDR, SROM8_MACHI, 0xffff},
+ {BRCMS_SROM_MACADDR, 0x000000e0, SRFL_ETHADDR, SROM5_MACHI, 0xffff},
+ {BRCMS_SROM_MACADDR, 0x00000010, SRFL_ETHADDR, SROM4_MACHI, 0xffff},
+ {BRCMS_SROM_MACADDR, 0x00000008, SRFL_ETHADDR, SROM3_MACHI, 0xffff},
+ {BRCMS_SROM_IL0MACADDR, 0x00000007, SRFL_ETHADDR, SROM_MACHI_IL0,
+ 0xffff},
+ {BRCMS_SROM_ET1MACADDR, 0x00000007, SRFL_ETHADDR, SROM_MACHI_ET1,
+ 0xffff},
+ {BRCMS_SROM_LEDDC, 0xffffff00, SRFL_NOFFS | SRFL_LEDDC, SROM8_LEDDC,
+ 0xffff},
+ {BRCMS_SROM_LEDDC, 0x000000e0, SRFL_NOFFS | SRFL_LEDDC, SROM5_LEDDC,
+ 0xffff},
+ {BRCMS_SROM_LEDDC, 0x00000010, SRFL_NOFFS | SRFL_LEDDC, SROM4_LEDDC,
+ 0xffff},
+ {BRCMS_SROM_LEDDC, 0x00000008, SRFL_NOFFS | SRFL_LEDDC, SROM3_LEDDC,
+ 0xffff},
+ {BRCMS_SROM_RAWTEMPSENSE, 0xffffff00, SRFL_PRHEX, SROM8_MPWR_RAWTS,
+ 0x01ff},
+ {BRCMS_SROM_MEASPOWER, 0xffffff00, SRFL_PRHEX, SROM8_MPWR_RAWTS,
+ 0xfe00},
+ {BRCMS_SROM_TEMPSENSE_SLOPE, 0xffffff00, SRFL_PRHEX,
+ SROM8_TS_SLP_OPT_CORRX, 0x00ff},
+ {BRCMS_SROM_TEMPCORRX, 0xffffff00, SRFL_PRHEX, SROM8_TS_SLP_OPT_CORRX,
+ 0xfc00},
+ {BRCMS_SROM_TEMPSENSE_OPTION, 0xffffff00, SRFL_PRHEX,
+ SROM8_TS_SLP_OPT_CORRX, 0x0300},
+ {BRCMS_SROM_FREQOFFSET_CORR, 0xffffff00, SRFL_PRHEX,
+ SROM8_FOC_HWIQ_IQSWP, 0x000f},
+ {BRCMS_SROM_IQCAL_SWP_DIS, 0xffffff00, SRFL_PRHEX, SROM8_FOC_HWIQ_IQSWP,
+ 0x0010},
+ {BRCMS_SROM_HW_IQCAL_EN, 0xffffff00, SRFL_PRHEX, SROM8_FOC_HWIQ_IQSWP,
+ 0x0020},
+ {BRCMS_SROM_PHYCAL_TEMPDELTA, 0xffffff00, 0, SROM8_PHYCAL_TEMPDELTA,
+ 0x00ff},
+
+ {BRCMS_SROM_CCK2GPO, 0x000000f0, 0, SROM4_2G_CCKPO, 0xffff},
+ {BRCMS_SROM_CCK2GPO, 0x00000100, 0, SROM8_2G_CCKPO, 0xffff},
+ {BRCMS_SROM_OFDM2GPO, 0x000000f0, SRFL_MORE, SROM4_2G_OFDMPO, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM4_2G_OFDMPO + 1, 0xffff},
+ {BRCMS_SROM_OFDM5GPO, 0x000000f0, SRFL_MORE, SROM4_5G_OFDMPO, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM4_5G_OFDMPO + 1, 0xffff},
+ {BRCMS_SROM_OFDM5GLPO, 0x000000f0, SRFL_MORE, SROM4_5GL_OFDMPO, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM4_5GL_OFDMPO + 1, 0xffff},
+ {BRCMS_SROM_OFDM5GHPO, 0x000000f0, SRFL_MORE, SROM4_5GH_OFDMPO, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM4_5GH_OFDMPO + 1, 0xffff},
+ {BRCMS_SROM_OFDM2GPO, 0x00000100, SRFL_MORE, SROM8_2G_OFDMPO, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM8_2G_OFDMPO + 1, 0xffff},
+ {BRCMS_SROM_OFDM5GPO, 0x00000100, SRFL_MORE, SROM8_5G_OFDMPO, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM8_5G_OFDMPO + 1, 0xffff},
+ {BRCMS_SROM_OFDM5GLPO, 0x00000100, SRFL_MORE, SROM8_5GL_OFDMPO, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM8_5GL_OFDMPO + 1, 0xffff},
+ {BRCMS_SROM_OFDM5GHPO, 0x00000100, SRFL_MORE, SROM8_5GH_OFDMPO, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM8_5GH_OFDMPO + 1, 0xffff},
+ {BRCMS_SROM_MCS2GPO0, 0x000000f0, 0, SROM4_2G_MCSPO, 0xffff},
+ {BRCMS_SROM_MCS2GPO1, 0x000000f0, 0, SROM4_2G_MCSPO + 1, 0xffff},
+ {BRCMS_SROM_MCS2GPO2, 0x000000f0, 0, SROM4_2G_MCSPO + 2, 0xffff},
+ {BRCMS_SROM_MCS2GPO3, 0x000000f0, 0, SROM4_2G_MCSPO + 3, 0xffff},
+ {BRCMS_SROM_MCS2GPO4, 0x000000f0, 0, SROM4_2G_MCSPO + 4, 0xffff},
+ {BRCMS_SROM_MCS2GPO5, 0x000000f0, 0, SROM4_2G_MCSPO + 5, 0xffff},
+ {BRCMS_SROM_MCS2GPO6, 0x000000f0, 0, SROM4_2G_MCSPO + 6, 0xffff},
+ {BRCMS_SROM_MCS2GPO7, 0x000000f0, 0, SROM4_2G_MCSPO + 7, 0xffff},
+ {BRCMS_SROM_MCS5GPO0, 0x000000f0, 0, SROM4_5G_MCSPO, 0xffff},
+ {BRCMS_SROM_MCS5GPO1, 0x000000f0, 0, SROM4_5G_MCSPO + 1, 0xffff},
+ {BRCMS_SROM_MCS5GPO2, 0x000000f0, 0, SROM4_5G_MCSPO + 2, 0xffff},
+ {BRCMS_SROM_MCS5GPO3, 0x000000f0, 0, SROM4_5G_MCSPO + 3, 0xffff},
+ {BRCMS_SROM_MCS5GPO4, 0x000000f0, 0, SROM4_5G_MCSPO + 4, 0xffff},
+ {BRCMS_SROM_MCS5GPO5, 0x000000f0, 0, SROM4_5G_MCSPO + 5, 0xffff},
+ {BRCMS_SROM_MCS5GPO6, 0x000000f0, 0, SROM4_5G_MCSPO + 6, 0xffff},
+ {BRCMS_SROM_MCS5GPO7, 0x000000f0, 0, SROM4_5G_MCSPO + 7, 0xffff},
+ {BRCMS_SROM_MCS5GLPO0, 0x000000f0, 0, SROM4_5GL_MCSPO, 0xffff},
+ {BRCMS_SROM_MCS5GLPO1, 0x000000f0, 0, SROM4_5GL_MCSPO + 1, 0xffff},
+ {BRCMS_SROM_MCS5GLPO2, 0x000000f0, 0, SROM4_5GL_MCSPO + 2, 0xffff},
+ {BRCMS_SROM_MCS5GLPO3, 0x000000f0, 0, SROM4_5GL_MCSPO + 3, 0xffff},
+ {BRCMS_SROM_MCS5GLPO4, 0x000000f0, 0, SROM4_5GL_MCSPO + 4, 0xffff},
+ {BRCMS_SROM_MCS5GLPO5, 0x000000f0, 0, SROM4_5GL_MCSPO + 5, 0xffff},
+ {BRCMS_SROM_MCS5GLPO6, 0x000000f0, 0, SROM4_5GL_MCSPO + 6, 0xffff},
+ {BRCMS_SROM_MCS5GLPO7, 0x000000f0, 0, SROM4_5GL_MCSPO + 7, 0xffff},
+ {BRCMS_SROM_MCS5GHPO0, 0x000000f0, 0, SROM4_5GH_MCSPO, 0xffff},
+ {BRCMS_SROM_MCS5GHPO1, 0x000000f0, 0, SROM4_5GH_MCSPO + 1, 0xffff},
+ {BRCMS_SROM_MCS5GHPO2, 0x000000f0, 0, SROM4_5GH_MCSPO + 2, 0xffff},
+ {BRCMS_SROM_MCS5GHPO3, 0x000000f0, 0, SROM4_5GH_MCSPO + 3, 0xffff},
+ {BRCMS_SROM_MCS5GHPO4, 0x000000f0, 0, SROM4_5GH_MCSPO + 4, 0xffff},
+ {BRCMS_SROM_MCS5GHPO5, 0x000000f0, 0, SROM4_5GH_MCSPO + 5, 0xffff},
+ {BRCMS_SROM_MCS5GHPO6, 0x000000f0, 0, SROM4_5GH_MCSPO + 6, 0xffff},
+ {BRCMS_SROM_MCS5GHPO7, 0x000000f0, 0, SROM4_5GH_MCSPO + 7, 0xffff},
+ {BRCMS_SROM_MCS2GPO0, 0x00000100, 0, SROM8_2G_MCSPO, 0xffff},
+ {BRCMS_SROM_MCS2GPO1, 0x00000100, 0, SROM8_2G_MCSPO + 1, 0xffff},
+ {BRCMS_SROM_MCS2GPO2, 0x00000100, 0, SROM8_2G_MCSPO + 2, 0xffff},
+ {BRCMS_SROM_MCS2GPO3, 0x00000100, 0, SROM8_2G_MCSPO + 3, 0xffff},
+ {BRCMS_SROM_MCS2GPO4, 0x00000100, 0, SROM8_2G_MCSPO + 4, 0xffff},
+ {BRCMS_SROM_MCS2GPO5, 0x00000100, 0, SROM8_2G_MCSPO + 5, 0xffff},
+ {BRCMS_SROM_MCS2GPO6, 0x00000100, 0, SROM8_2G_MCSPO + 6, 0xffff},
+ {BRCMS_SROM_MCS2GPO7, 0x00000100, 0, SROM8_2G_MCSPO + 7, 0xffff},
+ {BRCMS_SROM_MCS5GPO0, 0x00000100, 0, SROM8_5G_MCSPO, 0xffff},
+ {BRCMS_SROM_MCS5GPO1, 0x00000100, 0, SROM8_5G_MCSPO + 1, 0xffff},
+ {BRCMS_SROM_MCS5GPO2, 0x00000100, 0, SROM8_5G_MCSPO + 2, 0xffff},
+ {BRCMS_SROM_MCS5GPO3, 0x00000100, 0, SROM8_5G_MCSPO + 3, 0xffff},
+ {BRCMS_SROM_MCS5GPO4, 0x00000100, 0, SROM8_5G_MCSPO + 4, 0xffff},
+ {BRCMS_SROM_MCS5GPO5, 0x00000100, 0, SROM8_5G_MCSPO + 5, 0xffff},
+ {BRCMS_SROM_MCS5GPO6, 0x00000100, 0, SROM8_5G_MCSPO + 6, 0xffff},
+ {BRCMS_SROM_MCS5GPO7, 0x00000100, 0, SROM8_5G_MCSPO + 7, 0xffff},
+ {BRCMS_SROM_MCS5GLPO0, 0x00000100, 0, SROM8_5GL_MCSPO, 0xffff},
+ {BRCMS_SROM_MCS5GLPO1, 0x00000100, 0, SROM8_5GL_MCSPO + 1, 0xffff},
+ {BRCMS_SROM_MCS5GLPO2, 0x00000100, 0, SROM8_5GL_MCSPO + 2, 0xffff},
+ {BRCMS_SROM_MCS5GLPO3, 0x00000100, 0, SROM8_5GL_MCSPO + 3, 0xffff},
+ {BRCMS_SROM_MCS5GLPO4, 0x00000100, 0, SROM8_5GL_MCSPO + 4, 0xffff},
+ {BRCMS_SROM_MCS5GLPO5, 0x00000100, 0, SROM8_5GL_MCSPO + 5, 0xffff},
+ {BRCMS_SROM_MCS5GLPO6, 0x00000100, 0, SROM8_5GL_MCSPO + 6, 0xffff},
+ {BRCMS_SROM_MCS5GLPO7, 0x00000100, 0, SROM8_5GL_MCSPO + 7, 0xffff},
+ {BRCMS_SROM_MCS5GHPO0, 0x00000100, 0, SROM8_5GH_MCSPO, 0xffff},
+ {BRCMS_SROM_MCS5GHPO1, 0x00000100, 0, SROM8_5GH_MCSPO + 1, 0xffff},
+ {BRCMS_SROM_MCS5GHPO2, 0x00000100, 0, SROM8_5GH_MCSPO + 2, 0xffff},
+ {BRCMS_SROM_MCS5GHPO3, 0x00000100, 0, SROM8_5GH_MCSPO + 3, 0xffff},
+ {BRCMS_SROM_MCS5GHPO4, 0x00000100, 0, SROM8_5GH_MCSPO + 4, 0xffff},
+ {BRCMS_SROM_MCS5GHPO5, 0x00000100, 0, SROM8_5GH_MCSPO + 5, 0xffff},
+ {BRCMS_SROM_MCS5GHPO6, 0x00000100, 0, SROM8_5GH_MCSPO + 6, 0xffff},
+ {BRCMS_SROM_MCS5GHPO7, 0x00000100, 0, SROM8_5GH_MCSPO + 7, 0xffff},
+ {BRCMS_SROM_CDDPO, 0x000000f0, 0, SROM4_CDDPO, 0xffff},
+ {BRCMS_SROM_STBCPO, 0x000000f0, 0, SROM4_STBCPO, 0xffff},
+ {BRCMS_SROM_BW40PO, 0x000000f0, 0, SROM4_BW40PO, 0xffff},
+ {BRCMS_SROM_BWDUPPO, 0x000000f0, 0, SROM4_BWDUPPO, 0xffff},
+ {BRCMS_SROM_CDDPO, 0x00000100, 0, SROM8_CDDPO, 0xffff},
+ {BRCMS_SROM_STBCPO, 0x00000100, 0, SROM8_STBCPO, 0xffff},
+ {BRCMS_SROM_BW40PO, 0x00000100, 0, SROM8_BW40PO, 0xffff},
+ {BRCMS_SROM_BWDUPPO, 0x00000100, 0, SROM8_BWDUPPO, 0xffff},
+
+ /* power per rate from sromrev 9 */
+ {BRCMS_SROM_CCKBW202GPO, 0xfffffe00, 0, SROM9_2GPO_CCKBW20, 0xffff},
+ {BRCMS_SROM_CCKBW20UL2GPO, 0xfffffe00, 0, SROM9_2GPO_CCKBW20UL, 0xffff},
+ {BRCMS_SROM_LEGOFDMBW202GPO, 0xfffffe00, SRFL_MORE,
+ SROM9_2GPO_LOFDMBW20, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_2GPO_LOFDMBW20 + 1, 0xffff},
+ {BRCMS_SROM_LEGOFDMBW20UL2GPO, 0xfffffe00, SRFL_MORE,
+ SROM9_2GPO_LOFDMBW20UL, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_2GPO_LOFDMBW20UL + 1, 0xffff},
+ {BRCMS_SROM_LEGOFDMBW205GLPO, 0xfffffe00, SRFL_MORE,
+ SROM9_5GLPO_LOFDMBW20, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GLPO_LOFDMBW20 + 1, 0xffff},
+ {BRCMS_SROM_LEGOFDMBW20UL5GLPO, 0xfffffe00, SRFL_MORE,
+ SROM9_5GLPO_LOFDMBW20UL, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GLPO_LOFDMBW20UL + 1, 0xffff},
+ {BRCMS_SROM_LEGOFDMBW205GMPO, 0xfffffe00, SRFL_MORE,
+ SROM9_5GMPO_LOFDMBW20, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GMPO_LOFDMBW20 + 1, 0xffff},
+ {BRCMS_SROM_LEGOFDMBW20UL5GMPO, 0xfffffe00, SRFL_MORE,
+ SROM9_5GMPO_LOFDMBW20UL, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GMPO_LOFDMBW20UL + 1, 0xffff},
+ {BRCMS_SROM_LEGOFDMBW205GHPO, 0xfffffe00, SRFL_MORE,
+ SROM9_5GHPO_LOFDMBW20, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GHPO_LOFDMBW20 + 1, 0xffff},
+ {BRCMS_SROM_LEGOFDMBW20UL5GHPO, 0xfffffe00, SRFL_MORE,
+ SROM9_5GHPO_LOFDMBW20UL, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GHPO_LOFDMBW20UL + 1, 0xffff},
+ {BRCMS_SROM_MCSBW202GPO, 0xfffffe00, SRFL_MORE, SROM9_2GPO_MCSBW20,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_2GPO_MCSBW20 + 1, 0xffff},
+ {BRCMS_SROM_MCSBW20UL2GPO, 0xfffffe00, SRFL_MORE, SROM9_2GPO_MCSBW20UL,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_2GPO_MCSBW20UL + 1, 0xffff},
+ {BRCMS_SROM_MCSBW402GPO, 0xfffffe00, SRFL_MORE, SROM9_2GPO_MCSBW40,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_2GPO_MCSBW40 + 1, 0xffff},
+ {BRCMS_SROM_MCSBW205GLPO, 0xfffffe00, SRFL_MORE, SROM9_5GLPO_MCSBW20,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GLPO_MCSBW20 + 1, 0xffff},
+ {BRCMS_SROM_MCSBW20UL5GLPO, 0xfffffe00, SRFL_MORE,
+ SROM9_5GLPO_MCSBW20UL, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GLPO_MCSBW20UL + 1, 0xffff},
+ {BRCMS_SROM_MCSBW405GLPO, 0xfffffe00, SRFL_MORE, SROM9_5GLPO_MCSBW40,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GLPO_MCSBW40 + 1, 0xffff},
+ {BRCMS_SROM_MCSBW205GMPO, 0xfffffe00, SRFL_MORE, SROM9_5GMPO_MCSBW20,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GMPO_MCSBW20 + 1, 0xffff},
+ {BRCMS_SROM_MCSBW20UL5GMPO, 0xfffffe00, SRFL_MORE,
+ SROM9_5GMPO_MCSBW20UL, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GMPO_MCSBW20UL + 1, 0xffff},
+ {BRCMS_SROM_MCSBW405GMPO, 0xfffffe00, SRFL_MORE, SROM9_5GMPO_MCSBW40,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GMPO_MCSBW40 + 1, 0xffff},
+ {BRCMS_SROM_MCSBW205GHPO, 0xfffffe00, SRFL_MORE, SROM9_5GHPO_MCSBW20,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GHPO_MCSBW20 + 1, 0xffff},
+ {BRCMS_SROM_MCSBW20UL5GHPO, 0xfffffe00, SRFL_MORE,
+ SROM9_5GHPO_MCSBW20UL, 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GHPO_MCSBW20UL + 1, 0xffff},
+ {BRCMS_SROM_MCSBW405GHPO, 0xfffffe00, SRFL_MORE, SROM9_5GHPO_MCSBW40,
+ 0xffff},
+ {BRCMS_SROM_CONT, 0, 0, SROM9_5GHPO_MCSBW40 + 1, 0xffff},
+ {BRCMS_SROM_MCS32PO, 0xfffffe00, 0, SROM9_PO_MCS32, 0xffff},
+ {BRCMS_SROM_LEGOFDM40DUPPO, 0xfffffe00, 0, SROM9_PO_LOFDM40DUP, 0xffff},
+
+ {BRCMS_SROM_NULL, 0, 0, 0, 0}
+};
+
+static const struct brcms_sromvar perpath_pci_sromvars[] = {
+ {BRCMS_SROM_MAXP2GA0, 0x000000f0, 0, SROM4_2G_ITT_MAXP, 0x00ff},
+ {BRCMS_SROM_ITT2GA0, 0x000000f0, 0, SROM4_2G_ITT_MAXP, 0xff00},
+ {BRCMS_SROM_ITT5GA0, 0x000000f0, 0, SROM4_5G_ITT_MAXP, 0xff00},
+ {BRCMS_SROM_PA2GW0A0, 0x000000f0, SRFL_PRHEX, SROM4_2G_PA, 0xffff},
+ {BRCMS_SROM_PA2GW1A0, 0x000000f0, SRFL_PRHEX, SROM4_2G_PA + 1, 0xffff},
+ {BRCMS_SROM_PA2GW2A0, 0x000000f0, SRFL_PRHEX, SROM4_2G_PA + 2, 0xffff},
+ {BRCMS_SROM_PA2GW3A0, 0x000000f0, SRFL_PRHEX, SROM4_2G_PA + 3, 0xffff},
+ {BRCMS_SROM_MAXP5GA0, 0x000000f0, 0, SROM4_5G_ITT_MAXP, 0x00ff},
+ {BRCMS_SROM_MAXP5GHA0, 0x000000f0, 0, SROM4_5GLH_MAXP, 0x00ff},
+ {BRCMS_SROM_MAXP5GLA0, 0x000000f0, 0, SROM4_5GLH_MAXP, 0xff00},
+ {BRCMS_SROM_PA5GW0A0, 0x000000f0, SRFL_PRHEX, SROM4_5G_PA, 0xffff},
+ {BRCMS_SROM_PA5GW1A0, 0x000000f0, SRFL_PRHEX, SROM4_5G_PA + 1, 0xffff},
+ {BRCMS_SROM_PA5GW2A0, 0x000000f0, SRFL_PRHEX, SROM4_5G_PA + 2, 0xffff},
+ {BRCMS_SROM_PA5GW3A0, 0x000000f0, SRFL_PRHEX, SROM4_5G_PA + 3, 0xffff},
+ {BRCMS_SROM_PA5GLW0A0, 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA, 0xffff},
+ {BRCMS_SROM_PA5GLW1A0, 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA + 1,
+ 0xffff},
+ {BRCMS_SROM_PA5GLW2A0, 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA + 2,
+ 0xffff},
+ {BRCMS_SROM_PA5GLW3A0, 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA + 3,
+ 0xffff},
+ {BRCMS_SROM_PA5GHW0A0, 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA, 0xffff},
+ {BRCMS_SROM_PA5GHW1A0, 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA + 1,
+ 0xffff},
+ {BRCMS_SROM_PA5GHW2A0, 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA + 2,
+ 0xffff},
+ {BRCMS_SROM_PA5GHW3A0, 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA + 3,
+ 0xffff},
+ {BRCMS_SROM_MAXP2GA0, 0xffffff00, 0, SROM8_2G_ITT_MAXP, 0x00ff},
+ {BRCMS_SROM_ITT2GA0, 0xffffff00, 0, SROM8_2G_ITT_MAXP, 0xff00},
+ {BRCMS_SROM_ITT5GA0, 0xffffff00, 0, SROM8_5G_ITT_MAXP, 0xff00},
+ {BRCMS_SROM_PA2GW0A0, 0xffffff00, SRFL_PRHEX, SROM8_2G_PA, 0xffff},
+ {BRCMS_SROM_PA2GW1A0, 0xffffff00, SRFL_PRHEX, SROM8_2G_PA + 1, 0xffff},
+ {BRCMS_SROM_PA2GW2A0, 0xffffff00, SRFL_PRHEX, SROM8_2G_PA + 2, 0xffff},
+ {BRCMS_SROM_MAXP5GA0, 0xffffff00, 0, SROM8_5G_ITT_MAXP, 0x00ff},
+ {BRCMS_SROM_MAXP5GHA0, 0xffffff00, 0, SROM8_5GLH_MAXP, 0x00ff},
+ {BRCMS_SROM_MAXP5GLA0, 0xffffff00, 0, SROM8_5GLH_MAXP, 0xff00},
+ {BRCMS_SROM_PA5GW0A0, 0xffffff00, SRFL_PRHEX, SROM8_5G_PA, 0xffff},
+ {BRCMS_SROM_PA5GW1A0, 0xffffff00, SRFL_PRHEX, SROM8_5G_PA + 1, 0xffff},
+ {BRCMS_SROM_PA5GW2A0, 0xffffff00, SRFL_PRHEX, SROM8_5G_PA + 2, 0xffff},
+ {BRCMS_SROM_PA5GLW0A0, 0xffffff00, SRFL_PRHEX, SROM8_5GL_PA, 0xffff},
+ {BRCMS_SROM_PA5GLW1A0, 0xffffff00, SRFL_PRHEX, SROM8_5GL_PA + 1,
+ 0xffff},
+ {BRCMS_SROM_PA5GLW2A0, 0xffffff00, SRFL_PRHEX, SROM8_5GL_PA + 2,
+ 0xffff},
+ {BRCMS_SROM_PA5GHW0A0, 0xffffff00, SRFL_PRHEX, SROM8_5GH_PA, 0xffff},
+ {BRCMS_SROM_PA5GHW1A0, 0xffffff00, SRFL_PRHEX, SROM8_5GH_PA + 1,
+ 0xffff},
+ {BRCMS_SROM_PA5GHW2A0, 0xffffff00, SRFL_PRHEX, SROM8_5GH_PA + 2,
+ 0xffff},
+ {BRCMS_SROM_NULL, 0, 0, 0, 0}
+};
+
+/* crc table has the same contents for every device instance, so it can be
+ * shared between devices. */
+static u8 brcms_srom_crc8_table[CRC8_TABLE_SIZE];
+
+static u16 __iomem *
+srom_window_address(struct si_pub *sih, u8 __iomem *curmap)
+{
+ if (sih->ccrev < 32)
+ return (u16 __iomem *)(curmap + PCI_BAR0_SPROM_OFFSET);
+ if (sih->cccaps & CC_CAP_SROM)
+ return (u16 __iomem *)
+ (curmap + PCI_16KB0_CCREGS_OFFSET + CC_SROM_OTP);
+
+ return NULL;
+}
+
+/* Parse SROM and create name=value pairs. 'srom' points to
+ * the SROM word array. 'off' specifies the offset of the
+ * first word 'srom' points to, which should be either 0 or
+ * SROM3_SWRG_OFF (full SROM or software region).
+ */
+
+static uint mask_shift(u16 mask)
+{
+ uint i;
+ for (i = 0; i < (sizeof(mask) << 3); i++) {
+ if (mask & (1 << i))
+ return i;
+ }
+ return 0;
+}
+
+static uint mask_width(u16 mask)
+{
+ int i;
+ for (i = (sizeof(mask) << 3) - 1; i >= 0; i--) {
+ if (mask & (1 << i))
+ return (uint) (i - mask_shift(mask) + 1);
+ }
+ return 0;
+}
+
+static inline void ltoh16_buf(u16 *buf, unsigned int size)
+{
+ size /= 2;
+ while (size--)
+ *(buf + size) = le16_to_cpu(*(__le16 *)(buf + size));
+}
+
+static inline void htol16_buf(u16 *buf, unsigned int size)
+{
+ size /= 2;
+ while (size--)
+ *(__le16 *)(buf + size) = cpu_to_le16(*(buf + size));
+}
+
+/*
+ * convert binary srom data into linked list of srom variable items.
+ */
+static void
+_initvars_srom_pci(u8 sromrev, u16 *srom, struct list_head *var_list)
+{
+ struct brcms_srom_list_head *entry;
+ enum brcms_srom_id id;
+ u16 w;
+ u32 val;
+ const struct brcms_sromvar *srv;
+ uint width;
+ uint flags;
+ u32 sr = (1 << sromrev);
+
+ /* first store the srom revision */
+ entry = kzalloc(sizeof(struct brcms_srom_list_head), GFP_KERNEL);
+ entry->varid = BRCMS_SROM_REV;
+ entry->var_type = BRCMS_SROM_UNUMBER;
+ entry->uval = sromrev;
+ list_add(&entry->var_list, var_list);
+
+ for (srv = pci_sromvars; srv->varid != BRCMS_SROM_NULL; srv++) {
+ enum brcms_srom_var_type type;
+ u8 ea[ETH_ALEN];
+ u8 extra_space = 0;
+
+ if ((srv->revmask & sr) == 0)
+ continue;
+
+ flags = srv->flags;
+ id = srv->varid;
+
+ /* This entry is for mfgc only. Don't generate param for it, */
+ if (flags & SRFL_NOVAR)
+ continue;
+
+ if (flags & SRFL_ETHADDR) {
+ /*
+ * stored in string format XX:XX:XX:XX:XX:XX (17 chars)
+ */
+ ea[0] = (srom[srv->off] >> 8) & 0xff;
+ ea[1] = srom[srv->off] & 0xff;
+ ea[2] = (srom[srv->off + 1] >> 8) & 0xff;
+ ea[3] = srom[srv->off + 1] & 0xff;
+ ea[4] = (srom[srv->off + 2] >> 8) & 0xff;
+ ea[5] = srom[srv->off + 2] & 0xff;
+ /* 17 characters + string terminator - union size */
+ extra_space = 18 - sizeof(s32);
+ type = BRCMS_SROM_STRING;
+ } else {
+ w = srom[srv->off];
+ val = (w & srv->mask) >> mask_shift(srv->mask);
+ width = mask_width(srv->mask);
+
+ while (srv->flags & SRFL_MORE) {
+ srv++;
+ if (srv->off == 0)
+ continue;
+
+ w = srom[srv->off];
+ val +=
+ ((w & srv->mask) >> mask_shift(srv->
+ mask)) <<
+ width;
+ width += mask_width(srv->mask);
+ }
+
+ if ((flags & SRFL_NOFFS)
+ && ((int)val == (1 << width) - 1))
+ continue;
+
+ if (flags & SRFL_CCODE) {
+ type = BRCMS_SROM_STRING;
+ } else if (flags & SRFL_LEDDC) {
+ /* LED Powersave duty cycle has to be scaled:
+ *(oncount >> 24) (offcount >> 8)
+ */
+ u32 w32 = /* oncount */
+ (((val >> 8) & 0xff) << 24) |
+ /* offcount */
+ (((val & 0xff)) << 8);
+ type = BRCMS_SROM_UNUMBER;
+ val = w32;
+ } else if ((flags & SRFL_PRSIGN)
+ && (val & (1 << (width - 1)))) {
+ type = BRCMS_SROM_SNUMBER;
+ val |= ~0 << width;
+ } else
+ type = BRCMS_SROM_UNUMBER;
+ }
+
+ entry = kzalloc(sizeof(struct brcms_srom_list_head) +
+ extra_space, GFP_KERNEL);
+ entry->varid = id;
+ entry->var_type = type;
+ if (flags & SRFL_ETHADDR) {
+ snprintf(entry->buf, 18, "%pM", ea);
+ } else if (flags & SRFL_CCODE) {
+ if (val == 0)
+ entry->buf[0] = '\0';
+ else
+ snprintf(entry->buf, 3, "%c%c",
+ (val >> 8), (val & 0xff));
+ } else {
+ entry->uval = val;
+ }
+
+ list_add(&entry->var_list, var_list);
+ }
+
+ if (sromrev >= 4) {
+ /* Do per-path variables */
+ uint p, pb, psz;
+
+ if (sromrev >= 8) {
+ pb = SROM8_PATH0;
+ psz = SROM8_PATH1 - SROM8_PATH0;
+ } else {
+ pb = SROM4_PATH0;
+ psz = SROM4_PATH1 - SROM4_PATH0;
+ }
+
+ for (p = 0; p < MAX_PATH_SROM; p++) {
+ for (srv = perpath_pci_sromvars;
+ srv->varid != BRCMS_SROM_NULL; srv++) {
+ if ((srv->revmask & sr) == 0)
+ continue;
+
+ if (srv->flags & SRFL_NOVAR)
+ continue;
+
+ w = srom[pb + srv->off];
+ val = (w & srv->mask) >> mask_shift(srv->mask);
+ width = mask_width(srv->mask);
+
+ /* Cheating: no per-path var is more than
+ * 1 word */
+ if ((srv->flags & SRFL_NOFFS)
+ && ((int)val == (1 << width) - 1))
+ continue;
+
+ entry =
+ kzalloc(sizeof(struct brcms_srom_list_head),
+ GFP_KERNEL);
+ entry->varid = srv->varid+p;
+ entry->var_type = BRCMS_SROM_UNUMBER;
+ entry->uval = val;
+ list_add(&entry->var_list, var_list);
+ }
+ pb += psz;
+ }
+ }
+}
+
+/*
+ * Read in and validate sprom.
+ * Return 0 on success, nonzero on error.
+ */
+static int
+sprom_read_pci(struct si_pub *sih, u16 __iomem *sprom, uint wordoff,
+ u16 *buf, uint nwords, bool check_crc)
+{
+ int err = 0;
+ uint i;
+
+ /* read the sprom */
+ for (i = 0; i < nwords; i++)
+ buf[i] = R_REG(&sprom[wordoff + i]);
+
+ if (check_crc) {
+
+ if (buf[0] == 0xffff)
+ /*
+ * The hardware thinks that an srom that starts with
+ * 0xffff is blank, regardless of the rest of the
+ * content, so declare it bad.
+ */
+ return -ENODATA;
+
+ /* fixup the endianness so crc8 will pass */
+ htol16_buf(buf, nwords * 2);
+ if (crc8(brcms_srom_crc8_table, (u8 *) buf, nwords * 2,
+ CRC8_INIT_VALUE) !=
+ CRC8_GOOD_VALUE(brcms_srom_crc8_table))
+ /* DBG only pci always read srom4 first, then srom8/9 */
+ err = -EIO;
+
+ /* now correct the endianness of the byte array */
+ ltoh16_buf(buf, nwords * 2);
+ }
+ return err;
+}
+
+static int otp_read_pci(struct si_pub *sih, u16 *buf, uint bufsz)
+{
+ u8 *otp;
+ uint sz = OTP_SZ_MAX / 2; /* size in words */
+ int err = 0;
+
+ otp = kzalloc(OTP_SZ_MAX, GFP_ATOMIC);
+ if (otp == NULL)
+ return -ENOMEM;
+
+ err = otp_read_region(sih, OTP_HW_RGN, (u16 *) otp, &sz);
+
+ memcpy(buf, otp, bufsz);
+
+ kfree(otp);
+
+ /* Check CRC */
+ if (buf[0] == 0xffff)
+ /* The hardware thinks that an srom that starts with 0xffff
+ * is blank, regardless of the rest of the content, so declare
+ * it bad.
+ */
+ return -ENODATA;
+
+ /* fixup the endianness so crc8 will pass */
+ htol16_buf(buf, bufsz);
+ if (crc8(brcms_srom_crc8_table, (u8 *) buf, SROM4_WORDS * 2,
+ CRC8_INIT_VALUE) != CRC8_GOOD_VALUE(brcms_srom_crc8_table))
+ err = -EIO;
+
+ /* now correct the endianness of the byte array */
+ ltoh16_buf(buf, bufsz);
+
+ return err;
+}
+
+/*
+ * Initialize nonvolatile variable table from sprom.
+ * Return 0 on success, nonzero on error.
+ */
+static int initvars_srom_pci(struct si_pub *sih, void __iomem *curmap)
+{
+ u16 *srom;
+ u16 __iomem *sromwindow;
+ u8 sromrev = 0;
+ u32 sr;
+ int err = 0;
+
+ /*
+ * Apply CRC over SROM content regardless SROM is present or not.
+ */
+ srom = kmalloc(SROM_MAX, GFP_ATOMIC);
+ if (!srom)
+ return -ENOMEM;
+
+ sromwindow = srom_window_address(sih, curmap);
+
+ crc8_populate_lsb(brcms_srom_crc8_table, SROM_CRC8_POLY);
+ if (ai_is_sprom_available(sih)) {
+ err = sprom_read_pci(sih, sromwindow, 0, srom, SROM_WORDS,
+ true);
+
+ if ((srom[SROM4_SIGN] == SROM4_SIGNATURE) ||
+ (((sih->buscoretype == PCIE_CORE_ID)
+ && (sih->buscorerev >= 6))
+ || ((sih->buscoretype == PCI_CORE_ID)
+ && (sih->buscorerev >= 0xe)))) {
+ /* sromrev >= 4, read more */
+ err = sprom_read_pci(sih, sromwindow, 0, srom,
+ SROM4_WORDS, true);
+ sromrev = srom[SROM4_CRCREV] & 0xff;
+ } else if (err == 0) {
+ /* srom is good and is rev < 4 */
+ /* top word of sprom contains version and crc8 */
+ sromrev = srom[SROM_CRCREV] & 0xff;
+ /* bcm4401 sroms misprogrammed */
+ if (sromrev == 0x10)
+ sromrev = 1;
+ }
+ } else {
+ /* Use OTP if SPROM not available */
+ err = otp_read_pci(sih, srom, SROM_MAX);
+ if (err == 0)
+ /* OTP only contain SROM rev8/rev9 for now */
+ sromrev = srom[SROM4_CRCREV] & 0xff;
+ }
+
+ if (!err) {
+ struct si_info *sii = (struct si_info *)sih;
+
+ /* Bitmask for the sromrev */
+ sr = 1 << sromrev;
+
+ /*
+ * srom version check: Current valid versions: 1, 2, 3, 4, 5, 8,
+ * 9
+ */
+ if ((sr & 0x33e) == 0) {
+ err = -EINVAL;
+ goto errout;
+ }
+
+ INIT_LIST_HEAD(&sii->var_list);
+
+ /* parse SROM into name=value pairs. */
+ _initvars_srom_pci(sromrev, srom, &sii->var_list);
+ }
+
+errout:
+ kfree(srom);
+ return err;
+}
+
+void srom_free_vars(struct si_pub *sih)
+{
+ struct si_info *sii;
+ struct brcms_srom_list_head *entry, *next;
+
+ sii = (struct si_info *)sih;
+ list_for_each_entry_safe(entry, next, &sii->var_list, var_list) {
+ list_del(&entry->var_list);
+ kfree(entry);
+ }
+}
+/*
+ * Initialize local vars from the right source for this platform.
+ * Return 0 on success, nonzero on error.
+ */
+int srom_var_init(struct si_pub *sih, void __iomem *curmap)
+{
+ uint len;
+
+ len = 0;
+
+ if (curmap != NULL)
+ return initvars_srom_pci(sih, curmap);
+
+ return -EINVAL;
+}
+
+/*
+ * Search the name=value vars for a specific one and return its value.
+ * Returns NULL if not found.
+ */
+char *getvar(struct si_pub *sih, enum brcms_srom_id id)
+{
+ struct si_info *sii;
+ struct brcms_srom_list_head *entry;
+
+ sii = (struct si_info *)sih;
+
+ list_for_each_entry(entry, &sii->var_list, var_list)
+ if (entry->varid == id)
+ return &entry->buf[0];
+
+ /* nothing found */
+ return NULL;
+}
+
+/*
+ * Search the vars for a specific one and return its value as
+ * an integer. Returns 0 if not found.-
+ */
+int getintvar(struct si_pub *sih, enum brcms_srom_id id)
+{
+ struct si_info *sii;
+ struct brcms_srom_list_head *entry;
+ unsigned long res;
+
+ sii = (struct si_info *)sih;
+
+ list_for_each_entry(entry, &sii->var_list, var_list)
+ if (entry->varid == id) {
+ if (entry->var_type == BRCMS_SROM_SNUMBER ||
+ entry->var_type == BRCMS_SROM_UNUMBER)
+ return (int)entry->sval;
+ else if (!kstrtoul(&entry->buf[0], 0, &res))
+ return (int)res;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/brcm80211/include/bcmsrom.h b/drivers/net/wireless/brcm80211/brcmsmac/srom.h
index b2dc8951c5d2..708c43ff51cc 100644
--- a/drivers/staging/brcm80211/include/bcmsrom.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/srom.h
@@ -14,16 +14,16 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _bcmsrom_h_
-#define _bcmsrom_h_
+#ifndef _BRCM_SROM_H_
+#define _BRCM_SROM_H_
-#include <bcmsrom_fmt.h>
+#include "types.h"
/* Prototypes */
-extern int srom_var_init(si_t *sih, uint bus, void *curmap,
- char **vars, uint *count);
+extern int srom_var_init(struct si_pub *sih, void __iomem *curmap);
+extern void srom_free_vars(struct si_pub *sih);
-extern int srom_read(si_t *sih, uint bus, void *curmap,
+extern int srom_read(struct si_pub *sih, uint bus, void *curmap,
uint byteoff, uint nbytes, u16 *buf, bool check_crc);
/* parse standard PCMCIA cis, normally used by SB/PCMCIA/SDIO/SPI/OTP
@@ -31,4 +31,4 @@ extern int srom_read(si_t *sih, uint bus, void *curmap,
*/
extern int srom_parsecis(u8 **pcis, uint ciscnt,
char **vars, uint *count);
-#endif /* _bcmsrom_h_ */
+#endif /* _BRCM_SROM_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.c b/drivers/net/wireless/brcm80211/brcmsmac/stf.c
new file mode 100644
index 000000000000..d8f528eb180c
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/stf.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <net/mac80211.h>
+
+#include "types.h"
+#include "d11.h"
+#include "rate.h"
+#include "phy/phy_hal.h"
+#include "channel.h"
+#include "main.h"
+#include "stf.h"
+
+#define MIN_SPATIAL_EXPANSION 0
+#define MAX_SPATIAL_EXPANSION 1
+
+#define BRCMS_STF_SS_STBC_RX(wlc) (BRCMS_ISNPHY(wlc->band) && \
+ NREV_GT(wlc->band->phyrev, 3) && NREV_LE(wlc->band->phyrev, 6))
+
+#define NSTS_1 1
+#define NSTS_2 2
+#define NSTS_3 3
+#define NSTS_4 4
+
+static const u8 txcore_default[5] = {
+ (0), /* bitmap of the core enabled */
+ (0x01), /* For Nsts = 1, enable core 1 */
+ (0x03), /* For Nsts = 2, enable core 1 & 2 */
+ (0x07), /* For Nsts = 3, enable core 1, 2 & 3 */
+ (0x0f) /* For Nsts = 4, enable all cores */
+};
+
+static void brcms_c_stf_stbc_rx_ht_update(struct brcms_c_info *wlc, int val)
+{
+ /* MIMOPHYs rev3-6 cannot receive STBC with only one rx core active */
+ if (BRCMS_STF_SS_STBC_RX(wlc)) {
+ if ((wlc->stf->rxstreams == 1) && (val != HT_CAP_RX_STBC_NO))
+ return;
+ }
+
+ if (wlc->pub->up) {
+ brcms_c_update_beacon(wlc);
+ brcms_c_update_probe_resp(wlc, true);
+ }
+}
+
+/*
+ * every WLC_TEMPSENSE_PERIOD seconds temperature check to decide whether to
+ * turn on/off txchain.
+ */
+void brcms_c_tempsense_upd(struct brcms_c_info *wlc)
+{
+ struct brcms_phy_pub *pi = wlc->band->pi;
+ uint active_chains, txchain;
+
+ /* Check if the chip is too hot. Disable one Tx chain, if it is */
+ /* high 4 bits are for Rx chain, low 4 bits are for Tx chain */
+ active_chains = wlc_phy_stf_chain_active_get(pi);
+ txchain = active_chains & 0xf;
+
+ if (wlc->stf->txchain == wlc->stf->hw_txchain) {
+ if (txchain && (txchain < wlc->stf->hw_txchain))
+ /* turn off 1 tx chain */
+ brcms_c_stf_txchain_set(wlc, txchain, true);
+ } else if (wlc->stf->txchain < wlc->stf->hw_txchain) {
+ if (txchain == wlc->stf->hw_txchain)
+ /* turn back on txchain */
+ brcms_c_stf_txchain_set(wlc, txchain, true);
+ }
+}
+
+void
+brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, u16 *ss_algo_channel,
+ u16 chanspec)
+{
+ struct tx_power power;
+ u8 siso_mcs_id, cdd_mcs_id, stbc_mcs_id;
+
+ /* Clear previous settings */
+ *ss_algo_channel = 0;
+
+ if (!wlc->pub->up) {
+ *ss_algo_channel = (u16) -1;
+ return;
+ }
+
+ wlc_phy_txpower_get_current(wlc->band->pi, &power,
+ CHSPEC_CHANNEL(chanspec));
+
+ siso_mcs_id = (CHSPEC_IS40(chanspec)) ?
+ WL_TX_POWER_MCS40_SISO_FIRST : WL_TX_POWER_MCS20_SISO_FIRST;
+ cdd_mcs_id = (CHSPEC_IS40(chanspec)) ?
+ WL_TX_POWER_MCS40_CDD_FIRST : WL_TX_POWER_MCS20_CDD_FIRST;
+ stbc_mcs_id = (CHSPEC_IS40(chanspec)) ?
+ WL_TX_POWER_MCS40_STBC_FIRST : WL_TX_POWER_MCS20_STBC_FIRST;
+
+ /* criteria to choose stf mode */
+
+ /*
+ * the "+3dbm (12 0.25db units)" is to account for the fact that with
+ * CDD, tx occurs on both chains
+ */
+ if (power.target[siso_mcs_id] > (power.target[cdd_mcs_id] + 12))
+ setbit(ss_algo_channel, PHY_TXC1_MODE_SISO);
+ else
+ setbit(ss_algo_channel, PHY_TXC1_MODE_CDD);
+
+ /*
+ * STBC is ORed into to algo channel as STBC requires per-packet SCB
+ * capability check so cannot be default mode of operation. One of
+ * SISO, CDD have to be set
+ */
+ if (power.target[siso_mcs_id] <= (power.target[stbc_mcs_id] + 12))
+ setbit(ss_algo_channel, PHY_TXC1_MODE_STBC);
+}
+
+static bool brcms_c_stf_stbc_tx_set(struct brcms_c_info *wlc, s32 int_val)
+{
+ if ((int_val != AUTO) && (int_val != OFF) && (int_val != ON))
+ return false;
+
+ if ((int_val == ON) && (wlc->stf->txstreams == 1))
+ return false;
+
+ wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = (s8) int_val;
+ wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = (s8) int_val;
+
+ return true;
+}
+
+bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val)
+{
+ if ((int_val != HT_CAP_RX_STBC_NO)
+ && (int_val != HT_CAP_RX_STBC_ONE_STREAM))
+ return false;
+
+ if (BRCMS_STF_SS_STBC_RX(wlc)) {
+ if ((int_val != HT_CAP_RX_STBC_NO)
+ && (wlc->stf->rxstreams == 1))
+ return false;
+ }
+
+ brcms_c_stf_stbc_rx_ht_update(wlc, int_val);
+ return true;
+}
+
+static int brcms_c_stf_txcore_set(struct brcms_c_info *wlc, u8 Nsts,
+ u8 core_mask)
+{
+ BCMMSG(wlc->wiphy, "wl%d: Nsts %d core_mask %x\n",
+ wlc->pub->unit, Nsts, core_mask);
+
+ if (hweight8(core_mask) > wlc->stf->txstreams)
+ core_mask = 0;
+
+ if ((hweight8(core_mask) == wlc->stf->txstreams) &&
+ ((core_mask & ~wlc->stf->txchain)
+ || !(core_mask & wlc->stf->txchain)))
+ core_mask = wlc->stf->txchain;
+
+ wlc->stf->txcore[Nsts] = core_mask;
+ /* Nsts = 1..4, txcore index = 1..4 */
+ if (Nsts == 1) {
+ /* Needs to update beacon and ucode generated response
+ * frames when 1 stream core map changed
+ */
+ wlc->stf->phytxant = core_mask << PHY_TXC_ANT_SHIFT;
+ brcms_b_txant_set(wlc->hw, wlc->stf->phytxant);
+ if (wlc->clk) {
+ brcms_c_suspend_mac_and_wait(wlc);
+ brcms_c_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec);
+ brcms_c_enable_mac(wlc);
+ }
+ }
+
+ return 0;
+}
+
+static int brcms_c_stf_spatial_policy_set(struct brcms_c_info *wlc, int val)
+{
+ int i;
+ u8 core_mask = 0;
+
+ BCMMSG(wlc->wiphy, "wl%d: val %x\n", wlc->pub->unit, val);
+
+ wlc->stf->spatial_policy = (s8) val;
+ for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++) {
+ core_mask = (val == MAX_SPATIAL_EXPANSION) ?
+ wlc->stf->txchain : txcore_default[i];
+ brcms_c_stf_txcore_set(wlc, (u8) i, core_mask);
+ }
+ return 0;
+}
+
+/*
+ * Centralized txant update function. call it whenever wlc->stf->txant and/or
+ * wlc->stf->txchain change.
+ *
+ * Antennas are controlled by ucode indirectly, which drives PHY or GPIO to
+ * achieve various tx/rx antenna selection schemes
+ *
+ * legacy phy, bit 6 and bit 7 means antenna 0 and 1 respectively, bit6+bit7
+ * means auto(last rx).
+ * for NREV<3, bit 6 and bit 7 means antenna 0 and 1 respectively, bit6+bit7
+ * means last rx and do tx-antenna selection for SISO transmissions
+ * for NREV=3, bit 6 and bit _8_ means antenna 0 and 1 respectively, bit6+bit7
+ * means last rx and do tx-antenna selection for SISO transmissions
+ * for NREV>=7, bit 6 and bit 7 mean antenna 0 and 1 respectively, nit6+bit7
+ * means both cores active
+*/
+static void _brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc)
+{
+ s8 txant;
+
+ txant = (s8) wlc->stf->txant;
+ if (BRCMS_PHY_11N_CAP(wlc->band)) {
+ if (txant == ANT_TX_FORCE_0) {
+ wlc->stf->phytxant = PHY_TXC_ANT_0;
+ } else if (txant == ANT_TX_FORCE_1) {
+ wlc->stf->phytxant = PHY_TXC_ANT_1;
+
+ if (BRCMS_ISNPHY(wlc->band) &&
+ NREV_GE(wlc->band->phyrev, 3)
+ && NREV_LT(wlc->band->phyrev, 7))
+ wlc->stf->phytxant = PHY_TXC_ANT_2;
+ } else {
+ if (BRCMS_ISLCNPHY(wlc->band) ||
+ BRCMS_ISSSLPNPHY(wlc->band))
+ wlc->stf->phytxant = PHY_TXC_LCNPHY_ANT_LAST;
+ else {
+ /* catch out of sync wlc->stf->txcore */
+ WARN_ON(wlc->stf->txchain <= 0);
+ wlc->stf->phytxant =
+ wlc->stf->txchain << PHY_TXC_ANT_SHIFT;
+ }
+ }
+ } else {
+ if (txant == ANT_TX_FORCE_0)
+ wlc->stf->phytxant = PHY_TXC_OLD_ANT_0;
+ else if (txant == ANT_TX_FORCE_1)
+ wlc->stf->phytxant = PHY_TXC_OLD_ANT_1;
+ else
+ wlc->stf->phytxant = PHY_TXC_OLD_ANT_LAST;
+ }
+
+ brcms_b_txant_set(wlc->hw, wlc->stf->phytxant);
+}
+
+int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, bool force)
+{
+ u8 txchain = (u8) int_val;
+ u8 txstreams;
+ uint i;
+
+ if (wlc->stf->txchain == txchain)
+ return 0;
+
+ if ((txchain & ~wlc->stf->hw_txchain)
+ || !(txchain & wlc->stf->hw_txchain))
+ return -EINVAL;
+
+ /*
+ * if nrate override is configured to be non-SISO STF mode, reject
+ * reducing txchain to 1
+ */
+ txstreams = (u8) hweight8(txchain);
+ if (txstreams > MAX_STREAMS_SUPPORTED)
+ return -EINVAL;
+
+ wlc->stf->txchain = txchain;
+ wlc->stf->txstreams = txstreams;
+ brcms_c_stf_stbc_tx_set(wlc, wlc->band->band_stf_stbc_tx);
+ brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
+ brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
+ wlc->stf->txant =
+ (wlc->stf->txstreams == 1) ? ANT_TX_FORCE_0 : ANT_TX_DEF;
+ _brcms_c_stf_phy_txant_upd(wlc);
+
+ wlc_phy_stf_chain_set(wlc->band->pi, wlc->stf->txchain,
+ wlc->stf->rxchain);
+
+ for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++)
+ brcms_c_stf_txcore_set(wlc, (u8) i, txcore_default[i]);
+
+ return 0;
+}
+
+/*
+ * update wlc->stf->ss_opmode which represents the operational stf_ss mode
+ * we're using
+ */
+int brcms_c_stf_ss_update(struct brcms_c_info *wlc, struct brcms_band *band)
+{
+ int ret_code = 0;
+ u8 prev_stf_ss;
+ u8 upd_stf_ss;
+
+ prev_stf_ss = wlc->stf->ss_opmode;
+
+ /*
+ * NOTE: opmode can only be SISO or CDD as STBC is decided on a
+ * per-packet basis
+ */
+ if (BRCMS_STBC_CAP_PHY(wlc) &&
+ wlc->stf->ss_algosel_auto
+ && (wlc->stf->ss_algo_channel != (u16) -1)) {
+ upd_stf_ss = (wlc->stf->txstreams == 1 ||
+ isset(&wlc->stf->ss_algo_channel,
+ PHY_TXC1_MODE_SISO)) ?
+ PHY_TXC1_MODE_SISO : PHY_TXC1_MODE_CDD;
+ } else {
+ if (wlc->band != band)
+ return ret_code;
+ upd_stf_ss = (wlc->stf->txstreams == 1) ?
+ PHY_TXC1_MODE_SISO : band->band_stf_ss_mode;
+ }
+ if (prev_stf_ss != upd_stf_ss) {
+ wlc->stf->ss_opmode = upd_stf_ss;
+ brcms_b_band_stf_ss_set(wlc->hw, upd_stf_ss);
+ }
+
+ return ret_code;
+}
+
+int brcms_c_stf_attach(struct brcms_c_info *wlc)
+{
+ wlc->bandstate[BAND_2G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_SISO;
+ wlc->bandstate[BAND_5G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_CDD;
+
+ if (BRCMS_ISNPHY(wlc->band) &&
+ (wlc_phy_txpower_hw_ctrl_get(wlc->band->pi) != PHY_TPC_HW_ON))
+ wlc->bandstate[BAND_2G_INDEX]->band_stf_ss_mode =
+ PHY_TXC1_MODE_CDD;
+ brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
+ brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
+
+ brcms_c_stf_stbc_rx_ht_update(wlc, HT_CAP_RX_STBC_NO);
+ wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = OFF;
+ wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = OFF;
+
+ if (BRCMS_STBC_CAP_PHY(wlc)) {
+ wlc->stf->ss_algosel_auto = true;
+ /* Init the default value */
+ wlc->stf->ss_algo_channel = (u16) -1;
+ }
+ return 0;
+}
+
+void brcms_c_stf_detach(struct brcms_c_info *wlc)
+{
+}
+
+void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc)
+{
+ _brcms_c_stf_phy_txant_upd(wlc);
+}
+
+void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc)
+{
+ /* get available rx/tx chains */
+ wlc->stf->hw_txchain = (u8) getintvar(wlc->hw->sih, BRCMS_SROM_TXCHAIN);
+ wlc->stf->hw_rxchain = (u8) getintvar(wlc->hw->sih, BRCMS_SROM_RXCHAIN);
+
+ /* these parameter are intended to be used for all PHY types */
+ if (wlc->stf->hw_txchain == 0 || wlc->stf->hw_txchain == 0xf) {
+ if (BRCMS_ISNPHY(wlc->band))
+ wlc->stf->hw_txchain = TXCHAIN_DEF_NPHY;
+ else
+ wlc->stf->hw_txchain = TXCHAIN_DEF;
+ }
+
+ wlc->stf->txchain = wlc->stf->hw_txchain;
+ wlc->stf->txstreams = (u8) hweight8(wlc->stf->hw_txchain);
+
+ if (wlc->stf->hw_rxchain == 0 || wlc->stf->hw_rxchain == 0xf) {
+ if (BRCMS_ISNPHY(wlc->band))
+ wlc->stf->hw_rxchain = RXCHAIN_DEF_NPHY;
+ else
+ wlc->stf->hw_rxchain = RXCHAIN_DEF;
+ }
+
+ wlc->stf->rxchain = wlc->stf->hw_rxchain;
+ wlc->stf->rxstreams = (u8) hweight8(wlc->stf->hw_rxchain);
+
+ /* initialize the txcore table */
+ memcpy(wlc->stf->txcore, txcore_default, sizeof(wlc->stf->txcore));
+
+ /* default spatial_policy */
+ wlc->stf->spatial_policy = MIN_SPATIAL_EXPANSION;
+ brcms_c_stf_spatial_policy_set(wlc, MIN_SPATIAL_EXPANSION);
+}
+
+static u16 _brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc,
+ u32 rspec)
+{
+ u16 phytxant = wlc->stf->phytxant;
+
+ if (rspec_stf(rspec) != PHY_TXC1_MODE_SISO)
+ phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT;
+ else if (wlc->stf->txant == ANT_TX_DEF)
+ phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT;
+ phytxant &= PHY_TXC_ANT_MASK;
+ return phytxant;
+}
+
+u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec)
+{
+ return _brcms_c_stf_phytxchain_sel(wlc, rspec);
+}
+
+u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, u32 rspec)
+{
+ u16 phytxant = wlc->stf->phytxant;
+ u16 mask = PHY_TXC_ANT_MASK;
+
+ /* for non-siso rates or default setting, use the available chains */
+ if (BRCMS_ISNPHY(wlc->band)) {
+ phytxant = _brcms_c_stf_phytxchain_sel(wlc, rspec);
+ mask = PHY_TXC_HTANT_MASK;
+ }
+ phytxant |= phytxant & mask;
+ return phytxant;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.h b/drivers/net/wireless/brcm80211/brcmsmac/stf.h
new file mode 100644
index 000000000000..19f6580f69be
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/stf.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_STF_H_
+#define _BRCM_STF_H_
+
+#include "types.h"
+
+extern int brcms_c_stf_attach(struct brcms_c_info *wlc);
+extern void brcms_c_stf_detach(struct brcms_c_info *wlc);
+
+extern void brcms_c_tempsense_upd(struct brcms_c_info *wlc);
+extern void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc,
+ u16 *ss_algo_channel,
+ u16 chanspec);
+extern int brcms_c_stf_ss_update(struct brcms_c_info *wlc,
+ struct brcms_band *band);
+extern void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc);
+extern int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val,
+ bool force);
+extern bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val);
+extern void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc);
+extern void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc);
+extern u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc,
+ u32 rspec);
+extern u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc,
+ u32 rspec);
+
+#endif /* _BRCM_STF_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/types.h b/drivers/net/wireless/brcm80211/brcmsmac/types.h
new file mode 100644
index 000000000000..27a814b07462
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/types.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_TYPES_H_
+#define _BRCM_TYPES_H_
+
+#include <linux/types.h>
+#include <linux/io.h>
+
+#define WL_CHAN_FREQ_RANGE_2G 0
+#define WL_CHAN_FREQ_RANGE_5GL 1
+#define WL_CHAN_FREQ_RANGE_5GM 2
+#define WL_CHAN_FREQ_RANGE_5GH 3
+
+/* boardflags */
+
+/* Board has gpio 9 controlling the PA */
+#define BFL_PACTRL 0x00000002
+/* Not ok to power down the chip pll and oscillator */
+#define BFL_NOPLLDOWN 0x00000020
+/* Board supports the Front End Module */
+#define BFL_FEM 0x00000800
+/* Board has an external LNA in 2.4GHz band */
+#define BFL_EXTLNA 0x00001000
+/* Board has no PA */
+#define BFL_NOPA 0x00010000
+/* Power topology uses BUCKBOOST */
+#define BFL_BUCKBOOST 0x00200000
+/* Board has FEM and switch to share antenna w/ BT */
+#define BFL_FEM_BT 0x00400000
+/* Power topology doesn't use CBUCK */
+#define BFL_NOCBUCK 0x00800000
+/* Power topology uses PALDO */
+#define BFL_PALDO 0x02000000
+/* Board has an external LNA in 5GHz band */
+#define BFL_EXTLNA_5GHz 0x10000000
+
+/* boardflags2 */
+
+/* Board has an external rxbb regulator */
+#define BFL2_RXBB_INT_REG_DIS 0x00000001
+/* Flag to implement alternative A-band PLL settings */
+#define BFL2_APLL_WAR 0x00000002
+/* Board permits enabling TX Power Control */
+#define BFL2_TXPWRCTRL_EN 0x00000004
+/* Board supports the 2X4 diversity switch */
+#define BFL2_2X4_DIV 0x00000008
+/* Board supports 5G band power gain */
+#define BFL2_5G_PWRGAIN 0x00000010
+/* Board overrides ASPM and Clkreq settings */
+#define BFL2_PCIEWAR_OVR 0x00000020
+#define BFL2_LEGACY 0x00000080
+/* 4321mcm93 board uses Skyworks FEM */
+#define BFL2_SKWRKFEM_BRD 0x00000100
+/* Board has a WAR for clock-harmonic spurs */
+#define BFL2_SPUR_WAR 0x00000200
+/* Flag to narrow G-band PLL loop b/w */
+#define BFL2_GPLL_WAR 0x00000400
+/* Tx CCK pkts on Ant 0 only */
+#define BFL2_SINGLEANT_CCK 0x00001000
+/* WAR to reduce and avoid clock-harmonic spurs in 2G */
+#define BFL2_2G_SPUR_WAR 0x00002000
+/* Flag to widen G-band PLL loop b/w */
+#define BFL2_GPLL_WAR2 0x00010000
+#define BFL2_IPALVLSHIFT_3P3 0x00020000
+/* Use internal envelope detector for TX IQCAL */
+#define BFL2_INTERNDET_TXIQCAL 0x00040000
+/* Keep the buffered Xtal output from radio "ON". Most drivers will turn it
+ * off without this flag to save power. */
+#define BFL2_XTALBUFOUTEN 0x00080000
+
+/*
+ * board specific GPIO assignment, gpio 0-3 are also customer-configurable
+ * led
+ */
+
+/* bit 9 controls the PA on new 4306 boards */
+#define BOARD_GPIO_PACTRL 0x200
+#define BOARD_GPIO_12 0x1000
+#define BOARD_GPIO_13 0x2000
+
+/* **** Core type/rev defaults **** */
+#define D11CONF 0x0fffffb0 /* Supported D11 revs: 4, 5, 7-27
+ * also need to update wlc.h MAXCOREREV
+ */
+
+#define NCONF 0x000001ff /* Supported nphy revs:
+ * 0 4321a0
+ * 1 4321a1
+ * 2 4321b0/b1/c0/c1
+ * 3 4322a0
+ * 4 4322a1
+ * 5 4716a0
+ * 6 43222a0, 43224a0
+ * 7 43226a0
+ * 8 5357a0, 43236a0
+ */
+
+#define LCNCONF 0x00000007 /* Supported lcnphy revs:
+ * 0 4313a0, 4336a0, 4330a0
+ * 1
+ * 2 4330a0
+ */
+
+#define SSLPNCONF 0x0000000f /* Supported sslpnphy revs:
+ * 0 4329a0/k0
+ * 1 4329b0/4329C0
+ * 2 4319a0
+ * 3 5356a0
+ */
+
+/********************************************************************
+ * Phy/Core Configuration. Defines macros to to check core phy/rev *
+ * compile-time configuration. Defines default core support. *
+ * ******************************************************************
+ */
+
+/* Basic macros to check a configuration bitmask */
+
+#define CONF_HAS(config, val) ((config) & (1 << (val)))
+#define CONF_MSK(config, mask) ((config) & (mask))
+#define MSK_RANGE(low, hi) ((1 << ((hi)+1)) - (1 << (low)))
+#define CONF_RANGE(config, low, hi) (CONF_MSK(config, MSK_RANGE(low, high)))
+
+#define CONF_IS(config, val) ((config) == (1 << (val)))
+#define CONF_GE(config, val) ((config) & (0-(1 << (val))))
+#define CONF_GT(config, val) ((config) & (0-2*(1 << (val))))
+#define CONF_LT(config, val) ((config) & ((1 << (val))-1))
+#define CONF_LE(config, val) ((config) & (2*(1 << (val))-1))
+
+/* Wrappers for some of the above, specific to config constants */
+
+#define NCONF_HAS(val) CONF_HAS(NCONF, val)
+#define NCONF_MSK(mask) CONF_MSK(NCONF, mask)
+#define NCONF_IS(val) CONF_IS(NCONF, val)
+#define NCONF_GE(val) CONF_GE(NCONF, val)
+#define NCONF_GT(val) CONF_GT(NCONF, val)
+#define NCONF_LT(val) CONF_LT(NCONF, val)
+#define NCONF_LE(val) CONF_LE(NCONF, val)
+
+#define LCNCONF_HAS(val) CONF_HAS(LCNCONF, val)
+#define LCNCONF_MSK(mask) CONF_MSK(LCNCONF, mask)
+#define LCNCONF_IS(val) CONF_IS(LCNCONF, val)
+#define LCNCONF_GE(val) CONF_GE(LCNCONF, val)
+#define LCNCONF_GT(val) CONF_GT(LCNCONF, val)
+#define LCNCONF_LT(val) CONF_LT(LCNCONF, val)
+#define LCNCONF_LE(val) CONF_LE(LCNCONF, val)
+
+#define D11CONF_HAS(val) CONF_HAS(D11CONF, val)
+#define D11CONF_MSK(mask) CONF_MSK(D11CONF, mask)
+#define D11CONF_IS(val) CONF_IS(D11CONF, val)
+#define D11CONF_GE(val) CONF_GE(D11CONF, val)
+#define D11CONF_GT(val) CONF_GT(D11CONF, val)
+#define D11CONF_LT(val) CONF_LT(D11CONF, val)
+#define D11CONF_LE(val) CONF_LE(D11CONF, val)
+
+#define PHYCONF_HAS(val) CONF_HAS(PHYTYPE, val)
+#define PHYCONF_IS(val) CONF_IS(PHYTYPE, val)
+
+#define NREV_IS(var, val) \
+ (NCONF_HAS(val) && (NCONF_IS(val) || ((var) == (val))))
+
+#define NREV_GE(var, val) \
+ (NCONF_GE(val) && (!NCONF_LT(val) || ((var) >= (val))))
+
+#define NREV_GT(var, val) \
+ (NCONF_GT(val) && (!NCONF_LE(val) || ((var) > (val))))
+
+#define NREV_LT(var, val) \
+ (NCONF_LT(val) && (!NCONF_GE(val) || ((var) < (val))))
+
+#define NREV_LE(var, val) \
+ (NCONF_LE(val) && (!NCONF_GT(val) || ((var) <= (val))))
+
+#define LCNREV_IS(var, val) \
+ (LCNCONF_HAS(val) && (LCNCONF_IS(val) || ((var) == (val))))
+
+#define LCNREV_GE(var, val) \
+ (LCNCONF_GE(val) && (!LCNCONF_LT(val) || ((var) >= (val))))
+
+#define LCNREV_GT(var, val) \
+ (LCNCONF_GT(val) && (!LCNCONF_LE(val) || ((var) > (val))))
+
+#define LCNREV_LT(var, val) \
+ (LCNCONF_LT(val) && (!LCNCONF_GE(val) || ((var) < (val))))
+
+#define LCNREV_LE(var, val) \
+ (LCNCONF_LE(val) && (!LCNCONF_GT(val) || ((var) <= (val))))
+
+#define D11REV_IS(var, val) \
+ (D11CONF_HAS(val) && (D11CONF_IS(val) || ((var) == (val))))
+
+#define D11REV_GE(var, val) \
+ (D11CONF_GE(val) && (!D11CONF_LT(val) || ((var) >= (val))))
+
+#define D11REV_GT(var, val) \
+ (D11CONF_GT(val) && (!D11CONF_LE(val) || ((var) > (val))))
+
+#define D11REV_LT(var, val) \
+ (D11CONF_LT(val) && (!D11CONF_GE(val) || ((var) < (val))))
+
+#define D11REV_LE(var, val) \
+ (D11CONF_LE(val) && (!D11CONF_GT(val) || ((var) <= (val))))
+
+#define PHYTYPE_IS(var, val)\
+ (PHYCONF_HAS(val) && (PHYCONF_IS(val) || ((var) == (val))))
+
+/* Set up PHYTYPE automatically: (depends on PHY_TYPE_X, from d11.h) */
+
+#define _PHYCONF_N (1 << PHY_TYPE_N)
+#define _PHYCONF_LCN (1 << PHY_TYPE_LCN)
+#define _PHYCONF_SSLPN (1 << PHY_TYPE_SSN)
+
+#define PHYTYPE (_PHYCONF_N | _PHYCONF_LCN | _PHYCONF_SSLPN)
+
+/* Utility macro to identify 802.11n (HT) capable PHYs */
+#define PHYTYPE_11N_CAP(phytype) \
+ (PHYTYPE_IS(phytype, PHY_TYPE_N) || \
+ PHYTYPE_IS(phytype, PHY_TYPE_LCN) || \
+ PHYTYPE_IS(phytype, PHY_TYPE_SSN))
+
+/* Last but not least: shorter wlc-specific var checks */
+#define BRCMS_ISNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_N)
+#define BRCMS_ISLCNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_LCN)
+#define BRCMS_ISSSLPNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_SSN)
+
+#define BRCMS_PHY_11N_CAP(band) PHYTYPE_11N_CAP((band)->phytype)
+
+/**********************************************************************
+ * ------------- End of Core phy/rev configuration. ----------------- *
+ * ********************************************************************
+ */
+
+#define BCMMSG(dev, fmt, args...) \
+do { \
+ if (brcm_msg_level & LOG_TRACE_VAL) \
+ wiphy_err(dev, "%s: " fmt, __func__, ##args); \
+} while (0)
+
+/*
+ * Register access macros.
+ *
+ * These macro's take a pointer to the address to read as one of their
+ * arguments. The macro itself deduces the size of the IO transaction (u8, u16
+ * or u32). Advantage of this approach in combination with using a struct to
+ * define the registers in a register block, is that access size and access
+ * location are defined in only one spot. This reduces the risk of the
+ * programmer trying to use an unsupported transaction size on a register.
+ *
+ */
+
+#define R_REG(r) \
+ ({ \
+ __typeof(*(r)) __osl_v; \
+ switch (sizeof(*(r))) { \
+ case sizeof(u8): \
+ __osl_v = readb((u8 __iomem *)(r)); \
+ break; \
+ case sizeof(u16): \
+ __osl_v = readw((u16 __iomem *)(r)); \
+ break; \
+ case sizeof(u32): \
+ __osl_v = readl((u32 __iomem *)(r)); \
+ break; \
+ } \
+ __osl_v; \
+ })
+
+#define W_REG(r, v) do { \
+ switch (sizeof(*(r))) { \
+ case sizeof(u8): \
+ writeb((u8)((v) & 0xFF), (u8 __iomem *)(r)); \
+ break; \
+ case sizeof(u16): \
+ writew((u16)((v) & 0xFFFF), (u16 __iomem *)(r)); \
+ break; \
+ case sizeof(u32): \
+ writel((u32)(v), (u32 __iomem *)(r)); \
+ break; \
+ } \
+ } while (0)
+
+#ifdef CONFIG_BCM47XX
+/*
+ * bcm4716 (which includes 4717 & 4718), plus 4706 on PCIe can reorder
+ * transactions. As a fix, a read after write is performed on certain places
+ * in the code. Older chips and the newer 5357 family don't require this fix.
+ */
+#define W_REG_FLUSH(r, v) ({ W_REG((r), (v)); (void)R_REG(r); })
+#else
+#define W_REG_FLUSH(r, v) W_REG((r), (v))
+#endif /* CONFIG_BCM47XX */
+
+#define AND_REG(r, v) W_REG((r), R_REG(r) & (v))
+#define OR_REG(r, v) W_REG((r), R_REG(r) | (v))
+
+#define SET_REG(r, mask, val) \
+ W_REG((r), ((R_REG(r) & ~(mask)) | (val)))
+
+/* multi-bool data type: set of bools, mbool is true if any is set */
+
+/* set one bool */
+#define mboolset(mb, bit) ((mb) |= (bit))
+/* clear one bool */
+#define mboolclr(mb, bit) ((mb) &= ~(bit))
+/* true if one bool is set */
+#define mboolisset(mb, bit) (((mb) & (bit)) != 0)
+#define mboolmaskset(mb, mask, val) ((mb) = (((mb) & ~(mask)) | (val)))
+
+#define CEIL(x, y) (((x) + ((y)-1)) / (y))
+
+/* forward declarations */
+struct wiphy;
+struct ieee80211_sta;
+struct ieee80211_tx_queue_params;
+struct brcms_info;
+struct brcms_c_info;
+struct brcms_hardware;
+struct brcms_txq_info;
+struct brcms_band;
+struct dma_pub;
+struct si_pub;
+struct tx_status;
+struct d11rxhdr;
+struct txpwr_limits;
+
+/* iovar structure */
+struct brcmu_iovar {
+ const char *name; /* name for lookup and display */
+ u16 varid; /* id for switch */
+ u16 flags; /* driver-specific flag bits */
+ u16 type; /* base type of argument */
+ u16 minlen; /* min length for buffer vars */
+};
+
+/* brcm_msg_level is a bit vector with defs in defs.h */
+extern u32 brcm_msg_level;
+
+#endif /* _BRCM_TYPES_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c b/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c
new file mode 100644
index 000000000000..80e3ccf865e3
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <defs.h>
+#include "types.h"
+#include <ucode_loader.h>
+
+enum {
+ D11UCODE_NAMETAG_START = 0,
+ D11LCN0BSINITVALS24,
+ D11LCN0INITVALS24,
+ D11LCN1BSINITVALS24,
+ D11LCN1INITVALS24,
+ D11LCN2BSINITVALS24,
+ D11LCN2INITVALS24,
+ D11N0ABSINITVALS16,
+ D11N0BSINITVALS16,
+ D11N0INITVALS16,
+ D11UCODE_OVERSIGHT16_MIMO,
+ D11UCODE_OVERSIGHT16_MIMOSZ,
+ D11UCODE_OVERSIGHT24_LCN,
+ D11UCODE_OVERSIGHT24_LCNSZ,
+ D11UCODE_OVERSIGHT_BOMMAJOR,
+ D11UCODE_OVERSIGHT_BOMMINOR
+};
+
+int brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode)
+{
+ int rc;
+
+ rc = brcms_check_firmwares(wl);
+
+ rc = rc < 0 ? rc :
+ brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn0bsinitvals24,
+ D11LCN0BSINITVALS24);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn0initvals24,
+ D11LCN0INITVALS24);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn1bsinitvals24,
+ D11LCN1BSINITVALS24);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn1initvals24,
+ D11LCN1INITVALS24);
+ rc = rc < 0 ? rc :
+ brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn2bsinitvals24,
+ D11LCN2BSINITVALS24);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn2initvals24,
+ D11LCN2INITVALS24);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0absinitvals16,
+ D11N0ABSINITVALS16);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0bsinitvals16,
+ D11N0BSINITVALS16);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0initvals16,
+ D11N0INITVALS16);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_16_mimo,
+ D11UCODE_OVERSIGHT16_MIMO);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_uint(wl, &ucode->bcm43xx_16_mimosz,
+ D11UCODE_OVERSIGHT16_MIMOSZ);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_24_lcn,
+ D11UCODE_OVERSIGHT24_LCN);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_uint(wl, &ucode->bcm43xx_24_lcnsz,
+ D11UCODE_OVERSIGHT24_LCNSZ);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_bommajor,
+ D11UCODE_OVERSIGHT_BOMMAJOR);
+ rc = rc < 0 ?
+ rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_bomminor,
+ D11UCODE_OVERSIGHT_BOMMINOR);
+ return rc;
+}
+
+void brcms_ucode_data_free(struct brcms_ucode *ucode)
+{
+ brcms_ucode_free_buf((void *)ucode->d11lcn0bsinitvals24);
+ brcms_ucode_free_buf((void *)ucode->d11lcn0initvals24);
+ brcms_ucode_free_buf((void *)ucode->d11lcn1bsinitvals24);
+ brcms_ucode_free_buf((void *)ucode->d11lcn1initvals24);
+ brcms_ucode_free_buf((void *)ucode->d11lcn2bsinitvals24);
+ brcms_ucode_free_buf((void *)ucode->d11lcn2initvals24);
+ brcms_ucode_free_buf((void *)ucode->d11n0absinitvals16);
+ brcms_ucode_free_buf((void *)ucode->d11n0bsinitvals16);
+ brcms_ucode_free_buf((void *)ucode->d11n0initvals16);
+ brcms_ucode_free_buf((void *)ucode->bcm43xx_16_mimo);
+ brcms_ucode_free_buf((void *)ucode->bcm43xx_24_lcn);
+ brcms_ucode_free_buf((void *)ucode->bcm43xx_bommajor);
+ brcms_ucode_free_buf((void *)ucode->bcm43xx_bomminor);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h b/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h
new file mode 100644
index 000000000000..18750a814b4f
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _BRCM_UCODE_H_
+#define _BRCM_UCODE_H_
+
+#include "types.h" /* forward structure declarations */
+
+#define MIN_FW_SIZE 40000 /* minimum firmware file size in bytes */
+#define MAX_FW_SIZE 150000
+
+#define UCODE_LOADER_API_VER 0
+
+struct d11init;
+
+struct brcms_ucode {
+ struct d11init *d11lcn0bsinitvals24;
+ struct d11init *d11lcn0initvals24;
+ struct d11init *d11lcn1bsinitvals24;
+ struct d11init *d11lcn1initvals24;
+ struct d11init *d11lcn2bsinitvals24;
+ struct d11init *d11lcn2initvals24;
+ struct d11init *d11n0absinitvals16;
+ struct d11init *d11n0bsinitvals16;
+ struct d11init *d11n0initvals16;
+ __le32 *bcm43xx_16_mimo;
+ size_t bcm43xx_16_mimosz;
+ __le32 *bcm43xx_24_lcn;
+ size_t bcm43xx_24_lcnsz;
+ u32 *bcm43xx_bommajor;
+ u32 *bcm43xx_bomminor;
+};
+
+extern int
+brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode);
+
+extern void brcms_ucode_data_free(struct brcms_ucode *ucode);
+
+extern int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf,
+ unsigned int idx);
+extern int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes,
+ unsigned int idx);
+extern void brcms_ucode_free_buf(void *);
+extern int brcms_check_firmwares(struct brcms_info *wl);
+
+#endif /* _BRCM_UCODE_H_ */
diff --git a/drivers/staging/brcm80211/util/Makefile b/drivers/net/wireless/brcm80211/brcmutil/Makefile
index f9b36cafdc87..6281c416289e 100644
--- a/drivers/staging/brcm80211/util/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmutil/Makefile
@@ -16,12 +16,11 @@
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ccflags-y := \
- -Idrivers/staging/brcm80211/util \
- -Idrivers/staging/brcm80211/include
+ -Idrivers/net/wireless/brcm80211/brcmutil \
+ -Idrivers/net/wireless/brcm80211/include
BRCMUTIL_OFILES := \
- bcmutils.o \
- bcmwifi.o
+ utils.o
MODULEPFX := brcmutil
diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/brcm80211/brcmutil/utils.c
new file mode 100644
index 000000000000..f27c48910827
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmutil/utils.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <brcmu_utils.h>
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities.");
+MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct sk_buff *brcmu_pkt_buf_get_skb(uint len)
+{
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(len);
+ if (skb) {
+ skb_put(skb, len);
+ skb->priority = 0;
+ }
+
+ return skb;
+}
+EXPORT_SYMBOL(brcmu_pkt_buf_get_skb);
+
+/* Free the driver packet. Free the tag if present */
+void brcmu_pkt_buf_free_skb(struct sk_buff *skb)
+{
+ struct sk_buff *nskb;
+ int nest = 0;
+
+ /* perversion: we use skb->next to chain multi-skb packets */
+ while (skb) {
+ nskb = skb->next;
+ skb->next = NULL;
+
+ if (skb->destructor)
+ /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if
+ * destructor exists
+ */
+ dev_kfree_skb_any(skb);
+ else
+ /* can free immediately (even in_irq()) if destructor
+ * does not exist
+ */
+ dev_kfree_skb(skb);
+
+ nest++;
+ skb = nskb;
+ }
+}
+EXPORT_SYMBOL(brcmu_pkt_buf_free_skb);
+
+
+/* copy a buffer into a pkt buffer chain */
+uint brcmu_pktfrombuf(struct sk_buff *p, uint offset, int len,
+ unsigned char *buf)
+{
+ uint n, ret = 0;
+
+ /* skip 'offset' bytes */
+ for (; p && offset; p = p->next) {
+ if (offset < (uint) (p->len))
+ break;
+ offset -= p->len;
+ }
+
+ if (!p)
+ return 0;
+
+ /* copy the data */
+ for (; p && len; p = p->next) {
+ n = min((uint) (p->len) - offset, (uint) len);
+ memcpy(p->data + offset, buf, n);
+ buf += n;
+ len -= n;
+ ret += n;
+ offset = 0;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(brcmu_pktfrombuf);
+
+/* return total length of buffer chain */
+uint brcmu_pkttotlen(struct sk_buff *p)
+{
+ uint total;
+
+ total = 0;
+ for (; p; p = p->next)
+ total += p->len;
+ return total;
+}
+EXPORT_SYMBOL(brcmu_pkttotlen);
+
+/*
+ * osl multiple-precedence packet queue
+ * hi_prec is always >= the number of the highest non-empty precedence
+ */
+struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec,
+ struct sk_buff *p)
+{
+ struct pktq_prec *q;
+
+ if (pktq_full(pq) || pktq_pfull(pq, prec))
+ return NULL;
+
+ q = &pq->q[prec];
+
+ if (q->head)
+ q->tail->prev = p;
+ else
+ q->head = p;
+
+ q->tail = p;
+ q->len++;
+
+ pq->len++;
+
+ if (pq->hi_prec < prec)
+ pq->hi_prec = (u8) prec;
+
+ return p;
+}
+EXPORT_SYMBOL(brcmu_pktq_penq);
+
+struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
+ struct sk_buff *p)
+{
+ struct pktq_prec *q;
+
+ if (pktq_full(pq) || pktq_pfull(pq, prec))
+ return NULL;
+
+ q = &pq->q[prec];
+
+ if (q->head == NULL)
+ q->tail = p;
+
+ p->prev = q->head;
+ q->head = p;
+ q->len++;
+
+ pq->len++;
+
+ if (pq->hi_prec < prec)
+ pq->hi_prec = (u8) prec;
+
+ return p;
+}
+EXPORT_SYMBOL(brcmu_pktq_penq_head);
+
+struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec)
+{
+ struct pktq_prec *q;
+ struct sk_buff *p;
+
+ q = &pq->q[prec];
+
+ p = q->head;
+ if (p == NULL)
+ return NULL;
+
+ q->head = p->prev;
+ if (q->head == NULL)
+ q->tail = NULL;
+
+ q->len--;
+
+ pq->len--;
+
+ p->prev = NULL;
+
+ return p;
+}
+EXPORT_SYMBOL(brcmu_pktq_pdeq);
+
+struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec)
+{
+ struct pktq_prec *q;
+ struct sk_buff *p, *prev;
+
+ q = &pq->q[prec];
+
+ p = q->head;
+ if (p == NULL)
+ return NULL;
+
+ for (prev = NULL; p != q->tail; p = p->prev)
+ prev = p;
+
+ if (prev)
+ prev->prev = NULL;
+ else
+ q->head = NULL;
+
+ q->tail = prev;
+ q->len--;
+
+ pq->len--;
+
+ return p;
+}
+EXPORT_SYMBOL(brcmu_pktq_pdeq_tail);
+
+void
+brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir,
+ bool (*fn)(struct sk_buff *, void *), void *arg)
+{
+ struct pktq_prec *q;
+ struct sk_buff *p, *prev = NULL;
+
+ q = &pq->q[prec];
+ p = q->head;
+ while (p) {
+ if (fn == NULL || (*fn) (p, arg)) {
+ bool head = (p == q->head);
+ if (head)
+ q->head = p->prev;
+ else
+ prev->prev = p->prev;
+ p->prev = NULL;
+ brcmu_pkt_buf_free_skb(p);
+ q->len--;
+ pq->len--;
+ p = (head ? q->head : prev->prev);
+ } else {
+ prev = p;
+ p = p->prev;
+ }
+ }
+
+ if (q->head == NULL)
+ q->tail = NULL;
+}
+EXPORT_SYMBOL(brcmu_pktq_pflush);
+
+void brcmu_pktq_flush(struct pktq *pq, bool dir,
+ bool (*fn)(struct sk_buff *, void *), void *arg)
+{
+ int prec;
+ for (prec = 0; prec < pq->num_prec; prec++)
+ brcmu_pktq_pflush(pq, prec, dir, fn, arg);
+}
+EXPORT_SYMBOL(brcmu_pktq_flush);
+
+void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len)
+{
+ int prec;
+
+ /* pq is variable size; only zero out what's requested */
+ memset(pq, 0,
+ offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
+
+ pq->num_prec = (u16) num_prec;
+
+ pq->max = (u16) max_len;
+
+ for (prec = 0; prec < num_prec; prec++)
+ pq->q[prec].max = pq->max;
+}
+EXPORT_SYMBOL(brcmu_pktq_init);
+
+struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out)
+{
+ int prec;
+
+ if (pq->len == 0)
+ return NULL;
+
+ for (prec = 0; prec < pq->hi_prec; prec++)
+ if (pq->q[prec].head)
+ break;
+
+ if (prec_out)
+ *prec_out = prec;
+
+ return pq->q[prec].tail;
+}
+EXPORT_SYMBOL(brcmu_pktq_peek_tail);
+
+/* Return sum of lengths of a specific set of precedences */
+int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp)
+{
+ int prec, len;
+
+ len = 0;
+
+ for (prec = 0; prec <= pq->hi_prec; prec++)
+ if (prec_bmp & (1 << prec))
+ len += pq->q[prec].len;
+
+ return len;
+}
+EXPORT_SYMBOL(brcmu_pktq_mlen);
+
+/* Priority dequeue from a specific set of precedences */
+struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp,
+ int *prec_out)
+{
+ struct pktq_prec *q;
+ struct sk_buff *p;
+ int prec;
+
+ if (pq->len == 0)
+ return NULL;
+
+ while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
+ pq->hi_prec--;
+
+ while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)
+ if (prec-- == 0)
+ return NULL;
+
+ q = &pq->q[prec];
+
+ p = q->head;
+ if (p == NULL)
+ return NULL;
+
+ q->head = p->prev;
+ if (q->head == NULL)
+ q->tail = NULL;
+
+ q->len--;
+
+ if (prec_out)
+ *prec_out = prec;
+
+ pq->len--;
+
+ p->prev = NULL;
+
+ return p;
+}
+EXPORT_SYMBOL(brcmu_pktq_mdeq);
+
+#if defined(BCMDBG)
+/* pretty hex print a pkt buffer chain */
+void brcmu_prpkt(const char *msg, struct sk_buff *p0)
+{
+ struct sk_buff *p;
+
+ if (msg && (msg[0] != '\0'))
+ printk(KERN_DEBUG "%s:\n", msg);
+
+ for (p = p0; p; p = p->next)
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len);
+}
+EXPORT_SYMBOL(brcmu_prpkt);
+#endif /* defined(BCMDBG) */
+
+#if defined(BCMDBG)
+/*
+ * print bytes formatted as hex to a string. return the resulting
+ * string length
+ */
+int brcmu_format_hex(char *str, const void *bytes, int len)
+{
+ int i;
+ char *p = str;
+ const u8 *src = (const u8 *)bytes;
+
+ for (i = 0; i < len; i++) {
+ p += snprintf(p, 3, "%02X", *src);
+ src++;
+ }
+ return (int)(p - str);
+}
+EXPORT_SYMBOL(brcmu_format_hex);
+#endif /* defined(BCMDBG) */
diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
new file mode 100644
index 000000000000..5fb17d53c9b2
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_HW_IDS_H_
+#define _BRCM_HW_IDS_H_
+
+#define BCM4325_D11DUAL_ID 0x431b
+#define BCM4325_D11G_ID 0x431c
+#define BCM4325_D11A_ID 0x431d
+
+#define BCM4329_D11N2G_ID 0x432f /* 4329 802.11n 2.4G device */
+#define BCM4329_D11N5G_ID 0x4330 /* 4329 802.11n 5G device */
+#define BCM4329_D11NDUAL_ID 0x432e
+
+#define BCM4319_D11N_ID 0x4337 /* 4319 802.11n dualband device */
+#define BCM4319_D11N2G_ID 0x4338 /* 4319 802.11n 2.4G device */
+#define BCM4319_D11N5G_ID 0x4339 /* 4319 802.11n 5G device */
+
+#define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */
+#define BCM43224_D11N_ID_VEN1 0x0576 /* Vendor specific 43224 802.11n db */
+
+#define BCM43225_D11N2G_ID 0x4357 /* 43225 802.11n 2.4GHz device */
+
+#define BCM43236_D11N_ID 0x4346 /* 43236 802.11n dualband device */
+#define BCM43236_D11N2G_ID 0x4347 /* 43236 802.11n 2.4GHz device */
+
+#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */
+
+/* Chip IDs */
+#define BCM4313_CHIP_ID 0x4313 /* 4313 chip id */
+#define BCM4319_CHIP_ID 0x4319 /* 4319 chip id */
+
+#define BCM43224_CHIP_ID 43224 /* 43224 chipcommon chipid */
+#define BCM43225_CHIP_ID 43225 /* 43225 chipcommon chipid */
+#define BCM43421_CHIP_ID 43421 /* 43421 chipcommon chipid */
+#define BCM43235_CHIP_ID 43235 /* 43235 chipcommon chipid */
+#define BCM43236_CHIP_ID 43236 /* 43236 chipcommon chipid */
+#define BCM43238_CHIP_ID 43238 /* 43238 chipcommon chipid */
+#define BCM4329_CHIP_ID 0x4329 /* 4329 chipcommon chipid */
+#define BCM4325_CHIP_ID 0x4325 /* 4325 chipcommon chipid */
+#define BCM4331_CHIP_ID 0x4331 /* 4331 chipcommon chipid */
+#define BCM4336_CHIP_ID 0x4336 /* 4336 chipcommon chipid */
+#define BCM4330_CHIP_ID 0x4330 /* 4330 chipcommon chipid */
+#define BCM6362_CHIP_ID 0x6362 /* 6362 chipcommon chipid */
+
+#endif /* _BRCM_HW_IDS_H_ */
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h
new file mode 100644
index 000000000000..7d0f46e0eb95
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/include/brcmu_utils.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMU_UTILS_H_
+#define _BRCMU_UTILS_H_
+
+#include <linux/skbuff.h>
+
+/*
+ * Spin at most 'us' microseconds while 'exp' is true.
+ * Caller should explicitly test 'exp' when this completes
+ * and take appropriate error action if 'exp' is still true.
+ */
+#define SPINWAIT(exp, us) { \
+ uint countdown = (us) + 9; \
+ while ((exp) && (countdown >= 10)) {\
+ udelay(10); \
+ countdown -= 10; \
+ } \
+}
+
+/* osl multi-precedence packet queue */
+#define PKTQ_LEN_DEFAULT 128 /* Max 128 packets */
+#define PKTQ_MAX_PREC 16 /* Maximum precedence levels */
+
+#define BCME_STRLEN 64 /* Max string length for BCM errors */
+
+/* the largest reasonable packet buffer driver uses for ethernet MTU in bytes */
+#define PKTBUFSZ 2048
+
+#ifndef setbit
+#ifndef NBBY /* the BSD family defines NBBY */
+#define NBBY 8 /* 8 bits per byte */
+#endif /* #ifndef NBBY */
+#define setbit(a, i) (((u8 *)a)[(i)/NBBY] |= 1<<((i)%NBBY))
+#define clrbit(a, i) (((u8 *)a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+#define isset(a, i) (((const u8 *)a)[(i)/NBBY] & (1<<((i)%NBBY)))
+#define isclr(a, i) ((((const u8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#endif /* setbit */
+
+#define NBITS(type) (sizeof(type) * 8)
+#define NBITVAL(nbits) (1 << (nbits))
+#define MAXBITVAL(nbits) ((1 << (nbits)) - 1)
+#define NBITMASK(nbits) MAXBITVAL(nbits)
+#define MAXNBVAL(nbyte) MAXBITVAL((nbyte) * 8)
+
+/* crc defines */
+#define CRC16_INIT_VALUE 0xffff /* Initial CRC16 checksum value */
+#define CRC16_GOOD_VALUE 0xf0b8 /* Good final CRC16 checksum value */
+
+/* 18-bytes of Ethernet address buffer length */
+#define ETHER_ADDR_STR_LEN 18
+
+struct pktq_prec {
+ struct sk_buff *head; /* first packet to dequeue */
+ struct sk_buff *tail; /* last packet to dequeue */
+ u16 len; /* number of queued packets */
+ u16 max; /* maximum number of queued packets */
+};
+
+/* multi-priority pkt queue */
+struct pktq {
+ u16 num_prec; /* number of precedences in use */
+ u16 hi_prec; /* rapid dequeue hint (>= highest non-empty prec) */
+ u16 max; /* total max packets */
+ u16 len; /* total number of packets */
+ /*
+ * q array must be last since # of elements can be either
+ * PKTQ_MAX_PREC or 1
+ */
+ struct pktq_prec q[PKTQ_MAX_PREC];
+};
+
+/* operations on a specific precedence in packet queue */
+
+static inline int pktq_plen(struct pktq *pq, int prec)
+{
+ return pq->q[prec].len;
+}
+
+static inline int pktq_pavail(struct pktq *pq, int prec)
+{
+ return pq->q[prec].max - pq->q[prec].len;
+}
+
+static inline bool pktq_pfull(struct pktq *pq, int prec)
+{
+ return pq->q[prec].len >= pq->q[prec].max;
+}
+
+static inline bool pktq_pempty(struct pktq *pq, int prec)
+{
+ return pq->q[prec].len == 0;
+}
+
+static inline struct sk_buff *pktq_ppeek(struct pktq *pq, int prec)
+{
+ return pq->q[prec].head;
+}
+
+static inline struct sk_buff *pktq_ppeek_tail(struct pktq *pq, int prec)
+{
+ return pq->q[prec].tail;
+}
+
+extern struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec,
+ struct sk_buff *p);
+extern struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
+ struct sk_buff *p);
+extern struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec);
+extern struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec);
+
+/* packet primitives */
+extern struct sk_buff *brcmu_pkt_buf_get_skb(uint len);
+extern void brcmu_pkt_buf_free_skb(struct sk_buff *skb);
+
+/* Empty the queue at particular precedence level */
+/* callback function fn(pkt, arg) returns true if pkt belongs to if */
+extern void brcmu_pktq_pflush(struct pktq *pq, int prec,
+ bool dir, bool (*fn)(struct sk_buff *, void *), void *arg);
+
+/* operations on a set of precedences in packet queue */
+
+extern int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp);
+extern struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp,
+ int *prec_out);
+
+/* operations on packet queue as a whole */
+
+static inline int pktq_len(struct pktq *pq)
+{
+ return (int)pq->len;
+}
+
+static inline int pktq_max(struct pktq *pq)
+{
+ return (int)pq->max;
+}
+
+static inline int pktq_avail(struct pktq *pq)
+{
+ return (int)(pq->max - pq->len);
+}
+
+static inline bool pktq_full(struct pktq *pq)
+{
+ return pq->len >= pq->max;
+}
+
+static inline bool pktq_empty(struct pktq *pq)
+{
+ return pq->len == 0;
+}
+
+extern void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len);
+/* prec_out may be NULL if caller is not interested in return value */
+extern struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out);
+extern void brcmu_pktq_flush(struct pktq *pq, bool dir,
+ bool (*fn)(struct sk_buff *, void *), void *arg);
+
+/* externs */
+/* packet */
+extern uint brcmu_pktfrombuf(struct sk_buff *p,
+ uint offset, int len, unsigned char *buf);
+extern uint brcmu_pkttotlen(struct sk_buff *p);
+
+/* ip address */
+struct ipv4_addr;
+
+#ifdef BCMDBG
+extern void brcmu_prpkt(const char *msg, struct sk_buff *p0);
+#else
+#define brcmu_prpkt(a, b)
+#endif /* BCMDBG */
+
+/* externs */
+/* format/print */
+#if defined(BCMDBG)
+extern int brcmu_format_hex(char *str, const void *bytes, int len);
+#endif
+
+#endif /* _BRCMU_UTILS_H_ */
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h
new file mode 100644
index 000000000000..f10d30274c23
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMU_WIFI_H_
+#define _BRCMU_WIFI_H_
+
+#include <linux/if_ether.h> /* for ETH_ALEN */
+#include <linux/ieee80211.h> /* for WLAN_PMKID_LEN */
+
+/*
+ * A chanspec (u16) holds the channel number, band, bandwidth and control
+ * sideband
+ */
+
+/* channel defines */
+#define CH_UPPER_SB 0x01
+#define CH_LOWER_SB 0x02
+#define CH_EWA_VALID 0x04
+#define CH_20MHZ_APART 4
+#define CH_10MHZ_APART 2
+#define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */
+#define CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */
+#define BRCM_MAX_2G_CHANNEL CH_MAX_2G_CHANNEL /* legacy define */
+
+/* bandstate array indices */
+#define BAND_2G_INDEX 0 /* wlc->bandstate[x] index */
+#define BAND_5G_INDEX 1 /* wlc->bandstate[x] index */
+
+/*
+ * max # supported channels. The max channel no is 216, this is that + 1
+ * rounded up to a multiple of NBBY (8). DO NOT MAKE it > 255: channels are
+ * u8's all over
+*/
+#define MAXCHANNEL 224
+
+#define WL_CHANSPEC_CHAN_MASK 0x00ff
+#define WL_CHANSPEC_CHAN_SHIFT 0
+
+#define WL_CHANSPEC_CTL_SB_MASK 0x0300
+#define WL_CHANSPEC_CTL_SB_SHIFT 8
+#define WL_CHANSPEC_CTL_SB_LOWER 0x0100
+#define WL_CHANSPEC_CTL_SB_UPPER 0x0200
+#define WL_CHANSPEC_CTL_SB_NONE 0x0300
+
+#define WL_CHANSPEC_BW_MASK 0x0C00
+#define WL_CHANSPEC_BW_SHIFT 10
+#define WL_CHANSPEC_BW_10 0x0400
+#define WL_CHANSPEC_BW_20 0x0800
+#define WL_CHANSPEC_BW_40 0x0C00
+
+#define WL_CHANSPEC_BAND_MASK 0xf000
+#define WL_CHANSPEC_BAND_SHIFT 12
+#define WL_CHANSPEC_BAND_5G 0x1000
+#define WL_CHANSPEC_BAND_2G 0x2000
+#define INVCHANSPEC 255
+
+/* used to calculate the chan_freq = chan_factor * 500Mhz + 5 * chan_number */
+#define WF_CHAN_FACTOR_2_4_G 4814 /* 2.4 GHz band, 2407 MHz */
+#define WF_CHAN_FACTOR_5_G 10000 /* 5 GHz band, 5000 MHz */
+#define WF_CHAN_FACTOR_4_G 8000 /* 4.9 GHz band for Japan */
+
+#define CHSPEC_CHANNEL(chspec) ((u8)((chspec) & WL_CHANSPEC_CHAN_MASK))
+#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK)
+
+#define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK)
+#define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK)
+
+#define CHSPEC_IS10(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10)
+
+#define CHSPEC_IS20(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20)
+
+#ifndef CHSPEC_IS40
+#define CHSPEC_IS40(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)
+#endif
+
+#define CHSPEC_IS5G(chspec) \
+ (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G)
+
+#define CHSPEC_IS2G(chspec) \
+ (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G)
+
+#define CHSPEC_SB_NONE(chspec) \
+ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE)
+
+#define CHSPEC_SB_UPPER(chspec) \
+ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER)
+
+#define CHSPEC_SB_LOWER(chspec) \
+ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER)
+
+#define CHSPEC_CTL_CHAN(chspec) \
+ ((CHSPEC_SB_LOWER(chspec)) ? \
+ (lower_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \
+ (upper_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))))
+
+#define CHSPEC2BAND(chspec) (CHSPEC_IS5G(chspec) ? BRCM_BAND_5G : BRCM_BAND_2G)
+
+#define CHANSPEC_STR_LEN 8
+
+static inline int lower_20_sb(int channel)
+{
+ return channel > CH_10MHZ_APART ? (channel - CH_10MHZ_APART) : 0;
+}
+
+static inline int upper_20_sb(int channel)
+{
+ return (channel < (MAXCHANNEL - CH_10MHZ_APART)) ?
+ channel + CH_10MHZ_APART : 0;
+}
+
+static inline int chspec_bandunit(u16 chspec)
+{
+ return CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX;
+}
+
+static inline u16 ch20mhz_chspec(int channel)
+{
+ u16 rc = channel <= CH_MAX_2G_CHANNEL ?
+ WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G;
+
+ return (u16)((u16)channel | WL_CHANSPEC_BW_20 |
+ WL_CHANSPEC_CTL_SB_NONE | rc);
+}
+
+static inline int next_20mhz_chan(int channel)
+{
+ return channel < (MAXCHANNEL - CH_20MHZ_APART) ?
+ channel + CH_20MHZ_APART : 0;
+}
+
+/* defined rate in 500kbps */
+#define BRCM_MAXRATE 108 /* in 500kbps units */
+#define BRCM_RATE_1M 2 /* in 500kbps units */
+#define BRCM_RATE_2M 4 /* in 500kbps units */
+#define BRCM_RATE_5M5 11 /* in 500kbps units */
+#define BRCM_RATE_11M 22 /* in 500kbps units */
+#define BRCM_RATE_6M 12 /* in 500kbps units */
+#define BRCM_RATE_9M 18 /* in 500kbps units */
+#define BRCM_RATE_12M 24 /* in 500kbps units */
+#define BRCM_RATE_18M 36 /* in 500kbps units */
+#define BRCM_RATE_24M 48 /* in 500kbps units */
+#define BRCM_RATE_36M 72 /* in 500kbps units */
+#define BRCM_RATE_48M 96 /* in 500kbps units */
+#define BRCM_RATE_54M 108 /* in 500kbps units */
+
+#define BRCM_2G_25MHZ_OFFSET 5 /* 2.4GHz band channel offset */
+
+#define MCSSET_LEN 16
+
+static inline bool ac_bitmap_tst(u8 bitmap, int prec)
+{
+ return (bitmap & (1 << (prec))) != 0;
+}
+
+/* Enumerate crypto algorithms */
+#define CRYPTO_ALGO_OFF 0
+#define CRYPTO_ALGO_WEP1 1
+#define CRYPTO_ALGO_TKIP 2
+#define CRYPTO_ALGO_WEP128 3
+#define CRYPTO_ALGO_AES_CCM 4
+#define CRYPTO_ALGO_AES_RESERVED1 5
+#define CRYPTO_ALGO_AES_RESERVED2 6
+#define CRYPTO_ALGO_NALG 7
+
+/* wireless security bitvec */
+
+#define WEP_ENABLED 0x0001
+#define TKIP_ENABLED 0x0002
+#define AES_ENABLED 0x0004
+#define WSEC_SWFLAG 0x0008
+/* to go into transition mode without setting wep */
+#define SES_OW_ENABLED 0x0040
+
+/* WPA authentication mode bitvec */
+#define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */
+#define WPA_AUTH_NONE 0x0001 /* none (IBSS) */
+#define WPA_AUTH_UNSPECIFIED 0x0002 /* over 802.1x */
+#define WPA_AUTH_PSK 0x0004 /* Pre-shared key */
+#define WPA_AUTH_RESERVED1 0x0008
+#define WPA_AUTH_RESERVED2 0x0010
+
+#define WPA2_AUTH_RESERVED1 0x0020
+#define WPA2_AUTH_UNSPECIFIED 0x0040 /* over 802.1x */
+#define WPA2_AUTH_PSK 0x0080 /* Pre-shared key */
+#define WPA2_AUTH_RESERVED3 0x0200
+#define WPA2_AUTH_RESERVED4 0x0400
+#define WPA2_AUTH_RESERVED5 0x0800
+
+/* pmkid */
+#define MAXPMKID 16
+
+#define DOT11_DEFAULT_RTS_LEN 2347
+#define DOT11_DEFAULT_FRAG_LEN 2346
+
+#define DOT11_ICV_AES_LEN 8
+#define DOT11_QOS_LEN 2
+#define DOT11_IV_MAX_LEN 8
+#define DOT11_A4_HDR_LEN 30
+
+#define HT_CAP_RX_STBC_NO 0x0
+#define HT_CAP_RX_STBC_ONE_STREAM 0x1
+
+struct pmkid {
+ u8 BSSID[ETH_ALEN];
+ u8 PMKID[WLAN_PMKID_LEN];
+};
+
+struct pmkid_list {
+ __le32 npmkid;
+ struct pmkid pmkid[1];
+};
+
+struct pmkid_cand {
+ u8 BSSID[ETH_ALEN];
+ u8 preauth;
+};
+
+struct pmkid_cand_list {
+ u32 npmkid_cand;
+ struct pmkid_cand pmkid_cand[1];
+};
+
+#endif /* _BRCMU_WIFI_H_ */
diff --git a/drivers/net/wireless/brcm80211/include/chipcommon.h b/drivers/net/wireless/brcm80211/include/chipcommon.h
new file mode 100644
index 000000000000..fefabc39e646
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/include/chipcommon.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SBCHIPC_H
+#define _SBCHIPC_H
+
+#include "defs.h" /* for PAD macro */
+
+struct chipcregs {
+ u32 chipid; /* 0x0 */
+ u32 capabilities;
+ u32 corecontrol; /* corerev >= 1 */
+ u32 bist;
+
+ /* OTP */
+ u32 otpstatus; /* 0x10, corerev >= 10 */
+ u32 otpcontrol;
+ u32 otpprog;
+ u32 otplayout; /* corerev >= 23 */
+
+ /* Interrupt control */
+ u32 intstatus; /* 0x20 */
+ u32 intmask;
+
+ /* Chip specific regs */
+ u32 chipcontrol; /* 0x28, rev >= 11 */
+ u32 chipstatus; /* 0x2c, rev >= 11 */
+
+ /* Jtag Master */
+ u32 jtagcmd; /* 0x30, rev >= 10 */
+ u32 jtagir;
+ u32 jtagdr;
+ u32 jtagctrl;
+
+ /* serial flash interface registers */
+ u32 flashcontrol; /* 0x40 */
+ u32 flashaddress;
+ u32 flashdata;
+ u32 PAD[1];
+
+ /* Silicon backplane configuration broadcast control */
+ u32 broadcastaddress; /* 0x50 */
+ u32 broadcastdata;
+
+ /* gpio - cleared only by power-on-reset */
+ u32 gpiopullup; /* 0x58, corerev >= 20 */
+ u32 gpiopulldown; /* 0x5c, corerev >= 20 */
+ u32 gpioin; /* 0x60 */
+ u32 gpioout; /* 0x64 */
+ u32 gpioouten; /* 0x68 */
+ u32 gpiocontrol; /* 0x6C */
+ u32 gpiointpolarity; /* 0x70 */
+ u32 gpiointmask; /* 0x74 */
+
+ /* GPIO events corerev >= 11 */
+ u32 gpioevent;
+ u32 gpioeventintmask;
+
+ /* Watchdog timer */
+ u32 watchdog; /* 0x80 */
+
+ /* GPIO events corerev >= 11 */
+ u32 gpioeventintpolarity;
+
+ /* GPIO based LED powersave registers corerev >= 16 */
+ u32 gpiotimerval; /* 0x88 */
+ u32 gpiotimeroutmask;
+
+ /* clock control */
+ u32 clockcontrol_n; /* 0x90 */
+ u32 clockcontrol_sb; /* aka m0 */
+ u32 clockcontrol_pci; /* aka m1 */
+ u32 clockcontrol_m2; /* mii/uart/mipsref */
+ u32 clockcontrol_m3; /* cpu */
+ u32 clkdiv; /* corerev >= 3 */
+ u32 gpiodebugsel; /* corerev >= 28 */
+ u32 capabilities_ext; /* 0xac */
+
+ /* pll delay registers (corerev >= 4) */
+ u32 pll_on_delay; /* 0xb0 */
+ u32 fref_sel_delay;
+ u32 slow_clk_ctl; /* 5 < corerev < 10 */
+ u32 PAD;
+
+ /* Instaclock registers (corerev >= 10) */
+ u32 system_clk_ctl; /* 0xc0 */
+ u32 clkstatestretch;
+ u32 PAD[2];
+
+ /* Indirect backplane access (corerev >= 22) */
+ u32 bp_addrlow; /* 0xd0 */
+ u32 bp_addrhigh;
+ u32 bp_data;
+ u32 PAD;
+ u32 bp_indaccess;
+ u32 PAD[3];
+
+ /* More clock dividers (corerev >= 32) */
+ u32 clkdiv2;
+ u32 PAD[2];
+
+ /* In AI chips, pointer to erom */
+ u32 eromptr; /* 0xfc */
+
+ /* ExtBus control registers (corerev >= 3) */
+ u32 pcmcia_config; /* 0x100 */
+ u32 pcmcia_memwait;
+ u32 pcmcia_attrwait;
+ u32 pcmcia_iowait;
+ u32 ide_config;
+ u32 ide_memwait;
+ u32 ide_attrwait;
+ u32 ide_iowait;
+ u32 prog_config;
+ u32 prog_waitcount;
+ u32 flash_config;
+ u32 flash_waitcount;
+ u32 SECI_config; /* 0x130 SECI configuration */
+ u32 PAD[3];
+
+ /* Enhanced Coexistence Interface (ECI) registers (corerev >= 21) */
+ u32 eci_output; /* 0x140 */
+ u32 eci_control;
+ u32 eci_inputlo;
+ u32 eci_inputmi;
+ u32 eci_inputhi;
+ u32 eci_inputintpolaritylo;
+ u32 eci_inputintpolaritymi;
+ u32 eci_inputintpolarityhi;
+ u32 eci_intmasklo;
+ u32 eci_intmaskmi;
+ u32 eci_intmaskhi;
+ u32 eci_eventlo;
+ u32 eci_eventmi;
+ u32 eci_eventhi;
+ u32 eci_eventmasklo;
+ u32 eci_eventmaskmi;
+ u32 eci_eventmaskhi;
+ u32 PAD[3];
+
+ /* SROM interface (corerev >= 32) */
+ u32 sromcontrol; /* 0x190 */
+ u32 sromaddress;
+ u32 sromdata;
+ u32 PAD[17];
+
+ /* Clock control and hardware workarounds (corerev >= 20) */
+ u32 clk_ctl_st; /* 0x1e0 */
+ u32 hw_war;
+ u32 PAD[70];
+
+ /* UARTs */
+ u8 uart0data; /* 0x300 */
+ u8 uart0imr;
+ u8 uart0fcr;
+ u8 uart0lcr;
+ u8 uart0mcr;
+ u8 uart0lsr;
+ u8 uart0msr;
+ u8 uart0scratch;
+ u8 PAD[248]; /* corerev >= 1 */
+
+ u8 uart1data; /* 0x400 */
+ u8 uart1imr;
+ u8 uart1fcr;
+ u8 uart1lcr;
+ u8 uart1mcr;
+ u8 uart1lsr;
+ u8 uart1msr;
+ u8 uart1scratch;
+ u32 PAD[126];
+
+ /* PMU registers (corerev >= 20) */
+ u32 pmucontrol; /* 0x600 */
+ u32 pmucapabilities;
+ u32 pmustatus;
+ u32 res_state;
+ u32 res_pending;
+ u32 pmutimer;
+ u32 min_res_mask;
+ u32 max_res_mask;
+ u32 res_table_sel;
+ u32 res_dep_mask;
+ u32 res_updn_timer;
+ u32 res_timer;
+ u32 clkstretch;
+ u32 pmuwatchdog;
+ u32 gpiosel; /* 0x638, rev >= 1 */
+ u32 gpioenable; /* 0x63c, rev >= 1 */
+ u32 res_req_timer_sel;
+ u32 res_req_timer;
+ u32 res_req_mask;
+ u32 PAD;
+ u32 chipcontrol_addr; /* 0x650 */
+ u32 chipcontrol_data; /* 0x654 */
+ u32 regcontrol_addr;
+ u32 regcontrol_data;
+ u32 pllcontrol_addr;
+ u32 pllcontrol_data;
+ u32 pmustrapopt; /* 0x668, corerev >= 28 */
+ u32 pmu_xtalfreq; /* 0x66C, pmurev >= 10 */
+ u32 PAD[100];
+ u16 sromotp[768];
+};
+
+/* chipid */
+#define CID_ID_MASK 0x0000ffff /* Chip Id mask */
+#define CID_REV_MASK 0x000f0000 /* Chip Revision mask */
+#define CID_REV_SHIFT 16 /* Chip Revision shift */
+#define CID_PKG_MASK 0x00f00000 /* Package Option mask */
+#define CID_PKG_SHIFT 20 /* Package Option shift */
+#define CID_CC_MASK 0x0f000000 /* CoreCount (corerev >= 4) */
+#define CID_CC_SHIFT 24
+#define CID_TYPE_MASK 0xf0000000 /* Chip Type */
+#define CID_TYPE_SHIFT 28
+
+/* capabilities */
+#define CC_CAP_UARTS_MASK 0x00000003 /* Number of UARTs */
+#define CC_CAP_MIPSEB 0x00000004 /* MIPS is in big-endian mode */
+#define CC_CAP_UCLKSEL 0x00000018 /* UARTs clock select */
+/* UARTs are driven by internal divided clock */
+#define CC_CAP_UINTCLK 0x00000008
+#define CC_CAP_UARTGPIO 0x00000020 /* UARTs own GPIOs 15:12 */
+#define CC_CAP_EXTBUS_MASK 0x000000c0 /* External bus mask */
+#define CC_CAP_EXTBUS_NONE 0x00000000 /* No ExtBus present */
+#define CC_CAP_EXTBUS_FULL 0x00000040 /* ExtBus: PCMCIA, IDE & Prog */
+#define CC_CAP_EXTBUS_PROG 0x00000080 /* ExtBus: ProgIf only */
+#define CC_CAP_FLASH_MASK 0x00000700 /* Type of flash */
+#define CC_CAP_PLL_MASK 0x00038000 /* Type of PLL */
+#define CC_CAP_PWR_CTL 0x00040000 /* Power control */
+#define CC_CAP_OTPSIZE 0x00380000 /* OTP Size (0 = none) */
+#define CC_CAP_OTPSIZE_SHIFT 19 /* OTP Size shift */
+#define CC_CAP_OTPSIZE_BASE 5 /* OTP Size base */
+#define CC_CAP_JTAGP 0x00400000 /* JTAG Master Present */
+#define CC_CAP_ROM 0x00800000 /* Internal boot rom active */
+#define CC_CAP_BKPLN64 0x08000000 /* 64-bit backplane */
+#define CC_CAP_PMU 0x10000000 /* PMU Present, rev >= 20 */
+#define CC_CAP_SROM 0x40000000 /* Srom Present, rev >= 32 */
+/* Nand flash present, rev >= 35 */
+#define CC_CAP_NFLASH 0x80000000
+
+#define CC_CAP2_SECI 0x00000001 /* SECI Present, rev >= 36 */
+/* GSIO (spi/i2c) present, rev >= 37 */
+#define CC_CAP2_GSIO 0x00000002
+
+/* pmucapabilities */
+#define PCAP_REV_MASK 0x000000ff
+#define PCAP_RC_MASK 0x00001f00
+#define PCAP_RC_SHIFT 8
+#define PCAP_TC_MASK 0x0001e000
+#define PCAP_TC_SHIFT 13
+#define PCAP_PC_MASK 0x001e0000
+#define PCAP_PC_SHIFT 17
+#define PCAP_VC_MASK 0x01e00000
+#define PCAP_VC_SHIFT 21
+#define PCAP_CC_MASK 0x1e000000
+#define PCAP_CC_SHIFT 25
+#define PCAP5_PC_MASK 0x003e0000 /* PMU corerev >= 5 */
+#define PCAP5_PC_SHIFT 17
+#define PCAP5_VC_MASK 0x07c00000
+#define PCAP5_VC_SHIFT 22
+#define PCAP5_CC_MASK 0xf8000000
+#define PCAP5_CC_SHIFT 27
+
+/*
+* Maximum delay for the PMU state transition in us.
+* This is an upper bound intended for spinwaits etc.
+*/
+#define PMU_MAX_TRANSITION_DLY 15000
+
+#endif /* _SBCHIPC_H */
diff --git a/drivers/net/wireless/brcm80211/include/defs.h b/drivers/net/wireless/brcm80211/include/defs.h
new file mode 100644
index 000000000000..1e5f310af1e7
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/include/defs.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_DEFS_H_
+#define _BRCM_DEFS_H_
+
+#include <linux/types.h>
+
+#define SI_BUS 0
+#define PCI_BUS 1
+#define PCMCIA_BUS 2
+#define SDIO_BUS 3
+#define JTAG_BUS 4
+#define USB_BUS 5
+#define SPI_BUS 6
+
+#define OFF 0
+#define ON 1 /* ON = 1 */
+#define AUTO (-1) /* Auto = -1 */
+
+/*
+ * Priority definitions according 802.1D
+ */
+#define PRIO_8021D_NONE 2
+#define PRIO_8021D_BK 1
+#define PRIO_8021D_BE 0
+#define PRIO_8021D_EE 3
+#define PRIO_8021D_CL 4
+#define PRIO_8021D_VI 5
+#define PRIO_8021D_VO 6
+#define PRIO_8021D_NC 7
+
+#define MAXPRIO 7
+#define NUMPRIO (MAXPRIO + 1)
+
+#define WL_NUMRATES 16 /* max # of rates in a rateset */
+
+#define BRCM_CNTRY_BUF_SZ 4 /* Country string is 3 bytes + NUL */
+
+#define BRCM_SET_CHANNEL 30
+#define BRCM_SET_SRL 32
+#define BRCM_SET_LRL 34
+#define BRCM_SET_BCNPRD 76
+
+#define BRCM_GET_CURR_RATESET 114 /* current rateset */
+#define BRCM_GET_PHYLIST 180
+
+/* Bit masks for radio disabled status - returned by WL_GET_RADIO */
+
+#define WL_RADIO_SW_DISABLE (1<<0)
+#define WL_RADIO_HW_DISABLE (1<<1)
+#define WL_RADIO_MPC_DISABLE (1<<2)
+/* some countries don't support any channel */
+#define WL_RADIO_COUNTRY_DISABLE (1<<3)
+
+/* Override bit for SET_TXPWR. if set, ignore other level limits */
+#define WL_TXPWR_OVERRIDE (1U<<31)
+
+/* band types */
+#define BRCM_BAND_AUTO 0 /* auto-select */
+#define BRCM_BAND_5G 1 /* 5 Ghz */
+#define BRCM_BAND_2G 2 /* 2.4 Ghz */
+#define BRCM_BAND_ALL 3 /* all bands */
+
+/* Values for PM */
+#define PM_OFF 0
+#define PM_MAX 1
+
+/* Message levels */
+#define LOG_ERROR_VAL 0x00000001
+#define LOG_TRACE_VAL 0x00000002
+
+#define PM_OFF 0
+#define PM_MAX 1
+#define PM_FAST 2
+
+/*
+ * Sonics Configuration Space Registers.
+ */
+
+/* core sbconfig regs are top 256bytes of regs */
+#define SBCONFIGOFF 0xf00
+
+/* cpp contortions to concatenate w/arg prescan */
+#ifndef PAD
+#define _PADLINE(line) pad ## line
+#define _XSTR(line) _PADLINE(line)
+#define PAD _XSTR(__LINE__)
+#endif
+
+#endif /* _BRCM_DEFS_H_ */
diff --git a/drivers/net/wireless/brcm80211/include/soc.h b/drivers/net/wireless/brcm80211/include/soc.h
new file mode 100644
index 000000000000..4fcb956ad9e0
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/include/soc.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_SOC_H
+#define _BRCM_SOC_H
+
+#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */
+
+/* core codes */
+#define NODEV_CORE_ID 0x700 /* Invalid coreid */
+#define CC_CORE_ID 0x800 /* chipcommon core */
+#define ILINE20_CORE_ID 0x801 /* iline20 core */
+#define SRAM_CORE_ID 0x802 /* sram core */
+#define SDRAM_CORE_ID 0x803 /* sdram core */
+#define PCI_CORE_ID 0x804 /* pci core */
+#define MIPS_CORE_ID 0x805 /* mips core */
+#define ENET_CORE_ID 0x806 /* enet mac core */
+#define CODEC_CORE_ID 0x807 /* v90 codec core */
+#define USB_CORE_ID 0x808 /* usb 1.1 host/device core */
+#define ADSL_CORE_ID 0x809 /* ADSL core */
+#define ILINE100_CORE_ID 0x80a /* iline100 core */
+#define IPSEC_CORE_ID 0x80b /* ipsec core */
+#define UTOPIA_CORE_ID 0x80c /* utopia core */
+#define PCMCIA_CORE_ID 0x80d /* pcmcia core */
+#define SOCRAM_CORE_ID 0x80e /* internal memory core */
+#define MEMC_CORE_ID 0x80f /* memc sdram core */
+#define OFDM_CORE_ID 0x810 /* OFDM phy core */
+#define EXTIF_CORE_ID 0x811 /* external interface core */
+#define D11_CORE_ID 0x812 /* 802.11 MAC core */
+#define APHY_CORE_ID 0x813 /* 802.11a phy core */
+#define BPHY_CORE_ID 0x814 /* 802.11b phy core */
+#define GPHY_CORE_ID 0x815 /* 802.11g phy core */
+#define MIPS33_CORE_ID 0x816 /* mips3302 core */
+#define USB11H_CORE_ID 0x817 /* usb 1.1 host core */
+#define USB11D_CORE_ID 0x818 /* usb 1.1 device core */
+#define USB20H_CORE_ID 0x819 /* usb 2.0 host core */
+#define USB20D_CORE_ID 0x81a /* usb 2.0 device core */
+#define SDIOH_CORE_ID 0x81b /* sdio host core */
+#define ROBO_CORE_ID 0x81c /* roboswitch core */
+#define ATA100_CORE_ID 0x81d /* parallel ATA core */
+#define SATAXOR_CORE_ID 0x81e /* serial ATA & XOR DMA core */
+#define GIGETH_CORE_ID 0x81f /* gigabit ethernet core */
+#define PCIE_CORE_ID 0x820 /* pci express core */
+#define NPHY_CORE_ID 0x821 /* 802.11n 2x2 phy core */
+#define SRAMC_CORE_ID 0x822 /* SRAM controller core */
+#define MINIMAC_CORE_ID 0x823 /* MINI MAC/phy core */
+#define ARM11_CORE_ID 0x824 /* ARM 1176 core */
+#define ARM7S_CORE_ID 0x825 /* ARM7tdmi-s core */
+#define LPPHY_CORE_ID 0x826 /* 802.11a/b/g phy core */
+#define PMU_CORE_ID 0x827 /* PMU core */
+#define SSNPHY_CORE_ID 0x828 /* 802.11n single-stream phy core */
+#define SDIOD_CORE_ID 0x829 /* SDIO device core */
+#define ARMCM3_CORE_ID 0x82a /* ARM Cortex M3 core */
+#define HTPHY_CORE_ID 0x82b /* 802.11n 4x4 phy core */
+#define MIPS74K_CORE_ID 0x82c /* mips 74k core */
+#define GMAC_CORE_ID 0x82d /* Gigabit MAC core */
+#define DMEMC_CORE_ID 0x82e /* DDR1/2 memory controller core */
+#define PCIERC_CORE_ID 0x82f /* PCIE Root Complex core */
+#define OCP_CORE_ID 0x830 /* OCP2OCP bridge core */
+#define SC_CORE_ID 0x831 /* shared common core */
+#define AHB_CORE_ID 0x832 /* OCP2AHB bridge core */
+#define SPIH_CORE_ID 0x833 /* SPI host core */
+#define I2S_CORE_ID 0x834 /* I2S core */
+#define DMEMS_CORE_ID 0x835 /* SDR/DDR1 memory controller core */
+#define DEF_SHIM_COMP 0x837 /* SHIM component in ubus/6362 */
+#define OOB_ROUTER_CORE_ID 0x367 /* OOB router core ID */
+/* Default component, in ai chips it maps all unused address ranges */
+#define DEF_AI_COMP 0xfff
+
+/* Common core control flags */
+#define SICF_BIST_EN 0x8000
+#define SICF_PME_EN 0x4000
+#define SICF_CORE_BITS 0x3ffc
+#define SICF_FGC 0x0002
+#define SICF_CLOCK_EN 0x0001
+
+#endif /* _BRCM_SOC_H */
diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c
index e0b3e8d406b3..df7050abe717 100644
--- a/drivers/net/wireless/hostap/hostap_80211_rx.c
+++ b/drivers/net/wireless/hostap/hostap_80211_rx.c
@@ -1,5 +1,6 @@
#include <linux/etherdevice.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <net/lib80211.h>
#include <linux/if_arp.h>
diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c
index c34a3b7f1292..344a981a052e 100644
--- a/drivers/net/wireless/hostap/hostap_80211_tx.c
+++ b/drivers/net/wireless/hostap/hostap_80211_tx.c
@@ -1,4 +1,5 @@
#include <linux/slab.h>
+#include <linux/export.h>
#include "hostap_80211.h"
#include "hostap_common.h"
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c
index 3d05dc15c6b8..e1f410277242 100644
--- a/drivers/net/wireless/hostap/hostap_ap.c
+++ b/drivers/net/wireless/hostap/hostap_ap.c
@@ -21,6 +21,8 @@
#include <linux/random.h>
#include <linux/if_arp.h>
#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/moduleparam.h>
#include "hostap_wlan.h"
#include "hostap.h"
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
index c052a0d5cbdd..5441ad195119 100644
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ b/drivers/net/wireless/hostap/hostap_cs.c
@@ -648,6 +648,8 @@ static const struct pcmcia_device_id hostap_cs_ids[] = {
0x74c5e40d),
PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil",
0x4b801a17),
+ PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.02",
+ 0x4b74baa0),
PCMCIA_MFC_DEVICE_PROD_ID12(0, "SanDisk", "ConnectPlus",
0x7a954bd9, 0x74be00c6),
PCMCIA_DEVICE_PROD_ID123(
diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/hostap/hostap_info.c
index d737091cf6ac..47932b28aac1 100644
--- a/drivers/net/wireless/hostap/hostap_info.c
+++ b/drivers/net/wireless/hostap/hostap_info.c
@@ -3,6 +3,7 @@
#include <linux/if_arp.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "hostap_wlan.h"
#include "hostap.h"
#include "hostap_ap.h"
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c
index 12de46407c71..045a93645a3d 100644
--- a/drivers/net/wireless/hostap/hostap_ioctl.c
+++ b/drivers/net/wireless/hostap/hostap_ioctl.c
@@ -5,6 +5,7 @@
#include <linux/sched.h>
#include <linux/ethtool.h>
#include <linux/if_arp.h>
+#include <linux/module.h>
#include <net/lib80211.h>
#include "hostap_wlan.h"
diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c
index d5084829c9e5..bfa0d54221e8 100644
--- a/drivers/net/wireless/hostap/hostap_main.c
+++ b/drivers/net/wireless/hostap/hostap_main.c
@@ -816,7 +816,7 @@ static const struct net_device_ops hostap_netdev_ops = {
.ndo_stop = prism2_close,
.ndo_do_ioctl = hostap_ioctl,
.ndo_set_mac_address = prism2_set_mac_address,
- .ndo_set_multicast_list = hostap_set_multicast_list,
+ .ndo_set_rx_mode = hostap_set_multicast_list,
.ndo_change_mtu = prism2_change_mtu,
.ndo_tx_timeout = prism2_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
@@ -829,7 +829,7 @@ static const struct net_device_ops hostap_mgmt_netdev_ops = {
.ndo_stop = prism2_close,
.ndo_do_ioctl = hostap_ioctl,
.ndo_set_mac_address = prism2_set_mac_address,
- .ndo_set_multicast_list = hostap_set_multicast_list,
+ .ndo_set_rx_mode = hostap_set_multicast_list,
.ndo_change_mtu = prism2_change_mtu,
.ndo_tx_timeout = prism2_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
@@ -842,7 +842,7 @@ static const struct net_device_ops hostap_master_ops = {
.ndo_stop = prism2_close,
.ndo_do_ioctl = hostap_ioctl,
.ndo_set_mac_address = prism2_set_mac_address,
- .ndo_set_multicast_list = hostap_set_multicast_list,
+ .ndo_set_rx_mode = hostap_set_multicast_list,
.ndo_change_mtu = prism2_change_mtu,
.ndo_tx_timeout = prism2_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
@@ -855,6 +855,7 @@ void hostap_setup_dev(struct net_device *dev, local_info_t *local,
iface = netdev_priv(dev);
ether_setup(dev);
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
/* kernel callbacks */
if (iface) {
diff --git a/drivers/net/wireless/hostap/hostap_proc.c b/drivers/net/wireless/hostap/hostap_proc.c
index 005ff25a405f..75ef8f04aabe 100644
--- a/drivers/net/wireless/hostap/hostap_proc.c
+++ b/drivers/net/wireless/hostap/hostap_proc.c
@@ -2,6 +2,7 @@
#include <linux/types.h>
#include <linux/proc_fs.h>
+#include <linux/export.h>
#include <net/lib80211.h>
#include "hostap_wlan.h"
diff --git a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/hostap/hostap_wlan.h
index 88dc6a52bdf1..7bb0b4b3f2cb 100644
--- a/drivers/net/wireless/hostap/hostap_wlan.h
+++ b/drivers/net/wireless/hostap/hostap_wlan.h
@@ -1,6 +1,7 @@
#ifndef HOSTAP_WLAN_H
#define HOSTAP_WLAN_H
+#include <linux/interrupt.h>
#include <linux/wireless.h>
#include <linux/netdevice.h>
#include <linux/mutex.h>
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 44307753587d..127e9c63beaf 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -161,7 +161,7 @@ that only one external action is invoked at a time.
#include <linux/firmware.h>
#include <linux/acpi.h>
#include <linux/ctype.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <net/lib80211.h>
@@ -174,7 +174,7 @@ that only one external action is invoked at a time.
#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver"
#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
-static struct pm_qos_request_list ipw2100_pm_qos_req;
+static struct pm_qos_request ipw2100_pm_qos_req;
/* Debugging stuff */
#ifdef CONFIG_IPW2100_DEBUG
@@ -287,7 +287,7 @@ static const char *command_types[] = {
"unused", /* HOST_INTERRUPT_COALESCING */
"undefined",
"CARD_DISABLE_PHY_OFF",
- "MSDU_TX_RATES" "undefined",
+ "MSDU_TX_RATES",
"undefined",
"SET_STATION_STAT_BITS",
"CLEAR_STATIONS_STAT_BITS",
@@ -1903,15 +1903,17 @@ static void ipw2100_down(struct ipw2100_priv *priv)
static int ipw2100_net_init(struct net_device *dev)
{
struct ipw2100_priv *priv = libipw_priv(dev);
+
+ return ipw2100_up(priv, 1);
+}
+
+static int ipw2100_wdev_init(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
struct wireless_dev *wdev = &priv->ieee->wdev;
- int ret;
int i;
- ret = ipw2100_up(priv, 1);
- if (ret)
- return ret;
-
memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
/* fill-out priv->ieee->bg_band */
@@ -6350,9 +6352,13 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
"Error calling register_netdev.\n");
goto fail;
}
+ registered = 1;
+
+ err = ipw2100_wdev_init(dev);
+ if (err)
+ goto fail;
mutex_lock(&priv->action_mutex);
- registered = 1;
IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
@@ -6389,7 +6395,8 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
fail_unlock:
mutex_unlock(&priv->action_mutex);
-
+ wiphy_unregister(priv->ieee->wdev.wiphy);
+ kfree(priv->ieee->bg_band.channels);
fail:
if (dev) {
if (registered)
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c
index 87813c33bdc2..99a710dfe771 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/ipw2x00/ipw2200.c
@@ -32,6 +32,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
+#include <net/cfg80211-wext.h>
#include "ipw2200.h"
@@ -11425,16 +11426,23 @@ static void ipw_bg_down(struct work_struct *work)
/* Called by register_netdev() */
static int ipw_net_init(struct net_device *dev)
{
+ int rc = 0;
+ struct ipw_priv *priv = libipw_priv(dev);
+
+ mutex_lock(&priv->mutex);
+ if (ipw_up(priv))
+ rc = -EIO;
+ mutex_unlock(&priv->mutex);
+
+ return rc;
+}
+
+static int ipw_wdev_init(struct net_device *dev)
+{
int i, rc = 0;
struct ipw_priv *priv = libipw_priv(dev);
const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
struct wireless_dev *wdev = &priv->ieee->wdev;
- mutex_lock(&priv->mutex);
-
- if (ipw_up(priv)) {
- rc = -EIO;
- goto out;
- }
memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
@@ -11519,13 +11527,9 @@ static int ipw_net_init(struct net_device *dev)
set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev);
/* With that information in place, we can now register the wiphy... */
- if (wiphy_register(wdev->wiphy)) {
+ if (wiphy_register(wdev->wiphy))
rc = -EIO;
- goto out;
- }
-
out:
- mutex_unlock(&priv->mutex);
return rc;
}
@@ -11701,7 +11705,7 @@ static const struct net_device_ops ipw_netdev_ops = {
.ndo_init = ipw_net_init,
.ndo_open = ipw_net_open,
.ndo_stop = ipw_net_stop,
- .ndo_set_multicast_list = ipw_net_set_multicast_list,
+ .ndo_set_rx_mode = ipw_net_set_multicast_list,
.ndo_set_mac_address = ipw_net_set_mac_address,
.ndo_start_xmit = libipw_xmit,
.ndo_change_mtu = libipw_change_mtu,
@@ -11832,14 +11836,22 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev,
goto out_remove_sysfs;
}
+ err = ipw_wdev_init(net_dev);
+ if (err) {
+ IPW_ERROR("failed to register wireless device\n");
+ goto out_unregister_netdev;
+ }
+
#ifdef CONFIG_IPW2200_PROMISCUOUS
if (rtap_iface) {
err = ipw_prom_alloc(priv);
if (err) {
IPW_ERROR("Failed to register promiscuous network "
"device (error %d).\n", err);
- unregister_netdev(priv->net_dev);
- goto out_remove_sysfs;
+ wiphy_unregister(priv->ieee->wdev.wiphy);
+ kfree(priv->ieee->a_band.channels);
+ kfree(priv->ieee->bg_band.channels);
+ goto out_unregister_netdev;
}
}
#endif
@@ -11851,6 +11863,8 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev,
return 0;
+ out_unregister_netdev:
+ unregister_netdev(priv->net_dev);
out_remove_sysfs:
sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
out_release_irq:
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.h b/drivers/net/wireless/ipw2x00/ipw2200.h
index 91795b5a93c5..ecb561d7a7a0 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.h
+++ b/drivers/net/wireless/ipw2x00/ipw2200.h
@@ -32,6 +32,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/pci.h>
diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c
index e5ad76cd77da..32a9966c3bf6 100644
--- a/drivers/net/wireless/ipw2x00/libipw_rx.c
+++ b/drivers/net/wireless/ipw2x00/libipw_rx.c
@@ -442,7 +442,7 @@ int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb,
* 802.11, but makes it easier to use different keys with
* stations that do not support WEP key mapping). */
- if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key)
+ if (is_unicast_ether_addr(hdr->addr1) || local->bcrx_sta_key)
(void)hostap_handle_sta_crypto(local, hdr, &crypt,
&sta);
#endif
@@ -772,7 +772,7 @@ int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb,
#ifdef NOT_YET
if (ieee->iw_mode == IW_MODE_MASTER && !wds && ieee->ap->bridge_packets) {
- if (dst[0] & 0x01) {
+ if (is_multicast_ether_addr(dst)) {
/* copy multicast frame both to the higher layers and
* to the wireless media */
ieee->ap->bridged_multicast++;
diff --git a/drivers/net/wireless/ipw2x00/libipw_tx.c b/drivers/net/wireless/ipw2x00/libipw_tx.c
index 01c88a71abe1..e8c039879b05 100644
--- a/drivers/net/wireless/ipw2x00/libipw_tx.c
+++ b/drivers/net/wireless/ipw2x00/libipw_tx.c
@@ -395,7 +395,7 @@ netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev)
(CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS))
bytes_per_frag -= LIBIPW_FCS_LEN;
- /* Each fragment may need to have room for encryptiong
+ /* Each fragment may need to have room for encryption
* pre/postfix */
if (host_encrypt)
bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len +
diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/ipw2x00/libipw_wx.c
index d7bd6cf00a81..6623e5052254 100644
--- a/drivers/net/wireless/ipw2x00/libipw_wx.c
+++ b/drivers/net/wireless/ipw2x00/libipw_wx.c
@@ -30,6 +30,7 @@
******************************************************************************/
+#include <linux/hardirq.h>
#include <linux/kmod.h>
#include <linux/slab.h>
#include <linux/module.h>
diff --git a/drivers/net/wireless/iwlegacy/iwl-3945-led.c b/drivers/net/wireless/iwlegacy/iwl-3945-led.c
index abd923558d48..7a7f0f38c8ab 100644
--- a/drivers/net/wireless/iwlegacy/iwl-3945-led.c
+++ b/drivers/net/wireless/iwlegacy/iwl-3945-led.c
@@ -32,7 +32,6 @@
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
diff --git a/drivers/net/wireless/iwlegacy/iwl-3945-rs.c b/drivers/net/wireless/iwlegacy/iwl-3945-rs.c
index 977bd2477c6a..8faeaf2dddec 100644
--- a/drivers/net/wireless/iwlegacy/iwl-3945-rs.c
+++ b/drivers/net/wireless/iwlegacy/iwl-3945-rs.c
@@ -28,7 +28,6 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/netdevice.h>
@@ -822,12 +821,15 @@ static void iwl3945_rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
out:
- rs_sta->last_txrate_idx = index;
- if (sband->band == IEEE80211_BAND_5GHZ)
- info->control.rates[0].idx = rs_sta->last_txrate_idx -
- IWL_FIRST_OFDM_RATE;
- else
+ if (sband->band == IEEE80211_BAND_5GHZ) {
+ if (WARN_ON_ONCE(index < IWL_FIRST_OFDM_RATE))
+ index = IWL_FIRST_OFDM_RATE;
+ rs_sta->last_txrate_idx = index;
+ info->control.rates[0].idx = index - IWL_FIRST_OFDM_RATE;
+ } else {
+ rs_sta->last_txrate_idx = index;
info->control.rates[0].idx = rs_sta->last_txrate_idx;
+ }
IWL_DEBUG_RATE(priv, "leave: %d\n", index);
}
diff --git a/drivers/net/wireless/iwlegacy/iwl-3945.c b/drivers/net/wireless/iwlegacy/iwl-3945.c
index d096dc28204d..f7c0a7438476 100644
--- a/drivers/net/wireless/iwlegacy/iwl-3945.c
+++ b/drivers/net/wireless/iwlegacy/iwl-3945.c
@@ -34,7 +34,6 @@
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
@@ -408,7 +407,6 @@ void iwl3945_hw_rx_statistics(struct iwl_priv *priv,
#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS
iwl3945_accumulative_statistics(priv, (__le32 *)&pkt->u.raw);
#endif
- iwl_legacy_recover_from_statistics(priv, pkt);
memcpy(&priv->_3945.statistics, pkt->u.raw, sizeof(priv->_3945.statistics));
}
@@ -1747,7 +1745,11 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
}
memcpy(active_rxon, staging_rxon, sizeof(*active_rxon));
-
+ /*
+ * We do not commit tx power settings while channel changing,
+ * do it now if tx power changed.
+ */
+ iwl_legacy_set_tx_power(priv, priv->tx_power_next, false);
return 0;
}
@@ -2640,7 +2642,6 @@ static struct iwl_lib_ops iwl3945_lib = {
.txq_free_tfd = iwl3945_hw_txq_free_tfd,
.txq_init = iwl3945_hw_tx_queue_init,
.load_ucode = iwl3945_load_bsm,
- .dump_nic_event_log = iwl3945_dump_nic_event_log,
.dump_nic_error_log = iwl3945_dump_nic_error_log,
.apm_ops = {
.init = iwl3945_apm_init,
@@ -2698,9 +2699,7 @@ static struct iwl_base_params iwl3945_base_params = {
.set_l0s = false,
.use_bsm = true,
.led_compensation = 64,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.wd_timeout = IWL_DEF_WD_TIMEOUT,
- .max_event_log_size = 512,
};
static struct iwl_cfg iwl3945_bg_cfg = {
diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-led.c b/drivers/net/wireless/iwlegacy/iwl-4965-led.c
index 26d324e30692..6862fdcaee62 100644
--- a/drivers/net/wireless/iwlegacy/iwl-4965-led.c
+++ b/drivers/net/wireless/iwlegacy/iwl-4965-led.c
@@ -32,7 +32,6 @@
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-lib.c b/drivers/net/wireless/iwlegacy/iwl-4965-lib.c
index a7a4739880dc..2be6d9e3b019 100644
--- a/drivers/net/wireless/iwlegacy/iwl-4965-lib.c
+++ b/drivers/net/wireless/iwlegacy/iwl-4965-lib.c
@@ -694,47 +694,6 @@ void iwl4965_rx_reply_rx_phy(struct iwl_priv *priv,
sizeof(struct iwl_rx_phy_res));
}
-static int iwl4965_get_single_channel_for_scan(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- enum ieee80211_band band,
- struct iwl_scan_channel *scan_ch)
-{
- const struct ieee80211_supported_band *sband;
- u16 passive_dwell = 0;
- u16 active_dwell = 0;
- int added = 0;
- u16 channel = 0;
-
- sband = iwl_get_hw_mode(priv, band);
- if (!sband) {
- IWL_ERR(priv, "invalid band\n");
- return added;
- }
-
- active_dwell = iwl_legacy_get_active_dwell_time(priv, band, 0);
- passive_dwell = iwl_legacy_get_passive_dwell_time(priv, band, vif);
-
- if (passive_dwell <= active_dwell)
- passive_dwell = active_dwell + 1;
-
- channel = iwl_legacy_get_single_channel_number(priv, band);
- if (channel) {
- scan_ch->channel = cpu_to_le16(channel);
- scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
- scan_ch->active_dwell = cpu_to_le16(active_dwell);
- scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
- /* Set txpower levels to defaults */
- scan_ch->dsp_atten = 110;
- if (band == IEEE80211_BAND_5GHZ)
- scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
- else
- scan_ch->tx_gain = ((1 << 5) | (5 << 3));
- added++;
- } else
- IWL_ERR(priv, "no valid channel found\n");
- return added;
-}
-
static int iwl4965_get_channels_for_scan(struct iwl_priv *priv,
struct ieee80211_vif *vif,
enum ieee80211_band band,
@@ -858,16 +817,13 @@ int iwl4965_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
if (iwl_legacy_is_any_associated(priv)) {
- u16 interval = 0;
+ u16 interval;
u32 extra;
u32 suspend_time = 100;
u32 scan_suspend_time = 100;
IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
- if (priv->is_internal_short_scan)
- interval = 0;
- else
- interval = vif->bss_conf.beacon_int;
+ interval = vif->bss_conf.beacon_int;
scan->suspend_time = 0;
scan->max_out_time = cpu_to_le32(200 * 1024);
@@ -882,9 +838,7 @@ int iwl4965_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
scan_suspend_time, interval);
}
- if (priv->is_internal_short_scan) {
- IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
- } else if (priv->scan_request->n_ssids) {
+ if (priv->scan_request->n_ssids) {
int i, p = 0;
IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
for (i = 0; i < priv->scan_request->n_ssids; i++) {
@@ -981,38 +935,21 @@ int iwl4965_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
scan->rx_chain = cpu_to_le16(rx_chain);
- if (!priv->is_internal_short_scan) {
- cmd_len = iwl_legacy_fill_probe_req(priv,
+
+ cmd_len = iwl_legacy_fill_probe_req(priv,
(struct ieee80211_mgmt *)scan->data,
vif->addr,
priv->scan_request->ie,
priv->scan_request->ie_len,
IWL_MAX_SCAN_SIZE - sizeof(*scan));
- } else {
- /* use bcast addr, will not be transmitted but must be valid */
- cmd_len = iwl_legacy_fill_probe_req(priv,
- (struct ieee80211_mgmt *)scan->data,
- iwlegacy_bcast_addr, NULL, 0,
- IWL_MAX_SCAN_SIZE - sizeof(*scan));
-
- }
scan->tx_cmd.len = cpu_to_le16(cmd_len);
scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
RXON_FILTER_BCON_AWARE_MSK);
- if (priv->is_internal_short_scan) {
- scan->channel_count =
- iwl4965_get_single_channel_for_scan(priv, vif, band,
- (void *)&scan->data[le16_to_cpu(
- scan->tx_cmd.len)]);
- } else {
- scan->channel_count =
- iwl4965_get_channels_for_scan(priv, vif, band,
- is_active, n_probes,
- (void *)&scan->data[le16_to_cpu(
- scan->tx_cmd.len)]);
- }
+ scan->channel_count = iwl4965_get_channels_for_scan(priv, vif, band,
+ is_active, n_probes,
+ (void *)&scan->data[cmd_len]);
if (scan->channel_count == 0) {
IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
return -EIO;
diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-rs.c b/drivers/net/wireless/iwlegacy/iwl-4965-rs.c
index 24d149909ba3..57ebe214e68c 100644
--- a/drivers/net/wireless/iwlegacy/iwl-4965-rs.c
+++ b/drivers/net/wireless/iwlegacy/iwl-4965-rs.c
@@ -27,7 +27,6 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/netdevice.h>
@@ -2275,6 +2274,9 @@ iwl4965_rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
if (rate_control_send_low(sta, priv_sta, txrc))
return;
+ if (!lq_sta)
+ return;
+
rate_idx = lq_sta->last_txrate_idx;
if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-rx.c b/drivers/net/wireless/iwlegacy/iwl-4965-rx.c
index b9fa2f6411a7..2b144bbfc3c5 100644
--- a/drivers/net/wireless/iwlegacy/iwl-4965-rx.c
+++ b/drivers/net/wireless/iwlegacy/iwl-4965-rx.c
@@ -151,81 +151,6 @@ static void iwl4965_accumulative_statistics(struct iwl_priv *priv,
#define REG_RECALIB_PERIOD (60)
-/**
- * iwl4965_good_plcp_health - checks for plcp error.
- *
- * When the plcp error is exceeding the thresholds, reset the radio
- * to improve the throughput.
- */
-bool iwl4965_good_plcp_health(struct iwl_priv *priv,
- struct iwl_rx_packet *pkt)
-{
- bool rc = true;
- int combined_plcp_delta;
- unsigned int plcp_msec;
- unsigned long plcp_received_jiffies;
-
- if (priv->cfg->base_params->plcp_delta_threshold ==
- IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) {
- IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n");
- return rc;
- }
-
- /*
- * check for plcp_err and trigger radio reset if it exceeds
- * the plcp error threshold plcp_delta.
- */
- plcp_received_jiffies = jiffies;
- plcp_msec = jiffies_to_msecs((long) plcp_received_jiffies -
- (long) priv->plcp_jiffies);
- priv->plcp_jiffies = plcp_received_jiffies;
- /*
- * check to make sure plcp_msec is not 0 to prevent division
- * by zero.
- */
- if (plcp_msec) {
- struct statistics_rx_phy *ofdm;
- struct statistics_rx_ht_phy *ofdm_ht;
-
- ofdm = &pkt->u.stats.rx.ofdm;
- ofdm_ht = &pkt->u.stats.rx.ofdm_ht;
- combined_plcp_delta =
- (le32_to_cpu(ofdm->plcp_err) -
- le32_to_cpu(priv->_4965.statistics.
- rx.ofdm.plcp_err)) +
- (le32_to_cpu(ofdm_ht->plcp_err) -
- le32_to_cpu(priv->_4965.statistics.
- rx.ofdm_ht.plcp_err));
-
- if ((combined_plcp_delta > 0) &&
- ((combined_plcp_delta * 100) / plcp_msec) >
- priv->cfg->base_params->plcp_delta_threshold) {
- /*
- * if plcp_err exceed the threshold,
- * the following data is printed in csv format:
- * Text: plcp_err exceeded %d,
- * Received ofdm.plcp_err,
- * Current ofdm.plcp_err,
- * Received ofdm_ht.plcp_err,
- * Current ofdm_ht.plcp_err,
- * combined_plcp_delta,
- * plcp_msec
- */
- IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, "
- "%u, %u, %u, %u, %d, %u mSecs\n",
- priv->cfg->base_params->plcp_delta_threshold,
- le32_to_cpu(ofdm->plcp_err),
- le32_to_cpu(ofdm->plcp_err),
- le32_to_cpu(ofdm_ht->plcp_err),
- le32_to_cpu(ofdm_ht->plcp_err),
- combined_plcp_delta, plcp_msec);
-
- rc = false;
- }
- }
- return rc;
-}
-
void iwl4965_rx_statistics(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
@@ -248,8 +173,7 @@ void iwl4965_rx_statistics(struct iwl_priv *priv,
iwl4965_accumulative_statistics(priv, (__le32 *)&pkt->u.stats);
#endif
- iwl_legacy_recover_from_statistics(priv, pkt);
-
+ /* TODO: reading some of statistics is unneeded */
memcpy(&priv->_4965.statistics, &pkt->u.stats,
sizeof(priv->_4965.statistics));
diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-tx.c b/drivers/net/wireless/iwlegacy/iwl-4965-tx.c
index 79ac081832fb..7f12e3638bae 100644
--- a/drivers/net/wireless/iwlegacy/iwl-4965-tx.c
+++ b/drivers/net/wireless/iwlegacy/iwl-4965-tx.c
@@ -240,8 +240,7 @@ static void iwl4965_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
case WLAN_CIPHER_SUITE_TKIP:
tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
- ieee80211_get_tkip_key(keyconf, skb_frag,
- IEEE80211_TKIP_P2_KEY, tx_cmd->key);
+ ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n");
break;
@@ -336,7 +335,7 @@ int iwl4965_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
sta_priv = (void *)sta->drv_priv;
if (sta_priv && sta_priv->asleep &&
- (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) {
+ (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) {
/*
* This sends an asynchronous command to the device,
* but we can rely on it being processed before the
diff --git a/drivers/net/wireless/iwlegacy/iwl-4965.c b/drivers/net/wireless/iwlegacy/iwl-4965.c
index facc94e74b07..86f4fce193e4 100644
--- a/drivers/net/wireless/iwlegacy/iwl-4965.c
+++ b/drivers/net/wireless/iwlegacy/iwl-4965.c
@@ -33,7 +33,6 @@
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
@@ -496,7 +495,7 @@ static s32 iwl4965_get_tx_atten_grp(u16 channel)
channel <= CALIB_IWL_TX_ATTEN_GR4_LCH)
return CALIB_CH_GROUP_4;
- return -1;
+ return -EINVAL;
}
static u32 iwl4965_get_sub_band(const struct iwl_priv *priv, u32 channel)
@@ -915,7 +914,7 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel,
if (txatten_grp < 0) {
IWL_ERR(priv, "Can't find txatten group for channel %d.\n",
channel);
- return -EINVAL;
+ return txatten_grp;
}
IWL_DEBUG_TXPOWER(priv, "channel %d belongs to txatten group %d\n",
@@ -1185,8 +1184,6 @@ static int iwl4965_send_rxon_assoc(struct iwl_priv *priv,
ret = iwl_legacy_send_cmd_pdu_async(priv, REPLY_RXON_ASSOC,
sizeof(rxon_assoc), &rxon_assoc, NULL);
- if (ret)
- return ret;
return ret;
}
@@ -1237,7 +1234,12 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c
memcpy(active_rxon, &ctx->staging, sizeof(*active_rxon));
iwl_legacy_print_rx_config_cmd(priv, ctx);
- goto set_tx_power;
+ /*
+ * We do not commit tx power settings while channel changing,
+ * do it now if tx power changed.
+ */
+ iwl_legacy_set_tx_power(priv, priv->tx_power_next, false);
+ return 0;
}
/* If we are currently associated and the new config requires
@@ -1317,7 +1319,6 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c
iwl4965_init_sensitivity(priv);
-set_tx_power:
/* If we issue a new RXON command which required a tune then we must
* send a new TXPOWER command or we won't be able to Tx any frames */
ret = iwl_legacy_set_tx_power(priv, priv->tx_power_next, true);
@@ -2071,7 +2072,6 @@ static struct iwl_lib_ops iwl4965_lib = {
.is_valid_rtc_data_addr = iwl4965_hw_valid_rtc_data_addr,
.init_alive_start = iwl4965_init_alive_start,
.load_ucode = iwl4965_load_bsm,
- .dump_nic_event_log = iwl4965_dump_nic_event_log,
.dump_nic_error_log = iwl4965_dump_nic_error_log,
.dump_fh = iwl4965_dump_fh,
.set_channel_switch = iwl4965_hw_channel_switch,
@@ -2102,7 +2102,6 @@ static struct iwl_lib_ops iwl4965_lib = {
.tx_stats_read = iwl4965_ucode_tx_stats_read,
.general_stats_read = iwl4965_ucode_general_stats_read,
},
- .check_plcp_health = iwl4965_good_plcp_health,
};
static const struct iwl_legacy_ops iwl4965_legacy_ops = {
@@ -2152,10 +2151,8 @@ static struct iwl_base_params iwl4965_base_params = {
.use_bsm = true,
.led_compensation = 61,
.chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.wd_timeout = IWL_DEF_WD_TIMEOUT,
.temperature_kelvin = true,
- .max_event_log_size = 512,
.ucode_tracing = true,
.sensitivity_calib_by_driver = true,
.chain_noise_calib_by_driver = true,
diff --git a/drivers/net/wireless/iwlegacy/iwl-commands.h b/drivers/net/wireless/iwlegacy/iwl-commands.h
index 17a1d504348e..89904054473f 100644
--- a/drivers/net/wireless/iwlegacy/iwl-commands.h
+++ b/drivers/net/wireless/iwlegacy/iwl-commands.h
@@ -2297,14 +2297,7 @@ struct iwl_spectrum_notification {
#define IWL_POWER_VEC_SIZE 5
#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0))
-#define IWL_POWER_POWER_SAVE_ENA_MSK cpu_to_le16(BIT(0))
-#define IWL_POWER_POWER_MANAGEMENT_ENA_MSK cpu_to_le16(BIT(1))
-#define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2))
#define IWL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3))
-#define IWL_POWER_FAST_PD cpu_to_le16(BIT(4))
-#define IWL_POWER_BEACON_FILTERING cpu_to_le16(BIT(5))
-#define IWL_POWER_SHADOW_REG_ENA cpu_to_le16(BIT(6))
-#define IWL_POWER_CT_KILL_SET cpu_to_le16(BIT(7))
struct iwl3945_powertable_cmd {
__le16 flags;
@@ -2624,8 +2617,8 @@ struct iwl_scanstart_notification {
__le32 status;
} __packed;
-#define SCAN_OWNER_STATUS 0x1;
-#define MEASURE_OWNER_STATUS 0x2;
+#define SCAN_OWNER_STATUS 0x1
+#define MEASURE_OWNER_STATUS 0x2
#define IWL_PROBE_STATUS_OK 0
#define IWL_PROBE_STATUS_TX_FAILED BIT(0)
diff --git a/drivers/net/wireless/iwlegacy/iwl-core.c b/drivers/net/wireless/iwlegacy/iwl-core.c
index 3be76bd5499a..2bd5659310d7 100644
--- a/drivers/net/wireless/iwlegacy/iwl-core.c
+++ b/drivers/net/wireless/iwlegacy/iwl-core.c
@@ -931,14 +931,13 @@ void iwl_legacy_irq_handle_error(struct iwl_priv *priv)
priv->cfg->ops->lib->dump_nic_error_log(priv);
if (priv->cfg->ops->lib->dump_fh)
priv->cfg->ops->lib->dump_fh(priv, NULL, false);
- priv->cfg->ops->lib->dump_nic_event_log(priv, false, NULL, false);
#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG
if (iwl_legacy_get_debug_level(priv) & IWL_DL_FW_ERRORS)
iwl_legacy_print_rx_config_cmd(priv,
&priv->contexts[IWL_RXON_CTX_BSS]);
#endif
- wake_up_interruptible(&priv->wait_command_queue);
+ wake_up(&priv->wait_command_queue);
/* Keep the restart process from trying to send host
* commands by clearing the INIT status bit */
@@ -1251,7 +1250,8 @@ void iwl_legacy_clear_isr_stats(struct iwl_priv *priv)
memset(&priv->isr_stats, 0, sizeof(priv->isr_stats));
}
-int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
+int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct iwl_priv *priv = hw->priv;
@@ -1707,41 +1707,14 @@ iwl_legacy_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len)
EXPORT_SYMBOL(iwl_legacy_update_stats);
#endif
-static void _iwl_legacy_force_rf_reset(struct iwl_priv *priv)
-{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- if (!iwl_legacy_is_any_associated(priv)) {
- IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n");
- return;
- }
- /*
- * There is no easy and better way to force reset the radio,
- * the only known method is switching channel which will force to
- * reset and tune the radio.
- * Use internal short scan (single channel) operation to should
- * achieve this objective.
- * Driver should reset the radio when number of consecutive missed
- * beacon, or any other uCode error condition detected.
- */
- IWL_DEBUG_INFO(priv, "perform radio reset.\n");
- iwl_legacy_internal_short_hw_scan(priv);
-}
-
-
-int iwl_legacy_force_reset(struct iwl_priv *priv, int mode, bool external)
+int iwl_legacy_force_reset(struct iwl_priv *priv, bool external)
{
struct iwl_force_reset *force_reset;
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return -EINVAL;
- if (mode >= IWL_MAX_FORCE_RESET) {
- IWL_DEBUG_INFO(priv, "invalid reset request.\n");
- return -EINVAL;
- }
- force_reset = &priv->force_reset[mode];
+ force_reset = &priv->force_reset;
force_reset->reset_request_count++;
if (!external) {
if (force_reset->last_force_reset_jiffies &&
@@ -1754,37 +1727,34 @@ int iwl_legacy_force_reset(struct iwl_priv *priv, int mode, bool external)
}
force_reset->reset_success_count++;
force_reset->last_force_reset_jiffies = jiffies;
- IWL_DEBUG_INFO(priv, "perform force reset (%d)\n", mode);
- switch (mode) {
- case IWL_RF_RESET:
- _iwl_legacy_force_rf_reset(priv);
- break;
- case IWL_FW_RESET:
- /*
- * if the request is from external(ex: debugfs),
- * then always perform the request in regardless the module
- * parameter setting
- * if the request is from internal (uCode error or driver
- * detect failure), then fw_restart module parameter
- * need to be check before performing firmware reload
- */
- if (!external && !priv->cfg->mod_params->restart_fw) {
- IWL_DEBUG_INFO(priv, "Cancel firmware reload based on "
- "module parameter setting\n");
- break;
- }
- IWL_ERR(priv, "On demand firmware reload\n");
- /* Set the FW error flag -- cleared on iwl_down */
- set_bit(STATUS_FW_ERROR, &priv->status);
- wake_up_interruptible(&priv->wait_command_queue);
- /*
- * Keep the restart process from trying to send host
- * commands by clearing the INIT status bit
- */
- clear_bit(STATUS_READY, &priv->status);
- queue_work(priv->workqueue, &priv->restart);
- break;
+
+ /*
+ * if the request is from external(ex: debugfs),
+ * then always perform the request in regardless the module
+ * parameter setting
+ * if the request is from internal (uCode error or driver
+ * detect failure), then fw_restart module parameter
+ * need to be check before performing firmware reload
+ */
+
+ if (!external && !priv->cfg->mod_params->restart_fw) {
+ IWL_DEBUG_INFO(priv, "Cancel firmware reload based on "
+ "module parameter setting\n");
+ return 0;
}
+
+ IWL_ERR(priv, "On demand firmware reload\n");
+
+ /* Set the FW error flag -- cleared on iwl_down */
+ set_bit(STATUS_FW_ERROR, &priv->status);
+ wake_up(&priv->wait_command_queue);
+ /*
+ * Keep the restart process from trying to send host
+ * commands by clearing the INIT status bit
+ */
+ clear_bit(STATUS_READY, &priv->status);
+ queue_work(priv->workqueue, &priv->restart);
+
return 0;
}
@@ -1879,7 +1849,7 @@ static int iwl_legacy_check_stuck_queue(struct iwl_priv *priv, int cnt)
if (time_after(jiffies, timeout)) {
IWL_ERR(priv, "Queue %d stuck for %u ms.\n",
q->id, priv->cfg->base_params->wd_timeout);
- ret = iwl_legacy_force_reset(priv, IWL_FW_RESET, false);
+ ret = iwl_legacy_force_reset(priv, false);
return (ret == -EAGAIN) ? 0 : 1;
}
@@ -2251,7 +2221,8 @@ out:
}
EXPORT_SYMBOL(iwl_legacy_mac_config);
-void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw)
+void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct iwl_priv *priv = hw->priv;
unsigned long flags;
diff --git a/drivers/net/wireless/iwlegacy/iwl-core.h b/drivers/net/wireless/iwlegacy/iwl-core.h
index c5fbda0760de..d1271fe07d4b 100644
--- a/drivers/net/wireless/iwlegacy/iwl-core.h
+++ b/drivers/net/wireless/iwlegacy/iwl-core.h
@@ -143,8 +143,7 @@ struct iwl_lib_ops {
int (*is_valid_rtc_data_addr)(u32 addr);
/* 1st ucode load */
int (*load_ucode)(struct iwl_priv *priv);
- int (*dump_nic_event_log)(struct iwl_priv *priv,
- bool full_log, char **buf, bool display);
+
void (*dump_nic_error_log)(struct iwl_priv *priv);
int (*dump_fh)(struct iwl_priv *priv, char **buf, bool display);
int (*set_channel_switch)(struct iwl_priv *priv,
@@ -161,9 +160,6 @@ struct iwl_lib_ops {
/* temperature */
struct iwl_temp_ops temp_ops;
- /* check for plcp health */
- bool (*check_plcp_health)(struct iwl_priv *priv,
- struct iwl_rx_packet *pkt);
struct iwl_debugfs_ops debugfs_ops;
@@ -207,11 +203,8 @@ struct iwl_mod_params {
* to the deviation to achieve the desired led frequency.
* The detail algorithm is described in iwl-led.c
* @chain_noise_num_beacons: number of beacons used to compute chain noise
- * @plcp_delta_threshold: plcp error rate threshold used to trigger
- * radio tuning when there is a high receiving plcp error rate
* @wd_timeout: TX queues watchdog timeout
* @temperature_kelvin: temperature report by uCode in kelvin
- * @max_event_log_size: size of event log buffer size for ucode event logging
* @ucode_tracing: support ucode continuous tracing
* @sensitivity_calib_by_driver: driver has the capability to perform
* sensitivity calibration operation
@@ -229,10 +222,8 @@ struct iwl_base_params {
u16 led_compensation;
int chain_noise_num_beacons;
- u8 plcp_delta_threshold;
unsigned int wd_timeout;
bool temperature_kelvin;
- u32 max_event_log_size;
const bool ucode_tracing;
const bool sensitivity_calib_by_driver;
const bool chain_noise_calib_by_driver;
@@ -295,7 +286,8 @@ struct iwl_cfg {
***************************/
struct ieee80211_hw *iwl_legacy_alloc_all(struct iwl_cfg *cfg);
-int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
+int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params);
int iwl_legacy_mac_tx_last_beacon(struct ieee80211_hw *hw);
void iwl_legacy_set_rxon_hwcrypto(struct iwl_priv *priv,
@@ -441,7 +433,7 @@ int iwl_legacy_mac_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
void iwl_legacy_internal_short_hw_scan(struct iwl_priv *priv);
-int iwl_legacy_force_reset(struct iwl_priv *priv, int mode, bool external);
+int iwl_legacy_force_reset(struct iwl_priv *priv, bool external);
u16 iwl_legacy_fill_probe_req(struct iwl_priv *priv,
struct ieee80211_mgmt *frame,
const u8 *ta, const u8 *ie, int ie_len, int left);
@@ -493,7 +485,7 @@ static inline u16 iwl_legacy_pcie_link_ctl(struct iwl_priv *priv)
{
int pos;
u16 pci_lnk_ctl;
- pos = pci_find_capability(priv->pci_dev, PCI_CAP_ID_EXP);
+ pos = pci_pcie_cap(priv->pci_dev);
pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
return pci_lnk_ctl;
}
@@ -521,8 +513,6 @@ extern const struct dev_pm_ops iwl_legacy_pm_ops;
* Error Handling Debugging
******************************************************/
void iwl4965_dump_nic_error_log(struct iwl_priv *priv);
-int iwl4965_dump_nic_event_log(struct iwl_priv *priv,
- bool full_log, char **buf, bool display);
#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG
void iwl_legacy_print_rx_config_cmd(struct iwl_priv *priv,
struct iwl_rxon_context *ctx);
@@ -631,7 +621,8 @@ static inline const struct ieee80211_supported_band *iwl_get_hw_mode(
/* mac80211 handlers */
int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed);
-void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw);
+void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
void iwl_legacy_mac_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
diff --git a/drivers/net/wireless/iwlegacy/iwl-debugfs.c b/drivers/net/wireless/iwlegacy/iwl-debugfs.c
index 2d32438b4cb8..1407dca70def 100644
--- a/drivers/net/wireless/iwlegacy/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlegacy/iwl-debugfs.c
@@ -26,6 +26,7 @@
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
#include <linux/ieee80211.h>
+#include <linux/export.h>
#include <net/mac80211.h>
@@ -391,48 +392,6 @@ static ssize_t iwl_legacy_dbgfs_nvm_read(struct file *file,
return ret;
}
-static ssize_t iwl_legacy_dbgfs_log_event_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char *buf;
- int pos = 0;
- ssize_t ret = -ENOMEM;
-
- ret = pos = priv->cfg->ops->lib->dump_nic_event_log(
- priv, true, &buf, true);
- if (buf) {
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- }
- return ret;
-}
-
-static ssize_t iwl_legacy_dbgfs_log_event_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- u32 event_log_flag;
- char buf[8];
- int buf_size;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &event_log_flag) != 1)
- return -EFAULT;
- if (event_log_flag == 1)
- priv->cfg->ops->lib->dump_nic_event_log(priv, true,
- NULL, false);
-
- return count;
-}
-
-
-
static ssize_t
iwl_legacy_dbgfs_channels_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
@@ -706,7 +665,6 @@ static ssize_t iwl_legacy_dbgfs_disable_ht40_read(struct file *file,
}
DEBUGFS_READ_WRITE_FILE_OPS(sram);
-DEBUGFS_READ_WRITE_FILE_OPS(log_event);
DEBUGFS_READ_FILE_OPS(nvm);
DEBUGFS_READ_FILE_OPS(stations);
DEBUGFS_READ_FILE_OPS(channels);
@@ -1098,56 +1056,6 @@ static ssize_t iwl_legacy_dbgfs_clear_ucode_statistics_write(struct file *file,
return count;
}
-static ssize_t iwl_legacy_dbgfs_ucode_tracing_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- char buf[128];
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n",
- priv->event_log.ucode_trace ? "On" : "Off");
- pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n",
- priv->event_log.non_wraps_count);
- pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n",
- priv->event_log.wraps_once_count);
- pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n",
- priv->event_log.wraps_more_count);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_legacy_dbgfs_ucode_tracing_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int trace;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &trace) != 1)
- return -EFAULT;
-
- if (trace) {
- priv->event_log.ucode_trace = true;
- /* schedule the ucode timer to occur in UCODE_TRACE_PERIOD */
- mod_timer(&priv->ucode_trace,
- jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD));
- } else {
- priv->event_log.ucode_trace = false;
- del_timer_sync(&priv->ucode_trace);
- }
-
- return count;
-}
-
static ssize_t iwl_legacy_dbgfs_rxon_flags_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos) {
@@ -1236,72 +1144,31 @@ static ssize_t iwl_legacy_dbgfs_missed_beacon_write(struct file *file,
return count;
}
-static ssize_t iwl_legacy_dbgfs_plcp_delta_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- char buf[12];
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos, "%u\n",
- priv->cfg->base_params->plcp_delta_threshold);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_legacy_dbgfs_plcp_delta_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int plcp;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &plcp) != 1)
- return -EINVAL;
- if ((plcp < IWL_MAX_PLCP_ERR_THRESHOLD_MIN) ||
- (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX))
- priv->cfg->base_params->plcp_delta_threshold =
- IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE;
- else
- priv->cfg->base_params->plcp_delta_threshold = plcp;
- return count;
-}
-
static ssize_t iwl_legacy_dbgfs_force_reset_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos) {
struct iwl_priv *priv = file->private_data;
- int i, pos = 0;
+ int pos = 0;
char buf[300];
const size_t bufsz = sizeof(buf);
struct iwl_force_reset *force_reset;
- for (i = 0; i < IWL_MAX_FORCE_RESET; i++) {
- force_reset = &priv->force_reset[i];
- pos += scnprintf(buf + pos, bufsz - pos,
- "Force reset method %d\n", i);
- pos += scnprintf(buf + pos, bufsz - pos,
- "\tnumber of reset request: %d\n",
- force_reset->reset_request_count);
- pos += scnprintf(buf + pos, bufsz - pos,
- "\tnumber of reset request success: %d\n",
- force_reset->reset_success_count);
- pos += scnprintf(buf + pos, bufsz - pos,
- "\tnumber of reset request reject: %d\n",
- force_reset->reset_reject_count);
- pos += scnprintf(buf + pos, bufsz - pos,
- "\treset duration: %lu\n",
- force_reset->reset_duration);
- }
+ force_reset = &priv->force_reset;
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\tnumber of reset request: %d\n",
+ force_reset->reset_request_count);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\tnumber of reset request success: %d\n",
+ force_reset->reset_success_count);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\tnumber of reset request reject: %d\n",
+ force_reset->reset_reject_count);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\treset duration: %lu\n",
+ force_reset->reset_duration);
+
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
@@ -1309,25 +1176,11 @@ static ssize_t iwl_legacy_dbgfs_force_reset_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos) {
+ int ret;
struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int reset, ret;
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &reset) != 1)
- return -EINVAL;
- switch (reset) {
- case IWL_RF_RESET:
- case IWL_FW_RESET:
- ret = iwl_legacy_force_reset(priv, reset, true);
- break;
- default:
- return -EINVAL;
- }
+ ret = iwl_legacy_force_reset(priv, true);
+
return ret ? ret : count;
}
@@ -1367,10 +1220,8 @@ DEBUGFS_READ_FILE_OPS(chain_noise);
DEBUGFS_READ_FILE_OPS(power_save_status);
DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics);
DEBUGFS_WRITE_FILE_OPS(clear_traffic_statistics);
-DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing);
DEBUGFS_READ_FILE_OPS(fh_reg);
DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon);
-DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta);
DEBUGFS_READ_WRITE_FILE_OPS(force_reset);
DEBUGFS_READ_FILE_OPS(rxon_flags);
DEBUGFS_READ_FILE_OPS(rxon_filter_flags);
@@ -1403,7 +1254,6 @@ int iwl_legacy_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(log_event, dir_data, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR);
@@ -1420,7 +1270,6 @@ int iwl_legacy_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_FILE(clear_traffic_statistics, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(fh_reg, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(force_reset, dir_debug, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR);
@@ -1430,8 +1279,6 @@ int iwl_legacy_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR);
if (priv->cfg->base_params->chain_noise_calib_by_driver)
DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR);
- if (priv->cfg->base_params->ucode_tracing)
- DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(wd_timeout, dir_debug, S_IWUSR);
diff --git a/drivers/net/wireless/iwlegacy/iwl-dev.h b/drivers/net/wireless/iwlegacy/iwl-dev.h
index ea30122669ee..9c786edf56fd 100644
--- a/drivers/net/wireless/iwlegacy/iwl-dev.h
+++ b/drivers/net/wireless/iwlegacy/iwl-dev.h
@@ -32,6 +32,7 @@
#ifndef __iwl_legacy_dev_h__
#define __iwl_legacy_dev_h__
+#include <linux/interrupt.h>
#include <linux/pci.h> /* for struct pci_device_id */
#include <linux/kernel.h>
#include <linux/leds.h>
@@ -855,32 +856,6 @@ struct traffic_stats {
};
/*
- * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds
- * to perform continuous uCode event logging operation if enabled
- */
-#define UCODE_TRACE_PERIOD (100)
-
-/*
- * iwl_event_log: current uCode event log position
- *
- * @ucode_trace: enable/disable ucode continuous trace timer
- * @num_wraps: how many times the event buffer wraps
- * @next_entry: the entry just before the next one that uCode would fill
- * @non_wraps_count: counter for no wrap detected when dump ucode events
- * @wraps_once_count: counter for wrap once detected when dump ucode events
- * @wraps_more_count: counter for wrap more than once detected
- * when dump ucode events
- */
-struct iwl_event_log {
- bool ucode_trace;
- u32 num_wraps;
- u32 next_entry;
- int non_wraps_count;
- int wraps_once_count;
- int wraps_more_count;
-};
-
-/*
* host interrupt timeout value
* used with setting interrupt coalescing timer
* the CSR_INT_COALESCING is an 8 bit register in 32-usec unit
@@ -895,18 +870,6 @@ struct iwl_event_log {
#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10)
#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0)
-/*
- * This is the threshold value of plcp error rate per 100mSecs. It is
- * used to set and check for the validity of plcp_delta.
- */
-#define IWL_MAX_PLCP_ERR_THRESHOLD_MIN (1)
-#define IWL_MAX_PLCP_ERR_THRESHOLD_DEF (50)
-#define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF (100)
-#define IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF (200)
-#define IWL_MAX_PLCP_ERR_THRESHOLD_MAX (255)
-#define IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE (0)
-
-#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3)
#define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5)
/* TX queue watchdog timeouts in mSecs */
@@ -914,12 +877,6 @@ struct iwl_event_log {
#define IWL_LONG_WD_TIMEOUT (10000)
#define IWL_MAX_WD_TIMEOUT (120000)
-enum iwl_reset {
- IWL_RF_RESET = 0,
- IWL_FW_RESET,
- IWL_MAX_FORCE_RESET,
-};
-
struct iwl_force_reset {
int reset_request_count;
int reset_success_count;
@@ -1032,11 +989,8 @@ struct iwl_priv {
/* track IBSS manager (last beacon) status */
u32 ibss_manager;
- /* storing the jiffies when the plcp error rate is received */
- unsigned long plcp_jiffies;
-
/* force reset */
- struct iwl_force_reset force_reset[IWL_MAX_FORCE_RESET];
+ struct iwl_force_reset force_reset;
/* we allocate array of iwl_channel_info for NIC's valid channels.
* Access via channel # using indirect index array */
@@ -1057,7 +1011,6 @@ struct iwl_priv {
enum ieee80211_band scan_band;
struct cfg80211_scan_request *scan_request;
struct ieee80211_vif *scan_vif;
- bool is_internal_short_scan;
u8 scan_tx_ant[IEEE80211_NUM_BANDS];
u8 mgmt_tx_ant;
@@ -1212,12 +1165,6 @@ struct iwl_priv {
#endif
#if defined(CONFIG_IWL4965) || defined(CONFIG_IWL4965_MODULE)
struct {
- /*
- * reporting the number of tids has AGG on. 0 means
- * no AGGREGATION
- */
- u8 agg_tids_count;
-
struct iwl_rx_phy_res last_phy_res;
bool last_phy_res_valid;
@@ -1256,7 +1203,6 @@ struct iwl_priv {
struct iwl_rxon_context *beacon_ctx;
struct sk_buff *beacon_skb;
- struct work_struct start_internal_scan;
struct work_struct tx_flush;
struct tasklet_struct irq_tasklet;
@@ -1293,12 +1239,9 @@ struct iwl_priv {
u32 disable_tx_power_cal;
struct work_struct run_time_calib_work;
struct timer_list statistics_periodic;
- struct timer_list ucode_trace;
struct timer_list watchdog;
bool hw_ready;
- struct iwl_event_log event_log;
-
struct led_classdev led;
unsigned long blink_on, blink_off;
bool led_registered;
diff --git a/drivers/net/wireless/iwlegacy/iwl-devtrace.c b/drivers/net/wireless/iwlegacy/iwl-devtrace.c
index 080b852b33bd..acec99197ce0 100644
--- a/drivers/net/wireless/iwlegacy/iwl-devtrace.c
+++ b/drivers/net/wireless/iwlegacy/iwl-devtrace.c
@@ -38,8 +38,5 @@ EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_ioread32);
EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_iowrite32);
EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_rx);
EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_tx);
-EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_ucode_event);
EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_ucode_error);
-EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_ucode_cont_event);
-EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_ucode_wrap_event);
#endif
diff --git a/drivers/net/wireless/iwlegacy/iwl-devtrace.h b/drivers/net/wireless/iwlegacy/iwl-devtrace.h
index 9612aa0f6ec4..a443725ba6be 100644
--- a/drivers/net/wireless/iwlegacy/iwl-devtrace.h
+++ b/drivers/net/wireless/iwlegacy/iwl-devtrace.h
@@ -96,47 +96,6 @@ TRACE_EVENT(iwlwifi_legacy_dev_iowrite32,
#undef TRACE_SYSTEM
#define TRACE_SYSTEM iwlwifi_legacy_ucode
-TRACE_EVENT(iwlwifi_legacy_dev_ucode_cont_event,
- TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev),
- TP_ARGS(priv, time, data, ev),
- TP_STRUCT__entry(
- PRIV_ENTRY
-
- __field(u32, time)
- __field(u32, data)
- __field(u32, ev)
- ),
- TP_fast_assign(
- PRIV_ASSIGN;
- __entry->time = time;
- __entry->data = data;
- __entry->ev = ev;
- ),
- TP_printk("[%p] EVT_LOGT:%010u:0x%08x:%04u",
- __entry->priv, __entry->time, __entry->data, __entry->ev)
-);
-
-TRACE_EVENT(iwlwifi_legacy_dev_ucode_wrap_event,
- TP_PROTO(struct iwl_priv *priv, u32 wraps, u32 n_entry, u32 p_entry),
- TP_ARGS(priv, wraps, n_entry, p_entry),
- TP_STRUCT__entry(
- PRIV_ENTRY
-
- __field(u32, wraps)
- __field(u32, n_entry)
- __field(u32, p_entry)
- ),
- TP_fast_assign(
- PRIV_ASSIGN;
- __entry->wraps = wraps;
- __entry->n_entry = n_entry;
- __entry->p_entry = p_entry;
- ),
- TP_printk("[%p] wraps=#%02d n=0x%X p=0x%X",
- __entry->priv, __entry->wraps, __entry->n_entry,
- __entry->p_entry)
-);
-
#undef TRACE_SYSTEM
#define TRACE_SYSTEM iwlwifi
@@ -242,25 +201,6 @@ TRACE_EVENT(iwlwifi_legacy_dev_ucode_error,
__entry->blink2, __entry->ilink1, __entry->ilink2)
);
-TRACE_EVENT(iwlwifi_legacy_dev_ucode_event,
- TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev),
- TP_ARGS(priv, time, data, ev),
- TP_STRUCT__entry(
- PRIV_ENTRY
-
- __field(u32, time)
- __field(u32, data)
- __field(u32, ev)
- ),
- TP_fast_assign(
- PRIV_ASSIGN;
- __entry->time = time;
- __entry->data = data;
- __entry->ev = ev;
- ),
- TP_printk("[%p] EVT_LOGT:%010u:0x%08x:%04u",
- __entry->priv, __entry->time, __entry->data, __entry->ev)
-);
#endif /* __IWLWIFI_DEVICE_TRACE */
#undef TRACE_INCLUDE_PATH
diff --git a/drivers/net/wireless/iwlegacy/iwl-eeprom.c b/drivers/net/wireless/iwlegacy/iwl-eeprom.c
index cb346d1a9ffa..5bf3f49b74ab 100644
--- a/drivers/net/wireless/iwlegacy/iwl-eeprom.c
+++ b/drivers/net/wireless/iwlegacy/iwl-eeprom.c
@@ -316,7 +316,6 @@ static void iwl_legacy_init_band_reference(const struct iwl_priv *priv,
break;
default:
BUG();
- return;
}
}
diff --git a/drivers/net/wireless/iwlegacy/iwl-hcmd.c b/drivers/net/wireless/iwlegacy/iwl-hcmd.c
index 62b4b09122cb..ce1fc9feb61f 100644
--- a/drivers/net/wireless/iwlegacy/iwl-hcmd.c
+++ b/drivers/net/wireless/iwlegacy/iwl-hcmd.c
@@ -167,7 +167,7 @@ int iwl_legacy_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
goto out;
}
- ret = wait_event_interruptible_timeout(priv->wait_command_queue,
+ ret = wait_event_timeout(priv->wait_command_queue,
!test_bit(STATUS_HCMD_ACTIVE, &priv->status),
HOST_COMPLETE_TIMEOUT);
if (!ret) {
diff --git a/drivers/net/wireless/iwlegacy/iwl-helpers.h b/drivers/net/wireless/iwlegacy/iwl-helpers.h
index a6effdae63f9..5cf23eaecbbb 100644
--- a/drivers/net/wireless/iwlegacy/iwl-helpers.h
+++ b/drivers/net/wireless/iwlegacy/iwl-helpers.h
@@ -132,7 +132,16 @@ static inline void iwl_legacy_stop_queue(struct iwl_priv *priv,
ieee80211_stop_queue(priv->hw, ac);
}
+#ifdef ieee80211_stop_queue
+#undef ieee80211_stop_queue
+#endif
+
#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue
+
+#ifdef ieee80211_wake_queue
+#undef ieee80211_wake_queue
+#endif
+
#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue
static inline void iwl_legacy_disable_interrupts(struct iwl_priv *priv)
diff --git a/drivers/net/wireless/iwlegacy/iwl-led.c b/drivers/net/wireless/iwlegacy/iwl-led.c
index bda0d61b2c0d..dc568a474c5d 100644
--- a/drivers/net/wireless/iwlegacy/iwl-led.c
+++ b/drivers/net/wireless/iwlegacy/iwl-led.c
@@ -33,7 +33,6 @@
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
diff --git a/drivers/net/wireless/iwlegacy/iwl-rx.c b/drivers/net/wireless/iwlegacy/iwl-rx.c
index 654cf233a384..f4d21ec22497 100644
--- a/drivers/net/wireless/iwlegacy/iwl-rx.c
+++ b/drivers/net/wireless/iwlegacy/iwl-rx.c
@@ -29,6 +29,7 @@
#include <linux/etherdevice.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <net/mac80211.h>
#include <asm/unaligned.h>
#include "iwl-eeprom.h"
@@ -227,27 +228,6 @@ void iwl_legacy_rx_spectrum_measure_notif(struct iwl_priv *priv,
}
EXPORT_SYMBOL(iwl_legacy_rx_spectrum_measure_notif);
-void iwl_legacy_recover_from_statistics(struct iwl_priv *priv,
- struct iwl_rx_packet *pkt)
-{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
- if (iwl_legacy_is_any_associated(priv)) {
- if (priv->cfg->ops->lib->check_plcp_health) {
- if (!priv->cfg->ops->lib->check_plcp_health(
- priv, pkt)) {
- /*
- * high plcp error detected
- * reset Radio
- */
- iwl_legacy_force_reset(priv,
- IWL_RF_RESET, false);
- }
- }
- }
-}
-EXPORT_SYMBOL(iwl_legacy_recover_from_statistics);
-
/*
* returns non-zero if packet should be dropped
*/
diff --git a/drivers/net/wireless/iwlegacy/iwl-scan.c b/drivers/net/wireless/iwlegacy/iwl-scan.c
index 353234a02c6d..521b73b527d3 100644
--- a/drivers/net/wireless/iwlegacy/iwl-scan.c
+++ b/drivers/net/wireless/iwlegacy/iwl-scan.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/etherdevice.h>
+#include <linux/export.h>
#include <net/mac80211.h>
#include "iwl-eeprom.h"
@@ -101,7 +102,6 @@ static void iwl_legacy_complete_scan(struct iwl_priv *priv, bool aborted)
ieee80211_scan_completed(priv->hw, aborted);
}
- priv->is_internal_short_scan = false;
priv->scan_vif = NULL;
priv->scan_request = NULL;
}
@@ -329,10 +329,8 @@ void iwl_legacy_init_scan_params(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_legacy_init_scan_params);
-static int __must_check iwl_legacy_scan_initiate(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- bool internal,
- enum ieee80211_band band)
+static int iwl_legacy_scan_initiate(struct iwl_priv *priv,
+ struct ieee80211_vif *vif)
{
int ret;
@@ -359,18 +357,14 @@ static int __must_check iwl_legacy_scan_initiate(struct iwl_priv *priv,
return -EBUSY;
}
- IWL_DEBUG_SCAN(priv, "Starting %sscan...\n",
- internal ? "internal short " : "");
+ IWL_DEBUG_SCAN(priv, "Starting scan...\n");
set_bit(STATUS_SCANNING, &priv->status);
- priv->is_internal_short_scan = internal;
priv->scan_start = jiffies;
- priv->scan_band = band;
ret = priv->cfg->ops->utils->request_scan(priv, vif);
if (ret) {
clear_bit(STATUS_SCANNING, &priv->status);
- priv->is_internal_short_scan = false;
return ret;
}
@@ -394,8 +388,7 @@ int iwl_legacy_mac_hw_scan(struct ieee80211_hw *hw,
mutex_lock(&priv->mutex);
- if (test_bit(STATUS_SCANNING, &priv->status) &&
- !priv->is_internal_short_scan) {
+ if (test_bit(STATUS_SCANNING, &priv->status)) {
IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
ret = -EAGAIN;
goto out_unlock;
@@ -404,17 +397,9 @@ int iwl_legacy_mac_hw_scan(struct ieee80211_hw *hw,
/* mac80211 will only ask for one band at a time */
priv->scan_request = req;
priv->scan_vif = vif;
+ priv->scan_band = req->channels[0]->band;
- /*
- * If an internal scan is in progress, just set
- * up the scan_request as per above.
- */
- if (priv->is_internal_short_scan) {
- IWL_DEBUG_SCAN(priv, "SCAN request during internal scan\n");
- ret = 0;
- } else
- ret = iwl_legacy_scan_initiate(priv, vif, false,
- req->channels[0]->band);
+ ret = iwl_legacy_scan_initiate(priv, vif);
IWL_DEBUG_MAC80211(priv, "leave\n");
@@ -425,40 +410,6 @@ out_unlock:
}
EXPORT_SYMBOL(iwl_legacy_mac_hw_scan);
-/*
- * internal short scan, this function should only been called while associated.
- * It will reset and tune the radio to prevent possible RF related problem
- */
-void iwl_legacy_internal_short_hw_scan(struct iwl_priv *priv)
-{
- queue_work(priv->workqueue, &priv->start_internal_scan);
-}
-
-static void iwl_legacy_bg_start_internal_scan(struct work_struct *work)
-{
- struct iwl_priv *priv =
- container_of(work, struct iwl_priv, start_internal_scan);
-
- IWL_DEBUG_SCAN(priv, "Start internal scan\n");
-
- mutex_lock(&priv->mutex);
-
- if (priv->is_internal_short_scan == true) {
- IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n");
- goto unlock;
- }
-
- if (test_bit(STATUS_SCANNING, &priv->status)) {
- IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
- goto unlock;
- }
-
- if (iwl_legacy_scan_initiate(priv, NULL, true, priv->band))
- IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n");
- unlock:
- mutex_unlock(&priv->mutex);
-}
-
static void iwl_legacy_bg_scan_check(struct work_struct *data)
{
struct iwl_priv *priv =
@@ -542,8 +493,7 @@ static void iwl_legacy_bg_scan_completed(struct work_struct *work)
container_of(work, struct iwl_priv, scan_completed);
bool aborted;
- IWL_DEBUG_SCAN(priv, "Completed %sscan.\n",
- priv->is_internal_short_scan ? "internal short " : "");
+ IWL_DEBUG_SCAN(priv, "Completed scan.\n");
cancel_delayed_work(&priv->scan_check);
@@ -558,27 +508,6 @@ static void iwl_legacy_bg_scan_completed(struct work_struct *work)
goto out_settings;
}
- if (priv->is_internal_short_scan && !aborted) {
- int err;
-
- /* Check if mac80211 requested scan during our internal scan */
- if (priv->scan_request == NULL)
- goto out_complete;
-
- /* If so request a new scan */
- err = iwl_legacy_scan_initiate(priv, priv->scan_vif, false,
- priv->scan_request->channels[0]->band);
- if (err) {
- IWL_DEBUG_SCAN(priv,
- "failed to initiate pending scan: %d\n", err);
- aborted = true;
- goto out_complete;
- }
-
- goto out;
- }
-
-out_complete:
iwl_legacy_complete_scan(priv, aborted);
out_settings:
@@ -590,8 +519,7 @@ out_settings:
* We do not commit power settings while scan is pending,
* do it now if the settings changed.
*/
- iwl_legacy_power_set_mode(priv, &priv->power_data.sleep_cmd_next,
- false);
+ iwl_legacy_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false);
iwl_legacy_set_tx_power(priv, priv->tx_power_next, false);
priv->cfg->ops->utils->post_scan(priv);
@@ -604,15 +532,12 @@ void iwl_legacy_setup_scan_deferred_work(struct iwl_priv *priv)
{
INIT_WORK(&priv->scan_completed, iwl_legacy_bg_scan_completed);
INIT_WORK(&priv->abort_scan, iwl_legacy_bg_abort_scan);
- INIT_WORK(&priv->start_internal_scan,
- iwl_legacy_bg_start_internal_scan);
INIT_DELAYED_WORK(&priv->scan_check, iwl_legacy_bg_scan_check);
}
EXPORT_SYMBOL(iwl_legacy_setup_scan_deferred_work);
void iwl_legacy_cancel_scan_deferred_work(struct iwl_priv *priv)
{
- cancel_work_sync(&priv->start_internal_scan);
cancel_work_sync(&priv->abort_scan);
cancel_work_sync(&priv->scan_completed);
diff --git a/drivers/net/wireless/iwlegacy/iwl-sta.c b/drivers/net/wireless/iwlegacy/iwl-sta.c
index 66f0fb2bbe00..f10df3e2813a 100644
--- a/drivers/net/wireless/iwlegacy/iwl-sta.c
+++ b/drivers/net/wireless/iwlegacy/iwl-sta.c
@@ -31,6 +31,7 @@
#include <linux/etherdevice.h>
#include <linux/sched.h>
#include <linux/lockdep.h>
+#include <linux/export.h>
#include "iwl-dev.h"
#include "iwl-core.h"
diff --git a/drivers/net/wireless/iwlegacy/iwl-tx.c b/drivers/net/wireless/iwlegacy/iwl-tx.c
index 4fff995c6f3e..c0dfb1a4e968 100644
--- a/drivers/net/wireless/iwlegacy/iwl-tx.c
+++ b/drivers/net/wireless/iwlegacy/iwl-tx.c
@@ -30,6 +30,7 @@
#include <linux/etherdevice.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include <net/mac80211.h>
#include "iwl-eeprom.h"
#include "iwl-dev.h"
@@ -625,6 +626,8 @@ iwl_legacy_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
cmd = txq->cmd[cmd_index];
meta = &txq->meta[cmd_index];
+ txq->time_stamp = jiffies;
+
pci_unmap_single(priv->pci_dev,
dma_unmap_addr(meta, mapping),
dma_unmap_len(meta, len),
@@ -645,7 +648,7 @@ iwl_legacy_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s\n",
iwl_legacy_get_cmd_string(cmd->hdr.cmd));
- wake_up_interruptible(&priv->wait_command_queue);
+ wake_up(&priv->wait_command_queue);
}
/* Mark as unmapped */
diff --git a/drivers/net/wireless/iwlegacy/iwl3945-base.c b/drivers/net/wireless/iwlegacy/iwl3945-base.c
index 0ee6be6a9c5d..b282d869a546 100644
--- a/drivers/net/wireless/iwlegacy/iwl3945-base.c
+++ b/drivers/net/wireless/iwlegacy/iwl3945-base.c
@@ -40,7 +40,6 @@
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
@@ -841,7 +840,7 @@ static void iwl3945_rx_card_state_notif(struct iwl_priv *priv,
wiphy_rfkill_set_hw_state(priv->hw->wiphy,
test_bit(STATUS_RF_KILL_HW, &priv->status));
else
- wake_up_interruptible(&priv->wait_command_queue);
+ wake_up(&priv->wait_command_queue);
}
/**
@@ -1409,212 +1408,6 @@ void iwl3945_dump_nic_error_log(struct iwl_priv *priv)
}
}
-#define EVENT_START_OFFSET (6 * sizeof(u32))
-
-/**
- * iwl3945_print_event_log - Dump error event log to syslog
- *
- */
-static int iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
- u32 num_events, u32 mode,
- int pos, char **buf, size_t bufsz)
-{
- u32 i;
- u32 base; /* SRAM byte address of event log header */
- u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
- u32 ptr; /* SRAM byte address of log data */
- u32 ev, time, data; /* event log data */
- unsigned long reg_flags;
-
- if (num_events == 0)
- return pos;
-
- base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
-
- if (mode == 0)
- event_size = 2 * sizeof(u32);
- else
- event_size = 3 * sizeof(u32);
-
- ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
-
- /* Make sure device is powered up for SRAM reads */
- spin_lock_irqsave(&priv->reg_lock, reg_flags);
- iwl_grab_nic_access(priv);
-
- /* Set starting address; reads will auto-increment */
- _iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr);
- rmb();
-
- /* "time" is actually "data" for mode 0 (no timestamp).
- * place event id # at far right for easier visual parsing. */
- for (i = 0; i < num_events; i++) {
- ev = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT);
- time = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT);
- if (mode == 0) {
- /* data, ev */
- if (bufsz) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- "0x%08x:%04u\n",
- time, ev);
- } else {
- IWL_ERR(priv, "0x%08x\t%04u\n", time, ev);
- trace_iwlwifi_legacy_dev_ucode_event(priv, 0,
- time, ev);
- }
- } else {
- data = _iwl_legacy_read_direct32(priv,
- HBUS_TARG_MEM_RDAT);
- if (bufsz) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- "%010u:0x%08x:%04u\n",
- time, data, ev);
- } else {
- IWL_ERR(priv, "%010u\t0x%08x\t%04u\n",
- time, data, ev);
- trace_iwlwifi_legacy_dev_ucode_event(priv, time,
- data, ev);
- }
- }
- }
-
- /* Allow device to power down */
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
- return pos;
-}
-
-/**
- * iwl3945_print_last_event_logs - Dump the newest # of event log to syslog
- */
-static int iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
- u32 num_wraps, u32 next_entry,
- u32 size, u32 mode,
- int pos, char **buf, size_t bufsz)
-{
- /*
- * display the newest DEFAULT_LOG_ENTRIES entries
- * i.e the entries just before the next ont that uCode would fill.
- */
- if (num_wraps) {
- if (next_entry < size) {
- pos = iwl3945_print_event_log(priv,
- capacity - (size - next_entry),
- size - next_entry, mode,
- pos, buf, bufsz);
- pos = iwl3945_print_event_log(priv, 0,
- next_entry, mode,
- pos, buf, bufsz);
- } else
- pos = iwl3945_print_event_log(priv, next_entry - size,
- size, mode,
- pos, buf, bufsz);
- } else {
- if (next_entry < size)
- pos = iwl3945_print_event_log(priv, 0,
- next_entry, mode,
- pos, buf, bufsz);
- else
- pos = iwl3945_print_event_log(priv, next_entry - size,
- size, mode,
- pos, buf, bufsz);
- }
- return pos;
-}
-
-#define DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES (20)
-
-int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
- char **buf, bool display)
-{
- u32 base; /* SRAM byte address of event log header */
- u32 capacity; /* event log capacity in # entries */
- u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
- u32 num_wraps; /* # times uCode wrapped to top of log */
- u32 next_entry; /* index of next entry to be written by uCode */
- u32 size; /* # entries that we'll print */
- int pos = 0;
- size_t bufsz = 0;
-
- base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
- if (!iwl3945_hw_valid_rtc_data_addr(base)) {
- IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base);
- return -EINVAL;
- }
-
- /* event log header */
- capacity = iwl_legacy_read_targ_mem(priv, base);
- mode = iwl_legacy_read_targ_mem(priv, base + (1 * sizeof(u32)));
- num_wraps = iwl_legacy_read_targ_mem(priv, base + (2 * sizeof(u32)));
- next_entry = iwl_legacy_read_targ_mem(priv, base + (3 * sizeof(u32)));
-
- if (capacity > priv->cfg->base_params->max_event_log_size) {
- IWL_ERR(priv, "Log capacity %d is bogus, limit to %d entries\n",
- capacity, priv->cfg->base_params->max_event_log_size);
- capacity = priv->cfg->base_params->max_event_log_size;
- }
-
- if (next_entry > priv->cfg->base_params->max_event_log_size) {
- IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n",
- next_entry, priv->cfg->base_params->max_event_log_size);
- next_entry = priv->cfg->base_params->max_event_log_size;
- }
-
- size = num_wraps ? capacity : next_entry;
-
- /* bail out if nothing in log */
- if (size == 0) {
- IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
- return pos;
- }
-
-#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG
- if (!(iwl_legacy_get_debug_level(priv) & IWL_DL_FW_ERRORS) && !full_log)
- size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES)
- ? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size;
-#else
- size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES)
- ? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size;
-#endif
-
- IWL_ERR(priv, "Start IWL Event Log Dump: display last %d count\n",
- size);
-
-#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG
- if (display) {
- if (full_log)
- bufsz = capacity * 48;
- else
- bufsz = size * 48;
- *buf = kmalloc(bufsz, GFP_KERNEL);
- if (!*buf)
- return -ENOMEM;
- }
- if ((iwl_legacy_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) {
- /* if uCode has wrapped back to top of log,
- * start at the oldest entry,
- * i.e the next one that uCode would fill.
- */
- if (num_wraps)
- pos = iwl3945_print_event_log(priv, next_entry,
- capacity - next_entry, mode,
- pos, buf, bufsz);
-
- /* (then/else) start at top of log */
- pos = iwl3945_print_event_log(priv, 0, next_entry, mode,
- pos, buf, bufsz);
- } else
- pos = iwl3945_print_last_event_logs(priv, capacity, num_wraps,
- next_entry, size, mode,
- pos, buf, bufsz);
-#else
- pos = iwl3945_print_last_event_logs(priv, capacity, num_wraps,
- next_entry, size, mode,
- pos, buf, bufsz);
-#endif
- return pos;
-}
-
static void iwl3945_irq_tasklet(struct iwl_priv *priv)
{
u32 inta, handled = 0;
@@ -1762,49 +1555,6 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv)
#endif
}
-static int iwl3945_get_single_channel_for_scan(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- enum ieee80211_band band,
- struct iwl3945_scan_channel *scan_ch)
-{
- const struct ieee80211_supported_band *sband;
- u16 passive_dwell = 0;
- u16 active_dwell = 0;
- int added = 0;
- u8 channel = 0;
-
- sband = iwl_get_hw_mode(priv, band);
- if (!sband) {
- IWL_ERR(priv, "invalid band\n");
- return added;
- }
-
- active_dwell = iwl_legacy_get_active_dwell_time(priv, band, 0);
- passive_dwell = iwl_legacy_get_passive_dwell_time(priv, band, vif);
-
- if (passive_dwell <= active_dwell)
- passive_dwell = active_dwell + 1;
-
-
- channel = iwl_legacy_get_single_channel_number(priv, band);
-
- if (channel) {
- scan_ch->channel = channel;
- scan_ch->type = 0; /* passive */
- scan_ch->active_dwell = cpu_to_le16(active_dwell);
- scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
- /* Set txpower levels to defaults */
- scan_ch->tpc.dsp_atten = 110;
- if (band == IEEE80211_BAND_5GHZ)
- scan_ch->tpc.tx_gain = ((1 << 5) | (3 << 3)) | 3;
- else
- scan_ch->tpc.tx_gain = ((1 << 5) | (5 << 3));
- added++;
- } else
- IWL_ERR(priv, "no valid channel found\n");
- return added;
-}
-
static int iwl3945_get_channels_for_scan(struct iwl_priv *priv,
enum ieee80211_band band,
u8 is_active, u8 n_probes,
@@ -2518,7 +2268,7 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
iwl3945_reg_txpower_periodic(priv);
IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n");
- wake_up_interruptible(&priv->wait_command_queue);
+ wake_up(&priv->wait_command_queue);
return;
@@ -2549,7 +2299,7 @@ static void __iwl3945_down(struct iwl_priv *priv)
iwl_legacy_clear_driver_stations(priv);
/* Unblock any waiting calls */
- wake_up_interruptible_all(&priv->wait_command_queue);
+ wake_up_all(&priv->wait_command_queue);
/* Wipe out the EXIT_PENDING status bit if we are not actually
* exiting the module */
@@ -2816,6 +2566,7 @@ int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
enum ieee80211_band band;
bool is_active = false;
int ret;
+ u16 len;
lockdep_assert_held(&priv->mutex);
@@ -2834,17 +2585,14 @@ int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
if (iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS)) {
- u16 interval = 0;
+ u16 interval;
u32 extra;
u32 suspend_time = 100;
u32 scan_suspend_time = 100;
IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
- if (priv->is_internal_short_scan)
- interval = 0;
- else
- interval = vif->bss_conf.beacon_int;
+ interval = vif->bss_conf.beacon_int;
scan->suspend_time = 0;
scan->max_out_time = cpu_to_le32(200 * 1024);
@@ -2866,9 +2614,7 @@ int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
scan_suspend_time, interval);
}
- if (priv->is_internal_short_scan) {
- IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
- } else if (priv->scan_request->n_ssids) {
+ if (priv->scan_request->n_ssids) {
int i, p = 0;
IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
for (i = 0; i < priv->scan_request->n_ssids; i++) {
@@ -2919,36 +2665,17 @@ int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
IWL_GOOD_CRC_TH_DISABLED;
- if (!priv->is_internal_short_scan) {
- scan->tx_cmd.len = cpu_to_le16(
- iwl_legacy_fill_probe_req(priv,
- (struct ieee80211_mgmt *)scan->data,
- vif->addr,
- priv->scan_request->ie,
- priv->scan_request->ie_len,
- IWL_MAX_SCAN_SIZE - sizeof(*scan)));
- } else {
- /* use bcast addr, will not be transmitted but must be valid */
- scan->tx_cmd.len = cpu_to_le16(
- iwl_legacy_fill_probe_req(priv,
- (struct ieee80211_mgmt *)scan->data,
- iwlegacy_bcast_addr, NULL, 0,
- IWL_MAX_SCAN_SIZE - sizeof(*scan)));
- }
+ len = iwl_legacy_fill_probe_req(priv, (struct ieee80211_mgmt *)scan->data,
+ vif->addr, priv->scan_request->ie,
+ priv->scan_request->ie_len,
+ IWL_MAX_SCAN_SIZE - sizeof(*scan));
+ scan->tx_cmd.len = cpu_to_le16(len);
+
/* select Rx antennas */
scan->flags |= iwl3945_get_antenna_flags(priv);
- if (priv->is_internal_short_scan) {
- scan->channel_count =
- iwl3945_get_single_channel_for_scan(priv, vif, band,
- (void *)&scan->data[le16_to_cpu(
- scan->tx_cmd.len)]);
- } else {
- scan->channel_count =
- iwl3945_get_channels_for_scan(priv, band, is_active, n_probes,
- (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)], vif);
- }
-
+ scan->channel_count = iwl3945_get_channels_for_scan(priv, band, is_active, n_probes,
+ (void *)&scan->data[len], vif);
if (scan->channel_count == 0) {
IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
return -EIO;
@@ -3125,7 +2852,7 @@ static int iwl3945_mac_start(struct ieee80211_hw *hw)
/* Wait for START_ALIVE from ucode. Otherwise callbacks from
* mac80211 will not be run successfully. */
- ret = wait_event_interruptible_timeout(priv->wait_command_queue,
+ ret = wait_event_timeout(priv->wait_command_queue,
test_bit(STATUS_READY, &priv->status),
UCODE_READY_TIMEOUT);
if (!ret) {
@@ -3824,10 +3551,7 @@ static int iwl3945_init_drv(struct iwl_priv *priv)
priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
/* initialize force reset */
- priv->force_reset[IWL_RF_RESET].reset_duration =
- IWL_DELAY_NEXT_FORCE_RF_RESET;
- priv->force_reset[IWL_FW_RESET].reset_duration =
- IWL_DELAY_NEXT_FORCE_FW_RELOAD;
+ priv->force_reset.reset_duration = IWL_DELAY_NEXT_FORCE_FW_RELOAD;
if (eeprom->version < EEPROM_3945_EEPROM_VERSION) {
IWL_WARN(priv, "Unsupported EEPROM version: 0x%04X\n",
diff --git a/drivers/net/wireless/iwlegacy/iwl4965-base.c b/drivers/net/wireless/iwlegacy/iwl4965-base.c
index 7157ba529680..d2fba9eae153 100644
--- a/drivers/net/wireless/iwlegacy/iwl4965-base.c
+++ b/drivers/net/wireless/iwlegacy/iwl4965-base.c
@@ -40,7 +40,6 @@
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
@@ -488,134 +487,6 @@ static void iwl4965_bg_statistics_periodic(unsigned long data)
iwl_legacy_send_statistics_request(priv, CMD_ASYNC, false);
}
-
-static void iwl4965_print_cont_event_trace(struct iwl_priv *priv, u32 base,
- u32 start_idx, u32 num_events,
- u32 mode)
-{
- u32 i;
- u32 ptr; /* SRAM byte address of log data */
- u32 ev, time, data; /* event log data */
- unsigned long reg_flags;
-
- if (mode == 0)
- ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32));
- else
- ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
-
- /* Make sure device is powered up for SRAM reads */
- spin_lock_irqsave(&priv->reg_lock, reg_flags);
- if (iwl_grab_nic_access(priv)) {
- spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
- return;
- }
-
- /* Set starting address; reads will auto-increment */
- _iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr);
- rmb();
-
- /*
- * "time" is actually "data" for mode 0 (no timestamp).
- * place event id # at far right for easier visual parsing.
- */
- for (i = 0; i < num_events; i++) {
- ev = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT);
- time = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT);
- if (mode == 0) {
- trace_iwlwifi_legacy_dev_ucode_cont_event(priv,
- 0, time, ev);
- } else {
- data = _iwl_legacy_read_direct32(priv,
- HBUS_TARG_MEM_RDAT);
- trace_iwlwifi_legacy_dev_ucode_cont_event(priv,
- time, data, ev);
- }
- }
- /* Allow device to power down */
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
-}
-
-static void iwl4965_continuous_event_trace(struct iwl_priv *priv)
-{
- u32 capacity; /* event log capacity in # entries */
- u32 base; /* SRAM byte address of event log header */
- u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
- u32 num_wraps; /* # times uCode wrapped to top of log */
- u32 next_entry; /* index of next entry to be written by uCode */
-
- if (priv->ucode_type == UCODE_INIT)
- base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr);
- else
- base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
- if (priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
- capacity = iwl_legacy_read_targ_mem(priv, base);
- num_wraps = iwl_legacy_read_targ_mem(priv,
- base + (2 * sizeof(u32)));
- mode = iwl_legacy_read_targ_mem(priv, base + (1 * sizeof(u32)));
- next_entry = iwl_legacy_read_targ_mem(priv,
- base + (3 * sizeof(u32)));
- } else
- return;
-
- if (num_wraps == priv->event_log.num_wraps) {
- iwl4965_print_cont_event_trace(priv,
- base, priv->event_log.next_entry,
- next_entry - priv->event_log.next_entry,
- mode);
- priv->event_log.non_wraps_count++;
- } else {
- if ((num_wraps - priv->event_log.num_wraps) > 1)
- priv->event_log.wraps_more_count++;
- else
- priv->event_log.wraps_once_count++;
- trace_iwlwifi_legacy_dev_ucode_wrap_event(priv,
- num_wraps - priv->event_log.num_wraps,
- next_entry, priv->event_log.next_entry);
- if (next_entry < priv->event_log.next_entry) {
- iwl4965_print_cont_event_trace(priv, base,
- priv->event_log.next_entry,
- capacity - priv->event_log.next_entry,
- mode);
-
- iwl4965_print_cont_event_trace(priv, base, 0,
- next_entry, mode);
- } else {
- iwl4965_print_cont_event_trace(priv, base,
- next_entry, capacity - next_entry,
- mode);
-
- iwl4965_print_cont_event_trace(priv, base, 0,
- next_entry, mode);
- }
- }
- priv->event_log.num_wraps = num_wraps;
- priv->event_log.next_entry = next_entry;
-}
-
-/**
- * iwl4965_bg_ucode_trace - Timer callback to log ucode event
- *
- * The timer is continually set to execute every
- * UCODE_TRACE_PERIOD milliseconds after the last timer expired
- * this function is to perform continuous uCode event logging operation
- * if enabled
- */
-static void iwl4965_bg_ucode_trace(unsigned long data)
-{
- struct iwl_priv *priv = (struct iwl_priv *)data;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- if (priv->event_log.ucode_trace) {
- iwl4965_continuous_event_trace(priv);
- /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */
- mod_timer(&priv->ucode_trace,
- jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD));
- }
-}
-
static void iwl4965_rx_beacon_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
@@ -704,7 +575,7 @@ static void iwl4965_rx_card_state_notif(struct iwl_priv *priv,
wiphy_rfkill_set_hw_state(priv->hw->wiphy,
test_bit(STATUS_RF_KILL_HW, &priv->status));
else
- wake_up_interruptible(&priv->wait_command_queue);
+ wake_up(&priv->wait_command_queue);
}
/**
@@ -1054,7 +925,7 @@ static void iwl4965_irq_tasklet(struct iwl_priv *priv)
handled |= CSR_INT_BIT_FH_TX;
/* Wake up uCode load routine, now that load is complete */
priv->ucode_write_complete = 1;
- wake_up_interruptible(&priv->wait_command_queue);
+ wake_up(&priv->wait_command_queue);
}
if (inta & ~handled) {
@@ -1612,7 +1483,7 @@ static const char * const desc_lookup_text[] = {
"NMI_INTERRUPT_DATA_ACTION_PT",
"NMI_TRM_HW_ER",
"NMI_INTERRUPT_TRM",
- "NMI_INTERRUPT_BREAK_POINT"
+ "NMI_INTERRUPT_BREAK_POINT",
"DEBUG_0",
"DEBUG_1",
"DEBUG_2",
@@ -1711,209 +1582,6 @@ void iwl4965_dump_nic_error_log(struct iwl_priv *priv)
pc, blink1, blink2, ilink1, ilink2, hcmd);
}
-#define EVENT_START_OFFSET (4 * sizeof(u32))
-
-/**
- * iwl4965_print_event_log - Dump error event log to syslog
- *
- */
-static int iwl4965_print_event_log(struct iwl_priv *priv, u32 start_idx,
- u32 num_events, u32 mode,
- int pos, char **buf, size_t bufsz)
-{
- u32 i;
- u32 base; /* SRAM byte address of event log header */
- u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
- u32 ptr; /* SRAM byte address of log data */
- u32 ev, time, data; /* event log data */
- unsigned long reg_flags;
-
- if (num_events == 0)
- return pos;
-
- if (priv->ucode_type == UCODE_INIT) {
- base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr);
- } else {
- base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
- }
-
- if (mode == 0)
- event_size = 2 * sizeof(u32);
- else
- event_size = 3 * sizeof(u32);
-
- ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
-
- /* Make sure device is powered up for SRAM reads */
- spin_lock_irqsave(&priv->reg_lock, reg_flags);
- iwl_grab_nic_access(priv);
-
- /* Set starting address; reads will auto-increment */
- _iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr);
- rmb();
-
- /* "time" is actually "data" for mode 0 (no timestamp).
- * place event id # at far right for easier visual parsing. */
- for (i = 0; i < num_events; i++) {
- ev = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT);
- time = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT);
- if (mode == 0) {
- /* data, ev */
- if (bufsz) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- "EVT_LOG:0x%08x:%04u\n",
- time, ev);
- } else {
- trace_iwlwifi_legacy_dev_ucode_event(priv, 0,
- time, ev);
- IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n",
- time, ev);
- }
- } else {
- data = _iwl_legacy_read_direct32(priv,
- HBUS_TARG_MEM_RDAT);
- if (bufsz) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- "EVT_LOGT:%010u:0x%08x:%04u\n",
- time, data, ev);
- } else {
- IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
- time, data, ev);
- trace_iwlwifi_legacy_dev_ucode_event(priv, time,
- data, ev);
- }
- }
- }
-
- /* Allow device to power down */
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
- return pos;
-}
-
-/**
- * iwl4965_print_last_event_logs - Dump the newest # of event log to syslog
- */
-static int iwl4965_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
- u32 num_wraps, u32 next_entry,
- u32 size, u32 mode,
- int pos, char **buf, size_t bufsz)
-{
- /*
- * display the newest DEFAULT_LOG_ENTRIES entries
- * i.e the entries just before the next ont that uCode would fill.
- */
- if (num_wraps) {
- if (next_entry < size) {
- pos = iwl4965_print_event_log(priv,
- capacity - (size - next_entry),
- size - next_entry, mode,
- pos, buf, bufsz);
- pos = iwl4965_print_event_log(priv, 0,
- next_entry, mode,
- pos, buf, bufsz);
- } else
- pos = iwl4965_print_event_log(priv, next_entry - size,
- size, mode, pos, buf, bufsz);
- } else {
- if (next_entry < size) {
- pos = iwl4965_print_event_log(priv, 0, next_entry,
- mode, pos, buf, bufsz);
- } else {
- pos = iwl4965_print_event_log(priv, next_entry - size,
- size, mode, pos, buf, bufsz);
- }
- }
- return pos;
-}
-
-#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
-
-int iwl4965_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
- char **buf, bool display)
-{
- u32 base; /* SRAM byte address of event log header */
- u32 capacity; /* event log capacity in # entries */
- u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
- u32 num_wraps; /* # times uCode wrapped to top of log */
- u32 next_entry; /* index of next entry to be written by uCode */
- u32 size; /* # entries that we'll print */
- int pos = 0;
- size_t bufsz = 0;
-
- if (priv->ucode_type == UCODE_INIT) {
- base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr);
- } else {
- base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
- }
-
- if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
- IWL_ERR(priv,
- "Invalid event log pointer 0x%08X for %s uCode\n",
- base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT");
- return -EINVAL;
- }
-
- /* event log header */
- capacity = iwl_legacy_read_targ_mem(priv, base);
- mode = iwl_legacy_read_targ_mem(priv, base + (1 * sizeof(u32)));
- num_wraps = iwl_legacy_read_targ_mem(priv, base + (2 * sizeof(u32)));
- next_entry = iwl_legacy_read_targ_mem(priv, base + (3 * sizeof(u32)));
-
- size = num_wraps ? capacity : next_entry;
-
- /* bail out if nothing in log */
- if (size == 0) {
- IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
- return pos;
- }
-
-#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG
- if (!(iwl_legacy_get_debug_level(priv) & IWL_DL_FW_ERRORS) && !full_log)
- size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
- ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#else
- size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
- ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#endif
- IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
- size);
-
-#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG
- if (display) {
- if (full_log)
- bufsz = capacity * 48;
- else
- bufsz = size * 48;
- *buf = kmalloc(bufsz, GFP_KERNEL);
- if (!*buf)
- return -ENOMEM;
- }
- if ((iwl_legacy_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) {
- /*
- * if uCode has wrapped back to top of log,
- * start at the oldest entry,
- * i.e the next one that uCode would fill.
- */
- if (num_wraps)
- pos = iwl4965_print_event_log(priv, next_entry,
- capacity - next_entry, mode,
- pos, buf, bufsz);
- /* (then/else) start at top of log */
- pos = iwl4965_print_event_log(priv, 0,
- next_entry, mode, pos, buf, bufsz);
- } else
- pos = iwl4965_print_last_event_logs(priv, capacity, num_wraps,
- next_entry, size, mode,
- pos, buf, bufsz);
-#else
- pos = iwl4965_print_last_event_logs(priv, capacity, num_wraps,
- next_entry, size, mode,
- pos, buf, bufsz);
-#endif
- return pos;
-}
-
static void iwl4965_rf_kill_ct_config(struct iwl_priv *priv)
{
struct iwl_ct_kill_config cmd;
@@ -2126,7 +1794,7 @@ static void iwl4965_alive_start(struct iwl_priv *priv)
iwl4965_rf_kill_ct_config(priv);
IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n");
- wake_up_interruptible(&priv->wait_command_queue);
+ wake_up(&priv->wait_command_queue);
iwl_legacy_power_update_mode(priv, true);
IWL_DEBUG_INFO(priv, "Updated power mode\n");
@@ -2159,7 +1827,7 @@ static void __iwl4965_down(struct iwl_priv *priv)
iwl_legacy_clear_driver_stations(priv);
/* Unblock any waiting calls */
- wake_up_interruptible_all(&priv->wait_command_queue);
+ wake_up_all(&priv->wait_command_queue);
/* Wipe out the EXIT_PENDING status bit if we are not actually
* exiting the module */
@@ -2597,7 +2265,7 @@ int iwl4965_mac_start(struct ieee80211_hw *hw)
/* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from
* mac80211 will not be run successfully. */
- ret = wait_event_interruptible_timeout(priv->wait_command_queue,
+ ret = wait_event_timeout(priv->wait_command_queue,
test_bit(STATUS_READY, &priv->status),
UCODE_READY_TIMEOUT);
if (!ret) {
@@ -2773,20 +2441,10 @@ int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw,
case IEEE80211_AMPDU_TX_START:
IWL_DEBUG_HT(priv, "start Tx\n");
ret = iwl4965_tx_agg_start(priv, vif, sta, tid, ssn);
- if (ret == 0) {
- priv->_4965.agg_tids_count++;
- IWL_DEBUG_HT(priv, "priv->_4965.agg_tids_count = %u\n",
- priv->_4965.agg_tids_count);
- }
break;
case IEEE80211_AMPDU_TX_STOP:
IWL_DEBUG_HT(priv, "stop Tx\n");
ret = iwl4965_tx_agg_stop(priv, vif, sta, tid);
- if ((ret == 0) && (priv->_4965.agg_tids_count > 0)) {
- priv->_4965.agg_tids_count--;
- IWL_DEBUG_HT(priv, "priv->_4965.agg_tids_count = %u\n",
- priv->_4965.agg_tids_count);
- }
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
ret = 0;
break;
@@ -2851,7 +2509,6 @@ void iwl4965_mac_channel_switch(struct ieee80211_hw *hw,
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
u16 ch;
- unsigned long flags = 0;
IWL_DEBUG_MAC80211(priv, "enter\n");
@@ -2868,64 +2525,64 @@ void iwl4965_mac_channel_switch(struct ieee80211_hw *hw,
if (!iwl_legacy_is_associated_ctx(ctx))
goto out;
- if (priv->cfg->ops->lib->set_channel_switch) {
+ if (!priv->cfg->ops->lib->set_channel_switch)
+ goto out;
- ch = channel->hw_value;
- if (le16_to_cpu(ctx->active.channel) != ch) {
- ch_info = iwl_legacy_get_channel_info(priv,
- channel->band,
- ch);
- if (!iwl_legacy_is_channel_valid(ch_info)) {
- IWL_DEBUG_MAC80211(priv, "invalid channel\n");
- goto out;
- }
- spin_lock_irqsave(&priv->lock, flags);
-
- priv->current_ht_config.smps = conf->smps_mode;
-
- /* Configure HT40 channels */
- ctx->ht.enabled = conf_is_ht(conf);
- if (ctx->ht.enabled) {
- if (conf_is_ht40_minus(conf)) {
- ctx->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- ctx->ht.is_40mhz = true;
- } else if (conf_is_ht40_plus(conf)) {
- ctx->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
- ctx->ht.is_40mhz = true;
- } else {
- ctx->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_NONE;
- ctx->ht.is_40mhz = false;
- }
- } else
- ctx->ht.is_40mhz = false;
-
- if ((le16_to_cpu(ctx->staging.channel) != ch))
- ctx->staging.flags = 0;
-
- iwl_legacy_set_rxon_channel(priv, channel, ctx);
- iwl_legacy_set_rxon_ht(priv, ht_conf);
- iwl_legacy_set_flags_for_band(priv, ctx, channel->band,
- ctx->vif);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- iwl_legacy_set_rate(priv);
- /*
- * at this point, staging_rxon has the
- * configuration for channel switch
- */
- set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status);
- priv->switch_channel = cpu_to_le16(ch);
- if (priv->cfg->ops->lib->set_channel_switch(priv, ch_switch)) {
- clear_bit(STATUS_CHANNEL_SWITCH_PENDING,
- &priv->status);
- priv->switch_channel = 0;
- ieee80211_chswitch_done(ctx->vif, false);
- }
+ ch = channel->hw_value;
+ if (le16_to_cpu(ctx->active.channel) == ch)
+ goto out;
+
+ ch_info = iwl_legacy_get_channel_info(priv, channel->band, ch);
+ if (!iwl_legacy_is_channel_valid(ch_info)) {
+ IWL_DEBUG_MAC80211(priv, "invalid channel\n");
+ goto out;
+ }
+
+ spin_lock_irq(&priv->lock);
+
+ priv->current_ht_config.smps = conf->smps_mode;
+
+ /* Configure HT40 channels */
+ ctx->ht.enabled = conf_is_ht(conf);
+ if (ctx->ht.enabled) {
+ if (conf_is_ht40_minus(conf)) {
+ ctx->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ ctx->ht.is_40mhz = true;
+ } else if (conf_is_ht40_plus(conf)) {
+ ctx->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ ctx->ht.is_40mhz = true;
+ } else {
+ ctx->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ ctx->ht.is_40mhz = false;
}
+ } else
+ ctx->ht.is_40mhz = false;
+
+ if ((le16_to_cpu(ctx->staging.channel) != ch))
+ ctx->staging.flags = 0;
+
+ iwl_legacy_set_rxon_channel(priv, channel, ctx);
+ iwl_legacy_set_rxon_ht(priv, ht_conf);
+ iwl_legacy_set_flags_for_band(priv, ctx, channel->band, ctx->vif);
+
+ spin_unlock_irq(&priv->lock);
+
+ iwl_legacy_set_rate(priv);
+ /*
+ * at this point, staging_rxon has the
+ * configuration for channel switch
+ */
+ set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status);
+ priv->switch_channel = cpu_to_le16(ch);
+ if (priv->cfg->ops->lib->set_channel_switch(priv, ch_switch)) {
+ clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status);
+ priv->switch_channel = 0;
+ ieee80211_chswitch_done(ctx->vif, false);
}
+
out:
mutex_unlock(&priv->mutex);
IWL_DEBUG_MAC80211(priv, "leave\n");
@@ -3034,10 +2691,6 @@ static void iwl4965_setup_deferred_work(struct iwl_priv *priv)
priv->statistics_periodic.data = (unsigned long)priv;
priv->statistics_periodic.function = iwl4965_bg_statistics_periodic;
- init_timer(&priv->ucode_trace);
- priv->ucode_trace.data = (unsigned long)priv;
- priv->ucode_trace.function = iwl4965_bg_ucode_trace;
-
init_timer(&priv->watchdog);
priv->watchdog.data = (unsigned long)priv;
priv->watchdog.function = iwl_legacy_bg_watchdog;
@@ -3056,7 +2709,6 @@ static void iwl4965_cancel_deferred_work(struct iwl_priv *priv)
iwl_legacy_cancel_scan_deferred_work(priv);
del_timer_sync(&priv->statistics_periodic);
- del_timer_sync(&priv->ucode_trace);
}
static void iwl4965_init_hw_rates(struct iwl_priv *priv,
@@ -3132,13 +2784,9 @@ static int iwl4965_init_drv(struct iwl_priv *priv)
priv->iw_mode = NL80211_IFTYPE_STATION;
priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
- priv->_4965.agg_tids_count = 0;
/* initialize force reset */
- priv->force_reset[IWL_RF_RESET].reset_duration =
- IWL_DELAY_NEXT_FORCE_RF_RESET;
- priv->force_reset[IWL_FW_RESET].reset_duration =
- IWL_DELAY_NEXT_FORCE_FW_RELOAD;
+ priv->force_reset.reset_duration = IWL_DELAY_NEXT_FORCE_FW_RELOAD;
/* Choose which receivers/antennas to use */
if (priv->cfg->ops->hcmd->set_rxon_chain)
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index ad3bdba6beed..57703d5209d7 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -1,5 +1,5 @@
-config IWLAGN
- tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlagn) "
+config IWLWIFI
+ tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) "
depends on PCI && MAC80211
select FW_LOADER
select NEW_LEDS
@@ -39,14 +39,14 @@ config IWLAGN
If you want to compile the driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read <file:Documentation/kbuild/modules.txt>. The
- module will be called iwlagn.
+ module will be called iwlwifi.
menu "Debugging Options"
- depends on IWLAGN
+ depends on IWLWIFI
config IWLWIFI_DEBUG
- bool "Enable full debugging output in the iwlagn driver"
- depends on IWLAGN
+ bool "Enable full debugging output in the iwlwifi driver"
+ depends on IWLWIFI
---help---
This option will enable debug tracing output for the iwlwifi drivers
@@ -54,13 +54,13 @@ config IWLWIFI_DEBUG
control which debug output is sent to the kernel log by setting the
value in
- /sys/class/net/wlan0/device/debug_level
+ /sys/module/iwlwifi/parameters/debug
This entry will only exist if this option is enabled.
To set a value, simply echo an 8-byte hex value to the same file:
- % echo 0x43fff > /sys/class/net/wlan0/device/debug_level
+ % echo 0x43fff > /sys/module/iwlwifi/parameters/debug
You can find the list of debug mask values in:
drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -70,8 +70,8 @@ config IWLWIFI_DEBUG
any problems you may encounter.
config IWLWIFI_DEBUGFS
- bool "iwlagn debugfs support"
- depends on IWLAGN && MAC80211_DEBUGFS
+ bool "iwlwifi debugfs support"
+ depends on IWLWIFI && MAC80211_DEBUGFS
---help---
Enable creation of debugfs files for the iwlwifi drivers. This
is a low-impact option that allows getting insight into the
@@ -79,13 +79,13 @@ config IWLWIFI_DEBUGFS
config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
bool "Experimental uCode support"
- depends on IWLAGN && IWLWIFI_DEBUG
+ depends on IWLWIFI && IWLWIFI_DEBUG
---help---
Enable use of experimental ucode for testing and debugging.
config IWLWIFI_DEVICE_TRACING
bool "iwlwifi device access tracing"
- depends on IWLAGN
+ depends on IWLWIFI
depends on EVENT_TRACING
help
Say Y here to trace all commands, including TX frames and IO
@@ -104,27 +104,10 @@ endmenu
config IWLWIFI_DEVICE_SVTOOL
bool "iwlwifi device svtool support"
- depends on IWLAGN
+ depends on IWLWIFI
select NL80211_TESTMODE
help
This option enables the svtool support for iwlwifi device through
NL80211_TESTMODE. svtool is a software validation tool that runs in
the user space and interacts with the device in the kernel space
through the generic netlink message via NL80211_TESTMODE channel.
-
-config IWL_P2P
- bool "iwlwifi experimental P2P support"
- depends on IWLAGN
- help
- This option enables experimental P2P support for some devices
- based on microcode support. Since P2P support is still under
- development, this option may even enable it for some devices
- now that turn out to not support it in the future due to
- microcode restrictions.
-
- To determine if your microcode supports the experimental P2P
- offered by this option, check if the driver advertises AP
- support when it is loaded.
-
- Say Y only if you want to experiment with P2P.
-
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 822660483f9f..c73e5ed8db5e 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -1,22 +1,24 @@
-# AGN
-obj-$(CONFIG_IWLAGN) += iwlagn.o
-iwlagn-objs := iwl-agn.o iwl-agn-rs.o
-iwlagn-objs += iwl-agn-ucode.o iwl-agn-tx.o
-iwlagn-objs += iwl-agn-lib.o iwl-agn-calib.o iwl-io.o
-iwlagn-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-eeprom.o
+# WIFI
+obj-$(CONFIG_IWLWIFI) += iwlwifi.o
+iwlwifi-objs := iwl-agn.o iwl-agn-rs.o
+iwlwifi-objs += iwl-agn-ucode.o iwl-agn-tx.o
+iwlwifi-objs += iwl-agn-lib.o iwl-agn-calib.o iwl-io.o
+iwlwifi-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-rx.o
-iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-hcmd.o iwl-power.o
-iwlagn-objs += iwl-rx.o iwl-tx.o iwl-sta.o
-iwlagn-objs += iwl-scan.o iwl-led.o
-iwlagn-objs += iwl-agn-rxon.o iwl-agn-hcmd.o iwl-agn-ict.o
-iwlagn-objs += iwl-5000.o
-iwlagn-objs += iwl-6000.o
-iwlagn-objs += iwl-1000.o
-iwlagn-objs += iwl-2000.o
+iwlwifi-objs += iwl-core.o iwl-eeprom.o iwl-power.o
+iwlwifi-objs += iwl-scan.o iwl-led.o
+iwlwifi-objs += iwl-agn-rxon.o
+iwlwifi-objs += iwl-5000.o
+iwlwifi-objs += iwl-6000.o
+iwlwifi-objs += iwl-1000.o
+iwlwifi-objs += iwl-2000.o
+iwlwifi-objs += iwl-pci.o
+iwlwifi-objs += iwl-trans.o
+iwlwifi-objs += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o
-iwlagn-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
-iwlagn-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
-iwlagn-$(CONFIG_IWLWIFI_DEVICE_SVTOOL) += iwl-sv-open.o
+iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
+iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
+iwlwifi-$(CONFIG_IWLWIFI_DEVICE_SVTOOL) += iwl-sv-open.o
CFLAGS_iwl-devtrace.o := -I$(src)
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index 2a88e73bb39c..e12b48c2cff6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -27,12 +27,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
@@ -42,14 +39,18 @@
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-io.h"
-#include "iwl-sta.h"
#include "iwl-agn.h"
-#include "iwl-helpers.h"
#include "iwl-agn-hw.h"
+#include "iwl-shared.h"
+#include "iwl-cfg.h"
/* Highest firmware API version supported */
-#define IWL1000_UCODE_API_MAX 5
-#define IWL100_UCODE_API_MAX 5
+#define IWL1000_UCODE_API_MAX 6
+#define IWL100_UCODE_API_MAX 6
+
+/* Oldest version we won't warn about */
+#define IWL1000_UCODE_API_OK 5
+#define IWL100_UCODE_API_OK 5
/* Lowest firmware API version supported */
#define IWL1000_UCODE_API_MIN 1
@@ -75,21 +76,21 @@
static void iwl1000_set_ct_threshold(struct iwl_priv *priv)
{
/* want Celsius */
- priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY;
- priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
+ hw_params(priv).ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY;
+ hw_params(priv).ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
}
/* NIC configuration for 1000 series */
static void iwl1000_nic_config(struct iwl_priv *priv)
{
/* set CSR_HW_CONFIG_REG for uCode use */
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+ iwl_set_bit(bus(priv), CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
/* Setting digital SVR for 1000 card to 1.32V */
/* locking is acquired in iwl_set_bits_mask_prph() function */
- iwl_set_bits_mask_prph(priv, APMG_DIGITAL_SVR_REG,
+ iwl_set_bits_mask_prph(bus(priv), APMG_DIGITAL_SVR_REG,
APMG_SVR_DIGITAL_VOLTAGE_1_32,
~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK);
}
@@ -126,61 +127,43 @@ static int iwl1000_hw_set_hw_params(struct iwl_priv *priv)
priv->cfg->base_params->num_of_queues =
iwlagn_mod_params.num_of_queues;
- priv->hw_params.max_txq_num = priv->cfg->base_params->num_of_queues;
- priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM;
- priv->hw_params.scd_bc_tbls_size =
- priv->cfg->base_params->num_of_queues *
- sizeof(struct iwlagn_scd_bc_tbl);
- priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
- priv->hw_params.max_stations = IWLAGN_STATION_COUNT;
+ hw_params(priv).max_txq_num = priv->cfg->base_params->num_of_queues;
priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID;
- priv->hw_params.max_data_size = IWLAGN_RTC_DATA_SIZE;
- priv->hw_params.max_inst_size = IWLAGN_RTC_INST_SIZE;
+ hw_params(priv).max_data_size = IWLAGN_RTC_DATA_SIZE;
+ hw_params(priv).max_inst_size = IWLAGN_RTC_INST_SIZE;
- priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) |
- BIT(IEEE80211_BAND_5GHZ);
- priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
+ hw_params(priv).ht40_channel = BIT(IEEE80211_BAND_2GHZ);
- priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
+ hw_params(priv).tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
if (priv->cfg->rx_with_siso_diversity)
- priv->hw_params.rx_chains_num = 1;
+ hw_params(priv).rx_chains_num = 1;
else
- priv->hw_params.rx_chains_num =
+ hw_params(priv).rx_chains_num =
num_of_ant(priv->cfg->valid_rx_ant);
- priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
- priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
+ hw_params(priv).valid_tx_ant = priv->cfg->valid_tx_ant;
+ hw_params(priv).valid_rx_ant = priv->cfg->valid_rx_ant;
iwl1000_set_ct_threshold(priv);
/* Set initial sensitivity parameters */
/* Set initial calibration set */
- priv->hw_params.sens = &iwl1000_sensitivity;
- priv->hw_params.calib_init_cfg =
+ hw_params(priv).sens = &iwl1000_sensitivity;
+ hw_params(priv).calib_init_cfg =
BIT(IWL_CALIB_XTAL) |
BIT(IWL_CALIB_LO) |
BIT(IWL_CALIB_TX_IQ) |
BIT(IWL_CALIB_TX_IQ_PERD) |
BIT(IWL_CALIB_BASE_BAND);
if (priv->cfg->need_dc_calib)
- priv->hw_params.calib_init_cfg |= BIT(IWL_CALIB_DC);
-
- priv->hw_params.beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS;
+ hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_DC);
return 0;
}
static struct iwl_lib_ops iwl1000_lib = {
.set_hw_params = iwl1000_hw_set_hw_params,
- .rx_handler_setup = iwlagn_rx_handler_setup,
- .setup_deferred_work = iwlagn_setup_deferred_work,
- .is_valid_rtc_data_addr = iwlagn_hw_valid_rtc_data_addr,
- .send_tx_power = iwlagn_send_tx_power,
- .update_chain_flags = iwl_update_chain_flags,
- .apm_ops = {
- .init = iwl_apm_init,
- .config = iwl1000_nic_config,
- },
+ .nic_config = iwl1000_nic_config,
.eeprom_ops = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -191,19 +174,8 @@ static struct iwl_lib_ops iwl1000_lib = {
EEPROM_REG_BAND_24_HT40_CHANNELS,
EEPROM_REGULATORY_BAND_NO_HT40,
},
- .query_addr = iwlagn_eeprom_query_addr,
},
- .temp_ops = {
- .temperature = iwlagn_temperature,
- },
- .txfifo_flush = iwlagn_txfifo_flush,
- .dev_txfifo_flush = iwlagn_dev_txfifo_flush,
-};
-
-static const struct iwl_ops iwl1000_ops = {
- .lib = &iwl1000_lib,
- .hcmd = &iwlagn_hcmd,
- .utils = &iwlagn_hcmd_utils,
+ .temperature = iwlagn_temperature,
};
static struct iwl_base_params iwl1000_base_params = {
@@ -214,7 +186,6 @@ static struct iwl_base_params iwl1000_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_1000,
.shadow_ram_support = false,
.led_compensation = 51,
- .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
@@ -224,15 +195,17 @@ static struct iwl_base_params iwl1000_base_params = {
static struct iwl_ht_params iwl1000_ht_params = {
.ht_greenfield_support = true,
.use_rts_for_aggregation = true, /* use rts/cts protection */
+ .smps_mode = IEEE80211_SMPS_DYNAMIC,
};
#define IWL_DEVICE_1000 \
.fw_name_pre = IWL1000_FW_PRE, \
.ucode_api_max = IWL1000_UCODE_API_MAX, \
+ .ucode_api_ok = IWL1000_UCODE_API_OK, \
.ucode_api_min = IWL1000_UCODE_API_MIN, \
.eeprom_ver = EEPROM_1000_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_1000_TX_POWER_VERSION, \
- .ops = &iwl1000_ops, \
+ .lib = &iwl1000_lib, \
.base_params = &iwl1000_base_params, \
.led_mode = IWL_LED_BLINK
@@ -250,10 +223,11 @@ struct iwl_cfg iwl1000_bg_cfg = {
#define IWL_DEVICE_100 \
.fw_name_pre = IWL100_FW_PRE, \
.ucode_api_max = IWL100_UCODE_API_MAX, \
+ .ucode_api_ok = IWL100_UCODE_API_OK, \
.ucode_api_min = IWL100_UCODE_API_MIN, \
.eeprom_ver = EEPROM_1000_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_1000_TX_POWER_VERSION, \
- .ops = &iwl1000_ops, \
+ .lib = &iwl1000_lib, \
.base_params = &iwl1000_base_params, \
.led_mode = IWL_LED_RF_STATE, \
.rx_with_siso_diversity = true
diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c
index 3df76f53a41b..79431977a968 100644
--- a/drivers/net/wireless/iwlwifi/iwl-2000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-2000.c
@@ -27,12 +27,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
@@ -42,21 +39,28 @@
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-io.h"
-#include "iwl-sta.h"
#include "iwl-agn.h"
-#include "iwl-helpers.h"
#include "iwl-agn-hw.h"
-#include "iwl-6000-hw.h"
+#include "iwl-shared.h"
+#include "iwl-cfg.h"
/* Highest firmware API version supported */
-#define IWL2030_UCODE_API_MAX 5
-#define IWL2000_UCODE_API_MAX 5
-#define IWL105_UCODE_API_MAX 5
+#define IWL2030_UCODE_API_MAX 6
+#define IWL2000_UCODE_API_MAX 6
+#define IWL105_UCODE_API_MAX 6
+#define IWL135_UCODE_API_MAX 6
+
+/* Oldest version we won't warn about */
+#define IWL2030_UCODE_API_OK 5
+#define IWL2000_UCODE_API_OK 5
+#define IWL105_UCODE_API_OK 5
+#define IWL135_UCODE_API_OK 5
/* Lowest firmware API version supported */
#define IWL2030_UCODE_API_MIN 5
#define IWL2000_UCODE_API_MIN 5
#define IWL105_UCODE_API_MIN 5
+#define IWL135_UCODE_API_MIN 5
#define IWL2030_FW_PRE "iwlwifi-2030-"
#define IWL2030_MODULE_FIRMWARE(api) IWL2030_FW_PRE __stringify(api) ".ucode"
@@ -67,38 +71,24 @@
#define IWL105_FW_PRE "iwlwifi-105-"
#define IWL105_MODULE_FIRMWARE(api) IWL105_FW_PRE __stringify(api) ".ucode"
+#define IWL135_FW_PRE "iwlwifi-135-"
+#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE __stringify(api) ".ucode"
+
static void iwl2000_set_ct_threshold(struct iwl_priv *priv)
{
/* want Celsius */
- priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD;
- priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
+ hw_params(priv).ct_kill_threshold = CT_KILL_THRESHOLD;
+ hw_params(priv).ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
}
/* NIC configuration for 2000 series */
static void iwl2000_nic_config(struct iwl_priv *priv)
{
- u16 radio_cfg;
-
- radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
-
- /* write radio config values to register */
- if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX)
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
- EEPROM_RF_CFG_TYPE_MSK(radio_cfg) |
- EEPROM_RF_CFG_STEP_MSK(radio_cfg) |
- EEPROM_RF_CFG_DASH_MSK(radio_cfg));
-
- /* set CSR_HW_CONFIG_REG for uCode use */
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
- CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
+ iwl_rf_config(priv);
if (priv->cfg->iq_invert)
- iwl_set_bit(priv, CSR_GP_DRIVER_REG,
+ iwl_set_bit(bus(priv), CSR_GP_DRIVER_REG,
CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER);
-
- if (priv->cfg->disable_otp_refresh)
- iwl_write_prph(priv, APMG_ANALOG_SVR_REG, 0x80000010);
}
static struct iwl_sensitivity_ranges iwl2000_sensitivity = {
@@ -133,63 +123,44 @@ static int iwl2000_hw_set_hw_params(struct iwl_priv *priv)
priv->cfg->base_params->num_of_queues =
iwlagn_mod_params.num_of_queues;
- priv->hw_params.max_txq_num = priv->cfg->base_params->num_of_queues;
- priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM;
- priv->hw_params.scd_bc_tbls_size =
- priv->cfg->base_params->num_of_queues *
- sizeof(struct iwlagn_scd_bc_tbl);
- priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
- priv->hw_params.max_stations = IWLAGN_STATION_COUNT;
+ hw_params(priv).max_txq_num = priv->cfg->base_params->num_of_queues;
priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID;
- priv->hw_params.max_data_size = IWL60_RTC_DATA_SIZE;
- priv->hw_params.max_inst_size = IWL60_RTC_INST_SIZE;
+ hw_params(priv).max_data_size = IWL60_RTC_DATA_SIZE;
+ hw_params(priv).max_inst_size = IWL60_RTC_INST_SIZE;
- priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) |
- BIT(IEEE80211_BAND_5GHZ);
- priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
+ hw_params(priv).ht40_channel = BIT(IEEE80211_BAND_2GHZ);
- priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
+ hw_params(priv).tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
if (priv->cfg->rx_with_siso_diversity)
- priv->hw_params.rx_chains_num = 1;
+ hw_params(priv).rx_chains_num = 1;
else
- priv->hw_params.rx_chains_num =
+ hw_params(priv).rx_chains_num =
num_of_ant(priv->cfg->valid_rx_ant);
- priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
- priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
+ hw_params(priv).valid_tx_ant = priv->cfg->valid_tx_ant;
+ hw_params(priv).valid_rx_ant = priv->cfg->valid_rx_ant;
iwl2000_set_ct_threshold(priv);
/* Set initial sensitivity parameters */
/* Set initial calibration set */
- priv->hw_params.sens = &iwl2000_sensitivity;
- priv->hw_params.calib_init_cfg =
+ hw_params(priv).sens = &iwl2000_sensitivity;
+ hw_params(priv).calib_init_cfg =
BIT(IWL_CALIB_XTAL) |
BIT(IWL_CALIB_LO) |
BIT(IWL_CALIB_TX_IQ) |
BIT(IWL_CALIB_BASE_BAND);
if (priv->cfg->need_dc_calib)
- priv->hw_params.calib_rt_cfg |= BIT(IWL_CALIB_CFG_DC_IDX);
+ hw_params(priv).calib_rt_cfg |= IWL_CALIB_CFG_DC_IDX;
if (priv->cfg->need_temp_offset_calib)
- priv->hw_params.calib_init_cfg |= BIT(IWL_CALIB_TEMP_OFFSET);
-
- priv->hw_params.beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS;
+ hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_TEMP_OFFSET);
return 0;
}
static struct iwl_lib_ops iwl2000_lib = {
.set_hw_params = iwl2000_hw_set_hw_params,
- .rx_handler_setup = iwlagn_rx_handler_setup,
- .setup_deferred_work = iwlagn_bt_setup_deferred_work,
- .cancel_deferred_work = iwlagn_bt_cancel_deferred_work,
- .is_valid_rtc_data_addr = iwlagn_hw_valid_rtc_data_addr,
- .send_tx_power = iwlagn_send_tx_power,
- .update_chain_flags = iwl_update_chain_flags,
- .apm_ops = {
- .init = iwl_apm_init,
- .config = iwl2000_nic_config,
- },
+ .nic_config = iwl2000_nic_config,
.eeprom_ops = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -200,38 +171,30 @@ static struct iwl_lib_ops iwl2000_lib = {
EEPROM_6000_REG_BAND_24_HT40_CHANNELS,
EEPROM_REGULATORY_BAND_NO_HT40,
},
- .query_addr = iwlagn_eeprom_query_addr,
- .update_enhanced_txpower = iwlcore_eeprom_enhanced_txpower,
+ .update_enhanced_txpower = iwl_eeprom_enhanced_txpower,
},
- .temp_ops = {
- .temperature = iwlagn_temperature,
- },
- .txfifo_flush = iwlagn_txfifo_flush,
- .dev_txfifo_flush = iwlagn_dev_txfifo_flush,
-};
-
-static const struct iwl_ops iwl2000_ops = {
- .lib = &iwl2000_lib,
- .hcmd = &iwlagn_hcmd,
- .utils = &iwlagn_hcmd_utils,
-};
-
-static const struct iwl_ops iwl2030_ops = {
- .lib = &iwl2000_lib,
- .hcmd = &iwlagn_bt_hcmd,
- .utils = &iwlagn_hcmd_utils,
+ .temperature = iwlagn_temperature,
};
-static const struct iwl_ops iwl105_ops = {
- .lib = &iwl2000_lib,
- .hcmd = &iwlagn_hcmd,
- .utils = &iwlagn_hcmd_utils,
-};
-
-static const struct iwl_ops iwl135_ops = {
- .lib = &iwl2000_lib,
- .hcmd = &iwlagn_bt_hcmd,
- .utils = &iwlagn_hcmd_utils,
+static struct iwl_lib_ops iwl2030_lib = {
+ .set_hw_params = iwl2000_hw_set_hw_params,
+ .bt_rx_handler_setup = iwlagn_bt_rx_handler_setup,
+ .bt_setup_deferred_work = iwlagn_bt_setup_deferred_work,
+ .cancel_deferred_work = iwlagn_bt_cancel_deferred_work,
+ .nic_config = iwl2000_nic_config,
+ .eeprom_ops = {
+ .regulatory_bands = {
+ EEPROM_REG_BAND_1_CHANNELS,
+ EEPROM_REG_BAND_2_CHANNELS,
+ EEPROM_REG_BAND_3_CHANNELS,
+ EEPROM_REG_BAND_4_CHANNELS,
+ EEPROM_REG_BAND_5_CHANNELS,
+ EEPROM_6000_REG_BAND_24_HT40_CHANNELS,
+ EEPROM_REGULATORY_BAND_NO_HT40,
+ },
+ .update_enhanced_txpower = iwl_eeprom_enhanced_txpower,
+ },
+ .temperature = iwlagn_temperature,
};
static struct iwl_base_params iwl2000_base_params = {
@@ -242,7 +205,6 @@ static struct iwl_base_params iwl2000_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_2x00,
.shadow_ram_support = true,
.led_compensation = 51,
- .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
@@ -250,6 +212,7 @@ static struct iwl_base_params iwl2000_base_params = {
.wd_timeout = IWL_DEF_WD_TIMEOUT,
.max_event_log_size = 512,
.shadow_reg_enable = true,
+ .hd_v2 = true,
};
@@ -261,7 +224,6 @@ static struct iwl_base_params iwl2030_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_2x00,
.shadow_ram_support = true,
.led_compensation = 57,
- .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
@@ -269,6 +231,7 @@ static struct iwl_base_params iwl2030_base_params = {
.wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512,
.shadow_reg_enable = true,
+ .hd_v2 = true,
};
static struct iwl_ht_params iwl2000_ht_params = {
@@ -289,16 +252,17 @@ static struct iwl_bt_params iwl2030_bt_params = {
#define IWL_DEVICE_2000 \
.fw_name_pre = IWL2000_FW_PRE, \
.ucode_api_max = IWL2000_UCODE_API_MAX, \
+ .ucode_api_ok = IWL2000_UCODE_API_OK, \
.ucode_api_min = IWL2000_UCODE_API_MIN, \
.eeprom_ver = EEPROM_2000_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .ops = &iwl2000_ops, \
+ .lib = &iwl2000_lib, \
.base_params = &iwl2000_base_params, \
.need_dc_calib = true, \
.need_temp_offset_calib = true, \
+ .temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \
- .iq_invert = true, \
- .disable_otp_refresh = true \
+ .iq_invert = true \
struct iwl_cfg iwl2000_2bgn_cfg = {
.name = "2000 Series 2x2 BGN",
@@ -311,17 +275,25 @@ struct iwl_cfg iwl2000_2bg_cfg = {
IWL_DEVICE_2000,
};
+struct iwl_cfg iwl2000_2bgn_d_cfg = {
+ .name = "2000D Series 2x2 BGN",
+ IWL_DEVICE_2000,
+ .ht_params = &iwl2000_ht_params,
+};
+
#define IWL_DEVICE_2030 \
.fw_name_pre = IWL2030_FW_PRE, \
.ucode_api_max = IWL2030_UCODE_API_MAX, \
+ .ucode_api_ok = IWL2030_UCODE_API_OK, \
.ucode_api_min = IWL2030_UCODE_API_MIN, \
.eeprom_ver = EEPROM_2000_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .ops = &iwl2030_ops, \
+ .lib = &iwl2030_lib, \
.base_params = &iwl2030_base_params, \
.bt_params = &iwl2030_bt_params, \
.need_dc_calib = true, \
.need_temp_offset_calib = true, \
+ .temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \
.adv_pm = true, \
.iq_invert = true \
@@ -340,16 +312,19 @@ struct iwl_cfg iwl2030_2bg_cfg = {
#define IWL_DEVICE_105 \
.fw_name_pre = IWL105_FW_PRE, \
.ucode_api_max = IWL105_UCODE_API_MAX, \
+ .ucode_api_ok = IWL105_UCODE_API_OK, \
.ucode_api_min = IWL105_UCODE_API_MIN, \
.eeprom_ver = EEPROM_2000_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .ops = &iwl105_ops, \
+ .lib = &iwl2000_lib, \
.base_params = &iwl2000_base_params, \
.need_dc_calib = true, \
.need_temp_offset_calib = true, \
+ .temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \
.adv_pm = true, \
- .rx_with_siso_diversity = true \
+ .rx_with_siso_diversity = true, \
+ .iq_invert = true \
struct iwl_cfg iwl105_bg_cfg = {
.name = "105 Series 1x1 BG",
@@ -362,28 +337,37 @@ struct iwl_cfg iwl105_bgn_cfg = {
.ht_params = &iwl2000_ht_params,
};
+struct iwl_cfg iwl105_bgn_d_cfg = {
+ .name = "105D Series 1x1 BGN",
+ IWL_DEVICE_105,
+ .ht_params = &iwl2000_ht_params,
+};
+
#define IWL_DEVICE_135 \
- .fw_name_pre = IWL105_FW_PRE, \
- .ucode_api_max = IWL105_UCODE_API_MAX, \
- .ucode_api_min = IWL105_UCODE_API_MIN, \
+ .fw_name_pre = IWL135_FW_PRE, \
+ .ucode_api_max = IWL135_UCODE_API_MAX, \
+ .ucode_api_ok = IWL135_UCODE_API_OK, \
+ .ucode_api_min = IWL135_UCODE_API_MIN, \
.eeprom_ver = EEPROM_2000_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .ops = &iwl135_ops, \
+ .lib = &iwl2030_lib, \
.base_params = &iwl2030_base_params, \
.bt_params = &iwl2030_bt_params, \
.need_dc_calib = true, \
.need_temp_offset_calib = true, \
+ .temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \
.adv_pm = true, \
- .rx_with_siso_diversity = true \
+ .rx_with_siso_diversity = true, \
+ .iq_invert = true \
struct iwl_cfg iwl135_bg_cfg = {
- .name = "105 Series 1x1 BG/BT",
+ .name = "135 Series 1x1 BG/BT",
IWL_DEVICE_135,
};
struct iwl_cfg iwl135_bgn_cfg = {
- .name = "105 Series 1x1 BGN/BT",
+ .name = "135 Series 1x1 BGN/BT",
IWL_DEVICE_135,
.ht_params = &iwl2000_ht_params,
};
@@ -391,3 +375,4 @@ struct iwl_cfg iwl135_bgn_cfg = {
MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_MAX));
MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_MAX));
+MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_MAX));
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index e816c27db794..c511c98a89a8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -27,13 +27,10 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
@@ -43,11 +40,11 @@
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-io.h"
-#include "iwl-sta.h"
-#include "iwl-helpers.h"
#include "iwl-agn.h"
#include "iwl-agn-hw.h"
-#include "iwl-5000-hw.h"
+#include "iwl-trans.h"
+#include "iwl-shared.h"
+#include "iwl-cfg.h"
/* Highest firmware API version supported */
#define IWL5000_UCODE_API_MAX 5
@@ -67,43 +64,30 @@
static void iwl5000_nic_config(struct iwl_priv *priv)
{
unsigned long flags;
- u16 radio_cfg;
- spin_lock_irqsave(&priv->lock, flags);
+ iwl_rf_config(priv);
- radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
-
- /* write radio config values to register */
- if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) < EEPROM_RF_CONFIG_TYPE_MAX)
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
- EEPROM_RF_CFG_TYPE_MSK(radio_cfg) |
- EEPROM_RF_CFG_STEP_MSK(radio_cfg) |
- EEPROM_RF_CFG_DASH_MSK(radio_cfg));
-
- /* set CSR_HW_CONFIG_REG for uCode use */
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
- CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
+ spin_lock_irqsave(&priv->shrd->lock, flags);
/* W/A : NIC is stuck in a reset state after Early PCIe power off
* (PCIe power is lost before PERST# is asserted),
* causing ME FW to lose ownership and not being able to obtain it back.
*/
- iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
+ iwl_set_bits_mask_prph(bus(priv), APMG_PS_CTRL_REG,
APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
}
static struct iwl_sensitivity_ranges iwl5000_sensitivity = {
- .min_nrg_cck = 95,
+ .min_nrg_cck = 100,
.max_nrg_cck = 0, /* not used, set to 0 */
.auto_corr_min_ofdm = 90,
.auto_corr_min_ofdm_mrc = 170,
- .auto_corr_min_ofdm_x1 = 120,
- .auto_corr_min_ofdm_mrc_x1 = 240,
+ .auto_corr_min_ofdm_x1 = 105,
+ .auto_corr_min_ofdm_mrc_x1 = 220,
.auto_corr_max_ofdm = 120,
.auto_corr_max_ofdm_mrc = 210,
@@ -112,10 +96,10 @@ static struct iwl_sensitivity_ranges iwl5000_sensitivity = {
.auto_corr_min_cck = 125,
.auto_corr_max_cck = 200,
- .auto_corr_min_cck_mrc = 170,
+ .auto_corr_min_cck_mrc = 200,
.auto_corr_max_cck_mrc = 400,
- .nrg_th_cck = 95,
- .nrg_th_ofdm = 95,
+ .nrg_th_cck = 100,
+ .nrg_th_ofdm = 100,
.barker_corr_th_min = 190,
.barker_corr_th_min_mrc = 390,
@@ -148,19 +132,34 @@ static struct iwl_sensitivity_ranges iwl5150_sensitivity = {
.nrg_th_cca = 62,
};
+#define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5)
+
+static s32 iwl_temp_calib_to_offset(struct iwl_priv *priv)
+{
+ u16 temperature, voltage;
+ __le16 *temp_calib = (__le16 *)iwl_eeprom_query_addr(priv,
+ EEPROM_KELVIN_TEMPERATURE);
+
+ temperature = le16_to_cpu(temp_calib[0]);
+ voltage = le16_to_cpu(temp_calib[1]);
+
+ /* offset = temp - volt / coeff */
+ return (s32)(temperature - voltage / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF);
+}
+
static void iwl5150_set_ct_threshold(struct iwl_priv *priv)
{
const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF;
s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) -
iwl_temp_calib_to_offset(priv);
- priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef;
+ hw_params(priv).ct_kill_threshold = threshold * volt2temp_coef;
}
static void iwl5000_set_ct_threshold(struct iwl_priv *priv)
{
/* want Celsius */
- priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY;
+ hw_params(priv).ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY;
}
static int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
@@ -170,41 +169,32 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
priv->cfg->base_params->num_of_queues =
iwlagn_mod_params.num_of_queues;
- priv->hw_params.max_txq_num = priv->cfg->base_params->num_of_queues;
- priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM;
- priv->hw_params.scd_bc_tbls_size =
- priv->cfg->base_params->num_of_queues *
- sizeof(struct iwlagn_scd_bc_tbl);
- priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
- priv->hw_params.max_stations = IWLAGN_STATION_COUNT;
+ hw_params(priv).max_txq_num = priv->cfg->base_params->num_of_queues;
priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID;
- priv->hw_params.max_data_size = IWLAGN_RTC_DATA_SIZE;
- priv->hw_params.max_inst_size = IWLAGN_RTC_INST_SIZE;
+ hw_params(priv).max_data_size = IWLAGN_RTC_DATA_SIZE;
+ hw_params(priv).max_inst_size = IWLAGN_RTC_INST_SIZE;
- priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) |
+ hw_params(priv).ht40_channel = BIT(IEEE80211_BAND_2GHZ) |
BIT(IEEE80211_BAND_5GHZ);
- priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
- priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
- priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant);
- priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
- priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
+ hw_params(priv).tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
+ hw_params(priv).rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant);
+ hw_params(priv).valid_tx_ant = priv->cfg->valid_tx_ant;
+ hw_params(priv).valid_rx_ant = priv->cfg->valid_rx_ant;
iwl5000_set_ct_threshold(priv);
/* Set initial sensitivity parameters */
/* Set initial calibration set */
- priv->hw_params.sens = &iwl5000_sensitivity;
- priv->hw_params.calib_init_cfg =
+ hw_params(priv).sens = &iwl5000_sensitivity;
+ hw_params(priv).calib_init_cfg =
BIT(IWL_CALIB_XTAL) |
BIT(IWL_CALIB_LO) |
BIT(IWL_CALIB_TX_IQ) |
BIT(IWL_CALIB_TX_IQ_PERD) |
BIT(IWL_CALIB_BASE_BAND);
- priv->hw_params.beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS;
-
return 0;
}
@@ -215,40 +205,31 @@ static int iwl5150_hw_set_hw_params(struct iwl_priv *priv)
priv->cfg->base_params->num_of_queues =
iwlagn_mod_params.num_of_queues;
- priv->hw_params.max_txq_num = priv->cfg->base_params->num_of_queues;
- priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM;
- priv->hw_params.scd_bc_tbls_size =
- priv->cfg->base_params->num_of_queues *
- sizeof(struct iwlagn_scd_bc_tbl);
- priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
- priv->hw_params.max_stations = IWLAGN_STATION_COUNT;
+ hw_params(priv).max_txq_num = priv->cfg->base_params->num_of_queues;
priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID;
- priv->hw_params.max_data_size = IWLAGN_RTC_DATA_SIZE;
- priv->hw_params.max_inst_size = IWLAGN_RTC_INST_SIZE;
+ hw_params(priv).max_data_size = IWLAGN_RTC_DATA_SIZE;
+ hw_params(priv).max_inst_size = IWLAGN_RTC_INST_SIZE;
- priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) |
+ hw_params(priv).ht40_channel = BIT(IEEE80211_BAND_2GHZ) |
BIT(IEEE80211_BAND_5GHZ);
- priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
- priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
- priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant);
- priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
- priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
+ hw_params(priv).tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
+ hw_params(priv).rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant);
+ hw_params(priv).valid_tx_ant = priv->cfg->valid_tx_ant;
+ hw_params(priv).valid_rx_ant = priv->cfg->valid_rx_ant;
iwl5150_set_ct_threshold(priv);
/* Set initial sensitivity parameters */
/* Set initial calibration set */
- priv->hw_params.sens = &iwl5150_sensitivity;
- priv->hw_params.calib_init_cfg =
+ hw_params(priv).sens = &iwl5150_sensitivity;
+ hw_params(priv).calib_init_cfg =
BIT(IWL_CALIB_LO) |
BIT(IWL_CALIB_TX_IQ) |
BIT(IWL_CALIB_BASE_BAND);
if (priv->cfg->need_dc_calib)
- priv->hw_params.calib_init_cfg |= BIT(IWL_CALIB_DC);
-
- priv->hw_params.beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS;
+ hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_DC);
return 0;
}
@@ -270,7 +251,7 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
{
/*
* MULTI-FIXME
- * See iwl_mac_channel_switch.
+ * See iwlagn_mac_channel_switch.
*/
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
struct iwl5000_channel_switch_cmd cmd;
@@ -333,21 +314,13 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
return -EFAULT;
}
- return iwl_send_cmd_sync(priv, &hcmd);
+ return iwl_trans_send_cmd(trans(priv), &hcmd);
}
static struct iwl_lib_ops iwl5000_lib = {
.set_hw_params = iwl5000_hw_set_hw_params,
- .rx_handler_setup = iwlagn_rx_handler_setup,
- .setup_deferred_work = iwlagn_setup_deferred_work,
- .is_valid_rtc_data_addr = iwlagn_hw_valid_rtc_data_addr,
- .send_tx_power = iwlagn_send_tx_power,
- .update_chain_flags = iwl_update_chain_flags,
.set_channel_switch = iwl5000_hw_channel_switch,
- .apm_ops = {
- .init = iwl_apm_init,
- .config = iwl5000_nic_config,
- },
+ .nic_config = iwl5000_nic_config,
.eeprom_ops = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -358,27 +331,14 @@ static struct iwl_lib_ops iwl5000_lib = {
EEPROM_REG_BAND_24_HT40_CHANNELS,
EEPROM_REG_BAND_52_HT40_CHANNELS
},
- .query_addr = iwlagn_eeprom_query_addr,
},
- .temp_ops = {
- .temperature = iwlagn_temperature,
- },
- .txfifo_flush = iwlagn_txfifo_flush,
- .dev_txfifo_flush = iwlagn_dev_txfifo_flush,
+ .temperature = iwlagn_temperature,
};
static struct iwl_lib_ops iwl5150_lib = {
.set_hw_params = iwl5150_hw_set_hw_params,
- .rx_handler_setup = iwlagn_rx_handler_setup,
- .setup_deferred_work = iwlagn_setup_deferred_work,
- .is_valid_rtc_data_addr = iwlagn_hw_valid_rtc_data_addr,
- .send_tx_power = iwlagn_send_tx_power,
- .update_chain_flags = iwl_update_chain_flags,
.set_channel_switch = iwl5000_hw_channel_switch,
- .apm_ops = {
- .init = iwl_apm_init,
- .config = iwl5000_nic_config,
- },
+ .nic_config = iwl5000_nic_config,
.eeprom_ops = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -389,25 +349,8 @@ static struct iwl_lib_ops iwl5150_lib = {
EEPROM_REG_BAND_24_HT40_CHANNELS,
EEPROM_REG_BAND_52_HT40_CHANNELS
},
- .query_addr = iwlagn_eeprom_query_addr,
},
- .temp_ops = {
- .temperature = iwl5150_temperature,
- },
- .txfifo_flush = iwlagn_txfifo_flush,
- .dev_txfifo_flush = iwlagn_dev_txfifo_flush,
-};
-
-static const struct iwl_ops iwl5000_ops = {
- .lib = &iwl5000_lib,
- .hcmd = &iwlagn_hcmd,
- .utils = &iwlagn_hcmd_utils,
-};
-
-static const struct iwl_ops iwl5150_ops = {
- .lib = &iwl5150_lib,
- .hcmd = &iwlagn_hcmd,
- .utils = &iwlagn_hcmd_utils,
+ .temperature = iwl5150_temperature,
};
static struct iwl_base_params iwl5000_base_params = {
@@ -416,11 +359,11 @@ static struct iwl_base_params iwl5000_base_params = {
.num_of_ampdu_queues = IWLAGN_NUM_AMPDU_QUEUES,
.pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
.led_compensation = 51,
- .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512,
+ .no_idle_support = true,
};
static struct iwl_ht_params iwl5000_ht_params = {
.ht_greenfield_support = true,
@@ -432,7 +375,7 @@ static struct iwl_ht_params iwl5000_ht_params = {
.ucode_api_min = IWL5000_UCODE_API_MIN, \
.eeprom_ver = EEPROM_5000_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, \
- .ops = &iwl5000_ops, \
+ .lib = &iwl5000_lib, \
.base_params = &iwl5000_base_params, \
.led_mode = IWL_LED_BLINK
@@ -475,7 +418,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
.ucode_api_min = IWL5000_UCODE_API_MIN,
.eeprom_ver = EEPROM_5050_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION,
- .ops = &iwl5000_ops,
+ .lib = &iwl5000_lib,
.base_params = &iwl5000_base_params,
.ht_params = &iwl5000_ht_params,
.led_mode = IWL_LED_BLINK,
@@ -488,7 +431,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
.ucode_api_min = IWL5150_UCODE_API_MIN, \
.eeprom_ver = EEPROM_5050_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, \
- .ops = &iwl5150_ops, \
+ .lib = &iwl5150_lib, \
.base_params = &iwl5000_base_params, \
.need_dc_calib = true, \
.led_mode = IWL_LED_BLINK, \
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index 5b150bc70b06..c840c78278db 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -27,12 +27,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
@@ -42,16 +39,19 @@
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-io.h"
-#include "iwl-sta.h"
#include "iwl-agn.h"
-#include "iwl-helpers.h"
#include "iwl-agn-hw.h"
-#include "iwl-6000-hw.h"
+#include "iwl-trans.h"
+#include "iwl-shared.h"
+#include "iwl-cfg.h"
/* Highest firmware API version supported */
#define IWL6000_UCODE_API_MAX 4
#define IWL6050_UCODE_API_MAX 5
-#define IWL6000G2_UCODE_API_MAX 5
+#define IWL6000G2_UCODE_API_MAX 6
+
+/* Oldest version we won't warn about */
+#define IWL6000G2_UCODE_API_OK 5
/* Lowest firmware API version supported */
#define IWL6000_UCODE_API_MIN 4
@@ -73,15 +73,15 @@
static void iwl6000_set_ct_threshold(struct iwl_priv *priv)
{
/* want Celsius */
- priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD;
- priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
+ hw_params(priv).ct_kill_threshold = CT_KILL_THRESHOLD;
+ hw_params(priv).ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
}
static void iwl6050_additional_nic_config(struct iwl_priv *priv)
{
/* Indicate calibration version to uCode. */
if (iwlagn_eeprom_calib_version(priv) >= 6)
- iwl_set_bit(priv, CSR_GP_DRIVER_REG,
+ iwl_set_bit(bus(priv), CSR_GP_DRIVER_REG,
CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6);
}
@@ -89,46 +89,30 @@ static void iwl6150_additional_nic_config(struct iwl_priv *priv)
{
/* Indicate calibration version to uCode. */
if (iwlagn_eeprom_calib_version(priv) >= 6)
- iwl_set_bit(priv, CSR_GP_DRIVER_REG,
+ iwl_set_bit(bus(priv), CSR_GP_DRIVER_REG,
CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6);
- iwl_set_bit(priv, CSR_GP_DRIVER_REG,
+ iwl_set_bit(bus(priv), CSR_GP_DRIVER_REG,
CSR_GP_DRIVER_REG_BIT_6050_1x2);
}
/* NIC configuration for 6000 series */
static void iwl6000_nic_config(struct iwl_priv *priv)
{
- u16 radio_cfg;
-
- radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
-
- /* write radio config values to register */
- if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX)
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
- EEPROM_RF_CFG_TYPE_MSK(radio_cfg) |
- EEPROM_RF_CFG_STEP_MSK(radio_cfg) |
- EEPROM_RF_CFG_DASH_MSK(radio_cfg));
-
- /* set CSR_HW_CONFIG_REG for uCode use */
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
- CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
+ iwl_rf_config(priv);
/* no locking required for register write */
if (priv->cfg->pa_type == IWL_PA_INTERNAL) {
/* 2x2 IPA phy type */
- iwl_write32(priv, CSR_GP_DRIVER_REG,
+ iwl_write32(bus(priv), CSR_GP_DRIVER_REG,
CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA);
}
/* do additional nic configuration if needed */
- if (priv->cfg->ops->nic &&
- priv->cfg->ops->nic->additional_nic_config) {
- priv->cfg->ops->nic->additional_nic_config(priv);
- }
+ if (priv->cfg->additional_nic_config)
+ priv->cfg->additional_nic_config(priv);
}
static struct iwl_sensitivity_ranges iwl6000_sensitivity = {
- .min_nrg_cck = 97,
+ .min_nrg_cck = 110,
.max_nrg_cck = 0, /* not used, set to 0 */
.auto_corr_min_ofdm = 80,
.auto_corr_min_ofdm_mrc = 128,
@@ -144,11 +128,11 @@ static struct iwl_sensitivity_ranges iwl6000_sensitivity = {
.auto_corr_max_cck = 175,
.auto_corr_min_cck_mrc = 160,
.auto_corr_max_cck_mrc = 310,
- .nrg_th_cck = 97,
- .nrg_th_ofdm = 100,
+ .nrg_th_cck = 110,
+ .nrg_th_ofdm = 110,
.barker_corr_th_min = 190,
- .barker_corr_th_min_mrc = 390,
+ .barker_corr_th_min_mrc = 336,
.nrg_th_cca = 62,
};
@@ -159,47 +143,38 @@ static int iwl6000_hw_set_hw_params(struct iwl_priv *priv)
priv->cfg->base_params->num_of_queues =
iwlagn_mod_params.num_of_queues;
- priv->hw_params.max_txq_num = priv->cfg->base_params->num_of_queues;
- priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM;
- priv->hw_params.scd_bc_tbls_size =
- priv->cfg->base_params->num_of_queues *
- sizeof(struct iwlagn_scd_bc_tbl);
- priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
- priv->hw_params.max_stations = IWLAGN_STATION_COUNT;
+ hw_params(priv).max_txq_num = priv->cfg->base_params->num_of_queues;
priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID;
- priv->hw_params.max_data_size = IWL60_RTC_DATA_SIZE;
- priv->hw_params.max_inst_size = IWL60_RTC_INST_SIZE;
+ hw_params(priv).max_data_size = IWL60_RTC_DATA_SIZE;
+ hw_params(priv).max_inst_size = IWL60_RTC_INST_SIZE;
- priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) |
+ hw_params(priv).ht40_channel = BIT(IEEE80211_BAND_2GHZ) |
BIT(IEEE80211_BAND_5GHZ);
- priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
- priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
+ hw_params(priv).tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
if (priv->cfg->rx_with_siso_diversity)
- priv->hw_params.rx_chains_num = 1;
+ hw_params(priv).rx_chains_num = 1;
else
- priv->hw_params.rx_chains_num =
+ hw_params(priv).rx_chains_num =
num_of_ant(priv->cfg->valid_rx_ant);
- priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
- priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
+ hw_params(priv).valid_tx_ant = priv->cfg->valid_tx_ant;
+ hw_params(priv).valid_rx_ant = priv->cfg->valid_rx_ant;
iwl6000_set_ct_threshold(priv);
/* Set initial sensitivity parameters */
/* Set initial calibration set */
- priv->hw_params.sens = &iwl6000_sensitivity;
- priv->hw_params.calib_init_cfg =
+ hw_params(priv).sens = &iwl6000_sensitivity;
+ hw_params(priv).calib_init_cfg =
BIT(IWL_CALIB_XTAL) |
BIT(IWL_CALIB_LO) |
BIT(IWL_CALIB_TX_IQ) |
BIT(IWL_CALIB_BASE_BAND);
if (priv->cfg->need_dc_calib)
- priv->hw_params.calib_rt_cfg |= BIT(IWL_CALIB_CFG_DC_IDX);
+ hw_params(priv).calib_rt_cfg |= IWL_CALIB_CFG_DC_IDX;
if (priv->cfg->need_temp_offset_calib)
- priv->hw_params.calib_init_cfg |= BIT(IWL_CALIB_TEMP_OFFSET);
-
- priv->hw_params.beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS;
+ hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_TEMP_OFFSET);
return 0;
}
@@ -209,7 +184,7 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
{
/*
* MULTI-FIXME
- * See iwl_mac_channel_switch.
+ * See iwlagn_mac_channel_switch.
*/
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
struct iwl6000_channel_switch_cmd cmd;
@@ -272,21 +247,13 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
return -EFAULT;
}
- return iwl_send_cmd_sync(priv, &hcmd);
+ return iwl_trans_send_cmd(trans(priv), &hcmd);
}
static struct iwl_lib_ops iwl6000_lib = {
.set_hw_params = iwl6000_hw_set_hw_params,
- .rx_handler_setup = iwlagn_rx_handler_setup,
- .setup_deferred_work = iwlagn_setup_deferred_work,
- .is_valid_rtc_data_addr = iwlagn_hw_valid_rtc_data_addr,
- .send_tx_power = iwlagn_send_tx_power,
- .update_chain_flags = iwl_update_chain_flags,
.set_channel_switch = iwl6000_hw_channel_switch,
- .apm_ops = {
- .init = iwl_apm_init,
- .config = iwl6000_nic_config,
- },
+ .nic_config = iwl6000_nic_config,
.eeprom_ops = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -297,29 +264,18 @@ static struct iwl_lib_ops iwl6000_lib = {
EEPROM_6000_REG_BAND_24_HT40_CHANNELS,
EEPROM_REG_BAND_52_HT40_CHANNELS
},
- .query_addr = iwlagn_eeprom_query_addr,
- .update_enhanced_txpower = iwlcore_eeprom_enhanced_txpower,
+ .update_enhanced_txpower = iwl_eeprom_enhanced_txpower,
},
- .temp_ops = {
- .temperature = iwlagn_temperature,
- },
- .txfifo_flush = iwlagn_txfifo_flush,
- .dev_txfifo_flush = iwlagn_dev_txfifo_flush,
+ .temperature = iwlagn_temperature,
};
static struct iwl_lib_ops iwl6030_lib = {
.set_hw_params = iwl6000_hw_set_hw_params,
- .rx_handler_setup = iwlagn_bt_rx_handler_setup,
- .setup_deferred_work = iwlagn_bt_setup_deferred_work,
+ .bt_rx_handler_setup = iwlagn_bt_rx_handler_setup,
+ .bt_setup_deferred_work = iwlagn_bt_setup_deferred_work,
.cancel_deferred_work = iwlagn_bt_cancel_deferred_work,
- .is_valid_rtc_data_addr = iwlagn_hw_valid_rtc_data_addr,
- .send_tx_power = iwlagn_send_tx_power,
- .update_chain_flags = iwl_update_chain_flags,
.set_channel_switch = iwl6000_hw_channel_switch,
- .apm_ops = {
- .init = iwl_apm_init,
- .config = iwl6000_nic_config,
- },
+ .nic_config = iwl6000_nic_config,
.eeprom_ops = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -330,48 +286,9 @@ static struct iwl_lib_ops iwl6030_lib = {
EEPROM_6000_REG_BAND_24_HT40_CHANNELS,
EEPROM_REG_BAND_52_HT40_CHANNELS
},
- .query_addr = iwlagn_eeprom_query_addr,
- .update_enhanced_txpower = iwlcore_eeprom_enhanced_txpower,
+ .update_enhanced_txpower = iwl_eeprom_enhanced_txpower,
},
- .temp_ops = {
- .temperature = iwlagn_temperature,
- },
- .txfifo_flush = iwlagn_txfifo_flush,
- .dev_txfifo_flush = iwlagn_dev_txfifo_flush,
-};
-
-static struct iwl_nic_ops iwl6050_nic_ops = {
- .additional_nic_config = &iwl6050_additional_nic_config,
-};
-
-static struct iwl_nic_ops iwl6150_nic_ops = {
- .additional_nic_config = &iwl6150_additional_nic_config,
-};
-
-static const struct iwl_ops iwl6000_ops = {
- .lib = &iwl6000_lib,
- .hcmd = &iwlagn_hcmd,
- .utils = &iwlagn_hcmd_utils,
-};
-
-static const struct iwl_ops iwl6050_ops = {
- .lib = &iwl6000_lib,
- .hcmd = &iwlagn_hcmd,
- .utils = &iwlagn_hcmd_utils,
- .nic = &iwl6050_nic_ops,
-};
-
-static const struct iwl_ops iwl6150_ops = {
- .lib = &iwl6000_lib,
- .hcmd = &iwlagn_hcmd,
- .utils = &iwlagn_hcmd_utils,
- .nic = &iwl6150_nic_ops,
-};
-
-static const struct iwl_ops iwl6030_ops = {
- .lib = &iwl6030_lib,
- .hcmd = &iwlagn_bt_hcmd,
- .utils = &iwlagn_hcmd_utils,
+ .temperature = iwlagn_temperature,
};
static struct iwl_base_params iwl6000_base_params = {
@@ -382,7 +299,6 @@ static struct iwl_base_params iwl6000_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true,
.led_compensation = 51,
- .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
@@ -400,7 +316,6 @@ static struct iwl_base_params iwl6050_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_6x50,
.shadow_ram_support = true,
.led_compensation = 51,
- .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
@@ -417,7 +332,6 @@ static struct iwl_base_params iwl6000_g2_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true,
.led_compensation = 57,
- .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
@@ -442,12 +356,13 @@ static struct iwl_bt_params iwl6000_bt_params = {
};
#define IWL_DEVICE_6005 \
- .fw_name_pre = IWL6005_FW_PRE, \
+ .fw_name_pre = IWL6005_FW_PRE, \
.ucode_api_max = IWL6000G2_UCODE_API_MAX, \
+ .ucode_api_ok = IWL6000G2_UCODE_API_OK, \
.ucode_api_min = IWL6000G2_UCODE_API_MIN, \
.eeprom_ver = EEPROM_6005_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_6005_TX_POWER_VERSION, \
- .ops = &iwl6000_ops, \
+ .lib = &iwl6000_lib, \
.base_params = &iwl6000_g2_base_params, \
.need_dc_calib = true, \
.need_temp_offset_calib = true, \
@@ -469,13 +384,26 @@ struct iwl_cfg iwl6005_2bg_cfg = {
IWL_DEVICE_6005,
};
+struct iwl_cfg iwl6005_2agn_sff_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6205S AGN",
+ IWL_DEVICE_6005,
+ .ht_params = &iwl6000_ht_params,
+};
+
+struct iwl_cfg iwl6005_2agn_d_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6205D AGN",
+ IWL_DEVICE_6005,
+ .ht_params = &iwl6000_ht_params,
+};
+
#define IWL_DEVICE_6030 \
- .fw_name_pre = IWL6030_FW_PRE, \
+ .fw_name_pre = IWL6030_FW_PRE, \
.ucode_api_max = IWL6000G2_UCODE_API_MAX, \
+ .ucode_api_ok = IWL6000G2_UCODE_API_OK, \
.ucode_api_min = IWL6000G2_UCODE_API_MIN, \
.eeprom_ver = EEPROM_6030_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
- .ops = &iwl6030_ops, \
+ .lib = &iwl6030_lib, \
.base_params = &iwl6000_g2_base_params, \
.bt_params = &iwl6000_bt_params, \
.need_dc_calib = true, \
@@ -556,7 +484,7 @@ struct iwl_cfg iwl130_bg_cfg = {
.valid_rx_ant = ANT_BC, /* .cfg overwrite */ \
.eeprom_ver = EEPROM_6000_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION, \
- .ops = &iwl6000_ops, \
+ .lib = &iwl6000_lib, \
.base_params = &iwl6000_base_params, \
.pa_type = IWL_PA_INTERNAL, \
.led_mode = IWL_LED_BLINK
@@ -583,7 +511,8 @@ struct iwl_cfg iwl6000i_2bg_cfg = {
.ucode_api_min = IWL6050_UCODE_API_MIN, \
.valid_tx_ant = ANT_AB, /* .cfg overwrite */ \
.valid_rx_ant = ANT_AB, /* .cfg overwrite */ \
- .ops = &iwl6050_ops, \
+ .lib = &iwl6000_lib, \
+ .additional_nic_config = iwl6050_additional_nic_config, \
.eeprom_ver = EEPROM_6050_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_6050_TX_POWER_VERSION, \
.base_params = &iwl6050_base_params, \
@@ -606,7 +535,8 @@ struct iwl_cfg iwl6050_2abg_cfg = {
.fw_name_pre = IWL6050_FW_PRE, \
.ucode_api_max = IWL6050_UCODE_API_MAX, \
.ucode_api_min = IWL6050_UCODE_API_MIN, \
- .ops = &iwl6150_ops, \
+ .lib = &iwl6000_lib, \
+ .additional_nic_config = iwl6150_additional_nic_config, \
.eeprom_ver = EEPROM_6150_EEPROM_VERSION, \
.eeprom_calib_ver = EEPROM_6150_TX_POWER_VERSION, \
.base_params = &iwl6050_base_params, \
@@ -632,7 +562,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
.ucode_api_min = IWL6000_UCODE_API_MIN,
.eeprom_ver = EEPROM_6000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION,
- .ops = &iwl6000_ops,
+ .lib = &iwl6000_lib,
.base_params = &iwl6000_base_params,
.ht_params = &iwl6000_ht_params,
.need_dc_calib = true,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-calib.c b/drivers/net/wireless/iwlwifi/iwl-agn-calib.c
index c9255def1080..03bac48558b2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-calib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-calib.c
@@ -66,6 +66,8 @@
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-agn-calib.h"
+#include "iwl-trans.h"
+#include "iwl-agn.h"
/*****************************************************************************
* INIT calibrations framework
@@ -87,15 +89,16 @@ int iwl_send_calib_results(struct iwl_priv *priv)
struct iwl_host_cmd hcmd = {
.id = REPLY_PHY_CALIBRATION_CMD,
+ .flags = CMD_SYNC,
};
for (i = 0; i < IWL_CALIB_MAX; i++) {
- if ((BIT(i) & priv->hw_params.calib_init_cfg) &&
+ if ((BIT(i) & hw_params(priv).calib_init_cfg) &&
priv->calib_results[i].buf) {
hcmd.len[0] = priv->calib_results[i].buf_len;
hcmd.data[0] = priv->calib_results[i].buf;
hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
- ret = iwl_send_cmd_sync(priv, &hcmd);
+ ret = iwl_trans_send_cmd(trans(priv), &hcmd);
if (ret) {
IWL_ERR(priv, "Error %d iteration %d\n",
ret, i);
@@ -171,7 +174,7 @@ static int iwl_sens_energy_cck(struct iwl_priv *priv,
u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
struct iwl_sensitivity_data *data = NULL;
- const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
+ const struct iwl_sensitivity_ranges *ranges = hw_params(priv).sens;
data = &(priv->sensitivity_data);
@@ -354,7 +357,7 @@ static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv,
u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
struct iwl_sensitivity_data *data = NULL;
- const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
+ const struct iwl_sensitivity_ranges *ranges = hw_params(priv).sens;
data = &(priv->sensitivity_data);
@@ -481,7 +484,7 @@ static int iwl_sensitivity_write(struct iwl_priv *priv)
memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
sizeof(u16)*HD_TABLE_SIZE);
- return iwl_send_cmd(priv, &cmd_out);
+ return iwl_trans_send_cmd(trans(priv), &cmd_out);
}
/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
@@ -502,28 +505,53 @@ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
- cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
- HD_INA_NON_SQUARE_DET_OFDM_DATA;
- cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
- HD_INA_NON_SQUARE_DET_CCK_DATA;
- cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
- HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
- HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
- HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
- HD_OFDM_NON_SQUARE_DET_SLOPE_DATA;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
- HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
- HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
- HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
- HD_CCK_NON_SQUARE_DET_SLOPE_DATA;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
- HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA;
+ if (priv->cfg->base_params->hd_v2) {
+ cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
+ HD_INA_NON_SQUARE_DET_OFDM_DATA_V2;
+ cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
+ HD_INA_NON_SQUARE_DET_CCK_DATA_V2;
+ cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
+ HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
+ HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
+ HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
+ HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
+ HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2;
+ } else {
+ cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
+ HD_INA_NON_SQUARE_DET_OFDM_DATA_V1;
+ cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
+ HD_INA_NON_SQUARE_DET_CCK_DATA_V1;
+ cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
+ HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
+ HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
+ HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
+ HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
+ HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1;
+ }
/* Update uCode's "work" table, and copy it to DSP */
cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
@@ -545,7 +573,7 @@ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
&(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]),
sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES);
- return iwl_send_cmd(priv, &cmd_out);
+ return iwl_trans_send_cmd(trans(priv), &cmd_out);
}
void iwl_init_sensitivity(struct iwl_priv *priv)
@@ -553,7 +581,7 @@ void iwl_init_sensitivity(struct iwl_priv *priv)
int ret = 0;
int i;
struct iwl_sensitivity_data *data = NULL;
- const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
+ const struct iwl_sensitivity_ranges *ranges = hw_params(priv).sens;
if (priv->disable_sens_cal)
return;
@@ -630,13 +658,13 @@ void iwl_sensitivity_calibration(struct iwl_priv *priv)
return;
}
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&priv->shrd->lock, flags);
rx_info = &priv->statistics.rx_non_phy;
ofdm = &priv->statistics.rx_ofdm;
cck = &priv->statistics.rx_cck;
if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
IWL_DEBUG_CALIB(priv, "<< invalid data.\n");
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
return;
}
@@ -660,7 +688,7 @@ void iwl_sensitivity_calibration(struct iwl_priv *priv)
statis.beacon_energy_c =
le32_to_cpu(rx_info->beacon_energy_c);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time);
@@ -738,12 +766,9 @@ static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig,
u8 first_chain;
u16 i = 0;
- average_sig[0] = data->chain_signal_a /
- priv->cfg->base_params->chain_noise_num_beacons;
- average_sig[1] = data->chain_signal_b /
- priv->cfg->base_params->chain_noise_num_beacons;
- average_sig[2] = data->chain_signal_c /
- priv->cfg->base_params->chain_noise_num_beacons;
+ average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS;
+ average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS;
+ average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS;
if (average_sig[0] >= average_sig[1]) {
max_average_sig = average_sig[0];
@@ -793,21 +818,21 @@ static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig,
* To be safe, simply mask out any chains that we know
* are not on the device.
*/
- active_chains &= priv->hw_params.valid_rx_ant;
+ active_chains &= hw_params(priv).valid_rx_ant;
num_tx_chains = 0;
for (i = 0; i < NUM_RX_CHAINS; i++) {
/* loops on all the bits of
* priv->hw_setting.valid_tx_ant */
u8 ant_msk = (1 << i);
- if (!(priv->hw_params.valid_tx_ant & ant_msk))
+ if (!(hw_params(priv).valid_tx_ant & ant_msk))
continue;
num_tx_chains++;
if (data->disconn_array[i] == 0)
/* there is a Tx antenna connected */
break;
- if (num_tx_chains == priv->hw_params.tx_chains_num &&
+ if (num_tx_chains == hw_params(priv).tx_chains_num &&
data->disconn_array[i]) {
/*
* If all chains are disconnected
@@ -824,12 +849,13 @@ static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig,
}
}
- if (active_chains != priv->hw_params.valid_rx_ant &&
+ if (active_chains != hw_params(priv).valid_rx_ant &&
active_chains != priv->chain_noise_data.active_chains)
IWL_DEBUG_CALIB(priv,
"Detected that not all antennas are connected! "
"Connected: %#x, valid: %#x.\n",
- active_chains, priv->hw_params.valid_rx_ant);
+ active_chains,
+ hw_params(priv).valid_rx_ant);
/* Save for use within RXON, TX, SCAN commands, etc. */
data->active_chains = active_chains;
@@ -837,6 +863,65 @@ static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig,
active_chains);
}
+static void iwlagn_gain_computation(struct iwl_priv *priv,
+ u32 average_noise[NUM_RX_CHAINS],
+ u16 min_average_noise_antenna_i,
+ u32 min_average_noise,
+ u8 default_chain)
+{
+ int i;
+ s32 delta_g;
+ struct iwl_chain_noise_data *data = &priv->chain_noise_data;
+
+ /*
+ * Find Gain Code for the chains based on "default chain"
+ */
+ for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) {
+ if ((data->disconn_array[i])) {
+ data->delta_gain_code[i] = 0;
+ continue;
+ }
+
+ delta_g = (priv->cfg->base_params->chain_noise_scale *
+ ((s32)average_noise[default_chain] -
+ (s32)average_noise[i])) / 1500;
+
+ /* bound gain by 2 bits value max, 3rd bit is sign */
+ data->delta_gain_code[i] =
+ min(abs(delta_g),
+ (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
+
+ if (delta_g < 0)
+ /*
+ * set negative sign ...
+ * note to Intel developers: This is uCode API format,
+ * not the format of any internal device registers.
+ * Do not change this format for e.g. 6050 or similar
+ * devices. Change format only if more resolution
+ * (i.e. more than 2 bits magnitude) is needed.
+ */
+ data->delta_gain_code[i] |= (1 << 2);
+ }
+
+ IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d ANT_C = %d\n",
+ data->delta_gain_code[1], data->delta_gain_code[2]);
+
+ if (!data->radio_write) {
+ struct iwl_calib_chain_noise_gain_cmd cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ iwl_set_calib_hdr(&cmd.hdr,
+ priv->phy_calib_chain_noise_gain_cmd);
+ cmd.delta_gain_1 = data->delta_gain_code[1];
+ cmd.delta_gain_2 = data->delta_gain_code[2];
+ iwl_trans_send_cmd_pdu(trans(priv), REPLY_PHY_CALIBRATION_CMD,
+ CMD_ASYNC, sizeof(cmd), &cmd);
+
+ data->radio_write = 1;
+ data->state = IWL_CHAIN_NOISE_CALIBRATED;
+ }
+}
/*
* Accumulate 16 beacons of signal and noise statistics for each of
@@ -888,13 +973,13 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
return;
}
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&priv->shrd->lock, flags);
rx_info = &priv->statistics.rx_non_phy;
if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n");
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
return;
}
@@ -909,7 +994,7 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) {
IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n",
rxon_chnum, rxon_band24);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
return;
}
@@ -928,7 +1013,7 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
data->beacon_count++;
@@ -950,8 +1035,7 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
/* If this is the "chain_noise_num_beacons", determine:
* 1) Disconnected antennas (using signal strengths)
* 2) Differential gain (using silence noise) to balance receivers */
- if (data->beacon_count !=
- priv->cfg->base_params->chain_noise_num_beacons)
+ if (data->beacon_count != IWL_CAL_NUM_BEACONS)
return;
/* Analyze signal for disconnected antenna */
@@ -959,7 +1043,7 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
priv->cfg->bt_params->advanced_bt_coexist) {
/* Disable disconnected antenna algorithm for advanced
bt coex, assuming valid antennas are connected */
- data->active_chains = priv->hw_params.valid_rx_ant;
+ data->active_chains = hw_params(priv).valid_rx_ant;
for (i = 0; i < NUM_RX_CHAINS; i++)
if (!(data->active_chains & (1<<i)))
data->disconn_array[i] = 1;
@@ -967,12 +1051,9 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
iwl_find_disconn_antenna(priv, average_sig, data);
/* Analyze noise for rx balance */
- average_noise[0] = data->chain_noise_a /
- priv->cfg->base_params->chain_noise_num_beacons;
- average_noise[1] = data->chain_noise_b /
- priv->cfg->base_params->chain_noise_num_beacons;
- average_noise[2] = data->chain_noise_c /
- priv->cfg->base_params->chain_noise_num_beacons;
+ average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS;
+ average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS;
+ average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS;
for (i = 0; i < NUM_RX_CHAINS; i++) {
if (!(data->disconn_array[i]) &&
@@ -991,16 +1072,14 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n",
min_average_noise, min_average_noise_antenna_i);
- if (priv->cfg->ops->utils->gain_computation)
- priv->cfg->ops->utils->gain_computation(priv, average_noise,
+ iwlagn_gain_computation(priv, average_noise,
min_average_noise_antenna_i, min_average_noise,
find_first_chain(priv->cfg->valid_rx_ant));
/* Some power changes may have been made during the calibration.
* Update and commit the RXON
*/
- if (priv->cfg->ops->lib->update_chain_flags)
- priv->cfg->ops->lib->update_chain_flags(priv);
+ iwl_update_chain_flags(priv);
data->state = IWL_CHAIN_NOISE_DONE;
iwl_power_update_mode(priv, false);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-calib.h b/drivers/net/wireless/iwlwifi/iwl-agn-calib.h
index 4ef4dd934254..a869fc9205d2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-calib.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-calib.h
@@ -71,13 +71,6 @@ void iwl_sensitivity_calibration(struct iwl_priv *priv);
void iwl_init_sensitivity(struct iwl_priv *priv);
void iwl_reset_run_time_calib(struct iwl_priv *priv);
-static inline void iwl_chain_noise_reset(struct iwl_priv *priv)
-{
-
- if (!priv->disable_chain_noise_cal &&
- priv->cfg->ops->utils->chain_noise_reset)
- priv->cfg->ops->utils->chain_noise_reset(priv);
-}
int iwl_send_calib_results(struct iwl_priv *priv);
int iwl_calib_set(struct iwl_calib_result *res, const u8 *buf, int len);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c
deleted file mode 100644
index 2ef9448b1c20..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-
-#include <net/mac80211.h>
-
-#include "iwl-commands.h"
-#include "iwl-dev.h"
-#include "iwl-core.h"
-#include "iwl-debug.h"
-#include "iwl-agn.h"
-#include "iwl-io.h"
-
-/******************************************************************************
- *
- * EEPROM related functions
- *
-******************************************************************************/
-
-int iwl_eeprom_check_version(struct iwl_priv *priv)
-{
- u16 eeprom_ver;
- u16 calib_ver;
-
- eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
- calib_ver = iwlagn_eeprom_calib_version(priv);
-
- if (eeprom_ver < priv->cfg->eeprom_ver ||
- calib_ver < priv->cfg->eeprom_calib_ver)
- goto err;
-
- IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n",
- eeprom_ver, calib_ver);
-
- return 0;
-err:
- IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x "
- "CALIB=0x%x < 0x%x\n",
- eeprom_ver, priv->cfg->eeprom_ver,
- calib_ver, priv->cfg->eeprom_calib_ver);
- return -EINVAL;
-
-}
-
-int iwl_eeprom_check_sku(struct iwl_priv *priv)
-{
- u16 eeprom_sku;
- u16 radio_cfg;
-
- eeprom_sku = iwl_eeprom_query16(priv, EEPROM_SKU_CAP);
-
- if (!priv->cfg->sku) {
- /* not using sku overwrite */
- priv->cfg->sku =
- ((eeprom_sku & EEPROM_SKU_CAP_BAND_SELECTION) >>
- EEPROM_SKU_CAP_BAND_POS);
- if (eeprom_sku & EEPROM_SKU_CAP_11N_ENABLE)
- priv->cfg->sku |= IWL_SKU_N;
- }
- if (!priv->cfg->sku) {
- IWL_ERR(priv, "Invalid device sku\n");
- return -EINVAL;
- }
-
- IWL_INFO(priv, "Device SKU: 0X%x\n", priv->cfg->sku);
-
- if (!priv->cfg->valid_tx_ant && !priv->cfg->valid_rx_ant) {
- /* not using .cfg overwrite */
- radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
- priv->cfg->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg);
- priv->cfg->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg);
- if (!priv->cfg->valid_tx_ant || !priv->cfg->valid_rx_ant) {
- IWL_ERR(priv, "Invalid chain (0X%x, 0X%x)\n",
- priv->cfg->valid_tx_ant,
- priv->cfg->valid_rx_ant);
- return -EINVAL;
- }
- IWL_INFO(priv, "Valid Tx ant: 0X%x, Valid Rx ant: 0X%x\n",
- priv->cfg->valid_tx_ant, priv->cfg->valid_rx_ant);
- }
- /*
- * for some special cases,
- * EEPROM did not reflect the correct antenna setting
- * so overwrite the valid tx/rx antenna from .cfg
- */
- return 0;
-}
-
-void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac)
-{
- const u8 *addr = priv->cfg->ops->lib->eeprom_ops.query_addr(priv,
- EEPROM_MAC_ADDRESS);
- memcpy(mac, addr, ETH_ALEN);
-}
-
-/**
- * iwl_get_max_txpower_avg - get the highest tx power from all chains.
- * find the highest tx power from all chains for the channel
- */
-static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
- struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
- int element, s8 *max_txpower_in_half_dbm)
-{
- s8 max_txpower_avg = 0; /* (dBm) */
-
- /* Take the highest tx power from any valid chains */
- if ((priv->cfg->valid_tx_ant & ANT_A) &&
- (enhanced_txpower[element].chain_a_max > max_txpower_avg))
- max_txpower_avg = enhanced_txpower[element].chain_a_max;
- if ((priv->cfg->valid_tx_ant & ANT_B) &&
- (enhanced_txpower[element].chain_b_max > max_txpower_avg))
- max_txpower_avg = enhanced_txpower[element].chain_b_max;
- if ((priv->cfg->valid_tx_ant & ANT_C) &&
- (enhanced_txpower[element].chain_c_max > max_txpower_avg))
- max_txpower_avg = enhanced_txpower[element].chain_c_max;
- if (((priv->cfg->valid_tx_ant == ANT_AB) |
- (priv->cfg->valid_tx_ant == ANT_BC) |
- (priv->cfg->valid_tx_ant == ANT_AC)) &&
- (enhanced_txpower[element].mimo2_max > max_txpower_avg))
- max_txpower_avg = enhanced_txpower[element].mimo2_max;
- if ((priv->cfg->valid_tx_ant == ANT_ABC) &&
- (enhanced_txpower[element].mimo3_max > max_txpower_avg))
- max_txpower_avg = enhanced_txpower[element].mimo3_max;
-
- /*
- * max. tx power in EEPROM is in 1/2 dBm format
- * convert from 1/2 dBm to dBm (round-up convert)
- * but we also do not want to loss 1/2 dBm resolution which
- * will impact performance
- */
- *max_txpower_in_half_dbm = max_txpower_avg;
- return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1);
-}
-
-static void
-iwlcore_eeprom_enh_txp_read_element(struct iwl_priv *priv,
- struct iwl_eeprom_enhanced_txpwr *txp,
- s8 max_txpower_avg)
-{
- int ch_idx;
- bool is_ht40 = txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ;
- enum ieee80211_band band;
-
- band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ?
- IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
-
- for (ch_idx = 0; ch_idx < priv->channel_count; ch_idx++) {
- struct iwl_channel_info *ch_info = &priv->channel_info[ch_idx];
-
- /* update matching channel or from common data only */
- if (txp->channel != 0 && ch_info->channel != txp->channel)
- continue;
-
- /* update matching band only */
- if (band != ch_info->band)
- continue;
-
- if (ch_info->max_power_avg < max_txpower_avg && !is_ht40) {
- ch_info->max_power_avg = max_txpower_avg;
- ch_info->curr_txpow = max_txpower_avg;
- ch_info->scan_power = max_txpower_avg;
- }
-
- if (is_ht40 && ch_info->ht40_max_power_avg < max_txpower_avg)
- ch_info->ht40_max_power_avg = max_txpower_avg;
- }
-}
-
-#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT)
-#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr)
-#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE)
-
-#define TXP_CHECK_AND_PRINT(x) ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) \
- ? # x " " : "")
-
-void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv)
-{
- struct iwl_eeprom_enhanced_txpwr *txp_array, *txp;
- int idx, entries;
- __le16 *txp_len;
- s8 max_txp_avg, max_txp_avg_halfdbm;
-
- BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8);
-
- /* the length is in 16-bit words, but we want entries */
- txp_len = (__le16 *) iwlagn_eeprom_query_addr(priv, EEPROM_TXP_SZ_OFFS);
- entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN;
-
- txp_array = (void *) iwlagn_eeprom_query_addr(priv, EEPROM_TXP_OFFS);
-
- for (idx = 0; idx < entries; idx++) {
- txp = &txp_array[idx];
- /* skip invalid entries */
- if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID))
- continue;
-
- IWL_DEBUG_EEPROM(priv, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n",
- (txp->channel && (txp->flags &
- IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ?
- "Common " : (txp->channel) ?
- "Channel" : "Common",
- (txp->channel),
- TXP_CHECK_AND_PRINT(VALID),
- TXP_CHECK_AND_PRINT(BAND_52G),
- TXP_CHECK_AND_PRINT(OFDM),
- TXP_CHECK_AND_PRINT(40MHZ),
- TXP_CHECK_AND_PRINT(HT_AP),
- TXP_CHECK_AND_PRINT(RES1),
- TXP_CHECK_AND_PRINT(RES2),
- TXP_CHECK_AND_PRINT(COMMON_TYPE),
- txp->flags);
- IWL_DEBUG_EEPROM(priv, "\t\t chain_A: 0x%02x "
- "chain_B: 0X%02x chain_C: 0X%02x\n",
- txp->chain_a_max, txp->chain_b_max,
- txp->chain_c_max);
- IWL_DEBUG_EEPROM(priv, "\t\t MIMO2: 0x%02x "
- "MIMO3: 0x%02x High 20_on_40: 0x%02x "
- "Low 20_on_40: 0x%02x\n",
- txp->mimo2_max, txp->mimo3_max,
- ((txp->delta_20_in_40 & 0xf0) >> 4),
- (txp->delta_20_in_40 & 0x0f));
-
- max_txp_avg = iwl_get_max_txpower_avg(priv, txp_array, idx,
- &max_txp_avg_halfdbm);
-
- /*
- * Update the user limit values values to the highest
- * power supported by any channel
- */
- if (max_txp_avg > priv->tx_power_user_lmt)
- priv->tx_power_user_lmt = max_txp_avg;
- if (max_txp_avg_halfdbm > priv->tx_power_lmt_in_half_dbm)
- priv->tx_power_lmt_in_half_dbm = max_txp_avg_halfdbm;
-
- iwlcore_eeprom_enh_txp_read_element(priv, txp, max_txp_avg);
- }
-}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
deleted file mode 100644
index 23fa93deae96..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/******************************************************************************
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-
-#include "iwl-dev.h"
-#include "iwl-core.h"
-#include "iwl-io.h"
-#include "iwl-agn.h"
-
-int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant)
-{
- struct iwl_tx_ant_config_cmd tx_ant_cmd = {
- .valid = cpu_to_le32(valid_tx_ant),
- };
-
- if (IWL_UCODE_API(priv->ucode_ver) > 1) {
- IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant);
- return iwl_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD,
- sizeof(struct iwl_tx_ant_config_cmd),
- &tx_ant_cmd);
- } else {
- IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n");
- return -EOPNOTSUPP;
- }
-}
-
-static u16 iwlagn_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data)
-{
- u16 size = (u16)sizeof(struct iwl_addsta_cmd);
- struct iwl_addsta_cmd *addsta = (struct iwl_addsta_cmd *)data;
- memcpy(addsta, cmd, size);
- /* resrved in 5000 */
- addsta->rate_n_flags = cpu_to_le16(0);
- return size;
-}
-
-static void iwlagn_gain_computation(struct iwl_priv *priv,
- u32 average_noise[NUM_RX_CHAINS],
- u16 min_average_noise_antenna_i,
- u32 min_average_noise,
- u8 default_chain)
-{
- int i;
- s32 delta_g;
- struct iwl_chain_noise_data *data = &priv->chain_noise_data;
-
- /*
- * Find Gain Code for the chains based on "default chain"
- */
- for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) {
- if ((data->disconn_array[i])) {
- data->delta_gain_code[i] = 0;
- continue;
- }
-
- delta_g = (priv->cfg->base_params->chain_noise_scale *
- ((s32)average_noise[default_chain] -
- (s32)average_noise[i])) / 1500;
-
- /* bound gain by 2 bits value max, 3rd bit is sign */
- data->delta_gain_code[i] =
- min(abs(delta_g), (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
-
- if (delta_g < 0)
- /*
- * set negative sign ...
- * note to Intel developers: This is uCode API format,
- * not the format of any internal device registers.
- * Do not change this format for e.g. 6050 or similar
- * devices. Change format only if more resolution
- * (i.e. more than 2 bits magnitude) is needed.
- */
- data->delta_gain_code[i] |= (1 << 2);
- }
-
- IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d ANT_C = %d\n",
- data->delta_gain_code[1], data->delta_gain_code[2]);
-
- if (!data->radio_write) {
- struct iwl_calib_chain_noise_gain_cmd cmd;
-
- memset(&cmd, 0, sizeof(cmd));
-
- cmd.hdr.op_code = priv->_agn.phy_calib_chain_noise_gain_cmd;
- cmd.hdr.first_group = 0;
- cmd.hdr.groups_num = 1;
- cmd.hdr.data_valid = 1;
- cmd.delta_gain_1 = data->delta_gain_code[1];
- cmd.delta_gain_2 = data->delta_gain_code[2];
- iwl_send_cmd_pdu_async(priv, REPLY_PHY_CALIBRATION_CMD,
- sizeof(cmd), &cmd, NULL);
-
- data->radio_write = 1;
- data->state = IWL_CHAIN_NOISE_CALIBRATED;
- }
-}
-
-static void iwlagn_chain_noise_reset(struct iwl_priv *priv)
-{
- struct iwl_chain_noise_data *data = &priv->chain_noise_data;
- int ret;
-
- if ((data->state == IWL_CHAIN_NOISE_ALIVE) &&
- iwl_is_any_associated(priv)) {
- struct iwl_calib_chain_noise_reset_cmd cmd;
-
- /* clear data for chain noise calibration algorithm */
- data->chain_noise_a = 0;
- data->chain_noise_b = 0;
- data->chain_noise_c = 0;
- data->chain_signal_a = 0;
- data->chain_signal_b = 0;
- data->chain_signal_c = 0;
- data->beacon_count = 0;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.op_code = priv->_agn.phy_calib_chain_noise_reset_cmd;
- cmd.hdr.first_group = 0;
- cmd.hdr.groups_num = 1;
- cmd.hdr.data_valid = 1;
- ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
- sizeof(cmd), &cmd);
- if (ret)
- IWL_ERR(priv,
- "Could not send REPLY_PHY_CALIBRATION_CMD\n");
- data->state = IWL_CHAIN_NOISE_ACCUMULATE;
- IWL_DEBUG_CALIB(priv, "Run chain_noise_calibrate\n");
- }
-}
-
-static void iwlagn_tx_cmd_protection(struct iwl_priv *priv,
- struct ieee80211_tx_info *info,
- __le16 fc, __le32 *tx_flags)
-{
- if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS ||
- info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT ||
- info->flags & IEEE80211_TX_CTL_AMPDU)
- *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK;
-}
-
-/* Calc max signal level (dBm) among 3 possible receivers */
-static int iwlagn_calc_rssi(struct iwl_priv *priv,
- struct iwl_rx_phy_res *rx_resp)
-{
- /* data from PHY/DSP regarding signal strength, etc.,
- * contents are always there, not configurable by host
- */
- struct iwlagn_non_cfg_phy *ncphy =
- (struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
- u32 val, rssi_a, rssi_b, rssi_c, max_rssi;
- u8 agc;
-
- val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]);
- agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS;
-
- /* Find max rssi among 3 possible receivers.
- * These values are measured by the digital signal processor (DSP).
- * They should stay fairly constant even as the signal strength varies,
- * if the radio's automatic gain control (AGC) is working right.
- * AGC value (see below) will provide the "interesting" info.
- */
- val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]);
- rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >>
- IWLAGN_OFDM_RSSI_A_BIT_POS;
- rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >>
- IWLAGN_OFDM_RSSI_B_BIT_POS;
- val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_C_IDX]);
- rssi_c = (val & IWLAGN_OFDM_RSSI_INBAND_C_BITMSK) >>
- IWLAGN_OFDM_RSSI_C_BIT_POS;
-
- max_rssi = max_t(u32, rssi_a, rssi_b);
- max_rssi = max_t(u32, max_rssi, rssi_c);
-
- IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n",
- rssi_a, rssi_b, rssi_c, max_rssi, agc);
-
- /* dBm = max_rssi dB - agc dB - constant.
- * Higher AGC (higher radio gain) means lower signal. */
- return max_rssi - agc - IWLAGN_RSSI_OFFSET;
-}
-
-static int iwlagn_set_pan_params(struct iwl_priv *priv)
-{
- struct iwl_wipan_params_cmd cmd;
- struct iwl_rxon_context *ctx_bss, *ctx_pan;
- int slot0 = 300, slot1 = 0;
- int ret;
-
- if (priv->valid_contexts == BIT(IWL_RXON_CTX_BSS))
- return 0;
-
- BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
-
- lockdep_assert_held(&priv->mutex);
-
- ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS];
- ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN];
-
- /*
- * If the PAN context is inactive, then we don't need
- * to update the PAN parameters, the last thing we'll
- * have done before it goes inactive is making the PAN
- * parameters be WLAN-only.
- */
- if (!ctx_pan->is_active)
- return 0;
-
- memset(&cmd, 0, sizeof(cmd));
-
- /* only 2 slots are currently allowed */
- cmd.num_slots = 2;
-
- cmd.slots[0].type = 0; /* BSS */
- cmd.slots[1].type = 1; /* PAN */
-
- if (priv->_agn.hw_roc_channel) {
- /* both contexts must be used for this to happen */
- slot1 = priv->_agn.hw_roc_duration;
- slot0 = IWL_MIN_SLOT_TIME;
- } else if (ctx_bss->vif && ctx_pan->vif) {
- int bcnint = ctx_pan->vif->bss_conf.beacon_int;
- int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;
-
- /* should be set, but seems unused?? */
- cmd.flags |= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE);
-
- if (ctx_pan->vif->type == NL80211_IFTYPE_AP &&
- bcnint &&
- bcnint != ctx_bss->vif->bss_conf.beacon_int) {
- IWL_ERR(priv,
- "beacon intervals don't match (%d, %d)\n",
- ctx_bss->vif->bss_conf.beacon_int,
- ctx_pan->vif->bss_conf.beacon_int);
- } else
- bcnint = max_t(int, bcnint,
- ctx_bss->vif->bss_conf.beacon_int);
- if (!bcnint)
- bcnint = DEFAULT_BEACON_INTERVAL;
- slot0 = bcnint / 2;
- slot1 = bcnint - slot0;
-
- if (test_bit(STATUS_SCAN_HW, &priv->status) ||
- (!ctx_bss->vif->bss_conf.idle &&
- !ctx_bss->vif->bss_conf.assoc)) {
- slot0 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME;
- slot1 = IWL_MIN_SLOT_TIME;
- } else if (!ctx_pan->vif->bss_conf.idle &&
- !ctx_pan->vif->bss_conf.assoc) {
- slot1 = bcnint * 3 - IWL_MIN_SLOT_TIME;
- slot0 = IWL_MIN_SLOT_TIME;
- }
- } else if (ctx_pan->vif) {
- slot0 = 0;
- slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) *
- ctx_pan->vif->bss_conf.beacon_int;
- slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1);
-
- if (test_bit(STATUS_SCAN_HW, &priv->status)) {
- slot0 = slot1 * 3 - IWL_MIN_SLOT_TIME;
- slot1 = IWL_MIN_SLOT_TIME;
- }
- }
-
- cmd.slots[0].width = cpu_to_le16(slot0);
- cmd.slots[1].width = cpu_to_le16(slot1);
-
- ret = iwl_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, sizeof(cmd), &cmd);
- if (ret)
- IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret);
-
- return ret;
-}
-
-struct iwl_hcmd_ops iwlagn_hcmd = {
- .commit_rxon = iwlagn_commit_rxon,
- .set_rxon_chain = iwlagn_set_rxon_chain,
- .set_tx_ant = iwlagn_send_tx_ant_config,
- .send_bt_config = iwl_send_bt_config,
- .set_pan_params = iwlagn_set_pan_params,
-};
-
-struct iwl_hcmd_ops iwlagn_bt_hcmd = {
- .commit_rxon = iwlagn_commit_rxon,
- .set_rxon_chain = iwlagn_set_rxon_chain,
- .set_tx_ant = iwlagn_send_tx_ant_config,
- .send_bt_config = iwlagn_send_advance_bt_config,
- .set_pan_params = iwlagn_set_pan_params,
-};
-
-struct iwl_hcmd_utils_ops iwlagn_hcmd_utils = {
- .build_addsta_hcmd = iwlagn_build_addsta_hcmd,
- .gain_computation = iwlagn_gain_computation,
- .chain_noise_reset = iwlagn_chain_noise_reset,
- .tx_cmd_protection = iwlagn_tx_cmd_protection,
- .calc_rssi = iwlagn_calc_rssi,
- .request_scan = iwlagn_request_scan,
- .post_scan = iwlagn_post_scan,
-};
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
index 7bd19f4e66de..123ef5e129d5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
@@ -78,17 +78,23 @@
#define IWLAGN_RTC_DATA_SIZE (IWLAGN_RTC_DATA_UPPER_BOUND - \
IWLAGN_RTC_DATA_LOWER_BOUND)
+#define IWL60_RTC_INST_LOWER_BOUND (0x000000)
+#define IWL60_RTC_INST_UPPER_BOUND (0x040000)
+#define IWL60_RTC_DATA_LOWER_BOUND (0x800000)
+#define IWL60_RTC_DATA_UPPER_BOUND (0x814000)
+#define IWL60_RTC_INST_SIZE \
+ (IWL60_RTC_INST_UPPER_BOUND - IWL60_RTC_INST_LOWER_BOUND)
+#define IWL60_RTC_DATA_SIZE \
+ (IWL60_RTC_DATA_UPPER_BOUND - IWL60_RTC_DATA_LOWER_BOUND)
+
/* RSSI to dBm */
#define IWLAGN_RSSI_OFFSET 44
-/* PCI registers */
-#define PCI_CFG_RETRY_TIMEOUT 0x041
-
-/* PCI register values */
-#define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01
-#define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02
-
-#define IWLAGN_DEFAULT_TX_RETRY 15
+#define IWLAGN_DEFAULT_TX_RETRY 15
+#define IWLAGN_MGMT_DFAULT_RETRY_LIMIT 3
+#define IWLAGN_RTS_DFAULT_RETRY_LIMIT 60
+#define IWLAGN_BAR_DFAULT_RETRY_LIMIT 60
+#define IWLAGN_LOW_RETRY_LIMIT 7
/* Limit range of txpower output target to be between these values */
#define IWLAGN_TX_POWER_TARGET_POWER_MIN (0) /* 0 dBm: 1 milliwatt */
@@ -99,20 +105,7 @@
#define IWLAGN_CMD_FIFO_NUM 7
#define IWLAGN_NUM_QUEUES 20
-#define IWLAGN_NUM_AMPDU_QUEUES 10
-#define IWLAGN_FIRST_AMPDU_QUEUE 10
-
-/* Fixed (non-configurable) rx data from phy */
-
-/**
- * struct iwlagn_schedq_bc_tbl scheduler byte count table
- * base physical address provided by SCD_DRAM_BASE_ADDR
- * @tfd_offset 0-12 - tx command byte count
- * 12-16 - station index
- */
-struct iwlagn_scd_bc_tbl {
- __le16 tfd_offset[TFD_QUEUE_BC_SIZE];
-} __packed;
-
+#define IWLAGN_NUM_AMPDU_QUEUES 9
+#define IWLAGN_FIRST_AMPDU_QUEUE 11
#endif /* __iwl_agn_hw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-ict.c b/drivers/net/wireless/iwlwifi/iwl-agn-ict.c
deleted file mode 100644
index 0d5fda44c3a3..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-agn-ict.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/******************************************************************************
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *****************************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/etherdevice.h>
-#include <linux/sched.h>
-#include <linux/gfp.h>
-#include <net/mac80211.h>
-
-#include "iwl-dev.h"
-#include "iwl-core.h"
-#include "iwl-agn.h"
-#include "iwl-helpers.h"
-
-#define ICT_COUNT (PAGE_SIZE/sizeof(u32))
-
-/* Free dram table */
-void iwl_free_isr_ict(struct iwl_priv *priv)
-{
- if (priv->_agn.ict_tbl_vir) {
- dma_free_coherent(&priv->pci_dev->dev,
- (sizeof(u32) * ICT_COUNT) + PAGE_SIZE,
- priv->_agn.ict_tbl_vir,
- priv->_agn.ict_tbl_dma);
- priv->_agn.ict_tbl_vir = NULL;
- }
-}
-
-
-/* allocate dram shared table it is a PAGE_SIZE aligned
- * also reset all data related to ICT table interrupt.
- */
-int iwl_alloc_isr_ict(struct iwl_priv *priv)
-{
-
- /* allocate shrared data table */
- priv->_agn.ict_tbl_vir =
- dma_alloc_coherent(&priv->pci_dev->dev,
- (sizeof(u32) * ICT_COUNT) + PAGE_SIZE,
- &priv->_agn.ict_tbl_dma, GFP_KERNEL);
- if (!priv->_agn.ict_tbl_vir)
- return -ENOMEM;
-
- /* align table to PAGE_SIZE boundary */
- priv->_agn.aligned_ict_tbl_dma = ALIGN(priv->_agn.ict_tbl_dma, PAGE_SIZE);
-
- IWL_DEBUG_ISR(priv, "ict dma addr %Lx dma aligned %Lx diff %d\n",
- (unsigned long long)priv->_agn.ict_tbl_dma,
- (unsigned long long)priv->_agn.aligned_ict_tbl_dma,
- (int)(priv->_agn.aligned_ict_tbl_dma - priv->_agn.ict_tbl_dma));
-
- priv->_agn.ict_tbl = priv->_agn.ict_tbl_vir +
- (priv->_agn.aligned_ict_tbl_dma - priv->_agn.ict_tbl_dma);
-
- IWL_DEBUG_ISR(priv, "ict vir addr %p vir aligned %p diff %d\n",
- priv->_agn.ict_tbl, priv->_agn.ict_tbl_vir,
- (int)(priv->_agn.aligned_ict_tbl_dma - priv->_agn.ict_tbl_dma));
-
- /* reset table and index to all 0 */
- memset(priv->_agn.ict_tbl_vir,0, (sizeof(u32) * ICT_COUNT) + PAGE_SIZE);
- priv->_agn.ict_index = 0;
-
- /* add periodic RX interrupt */
- priv->inta_mask |= CSR_INT_BIT_RX_PERIODIC;
- return 0;
-}
-
-/* Device is going up inform it about using ICT interrupt table,
- * also we need to tell the driver to start using ICT interrupt.
- */
-int iwl_reset_ict(struct iwl_priv *priv)
-{
- u32 val;
- unsigned long flags;
-
- if (!priv->_agn.ict_tbl_vir)
- return 0;
-
- spin_lock_irqsave(&priv->lock, flags);
- iwl_disable_interrupts(priv);
-
- memset(&priv->_agn.ict_tbl[0], 0, sizeof(u32) * ICT_COUNT);
-
- val = priv->_agn.aligned_ict_tbl_dma >> PAGE_SHIFT;
-
- val |= CSR_DRAM_INT_TBL_ENABLE;
- val |= CSR_DRAM_INIT_TBL_WRAP_CHECK;
-
- IWL_DEBUG_ISR(priv, "CSR_DRAM_INT_TBL_REG =0x%X "
- "aligned dma address %Lx\n",
- val, (unsigned long long)priv->_agn.aligned_ict_tbl_dma);
-
- iwl_write32(priv, CSR_DRAM_INT_TBL_REG, val);
- priv->_agn.use_ict = true;
- priv->_agn.ict_index = 0;
- iwl_write32(priv, CSR_INT, priv->inta_mask);
- iwl_enable_interrupts(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return 0;
-}
-
-/* Device is going down disable ict interrupt usage */
-void iwl_disable_ict(struct iwl_priv *priv)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
- priv->_agn.use_ict = false;
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static irqreturn_t iwl_isr(int irq, void *data)
-{
- struct iwl_priv *priv = data;
- u32 inta, inta_mask;
- unsigned long flags;
-#ifdef CONFIG_IWLWIFI_DEBUG
- u32 inta_fh;
-#endif
- if (!priv)
- return IRQ_NONE;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- /* Disable (but don't clear!) interrupts here to avoid
- * back-to-back ISRs and sporadic interrupts from our NIC.
- * If we have something to service, the tasklet will re-enable ints.
- * If we *don't* have something, we'll re-enable before leaving here. */
- inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
- iwl_write32(priv, CSR_INT_MASK, 0x00000000);
-
- /* Discover which interrupts are active/pending */
- inta = iwl_read32(priv, CSR_INT);
-
- /* Ignore interrupt if there's nothing in NIC to service.
- * This may be due to IRQ shared with another device,
- * or due to sporadic interrupts thrown from our NIC. */
- if (!inta) {
- IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
- goto none;
- }
-
- if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
- /* Hardware disappeared. It might have already raised
- * an interrupt */
- IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
- goto unplugged;
- }
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) {
- inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
- IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, "
- "fh 0x%08x\n", inta, inta_mask, inta_fh);
- }
-#endif
-
- priv->_agn.inta |= inta;
- /* iwl_irq_tasklet() will service interrupts and re-enable them */
- if (likely(inta))
- tasklet_schedule(&priv->irq_tasklet);
- else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta)
- iwl_enable_interrupts(priv);
-
- unplugged:
- spin_unlock_irqrestore(&priv->lock, flags);
- return IRQ_HANDLED;
-
- none:
- /* re-enable interrupts here since we don't have anything to service. */
- /* only Re-enable if disabled by irq and no schedules tasklet. */
- if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta)
- iwl_enable_interrupts(priv);
-
- spin_unlock_irqrestore(&priv->lock, flags);
- return IRQ_NONE;
-}
-
-/* interrupt handler using ict table, with this interrupt driver will
- * stop using INTA register to get device's interrupt, reading this register
- * is expensive, device will write interrupts in ICT dram table, increment
- * index then will fire interrupt to driver, driver will OR all ICT table
- * entries from current index up to table entry with 0 value. the result is
- * the interrupt we need to service, driver will set the entries back to 0 and
- * set index.
- */
-irqreturn_t iwl_isr_ict(int irq, void *data)
-{
- struct iwl_priv *priv = data;
- u32 inta, inta_mask;
- u32 val = 0;
- unsigned long flags;
-
- if (!priv)
- return IRQ_NONE;
-
- /* dram interrupt table not set yet,
- * use legacy interrupt.
- */
- if (!priv->_agn.use_ict)
- return iwl_isr(irq, data);
-
- spin_lock_irqsave(&priv->lock, flags);
-
- /* Disable (but don't clear!) interrupts here to avoid
- * back-to-back ISRs and sporadic interrupts from our NIC.
- * If we have something to service, the tasklet will re-enable ints.
- * If we *don't* have something, we'll re-enable before leaving here.
- */
- inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
- iwl_write32(priv, CSR_INT_MASK, 0x00000000);
-
-
- /* Ignore interrupt if there's nothing in NIC to service.
- * This may be due to IRQ shared with another device,
- * or due to sporadic interrupts thrown from our NIC. */
- if (!priv->_agn.ict_tbl[priv->_agn.ict_index]) {
- IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
- goto none;
- }
-
- /* read all entries that not 0 start with ict_index */
- while (priv->_agn.ict_tbl[priv->_agn.ict_index]) {
-
- val |= le32_to_cpu(priv->_agn.ict_tbl[priv->_agn.ict_index]);
- IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n",
- priv->_agn.ict_index,
- le32_to_cpu(priv->_agn.ict_tbl[priv->_agn.ict_index]));
- priv->_agn.ict_tbl[priv->_agn.ict_index] = 0;
- priv->_agn.ict_index = iwl_queue_inc_wrap(priv->_agn.ict_index,
- ICT_COUNT);
-
- }
-
- /* We should not get this value, just ignore it. */
- if (val == 0xffffffff)
- val = 0;
-
- /*
- * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit
- * (bit 15 before shifting it to 31) to clear when using interrupt
- * coalescing. fortunately, bits 18 and 19 stay set when this happens
- * so we use them to decide on the real state of the Rx bit.
- * In order words, bit 15 is set if bit 18 or bit 19 are set.
- */
- if (val & 0xC0000)
- val |= 0x8000;
-
- inta = (0xff & val) | ((0xff00 & val) << 16);
- IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
- inta, inta_mask, val);
-
- inta &= priv->inta_mask;
- priv->_agn.inta |= inta;
-
- /* iwl_irq_tasklet() will service interrupts and re-enable them */
- if (likely(inta))
- tasklet_schedule(&priv->irq_tasklet);
- else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta) {
- /* Allow interrupt if was disabled by this handler and
- * no tasklet was schedules, We should not enable interrupt,
- * tasklet will enable it.
- */
- iwl_enable_interrupts(priv);
- }
-
- spin_unlock_irqrestore(&priv->lock, flags);
- return IRQ_HANDLED;
-
- none:
- /* re-enable interrupts here since we don't have anything to service.
- * only Re-enable if disabled by irq.
- */
- if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta)
- iwl_enable_interrupts(priv);
-
- spin_unlock_irqrestore(&priv->lock, flags);
- return IRQ_NONE;
-}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
index f803fb62f8bc..1a52ed29f2d6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
@@ -35,469 +35,10 @@
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-io.h"
-#include "iwl-helpers.h"
#include "iwl-agn-hw.h"
#include "iwl-agn.h"
-#include "iwl-sta.h"
-
-static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp)
-{
- return le32_to_cpup((__le32 *)&tx_resp->status +
- tx_resp->frame_count) & MAX_SN;
-}
-
-static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status)
-{
- status &= TX_STATUS_MSK;
-
- switch (status) {
- case TX_STATUS_POSTPONE_DELAY:
- priv->_agn.reply_tx_stats.pp_delay++;
- break;
- case TX_STATUS_POSTPONE_FEW_BYTES:
- priv->_agn.reply_tx_stats.pp_few_bytes++;
- break;
- case TX_STATUS_POSTPONE_BT_PRIO:
- priv->_agn.reply_tx_stats.pp_bt_prio++;
- break;
- case TX_STATUS_POSTPONE_QUIET_PERIOD:
- priv->_agn.reply_tx_stats.pp_quiet_period++;
- break;
- case TX_STATUS_POSTPONE_CALC_TTAK:
- priv->_agn.reply_tx_stats.pp_calc_ttak++;
- break;
- case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
- priv->_agn.reply_tx_stats.int_crossed_retry++;
- break;
- case TX_STATUS_FAIL_SHORT_LIMIT:
- priv->_agn.reply_tx_stats.short_limit++;
- break;
- case TX_STATUS_FAIL_LONG_LIMIT:
- priv->_agn.reply_tx_stats.long_limit++;
- break;
- case TX_STATUS_FAIL_FIFO_UNDERRUN:
- priv->_agn.reply_tx_stats.fifo_underrun++;
- break;
- case TX_STATUS_FAIL_DRAIN_FLOW:
- priv->_agn.reply_tx_stats.drain_flow++;
- break;
- case TX_STATUS_FAIL_RFKILL_FLUSH:
- priv->_agn.reply_tx_stats.rfkill_flush++;
- break;
- case TX_STATUS_FAIL_LIFE_EXPIRE:
- priv->_agn.reply_tx_stats.life_expire++;
- break;
- case TX_STATUS_FAIL_DEST_PS:
- priv->_agn.reply_tx_stats.dest_ps++;
- break;
- case TX_STATUS_FAIL_HOST_ABORTED:
- priv->_agn.reply_tx_stats.host_abort++;
- break;
- case TX_STATUS_FAIL_BT_RETRY:
- priv->_agn.reply_tx_stats.bt_retry++;
- break;
- case TX_STATUS_FAIL_STA_INVALID:
- priv->_agn.reply_tx_stats.sta_invalid++;
- break;
- case TX_STATUS_FAIL_FRAG_DROPPED:
- priv->_agn.reply_tx_stats.frag_drop++;
- break;
- case TX_STATUS_FAIL_TID_DISABLE:
- priv->_agn.reply_tx_stats.tid_disable++;
- break;
- case TX_STATUS_FAIL_FIFO_FLUSHED:
- priv->_agn.reply_tx_stats.fifo_flush++;
- break;
- case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL:
- priv->_agn.reply_tx_stats.insuff_cf_poll++;
- break;
- case TX_STATUS_FAIL_PASSIVE_NO_RX:
- priv->_agn.reply_tx_stats.fail_hw_drop++;
- break;
- case TX_STATUS_FAIL_NO_BEACON_ON_RADAR:
- priv->_agn.reply_tx_stats.sta_color_mismatch++;
- break;
- default:
- priv->_agn.reply_tx_stats.unknown++;
- break;
- }
-}
-
-static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
-{
- status &= AGG_TX_STATUS_MSK;
-
- switch (status) {
- case AGG_TX_STATE_UNDERRUN_MSK:
- priv->_agn.reply_agg_tx_stats.underrun++;
- break;
- case AGG_TX_STATE_BT_PRIO_MSK:
- priv->_agn.reply_agg_tx_stats.bt_prio++;
- break;
- case AGG_TX_STATE_FEW_BYTES_MSK:
- priv->_agn.reply_agg_tx_stats.few_bytes++;
- break;
- case AGG_TX_STATE_ABORT_MSK:
- priv->_agn.reply_agg_tx_stats.abort++;
- break;
- case AGG_TX_STATE_LAST_SENT_TTL_MSK:
- priv->_agn.reply_agg_tx_stats.last_sent_ttl++;
- break;
- case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK:
- priv->_agn.reply_agg_tx_stats.last_sent_try++;
- break;
- case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK:
- priv->_agn.reply_agg_tx_stats.last_sent_bt_kill++;
- break;
- case AGG_TX_STATE_SCD_QUERY_MSK:
- priv->_agn.reply_agg_tx_stats.scd_query++;
- break;
- case AGG_TX_STATE_TEST_BAD_CRC32_MSK:
- priv->_agn.reply_agg_tx_stats.bad_crc32++;
- break;
- case AGG_TX_STATE_RESPONSE_MSK:
- priv->_agn.reply_agg_tx_stats.response++;
- break;
- case AGG_TX_STATE_DUMP_TX_MSK:
- priv->_agn.reply_agg_tx_stats.dump_tx++;
- break;
- case AGG_TX_STATE_DELAY_TX_MSK:
- priv->_agn.reply_agg_tx_stats.delay_tx++;
- break;
- default:
- priv->_agn.reply_agg_tx_stats.unknown++;
- break;
- }
-}
-
-static void iwlagn_set_tx_status(struct iwl_priv *priv,
- struct ieee80211_tx_info *info,
- struct iwl_rxon_context *ctx,
- struct iwlagn_tx_resp *tx_resp,
- int txq_id, bool is_agg)
-{
- u16 status = le16_to_cpu(tx_resp->status.status);
-
- info->status.rates[0].count = tx_resp->failure_frame + 1;
- if (is_agg)
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- info->flags |= iwl_tx_status_to_mac80211(status);
- iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
- info);
- if (!iwl_is_tx_success(status))
- iwlagn_count_tx_err_status(priv, status);
-
- if (status == TX_STATUS_FAIL_PASSIVE_NO_RX &&
- iwl_is_associated_ctx(ctx) && ctx->vif &&
- ctx->vif->type == NL80211_IFTYPE_STATION) {
- ctx->last_tx_rejected = true;
- iwl_stop_queue(priv, &priv->txq[txq_id]);
- }
-
- IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) rate_n_flags "
- "0x%x retries %d\n",
- txq_id,
- iwl_get_tx_fail_reason(status), status,
- le32_to_cpu(tx_resp->rate_n_flags),
- tx_resp->failure_frame);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x
-
-const char *iwl_get_agg_tx_fail_reason(u16 status)
-{
- status &= AGG_TX_STATUS_MSK;
- switch (status) {
- case AGG_TX_STATE_TRANSMITTED:
- return "SUCCESS";
- AGG_TX_STATE_FAIL(UNDERRUN_MSK);
- AGG_TX_STATE_FAIL(BT_PRIO_MSK);
- AGG_TX_STATE_FAIL(FEW_BYTES_MSK);
- AGG_TX_STATE_FAIL(ABORT_MSK);
- AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK);
- AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK);
- AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK);
- AGG_TX_STATE_FAIL(SCD_QUERY_MSK);
- AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK);
- AGG_TX_STATE_FAIL(RESPONSE_MSK);
- AGG_TX_STATE_FAIL(DUMP_TX_MSK);
- AGG_TX_STATE_FAIL(DELAY_TX_MSK);
- }
-
- return "UNKNOWN";
-}
-#endif /* CONFIG_IWLWIFI_DEBUG */
-
-static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
- struct iwl_ht_agg *agg,
- struct iwlagn_tx_resp *tx_resp,
- int txq_id, u16 start_idx)
-{
- u16 status;
- struct agg_tx_status *frame_status = &tx_resp->status;
- struct ieee80211_hdr *hdr = NULL;
- int i, sh, idx;
- u16 seq;
-
- if (agg->wait_for_ba)
- IWL_DEBUG_TX_REPLY(priv, "got tx response w/o block-ack\n");
-
- agg->frame_count = tx_resp->frame_count;
- agg->start_idx = start_idx;
- agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
- agg->bitmap = 0;
-
- /* # frames attempted by Tx command */
- if (agg->frame_count == 1) {
- struct iwl_tx_info *txb;
-
- /* Only one frame was attempted; no block-ack will arrive */
- idx = start_idx;
-
- IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n",
- agg->frame_count, agg->start_idx, idx);
- txb = &priv->txq[txq_id].txb[idx];
- iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(txb->skb),
- txb->ctx, tx_resp, txq_id, true);
- agg->wait_for_ba = 0;
- } else {
- /* Two or more frames were attempted; expect block-ack */
- u64 bitmap = 0;
-
- /*
- * Start is the lowest frame sent. It may not be the first
- * frame in the batch; we figure this out dynamically during
- * the following loop.
- */
- int start = agg->start_idx;
-
- /* Construct bit-map of pending frames within Tx window */
- for (i = 0; i < agg->frame_count; i++) {
- u16 sc;
- status = le16_to_cpu(frame_status[i].status);
- seq = le16_to_cpu(frame_status[i].sequence);
- idx = SEQ_TO_INDEX(seq);
- txq_id = SEQ_TO_QUEUE(seq);
-
- if (status & AGG_TX_STATUS_MSK)
- iwlagn_count_agg_tx_err_status(priv, status);
-
- if (status & (AGG_TX_STATE_FEW_BYTES_MSK |
- AGG_TX_STATE_ABORT_MSK))
- continue;
-
- IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, txq_id=%d idx=%d\n",
- agg->frame_count, txq_id, idx);
- IWL_DEBUG_TX_REPLY(priv, "status %s (0x%08x), "
- "try-count (0x%08x)\n",
- iwl_get_agg_tx_fail_reason(status),
- status & AGG_TX_STATUS_MSK,
- status & AGG_TX_TRY_MSK);
-
- hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx);
- if (!hdr) {
- IWL_ERR(priv,
- "BUG_ON idx doesn't point to valid skb"
- " idx=%d, txq_id=%d\n", idx, txq_id);
- return -1;
- }
-
- sc = le16_to_cpu(hdr->seq_ctrl);
- if (idx != (SEQ_TO_SN(sc) & 0xff)) {
- IWL_ERR(priv,
- "BUG_ON idx doesn't match seq control"
- " idx=%d, seq_idx=%d, seq=%d\n",
- idx, SEQ_TO_SN(sc),
- hdr->seq_ctrl);
- return -1;
- }
-
- IWL_DEBUG_TX_REPLY(priv, "AGG Frame i=%d idx %d seq=%d\n",
- i, idx, SEQ_TO_SN(sc));
-
- /*
- * sh -> how many frames ahead of the starting frame is
- * the current one?
- *
- * Note that all frames sent in the batch must be in a
- * 64-frame window, so this number should be in [0,63].
- * If outside of this window, then we've found a new
- * "first" frame in the batch and need to change start.
- */
- sh = idx - start;
-
- /*
- * If >= 64, out of window. start must be at the front
- * of the circular buffer, idx must be near the end of
- * the buffer, and idx is the new "first" frame. Shift
- * the indices around.
- */
- if (sh >= 64) {
- /* Shift bitmap by start - idx, wrapped */
- sh = 0x100 - idx + start;
- bitmap = bitmap << sh;
- /* Now idx is the new start so sh = 0 */
- sh = 0;
- start = idx;
- /*
- * If <= -64 then wraps the 256-pkt circular buffer
- * (e.g., start = 255 and idx = 0, sh should be 1)
- */
- } else if (sh <= -64) {
- sh = 0x100 - start + idx;
- /*
- * If < 0 but > -64, out of window. idx is before start
- * but not wrapped. Shift the indices around.
- */
- } else if (sh < 0) {
- /* Shift by how far start is ahead of idx */
- sh = start - idx;
- bitmap = bitmap << sh;
- /* Now idx is the new start so sh = 0 */
- start = idx;
- sh = 0;
- }
- /* Sequence number start + sh was sent in this batch */
- bitmap |= 1ULL << sh;
- IWL_DEBUG_TX_REPLY(priv, "start=%d bitmap=0x%llx\n",
- start, (unsigned long long)bitmap);
- }
-
- /*
- * Store the bitmap and possibly the new start, if we wrapped
- * the buffer above
- */
- agg->bitmap = bitmap;
- agg->start_idx = start;
- IWL_DEBUG_TX_REPLY(priv, "Frames %d start_idx=%d bitmap=0x%llx\n",
- agg->frame_count, agg->start_idx,
- (unsigned long long)agg->bitmap);
-
- if (bitmap)
- agg->wait_for_ba = 1;
- }
- return 0;
-}
-
-void iwl_check_abort_status(struct iwl_priv *priv,
- u8 frame_count, u32 status)
-{
- if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
- IWL_ERR(priv, "Tx flush command to flush out all frames\n");
- if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
- queue_work(priv->workqueue, &priv->tx_flush);
- }
-}
-
-static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- u16 sequence = le16_to_cpu(pkt->hdr.sequence);
- int txq_id = SEQ_TO_QUEUE(sequence);
- int index = SEQ_TO_INDEX(sequence);
- struct iwl_tx_queue *txq = &priv->txq[txq_id];
- struct ieee80211_tx_info *info;
- struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
- struct iwl_tx_info *txb;
- u32 status = le16_to_cpu(tx_resp->status.status);
- int tid;
- int sta_id;
- int freed;
- unsigned long flags;
-
- if ((index >= txq->q.n_bd) || (iwl_queue_used(&txq->q, index) == 0)) {
- IWL_ERR(priv, "Read index for DMA queue txq_id (%d) index %d "
- "is out of range [0-%d] %d %d\n", txq_id,
- index, txq->q.n_bd, txq->q.write_ptr,
- txq->q.read_ptr);
- return;
- }
-
- txq->time_stamp = jiffies;
- txb = &txq->txb[txq->q.read_ptr];
- info = IEEE80211_SKB_CB(txb->skb);
- memset(&info->status, 0, sizeof(info->status));
-
- tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
- IWLAGN_TX_RES_TID_POS;
- sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >>
- IWLAGN_TX_RES_RA_POS;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
- if (txq->sched_retry) {
- const u32 scd_ssn = iwlagn_get_scd_ssn(tx_resp);
- struct iwl_ht_agg *agg;
-
- agg = &priv->stations[sta_id].tid[tid].agg;
- /*
- * If the BT kill count is non-zero, we'll get this
- * notification again.
- */
- if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 &&
- priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist) {
- IWL_WARN(priv, "receive reply tx with bt_kill\n");
- }
- iwlagn_tx_status_reply_tx(priv, agg, tx_resp, txq_id, index);
-
- /* check if BAR is needed */
- if ((tx_resp->frame_count == 1) && !iwl_is_tx_success(status))
- info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
-
- if (txq->q.read_ptr != (scd_ssn & 0xff)) {
- index = iwl_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
- IWL_DEBUG_TX_REPLY(priv, "Retry scheduler reclaim "
- "scd_ssn=%d idx=%d txq=%d swq=%d\n",
- scd_ssn , index, txq_id, txq->swq_id);
-
- freed = iwlagn_tx_queue_reclaim(priv, txq_id, index);
- iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
-
- if (priv->mac80211_registered &&
- (iwl_queue_space(&txq->q) > txq->q.low_mark) &&
- (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA))
- iwl_wake_queue(priv, txq);
- }
- } else {
- iwlagn_set_tx_status(priv, info, txb->ctx, tx_resp,
- txq_id, false);
- freed = iwlagn_tx_queue_reclaim(priv, txq_id, index);
- iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
-
- if (priv->mac80211_registered &&
- iwl_queue_space(&txq->q) > txq->q.low_mark &&
- status != TX_STATUS_FAIL_PASSIVE_NO_RX)
- iwl_wake_queue(priv, txq);
- }
-
- iwlagn_txq_check_empty(priv, sta_id, tid, txq_id);
-
- iwl_check_abort_status(priv, tx_resp->frame_count, status);
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-}
-
-void iwlagn_rx_handler_setup(struct iwl_priv *priv)
-{
- /* init calibration handlers */
- priv->rx_handlers[CALIBRATION_RES_NOTIFICATION] =
- iwlagn_rx_calib_result;
- priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx;
-
- /* set up notification wait support */
- spin_lock_init(&priv->_agn.notif_wait_lock);
- INIT_LIST_HEAD(&priv->_agn.notif_waits);
- init_waitqueue_head(&priv->_agn.notif_waitq);
-}
-
-void iwlagn_setup_deferred_work(struct iwl_priv *priv)
-{
- /*
- * nothing need to be done here anymore
- * still keep for future use if needed
- */
-}
+#include "iwl-trans.h"
+#include "iwl-shared.h"
int iwlagn_hw_valid_rtc_data_addr(u32 addr)
{
@@ -510,7 +51,7 @@ int iwlagn_send_tx_power(struct iwl_priv *priv)
struct iwlagn_tx_power_dbm_cmd tx_power_cmd;
u8 tx_ant_cfg_cmd;
- if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status),
+ if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->shrd->status),
"TX Power requested while scanning!\n"))
return -EAGAIN;
@@ -540,8 +81,8 @@ int iwlagn_send_tx_power(struct iwl_priv *priv)
else
tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD;
- return iwl_send_cmd_pdu(priv, tx_ant_cfg_cmd, sizeof(tx_power_cmd),
- &tx_power_cmd);
+ return iwl_trans_send_cmd_pdu(trans(priv), tx_ant_cfg_cmd, CMD_SYNC,
+ sizeof(tx_power_cmd), &tx_power_cmd);
}
void iwlagn_temperature(struct iwl_priv *priv)
@@ -553,11 +94,7 @@ void iwlagn_temperature(struct iwl_priv *priv)
u16 iwlagn_eeprom_calib_version(struct iwl_priv *priv)
{
- struct iwl_eeprom_calib_hdr {
- u8 version;
- u8 pa_type;
- u16 voltage;
- } *hdr;
+ struct iwl_eeprom_calib_hdr *hdr;
hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv,
EEPROM_CALIB_ALL);
@@ -610,8 +147,7 @@ static u32 eeprom_indirect_address(const struct iwl_priv *priv, u32 address)
return (address & ADDRESS_MSK) + (offset << 1);
}
-const u8 *iwlagn_eeprom_query_addr(const struct iwl_priv *priv,
- size_t offset)
+const u8 *iwl_eeprom_query_addr(const struct iwl_priv *priv, size_t offset)
{
u32 address = eeprom_indirect_address(priv, offset);
BUG_ON(address >= priv->cfg->base_params->eeprom_size);
@@ -622,367 +158,15 @@ struct iwl_mod_params iwlagn_mod_params = {
.amsdu_size_8K = 1,
.restart_fw = 1,
.plcp_check = true,
+ .bt_coex_active = true,
+ .no_sleep_autoadjust = true,
+ .power_level = IWL_POWER_INDEX_1,
+ .bt_ch_announce = true,
+ .wanted_ucode_alternative = 1,
+ .auto_agg = true,
/* the rest are 0 by default */
};
-void iwlagn_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
-{
- unsigned long flags;
- int i;
- spin_lock_irqsave(&rxq->lock, flags);
- INIT_LIST_HEAD(&rxq->rx_free);
- INIT_LIST_HEAD(&rxq->rx_used);
- /* Fill the rx_used queue with _all_ of the Rx buffers */
- for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
- /* In the reset function, these buffers may have been allocated
- * to an SKB, so we need to unmap and free potential storage */
- if (rxq->pool[i].page != NULL) {
- pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma,
- PAGE_SIZE << priv->hw_params.rx_page_order,
- PCI_DMA_FROMDEVICE);
- __iwl_free_pages(priv, rxq->pool[i].page);
- rxq->pool[i].page = NULL;
- }
- list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
- }
-
- for (i = 0; i < RX_QUEUE_SIZE; i++)
- rxq->queue[i] = NULL;
-
- /* Set us so that we have processed and used all buffers, but have
- * not restocked the Rx queue with fresh buffers */
- rxq->read = rxq->write = 0;
- rxq->write_actual = 0;
- rxq->free_count = 0;
- spin_unlock_irqrestore(&rxq->lock, flags);
-}
-
-int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
-{
- u32 rb_size;
- const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
- u32 rb_timeout = 0; /* FIXME: RX_RB_TIMEOUT for all devices? */
-
- rb_timeout = RX_RB_TIMEOUT;
-
- if (iwlagn_mod_params.amsdu_size_8K)
- rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
- else
- rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
-
- /* Stop Rx DMA */
- iwl_write_direct32(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
-
- /* Reset driver's Rx queue write index */
- iwl_write_direct32(priv, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
-
- /* Tell device where to find RBD circular buffer in DRAM */
- iwl_write_direct32(priv, FH_RSCSR_CHNL0_RBDCB_BASE_REG,
- (u32)(rxq->bd_dma >> 8));
-
- /* Tell device where in DRAM to update its Rx status */
- iwl_write_direct32(priv, FH_RSCSR_CHNL0_STTS_WPTR_REG,
- rxq->rb_stts_dma >> 4);
-
- /* Enable Rx DMA
- * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in
- * the credit mechanism in 5000 HW RX FIFO
- * Direct rx interrupts to hosts
- * Rx buffer size 4 or 8k
- * RB timeout 0x10
- * 256 RBDs
- */
- iwl_write_direct32(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG,
- FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
- FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |
- FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
- FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
- rb_size|
- (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
- (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
-
- /* Set interrupt coalescing timer to default (2048 usecs) */
- iwl_write8(priv, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF);
-
- return 0;
-}
-
-static void iwlagn_set_pwr_vmain(struct iwl_priv *priv)
-{
-/*
- * (for documentation purposes)
- * to set power to V_AUX, do:
-
- if (pci_pme_capable(priv->pci_dev, PCI_D3cold))
- iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
- ~APMG_PS_CTRL_MSK_PWR_SRC);
- */
-
- iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
- ~APMG_PS_CTRL_MSK_PWR_SRC);
-}
-
-int iwlagn_hw_nic_init(struct iwl_priv *priv)
-{
- unsigned long flags;
- struct iwl_rx_queue *rxq = &priv->rxq;
- int ret;
-
- /* nic_init */
- spin_lock_irqsave(&priv->lock, flags);
- priv->cfg->ops->lib->apm_ops.init(priv);
-
- /* Set interrupt coalescing calibration timer to default (512 usecs) */
- iwl_write8(priv, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- iwlagn_set_pwr_vmain(priv);
-
- priv->cfg->ops->lib->apm_ops.config(priv);
-
- /* Allocate the RX queue, or reset if it is already allocated */
- if (!rxq->bd) {
- ret = iwl_rx_queue_alloc(priv);
- if (ret) {
- IWL_ERR(priv, "Unable to initialize Rx queue\n");
- return -ENOMEM;
- }
- } else
- iwlagn_rx_queue_reset(priv, rxq);
-
- iwlagn_rx_replenish(priv);
-
- iwlagn_rx_init(priv, rxq);
-
- spin_lock_irqsave(&priv->lock, flags);
-
- rxq->need_update = 1;
- iwl_rx_queue_update_write_ptr(priv, rxq);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- /* Allocate or reset and init all Tx and Command queues */
- if (!priv->txq) {
- ret = iwlagn_txq_ctx_alloc(priv);
- if (ret)
- return ret;
- } else
- iwlagn_txq_ctx_reset(priv);
-
- if (priv->cfg->base_params->shadow_reg_enable) {
- /* enable shadow regs in HW */
- iwl_set_bit(priv, CSR_MAC_SHADOW_REG_CTRL,
- 0x800FFFFF);
- }
-
- set_bit(STATUS_INIT, &priv->status);
-
- return 0;
-}
-
-/**
- * iwlagn_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr
- */
-static inline __le32 iwlagn_dma_addr2rbd_ptr(struct iwl_priv *priv,
- dma_addr_t dma_addr)
-{
- return cpu_to_le32((u32)(dma_addr >> 8));
-}
-
-/**
- * iwlagn_rx_queue_restock - refill RX queue from pre-allocated pool
- *
- * If there are slots in the RX queue that need to be restocked,
- * and we have free pre-allocated buffers, fill the ranks as much
- * as we can, pulling from rx_free.
- *
- * This moves the 'write' index forward to catch up with 'processed', and
- * also updates the memory address in the firmware to reference the new
- * target buffer.
- */
-void iwlagn_rx_queue_restock(struct iwl_priv *priv)
-{
- struct iwl_rx_queue *rxq = &priv->rxq;
- struct list_head *element;
- struct iwl_rx_mem_buffer *rxb;
- unsigned long flags;
-
- spin_lock_irqsave(&rxq->lock, flags);
- while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) {
- /* The overwritten rxb must be a used one */
- rxb = rxq->queue[rxq->write];
- BUG_ON(rxb && rxb->page);
-
- /* Get next free Rx buffer, remove from free list */
- element = rxq->rx_free.next;
- rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
- list_del(element);
-
- /* Point to Rx buffer via next RBD in circular buffer */
- rxq->bd[rxq->write] = iwlagn_dma_addr2rbd_ptr(priv,
- rxb->page_dma);
- rxq->queue[rxq->write] = rxb;
- rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
- rxq->free_count--;
- }
- spin_unlock_irqrestore(&rxq->lock, flags);
- /* If the pre-allocated buffer pool is dropping low, schedule to
- * refill it */
- if (rxq->free_count <= RX_LOW_WATERMARK)
- queue_work(priv->workqueue, &priv->rx_replenish);
-
-
- /* If we've added more space for the firmware to place data, tell it.
- * Increment device's write pointer in multiples of 8. */
- if (rxq->write_actual != (rxq->write & ~0x7)) {
- spin_lock_irqsave(&rxq->lock, flags);
- rxq->need_update = 1;
- spin_unlock_irqrestore(&rxq->lock, flags);
- iwl_rx_queue_update_write_ptr(priv, rxq);
- }
-}
-
-/**
- * iwlagn_rx_replenish - Move all used packet from rx_used to rx_free
- *
- * When moving to rx_free an SKB is allocated for the slot.
- *
- * Also restock the Rx queue via iwl_rx_queue_restock.
- * This is called as a scheduled work item (except for during initialization)
- */
-void iwlagn_rx_allocate(struct iwl_priv *priv, gfp_t priority)
-{
- struct iwl_rx_queue *rxq = &priv->rxq;
- struct list_head *element;
- struct iwl_rx_mem_buffer *rxb;
- struct page *page;
- unsigned long flags;
- gfp_t gfp_mask = priority;
-
- while (1) {
- spin_lock_irqsave(&rxq->lock, flags);
- if (list_empty(&rxq->rx_used)) {
- spin_unlock_irqrestore(&rxq->lock, flags);
- return;
- }
- spin_unlock_irqrestore(&rxq->lock, flags);
-
- if (rxq->free_count > RX_LOW_WATERMARK)
- gfp_mask |= __GFP_NOWARN;
-
- if (priv->hw_params.rx_page_order > 0)
- gfp_mask |= __GFP_COMP;
-
- /* Alloc a new receive buffer */
- page = alloc_pages(gfp_mask, priv->hw_params.rx_page_order);
- if (!page) {
- if (net_ratelimit())
- IWL_DEBUG_INFO(priv, "alloc_pages failed, "
- "order: %d\n",
- priv->hw_params.rx_page_order);
-
- if ((rxq->free_count <= RX_LOW_WATERMARK) &&
- net_ratelimit())
- IWL_CRIT(priv, "Failed to alloc_pages with %s. Only %u free buffers remaining.\n",
- priority == GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL",
- rxq->free_count);
- /* We don't reschedule replenish work here -- we will
- * call the restock method and if it still needs
- * more buffers it will schedule replenish */
- return;
- }
-
- spin_lock_irqsave(&rxq->lock, flags);
-
- if (list_empty(&rxq->rx_used)) {
- spin_unlock_irqrestore(&rxq->lock, flags);
- __free_pages(page, priv->hw_params.rx_page_order);
- return;
- }
- element = rxq->rx_used.next;
- rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
- list_del(element);
-
- spin_unlock_irqrestore(&rxq->lock, flags);
-
- BUG_ON(rxb->page);
- rxb->page = page;
- /* Get physical address of the RB */
- rxb->page_dma = pci_map_page(priv->pci_dev, page, 0,
- PAGE_SIZE << priv->hw_params.rx_page_order,
- PCI_DMA_FROMDEVICE);
- /* dma address must be no more than 36 bits */
- BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36));
- /* and also 256 byte aligned! */
- BUG_ON(rxb->page_dma & DMA_BIT_MASK(8));
-
- spin_lock_irqsave(&rxq->lock, flags);
-
- list_add_tail(&rxb->list, &rxq->rx_free);
- rxq->free_count++;
-
- spin_unlock_irqrestore(&rxq->lock, flags);
- }
-}
-
-void iwlagn_rx_replenish(struct iwl_priv *priv)
-{
- unsigned long flags;
-
- iwlagn_rx_allocate(priv, GFP_KERNEL);
-
- spin_lock_irqsave(&priv->lock, flags);
- iwlagn_rx_queue_restock(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-void iwlagn_rx_replenish_now(struct iwl_priv *priv)
-{
- iwlagn_rx_allocate(priv, GFP_ATOMIC);
-
- iwlagn_rx_queue_restock(priv);
-}
-
-/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
- * If an SKB has been detached, the POOL needs to have its SKB set to NULL
- * This free routine walks the list of POOL entries and if SKB is set to
- * non NULL it is unmapped and freed
- */
-void iwlagn_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
-{
- int i;
- for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
- if (rxq->pool[i].page != NULL) {
- pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma,
- PAGE_SIZE << priv->hw_params.rx_page_order,
- PCI_DMA_FROMDEVICE);
- __iwl_free_pages(priv, rxq->pool[i].page);
- rxq->pool[i].page = NULL;
- }
- }
-
- dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
- rxq->bd_dma);
- dma_free_coherent(&priv->pci_dev->dev, sizeof(struct iwl_rb_status),
- rxq->rb_stts, rxq->rb_stts_dma);
- rxq->bd = NULL;
- rxq->rb_stts = NULL;
-}
-
-int iwlagn_rxq_stop(struct iwl_priv *priv)
-{
-
- /* stop Rx DMA */
- iwl_write_direct32(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
- iwl_poll_direct_bit(priv, FH_MEM_RSSR_RX_STATUS_REG,
- FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000);
-
- return 0;
-}
-
int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band)
{
int idx = 0;
@@ -1004,451 +188,6 @@ int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band)
return -1;
}
-static int iwl_get_single_channel_for_scan(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- enum ieee80211_band band,
- struct iwl_scan_channel *scan_ch)
-{
- const struct ieee80211_supported_band *sband;
- u16 passive_dwell = 0;
- u16 active_dwell = 0;
- int added = 0;
- u16 channel = 0;
-
- sband = iwl_get_hw_mode(priv, band);
- if (!sband) {
- IWL_ERR(priv, "invalid band\n");
- return added;
- }
-
- active_dwell = iwl_get_active_dwell_time(priv, band, 0);
- passive_dwell = iwl_get_passive_dwell_time(priv, band, vif);
-
- if (passive_dwell <= active_dwell)
- passive_dwell = active_dwell + 1;
-
- channel = iwl_get_single_channel_number(priv, band);
- if (channel) {
- scan_ch->channel = cpu_to_le16(channel);
- scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
- scan_ch->active_dwell = cpu_to_le16(active_dwell);
- scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
- /* Set txpower levels to defaults */
- scan_ch->dsp_atten = 110;
- if (band == IEEE80211_BAND_5GHZ)
- scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
- else
- scan_ch->tx_gain = ((1 << 5) | (5 << 3));
- added++;
- } else
- IWL_ERR(priv, "no valid channel found\n");
- return added;
-}
-
-static int iwl_get_channels_for_scan(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- enum ieee80211_band band,
- u8 is_active, u8 n_probes,
- struct iwl_scan_channel *scan_ch)
-{
- struct ieee80211_channel *chan;
- const struct ieee80211_supported_band *sband;
- const struct iwl_channel_info *ch_info;
- u16 passive_dwell = 0;
- u16 active_dwell = 0;
- int added, i;
- u16 channel;
-
- sband = iwl_get_hw_mode(priv, band);
- if (!sband)
- return 0;
-
- active_dwell = iwl_get_active_dwell_time(priv, band, n_probes);
- passive_dwell = iwl_get_passive_dwell_time(priv, band, vif);
-
- if (passive_dwell <= active_dwell)
- passive_dwell = active_dwell + 1;
-
- for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) {
- chan = priv->scan_request->channels[i];
-
- if (chan->band != band)
- continue;
-
- channel = chan->hw_value;
- scan_ch->channel = cpu_to_le16(channel);
-
- ch_info = iwl_get_channel_info(priv, band, channel);
- if (!is_channel_valid(ch_info)) {
- IWL_DEBUG_SCAN(priv, "Channel %d is INVALID for this band.\n",
- channel);
- continue;
- }
-
- if (!is_active || is_channel_passive(ch_info) ||
- (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN))
- scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
- else
- scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;
-
- if (n_probes)
- scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes);
-
- scan_ch->active_dwell = cpu_to_le16(active_dwell);
- scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
-
- /* Set txpower levels to defaults */
- scan_ch->dsp_atten = 110;
-
- /* NOTE: if we were doing 6Mb OFDM for scans we'd use
- * power level:
- * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
- */
- if (band == IEEE80211_BAND_5GHZ)
- scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
- else
- scan_ch->tx_gain = ((1 << 5) | (5 << 3));
-
- IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n",
- channel, le32_to_cpu(scan_ch->type),
- (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ?
- "ACTIVE" : "PASSIVE",
- (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ?
- active_dwell : passive_dwell);
-
- scan_ch++;
- added++;
- }
-
- IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added);
- return added;
-}
-
-static int iwl_fill_offch_tx(struct iwl_priv *priv, void *data, size_t maxlen)
-{
- struct sk_buff *skb = priv->_agn.offchan_tx_skb;
-
- if (skb->len < maxlen)
- maxlen = skb->len;
-
- memcpy(data, skb->data, maxlen);
-
- return maxlen;
-}
-
-int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
-{
- struct iwl_host_cmd cmd = {
- .id = REPLY_SCAN_CMD,
- .len = { sizeof(struct iwl_scan_cmd), },
- };
- struct iwl_scan_cmd *scan;
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- u32 rate_flags = 0;
- u16 cmd_len;
- u16 rx_chain = 0;
- enum ieee80211_band band;
- u8 n_probes = 0;
- u8 rx_ant = priv->hw_params.valid_rx_ant;
- u8 rate;
- bool is_active = false;
- int chan_mod;
- u8 active_chains;
- u8 scan_tx_antennas = priv->hw_params.valid_tx_ant;
- int ret;
-
- lockdep_assert_held(&priv->mutex);
-
- if (vif)
- ctx = iwl_rxon_ctx_from_vif(vif);
-
- if (!priv->scan_cmd) {
- priv->scan_cmd = kmalloc(sizeof(struct iwl_scan_cmd) +
- IWL_MAX_SCAN_SIZE, GFP_KERNEL);
- if (!priv->scan_cmd) {
- IWL_DEBUG_SCAN(priv,
- "fail to allocate memory for scan\n");
- return -ENOMEM;
- }
- }
- scan = priv->scan_cmd;
- memset(scan, 0, sizeof(struct iwl_scan_cmd) + IWL_MAX_SCAN_SIZE);
-
- scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
- scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
-
- if (priv->scan_type != IWL_SCAN_OFFCH_TX &&
- iwl_is_any_associated(priv)) {
- u16 interval = 0;
- u32 extra;
- u32 suspend_time = 100;
- u32 scan_suspend_time = 100;
-
- IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
- switch (priv->scan_type) {
- case IWL_SCAN_OFFCH_TX:
- WARN_ON(1);
- break;
- case IWL_SCAN_RADIO_RESET:
- interval = 0;
- break;
- case IWL_SCAN_NORMAL:
- interval = vif->bss_conf.beacon_int;
- break;
- }
-
- scan->suspend_time = 0;
- scan->max_out_time = cpu_to_le32(200 * 1024);
- if (!interval)
- interval = suspend_time;
-
- extra = (suspend_time / interval) << 22;
- scan_suspend_time = (extra |
- ((suspend_time % interval) * 1024));
- scan->suspend_time = cpu_to_le32(scan_suspend_time);
- IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n",
- scan_suspend_time, interval);
- } else if (priv->scan_type == IWL_SCAN_OFFCH_TX) {
- scan->suspend_time = 0;
- scan->max_out_time =
- cpu_to_le32(1024 * priv->_agn.offchan_tx_timeout);
- }
-
- switch (priv->scan_type) {
- case IWL_SCAN_RADIO_RESET:
- IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
- break;
- case IWL_SCAN_NORMAL:
- if (priv->scan_request->n_ssids) {
- int i, p = 0;
- IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
- for (i = 0; i < priv->scan_request->n_ssids; i++) {
- /* always does wildcard anyway */
- if (!priv->scan_request->ssids[i].ssid_len)
- continue;
- scan->direct_scan[p].id = WLAN_EID_SSID;
- scan->direct_scan[p].len =
- priv->scan_request->ssids[i].ssid_len;
- memcpy(scan->direct_scan[p].ssid,
- priv->scan_request->ssids[i].ssid,
- priv->scan_request->ssids[i].ssid_len);
- n_probes++;
- p++;
- }
- is_active = true;
- } else
- IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
- break;
- case IWL_SCAN_OFFCH_TX:
- IWL_DEBUG_SCAN(priv, "Start offchannel TX scan.\n");
- break;
- }
-
- scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
- scan->tx_cmd.sta_id = ctx->bcast_sta_id;
- scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
-
- switch (priv->scan_band) {
- case IEEE80211_BAND_2GHZ:
- scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
- chan_mod = le32_to_cpu(
- priv->contexts[IWL_RXON_CTX_BSS].active.flags &
- RXON_FLG_CHANNEL_MODE_MSK)
- >> RXON_FLG_CHANNEL_MODE_POS;
- if (chan_mod == CHANNEL_MODE_PURE_40) {
- rate = IWL_RATE_6M_PLCP;
- } else {
- rate = IWL_RATE_1M_PLCP;
- rate_flags = RATE_MCS_CCK_MSK;
- }
- /*
- * Internal scans are passive, so we can indiscriminately set
- * the BT ignore flag on 2.4 GHz since it applies to TX only.
- */
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist)
- scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT;
- break;
- case IEEE80211_BAND_5GHZ:
- rate = IWL_RATE_6M_PLCP;
- break;
- default:
- IWL_WARN(priv, "Invalid scan band\n");
- return -EIO;
- }
-
- /*
- * If active scanning is requested but a certain channel is
- * marked passive, we can do active scanning if we detect
- * transmissions.
- *
- * There is an issue with some firmware versions that triggers
- * a sysassert on a "good CRC threshold" of zero (== disabled),
- * on a radar channel even though this means that we should NOT
- * send probes.
- *
- * The "good CRC threshold" is the number of frames that we
- * need to receive during our dwell time on a channel before
- * sending out probes -- setting this to a huge value will
- * mean we never reach it, but at the same time work around
- * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER
- * here instead of IWL_GOOD_CRC_TH_DISABLED.
- *
- * This was fixed in later versions along with some other
- * scan changes, and the threshold behaves as a flag in those
- * versions.
- */
- if (priv->new_scan_threshold_behaviour)
- scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
- IWL_GOOD_CRC_TH_DISABLED;
- else
- scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
- IWL_GOOD_CRC_TH_NEVER;
-
- band = priv->scan_band;
-
- if (priv->cfg->scan_rx_antennas[band])
- rx_ant = priv->cfg->scan_rx_antennas[band];
-
- if (band == IEEE80211_BAND_2GHZ &&
- priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist) {
- /* transmit 2.4 GHz probes only on first antenna */
- scan_tx_antennas = first_antenna(scan_tx_antennas);
- }
-
- priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, priv->scan_tx_ant[band],
- scan_tx_antennas);
- rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);
- scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags);
-
- /* In power save mode use one chain, otherwise use all chains */
- if (test_bit(STATUS_POWER_PMI, &priv->status)) {
- /* rx_ant has been set to all valid chains previously */
- active_chains = rx_ant &
- ((u8)(priv->chain_noise_data.active_chains));
- if (!active_chains)
- active_chains = rx_ant;
-
- IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n",
- priv->chain_noise_data.active_chains);
-
- rx_ant = first_antenna(active_chains);
- }
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist &&
- priv->bt_full_concurrent) {
- /* operated as 1x1 in full concurrency mode */
- rx_ant = first_antenna(rx_ant);
- }
-
- /* MIMO is not used here, but value is required */
- rx_chain |= priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
- rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
- rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
- rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
- scan->rx_chain = cpu_to_le16(rx_chain);
- switch (priv->scan_type) {
- case IWL_SCAN_NORMAL:
- cmd_len = iwl_fill_probe_req(priv,
- (struct ieee80211_mgmt *)scan->data,
- vif->addr,
- priv->scan_request->ie,
- priv->scan_request->ie_len,
- IWL_MAX_SCAN_SIZE - sizeof(*scan));
- break;
- case IWL_SCAN_RADIO_RESET:
- /* use bcast addr, will not be transmitted but must be valid */
- cmd_len = iwl_fill_probe_req(priv,
- (struct ieee80211_mgmt *)scan->data,
- iwl_bcast_addr, NULL, 0,
- IWL_MAX_SCAN_SIZE - sizeof(*scan));
- break;
- case IWL_SCAN_OFFCH_TX:
- cmd_len = iwl_fill_offch_tx(priv, scan->data,
- IWL_MAX_SCAN_SIZE
- - sizeof(*scan)
- - sizeof(struct iwl_scan_channel));
- scan->scan_flags |= IWL_SCAN_FLAGS_ACTION_FRAME_TX;
- break;
- default:
- BUG();
- }
- scan->tx_cmd.len = cpu_to_le16(cmd_len);
-
- scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
- RXON_FILTER_BCON_AWARE_MSK);
-
- switch (priv->scan_type) {
- case IWL_SCAN_RADIO_RESET:
- scan->channel_count =
- iwl_get_single_channel_for_scan(priv, vif, band,
- (void *)&scan->data[cmd_len]);
- break;
- case IWL_SCAN_NORMAL:
- scan->channel_count =
- iwl_get_channels_for_scan(priv, vif, band,
- is_active, n_probes,
- (void *)&scan->data[cmd_len]);
- break;
- case IWL_SCAN_OFFCH_TX: {
- struct iwl_scan_channel *scan_ch;
-
- scan->channel_count = 1;
-
- scan_ch = (void *)&scan->data[cmd_len];
- scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;
- scan_ch->channel =
- cpu_to_le16(priv->_agn.offchan_tx_chan->hw_value);
- scan_ch->active_dwell =
- cpu_to_le16(priv->_agn.offchan_tx_timeout);
- scan_ch->passive_dwell = 0;
-
- /* Set txpower levels to defaults */
- scan_ch->dsp_atten = 110;
-
- /* NOTE: if we were doing 6Mb OFDM for scans we'd use
- * power level:
- * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
- */
- if (priv->_agn.offchan_tx_chan->band == IEEE80211_BAND_5GHZ)
- scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
- else
- scan_ch->tx_gain = ((1 << 5) | (5 << 3));
- }
- break;
- }
-
- if (scan->channel_count == 0) {
- IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
- return -EIO;
- }
-
- cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) +
- scan->channel_count * sizeof(struct iwl_scan_channel);
- cmd.data[0] = scan;
- cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
- scan->len = cpu_to_le16(cmd.len[0]);
-
- /* set scan bit here for PAN params */
- set_bit(STATUS_SCAN_HW, &priv->status);
-
- if (priv->cfg->ops->hcmd->set_pan_params) {
- ret = priv->cfg->ops->hcmd->set_pan_params(priv);
- if (ret)
- return ret;
- }
-
- ret = iwl_send_cmd_sync(priv, &cmd);
- if (ret) {
- clear_bit(STATUS_SCAN_HW, &priv->status);
- if (priv->cfg->ops->hcmd->set_pan_params)
- priv->cfg->ops->hcmd->set_pan_params(priv);
- }
-
- return ret;
-}
-
int iwlagn_manage_ibss_station(struct iwl_priv *priv,
struct ieee80211_vif *vif, bool add)
{
@@ -1462,52 +201,6 @@ int iwlagn_manage_ibss_station(struct iwl_priv *priv,
vif->bss_conf.bssid);
}
-void iwl_free_tfds_in_queue(struct iwl_priv *priv,
- int sta_id, int tid, int freed)
-{
- lockdep_assert_held(&priv->sta_lock);
-
- if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed)
- priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
- else {
- IWL_DEBUG_TX(priv, "free more than tfds_in_queue (%u:%d)\n",
- priv->stations[sta_id].tid[tid].tfds_in_queue,
- freed);
- priv->stations[sta_id].tid[tid].tfds_in_queue = 0;
- }
-}
-
-#define IWL_FLUSH_WAIT_MS 2000
-
-int iwlagn_wait_tx_queue_empty(struct iwl_priv *priv)
-{
- struct iwl_tx_queue *txq;
- struct iwl_queue *q;
- int cnt;
- unsigned long now = jiffies;
- int ret = 0;
-
- /* waiting for all the tx frames complete might take a while */
- for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
- if (cnt == priv->cmd_queue)
- continue;
- txq = &priv->txq[cnt];
- q = &txq->q;
- while (q->read_ptr != q->write_ptr && !time_after(jiffies,
- now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS)))
- msleep(1);
-
- if (q->read_ptr != q->write_ptr) {
- IWL_ERR(priv, "fail to flush all tx fifo queues\n");
- ret = -ETIMEDOUT;
- break;
- }
- }
- return ret;
-}
-
-#define IWL_TX_QUEUE_MSK 0xfffff
-
/**
* iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode
*
@@ -1528,31 +221,40 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control)
might_sleep();
memset(&flush_cmd, 0, sizeof(flush_cmd));
- flush_cmd.fifo_control = IWL_TX_FIFO_VO_MSK | IWL_TX_FIFO_VI_MSK |
- IWL_TX_FIFO_BE_MSK | IWL_TX_FIFO_BK_MSK;
- if (priv->cfg->sku & IWL_SKU_N)
+ if (flush_control & BIT(IWL_RXON_CTX_BSS))
+ flush_cmd.fifo_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK |
+ IWL_SCD_BE_MSK | IWL_SCD_BK_MSK |
+ IWL_SCD_MGMT_MSK;
+ if ((flush_control & BIT(IWL_RXON_CTX_PAN)) &&
+ (priv->shrd->valid_contexts != BIT(IWL_RXON_CTX_BSS)))
+ flush_cmd.fifo_control |= IWL_PAN_SCD_VO_MSK |
+ IWL_PAN_SCD_VI_MSK | IWL_PAN_SCD_BE_MSK |
+ IWL_PAN_SCD_BK_MSK | IWL_PAN_SCD_MGMT_MSK |
+ IWL_PAN_SCD_MULTICAST_MSK;
+
+ if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE)
flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK;
IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n",
flush_cmd.fifo_control);
flush_cmd.flush_control = cpu_to_le16(flush_control);
- return iwl_send_cmd(priv, &cmd);
+ return iwl_trans_send_cmd(trans(priv), &cmd);
}
void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control)
{
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
ieee80211_stop_queues(priv->hw);
- if (priv->cfg->ops->lib->txfifo_flush(priv, IWL_DROP_ALL)) {
+ if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) {
IWL_ERR(priv, "flush request fail\n");
goto done;
}
IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n");
- iwlagn_wait_tx_queue_empty(priv);
+ iwl_trans_wait_tx_queue_empty(trans(priv));
done:
ieee80211_wake_queues(priv->hw);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
/*
@@ -1699,18 +401,21 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
* (might be in monitor mode), or the interface is in
* IBSS mode (no proper uCode support for coex then).
*/
- if (!bt_coex_active || priv->iw_mode == NL80211_IFTYPE_ADHOC) {
+ if (!iwlagn_mod_params.bt_coex_active ||
+ priv->iw_mode == NL80211_IFTYPE_ADHOC) {
basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED;
} else {
basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W <<
IWLAGN_BT_FLAG_COEX_MODE_SHIFT;
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->bt_sco_disable)
+
+ if (!priv->bt_enable_pspoll)
basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE;
+ else
+ basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE;
if (priv->bt_ch_announce)
basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION;
- IWL_DEBUG_INFO(priv, "BT coex flag: 0X%x\n", basic.flags);
+ IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags);
}
priv->bt_enable_flag = basic.flags;
if (priv->bt_full_concurrent)
@@ -1720,7 +425,7 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup,
sizeof(iwlagn_def_3w_lookup));
- IWL_DEBUG_INFO(priv, "BT coex %s in %s mode\n",
+ IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n",
basic.flags ? "active" : "disabled",
priv->bt_full_concurrent ?
"full concurrency" : "3-wire");
@@ -1728,19 +433,97 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
if (priv->cfg->bt_params->bt_session_2) {
memcpy(&bt_cmd_2000.basic, &basic,
sizeof(basic));
- ret = iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG,
- sizeof(bt_cmd_2000), &bt_cmd_2000);
+ ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_BT_CONFIG,
+ CMD_SYNC, sizeof(bt_cmd_2000), &bt_cmd_2000);
} else {
memcpy(&bt_cmd_6000.basic, &basic,
sizeof(basic));
- ret = iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG,
- sizeof(bt_cmd_6000), &bt_cmd_6000);
+ ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_BT_CONFIG,
+ CMD_SYNC, sizeof(bt_cmd_6000), &bt_cmd_6000);
}
if (ret)
IWL_ERR(priv, "failed to send BT Coex Config\n");
}
+void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena)
+{
+ struct iwl_rxon_context *ctx, *found_ctx = NULL;
+ bool found_ap = false;
+
+ lockdep_assert_held(&priv->shrd->mutex);
+
+ /* Check whether AP or GO mode is active. */
+ if (rssi_ena) {
+ for_each_context(priv, ctx) {
+ if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP &&
+ iwl_is_associated_ctx(ctx)) {
+ found_ap = true;
+ break;
+ }
+ }
+ }
+
+ /*
+ * If disable was received or If GO/AP mode, disable RSSI
+ * measurements.
+ */
+ if (!rssi_ena || found_ap) {
+ if (priv->cur_rssi_ctx) {
+ ctx = priv->cur_rssi_ctx;
+ ieee80211_disable_rssi_reports(ctx->vif);
+ priv->cur_rssi_ctx = NULL;
+ }
+ return;
+ }
+
+ /*
+ * If rssi measurements need to be enabled, consider all cases now.
+ * Figure out how many contexts are active.
+ */
+ for_each_context(priv, ctx) {
+ if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION &&
+ iwl_is_associated_ctx(ctx)) {
+ found_ctx = ctx;
+ break;
+ }
+ }
+
+ /*
+ * rssi monitor already enabled for the correct interface...nothing
+ * to do.
+ */
+ if (found_ctx == priv->cur_rssi_ctx)
+ return;
+
+ /*
+ * Figure out if rssi monitor is currently enabled, and needs
+ * to be changed. If rssi monitor is already enabled, disable
+ * it first else just enable rssi measurements on the
+ * interface found above.
+ */
+ if (priv->cur_rssi_ctx) {
+ ctx = priv->cur_rssi_ctx;
+ if (ctx->vif)
+ ieee80211_disable_rssi_reports(ctx->vif);
+ }
+
+ priv->cur_rssi_ctx = found_ctx;
+
+ if (!found_ctx)
+ return;
+
+ ieee80211_enable_rssi_reports(found_ctx->vif,
+ IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD,
+ IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD);
+}
+
+static bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg)
+{
+ return BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3 >>
+ BT_UART_MSG_FRAME3SCOESCO_POS;
+}
+
static void iwlagn_bt_traffic_change_work(struct work_struct *work)
{
struct iwl_priv *priv =
@@ -1758,7 +541,7 @@ static void iwlagn_bt_traffic_change_work(struct work_struct *work)
* coex profile notifications. Ignore that since only bad consequence
* can be not matching debug print with actual state.
*/
- IWL_DEBUG_INFO(priv, "BT traffic load changes: %d\n",
+ IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n",
priv->bt_traffic_load);
switch (priv->bt_traffic_load) {
@@ -1781,7 +564,7 @@ static void iwlagn_bt_traffic_change_work(struct work_struct *work)
break;
}
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
/*
* We can not send command to firmware while scanning. When the scan
@@ -1790,26 +573,46 @@ static void iwlagn_bt_traffic_change_work(struct work_struct *work)
* STATUS_SCANNING to avoid race when queue_work two times from
* different notifications, but quit and not perform any work at all.
*/
- if (test_bit(STATUS_SCAN_HW, &priv->status))
+ if (test_bit(STATUS_SCAN_HW, &priv->shrd->status))
goto out;
- if (priv->cfg->ops->lib->update_chain_flags)
- priv->cfg->ops->lib->update_chain_flags(priv);
+ iwl_update_chain_flags(priv);
if (smps_request != -1) {
+ priv->current_ht_config.smps = smps_request;
for_each_context(priv, ctx) {
if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION)
ieee80211_request_smps(ctx->vif, smps_request);
}
}
+
+ /*
+ * Dynamic PS poll related functionality. Adjust RSSI measurements if
+ * necessary.
+ */
+ iwlagn_bt_coex_rssi_monitor(priv);
out:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
+}
+
+/*
+ * If BT sco traffic, and RSSI monitor is enabled, move measurements to the
+ * correct interface or disable it if this is the last interface to be
+ * removed.
+ */
+void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv)
+{
+ if (priv->bt_is_sco &&
+ priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS)
+ iwlagn_bt_adjust_rssi_monitor(priv, true);
+ else
+ iwlagn_bt_adjust_rssi_monitor(priv, false);
}
static void iwlagn_print_uartmsg(struct iwl_priv *priv,
struct iwl_bt_uart_msg *uart_msg)
{
- IWL_DEBUG_NOTIF(priv, "Message Type = 0x%X, SSN = 0x%X, "
+ IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, "
"Update Req = 0x%X",
(BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >>
BT_UART_MSG_FRAME1MSGTYPE_POS,
@@ -1818,7 +621,7 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv,
(BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >>
BT_UART_MSG_FRAME1UPDATEREQ_POS);
- IWL_DEBUG_NOTIF(priv, "Open connections = 0x%X, Traffic load = 0x%X, "
+ IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, "
"Chl_SeqN = 0x%X, In band = 0x%X",
(BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >>
BT_UART_MSG_FRAME2OPENCONNECTIONS_POS,
@@ -1829,7 +632,7 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv,
(BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >>
BT_UART_MSG_FRAME2INBAND_POS);
- IWL_DEBUG_NOTIF(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, "
+ IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, "
"ACL = 0x%X, Master = 0x%X, OBEX = 0x%X",
(BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >>
BT_UART_MSG_FRAME3SCOESCO_POS,
@@ -1844,11 +647,11 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv,
(BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >>
BT_UART_MSG_FRAME3OBEX_POS);
- IWL_DEBUG_NOTIF(priv, "Idle duration = 0x%X",
+ IWL_DEBUG_COEX(priv, "Idle duration = 0x%X",
(BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >>
BT_UART_MSG_FRAME4IDLEDURATION_POS);
- IWL_DEBUG_NOTIF(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, "
+ IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, "
"eSCO Retransmissions = 0x%X",
(BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >>
BT_UART_MSG_FRAME5TXACTIVITY_POS,
@@ -1857,13 +660,13 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv,
(BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >>
BT_UART_MSG_FRAME5ESCORETRANSMIT_POS);
- IWL_DEBUG_NOTIF(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X",
+ IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X",
(BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >>
BT_UART_MSG_FRAME6SNIFFINTERVAL_POS,
(BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >>
BT_UART_MSG_FRAME6DISCOVERABLE_POS);
- IWL_DEBUG_NOTIF(priv, "Sniff Activity = 0x%X, Page = "
+ IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = "
"0x%X, Inquiry = 0x%X, Connectable = 0x%X",
(BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >>
BT_UART_MSG_FRAME7SNIFFACTIVITY_POS,
@@ -1896,12 +699,13 @@ static void iwlagn_set_kill_msk(struct iwl_priv *priv,
priv->kill_cts_mask = bt_kill_cts_msg[kill_msk];
/* schedule to send runtime bt_config */
- queue_work(priv->workqueue, &priv->bt_runtime_config);
+ queue_work(priv->shrd->workqueue, &priv->bt_runtime_config);
}
}
-void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
unsigned long flags;
struct iwl_rx_packet *pkt = rxb_addr(rxb);
@@ -1910,17 +714,19 @@ void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) {
/* bt coex disabled */
- return;
+ return 0;
}
- IWL_DEBUG_NOTIF(priv, "BT Coex notification:\n");
- IWL_DEBUG_NOTIF(priv, " status: %d\n", coex->bt_status);
- IWL_DEBUG_NOTIF(priv, " traffic load: %d\n", coex->bt_traffic_load);
- IWL_DEBUG_NOTIF(priv, " CI compliance: %d\n",
+ IWL_DEBUG_COEX(priv, "BT Coex notification:\n");
+ IWL_DEBUG_COEX(priv, " status: %d\n", coex->bt_status);
+ IWL_DEBUG_COEX(priv, " traffic load: %d\n", coex->bt_traffic_load);
+ IWL_DEBUG_COEX(priv, " CI compliance: %d\n",
coex->bt_ci_compliance);
iwlagn_print_uartmsg(priv, uart_msg);
priv->last_bt_traffic_load = priv->bt_traffic_load;
+ priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg);
+
if (priv->iw_mode != NL80211_IFTYPE_ADHOC) {
if (priv->bt_status != coex->bt_status ||
priv->last_bt_traffic_load != coex->bt_traffic_load) {
@@ -1938,7 +744,7 @@ void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
IWL_BT_COEX_TRAFFIC_LOAD_NONE;
}
priv->bt_status = coex->bt_status;
- queue_work(priv->workqueue,
+ queue_work(priv->shrd->workqueue,
&priv->bt_traffic_change_work);
}
}
@@ -1947,22 +753,20 @@ void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
/* FIXME: based on notification, adjust the prio_boost */
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&priv->shrd->lock, flags);
priv->bt_ci_compliance = coex->bt_ci_compliance;
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
+ return 0;
}
void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv)
{
- iwlagn_rx_handler_setup(priv);
priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] =
iwlagn_bt_coex_profile_notif;
}
void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv)
{
- iwlagn_setup_deferred_work(priv);
-
INIT_WORK(&priv->bt_traffic_change_work,
iwlagn_bt_traffic_change_work);
}
@@ -2052,7 +856,7 @@ static u8 iwl_count_chain_bitmap(u32 chain_bitmap)
void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
{
bool is_single = is_single_rx_stream(priv);
- bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
+ bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->shrd->status);
u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
u32 active_chains;
u16 rx_chain;
@@ -2064,7 +868,7 @@ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
if (priv->chain_noise_data.active_chains)
active_chains = priv->chain_noise_data.active_chains;
else
- active_chains = priv->hw_params.valid_rx_ant;
+ active_chains = hw_params(priv).valid_rx_ant;
if (priv->cfg->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist &&
@@ -2129,136 +933,6 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid)
return ant;
}
-static const char *get_csr_string(int cmd)
-{
- switch (cmd) {
- IWL_CMD(CSR_HW_IF_CONFIG_REG);
- IWL_CMD(CSR_INT_COALESCING);
- IWL_CMD(CSR_INT);
- IWL_CMD(CSR_INT_MASK);
- IWL_CMD(CSR_FH_INT_STATUS);
- IWL_CMD(CSR_GPIO_IN);
- IWL_CMD(CSR_RESET);
- IWL_CMD(CSR_GP_CNTRL);
- IWL_CMD(CSR_HW_REV);
- IWL_CMD(CSR_EEPROM_REG);
- IWL_CMD(CSR_EEPROM_GP);
- IWL_CMD(CSR_OTP_GP_REG);
- IWL_CMD(CSR_GIO_REG);
- IWL_CMD(CSR_GP_UCODE_REG);
- IWL_CMD(CSR_GP_DRIVER_REG);
- IWL_CMD(CSR_UCODE_DRV_GP1);
- IWL_CMD(CSR_UCODE_DRV_GP2);
- IWL_CMD(CSR_LED_REG);
- IWL_CMD(CSR_DRAM_INT_TBL_REG);
- IWL_CMD(CSR_GIO_CHICKEN_BITS);
- IWL_CMD(CSR_ANA_PLL_CFG);
- IWL_CMD(CSR_HW_REV_WA_REG);
- IWL_CMD(CSR_DBG_HPET_MEM_REG);
- default:
- return "UNKNOWN";
- }
-}
-
-void iwl_dump_csr(struct iwl_priv *priv)
-{
- int i;
- static const u32 csr_tbl[] = {
- CSR_HW_IF_CONFIG_REG,
- CSR_INT_COALESCING,
- CSR_INT,
- CSR_INT_MASK,
- CSR_FH_INT_STATUS,
- CSR_GPIO_IN,
- CSR_RESET,
- CSR_GP_CNTRL,
- CSR_HW_REV,
- CSR_EEPROM_REG,
- CSR_EEPROM_GP,
- CSR_OTP_GP_REG,
- CSR_GIO_REG,
- CSR_GP_UCODE_REG,
- CSR_GP_DRIVER_REG,
- CSR_UCODE_DRV_GP1,
- CSR_UCODE_DRV_GP2,
- CSR_LED_REG,
- CSR_DRAM_INT_TBL_REG,
- CSR_GIO_CHICKEN_BITS,
- CSR_ANA_PLL_CFG,
- CSR_HW_REV_WA_REG,
- CSR_DBG_HPET_MEM_REG
- };
- IWL_ERR(priv, "CSR values:\n");
- IWL_ERR(priv, "(2nd byte of CSR_INT_COALESCING is "
- "CSR_INT_PERIODIC_REG)\n");
- for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) {
- IWL_ERR(priv, " %25s: 0X%08x\n",
- get_csr_string(csr_tbl[i]),
- iwl_read32(priv, csr_tbl[i]));
- }
-}
-
-static const char *get_fh_string(int cmd)
-{
- switch (cmd) {
- IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
- IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
- IWL_CMD(FH_RSCSR_CHNL0_WPTR);
- IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
- IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
- IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
- IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
- IWL_CMD(FH_TSSR_TX_STATUS_REG);
- IWL_CMD(FH_TSSR_TX_ERROR_REG);
- default:
- return "UNKNOWN";
- }
-}
-
-int iwl_dump_fh(struct iwl_priv *priv, char **buf, bool display)
-{
- int i;
-#ifdef CONFIG_IWLWIFI_DEBUG
- int pos = 0;
- size_t bufsz = 0;
-#endif
- static const u32 fh_tbl[] = {
- FH_RSCSR_CHNL0_STTS_WPTR_REG,
- FH_RSCSR_CHNL0_RBDCB_BASE_REG,
- FH_RSCSR_CHNL0_WPTR,
- FH_MEM_RCSR_CHNL0_CONFIG_REG,
- FH_MEM_RSSR_SHARED_CTRL_REG,
- FH_MEM_RSSR_RX_STATUS_REG,
- FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
- FH_TSSR_TX_STATUS_REG,
- FH_TSSR_TX_ERROR_REG
- };
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (display) {
- bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
- *buf = kmalloc(bufsz, GFP_KERNEL);
- if (!*buf)
- return -ENOMEM;
- pos += scnprintf(*buf + pos, bufsz - pos,
- "FH register values:\n");
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- " %34s: 0X%08x\n",
- get_fh_string(fh_tbl[i]),
- iwl_read_direct32(priv, fh_tbl[i]));
- }
- return pos;
- }
-#endif
- IWL_ERR(priv, "FH register values:\n");
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
- IWL_ERR(priv, " %34s: 0X%08x\n",
- get_fh_string(fh_tbl[i]),
- iwl_read_direct32(priv, fh_tbl[i]));
- }
- return 0;
-}
-
/* notification wait support */
void iwlagn_init_notification_wait(struct iwl_priv *priv,
struct iwl_notification_wait *wait_entry,
@@ -2274,9 +948,9 @@ void iwlagn_init_notification_wait(struct iwl_priv *priv,
wait_entry->triggered = false;
wait_entry->aborted = false;
- spin_lock_bh(&priv->_agn.notif_wait_lock);
- list_add(&wait_entry->list, &priv->_agn.notif_waits);
- spin_unlock_bh(&priv->_agn.notif_wait_lock);
+ spin_lock_bh(&priv->notif_wait_lock);
+ list_add(&wait_entry->list, &priv->notif_waits);
+ spin_unlock_bh(&priv->notif_wait_lock);
}
int iwlagn_wait_notification(struct iwl_priv *priv,
@@ -2285,13 +959,13 @@ int iwlagn_wait_notification(struct iwl_priv *priv,
{
int ret;
- ret = wait_event_timeout(priv->_agn.notif_waitq,
+ ret = wait_event_timeout(priv->notif_waitq,
wait_entry->triggered || wait_entry->aborted,
timeout);
- spin_lock_bh(&priv->_agn.notif_wait_lock);
+ spin_lock_bh(&priv->notif_wait_lock);
list_del(&wait_entry->list);
- spin_unlock_bh(&priv->_agn.notif_wait_lock);
+ spin_unlock_bh(&priv->notif_wait_lock);
if (wait_entry->aborted)
return -EIO;
@@ -2305,91 +979,7 @@ int iwlagn_wait_notification(struct iwl_priv *priv,
void iwlagn_remove_notification(struct iwl_priv *priv,
struct iwl_notification_wait *wait_entry)
{
- spin_lock_bh(&priv->_agn.notif_wait_lock);
+ spin_lock_bh(&priv->notif_wait_lock);
list_del(&wait_entry->list);
- spin_unlock_bh(&priv->_agn.notif_wait_lock);
-}
-
-int iwlagn_start_device(struct iwl_priv *priv)
-{
- int ret;
-
- if (iwl_prepare_card_hw(priv)) {
- IWL_WARN(priv, "Exit HW not ready\n");
- return -EIO;
- }
-
- /* If platform's RF_KILL switch is NOT set to KILL */
- if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
- clear_bit(STATUS_RF_KILL_HW, &priv->status);
- else
- set_bit(STATUS_RF_KILL_HW, &priv->status);
-
- if (iwl_is_rfkill(priv)) {
- wiphy_rfkill_set_hw_state(priv->hw->wiphy, true);
- iwl_enable_interrupts(priv);
- return -ERFKILL;
- }
-
- iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
-
- ret = iwlagn_hw_nic_init(priv);
- if (ret) {
- IWL_ERR(priv, "Unable to init nic\n");
- return ret;
- }
-
- /* make sure rfkill handshake bits are cleared */
- iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
- iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
- CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
-
- /* clear (again), then enable host interrupts */
- iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
- iwl_enable_interrupts(priv);
-
- /* really make sure rfkill handshake bits are cleared */
- iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
- iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
-
- return 0;
-}
-
-void iwlagn_stop_device(struct iwl_priv *priv)
-{
- unsigned long flags;
-
- /* stop and reset the on-board processor */
- iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
-
- /* tell the device to stop sending interrupts */
- spin_lock_irqsave(&priv->lock, flags);
- iwl_disable_interrupts(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
- iwl_synchronize_irq(priv);
-
- /* device going down, Stop using ICT table */
- iwl_disable_ict(priv);
-
- /*
- * If a HW restart happens during firmware loading,
- * then the firmware loading might call this function
- * and later it might be called again due to the
- * restart. So don't process again if the device is
- * already dead.
- */
- if (test_bit(STATUS_DEVICE_ENABLED, &priv->status)) {
- iwlagn_txq_ctx_stop(priv);
- iwlagn_rxq_stop(priv);
-
- /* Power-down device's busmaster DMA clocks */
- iwl_write_prph(priv, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT);
- udelay(5);
- }
-
- /* Make sure (redundant) we've released our request to stay awake */
- iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-
- /* Stop the device, and put it in low power state */
- iwl_apm_stop(priv);
+ spin_unlock_bh(&priv->notif_wait_lock);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
index 592b0cfcf717..66118cea2af3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
@@ -27,7 +27,6 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/netdevice.h>
@@ -37,7 +36,6 @@
#include <linux/workqueue.h>
#include "iwl-dev.h"
-#include "iwl-sta.h"
#include "iwl-core.h"
#include "iwl-agn.h"
@@ -298,10 +296,10 @@ static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data,
u8 *qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & 0xf;
} else
- return MAX_TID_COUNT;
+ return IWL_MAX_TID_COUNT;
if (unlikely(tid >= TID_MAX_LOAD_COUNT))
- return MAX_TID_COUNT;
+ return IWL_MAX_TID_COUNT;
tl = &lq_data->load[tid];
@@ -314,7 +312,7 @@ static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data,
tl->queue_count = 1;
tl->head = 0;
tl->packet_count[0] = 1;
- return MAX_TID_COUNT;
+ return IWL_MAX_TID_COUNT;
}
time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
@@ -336,25 +334,35 @@ static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data,
}
#ifdef CONFIG_MAC80211_DEBUGFS
+/**
+ * Program the device to use fixed rate for frame transmit
+ * This is for debugging/testing only
+ * once the device start use fixed rate, we need to reload the module
+ * to being back the normal operation.
+ */
static void rs_program_fix_rate(struct iwl_priv *priv,
struct iwl_lq_sta *lq_sta)
{
struct iwl_station_priv *sta_priv =
container_of(lq_sta, struct iwl_station_priv, lq_sta);
- struct iwl_rxon_context *ctx = sta_priv->common.ctx;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */
lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
- lq_sta->dbg_fixed_rate = priv->dbg_fixed_rate;
+#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL
+ /* testmode has higher priority to overwirte the fixed rate */
+ if (priv->tm_fixed_rate)
+ lq_sta->dbg_fixed_rate = priv->tm_fixed_rate;
+#endif
IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n",
- lq_sta->lq.sta_id, priv->dbg_fixed_rate);
+ lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
- if (priv->dbg_fixed_rate) {
- rs_fill_link_cmd(NULL, lq_sta, priv->dbg_fixed_rate);
+ if (lq_sta->dbg_fixed_rate) {
+ rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate);
iwl_send_lq_cmd(lq_sta->drv, ctx, &lq_sta->lq, CMD_ASYNC,
false);
}
@@ -411,7 +419,7 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
load = rs_tl_get_load(lq_data, tid);
- if (load > IWL_AGG_LOAD_THRESHOLD) {
+ if ((iwlagn_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) {
IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n",
sta->addr, tid);
ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
@@ -426,7 +434,7 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
ieee80211_stop_tx_ba_session(sta, tid);
}
} else {
- IWL_ERR(priv, "Aggregation not enabled for tid %d "
+ IWL_DEBUG_HT(priv, "Aggregation not enabled for tid %d "
"because load = %u\n", tid, load);
}
return ret;
@@ -701,7 +709,7 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
static bool rs_use_green(struct ieee80211_sta *sta)
{
struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->common.ctx;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
!(ctx->ht.non_gf_sta_present);
@@ -810,7 +818,7 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
if (num_of_ant(tbl->ant_type) > 1)
tbl->ant_type =
- first_antenna(priv->hw_params.valid_tx_ant);
+ first_antenna(hw_params(priv).valid_tx_ant);
tbl->is_ht40 = 0;
tbl->is_SGI = 0;
@@ -868,12 +876,12 @@ static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
* Is there a need to switch between
* full concurrency and 3-wire?
*/
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&priv->shrd->lock, flags);
if (priv->bt_ci_compliance && priv->bt_ant_couple_ok)
full_concurrent = true;
else
full_concurrent = false;
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
}
if ((priv->bt_traffic_load != priv->last_bt_traffic_load) ||
(priv->bt_full_concurrent != full_concurrent)) {
@@ -884,7 +892,7 @@ static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
rs_fill_link_cmd(priv, lq_sta, tbl->current_rate);
iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false);
- queue_work(priv->workqueue, &priv->bt_full_concurrency);
+ queue_work(priv->shrd->workqueue, &priv->bt_full_concurrency);
}
}
@@ -908,7 +916,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
struct iwl_scale_tbl_info tbl_type;
struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->common.ctx;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n");
@@ -1072,8 +1080,10 @@ done:
/* See if there's a better rate or modulation mode to try. */
if (sta && sta->supp_rates[sband->band])
rs_rate_scale_perform(priv, skb, sta, lq_sta);
-#ifdef CONFIG_MAC80211_DEBUGFS
- if (priv->dbg_fixed_rate != lq_sta->dbg_fixed_rate)
+
+#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_IWLWIFI_DEVICE_SVTOOL)
+ if ((priv->tm_fixed_rate) &&
+ (priv->tm_fixed_rate != lq_sta->dbg_fixed_rate))
rs_program_fix_rate(priv, lq_sta);
#endif
if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist)
@@ -1272,7 +1282,7 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv,
s32 rate;
s8 is_green = lq_sta->is_green;
struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->common.ctx;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported)
return -1;
@@ -1282,7 +1292,7 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv,
return -1;
/* Need both Tx chains/antennas to support MIMO */
- if (priv->hw_params.tx_chains_num < 2)
+ if (hw_params(priv).tx_chains_num < 2)
return -1;
IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO2\n");
@@ -1328,7 +1338,7 @@ static int rs_switch_to_mimo3(struct iwl_priv *priv,
s32 rate;
s8 is_green = lq_sta->is_green;
struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->common.ctx;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported)
return -1;
@@ -1338,7 +1348,7 @@ static int rs_switch_to_mimo3(struct iwl_priv *priv,
return -1;
/* Need both Tx chains/antennas to support MIMO */
- if (priv->hw_params.tx_chains_num < 3)
+ if (hw_params(priv).tx_chains_num < 3)
return -1;
IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO3\n");
@@ -1385,7 +1395,7 @@ static int rs_switch_to_siso(struct iwl_priv *priv,
u8 is_green = lq_sta->is_green;
s32 rate;
struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->common.ctx;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported)
return -1;
@@ -1437,8 +1447,8 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action;
- u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
- u8 tx_chains_num = priv->hw_params.tx_chains_num;
+ u8 valid_tx_ant = hw_params(priv).valid_tx_ant;
+ u8 tx_chains_num = hw_params(priv).tx_chains_num;
int ret = 0;
u8 update_search_tbl_counter = 0;
@@ -1448,14 +1458,16 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
break;
case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
/* avoid antenna B unless MIMO */
- valid_tx_ant = first_antenna(priv->hw_params.valid_tx_ant);
+ valid_tx_ant =
+ first_antenna(hw_params(priv).valid_tx_ant);
if (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2)
tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
break;
case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
/* avoid antenna B and MIMO */
- valid_tx_ant = first_antenna(priv->hw_params.valid_tx_ant);
+ valid_tx_ant =
+ first_antenna(hw_params(priv).valid_tx_ant);
if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2 &&
tbl->action != IWL_LEGACY_SWITCH_SISO)
tbl->action = IWL_LEGACY_SWITCH_SISO;
@@ -1478,7 +1490,8 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2)
tbl->action = IWL_LEGACY_SWITCH_SISO;
- valid_tx_ant = first_antenna(priv->hw_params.valid_tx_ant);
+ valid_tx_ant =
+ first_antenna(hw_params(priv).valid_tx_ant);
}
start_action = tbl->action;
@@ -1612,8 +1625,8 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action;
- u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
- u8 tx_chains_num = priv->hw_params.tx_chains_num;
+ u8 valid_tx_ant = hw_params(priv).valid_tx_ant;
+ u8 tx_chains_num = hw_params(priv).tx_chains_num;
u8 update_search_tbl_counter = 0;
int ret;
@@ -1623,14 +1636,16 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
break;
case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
/* avoid antenna B unless MIMO */
- valid_tx_ant = first_antenna(priv->hw_params.valid_tx_ant);
+ valid_tx_ant =
+ first_antenna(hw_params(priv).valid_tx_ant);
if (tbl->action == IWL_SISO_SWITCH_ANTENNA2)
tbl->action = IWL_SISO_SWITCH_ANTENNA1;
break;
case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
/* avoid antenna B and MIMO */
- valid_tx_ant = first_antenna(priv->hw_params.valid_tx_ant);
+ valid_tx_ant =
+ first_antenna(hw_params(priv).valid_tx_ant);
if (tbl->action != IWL_SISO_SWITCH_ANTENNA1)
tbl->action = IWL_SISO_SWITCH_ANTENNA1;
break;
@@ -1647,7 +1662,8 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
/* configure as 1x1 if bt full concurrency */
if (priv->bt_full_concurrent) {
- valid_tx_ant = first_antenna(priv->hw_params.valid_tx_ant);
+ valid_tx_ant =
+ first_antenna(hw_params(priv).valid_tx_ant);
if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2)
tbl->action = IWL_SISO_SWITCH_ANTENNA1;
}
@@ -1783,8 +1799,8 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv,
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action;
- u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
- u8 tx_chains_num = priv->hw_params.tx_chains_num;
+ u8 valid_tx_ant = hw_params(priv).valid_tx_ant;
+ u8 tx_chains_num = hw_params(priv).tx_chains_num;
u8 update_search_tbl_counter = 0;
int ret;
@@ -1953,8 +1969,8 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv,
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action;
- u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
- u8 tx_chains_num = priv->hw_params.tx_chains_num;
+ u8 valid_tx_ant = hw_params(priv).valid_tx_ant;
+ u8 tx_chains_num = hw_params(priv).tx_chains_num;
int ret;
u8 update_search_tbl_counter = 0;
@@ -2197,7 +2213,6 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
/*
* setup rate table in uCode
- * return rate_n_flags as used in the table
*/
static void rs_update_rate_tbl(struct iwl_priv *priv,
struct iwl_rxon_context *ctx,
@@ -2244,10 +2259,10 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
u8 done_search = 0;
u16 high_low;
s32 sr;
- u8 tid = MAX_TID_COUNT;
+ u8 tid = IWL_MAX_TID_COUNT;
struct iwl_tid_data *tid_data;
struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->common.ctx;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n");
@@ -2257,14 +2272,12 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
info->flags & IEEE80211_TX_CTL_NO_ACK)
return;
- if (!sta || !lq_sta)
- return;
-
lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
tid = rs_tl_add_packet(lq_sta, hdr);
- if ((tid != MAX_TID_COUNT) && (lq_sta->tx_agg_tid_en & (1 << tid))) {
- tid_data = &priv->stations[lq_sta->lq.sta_id].tid[tid];
+ if ((tid != IWL_MAX_TID_COUNT) &&
+ (lq_sta->tx_agg_tid_en & (1 << tid))) {
+ tid_data = &priv->shrd->tid_data[lq_sta->lq.sta_id][tid];
if (tid_data->agg.state == IWL_AGG_OFF)
lq_sta->is_agg = 0;
else
@@ -2634,9 +2647,10 @@ lq_update:
iwl_ht_enabled(priv)) {
if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) &&
(lq_sta->tx_agg_tid_en & (1 << tid)) &&
- (tid != MAX_TID_COUNT)) {
+ (tid != IWL_MAX_TID_COUNT)) {
+ u8 sta_id = lq_sta->lq.sta_id;
tid_data =
- &priv->stations[lq_sta->lq.sta_id].tid[tid];
+ &priv->shrd->tid_data[sta_id][tid];
if (tid_data->agg.state == IWL_AGG_OFF) {
IWL_DEBUG_RATE(priv,
"try to aggregate tid %d\n",
@@ -2651,8 +2665,7 @@ lq_update:
out:
tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, index, is_green);
- i = index;
- lq_sta->last_txrate_idx = i;
+ lq_sta->last_txrate_idx = index;
}
/**
@@ -2688,11 +2701,11 @@ static void rs_initialize_lq(struct iwl_priv *priv,
return;
sta_priv = (void *)sta->drv_priv;
- ctx = sta_priv->common.ctx;
+ ctx = sta_priv->ctx;
i = lq_sta->last_txrate_idx;
- valid_tx_ant = priv->hw_params.valid_tx_ant;
+ valid_tx_ant = hw_params(priv).valid_tx_ant;
if (!lq_sta->search_better_tbl)
active_tbl = lq_sta->active_tbl;
@@ -2875,15 +2888,15 @@ void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_i
/* These values will be overridden later */
lq_sta->lq.general_params.single_stream_ant_msk =
- first_antenna(priv->hw_params.valid_tx_ant);
+ first_antenna(hw_params(priv).valid_tx_ant);
lq_sta->lq.general_params.dual_stream_ant_msk =
- priv->hw_params.valid_tx_ant &
- ~first_antenna(priv->hw_params.valid_tx_ant);
+ hw_params(priv).valid_tx_ant &
+ ~first_antenna(hw_params(priv).valid_tx_ant);
if (!lq_sta->lq.general_params.dual_stream_ant_msk) {
lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB;
- } else if (num_of_ant(priv->hw_params.valid_tx_ant) == 2) {
+ } else if (num_of_ant(hw_params(priv).valid_tx_ant) == 2) {
lq_sta->lq.general_params.dual_stream_ant_msk =
- priv->hw_params.valid_tx_ant;
+ hw_params(priv).valid_tx_ant;
}
/* as default allow aggregation for all tids */
@@ -2895,8 +2908,9 @@ void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_i
if (sband->band == IEEE80211_BAND_5GHZ)
lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
lq_sta->is_agg = 0;
-
- priv->dbg_fixed_rate = 0;
+#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL
+ priv->tm_fixed_rate = 0;
+#endif
#ifdef CONFIG_MAC80211_DEBUGFS
lq_sta->dbg_fixed_rate = 0;
#endif
@@ -2928,7 +2942,7 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
if (priv && priv->bt_full_concurrent) {
/* 1x1 only */
tbl_type.ant_type =
- first_antenna(priv->hw_params.valid_tx_ant);
+ first_antenna(hw_params(priv).valid_tx_ant);
}
/* How many times should we repeat the initial rate? */
@@ -2960,7 +2974,7 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
if (priv->bt_full_concurrent)
valid_tx_ant = ANT_A;
else
- valid_tx_ant = priv->hw_params.valid_tx_ant;
+ valid_tx_ant = hw_params(priv).valid_tx_ant;
}
/* Fill rest of rate table */
@@ -2994,7 +3008,7 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
if (priv && priv->bt_full_concurrent) {
/* 1x1 only */
tbl_type.ant_type =
- first_antenna(priv->hw_params.valid_tx_ant);
+ first_antenna(hw_params(priv).valid_tx_ant);
}
/* Indicate to uCode which entries might be MIMO.
@@ -3085,7 +3099,7 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
u8 ant_sel_tx;
priv = lq_sta->drv;
- valid_tx_ant = priv->hw_params.valid_tx_ant;
+ valid_tx_ant = hw_params(priv).valid_tx_ant;
if (lq_sta->dbg_fixed_rate) {
ant_sel_tx =
((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK)
@@ -3095,7 +3109,6 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
IWL_DEBUG_RATE(priv, "Fixed rate ON\n");
} else {
lq_sta->dbg_fixed_rate = 0;
- priv->dbg_fixed_rate = 0;
IWL_ERR(priv,
"Invalid antenna selection 0x%X, Valid is 0x%X\n",
ant_sel_tx, valid_tx_ant);
@@ -3123,9 +3136,9 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
return -EFAULT;
if (sscanf(buf, "%x", &parsed_rate) == 1)
- priv->dbg_fixed_rate = lq_sta->dbg_fixed_rate = parsed_rate;
+ lq_sta->dbg_fixed_rate = parsed_rate;
else
- priv->dbg_fixed_rate = lq_sta->dbg_fixed_rate = 0;
+ lq_sta->dbg_fixed_rate = 0;
rs_program_fix_rate(priv, lq_sta);
@@ -3155,11 +3168,11 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
lq_sta->total_failed, lq_sta->total_success,
lq_sta->active_legacy_rate);
desc += sprintf(buff+desc, "fixed rate 0x%X\n",
- priv->dbg_fixed_rate);
+ lq_sta->dbg_fixed_rate);
desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
- (priv->hw_params.valid_tx_ant & ANT_A) ? "ANT_A," : "",
- (priv->hw_params.valid_tx_ant & ANT_B) ? "ANT_B," : "",
- (priv->hw_params.valid_tx_ant & ANT_C) ? "ANT_C" : "");
+ (hw_params(priv).valid_tx_ant & ANT_A) ? "ANT_A," : "",
+ (hw_params(priv).valid_tx_ant & ANT_B) ? "ANT_B," : "",
+ (hw_params(priv).valid_tx_ant & ANT_C) ? "ANT_C" : "");
desc += sprintf(buff+desc, "lq type %s\n",
(is_legacy(tbl->lq_type)) ? "legacy" : "HT");
if (is_Ht(tbl->lq_type)) {
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
index bdae82e7fa90..f4f6deb829ae 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
@@ -27,6 +27,10 @@
#ifndef __iwl_agn_rs_h__
#define __iwl_agn_rs_h__
+#include <net/mac80211.h>
+
+#include "iwl-commands.h"
+
struct iwl_rate_info {
u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */
u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
index b774517aa9fa..5af9e6258a16 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
@@ -3,7 +3,7 @@
* Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
*
* Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
+ * as portionhelp of the ieee80211 subsystem header files.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
@@ -35,188 +35,92 @@
#include "iwl-eeprom.h"
#include "iwl-dev.h"
#include "iwl-core.h"
-#include "iwl-sta.h"
#include "iwl-io.h"
-#include "iwl-helpers.h"
#include "iwl-agn-calib.h"
#include "iwl-agn.h"
+#include "iwl-shared.h"
-/******************************************************************************
- *
- * RX path functions
- *
- ******************************************************************************/
-
-/*
- * Rx theory of operation
- *
- * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs),
- * each of which point to Receive Buffers to be filled by the NIC. These get
- * used not only for Rx frames, but for any command response or notification
- * from the NIC. The driver and NIC manage the Rx buffers by means
- * of indexes into the circular buffer.
- *
- * Rx Queue Indexes
- * The host/firmware share two index registers for managing the Rx buffers.
- *
- * The READ index maps to the first position that the firmware may be writing
- * to -- the driver can read up to (but not including) this position and get
- * good data.
- * The READ index is managed by the firmware once the card is enabled.
- *
- * The WRITE index maps to the last position the driver has read from -- the
- * position preceding WRITE is the last slot the firmware can place a packet.
- *
- * The queue is empty (no good data) if WRITE = READ - 1, and is full if
- * WRITE = READ.
- *
- * During initialization, the host sets up the READ queue position to the first
- * INDEX position, and WRITE to the last (READ - 1 wrapped)
- *
- * When the firmware places a packet in a buffer, it will advance the READ index
- * and fire the RX interrupt. The driver can then query the READ index and
- * process as many packets as possible, moving the WRITE index forward as it
- * resets the Rx queue buffers with new memory.
- *
- * The management in the driver is as follows:
- * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When
- * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
- * to replenish the iwl->rxq->rx_free.
- * + In iwl_rx_replenish (scheduled) if 'processed' != 'read' then the
- * iwl->rxq is replenished and the READ INDEX is updated (updating the
- * 'processed' and 'read' driver indexes as well)
- * + A received packet is processed and handed to the kernel network stack,
- * detached from the iwl->rxq. The driver 'processed' index is updated.
- * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free
- * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ
- * INDEX is not incremented and iwl->status(RX_STALLED) is set. If there
- * were enough free buffers and RX_STALLED is set it is cleared.
- *
- *
- * Driver sequence:
- *
- * iwl_rx_queue_alloc() Allocates rx_free
- * iwl_rx_replenish() Replenishes rx_free list from rx_used, and calls
- * iwl_rx_queue_restock
- * iwl_rx_queue_restock() Moves available buffers from rx_free into Rx
- * queue, updates firmware pointers, and updates
- * the WRITE index. If insufficient rx_free buffers
- * are available, schedules iwl_rx_replenish
- *
- * -- enable interrupts --
- * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the
- * READ INDEX, detaching the SKB from the pool.
- * Moves the packet buffer from queue to rx_used.
- * Calls iwl_rx_queue_restock to refill any empty
- * slots.
- * ...
- *
- */
-
-/**
- * iwl_rx_queue_space - Return number of free slots available in queue.
- */
-int iwl_rx_queue_space(const struct iwl_rx_queue *q)
+const char *get_cmd_string(u8 cmd)
{
- int s = q->read - q->write;
- if (s <= 0)
- s += RX_QUEUE_SIZE;
- /* keep some buffer to not confuse full and empty queue */
- s -= 2;
- if (s < 0)
- s = 0;
- return s;
-}
-
-/**
- * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue
- */
-void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q)
-{
- unsigned long flags;
- u32 rx_wrt_ptr_reg = priv->hw_params.rx_wrt_ptr_reg;
- u32 reg;
-
- spin_lock_irqsave(&q->lock, flags);
-
- if (q->need_update == 0)
- goto exit_unlock;
+ switch (cmd) {
+ IWL_CMD(REPLY_ALIVE);
+ IWL_CMD(REPLY_ERROR);
+ IWL_CMD(REPLY_ECHO);
+ IWL_CMD(REPLY_RXON);
+ IWL_CMD(REPLY_RXON_ASSOC);
+ IWL_CMD(REPLY_QOS_PARAM);
+ IWL_CMD(REPLY_RXON_TIMING);
+ IWL_CMD(REPLY_ADD_STA);
+ IWL_CMD(REPLY_REMOVE_STA);
+ IWL_CMD(REPLY_REMOVE_ALL_STA);
+ IWL_CMD(REPLY_TXFIFO_FLUSH);
+ IWL_CMD(REPLY_WEPKEY);
+ IWL_CMD(REPLY_TX);
+ IWL_CMD(REPLY_LEDS_CMD);
+ IWL_CMD(REPLY_TX_LINK_QUALITY_CMD);
+ IWL_CMD(COEX_PRIORITY_TABLE_CMD);
+ IWL_CMD(COEX_MEDIUM_NOTIFICATION);
+ IWL_CMD(COEX_EVENT_CMD);
+ IWL_CMD(REPLY_QUIET_CMD);
+ IWL_CMD(REPLY_CHANNEL_SWITCH);
+ IWL_CMD(CHANNEL_SWITCH_NOTIFICATION);
+ IWL_CMD(REPLY_SPECTRUM_MEASUREMENT_CMD);
+ IWL_CMD(SPECTRUM_MEASURE_NOTIFICATION);
+ IWL_CMD(POWER_TABLE_CMD);
+ IWL_CMD(PM_SLEEP_NOTIFICATION);
+ IWL_CMD(PM_DEBUG_STATISTIC_NOTIFIC);
+ IWL_CMD(REPLY_SCAN_CMD);
+ IWL_CMD(REPLY_SCAN_ABORT_CMD);
+ IWL_CMD(SCAN_START_NOTIFICATION);
+ IWL_CMD(SCAN_RESULTS_NOTIFICATION);
+ IWL_CMD(SCAN_COMPLETE_NOTIFICATION);
+ IWL_CMD(BEACON_NOTIFICATION);
+ IWL_CMD(REPLY_TX_BEACON);
+ IWL_CMD(WHO_IS_AWAKE_NOTIFICATION);
+ IWL_CMD(QUIET_NOTIFICATION);
+ IWL_CMD(REPLY_TX_PWR_TABLE_CMD);
+ IWL_CMD(MEASURE_ABORT_NOTIFICATION);
+ IWL_CMD(REPLY_BT_CONFIG);
+ IWL_CMD(REPLY_STATISTICS_CMD);
+ IWL_CMD(STATISTICS_NOTIFICATION);
+ IWL_CMD(REPLY_CARD_STATE_CMD);
+ IWL_CMD(CARD_STATE_NOTIFICATION);
+ IWL_CMD(MISSED_BEACONS_NOTIFICATION);
+ IWL_CMD(REPLY_CT_KILL_CONFIG_CMD);
+ IWL_CMD(SENSITIVITY_CMD);
+ IWL_CMD(REPLY_PHY_CALIBRATION_CMD);
+ IWL_CMD(REPLY_RX_PHY_CMD);
+ IWL_CMD(REPLY_RX_MPDU_CMD);
+ IWL_CMD(REPLY_RX);
+ IWL_CMD(REPLY_COMPRESSED_BA);
+ IWL_CMD(CALIBRATION_CFG_CMD);
+ IWL_CMD(CALIBRATION_RES_NOTIFICATION);
+ IWL_CMD(CALIBRATION_COMPLETE_NOTIFICATION);
+ IWL_CMD(REPLY_TX_POWER_DBM_CMD);
+ IWL_CMD(TEMPERATURE_NOTIFICATION);
+ IWL_CMD(TX_ANT_CONFIGURATION_CMD);
+ IWL_CMD(REPLY_BT_COEX_PROFILE_NOTIF);
+ IWL_CMD(REPLY_BT_COEX_PRIO_TABLE);
+ IWL_CMD(REPLY_BT_COEX_PROT_ENV);
+ IWL_CMD(REPLY_WIPAN_PARAMS);
+ IWL_CMD(REPLY_WIPAN_RXON);
+ IWL_CMD(REPLY_WIPAN_RXON_TIMING);
+ IWL_CMD(REPLY_WIPAN_RXON_ASSOC);
+ IWL_CMD(REPLY_WIPAN_QOS_PARAM);
+ IWL_CMD(REPLY_WIPAN_WEPKEY);
+ IWL_CMD(REPLY_WIPAN_P2P_CHANNEL_SWITCH);
+ IWL_CMD(REPLY_WIPAN_NOA_NOTIFICATION);
+ IWL_CMD(REPLY_WIPAN_DEACTIVATION_COMPLETE);
+ IWL_CMD(REPLY_WOWLAN_PATTERNS);
+ IWL_CMD(REPLY_WOWLAN_WAKEUP_FILTER);
+ IWL_CMD(REPLY_WOWLAN_TSC_RSC_PARAMS);
+ IWL_CMD(REPLY_WOWLAN_TKIP_PARAMS);
+ IWL_CMD(REPLY_WOWLAN_KEK_KCK_MATERIAL);
+ IWL_CMD(REPLY_WOWLAN_GET_STATUS);
+ default:
+ return "UNKNOWN";
- if (priv->cfg->base_params->shadow_reg_enable) {
- /* shadow register enabled */
- /* Device expects a multiple of 8 */
- q->write_actual = (q->write & ~0x7);
- iwl_write32(priv, rx_wrt_ptr_reg, q->write_actual);
- } else {
- /* If power-saving is in use, make sure device is awake */
- if (test_bit(STATUS_POWER_PMI, &priv->status)) {
- reg = iwl_read32(priv, CSR_UCODE_DRV_GP1);
-
- if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
- IWL_DEBUG_INFO(priv,
- "Rx queue requesting wakeup,"
- " GP1 = 0x%x\n", reg);
- iwl_set_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- goto exit_unlock;
- }
-
- q->write_actual = (q->write & ~0x7);
- iwl_write_direct32(priv, rx_wrt_ptr_reg,
- q->write_actual);
-
- /* Else device is assumed to be awake */
- } else {
- /* Device expects a multiple of 8 */
- q->write_actual = (q->write & ~0x7);
- iwl_write_direct32(priv, rx_wrt_ptr_reg,
- q->write_actual);
- }
}
- q->need_update = 0;
-
- exit_unlock:
- spin_unlock_irqrestore(&q->lock, flags);
-}
-
-int iwl_rx_queue_alloc(struct iwl_priv *priv)
-{
- struct iwl_rx_queue *rxq = &priv->rxq;
- struct device *dev = &priv->pci_dev->dev;
- int i;
-
- spin_lock_init(&rxq->lock);
- INIT_LIST_HEAD(&rxq->rx_free);
- INIT_LIST_HEAD(&rxq->rx_used);
-
- /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */
- rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma,
- GFP_KERNEL);
- if (!rxq->bd)
- goto err_bd;
-
- rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct iwl_rb_status),
- &rxq->rb_stts_dma, GFP_KERNEL);
- if (!rxq->rb_stts)
- goto err_rb;
-
- /* Fill the rx_used queue with _all_ of the Rx buffers */
- for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
- list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
-
- /* Set us so that we have processed and used all buffers, but have
- * not restocked the Rx queue with fresh buffers */
- rxq->read = rxq->write = 0;
- rxq->write_actual = 0;
- rxq->free_count = 0;
- rxq->need_update = 0;
- return 0;
-
-err_rb:
- dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
- rxq->bd_dma);
-err_bd:
- return -ENOMEM;
}
/******************************************************************************
@@ -225,8 +129,9 @@ err_bd:
*
******************************************************************************/
-static void iwl_rx_reply_error(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_reply_error(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
@@ -237,21 +142,23 @@ static void iwl_rx_reply_error(struct iwl_priv *priv,
pkt->u.err_resp.cmd_id,
le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num),
le32_to_cpu(pkt->u.err_resp.error_info));
+ return 0;
}
-static void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_csa_notification *csa = &(pkt->u.csa_notif);
/*
* MULTI-FIXME
- * See iwl_mac_channel_switch.
+ * See iwlagn_mac_channel_switch.
*/
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
struct iwl_rxon_cmd *rxon = (void *)&ctx->active;
- if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
- return;
+ if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status))
+ return 0;
if (!le32_to_cpu(csa->status) && csa->channel == priv->switch_channel) {
rxon->channel = csa->channel;
@@ -264,11 +171,13 @@ static void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
le16_to_cpu(csa->channel));
iwl_chswitch_done(priv, false);
}
+ return 0;
}
-static void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_spectrum_measure_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif);
@@ -276,15 +185,17 @@ static void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
if (!report->state) {
IWL_DEBUG_11H(priv,
"Spectrum Measure Notification: Start\n");
- return;
+ return 0;
}
memcpy(&priv->measure_report, report, sizeof(*report));
priv->measurement_status |= MEASUREMENT_READY;
+ return 0;
}
-static void iwl_rx_pm_sleep_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_pm_sleep_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
#ifdef CONFIG_IWLWIFI_DEBUG
struct iwl_rx_packet *pkt = rxb_addr(rxb);
@@ -292,21 +203,26 @@ static void iwl_rx_pm_sleep_notif(struct iwl_priv *priv,
IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n",
sleep->pm_sleep_mode, sleep->pm_wakeup_src);
#endif
+ return 0;
}
-static void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
- u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+ u32 __maybe_unused len =
+ le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled "
"notification for %s:\n", len,
get_cmd_string(pkt->hdr.cmd));
iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->u.raw, len);
+ return 0;
}
-static void iwl_rx_beacon_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_beacon_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwlagn_beacon_notif *beacon = (void *)pkt->u.raw;
@@ -325,8 +241,7 @@ static void iwl_rx_beacon_notif(struct iwl_priv *priv,
priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status);
- if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
- queue_work(priv->workqueue, &priv->beacon_update);
+ return 0;
}
/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */
@@ -341,13 +256,13 @@ static void iwl_rx_beacon_notif(struct iwl_priv *priv,
* the BA_TIMEOUT_MAX, reload firmware and bring system back to normal
* operation state.
*/
-static bool iwl_good_ack_health(struct iwl_priv *priv,
+static bool iwlagn_good_ack_health(struct iwl_priv *priv,
struct statistics_tx *cur)
{
int actual_delta, expected_delta, ba_timeout_delta;
struct statistics_tx *old;
- if (priv->_agn.agg_tids_count)
+ if (priv->agg_tids_count)
return true;
old = &priv->statistics.tx;
@@ -366,8 +281,9 @@ static bool iwl_good_ack_health(struct iwl_priv *priv,
if ((actual_delta * 100 / expected_delta) < ACK_CNT_RATIO &&
ba_timeout_delta > BA_TIMEOUT_CNT) {
- IWL_DEBUG_RADIO(priv, "deltas: actual %d expected %d ba_timeout %d\n",
- actual_delta, expected_delta, ba_timeout_delta);
+ IWL_DEBUG_RADIO(priv,
+ "deltas: actual %d expected %d ba_timeout %d\n",
+ actual_delta, expected_delta, ba_timeout_delta);
#ifdef CONFIG_IWLWIFI_DEBUGFS
/*
@@ -395,7 +311,7 @@ static bool iwl_good_ack_health(struct iwl_priv *priv,
* When the plcp error is exceeding the thresholds, reset the radio
* to improve the throughput.
*/
-static bool iwl_good_plcp_health(struct iwl_priv *priv,
+static bool iwlagn_good_plcp_health(struct iwl_priv *priv,
struct statistics_rx_phy *cur_ofdm,
struct statistics_rx_ht_phy *cur_ofdm_ht,
unsigned int msecs)
@@ -427,15 +343,15 @@ static bool iwl_good_plcp_health(struct iwl_priv *priv,
return true;
}
-static void iwl_recover_from_statistics(struct iwl_priv *priv,
- struct statistics_rx_phy *cur_ofdm,
- struct statistics_rx_ht_phy *cur_ofdm_ht,
- struct statistics_tx *tx,
- unsigned long stamp)
+static void iwlagn_recover_from_statistics(struct iwl_priv *priv,
+ struct statistics_rx_phy *cur_ofdm,
+ struct statistics_rx_ht_phy *cur_ofdm_ht,
+ struct statistics_tx *tx,
+ unsigned long stamp)
{
unsigned int msecs;
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
msecs = jiffies_to_msecs(stamp - priv->rx_statistics_jiffies);
@@ -448,21 +364,21 @@ static void iwl_recover_from_statistics(struct iwl_priv *priv,
if (msecs < 99)
return;
- if (iwlagn_mod_params.ack_check && !iwl_good_ack_health(priv, tx)) {
+ if (iwlagn_mod_params.ack_check && !iwlagn_good_ack_health(priv, tx)) {
IWL_ERR(priv, "low ack count detected, restart firmware\n");
if (!iwl_force_reset(priv, IWL_FW_RESET, false))
return;
}
if (iwlagn_mod_params.plcp_check &&
- !iwl_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs))
+ !iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs))
iwl_force_reset(priv, IWL_RF_RESET, false);
}
/* Calculate noise level, based on measurements during network silence just
* before arriving beacon. This measurement can be done only if we know
* exactly when to expect beacons, therefore only when we're associated. */
-static void iwl_rx_calc_noise(struct iwl_priv *priv)
+static void iwlagn_rx_calc_noise(struct iwl_priv *priv)
{
struct statistics_rx_non_phy *rx_info;
int num_active_rx = 0;
@@ -528,7 +444,7 @@ static void accum_stats(__le32 *prev, __le32 *cur, __le32 *delta,
}
static void
-iwl_accumulative_statistics(struct iwl_priv *priv,
+iwlagn_accumulative_statistics(struct iwl_priv *priv,
struct statistics_general_common *common,
struct statistics_rx_non_phy *rx_non_phy,
struct statistics_rx_phy *rx_ofdm,
@@ -557,7 +473,7 @@ iwl_accumulative_statistics(struct iwl_priv *priv,
}
#else
static inline void
-iwl_accumulative_statistics(struct iwl_priv *priv,
+iwlagn_accumulative_statistics(struct iwl_priv *priv,
struct statistics_general_common *common,
struct statistics_rx_non_phy *rx_non_phy,
struct statistics_rx_phy *rx_ofdm,
@@ -569,8 +485,9 @@ iwl_accumulative_statistics(struct iwl_priv *priv,
}
#endif
-static void iwl_rx_statistics(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_statistics(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
unsigned long stamp = jiffies;
const int reg_recalib_period = 60;
@@ -624,17 +541,17 @@ static void iwl_rx_statistics(struct iwl_priv *priv,
WARN_ONCE(1, "len %d doesn't match BT (%zu) or normal (%zu)\n",
len, sizeof(struct iwl_bt_notif_statistics),
sizeof(struct iwl_notif_statistics));
- return;
+ return 0;
}
change = common->temperature != priv->statistics.common.temperature ||
(*flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
(priv->statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK);
- iwl_accumulative_statistics(priv, common, rx_non_phy, rx_ofdm,
+ iwlagn_accumulative_statistics(priv, common, rx_non_phy, rx_ofdm,
rx_ofdm_ht, rx_cck, tx, bt_activity);
- iwl_recover_from_statistics(priv, rx_ofdm, rx_ofdm_ht, tx, stamp);
+ iwlagn_recover_from_statistics(priv, rx_ofdm, rx_ofdm_ht, tx, stamp);
priv->statistics.flag = *flag;
memcpy(&priv->statistics.common, common, sizeof(*common));
@@ -651,7 +568,7 @@ static void iwl_rx_statistics(struct iwl_priv *priv,
priv->rx_statistics_jiffies = stamp;
- set_bit(STATUS_STATISTICS, &priv->status);
+ set_bit(STATUS_STATISTICS, &priv->shrd->status);
/* Reschedule the statistics timer to occur in
* reg_recalib_period seconds to ensure we get a
@@ -660,17 +577,19 @@ static void iwl_rx_statistics(struct iwl_priv *priv,
mod_timer(&priv->statistics_periodic, jiffies +
msecs_to_jiffies(reg_recalib_period * 1000));
- if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) &&
+ if (unlikely(!test_bit(STATUS_SCANNING, &priv->shrd->status)) &&
(pkt->hdr.cmd == STATISTICS_NOTIFICATION)) {
- iwl_rx_calc_noise(priv);
- queue_work(priv->workqueue, &priv->run_time_calib_work);
+ iwlagn_rx_calc_noise(priv);
+ queue_work(priv->shrd->workqueue, &priv->run_time_calib_work);
}
- if (priv->cfg->ops->lib->temp_ops.temperature && change)
- priv->cfg->ops->lib->temp_ops.temperature(priv);
+ if (priv->cfg->lib->temperature && change)
+ priv->cfg->lib->temperature(priv);
+ return 0;
}
-static void iwl_rx_reply_statistics(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_reply_statistics(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
@@ -685,17 +604,19 @@ static void iwl_rx_reply_statistics(struct iwl_priv *priv,
#endif
IWL_DEBUG_RX(priv, "Statistics have been cleared\n");
}
- iwl_rx_statistics(priv, rxb);
+ iwlagn_rx_statistics(priv, rxb, cmd);
+ return 0;
}
/* Handle notification from uCode that card's power state is changing
* due to software, hardware, or critical temperature RFKILL */
-static void iwl_rx_card_state_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_card_state_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
- unsigned long status = priv->status;
+ unsigned long status = priv->shrd->status;
IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n",
(flags & HW_CARD_DISABLED) ? "Kill" : "On",
@@ -706,16 +627,16 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED |
CT_CARD_DISABLED)) {
- iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
+ iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_SET,
CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
- iwl_write_direct32(priv, HBUS_TARG_MBX_C,
+ iwl_write_direct32(bus(priv), HBUS_TARG_MBX_C,
HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
if (!(flags & RXON_CARD_DISABLED)) {
- iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
+ iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_CLR,
CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
- iwl_write_direct32(priv, HBUS_TARG_MBX_C,
+ iwl_write_direct32(bus(priv), HBUS_TARG_MBX_C,
HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
}
if (flags & CT_CARD_DISABLED)
@@ -725,24 +646,26 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
iwl_tt_exit_ct_kill(priv);
if (flags & HW_CARD_DISABLED)
- set_bit(STATUS_RF_KILL_HW, &priv->status);
+ set_bit(STATUS_RF_KILL_HW, &priv->shrd->status);
else
- clear_bit(STATUS_RF_KILL_HW, &priv->status);
+ clear_bit(STATUS_RF_KILL_HW, &priv->shrd->status);
if (!(flags & RXON_CARD_DISABLED))
iwl_scan_cancel(priv);
if ((test_bit(STATUS_RF_KILL_HW, &status) !=
- test_bit(STATUS_RF_KILL_HW, &priv->status)))
+ test_bit(STATUS_RF_KILL_HW, &priv->shrd->status)))
wiphy_rfkill_set_hw_state(priv->hw->wiphy,
- test_bit(STATUS_RF_KILL_HW, &priv->status));
+ test_bit(STATUS_RF_KILL_HW, &priv->shrd->status));
else
- wake_up_interruptible(&priv->wait_command_queue);
+ wake_up(&priv->shrd->wait_command_queue);
+ return 0;
}
-static void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_missed_beacon_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
@@ -757,27 +680,30 @@ static void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
le32_to_cpu(missed_beacon->total_missed_becons),
le32_to_cpu(missed_beacon->num_recvd_beacons),
le32_to_cpu(missed_beacon->num_expected_beacons));
- if (!test_bit(STATUS_SCANNING, &priv->status))
+ if (!test_bit(STATUS_SCANNING, &priv->shrd->status))
iwl_init_sensitivity(priv);
}
+ return 0;
}
/* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD).
* This will be used later in iwl_rx_reply_rx() for REPLY_RX_MPDU_CMD. */
-static void iwl_rx_reply_rx_phy(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_reply_rx_phy(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
- priv->_agn.last_phy_res_valid = true;
- memcpy(&priv->_agn.last_phy_res, pkt->u.raw,
+ priv->last_phy_res_valid = true;
+ memcpy(&priv->last_phy_res, pkt->u.raw,
sizeof(struct iwl_rx_phy_res));
+ return 0;
}
/*
* returns non-zero if packet should be dropped
*/
-static int iwl_set_decrypted_flag(struct iwl_priv *priv,
+static int iwlagn_set_decrypted_flag(struct iwl_priv *priv,
struct ieee80211_hdr *hdr,
u32 decrypt_res,
struct ieee80211_rx_status *stats)
@@ -826,7 +752,7 @@ static int iwl_set_decrypted_flag(struct iwl_priv *priv,
return 0;
}
-static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
+static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
struct ieee80211_hdr *hdr,
u16 len,
u32 ampdu_status,
@@ -846,7 +772,7 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
/* In case of HW accelerated crypto and bad decryption, drop */
if (!iwlagn_mod_params.sw_crypto &&
- iwl_set_decrypted_flag(priv, hdr, ampdu_status, stats))
+ iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats))
return;
skb = dev_alloc_skb(128);
@@ -874,7 +800,7 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
ctx->active.bssid_addr))
continue;
ctx->last_tx_rejected = false;
- iwl_wake_any_queue(priv, ctx);
+ iwl_trans_wake_any_queue(trans(priv), ctx->ctxid);
}
}
@@ -884,7 +810,7 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
rxb->page = NULL;
}
-static u32 iwl_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
+static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
{
u32 decrypt_out = 0;
@@ -943,10 +869,52 @@ static u32 iwl_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
return decrypt_out;
}
+/* Calc max signal level (dBm) among 3 possible receivers */
+static int iwlagn_calc_rssi(struct iwl_priv *priv,
+ struct iwl_rx_phy_res *rx_resp)
+{
+ /* data from PHY/DSP regarding signal strength, etc.,
+ * contents are always there, not configurable by host
+ */
+ struct iwlagn_non_cfg_phy *ncphy =
+ (struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
+ u32 val, rssi_a, rssi_b, rssi_c, max_rssi;
+ u8 agc;
+
+ val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]);
+ agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS;
+
+ /* Find max rssi among 3 possible receivers.
+ * These values are measured by the digital signal processor (DSP).
+ * They should stay fairly constant even as the signal strength varies,
+ * if the radio's automatic gain control (AGC) is working right.
+ * AGC value (see below) will provide the "interesting" info.
+ */
+ val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]);
+ rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >>
+ IWLAGN_OFDM_RSSI_A_BIT_POS;
+ rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >>
+ IWLAGN_OFDM_RSSI_B_BIT_POS;
+ val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_C_IDX]);
+ rssi_c = (val & IWLAGN_OFDM_RSSI_INBAND_C_BITMSK) >>
+ IWLAGN_OFDM_RSSI_C_BIT_POS;
+
+ max_rssi = max_t(u32, rssi_a, rssi_b);
+ max_rssi = max_t(u32, max_rssi, rssi_c);
+
+ IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n",
+ rssi_a, rssi_b, rssi_c, max_rssi, agc);
+
+ /* dBm = max_rssi dB - agc dB - constant.
+ * Higher AGC (higher radio gain) means lower signal. */
+ return max_rssi - agc - IWLAGN_RSSI_OFFSET;
+}
+
/* Called for REPLY_RX (legacy ABG frames), or
* REPLY_RX_MPDU_CMD (HT high-throughput N frames). */
-static void iwl_rx_reply_rx(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwlagn_rx_reply_rx(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct ieee80211_hdr *header;
struct ieee80211_rx_status rx_status;
@@ -977,30 +945,30 @@ static void iwl_rx_reply_rx(struct iwl_priv *priv,
phy_res->cfg_phy_cnt + len);
ampdu_status = le32_to_cpu(rx_pkt_status);
} else {
- if (!priv->_agn.last_phy_res_valid) {
+ if (!priv->last_phy_res_valid) {
IWL_ERR(priv, "MPDU frame without cached PHY data\n");
- return;
+ return 0;
}
- phy_res = &priv->_agn.last_phy_res;
+ phy_res = &priv->last_phy_res;
amsdu = (struct iwl_rx_mpdu_res_start *)pkt->u.raw;
header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu));
len = le16_to_cpu(amsdu->byte_count);
rx_pkt_status = *(__le32 *)(pkt->u.raw + sizeof(*amsdu) + len);
- ampdu_status = iwl_translate_rx_status(priv,
+ ampdu_status = iwlagn_translate_rx_status(priv,
le32_to_cpu(rx_pkt_status));
}
if ((unlikely(phy_res->cfg_phy_cnt > 20))) {
IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d/n",
phy_res->cfg_phy_cnt);
- return;
+ return 0;
}
if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) ||
!(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n",
le32_to_cpu(rx_pkt_status));
- return;
+ return 0;
}
/* This will be used in several places later */
@@ -1024,7 +992,7 @@ static void iwl_rx_reply_rx(struct iwl_priv *priv,
priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp);
/* Find max signal strength (dBm) among 3 antenna/receiver chains */
- rx_status.signal = priv->cfg->ops->utils->calc_rssi(priv, phy_res);
+ rx_status.signal = iwlagn_calc_rssi(priv, phy_res);
iwl_dbg_log_rx_data_frame(priv, len, header);
IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, TSF %llu\n",
@@ -1059,8 +1027,9 @@ static void iwl_rx_reply_rx(struct iwl_priv *priv,
if (rate_n_flags & RATE_MCS_SGI_MSK)
rx_status.flag |= RX_FLAG_SHORT_GI;
- iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status,
+ iwlagn_pass_packet_to_mac80211(priv, header, len, ampdu_status,
rxb, &rx_status);
+ return 0;
}
/**
@@ -1071,37 +1040,104 @@ static void iwl_rx_reply_rx(struct iwl_priv *priv,
*/
void iwl_setup_rx_handlers(struct iwl_priv *priv)
{
- void (**handlers)(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb);
+ int (**handlers)(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd);
handlers = priv->rx_handlers;
- handlers[REPLY_ERROR] = iwl_rx_reply_error;
- handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl_rx_csa;
- handlers[SPECTRUM_MEASURE_NOTIFICATION] = iwl_rx_spectrum_measure_notif;
- handlers[PM_SLEEP_NOTIFICATION] = iwl_rx_pm_sleep_notif;
- handlers[PM_DEBUG_STATISTIC_NOTIFIC] = iwl_rx_pm_debug_statistics_notif;
- handlers[BEACON_NOTIFICATION] = iwl_rx_beacon_notif;
+ handlers[REPLY_ERROR] = iwlagn_rx_reply_error;
+ handlers[CHANNEL_SWITCH_NOTIFICATION] = iwlagn_rx_csa;
+ handlers[SPECTRUM_MEASURE_NOTIFICATION] =
+ iwlagn_rx_spectrum_measure_notif;
+ handlers[PM_SLEEP_NOTIFICATION] = iwlagn_rx_pm_sleep_notif;
+ handlers[PM_DEBUG_STATISTIC_NOTIFIC] =
+ iwlagn_rx_pm_debug_statistics_notif;
+ handlers[BEACON_NOTIFICATION] = iwlagn_rx_beacon_notif;
+ handlers[REPLY_ADD_STA] = iwl_add_sta_callback;
/*
* The same handler is used for both the REPLY to a discrete
* statistics request from the host as well as for the periodic
* statistics notifications (after received beacons) from the uCode.
*/
- handlers[REPLY_STATISTICS_CMD] = iwl_rx_reply_statistics;
- handlers[STATISTICS_NOTIFICATION] = iwl_rx_statistics;
+ handlers[REPLY_STATISTICS_CMD] = iwlagn_rx_reply_statistics;
+ handlers[STATISTICS_NOTIFICATION] = iwlagn_rx_statistics;
iwl_setup_rx_scan_handlers(priv);
- handlers[CARD_STATE_NOTIFICATION] = iwl_rx_card_state_notif;
- handlers[MISSED_BEACONS_NOTIFICATION] = iwl_rx_missed_beacon_notif;
+ handlers[CARD_STATE_NOTIFICATION] = iwlagn_rx_card_state_notif;
+ handlers[MISSED_BEACONS_NOTIFICATION] =
+ iwlagn_rx_missed_beacon_notif;
/* Rx handlers */
- handlers[REPLY_RX_PHY_CMD] = iwl_rx_reply_rx_phy;
- handlers[REPLY_RX_MPDU_CMD] = iwl_rx_reply_rx;
+ handlers[REPLY_RX_PHY_CMD] = iwlagn_rx_reply_rx_phy;
+ handlers[REPLY_RX_MPDU_CMD] = iwlagn_rx_reply_rx;
/* block ack */
- handlers[REPLY_COMPRESSED_BA] = iwlagn_rx_reply_compressed_ba;
+ handlers[REPLY_COMPRESSED_BA] =
+ iwlagn_rx_reply_compressed_ba;
+
+ /* init calibration handlers */
+ priv->rx_handlers[CALIBRATION_RES_NOTIFICATION] =
+ iwlagn_rx_calib_result;
+ priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx;
+
+ /* set up notification wait support */
+ spin_lock_init(&priv->notif_wait_lock);
+ INIT_LIST_HEAD(&priv->notif_waits);
+ init_waitqueue_head(&priv->notif_waitq);
+
+ /* Set up BT Rx handlers */
+ if (priv->cfg->lib->bt_rx_handler_setup)
+ priv->cfg->lib->bt_rx_handler_setup(priv);
+
+}
+
+int iwl_rx_dispatch(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ int err = 0;
- /* Set up hardware specific Rx handlers */
- priv->cfg->ops->lib->rx_handler_setup(priv);
+ /*
+ * Do the notification wait before RX handlers so
+ * even if the RX handler consumes the RXB we have
+ * access to it in the notification wait entry.
+ */
+ if (!list_empty(&priv->notif_waits)) {
+ struct iwl_notification_wait *w;
+
+ spin_lock(&priv->notif_wait_lock);
+ list_for_each_entry(w, &priv->notif_waits, list) {
+ if (w->cmd != pkt->hdr.cmd)
+ continue;
+ IWL_DEBUG_RX(priv,
+ "Notif: %s, 0x%02x - wake the callers up\n",
+ get_cmd_string(pkt->hdr.cmd),
+ pkt->hdr.cmd);
+ w->triggered = true;
+ if (w->fn)
+ w->fn(priv, pkt, w->fn_data);
+ }
+ spin_unlock(&priv->notif_wait_lock);
+
+ wake_up_all(&priv->notif_waitq);
+ }
+
+ if (priv->pre_rx_handler)
+ priv->pre_rx_handler(priv, rxb);
+
+ /* Based on type of command response or notification,
+ * handle those that need handling via function in
+ * rx_handlers table. See iwl_setup_rx_handlers() */
+ if (priv->rx_handlers[pkt->hdr.cmd]) {
+ priv->rx_handlers_stats[pkt->hdr.cmd]++;
+ err = priv->rx_handlers[pkt->hdr.cmd] (priv, rxb, cmd);
+ } else {
+ /* No handling needed */
+ IWL_DEBUG_RX(priv,
+ "No handler needed for %s, 0x%02x\n",
+ get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
+ }
+ return err;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
index 09f679d6046f..58a381c01c89 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
@@ -26,10 +26,10 @@
#include "iwl-dev.h"
#include "iwl-agn.h"
-#include "iwl-sta.h"
#include "iwl-core.h"
#include "iwl-agn-calib.h"
-#include "iwl-helpers.h"
+#include "iwl-trans.h"
+#include "iwl-shared.h"
static int iwlagn_disable_bss(struct iwl_priv *priv,
struct iwl_rxon_context *ctx,
@@ -39,7 +39,8 @@ static int iwlagn_disable_bss(struct iwl_priv *priv,
int ret;
send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd, sizeof(*send), send);
+ ret = iwl_trans_send_cmd_pdu(trans(priv), ctx->rxon_cmd,
+ CMD_SYNC, sizeof(*send), send);
send->filter_flags = old_filter;
@@ -64,7 +65,8 @@ static int iwlagn_disable_pan(struct iwl_priv *priv,
send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
send->dev_type = RXON_DEV_TYPE_P2P;
- ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd, sizeof(*send), send);
+ ret = iwl_trans_send_cmd_pdu(trans(priv), ctx->rxon_cmd,
+ CMD_SYNC, sizeof(*send), send);
send->filter_flags = old_filter;
send->dev_type = old_dev_type;
@@ -81,6 +83,22 @@ static int iwlagn_disable_pan(struct iwl_priv *priv,
return ret;
}
+static int iwlagn_disconn_pan(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct iwl_rxon_cmd *send)
+{
+ __le32 old_filter = send->filter_flags;
+ int ret;
+
+ send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+ ret = iwl_trans_send_cmd_pdu(trans(priv), ctx->rxon_cmd, CMD_SYNC,
+ sizeof(*send), send);
+
+ send->filter_flags = old_filter;
+
+ return ret;
+}
+
static void iwlagn_update_qos(struct iwl_priv *priv,
struct iwl_rxon_context *ctx)
{
@@ -102,7 +120,7 @@ static void iwlagn_update_qos(struct iwl_priv *priv,
ctx->qos_data.qos_active,
ctx->qos_data.def_qos_parm.qos_flags);
- ret = iwl_send_cmd_pdu(priv, ctx->qos_cmd,
+ ret = iwl_trans_send_cmd_pdu(trans(priv), ctx->qos_cmd, CMD_SYNC,
sizeof(struct iwl_qosparam_cmd),
&ctx->qos_data.def_qos_parm);
if (ret)
@@ -112,7 +130,7 @@ static void iwlagn_update_qos(struct iwl_priv *priv,
static int iwlagn_update_beacon(struct iwl_priv *priv,
struct ieee80211_vif *vif)
{
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
dev_kfree_skb(priv->beacon_skb);
priv->beacon_skb = ieee80211_beacon_get(priv->hw, vif);
@@ -161,11 +179,8 @@ static int iwlagn_send_rxon_assoc(struct iwl_priv *priv,
ctx->staging.ofdm_ht_triple_stream_basic_rates;
rxon_assoc.acquisition_data = ctx->staging.acquisition_data;
- ret = iwl_send_cmd_pdu_async(priv, ctx->rxon_assoc_cmd,
- sizeof(rxon_assoc), &rxon_assoc, NULL);
- if (ret)
- return ret;
-
+ ret = iwl_trans_send_cmd_pdu(trans(priv), ctx->rxon_assoc_cmd,
+ CMD_ASYNC, sizeof(rxon_assoc), &rxon_assoc);
return ret;
}
@@ -175,10 +190,21 @@ static int iwlagn_rxon_disconn(struct iwl_priv *priv,
int ret;
struct iwl_rxon_cmd *active = (void *)&ctx->active;
- if (ctx->ctxid == IWL_RXON_CTX_BSS)
+ if (ctx->ctxid == IWL_RXON_CTX_BSS) {
ret = iwlagn_disable_bss(priv, ctx, &ctx->staging);
- else
+ } else {
ret = iwlagn_disable_pan(priv, ctx, &ctx->staging);
+ if (ret)
+ return ret;
+ if (ctx->vif) {
+ ret = iwl_send_rxon_timing(priv, ctx);
+ if (ret) {
+ IWL_ERR(priv, "Failed to send timing (%d)!\n", ret);
+ return ret;
+ }
+ ret = iwlagn_disconn_pan(priv, ctx, &ctx->staging);
+ }
+ }
if (ret)
return ret;
@@ -187,6 +213,8 @@ static int iwlagn_rxon_disconn(struct iwl_priv *priv,
* keys, so we have to restore those afterwards.
*/
iwl_clear_ucode_stations(priv, ctx);
+ /* update -- might need P2P now */
+ iwl_update_bcast_station(priv, ctx);
iwl_restore_stations(priv, ctx);
ret = iwl_restore_default_wep_keys(priv, ctx);
if (ret) {
@@ -205,10 +233,12 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv,
struct iwl_rxon_cmd *active = (void *)&ctx->active;
/* RXON timing must be before associated RXON */
- ret = iwl_send_rxon_timing(priv, ctx);
- if (ret) {
- IWL_ERR(priv, "Failed to send timing (%d)!\n", ret);
- return ret;
+ if (ctx->ctxid == IWL_RXON_CTX_BSS) {
+ ret = iwl_send_rxon_timing(priv, ctx);
+ if (ret) {
+ IWL_ERR(priv, "Failed to send timing (%d)!\n", ret);
+ return ret;
+ }
}
/* QoS info may be cleared by previous un-assoc RXON */
iwlagn_update_qos(priv, ctx);
@@ -235,7 +265,7 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv,
* Associated RXON doesn't clear the station table in uCode,
* so we don't need to restore stations etc. after this.
*/
- ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd,
+ ret = iwl_trans_send_cmd_pdu(trans(priv), ctx->rxon_cmd, CMD_SYNC,
sizeof(struct iwl_rxon_cmd), &ctx->staging);
if (ret) {
IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
@@ -263,9 +293,107 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv,
IWL_ERR(priv, "Error sending TX power (%d)\n", ret);
return ret;
}
+
+ if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION &&
+ priv->cfg->ht_params && priv->cfg->ht_params->smps_mode)
+ ieee80211_request_smps(ctx->vif,
+ priv->cfg->ht_params->smps_mode);
+
return 0;
}
+int iwlagn_set_pan_params(struct iwl_priv *priv)
+{
+ struct iwl_wipan_params_cmd cmd;
+ struct iwl_rxon_context *ctx_bss, *ctx_pan;
+ int slot0 = 300, slot1 = 0;
+ int ret;
+
+ if (priv->shrd->valid_contexts == BIT(IWL_RXON_CTX_BSS))
+ return 0;
+
+ BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
+
+ lockdep_assert_held(&priv->shrd->mutex);
+
+ ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS];
+ ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN];
+
+ /*
+ * If the PAN context is inactive, then we don't need
+ * to update the PAN parameters, the last thing we'll
+ * have done before it goes inactive is making the PAN
+ * parameters be WLAN-only.
+ */
+ if (!ctx_pan->is_active)
+ return 0;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ /* only 2 slots are currently allowed */
+ cmd.num_slots = 2;
+
+ cmd.slots[0].type = 0; /* BSS */
+ cmd.slots[1].type = 1; /* PAN */
+
+ if (priv->hw_roc_setup) {
+ /* both contexts must be used for this to happen */
+ slot1 = IWL_MIN_SLOT_TIME;
+ slot0 = 3000;
+ } else if (ctx_bss->vif && ctx_pan->vif) {
+ int bcnint = ctx_pan->beacon_int;
+ int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;
+
+ /* should be set, but seems unused?? */
+ cmd.flags |= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE);
+
+ if (ctx_pan->vif->type == NL80211_IFTYPE_AP &&
+ bcnint &&
+ bcnint != ctx_bss->beacon_int) {
+ IWL_ERR(priv,
+ "beacon intervals don't match (%d, %d)\n",
+ ctx_bss->beacon_int, ctx_pan->beacon_int);
+ } else
+ bcnint = max_t(int, bcnint,
+ ctx_bss->beacon_int);
+ if (!bcnint)
+ bcnint = DEFAULT_BEACON_INTERVAL;
+ slot0 = bcnint / 2;
+ slot1 = bcnint - slot0;
+
+ if (test_bit(STATUS_SCAN_HW, &priv->shrd->status) ||
+ (!ctx_bss->vif->bss_conf.idle &&
+ !ctx_bss->vif->bss_conf.assoc)) {
+ slot0 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME;
+ slot1 = IWL_MIN_SLOT_TIME;
+ } else if (!ctx_pan->vif->bss_conf.idle &&
+ !ctx_pan->vif->bss_conf.assoc) {
+ slot1 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME;
+ slot0 = IWL_MIN_SLOT_TIME;
+ }
+ } else if (ctx_pan->vif) {
+ slot0 = 0;
+ slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) *
+ ctx_pan->beacon_int;
+ slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1);
+
+ if (test_bit(STATUS_SCAN_HW, &priv->shrd->status)) {
+ slot0 = slot1 * 3 - IWL_MIN_SLOT_TIME;
+ slot1 = IWL_MIN_SLOT_TIME;
+ }
+ }
+
+ cmd.slots[0].width = cpu_to_le16(slot0);
+ cmd.slots[1].width = cpu_to_le16(slot1);
+
+ ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_WIPAN_PARAMS, CMD_SYNC,
+ sizeof(cmd), &cmd);
+ if (ret)
+ IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret);
+
+ return ret;
+}
+
/**
* iwlagn_commit_rxon - commit staging_rxon to hardware
*
@@ -291,12 +419,12 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK);
int ret;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return -EINVAL;
- if (!iwl_is_alive(priv))
+ if (!iwl_is_alive(priv->shrd))
return -EBUSY;
/* This function hardcodes a bunch of dual-mode assumptions */
@@ -305,26 +433,13 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
if (!ctx->is_active)
return 0;
+ /* override BSSID if necessary due to preauth */
+ if (ctx->preauth_bssid)
+ memcpy(ctx->staging.bssid_addr, ctx->bssid, ETH_ALEN);
+
/* always get timestamp with Rx frame */
ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK;
- if (ctx->ctxid == IWL_RXON_CTX_PAN && priv->_agn.hw_roc_channel) {
- struct ieee80211_channel *chan = priv->_agn.hw_roc_channel;
-
- iwl_set_rxon_channel(priv, chan, ctx);
- iwl_set_flags_for_band(priv, ctx, chan->band, NULL);
- ctx->staging.filter_flags |=
- RXON_FILTER_ASSOC_MSK |
- RXON_FILTER_PROMISC_MSK |
- RXON_FILTER_CTL2HOST_MSK;
- ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
- new_assoc = true;
-
- if (memcmp(&ctx->staging, &ctx->active,
- sizeof(ctx->staging)) == 0)
- return 0;
- }
-
/*
* force CTS-to-self frames protection if RTS-CTS is not preferred
* one aggregation protection method
@@ -339,7 +454,7 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
else
ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
- iwl_print_rx_config_cmd(priv, ctx);
+ iwl_print_rx_config_cmd(priv, ctx->ctxid);
ret = iwl_check_rxon_cmd(priv, ctx);
if (ret) {
IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n");
@@ -350,7 +465,7 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
* receive commit_rxon request
* abort any previous channel switch if still in process
*/
- if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) &&
+ if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status) &&
(priv->switch_channel != ctx->staging.channel)) {
IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
le16_to_cpu(priv->switch_channel));
@@ -375,13 +490,11 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
* do it now if after settings changed.
*/
iwl_set_tx_power(priv, priv->tx_power_next, false);
- return 0;
- }
- if (priv->cfg->ops->hcmd->set_pan_params) {
- ret = priv->cfg->ops->hcmd->set_pan_params(priv);
- if (ret)
- return ret;
+ /* make sure we are in the right PS state */
+ iwl_power_update_mode(priv, true);
+
+ return 0;
}
iwl_set_rxon_hwcrypto(priv, ctx, !iwlagn_mod_params.sw_crypto);
@@ -405,6 +518,10 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
if (ret)
return ret;
+ ret = iwlagn_set_pan_params(priv);
+ if (ret)
+ return ret;
+
if (new_assoc)
return iwlagn_rxon_connect(priv, ctx);
@@ -420,16 +537,16 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
const struct iwl_channel_info *ch_info;
int ret = 0;
- IWL_DEBUG_MAC80211(priv, "changed %#x", changed);
+ IWL_DEBUG_MAC80211(priv, "enter: changed %#x", changed);
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
- if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
+ if (unlikely(test_bit(STATUS_SCANNING, &priv->shrd->status))) {
IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
goto out;
}
- if (!iwl_is_ready(priv)) {
+ if (!iwl_is_ready(priv->shrd)) {
IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
goto out;
}
@@ -446,9 +563,8 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
* set up the SM PS mode to OFF if an HT channel is
* configured.
*/
- if (priv->cfg->ops->hcmd->set_rxon_chain)
- for_each_context(priv, ctx)
- priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
+ for_each_context(priv, ctx)
+ iwlagn_set_rxon_chain(priv, ctx);
}
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
@@ -462,7 +578,7 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
goto out;
}
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&priv->shrd->lock, flags);
for_each_context(priv, ctx) {
/* Configure HT40 channels */
@@ -506,7 +622,7 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
ctx->vif);
}
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
iwl_update_bcast_stations(priv);
@@ -538,7 +654,9 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
iwlagn_commit_rxon(priv, ctx);
}
out:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
return ret;
}
@@ -553,7 +671,7 @@ static void iwlagn_check_needed_chains(struct iwl_priv *priv,
struct ieee80211_sta_ht_cap *ht_cap;
bool need_multiple;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
switch (vif->type) {
case NL80211_IFTYPE_STATION:
@@ -636,6 +754,38 @@ static void iwlagn_check_needed_chains(struct iwl_priv *priv,
ht_conf->single_chain_sufficient = !need_multiple;
}
+static void iwlagn_chain_noise_reset(struct iwl_priv *priv)
+{
+ struct iwl_chain_noise_data *data = &priv->chain_noise_data;
+ int ret;
+
+ if ((data->state == IWL_CHAIN_NOISE_ALIVE) &&
+ iwl_is_any_associated(priv)) {
+ struct iwl_calib_chain_noise_reset_cmd cmd;
+
+ /* clear data for chain noise calibration algorithm */
+ data->chain_noise_a = 0;
+ data->chain_noise_b = 0;
+ data->chain_noise_c = 0;
+ data->chain_signal_a = 0;
+ data->chain_signal_b = 0;
+ data->chain_signal_c = 0;
+ data->beacon_count = 0;
+
+ memset(&cmd, 0, sizeof(cmd));
+ iwl_set_calib_hdr(&cmd.hdr,
+ priv->phy_calib_chain_noise_reset_cmd);
+ ret = iwl_trans_send_cmd_pdu(trans(priv),
+ REPLY_PHY_CALIBRATION_CMD,
+ CMD_SYNC, sizeof(cmd), &cmd);
+ if (ret)
+ IWL_ERR(priv,
+ "Could not send REPLY_PHY_CALIBRATION_CMD\n");
+ data->state = IWL_CHAIN_NOISE_ACCUMULATE;
+ IWL_DEBUG_CALIB(priv, "Run chain_noise_calibrate\n");
+ }
+}
+
void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
@@ -646,17 +796,17 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
int ret;
bool force = false;
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
- if (unlikely(!iwl_is_ready(priv))) {
+ if (unlikely(!iwl_is_ready(priv->shrd))) {
IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
return;
}
if (unlikely(!ctx->vif)) {
IWL_DEBUG_MAC80211(priv, "leave - vif is NULL\n");
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
return;
}
@@ -689,10 +839,16 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
*/
if (ctx->last_tx_rejected) {
ctx->last_tx_rejected = false;
- iwl_wake_any_queue(priv, ctx);
+ iwl_trans_wake_any_queue(trans(priv),
+ ctx->ctxid);
}
ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+
+ if (ctx->ctxid == IWL_RXON_CTX_BSS)
+ priv->have_rekey_data = false;
}
+
+ iwlagn_bt_coex_rssi_monitor(priv);
}
if (ctx->ht.enabled) {
@@ -704,8 +860,7 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
iwl_set_rxon_ht(priv, &priv->current_ht_config);
}
- if (priv->cfg->ops->hcmd->set_rxon_chain)
- priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
+ iwlagn_set_rxon_chain(priv, ctx);
if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ))
ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
@@ -743,8 +898,10 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
iwl_power_update_mode(priv, false);
/* Enable RX differential gain and sensitivity calibrations */
- iwl_chain_noise_reset(priv);
+ if (!priv->disable_chain_noise_cal)
+ iwlagn_chain_noise_reset(priv);
priv->start_calib = 1;
+ WARN_ON(ctx->preauth_bssid);
}
if (changes & BSS_CHANGED_IBSS) {
@@ -762,7 +919,7 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
IWL_ERR(priv, "Error sending IBSS beacon\n");
}
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
void iwlagn_post_scan(struct iwl_priv *priv)
@@ -770,6 +927,13 @@ void iwlagn_post_scan(struct iwl_priv *priv)
struct iwl_rxon_context *ctx;
/*
+ * We do not commit power settings while scan is pending,
+ * do it now if the settings changed.
+ */
+ iwl_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false);
+ iwl_set_tx_power(priv, priv->tx_power_next, false);
+
+ /*
* Since setting the RXON may have been deferred while
* performing the scan, fire one off if needed
*/
@@ -777,6 +941,5 @@ void iwlagn_post_scan(struct iwl_priv *priv)
if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
iwlagn_commit_rxon(priv, ctx);
- if (priv->cfg->ops->hcmd->set_pan_params)
- priv->cfg->ops->hcmd->set_pan_params(priv);
+ iwlagn_set_pan_params(priv);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-sta.c b/drivers/net/wireless/iwlwifi/iwl-agn-sta.c
index 0bd722cee5ae..ed6283623932 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-sta.c
@@ -31,56 +31,886 @@
#include "iwl-dev.h"
#include "iwl-core.h"
-#include "iwl-sta.h"
#include "iwl-agn.h"
+#include "iwl-trans.h"
-static struct iwl_link_quality_cmd *
-iwl_sta_alloc_lq(struct iwl_priv *priv, u8 sta_id)
+/* priv->shrd->sta_lock must be held */
+static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
+{
+
+ if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE))
+ IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u "
+ "addr %pM\n",
+ sta_id, priv->stations[sta_id].sta.sta.addr);
+
+ if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) {
+ IWL_DEBUG_ASSOC(priv,
+ "STA id %u addr %pM already present in uCode "
+ "(according to driver)\n",
+ sta_id, priv->stations[sta_id].sta.sta.addr);
+ } else {
+ priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE;
+ IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n",
+ sta_id, priv->stations[sta_id].sta.sta.addr);
+ }
+}
+
+static int iwl_process_add_sta_resp(struct iwl_priv *priv,
+ struct iwl_addsta_cmd *addsta,
+ struct iwl_rx_packet *pkt)
+{
+ u8 sta_id = addsta->sta.sta_id;
+ unsigned long flags;
+ int ret = -EIO;
+
+ if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+ IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n",
+ pkt->hdr.flags);
+ return ret;
+ }
+
+ IWL_DEBUG_INFO(priv, "Processing response for adding station %u\n",
+ sta_id);
+
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+
+ switch (pkt->u.add_sta.status) {
+ case ADD_STA_SUCCESS_MSK:
+ IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n");
+ iwl_sta_ucode_activate(priv, sta_id);
+ ret = 0;
+ break;
+ case ADD_STA_NO_ROOM_IN_TABLE:
+ IWL_ERR(priv, "Adding station %d failed, no room in table.\n",
+ sta_id);
+ break;
+ case ADD_STA_NO_BLOCK_ACK_RESOURCE:
+ IWL_ERR(priv, "Adding station %d failed, no block ack "
+ "resource.\n", sta_id);
+ break;
+ case ADD_STA_MODIFY_NON_EXIST_STA:
+ IWL_ERR(priv, "Attempting to modify non-existing station %d\n",
+ sta_id);
+ break;
+ default:
+ IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n",
+ pkt->u.add_sta.status);
+ break;
+ }
+
+ IWL_DEBUG_INFO(priv, "%s station id %u addr %pM\n",
+ priv->stations[sta_id].sta.mode ==
+ STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
+ sta_id, priv->stations[sta_id].sta.sta.addr);
+
+ /*
+ * XXX: The MAC address in the command buffer is often changed from
+ * the original sent to the device. That is, the MAC address
+ * written to the command buffer often is not the same MAC address
+ * read from the command buffer when the command returns. This
+ * issue has not yet been resolved and this debugging is left to
+ * observe the problem.
+ */
+ IWL_DEBUG_INFO(priv, "%s station according to cmd buffer %pM\n",
+ priv->stations[sta_id].sta.mode ==
+ STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
+ addsta->sta.addr);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+
+ return ret;
+}
+
+int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_addsta_cmd *addsta =
+ (struct iwl_addsta_cmd *) cmd->payload;
+
+ return iwl_process_add_sta_resp(priv, addsta, pkt);
+}
+
+static u16 iwlagn_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data)
+{
+ u16 size = (u16)sizeof(struct iwl_addsta_cmd);
+ struct iwl_addsta_cmd *addsta = (struct iwl_addsta_cmd *)data;
+ memcpy(addsta, cmd, size);
+ /* resrved in 5000 */
+ addsta->rate_n_flags = cpu_to_le16(0);
+ return size;
+}
+
+int iwl_send_add_sta(struct iwl_priv *priv,
+ struct iwl_addsta_cmd *sta, u8 flags)
+{
+ int ret = 0;
+ u8 data[sizeof(*sta)];
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_ADD_STA,
+ .flags = flags,
+ .data = { data, },
+ };
+ u8 sta_id __maybe_unused = sta->sta.sta_id;
+
+ IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n",
+ sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : "");
+
+ if (!(flags & CMD_ASYNC)) {
+ cmd.flags |= CMD_WANT_SKB;
+ might_sleep();
+ }
+
+ cmd.len[0] = iwlagn_build_addsta_hcmd(sta, data);
+ ret = iwl_trans_send_cmd(trans(priv), &cmd);
+
+ if (ret || (flags & CMD_ASYNC))
+ return ret;
+ /*else the command was successfully sent in SYNC mode, need to free
+ * the reply page */
+
+ iwl_free_pages(priv->shrd, cmd.reply_page);
+
+ if (cmd.handler_status)
+ IWL_ERR(priv, "%s - error in the CMD response %d", __func__,
+ cmd.handler_status);
+
+ return cmd.handler_status;
+}
+
+static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
+ struct ieee80211_sta *sta,
+ struct iwl_rxon_context *ctx)
+{
+ struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap;
+ __le32 sta_flags;
+ u8 mimo_ps_mode;
+
+ if (!sta || !sta_ht_inf->ht_supported)
+ goto done;
+
+ mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2;
+ IWL_DEBUG_ASSOC(priv, "spatial multiplexing power save mode: %s\n",
+ (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ?
+ "static" :
+ (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ?
+ "dynamic" : "disabled");
+
+ sta_flags = priv->stations[index].sta.station_flags;
+
+ sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK);
+
+ switch (mimo_ps_mode) {
+ case WLAN_HT_CAP_SM_PS_STATIC:
+ sta_flags |= STA_FLG_MIMO_DIS_MSK;
+ break;
+ case WLAN_HT_CAP_SM_PS_DYNAMIC:
+ sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
+ break;
+ case WLAN_HT_CAP_SM_PS_DISABLED:
+ break;
+ default:
+ IWL_WARN(priv, "Invalid MIMO PS mode %d\n", mimo_ps_mode);
+ break;
+ }
+
+ sta_flags |= cpu_to_le32(
+ (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);
+
+ sta_flags |= cpu_to_le32(
+ (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);
+
+ if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap))
+ sta_flags |= STA_FLG_HT40_EN_MSK;
+ else
+ sta_flags &= ~STA_FLG_HT40_EN_MSK;
+
+ priv->stations[index].sta.station_flags = sta_flags;
+ done:
+ return;
+}
+
+/**
+ * iwl_prep_station - Prepare station information for addition
+ *
+ * should be called with sta_lock held
+ */
+u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ const u8 *addr, bool is_ap, struct ieee80211_sta *sta)
+{
+ struct iwl_station_entry *station;
+ int i;
+ u8 sta_id = IWL_INVALID_STATION;
+
+ if (is_ap)
+ sta_id = ctx->ap_sta_id;
+ else if (is_broadcast_ether_addr(addr))
+ sta_id = ctx->bcast_sta_id;
+ else
+ for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) {
+ if (!compare_ether_addr(priv->stations[i].sta.sta.addr,
+ addr)) {
+ sta_id = i;
+ break;
+ }
+
+ if (!priv->stations[i].used &&
+ sta_id == IWL_INVALID_STATION)
+ sta_id = i;
+ }
+
+ /*
+ * These two conditions have the same outcome, but keep them
+ * separate
+ */
+ if (unlikely(sta_id == IWL_INVALID_STATION))
+ return sta_id;
+
+ /*
+ * uCode is not able to deal with multiple requests to add a
+ * station. Keep track if one is in progress so that we do not send
+ * another.
+ */
+ if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
+ IWL_DEBUG_INFO(priv, "STA %d already in process of being "
+ "added.\n", sta_id);
+ return sta_id;
+ }
+
+ if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
+ (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) &&
+ !compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) {
+ IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not "
+ "adding again.\n", sta_id, addr);
+ return sta_id;
+ }
+
+ station = &priv->stations[sta_id];
+ station->used = IWL_STA_DRIVER_ACTIVE;
+ IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n",
+ sta_id, addr);
+ priv->num_stations++;
+
+ /* Set up the REPLY_ADD_STA command to send to device */
+ memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd));
+ memcpy(station->sta.sta.addr, addr, ETH_ALEN);
+ station->sta.mode = 0;
+ station->sta.sta.sta_id = sta_id;
+ station->sta.station_flags = ctx->station_flags;
+ station->ctxid = ctx->ctxid;
+
+ if (sta) {
+ struct iwl_station_priv *sta_priv;
+
+ sta_priv = (void *)sta->drv_priv;
+ sta_priv->ctx = ctx;
+ }
+
+ /*
+ * OK to call unconditionally, since local stations (IBSS BSSID
+ * STA and broadcast STA) pass in a NULL sta, and mac80211
+ * doesn't allow HT IBSS.
+ */
+ iwl_set_ht_add_station(priv, sta_id, sta, ctx);
+
+ return sta_id;
+
+}
+
+#define STA_WAIT_TIMEOUT (HZ/2)
+
+/**
+ * iwl_add_station_common -
+ */
+int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ const u8 *addr, bool is_ap,
+ struct ieee80211_sta *sta, u8 *sta_id_r)
+{
+ unsigned long flags_spin;
+ int ret = 0;
+ u8 sta_id;
+ struct iwl_addsta_cmd sta_cmd;
+
+ *sta_id_r = 0;
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags_spin);
+ sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta);
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_ERR(priv, "Unable to prepare station %pM for addition\n",
+ addr);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin);
+ return -EINVAL;
+ }
+
+ /*
+ * uCode is not able to deal with multiple requests to add a
+ * station. Keep track if one is in progress so that we do not send
+ * another.
+ */
+ if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
+ IWL_DEBUG_INFO(priv, "STA %d already in process of being "
+ "added.\n", sta_id);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin);
+ return -EEXIST;
+ }
+
+ if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
+ (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
+ IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not "
+ "adding again.\n", sta_id, addr);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin);
+ return -EEXIST;
+ }
+
+ priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS;
+ memcpy(&sta_cmd, &priv->stations[sta_id].sta,
+ sizeof(struct iwl_addsta_cmd));
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin);
+
+ /* Add station to device's station table */
+ ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+ if (ret) {
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags_spin);
+ IWL_ERR(priv, "Adding station %pM failed.\n",
+ priv->stations[sta_id].sta.sta.addr);
+ priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
+ priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin);
+ }
+ *sta_id_r = sta_id;
+ return ret;
+}
+
+/**
+ * iwl_sta_ucode_deactivate - deactivate ucode status for a station
+ *
+ * priv->shrd->sta_lock must be held
+ */
+static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id)
+{
+ /* Ucode must be active and driver must be non active */
+ if ((priv->stations[sta_id].used &
+ (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) !=
+ IWL_STA_UCODE_ACTIVE)
+ IWL_ERR(priv, "removed non active STA %u\n", sta_id);
+
+ priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE;
+
+ memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry));
+ IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id);
+}
+
+static int iwl_send_remove_station(struct iwl_priv *priv,
+ const u8 *addr, int sta_id,
+ bool temporary)
+{
+ struct iwl_rx_packet *pkt;
+ int ret;
+
+ unsigned long flags_spin;
+ struct iwl_rem_sta_cmd rm_sta_cmd;
+
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_REMOVE_STA,
+ .len = { sizeof(struct iwl_rem_sta_cmd), },
+ .flags = CMD_SYNC,
+ .data = { &rm_sta_cmd, },
+ };
+
+ memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
+ rm_sta_cmd.num_sta = 1;
+ memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN);
+
+ cmd.flags |= CMD_WANT_SKB;
+
+ ret = iwl_trans_send_cmd(trans(priv), &cmd);
+
+ if (ret)
+ return ret;
+
+ pkt = (struct iwl_rx_packet *)cmd.reply_page;
+ if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+ IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n",
+ pkt->hdr.flags);
+ ret = -EIO;
+ }
+
+ if (!ret) {
+ switch (pkt->u.rem_sta.status) {
+ case REM_STA_SUCCESS_MSK:
+ if (!temporary) {
+ spin_lock_irqsave(&priv->shrd->sta_lock,
+ flags_spin);
+ iwl_sta_ucode_deactivate(priv, sta_id);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock,
+ flags_spin);
+ }
+ IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
+ break;
+ default:
+ ret = -EIO;
+ IWL_ERR(priv, "REPLY_REMOVE_STA failed\n");
+ break;
+ }
+ }
+ iwl_free_pages(priv->shrd, cmd.reply_page);
+
+ return ret;
+}
+
+/**
+ * iwl_remove_station - Remove driver's knowledge of station.
+ */
+int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
+ const u8 *addr)
+{
+ unsigned long flags;
+
+ if (!iwl_is_ready(priv->shrd)) {
+ IWL_DEBUG_INFO(priv,
+ "Unable to remove station %pM, device not ready.\n",
+ addr);
+ /*
+ * It is typical for stations to be removed when we are
+ * going down. Return success since device will be down
+ * soon anyway
+ */
+ return 0;
+ }
+
+ IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n",
+ sta_id, addr);
+
+ if (WARN_ON(sta_id == IWL_INVALID_STATION))
+ return -EINVAL;
+
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+
+ if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
+ IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n",
+ addr);
+ goto out_err;
+ }
+
+ if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
+ IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n",
+ addr);
+ goto out_err;
+ }
+
+ if (priv->stations[sta_id].used & IWL_STA_LOCAL) {
+ kfree(priv->stations[sta_id].lq);
+ priv->stations[sta_id].lq = NULL;
+ }
+
+ priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
+
+ priv->num_stations--;
+
+ if (WARN_ON(priv->num_stations < 0))
+ priv->num_stations = 0;
+
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+
+ return iwl_send_remove_station(priv, addr, sta_id, false);
+out_err:
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+ return -EINVAL;
+}
+
+/**
+ * iwl_clear_ucode_stations - clear ucode station table bits
+ *
+ * This function clears all the bits in the driver indicating
+ * which stations are active in the ucode. Call when something
+ * other than explicit station management would cause this in
+ * the ucode, e.g. unassociated RXON.
+ */
+void iwl_clear_ucode_stations(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ int i;
+ unsigned long flags_spin;
+ bool cleared = false;
+
+ IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n");
+
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags_spin);
+ for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
+ if (ctx && ctx->ctxid != priv->stations[i].ctxid)
+ continue;
+
+ if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) {
+ IWL_DEBUG_INFO(priv,
+ "Clearing ucode active for station %d\n", i);
+ priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
+ cleared = true;
+ }
+ }
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin);
+
+ if (!cleared)
+ IWL_DEBUG_INFO(priv,
+ "No active stations found to be cleared\n");
+}
+
+/**
+ * iwl_restore_stations() - Restore driver known stations to device
+ *
+ * All stations considered active by driver, but not present in ucode, is
+ * restored.
+ *
+ * Function sleeps.
+ */
+void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
+{
+ struct iwl_addsta_cmd sta_cmd;
+ struct iwl_link_quality_cmd lq;
+ unsigned long flags_spin;
+ int i;
+ bool found = false;
+ int ret;
+ bool send_lq;
+
+ if (!iwl_is_ready(priv->shrd)) {
+ IWL_DEBUG_INFO(priv,
+ "Not ready yet, not restoring any stations.\n");
+ return;
+ }
+
+ IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n");
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags_spin);
+ for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
+ if (ctx->ctxid != priv->stations[i].ctxid)
+ continue;
+ if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) &&
+ !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) {
+ IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n",
+ priv->stations[i].sta.sta.addr);
+ priv->stations[i].sta.mode = 0;
+ priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS;
+ found = true;
+ }
+ }
+
+ for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
+ if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) {
+ memcpy(&sta_cmd, &priv->stations[i].sta,
+ sizeof(struct iwl_addsta_cmd));
+ send_lq = false;
+ if (priv->stations[i].lq) {
+ if (priv->shrd->wowlan)
+ iwl_sta_fill_lq(priv, ctx, i, &lq);
+ else
+ memcpy(&lq, priv->stations[i].lq,
+ sizeof(struct iwl_link_quality_cmd));
+ send_lq = true;
+ }
+ spin_unlock_irqrestore(&priv->shrd->sta_lock,
+ flags_spin);
+ ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+ if (ret) {
+ spin_lock_irqsave(&priv->shrd->sta_lock,
+ flags_spin);
+ IWL_ERR(priv, "Adding station %pM failed.\n",
+ priv->stations[i].sta.sta.addr);
+ priv->stations[i].used &=
+ ~IWL_STA_DRIVER_ACTIVE;
+ priv->stations[i].used &=
+ ~IWL_STA_UCODE_INPROGRESS;
+ spin_unlock_irqrestore(&priv->shrd->sta_lock,
+ flags_spin);
+ }
+ /*
+ * Rate scaling has already been initialized, send
+ * current LQ command
+ */
+ if (send_lq)
+ iwl_send_lq_cmd(priv, ctx, &lq,
+ CMD_SYNC, true);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags_spin);
+ priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
+ }
+ }
+
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin);
+ if (!found)
+ IWL_DEBUG_INFO(priv, "Restoring all known stations .... "
+ "no stations to be restored.\n");
+ else
+ IWL_DEBUG_INFO(priv, "Restoring all known stations .... "
+ "complete.\n");
+}
+
+void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
+{
+ unsigned long flags;
+ int sta_id = ctx->ap_sta_id;
+ int ret;
+ struct iwl_addsta_cmd sta_cmd;
+ struct iwl_link_quality_cmd lq;
+ bool active;
+
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+ if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+ return;
+ }
+
+ memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd));
+ sta_cmd.mode = 0;
+ memcpy(&lq, priv->stations[sta_id].lq, sizeof(lq));
+
+ active = priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE;
+ priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+
+ if (active) {
+ ret = iwl_send_remove_station(
+ priv, priv->stations[sta_id].sta.sta.addr,
+ sta_id, true);
+ if (ret)
+ IWL_ERR(priv, "failed to remove STA %pM (%d)\n",
+ priv->stations[sta_id].sta.sta.addr, ret);
+ }
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+ priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE;
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+
+ ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+ if (ret)
+ IWL_ERR(priv, "failed to re-add STA %pM (%d)\n",
+ priv->stations[sta_id].sta.sta.addr, ret);
+ iwl_send_lq_cmd(priv, ctx, &lq, CMD_SYNC, true);
+}
+
+int iwl_get_free_ucode_key_offset(struct iwl_priv *priv)
+{
+ int i;
+
+ for (i = 0; i < priv->sta_key_max_num; i++)
+ if (!test_and_set_bit(i, &priv->ucode_key_table))
+ return i;
+
+ return WEP_INVALID_OFFSET;
+}
+
+void iwl_dealloc_bcast_stations(struct iwl_priv *priv)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+ for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
+ if (!(priv->stations[i].used & IWL_STA_BCAST))
+ continue;
+
+ priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
+ priv->num_stations--;
+ if (WARN_ON(priv->num_stations < 0))
+ priv->num_stations = 0;
+ kfree(priv->stations[i].lq);
+ priv->stations[i].lq = NULL;
+ }
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+static void iwl_dump_lq_cmd(struct iwl_priv *priv,
+ struct iwl_link_quality_cmd *lq)
+{
+ int i;
+ IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id);
+ IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n",
+ lq->general_params.single_stream_ant_msk,
+ lq->general_params.dual_stream_ant_msk);
+
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
+ IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n",
+ i, lq->rs_table[i].rate_n_flags);
+}
+#else
+static inline void iwl_dump_lq_cmd(struct iwl_priv *priv,
+ struct iwl_link_quality_cmd *lq)
+{
+}
+#endif
+
+/**
+ * is_lq_table_valid() - Test one aspect of LQ cmd for validity
+ *
+ * It sometimes happens when a HT rate has been in use and we
+ * loose connectivity with AP then mac80211 will first tell us that the
+ * current channel is not HT anymore before removing the station. In such a
+ * scenario the RXON flags will be updated to indicate we are not
+ * communicating HT anymore, but the LQ command may still contain HT rates.
+ * Test for this to prevent driver from sending LQ command between the time
+ * RXON flags are updated and when LQ command is updated.
+ */
+static bool is_lq_table_valid(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct iwl_link_quality_cmd *lq)
+{
+ int i;
+
+ if (ctx->ht.enabled)
+ return true;
+
+ IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n",
+ ctx->active.channel);
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+ if (le32_to_cpu(lq->rs_table[i].rate_n_flags) &
+ RATE_MCS_HT_MSK) {
+ IWL_DEBUG_INFO(priv,
+ "index %d of LQ expects HT channel\n",
+ i);
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * iwl_send_lq_cmd() - Send link quality command
+ * @init: This command is sent as part of station initialization right
+ * after station has been added.
+ *
+ * The link quality command is sent as the last step of station creation.
+ * This is the special case in which init is set and we call a callback in
+ * this case to clear the state indicating that station creation is in
+ * progress.
+ */
+int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ struct iwl_link_quality_cmd *lq, u8 flags, bool init)
+{
+ int ret = 0;
+ unsigned long flags_spin;
+
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_TX_LINK_QUALITY_CMD,
+ .len = { sizeof(struct iwl_link_quality_cmd), },
+ .flags = flags,
+ .data = { lq, },
+ };
+
+ if (WARN_ON(lq->sta_id == IWL_INVALID_STATION))
+ return -EINVAL;
+
+
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags_spin);
+ if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin);
+
+ iwl_dump_lq_cmd(priv, lq);
+ if (WARN_ON(init && (cmd.flags & CMD_ASYNC)))
+ return -EINVAL;
+
+ if (is_lq_table_valid(priv, ctx, lq))
+ ret = iwl_trans_send_cmd(trans(priv), &cmd);
+ else
+ ret = -EINVAL;
+
+ if (cmd.flags & CMD_ASYNC)
+ return ret;
+
+ if (init) {
+ IWL_DEBUG_INFO(priv, "init LQ command complete, "
+ "clearing sta addition status for sta %d\n",
+ lq->sta_id);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags_spin);
+ priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin);
+ }
+ return ret;
+}
+
+int iwlagn_mac_sta_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_priv *priv = hw->priv;
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ int ret;
+
+ IWL_DEBUG_MAC80211(priv, "enter: received request to remove "
+ "station %pM\n", sta->addr);
+ mutex_lock(&priv->shrd->mutex);
+ IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n",
+ sta->addr);
+ ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr);
+ if (ret)
+ IWL_ERR(priv, "Error removing station %pM\n",
+ sta->addr);
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ return ret;
+}
+
+void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ u8 sta_id, struct iwl_link_quality_cmd *link_cmd)
{
int i, r;
- struct iwl_link_quality_cmd *link_cmd;
u32 rate_flags = 0;
__le32 rate_n_flags;
- link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL);
- if (!link_cmd) {
- IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n");
- return NULL;
- }
+ lockdep_assert_held(&priv->shrd->mutex);
+
+ memset(link_cmd, 0, sizeof(*link_cmd));
+
/* Set up the rate scaling to start at selected rate, fall back
* all the way down to 1M in IEEE order, and then spin on 1M */
if (priv->band == IEEE80211_BAND_5GHZ)
r = IWL_RATE_6M_INDEX;
+ else if (ctx && ctx->vif && ctx->vif->p2p)
+ r = IWL_RATE_6M_INDEX;
else
r = IWL_RATE_1M_INDEX;
if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
rate_flags |= RATE_MCS_CCK_MSK;
- rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
+ rate_flags |= first_antenna(hw_params(priv).valid_tx_ant) <<
RATE_MCS_ANT_POS;
rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
link_cmd->rs_table[i].rate_n_flags = rate_n_flags;
link_cmd->general_params.single_stream_ant_msk =
- first_antenna(priv->hw_params.valid_tx_ant);
+ first_antenna(hw_params(priv).valid_tx_ant);
link_cmd->general_params.dual_stream_ant_msk =
- priv->hw_params.valid_tx_ant &
- ~first_antenna(priv->hw_params.valid_tx_ant);
+ hw_params(priv).valid_tx_ant &
+ ~first_antenna(hw_params(priv).valid_tx_ant);
if (!link_cmd->general_params.dual_stream_ant_msk) {
link_cmd->general_params.dual_stream_ant_msk = ANT_AB;
- } else if (num_of_ant(priv->hw_params.valid_tx_ant) == 2) {
+ } else if (num_of_ant(hw_params(priv).valid_tx_ant) == 2) {
link_cmd->general_params.dual_stream_ant_msk =
- priv->hw_params.valid_tx_ant;
+ hw_params(priv).valid_tx_ant;
}
- link_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
+ link_cmd->agg_params.agg_dis_start_th =
+ LINK_QUAL_AGG_DISABLE_START_DEF;
link_cmd->agg_params.agg_time_limit =
cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
link_cmd->sta_id = sta_id;
+}
+
+static struct iwl_link_quality_cmd *
+iwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ u8 sta_id)
+{
+ struct iwl_link_quality_cmd *link_cmd;
+
+ link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL);
+ if (!link_cmd) {
+ IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n");
+ return NULL;
+ }
+
+ iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd);
return link_cmd;
}
@@ -90,7 +920,8 @@ iwl_sta_alloc_lq(struct iwl_priv *priv, u8 sta_id)
*
* Function sleeps.
*/
-int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+int iwlagn_add_bssid_station(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
const u8 *addr, u8 *sta_id_r)
{
int ret;
@@ -110,14 +941,15 @@ int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx
if (sta_id_r)
*sta_id_r = sta_id;
- spin_lock_irqsave(&priv->sta_lock, flags);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
priv->stations[sta_id].used |= IWL_STA_LOCAL;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
/* Set up default rate scaling table in device's station table */
- link_cmd = iwl_sta_alloc_lq(priv, sta_id);
+ link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
if (!link_cmd) {
- IWL_ERR(priv, "Unable to initialize rate scaling for station %pM.\n",
+ IWL_ERR(priv,
+ "Unable to initialize rate scaling for station %pM.\n",
addr);
return -ENOMEM;
}
@@ -126,13 +958,21 @@ int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx
if (ret)
IWL_ERR(priv, "Link quality command failed (%d)\n", ret);
- spin_lock_irqsave(&priv->sta_lock, flags);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
priv->stations[sta_id].lq = link_cmd;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
return 0;
}
+/*
+ * static WEP keys
+ *
+ * For each context, the device has a table of 4 static WEP keys
+ * (one for each key index) that is updated with the following
+ * commands.
+ */
+
static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv,
struct iwl_rxon_context *ctx,
bool send_if_empty)
@@ -175,7 +1015,7 @@ static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv,
cmd.len[0] = cmd_size;
if (not_empty || send_if_empty)
- return iwl_send_cmd(priv, &cmd);
+ return iwl_trans_send_cmd(trans(priv), &cmd);
else
return 0;
}
@@ -183,7 +1023,7 @@ static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv,
int iwl_restore_default_wep_keys(struct iwl_priv *priv,
struct iwl_rxon_context *ctx)
{
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
return iwl_send_static_wepkey_cmd(priv, ctx, false);
}
@@ -194,14 +1034,15 @@ int iwl_remove_default_wep_key(struct iwl_priv *priv,
{
int ret;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n",
keyconf->keyidx);
memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0]));
- if (iwl_is_rfkill(priv)) {
- IWL_DEBUG_WEP(priv, "Not sending REPLY_WEPKEY command due to RFKILL.\n");
+ if (iwl_is_rfkill(priv->shrd)) {
+ IWL_DEBUG_WEP(priv,
+ "Not sending REPLY_WEPKEY command due to RFKILL.\n");
/* but keys in device are clear anyway so return success */
return 0;
}
@@ -218,17 +1059,16 @@ int iwl_set_default_wep_key(struct iwl_priv *priv,
{
int ret;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
if (keyconf->keylen != WEP_KEY_LEN_128 &&
keyconf->keylen != WEP_KEY_LEN_64) {
- IWL_DEBUG_WEP(priv, "Bad WEP key length %d\n", keyconf->keylen);
+ IWL_DEBUG_WEP(priv,
+ "Bad WEP key length %d\n", keyconf->keylen);
return -EINVAL;
}
- keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
- keyconf->hw_key_idx = HW_KEY_DEFAULT;
- priv->stations[ctx->ap_sta_id].keyinfo.cipher = keyconf->cipher;
+ keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT;
ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen;
memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key,
@@ -241,166 +1081,117 @@ int iwl_set_default_wep_key(struct iwl_priv *priv,
return ret;
}
-static int iwl_set_wep_dynamic_key_info(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *keyconf,
- u8 sta_id)
-{
- unsigned long flags;
- __le16 key_flags = 0;
- struct iwl_addsta_cmd sta_cmd;
-
- lockdep_assert_held(&priv->mutex);
-
- keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
-
- key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK);
- key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
- key_flags &= ~STA_KEY_FLG_INVALID;
-
- if (keyconf->keylen == WEP_KEY_LEN_128)
- key_flags |= STA_KEY_FLG_KEY_SIZE_MSK;
-
- if (sta_id == ctx->bcast_sta_id)
- key_flags |= STA_KEY_MULTICAST_MSK;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
-
- priv->stations[sta_id].keyinfo.cipher = keyconf->cipher;
- priv->stations[sta_id].keyinfo.keylen = keyconf->keylen;
- priv->stations[sta_id].keyinfo.keyidx = keyconf->keyidx;
-
- memcpy(priv->stations[sta_id].keyinfo.key,
- keyconf->key, keyconf->keylen);
-
- memcpy(&priv->stations[sta_id].sta.key.key[3],
- keyconf->key, keyconf->keylen);
-
- if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK)
- == STA_KEY_FLG_NO_ENC)
- priv->stations[sta_id].sta.key.key_offset =
- iwl_get_free_ucode_key_index(priv);
- /* else, we are overriding an existing key => no need to allocated room
- * in uCode. */
+/*
+ * dynamic (per-station) keys
+ *
+ * The dynamic keys are a little more complicated. The device has
+ * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys.
+ * These are linked to stations by a table that contains an index
+ * into the key table for each station/key index/{mcast,unicast},
+ * i.e. it's basically an array of pointers like this:
+ * key_offset_t key_mapping[NUM_STATIONS][4][2];
+ * (it really works differently, but you can think of it as such)
+ *
+ * The key uploading and linking happens in the same command, the
+ * add station command with STA_MODIFY_KEY_MASK.
+ */
- WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
- "no space for a new key");
+static u8 iwlagn_key_sta_id(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+ u8 sta_id = IWL_INVALID_STATION;
- priv->stations[sta_id].sta.key.key_flags = key_flags;
- priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ if (sta)
+ sta_id = iwl_sta_id(sta);
- memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ /*
+ * The device expects GTKs for station interfaces to be
+ * installed as GTKs for the AP station. If we have no
+ * station ID, then use the ap_sta_id in that case.
+ */
+ if (!sta && vif && vif_priv->ctx) {
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ sta_id = vif_priv->ctx->ap_sta_id;
+ break;
+ default:
+ /*
+ * In all other cases, the key will be
+ * used either for TX only or is bound
+ * to a station already.
+ */
+ break;
+ }
+ }
- return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+ return sta_id;
}
-static int iwl_set_ccmp_dynamic_key_info(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *keyconf,
- u8 sta_id)
+static int iwlagn_send_sta_key(struct iwl_priv *priv,
+ struct ieee80211_key_conf *keyconf,
+ u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
+ u32 cmd_flags)
{
unsigned long flags;
- __le16 key_flags = 0;
+ __le16 key_flags;
struct iwl_addsta_cmd sta_cmd;
+ int i;
- lockdep_assert_held(&priv->mutex);
-
- key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK);
- key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
- key_flags &= ~STA_KEY_FLG_INVALID;
-
- if (sta_id == ctx->bcast_sta_id)
- key_flags |= STA_KEY_MULTICAST_MSK;
-
- keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
- priv->stations[sta_id].keyinfo.cipher = keyconf->cipher;
- priv->stations[sta_id].keyinfo.keylen = keyconf->keylen;
-
- memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key,
- keyconf->keylen);
-
- memcpy(priv->stations[sta_id].sta.key.key, keyconf->key,
- keyconf->keylen);
-
- if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK)
- == STA_KEY_FLG_NO_ENC)
- priv->stations[sta_id].sta.key.key_offset =
- iwl_get_free_ucode_key_index(priv);
- /* else, we are overriding an existing key => no need to allocated room
- * in uCode. */
-
- WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
- "no space for a new key");
-
- priv->stations[sta_id].sta.key.key_flags = key_flags;
- priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
-
- memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
-}
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+ memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd));
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
-static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *keyconf,
- u8 sta_id)
-{
- unsigned long flags;
- int ret = 0;
- __le16 key_flags = 0;
+ key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
+ key_flags |= STA_KEY_FLG_MAP_KEY_MSK;
- key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK);
- key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
- key_flags &= ~STA_KEY_FLG_INVALID;
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ key_flags |= STA_KEY_FLG_CCMP;
+ memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ key_flags |= STA_KEY_FLG_TKIP;
+ sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32;
+ for (i = 0; i < 5; i++)
+ sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
+ memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen);
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key_flags |= STA_KEY_FLG_KEY_SIZE_MSK;
+ /* fall through */
+ case WLAN_CIPHER_SUITE_WEP40:
+ key_flags |= STA_KEY_FLG_WEP;
+ memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen);
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
- if (sta_id == ctx->bcast_sta_id)
+ if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
key_flags |= STA_KEY_MULTICAST_MSK;
- keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
- keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
-
- priv->stations[sta_id].keyinfo.cipher = keyconf->cipher;
- priv->stations[sta_id].keyinfo.keylen = 16;
-
- if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK)
- == STA_KEY_FLG_NO_ENC)
- priv->stations[sta_id].sta.key.key_offset =
- iwl_get_free_ucode_key_index(priv);
- /* else, we are overriding an existing key => no need to allocated room
- * in uCode. */
-
- WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
- "no space for a new key");
+ /* key pointer (offset) */
+ sta_cmd.key.key_offset = keyconf->hw_key_idx;
- priv->stations[sta_id].sta.key.key_flags = key_flags;
+ sta_cmd.key.key_flags = key_flags;
+ sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
+ sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
-
- /* This copy is acutally not needed: we get the key with each TX */
- memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, 16);
-
- memcpy(priv->stations[sta_id].sta.key.key, keyconf->key, 16);
-
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- return ret;
+ return iwl_send_add_sta(priv, &sta_cmd, cmd_flags);
}
void iwl_update_tkip_key(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
+ struct ieee80211_vif *vif,
struct ieee80211_key_conf *keyconf,
struct ieee80211_sta *sta, u32 iv32, u16 *phase1key)
{
- u8 sta_id;
- unsigned long flags;
- int i;
+ u8 sta_id = iwlagn_key_sta_id(priv, vif, sta);
+
+ if (sta_id == IWL_INVALID_STATION)
+ return;
if (iwl_scan_cancel(priv)) {
/* cancel scan failed, just live w/ bad key and rely
@@ -408,121 +1199,110 @@ void iwl_update_tkip_key(struct iwl_priv *priv,
return;
}
- sta_id = iwl_sta_id_or_broadcast(priv, ctx, sta);
- if (sta_id == IWL_INVALID_STATION)
- return;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
-
- priv->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32;
-
- for (i = 0; i < 5; i++)
- priv->stations[sta_id].sta.key.tkip_rx_ttak[i] =
- cpu_to_le16(phase1key[i]);
-
- priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
-
- iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
-
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
+ iwlagn_send_sta_key(priv, keyconf, sta_id,
+ iv32, phase1key, CMD_ASYNC);
}
int iwl_remove_dynamic_key(struct iwl_priv *priv,
struct iwl_rxon_context *ctx,
struct ieee80211_key_conf *keyconf,
- u8 sta_id)
+ struct ieee80211_sta *sta)
{
unsigned long flags;
- u16 key_flags;
- u8 keyidx;
struct iwl_addsta_cmd sta_cmd;
+ u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta);
- lockdep_assert_held(&priv->mutex);
+ /* if station isn't there, neither is the key */
+ if (sta_id == IWL_INVALID_STATION)
+ return -ENOENT;
- ctx->key_mapping_keys--;
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+ memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd));
+ if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE))
+ sta_id = IWL_INVALID_STATION;
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
- spin_lock_irqsave(&priv->sta_lock, flags);
- key_flags = le16_to_cpu(priv->stations[sta_id].sta.key.key_flags);
- keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3;
+ if (sta_id == IWL_INVALID_STATION)
+ return 0;
- IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n",
- keyconf->keyidx, sta_id);
+ lockdep_assert_held(&priv->shrd->mutex);
- if (keyconf->keyidx != keyidx) {
- /* We need to remove a key with index different that the one
- * in the uCode. This means that the key we need to remove has
- * been replaced by another one with different index.
- * Don't do anything and return ok
- */
- spin_unlock_irqrestore(&priv->sta_lock, flags);
- return 0;
- }
+ ctx->key_mapping_keys--;
- if (priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET) {
- IWL_WARN(priv, "Removing wrong key %d 0x%x\n",
- keyconf->keyidx, key_flags);
- spin_unlock_irqrestore(&priv->sta_lock, flags);
- return 0;
- }
+ IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n",
+ keyconf->keyidx, sta_id);
- if (!test_and_clear_bit(priv->stations[sta_id].sta.key.key_offset,
- &priv->ucode_key_table))
- IWL_ERR(priv, "index %d not used in uCode key table.\n",
- priv->stations[sta_id].sta.key.key_offset);
- memset(&priv->stations[sta_id].keyinfo, 0,
- sizeof(struct iwl_hw_key));
- memset(&priv->stations[sta_id].sta.key, 0,
- sizeof(struct iwl_keyinfo));
- priv->stations[sta_id].sta.key.key_flags =
- STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID;
- priv->stations[sta_id].sta.key.key_offset = WEP_INVALID_OFFSET;
- priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table))
+ IWL_ERR(priv, "offset %d not used in uCode key table.\n",
+ keyconf->hw_key_idx);
- if (iwl_is_rfkill(priv)) {
- IWL_DEBUG_WEP(priv, "Not sending REPLY_ADD_STA command because RFKILL enabled.\n");
- spin_unlock_irqrestore(&priv->sta_lock, flags);
- return 0;
- }
- memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ sta_cmd.key.key_flags = STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID;
+ sta_cmd.key.key_offset = WEP_INVALID_OFFSET;
+ sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
+ sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
}
-int iwl_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *keyconf, u8 sta_id)
+int iwl_set_dynamic_key(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta)
{
+ struct ieee80211_key_seq seq;
+ u16 p1k[5];
int ret;
+ u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta);
+ const u8 *addr;
+
+ if (sta_id == IWL_INVALID_STATION)
+ return -EINVAL;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
+
+ keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv);
+ if (keyconf->hw_key_idx == WEP_INVALID_OFFSET)
+ return -ENOSPC;
ctx->key_mapping_keys++;
- keyconf->hw_key_idx = HW_KEY_DYNAMIC;
switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_CCMP:
- ret = iwl_set_ccmp_dynamic_key_info(priv, ctx, keyconf, sta_id);
- break;
case WLAN_CIPHER_SUITE_TKIP:
- ret = iwl_set_tkip_dynamic_key_info(priv, ctx, keyconf, sta_id);
+ keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ if (sta)
+ addr = sta->addr;
+ else /* station mode case only */
+ addr = ctx->active.bssid_addr;
+
+ /* pre-fill phase 1 key into device cache */
+ ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+ ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
+ ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
+ seq.tkip.iv32, p1k, CMD_SYNC);
break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ /* fall through */
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
- ret = iwl_set_wep_dynamic_key_info(priv, ctx, keyconf, sta_id);
+ ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
+ 0, NULL, CMD_SYNC);
break;
default:
- IWL_ERR(priv,
- "Unknown alg: %s cipher = %x\n", __func__,
- keyconf->cipher);
+ IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher);
ret = -EINVAL;
}
- IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n",
+ if (ret) {
+ ctx->key_mapping_keys--;
+ clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table);
+ }
+
+ IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
keyconf->cipher, keyconf->keylen, keyconf->keyidx,
- sta_id, ret);
+ sta ? sta->addr : NULL, ret);
return ret;
}
@@ -541,29 +1321,29 @@ int iwlagn_alloc_bcast_station(struct iwl_priv *priv,
unsigned long flags;
u8 sta_id;
- spin_lock_irqsave(&priv->sta_lock, flags);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL);
if (sta_id == IWL_INVALID_STATION) {
IWL_ERR(priv, "Unable to prepare broadcast station\n");
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
return -EINVAL;
}
priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE;
priv->stations[sta_id].used |= IWL_STA_BCAST;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
- link_cmd = iwl_sta_alloc_lq(priv, sta_id);
+ link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
if (!link_cmd) {
IWL_ERR(priv,
"Unable to initialize rate scaling for bcast station.\n");
return -ENOMEM;
}
- spin_lock_irqsave(&priv->sta_lock, flags);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
priv->stations[sta_id].lq = link_cmd;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
return 0;
}
@@ -574,26 +1354,26 @@ int iwlagn_alloc_bcast_station(struct iwl_priv *priv,
* Only used by iwlagn. Placed here to have all bcast station management
* code together.
*/
-static int iwl_update_bcast_station(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
+int iwl_update_bcast_station(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
{
unsigned long flags;
struct iwl_link_quality_cmd *link_cmd;
u8 sta_id = ctx->bcast_sta_id;
- link_cmd = iwl_sta_alloc_lq(priv, sta_id);
+ link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
if (!link_cmd) {
IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n");
return -ENOMEM;
}
- spin_lock_irqsave(&priv->sta_lock, flags);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
if (priv->stations[sta_id].lq)
kfree(priv->stations[sta_id].lq);
else
IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n");
priv->stations[sta_id].lq = link_cmd;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
return 0;
}
@@ -620,15 +1400,15 @@ int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid)
unsigned long flags;
struct iwl_addsta_cmd sta_cmd;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
/* Remove "disable" flag, to enable Tx for this TID */
- spin_lock_irqsave(&priv->sta_lock, flags);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid));
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
}
@@ -640,20 +1420,20 @@ int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
int sta_id;
struct iwl_addsta_cmd sta_cmd;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
sta_id = iwl_sta_id(sta);
if (sta_id == IWL_INVALID_STATION)
return -ENXIO;
- spin_lock_irqsave(&priv->sta_lock, flags);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
priv->stations[sta_id].sta.station_flags_msk = 0;
priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid;
priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
}
@@ -665,7 +1445,7 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
int sta_id;
struct iwl_addsta_cmd sta_cmd;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
sta_id = iwl_sta_id(sta);
if (sta_id == IWL_INVALID_STATION) {
@@ -673,13 +1453,13 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
return -ENXIO;
}
- spin_lock_irqsave(&priv->sta_lock, flags);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
priv->stations[sta_id].sta.station_flags_msk = 0;
priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid;
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
}
@@ -688,14 +1468,14 @@ static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
{
unsigned long flags;
- spin_lock_irqsave(&priv->sta_lock, flags);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK;
priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
priv->stations[sta_id].sta.sta.modify_mask = 0;
priv->stations[sta_id].sta.sleep_tx_count = 0;
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
}
@@ -703,7 +1483,7 @@ void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt)
{
unsigned long flags;
- spin_lock_irqsave(&priv->sta_lock, flags);
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
priv->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK;
priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
priv->stations[sta_id].sta.sta.modify_mask =
@@ -711,7 +1491,7 @@ void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt)
priv->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt);
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
}
@@ -724,6 +1504,8 @@ void iwlagn_mac_sta_notify(struct ieee80211_hw *hw,
struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
int sta_id;
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
switch (cmd) {
case STA_NOTIFY_SLEEP:
WARN_ON(!sta_priv->client);
@@ -743,4 +1525,5 @@ void iwlagn_mac_sta_notify(struct ieee80211_hw *hw,
default:
break;
}
+ IWL_DEBUG_MAC80211(priv, "leave\n");
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tt.c b/drivers/net/wireless/iwlwifi/iwl-agn-tt.c
index 348f74f1c8e8..c27180a73351 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tt.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tt.c
@@ -114,9 +114,6 @@ static bool iwl_within_ct_kill_margin(struct iwl_priv *priv)
s32 temp = priv->temperature; /* degrees CELSIUS except specified */
bool within_margin = false;
- if (priv->cfg->base_params->temperature_kelvin)
- temp = KELVIN_TO_CELSIUS(priv->temperature);
-
if (!priv->thermal_throttle.advanced_tt)
within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
CT_KILL_THRESHOLD_LEGACY) ? true : false;
@@ -176,29 +173,29 @@ static void iwl_tt_check_exit_ct_kill(unsigned long data)
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
unsigned long flags;
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
if (tt->state == IWL_TI_CT_KILL) {
if (priv->thermal_throttle.ct_kill_toggle) {
- iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
+ iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_CLR,
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
priv->thermal_throttle.ct_kill_toggle = false;
} else {
- iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
+ iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_SET,
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
priv->thermal_throttle.ct_kill_toggle = true;
}
- iwl_read32(priv, CSR_UCODE_DRV_GP1);
- spin_lock_irqsave(&priv->reg_lock, flags);
- if (!iwl_grab_nic_access(priv))
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ iwl_read32(bus(priv), CSR_UCODE_DRV_GP1);
+ spin_lock_irqsave(&bus(priv)->reg_lock, flags);
+ if (!iwl_grab_nic_access(bus(priv)))
+ iwl_release_nic_access(bus(priv));
+ spin_unlock_irqrestore(&bus(priv)->reg_lock, flags);
/* Reschedule the ct_kill timer to occur in
* CT_KILL_EXIT_DURATION seconds to ensure we get a
* thermal update */
- IWL_DEBUG_POWER(priv, "schedule ct_kill exit timer\n");
+ IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n");
mod_timer(&priv->thermal_throttle.ct_kill_exit_tm,
jiffies + CT_KILL_EXIT_DURATION * HZ);
}
@@ -208,15 +205,15 @@ static void iwl_perform_ct_kill_task(struct iwl_priv *priv,
bool stop)
{
if (stop) {
- IWL_DEBUG_POWER(priv, "Stop all queues\n");
+ IWL_DEBUG_TEMP(priv, "Stop all queues\n");
if (priv->mac80211_registered)
ieee80211_stop_queues(priv->hw);
- IWL_DEBUG_POWER(priv,
+ IWL_DEBUG_TEMP(priv,
"Schedule 5 seconds CT_KILL Timer\n");
mod_timer(&priv->thermal_throttle.ct_kill_exit_tm,
jiffies + CT_KILL_EXIT_DURATION * HZ);
} else {
- IWL_DEBUG_POWER(priv, "Wake all queues\n");
+ IWL_DEBUG_TEMP(priv, "Wake all queues\n");
if (priv->mac80211_registered)
ieee80211_wake_queues(priv->hw);
}
@@ -227,22 +224,22 @@ static void iwl_tt_ready_for_ct_kill(unsigned long data)
struct iwl_priv *priv = (struct iwl_priv *)data;
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
/* temperature timer expired, ready to go into CT_KILL state */
if (tt->state != IWL_TI_CT_KILL) {
- IWL_DEBUG_POWER(priv, "entering CT_KILL state when "
+ IWL_DEBUG_TEMP(priv, "entering CT_KILL state when "
"temperature timer expired\n");
tt->state = IWL_TI_CT_KILL;
- set_bit(STATUS_CT_KILL, &priv->status);
+ set_bit(STATUS_CT_KILL, &priv->shrd->status);
iwl_perform_ct_kill_task(priv, true);
}
}
static void iwl_prepare_ct_kill_task(struct iwl_priv *priv)
{
- IWL_DEBUG_POWER(priv, "Prepare to enter IWL_TI_CT_KILL\n");
+ IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n");
/* make request to retrieve statistics information */
iwl_send_statistics_request(priv, CMD_SYNC, false);
/* Reschedule the ct_kill wait timer */
@@ -273,7 +270,7 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
(temp > tt->tt_previous_temp) &&
((temp - tt->tt_previous_temp) >
IWL_TT_INCREASE_MARGIN)) {
- IWL_DEBUG_POWER(priv,
+ IWL_DEBUG_TEMP(priv,
"Temperature increase %d degree Celsius\n",
(temp - tt->tt_previous_temp));
}
@@ -313,23 +310,24 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
tt->tt_power_mode = IWL_POWER_INDEX_5;
break;
}
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
if (old_state == IWL_TI_CT_KILL)
- clear_bit(STATUS_CT_KILL, &priv->status);
+ clear_bit(STATUS_CT_KILL, &priv->shrd->status);
if (tt->state != IWL_TI_CT_KILL &&
iwl_power_update_mode(priv, true)) {
/* TT state not updated
* try again during next temperature read
*/
if (old_state == IWL_TI_CT_KILL)
- set_bit(STATUS_CT_KILL, &priv->status);
+ set_bit(STATUS_CT_KILL, &priv->shrd->status);
tt->state = old_state;
IWL_ERR(priv, "Cannot update power mode, "
"TT state not updated\n");
} else {
if (tt->state == IWL_TI_CT_KILL) {
if (force) {
- set_bit(STATUS_CT_KILL, &priv->status);
+ set_bit(STATUS_CT_KILL,
+ &priv->shrd->status);
iwl_perform_ct_kill_task(priv, true);
} else {
iwl_prepare_ct_kill_task(priv);
@@ -338,12 +336,12 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
} else if (old_state == IWL_TI_CT_KILL &&
tt->state != IWL_TI_CT_KILL)
iwl_perform_ct_kill_task(priv, false);
- IWL_DEBUG_POWER(priv, "Temperature state changed %u\n",
+ IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n",
tt->state);
- IWL_DEBUG_POWER(priv, "Power Index change to %u\n",
+ IWL_DEBUG_TEMP(priv, "Power Index change to %u\n",
tt->tt_power_mode);
}
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
}
@@ -397,7 +395,7 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
(temp > tt->tt_previous_temp) &&
((temp - tt->tt_previous_temp) >
IWL_TT_INCREASE_MARGIN)) {
- IWL_DEBUG_POWER(priv,
+ IWL_DEBUG_TEMP(priv,
"Temperature increase %d "
"degree Celsius\n",
(temp - tt->tt_previous_temp));
@@ -453,9 +451,9 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
* in case get disabled before */
iwl_set_rxon_ht(priv, &priv->current_ht_config);
}
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
if (old_state == IWL_TI_CT_KILL)
- clear_bit(STATUS_CT_KILL, &priv->status);
+ clear_bit(STATUS_CT_KILL, &priv->shrd->status);
if (tt->state != IWL_TI_CT_KILL &&
iwl_power_update_mode(priv, true)) {
/* TT state not updated
@@ -464,18 +462,19 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
IWL_ERR(priv, "Cannot update power mode, "
"TT state not updated\n");
if (old_state == IWL_TI_CT_KILL)
- set_bit(STATUS_CT_KILL, &priv->status);
+ set_bit(STATUS_CT_KILL, &priv->shrd->status);
tt->state = old_state;
} else {
- IWL_DEBUG_POWER(priv,
+ IWL_DEBUG_TEMP(priv,
"Thermal Throttling to new state: %u\n",
tt->state);
if (old_state != IWL_TI_CT_KILL &&
tt->state == IWL_TI_CT_KILL) {
if (force) {
- IWL_DEBUG_POWER(priv,
+ IWL_DEBUG_TEMP(priv,
"Enter IWL_TI_CT_KILL\n");
- set_bit(STATUS_CT_KILL, &priv->status);
+ set_bit(STATUS_CT_KILL,
+ &priv->shrd->status);
iwl_perform_ct_kill_task(priv, true);
} else {
iwl_prepare_ct_kill_task(priv);
@@ -483,11 +482,11 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
}
} else if (old_state == IWL_TI_CT_KILL &&
tt->state != IWL_TI_CT_KILL) {
- IWL_DEBUG_POWER(priv, "Exit IWL_TI_CT_KILL\n");
+ IWL_DEBUG_TEMP(priv, "Exit IWL_TI_CT_KILL\n");
iwl_perform_ct_kill_task(priv, false);
}
}
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
}
@@ -506,10 +505,10 @@ static void iwl_bg_ct_enter(struct work_struct *work)
struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter);
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
- if (!iwl_is_ready(priv))
+ if (!iwl_is_ready(priv->shrd))
return;
if (tt->state != IWL_TI_CT_KILL) {
@@ -535,10 +534,10 @@ static void iwl_bg_ct_exit(struct work_struct *work)
struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit);
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
- if (!iwl_is_ready(priv))
+ if (!iwl_is_ready(priv->shrd))
return;
/* stop ct_kill_exit_tm timer */
@@ -565,20 +564,20 @@ static void iwl_bg_ct_exit(struct work_struct *work)
void iwl_tt_enter_ct_kill(struct iwl_priv *priv)
{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
- IWL_DEBUG_POWER(priv, "Queueing critical temperature enter.\n");
- queue_work(priv->workqueue, &priv->ct_enter);
+ IWL_DEBUG_TEMP(priv, "Queueing critical temperature enter.\n");
+ queue_work(priv->shrd->workqueue, &priv->ct_enter);
}
void iwl_tt_exit_ct_kill(struct iwl_priv *priv)
{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
- IWL_DEBUG_POWER(priv, "Queueing critical temperature exit.\n");
- queue_work(priv->workqueue, &priv->ct_exit);
+ IWL_DEBUG_TEMP(priv, "Queueing critical temperature exit.\n");
+ queue_work(priv->shrd->workqueue, &priv->ct_exit);
}
static void iwl_bg_tt_work(struct work_struct *work)
@@ -586,12 +585,9 @@ static void iwl_bg_tt_work(struct work_struct *work)
struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work);
s32 temp = priv->temperature; /* degrees CELSIUS except specified */
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
- if (priv->cfg->base_params->temperature_kelvin)
- temp = KELVIN_TO_CELSIUS(priv->temperature);
-
if (!priv->thermal_throttle.advanced_tt)
iwl_legacy_tt_handler(priv, temp, false);
else
@@ -600,11 +596,11 @@ static void iwl_bg_tt_work(struct work_struct *work)
void iwl_tt_handler(struct iwl_priv *priv)
{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
- IWL_DEBUG_POWER(priv, "Queueing thermal throttling work.\n");
- queue_work(priv->workqueue, &priv->tt_work);
+ IWL_DEBUG_TEMP(priv, "Queueing thermal throttling work.\n");
+ queue_work(priv->shrd->workqueue, &priv->tt_work);
}
/* Thermal throttling initialization
@@ -618,7 +614,7 @@ void iwl_tt_initialize(struct iwl_priv *priv)
int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1);
struct iwl_tt_trans *transaction;
- IWL_DEBUG_POWER(priv, "Initialize Thermal Throttling\n");
+ IWL_DEBUG_TEMP(priv, "Initialize Thermal Throttling\n");
memset(tt, 0, sizeof(struct iwl_tt_mgmt));
@@ -638,12 +634,14 @@ void iwl_tt_initialize(struct iwl_priv *priv)
INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit);
if (priv->cfg->base_params->adv_thermal_throttle) {
- IWL_DEBUG_POWER(priv, "Advanced Thermal Throttling\n");
- tt->restriction = kzalloc(sizeof(struct iwl_tt_restriction) *
- IWL_TI_STATE_MAX, GFP_KERNEL);
- tt->transaction = kzalloc(sizeof(struct iwl_tt_trans) *
- IWL_TI_STATE_MAX * (IWL_TI_STATE_MAX - 1),
- GFP_KERNEL);
+ IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n");
+ tt->restriction = kcalloc(IWL_TI_STATE_MAX,
+ sizeof(struct iwl_tt_restriction),
+ GFP_KERNEL);
+ tt->transaction = kcalloc(IWL_TI_STATE_MAX *
+ (IWL_TI_STATE_MAX - 1),
+ sizeof(struct iwl_tt_trans),
+ GFP_KERNEL);
if (!tt->restriction || !tt->transaction) {
IWL_ERR(priv, "Fallback to Legacy Throttling\n");
priv->thermal_throttle.advanced_tt = false;
@@ -671,7 +669,7 @@ void iwl_tt_initialize(struct iwl_priv *priv)
priv->thermal_throttle.advanced_tt = true;
}
} else {
- IWL_DEBUG_POWER(priv, "Legacy Thermal Throttling\n");
+ IWL_DEBUG_TEMP(priv, "Legacy Thermal Throttling\n");
priv->thermal_throttle.advanced_tt = false;
}
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tt.h b/drivers/net/wireless/iwlwifi/iwl-agn-tt.h
index d118ed29bf3f..7282a23e8f1c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tt.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tt.h
@@ -117,7 +117,6 @@ struct iwl_tt_mgmt {
u8 iwl_tt_current_power_mode(struct iwl_priv *priv);
bool iwl_tt_is_low_power_state(struct iwl_priv *priv);
bool iwl_ht_enabled(struct iwl_priv *priv);
-bool iwl_check_for_ct_kill(struct iwl_priv *priv);
enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv);
enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv);
void iwl_tt_enter_ct_kill(struct iwl_priv *priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index 4974cd7837cb..35a6b71f358c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -31,341 +31,47 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
+#include <linux/ieee80211.h>
#include "iwl-dev.h"
#include "iwl-core.h"
-#include "iwl-sta.h"
#include "iwl-io.h"
-#include "iwl-helpers.h"
#include "iwl-agn-hw.h"
#include "iwl-agn.h"
+#include "iwl-trans.h"
-/*
- * mac80211 queues, ACs, hardware queues, FIFOs.
- *
- * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues
- *
- * Mac80211 uses the following numbers, which we get as from it
- * by way of skb_get_queue_mapping(skb):
- *
- * VO 0
- * VI 1
- * BE 2
- * BK 3
- *
- *
- * Regular (not A-MPDU) frames are put into hardware queues corresponding
- * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their
- * own queue per aggregation session (RA/TID combination), such queues are
- * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In
- * order to map frames to the right queue, we also need an AC->hw queue
- * mapping. This is implemented here.
- *
- * Due to the way hw queues are set up (by the hw specific modules like
- * iwl-4965.c, iwl-5000.c etc.), the AC->hw queue mapping is the identity
- * mapping.
- */
-
-static const u8 tid_to_ac[] = {
- IEEE80211_AC_BE,
- IEEE80211_AC_BK,
- IEEE80211_AC_BK,
- IEEE80211_AC_BE,
- IEEE80211_AC_VI,
- IEEE80211_AC_VI,
- IEEE80211_AC_VO,
- IEEE80211_AC_VO
-};
-
-static inline int get_ac_from_tid(u16 tid)
-{
- if (likely(tid < ARRAY_SIZE(tid_to_ac)))
- return tid_to_ac[tid];
-
- /* no support for TIDs 8-15 yet */
- return -EINVAL;
-}
-
-static inline int get_fifo_from_tid(struct iwl_rxon_context *ctx, u16 tid)
-{
- if (likely(tid < ARRAY_SIZE(tid_to_ac)))
- return ctx->ac_to_fifo[tid_to_ac[tid]];
-
- /* no support for TIDs 8-15 yet */
- return -EINVAL;
-}
-
-/**
- * iwlagn_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
- */
-static void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
- struct iwl_tx_queue *txq,
- u16 byte_cnt)
-{
- struct iwlagn_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
- int write_ptr = txq->q.write_ptr;
- int txq_id = txq->q.id;
- u8 sec_ctl = 0;
- u8 sta_id = 0;
- u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
- __le16 bc_ent;
-
- WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX);
-
- sta_id = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id;
- sec_ctl = txq->cmd[txq->q.write_ptr]->cmd.tx.sec_ctl;
-
- switch (sec_ctl & TX_CMD_SEC_MSK) {
- case TX_CMD_SEC_CCM:
- len += CCMP_MIC_LEN;
- break;
- case TX_CMD_SEC_TKIP:
- len += TKIP_ICV_LEN;
- break;
- case TX_CMD_SEC_WEP:
- len += WEP_IV_LEN + WEP_ICV_LEN;
- break;
- }
-
- bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12));
-
- scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
-
- if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
- scd_bc_tbl[txq_id].
- tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
-}
-
-static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_priv *priv,
- struct iwl_tx_queue *txq)
-{
- struct iwlagn_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
- int txq_id = txq->q.id;
- int read_ptr = txq->q.read_ptr;
- u8 sta_id = 0;
- __le16 bc_ent;
-
- WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
-
- if (txq_id != priv->cmd_queue)
- sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id;
-
- bc_ent = cpu_to_le16(1 | (sta_id << 12));
- scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
-
- if (read_ptr < TFD_QUEUE_SIZE_BC_DUP)
- scd_bc_tbl[txq_id].
- tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
-}
-
-static int iwlagn_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid,
- u16 txq_id)
-{
- u32 tbl_dw_addr;
- u32 tbl_dw;
- u16 scd_q2ratid;
-
- scd_q2ratid = ra_tid & IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK;
-
- tbl_dw_addr = priv->scd_base_addr +
- IWLAGN_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id);
-
- tbl_dw = iwl_read_targ_mem(priv, tbl_dw_addr);
-
- if (txq_id & 0x1)
- tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
- else
- tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
-
- iwl_write_targ_mem(priv, tbl_dw_addr, tbl_dw);
-
- return 0;
-}
-
-static void iwlagn_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id)
-{
- /* Simply stop the queue, but don't change any configuration;
- * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
- iwl_write_prph(priv,
- IWLAGN_SCD_QUEUE_STATUS_BITS(txq_id),
- (0 << IWLAGN_SCD_QUEUE_STTS_REG_POS_ACTIVE)|
- (1 << IWLAGN_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
-}
-
-void iwlagn_set_wr_ptrs(struct iwl_priv *priv,
- int txq_id, u32 index)
-{
- iwl_write_direct32(priv, HBUS_TARG_WRPTR,
- (index & 0xff) | (txq_id << 8));
- iwl_write_prph(priv, IWLAGN_SCD_QUEUE_RDPTR(txq_id), index);
-}
-
-void iwlagn_tx_queue_set_status(struct iwl_priv *priv,
- struct iwl_tx_queue *txq,
- int tx_fifo_id, int scd_retry)
-{
- int txq_id = txq->q.id;
- int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0;
-
- iwl_write_prph(priv, IWLAGN_SCD_QUEUE_STATUS_BITS(txq_id),
- (active << IWLAGN_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
- (tx_fifo_id << IWLAGN_SCD_QUEUE_STTS_REG_POS_TXF) |
- (1 << IWLAGN_SCD_QUEUE_STTS_REG_POS_WSL) |
- IWLAGN_SCD_QUEUE_STTS_REG_MSK);
-
- txq->sched_retry = scd_retry;
-
- IWL_DEBUG_INFO(priv, "%s %s Queue %d on FIFO %d\n",
- active ? "Activate" : "Deactivate",
- scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id);
-}
-
-static int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, int sta_id, int tid)
-{
- if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
- (IWLAGN_FIRST_AMPDU_QUEUE +
- priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) {
- IWL_WARN(priv,
- "queue number out of range: %d, must be %d to %d\n",
- txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
- IWLAGN_FIRST_AMPDU_QUEUE +
- priv->cfg->base_params->num_of_ampdu_queues - 1);
- return -EINVAL;
- }
-
- /* Modify device's station table to Tx this TID */
- return iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
-}
-
-void iwlagn_txq_agg_queue_setup(struct iwl_priv *priv,
- struct ieee80211_sta *sta,
- int tid, int frame_limit)
-{
- int sta_id, tx_fifo, txq_id, ssn_idx;
- u16 ra_tid;
- unsigned long flags;
- struct iwl_tid_data *tid_data;
-
- sta_id = iwl_sta_id(sta);
- if (WARN_ON(sta_id == IWL_INVALID_STATION))
- return;
- if (WARN_ON(tid >= MAX_TID_COUNT))
- return;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
- tid_data = &priv->stations[sta_id].tid[tid];
- ssn_idx = SEQ_TO_SN(tid_data->seq_number);
- txq_id = tid_data->agg.txq_id;
- tx_fifo = tid_data->agg.tx_fifo;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- ra_tid = BUILD_RAxTID(sta_id, tid);
-
- spin_lock_irqsave(&priv->lock, flags);
-
- /* Stop this Tx queue before configuring it */
- iwlagn_tx_queue_stop_scheduler(priv, txq_id);
-
- /* Map receiver-address / traffic-ID to this queue */
- iwlagn_tx_queue_set_q2ratid(priv, ra_tid, txq_id);
-
- /* Set this queue as a chain-building queue */
- iwl_set_bits_prph(priv, IWLAGN_SCD_QUEUECHAIN_SEL, (1<<txq_id));
-
- /* enable aggregations for the queue */
- iwl_set_bits_prph(priv, IWLAGN_SCD_AGGR_SEL, (1<<txq_id));
-
- /* Place first TFD at index corresponding to start sequence number.
- * Assumes that ssn_idx is valid (!= 0xFFF) */
- priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
- priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
- iwlagn_set_wr_ptrs(priv, txq_id, ssn_idx);
-
- /* Set up Tx window size and frame limit for this queue */
- iwl_write_targ_mem(priv, priv->scd_base_addr +
- IWLAGN_SCD_CONTEXT_QUEUE_OFFSET(txq_id) +
- sizeof(u32),
- ((frame_limit <<
- IWLAGN_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
- IWLAGN_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
- ((frame_limit <<
- IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
- IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
-
- iwl_set_bits_prph(priv, IWLAGN_SCD_INTERRUPT_MASK, (1 << txq_id));
-
- /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
- iwlagn_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static int iwlagn_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
- u16 ssn_idx, u8 tx_fifo)
+static void iwlagn_tx_cmd_protection(struct iwl_priv *priv,
+ struct ieee80211_tx_info *info,
+ __le16 fc, __le32 *tx_flags)
{
- if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
- (IWLAGN_FIRST_AMPDU_QUEUE +
- priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) {
- IWL_ERR(priv,
- "queue number out of range: %d, must be %d to %d\n",
- txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
- IWLAGN_FIRST_AMPDU_QUEUE +
- priv->cfg->base_params->num_of_ampdu_queues - 1);
- return -EINVAL;
- }
-
- iwlagn_tx_queue_stop_scheduler(priv, txq_id);
-
- iwl_clear_bits_prph(priv, IWLAGN_SCD_AGGR_SEL, (1 << txq_id));
-
- priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
- priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
- /* supposes that ssn_idx is valid (!= 0xFFF) */
- iwlagn_set_wr_ptrs(priv, txq_id, ssn_idx);
-
- iwl_clear_bits_prph(priv, IWLAGN_SCD_INTERRUPT_MASK, (1 << txq_id));
- iwl_txq_ctx_deactivate(priv, txq_id);
- iwlagn_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
-
- return 0;
-}
-
-/*
- * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask
- * must be called under priv->lock and mac access
- */
-void iwlagn_txq_set_sched(struct iwl_priv *priv, u32 mask)
-{
- iwl_write_prph(priv, IWLAGN_SCD_TXFACT, mask);
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS ||
+ info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT ||
+ info->flags & IEEE80211_TX_CTL_AMPDU)
+ *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK;
}
/*
* handle build REPLY_TX command notification.
*/
static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
- struct sk_buff *skb,
- struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info,
- struct ieee80211_hdr *hdr,
- u8 std_id)
+ struct sk_buff *skb,
+ struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_hdr *hdr, u8 sta_id)
{
__le16 fc = hdr->frame_control;
__le32 tx_flags = tx_cmd->tx_flags;
tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
+
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
tx_flags |= TX_CMD_FLG_ACK_MSK;
- if (ieee80211_is_mgmt(fc))
- tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
- if (ieee80211_is_probe_resp(fc) &&
- !(le16_to_cpu(hdr->seq_ctrl) & 0xf))
- tx_flags |= TX_CMD_FLG_TSF_MSK;
- } else {
- tx_flags &= (~TX_CMD_FLG_ACK_MSK);
- tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
- }
+ else
+ tx_flags &= ~TX_CMD_FLG_ACK_MSK;
- if (ieee80211_is_back_req(fc))
+ if (ieee80211_is_probe_resp(fc))
+ tx_flags |= TX_CMD_FLG_TSF_MSK;
+ else if (ieee80211_is_back_req(fc))
tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
else if (info->band == IEEE80211_BAND_2GHZ &&
priv->cfg->bt_params &&
@@ -376,7 +82,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
tx_flags |= TX_CMD_FLG_IGNORE_BT;
- tx_cmd->sta_id = std_id;
+ tx_cmd->sta_id = sta_id;
if (ieee80211_has_morefrags(fc))
tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK;
@@ -388,7 +94,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
}
- priv->cfg->ops->utils->tx_cmd_protection(priv, info, fc, &tx_flags);
+ iwlagn_tx_cmd_protection(priv, info, fc, &tx_flags);
tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
if (ieee80211_is_mgmt(fc)) {
@@ -405,12 +111,10 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
tx_cmd->next_frame_len = 0;
}
-#define RTS_DFAULT_RETRY_LIMIT 60
-
static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
- struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info,
- __le16 fc)
+ struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ __le16 fc)
{
u32 rate_flags;
int rate_idx;
@@ -418,17 +122,25 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
u8 data_retry_limit;
u8 rate_plcp;
- /* Set retry limit on DATA packets and Probe Responses*/
- if (ieee80211_is_probe_resp(fc))
- data_retry_limit = 3;
- else
- data_retry_limit = IWLAGN_DEFAULT_TX_RETRY;
- tx_cmd->data_retry_limit = data_retry_limit;
+ if (priv->shrd->wowlan) {
+ rts_retry_limit = IWLAGN_LOW_RETRY_LIMIT;
+ data_retry_limit = IWLAGN_LOW_RETRY_LIMIT;
+ } else {
+ /* Set retry limit on RTS packets */
+ rts_retry_limit = IWLAGN_RTS_DFAULT_RETRY_LIMIT;
+
+ /* Set retry limit on DATA packets and Probe Responses*/
+ if (ieee80211_is_probe_resp(fc)) {
+ data_retry_limit = IWLAGN_MGMT_DFAULT_RETRY_LIMIT;
+ rts_retry_limit =
+ min(data_retry_limit, rts_retry_limit);
+ } else if (ieee80211_is_back_req(fc))
+ data_retry_limit = IWLAGN_BAR_DFAULT_RETRY_LIMIT;
+ else
+ data_retry_limit = IWLAGN_DEFAULT_TX_RETRY;
+ }
- /* Set retry limit on RTS packets */
- rts_retry_limit = RTS_DFAULT_RETRY_LIMIT;
- if (data_retry_limit < rts_retry_limit)
- rts_retry_limit = data_retry_limit;
+ tx_cmd->data_retry_limit = data_retry_limit;
tx_cmd->rts_retry_limit = rts_retry_limit;
/* DATA packets will use the uCode station table for rate/antenna
@@ -436,6 +148,18 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
if (ieee80211_is_data(fc)) {
tx_cmd->initial_rate_index = 0;
tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
+#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL
+ if (priv->tm_fixed_rate) {
+ /*
+ * rate overwrite by testmode
+ * we not only send lq command to change rate
+ * we also re-enforce per data pkt base.
+ */
+ tx_cmd->tx_flags &= ~TX_CMD_FLG_STA_RATE_MSK;
+ memcpy(&tx_cmd->rate_n_flags, &priv->tm_fixed_rate,
+ sizeof(tx_cmd->rate_n_flags));
+ }
+#endif
return;
}
@@ -468,10 +192,10 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
priv->bt_full_concurrent) {
/* operated as 1x1 in full concurrency mode */
priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
- first_antenna(priv->hw_params.valid_tx_ant));
+ first_antenna(hw_params(priv).valid_tx_ant));
} else
priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
- priv->hw_params.valid_tx_ant);
+ hw_params(priv).valid_tx_ant);
rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
/* Set the rate in the TX cmd */
@@ -497,8 +221,7 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
case WLAN_CIPHER_SUITE_TKIP:
tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
- ieee80211_get_tkip_key(keyconf, skb_frag,
- IEEE80211_TKIP_P2_KEY, tx_cmd->key);
+ ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n");
break;
@@ -528,41 +251,23 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_sta *sta = info->control.sta;
struct iwl_station_priv *sta_priv = NULL;
- struct iwl_tx_queue *txq;
- struct iwl_queue *q;
- struct iwl_device_cmd *out_cmd;
- struct iwl_cmd_meta *out_meta;
- struct iwl_tx_cmd *tx_cmd;
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- int txq_id;
- dma_addr_t phys_addr = 0;
- dma_addr_t txcmd_phys;
- dma_addr_t scratch_phys;
- u16 len, firstlen, secondlen;
- u16 seq_number = 0;
+ struct iwl_device_cmd *dev_cmd = NULL;
+ struct iwl_tx_cmd *tx_cmd;
+
__le16 fc;
u8 hdr_len;
+ u16 len;
u8 sta_id;
- u8 wait_write_ptr = 0;
- u8 tid = 0;
- u8 *qc = NULL;
unsigned long flags;
bool is_agg = false;
- /*
- * If the frame needs to go out off-channel, then
- * we'll have put the PAN context to that channel,
- * so make the frame go out there.
- */
- if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
- ctx = &priv->contexts[IWL_RXON_CTX_PAN];
- else if (info->control.vif)
+ if (info->control.vif)
ctx = iwl_rxon_ctx_from_vif(info->control.vif);
- spin_lock_irqsave(&priv->lock, flags);
- if (iwl_is_rfkill(priv)) {
+ spin_lock_irqsave(&priv->shrd->lock, flags);
+ if (iwl_is_rfkill(priv->shrd)) {
IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n");
goto drop_unlock_priv;
}
@@ -595,11 +300,11 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
IWL_DEBUG_TX(priv, "station Id %d\n", sta_id);
- if (sta)
- sta_priv = (void *)sta->drv_priv;
+ if (info->control.sta)
+ sta_priv = (void *)info->control.sta->drv_priv;
if (sta_priv && sta_priv->asleep &&
- (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) {
+ (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) {
/*
* This sends an asynchronous command to the device,
* but we can rely on it being processed before the
@@ -612,76 +317,19 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
iwl_sta_modify_sleep_tx_count(priv, sta_id, 1);
}
- /*
- * Send this frame after DTIM -- there's a special queue
- * reserved for this for contexts that support AP mode.
- */
- if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
- txq_id = ctx->mcast_queue;
- /*
- * The microcode will clear the more data
- * bit in the last frame it transmits.
- */
- hdr->frame_control |=
- cpu_to_le16(IEEE80211_FCTL_MOREDATA);
- } else
- txq_id = ctx->ac_to_queue[skb_get_queue_mapping(skb)];
-
- /* irqs already disabled/saved above when locking priv->lock */
- spin_lock(&priv->sta_lock);
+ if (info->flags & IEEE80211_TX_CTL_AMPDU)
+ is_agg = true;
- if (ieee80211_is_data_qos(fc)) {
- qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
-
- if (WARN_ON_ONCE(tid >= MAX_TID_COUNT))
- goto drop_unlock_sta;
-
- seq_number = priv->stations[sta_id].tid[tid].seq_number;
- seq_number &= IEEE80211_SCTL_SEQ;
- hdr->seq_ctrl = hdr->seq_ctrl &
- cpu_to_le16(IEEE80211_SCTL_FRAG);
- hdr->seq_ctrl |= cpu_to_le16(seq_number);
- seq_number += 0x10;
- /* aggregation is on for this <sta,tid> */
- if (info->flags & IEEE80211_TX_CTL_AMPDU &&
- priv->stations[sta_id].tid[tid].agg.state == IWL_AGG_ON) {
- txq_id = priv->stations[sta_id].tid[tid].agg.txq_id;
- is_agg = true;
- }
- }
+ /* irqs already disabled/saved above when locking priv->shrd->lock */
+ spin_lock(&priv->shrd->sta_lock);
- txq = &priv->txq[txq_id];
- q = &txq->q;
+ dev_cmd = kmem_cache_alloc(priv->tx_cmd_pool, GFP_ATOMIC);
- if (unlikely(iwl_queue_space(q) < q->high_mark))
+ if (unlikely(!dev_cmd))
goto drop_unlock_sta;
- /* Set up driver data for this TFD */
- memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info));
- txq->txb[q->write_ptr].skb = skb;
- txq->txb[q->write_ptr].ctx = ctx;
-
- /* Set up first empty entry in queue's array of Tx/cmd buffers */
- out_cmd = txq->cmd[q->write_ptr];
- out_meta = &txq->meta[q->write_ptr];
- tx_cmd = &out_cmd->cmd.tx;
- memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr));
- memset(tx_cmd, 0, sizeof(struct iwl_tx_cmd));
-
- /*
- * Set up the Tx-command (not MAC!) header.
- * Store the chosen Tx queue and TFD index within the sequence field;
- * after Tx, uCode's Tx response will return this value so driver can
- * locate the frame within the tx queue and do post-tx processing.
- */
- out_cmd->hdr.cmd = REPLY_TX;
- out_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) |
- INDEX_TO_SEQ(q->write_ptr)));
-
- /* Copy MAC header from skb into command buffer */
- memcpy(tx_cmd->hdr, hdr, hdr_len);
-
+ memset(dev_cmd, 0, sizeof(*dev_cmd));
+ tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload;
/* Total # bytes to be transmitted */
len = (u16)skb->len;
@@ -697,109 +345,17 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, fc);
iwl_update_stats(priv, true, fc, len);
- /*
- * Use the first empty entry in this queue's command buffer array
- * to contain the Tx command and MAC header concatenated together
- * (payload data will be in another buffer).
- * Size of this varies, due to varying MAC header length.
- * If end is not dword aligned, we'll have 2 extra bytes at the end
- * of the MAC header (device reads on dword boundaries).
- * We'll tell device about this padding later.
- */
- len = sizeof(struct iwl_tx_cmd) +
- sizeof(struct iwl_cmd_header) + hdr_len;
- firstlen = (len + 3) & ~3;
-
- /* Tell NIC about any 2-byte padding after MAC header */
- if (firstlen != len)
- tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
-
- /* Physical address of this Tx command's header (not MAC header!),
- * within command buffer array. */
- txcmd_phys = pci_map_single(priv->pci_dev,
- &out_cmd->hdr, firstlen,
- PCI_DMA_BIDIRECTIONAL);
- if (unlikely(pci_dma_mapping_error(priv->pci_dev, txcmd_phys)))
- goto drop_unlock_sta;
- dma_unmap_addr_set(out_meta, mapping, txcmd_phys);
- dma_unmap_len_set(out_meta, len, firstlen);
-
- if (!ieee80211_has_morefrags(hdr->frame_control)) {
- txq->need_update = 1;
- } else {
- wait_write_ptr = 1;
- txq->need_update = 0;
- }
-
- /* Set up TFD's 2nd entry to point directly to remainder of skb,
- * if any (802.11 null frames have no payload). */
- secondlen = skb->len - hdr_len;
- if (secondlen > 0) {
- phys_addr = pci_map_single(priv->pci_dev, skb->data + hdr_len,
- secondlen, PCI_DMA_TODEVICE);
- if (unlikely(pci_dma_mapping_error(priv->pci_dev, phys_addr))) {
- pci_unmap_single(priv->pci_dev,
- dma_unmap_addr(out_meta, mapping),
- dma_unmap_len(out_meta, len),
- PCI_DMA_BIDIRECTIONAL);
- goto drop_unlock_sta;
- }
- }
-
- if (ieee80211_is_data_qos(fc)) {
- priv->stations[sta_id].tid[tid].tfds_in_queue++;
- if (!ieee80211_has_morefrags(fc))
- priv->stations[sta_id].tid[tid].seq_number = seq_number;
- }
- spin_unlock(&priv->sta_lock);
-
- /* Attach buffers to TFD */
- iwlagn_txq_attach_buf_to_tfd(priv, txq, txcmd_phys, firstlen, 1);
- if (secondlen > 0)
- iwlagn_txq_attach_buf_to_tfd(priv, txq, phys_addr,
- secondlen, 0);
-
- scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) +
- offsetof(struct iwl_tx_cmd, scratch);
-
- /* take back ownership of DMA buffer to enable update */
- pci_dma_sync_single_for_cpu(priv->pci_dev, txcmd_phys,
- firstlen, PCI_DMA_BIDIRECTIONAL);
- tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
- tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys);
-
- IWL_DEBUG_TX(priv, "sequence nr = 0X%x\n",
- le16_to_cpu(out_cmd->hdr.sequence));
- IWL_DEBUG_TX(priv, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags));
- iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd));
- iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len);
-
- /* Set up entry for this TFD in Tx byte-count array */
- if (info->flags & IEEE80211_TX_CTL_AMPDU)
- iwlagn_txq_update_byte_cnt_tbl(priv, txq,
- le16_to_cpu(tx_cmd->len));
+ memset(&info->status, 0, sizeof(info->status));
- pci_dma_sync_single_for_device(priv->pci_dev, txcmd_phys,
- firstlen, PCI_DMA_BIDIRECTIONAL);
+ info->driver_data[0] = ctx;
+ info->driver_data[1] = dev_cmd;
- trace_iwlwifi_dev_tx(priv,
- &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr],
- sizeof(struct iwl_tfd),
- &out_cmd->hdr, firstlen,
- skb->data + hdr_len, secondlen);
+ if (iwl_trans_tx(trans(priv), skb, dev_cmd, ctx->ctxid, sta_id))
+ goto drop_unlock_sta;
- /* Tell device the write index *just past* this latest filled TFD */
- q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
- iwl_txq_update_write_ptr(priv, txq);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- /*
- * At this point the frame is "transmitted" successfully
- * and we will get a TX status notification eventually,
- * regardless of the value of ret. "ret" only indicates
- * whether or not we should update the write pointer.
- */
+ spin_unlock(&priv->shrd->sta_lock);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
/*
* Avoid atomic ops if it isn't an associated client.
@@ -811,288 +367,55 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
if (sta_priv && sta_priv->client && !is_agg)
atomic_inc(&sta_priv->pending_frames);
- if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) {
- if (wait_write_ptr) {
- spin_lock_irqsave(&priv->lock, flags);
- txq->need_update = 1;
- iwl_txq_update_write_ptr(priv, txq);
- spin_unlock_irqrestore(&priv->lock, flags);
- } else {
- iwl_stop_queue(priv, txq);
- }
- }
-
return 0;
drop_unlock_sta:
- spin_unlock(&priv->sta_lock);
+ if (dev_cmd)
+ kmem_cache_free(priv->tx_cmd_pool, dev_cmd);
+ spin_unlock(&priv->shrd->sta_lock);
drop_unlock_priv:
- spin_unlock_irqrestore(&priv->lock, flags);
- return -1;
-}
-
-static inline int iwlagn_alloc_dma_ptr(struct iwl_priv *priv,
- struct iwl_dma_ptr *ptr, size_t size)
-{
- ptr->addr = dma_alloc_coherent(&priv->pci_dev->dev, size, &ptr->dma,
- GFP_KERNEL);
- if (!ptr->addr)
- return -ENOMEM;
- ptr->size = size;
- return 0;
-}
-
-static inline void iwlagn_free_dma_ptr(struct iwl_priv *priv,
- struct iwl_dma_ptr *ptr)
-{
- if (unlikely(!ptr->addr))
- return;
-
- dma_free_coherent(&priv->pci_dev->dev, ptr->size, ptr->addr, ptr->dma);
- memset(ptr, 0, sizeof(*ptr));
-}
-
-/**
- * iwlagn_hw_txq_ctx_free - Free TXQ Context
- *
- * Destroy all TX DMA queues and structures
- */
-void iwlagn_hw_txq_ctx_free(struct iwl_priv *priv)
-{
- int txq_id;
-
- /* Tx queues */
- if (priv->txq) {
- for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++)
- if (txq_id == priv->cmd_queue)
- iwl_cmd_queue_free(priv);
- else
- iwl_tx_queue_free(priv, txq_id);
- }
- iwlagn_free_dma_ptr(priv, &priv->kw);
-
- iwlagn_free_dma_ptr(priv, &priv->scd_bc_tbls);
-
- /* free tx queue structure */
- iwl_free_txq_mem(priv);
-}
-
-/**
- * iwlagn_txq_ctx_alloc - allocate TX queue context
- * Allocate all Tx DMA structures and initialize them
- *
- * @param priv
- * @return error code
- */
-int iwlagn_txq_ctx_alloc(struct iwl_priv *priv)
-{
- int ret;
- int txq_id, slots_num;
- unsigned long flags;
-
- /* Free all tx/cmd queues and keep-warm buffer */
- iwlagn_hw_txq_ctx_free(priv);
-
- ret = iwlagn_alloc_dma_ptr(priv, &priv->scd_bc_tbls,
- priv->hw_params.scd_bc_tbls_size);
- if (ret) {
- IWL_ERR(priv, "Scheduler BC Table allocation failed\n");
- goto error_bc_tbls;
- }
- /* Alloc keep-warm buffer */
- ret = iwlagn_alloc_dma_ptr(priv, &priv->kw, IWL_KW_SIZE);
- if (ret) {
- IWL_ERR(priv, "Keep Warm allocation failed\n");
- goto error_kw;
- }
-
- /* allocate tx queue structure */
- ret = iwl_alloc_txq_mem(priv);
- if (ret)
- goto error;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- /* Turn off all Tx DMA fifos */
- iwlagn_txq_set_sched(priv, 0);
-
- /* Tell NIC where to find the "keep warm" buffer */
- iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- /* Alloc and init all Tx queues, including the command queue (#4/#9) */
- for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
- slots_num = (txq_id == priv->cmd_queue) ?
- TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
- ret = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num,
- txq_id);
- if (ret) {
- IWL_ERR(priv, "Tx %d queue init failed\n", txq_id);
- goto error;
- }
- }
-
- return ret;
-
- error:
- iwlagn_hw_txq_ctx_free(priv);
- iwlagn_free_dma_ptr(priv, &priv->kw);
- error_kw:
- iwlagn_free_dma_ptr(priv, &priv->scd_bc_tbls);
- error_bc_tbls:
- return ret;
-}
-
-void iwlagn_txq_ctx_reset(struct iwl_priv *priv)
-{
- int txq_id, slots_num;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- /* Turn off all Tx DMA fifos */
- iwlagn_txq_set_sched(priv, 0);
-
- /* Tell NIC where to find the "keep warm" buffer */
- iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- /* Alloc and init all Tx queues, including the command queue (#4) */
- for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
- slots_num = txq_id == priv->cmd_queue ?
- TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
- iwl_tx_queue_reset(priv, &priv->txq[txq_id], slots_num, txq_id);
- }
-}
-
-/**
- * iwlagn_txq_ctx_stop - Stop all Tx DMA channels
- */
-void iwlagn_txq_ctx_stop(struct iwl_priv *priv)
-{
- int ch, txq_id;
- unsigned long flags;
-
- /* Turn off all Tx DMA fifos */
- spin_lock_irqsave(&priv->lock, flags);
-
- iwlagn_txq_set_sched(priv, 0);
-
- /* Stop each Tx DMA channel, and wait for it to be idle */
- for (ch = 0; ch < priv->hw_params.dma_chnl_num; ch++) {
- iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
- if (iwl_poll_direct_bit(priv, FH_TSSR_TX_STATUS_REG,
- FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
- 1000))
- IWL_ERR(priv, "Failing on timeout while stopping"
- " DMA channel %d [0x%08x]", ch,
- iwl_read_direct32(priv, FH_TSSR_TX_STATUS_REG));
- }
- spin_unlock_irqrestore(&priv->lock, flags);
-
- if (!priv->txq)
- return;
-
- /* Unmap DMA from host system and free skb's */
- for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++)
- if (txq_id == priv->cmd_queue)
- iwl_cmd_queue_unmap(priv);
- else
- iwl_tx_queue_unmap(priv, txq_id);
-}
-
-/*
- * Find first available (lowest unused) Tx Queue, mark it "active".
- * Called only when finding queue for aggregation.
- * Should never return anything < 7, because they should already
- * be in use as EDCA AC (0-3), Command (4), reserved (5, 6)
- */
-static int iwlagn_txq_ctx_activate_free(struct iwl_priv *priv)
-{
- int txq_id;
-
- for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++)
- if (!test_and_set_bit(txq_id, &priv->txq_ctx_active_msk))
- return txq_id;
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
return -1;
}
int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
int sta_id;
- int tx_fifo;
- int txq_id;
int ret;
- unsigned long flags;
- struct iwl_tid_data *tid_data;
-
- tx_fifo = get_fifo_from_tid(iwl_rxon_ctx_from_vif(vif), tid);
- if (unlikely(tx_fifo < 0))
- return tx_fifo;
- IWL_WARN(priv, "%s on ra = %pM tid = %d\n",
- __func__, sta->addr, tid);
+ IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n",
+ sta->addr, tid);
sta_id = iwl_sta_id(sta);
if (sta_id == IWL_INVALID_STATION) {
IWL_ERR(priv, "Start AGG on invalid station\n");
return -ENXIO;
}
- if (unlikely(tid >= MAX_TID_COUNT))
+ if (unlikely(tid >= IWL_MAX_TID_COUNT))
return -EINVAL;
- if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_OFF) {
+ if (priv->shrd->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) {
IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n");
return -ENXIO;
}
- txq_id = iwlagn_txq_ctx_activate_free(priv);
- if (txq_id == -1) {
- IWL_ERR(priv, "No free aggregation queue available\n");
- return -ENXIO;
- }
-
- spin_lock_irqsave(&priv->sta_lock, flags);
- tid_data = &priv->stations[sta_id].tid[tid];
- *ssn = SEQ_TO_SN(tid_data->seq_number);
- tid_data->agg.txq_id = txq_id;
- tid_data->agg.tx_fifo = tx_fifo;
- iwl_set_swq_id(&priv->txq[txq_id], get_ac_from_tid(tid), txq_id);
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- ret = iwlagn_txq_agg_enable(priv, txq_id, sta_id, tid);
+ ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
if (ret)
return ret;
- spin_lock_irqsave(&priv->sta_lock, flags);
- tid_data = &priv->stations[sta_id].tid[tid];
- if (tid_data->tfds_in_queue == 0) {
- IWL_DEBUG_HT(priv, "HW queue is empty\n");
- tid_data->agg.state = IWL_AGG_ON;
- ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- } else {
- IWL_DEBUG_HT(priv, "HW queue is NOT empty: %d packets in HW queue\n",
- tid_data->tfds_in_queue);
- tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
- }
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ ret = iwl_trans_tx_agg_alloc(trans(priv), vif_priv->ctx->ctxid, sta_id,
+ tid, ssn);
+
return ret;
}
int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid)
{
- int tx_fifo_id, txq_id, sta_id, ssn;
- struct iwl_tid_data *tid_data;
- int write_ptr, read_ptr;
- unsigned long flags;
-
- tx_fifo_id = get_fifo_from_tid(iwl_rxon_ctx_from_vif(vif), tid);
- if (unlikely(tx_fifo_id < 0))
- return tx_fifo_id;
+ int sta_id;
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
sta_id = iwl_sta_id(sta);
@@ -1101,100 +424,8 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
return -ENXIO;
}
- spin_lock_irqsave(&priv->sta_lock, flags);
-
- tid_data = &priv->stations[sta_id].tid[tid];
- ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4;
- txq_id = tid_data->agg.txq_id;
-
- switch (priv->stations[sta_id].tid[tid].agg.state) {
- case IWL_EMPTYING_HW_QUEUE_ADDBA:
- /*
- * This can happen if the peer stops aggregation
- * again before we've had a chance to drain the
- * queue we selected previously, i.e. before the
- * session was really started completely.
- */
- IWL_DEBUG_HT(priv, "AGG stop before setup done\n");
- goto turn_off;
- case IWL_AGG_ON:
- break;
- default:
- IWL_WARN(priv, "Stopping AGG while state not ON or starting\n");
- }
-
- write_ptr = priv->txq[txq_id].q.write_ptr;
- read_ptr = priv->txq[txq_id].q.read_ptr;
-
- /* The queue is not empty */
- if (write_ptr != read_ptr) {
- IWL_DEBUG_HT(priv, "Stopping a non empty AGG HW QUEUE\n");
- priv->stations[sta_id].tid[tid].agg.state =
- IWL_EMPTYING_HW_QUEUE_DELBA;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
- return 0;
- }
-
- IWL_DEBUG_HT(priv, "HW queue is empty\n");
- turn_off:
- priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;
-
- /* do not restore/save irqs */
- spin_unlock(&priv->sta_lock);
- spin_lock(&priv->lock);
-
- /*
- * the only reason this call can fail is queue number out of range,
- * which can happen if uCode is reloaded and all the station
- * information are lost. if it is outside the range, there is no need
- * to deactivate the uCode queue, just return "success" to allow
- * mac80211 to clean up it own data.
- */
- iwlagn_txq_agg_disable(priv, txq_id, ssn, tx_fifo_id);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-
- return 0;
-}
-
-int iwlagn_txq_check_empty(struct iwl_priv *priv,
- int sta_id, u8 tid, int txq_id)
-{
- struct iwl_queue *q = &priv->txq[txq_id].q;
- u8 *addr = priv->stations[sta_id].sta.sta.addr;
- struct iwl_tid_data *tid_data = &priv->stations[sta_id].tid[tid];
- struct iwl_rxon_context *ctx;
-
- ctx = &priv->contexts[priv->stations[sta_id].ctxid];
-
- lockdep_assert_held(&priv->sta_lock);
-
- switch (priv->stations[sta_id].tid[tid].agg.state) {
- case IWL_EMPTYING_HW_QUEUE_DELBA:
- /* We are reclaiming the last packet of the */
- /* aggregated HW queue */
- if ((txq_id == tid_data->agg.txq_id) &&
- (q->read_ptr == q->write_ptr)) {
- u16 ssn = SEQ_TO_SN(tid_data->seq_number);
- int tx_fifo = get_fifo_from_tid(ctx, tid);
- IWL_DEBUG_HT(priv, "HW queue empty: continue DELBA flow\n");
- iwlagn_txq_agg_disable(priv, txq_id, ssn, tx_fifo);
- tid_data->agg.state = IWL_AGG_OFF;
- ieee80211_stop_tx_ba_cb_irqsafe(ctx->vif, addr, tid);
- }
- break;
- case IWL_EMPTYING_HW_QUEUE_ADDBA:
- /* We are reclaiming the last packet of the queue */
- if (tid_data->tfds_in_queue == 0) {
- IWL_DEBUG_HT(priv, "HW queue empty: continue ADDBA flow\n");
- tid_data->agg.state = IWL_AGG_ON;
- ieee80211_start_tx_ba_cb_irqsafe(ctx->vif, addr, tid);
- }
- break;
- }
-
- return 0;
+ return iwl_trans_tx_agg_disable(trans(priv), vif_priv->ctx->ctxid,
+ sta_id, tid);
}
static void iwlagn_non_agg_tx_status(struct iwl_priv *priv,
@@ -1216,147 +447,394 @@ static void iwlagn_non_agg_tx_status(struct iwl_priv *priv,
rcu_read_unlock();
}
-static void iwlagn_tx_status(struct iwl_priv *priv, struct iwl_tx_info *tx_info,
- bool is_agg)
+/**
+ * translate ucode response to mac80211 tx status control values
+ */
+static void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
+ struct ieee80211_tx_info *info)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx_info->skb->data;
+ struct ieee80211_tx_rate *r = &info->control.rates[0];
+
+ info->antenna_sel_tx =
+ ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
+ if (rate_n_flags & RATE_MCS_HT_MSK)
+ r->flags |= IEEE80211_TX_RC_MCS;
+ if (rate_n_flags & RATE_MCS_GF_MSK)
+ r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
+ if (rate_n_flags & RATE_MCS_HT40_MSK)
+ r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ if (rate_n_flags & RATE_MCS_DUP_MSK)
+ r->flags |= IEEE80211_TX_RC_DUP_DATA;
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ r->flags |= IEEE80211_TX_RC_SHORT_GI;
+ r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_get_tx_fail_reason(u32 status)
+{
+#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
+#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
+
+ switch (status & TX_STATUS_MSK) {
+ case TX_STATUS_SUCCESS:
+ return "SUCCESS";
+ TX_STATUS_POSTPONE(DELAY);
+ TX_STATUS_POSTPONE(FEW_BYTES);
+ TX_STATUS_POSTPONE(BT_PRIO);
+ TX_STATUS_POSTPONE(QUIET_PERIOD);
+ TX_STATUS_POSTPONE(CALC_TTAK);
+ TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
+ TX_STATUS_FAIL(SHORT_LIMIT);
+ TX_STATUS_FAIL(LONG_LIMIT);
+ TX_STATUS_FAIL(FIFO_UNDERRUN);
+ TX_STATUS_FAIL(DRAIN_FLOW);
+ TX_STATUS_FAIL(RFKILL_FLUSH);
+ TX_STATUS_FAIL(LIFE_EXPIRE);
+ TX_STATUS_FAIL(DEST_PS);
+ TX_STATUS_FAIL(HOST_ABORTED);
+ TX_STATUS_FAIL(BT_RETRY);
+ TX_STATUS_FAIL(STA_INVALID);
+ TX_STATUS_FAIL(FRAG_DROPPED);
+ TX_STATUS_FAIL(TID_DISABLE);
+ TX_STATUS_FAIL(FIFO_FLUSHED);
+ TX_STATUS_FAIL(INSUFFICIENT_CF_POLL);
+ TX_STATUS_FAIL(PASSIVE_NO_RX);
+ TX_STATUS_FAIL(NO_BEACON_ON_RADAR);
+ }
- if (!is_agg)
- iwlagn_non_agg_tx_status(priv, tx_info->ctx, hdr->addr1);
+ return "UNKNOWN";
- ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb);
+#undef TX_STATUS_FAIL
+#undef TX_STATUS_POSTPONE
}
+#endif /* CONFIG_IWLWIFI_DEBUG */
-int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
+static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
{
- struct iwl_tx_queue *txq = &priv->txq[txq_id];
- struct iwl_queue *q = &txq->q;
- struct iwl_tx_info *tx_info;
- int nfreed = 0;
- struct ieee80211_hdr *hdr;
+ status &= AGG_TX_STATUS_MSK;
- if ((index >= q->n_bd) || (iwl_queue_used(q, index) == 0)) {
- IWL_ERR(priv, "Read index for DMA queue txq id (%d), index %d, "
- "is out of range [0-%d] %d %d.\n", txq_id,
- index, q->n_bd, q->write_ptr, q->read_ptr);
- return 0;
+ switch (status) {
+ case AGG_TX_STATE_UNDERRUN_MSK:
+ priv->reply_agg_tx_stats.underrun++;
+ break;
+ case AGG_TX_STATE_BT_PRIO_MSK:
+ priv->reply_agg_tx_stats.bt_prio++;
+ break;
+ case AGG_TX_STATE_FEW_BYTES_MSK:
+ priv->reply_agg_tx_stats.few_bytes++;
+ break;
+ case AGG_TX_STATE_ABORT_MSK:
+ priv->reply_agg_tx_stats.abort++;
+ break;
+ case AGG_TX_STATE_LAST_SENT_TTL_MSK:
+ priv->reply_agg_tx_stats.last_sent_ttl++;
+ break;
+ case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK:
+ priv->reply_agg_tx_stats.last_sent_try++;
+ break;
+ case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK:
+ priv->reply_agg_tx_stats.last_sent_bt_kill++;
+ break;
+ case AGG_TX_STATE_SCD_QUERY_MSK:
+ priv->reply_agg_tx_stats.scd_query++;
+ break;
+ case AGG_TX_STATE_TEST_BAD_CRC32_MSK:
+ priv->reply_agg_tx_stats.bad_crc32++;
+ break;
+ case AGG_TX_STATE_RESPONSE_MSK:
+ priv->reply_agg_tx_stats.response++;
+ break;
+ case AGG_TX_STATE_DUMP_TX_MSK:
+ priv->reply_agg_tx_stats.dump_tx++;
+ break;
+ case AGG_TX_STATE_DELAY_TX_MSK:
+ priv->reply_agg_tx_stats.delay_tx++;
+ break;
+ default:
+ priv->reply_agg_tx_stats.unknown++;
+ break;
}
+}
- for (index = iwl_queue_inc_wrap(index, q->n_bd);
- q->read_ptr != index;
- q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
+ struct iwlagn_tx_resp *tx_resp)
+{
+ struct agg_tx_status *frame_status = &tx_resp->status;
+ int tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
+ IWLAGN_TX_RES_TID_POS;
+ int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >>
+ IWLAGN_TX_RES_RA_POS;
+ struct iwl_ht_agg *agg = &priv->shrd->tid_data[sta_id][tid].agg;
+ u32 status = le16_to_cpu(tx_resp->status.status);
+ int i;
- tx_info = &txq->txb[txq->q.read_ptr];
+ if (agg->wait_for_ba)
+ IWL_DEBUG_TX_REPLY(priv,
+ "got tx response w/o block-ack\n");
- if (WARN_ON_ONCE(tx_info->skb == NULL))
- continue;
+ agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
+ agg->wait_for_ba = (tx_resp->frame_count > 1);
- hdr = (struct ieee80211_hdr *)tx_info->skb->data;
- if (ieee80211_is_data_qos(hdr->frame_control))
- nfreed++;
+ /*
+ * If the BT kill count is non-zero, we'll get this
+ * notification again.
+ */
+ if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 &&
+ priv->cfg->bt_params &&
+ priv->cfg->bt_params->advanced_bt_coexist) {
+ IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n");
+ }
+
+ if (tx_resp->frame_count == 1)
+ return;
+
+ /* Construct bit-map of pending frames within Tx window */
+ for (i = 0; i < tx_resp->frame_count; i++) {
+ u16 fstatus = le16_to_cpu(frame_status[i].status);
- iwlagn_tx_status(priv, tx_info,
- txq_id >= IWLAGN_FIRST_AMPDU_QUEUE);
- tx_info->skb = NULL;
+ if (status & AGG_TX_STATUS_MSK)
+ iwlagn_count_agg_tx_err_status(priv, fstatus);
- iwlagn_txq_inval_byte_cnt_tbl(priv, txq);
+ if (status & (AGG_TX_STATE_FEW_BYTES_MSK |
+ AGG_TX_STATE_ABORT_MSK))
+ continue;
- iwlagn_txq_free_tfd(priv, txq);
+ IWL_DEBUG_TX_REPLY(priv, "status %s (0x%08x), "
+ "try-count (0x%08x)\n",
+ iwl_get_agg_tx_fail_reason(fstatus),
+ fstatus & AGG_TX_STATUS_MSK,
+ fstatus & AGG_TX_TRY_MSK);
}
- return nfreed;
}
-/**
- * iwlagn_tx_status_reply_compressed_ba - Update tx status from block-ack
- *
- * Go through block-ack's bitmap of ACK'd frames, update driver's record of
- * ACK vs. not. This gets sent to mac80211, then to rate scaling algo.
- */
-static int iwlagn_tx_status_reply_compressed_ba(struct iwl_priv *priv,
- struct iwl_ht_agg *agg,
- struct iwl_compressed_ba_resp *ba_resp)
+#ifdef CONFIG_IWLWIFI_DEBUG
+#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x
+const char *iwl_get_agg_tx_fail_reason(u16 status)
{
- int sh;
- u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl);
- u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
- struct ieee80211_tx_info *info;
- u64 bitmap, sent_bitmap;
-
- if (unlikely(!agg->wait_for_ba)) {
- if (unlikely(ba_resp->bitmap))
- IWL_ERR(priv, "Received BA when not expected\n");
- return -EINVAL;
+ status &= AGG_TX_STATUS_MSK;
+ switch (status) {
+ case AGG_TX_STATE_TRANSMITTED:
+ return "SUCCESS";
+ AGG_TX_STATE_FAIL(UNDERRUN_MSK);
+ AGG_TX_STATE_FAIL(BT_PRIO_MSK);
+ AGG_TX_STATE_FAIL(FEW_BYTES_MSK);
+ AGG_TX_STATE_FAIL(ABORT_MSK);
+ AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK);
+ AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK);
+ AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK);
+ AGG_TX_STATE_FAIL(SCD_QUERY_MSK);
+ AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK);
+ AGG_TX_STATE_FAIL(RESPONSE_MSK);
+ AGG_TX_STATE_FAIL(DUMP_TX_MSK);
+ AGG_TX_STATE_FAIL(DELAY_TX_MSK);
}
- /* Mark that the expected block-ack response arrived */
- agg->wait_for_ba = 0;
- IWL_DEBUG_TX_REPLY(priv, "BA %d %d\n", agg->start_idx, ba_resp->seq_ctl);
+ return "UNKNOWN";
+}
+#endif /* CONFIG_IWLWIFI_DEBUG */
- /* Calculate shift to align block-ack bits with our Tx window bits */
- sh = agg->start_idx - SEQ_TO_INDEX(seq_ctl >> 4);
- if (sh < 0)
- sh += 0x100;
+static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp)
+{
+ return le32_to_cpup((__le32 *)&tx_resp->status +
+ tx_resp->frame_count) & MAX_SN;
+}
- /*
- * Check for success or failure according to the
- * transmitted bitmap and block-ack bitmap
- */
- bitmap = le64_to_cpu(ba_resp->bitmap) >> sh;
- sent_bitmap = bitmap & agg->bitmap;
+static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status)
+{
+ status &= TX_STATUS_MSK;
- /* Sanity check values reported by uCode */
- if (ba_resp->txed_2_done > ba_resp->txed) {
- IWL_DEBUG_TX_REPLY(priv,
- "bogus sent(%d) and ack(%d) count\n",
- ba_resp->txed, ba_resp->txed_2_done);
- /*
- * set txed_2_done = txed,
- * so it won't impact rate scale
- */
- ba_resp->txed = ba_resp->txed_2_done;
+ switch (status) {
+ case TX_STATUS_POSTPONE_DELAY:
+ priv->reply_tx_stats.pp_delay++;
+ break;
+ case TX_STATUS_POSTPONE_FEW_BYTES:
+ priv->reply_tx_stats.pp_few_bytes++;
+ break;
+ case TX_STATUS_POSTPONE_BT_PRIO:
+ priv->reply_tx_stats.pp_bt_prio++;
+ break;
+ case TX_STATUS_POSTPONE_QUIET_PERIOD:
+ priv->reply_tx_stats.pp_quiet_period++;
+ break;
+ case TX_STATUS_POSTPONE_CALC_TTAK:
+ priv->reply_tx_stats.pp_calc_ttak++;
+ break;
+ case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
+ priv->reply_tx_stats.int_crossed_retry++;
+ break;
+ case TX_STATUS_FAIL_SHORT_LIMIT:
+ priv->reply_tx_stats.short_limit++;
+ break;
+ case TX_STATUS_FAIL_LONG_LIMIT:
+ priv->reply_tx_stats.long_limit++;
+ break;
+ case TX_STATUS_FAIL_FIFO_UNDERRUN:
+ priv->reply_tx_stats.fifo_underrun++;
+ break;
+ case TX_STATUS_FAIL_DRAIN_FLOW:
+ priv->reply_tx_stats.drain_flow++;
+ break;
+ case TX_STATUS_FAIL_RFKILL_FLUSH:
+ priv->reply_tx_stats.rfkill_flush++;
+ break;
+ case TX_STATUS_FAIL_LIFE_EXPIRE:
+ priv->reply_tx_stats.life_expire++;
+ break;
+ case TX_STATUS_FAIL_DEST_PS:
+ priv->reply_tx_stats.dest_ps++;
+ break;
+ case TX_STATUS_FAIL_HOST_ABORTED:
+ priv->reply_tx_stats.host_abort++;
+ break;
+ case TX_STATUS_FAIL_BT_RETRY:
+ priv->reply_tx_stats.bt_retry++;
+ break;
+ case TX_STATUS_FAIL_STA_INVALID:
+ priv->reply_tx_stats.sta_invalid++;
+ break;
+ case TX_STATUS_FAIL_FRAG_DROPPED:
+ priv->reply_tx_stats.frag_drop++;
+ break;
+ case TX_STATUS_FAIL_TID_DISABLE:
+ priv->reply_tx_stats.tid_disable++;
+ break;
+ case TX_STATUS_FAIL_FIFO_FLUSHED:
+ priv->reply_tx_stats.fifo_flush++;
+ break;
+ case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL:
+ priv->reply_tx_stats.insuff_cf_poll++;
+ break;
+ case TX_STATUS_FAIL_PASSIVE_NO_RX:
+ priv->reply_tx_stats.fail_hw_drop++;
+ break;
+ case TX_STATUS_FAIL_NO_BEACON_ON_RADAR:
+ priv->reply_tx_stats.sta_color_mismatch++;
+ break;
+ default:
+ priv->reply_tx_stats.unknown++;
+ break;
}
- IWL_DEBUG_HT(priv, "agg frames sent:%d, acked:%d\n",
- ba_resp->txed, ba_resp->txed_2_done);
+}
- /* Find the first ACKed frame to store the TX status */
- while (sent_bitmap && !(sent_bitmap & 1)) {
- agg->start_idx = (agg->start_idx + 1) & 0xff;
- sent_bitmap >>= 1;
- }
+static void iwlagn_set_tx_status(struct iwl_priv *priv,
+ struct ieee80211_tx_info *info,
+ struct iwlagn_tx_resp *tx_resp,
+ bool is_agg)
+{
+ u16 status = le16_to_cpu(tx_resp->status.status);
- info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb);
- memset(&info->status, 0, sizeof(info->status));
- info->flags |= IEEE80211_TX_STAT_ACK;
- info->flags |= IEEE80211_TX_STAT_AMPDU;
- info->status.ampdu_ack_len = ba_resp->txed_2_done;
- info->status.ampdu_len = ba_resp->txed;
- iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, info);
+ info->status.rates[0].count = tx_resp->failure_frame + 1;
+ if (is_agg)
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+ info->flags |= iwl_tx_status_to_mac80211(status);
+ iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
+ info);
+ if (!iwl_is_tx_success(status))
+ iwlagn_count_tx_err_status(priv, status);
+}
- return 0;
+static void iwl_check_abort_status(struct iwl_priv *priv,
+ u8 frame_count, u32 status)
+{
+ if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
+ IWL_ERR(priv, "Tx flush command to flush out all frames\n");
+ if (!test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
+ queue_work(priv->shrd->workqueue, &priv->tx_flush);
+ }
}
-/**
- * translate ucode response to mac80211 tx status control values
- */
-void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
- struct ieee80211_tx_info *info)
+int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
- struct ieee80211_tx_rate *r = &info->control.rates[0];
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+ int txq_id = SEQ_TO_QUEUE(sequence);
+ int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence);
+ struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
+ struct ieee80211_hdr *hdr;
+ u32 status = le16_to_cpu(tx_resp->status.status);
+ u32 ssn = iwlagn_get_scd_ssn(tx_resp);
+ int tid;
+ int sta_id;
+ int freed;
+ struct ieee80211_tx_info *info;
+ unsigned long flags;
+ struct sk_buff_head skbs;
+ struct sk_buff *skb;
+ struct iwl_rxon_context *ctx;
+ bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE);
+
+ tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
+ IWLAGN_TX_RES_TID_POS;
+ sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >>
+ IWLAGN_TX_RES_RA_POS;
+
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+
+ if (is_agg)
+ iwl_rx_reply_tx_agg(priv, tx_resp);
+
+ if (tx_resp->frame_count == 1) {
+ __skb_queue_head_init(&skbs);
+ /*we can free until ssn % q.n_bd not inclusive */
+ iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id,
+ ssn, status, &skbs);
+ freed = 0;
+ while (!skb_queue_empty(&skbs)) {
+ skb = __skb_dequeue(&skbs);
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ priv->last_seq_ctl = tx_resp->seq_ctl;
+
+ info = IEEE80211_SKB_CB(skb);
+ ctx = info->driver_data[0];
+ kmem_cache_free(priv->tx_cmd_pool,
+ (info->driver_data[1]));
+
+ memset(&info->status, 0, sizeof(info->status));
+
+ if (status == TX_STATUS_FAIL_PASSIVE_NO_RX &&
+ iwl_is_associated_ctx(ctx) && ctx->vif &&
+ ctx->vif->type == NL80211_IFTYPE_STATION) {
+ ctx->last_tx_rejected = true;
+ iwl_trans_stop_queue(trans(priv), txq_id);
+
+ IWL_DEBUG_TX_REPLY(priv,
+ "TXQ %d status %s (0x%08x) "
+ "rate_n_flags 0x%x retries %d\n",
+ txq_id,
+ iwl_get_tx_fail_reason(status),
+ status,
+ le32_to_cpu(tx_resp->rate_n_flags),
+ tx_resp->failure_frame);
+
+ IWL_DEBUG_TX_REPLY(priv,
+ "FrameCnt = %d, idx=%d\n",
+ tx_resp->frame_count, cmd_index);
+ }
+
+ /* check if BAR is needed */
+ if (is_agg && !iwl_is_tx_success(status))
+ info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
+ iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb),
+ tx_resp, is_agg);
+ if (!is_agg)
+ iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1);
+
+ ieee80211_tx_status_irqsafe(priv->hw, skb);
+
+ freed++;
+ }
- info->antenna_sel_tx =
- ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
- if (rate_n_flags & RATE_MCS_HT_MSK)
- r->flags |= IEEE80211_TX_RC_MCS;
- if (rate_n_flags & RATE_MCS_GF_MSK)
- r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
- if (rate_n_flags & RATE_MCS_HT40_MSK)
- r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
- if (rate_n_flags & RATE_MCS_DUP_MSK)
- r->flags |= IEEE80211_TX_RC_DUP_DATA;
- if (rate_n_flags & RATE_MCS_SGI_MSK)
- r->flags |= IEEE80211_TX_RC_SHORT_GI;
- r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band);
+ WARN_ON(!is_agg && freed != 1);
+ }
+
+ iwl_check_abort_status(priv, tx_resp->frame_count, status);
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+ return 0;
}
/**
@@ -1365,17 +843,21 @@ void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
* Handles block-acknowledge notification from device, which reports success
* of frames sent via aggregation.
*/
-void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba;
- struct iwl_tx_queue *txq = NULL;
struct iwl_ht_agg *agg;
- int index;
+ struct sk_buff_head reclaimed_skbs;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_hdr *hdr;
+ struct sk_buff *skb;
+ unsigned long flags;
int sta_id;
int tid;
- unsigned long flags;
+ int freed;
/* "flow" corresponds to Tx queue */
u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
@@ -1384,16 +866,18 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
* (in Tx queue's circular buffer) of first TFD/frame in window */
u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn);
- if (scd_flow >= priv->hw_params.max_txq_num) {
+ if (scd_flow >= hw_params(priv).max_txq_num) {
IWL_ERR(priv,
"BUG_ON scd_flow is bigger than number of queues\n");
- return;
+ return 0;
}
- txq = &priv->txq[scd_flow];
sta_id = ba_resp->sta_id;
tid = ba_resp->tid;
- agg = &priv->stations[sta_id].tid[tid].agg;
+ agg = &priv->shrd->tid_data[sta_id][tid].agg;
+
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+
if (unlikely(agg->txq_id != scd_flow)) {
/*
* FIXME: this is a uCode bug which need to be addressed,
@@ -1404,88 +888,84 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
IWL_DEBUG_TX_REPLY(priv,
"BA scd_flow %d does not match txq_id %d\n",
scd_flow, agg->txq_id);
- return;
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+ return 0;
}
- /* Find index just before block-ack window */
- index = iwl_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd);
-
- spin_lock_irqsave(&priv->sta_lock, flags);
+ if (unlikely(!agg->wait_for_ba)) {
+ if (unlikely(ba_resp->bitmap))
+ IWL_ERR(priv, "Received BA when not expected\n");
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+ return 0;
+ }
IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, "
"sta_id = %d\n",
agg->wait_for_ba,
(u8 *) &ba_resp->sta_addr_lo32,
ba_resp->sta_id);
- IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = "
- "%d, scd_ssn = %d\n",
+ IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, "
+ "scd_flow = %d, scd_ssn = %d\n",
ba_resp->tid,
ba_resp->seq_ctl,
(unsigned long long)le64_to_cpu(ba_resp->bitmap),
ba_resp->scd_flow,
ba_resp->scd_ssn);
- IWL_DEBUG_TX_REPLY(priv, "DAT start_idx = %d, bitmap = 0x%llx\n",
- agg->start_idx,
- (unsigned long long)agg->bitmap);
- /* Update driver's record of ACK vs. not for each frame in window */
- iwlagn_tx_status_reply_compressed_ba(priv, agg, ba_resp);
+ /* Mark that the expected block-ack response arrived */
+ agg->wait_for_ba = false;
+
+ /* Sanity check values reported by uCode */
+ if (ba_resp->txed_2_done > ba_resp->txed) {
+ IWL_DEBUG_TX_REPLY(priv,
+ "bogus sent(%d) and ack(%d) count\n",
+ ba_resp->txed, ba_resp->txed_2_done);
+ /*
+ * set txed_2_done = txed,
+ * so it won't impact rate scale
+ */
+ ba_resp->txed = ba_resp->txed_2_done;
+ }
+ IWL_DEBUG_HT(priv, "agg frames sent:%d, acked:%d\n",
+ ba_resp->txed, ba_resp->txed_2_done);
+
+ __skb_queue_head_init(&reclaimed_skbs);
/* Release all TFDs before the SSN, i.e. all TFDs in front of
* block-ack window (we assume that they've been successfully
* transmitted ... if not, it's too late anyway). */
- if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) {
- /* calculate mac80211 ampdu sw queue to wake */
- int freed = iwlagn_tx_queue_reclaim(priv, scd_flow, index);
- iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
-
- if ((iwl_queue_space(&txq->q) > txq->q.low_mark) &&
- priv->mac80211_registered &&
- (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA))
- iwl_wake_queue(priv, txq);
-
- iwlagn_txq_check_empty(priv, sta_id, tid, scd_flow);
- }
+ iwl_trans_reclaim(trans(priv), sta_id, tid, scd_flow, ba_resp_scd_ssn,
+ 0, &reclaimed_skbs);
+ freed = 0;
+ while (!skb_queue_empty(&reclaimed_skbs)) {
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-}
+ skb = __skb_dequeue(&reclaimed_skbs);
+ hdr = (struct ieee80211_hdr *)skb->data;
-#ifdef CONFIG_IWLWIFI_DEBUG
-const char *iwl_get_tx_fail_reason(u32 status)
-{
-#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
-#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ freed++;
+ else
+ WARN_ON_ONCE(1);
+
+ info = IEEE80211_SKB_CB(skb);
+ kmem_cache_free(priv->tx_cmd_pool, (info->driver_data[1]));
+
+ if (freed == 1) {
+ /* this is the first skb we deliver in this batch */
+ /* put the rate scaling data there */
+ info = IEEE80211_SKB_CB(skb);
+ memset(&info->status, 0, sizeof(info->status));
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ info->flags |= IEEE80211_TX_STAT_AMPDU;
+ info->status.ampdu_ack_len = ba_resp->txed_2_done;
+ info->status.ampdu_len = ba_resp->txed;
+ iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags,
+ info);
+ }
- switch (status & TX_STATUS_MSK) {
- case TX_STATUS_SUCCESS:
- return "SUCCESS";
- TX_STATUS_POSTPONE(DELAY);
- TX_STATUS_POSTPONE(FEW_BYTES);
- TX_STATUS_POSTPONE(BT_PRIO);
- TX_STATUS_POSTPONE(QUIET_PERIOD);
- TX_STATUS_POSTPONE(CALC_TTAK);
- TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
- TX_STATUS_FAIL(SHORT_LIMIT);
- TX_STATUS_FAIL(LONG_LIMIT);
- TX_STATUS_FAIL(FIFO_UNDERRUN);
- TX_STATUS_FAIL(DRAIN_FLOW);
- TX_STATUS_FAIL(RFKILL_FLUSH);
- TX_STATUS_FAIL(LIFE_EXPIRE);
- TX_STATUS_FAIL(DEST_PS);
- TX_STATUS_FAIL(HOST_ABORTED);
- TX_STATUS_FAIL(BT_RETRY);
- TX_STATUS_FAIL(STA_INVALID);
- TX_STATUS_FAIL(FRAG_DROPPED);
- TX_STATUS_FAIL(TID_DISABLE);
- TX_STATUS_FAIL(FIFO_FLUSHED);
- TX_STATUS_FAIL(INSUFFICIENT_CF_POLL);
- TX_STATUS_FAIL(PASSIVE_NO_RX);
- TX_STATUS_FAIL(NO_BEACON_ON_RADAR);
+ ieee80211_tx_status_irqsafe(priv->hw, skb);
}
- return "UNKNOWN";
-
-#undef TX_STATUS_FAIL
-#undef TX_STATUS_POSTPONE
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+ return 0;
}
-#endif /* CONFIG_IWLWIFI_DEBUG */
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c b/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c
index 97de5d9de67b..8ba0dd54e37d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c
@@ -35,42 +35,11 @@
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-io.h"
-#include "iwl-helpers.h"
#include "iwl-agn-hw.h"
#include "iwl-agn.h"
#include "iwl-agn-calib.h"
-
-#define IWL_AC_UNSET -1
-
-struct queue_to_fifo_ac {
- s8 fifo, ac;
-};
-
-static const struct queue_to_fifo_ac iwlagn_default_queue_to_tx_fifo[] = {
- { IWL_TX_FIFO_VO, IEEE80211_AC_VO, },
- { IWL_TX_FIFO_VI, IEEE80211_AC_VI, },
- { IWL_TX_FIFO_BE, IEEE80211_AC_BE, },
- { IWL_TX_FIFO_BK, IEEE80211_AC_BK, },
- { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
-};
-
-static const struct queue_to_fifo_ac iwlagn_ipan_queue_to_tx_fifo[] = {
- { IWL_TX_FIFO_VO, IEEE80211_AC_VO, },
- { IWL_TX_FIFO_VI, IEEE80211_AC_VI, },
- { IWL_TX_FIFO_BE, IEEE80211_AC_BE, },
- { IWL_TX_FIFO_BK, IEEE80211_AC_BK, },
- { IWL_TX_FIFO_BK_IPAN, IEEE80211_AC_BK, },
- { IWL_TX_FIFO_BE_IPAN, IEEE80211_AC_BE, },
- { IWL_TX_FIFO_VI_IPAN, IEEE80211_AC_VI, },
- { IWL_TX_FIFO_VO_IPAN, IEEE80211_AC_VO, },
- { IWL_TX_FIFO_BE_IPAN, 2, },
- { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, },
-};
+#include "iwl-trans.h"
+#include "iwl-fh.h"
static struct iwl_wimax_coex_event_entry cu_priorities[COEX_NUM_OF_EVENTS] = {
{COEX_CU_UNASSOC_IDLE_RP, COEX_CU_UNASSOC_IDLE_WP,
@@ -115,42 +84,37 @@ static int iwlagn_load_section(struct iwl_priv *priv, const char *name,
priv->ucode_write_complete = 0;
- iwl_write_direct32(priv,
+ iwl_write_direct32(bus(priv),
FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
- iwl_write_direct32(priv,
+ iwl_write_direct32(bus(priv),
FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), dst_addr);
- iwl_write_direct32(priv,
+ iwl_write_direct32(bus(priv),
FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
- iwl_write_direct32(priv,
+ iwl_write_direct32(bus(priv),
FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
(iwl_get_dma_hi_addr(phy_addr)
<< FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
- iwl_write_direct32(priv,
+ iwl_write_direct32(bus(priv),
FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL),
1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM |
1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX |
FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID);
- iwl_write_direct32(priv,
+ iwl_write_direct32(bus(priv),
FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
- IWL_DEBUG_INFO(priv, "%s uCode section being loaded...\n", name);
- ret = wait_event_interruptible_timeout(priv->wait_command_queue,
- priv->ucode_write_complete, 5 * HZ);
- if (ret == -ERESTARTSYS) {
- IWL_ERR(priv, "Could not load the %s uCode section due "
- "to interrupt\n", name);
- return ret;
- }
+ IWL_DEBUG_FW(priv, "%s uCode section being loaded...\n", name);
+ ret = wait_event_timeout(priv->shrd->wait_command_queue,
+ priv->ucode_write_complete, 5 * HZ);
if (!ret) {
IWL_ERR(priv, "Could not load the %s uCode section\n",
name);
@@ -183,10 +147,7 @@ static int iwlagn_set_Xtal_calib(struct iwl_priv *priv)
__le16 *xtal_calib =
(__le16 *)iwl_eeprom_query_addr(priv, EEPROM_XTAL);
- cmd.hdr.op_code = IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD;
- cmd.hdr.first_group = 0;
- cmd.hdr.groups_num = 1;
- cmd.hdr.data_valid = 1;
+ iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD);
cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]);
cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]);
return iwl_calib_set(&priv->calib_results[IWL_CALIB_XTAL],
@@ -197,17 +158,52 @@ static int iwlagn_set_temperature_offset_calib(struct iwl_priv *priv)
{
struct iwl_calib_temperature_offset_cmd cmd;
__le16 *offset_calib =
- (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_5000_TEMPERATURE);
- cmd.hdr.op_code = IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD;
- cmd.hdr.first_group = 0;
- cmd.hdr.groups_num = 1;
- cmd.hdr.data_valid = 1;
- cmd.radio_sensor_offset = le16_to_cpu(offset_calib[1]);
+ (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE);
+
+ memset(&cmd, 0, sizeof(cmd));
+ iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD);
+ memcpy(&cmd.radio_sensor_offset, offset_calib, sizeof(*offset_calib));
if (!(cmd.radio_sensor_offset))
cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET;
- cmd.reserved = 0;
+
IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n",
- cmd.radio_sensor_offset);
+ le16_to_cpu(cmd.radio_sensor_offset));
+ return iwl_calib_set(&priv->calib_results[IWL_CALIB_TEMP_OFFSET],
+ (u8 *)&cmd, sizeof(cmd));
+}
+
+static int iwlagn_set_temperature_offset_calib_v2(struct iwl_priv *priv)
+{
+ struct iwl_calib_temperature_offset_v2_cmd cmd;
+ __le16 *offset_calib_high = (__le16 *)iwl_eeprom_query_addr(priv,
+ EEPROM_KELVIN_TEMPERATURE);
+ __le16 *offset_calib_low =
+ (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE);
+ struct iwl_eeprom_calib_hdr *hdr;
+
+ memset(&cmd, 0, sizeof(cmd));
+ iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD);
+ hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv,
+ EEPROM_CALIB_ALL);
+ memcpy(&cmd.radio_sensor_offset_high, offset_calib_high,
+ sizeof(*offset_calib_high));
+ memcpy(&cmd.radio_sensor_offset_low, offset_calib_low,
+ sizeof(*offset_calib_low));
+ if (!(cmd.radio_sensor_offset_low)) {
+ IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n");
+ cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET;
+ cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET;
+ }
+ memcpy(&cmd.burntVoltageRef, &hdr->voltage,
+ sizeof(hdr->voltage));
+
+ IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n",
+ le16_to_cpu(cmd.radio_sensor_offset_high));
+ IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n",
+ le16_to_cpu(cmd.radio_sensor_offset_low));
+ IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n",
+ le16_to_cpu(cmd.burntVoltageRef));
+
return iwl_calib_set(&priv->calib_results[IWL_CALIB_TEMP_OFFSET],
(u8 *)&cmd, sizeof(cmd));
}
@@ -225,13 +221,15 @@ static int iwlagn_send_calib_cfg(struct iwl_priv *priv)
calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL;
calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL;
calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL;
- calib_cfg_cmd.ucd_calib_cfg.flags = IWL_CALIB_INIT_CFG_ALL;
+ calib_cfg_cmd.ucd_calib_cfg.flags =
+ IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK;
- return iwl_send_cmd(priv, &cmd);
+ return iwl_trans_send_cmd(trans(priv), &cmd);
}
-void iwlagn_rx_calib_result(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+int iwlagn_rx_calib_result(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->u.raw;
@@ -264,9 +262,10 @@ void iwlagn_rx_calib_result(struct iwl_priv *priv,
default:
IWL_ERR(priv, "Unknown calibration notification %d\n",
hdr->op_code);
- return;
+ return -1;
}
iwl_calib_set(&priv->calib_results[index], pkt->u.raw, len);
+ return 0;
}
int iwlagn_init_alive_start(struct iwl_priv *priv)
@@ -296,8 +295,12 @@ int iwlagn_init_alive_start(struct iwl_priv *priv)
* temperature offset calibration is only needed for runtime ucode,
* so prepare the value now.
*/
- if (priv->cfg->need_temp_offset_calib)
- return iwlagn_set_temperature_offset_calib(priv);
+ if (priv->cfg->need_temp_offset_calib) {
+ if (priv->cfg->temp_offset_v2)
+ return iwlagn_set_temperature_offset_calib_v2(priv);
+ else
+ return iwlagn_set_temperature_offset_calib(priv);
+ }
return 0;
}
@@ -325,7 +328,8 @@ static int iwlagn_send_wimax_coex(struct iwl_priv *priv)
/* coexistence is disabled */
memset(&coex_cmd, 0, sizeof(coex_cmd));
}
- return iwl_send_cmd_pdu(priv, COEX_PRIORITY_TABLE_CMD,
+ return iwl_trans_send_cmd_pdu(trans(priv),
+ COEX_PRIORITY_TABLE_CMD, CMD_SYNC,
sizeof(coex_cmd), &coex_cmd);
}
@@ -357,7 +361,8 @@ void iwlagn_send_prio_tbl(struct iwl_priv *priv)
memcpy(prio_tbl_cmd.prio_tbl, iwlagn_bt_prio_tbl,
sizeof(iwlagn_bt_prio_tbl));
- if (iwl_send_cmd_pdu(priv, REPLY_BT_COEX_PRIO_TABLE,
+ if (iwl_trans_send_cmd_pdu(trans(priv),
+ REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC,
sizeof(prio_tbl_cmd), &prio_tbl_cmd))
IWL_ERR(priv, "failed to send BT prio tbl command\n");
}
@@ -369,7 +374,8 @@ int iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
env_cmd.action = action;
env_cmd.type = type;
- ret = iwl_send_cmd_pdu(priv, REPLY_BT_COEX_PROT_ENV,
+ ret = iwl_trans_send_cmd_pdu(trans(priv),
+ REPLY_BT_COEX_PROT_ENV, CMD_SYNC,
sizeof(env_cmd), &env_cmd);
if (ret)
IWL_ERR(priv, "failed to send BT env command\n");
@@ -379,110 +385,22 @@ int iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
static int iwlagn_alive_notify(struct iwl_priv *priv)
{
- const struct queue_to_fifo_ac *queue_to_fifo;
struct iwl_rxon_context *ctx;
- u32 a;
- unsigned long flags;
- int i, chan;
- u32 reg_val;
int ret;
- spin_lock_irqsave(&priv->lock, flags);
-
- priv->scd_base_addr = iwl_read_prph(priv, IWLAGN_SCD_SRAM_BASE_ADDR);
- a = priv->scd_base_addr + IWLAGN_SCD_CONTEXT_DATA_OFFSET;
- for (; a < priv->scd_base_addr + IWLAGN_SCD_TX_STTS_BITMAP_OFFSET;
- a += 4)
- iwl_write_targ_mem(priv, a, 0);
- for (; a < priv->scd_base_addr + IWLAGN_SCD_TRANSLATE_TBL_OFFSET;
- a += 4)
- iwl_write_targ_mem(priv, a, 0);
- for (; a < priv->scd_base_addr +
- IWLAGN_SCD_TRANSLATE_TBL_OFFSET_QUEUE(priv->hw_params.max_txq_num); a += 4)
- iwl_write_targ_mem(priv, a, 0);
-
- iwl_write_prph(priv, IWLAGN_SCD_DRAM_BASE_ADDR,
- priv->scd_bc_tbls.dma >> 10);
-
- /* Enable DMA channel */
- for (chan = 0; chan < FH50_TCSR_CHNL_NUM ; chan++)
- iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(chan),
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
-
- /* Update FH chicken bits */
- reg_val = iwl_read_direct32(priv, FH_TX_CHICKEN_BITS_REG);
- iwl_write_direct32(priv, FH_TX_CHICKEN_BITS_REG,
- reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
-
- iwl_write_prph(priv, IWLAGN_SCD_QUEUECHAIN_SEL,
- IWLAGN_SCD_QUEUECHAIN_SEL_ALL(priv));
- iwl_write_prph(priv, IWLAGN_SCD_AGGR_SEL, 0);
-
- /* initiate the queues */
- for (i = 0; i < priv->hw_params.max_txq_num; i++) {
- iwl_write_prph(priv, IWLAGN_SCD_QUEUE_RDPTR(i), 0);
- iwl_write_direct32(priv, HBUS_TARG_WRPTR, 0 | (i << 8));
- iwl_write_targ_mem(priv, priv->scd_base_addr +
- IWLAGN_SCD_CONTEXT_QUEUE_OFFSET(i), 0);
- iwl_write_targ_mem(priv, priv->scd_base_addr +
- IWLAGN_SCD_CONTEXT_QUEUE_OFFSET(i) +
- sizeof(u32),
- ((SCD_WIN_SIZE <<
- IWLAGN_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
- IWLAGN_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
- ((SCD_FRAME_LIMIT <<
- IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
- IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
- }
-
- iwl_write_prph(priv, IWLAGN_SCD_INTERRUPT_MASK,
- IWL_MASK(0, priv->hw_params.max_txq_num));
+ if (!priv->tx_cmd_pool)
+ priv->tx_cmd_pool =
+ kmem_cache_create("iwlagn_dev_cmd",
+ sizeof(struct iwl_device_cmd),
+ sizeof(void *), 0, NULL);
- /* Activate all Tx DMA/FIFO channels */
- iwlagn_txq_set_sched(priv, IWL_MASK(0, 7));
+ if (!priv->tx_cmd_pool)
+ return -ENOMEM;
- /* map queues to FIFOs */
- if (priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))
- queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo;
- else
- queue_to_fifo = iwlagn_default_queue_to_tx_fifo;
-
- iwlagn_set_wr_ptrs(priv, priv->cmd_queue, 0);
-
- /* make sure all queue are not stopped */
- memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped));
- for (i = 0; i < 4; i++)
- atomic_set(&priv->queue_stop_count[i], 0);
+ iwl_trans_tx_start(trans(priv));
for_each_context(priv, ctx)
ctx->last_tx_rejected = false;
- /* reset to 0 to enable all the queue first */
- priv->txq_ctx_active_msk = 0;
-
- BUILD_BUG_ON(ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo) != 10);
- BUILD_BUG_ON(ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo) != 10);
-
- for (i = 0; i < 10; i++) {
- int fifo = queue_to_fifo[i].fifo;
- int ac = queue_to_fifo[i].ac;
-
- iwl_txq_ctx_activate(priv, i);
-
- if (fifo == IWL_TX_FIFO_UNUSED)
- continue;
-
- if (ac != IWL_AC_UNSET)
- iwl_set_swq_id(&priv->txq[i], ac, i);
- iwlagn_tx_queue_set_status(priv, &priv->txq[i], fifo, 0);
- }
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- /* Enable L1-Active */
- iwl_clear_bits_prph(priv, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
-
ret = iwlagn_send_wimax_coex(priv);
if (ret)
return ret;
@@ -500,7 +418,7 @@ static int iwlagn_alive_notify(struct iwl_priv *priv)
* using sample data 100 bytes apart. If these sample points are good,
* it's a pretty good bet that everything between them is good, too.
*/
-static int iwlcore_verify_inst_sparse(struct iwl_priv *priv,
+static int iwl_verify_inst_sparse(struct iwl_priv *priv,
struct fw_desc *fw_desc)
{
__le32 *image = (__le32 *)fw_desc->v_addr;
@@ -508,15 +426,15 @@ static int iwlcore_verify_inst_sparse(struct iwl_priv *priv,
u32 val;
u32 i;
- IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len);
+ IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len);
for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) {
/* read data comes through single port, auto-incr addr */
/* NOTE: Use the debugless read so we don't flood kernel log
* if IWL_DL_IO is set */
- iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR,
+ iwl_write_direct32(bus(priv), HBUS_TARG_MEM_RADDR,
i + IWLAGN_RTC_INST_LOWER_BOUND);
- val = iwl_read32(priv, HBUS_TARG_MEM_RDAT);
+ val = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
if (val != le32_to_cpu(*image))
return -EIO;
}
@@ -533,16 +451,16 @@ static void iwl_print_mismatch_inst(struct iwl_priv *priv,
u32 offs;
int errors = 0;
- IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len);
+ IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len);
- iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR,
+ iwl_write_direct32(bus(priv), HBUS_TARG_MEM_RADDR,
IWLAGN_RTC_INST_LOWER_BOUND);
for (offs = 0;
offs < len && errors < 20;
offs += sizeof(u32), image++) {
/* read data comes through single port, auto-incr addr */
- val = iwl_read32(priv, HBUS_TARG_MEM_RDAT);
+ val = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
if (val != le32_to_cpu(*image)) {
IWL_ERR(priv, "uCode INST section at "
"offset 0x%x, is 0x%x, s/b 0x%x\n",
@@ -558,8 +476,8 @@ static void iwl_print_mismatch_inst(struct iwl_priv *priv,
*/
static int iwl_verify_ucode(struct iwl_priv *priv, struct fw_img *img)
{
- if (!iwlcore_verify_inst_sparse(priv, &img->code)) {
- IWL_DEBUG_INFO(priv, "uCode is good in inst SRAM\n");
+ if (!iwl_verify_inst_sparse(priv, &img->code)) {
+ IWL_DEBUG_FW(priv, "uCode is good in inst SRAM\n");
return 0;
}
@@ -583,7 +501,7 @@ static void iwlagn_alive_fn(struct iwl_priv *priv,
palive = &pkt->u.alive_frame;
- IWL_DEBUG_INFO(priv, "Alive ucode status 0x%08X revision "
+ IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision "
"0x%01X 0x%01X\n",
palive->is_valid, palive->ver_type,
palive->ver_subtype);
@@ -602,14 +520,14 @@ static void iwlagn_alive_fn(struct iwl_priv *priv,
int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
struct fw_img *image,
- int subtype, int alternate_subtype)
+ enum iwlagn_ucode_type ucode_type)
{
struct iwl_notification_wait alive_wait;
struct iwlagn_alive_data alive_data;
int ret;
- enum iwlagn_ucode_subtype old_type;
+ enum iwlagn_ucode_type old_type;
- ret = iwlagn_start_device(priv);
+ ret = iwl_trans_start_device(trans(priv));
if (ret)
return ret;
@@ -617,7 +535,7 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
iwlagn_alive_fn, &alive_data);
old_type = priv->ucode_type;
- priv->ucode_type = subtype;
+ priv->ucode_type = ucode_type;
ret = iwlagn_load_given_ucode(priv, image);
if (ret) {
@@ -626,8 +544,7 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
return ret;
}
- /* Remove all resets to allow NIC to operate */
- iwl_write32(priv, CSR_RESET, 0);
+ iwl_trans_kick_nic(trans(priv));
/*
* Some things may run in the background now, but we
@@ -645,24 +562,22 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
return -EIO;
}
- if (alive_data.subtype != subtype &&
- alive_data.subtype != alternate_subtype) {
- IWL_ERR(priv,
- "Loaded ucode is not expected type (got %d, expected %d)!\n",
- alive_data.subtype, subtype);
- priv->ucode_type = old_type;
- return -EIO;
- }
+ /*
+ * This step takes a long time (60-80ms!!) and
+ * WoWLAN image should be loaded quickly, so
+ * skip it for WoWLAN.
+ */
+ if (ucode_type != IWL_UCODE_WOWLAN) {
+ ret = iwl_verify_ucode(priv, image);
+ if (ret) {
+ priv->ucode_type = old_type;
+ return ret;
+ }
- ret = iwl_verify_ucode(priv, image);
- if (ret) {
- priv->ucode_type = old_type;
- return ret;
+ /* delay a bit to give rfkill time to run */
+ msleep(5);
}
- /* delay a bit to give rfkill time to run */
- msleep(5);
-
ret = iwlagn_alive_notify(priv);
if (ret) {
IWL_WARN(priv,
@@ -679,13 +594,13 @@ int iwlagn_run_init_ucode(struct iwl_priv *priv)
struct iwl_notification_wait calib_wait;
int ret;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
/* No init ucode required? Curious, but maybe ok */
if (!priv->ucode_init.code.len)
return 0;
- if (priv->ucode_type != UCODE_SUBTYPE_NONE_LOADED)
+ if (priv->ucode_type != IWL_UCODE_NONE)
return 0;
iwlagn_init_notification_wait(priv, &calib_wait,
@@ -694,7 +609,7 @@ int iwlagn_run_init_ucode(struct iwl_priv *priv)
/* Will also start the device */
ret = iwlagn_load_ucode_wait_alive(priv, &priv->ucode_init,
- UCODE_SUBTYPE_INIT, -1);
+ IWL_UCODE_INIT);
if (ret)
goto error;
@@ -714,6 +629,6 @@ int iwlagn_run_init_ucode(struct iwl_priv *priv)
iwlagn_remove_notification(priv, &calib_wait);
out:
/* Whatever happened, stop the device */
- iwlagn_stop_device(priv);
+ iwl_trans_stop_device(trans(priv));
return ret;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 8e1942ebd9a0..ccba69b7f8a7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -26,21 +26,15 @@
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/pci-aspm.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
@@ -49,17 +43,15 @@
#include <asm/div64.h>
-#define DRV_NAME "iwlagn"
-
#include "iwl-eeprom.h"
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-io.h"
-#include "iwl-helpers.h"
-#include "iwl-sta.h"
#include "iwl-agn-calib.h"
#include "iwl-agn.h"
-
+#include "iwl-shared.h"
+#include "iwl-bus.h"
+#include "iwl-trans.h"
/******************************************************************************
*
@@ -85,20 +77,16 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
MODULE_LICENSE("GPL");
-
-static int iwlagn_ant_coupling;
-static bool iwlagn_bt_ch_announce = 1;
+MODULE_ALIAS("iwlagn");
void iwl_update_chain_flags(struct iwl_priv *priv)
{
struct iwl_rxon_context *ctx;
- if (priv->cfg->ops->hcmd->set_rxon_chain) {
- for_each_context(priv, ctx) {
- priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
- if (ctx->active.rx_chain != ctx->staging.rx_chain)
- iwlcore_commit_rxon(priv, ctx);
- }
+ for_each_context(priv, ctx) {
+ iwlagn_set_rxon_chain(priv, ctx);
+ if (ctx->active.rx_chain != ctx->staging.rx_chain)
+ iwlagn_commit_rxon(priv, ctx);
}
}
@@ -134,7 +122,9 @@ int iwlagn_send_beacon_cmd(struct iwl_priv *priv)
struct iwl_tx_beacon_cmd *tx_beacon_cmd;
struct iwl_host_cmd cmd = {
.id = REPLY_TX_BEACON,
+ .flags = CMD_SYNC,
};
+ struct ieee80211_tx_info *info;
u32 frame_size;
u32 rate_flags;
u32 rate;
@@ -144,7 +134,7 @@ int iwlagn_send_beacon_cmd(struct iwl_priv *priv)
* beacon contents.
*/
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
if (!priv->beacon_ctx) {
IWL_ERR(priv, "trying to build beacon w/o beacon context!\n");
@@ -175,14 +165,31 @@ int iwlagn_send_beacon_cmd(struct iwl_priv *priv)
frame_size);
/* Set up packet rate and flags */
- rate = iwl_rate_get_lowest_plcp(priv, priv->beacon_ctx);
+ info = IEEE80211_SKB_CB(priv->beacon_skb);
+
+ /*
+ * Let's set up the rate at least somewhat correctly;
+ * it will currently not actually be used by the uCode,
+ * it uses the broadcast station's rate instead.
+ */
+ if (info->control.rates[0].idx < 0 ||
+ info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
+ rate = 0;
+ else
+ rate = info->control.rates[0].idx;
+
priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
- priv->hw_params.valid_tx_ant);
+ hw_params(priv).valid_tx_ant);
rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
- if ((rate >= IWL_FIRST_CCK_RATE) && (rate <= IWL_LAST_CCK_RATE))
+
+ /* In mac80211, rates for 5 GHz start at 0 */
+ if (info->band == IEEE80211_BAND_5GHZ)
+ rate += IWL_FIRST_OFDM_RATE;
+ else if (rate >= IWL_FIRST_CCK_RATE && rate <= IWL_LAST_CCK_RATE)
rate_flags |= RATE_MCS_CCK_MSK;
- tx_beacon_cmd->tx.rate_n_flags = iwl_hw_set_rate_n_flags(rate,
- rate_flags);
+
+ tx_beacon_cmd->tx.rate_n_flags =
+ iwl_hw_set_rate_n_flags(rate, rate_flags);
/* Submit command */
cmd.len[0] = sizeof(*tx_beacon_cmd);
@@ -192,7 +199,7 @@ int iwlagn_send_beacon_cmd(struct iwl_priv *priv)
cmd.data[1] = priv->beacon_skb->data;
cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
- return iwl_send_cmd_sync(priv, &cmd);
+ return iwl_trans_send_cmd(trans(priv), &cmd);
}
static void iwl_bg_beacon_update(struct work_struct *work)
@@ -201,7 +208,7 @@ static void iwl_bg_beacon_update(struct work_struct *work)
container_of(work, struct iwl_priv, beacon_update);
struct sk_buff *beacon;
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
if (!priv->beacon_ctx) {
IWL_ERR(priv, "updating beacon w/o beacon context!\n");
goto out;
@@ -231,7 +238,7 @@ static void iwl_bg_beacon_update(struct work_struct *work)
iwlagn_send_beacon_cmd(priv);
out:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
static void iwl_bg_bt_runtime_config(struct work_struct *work)
@@ -239,13 +246,13 @@ static void iwl_bg_bt_runtime_config(struct work_struct *work)
struct iwl_priv *priv =
container_of(work, struct iwl_priv, bt_runtime_config);
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
/* dont send host command if rf-kill is on */
- if (!iwl_is_ready_rf(priv))
+ if (!iwl_is_ready_rf(priv->shrd))
return;
- priv->cfg->ops->hcmd->send_bt_config(priv);
+ iwlagn_send_advance_bt_config(priv);
}
static void iwl_bg_bt_full_concurrency(struct work_struct *work)
@@ -254,13 +261,13 @@ static void iwl_bg_bt_full_concurrency(struct work_struct *work)
container_of(work, struct iwl_priv, bt_full_concurrency);
struct iwl_rxon_context *ctx;
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
goto out;
/* dont send host command if rf-kill is on */
- if (!iwl_is_ready_rf(priv))
+ if (!iwl_is_ready_rf(priv->shrd))
goto out;
IWL_DEBUG_INFO(priv, "BT coex in %s mode\n",
@@ -272,14 +279,13 @@ static void iwl_bg_bt_full_concurrency(struct work_struct *work)
* to avoid 3-wire collisions
*/
for_each_context(priv, ctx) {
- if (priv->cfg->ops->hcmd->set_rxon_chain)
- priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
- iwlcore_commit_rxon(priv, ctx);
+ iwlagn_set_rxon_chain(priv, ctx);
+ iwlagn_commit_rxon(priv, ctx);
}
- priv->cfg->ops->hcmd->send_bt_config(priv);
+ iwlagn_send_advance_bt_config(priv);
out:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
/**
@@ -296,11 +302,11 @@ static void iwl_bg_statistics_periodic(unsigned long data)
{
struct iwl_priv *priv = (struct iwl_priv *)data;
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
/* dont send host command if rf-kill is on */
- if (!iwl_is_ready_rf(priv))
+ if (!iwl_is_ready_rf(priv->shrd))
return;
iwl_send_statistics_request(priv, CMD_ASYNC, false);
@@ -322,14 +328,14 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
/* Make sure device is powered up for SRAM reads */
- spin_lock_irqsave(&priv->reg_lock, reg_flags);
- if (iwl_grab_nic_access(priv)) {
- spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+ spin_lock_irqsave(&bus(priv)->reg_lock, reg_flags);
+ if (iwl_grab_nic_access(bus(priv))) {
+ spin_unlock_irqrestore(&bus(priv)->reg_lock, reg_flags);
return;
}
/* Set starting address; reads will auto-increment */
- iwl_write32(priv, HBUS_TARG_MEM_RADDR, ptr);
+ iwl_write32(bus(priv), HBUS_TARG_MEM_RADDR, ptr);
rmb();
/*
@@ -337,20 +343,20 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
* place event id # at far right for easier visual parsing.
*/
for (i = 0; i < num_events; i++) {
- ev = iwl_read32(priv, HBUS_TARG_MEM_RDAT);
- time = iwl_read32(priv, HBUS_TARG_MEM_RDAT);
+ ev = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
+ time = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
if (mode == 0) {
trace_iwlwifi_dev_ucode_cont_event(priv,
0, time, ev);
} else {
- data = iwl_read32(priv, HBUS_TARG_MEM_RDAT);
+ data = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
trace_iwlwifi_dev_ucode_cont_event(priv,
time, data, ev);
}
}
/* Allow device to power down */
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+ iwl_release_nic_access(bus(priv));
+ spin_unlock_irqrestore(&bus(priv)->reg_lock, reg_flags);
}
static void iwl_continuous_event_trace(struct iwl_priv *priv)
@@ -362,11 +368,13 @@ static void iwl_continuous_event_trace(struct iwl_priv *priv)
u32 next_entry; /* index of next entry to be written by uCode */
base = priv->device_pointers.error_event_table;
- if (priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
- capacity = iwl_read_targ_mem(priv, base);
- num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32)));
- mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32)));
- next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32)));
+ if (iwlagn_hw_valid_rtc_data_addr(base)) {
+ capacity = iwl_read_targ_mem(bus(priv), base);
+ num_wraps = iwl_read_targ_mem(bus(priv),
+ base + (2 * sizeof(u32)));
+ mode = iwl_read_targ_mem(bus(priv), base + (1 * sizeof(u32)));
+ next_entry = iwl_read_targ_mem(bus(priv),
+ base + (3 * sizeof(u32)));
} else
return;
@@ -417,7 +425,7 @@ static void iwl_bg_ucode_trace(unsigned long data)
{
struct iwl_priv *priv = (struct iwl_priv *)data;
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
if (priv->event_log.ucode_trace) {
@@ -433,528 +441,46 @@ static void iwl_bg_tx_flush(struct work_struct *work)
struct iwl_priv *priv =
container_of(work, struct iwl_priv, tx_flush);
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
/* do nothing if rf-kill is on */
- if (!iwl_is_ready_rf(priv))
+ if (!iwl_is_ready_rf(priv->shrd))
return;
- if (priv->cfg->ops->lib->txfifo_flush) {
- IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n");
- iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL);
- }
-}
-
-/**
- * iwl_rx_handle - Main entry function for receiving responses from uCode
- *
- * Uses the priv->rx_handlers callback function array to invoke
- * the appropriate handlers, including command responses,
- * frame-received notifications, and other notifications.
- */
-static void iwl_rx_handle(struct iwl_priv *priv)
-{
- struct iwl_rx_mem_buffer *rxb;
- struct iwl_rx_packet *pkt;
- struct iwl_rx_queue *rxq = &priv->rxq;
- u32 r, i;
- int reclaim;
- unsigned long flags;
- u8 fill_rx = 0;
- u32 count = 8;
- int total_empty;
-
- /* uCode's read index (stored in shared DRAM) indicates the last Rx
- * buffer that the driver may process (last buffer filled by ucode). */
- r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF;
- i = rxq->read;
-
- /* Rx interrupt, but nothing sent from uCode */
- if (i == r)
- IWL_DEBUG_RX(priv, "r = %d, i = %d\n", r, i);
-
- /* calculate total frames need to be restock after handling RX */
- total_empty = r - rxq->write_actual;
- if (total_empty < 0)
- total_empty += RX_QUEUE_SIZE;
-
- if (total_empty > (RX_QUEUE_SIZE / 2))
- fill_rx = 1;
-
- while (i != r) {
- int len;
-
- rxb = rxq->queue[i];
-
- /* If an RXB doesn't have a Rx queue slot associated with it,
- * then a bug has been introduced in the queue refilling
- * routines -- catch it here */
- if (WARN_ON(rxb == NULL)) {
- i = (i + 1) & RX_QUEUE_MASK;
- continue;
- }
-
- rxq->queue[i] = NULL;
-
- pci_unmap_page(priv->pci_dev, rxb->page_dma,
- PAGE_SIZE << priv->hw_params.rx_page_order,
- PCI_DMA_FROMDEVICE);
- pkt = rxb_addr(rxb);
-
- len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
- len += sizeof(u32); /* account for status word */
- trace_iwlwifi_dev_rx(priv, pkt, len);
-
- /* Reclaim a command buffer only if this packet is a response
- * to a (driver-originated) command.
- * If the packet (e.g. Rx frame) originated from uCode,
- * there is no command buffer to reclaim.
- * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
- * but apparently a few don't get set; catch them here. */
- reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) &&
- (pkt->hdr.cmd != REPLY_RX_PHY_CMD) &&
- (pkt->hdr.cmd != REPLY_RX) &&
- (pkt->hdr.cmd != REPLY_RX_MPDU_CMD) &&
- (pkt->hdr.cmd != REPLY_COMPRESSED_BA) &&
- (pkt->hdr.cmd != STATISTICS_NOTIFICATION) &&
- (pkt->hdr.cmd != REPLY_TX);
-
- /*
- * Do the notification wait before RX handlers so
- * even if the RX handler consumes the RXB we have
- * access to it in the notification wait entry.
- */
- if (!list_empty(&priv->_agn.notif_waits)) {
- struct iwl_notification_wait *w;
-
- spin_lock(&priv->_agn.notif_wait_lock);
- list_for_each_entry(w, &priv->_agn.notif_waits, list) {
- if (w->cmd == pkt->hdr.cmd) {
- w->triggered = true;
- if (w->fn)
- w->fn(priv, pkt, w->fn_data);
- }
- }
- spin_unlock(&priv->_agn.notif_wait_lock);
-
- wake_up_all(&priv->_agn.notif_waitq);
- }
- if (priv->pre_rx_handler)
- priv->pre_rx_handler(priv, rxb);
-
- /* Based on type of command response or notification,
- * handle those that need handling via function in
- * rx_handlers table. See iwl_setup_rx_handlers() */
- if (priv->rx_handlers[pkt->hdr.cmd]) {
- IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r,
- i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
- priv->isr_stats.rx_handlers[pkt->hdr.cmd]++;
- priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
- } else {
- /* No handling needed */
- IWL_DEBUG_RX(priv,
- "r %d i %d No handler needed for %s, 0x%02x\n",
- r, i, get_cmd_string(pkt->hdr.cmd),
- pkt->hdr.cmd);
- }
-
- /*
- * XXX: After here, we should always check rxb->page
- * against NULL before touching it or its virtual
- * memory (pkt). Because some rx_handler might have
- * already taken or freed the pages.
- */
-
- if (reclaim) {
- /* Invoke any callbacks, transfer the buffer to caller,
- * and fire off the (possibly) blocking iwl_send_cmd()
- * as we reclaim the driver command queue */
- if (rxb->page)
- iwl_tx_cmd_complete(priv, rxb);
- else
- IWL_WARN(priv, "Claim null rxb?\n");
- }
-
- /* Reuse the page if possible. For notification packets and
- * SKBs that fail to Rx correctly, add them back into the
- * rx_free list for reuse later. */
- spin_lock_irqsave(&rxq->lock, flags);
- if (rxb->page != NULL) {
- rxb->page_dma = pci_map_page(priv->pci_dev, rxb->page,
- 0, PAGE_SIZE << priv->hw_params.rx_page_order,
- PCI_DMA_FROMDEVICE);
- list_add_tail(&rxb->list, &rxq->rx_free);
- rxq->free_count++;
- } else
- list_add_tail(&rxb->list, &rxq->rx_used);
-
- spin_unlock_irqrestore(&rxq->lock, flags);
-
- i = (i + 1) & RX_QUEUE_MASK;
- /* If there are a lot of unused frames,
- * restock the Rx queue so ucode wont assert. */
- if (fill_rx) {
- count++;
- if (count >= 8) {
- rxq->read = i;
- iwlagn_rx_replenish_now(priv);
- count = 0;
- }
- }
- }
-
- /* Backtrack one entry */
- rxq->read = i;
- if (fill_rx)
- iwlagn_rx_replenish_now(priv);
- else
- iwlagn_rx_queue_restock(priv);
+ IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n");
+ iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL);
}
-/* tasklet for iwlagn interrupt */
-static void iwl_irq_tasklet(struct iwl_priv *priv)
-{
- u32 inta = 0;
- u32 handled = 0;
- unsigned long flags;
- u32 i;
-#ifdef CONFIG_IWLWIFI_DEBUG
- u32 inta_mask;
-#endif
-
- spin_lock_irqsave(&priv->lock, flags);
-
- /* Ack/clear/reset pending uCode interrupts.
- * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
- */
- /* There is a hardware bug in the interrupt mask function that some
- * interrupts (i.e. CSR_INT_BIT_SCD) can still be generated even if
- * they are disabled in the CSR_INT_MASK register. Furthermore the
- * ICT interrupt handling mechanism has another bug that might cause
- * these unmasked interrupts fail to be detected. We workaround the
- * hardware bugs here by ACKing all the possible interrupts so that
- * interrupt coalescing can still be achieved.
- */
- iwl_write32(priv, CSR_INT, priv->_agn.inta | ~priv->inta_mask);
-
- inta = priv->_agn.inta;
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (iwl_get_debug_level(priv) & IWL_DL_ISR) {
- /* just for debug */
- inta_mask = iwl_read32(priv, CSR_INT_MASK);
- IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x\n ",
- inta, inta_mask);
- }
-#endif
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- /* saved interrupt in inta variable now we can reset priv->_agn.inta */
- priv->_agn.inta = 0;
-
- /* Now service all interrupt bits discovered above. */
- if (inta & CSR_INT_BIT_HW_ERR) {
- IWL_ERR(priv, "Hardware error detected. Restarting.\n");
-
- /* Tell the device to stop sending interrupts */
- iwl_disable_interrupts(priv);
-
- priv->isr_stats.hw++;
- iwl_irq_handle_error(priv);
-
- handled |= CSR_INT_BIT_HW_ERR;
-
- return;
- }
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) {
- /* NIC fires this, but we don't use it, redundant with WAKEUP */
- if (inta & CSR_INT_BIT_SCD) {
- IWL_DEBUG_ISR(priv, "Scheduler finished to transmit "
- "the frame/frames.\n");
- priv->isr_stats.sch++;
- }
-
- /* Alive notification via Rx interrupt will do the real work */
- if (inta & CSR_INT_BIT_ALIVE) {
- IWL_DEBUG_ISR(priv, "Alive interrupt\n");
- priv->isr_stats.alive++;
- }
- }
-#endif
- /* Safely ignore these bits for debug checks below */
- inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
-
- /* HW RF KILL switch toggled */
- if (inta & CSR_INT_BIT_RF_KILL) {
- int hw_rf_kill = 0;
- if (!(iwl_read32(priv, CSR_GP_CNTRL) &
- CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
- hw_rf_kill = 1;
-
- IWL_WARN(priv, "RF_KILL bit toggled to %s.\n",
- hw_rf_kill ? "disable radio" : "enable radio");
-
- priv->isr_stats.rfkill++;
-
- /* driver only loads ucode once setting the interface up.
- * the driver allows loading the ucode even if the radio
- * is killed. Hence update the killswitch state here. The
- * rfkill handler will care about restarting if needed.
- */
- if (!test_bit(STATUS_ALIVE, &priv->status)) {
- if (hw_rf_kill)
- set_bit(STATUS_RF_KILL_HW, &priv->status);
- else
- clear_bit(STATUS_RF_KILL_HW, &priv->status);
- wiphy_rfkill_set_hw_state(priv->hw->wiphy, hw_rf_kill);
- }
-
- handled |= CSR_INT_BIT_RF_KILL;
- }
-
- /* Chip got too hot and stopped itself */
- if (inta & CSR_INT_BIT_CT_KILL) {
- IWL_ERR(priv, "Microcode CT kill error detected.\n");
- priv->isr_stats.ctkill++;
- handled |= CSR_INT_BIT_CT_KILL;
- }
-
- /* Error detected by uCode */
- if (inta & CSR_INT_BIT_SW_ERR) {
- IWL_ERR(priv, "Microcode SW error detected. "
- " Restarting 0x%X.\n", inta);
- priv->isr_stats.sw++;
- iwl_irq_handle_error(priv);
- handled |= CSR_INT_BIT_SW_ERR;
- }
-
- /* uCode wakes up after power-down sleep */
- if (inta & CSR_INT_BIT_WAKEUP) {
- IWL_DEBUG_ISR(priv, "Wakeup interrupt\n");
- iwl_rx_queue_update_write_ptr(priv, &priv->rxq);
- for (i = 0; i < priv->hw_params.max_txq_num; i++)
- iwl_txq_update_write_ptr(priv, &priv->txq[i]);
-
- priv->isr_stats.wakeup++;
-
- handled |= CSR_INT_BIT_WAKEUP;
- }
-
- /* All uCode command responses, including Tx command responses,
- * Rx "responses" (frame-received notification), and other
- * notifications from uCode come through here*/
- if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX |
- CSR_INT_BIT_RX_PERIODIC)) {
- IWL_DEBUG_ISR(priv, "Rx interrupt\n");
- if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) {
- handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
- iwl_write32(priv, CSR_FH_INT_STATUS,
- CSR_FH_INT_RX_MASK);
- }
- if (inta & CSR_INT_BIT_RX_PERIODIC) {
- handled |= CSR_INT_BIT_RX_PERIODIC;
- iwl_write32(priv, CSR_INT, CSR_INT_BIT_RX_PERIODIC);
- }
- /* Sending RX interrupt require many steps to be done in the
- * the device:
- * 1- write interrupt to current index in ICT table.
- * 2- dma RX frame.
- * 3- update RX shared data to indicate last write index.
- * 4- send interrupt.
- * This could lead to RX race, driver could receive RX interrupt
- * but the shared data changes does not reflect this;
- * periodic interrupt will detect any dangling Rx activity.
- */
-
- /* Disable periodic interrupt; we use it as just a one-shot. */
- iwl_write8(priv, CSR_INT_PERIODIC_REG,
- CSR_INT_PERIODIC_DIS);
- iwl_rx_handle(priv);
-
- /*
- * Enable periodic interrupt in 8 msec only if we received
- * real RX interrupt (instead of just periodic int), to catch
- * any dangling Rx interrupt. If it was just the periodic
- * interrupt, there was no dangling Rx activity, and no need
- * to extend the periodic interrupt; one-shot is enough.
- */
- if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX))
- iwl_write8(priv, CSR_INT_PERIODIC_REG,
- CSR_INT_PERIODIC_ENA);
-
- priv->isr_stats.rx++;
- }
-
- /* This "Tx" DMA channel is used only for loading uCode */
- if (inta & CSR_INT_BIT_FH_TX) {
- iwl_write32(priv, CSR_FH_INT_STATUS, CSR_FH_INT_TX_MASK);
- IWL_DEBUG_ISR(priv, "uCode load interrupt\n");
- priv->isr_stats.tx++;
- handled |= CSR_INT_BIT_FH_TX;
- /* Wake up uCode load routine, now that load is complete */
- priv->ucode_write_complete = 1;
- wake_up_interruptible(&priv->wait_command_queue);
- }
-
- if (inta & ~handled) {
- IWL_ERR(priv, "Unhandled INTA bits 0x%08x\n", inta & ~handled);
- priv->isr_stats.unhandled++;
- }
-
- if (inta & ~(priv->inta_mask)) {
- IWL_WARN(priv, "Disabled INTA bits 0x%08x were pending\n",
- inta & ~priv->inta_mask);
- }
-
- /* Re-enable all interrupts */
- /* only Re-enable if disabled by irq */
- if (test_bit(STATUS_INT_ENABLED, &priv->status))
- iwl_enable_interrupts(priv);
- /* Re-enable RF_KILL if it occurred */
- else if (handled & CSR_INT_BIT_RF_KILL)
- iwl_enable_rfkill_int(priv);
-}
-
-/*****************************************************************************
- *
- * sysfs attributes
- *
- *****************************************************************************/
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-
-/*
- * The following adds a new attribute to the sysfs representation
- * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/)
- * used for controlling the debug level.
- *
- * See the level definitions in iwl for details.
- *
- * The debug_level being managed using sysfs below is a per device debug
- * level that is used instead of the global debug level if it (the per
- * device debug level) is set.
- */
-static ssize_t show_debug_level(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct iwl_priv *priv = dev_get_drvdata(d);
- return sprintf(buf, "0x%08X\n", iwl_get_debug_level(priv));
-}
-static ssize_t store_debug_level(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct iwl_priv *priv = dev_get_drvdata(d);
- unsigned long val;
- int ret;
-
- ret = strict_strtoul(buf, 0, &val);
- if (ret)
- IWL_ERR(priv, "%s is not in hex or decimal form.\n", buf);
- else {
- priv->debug_level = val;
- if (iwl_alloc_traffic_mem(priv))
- IWL_ERR(priv,
- "Not enough memory to generate traffic log\n");
- }
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO,
- show_debug_level, store_debug_level);
-
-
-#endif /* CONFIG_IWLWIFI_DEBUG */
-
-
-static ssize_t show_temperature(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct iwl_priv *priv = dev_get_drvdata(d);
-
- if (!iwl_is_alive(priv))
- return -EAGAIN;
-
- return sprintf(buf, "%d\n", priv->temperature);
-}
-
-static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL);
-
-static ssize_t show_tx_power(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct iwl_priv *priv = dev_get_drvdata(d);
-
- if (!iwl_is_ready_rf(priv))
- return sprintf(buf, "off\n");
- else
- return sprintf(buf, "%d\n", priv->tx_power_user_lmt);
-}
-
-static ssize_t store_tx_power(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct iwl_priv *priv = dev_get_drvdata(d);
- unsigned long val;
- int ret;
-
- ret = strict_strtoul(buf, 10, &val);
- if (ret)
- IWL_INFO(priv, "%s is not in decimal form.\n", buf);
- else {
- ret = iwl_set_tx_power(priv, val, false);
- if (ret)
- IWL_ERR(priv, "failed setting tx power (0x%d).\n",
- ret);
- else
- ret = count;
- }
- return ret;
-}
-
-static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power);
-
-static struct attribute *iwl_sysfs_entries[] = {
- &dev_attr_temperature.attr,
- &dev_attr_tx_power.attr,
-#ifdef CONFIG_IWLWIFI_DEBUG
- &dev_attr_debug_level.attr,
-#endif
- NULL
-};
-
-static struct attribute_group iwl_attribute_group = {
- .name = NULL, /* put in device directory */
- .attrs = iwl_sysfs_entries,
-};
-
/******************************************************************************
*
* uCode download functions
*
******************************************************************************/
-static void iwl_free_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc)
+static void iwl_free_fw_desc(struct iwl_priv *priv, struct fw_desc *desc)
{
if (desc->v_addr)
- dma_free_coherent(&pci_dev->dev, desc->len,
+ dma_free_coherent(bus(priv)->dev, desc->len,
desc->v_addr, desc->p_addr);
desc->v_addr = NULL;
desc->len = 0;
}
-static void iwl_free_fw_img(struct pci_dev *pci_dev, struct fw_img *img)
+static void iwl_free_fw_img(struct iwl_priv *priv, struct fw_img *img)
+{
+ iwl_free_fw_desc(priv, &img->code);
+ iwl_free_fw_desc(priv, &img->data);
+}
+
+static void iwl_dealloc_ucode(struct iwl_priv *priv)
{
- iwl_free_fw_desc(pci_dev, &img->code);
- iwl_free_fw_desc(pci_dev, &img->data);
+ iwl_free_fw_img(priv, &priv->ucode_rt);
+ iwl_free_fw_img(priv, &priv->ucode_init);
+ iwl_free_fw_img(priv, &priv->ucode_wowlan);
}
-static int iwl_alloc_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc,
+static int iwl_alloc_fw_desc(struct iwl_priv *priv, struct fw_desc *desc,
const void *data, size_t len)
{
if (!len) {
@@ -962,21 +488,74 @@ static int iwl_alloc_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc,
return -EINVAL;
}
- desc->v_addr = dma_alloc_coherent(&pci_dev->dev, len,
+ desc->v_addr = dma_alloc_coherent(bus(priv)->dev, len,
&desc->p_addr, GFP_KERNEL);
if (!desc->v_addr)
return -ENOMEM;
+
desc->len = len;
memcpy(desc->v_addr, data, len);
return 0;
}
-static void iwl_dealloc_ucode_pci(struct iwl_priv *priv)
+static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
{
- iwl_free_fw_img(priv->pci_dev, &priv->ucode_rt);
- iwl_free_fw_img(priv->pci_dev, &priv->ucode_init);
+ int i;
+
+ /*
+ * The default context is always valid,
+ * the PAN context depends on uCode.
+ */
+ priv->shrd->valid_contexts = BIT(IWL_RXON_CTX_BSS);
+ if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN)
+ priv->shrd->valid_contexts |= BIT(IWL_RXON_CTX_PAN);
+
+ for (i = 0; i < NUM_IWL_RXON_CTX; i++)
+ priv->contexts[i].ctxid = i;
+
+ priv->contexts[IWL_RXON_CTX_BSS].always_active = true;
+ priv->contexts[IWL_RXON_CTX_BSS].is_active = true;
+ priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON;
+ priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING;
+ priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC;
+ priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM;
+ priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID;
+ priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY;
+ priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes =
+ BIT(NL80211_IFTYPE_ADHOC);
+ priv->contexts[IWL_RXON_CTX_BSS].interface_modes =
+ BIT(NL80211_IFTYPE_STATION);
+ priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP;
+ priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS;
+ priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS;
+ priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS;
+
+ priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON;
+ priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd =
+ REPLY_WIPAN_RXON_TIMING;
+ priv->contexts[IWL_RXON_CTX_PAN].rxon_assoc_cmd =
+ REPLY_WIPAN_RXON_ASSOC;
+ priv->contexts[IWL_RXON_CTX_PAN].qos_cmd = REPLY_WIPAN_QOS_PARAM;
+ priv->contexts[IWL_RXON_CTX_PAN].ap_sta_id = IWL_AP_ID_PAN;
+ priv->contexts[IWL_RXON_CTX_PAN].wep_key_cmd = REPLY_WIPAN_WEPKEY;
+ priv->contexts[IWL_RXON_CTX_PAN].bcast_sta_id = IWLAGN_PAN_BCAST_ID;
+ priv->contexts[IWL_RXON_CTX_PAN].station_flags = STA_FLG_PAN_STATION;
+ priv->contexts[IWL_RXON_CTX_PAN].interface_modes =
+ BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP);
+
+ if (ucode_flags & IWL_UCODE_TLV_FLAGS_P2P)
+ priv->contexts[IWL_RXON_CTX_PAN].interface_modes |=
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO);
+
+ priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP;
+ priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA;
+ priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P;
+
+ BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
}
+
struct iwlagn_ucode_capabilities {
u32 max_probe_length;
u32 standard_phy_calibration_size;
@@ -984,7 +563,7 @@ struct iwlagn_ucode_capabilities {
};
static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context);
-static int iwl_mac_setup_register(struct iwl_priv *priv,
+static int iwlagn_mac_setup_register(struct iwl_priv *priv,
struct iwlagn_ucode_capabilities *capa);
#define UCODE_EXPERIMENTAL_INDEX 100
@@ -1021,13 +600,14 @@ static int __must_check iwl_request_firmware(struct iwl_priv *priv, bool first)
priv->firmware_name);
return request_firmware_nowait(THIS_MODULE, 1, priv->firmware_name,
- &priv->pci_dev->dev, GFP_KERNEL, priv,
- iwl_ucode_callback);
+ bus(priv)->dev,
+ GFP_KERNEL, priv, iwl_ucode_callback);
}
struct iwlagn_firmware_pieces {
- const void *inst, *data, *init, *init_data;
- size_t inst_size, data_size, init_size, init_data_size;
+ const void *inst, *data, *init, *init_data, *wowlan_inst, *wowlan_data;
+ size_t inst_size, data_size, init_size, init_data_size,
+ wowlan_inst_size, wowlan_data_size;
u32 build;
@@ -1100,8 +680,6 @@ static int iwlagn_load_legacy_firmware(struct iwl_priv *priv,
return 0;
}
-static int iwlagn_wanted_ucode_alternative = 1;
-
static int iwlagn_load_firmware(struct iwl_priv *priv,
const struct firmware *ucode_raw,
struct iwlagn_firmware_pieces *pieces,
@@ -1111,7 +689,8 @@ static int iwlagn_load_firmware(struct iwl_priv *priv,
struct iwl_ucode_tlv *tlv;
size_t len = ucode_raw->size;
const u8 *data;
- int wanted_alternative = iwlagn_wanted_ucode_alternative, tmp;
+ int wanted_alternative = iwlagn_mod_params.wanted_ucode_alternative;
+ int tmp;
u64 alternatives;
u32 tlv_len;
enum iwl_ucode_tlv_type tlv_type;
@@ -1266,6 +845,14 @@ static int iwlagn_load_firmware(struct iwl_priv *priv,
goto invalid_tlv_len;
priv->enhance_sensitivity_table = true;
break;
+ case IWL_UCODE_TLV_WOWLAN_INST:
+ pieces->wowlan_inst = tlv_data;
+ pieces->wowlan_inst_size = tlv_len;
+ break;
+ case IWL_UCODE_TLV_WOWLAN_DATA:
+ pieces->wowlan_data = tlv_data;
+ pieces->wowlan_data_size = tlv_len;
+ break;
case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE:
if (tlv_len != sizeof(u32))
goto invalid_tlv_len;
@@ -1306,6 +893,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
int err;
struct iwlagn_firmware_pieces pieces;
const unsigned int api_max = priv->cfg->ucode_api_max;
+ unsigned int api_ok = priv->cfg->ucode_api_ok;
const unsigned int api_min = priv->cfg->ucode_api_min;
u32 api_ver;
char buildstr[25];
@@ -1316,10 +904,13 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE,
};
+ if (!api_ok)
+ api_ok = api_max;
+
memset(&pieces, 0, sizeof(pieces));
if (!ucode_raw) {
- if (priv->fw_index <= priv->cfg->ucode_api_max)
+ if (priv->fw_index <= api_ok)
IWL_ERR(priv,
"request for firmware file '%s' failed.\n",
priv->firmware_name);
@@ -1365,12 +956,18 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
goto try_again;
}
- if (api_ver != api_max)
- IWL_ERR(priv,
- "Firmware has old API version. Expected v%u, "
- "got v%u. New firmware can be obtained "
- "from http://www.intellinuxwireless.org.\n",
- api_max, api_ver);
+ if (api_ver < api_ok) {
+ if (api_ok != api_max)
+ IWL_ERR(priv, "Firmware has old API version, "
+ "expected v%u through v%u, got v%u.\n",
+ api_ok, api_max, api_ver);
+ else
+ IWL_ERR(priv, "Firmware has old API version, "
+ "expected v%u, got v%u.\n",
+ api_max, api_ver);
+ IWL_ERR(priv, "New firmware can be obtained from "
+ "http://www.intellinuxwireless.org/.\n");
+ }
}
if (build)
@@ -1414,25 +1011,25 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
pieces.init_data_size);
/* Verify that uCode images will fit in card's SRAM */
- if (pieces.inst_size > priv->hw_params.max_inst_size) {
+ if (pieces.inst_size > hw_params(priv).max_inst_size) {
IWL_ERR(priv, "uCode instr len %Zd too large to fit in\n",
pieces.inst_size);
goto try_again;
}
- if (pieces.data_size > priv->hw_params.max_data_size) {
+ if (pieces.data_size > hw_params(priv).max_data_size) {
IWL_ERR(priv, "uCode data len %Zd too large to fit in\n",
pieces.data_size);
goto try_again;
}
- if (pieces.init_size > priv->hw_params.max_inst_size) {
+ if (pieces.init_size > hw_params(priv).max_inst_size) {
IWL_ERR(priv, "uCode init instr len %Zd too large to fit in\n",
pieces.init_size);
goto try_again;
}
- if (pieces.init_data_size > priv->hw_params.max_data_size) {
+ if (pieces.init_data_size > hw_params(priv).max_data_size) {
IWL_ERR(priv, "uCode init data len %Zd too large to fit in\n",
pieces.init_data_size);
goto try_again;
@@ -1443,23 +1040,35 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
/* Runtime instructions and 2 copies of data:
* 1) unmodified from disk
* 2) backup cache for save/restore during power-downs */
- if (iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_rt.code,
+ if (iwl_alloc_fw_desc(priv, &priv->ucode_rt.code,
pieces.inst, pieces.inst_size))
goto err_pci_alloc;
- if (iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_rt.data,
+ if (iwl_alloc_fw_desc(priv, &priv->ucode_rt.data,
pieces.data, pieces.data_size))
goto err_pci_alloc;
/* Initialization instructions and data */
if (pieces.init_size && pieces.init_data_size) {
- if (iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_init.code,
+ if (iwl_alloc_fw_desc(priv, &priv->ucode_init.code,
pieces.init, pieces.init_size))
goto err_pci_alloc;
- if (iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_init.data,
+ if (iwl_alloc_fw_desc(priv, &priv->ucode_init.data,
pieces.init_data, pieces.init_data_size))
goto err_pci_alloc;
}
+ /* WoWLAN instructions and data */
+ if (pieces.wowlan_inst_size && pieces.wowlan_data_size) {
+ if (iwl_alloc_fw_desc(priv, &priv->ucode_wowlan.code,
+ pieces.wowlan_inst,
+ pieces.wowlan_inst_size))
+ goto err_pci_alloc;
+ if (iwl_alloc_fw_desc(priv, &priv->ucode_wowlan.data,
+ pieces.wowlan_data,
+ pieces.wowlan_data_size))
+ goto err_pci_alloc;
+ }
+
/* Now that we can no longer fail, copy information */
/*
@@ -1467,34 +1076,41 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
* for each event, which is of mode 1 (including timestamp) for all
* new microcodes that include this information.
*/
- priv->_agn.init_evtlog_ptr = pieces.init_evtlog_ptr;
+ priv->init_evtlog_ptr = pieces.init_evtlog_ptr;
if (pieces.init_evtlog_size)
- priv->_agn.init_evtlog_size = (pieces.init_evtlog_size - 16)/12;
+ priv->init_evtlog_size = (pieces.init_evtlog_size - 16)/12;
else
- priv->_agn.init_evtlog_size =
+ priv->init_evtlog_size =
priv->cfg->base_params->max_event_log_size;
- priv->_agn.init_errlog_ptr = pieces.init_errlog_ptr;
- priv->_agn.inst_evtlog_ptr = pieces.inst_evtlog_ptr;
+ priv->init_errlog_ptr = pieces.init_errlog_ptr;
+ priv->inst_evtlog_ptr = pieces.inst_evtlog_ptr;
if (pieces.inst_evtlog_size)
- priv->_agn.inst_evtlog_size = (pieces.inst_evtlog_size - 16)/12;
+ priv->inst_evtlog_size = (pieces.inst_evtlog_size - 16)/12;
else
- priv->_agn.inst_evtlog_size =
+ priv->inst_evtlog_size =
priv->cfg->base_params->max_event_log_size;
- priv->_agn.inst_errlog_ptr = pieces.inst_errlog_ptr;
+ priv->inst_errlog_ptr = pieces.inst_errlog_ptr;
priv->new_scan_threshold_behaviour =
!!(ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWSCAN);
+ if (!(priv->cfg->sku & EEPROM_SKU_CAP_IPAN_ENABLE))
+ ucode_capa.flags &= ~IWL_UCODE_TLV_FLAGS_PAN;
+
+ /*
+ * if not PAN, then don't support P2P -- might be a uCode
+ * packaging bug or due to the eeprom check above
+ */
+ if (!(ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN))
+ ucode_capa.flags &= ~IWL_UCODE_TLV_FLAGS_P2P;
+
if (ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN) {
- priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN);
priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN;
- } else
+ priv->shrd->cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
+ } else {
priv->sta_key_max_num = STA_KEY_MAX_NUM;
-
- if (priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))
- priv->cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
- else
- priv->cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
+ priv->shrd->cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
+ }
/*
* figure out the offset of chain noise reset and gain commands
@@ -1505,17 +1121,20 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
ucode_capa.standard_phy_calibration_size =
IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE;
- priv->_agn.phy_calib_chain_noise_reset_cmd =
+ priv->phy_calib_chain_noise_reset_cmd =
ucode_capa.standard_phy_calibration_size;
- priv->_agn.phy_calib_chain_noise_gain_cmd =
+ priv->phy_calib_chain_noise_gain_cmd =
ucode_capa.standard_phy_calibration_size + 1;
+ /* initialize all valid contexts */
+ iwl_init_context(priv, ucode_capa.flags);
+
/**************************************************
* This is still part of probe() in a sense...
*
* 9. Setup and register with mac80211 and debugfs
**************************************************/
- err = iwl_mac_setup_register(priv, &ucode_capa);
+ err = iwlagn_mac_setup_register(priv, &ucode_capa);
if (err)
goto out_unbind;
@@ -1523,16 +1142,9 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
if (err)
IWL_ERR(priv, "failed to create debugfs files. Ignoring error: %d\n", err);
- err = sysfs_create_group(&priv->pci_dev->dev.kobj,
- &iwl_attribute_group);
- if (err) {
- IWL_ERR(priv, "failed to create sysfs device attributes\n");
- goto out_unbind;
- }
-
/* We have our copies now, allow OS release its copies */
release_firmware(ucode_raw);
- complete(&priv->_agn.firmware_loading_complete);
+ complete(&priv->firmware_loading_complete);
return;
try_again:
@@ -1544,371 +1156,13 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
err_pci_alloc:
IWL_ERR(priv, "failed to allocate pci memory\n");
- iwl_dealloc_ucode_pci(priv);
+ iwl_dealloc_ucode(priv);
out_unbind:
- complete(&priv->_agn.firmware_loading_complete);
- device_release_driver(&priv->pci_dev->dev);
+ complete(&priv->firmware_loading_complete);
+ device_release_driver(bus(priv)->dev);
release_firmware(ucode_raw);
}
-static const char *desc_lookup_text[] = {
- "OK",
- "FAIL",
- "BAD_PARAM",
- "BAD_CHECKSUM",
- "NMI_INTERRUPT_WDG",
- "SYSASSERT",
- "FATAL_ERROR",
- "BAD_COMMAND",
- "HW_ERROR_TUNE_LOCK",
- "HW_ERROR_TEMPERATURE",
- "ILLEGAL_CHAN_FREQ",
- "VCC_NOT_STABLE",
- "FH_ERROR",
- "NMI_INTERRUPT_HOST",
- "NMI_INTERRUPT_ACTION_PT",
- "NMI_INTERRUPT_UNKNOWN",
- "UCODE_VERSION_MISMATCH",
- "HW_ERROR_ABS_LOCK",
- "HW_ERROR_CAL_LOCK_FAIL",
- "NMI_INTERRUPT_INST_ACTION_PT",
- "NMI_INTERRUPT_DATA_ACTION_PT",
- "NMI_TRM_HW_ER",
- "NMI_INTERRUPT_TRM",
- "NMI_INTERRUPT_BREAK_POINT"
- "DEBUG_0",
- "DEBUG_1",
- "DEBUG_2",
- "DEBUG_3",
-};
-
-static struct { char *name; u8 num; } advanced_lookup[] = {
- { "NMI_INTERRUPT_WDG", 0x34 },
- { "SYSASSERT", 0x35 },
- { "UCODE_VERSION_MISMATCH", 0x37 },
- { "BAD_COMMAND", 0x38 },
- { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
- { "FATAL_ERROR", 0x3D },
- { "NMI_TRM_HW_ERR", 0x46 },
- { "NMI_INTERRUPT_TRM", 0x4C },
- { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
- { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
- { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
- { "NMI_INTERRUPT_HOST", 0x66 },
- { "NMI_INTERRUPT_ACTION_PT", 0x7C },
- { "NMI_INTERRUPT_UNKNOWN", 0x84 },
- { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
- { "ADVANCED_SYSASSERT", 0 },
-};
-
-static const char *desc_lookup(u32 num)
-{
- int i;
- int max = ARRAY_SIZE(desc_lookup_text);
-
- if (num < max)
- return desc_lookup_text[num];
-
- max = ARRAY_SIZE(advanced_lookup) - 1;
- for (i = 0; i < max; i++) {
- if (advanced_lookup[i].num == num)
- break;
- }
- return advanced_lookup[i].name;
-}
-
-#define ERROR_START_OFFSET (1 * sizeof(u32))
-#define ERROR_ELEM_SIZE (7 * sizeof(u32))
-
-void iwl_dump_nic_error_log(struct iwl_priv *priv)
-{
- u32 base;
- struct iwl_error_event_table table;
-
- base = priv->device_pointers.error_event_table;
- if (priv->ucode_type == UCODE_SUBTYPE_INIT) {
- if (!base)
- base = priv->_agn.init_errlog_ptr;
- } else {
- if (!base)
- base = priv->_agn.inst_errlog_ptr;
- }
-
- if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
- IWL_ERR(priv,
- "Not valid error log pointer 0x%08X for %s uCode\n",
- base,
- (priv->ucode_type == UCODE_SUBTYPE_INIT)
- ? "Init" : "RT");
- return;
- }
-
- iwl_read_targ_mem_words(priv, base, &table, sizeof(table));
-
- if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
- IWL_ERR(priv, "Start IWL Error Log Dump:\n");
- IWL_ERR(priv, "Status: 0x%08lX, count: %d\n",
- priv->status, table.valid);
- }
-
- priv->isr_stats.err_code = table.error_id;
-
- trace_iwlwifi_dev_ucode_error(priv, table.error_id, table.tsf_low,
- table.data1, table.data2, table.line,
- table.blink1, table.blink2, table.ilink1,
- table.ilink2, table.bcon_time, table.gp1,
- table.gp2, table.gp3, table.ucode_ver,
- table.hw_ver, table.brd_ver);
- IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id,
- desc_lookup(table.error_id));
- IWL_ERR(priv, "0x%08X | uPc\n", table.pc);
- IWL_ERR(priv, "0x%08X | branchlink1\n", table.blink1);
- IWL_ERR(priv, "0x%08X | branchlink2\n", table.blink2);
- IWL_ERR(priv, "0x%08X | interruptlink1\n", table.ilink1);
- IWL_ERR(priv, "0x%08X | interruptlink2\n", table.ilink2);
- IWL_ERR(priv, "0x%08X | data1\n", table.data1);
- IWL_ERR(priv, "0x%08X | data2\n", table.data2);
- IWL_ERR(priv, "0x%08X | line\n", table.line);
- IWL_ERR(priv, "0x%08X | beacon time\n", table.bcon_time);
- IWL_ERR(priv, "0x%08X | tsf low\n", table.tsf_low);
- IWL_ERR(priv, "0x%08X | tsf hi\n", table.tsf_hi);
- IWL_ERR(priv, "0x%08X | time gp1\n", table.gp1);
- IWL_ERR(priv, "0x%08X | time gp2\n", table.gp2);
- IWL_ERR(priv, "0x%08X | time gp3\n", table.gp3);
- IWL_ERR(priv, "0x%08X | uCode version\n", table.ucode_ver);
- IWL_ERR(priv, "0x%08X | hw version\n", table.hw_ver);
- IWL_ERR(priv, "0x%08X | board version\n", table.brd_ver);
- IWL_ERR(priv, "0x%08X | hcmd\n", table.hcmd);
-}
-
-#define EVENT_START_OFFSET (4 * sizeof(u32))
-
-/**
- * iwl_print_event_log - Dump error event log to syslog
- *
- */
-static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
- u32 num_events, u32 mode,
- int pos, char **buf, size_t bufsz)
-{
- u32 i;
- u32 base; /* SRAM byte address of event log header */
- u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
- u32 ptr; /* SRAM byte address of log data */
- u32 ev, time, data; /* event log data */
- unsigned long reg_flags;
-
- if (num_events == 0)
- return pos;
-
- base = priv->device_pointers.log_event_table;
- if (priv->ucode_type == UCODE_SUBTYPE_INIT) {
- if (!base)
- base = priv->_agn.init_evtlog_ptr;
- } else {
- if (!base)
- base = priv->_agn.inst_evtlog_ptr;
- }
-
- if (mode == 0)
- event_size = 2 * sizeof(u32);
- else
- event_size = 3 * sizeof(u32);
-
- ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
-
- /* Make sure device is powered up for SRAM reads */
- spin_lock_irqsave(&priv->reg_lock, reg_flags);
- iwl_grab_nic_access(priv);
-
- /* Set starting address; reads will auto-increment */
- iwl_write32(priv, HBUS_TARG_MEM_RADDR, ptr);
- rmb();
-
- /* "time" is actually "data" for mode 0 (no timestamp).
- * place event id # at far right for easier visual parsing. */
- for (i = 0; i < num_events; i++) {
- ev = iwl_read32(priv, HBUS_TARG_MEM_RDAT);
- time = iwl_read32(priv, HBUS_TARG_MEM_RDAT);
- if (mode == 0) {
- /* data, ev */
- if (bufsz) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- "EVT_LOG:0x%08x:%04u\n",
- time, ev);
- } else {
- trace_iwlwifi_dev_ucode_event(priv, 0,
- time, ev);
- IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n",
- time, ev);
- }
- } else {
- data = iwl_read32(priv, HBUS_TARG_MEM_RDAT);
- if (bufsz) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- "EVT_LOGT:%010u:0x%08x:%04u\n",
- time, data, ev);
- } else {
- IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
- time, data, ev);
- trace_iwlwifi_dev_ucode_event(priv, time,
- data, ev);
- }
- }
- }
-
- /* Allow device to power down */
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
- return pos;
-}
-
-/**
- * iwl_print_last_event_logs - Dump the newest # of event log to syslog
- */
-static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
- u32 num_wraps, u32 next_entry,
- u32 size, u32 mode,
- int pos, char **buf, size_t bufsz)
-{
- /*
- * display the newest DEFAULT_LOG_ENTRIES entries
- * i.e the entries just before the next ont that uCode would fill.
- */
- if (num_wraps) {
- if (next_entry < size) {
- pos = iwl_print_event_log(priv,
- capacity - (size - next_entry),
- size - next_entry, mode,
- pos, buf, bufsz);
- pos = iwl_print_event_log(priv, 0,
- next_entry, mode,
- pos, buf, bufsz);
- } else
- pos = iwl_print_event_log(priv, next_entry - size,
- size, mode, pos, buf, bufsz);
- } else {
- if (next_entry < size) {
- pos = iwl_print_event_log(priv, 0, next_entry,
- mode, pos, buf, bufsz);
- } else {
- pos = iwl_print_event_log(priv, next_entry - size,
- size, mode, pos, buf, bufsz);
- }
- }
- return pos;
-}
-
-#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
-
-int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
- char **buf, bool display)
-{
- u32 base; /* SRAM byte address of event log header */
- u32 capacity; /* event log capacity in # entries */
- u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
- u32 num_wraps; /* # times uCode wrapped to top of log */
- u32 next_entry; /* index of next entry to be written by uCode */
- u32 size; /* # entries that we'll print */
- u32 logsize;
- int pos = 0;
- size_t bufsz = 0;
-
- base = priv->device_pointers.log_event_table;
- if (priv->ucode_type == UCODE_SUBTYPE_INIT) {
- logsize = priv->_agn.init_evtlog_size;
- if (!base)
- base = priv->_agn.init_evtlog_ptr;
- } else {
- logsize = priv->_agn.inst_evtlog_size;
- if (!base)
- base = priv->_agn.inst_evtlog_ptr;
- }
-
- if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
- IWL_ERR(priv,
- "Invalid event log pointer 0x%08X for %s uCode\n",
- base,
- (priv->ucode_type == UCODE_SUBTYPE_INIT)
- ? "Init" : "RT");
- return -EINVAL;
- }
-
- /* event log header */
- capacity = iwl_read_targ_mem(priv, base);
- mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32)));
- num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32)));
- next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32)));
-
- if (capacity > logsize) {
- IWL_ERR(priv, "Log capacity %d is bogus, limit to %d entries\n",
- capacity, logsize);
- capacity = logsize;
- }
-
- if (next_entry > logsize) {
- IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n",
- next_entry, logsize);
- next_entry = logsize;
- }
-
- size = num_wraps ? capacity : next_entry;
-
- /* bail out if nothing in log */
- if (size == 0) {
- IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
- return pos;
- }
-
- /* enable/disable bt channel inhibition */
- priv->bt_ch_announce = iwlagn_bt_ch_announce;
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) && !full_log)
- size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
- ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#else
- size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
- ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#endif
- IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
- size);
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (display) {
- if (full_log)
- bufsz = capacity * 48;
- else
- bufsz = size * 48;
- *buf = kmalloc(bufsz, GFP_KERNEL);
- if (!*buf)
- return -ENOMEM;
- }
- if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) {
- /*
- * if uCode has wrapped back to top of log,
- * start at the oldest entry,
- * i.e the next one that uCode would fill.
- */
- if (num_wraps)
- pos = iwl_print_event_log(priv, next_entry,
- capacity - next_entry, mode,
- pos, buf, bufsz);
- /* (then/else) start at top of log */
- pos = iwl_print_event_log(priv, 0,
- next_entry, mode, pos, buf, bufsz);
- } else
- pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
- next_entry, size, mode,
- pos, buf, bufsz);
-#else
- pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
- next_entry, size, mode,
- pos, buf, bufsz);
-#endif
- return pos;
-}
-
static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
{
struct iwl_ct_kill_config cmd;
@@ -1916,42 +1170,43 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(&priv->lock, flags);
- iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
+ spin_lock_irqsave(&priv->shrd->lock, flags);
+ iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_CLR,
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
priv->thermal_throttle.ct_kill_toggle = false;
if (priv->cfg->base_params->support_ct_kill_exit) {
adv_cmd.critical_temperature_enter =
- cpu_to_le32(priv->hw_params.ct_kill_threshold);
+ cpu_to_le32(hw_params(priv).ct_kill_threshold);
adv_cmd.critical_temperature_exit =
- cpu_to_le32(priv->hw_params.ct_kill_exit_threshold);
+ cpu_to_le32(hw_params(priv).ct_kill_exit_threshold);
- ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD,
- sizeof(adv_cmd), &adv_cmd);
+ ret = iwl_trans_send_cmd_pdu(trans(priv),
+ REPLY_CT_KILL_CONFIG_CMD,
+ CMD_SYNC, sizeof(adv_cmd), &adv_cmd);
if (ret)
IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
else
IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
- "succeeded, "
- "critical temperature enter is %d,"
- "exit is %d\n",
- priv->hw_params.ct_kill_threshold,
- priv->hw_params.ct_kill_exit_threshold);
+ "succeeded, critical temperature enter is %d,"
+ "exit is %d\n",
+ hw_params(priv).ct_kill_threshold,
+ hw_params(priv).ct_kill_exit_threshold);
} else {
cmd.critical_temperature_R =
- cpu_to_le32(priv->hw_params.ct_kill_threshold);
+ cpu_to_le32(hw_params(priv).ct_kill_threshold);
- ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD,
- sizeof(cmd), &cmd);
+ ret = iwl_trans_send_cmd_pdu(trans(priv),
+ REPLY_CT_KILL_CONFIG_CMD,
+ CMD_SYNC, sizeof(cmd), &cmd);
if (ret)
IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
else
IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
- "succeeded, "
- "critical temperature is %d\n",
- priv->hw_params.ct_kill_threshold);
+ "succeeded, "
+ "critical temperature is %d\n",
+ hw_params(priv).ct_kill_threshold);
}
}
@@ -1965,13 +1220,32 @@ static int iwlagn_send_calib_cfg_rt(struct iwl_priv *priv, u32 cfg)
};
memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd));
- calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL;
+ calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_RT_CFG_ALL;
calib_cfg_cmd.ucd_calib_cfg.once.start = cpu_to_le32(cfg);
- return iwl_send_cmd(priv, &cmd);
+ return iwl_trans_send_cmd(trans(priv), &cmd);
}
+static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant)
+{
+ struct iwl_tx_ant_config_cmd tx_ant_cmd = {
+ .valid = cpu_to_le32(valid_tx_ant),
+ };
+
+ if (IWL_UCODE_API(priv->ucode_ver) > 1) {
+ IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant);
+ return iwl_trans_send_cmd_pdu(trans(priv),
+ TX_ANT_CONFIGURATION_CMD,
+ CMD_SYNC,
+ sizeof(struct iwl_tx_ant_config_cmd),
+ &tx_ant_cmd);
+ } else {
+ IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n");
+ return -EOPNOTSUPP;
+ }
+}
+
/**
* iwl_alive_start - called after REPLY_ALIVE notification received
* from protocol/runtime uCode (initialization uCode's
@@ -1982,28 +1256,36 @@ int iwl_alive_start(struct iwl_priv *priv)
int ret = 0;
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- iwl_reset_ict(priv);
+ /*TODO: this should go to the transport layer */
+ iwl_reset_ict(trans(priv));
IWL_DEBUG_INFO(priv, "Runtime Alive received.\n");
/* After the ALIVE response, we can send host commands to the uCode */
- set_bit(STATUS_ALIVE, &priv->status);
+ set_bit(STATUS_ALIVE, &priv->shrd->status);
/* Enable watchdog to monitor the driver tx queues */
iwl_setup_watchdog(priv);
- if (iwl_is_rfkill(priv))
+ if (iwl_is_rfkill(priv->shrd))
return -ERFKILL;
/* download priority table before any calibration request */
if (priv->cfg->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist) {
/* Configure Bluetooth device coexistence support */
+ if (priv->cfg->bt_params->bt_sco_disable)
+ priv->bt_enable_pspoll = false;
+ else
+ priv->bt_enable_pspoll = true;
+
priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
- priv->cfg->ops->hcmd->send_bt_config(priv);
+ iwlagn_send_advance_bt_config(priv);
priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS;
+ priv->cur_rssi_ctx = NULL;
+
iwlagn_send_prio_tbl(priv);
/* FIXME: w/a to force change uCode BT state machine */
@@ -2015,19 +1297,25 @@ int iwl_alive_start(struct iwl_priv *priv)
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
if (ret)
return ret;
+ } else {
+ /*
+ * default is 2-wire BT coexexistence support
+ */
+ iwl_send_bt_config(priv);
}
- if (priv->hw_params.calib_rt_cfg)
- iwlagn_send_calib_cfg_rt(priv, priv->hw_params.calib_rt_cfg);
+
+ if (hw_params(priv).calib_rt_cfg)
+ iwlagn_send_calib_cfg_rt(priv,
+ hw_params(priv).calib_rt_cfg);
ieee80211_wake_queues(priv->hw);
priv->active_rate = IWL_RATES_MASK;
/* Configure Tx antenna selection based on H/W config */
- if (priv->cfg->ops->hcmd->set_tx_ant)
- priv->cfg->ops->hcmd->set_tx_ant(priv, priv->cfg->valid_tx_ant);
+ iwlagn_send_tx_ant_config(priv, priv->cfg->valid_tx_ant);
- if (iwl_is_associated_ctx(ctx)) {
+ if (iwl_is_associated_ctx(ctx) && !priv->shrd->wowlan) {
struct iwl_rxon_cmd *active_rxon =
(struct iwl_rxon_cmd *)&ctx->active;
/* apply any changes in staging */
@@ -2039,24 +1327,18 @@ int iwl_alive_start(struct iwl_priv *priv)
for_each_context(priv, tmp)
iwl_connection_init_rx_config(priv, tmp);
- if (priv->cfg->ops->hcmd->set_rxon_chain)
- priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
+ iwlagn_set_rxon_chain(priv, ctx);
}
- if (!priv->cfg->bt_params || (priv->cfg->bt_params &&
- !priv->cfg->bt_params->advanced_bt_coexist)) {
- /*
- * default is 2-wire BT coexexistence support
- */
- priv->cfg->ops->hcmd->send_bt_config(priv);
+ if (!priv->shrd->wowlan) {
+ /* WoWLAN ucode will not reply in the same way, skip it */
+ iwl_reset_run_time_calib(priv);
}
- iwl_reset_run_time_calib(priv);
-
- set_bit(STATUS_READY, &priv->status);
+ set_bit(STATUS_READY, &priv->shrd->status);
/* Configure the adapter for unassociated operation */
- ret = iwlcore_commit_rxon(priv, ctx);
+ ret = iwlagn_commit_rxon(priv, ctx);
if (ret)
return ret;
@@ -2078,7 +1360,15 @@ static void __iwl_down(struct iwl_priv *priv)
iwl_scan_cancel_timeout(priv, 200);
- exit_pending = test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
+ /*
+ * If active, scanning won't cancel it, so say it expired.
+ * No race since we hold the mutex here and a new one
+ * can't come in at this time.
+ */
+ ieee80211_remain_on_channel_expired(priv->hw);
+
+ exit_pending =
+ test_and_set_bit(STATUS_EXIT_PENDING, &priv->shrd->status);
/* Stop TX queues watchdog. We need to have STATUS_EXIT_PENDING bit set
* to prevent rearm timer */
@@ -2090,6 +1380,8 @@ static void __iwl_down(struct iwl_priv *priv)
/* reset BT coex data */
priv->bt_status = 0;
+ priv->cur_rssi_ctx = NULL;
+ priv->bt_is_sco = 0;
if (priv->cfg->bt_params)
priv->bt_traffic_load =
priv->cfg->bt_params->bt_init_traffic_load;
@@ -2101,85 +1393,37 @@ static void __iwl_down(struct iwl_priv *priv)
/* Wipe out the EXIT_PENDING status bit if we are not actually
* exiting the module */
if (!exit_pending)
- clear_bit(STATUS_EXIT_PENDING, &priv->status);
+ clear_bit(STATUS_EXIT_PENDING, &priv->shrd->status);
if (priv->mac80211_registered)
ieee80211_stop_queues(priv->hw);
+ iwl_trans_stop_device(trans(priv));
+
/* Clear out all status bits but a few that are stable across reset */
- priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) <<
+ priv->shrd->status &=
+ test_bit(STATUS_RF_KILL_HW, &priv->shrd->status) <<
STATUS_RF_KILL_HW |
- test_bit(STATUS_GEO_CONFIGURED, &priv->status) <<
+ test_bit(STATUS_GEO_CONFIGURED, &priv->shrd->status) <<
STATUS_GEO_CONFIGURED |
- test_bit(STATUS_FW_ERROR, &priv->status) <<
+ test_bit(STATUS_FW_ERROR, &priv->shrd->status) <<
STATUS_FW_ERROR |
- test_bit(STATUS_EXIT_PENDING, &priv->status) <<
+ test_bit(STATUS_EXIT_PENDING, &priv->shrd->status) <<
STATUS_EXIT_PENDING;
- iwlagn_stop_device(priv);
-
dev_kfree_skb(priv->beacon_skb);
priv->beacon_skb = NULL;
}
static void iwl_down(struct iwl_priv *priv)
{
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
__iwl_down(priv);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
iwl_cancel_deferred_work(priv);
}
-#define HW_READY_TIMEOUT (50)
-
-/* Note: returns poll_bit return value, which is >= 0 if success */
-static int iwl_set_hw_ready(struct iwl_priv *priv)
-{
- int ret;
-
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_READY);
-
- /* See if we got it */
- ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
- HW_READY_TIMEOUT);
-
- IWL_DEBUG_INFO(priv, "hardware%s ready\n", ret < 0 ? " not" : "");
- return ret;
-}
-
-/* Note: returns standard 0/-ERROR code */
-int iwl_prepare_card_hw(struct iwl_priv *priv)
-{
- int ret;
-
- IWL_DEBUG_INFO(priv, "iwl_prepare_card_hw enter\n");
-
- ret = iwl_set_hw_ready(priv);
- if (ret >= 0)
- return 0;
-
- /* If HW is not ready, prepare the conditions to check again */
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_PREPARE);
-
- ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
- ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000);
-
- if (ret < 0)
- return ret;
-
- /* HW should be ready by now, check again. */
- ret = iwl_set_hw_ready(priv);
- if (ret >= 0)
- return 0;
- return ret;
-}
-
#define MAX_HW_RESTARTS 5
static int __iwl_up(struct iwl_priv *priv)
@@ -2187,9 +1431,9 @@ static int __iwl_up(struct iwl_priv *priv)
struct iwl_rxon_context *ctx;
int ret;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
- if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) {
IWL_WARN(priv, "Exit pending; will not bring the NIC up\n");
return -EIO;
}
@@ -2210,8 +1454,7 @@ static int __iwl_up(struct iwl_priv *priv)
ret = iwlagn_load_ucode_wait_alive(priv,
&priv->ucode_rt,
- UCODE_SUBTYPE_REGULAR,
- UCODE_SUBTYPE_REGULAR_NEW);
+ IWL_UCODE_REGULAR);
if (ret) {
IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret);
goto error;
@@ -2223,9 +1466,9 @@ static int __iwl_up(struct iwl_priv *priv)
return 0;
error:
- set_bit(STATUS_EXIT_PENDING, &priv->status);
+ set_bit(STATUS_EXIT_PENDING, &priv->shrd->status);
__iwl_down(priv);
- clear_bit(STATUS_EXIT_PENDING, &priv->status);
+ clear_bit(STATUS_EXIT_PENDING, &priv->shrd->status);
IWL_ERR(priv, "Unable to initialize device.\n");
return ret;
@@ -2243,11 +1486,11 @@ static void iwl_bg_run_time_calib_work(struct work_struct *work)
struct iwl_priv *priv = container_of(work, struct iwl_priv,
run_time_calib_work);
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
- if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
- test_bit(STATUS_SCANNING, &priv->status)) {
- mutex_unlock(&priv->mutex);
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status) ||
+ test_bit(STATUS_SCANNING, &priv->shrd->status)) {
+ mutex_unlock(&priv->shrd->mutex);
return;
}
@@ -2256,7 +1499,7 @@ static void iwl_bg_run_time_calib_work(struct work_struct *work)
iwl_sensitivity_calibration(priv);
}
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
static void iwlagn_prepare_restart(struct iwl_priv *priv)
@@ -2266,8 +1509,9 @@ static void iwlagn_prepare_restart(struct iwl_priv *priv)
u8 bt_ci_compliance;
u8 bt_load;
u8 bt_status;
+ bool bt_is_sco;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
for_each_context(priv, ctx)
ctx->vif = NULL;
@@ -2286,6 +1530,7 @@ static void iwlagn_prepare_restart(struct iwl_priv *priv)
bt_ci_compliance = priv->bt_ci_compliance;
bt_load = priv->bt_traffic_load;
bt_status = priv->bt_status;
+ bt_is_sco = priv->bt_is_sco;
__iwl_down(priv);
@@ -2293,19 +1538,20 @@ static void iwlagn_prepare_restart(struct iwl_priv *priv)
priv->bt_ci_compliance = bt_ci_compliance;
priv->bt_traffic_load = bt_load;
priv->bt_status = bt_status;
+ priv->bt_is_sco = bt_is_sco;
}
static void iwl_bg_restart(struct work_struct *data)
{
struct iwl_priv *priv = container_of(data, struct iwl_priv, restart);
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
- if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) {
- mutex_lock(&priv->mutex);
+ if (test_and_clear_bit(STATUS_FW_ERROR, &priv->shrd->status)) {
+ mutex_lock(&priv->shrd->mutex);
iwlagn_prepare_restart(priv);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
iwl_cancel_deferred_work(priv);
ieee80211_restart_hw(priv->hw);
} else {
@@ -2313,118 +1559,88 @@ static void iwl_bg_restart(struct work_struct *data)
}
}
-static void iwl_bg_rx_replenish(struct work_struct *data)
-{
- struct iwl_priv *priv =
- container_of(data, struct iwl_priv, rx_replenish);
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- mutex_lock(&priv->mutex);
- iwlagn_rx_replenish(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static int iwl_mac_offchannel_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
- unsigned int wait)
-{
- struct iwl_priv *priv = hw->priv;
- int ret;
-
- /* Not supported if we don't have PAN */
- if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) {
- ret = -EOPNOTSUPP;
- goto free;
- }
-
- /* Not supported on pre-P2P firmware */
- if (!(priv->contexts[IWL_RXON_CTX_PAN].interface_modes &
- BIT(NL80211_IFTYPE_P2P_CLIENT))) {
- ret = -EOPNOTSUPP;
- goto free;
- }
-
- mutex_lock(&priv->mutex);
-
- if (!priv->contexts[IWL_RXON_CTX_PAN].is_active) {
- /*
- * If the PAN context is free, use the normal
- * way of doing remain-on-channel offload + TX.
- */
- ret = 1;
- goto out;
- }
-
- /* TODO: queue up if scanning? */
- if (test_bit(STATUS_SCANNING, &priv->status) ||
- priv->_agn.offchan_tx_skb) {
- ret = -EBUSY;
- goto out;
- }
-
- /*
- * max_scan_ie_len doesn't include the blank SSID or the header,
- * so need to add that again here.
- */
- if (skb->len > hw->wiphy->max_scan_ie_len + 24 + 2) {
- ret = -ENOBUFS;
- goto out;
- }
-
- priv->_agn.offchan_tx_skb = skb;
- priv->_agn.offchan_tx_timeout = wait;
- priv->_agn.offchan_tx_chan = chan;
-
- ret = iwl_scan_initiate(priv, priv->contexts[IWL_RXON_CTX_PAN].vif,
- IWL_SCAN_OFFCH_TX, chan->band);
- if (ret)
- priv->_agn.offchan_tx_skb = NULL;
- out:
- mutex_unlock(&priv->mutex);
- free:
- if (ret < 0)
- kfree_skb(skb);
-
- return ret;
-}
-
-static int iwl_mac_offchannel_tx_cancel_wait(struct ieee80211_hw *hw)
-{
- struct iwl_priv *priv = hw->priv;
- int ret;
+/*****************************************************************************
+ *
+ * mac80211 entry point functions
+ *
+ *****************************************************************************/
- mutex_lock(&priv->mutex);
+static const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = {
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP),
+ },
+};
- if (!priv->_agn.offchan_tx_skb) {
- ret = -EINVAL;
- goto unlock;
- }
+static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = {
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+};
- priv->_agn.offchan_tx_skb = NULL;
+static const struct ieee80211_iface_limit iwlagn_p2p_sta_go_limits[] = {
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_AP),
+ },
+};
- ret = iwl_scan_cancel_timeout(priv, 200);
- if (ret)
- ret = -EIO;
-unlock:
- mutex_unlock(&priv->mutex);
+static const struct ieee80211_iface_limit iwlagn_p2p_2sta_limits[] = {
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_CLIENT),
+ },
+};
- return ret;
-}
+static const struct ieee80211_iface_combination
+iwlagn_iface_combinations_dualmode[] = {
+ { .num_different_channels = 1,
+ .max_interfaces = 2,
+ .beacon_int_infra_match = true,
+ .limits = iwlagn_sta_ap_limits,
+ .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits),
+ },
+ { .num_different_channels = 1,
+ .max_interfaces = 2,
+ .limits = iwlagn_2sta_limits,
+ .n_limits = ARRAY_SIZE(iwlagn_2sta_limits),
+ },
+};
-/*****************************************************************************
- *
- * mac80211 entry point functions
- *
- *****************************************************************************/
+static const struct ieee80211_iface_combination
+iwlagn_iface_combinations_p2p[] = {
+ { .num_different_channels = 1,
+ .max_interfaces = 2,
+ .beacon_int_infra_match = true,
+ .limits = iwlagn_p2p_sta_go_limits,
+ .n_limits = ARRAY_SIZE(iwlagn_p2p_sta_go_limits),
+ },
+ { .num_different_channels = 1,
+ .max_interfaces = 2,
+ .limits = iwlagn_p2p_2sta_limits,
+ .n_limits = ARRAY_SIZE(iwlagn_p2p_2sta_limits),
+ },
+};
/*
* Not a mac80211 entry point function, but it fits in with all the
* other mac80211 functions grouped here.
*/
-static int iwl_mac_setup_register(struct iwl_priv *priv,
+static int iwlagn_mac_setup_register(struct iwl_priv *priv,
struct iwlagn_ucode_capabilities *capa)
{
int ret;
@@ -2440,12 +1656,17 @@ static int iwl_mac_setup_register(struct iwl_priv *priv,
IEEE80211_HW_SPECTRUM_MGMT |
IEEE80211_HW_REPORTS_TX_ACK_STATUS;
+ /*
+ * Including the following line will crash some AP's. This
+ * workaround removes the stimulus which causes the crash until
+ * the AP software can be fixed.
hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+ */
hw->flags |= IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
- if (priv->cfg->sku & IWL_SKU_N)
+ if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE)
hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
IEEE80211_HW_SUPPORTS_STATIC_SMPS;
@@ -2460,17 +1681,45 @@ static int iwl_mac_setup_register(struct iwl_priv *priv,
hw->wiphy->interface_modes |= ctx->exclusive_interface_modes;
}
+ BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
+
+ if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) {
+ hw->wiphy->iface_combinations = iwlagn_iface_combinations_p2p;
+ hw->wiphy->n_iface_combinations =
+ ARRAY_SIZE(iwlagn_iface_combinations_p2p);
+ } else if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
+ hw->wiphy->iface_combinations = iwlagn_iface_combinations_dualmode;
+ hw->wiphy->n_iface_combinations =
+ ARRAY_SIZE(iwlagn_iface_combinations_dualmode);
+ }
+
hw->wiphy->max_remain_on_channel_duration = 1000;
hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
WIPHY_FLAG_DISABLE_BEACON_HINTS |
WIPHY_FLAG_IBSS_RSN;
- /*
- * For now, disable PS by default because it affects
- * RX performance significantly.
- */
- hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+ if (priv->ucode_wowlan.code.len && device_can_wakeup(bus(priv)->dev)) {
+ hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
+ WIPHY_WOWLAN_DISCONNECT |
+ WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+ WIPHY_WOWLAN_RFKILL_RELEASE;
+ if (!iwlagn_mod_params.sw_crypto)
+ hw->wiphy->wowlan.flags |=
+ WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE;
+
+ hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS;
+ hw->wiphy->wowlan.pattern_min_len =
+ IWLAGN_WOWLAN_MIN_PATTERN_LEN;
+ hw->wiphy->wowlan.pattern_max_len =
+ IWLAGN_WOWLAN_MAX_PATTERN_LEN;
+ }
+
+ if (iwlagn_mod_params.power_save)
+ hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+ else
+ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
/* we create the 802.11 header and a zero-length SSID element */
@@ -2509,16 +1758,16 @@ static int iwlagn_mac_start(struct ieee80211_hw *hw)
IWL_DEBUG_MAC80211(priv, "enter\n");
/* we should be verifying the device is ready to be opened */
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
ret = __iwl_up(priv);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
if (ret)
return ret;
IWL_DEBUG_INFO(priv, "Start UP work done.\n");
/* Now we should be done, and the READY bit should be set. */
- if (WARN_ON(!test_bit(STATUS_READY, &priv->status)))
+ if (WARN_ON(!test_bit(STATUS_READY, &priv->shrd->status)))
ret = -EIO;
iwlagn_led_enable(priv);
@@ -2541,16 +1790,488 @@ static void iwlagn_mac_stop(struct ieee80211_hw *hw)
iwl_down(priv);
- flush_workqueue(priv->workqueue);
+ flush_workqueue(priv->shrd->workqueue);
/* User space software may expect getting rfkill changes
* even if interface is down */
- iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
+ iwl_write32(bus(priv), CSR_INT, 0xFFFFFFFF);
iwl_enable_rfkill_int(priv);
IWL_DEBUG_MAC80211(priv, "leave\n");
}
+#ifdef CONFIG_PM_SLEEP
+static int iwlagn_send_patterns(struct iwl_priv *priv,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct iwlagn_wowlan_patterns_cmd *pattern_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_WOWLAN_PATTERNS,
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ .flags = CMD_SYNC,
+ };
+ int i, err;
+
+ if (!wowlan->n_patterns)
+ return 0;
+
+ cmd.len[0] = sizeof(*pattern_cmd) +
+ wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern);
+
+ pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL);
+ if (!pattern_cmd)
+ return -ENOMEM;
+
+ pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns);
+
+ for (i = 0; i < wowlan->n_patterns; i++) {
+ int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
+
+ memcpy(&pattern_cmd->patterns[i].mask,
+ wowlan->patterns[i].mask, mask_len);
+ memcpy(&pattern_cmd->patterns[i].pattern,
+ wowlan->patterns[i].pattern,
+ wowlan->patterns[i].pattern_len);
+ pattern_cmd->patterns[i].mask_size = mask_len;
+ pattern_cmd->patterns[i].pattern_size =
+ wowlan->patterns[i].pattern_len;
+ }
+
+ cmd.data[0] = pattern_cmd;
+ err = iwl_trans_send_cmd(trans(priv), &cmd);
+ kfree(pattern_cmd);
+ return err;
+}
+#endif
+
+static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_gtk_rekey_data *data)
+{
+ struct iwl_priv *priv = hw->priv;
+
+ if (iwlagn_mod_params.sw_crypto)
+ return;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->shrd->mutex);
+
+ if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif)
+ goto out;
+
+ memcpy(priv->kek, data->kek, NL80211_KEK_LEN);
+ memcpy(priv->kck, data->kck, NL80211_KCK_LEN);
+ priv->replay_ctr = cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr));
+ priv->have_rekey_data = true;
+
+ out:
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+}
+
+struct wowlan_key_data {
+ struct iwl_rxon_context *ctx;
+ struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc;
+ struct iwlagn_wowlan_tkip_params_cmd *tkip;
+ const u8 *bssid;
+ bool error, use_rsc_tsc, use_tkip;
+};
+
+#ifdef CONFIG_PM_SLEEP
+static void iwlagn_convert_p1k(u16 *p1k, __le16 *out)
+{
+ int i;
+
+ for (i = 0; i < IWLAGN_P1K_SIZE; i++)
+ out[i] = cpu_to_le16(p1k[i]);
+}
+
+static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_priv *priv = hw->priv;
+ struct wowlan_key_data *data = _data;
+ struct iwl_rxon_context *ctx = data->ctx;
+ struct aes_sc *aes_sc, *aes_tx_sc = NULL;
+ struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL;
+ struct iwlagn_p1k_cache *rx_p1ks;
+ u8 *rx_mic_key;
+ struct ieee80211_key_seq seq;
+ u32 cur_rx_iv32 = 0;
+ u16 p1k[IWLAGN_P1K_SIZE];
+ int ret, i;
+
+ mutex_lock(&priv->shrd->mutex);
+
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
+ !sta && !ctx->key_mapping_keys)
+ ret = iwl_set_default_wep_key(priv, ctx, key);
+ else
+ ret = iwl_set_dynamic_key(priv, ctx, key, sta);
+
+ if (ret) {
+ IWL_ERR(priv, "Error setting key during suspend!\n");
+ data->error = true;
+ }
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (sta) {
+ tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
+ tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc;
+
+ rx_p1ks = data->tkip->rx_uni;
+
+ ieee80211_get_key_tx_seq(key, &seq);
+ tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16);
+ tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32);
+
+ ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k);
+ iwlagn_convert_p1k(p1k, data->tkip->tx.p1k);
+
+ memcpy(data->tkip->mic_keys.tx,
+ &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY],
+ IWLAGN_MIC_KEY_SIZE);
+
+ rx_mic_key = data->tkip->mic_keys.rx_unicast;
+ } else {
+ tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc;
+ rx_p1ks = data->tkip->rx_multi;
+ rx_mic_key = data->tkip->mic_keys.rx_mcast;
+ }
+
+ /*
+ * For non-QoS this relies on the fact that both the uCode and
+ * mac80211 use TID 0 (as they need to to avoid replay attacks)
+ * for checking the IV in the frames.
+ */
+ for (i = 0; i < IWLAGN_NUM_RSC; i++) {
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16);
+ tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32);
+ /* wrapping isn't allowed, AP must rekey */
+ if (seq.tkip.iv32 > cur_rx_iv32)
+ cur_rx_iv32 = seq.tkip.iv32;
+ }
+
+ ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k);
+ iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k);
+ ieee80211_get_tkip_rx_p1k(key, data->bssid,
+ cur_rx_iv32 + 1, p1k);
+ iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k);
+
+ memcpy(rx_mic_key,
+ &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY],
+ IWLAGN_MIC_KEY_SIZE);
+
+ data->use_tkip = true;
+ data->use_rsc_tsc = true;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (sta) {
+ u8 *pn = seq.ccmp.pn;
+
+ aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc;
+ aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc;
+
+ ieee80211_get_key_tx_seq(key, &seq);
+ aes_tx_sc->pn = cpu_to_le64(
+ (u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ } else
+ aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc;
+
+ /*
+ * For non-QoS this relies on the fact that both the uCode and
+ * mac80211 use TID 0 for checking the IV in the frames.
+ */
+ for (i = 0; i < IWLAGN_NUM_RSC; i++) {
+ u8 *pn = seq.ccmp.pn;
+
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ aes_sc->pn = cpu_to_le64(
+ (u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ }
+ data->use_rsc_tsc = true;
+ break;
+ }
+
+ mutex_unlock(&priv->shrd->mutex);
+}
+
+static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct iwl_priv *priv = hw->priv;
+ struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd;
+ struct iwl_rxon_cmd rxon;
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd;
+ struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {};
+ struct wowlan_key_data key_data = {
+ .ctx = ctx,
+ .bssid = ctx->active.bssid_addr,
+ .use_rsc_tsc = false,
+ .tkip = &tkip_cmd,
+ .use_tkip = false,
+ };
+ int ret, i;
+ u16 seq;
+
+ if (WARN_ON(!wowlan))
+ return -EINVAL;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->shrd->mutex);
+
+ /* Don't attempt WoWLAN when not associated, tear down instead. */
+ if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION ||
+ !iwl_is_associated_ctx(ctx)) {
+ ret = 1;
+ goto out;
+ }
+
+ key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
+ if (!key_data.rsc_tsc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd));
+
+ /*
+ * We know the last used seqno, and the uCode expects to know that
+ * one, it will increment before TX.
+ */
+ seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ;
+ wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq);
+
+ /*
+ * For QoS counters, we store the one to use next, so subtract 0x10
+ * since the uCode will add 0x10 before using the value.
+ */
+ for (i = 0; i < 8; i++) {
+ seq = priv->shrd->tid_data[IWL_AP_ID][i].seq_number;
+ seq -= 0x10;
+ wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq);
+ }
+
+ if (wowlan->disconnect)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS |
+ IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE);
+ if (wowlan->magic_pkt)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET);
+ if (wowlan->gtk_rekey_failure)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
+ if (wowlan->eap_identity_req)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ);
+ if (wowlan->four_way_handshake)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
+ if (wowlan->rfkill_release)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_RFKILL);
+ if (wowlan->n_patterns)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH);
+
+ iwl_scan_cancel_timeout(priv, 200);
+
+ memcpy(&rxon, &ctx->active, sizeof(rxon));
+
+ iwl_trans_stop_device(trans(priv));
+
+ priv->shrd->wowlan = true;
+
+ ret = iwlagn_load_ucode_wait_alive(priv, &priv->ucode_wowlan,
+ IWL_UCODE_WOWLAN);
+ if (ret)
+ goto error;
+
+ /* now configure WoWLAN ucode */
+ ret = iwl_alive_start(priv);
+ if (ret)
+ goto error;
+
+ memcpy(&ctx->staging, &rxon, sizeof(rxon));
+ ret = iwlagn_commit_rxon(priv, ctx);
+ if (ret)
+ goto error;
+
+ ret = iwl_power_update_mode(priv, true);
+ if (ret)
+ goto error;
+
+ if (!iwlagn_mod_params.sw_crypto) {
+ /* mark all keys clear */
+ priv->ucode_key_table = 0;
+ ctx->key_mapping_keys = 0;
+
+ /*
+ * This needs to be unlocked due to lock ordering
+ * constraints. Since we're in the suspend path
+ * that isn't really a problem though.
+ */
+ mutex_unlock(&priv->shrd->mutex);
+ ieee80211_iter_keys(priv->hw, ctx->vif,
+ iwlagn_wowlan_program_keys,
+ &key_data);
+ mutex_lock(&priv->shrd->mutex);
+ if (key_data.error) {
+ ret = -EIO;
+ goto error;
+ }
+
+ if (key_data.use_rsc_tsc) {
+ struct iwl_host_cmd rsc_tsc_cmd = {
+ .id = REPLY_WOWLAN_TSC_RSC_PARAMS,
+ .flags = CMD_SYNC,
+ .data[0] = key_data.rsc_tsc,
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ .len[0] = sizeof(*key_data.rsc_tsc),
+ };
+
+ ret = iwl_trans_send_cmd(trans(priv), &rsc_tsc_cmd);
+ if (ret)
+ goto error;
+ }
+
+ if (key_data.use_tkip) {
+ ret = iwl_trans_send_cmd_pdu(trans(priv),
+ REPLY_WOWLAN_TKIP_PARAMS,
+ CMD_SYNC, sizeof(tkip_cmd),
+ &tkip_cmd);
+ if (ret)
+ goto error;
+ }
+
+ if (priv->have_rekey_data) {
+ memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
+ memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN);
+ kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
+ memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN);
+ kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
+ kek_kck_cmd.replay_ctr = priv->replay_ctr;
+
+ ret = iwl_trans_send_cmd_pdu(trans(priv),
+ REPLY_WOWLAN_KEK_KCK_MATERIAL,
+ CMD_SYNC, sizeof(kek_kck_cmd),
+ &kek_kck_cmd);
+ if (ret)
+ goto error;
+ }
+ }
+
+ ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_WOWLAN_WAKEUP_FILTER,
+ CMD_SYNC, sizeof(wakeup_filter_cmd),
+ &wakeup_filter_cmd);
+ if (ret)
+ goto error;
+
+ ret = iwlagn_send_patterns(priv, wowlan);
+ if (ret)
+ goto error;
+
+ device_set_wakeup_enable(bus(priv)->dev, true);
+
+ /* Now let the ucode operate on its own */
+ iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_SET,
+ CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
+
+ goto out;
+
+ error:
+ priv->shrd->wowlan = false;
+ iwlagn_prepare_restart(priv);
+ ieee80211_restart_hw(priv->hw);
+ out:
+ mutex_unlock(&priv->shrd->mutex);
+ kfree(key_data.rsc_tsc);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ return ret;
+}
+
+static int iwlagn_mac_resume(struct ieee80211_hw *hw)
+{
+ struct iwl_priv *priv = hw->priv;
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ struct ieee80211_vif *vif;
+ unsigned long flags;
+ u32 base, status = 0xffffffff;
+ int ret = -EIO;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->shrd->mutex);
+
+ iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
+
+ base = priv->device_pointers.error_event_table;
+ if (iwlagn_hw_valid_rtc_data_addr(base)) {
+ spin_lock_irqsave(&bus(priv)->reg_lock, flags);
+ ret = iwl_grab_nic_access_silent(bus(priv));
+ if (ret == 0) {
+ iwl_write32(bus(priv), HBUS_TARG_MEM_RADDR, base);
+ status = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
+ iwl_release_nic_access(bus(priv));
+ }
+ spin_unlock_irqrestore(&bus(priv)->reg_lock, flags);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (ret == 0) {
+ if (!priv->wowlan_sram)
+ priv->wowlan_sram =
+ kzalloc(priv->ucode_wowlan.data.len,
+ GFP_KERNEL);
+
+ if (priv->wowlan_sram)
+ _iwl_read_targ_mem_words(
+ bus(priv), 0x800000, priv->wowlan_sram,
+ priv->ucode_wowlan.data.len / 4);
+ }
+#endif
+ }
+
+ /* we'll clear ctx->vif during iwlagn_prepare_restart() */
+ vif = ctx->vif;
+
+ priv->shrd->wowlan = false;
+
+ device_set_wakeup_enable(bus(priv)->dev, false);
+
+ iwlagn_prepare_restart(priv);
+
+ memset((void *)&ctx->active, 0, sizeof(ctx->active));
+ iwl_connection_init_rx_config(priv, ctx);
+ iwlagn_set_rxon_chain(priv, ctx);
+
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ ieee80211_resume_disconnect(vif);
+
+ return 1;
+}
+#endif
+
static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct iwl_priv *priv = hw->priv;
@@ -2573,14 +2294,8 @@ static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw,
u32 iv32, u16 *phase1key)
{
struct iwl_priv *priv = hw->priv;
- struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
- iwl_update_tkip_key(priv, vif_priv->ctx, keyconf, sta,
- iv32, phase1key);
-
- IWL_DEBUG_MAC80211(priv, "leave\n");
+ iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key);
}
static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -2592,7 +2307,6 @@ static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
struct iwl_rxon_context *ctx = vif_priv->ctx;
int ret;
- u8 sta_id;
bool is_default_wep_key = false;
IWL_DEBUG_MAC80211(priv, "enter\n");
@@ -2603,20 +2317,27 @@ static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
}
/*
- * To support IBSS RSN, don't program group keys in IBSS, the
- * hardware will then not attempt to decrypt the frames.
+ * We could program these keys into the hardware as well, but we
+ * don't expect much multicast traffic in IBSS and having keys
+ * for more stations is probably more useful.
+ *
+ * Mark key TX-only and return 0.
*/
if (vif->type == NL80211_IFTYPE_ADHOC &&
- !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
- return -EOPNOTSUPP;
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+ key->hw_key_idx = WEP_INVALID_OFFSET;
+ return 0;
+ }
- sta_id = iwl_sta_id_or_broadcast(priv, vif_priv->ctx, sta);
- if (sta_id == IWL_INVALID_STATION)
- return -EINVAL;
+ /* If they key was TX-only, accept deletion */
+ if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET)
+ return 0;
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
iwl_scan_cancel_timeout(priv, 100);
+ BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT);
+
/*
* If we are getting WEP group key and we didn't receive any key mapping
* so far, we are in legacy wep mode (group key only), otherwise we are
@@ -2624,22 +2345,30 @@ static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
* In legacy wep mode, we use another host command to the uCode.
*/
if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
- key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
- !sta) {
+ key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) {
if (cmd == SET_KEY)
is_default_wep_key = !ctx->key_mapping_keys;
else
is_default_wep_key =
- (key->hw_key_idx == HW_KEY_DEFAULT);
+ key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT;
}
+
switch (cmd) {
case SET_KEY:
- if (is_default_wep_key)
+ if (is_default_wep_key) {
ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key);
- else
- ret = iwl_set_dynamic_key(priv, vif_priv->ctx,
- key, sta_id);
+ break;
+ }
+ ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta);
+ if (ret) {
+ /*
+ * can't add key for RX, but we don't need it
+ * in the device for TX so still return 0
+ */
+ ret = 0;
+ key->hw_key_idx = WEP_INVALID_OFFSET;
+ }
IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n");
break;
@@ -2647,7 +2376,7 @@ static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
if (is_default_wep_key)
ret = iwl_remove_default_wep_key(priv, ctx, key);
else
- ret = iwl_remove_dynamic_key(priv, ctx, key, sta_id);
+ ret = iwl_remove_dynamic_key(priv, ctx, key, sta);
IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n");
break;
@@ -2655,7 +2384,7 @@ static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ret = -EINVAL;
}
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
IWL_DEBUG_MAC80211(priv, "leave\n");
return ret;
@@ -2670,14 +2399,16 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
struct iwl_priv *priv = hw->priv;
int ret = -EINVAL;
struct iwl_station_priv *sta_priv = (void *) sta->drv_priv;
+ struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n",
sta->addr, tid);
- if (!(priv->cfg->sku & IWL_SKU_N))
+ if (!(priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE))
return -EACCES;
- mutex_lock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->shrd->mutex);
switch (action) {
case IEEE80211_AMPDU_RX_START:
@@ -2687,36 +2418,28 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
case IEEE80211_AMPDU_RX_STOP:
IWL_DEBUG_HT(priv, "stop Rx\n");
ret = iwl_sta_rx_agg_stop(priv, sta, tid);
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
ret = 0;
break;
case IEEE80211_AMPDU_TX_START:
IWL_DEBUG_HT(priv, "start Tx\n");
ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn);
- if (ret == 0) {
- priv->_agn.agg_tids_count++;
- IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
- priv->_agn.agg_tids_count);
- }
break;
case IEEE80211_AMPDU_TX_STOP:
IWL_DEBUG_HT(priv, "stop Tx\n");
ret = iwlagn_tx_agg_stop(priv, vif, sta, tid);
- if ((ret == 0) && (priv->_agn.agg_tids_count > 0)) {
- priv->_agn.agg_tids_count--;
- IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
- priv->_agn.agg_tids_count);
+ if ((ret == 0) && (priv->agg_tids_count > 0)) {
+ priv->agg_tids_count--;
+ IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n",
+ priv->agg_tids_count);
}
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
ret = 0;
- if (priv->cfg->ht_params &&
+ if (!priv->agg_tids_count && priv->cfg->ht_params &&
priv->cfg->ht_params->use_rts_for_aggregation) {
- struct iwl_station_priv *sta_priv =
- (void *) sta->drv_priv;
/*
* switch off RTS/CTS if it was previously enabled
*/
-
sta_priv->lq_sta.lq.general_params.flags &=
~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK;
iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif),
@@ -2726,7 +2449,8 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
case IEEE80211_AMPDU_TX_OPERATIONAL:
buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF);
- iwlagn_txq_agg_queue_setup(priv, sta, tid, buf_size);
+ iwl_trans_tx_agg_setup(trans(priv), ctx->ctxid, iwl_sta_id(sta),
+ tid, buf_size);
/*
* If the limit is 0, then it wasn't initialised yet,
@@ -2758,17 +2482,23 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
sta_priv->lq_sta.lq.general_params.flags |=
LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK;
}
+ priv->agg_tids_count++;
+ IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n",
+ priv->agg_tids_count);
sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit =
sta_priv->max_agg_bufsize;
iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif),
&sta_priv->lq_sta.lq, CMD_ASYNC, false);
+
+ IWL_INFO(priv, "Tx aggregation enabled on ra = %pM tid = %d\n",
+ sta->addr, tid);
ret = 0;
break;
}
- mutex_unlock(&priv->mutex);
-
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
return ret;
}
@@ -2780,15 +2510,15 @@ static int iwlagn_mac_sta_add(struct ieee80211_hw *hw,
struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
bool is_ap = vif->type == NL80211_IFTYPE_STATION;
- int ret;
+ int ret = 0;
u8 sta_id;
- IWL_DEBUG_INFO(priv, "received request to add station %pM\n",
+ IWL_DEBUG_MAC80211(priv, "received request to add station %pM\n",
sta->addr);
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n",
sta->addr);
- sta_priv->common.sta_id = IWL_INVALID_STATION;
+ sta_priv->sta_id = IWL_INVALID_STATION;
atomic_set(&sta_priv->pending_frames, 0);
if (vif->type == NL80211_IFTYPE_AP)
@@ -2800,19 +2530,20 @@ static int iwlagn_mac_sta_add(struct ieee80211_hw *hw,
IWL_ERR(priv, "Unable to add station %pM (%d)\n",
sta->addr, ret);
/* Should we return success if return code is EEXIST ? */
- mutex_unlock(&priv->mutex);
- return ret;
+ goto out;
}
- sta_priv->common.sta_id = sta_id;
+ sta_priv->sta_id = sta_id;
/* Initialize rate scaling */
IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM\n",
sta->addr);
iwl_rs_rate_init(priv, sta, sta_id);
- mutex_unlock(&priv->mutex);
+ out:
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
- return 0;
+ return ret;
}
static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
@@ -2833,84 +2564,82 @@ static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
*/
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
u16 ch;
- unsigned long flags = 0;
IWL_DEBUG_MAC80211(priv, "enter\n");
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
- if (iwl_is_rfkill(priv))
+ if (iwl_is_rfkill(priv->shrd))
goto out;
- if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
- test_bit(STATUS_SCANNING, &priv->status) ||
- test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status) ||
+ test_bit(STATUS_SCANNING, &priv->shrd->status) ||
+ test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status))
goto out;
if (!iwl_is_associated_ctx(ctx))
goto out;
- if (priv->cfg->ops->lib->set_channel_switch) {
-
- ch = channel->hw_value;
- if (le16_to_cpu(ctx->active.channel) != ch) {
- ch_info = iwl_get_channel_info(priv,
- channel->band,
- ch);
- if (!is_channel_valid(ch_info)) {
- IWL_DEBUG_MAC80211(priv, "invalid channel\n");
- goto out;
- }
- spin_lock_irqsave(&priv->lock, flags);
-
- priv->current_ht_config.smps = conf->smps_mode;
-
- /* Configure HT40 channels */
- ctx->ht.enabled = conf_is_ht(conf);
- if (ctx->ht.enabled) {
- if (conf_is_ht40_minus(conf)) {
- ctx->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- ctx->ht.is_40mhz = true;
- } else if (conf_is_ht40_plus(conf)) {
- ctx->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
- ctx->ht.is_40mhz = true;
- } else {
- ctx->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_NONE;
- ctx->ht.is_40mhz = false;
- }
- } else
- ctx->ht.is_40mhz = false;
-
- if ((le16_to_cpu(ctx->staging.channel) != ch))
- ctx->staging.flags = 0;
-
- iwl_set_rxon_channel(priv, channel, ctx);
- iwl_set_rxon_ht(priv, ht_conf);
- iwl_set_flags_for_band(priv, ctx, channel->band,
- ctx->vif);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- iwl_set_rate(priv);
- /*
- * at this point, staging_rxon has the
- * configuration for channel switch
- */
- set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status);
- priv->switch_channel = cpu_to_le16(ch);
- if (priv->cfg->ops->lib->set_channel_switch(priv,
- ch_switch)) {
- clear_bit(STATUS_CHANNEL_SWITCH_PENDING,
- &priv->status);
- priv->switch_channel = 0;
- ieee80211_chswitch_done(ctx->vif, false);
- }
+ if (!priv->cfg->lib->set_channel_switch)
+ goto out;
+
+ ch = channel->hw_value;
+ if (le16_to_cpu(ctx->active.channel) == ch)
+ goto out;
+
+ ch_info = iwl_get_channel_info(priv, channel->band, ch);
+ if (!is_channel_valid(ch_info)) {
+ IWL_DEBUG_MAC80211(priv, "invalid channel\n");
+ goto out;
+ }
+
+ spin_lock_irq(&priv->shrd->lock);
+
+ priv->current_ht_config.smps = conf->smps_mode;
+
+ /* Configure HT40 channels */
+ ctx->ht.enabled = conf_is_ht(conf);
+ if (ctx->ht.enabled) {
+ if (conf_is_ht40_minus(conf)) {
+ ctx->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ ctx->ht.is_40mhz = true;
+ } else if (conf_is_ht40_plus(conf)) {
+ ctx->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ ctx->ht.is_40mhz = true;
+ } else {
+ ctx->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ ctx->ht.is_40mhz = false;
}
+ } else
+ ctx->ht.is_40mhz = false;
+
+ if ((le16_to_cpu(ctx->staging.channel) != ch))
+ ctx->staging.flags = 0;
+
+ iwl_set_rxon_channel(priv, channel, ctx);
+ iwl_set_rxon_ht(priv, ht_conf);
+ iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif);
+
+ spin_unlock_irq(&priv->shrd->lock);
+
+ iwl_set_rate(priv);
+ /*
+ * at this point, staging_rxon has the
+ * configuration for channel switch
+ */
+ set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status);
+ priv->switch_channel = cpu_to_le16(ch);
+ if (priv->cfg->lib->set_channel_switch(priv, ch_switch)) {
+ clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status);
+ priv->switch_channel = 0;
+ ieee80211_chswitch_done(ctx->vif, false);
}
+
out:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
IWL_DEBUG_MAC80211(priv, "leave\n");
}
@@ -2940,7 +2669,7 @@ static void iwlagn_configure_filter(struct ieee80211_hw *hw,
#undef CHK
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
for_each_context(priv, ctx) {
ctx->staging.filter_flags &= ~filter_nand;
@@ -2952,7 +2681,7 @@ static void iwlagn_configure_filter(struct ieee80211_hw *hw,
*/
}
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
/*
* Receiving all multicast frames is always enabled by the
@@ -2968,18 +2697,14 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop)
{
struct iwl_priv *priv = hw->priv;
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
IWL_DEBUG_MAC80211(priv, "enter\n");
- /* do not support "flush" */
- if (!priv->cfg->ops->lib->txfifo_flush)
- goto done;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) {
IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n");
goto done;
}
- if (iwl_is_rfkill(priv)) {
+ if (iwl_is_rfkill(priv->shrd)) {
IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n");
goto done;
}
@@ -2990,107 +2715,204 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop)
*/
if (drop) {
IWL_DEBUG_MAC80211(priv, "send flush command\n");
- if (priv->cfg->ops->lib->txfifo_flush(priv, IWL_DROP_ALL)) {
+ if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) {
IWL_ERR(priv, "flush request fail\n");
goto done;
}
}
IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n");
- iwlagn_wait_tx_queue_empty(priv);
+ iwl_trans_wait_tx_queue_empty(trans(priv));
done:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
IWL_DEBUG_MAC80211(priv, "leave\n");
}
-static void iwlagn_disable_roc(struct iwl_priv *priv)
+void iwlagn_disable_roc(struct iwl_priv *priv)
{
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
- struct ieee80211_channel *chan = ACCESS_ONCE(priv->hw->conf.channel);
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
- if (!ctx->is_active)
+ if (!priv->hw_roc_setup)
return;
- ctx->staging.dev_type = RXON_DEV_TYPE_2STA;
+ ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- iwl_set_rxon_channel(priv, chan, ctx);
- iwl_set_flags_for_band(priv, ctx, chan->band, NULL);
- priv->_agn.hw_roc_channel = NULL;
+ priv->hw_roc_channel = NULL;
- iwlcore_commit_rxon(priv, ctx);
+ memset(ctx->staging.node_addr, 0, ETH_ALEN);
+
+ iwlagn_commit_rxon(priv, ctx);
ctx->is_active = false;
+ priv->hw_roc_setup = false;
}
-static void iwlagn_bg_roc_done(struct work_struct *work)
+static void iwlagn_disable_roc_work(struct work_struct *work)
{
struct iwl_priv *priv = container_of(work, struct iwl_priv,
- _agn.hw_roc_work.work);
+ hw_roc_disable_work.work);
- mutex_lock(&priv->mutex);
- ieee80211_remain_on_channel_expired(priv->hw);
+ mutex_lock(&priv->shrd->mutex);
iwlagn_disable_roc(priv);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
-static int iwl_mac_remain_on_channel(struct ieee80211_hw *hw,
+static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw,
struct ieee80211_channel *channel,
enum nl80211_channel_type channel_type,
int duration)
{
struct iwl_priv *priv = hw->priv;
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
int err = 0;
- if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
+ if (!(priv->shrd->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
return -EOPNOTSUPP;
- if (!(priv->contexts[IWL_RXON_CTX_PAN].interface_modes &
- BIT(NL80211_IFTYPE_P2P_CLIENT)))
+ if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)))
return -EOPNOTSUPP;
- mutex_lock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->shrd->mutex);
- if (priv->contexts[IWL_RXON_CTX_PAN].is_active ||
- test_bit(STATUS_SCAN_HW, &priv->status)) {
+ if (test_bit(STATUS_SCAN_HW, &priv->shrd->status)) {
err = -EBUSY;
goto out;
}
- priv->contexts[IWL_RXON_CTX_PAN].is_active = true;
- priv->_agn.hw_roc_channel = channel;
- priv->_agn.hw_roc_chantype = channel_type;
- priv->_agn.hw_roc_duration = DIV_ROUND_UP(duration * 1000, 1024);
- iwlcore_commit_rxon(priv, &priv->contexts[IWL_RXON_CTX_PAN]);
- queue_delayed_work(priv->workqueue, &priv->_agn.hw_roc_work,
- msecs_to_jiffies(duration + 20));
+ priv->hw_roc_channel = channel;
+ priv->hw_roc_chantype = channel_type;
+ priv->hw_roc_duration = duration;
+ priv->hw_roc_start_notified = false;
+ cancel_delayed_work(&priv->hw_roc_disable_work);
+
+ if (!ctx->is_active) {
+ ctx->is_active = true;
+ ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
+ memcpy(ctx->staging.node_addr,
+ priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr,
+ ETH_ALEN);
+ memcpy(ctx->staging.bssid_addr,
+ priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr,
+ ETH_ALEN);
+ err = iwlagn_commit_rxon(priv, ctx);
+ if (err)
+ goto out;
+ ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK |
+ RXON_FILTER_PROMISC_MSK |
+ RXON_FILTER_CTL2HOST_MSK;
+
+ err = iwlagn_commit_rxon(priv, ctx);
+ if (err) {
+ iwlagn_disable_roc(priv);
+ goto out;
+ }
+ priv->hw_roc_setup = true;
+ }
- msleep(IWL_MIN_SLOT_TIME); /* TU is almost ms */
- ieee80211_ready_on_channel(priv->hw);
+ err = iwl_scan_initiate(priv, ctx->vif, IWL_SCAN_ROC, channel->band);
+ if (err)
+ iwlagn_disable_roc(priv);
out:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
return err;
}
-static int iwl_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
+static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
{
struct iwl_priv *priv = hw->priv;
- if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
+ if (!(priv->shrd->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
return -EOPNOTSUPP;
- cancel_delayed_work_sync(&priv->_agn.hw_roc_work);
-
- mutex_lock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->shrd->mutex);
+ iwl_scan_cancel_timeout(priv, priv->hw_roc_duration);
iwlagn_disable_roc(priv);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
return 0;
}
+static int iwlagn_mac_tx_sync(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *bssid,
+ enum ieee80211_tx_sync_type type)
+{
+ struct iwl_priv *priv = hw->priv;
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+ struct iwl_rxon_context *ctx = vif_priv->ctx;
+ int ret;
+ u8 sta_id;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->shrd->mutex);
+
+ if (iwl_is_associated_ctx(ctx)) {
+ ret = 0;
+ goto out;
+ }
+
+ if (ctx->preauth_bssid || test_bit(STATUS_SCAN_HW, &priv->shrd->status)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = iwl_add_station_common(priv, ctx, bssid, true, NULL, &sta_id);
+ if (ret)
+ goto out;
+
+ if (WARN_ON(sta_id != ctx->ap_sta_id)) {
+ ret = -EIO;
+ goto out_remove_sta;
+ }
+
+ memcpy(ctx->bssid, bssid, ETH_ALEN);
+ ctx->preauth_bssid = true;
+
+ ret = iwlagn_commit_rxon(priv, ctx);
+
+ if (ret == 0)
+ goto out;
+
+ out_remove_sta:
+ iwl_remove_station(priv, sta_id, bssid);
+ out:
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ return ret;
+}
+
+static void iwlagn_mac_finish_tx_sync(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *bssid,
+ enum ieee80211_tx_sync_type type)
+{
+ struct iwl_priv *priv = hw->priv;
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+ struct iwl_rxon_context *ctx = vif_priv->ctx;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->shrd->mutex);
+
+ if (iwl_is_associated_ctx(ctx))
+ goto out;
+
+ iwl_remove_station(priv, ctx->ap_sta_id, bssid);
+ ctx->preauth_bssid = false;
+ /* no need to commit */
+ out:
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+}
+
/*****************************************************************************
*
* driver setup and teardown
@@ -3099,23 +2921,23 @@ static int iwl_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
static void iwl_setup_deferred_work(struct iwl_priv *priv)
{
- priv->workqueue = create_singlethread_workqueue(DRV_NAME);
+ priv->shrd->workqueue = create_singlethread_workqueue(DRV_NAME);
- init_waitqueue_head(&priv->wait_command_queue);
+ init_waitqueue_head(&priv->shrd->wait_command_queue);
INIT_WORK(&priv->restart, iwl_bg_restart);
- INIT_WORK(&priv->rx_replenish, iwl_bg_rx_replenish);
INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update);
INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work);
INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush);
INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency);
INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config);
- INIT_DELAYED_WORK(&priv->_agn.hw_roc_work, iwlagn_bg_roc_done);
+ INIT_DELAYED_WORK(&priv->hw_roc_disable_work,
+ iwlagn_disable_roc_work);
iwl_setup_scan_deferred_work(priv);
- if (priv->cfg->ops->lib->setup_deferred_work)
- priv->cfg->ops->lib->setup_deferred_work(priv);
+ if (priv->cfg->lib->bt_setup_deferred_work)
+ priv->cfg->lib->bt_setup_deferred_work(priv);
init_timer(&priv->statistics_periodic);
priv->statistics_periodic.data = (unsigned long)priv;
@@ -3128,15 +2950,12 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
init_timer(&priv->watchdog);
priv->watchdog.data = (unsigned long)priv;
priv->watchdog.function = iwl_bg_watchdog;
-
- tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
- iwl_irq_tasklet, (unsigned long)priv);
}
static void iwl_cancel_deferred_work(struct iwl_priv *priv)
{
- if (priv->cfg->ops->lib->cancel_deferred_work)
- priv->cfg->ops->lib->cancel_deferred_work(priv);
+ if (priv->cfg->lib->cancel_deferred_work)
+ priv->cfg->lib->cancel_deferred_work(priv);
cancel_work_sync(&priv->run_time_calib_work);
cancel_work_sync(&priv->beacon_update);
@@ -3145,6 +2964,7 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
cancel_work_sync(&priv->bt_full_concurrency);
cancel_work_sync(&priv->bt_runtime_config);
+ cancel_delayed_work_sync(&priv->hw_roc_disable_work);
del_timer_sync(&priv->statistics_periodic);
del_timer_sync(&priv->ucode_trace);
@@ -3175,10 +2995,9 @@ static int iwl_init_drv(struct iwl_priv *priv)
{
int ret;
- spin_lock_init(&priv->sta_lock);
- spin_lock_init(&priv->hcmd_lock);
+ spin_lock_init(&priv->shrd->sta_lock);
- mutex_init(&priv->mutex);
+ mutex_init(&priv->shrd->mutex);
priv->ieee_channels = NULL;
priv->ieee_rates = NULL;
@@ -3187,7 +3006,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
priv->iw_mode = NL80211_IFTYPE_STATION;
priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
- priv->_agn.agg_tids_count = 0;
+ priv->agg_tids_count = 0;
/* initialize force reset */
priv->force_reset[IWL_RF_RESET].reset_duration =
@@ -3198,9 +3017,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
priv->rx_statistics_jiffies = jiffies;
/* Choose which receivers/antennas to use */
- if (priv->cfg->ops->hcmd->set_rxon_chain)
- priv->cfg->ops->hcmd->set_rxon_chain(priv,
- &priv->contexts[IWL_RXON_CTX_BSS]);
+ iwlagn_set_rxon_chain(priv, &priv->contexts[IWL_RXON_CTX_BSS]);
iwl_init_scan_params(priv);
@@ -3221,7 +3038,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
goto err;
}
- ret = iwlcore_init_geos(priv);
+ ret = iwl_init_geos(priv);
if (ret) {
IWL_ERR(priv, "initializing geos failed: %d\n", ret);
goto err_free_channel_map;
@@ -3239,89 +3056,120 @@ err:
static void iwl_uninit_drv(struct iwl_priv *priv)
{
iwl_calib_free_results(priv);
- iwlcore_free_geos(priv);
+ iwl_free_geos(priv);
iwl_free_channel_map(priv);
+ if (priv->tx_cmd_pool)
+ kmem_cache_destroy(priv->tx_cmd_pool);
kfree(priv->scan_cmd);
kfree(priv->beacon_cmd);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ kfree(priv->wowlan_sram);
+#endif
+}
+
+static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
+ enum ieee80211_rssi_event rssi_event)
+{
+ struct iwl_priv *priv = hw->priv;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->shrd->mutex);
+
+ if (priv->cfg->bt_params &&
+ priv->cfg->bt_params->advanced_bt_coexist) {
+ if (rssi_event == RSSI_EVENT_LOW)
+ priv->bt_enable_pspoll = true;
+ else if (rssi_event == RSSI_EVENT_HIGH)
+ priv->bt_enable_pspoll = false;
+
+ iwlagn_send_advance_bt_config(priv);
+ } else {
+ IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled,"
+ "ignoring RSSI callback\n");
+ }
+
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+}
+
+static int iwlagn_mac_set_tim(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, bool set)
+{
+ struct iwl_priv *priv = hw->priv;
+
+ queue_work(priv->shrd->workqueue, &priv->beacon_update);
+
+ return 0;
}
struct ieee80211_ops iwlagn_hw_ops = {
.tx = iwlagn_mac_tx,
.start = iwlagn_mac_start,
.stop = iwlagn_mac_stop,
- .add_interface = iwl_mac_add_interface,
- .remove_interface = iwl_mac_remove_interface,
- .change_interface = iwl_mac_change_interface,
+#ifdef CONFIG_PM_SLEEP
+ .suspend = iwlagn_mac_suspend,
+ .resume = iwlagn_mac_resume,
+#endif
+ .add_interface = iwlagn_mac_add_interface,
+ .remove_interface = iwlagn_mac_remove_interface,
+ .change_interface = iwlagn_mac_change_interface,
.config = iwlagn_mac_config,
.configure_filter = iwlagn_configure_filter,
.set_key = iwlagn_mac_set_key,
.update_tkip_key = iwlagn_mac_update_tkip_key,
- .conf_tx = iwl_mac_conf_tx,
+ .set_rekey_data = iwlagn_mac_set_rekey_data,
+ .conf_tx = iwlagn_mac_conf_tx,
.bss_info_changed = iwlagn_bss_info_changed,
.ampdu_action = iwlagn_mac_ampdu_action,
- .hw_scan = iwl_mac_hw_scan,
+ .hw_scan = iwlagn_mac_hw_scan,
.sta_notify = iwlagn_mac_sta_notify,
.sta_add = iwlagn_mac_sta_add,
- .sta_remove = iwl_mac_sta_remove,
+ .sta_remove = iwlagn_mac_sta_remove,
.channel_switch = iwlagn_mac_channel_switch,
.flush = iwlagn_mac_flush,
- .tx_last_beacon = iwl_mac_tx_last_beacon,
- .remain_on_channel = iwl_mac_remain_on_channel,
- .cancel_remain_on_channel = iwl_mac_cancel_remain_on_channel,
- .offchannel_tx = iwl_mac_offchannel_tx,
- .offchannel_tx_cancel_wait = iwl_mac_offchannel_tx_cancel_wait,
- CFG80211_TESTMODE_CMD(iwl_testmode_cmd)
+ .tx_last_beacon = iwlagn_mac_tx_last_beacon,
+ .remain_on_channel = iwlagn_mac_remain_on_channel,
+ .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel,
+ .rssi_callback = iwlagn_mac_rssi_callback,
+ CFG80211_TESTMODE_CMD(iwlagn_mac_testmode_cmd)
+ CFG80211_TESTMODE_DUMP(iwlagn_mac_testmode_dump)
+ .tx_sync = iwlagn_mac_tx_sync,
+ .finish_tx_sync = iwlagn_mac_finish_tx_sync,
+ .set_tim = iwlagn_mac_set_tim,
};
static u32 iwl_hw_detect(struct iwl_priv *priv)
{
- u8 rev_id;
-
- pci_read_config_byte(priv->pci_dev, PCI_REVISION_ID, &rev_id);
- IWL_DEBUG_INFO(priv, "HW Revision ID = 0x%X\n", rev_id);
- return iwl_read32(priv, CSR_HW_REV);
+ return iwl_read32(bus(priv), CSR_HW_REV);
}
+/* Size of one Rx buffer in host DRAM */
+#define IWL_RX_BUF_SIZE_4K (4 * 1024)
+#define IWL_RX_BUF_SIZE_8K (8 * 1024)
+
static int iwl_set_hw_params(struct iwl_priv *priv)
{
- priv->hw_params.max_rxq_size = RX_QUEUE_SIZE;
- priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
if (iwlagn_mod_params.amsdu_size_8K)
- priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_8K);
+ hw_params(priv).rx_page_order =
+ get_order(IWL_RX_BUF_SIZE_8K);
else
- priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_4K);
-
- priv->hw_params.max_beacon_itrvl = IWL_MAX_UCODE_BEACON_INTERVAL;
+ hw_params(priv).rx_page_order =
+ get_order(IWL_RX_BUF_SIZE_4K);
if (iwlagn_mod_params.disable_11n)
- priv->cfg->sku &= ~IWL_SKU_N;
+ priv->cfg->sku &= ~EEPROM_SKU_CAP_11N_ENABLE;
+
+ hw_params(priv).num_ampdu_queues =
+ priv->cfg->base_params->num_of_ampdu_queues;
+ hw_params(priv).shadow_reg_enable =
+ priv->cfg->base_params->shadow_reg_enable;
+ hw_params(priv).sku = priv->cfg->sku;
+ hw_params(priv).wd_timeout = priv->cfg->base_params->wd_timeout;
/* Device-specific setup */
- return priv->cfg->ops->lib->set_hw_params(priv);
+ return priv->cfg->lib->set_hw_params(priv);
}
-static const u8 iwlagn_bss_ac_to_fifo[] = {
- IWL_TX_FIFO_VO,
- IWL_TX_FIFO_VI,
- IWL_TX_FIFO_BE,
- IWL_TX_FIFO_BK,
-};
-
-static const u8 iwlagn_bss_ac_to_queue[] = {
- 0, 1, 2, 3,
-};
-
-static const u8 iwlagn_pan_ac_to_fifo[] = {
- IWL_TX_FIFO_VO_IPAN,
- IWL_TX_FIFO_VI_IPAN,
- IWL_TX_FIFO_BE_IPAN,
- IWL_TX_FIFO_BK_IPAN,
-};
-
-static const u8 iwlagn_pan_ac_to_queue[] = {
- 7, 6, 5, 4,
-};
-
/* This function both allocates and initializes hw and priv. */
static struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg)
{
@@ -3344,173 +3192,85 @@ out:
return hw;
}
-static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops,
+ struct iwl_cfg *cfg)
{
- int err = 0, i;
+ int err = 0;
struct iwl_priv *priv;
struct ieee80211_hw *hw;
- struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
- unsigned long flags;
- u16 pci_cmd, num_mac;
+ u16 num_mac;
u32 hw_rev;
/************************
* 1. Allocating HW data
************************/
-
hw = iwl_alloc_all(cfg);
if (!hw) {
err = -ENOMEM;
goto out;
}
- priv = hw->priv;
- /* At this point both hw and priv are allocated. */
- priv->ucode_type = UCODE_SUBTYPE_NONE_LOADED;
-
- /*
- * The default context is always valid,
- * more may be discovered when firmware
- * is loaded.
- */
- priv->valid_contexts = BIT(IWL_RXON_CTX_BSS);
-
- for (i = 0; i < NUM_IWL_RXON_CTX; i++)
- priv->contexts[i].ctxid = i;
-
- priv->contexts[IWL_RXON_CTX_BSS].always_active = true;
- priv->contexts[IWL_RXON_CTX_BSS].is_active = true;
- priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON;
- priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING;
- priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC;
- priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM;
- priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID;
- priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY;
- priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo = iwlagn_bss_ac_to_fifo;
- priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue = iwlagn_bss_ac_to_queue;
- priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes =
- BIT(NL80211_IFTYPE_ADHOC);
- priv->contexts[IWL_RXON_CTX_BSS].interface_modes =
- BIT(NL80211_IFTYPE_STATION);
- priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP;
- priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS;
- priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS;
- priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS;
+ priv = hw->priv;
+ priv->shrd = &priv->_shrd;
+ bus->shrd = priv->shrd;
+ priv->shrd->bus = bus;
+ priv->shrd->priv = priv;
- priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON;
- priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd = REPLY_WIPAN_RXON_TIMING;
- priv->contexts[IWL_RXON_CTX_PAN].rxon_assoc_cmd = REPLY_WIPAN_RXON_ASSOC;
- priv->contexts[IWL_RXON_CTX_PAN].qos_cmd = REPLY_WIPAN_QOS_PARAM;
- priv->contexts[IWL_RXON_CTX_PAN].ap_sta_id = IWL_AP_ID_PAN;
- priv->contexts[IWL_RXON_CTX_PAN].wep_key_cmd = REPLY_WIPAN_WEPKEY;
- priv->contexts[IWL_RXON_CTX_PAN].bcast_sta_id = IWLAGN_PAN_BCAST_ID;
- priv->contexts[IWL_RXON_CTX_PAN].station_flags = STA_FLG_PAN_STATION;
- priv->contexts[IWL_RXON_CTX_PAN].ac_to_fifo = iwlagn_pan_ac_to_fifo;
- priv->contexts[IWL_RXON_CTX_PAN].ac_to_queue = iwlagn_pan_ac_to_queue;
- priv->contexts[IWL_RXON_CTX_PAN].mcast_queue = IWL_IPAN_MCAST_QUEUE;
- priv->contexts[IWL_RXON_CTX_PAN].interface_modes =
- BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP);
-#ifdef CONFIG_IWL_P2P
- priv->contexts[IWL_RXON_CTX_PAN].interface_modes |=
- BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO);
-#endif
- priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP;
- priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA;
- priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P;
+ priv->shrd->trans = trans_ops->alloc(priv->shrd);
+ if (priv->shrd->trans == NULL) {
+ err = -ENOMEM;
+ goto out_free_traffic_mem;
+ }
- BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
+ /* At this point both hw and priv are allocated. */
- SET_IEEE80211_DEV(hw, &pdev->dev);
+ SET_IEEE80211_DEV(hw, bus(priv)->dev);
IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n");
priv->cfg = cfg;
- priv->pci_dev = pdev;
- priv->inta_mask = CSR_INI_SET_MASK;
/* is antenna coupling more than 35dB ? */
priv->bt_ant_couple_ok =
- (iwlagn_ant_coupling > IWL_BT_ANTENNA_COUPLING_THRESHOLD) ?
- true : false;
+ (iwlagn_mod_params.ant_coupling >
+ IWL_BT_ANTENNA_COUPLING_THRESHOLD) ?
+ true : false;
/* enable/disable bt channel inhibition */
- priv->bt_ch_announce = iwlagn_bt_ch_announce;
+ priv->bt_ch_announce = iwlagn_mod_params.bt_ch_announce;
IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n",
(priv->bt_ch_announce) ? "On" : "Off");
if (iwl_alloc_traffic_mem(priv))
IWL_ERR(priv, "Not enough memory to generate traffic log\n");
- /**************************
- * 2. Initializing PCI bus
- **************************/
- pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
- PCIE_LINK_STATE_CLKPM);
-
- if (pci_enable_device(pdev)) {
- err = -ENODEV;
- goto out_ieee80211_free_hw;
- }
-
- pci_set_master(pdev);
-
- err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
- if (!err)
- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36));
- if (err) {
- err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
- if (!err)
- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- /* both attempts failed: */
- if (err) {
- IWL_WARN(priv, "No suitable DMA available.\n");
- goto out_pci_disable_device;
- }
- }
-
- err = pci_request_regions(pdev, DRV_NAME);
- if (err)
- goto out_pci_disable_device;
-
- pci_set_drvdata(pdev, priv);
-
-
- /***********************
- * 3. Read REV register
- ***********************/
- priv->hw_base = pci_iomap(pdev, 0, 0);
- if (!priv->hw_base) {
- err = -ENODEV;
- goto out_pci_release_regions;
- }
-
- IWL_DEBUG_INFO(priv, "pci_resource_len = 0x%08llx\n",
- (unsigned long long) pci_resource_len(pdev, 0));
- IWL_DEBUG_INFO(priv, "pci_resource_base = %p\n", priv->hw_base);
-
/* these spin locks will be used in apm_ops.init and EEPROM access
* we should init now
*/
- spin_lock_init(&priv->reg_lock);
- spin_lock_init(&priv->lock);
+ spin_lock_init(&bus(priv)->reg_lock);
+ spin_lock_init(&priv->shrd->lock);
/*
* stop and reset the on-board processor just in case it is in a
* strange state ... like being left stranded by a primary kernel
* and this is now the kdump kernel trying to start up
*/
- iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
+ iwl_write32(bus(priv), CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
+ /***********************
+ * 3. Read REV register
+ ***********************/
hw_rev = iwl_hw_detect(priv);
IWL_INFO(priv, "Detected %s, REV=0x%X\n",
priv->cfg->name, hw_rev);
- /* We disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state */
- pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
+ err = iwl_trans_request_irq(trans(priv));
+ if (err)
+ goto out_free_trans;
- if (iwl_prepare_card_hw(priv)) {
+ if (iwl_trans_prepare_card_hw(trans(priv))) {
+ err = -EIO;
IWL_WARN(priv, "Failed, HW not ready\n");
- goto out_iounmap;
+ goto out_free_trans;
}
/*****************
@@ -3520,7 +3280,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = iwl_eeprom_init(priv, hw_rev);
if (err) {
IWL_ERR(priv, "Unable to init EEPROM\n");
- goto out_iounmap;
+ goto out_free_trans;
}
err = iwl_eeprom_check_version(priv);
if (err)
@@ -3547,6 +3307,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
* 5. Setup HW constants
************************/
if (iwl_set_hw_params(priv)) {
+ err = -ENOENT;
IWL_ERR(priv, "failed to set hw parameters\n");
goto out_free_eeprom;
}
@@ -3563,51 +3324,30 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/********************
* 7. Setup services
********************/
- spin_lock_irqsave(&priv->lock, flags);
- iwl_disable_interrupts(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- pci_enable_msi(priv->pci_dev);
-
- iwl_alloc_isr_ict(priv);
-
- err = request_irq(priv->pci_dev->irq, iwl_isr_ict,
- IRQF_SHARED, DRV_NAME, priv);
- if (err) {
- IWL_ERR(priv, "Error allocating IRQ %d\n", priv->pci_dev->irq);
- goto out_disable_msi;
- }
-
iwl_setup_deferred_work(priv);
iwl_setup_rx_handlers(priv);
iwl_testmode_init(priv);
/*********************************************
- * 8. Enable interrupts and read RFKILL state
+ * 8. Enable interrupts
*********************************************/
- /* enable rfkill interrupt: hw bug w/a */
- pci_read_config_word(priv->pci_dev, PCI_COMMAND, &pci_cmd);
- if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
- pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
- pci_write_config_word(priv->pci_dev, PCI_COMMAND, pci_cmd);
- }
-
iwl_enable_rfkill_int(priv);
/* If platform's RF_KILL switch is NOT set to KILL */
- if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
- clear_bit(STATUS_RF_KILL_HW, &priv->status);
+ if (iwl_read32(bus(priv),
+ CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
+ clear_bit(STATUS_RF_KILL_HW, &priv->shrd->status);
else
- set_bit(STATUS_RF_KILL_HW, &priv->status);
+ set_bit(STATUS_RF_KILL_HW, &priv->shrd->status);
wiphy_rfkill_set_hw_state(priv->hw->wiphy,
- test_bit(STATUS_RF_KILL_HW, &priv->status));
+ test_bit(STATUS_RF_KILL_HW, &priv->shrd->status));
iwl_power_initialize(priv);
iwl_tt_initialize(priv);
- init_completion(&priv->_agn.firmware_loading_complete);
+ init_completion(&priv->firmware_loading_complete);
err = iwl_request_firmware(priv, true);
if (err)
@@ -3615,50 +3355,34 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
- out_destroy_workqueue:
- destroy_workqueue(priv->workqueue);
- priv->workqueue = NULL;
- free_irq(priv->pci_dev->irq, priv);
- iwl_free_isr_ict(priv);
- out_disable_msi:
- pci_disable_msi(priv->pci_dev);
+out_destroy_workqueue:
+ destroy_workqueue(priv->shrd->workqueue);
+ priv->shrd->workqueue = NULL;
iwl_uninit_drv(priv);
- out_free_eeprom:
+out_free_eeprom:
iwl_eeprom_free(priv);
- out_iounmap:
- pci_iounmap(pdev, priv->hw_base);
- out_pci_release_regions:
- pci_set_drvdata(pdev, NULL);
- pci_release_regions(pdev);
- out_pci_disable_device:
- pci_disable_device(pdev);
- out_ieee80211_free_hw:
+out_free_trans:
+ iwl_trans_free(trans(priv));
+out_free_traffic_mem:
iwl_free_traffic_mem(priv);
ieee80211_free_hw(priv->hw);
- out:
+out:
return err;
}
-static void __devexit iwl_pci_remove(struct pci_dev *pdev)
+void __devexit iwl_remove(struct iwl_priv * priv)
{
- struct iwl_priv *priv = pci_get_drvdata(pdev);
- unsigned long flags;
-
- if (!priv)
- return;
-
- wait_for_completion(&priv->_agn.firmware_loading_complete);
+ wait_for_completion(&priv->firmware_loading_complete);
IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n");
iwl_dbgfs_unregister(priv);
- sysfs_remove_group(&pdev->dev.kobj, &iwl_attribute_group);
- /* ieee80211_unregister_hw call wil cause iwl_mac_stop to
+ /* ieee80211_unregister_hw call wil cause iwlagn_mac_stop to
* to be called and iwl_down since we are removing the device
* we need to set STATUS_EXIT_PENDING bit.
*/
- set_bit(STATUS_EXIT_PENDING, &priv->status);
+ set_bit(STATUS_EXIT_PENDING, &priv->shrd->status);
iwl_testmode_cleanup(priv);
iwl_leds_exit(priv);
@@ -3668,50 +3392,29 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev)
priv->mac80211_registered = 0;
}
- /* Reset to low power before unloading driver. */
- iwl_apm_stop(priv);
-
iwl_tt_exit(priv);
- /* make sure we flush any pending irq or
- * tasklet for the driver
- */
- spin_lock_irqsave(&priv->lock, flags);
- iwl_disable_interrupts(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
+ /*This will stop the queues, move the device to low power state */
+ iwl_trans_stop_device(trans(priv));
- iwl_synchronize_irq(priv);
-
- iwl_dealloc_ucode_pci(priv);
-
- if (priv->rxq.bd)
- iwlagn_rx_queue_free(priv, &priv->rxq);
- iwlagn_hw_txq_ctx_free(priv);
+ iwl_dealloc_ucode(priv);
iwl_eeprom_free(priv);
-
/*netif_stop_queue(dev); */
- flush_workqueue(priv->workqueue);
+ flush_workqueue(priv->shrd->workqueue);
- /* ieee80211_unregister_hw calls iwl_mac_stop, which flushes
- * priv->workqueue... so we can't take down the workqueue
+ /* ieee80211_unregister_hw calls iwlagn_mac_stop, which flushes
+ * priv->shrd->workqueue... so we can't take down the workqueue
* until now... */
- destroy_workqueue(priv->workqueue);
- priv->workqueue = NULL;
+ destroy_workqueue(priv->shrd->workqueue);
+ priv->shrd->workqueue = NULL;
iwl_free_traffic_mem(priv);
- free_irq(priv->pci_dev->irq, priv);
- pci_disable_msi(priv->pci_dev);
- pci_iounmap(pdev, priv->hw_base);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
+ iwl_trans_free(trans(priv));
iwl_uninit_drv(priv);
- iwl_free_isr_ict(priv);
-
dev_kfree_skb(priv->beacon_skb);
ieee80211_free_hw(priv->hw);
@@ -3723,206 +3426,6 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev)
* driver and module entry point
*
*****************************************************************************/
-
-/* Hardware specific file defines the PCI IDs table for that hardware module */
-static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
- {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */
-
-/* 5300 Series WiFi */
- {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */
-
-/* 5350 Series WiFi/WiMax */
- {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */
-
-/* 5150 Series Wifi/WiMax */
- {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */
-
- {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */
-
-/* 6x00 Series */
- {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)},
- {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)},
-
-/* 6x05 Series */
- {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)},
-
-/* 6x30 Series */
- {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)},
- {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)},
- {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)},
- {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)},
- {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)},
- {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)},
- {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)},
-
-/* 6x50 WiFi/WiMax Series */
- {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)},
-
-/* 6150 WiFi/WiMax Series */
- {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)},
- {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)},
- {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)},
-
-/* 1000 Series WiFi */
- {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)},
-
-/* 100 Series WiFi */
- {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)},
- {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)},
- {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)},
- {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)},
- {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)},
- {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)},
-
-/* 130 Series WiFi */
- {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)},
- {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)},
- {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)},
-
-/* 2x00 Series */
- {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0890, 0x4026, iwl2000_2bg_cfg)},
- {IWL_PCI_DEVICE(0x0891, 0x4226, iwl2000_2bg_cfg)},
- {IWL_PCI_DEVICE(0x0890, 0x4426, iwl2000_2bg_cfg)},
-
-/* 2x30 Series */
- {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0887, 0x4066, iwl2030_2bg_cfg)},
- {IWL_PCI_DEVICE(0x0888, 0x4266, iwl2030_2bg_cfg)},
- {IWL_PCI_DEVICE(0x0887, 0x4466, iwl2030_2bg_cfg)},
-
-/* 6x35 Series */
- {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x4064, iwl6035_2abg_cfg)},
- {IWL_PCI_DEVICE(0x088F, 0x4264, iwl6035_2abg_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x4464, iwl6035_2abg_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x4066, iwl6035_2bg_cfg)},
- {IWL_PCI_DEVICE(0x088F, 0x4266, iwl6035_2bg_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x4466, iwl6035_2bg_cfg)},
-
-/* 105 Series */
- {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0894, 0x0026, iwl105_bg_cfg)},
- {IWL_PCI_DEVICE(0x0895, 0x0226, iwl105_bg_cfg)},
- {IWL_PCI_DEVICE(0x0894, 0x0426, iwl105_bg_cfg)},
-
-/* 135 Series */
- {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0892, 0x0066, iwl135_bg_cfg)},
- {IWL_PCI_DEVICE(0x0893, 0x0266, iwl135_bg_cfg)},
- {IWL_PCI_DEVICE(0x0892, 0x0466, iwl135_bg_cfg)},
-
- {0}
-};
-MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
-
-static struct pci_driver iwl_driver = {
- .name = DRV_NAME,
- .id_table = iwl_hw_card_ids,
- .probe = iwl_pci_probe,
- .remove = __devexit_p(iwl_pci_remove),
- .driver.pm = IWL_PM_OPS,
-};
-
static int __init iwl_init(void)
{
@@ -3936,12 +3439,10 @@ static int __init iwl_init(void)
return ret;
}
- ret = pci_register_driver(&iwl_driver);
- if (ret) {
- pr_err("Unable to initialize PCI module\n");
- goto error_register;
- }
+ ret = iwl_pci_register_driver();
+ if (ret)
+ goto error_register;
return ret;
error_register:
@@ -3951,7 +3452,7 @@ error_register:
static void __exit iwl_exit(void)
{
- pci_unregister_driver(&iwl_driver);
+ iwl_pci_unregister_driver();
iwlagn_rate_control_unregister();
}
@@ -3959,7 +3460,8 @@ module_exit(iwl_exit);
module_init(iwl_init);
#ifdef CONFIG_IWLWIFI_DEBUG
-module_param_named(debug, iwl_debug_level, uint, S_IRUGO | S_IWUSR);
+module_param_named(debug, iwlagn_mod_params.debug_level, uint,
+ S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "debug output mask");
#endif
@@ -3975,21 +3477,77 @@ MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size");
module_param_named(fw_restart, iwlagn_mod_params.restart_fw, int, S_IRUGO);
MODULE_PARM_DESC(fw_restart, "restart firmware in case of error");
-module_param_named(ucode_alternative, iwlagn_wanted_ucode_alternative, int,
- S_IRUGO);
+module_param_named(ucode_alternative,
+ iwlagn_mod_params.wanted_ucode_alternative,
+ int, S_IRUGO);
MODULE_PARM_DESC(ucode_alternative,
"specify ucode alternative to use from ucode file");
-module_param_named(antenna_coupling, iwlagn_ant_coupling, int, S_IRUGO);
+module_param_named(antenna_coupling, iwlagn_mod_params.ant_coupling,
+ int, S_IRUGO);
MODULE_PARM_DESC(antenna_coupling,
"specify antenna coupling in dB (defualt: 0 dB)");
-module_param_named(bt_ch_inhibition, iwlagn_bt_ch_announce, bool, S_IRUGO);
+module_param_named(bt_ch_inhibition, iwlagn_mod_params.bt_ch_announce,
+ bool, S_IRUGO);
MODULE_PARM_DESC(bt_ch_inhibition,
- "Disable BT channel inhibition (default: enable)");
+ "Enable BT channel inhibition (default: enable)");
module_param_named(plcp_check, iwlagn_mod_params.plcp_check, bool, S_IRUGO);
MODULE_PARM_DESC(plcp_check, "Check plcp health (default: 1 [enabled])");
module_param_named(ack_check, iwlagn_mod_params.ack_check, bool, S_IRUGO);
MODULE_PARM_DESC(ack_check, "Check ack health (default: 0 [disabled])");
+
+module_param_named(wd_disable, iwlagn_mod_params.wd_disable, bool, S_IRUGO);
+MODULE_PARM_DESC(wd_disable,
+ "Disable stuck queue watchdog timer (default: 0 [enabled])");
+
+/*
+ * set bt_coex_active to true, uCode will do kill/defer
+ * every time the priority line is asserted (BT is sending signals on the
+ * priority line in the PCIx).
+ * set bt_coex_active to false, uCode will ignore the BT activity and
+ * perform the normal operation
+ *
+ * User might experience transmit issue on some platform due to WiFi/BT
+ * co-exist problem. The possible behaviors are:
+ * Able to scan and finding all the available AP
+ * Not able to associate with any AP
+ * On those platforms, WiFi communication can be restored by set
+ * "bt_coex_active" module parameter to "false"
+ *
+ * default: bt_coex_active = true (BT_COEX_ENABLE)
+ */
+module_param_named(bt_coex_active, iwlagn_mod_params.bt_coex_active,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(bt_coex_active, "enable wifi/bt co-exist (default: enable)");
+
+module_param_named(led_mode, iwlagn_mod_params.led_mode, int, S_IRUGO);
+MODULE_PARM_DESC(led_mode, "0=system default, "
+ "1=On(RF On)/Off(RF Off), 2=blinking (default: 0)");
+
+module_param_named(power_save, iwlagn_mod_params.power_save,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(power_save,
+ "enable WiFi power management (default: disable)");
+
+module_param_named(power_level, iwlagn_mod_params.power_level,
+ int, S_IRUGO);
+MODULE_PARM_DESC(power_level,
+ "default power save level (range from 1 - 5, default: 1)");
+
+module_param_named(auto_agg, iwlagn_mod_params.auto_agg,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(auto_agg,
+ "enable agg w/o check traffic load (default: enable)");
+
+/*
+ * For now, keep using power level 1 instead of automatically
+ * adjusting ...
+ */
+module_param_named(no_sleep_autoadjust, iwlagn_mod_params.no_sleep_autoadjust,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(no_sleep_autoadjust,
+ "don't automatically adjust sleep level "
+ "according to maximum network latency (default: true)");
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h
index d1716844002e..5b936ec1a541 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.h
@@ -65,86 +65,20 @@
#include "iwl-dev.h"
-/* configuration for the _agn devices */
-extern struct iwl_cfg iwl5300_agn_cfg;
-extern struct iwl_cfg iwl5100_agn_cfg;
-extern struct iwl_cfg iwl5350_agn_cfg;
-extern struct iwl_cfg iwl5100_bgn_cfg;
-extern struct iwl_cfg iwl5100_abg_cfg;
-extern struct iwl_cfg iwl5150_agn_cfg;
-extern struct iwl_cfg iwl5150_abg_cfg;
-extern struct iwl_cfg iwl6005_2agn_cfg;
-extern struct iwl_cfg iwl6005_2abg_cfg;
-extern struct iwl_cfg iwl6005_2bg_cfg;
-extern struct iwl_cfg iwl1030_bgn_cfg;
-extern struct iwl_cfg iwl1030_bg_cfg;
-extern struct iwl_cfg iwl6030_2agn_cfg;
-extern struct iwl_cfg iwl6030_2abg_cfg;
-extern struct iwl_cfg iwl6030_2bgn_cfg;
-extern struct iwl_cfg iwl6030_2bg_cfg;
-extern struct iwl_cfg iwl6000i_2agn_cfg;
-extern struct iwl_cfg iwl6000i_2abg_cfg;
-extern struct iwl_cfg iwl6000i_2bg_cfg;
-extern struct iwl_cfg iwl6000_3agn_cfg;
-extern struct iwl_cfg iwl6050_2agn_cfg;
-extern struct iwl_cfg iwl6050_2abg_cfg;
-extern struct iwl_cfg iwl6150_bgn_cfg;
-extern struct iwl_cfg iwl6150_bg_cfg;
-extern struct iwl_cfg iwl1000_bgn_cfg;
-extern struct iwl_cfg iwl1000_bg_cfg;
-extern struct iwl_cfg iwl100_bgn_cfg;
-extern struct iwl_cfg iwl100_bg_cfg;
-extern struct iwl_cfg iwl130_bgn_cfg;
-extern struct iwl_cfg iwl130_bg_cfg;
-extern struct iwl_cfg iwl2000_2bgn_cfg;
-extern struct iwl_cfg iwl2000_2bg_cfg;
-extern struct iwl_cfg iwl2030_2bgn_cfg;
-extern struct iwl_cfg iwl2030_2bg_cfg;
-extern struct iwl_cfg iwl6035_2agn_cfg;
-extern struct iwl_cfg iwl6035_2abg_cfg;
-extern struct iwl_cfg iwl6035_2bg_cfg;
-extern struct iwl_cfg iwl105_bg_cfg;
-extern struct iwl_cfg iwl105_bgn_cfg;
-extern struct iwl_cfg iwl135_bg_cfg;
-extern struct iwl_cfg iwl135_bgn_cfg;
-
-extern struct iwl_mod_params iwlagn_mod_params;
-extern struct iwl_hcmd_ops iwlagn_hcmd;
-extern struct iwl_hcmd_ops iwlagn_bt_hcmd;
-extern struct iwl_hcmd_utils_ops iwlagn_hcmd_utils;
-
extern struct ieee80211_ops iwlagn_hw_ops;
-int iwl_reset_ict(struct iwl_priv *priv);
-void iwl_disable_ict(struct iwl_priv *priv);
-int iwl_alloc_isr_ict(struct iwl_priv *priv);
-void iwl_free_isr_ict(struct iwl_priv *priv);
-irqreturn_t iwl_isr_ict(int irq, void *data);
+int iwl_reset_ict(struct iwl_trans *trans);
-/* call this function to flush any scheduled tasklet */
-static inline void iwl_synchronize_irq(struct iwl_priv *priv)
+static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd)
{
- /* wait to make sure we flush pending tasklet*/
- synchronize_irq(priv->pci_dev->irq);
- tasklet_kill(&priv->irq_tasklet);
+ hdr->op_code = cmd;
+ hdr->first_group = 0;
+ hdr->groups_num = 1;
+ hdr->data_valid = 1;
}
-int iwl_prepare_card_hw(struct iwl_priv *priv);
-
-int iwlagn_start_device(struct iwl_priv *priv);
-void iwlagn_stop_device(struct iwl_priv *priv);
-
-/* tx queue */
-void iwlagn_set_wr_ptrs(struct iwl_priv *priv,
- int txq_id, u32 index);
-void iwlagn_tx_queue_set_status(struct iwl_priv *priv,
- struct iwl_tx_queue *txq,
- int tx_fifo_id, int scd_retry);
-void iwlagn_txq_set_sched(struct iwl_priv *priv, u32 mask);
-void iwl_free_tfds_in_queue(struct iwl_priv *priv,
- int sta_id, int tid, int freed);
-
/* RXON */
+int iwlagn_set_pan_params(struct iwl_priv *priv);
int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed);
@@ -154,67 +88,40 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
u32 changes);
/* uCode */
-void iwlagn_rx_calib_result(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb);
+int iwlagn_rx_calib_result(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd);
int iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type);
void iwlagn_send_prio_tbl(struct iwl_priv *priv);
int iwlagn_run_init_ucode(struct iwl_priv *priv);
int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
struct fw_img *image,
- int subtype, int alternate_subtype);
+ enum iwlagn_ucode_type ucode_type);
/* lib */
-void iwl_check_abort_status(struct iwl_priv *priv,
- u8 frame_count, u32 status);
-void iwlagn_rx_handler_setup(struct iwl_priv *priv);
-void iwlagn_setup_deferred_work(struct iwl_priv *priv);
-int iwlagn_hw_valid_rtc_data_addr(u32 addr);
int iwlagn_send_tx_power(struct iwl_priv *priv);
void iwlagn_temperature(struct iwl_priv *priv);
u16 iwlagn_eeprom_calib_version(struct iwl_priv *priv);
-const u8 *iwlagn_eeprom_query_addr(const struct iwl_priv *priv,
- size_t offset);
-void iwlagn_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
-int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
-int iwlagn_hw_nic_init(struct iwl_priv *priv);
-int iwlagn_wait_tx_queue_empty(struct iwl_priv *priv);
int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control);
void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control);
+int iwlagn_send_beacon_cmd(struct iwl_priv *priv);
/* rx */
-void iwlagn_rx_queue_restock(struct iwl_priv *priv);
-void iwlagn_rx_allocate(struct iwl_priv *priv, gfp_t priority);
-void iwlagn_rx_replenish(struct iwl_priv *priv);
-void iwlagn_rx_replenish_now(struct iwl_priv *priv);
-void iwlagn_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
-int iwlagn_rxq_stop(struct iwl_priv *priv);
int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band);
void iwl_setup_rx_handlers(struct iwl_priv *priv);
+
/* tx */
-void iwlagn_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq);
-int iwlagn_txq_attach_buf_to_tfd(struct iwl_priv *priv,
- struct iwl_tx_queue *txq,
- dma_addr_t addr, u16 len, u8 reset);
-void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
- struct ieee80211_tx_info *info);
int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb);
int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 *ssn);
int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid);
-void iwlagn_txq_agg_queue_setup(struct iwl_priv *priv,
- struct ieee80211_sta *sta,
- int tid, int frame_limit);
-int iwlagn_txq_check_empty(struct iwl_priv *priv,
- int sta_id, u8 tid, int txq_id);
-void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb);
-int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index);
-void iwlagn_hw_txq_ctx_free(struct iwl_priv *priv);
-int iwlagn_txq_ctx_alloc(struct iwl_priv *priv);
-void iwlagn_txq_ctx_reset(struct iwl_priv *priv);
-void iwlagn_txq_ctx_stop(struct iwl_priv *priv);
+int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd);
static inline u32 iwl_tx_status_to_mac80211(u32 status)
{
@@ -242,24 +149,19 @@ static inline bool iwl_is_tx_success(u32 status)
u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid);
/* scan */
-int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
void iwlagn_post_scan(struct iwl_priv *priv);
-
-/* station mgmt */
-int iwlagn_manage_ibss_station(struct iwl_priv *priv,
- struct ieee80211_vif *vif, bool add);
-
-/* hcmd */
-int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant);
-int iwlagn_send_beacon_cmd(struct iwl_priv *priv);
+void iwlagn_disable_roc(struct iwl_priv *priv);
/* bt coex */
void iwlagn_send_advance_bt_config(struct iwl_priv *priv);
-void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb);
+int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd);
void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv);
void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv);
void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv);
+void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv);
+void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena);
#ifdef CONFIG_IWLWIFI_DEBUG
const char *iwl_get_tx_fail_reason(u32 status);
@@ -269,7 +171,120 @@ static inline const char *iwl_get_tx_fail_reason(u32 status) { return ""; }
static inline const char *iwl_get_agg_tx_fail_reason(u16 status) { return ""; }
#endif
+
/* station management */
+int iwlagn_manage_ibss_station(struct iwl_priv *priv,
+ struct ieee80211_vif *vif, bool add);
+#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
+#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */
+#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of
+ being activated */
+#define IWL_STA_LOCAL BIT(3) /* station state not directed by mac80211;
+ (this is for the IBSS BSSID stations) */
+#define IWL_STA_BCAST BIT(4) /* this station is the special bcast station */
+
+
+void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
+void iwl_clear_ucode_stations(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx);
+void iwl_dealloc_bcast_stations(struct iwl_priv *priv);
+int iwl_get_free_ucode_key_offset(struct iwl_priv *priv);
+int iwl_send_add_sta(struct iwl_priv *priv,
+ struct iwl_addsta_cmd *sta, u8 flags);
+int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ const u8 *addr, bool is_ap,
+ struct ieee80211_sta *sta, u8 *sta_id_r);
+int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
+ const u8 *addr);
+int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+
+u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ const u8 *addr, bool is_ap, struct ieee80211_sta *sta);
+
+void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ u8 sta_id, struct iwl_link_quality_cmd *link_cmd);
+int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ struct iwl_link_quality_cmd *lq, u8 flags, bool init);
+void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
+int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+
+
+/**
+ * iwl_clear_driver_stations - clear knowledge of all stations from driver
+ * @priv: iwl priv struct
+ *
+ * This is called during iwl_down() to make sure that in the case
+ * we're coming there from a hardware restart mac80211 will be
+ * able to reconfigure stations -- if we're getting there in the
+ * normal down flow then the stations will already be cleared.
+ */
+static inline void iwl_clear_driver_stations(struct iwl_priv *priv)
+{
+ unsigned long flags;
+ struct iwl_rxon_context *ctx;
+
+ spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+ memset(priv->stations, 0, sizeof(priv->stations));
+ priv->num_stations = 0;
+
+ priv->ucode_key_table = 0;
+
+ for_each_context(priv, ctx) {
+ /*
+ * Remove all key information that is not stored as part
+ * of station information since mac80211 may not have had
+ * a chance to remove all the keys. When device is
+ * reconfigured by mac80211 after an error all keys will
+ * be reconfigured.
+ */
+ memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys));
+ ctx->key_mapping_keys = 0;
+ }
+
+ spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+}
+
+static inline int iwl_sta_id(struct ieee80211_sta *sta)
+{
+ if (WARN_ON(!sta))
+ return IWL_INVALID_STATION;
+
+ return ((struct iwl_station_priv *)sta->drv_priv)->sta_id;
+}
+
+/**
+ * iwl_sta_id_or_broadcast - return sta_id or broadcast sta
+ * @priv: iwl priv
+ * @context: the current context
+ * @sta: mac80211 station
+ *
+ * In certain circumstances mac80211 passes a station pointer
+ * that may be %NULL, for example during TX or key setup. In
+ * that case, we need to use the broadcast station, so this
+ * inline wraps that pattern.
+ */
+static inline int iwl_sta_id_or_broadcast(struct iwl_priv *priv,
+ struct iwl_rxon_context *context,
+ struct ieee80211_sta *sta)
+{
+ int sta_id;
+
+ if (!sta)
+ return context->bcast_sta_id;
+
+ sta_id = iwl_sta_id(sta);
+
+ /*
+ * mac80211 should not be passing a partially
+ * initialised station!
+ */
+ WARN_ON(sta_id == IWL_INVALID_STATION);
+
+ return sta_id;
+}
+
int iwlagn_alloc_bcast_station(struct iwl_priv *priv,
struct iwl_rxon_context *ctx);
int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
@@ -283,11 +298,13 @@ int iwl_set_default_wep_key(struct iwl_priv *priv,
int iwl_restore_default_wep_keys(struct iwl_priv *priv,
struct iwl_rxon_context *ctx);
int iwl_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *key, u8 sta_id);
+ struct ieee80211_key_conf *key,
+ struct ieee80211_sta *sta);
int iwl_remove_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *key, u8 sta_id);
+ struct ieee80211_key_conf *key,
+ struct ieee80211_sta *sta);
void iwl_update_tkip_key(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
+ struct ieee80211_vif *vif,
struct ieee80211_key_conf *keyconf,
struct ieee80211_sta *sta, u32 iv32, u16 *phase1key);
int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid);
@@ -296,6 +313,8 @@ int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
int tid);
void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt);
+int iwl_update_bcast_station(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx);
int iwl_update_bcast_stations(struct iwl_priv *priv);
void iwlagn_mac_sta_notify(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -319,7 +338,7 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
}
/* eeprom */
-void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv);
+void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv);
void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac);
/* notification wait support */
@@ -342,12 +361,24 @@ extern int iwlagn_init_alive_start(struct iwl_priv *priv);
extern int iwl_alive_start(struct iwl_priv *priv);
/* svtool */
#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL
-extern int iwl_testmode_cmd(struct ieee80211_hw *hw, void *data, int len);
+extern int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data,
+ int len);
+extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct netlink_callback *cb,
+ void *data, int len);
extern void iwl_testmode_init(struct iwl_priv *priv);
extern void iwl_testmode_cleanup(struct iwl_priv *priv);
#else
static inline
-int iwl_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
+int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
+{
+ return -ENOSYS;
+}
+static inline
+int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct netlink_callback *cb,
+ void *data, int len)
{
return -ENOSYS;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-bus.h b/drivers/net/wireless/iwlwifi/iwl-bus.h
new file mode 100644
index 000000000000..08b97594e305
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-bus.h
@@ -0,0 +1,201 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_bus_h__
+#define __iwl_bus_h__
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+/**
+ * DOC: Bus layer - role and goal
+ *
+ * iwl-bus.h defines the API to the bus layer of the iwlwifi driver.
+ * The bus layer is responsible for doing very basic bus operations that are
+ * listed in the iwl_bus_ops structure.
+ * The bus layer registers to the bus driver, advertises the supported HW and
+ * gets notifications about enumeration, suspend, resume.
+ * For the moment, the bus layer is not a linux kernel module as itself, and
+ * the module_init function of the driver must call the bus specific
+ * registration functions. These functions are listed at the end of this file.
+ * For the moment, there is only one implementation of this interface: PCI-e.
+ * This implementation is iwl-pci.c
+ */
+
+/**
+ * DOC: encapsulation and type safety
+ *
+ * The iwl_bus describes the data that is shared amongst all the bus layer
+ * implementations. This data is visible to other layers. Data in the bus
+ * specific area is not visible outside the bus specific implementation.
+ * iwl_bus holds a pointer to iwl_shared which holds pointer to all the other
+ * layers of the driver (iwl_priv, iwl_trans). In fact, this is the way to go
+ * when the transport layer needs to call a function of another layer.
+ *
+ * In order to achieve encapsulation, iwl_priv cannot be dereferenced from the
+ * bus layer. Type safety is still kept since functions that gets iwl_priv gets
+ * a typed pointer (as opposed to void *).
+ */
+
+/**
+ * DOC: probe flow
+ *
+ * The module_init calls the bus specific registration function. The
+ * registration to the bus layer will trigger an enumeration of the bus which
+ * will call the bus specific probe function.
+ * The first thing this function must do is to allocate the memory needed by
+ * iwl_bus + the bus_specific data.
+ * Once the bus specific probe function has configured the hardware, it
+ * chooses the appropriate transport layer and calls iwl_probe that will run
+ * the bus independent probe flow.
+ *
+ * Note: The bus specific code must set the following data in iwl_bus before it
+ * calls iwl_probe:
+ * * bus->dev
+ * * bus->irq
+ * * bus->ops
+ */
+
+struct iwl_shared;
+struct iwl_bus;
+
+/**
+ * struct iwl_bus_ops - bus specific operations
+ * @get_pm_support: must returns true if the bus can go to sleep
+ * @apm_config: will be called during the config of the APM
+ * @get_hw_id: prints the hw_id in the provided buffer
+ * @write8: write a byte to register at offset ofs
+ * @write32: write a dword to register at offset ofs
+ * @wread32: read a dword at register at offset ofs
+ */
+struct iwl_bus_ops {
+ bool (*get_pm_support)(struct iwl_bus *bus);
+ void (*apm_config)(struct iwl_bus *bus);
+ void (*get_hw_id)(struct iwl_bus *bus, char buf[], int buf_len);
+ void (*write8)(struct iwl_bus *bus, u32 ofs, u8 val);
+ void (*write32)(struct iwl_bus *bus, u32 ofs, u32 val);
+ u32 (*read32)(struct iwl_bus *bus, u32 ofs);
+};
+
+/**
+ * struct iwl_bus - bus common data
+ *
+ * This data is common to all bus layer implementations.
+ *
+ * @dev - pointer to struct device * that represents the device
+ * @ops - pointer to iwl_bus_ops
+ * @shrd - pointer to iwl_shared which holds shared data from the upper layer
+ * NB: for the time being this needs to be set by the upper layer since
+ * it allocates the shared data
+ * @irq - the irq number for the device
+ * @reg_lock - protect hw register access
+ */
+struct iwl_bus {
+ struct device *dev;
+ const struct iwl_bus_ops *ops;
+ struct iwl_shared *shrd;
+
+ unsigned int irq;
+ spinlock_t reg_lock;
+
+ /* pointer to bus specific struct */
+ /*Ensure that this pointer will always be aligned to sizeof pointer */
+ char bus_specific[0] __attribute__((__aligned__(sizeof(void *))));
+};
+
+static inline bool bus_get_pm_support(struct iwl_bus *bus)
+{
+ return bus->ops->get_pm_support(bus);
+}
+
+static inline void bus_apm_config(struct iwl_bus *bus)
+{
+ bus->ops->apm_config(bus);
+}
+
+static inline void bus_get_hw_id(struct iwl_bus *bus, char buf[], int buf_len)
+{
+ bus->ops->get_hw_id(bus, buf, buf_len);
+}
+
+static inline void bus_write8(struct iwl_bus *bus, u32 ofs, u8 val)
+{
+ bus->ops->write8(bus, ofs, val);
+}
+
+static inline void bus_write32(struct iwl_bus *bus, u32 ofs, u32 val)
+{
+ bus->ops->write32(bus, ofs, val);
+}
+
+static inline u32 bus_read32(struct iwl_bus *bus, u32 ofs)
+{
+ return bus->ops->read32(bus, ofs);
+}
+
+/*****************************************************
+* Bus layer registration functions
+******************************************************/
+int __must_check iwl_pci_register_driver(void);
+void iwl_pci_unregister_driver(void);
+
+#endif /* __iwl_bus_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000-hw.h b/drivers/net/wireless/iwlwifi/iwl-cfg.h
index 05ad47628b63..2a2dc4597ba1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-cfg.h
@@ -60,29 +60,58 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
-/*
- * Please use this file (iwl-5000-hw.h) only for hardware-related definitions.
- * Use iwl-commands.h for uCode API definitions.
- */
-
-#ifndef __iwl_5000_hw_h__
-#define __iwl_5000_hw_h__
-
-/* 5150 only */
-#define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5)
+#ifndef __iwl_pci_h__
+#define __iwl_pci_h__
-static inline s32 iwl_temp_calib_to_offset(struct iwl_priv *priv)
-{
- u16 temperature, voltage;
- __le16 *temp_calib =
- (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_5000_TEMPERATURE);
- temperature = le16_to_cpu(temp_calib[0]);
- voltage = le16_to_cpu(temp_calib[1]);
-
- /* offset = temp - volt / coeff */
- return (s32)(temperature - voltage / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF);
-}
+/*
+ * This file declares the config structures for all devices.
+ */
-#endif /* __iwl_5000_hw_h__ */
+extern struct iwl_cfg iwl5300_agn_cfg;
+extern struct iwl_cfg iwl5100_agn_cfg;
+extern struct iwl_cfg iwl5350_agn_cfg;
+extern struct iwl_cfg iwl5100_bgn_cfg;
+extern struct iwl_cfg iwl5100_abg_cfg;
+extern struct iwl_cfg iwl5150_agn_cfg;
+extern struct iwl_cfg iwl5150_abg_cfg;
+extern struct iwl_cfg iwl6005_2agn_cfg;
+extern struct iwl_cfg iwl6005_2abg_cfg;
+extern struct iwl_cfg iwl6005_2bg_cfg;
+extern struct iwl_cfg iwl6005_2agn_sff_cfg;
+extern struct iwl_cfg iwl6005_2agn_d_cfg;
+extern struct iwl_cfg iwl1030_bgn_cfg;
+extern struct iwl_cfg iwl1030_bg_cfg;
+extern struct iwl_cfg iwl6030_2agn_cfg;
+extern struct iwl_cfg iwl6030_2abg_cfg;
+extern struct iwl_cfg iwl6030_2bgn_cfg;
+extern struct iwl_cfg iwl6030_2bg_cfg;
+extern struct iwl_cfg iwl6000i_2agn_cfg;
+extern struct iwl_cfg iwl6000i_2abg_cfg;
+extern struct iwl_cfg iwl6000i_2bg_cfg;
+extern struct iwl_cfg iwl6000_3agn_cfg;
+extern struct iwl_cfg iwl6050_2agn_cfg;
+extern struct iwl_cfg iwl6050_2abg_cfg;
+extern struct iwl_cfg iwl6150_bgn_cfg;
+extern struct iwl_cfg iwl6150_bg_cfg;
+extern struct iwl_cfg iwl1000_bgn_cfg;
+extern struct iwl_cfg iwl1000_bg_cfg;
+extern struct iwl_cfg iwl100_bgn_cfg;
+extern struct iwl_cfg iwl100_bg_cfg;
+extern struct iwl_cfg iwl130_bgn_cfg;
+extern struct iwl_cfg iwl130_bg_cfg;
+extern struct iwl_cfg iwl2000_2bgn_cfg;
+extern struct iwl_cfg iwl2000_2bg_cfg;
+extern struct iwl_cfg iwl2000_2bgn_d_cfg;
+extern struct iwl_cfg iwl2030_2bgn_cfg;
+extern struct iwl_cfg iwl2030_2bg_cfg;
+extern struct iwl_cfg iwl6035_2agn_cfg;
+extern struct iwl_cfg iwl6035_2abg_cfg;
+extern struct iwl_cfg iwl6035_2bg_cfg;
+extern struct iwl_cfg iwl105_bg_cfg;
+extern struct iwl_cfg iwl105_bgn_cfg;
+extern struct iwl_cfg iwl105_bgn_d_cfg;
+extern struct iwl_cfg iwl135_bg_cfg;
+extern struct iwl_cfg iwl135_bgn_cfg;
+#endif /* __iwl_pci_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h
index 6ee5f1aa555c..69d5f85d11e2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-commands.h
@@ -69,6 +69,9 @@
#ifndef __iwl_commands_h__
#define __iwl_commands_h__
+#include <linux/etherdevice.h>
+#include <linux/ieee80211.h>
+
struct iwl_priv;
/* uCode version contains 4 values: Major/Minor/API/Serial */
@@ -86,6 +89,7 @@ struct iwl_priv;
enum {
REPLY_ALIVE = 0x1,
REPLY_ERROR = 0x2,
+ REPLY_ECHO = 0x3, /* test command */
/* RXON and QOS commands */
REPLY_RXON = 0x10,
@@ -188,6 +192,13 @@ enum {
REPLY_WIPAN_NOA_NOTIFICATION = 0xbc,
REPLY_WIPAN_DEACTIVATION_COMPLETE = 0xbd,
+ REPLY_WOWLAN_PATTERNS = 0xe0,
+ REPLY_WOWLAN_WAKEUP_FILTER = 0xe1,
+ REPLY_WOWLAN_TSC_RSC_PARAMS = 0xe2,
+ REPLY_WOWLAN_TKIP_PARAMS = 0xe3,
+ REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4,
+ REPLY_WOWLAN_GET_STATUS = 0xe5,
+
REPLY_MAX = 0xff
};
@@ -384,18 +395,6 @@ struct iwl_tx_ant_config_cmd {
#define UCODE_VALID_OK cpu_to_le32(0x1)
-enum iwlagn_ucode_subtype {
- UCODE_SUBTYPE_REGULAR = 0,
- UCODE_SUBTYPE_REGULAR_NEW = 1,
- UCODE_SUBTYPE_INIT = 9,
-
- /*
- * Not a valid subtype, the ucode has just a u8, so
- * we can use something > 0xff for this value.
- */
- UCODE_SUBTYPE_NONE_LOADED = 0x100,
-};
-
/**
* REPLY_ALIVE = 0x1 (response only, not a command)
*
@@ -675,7 +674,6 @@ struct iwl_rxon_assoc_cmd {
#define IWL_CONN_MAX_LISTEN_INTERVAL 10
#define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */
-#define IWL39_MAX_UCODE_BEACON_INTERVAL 1 /* 1024 */
/*
* REPLY_RXON_TIMING = 0x14 (command, has simple generic response)
@@ -811,6 +809,7 @@ struct iwl_qosparam_cmd {
#define IWLAGN_STATION_COUNT 16
#define IWL_INVALID_STATION 255
+#define IWL_MAX_TID_COUNT 9
#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2)
#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8)
@@ -844,6 +843,8 @@ struct iwl_qosparam_cmd {
#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000)
#define STA_KEY_MAX_NUM 8
#define STA_KEY_MAX_NUM_PAN 16
+/* must not match WEP_INVALID_OFFSET */
+#define IWLAGN_HW_KEY_DEFAULT 0xfe
/* Flags indicate whether to modify vs. don't change various station params */
#define STA_MODIFY_KEY_MASK 0x01
@@ -984,15 +985,26 @@ struct iwl_rem_sta_cmd {
u8 reserved2[2];
} __packed;
-#define IWL_TX_FIFO_BK_MSK cpu_to_le32(BIT(0))
-#define IWL_TX_FIFO_BE_MSK cpu_to_le32(BIT(1))
-#define IWL_TX_FIFO_VI_MSK cpu_to_le32(BIT(2))
-#define IWL_TX_FIFO_VO_MSK cpu_to_le32(BIT(3))
+
+/* WiFi queues mask */
+#define IWL_SCD_BK_MSK cpu_to_le32(BIT(0))
+#define IWL_SCD_BE_MSK cpu_to_le32(BIT(1))
+#define IWL_SCD_VI_MSK cpu_to_le32(BIT(2))
+#define IWL_SCD_VO_MSK cpu_to_le32(BIT(3))
+#define IWL_SCD_MGMT_MSK cpu_to_le32(BIT(3))
+
+/* PAN queues mask */
+#define IWL_PAN_SCD_BK_MSK cpu_to_le32(BIT(4))
+#define IWL_PAN_SCD_BE_MSK cpu_to_le32(BIT(5))
+#define IWL_PAN_SCD_VI_MSK cpu_to_le32(BIT(6))
+#define IWL_PAN_SCD_VO_MSK cpu_to_le32(BIT(7))
+#define IWL_PAN_SCD_MGMT_MSK cpu_to_le32(BIT(7))
+#define IWL_PAN_SCD_MULTICAST_MSK cpu_to_le32(BIT(8))
+
#define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00)
#define IWL_DROP_SINGLE 0
-#define IWL_DROP_SELECTED 1
-#define IWL_DROP_ALL 2
+#define IWL_DROP_ALL (BIT(IWL_RXON_CTX_BSS) | BIT(IWL_RXON_CTX_PAN))
/*
* REPLY_TXFIFO_FLUSH = 0x1e(command and response)
@@ -1932,6 +1944,9 @@ struct iwl_bt_cmd {
/* Disable Sync PSPoll on SCO/eSCO */
#define IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE BIT(7)
+#define IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD -75 /* dBm */
+#define IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD -65 /* dBm */
+
#define IWLAGN_BT_PRIO_BOOST_MAX 0xFF
#define IWLAGN_BT_PRIO_BOOST_MIN 0x00
#define IWLAGN_BT_PRIO_BOOST_DEFAULT 0xF0
@@ -2457,8 +2472,8 @@ struct iwl_scanstart_notification {
__le32 status;
} __packed;
-#define SCAN_OWNER_STATUS 0x1;
-#define MEASURE_OWNER_STATUS 0x2;
+#define SCAN_OWNER_STATUS 0x1
+#define MEASURE_OWNER_STATUS 0x2
#define IWL_PROBE_STATUS_OK 0
#define IWL_PROBE_STATUS_TX_FAILED BIT(0)
@@ -3056,17 +3071,29 @@ struct iwl_missed_beacon_notif {
/* number of additional entries for enhanced tbl */
#define ENHANCE_HD_TABLE_ENTRIES (ENHANCE_HD_TABLE_SIZE - HD_TABLE_SIZE)
-#define HD_INA_NON_SQUARE_DET_OFDM_DATA cpu_to_le16(0)
-#define HD_INA_NON_SQUARE_DET_CCK_DATA cpu_to_le16(0)
-#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA cpu_to_le16(0)
-#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA cpu_to_le16(668)
-#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA cpu_to_le16(4)
-#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA cpu_to_le16(486)
-#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA cpu_to_le16(37)
-#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA cpu_to_le16(853)
-#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA cpu_to_le16(4)
-#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA cpu_to_le16(476)
-#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA cpu_to_le16(99)
+#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V1 cpu_to_le16(0)
+#define HD_INA_NON_SQUARE_DET_CCK_DATA_V1 cpu_to_le16(0)
+#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1 cpu_to_le16(0)
+#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(668)
+#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4)
+#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(486)
+#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(37)
+#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(853)
+#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4)
+#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(476)
+#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(99)
+
+#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V2 cpu_to_le16(1)
+#define HD_INA_NON_SQUARE_DET_CCK_DATA_V2 cpu_to_le16(1)
+#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2 cpu_to_le16(1)
+#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(600)
+#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(40)
+#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(486)
+#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(45)
+#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(853)
+#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(60)
+#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(476)
+#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(99)
/* Control field in struct iwl_sensitivity_cmd */
@@ -3153,7 +3180,6 @@ struct iwl_enhance_sensitivity_cmd {
/* The default calibrate table size if not specified by firmware */
#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18
enum {
- IWL_PHY_CALIBRATE_DIFF_GAIN_CMD = 7,
IWL_PHY_CALIBRATE_DC_CMD = 8,
IWL_PHY_CALIBRATE_LO_CMD = 9,
IWL_PHY_CALIBRATE_TX_IQ_CMD = 11,
@@ -3166,22 +3192,41 @@ enum {
#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE (253)
-#define IWL_CALIB_INIT_CFG_ALL cpu_to_le32(0xffffffff)
-
/* This enum defines the bitmap of various calibrations to enable in both
* init ucode and runtime ucode through CALIBRATION_CFG_CMD.
*/
enum iwl_ucode_calib_cfg {
- IWL_CALIB_CFG_RX_BB_IDX,
- IWL_CALIB_CFG_DC_IDX,
- IWL_CALIB_CFG_TX_IQ_IDX,
- IWL_CALIB_CFG_RX_IQ_IDX,
- IWL_CALIB_CFG_NOISE_IDX,
- IWL_CALIB_CFG_CRYSTAL_IDX,
- IWL_CALIB_CFG_TEMPERATURE_IDX,
- IWL_CALIB_CFG_PAPD_IDX,
+ IWL_CALIB_CFG_RX_BB_IDX = BIT(0),
+ IWL_CALIB_CFG_DC_IDX = BIT(1),
+ IWL_CALIB_CFG_LO_IDX = BIT(2),
+ IWL_CALIB_CFG_TX_IQ_IDX = BIT(3),
+ IWL_CALIB_CFG_RX_IQ_IDX = BIT(4),
+ IWL_CALIB_CFG_NOISE_IDX = BIT(5),
+ IWL_CALIB_CFG_CRYSTAL_IDX = BIT(6),
+ IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(7),
+ IWL_CALIB_CFG_PAPD_IDX = BIT(8),
+ IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(9),
+ IWL_CALIB_CFG_TX_PWR_IDX = BIT(10),
};
+#define IWL_CALIB_INIT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \
+ IWL_CALIB_CFG_DC_IDX | \
+ IWL_CALIB_CFG_LO_IDX | \
+ IWL_CALIB_CFG_TX_IQ_IDX | \
+ IWL_CALIB_CFG_RX_IQ_IDX | \
+ IWL_CALIB_CFG_CRYSTAL_IDX)
+
+#define IWL_CALIB_RT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \
+ IWL_CALIB_CFG_DC_IDX | \
+ IWL_CALIB_CFG_LO_IDX | \
+ IWL_CALIB_CFG_TX_IQ_IDX | \
+ IWL_CALIB_CFG_RX_IQ_IDX | \
+ IWL_CALIB_CFG_TEMPERATURE_IDX | \
+ IWL_CALIB_CFG_PAPD_IDX | \
+ IWL_CALIB_CFG_TX_PWR_IDX | \
+ IWL_CALIB_CFG_CRYSTAL_IDX)
+
+#define IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK cpu_to_le32(BIT(0))
struct iwl_calib_cfg_elmnt_s {
__le32 is_enable;
@@ -3215,15 +3260,6 @@ struct iwl_calib_cmd {
u8 data[0];
} __packed;
-/* IWL_PHY_CALIBRATE_DIFF_GAIN_CMD (7) */
-struct iwl_calib_diff_gain_cmd {
- struct iwl_calib_hdr hdr;
- s8 diff_gain_a; /* see above */
- s8 diff_gain_b;
- s8 diff_gain_c;
- u8 reserved1;
-} __packed;
-
struct iwl_calib_xtal_freq_cmd {
struct iwl_calib_hdr hdr;
u8 cap_pin1;
@@ -3231,11 +3267,19 @@ struct iwl_calib_xtal_freq_cmd {
u8 pad[2];
} __packed;
-#define DEFAULT_RADIO_SENSOR_OFFSET 2700
+#define DEFAULT_RADIO_SENSOR_OFFSET cpu_to_le16(2700)
struct iwl_calib_temperature_offset_cmd {
struct iwl_calib_hdr hdr;
- s16 radio_sensor_offset;
- s16 reserved;
+ __le16 radio_sensor_offset;
+ __le16 reserved;
+} __packed;
+
+struct iwl_calib_temperature_offset_v2_cmd {
+ struct iwl_calib_hdr hdr;
+ __le16 radio_sensor_offset_high;
+ __le16 radio_sensor_offset_low;
+ __le16 burntVoltageRef;
+ __le16 reserved;
} __packed;
/* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */
@@ -3756,11 +3800,133 @@ struct iwl_bt_coex_prot_env_cmd {
u8 reserved[2];
} __attribute__((packed));
+/*
+ * REPLY_WOWLAN_PATTERNS
+ */
+#define IWLAGN_WOWLAN_MIN_PATTERN_LEN 16
+#define IWLAGN_WOWLAN_MAX_PATTERN_LEN 128
+
+struct iwlagn_wowlan_pattern {
+ u8 mask[IWLAGN_WOWLAN_MAX_PATTERN_LEN / 8];
+ u8 pattern[IWLAGN_WOWLAN_MAX_PATTERN_LEN];
+ u8 mask_size;
+ u8 pattern_size;
+ __le16 reserved;
+} __packed;
+
+#define IWLAGN_WOWLAN_MAX_PATTERNS 20
+
+struct iwlagn_wowlan_patterns_cmd {
+ __le32 n_patterns;
+ struct iwlagn_wowlan_pattern patterns[];
+} __packed;
+
+/*
+ * REPLY_WOWLAN_WAKEUP_FILTER
+ */
+enum iwlagn_wowlan_wakeup_filters {
+ IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0),
+ IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1),
+ IWLAGN_WOWLAN_WAKEUP_BEACON_MISS = BIT(2),
+ IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3),
+ IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4),
+ IWLAGN_WOWLAN_WAKEUP_RFKILL = BIT(5),
+ IWLAGN_WOWLAN_WAKEUP_UCODE_ERROR = BIT(6),
+ IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(7),
+ IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(8),
+ IWLAGN_WOWLAN_WAKEUP_ALWAYS = BIT(9),
+ IWLAGN_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(10),
+};
+
+struct iwlagn_wowlan_wakeup_filter_cmd {
+ __le32 enabled;
+ __le16 non_qos_seq;
+ u8 min_sleep_seconds;
+ u8 reserved;
+ __le16 qos_seq[8];
+};
+
+/*
+ * REPLY_WOWLAN_TSC_RSC_PARAMS
+ */
+#define IWLAGN_NUM_RSC 16
+
+struct tkip_sc {
+ __le16 iv16;
+ __le16 pad;
+ __le32 iv32;
+} __packed;
+
+struct iwlagn_tkip_rsc_tsc {
+ struct tkip_sc unicast_rsc[IWLAGN_NUM_RSC];
+ struct tkip_sc multicast_rsc[IWLAGN_NUM_RSC];
+ struct tkip_sc tsc;
+} __packed;
+
+struct aes_sc {
+ __le64 pn;
+} __packed;
+
+struct iwlagn_aes_rsc_tsc {
+ struct aes_sc unicast_rsc[IWLAGN_NUM_RSC];
+ struct aes_sc multicast_rsc[IWLAGN_NUM_RSC];
+ struct aes_sc tsc;
+} __packed;
+
+union iwlagn_all_tsc_rsc {
+ struct iwlagn_tkip_rsc_tsc tkip;
+ struct iwlagn_aes_rsc_tsc aes;
+};
+
+struct iwlagn_wowlan_rsc_tsc_params_cmd {
+ union iwlagn_all_tsc_rsc all_tsc_rsc;
+} __packed;
+
+/*
+ * REPLY_WOWLAN_TKIP_PARAMS
+ */
+#define IWLAGN_MIC_KEY_SIZE 8
+#define IWLAGN_P1K_SIZE 5
+struct iwlagn_mic_keys {
+ u8 tx[IWLAGN_MIC_KEY_SIZE];
+ u8 rx_unicast[IWLAGN_MIC_KEY_SIZE];
+ u8 rx_mcast[IWLAGN_MIC_KEY_SIZE];
+} __packed;
+
+struct iwlagn_p1k_cache {
+ __le16 p1k[IWLAGN_P1K_SIZE];
+} __packed;
+
+#define IWLAGN_NUM_RX_P1K_CACHE 2
+
+struct iwlagn_wowlan_tkip_params_cmd {
+ struct iwlagn_mic_keys mic_keys;
+ struct iwlagn_p1k_cache tx;
+ struct iwlagn_p1k_cache rx_uni[IWLAGN_NUM_RX_P1K_CACHE];
+ struct iwlagn_p1k_cache rx_multi[IWLAGN_NUM_RX_P1K_CACHE];
+} __packed;
+
+/*
+ * REPLY_WOWLAN_KEK_KCK_MATERIAL
+ */
+
+#define IWLAGN_KCK_MAX_SIZE 32
+#define IWLAGN_KEK_MAX_SIZE 32
+
+struct iwlagn_wowlan_kek_kck_material_cmd {
+ u8 kck[IWLAGN_KCK_MAX_SIZE];
+ u8 kek[IWLAGN_KEK_MAX_SIZE];
+ __le16 kck_len;
+ __le16 kek_len;
+ __le64 replay_ctr;
+} __packed;
+
/******************************************************************************
* (13)
* Union of all expected notifications/responses:
*
*****************************************************************************/
+#define FH_RSCSR_FRAME_SIZE_MSK (0x00003FFF) /* bits 0-13 */
struct iwl_rx_packet {
/*
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 45cc51c9c93e..001fdf140abb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -34,49 +34,26 @@
#include <net/mac80211.h>
#include "iwl-eeprom.h"
-#include "iwl-dev.h" /* FIXME: remove */
#include "iwl-debug.h"
#include "iwl-core.h"
#include "iwl-io.h"
#include "iwl-power.h"
-#include "iwl-sta.h"
-#include "iwl-helpers.h"
#include "iwl-agn.h"
-
-
-/*
- * set bt_coex_active to true, uCode will do kill/defer
- * every time the priority line is asserted (BT is sending signals on the
- * priority line in the PCIx).
- * set bt_coex_active to false, uCode will ignore the BT activity and
- * perform the normal operation
- *
- * User might experience transmit issue on some platform due to WiFi/BT
- * co-exist problem. The possible behaviors are:
- * Able to scan and finding all the available AP
- * Not able to associate with any AP
- * On those platforms, WiFi communication can be restored by set
- * "bt_coex_active" module parameter to "false"
- *
- * default: bt_coex_active = true (BT_COEX_ENABLE)
- */
-bool bt_coex_active = true;
-module_param(bt_coex_active, bool, S_IRUGO);
-MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist");
-
-u32 iwl_debug_level;
+#include "iwl-shared.h"
+#include "iwl-agn.h"
+#include "iwl-trans.h"
const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */
#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */
-static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
+static void iwl_init_ht_hw_capab(const struct iwl_priv *priv,
struct ieee80211_sta_ht_cap *ht_info,
enum ieee80211_band band)
{
u16 max_bit_rate = 0;
- u8 rx_chains_num = priv->hw_params.rx_chains_num;
- u8 tx_chains_num = priv->hw_params.tx_chains_num;
+ u8 rx_chains_num = hw_params(priv).rx_chains_num;
+ u8 tx_chains_num = hw_params(priv).tx_chains_num;
ht_info->cap = 0;
memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
@@ -88,7 +65,7 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
max_bit_rate = MAX_BIT_RATE_20_MHZ;
- if (priv->hw_params.ht40_channel & BIT(band)) {
+ if (hw_params(priv).ht40_channel & BIT(band)) {
ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
ht_info->mcs.rx_mask[4] = 0x01;
@@ -126,9 +103,9 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
}
/**
- * iwlcore_init_geos - Initialize mac80211's geo/channel info based from eeprom
+ * iwl_init_geos - Initialize mac80211's geo/channel info based from eeprom
*/
-int iwlcore_init_geos(struct iwl_priv *priv)
+int iwl_init_geos(struct iwl_priv *priv)
{
struct iwl_channel_info *ch;
struct ieee80211_supported_band *sband;
@@ -141,16 +118,16 @@ int iwlcore_init_geos(struct iwl_priv *priv)
if (priv->bands[IEEE80211_BAND_2GHZ].n_bitrates ||
priv->bands[IEEE80211_BAND_5GHZ].n_bitrates) {
IWL_DEBUG_INFO(priv, "Geography modes already initialized.\n");
- set_bit(STATUS_GEO_CONFIGURED, &priv->status);
+ set_bit(STATUS_GEO_CONFIGURED, &priv->shrd->status);
return 0;
}
- channels = kzalloc(sizeof(struct ieee80211_channel) *
- priv->channel_count, GFP_KERNEL);
+ channels = kcalloc(priv->channel_count,
+ sizeof(struct ieee80211_channel), GFP_KERNEL);
if (!channels)
return -ENOMEM;
- rates = kzalloc((sizeof(struct ieee80211_rate) * IWL_RATE_COUNT_LEGACY),
+ rates = kcalloc(IWL_RATE_COUNT_LEGACY, sizeof(struct ieee80211_rate),
GFP_KERNEL);
if (!rates) {
kfree(channels);
@@ -164,8 +141,8 @@ int iwlcore_init_geos(struct iwl_priv *priv)
sband->bitrates = &rates[IWL_FIRST_OFDM_RATE];
sband->n_bitrates = IWL_RATE_COUNT_LEGACY - IWL_FIRST_OFDM_RATE;
- if (priv->cfg->sku & IWL_SKU_N)
- iwlcore_init_ht_hw_capab(priv, &sband->ht_cap,
+ if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE)
+ iwl_init_ht_hw_capab(priv, &sband->ht_cap,
IEEE80211_BAND_5GHZ);
sband = &priv->bands[IEEE80211_BAND_2GHZ];
@@ -174,8 +151,8 @@ int iwlcore_init_geos(struct iwl_priv *priv)
sband->bitrates = rates;
sband->n_bitrates = IWL_RATE_COUNT_LEGACY;
- if (priv->cfg->sku & IWL_SKU_N)
- iwlcore_init_ht_hw_capab(priv, &sband->ht_cap,
+ if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE)
+ iwl_init_ht_hw_capab(priv, &sband->ht_cap,
IEEE80211_BAND_2GHZ);
priv->ieee_channels = channels;
@@ -229,31 +206,31 @@ int iwlcore_init_geos(struct iwl_priv *priv)
priv->tx_power_next = max_tx_power;
if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) &&
- priv->cfg->sku & IWL_SKU_A) {
+ priv->cfg->sku & EEPROM_SKU_CAP_BAND_52GHZ) {
+ char buf[32];
+ bus_get_hw_id(bus(priv), buf, sizeof(buf));
IWL_INFO(priv, "Incorrectly detected BG card as ABG. "
- "Please send your PCI ID 0x%04X:0x%04X to maintainer.\n",
- priv->pci_dev->device,
- priv->pci_dev->subsystem_device);
- priv->cfg->sku &= ~IWL_SKU_A;
+ "Please send your %s to maintainer.\n", buf);
+ priv->cfg->sku &= ~EEPROM_SKU_CAP_BAND_52GHZ;
}
IWL_INFO(priv, "Tunable channels: %d 802.11bg, %d 802.11a channels\n",
priv->bands[IEEE80211_BAND_2GHZ].n_channels,
priv->bands[IEEE80211_BAND_5GHZ].n_channels);
- set_bit(STATUS_GEO_CONFIGURED, &priv->status);
+ set_bit(STATUS_GEO_CONFIGURED, &priv->shrd->status);
return 0;
}
/*
- * iwlcore_free_geos - undo allocations in iwlcore_init_geos
+ * iwl_free_geos - undo allocations in iwl_init_geos
*/
-void iwlcore_free_geos(struct iwl_priv *priv)
+void iwl_free_geos(struct iwl_priv *priv)
{
kfree(priv->ieee_channels);
kfree(priv->ieee_rates);
- clear_bit(STATUS_GEO_CONFIGURED, &priv->status);
+ clear_bit(STATUS_GEO_CONFIGURED, &priv->shrd->status);
}
static bool iwl_is_channel_extension(struct iwl_priv *priv,
@@ -343,9 +320,9 @@ int iwl_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
u16 beacon_int;
struct ieee80211_vif *vif = ctx->vif;
- conf = ieee80211_get_hw_conf(priv->hw);
+ conf = &priv->hw->conf;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd));
@@ -379,10 +356,12 @@ int iwl_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
} else {
beacon_int = iwl_adjust_beacon_interval(beacon_int,
- priv->hw_params.max_beacon_itrvl * TIME_UNIT);
+ IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT);
ctx->timing.beacon_interval = cpu_to_le16(beacon_int);
}
+ ctx->beacon_int = beacon_int;
+
tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */
interval_tm = beacon_int * TIME_UNIT;
rem = do_div(tsf, interval_tm);
@@ -396,8 +375,8 @@ int iwl_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
le32_to_cpu(ctx->timing.beacon_init_val),
le16_to_cpu(ctx->timing.atim_window));
- return iwl_send_cmd_pdu(priv, ctx->rxon_timing_cmd,
- sizeof(ctx->timing), &ctx->timing);
+ return iwl_trans_send_cmd_pdu(trans(priv), ctx->rxon_timing_cmd,
+ CMD_SYNC, sizeof(ctx->timing), &ctx->timing);
}
void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
@@ -547,19 +526,6 @@ int iwl_full_rxon_required(struct iwl_priv *priv,
return 0;
}
-u8 iwl_rate_get_lowest_plcp(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- /*
- * Assign the lowest rate -- should really get this from
- * the beacon skb from mac80211.
- */
- if (ctx->staging.flags & RXON_FLG_BAND_24G_MSK)
- return IWL_RATE_1M_PLCP;
- else
- return IWL_RATE_6M_PLCP;
-}
-
static void _iwl_set_rxon_ht(struct iwl_priv *priv,
struct iwl_ht_config *ht_conf,
struct iwl_rxon_context *ctx)
@@ -619,8 +585,7 @@ static void _iwl_set_rxon_ht(struct iwl_priv *priv,
rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
}
- if (priv->cfg->ops->hcmd->set_rxon_chain)
- priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
+ iwlagn_set_rxon_chain(priv, ctx);
IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X "
"extension channel offset 0x%x\n",
@@ -836,21 +801,23 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success)
{
/*
* MULTI-FIXME
- * See iwl_mac_channel_switch.
+ * See iwlagn_mac_channel_switch.
*/
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
- if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
+ if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING,
+ &priv->shrd->status))
ieee80211_chswitch_done(ctx->vif, is_success);
}
#ifdef CONFIG_IWLWIFI_DEBUG
void iwl_print_rx_config_cmd(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
+ enum iwl_rxon_context_id ctxid)
{
+ struct iwl_rxon_context *ctx = &priv->contexts[ctxid];
struct iwl_rxon_cmd *rxon = &ctx->staging;
IWL_DEBUG_RADIO(priv, "RX CONFIG:\n");
@@ -874,12 +841,12 @@ static void iwlagn_abort_notification_waits(struct iwl_priv *priv)
unsigned long flags;
struct iwl_notification_wait *wait_entry;
- spin_lock_irqsave(&priv->_agn.notif_wait_lock, flags);
- list_for_each_entry(wait_entry, &priv->_agn.notif_waits, list)
+ spin_lock_irqsave(&priv->notif_wait_lock, flags);
+ list_for_each_entry(wait_entry, &priv->notif_waits, list)
wait_entry->aborted = true;
- spin_unlock_irqrestore(&priv->_agn.notif_wait_lock, flags);
+ spin_unlock_irqrestore(&priv->notif_wait_lock, flags);
- wake_up_all(&priv->_agn.notif_waitq);
+ wake_up_all(&priv->notif_waitq);
}
void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
@@ -888,18 +855,18 @@ void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
unsigned long reload_jiffies;
/* Set the FW error flag -- cleared on iwl_down */
- set_bit(STATUS_FW_ERROR, &priv->status);
+ set_bit(STATUS_FW_ERROR, &priv->shrd->status);
/* Cancel currently queued command. */
- clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
+ clear_bit(STATUS_HCMD_ACTIVE, &priv->shrd->status);
iwlagn_abort_notification_waits(priv);
/* Keep the restart process from trying to send host
* commands by clearing the ready bit */
- clear_bit(STATUS_READY, &priv->status);
+ clear_bit(STATUS_READY, &priv->shrd->status);
- wake_up_interruptible(&priv->wait_command_queue);
+ wake_up(&priv->shrd->wait_command_queue);
if (!ondemand) {
/*
@@ -922,63 +889,26 @@ void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
priv->reload_count = 0;
}
- if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) {
+ if (!test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) {
if (iwlagn_mod_params.restart_fw) {
- IWL_DEBUG(priv, IWL_DL_FW_ERRORS,
+ IWL_DEBUG_FW_ERRORS(priv,
"Restarting adapter due to uCode error.\n");
- queue_work(priv->workqueue, &priv->restart);
+ queue_work(priv->shrd->workqueue, &priv->restart);
} else
- IWL_DEBUG(priv, IWL_DL_FW_ERRORS,
+ IWL_DEBUG_FW_ERRORS(priv,
"Detected FW error, but not restarting\n");
}
}
-/**
- * iwl_irq_handle_error - called for HW or SW error interrupt from card
- */
-void iwl_irq_handle_error(struct iwl_priv *priv)
-{
- /* W/A for WiFi/WiMAX coex and WiMAX own the RF */
- if (priv->cfg->internal_wimax_coex &&
- (!(iwl_read_prph(priv, APMG_CLK_CTRL_REG) &
- APMS_CLK_VAL_MRB_FUNC_MODE) ||
- (iwl_read_prph(priv, APMG_PS_CTRL_REG) &
- APMG_PS_CTRL_VAL_RESET_REQ))) {
- /*
- * Keep the restart process from trying to send host
- * commands by clearing the ready bit.
- */
- clear_bit(STATUS_READY, &priv->status);
- clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
- wake_up_interruptible(&priv->wait_command_queue);
- IWL_ERR(priv, "RF is used by WiMAX\n");
- return;
- }
-
- IWL_ERR(priv, "Loaded firmware version: %s\n",
- priv->hw->wiphy->fw_version);
-
- iwl_dump_nic_error_log(priv);
- iwl_dump_csr(priv);
- iwl_dump_fh(priv, NULL, false);
- iwl_dump_nic_event_log(priv, false, NULL, false);
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS)
- iwl_print_rx_config_cmd(priv,
- &priv->contexts[IWL_RXON_CTX_BSS]);
-#endif
-
- iwlagn_fw_error(priv, false);
-}
-
static int iwl_apm_stop_master(struct iwl_priv *priv)
{
int ret = 0;
/* stop device's busmaster DMA activity */
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
+ iwl_set_bit(bus(priv), CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
- ret = iwl_poll_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED,
+ ret = iwl_poll_bit(bus(priv), CSR_RESET,
+ CSR_RESET_REG_FLAG_MASTER_DISABLED,
CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
if (ret)
IWL_WARN(priv, "Master Disable Timed Out, 100 usec\n");
@@ -992,13 +922,13 @@ void iwl_apm_stop(struct iwl_priv *priv)
{
IWL_DEBUG_INFO(priv, "Stop card, put in low power state\n");
- clear_bit(STATUS_DEVICE_ENABLED, &priv->status);
+ clear_bit(STATUS_DEVICE_ENABLED, &priv->shrd->status);
/* Stop device's DMA activity */
iwl_apm_stop_master(priv);
/* Reset the entire device */
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+ iwl_set_bit(bus(priv), CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
udelay(10);
@@ -1006,7 +936,7 @@ void iwl_apm_stop(struct iwl_priv *priv)
* Clear "initialization complete" bit to move adapter from
* D0A* (powered-up Active) --> D0U* (Uninitialized) state.
*/
- iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+ iwl_clear_bit(bus(priv), CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
}
@@ -1018,8 +948,6 @@ void iwl_apm_stop(struct iwl_priv *priv)
int iwl_apm_init(struct iwl_priv *priv)
{
int ret = 0;
- u16 lctl;
-
IWL_DEBUG_INFO(priv, "Init card's basic functions\n");
/*
@@ -1028,65 +956,45 @@ int iwl_apm_init(struct iwl_priv *priv)
*/
/* Disable L0S exit timer (platform NMI Work/Around) */
- iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
+ iwl_set_bit(bus(priv), CSR_GIO_CHICKEN_BITS,
CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
/*
* Disable L0s without affecting L1;
* don't wait for ICH L0s (ICH bug W/A)
*/
- iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
+ iwl_set_bit(bus(priv), CSR_GIO_CHICKEN_BITS,
CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
/* Set FH wait threshold to maximum (HW error during stress W/A) */
- iwl_set_bit(priv, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
+ iwl_set_bit(bus(priv), CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
/*
* Enable HAP INTA (interrupt from management bus) to
* wake device's PCI Express link L1a -> L0s
*/
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+ iwl_set_bit(bus(priv), CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
- /*
- * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition.
- * Check if BIOS (or OS) enabled L1-ASPM on this device.
- * If so (likely), disable L0S, so device moves directly L0->L1;
- * costs negligible amount of power savings.
- * If not (unlikely), enable L0S, so there is at least some
- * power savings, even without L1.
- */
- lctl = iwl_pcie_link_ctl(priv);
- if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) ==
- PCI_CFG_LINK_CTRL_VAL_L1_EN) {
- /* L1-ASPM enabled; disable(!) L0S */
- iwl_set_bit(priv, CSR_GIO_REG,
- CSR_GIO_REG_VAL_L0S_ENABLED);
- IWL_DEBUG_POWER(priv, "L1 Enabled; Disabling L0S\n");
- } else {
- /* L1-ASPM disabled; enable(!) L0S */
- iwl_clear_bit(priv, CSR_GIO_REG,
- CSR_GIO_REG_VAL_L0S_ENABLED);
- IWL_DEBUG_POWER(priv, "L1 Disabled; Enabling L0S\n");
- }
+ bus_apm_config(bus(priv));
/* Configure analog phase-lock-loop before activating to D0A */
if (priv->cfg->base_params->pll_cfg_val)
- iwl_set_bit(priv, CSR_ANA_PLL_CFG,
+ iwl_set_bit(bus(priv), CSR_ANA_PLL_CFG,
priv->cfg->base_params->pll_cfg_val);
/*
* Set "initialization complete" bit to move adapter from
* D0U* --> D0A* (powered-up active) state.
*/
- iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+ iwl_set_bit(bus(priv), CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
/*
* Wait for clock stabilization; once stabilized, access to
* device-internal resources is supported, e.g. iwl_write_prph()
* and accesses to uCode SRAM.
*/
- ret = iwl_poll_bit(priv, CSR_GP_CNTRL,
+ ret = iwl_poll_bit(bus(priv), CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
if (ret < 0) {
@@ -1101,14 +1009,14 @@ int iwl_apm_init(struct iwl_priv *priv)
* do not disable clocks. This preserves any hardware bits already
* set by default in "CLK_CTRL_REG" after reset.
*/
- iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
+ iwl_write_prph(bus(priv), APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
udelay(20);
/* Disable L1-Active */
- iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
+ iwl_set_bits_prph(bus(priv), APMG_PCIDEV_STT_REG,
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
- set_bit(STATUS_DEVICE_ENABLED, &priv->status);
+ set_bit(STATUS_DEVICE_ENABLED, &priv->shrd->status);
out:
return ret;
@@ -1122,14 +1030,11 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
bool defer;
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
if (priv->tx_power_user_lmt == tx_power && !force)
return 0;
- if (!priv->cfg->ops->lib->send_tx_power)
- return -EOPNOTSUPP;
-
if (tx_power < IWLAGN_TX_POWER_TARGET_POWER_MIN) {
IWL_WARN(priv,
"Requested user TXPOWER %d below lower limit %d.\n",
@@ -1145,7 +1050,7 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
return -EINVAL;
}
- if (!iwl_is_ready_rf(priv))
+ if (!iwl_is_ready_rf(priv->shrd))
return -EIO;
/* scan complete and commit_rxon use tx_power_next value,
@@ -1153,7 +1058,7 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
priv->tx_power_next = tx_power;
/* do not set tx power when scanning or channel changing */
- defer = test_bit(STATUS_SCANNING, &priv->status) ||
+ defer = test_bit(STATUS_SCANNING, &priv->shrd->status) ||
memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging));
if (defer && !force) {
IWL_DEBUG_INFO(priv, "Deferring tx power set\n");
@@ -1163,7 +1068,7 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
prev_tx_power = priv->tx_power_user_lmt;
priv->tx_power_user_lmt = tx_power;
- ret = priv->cfg->ops->lib->send_tx_power(priv);
+ ret = iwlagn_send_tx_power(priv);
/* if fail to set tx_power, restore the orig. tx power */
if (ret) {
@@ -1182,7 +1087,7 @@ void iwl_send_bt_config(struct iwl_priv *priv)
.kill_cts_mask = 0,
};
- if (!bt_coex_active)
+ if (!iwlagn_mod_params.bt_coex_active)
bt_cmd.flags = BT_COEX_DISABLE;
else
bt_cmd.flags = BT_COEX_ENABLE;
@@ -1191,8 +1096,8 @@ void iwl_send_bt_config(struct iwl_priv *priv)
IWL_DEBUG_INFO(priv, "BT coex %s\n",
(bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
- if (iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG,
- sizeof(struct iwl_bt_cmd), &bt_cmd))
+ if (iwl_trans_send_cmd_pdu(trans(priv), REPLY_BT_CONFIG,
+ CMD_SYNC, sizeof(struct iwl_bt_cmd), &bt_cmd))
IWL_ERR(priv, "failed to send BT Coex Config\n");
}
@@ -1204,22 +1109,20 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear)
};
if (flags & CMD_ASYNC)
- return iwl_send_cmd_pdu_async(priv, REPLY_STATISTICS_CMD,
+ return iwl_trans_send_cmd_pdu(trans(priv), REPLY_STATISTICS_CMD,
+ CMD_ASYNC,
sizeof(struct iwl_statistics_cmd),
- &statistics_cmd, NULL);
+ &statistics_cmd);
else
- return iwl_send_cmd_pdu(priv, REPLY_STATISTICS_CMD,
+ return iwl_trans_send_cmd_pdu(trans(priv), REPLY_STATISTICS_CMD,
+ CMD_SYNC,
sizeof(struct iwl_statistics_cmd),
&statistics_cmd);
}
-void iwl_clear_isr_stats(struct iwl_priv *priv)
-{
- memset(&priv->isr_stats, 0, sizeof(priv->isr_stats));
-}
-
-int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
- const struct ieee80211_tx_queue_params *params)
+int iwlagn_mac_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
{
struct iwl_priv *priv = hw->priv;
struct iwl_rxon_context *ctx;
@@ -1228,7 +1131,7 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
IWL_DEBUG_MAC80211(priv, "enter\n");
- if (!iwl_is_ready_rf(priv)) {
+ if (!iwl_is_ready_rf(priv->shrd)) {
IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n");
return -EIO;
}
@@ -1240,7 +1143,7 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
q = AC_NUM - 1 - queue;
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&priv->shrd->lock, flags);
/*
* MULTI-FIXME
@@ -1258,13 +1161,13 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0;
}
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->shrd->lock, flags);
IWL_DEBUG_MAC80211(priv, "leave\n");
return 0;
}
-int iwl_mac_tx_last_beacon(struct ieee80211_hw *hw)
+int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw)
{
struct iwl_priv *priv = hw->priv;
@@ -1275,10 +1178,9 @@ static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
{
iwl_connection_init_rx_config(priv, ctx);
- if (priv->cfg->ops->hcmd->set_rxon_chain)
- priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
+ iwlagn_set_rxon_chain(priv, ctx);
- return iwlcore_commit_rxon(priv, ctx);
+ return iwlagn_commit_rxon(priv, ctx);
}
static int iwl_setup_interface(struct iwl_priv *priv,
@@ -1287,7 +1189,7 @@ static int iwl_setup_interface(struct iwl_priv *priv,
struct ieee80211_vif *vif = ctx->vif;
int err;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
/*
* This variable will be correct only when there's just
@@ -1318,7 +1220,8 @@ static int iwl_setup_interface(struct iwl_priv *priv,
return 0;
}
-int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+int iwlagn_mac_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct iwl_priv *priv = hw->priv;
struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
@@ -1329,9 +1232,13 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n",
viftype, vif->addr);
- mutex_lock(&priv->mutex);
+ cancel_delayed_work_sync(&priv->hw_roc_disable_work);
+
+ mutex_lock(&priv->shrd->mutex);
+
+ iwlagn_disable_roc(priv);
- if (!iwl_is_ready_rf(priv)) {
+ if (!iwl_is_ready_rf(priv->shrd)) {
IWL_WARN(priv, "Try to add interface when device not ready\n");
err = -EINVAL;
goto out;
@@ -1374,7 +1281,7 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
ctx->vif = NULL;
priv->iw_mode = NL80211_IFTYPE_STATION;
out:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
IWL_DEBUG_MAC80211(priv, "leave\n");
return err;
@@ -1386,7 +1293,7 @@ static void iwl_teardown_interface(struct iwl_priv *priv,
{
struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
if (priv->scan_vif == vif) {
iwl_scan_cancel_timeout(priv, 200);
@@ -1410,7 +1317,7 @@ static void iwl_teardown_interface(struct iwl_priv *priv,
priv->bt_traffic_load = priv->last_bt_traffic_load;
}
-void iwl_mac_remove_interface(struct ieee80211_hw *hw,
+void iwlagn_mac_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct iwl_priv *priv = hw->priv;
@@ -1418,39 +1325,25 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
IWL_DEBUG_MAC80211(priv, "enter\n");
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
- WARN_ON(ctx->vif != vif);
+ if (WARN_ON(ctx->vif != vif)) {
+ struct iwl_rxon_context *tmp;
+ IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif);
+ for_each_context(priv, tmp)
+ IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n",
+ tmp->ctxid, tmp, tmp->vif);
+ }
ctx->vif = NULL;
iwl_teardown_interface(priv, vif, false);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
IWL_DEBUG_MAC80211(priv, "leave\n");
}
-int iwl_alloc_txq_mem(struct iwl_priv *priv)
-{
- if (!priv->txq)
- priv->txq = kzalloc(
- sizeof(struct iwl_tx_queue) *
- priv->cfg->base_params->num_of_queues,
- GFP_KERNEL);
- if (!priv->txq) {
- IWL_ERR(priv, "Not enough memory for txq\n");
- return -ENOMEM;
- }
- return 0;
-}
-
-void iwl_free_txq_mem(struct iwl_priv *priv)
-{
- kfree(priv->txq);
- priv->txq = NULL;
-}
-
#ifdef CONFIG_IWLWIFI_DEBUGFS
#define IWL_TRAFFIC_DUMP_SIZE (IWL_TRAFFIC_ENTRY_SIZE * IWL_TRAFFIC_ENTRIES)
@@ -1469,7 +1362,7 @@ int iwl_alloc_traffic_mem(struct iwl_priv *priv)
{
u32 traffic_size = IWL_TRAFFIC_DUMP_SIZE;
- if (iwl_debug_level & IWL_DL_TX) {
+ if (iwl_get_debug_level(priv->shrd) & IWL_DL_TX) {
if (!priv->tx_traffic) {
priv->tx_traffic =
kzalloc(traffic_size, GFP_KERNEL);
@@ -1477,7 +1370,7 @@ int iwl_alloc_traffic_mem(struct iwl_priv *priv)
return -ENOMEM;
}
}
- if (iwl_debug_level & IWL_DL_RX) {
+ if (iwl_get_debug_level(priv->shrd) & IWL_DL_RX) {
if (!priv->rx_traffic) {
priv->rx_traffic =
kzalloc(traffic_size, GFP_KERNEL);
@@ -1504,7 +1397,7 @@ void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv,
__le16 fc;
u16 len;
- if (likely(!(iwl_debug_level & IWL_DL_TX)))
+ if (likely(!(iwl_get_debug_level(priv->shrd) & IWL_DL_TX)))
return;
if (!priv->tx_traffic)
@@ -1528,7 +1421,7 @@ void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv,
__le16 fc;
u16 len;
- if (likely(!(iwl_debug_level & IWL_DL_RX)))
+ if (likely(!(iwl_get_debug_level(priv->shrd) & IWL_DL_RX)))
return;
if (!priv->rx_traffic)
@@ -1685,7 +1578,7 @@ void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len)
static void iwl_force_rf_reset(struct iwl_priv *priv)
{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return;
if (!iwl_is_any_associated(priv)) {
@@ -1710,7 +1603,7 @@ int iwl_force_reset(struct iwl_priv *priv, int mode, bool external)
{
struct iwl_force_reset *force_reset;
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return -EINVAL;
if (mode >= IWL_MAX_FORCE_RESET) {
@@ -1756,8 +1649,9 @@ int iwl_force_reset(struct iwl_priv *priv, int mode, bool external)
return 0;
}
-int iwl_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- enum nl80211_iftype newtype, bool newp2p)
+int iwlagn_mac_change_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype newtype, bool newp2p)
{
struct iwl_priv *priv = hw->priv;
struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
@@ -1767,11 +1661,13 @@ int iwl_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 interface_modes;
int err;
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
newtype = ieee80211_iftype_p2p(newtype, newp2p);
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
- if (!ctx->vif || !iwl_is_ready_rf(priv)) {
+ if (!ctx->vif || !iwl_is_ready_rf(priv->shrd)) {
/*
* Huh? But wait ... this can maybe happen when
* we're in the middle of a firmware restart!
@@ -1833,36 +1729,35 @@ int iwl_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
err = 0;
out:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
return err;
}
-/*
- * On every watchdog tick we check (latest) time stamp. If it does not
- * change during timeout period and queue is not empty we reset firmware.
- */
-static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt)
+int iwl_cmd_echo_test(struct iwl_priv *priv)
{
- struct iwl_tx_queue *txq = &priv->txq[cnt];
- struct iwl_queue *q = &txq->q;
- unsigned long timeout;
int ret;
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_ECHO,
+ .flags = CMD_SYNC,
+ };
- if (q->read_ptr == q->write_ptr) {
- txq->time_stamp = jiffies;
- return 0;
- }
-
- timeout = txq->time_stamp +
- msecs_to_jiffies(priv->cfg->base_params->wd_timeout);
+ ret = iwl_trans_send_cmd(trans(priv), &cmd);
+ if (ret)
+ IWL_ERR(priv, "echo testing fail: 0X%x\n", ret);
+ else
+ IWL_DEBUG_INFO(priv, "echo testing pass\n");
+ return ret;
+}
- if (time_after(jiffies, timeout)) {
- IWL_ERR(priv, "Queue %d stuck for %u ms.\n",
- q->id, priv->cfg->base_params->wd_timeout);
+static inline int iwl_check_stuck_queue(struct iwl_priv *priv, int txq)
+{
+ if (iwl_trans_check_stuck_queue(trans(priv), txq)) {
+ int ret;
ret = iwl_force_reset(priv, IWL_FW_RESET, false);
return (ret == -EAGAIN) ? 0 : 1;
}
-
return 0;
}
@@ -1882,7 +1777,10 @@ void iwl_bg_watchdog(unsigned long data)
int cnt;
unsigned long timeout;
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
+ return;
+
+ if (iwl_is_rfkill(priv->shrd))
return;
timeout = priv->cfg->base_params->wd_timeout;
@@ -1890,14 +1788,14 @@ void iwl_bg_watchdog(unsigned long data)
return;
/* monitor and check for stuck cmd queue */
- if (iwl_check_stuck_queue(priv, priv->cmd_queue))
+ if (iwl_check_stuck_queue(priv, priv->shrd->cmd_queue))
return;
/* monitor and check for other stuck queues */
if (iwl_is_any_associated(priv)) {
- for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
+ for (cnt = 0; cnt < hw_params(priv).max_txq_num; cnt++) {
/* skip as we already checked the command queue */
- if (cnt == priv->cmd_queue)
+ if (cnt == priv->shrd->cmd_queue)
continue;
if (iwl_check_stuck_queue(priv, cnt))
return;
@@ -1912,13 +1810,35 @@ void iwl_setup_watchdog(struct iwl_priv *priv)
{
unsigned int timeout = priv->cfg->base_params->wd_timeout;
- if (timeout)
+ if (timeout && !iwlagn_mod_params.wd_disable)
mod_timer(&priv->watchdog,
jiffies + msecs_to_jiffies(IWL_WD_TICK(timeout)));
else
del_timer(&priv->watchdog);
}
+/**
+ * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time
+ * @priv -- pointer to iwl_priv data structure
+ * @tsf_bits -- number of bits need to shift for masking)
+ */
+static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv,
+ u16 tsf_bits)
+{
+ return (1 << tsf_bits) - 1;
+}
+
+/**
+ * iwl_beacon_time_mask_high - mask of higher 32 bit of beacon time
+ * @priv -- pointer to iwl_priv data structure
+ * @tsf_bits -- number of bits need to shift for masking)
+ */
+static inline u32 iwl_beacon_time_mask_high(struct iwl_priv *priv,
+ u16 tsf_bits)
+{
+ return ((1 << (32 - tsf_bits)) - 1) << tsf_bits;
+}
+
/*
* extended beacon time format
* time in usec will be changed into a 32-bit value in extended:internal format
@@ -1935,13 +1855,12 @@ u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, u32 beacon_interval)
return 0;
quot = (usec / interval) &
- (iwl_beacon_time_mask_high(priv,
- priv->hw_params.beacon_time_tsf_bits) >>
- priv->hw_params.beacon_time_tsf_bits);
+ (iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS) >>
+ IWLAGN_EXT_BEACON_TIME_POS);
rem = (usec % interval) & iwl_beacon_time_mask_low(priv,
- priv->hw_params.beacon_time_tsf_bits);
+ IWLAGN_EXT_BEACON_TIME_POS);
- return (quot << priv->hw_params.beacon_time_tsf_bits) + rem;
+ return (quot << IWLAGN_EXT_BEACON_TIME_POS) + rem;
}
/* base is usually what we get from ucode with each received frame,
@@ -1951,80 +1870,80 @@ __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base,
u32 addon, u32 beacon_interval)
{
u32 base_low = base & iwl_beacon_time_mask_low(priv,
- priv->hw_params.beacon_time_tsf_bits);
+ IWLAGN_EXT_BEACON_TIME_POS);
u32 addon_low = addon & iwl_beacon_time_mask_low(priv,
- priv->hw_params.beacon_time_tsf_bits);
+ IWLAGN_EXT_BEACON_TIME_POS);
u32 interval = beacon_interval * TIME_UNIT;
u32 res = (base & iwl_beacon_time_mask_high(priv,
- priv->hw_params.beacon_time_tsf_bits)) +
+ IWLAGN_EXT_BEACON_TIME_POS)) +
(addon & iwl_beacon_time_mask_high(priv,
- priv->hw_params.beacon_time_tsf_bits));
+ IWLAGN_EXT_BEACON_TIME_POS));
if (base_low > addon_low)
res += base_low - addon_low;
else if (base_low < addon_low) {
res += interval + base_low - addon_low;
- res += (1 << priv->hw_params.beacon_time_tsf_bits);
+ res += (1 << IWLAGN_EXT_BEACON_TIME_POS);
} else
- res += (1 << priv->hw_params.beacon_time_tsf_bits);
+ res += (1 << IWLAGN_EXT_BEACON_TIME_POS);
return cpu_to_le32(res);
}
-#ifdef CONFIG_PM
-
-int iwl_pci_suspend(struct device *device)
+void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv,
+ enum iwl_rxon_context_id ctx,
+ u8 sta_id, u8 tid)
{
- struct pci_dev *pdev = to_pci_dev(device);
- struct iwl_priv *priv = pci_get_drvdata(pdev);
+ struct ieee80211_vif *vif;
+ u8 *addr = priv->stations[sta_id].sta.sta.addr;
- /*
- * This function is called when system goes into suspend state
- * mac80211 will call iwl_mac_stop() from the mac80211 suspend function
- * first but since iwl_mac_stop() has no knowledge of who the caller is,
- * it will not call apm_ops.stop() to stop the DMA operation.
- * Calling apm_ops.stop here to make sure we stop the DMA.
- */
- iwl_apm_stop(priv);
+ if (ctx == NUM_IWL_RXON_CTX)
+ ctx = priv->stations[sta_id].ctxid;
+ vif = priv->contexts[ctx].vif;
- return 0;
+ ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
}
-int iwl_pci_resume(struct device *device)
+void iwl_stop_tx_ba_trans_ready(struct iwl_priv *priv,
+ enum iwl_rxon_context_id ctx,
+ u8 sta_id, u8 tid)
{
- struct pci_dev *pdev = to_pci_dev(device);
- struct iwl_priv *priv = pci_get_drvdata(pdev);
- bool hw_rfkill = false;
+ struct ieee80211_vif *vif;
+ u8 *addr = priv->stations[sta_id].sta.sta.addr;
- /*
- * We disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state.
- */
- pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
+ if (ctx == NUM_IWL_RXON_CTX)
+ ctx = priv->stations[sta_id].ctxid;
+ vif = priv->contexts[ctx].vif;
- iwl_enable_interrupts(priv);
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
+}
- if (!(iwl_read32(priv, CSR_GP_CNTRL) &
- CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
- hw_rfkill = true;
+void iwl_set_hw_rfkill_state(struct iwl_priv *priv, bool state)
+{
+ wiphy_rfkill_set_hw_state(priv->hw->wiphy, state);
+}
- if (hw_rfkill)
- set_bit(STATUS_RF_KILL_HW, &priv->status);
- else
- clear_bit(STATUS_RF_KILL_HW, &priv->status);
+void iwl_nic_config(struct iwl_priv *priv)
+{
+ priv->cfg->lib->nic_config(priv);
- wiphy_rfkill_set_hw_state(priv->hw->wiphy, hw_rfkill);
+}
- return 0;
+void iwl_free_skb(struct iwl_priv *priv, struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info;
+
+ info = IEEE80211_SKB_CB(skb);
+ kmem_cache_free(priv->tx_cmd_pool, (info->driver_data[1]));
+ dev_kfree_skb_any(skb);
}
-const struct dev_pm_ops iwl_pm_ops = {
- .suspend = iwl_pci_suspend,
- .resume = iwl_pci_resume,
- .freeze = iwl_pci_suspend,
- .thaw = iwl_pci_resume,
- .poweroff = iwl_pci_suspend,
- .restore = iwl_pci_resume,
-};
+void iwl_stop_sw_queue(struct iwl_priv *priv, u8 ac)
+{
+ ieee80211_stop_queue(priv->hw, ac);
+}
-#endif /* CONFIG_PM */
+void iwl_wake_sw_queue(struct iwl_priv *priv, u8 ac)
+{
+ ieee80211_wake_queue(priv->hw, ac);
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index a54d416ec345..137da3380704 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -64,6 +64,7 @@
#define __iwl_core_h__
#include "iwl-dev.h"
+#include "iwl-io.h"
/************************
* forward declarations *
@@ -71,111 +72,27 @@
struct iwl_host_cmd;
struct iwl_cmd;
-
-#define IWLWIFI_VERSION "in-tree:"
-#define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation"
-#define DRV_AUTHOR "<ilw@linux.intel.com>"
-
-#define IWL_PCI_DEVICE(dev, subdev, cfg) \
- .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
- .subvendor = PCI_ANY_ID, .subdevice = (subdev), \
- .driver_data = (kernel_ulong_t)&(cfg)
-
#define TIME_UNIT 1024
-#define IWL_SKU_G 0x1
-#define IWL_SKU_A 0x2
-#define IWL_SKU_N 0x8
-
-#define IWL_CMD(x) case x: return #x
-
-struct iwl_hcmd_ops {
- int (*commit_rxon)(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
- void (*set_rxon_chain)(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx);
- int (*set_tx_ant)(struct iwl_priv *priv, u8 valid_tx_ant);
- void (*send_bt_config)(struct iwl_priv *priv);
- int (*set_pan_params)(struct iwl_priv *priv);
-};
-
-struct iwl_hcmd_utils_ops {
- u16 (*build_addsta_hcmd)(const struct iwl_addsta_cmd *cmd, u8 *data);
- void (*gain_computation)(struct iwl_priv *priv,
- u32 *average_noise,
- u16 min_average_noise_antennat_i,
- u32 min_average_noise,
- u8 default_chain);
- void (*chain_noise_reset)(struct iwl_priv *priv);
- void (*tx_cmd_protection)(struct iwl_priv *priv,
- struct ieee80211_tx_info *info,
- __le16 fc, __le32 *tx_flags);
- int (*calc_rssi)(struct iwl_priv *priv,
- struct iwl_rx_phy_res *rx_resp);
- int (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif);
- void (*post_scan)(struct iwl_priv *priv);
-};
-
-struct iwl_apm_ops {
- int (*init)(struct iwl_priv *priv);
- void (*config)(struct iwl_priv *priv);
-};
-
-struct iwl_temp_ops {
- void (*temperature)(struct iwl_priv *priv);
-};
-
struct iwl_lib_ops {
/* set hw dependent parameters */
int (*set_hw_params)(struct iwl_priv *priv);
- /* setup Rx handler */
- void (*rx_handler_setup)(struct iwl_priv *priv);
- /* setup deferred work */
- void (*setup_deferred_work)(struct iwl_priv *priv);
+ /* setup BT Rx handler */
+ void (*bt_rx_handler_setup)(struct iwl_priv *priv);
+ /* setup BT related deferred work */
+ void (*bt_setup_deferred_work)(struct iwl_priv *priv);
/* cancel deferred work */
void (*cancel_deferred_work)(struct iwl_priv *priv);
- /* check validity of rtc data address */
- int (*is_valid_rtc_data_addr)(u32 addr);
int (*set_channel_switch)(struct iwl_priv *priv,
struct ieee80211_channel_switch *ch_switch);
- /* power management */
- struct iwl_apm_ops apm_ops;
-
- /* power */
- int (*send_tx_power) (struct iwl_priv *priv);
- void (*update_chain_flags)(struct iwl_priv *priv);
+ /* device specific configuration */
+ void (*nic_config)(struct iwl_priv *priv);
/* eeprom operations (as defined in iwl-eeprom.h) */
struct iwl_eeprom_ops eeprom_ops;
/* temperature */
- struct iwl_temp_ops temp_ops;
-
- int (*txfifo_flush)(struct iwl_priv *priv, u16 flush_control);
- void (*dev_txfifo_flush)(struct iwl_priv *priv, u16 flush_control);
-
-};
-
-/* NIC specific ops */
-struct iwl_nic_ops {
- void (*additional_nic_config)(struct iwl_priv *priv);
-};
-
-struct iwl_ops {
- const struct iwl_lib_ops *lib;
- const struct iwl_hcmd_ops *hcmd;
- const struct iwl_hcmd_utils_ops *utils;
- const struct iwl_nic_ops *nic;
-};
-
-struct iwl_mod_params {
- int sw_crypto; /* def: 0 = using hardware encryption */
- int num_of_queues; /* def: HW dependent */
- int disable_11n; /* def: 0 = 11n capabilities enabled */
- int amsdu_size_8K; /* def: 1 = enable 8K amsdu size */
- int antenna; /* def: 0 = both antennas (use diversity) */
- int restart_fw; /* def: 1 = restart firmware */
- bool plcp_check; /* def: true = enable plcp health check */
- bool ack_check; /* def: false = disable ack health check */
+ void (*temperature)(struct iwl_priv *priv);
};
/*
@@ -192,9 +109,10 @@ struct iwl_mod_params {
* radio tuning when there is a high receiving plcp error rate
* @chain_noise_scale: default chain noise scale used for gain computation
* @wd_timeout: TX queues watchdog timeout
- * @temperature_kelvin: temperature report by uCode in kelvin
* @max_event_log_size: size of event log buffer size for ucode event logging
* @shadow_reg_enable: HW shadhow register bit
+ * @no_idle_support: do not support idle mode
+ * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
*/
struct iwl_base_params {
int eeprom_size;
@@ -206,16 +124,16 @@ struct iwl_base_params {
const u16 max_ll_items;
const bool shadow_ram_support;
u16 led_compensation;
- int chain_noise_num_beacons;
bool adv_thermal_throttle;
bool support_ct_kill_exit;
const bool support_wimax_coexist;
u8 plcp_delta_threshold;
s32 chain_noise_scale;
unsigned int wd_timeout;
- bool temperature_kelvin;
u32 max_event_log_size;
const bool shadow_reg_enable;
+ const bool no_idle_support;
+ const bool hd_v2;
};
/*
* @advanced_bt_coexist: support advanced bt coexist
@@ -225,7 +143,7 @@ struct iwl_base_params {
* @ampdu_factor: Maximum A-MPDU length factor
* @ampdu_density: Minimum A-MPDU spacing
* @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
-*/
+ */
struct iwl_bt_params {
bool advanced_bt_coexist;
u8 bt_init_traffic_load;
@@ -238,19 +156,33 @@ struct iwl_bt_params {
};
/*
* @use_rts_for_aggregation: use rts/cts protection for HT traffic
-*/
+ */
struct iwl_ht_params {
const bool ht_greenfield_support; /* if used set to true */
bool use_rts_for_aggregation;
+ enum ieee80211_smps_mode smps_mode;
};
/**
* struct iwl_cfg
+ * @name: Offical name of the device
* @fw_name_pre: Firmware filename prefix. The api version and extension
* (.ucode) will be added to filename before loading from disk. The
* filename is constructed as fw_name_pre<api>.ucode.
* @ucode_api_max: Highest version of uCode API supported by driver.
+ * @ucode_api_ok: oldest version of the uCode API that is OK to load
+ * without a warning, for use in transitions
* @ucode_api_min: Lowest version of uCode API supported by driver.
+ * @valid_tx_ant: valid transmit antenna
+ * @valid_rx_ant: valid receive antenna
+ * @sku: sku information from EEPROM
+ * @eeprom_ver: EEPROM version
+ * @eeprom_calib_ver: EEPROM calibration version
+ * @lib: pointer to the lib ops
+ * @additional_nic_config: additional nic configuration
+ * @base_params: pointer to basic parameters
+ * @ht_params: point to ht patameters
+ * @bt_params: pointer to bt parameters
* @pa_type: used by 6000 series only to identify the type of Power Amplifier
* @need_dc_calib: need to perform init dc calibration
* @need_temp_offset_calib: need to perform temperature offset calibration
@@ -260,41 +192,30 @@ struct iwl_ht_params {
* @rx_with_siso_diversity: 1x1 device with rx antenna diversity
* @internal_wimax_coex: internal wifi/wimax combo device
* @iq_invert: I/Q inversion
- * @disable_otp_refresh: disable OTP refresh current limit
+ * @temp_offset_v2: support v2 of temperature offset calibration
*
* We enable the driver to be backward compatible wrt API version. The
* driver specifies which APIs it supports (with @ucode_api_max being the
* highest and @ucode_api_min the lowest). Firmware will only be loaded if
- * it has a supported API version. The firmware's API version will be
- * stored in @iwl_priv, enabling the driver to make runtime changes based
- * on firmware version used.
- *
- * For example,
- * if (IWL_UCODE_API(priv->ucode_ver) >= 2) {
- * Driver interacts with Firmware API version >= 2.
- * } else {
- * Driver interacts with Firmware API version 1.
- * }
+ * it has a supported API version.
*
* The ideal usage of this infrastructure is to treat a new ucode API
- * release as a new hardware revision. That is, through utilizing the
- * iwl_hcmd_utils_ops etc. we accommodate different command structures
- * and flows between hardware versions (4965/5000) as well as their API
- * versions.
- *
+ * release as a new hardware revision.
*/
struct iwl_cfg {
/* params specific to an individual device within a device family */
const char *name;
const char *fw_name_pre;
const unsigned int ucode_api_max;
+ const unsigned int ucode_api_ok;
const unsigned int ucode_api_min;
u8 valid_tx_ant;
u8 valid_rx_ant;
- unsigned int sku;
+ u16 sku;
u16 eeprom_ver;
u16 eeprom_calib_ver;
- const struct iwl_ops *ops;
+ const struct iwl_lib_ops *lib;
+ void (*additional_nic_config)(struct iwl_priv *priv);
/* params not likely to change within a device family */
struct iwl_base_params *base_params;
/* params likely to change within a device family */
@@ -309,16 +230,17 @@ struct iwl_cfg {
const bool rx_with_siso_diversity;
const bool internal_wimax_coex;
const bool iq_invert;
- const bool disable_otp_refresh;
+ const bool temp_offset_v2;
};
/***************************
* L i b *
***************************/
-int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
+int iwlagn_mac_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params);
-int iwl_mac_tx_last_beacon(struct ieee80211_hw *hw);
+int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw);
void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
int hw_decrypt);
int iwl_check_rxon_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
@@ -338,21 +260,17 @@ bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
void iwl_connection_init_rx_config(struct iwl_priv *priv,
struct iwl_rxon_context *ctx);
void iwl_set_rate(struct iwl_priv *priv);
-void iwl_irq_handle_error(struct iwl_priv *priv);
-int iwl_mac_add_interface(struct ieee80211_hw *hw,
+int iwlagn_mac_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
-void iwl_mac_remove_interface(struct ieee80211_hw *hw,
+void iwlagn_mac_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
-int iwl_mac_change_interface(struct ieee80211_hw *hw,
+int iwlagn_mac_change_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum nl80211_iftype newtype, bool newp2p);
-int iwl_alloc_txq_mem(struct iwl_priv *priv);
-void iwl_free_txq_mem(struct iwl_priv *priv);
-
+int iwl_cmd_echo_test(struct iwl_priv *priv);
#ifdef CONFIG_IWLWIFI_DEBUGFS
int iwl_alloc_traffic_mem(struct iwl_priv *priv);
void iwl_free_traffic_mem(struct iwl_priv *priv);
-void iwl_reset_traffic_log(struct iwl_priv *priv);
void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv,
u16 length, struct ieee80211_hdr *header);
void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv,
@@ -390,28 +308,8 @@ static inline void iwl_update_stats(struct iwl_priv *priv, bool is_tx,
/*****************************************************
* RX
******************************************************/
-void iwl_cmd_queue_free(struct iwl_priv *priv);
-void iwl_cmd_queue_unmap(struct iwl_priv *priv);
-int iwl_rx_queue_alloc(struct iwl_priv *priv);
-void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv,
- struct iwl_rx_queue *q);
-int iwl_rx_queue_space(const struct iwl_rx_queue *q);
-void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb);
-
void iwl_chswitch_done(struct iwl_priv *priv, bool is_success);
-/* TX helpers */
-
-/*****************************************************
-* TX
-******************************************************/
-void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq);
-int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq,
- int slots_num, u32 txq_id);
-void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq,
- int slots_num, u32 txq_id);
-void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id);
-void iwl_tx_queue_unmap(struct iwl_priv *priv, int txq_id);
void iwl_setup_watchdog(struct iwl_priv *priv);
/*****************************************************
* TX power
@@ -419,20 +317,13 @@ void iwl_setup_watchdog(struct iwl_priv *priv);
int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force);
/*******************************************************************************
- * Rate
- ******************************************************************************/
-
-u8 iwl_rate_get_lowest_plcp(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx);
-
-/*******************************************************************************
* Scanning
******************************************************************************/
void iwl_init_scan_params(struct iwl_priv *priv);
int iwl_scan_cancel(struct iwl_priv *priv);
-int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
+void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
void iwl_force_scan_end(struct iwl_priv *priv);
-int iwl_mac_hw_scan(struct ieee80211_hw *hw,
+int iwlagn_mac_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
void iwl_internal_short_hw_scan(struct iwl_priv *priv);
@@ -440,12 +331,6 @@ int iwl_force_reset(struct iwl_priv *priv, int mode, bool external);
u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
const u8 *ta, const u8 *ie, int ie_len, int left);
void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);
-u16 iwl_get_active_dwell_time(struct iwl_priv *priv,
- enum ieee80211_band band,
- u8 n_probes);
-u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
- enum ieee80211_band band,
- struct ieee80211_vif *vif);
void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
void iwl_cancel_scan_deferred_work(struct iwl_priv *priv);
int __must_check iwl_scan_initiate(struct iwl_priv *priv,
@@ -468,156 +353,24 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv,
* S e n d i n g H o s t C o m m a n d s *
*****************************************************/
-const char *get_cmd_string(u8 cmd);
-int __must_check iwl_send_cmd_sync(struct iwl_priv *priv,
- struct iwl_host_cmd *cmd);
-int iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
-int __must_check iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id,
- u16 len, const void *data);
-int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len,
- const void *data,
- void (*callback)(struct iwl_priv *priv,
- struct iwl_device_cmd *cmd,
- struct iwl_rx_packet *pkt));
-
-int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
-
-
-/*****************************************************
- * PCI *
- *****************************************************/
-
-static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv)
-{
- int pos;
- u16 pci_lnk_ctl;
- pos = pci_find_capability(priv->pci_dev, PCI_CAP_ID_EXP);
- pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
- return pci_lnk_ctl;
-}
-
void iwl_bg_watchdog(unsigned long data);
u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, u32 beacon_interval);
__le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base,
u32 addon, u32 beacon_interval);
-#ifdef CONFIG_PM
-int iwl_pci_suspend(struct device *device);
-int iwl_pci_resume(struct device *device);
-extern const struct dev_pm_ops iwl_pm_ops;
-
-#define IWL_PM_OPS (&iwl_pm_ops)
-
-#else /* !CONFIG_PM */
-
-#define IWL_PM_OPS NULL
-
-#endif /* !CONFIG_PM */
-
-/*****************************************************
-* Error Handling Debugging
-******************************************************/
-void iwl_dump_nic_error_log(struct iwl_priv *priv);
-int iwl_dump_nic_event_log(struct iwl_priv *priv,
- bool full_log, char **buf, bool display);
-void iwl_dump_csr(struct iwl_priv *priv);
-int iwl_dump_fh(struct iwl_priv *priv, char **buf, bool display);
-#ifdef CONFIG_IWLWIFI_DEBUG
-void iwl_print_rx_config_cmd(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx);
-#else
-static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
-}
-#endif
-
-void iwl_clear_isr_stats(struct iwl_priv *priv);
/*****************************************************
* GEOS
******************************************************/
-int iwlcore_init_geos(struct iwl_priv *priv);
-void iwlcore_free_geos(struct iwl_priv *priv);
-
-/*************** DRIVER STATUS FUNCTIONS *****/
-
-#define STATUS_HCMD_ACTIVE 0 /* host command in progress */
-/* 1 is unused (used to be STATUS_HCMD_SYNC_ACTIVE) */
-#define STATUS_INT_ENABLED 2
-#define STATUS_RF_KILL_HW 3
-#define STATUS_CT_KILL 4
-#define STATUS_INIT 5
-#define STATUS_ALIVE 6
-#define STATUS_READY 7
-#define STATUS_TEMPERATURE 8
-#define STATUS_GEO_CONFIGURED 9
-#define STATUS_EXIT_PENDING 10
-#define STATUS_STATISTICS 12
-#define STATUS_SCANNING 13
-#define STATUS_SCAN_ABORTING 14
-#define STATUS_SCAN_HW 15
-#define STATUS_POWER_PMI 16
-#define STATUS_FW_ERROR 17
-#define STATUS_DEVICE_ENABLED 18
-#define STATUS_CHANNEL_SWITCH_PENDING 19
-
-
-static inline int iwl_is_ready(struct iwl_priv *priv)
-{
- /* The adapter is 'ready' if READY and GEO_CONFIGURED bits are
- * set but EXIT_PENDING is not */
- return test_bit(STATUS_READY, &priv->status) &&
- test_bit(STATUS_GEO_CONFIGURED, &priv->status) &&
- !test_bit(STATUS_EXIT_PENDING, &priv->status);
-}
-
-static inline int iwl_is_alive(struct iwl_priv *priv)
-{
- return test_bit(STATUS_ALIVE, &priv->status);
-}
-
-static inline int iwl_is_init(struct iwl_priv *priv)
-{
- return test_bit(STATUS_INIT, &priv->status);
-}
-
-static inline int iwl_is_rfkill_hw(struct iwl_priv *priv)
-{
- return test_bit(STATUS_RF_KILL_HW, &priv->status);
-}
-
-static inline int iwl_is_rfkill(struct iwl_priv *priv)
-{
- return iwl_is_rfkill_hw(priv);
-}
-
-static inline int iwl_is_ctkill(struct iwl_priv *priv)
-{
- return test_bit(STATUS_CT_KILL, &priv->status);
-}
-
-static inline int iwl_is_ready_rf(struct iwl_priv *priv)
-{
-
- if (iwl_is_rfkill(priv))
- return 0;
-
- return iwl_is_ready(priv);
-}
+int iwl_init_geos(struct iwl_priv *priv);
+void iwl_free_geos(struct iwl_priv *priv);
extern void iwl_send_bt_config(struct iwl_priv *priv);
extern int iwl_send_statistics_request(struct iwl_priv *priv,
u8 flags, bool clear);
-void iwl_apm_stop(struct iwl_priv *priv);
-int iwl_apm_init(struct iwl_priv *priv);
int iwl_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
-static inline int iwlcore_commit_rxon(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- return priv->cfg->ops->hcmd->commit_rxon(priv, ctx);
-}
+
static inline const struct ieee80211_supported_band *iwl_get_hw_mode(
struct iwl_priv *priv, enum ieee80211_band band)
{
@@ -630,10 +383,12 @@ static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv)
priv->cfg->bt_params->advanced_bt_coexist;
}
-extern bool bt_coex_active;
-extern bool bt_siso_mode;
-
+static inline void iwl_enable_rfkill_int(struct iwl_priv *priv)
+{
+ IWL_DEBUG_ISR(priv, "Enabling rfkill interrupt\n");
+ iwl_write32(bus(priv), CSR_INT_MASK, CSR_INT_BIT_RF_KILL);
+}
-void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand);
+extern bool bt_siso_mode;
#endif /* __iwl_core_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index 5ab90ba7a024..b9f3267e720c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -351,6 +351,7 @@
#define CSR_UCODE_SW_BIT_RFKILL (0x00000002)
#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004)
#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008)
+#define CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE (0x00000020)
/* GP Driver */
#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003)
@@ -438,4 +439,22 @@
*/
#define HBUS_TARG_WRPTR (HBUS_BASE+0x060)
+/**********************************************************
+ * CSR values
+ **********************************************************/
+ /*
+ * host interrupt timeout value
+ * used with setting interrupt coalescing timer
+ * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit
+ *
+ * default interrupt coalescing timer is 64 x 32 = 2048 usecs
+ * default interrupt coalescing calibration timer is 16 x 32 = 512 usecs
+ */
+#define IWL_HOST_INT_TIMEOUT_MAX (0xFF)
+#define IWL_HOST_INT_TIMEOUT_DEF (0x40)
+#define IWL_HOST_INT_TIMEOUT_MIN (0x0)
+#define IWL_HOST_INT_CALIB_TIMEOUT_MAX (0xFF)
+#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10)
+#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0)
+
#endif /* !__iwl_csr_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index 2824ccbcc1fc..69a77e24d229 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -29,57 +29,56 @@
#ifndef __iwl_debug_h__
#define __iwl_debug_h__
+#include "iwl-bus.h"
+#include "iwl-shared.h"
+
struct iwl_priv;
-extern u32 iwl_debug_level;
-#define IWL_ERR(p, f, a...) dev_err(&((p)->pci_dev->dev), f, ## a)
-#define IWL_WARN(p, f, a...) dev_warn(&((p)->pci_dev->dev), f, ## a)
-#define IWL_INFO(p, f, a...) dev_info(&((p)->pci_dev->dev), f, ## a)
-#define IWL_CRIT(p, f, a...) dev_crit(&((p)->pci_dev->dev), f, ## a)
+/*No matter what is m (priv, bus, trans), this will work */
+#define IWL_ERR(m, f, a...) dev_err(bus(m)->dev, f, ## a)
+#define IWL_WARN(m, f, a...) dev_warn(bus(m)->dev, f, ## a)
+#define IWL_INFO(m, f, a...) dev_info(bus(m)->dev, f, ## a)
+#define IWL_CRIT(m, f, a...) dev_crit(bus(m)->dev, f, ## a)
-#define iwl_print_hex_error(priv, p, len) \
+#define iwl_print_hex_error(m, p, len) \
do { \
print_hex_dump(KERN_ERR, "iwl data: ", \
DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \
} while (0)
#ifdef CONFIG_IWLWIFI_DEBUG
-#define IWL_DEBUG(__priv, level, fmt, args...) \
+#define IWL_DEBUG(m, level, fmt, args...) \
do { \
- if (iwl_get_debug_level(__priv) & (level)) \
- dev_printk(KERN_ERR, &(__priv->hw->wiphy->dev), \
+ if (iwl_get_debug_level((m)->shrd) & (level)) \
+ dev_printk(KERN_ERR, bus(m)->dev, \
"%c %s " fmt, in_interrupt() ? 'I' : 'U', \
__func__ , ## args); \
} while (0)
-#define IWL_DEBUG_LIMIT(__priv, level, fmt, args...) \
+#define IWL_DEBUG_LIMIT(m, level, fmt, args...) \
do { \
- if ((iwl_get_debug_level(__priv) & (level)) && net_ratelimit()) \
- dev_printk(KERN_ERR, &(__priv->hw->wiphy->dev), \
+ if (iwl_get_debug_level((m)->shrd) & (level) && net_ratelimit())\
+ dev_printk(KERN_ERR, bus(m)->dev, \
"%c %s " fmt, in_interrupt() ? 'I' : 'U', \
__func__ , ## args); \
} while (0)
-#define iwl_print_hex_dump(priv, level, p, len) \
+#define iwl_print_hex_dump(m, level, p, len) \
do { \
- if (iwl_get_debug_level(priv) & level) \
+ if (iwl_get_debug_level((m)->shrd) & level) \
print_hex_dump(KERN_DEBUG, "iwl data: ", \
DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \
} while (0)
#else
-#define IWL_DEBUG(__priv, level, fmt, args...)
-#define IWL_DEBUG_LIMIT(__priv, level, fmt, args...)
-static inline void iwl_print_hex_dump(struct iwl_priv *priv, int level,
- const void *p, u32 len)
-{}
+#define IWL_DEBUG(m, level, fmt, args...)
+#define IWL_DEBUG_LIMIT(m, level, fmt, args...)
+#define iwl_print_hex_dump(m, level, p, len)
#endif /* CONFIG_IWLWIFI_DEBUG */
#ifdef CONFIG_IWLWIFI_DEBUGFS
int iwl_dbgfs_register(struct iwl_priv *priv, const char *name);
void iwl_dbgfs_unregister(struct iwl_priv *priv);
-extern int iwl_dbgfs_statistics_flag(struct iwl_priv *priv, char *buf,
- int bufsz);
#else
static inline int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
{
@@ -106,10 +105,12 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv)
*
* The active debug levels can be accessed via files
*
- * /sys/module/iwlagn/parameters/debug{50}
- * /sys/class/net/wlan0/device/debug_level
- *
+ * /sys/module/iwlwifi/parameters/debug
* when CONFIG_IWLWIFI_DEBUG=y.
+ *
+ * /sys/kernel/debug/phy0/iwlwifi/debug/debug_level
+ * when CONFIG_IWLWIFI_DEBUGFS=y.
+ *
*/
/* 0x0000000F - 0x00000001 */
@@ -125,13 +126,13 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv)
/* 0x00000F00 - 0x00000100 */
#define IWL_DL_POWER (1 << 8)
#define IWL_DL_TEMP (1 << 9)
-#define IWL_DL_NOTIF (1 << 10)
+/* reserved (1 << 10) */
#define IWL_DL_SCAN (1 << 11)
/* 0x0000F000 - 0x00001000 */
#define IWL_DL_ASSOC (1 << 12)
#define IWL_DL_DROP (1 << 13)
-#define IWL_DL_TXPOWER (1 << 14)
-#define IWL_DL_AP (1 << 15)
+/* reserved (1 << 14) */
+#define IWL_DL_COEX (1 << 15)
/* 0x000F0000 - 0x00010000 */
#define IWL_DL_FW (1 << 16)
#define IWL_DL_RF_KILL (1 << 17)
@@ -168,15 +169,14 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv)
#define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a)
#define IWL_DEBUG_FW(p, f, a...) IWL_DEBUG(p, IWL_DL_FW, f, ## a)
#define IWL_DEBUG_RF_KILL(p, f, a...) IWL_DEBUG(p, IWL_DL_RF_KILL, f, ## a)
+#define IWL_DEBUG_FW_ERRORS(p, f, a...) IWL_DEBUG(p, IWL_DL_FW_ERRORS, f, ## a)
#define IWL_DEBUG_DROP(p, f, a...) IWL_DEBUG(p, IWL_DL_DROP, f, ## a)
#define IWL_DEBUG_DROP_LIMIT(p, f, a...) \
IWL_DEBUG_LIMIT(p, IWL_DL_DROP, f, ## a)
-#define IWL_DEBUG_AP(p, f, a...) IWL_DEBUG(p, IWL_DL_AP, f, ## a)
-#define IWL_DEBUG_TXPOWER(p, f, a...) IWL_DEBUG(p, IWL_DL_TXPOWER, f, ## a)
+#define IWL_DEBUG_COEX(p, f, a...) IWL_DEBUG(p, IWL_DL_COEX, f, ## a)
#define IWL_DEBUG_RATE(p, f, a...) IWL_DEBUG(p, IWL_DL_RATE, f, ## a)
#define IWL_DEBUG_RATE_LIMIT(p, f, a...) \
IWL_DEBUG_LIMIT(p, IWL_DL_RATE, f, ## a)
-#define IWL_DEBUG_NOTIF(p, f, a...) IWL_DEBUG(p, IWL_DL_NOTIF, f, ## a)
#define IWL_DEBUG_ASSOC(p, f, a...) \
IWL_DEBUG(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
#define IWL_DEBUG_ASSOC_LIMIT(p, f, a...) \
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index 0e6a04b739ad..a1670e3f8bfa 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -64,6 +64,14 @@
goto err; \
} while (0)
+#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_u32(#name, mode, \
+ parent, ptr); \
+ if (IS_ERR(__tmp) || !__tmp) \
+ goto err; \
+} while (0)
+
/* file operation */
#define DEBUGFS_READ_FUNC(name) \
static ssize_t iwl_dbgfs_##name##_read(struct file *file, \
@@ -227,7 +235,7 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
/* default is to dump the entire data segment */
if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) {
priv->dbgfs_sram_offset = 0x800000;
- if (priv->ucode_type == UCODE_SUBTYPE_INIT)
+ if (priv->ucode_type == IWL_UCODE_INIT)
priv->dbgfs_sram_len = priv->ucode_init.data.len;
else
priv->dbgfs_sram_len = priv->ucode_rt.data.len;
@@ -254,7 +262,7 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
sram = priv->dbgfs_sram_offset & ~0x3;
/* read the first u32 from sram */
- val = iwl_read_targ_mem(priv, sram);
+ val = iwl_read_targ_mem(bus(priv), sram);
for (; len; len--) {
/* put the address at the start of every line */
@@ -273,7 +281,7 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
if (++offset == 4) {
sram += 4;
offset = 0;
- val = iwl_read_targ_mem(priv, sram);
+ val = iwl_read_targ_mem(bus(priv), sram);
}
/* put in extra spaces and split lines for human readability */
@@ -322,12 +330,25 @@ static ssize_t iwl_dbgfs_sram_write(struct file *file,
return count;
}
+static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+
+ if (!priv->wowlan_sram)
+ return -ENODATA;
+
+ return simple_read_from_buffer(user_buf, count, ppos,
+ priv->wowlan_sram,
+ priv->ucode_wowlan.data.len);
+}
static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_priv *priv = file->private_data;
struct iwl_station_entry *station;
- int max_sta = priv->hw_params.max_stations;
+ struct iwl_tid_data *tid_data;
char *buf;
int i, j, pos = 0;
ssize_t ret;
@@ -341,7 +362,7 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n",
priv->num_stations);
- for (i = 0; i < max_sta; i++) {
+ for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
station = &priv->stations[i];
if (!station->used)
continue;
@@ -350,22 +371,18 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
i, station->sta.sta.addr,
station->sta.station_flags_msk);
pos += scnprintf(buf + pos, bufsz - pos,
- "TID\tseq_num\ttxq_id\tframes\ttfds\t");
- pos += scnprintf(buf + pos, bufsz - pos,
- "start_idx\tbitmap\t\t\trate_n_flags\n");
+ "TID\tseq_num\ttxq_id\ttfds\trate_n_flags\n");
- for (j = 0; j < MAX_TID_COUNT; j++) {
+ for (j = 0; j < IWL_MAX_TID_COUNT; j++) {
+ tid_data = &priv->shrd->tid_data[i][j];
pos += scnprintf(buf + pos, bufsz - pos,
- "%d:\t%#x\t%#x\t%u\t%u\t%u\t\t%#.16llx\t%#x",
- j, station->tid[j].seq_number,
- station->tid[j].agg.txq_id,
- station->tid[j].agg.frame_count,
- station->tid[j].tfds_in_queue,
- station->tid[j].agg.start_idx,
- station->tid[j].agg.bitmap,
- station->tid[j].agg.rate_n_flags);
-
- if (station->tid[j].agg.wait_for_ba)
+ "%d:\t%#x\t%#x\t%u\t%#x",
+ j, tid_data->seq_number,
+ tid_data->agg.txq_id,
+ tid_data->tfds_in_queue,
+ tid_data->agg.rate_n_flags);
+
+ if (tid_data->agg.wait_for_ba)
pos += scnprintf(buf + pos, bufsz - pos,
" - waitforba");
pos += scnprintf(buf + pos, bufsz - pos, "\n");
@@ -429,46 +446,6 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file,
return ret;
}
-static ssize_t iwl_dbgfs_log_event_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char *buf;
- int pos = 0;
- ssize_t ret = -ENOMEM;
-
- ret = pos = iwl_dump_nic_event_log(priv, true, &buf, true);
- if (buf) {
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- }
- return ret;
-}
-
-static ssize_t iwl_dbgfs_log_event_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- u32 event_log_flag;
- char buf[8];
- int buf_size;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &event_log_flag) != 1)
- return -EFAULT;
- if (event_log_flag == 1)
- iwl_dump_nic_event_log(priv, true, NULL, false);
-
- return count;
-}
-
-
-
static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -479,7 +456,7 @@ static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf,
char *buf;
ssize_t ret;
- if (!test_bit(STATUS_GEO_CONFIGURED, &priv->status))
+ if (!test_bit(STATUS_GEO_CONFIGURED, &priv->shrd->status))
return -EAGAIN;
buf = kzalloc(bufsz, GFP_KERNEL);
@@ -549,45 +526,46 @@ static ssize_t iwl_dbgfs_status_read(struct file *file,
const size_t bufsz = sizeof(buf);
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_ACTIVE:\t %d\n",
- test_bit(STATUS_HCMD_ACTIVE, &priv->status));
+ test_bit(STATUS_HCMD_ACTIVE, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INT_ENABLED:\t %d\n",
- test_bit(STATUS_INT_ENABLED, &priv->status));
+ test_bit(STATUS_INT_ENABLED, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n",
- test_bit(STATUS_RF_KILL_HW, &priv->status));
+ test_bit(STATUS_RF_KILL_HW, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n",
- test_bit(STATUS_CT_KILL, &priv->status));
+ test_bit(STATUS_CT_KILL, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INIT:\t\t %d\n",
- test_bit(STATUS_INIT, &priv->status));
+ test_bit(STATUS_INIT, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n",
- test_bit(STATUS_ALIVE, &priv->status));
+ test_bit(STATUS_ALIVE, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n",
- test_bit(STATUS_READY, &priv->status));
+ test_bit(STATUS_READY, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_TEMPERATURE:\t %d\n",
- test_bit(STATUS_TEMPERATURE, &priv->status));
+ test_bit(STATUS_TEMPERATURE, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_GEO_CONFIGURED:\t %d\n",
- test_bit(STATUS_GEO_CONFIGURED, &priv->status));
+ test_bit(STATUS_GEO_CONFIGURED, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n",
- test_bit(STATUS_EXIT_PENDING, &priv->status));
+ test_bit(STATUS_EXIT_PENDING, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n",
- test_bit(STATUS_STATISTICS, &priv->status));
+ test_bit(STATUS_STATISTICS, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n",
- test_bit(STATUS_SCANNING, &priv->status));
+ test_bit(STATUS_SCANNING, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n",
- test_bit(STATUS_SCAN_ABORTING, &priv->status));
+ test_bit(STATUS_SCAN_ABORTING, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n",
- test_bit(STATUS_SCAN_HW, &priv->status));
+ test_bit(STATUS_SCAN_HW, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n",
- test_bit(STATUS_POWER_PMI, &priv->status));
+ test_bit(STATUS_POWER_PMI, &priv->shrd->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n",
- test_bit(STATUS_FW_ERROR, &priv->status));
+ test_bit(STATUS_FW_ERROR, &priv->shrd->status));
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
-static ssize_t iwl_dbgfs_interrupt_read(struct file *file,
+static ssize_t iwl_dbgfs_rx_handlers_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos) {
struct iwl_priv *priv = file->private_data;
+
int pos = 0;
int cnt = 0;
char *buf;
@@ -600,61 +578,25 @@ static ssize_t iwl_dbgfs_interrupt_read(struct file *file,
return -ENOMEM;
}
- pos += scnprintf(buf + pos, bufsz - pos,
- "Interrupt Statistics Report:\n");
-
- pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n",
- priv->isr_stats.hw);
- pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n",
- priv->isr_stats.sw);
- if (priv->isr_stats.sw || priv->isr_stats.hw) {
- pos += scnprintf(buf + pos, bufsz - pos,
- "\tLast Restarting Code: 0x%X\n",
- priv->isr_stats.err_code);
- }
-#ifdef CONFIG_IWLWIFI_DEBUG
- pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n",
- priv->isr_stats.sch);
- pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n",
- priv->isr_stats.alive);
-#endif
- pos += scnprintf(buf + pos, bufsz - pos,
- "HW RF KILL switch toggled:\t %u\n",
- priv->isr_stats.rfkill);
-
- pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n",
- priv->isr_stats.ctkill);
-
- pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n",
- priv->isr_stats.wakeup);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "Rx command responses:\t\t %u\n",
- priv->isr_stats.rx);
for (cnt = 0; cnt < REPLY_MAX; cnt++) {
- if (priv->isr_stats.rx_handlers[cnt] > 0)
+ if (priv->rx_handlers_stats[cnt] > 0)
pos += scnprintf(buf + pos, bufsz - pos,
"\tRx handler[%36s]:\t\t %u\n",
get_cmd_string(cnt),
- priv->isr_stats.rx_handlers[cnt]);
+ priv->rx_handlers_stats[cnt]);
}
- pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n",
- priv->isr_stats.tx);
-
- pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n",
- priv->isr_stats.unhandled);
-
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
return ret;
}
-static ssize_t iwl_dbgfs_interrupt_write(struct file *file,
+static ssize_t iwl_dbgfs_rx_handlers_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_priv *priv = file->private_data;
+
char buf[8];
int buf_size;
u32 reset_flag;
@@ -666,7 +608,8 @@ static ssize_t iwl_dbgfs_interrupt_write(struct file *file,
if (sscanf(buf, "%x", &reset_flag) != 1)
return -EFAULT;
if (reset_flag == 0)
- iwl_clear_isr_stats(priv);
+ memset(&priv->rx_handlers_stats[0], 0,
+ sizeof(priv->rx_handlers_stats));
return count;
}
@@ -771,6 +714,20 @@ static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
+static ssize_t iwl_dbgfs_temperature_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "%d\n", priv->temperature);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+
static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
@@ -801,14 +758,14 @@ static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file,
if (value != -1 && (value < 0 || value >= IWL_POWER_NUM))
return -EINVAL;
- if (!iwl_is_ready_rf(priv))
+ if (!iwl_is_ready_rf(priv->shrd))
return -EAGAIN;
priv->power_data.debug_sleep_level_override = value;
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
iwl_power_update_mode(priv, true);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
return count;
}
@@ -856,15 +813,16 @@ static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file,
}
DEBUGFS_READ_WRITE_FILE_OPS(sram);
-DEBUGFS_READ_WRITE_FILE_OPS(log_event);
+DEBUGFS_READ_FILE_OPS(wowlan_sram);
DEBUGFS_READ_FILE_OPS(nvm);
DEBUGFS_READ_FILE_OPS(stations);
DEBUGFS_READ_FILE_OPS(channels);
DEBUGFS_READ_FILE_OPS(status);
-DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
+DEBUGFS_READ_WRITE_FILE_OPS(rx_handlers);
DEBUGFS_READ_FILE_OPS(qos);
DEBUGFS_READ_FILE_OPS(thermal_throttling);
DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40);
+DEBUGFS_READ_FILE_OPS(temperature);
DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override);
DEBUGFS_READ_FILE_OPS(current_sleep_command);
@@ -875,36 +833,23 @@ static ssize_t iwl_dbgfs_traffic_log_read(struct file *file,
struct iwl_priv *priv = file->private_data;
int pos = 0, ofs = 0;
int cnt = 0, entry;
- struct iwl_tx_queue *txq;
- struct iwl_queue *q;
- struct iwl_rx_queue *rxq = &priv->rxq;
+
char *buf;
int bufsz = ((IWL_TRAFFIC_ENTRIES * IWL_TRAFFIC_ENTRY_SIZE * 64) * 2) +
- (priv->cfg->base_params->num_of_queues * 32 * 8) + 400;
+ (hw_params(priv).max_txq_num * 32 * 8) + 400;
const u8 *ptr;
ssize_t ret;
- if (!priv->txq) {
- IWL_ERR(priv, "txq not ready\n");
- return -EAGAIN;
- }
buf = kzalloc(bufsz, GFP_KERNEL);
if (!buf) {
IWL_ERR(priv, "Can not allocate buffer\n");
return -ENOMEM;
}
- pos += scnprintf(buf + pos, bufsz - pos, "Tx Queue\n");
- for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
- txq = &priv->txq[cnt];
- q = &txq->q;
- pos += scnprintf(buf + pos, bufsz - pos,
- "q[%d]: read_ptr: %u, write_ptr: %u\n",
- cnt, q->read_ptr, q->write_ptr);
- }
- if (priv->tx_traffic && (iwl_debug_level & IWL_DL_TX)) {
+ if (priv->tx_traffic &&
+ (iwl_get_debug_level(priv->shrd) & IWL_DL_TX)) {
ptr = priv->tx_traffic;
pos += scnprintf(buf + pos, bufsz - pos,
- "Tx Traffic idx: %u\n", priv->tx_traffic_idx);
+ "Tx Traffic idx: %u\n", priv->tx_traffic_idx);
for (cnt = 0, ofs = 0; cnt < IWL_TRAFFIC_ENTRIES; cnt++) {
for (entry = 0; entry < IWL_TRAFFIC_ENTRY_SIZE / 16;
entry++, ofs += 16) {
@@ -919,15 +864,11 @@ static ssize_t iwl_dbgfs_traffic_log_read(struct file *file,
}
}
- pos += scnprintf(buf + pos, bufsz - pos, "Rx Queue\n");
- pos += scnprintf(buf + pos, bufsz - pos,
- "read: %u, write: %u\n",
- rxq->read, rxq->write);
-
- if (priv->rx_traffic && (iwl_debug_level & IWL_DL_RX)) {
+ if (priv->rx_traffic &&
+ (iwl_get_debug_level(priv->shrd) & IWL_DL_RX)) {
ptr = priv->rx_traffic;
pos += scnprintf(buf + pos, bufsz - pos,
- "Rx Traffic idx: %u\n", priv->rx_traffic_idx);
+ "Rx Traffic idx: %u\n", priv->rx_traffic_idx);
for (cnt = 0, ofs = 0; cnt < IWL_TRAFFIC_ENTRIES; cnt++) {
for (entry = 0; entry < IWL_TRAFFIC_ENTRY_SIZE / 16;
entry++, ofs += 16) {
@@ -968,76 +909,6 @@ static ssize_t iwl_dbgfs_traffic_log_write(struct file *file,
return count;
}
-static ssize_t iwl_dbgfs_tx_queue_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- struct iwl_tx_queue *txq;
- struct iwl_queue *q;
- char *buf;
- int pos = 0;
- int cnt;
- int ret;
- const size_t bufsz = sizeof(char) * 64 *
- priv->cfg->base_params->num_of_queues;
-
- if (!priv->txq) {
- IWL_ERR(priv, "txq not ready\n");
- return -EAGAIN;
- }
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
- txq = &priv->txq[cnt];
- q = &txq->q;
- pos += scnprintf(buf + pos, bufsz - pos,
- "hwq %.2d: read=%u write=%u stop=%d"
- " swq_id=%#.2x (ac %d/hwq %d)\n",
- cnt, q->read_ptr, q->write_ptr,
- !!test_bit(cnt, priv->queue_stopped),
- txq->swq_id, txq->swq_id & 3,
- (txq->swq_id >> 2) & 0x1f);
- if (cnt >= 4)
- continue;
- /* for the ACs, display the stop count too */
- pos += scnprintf(buf + pos, bufsz - pos,
- " stop-count: %d\n",
- atomic_read(&priv->queue_stop_count[cnt]));
- }
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- struct iwl_rx_queue *rxq = &priv->rxq;
- char buf[256];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n",
- rxq->read);
- pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n",
- rxq->write);
- pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n",
- rxq->free_count);
- if (rxq->rb_stts) {
- pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n",
- le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF);
- } else {
- pos += scnprintf(buf + pos, bufsz - pos,
- "closed_rb_num: Not Allocated\n");
- }
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
static const char *fmt_value = " %-30s %10u\n";
static const char *fmt_hex = " %-30s 0x%02X\n";
static const char *fmt_table = " %-30s %10u %10u %10u %10u\n";
@@ -1082,7 +953,7 @@ static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file,
struct statistics_rx_non_phy *delta_general, *max_general;
struct statistics_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht;
- if (!iwl_is_alive(priv))
+ if (!iwl_is_alive(priv->shrd))
return -EAGAIN;
buf = kzalloc(bufsz, GFP_KERNEL);
@@ -1508,7 +1379,7 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file,
ssize_t ret;
struct statistics_tx *tx, *accum_tx, *delta_tx, *max_tx;
- if (!iwl_is_alive(priv))
+ if (!iwl_is_alive(priv->shrd))
return -EAGAIN;
buf = kzalloc(bufsz, GFP_KERNEL);
@@ -1702,7 +1573,7 @@ static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file,
struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg;
struct statistics_div *div, *accum_div, *delta_div, *max_div;
- if (!iwl_is_alive(priv))
+ if (!iwl_is_alive(priv->shrd))
return -EAGAIN;
buf = kzalloc(bufsz, GFP_KERNEL);
@@ -1815,16 +1686,16 @@ static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file,
ssize_t ret;
struct statistics_bt_activity *bt, *accum_bt;
- if (!iwl_is_alive(priv))
+ if (!iwl_is_alive(priv->shrd))
return -EAGAIN;
if (!priv->bt_enable_flag)
return -EINVAL;
/* make request to uCode to retrieve statistics information */
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
ret = iwl_send_statistics_request(priv, CMD_SYNC, false);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
if (ret) {
IWL_ERR(priv,
@@ -1903,7 +1774,7 @@ static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file,
(sizeof(struct reply_agg_tx_error_statistics) * 24) + 200;
ssize_t ret;
- if (!iwl_is_alive(priv))
+ if (!iwl_is_alive(priv->shrd))
return -EAGAIN;
buf = kzalloc(bufsz, GFP_KERNEL);
@@ -1915,121 +1786,121 @@ static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file,
pos += scnprintf(buf + pos, bufsz - pos, "Statistics_TX_Error:\n");
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_DELAY),
- priv->_agn.reply_tx_stats.pp_delay);
+ priv->reply_tx_stats.pp_delay);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_FEW_BYTES),
- priv->_agn.reply_tx_stats.pp_few_bytes);
+ priv->reply_tx_stats.pp_few_bytes);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_BT_PRIO),
- priv->_agn.reply_tx_stats.pp_bt_prio);
+ priv->reply_tx_stats.pp_bt_prio);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_QUIET_PERIOD),
- priv->_agn.reply_tx_stats.pp_quiet_period);
+ priv->reply_tx_stats.pp_quiet_period);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_CALC_TTAK),
- priv->_agn.reply_tx_stats.pp_calc_ttak);
+ priv->reply_tx_stats.pp_calc_ttak);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
iwl_get_tx_fail_reason(
TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY),
- priv->_agn.reply_tx_stats.int_crossed_retry);
+ priv->reply_tx_stats.int_crossed_retry);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_SHORT_LIMIT),
- priv->_agn.reply_tx_stats.short_limit);
+ priv->reply_tx_stats.short_limit);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_LONG_LIMIT),
- priv->_agn.reply_tx_stats.long_limit);
+ priv->reply_tx_stats.long_limit);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_UNDERRUN),
- priv->_agn.reply_tx_stats.fifo_underrun);
+ priv->reply_tx_stats.fifo_underrun);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_DRAIN_FLOW),
- priv->_agn.reply_tx_stats.drain_flow);
+ priv->reply_tx_stats.drain_flow);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_RFKILL_FLUSH),
- priv->_agn.reply_tx_stats.rfkill_flush);
+ priv->reply_tx_stats.rfkill_flush);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_LIFE_EXPIRE),
- priv->_agn.reply_tx_stats.life_expire);
+ priv->reply_tx_stats.life_expire);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_DEST_PS),
- priv->_agn.reply_tx_stats.dest_ps);
+ priv->reply_tx_stats.dest_ps);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_HOST_ABORTED),
- priv->_agn.reply_tx_stats.host_abort);
+ priv->reply_tx_stats.host_abort);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_BT_RETRY),
- priv->_agn.reply_tx_stats.pp_delay);
+ priv->reply_tx_stats.pp_delay);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_STA_INVALID),
- priv->_agn.reply_tx_stats.sta_invalid);
+ priv->reply_tx_stats.sta_invalid);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_FRAG_DROPPED),
- priv->_agn.reply_tx_stats.frag_drop);
+ priv->reply_tx_stats.frag_drop);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_TID_DISABLE),
- priv->_agn.reply_tx_stats.tid_disable);
+ priv->reply_tx_stats.tid_disable);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_FLUSHED),
- priv->_agn.reply_tx_stats.fifo_flush);
+ priv->reply_tx_stats.fifo_flush);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
iwl_get_tx_fail_reason(
TX_STATUS_FAIL_INSUFFICIENT_CF_POLL),
- priv->_agn.reply_tx_stats.insuff_cf_poll);
+ priv->reply_tx_stats.insuff_cf_poll);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_tx_fail_reason(TX_STATUS_FAIL_PASSIVE_NO_RX),
- priv->_agn.reply_tx_stats.fail_hw_drop);
+ priv->reply_tx_stats.fail_hw_drop);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
iwl_get_tx_fail_reason(
TX_STATUS_FAIL_NO_BEACON_ON_RADAR),
- priv->_agn.reply_tx_stats.sta_color_mismatch);
+ priv->reply_tx_stats.sta_color_mismatch);
pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n",
- priv->_agn.reply_tx_stats.unknown);
+ priv->reply_tx_stats.unknown);
pos += scnprintf(buf + pos, bufsz - pos,
"\nStatistics_Agg_TX_Error:\n");
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_agg_tx_fail_reason(AGG_TX_STATE_UNDERRUN_MSK),
- priv->_agn.reply_agg_tx_stats.underrun);
+ priv->reply_agg_tx_stats.underrun);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_agg_tx_fail_reason(AGG_TX_STATE_BT_PRIO_MSK),
- priv->_agn.reply_agg_tx_stats.bt_prio);
+ priv->reply_agg_tx_stats.bt_prio);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_agg_tx_fail_reason(AGG_TX_STATE_FEW_BYTES_MSK),
- priv->_agn.reply_agg_tx_stats.few_bytes);
+ priv->reply_agg_tx_stats.few_bytes);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_agg_tx_fail_reason(AGG_TX_STATE_ABORT_MSK),
- priv->_agn.reply_agg_tx_stats.abort);
+ priv->reply_agg_tx_stats.abort);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
iwl_get_agg_tx_fail_reason(
AGG_TX_STATE_LAST_SENT_TTL_MSK),
- priv->_agn.reply_agg_tx_stats.last_sent_ttl);
+ priv->reply_agg_tx_stats.last_sent_ttl);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
iwl_get_agg_tx_fail_reason(
AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK),
- priv->_agn.reply_agg_tx_stats.last_sent_try);
+ priv->reply_agg_tx_stats.last_sent_try);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
iwl_get_agg_tx_fail_reason(
AGG_TX_STATE_LAST_SENT_BT_KILL_MSK),
- priv->_agn.reply_agg_tx_stats.last_sent_bt_kill);
+ priv->reply_agg_tx_stats.last_sent_bt_kill);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_agg_tx_fail_reason(AGG_TX_STATE_SCD_QUERY_MSK),
- priv->_agn.reply_agg_tx_stats.scd_query);
+ priv->reply_agg_tx_stats.scd_query);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
iwl_get_agg_tx_fail_reason(
AGG_TX_STATE_TEST_BAD_CRC32_MSK),
- priv->_agn.reply_agg_tx_stats.bad_crc32);
+ priv->reply_agg_tx_stats.bad_crc32);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_agg_tx_fail_reason(AGG_TX_STATE_RESPONSE_MSK),
- priv->_agn.reply_agg_tx_stats.response);
+ priv->reply_agg_tx_stats.response);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DUMP_TX_MSK),
- priv->_agn.reply_agg_tx_stats.dump_tx);
+ priv->reply_agg_tx_stats.dump_tx);
pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DELAY_TX_MSK),
- priv->_agn.reply_agg_tx_stats.delay_tx);
+ priv->reply_agg_tx_stats.delay_tx);
pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n",
- priv->_agn.reply_agg_tx_stats.unknown);
+ priv->reply_agg_tx_stats.unknown);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
@@ -2185,7 +2056,7 @@ static ssize_t iwl_dbgfs_power_save_status_read(struct file *file,
const size_t bufsz = sizeof(buf);
u32 pwrsave_status;
- pwrsave_status = iwl_read32(priv, CSR_GP_CNTRL) &
+ pwrsave_status = iwl_read32(bus(priv), CSR_GP_CNTRL) &
CSR_GP_REG_POWER_SAVE_STATUS_MSK;
pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: ");
@@ -2215,30 +2086,9 @@ static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file,
return -EFAULT;
/* make request to uCode to retrieve statistics information */
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
iwl_send_statistics_request(priv, CMD_SYNC, true);
- mutex_unlock(&priv->mutex);
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_csr_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int csr;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &csr) != 1)
- return -EFAULT;
-
- iwl_dump_csr(priv);
+ mutex_unlock(&priv->shrd->mutex);
return count;
}
@@ -2319,25 +2169,6 @@ static ssize_t iwl_dbgfs_rxon_filter_flags_read(struct file *file,
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
-static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char *buf;
- int pos = 0;
- ssize_t ret = -EFAULT;
-
- ret = pos = iwl_dump_fh(priv, &buf, true);
- if (buf) {
- ret = simple_read_from_buffer(user_buf,
- count, ppos, buf, pos);
- kfree(buf);
- }
-
- return ret;
-}
-
static ssize_t iwl_dbgfs_missed_beacon_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos) {
@@ -2420,8 +2251,8 @@ static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file,
static ssize_t iwl_dbgfs_force_reset_read(struct file *file,
char __user *user_buf,
- size_t count, loff_t *ppos) {
-
+ size_t count, loff_t *ppos)
+{
struct iwl_priv *priv = file->private_data;
int i, pos = 0;
char buf[300];
@@ -2490,18 +2321,18 @@ static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file,
if (sscanf(buf, "%d", &flush) != 1)
return -EINVAL;
- if (iwl_is_rfkill(priv))
+ if (iwl_is_rfkill(priv->shrd))
return -EFAULT;
- priv->cfg->ops->lib->dev_txfifo_flush(priv, IWL_DROP_ALL);
+ iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL);
return count;
}
static ssize_t iwl_dbgfs_wd_timeout_write(struct file *file,
const char __user *user_buf,
- size_t count, loff_t *ppos) {
-
+ size_t count, loff_t *ppos)
+{
struct iwl_priv *priv = file->private_data;
char buf[8];
int buf_size;
@@ -2612,11 +2443,26 @@ static ssize_t iwl_dbgfs_protection_mode_write(struct file *file,
return count;
}
+static ssize_t iwl_dbgfs_echo_test_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ iwl_cmd_echo_test(priv);
+ return count;
+}
+
DEBUGFS_READ_FILE_OPS(rx_statistics);
DEBUGFS_READ_FILE_OPS(tx_statistics);
DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
-DEBUGFS_READ_FILE_OPS(rx_queue);
-DEBUGFS_READ_FILE_OPS(tx_queue);
DEBUGFS_READ_FILE_OPS(ucode_rx_stats);
DEBUGFS_READ_FILE_OPS(ucode_tx_stats);
DEBUGFS_READ_FILE_OPS(ucode_general_stats);
@@ -2625,9 +2471,7 @@ DEBUGFS_READ_FILE_OPS(chain_noise);
DEBUGFS_READ_FILE_OPS(power_save_status);
DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics);
DEBUGFS_WRITE_FILE_OPS(clear_traffic_statistics);
-DEBUGFS_WRITE_FILE_OPS(csr);
DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing);
-DEBUGFS_READ_FILE_OPS(fh_reg);
DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon);
DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta);
DEBUGFS_READ_WRITE_FILE_OPS(force_reset);
@@ -2639,6 +2483,53 @@ DEBUGFS_WRITE_FILE_OPS(wd_timeout);
DEBUGFS_READ_FILE_OPS(bt_traffic);
DEBUGFS_READ_WRITE_FILE_OPS(protection_mode);
DEBUGFS_READ_FILE_OPS(reply_tx_error);
+DEBUGFS_WRITE_FILE_OPS(echo_test);
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+static ssize_t iwl_dbgfs_debug_level_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ struct iwl_shared *shrd = priv->shrd;
+ char buf[11];
+ int len;
+
+ len = scnprintf(buf, sizeof(buf), "0x%.8x",
+ iwl_get_debug_level(shrd));
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t iwl_dbgfs_debug_level_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ struct iwl_shared *shrd = priv->shrd;
+ char buf[11];
+ unsigned long val;
+ int ret;
+
+ if (count > sizeof(buf))
+ return -EINVAL;
+
+ memset(buf, 0, sizeof(buf));
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ shrd->dbg_level_dev = val;
+ if (iwl_alloc_traffic_mem(priv))
+ IWL_ERR(priv, "Not enough memory to generate traffic log\n");
+
+ return count;
+}
+DEBUGFS_READ_WRITE_FILE_OPS(debug_level);
+#endif /* CONFIG_IWLWIFI_DEBUG */
/*
* Create the debugfs files and directories
@@ -2667,36 +2558,32 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(log_event, dir_data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(wowlan_sram, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(interrupt, dir_data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(rx_handlers, dir_data, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(sleep_level_override, dir_data, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(temperature, dir_data, S_IRUSR);
+
DEBUGFS_ADD_FILE(rx_statistics, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(tx_statistics, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(traffic_log, dir_debug, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(rx_queue, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(tx_queue, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(clear_traffic_statistics, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(csr, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(fh_reg, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(force_reset, dir_debug, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR);
- if (priv->cfg->ops->lib->dev_txfifo_flush)
- DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR);
-
DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR);
@@ -2705,12 +2592,20 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(wd_timeout, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR);
if (iwl_advanced_bt_coexist(priv))
DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR);
+#ifdef CONFIG_IWLWIFI_DEBUG
+ DEBUGFS_ADD_FILE(debug_level, dir_debug, S_IRUSR | S_IWUSR);
+#endif
+
DEBUGFS_ADD_BOOL(disable_sensitivity, dir_rf,
&priv->disable_sens_cal);
DEBUGFS_ADD_BOOL(disable_chain_noise, dir_rf,
&priv->disable_chain_noise_cal);
+
+ if (iwl_trans_dbgfs_register(trans(priv), dir_debug))
+ goto err;
return 0;
err:
@@ -2731,6 +2626,3 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv)
debugfs_remove_recursive(priv->debugfs_dir);
priv->debugfs_dir = NULL;
}
-
-
-
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index c8de236c141b..6c00a447963d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -31,22 +31,25 @@
#ifndef __iwl_dev_h__
#define __iwl_dev_h__
-#include <linux/pci.h> /* for struct pci_device_id */
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/leds.h>
+#include <linux/slab.h>
#include <net/ieee80211_radiotap.h>
#include "iwl-eeprom.h"
#include "iwl-csr.h"
#include "iwl-prph.h"
-#include "iwl-fh.h"
#include "iwl-debug.h"
#include "iwl-agn-hw.h"
#include "iwl-led.h"
#include "iwl-power.h"
#include "iwl-agn-rs.h"
#include "iwl-agn-tt.h"
+#include "iwl-bus.h"
+#include "iwl-trans.h"
+#include "iwl-shared.h"
struct iwl_tx_queue;
@@ -85,109 +88,6 @@ struct iwl_tx_queue;
#define DEFAULT_SHORT_RETRY_LIMIT 7U
#define DEFAULT_LONG_RETRY_LIMIT 4U
-struct iwl_rx_mem_buffer {
- dma_addr_t page_dma;
- struct page *page;
- struct list_head list;
-};
-
-#define rxb_addr(r) page_address(r->page)
-
-/* defined below */
-struct iwl_device_cmd;
-
-struct iwl_cmd_meta {
- /* only for SYNC commands, iff the reply skb is wanted */
- struct iwl_host_cmd *source;
- /*
- * only for ASYNC commands
- * (which is somewhat stupid -- look at iwl-sta.c for instance
- * which duplicates a bunch of code because the callback isn't
- * invoked for SYNC commands, if it were and its result passed
- * through it would be simpler...)
- */
- void (*callback)(struct iwl_priv *priv,
- struct iwl_device_cmd *cmd,
- struct iwl_rx_packet *pkt);
-
- u32 flags;
-
- DEFINE_DMA_UNMAP_ADDR(mapping);
- DEFINE_DMA_UNMAP_LEN(len);
-};
-
-/*
- * Generic queue structure
- *
- * Contains common data for Rx and Tx queues.
- *
- * Note the difference between n_bd and n_window: the hardware
- * always assumes 256 descriptors, so n_bd is always 256 (unless
- * there might be HW changes in the future). For the normal TX
- * queues, n_window, which is the size of the software queue data
- * is also 256; however, for the command queue, n_window is only
- * 32 since we don't need so many commands pending. Since the HW
- * still uses 256 BDs for DMA though, n_bd stays 256. As a result,
- * the software buffers (in the variables @meta, @txb in struct
- * iwl_tx_queue) only have 32 entries, while the HW buffers (@tfds
- * in the same struct) have 256.
- * This means that we end up with the following:
- * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 |
- * SW entries: | 0 | ... | 31 |
- * where N is a number between 0 and 7. This means that the SW
- * data is a window overlayed over the HW queue.
- */
-struct iwl_queue {
- int n_bd; /* number of BDs in this queue */
- int write_ptr; /* 1-st empty entry (index) host_w*/
- int read_ptr; /* last used entry (index) host_r*/
- /* use for monitoring and recovering the stuck queue */
- dma_addr_t dma_addr; /* physical addr for BD's */
- int n_window; /* safe queue window */
- u32 id;
- int low_mark; /* low watermark, resume queue if free
- * space more than this */
- int high_mark; /* high watermark, stop queue if free
- * space less than this */
-};
-
-/* One for each TFD */
-struct iwl_tx_info {
- struct sk_buff *skb;
- struct iwl_rxon_context *ctx;
-};
-
-/**
- * struct iwl_tx_queue - Tx Queue for DMA
- * @q: generic Rx/Tx queue descriptor
- * @bd: base of circular buffer of TFDs
- * @cmd: array of command/TX buffer pointers
- * @meta: array of meta data for each command/tx buffer
- * @dma_addr_cmd: physical address of cmd/tx buffer array
- * @txb: array of per-TFD driver data
- * @time_stamp: time (in jiffies) of last read_ptr change
- * @need_update: indicates need to update read/write index
- * @sched_retry: indicates queue is high-throughput aggregation (HT AGG) enabled
- *
- * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
- * descriptors) and required locking structures.
- */
-#define TFD_TX_CMD_SLOTS 256
-#define TFD_CMD_SLOTS 32
-
-struct iwl_tx_queue {
- struct iwl_queue q;
- struct iwl_tfd *tfds;
- struct iwl_device_cmd **cmd;
- struct iwl_cmd_meta *meta;
- struct iwl_tx_info *txb;
- unsigned long time_stamp;
- u8 need_update;
- u8 sched_retry;
- u8 active;
- u8 swq_id;
-};
-
#define IWL_NUM_SCAN_RATES (2)
/*
@@ -217,20 +117,16 @@ struct iwl_channel_info {
u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */
};
-#define IWL_TX_FIFO_BK 0 /* shared */
-#define IWL_TX_FIFO_BE 1
-#define IWL_TX_FIFO_VI 2 /* shared */
-#define IWL_TX_FIFO_VO 3
-#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK
-#define IWL_TX_FIFO_BE_IPAN 4
-#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI
-#define IWL_TX_FIFO_VO_IPAN 5
-#define IWL_TX_FIFO_UNUSED -1
-
-/* Minimum number of queues. MAX_NUM is defined in hw specific files.
- * Set the minimum to accommodate the 4 standard TX queues, 1 command
- * queue, 2 (unused) HCCA queues, and 4 HT queues (one for each AC) */
-#define IWL_MIN_NUM_QUEUES 10
+/*
+ * Minimum number of queues. MAX_NUM is defined in hw specific files.
+ * Set the minimum to accommodate
+ * - 4 standard TX queues
+ * - the command queue
+ * - 4 PAN TX queues
+ * - the PAN multicast queue, and
+ * - the AUX (TX during scan dwell) queue.
+ */
+#define IWL_MIN_NUM_QUEUES 11
/*
* Command queue depends on iPAN support.
@@ -238,160 +134,20 @@ struct iwl_channel_info {
#define IWL_DEFAULT_CMD_QUEUE_NUM 4
#define IWL_IPAN_CMD_QUEUE_NUM 9
-/*
- * This queue number is required for proper operation
- * because the ucode will stop/start the scheduler as
- * required.
- */
-#define IWL_IPAN_MCAST_QUEUE 8
-
#define IEEE80211_DATA_LEN 2304
#define IEEE80211_4ADDR_LEN 30
#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN)
#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
-
-#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
-#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
-#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
-
-enum {
- CMD_SYNC = 0,
- CMD_SIZE_NORMAL = 0,
- CMD_NO_SKB = 0,
- CMD_ASYNC = (1 << 1),
- CMD_WANT_SKB = (1 << 2),
- CMD_MAPPED = (1 << 3),
-};
-
-#define DEF_CMD_PAYLOAD_SIZE 320
-
-/**
- * struct iwl_device_cmd
- *
- * For allocation of the command and tx queues, this establishes the overall
- * size of the largest command we send to uCode, except for commands that
- * aren't fully copied and use other TFD space.
- */
-struct iwl_device_cmd {
- struct iwl_cmd_header hdr; /* uCode API */
- union {
- u32 flags;
- u8 val8;
- u16 val16;
- u32 val32;
- struct iwl_tx_cmd tx;
- struct iwl6000_channel_switch_cmd chswitch;
- u8 payload[DEF_CMD_PAYLOAD_SIZE];
- } __packed cmd;
-} __packed;
-
-#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd))
-
-#define IWL_MAX_CMD_TFDS 2
-
-enum iwl_hcmd_dataflag {
- IWL_HCMD_DFL_NOCOPY = BIT(0),
-};
-
-struct iwl_host_cmd {
- const void *data[IWL_MAX_CMD_TFDS];
- unsigned long reply_page;
- void (*callback)(struct iwl_priv *priv,
- struct iwl_device_cmd *cmd,
- struct iwl_rx_packet *pkt);
- u32 flags;
- u16 len[IWL_MAX_CMD_TFDS];
- u8 dataflags[IWL_MAX_CMD_TFDS];
- u8 id;
-};
-
#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
-/**
- * struct iwl_rx_queue - Rx queue
- * @bd: driver's pointer to buffer of receive buffer descriptors (rbd)
- * @bd_dma: bus address of buffer of receive buffer descriptors (rbd)
- * @read: Shared index to newest available Rx buffer
- * @write: Shared index to oldest written Rx packet
- * @free_count: Number of pre-allocated buffers in rx_free
- * @rx_free: list of free SKBs for use
- * @rx_used: List of Rx buffers with no SKB
- * @need_update: flag to indicate we need to update read/write index
- * @rb_stts: driver's pointer to receive buffer status
- * @rb_stts_dma: bus address of receive buffer status
- *
- * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers
- */
-struct iwl_rx_queue {
- __le32 *bd;
- dma_addr_t bd_dma;
- struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS];
- struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE];
- u32 read;
- u32 write;
- u32 free_count;
- u32 write_actual;
- struct list_head rx_free;
- struct list_head rx_used;
- int need_update;
- struct iwl_rb_status *rb_stts;
- dma_addr_t rb_stts_dma;
- spinlock_t lock;
-};
-
#define IWL_SUPPORTED_RATES_IE_LEN 8
-#define MAX_TID_COUNT 9
-
#define IWL_INVALID_RATE 0xFF
#define IWL_INVALID_VALUE -1
-/**
- * struct iwl_ht_agg -- aggregation status while waiting for block-ack
- * @txq_id: Tx queue used for Tx attempt
- * @frame_count: # frames attempted by Tx command
- * @wait_for_ba: Expect block-ack before next Tx reply
- * @start_idx: Index of 1st Transmit Frame Descriptor (TFD) in Tx window
- * @bitmap0: Low order bitmap, one bit for each frame pending ACK in Tx window
- * @bitmap1: High order, one bit for each frame pending ACK in Tx window
- * @rate_n_flags: Rate at which Tx was attempted
- *
- * If REPLY_TX indicates that aggregation was attempted, driver must wait
- * for block ack (REPLY_COMPRESSED_BA). This struct stores tx reply info
- * until block ack arrives.
- */
-struct iwl_ht_agg {
- u16 txq_id;
- u16 frame_count;
- u16 wait_for_ba;
- u16 start_idx;
- u64 bitmap;
- u32 rate_n_flags;
-#define IWL_AGG_OFF 0
-#define IWL_AGG_ON 1
-#define IWL_EMPTYING_HW_QUEUE_ADDBA 2
-#define IWL_EMPTYING_HW_QUEUE_DELBA 3
- u8 state;
- u8 tx_fifo;
-};
-
-
-struct iwl_tid_data {
- u16 seq_number; /* agn only */
- u16 tfds_in_queue;
- struct iwl_ht_agg agg;
-};
-
-struct iwl_hw_key {
- u32 cipher;
- int keylen;
- u8 keyidx;
- u8 key[32];
-};
-
union iwl_ht_rate_supp {
u16 rates;
struct {
@@ -442,17 +198,10 @@ struct iwl_qos_info {
*/
struct iwl_station_entry {
struct iwl_addsta_cmd sta;
- struct iwl_tid_data tid[MAX_TID_COUNT];
u8 used, ctxid;
- struct iwl_hw_key keyinfo;
struct iwl_link_quality_cmd *lq;
};
-struct iwl_station_priv_common {
- struct iwl_rxon_context *ctx;
- u8 sta_id;
-};
-
/*
* iwl_station_priv: Driver's private station information
*
@@ -461,12 +210,13 @@ struct iwl_station_priv_common {
* space.
*/
struct iwl_station_priv {
- struct iwl_station_priv_common common;
+ struct iwl_rxon_context *ctx;
struct iwl_lq_sta lq_sta;
atomic_t pending_frames;
bool client;
bool asleep;
u8 max_agg_bufsize;
+ u8 sta_id;
};
/**
@@ -547,7 +297,8 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_INIT_ERRLOG_PTR = 13,
IWL_UCODE_TLV_ENHANCE_SENS_TBL = 14,
IWL_UCODE_TLV_PHY_CALIBRATION_SIZE = 15,
- /* 16 and 17 reserved for future use */
+ IWL_UCODE_TLV_WOWLAN_INST = 16,
+ IWL_UCODE_TLV_WOWLAN_DATA = 17,
IWL_UCODE_TLV_FLAGS = 18,
};
@@ -558,11 +309,13 @@ enum iwl_ucode_tlv_type {
* @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID,
* treats good CRC threshold as a boolean
* @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
+ * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
*/
enum iwl_ucode_tlv_flag {
IWL_UCODE_TLV_FLAGS_PAN = BIT(0),
IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1),
IWL_UCODE_TLV_FLAGS_MFP = BIT(2),
+ IWL_UCODE_TLV_FLAGS_P2P = BIT(3),
};
struct iwl_ucode_tlv {
@@ -628,57 +381,6 @@ struct iwl_sensitivity_ranges {
#define CELSIUS_TO_KELVIN(x) ((x)+273)
-/**
- * struct iwl_hw_params
- * @max_txq_num: Max # Tx queues supported
- * @dma_chnl_num: Number of Tx DMA/FIFO channels
- * @scd_bc_tbls_size: size of scheduler byte count tables
- * @tfd_size: TFD size
- * @tx/rx_chains_num: Number of TX/RX chains
- * @valid_tx/rx_ant: usable antennas
- * @max_rxq_size: Max # Rx frames in Rx queue (must be power-of-2)
- * @max_rxq_log: Log-base-2 of max_rxq_size
- * @rx_page_order: Rx buffer page order
- * @rx_wrt_ptr_reg: FH{39}_RSCSR_CHNL0_WPTR
- * @max_stations:
- * @ht40_channel: is 40MHz width possible in band 2.4
- * BIT(IEEE80211_BAND_5GHZ) BIT(IEEE80211_BAND_5GHZ)
- * @sw_crypto: 0 for hw, 1 for sw
- * @max_xxx_size: for ucode uses
- * @ct_kill_threshold: temperature threshold
- * @beacon_time_tsf_bits: number of valid tsf bits for beacon time
- * @calib_init_cfg: setup initial calibrations for the hw
- * @calib_rt_cfg: setup runtime calibrations for the hw
- * @struct iwl_sensitivity_ranges: range of sensitivity values
- */
-struct iwl_hw_params {
- u8 max_txq_num;
- u8 dma_chnl_num;
- u16 scd_bc_tbls_size;
- u32 tfd_size;
- u8 tx_chains_num;
- u8 rx_chains_num;
- u8 valid_tx_ant;
- u8 valid_rx_ant;
- u16 max_rxq_size;
- u16 max_rxq_log;
- u32 rx_page_order;
- u32 rx_wrt_ptr_reg;
- u8 max_stations;
- u8 ht40_channel;
- u8 max_beacon_itrvl; /* in 1024 ms */
- u32 max_inst_size;
- u32 max_data_size;
- u32 ct_kill_threshold; /* value in hw-dependent units */
- u32 ct_kill_exit_threshold; /* value in hw-dependent units */
- /* for 1000, 6000 series and up */
- u16 beacon_time_tsf_bits;
- u32 calib_init_cfg;
- u32 calib_rt_cfg;
- const struct iwl_sensitivity_ranges *sens;
-};
-
-
/******************************************************************************
*
* Functions implemented in core module which are forward declared here
@@ -694,37 +396,12 @@ struct iwl_hw_params {
****************************************************************************/
extern void iwl_update_chain_flags(struct iwl_priv *priv);
extern const u8 iwl_bcast_addr[ETH_ALEN];
-extern int iwl_rxq_stop(struct iwl_priv *priv);
-extern void iwl_txq_ctx_stop(struct iwl_priv *priv);
-extern int iwl_queue_space(const struct iwl_queue *q);
-static inline int iwl_queue_used(const struct iwl_queue *q, int i)
-{
- return q->write_ptr >= q->read_ptr ?
- (i >= q->read_ptr && i < q->write_ptr) :
- !(i < q->read_ptr && i >= q->write_ptr);
-}
-
-
-static inline u8 get_cmd_index(struct iwl_queue *q, u32 index)
-{
- return index & (q->n_window - 1);
-}
-
-
-struct iwl_dma_ptr {
- dma_addr_t dma;
- void *addr;
- size_t size;
-};
#define IWL_OPERATION_MODE_AUTO 0
#define IWL_OPERATION_MODE_HT_ONLY 1
#define IWL_OPERATION_MODE_MIXED 2
#define IWL_OPERATION_MODE_20MHZ 3
-#define IWL_TX_CRC_SIZE 4
-#define IWL_TX_DELIMITER_SIZE 4
-
#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000
/* Sensitivity and chain noise calibration */
@@ -848,9 +525,6 @@ struct iwl_chain_noise_data {
#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */
#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */
-#define IWL_TRAFFIC_ENTRIES (256)
-#define IWL_TRAFFIC_ENTRY_SIZE (64)
-
enum {
MEASUREMENT_READY = (1 << 0),
MEASUREMENT_ACTIVE = (1 << 1),
@@ -883,22 +557,6 @@ enum iwl_pa_type {
IWL_PA_INTERNAL = 1,
};
-/* interrupt statistics */
-struct isr_statistics {
- u32 hw;
- u32 sw;
- u32 err_code;
- u32 sch;
- u32 alive;
- u32 rfkill;
- u32 ctkill;
- u32 wakeup;
- u32 rx;
- u32 rx_handlers[REPLY_MAX];
- u32 tx;
- u32 unhandled;
-};
-
/* reply_tx_statistics (for _agn devices) */
struct reply_tx_error_statistics {
u32 pp_delay;
@@ -1008,21 +666,6 @@ struct iwl_event_log {
};
/*
- * host interrupt timeout value
- * used with setting interrupt coalescing timer
- * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit
- *
- * default interrupt coalescing timer is 64 x 32 = 2048 usecs
- * default interrupt coalescing calibration timer is 16 x 32 = 512 usecs
- */
-#define IWL_HOST_INT_TIMEOUT_MAX (0xFF)
-#define IWL_HOST_INT_TIMEOUT_DEF (0x40)
-#define IWL_HOST_INT_TIMEOUT_MIN (0x0)
-#define IWL_HOST_INT_CALIB_TIMEOUT_MAX (0xFF)
-#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10)
-#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0)
-
-/*
* This is the threshold value of plcp error rate per 100mSecs. It is
* used to set and check for the validity of plcp_delta.
*/
@@ -1100,20 +743,9 @@ struct iwl_notification_wait {
bool triggered, aborted;
};
-enum iwl_rxon_context_id {
- IWL_RXON_CTX_BSS,
- IWL_RXON_CTX_PAN,
-
- NUM_IWL_RXON_CTX
-};
-
struct iwl_rxon_context {
struct ieee80211_vif *vif;
- const u8 *ac_to_fifo;
- const u8 *ac_to_queue;
- u8 mcast_queue;
-
/*
* We could use the vif to indicate active, but we
* also need it to be active during disabling when
@@ -1152,6 +784,8 @@ struct iwl_rxon_context {
__le32 station_flags;
+ int beacon_int;
+
struct {
bool non_gf_sta_present;
u8 protection;
@@ -1159,37 +793,57 @@ struct iwl_rxon_context {
u8 extension_chan_offset;
} ht;
+ u8 bssid[ETH_ALEN];
+ bool preauth_bssid;
+
bool last_tx_rejected;
};
enum iwl_scan_type {
IWL_SCAN_NORMAL,
IWL_SCAN_RADIO_RESET,
- IWL_SCAN_OFFCH_TX,
+ IWL_SCAN_ROC,
+};
+
+enum iwlagn_ucode_type {
+ IWL_UCODE_NONE,
+ IWL_UCODE_REGULAR,
+ IWL_UCODE_INIT,
+ IWL_UCODE_WOWLAN,
};
#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL
struct iwl_testmode_trace {
+ u32 buff_size;
+ u32 total_size;
+ u32 num_chunks;
u8 *cpu_addr;
u8 *trace_addr;
dma_addr_t dma_addr;
bool trace_enabled;
};
#endif
+
struct iwl_priv {
+ /*data shared among all the driver's layers */
+ struct iwl_shared _shrd;
+ struct iwl_shared *shrd;
+
/* ieee device used by generic ieee processing code */
struct ieee80211_hw *hw;
struct ieee80211_channel *ieee_channels;
struct ieee80211_rate *ieee_rates;
+ struct kmem_cache *tx_cmd_pool;
struct iwl_cfg *cfg;
enum ieee80211_band band;
void (*pre_rx_handler)(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb);
- void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb);
+ int (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd);
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
@@ -1207,6 +861,9 @@ struct iwl_priv {
/* jiffies when last recovery from statistics was performed */
unsigned long rx_statistics_jiffies;
+ /*counters */
+ u32 rx_handlers_stats[REPLY_MAX];
+
/* force reset */
struct iwl_force_reset force_reset[IWL_MAX_FORCE_RESET];
@@ -1220,7 +877,7 @@ struct iwl_priv {
u8 channel_count; /* # of channels */
/* thermal calibration */
- s32 temperature; /* degrees Kelvin */
+ s32 temperature; /* Celsius */
s32 last_temperature;
/* init calibration results */
@@ -1237,24 +894,6 @@ struct iwl_priv {
u8 scan_tx_ant[IEEE80211_NUM_BANDS];
u8 mgmt_tx_ant;
- /* spinlock */
- spinlock_t lock; /* protect general shared data */
- spinlock_t hcmd_lock; /* protect hcmd */
- spinlock_t reg_lock; /* protect hw register access */
- struct mutex mutex;
-
- /* basic pci-network driver stuff */
- struct pci_dev *pci_dev;
-
- /* pci hardware address support */
- void __iomem *hw_base;
-
- /* microcode/device supports multiple contexts */
- u8 valid_contexts;
-
- /* command queue number */
- u8 cmd_queue;
-
/* max number of station keys */
u8 sta_key_max_num;
@@ -1267,10 +906,12 @@ struct iwl_priv {
int fw_index; /* firmware we're trying to load */
u32 ucode_ver; /* version of ucode, copy of
iwl_ucode.ver */
+
struct fw_img ucode_rt;
struct fw_img ucode_init;
+ struct fw_img ucode_wowlan;
- enum iwlagn_ucode_subtype ucode_type;
+ enum iwlagn_ucode_type ucode_type;
u8 ucode_write_complete; /* the image write is complete */
char firmware_name[25];
@@ -1297,50 +938,25 @@ struct iwl_priv {
/* Rate scaling data */
u8 retry_rate;
- wait_queue_head_t wait_command_queue;
-
int activity_timer_active;
- /* Rx and Tx DMA processing queues */
- struct iwl_rx_queue rxq;
- struct iwl_tx_queue *txq;
- unsigned long txq_ctx_active_msk;
- struct iwl_dma_ptr kw; /* keep warm address */
- struct iwl_dma_ptr scd_bc_tbls;
-
- u32 scd_base_addr; /* scheduler sram base address */
-
- unsigned long status;
-
/* counts mgmt, ctl, and data packets */
struct traffic_stats tx_stats;
struct traffic_stats rx_stats;
- /* counts interrupts */
- struct isr_statistics isr_stats;
-
struct iwl_power_mgr power_data;
struct iwl_tt_mgmt thermal_throttle;
/* station table variables */
-
- /* Note: if lock and sta_lock are needed, lock must be acquired first */
- spinlock_t sta_lock;
int num_stations;
struct iwl_station_entry stations[IWLAGN_STATION_COUNT];
unsigned long ucode_key_table;
- /* queue refcounts */
-#define IWL_MAX_HW_QUEUES 32
- unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
- /* for each AC */
- atomic_t queue_stop_count[4];
+ u8 mac80211_registered;
/* Indication if ieee80211_ops->open has been called */
u8 is_open;
- u8 mac80211_registered;
-
/* eeprom -- this is in the card's little endian byte order */
u8 *eeprom;
int nvm_device_type;
@@ -1376,56 +992,42 @@ struct iwl_priv {
} accum_stats, delta_stats, max_delta_stats;
#endif
- struct {
- /* INT ICT Table */
- __le32 *ict_tbl;
- void *ict_tbl_vir;
- dma_addr_t ict_tbl_dma;
- dma_addr_t aligned_ict_tbl_dma;
- int ict_index;
- u32 inta;
- bool use_ict;
- /*
- * reporting the number of tids has AGG on. 0 means
- * no AGGREGATION
- */
- u8 agg_tids_count;
-
- struct iwl_rx_phy_res last_phy_res;
- bool last_phy_res_valid;
-
- struct completion firmware_loading_complete;
-
- u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
- u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
-
- /*
- * chain noise reset and gain commands are the
- * two extra calibration commands follows the standard
- * phy calibration commands
- */
- u8 phy_calib_chain_noise_reset_cmd;
- u8 phy_calib_chain_noise_gain_cmd;
-
- /* counts reply_tx error */
- struct reply_tx_error_statistics reply_tx_stats;
- struct reply_agg_tx_error_statistics reply_agg_tx_stats;
- /* notification wait support */
- struct list_head notif_waits;
- spinlock_t notif_wait_lock;
- wait_queue_head_t notif_waitq;
-
- /* remain-on-channel offload support */
- struct ieee80211_channel *hw_roc_channel;
- struct delayed_work hw_roc_work;
- enum nl80211_channel_type hw_roc_chantype;
- int hw_roc_duration;
- bool hw_roc_setup;
-
- struct sk_buff *offchan_tx_skb;
- int offchan_tx_timeout;
- struct ieee80211_channel *offchan_tx_chan;
- } _agn;
+ /*
+ * reporting the number of tids has AGG on. 0 means
+ * no AGGREGATION
+ */
+ u8 agg_tids_count;
+
+ struct iwl_rx_phy_res last_phy_res;
+ bool last_phy_res_valid;
+
+ struct completion firmware_loading_complete;
+
+ u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
+ u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
+
+ /*
+ * chain noise reset and gain commands are the
+ * two extra calibration commands follows the standard
+ * phy calibration commands
+ */
+ u8 phy_calib_chain_noise_reset_cmd;
+ u8 phy_calib_chain_noise_gain_cmd;
+
+ /* counts reply_tx error */
+ struct reply_tx_error_statistics reply_tx_stats;
+ struct reply_agg_tx_error_statistics reply_agg_tx_stats;
+ /* notification wait support */
+ struct list_head notif_waits;
+ spinlock_t notif_wait_lock;
+ wait_queue_head_t notif_waitq;
+
+ /* remain-on-channel offload support */
+ struct ieee80211_channel *hw_roc_channel;
+ struct delayed_work hw_roc_disable_work;
+ enum nl80211_channel_type hw_roc_chantype;
+ int hw_roc_duration;
+ bool hw_roc_setup, hw_roc_start_notified;
/* bt coex */
u8 bt_enable_flag;
@@ -1442,16 +1044,12 @@ struct iwl_priv {
u16 dynamic_frag_thresh;
u8 bt_ci_compliance;
struct work_struct bt_traffic_change_work;
-
- struct iwl_hw_params hw_params;
-
- u32 inta_mask;
-
- struct workqueue_struct *workqueue;
+ bool bt_enable_pspoll;
+ struct iwl_rxon_context *cur_rssi_ctx;
+ bool bt_is_sco;
struct work_struct restart;
struct work_struct scan_completed;
- struct work_struct rx_replenish;
struct work_struct abort_scan;
struct work_struct beacon_update;
@@ -1467,8 +1065,6 @@ struct iwl_priv {
struct work_struct bt_full_concurrency;
struct work_struct bt_runtime_config;
- struct tasklet_struct irq_tasklet;
-
struct delayed_work scan_check;
/* TX Power */
@@ -1477,12 +1073,6 @@ struct iwl_priv {
s8 tx_power_lmt_in_half_dbm; /* max tx power in half-dBm format */
s8 tx_power_next;
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- /* debugging info */
- u32 debug_level; /* per device debugging will override global
- iwl_debug_level if set */
-#endif /* CONFIG_IWLWIFI_DEBUG */
#ifdef CONFIG_IWLWIFI_DEBUGFS
/* debugfs */
u16 tx_traffic_idx;
@@ -1492,6 +1082,7 @@ struct iwl_priv {
struct dentry *debugfs_dir;
u32 dbgfs_sram_offset, dbgfs_sram_len;
bool disable_ht40;
+ void *wowlan_sram;
#endif /* CONFIG_IWLWIFI_DEBUGFS */
struct work_struct txpower_work;
@@ -1509,52 +1100,17 @@ struct iwl_priv {
bool led_registered;
#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL
struct iwl_testmode_trace testmode_trace;
+ u32 tm_fixed_rate;
#endif
- u32 dbg_fixed_rate;
+ /* WoWLAN GTK rekey data */
+ u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
+ __le64 replay_ctr;
+ __le16 last_seq_ctl;
+ bool have_rekey_data;
}; /*iwl_priv */
-static inline void iwl_txq_ctx_activate(struct iwl_priv *priv, int txq_id)
-{
- set_bit(txq_id, &priv->txq_ctx_active_msk);
-}
-
-static inline void iwl_txq_ctx_deactivate(struct iwl_priv *priv, int txq_id)
-{
- clear_bit(txq_id, &priv->txq_ctx_active_msk);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-/*
- * iwl_get_debug_level: Return active debug level for device
- *
- * Using sysfs it is possible to set per device debug level. This debug
- * level will be used if set, otherwise the global debug level which can be
- * set via module parameter is used.
- */
-static inline u32 iwl_get_debug_level(struct iwl_priv *priv)
-{
- if (priv->debug_level)
- return priv->debug_level;
- else
- return iwl_debug_level;
-}
-#else
-static inline u32 iwl_get_debug_level(struct iwl_priv *priv)
-{
- return iwl_debug_level;
-}
-#endif
-
-
-static inline struct ieee80211_hdr *iwl_tx_queue_get_hdr(struct iwl_priv *priv,
- int txq_id, int idx)
-{
- if (priv->txq[txq_id].txb[idx].skb)
- return (struct ieee80211_hdr *)priv->txq[txq_id].
- txb[idx].skb->data;
- return NULL;
-}
+extern struct iwl_mod_params iwlagn_mod_params;
static inline struct iwl_rxon_context *
iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif)
@@ -1567,7 +1123,7 @@ iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif)
#define for_each_context(priv, ctx) \
for (ctx = &priv->contexts[IWL_RXON_CTX_BSS]; \
ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \
- if (priv->valid_contexts & BIT(ctx->ctxid))
+ if (priv->shrd->valid_contexts & BIT(ctx->ctxid))
static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx)
{
@@ -1621,13 +1177,4 @@ static inline int is_channel_ibss(const struct iwl_channel_info *ch)
return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0;
}
-static inline void __iwl_free_pages(struct iwl_priv *priv, struct page *page)
-{
- __free_pages(page, priv->hw_params.rx_page_order);
-}
-
-static inline void iwl_free_pages(struct iwl_priv *priv, unsigned long page)
-{
- free_pages(page, priv->hw_params.rx_page_order);
-}
#endif /* __iwl_dev_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
index 2c84ba95afca..8a51c5ccda1e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
@@ -29,6 +29,8 @@
#include <linux/tracepoint.h>
+struct iwl_priv;
+
#if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__)
#undef TRACE_EVENT
#define TRACE_EVENT(name, proto, ...) \
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c
index 47a56bc1cd12..a4e43bd4a547 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c
@@ -72,6 +72,7 @@
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-debug.h"
+#include "iwl-agn.h"
#include "iwl-eeprom.h"
#include "iwl-io.h"
@@ -138,7 +139,7 @@ static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */
/******************************************************************************
*
- * EEPROM related functions
+ * generic NVM functions
*
******************************************************************************/
@@ -155,11 +156,11 @@ static int iwl_eeprom_acquire_semaphore(struct iwl_priv *priv)
for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) {
/* Request semaphore */
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+ iwl_set_bit(bus(priv), CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
/* See if we got it */
- ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
+ ret = iwl_poll_bit(bus(priv), CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
EEPROM_SEM_TIMEOUT);
@@ -176,14 +177,14 @@ static int iwl_eeprom_acquire_semaphore(struct iwl_priv *priv)
static void iwl_eeprom_release_semaphore(struct iwl_priv *priv)
{
- iwl_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
+ iwl_clear_bit(bus(priv), CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
}
static int iwl_eeprom_verify_signature(struct iwl_priv *priv)
{
- u32 gp = iwl_read32(priv, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK;
+ u32 gp = iwl_read32(bus(priv), CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK;
int ret = 0;
IWL_DEBUG_EEPROM(priv, "EEPROM signature=0x%08x\n", gp);
@@ -214,19 +215,106 @@ static int iwl_eeprom_verify_signature(struct iwl_priv *priv)
return ret;
}
+u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset)
+{
+ if (!priv->eeprom)
+ return 0;
+ return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8);
+}
+
+int iwl_eeprom_check_version(struct iwl_priv *priv)
+{
+ u16 eeprom_ver;
+ u16 calib_ver;
+
+ eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
+ calib_ver = iwlagn_eeprom_calib_version(priv);
+
+ if (eeprom_ver < priv->cfg->eeprom_ver ||
+ calib_ver < priv->cfg->eeprom_calib_ver)
+ goto err;
+
+ IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n",
+ eeprom_ver, calib_ver);
+
+ return 0;
+err:
+ IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x "
+ "CALIB=0x%x < 0x%x\n",
+ eeprom_ver, priv->cfg->eeprom_ver,
+ calib_ver, priv->cfg->eeprom_calib_ver);
+ return -EINVAL;
+
+}
+
+int iwl_eeprom_check_sku(struct iwl_priv *priv)
+{
+ u16 radio_cfg;
+
+ if (!priv->cfg->sku) {
+ /* not using sku overwrite */
+ priv->cfg->sku = iwl_eeprom_query16(priv, EEPROM_SKU_CAP);
+ if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE &&
+ !priv->cfg->ht_params) {
+ IWL_ERR(priv, "Invalid 11n configuration\n");
+ return -EINVAL;
+ }
+ }
+ if (!priv->cfg->sku) {
+ IWL_ERR(priv, "Invalid device sku\n");
+ return -EINVAL;
+ }
+
+ IWL_INFO(priv, "Device SKU: 0X%x\n", priv->cfg->sku);
+
+ if (!priv->cfg->valid_tx_ant && !priv->cfg->valid_rx_ant) {
+ /* not using .cfg overwrite */
+ radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
+ priv->cfg->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg);
+ priv->cfg->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg);
+ if (!priv->cfg->valid_tx_ant || !priv->cfg->valid_rx_ant) {
+ IWL_ERR(priv, "Invalid chain (0X%x, 0X%x)\n",
+ priv->cfg->valid_tx_ant,
+ priv->cfg->valid_rx_ant);
+ return -EINVAL;
+ }
+ IWL_INFO(priv, "Valid Tx ant: 0X%x, Valid Rx ant: 0X%x\n",
+ priv->cfg->valid_tx_ant, priv->cfg->valid_rx_ant);
+ }
+ /*
+ * for some special cases,
+ * EEPROM did not reflect the correct antenna setting
+ * so overwrite the valid tx/rx antenna from .cfg
+ */
+ return 0;
+}
+
+void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac)
+{
+ const u8 *addr = iwl_eeprom_query_addr(priv,
+ EEPROM_MAC_ADDRESS);
+ memcpy(mac, addr, ETH_ALEN);
+}
+
+/******************************************************************************
+ *
+ * OTP related functions
+ *
+******************************************************************************/
+
static void iwl_set_otp_access(struct iwl_priv *priv, enum iwl_access_mode mode)
{
- iwl_read32(priv, CSR_OTP_GP_REG);
+ iwl_read32(bus(priv), CSR_OTP_GP_REG);
if (mode == IWL_OTP_ACCESS_ABSOLUTE)
- iwl_clear_bit(priv, CSR_OTP_GP_REG,
+ iwl_clear_bit(bus(priv), CSR_OTP_GP_REG,
CSR_OTP_GP_REG_OTP_ACCESS_MODE);
else
- iwl_set_bit(priv, CSR_OTP_GP_REG,
+ iwl_set_bit(bus(priv), CSR_OTP_GP_REG,
CSR_OTP_GP_REG_OTP_ACCESS_MODE);
}
-static int iwlcore_get_nvm_type(struct iwl_priv *priv, u32 hw_rev)
+static int iwl_get_nvm_type(struct iwl_priv *priv, u32 hw_rev)
{
u32 otpgp;
int nvm_type;
@@ -243,7 +331,7 @@ static int iwlcore_get_nvm_type(struct iwl_priv *priv, u32 hw_rev)
nvm_type = NVM_DEVICE_TYPE_EEPROM;
break;
default:
- otpgp = iwl_read32(priv, CSR_OTP_GP_REG);
+ otpgp = iwl_read32(bus(priv), CSR_OTP_GP_REG);
if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT)
nvm_type = NVM_DEVICE_TYPE_OTP;
else
@@ -258,22 +346,22 @@ static int iwl_init_otp_access(struct iwl_priv *priv)
int ret;
/* Enable 40MHz radio clock */
- iwl_write32(priv, CSR_GP_CNTRL,
- iwl_read32(priv, CSR_GP_CNTRL) |
+ iwl_write32(bus(priv), CSR_GP_CNTRL,
+ iwl_read32(bus(priv), CSR_GP_CNTRL) |
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
/* wait for clock to be ready */
- ret = iwl_poll_bit(priv, CSR_GP_CNTRL,
+ ret = iwl_poll_bit(bus(priv), CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
25000);
if (ret < 0)
IWL_ERR(priv, "Time out access OTP\n");
else {
- iwl_set_bits_prph(priv, APMG_PS_CTRL_REG,
+ iwl_set_bits_prph(bus(priv), APMG_PS_CTRL_REG,
APMG_PS_CTRL_VAL_RESET_REQ);
udelay(5);
- iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG,
+ iwl_clear_bits_prph(bus(priv), APMG_PS_CTRL_REG,
APMG_PS_CTRL_VAL_RESET_REQ);
/*
@@ -281,7 +369,7 @@ static int iwl_init_otp_access(struct iwl_priv *priv)
* this is only applicable for HW with OTP shadow RAM
*/
if (priv->cfg->base_params->shadow_ram_support)
- iwl_set_bit(priv, CSR_DBG_LINK_PWR_MGMT_REG,
+ iwl_set_bit(bus(priv), CSR_DBG_LINK_PWR_MGMT_REG,
CSR_RESET_LINK_PWR_MGMT_DISABLED);
}
return ret;
@@ -293,9 +381,9 @@ static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, __le16 *eeprom_dat
u32 r;
u32 otpgp;
- iwl_write32(priv, CSR_EEPROM_REG,
+ iwl_write32(bus(priv), CSR_EEPROM_REG,
CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
- ret = iwl_poll_bit(priv, CSR_EEPROM_REG,
+ ret = iwl_poll_bit(bus(priv), CSR_EEPROM_REG,
CSR_EEPROM_REG_READ_VALID_MSK,
CSR_EEPROM_REG_READ_VALID_MSK,
IWL_EEPROM_ACCESS_TIMEOUT);
@@ -303,13 +391,13 @@ static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, __le16 *eeprom_dat
IWL_ERR(priv, "Time out reading OTP[%d]\n", addr);
return ret;
}
- r = iwl_read32(priv, CSR_EEPROM_REG);
+ r = iwl_read32(bus(priv), CSR_EEPROM_REG);
/* check for ECC errors: */
- otpgp = iwl_read32(priv, CSR_OTP_GP_REG);
+ otpgp = iwl_read32(bus(priv), CSR_OTP_GP_REG);
if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
/* stop in this case */
/* set the uncorrectable OTP ECC bit for acknowledgement */
- iwl_set_bit(priv, CSR_OTP_GP_REG,
+ iwl_set_bit(bus(priv), CSR_OTP_GP_REG,
CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
IWL_ERR(priv, "Uncorrectable OTP ECC error, abort OTP read\n");
return -EINVAL;
@@ -317,7 +405,7 @@ static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, __le16 *eeprom_dat
if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
/* continue in this case */
/* set the correctable OTP ECC bit for acknowledgement */
- iwl_set_bit(priv, CSR_OTP_GP_REG,
+ iwl_set_bit(bus(priv), CSR_OTP_GP_REG,
CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
IWL_ERR(priv, "Correctable OTP ECC error, continue read\n");
}
@@ -407,16 +495,152 @@ static int iwl_find_otp_image(struct iwl_priv *priv,
return -EINVAL;
}
-const u8 *iwl_eeprom_query_addr(const struct iwl_priv *priv, size_t offset)
+/******************************************************************************
+ *
+ * Tx Power related functions
+ *
+******************************************************************************/
+/**
+ * iwl_get_max_txpower_avg - get the highest tx power from all chains.
+ * find the highest tx power from all chains for the channel
+ */
+static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
+ struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
+ int element, s8 *max_txpower_in_half_dbm)
{
- return priv->cfg->ops->lib->eeprom_ops.query_addr(priv, offset);
+ s8 max_txpower_avg = 0; /* (dBm) */
+
+ /* Take the highest tx power from any valid chains */
+ if ((priv->cfg->valid_tx_ant & ANT_A) &&
+ (enhanced_txpower[element].chain_a_max > max_txpower_avg))
+ max_txpower_avg = enhanced_txpower[element].chain_a_max;
+ if ((priv->cfg->valid_tx_ant & ANT_B) &&
+ (enhanced_txpower[element].chain_b_max > max_txpower_avg))
+ max_txpower_avg = enhanced_txpower[element].chain_b_max;
+ if ((priv->cfg->valid_tx_ant & ANT_C) &&
+ (enhanced_txpower[element].chain_c_max > max_txpower_avg))
+ max_txpower_avg = enhanced_txpower[element].chain_c_max;
+ if (((priv->cfg->valid_tx_ant == ANT_AB) |
+ (priv->cfg->valid_tx_ant == ANT_BC) |
+ (priv->cfg->valid_tx_ant == ANT_AC)) &&
+ (enhanced_txpower[element].mimo2_max > max_txpower_avg))
+ max_txpower_avg = enhanced_txpower[element].mimo2_max;
+ if ((priv->cfg->valid_tx_ant == ANT_ABC) &&
+ (enhanced_txpower[element].mimo3_max > max_txpower_avg))
+ max_txpower_avg = enhanced_txpower[element].mimo3_max;
+
+ /*
+ * max. tx power in EEPROM is in 1/2 dBm format
+ * convert from 1/2 dBm to dBm (round-up convert)
+ * but we also do not want to loss 1/2 dBm resolution which
+ * will impact performance
+ */
+ *max_txpower_in_half_dbm = max_txpower_avg;
+ return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1);
}
-u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset)
+static void
+iwl_eeprom_enh_txp_read_element(struct iwl_priv *priv,
+ struct iwl_eeprom_enhanced_txpwr *txp,
+ s8 max_txpower_avg)
{
- if (!priv->eeprom)
- return 0;
- return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8);
+ int ch_idx;
+ bool is_ht40 = txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ;
+ enum ieee80211_band band;
+
+ band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ?
+ IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
+
+ for (ch_idx = 0; ch_idx < priv->channel_count; ch_idx++) {
+ struct iwl_channel_info *ch_info = &priv->channel_info[ch_idx];
+
+ /* update matching channel or from common data only */
+ if (txp->channel != 0 && ch_info->channel != txp->channel)
+ continue;
+
+ /* update matching band only */
+ if (band != ch_info->band)
+ continue;
+
+ if (ch_info->max_power_avg < max_txpower_avg && !is_ht40) {
+ ch_info->max_power_avg = max_txpower_avg;
+ ch_info->curr_txpow = max_txpower_avg;
+ ch_info->scan_power = max_txpower_avg;
+ }
+
+ if (is_ht40 && ch_info->ht40_max_power_avg < max_txpower_avg)
+ ch_info->ht40_max_power_avg = max_txpower_avg;
+ }
+}
+
+#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT)
+#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr)
+#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE)
+
+#define TXP_CHECK_AND_PRINT(x) ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) \
+ ? # x " " : "")
+
+void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv)
+{
+ struct iwl_eeprom_enhanced_txpwr *txp_array, *txp;
+ int idx, entries;
+ __le16 *txp_len;
+ s8 max_txp_avg, max_txp_avg_halfdbm;
+
+ BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8);
+
+ /* the length is in 16-bit words, but we want entries */
+ txp_len = (__le16 *) iwl_eeprom_query_addr(priv, EEPROM_TXP_SZ_OFFS);
+ entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN;
+
+ txp_array = (void *) iwl_eeprom_query_addr(priv, EEPROM_TXP_OFFS);
+
+ for (idx = 0; idx < entries; idx++) {
+ txp = &txp_array[idx];
+ /* skip invalid entries */
+ if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID))
+ continue;
+
+ IWL_DEBUG_EEPROM(priv, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n",
+ (txp->channel && (txp->flags &
+ IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ?
+ "Common " : (txp->channel) ?
+ "Channel" : "Common",
+ (txp->channel),
+ TXP_CHECK_AND_PRINT(VALID),
+ TXP_CHECK_AND_PRINT(BAND_52G),
+ TXP_CHECK_AND_PRINT(OFDM),
+ TXP_CHECK_AND_PRINT(40MHZ),
+ TXP_CHECK_AND_PRINT(HT_AP),
+ TXP_CHECK_AND_PRINT(RES1),
+ TXP_CHECK_AND_PRINT(RES2),
+ TXP_CHECK_AND_PRINT(COMMON_TYPE),
+ txp->flags);
+ IWL_DEBUG_EEPROM(priv, "\t\t chain_A: 0x%02x "
+ "chain_B: 0X%02x chain_C: 0X%02x\n",
+ txp->chain_a_max, txp->chain_b_max,
+ txp->chain_c_max);
+ IWL_DEBUG_EEPROM(priv, "\t\t MIMO2: 0x%02x "
+ "MIMO3: 0x%02x High 20_on_40: 0x%02x "
+ "Low 20_on_40: 0x%02x\n",
+ txp->mimo2_max, txp->mimo3_max,
+ ((txp->delta_20_in_40 & 0xf0) >> 4),
+ (txp->delta_20_in_40 & 0x0f));
+
+ max_txp_avg = iwl_get_max_txpower_avg(priv, txp_array, idx,
+ &max_txp_avg_halfdbm);
+
+ /*
+ * Update the user limit values values to the highest
+ * power supported by any channel
+ */
+ if (max_txp_avg > priv->tx_power_user_lmt)
+ priv->tx_power_user_lmt = max_txp_avg;
+ if (max_txp_avg_halfdbm > priv->tx_power_lmt_in_half_dbm)
+ priv->tx_power_lmt_in_half_dbm = max_txp_avg_halfdbm;
+
+ iwl_eeprom_enh_txp_read_element(priv, txp, max_txp_avg);
+ }
}
/**
@@ -429,14 +653,14 @@ u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset)
int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev)
{
__le16 *e;
- u32 gp = iwl_read32(priv, CSR_EEPROM_GP);
+ u32 gp = iwl_read32(bus(priv), CSR_EEPROM_GP);
int sz;
int ret;
u16 addr;
u16 validblockaddr = 0;
u16 cache_addr = 0;
- priv->nvm_device_type = iwlcore_get_nvm_type(priv, hw_rev);
+ priv->nvm_device_type = iwl_get_nvm_type(priv, hw_rev);
if (priv->nvm_device_type == -ENOENT)
return -ENOENT;
/* allocate eeprom */
@@ -449,7 +673,7 @@ int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev)
}
e = (__le16 *)priv->eeprom;
- priv->cfg->ops->lib->apm_ops.init(priv);
+ iwl_apm_init(priv);
ret = iwl_eeprom_verify_signature(priv);
if (ret < 0) {
@@ -474,11 +698,11 @@ int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev)
ret = -ENOENT;
goto done;
}
- iwl_write32(priv, CSR_EEPROM_GP,
- iwl_read32(priv, CSR_EEPROM_GP) &
+ iwl_write32(bus(priv), CSR_EEPROM_GP,
+ iwl_read32(bus(priv), CSR_EEPROM_GP) &
~CSR_EEPROM_GP_IF_OWNER_MSK);
- iwl_set_bit(priv, CSR_OTP_GP_REG,
+ iwl_set_bit(bus(priv), CSR_OTP_GP_REG,
CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK |
CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
/* traversing the linked list if no shadow ram supported */
@@ -503,10 +727,10 @@ int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev)
for (addr = 0; addr < sz; addr += sizeof(u16)) {
u32 r;
- iwl_write32(priv, CSR_EEPROM_REG,
+ iwl_write32(bus(priv), CSR_EEPROM_REG,
CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
- ret = iwl_poll_bit(priv, CSR_EEPROM_REG,
+ ret = iwl_poll_bit(bus(priv), CSR_EEPROM_REG,
CSR_EEPROM_REG_READ_VALID_MSK,
CSR_EEPROM_REG_READ_VALID_MSK,
IWL_EEPROM_ACCESS_TIMEOUT);
@@ -514,7 +738,7 @@ int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev)
IWL_ERR(priv, "Time out reading EEPROM[%d]\n", addr);
goto done;
}
- r = iwl_read32(priv, CSR_EEPROM_REG);
+ r = iwl_read32(bus(priv), CSR_EEPROM_REG);
e[addr / 2] = cpu_to_le16(r >> 16);
}
}
@@ -548,7 +772,7 @@ static void iwl_init_band_reference(const struct iwl_priv *priv,
const struct iwl_eeprom_channel **eeprom_ch_info,
const u8 **eeprom_ch_index)
{
- u32 offset = priv->cfg->ops->lib->
+ u32 offset = priv->cfg->lib->
eeprom_ops.regulatory_bands[eep_band - 1];
switch (eep_band) {
case 1: /* 2.4GHz band */
@@ -675,8 +899,9 @@ int iwl_init_channel_map(struct iwl_priv *priv)
IWL_DEBUG_EEPROM(priv, "Parsing data for %d channels.\n",
priv->channel_count);
- priv->channel_info = kzalloc(sizeof(struct iwl_channel_info) *
- priv->channel_count, GFP_KERNEL);
+ priv->channel_info = kcalloc(priv->channel_count,
+ sizeof(struct iwl_channel_info),
+ GFP_KERNEL);
if (!priv->channel_info) {
IWL_ERR(priv, "Could not allocate channel_info\n");
priv->channel_count = 0;
@@ -754,9 +979,9 @@ int iwl_init_channel_map(struct iwl_priv *priv)
}
/* Check if we do have HT40 channels */
- if (priv->cfg->ops->lib->eeprom_ops.regulatory_bands[5] ==
+ if (priv->cfg->lib->eeprom_ops.regulatory_bands[5] ==
EEPROM_REGULATORY_BAND_NO_HT40 &&
- priv->cfg->ops->lib->eeprom_ops.regulatory_bands[6] ==
+ priv->cfg->lib->eeprom_ops.regulatory_bands[6] ==
EEPROM_REGULATORY_BAND_NO_HT40)
return 0;
@@ -792,8 +1017,8 @@ int iwl_init_channel_map(struct iwl_priv *priv)
* driver need to process addition information
* to determine the max channel tx power limits
*/
- if (priv->cfg->ops->lib->eeprom_ops.update_enhanced_txpower)
- priv->cfg->ops->lib->eeprom_ops.update_enhanced_txpower(priv);
+ if (priv->cfg->lib->eeprom_ops.update_enhanced_txpower)
+ priv->cfg->lib->eeprom_ops.update_enhanced_txpower(priv);
return 0;
}
@@ -834,3 +1059,28 @@ const struct iwl_channel_info *iwl_get_channel_info(const struct iwl_priv *priv,
return NULL;
}
+
+void iwl_rf_config(struct iwl_priv *priv)
+{
+ u16 radio_cfg;
+
+ radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
+
+ /* write radio config values to register */
+ if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) {
+ iwl_set_bit(bus(priv), CSR_HW_IF_CONFIG_REG,
+ EEPROM_RF_CFG_TYPE_MSK(radio_cfg) |
+ EEPROM_RF_CFG_STEP_MSK(radio_cfg) |
+ EEPROM_RF_CFG_DASH_MSK(radio_cfg));
+ IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n",
+ EEPROM_RF_CFG_TYPE_MSK(radio_cfg),
+ EEPROM_RF_CFG_STEP_MSK(radio_cfg),
+ EEPROM_RF_CFG_DASH_MSK(radio_cfg));
+ } else
+ WARN_ON(1);
+
+ /* set CSR_HW_CONFIG_REG for uCode use */
+ iwl_set_bit(bus(priv), CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
+ CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h
index c960c6fa009b..c94747e7299e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h
@@ -110,12 +110,10 @@ enum {
};
/* SKU Capabilities */
-/* 5000 and up */
-#define EEPROM_SKU_CAP_BAND_POS (4)
-#define EEPROM_SKU_CAP_BAND_SELECTION \
- (3 << EEPROM_SKU_CAP_BAND_POS)
+#define EEPROM_SKU_CAP_BAND_24GHZ (1 << 4)
+#define EEPROM_SKU_CAP_BAND_52GHZ (1 << 5)
#define EEPROM_SKU_CAP_11N_ENABLE (1 << 6)
-#define EEPROM_SKU_CAP_AMT_ENABLE (1 << 7)
+#define EEPROM_SKU_CAP_AMT_ENABLE (1 << 7)
#define EEPROM_SKU_CAP_IPAN_ENABLE (1 << 8)
/* *regulatory* channel data format in eeprom, one for each channel.
@@ -164,16 +162,20 @@ struct iwl_eeprom_enhanced_txpwr {
s8 mimo3_max;
} __packed;
-/* 5000 Specific */
-#define EEPROM_5000_TX_POWER_VERSION (4)
-#define EEPROM_5000_EEPROM_VERSION (0x11A)
+/* calibration */
+struct iwl_eeprom_calib_hdr {
+ u8 version;
+ u8 pa_type;
+ __le16 voltage;
+} __packed;
-/* 5000 and up calibration */
#define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION)
#define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL)
-/* 5000 temperature */
-#define EEPROM_5000_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL)
+/* temperature */
+#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL)
+#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL)
+
/* agn links */
#define EEPROM_LINK_HOST (2*0x64)
@@ -205,6 +207,10 @@ struct iwl_eeprom_enhanced_txpwr {
#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS ((0x80)\
| INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 14 bytes */
+/* 5000 Specific */
+#define EEPROM_5000_TX_POWER_VERSION (4)
+#define EEPROM_5000_EEPROM_VERSION (0x11A)
+
/* 5050 Specific */
#define EEPROM_5050_TX_POWER_VERSION (4)
#define EEPROM_5050_EEPROM_VERSION (0x21E)
@@ -270,13 +276,13 @@ extern const u8 iwl_eeprom_band_1[14];
/* General */
#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */
+#define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */
#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */
#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */
#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */
#define EEPROM_VERSION (2*0x44) /* 2 bytes */
#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */
#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */
-#define EEPROM_WOWLAN_MODE (2*0x47) /* 2 bytes */
#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */
#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */
@@ -294,7 +300,6 @@ extern const u8 iwl_eeprom_band_1[14];
struct iwl_eeprom_ops {
const u32 regulatory_bands[7];
- const u8* (*query_addr) (const struct iwl_priv *priv, size_t offset);
void (*update_enhanced_txpower) (struct iwl_priv *priv);
};
@@ -304,12 +309,12 @@ void iwl_eeprom_free(struct iwl_priv *priv);
int iwl_eeprom_check_version(struct iwl_priv *priv);
int iwl_eeprom_check_sku(struct iwl_priv *priv);
const u8 *iwl_eeprom_query_addr(const struct iwl_priv *priv, size_t offset);
-int iwlcore_eeprom_verify_signature(struct iwl_priv *priv);
u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset);
int iwl_init_channel_map(struct iwl_priv *priv);
void iwl_free_channel_map(struct iwl_priv *priv);
const struct iwl_channel_info *iwl_get_channel_info(
const struct iwl_priv *priv,
enum ieee80211_band band, u16 channel);
+void iwl_rf_config(struct iwl_priv *priv);
#endif /* __iwl_eeprom_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h
index 6dfa806aefec..5bede9d7f955 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fh.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fh.h
@@ -63,6 +63,8 @@
#ifndef __iwl_fh_h__
#define __iwl_fh_h__
+#include <linux/types.h>
+
/****************************/
/* Flow Handler Definitions */
/****************************/
@@ -266,8 +268,6 @@
#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000)
#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000)
-#define FH_RSCSR_FRAME_SIZE_MSK (0x00003FFF) /* bits 0-13 */
-
/**
* Rx Shared Status Registers (RSSR)
*
@@ -326,7 +326,7 @@
#define FH_TCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xE60)
/* Find Control/Status reg for given Tx DMA/FIFO channel */
-#define FH50_TCSR_CHNL_NUM (8)
+#define FH_TCSR_CHNL_NUM (8)
/* TCSR: tx_config register values */
#define FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \
@@ -422,10 +422,6 @@
#define RX_FREE_BUFFERS 64
#define RX_LOW_WATERMARK 8
-/* Size of one Rx buffer in host DRAM */
-#define IWL_RX_BUF_SIZE_4K (4 * 1024)
-#define IWL_RX_BUF_SIZE_8K (8 * 1024)
-
/**
* struct iwl_rb_status - reseve buffer status
* host memory mapped FH registers
@@ -508,4 +504,16 @@ struct iwl_tfd {
/* Keep Warm Size */
#define IWL_KW_SIZE 0x1000 /* 4k */
+/* Fixed (non-configurable) rx data from phy */
+
+/**
+ * struct iwlagn_schedq_bc_tbl scheduler byte count table
+ * base physical address provided by SCD_DRAM_BASE_ADDR
+ * @tfd_offset 0-12 - tx command byte count
+ * 12-16 - station index
+ */
+struct iwlagn_scd_bc_tbl {
+ __le16 tfd_offset[TFD_QUEUE_BC_SIZE];
+} __packed;
+
#endif /* !__iwl_fh_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c
deleted file mode 100644
index 76f996623140..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/******************************************************************************
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *****************************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <net/mac80211.h>
-
-#include "iwl-dev.h" /* FIXME: remove */
-#include "iwl-debug.h"
-#include "iwl-eeprom.h"
-#include "iwl-core.h"
-
-
-const char *get_cmd_string(u8 cmd)
-{
- switch (cmd) {
- IWL_CMD(REPLY_ALIVE);
- IWL_CMD(REPLY_ERROR);
- IWL_CMD(REPLY_RXON);
- IWL_CMD(REPLY_RXON_ASSOC);
- IWL_CMD(REPLY_QOS_PARAM);
- IWL_CMD(REPLY_RXON_TIMING);
- IWL_CMD(REPLY_ADD_STA);
- IWL_CMD(REPLY_REMOVE_STA);
- IWL_CMD(REPLY_REMOVE_ALL_STA);
- IWL_CMD(REPLY_TXFIFO_FLUSH);
- IWL_CMD(REPLY_WEPKEY);
- IWL_CMD(REPLY_TX);
- IWL_CMD(REPLY_LEDS_CMD);
- IWL_CMD(REPLY_TX_LINK_QUALITY_CMD);
- IWL_CMD(COEX_PRIORITY_TABLE_CMD);
- IWL_CMD(COEX_MEDIUM_NOTIFICATION);
- IWL_CMD(COEX_EVENT_CMD);
- IWL_CMD(REPLY_QUIET_CMD);
- IWL_CMD(REPLY_CHANNEL_SWITCH);
- IWL_CMD(CHANNEL_SWITCH_NOTIFICATION);
- IWL_CMD(REPLY_SPECTRUM_MEASUREMENT_CMD);
- IWL_CMD(SPECTRUM_MEASURE_NOTIFICATION);
- IWL_CMD(POWER_TABLE_CMD);
- IWL_CMD(PM_SLEEP_NOTIFICATION);
- IWL_CMD(PM_DEBUG_STATISTIC_NOTIFIC);
- IWL_CMD(REPLY_SCAN_CMD);
- IWL_CMD(REPLY_SCAN_ABORT_CMD);
- IWL_CMD(SCAN_START_NOTIFICATION);
- IWL_CMD(SCAN_RESULTS_NOTIFICATION);
- IWL_CMD(SCAN_COMPLETE_NOTIFICATION);
- IWL_CMD(BEACON_NOTIFICATION);
- IWL_CMD(REPLY_TX_BEACON);
- IWL_CMD(WHO_IS_AWAKE_NOTIFICATION);
- IWL_CMD(QUIET_NOTIFICATION);
- IWL_CMD(REPLY_TX_PWR_TABLE_CMD);
- IWL_CMD(MEASURE_ABORT_NOTIFICATION);
- IWL_CMD(REPLY_BT_CONFIG);
- IWL_CMD(REPLY_STATISTICS_CMD);
- IWL_CMD(STATISTICS_NOTIFICATION);
- IWL_CMD(REPLY_CARD_STATE_CMD);
- IWL_CMD(CARD_STATE_NOTIFICATION);
- IWL_CMD(MISSED_BEACONS_NOTIFICATION);
- IWL_CMD(REPLY_CT_KILL_CONFIG_CMD);
- IWL_CMD(SENSITIVITY_CMD);
- IWL_CMD(REPLY_PHY_CALIBRATION_CMD);
- IWL_CMD(REPLY_RX_PHY_CMD);
- IWL_CMD(REPLY_RX_MPDU_CMD);
- IWL_CMD(REPLY_RX);
- IWL_CMD(REPLY_COMPRESSED_BA);
- IWL_CMD(CALIBRATION_CFG_CMD);
- IWL_CMD(CALIBRATION_RES_NOTIFICATION);
- IWL_CMD(CALIBRATION_COMPLETE_NOTIFICATION);
- IWL_CMD(REPLY_TX_POWER_DBM_CMD);
- IWL_CMD(TEMPERATURE_NOTIFICATION);
- IWL_CMD(TX_ANT_CONFIGURATION_CMD);
- IWL_CMD(REPLY_BT_COEX_PROFILE_NOTIF);
- IWL_CMD(REPLY_BT_COEX_PRIO_TABLE);
- IWL_CMD(REPLY_BT_COEX_PROT_ENV);
- IWL_CMD(REPLY_WIPAN_PARAMS);
- IWL_CMD(REPLY_WIPAN_RXON);
- IWL_CMD(REPLY_WIPAN_RXON_TIMING);
- IWL_CMD(REPLY_WIPAN_RXON_ASSOC);
- IWL_CMD(REPLY_WIPAN_QOS_PARAM);
- IWL_CMD(REPLY_WIPAN_WEPKEY);
- IWL_CMD(REPLY_WIPAN_P2P_CHANNEL_SWITCH);
- IWL_CMD(REPLY_WIPAN_NOA_NOTIFICATION);
- IWL_CMD(REPLY_WIPAN_DEACTIVATION_COMPLETE);
- default:
- return "UNKNOWN";
-
- }
-}
-
-#define HOST_COMPLETE_TIMEOUT (HZ / 2)
-
-static void iwl_generic_cmd_callback(struct iwl_priv *priv,
- struct iwl_device_cmd *cmd,
- struct iwl_rx_packet *pkt)
-{
- if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
- IWL_ERR(priv, "Bad return from %s (0x%08X)\n",
- get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
- return;
- }
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- switch (cmd->hdr.cmd) {
- case REPLY_TX_LINK_QUALITY_CMD:
- case SENSITIVITY_CMD:
- IWL_DEBUG_HC_DUMP(priv, "back from %s (0x%08X)\n",
- get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
- break;
- default:
- IWL_DEBUG_HC(priv, "back from %s (0x%08X)\n",
- get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
- }
-#endif
-}
-
-static int iwl_send_cmd_async(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
-{
- int ret;
-
- if (WARN_ON(!(cmd->flags & CMD_ASYNC)))
- return -EINVAL;
-
- /* An asynchronous command can not expect an SKB to be set. */
- if (WARN_ON(cmd->flags & CMD_WANT_SKB))
- return -EINVAL;
-
- /* Assign a generic callback if one is not provided */
- if (!cmd->callback)
- cmd->callback = iwl_generic_cmd_callback;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return -EBUSY;
-
- ret = iwl_enqueue_hcmd(priv, cmd);
- if (ret < 0) {
- IWL_ERR(priv, "Error sending %s: enqueue_hcmd failed: %d\n",
- get_cmd_string(cmd->id), ret);
- return ret;
- }
- return 0;
-}
-
-int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
-{
- int cmd_idx;
- int ret;
-
- if (WARN_ON(cmd->flags & CMD_ASYNC))
- return -EINVAL;
-
- /* A synchronous command can not have a callback set. */
- if (WARN_ON(cmd->callback))
- return -EINVAL;
-
- IWL_DEBUG_INFO(priv, "Attempting to send sync command %s\n",
- get_cmd_string(cmd->id));
-
- set_bit(STATUS_HCMD_ACTIVE, &priv->status);
- IWL_DEBUG_INFO(priv, "Setting HCMD_ACTIVE for command %s\n",
- get_cmd_string(cmd->id));
-
- cmd_idx = iwl_enqueue_hcmd(priv, cmd);
- if (cmd_idx < 0) {
- ret = cmd_idx;
- clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
- IWL_ERR(priv, "Error sending %s: enqueue_hcmd failed: %d\n",
- get_cmd_string(cmd->id), ret);
- return ret;
- }
-
- ret = wait_event_interruptible_timeout(priv->wait_command_queue,
- !test_bit(STATUS_HCMD_ACTIVE, &priv->status),
- HOST_COMPLETE_TIMEOUT);
- if (!ret) {
- if (test_bit(STATUS_HCMD_ACTIVE, &priv->status)) {
- IWL_ERR(priv,
- "Error sending %s: time out after %dms.\n",
- get_cmd_string(cmd->id),
- jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
-
- clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
- IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s\n",
- get_cmd_string(cmd->id));
- ret = -ETIMEDOUT;
- goto cancel;
- }
- }
-
- if (test_bit(STATUS_RF_KILL_HW, &priv->status)) {
- IWL_ERR(priv, "Command %s aborted: RF KILL Switch\n",
- get_cmd_string(cmd->id));
- ret = -ECANCELED;
- goto fail;
- }
- if (test_bit(STATUS_FW_ERROR, &priv->status)) {
- IWL_ERR(priv, "Command %s failed: FW Error\n",
- get_cmd_string(cmd->id));
- ret = -EIO;
- goto fail;
- }
- if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) {
- IWL_ERR(priv, "Error: Response NULL in '%s'\n",
- get_cmd_string(cmd->id));
- ret = -EIO;
- goto cancel;
- }
-
- return 0;
-
-cancel:
- if (cmd->flags & CMD_WANT_SKB) {
- /*
- * Cancel the CMD_WANT_SKB flag for the cmd in the
- * TX cmd queue. Otherwise in case the cmd comes
- * in later, it will possibly set an invalid
- * address (cmd->meta.source).
- */
- priv->txq[priv->cmd_queue].meta[cmd_idx].flags &=
- ~CMD_WANT_SKB;
- }
-fail:
- if (cmd->reply_page) {
- iwl_free_pages(priv, cmd->reply_page);
- cmd->reply_page = 0;
- }
-
- return ret;
-}
-
-int iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
-{
- if (cmd->flags & CMD_ASYNC)
- return iwl_send_cmd_async(priv, cmd);
-
- return iwl_send_cmd_sync(priv, cmd);
-}
-
-int iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, const void *data)
-{
- struct iwl_host_cmd cmd = {
- .id = id,
- .len = { len, },
- .data = { data, },
- };
-
- return iwl_send_cmd_sync(priv, &cmd);
-}
-
-int iwl_send_cmd_pdu_async(struct iwl_priv *priv,
- u8 id, u16 len, const void *data,
- void (*callback)(struct iwl_priv *priv,
- struct iwl_device_cmd *cmd,
- struct iwl_rx_packet *pkt))
-{
- struct iwl_host_cmd cmd = {
- .id = id,
- .len = { len, },
- .data = { data, },
- };
-
- cmd.flags |= CMD_ASYNC;
- cmd.callback = callback;
-
- return iwl_send_cmd_async(priv, &cmd);
-}
diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h
deleted file mode 100644
index 41207a3645b8..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-helpers.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#ifndef __iwl_helpers_h__
-#define __iwl_helpers_h__
-
-#include <linux/ctype.h>
-#include <net/mac80211.h>
-
-#include "iwl-io.h"
-
-#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
-
-
-static inline struct ieee80211_conf *ieee80211_get_hw_conf(
- struct ieee80211_hw *hw)
-{
- return &hw->conf;
-}
-
-/**
- * iwl_queue_inc_wrap - increment queue index, wrap back to beginning
- * @index -- current index
- * @n_bd -- total number of entries in queue (must be power of 2)
- */
-static inline int iwl_queue_inc_wrap(int index, int n_bd)
-{
- return ++index & (n_bd - 1);
-}
-
-/**
- * iwl_queue_dec_wrap - decrement queue index, wrap back to end
- * @index -- current index
- * @n_bd -- total number of entries in queue (must be power of 2)
- */
-static inline int iwl_queue_dec_wrap(int index, int n_bd)
-{
- return --index & (n_bd - 1);
-}
-
-/*
- * we have 8 bits used like this:
- *
- * 7 6 5 4 3 2 1 0
- * | | | | | | | |
- * | | | | | | +-+-------- AC queue (0-3)
- * | | | | | |
- * | +-+-+-+-+------------ HW queue ID
- * |
- * +---------------------- unused
- */
-static inline void iwl_set_swq_id(struct iwl_tx_queue *txq, u8 ac, u8 hwq)
-{
- BUG_ON(ac > 3); /* only have 2 bits */
- BUG_ON(hwq > 31); /* only use 5 bits */
-
- txq->swq_id = (hwq << 2) | ac;
-}
-
-static inline void iwl_wake_queue(struct iwl_priv *priv,
- struct iwl_tx_queue *txq)
-{
- u8 queue = txq->swq_id;
- u8 ac = queue & 3;
- u8 hwq = (queue >> 2) & 0x1f;
-
- if (test_and_clear_bit(hwq, priv->queue_stopped))
- if (atomic_dec_return(&priv->queue_stop_count[ac]) <= 0)
- ieee80211_wake_queue(priv->hw, ac);
-}
-
-static inline void iwl_stop_queue(struct iwl_priv *priv,
- struct iwl_tx_queue *txq)
-{
- u8 queue = txq->swq_id;
- u8 ac = queue & 3;
- u8 hwq = (queue >> 2) & 0x1f;
-
- if (!test_and_set_bit(hwq, priv->queue_stopped))
- if (atomic_inc_return(&priv->queue_stop_count[ac]) > 0)
- ieee80211_stop_queue(priv->hw, ac);
-}
-
-static inline void iwl_wake_any_queue(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- u8 ac;
-
- for (ac = 0; ac < AC_NUM; ac++) {
- IWL_DEBUG_INFO(priv, "Queue Status: Q[%d] %s\n",
- ac, (atomic_read(&priv->queue_stop_count[ac]) > 0)
- ? "stopped" : "awake");
- iwl_wake_queue(priv, &priv->txq[ctx->ac_to_queue[ac]]);
- }
-}
-
-#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue
-#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue
-
-static inline void iwl_disable_interrupts(struct iwl_priv *priv)
-{
- clear_bit(STATUS_INT_ENABLED, &priv->status);
-
- /* disable interrupts from uCode/NIC to host */
- iwl_write32(priv, CSR_INT_MASK, 0x00000000);
-
- /* acknowledge/clear/reset any interrupts still pending
- * from uCode or flow handler (Rx/Tx DMA) */
- iwl_write32(priv, CSR_INT, 0xffffffff);
- iwl_write32(priv, CSR_FH_INT_STATUS, 0xffffffff);
- IWL_DEBUG_ISR(priv, "Disabled interrupts\n");
-}
-
-static inline void iwl_enable_rfkill_int(struct iwl_priv *priv)
-{
- IWL_DEBUG_ISR(priv, "Enabling rfkill interrupt\n");
- iwl_write32(priv, CSR_INT_MASK, CSR_INT_BIT_RF_KILL);
-}
-
-static inline void iwl_enable_interrupts(struct iwl_priv *priv)
-{
- IWL_DEBUG_ISR(priv, "Enabling interrupts\n");
- set_bit(STATUS_INT_ENABLED, &priv->status);
- iwl_write32(priv, CSR_INT_MASK, priv->inta_mask);
-}
-
-/**
- * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time
- * @priv -- pointer to iwl_priv data structure
- * @tsf_bits -- number of bits need to shift for masking)
- */
-static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv,
- u16 tsf_bits)
-{
- return (1 << tsf_bits) - 1;
-}
-
-/**
- * iwl_beacon_time_mask_high - mask of higher 32 bit of beacon time
- * @priv -- pointer to iwl_priv data structure
- * @tsf_bits -- number of bits need to shift for masking)
- */
-static inline u32 iwl_beacon_time_mask_high(struct iwl_priv *priv,
- u16 tsf_bits)
-{
- return ((1 << (32 - tsf_bits)) - 1) << tsf_bits;
-}
-
-#endif /* __iwl_helpers_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c
index aa4a90674452..3ffa8e62b856 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.c
+++ b/drivers/net/wireless/iwlwifi/iwl-io.c
@@ -25,46 +25,50 @@
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
+#include <linux/delay.h>
+#include <linux/device.h>
#include "iwl-io.h"
+#include"iwl-csr.h"
+#include "iwl-debug.h"
#define IWL_POLL_INTERVAL 10 /* microseconds */
-static inline void __iwl_set_bit(struct iwl_priv *priv, u32 reg, u32 mask)
+static inline void __iwl_set_bit(struct iwl_bus *bus, u32 reg, u32 mask)
{
- iwl_write32(priv, reg, iwl_read32(priv, reg) | mask);
+ iwl_write32(bus, reg, iwl_read32(bus, reg) | mask);
}
-static inline void __iwl_clear_bit(struct iwl_priv *priv, u32 reg, u32 mask)
+static inline void __iwl_clear_bit(struct iwl_bus *bus, u32 reg, u32 mask)
{
- iwl_write32(priv, reg, iwl_read32(priv, reg) & ~mask);
+ iwl_write32(bus, reg, iwl_read32(bus, reg) & ~mask);
}
-void iwl_set_bit(struct iwl_priv *priv, u32 reg, u32 mask)
+void iwl_set_bit(struct iwl_bus *bus, u32 reg, u32 mask)
{
unsigned long flags;
- spin_lock_irqsave(&priv->reg_lock, flags);
- __iwl_set_bit(priv, reg, mask);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ __iwl_set_bit(bus, reg, mask);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
}
-void iwl_clear_bit(struct iwl_priv *priv, u32 reg, u32 mask)
+void iwl_clear_bit(struct iwl_bus *bus, u32 reg, u32 mask)
{
unsigned long flags;
- spin_lock_irqsave(&priv->reg_lock, flags);
- __iwl_clear_bit(priv, reg, mask);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ __iwl_clear_bit(bus, reg, mask);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
}
-int iwl_poll_bit(struct iwl_priv *priv, u32 addr,
+int iwl_poll_bit(struct iwl_bus *bus, u32 addr,
u32 bits, u32 mask, int timeout)
{
int t = 0;
do {
- if ((iwl_read32(priv, addr) & mask) == (bits & mask))
+ if ((iwl_read32(bus, addr) & mask) == (bits & mask))
return t;
udelay(IWL_POLL_INTERVAL);
t += IWL_POLL_INTERVAL;
@@ -73,14 +77,14 @@ int iwl_poll_bit(struct iwl_priv *priv, u32 addr,
return -ETIMEDOUT;
}
-int iwl_grab_nic_access_silent(struct iwl_priv *priv)
+int iwl_grab_nic_access_silent(struct iwl_bus *bus)
{
int ret;
- lockdep_assert_held(&priv->reg_lock);
+ lockdep_assert_held(&bus->reg_lock);
/* this bit wakes up the NIC */
- __iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ __iwl_set_bit(bus, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
/*
* These bits say the device is running, and should keep running for
@@ -101,70 +105,70 @@ int iwl_grab_nic_access_silent(struct iwl_priv *priv)
* 5000 series and later (including 1000 series) have non-volatile SRAM,
* and do not save/restore SRAM when power cycling.
*/
- ret = iwl_poll_bit(priv, CSR_GP_CNTRL,
+ ret = iwl_poll_bit(bus, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
(CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
if (ret < 0) {
- iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
+ iwl_write32(bus, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
return -EIO;
}
return 0;
}
-int iwl_grab_nic_access(struct iwl_priv *priv)
+int iwl_grab_nic_access(struct iwl_bus *bus)
{
- int ret = iwl_grab_nic_access_silent(priv);
+ int ret = iwl_grab_nic_access_silent(bus);
if (ret) {
- u32 val = iwl_read32(priv, CSR_GP_CNTRL);
- IWL_ERR(priv,
+ u32 val = iwl_read32(bus, CSR_GP_CNTRL);
+ IWL_ERR(bus,
"MAC is in deep sleep!. CSR_GP_CNTRL = 0x%08X\n", val);
}
return ret;
}
-void iwl_release_nic_access(struct iwl_priv *priv)
+void iwl_release_nic_access(struct iwl_bus *bus)
{
- lockdep_assert_held(&priv->reg_lock);
- __iwl_clear_bit(priv, CSR_GP_CNTRL,
+ lockdep_assert_held(&bus->reg_lock);
+ __iwl_clear_bit(bus, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
}
-u32 iwl_read_direct32(struct iwl_priv *priv, u32 reg)
+u32 iwl_read_direct32(struct iwl_bus *bus, u32 reg)
{
u32 value;
unsigned long flags;
- spin_lock_irqsave(&priv->reg_lock, flags);
- iwl_grab_nic_access(priv);
- value = iwl_read32(priv, reg);
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ iwl_grab_nic_access(bus);
+ value = iwl_read32(bus(bus), reg);
+ iwl_release_nic_access(bus);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
return value;
}
-void iwl_write_direct32(struct iwl_priv *priv, u32 reg, u32 value)
+void iwl_write_direct32(struct iwl_bus *bus, u32 reg, u32 value)
{
unsigned long flags;
- spin_lock_irqsave(&priv->reg_lock, flags);
- if (!iwl_grab_nic_access(priv)) {
- iwl_write32(priv, reg, value);
- iwl_release_nic_access(priv);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ if (!iwl_grab_nic_access(bus)) {
+ iwl_write32(bus, reg, value);
+ iwl_release_nic_access(bus);
}
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
}
-int iwl_poll_direct_bit(struct iwl_priv *priv, u32 addr, u32 mask,
+int iwl_poll_direct_bit(struct iwl_bus *bus, u32 addr, u32 mask,
int timeout)
{
int t = 0;
do {
- if ((iwl_read_direct32(priv, addr) & mask) == mask)
+ if ((iwl_read_direct32(bus, addr) & mask) == mask)
return t;
udelay(IWL_POLL_INTERVAL);
t += IWL_POLL_INTERVAL;
@@ -173,122 +177,122 @@ int iwl_poll_direct_bit(struct iwl_priv *priv, u32 addr, u32 mask,
return -ETIMEDOUT;
}
-static inline u32 __iwl_read_prph(struct iwl_priv *priv, u32 reg)
+static inline u32 __iwl_read_prph(struct iwl_bus *bus, u32 reg)
{
- iwl_write32(priv, HBUS_TARG_PRPH_RADDR, reg | (3 << 24));
+ iwl_write32(bus, HBUS_TARG_PRPH_RADDR, reg | (3 << 24));
rmb();
- return iwl_read32(priv, HBUS_TARG_PRPH_RDAT);
+ return iwl_read32(bus, HBUS_TARG_PRPH_RDAT);
}
-static inline void __iwl_write_prph(struct iwl_priv *priv, u32 addr, u32 val)
+static inline void __iwl_write_prph(struct iwl_bus *bus, u32 addr, u32 val)
{
- iwl_write32(priv, HBUS_TARG_PRPH_WADDR,
+ iwl_write32(bus, HBUS_TARG_PRPH_WADDR,
((addr & 0x0000FFFF) | (3 << 24)));
wmb();
- iwl_write32(priv, HBUS_TARG_PRPH_WDAT, val);
+ iwl_write32(bus, HBUS_TARG_PRPH_WDAT, val);
}
-u32 iwl_read_prph(struct iwl_priv *priv, u32 reg)
+u32 iwl_read_prph(struct iwl_bus *bus, u32 reg)
{
unsigned long flags;
u32 val;
- spin_lock_irqsave(&priv->reg_lock, flags);
- iwl_grab_nic_access(priv);
- val = __iwl_read_prph(priv, reg);
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ iwl_grab_nic_access(bus);
+ val = __iwl_read_prph(bus, reg);
+ iwl_release_nic_access(bus);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
return val;
}
-void iwl_write_prph(struct iwl_priv *priv, u32 addr, u32 val)
+void iwl_write_prph(struct iwl_bus *bus, u32 addr, u32 val)
{
unsigned long flags;
- spin_lock_irqsave(&priv->reg_lock, flags);
- if (!iwl_grab_nic_access(priv)) {
- __iwl_write_prph(priv, addr, val);
- iwl_release_nic_access(priv);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ if (!iwl_grab_nic_access(bus)) {
+ __iwl_write_prph(bus, addr, val);
+ iwl_release_nic_access(bus);
}
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
}
-void iwl_set_bits_prph(struct iwl_priv *priv, u32 reg, u32 mask)
+void iwl_set_bits_prph(struct iwl_bus *bus, u32 reg, u32 mask)
{
unsigned long flags;
- spin_lock_irqsave(&priv->reg_lock, flags);
- iwl_grab_nic_access(priv);
- __iwl_write_prph(priv, reg, __iwl_read_prph(priv, reg) | mask);
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ iwl_grab_nic_access(bus);
+ __iwl_write_prph(bus, reg, __iwl_read_prph(bus, reg) | mask);
+ iwl_release_nic_access(bus);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
}
-void iwl_set_bits_mask_prph(struct iwl_priv *priv, u32 reg,
+void iwl_set_bits_mask_prph(struct iwl_bus *bus, u32 reg,
u32 bits, u32 mask)
{
unsigned long flags;
- spin_lock_irqsave(&priv->reg_lock, flags);
- iwl_grab_nic_access(priv);
- __iwl_write_prph(priv, reg,
- (__iwl_read_prph(priv, reg) & mask) | bits);
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ iwl_grab_nic_access(bus);
+ __iwl_write_prph(bus, reg,
+ (__iwl_read_prph(bus, reg) & mask) | bits);
+ iwl_release_nic_access(bus);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
}
-void iwl_clear_bits_prph(struct iwl_priv *priv, u32 reg, u32 mask)
+void iwl_clear_bits_prph(struct iwl_bus *bus, u32 reg, u32 mask)
{
unsigned long flags;
u32 val;
- spin_lock_irqsave(&priv->reg_lock, flags);
- iwl_grab_nic_access(priv);
- val = __iwl_read_prph(priv, reg);
- __iwl_write_prph(priv, reg, (val & ~mask));
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ iwl_grab_nic_access(bus);
+ val = __iwl_read_prph(bus, reg);
+ __iwl_write_prph(bus, reg, (val & ~mask));
+ iwl_release_nic_access(bus);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
}
-void _iwl_read_targ_mem_words(struct iwl_priv *priv, u32 addr,
+void _iwl_read_targ_mem_words(struct iwl_bus *bus, u32 addr,
void *buf, int words)
{
unsigned long flags;
int offs;
u32 *vals = buf;
- spin_lock_irqsave(&priv->reg_lock, flags);
- iwl_grab_nic_access(priv);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ iwl_grab_nic_access(bus);
- iwl_write32(priv, HBUS_TARG_MEM_RADDR, addr);
+ iwl_write32(bus, HBUS_TARG_MEM_RADDR, addr);
rmb();
for (offs = 0; offs < words; offs++)
- vals[offs] = iwl_read32(priv, HBUS_TARG_MEM_RDAT);
+ vals[offs] = iwl_read32(bus, HBUS_TARG_MEM_RDAT);
- iwl_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ iwl_release_nic_access(bus);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
}
-u32 iwl_read_targ_mem(struct iwl_priv *priv, u32 addr)
+u32 iwl_read_targ_mem(struct iwl_bus *bus, u32 addr)
{
u32 value;
- _iwl_read_targ_mem_words(priv, addr, &value, 1);
+ _iwl_read_targ_mem_words(bus, addr, &value, 1);
return value;
}
-void iwl_write_targ_mem(struct iwl_priv *priv, u32 addr, u32 val)
+void iwl_write_targ_mem(struct iwl_bus *bus, u32 addr, u32 val)
{
unsigned long flags;
- spin_lock_irqsave(&priv->reg_lock, flags);
- if (!iwl_grab_nic_access(priv)) {
- iwl_write32(priv, HBUS_TARG_MEM_WADDR, addr);
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ if (!iwl_grab_nic_access(bus)) {
+ iwl_write32(bus, HBUS_TARG_MEM_WADDR, addr);
wmb();
- iwl_write32(priv, HBUS_TARG_MEM_WDAT, val);
- iwl_release_nic_access(priv);
+ iwl_write32(bus, HBUS_TARG_MEM_WDAT, val);
+ iwl_release_nic_access(bus);
}
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h
index 869edc580ec6..ced2cbeb6eae 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/iwlwifi/iwl-io.h
@@ -29,64 +29,62 @@
#ifndef __iwl_io_h__
#define __iwl_io_h__
-#include <linux/io.h>
-
-#include "iwl-dev.h"
-#include "iwl-debug.h"
#include "iwl-devtrace.h"
+#include "iwl-shared.h"
+#include "iwl-bus.h"
-static inline void iwl_write8(struct iwl_priv *priv, u32 ofs, u8 val)
+static inline void iwl_write8(struct iwl_bus *bus, u32 ofs, u8 val)
{
- trace_iwlwifi_dev_iowrite8(priv, ofs, val);
- iowrite8(val, priv->hw_base + ofs);
+ trace_iwlwifi_dev_iowrite8(priv(bus), ofs, val);
+ bus_write8(bus, ofs, val);
}
-static inline void iwl_write32(struct iwl_priv *priv, u32 ofs, u32 val)
+static inline void iwl_write32(struct iwl_bus *bus, u32 ofs, u32 val)
{
- trace_iwlwifi_dev_iowrite32(priv, ofs, val);
- iowrite32(val, priv->hw_base + ofs);
+ trace_iwlwifi_dev_iowrite32(priv(bus), ofs, val);
+ bus_write32(bus, ofs, val);
}
-static inline u32 iwl_read32(struct iwl_priv *priv, u32 ofs)
+static inline u32 iwl_read32(struct iwl_bus *bus, u32 ofs)
{
- u32 val = ioread32(priv->hw_base + ofs);
- trace_iwlwifi_dev_ioread32(priv, ofs, val);
+ u32 val = bus_read32(bus, ofs);
+ trace_iwlwifi_dev_ioread32(priv(bus), ofs, val);
return val;
}
-void iwl_set_bit(struct iwl_priv *priv, u32 reg, u32 mask);
-void iwl_clear_bit(struct iwl_priv *priv, u32 reg, u32 mask);
+void iwl_set_bit(struct iwl_bus *bus, u32 reg, u32 mask);
+void iwl_clear_bit(struct iwl_bus *bus, u32 reg, u32 mask);
-int iwl_poll_bit(struct iwl_priv *priv, u32 addr,
+int iwl_poll_bit(struct iwl_bus *bus, u32 addr,
u32 bits, u32 mask, int timeout);
-int iwl_poll_direct_bit(struct iwl_priv *priv, u32 addr, u32 mask,
+int iwl_poll_direct_bit(struct iwl_bus *bus, u32 addr, u32 mask,
int timeout);
-int iwl_grab_nic_access_silent(struct iwl_priv *priv);
-int iwl_grab_nic_access(struct iwl_priv *priv);
-void iwl_release_nic_access(struct iwl_priv *priv);
+int iwl_grab_nic_access_silent(struct iwl_bus *bus);
+int iwl_grab_nic_access(struct iwl_bus *bus);
+void iwl_release_nic_access(struct iwl_bus *bus);
-u32 iwl_read_direct32(struct iwl_priv *priv, u32 reg);
-void iwl_write_direct32(struct iwl_priv *priv, u32 reg, u32 value);
+u32 iwl_read_direct32(struct iwl_bus *bus, u32 reg);
+void iwl_write_direct32(struct iwl_bus *bus, u32 reg, u32 value);
-u32 iwl_read_prph(struct iwl_priv *priv, u32 reg);
-void iwl_write_prph(struct iwl_priv *priv, u32 addr, u32 val);
-void iwl_set_bits_prph(struct iwl_priv *priv, u32 reg, u32 mask);
-void iwl_set_bits_mask_prph(struct iwl_priv *priv, u32 reg,
+u32 iwl_read_prph(struct iwl_bus *bus, u32 reg);
+void iwl_write_prph(struct iwl_bus *bus, u32 addr, u32 val);
+void iwl_set_bits_prph(struct iwl_bus *bus, u32 reg, u32 mask);
+void iwl_set_bits_mask_prph(struct iwl_bus *bus, u32 reg,
u32 bits, u32 mask);
-void iwl_clear_bits_prph(struct iwl_priv *priv, u32 reg, u32 mask);
+void iwl_clear_bits_prph(struct iwl_bus *bus, u32 reg, u32 mask);
-void _iwl_read_targ_mem_words(struct iwl_priv *priv, u32 addr,
+void _iwl_read_targ_mem_words(struct iwl_bus *bus, u32 addr,
void *buf, int words);
-#define iwl_read_targ_mem_words(priv, addr, buf, bufsize) \
+#define iwl_read_targ_mem_words(bus, addr, buf, bufsize) \
do { \
BUILD_BUG_ON((bufsize) % sizeof(u32)); \
- _iwl_read_targ_mem_words(priv, addr, buf, \
+ _iwl_read_targ_mem_words(bus, addr, buf, \
(bufsize) / sizeof(u32));\
} while (0)
-u32 iwl_read_targ_mem(struct iwl_priv *priv, u32 addr);
-void iwl_write_targ_mem(struct iwl_priv *priv, u32 addr, u32 val);
+u32 iwl_read_targ_mem(struct iwl_bus *bus, u32 addr);
+void iwl_write_targ_mem(struct iwl_bus *bus, u32 addr, u32 val);
#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c
index 7c23beb49d7c..eb541735296c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-led.c
@@ -28,25 +28,19 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#include <linux/wireless.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
#include "iwl-dev.h"
#include "iwl-core.h"
+#include "iwl-agn.h"
#include "iwl-io.h"
-
-/* default: IWL_LED_BLINK(0) using blinking index table */
-static int led_mode;
-module_param(led_mode, int, S_IRUGO);
-MODULE_PARM_DESC(led_mode, "0=system default, "
- "1=On(RF On)/Off(RF Off), 2=blinking");
+#include "iwl-trans.h"
+#include "iwl-shared.h"
/* Throughput OFF time(ms) ON time (ms)
* >300 25 25
@@ -77,7 +71,7 @@ static const struct ieee80211_tpt_blink iwl_blink[] = {
/* Set led register off */
void iwlagn_led_enable(struct iwl_priv *priv)
{
- iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON);
+ iwl_write32(bus(priv), CSR_LED_REG, CSR_LED_REG_TRUN_ON);
}
/*
@@ -110,15 +104,14 @@ static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd)
.len = { sizeof(struct iwl_led_cmd), },
.data = { led_cmd, },
.flags = CMD_ASYNC,
- .callback = NULL,
};
u32 reg;
- reg = iwl_read32(priv, CSR_LED_REG);
+ reg = iwl_read32(bus(priv), CSR_LED_REG);
if (reg != (reg & CSR_LED_BSM_CTRL_MSK))
- iwl_write32(priv, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK);
+ iwl_write32(bus(priv), CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK);
- return iwl_send_cmd(priv, &cmd);
+ return iwl_trans_send_cmd(trans(priv), &cmd);
}
/* Set led pattern command */
@@ -132,7 +125,7 @@ static int iwl_led_cmd(struct iwl_priv *priv,
};
int ret;
- if (!test_bit(STATUS_READY, &priv->status))
+ if (!test_bit(STATUS_READY, &priv->shrd->status))
return -EBUSY;
if (priv->blink_on == on && priv->blink_off == off)
@@ -181,7 +174,7 @@ static int iwl_led_blink_set(struct led_classdev *led_cdev,
void iwl_leds_init(struct iwl_priv *priv)
{
- int mode = led_mode;
+ int mode = iwlagn_mod_params.led_mode;
int ret;
if (mode == IWL_LED_DEFAULT)
@@ -209,7 +202,7 @@ void iwl_leds_init(struct iwl_priv *priv)
break;
}
- ret = led_classdev_register(&priv->pci_dev->dev, &priv->led);
+ ret = led_classdev_register(bus(priv)->dev, &priv->led);
if (ret) {
kfree(priv->led.name);
return;
diff --git a/drivers/net/wireless/iwlwifi/iwl-pci.c b/drivers/net/wireless/iwlwifi/iwl-pci.c
new file mode 100644
index 000000000000..1800029911ad
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-pci.c
@@ -0,0 +1,568 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pci-aspm.h>
+
+#include "iwl-bus.h"
+#include "iwl-io.h"
+#include "iwl-shared.h"
+#include "iwl-trans.h"
+#include "iwl-csr.h"
+#include "iwl-cfg.h"
+
+/* PCI registers */
+#define PCI_CFG_RETRY_TIMEOUT 0x041
+#define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01
+#define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02
+
+struct iwl_pci_bus {
+ /* basic pci-network driver stuff */
+ struct pci_dev *pci_dev;
+
+ /* pci hardware address support */
+ void __iomem *hw_base;
+};
+
+#define IWL_BUS_GET_PCI_BUS(_iwl_bus) \
+ ((struct iwl_pci_bus *) ((_iwl_bus)->bus_specific))
+
+#define IWL_BUS_GET_PCI_DEV(_iwl_bus) \
+ ((IWL_BUS_GET_PCI_BUS(_iwl_bus))->pci_dev)
+
+static u16 iwl_pciexp_link_ctrl(struct iwl_bus *bus)
+{
+ int pos;
+ u16 pci_lnk_ctl;
+
+ struct pci_dev *pci_dev = IWL_BUS_GET_PCI_DEV(bus);
+
+ pos = pci_pcie_cap(pci_dev);
+ pci_read_config_word(pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
+ return pci_lnk_ctl;
+}
+
+static bool iwl_pci_is_pm_supported(struct iwl_bus *bus)
+{
+ u16 lctl = iwl_pciexp_link_ctrl(bus);
+
+ return !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN);
+}
+
+static void iwl_pci_apm_config(struct iwl_bus *bus)
+{
+ /*
+ * HW bug W/A for instability in PCIe bus L0S->L1 transition.
+ * Check if BIOS (or OS) enabled L1-ASPM on this device.
+ * If so (likely), disable L0S, so device moves directly L0->L1;
+ * costs negligible amount of power savings.
+ * If not (unlikely), enable L0S, so there is at least some
+ * power savings, even without L1.
+ */
+ u16 lctl = iwl_pciexp_link_ctrl(bus);
+
+ if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) ==
+ PCI_CFG_LINK_CTRL_VAL_L1_EN) {
+ /* L1-ASPM enabled; disable(!) L0S */
+ iwl_set_bit(bus, CSR_GIO_REG,
+ CSR_GIO_REG_VAL_L0S_ENABLED);
+ dev_printk(KERN_INFO, bus->dev, "L1 Enabled; Disabling L0S\n");
+ } else {
+ /* L1-ASPM disabled; enable(!) L0S */
+ iwl_clear_bit(bus, CSR_GIO_REG,
+ CSR_GIO_REG_VAL_L0S_ENABLED);
+ dev_printk(KERN_INFO, bus->dev, "L1 Disabled; Enabling L0S\n");
+ }
+}
+
+static void iwl_pci_get_hw_id(struct iwl_bus *bus, char buf[],
+ int buf_len)
+{
+ struct pci_dev *pci_dev = IWL_BUS_GET_PCI_DEV(bus);
+
+ snprintf(buf, buf_len, "PCI ID: 0x%04X:0x%04X", pci_dev->device,
+ pci_dev->subsystem_device);
+}
+
+static void iwl_pci_write8(struct iwl_bus *bus, u32 ofs, u8 val)
+{
+ iowrite8(val, IWL_BUS_GET_PCI_BUS(bus)->hw_base + ofs);
+}
+
+static void iwl_pci_write32(struct iwl_bus *bus, u32 ofs, u32 val)
+{
+ iowrite32(val, IWL_BUS_GET_PCI_BUS(bus)->hw_base + ofs);
+}
+
+static u32 iwl_pci_read32(struct iwl_bus *bus, u32 ofs)
+{
+ u32 val = ioread32(IWL_BUS_GET_PCI_BUS(bus)->hw_base + ofs);
+ return val;
+}
+
+static const struct iwl_bus_ops bus_ops_pci = {
+ .get_pm_support = iwl_pci_is_pm_supported,
+ .apm_config = iwl_pci_apm_config,
+ .get_hw_id = iwl_pci_get_hw_id,
+ .write8 = iwl_pci_write8,
+ .write32 = iwl_pci_write32,
+ .read32 = iwl_pci_read32,
+};
+
+#define IWL_PCI_DEVICE(dev, subdev, cfg) \
+ .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
+ .subvendor = PCI_ANY_ID, .subdevice = (subdev), \
+ .driver_data = (kernel_ulong_t)&(cfg)
+
+/* Hardware specific file defines the PCI IDs table for that hardware module */
+static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
+ {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */
+
+/* 5300 Series WiFi */
+ {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */
+
+/* 5350 Series WiFi/WiMax */
+ {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */
+
+/* 5150 Series Wifi/WiMax */
+ {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */
+
+ {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */
+
+/* 6x00 Series */
+ {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)},
+
+/* 6x05 Series */
+ {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1341, iwl6005_2agn_d_cfg)},
+
+/* 6x30 Series */
+ {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)},
+ {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)},
+ {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)},
+
+/* 6x50 WiFi/WiMax Series */
+ {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)},
+
+/* 6150 WiFi/WiMax Series */
+ {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)},
+
+/* 1000 Series WiFi */
+ {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)},
+
+/* 100 Series WiFi */
+ {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)},
+ {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)},
+ {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)},
+
+/* 130 Series WiFi */
+ {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)},
+
+/* 2x00 Series */
+ {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0890, 0x4026, iwl2000_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x0891, 0x4226, iwl2000_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x0890, 0x4426, iwl2000_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)},
+
+/* 2x30 Series */
+ {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0887, 0x4066, iwl2030_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x0888, 0x4266, iwl2030_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x0887, 0x4466, iwl2030_2bg_cfg)},
+
+/* 6x35 Series */
+ {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x4064, iwl6035_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x088F, 0x4264, iwl6035_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x4464, iwl6035_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x4066, iwl6035_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x088F, 0x4266, iwl6035_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x4466, iwl6035_2bg_cfg)},
+
+/* 105 Series */
+ {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0894, 0x0026, iwl105_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0895, 0x0226, iwl105_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0894, 0x0426, iwl105_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)},
+
+/* 135 Series */
+ {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0892, 0x0066, iwl135_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0893, 0x0266, iwl135_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0892, 0x0466, iwl135_bg_cfg)},
+
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
+
+static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
+ struct iwl_bus *bus;
+ struct iwl_pci_bus *pci_bus;
+ u16 pci_cmd;
+ int err;
+
+ bus = kzalloc(sizeof(*bus) + sizeof(*pci_bus), GFP_KERNEL);
+ if (!bus) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "Couldn't allocate iwl_pci_bus");
+ err = -ENOMEM;
+ goto out_no_pci;
+ }
+
+ pci_bus = IWL_BUS_GET_PCI_BUS(bus);
+ pci_bus->pci_dev = pdev;
+
+ pci_set_drvdata(pdev, bus);
+
+ /* W/A - seems to solve weird behavior. We need to remove this if we
+ * don't want to stay in L1 all the time. This wastes a lot of power */
+ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
+ PCIE_LINK_STATE_CLKPM);
+
+ if (pci_enable_device(pdev)) {
+ err = -ENODEV;
+ goto out_no_pci;
+ }
+
+ pci_set_master(pdev);
+
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
+ if (!err)
+ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36));
+ if (err) {
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (!err)
+ err = pci_set_consistent_dma_mask(pdev,
+ DMA_BIT_MASK(32));
+ /* both attempts failed: */
+ if (err) {
+ dev_printk(KERN_ERR, bus->dev,
+ "No suitable DMA available.\n");
+ goto out_pci_disable_device;
+ }
+ }
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err) {
+ dev_printk(KERN_ERR, bus->dev, "pci_request_regions failed");
+ goto out_pci_disable_device;
+ }
+
+ pci_bus->hw_base = pci_iomap(pdev, 0, 0);
+ if (!pci_bus->hw_base) {
+ dev_printk(KERN_ERR, bus->dev, "pci_iomap failed");
+ err = -ENODEV;
+ goto out_pci_release_regions;
+ }
+
+ dev_printk(KERN_INFO, &pdev->dev,
+ "pci_resource_len = 0x%08llx\n",
+ (unsigned long long) pci_resource_len(pdev, 0));
+ dev_printk(KERN_INFO, &pdev->dev,
+ "pci_resource_base = %p\n", pci_bus->hw_base);
+
+ dev_printk(KERN_INFO, &pdev->dev,
+ "HW Revision ID = 0x%X\n", pdev->revision);
+
+ /* We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state */
+ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
+
+ err = pci_enable_msi(pdev);
+ if (err)
+ dev_printk(KERN_ERR, &pdev->dev,
+ "pci_enable_msi failed(0X%x)", err);
+
+ /* TODO: Move this away, not needed if not MSI */
+ /* enable rfkill interrupt: hw bug w/a */
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
+ if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
+ pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
+ }
+
+ bus->dev = &pdev->dev;
+ bus->irq = pdev->irq;
+ bus->ops = &bus_ops_pci;
+
+ err = iwl_probe(bus, &trans_ops_pcie, cfg);
+ if (err)
+ goto out_disable_msi;
+ return 0;
+
+out_disable_msi:
+ pci_disable_msi(pdev);
+ pci_iounmap(pdev, pci_bus->hw_base);
+out_pci_release_regions:
+ pci_set_drvdata(pdev, NULL);
+ pci_release_regions(pdev);
+out_pci_disable_device:
+ pci_disable_device(pdev);
+out_no_pci:
+ kfree(bus);
+ return err;
+}
+
+static void __devexit iwl_pci_remove(struct pci_dev *pdev)
+{
+ struct iwl_bus *bus = pci_get_drvdata(pdev);
+ struct iwl_pci_bus *pci_bus = IWL_BUS_GET_PCI_BUS(bus);
+ struct pci_dev *pci_dev = IWL_BUS_GET_PCI_DEV(bus);
+ struct iwl_shared *shrd = bus->shrd;
+
+ iwl_remove(shrd->priv);
+
+ pci_disable_msi(pci_dev);
+ pci_iounmap(pci_dev, pci_bus->hw_base);
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+ pci_set_drvdata(pci_dev, NULL);
+
+ kfree(bus);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int iwl_pci_suspend(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct iwl_bus *bus = pci_get_drvdata(pdev);
+ struct iwl_shared *shrd = bus->shrd;
+
+ /* Before you put code here, think about WoWLAN. You cannot check here
+ * whether WoWLAN is enabled or not, and your code will run even if
+ * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx.
+ */
+
+ return iwl_trans_suspend(shrd->trans);
+}
+
+static int iwl_pci_resume(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct iwl_bus *bus = pci_get_drvdata(pdev);
+ struct iwl_shared *shrd = bus->shrd;
+
+ /* Before you put code here, think about WoWLAN. You cannot check here
+ * whether WoWLAN is enabled or not, and your code will run even if
+ * WoWLAN is enabled - the NIC may be alive.
+ */
+
+ /*
+ * We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state.
+ */
+ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
+
+ return iwl_trans_resume(shrd->trans);
+}
+
+static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume);
+
+#define IWL_PM_OPS (&iwl_dev_pm_ops)
+
+#else
+
+#define IWL_PM_OPS NULL
+
+#endif
+
+static struct pci_driver iwl_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = iwl_hw_card_ids,
+ .probe = iwl_pci_probe,
+ .remove = __devexit_p(iwl_pci_remove),
+ .driver.pm = IWL_PM_OPS,
+};
+
+int __must_check iwl_pci_register_driver(void)
+{
+ int ret;
+ ret = pci_register_driver(&iwl_pci_driver);
+ if (ret)
+ pr_err("Unable to initialize PCI module\n");
+
+ return ret;
+}
+
+void iwl_pci_unregister_driver(void)
+{
+ pci_unregister_driver(&iwl_pci_driver);
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c
index 595c930b28ae..4eaab204322d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-power.c
+++ b/drivers/net/wireless/iwlwifi/iwl-power.c
@@ -36,11 +36,14 @@
#include "iwl-eeprom.h"
#include "iwl-dev.h"
+#include "iwl-agn.h"
#include "iwl-core.h"
#include "iwl-io.h"
#include "iwl-commands.h"
#include "iwl-debug.h"
#include "iwl-power.h"
+#include "iwl-trans.h"
+#include "iwl-shared.h"
/*
* Setting power level allows the card to go to sleep when not busy.
@@ -51,16 +54,6 @@
*/
/*
- * For now, keep using power level 1 instead of automatically
- * adjusting ...
- */
-bool no_sleep_autoadjust = true;
-module_param(no_sleep_autoadjust, bool, S_IRUGO);
-MODULE_PARM_DESC(no_sleep_autoadjust,
- "don't automatically adjust sleep level "
- "according to maximum network latency");
-
-/*
* This defines the old power levels. They are still used by default
* (level 1) and for thermal throttle (levels 3 through 5)
*/
@@ -222,7 +215,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
else
cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK;
- if (priv->cfg->base_params->shadow_reg_enable)
+ if (hw_params(priv).shadow_reg_enable)
cmd->flags |= IWL_POWER_SHADOW_REG_ENA;
else
cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
@@ -254,7 +247,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
}
}
- if (priv->power_data.pci_pm)
+ if (priv->power_data.bus_pm)
cmd->flags |= IWL_POWER_PCI_PM_MSK;
else
cmd->flags &= ~IWL_POWER_PCI_PM_MSK;
@@ -269,7 +262,7 @@ static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv,
{
memset(cmd, 0, sizeof(*cmd));
- if (priv->power_data.pci_pm)
+ if (priv->power_data.bus_pm)
cmd->flags |= IWL_POWER_PCI_PM_MSK;
IWL_DEBUG_POWER(priv, "Sleep command for CAM\n");
@@ -305,10 +298,10 @@ static void iwl_power_fill_sleep_cmd(struct iwl_priv *priv,
cmd->flags = IWL_POWER_DRIVER_ALLOW_SLEEP_MSK |
IWL_POWER_FAST_PD; /* no use seeing frames for others */
- if (priv->power_data.pci_pm)
+ if (priv->power_data.bus_pm)
cmd->flags |= IWL_POWER_PCI_PM_MSK;
- if (priv->cfg->base_params->shadow_reg_enable)
+ if (hw_params(priv).shadow_reg_enable)
cmd->flags |= IWL_POWER_SHADOW_REG_ENA;
else
cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
@@ -343,7 +336,7 @@ static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd)
le32_to_cpu(cmd->sleep_interval[3]),
le32_to_cpu(cmd->sleep_interval[4]));
- return iwl_send_cmd_pdu(priv, POWER_TABLE_CMD,
+ return iwl_trans_send_cmd_pdu(trans(priv), POWER_TABLE_CMD, CMD_SYNC,
sizeof(struct iwl_powertable_cmd), cmd);
}
@@ -355,7 +348,10 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
dtimper = priv->hw->conf.ps_dtim_period ?: 1;
- if (priv->hw->conf.flags & IEEE80211_CONF_IDLE)
+ if (priv->shrd->wowlan)
+ iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
+ else if (!priv->cfg->base_params->no_idle_support &&
+ priv->hw->conf.flags & IEEE80211_CONF_IDLE)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
else if (iwl_tt_is_low_power_state(priv)) {
/* in thermal throttling low power state */
@@ -367,9 +363,15 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
iwl_static_sleep_cmd(priv, cmd,
priv->power_data.debug_sleep_level_override,
dtimper);
- else if (no_sleep_autoadjust)
- iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_1, dtimper);
- else
+ else if (iwlagn_mod_params.no_sleep_autoadjust) {
+ if (iwlagn_mod_params.power_level > IWL_POWER_INDEX_1 &&
+ iwlagn_mod_params.power_level <= IWL_POWER_INDEX_5)
+ iwl_static_sleep_cmd(priv, cmd,
+ iwlagn_mod_params.power_level, dtimper);
+ else
+ iwl_static_sleep_cmd(priv, cmd,
+ IWL_POWER_INDEX_1, dtimper);
+ } else
iwl_power_fill_sleep_cmd(priv, cmd,
priv->hw->conf.dynamic_ps_timeout,
priv->hw->conf.max_sleep_period);
@@ -381,7 +383,7 @@ int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
int ret;
bool update_chains;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
/* Don't update the RX chain when chain noise calibration is running */
update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE ||
@@ -390,27 +392,27 @@ int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force)
return 0;
- if (!iwl_is_ready_rf(priv))
+ if (!iwl_is_ready_rf(priv->shrd))
return -EIO;
/* scan complete use sleep_power_next, need to be updated */
memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd));
- if (test_bit(STATUS_SCANNING, &priv->status) && !force) {
+ if (test_bit(STATUS_SCANNING, &priv->shrd->status) && !force) {
IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n");
return 0;
}
if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)
- set_bit(STATUS_POWER_PMI, &priv->status);
+ set_bit(STATUS_POWER_PMI, &priv->shrd->status);
ret = iwl_set_power(priv, cmd);
if (!ret) {
if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK))
- clear_bit(STATUS_POWER_PMI, &priv->status);
+ clear_bit(STATUS_POWER_PMI, &priv->shrd->status);
- if (priv->cfg->ops->lib->update_chain_flags && update_chains)
- priv->cfg->ops->lib->update_chain_flags(priv);
- else if (priv->cfg->ops->lib->update_chain_flags)
+ if (update_chains)
+ iwl_update_chain_flags(priv);
+ else
IWL_DEBUG_POWER(priv,
"Cannot update the power, chain noise "
"calibration running: %d\n",
@@ -434,9 +436,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
/* initialize to default */
void iwl_power_initialize(struct iwl_priv *priv)
{
- u16 lctl = iwl_pcie_link_ctl(priv);
-
- priv->power_data.pci_pm = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN);
+ priv->power_data.bus_pm = bus_get_pm_support(bus(priv));
priv->power_data.debug_sleep_level_override = -1;
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h
index 59635d784e27..5f7b720cf1a4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-power.h
+++ b/drivers/net/wireless/iwlwifi/iwl-power.h
@@ -43,7 +43,7 @@ struct iwl_power_mgr {
struct iwl_powertable_cmd sleep_cmd;
struct iwl_powertable_cmd sleep_cmd_next;
int debug_sleep_level_override;
- bool pci_pm;
+ bool bus_pm;
};
int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index f00d188b2cfc..bebdd828f324 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -168,6 +168,7 @@
* the scheduler (especially for queue #4/#9, the command queue, otherwise
* the driver can't issue commands!):
*/
+#define SCD_MEM_LOWER_BOUND (0x0000)
/**
* Max Tx window size is the max number of contiguous TFDs that the scheduler
@@ -177,53 +178,61 @@
#define SCD_WIN_SIZE 64
#define SCD_FRAME_LIMIT 64
-#define IWL_SCD_TXFIFO_POS_TID (0)
-#define IWL_SCD_TXFIFO_POS_RA (4)
-#define IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF)
+#define SCD_TXFIFO_POS_TID (0)
+#define SCD_TXFIFO_POS_RA (4)
+#define SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF)
/* agn SCD */
-#define IWLAGN_SCD_QUEUE_STTS_REG_POS_TXF (0)
-#define IWLAGN_SCD_QUEUE_STTS_REG_POS_ACTIVE (3)
-#define IWLAGN_SCD_QUEUE_STTS_REG_POS_WSL (4)
-#define IWLAGN_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (19)
-#define IWLAGN_SCD_QUEUE_STTS_REG_MSK (0x00FF0000)
-
-#define IWLAGN_SCD_QUEUE_CTX_REG1_CREDIT_POS (8)
-#define IWLAGN_SCD_QUEUE_CTX_REG1_CREDIT_MSK (0x00FFFF00)
-#define IWLAGN_SCD_QUEUE_CTX_REG1_SUPER_CREDIT_POS (24)
-#define IWLAGN_SCD_QUEUE_CTX_REG1_SUPER_CREDIT_MSK (0xFF000000)
-#define IWLAGN_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS (0)
-#define IWLAGN_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK (0x0000007F)
-#define IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16)
-#define IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000)
-
-#define IWLAGN_SCD_CONTEXT_DATA_OFFSET (0x600)
-#define IWLAGN_SCD_TX_STTS_BITMAP_OFFSET (0x7B1)
-#define IWLAGN_SCD_TRANSLATE_TBL_OFFSET (0x7E0)
-
-#define IWLAGN_SCD_CONTEXT_QUEUE_OFFSET(x)\
- (IWLAGN_SCD_CONTEXT_DATA_OFFSET + ((x) * 8))
-
-#define IWLAGN_SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \
- ((IWLAGN_SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffc)
-
-#define IWLAGN_SCD_QUEUECHAIN_SEL_ALL(priv) \
- (((1<<(priv)->hw_params.max_txq_num) - 1) &\
- (~(1<<(priv)->cmd_queue)))
-
-#define IWLAGN_SCD_BASE (PRPH_BASE + 0xa02c00)
-
-#define IWLAGN_SCD_SRAM_BASE_ADDR (IWLAGN_SCD_BASE + 0x0)
-#define IWLAGN_SCD_DRAM_BASE_ADDR (IWLAGN_SCD_BASE + 0x8)
-#define IWLAGN_SCD_AIT (IWLAGN_SCD_BASE + 0x0c)
-#define IWLAGN_SCD_TXFACT (IWLAGN_SCD_BASE + 0x10)
-#define IWLAGN_SCD_ACTIVE (IWLAGN_SCD_BASE + 0x14)
-#define IWLAGN_SCD_QUEUE_WRPTR(x) (IWLAGN_SCD_BASE + 0x18 + (x) * 4)
-#define IWLAGN_SCD_QUEUE_RDPTR(x) (IWLAGN_SCD_BASE + 0x68 + (x) * 4)
-#define IWLAGN_SCD_QUEUECHAIN_SEL (IWLAGN_SCD_BASE + 0xe8)
-#define IWLAGN_SCD_AGGR_SEL (IWLAGN_SCD_BASE + 0x248)
-#define IWLAGN_SCD_INTERRUPT_MASK (IWLAGN_SCD_BASE + 0x108)
-#define IWLAGN_SCD_QUEUE_STATUS_BITS(x) (IWLAGN_SCD_BASE + 0x10c + (x) * 4)
+#define SCD_QUEUE_STTS_REG_POS_TXF (0)
+#define SCD_QUEUE_STTS_REG_POS_ACTIVE (3)
+#define SCD_QUEUE_STTS_REG_POS_WSL (4)
+#define SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (19)
+#define SCD_QUEUE_STTS_REG_MSK (0x00FF0000)
+
+#define SCD_QUEUE_CTX_REG1_CREDIT_POS (8)
+#define SCD_QUEUE_CTX_REG1_CREDIT_MSK (0x00FFFF00)
+#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_POS (24)
+#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_MSK (0xFF000000)
+#define SCD_QUEUE_CTX_REG2_WIN_SIZE_POS (0)
+#define SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK (0x0000007F)
+#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16)
+#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000)
+
+/* Context Data */
+#define SCD_CONTEXT_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x600)
+#define SCD_CONTEXT_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0)
+
+/* Tx status */
+#define SCD_TX_STTS_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0)
+#define SCD_TX_STTS_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0)
+
+/* Translation Data */
+#define SCD_TRANS_TBL_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0)
+#define SCD_TRANS_TBL_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x808)
+
+#define SCD_CONTEXT_QUEUE_OFFSET(x)\
+ (SCD_CONTEXT_MEM_LOWER_BOUND + ((x) * 8))
+
+#define SCD_TRANS_TBL_OFFSET_QUEUE(x) \
+ ((SCD_TRANS_TBL_MEM_LOWER_BOUND + ((x) * 2)) & 0xfffc)
+
+#define SCD_QUEUECHAIN_SEL_ALL(priv) \
+ (((1<<hw_params(priv).max_txq_num) - 1) &\
+ (~(1<<(priv)->shrd->cmd_queue)))
+
+#define SCD_BASE (PRPH_BASE + 0xa02c00)
+
+#define SCD_SRAM_BASE_ADDR (SCD_BASE + 0x0)
+#define SCD_DRAM_BASE_ADDR (SCD_BASE + 0x8)
+#define SCD_AIT (SCD_BASE + 0x0c)
+#define SCD_TXFACT (SCD_BASE + 0x10)
+#define SCD_ACTIVE (SCD_BASE + 0x14)
+#define SCD_QUEUE_WRPTR(x) (SCD_BASE + 0x18 + (x) * 4)
+#define SCD_QUEUE_RDPTR(x) (SCD_BASE + 0x68 + (x) * 4)
+#define SCD_QUEUECHAIN_SEL (SCD_BASE + 0xe8)
+#define SCD_AGGR_SEL (SCD_BASE + 0x248)
+#define SCD_INTERRUPT_MASK (SCD_BASE + 0x108)
+#define SCD_QUEUE_STATUS_BITS(x) (SCD_BASE + 0x10c + (x) * 4)
/*********************** END TX SCHEDULER *************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index d60d630cb93a..e5d727f537d0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -33,9 +33,9 @@
#include "iwl-eeprom.h"
#include "iwl-dev.h"
#include "iwl-core.h"
-#include "iwl-sta.h"
#include "iwl-io.h"
-#include "iwl-helpers.h"
+#include "iwl-agn.h"
+#include "iwl-trans.h"
/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
* sending probe req. This should be set long enough to hear probe responses
@@ -60,20 +60,20 @@ static int iwl_send_scan_abort(struct iwl_priv *priv)
struct iwl_rx_packet *pkt;
struct iwl_host_cmd cmd = {
.id = REPLY_SCAN_ABORT_CMD,
- .flags = CMD_WANT_SKB,
+ .flags = CMD_SYNC | CMD_WANT_SKB,
};
/* Exit instantly with error when device is not ready
* to receive scan abort command or it does not perform
* hardware scan currently */
- if (!test_bit(STATUS_READY, &priv->status) ||
- !test_bit(STATUS_GEO_CONFIGURED, &priv->status) ||
- !test_bit(STATUS_SCAN_HW, &priv->status) ||
- test_bit(STATUS_FW_ERROR, &priv->status) ||
- test_bit(STATUS_EXIT_PENDING, &priv->status))
+ if (!test_bit(STATUS_READY, &priv->shrd->status) ||
+ !test_bit(STATUS_GEO_CONFIGURED, &priv->shrd->status) ||
+ !test_bit(STATUS_SCAN_HW, &priv->shrd->status) ||
+ test_bit(STATUS_FW_ERROR, &priv->shrd->status) ||
+ test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
return -EIO;
- ret = iwl_send_cmd_sync(priv, &cmd);
+ ret = iwl_trans_send_cmd(trans(priv), &cmd);
if (ret)
return ret;
@@ -89,7 +89,7 @@ static int iwl_send_scan_abort(struct iwl_priv *priv)
ret = -EIO;
}
- iwl_free_pages(priv, cmd.reply_page);
+ iwl_free_pages(priv->shrd, cmd.reply_page);
return ret;
}
@@ -101,24 +101,90 @@ static void iwl_complete_scan(struct iwl_priv *priv, bool aborted)
ieee80211_scan_completed(priv->hw, aborted);
}
+ if (priv->scan_type == IWL_SCAN_ROC) {
+ ieee80211_remain_on_channel_expired(priv->hw);
+ priv->hw_roc_channel = NULL;
+ schedule_delayed_work(&priv->hw_roc_disable_work, 10 * HZ);
+ }
+
priv->scan_type = IWL_SCAN_NORMAL;
priv->scan_vif = NULL;
priv->scan_request = NULL;
}
+static void iwl_process_scan_complete(struct iwl_priv *priv)
+{
+ bool aborted;
+
+ lockdep_assert_held(&priv->shrd->mutex);
+
+ if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->shrd->status))
+ return;
+
+ IWL_DEBUG_SCAN(priv, "Completed scan.\n");
+
+ cancel_delayed_work(&priv->scan_check);
+
+ aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->shrd->status);
+ if (aborted)
+ IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n");
+
+ if (!test_and_clear_bit(STATUS_SCANNING, &priv->shrd->status)) {
+ IWL_DEBUG_SCAN(priv, "Scan already completed.\n");
+ goto out_settings;
+ }
+
+ if (priv->scan_type == IWL_SCAN_ROC) {
+ ieee80211_remain_on_channel_expired(priv->hw);
+ priv->hw_roc_channel = NULL;
+ schedule_delayed_work(&priv->hw_roc_disable_work, 10 * HZ);
+ }
+
+ if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) {
+ int err;
+
+ /* Check if mac80211 requested scan during our internal scan */
+ if (priv->scan_request == NULL)
+ goto out_complete;
+
+ /* If so request a new scan */
+ err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL,
+ priv->scan_request->channels[0]->band);
+ if (err) {
+ IWL_DEBUG_SCAN(priv,
+ "failed to initiate pending scan: %d\n", err);
+ aborted = true;
+ goto out_complete;
+ }
+
+ return;
+ }
+
+out_complete:
+ iwl_complete_scan(priv, aborted);
+
+out_settings:
+ /* Can we still talk to firmware ? */
+ if (!iwl_is_ready_rf(priv->shrd))
+ return;
+
+ iwlagn_post_scan(priv);
+}
+
void iwl_force_scan_end(struct iwl_priv *priv)
{
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
- if (!test_bit(STATUS_SCANNING, &priv->status)) {
+ if (!test_bit(STATUS_SCANNING, &priv->shrd->status)) {
IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n");
return;
}
IWL_DEBUG_SCAN(priv, "Forcing scan end\n");
- clear_bit(STATUS_SCANNING, &priv->status);
- clear_bit(STATUS_SCAN_HW, &priv->status);
- clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+ clear_bit(STATUS_SCANNING, &priv->shrd->status);
+ clear_bit(STATUS_SCAN_HW, &priv->shrd->status);
+ clear_bit(STATUS_SCAN_ABORTING, &priv->shrd->status);
+ clear_bit(STATUS_SCAN_COMPLETE, &priv->shrd->status);
iwl_complete_scan(priv, true);
}
@@ -126,14 +192,14 @@ static void iwl_do_scan_abort(struct iwl_priv *priv)
{
int ret;
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
- if (!test_bit(STATUS_SCANNING, &priv->status)) {
+ if (!test_bit(STATUS_SCANNING, &priv->shrd->status)) {
IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n");
return;
}
- if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+ if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->shrd->status)) {
IWL_DEBUG_SCAN(priv, "Scan abort in progress\n");
return;
}
@@ -152,7 +218,7 @@ static void iwl_do_scan_abort(struct iwl_priv *priv)
int iwl_scan_cancel(struct iwl_priv *priv)
{
IWL_DEBUG_SCAN(priv, "Queuing abort scan\n");
- queue_work(priv->workqueue, &priv->abort_scan);
+ queue_work(priv->shrd->workqueue, &priv->abort_scan);
return 0;
}
@@ -161,28 +227,42 @@ int iwl_scan_cancel(struct iwl_priv *priv)
* @ms: amount of time to wait (in milliseconds) for scan to abort
*
*/
-int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
+void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
{
unsigned long timeout = jiffies + msecs_to_jiffies(ms);
- lockdep_assert_held(&priv->mutex);
+ lockdep_assert_held(&priv->shrd->mutex);
IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n");
iwl_do_scan_abort(priv);
while (time_before_eq(jiffies, timeout)) {
- if (!test_bit(STATUS_SCAN_HW, &priv->status))
- break;
+ if (!test_bit(STATUS_SCAN_HW, &priv->shrd->status))
+ goto finished;
msleep(20);
}
- return test_bit(STATUS_SCAN_HW, &priv->status);
+ return;
+
+ finished:
+ /*
+ * Now STATUS_SCAN_HW is clear. This means that the
+ * device finished, but the background work is going
+ * to execute at best as soon as we release the mutex.
+ * Since we need to be able to issue a new scan right
+ * after this function returns, run the complete here.
+ * The STATUS_SCAN_COMPLETE bit will then be cleared
+ * and prevent the background work from "completing"
+ * a possible new scan.
+ */
+ iwl_process_scan_complete(priv);
}
/* Service response to REPLY_SCAN_CMD (0x80) */
-static void iwl_rx_reply_scan(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwl_rx_reply_scan(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
#ifdef CONFIG_IWLWIFI_DEBUG
struct iwl_rx_packet *pkt = rxb_addr(rxb);
@@ -191,11 +271,13 @@ static void iwl_rx_reply_scan(struct iwl_priv *priv,
IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status);
#endif
+ return 0;
}
/* Service SCAN_START_NOTIFICATION (0x82) */
-static void iwl_rx_scan_start_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwl_rx_scan_start_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_scanstart_notification *notif =
@@ -209,11 +291,20 @@ static void iwl_rx_scan_start_notif(struct iwl_priv *priv,
le32_to_cpu(notif->tsf_high),
le32_to_cpu(notif->tsf_low),
notif->status, notif->beacon_timer);
+
+ if (priv->scan_type == IWL_SCAN_ROC &&
+ !priv->hw_roc_start_notified) {
+ ieee80211_ready_on_channel(priv->hw);
+ priv->hw_roc_start_notified = true;
+ }
+
+ return 0;
}
/* Service SCAN_RESULTS_NOTIFICATION (0x83) */
-static void iwl_rx_scan_results_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwl_rx_scan_results_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
#ifdef CONFIG_IWLWIFI_DEBUG
struct iwl_rx_packet *pkt = rxb_addr(rxb);
@@ -222,20 +313,24 @@ static void iwl_rx_scan_results_notif(struct iwl_priv *priv,
IWL_DEBUG_SCAN(priv, "Scan ch.res: "
"%d [802.11%s] "
+ "probe status: %u:%u "
"(TSF: 0x%08X:%08X) - %d "
"elapsed=%lu usec\n",
notif->channel,
notif->band ? "bg" : "a",
+ notif->probe_status, notif->num_probe_not_sent,
le32_to_cpu(notif->tsf_high),
le32_to_cpu(notif->tsf_low),
le32_to_cpu(notif->statistics[0]),
le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf);
#endif
+ return 0;
}
/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */
-static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
+static int iwl_rx_scan_complete_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw;
@@ -245,14 +340,21 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
scan_notif->tsf_low,
scan_notif->tsf_high, scan_notif->status);
- /* The HW is no longer scanning */
- clear_bit(STATUS_SCAN_HW, &priv->status);
-
IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n",
(priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2",
jiffies_to_msecs(jiffies - priv->scan_start));
- queue_work(priv->workqueue, &priv->scan_completed);
+ /*
+ * When aborting, we run the scan completed background work inline
+ * and the background work must then do nothing. The SCAN_COMPLETE
+ * bit helps implement that logic and thus needs to be set before
+ * queueing the work. Also, since the scan abort waits for SCAN_HW
+ * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW
+ * to avoid a race there.
+ */
+ set_bit(STATUS_SCAN_COMPLETE, &priv->shrd->status);
+ clear_bit(STATUS_SCAN_HW, &priv->shrd->status);
+ queue_work(priv->shrd->workqueue, &priv->scan_completed);
if (priv->iw_mode != NL80211_IFTYPE_ADHOC &&
iwl_advanced_bt_coexist(priv) &&
@@ -272,8 +374,10 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
IWL_BT_COEX_TRAFFIC_LOAD_NONE;
}
priv->bt_status = scan_notif->bt_status;
- queue_work(priv->workqueue, &priv->bt_traffic_change_work);
+ queue_work(priv->shrd->workqueue,
+ &priv->bt_traffic_change_work);
}
+ return 0;
}
void iwl_setup_rx_scan_handlers(struct iwl_priv *priv)
@@ -287,9 +391,8 @@ void iwl_setup_rx_scan_handlers(struct iwl_priv *priv)
iwl_rx_scan_complete_notif;
}
-inline u16 iwl_get_active_dwell_time(struct iwl_priv *priv,
- enum ieee80211_band band,
- u8 n_probes)
+static u16 iwl_get_active_dwell_time(struct iwl_priv *priv,
+ enum ieee80211_band band, u8 n_probes)
{
if (band == IEEE80211_BAND_5GHZ)
return IWL_ACTIVE_DWELL_TIME_52 +
@@ -299,40 +402,486 @@ inline u16 iwl_get_active_dwell_time(struct iwl_priv *priv,
IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1);
}
-u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
- enum ieee80211_band band,
- struct ieee80211_vif *vif)
+static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time)
{
struct iwl_rxon_context *ctx;
+
+ /*
+ * If we're associated, we clamp the dwell time 98%
+ * of the smallest beacon interval (minus 2 * channel
+ * tune time)
+ */
+ for_each_context(priv, ctx) {
+ u16 value;
+
+ if (!iwl_is_associated_ctx(ctx))
+ continue;
+ value = ctx->beacon_int;
+ if (!value)
+ value = IWL_PASSIVE_DWELL_BASE;
+ value = (value * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2;
+ dwell_time = min(value, dwell_time);
+ }
+
+ return dwell_time;
+}
+
+static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
+ enum ieee80211_band band)
+{
u16 passive = (band == IEEE80211_BAND_2GHZ) ?
IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 :
IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52;
- if (iwl_is_any_associated(priv)) {
+ return iwl_limit_dwell(priv, passive);
+}
+
+static int iwl_get_single_channel_for_scan(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ enum ieee80211_band band,
+ struct iwl_scan_channel *scan_ch)
+{
+ const struct ieee80211_supported_band *sband;
+ u16 passive_dwell = 0;
+ u16 active_dwell = 0;
+ int added = 0;
+ u16 channel = 0;
+
+ sband = iwl_get_hw_mode(priv, band);
+ if (!sband) {
+ IWL_ERR(priv, "invalid band\n");
+ return added;
+ }
+
+ active_dwell = iwl_get_active_dwell_time(priv, band, 0);
+ passive_dwell = iwl_get_passive_dwell_time(priv, band);
+
+ if (passive_dwell <= active_dwell)
+ passive_dwell = active_dwell + 1;
+
+ channel = iwl_get_single_channel_number(priv, band);
+ if (channel) {
+ scan_ch->channel = cpu_to_le16(channel);
+ scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
+ scan_ch->active_dwell = cpu_to_le16(active_dwell);
+ scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
+ /* Set txpower levels to defaults */
+ scan_ch->dsp_atten = 110;
+ if (band == IEEE80211_BAND_5GHZ)
+ scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
+ else
+ scan_ch->tx_gain = ((1 << 5) | (5 << 3));
+ added++;
+ } else
+ IWL_ERR(priv, "no valid channel found\n");
+ return added;
+}
+
+static int iwl_get_channels_for_scan(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ enum ieee80211_band band,
+ u8 is_active, u8 n_probes,
+ struct iwl_scan_channel *scan_ch)
+{
+ struct ieee80211_channel *chan;
+ const struct ieee80211_supported_band *sband;
+ const struct iwl_channel_info *ch_info;
+ u16 passive_dwell = 0;
+ u16 active_dwell = 0;
+ int added, i;
+ u16 channel;
+
+ sband = iwl_get_hw_mode(priv, band);
+ if (!sband)
+ return 0;
+
+ active_dwell = iwl_get_active_dwell_time(priv, band, n_probes);
+ passive_dwell = iwl_get_passive_dwell_time(priv, band);
+
+ if (passive_dwell <= active_dwell)
+ passive_dwell = active_dwell + 1;
+
+ for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) {
+ chan = priv->scan_request->channels[i];
+
+ if (chan->band != band)
+ continue;
+
+ channel = chan->hw_value;
+ scan_ch->channel = cpu_to_le16(channel);
+
+ ch_info = iwl_get_channel_info(priv, band, channel);
+ if (!is_channel_valid(ch_info)) {
+ IWL_DEBUG_SCAN(priv,
+ "Channel %d is INVALID for this band.\n",
+ channel);
+ continue;
+ }
+
+ if (!is_active || is_channel_passive(ch_info) ||
+ (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN))
+ scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
+ else
+ scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;
+
+ if (n_probes)
+ scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes);
+
+ scan_ch->active_dwell = cpu_to_le16(active_dwell);
+ scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
+
+ /* Set txpower levels to defaults */
+ scan_ch->dsp_atten = 110;
+
+ /* NOTE: if we were doing 6Mb OFDM for scans we'd use
+ * power level:
+ * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
+ */
+ if (band == IEEE80211_BAND_5GHZ)
+ scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
+ else
+ scan_ch->tx_gain = ((1 << 5) | (5 << 3));
+
+ IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n",
+ channel, le32_to_cpu(scan_ch->type),
+ (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ?
+ "ACTIVE" : "PASSIVE",
+ (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ?
+ active_dwell : passive_dwell);
+
+ scan_ch++;
+ added++;
+ }
+
+ IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added);
+ return added;
+}
+
+static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
+{
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_SCAN_CMD,
+ .len = { sizeof(struct iwl_scan_cmd), },
+ .flags = CMD_SYNC,
+ };
+ struct iwl_scan_cmd *scan;
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ u32 rate_flags = 0;
+ u16 cmd_len;
+ u16 rx_chain = 0;
+ enum ieee80211_band band;
+ u8 n_probes = 0;
+ u8 rx_ant = hw_params(priv).valid_rx_ant;
+ u8 rate;
+ bool is_active = false;
+ int chan_mod;
+ u8 active_chains;
+ u8 scan_tx_antennas = hw_params(priv).valid_tx_ant;
+ int ret;
+
+ lockdep_assert_held(&priv->shrd->mutex);
+
+ if (vif)
+ ctx = iwl_rxon_ctx_from_vif(vif);
+
+ if (!priv->scan_cmd) {
+ priv->scan_cmd = kmalloc(sizeof(struct iwl_scan_cmd) +
+ IWL_MAX_SCAN_SIZE, GFP_KERNEL);
+ if (!priv->scan_cmd) {
+ IWL_DEBUG_SCAN(priv,
+ "fail to allocate memory for scan\n");
+ return -ENOMEM;
+ }
+ }
+ scan = priv->scan_cmd;
+ memset(scan, 0, sizeof(struct iwl_scan_cmd) + IWL_MAX_SCAN_SIZE);
+
+ scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
+ scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
+
+ if (priv->scan_type != IWL_SCAN_ROC &&
+ iwl_is_any_associated(priv)) {
+ u16 interval = 0;
+ u32 extra;
+ u32 suspend_time = 100;
+ u32 scan_suspend_time = 100;
+
+ IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
+ switch (priv->scan_type) {
+ case IWL_SCAN_ROC:
+ WARN_ON(1);
+ break;
+ case IWL_SCAN_RADIO_RESET:
+ interval = 0;
+ break;
+ case IWL_SCAN_NORMAL:
+ interval = vif->bss_conf.beacon_int;
+ break;
+ }
+
+ scan->suspend_time = 0;
+ scan->max_out_time = cpu_to_le32(200 * 1024);
+ if (!interval)
+ interval = suspend_time;
+
+ extra = (suspend_time / interval) << 22;
+ scan_suspend_time = (extra |
+ ((suspend_time % interval) * 1024));
+ scan->suspend_time = cpu_to_le32(scan_suspend_time);
+ IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n",
+ scan_suspend_time, interval);
+ } else if (priv->scan_type == IWL_SCAN_ROC) {
+ scan->suspend_time = 0;
+ scan->max_out_time = 0;
+ scan->quiet_time = 0;
+ scan->quiet_plcp_th = 0;
+ }
+
+ switch (priv->scan_type) {
+ case IWL_SCAN_RADIO_RESET:
+ IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
+ break;
+ case IWL_SCAN_NORMAL:
+ if (priv->scan_request->n_ssids) {
+ int i, p = 0;
+ IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
+ for (i = 0; i < priv->scan_request->n_ssids; i++) {
+ /* always does wildcard anyway */
+ if (!priv->scan_request->ssids[i].ssid_len)
+ continue;
+ scan->direct_scan[p].id = WLAN_EID_SSID;
+ scan->direct_scan[p].len =
+ priv->scan_request->ssids[i].ssid_len;
+ memcpy(scan->direct_scan[p].ssid,
+ priv->scan_request->ssids[i].ssid,
+ priv->scan_request->ssids[i].ssid_len);
+ n_probes++;
+ p++;
+ }
+ is_active = true;
+ } else
+ IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
+ break;
+ case IWL_SCAN_ROC:
+ IWL_DEBUG_SCAN(priv, "Start ROC scan.\n");
+ break;
+ }
+
+ scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
+ scan->tx_cmd.sta_id = ctx->bcast_sta_id;
+ scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
+
+ switch (priv->scan_band) {
+ case IEEE80211_BAND_2GHZ:
+ scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
+ chan_mod = le32_to_cpu(
+ priv->contexts[IWL_RXON_CTX_BSS].active.flags &
+ RXON_FLG_CHANNEL_MODE_MSK)
+ >> RXON_FLG_CHANNEL_MODE_POS;
+ if (chan_mod == CHANNEL_MODE_PURE_40) {
+ rate = IWL_RATE_6M_PLCP;
+ } else {
+ rate = IWL_RATE_1M_PLCP;
+ rate_flags = RATE_MCS_CCK_MSK;
+ }
/*
- * If we're associated, we clamp the maximum passive
- * dwell time to be 98% of the smallest beacon interval
- * (minus 2 * channel tune time)
+ * Internal scans are passive, so we can indiscriminately set
+ * the BT ignore flag on 2.4 GHz since it applies to TX only.
*/
- for_each_context(priv, ctx) {
- u16 value;
-
- if (!iwl_is_associated_ctx(ctx))
- continue;
- value = ctx->vif ? ctx->vif->bss_conf.beacon_int : 0;
- if ((value > IWL_PASSIVE_DWELL_BASE) || !value)
- value = IWL_PASSIVE_DWELL_BASE;
- value = (value * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2;
- passive = min(value, passive);
+ if (priv->cfg->bt_params &&
+ priv->cfg->bt_params->advanced_bt_coexist)
+ scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT;
+ break;
+ case IEEE80211_BAND_5GHZ:
+ rate = IWL_RATE_6M_PLCP;
+ break;
+ default:
+ IWL_WARN(priv, "Invalid scan band\n");
+ return -EIO;
+ }
+
+ /*
+ * If active scanning is requested but a certain channel is
+ * marked passive, we can do active scanning if we detect
+ * transmissions.
+ *
+ * There is an issue with some firmware versions that triggers
+ * a sysassert on a "good CRC threshold" of zero (== disabled),
+ * on a radar channel even though this means that we should NOT
+ * send probes.
+ *
+ * The "good CRC threshold" is the number of frames that we
+ * need to receive during our dwell time on a channel before
+ * sending out probes -- setting this to a huge value will
+ * mean we never reach it, but at the same time work around
+ * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER
+ * here instead of IWL_GOOD_CRC_TH_DISABLED.
+ *
+ * This was fixed in later versions along with some other
+ * scan changes, and the threshold behaves as a flag in those
+ * versions.
+ */
+ if (priv->new_scan_threshold_behaviour)
+ scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
+ IWL_GOOD_CRC_TH_DISABLED;
+ else
+ scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
+ IWL_GOOD_CRC_TH_NEVER;
+
+ band = priv->scan_band;
+
+ if (priv->cfg->scan_rx_antennas[band])
+ rx_ant = priv->cfg->scan_rx_antennas[band];
+
+ if (band == IEEE80211_BAND_2GHZ &&
+ priv->cfg->bt_params &&
+ priv->cfg->bt_params->advanced_bt_coexist) {
+ /* transmit 2.4 GHz probes only on first antenna */
+ scan_tx_antennas = first_antenna(scan_tx_antennas);
+ }
+
+ priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv,
+ priv->scan_tx_ant[band],
+ scan_tx_antennas);
+ rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);
+ scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags);
+
+ /* In power save mode use one chain, otherwise use all chains */
+ if (test_bit(STATUS_POWER_PMI, &priv->shrd->status)) {
+ /* rx_ant has been set to all valid chains previously */
+ active_chains = rx_ant &
+ ((u8)(priv->chain_noise_data.active_chains));
+ if (!active_chains)
+ active_chains = rx_ant;
+
+ IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n",
+ priv->chain_noise_data.active_chains);
+
+ rx_ant = first_antenna(active_chains);
+ }
+ if (priv->cfg->bt_params &&
+ priv->cfg->bt_params->advanced_bt_coexist &&
+ priv->bt_full_concurrent) {
+ /* operated as 1x1 in full concurrency mode */
+ rx_ant = first_antenna(rx_ant);
+ }
+
+ /* MIMO is not used here, but value is required */
+ rx_chain |=
+ hw_params(priv).valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
+ rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
+ rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
+ rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
+ scan->rx_chain = cpu_to_le16(rx_chain);
+ switch (priv->scan_type) {
+ case IWL_SCAN_NORMAL:
+ cmd_len = iwl_fill_probe_req(priv,
+ (struct ieee80211_mgmt *)scan->data,
+ vif->addr,
+ priv->scan_request->ie,
+ priv->scan_request->ie_len,
+ IWL_MAX_SCAN_SIZE - sizeof(*scan));
+ break;
+ case IWL_SCAN_RADIO_RESET:
+ case IWL_SCAN_ROC:
+ /* use bcast addr, will not be transmitted but must be valid */
+ cmd_len = iwl_fill_probe_req(priv,
+ (struct ieee80211_mgmt *)scan->data,
+ iwl_bcast_addr, NULL, 0,
+ IWL_MAX_SCAN_SIZE - sizeof(*scan));
+ break;
+ default:
+ BUG();
+ }
+ scan->tx_cmd.len = cpu_to_le16(cmd_len);
+
+ scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
+ RXON_FILTER_BCON_AWARE_MSK);
+
+ switch (priv->scan_type) {
+ case IWL_SCAN_RADIO_RESET:
+ scan->channel_count =
+ iwl_get_single_channel_for_scan(priv, vif, band,
+ (void *)&scan->data[cmd_len]);
+ break;
+ case IWL_SCAN_NORMAL:
+ scan->channel_count =
+ iwl_get_channels_for_scan(priv, vif, band,
+ is_active, n_probes,
+ (void *)&scan->data[cmd_len]);
+ break;
+ case IWL_SCAN_ROC: {
+ struct iwl_scan_channel *scan_ch;
+ int n_chan, i;
+ u16 dwell;
+
+ dwell = iwl_limit_dwell(priv, priv->hw_roc_duration);
+ n_chan = DIV_ROUND_UP(priv->hw_roc_duration, dwell);
+
+ scan->channel_count = n_chan;
+
+ scan_ch = (void *)&scan->data[cmd_len];
+
+ for (i = 0; i < n_chan; i++) {
+ scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
+ scan_ch->channel =
+ cpu_to_le16(priv->hw_roc_channel->hw_value);
+
+ if (i == n_chan - 1)
+ dwell = priv->hw_roc_duration - i * dwell;
+
+ scan_ch->active_dwell =
+ scan_ch->passive_dwell = cpu_to_le16(dwell);
+
+ /* Set txpower levels to defaults */
+ scan_ch->dsp_atten = 110;
+
+ /* NOTE: if we were doing 6Mb OFDM for scans we'd use
+ * power level:
+ * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
+ */
+ if (priv->hw_roc_channel->band == IEEE80211_BAND_5GHZ)
+ scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
+ else
+ scan_ch->tx_gain = ((1 << 5) | (5 << 3));
+
+ scan_ch++;
}
+ }
+
+ break;
+ }
+
+ if (scan->channel_count == 0) {
+ IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
+ return -EIO;
}
- return passive;
+ cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) +
+ scan->channel_count * sizeof(struct iwl_scan_channel);
+ cmd.data[0] = scan;
+ cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+ scan->len = cpu_to_le16(cmd.len[0]);
+
+ /* set scan bit here for PAN params */
+ set_bit(STATUS_SCAN_HW, &priv->shrd->status);
+
+ ret = iwlagn_set_pan_params(priv);
+ if (ret)
+ return ret;
+
+ ret = iwl_trans_send_cmd(trans(priv), &cmd);
+ if (ret) {
+ clear_bit(STATUS_SCAN_HW, &priv->shrd->status);
+ iwlagn_set_pan_params(priv);
+ }
+
+ return ret;
}
void iwl_init_scan_params(struct iwl_priv *priv)
{
- u8 ant_idx = fls(priv->hw_params.valid_tx_ant) - 1;
+ u8 ant_idx = fls(hw_params(priv).valid_tx_ant) - 1;
if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ])
priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx;
if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ])
@@ -346,53 +895,50 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv,
{
int ret;
- lockdep_assert_held(&priv->mutex);
-
- if (WARN_ON(!priv->cfg->ops->utils->request_scan))
- return -EOPNOTSUPP;
+ lockdep_assert_held(&priv->shrd->mutex);
cancel_delayed_work(&priv->scan_check);
- if (!iwl_is_ready_rf(priv)) {
+ if (!iwl_is_ready_rf(priv->shrd)) {
IWL_WARN(priv, "Request scan called when driver not ready.\n");
return -EIO;
}
- if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+ if (test_bit(STATUS_SCAN_HW, &priv->shrd->status)) {
IWL_DEBUG_SCAN(priv,
"Multiple concurrent scan requests in parallel.\n");
return -EBUSY;
}
- if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+ if (test_bit(STATUS_SCAN_ABORTING, &priv->shrd->status)) {
IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n");
return -EBUSY;
}
IWL_DEBUG_SCAN(priv, "Starting %sscan...\n",
scan_type == IWL_SCAN_NORMAL ? "" :
- scan_type == IWL_SCAN_OFFCH_TX ? "offchan TX " :
+ scan_type == IWL_SCAN_ROC ? "remain-on-channel " :
"internal short ");
- set_bit(STATUS_SCANNING, &priv->status);
+ set_bit(STATUS_SCANNING, &priv->shrd->status);
priv->scan_type = scan_type;
priv->scan_start = jiffies;
priv->scan_band = band;
- ret = priv->cfg->ops->utils->request_scan(priv, vif);
+ ret = iwlagn_request_scan(priv, vif);
if (ret) {
- clear_bit(STATUS_SCANNING, &priv->status);
+ clear_bit(STATUS_SCANNING, &priv->shrd->status);
priv->scan_type = IWL_SCAN_NORMAL;
return ret;
}
- queue_delayed_work(priv->workqueue, &priv->scan_check,
+ queue_delayed_work(priv->shrd->workqueue, &priv->scan_check,
IWL_SCAN_CHECK_WATCHDOG);
return 0;
}
-int iwl_mac_hw_scan(struct ieee80211_hw *hw,
+int iwlagn_mac_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
{
@@ -404,34 +950,36 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw,
if (req->n_channels == 0)
return -EINVAL;
- mutex_lock(&priv->mutex);
-
- if (test_bit(STATUS_SCANNING, &priv->status) &&
- priv->scan_type != IWL_SCAN_NORMAL) {
- IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
- ret = -EAGAIN;
- goto out_unlock;
- }
-
- /* mac80211 will only ask for one band at a time */
- priv->scan_request = req;
- priv->scan_vif = vif;
+ mutex_lock(&priv->shrd->mutex);
/*
* If an internal scan is in progress, just set
* up the scan_request as per above.
*/
if (priv->scan_type != IWL_SCAN_NORMAL) {
- IWL_DEBUG_SCAN(priv, "SCAN request during internal scan\n");
+ IWL_DEBUG_SCAN(priv,
+ "SCAN request during internal scan - defer\n");
+ priv->scan_request = req;
+ priv->scan_vif = vif;
ret = 0;
- } else
+ } else {
+ priv->scan_request = req;
+ priv->scan_vif = vif;
+ /*
+ * mac80211 will only ask for one band at a time
+ * so using channels[0] here is ok
+ */
ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL,
req->channels[0]->band);
+ if (ret) {
+ priv->scan_request = NULL;
+ priv->scan_vif = NULL;
+ }
+ }
IWL_DEBUG_MAC80211(priv, "leave\n");
-out_unlock:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
return ret;
}
@@ -442,7 +990,7 @@ out_unlock:
*/
void iwl_internal_short_hw_scan(struct iwl_priv *priv)
{
- queue_work(priv->workqueue, &priv->start_internal_scan);
+ queue_work(priv->shrd->workqueue, &priv->start_internal_scan);
}
static void iwl_bg_start_internal_scan(struct work_struct *work)
@@ -452,14 +1000,14 @@ static void iwl_bg_start_internal_scan(struct work_struct *work)
IWL_DEBUG_SCAN(priv, "Start internal scan\n");
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
if (priv->scan_type == IWL_SCAN_RADIO_RESET) {
IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n");
goto unlock;
}
- if (test_bit(STATUS_SCANNING, &priv->status)) {
+ if (test_bit(STATUS_SCANNING, &priv->shrd->status)) {
IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
goto unlock;
}
@@ -467,7 +1015,7 @@ static void iwl_bg_start_internal_scan(struct work_struct *work)
if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band))
IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n");
unlock:
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
static void iwl_bg_scan_check(struct work_struct *data)
@@ -480,9 +1028,9 @@ static void iwl_bg_scan_check(struct work_struct *data)
/* Since we are here firmware does not finish scan and
* most likely is in bad shape, so we don't bother to
* send abort command, just force scan complete to mac80211 */
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
iwl_force_scan_end(priv);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
/**
@@ -540,77 +1088,19 @@ static void iwl_bg_abort_scan(struct work_struct *work)
/* We keep scan_check work queued in case when firmware will not
* report back scan completed notification */
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
iwl_scan_cancel_timeout(priv, 200);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
static void iwl_bg_scan_completed(struct work_struct *work)
{
struct iwl_priv *priv =
- container_of(work, struct iwl_priv, scan_completed);
- bool aborted;
-
- IWL_DEBUG_SCAN(priv, "Completed scan.\n");
-
- cancel_delayed_work(&priv->scan_check);
-
- mutex_lock(&priv->mutex);
-
- aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status);
- if (aborted)
- IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n");
-
- if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) {
- IWL_DEBUG_SCAN(priv, "Scan already completed.\n");
- goto out_settings;
- }
-
- if (priv->scan_type == IWL_SCAN_OFFCH_TX && priv->_agn.offchan_tx_skb) {
- ieee80211_tx_status_irqsafe(priv->hw,
- priv->_agn.offchan_tx_skb);
- priv->_agn.offchan_tx_skb = NULL;
- }
-
- if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) {
- int err;
-
- /* Check if mac80211 requested scan during our internal scan */
- if (priv->scan_request == NULL)
- goto out_complete;
-
- /* If so request a new scan */
- err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL,
- priv->scan_request->channels[0]->band);
- if (err) {
- IWL_DEBUG_SCAN(priv,
- "failed to initiate pending scan: %d\n", err);
- aborted = true;
- goto out_complete;
- }
-
- goto out;
- }
-
-out_complete:
- iwl_complete_scan(priv, aborted);
-
-out_settings:
- /* Can we still talk to firmware ? */
- if (!iwl_is_ready_rf(priv))
- goto out;
-
- /*
- * We do not commit power settings while scan is pending,
- * do it now if the settings changed.
- */
- iwl_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false);
- iwl_set_tx_power(priv, priv->tx_power_next, false);
-
- priv->cfg->ops->utils->post_scan(priv);
+ container_of(work, struct iwl_priv, scan_completed);
-out:
- mutex_unlock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
+ iwl_process_scan_complete(priv);
+ mutex_unlock(&priv->shrd->mutex);
}
void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
@@ -628,8 +1118,8 @@ void iwl_cancel_scan_deferred_work(struct iwl_priv *priv)
cancel_work_sync(&priv->scan_completed);
if (cancel_delayed_work_sync(&priv->scan_check)) {
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
iwl_force_scan_end(priv);
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
}
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-shared.h b/drivers/net/wireless/iwlwifi/iwl-shared.h
new file mode 100644
index 000000000000..1f7a93c67c45
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-shared.h
@@ -0,0 +1,534 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_shared_h__
+#define __iwl_shared_h__
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/gfp.h>
+#include <linux/mm.h> /* for page_address */
+#include <net/mac80211.h>
+
+#include "iwl-commands.h"
+
+/**
+ * DOC: shared area - role and goal
+ *
+ * The shared area contains all the data exported by the upper layer to the
+ * other layers. Since the bus and transport layer shouldn't dereference
+ * iwl_priv, all the data needed by the upper layer and the transport / bus
+ * layer must be here.
+ * The shared area also holds pointer to all the other layers. This allows a
+ * layer to call a function from another layer.
+ *
+ * NOTE: All the layers hold a pointer to the shared area which must be shrd.
+ * A few macros assume that (_m)->shrd points to the shared area no matter
+ * what _m is.
+ *
+ * gets notifications about enumeration, suspend, resume.
+ * For the moment, the bus layer is not a linux kernel module as itself, and
+ * the module_init function of the driver must call the bus specific
+ * registration functions. These functions are listed at the end of this file.
+ * For the moment, there is only one implementation of this interface: PCI-e.
+ * This implementation is iwl-pci.c
+ */
+
+struct iwl_cfg;
+struct iwl_bus;
+struct iwl_priv;
+struct iwl_sensitivity_ranges;
+struct iwl_trans_ops;
+
+#define DRV_NAME "iwlwifi"
+#define IWLWIFI_VERSION "in-tree:"
+#define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation"
+#define DRV_AUTHOR "<ilw@linux.intel.com>"
+
+extern struct iwl_mod_params iwlagn_mod_params;
+
+/**
+ * struct iwl_mod_params
+ *
+ * Holds the module parameters
+ *
+ * @sw_crypto: using hardware encryption, default = 0
+ * @num_of_queues: number of tx queue, HW dependent
+ * @disable_11n: 11n capabilities enabled, default = 0
+ * @amsdu_size_8K: enable 8K amsdu size, default = 1
+ * @antenna: both antennas (use diversity), default = 0
+ * @restart_fw: restart firmware, default = 1
+ * @plcp_check: enable plcp health check, default = true
+ * @ack_check: disable ack health check, default = false
+ * @wd_disable: enable stuck queue check, default = false
+ * @bt_coex_active: enable bt coex, default = true
+ * @led_mode: system default, default = 0
+ * @no_sleep_autoadjust: disable autoadjust, default = true
+ * @power_save: disable power save, default = false
+ * @power_level: power level, default = 1
+ * @debug_level: levels are IWL_DL_*
+ * @ant_coupling: antenna coupling in dB, default = 0
+ * @bt_ch_announce: BT channel inhibition, default = enable
+ * @wanted_ucode_alternative: ucode alternative to use, default = 1
+ * @auto_agg: enable agg. without check, default = true
+ */
+struct iwl_mod_params {
+ int sw_crypto;
+ int num_of_queues;
+ int disable_11n;
+ int amsdu_size_8K;
+ int antenna;
+ int restart_fw;
+ bool plcp_check;
+ bool ack_check;
+ bool wd_disable;
+ bool bt_coex_active;
+ int led_mode;
+ bool no_sleep_autoadjust;
+ bool power_save;
+ int power_level;
+ u32 debug_level;
+ int ant_coupling;
+ bool bt_ch_announce;
+ int wanted_ucode_alternative;
+ bool auto_agg;
+};
+
+/**
+ * struct iwl_hw_params
+ *
+ * Holds the module parameters
+ *
+ * @max_txq_num: Max # Tx queues supported
+ * @num_ampdu_queues: num of ampdu queues
+ * @tx_chains_num: Number of TX chains
+ * @rx_chains_num: Number of RX chains
+ * @valid_tx_ant: usable antennas for TX
+ * @valid_rx_ant: usable antennas for RX
+ * @ht40_channel: is 40MHz width possible: BIT(IEEE80211_BAND_XXX)
+ * @sku: sku read from EEPROM
+ * @rx_page_order: Rx buffer page order
+ * @max_inst_size: for ucode use
+ * @max_data_size: for ucode use
+ * @ct_kill_threshold: temperature threshold - in hw dependent unit
+ * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit
+ * relevant for 1000, 6000 and up
+ * @wd_timeout: TX queues watchdog timeout
+ * @calib_init_cfg: setup initial calibrations for the hw
+ * @calib_rt_cfg: setup runtime calibrations for the hw
+ * @struct iwl_sensitivity_ranges: range of sensitivity values
+ */
+struct iwl_hw_params {
+ u8 max_txq_num;
+ u8 num_ampdu_queues;
+ u8 tx_chains_num;
+ u8 rx_chains_num;
+ u8 valid_tx_ant;
+ u8 valid_rx_ant;
+ u8 ht40_channel;
+ bool shadow_reg_enable;
+ u16 sku;
+ u32 rx_page_order;
+ u32 max_inst_size;
+ u32 max_data_size;
+ u32 ct_kill_threshold;
+ u32 ct_kill_exit_threshold;
+ unsigned int wd_timeout;
+
+ u32 calib_init_cfg;
+ u32 calib_rt_cfg;
+ const struct iwl_sensitivity_ranges *sens;
+};
+
+/**
+ * enum iwl_agg_state
+ *
+ * The state machine of the BA agreement establishment / tear down.
+ * These states relate to a specific RA / TID.
+ *
+ * @IWL_AGG_OFF: aggregation is not used
+ * @IWL_AGG_ON: aggregation session is up
+ * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the
+ * HW queue to be empty from packets for this RA /TID.
+ * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the
+ * HW queue to be empty from packets for this RA /TID.
+ */
+enum iwl_agg_state {
+ IWL_AGG_OFF = 0,
+ IWL_AGG_ON,
+ IWL_EMPTYING_HW_QUEUE_ADDBA,
+ IWL_EMPTYING_HW_QUEUE_DELBA,
+};
+
+/**
+ * struct iwl_ht_agg - aggregation state machine
+
+ * This structs holds the states for the BA agreement establishment and tear
+ * down. It also holds the state during the BA session itself. This struct is
+ * duplicated for each RA / TID.
+
+ * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
+ * Tx response (REPLY_TX), and the block ack notification
+ * (REPLY_COMPRESSED_BA).
+ * @state: state of the BA agreement establishment / tear down.
+ * @txq_id: Tx queue used by the BA session - used by the transport layer.
+ * Needed by the upper layer for debugfs only.
+ * @wait_for_ba: Expect block-ack before next Tx reply
+ */
+struct iwl_ht_agg {
+ u32 rate_n_flags;
+ enum iwl_agg_state state;
+ u16 txq_id;
+ bool wait_for_ba;
+};
+
+/**
+ * struct iwl_tid_data - one for each RA / TID
+
+ * This structs holds the states for each RA / TID.
+
+ * @seq_number: the next WiFi sequence number to use
+ * @tfds_in_queue: number of packets sent to the HW queues.
+ * Exported for debugfs only
+ * @agg: aggregation state machine
+ */
+struct iwl_tid_data {
+ u16 seq_number;
+ u16 tfds_in_queue;
+ struct iwl_ht_agg agg;
+};
+
+/**
+ * struct iwl_shared - shared fields for all the layers of the driver
+ *
+ * @dbg_level_dev: dbg level set per device. Prevails on
+ * iwlagn_mod_params.debug_level if set (!= 0)
+ * @ucode_owner: IWL_OWNERSHIP_*
+ * @cmd_queue: command queue number
+ * @status: STATUS_*
+ * @valid_contexts: microcode/device supports multiple contexts
+ * @bus: pointer to the bus layer data
+ * @priv: pointer to the upper layer data
+ * @hw_params: see struct iwl_hw_params
+ * @workqueue: the workqueue used by all the layers of the driver
+ * @lock: protect general shared data
+ * @sta_lock: protects the station table.
+ * If lock and sta_lock are needed, lock must be acquired first.
+ * @mutex:
+ */
+struct iwl_shared {
+#ifdef CONFIG_IWLWIFI_DEBUG
+ u32 dbg_level_dev;
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+#define IWL_OWNERSHIP_DRIVER 0
+#define IWL_OWNERSHIP_TM 1
+ u8 ucode_owner;
+ u8 cmd_queue;
+ unsigned long status;
+ bool wowlan;
+ u8 valid_contexts;
+
+ struct iwl_bus *bus;
+ struct iwl_priv *priv;
+ struct iwl_trans *trans;
+ struct iwl_hw_params hw_params;
+
+ struct workqueue_struct *workqueue;
+ spinlock_t lock;
+ spinlock_t sta_lock;
+ struct mutex mutex;
+
+ struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT];
+
+ wait_queue_head_t wait_command_queue;
+};
+
+/*Whatever _m is (iwl_trans, iwl_priv, iwl_bus, these macros will work */
+#define priv(_m) ((_m)->shrd->priv)
+#define bus(_m) ((_m)->shrd->bus)
+#define trans(_m) ((_m)->shrd->trans)
+#define hw_params(_m) ((_m)->shrd->hw_params)
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+/*
+ * iwl_get_debug_level: Return active debug level for device
+ *
+ * Using sysfs it is possible to set per device debug level. This debug
+ * level will be used if set, otherwise the global debug level which can be
+ * set via module parameter is used.
+ */
+static inline u32 iwl_get_debug_level(struct iwl_shared *shrd)
+{
+ if (shrd->dbg_level_dev)
+ return shrd->dbg_level_dev;
+ else
+ return iwlagn_mod_params.debug_level;
+}
+#else
+static inline u32 iwl_get_debug_level(struct iwl_shared *shrd)
+{
+ return iwlagn_mod_params.debug_level;
+}
+#endif
+
+static inline void iwl_free_pages(struct iwl_shared *shrd, unsigned long page)
+{
+ free_pages(page, shrd->hw_params.rx_page_order);
+}
+
+/**
+ * iwl_queue_inc_wrap - increment queue index, wrap back to beginning
+ * @index -- current index
+ * @n_bd -- total number of entries in queue (must be power of 2)
+ */
+static inline int iwl_queue_inc_wrap(int index, int n_bd)
+{
+ return ++index & (n_bd - 1);
+}
+
+/**
+ * iwl_queue_dec_wrap - decrement queue index, wrap back to end
+ * @index -- current index
+ * @n_bd -- total number of entries in queue (must be power of 2)
+ */
+static inline int iwl_queue_dec_wrap(int index, int n_bd)
+{
+ return --index & (n_bd - 1);
+}
+
+struct iwl_rx_mem_buffer {
+ dma_addr_t page_dma;
+ struct page *page;
+ struct list_head list;
+};
+
+#define rxb_addr(r) page_address(r->page)
+
+/*
+ * mac80211 queues, ACs, hardware queues, FIFOs.
+ *
+ * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues
+ *
+ * Mac80211 uses the following numbers, which we get as from it
+ * by way of skb_get_queue_mapping(skb):
+ *
+ * VO 0
+ * VI 1
+ * BE 2
+ * BK 3
+ *
+ *
+ * Regular (not A-MPDU) frames are put into hardware queues corresponding
+ * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their
+ * own queue per aggregation session (RA/TID combination), such queues are
+ * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In
+ * order to map frames to the right queue, we also need an AC->hw queue
+ * mapping. This is implemented here.
+ *
+ * Due to the way hw queues are set up (by the hw specific modules like
+ * iwl-4965.c, iwl-5000.c etc.), the AC->hw queue mapping is the identity
+ * mapping.
+ */
+
+static const u8 tid_to_ac[] = {
+ IEEE80211_AC_BE,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VO,
+ IEEE80211_AC_VO
+};
+
+static inline int get_ac_from_tid(u16 tid)
+{
+ if (likely(tid < ARRAY_SIZE(tid_to_ac)))
+ return tid_to_ac[tid];
+
+ /* no support for TIDs 8-15 yet */
+ return -EINVAL;
+}
+
+enum iwl_rxon_context_id {
+ IWL_RXON_CTX_BSS,
+ IWL_RXON_CTX_PAN,
+
+ NUM_IWL_RXON_CTX
+};
+
+int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops,
+ struct iwl_cfg *cfg);
+void __devexit iwl_remove(struct iwl_priv * priv);
+struct iwl_device_cmd;
+int __must_check iwl_rx_dispatch(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+
+int iwlagn_hw_valid_rtc_data_addr(u32 addr);
+void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv,
+ enum iwl_rxon_context_id ctx,
+ u8 sta_id, u8 tid);
+void iwl_stop_tx_ba_trans_ready(struct iwl_priv *priv,
+ enum iwl_rxon_context_id ctx,
+ u8 sta_id, u8 tid);
+void iwl_set_hw_rfkill_state(struct iwl_priv *priv, bool state);
+void iwl_nic_config(struct iwl_priv *priv);
+void iwl_free_skb(struct iwl_priv *priv, struct sk_buff *skb);
+void iwl_apm_stop(struct iwl_priv *priv);
+int iwl_apm_init(struct iwl_priv *priv);
+void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand);
+const char *get_cmd_string(u8 cmd);
+bool iwl_check_for_ct_kill(struct iwl_priv *priv);
+
+void iwl_stop_sw_queue(struct iwl_priv *priv, u8 ac);
+void iwl_wake_sw_queue(struct iwl_priv *priv, u8 ac);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+void iwl_reset_traffic_log(struct iwl_priv *priv);
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+void iwl_print_rx_config_cmd(struct iwl_priv *priv,
+ enum iwl_rxon_context_id ctxid);
+#else
+static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv,
+ enum iwl_rxon_context_id ctxid)
+{
+}
+#endif
+
+#define IWL_CMD(x) case x: return #x
+#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
+
+#define IWL_TRAFFIC_ENTRIES (256)
+#define IWL_TRAFFIC_ENTRY_SIZE (64)
+
+/*****************************************************
+* DRIVER STATUS FUNCTIONS
+******************************************************/
+#define STATUS_HCMD_ACTIVE 0 /* host command in progress */
+/* 1 is unused (used to be STATUS_HCMD_SYNC_ACTIVE) */
+#define STATUS_INT_ENABLED 2
+#define STATUS_RF_KILL_HW 3
+#define STATUS_CT_KILL 4
+#define STATUS_INIT 5
+#define STATUS_ALIVE 6
+#define STATUS_READY 7
+#define STATUS_TEMPERATURE 8
+#define STATUS_GEO_CONFIGURED 9
+#define STATUS_EXIT_PENDING 10
+#define STATUS_STATISTICS 12
+#define STATUS_SCANNING 13
+#define STATUS_SCAN_ABORTING 14
+#define STATUS_SCAN_HW 15
+#define STATUS_POWER_PMI 16
+#define STATUS_FW_ERROR 17
+#define STATUS_DEVICE_ENABLED 18
+#define STATUS_CHANNEL_SWITCH_PENDING 19
+#define STATUS_SCAN_COMPLETE 20
+
+static inline int iwl_is_ready(struct iwl_shared *shrd)
+{
+ /* The adapter is 'ready' if READY and GEO_CONFIGURED bits are
+ * set but EXIT_PENDING is not */
+ return test_bit(STATUS_READY, &shrd->status) &&
+ test_bit(STATUS_GEO_CONFIGURED, &shrd->status) &&
+ !test_bit(STATUS_EXIT_PENDING, &shrd->status);
+}
+
+static inline int iwl_is_alive(struct iwl_shared *shrd)
+{
+ return test_bit(STATUS_ALIVE, &shrd->status);
+}
+
+static inline int iwl_is_init(struct iwl_shared *shrd)
+{
+ return test_bit(STATUS_INIT, &shrd->status);
+}
+
+static inline int iwl_is_rfkill_hw(struct iwl_shared *shrd)
+{
+ return test_bit(STATUS_RF_KILL_HW, &shrd->status);
+}
+
+static inline int iwl_is_rfkill(struct iwl_shared *shrd)
+{
+ return iwl_is_rfkill_hw(shrd);
+}
+
+static inline int iwl_is_ctkill(struct iwl_shared *shrd)
+{
+ return test_bit(STATUS_CT_KILL, &shrd->status);
+}
+
+static inline int iwl_is_ready_rf(struct iwl_shared *shrd)
+{
+ if (iwl_is_rfkill(shrd))
+ return 0;
+
+ return iwl_is_ready(shrd);
+}
+
+#endif /* #__iwl_shared_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
deleted file mode 100644
index 7df2814fd4f8..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ /dev/null
@@ -1,820 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <net/mac80211.h>
-#include <linux/etherdevice.h>
-#include <linux/sched.h>
-#include <linux/lockdep.h>
-
-#include "iwl-dev.h"
-#include "iwl-core.h"
-#include "iwl-sta.h"
-
-/* priv->sta_lock must be held */
-static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
-{
-
- if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE))
- IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u addr %pM\n",
- sta_id, priv->stations[sta_id].sta.sta.addr);
-
- if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) {
- IWL_DEBUG_ASSOC(priv,
- "STA id %u addr %pM already present in uCode (according to driver)\n",
- sta_id, priv->stations[sta_id].sta.sta.addr);
- } else {
- priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE;
- IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n",
- sta_id, priv->stations[sta_id].sta.sta.addr);
- }
-}
-
-static int iwl_process_add_sta_resp(struct iwl_priv *priv,
- struct iwl_addsta_cmd *addsta,
- struct iwl_rx_packet *pkt,
- bool sync)
-{
- u8 sta_id = addsta->sta.sta_id;
- unsigned long flags;
- int ret = -EIO;
-
- if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
- IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n",
- pkt->hdr.flags);
- return ret;
- }
-
- IWL_DEBUG_INFO(priv, "Processing response for adding station %u\n",
- sta_id);
-
- spin_lock_irqsave(&priv->sta_lock, flags);
-
- switch (pkt->u.add_sta.status) {
- case ADD_STA_SUCCESS_MSK:
- IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n");
- iwl_sta_ucode_activate(priv, sta_id);
- ret = 0;
- break;
- case ADD_STA_NO_ROOM_IN_TABLE:
- IWL_ERR(priv, "Adding station %d failed, no room in table.\n",
- sta_id);
- break;
- case ADD_STA_NO_BLOCK_ACK_RESOURCE:
- IWL_ERR(priv, "Adding station %d failed, no block ack resource.\n",
- sta_id);
- break;
- case ADD_STA_MODIFY_NON_EXIST_STA:
- IWL_ERR(priv, "Attempting to modify non-existing station %d\n",
- sta_id);
- break;
- default:
- IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n",
- pkt->u.add_sta.status);
- break;
- }
-
- IWL_DEBUG_INFO(priv, "%s station id %u addr %pM\n",
- priv->stations[sta_id].sta.mode ==
- STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
- sta_id, priv->stations[sta_id].sta.sta.addr);
-
- /*
- * XXX: The MAC address in the command buffer is often changed from
- * the original sent to the device. That is, the MAC address
- * written to the command buffer often is not the same MAC address
- * read from the command buffer when the command returns. This
- * issue has not yet been resolved and this debugging is left to
- * observe the problem.
- */
- IWL_DEBUG_INFO(priv, "%s station according to cmd buffer %pM\n",
- priv->stations[sta_id].sta.mode ==
- STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
- addsta->sta.addr);
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- return ret;
-}
-
-static void iwl_add_sta_callback(struct iwl_priv *priv,
- struct iwl_device_cmd *cmd,
- struct iwl_rx_packet *pkt)
-{
- struct iwl_addsta_cmd *addsta =
- (struct iwl_addsta_cmd *)cmd->cmd.payload;
-
- iwl_process_add_sta_resp(priv, addsta, pkt, false);
-
-}
-
-int iwl_send_add_sta(struct iwl_priv *priv,
- struct iwl_addsta_cmd *sta, u8 flags)
-{
- struct iwl_rx_packet *pkt = NULL;
- int ret = 0;
- u8 data[sizeof(*sta)];
- struct iwl_host_cmd cmd = {
- .id = REPLY_ADD_STA,
- .flags = flags,
- .data = { data, },
- };
- u8 sta_id __maybe_unused = sta->sta.sta_id;
-
- IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n",
- sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : "");
-
- if (flags & CMD_ASYNC)
- cmd.callback = iwl_add_sta_callback;
- else {
- cmd.flags |= CMD_WANT_SKB;
- might_sleep();
- }
-
- cmd.len[0] = priv->cfg->ops->utils->build_addsta_hcmd(sta, data);
- ret = iwl_send_cmd(priv, &cmd);
-
- if (ret || (flags & CMD_ASYNC))
- return ret;
-
- if (ret == 0) {
- pkt = (struct iwl_rx_packet *)cmd.reply_page;
- ret = iwl_process_add_sta_resp(priv, sta, pkt, true);
- }
- iwl_free_pages(priv, cmd.reply_page);
-
- return ret;
-}
-
-static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
- struct ieee80211_sta *sta,
- struct iwl_rxon_context *ctx)
-{
- struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap;
- __le32 sta_flags;
- u8 mimo_ps_mode;
-
- if (!sta || !sta_ht_inf->ht_supported)
- goto done;
-
- mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2;
- IWL_DEBUG_ASSOC(priv, "spatial multiplexing power save mode: %s\n",
- (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ?
- "static" :
- (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ?
- "dynamic" : "disabled");
-
- sta_flags = priv->stations[index].sta.station_flags;
-
- sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK);
-
- switch (mimo_ps_mode) {
- case WLAN_HT_CAP_SM_PS_STATIC:
- sta_flags |= STA_FLG_MIMO_DIS_MSK;
- break;
- case WLAN_HT_CAP_SM_PS_DYNAMIC:
- sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
- break;
- case WLAN_HT_CAP_SM_PS_DISABLED:
- break;
- default:
- IWL_WARN(priv, "Invalid MIMO PS mode %d\n", mimo_ps_mode);
- break;
- }
-
- sta_flags |= cpu_to_le32(
- (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);
-
- sta_flags |= cpu_to_le32(
- (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);
-
- if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap))
- sta_flags |= STA_FLG_HT40_EN_MSK;
- else
- sta_flags &= ~STA_FLG_HT40_EN_MSK;
-
- priv->stations[index].sta.station_flags = sta_flags;
- done:
- return;
-}
-
-/**
- * iwl_prep_station - Prepare station information for addition
- *
- * should be called with sta_lock held
- */
-u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- const u8 *addr, bool is_ap, struct ieee80211_sta *sta)
-{
- struct iwl_station_entry *station;
- int i;
- u8 sta_id = IWL_INVALID_STATION;
-
- if (is_ap)
- sta_id = ctx->ap_sta_id;
- else if (is_broadcast_ether_addr(addr))
- sta_id = ctx->bcast_sta_id;
- else
- for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++) {
- if (!compare_ether_addr(priv->stations[i].sta.sta.addr,
- addr)) {
- sta_id = i;
- break;
- }
-
- if (!priv->stations[i].used &&
- sta_id == IWL_INVALID_STATION)
- sta_id = i;
- }
-
- /*
- * These two conditions have the same outcome, but keep them
- * separate
- */
- if (unlikely(sta_id == IWL_INVALID_STATION))
- return sta_id;
-
- /*
- * uCode is not able to deal with multiple requests to add a
- * station. Keep track if one is in progress so that we do not send
- * another.
- */
- if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
- IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n",
- sta_id);
- return sta_id;
- }
-
- if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
- (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) &&
- !compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) {
- IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n",
- sta_id, addr);
- return sta_id;
- }
-
- station = &priv->stations[sta_id];
- station->used = IWL_STA_DRIVER_ACTIVE;
- IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n",
- sta_id, addr);
- priv->num_stations++;
-
- /* Set up the REPLY_ADD_STA command to send to device */
- memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd));
- memcpy(station->sta.sta.addr, addr, ETH_ALEN);
- station->sta.mode = 0;
- station->sta.sta.sta_id = sta_id;
- station->sta.station_flags = ctx->station_flags;
- station->ctxid = ctx->ctxid;
-
- if (sta) {
- struct iwl_station_priv_common *sta_priv;
-
- sta_priv = (void *)sta->drv_priv;
- sta_priv->ctx = ctx;
- }
-
- /*
- * OK to call unconditionally, since local stations (IBSS BSSID
- * STA and broadcast STA) pass in a NULL sta, and mac80211
- * doesn't allow HT IBSS.
- */
- iwl_set_ht_add_station(priv, sta_id, sta, ctx);
-
- return sta_id;
-
-}
-
-#define STA_WAIT_TIMEOUT (HZ/2)
-
-/**
- * iwl_add_station_common -
- */
-int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- const u8 *addr, bool is_ap,
- struct ieee80211_sta *sta, u8 *sta_id_r)
-{
- unsigned long flags_spin;
- int ret = 0;
- u8 sta_id;
- struct iwl_addsta_cmd sta_cmd;
-
- *sta_id_r = 0;
- spin_lock_irqsave(&priv->sta_lock, flags_spin);
- sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta);
- if (sta_id == IWL_INVALID_STATION) {
- IWL_ERR(priv, "Unable to prepare station %pM for addition\n",
- addr);
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
- return -EINVAL;
- }
-
- /*
- * uCode is not able to deal with multiple requests to add a
- * station. Keep track if one is in progress so that we do not send
- * another.
- */
- if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
- IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n",
- sta_id);
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
- return -EEXIST;
- }
-
- if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
- (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
- IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n",
- sta_id, addr);
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
- return -EEXIST;
- }
-
- priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS;
- memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
-
- /* Add station to device's station table */
- ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
- if (ret) {
- spin_lock_irqsave(&priv->sta_lock, flags_spin);
- IWL_ERR(priv, "Adding station %pM failed.\n",
- priv->stations[sta_id].sta.sta.addr);
- priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
- priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
- }
- *sta_id_r = sta_id;
- return ret;
-}
-
-/**
- * iwl_sta_ucode_deactivate - deactivate ucode status for a station
- *
- * priv->sta_lock must be held
- */
-static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id)
-{
- /* Ucode must be active and driver must be non active */
- if ((priv->stations[sta_id].used &
- (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) != IWL_STA_UCODE_ACTIVE)
- IWL_ERR(priv, "removed non active STA %u\n", sta_id);
-
- priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE;
-
- memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry));
- IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id);
-}
-
-static int iwl_send_remove_station(struct iwl_priv *priv,
- const u8 *addr, int sta_id,
- bool temporary)
-{
- struct iwl_rx_packet *pkt;
- int ret;
-
- unsigned long flags_spin;
- struct iwl_rem_sta_cmd rm_sta_cmd;
-
- struct iwl_host_cmd cmd = {
- .id = REPLY_REMOVE_STA,
- .len = { sizeof(struct iwl_rem_sta_cmd), },
- .flags = CMD_SYNC,
- .data = { &rm_sta_cmd, },
- };
-
- memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
- rm_sta_cmd.num_sta = 1;
- memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN);
-
- cmd.flags |= CMD_WANT_SKB;
-
- ret = iwl_send_cmd(priv, &cmd);
-
- if (ret)
- return ret;
-
- pkt = (struct iwl_rx_packet *)cmd.reply_page;
- if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
- IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n",
- pkt->hdr.flags);
- ret = -EIO;
- }
-
- if (!ret) {
- switch (pkt->u.rem_sta.status) {
- case REM_STA_SUCCESS_MSK:
- if (!temporary) {
- spin_lock_irqsave(&priv->sta_lock, flags_spin);
- iwl_sta_ucode_deactivate(priv, sta_id);
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
- }
- IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
- break;
- default:
- ret = -EIO;
- IWL_ERR(priv, "REPLY_REMOVE_STA failed\n");
- break;
- }
- }
- iwl_free_pages(priv, cmd.reply_page);
-
- return ret;
-}
-
-/**
- * iwl_remove_station - Remove driver's knowledge of station.
- */
-int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
- const u8 *addr)
-{
- unsigned long flags;
-
- if (!iwl_is_ready(priv)) {
- IWL_DEBUG_INFO(priv,
- "Unable to remove station %pM, device not ready.\n",
- addr);
- /*
- * It is typical for stations to be removed when we are
- * going down. Return success since device will be down
- * soon anyway
- */
- return 0;
- }
-
- IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n",
- sta_id, addr);
-
- if (WARN_ON(sta_id == IWL_INVALID_STATION))
- return -EINVAL;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
-
- if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
- IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n",
- addr);
- goto out_err;
- }
-
- if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
- IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n",
- addr);
- goto out_err;
- }
-
- if (priv->stations[sta_id].used & IWL_STA_LOCAL) {
- kfree(priv->stations[sta_id].lq);
- priv->stations[sta_id].lq = NULL;
- }
-
- priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
-
- priv->num_stations--;
-
- if (WARN_ON(priv->num_stations < 0))
- priv->num_stations = 0;
-
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- return iwl_send_remove_station(priv, addr, sta_id, false);
-out_err:
- spin_unlock_irqrestore(&priv->sta_lock, flags);
- return -EINVAL;
-}
-
-/**
- * iwl_clear_ucode_stations - clear ucode station table bits
- *
- * This function clears all the bits in the driver indicating
- * which stations are active in the ucode. Call when something
- * other than explicit station management would cause this in
- * the ucode, e.g. unassociated RXON.
- */
-void iwl_clear_ucode_stations(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- int i;
- unsigned long flags_spin;
- bool cleared = false;
-
- IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n");
-
- spin_lock_irqsave(&priv->sta_lock, flags_spin);
- for (i = 0; i < priv->hw_params.max_stations; i++) {
- if (ctx && ctx->ctxid != priv->stations[i].ctxid)
- continue;
-
- if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) {
- IWL_DEBUG_INFO(priv, "Clearing ucode active for station %d\n", i);
- priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
- cleared = true;
- }
- }
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
-
- if (!cleared)
- IWL_DEBUG_INFO(priv, "No active stations found to be cleared\n");
-}
-
-/**
- * iwl_restore_stations() - Restore driver known stations to device
- *
- * All stations considered active by driver, but not present in ucode, is
- * restored.
- *
- * Function sleeps.
- */
-void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
-{
- struct iwl_addsta_cmd sta_cmd;
- struct iwl_link_quality_cmd lq;
- unsigned long flags_spin;
- int i;
- bool found = false;
- int ret;
- bool send_lq;
-
- if (!iwl_is_ready(priv)) {
- IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n");
- return;
- }
-
- IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n");
- spin_lock_irqsave(&priv->sta_lock, flags_spin);
- for (i = 0; i < priv->hw_params.max_stations; i++) {
- if (ctx->ctxid != priv->stations[i].ctxid)
- continue;
- if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) &&
- !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) {
- IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n",
- priv->stations[i].sta.sta.addr);
- priv->stations[i].sta.mode = 0;
- priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS;
- found = true;
- }
- }
-
- for (i = 0; i < priv->hw_params.max_stations; i++) {
- if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) {
- memcpy(&sta_cmd, &priv->stations[i].sta,
- sizeof(struct iwl_addsta_cmd));
- send_lq = false;
- if (priv->stations[i].lq) {
- memcpy(&lq, priv->stations[i].lq,
- sizeof(struct iwl_link_quality_cmd));
- send_lq = true;
- }
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
- ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
- if (ret) {
- spin_lock_irqsave(&priv->sta_lock, flags_spin);
- IWL_ERR(priv, "Adding station %pM failed.\n",
- priv->stations[i].sta.sta.addr);
- priv->stations[i].used &= ~IWL_STA_DRIVER_ACTIVE;
- priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
- }
- /*
- * Rate scaling has already been initialized, send
- * current LQ command
- */
- if (send_lq)
- iwl_send_lq_cmd(priv, ctx, &lq, CMD_SYNC, true);
- spin_lock_irqsave(&priv->sta_lock, flags_spin);
- priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
- }
- }
-
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
- if (!found)
- IWL_DEBUG_INFO(priv, "Restoring all known stations .... no stations to be restored.\n");
- else
- IWL_DEBUG_INFO(priv, "Restoring all known stations .... complete.\n");
-}
-
-void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
-{
- unsigned long flags;
- int sta_id = ctx->ap_sta_id;
- int ret;
- struct iwl_addsta_cmd sta_cmd;
- struct iwl_link_quality_cmd lq;
- bool active;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
- if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
- spin_unlock_irqrestore(&priv->sta_lock, flags);
- return;
- }
-
- memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd));
- sta_cmd.mode = 0;
- memcpy(&lq, priv->stations[sta_id].lq, sizeof(lq));
-
- active = priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE;
- priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- if (active) {
- ret = iwl_send_remove_station(
- priv, priv->stations[sta_id].sta.sta.addr,
- sta_id, true);
- if (ret)
- IWL_ERR(priv, "failed to remove STA %pM (%d)\n",
- priv->stations[sta_id].sta.sta.addr, ret);
- }
- spin_lock_irqsave(&priv->sta_lock, flags);
- priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
- if (ret)
- IWL_ERR(priv, "failed to re-add STA %pM (%d)\n",
- priv->stations[sta_id].sta.sta.addr, ret);
- iwl_send_lq_cmd(priv, ctx, &lq, CMD_SYNC, true);
-}
-
-int iwl_get_free_ucode_key_index(struct iwl_priv *priv)
-{
- int i;
-
- for (i = 0; i < priv->sta_key_max_num; i++)
- if (!test_and_set_bit(i, &priv->ucode_key_table))
- return i;
-
- return WEP_INVALID_OFFSET;
-}
-
-void iwl_dealloc_bcast_stations(struct iwl_priv *priv)
-{
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
- for (i = 0; i < priv->hw_params.max_stations; i++) {
- if (!(priv->stations[i].used & IWL_STA_BCAST))
- continue;
-
- priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
- priv->num_stations--;
- if (WARN_ON(priv->num_stations < 0))
- priv->num_stations = 0;
- kfree(priv->stations[i].lq);
- priv->stations[i].lq = NULL;
- }
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-static void iwl_dump_lq_cmd(struct iwl_priv *priv,
- struct iwl_link_quality_cmd *lq)
-{
- int i;
- IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id);
- IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n",
- lq->general_params.single_stream_ant_msk,
- lq->general_params.dual_stream_ant_msk);
-
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
- IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n",
- i, lq->rs_table[i].rate_n_flags);
-}
-#else
-static inline void iwl_dump_lq_cmd(struct iwl_priv *priv,
- struct iwl_link_quality_cmd *lq)
-{
-}
-#endif
-
-/**
- * is_lq_table_valid() - Test one aspect of LQ cmd for validity
- *
- * It sometimes happens when a HT rate has been in use and we
- * loose connectivity with AP then mac80211 will first tell us that the
- * current channel is not HT anymore before removing the station. In such a
- * scenario the RXON flags will be updated to indicate we are not
- * communicating HT anymore, but the LQ command may still contain HT rates.
- * Test for this to prevent driver from sending LQ command between the time
- * RXON flags are updated and when LQ command is updated.
- */
-static bool is_lq_table_valid(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct iwl_link_quality_cmd *lq)
-{
- int i;
-
- if (ctx->ht.enabled)
- return true;
-
- IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n",
- ctx->active.channel);
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
- if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) {
- IWL_DEBUG_INFO(priv,
- "index %d of LQ expects HT channel\n",
- i);
- return false;
- }
- }
- return true;
-}
-
-/**
- * iwl_send_lq_cmd() - Send link quality command
- * @init: This command is sent as part of station initialization right
- * after station has been added.
- *
- * The link quality command is sent as the last step of station creation.
- * This is the special case in which init is set and we call a callback in
- * this case to clear the state indicating that station creation is in
- * progress.
- */
-int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct iwl_link_quality_cmd *lq, u8 flags, bool init)
-{
- int ret = 0;
- unsigned long flags_spin;
-
- struct iwl_host_cmd cmd = {
- .id = REPLY_TX_LINK_QUALITY_CMD,
- .len = { sizeof(struct iwl_link_quality_cmd), },
- .flags = flags,
- .data = { lq, },
- };
-
- if (WARN_ON(lq->sta_id == IWL_INVALID_STATION))
- return -EINVAL;
-
-
- spin_lock_irqsave(&priv->sta_lock, flags_spin);
- if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
-
- iwl_dump_lq_cmd(priv, lq);
- if (WARN_ON(init && (cmd.flags & CMD_ASYNC)))
- return -EINVAL;
-
- if (is_lq_table_valid(priv, ctx, lq))
- ret = iwl_send_cmd(priv, &cmd);
- else
- ret = -EINVAL;
-
- if (cmd.flags & CMD_ASYNC)
- return ret;
-
- if (init) {
- IWL_DEBUG_INFO(priv, "init LQ command complete, clearing sta addition status for sta %d\n",
- lq->sta_id);
- spin_lock_irqsave(&priv->sta_lock, flags_spin);
- priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
- spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
- }
- return ret;
-}
-
-int iwl_mac_sta_remove(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct iwl_priv *priv = hw->priv;
- struct iwl_station_priv_common *sta_common = (void *)sta->drv_priv;
- int ret;
-
- IWL_DEBUG_INFO(priv, "received request to remove station %pM\n",
- sta->addr);
- mutex_lock(&priv->mutex);
- IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n",
- sta->addr);
- ret = iwl_remove_station(priv, sta_common->sta_id, sta->addr);
- if (ret)
- IWL_ERR(priv, "Error removing station %pM\n",
- sta->addr);
- mutex_unlock(&priv->mutex);
- return ret;
-}
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.h b/drivers/net/wireless/iwlwifi/iwl-sta.h
deleted file mode 100644
index ff64027ff4cb..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-sta.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-#ifndef __iwl_sta_h__
-#define __iwl_sta_h__
-
-#include "iwl-dev.h"
-
-#define HW_KEY_DYNAMIC 0
-#define HW_KEY_DEFAULT 1
-
-#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
-#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */
-#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of
- being activated */
-#define IWL_STA_LOCAL BIT(3) /* station state not directed by mac80211;
- (this is for the IBSS BSSID stations) */
-#define IWL_STA_BCAST BIT(4) /* this station is the special bcast station */
-
-
-void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
-void iwl_clear_ucode_stations(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx);
-void iwl_dealloc_bcast_stations(struct iwl_priv *priv);
-int iwl_get_free_ucode_key_index(struct iwl_priv *priv);
-int iwl_send_add_sta(struct iwl_priv *priv,
- struct iwl_addsta_cmd *sta, u8 flags);
-int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- const u8 *addr, bool is_ap,
- struct ieee80211_sta *sta, u8 *sta_id_r);
-int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
- const u8 *addr);
-int iwl_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
-
-u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- const u8 *addr, bool is_ap, struct ieee80211_sta *sta);
-
-int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct iwl_link_quality_cmd *lq, u8 flags, bool init);
-void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
-
-/**
- * iwl_clear_driver_stations - clear knowledge of all stations from driver
- * @priv: iwl priv struct
- *
- * This is called during iwl_down() to make sure that in the case
- * we're coming there from a hardware restart mac80211 will be
- * able to reconfigure stations -- if we're getting there in the
- * normal down flow then the stations will already be cleared.
- */
-static inline void iwl_clear_driver_stations(struct iwl_priv *priv)
-{
- unsigned long flags;
- struct iwl_rxon_context *ctx;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
- memset(priv->stations, 0, sizeof(priv->stations));
- priv->num_stations = 0;
-
- priv->ucode_key_table = 0;
-
- for_each_context(priv, ctx) {
- /*
- * Remove all key information that is not stored as part
- * of station information since mac80211 may not have had
- * a chance to remove all the keys. When device is
- * reconfigured by mac80211 after an error all keys will
- * be reconfigured.
- */
- memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys));
- ctx->key_mapping_keys = 0;
- }
-
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-}
-
-static inline int iwl_sta_id(struct ieee80211_sta *sta)
-{
- if (WARN_ON(!sta))
- return IWL_INVALID_STATION;
-
- return ((struct iwl_station_priv_common *)sta->drv_priv)->sta_id;
-}
-
-/**
- * iwl_sta_id_or_broadcast - return sta_id or broadcast sta
- * @priv: iwl priv
- * @context: the current context
- * @sta: mac80211 station
- *
- * In certain circumstances mac80211 passes a station pointer
- * that may be %NULL, for example during TX or key setup. In
- * that case, we need to use the broadcast station, so this
- * inline wraps that pattern.
- */
-static inline int iwl_sta_id_or_broadcast(struct iwl_priv *priv,
- struct iwl_rxon_context *context,
- struct ieee80211_sta *sta)
-{
- int sta_id;
-
- if (!sta)
- return context->bcast_sta_id;
-
- sta_id = iwl_sta_id(sta);
-
- /*
- * mac80211 should not be passing a partially
- * initialised station!
- */
- WARN_ON(sta_id == IWL_INVALID_STATION);
-
- return sta_id;
-}
-#endif /* __iwl_sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-sv-open.c b/drivers/net/wireless/iwlwifi/iwl-sv-open.c
index 69b7e6bf2d6f..5e50d88f302b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sv-open.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sv-open.c
@@ -63,21 +63,20 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/dma-mapping.h>
#include <net/net_namespace.h>
#include <linux/netdevice.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <net/netlink.h>
-
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-debug.h"
-#include "iwl-fh.h"
#include "iwl-io.h"
#include "iwl-agn.h"
#include "iwl-testmode.h"
-
+#include "iwl-trans.h"
/* The TLVs used in the gnl message policy between the kernel module and
* user space application. iwl_testmode_gnl_msg_policy is to be carried
@@ -101,9 +100,12 @@ struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = {
[IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, },
[IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_TRACE_DATA] = { .type = NLA_UNSPEC, },
+ [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, },
+ [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, },
[IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, },
+
+ [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, },
};
/*
@@ -179,19 +181,19 @@ void iwl_testmode_init(struct iwl_priv *priv)
static void iwl_trace_cleanup(struct iwl_priv *priv)
{
- struct device *dev = &priv->pci_dev->dev;
-
if (priv->testmode_trace.trace_enabled) {
if (priv->testmode_trace.cpu_addr &&
priv->testmode_trace.dma_addr)
- dma_free_coherent(dev,
- TRACE_TOTAL_SIZE,
+ dma_free_coherent(bus(priv)->dev,
+ priv->testmode_trace.total_size,
priv->testmode_trace.cpu_addr,
priv->testmode_trace.dma_addr);
priv->testmode_trace.trace_enabled = false;
priv->testmode_trace.cpu_addr = NULL;
priv->testmode_trace.trace_addr = NULL;
priv->testmode_trace.dma_addr = 0;
+ priv->testmode_trace.buff_size = 0;
+ priv->testmode_trace.total_size = 0;
}
}
@@ -229,6 +231,7 @@ static int iwl_testmode_ucode(struct ieee80211_hw *hw, struct nlattr **tb)
return -ENOMSG;
}
+ cmd.flags = CMD_ON_DEMAND;
cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]);
cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
@@ -236,7 +239,7 @@ static int iwl_testmode_ucode(struct ieee80211_hw *hw, struct nlattr **tb)
IWL_INFO(priv, "testmode ucode command ID 0x%x, flags 0x%x,"
" len %d\n", cmd.id, cmd.flags, cmd.len[0]);
/* ok, let's submit the command to ucode */
- return iwl_send_cmd(priv, &cmd);
+ return iwl_trans_send_cmd(trans(priv), &cmd);
}
@@ -274,7 +277,7 @@ static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb)
switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
case IWL_TM_CMD_APP2DEV_REG_READ32:
- val32 = iwl_read32(priv, ofs);
+ val32 = iwl_read32(bus(priv), ofs);
IWL_INFO(priv, "32bit value to read 0x%x\n", val32);
skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
@@ -296,7 +299,7 @@ static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb)
} else {
val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]);
IWL_INFO(priv, "32bit value to write 0x%x\n", val32);
- iwl_write32(priv, ofs, val32);
+ iwl_write32(bus(priv), ofs, val32);
}
break;
case IWL_TM_CMD_APP2DEV_REG_WRITE8:
@@ -306,7 +309,7 @@ static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb)
} else {
val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]);
IWL_INFO(priv, "8bit value to write 0x%x\n", val8);
- iwl_write8(priv, ofs, val8);
+ iwl_write8(bus(priv), ofs, val8);
}
break;
default:
@@ -394,7 +397,7 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW:
status = iwlagn_load_ucode_wait_alive(priv, &priv->ucode_init,
- UCODE_SUBTYPE_INIT, -1);
+ IWL_UCODE_INIT);
if (status)
IWL_DEBUG_INFO(priv,
"Error loading init ucode: %d\n", status);
@@ -402,14 +405,13 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB:
iwl_testmode_cfg_init_calib(priv);
- iwlagn_stop_device(priv);
+ iwl_trans_stop_device(trans(priv));
break;
case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW:
status = iwlagn_load_ucode_wait_alive(priv,
&priv->ucode_rt,
- UCODE_SUBTYPE_REGULAR,
- UCODE_SUBTYPE_REGULAR_NEW);
+ IWL_UCODE_REGULAR);
if (status) {
IWL_DEBUG_INFO(priv,
"Error loading runtime ucode: %d\n", status);
@@ -450,7 +452,7 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
"Error finding fixrate setting\n");
return -ENOMSG;
}
- priv->dbg_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]);
+ priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]);
break;
default:
@@ -482,16 +484,29 @@ static int iwl_testmode_trace(struct ieee80211_hw *hw, struct nlattr **tb)
struct iwl_priv *priv = hw->priv;
struct sk_buff *skb;
int status = 0;
- struct device *dev = &priv->pci_dev->dev;
+ struct device *dev = bus(priv)->dev;
switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
if (priv->testmode_trace.trace_enabled)
return -EBUSY;
+ if (!tb[IWL_TM_ATTR_TRACE_SIZE])
+ priv->testmode_trace.buff_size = TRACE_BUFF_SIZE_DEF;
+ else
+ priv->testmode_trace.buff_size =
+ nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]);
+ if (!priv->testmode_trace.buff_size)
+ return -EINVAL;
+ if (priv->testmode_trace.buff_size < TRACE_BUFF_SIZE_MIN ||
+ priv->testmode_trace.buff_size > TRACE_BUFF_SIZE_MAX)
+ return -EINVAL;
+
+ priv->testmode_trace.total_size =
+ priv->testmode_trace.buff_size + TRACE_BUFF_PADD;
priv->testmode_trace.cpu_addr =
dma_alloc_coherent(dev,
- TRACE_TOTAL_SIZE,
+ priv->testmode_trace.total_size,
&priv->testmode_trace.dma_addr,
GFP_KERNEL);
if (!priv->testmode_trace.cpu_addr)
@@ -500,7 +515,7 @@ static int iwl_testmode_trace(struct ieee80211_hw *hw, struct nlattr **tb)
priv->testmode_trace.trace_addr = (u8 *)PTR_ALIGN(
priv->testmode_trace.cpu_addr, 0x100);
memset(priv->testmode_trace.trace_addr, 0x03B,
- TRACE_BUFF_SIZE);
+ priv->testmode_trace.buff_size);
skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
sizeof(priv->testmode_trace.dma_addr) + 20);
if (!skb) {
@@ -518,34 +533,14 @@ static int iwl_testmode_trace(struct ieee80211_hw *hw, struct nlattr **tb)
"Error sending msg : %d\n",
status);
}
+ priv->testmode_trace.num_chunks =
+ DIV_ROUND_UP(priv->testmode_trace.buff_size,
+ TRACE_CHUNK_SIZE);
break;
case IWL_TM_CMD_APP2DEV_END_TRACE:
iwl_trace_cleanup(priv);
break;
-
- case IWL_TM_CMD_APP2DEV_READ_TRACE:
- if (priv->testmode_trace.trace_enabled &&
- priv->testmode_trace.trace_addr) {
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
- 20 + TRACE_BUFF_SIZE);
- if (skb == NULL) {
- IWL_DEBUG_INFO(priv,
- "Error allocating memory\n");
- return -ENOMEM;
- }
- NLA_PUT(skb, IWL_TM_ATTR_TRACE_DATA,
- TRACE_BUFF_SIZE,
- priv->testmode_trace.trace_addr);
- status = cfg80211_testmode_reply(skb);
- if (status < 0) {
- IWL_DEBUG_INFO(priv,
- "Error sending msg : %d\n", status);
- }
- } else
- return -EFAULT;
- break;
-
default:
IWL_DEBUG_INFO(priv, "Unknown testmode mem command ID\n");
return -ENOSYS;
@@ -560,6 +555,73 @@ nla_put_failure:
return -EMSGSIZE;
}
+static int iwl_testmode_trace_dump(struct ieee80211_hw *hw, struct nlattr **tb,
+ struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct iwl_priv *priv = hw->priv;
+ int idx, length;
+
+ if (priv->testmode_trace.trace_enabled &&
+ priv->testmode_trace.trace_addr) {
+ idx = cb->args[4];
+ if (idx >= priv->testmode_trace.num_chunks)
+ return -ENOENT;
+ length = TRACE_CHUNK_SIZE;
+ if (((idx + 1) == priv->testmode_trace.num_chunks) &&
+ (priv->testmode_trace.buff_size % TRACE_CHUNK_SIZE))
+ length = priv->testmode_trace.buff_size %
+ TRACE_CHUNK_SIZE;
+
+ NLA_PUT(skb, IWL_TM_ATTR_TRACE_DUMP, length,
+ priv->testmode_trace.trace_addr +
+ (TRACE_CHUNK_SIZE * idx));
+ idx++;
+ cb->args[4] = idx;
+ return 0;
+ } else
+ return -EFAULT;
+
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+/*
+ * This function handles the user application switch ucode ownership.
+ *
+ * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_OWNER and
+ * decide who the current owner of the uCode
+ *
+ * If the current owner is OWNERSHIP_TM, then the only host command
+ * can deliver to uCode is from testmode, all the other host commands
+ * will dropped.
+ *
+ * default driver is the owner of uCode in normal operational mode
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: gnl message fields from the user space
+ */
+static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+ struct iwl_priv *priv = hw->priv;
+ u8 owner;
+
+ if (!tb[IWL_TM_ATTR_UCODE_OWNER]) {
+ IWL_DEBUG_INFO(priv, "Error finding ucode owner\n");
+ return -ENOMSG;
+ }
+
+ owner = nla_get_u8(tb[IWL_TM_ATTR_UCODE_OWNER]);
+ if ((owner == IWL_OWNERSHIP_DRIVER) || (owner == IWL_OWNERSHIP_TM))
+ priv->shrd->ucode_owner = owner;
+ else {
+ IWL_DEBUG_INFO(priv, "Invalid owner\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
/* The testmode gnl message handler that takes the gnl message from the
* user space and parses it per the policy iwl_testmode_gnl_msg_policy, then
* invoke the corresponding handlers.
@@ -579,9 +641,9 @@ nla_put_failure:
* @data: pointer to user space message
* @len: length in byte of @data
*/
-int iwl_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
+int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
{
- struct nlattr *tb[IWL_TM_ATTR_MAX - 1];
+ struct nlattr *tb[IWL_TM_ATTR_MAX];
struct iwl_priv *priv = hw->priv;
int result;
@@ -599,7 +661,7 @@ int iwl_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
return -ENOMSG;
}
/* in case multiple accesses to the device happens */
- mutex_lock(&priv->mutex);
+ mutex_lock(&priv->shrd->mutex);
switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
case IWL_TM_CMD_APP2DEV_UCODE:
@@ -629,12 +691,64 @@ int iwl_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
result = iwl_testmode_trace(hw, tb);
break;
+ case IWL_TM_CMD_APP2DEV_OWNERSHIP:
+ IWL_DEBUG_INFO(priv, "testmode change uCode ownership\n");
+ result = iwl_testmode_ownership(hw, tb);
+ break;
+
default:
IWL_DEBUG_INFO(priv, "Unknown testmode command\n");
result = -ENOSYS;
break;
}
- mutex_unlock(&priv->mutex);
+ mutex_unlock(&priv->shrd->mutex);
+ return result;
+}
+
+int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct netlink_callback *cb,
+ void *data, int len)
+{
+ struct nlattr *tb[IWL_TM_ATTR_MAX];
+ struct iwl_priv *priv = hw->priv;
+ int result;
+ u32 cmd;
+
+ if (cb->args[3]) {
+ /* offset by 1 since commands start at 0 */
+ cmd = cb->args[3] - 1;
+ } else {
+ result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len,
+ iwl_testmode_gnl_msg_policy);
+ if (result) {
+ IWL_DEBUG_INFO(priv,
+ "Error parsing the gnl message : %d\n", result);
+ return result;
+ }
+
+ /* IWL_TM_ATTR_COMMAND is absolutely mandatory */
+ if (!tb[IWL_TM_ATTR_COMMAND]) {
+ IWL_DEBUG_INFO(priv,
+ "Error finding testmode command type\n");
+ return -ENOMSG;
+ }
+ cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
+ cb->args[3] = cmd + 1;
+ }
+
+ /* in case multiple accesses to the device happens */
+ mutex_lock(&priv->shrd->mutex);
+ switch (cmd) {
+ case IWL_TM_CMD_APP2DEV_READ_TRACE:
+ IWL_DEBUG_INFO(priv, "uCode trace cmd to driver\n");
+ result = iwl_testmode_trace_dump(hw, tb, skb, cb);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&priv->shrd->mutex);
return result;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.h b/drivers/net/wireless/iwlwifi/iwl-testmode.h
index a88085e9b361..b980bda4b0f8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-testmode.h
+++ b/drivers/net/wireless/iwlwifi/iwl-testmode.h
@@ -66,120 +66,161 @@
#include <linux/types.h>
-/* Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and
+/*
+ * Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and
* from and kernel space to user space(IWL_TM_CMD_ID_DEV2APP_XX).
- * The command ID is carried with IWL_TM_ATTR_COMMAND. There are three types of
- * of command from user space and two types of command from kernel space.
- * See below.
+ * The command ID is carried with IWL_TM_ATTR_COMMAND.
+ *
+ * @IWL_TM_CMD_APP2DEV_UCODE:
+ * commands from user application to the uCode,
+ * the actual uCode host command ID is carried with
+ * IWL_TM_ATTR_UCODE_CMD_ID
+ *
+ * @IWL_TM_CMD_APP2DEV_REG_READ32:
+ * @IWL_TM_CMD_APP2DEV_REG_WRITE32:
+ * @IWL_TM_CMD_APP2DEV_REG_WRITE8:
+ * commands from user applicaiton to access register
+ *
+ * @IWL_TM_CMD_APP2DEV_GET_DEVICENAME: retrieve device name
+ * @IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: load initial uCode image
+ * @IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: perform calibration
+ * @IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: load runtime uCode image
+ * @IWL_TM_CMD_APP2DEV_GET_EEPROM: request EEPROM data
+ * @IWL_TM_CMD_APP2DEV_FIXRATE_REQ: set fix MCS
+ * commands fom user space for pure driver level operations
+ *
+ * @IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
+ * @IWL_TM_CMD_APP2DEV_END_TRACE:
+ * @IWL_TM_CMD_APP2DEV_READ_TRACE:
+ * commands fom user space for uCode trace operations
+ *
+ * @IWL_TM_CMD_DEV2APP_SYNC_RSP:
+ * commands from kernel space to carry the synchronous response
+ * to user application
+ * @IWL_TM_CMD_DEV2APP_UCODE_RX_PKT:
+ * commands from kernel space to multicast the spontaneous messages
+ * to user application
+ * @IWL_TM_CMD_DEV2APP_EEPROM_RSP:
+ * commands from kernel space to carry the eeprom response
+ * to user application
+ * @IWL_TM_CMD_APP2DEV_OWNERSHIP:
+ * commands from user application to own change the ownership of the uCode
+ * if application has the ownership, the only host command from
+ * testmode will deliver to uCode. Default owner is driver
*/
enum iwl_tm_cmd_t {
- /* commands from user application to the uCode,
- * the actual uCode host command ID is carried with
- * IWL_TM_ATTR_UCODE_CMD_ID */
- IWL_TM_CMD_APP2DEV_UCODE = 1,
-
- /* commands from user applicaiton to access register */
- IWL_TM_CMD_APP2DEV_REG_READ32,
- IWL_TM_CMD_APP2DEV_REG_WRITE32,
- IWL_TM_CMD_APP2DEV_REG_WRITE8,
-
- /* commands fom user space for pure driver level operations */
- IWL_TM_CMD_APP2DEV_GET_DEVICENAME,
- IWL_TM_CMD_APP2DEV_LOAD_INIT_FW,
- IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB,
- IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW,
- IWL_TM_CMD_APP2DEV_GET_EEPROM,
- IWL_TM_CMD_APP2DEV_FIXRATE_REQ,
- /* if there is other new command for the driver layer operation,
- * append them here */
-
- /* commands fom user space for uCode trace operations */
- IWL_TM_CMD_APP2DEV_BEGIN_TRACE,
- IWL_TM_CMD_APP2DEV_END_TRACE,
- IWL_TM_CMD_APP2DEV_READ_TRACE,
-
- /* commands from kernel space to carry the synchronous response
- * to user application */
- IWL_TM_CMD_DEV2APP_SYNC_RSP,
-
- /* commands from kernel space to multicast the spontaneous messages
- * to user application */
- IWL_TM_CMD_DEV2APP_UCODE_RX_PKT,
-
- /* commands from kernel space to carry the eeprom response
- * to user application */
- IWL_TM_CMD_DEV2APP_EEPROM_RSP,
-
- IWL_TM_CMD_MAX,
+ IWL_TM_CMD_APP2DEV_UCODE = 1,
+ IWL_TM_CMD_APP2DEV_REG_READ32 = 2,
+ IWL_TM_CMD_APP2DEV_REG_WRITE32 = 3,
+ IWL_TM_CMD_APP2DEV_REG_WRITE8 = 4,
+ IWL_TM_CMD_APP2DEV_GET_DEVICENAME = 5,
+ IWL_TM_CMD_APP2DEV_LOAD_INIT_FW = 6,
+ IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB = 7,
+ IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW = 8,
+ IWL_TM_CMD_APP2DEV_GET_EEPROM = 9,
+ IWL_TM_CMD_APP2DEV_FIXRATE_REQ = 10,
+ IWL_TM_CMD_APP2DEV_BEGIN_TRACE = 11,
+ IWL_TM_CMD_APP2DEV_END_TRACE = 12,
+ IWL_TM_CMD_APP2DEV_READ_TRACE = 13,
+ IWL_TM_CMD_DEV2APP_SYNC_RSP = 14,
+ IWL_TM_CMD_DEV2APP_UCODE_RX_PKT = 15,
+ IWL_TM_CMD_DEV2APP_EEPROM_RSP = 16,
+ IWL_TM_CMD_APP2DEV_OWNERSHIP = 17,
+ IWL_TM_CMD_MAX = 18,
};
+/*
+ * Atrribute filed in testmode command
+ * See enum iwl_tm_cmd_t.
+ *
+ * @IWL_TM_ATTR_NOT_APPLICABLE:
+ * The attribute is not applicable or invalid
+ * @IWL_TM_ATTR_COMMAND:
+ * From user space to kernel space:
+ * the command either destines to ucode, driver, or register;
+ * From kernel space to user space:
+ * the command either carries synchronous response,
+ * or the spontaneous message multicast from the device;
+ *
+ * @IWL_TM_ATTR_UCODE_CMD_ID:
+ * @IWL_TM_ATTR_UCODE_CMD_DATA:
+ * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE,
+ * The mandatory fields are :
+ * IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID;
+ * IWL_TM_ATTR_COMMAND_FLAG for the flags of the commands;
+ * The optional fields are:
+ * IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload
+ * to the ucode
+ *
+ * @IWL_TM_ATTR_REG_OFFSET:
+ * @IWL_TM_ATTR_REG_VALUE8:
+ * @IWL_TM_ATTR_REG_VALUE32:
+ * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX,
+ * The mandatory fields are:
+ * IWL_TM_ATTR_REG_OFFSET for the offset of the target register;
+ * IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value
+ *
+ * @IWL_TM_ATTR_SYNC_RSP:
+ * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP,
+ * The mandatory fields are:
+ * IWL_TM_ATTR_SYNC_RSP for the data content responding to the user
+ * application command
+ *
+ * @IWL_TM_ATTR_UCODE_RX_PKT:
+ * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT,
+ * The mandatory fields are:
+ * IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user
+ * application
+ *
+ * @IWL_TM_ATTR_EEPROM:
+ * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM,
+ * The mandatory fields are:
+ * IWL_TM_ATTR_EEPROM for the data content responging to the user
+ * application
+ *
+ * @IWL_TM_ATTR_TRACE_ADDR:
+ * @IWL_TM_ATTR_TRACE_SIZE:
+ * @IWL_TM_ATTR_TRACE_DUMP:
+ * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE,
+ * The mandatory fields are:
+ * IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address
+ * IWL_TM_ATTR_MEM_TRACE_SIZE for the trace buffer size
+ * IWL_TM_ATTR_MEM_TRACE_DUMP for the trace dump
+ *
+ * @IWL_TM_ATTR_FIXRATE:
+ * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ,
+ * The mandatory fields are:
+ * IWL_TM_ATTR_FIXRATE for the fixed rate
+ *
+ * @IWL_TM_ATTR_UCODE_OWNER:
+ * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_OWNERSHIP,
+ * The mandatory fields are:
+ * IWL_TM_ATTR_UCODE_OWNER for the new owner
+ */
enum iwl_tm_attr_t {
- IWL_TM_ATTR_NOT_APPLICABLE = 0,
-
- /* From user space to kernel space:
- * the command either destines to ucode, driver, or register;
- * See enum iwl_tm_cmd_t.
- *
- * From kernel space to user space:
- * the command either carries synchronous response,
- * or the spontaneous message multicast from the device;
- * See enum iwl_tm_cmd_t. */
- IWL_TM_ATTR_COMMAND,
-
- /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE,
- * The mandatory fields are :
- * IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID;
- * IWL_TM_ATTR_COMMAND_FLAG for the flags of the commands;
- * The optional fields are:
- * IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload
- * to the ucode */
- IWL_TM_ATTR_UCODE_CMD_ID,
- IWL_TM_ATTR_UCODE_CMD_DATA,
-
- /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX,
- * The mandatory fields are:
- * IWL_TM_ATTR_REG_OFFSET for the offset of the target register;
- * IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value */
- IWL_TM_ATTR_REG_OFFSET,
- IWL_TM_ATTR_REG_VALUE8,
- IWL_TM_ATTR_REG_VALUE32,
-
- /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP,
- * The mandatory fields are:
- * IWL_TM_ATTR_SYNC_RSP for the data content responding to the user
- * application command */
- IWL_TM_ATTR_SYNC_RSP,
- /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT,
- * The mandatory fields are:
- * IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user
- * application */
- IWL_TM_ATTR_UCODE_RX_PKT,
-
- /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM,
- * The mandatory fields are:
- * IWL_TM_ATTR_EEPROM for the data content responging to the user
- * application */
- IWL_TM_ATTR_EEPROM,
-
- /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE,
- * The mandatory fields are:
- * IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address
- */
- IWL_TM_ATTR_TRACE_ADDR,
- IWL_TM_ATTR_TRACE_DATA,
-
- /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ,
- * The mandatory fields are:
- * IWL_TM_ATTR_FIXRATE for the fixed rate
- */
- IWL_TM_ATTR_FIXRATE,
-
- IWL_TM_ATTR_MAX,
+ IWL_TM_ATTR_NOT_APPLICABLE = 0,
+ IWL_TM_ATTR_COMMAND = 1,
+ IWL_TM_ATTR_UCODE_CMD_ID = 2,
+ IWL_TM_ATTR_UCODE_CMD_DATA = 3,
+ IWL_TM_ATTR_REG_OFFSET = 4,
+ IWL_TM_ATTR_REG_VALUE8 = 5,
+ IWL_TM_ATTR_REG_VALUE32 = 6,
+ IWL_TM_ATTR_SYNC_RSP = 7,
+ IWL_TM_ATTR_UCODE_RX_PKT = 8,
+ IWL_TM_ATTR_EEPROM = 9,
+ IWL_TM_ATTR_TRACE_ADDR = 10,
+ IWL_TM_ATTR_TRACE_SIZE = 11,
+ IWL_TM_ATTR_TRACE_DUMP = 12,
+ IWL_TM_ATTR_FIXRATE = 13,
+ IWL_TM_ATTR_UCODE_OWNER = 14,
+ IWL_TM_ATTR_MAX = 15,
};
/* uCode trace buffer */
-#define TRACE_BUFF_SIZE 0x20000
+#define TRACE_BUFF_SIZE_MAX 0x200000
+#define TRACE_BUFF_SIZE_MIN 0x20000
+#define TRACE_BUFF_SIZE_DEF TRACE_BUFF_SIZE_MIN
#define TRACE_BUFF_PADD 0x2000
-#define TRACE_TOTAL_SIZE (TRACE_BUFF_SIZE + TRACE_BUFF_PADD)
+#define TRACE_CHUNK_SIZE (PAGE_SIZE - 1024)
#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
new file mode 100644
index 000000000000..2b6756e8b8f9
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
@@ -0,0 +1,436 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#ifndef __iwl_trans_int_pcie_h__
+#define __iwl_trans_int_pcie_h__
+
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/pci.h>
+
+#include "iwl-fh.h"
+#include "iwl-csr.h"
+#include "iwl-shared.h"
+#include "iwl-trans.h"
+#include "iwl-debug.h"
+#include "iwl-io.h"
+
+struct iwl_tx_queue;
+struct iwl_queue;
+struct iwl_host_cmd;
+
+/*This file includes the declaration that are internal to the
+ * trans_pcie layer */
+
+/**
+ * struct isr_statistics - interrupt statistics
+ *
+ */
+struct isr_statistics {
+ u32 hw;
+ u32 sw;
+ u32 err_code;
+ u32 sch;
+ u32 alive;
+ u32 rfkill;
+ u32 ctkill;
+ u32 wakeup;
+ u32 rx;
+ u32 tx;
+ u32 unhandled;
+};
+
+/**
+ * struct iwl_rx_queue - Rx queue
+ * @bd: driver's pointer to buffer of receive buffer descriptors (rbd)
+ * @bd_dma: bus address of buffer of receive buffer descriptors (rbd)
+ * @pool:
+ * @queue:
+ * @read: Shared index to newest available Rx buffer
+ * @write: Shared index to oldest written Rx packet
+ * @free_count: Number of pre-allocated buffers in rx_free
+ * @write_actual:
+ * @rx_free: list of free SKBs for use
+ * @rx_used: List of Rx buffers with no SKB
+ * @need_update: flag to indicate we need to update read/write index
+ * @rb_stts: driver's pointer to receive buffer status
+ * @rb_stts_dma: bus address of receive buffer status
+ * @lock:
+ *
+ * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers
+ */
+struct iwl_rx_queue {
+ __le32 *bd;
+ dma_addr_t bd_dma;
+ struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS];
+ struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE];
+ u32 read;
+ u32 write;
+ u32 free_count;
+ u32 write_actual;
+ struct list_head rx_free;
+ struct list_head rx_used;
+ int need_update;
+ struct iwl_rb_status *rb_stts;
+ dma_addr_t rb_stts_dma;
+ spinlock_t lock;
+};
+
+struct iwl_dma_ptr {
+ dma_addr_t dma;
+ void *addr;
+ size_t size;
+};
+
+/*
+ * This queue number is required for proper operation
+ * because the ucode will stop/start the scheduler as
+ * required.
+ */
+#define IWL_IPAN_MCAST_QUEUE 8
+
+struct iwl_cmd_meta {
+ /* only for SYNC commands, iff the reply skb is wanted */
+ struct iwl_host_cmd *source;
+
+ u32 flags;
+
+ DEFINE_DMA_UNMAP_ADDR(mapping);
+ DEFINE_DMA_UNMAP_LEN(len);
+};
+
+/*
+ * Generic queue structure
+ *
+ * Contains common data for Rx and Tx queues.
+ *
+ * Note the difference between n_bd and n_window: the hardware
+ * always assumes 256 descriptors, so n_bd is always 256 (unless
+ * there might be HW changes in the future). For the normal TX
+ * queues, n_window, which is the size of the software queue data
+ * is also 256; however, for the command queue, n_window is only
+ * 32 since we don't need so many commands pending. Since the HW
+ * still uses 256 BDs for DMA though, n_bd stays 256. As a result,
+ * the software buffers (in the variables @meta, @txb in struct
+ * iwl_tx_queue) only have 32 entries, while the HW buffers (@tfds
+ * in the same struct) have 256.
+ * This means that we end up with the following:
+ * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 |
+ * SW entries: | 0 | ... | 31 |
+ * where N is a number between 0 and 7. This means that the SW
+ * data is a window overlayed over the HW queue.
+ */
+struct iwl_queue {
+ int n_bd; /* number of BDs in this queue */
+ int write_ptr; /* 1-st empty entry (index) host_w*/
+ int read_ptr; /* last used entry (index) host_r*/
+ /* use for monitoring and recovering the stuck queue */
+ dma_addr_t dma_addr; /* physical addr for BD's */
+ int n_window; /* safe queue window */
+ u32 id;
+ int low_mark; /* low watermark, resume queue if free
+ * space more than this */
+ int high_mark; /* high watermark, stop queue if free
+ * space less than this */
+};
+
+/**
+ * struct iwl_tx_queue - Tx Queue for DMA
+ * @q: generic Rx/Tx queue descriptor
+ * @bd: base of circular buffer of TFDs
+ * @cmd: array of command/TX buffer pointers
+ * @meta: array of meta data for each command/tx buffer
+ * @dma_addr_cmd: physical address of cmd/tx buffer array
+ * @txb: array of per-TFD driver data
+ * @time_stamp: time (in jiffies) of last read_ptr change
+ * @need_update: indicates need to update read/write index
+ * @sched_retry: indicates queue is high-throughput aggregation (HT AGG) enabled
+ * @sta_id: valid if sched_retry is set
+ * @tid: valid if sched_retry is set
+ *
+ * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
+ * descriptors) and required locking structures.
+ */
+#define TFD_TX_CMD_SLOTS 256
+#define TFD_CMD_SLOTS 32
+
+struct iwl_tx_queue {
+ struct iwl_queue q;
+ struct iwl_tfd *tfds;
+ struct iwl_device_cmd **cmd;
+ struct iwl_cmd_meta *meta;
+ struct sk_buff **skbs;
+ unsigned long time_stamp;
+ u8 need_update;
+ u8 sched_retry;
+ u8 active;
+ u8 swq_id;
+
+ u16 sta_id;
+ u16 tid;
+};
+
+/**
+ * struct iwl_trans_pcie - PCIe transport specific data
+ * @rxq: all the RX queue data
+ * @rx_replenish: work that will be called when buffers need to be allocated
+ * @trans: pointer to the generic transport area
+ * @scd_base_addr: scheduler sram base address in SRAM
+ * @scd_bc_tbls: pointer to the byte count table of the scheduler
+ * @kw: keep warm address
+ * @ac_to_fifo: to what fifo is a specifc AC mapped ?
+ * @ac_to_queue: to what tx queue is a specifc AC mapped ?
+ * @mcast_queue:
+ * @txq: Tx DMA processing queues
+ * @txq_ctx_active_msk: what queue is active
+ * queue_stopped: tracks what queue is stopped
+ * queue_stop_count: tracks what SW queue is stopped
+ */
+struct iwl_trans_pcie {
+ struct iwl_rx_queue rxq;
+ struct work_struct rx_replenish;
+ struct iwl_trans *trans;
+
+ /* INT ICT Table */
+ __le32 *ict_tbl;
+ void *ict_tbl_vir;
+ dma_addr_t ict_tbl_dma;
+ dma_addr_t aligned_ict_tbl_dma;
+ int ict_index;
+ u32 inta;
+ bool use_ict;
+ struct tasklet_struct irq_tasklet;
+ struct isr_statistics isr_stats;
+
+ u32 inta_mask;
+ u32 scd_base_addr;
+ struct iwl_dma_ptr scd_bc_tbls;
+ struct iwl_dma_ptr kw;
+
+ const u8 *ac_to_fifo[NUM_IWL_RXON_CTX];
+ const u8 *ac_to_queue[NUM_IWL_RXON_CTX];
+ u8 mcast_queue[NUM_IWL_RXON_CTX];
+
+ struct iwl_tx_queue *txq;
+ unsigned long txq_ctx_active_msk;
+#define IWL_MAX_HW_QUEUES 32
+ unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
+ atomic_t queue_stop_count[4];
+};
+
+#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
+ ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific))
+
+/*****************************************************
+* RX
+******************************************************/
+void iwl_bg_rx_replenish(struct work_struct *data);
+void iwl_irq_tasklet(struct iwl_trans *trans);
+void iwlagn_rx_replenish(struct iwl_trans *trans);
+void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans,
+ struct iwl_rx_queue *q);
+
+/*****************************************************
+* ICT
+******************************************************/
+int iwl_reset_ict(struct iwl_trans *trans);
+void iwl_disable_ict(struct iwl_trans *trans);
+int iwl_alloc_isr_ict(struct iwl_trans *trans);
+void iwl_free_isr_ict(struct iwl_trans *trans);
+irqreturn_t iwl_isr_ict(int irq, void *data);
+
+/*****************************************************
+* TX / HCMD
+******************************************************/
+void iwl_txq_update_write_ptr(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq);
+int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq,
+ dma_addr_t addr, u16 len, u8 reset);
+int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id);
+int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
+void iwl_tx_cmd_complete(struct iwl_trans *trans,
+ struct iwl_rx_mem_buffer *rxb, int handler_status);
+void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq,
+ u16 byte_cnt);
+void iwl_trans_pcie_txq_agg_disable(struct iwl_trans *trans, int txq_id);
+int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx, int sta_id,
+ int tid);
+void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index);
+void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq,
+ int tx_fifo_id, int scd_retry);
+int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx, int sta_id,
+ int tid, u16 *ssn);
+void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx,
+ int sta_id, int tid, int frame_limit);
+void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
+ int index, enum dma_data_direction dma_dir);
+int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index,
+ struct sk_buff_head *skbs);
+int iwl_queue_space(const struct iwl_queue *q);
+
+/*****************************************************
+* Error handling
+******************************************************/
+int iwl_dump_nic_event_log(struct iwl_trans *trans, bool full_log,
+ char **buf, bool display);
+int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display);
+void iwl_dump_csr(struct iwl_trans *trans);
+
+/*****************************************************
+* Helpers
+******************************************************/
+static inline void iwl_disable_interrupts(struct iwl_trans *trans)
+{
+ clear_bit(STATUS_INT_ENABLED, &trans->shrd->status);
+
+ /* disable interrupts from uCode/NIC to host */
+ iwl_write32(bus(trans), CSR_INT_MASK, 0x00000000);
+
+ /* acknowledge/clear/reset any interrupts still pending
+ * from uCode or flow handler (Rx/Tx DMA) */
+ iwl_write32(bus(trans), CSR_INT, 0xffffffff);
+ iwl_write32(bus(trans), CSR_FH_INT_STATUS, 0xffffffff);
+ IWL_DEBUG_ISR(trans, "Disabled interrupts\n");
+}
+
+static inline void iwl_enable_interrupts(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ IWL_DEBUG_ISR(trans, "Enabling interrupts\n");
+ set_bit(STATUS_INT_ENABLED, &trans->shrd->status);
+ iwl_write32(bus(trans), CSR_INT_MASK, trans_pcie->inta_mask);
+}
+
+/*
+ * we have 8 bits used like this:
+ *
+ * 7 6 5 4 3 2 1 0
+ * | | | | | | | |
+ * | | | | | | +-+-------- AC queue (0-3)
+ * | | | | | |
+ * | +-+-+-+-+------------ HW queue ID
+ * |
+ * +---------------------- unused
+ */
+static inline void iwl_set_swq_id(struct iwl_tx_queue *txq, u8 ac, u8 hwq)
+{
+ BUG_ON(ac > 3); /* only have 2 bits */
+ BUG_ON(hwq > 31); /* only use 5 bits */
+
+ txq->swq_id = (hwq << 2) | ac;
+}
+
+static inline void iwl_wake_queue(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq)
+{
+ u8 queue = txq->swq_id;
+ u8 ac = queue & 3;
+ u8 hwq = (queue >> 2) & 0x1f;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (test_and_clear_bit(hwq, trans_pcie->queue_stopped))
+ if (atomic_dec_return(&trans_pcie->queue_stop_count[ac]) <= 0)
+ iwl_wake_sw_queue(priv(trans), ac);
+}
+
+static inline void iwl_stop_queue(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq)
+{
+ u8 queue = txq->swq_id;
+ u8 ac = queue & 3;
+ u8 hwq = (queue >> 2) & 0x1f;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (!test_and_set_bit(hwq, trans_pcie->queue_stopped))
+ if (atomic_inc_return(&trans_pcie->queue_stop_count[ac]) > 0)
+ iwl_stop_sw_queue(priv(trans), ac);
+}
+
+#ifdef ieee80211_stop_queue
+#undef ieee80211_stop_queue
+#endif
+
+#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue
+
+#ifdef ieee80211_wake_queue
+#undef ieee80211_wake_queue
+#endif
+
+#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue
+
+static inline void iwl_txq_ctx_activate(struct iwl_trans_pcie *trans_pcie,
+ int txq_id)
+{
+ set_bit(txq_id, &trans_pcie->txq_ctx_active_msk);
+}
+
+static inline void iwl_txq_ctx_deactivate(struct iwl_trans_pcie *trans_pcie,
+ int txq_id)
+{
+ clear_bit(txq_id, &trans_pcie->txq_ctx_active_msk);
+}
+
+static inline int iwl_queue_used(const struct iwl_queue *q, int i)
+{
+ return q->write_ptr >= q->read_ptr ?
+ (i >= q->read_ptr && i < q->write_ptr) :
+ !(i < q->read_ptr && i >= q->write_ptr);
+}
+
+static inline u8 get_cmd_index(struct iwl_queue *q, u32 index)
+{
+ return index & (q->n_window - 1);
+}
+
+#define IWL_TX_FIFO_BK 0 /* shared */
+#define IWL_TX_FIFO_BE 1
+#define IWL_TX_FIFO_VI 2 /* shared */
+#define IWL_TX_FIFO_VO 3
+#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK
+#define IWL_TX_FIFO_BE_IPAN 4
+#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI
+#define IWL_TX_FIFO_VO_IPAN 5
+/* re-uses the VO FIFO, uCode will properly flush/schedule */
+#define IWL_TX_FIFO_AUX 5
+#define IWL_TX_FIFO_UNUSED -1
+
+/* AUX (TX during scan dwell) queue */
+#define IWL_AUX_QUEUE 10
+
+#endif /* __iwl_trans_int_pcie_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
new file mode 100644
index 000000000000..374c68cc1d70
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
@@ -0,0 +1,1435 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/gfp.h>
+
+/*TODO: Remove include to iwl-core.h*/
+#include "iwl-core.h"
+#include "iwl-io.h"
+#include "iwl-trans-pcie-int.h"
+
+/******************************************************************************
+ *
+ * RX path functions
+ *
+ ******************************************************************************/
+
+/*
+ * Rx theory of operation
+ *
+ * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs),
+ * each of which point to Receive Buffers to be filled by the NIC. These get
+ * used not only for Rx frames, but for any command response or notification
+ * from the NIC. The driver and NIC manage the Rx buffers by means
+ * of indexes into the circular buffer.
+ *
+ * Rx Queue Indexes
+ * The host/firmware share two index registers for managing the Rx buffers.
+ *
+ * The READ index maps to the first position that the firmware may be writing
+ * to -- the driver can read up to (but not including) this position and get
+ * good data.
+ * The READ index is managed by the firmware once the card is enabled.
+ *
+ * The WRITE index maps to the last position the driver has read from -- the
+ * position preceding WRITE is the last slot the firmware can place a packet.
+ *
+ * The queue is empty (no good data) if WRITE = READ - 1, and is full if
+ * WRITE = READ.
+ *
+ * During initialization, the host sets up the READ queue position to the first
+ * INDEX position, and WRITE to the last (READ - 1 wrapped)
+ *
+ * When the firmware places a packet in a buffer, it will advance the READ index
+ * and fire the RX interrupt. The driver can then query the READ index and
+ * process as many packets as possible, moving the WRITE index forward as it
+ * resets the Rx queue buffers with new memory.
+ *
+ * The management in the driver is as follows:
+ * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When
+ * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
+ * to replenish the iwl->rxq->rx_free.
+ * + In iwl_rx_replenish (scheduled) if 'processed' != 'read' then the
+ * iwl->rxq is replenished and the READ INDEX is updated (updating the
+ * 'processed' and 'read' driver indexes as well)
+ * + A received packet is processed and handed to the kernel network stack,
+ * detached from the iwl->rxq. The driver 'processed' index is updated.
+ * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free
+ * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ
+ * INDEX is not incremented and iwl->status(RX_STALLED) is set. If there
+ * were enough free buffers and RX_STALLED is set it is cleared.
+ *
+ *
+ * Driver sequence:
+ *
+ * iwl_rx_queue_alloc() Allocates rx_free
+ * iwl_rx_replenish() Replenishes rx_free list from rx_used, and calls
+ * iwl_rx_queue_restock
+ * iwl_rx_queue_restock() Moves available buffers from rx_free into Rx
+ * queue, updates firmware pointers, and updates
+ * the WRITE index. If insufficient rx_free buffers
+ * are available, schedules iwl_rx_replenish
+ *
+ * -- enable interrupts --
+ * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the
+ * READ INDEX, detaching the SKB from the pool.
+ * Moves the packet buffer from queue to rx_used.
+ * Calls iwl_rx_queue_restock to refill any empty
+ * slots.
+ * ...
+ *
+ */
+
+/**
+ * iwl_rx_queue_space - Return number of free slots available in queue.
+ */
+static int iwl_rx_queue_space(const struct iwl_rx_queue *q)
+{
+ int s = q->read - q->write;
+ if (s <= 0)
+ s += RX_QUEUE_SIZE;
+ /* keep some buffer to not confuse full and empty queue */
+ s -= 2;
+ if (s < 0)
+ s = 0;
+ return s;
+}
+
+/**
+ * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue
+ */
+void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans,
+ struct iwl_rx_queue *q)
+{
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ if (q->need_update == 0)
+ goto exit_unlock;
+
+ if (hw_params(trans).shadow_reg_enable) {
+ /* shadow register enabled */
+ /* Device expects a multiple of 8 */
+ q->write_actual = (q->write & ~0x7);
+ iwl_write32(bus(trans), FH_RSCSR_CHNL0_WPTR, q->write_actual);
+ } else {
+ /* If power-saving is in use, make sure device is awake */
+ if (test_bit(STATUS_POWER_PMI, &trans->shrd->status)) {
+ reg = iwl_read32(bus(trans), CSR_UCODE_DRV_GP1);
+
+ if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+ IWL_DEBUG_INFO(trans,
+ "Rx queue requesting wakeup,"
+ " GP1 = 0x%x\n", reg);
+ iwl_set_bit(bus(trans), CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ goto exit_unlock;
+ }
+
+ q->write_actual = (q->write & ~0x7);
+ iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_WPTR,
+ q->write_actual);
+
+ /* Else device is assumed to be awake */
+ } else {
+ /* Device expects a multiple of 8 */
+ q->write_actual = (q->write & ~0x7);
+ iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_WPTR,
+ q->write_actual);
+ }
+ }
+ q->need_update = 0;
+
+ exit_unlock:
+ spin_unlock_irqrestore(&q->lock, flags);
+}
+
+/**
+ * iwlagn_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr
+ */
+static inline __le32 iwlagn_dma_addr2rbd_ptr(dma_addr_t dma_addr)
+{
+ return cpu_to_le32((u32)(dma_addr >> 8));
+}
+
+/**
+ * iwlagn_rx_queue_restock - refill RX queue from pre-allocated pool
+ *
+ * If there are slots in the RX queue that need to be restocked,
+ * and we have free pre-allocated buffers, fill the ranks as much
+ * as we can, pulling from rx_free.
+ *
+ * This moves the 'write' index forward to catch up with 'processed', and
+ * also updates the memory address in the firmware to reference the new
+ * target buffer.
+ */
+static void iwlagn_rx_queue_restock(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ struct iwl_rx_queue *rxq = &trans_pcie->rxq;
+ struct list_head *element;
+ struct iwl_rx_mem_buffer *rxb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rxq->lock, flags);
+ while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) {
+ /* The overwritten rxb must be a used one */
+ rxb = rxq->queue[rxq->write];
+ BUG_ON(rxb && rxb->page);
+
+ /* Get next free Rx buffer, remove from free list */
+ element = rxq->rx_free.next;
+ rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
+ list_del(element);
+
+ /* Point to Rx buffer via next RBD in circular buffer */
+ rxq->bd[rxq->write] = iwlagn_dma_addr2rbd_ptr(rxb->page_dma);
+ rxq->queue[rxq->write] = rxb;
+ rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
+ rxq->free_count--;
+ }
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ /* If the pre-allocated buffer pool is dropping low, schedule to
+ * refill it */
+ if (rxq->free_count <= RX_LOW_WATERMARK)
+ queue_work(trans->shrd->workqueue, &trans_pcie->rx_replenish);
+
+
+ /* If we've added more space for the firmware to place data, tell it.
+ * Increment device's write pointer in multiples of 8. */
+ if (rxq->write_actual != (rxq->write & ~0x7)) {
+ spin_lock_irqsave(&rxq->lock, flags);
+ rxq->need_update = 1;
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ iwl_rx_queue_update_write_ptr(trans, rxq);
+ }
+}
+
+/**
+ * iwlagn_rx_replenish - Move all used packet from rx_used to rx_free
+ *
+ * When moving to rx_free an SKB is allocated for the slot.
+ *
+ * Also restock the Rx queue via iwl_rx_queue_restock.
+ * This is called as a scheduled work item (except for during initialization)
+ */
+static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ struct iwl_rx_queue *rxq = &trans_pcie->rxq;
+ struct list_head *element;
+ struct iwl_rx_mem_buffer *rxb;
+ struct page *page;
+ unsigned long flags;
+ gfp_t gfp_mask = priority;
+
+ while (1) {
+ spin_lock_irqsave(&rxq->lock, flags);
+ if (list_empty(&rxq->rx_used)) {
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ if (rxq->free_count > RX_LOW_WATERMARK)
+ gfp_mask |= __GFP_NOWARN;
+
+ if (hw_params(trans).rx_page_order > 0)
+ gfp_mask |= __GFP_COMP;
+
+ /* Alloc a new receive buffer */
+ page = alloc_pages(gfp_mask,
+ hw_params(trans).rx_page_order);
+ if (!page) {
+ if (net_ratelimit())
+ IWL_DEBUG_INFO(trans, "alloc_pages failed, "
+ "order: %d\n",
+ hw_params(trans).rx_page_order);
+
+ if ((rxq->free_count <= RX_LOW_WATERMARK) &&
+ net_ratelimit())
+ IWL_CRIT(trans, "Failed to alloc_pages with %s."
+ "Only %u free buffers remaining.\n",
+ priority == GFP_ATOMIC ?
+ "GFP_ATOMIC" : "GFP_KERNEL",
+ rxq->free_count);
+ /* We don't reschedule replenish work here -- we will
+ * call the restock method and if it still needs
+ * more buffers it will schedule replenish */
+ return;
+ }
+
+ spin_lock_irqsave(&rxq->lock, flags);
+
+ if (list_empty(&rxq->rx_used)) {
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ __free_pages(page, hw_params(trans).rx_page_order);
+ return;
+ }
+ element = rxq->rx_used.next;
+ rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
+ list_del(element);
+
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ BUG_ON(rxb->page);
+ rxb->page = page;
+ /* Get physical address of the RB */
+ rxb->page_dma = dma_map_page(bus(trans)->dev, page, 0,
+ PAGE_SIZE << hw_params(trans).rx_page_order,
+ DMA_FROM_DEVICE);
+ /* dma address must be no more than 36 bits */
+ BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36));
+ /* and also 256 byte aligned! */
+ BUG_ON(rxb->page_dma & DMA_BIT_MASK(8));
+
+ spin_lock_irqsave(&rxq->lock, flags);
+
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ }
+}
+
+void iwlagn_rx_replenish(struct iwl_trans *trans)
+{
+ unsigned long flags;
+
+ iwlagn_rx_allocate(trans, GFP_KERNEL);
+
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+ iwlagn_rx_queue_restock(trans);
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+}
+
+static void iwlagn_rx_replenish_now(struct iwl_trans *trans)
+{
+ iwlagn_rx_allocate(trans, GFP_ATOMIC);
+
+ iwlagn_rx_queue_restock(trans);
+}
+
+void iwl_bg_rx_replenish(struct work_struct *data)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ container_of(data, struct iwl_trans_pcie, rx_replenish);
+ struct iwl_trans *trans = trans_pcie->trans;
+
+ if (test_bit(STATUS_EXIT_PENDING, &trans->shrd->status))
+ return;
+
+ mutex_lock(&trans->shrd->mutex);
+ iwlagn_rx_replenish(trans);
+ mutex_unlock(&trans->shrd->mutex);
+}
+
+/**
+ * iwl_rx_handle - Main entry function for receiving responses from uCode
+ *
+ * Uses the priv->rx_handlers callback function array to invoke
+ * the appropriate handlers, including command responses,
+ * frame-received notifications, and other notifications.
+ */
+static void iwl_rx_handle(struct iwl_trans *trans)
+{
+ struct iwl_rx_mem_buffer *rxb;
+ struct iwl_rx_packet *pkt;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rx_queue *rxq = &trans_pcie->rxq;
+ struct iwl_tx_queue *txq = &trans_pcie->txq[trans->shrd->cmd_queue];
+ struct iwl_device_cmd *cmd;
+ u32 r, i;
+ int reclaim;
+ unsigned long flags;
+ u8 fill_rx = 0;
+ u32 count = 8;
+ int total_empty;
+ int index, cmd_index;
+
+ /* uCode's read index (stored in shared DRAM) indicates the last Rx
+ * buffer that the driver may process (last buffer filled by ucode). */
+ r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF;
+ i = rxq->read;
+
+ /* Rx interrupt, but nothing sent from uCode */
+ if (i == r)
+ IWL_DEBUG_RX(trans, "r = %d, i = %d\n", r, i);
+
+ /* calculate total frames need to be restock after handling RX */
+ total_empty = r - rxq->write_actual;
+ if (total_empty < 0)
+ total_empty += RX_QUEUE_SIZE;
+
+ if (total_empty > (RX_QUEUE_SIZE / 2))
+ fill_rx = 1;
+
+ while (i != r) {
+ int len, err;
+ u16 sequence;
+
+ rxb = rxq->queue[i];
+
+ /* If an RXB doesn't have a Rx queue slot associated with it,
+ * then a bug has been introduced in the queue refilling
+ * routines -- catch it here */
+ if (WARN_ON(rxb == NULL)) {
+ i = (i + 1) & RX_QUEUE_MASK;
+ continue;
+ }
+
+ rxq->queue[i] = NULL;
+
+ dma_unmap_page(bus(trans)->dev, rxb->page_dma,
+ PAGE_SIZE << hw_params(trans).rx_page_order,
+ DMA_FROM_DEVICE);
+ pkt = rxb_addr(rxb);
+
+ IWL_DEBUG_RX(trans, "r = %d, i = %d, %s, 0x%02x\n", r,
+ i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
+
+ len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+ len += sizeof(u32); /* account for status word */
+ trace_iwlwifi_dev_rx(priv(trans), pkt, len);
+
+ /* Reclaim a command buffer only if this packet is a response
+ * to a (driver-originated) command.
+ * If the packet (e.g. Rx frame) originated from uCode,
+ * there is no command buffer to reclaim.
+ * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
+ * but apparently a few don't get set; catch them here. */
+ reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) &&
+ (pkt->hdr.cmd != REPLY_RX_PHY_CMD) &&
+ (pkt->hdr.cmd != REPLY_RX) &&
+ (pkt->hdr.cmd != REPLY_RX_MPDU_CMD) &&
+ (pkt->hdr.cmd != REPLY_COMPRESSED_BA) &&
+ (pkt->hdr.cmd != STATISTICS_NOTIFICATION) &&
+ (pkt->hdr.cmd != REPLY_TX);
+
+ sequence = le16_to_cpu(pkt->hdr.sequence);
+ index = SEQ_TO_INDEX(sequence);
+ cmd_index = get_cmd_index(&txq->q, index);
+
+ if (reclaim)
+ cmd = txq->cmd[cmd_index];
+ else
+ cmd = NULL;
+
+ /* warn if this is cmd response / notification and the uCode
+ * didn't set the SEQ_RX_FRAME for a frame that is
+ * uCode-originated
+ * If you saw this code after the second half of 2012, then
+ * please remove it
+ */
+ WARN(pkt->hdr.cmd != REPLY_TX && reclaim == false &&
+ (!(pkt->hdr.sequence & SEQ_RX_FRAME)),
+ "reclaim is false, SEQ_RX_FRAME unset: %s\n",
+ get_cmd_string(pkt->hdr.cmd));
+
+ err = iwl_rx_dispatch(priv(trans), rxb, cmd);
+
+ /*
+ * XXX: After here, we should always check rxb->page
+ * against NULL before touching it or its virtual
+ * memory (pkt). Because some rx_handler might have
+ * already taken or freed the pages.
+ */
+
+ if (reclaim) {
+ /* Invoke any callbacks, transfer the buffer to caller,
+ * and fire off the (possibly) blocking
+ * iwl_trans_send_cmd()
+ * as we reclaim the driver command queue */
+ if (rxb->page)
+ iwl_tx_cmd_complete(trans, rxb, err);
+ else
+ IWL_WARN(trans, "Claim null rxb?\n");
+ }
+
+ /* Reuse the page if possible. For notification packets and
+ * SKBs that fail to Rx correctly, add them back into the
+ * rx_free list for reuse later. */
+ spin_lock_irqsave(&rxq->lock, flags);
+ if (rxb->page != NULL) {
+ rxb->page_dma = dma_map_page(bus(trans)->dev, rxb->page,
+ 0, PAGE_SIZE <<
+ hw_params(trans).rx_page_order,
+ DMA_FROM_DEVICE);
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+ } else
+ list_add_tail(&rxb->list, &rxq->rx_used);
+
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ i = (i + 1) & RX_QUEUE_MASK;
+ /* If there are a lot of unused frames,
+ * restock the Rx queue so ucode wont assert. */
+ if (fill_rx) {
+ count++;
+ if (count >= 8) {
+ rxq->read = i;
+ iwlagn_rx_replenish_now(trans);
+ count = 0;
+ }
+ }
+ }
+
+ /* Backtrack one entry */
+ rxq->read = i;
+ if (fill_rx)
+ iwlagn_rx_replenish_now(trans);
+ else
+ iwlagn_rx_queue_restock(trans);
+}
+
+static const char * const desc_lookup_text[] = {
+ "OK",
+ "FAIL",
+ "BAD_PARAM",
+ "BAD_CHECKSUM",
+ "NMI_INTERRUPT_WDG",
+ "SYSASSERT",
+ "FATAL_ERROR",
+ "BAD_COMMAND",
+ "HW_ERROR_TUNE_LOCK",
+ "HW_ERROR_TEMPERATURE",
+ "ILLEGAL_CHAN_FREQ",
+ "VCC_NOT_STABLE",
+ "FH_ERROR",
+ "NMI_INTERRUPT_HOST",
+ "NMI_INTERRUPT_ACTION_PT",
+ "NMI_INTERRUPT_UNKNOWN",
+ "UCODE_VERSION_MISMATCH",
+ "HW_ERROR_ABS_LOCK",
+ "HW_ERROR_CAL_LOCK_FAIL",
+ "NMI_INTERRUPT_INST_ACTION_PT",
+ "NMI_INTERRUPT_DATA_ACTION_PT",
+ "NMI_TRM_HW_ER",
+ "NMI_INTERRUPT_TRM",
+ "NMI_INTERRUPT_BREAK_POINT",
+ "DEBUG_0",
+ "DEBUG_1",
+ "DEBUG_2",
+ "DEBUG_3",
+};
+
+static struct { char *name; u8 num; } advanced_lookup[] = {
+ { "NMI_INTERRUPT_WDG", 0x34 },
+ { "SYSASSERT", 0x35 },
+ { "UCODE_VERSION_MISMATCH", 0x37 },
+ { "BAD_COMMAND", 0x38 },
+ { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
+ { "FATAL_ERROR", 0x3D },
+ { "NMI_TRM_HW_ERR", 0x46 },
+ { "NMI_INTERRUPT_TRM", 0x4C },
+ { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
+ { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
+ { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
+ { "NMI_INTERRUPT_HOST", 0x66 },
+ { "NMI_INTERRUPT_ACTION_PT", 0x7C },
+ { "NMI_INTERRUPT_UNKNOWN", 0x84 },
+ { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
+ { "ADVANCED_SYSASSERT", 0 },
+};
+
+static const char *desc_lookup(u32 num)
+{
+ int i;
+ int max = ARRAY_SIZE(desc_lookup_text);
+
+ if (num < max)
+ return desc_lookup_text[num];
+
+ max = ARRAY_SIZE(advanced_lookup) - 1;
+ for (i = 0; i < max; i++) {
+ if (advanced_lookup[i].num == num)
+ break;
+ }
+ return advanced_lookup[i].name;
+}
+
+#define ERROR_START_OFFSET (1 * sizeof(u32))
+#define ERROR_ELEM_SIZE (7 * sizeof(u32))
+
+static void iwl_dump_nic_error_log(struct iwl_trans *trans)
+{
+ u32 base;
+ struct iwl_error_event_table table;
+ struct iwl_priv *priv = priv(trans);
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ base = priv->device_pointers.error_event_table;
+ if (priv->ucode_type == IWL_UCODE_INIT) {
+ if (!base)
+ base = priv->init_errlog_ptr;
+ } else {
+ if (!base)
+ base = priv->inst_errlog_ptr;
+ }
+
+ if (!iwlagn_hw_valid_rtc_data_addr(base)) {
+ IWL_ERR(trans,
+ "Not valid error log pointer 0x%08X for %s uCode\n",
+ base,
+ (priv->ucode_type == IWL_UCODE_INIT)
+ ? "Init" : "RT");
+ return;
+ }
+
+ iwl_read_targ_mem_words(bus(priv), base, &table, sizeof(table));
+
+ if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+ IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+ IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+ trans->shrd->status, table.valid);
+ }
+
+ trans_pcie->isr_stats.err_code = table.error_id;
+
+ trace_iwlwifi_dev_ucode_error(priv, table.error_id, table.tsf_low,
+ table.data1, table.data2, table.line,
+ table.blink1, table.blink2, table.ilink1,
+ table.ilink2, table.bcon_time, table.gp1,
+ table.gp2, table.gp3, table.ucode_ver,
+ table.hw_ver, table.brd_ver);
+ IWL_ERR(trans, "0x%08X | %-28s\n", table.error_id,
+ desc_lookup(table.error_id));
+ IWL_ERR(trans, "0x%08X | uPc\n", table.pc);
+ IWL_ERR(trans, "0x%08X | branchlink1\n", table.blink1);
+ IWL_ERR(trans, "0x%08X | branchlink2\n", table.blink2);
+ IWL_ERR(trans, "0x%08X | interruptlink1\n", table.ilink1);
+ IWL_ERR(trans, "0x%08X | interruptlink2\n", table.ilink2);
+ IWL_ERR(trans, "0x%08X | data1\n", table.data1);
+ IWL_ERR(trans, "0x%08X | data2\n", table.data2);
+ IWL_ERR(trans, "0x%08X | line\n", table.line);
+ IWL_ERR(trans, "0x%08X | beacon time\n", table.bcon_time);
+ IWL_ERR(trans, "0x%08X | tsf low\n", table.tsf_low);
+ IWL_ERR(trans, "0x%08X | tsf hi\n", table.tsf_hi);
+ IWL_ERR(trans, "0x%08X | time gp1\n", table.gp1);
+ IWL_ERR(trans, "0x%08X | time gp2\n", table.gp2);
+ IWL_ERR(trans, "0x%08X | time gp3\n", table.gp3);
+ IWL_ERR(trans, "0x%08X | uCode version\n", table.ucode_ver);
+ IWL_ERR(trans, "0x%08X | hw version\n", table.hw_ver);
+ IWL_ERR(trans, "0x%08X | board version\n", table.brd_ver);
+ IWL_ERR(trans, "0x%08X | hcmd\n", table.hcmd);
+}
+
+/**
+ * iwl_irq_handle_error - called for HW or SW error interrupt from card
+ */
+static void iwl_irq_handle_error(struct iwl_trans *trans)
+{
+ struct iwl_priv *priv = priv(trans);
+ /* W/A for WiFi/WiMAX coex and WiMAX own the RF */
+ if (priv->cfg->internal_wimax_coex &&
+ (!(iwl_read_prph(bus(trans), APMG_CLK_CTRL_REG) &
+ APMS_CLK_VAL_MRB_FUNC_MODE) ||
+ (iwl_read_prph(bus(trans), APMG_PS_CTRL_REG) &
+ APMG_PS_CTRL_VAL_RESET_REQ))) {
+ /*
+ * Keep the restart process from trying to send host
+ * commands by clearing the ready bit.
+ */
+ clear_bit(STATUS_READY, &trans->shrd->status);
+ clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status);
+ wake_up(&priv->shrd->wait_command_queue);
+ IWL_ERR(trans, "RF is used by WiMAX\n");
+ return;
+ }
+
+ IWL_ERR(trans, "Loaded firmware version: %s\n",
+ priv->hw->wiphy->fw_version);
+
+ iwl_dump_nic_error_log(trans);
+ iwl_dump_csr(trans);
+ iwl_dump_fh(trans, NULL, false);
+ iwl_dump_nic_event_log(trans, false, NULL, false);
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (iwl_get_debug_level(trans->shrd) & IWL_DL_FW_ERRORS)
+ iwl_print_rx_config_cmd(priv(trans), IWL_RXON_CTX_BSS);
+#endif
+
+ iwlagn_fw_error(priv, false);
+}
+
+#define EVENT_START_OFFSET (4 * sizeof(u32))
+
+/**
+ * iwl_print_event_log - Dump error event log to syslog
+ *
+ */
+static int iwl_print_event_log(struct iwl_trans *trans, u32 start_idx,
+ u32 num_events, u32 mode,
+ int pos, char **buf, size_t bufsz)
+{
+ u32 i;
+ u32 base; /* SRAM byte address of event log header */
+ u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
+ u32 ptr; /* SRAM byte address of log data */
+ u32 ev, time, data; /* event log data */
+ unsigned long reg_flags;
+ struct iwl_priv *priv = priv(trans);
+
+ if (num_events == 0)
+ return pos;
+
+ base = priv->device_pointers.log_event_table;
+ if (priv->ucode_type == IWL_UCODE_INIT) {
+ if (!base)
+ base = priv->init_evtlog_ptr;
+ } else {
+ if (!base)
+ base = priv->inst_evtlog_ptr;
+ }
+
+ if (mode == 0)
+ event_size = 2 * sizeof(u32);
+ else
+ event_size = 3 * sizeof(u32);
+
+ ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
+
+ /* Make sure device is powered up for SRAM reads */
+ spin_lock_irqsave(&bus(trans)->reg_lock, reg_flags);
+ iwl_grab_nic_access(bus(trans));
+
+ /* Set starting address; reads will auto-increment */
+ iwl_write32(bus(trans), HBUS_TARG_MEM_RADDR, ptr);
+ rmb();
+
+ /* "time" is actually "data" for mode 0 (no timestamp).
+ * place event id # at far right for easier visual parsing. */
+ for (i = 0; i < num_events; i++) {
+ ev = iwl_read32(bus(trans), HBUS_TARG_MEM_RDAT);
+ time = iwl_read32(bus(trans), HBUS_TARG_MEM_RDAT);
+ if (mode == 0) {
+ /* data, ev */
+ if (bufsz) {
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ "EVT_LOG:0x%08x:%04u\n",
+ time, ev);
+ } else {
+ trace_iwlwifi_dev_ucode_event(priv, 0,
+ time, ev);
+ IWL_ERR(trans, "EVT_LOG:0x%08x:%04u\n",
+ time, ev);
+ }
+ } else {
+ data = iwl_read32(bus(trans), HBUS_TARG_MEM_RDAT);
+ if (bufsz) {
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ "EVT_LOGT:%010u:0x%08x:%04u\n",
+ time, data, ev);
+ } else {
+ IWL_ERR(trans, "EVT_LOGT:%010u:0x%08x:%04u\n",
+ time, data, ev);
+ trace_iwlwifi_dev_ucode_event(priv, time,
+ data, ev);
+ }
+ }
+ }
+
+ /* Allow device to power down */
+ iwl_release_nic_access(bus(trans));
+ spin_unlock_irqrestore(&bus(trans)->reg_lock, reg_flags);
+ return pos;
+}
+
+/**
+ * iwl_print_last_event_logs - Dump the newest # of event log to syslog
+ */
+static int iwl_print_last_event_logs(struct iwl_trans *trans, u32 capacity,
+ u32 num_wraps, u32 next_entry,
+ u32 size, u32 mode,
+ int pos, char **buf, size_t bufsz)
+{
+ /*
+ * display the newest DEFAULT_LOG_ENTRIES entries
+ * i.e the entries just before the next ont that uCode would fill.
+ */
+ if (num_wraps) {
+ if (next_entry < size) {
+ pos = iwl_print_event_log(trans,
+ capacity - (size - next_entry),
+ size - next_entry, mode,
+ pos, buf, bufsz);
+ pos = iwl_print_event_log(trans, 0,
+ next_entry, mode,
+ pos, buf, bufsz);
+ } else
+ pos = iwl_print_event_log(trans, next_entry - size,
+ size, mode, pos, buf, bufsz);
+ } else {
+ if (next_entry < size) {
+ pos = iwl_print_event_log(trans, 0, next_entry,
+ mode, pos, buf, bufsz);
+ } else {
+ pos = iwl_print_event_log(trans, next_entry - size,
+ size, mode, pos, buf, bufsz);
+ }
+ }
+ return pos;
+}
+
+#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
+
+int iwl_dump_nic_event_log(struct iwl_trans *trans, bool full_log,
+ char **buf, bool display)
+{
+ u32 base; /* SRAM byte address of event log header */
+ u32 capacity; /* event log capacity in # entries */
+ u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
+ u32 num_wraps; /* # times uCode wrapped to top of log */
+ u32 next_entry; /* index of next entry to be written by uCode */
+ u32 size; /* # entries that we'll print */
+ u32 logsize;
+ int pos = 0;
+ size_t bufsz = 0;
+ struct iwl_priv *priv = priv(trans);
+
+ base = priv->device_pointers.log_event_table;
+ if (priv->ucode_type == IWL_UCODE_INIT) {
+ logsize = priv->init_evtlog_size;
+ if (!base)
+ base = priv->init_evtlog_ptr;
+ } else {
+ logsize = priv->inst_evtlog_size;
+ if (!base)
+ base = priv->inst_evtlog_ptr;
+ }
+
+ if (!iwlagn_hw_valid_rtc_data_addr(base)) {
+ IWL_ERR(trans,
+ "Invalid event log pointer 0x%08X for %s uCode\n",
+ base,
+ (priv->ucode_type == IWL_UCODE_INIT)
+ ? "Init" : "RT");
+ return -EINVAL;
+ }
+
+ /* event log header */
+ capacity = iwl_read_targ_mem(bus(trans), base);
+ mode = iwl_read_targ_mem(bus(trans), base + (1 * sizeof(u32)));
+ num_wraps = iwl_read_targ_mem(bus(trans), base + (2 * sizeof(u32)));
+ next_entry = iwl_read_targ_mem(bus(trans), base + (3 * sizeof(u32)));
+
+ if (capacity > logsize) {
+ IWL_ERR(trans, "Log capacity %d is bogus, limit to %d "
+ "entries\n", capacity, logsize);
+ capacity = logsize;
+ }
+
+ if (next_entry > logsize) {
+ IWL_ERR(trans, "Log write index %d is bogus, limit to %d\n",
+ next_entry, logsize);
+ next_entry = logsize;
+ }
+
+ size = num_wraps ? capacity : next_entry;
+
+ /* bail out if nothing in log */
+ if (size == 0) {
+ IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n");
+ return pos;
+ }
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (!(iwl_get_debug_level(trans->shrd) & IWL_DL_FW_ERRORS) && !full_log)
+ size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
+#else
+ size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
+#endif
+ IWL_ERR(trans, "Start IWL Event Log Dump: display last %u entries\n",
+ size);
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (display) {
+ if (full_log)
+ bufsz = capacity * 48;
+ else
+ bufsz = size * 48;
+ *buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!*buf)
+ return -ENOMEM;
+ }
+ if ((iwl_get_debug_level(trans->shrd) & IWL_DL_FW_ERRORS) || full_log) {
+ /*
+ * if uCode has wrapped back to top of log,
+ * start at the oldest entry,
+ * i.e the next one that uCode would fill.
+ */
+ if (num_wraps)
+ pos = iwl_print_event_log(trans, next_entry,
+ capacity - next_entry, mode,
+ pos, buf, bufsz);
+ /* (then/else) start at top of log */
+ pos = iwl_print_event_log(trans, 0,
+ next_entry, mode, pos, buf, bufsz);
+ } else
+ pos = iwl_print_last_event_logs(trans, capacity, num_wraps,
+ next_entry, size, mode,
+ pos, buf, bufsz);
+#else
+ pos = iwl_print_last_event_logs(trans, capacity, num_wraps,
+ next_entry, size, mode,
+ pos, buf, bufsz);
+#endif
+ return pos;
+}
+
+/* tasklet for iwlagn interrupt */
+void iwl_irq_tasklet(struct iwl_trans *trans)
+{
+ u32 inta = 0;
+ u32 handled = 0;
+ unsigned long flags;
+ u32 i;
+#ifdef CONFIG_IWLWIFI_DEBUG
+ u32 inta_mask;
+#endif
+
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
+
+
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+
+ /* Ack/clear/reset pending uCode interrupts.
+ * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
+ */
+ /* There is a hardware bug in the interrupt mask function that some
+ * interrupts (i.e. CSR_INT_BIT_SCD) can still be generated even if
+ * they are disabled in the CSR_INT_MASK register. Furthermore the
+ * ICT interrupt handling mechanism has another bug that might cause
+ * these unmasked interrupts fail to be detected. We workaround the
+ * hardware bugs here by ACKing all the possible interrupts so that
+ * interrupt coalescing can still be achieved.
+ */
+ iwl_write32(bus(trans), CSR_INT,
+ trans_pcie->inta | ~trans_pcie->inta_mask);
+
+ inta = trans_pcie->inta;
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (iwl_get_debug_level(trans->shrd) & IWL_DL_ISR) {
+ /* just for debug */
+ inta_mask = iwl_read32(bus(trans), CSR_INT_MASK);
+ IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n ",
+ inta, inta_mask);
+ }
+#endif
+
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ /* saved interrupt in inta variable now we can reset trans_pcie->inta */
+ trans_pcie->inta = 0;
+
+ /* Now service all interrupt bits discovered above. */
+ if (inta & CSR_INT_BIT_HW_ERR) {
+ IWL_ERR(trans, "Hardware error detected. Restarting.\n");
+
+ /* Tell the device to stop sending interrupts */
+ iwl_disable_interrupts(trans);
+
+ isr_stats->hw++;
+ iwl_irq_handle_error(trans);
+
+ handled |= CSR_INT_BIT_HW_ERR;
+
+ return;
+ }
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (iwl_get_debug_level(trans->shrd) & (IWL_DL_ISR)) {
+ /* NIC fires this, but we don't use it, redundant with WAKEUP */
+ if (inta & CSR_INT_BIT_SCD) {
+ IWL_DEBUG_ISR(trans, "Scheduler finished to transmit "
+ "the frame/frames.\n");
+ isr_stats->sch++;
+ }
+
+ /* Alive notification via Rx interrupt will do the real work */
+ if (inta & CSR_INT_BIT_ALIVE) {
+ IWL_DEBUG_ISR(trans, "Alive interrupt\n");
+ isr_stats->alive++;
+ }
+ }
+#endif
+ /* Safely ignore these bits for debug checks below */
+ inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
+
+ /* HW RF KILL switch toggled */
+ if (inta & CSR_INT_BIT_RF_KILL) {
+ int hw_rf_kill = 0;
+ if (!(iwl_read32(bus(trans), CSR_GP_CNTRL) &
+ CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
+ hw_rf_kill = 1;
+
+ IWL_WARN(trans, "RF_KILL bit toggled to %s.\n",
+ hw_rf_kill ? "disable radio" : "enable radio");
+
+ isr_stats->rfkill++;
+
+ /* driver only loads ucode once setting the interface up.
+ * the driver allows loading the ucode even if the radio
+ * is killed. Hence update the killswitch state here. The
+ * rfkill handler will care about restarting if needed.
+ */
+ if (!test_bit(STATUS_ALIVE, &trans->shrd->status)) {
+ if (hw_rf_kill)
+ set_bit(STATUS_RF_KILL_HW,
+ &trans->shrd->status);
+ else
+ clear_bit(STATUS_RF_KILL_HW,
+ &trans->shrd->status);
+ iwl_set_hw_rfkill_state(priv(trans), hw_rf_kill);
+ }
+
+ handled |= CSR_INT_BIT_RF_KILL;
+ }
+
+ /* Chip got too hot and stopped itself */
+ if (inta & CSR_INT_BIT_CT_KILL) {
+ IWL_ERR(trans, "Microcode CT kill error detected.\n");
+ isr_stats->ctkill++;
+ handled |= CSR_INT_BIT_CT_KILL;
+ }
+
+ /* Error detected by uCode */
+ if (inta & CSR_INT_BIT_SW_ERR) {
+ IWL_ERR(trans, "Microcode SW error detected. "
+ " Restarting 0x%X.\n", inta);
+ isr_stats->sw++;
+ iwl_irq_handle_error(trans);
+ handled |= CSR_INT_BIT_SW_ERR;
+ }
+
+ /* uCode wakes up after power-down sleep */
+ if (inta & CSR_INT_BIT_WAKEUP) {
+ IWL_DEBUG_ISR(trans, "Wakeup interrupt\n");
+ iwl_rx_queue_update_write_ptr(trans, &trans_pcie->rxq);
+ for (i = 0; i < hw_params(trans).max_txq_num; i++)
+ iwl_txq_update_write_ptr(trans,
+ &trans_pcie->txq[i]);
+
+ isr_stats->wakeup++;
+
+ handled |= CSR_INT_BIT_WAKEUP;
+ }
+
+ /* All uCode command responses, including Tx command responses,
+ * Rx "responses" (frame-received notification), and other
+ * notifications from uCode come through here*/
+ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX |
+ CSR_INT_BIT_RX_PERIODIC)) {
+ IWL_DEBUG_ISR(trans, "Rx interrupt\n");
+ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) {
+ handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
+ iwl_write32(bus(trans), CSR_FH_INT_STATUS,
+ CSR_FH_INT_RX_MASK);
+ }
+ if (inta & CSR_INT_BIT_RX_PERIODIC) {
+ handled |= CSR_INT_BIT_RX_PERIODIC;
+ iwl_write32(bus(trans),
+ CSR_INT, CSR_INT_BIT_RX_PERIODIC);
+ }
+ /* Sending RX interrupt require many steps to be done in the
+ * the device:
+ * 1- write interrupt to current index in ICT table.
+ * 2- dma RX frame.
+ * 3- update RX shared data to indicate last write index.
+ * 4- send interrupt.
+ * This could lead to RX race, driver could receive RX interrupt
+ * but the shared data changes does not reflect this;
+ * periodic interrupt will detect any dangling Rx activity.
+ */
+
+ /* Disable periodic interrupt; we use it as just a one-shot. */
+ iwl_write8(bus(trans), CSR_INT_PERIODIC_REG,
+ CSR_INT_PERIODIC_DIS);
+ iwl_rx_handle(trans);
+
+ /*
+ * Enable periodic interrupt in 8 msec only if we received
+ * real RX interrupt (instead of just periodic int), to catch
+ * any dangling Rx interrupt. If it was just the periodic
+ * interrupt, there was no dangling Rx activity, and no need
+ * to extend the periodic interrupt; one-shot is enough.
+ */
+ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX))
+ iwl_write8(bus(trans), CSR_INT_PERIODIC_REG,
+ CSR_INT_PERIODIC_ENA);
+
+ isr_stats->rx++;
+ }
+
+ /* This "Tx" DMA channel is used only for loading uCode */
+ if (inta & CSR_INT_BIT_FH_TX) {
+ iwl_write32(bus(trans), CSR_FH_INT_STATUS, CSR_FH_INT_TX_MASK);
+ IWL_DEBUG_ISR(trans, "uCode load interrupt\n");
+ isr_stats->tx++;
+ handled |= CSR_INT_BIT_FH_TX;
+ /* Wake up uCode load routine, now that load is complete */
+ priv(trans)->ucode_write_complete = 1;
+ wake_up(&trans->shrd->wait_command_queue);
+ }
+
+ if (inta & ~handled) {
+ IWL_ERR(trans, "Unhandled INTA bits 0x%08x\n", inta & ~handled);
+ isr_stats->unhandled++;
+ }
+
+ if (inta & ~(trans_pcie->inta_mask)) {
+ IWL_WARN(trans, "Disabled INTA bits 0x%08x were pending\n",
+ inta & ~trans_pcie->inta_mask);
+ }
+
+ /* Re-enable all interrupts */
+ /* only Re-enable if disabled by irq */
+ if (test_bit(STATUS_INT_ENABLED, &trans->shrd->status))
+ iwl_enable_interrupts(trans);
+ /* Re-enable RF_KILL if it occurred */
+ else if (handled & CSR_INT_BIT_RF_KILL)
+ iwl_enable_rfkill_int(priv(trans));
+}
+
+/******************************************************************************
+ *
+ * ICT functions
+ *
+ ******************************************************************************/
+#define ICT_COUNT (PAGE_SIZE/sizeof(u32))
+
+/* Free dram table */
+void iwl_free_isr_ict(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (trans_pcie->ict_tbl_vir) {
+ dma_free_coherent(bus(trans)->dev,
+ (sizeof(u32) * ICT_COUNT) + PAGE_SIZE,
+ trans_pcie->ict_tbl_vir,
+ trans_pcie->ict_tbl_dma);
+ trans_pcie->ict_tbl_vir = NULL;
+ memset(&trans_pcie->ict_tbl_dma, 0,
+ sizeof(trans_pcie->ict_tbl_dma));
+ memset(&trans_pcie->aligned_ict_tbl_dma, 0,
+ sizeof(trans_pcie->aligned_ict_tbl_dma));
+ }
+}
+
+
+/* allocate dram shared table it is a PAGE_SIZE aligned
+ * also reset all data related to ICT table interrupt.
+ */
+int iwl_alloc_isr_ict(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ /* allocate shrared data table */
+ trans_pcie->ict_tbl_vir =
+ dma_alloc_coherent(bus(trans)->dev,
+ (sizeof(u32) * ICT_COUNT) + PAGE_SIZE,
+ &trans_pcie->ict_tbl_dma, GFP_KERNEL);
+ if (!trans_pcie->ict_tbl_vir)
+ return -ENOMEM;
+
+ /* align table to PAGE_SIZE boundary */
+ trans_pcie->aligned_ict_tbl_dma =
+ ALIGN(trans_pcie->ict_tbl_dma, PAGE_SIZE);
+
+ IWL_DEBUG_ISR(trans, "ict dma addr %Lx dma aligned %Lx diff %d\n",
+ (unsigned long long)trans_pcie->ict_tbl_dma,
+ (unsigned long long)trans_pcie->aligned_ict_tbl_dma,
+ (int)(trans_pcie->aligned_ict_tbl_dma -
+ trans_pcie->ict_tbl_dma));
+
+ trans_pcie->ict_tbl = trans_pcie->ict_tbl_vir +
+ (trans_pcie->aligned_ict_tbl_dma -
+ trans_pcie->ict_tbl_dma);
+
+ IWL_DEBUG_ISR(trans, "ict vir addr %p vir aligned %p diff %d\n",
+ trans_pcie->ict_tbl, trans_pcie->ict_tbl_vir,
+ (int)(trans_pcie->aligned_ict_tbl_dma -
+ trans_pcie->ict_tbl_dma));
+
+ /* reset table and index to all 0 */
+ memset(trans_pcie->ict_tbl_vir, 0,
+ (sizeof(u32) * ICT_COUNT) + PAGE_SIZE);
+ trans_pcie->ict_index = 0;
+
+ /* add periodic RX interrupt */
+ trans_pcie->inta_mask |= CSR_INT_BIT_RX_PERIODIC;
+ return 0;
+}
+
+/* Device is going up inform it about using ICT interrupt table,
+ * also we need to tell the driver to start using ICT interrupt.
+ */
+int iwl_reset_ict(struct iwl_trans *trans)
+{
+ u32 val;
+ unsigned long flags;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (!trans_pcie->ict_tbl_vir)
+ return 0;
+
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+ iwl_disable_interrupts(trans);
+
+ memset(&trans_pcie->ict_tbl[0], 0, sizeof(u32) * ICT_COUNT);
+
+ val = trans_pcie->aligned_ict_tbl_dma >> PAGE_SHIFT;
+
+ val |= CSR_DRAM_INT_TBL_ENABLE;
+ val |= CSR_DRAM_INIT_TBL_WRAP_CHECK;
+
+ IWL_DEBUG_ISR(trans, "CSR_DRAM_INT_TBL_REG =0x%X "
+ "aligned dma address %Lx\n",
+ val,
+ (unsigned long long)trans_pcie->aligned_ict_tbl_dma);
+
+ iwl_write32(bus(trans), CSR_DRAM_INT_TBL_REG, val);
+ trans_pcie->use_ict = true;
+ trans_pcie->ict_index = 0;
+ iwl_write32(bus(trans), CSR_INT, trans_pcie->inta_mask);
+ iwl_enable_interrupts(trans);
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ return 0;
+}
+
+/* Device is going down disable ict interrupt usage */
+void iwl_disable_ict(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ unsigned long flags;
+
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+ trans_pcie->use_ict = false;
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+}
+
+static irqreturn_t iwl_isr(int irq, void *data)
+{
+ struct iwl_trans *trans = data;
+ struct iwl_trans_pcie *trans_pcie;
+ u32 inta, inta_mask;
+ unsigned long flags;
+#ifdef CONFIG_IWLWIFI_DEBUG
+ u32 inta_fh;
+#endif
+ if (!trans)
+ return IRQ_NONE;
+
+ trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+
+ /* Disable (but don't clear!) interrupts here to avoid
+ * back-to-back ISRs and sporadic interrupts from our NIC.
+ * If we have something to service, the tasklet will re-enable ints.
+ * If we *don't* have something, we'll re-enable before leaving here. */
+ inta_mask = iwl_read32(bus(trans), CSR_INT_MASK); /* just for debug */
+ iwl_write32(bus(trans), CSR_INT_MASK, 0x00000000);
+
+ /* Discover which interrupts are active/pending */
+ inta = iwl_read32(bus(trans), CSR_INT);
+
+ /* Ignore interrupt if there's nothing in NIC to service.
+ * This may be due to IRQ shared with another device,
+ * or due to sporadic interrupts thrown from our NIC. */
+ if (!inta) {
+ IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n");
+ goto none;
+ }
+
+ if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
+ /* Hardware disappeared. It might have already raised
+ * an interrupt */
+ IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
+ goto unplugged;
+ }
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (iwl_get_debug_level(trans->shrd) & (IWL_DL_ISR)) {
+ inta_fh = iwl_read32(bus(trans), CSR_FH_INT_STATUS);
+ IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x, "
+ "fh 0x%08x\n", inta, inta_mask, inta_fh);
+ }
+#endif
+
+ trans_pcie->inta |= inta;
+ /* iwl_irq_tasklet() will service interrupts and re-enable them */
+ if (likely(inta))
+ tasklet_schedule(&trans_pcie->irq_tasklet);
+ else if (test_bit(STATUS_INT_ENABLED, &trans->shrd->status) &&
+ !trans_pcie->inta)
+ iwl_enable_interrupts(trans);
+
+ unplugged:
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+ return IRQ_HANDLED;
+
+ none:
+ /* re-enable interrupts here since we don't have anything to service. */
+ /* only Re-enable if disabled by irq and no schedules tasklet. */
+ if (test_bit(STATUS_INT_ENABLED, &trans->shrd->status) &&
+ !trans_pcie->inta)
+ iwl_enable_interrupts(trans);
+
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+ return IRQ_NONE;
+}
+
+/* interrupt handler using ict table, with this interrupt driver will
+ * stop using INTA register to get device's interrupt, reading this register
+ * is expensive, device will write interrupts in ICT dram table, increment
+ * index then will fire interrupt to driver, driver will OR all ICT table
+ * entries from current index up to table entry with 0 value. the result is
+ * the interrupt we need to service, driver will set the entries back to 0 and
+ * set index.
+ */
+irqreturn_t iwl_isr_ict(int irq, void *data)
+{
+ struct iwl_trans *trans = data;
+ struct iwl_trans_pcie *trans_pcie;
+ u32 inta, inta_mask;
+ u32 val = 0;
+ unsigned long flags;
+
+ if (!trans)
+ return IRQ_NONE;
+
+ trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ /* dram interrupt table not set yet,
+ * use legacy interrupt.
+ */
+ if (!trans_pcie->use_ict)
+ return iwl_isr(irq, data);
+
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+
+ /* Disable (but don't clear!) interrupts here to avoid
+ * back-to-back ISRs and sporadic interrupts from our NIC.
+ * If we have something to service, the tasklet will re-enable ints.
+ * If we *don't* have something, we'll re-enable before leaving here.
+ */
+ inta_mask = iwl_read32(bus(trans), CSR_INT_MASK); /* just for debug */
+ iwl_write32(bus(trans), CSR_INT_MASK, 0x00000000);
+
+
+ /* Ignore interrupt if there's nothing in NIC to service.
+ * This may be due to IRQ shared with another device,
+ * or due to sporadic interrupts thrown from our NIC. */
+ if (!trans_pcie->ict_tbl[trans_pcie->ict_index]) {
+ IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n");
+ goto none;
+ }
+
+ /* read all entries that not 0 start with ict_index */
+ while (trans_pcie->ict_tbl[trans_pcie->ict_index]) {
+
+ val |= le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
+ IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n",
+ trans_pcie->ict_index,
+ le32_to_cpu(
+ trans_pcie->ict_tbl[trans_pcie->ict_index]));
+ trans_pcie->ict_tbl[trans_pcie->ict_index] = 0;
+ trans_pcie->ict_index =
+ iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT);
+
+ }
+
+ /* We should not get this value, just ignore it. */
+ if (val == 0xffffffff)
+ val = 0;
+
+ /*
+ * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit
+ * (bit 15 before shifting it to 31) to clear when using interrupt
+ * coalescing. fortunately, bits 18 and 19 stay set when this happens
+ * so we use them to decide on the real state of the Rx bit.
+ * In order words, bit 15 is set if bit 18 or bit 19 are set.
+ */
+ if (val & 0xC0000)
+ val |= 0x8000;
+
+ inta = (0xff & val) | ((0xff00 & val) << 16);
+ IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
+ inta, inta_mask, val);
+
+ inta &= trans_pcie->inta_mask;
+ trans_pcie->inta |= inta;
+
+ /* iwl_irq_tasklet() will service interrupts and re-enable them */
+ if (likely(inta))
+ tasklet_schedule(&trans_pcie->irq_tasklet);
+ else if (test_bit(STATUS_INT_ENABLED, &trans->shrd->status) &&
+ !trans_pcie->inta) {
+ /* Allow interrupt if was disabled by this handler and
+ * no tasklet was schedules, We should not enable interrupt,
+ * tasklet will enable it.
+ */
+ iwl_enable_interrupts(trans);
+ }
+
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+ return IRQ_HANDLED;
+
+ none:
+ /* re-enable interrupts here since we don't have anything to service.
+ * only Re-enable if disabled by irq.
+ */
+ if (test_bit(STATUS_INT_ENABLED, &trans->shrd->status) &&
+ !trans_pcie->inta)
+ iwl_enable_interrupts(trans);
+
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+ return IRQ_NONE;
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
new file mode 100644
index 000000000000..4a0c95302a7e
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
@@ -0,0 +1,1141 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#include "iwl-debug.h"
+#include "iwl-csr.h"
+#include "iwl-prph.h"
+#include "iwl-io.h"
+#include "iwl-agn-hw.h"
+#include "iwl-trans-pcie-int.h"
+
+#define IWL_TX_CRC_SIZE 4
+#define IWL_TX_DELIMITER_SIZE 4
+
+/**
+ * iwl_trans_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
+ */
+void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq,
+ u16 byte_cnt)
+{
+ struct iwlagn_scd_bc_tbl *scd_bc_tbl;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ int write_ptr = txq->q.write_ptr;
+ int txq_id = txq->q.id;
+ u8 sec_ctl = 0;
+ u8 sta_id = 0;
+ u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
+ __le16 bc_ent;
+ struct iwl_tx_cmd *tx_cmd =
+ (struct iwl_tx_cmd *) txq->cmd[txq->q.write_ptr]->payload;
+
+ scd_bc_tbl = trans_pcie->scd_bc_tbls.addr;
+
+ WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX);
+
+ sta_id = tx_cmd->sta_id;
+ sec_ctl = tx_cmd->sec_ctl;
+
+ switch (sec_ctl & TX_CMD_SEC_MSK) {
+ case TX_CMD_SEC_CCM:
+ len += CCMP_MIC_LEN;
+ break;
+ case TX_CMD_SEC_TKIP:
+ len += TKIP_ICV_LEN;
+ break;
+ case TX_CMD_SEC_WEP:
+ len += WEP_IV_LEN + WEP_ICV_LEN;
+ break;
+ }
+
+ bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12));
+
+ scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
+
+ if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
+ scd_bc_tbl[txq_id].
+ tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
+}
+
+/**
+ * iwl_txq_update_write_ptr - Send new write index to hardware
+ */
+void iwl_txq_update_write_ptr(struct iwl_trans *trans, struct iwl_tx_queue *txq)
+{
+ u32 reg = 0;
+ int txq_id = txq->q.id;
+
+ if (txq->need_update == 0)
+ return;
+
+ if (hw_params(trans).shadow_reg_enable) {
+ /* shadow register enabled */
+ iwl_write32(bus(trans), HBUS_TARG_WRPTR,
+ txq->q.write_ptr | (txq_id << 8));
+ } else {
+ /* if we're trying to save power */
+ if (test_bit(STATUS_POWER_PMI, &trans->shrd->status)) {
+ /* wake up nic if it's powered down ...
+ * uCode will wake up, and interrupt us again, so next
+ * time we'll skip this part. */
+ reg = iwl_read32(bus(trans), CSR_UCODE_DRV_GP1);
+
+ if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+ IWL_DEBUG_INFO(trans,
+ "Tx queue %d requesting wakeup,"
+ " GP1 = 0x%x\n", txq_id, reg);
+ iwl_set_bit(bus(trans), CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ return;
+ }
+
+ iwl_write_direct32(bus(trans), HBUS_TARG_WRPTR,
+ txq->q.write_ptr | (txq_id << 8));
+
+ /*
+ * else not in power-save mode,
+ * uCode will never sleep when we're
+ * trying to tx (during RFKILL, we're not trying to tx).
+ */
+ } else
+ iwl_write32(bus(trans), HBUS_TARG_WRPTR,
+ txq->q.write_ptr | (txq_id << 8));
+ }
+ txq->need_update = 0;
+}
+
+static inline dma_addr_t iwl_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
+{
+ struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+
+ dma_addr_t addr = get_unaligned_le32(&tb->lo);
+ if (sizeof(dma_addr_t) > sizeof(u32))
+ addr |=
+ ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16;
+
+ return addr;
+}
+
+static inline u16 iwl_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
+{
+ struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+
+ return le16_to_cpu(tb->hi_n_len) >> 4;
+}
+
+static inline void iwl_tfd_set_tb(struct iwl_tfd *tfd, u8 idx,
+ dma_addr_t addr, u16 len)
+{
+ struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+ u16 hi_n_len = len << 4;
+
+ put_unaligned_le32(addr, &tb->lo);
+ if (sizeof(dma_addr_t) > sizeof(u32))
+ hi_n_len |= ((addr >> 16) >> 16) & 0xF;
+
+ tb->hi_n_len = cpu_to_le16(hi_n_len);
+
+ tfd->num_tbs = idx + 1;
+}
+
+static inline u8 iwl_tfd_get_num_tbs(struct iwl_tfd *tfd)
+{
+ return tfd->num_tbs & 0x1f;
+}
+
+static void iwlagn_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta,
+ struct iwl_tfd *tfd, enum dma_data_direction dma_dir)
+{
+ int i;
+ int num_tbs;
+
+ /* Sanity check on number of chunks */
+ num_tbs = iwl_tfd_get_num_tbs(tfd);
+
+ if (num_tbs >= IWL_NUM_OF_TBS) {
+ IWL_ERR(trans, "Too many chunks: %i\n", num_tbs);
+ /* @todo issue fatal error, it is quite serious situation */
+ return;
+ }
+
+ /* Unmap tx_cmd */
+ if (num_tbs)
+ dma_unmap_single(bus(trans)->dev,
+ dma_unmap_addr(meta, mapping),
+ dma_unmap_len(meta, len),
+ DMA_BIDIRECTIONAL);
+
+ /* Unmap chunks, if any. */
+ for (i = 1; i < num_tbs; i++)
+ dma_unmap_single(bus(trans)->dev, iwl_tfd_tb_get_addr(tfd, i),
+ iwl_tfd_tb_get_len(tfd, i), dma_dir);
+}
+
+/**
+ * iwlagn_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr]
+ * @trans - transport private data
+ * @txq - tx queue
+ * @index - the index of the TFD to be freed
+ *@dma_dir - the direction of the DMA mapping
+ *
+ * Does NOT advance any TFD circular buffer read/write indexes
+ * Does NOT free the TFD itself (which is within circular buffer)
+ */
+void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
+ int index, enum dma_data_direction dma_dir)
+{
+ struct iwl_tfd *tfd_tmp = txq->tfds;
+
+ iwlagn_unmap_tfd(trans, &txq->meta[index], &tfd_tmp[index], dma_dir);
+
+ /* free SKB */
+ if (txq->skbs) {
+ struct sk_buff *skb;
+
+ skb = txq->skbs[index];
+
+ /* Can be called from irqs-disabled context
+ * If skb is not NULL, it means that the whole queue is being
+ * freed and that the queue is not empty - free the skb
+ */
+ if (skb) {
+ iwl_free_skb(priv(trans), skb);
+ txq->skbs[index] = NULL;
+ }
+ }
+}
+
+int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq,
+ dma_addr_t addr, u16 len,
+ u8 reset)
+{
+ struct iwl_queue *q;
+ struct iwl_tfd *tfd, *tfd_tmp;
+ u32 num_tbs;
+
+ q = &txq->q;
+ tfd_tmp = txq->tfds;
+ tfd = &tfd_tmp[q->write_ptr];
+
+ if (reset)
+ memset(tfd, 0, sizeof(*tfd));
+
+ num_tbs = iwl_tfd_get_num_tbs(tfd);
+
+ /* Each TFD can point to a maximum 20 Tx buffers */
+ if (num_tbs >= IWL_NUM_OF_TBS) {
+ IWL_ERR(trans, "Error can not send more than %d chunks\n",
+ IWL_NUM_OF_TBS);
+ return -EINVAL;
+ }
+
+ if (WARN_ON(addr & ~DMA_BIT_MASK(36)))
+ return -EINVAL;
+
+ if (unlikely(addr & ~IWL_TX_DMA_MASK))
+ IWL_ERR(trans, "Unaligned address = %llx\n",
+ (unsigned long long)addr);
+
+ iwl_tfd_set_tb(tfd, num_tbs, addr, len);
+
+ return 0;
+}
+
+/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
+ * DMA services
+ *
+ * Theory of operation
+ *
+ * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer
+ * of buffer descriptors, each of which points to one or more data buffers for
+ * the device to read from or fill. Driver and device exchange status of each
+ * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty
+ * entries in each circular buffer, to protect against confusing empty and full
+ * queue states.
+ *
+ * The device reads or writes the data in the queues via the device's several
+ * DMA/FIFO channels. Each queue is mapped to a single DMA channel.
+ *
+ * For Tx queue, there are low mark and high mark limits. If, after queuing
+ * the packet for Tx, free space become < low mark, Tx queue stopped. When
+ * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
+ * Tx queue resumed.
+ *
+ ***************************************************/
+
+int iwl_queue_space(const struct iwl_queue *q)
+{
+ int s = q->read_ptr - q->write_ptr;
+
+ if (q->read_ptr > q->write_ptr)
+ s -= q->n_bd;
+
+ if (s <= 0)
+ s += q->n_window;
+ /* keep some reserve to not confuse empty and full situations */
+ s -= 2;
+ if (s < 0)
+ s = 0;
+ return s;
+}
+
+/**
+ * iwl_queue_init - Initialize queue's high/low-water and read/write indexes
+ */
+int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id)
+{
+ q->n_bd = count;
+ q->n_window = slots_num;
+ q->id = id;
+
+ /* count must be power-of-two size, otherwise iwl_queue_inc_wrap
+ * and iwl_queue_dec_wrap are broken. */
+ if (WARN_ON(!is_power_of_2(count)))
+ return -EINVAL;
+
+ /* slots_num must be power-of-two size, otherwise
+ * get_cmd_index is broken. */
+ if (WARN_ON(!is_power_of_2(slots_num)))
+ return -EINVAL;
+
+ q->low_mark = q->n_window / 4;
+ if (q->low_mark < 4)
+ q->low_mark = 4;
+
+ q->high_mark = q->n_window / 8;
+ if (q->high_mark < 2)
+ q->high_mark = 2;
+
+ q->write_ptr = q->read_ptr = 0;
+
+ return 0;
+}
+
+static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->scd_bc_tbls.addr;
+ int txq_id = txq->q.id;
+ int read_ptr = txq->q.read_ptr;
+ u8 sta_id = 0;
+ __le16 bc_ent;
+ struct iwl_tx_cmd *tx_cmd =
+ (struct iwl_tx_cmd *) txq->cmd[txq->q.read_ptr]->payload;
+
+ WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
+
+ if (txq_id != trans->shrd->cmd_queue)
+ sta_id = tx_cmd->sta_id;
+
+ bc_ent = cpu_to_le16(1 | (sta_id << 12));
+ scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
+
+ if (read_ptr < TFD_QUEUE_SIZE_BC_DUP)
+ scd_bc_tbl[txq_id].
+ tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
+}
+
+static int iwlagn_tx_queue_set_q2ratid(struct iwl_trans *trans, u16 ra_tid,
+ u16 txq_id)
+{
+ u32 tbl_dw_addr;
+ u32 tbl_dw;
+ u16 scd_q2ratid;
+
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK;
+
+ tbl_dw_addr = trans_pcie->scd_base_addr +
+ SCD_TRANS_TBL_OFFSET_QUEUE(txq_id);
+
+ tbl_dw = iwl_read_targ_mem(bus(trans), tbl_dw_addr);
+
+ if (txq_id & 0x1)
+ tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
+ else
+ tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
+
+ iwl_write_targ_mem(bus(trans), tbl_dw_addr, tbl_dw);
+
+ return 0;
+}
+
+static void iwlagn_tx_queue_stop_scheduler(struct iwl_trans *trans, u16 txq_id)
+{
+ /* Simply stop the queue, but don't change any configuration;
+ * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
+ iwl_write_prph(bus(trans),
+ SCD_QUEUE_STATUS_BITS(txq_id),
+ (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
+ (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
+}
+
+void iwl_trans_set_wr_ptrs(struct iwl_trans *trans,
+ int txq_id, u32 index)
+{
+ iwl_write_direct32(bus(trans), HBUS_TARG_WRPTR,
+ (index & 0xff) | (txq_id << 8));
+ iwl_write_prph(bus(trans), SCD_QUEUE_RDPTR(txq_id), index);
+}
+
+void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq,
+ int tx_fifo_id, int scd_retry)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int txq_id = txq->q.id;
+ int active =
+ test_bit(txq_id, &trans_pcie->txq_ctx_active_msk) ? 1 : 0;
+
+ iwl_write_prph(bus(trans), SCD_QUEUE_STATUS_BITS(txq_id),
+ (active << SCD_QUEUE_STTS_REG_POS_ACTIVE) |
+ (tx_fifo_id << SCD_QUEUE_STTS_REG_POS_TXF) |
+ (1 << SCD_QUEUE_STTS_REG_POS_WSL) |
+ SCD_QUEUE_STTS_REG_MSK);
+
+ txq->sched_retry = scd_retry;
+
+ IWL_DEBUG_INFO(trans, "%s %s Queue %d on FIFO %d\n",
+ active ? "Activate" : "Deactivate",
+ scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id);
+}
+
+static inline int get_fifo_from_tid(struct iwl_trans_pcie *trans_pcie,
+ u8 ctx, u16 tid)
+{
+ const u8 *ac_to_fifo = trans_pcie->ac_to_fifo[ctx];
+ if (likely(tid < ARRAY_SIZE(tid_to_ac)))
+ return ac_to_fifo[tid_to_ac[tid]];
+
+ /* no support for TIDs 8-15 yet */
+ return -EINVAL;
+}
+
+void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx, int sta_id,
+ int tid, int frame_limit)
+{
+ int tx_fifo, txq_id, ssn_idx;
+ u16 ra_tid;
+ unsigned long flags;
+ struct iwl_tid_data *tid_data;
+
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (WARN_ON(sta_id == IWL_INVALID_STATION))
+ return;
+ if (WARN_ON(tid >= IWL_MAX_TID_COUNT))
+ return;
+
+ tx_fifo = get_fifo_from_tid(trans_pcie, ctx, tid);
+ if (WARN_ON(tx_fifo < 0)) {
+ IWL_ERR(trans, "txq_agg_setup, bad fifo: %d\n", tx_fifo);
+ return;
+ }
+
+ spin_lock_irqsave(&trans->shrd->sta_lock, flags);
+ tid_data = &trans->shrd->tid_data[sta_id][tid];
+ ssn_idx = SEQ_TO_SN(tid_data->seq_number);
+ txq_id = tid_data->agg.txq_id;
+ spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
+
+ ra_tid = BUILD_RAxTID(sta_id, tid);
+
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+
+ /* Stop this Tx queue before configuring it */
+ iwlagn_tx_queue_stop_scheduler(trans, txq_id);
+
+ /* Map receiver-address / traffic-ID to this queue */
+ iwlagn_tx_queue_set_q2ratid(trans, ra_tid, txq_id);
+
+ /* Set this queue as a chain-building queue */
+ iwl_set_bits_prph(bus(trans), SCD_QUEUECHAIN_SEL, (1<<txq_id));
+
+ /* enable aggregations for the queue */
+ iwl_set_bits_prph(bus(trans), SCD_AGGR_SEL, (1<<txq_id));
+
+ /* Place first TFD at index corresponding to start sequence number.
+ * Assumes that ssn_idx is valid (!= 0xFFF) */
+ trans_pcie->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
+ trans_pcie->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
+ iwl_trans_set_wr_ptrs(trans, txq_id, ssn_idx);
+
+ /* Set up Tx window size and frame limit for this queue */
+ iwl_write_targ_mem(bus(trans), trans_pcie->scd_base_addr +
+ SCD_CONTEXT_QUEUE_OFFSET(txq_id) +
+ sizeof(u32),
+ ((frame_limit <<
+ SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
+ SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
+ ((frame_limit <<
+ SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
+ SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
+
+ iwl_set_bits_prph(bus(trans), SCD_INTERRUPT_MASK, (1 << txq_id));
+
+ /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
+ iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id],
+ tx_fifo, 1);
+
+ trans_pcie->txq[txq_id].sta_id = sta_id;
+ trans_pcie->txq[txq_id].tid = tid;
+
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+}
+
+/*
+ * Find first available (lowest unused) Tx Queue, mark it "active".
+ * Called only when finding queue for aggregation.
+ * Should never return anything < 7, because they should already
+ * be in use as EDCA AC (0-3), Command (4), reserved (5, 6)
+ */
+static int iwlagn_txq_ctx_activate_free(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int txq_id;
+
+ for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++)
+ if (!test_and_set_bit(txq_id,
+ &trans_pcie->txq_ctx_active_msk))
+ return txq_id;
+ return -1;
+}
+
+int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx, int sta_id,
+ int tid, u16 *ssn)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tid_data *tid_data;
+ unsigned long flags;
+ int txq_id;
+
+ txq_id = iwlagn_txq_ctx_activate_free(trans);
+ if (txq_id == -1) {
+ IWL_ERR(trans, "No free aggregation queue available\n");
+ return -ENXIO;
+ }
+
+ spin_lock_irqsave(&trans->shrd->sta_lock, flags);
+ tid_data = &trans->shrd->tid_data[sta_id][tid];
+ *ssn = SEQ_TO_SN(tid_data->seq_number);
+ tid_data->agg.txq_id = txq_id;
+ iwl_set_swq_id(&trans_pcie->txq[txq_id], get_ac_from_tid(tid), txq_id);
+
+ tid_data = &trans->shrd->tid_data[sta_id][tid];
+ if (tid_data->tfds_in_queue == 0) {
+ IWL_DEBUG_HT(trans, "HW queue is empty\n");
+ tid_data->agg.state = IWL_AGG_ON;
+ iwl_start_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid);
+ } else {
+ IWL_DEBUG_HT(trans, "HW queue is NOT empty: %d packets in HW"
+ "queue\n", tid_data->tfds_in_queue);
+ tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
+ }
+ spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
+
+ return 0;
+}
+
+void iwl_trans_pcie_txq_agg_disable(struct iwl_trans *trans, int txq_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ iwlagn_tx_queue_stop_scheduler(trans, txq_id);
+
+ iwl_clear_bits_prph(bus(trans), SCD_AGGR_SEL, (1 << txq_id));
+
+ trans_pcie->txq[txq_id].q.read_ptr = 0;
+ trans_pcie->txq[txq_id].q.write_ptr = 0;
+ /* supposes that ssn_idx is valid (!= 0xFFF) */
+ iwl_trans_set_wr_ptrs(trans, txq_id, 0);
+
+ iwl_clear_bits_prph(bus(trans), SCD_INTERRUPT_MASK, (1 << txq_id));
+ iwl_txq_ctx_deactivate(trans_pcie, txq_id);
+ iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], 0, 0);
+}
+
+int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx, int sta_id,
+ int tid)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ unsigned long flags;
+ int read_ptr, write_ptr;
+ struct iwl_tid_data *tid_data;
+ int txq_id;
+
+ spin_lock_irqsave(&trans->shrd->sta_lock, flags);
+
+ tid_data = &trans->shrd->tid_data[sta_id][tid];
+ txq_id = tid_data->agg.txq_id;
+
+ if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
+ (IWLAGN_FIRST_AMPDU_QUEUE +
+ hw_params(trans).num_ampdu_queues <= txq_id)) {
+ IWL_ERR(trans,
+ "queue number out of range: %d, must be %d to %d\n",
+ txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
+ IWLAGN_FIRST_AMPDU_QUEUE +
+ hw_params(trans).num_ampdu_queues - 1);
+ spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
+ return -EINVAL;
+ }
+
+ switch (trans->shrd->tid_data[sta_id][tid].agg.state) {
+ case IWL_EMPTYING_HW_QUEUE_ADDBA:
+ /*
+ * This can happen if the peer stops aggregation
+ * again before we've had a chance to drain the
+ * queue we selected previously, i.e. before the
+ * session was really started completely.
+ */
+ IWL_DEBUG_HT(trans, "AGG stop before setup done\n");
+ goto turn_off;
+ case IWL_AGG_ON:
+ break;
+ default:
+ IWL_WARN(trans, "Stopping AGG while state not ON "
+ "or starting for %d on %d (%d)\n", sta_id, tid,
+ trans->shrd->tid_data[sta_id][tid].agg.state);
+ spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
+ return 0;
+ }
+
+ write_ptr = trans_pcie->txq[txq_id].q.write_ptr;
+ read_ptr = trans_pcie->txq[txq_id].q.read_ptr;
+
+ /* The queue is not empty */
+ if (write_ptr != read_ptr) {
+ IWL_DEBUG_HT(trans, "Stopping a non empty AGG HW QUEUE\n");
+ trans->shrd->tid_data[sta_id][tid].agg.state =
+ IWL_EMPTYING_HW_QUEUE_DELBA;
+ spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
+ return 0;
+ }
+
+ IWL_DEBUG_HT(trans, "HW queue is empty\n");
+turn_off:
+ trans->shrd->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF;
+
+ /* do not restore/save irqs */
+ spin_unlock(&trans->shrd->sta_lock);
+ spin_lock(&trans->shrd->lock);
+
+ iwl_trans_pcie_txq_agg_disable(trans, txq_id);
+
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ iwl_stop_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid);
+
+ return 0;
+}
+
+/*************** HOST COMMAND QUEUE FUNCTIONS *****/
+
+/**
+ * iwl_enqueue_hcmd - enqueue a uCode command
+ * @priv: device private data point
+ * @cmd: a point to the ucode command structure
+ *
+ * The function returns < 0 values to indicate the operation is
+ * failed. On success, it turns the index (> 0) of command in the
+ * command queue.
+ */
+static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tx_queue *txq = &trans_pcie->txq[trans->shrd->cmd_queue];
+ struct iwl_queue *q = &txq->q;
+ struct iwl_device_cmd *out_cmd;
+ struct iwl_cmd_meta *out_meta;
+ dma_addr_t phys_addr;
+ unsigned long flags;
+ u32 idx;
+ u16 copy_size, cmd_size;
+ bool is_ct_kill = false;
+ bool had_nocopy = false;
+ int i;
+ u8 *cmd_dest;
+#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
+ const void *trace_bufs[IWL_MAX_CMD_TFDS + 1] = {};
+ int trace_lens[IWL_MAX_CMD_TFDS + 1] = {};
+ int trace_idx;
+#endif
+
+ if (test_bit(STATUS_FW_ERROR, &trans->shrd->status)) {
+ IWL_WARN(trans, "fw recovery, no hcmd send\n");
+ return -EIO;
+ }
+
+ if ((trans->shrd->ucode_owner == IWL_OWNERSHIP_TM) &&
+ !(cmd->flags & CMD_ON_DEMAND)) {
+ IWL_DEBUG_HC(trans, "tm own the uCode, no regular hcmd send\n");
+ return -EIO;
+ }
+
+ copy_size = sizeof(out_cmd->hdr);
+ cmd_size = sizeof(out_cmd->hdr);
+
+ /* need one for the header if the first is NOCOPY */
+ BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1);
+
+ for (i = 0; i < IWL_MAX_CMD_TFDS; i++) {
+ if (!cmd->len[i])
+ continue;
+ if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) {
+ had_nocopy = true;
+ } else {
+ /* NOCOPY must not be followed by normal! */
+ if (WARN_ON(had_nocopy))
+ return -EINVAL;
+ copy_size += cmd->len[i];
+ }
+ cmd_size += cmd->len[i];
+ }
+
+ /*
+ * If any of the command structures end up being larger than
+ * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically
+ * allocated into separate TFDs, then we will need to
+ * increase the size of the buffers.
+ */
+ if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE))
+ return -EINVAL;
+
+ if (iwl_is_rfkill(trans->shrd) || iwl_is_ctkill(trans->shrd)) {
+ IWL_WARN(trans, "Not sending command - %s KILL\n",
+ iwl_is_rfkill(trans->shrd) ? "RF" : "CT");
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&trans->hcmd_lock, flags);
+
+ if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
+ spin_unlock_irqrestore(&trans->hcmd_lock, flags);
+
+ IWL_ERR(trans, "No space in command queue\n");
+ is_ct_kill = iwl_check_for_ct_kill(priv(trans));
+ if (!is_ct_kill) {
+ IWL_ERR(trans, "Restarting adapter queue is full\n");
+ iwlagn_fw_error(priv(trans), false);
+ }
+ return -ENOSPC;
+ }
+
+ idx = get_cmd_index(q, q->write_ptr);
+ out_cmd = txq->cmd[idx];
+ out_meta = &txq->meta[idx];
+
+ memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */
+ if (cmd->flags & CMD_WANT_SKB)
+ out_meta->source = cmd;
+
+ /* set up the header */
+
+ out_cmd->hdr.cmd = cmd->id;
+ out_cmd->hdr.flags = 0;
+ out_cmd->hdr.sequence =
+ cpu_to_le16(QUEUE_TO_SEQ(trans->shrd->cmd_queue) |
+ INDEX_TO_SEQ(q->write_ptr));
+
+ /* and copy the data that needs to be copied */
+
+ cmd_dest = out_cmd->payload;
+ for (i = 0; i < IWL_MAX_CMD_TFDS; i++) {
+ if (!cmd->len[i])
+ continue;
+ if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)
+ break;
+ memcpy(cmd_dest, cmd->data[i], cmd->len[i]);
+ cmd_dest += cmd->len[i];
+ }
+
+ IWL_DEBUG_HC(trans, "Sending command %s (#%x), seq: 0x%04X, "
+ "%d bytes at %d[%d]:%d\n",
+ get_cmd_string(out_cmd->hdr.cmd),
+ out_cmd->hdr.cmd,
+ le16_to_cpu(out_cmd->hdr.sequence), cmd_size,
+ q->write_ptr, idx, trans->shrd->cmd_queue);
+
+ phys_addr = dma_map_single(bus(trans)->dev, &out_cmd->hdr, copy_size,
+ DMA_BIDIRECTIONAL);
+ if (unlikely(dma_mapping_error(bus(trans)->dev, phys_addr))) {
+ idx = -ENOMEM;
+ goto out;
+ }
+
+ dma_unmap_addr_set(out_meta, mapping, phys_addr);
+ dma_unmap_len_set(out_meta, len, copy_size);
+
+ iwlagn_txq_attach_buf_to_tfd(trans, txq,
+ phys_addr, copy_size, 1);
+#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
+ trace_bufs[0] = &out_cmd->hdr;
+ trace_lens[0] = copy_size;
+ trace_idx = 1;
+#endif
+
+ for (i = 0; i < IWL_MAX_CMD_TFDS; i++) {
+ if (!cmd->len[i])
+ continue;
+ if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY))
+ continue;
+ phys_addr = dma_map_single(bus(trans)->dev,
+ (void *)cmd->data[i],
+ cmd->len[i], DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(bus(trans)->dev, phys_addr)) {
+ iwlagn_unmap_tfd(trans, out_meta,
+ &txq->tfds[q->write_ptr],
+ DMA_BIDIRECTIONAL);
+ idx = -ENOMEM;
+ goto out;
+ }
+
+ iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr,
+ cmd->len[i], 0);
+#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
+ trace_bufs[trace_idx] = cmd->data[i];
+ trace_lens[trace_idx] = cmd->len[i];
+ trace_idx++;
+#endif
+ }
+
+ out_meta->flags = cmd->flags;
+
+ txq->need_update = 1;
+
+ /* check that tracing gets all possible blocks */
+ BUILD_BUG_ON(IWL_MAX_CMD_TFDS + 1 != 3);
+#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
+ trace_iwlwifi_dev_hcmd(priv(trans), cmd->flags,
+ trace_bufs[0], trace_lens[0],
+ trace_bufs[1], trace_lens[1],
+ trace_bufs[2], trace_lens[2]);
+#endif
+
+ /* Increment and update queue's write index */
+ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
+ iwl_txq_update_write_ptr(trans, txq);
+
+ out:
+ spin_unlock_irqrestore(&trans->hcmd_lock, flags);
+ return idx;
+}
+
+/**
+ * iwl_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd
+ *
+ * When FW advances 'R' index, all entries between old and new 'R' index
+ * need to be reclaimed. As result, some free space forms. If there is
+ * enough free space (> low mark), wake the stack that feeds us.
+ */
+static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id,
+ int idx)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id];
+ struct iwl_queue *q = &txq->q;
+ int nfreed = 0;
+
+ if ((idx >= q->n_bd) || (iwl_queue_used(q, idx) == 0)) {
+ IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), "
+ "index %d is out of range [0-%d] %d %d.\n", __func__,
+ txq_id, idx, q->n_bd, q->write_ptr, q->read_ptr);
+ return;
+ }
+
+ 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)) {
+
+ if (nfreed++ > 0) {
+ IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", idx,
+ q->write_ptr, q->read_ptr);
+ iwlagn_fw_error(priv(trans), false);
+ }
+
+ }
+}
+
+/**
+ * iwl_tx_cmd_complete - Pull unused buffers off the queue and reclaim them
+ * @rxb: Rx buffer to reclaim
+ * @handler_status: return value of the handler of the command
+ * (put in setup_rx_handlers)
+ *
+ * If an Rx buffer has an async callback associated with it the callback
+ * will be executed. The attached skb (if present) will only be freed
+ * if the callback returns 1
+ */
+void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb,
+ int handler_status)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+ int txq_id = SEQ_TO_QUEUE(sequence);
+ int index = SEQ_TO_INDEX(sequence);
+ int cmd_index;
+ struct iwl_device_cmd *cmd;
+ struct iwl_cmd_meta *meta;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tx_queue *txq = &trans_pcie->txq[trans->shrd->cmd_queue];
+ unsigned long flags;
+
+ /* If a Tx command is being handled and it isn't in the actual
+ * command queue then there a command routing bug has been introduced
+ * in the queue management code. */
+ if (WARN(txq_id != trans->shrd->cmd_queue,
+ "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n",
+ txq_id, trans->shrd->cmd_queue, sequence,
+ trans_pcie->txq[trans->shrd->cmd_queue].q.read_ptr,
+ trans_pcie->txq[trans->shrd->cmd_queue].q.write_ptr)) {
+ iwl_print_hex_error(trans, pkt, 32);
+ return;
+ }
+
+ cmd_index = get_cmd_index(&txq->q, index);
+ cmd = txq->cmd[cmd_index];
+ meta = &txq->meta[cmd_index];
+
+ txq->time_stamp = jiffies;
+
+ iwlagn_unmap_tfd(trans, meta, &txq->tfds[index],
+ DMA_BIDIRECTIONAL);
+
+ /* Input error checking is done when commands are added to queue. */
+ if (meta->flags & CMD_WANT_SKB) {
+ meta->source->reply_page = (unsigned long)rxb_addr(rxb);
+ meta->source->handler_status = handler_status;
+ rxb->page = NULL;
+ }
+
+ spin_lock_irqsave(&trans->hcmd_lock, flags);
+
+ iwl_hcmd_queue_reclaim(trans, txq_id, index);
+
+ if (!(meta->flags & CMD_ASYNC)) {
+ if (!test_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status)) {
+ IWL_WARN(trans,
+ "HCMD_ACTIVE already clear for command %s\n",
+ get_cmd_string(cmd->hdr.cmd));
+ }
+ clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status);
+ IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
+ get_cmd_string(cmd->hdr.cmd));
+ wake_up(&trans->shrd->wait_command_queue);
+ }
+
+ meta->flags = 0;
+
+ spin_unlock_irqrestore(&trans->hcmd_lock, flags);
+}
+
+#define HOST_COMPLETE_TIMEOUT (2 * HZ)
+
+static int iwl_send_cmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
+{
+ int ret;
+
+ /* An asynchronous command can not expect an SKB to be set. */
+ if (WARN_ON(cmd->flags & CMD_WANT_SKB))
+ return -EINVAL;
+
+
+ if (test_bit(STATUS_EXIT_PENDING, &trans->shrd->status))
+ return -EBUSY;
+
+ ret = iwl_enqueue_hcmd(trans, cmd);
+ if (ret < 0) {
+ IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n",
+ get_cmd_string(cmd->id), ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int cmd_idx;
+ int ret;
+
+ lockdep_assert_held(&trans->shrd->mutex);
+
+ IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
+ get_cmd_string(cmd->id));
+
+ set_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status);
+ IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n",
+ get_cmd_string(cmd->id));
+
+ cmd_idx = iwl_enqueue_hcmd(trans, cmd);
+ if (cmd_idx < 0) {
+ ret = cmd_idx;
+ clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status);
+ IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n",
+ get_cmd_string(cmd->id), ret);
+ return ret;
+ }
+
+ ret = wait_event_timeout(trans->shrd->wait_command_queue,
+ !test_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status),
+ HOST_COMPLETE_TIMEOUT);
+ if (!ret) {
+ if (test_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status)) {
+ struct iwl_tx_queue *txq =
+ &trans_pcie->txq[trans->shrd->cmd_queue];
+ struct iwl_queue *q = &txq->q;
+
+ IWL_ERR(trans,
+ "Error sending %s: time out after %dms.\n",
+ get_cmd_string(cmd->id),
+ jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
+
+ IWL_ERR(trans,
+ "Current CMD queue read_ptr %d write_ptr %d\n",
+ q->read_ptr, q->write_ptr);
+
+ clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status);
+ IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command"
+ "%s\n", get_cmd_string(cmd->id));
+ ret = -ETIMEDOUT;
+ goto cancel;
+ }
+ }
+
+ if (test_bit(STATUS_RF_KILL_HW, &trans->shrd->status)) {
+ IWL_ERR(trans, "Command %s aborted: RF KILL Switch\n",
+ get_cmd_string(cmd->id));
+ ret = -ECANCELED;
+ goto fail;
+ }
+ if (test_bit(STATUS_FW_ERROR, &trans->shrd->status)) {
+ IWL_ERR(trans, "Command %s failed: FW Error\n",
+ get_cmd_string(cmd->id));
+ ret = -EIO;
+ goto fail;
+ }
+ if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) {
+ IWL_ERR(trans, "Error: Response NULL in '%s'\n",
+ get_cmd_string(cmd->id));
+ ret = -EIO;
+ goto cancel;
+ }
+
+ return 0;
+
+cancel:
+ if (cmd->flags & CMD_WANT_SKB) {
+ /*
+ * Cancel the CMD_WANT_SKB flag for the cmd in the
+ * TX cmd queue. Otherwise in case the cmd comes
+ * in later, it will possibly set an invalid
+ * address (cmd->meta.source).
+ */
+ trans_pcie->txq[trans->shrd->cmd_queue].meta[cmd_idx].flags &=
+ ~CMD_WANT_SKB;
+ }
+fail:
+ if (cmd->reply_page) {
+ iwl_free_pages(trans->shrd, cmd->reply_page);
+ cmd->reply_page = 0;
+ }
+
+ return ret;
+}
+
+int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
+{
+ if (cmd->flags & CMD_ASYNC)
+ return iwl_send_cmd_async(trans, cmd);
+
+ return iwl_send_cmd_sync(trans, cmd);
+}
+
+/* Frees buffers until index _not_ inclusive */
+int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index,
+ struct sk_buff_head *skbs)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id];
+ struct iwl_queue *q = &txq->q;
+ int last_to_free;
+ int freed = 0;
+
+ /* This function is not meant to release cmd queue*/
+ if (WARN_ON(txq_id == trans->shrd->cmd_queue))
+ return 0;
+
+ /*Since we free until index _not_ inclusive, the one before index is
+ * the last we will free. This one must be used */
+ last_to_free = iwl_queue_dec_wrap(index, q->n_bd);
+
+ if ((index >= q->n_bd) ||
+ (iwl_queue_used(q, last_to_free) == 0)) {
+ IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), "
+ "last_to_free %d is out of range [0-%d] %d %d.\n",
+ __func__, txq_id, last_to_free, q->n_bd,
+ q->write_ptr, q->read_ptr);
+ return 0;
+ }
+
+ IWL_DEBUG_TX_REPLY(trans, "reclaim: [%d, %d, %d]\n", txq_id,
+ q->read_ptr, index);
+
+ if (WARN_ON(!skb_queue_empty(skbs)))
+ return 0;
+
+ for (;
+ q->read_ptr != index;
+ q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+
+ if (WARN_ON_ONCE(txq->skbs[txq->q.read_ptr] == NULL))
+ continue;
+
+ __skb_queue_tail(skbs, txq->skbs[txq->q.read_ptr]);
+
+ txq->skbs[txq->q.read_ptr] = NULL;
+
+ iwlagn_txq_inval_byte_cnt_tbl(trans, txq);
+
+ iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr, DMA_TO_DEVICE);
+ freed++;
+ }
+ return freed;
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
new file mode 100644
index 000000000000..ce918980e977
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
@@ -0,0 +1,2003 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/interrupt.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/gfp.h>
+
+#include "iwl-trans.h"
+#include "iwl-trans-pcie-int.h"
+#include "iwl-csr.h"
+#include "iwl-prph.h"
+#include "iwl-shared.h"
+#include "iwl-eeprom.h"
+#include "iwl-agn-hw.h"
+
+static int iwl_trans_rx_alloc(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rx_queue *rxq = &trans_pcie->rxq;
+ struct device *dev = bus(trans)->dev;
+
+ memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq));
+
+ spin_lock_init(&rxq->lock);
+
+ if (WARN_ON(rxq->bd || rxq->rb_stts))
+ return -EINVAL;
+
+ /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */
+ rxq->bd = dma_alloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
+ &rxq->bd_dma, GFP_KERNEL);
+ if (!rxq->bd)
+ goto err_bd;
+ memset(rxq->bd, 0, sizeof(__le32) * RX_QUEUE_SIZE);
+
+ /*Allocate the driver's pointer to receive buffer status */
+ rxq->rb_stts = dma_alloc_coherent(dev, sizeof(*rxq->rb_stts),
+ &rxq->rb_stts_dma, GFP_KERNEL);
+ if (!rxq->rb_stts)
+ goto err_rb_stts;
+ memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
+
+ return 0;
+
+err_rb_stts:
+ dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
+ rxq->bd, rxq->bd_dma);
+ memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma));
+ rxq->bd = NULL;
+err_bd:
+ return -ENOMEM;
+}
+
+static void iwl_trans_rxq_free_rx_bufs(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rx_queue *rxq = &trans_pcie->rxq;
+ int i;
+
+ /* Fill the rx_used queue with _all_ of the Rx buffers */
+ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
+ /* In the reset function, these buffers may have been allocated
+ * to an SKB, so we need to unmap and free potential storage */
+ if (rxq->pool[i].page != NULL) {
+ dma_unmap_page(bus(trans)->dev, rxq->pool[i].page_dma,
+ PAGE_SIZE << hw_params(trans).rx_page_order,
+ DMA_FROM_DEVICE);
+ __free_pages(rxq->pool[i].page,
+ hw_params(trans).rx_page_order);
+ rxq->pool[i].page = NULL;
+ }
+ list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+ }
+}
+
+static void iwl_trans_rx_hw_init(struct iwl_trans *trans,
+ struct iwl_rx_queue *rxq)
+{
+ u32 rb_size;
+ const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
+ u32 rb_timeout = RX_RB_TIMEOUT; /* FIXME: RX_RB_TIMEOUT for all devices? */
+
+ if (iwlagn_mod_params.amsdu_size_8K)
+ rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
+ else
+ rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
+
+ /* Stop Rx DMA */
+ iwl_write_direct32(bus(trans), FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
+
+ /* Reset driver's Rx queue write index */
+ iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
+
+ /* Tell device where to find RBD circular buffer in DRAM */
+ iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_RBDCB_BASE_REG,
+ (u32)(rxq->bd_dma >> 8));
+
+ /* Tell device where in DRAM to update its Rx status */
+ iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_STTS_WPTR_REG,
+ rxq->rb_stts_dma >> 4);
+
+ /* Enable Rx DMA
+ * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in
+ * the credit mechanism in 5000 HW RX FIFO
+ * Direct rx interrupts to hosts
+ * Rx buffer size 4 or 8k
+ * RB timeout 0x10
+ * 256 RBDs
+ */
+ iwl_write_direct32(bus(trans), FH_MEM_RCSR_CHNL0_CONFIG_REG,
+ FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
+ FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |
+ FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
+ FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
+ rb_size|
+ (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
+ (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
+
+ /* Set interrupt coalescing timer to default (2048 usecs) */
+ iwl_write8(bus(trans), CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF);
+}
+
+static int iwl_rx_init(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rx_queue *rxq = &trans_pcie->rxq;
+
+ int i, err;
+ unsigned long flags;
+
+ if (!rxq->bd) {
+ err = iwl_trans_rx_alloc(trans);
+ if (err)
+ return err;
+ }
+
+ spin_lock_irqsave(&rxq->lock, flags);
+ INIT_LIST_HEAD(&rxq->rx_free);
+ INIT_LIST_HEAD(&rxq->rx_used);
+
+ iwl_trans_rxq_free_rx_bufs(trans);
+
+ for (i = 0; i < RX_QUEUE_SIZE; i++)
+ rxq->queue[i] = NULL;
+
+ /* Set us so that we have processed and used all buffers, but have
+ * not restocked the Rx queue with fresh buffers */
+ rxq->read = rxq->write = 0;
+ rxq->write_actual = 0;
+ rxq->free_count = 0;
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ iwlagn_rx_replenish(trans);
+
+ iwl_trans_rx_hw_init(trans, rxq);
+
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+ rxq->need_update = 1;
+ iwl_rx_queue_update_write_ptr(trans, rxq);
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ return 0;
+}
+
+static void iwl_trans_pcie_rx_free(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rx_queue *rxq = &trans_pcie->rxq;
+
+ unsigned long flags;
+
+ /*if rxq->bd is NULL, it means that nothing has been allocated,
+ * exit now */
+ if (!rxq->bd) {
+ IWL_DEBUG_INFO(trans, "Free NULL rx context\n");
+ return;
+ }
+
+ spin_lock_irqsave(&rxq->lock, flags);
+ iwl_trans_rxq_free_rx_bufs(trans);
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ dma_free_coherent(bus(trans)->dev, sizeof(__le32) * RX_QUEUE_SIZE,
+ rxq->bd, rxq->bd_dma);
+ memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma));
+ rxq->bd = NULL;
+
+ if (rxq->rb_stts)
+ dma_free_coherent(bus(trans)->dev,
+ sizeof(struct iwl_rb_status),
+ rxq->rb_stts, rxq->rb_stts_dma);
+ else
+ IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n");
+ memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma));
+ rxq->rb_stts = NULL;
+}
+
+static int iwl_trans_rx_stop(struct iwl_trans *trans)
+{
+
+ /* stop Rx DMA */
+ iwl_write_direct32(bus(trans), FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
+ return iwl_poll_direct_bit(bus(trans), FH_MEM_RSSR_RX_STATUS_REG,
+ FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000);
+}
+
+static inline int iwlagn_alloc_dma_ptr(struct iwl_trans *trans,
+ struct iwl_dma_ptr *ptr, size_t size)
+{
+ if (WARN_ON(ptr->addr))
+ return -EINVAL;
+
+ ptr->addr = dma_alloc_coherent(bus(trans)->dev, size,
+ &ptr->dma, GFP_KERNEL);
+ if (!ptr->addr)
+ return -ENOMEM;
+ ptr->size = size;
+ return 0;
+}
+
+static inline void iwlagn_free_dma_ptr(struct iwl_trans *trans,
+ struct iwl_dma_ptr *ptr)
+{
+ if (unlikely(!ptr->addr))
+ return;
+
+ dma_free_coherent(bus(trans)->dev, ptr->size, ptr->addr, ptr->dma);
+ memset(ptr, 0, sizeof(*ptr));
+}
+
+static int iwl_trans_txq_alloc(struct iwl_trans *trans,
+ struct iwl_tx_queue *txq, int slots_num,
+ u32 txq_id)
+{
+ size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX;
+ int i;
+
+ if (WARN_ON(txq->meta || txq->cmd || txq->skbs || txq->tfds))
+ return -EINVAL;
+
+ txq->q.n_window = slots_num;
+
+ txq->meta = kcalloc(slots_num, sizeof(txq->meta[0]), GFP_KERNEL);
+ txq->cmd = kcalloc(slots_num, sizeof(txq->cmd[0]), GFP_KERNEL);
+
+ if (!txq->meta || !txq->cmd)
+ goto error;
+
+ if (txq_id == trans->shrd->cmd_queue)
+ for (i = 0; i < slots_num; i++) {
+ txq->cmd[i] = kmalloc(sizeof(struct iwl_device_cmd),
+ GFP_KERNEL);
+ if (!txq->cmd[i])
+ goto error;
+ }
+
+ /* Alloc driver data array and TFD circular buffer */
+ /* Driver private data, only for Tx (not command) queues,
+ * not shared with device. */
+ if (txq_id != trans->shrd->cmd_queue) {
+ txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX, sizeof(txq->skbs[0]),
+ GFP_KERNEL);
+ if (!txq->skbs) {
+ IWL_ERR(trans, "kmalloc for auxiliary BD "
+ "structures failed\n");
+ goto error;
+ }
+ } else {
+ txq->skbs = NULL;
+ }
+
+ /* Circular buffer of transmit frame descriptors (TFDs),
+ * shared with device */
+ txq->tfds = dma_alloc_coherent(bus(trans)->dev, tfd_sz,
+ &txq->q.dma_addr, GFP_KERNEL);
+ if (!txq->tfds) {
+ IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz);
+ goto error;
+ }
+ txq->q.id = txq_id;
+
+ return 0;
+error:
+ kfree(txq->skbs);
+ txq->skbs = NULL;
+ /* since txq->cmd has been zeroed,
+ * all non allocated cmd[i] will be NULL */
+ if (txq->cmd && txq_id == trans->shrd->cmd_queue)
+ for (i = 0; i < slots_num; i++)
+ kfree(txq->cmd[i]);
+ kfree(txq->meta);
+ kfree(txq->cmd);
+ txq->meta = NULL;
+ txq->cmd = NULL;
+
+ return -ENOMEM;
+
+}
+
+static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq,
+ int slots_num, u32 txq_id)
+{
+ int ret;
+
+ txq->need_update = 0;
+ memset(txq->meta, 0, sizeof(txq->meta[0]) * slots_num);
+
+ /*
+ * For the default queues 0-3, set up the swq_id
+ * already -- all others need to get one later
+ * (if they need one at all).
+ */
+ if (txq_id < 4)
+ iwl_set_swq_id(txq, txq_id, txq_id);
+
+ /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
+ * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
+ BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
+
+ /* Initialize queue's high/low-water marks, and head/tail indexes */
+ ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num,
+ txq_id);
+ if (ret)
+ return ret;
+
+ /*
+ * Tell nic where to find circular buffer of Tx Frame Descriptors for
+ * given Tx queue, and enable the DMA channel used for that queue.
+ * Circular buffer (TFD queue in DRAM) physical base address */
+ iwl_write_direct32(bus(trans), FH_MEM_CBBC_QUEUE(txq_id),
+ txq->q.dma_addr >> 8);
+
+ return 0;
+}
+
+/**
+ * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's
+ */
+static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id];
+ struct iwl_queue *q = &txq->q;
+ enum dma_data_direction dma_dir;
+ unsigned long flags;
+ spinlock_t *lock;
+
+ if (!q->n_bd)
+ return;
+
+ /* In the command queue, all the TBs are mapped as BIDI
+ * so unmap them as such.
+ */
+ if (txq_id == trans->shrd->cmd_queue) {
+ dma_dir = DMA_BIDIRECTIONAL;
+ lock = &trans->hcmd_lock;
+ } else {
+ dma_dir = DMA_TO_DEVICE;
+ lock = &trans->shrd->sta_lock;
+ }
+
+ spin_lock_irqsave(lock, flags);
+ while (q->write_ptr != q->read_ptr) {
+ /* The read_ptr needs to bound by q->n_window */
+ iwlagn_txq_free_tfd(trans, txq, get_cmd_index(q, q->read_ptr),
+ dma_dir);
+ q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
+ }
+ spin_unlock_irqrestore(lock, flags);
+}
+
+/**
+ * iwl_tx_queue_free - Deallocate DMA queue.
+ * @txq: Transmit queue to deallocate.
+ *
+ * Empty queue by removing and destroying all BD's.
+ * Free all buffers.
+ * 0-fill, but do not free "txq" descriptor structure.
+ */
+static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id];
+ struct device *dev = bus(trans)->dev;
+ int i;
+ if (WARN_ON(!txq))
+ return;
+
+ iwl_tx_queue_unmap(trans, txq_id);
+
+ /* De-alloc array of command/tx buffers */
+
+ if (txq_id == trans->shrd->cmd_queue)
+ for (i = 0; i < txq->q.n_window; i++)
+ kfree(txq->cmd[i]);
+
+ /* De-alloc circular buffer of TFDs */
+ if (txq->q.n_bd) {
+ dma_free_coherent(dev, sizeof(struct iwl_tfd) *
+ txq->q.n_bd, txq->tfds, txq->q.dma_addr);
+ memset(&txq->q.dma_addr, 0, sizeof(txq->q.dma_addr));
+ }
+
+ /* De-alloc array of per-TFD driver data */
+ kfree(txq->skbs);
+ txq->skbs = NULL;
+
+ /* deallocate arrays */
+ kfree(txq->cmd);
+ kfree(txq->meta);
+ txq->cmd = NULL;
+ txq->meta = NULL;
+
+ /* 0-fill queue descriptor structure */
+ memset(txq, 0, sizeof(*txq));
+}
+
+/**
+ * iwl_trans_tx_free - Free TXQ Context
+ *
+ * Destroy all TX DMA queues and structures
+ */
+static void iwl_trans_pcie_tx_free(struct iwl_trans *trans)
+{
+ int txq_id;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ /* Tx queues */
+ if (trans_pcie->txq) {
+ for (txq_id = 0;
+ txq_id < hw_params(trans).max_txq_num; txq_id++)
+ iwl_tx_queue_free(trans, txq_id);
+ }
+
+ kfree(trans_pcie->txq);
+ trans_pcie->txq = NULL;
+
+ iwlagn_free_dma_ptr(trans, &trans_pcie->kw);
+
+ iwlagn_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls);
+}
+
+/**
+ * iwl_trans_tx_alloc - allocate TX context
+ * Allocate all Tx DMA structures and initialize them
+ *
+ * @param priv
+ * @return error code
+ */
+static int iwl_trans_tx_alloc(struct iwl_trans *trans)
+{
+ int ret;
+ int txq_id, slots_num;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ u16 scd_bc_tbls_size = hw_params(trans).max_txq_num *
+ sizeof(struct iwlagn_scd_bc_tbl);
+
+ /*It is not allowed to alloc twice, so warn when this happens.
+ * We cannot rely on the previous allocation, so free and fail */
+ if (WARN_ON(trans_pcie->txq)) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls,
+ scd_bc_tbls_size);
+ if (ret) {
+ IWL_ERR(trans, "Scheduler BC Table allocation failed\n");
+ goto error;
+ }
+
+ /* Alloc keep-warm buffer */
+ ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE);
+ if (ret) {
+ IWL_ERR(trans, "Keep Warm allocation failed\n");
+ goto error;
+ }
+
+ trans_pcie->txq = kcalloc(hw_params(trans).max_txq_num,
+ sizeof(struct iwl_tx_queue), GFP_KERNEL);
+ if (!trans_pcie->txq) {
+ IWL_ERR(trans, "Not enough memory for txq\n");
+ ret = ENOMEM;
+ goto error;
+ }
+
+ /* Alloc and init all Tx queues, including the command queue (#4/#9) */
+ for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++) {
+ slots_num = (txq_id == trans->shrd->cmd_queue) ?
+ TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
+ ret = iwl_trans_txq_alloc(trans, &trans_pcie->txq[txq_id],
+ slots_num, txq_id);
+ if (ret) {
+ IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id);
+ goto error;
+ }
+ }
+
+ return 0;
+
+error:
+ iwl_trans_pcie_tx_free(trans);
+
+ return ret;
+}
+static int iwl_tx_init(struct iwl_trans *trans)
+{
+ int ret;
+ int txq_id, slots_num;
+ unsigned long flags;
+ bool alloc = false;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (!trans_pcie->txq) {
+ ret = iwl_trans_tx_alloc(trans);
+ if (ret)
+ goto error;
+ alloc = true;
+ }
+
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+
+ /* Turn off all Tx DMA fifos */
+ iwl_write_prph(bus(trans), SCD_TXFACT, 0);
+
+ /* Tell NIC where to find the "keep warm" buffer */
+ iwl_write_direct32(bus(trans), FH_KW_MEM_ADDR_REG,
+ trans_pcie->kw.dma >> 4);
+
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ /* Alloc and init all Tx queues, including the command queue (#4/#9) */
+ for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++) {
+ slots_num = (txq_id == trans->shrd->cmd_queue) ?
+ TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
+ ret = iwl_trans_txq_init(trans, &trans_pcie->txq[txq_id],
+ slots_num, txq_id);
+ if (ret) {
+ IWL_ERR(trans, "Tx %d queue init failed\n", txq_id);
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ /*Upon error, free only if we allocated something */
+ if (alloc)
+ iwl_trans_pcie_tx_free(trans);
+ return ret;
+}
+
+static void iwl_set_pwr_vmain(struct iwl_trans *trans)
+{
+/*
+ * (for documentation purposes)
+ * to set power to V_AUX, do:
+
+ if (pci_pme_capable(priv->pci_dev, PCI_D3cold))
+ iwl_set_bits_mask_prph(bus(trans), APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
+ ~APMG_PS_CTRL_MSK_PWR_SRC);
+ */
+
+ iwl_set_bits_mask_prph(bus(trans), APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
+ ~APMG_PS_CTRL_MSK_PWR_SRC);
+}
+
+static int iwl_nic_init(struct iwl_trans *trans)
+{
+ unsigned long flags;
+
+ /* nic_init */
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+ iwl_apm_init(priv(trans));
+
+ /* Set interrupt coalescing calibration timer to default (512 usecs) */
+ iwl_write8(bus(trans), CSR_INT_COALESCING,
+ IWL_HOST_INT_CALIB_TIMEOUT_DEF);
+
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ iwl_set_pwr_vmain(trans);
+
+ iwl_nic_config(priv(trans));
+
+ /* Allocate the RX queue, or reset if it is already allocated */
+ iwl_rx_init(trans);
+
+ /* Allocate or reset and init all Tx and Command queues */
+ if (iwl_tx_init(trans))
+ return -ENOMEM;
+
+ if (hw_params(trans).shadow_reg_enable) {
+ /* enable shadow regs in HW */
+ iwl_set_bit(bus(trans), CSR_MAC_SHADOW_REG_CTRL,
+ 0x800FFFFF);
+ }
+
+ set_bit(STATUS_INIT, &trans->shrd->status);
+
+ return 0;
+}
+
+#define HW_READY_TIMEOUT (50)
+
+/* Note: returns poll_bit return value, which is >= 0 if success */
+static int iwl_set_hw_ready(struct iwl_trans *trans)
+{
+ int ret;
+
+ iwl_set_bit(bus(trans), CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_READY);
+
+ /* See if we got it */
+ ret = iwl_poll_bit(bus(trans), CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
+ HW_READY_TIMEOUT);
+
+ IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : "");
+ return ret;
+}
+
+/* Note: returns standard 0/-ERROR code */
+static int iwl_trans_pcie_prepare_card_hw(struct iwl_trans *trans)
+{
+ int ret;
+
+ IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n");
+
+ ret = iwl_set_hw_ready(trans);
+ if (ret >= 0)
+ return 0;
+
+ /* If HW is not ready, prepare the conditions to check again */
+ iwl_set_bit(bus(trans), CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_PREPARE);
+
+ ret = iwl_poll_bit(bus(trans), CSR_HW_IF_CONFIG_REG,
+ ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000);
+
+ if (ret < 0)
+ return ret;
+
+ /* HW should be ready by now, check again. */
+ ret = iwl_set_hw_ready(trans);
+ if (ret >= 0)
+ return 0;
+ return ret;
+}
+
+#define IWL_AC_UNSET -1
+
+struct queue_to_fifo_ac {
+ s8 fifo, ac;
+};
+
+static const struct queue_to_fifo_ac iwlagn_default_queue_to_tx_fifo[] = {
+ { IWL_TX_FIFO_VO, IEEE80211_AC_VO, },
+ { IWL_TX_FIFO_VI, IEEE80211_AC_VI, },
+ { IWL_TX_FIFO_BE, IEEE80211_AC_BE, },
+ { IWL_TX_FIFO_BK, IEEE80211_AC_BK, },
+ { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, },
+ { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
+ { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
+ { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
+ { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
+ { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
+ { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
+};
+
+static const struct queue_to_fifo_ac iwlagn_ipan_queue_to_tx_fifo[] = {
+ { IWL_TX_FIFO_VO, IEEE80211_AC_VO, },
+ { IWL_TX_FIFO_VI, IEEE80211_AC_VI, },
+ { IWL_TX_FIFO_BE, IEEE80211_AC_BE, },
+ { IWL_TX_FIFO_BK, IEEE80211_AC_BK, },
+ { IWL_TX_FIFO_BK_IPAN, IEEE80211_AC_BK, },
+ { IWL_TX_FIFO_BE_IPAN, IEEE80211_AC_BE, },
+ { IWL_TX_FIFO_VI_IPAN, IEEE80211_AC_VI, },
+ { IWL_TX_FIFO_VO_IPAN, IEEE80211_AC_VO, },
+ { IWL_TX_FIFO_BE_IPAN, 2, },
+ { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, },
+ { IWL_TX_FIFO_AUX, IWL_AC_UNSET, },
+};
+
+static const u8 iwlagn_bss_ac_to_fifo[] = {
+ IWL_TX_FIFO_VO,
+ IWL_TX_FIFO_VI,
+ IWL_TX_FIFO_BE,
+ IWL_TX_FIFO_BK,
+};
+static const u8 iwlagn_bss_ac_to_queue[] = {
+ 0, 1, 2, 3,
+};
+static const u8 iwlagn_pan_ac_to_fifo[] = {
+ IWL_TX_FIFO_VO_IPAN,
+ IWL_TX_FIFO_VI_IPAN,
+ IWL_TX_FIFO_BE_IPAN,
+ IWL_TX_FIFO_BK_IPAN,
+};
+static const u8 iwlagn_pan_ac_to_queue[] = {
+ 7, 6, 5, 4,
+};
+
+static int iwl_trans_pcie_start_device(struct iwl_trans *trans)
+{
+ int ret;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ trans->shrd->ucode_owner = IWL_OWNERSHIP_DRIVER;
+ trans_pcie->ac_to_queue[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_queue;
+ trans_pcie->ac_to_queue[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_queue;
+
+ trans_pcie->ac_to_fifo[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_fifo;
+ trans_pcie->ac_to_fifo[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_fifo;
+
+ trans_pcie->mcast_queue[IWL_RXON_CTX_BSS] = 0;
+ trans_pcie->mcast_queue[IWL_RXON_CTX_PAN] = IWL_IPAN_MCAST_QUEUE;
+
+ if ((hw_params(trans).sku & EEPROM_SKU_CAP_AMT_ENABLE) &&
+ iwl_trans_pcie_prepare_card_hw(trans)) {
+ IWL_WARN(trans, "Exit HW not ready\n");
+ return -EIO;
+ }
+
+ /* If platform's RF_KILL switch is NOT set to KILL */
+ if (iwl_read32(bus(trans), CSR_GP_CNTRL) &
+ CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
+ clear_bit(STATUS_RF_KILL_HW, &trans->shrd->status);
+ else
+ set_bit(STATUS_RF_KILL_HW, &trans->shrd->status);
+
+ if (iwl_is_rfkill(trans->shrd)) {
+ iwl_set_hw_rfkill_state(priv(trans), true);
+ iwl_enable_interrupts(trans);
+ return -ERFKILL;
+ }
+
+ iwl_write32(bus(trans), CSR_INT, 0xFFFFFFFF);
+
+ ret = iwl_nic_init(trans);
+ if (ret) {
+ IWL_ERR(trans, "Unable to init nic\n");
+ return ret;
+ }
+
+ /* make sure rfkill handshake bits are cleared */
+ iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+ iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+
+ /* clear (again), then enable host interrupts */
+ iwl_write32(bus(trans), CSR_INT, 0xFFFFFFFF);
+ iwl_enable_interrupts(trans);
+
+ /* really make sure rfkill handshake bits are cleared */
+ iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+ iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+
+ return 0;
+}
+
+/*
+ * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask
+ * must be called under priv->shrd->lock and mac access
+ */
+static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask)
+{
+ iwl_write_prph(bus(trans), SCD_TXFACT, mask);
+}
+
+static void iwl_trans_pcie_tx_start(struct iwl_trans *trans)
+{
+ const struct queue_to_fifo_ac *queue_to_fifo;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ u32 a;
+ unsigned long flags;
+ int i, chan;
+ u32 reg_val;
+
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+
+ trans_pcie->scd_base_addr =
+ iwl_read_prph(bus(trans), SCD_SRAM_BASE_ADDR);
+ a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND;
+ /* reset conext data memory */
+ for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND;
+ a += 4)
+ iwl_write_targ_mem(bus(trans), a, 0);
+ /* reset tx status memory */
+ for (; a < trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_UPPER_BOUND;
+ a += 4)
+ iwl_write_targ_mem(bus(trans), a, 0);
+ for (; a < trans_pcie->scd_base_addr +
+ SCD_TRANS_TBL_OFFSET_QUEUE(hw_params(trans).max_txq_num);
+ a += 4)
+ iwl_write_targ_mem(bus(trans), a, 0);
+
+ iwl_write_prph(bus(trans), SCD_DRAM_BASE_ADDR,
+ trans_pcie->scd_bc_tbls.dma >> 10);
+
+ /* Enable DMA channel */
+ for (chan = 0; chan < FH_TCSR_CHNL_NUM ; chan++)
+ iwl_write_direct32(bus(trans), FH_TCSR_CHNL_TX_CONFIG_REG(chan),
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
+
+ /* Update FH chicken bits */
+ reg_val = iwl_read_direct32(bus(trans), FH_TX_CHICKEN_BITS_REG);
+ iwl_write_direct32(bus(trans), FH_TX_CHICKEN_BITS_REG,
+ reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
+
+ iwl_write_prph(bus(trans), SCD_QUEUECHAIN_SEL,
+ SCD_QUEUECHAIN_SEL_ALL(trans));
+ iwl_write_prph(bus(trans), SCD_AGGR_SEL, 0);
+
+ /* initiate the queues */
+ for (i = 0; i < hw_params(trans).max_txq_num; i++) {
+ iwl_write_prph(bus(trans), SCD_QUEUE_RDPTR(i), 0);
+ iwl_write_direct32(bus(trans), HBUS_TARG_WRPTR, 0 | (i << 8));
+ iwl_write_targ_mem(bus(trans), trans_pcie->scd_base_addr +
+ SCD_CONTEXT_QUEUE_OFFSET(i), 0);
+ iwl_write_targ_mem(bus(trans), trans_pcie->scd_base_addr +
+ SCD_CONTEXT_QUEUE_OFFSET(i) +
+ sizeof(u32),
+ ((SCD_WIN_SIZE <<
+ SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
+ SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
+ ((SCD_FRAME_LIMIT <<
+ SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
+ SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
+ }
+
+ iwl_write_prph(bus(trans), SCD_INTERRUPT_MASK,
+ IWL_MASK(0, hw_params(trans).max_txq_num));
+
+ /* Activate all Tx DMA/FIFO channels */
+ iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7));
+
+ /* map queues to FIFOs */
+ if (trans->shrd->valid_contexts != BIT(IWL_RXON_CTX_BSS))
+ queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo;
+ else
+ queue_to_fifo = iwlagn_default_queue_to_tx_fifo;
+
+ iwl_trans_set_wr_ptrs(trans, trans->shrd->cmd_queue, 0);
+
+ /* make sure all queue are not stopped */
+ memset(&trans_pcie->queue_stopped[0], 0,
+ sizeof(trans_pcie->queue_stopped));
+ for (i = 0; i < 4; i++)
+ atomic_set(&trans_pcie->queue_stop_count[i], 0);
+
+ /* reset to 0 to enable all the queue first */
+ trans_pcie->txq_ctx_active_msk = 0;
+
+ BUILD_BUG_ON(ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo) <
+ IWLAGN_FIRST_AMPDU_QUEUE);
+ BUILD_BUG_ON(ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo) <
+ IWLAGN_FIRST_AMPDU_QUEUE);
+
+ for (i = 0; i < IWLAGN_FIRST_AMPDU_QUEUE; i++) {
+ int fifo = queue_to_fifo[i].fifo;
+ int ac = queue_to_fifo[i].ac;
+
+ iwl_txq_ctx_activate(trans_pcie, i);
+
+ if (fifo == IWL_TX_FIFO_UNUSED)
+ continue;
+
+ if (ac != IWL_AC_UNSET)
+ iwl_set_swq_id(&trans_pcie->txq[i], ac, i);
+ iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[i],
+ fifo, 0);
+ }
+
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ /* Enable L1-Active */
+ iwl_clear_bits_prph(bus(trans), APMG_PCIDEV_STT_REG,
+ APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
+}
+
+/**
+ * iwlagn_txq_ctx_stop - Stop all Tx DMA channels
+ */
+static int iwl_trans_tx_stop(struct iwl_trans *trans)
+{
+ int ch, txq_id;
+ unsigned long flags;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ /* Turn off all Tx DMA fifos */
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+
+ iwl_trans_txq_set_sched(trans, 0);
+
+ /* Stop each Tx DMA channel, and wait for it to be idle */
+ for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) {
+ iwl_write_direct32(bus(trans),
+ FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
+ if (iwl_poll_direct_bit(bus(trans), FH_TSSR_TX_STATUS_REG,
+ FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
+ 1000))
+ IWL_ERR(trans, "Failing on timeout while stopping"
+ " DMA channel %d [0x%08x]", ch,
+ iwl_read_direct32(bus(trans),
+ FH_TSSR_TX_STATUS_REG));
+ }
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ if (!trans_pcie->txq) {
+ IWL_WARN(trans, "Stopping tx queues that aren't allocated...");
+ return 0;
+ }
+
+ /* Unmap DMA from host system and free skb's */
+ for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++)
+ iwl_tx_queue_unmap(trans, txq_id);
+
+ return 0;
+}
+
+static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
+{
+ unsigned long flags;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ /* tell the device to stop sending interrupts */
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+ iwl_disable_interrupts(trans);
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ /* device going down, Stop using ICT table */
+ iwl_disable_ict(trans);
+
+ /*
+ * If a HW restart happens during firmware loading,
+ * then the firmware loading might call this function
+ * and later it might be called again due to the
+ * restart. So don't process again if the device is
+ * already dead.
+ */
+ if (test_bit(STATUS_DEVICE_ENABLED, &trans->shrd->status)) {
+ iwl_trans_tx_stop(trans);
+ iwl_trans_rx_stop(trans);
+
+ /* Power-down device's busmaster DMA clocks */
+ iwl_write_prph(bus(trans), APMG_CLK_DIS_REG,
+ APMG_CLK_VAL_DMA_CLK_RQT);
+ udelay(5);
+ }
+
+ /* Make sure (redundant) we've released our request to stay awake */
+ iwl_clear_bit(bus(trans), CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+ /* Stop the device, and put it in low power state */
+ iwl_apm_stop(priv(trans));
+
+ /* Upon stop, the APM issues an interrupt if HW RF kill is set.
+ * Clean again the interrupt here
+ */
+ spin_lock_irqsave(&trans->shrd->lock, flags);
+ iwl_disable_interrupts(trans);
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ /* wait to make sure we flush pending tasklet*/
+ synchronize_irq(bus(trans)->irq);
+ tasklet_kill(&trans_pcie->irq_tasklet);
+
+ /* stop and reset the on-board processor */
+ iwl_write32(bus(trans), CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
+}
+
+static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx,
+ u8 sta_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload;
+ struct iwl_cmd_meta *out_meta;
+ struct iwl_tx_queue *txq;
+ struct iwl_queue *q;
+
+ dma_addr_t phys_addr = 0;
+ dma_addr_t txcmd_phys;
+ dma_addr_t scratch_phys;
+ u16 len, firstlen, secondlen;
+ u16 seq_number = 0;
+ u8 wait_write_ptr = 0;
+ u8 txq_id;
+ u8 tid = 0;
+ bool is_agg = false;
+ __le16 fc = hdr->frame_control;
+ u8 hdr_len = ieee80211_hdrlen(fc);
+
+ /*
+ * Send this frame after DTIM -- there's a special queue
+ * reserved for this for contexts that support AP mode.
+ */
+ if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
+ txq_id = trans_pcie->mcast_queue[ctx];
+
+ /*
+ * The microcode will clear the more data
+ * bit in the last frame it transmits.
+ */
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+ txq_id = IWL_AUX_QUEUE;
+ else
+ txq_id =
+ trans_pcie->ac_to_queue[ctx][skb_get_queue_mapping(skb)];
+
+ if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
+ u8 *qc = NULL;
+ struct iwl_tid_data *tid_data;
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+ tid_data = &trans->shrd->tid_data[sta_id][tid];
+
+ if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+ return -1;
+
+ seq_number = tid_data->seq_number;
+ seq_number &= IEEE80211_SCTL_SEQ;
+ hdr->seq_ctrl = hdr->seq_ctrl &
+ cpu_to_le16(IEEE80211_SCTL_FRAG);
+ hdr->seq_ctrl |= cpu_to_le16(seq_number);
+ seq_number += 0x10;
+ /* aggregation is on for this <sta,tid> */
+ if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+ WARN_ON_ONCE(tid_data->agg.state != IWL_AGG_ON);
+ txq_id = tid_data->agg.txq_id;
+ is_agg = true;
+ }
+ }
+
+ /* Copy MAC header from skb into command buffer */
+ memcpy(tx_cmd->hdr, hdr, hdr_len);
+
+ txq = &trans_pcie->txq[txq_id];
+ q = &txq->q;
+
+ /* Set up driver data for this TFD */
+ txq->skbs[q->write_ptr] = skb;
+ txq->cmd[q->write_ptr] = dev_cmd;
+
+ dev_cmd->hdr.cmd = REPLY_TX;
+ dev_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) |
+ INDEX_TO_SEQ(q->write_ptr)));
+
+ /* Set up first empty entry in queue's array of Tx/cmd buffers */
+ out_meta = &txq->meta[q->write_ptr];
+
+ /*
+ * Use the first empty entry in this queue's command buffer array
+ * to contain the Tx command and MAC header concatenated together
+ * (payload data will be in another buffer).
+ * Size of this varies, due to varying MAC header length.
+ * If end is not dword aligned, we'll have 2 extra bytes at the end
+ * of the MAC header (device reads on dword boundaries).
+ * We'll tell device about this padding later.
+ */
+ len = sizeof(struct iwl_tx_cmd) +
+ sizeof(struct iwl_cmd_header) + hdr_len;
+ firstlen = (len + 3) & ~3;
+
+ /* Tell NIC about any 2-byte padding after MAC header */
+ if (firstlen != len)
+ tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
+
+ /* Physical address of this Tx command's header (not MAC header!),
+ * within command buffer array. */
+ txcmd_phys = dma_map_single(bus(trans)->dev,
+ &dev_cmd->hdr, firstlen,
+ DMA_BIDIRECTIONAL);
+ if (unlikely(dma_mapping_error(bus(trans)->dev, txcmd_phys)))
+ return -1;
+ dma_unmap_addr_set(out_meta, mapping, txcmd_phys);
+ dma_unmap_len_set(out_meta, len, firstlen);
+
+ if (!ieee80211_has_morefrags(fc)) {
+ txq->need_update = 1;
+ } else {
+ wait_write_ptr = 1;
+ txq->need_update = 0;
+ }
+
+ /* Set up TFD's 2nd entry to point directly to remainder of skb,
+ * if any (802.11 null frames have no payload). */
+ secondlen = skb->len - hdr_len;
+ if (secondlen > 0) {
+ phys_addr = dma_map_single(bus(trans)->dev, skb->data + hdr_len,
+ secondlen, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(bus(trans)->dev, phys_addr))) {
+ dma_unmap_single(bus(trans)->dev,
+ dma_unmap_addr(out_meta, mapping),
+ dma_unmap_len(out_meta, len),
+ DMA_BIDIRECTIONAL);
+ return -1;
+ }
+ }
+
+ /* Attach buffers to TFD */
+ iwlagn_txq_attach_buf_to_tfd(trans, txq, txcmd_phys, firstlen, 1);
+ if (secondlen > 0)
+ iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr,
+ secondlen, 0);
+
+ scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) +
+ offsetof(struct iwl_tx_cmd, scratch);
+
+ /* take back ownership of DMA buffer to enable update */
+ dma_sync_single_for_cpu(bus(trans)->dev, txcmd_phys, firstlen,
+ DMA_BIDIRECTIONAL);
+ tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
+ tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys);
+
+ IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n",
+ le16_to_cpu(dev_cmd->hdr.sequence));
+ IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags));
+ iwl_print_hex_dump(trans, IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd));
+ iwl_print_hex_dump(trans, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len);
+
+ /* Set up entry for this TFD in Tx byte-count array */
+ if (is_agg)
+ iwl_trans_txq_update_byte_cnt_tbl(trans, txq,
+ le16_to_cpu(tx_cmd->len));
+
+ dma_sync_single_for_device(bus(trans)->dev, txcmd_phys, firstlen,
+ DMA_BIDIRECTIONAL);
+
+ trace_iwlwifi_dev_tx(priv(trans),
+ &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr],
+ sizeof(struct iwl_tfd),
+ &dev_cmd->hdr, firstlen,
+ skb->data + hdr_len, secondlen);
+
+ /* Tell device the write index *just past* this latest filled TFD */
+ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
+ iwl_txq_update_write_ptr(trans, txq);
+
+ if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
+ trans->shrd->tid_data[sta_id][tid].tfds_in_queue++;
+ if (!ieee80211_has_morefrags(fc))
+ trans->shrd->tid_data[sta_id][tid].seq_number =
+ seq_number;
+ }
+
+ /*
+ * At this point the frame is "transmitted" successfully
+ * and we will get a TX status notification eventually,
+ * regardless of the value of ret. "ret" only indicates
+ * whether or not we should update the write pointer.
+ */
+ if (iwl_queue_space(q) < q->high_mark) {
+ if (wait_write_ptr) {
+ txq->need_update = 1;
+ iwl_txq_update_write_ptr(trans, txq);
+ } else {
+ iwl_stop_queue(trans, txq);
+ }
+ }
+ return 0;
+}
+
+static void iwl_trans_pcie_kick_nic(struct iwl_trans *trans)
+{
+ /* Remove all resets to allow NIC to operate */
+ iwl_write32(bus(trans), CSR_RESET, 0);
+}
+
+static int iwl_trans_pcie_request_irq(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ int err;
+
+ trans_pcie->inta_mask = CSR_INI_SET_MASK;
+
+ tasklet_init(&trans_pcie->irq_tasklet, (void (*)(unsigned long))
+ iwl_irq_tasklet, (unsigned long)trans);
+
+ iwl_alloc_isr_ict(trans);
+
+ err = request_irq(bus(trans)->irq, iwl_isr_ict, IRQF_SHARED,
+ DRV_NAME, trans);
+ if (err) {
+ IWL_ERR(trans, "Error allocating IRQ %d\n", bus(trans)->irq);
+ iwl_free_isr_ict(trans);
+ return err;
+ }
+
+ INIT_WORK(&trans_pcie->rx_replenish, iwl_bg_rx_replenish);
+ return 0;
+}
+
+static int iwlagn_txq_check_empty(struct iwl_trans *trans,
+ int sta_id, u8 tid, int txq_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_queue *q = &trans_pcie->txq[txq_id].q;
+ struct iwl_tid_data *tid_data = &trans->shrd->tid_data[sta_id][tid];
+
+ lockdep_assert_held(&trans->shrd->sta_lock);
+
+ switch (trans->shrd->tid_data[sta_id][tid].agg.state) {
+ case IWL_EMPTYING_HW_QUEUE_DELBA:
+ /* We are reclaiming the last packet of the */
+ /* aggregated HW queue */
+ if ((txq_id == tid_data->agg.txq_id) &&
+ (q->read_ptr == q->write_ptr)) {
+ IWL_DEBUG_HT(trans,
+ "HW queue empty: continue DELBA flow\n");
+ iwl_trans_pcie_txq_agg_disable(trans, txq_id);
+ tid_data->agg.state = IWL_AGG_OFF;
+ iwl_stop_tx_ba_trans_ready(priv(trans),
+ NUM_IWL_RXON_CTX,
+ sta_id, tid);
+ iwl_wake_queue(trans, &trans_pcie->txq[txq_id]);
+ }
+ break;
+ case IWL_EMPTYING_HW_QUEUE_ADDBA:
+ /* We are reclaiming the last packet of the queue */
+ if (tid_data->tfds_in_queue == 0) {
+ IWL_DEBUG_HT(trans,
+ "HW queue empty: continue ADDBA flow\n");
+ tid_data->agg.state = IWL_AGG_ON;
+ iwl_start_tx_ba_trans_ready(priv(trans),
+ NUM_IWL_RXON_CTX,
+ sta_id, tid);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void iwl_free_tfds_in_queue(struct iwl_trans *trans,
+ int sta_id, int tid, int freed)
+{
+ lockdep_assert_held(&trans->shrd->sta_lock);
+
+ if (trans->shrd->tid_data[sta_id][tid].tfds_in_queue >= freed)
+ trans->shrd->tid_data[sta_id][tid].tfds_in_queue -= freed;
+ else {
+ IWL_DEBUG_TX(trans, "free more than tfds_in_queue (%u:%d)\n",
+ trans->shrd->tid_data[sta_id][tid].tfds_in_queue,
+ freed);
+ trans->shrd->tid_data[sta_id][tid].tfds_in_queue = 0;
+ }
+}
+
+static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid,
+ int txq_id, int ssn, u32 status,
+ struct sk_buff_head *skbs)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id];
+ enum iwl_agg_state agg_state;
+ /* n_bd is usually 256 => n_bd - 1 = 0xff */
+ int tfd_num = ssn & (txq->q.n_bd - 1);
+ int freed = 0;
+ bool cond;
+
+ txq->time_stamp = jiffies;
+
+ if (txq->sched_retry) {
+ agg_state =
+ trans->shrd->tid_data[txq->sta_id][txq->tid].agg.state;
+ cond = (agg_state != IWL_EMPTYING_HW_QUEUE_DELBA);
+ } else {
+ cond = (status != TX_STATUS_FAIL_PASSIVE_NO_RX);
+ }
+
+ if (txq->q.read_ptr != tfd_num) {
+ IWL_DEBUG_TX_REPLY(trans, "Retry scheduler reclaim "
+ "scd_ssn=%d idx=%d txq=%d swq=%d\n",
+ ssn , tfd_num, txq_id, txq->swq_id);
+ freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs);
+ if (iwl_queue_space(&txq->q) > txq->q.low_mark && cond)
+ iwl_wake_queue(trans, txq);
+ }
+
+ iwl_free_tfds_in_queue(trans, sta_id, tid, freed);
+ iwlagn_txq_check_empty(trans, sta_id, tid, txq_id);
+}
+
+static void iwl_trans_pcie_free(struct iwl_trans *trans)
+{
+ iwl_trans_pcie_tx_free(trans);
+ iwl_trans_pcie_rx_free(trans);
+ free_irq(bus(trans)->irq, trans);
+ iwl_free_isr_ict(trans);
+ trans->shrd->trans = NULL;
+ kfree(trans);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int iwl_trans_pcie_suspend(struct iwl_trans *trans)
+{
+ /*
+ * This function is called when system goes into suspend state
+ * mac80211 will call iwlagn_mac_stop() from the mac80211 suspend
+ * function first but since iwlagn_mac_stop() has no knowledge of
+ * who the caller is,
+ * it will not call apm_ops.stop() to stop the DMA operation.
+ * Calling apm_ops.stop here to make sure we stop the DMA.
+ *
+ * But of course ... if we have configured WoWLAN then we did other
+ * things already :-)
+ */
+ if (!trans->shrd->wowlan) {
+ iwl_apm_stop(priv(trans));
+ } else {
+ iwl_disable_interrupts(trans);
+ iwl_clear_bit(bus(trans), CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ }
+
+ return 0;
+}
+
+static int iwl_trans_pcie_resume(struct iwl_trans *trans)
+{
+ bool hw_rfkill = false;
+
+ iwl_enable_interrupts(trans);
+
+ if (!(iwl_read32(bus(trans), CSR_GP_CNTRL) &
+ CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
+ hw_rfkill = true;
+
+ if (hw_rfkill)
+ set_bit(STATUS_RF_KILL_HW, &trans->shrd->status);
+ else
+ clear_bit(STATUS_RF_KILL_HW, &trans->shrd->status);
+
+ iwl_set_hw_rfkill_state(priv(trans), hw_rfkill);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static void iwl_trans_pcie_wake_any_queue(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx)
+{
+ u8 ac, txq_id;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ for (ac = 0; ac < AC_NUM; ac++) {
+ txq_id = trans_pcie->ac_to_queue[ctx][ac];
+ IWL_DEBUG_INFO(trans, "Queue Status: Q[%d] %s\n",
+ ac,
+ (atomic_read(&trans_pcie->queue_stop_count[ac]) > 0)
+ ? "stopped" : "awake");
+ iwl_wake_queue(trans, &trans_pcie->txq[txq_id]);
+ }
+}
+
+const struct iwl_trans_ops trans_ops_pcie;
+
+static struct iwl_trans *iwl_trans_pcie_alloc(struct iwl_shared *shrd)
+{
+ struct iwl_trans *iwl_trans = kzalloc(sizeof(struct iwl_trans) +
+ sizeof(struct iwl_trans_pcie),
+ GFP_KERNEL);
+ if (iwl_trans) {
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(iwl_trans);
+ iwl_trans->ops = &trans_ops_pcie;
+ iwl_trans->shrd = shrd;
+ trans_pcie->trans = iwl_trans;
+ spin_lock_init(&iwl_trans->hcmd_lock);
+ }
+
+ return iwl_trans;
+}
+
+static void iwl_trans_pcie_stop_queue(struct iwl_trans *trans, int txq_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ iwl_stop_queue(trans, &trans_pcie->txq[txq_id]);
+}
+
+#define IWL_FLUSH_WAIT_MS 2000
+
+static int iwl_trans_pcie_wait_tx_queue_empty(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tx_queue *txq;
+ struct iwl_queue *q;
+ int cnt;
+ unsigned long now = jiffies;
+ int ret = 0;
+
+ /* waiting for all the tx frames complete might take a while */
+ for (cnt = 0; cnt < hw_params(trans).max_txq_num; cnt++) {
+ if (cnt == trans->shrd->cmd_queue)
+ continue;
+ txq = &trans_pcie->txq[cnt];
+ q = &txq->q;
+ while (q->read_ptr != q->write_ptr && !time_after(jiffies,
+ now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS)))
+ msleep(1);
+
+ if (q->read_ptr != q->write_ptr) {
+ IWL_ERR(trans, "fail to flush all tx fifo queues\n");
+ ret = -ETIMEDOUT;
+ break;
+ }
+ }
+ return ret;
+}
+
+/*
+ * On every watchdog tick we check (latest) time stamp. If it does not
+ * change during timeout period and queue is not empty we reset firmware.
+ */
+static int iwl_trans_pcie_check_stuck_queue(struct iwl_trans *trans, int cnt)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tx_queue *txq = &trans_pcie->txq[cnt];
+ struct iwl_queue *q = &txq->q;
+ unsigned long timeout;
+
+ if (q->read_ptr == q->write_ptr) {
+ txq->time_stamp = jiffies;
+ return 0;
+ }
+
+ timeout = txq->time_stamp +
+ msecs_to_jiffies(hw_params(trans).wd_timeout);
+
+ if (time_after(jiffies, timeout)) {
+ IWL_ERR(trans, "Queue %d stuck for %u ms.\n", q->id,
+ hw_params(trans).wd_timeout);
+ IWL_ERR(trans, "Current read_ptr %d write_ptr %d\n",
+ q->read_ptr, q->write_ptr);
+ return 1;
+ }
+
+ return 0;
+}
+
+static const char *get_fh_string(int cmd)
+{
+ switch (cmd) {
+ IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
+ IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
+ IWL_CMD(FH_RSCSR_CHNL0_WPTR);
+ IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
+ IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
+ IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
+ IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
+ IWL_CMD(FH_TSSR_TX_STATUS_REG);
+ IWL_CMD(FH_TSSR_TX_ERROR_REG);
+ default:
+ return "UNKNOWN";
+ }
+}
+
+int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display)
+{
+ int i;
+#ifdef CONFIG_IWLWIFI_DEBUG
+ int pos = 0;
+ size_t bufsz = 0;
+#endif
+ static const u32 fh_tbl[] = {
+ FH_RSCSR_CHNL0_STTS_WPTR_REG,
+ FH_RSCSR_CHNL0_RBDCB_BASE_REG,
+ FH_RSCSR_CHNL0_WPTR,
+ FH_MEM_RCSR_CHNL0_CONFIG_REG,
+ FH_MEM_RSSR_SHARED_CTRL_REG,
+ FH_MEM_RSSR_RX_STATUS_REG,
+ FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
+ FH_TSSR_TX_STATUS_REG,
+ FH_TSSR_TX_ERROR_REG
+ };
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (display) {
+ bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+ *buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!*buf)
+ return -ENOMEM;
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ "FH register values:\n");
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ " %34s: 0X%08x\n",
+ get_fh_string(fh_tbl[i]),
+ iwl_read_direct32(bus(trans), fh_tbl[i]));
+ }
+ return pos;
+ }
+#endif
+ IWL_ERR(trans, "FH register values:\n");
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
+ IWL_ERR(trans, " %34s: 0X%08x\n",
+ get_fh_string(fh_tbl[i]),
+ iwl_read_direct32(bus(trans), fh_tbl[i]));
+ }
+ return 0;
+}
+
+static const char *get_csr_string(int cmd)
+{
+ switch (cmd) {
+ IWL_CMD(CSR_HW_IF_CONFIG_REG);
+ IWL_CMD(CSR_INT_COALESCING);
+ IWL_CMD(CSR_INT);
+ IWL_CMD(CSR_INT_MASK);
+ IWL_CMD(CSR_FH_INT_STATUS);
+ IWL_CMD(CSR_GPIO_IN);
+ IWL_CMD(CSR_RESET);
+ IWL_CMD(CSR_GP_CNTRL);
+ IWL_CMD(CSR_HW_REV);
+ IWL_CMD(CSR_EEPROM_REG);
+ IWL_CMD(CSR_EEPROM_GP);
+ IWL_CMD(CSR_OTP_GP_REG);
+ IWL_CMD(CSR_GIO_REG);
+ IWL_CMD(CSR_GP_UCODE_REG);
+ IWL_CMD(CSR_GP_DRIVER_REG);
+ IWL_CMD(CSR_UCODE_DRV_GP1);
+ IWL_CMD(CSR_UCODE_DRV_GP2);
+ IWL_CMD(CSR_LED_REG);
+ IWL_CMD(CSR_DRAM_INT_TBL_REG);
+ IWL_CMD(CSR_GIO_CHICKEN_BITS);
+ IWL_CMD(CSR_ANA_PLL_CFG);
+ IWL_CMD(CSR_HW_REV_WA_REG);
+ IWL_CMD(CSR_DBG_HPET_MEM_REG);
+ default:
+ return "UNKNOWN";
+ }
+}
+
+void iwl_dump_csr(struct iwl_trans *trans)
+{
+ int i;
+ static const u32 csr_tbl[] = {
+ CSR_HW_IF_CONFIG_REG,
+ CSR_INT_COALESCING,
+ CSR_INT,
+ CSR_INT_MASK,
+ CSR_FH_INT_STATUS,
+ CSR_GPIO_IN,
+ CSR_RESET,
+ CSR_GP_CNTRL,
+ CSR_HW_REV,
+ CSR_EEPROM_REG,
+ CSR_EEPROM_GP,
+ CSR_OTP_GP_REG,
+ CSR_GIO_REG,
+ CSR_GP_UCODE_REG,
+ CSR_GP_DRIVER_REG,
+ CSR_UCODE_DRV_GP1,
+ CSR_UCODE_DRV_GP2,
+ CSR_LED_REG,
+ CSR_DRAM_INT_TBL_REG,
+ CSR_GIO_CHICKEN_BITS,
+ CSR_ANA_PLL_CFG,
+ CSR_HW_REV_WA_REG,
+ CSR_DBG_HPET_MEM_REG
+ };
+ IWL_ERR(trans, "CSR values:\n");
+ IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is "
+ "CSR_INT_PERIODIC_REG)\n");
+ for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) {
+ IWL_ERR(trans, " %25s: 0X%08x\n",
+ get_csr_string(csr_tbl[i]),
+ iwl_read32(bus(trans), csr_tbl[i]));
+ }
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+/* create and remove of files */
+#define DEBUGFS_ADD_FILE(name, parent, mode) do { \
+ if (!debugfs_create_file(#name, mode, parent, trans, \
+ &iwl_dbgfs_##name##_ops)) \
+ return -ENOMEM; \
+} while (0)
+
+/* file operation */
+#define DEBUGFS_READ_FUNC(name) \
+static ssize_t iwl_dbgfs_##name##_read(struct file *file, \
+ char __user *user_buf, \
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_WRITE_FUNC(name) \
+static ssize_t iwl_dbgfs_##name##_write(struct file *file, \
+ const char __user *user_buf, \
+ size_t count, loff_t *ppos);
+
+
+static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+#define DEBUGFS_READ_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name); \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .read = iwl_dbgfs_##name##_read, \
+ .open = iwl_dbgfs_open_file_generic, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_WRITE_FILE_OPS(name) \
+ DEBUGFS_WRITE_FUNC(name); \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = iwl_dbgfs_##name##_write, \
+ .open = iwl_dbgfs_open_file_generic, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name); \
+ DEBUGFS_WRITE_FUNC(name); \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = iwl_dbgfs_##name##_write, \
+ .read = iwl_dbgfs_##name##_read, \
+ .open = iwl_dbgfs_open_file_generic, \
+ .llseek = generic_file_llseek, \
+};
+
+static ssize_t iwl_dbgfs_tx_queue_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tx_queue *txq;
+ struct iwl_queue *q;
+ char *buf;
+ int pos = 0;
+ int cnt;
+ int ret;
+ const size_t bufsz = sizeof(char) * 64 * hw_params(trans).max_txq_num;
+
+ if (!trans_pcie->txq) {
+ IWL_ERR(trans, "txq not ready\n");
+ return -EAGAIN;
+ }
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (cnt = 0; cnt < hw_params(trans).max_txq_num; cnt++) {
+ txq = &trans_pcie->txq[cnt];
+ q = &txq->q;
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "hwq %.2d: read=%u write=%u stop=%d"
+ " swq_id=%#.2x (ac %d/hwq %d)\n",
+ cnt, q->read_ptr, q->write_ptr,
+ !!test_bit(cnt, trans_pcie->queue_stopped),
+ txq->swq_id, txq->swq_id & 3,
+ (txq->swq_id >> 2) & 0x1f);
+ if (cnt >= 4)
+ continue;
+ /* for the ACs, display the stop count too */
+ pos += scnprintf(buf + pos, bufsz - pos,
+ " stop-count: %d\n",
+ atomic_read(&trans_pcie->queue_stop_count[cnt]));
+ }
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+ struct iwl_trans *trans = file->private_data;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rx_queue *rxq = &trans_pcie->rxq;
+ char buf[256];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n",
+ rxq->read);
+ pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n",
+ rxq->write);
+ pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n",
+ rxq->free_count);
+ if (rxq->rb_stts) {
+ pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n",
+ le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF);
+ } else {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "closed_rb_num: Not Allocated\n");
+ }
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_log_event_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ char *buf;
+ int pos = 0;
+ ssize_t ret = -ENOMEM;
+
+ ret = pos = iwl_dump_nic_event_log(trans, true, &buf, true);
+ if (buf) {
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ }
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_log_event_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ u32 event_log_flag;
+ char buf[8];
+ int buf_size;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &event_log_flag) != 1)
+ return -EFAULT;
+ if (event_log_flag == 1)
+ iwl_dump_nic_event_log(trans, true, NULL, false);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_interrupt_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_trans *trans = file->private_data;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
+
+ int pos = 0;
+ char *buf;
+ int bufsz = 24 * 64; /* 24 items * 64 char per item */
+ ssize_t ret;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf) {
+ IWL_ERR(trans, "Can not allocate Buffer\n");
+ return -ENOMEM;
+ }
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Interrupt Statistics Report:\n");
+
+ pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n",
+ isr_stats->hw);
+ pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n",
+ isr_stats->sw);
+ if (isr_stats->sw || isr_stats->hw) {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\tLast Restarting Code: 0x%X\n",
+ isr_stats->err_code);
+ }
+#ifdef CONFIG_IWLWIFI_DEBUG
+ pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n",
+ isr_stats->sch);
+ pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n",
+ isr_stats->alive);
+#endif
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n",
+ isr_stats->ctkill);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n",
+ isr_stats->wakeup);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Rx command responses:\t\t %u\n", isr_stats->rx);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n",
+ isr_stats->tx);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n",
+ isr_stats->unhandled);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_interrupt_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
+
+ char buf[8];
+ int buf_size;
+ u32 reset_flag;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%x", &reset_flag) != 1)
+ return -EFAULT;
+ if (reset_flag == 0)
+ memset(isr_stats, 0, sizeof(*isr_stats));
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_csr_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ char buf[8];
+ int buf_size;
+ int csr;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &csr) != 1)
+ return -EFAULT;
+
+ iwl_dump_csr(trans);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ char *buf;
+ int pos = 0;
+ ssize_t ret = -EFAULT;
+
+ ret = pos = iwl_dump_fh(trans, &buf, true);
+ if (buf) {
+ ret = simple_read_from_buffer(user_buf,
+ count, ppos, buf, pos);
+ kfree(buf);
+ }
+
+ return ret;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(log_event);
+DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
+DEBUGFS_READ_FILE_OPS(fh_reg);
+DEBUGFS_READ_FILE_OPS(rx_queue);
+DEBUGFS_READ_FILE_OPS(tx_queue);
+DEBUGFS_WRITE_FILE_OPS(csr);
+
+/*
+ * Create the debugfs files and directories
+ *
+ */
+static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
+ struct dentry *dir)
+{
+ DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR);
+ DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR);
+ DEBUGFS_ADD_FILE(log_event, dir, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(csr, dir, S_IWUSR);
+ DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR);
+ return 0;
+}
+#else
+static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
+ struct dentry *dir)
+{ return 0; }
+
+#endif /*CONFIG_IWLWIFI_DEBUGFS */
+
+const struct iwl_trans_ops trans_ops_pcie = {
+ .alloc = iwl_trans_pcie_alloc,
+ .request_irq = iwl_trans_pcie_request_irq,
+ .start_device = iwl_trans_pcie_start_device,
+ .prepare_card_hw = iwl_trans_pcie_prepare_card_hw,
+ .stop_device = iwl_trans_pcie_stop_device,
+
+ .tx_start = iwl_trans_pcie_tx_start,
+ .wake_any_queue = iwl_trans_pcie_wake_any_queue,
+
+ .send_cmd = iwl_trans_pcie_send_cmd,
+
+ .tx = iwl_trans_pcie_tx,
+ .reclaim = iwl_trans_pcie_reclaim,
+
+ .tx_agg_disable = iwl_trans_pcie_tx_agg_disable,
+ .tx_agg_alloc = iwl_trans_pcie_tx_agg_alloc,
+ .tx_agg_setup = iwl_trans_pcie_tx_agg_setup,
+
+ .kick_nic = iwl_trans_pcie_kick_nic,
+
+ .free = iwl_trans_pcie_free,
+ .stop_queue = iwl_trans_pcie_stop_queue,
+
+ .dbgfs_register = iwl_trans_pcie_dbgfs_register,
+
+ .wait_tx_queue_empty = iwl_trans_pcie_wait_tx_queue_empty,
+ .check_stuck_queue = iwl_trans_pcie_check_stuck_queue,
+
+#ifdef CONFIG_PM_SLEEP
+ .suspend = iwl_trans_pcie_suspend,
+ .resume = iwl_trans_pcie_resume,
+#endif
+};
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000-hw.h b/drivers/net/wireless/iwlwifi/iwl-trans.c
index b27986e57c92..1b20c4fb791b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.c
@@ -60,22 +60,18 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
-/*
- * Please use this file (iwl-6000-hw.h) only for hardware-related definitions.
- * Use iwl-commands.h for uCode API definitions.
- */
-#ifndef __iwl_6000_hw_h__
-#define __iwl_6000_hw_h__
+#include "iwl-trans.h"
-#define IWL60_RTC_INST_LOWER_BOUND (0x000000)
-#define IWL60_RTC_INST_UPPER_BOUND (0x040000)
-#define IWL60_RTC_DATA_LOWER_BOUND (0x800000)
-#define IWL60_RTC_DATA_UPPER_BOUND (0x814000)
-#define IWL60_RTC_INST_SIZE \
- (IWL60_RTC_INST_UPPER_BOUND - IWL60_RTC_INST_LOWER_BOUND)
-#define IWL60_RTC_DATA_SIZE \
- (IWL60_RTC_DATA_UPPER_BOUND - IWL60_RTC_DATA_LOWER_BOUND)
-
-#endif /* __iwl_6000_hw_h__ */
+int iwl_trans_send_cmd_pdu(struct iwl_trans *trans, u8 id,
+ u32 flags, u16 len, const void *data)
+{
+ struct iwl_host_cmd cmd = {
+ .id = id,
+ .len = { len, },
+ .data = { data, },
+ .flags = flags,
+ };
+ return iwl_trans_send_cmd(trans, &cmd);
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
new file mode 100644
index 000000000000..c5923125c3f9
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -0,0 +1,351 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_trans_h__
+#define __iwl_trans_h__
+
+#include <linux/debugfs.h>
+#include <linux/skbuff.h>
+
+#include "iwl-shared.h"
+#include "iwl-commands.h"
+
+ /*This file includes the declaration that are exported from the transport
+ * layer */
+
+struct iwl_priv;
+struct iwl_shared;
+
+#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
+#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
+#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
+
+enum {
+ CMD_SYNC = 0,
+ CMD_ASYNC = BIT(0),
+ CMD_WANT_SKB = BIT(1),
+ CMD_ON_DEMAND = BIT(2),
+};
+
+#define DEF_CMD_PAYLOAD_SIZE 320
+
+/**
+ * struct iwl_device_cmd
+ *
+ * For allocation of the command and tx queues, this establishes the overall
+ * size of the largest command we send to uCode, except for commands that
+ * aren't fully copied and use other TFD space.
+ */
+struct iwl_device_cmd {
+ struct iwl_cmd_header hdr; /* uCode API */
+ u8 payload[DEF_CMD_PAYLOAD_SIZE];
+} __packed;
+
+#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd))
+
+#define IWL_MAX_CMD_TFDS 2
+
+enum iwl_hcmd_dataflag {
+ IWL_HCMD_DFL_NOCOPY = BIT(0),
+};
+
+/**
+ * struct iwl_host_cmd - Host command to the uCode
+ * @data: array of chunks that composes the data of the host command
+ * @reply_page: pointer to the page that holds the response to the host command
+ * @handler_status: return value of the handler of the command
+ * (put in setup_rx_handlers) - valid for SYNC mode only
+ * @callback:
+ * @flags: can be CMD_* note CMD_WANT_SKB is incompatible withe CMD_ASYNC
+ * @len: array of the lenths of the chunks in data
+ * @dataflags:
+ * @id: id of the host command
+ */
+struct iwl_host_cmd {
+ const void *data[IWL_MAX_CMD_TFDS];
+ unsigned long reply_page;
+ int handler_status;
+
+ u32 flags;
+ u16 len[IWL_MAX_CMD_TFDS];
+ u8 dataflags[IWL_MAX_CMD_TFDS];
+ u8 id;
+};
+
+/**
+ * struct iwl_trans_ops - transport specific operations
+ * @alloc: allocates the meta data (not the queues themselves)
+ * @request_irq: requests IRQ - will be called before the FW load in probe flow
+ * @start_device: allocates and inits all the resources for the transport
+ * layer.
+ * @prepare_card_hw: claim the ownership on the HW. Will be called during
+ * probe.
+ * @tx_start: starts and configures all the Tx fifo - usually done once the fw
+ * is alive.
+ * @wake_any_queue: wake all the queues of a specfic context IWL_RXON_CTX_*
+ * @stop_device:stops the whole device (embedded CPU put to reset)
+ * @send_cmd:send a host command
+ * @tx: send an skb
+ * @reclaim: free packet until ssn. Returns a list of freed packets.
+ * @tx_agg_alloc: allocate resources for a TX BA session
+ * @tx_agg_setup: setup a tx queue for AMPDU - will be called once the HW is
+ * ready and a successful ADDBA response has been received.
+ * @tx_agg_disable: de-configure a Tx queue to send AMPDUs
+ * @kick_nic: remove the RESET from the embedded CPU and let it run
+ * @free: release all the ressource for the transport layer itself such as
+ * irq, tasklet etc...
+ * @stop_queue: stop a specific queue
+ * @check_stuck_queue: check if a specific queue is stuck
+ * @wait_tx_queue_empty: wait until all tx queues are empty
+ * @dbgfs_register: add the dbgfs files under this directory. Files will be
+ * automatically deleted.
+ * @suspend: stop the device unless WoWLAN is configured
+ * @resume: resume activity of the device
+ */
+struct iwl_trans_ops {
+
+ struct iwl_trans *(*alloc)(struct iwl_shared *shrd);
+ int (*request_irq)(struct iwl_trans *iwl_trans);
+ int (*start_device)(struct iwl_trans *trans);
+ int (*prepare_card_hw)(struct iwl_trans *trans);
+ void (*stop_device)(struct iwl_trans *trans);
+ void (*tx_start)(struct iwl_trans *trans);
+
+ void (*wake_any_queue)(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx);
+
+ int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
+
+ int (*tx)(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx,
+ u8 sta_id);
+ void (*reclaim)(struct iwl_trans *trans, int sta_id, int tid,
+ int txq_id, int ssn, u32 status,
+ struct sk_buff_head *skbs);
+
+ int (*tx_agg_disable)(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx, int sta_id,
+ int tid);
+ int (*tx_agg_alloc)(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx, int sta_id, int tid,
+ u16 *ssn);
+ void (*tx_agg_setup)(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx, int sta_id, int tid,
+ int frame_limit);
+
+ void (*kick_nic)(struct iwl_trans *trans);
+
+ void (*free)(struct iwl_trans *trans);
+
+ void (*stop_queue)(struct iwl_trans *trans, int q);
+
+ int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir);
+ int (*check_stuck_queue)(struct iwl_trans *trans, int q);
+ int (*wait_tx_queue_empty)(struct iwl_trans *trans);
+#ifdef CONFIG_PM_SLEEP
+ int (*suspend)(struct iwl_trans *trans);
+ int (*resume)(struct iwl_trans *trans);
+#endif
+};
+
+/**
+ * struct iwl_trans - transport common data
+ * @ops - pointer to iwl_trans_ops
+ * @shrd - pointer to iwl_shared which holds shared data from the upper layer
+ * @hcmd_lock: protects HCMD
+ */
+struct iwl_trans {
+ const struct iwl_trans_ops *ops;
+ struct iwl_shared *shrd;
+ spinlock_t hcmd_lock;
+
+ /* pointer to trans specific struct */
+ /*Ensure that this pointer will always be aligned to sizeof pointer */
+ char trans_specific[0] __attribute__((__aligned__(sizeof(void *))));
+};
+
+static inline int iwl_trans_request_irq(struct iwl_trans *trans)
+{
+ return trans->ops->request_irq(trans);
+}
+
+static inline int iwl_trans_start_device(struct iwl_trans *trans)
+{
+ return trans->ops->start_device(trans);
+}
+
+static inline int iwl_trans_prepare_card_hw(struct iwl_trans *trans)
+{
+ return trans->ops->prepare_card_hw(trans);
+}
+
+static inline void iwl_trans_stop_device(struct iwl_trans *trans)
+{
+ trans->ops->stop_device(trans);
+}
+
+static inline void iwl_trans_tx_start(struct iwl_trans *trans)
+{
+ trans->ops->tx_start(trans);
+}
+
+static inline void iwl_trans_wake_any_queue(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx)
+{
+ trans->ops->wake_any_queue(trans, ctx);
+}
+
+
+static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
+ struct iwl_host_cmd *cmd)
+{
+ return trans->ops->send_cmd(trans, cmd);
+}
+
+int iwl_trans_send_cmd_pdu(struct iwl_trans *trans, u8 id,
+ u32 flags, u16 len, const void *data);
+
+static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx,
+ u8 sta_id)
+{
+ return trans->ops->tx(trans, skb, dev_cmd, ctx, sta_id);
+}
+
+static inline void iwl_trans_reclaim(struct iwl_trans *trans, int sta_id,
+ int tid, int txq_id, int ssn, u32 status,
+ struct sk_buff_head *skbs)
+{
+ trans->ops->reclaim(trans, sta_id, tid, txq_id, ssn, status, skbs);
+}
+
+static inline int iwl_trans_tx_agg_disable(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx,
+ int sta_id, int tid)
+{
+ return trans->ops->tx_agg_disable(trans, ctx, sta_id, tid);
+}
+
+static inline int iwl_trans_tx_agg_alloc(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx,
+ int sta_id, int tid, u16 *ssn)
+{
+ return trans->ops->tx_agg_alloc(trans, ctx, sta_id, tid, ssn);
+}
+
+
+static inline void iwl_trans_tx_agg_setup(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx,
+ int sta_id, int tid,
+ int frame_limit)
+{
+ trans->ops->tx_agg_setup(trans, ctx, sta_id, tid, frame_limit);
+}
+
+static inline void iwl_trans_kick_nic(struct iwl_trans *trans)
+{
+ trans->ops->kick_nic(trans);
+}
+
+static inline void iwl_trans_free(struct iwl_trans *trans)
+{
+ trans->ops->free(trans);
+}
+
+static inline void iwl_trans_stop_queue(struct iwl_trans *trans, int q)
+{
+ trans->ops->stop_queue(trans, q);
+}
+
+static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans)
+{
+ return trans->ops->wait_tx_queue_empty(trans);
+}
+
+static inline int iwl_trans_check_stuck_queue(struct iwl_trans *trans, int q)
+{
+ return trans->ops->check_stuck_queue(trans, q);
+}
+static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans,
+ struct dentry *dir)
+{
+ return trans->ops->dbgfs_register(trans, dir);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static inline int iwl_trans_suspend(struct iwl_trans *trans)
+{
+ return trans->ops->suspend(trans);
+}
+
+static inline int iwl_trans_resume(struct iwl_trans *trans)
+{
+ return trans->ops->resume(trans);
+}
+#endif
+
+/*****************************************************
+* Transport layers implementations
+******************************************************/
+extern const struct iwl_trans_ops trans_ops_pcie;
+
+#endif /* __iwl_trans_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
deleted file mode 100644
index 137dba95b1ad..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ /dev/null
@@ -1,829 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/etherdevice.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <net/mac80211.h>
-#include "iwl-eeprom.h"
-#include "iwl-agn.h"
-#include "iwl-dev.h"
-#include "iwl-core.h"
-#include "iwl-sta.h"
-#include "iwl-io.h"
-#include "iwl-helpers.h"
-
-/**
- * iwl_txq_update_write_ptr - Send new write index to hardware
- */
-void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq)
-{
- u32 reg = 0;
- int txq_id = txq->q.id;
-
- if (txq->need_update == 0)
- return;
-
- if (priv->cfg->base_params->shadow_reg_enable) {
- /* shadow register enabled */
- iwl_write32(priv, HBUS_TARG_WRPTR,
- txq->q.write_ptr | (txq_id << 8));
- } else {
- /* if we're trying to save power */
- if (test_bit(STATUS_POWER_PMI, &priv->status)) {
- /* wake up nic if it's powered down ...
- * uCode will wake up, and interrupt us again, so next
- * time we'll skip this part. */
- reg = iwl_read32(priv, CSR_UCODE_DRV_GP1);
-
- if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
- IWL_DEBUG_INFO(priv,
- "Tx queue %d requesting wakeup,"
- " GP1 = 0x%x\n", txq_id, reg);
- iwl_set_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- return;
- }
-
- iwl_write_direct32(priv, HBUS_TARG_WRPTR,
- txq->q.write_ptr | (txq_id << 8));
-
- /*
- * else not in power-save mode,
- * uCode will never sleep when we're
- * trying to tx (during RFKILL, we're not trying to tx).
- */
- } else
- iwl_write32(priv, HBUS_TARG_WRPTR,
- txq->q.write_ptr | (txq_id << 8));
- }
- txq->need_update = 0;
-}
-
-static inline dma_addr_t iwl_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
-{
- struct iwl_tfd_tb *tb = &tfd->tbs[idx];
-
- dma_addr_t addr = get_unaligned_le32(&tb->lo);
- if (sizeof(dma_addr_t) > sizeof(u32))
- addr |=
- ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16;
-
- return addr;
-}
-
-static inline u16 iwl_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
-{
- struct iwl_tfd_tb *tb = &tfd->tbs[idx];
-
- return le16_to_cpu(tb->hi_n_len) >> 4;
-}
-
-static inline void iwl_tfd_set_tb(struct iwl_tfd *tfd, u8 idx,
- dma_addr_t addr, u16 len)
-{
- struct iwl_tfd_tb *tb = &tfd->tbs[idx];
- u16 hi_n_len = len << 4;
-
- put_unaligned_le32(addr, &tb->lo);
- if (sizeof(dma_addr_t) > sizeof(u32))
- hi_n_len |= ((addr >> 16) >> 16) & 0xF;
-
- tb->hi_n_len = cpu_to_le16(hi_n_len);
-
- tfd->num_tbs = idx + 1;
-}
-
-static inline u8 iwl_tfd_get_num_tbs(struct iwl_tfd *tfd)
-{
- return tfd->num_tbs & 0x1f;
-}
-
-static void iwlagn_unmap_tfd(struct iwl_priv *priv, struct iwl_cmd_meta *meta,
- struct iwl_tfd *tfd, int dma_dir)
-{
- struct pci_dev *dev = priv->pci_dev;
- int i;
- int num_tbs;
-
- /* Sanity check on number of chunks */
- num_tbs = iwl_tfd_get_num_tbs(tfd);
-
- if (num_tbs >= IWL_NUM_OF_TBS) {
- IWL_ERR(priv, "Too many chunks: %i\n", num_tbs);
- /* @todo issue fatal error, it is quite serious situation */
- return;
- }
-
- /* Unmap tx_cmd */
- if (num_tbs)
- pci_unmap_single(dev,
- dma_unmap_addr(meta, mapping),
- dma_unmap_len(meta, len),
- PCI_DMA_BIDIRECTIONAL);
-
- /* Unmap chunks, if any. */
- for (i = 1; i < num_tbs; i++)
- pci_unmap_single(dev, iwl_tfd_tb_get_addr(tfd, i),
- iwl_tfd_tb_get_len(tfd, i), dma_dir);
-}
-
-/**
- * iwlagn_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr]
- * @priv - driver private data
- * @txq - tx queue
- *
- * Does NOT advance any TFD circular buffer read/write indexes
- * Does NOT free the TFD itself (which is within circular buffer)
- */
-void iwlagn_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
-{
- struct iwl_tfd *tfd_tmp = txq->tfds;
- int index = txq->q.read_ptr;
-
- iwlagn_unmap_tfd(priv, &txq->meta[index], &tfd_tmp[index],
- PCI_DMA_TODEVICE);
-
- /* free SKB */
- if (txq->txb) {
- struct sk_buff *skb;
-
- skb = txq->txb[txq->q.read_ptr].skb;
-
- /* can be called from irqs-disabled context */
- if (skb) {
- dev_kfree_skb_any(skb);
- txq->txb[txq->q.read_ptr].skb = NULL;
- }
- }
-}
-
-int iwlagn_txq_attach_buf_to_tfd(struct iwl_priv *priv,
- struct iwl_tx_queue *txq,
- dma_addr_t addr, u16 len,
- u8 reset)
-{
- struct iwl_queue *q;
- struct iwl_tfd *tfd, *tfd_tmp;
- u32 num_tbs;
-
- q = &txq->q;
- tfd_tmp = txq->tfds;
- tfd = &tfd_tmp[q->write_ptr];
-
- if (reset)
- memset(tfd, 0, sizeof(*tfd));
-
- num_tbs = iwl_tfd_get_num_tbs(tfd);
-
- /* Each TFD can point to a maximum 20 Tx buffers */
- if (num_tbs >= IWL_NUM_OF_TBS) {
- IWL_ERR(priv, "Error can not send more than %d chunks\n",
- IWL_NUM_OF_TBS);
- return -EINVAL;
- }
-
- if (WARN_ON(addr & ~DMA_BIT_MASK(36)))
- return -EINVAL;
-
- if (unlikely(addr & ~IWL_TX_DMA_MASK))
- IWL_ERR(priv, "Unaligned address = %llx\n",
- (unsigned long long)addr);
-
- iwl_tfd_set_tb(tfd, num_tbs, addr, len);
-
- return 0;
-}
-
-/*
- * Tell nic where to find circular buffer of Tx Frame Descriptors for
- * given Tx queue, and enable the DMA channel used for that queue.
- *
- * supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA
- * channels supported in hardware.
- */
-static int iwlagn_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq)
-{
- int txq_id = txq->q.id;
-
- /* Circular buffer (TFD queue in DRAM) physical base address */
- iwl_write_direct32(priv, FH_MEM_CBBC_QUEUE(txq_id),
- txq->q.dma_addr >> 8);
-
- return 0;
-}
-
-/**
- * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's
- */
-void iwl_tx_queue_unmap(struct iwl_priv *priv, int txq_id)
-{
- struct iwl_tx_queue *txq = &priv->txq[txq_id];
- struct iwl_queue *q = &txq->q;
-
- if (q->n_bd == 0)
- return;
-
- while (q->write_ptr != q->read_ptr) {
- iwlagn_txq_free_tfd(priv, txq);
- q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
- }
-}
-
-/**
- * iwl_tx_queue_free - Deallocate DMA queue.
- * @txq: Transmit queue to deallocate.
- *
- * Empty queue by removing and destroying all BD's.
- * Free all buffers.
- * 0-fill, but do not free "txq" descriptor structure.
- */
-void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id)
-{
- struct iwl_tx_queue *txq = &priv->txq[txq_id];
- struct device *dev = &priv->pci_dev->dev;
- int i;
-
- iwl_tx_queue_unmap(priv, txq_id);
-
- /* De-alloc array of command/tx buffers */
- for (i = 0; i < TFD_TX_CMD_SLOTS; i++)
- kfree(txq->cmd[i]);
-
- /* De-alloc circular buffer of TFDs */
- if (txq->q.n_bd)
- dma_free_coherent(dev, priv->hw_params.tfd_size *
- txq->q.n_bd, txq->tfds, txq->q.dma_addr);
-
- /* De-alloc array of per-TFD driver data */
- kfree(txq->txb);
- txq->txb = NULL;
-
- /* deallocate arrays */
- kfree(txq->cmd);
- kfree(txq->meta);
- txq->cmd = NULL;
- txq->meta = NULL;
-
- /* 0-fill queue descriptor structure */
- memset(txq, 0, sizeof(*txq));
-}
-
-/**
- * iwl_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue
- */
-void iwl_cmd_queue_unmap(struct iwl_priv *priv)
-{
- struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue];
- struct iwl_queue *q = &txq->q;
- int i;
-
- if (q->n_bd == 0)
- return;
-
- while (q->read_ptr != q->write_ptr) {
- i = get_cmd_index(q, q->read_ptr);
-
- if (txq->meta[i].flags & CMD_MAPPED) {
- iwlagn_unmap_tfd(priv, &txq->meta[i], &txq->tfds[i],
- PCI_DMA_BIDIRECTIONAL);
- txq->meta[i].flags = 0;
- }
-
- q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
- }
-}
-
-/**
- * iwl_cmd_queue_free - Deallocate DMA queue.
- * @txq: Transmit queue to deallocate.
- *
- * Empty queue by removing and destroying all BD's.
- * Free all buffers.
- * 0-fill, but do not free "txq" descriptor structure.
- */
-void iwl_cmd_queue_free(struct iwl_priv *priv)
-{
- struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue];
- struct device *dev = &priv->pci_dev->dev;
- int i;
-
- iwl_cmd_queue_unmap(priv);
-
- /* De-alloc array of command/tx buffers */
- for (i = 0; i < TFD_CMD_SLOTS; i++)
- kfree(txq->cmd[i]);
-
- /* De-alloc circular buffer of TFDs */
- if (txq->q.n_bd)
- dma_free_coherent(dev, priv->hw_params.tfd_size * txq->q.n_bd,
- txq->tfds, txq->q.dma_addr);
-
- /* deallocate arrays */
- kfree(txq->cmd);
- kfree(txq->meta);
- txq->cmd = NULL;
- txq->meta = NULL;
-
- /* 0-fill queue descriptor structure */
- memset(txq, 0, sizeof(*txq));
-}
-
-/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
- * DMA services
- *
- * Theory of operation
- *
- * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer
- * of buffer descriptors, each of which points to one or more data buffers for
- * the device to read from or fill. Driver and device exchange status of each
- * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty
- * entries in each circular buffer, to protect against confusing empty and full
- * queue states.
- *
- * The device reads or writes the data in the queues via the device's several
- * DMA/FIFO channels. Each queue is mapped to a single DMA channel.
- *
- * For Tx queue, there are low mark and high mark limits. If, after queuing
- * the packet for Tx, free space become < low mark, Tx queue stopped. When
- * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
- * Tx queue resumed.
- *
- ***************************************************/
-
-int iwl_queue_space(const struct iwl_queue *q)
-{
- int s = q->read_ptr - q->write_ptr;
-
- if (q->read_ptr > q->write_ptr)
- s -= q->n_bd;
-
- if (s <= 0)
- s += q->n_window;
- /* keep some reserve to not confuse empty and full situations */
- s -= 2;
- if (s < 0)
- s = 0;
- return s;
-}
-
-
-/**
- * iwl_queue_init - Initialize queue's high/low-water and read/write indexes
- */
-static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
- int count, int slots_num, u32 id)
-{
- q->n_bd = count;
- q->n_window = slots_num;
- q->id = id;
-
- /* count must be power-of-two size, otherwise iwl_queue_inc_wrap
- * and iwl_queue_dec_wrap are broken. */
- if (WARN_ON(!is_power_of_2(count)))
- return -EINVAL;
-
- /* slots_num must be power-of-two size, otherwise
- * get_cmd_index is broken. */
- if (WARN_ON(!is_power_of_2(slots_num)))
- return -EINVAL;
-
- q->low_mark = q->n_window / 4;
- if (q->low_mark < 4)
- q->low_mark = 4;
-
- q->high_mark = q->n_window / 8;
- if (q->high_mark < 2)
- q->high_mark = 2;
-
- q->write_ptr = q->read_ptr = 0;
-
- return 0;
-}
-
-/**
- * iwl_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue
- */
-static int iwl_tx_queue_alloc(struct iwl_priv *priv,
- struct iwl_tx_queue *txq, u32 id)
-{
- struct device *dev = &priv->pci_dev->dev;
- size_t tfd_sz = priv->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX;
-
- /* Driver private data, only for Tx (not command) queues,
- * not shared with device. */
- if (id != priv->cmd_queue) {
- txq->txb = kzalloc(sizeof(txq->txb[0]) *
- TFD_QUEUE_SIZE_MAX, GFP_KERNEL);
- if (!txq->txb) {
- IWL_ERR(priv, "kmalloc for auxiliary BD "
- "structures failed\n");
- goto error;
- }
- } else {
- txq->txb = NULL;
- }
-
- /* Circular buffer of transmit frame descriptors (TFDs),
- * shared with device */
- txq->tfds = dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr,
- GFP_KERNEL);
- if (!txq->tfds) {
- IWL_ERR(priv, "pci_alloc_consistent(%zd) failed\n", tfd_sz);
- goto error;
- }
- txq->q.id = id;
-
- return 0;
-
- error:
- kfree(txq->txb);
- txq->txb = NULL;
-
- return -ENOMEM;
-}
-
-/**
- * iwl_tx_queue_init - Allocate and initialize one tx/cmd queue
- */
-int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq,
- int slots_num, u32 txq_id)
-{
- int i, len;
- int ret;
-
- txq->meta = kzalloc(sizeof(struct iwl_cmd_meta) * slots_num,
- GFP_KERNEL);
- txq->cmd = kzalloc(sizeof(struct iwl_device_cmd *) * slots_num,
- GFP_KERNEL);
-
- if (!txq->meta || !txq->cmd)
- goto out_free_arrays;
-
- len = sizeof(struct iwl_device_cmd);
- for (i = 0; i < slots_num; i++) {
- txq->cmd[i] = kmalloc(len, GFP_KERNEL);
- if (!txq->cmd[i])
- goto err;
- }
-
- /* Alloc driver data array and TFD circular buffer */
- ret = iwl_tx_queue_alloc(priv, txq, txq_id);
- if (ret)
- goto err;
-
- txq->need_update = 0;
-
- /*
- * For the default queues 0-3, set up the swq_id
- * already -- all others need to get one later
- * (if they need one at all).
- */
- if (txq_id < 4)
- iwl_set_swq_id(txq, txq_id, txq_id);
-
- /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
- * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
- BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
-
- /* Initialize queue's high/low-water marks, and head/tail indexes */
- ret = iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id);
- if (ret)
- return ret;
-
- /* Tell device where to find queue */
- iwlagn_tx_queue_init(priv, txq);
-
- return 0;
-err:
- for (i = 0; i < slots_num; i++)
- kfree(txq->cmd[i]);
-out_free_arrays:
- kfree(txq->meta);
- kfree(txq->cmd);
-
- return -ENOMEM;
-}
-
-void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq,
- int slots_num, u32 txq_id)
-{
- memset(txq->meta, 0, sizeof(struct iwl_cmd_meta) * slots_num);
-
- txq->need_update = 0;
-
- /* Initialize queue's high/low-water marks, and head/tail indexes */
- iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id);
-
- /* Tell device where to find queue */
- iwlagn_tx_queue_init(priv, txq);
-}
-
-/*************** HOST COMMAND QUEUE FUNCTIONS *****/
-
-/**
- * iwl_enqueue_hcmd - enqueue a uCode command
- * @priv: device private data point
- * @cmd: a point to the ucode command structure
- *
- * The function returns < 0 values to indicate the operation is
- * failed. On success, it turns the index (> 0) of command in the
- * command queue.
- */
-int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
-{
- struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue];
- struct iwl_queue *q = &txq->q;
- struct iwl_device_cmd *out_cmd;
- struct iwl_cmd_meta *out_meta;
- dma_addr_t phys_addr;
- unsigned long flags;
- u32 idx;
- u16 copy_size, cmd_size;
- bool is_ct_kill = false;
- bool had_nocopy = false;
- int i;
- u8 *cmd_dest;
-#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
- const void *trace_bufs[IWL_MAX_CMD_TFDS + 1] = {};
- int trace_lens[IWL_MAX_CMD_TFDS + 1] = {};
- int trace_idx;
-#endif
-
- if (test_bit(STATUS_FW_ERROR, &priv->status)) {
- IWL_WARN(priv, "fw recovery, no hcmd send\n");
- return -EIO;
- }
-
- copy_size = sizeof(out_cmd->hdr);
- cmd_size = sizeof(out_cmd->hdr);
-
- /* need one for the header if the first is NOCOPY */
- BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1);
-
- for (i = 0; i < IWL_MAX_CMD_TFDS; i++) {
- if (!cmd->len[i])
- continue;
- if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) {
- had_nocopy = true;
- } else {
- /* NOCOPY must not be followed by normal! */
- if (WARN_ON(had_nocopy))
- return -EINVAL;
- copy_size += cmd->len[i];
- }
- cmd_size += cmd->len[i];
- }
-
- /*
- * If any of the command structures end up being larger than
- * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically
- * allocated into separate TFDs, then we will need to
- * increase the size of the buffers.
- */
- if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE))
- return -EINVAL;
-
- if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) {
- IWL_WARN(priv, "Not sending command - %s KILL\n",
- iwl_is_rfkill(priv) ? "RF" : "CT");
- return -EIO;
- }
-
- spin_lock_irqsave(&priv->hcmd_lock, flags);
-
- if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
- spin_unlock_irqrestore(&priv->hcmd_lock, flags);
-
- IWL_ERR(priv, "No space in command queue\n");
- is_ct_kill = iwl_check_for_ct_kill(priv);
- if (!is_ct_kill) {
- IWL_ERR(priv, "Restarting adapter due to queue full\n");
- iwlagn_fw_error(priv, false);
- }
- return -ENOSPC;
- }
-
- idx = get_cmd_index(q, q->write_ptr);
- out_cmd = txq->cmd[idx];
- out_meta = &txq->meta[idx];
-
- if (WARN_ON(out_meta->flags & CMD_MAPPED)) {
- spin_unlock_irqrestore(&priv->hcmd_lock, flags);
- return -ENOSPC;
- }
-
- memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */
- if (cmd->flags & CMD_WANT_SKB)
- out_meta->source = cmd;
- if (cmd->flags & CMD_ASYNC)
- out_meta->callback = cmd->callback;
-
- /* set up the header */
-
- out_cmd->hdr.cmd = cmd->id;
- out_cmd->hdr.flags = 0;
- out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(priv->cmd_queue) |
- INDEX_TO_SEQ(q->write_ptr));
-
- /* and copy the data that needs to be copied */
-
- cmd_dest = &out_cmd->cmd.payload[0];
- for (i = 0; i < IWL_MAX_CMD_TFDS; i++) {
- if (!cmd->len[i])
- continue;
- if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)
- break;
- memcpy(cmd_dest, cmd->data[i], cmd->len[i]);
- cmd_dest += cmd->len[i];
- }
-
- IWL_DEBUG_HC(priv, "Sending command %s (#%x), seq: 0x%04X, "
- "%d bytes at %d[%d]:%d\n",
- get_cmd_string(out_cmd->hdr.cmd),
- out_cmd->hdr.cmd,
- le16_to_cpu(out_cmd->hdr.sequence), cmd_size,
- q->write_ptr, idx, priv->cmd_queue);
-
- phys_addr = pci_map_single(priv->pci_dev, &out_cmd->hdr,
- copy_size, PCI_DMA_BIDIRECTIONAL);
- if (unlikely(pci_dma_mapping_error(priv->pci_dev, phys_addr))) {
- idx = -ENOMEM;
- goto out;
- }
-
- dma_unmap_addr_set(out_meta, mapping, phys_addr);
- dma_unmap_len_set(out_meta, len, copy_size);
-
- iwlagn_txq_attach_buf_to_tfd(priv, txq, phys_addr, copy_size, 1);
-#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
- trace_bufs[0] = &out_cmd->hdr;
- trace_lens[0] = copy_size;
- trace_idx = 1;
-#endif
-
- for (i = 0; i < IWL_MAX_CMD_TFDS; i++) {
- if (!cmd->len[i])
- continue;
- if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY))
- continue;
- phys_addr = pci_map_single(priv->pci_dev, (void *)cmd->data[i],
- cmd->len[i], PCI_DMA_BIDIRECTIONAL);
- if (pci_dma_mapping_error(priv->pci_dev, phys_addr)) {
- iwlagn_unmap_tfd(priv, out_meta,
- &txq->tfds[q->write_ptr],
- PCI_DMA_BIDIRECTIONAL);
- idx = -ENOMEM;
- goto out;
- }
-
- iwlagn_txq_attach_buf_to_tfd(priv, txq, phys_addr,
- cmd->len[i], 0);
-#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
- trace_bufs[trace_idx] = cmd->data[i];
- trace_lens[trace_idx] = cmd->len[i];
- trace_idx++;
-#endif
- }
-
- out_meta->flags = cmd->flags | CMD_MAPPED;
-
- txq->need_update = 1;
-
- /* check that tracing gets all possible blocks */
- BUILD_BUG_ON(IWL_MAX_CMD_TFDS + 1 != 3);
-#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
- trace_iwlwifi_dev_hcmd(priv, cmd->flags,
- trace_bufs[0], trace_lens[0],
- trace_bufs[1], trace_lens[1],
- trace_bufs[2], trace_lens[2]);
-#endif
-
- /* Increment and update queue's write index */
- q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
- iwl_txq_update_write_ptr(priv, txq);
-
- out:
- spin_unlock_irqrestore(&priv->hcmd_lock, flags);
- return idx;
-}
-
-/**
- * iwl_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd
- *
- * When FW advances 'R' index, all entries between old and new 'R' index
- * need to be reclaimed. As result, some free space forms. If there is
- * enough free space (> low mark), wake the stack that feeds us.
- */
-static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, int idx)
-{
- struct iwl_tx_queue *txq = &priv->txq[txq_id];
- struct iwl_queue *q = &txq->q;
- int nfreed = 0;
-
- if ((idx >= q->n_bd) || (iwl_queue_used(q, idx) == 0)) {
- IWL_ERR(priv, "Read index for DMA queue txq id (%d), index %d, "
- "is out of range [0-%d] %d %d.\n", txq_id,
- idx, q->n_bd, q->write_ptr, q->read_ptr);
- return;
- }
-
- 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)) {
-
- if (nfreed++ > 0) {
- IWL_ERR(priv, "HCMD skipped: index (%d) %d %d\n", idx,
- q->write_ptr, q->read_ptr);
- iwlagn_fw_error(priv, false);
- }
-
- }
-}
-
-/**
- * iwl_tx_cmd_complete - Pull unused buffers off the queue and reclaim them
- * @rxb: Rx buffer to reclaim
- *
- * If an Rx buffer has an async callback associated with it the callback
- * will be executed. The attached skb (if present) will only be freed
- * if the callback returns 1
- */
-void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- u16 sequence = le16_to_cpu(pkt->hdr.sequence);
- int txq_id = SEQ_TO_QUEUE(sequence);
- int index = SEQ_TO_INDEX(sequence);
- int cmd_index;
- struct iwl_device_cmd *cmd;
- struct iwl_cmd_meta *meta;
- struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue];
- unsigned long flags;
-
- /* If a Tx command is being handled and it isn't in the actual
- * command queue then there a command routing bug has been introduced
- * in the queue management code. */
- if (WARN(txq_id != priv->cmd_queue,
- "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n",
- txq_id, priv->cmd_queue, sequence,
- priv->txq[priv->cmd_queue].q.read_ptr,
- priv->txq[priv->cmd_queue].q.write_ptr)) {
- iwl_print_hex_error(priv, pkt, 32);
- return;
- }
-
- cmd_index = get_cmd_index(&txq->q, index);
- cmd = txq->cmd[cmd_index];
- meta = &txq->meta[cmd_index];
-
- iwlagn_unmap_tfd(priv, meta, &txq->tfds[index], PCI_DMA_BIDIRECTIONAL);
-
- /* Input error checking is done when commands are added to queue. */
- if (meta->flags & CMD_WANT_SKB) {
- meta->source->reply_page = (unsigned long)rxb_addr(rxb);
- rxb->page = NULL;
- } else if (meta->callback)
- meta->callback(priv, cmd, pkt);
-
- spin_lock_irqsave(&priv->hcmd_lock, flags);
-
- iwl_hcmd_queue_reclaim(priv, txq_id, index);
-
- if (!(meta->flags & CMD_ASYNC)) {
- clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
- IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s\n",
- get_cmd_string(cmd->hdr.cmd));
- wake_up_interruptible(&priv->wait_command_queue);
- }
-
- /* Mark as unmapped */
- meta->flags = 0;
-
- spin_unlock_irqrestore(&priv->hcmd_lock, flags);
-}
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index ed57e4402800..c42be81e979e 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -187,13 +187,17 @@ static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
struct key_params*))
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
- struct iwm_key *key = &iwm->keys[key_index];
+ struct iwm_key *key;
struct key_params params;
IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index);
+ if (key_index >= IWM_NUM_KEYS)
+ return -ENOENT;
+
memset(&params, 0, sizeof(params));
+ key = &iwm->keys[key_index];
params.cipher = key->cipher;
params.key_len = key->key_len;
params.seq_len = key->seq_len;
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
index 50dee6a0a5ca..bd75078c454b 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.c
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -42,6 +42,7 @@
#include <linux/ieee80211.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/moduleparam.h>
#include "iwm.h"
#include "bus.h"
diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c
index 0a0cc9667cd6..87eef5773a02 100644
--- a/drivers/net/wireless/iwmc3200wifi/debugfs.c
+++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c
@@ -25,6 +25,7 @@
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/debugfs.h>
+#include <linux/export.h>
#include "iwm.h"
#include "bus.h"
diff --git a/drivers/net/wireless/iwmc3200wifi/fw.c b/drivers/net/wireless/iwmc3200wifi/fw.c
index 49067092d336..6f1afe6bbc8c 100644
--- a/drivers/net/wireless/iwmc3200wifi/fw.c
+++ b/drivers/net/wireless/iwmc3200wifi/fw.c
@@ -187,7 +187,7 @@ static int iwm_load_img(struct iwm_priv *iwm, const char *img_name)
if (ret < 0)
goto err_release_fw;
opcode_idx++;
- };
+ }
/* Read firmware version */
fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_SW_VER, 0);
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index 362002735b12..98a179f98ea1 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -42,6 +42,7 @@
#include <linux/ieee80211.h>
#include <linux/wireless.h>
#include <linux/slab.h>
+#include <linux/moduleparam.h>
#include "iwm.h"
#include "debug.h"
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
index 56383e7be835..764b40dd24ad 100644
--- a/drivers/net/wireless/iwmc3200wifi/sdio.c
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
@@ -63,6 +63,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/debugfs.h>
diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
index 1453eec82a99..91f2ca90c70f 100644
--- a/drivers/net/wireless/libertas/README
+++ b/drivers/net/wireless/libertas/README
@@ -238,28 +238,3 @@ hostsleep
echo "1" > hostsleep : enable host sleep.
echo "0" > hostsleep : disable host sleep
-========================
-IWCONFIG COMMANDS
-========================
-power period
-
- This command is used to configure the station in deep sleep mode /
- auto deep sleep mode.
-
- The timer is implemented to monitor the activities (command, event,
- etc.). When an activity is detected station will exit from deep
- sleep mode automatically and restart the timer. At timer expiry
- (no activity for defined time period) the deep sleep mode is entered
- automatically.
-
- Note: this command is for SDIO interface only.
-
- Usage:
- To enable deep sleep mode do:
- iwconfig wlan0 power period 0
- To enable auto deep sleep mode with idle time period 5 seconds do:
- iwconfig wlan0 power period 5
- To disable deep sleep/auto deep sleep mode do:
- iwconfig wlan0 power period -1
-
-==============================================================================
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 5d637af2d7c3..a7f1ab28940d 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -8,6 +8,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/hardirq.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/slab.h>
@@ -18,6 +19,7 @@
#include "decl.h"
#include "cfg.h"
#include "cmd.h"
+#include "mesh.h"
#define CHAN2G(_channel, _freq, _flags) { \
@@ -441,13 +443,16 @@ static int lbs_cfg_set_channel(struct wiphy *wiphy,
struct lbs_private *priv = wiphy_priv(wiphy);
int ret = -ENOTSUPP;
- lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d",
- channel->center_freq, channel_type);
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d, type %d",
+ netdev_name(netdev), channel->center_freq, channel_type);
if (channel_type != NL80211_CHAN_NO_HT)
goto out;
- ret = lbs_set_channel(priv, channel->hw_value);
+ if (netdev == priv->mesh_dev)
+ ret = lbs_mesh_set_channel(priv, channel->hw_value);
+ else
+ ret = lbs_set_channel(priv, channel->hw_value);
out:
lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
@@ -629,7 +634,7 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
if (channel &&
!(channel->flags & IEEE80211_CHAN_DISABLED))
cfg80211_inform_bss(wiphy, channel,
- bssid, le64_to_cpu(*(__le64 *)tsfdesc),
+ bssid, get_unaligned_le64(tsfdesc),
capa, intvl, ie, ielen,
LBS_SCAN_RSSI_TO_MBM(rssi),
GFP_KERNEL);
@@ -690,7 +695,7 @@ static void lbs_scan_worker(struct work_struct *work)
tlv = scan_cmd->tlvbuffer;
/* add SSID TLV */
- if (priv->scan_req->n_ssids)
+ if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0)
tlv += lbs_add_ssid_tlv(tlv,
priv->scan_req->ssids[0].ssid,
priv->scan_req->ssids[0].ssid_len);
@@ -707,7 +712,7 @@ static void lbs_scan_worker(struct work_struct *work)
if (priv->scan_channel < priv->scan_req->n_channels) {
cancel_delayed_work(&priv->scan_work);
- if (!priv->stopping)
+ if (netif_running(priv->dev))
queue_delayed_work(priv->work_thread, &priv->scan_work,
msecs_to_jiffies(300));
}
@@ -723,16 +728,9 @@ static void lbs_scan_worker(struct work_struct *work)
le16_to_cpu(scan_cmd->hdr.size),
lbs_ret_scan, 0);
- if (priv->scan_channel >= priv->scan_req->n_channels) {
+ if (priv->scan_channel >= priv->scan_req->n_channels)
/* Mark scan done */
- if (priv->internal_scan)
- kfree(priv->scan_req);
- else
- cfg80211_scan_done(priv->scan_req, false);
-
- priv->scan_req = NULL;
- priv->last_scan = jiffies;
- }
+ lbs_scan_done(priv);
/* Restart network */
if (carrier)
@@ -770,6 +768,21 @@ static void _internal_start_scan(struct lbs_private *priv, bool internal,
lbs_deb_leave(LBS_DEB_CFG80211);
}
+/*
+ * Clean up priv->scan_req. Should be used to handle the allocation details.
+ */
+void lbs_scan_done(struct lbs_private *priv)
+{
+ WARN_ON(!priv->scan_req);
+
+ if (priv->internal_scan)
+ kfree(priv->scan_req);
+ else
+ cfg80211_scan_done(priv->scan_req, false);
+
+ priv->scan_req = NULL;
+}
+
static int lbs_cfg_scan(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_scan_request *request)
@@ -1291,27 +1304,32 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
int ret = 0;
u8 preamble = RADIO_PREAMBLE_SHORT;
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);
if (!sme->bssid) {
- /* Run a scan if one isn't in-progress already and if the last
- * scan was done more than 2 seconds ago.
- */
- if (priv->scan_req == NULL &&
- time_after(jiffies, priv->last_scan + (2 * HZ))) {
- struct cfg80211_scan_request *creq;
+ struct cfg80211_scan_request *creq;
- creq = _new_connect_scan_req(wiphy, sme);
- if (!creq) {
- ret = -EINVAL;
- goto done;
- }
+ /*
+ * Scan for the requested network after waiting for existing
+ * scans to finish.
+ */
+ lbs_deb_assoc("assoc: waiting for existing scans\n");
+ wait_event_interruptible_timeout(priv->scan_q,
+ (priv->scan_req == NULL),
+ (15 * HZ));
- lbs_deb_assoc("assoc: scanning for compatible AP\n");
- _internal_start_scan(priv, true, creq);
+ creq = _new_connect_scan_req(wiphy, sme);
+ if (!creq) {
+ ret = -EINVAL;
+ goto done;
}
- /* Wait for any in-progress scan to complete */
+ lbs_deb_assoc("assoc: scanning for compatible AP\n");
+ _internal_start_scan(priv, true, creq);
+
lbs_deb_assoc("assoc: waiting for scan to complete\n");
wait_event_interruptible_timeout(priv->scan_q,
(priv->scan_req == NULL),
@@ -1401,28 +1419,23 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
return ret;
}
-static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
- u16 reason_code)
+int lbs_disconnect(struct lbs_private *priv, u16 reason)
{
- struct lbs_private *priv = wiphy_priv(wiphy);
struct cmd_ds_802_11_deauthenticate cmd;
-
- lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);
-
- /* store for lbs_cfg_ret_disconnect() */
- priv->disassoc_reason = reason_code;
+ int ret;
memset(&cmd, 0, sizeof(cmd));
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
/* Mildly ugly to use a locally store my own BSSID ... */
memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN);
- cmd.reasoncode = cpu_to_le16(reason_code);
+ cmd.reasoncode = cpu_to_le16(reason);
- if (lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd))
- return -EFAULT;
+ ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd);
+ if (ret)
+ return ret;
cfg80211_disconnected(priv->dev,
- priv->disassoc_reason,
+ reason,
NULL, 0,
GFP_KERNEL);
priv->connect_status = LBS_DISCONNECTED;
@@ -1430,6 +1443,21 @@ static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
+static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);
+
+ /* store for lbs_cfg_ret_disconnect() */
+ priv->disassoc_reason = reason_code;
+
+ return lbs_disconnect(priv, reason_code);
+}
static int lbs_cfg_set_default_key(struct wiphy *wiphy,
struct net_device *netdev,
@@ -1438,6 +1466,9 @@ static int lbs_cfg_set_default_key(struct wiphy *wiphy,
{
struct lbs_private *priv = wiphy_priv(wiphy);
+ if (netdev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);
if (key_index != priv->wep_tx_key) {
@@ -1459,6 +1490,9 @@ static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev,
u16 key_type;
int ret = 0;
+ if (netdev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);
lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n",
@@ -1602,6 +1636,9 @@ static int lbs_get_survey(struct wiphy *wiphy, struct net_device *dev,
s8 signal, noise;
int ret;
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
if (idx != 0)
ret = -ENOENT;
@@ -1635,28 +1672,23 @@ static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev,
struct lbs_private *priv = wiphy_priv(wiphy);
int ret = 0;
- lbs_deb_enter(LBS_DEB_CFG80211);
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
switch (type) {
case NL80211_IFTYPE_MONITOR:
- ret = lbs_set_monitor_mode(priv, 1);
- break;
case NL80211_IFTYPE_STATION:
- if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
- ret = lbs_set_monitor_mode(priv, 0);
- if (!ret)
- ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1);
- break;
case NL80211_IFTYPE_ADHOC:
- if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
- ret = lbs_set_monitor_mode(priv, 0);
- if (!ret)
- ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2);
break;
default:
- ret = -ENOTSUPP;
+ return -EOPNOTSUPP;
}
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (priv->iface_running)
+ ret = lbs_set_iface_type(priv, type);
+
if (!ret)
priv->wdev->iftype = type;
@@ -1958,6 +1990,9 @@ static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_bss *bss;
DECLARE_SSID_BUF(ssid_buf);
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);
if (!params->channel) {
@@ -1994,6 +2029,9 @@ static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
struct cmd_ds_802_11_ad_hoc_stop cmd;
int ret = 0;
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);
memset(&cmd, 0, sizeof(cmd));
@@ -2116,6 +2154,8 @@ int lbs_cfg_register(struct lbs_private *priv)
BIT(NL80211_IFTYPE_ADHOC);
if (lbs_rtap_supported(priv))
wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
+ if (lbs_mesh_activated(priv))
+ wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz;
diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h
index 4f46bb744bee..558168ce634d 100644
--- a/drivers/net/wireless/libertas/cfg.h
+++ b/drivers/net/wireless/libertas/cfg.h
@@ -16,6 +16,8 @@ int lbs_reg_notifier(struct wiphy *wiphy,
void lbs_send_disconnect_notification(struct lbs_private *priv);
void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event);
+void lbs_scan_done(struct lbs_private *priv);
void lbs_scan_deinit(struct lbs_private *priv);
+int lbs_disconnect(struct lbs_private *priv, u16 reason);
#endif
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index 71c8f3fccfa1..d798bcc0d83a 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -3,10 +3,12 @@
* It prepares command and sends it to firmware when it is ready.
*/
+#include <linux/hardirq.h>
#include <linux/kfifo.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/if_arp.h>
+#include <linux/export.h>
#include "decl.h"
#include "cfg.h"
@@ -873,6 +875,7 @@ int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value)
memset(&cmd, 0, sizeof(cmd));
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
cmd.action = cpu_to_le16(CMD_ACT_GET);
+ cmd.offset = cpu_to_le16(offset);
if (reg != CMD_MAC_REG_ACCESS &&
reg != CMD_BBP_REG_ACCESS &&
@@ -882,7 +885,7 @@ int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value)
}
ret = lbs_cmd_with_response(priv, reg, &cmd);
- if (ret) {
+ if (!ret) {
if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS)
*value = cmd.value.bbp_rf;
else if (reg == CMD_MAC_REG_ACCESS)
@@ -915,6 +918,7 @@ int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value)
memset(&cmd, 0, sizeof(cmd));
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
cmd.action = cpu_to_le16(CMD_ACT_SET);
+ cmd.offset = cpu_to_le16(offset);
if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS)
cmd.value.bbp_rf = (u8) (value & 0xFF);
@@ -1067,16 +1071,34 @@ static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
spin_unlock_irqrestore(&priv->driver_lock, flags);
}
-void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
- int result)
+void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
+ int result)
{
+ /*
+ * Normally, commands are removed from cmdpendingq before being
+ * submitted. However, we can arrive here on alternative codepaths
+ * where the command is still pending. Make sure the command really
+ * isn't part of a list at this point.
+ */
+ list_del_init(&cmd->list);
+
cmd->result = result;
cmd->cmdwaitqwoken = 1;
- wake_up_interruptible(&cmd->cmdwait_q);
+ wake_up(&cmd->cmdwait_q);
if (!cmd->callback || cmd->callback == lbs_cmd_async_callback)
__lbs_cleanup_and_insert_cmd(priv, cmd);
priv->cur_cmd = NULL;
+ wake_up(&priv->waitq);
+}
+
+void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
+ int result)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ __lbs_complete_command(priv, cmd, result);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
}
int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
@@ -1248,7 +1270,7 @@ static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv)
if (!list_empty(&priv->cmdfreeq)) {
tempnode = list_first_entry(&priv->cmdfreeq,
struct cmd_ctrl_node, list);
- list_del(&tempnode->list);
+ list_del_init(&tempnode->list);
} else {
lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
tempnode = NULL;
@@ -1356,10 +1378,7 @@ int lbs_execute_next_command(struct lbs_private *priv)
cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) {
lbs_deb_host(
"EXEC_NEXT_CMD: ignore ENTER_PS cmd\n");
- spin_lock_irqsave(&priv->driver_lock, flags);
- list_del(&cmdnode->list);
lbs_complete_command(priv, cmdnode, 0);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = 0;
goto done;
@@ -1369,10 +1388,7 @@ int lbs_execute_next_command(struct lbs_private *priv)
(priv->psstate == PS_STATE_PRE_SLEEP)) {
lbs_deb_host(
"EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n");
- spin_lock_irqsave(&priv->driver_lock, flags);
- list_del(&cmdnode->list);
lbs_complete_command(priv, cmdnode, 0);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
priv->needtowakeup = 1;
ret = 0;
@@ -1384,7 +1400,7 @@ int lbs_execute_next_command(struct lbs_private *priv)
}
}
spin_lock_irqsave(&priv->driver_lock, flags);
- list_del(&cmdnode->list);
+ list_del_init(&cmdnode->list);
spin_unlock_irqrestore(&priv->driver_lock, flags);
lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
le16_to_cpu(cmd->command));
@@ -1612,7 +1628,7 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
/* Wake up main thread to execute next command */
- wake_up_interruptible(&priv->waitq);
+ wake_up(&priv->waitq);
cmdnode = ERR_PTR(-ENOBUFS);
goto done;
}
@@ -1632,7 +1648,7 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
cmdnode->cmdwaitqwoken = 0;
lbs_queue_cmd(priv, cmdnode);
- wake_up_interruptible(&priv->waitq);
+ wake_up(&priv->waitq);
done:
lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode);
@@ -1667,7 +1683,13 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command,
}
might_sleep();
- wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
+
+ /*
+ * Be careful with signals here. A signal may be received as the system
+ * goes into suspend or resume. We do not want this to interrupt the
+ * command, so we perform an uninterruptible sleep.
+ */
+ wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
spin_lock_irqsave(&priv->driver_lock, flags);
ret = cmdnode->result;
diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h
index 7109d6b717ea..b280ef7a0aea 100644
--- a/drivers/net/wireless/libertas/cmd.h
+++ b/drivers/net/wireless/libertas/cmd.h
@@ -59,6 +59,8 @@ int lbs_allocate_cmd_buffer(struct lbs_private *priv);
int lbs_free_cmd_buffer(struct lbs_private *priv);
int lbs_execute_next_command(struct lbs_private *priv);
+void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
+ int result);
void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
int result);
int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len);
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
index 207fc361db84..178b222b3ce1 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -3,6 +3,7 @@
* responses as well as events generated by firmware.
*/
+#include <linux/hardirq.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/sched.h>
@@ -165,7 +166,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
lbs_deb_host("CMD_RESP: PS action 0x%X\n", action);
}
- lbs_complete_command(priv, priv->cur_cmd, result);
+ __lbs_complete_command(priv, priv->cur_cmd, result);
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = 0;
@@ -186,7 +187,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
break;
}
- lbs_complete_command(priv, priv->cur_cmd, result);
+ __lbs_complete_command(priv, priv->cur_cmd, result);
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = -1;
@@ -204,7 +205,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
if (priv->cur_cmd) {
/* Clean up and Put current command back to cmdfreeq */
- lbs_complete_command(priv, priv->cur_cmd, result);
+ __lbs_complete_command(priv, priv->cur_cmd, result);
}
spin_unlock_irqrestore(&priv->driver_lock, flags);
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
index 23250f621761..d8d8f0d0899f 100644
--- a/drivers/net/wireless/libertas/debugfs.c
+++ b/drivers/net/wireless/libertas/debugfs.c
@@ -1,9 +1,11 @@
#include <linux/dcache.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/hardirq.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "decl.h"
#include "cmd.h"
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
index da0b05bb89fe..bc951ab4b681 100644
--- a/drivers/net/wireless/libertas/decl.h
+++ b/drivers/net/wireless/libertas/decl.h
@@ -9,6 +9,7 @@
#include <linux/netdevice.h>
#include <linux/firmware.h>
+#include <linux/nl80211.h>
/* Should be terminated by a NULL entry */
struct lbs_fw_table {
@@ -43,10 +44,15 @@ int lbs_start_card(struct lbs_private *priv);
void lbs_stop_card(struct lbs_private *priv);
void lbs_host_to_card_done(struct lbs_private *priv);
+int lbs_start_iface(struct lbs_private *priv);
+int lbs_stop_iface(struct lbs_private *priv);
+int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type);
+
int lbs_rtap_supported(struct lbs_private *priv);
int lbs_set_mac_address(struct net_device *dev, void *addr);
void lbs_set_multicast_list(struct net_device *dev);
+void lbs_update_mcast(struct lbs_private *priv);
int lbs_suspend(struct lbs_private *priv);
int lbs_resume(struct lbs_private *priv);
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index 76d018beebf4..f3fd447131c2 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -6,7 +6,6 @@
#ifndef _LBS_DEV_H_
#define _LBS_DEV_H_
-#include "mesh.h"
#include "defs.h"
#include "host.h"
@@ -22,6 +21,17 @@ struct sleep_params {
uint16_t sp_reserved;
};
+/* Mesh statistics */
+struct lbs_mesh_stats {
+ u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */
+ u32 fwd_unicast_cnt; /* Fwd: Unicast counter */
+ u32 fwd_drop_ttl; /* Fwd: TTL zero */
+ u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */
+ u32 fwd_drop_noroute; /* Fwd: No route to Destination */
+ u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */
+ u32 drop_blind; /* Rx: Dropped by blinding table */
+ u32 tx_failed_cnt; /* Tx: Failed transmissions */
+};
/* Private structure for the MV device */
struct lbs_private {
@@ -36,7 +46,6 @@ struct lbs_private {
/* CFG80211 */
struct wireless_dev *wdev;
bool wiphy_registered;
- bool stopping;
struct cfg80211_scan_request *scan_req;
u8 assoc_bss[ETH_ALEN];
u8 disassoc_reason;
@@ -44,9 +53,7 @@ struct lbs_private {
/* Mesh */
struct net_device *mesh_dev; /* Virtual device */
#ifdef CONFIG_LIBERTAS_MESH
- u32 mesh_connect_status;
struct lbs_mesh_stats mstats;
- int mesh_open;
uint16_t mesh_tlv;
u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1];
u8 mesh_ssid_len;
@@ -88,11 +95,14 @@ struct lbs_private {
/* Hardware access */
void *card;
+ bool iface_running;
u8 fw_ready;
u8 surpriseremoved;
u8 setup_fw_on_resume;
int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
void (*reset_card) (struct lbs_private *priv);
+ int (*power_save) (struct lbs_private *priv);
+ int (*power_restore) (struct lbs_private *priv);
int (*enter_deep_sleep) (struct lbs_private *priv);
int (*exit_deep_sleep) (struct lbs_private *priv);
int (*reset_deep_sleep_wakeup) (struct lbs_private *priv);
@@ -148,6 +158,7 @@ struct lbs_private {
/* protected by hard_start_xmit serialization */
u8 txretrycount;
struct sk_buff *currenttxskb;
+ struct timer_list tx_lockup_timer;
/* Locks */
struct mutex lock;
@@ -169,9 +180,20 @@ struct lbs_private {
wait_queue_head_t scan_q;
/* Whether the scan was initiated internally and not by cfg80211 */
bool internal_scan;
- unsigned long last_scan;
};
extern struct cmd_confirm_sleep confirm_sleep;
+/* Check if there is an interface active. */
+static inline int lbs_iface_active(struct lbs_private *priv)
+{
+ int r;
+
+ r = netif_running(priv->dev);
+ if (priv->mesh_dev)
+ r |= netif_running(priv->mesh_dev);
+
+ return r;
+}
+
#endif
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c
index 29dbce4a9f86..885ddc1c4fed 100644
--- a/drivers/net/wireless/libertas/ethtool.c
+++ b/drivers/net/wireless/libertas/ethtool.c
@@ -1,9 +1,11 @@
+#include <linux/hardirq.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/delay.h>
#include "decl.h"
#include "cmd.h"
+#include "mesh.h"
static void lbs_ethtool_get_drvinfo(struct net_device *dev,
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 224e9853c480..9804ebc892d4 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -29,7 +29,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
@@ -39,6 +39,7 @@
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/host.h>
+#include <linux/pm_runtime.h>
#include "host.h"
#include "decl.h"
@@ -47,6 +48,8 @@
#include "cmd.h"
#include "if_sdio.h"
+static void if_sdio_interrupt(struct sdio_func *func);
+
/* The if_sdio_remove() callback function is called when
* user removes this module from kernel space or ejects
* the card from the slot. The driver handles these 2 cases
@@ -757,6 +760,136 @@ out:
return ret;
}
+/********************************************************************/
+/* Power management */
+/********************************************************************/
+
+static int if_sdio_power_on(struct if_sdio_card *card)
+{
+ struct sdio_func *func = card->func;
+ struct lbs_private *priv = card->priv;
+ struct mmc_host *host = func->card->host;
+ int ret;
+
+ sdio_claim_host(func);
+
+ ret = sdio_enable_func(func);
+ if (ret)
+ goto release;
+
+ /* For 1-bit transfers to the 8686 model, we need to enable the
+ * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
+ * bit to allow access to non-vendor registers. */
+ if ((card->model == MODEL_8686) &&
+ (host->caps & MMC_CAP_SDIO_IRQ) &&
+ (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
+ u8 reg;
+
+ func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+ reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
+ if (ret)
+ goto disable;
+
+ reg |= SDIO_BUS_ECSI;
+ sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
+ if (ret)
+ goto disable;
+ }
+
+ card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
+ if (ret)
+ goto disable;
+
+ card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
+ if (ret)
+ goto disable;
+
+ card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
+ if (ret)
+ goto disable;
+
+ sdio_release_host(func);
+ ret = if_sdio_prog_firmware(card);
+ sdio_claim_host(func);
+ if (ret)
+ goto disable;
+
+ /*
+ * Get rx_unit if the chip is SD8688 or newer.
+ * SD8385 & SD8686 do not have rx_unit.
+ */
+ if ((card->model != MODEL_8385)
+ && (card->model != MODEL_8686))
+ card->rx_unit = if_sdio_read_rx_unit(card);
+ else
+ card->rx_unit = 0;
+
+ /*
+ * Set up the interrupt handler late.
+ *
+ * If we set it up earlier, the (buggy) hardware generates a spurious
+ * interrupt, even before the interrupt has been enabled, with
+ * CCCR_INTx = 0.
+ *
+ * We register the interrupt handler late so that we can handle any
+ * spurious interrupts, and also to avoid generation of that known
+ * spurious interrupt in the first place.
+ */
+ ret = sdio_claim_irq(func, if_sdio_interrupt);
+ if (ret)
+ goto disable;
+
+ /*
+ * Enable interrupts now that everything is set up
+ */
+ sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
+ if (ret)
+ goto release_irq;
+
+ sdio_release_host(func);
+
+ /*
+ * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
+ */
+ if (card->model == MODEL_8688) {
+ struct cmd_header cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ lbs_deb_sdio("send function INIT command\n");
+ if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
+ lbs_cmd_copyback, (unsigned long) &cmd))
+ netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
+ }
+
+ priv->fw_ready = 1;
+
+ return 0;
+
+release_irq:
+ sdio_release_irq(func);
+disable:
+ sdio_disable_func(func);
+release:
+ sdio_release_host(func);
+ return ret;
+}
+
+static int if_sdio_power_off(struct if_sdio_card *card)
+{
+ struct sdio_func *func = card->func;
+ struct lbs_private *priv = card->priv;
+
+ priv->fw_ready = 0;
+
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+ return 0;
+}
+
+
/*******************************************************************/
/* Libertas callbacks */
/*******************************************************************/
@@ -892,6 +1025,63 @@ static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv)
}
+static struct mmc_host *reset_host;
+
+static void if_sdio_reset_card_worker(struct work_struct *work)
+{
+ /*
+ * The actual reset operation must be run outside of lbs_thread. This
+ * is because mmc_remove_host() will cause the device to be instantly
+ * destroyed, and the libertas driver then needs to end lbs_thread,
+ * leading to a deadlock.
+ *
+ * We run it in a workqueue totally independent from the if_sdio_card
+ * instance for that reason.
+ */
+
+ pr_info("Resetting card...");
+ mmc_remove_host(reset_host);
+ mmc_add_host(reset_host);
+}
+static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker);
+
+static void if_sdio_reset_card(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+
+ if (work_pending(&card_reset_work))
+ return;
+
+ reset_host = card->func->card->host;
+ schedule_work(&card_reset_work);
+}
+
+static int if_sdio_power_save(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int ret;
+
+ flush_workqueue(card->workqueue);
+
+ ret = if_sdio_power_off(card);
+
+ /* Let runtime PM know the card is powered off */
+ pm_runtime_put_sync(&card->func->dev);
+
+ return ret;
+}
+
+static int if_sdio_power_restore(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+
+ /* Make sure the card will not be powered off by runtime PM */
+ pm_runtime_get_sync(&card->func->dev);
+
+ return if_sdio_power_on(card);
+}
+
+
/*******************************************************************/
/* SDIO callbacks */
/*******************************************************************/
@@ -945,7 +1135,6 @@ static int if_sdio_probe(struct sdio_func *func,
int ret, i;
unsigned int model;
struct if_sdio_packet *packet;
- struct mmc_host *host = func->card->host;
lbs_deb_enter(LBS_DEB_SDIO);
@@ -1002,45 +1191,6 @@ static int if_sdio_probe(struct sdio_func *func,
goto free;
}
- sdio_claim_host(func);
-
- ret = sdio_enable_func(func);
- if (ret)
- goto release;
-
- /* For 1-bit transfers to the 8686 model, we need to enable the
- * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
- * bit to allow access to non-vendor registers. */
- if ((card->model == MODEL_8686) &&
- (host->caps & MMC_CAP_SDIO_IRQ) &&
- (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
- u8 reg;
-
- func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
- reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
- if (ret)
- goto release_int;
-
- reg |= SDIO_BUS_ECSI;
- sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
- if (ret)
- goto release_int;
- }
-
- card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
- if (ret)
- goto release_int;
-
- card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
- if (ret)
- goto release_int;
-
- card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
- if (ret)
- goto release_int;
-
- sdio_release_host(func);
-
sdio_set_drvdata(func, card);
lbs_deb_sdio("class = 0x%X, vendor = 0x%X, "
@@ -1048,14 +1198,11 @@ static int if_sdio_probe(struct sdio_func *func,
func->class, func->vendor, func->device,
model, (unsigned)card->ioport);
- ret = if_sdio_prog_firmware(card);
- if (ret)
- goto reclaim;
priv = lbs_add_card(card, &func->dev);
if (!priv) {
ret = -ENOMEM;
- goto reclaim;
+ goto free;
}
card->priv = priv;
@@ -1065,62 +1212,22 @@ static int if_sdio_probe(struct sdio_func *func,
priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
+ priv->reset_card = if_sdio_reset_card;
+ priv->power_save = if_sdio_power_save;
+ priv->power_restore = if_sdio_power_restore;
- sdio_claim_host(func);
-
- /*
- * Get rx_unit if the chip is SD8688 or newer.
- * SD8385 & SD8686 do not have rx_unit.
- */
- if ((card->model != MODEL_8385)
- && (card->model != MODEL_8686))
- card->rx_unit = if_sdio_read_rx_unit(card);
- else
- card->rx_unit = 0;
-
- /*
- * Set up the interrupt handler late.
- *
- * If we set it up earlier, the (buggy) hardware generates a spurious
- * interrupt, even before the interrupt has been enabled, with
- * CCCR_INTx = 0.
- *
- * We register the interrupt handler late so that we can handle any
- * spurious interrupts, and also to avoid generation of that known
- * spurious interrupt in the first place.
- */
- ret = sdio_claim_irq(func, if_sdio_interrupt);
- if (ret)
- goto disable;
-
- /*
- * Enable interrupts now that everything is set up
- */
- sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
- sdio_release_host(func);
+ ret = if_sdio_power_on(card);
if (ret)
- goto reclaim;
-
- priv->fw_ready = 1;
-
- /*
- * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
- */
- if (card->model == MODEL_8688) {
- struct cmd_header cmd;
-
- memset(&cmd, 0, sizeof(cmd));
-
- lbs_deb_sdio("send function INIT command\n");
- if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
- lbs_cmd_copyback, (unsigned long) &cmd))
- netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
- }
+ goto err_activate_card;
ret = lbs_start_card(priv);
+ if_sdio_power_off(card);
if (ret)
goto err_activate_card;
+ /* Tell PM core that we don't need the card to be powered now */
+ pm_runtime_put_noidle(&func->dev);
+
out:
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
@@ -1129,14 +1236,6 @@ out:
err_activate_card:
flush_workqueue(card->workqueue);
lbs_remove_card(priv);
-reclaim:
- sdio_claim_host(func);
-release_int:
- sdio_release_irq(func);
-disable:
- sdio_disable_func(func);
-release:
- sdio_release_host(func);
free:
destroy_workqueue(card->workqueue);
while (card->packets) {
@@ -1163,6 +1262,9 @@ static void if_sdio_remove(struct sdio_func *func)
card = sdio_get_drvdata(func);
+ /* Undo decrement done above in if_sdio_probe */
+ pm_runtime_get_noresume(&func->dev);
+
if (user_rmmod && (card->model == MODEL_8688)) {
/*
* FUNC_SHUTDOWN is required for SD8688 WLAN/BT
@@ -1187,11 +1289,6 @@ static void if_sdio_remove(struct sdio_func *func)
flush_workqueue(card->workqueue);
destroy_workqueue(card->workqueue);
- sdio_claim_host(func);
- sdio_release_irq(func);
- sdio_disable_func(func);
- sdio_release_host(func);
-
while (card->packets) {
packet = card->packets;
card->packets = card->packets->next;
@@ -1301,6 +1398,8 @@ static void __exit if_sdio_exit_module(void)
/* Set the flag as user is removing this module. */
user_rmmod = 1;
+ cancel_work_sync(&card_reset_work);
+
sdio_unregister_driver(&if_sdio_driver);
lbs_deb_leave(LBS_DEB_SDIO);
diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
index 463352c890d7..728baa445259 100644
--- a/drivers/net/wireless/libertas/if_spi.c
+++ b/drivers/net/wireless/libertas/if_spi.c
@@ -19,7 +19,9 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/moduleparam.h>
+#include <linux/hardirq.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/jiffies.h>
#include <linux/list.h>
@@ -529,10 +531,6 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card,
goto out;
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
IF_SPI_CIC_CMD_DOWNLOAD_OVER);
- goto out;
-
- lbs_deb_spi("waiting for helper to boot...\n");
-
out:
if (err)
pr_err("failed to load helper firmware (err=%d)\n", err);
@@ -997,6 +995,7 @@ static int if_spi_host_to_card(struct lbs_private *priv,
spin_unlock_irqrestore(&card->buffer_lock, flags);
break;
default:
+ kfree(packet);
netdev_err(priv->dev, "can't transfer buffer of type %d\n",
type);
err = -EINVAL;
@@ -1032,7 +1031,6 @@ static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)
static int if_spi_init_card(struct if_spi_card *card)
{
struct lbs_private *priv = card->priv;
- struct spi_device *spi = card->spi;
int err, i;
u32 scratch;
const struct firmware *helper = NULL;
@@ -1080,8 +1078,9 @@ static int if_spi_init_card(struct if_spi_card *card)
"attached to SPI bus_num %d, chip_select %d. "
"spi->max_speed_hz=%d\n",
card->card_id, card->card_rev,
- spi->master->bus_num, spi->chip_select,
- spi->max_speed_hz);
+ card->spi->master->bus_num,
+ card->spi->chip_select,
+ card->spi->max_speed_hz);
err = if_spi_prog_helper_firmware(card, helper);
if (err)
goto out;
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index b5acc393a65a..db879c364ebf 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -5,7 +5,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/delay.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
@@ -324,7 +324,7 @@ static int if_usb_probe(struct usb_interface *intf,
}
kparam_unblock_sysfs_write(fw_name);
- if (!(priv = lbs_add_card(cardp, &udev->dev)))
+ if (!(priv = lbs_add_card(cardp, &intf->dev)))
goto err_prog_firmware;
cardp->priv = priv;
@@ -956,7 +956,7 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp,
priv->dnld_sent = DNLD_RES_RECEIVED;
spin_unlock_irqrestore(&priv->driver_lock, flags);
- wake_up_interruptible(&priv->waitq);
+ wake_up(&priv->waitq);
return ret;
}
@@ -973,6 +973,23 @@ static const struct {
{ MODEL_8682, "libertas/usb8682.bin" }
};
+#ifdef CONFIG_OLPC
+
+static int try_olpc_fw(struct if_usb_card *cardp)
+{
+ int retval = -ENOENT;
+
+ /* try the OLPC firmware first; fall back to fw_table list */
+ if (machine_is_olpc() && cardp->model == MODEL_8388)
+ retval = request_firmware(&cardp->fw,
+ "libertas/usb8388_olpc.bin", &cardp->udev->dev);
+ return retval;
+}
+
+#else
+static int try_olpc_fw(struct if_usb_card *cardp) { return -ENOENT; }
+#endif /* !CONFIG_OLPC */
+
static int get_fw(struct if_usb_card *cardp, const char *fwname)
{
int i;
@@ -981,6 +998,10 @@ static int get_fw(struct if_usb_card *cardp, const char *fwname)
if (fwname)
return request_firmware(&cardp->fw, fwname, &cardp->udev->dev);
+ /* Handle OLPC firmware */
+ if (try_olpc_fw(cardp) == 0)
+ return 0;
+
/* Otherwise search for firmware to use */
for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
if (fw_table[i].model != cardp->model)
@@ -1112,6 +1133,15 @@ static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
if (priv->psstate != PS_STATE_FULL_POWER)
return -1;
+#ifdef CONFIG_OLPC
+ if (machine_is_olpc()) {
+ if (priv->wol_criteria == EHS_REMOVE_WAKEUP)
+ olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN);
+ else
+ olpc_ec_wakeup_set(EC_SCI_SRC_WLAN);
+ }
+#endif
+
ret = lbs_suspend(priv);
if (ret)
goto out;
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 8c40949cb076..957681dede17 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -6,9 +6,10 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
+#include <linux/hardirq.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/kthread.h>
@@ -22,6 +23,7 @@
#include "cfg.h"
#include "debugfs.h"
#include "cmd.h"
+#include "mesh.h"
#define DRIVER_RELEASE_VERSION "323.p0"
const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION
@@ -97,6 +99,69 @@ u8 lbs_data_rate_to_fw_index(u32 rate)
return 0;
}
+int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type)
+{
+ int ret = 0;
+
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ ret = lbs_set_monitor_mode(priv, 1);
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
+ ret = lbs_set_monitor_mode(priv, 0);
+ if (!ret)
+ ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
+ ret = lbs_set_monitor_mode(priv, 0);
+ if (!ret)
+ ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+ return ret;
+}
+
+int lbs_start_iface(struct lbs_private *priv)
+{
+ struct cmd_ds_802_11_mac_address cmd;
+ int ret;
+
+ if (priv->power_restore) {
+ ret = priv->power_restore(priv);
+ if (ret)
+ return ret;
+ }
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+ memcpy(cmd.macadd, priv->current_addr, ETH_ALEN);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd);
+ if (ret) {
+ lbs_deb_net("set MAC address failed\n");
+ goto err;
+ }
+
+ ret = lbs_set_iface_type(priv, priv->wdev->iftype);
+ if (ret) {
+ lbs_deb_net("set iface type failed\n");
+ goto err;
+ }
+
+ lbs_update_channel(priv);
+
+ priv->iface_running = true;
+ return 0;
+
+err:
+ if (priv->power_save)
+ priv->power_save(priv);
+ return ret;
+}
/**
* lbs_dev_open - open the ethX interface
@@ -110,23 +175,65 @@ static int lbs_dev_open(struct net_device *dev)
int ret = 0;
lbs_deb_enter(LBS_DEB_NET);
+ if (!priv->iface_running) {
+ ret = lbs_start_iface(priv);
+ if (ret)
+ goto out;
+ }
spin_lock_irq(&priv->driver_lock);
- priv->stopping = false;
- if (priv->connect_status == LBS_CONNECTED)
- netif_carrier_on(dev);
- else
- netif_carrier_off(dev);
+ netif_carrier_off(dev);
if (!priv->tx_pending_len)
netif_wake_queue(dev);
spin_unlock_irq(&priv->driver_lock);
+
+out:
lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
return ret;
}
+static bool lbs_command_queue_empty(struct lbs_private *priv)
+{
+ unsigned long flags;
+ bool ret;
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ return ret;
+}
+
+int lbs_stop_iface(struct lbs_private *priv)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_MAIN);
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ priv->iface_running = false;
+ kfree_skb(priv->currenttxskb);
+ priv->currenttxskb = NULL;
+ priv->tx_pending_len = 0;
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ cancel_work_sync(&priv->mcast_work);
+ del_timer_sync(&priv->tx_lockup_timer);
+
+ /* Disable command processing, and wait for all commands to complete */
+ lbs_deb_main("waiting for commands to complete\n");
+ wait_event(priv->waitq, lbs_command_queue_empty(priv));
+ lbs_deb_main("all commands completed\n");
+
+ if (priv->power_save)
+ ret = priv->power_save(priv);
+
+ lbs_deb_leave(LBS_DEB_MAIN);
+ return ret;
+}
+
/**
* lbs_eth_stop - close the ethX interface
*
@@ -139,17 +246,22 @@ static int lbs_eth_stop(struct net_device *dev)
lbs_deb_enter(LBS_DEB_NET);
+ if (priv->connect_status == LBS_CONNECTED)
+ lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING);
+
spin_lock_irq(&priv->driver_lock);
- priv->stopping = true;
netif_stop_queue(dev);
spin_unlock_irq(&priv->driver_lock);
- schedule_work(&priv->mcast_work);
+ lbs_update_mcast(priv);
cancel_delayed_work_sync(&priv->scan_work);
- if (priv->scan_req) {
- cfg80211_scan_done(priv->scan_req, false);
- priv->scan_req = NULL;
- }
+ if (priv->scan_req)
+ lbs_scan_done(priv);
+
+ netif_carrier_off(priv->dev);
+
+ if (!lbs_iface_active(priv))
+ lbs_stop_iface(priv);
lbs_deb_leave(LBS_DEB_NET);
return 0;
@@ -162,13 +274,14 @@ void lbs_host_to_card_done(struct lbs_private *priv)
lbs_deb_enter(LBS_DEB_THREAD);
spin_lock_irqsave(&priv->driver_lock, flags);
+ del_timer(&priv->tx_lockup_timer);
priv->dnld_sent = DNLD_RES_RECEIVED;
/* Wake main thread if commands are pending */
if (!priv->cur_cmd || priv->tx_pending_len > 0) {
if (!priv->wakeup_dev_required)
- wake_up_interruptible(&priv->waitq);
+ wake_up(&priv->waitq);
}
spin_unlock_irqrestore(&priv->driver_lock, flags);
@@ -181,29 +294,24 @@ int lbs_set_mac_address(struct net_device *dev, void *addr)
int ret = 0;
struct lbs_private *priv = dev->ml_priv;
struct sockaddr *phwaddr = addr;
- struct cmd_ds_802_11_mac_address cmd;
lbs_deb_enter(LBS_DEB_NET);
+ /*
+ * Can only set MAC address when all interfaces are down, to be written
+ * to the hardware when one of them is brought up.
+ */
+ if (lbs_iface_active(priv))
+ return -EBUSY;
+
/* In case it was called from the mesh device */
dev = priv->dev;
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
- memcpy(cmd.macadd, phwaddr->sa_data, ETH_ALEN);
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd);
- if (ret) {
- lbs_deb_net("set MAC address failed\n");
- goto done;
- }
-
memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN);
memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN);
if (priv->mesh_dev)
memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN);
-done:
lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
return ret;
}
@@ -257,18 +365,18 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd,
return i;
}
-static void lbs_set_mcast_worker(struct work_struct *work)
+void lbs_update_mcast(struct lbs_private *priv)
{
- struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work);
struct cmd_ds_mac_multicast_adr mcast_cmd;
- int dev_flags;
+ int dev_flags = 0;
int nr_addrs;
int old_mac_control = priv->mac_control;
lbs_deb_enter(LBS_DEB_NET);
- dev_flags = priv->dev->flags;
- if (priv->mesh_dev)
+ if (netif_running(priv->dev))
+ dev_flags |= priv->dev->flags;
+ if (priv->mesh_dev && netif_running(priv->mesh_dev))
dev_flags |= priv->mesh_dev->flags;
if (dev_flags & IFF_PROMISC) {
@@ -314,6 +422,12 @@ static void lbs_set_mcast_worker(struct work_struct *work)
lbs_deb_leave(LBS_DEB_NET);
}
+static void lbs_set_mcast_worker(struct work_struct *work)
+{
+ struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work);
+ lbs_update_mcast(priv);
+}
+
void lbs_set_multicast_list(struct net_device *dev)
{
struct lbs_private *priv = dev->ml_priv;
@@ -503,6 +617,9 @@ static int lbs_thread(void *data)
if (ret) {
lbs_deb_tx("host_to_card failed %d\n", ret);
priv->dnld_sent = DNLD_RES_RECEIVED;
+ } else {
+ mod_timer(&priv->tx_lockup_timer,
+ jiffies + (HZ * 5));
}
priv->tx_pending_len = 0;
if (!priv->currenttxskb) {
@@ -511,7 +628,7 @@ static int lbs_thread(void *data)
if (priv->connect_status == LBS_CONNECTED)
netif_wake_queue(priv->dev);
if (priv->mesh_dev &&
- lbs_mesh_connected(priv))
+ netif_running(priv->mesh_dev))
netif_wake_queue(priv->mesh_dev);
}
}
@@ -519,6 +636,7 @@ static int lbs_thread(void *data)
}
del_timer(&priv->command_timer);
+ del_timer(&priv->tx_lockup_timer);
del_timer(&priv->auto_deepsleep_timer);
lbs_deb_leave(LBS_DEB_THREAD);
@@ -638,13 +756,47 @@ static void lbs_cmd_timeout_handler(unsigned long data)
le16_to_cpu(priv->cur_cmd->cmdbuf->command));
priv->cmd_timed_out = 1;
- wake_up_interruptible(&priv->waitq);
+
+ /*
+ * If the device didn't even acknowledge the command, reset the state
+ * so that we don't block all future commands due to this one timeout.
+ */
+ if (priv->dnld_sent == DNLD_CMD_SENT)
+ priv->dnld_sent = DNLD_RES_RECEIVED;
+
+ wake_up(&priv->waitq);
out:
spin_unlock_irqrestore(&priv->driver_lock, flags);
lbs_deb_leave(LBS_DEB_CMD);
}
/**
+ * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames
+ * to the hardware. This is known to frequently happen with SD8686 when
+ * waking up after a Wake-on-WLAN-triggered resume.
+ *
+ * @data: &struct lbs_private pointer
+ */
+static void lbs_tx_lockup_handler(unsigned long data)
+{
+ struct lbs_private *priv = (struct lbs_private *)data;
+ unsigned long flags;
+
+ lbs_deb_enter(LBS_DEB_TX);
+ spin_lock_irqsave(&priv->driver_lock, flags);
+
+ netdev_info(priv->dev, "TX lockup detected\n");
+ if (priv->reset_card)
+ priv->reset_card(priv);
+
+ priv->dnld_sent = DNLD_RES_RECEIVED;
+ wake_up_interruptible(&priv->waitq);
+
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ lbs_deb_leave(LBS_DEB_TX);
+}
+
+/**
* auto_deepsleep_timer_fn - put the device back to deep sleep mode when
* timer expires and no activity (command, event, data etc.) is detected.
* @data: &struct lbs_private pointer
@@ -730,6 +882,8 @@ static int lbs_init_adapter(struct lbs_private *priv)
setup_timer(&priv->command_timer, lbs_cmd_timeout_handler,
(unsigned long)priv);
+ setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler,
+ (unsigned long)priv);
setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
(unsigned long)priv);
@@ -767,6 +921,7 @@ static void lbs_free_adapter(struct lbs_private *priv)
lbs_free_cmd_buffer(priv);
kfifo_free(&priv->event_fifo);
del_timer(&priv->command_timer);
+ del_timer(&priv->tx_lockup_timer);
del_timer(&priv->auto_deepsleep_timer);
lbs_deb_leave(LBS_DEB_MAIN);
@@ -777,7 +932,7 @@ static const struct net_device_ops lbs_netdev_ops = {
.ndo_stop = lbs_eth_stop,
.ndo_start_xmit = lbs_hard_start_xmit,
.ndo_set_mac_address = lbs_set_mac_address,
- .ndo_set_multicast_list = lbs_set_multicast_list,
+ .ndo_set_rx_mode = lbs_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
};
@@ -880,10 +1035,6 @@ void lbs_remove_card(struct lbs_private *priv)
lbs_remove_mesh(priv);
lbs_scan_deinit(priv);
- dev = priv->dev;
-
- cancel_work_sync(&priv->mcast_work);
-
/* worker thread destruction blocks on the in-flight command which
* should have been cleared already in lbs_stop_card().
*/
@@ -941,17 +1092,18 @@ int lbs_start_card(struct lbs_private *priv)
if (ret)
goto done;
+ if (!lbs_disablemesh)
+ lbs_init_mesh(priv);
+ else
+ pr_info("%s: mesh disabled\n", dev->name);
+
if (lbs_cfg_register(priv)) {
pr_err("cannot register device\n");
goto done;
}
- lbs_update_channel(priv);
-
- if (!lbs_disablemesh)
- lbs_init_mesh(priv);
- else
- pr_info("%s: mesh disabled\n", dev->name);
+ if (lbs_mesh_activated(priv))
+ lbs_start_mesh(priv);
lbs_debugfs_init_one(priv, dev);
@@ -969,8 +1121,6 @@ EXPORT_SYMBOL_GPL(lbs_start_card);
void lbs_stop_card(struct lbs_private *priv)
{
struct net_device *dev;
- struct cmd_ctrl_node *cmdnode;
- unsigned long flags;
lbs_deb_enter(LBS_DEB_MAIN);
@@ -983,30 +1133,6 @@ void lbs_stop_card(struct lbs_private *priv)
lbs_debugfs_remove_one(priv);
lbs_deinit_mesh(priv);
-
- /* Delete the timeout of the currently processing command */
- del_timer_sync(&priv->command_timer);
- del_timer_sync(&priv->auto_deepsleep_timer);
-
- /* Flush pending command nodes */
- spin_lock_irqsave(&priv->driver_lock, flags);
- lbs_deb_main("clearing pending commands\n");
- list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
- cmdnode->result = -ENOENT;
- cmdnode->cmdwaitqwoken = 1;
- wake_up_interruptible(&cmdnode->cmdwait_q);
- }
-
- /* Flush the command the card is currently processing */
- if (priv->cur_cmd) {
- lbs_deb_main("clearing current command\n");
- priv->cur_cmd->result = -ENOENT;
- priv->cur_cmd->cmdwaitqwoken = 1;
- wake_up_interruptible(&priv->cur_cmd->cmdwait_q);
- }
- lbs_deb_main("done clearing commands\n");
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
unregister_netdev(dev);
out:
@@ -1027,7 +1153,7 @@ void lbs_queue_event(struct lbs_private *priv, u32 event)
kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32));
- wake_up_interruptible(&priv->waitq);
+ wake_up(&priv->waitq);
spin_unlock_irqrestore(&priv->driver_lock, flags);
lbs_deb_leave(LBS_DEB_THREAD);
@@ -1045,7 +1171,7 @@ void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx)
BUG_ON(resp_idx > 1);
priv->resp_idx = resp_idx;
- wake_up_interruptible(&priv->waitq);
+ wake_up(&priv->waitq);
lbs_deb_leave(LBS_DEB_THREAD);
}
diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c
index 24cf06680c6b..e87c031b298f 100644
--- a/drivers/net/wireless/libertas/mesh.c
+++ b/drivers/net/wireless/libertas/mesh.c
@@ -2,6 +2,7 @@
#include <linux/delay.h>
#include <linux/etherdevice.h>
+#include <linux/hardirq.h>
#include <linux/netdevice.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
@@ -14,6 +15,134 @@
#include "cmd.h"
+static int lbs_add_mesh(struct lbs_private *priv);
+
+/***************************************************************************
+ * Mesh command handling
+ */
+
+static int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
+ struct cmd_ds_mesh_access *cmd)
+{
+ int ret;
+
+ lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
+
+ cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
+ cmd->hdr.size = cpu_to_le16(sizeof(*cmd));
+ cmd->hdr.result = 0;
+
+ cmd->action = cpu_to_le16(cmd_action);
+
+ ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return ret;
+}
+
+static int __lbs_mesh_config_send(struct lbs_private *priv,
+ struct cmd_ds_mesh_config *cmd,
+ uint16_t action, uint16_t type)
+{
+ int ret;
+ u16 command = CMD_MESH_CONFIG_OLD;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ /*
+ * Command id is 0xac for v10 FW along with mesh interface
+ * id in bits 14-13-12.
+ */
+ if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
+ command = CMD_MESH_CONFIG |
+ (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);
+
+ cmd->hdr.command = cpu_to_le16(command);
+ cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
+ cmd->hdr.result = 0;
+
+ cmd->type = cpu_to_le16(type);
+ cmd->action = cpu_to_le16(action);
+
+ ret = lbs_cmd_with_response(priv, command, cmd);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return ret;
+}
+
+static int lbs_mesh_config_send(struct lbs_private *priv,
+ struct cmd_ds_mesh_config *cmd,
+ uint16_t action, uint16_t type)
+{
+ int ret;
+
+ if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
+ return -EOPNOTSUPP;
+
+ ret = __lbs_mesh_config_send(priv, cmd, action, type);
+ return ret;
+}
+
+/* This function is the CMD_MESH_CONFIG legacy function. It only handles the
+ * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG
+ * are all handled by preparing a struct cmd_ds_mesh_config and passing it to
+ * lbs_mesh_config_send.
+ */
+static int lbs_mesh_config(struct lbs_private *priv, uint16_t action,
+ uint16_t chan)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_meshie *ie;
+ DECLARE_SSID_BUF(ssid);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.channel = cpu_to_le16(chan);
+ ie = (struct mrvl_meshie *)cmd.data;
+
+ switch (action) {
+ case CMD_ACT_MESH_CONFIG_START:
+ ie->id = WLAN_EID_GENERIC;
+ ie->val.oui[0] = 0x00;
+ ie->val.oui[1] = 0x50;
+ ie->val.oui[2] = 0x43;
+ ie->val.type = MARVELL_MESH_IE_TYPE;
+ ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
+ ie->val.version = MARVELL_MESH_IE_VERSION;
+ ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
+ ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
+ ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
+ ie->val.mesh_id_len = priv->mesh_ssid_len;
+ memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len);
+ ie->len = sizeof(struct mrvl_meshie_val) -
+ IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len;
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
+ break;
+ case CMD_ACT_MESH_CONFIG_STOP:
+ break;
+ default:
+ return -1;
+ }
+ lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
+ action, priv->mesh_tlv, chan,
+ print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len));
+
+ return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
+}
+
+int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)
+{
+ return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel);
+}
+
+static uint16_t lbs_mesh_get_channel(struct lbs_private *priv)
+{
+ struct wireless_dev *mesh_wdev = priv->mesh_dev->ieee80211_ptr;
+ if (mesh_wdev->channel)
+ return mesh_wdev->channel->hw_value;
+ else
+ return 1;
+}
+
/***************************************************************************
* Mesh sysfs support
*/
@@ -154,17 +283,11 @@ static ssize_t lbs_mesh_set(struct device *dev,
{
struct lbs_private *priv = to_net_dev(dev)->ml_priv;
int enable;
- int ret, action = CMD_ACT_MESH_CONFIG_STOP;
sscanf(buf, "%x", &enable);
enable = !!enable;
if (enable == !!priv->mesh_dev)
return count;
- if (enable)
- action = CMD_ACT_MESH_CONFIG_START;
- ret = lbs_mesh_config(priv, action, priv->channel);
- if (ret)
- return ret;
if (enable)
lbs_add_mesh(priv);
@@ -199,582 +322,11 @@ static struct attribute *lbs_mesh_sysfs_entries[] = {
NULL,
};
-static struct attribute_group lbs_mesh_attr_group = {
+static const struct attribute_group lbs_mesh_attr_group = {
.attrs = lbs_mesh_sysfs_entries,
};
-
-/***************************************************************************
- * Initializing and starting, stopping mesh
- */
-
-/*
- * Check mesh FW version and appropriately send the mesh start
- * command
- */
-int lbs_init_mesh(struct lbs_private *priv)
-{
- struct net_device *dev = priv->dev;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_MESH);
-
- priv->mesh_connect_status = LBS_DISCONNECTED;
-
- /* Determine mesh_fw_ver from fwrelease and fwcapinfo */
- /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */
- /* 5.110.22 have mesh command with 0xa3 command id */
- /* 10.0.0.p0 FW brings in mesh config command with different id */
- /* Check FW version MSB and initialize mesh_fw_ver */
- if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) {
- /* Enable mesh, if supported, and work out which TLV it uses.
- 0x100 + 291 is an unofficial value used in 5.110.20.pXX
- 0x100 + 37 is the official value used in 5.110.21.pXX
- but we check them in that order because 20.pXX doesn't
- give an error -- it just silently fails. */
-
- /* 5.110.20.pXX firmware will fail the command if the channel
- doesn't match the existing channel. But only if the TLV
- is correct. If the channel is wrong, _BOTH_ versions will
- give an error to 0x100+291, and allow 0x100+37 to succeed.
- It's just that 5.110.20.pXX will not have done anything
- useful */
-
- priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->channel)) {
- priv->mesh_tlv = TLV_TYPE_MESH_ID;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->channel))
- priv->mesh_tlv = 0;
- }
- } else
- if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
- (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) {
- /* 10.0.0.pXX new firmwares should succeed with TLV
- * 0x100+37; Do not invoke command with old TLV.
- */
- priv->mesh_tlv = TLV_TYPE_MESH_ID;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->channel))
- priv->mesh_tlv = 0;
- }
-
-
- if (priv->mesh_tlv) {
- sprintf(priv->mesh_ssid, "mesh");
- priv->mesh_ssid_len = 4;
-
- lbs_add_mesh(priv);
-
- if (device_create_file(&dev->dev, &dev_attr_lbs_mesh))
- netdev_err(dev, "cannot register lbs_mesh attribute\n");
-
- ret = 1;
- }
-
- lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
- return ret;
-}
-
-
-int lbs_deinit_mesh(struct lbs_private *priv)
-{
- struct net_device *dev = priv->dev;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_MESH);
-
- if (priv->mesh_tlv) {
- device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
- ret = 1;
- }
-
- lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
- return ret;
-}
-
-
-/**
- * lbs_mesh_stop - close the mshX interface
- *
- * @dev: A pointer to &net_device structure
- * returns: 0
- */
-static int lbs_mesh_stop(struct net_device *dev)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_MESH);
- spin_lock_irq(&priv->driver_lock);
-
- priv->mesh_open = 0;
- priv->mesh_connect_status = LBS_DISCONNECTED;
-
- netif_stop_queue(dev);
- netif_carrier_off(dev);
-
- spin_unlock_irq(&priv->driver_lock);
-
- schedule_work(&priv->mcast_work);
-
- lbs_deb_leave(LBS_DEB_MESH);
- return 0;
-}
-
-/**
- * lbs_mesh_dev_open - open the mshX interface
- *
- * @dev: A pointer to &net_device structure
- * returns: 0 or -EBUSY if monitor mode active
- */
-static int lbs_mesh_dev_open(struct net_device *dev)
-{
- struct lbs_private *priv = dev->ml_priv;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_NET);
-
- spin_lock_irq(&priv->driver_lock);
-
- if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
- ret = -EBUSY;
- goto out;
- }
-
- priv->mesh_open = 1;
- priv->mesh_connect_status = LBS_CONNECTED;
- netif_carrier_on(dev);
-
- if (!priv->tx_pending_len)
- netif_wake_queue(dev);
- out:
-
- spin_unlock_irq(&priv->driver_lock);
- lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
- return ret;
-}
-
-static const struct net_device_ops mesh_netdev_ops = {
- .ndo_open = lbs_mesh_dev_open,
- .ndo_stop = lbs_mesh_stop,
- .ndo_start_xmit = lbs_hard_start_xmit,
- .ndo_set_mac_address = lbs_set_mac_address,
- .ndo_set_multicast_list = lbs_set_multicast_list,
-};
-
-/**
- * lbs_add_mesh - add mshX interface
- *
- * @priv: A pointer to the &struct lbs_private structure
- * returns: 0 if successful, -X otherwise
- */
-int lbs_add_mesh(struct lbs_private *priv)
-{
- struct net_device *mesh_dev = NULL;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_MESH);
-
- /* Allocate a virtual mesh device */
- mesh_dev = alloc_netdev(0, "msh%d", ether_setup);
- if (!mesh_dev) {
- lbs_deb_mesh("init mshX device failed\n");
- ret = -ENOMEM;
- goto done;
- }
- mesh_dev->ml_priv = priv;
- priv->mesh_dev = mesh_dev;
-
- mesh_dev->netdev_ops = &mesh_netdev_ops;
- mesh_dev->ethtool_ops = &lbs_ethtool_ops;
- memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, ETH_ALEN);
-
- SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
-
- mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
- /* Register virtual mesh interface */
- ret = register_netdev(mesh_dev);
- if (ret) {
- pr_err("cannot register mshX virtual interface\n");
- goto err_free;
- }
-
- ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
- if (ret)
- goto err_unregister;
-
- lbs_persist_config_init(mesh_dev);
-
- /* Everything successful */
- ret = 0;
- goto done;
-
-err_unregister:
- unregister_netdev(mesh_dev);
-
-err_free:
- free_netdev(mesh_dev);
-
-done:
- lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
- return ret;
-}
-
-void lbs_remove_mesh(struct lbs_private *priv)
-{
- struct net_device *mesh_dev;
-
- mesh_dev = priv->mesh_dev;
- if (!mesh_dev)
- return;
-
- lbs_deb_enter(LBS_DEB_MESH);
- netif_stop_queue(mesh_dev);
- netif_carrier_off(mesh_dev);
- sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
- lbs_persist_config_remove(mesh_dev);
- unregister_netdev(mesh_dev);
- priv->mesh_dev = NULL;
- free_netdev(mesh_dev);
- lbs_deb_leave(LBS_DEB_MESH);
-}
-
-
-
-/***************************************************************************
- * Sending and receiving
- */
-struct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
- struct net_device *dev, struct rxpd *rxpd)
-{
- if (priv->mesh_dev) {
- if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) {
- if (rxpd->rx_control & RxPD_MESH_FRAME)
- dev = priv->mesh_dev;
- } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) {
- if (rxpd->u.bss.bss_num == MESH_IFACE_ID)
- dev = priv->mesh_dev;
- }
- }
- return dev;
-}
-
-
-void lbs_mesh_set_txpd(struct lbs_private *priv,
- struct net_device *dev, struct txpd *txpd)
-{
- if (dev == priv->mesh_dev) {
- if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID)
- txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
- else if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
- txpd->u.bss.bss_num = MESH_IFACE_ID;
- }
-}
-
-
-/***************************************************************************
- * Mesh command handling
- */
-
-/**
- * lbs_mesh_bt_add_del - Add or delete Mesh Blinding Table entries
- *
- * @priv: A pointer to &struct lbs_private structure
- * @add: TRUE to add the entry, FALSE to delete it
- * @addr1: Destination address to blind or unblind
- *
- * returns: 0 on success, error on failure
- */
-int lbs_mesh_bt_add_del(struct lbs_private *priv, bool add, u8 *addr1)
-{
- struct cmd_ds_bt_access cmd;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- BUG_ON(addr1 == NULL);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- memcpy(cmd.addr1, addr1, ETH_ALEN);
- if (add) {
- cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_ADD);
- lbs_deb_hex(LBS_DEB_MESH, "BT_ADD: blinded MAC addr",
- addr1, ETH_ALEN);
- } else {
- cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_DEL);
- lbs_deb_hex(LBS_DEB_MESH, "BT_DEL: blinded MAC addr",
- addr1, ETH_ALEN);
- }
-
- ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
-
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return ret;
-}
-
-/**
- * lbs_mesh_bt_reset - Reset/clear the mesh blinding table
- *
- * @priv: A pointer to &struct lbs_private structure
- *
- * returns: 0 on success, error on failure
- */
-int lbs_mesh_bt_reset(struct lbs_private *priv)
-{
- struct cmd_ds_bt_access cmd;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_RESET);
-
- ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
-
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return ret;
-}
-
-/**
- * lbs_mesh_bt_get_inverted - Gets the inverted status of the mesh
- * blinding table
- *
- * Normally the firmware "blinds" or ignores traffic from mesh nodes in the
- * table, but an inverted table allows *only* traffic from nodes listed in
- * the table.
- *
- * @priv: A pointer to &struct lbs_private structure
- * @inverted: On success, TRUE if the blinding table is inverted,
- * FALSE if it is not inverted
- *
- * returns: 0 on success, error on failure
- */
-int lbs_mesh_bt_get_inverted(struct lbs_private *priv, bool *inverted)
-{
- struct cmd_ds_bt_access cmd;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- BUG_ON(inverted == NULL);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_GET_INVERT);
-
- ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
- if (ret == 0)
- *inverted = !!cmd.id;
-
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return ret;
-}
-
-/**
- * lbs_mesh_bt_set_inverted - Sets the inverted status of the mesh
- * blinding table
- *
- * Normally the firmware "blinds" or ignores traffic from mesh nodes in the
- * table, but an inverted table allows *only* traffic from nodes listed in
- * the table.
- *
- * @priv: A pointer to &struct lbs_private structure
- * @inverted: TRUE to invert the blinding table (only traffic from
- * listed nodes allowed), FALSE to return it
- * to normal state (listed nodes ignored)
- *
- * returns: 0 on success, error on failure
- */
-int lbs_mesh_bt_set_inverted(struct lbs_private *priv, bool inverted)
-{
- struct cmd_ds_bt_access cmd;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_SET_INVERT);
- cmd.id = cpu_to_le32(!!inverted);
-
- ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
-
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return ret;
-}
-
-/**
- * lbs_mesh_bt_get_entry - List an entry in the mesh blinding table
- *
- * @priv: A pointer to &struct lbs_private structure
- * @id: The ID of the entry to list
- * @addr1: MAC address associated with the table entry
- *
- * returns: 0 on success, error on failure
- */
-int lbs_mesh_bt_get_entry(struct lbs_private *priv, u32 id, u8 *addr1)
-{
- struct cmd_ds_bt_access cmd;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- BUG_ON(addr1 == NULL);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_SET_INVERT);
- cmd.id = cpu_to_le32(id);
-
- ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
- if (ret == 0)
- memcpy(addr1, cmd.addr1, sizeof(cmd.addr1));
-
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return ret;
-}
-
-/**
- * lbs_cmd_fwt_access - Access the mesh forwarding table
- *
- * @priv: A pointer to &struct lbs_private structure
- * @cmd_action: The forwarding table action to perform
- * @cmd: The pre-filled FWT_ACCESS command
- *
- * returns: 0 on success and 'cmd' will be filled with the
- * firmware's response
- */
-int lbs_cmd_fwt_access(struct lbs_private *priv, u16 cmd_action,
- struct cmd_ds_fwt_access *cmd)
-{
- int ret;
-
- lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
-
- cmd->hdr.command = cpu_to_le16(CMD_FWT_ACCESS);
- cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access));
- cmd->hdr.result = 0;
- cmd->action = cpu_to_le16(cmd_action);
-
- ret = lbs_cmd_with_response(priv, CMD_FWT_ACCESS, cmd);
-
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return 0;
-}
-
-int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
- struct cmd_ds_mesh_access *cmd)
-{
- int ret;
-
- lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
-
- cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
- cmd->hdr.size = cpu_to_le16(sizeof(*cmd));
- cmd->hdr.result = 0;
-
- cmd->action = cpu_to_le16(cmd_action);
-
- ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);
-
- lbs_deb_leave(LBS_DEB_CMD);
- return ret;
-}
-
-static int __lbs_mesh_config_send(struct lbs_private *priv,
- struct cmd_ds_mesh_config *cmd,
- uint16_t action, uint16_t type)
-{
- int ret;
- u16 command = CMD_MESH_CONFIG_OLD;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- /*
- * Command id is 0xac for v10 FW along with mesh interface
- * id in bits 14-13-12.
- */
- if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
- command = CMD_MESH_CONFIG |
- (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);
-
- cmd->hdr.command = cpu_to_le16(command);
- cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
- cmd->hdr.result = 0;
-
- cmd->type = cpu_to_le16(type);
- cmd->action = cpu_to_le16(action);
-
- ret = lbs_cmd_with_response(priv, command, cmd);
-
- lbs_deb_leave(LBS_DEB_CMD);
- return ret;
-}
-
-int lbs_mesh_config_send(struct lbs_private *priv,
- struct cmd_ds_mesh_config *cmd,
- uint16_t action, uint16_t type)
-{
- int ret;
-
- if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
- return -EOPNOTSUPP;
-
- ret = __lbs_mesh_config_send(priv, cmd, action, type);
- return ret;
-}
-
-/* This function is the CMD_MESH_CONFIG legacy function. It only handles the
- * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG
- * are all handled by preparing a struct cmd_ds_mesh_config and passing it to
- * lbs_mesh_config_send.
- */
-int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
-{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_meshie *ie;
- DECLARE_SSID_BUF(ssid);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.channel = cpu_to_le16(chan);
- ie = (struct mrvl_meshie *)cmd.data;
-
- switch (action) {
- case CMD_ACT_MESH_CONFIG_START:
- ie->id = WLAN_EID_GENERIC;
- ie->val.oui[0] = 0x00;
- ie->val.oui[1] = 0x50;
- ie->val.oui[2] = 0x43;
- ie->val.type = MARVELL_MESH_IE_TYPE;
- ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
- ie->val.version = MARVELL_MESH_IE_VERSION;
- ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
- ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
- ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
- ie->val.mesh_id_len = priv->mesh_ssid_len;
- memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len);
- ie->len = sizeof(struct mrvl_meshie_val) -
- IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len;
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
- break;
- case CMD_ACT_MESH_CONFIG_STOP:
- break;
- default:
- return -1;
- }
- lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
- action, priv->mesh_tlv, chan,
- print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len));
-
- return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
-}
-
-
-
/***************************************************************************
* Persistent configuration support
*/
@@ -1231,7 +783,7 @@ static struct attribute *boot_opts_attrs[] = {
NULL
};
-static struct attribute_group boot_opts_group = {
+static const struct attribute_group boot_opts_group = {
.name = "boot_options",
.attrs = boot_opts_attrs,
};
@@ -1244,31 +796,323 @@ static struct attribute *mesh_ie_attrs[] = {
NULL
};
-static struct attribute_group mesh_ie_group = {
+static const struct attribute_group mesh_ie_group = {
.name = "mesh_ie",
.attrs = mesh_ie_attrs,
};
-void lbs_persist_config_init(struct net_device *dev)
+static void lbs_persist_config_init(struct net_device *dev)
{
int ret;
ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group);
ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group);
}
-void lbs_persist_config_remove(struct net_device *dev)
+static void lbs_persist_config_remove(struct net_device *dev)
{
sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group);
sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group);
}
+/***************************************************************************
+ * Initializing and starting, stopping mesh
+ */
+
+/*
+ * Check mesh FW version and appropriately send the mesh start
+ * command
+ */
+int lbs_init_mesh(struct lbs_private *priv)
+{
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+
+ /* Determine mesh_fw_ver from fwrelease and fwcapinfo */
+ /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */
+ /* 5.110.22 have mesh command with 0xa3 command id */
+ /* 10.0.0.p0 FW brings in mesh config command with different id */
+ /* Check FW version MSB and initialize mesh_fw_ver */
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) {
+ /* Enable mesh, if supported, and work out which TLV it uses.
+ 0x100 + 291 is an unofficial value used in 5.110.20.pXX
+ 0x100 + 37 is the official value used in 5.110.21.pXX
+ but we check them in that order because 20.pXX doesn't
+ give an error -- it just silently fails. */
+
+ /* 5.110.20.pXX firmware will fail the command if the channel
+ doesn't match the existing channel. But only if the TLV
+ is correct. If the channel is wrong, _BOTH_ versions will
+ give an error to 0x100+291, and allow 0x100+37 to succeed.
+ It's just that 5.110.20.pXX will not have done anything
+ useful */
+
+ priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) {
+ priv->mesh_tlv = TLV_TYPE_MESH_ID;
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
+ priv->mesh_tlv = 0;
+ }
+ } else
+ if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
+ (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) {
+ /* 10.0.0.pXX new firmwares should succeed with TLV
+ * 0x100+37; Do not invoke command with old TLV.
+ */
+ priv->mesh_tlv = TLV_TYPE_MESH_ID;
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
+ priv->mesh_tlv = 0;
+ }
+
+ /* Stop meshing until interface is brought up */
+ lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1);
+
+ if (priv->mesh_tlv) {
+ sprintf(priv->mesh_ssid, "mesh");
+ priv->mesh_ssid_len = 4;
+ ret = 1;
+ }
+
+ lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
+ return ret;
+}
+
+void lbs_start_mesh(struct lbs_private *priv)
+{
+ lbs_add_mesh(priv);
+
+ if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh))
+ netdev_err(priv->dev, "cannot register lbs_mesh attribute\n");
+}
+
+int lbs_deinit_mesh(struct lbs_private *priv)
+{
+ struct net_device *dev = priv->dev;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+
+ if (priv->mesh_tlv) {
+ device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
+ ret = 1;
+ }
+
+ lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
+ return ret;
+}
+
+
+/**
+ * lbs_mesh_stop - close the mshX interface
+ *
+ * @dev: A pointer to &net_device structure
+ * returns: 0
+ */
+static int lbs_mesh_stop(struct net_device *dev)
+{
+ struct lbs_private *priv = dev->ml_priv;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+ lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
+ lbs_mesh_get_channel(priv));
+
+ spin_lock_irq(&priv->driver_lock);
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ spin_unlock_irq(&priv->driver_lock);
+
+ lbs_update_mcast(priv);
+ if (!lbs_iface_active(priv))
+ lbs_stop_iface(priv);
+
+ lbs_deb_leave(LBS_DEB_MESH);
+ return 0;
+}
+
+/**
+ * lbs_mesh_dev_open - open the mshX interface
+ *
+ * @dev: A pointer to &net_device structure
+ * returns: 0 or -EBUSY if monitor mode active
+ */
+static int lbs_mesh_dev_open(struct net_device *dev)
+{
+ struct lbs_private *priv = dev->ml_priv;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_NET);
+ if (!priv->iface_running) {
+ ret = lbs_start_iface(priv);
+ if (ret)
+ goto out;
+ }
+
+ spin_lock_irq(&priv->driver_lock);
+
+ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+ ret = -EBUSY;
+ spin_unlock_irq(&priv->driver_lock);
+ goto out;
+ }
+
+ netif_carrier_on(dev);
+
+ if (!priv->tx_pending_len)
+ netif_wake_queue(dev);
+
+ spin_unlock_irq(&priv->driver_lock);
+
+ ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
+ lbs_mesh_get_channel(priv));
+
+out:
+ lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
+ return ret;
+}
+
+static const struct net_device_ops mesh_netdev_ops = {
+ .ndo_open = lbs_mesh_dev_open,
+ .ndo_stop = lbs_mesh_stop,
+ .ndo_start_xmit = lbs_hard_start_xmit,
+ .ndo_set_mac_address = lbs_set_mac_address,
+ .ndo_set_rx_mode = lbs_set_multicast_list,
+};
+
+/**
+ * lbs_add_mesh - add mshX interface
+ *
+ * @priv: A pointer to the &struct lbs_private structure
+ * returns: 0 if successful, -X otherwise
+ */
+static int lbs_add_mesh(struct lbs_private *priv)
+{
+ struct net_device *mesh_dev = NULL;
+ struct wireless_dev *mesh_wdev;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+
+ /* Allocate a virtual mesh device */
+ mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ if (!mesh_wdev) {
+ lbs_deb_mesh("init mshX wireless device failed\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ mesh_dev = alloc_netdev(0, "msh%d", ether_setup);
+ if (!mesh_dev) {
+ lbs_deb_mesh("init mshX device failed\n");
+ ret = -ENOMEM;
+ goto err_free_wdev;
+ }
+
+ mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT;
+ mesh_wdev->wiphy = priv->wdev->wiphy;
+ mesh_wdev->netdev = mesh_dev;
+
+ mesh_dev->ml_priv = priv;
+ mesh_dev->ieee80211_ptr = mesh_wdev;
+ priv->mesh_dev = mesh_dev;
+
+ mesh_dev->netdev_ops = &mesh_netdev_ops;
+ mesh_dev->ethtool_ops = &lbs_ethtool_ops;
+ memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, ETH_ALEN);
+
+ SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
+
+ mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+ /* Register virtual mesh interface */
+ ret = register_netdev(mesh_dev);
+ if (ret) {
+ pr_err("cannot register mshX virtual interface\n");
+ goto err_free_netdev;
+ }
+
+ ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
+ if (ret)
+ goto err_unregister;
+
+ lbs_persist_config_init(mesh_dev);
+
+ /* Everything successful */
+ ret = 0;
+ goto done;
+
+err_unregister:
+ unregister_netdev(mesh_dev);
+
+err_free_netdev:
+ free_netdev(mesh_dev);
+
+err_free_wdev:
+ kfree(mesh_wdev);
+
+done:
+ lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
+ return ret;
+}
+
+void lbs_remove_mesh(struct lbs_private *priv)
+{
+ struct net_device *mesh_dev;
+
+ mesh_dev = priv->mesh_dev;
+ if (!mesh_dev)
+ return;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+ netif_stop_queue(mesh_dev);
+ netif_carrier_off(mesh_dev);
+ sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
+ lbs_persist_config_remove(mesh_dev);
+ unregister_netdev(mesh_dev);
+ priv->mesh_dev = NULL;
+ kfree(mesh_dev->ieee80211_ptr);
+ free_netdev(mesh_dev);
+ lbs_deb_leave(LBS_DEB_MESH);
+}
+
+
+/***************************************************************************
+ * Sending and receiving
+ */
+struct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
+ struct net_device *dev, struct rxpd *rxpd)
+{
+ if (priv->mesh_dev) {
+ if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) {
+ if (rxpd->rx_control & RxPD_MESH_FRAME)
+ dev = priv->mesh_dev;
+ } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) {
+ if (rxpd->u.bss.bss_num == MESH_IFACE_ID)
+ dev = priv->mesh_dev;
+ }
+ }
+ return dev;
+}
+
+
+void lbs_mesh_set_txpd(struct lbs_private *priv,
+ struct net_device *dev, struct txpd *txpd)
+{
+ if (dev == priv->mesh_dev) {
+ if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID)
+ txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
+ else if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
+ txpd->u.bss.bss_num = MESH_IFACE_ID;
+ }
+}
+
/***************************************************************************
* Ethtool related
*/
-static const char *mesh_stat_strings[] = {
+static const char * const mesh_stat_strings[] = {
"drop_duplicate_bcast",
"drop_ttl_zero",
"drop_no_fwd_route",
diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/libertas/mesh.h
index ee95c73ed5f4..6603f341c874 100644
--- a/drivers/net/wireless/libertas/mesh.h
+++ b/drivers/net/wireless/libertas/mesh.h
@@ -9,31 +9,25 @@
#include <net/lib80211.h>
#include "host.h"
+#include "dev.h"
#ifdef CONFIG_LIBERTAS_MESH
-/* Mesh statistics */
-struct lbs_mesh_stats {
- u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */
- u32 fwd_unicast_cnt; /* Fwd: Unicast counter */
- u32 fwd_drop_ttl; /* Fwd: TTL zero */
- u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */
- u32 fwd_drop_noroute; /* Fwd: No route to Destination */
- u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */
- u32 drop_blind; /* Rx: Dropped by blinding table */
- u32 tx_failed_cnt; /* Tx: Failed transmissions */
-};
-
-
struct net_device;
-struct lbs_private;
int lbs_init_mesh(struct lbs_private *priv);
+void lbs_start_mesh(struct lbs_private *priv);
int lbs_deinit_mesh(struct lbs_private *priv);
-int lbs_add_mesh(struct lbs_private *priv);
void lbs_remove_mesh(struct lbs_private *priv);
+static inline bool lbs_mesh_activated(struct lbs_private *priv)
+{
+ /* Mesh SSID is only programmed after successful init */
+ return priv->mesh_ssid_len != 0;
+}
+
+int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel);
/* Sending / Receiving */
@@ -52,29 +46,6 @@ struct cmd_ds_command;
struct cmd_ds_mesh_access;
struct cmd_ds_mesh_config;
-int lbs_mesh_bt_add_del(struct lbs_private *priv, bool add, u8 *addr1);
-int lbs_mesh_bt_reset(struct lbs_private *priv);
-int lbs_mesh_bt_get_inverted(struct lbs_private *priv, bool *inverted);
-int lbs_mesh_bt_set_inverted(struct lbs_private *priv, bool inverted);
-int lbs_mesh_bt_get_entry(struct lbs_private *priv, u32 id, u8 *addr1);
-
-int lbs_cmd_fwt_access(struct lbs_private *priv, u16 cmd_action,
- struct cmd_ds_fwt_access *cmd);
-
-int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
- struct cmd_ds_mesh_access *cmd);
-int lbs_mesh_config_send(struct lbs_private *priv,
- struct cmd_ds_mesh_config *cmd,
- uint16_t action, uint16_t type);
-int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan);
-
-
-
-/* Persistent configuration */
-
-void lbs_persist_config_init(struct net_device *net);
-void lbs_persist_config_remove(struct net_device *net);
-
/* Ethtool statistics */
@@ -87,22 +58,17 @@ void lbs_mesh_ethtool_get_strings(struct net_device *dev,
uint32_t stringset, uint8_t *s);
-/* Accessors */
-
-#define lbs_mesh_open(priv) (priv->mesh_open)
-#define lbs_mesh_connected(priv) (priv->mesh_connect_status == LBS_CONNECTED)
-
#else
#define lbs_init_mesh(priv)
#define lbs_deinit_mesh(priv)
+#define lbs_start_mesh(priv)
#define lbs_add_mesh(priv)
#define lbs_remove_mesh(priv)
#define lbs_mesh_set_dev(priv, dev, rxpd) (dev)
#define lbs_mesh_set_txpd(priv, dev, txpd)
-#define lbs_mesh_config(priv, enable, chan)
-#define lbs_mesh_open(priv) (0)
-#define lbs_mesh_connected(priv) (0)
+#define lbs_mesh_set_channel(priv, channel) (0)
+#define lbs_mesh_activated(priv) (false)
#endif
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c
index fdb0448301a0..c7366b07b568 100644
--- a/drivers/net/wireless/libertas/rx.c
+++ b/drivers/net/wireless/libertas/rx.c
@@ -5,8 +5,10 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/etherdevice.h>
+#include <linux/hardirq.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/export.h>
#include <net/cfg80211.h>
#include "defs.h"
@@ -14,6 +16,7 @@
#include "radiotap.h"
#include "decl.h"
#include "dev.h"
+#include "mesh.h"
struct eth803hdr {
u8 dest_addr[6];
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c
index bbb95f88dc01..c025f9c18282 100644
--- a/drivers/net/wireless/libertas/tx.c
+++ b/drivers/net/wireless/libertas/tx.c
@@ -1,9 +1,11 @@
/*
* This file contains the handling of TX in wlan driver.
*/
+#include <linux/hardirq.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/sched.h>
+#include <linux/export.h>
#include <net/cfg80211.h>
#include "host.h"
@@ -11,6 +13,7 @@
#include "decl.h"
#include "defs.h"
#include "dev.h"
+#include "mesh.h"
/**
* convert_radiotap_rate_to_mv - converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE
@@ -198,7 +201,7 @@ void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count)
if (priv->connect_status == LBS_CONNECTED)
netif_wake_queue(priv->dev);
- if (priv->mesh_dev && lbs_mesh_connected(priv))
+ if (priv->mesh_dev && netif_running(priv->mesh_dev))
netif_wake_queue(priv->mesh_dev);
}
EXPORT_SYMBOL_GPL(lbs_send_tx_feedback);
diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/libertas_tf/cmd.c
index 8945afd6ce3e..909ac3685010 100644
--- a/drivers/net/wireless/libertas_tf/cmd.c
+++ b/drivers/net/wireless/libertas_tf/cmd.c
@@ -9,7 +9,9 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/hardirq.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "libertas_tf.h"
diff --git a/drivers/net/wireless/libertas_tf/deb_defs.h b/drivers/net/wireless/libertas_tf/deb_defs.h
index ae753962d8b5..4bd3dc5adf7c 100644
--- a/drivers/net/wireless/libertas_tf/deb_defs.h
+++ b/drivers/net/wireless/libertas_tf/deb_defs.h
@@ -3,7 +3,7 @@
* global variable declaration.
*/
#ifndef _LBS_DEB_DEFS_H_
-#define _LBS_DEB_EFS_H_
+#define _LBS_DEB_DEFS_H_
#ifndef DRV_NAME
#define DRV_NAME "libertas_tf"
diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c
index ba7d96584cb6..68202e63873a 100644
--- a/drivers/net/wireless/libertas_tf/if_usb.c
+++ b/drivers/net/wireless/libertas_tf/if_usb.c
@@ -15,7 +15,7 @@
#include "if_usb.h"
#include <linux/delay.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c
index d4005081f1df..ceb51b6e6702 100644
--- a/drivers/net/wireless/libertas_tf/main.c
+++ b/drivers/net/wireless/libertas_tf/main.c
@@ -9,9 +9,11 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/hardirq.h>
#include <linux/slab.h>
#include <linux/etherdevice.h>
+#include <linux/module.h>
#include "libertas_tf.h"
#define DRIVER_RELEASE_VERSION "004.p0"
@@ -585,7 +587,7 @@ int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb)
need_padding ^= ieee80211_has_a4(hdr->frame_control);
need_padding ^= ieee80211_is_data_qos(hdr->frame_control) &&
(*ieee80211_get_qos_ctl(hdr) &
- IEEE80211_QOS_CONTROL_A_MSDU_PRESENT);
+ IEEE80211_QOS_CTL_A_MSDU_PRESENT);
if (need_padding) {
memmove(skb->data + 2, skb->data, skb->len);
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 9d4a40ee16c4..523ad55a2885 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1,6 +1,7 @@
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,11 +26,18 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <net/genetlink.h>
+#include "mac80211_hwsim.h"
+
+#define WARN_QUEUE 100
+#define MAX_QUEUE 200
MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
MODULE_LICENSE("GPL");
+int wmediumd_pid;
static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
@@ -302,6 +310,7 @@ struct mac80211_hwsim_data {
struct dentry *debugfs;
struct dentry *debugfs_ps;
+ struct sk_buff_head pending; /* packets pending */
/*
* Only radios in the same group can communicate together (the
* channel has to match too). Each bit represents a group. A
@@ -322,6 +331,32 @@ struct hwsim_radiotap_hdr {
__le16 rt_chbitmask;
} __packed;
+/* MAC80211_HWSIM netlinf family */
+static struct genl_family hwsim_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = "MAC80211_HWSIM",
+ .version = 1,
+ .maxattr = HWSIM_ATTR_MAX,
+};
+
+/* MAC80211_HWSIM netlink policy */
+
+static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+ [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
+ [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
+ [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
+ [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
+ .len = IEEE80211_TX_MAX_RATES*sizeof(
+ struct hwsim_tx_rate)},
+ [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 },
+};
static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
struct net_device *dev)
@@ -478,9 +513,89 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
return md.ret;
}
+static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
+ struct sk_buff *my_skb,
+ int dst_pid)
+{
+ struct sk_buff *skb;
+ struct mac80211_hwsim_data *data = hw->priv;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb);
+ void *msg_head;
+ unsigned int hwsim_flags = 0;
+ int i;
+ struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];
+
+ if (data->idle) {
+ wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
+ dev_kfree_skb(my_skb);
+ return;
+ }
+
+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+ /* If the queue contains MAX_QUEUE skb's drop some */
+ if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
+ /* Droping until WARN_QUEUE level */
+ while (skb_queue_len(&data->pending) >= WARN_QUEUE)
+ skb_dequeue(&data->pending);
+ }
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (skb == NULL)
+ goto nla_put_failure;
+
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
+ goto nla_put_failure;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
+ sizeof(struct mac_address), data->addresses[1].addr);
-static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
- struct sk_buff *skb)
+ /* We get the skb->data */
+ NLA_PUT(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data);
+
+ /* We get the flags for this transmission, and we translate them to
+ wmediumd flags */
+
+ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
+ hwsim_flags |= HWSIM_TX_CTL_REQ_TX_STATUS;
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ hwsim_flags |= HWSIM_TX_CTL_NO_ACK;
+
+ NLA_PUT_U32(skb, HWSIM_ATTR_FLAGS, hwsim_flags);
+
+ /* We get the tx control (rate and retries) info*/
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ tx_attempts[i].idx = info->status.rates[i].idx;
+ tx_attempts[i].count = info->status.rates[i].count;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_TX_INFO,
+ sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES,
+ tx_attempts);
+
+ /* We create a cookie to identify this skb */
+ NLA_PUT_U64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb);
+
+ genlmsg_end(skb, msg_head);
+ genlmsg_unicast(&init_net, skb, dst_pid);
+
+ /* Enqueue the packet */
+ skb_queue_tail(&data->pending, my_skb);
+ return;
+
+nla_put_failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+}
+
+static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
{
struct mac80211_hwsim_data *data = hw->priv, *data2;
bool ack = false;
@@ -498,6 +613,12 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
rx_status.freq = data->channel->center_freq;
rx_status.band = data->channel->band;
rx_status.rate_idx = info->control.rates[0].idx;
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
+ rx_status.flag |= RX_FLAG_HT;
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ rx_status.flag |= RX_FLAG_40MHZ;
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
+ rx_status.flag |= RX_FLAG_SHORT_GI;
/* TODO: simulate real signal strength (and optional packet loss) */
rx_status.signal = data->power_level - 50;
@@ -540,11 +661,11 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
return ack;
}
-
static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
bool ack;
struct ieee80211_tx_info *txi;
+ int _pid;
mac80211_hwsim_monitor_rx(hw, skb);
@@ -554,7 +675,15 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}
- ack = mac80211_hwsim_tx_frame(hw, skb);
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
+
+ /* NO wmediumd detected, perfect medium simulation */
+ ack = mac80211_hwsim_tx_frame_no_nl(hw, skb);
+
if (ack && skb->len >= 16) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
mac80211_hwsim_monitor_ack(hw, hdr->addr2);
@@ -635,6 +764,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
struct ieee80211_hw *hw = arg;
struct sk_buff *skb;
struct ieee80211_tx_info *info;
+ int _pid;
hwsim_check_magic(vif);
@@ -649,7 +779,14 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
info = IEEE80211_SKB_CB(skb);
mac80211_hwsim_monitor_rx(hw, skb);
- mac80211_hwsim_tx_frame(hw, skb);
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
+
+ mac80211_hwsim_tx_frame_no_nl(hw, skb);
dev_kfree_skb(skb);
}
@@ -834,7 +971,8 @@ static int mac80211_hwsim_set_tim(struct ieee80211_hw *hw,
}
static int mac80211_hwsim_conf_tx(
- struct ieee80211_hw *hw, u16 queue,
+ struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
wiphy_debug(hw->wiphy,
@@ -966,12 +1104,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
{
- /*
- * In this special case, there's nothing we need to
- * do because hwsim does transmission synchronously.
- * In the future, when it does transmissions via
- * userspace, we may need to do something.
- */
+ /* Not implemented, queues only on kernel side */
}
struct hw_scan_done {
@@ -1005,6 +1138,8 @@ static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
for (i = 0; i < req->n_channels; i++)
printk(KERN_DEBUG "hwsim hw_scan freq %d\n",
req->channels[i]->center_freq);
+ print_hex_dump(KERN_DEBUG, "scan IEs: ", DUMP_PREFIX_OFFSET,
+ 16, 1, req->ie, req->ie_len, 1);
ieee80211_queue_delayed_work(hw, &hsd->w, 2 * HZ);
@@ -1119,6 +1254,7 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
struct sk_buff *skb;
struct ieee80211_pspoll *pspoll;
+ int _pid;
if (!vp->assoc)
return;
@@ -1137,8 +1273,15 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
pspoll->aid = cpu_to_le16(0xc000 | vp->aid);
memcpy(pspoll->bssid, vp->bssid, ETH_ALEN);
memcpy(pspoll->ta, mac, ETH_ALEN);
- if (!mac80211_hwsim_tx_frame(data->hw, skb))
- printk(KERN_DEBUG "%s: PS-Poll frame not ack'ed\n", __func__);
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(data->hw, skb, _pid);
+
+ if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
+ printk(KERN_DEBUG "%s: PS-poll frame not ack'ed\n", __func__);
dev_kfree_skb(skb);
}
@@ -1149,6 +1292,7 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
struct sk_buff *skb;
struct ieee80211_hdr *hdr;
+ int _pid;
if (!vp->assoc)
return;
@@ -1168,7 +1312,14 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
memcpy(hdr->addr1, vp->bssid, ETH_ALEN);
memcpy(hdr->addr2, mac, ETH_ALEN);
memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
- if (!mac80211_hwsim_tx_frame(data->hw, skb))
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(data->hw, skb, _pid);
+
+ if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
dev_kfree_skb(skb);
}
@@ -1248,6 +1399,273 @@ DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");
+struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+ struct mac_address *addr)
+{
+ struct mac80211_hwsim_data *data;
+ bool _found = false;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (memcmp(data->addresses[1].addr, addr,
+ sizeof(struct mac_address)) == 0) {
+ _found = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ if (!_found)
+ return NULL;
+
+ return data;
+}
+
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct ieee80211_hdr *hdr;
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_tx_info *txi;
+ struct hwsim_tx_rate *tx_attempts;
+ struct sk_buff __user *ret_skb;
+ struct sk_buff *skb, *tmp;
+ struct mac_address *src;
+ unsigned int hwsim_flags;
+
+ int i;
+ bool found = false;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] ||
+ !info->attrs[HWSIM_ATTR_FLAGS] ||
+ !info->attrs[HWSIM_ATTR_COOKIE] ||
+ !info->attrs[HWSIM_ATTR_TX_INFO])
+ goto out;
+
+ src = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+ hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
+
+ ret_skb = (struct sk_buff __user *)
+ (unsigned long) nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
+
+ data2 = get_hwsim_data_ref_from_addr(src);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* look for the skb matching the cookie passed back from user */
+ skb_queue_walk_safe(&data2->pending, skb, tmp) {
+ if (skb == ret_skb) {
+ skb_unlink(skb, &data2->pending);
+ found = true;
+ break;
+ }
+ }
+
+ /* not found */
+ if (!found)
+ goto out;
+
+ /* Tx info received because the frame was broadcasted on user space,
+ so we get all the necessary info: tx attempts and skb control buff */
+
+ tx_attempts = (struct hwsim_tx_rate *)nla_data(
+ info->attrs[HWSIM_ATTR_TX_INFO]);
+
+ /* now send back TX status */
+ txi = IEEE80211_SKB_CB(skb);
+
+ if (txi->control.vif)
+ hwsim_check_magic(txi->control.vif);
+ if (txi->control.sta)
+ hwsim_check_sta_magic(txi->control.sta);
+
+ ieee80211_tx_info_clear_status(txi);
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ txi->status.rates[i].idx = tx_attempts[i].idx;
+ txi->status.rates[i].count = tx_attempts[i].count;
+ /*txi->status.rates[i].flags = 0;*/
+ }
+
+ txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ if (!(hwsim_flags & HWSIM_TX_CTL_NO_ACK) &&
+ (hwsim_flags & HWSIM_TX_STAT_ACK)) {
+ if (skb->len >= 16) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
+ }
+ }
+ ieee80211_tx_status_irqsafe(data2->hw, skb);
+ return 0;
+out:
+ return -EINVAL;
+
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_rx_status rx_status;
+ struct mac_address *dst;
+ int frame_data_len;
+ char *frame_data;
+ struct sk_buff *skb = NULL;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] ||
+ !info->attrs[HWSIM_ATTR_FRAME] ||
+ !info->attrs[HWSIM_ATTR_RX_RATE] ||
+ !info->attrs[HWSIM_ATTR_SIGNAL])
+ goto out;
+
+ dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
+
+ frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+
+ /* Allocate new skb here */
+ skb = alloc_skb(frame_data_len, GFP_KERNEL);
+ if (skb == NULL)
+ goto err;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto err;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* check if radio is configured properly */
+
+ if (data2->idle || !data2->started || !data2->channel)
+ goto out;
+
+ /*A frame is received from user space*/
+ memset(&rx_status, 0, sizeof(rx_status));
+ rx_status.freq = data2->channel->center_freq;
+ rx_status.band = data2->channel->band;
+ rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
+ rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(data2->hw, skb);
+
+ return 0;
+err:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ goto out;
+out:
+ dev_kfree_skb(skb);
+ return -EINVAL;
+}
+
+static int hwsim_register_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ if (info == NULL)
+ goto out;
+
+ wmediumd_pid = info->snd_pid;
+
+ printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_pid);
+
+ return 0;
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -EINVAL;
+}
+
+/* Generic Netlink operations array */
+static struct genl_ops hwsim_ops[] = {
+ {
+ .cmd = HWSIM_CMD_REGISTER,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_register_received_nl,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = HWSIM_CMD_FRAME,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_cloned_frame_received_nl,
+ },
+ {
+ .cmd = HWSIM_CMD_TX_INFO_FRAME,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_tx_info_frame_received_nl,
+ },
+};
+
+static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
+ unsigned long state,
+ void *_notify)
+{
+ struct netlink_notify *notify = _notify;
+
+ if (state != NETLINK_URELEASE)
+ return NOTIFY_DONE;
+
+ if (notify->pid == wmediumd_pid) {
+ printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
+ " socket, switching to perfect channel medium\n");
+ wmediumd_pid = 0;
+ }
+ return NOTIFY_DONE;
+
+}
+
+static struct notifier_block hwsim_netlink_notifier = {
+ .notifier_call = mac80211_hwsim_netlink_notify,
+};
+
+static int hwsim_init_netlink(void)
+{
+ int rc;
+ printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+ wmediumd_pid = 0;
+
+ rc = genl_register_family_with_ops(&hwsim_genl_family,
+ hwsim_ops, ARRAY_SIZE(hwsim_ops));
+ if (rc)
+ goto failure;
+
+ rc = netlink_register_notifier(&hwsim_netlink_notifier);
+ if (rc)
+ goto failure;
+
+ return 0;
+
+failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -EINVAL;
+}
+
+static void hwsim_exit_netlink(void)
+{
+ int ret;
+
+ printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
+ /* unregister the notifier */
+ netlink_unregister_notifier(&hwsim_netlink_notifier);
+ /* unregister the family */
+ ret = genl_unregister_family(&hwsim_genl_family);
+ if (ret)
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "unregister family %i\n", ret);
+}
+
static int __init init_mac80211_hwsim(void)
{
int i, err = 0;
@@ -1298,6 +1716,7 @@ static int __init init_mac80211_hwsim(void)
goto failed_drvdata;
}
data->dev->driver = &mac80211_hwsim_driver;
+ skb_queue_head_init(&data->pending);
SET_IEEE80211_DEV(hw, data->dev);
addr[3] = i >> 8;
@@ -1379,6 +1798,10 @@ static int __init init_mac80211_hwsim(void)
data->group = 1;
mutex_init(&data->mutex);
+ /* Enable frame retransmissions for lossy channels */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
+
/* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
case HWSIM_REGTEST_DISABLED:
@@ -1515,12 +1938,29 @@ static int __init init_mac80211_hwsim(void)
if (hwsim_mon == NULL)
goto failed;
- err = register_netdev(hwsim_mon);
+ rtnl_lock();
+
+ err = dev_alloc_name(hwsim_mon, hwsim_mon->name);
if (err < 0)
goto failed_mon;
+
+ err = register_netdevice(hwsim_mon);
+ if (err < 0)
+ goto failed_mon;
+
+ rtnl_unlock();
+
+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto failed_nl;
+
return 0;
+failed_nl:
+ printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n");
+ return err;
+
failed_mon:
rtnl_unlock();
free_netdev(hwsim_mon);
@@ -1541,6 +1981,8 @@ static void __exit exit_mac80211_hwsim(void)
{
printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");
+ hwsim_exit_netlink();
+
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
new file mode 100644
index 000000000000..afaad5a443b6
--- /dev/null
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -0,0 +1,133 @@
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MAC80211_HWSIM_H
+#define __MAC80211_HWSIM_H
+
+/**
+ * enum hwsim_tx_control_flags - flags to describe transmission info/status
+ *
+ * These flags are used to give the wmediumd extra information in order to
+ * modify its behavior for each frame
+ *
+ * @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame.
+ * @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack
+ * @HWSIM_TX_STAT_ACK: Frame was acknowledged
+ *
+ */
+enum hwsim_tx_control_flags {
+ HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0),
+ HWSIM_TX_CTL_NO_ACK = BIT(1),
+ HWSIM_TX_STAT_ACK = BIT(2),
+};
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * entities such as wmediumd to receive and process all broadcasted
+ * frames from a mac80211_hwsim radio device.
+ *
+ * This allow user space applications to decide if the frame should be
+ * dropped or not and implement a wireless medium simulator at user space.
+ *
+ * Registration is done by sending a register message to the driver and
+ * will be automatically unregistered if the user application doesn't
+ * responds to sent frames.
+ * Once registered the user application has to take responsibility of
+ * broadcasting the frames to all listening mac80211_hwsim radio
+ * interfaces.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * enum hwsim_commands - supported hwsim commands
+ *
+ * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac80211_hwsim radio device.
+ * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
+ * space, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
+ * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
+ * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
+ * kernel, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
+ * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @__HWSIM_CMD_MAX: enum limit
+ */
+enum {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ __HWSIM_CMD_MAX,
+};
+#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+
+/**
+ * enum hwsim_attrs - hwsim netlink attributes
+ *
+ * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
+ * the frame is broadcasted to
+ * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
+ * the frame was broadcasted from
+ * @HWSIM_ATTR_FRAME: Data array
+ * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
+ properly the frame at user space
+ * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
+ space
+ * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
+ space
+ * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
+ * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
+ * @__HWSIM_ATTR_MAX: enum limit
+ */
+
+
+enum {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_FRAME,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_COOKIE,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
+/**
+ * struct hwsim_tx_rate - rate selection/status
+ *
+ * @idx: rate index to attempt to send with
+ * @count: number of tries in this rate before going to the next rate
+ *
+ * A value of -1 for @idx indicates an invalid rate and, if used
+ * in an array of retry rates, that no more rates should be tried.
+ *
+ * When used for transmit status reporting, the driver should
+ * always report the rate and number of retries used.
+ *
+ */
+struct hwsim_tx_rate {
+ s8 idx;
+ u8 count;
+} __packed;
+
+#endif /* __MAC80211_HWSIM_H */
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
index 916183d39009..34bba5234294 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/mwifiex/11n.c
@@ -185,13 +185,12 @@ int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv,
*
* Handling includes changing the header fields into CPU format.
*/
-int mwifiex_ret_11n_cfg(struct host_cmd_ds_command *resp, void *data_buf)
+int mwifiex_ret_11n_cfg(struct host_cmd_ds_command *resp,
+ struct mwifiex_ds_11n_tx_cfg *tx_cfg)
{
- struct mwifiex_ds_11n_tx_cfg *tx_cfg;
struct host_cmd_ds_11n_cfg *htcfg = &resp->params.htcfg;
- if (data_buf) {
- tx_cfg = (struct mwifiex_ds_11n_tx_cfg *) data_buf;
+ if (tx_cfg) {
tx_cfg->tx_htcap = le16_to_cpu(htcfg->ht_tx_cap);
tx_cfg->tx_htinfo = le16_to_cpu(htcfg->ht_tx_info);
}
@@ -208,11 +207,10 @@ int mwifiex_ret_11n_cfg(struct host_cmd_ds_command *resp, void *data_buf)
*/
int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd, int cmd_action,
- void *data_buf)
+ u16 *buf_size)
{
struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf;
u16 action = (u16) cmd_action;
- u16 buf_size = *((u16 *) data_buf);
cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF);
cmd->size =
@@ -220,8 +218,8 @@ int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,
tx_buf->action = cpu_to_le16(action);
switch (action) {
case HostCmd_ACT_GEN_SET:
- dev_dbg(priv->adapter->dev, "cmd: set tx_buf=%d\n", buf_size);
- tx_buf->buff_size = cpu_to_le16(buf_size);
+ dev_dbg(priv->adapter->dev, "cmd: set tx_buf=%d\n", *buf_size);
+ tx_buf->buff_size = cpu_to_le16(*buf_size);
break;
case HostCmd_ACT_GEN_GET:
default:
@@ -240,13 +238,12 @@ int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,
* - Ensuring correct endian-ness
*/
int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
- int cmd_action, void *data_buf)
+ int cmd_action,
+ struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl)
{
struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =
&cmd->params.amsdu_aggr_ctrl;
u16 action = (u16) cmd_action;
- struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl =
- (struct mwifiex_ds_11n_amsdu_aggr_ctrl *) data_buf;
cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL);
cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl)
@@ -272,15 +269,13 @@ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
* Handling includes changing the header fields into CPU format.
*/
int mwifiex_ret_amsdu_aggr_ctrl(struct host_cmd_ds_command *resp,
- void *data_buf)
+ struct mwifiex_ds_11n_amsdu_aggr_ctrl
+ *amsdu_aggr_ctrl)
{
- struct mwifiex_ds_11n_amsdu_aggr_ctrl *amsdu_aggr_ctrl;
struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =
&resp->params.amsdu_aggr_ctrl;
- if (data_buf) {
- amsdu_aggr_ctrl =
- (struct mwifiex_ds_11n_amsdu_aggr_ctrl *) data_buf;
+ if (amsdu_aggr_ctrl) {
amsdu_aggr_ctrl->enable = le16_to_cpu(amsdu_ctrl->enable);
amsdu_aggr_ctrl->curr_buf_size =
le16_to_cpu(amsdu_ctrl->curr_buf_size);
@@ -296,12 +291,10 @@ int mwifiex_ret_amsdu_aggr_ctrl(struct host_cmd_ds_command *resp,
* - Setting HT Tx capability and HT Tx information fields
* - Ensuring correct endian-ness
*/
-int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd,
- u16 cmd_action, void *data_buf)
+int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action,
+ struct mwifiex_ds_11n_tx_cfg *txcfg)
{
struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg;
- struct mwifiex_ds_11n_tx_cfg *txcfg =
- (struct mwifiex_ds_11n_tx_cfg *) data_buf;
cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG);
cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN);
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h
index a4390a1a2a9f..90b421e343d4 100644
--- a/drivers/net/wireless/mwifiex/11n.h
+++ b/drivers/net/wireless/mwifiex/11n.h
@@ -29,9 +29,9 @@ int mwifiex_ret_11n_delba(struct mwifiex_private *priv,
int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp);
int mwifiex_ret_11n_cfg(struct host_cmd_ds_command *resp,
- void *data_buf);
-int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd,
- u16 cmd_action, void *data_buf);
+ struct mwifiex_ds_11n_tx_cfg *tx_cfg);
+int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action,
+ struct mwifiex_ds_11n_tx_cfg *txcfg);
int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
struct mwifiex_bssdescriptor *bss_desc,
@@ -62,12 +62,14 @@ int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv,
int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv,
struct mwifiex_ds_tx_ba_stream_tbl *buf);
int mwifiex_ret_amsdu_aggr_ctrl(struct host_cmd_ds_command *resp,
- void *data_buf);
+ struct mwifiex_ds_11n_amsdu_aggr_ctrl
+ *amsdu_aggr_ctrl);
int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
- int cmd_action, void *data_buf);
+ int cmd_action, u16 *buf_size);
int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
- int cmd_action, void *data_buf);
+ int cmd_action,
+ struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl);
/*
* This function checks whether AMPDU is allowed or not for a particular TID.
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c
index f807447e4d99..079e5532e686 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.c
+++ b/drivers/net/wireless/mwifiex/11n_aggr.c
@@ -164,12 +164,13 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
struct mwifiex_tx_param tx_param;
struct txpd *ptx_pd = NULL;
- if (skb_queue_empty(&pra_list->skb_head)) {
+ skb_src = skb_peek(&pra_list->skb_head);
+ if (!skb_src) {
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
ra_list_flags);
return 0;
}
- skb_src = skb_peek(&pra_list->skb_head);
+
tx_info_src = MWIFIEX_SKB_TXCB(skb_src);
skb_aggr = dev_alloc_skb(adapter->tx_buf_size);
if (!skb_aggr) {
@@ -184,17 +185,14 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
tx_info_aggr->bss_index = tx_info_src->bss_index;
skb_aggr->priority = skb_src->priority;
- while (skb_src && ((skb_headroom(skb_aggr) + skb_src->len
- + LLC_SNAP_LEN)
- <= adapter->tx_buf_size)) {
+ do {
+ /* Check if AMSDU can accommodate this MSDU */
+ if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN))
+ break;
- if (!skb_queue_empty(&pra_list->skb_head))
- skb_src = skb_dequeue(&pra_list->skb_head);
- else
- skb_src = NULL;
+ skb_src = skb_dequeue(&pra_list->skb_head);
- if (skb_src)
- pra_list->total_pkts_size -= skb_src->len;
+ pra_list->total_pkts_size -= skb_src->len;
atomic_dec(&priv->wmm.tx_pkts_queued);
@@ -212,11 +210,15 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
return -1;
}
- if (!skb_queue_empty(&pra_list->skb_head))
- skb_src = skb_peek(&pra_list->skb_head);
- else
- skb_src = NULL;
- }
+ if (skb_tailroom(skb_aggr) < pad) {
+ pad = 0;
+ break;
+ }
+ skb_put(skb_aggr, pad);
+
+ skb_src = skb_peek(&pra_list->skb_head);
+
+ } while (skb_src);
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags);
@@ -230,14 +232,21 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
skb_push(skb_aggr, headroom);
- tx_param.next_pkt_len = ((pra_list->total_pkts_size) ?
- (((pra_list->total_pkts_size) >
- adapter->tx_buf_size) ? adapter->
- tx_buf_size : pra_list->total_pkts_size +
- LLC_SNAP_LEN + sizeof(struct txpd)) : 0);
+ /*
+ * Padding per MSDU will affect the length of next
+ * packet and hence the exact length of next packet
+ * is uncertain here.
+ *
+ * Also, aggregation of transmission buffer, while
+ * downloading the data to the card, wont gain much
+ * on the AMSDU packets as the AMSDU packets utilizes
+ * the transmission buffer space to the maximum
+ * (adapter->tx_buf_size).
+ */
+ tx_param.next_pkt_len = 0;
+
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
- skb_aggr->data,
- skb_aggr->len, &tx_param);
+ skb_aggr, &tx_param);
switch (ret) {
case -EBUSY:
spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/mwifiex/11n_aggr.h
index 9c6dca7ab02c..900e1c62a0cc 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.h
+++ b/drivers/net/wireless/mwifiex/11n_aggr.h
@@ -21,6 +21,7 @@
#define _MWIFIEX_11N_AGGR_H_
#define PKT_TYPE_AMSDU 0xE6
+#define MIN_NUM_AMSDU 2
int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv,
struct sk_buff *skb);
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
index e5dfdc39a921..7aa9aa0ac958 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c
@@ -328,13 +328,12 @@ int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf)
*/
int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
- void *data_buf)
+ struct host_cmd_ds_11n_addba_req
+ *cmd_addba_req)
{
struct host_cmd_ds_11n_addba_rsp *add_ba_rsp =
(struct host_cmd_ds_11n_addba_rsp *)
&cmd->params.add_ba_rsp;
- struct host_cmd_ds_11n_addba_req *cmd_addba_req =
- (struct host_cmd_ds_11n_addba_req *) data_buf;
u8 tid;
int win_size;
uint16_t block_ack_param_set;
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h
index f3ca8c8c18f9..033c8adbdcd4 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.h
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h
@@ -52,8 +52,9 @@ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd,
void *data_buf);
int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
- struct host_cmd_ds_command
- *cmd, void *data_buf);
+ struct host_cmd_ds_command *cmd,
+ struct host_cmd_ds_11n_addba_req
+ *cmd_addba_req);
int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd,
void *data_buf);
void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv);
diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig
index 86962920cef3..8f2797aa0c60 100644
--- a/drivers/net/wireless/mwifiex/Kconfig
+++ b/drivers/net/wireless/mwifiex/Kconfig
@@ -19,3 +19,14 @@ config MWIFIEX_SDIO
If you choose to build it as a module, it will be called
mwifiex_sdio.
+
+config MWIFIEX_PCIE
+ tristate "Marvell WiFi-Ex Driver for PCIE 8766"
+ depends on MWIFIEX && PCI
+ select FW_LOADER
+ ---help---
+ This adds support for wireless adapters based on Marvell
+ 8766 chipset with PCIe interface.
+
+ If you choose to build it as a module, it will be called
+ mwifiex_pcie.
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile
index 42cb733ea33a..b0257ad1bbed 100644
--- a/drivers/net/wireless/mwifiex/Makefile
+++ b/drivers/net/wireless/mwifiex/Makefile
@@ -39,3 +39,6 @@ obj-$(CONFIG_MWIFIEX) += mwifiex.o
mwifiex_sdio-y += sdio.o
obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o
+
+mwifiex_pcie-y += pcie.o
+obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index 687c1f223497..462c71067bfb 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -543,12 +543,28 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
ret = -EFAULT;
}
+ /*
+ * Bit 0 in tx_htinfo indicates that current Tx rate is 11n rate. Valid
+ * MCS index values for us are 0 to 7.
+ */
+ if ((priv->tx_htinfo & BIT(0)) && (priv->tx_rate < 8)) {
+ sinfo->txrate.mcs = priv->tx_rate;
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+ /* 40MHz rate */
+ if (priv->tx_htinfo & BIT(1))
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ /* SGI enabled */
+ if (priv->tx_htinfo & BIT(2))
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ }
+
sinfo->rx_bytes = priv->stats.rx_bytes;
sinfo->tx_bytes = priv->stats.tx_bytes;
sinfo->rx_packets = priv->stats.rx_packets;
sinfo->tx_packets = priv->stats.tx_packets;
- sinfo->signal = priv->w_stats.qual.level;
- sinfo->txrate.legacy = rate.rate;
+ sinfo->signal = priv->qual_level;
+ /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */
+ sinfo->txrate.legacy = rate.rate * 5;
return ret;
}
@@ -565,8 +581,6 @@ mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- mwifiex_dump_station_info(priv, sinfo);
-
if (!priv->media_connected)
return -ENOENT;
if (memcmp(mac, priv->cfg_bssid, ETH_ALEN))
@@ -672,6 +686,59 @@ static const u32 mwifiex_cipher_suites[] = {
};
/*
+ * CFG802.11 operation handler for setting bit rates.
+ *
+ * Function selects legacy bang B/G/BG from corresponding bitrates selection.
+ * Currently only 2.4GHz band is supported.
+ */
+static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *peer,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct mwifiex_ds_band_cfg band_cfg;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ int index = 0, mode = 0, i;
+
+ /* Currently only 2.4GHz is supported */
+ for (i = 0; i < mwifiex_band_2ghz.n_bitrates; i++) {
+ /*
+ * Rates below 6 Mbps in the table are CCK rates; 802.11b
+ * and from 6 they are OFDM; 802.11G
+ */
+ if (mwifiex_rates[i].bitrate == 60) {
+ index = 1 << i;
+ break;
+ }
+ }
+
+ if (mask->control[IEEE80211_BAND_2GHZ].legacy < index) {
+ mode = BAND_B;
+ } else {
+ mode = BAND_G;
+ if (mask->control[IEEE80211_BAND_2GHZ].legacy % index)
+ mode |= BAND_B;
+ }
+
+ memset(&band_cfg, 0, sizeof(band_cfg));
+ band_cfg.config_bands = mode;
+
+ if (priv->bss_mode == NL80211_IFTYPE_ADHOC)
+ band_cfg.adhoc_start_band = mode;
+
+ band_cfg.sec_chan_offset = NO_SEC_CHANNEL;
+
+ if (mwifiex_set_radio_band_cfg(priv, &band_cfg))
+ return -EFAULT;
+
+ wiphy_debug(wiphy, "info: device configured in 802.11%s%s mode\n",
+ (mode & BAND_B) ? "b" : "",
+ (mode & BAND_G) ? "g" : "");
+
+ return 0;
+}
+
+/*
* CFG802.11 operation handler for disconnection request.
*
* This function does not work when there is already a disconnection
@@ -715,6 +782,7 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
struct mwifiex_bss_info bss_info;
int ie_len;
u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)];
+ enum ieee80211_band band;
if (mwifiex_get_bss_info(priv, &bss_info))
return -1;
@@ -727,9 +795,10 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
bss_info.ssid.ssid_len);
ie_len = ie_buf[1] + sizeof(struct ieee_types_header);
+ band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
chan = __ieee80211_get_channel(priv->wdev->wiphy,
ieee80211_channel_to_frequency(bss_info.bss_chan,
- priv->curr_bss_params.band));
+ band));
cfg80211_inform_bss(priv->wdev->wiphy, chan,
bss_info.bssid, 0, WLAN_CAPABILITY_IBSS,
@@ -740,139 +809,6 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
}
/*
- * This function informs the CFG802.11 subsystem of a new BSS connection.
- *
- * The following information are sent to the CFG802.11 subsystem
- * to register the new BSS connection. If we do not register the new BSS,
- * a kernel panic will result.
- * - MAC address
- * - Capabilities
- * - Beacon period
- * - RSSI value
- * - Channel
- * - Supported rates IE
- * - Extended capabilities IE
- * - DS parameter set IE
- * - HT Capability IE
- * - Vendor Specific IE (221)
- * - WPA IE
- * - RSN IE
- */
-static int mwifiex_inform_bss_from_scan_result(struct mwifiex_private *priv,
- struct mwifiex_802_11_ssid *ssid)
-{
- struct mwifiex_bssdescriptor *scan_table;
- int i, j;
- struct ieee80211_channel *chan;
- u8 *ie, *ie_buf;
- u32 ie_len;
- u8 *beacon;
- int beacon_size;
- u8 element_id, element_len;
-
-#define MAX_IE_BUF 2048
- ie_buf = kzalloc(MAX_IE_BUF, GFP_KERNEL);
- if (!ie_buf) {
- dev_err(priv->adapter->dev, "%s: failed to alloc ie_buf\n",
- __func__);
- return -ENOMEM;
- }
-
- scan_table = priv->adapter->scan_table;
- for (i = 0; i < priv->adapter->num_in_scan_table; i++) {
- if (ssid) {
- /* Inform specific BSS only */
- if (memcmp(ssid->ssid, scan_table[i].ssid.ssid,
- ssid->ssid_len))
- continue;
- }
- memset(ie_buf, 0, MAX_IE_BUF);
- ie_buf[0] = WLAN_EID_SSID;
- ie_buf[1] = scan_table[i].ssid.ssid_len;
- memcpy(&ie_buf[sizeof(struct ieee_types_header)],
- scan_table[i].ssid.ssid, ie_buf[1]);
-
- ie = ie_buf + ie_buf[1] + sizeof(struct ieee_types_header);
- ie_len = ie_buf[1] + sizeof(struct ieee_types_header);
-
- ie[0] = WLAN_EID_SUPP_RATES;
-
- for (j = 0; j < sizeof(scan_table[i].supported_rates); j++) {
- if (!scan_table[i].supported_rates[j])
- break;
- else
- ie[j + sizeof(struct ieee_types_header)] =
- scan_table[i].supported_rates[j];
- }
-
- ie[1] = j;
- ie_len += ie[1] + sizeof(struct ieee_types_header);
-
- beacon = scan_table[i].beacon_buf;
- beacon_size = scan_table[i].beacon_buf_size;
-
- /* Skip time stamp, beacon interval and capability */
-
- if (beacon) {
- beacon += sizeof(scan_table[i].beacon_period)
- + sizeof(scan_table[i].time_stamp) +
- +sizeof(scan_table[i].cap_info_bitmap);
-
- beacon_size -= sizeof(scan_table[i].beacon_period)
- + sizeof(scan_table[i].time_stamp)
- + sizeof(scan_table[i].cap_info_bitmap);
- }
-
- while (beacon_size >= sizeof(struct ieee_types_header)) {
- ie = ie_buf + ie_len;
- element_id = *beacon;
- element_len = *(beacon + 1);
- if (beacon_size < (int) element_len +
- sizeof(struct ieee_types_header)) {
- dev_err(priv->adapter->dev, "%s: in processing"
- " IE, bytes left < IE length\n",
- __func__);
- break;
- }
- switch (element_id) {
- case WLAN_EID_EXT_CAPABILITY:
- case WLAN_EID_DS_PARAMS:
- case WLAN_EID_HT_CAPABILITY:
- case WLAN_EID_VENDOR_SPECIFIC:
- case WLAN_EID_RSN:
- case WLAN_EID_BSS_AC_ACCESS_DELAY:
- ie[0] = element_id;
- ie[1] = element_len;
- memcpy(&ie[sizeof(struct ieee_types_header)],
- (u8 *) beacon
- + sizeof(struct ieee_types_header),
- element_len);
- ie_len += ie[1] +
- sizeof(struct ieee_types_header);
- break;
- default:
- break;
- }
- beacon += element_len +
- sizeof(struct ieee_types_header);
- beacon_size -= element_len +
- sizeof(struct ieee_types_header);
- }
- chan = ieee80211_get_channel(priv->wdev->wiphy,
- scan_table[i].freq);
- cfg80211_inform_bss(priv->wdev->wiphy, chan,
- scan_table[i].mac_address,
- 0, scan_table[i].cap_info_bitmap,
- scan_table[i].beacon_period,
- ie_buf, ie_len,
- scan_table[i].rssi, GFP_KERNEL);
- }
-
- kfree(ie_buf);
- return 0;
-}
-
-/*
* This function connects with a BSS.
*
* This function handles both Infra and Ad-Hoc modes. It also performs
@@ -884,8 +820,7 @@ static int mwifiex_inform_bss_from_scan_result(struct mwifiex_private *priv,
* For Infra mode, the function returns failure if the specified SSID
* is not found in scan table. However, for Ad-Hoc mode, it can create
* the IBSS if it does not exist. On successful completion in either case,
- * the function notifies the CFG802.11 subsystem of the new BSS connection,
- * otherwise the kernel will panic.
+ * the function notifies the CFG802.11 subsystem of the new BSS connection.
*/
static int
mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
@@ -893,11 +828,11 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
struct cfg80211_connect_params *sme, bool privacy)
{
struct mwifiex_802_11_ssid req_ssid;
- struct mwifiex_ssid_bssid ssid_bssid;
int ret, auth_type = 0;
+ struct cfg80211_bss *bss = NULL;
+ u8 is_scanning_required = 0;
memset(&req_ssid, 0, sizeof(struct mwifiex_802_11_ssid));
- memset(&ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid));
req_ssid.ssid_len = ssid_len;
if (ssid_len > IEEE80211_MAX_SSID_LEN) {
@@ -960,7 +895,7 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len);
if (sme->key) {
- if (mwifiex_is_alg_wep(0) | mwifiex_is_alg_wep(0)) {
+ if (mwifiex_is_alg_wep(priv->sec_info.encryption_mode)) {
dev_dbg(priv->adapter->dev,
"info: setting wep encryption"
" with key len %d\n", sme->key_len);
@@ -975,30 +910,48 @@ done:
return -EFAULT;
}
+ /*
+ * Scan entries are valid for some time (15 sec). So we can save one
+ * active scan time if we just try cfg80211_get_bss first. If it fails
+ * then request scan and cfg80211_get_bss() again for final output.
+ */
+ while (1) {
+ if (is_scanning_required) {
+ /* Do specific SSID scanning */
+ if (mwifiex_request_scan(priv, &req_ssid)) {
+ dev_err(priv->adapter->dev, "scan error\n");
+ return -EFAULT;
+ }
+ }
- memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(struct mwifiex_802_11_ssid));
-
- if (mode != NL80211_IFTYPE_ADHOC) {
- if (mwifiex_find_best_bss(priv, &ssid_bssid))
- return -EFAULT;
- /* Inform the BSS information to kernel, otherwise
- * kernel will give a panic after successful assoc */
- if (mwifiex_inform_bss_from_scan_result(priv, &req_ssid))
- return -EFAULT;
+ /* Find the BSS we want using available scan results */
+ if (mode == NL80211_IFTYPE_ADHOC)
+ bss = cfg80211_get_bss(priv->wdev->wiphy, channel,
+ bssid, ssid, ssid_len,
+ WLAN_CAPABILITY_IBSS,
+ WLAN_CAPABILITY_IBSS);
+ else
+ bss = cfg80211_get_bss(priv->wdev->wiphy, channel,
+ bssid, ssid, ssid_len,
+ WLAN_CAPABILITY_ESS,
+ WLAN_CAPABILITY_ESS);
+
+ if (!bss) {
+ if (is_scanning_required) {
+ dev_warn(priv->adapter->dev, "assoc: requested "
+ "bss not found in scan results\n");
+ break;
+ }
+ is_scanning_required = 1;
+ } else {
+ dev_dbg(priv->adapter->dev, "info: trying to associate to %s and bssid %pM\n",
+ (char *) req_ssid.ssid, bss->bssid);
+ memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN);
+ break;
+ }
}
- dev_dbg(priv->adapter->dev, "info: trying to associate to %s and bssid %pM\n",
- (char *) req_ssid.ssid, ssid_bssid.bssid);
-
- memcpy(&priv->cfg_bssid, ssid_bssid.bssid, 6);
-
- /* Connect to BSS by ESSID */
- memset(&ssid_bssid.bssid, 0, ETH_ALEN);
-
- if (!netif_queue_stopped(priv->netdev))
- netif_stop_queue(priv->netdev);
-
- if (mwifiex_bss_start(priv, &ssid_bssid))
+ if (mwifiex_bss_start(priv, bss, &req_ssid))
return -EFAULT;
if (mode == NL80211_IFTYPE_ADHOC) {
@@ -1209,8 +1162,150 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
}
+/*
+ * create a new virtual interface with the given name
+ */
+struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy,
+ char *name,
+ enum nl80211_iftype type,
+ u32 *flags,
+ struct vif_params *params)
+{
+ struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+ struct mwifiex_adapter *adapter;
+ struct net_device *dev;
+ void *mdev_priv;
+
+ if (!priv)
+ return NULL;
+
+ adapter = priv->adapter;
+ if (!adapter)
+ return NULL;
+
+ switch (type) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ if (priv->bss_mode) {
+ wiphy_err(wiphy, "cannot create multiple"
+ " station/adhoc interfaces\n");
+ return NULL;
+ }
+
+ if (type == NL80211_IFTYPE_UNSPECIFIED)
+ priv->bss_mode = NL80211_IFTYPE_STATION;
+ else
+ priv->bss_mode = type;
+
+ priv->bss_type = MWIFIEX_BSS_TYPE_STA;
+ priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
+ priv->bss_priority = 0;
+ priv->bss_role = MWIFIEX_BSS_ROLE_STA;
+ priv->bss_index = 0;
+ priv->bss_num = 0;
+
+ break;
+ default:
+ wiphy_err(wiphy, "type not supported\n");
+ return NULL;
+ }
+
+ dev = alloc_netdev_mq(sizeof(struct mwifiex_private *), name,
+ ether_setup, 1);
+ if (!dev) {
+ wiphy_err(wiphy, "no memory available for netdevice\n");
+ goto error;
+ }
+
+ dev_net_set(dev, wiphy_net(wiphy));
+ dev->ieee80211_ptr = priv->wdev;
+ dev->ieee80211_ptr->iftype = priv->bss_mode;
+ memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN);
+ memcpy(dev->perm_addr, wiphy->perm_addr, ETH_ALEN);
+ SET_NETDEV_DEV(dev, wiphy_dev(wiphy));
+
+ dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+ dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT;
+ dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN;
+
+ mdev_priv = netdev_priv(dev);
+ *((unsigned long *) mdev_priv) = (unsigned long) priv;
+
+ priv->netdev = dev;
+ mwifiex_init_priv_params(priv, dev);
+
+ SET_NETDEV_DEV(dev, adapter->dev);
+
+ /* Register network device */
+ if (register_netdevice(dev)) {
+ wiphy_err(wiphy, "cannot register virtual network device\n");
+ goto error;
+ }
+
+ sema_init(&priv->async_sem, 1);
+ priv->scan_pending_on_block = false;
+
+ dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name);
+
+#ifdef CONFIG_DEBUG_FS
+ mwifiex_dev_debugfs_init(priv);
+#endif
+ return dev;
+error:
+ if (dev && (dev->reg_state == NETREG_UNREGISTERED))
+ free_netdev(dev);
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf);
+
+/*
+ * del_virtual_intf: remove the virtual interface determined by dev
+ */
+int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+
+ if (!priv || !dev)
+ return 0;
+
+#ifdef CONFIG_DEBUG_FS
+ mwifiex_dev_debugfs_remove(priv);
+#endif
+
+ if (!netif_queue_stopped(priv->netdev))
+ netif_stop_queue(priv->netdev);
+
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+
+ if (dev->reg_state == NETREG_REGISTERED)
+ unregister_netdevice(dev);
+
+ if (dev->reg_state == NETREG_UNREGISTERED)
+ free_netdev(dev);
+
+ /* Clear the priv in adapter */
+ priv->netdev = NULL;
+
+ priv->media_connected = false;
+
+ cancel_work_sync(&priv->cfg_workqueue);
+ flush_workqueue(priv->workqueue);
+ destroy_workqueue(priv->workqueue);
+
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
+
/* station cfg80211 operations */
static struct cfg80211_ops mwifiex_cfg80211_ops = {
+ .add_virtual_intf = mwifiex_add_virtual_intf,
+ .del_virtual_intf = mwifiex_del_virtual_intf,
.change_virtual_intf = mwifiex_cfg80211_change_virtual_intf,
.scan = mwifiex_cfg80211_scan,
.connect = mwifiex_cfg80211_connect,
@@ -1225,6 +1320,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
.set_default_key = mwifiex_cfg80211_set_default_key,
.set_power_mgmt = mwifiex_cfg80211_set_power_mgmt,
.set_tx_power = mwifiex_cfg80211_set_tx_power,
+ .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask,
};
/*
@@ -1234,8 +1330,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
* default parameters and handler function pointers, and finally
* registers the device.
*/
-int mwifiex_register_cfg80211(struct net_device *dev, u8 *mac,
- struct mwifiex_private *priv)
+int mwifiex_register_cfg80211(struct mwifiex_private *priv)
{
int ret;
void *wdev_priv;
@@ -1275,12 +1370,15 @@ int mwifiex_register_cfg80211(struct net_device *dev, u8 *mac,
wdev->wiphy->cipher_suites = mwifiex_cipher_suites;
wdev->wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites);
- memcpy(wdev->wiphy->perm_addr, mac, 6);
+ memcpy(wdev->wiphy->perm_addr, priv->curr_addr, ETH_ALEN);
wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
/* We are using custom domains */
wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
+ /* Reserve space for bss band information */
+ wdev->wiphy->bss_priv_size = sizeof(u8);
+
wdev->wiphy->reg_notifier = mwifiex_reg_notifier;
/* Set struct mwifiex_private pointer in wiphy_priv */
@@ -1302,17 +1400,8 @@ int mwifiex_register_cfg80211(struct net_device *dev, u8 *mac,
"info: successfully registered wiphy device\n");
}
- dev_net_set(dev, wiphy_net(wdev->wiphy));
- dev->ieee80211_ptr = wdev;
- memcpy(dev->dev_addr, wdev->wiphy->perm_addr, 6);
- memcpy(dev->perm_addr, wdev->wiphy->perm_addr, 6);
- SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy));
priv->wdev = wdev;
- dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
- dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT;
- dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN;
-
return ret;
}
@@ -1362,13 +1451,8 @@ mwifiex_cfg80211_results(struct work_struct *work)
MWIFIEX_SCAN_TYPE_ACTIVE;
scan_req->chan_list[i].scan_time = 0;
}
- if (mwifiex_set_user_scan_ioctl(priv, scan_req)) {
- ret = -EFAULT;
- goto done;
- }
- if (mwifiex_inform_bss_from_scan_result(priv, NULL))
+ if (mwifiex_set_user_scan_ioctl(priv, scan_req))
ret = -EFAULT;
-done:
priv->scan_result_status = ret;
dev_dbg(priv->adapter->dev, "info: %s: sending scan results\n",
__func__);
diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/mwifiex/cfg80211.h
index c4db8f36aa16..8d010f2500c5 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.h
+++ b/drivers/net/wireless/mwifiex/cfg80211.h
@@ -24,8 +24,7 @@
#include "main.h"
-int mwifiex_register_cfg80211(struct net_device *, u8 *,
- struct mwifiex_private *);
+int mwifiex_register_cfg80211(struct mwifiex_private *);
void mwifiex_cfg80211_results(struct work_struct *work);
#endif
diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c
index d0cada5a29a0..f2e6de03805c 100644
--- a/drivers/net/wireless/mwifiex/cfp.c
+++ b/drivers/net/wireless/mwifiex/cfp.c
@@ -48,7 +48,7 @@ static u8 adhoc_rates_bg[BG_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96,
static u8 adhoc_rates_a[A_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24,
0xb0, 0x48, 0x60, 0x6c, 0 };
-u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
+static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
0xb0, 0x48, 0x60, 0x6c, 0 };
static u16 mwifiex_data_rates[MWIFIEX_SUPPORTED_RATES_EXT] = { 0x02, 0x04,
0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18,
@@ -57,19 +57,19 @@ static u16 mwifiex_data_rates[MWIFIEX_SUPPORTED_RATES_EXT] = { 0x02, 0x04,
0x75, 0x82, 0x0C, 0x1B, 0x36, 0x51,
0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00 };
-u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 };
+static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 };
-u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
+static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
0x30, 0x48, 0x60, 0x6c, 0 };
-u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c,
+static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c,
0x12, 0x16, 0x18, 0x24, 0x30, 0x48,
0x60, 0x6c, 0 };
u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30,
0x32, 0x40, 0x41, 0xff };
-u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 };
+static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 };
/*
* This function maps an index in supported rates table into
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index cd89fed206ae..ac278156d390 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -40,8 +40,12 @@ mwifiex_init_cmd_node(struct mwifiex_private *priv,
{
cmd_node->priv = priv;
cmd_node->cmd_oid = cmd_oid;
- cmd_node->wait_q_enabled = priv->adapter->cmd_wait_q_required;
- priv->adapter->cmd_wait_q_required = false;
+ if (priv->adapter->cmd_wait_q_required) {
+ cmd_node->wait_q_enabled = priv->adapter->cmd_wait_q_required;
+ priv->adapter->cmd_wait_q_required = false;
+ cmd_node->cmd_wait_q_woken = false;
+ cmd_node->condition = &cmd_node->cmd_wait_q_woken;
+ }
cmd_node->data_buf = data_buf;
cmd_node->cmd_skb = cmd_node->skb;
}
@@ -90,8 +94,11 @@ mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter,
cmd_node->data_buf = NULL;
cmd_node->wait_q_enabled = false;
+ if (cmd_node->cmd_skb)
+ skb_trim(cmd_node->cmd_skb, 0);
+
if (cmd_node->resp_skb) {
- dev_kfree_skb_any(cmd_node->resp_skb);
+ adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb);
cmd_node->resp_skb = NULL;
}
}
@@ -104,13 +111,11 @@ mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter,
* main thread.
*/
static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd, void *data_buf)
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_ds_misc_cmd *pcmd_ptr)
{
- struct mwifiex_ds_misc_cmd *pcmd_ptr =
- (struct mwifiex_ds_misc_cmd *) data_buf;
-
/* Copy the HOST command to command buffer */
- memcpy((void *) cmd, pcmd_ptr->cmd, pcmd_ptr->len);
+ memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len);
dev_dbg(priv->adapter->dev, "cmd: host cmd size = %d\n", pcmd_ptr->len);
return 0;
}
@@ -175,8 +180,7 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN);
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,
- cmd_node->cmd_skb->data,
- cmd_node->cmd_skb->len, NULL);
+ cmd_node->cmd_skb, NULL);
skb_pull(cmd_node->cmd_skb, INTF_HEADER_LEN);
@@ -237,8 +241,7 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
skb_push(adapter->sleep_cfm, INTF_HEADER_LEN);
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,
- adapter->sleep_cfm->data,
- adapter->sleep_cfm->len, NULL);
+ adapter->sleep_cfm, NULL);
skb_pull(adapter->sleep_cfm, INTF_HEADER_LEN);
if (ret == -1) {
@@ -401,8 +404,7 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter)
adapter->event_cause = 0;
adapter->event_skb = NULL;
-
- dev_kfree_skb_any(skb);
+ adapter->if_ops.event_complete(adapter, skb);
return ret;
}
@@ -420,7 +422,6 @@ int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no,
struct mwifiex_adapter *adapter = priv->adapter;
adapter->cmd_wait_q_required = true;
- adapter->cmd_wait_q.condition = false;
ret = mwifiex_send_cmd_async(priv, cmd_no, cmd_action, cmd_oid,
data_buf);
@@ -513,10 +514,12 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
}
/* Send command */
- if (cmd_no == HostCmd_CMD_802_11_SCAN)
+ if (cmd_no == HostCmd_CMD_802_11_SCAN) {
mwifiex_queue_scan_cmd(priv, cmd_node);
- else
+ } else {
+ adapter->cmd_queued = cmd_node;
mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);
+ }
return ret;
}
@@ -537,7 +540,7 @@ mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,
return;
if (cmd_node->wait_q_enabled)
- mwifiex_complete_cmd(adapter);
+ mwifiex_complete_cmd(adapter, cmd_node);
/* Clean the node */
mwifiex_clean_cmd_node(adapter, cmd_node);
@@ -707,15 +710,14 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
/* Copy original response back to response buffer */
- struct mwifiex_ds_misc_cmd *hostcmd = NULL;
+ struct mwifiex_ds_misc_cmd *hostcmd;
uint16_t size = le16_to_cpu(resp->size);
dev_dbg(adapter->dev, "info: host cmd resp size = %d\n", size);
size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER);
if (adapter->curr_cmd->data_buf) {
- hostcmd = (struct mwifiex_ds_misc_cmd *)
- adapter->curr_cmd->data_buf;
+ hostcmd = adapter->curr_cmd->data_buf;
hostcmd->len = size;
- memcpy(hostcmd->cmd, (void *) resp, size);
+ memcpy(hostcmd->cmd, resp, size);
}
}
orig_cmdresp_no = le16_to_cpu(resp->command);
@@ -885,7 +887,7 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
adapter->curr_cmd->wait_q_enabled = false;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
adapter->cmd_wait_q.status = -1;
- mwifiex_complete_cmd(adapter);
+ mwifiex_complete_cmd(adapter, adapter->curr_cmd);
}
/* Cancel all pending command */
spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
@@ -896,7 +898,7 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
if (cmd_node->wait_q_enabled) {
adapter->cmd_wait_q.status = -1;
- mwifiex_complete_cmd(adapter);
+ mwifiex_complete_cmd(adapter, cmd_node);
cmd_node->wait_q_enabled = false;
}
mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
@@ -979,7 +981,7 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
}
adapter->cmd_wait_q.status = -1;
- mwifiex_complete_cmd(adapter);
+ mwifiex_complete_cmd(adapter, adapter->curr_cmd);
}
/*
@@ -1155,7 +1157,7 @@ EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp);
int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
u16 cmd_action, uint16_t ps_bitmap,
- void *data_buf)
+ struct mwifiex_ds_auto_ds *auto_ds)
{
struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh =
&cmd->params.psmode_enh;
@@ -1218,9 +1220,8 @@ int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,
sizeof(struct mwifiex_ie_types_header));
cmd_size += sizeof(*auto_ds_tlv);
tlv += sizeof(*auto_ds_tlv);
- if (data_buf)
- idletime = ((struct mwifiex_ds_auto_ds *)
- data_buf)->idle_time;
+ if (auto_ds)
+ idletime = auto_ds->idle_time;
dev_dbg(priv->adapter->dev,
"cmd: PS Command: Enter Auto Deep Sleep\n");
auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime);
@@ -1239,7 +1240,7 @@ int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,
*/
int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
- void *data_buf)
+ struct mwifiex_ds_pm_cfg *pm_cfg)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct host_cmd_ds_802_11_ps_mode_enh *ps_mode =
@@ -1282,10 +1283,8 @@ int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
dev_dbg(adapter->dev, "cmd: ps_bitmap=%#x\n", ps_bitmap);
- if (data_buf) {
+ if (pm_cfg) {
/* This section is for get power save mode */
- struct mwifiex_ds_pm_cfg *pm_cfg =
- (struct mwifiex_ds_pm_cfg *)data_buf;
if (ps_bitmap & BITMAP_STA_PS)
pm_cfg->param.ps_mode = 1;
else
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c
index 46d65e02c7ba..d26a78b6b3c4 100644
--- a/drivers/net/wireless/mwifiex/debugfs.c
+++ b/drivers/net/wireless/mwifiex/debugfs.c
@@ -27,8 +27,8 @@ static struct dentry *mwifiex_dfs_dir;
static char *bss_modes[] = {
"Unknown",
- "Managed",
"Ad-hoc",
+ "Managed",
"Auto"
};
@@ -216,28 +216,19 @@ mwifiex_info_read(struct file *file, char __user *ubuf,
p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]);
p += sprintf(p, "media_state=\"%s\"\n",
(!priv->media_connected ? "Disconnected" : "Connected"));
- p += sprintf(p, "mac_address=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n",
- netdev->dev_addr[0], netdev->dev_addr[1],
- netdev->dev_addr[2], netdev->dev_addr[3],
- netdev->dev_addr[4], netdev->dev_addr[5]);
+ p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr);
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
p += sprintf(p, "multicast_count=\"%d\"\n",
netdev_mc_count(netdev));
p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid);
- p += sprintf(p, "bssid=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n",
- info.bssid[0], info.bssid[1],
- info.bssid[2], info.bssid[3],
- info.bssid[4], info.bssid[5]);
+ p += sprintf(p, "bssid=\"%pM\"\n", info.bssid);
p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan);
p += sprintf(p, "region_code = \"%02x\"\n", info.region_code);
netdev_for_each_mc_addr(ha, netdev)
- p += sprintf(p, "multicast_address[%d]="
- "\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", i++,
- ha->addr[0], ha->addr[1],
- ha->addr[2], ha->addr[3],
- ha->addr[4], ha->addr[5]);
+ p += sprintf(p, "multicast_address[%d]=\"%pM\"\n",
+ i++, ha->addr);
}
p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes);
@@ -451,26 +442,18 @@ mwifiex_debug_read(struct file *file, char __user *ubuf,
if (info.tx_tbl_num) {
p += sprintf(p, "Tx BA stream table:\n");
for (i = 0; i < info.tx_tbl_num; i++)
- p += sprintf(p, "tid = %d, "
- "ra = %02x:%02x:%02x:%02x:%02x:%02x\n",
- info.tx_tbl[i].tid, info.tx_tbl[i].ra[0],
- info.tx_tbl[i].ra[1], info.tx_tbl[i].ra[2],
- info.tx_tbl[i].ra[3], info.tx_tbl[i].ra[4],
- info.tx_tbl[i].ra[5]);
+ p += sprintf(p, "tid = %d, ra = %pM\n",
+ info.tx_tbl[i].tid, info.tx_tbl[i].ra);
}
if (info.rx_tbl_num) {
p += sprintf(p, "Rx reorder table:\n");
for (i = 0; i < info.rx_tbl_num; i++) {
-
- p += sprintf(p, "tid = %d, "
- "ta = %02x:%02x:%02x:%02x:%02x:%02x, "
+ p += sprintf(p, "tid = %d, ta = %pM, "
"start_win = %d, "
"win_size = %d, buffer: ",
info.rx_tbl[i].tid,
- info.rx_tbl[i].ta[0], info.rx_tbl[i].ta[1],
- info.rx_tbl[i].ta[2], info.rx_tbl[i].ta[3],
- info.rx_tbl[i].ta[4], info.rx_tbl[i].ta[5],
+ info.rx_tbl[i].ta,
info.rx_tbl[i].start_win,
info.rx_tbl[i].win_size);
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index 0e90b0986ed8..ae17ce02a3d0 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -30,7 +30,9 @@
#define MWIFIEX_MAX_BSS_NUM (1)
-#define MWIFIEX_MIN_DATA_HEADER_LEN 32 /* (sizeof(mwifiex_txpd)) */
+#define MWIFIEX_MIN_DATA_HEADER_LEN 36 /* sizeof(mwifiex_txpd)
+ * + 4 byte alignment
+ */
#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2
#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16
@@ -96,7 +98,6 @@ struct mwifiex_802_11_ssid {
struct mwifiex_wait_queue {
wait_queue_head_t wait;
- u16 condition;
int status;
};
@@ -112,14 +113,6 @@ struct mwifiex_txinfo {
u8 bss_index;
};
-struct mwifiex_bss_attr {
- u8 bss_type;
- u8 frame_type;
- u8 active;
- u8 bss_priority;
- u8 bss_num;
-};
-
enum mwifiex_wmm_ac_e {
WMM_AC_BK,
WMM_AC_BE,
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index afdd145dff0b..0cc5d73cb0c1 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -57,12 +57,6 @@ struct tx_packet_hdr {
#define GET_FW_DEFAULT_BANDS(adapter) \
((adapter->fw_cap_info >> 8) & ALL_802_11_BANDS)
-extern u8 supported_rates_b[B_SUPPORTED_RATES];
-extern u8 supported_rates_g[G_SUPPORTED_RATES];
-extern u8 supported_rates_bg[BG_SUPPORTED_RATES];
-extern u8 supported_rates_a[A_SUPPORTED_RATES];
-extern u8 supported_rates_n[N_SUPPORTED_RATES];
-
#define HostCmd_WEP_KEY_INDEX_MASK 0x3fff
#define KEY_INFO_ENABLED 0x01
@@ -84,7 +78,8 @@ enum KEY_TYPE_ID {
#define MAX_FIRMWARE_POLL_TRIES 100
-#define FIRMWARE_READY 0xfedc
+#define FIRMWARE_READY_SDIO 0xfedc
+#define FIRMWARE_READY_PCIE 0xfedcba00
enum MWIFIEX_802_11_PRIVACY_FILTER {
MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL,
@@ -157,6 +152,17 @@ enum MWIFIEX_802_11_WEP_STATUS {
#define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26))
#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29))
+/* httxcfg bitmap
+ * 0 reserved
+ * 1 20/40 Mhz enable(1)/disable(0)
+ * 2-3 reserved
+ * 4 green field enable(1)/disable(0)
+ * 5 short GI in 20 Mhz enable(1)/disable(0)
+ * 6 short GI in 40 Mhz enable(1)/disable(0)
+ * 7-15 reserved
+ */
+#define MWIFIEX_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6))
+
#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f)
#define SETHT_MCS32(x) (x[4] |= 1)
@@ -210,7 +216,7 @@ enum MWIFIEX_802_11_WEP_STATUS {
#define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5
#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed
#define HostCmd_CMD_SET_BSS_MODE 0x00f7
-
+#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa
enum ENH_PS_MODES {
EN_PS = 1,
@@ -810,6 +816,14 @@ struct host_cmd_ds_txpwr_cfg {
__le32 mode;
} __packed;
+struct mwifiex_bcn_param {
+ u8 bssid[ETH_ALEN];
+ u8 rssi;
+ __le32 timestamp[2];
+ __le16 beacon_period;
+ __le16 cap_info_bitmap;
+} __packed;
+
#define MWIFIEX_USER_SCAN_CHAN_MAX 50
#define MWIFIEX_MAX_SSID_LIST_LENGTH 10
@@ -851,13 +865,6 @@ struct mwifiex_user_scan_ssid {
struct mwifiex_user_scan_cfg {
/*
- * Flag set to keep the previous scan table intact
- *
- * If set, the scan results will accumulate, replacing any previous
- * matched entries for a BSS with the new scan data
- */
- u8 keep_previous_scan;
- /*
* BSS mode to be sent in the firmware command
*/
u8 bss_mode;
@@ -1125,6 +1132,30 @@ struct host_cmd_ds_set_bss_mode {
u8 con_type;
} __packed;
+struct host_cmd_ds_pcie_details {
+ /* TX buffer descriptor ring address */
+ u32 txbd_addr_lo;
+ u32 txbd_addr_hi;
+ /* TX buffer descriptor ring count */
+ u32 txbd_count;
+
+ /* RX buffer descriptor ring address */
+ u32 rxbd_addr_lo;
+ u32 rxbd_addr_hi;
+ /* RX buffer descriptor ring count */
+ u32 rxbd_count;
+
+ /* Event buffer descriptor ring address */
+ u32 evtbd_addr_lo;
+ u32 evtbd_addr_hi;
+ /* Event buffer descriptor ring count */
+ u32 evtbd_count;
+
+ /* Sleep cookie buffer physical address */
+ u32 sleep_cookie_addr_lo;
+ u32 sleep_cookie_addr_hi;
+} __packed;
+
struct host_cmd_ds_command {
__le16 command;
__le16 size;
@@ -1172,6 +1203,7 @@ struct host_cmd_ds_command {
struct host_cmd_ds_rf_reg_access rf_reg;
struct host_cmd_ds_pmic_reg_access pmic_reg;
struct host_cmd_ds_set_bss_mode bss_mode;
+ struct host_cmd_ds_pcie_details pcie_host_spec;
struct host_cmd_ds_802_11_eeprom_access eeprom;
} params;
} __packed;
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index 3f1559e61320..d792b3fb7c16 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -76,7 +76,7 @@ static int mwifiex_init_priv(struct mwifiex_private *priv)
memset(priv->curr_addr, 0xff, ETH_ALEN);
priv->pkt_tx_ctrl = 0;
- priv->bss_mode = NL80211_IFTYPE_STATION;
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
priv->data_rate = 0; /* Initially indicate the rate as auto */
priv->is_data_rate_auto = true;
priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
@@ -152,19 +152,6 @@ static int mwifiex_init_priv(struct mwifiex_private *priv)
static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter)
{
int ret;
- u32 buf_size;
- struct mwifiex_bssdescriptor *temp_scan_table;
-
- /* Allocate buffer to store the BSSID list */
- buf_size = sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP;
- temp_scan_table = kzalloc(buf_size, GFP_KERNEL);
- if (!temp_scan_table) {
- dev_err(adapter->dev, "%s: failed to alloc temp_scan_table\n",
- __func__);
- return -ENOMEM;
- }
-
- adapter->scan_table = temp_scan_table;
/* Allocate command buffer */
ret = mwifiex_alloc_cmd_buffer(adapter);
@@ -204,7 +191,12 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
(adapter->sleep_cfm->data);
adapter->cmd_sent = false;
- adapter->data_sent = true;
+
+ if (adapter->iface_type == MWIFIEX_PCIE)
+ adapter->data_sent = false;
+ else
+ adapter->data_sent = true;
+
adapter->cmd_resp_received = false;
adapter->event_received = false;
adapter->data_received = false;
@@ -222,14 +214,8 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME;
adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME;
- adapter->num_in_scan_table = 0;
- memset(adapter->scan_table, 0,
- (sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP));
adapter->scan_probes = 1;
- memset(adapter->bcn_buf, 0, sizeof(adapter->bcn_buf));
- adapter->bcn_buf_end = adapter->bcn_buf;
-
adapter->multiple_dtim = 1;
adapter->local_listen_interval = 0; /* default value in firmware
@@ -297,6 +283,34 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
}
/*
+ * This function releases the lock variables and frees the locks and
+ * associated locks.
+ */
+static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
+{
+ struct mwifiex_private *priv;
+ s32 i, j;
+
+ /* Free lists */
+ list_del(&adapter->cmd_free_q);
+ list_del(&adapter->cmd_pending_q);
+ list_del(&adapter->scan_pending_q);
+
+ for (i = 0; i < adapter->priv_num; i++)
+ list_del(&adapter->bss_prio_tbl[i].bss_prio_head);
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+ for (j = 0; j < MAX_NUM_TID; ++j)
+ list_del(&priv->wmm.tid_tbl_ptr[j].ra_list);
+ list_del(&priv->tx_ba_stream_tbl_ptr);
+ list_del(&priv->rx_reorder_tbl_ptr);
+ }
+ }
+}
+
+/*
* This function frees the adapter structure.
*
* The freeing operation is done recursively, by canceling all
@@ -326,8 +340,6 @@ mwifiex_free_adapter(struct mwifiex_adapter *adapter)
del_timer(&adapter->cmd_timer);
dev_dbg(adapter->dev, "info: free scan table\n");
- kfree(adapter->scan_table);
- adapter->scan_table = NULL;
adapter->if_ops.cleanup_if(adapter);
@@ -392,34 +404,6 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
}
/*
- * This function releases the lock variables and frees the locks and
- * associated locks.
- */
-void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
-{
- struct mwifiex_private *priv;
- s32 i, j;
-
- /* Free lists */
- list_del(&adapter->cmd_free_q);
- list_del(&adapter->cmd_pending_q);
- list_del(&adapter->scan_pending_q);
-
- for (i = 0; i < adapter->priv_num; i++)
- list_del(&adapter->bss_prio_tbl[i].bss_prio_head);
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- priv = adapter->priv[i];
- for (j = 0; j < MAX_NUM_TID; ++j)
- list_del(&priv->wmm.tid_tbl_ptr[j].ra_list);
- list_del(&priv->tx_ba_stream_tbl_ptr);
- list_del(&priv->rx_reorder_tbl_ptr);
- }
- }
-}
-
-/*
* This function initializes the firmware.
*
* The following operations are performed sequentially -
@@ -602,11 +586,13 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
int mwifiex_dnld_fw(struct mwifiex_adapter *adapter,
struct mwifiex_fw_image *pmfw)
{
- int ret, winner;
+ int ret;
u32 poll_num = 1;
+ adapter->winner = 0;
+
/* Check if firmware is already running */
- ret = adapter->if_ops.check_fw_status(adapter, poll_num, &winner);
+ ret = adapter->if_ops.check_fw_status(adapter, poll_num);
if (!ret) {
dev_notice(adapter->dev,
"WLAN FW already running! Skip FW download\n");
@@ -615,7 +601,7 @@ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter,
poll_num = MAX_FIRMWARE_POLL_TRIES;
/* Check if we are the winner for downloading FW */
- if (!winner) {
+ if (!adapter->winner) {
dev_notice(adapter->dev,
"Other interface already running!"
" Skip FW download\n");
@@ -633,7 +619,7 @@ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter,
poll_fw:
/* Check if the firmware is downloaded successfully or not */
- ret = adapter->if_ops.check_fw_status(adapter, poll_num, NULL);
+ ret = adapter->if_ops.check_fw_status(adapter, poll_num);
if (ret) {
dev_err(adapter->dev, "FW failed to be active in time\n");
return -1;
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h
index 7c1c5ee40eb9..e0b68e7c8ca2 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/mwifiex/ioctl.h
@@ -134,7 +134,6 @@ struct mwifiex_ver_ext {
struct mwifiex_bss_info {
u32 bss_mode;
struct mwifiex_802_11_ssid ssid;
- u32 scan_table_idx;
u32 bss_chan;
u32 region_code;
u32 media_connected;
@@ -249,6 +248,7 @@ struct mwifiex_ds_hs_cfg {
};
#define DEEP_SLEEP_ON 1
+#define DEEP_SLEEP_OFF 0
#define DEEP_SLEEP_IDLE_TIME 100
#define PS_MODE_AUTO 1
@@ -306,10 +306,12 @@ struct mwifiex_ds_read_eeprom {
u8 value[MAX_EEPROM_DATA];
};
+#define IEEE_MAX_IE_SIZE 256
+
struct mwifiex_ds_misc_gen_ie {
u32 type;
u32 len;
- u8 ie_data[IW_CUSTOM_MAX];
+ u8 ie_data[IEEE_MAX_IE_SIZE];
};
struct mwifiex_ds_misc_cmd {
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 5eab3dc29b1c..62b4c2938608 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -147,13 +147,12 @@ static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1,
u8 *ptr = rate1, *tmp;
u32 i, j;
- tmp = kmalloc(rate1_size, GFP_KERNEL);
+ tmp = kmemdup(rate1, rate1_size, GFP_KERNEL);
if (!tmp) {
dev_err(priv->adapter->dev, "failed to alloc tmp buf\n");
return -ENOMEM;
}
- memcpy(tmp, rate1, rate1_size);
memset(rate1, 0, rate1_size);
for (i = 0; rate2[i] && i < rate2_size; i++) {
@@ -224,32 +223,6 @@ mwifiex_setup_rates_from_bssdesc(struct mwifiex_private *priv,
}
/*
- * This function updates the scan entry TSF timestamps to reflect
- * a new association.
- */
-static void
-mwifiex_update_tsf_timestamps(struct mwifiex_private *priv,
- struct mwifiex_bssdescriptor *new_bss_desc)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- u32 table_idx;
- long long new_tsf_base;
- signed long long tsf_delta;
-
- memcpy(&new_tsf_base, new_bss_desc->time_stamp, sizeof(new_tsf_base));
-
- tsf_delta = new_tsf_base - new_bss_desc->network_tsf;
-
- dev_dbg(adapter->dev, "info: TSF: update TSF timestamps, "
- "0x%016llx -> 0x%016llx\n",
- new_bss_desc->network_tsf, new_tsf_base);
-
- for (table_idx = 0; table_idx < adapter->num_in_scan_table;
- table_idx++)
- adapter->scan_table[table_idx].network_tsf += tsf_delta;
-}
-
-/*
* This function appends a WAPI IE.
*
* This function is called from the network join command preparation routine.
@@ -364,10 +337,9 @@ static int mwifiex_append_rsn_ie_wpa_wpa2(struct mwifiex_private *priv,
*/
int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
- void *data_buf)
+ struct mwifiex_bssdescriptor *bss_desc)
{
struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate;
- struct mwifiex_bssdescriptor *bss_desc;
struct mwifiex_ie_types_ssid_param_set *ssid_tlv;
struct mwifiex_ie_types_phy_param_set *phy_tlv;
struct mwifiex_ie_types_ss_param_set *ss_tlv;
@@ -380,7 +352,6 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
u8 *pos;
int rsn_ie_len = 0;
- bss_desc = (struct mwifiex_bssdescriptor *) data_buf;
pos = (u8 *) assoc;
mwifiex_cfg_tx_buf(priv, bss_desc);
@@ -641,12 +612,6 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
priv->curr_bss_params.band = (u8) bss_desc->bss_band;
- /*
- * Adjust the timestamps in the scan table to be relative to the newly
- * associated AP's TSF
- */
- mwifiex_update_tsf_timestamps(priv, bss_desc);
-
if (bss_desc->wmm_ie.vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC)
priv->curr_bss_params.wmm_enabled = true;
else
@@ -748,7 +713,8 @@ done:
*/
int
mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd, void *data_buf)
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_802_11_ssid *req_ssid)
{
int rsn_ie_len = 0;
struct mwifiex_adapter *adapter = priv->adapter;
@@ -786,20 +752,15 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
memset(adhoc_start->ssid, 0, IEEE80211_MAX_SSID_LEN);
- memcpy(adhoc_start->ssid,
- ((struct mwifiex_802_11_ssid *) data_buf)->ssid,
- ((struct mwifiex_802_11_ssid *) data_buf)->ssid_len);
+ memcpy(adhoc_start->ssid, req_ssid->ssid, req_ssid->ssid_len);
dev_dbg(adapter->dev, "info: ADHOC_S_CMD: SSID = %s\n",
adhoc_start->ssid);
memset(bss_desc->ssid.ssid, 0, IEEE80211_MAX_SSID_LEN);
- memcpy(bss_desc->ssid.ssid,
- ((struct mwifiex_802_11_ssid *) data_buf)->ssid,
- ((struct mwifiex_802_11_ssid *) data_buf)->ssid_len);
+ memcpy(bss_desc->ssid.ssid, req_ssid->ssid, req_ssid->ssid_len);
- bss_desc->ssid.ssid_len =
- ((struct mwifiex_802_11_ssid *) data_buf)->ssid_len;
+ bss_desc->ssid.ssid_len = req_ssid->ssid_len;
/* Set the BSS mode */
adhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS;
@@ -1036,13 +997,12 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
*/
int
mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd, void *data_buf)
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_bssdescriptor *bss_desc)
{
int rsn_ie_len = 0;
struct host_cmd_ds_802_11_ad_hoc_join *adhoc_join =
&cmd->params.adhoc_join;
- struct mwifiex_bssdescriptor *bss_desc =
- (struct mwifiex_bssdescriptor *) data_buf;
struct mwifiex_ie_types_chan_list_param_set *chan_tlv;
u32 cmd_append_size = 0;
u16 tmp_cap;
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index f0582259c935..67e6db7d672d 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -26,26 +26,6 @@
const char driver_version[] = "mwifiex " VERSION " (%s) ";
-struct mwifiex_adapter *g_adapter;
-EXPORT_SYMBOL_GPL(g_adapter);
-
-static struct mwifiex_bss_attr mwifiex_bss_sta[] = {
- {MWIFIEX_BSS_TYPE_STA, MWIFIEX_DATA_FRAME_TYPE_ETH_II, true, 0, 0},
-};
-
-static int drv_mode = DRV_MODE_STA;
-
-static char fw_name[32] = DEFAULT_FW_NAME;
-
-/* Supported drv_mode table */
-static struct mwifiex_drv_mode mwifiex_drv_mode_tbl[] = {
- {
- .drv_mode = DRV_MODE_STA,
- .intf_num = ARRAY_SIZE(mwifiex_bss_sta),
- .bss_attr = mwifiex_bss_sta,
- },
-};
-
/*
* This function registers the device and performs all the necessary
* initializations.
@@ -62,7 +42,7 @@ static struct mwifiex_drv_mode mwifiex_drv_mode_tbl[] = {
* proper cleanup before exiting.
*/
static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
- struct mwifiex_drv_mode *drv_mode_ptr)
+ void **padapter)
{
struct mwifiex_adapter *adapter;
int i;
@@ -71,7 +51,7 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
if (!adapter)
return -ENOMEM;
- g_adapter = adapter;
+ *padapter = adapter;
adapter->card = card;
/* Save interface specific operations in adapter */
@@ -82,44 +62,20 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
goto error;
adapter->priv_num = 0;
- for (i = 0; i < drv_mode_ptr->intf_num; i++) {
- adapter->priv[i] = NULL;
- if (!drv_mode_ptr->bss_attr[i].active)
- continue;
-
- /* Allocate memory for private structure */
- adapter->priv[i] = kzalloc(sizeof(struct mwifiex_private),
- GFP_KERNEL);
- if (!adapter->priv[i]) {
- dev_err(adapter->dev, "%s: failed to alloc priv[%d]\n",
- __func__, i);
- goto error;
- }
-
- adapter->priv_num++;
- adapter->priv[i]->adapter = adapter;
- /* Save bss_type, frame_type & bss_priority */
- adapter->priv[i]->bss_type = drv_mode_ptr->bss_attr[i].bss_type;
- adapter->priv[i]->frame_type =
- drv_mode_ptr->bss_attr[i].frame_type;
- adapter->priv[i]->bss_priority =
- drv_mode_ptr->bss_attr[i].bss_priority;
-
- if (drv_mode_ptr->bss_attr[i].bss_type == MWIFIEX_BSS_TYPE_STA)
- adapter->priv[i]->bss_role = MWIFIEX_BSS_ROLE_STA;
- else if (drv_mode_ptr->bss_attr[i].bss_type ==
- MWIFIEX_BSS_TYPE_UAP)
- adapter->priv[i]->bss_role = MWIFIEX_BSS_ROLE_UAP;
-
- /* Save bss_index & bss_num */
- adapter->priv[i]->bss_index = i;
- adapter->priv[i]->bss_num = drv_mode_ptr->bss_attr[i].bss_num;
+ /* Allocate memory for private structure */
+ adapter->priv[0] = kzalloc(sizeof(struct mwifiex_private),
+ GFP_KERNEL);
+ if (!adapter->priv[0]) {
+ dev_err(adapter->dev, "%s: failed to alloc priv[0]\n",
+ __func__);
+ goto error;
}
- adapter->drv_mode = drv_mode_ptr;
- if (mwifiex_init_lock_list(adapter))
- goto error;
+ adapter->priv_num++;
+
+ adapter->priv[0]->adapter = adapter;
+ mwifiex_init_lock_list(adapter);
init_timer(&adapter->cmd_timer);
adapter->cmd_timer.function = mwifiex_cmd_timeout_func;
@@ -130,9 +86,9 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
error:
dev_dbg(adapter->dev, "info: leave mwifiex_register with error\n");
- mwifiex_free_lock_list(adapter);
- for (i = 0; i < drv_mode_ptr->intf_num; i++)
+ for (i = 0; i < adapter->priv_num; i++)
kfree(adapter->priv[i]);
+
kfree(adapter);
return -1;
@@ -320,38 +276,6 @@ exit_main_proc:
}
/*
- * This function initializes the software.
- *
- * The main work includes allocating and initializing the adapter structure
- * and initializing the private structures.
- */
-static int
-mwifiex_init_sw(void *card, struct mwifiex_if_ops *if_ops)
-{
- int i;
- struct mwifiex_drv_mode *drv_mode_ptr;
-
- /* find mwifiex_drv_mode entry from mwifiex_drv_mode_tbl */
- drv_mode_ptr = NULL;
- for (i = 0; i < ARRAY_SIZE(mwifiex_drv_mode_tbl); i++) {
- if (mwifiex_drv_mode_tbl[i].drv_mode == drv_mode) {
- drv_mode_ptr = &mwifiex_drv_mode_tbl[i];
- break;
- }
- }
-
- if (!drv_mode_ptr) {
- pr_err("invalid drv_mode=%d\n", drv_mode);
- return -1;
- }
-
- if (mwifiex_register(card, if_ops, drv_mode_ptr))
- return -1;
-
- return 0;
-}
-
-/*
* This function frees the adapter structure.
*
* Additionally, this closes the netlink socket, frees the timers
@@ -384,20 +308,8 @@ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter)
memset(&fw, 0, sizeof(struct mwifiex_fw_image));
- switch (adapter->revision_id) {
- case SD8787_W0:
- case SD8787_W1:
- strcpy(fw_name, SD8787_W1_FW_NAME);
- break;
- case SD8787_A0:
- case SD8787_A1:
- strcpy(fw_name, SD8787_AX_FW_NAME);
- break;
- default:
- break;
- }
-
- err = request_firmware(&adapter->firmware, fw_name, adapter->dev);
+ err = request_firmware(&adapter->firmware, adapter->fw_name,
+ adapter->dev);
if (err < 0) {
dev_err(adapter->dev, "request_firmware() returned"
" error code %#x\n", err);
@@ -569,7 +481,7 @@ static int
mwifiex_set_mac_address(struct net_device *dev, void *addr)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- struct sockaddr *hw_addr = (struct sockaddr *) addr;
+ struct sockaddr *hw_addr = addr;
int ret;
memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN);
@@ -643,7 +555,7 @@ static const struct net_device_ops mwifiex_netdev_ops = {
.ndo_set_mac_address = mwifiex_set_mac_address,
.ndo_tx_timeout = mwifiex_tx_timeout,
.ndo_get_stats = mwifiex_get_stats,
- .ndo_set_multicast_list = mwifiex_set_multicast_list,
+ .ndo_set_rx_mode = mwifiex_set_multicast_list,
};
/*
@@ -665,8 +577,8 @@ static const struct net_device_ops mwifiex_netdev_ops = {
*
* In addition, the CFG80211 work queue is also created.
*/
-static void
-mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev)
+void mwifiex_init_priv_params(struct mwifiex_private *priv,
+ struct net_device *dev)
{
dev->netdev_ops = &mwifiex_netdev_ops;
/* Initialize private structure */
@@ -680,118 +592,6 @@ mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev)
}
/*
- * This function adds a new logical interface.
- *
- * It allocates, initializes and registers the interface by performing
- * the following opearations -
- * - Allocate a new net device structure
- * - Assign device name
- * - Register the new device with CFG80211 subsystem
- * - Initialize semaphore and private structure
- * - Register the new device with kernel
- * - Create the complete debug FS structure if configured
- */
-static struct mwifiex_private *mwifiex_add_interface(
- struct mwifiex_adapter *adapter,
- u8 bss_index, u8 bss_type)
-{
- struct net_device *dev;
- struct mwifiex_private *priv;
- void *mdev_priv;
-
- dev = alloc_netdev_mq(sizeof(struct mwifiex_private *), "mlan%d",
- ether_setup, 1);
- if (!dev) {
- dev_err(adapter->dev, "no memory available for netdevice\n");
- goto error;
- }
-
- if (mwifiex_register_cfg80211(dev, adapter->priv[bss_index]->curr_addr,
- adapter->priv[bss_index]) != 0) {
- dev_err(adapter->dev, "cannot register netdevice with cfg80211\n");
- goto error;
- }
- /* Save the priv pointer in netdev */
- priv = adapter->priv[bss_index];
- mdev_priv = netdev_priv(dev);
- *((unsigned long *) mdev_priv) = (unsigned long) priv;
-
- priv->netdev = dev;
-
- sema_init(&priv->async_sem, 1);
- priv->scan_pending_on_block = false;
-
- mwifiex_init_priv_params(priv, dev);
-
- SET_NETDEV_DEV(dev, adapter->dev);
-
- /* Register network device */
- if (register_netdev(dev)) {
- dev_err(adapter->dev, "cannot register virtual network device\n");
- goto error;
- }
-
- dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name);
-#ifdef CONFIG_DEBUG_FS
- mwifiex_dev_debugfs_init(priv);
-#endif
- return priv;
-error:
- if (dev)
- free_netdev(dev);
- return NULL;
-}
-
-/*
- * This function removes a logical interface.
- *
- * It deregisters, resets and frees the interface by performing
- * the following operations -
- * - Disconnect the device if connected, send wireless event to
- * notify applications.
- * - Remove the debug FS structure if configured
- * - Unregister the device from kernel
- * - Free the net device structure
- * - Cancel all works and destroy work queue
- * - Unregister and free the wireless device from CFG80211 subsystem
- */
-static void
-mwifiex_remove_interface(struct mwifiex_adapter *adapter, u8 bss_index)
-{
- struct net_device *dev;
- struct mwifiex_private *priv = adapter->priv[bss_index];
-
- if (!priv)
- return;
- dev = priv->netdev;
-
- if (priv->media_connected)
- priv->media_connected = false;
-
-#ifdef CONFIG_DEBUG_FS
- mwifiex_dev_debugfs_remove(priv);
-#endif
- /* Last reference is our one */
- dev_dbg(adapter->dev, "info: %s: refcnt = %d\n",
- dev->name, netdev_refcnt_read(dev));
-
- if (dev->reg_state == NETREG_REGISTERED)
- unregister_netdev(dev);
-
- /* Clear the priv in adapter */
- priv->netdev = NULL;
- if (dev)
- free_netdev(dev);
-
- cancel_work_sync(&priv->cfg_workqueue);
- flush_workqueue(priv->workqueue);
- destroy_workqueue(priv->workqueue);
- wiphy_unregister(priv->wdev->wiphy);
- wiphy_free(priv->wdev->wiphy);
- kfree(priv->wdev);
-}
-
-/*
* This function check if command is pending.
*/
int is_command_pending(struct mwifiex_adapter *adapter)
@@ -861,20 +661,21 @@ mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
*/
int
mwifiex_add_card(void *card, struct semaphore *sem,
- struct mwifiex_if_ops *if_ops)
+ struct mwifiex_if_ops *if_ops, u8 iface_type)
{
- int i;
struct mwifiex_adapter *adapter;
+ char fmt[64];
+ struct mwifiex_private *priv;
if (down_interruptible(sem))
goto exit_sem_err;
- if (mwifiex_init_sw(card, if_ops)) {
+ if (mwifiex_register(card, if_ops, (void **)&adapter)) {
pr_err("%s: software init failed\n", __func__);
goto err_init_sw;
}
- adapter = g_adapter;
+ adapter->iface_type = iface_type;
adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
adapter->surprise_removed = false;
@@ -884,8 +685,8 @@ mwifiex_add_card(void *card, struct semaphore *sem,
init_waitqueue_head(&adapter->hs_activate_wait_q);
adapter->cmd_wait_q_required = false;
init_waitqueue_head(&adapter->cmd_wait_q.wait);
- adapter->cmd_wait_q.condition = false;
adapter->cmd_wait_q.status = 0;
+ adapter->scan_wait_q_woken = false;
adapter->workqueue = create_workqueue("MWIFIEX_WORK_QUEUE");
if (!adapter->workqueue)
@@ -905,21 +706,37 @@ mwifiex_add_card(void *card, struct semaphore *sem,
goto err_init_fw;
}
- /* Add interfaces */
- for (i = 0; i < adapter->drv_mode->intf_num; i++) {
- if (!mwifiex_add_interface(adapter, i,
- adapter->drv_mode->bss_attr[i].bss_type)) {
- goto err_add_intf;
- }
+ priv = adapter->priv[0];
+
+ if (mwifiex_register_cfg80211(priv) != 0) {
+ dev_err(adapter->dev, "cannot register netdevice"
+ " with cfg80211\n");
+ goto err_init_fw;
}
+ rtnl_lock();
+ /* Create station interface by default */
+ if (!mwifiex_add_virtual_intf(priv->wdev->wiphy, "mlan%d",
+ NL80211_IFTYPE_STATION, NULL, NULL)) {
+ rtnl_unlock();
+ dev_err(adapter->dev, "cannot create default station"
+ " interface\n");
+ goto err_add_intf;
+ }
+
+ rtnl_unlock();
+
up(sem);
+ mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
+ dev_notice(adapter->dev, "driver_version = %s\n", fmt);
+
return 0;
err_add_intf:
- for (i = 0; i < adapter->priv_num; i++)
- mwifiex_remove_interface(adapter, i);
+ rtnl_lock();
+ mwifiex_del_virtual_intf(priv->wdev->wiphy, priv->netdev);
+ rtnl_unlock();
err_init_fw:
pr_debug("info: %s: unregister device\n", __func__);
adapter->if_ops.unregister_dev(adapter);
@@ -974,7 +791,7 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem)
/* Stop data */
for (i = 0; i < adapter->priv_num; i++) {
priv = adapter->priv[i];
- if (priv) {
+ if (priv && priv->netdev) {
if (!netif_queue_stopped(priv->netdev))
netif_stop_queue(priv->netdev);
if (netif_carrier_ok(priv->netdev))
@@ -999,9 +816,24 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem)
atomic_read(&adapter->cmd_pending));
}
- /* Remove interface */
- for (i = 0; i < adapter->priv_num; i++)
- mwifiex_remove_interface(adapter, i);
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+
+ if (!priv)
+ continue;
+
+ rtnl_lock();
+ mwifiex_del_virtual_intf(priv->wdev->wiphy, priv->netdev);
+ rtnl_unlock();
+ }
+
+ priv = adapter->priv[0];
+ if (!priv)
+ goto exit_remove;
+
+ wiphy_unregister(priv->wdev->wiphy);
+ wiphy_free(priv->wdev->wiphy);
+ kfree(priv->wdev);
mwifiex_terminate_workqueue(adapter);
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 8316b3cd92cd..30f138b6fa4c 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -37,32 +37,16 @@
#include "ioctl.h"
#include "util.h"
#include "fw.h"
+#include "pcie.h"
extern const char driver_version[];
-extern struct mwifiex_adapter *g_adapter;
enum {
MWIFIEX_ASYNC_CMD,
MWIFIEX_SYNC_CMD
};
-#define DRV_MODE_STA 0x1
-
-#define SD8787_W0 0x30
-#define SD8787_W1 0x31
-#define SD8787_A0 0x40
-#define SD8787_A1 0x41
-
-#define DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin"
-#define SD8787_W1_FW_NAME "mrvl/sd8787_uapsta_w1.bin"
-#define SD8787_AX_FW_NAME "mrvl/sd8787_uapsta.bin"
-
-struct mwifiex_drv_mode {
- u16 drv_mode;
- u16 intf_num;
- struct mwifiex_bss_attr *bss_attr;
-};
-
+#define MWIFIEX_MAX_AP 64
#define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ)
@@ -124,6 +108,8 @@ struct mwifiex_drv_mode {
#define MAX_FREQUENCY_BAND_BG 2484
+#define MWIFIEX_EVENT_HEADER_LEN 4
+
struct mwifiex_dbg {
u32 num_cmd_host_to_card_failure;
u32 num_cmd_sleep_cfm_host_to_card_failure;
@@ -173,6 +159,11 @@ enum MWIFIEX_PS_STATE {
PS_STATE_SLEEP
};
+enum mwifiex_iface_type {
+ MWIFIEX_SDIO,
+ MWIFIEX_PCIE,
+};
+
struct mwifiex_add_ba_param {
u32 tx_win_size;
u32 rx_win_size;
@@ -236,27 +227,10 @@ struct ieee_types_header {
u8 len;
} __packed;
-struct ieee_obss_scan_param {
- u16 obss_scan_passive_dwell;
- u16 obss_scan_active_dwell;
- u16 bss_chan_width_trigger_scan_int;
- u16 obss_scan_passive_total;
- u16 obss_scan_active_total;
- u16 bss_width_chan_trans_delay;
- u16 obss_scan_active_threshold;
-} __packed;
-
-struct ieee_types_obss_scan_param {
- struct ieee_types_header ieee_hdr;
- struct ieee_obss_scan_param obss_scan;
-} __packed;
-
#define MWIFIEX_SUPPORTED_RATES 14
#define MWIFIEX_SUPPORTED_RATES_EXT 32
-#define IEEE_MAX_IE_SIZE 256
-
struct ieee_types_vendor_specific {
struct ieee_types_vendor_header vend_hdr;
u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)];
@@ -300,8 +274,6 @@ struct mwifiex_bssdescriptor {
u16 bss_co_2040_offset;
u8 *bcn_ext_cap;
u16 ext_cap_offset;
- struct ieee_types_obss_scan_param *bcn_obss_scan;
- u16 overlap_bss_offset;
struct ieee_types_vendor_specific *bcn_wpa_ie;
u16 wpa_offset;
struct ieee_types_generic *bcn_rsn_ie;
@@ -310,8 +282,6 @@ struct mwifiex_bssdescriptor {
u16 wapi_offset;
u8 *beacon_buf;
u32 beacon_buf_size;
- u32 beacon_buf_size_max;
-
};
struct mwifiex_current_bss_params {
@@ -477,7 +447,7 @@ struct mwifiex_private {
struct dentry *dfs_dev_dir;
#endif
u8 nick_name[16];
- struct iw_statistics w_stats;
+ u8 qual_level, qual_noise;
u16 current_key_index;
struct semaphore async_sem;
u8 scan_pending_on_block;
@@ -550,36 +520,41 @@ struct cmd_ctrl_node {
void *data_buf;
u32 wait_q_enabled;
struct sk_buff *skb;
+ u8 *condition;
+ u8 cmd_wait_q_woken;
};
struct mwifiex_if_ops {
int (*init_if) (struct mwifiex_adapter *);
void (*cleanup_if) (struct mwifiex_adapter *);
- int (*check_fw_status) (struct mwifiex_adapter *, u32, int *);
+ int (*check_fw_status) (struct mwifiex_adapter *, u32);
int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
int (*register_dev) (struct mwifiex_adapter *);
void (*unregister_dev) (struct mwifiex_adapter *);
int (*enable_int) (struct mwifiex_adapter *);
int (*process_int_status) (struct mwifiex_adapter *);
- int (*host_to_card) (struct mwifiex_adapter *, u8,
- u8 *payload, u32 pkt_len,
+ int (*host_to_card) (struct mwifiex_adapter *, u8, struct sk_buff *,
struct mwifiex_tx_param *);
int (*wakeup) (struct mwifiex_adapter *);
int (*wakeup_complete) (struct mwifiex_adapter *);
+ /* Interface specific functions */
void (*update_mp_end_port) (struct mwifiex_adapter *, u16);
void (*cleanup_mpa_buf) (struct mwifiex_adapter *);
+ int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *);
+ int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *);
};
struct mwifiex_adapter {
+ u8 iface_type;
struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM];
u8 priv_num;
- struct mwifiex_drv_mode *drv_mode;
const struct firmware *firmware;
+ char fw_name[32];
+ int winner;
struct device *dev;
bool surprise_removed;
u32 fw_release_number;
- u32 revision_id;
u16 init_wait_q_woken;
wait_queue_head_t init_wait_q;
void *card;
@@ -633,15 +608,11 @@ struct mwifiex_adapter {
u32 scan_processing;
u16 region_code;
struct mwifiex_802_11d_domain_reg domain_reg;
- struct mwifiex_bssdescriptor *scan_table;
- u32 num_in_scan_table;
u16 scan_probes;
u32 scan_mode;
u16 specific_scan_time;
u16 active_scan_time;
u16 passive_scan_time;
- u8 bcn_buf[MAX_SCAN_BEACON_BUFFER];
- u8 *bcn_buf_end;
u8 fw_bands;
u8 adhoc_start_band;
u8 config_bands;
@@ -682,10 +653,11 @@ struct mwifiex_adapter {
u32 arp_filter_size;
u16 cmd_wait_q_required;
struct mwifiex_wait_queue cmd_wait_q;
+ u8 scan_wait_q_woken;
+ struct cmd_ctrl_node *cmd_queued;
};
int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
-void mwifiex_free_lock_list(struct mwifiex_adapter *adapter);
int mwifiex_init_fw(struct mwifiex_adapter *adapter);
@@ -701,7 +673,8 @@ int mwifiex_recv_packet(struct mwifiex_adapter *, struct sk_buff *skb);
int mwifiex_process_event(struct mwifiex_adapter *adapter);
-int mwifiex_complete_cmd(struct mwifiex_adapter *adapter);
+int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node);
int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
u16 cmd_action, u32 cmd_oid, void *data_buf);
@@ -735,8 +708,6 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags);
int mwifiex_write_data_complete(struct mwifiex_adapter *adapter,
struct sk_buff *skb, int status);
-int mwifiex_recv_packet_complete(struct mwifiex_adapter *,
- struct sk_buff *skb, int status);
void mwifiex_clean_txrx(struct mwifiex_private *priv);
u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv);
void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter);
@@ -745,10 +716,10 @@ void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *, u8 *,
int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
u16 cmd_action, uint16_t ps_bitmap,
- void *data_buf);
+ struct mwifiex_ds_auto_ds *auto_ds);
int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
- void *data_buf);
+ struct mwifiex_ds_pm_cfg *pm_cfg);
void mwifiex_process_hs_config(struct mwifiex_adapter *adapter);
void mwifiex_hs_activated_event(struct mwifiex_private *priv,
u8 activated);
@@ -760,38 +731,28 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *, uint16_t cmd_no,
u16 cmd_action, u32 cmd_oid,
void *data_buf, void *cmd_buf);
int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no,
- void *cmd_buf);
+ struct host_cmd_ds_command *resp);
int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *,
struct sk_buff *skb);
int mwifiex_process_sta_event(struct mwifiex_private *);
void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb);
int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta);
-int mwifiex_scan_networks(struct mwifiex_private *priv,
- const struct mwifiex_user_scan_cfg *user_scan_in);
int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
- void *data_buf);
+ struct mwifiex_scan_cmd_config *scan_cfg);
void mwifiex_queue_scan_cmd(struct mwifiex_private *priv,
struct cmd_ctrl_node *cmd_node);
int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp);
-s32 mwifiex_find_ssid_in_list(struct mwifiex_private *priv,
- struct mwifiex_802_11_ssid *ssid, u8 *bssid,
- u32 mode);
-s32 mwifiex_find_bssid_in_list(struct mwifiex_private *priv, u8 *bssid,
- u32 mode);
-int mwifiex_find_best_network(struct mwifiex_private *priv,
- struct mwifiex_ssid_bssid *req_ssid_bssid);
s32 mwifiex_ssid_cmp(struct mwifiex_802_11_ssid *ssid1,
struct mwifiex_802_11_ssid *ssid2);
int mwifiex_associate(struct mwifiex_private *priv,
struct mwifiex_bssdescriptor *bss_desc);
int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
- struct host_cmd_ds_command
- *cmd, void *data_buf);
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_bssdescriptor *bss_desc);
int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp);
void mwifiex_reset_connect_state(struct mwifiex_private *priv);
-void mwifiex_2040_coex_event(struct mwifiex_private *priv);
u8 mwifiex_band_to_radio_type(u8 band);
int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac);
int mwifiex_adhoc_start(struct mwifiex_private *priv,
@@ -800,10 +761,10 @@ int mwifiex_adhoc_join(struct mwifiex_private *priv,
struct mwifiex_bssdescriptor *bss_desc);
int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
- void *data_buf);
+ struct mwifiex_802_11_ssid *req_ssid);
int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
- void *data_buf);
+ struct mwifiex_bssdescriptor *bss_desc);
int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp);
int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd);
@@ -832,6 +793,8 @@ int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv,
int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp);
int is_command_pending(struct mwifiex_adapter *adapter);
+void mwifiex_init_priv_params(struct mwifiex_private *priv,
+ struct net_device *dev);
/*
* This function checks if the queuing is RA based or not.
@@ -921,7 +884,7 @@ struct mwifiex_private *mwifiex_bss_index_to_priv(struct mwifiex_adapter
*adapter, u8 bss_index);
int mwifiex_init_shutdown_fw(struct mwifiex_private *priv,
u32 func_init_shutdown);
-int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *);
+int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8);
int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *);
void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version,
@@ -931,19 +894,15 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
struct net_device *dev);
int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter);
-int mwifiex_bss_start(struct mwifiex_private *priv,
- struct mwifiex_ssid_bssid *ssid_bssid);
-int mwifiex_set_hs_params(struct mwifiex_private *priv,
- u16 action, int cmd_type,
- struct mwifiex_ds_hs_cfg *hscfg);
+int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
+ struct mwifiex_802_11_ssid *req_ssid);
int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type);
int mwifiex_enable_hs(struct mwifiex_adapter *adapter);
+int mwifiex_disable_auto_ds(struct mwifiex_private *priv);
int mwifiex_get_signal_info(struct mwifiex_private *priv,
struct mwifiex_ds_get_signal *signal);
int mwifiex_drv_get_data_rate(struct mwifiex_private *priv,
struct mwifiex_rate_cfg *rate);
-int mwifiex_find_best_bss(struct mwifiex_private *priv,
- struct mwifiex_ssid_bssid *ssid_bssid);
int mwifiex_request_scan(struct mwifiex_private *priv,
struct mwifiex_802_11_ssid *req_ssid);
int mwifiex_set_user_scan_ioctl(struct mwifiex_private *priv,
@@ -992,12 +951,26 @@ int mwifiex_main_process(struct mwifiex_adapter *);
int mwifiex_bss_set_channel(struct mwifiex_private *,
struct mwifiex_chan_freq_power *cfp);
-int mwifiex_bss_ioctl_find_bss(struct mwifiex_private *,
- struct mwifiex_ssid_bssid *);
int mwifiex_set_radio_band_cfg(struct mwifiex_private *,
struct mwifiex_ds_band_cfg *);
int mwifiex_get_bss_info(struct mwifiex_private *,
struct mwifiex_bss_info *);
+int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
+ u8 *bssid, s32 rssi, u8 *ie_buf,
+ size_t ie_len, u16 beacon_period,
+ u16 cap_info_bitmap, u8 band,
+ struct mwifiex_bssdescriptor *bss_desc);
+int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
+ struct mwifiex_bssdescriptor *bss_entry,
+ u8 *ie_buf, u32 ie_len);
+int mwifiex_check_network_compatibility(struct mwifiex_private *priv,
+ struct mwifiex_bssdescriptor *bss_desc);
+
+struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy,
+ char *name, enum nl80211_iftype type,
+ u32 *flags, struct vif_params *params);
+int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev);
+
#ifdef CONFIG_DEBUG_FS
void mwifiex_debugfs_init(void);
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c
new file mode 100644
index 000000000000..d34acf082d3a
--- /dev/null
+++ b/drivers/net/wireless/mwifiex/pcie.c
@@ -0,0 +1,1948 @@
+/*
+ * Marvell Wireless LAN device driver: PCIE specific handling
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include <linux/firmware.h>
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "pcie.h"
+
+#define PCIE_VERSION "1.0"
+#define DRV_NAME "Marvell mwifiex PCIe"
+
+static u8 user_rmmod;
+
+static struct mwifiex_if_ops pcie_ops;
+
+static struct semaphore add_remove_card_sem;
+static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter);
+static int mwifiex_pcie_resume(struct pci_dev *pdev);
+
+/*
+ * This function is called after skb allocation to update
+ * "skb->cb" with physical address of data pointer.
+ */
+static phys_addr_t *mwifiex_update_sk_buff_pa(struct sk_buff *skb)
+{
+ phys_addr_t *buf_pa = MWIFIEX_SKB_PACB(skb);
+
+ *buf_pa = (phys_addr_t)virt_to_phys(skb->data);
+
+ return buf_pa;
+}
+
+/*
+ * This function reads sleep cookie and checks if FW is ready
+ */
+static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
+{
+ u32 *cookie_addr;
+ struct pcie_service_card *card = adapter->card;
+
+ if (card->sleep_cookie) {
+ cookie_addr = (u32 *)card->sleep_cookie->data;
+ dev_dbg(adapter->dev, "info: ACCESS_HW: sleep cookie=0x%x\n",
+ *cookie_addr);
+ if (*cookie_addr == FW_AWAKE_COOKIE)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * This function probes an mwifiex device and registers it. It allocates
+ * the card structure, enables PCIE function number and initiates the
+ * device registration and initialization procedure by adding a logical
+ * interface.
+ */
+static int mwifiex_pcie_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct pcie_service_card *card;
+
+ pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n",
+ pdev->vendor, pdev->device, pdev->revision);
+
+ card = kzalloc(sizeof(struct pcie_service_card), GFP_KERNEL);
+ if (!card) {
+ pr_err("%s: failed to alloc memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ card->dev = pdev;
+
+ if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops,
+ MWIFIEX_PCIE)) {
+ pr_err("%s failed\n", __func__);
+ kfree(card);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * This function removes the interface and frees up the card structure.
+ */
+static void mwifiex_pcie_remove(struct pci_dev *pdev)
+{
+ struct pcie_service_card *card;
+ struct mwifiex_adapter *adapter;
+ int i;
+
+ card = pci_get_drvdata(pdev);
+ if (!card)
+ return;
+
+ adapter = card->adapter;
+ if (!adapter || !adapter->priv_num)
+ return;
+
+ if (user_rmmod) {
+#ifdef CONFIG_PM
+ if (adapter->is_suspended)
+ mwifiex_pcie_resume(pdev);
+#endif
+
+ for (i = 0; i < adapter->priv_num; i++)
+ if ((GET_BSS_ROLE(adapter->priv[i]) ==
+ MWIFIEX_BSS_ROLE_STA) &&
+ adapter->priv[i]->media_connected)
+ mwifiex_deauthenticate(adapter->priv[i], NULL);
+
+ mwifiex_disable_auto_ds(mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_ANY));
+
+ mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_ANY),
+ MWIFIEX_FUNC_SHUTDOWN);
+ }
+
+ mwifiex_remove_card(card->adapter, &add_remove_card_sem);
+ kfree(card);
+}
+
+/*
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not suspended, this function allocates and sends a host
+ * sleep activate request to the firmware and turns off the traffic.
+ */
+static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct mwifiex_adapter *adapter;
+ struct pcie_service_card *card;
+ int hs_actived, i;
+
+ if (pdev) {
+ card = (struct pcie_service_card *) pci_get_drvdata(pdev);
+ if (!card || card->adapter) {
+ pr_err("Card or adapter structure is not valid\n");
+ return 0;
+ }
+ } else {
+ pr_err("PCIE device is not specified\n");
+ return 0;
+ }
+
+ adapter = card->adapter;
+
+ hs_actived = mwifiex_enable_hs(adapter);
+
+ /* Indicate device suspended */
+ adapter->is_suspended = true;
+
+ for (i = 0; i < adapter->priv_num; i++)
+ netif_carrier_off(adapter->priv[i]->netdev);
+
+ return 0;
+}
+
+/*
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not resumed, this function turns on the traffic and
+ * sends a host sleep cancel request to the firmware.
+ */
+static int mwifiex_pcie_resume(struct pci_dev *pdev)
+{
+ struct mwifiex_adapter *adapter;
+ struct pcie_service_card *card;
+ int i;
+
+ if (pdev) {
+ card = (struct pcie_service_card *) pci_get_drvdata(pdev);
+ if (!card || !card->adapter) {
+ pr_err("Card or adapter structure is not valid\n");
+ return 0;
+ }
+ } else {
+ pr_err("PCIE device is not specified\n");
+ return 0;
+ }
+
+ adapter = card->adapter;
+
+ if (!adapter->is_suspended) {
+ dev_warn(adapter->dev, "Device already resumed\n");
+ return 0;
+ }
+
+ adapter->is_suspended = false;
+
+ for (i = 0; i < adapter->priv_num; i++)
+ if (adapter->priv[i]->media_connected)
+ netif_carrier_on(adapter->priv[i]->netdev);
+
+ mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
+ MWIFIEX_ASYNC_CMD);
+
+ return 0;
+}
+
+#define PCIE_VENDOR_ID_MARVELL (0x11ab)
+#define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30)
+
+static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = {
+ {
+ PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(pci, mwifiex_ids);
+
+/* PCI Device Driver */
+static struct pci_driver __refdata mwifiex_pcie = {
+ .name = "mwifiex_pcie",
+ .id_table = mwifiex_ids,
+ .probe = mwifiex_pcie_probe,
+ .remove = mwifiex_pcie_remove,
+#ifdef CONFIG_PM
+ /* Power Management Hooks */
+ .suspend = mwifiex_pcie_suspend,
+ .resume = mwifiex_pcie_resume,
+#endif
+};
+
+/*
+ * This function writes data into PCIE card register.
+ */
+static int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ iowrite32(data, card->pci_mmap1 + reg);
+
+ return 0;
+}
+
+/*
+ * This function reads data from PCIE card register.
+ */
+static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ *data = ioread32(card->pci_mmap1 + reg);
+
+ return 0;
+}
+
+/*
+ * This function wakes up the card.
+ *
+ * A host power up command is written to the card configuration
+ * register to wake up the card.
+ */
+static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
+{
+ int i = 0;
+
+ while (mwifiex_pcie_ok_to_access_hw(adapter)) {
+ i++;
+ udelay(10);
+ /* 50ms max wait */
+ if (i == 50000)
+ break;
+ }
+
+ dev_dbg(adapter->dev, "event: Wakeup device...\n");
+
+ /* Enable interrupts or any chip access will wakeup device */
+ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, HOST_INTR_MASK)) {
+ dev_warn(adapter->dev, "Enable host interrupt failed\n");
+ return -1;
+ }
+
+ dev_dbg(adapter->dev, "PCIE wakeup: Setting PS_STATE_AWAKE\n");
+ adapter->ps_state = PS_STATE_AWAKE;
+
+ return 0;
+}
+
+/*
+ * This function is called after the card has woken up.
+ *
+ * The card configuration register is reset.
+ */
+static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
+{
+ dev_dbg(adapter->dev, "cmd: Wakeup device completed\n");
+
+ return 0;
+}
+
+/*
+ * This function disables the host interrupt.
+ *
+ * The host interrupt mask is read, the disable bit is reset and
+ * written back to the card host interrupt mask register.
+ */
+static int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter)
+{
+ if (mwifiex_pcie_ok_to_access_hw(adapter)) {
+ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK,
+ 0x00000000)) {
+ dev_warn(adapter->dev, "Disable host interrupt failed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function enables the host interrupt.
+ *
+ * The host interrupt enable mask is written to the card
+ * host interrupt mask register.
+ */
+static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter)
+{
+ if (mwifiex_pcie_ok_to_access_hw(adapter)) {
+ /* Simply write the mask to the register */
+ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK,
+ HOST_INTR_MASK)) {
+ dev_warn(adapter->dev, "Enable host interrupt failed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function creates buffer descriptor ring for TX
+ */
+static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct sk_buff *skb;
+ int i;
+ phys_addr_t *buf_pa;
+
+ /*
+ * driver maintaines the write pointer and firmware maintaines the read
+ * pointer. The write pointer starts at 0 (zero) while the read pointer
+ * starts at zero with rollover bit set
+ */
+ card->txbd_wrptr = 0;
+ card->txbd_rdptr |= MWIFIEX_BD_FLAG_ROLLOVER_IND;
+
+ /* allocate shared memory for the BD ring and divide the same in to
+ several descriptors */
+ card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) *
+ MWIFIEX_MAX_TXRX_BD;
+ dev_dbg(adapter->dev, "info: txbd_ring: Allocating %d bytes\n",
+ card->txbd_ring_size);
+ card->txbd_ring_vbase = kzalloc(card->txbd_ring_size, GFP_KERNEL);
+ if (!card->txbd_ring_vbase) {
+ dev_err(adapter->dev, "Unable to allocate buffer for txbd ring.\n");
+ kfree(card->txbd_ring_vbase);
+ return -1;
+ }
+ card->txbd_ring_pbase = virt_to_phys(card->txbd_ring_vbase);
+
+ dev_dbg(adapter->dev, "info: txbd_ring - base: %p, pbase: %#x:%x,"
+ "len: %x\n", card->txbd_ring_vbase,
+ (u32)card->txbd_ring_pbase,
+ (u32)((u64)card->txbd_ring_pbase >> 32),
+ card->txbd_ring_size);
+
+ for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
+ card->txbd_ring[i] = (struct mwifiex_pcie_buf_desc *)
+ (card->txbd_ring_vbase +
+ (sizeof(struct mwifiex_pcie_buf_desc) * i));
+
+ /* Allocate buffer here so that firmware can DMA data from it */
+ skb = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE);
+ if (!skb) {
+ dev_err(adapter->dev, "Unable to allocate skb for TX ring.\n");
+ kfree(card->txbd_ring_vbase);
+ return -ENOMEM;
+ }
+ buf_pa = mwifiex_update_sk_buff_pa(skb);
+
+ skb_put(skb, MWIFIEX_RX_DATA_BUF_SIZE);
+ dev_dbg(adapter->dev, "info: TX ring: add new skb base: %p, "
+ "buf_base: %p, buf_pbase: %#x:%x, "
+ "buf_len: %#x\n", skb, skb->data,
+ (u32)*buf_pa, (u32)(((u64)*buf_pa >> 32)),
+ skb->len);
+
+ card->tx_buf_list[i] = skb;
+ card->txbd_ring[i]->paddr = *buf_pa;
+ card->txbd_ring[i]->len = (u16)skb->len;
+ card->txbd_ring[i]->flags = 0;
+ }
+
+ return 0;
+}
+
+static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ int i;
+
+ for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
+ if (card->tx_buf_list[i])
+ dev_kfree_skb_any(card->tx_buf_list[i]);
+ card->tx_buf_list[i] = NULL;
+ card->txbd_ring[i]->paddr = 0;
+ card->txbd_ring[i]->len = 0;
+ card->txbd_ring[i]->flags = 0;
+ card->txbd_ring[i] = NULL;
+ }
+
+ kfree(card->txbd_ring_vbase);
+ card->txbd_ring_size = 0;
+ card->txbd_wrptr = 0;
+ card->txbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND;
+ card->txbd_ring_vbase = NULL;
+
+ return 0;
+}
+
+/*
+ * This function creates buffer descriptor ring for RX
+ */
+static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct sk_buff *skb;
+ int i;
+ phys_addr_t *buf_pa;
+
+ /*
+ * driver maintaines the read pointer and firmware maintaines the write
+ * pointer. The write pointer starts at 0 (zero) while the read pointer
+ * starts at zero with rollover bit set
+ */
+ card->rxbd_wrptr = 0;
+ card->rxbd_rdptr |= MWIFIEX_BD_FLAG_ROLLOVER_IND;
+
+ card->rxbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) *
+ MWIFIEX_MAX_TXRX_BD;
+ dev_dbg(adapter->dev, "info: rxbd_ring: Allocating %d bytes\n",
+ card->rxbd_ring_size);
+ card->rxbd_ring_vbase = kzalloc(card->rxbd_ring_size, GFP_KERNEL);
+ if (!card->rxbd_ring_vbase) {
+ dev_err(adapter->dev, "Unable to allocate buffer for "
+ "rxbd_ring.\n");
+ return -1;
+ }
+ card->rxbd_ring_pbase = virt_to_phys(card->rxbd_ring_vbase);
+
+ dev_dbg(adapter->dev, "info: rxbd_ring - base: %p, pbase: %#x:%x,"
+ "len: %#x\n", card->rxbd_ring_vbase,
+ (u32)card->rxbd_ring_pbase,
+ (u32)((u64)card->rxbd_ring_pbase >> 32),
+ card->rxbd_ring_size);
+
+ for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
+ card->rxbd_ring[i] = (struct mwifiex_pcie_buf_desc *)
+ (card->rxbd_ring_vbase +
+ (sizeof(struct mwifiex_pcie_buf_desc) * i));
+
+ /* Allocate skb here so that firmware can DMA data from it */
+ skb = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE);
+ if (!skb) {
+ dev_err(adapter->dev, "Unable to allocate skb for RX ring.\n");
+ kfree(card->rxbd_ring_vbase);
+ return -ENOMEM;
+ }
+ buf_pa = mwifiex_update_sk_buff_pa(skb);
+ skb_put(skb, MWIFIEX_RX_DATA_BUF_SIZE);
+
+ dev_dbg(adapter->dev, "info: RX ring: add new skb base: %p, "
+ "buf_base: %p, buf_pbase: %#x:%x, "
+ "buf_len: %#x\n", skb, skb->data,
+ (u32)*buf_pa, (u32)((u64)*buf_pa >> 32),
+ skb->len);
+
+ card->rx_buf_list[i] = skb;
+ card->rxbd_ring[i]->paddr = *buf_pa;
+ card->rxbd_ring[i]->len = (u16)skb->len;
+ card->rxbd_ring[i]->flags = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * This function deletes Buffer descriptor ring for RX
+ */
+static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ int i;
+
+ for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
+ if (card->rx_buf_list[i])
+ dev_kfree_skb_any(card->rx_buf_list[i]);
+ card->rx_buf_list[i] = NULL;
+ card->rxbd_ring[i]->paddr = 0;
+ card->rxbd_ring[i]->len = 0;
+ card->rxbd_ring[i]->flags = 0;
+ card->rxbd_ring[i] = NULL;
+ }
+
+ kfree(card->rxbd_ring_vbase);
+ card->rxbd_ring_size = 0;
+ card->rxbd_wrptr = 0;
+ card->rxbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND;
+ card->rxbd_ring_vbase = NULL;
+
+ return 0;
+}
+
+/*
+ * This function creates buffer descriptor ring for Events
+ */
+static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct sk_buff *skb;
+ int i;
+ phys_addr_t *buf_pa;
+
+ /*
+ * driver maintaines the read pointer and firmware maintaines the write
+ * pointer. The write pointer starts at 0 (zero) while the read pointer
+ * starts at zero with rollover bit set
+ */
+ card->evtbd_wrptr = 0;
+ card->evtbd_rdptr |= MWIFIEX_BD_FLAG_ROLLOVER_IND;
+
+ card->evtbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) *
+ MWIFIEX_MAX_EVT_BD;
+ dev_dbg(adapter->dev, "info: evtbd_ring: Allocating %d bytes\n",
+ card->evtbd_ring_size);
+ card->evtbd_ring_vbase = kzalloc(card->evtbd_ring_size, GFP_KERNEL);
+ if (!card->evtbd_ring_vbase) {
+ dev_err(adapter->dev, "Unable to allocate buffer. "
+ "Terminating download\n");
+ return -1;
+ }
+ card->evtbd_ring_pbase = virt_to_phys(card->evtbd_ring_vbase);
+
+ dev_dbg(adapter->dev, "info: CMDRSP/EVT bd_ring - base: %p, "
+ "pbase: %#x:%x, len: %#x\n", card->evtbd_ring_vbase,
+ (u32)card->evtbd_ring_pbase,
+ (u32)((u64)card->evtbd_ring_pbase >> 32),
+ card->evtbd_ring_size);
+
+ for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) {
+ card->evtbd_ring[i] = (struct mwifiex_pcie_buf_desc *)
+ (card->evtbd_ring_vbase +
+ (sizeof(struct mwifiex_pcie_buf_desc) * i));
+
+ /* Allocate skb here so that firmware can DMA data from it */
+ skb = dev_alloc_skb(MAX_EVENT_SIZE);
+ if (!skb) {
+ dev_err(adapter->dev, "Unable to allocate skb for EVENT buf.\n");
+ kfree(card->evtbd_ring_vbase);
+ return -ENOMEM;
+ }
+ buf_pa = mwifiex_update_sk_buff_pa(skb);
+ skb_put(skb, MAX_EVENT_SIZE);
+
+ dev_dbg(adapter->dev, "info: Evt ring: add new skb. base: %p, "
+ "buf_base: %p, buf_pbase: %#x:%x, "
+ "buf_len: %#x\n", skb, skb->data,
+ (u32)*buf_pa, (u32)((u64)*buf_pa >> 32),
+ skb->len);
+
+ card->evt_buf_list[i] = skb;
+ card->evtbd_ring[i]->paddr = *buf_pa;
+ card->evtbd_ring[i]->len = (u16)skb->len;
+ card->evtbd_ring[i]->flags = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * This function deletes Buffer descriptor ring for Events
+ */
+static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ int i;
+
+ for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) {
+ if (card->evt_buf_list[i])
+ dev_kfree_skb_any(card->evt_buf_list[i]);
+ card->evt_buf_list[i] = NULL;
+ card->evtbd_ring[i]->paddr = 0;
+ card->evtbd_ring[i]->len = 0;
+ card->evtbd_ring[i]->flags = 0;
+ card->evtbd_ring[i] = NULL;
+ }
+
+ kfree(card->evtbd_ring_vbase);
+ card->evtbd_wrptr = 0;
+ card->evtbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND;
+ card->evtbd_ring_size = 0;
+ card->evtbd_ring_vbase = NULL;
+
+ return 0;
+}
+
+/*
+ * This function allocates a buffer for CMDRSP
+ */
+static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct sk_buff *skb;
+
+ /* Allocate memory for receiving command response data */
+ skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE);
+ if (!skb) {
+ dev_err(adapter->dev, "Unable to allocate skb for command "
+ "response data.\n");
+ return -ENOMEM;
+ }
+ mwifiex_update_sk_buff_pa(skb);
+ skb_put(skb, MWIFIEX_UPLD_SIZE);
+ card->cmdrsp_buf = skb;
+
+ skb = NULL;
+ /* Allocate memory for sending command to firmware */
+ skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER);
+ if (!skb) {
+ dev_err(adapter->dev, "Unable to allocate skb for command "
+ "data.\n");
+ return -ENOMEM;
+ }
+ mwifiex_update_sk_buff_pa(skb);
+ skb_put(skb, MWIFIEX_SIZE_OF_CMD_BUFFER);
+ card->cmd_buf = skb;
+
+ return 0;
+}
+
+/*
+ * This function deletes a buffer for CMDRSP
+ */
+static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card;
+
+ if (!adapter)
+ return 0;
+
+ card = adapter->card;
+
+ if (card && card->cmdrsp_buf)
+ dev_kfree_skb_any(card->cmdrsp_buf);
+
+ if (card && card->cmd_buf)
+ dev_kfree_skb_any(card->cmd_buf);
+
+ return 0;
+}
+
+/*
+ * This function allocates a buffer for sleep cookie
+ */
+static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter)
+{
+ struct sk_buff *skb;
+ struct pcie_service_card *card = adapter->card;
+
+ /* Allocate memory for sleep cookie */
+ skb = dev_alloc_skb(sizeof(u32));
+ if (!skb) {
+ dev_err(adapter->dev, "Unable to allocate skb for sleep "
+ "cookie!\n");
+ return -ENOMEM;
+ }
+ mwifiex_update_sk_buff_pa(skb);
+ skb_put(skb, sizeof(u32));
+
+ /* Init val of Sleep Cookie */
+ *(u32 *)skb->data = FW_AWAKE_COOKIE;
+
+ dev_dbg(adapter->dev, "alloc_scook: sleep cookie=0x%x\n",
+ *((u32 *)skb->data));
+
+ /* Save the sleep cookie */
+ card->sleep_cookie = skb;
+
+ return 0;
+}
+
+/*
+ * This function deletes buffer for sleep cookie
+ */
+static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card;
+
+ if (!adapter)
+ return 0;
+
+ card = adapter->card;
+
+ if (card && card->sleep_cookie) {
+ dev_kfree_skb_any(card->sleep_cookie);
+ card->sleep_cookie = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * This function sends data buffer to device
+ */
+static int
+mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb)
+{
+ struct pcie_service_card *card = adapter->card;
+ u32 wrindx, rdptr;
+ phys_addr_t *buf_pa;
+ __le16 *tmp;
+
+ if (!mwifiex_pcie_ok_to_access_hw(adapter))
+ mwifiex_pm_wakeup_card(adapter);
+
+ /* Read the TX ring read pointer set by firmware */
+ if (mwifiex_read_reg(adapter, REG_TXBD_RDPTR, &rdptr)) {
+ dev_err(adapter->dev, "SEND DATA: failed to read "
+ "REG_TXBD_RDPTR\n");
+ return -1;
+ }
+
+ wrindx = card->txbd_wrptr & MWIFIEX_TXBD_MASK;
+
+ dev_dbg(adapter->dev, "info: SEND DATA: <Rd: %#x, Wr: %#x>\n", rdptr,
+ card->txbd_wrptr);
+ if (((card->txbd_wrptr & MWIFIEX_TXBD_MASK) !=
+ (rdptr & MWIFIEX_TXBD_MASK)) ||
+ ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) !=
+ (rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) {
+ struct sk_buff *skb_data;
+ u8 *payload;
+
+ adapter->data_sent = true;
+ skb_data = card->tx_buf_list[wrindx];
+ memcpy(skb_data->data, skb->data, skb->len);
+ payload = skb_data->data;
+ tmp = (__le16 *)&payload[0];
+ *tmp = cpu_to_le16((u16)skb->len);
+ tmp = (__le16 *)&payload[2];
+ *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA);
+ skb_put(skb_data, MWIFIEX_RX_DATA_BUF_SIZE - skb_data->len);
+ skb_trim(skb_data, skb->len);
+ buf_pa = MWIFIEX_SKB_PACB(skb_data);
+ card->txbd_ring[wrindx]->paddr = *buf_pa;
+ card->txbd_ring[wrindx]->len = (u16)skb_data->len;
+ card->txbd_ring[wrindx]->flags = MWIFIEX_BD_FLAG_FIRST_DESC |
+ MWIFIEX_BD_FLAG_LAST_DESC;
+
+ if ((++card->txbd_wrptr & MWIFIEX_TXBD_MASK) ==
+ MWIFIEX_MAX_TXRX_BD)
+ card->txbd_wrptr = ((card->txbd_wrptr &
+ MWIFIEX_BD_FLAG_ROLLOVER_IND) ^
+ MWIFIEX_BD_FLAG_ROLLOVER_IND);
+
+ /* Write the TX ring write pointer in to REG_TXBD_WRPTR */
+ if (mwifiex_write_reg(adapter, REG_TXBD_WRPTR,
+ card->txbd_wrptr)) {
+ dev_err(adapter->dev, "SEND DATA: failed to write "
+ "REG_TXBD_WRPTR\n");
+ return 0;
+ }
+
+ /* Send the TX ready interrupt */
+ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+ CPU_INTR_DNLD_RDY)) {
+ dev_err(adapter->dev, "SEND DATA: failed to assert "
+ "door-bell interrupt.\n");
+ return -1;
+ }
+ dev_dbg(adapter->dev, "info: SEND DATA: Updated <Rd: %#x, Wr: "
+ "%#x> and sent packet to firmware "
+ "successfully\n", rdptr,
+ card->txbd_wrptr);
+ } else {
+ dev_dbg(adapter->dev, "info: TX Ring full, can't send anymore "
+ "packets to firmware\n");
+ adapter->data_sent = true;
+ /* Send the TX ready interrupt */
+ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+ CPU_INTR_DNLD_RDY))
+ dev_err(adapter->dev, "SEND DATA: failed to assert "
+ "door-bell interrupt\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/*
+ * This function handles received buffer ring and
+ * dispatches packets to upper
+ */
+static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ u32 wrptr, rd_index;
+ int ret = 0;
+ struct sk_buff *skb_tmp = NULL;
+
+ /* Read the RX ring Write pointer set by firmware */
+ if (mwifiex_read_reg(adapter, REG_RXBD_WRPTR, &wrptr)) {
+ dev_err(adapter->dev, "RECV DATA: failed to read "
+ "REG_TXBD_RDPTR\n");
+ ret = -1;
+ goto done;
+ }
+
+ while (((wrptr & MWIFIEX_RXBD_MASK) !=
+ (card->rxbd_rdptr & MWIFIEX_RXBD_MASK)) ||
+ ((wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) ==
+ (card->rxbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) {
+ struct sk_buff *skb_data;
+ u16 rx_len;
+
+ rd_index = card->rxbd_rdptr & MWIFIEX_RXBD_MASK;
+ skb_data = card->rx_buf_list[rd_index];
+
+ /* Get data length from interface header -
+ first byte is len, second byte is type */
+ rx_len = *((u16 *)skb_data->data);
+ dev_dbg(adapter->dev, "info: RECV DATA: Rd=%#x, Wr=%#x, "
+ "Len=%d\n", card->rxbd_rdptr, wrptr, rx_len);
+ skb_tmp = dev_alloc_skb(rx_len);
+ if (!skb_tmp) {
+ dev_dbg(adapter->dev, "info: Failed to alloc skb "
+ "for RX\n");
+ ret = -EBUSY;
+ goto done;
+ }
+
+ skb_put(skb_tmp, rx_len);
+
+ memcpy(skb_tmp->data, skb_data->data + INTF_HEADER_LEN, rx_len);
+ if ((++card->rxbd_rdptr & MWIFIEX_RXBD_MASK) ==
+ MWIFIEX_MAX_TXRX_BD) {
+ card->rxbd_rdptr = ((card->rxbd_rdptr &
+ MWIFIEX_BD_FLAG_ROLLOVER_IND) ^
+ MWIFIEX_BD_FLAG_ROLLOVER_IND);
+ }
+ dev_dbg(adapter->dev, "info: RECV DATA: <Rd: %#x, Wr: %#x>\n",
+ card->rxbd_rdptr, wrptr);
+
+ /* Write the RX ring read pointer in to REG_RXBD_RDPTR */
+ if (mwifiex_write_reg(adapter, REG_RXBD_RDPTR,
+ card->rxbd_rdptr)) {
+ dev_err(adapter->dev, "RECV DATA: failed to "
+ "write REG_RXBD_RDPTR\n");
+ ret = -1;
+ goto done;
+ }
+
+ /* Read the RX ring Write pointer set by firmware */
+ if (mwifiex_read_reg(adapter, REG_RXBD_WRPTR, &wrptr)) {
+ dev_err(adapter->dev, "RECV DATA: failed to read "
+ "REG_TXBD_RDPTR\n");
+ ret = -1;
+ goto done;
+ }
+ dev_dbg(adapter->dev, "info: RECV DATA: Received packet from "
+ "firmware successfully\n");
+ mwifiex_handle_rx_packet(adapter, skb_tmp);
+ }
+
+done:
+ if (ret && skb_tmp)
+ dev_kfree_skb_any(skb_tmp);
+ return ret;
+}
+
+/*
+ * This function downloads the boot command to device
+ */
+static int
+mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
+{
+ phys_addr_t *buf_pa = MWIFIEX_SKB_PACB(skb);
+
+ if (!(skb->data && skb->len && *buf_pa)) {
+ dev_err(adapter->dev, "Invalid parameter in %s <%p, %#x:%x, "
+ "%x>\n", __func__, skb->data, skb->len,
+ (u32)*buf_pa, (u32)((u64)*buf_pa >> 32));
+ return -1;
+ }
+
+ /* Write the lower 32bits of the physical address to scratch
+ * register 0 */
+ if (mwifiex_write_reg(adapter, PCIE_SCRATCH_0_REG, (u32)*buf_pa)) {
+ dev_err(adapter->dev, "%s: failed to write download command "
+ "to boot code.\n", __func__);
+ return -1;
+ }
+
+ /* Write the upper 32bits of the physical address to scratch
+ * register 1 */
+ if (mwifiex_write_reg(adapter, PCIE_SCRATCH_1_REG,
+ (u32)((u64)*buf_pa >> 32))) {
+ dev_err(adapter->dev, "%s: failed to write download command "
+ "to boot code.\n", __func__);
+ return -1;
+ }
+
+ /* Write the command length to scratch register 2 */
+ if (mwifiex_write_reg(adapter, PCIE_SCRATCH_2_REG, skb->len)) {
+ dev_err(adapter->dev, "%s: failed to write command length to "
+ "scratch register 2\n", __func__);
+ return -1;
+ }
+
+ /* Ring the door bell */
+ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+ CPU_INTR_DOOR_BELL)) {
+ dev_err(adapter->dev, "%s: failed to assert door-bell "
+ "interrupt.\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * This function downloads commands to the device
+ */
+static int
+mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
+{
+ struct pcie_service_card *card = adapter->card;
+ int ret = 0;
+ phys_addr_t *cmd_buf_pa;
+ phys_addr_t *cmdrsp_buf_pa;
+
+ if (!(skb->data && skb->len)) {
+ dev_err(adapter->dev, "Invalid parameter in %s <%p, %#x>\n",
+ __func__, skb->data, skb->len);
+ return -1;
+ }
+
+ /* Make sure a command response buffer is available */
+ if (!card->cmdrsp_buf) {
+ dev_err(adapter->dev, "No response buffer available, send "
+ "command failed\n");
+ return -EBUSY;
+ }
+
+ /* Make sure a command buffer is available */
+ if (!card->cmd_buf) {
+ dev_err(adapter->dev, "Command buffer not available\n");
+ return -EBUSY;
+ }
+
+ adapter->cmd_sent = true;
+ /* Copy the given skb in to DMA accessable shared buffer */
+ skb_put(card->cmd_buf, MWIFIEX_SIZE_OF_CMD_BUFFER - card->cmd_buf->len);
+ skb_trim(card->cmd_buf, skb->len);
+ memcpy(card->cmd_buf->data, skb->data, skb->len);
+
+ /* To send a command, the driver will:
+ 1. Write the 64bit physical address of the data buffer to
+ SCRATCH1 + SCRATCH0
+ 2. Ring the door bell (i.e. set the door bell interrupt)
+
+ In response to door bell interrupt, the firmware will perform
+ the DMA of the command packet (first header to obtain the total
+ length and then rest of the command).
+ */
+
+ if (card->cmdrsp_buf) {
+ cmdrsp_buf_pa = MWIFIEX_SKB_PACB(card->cmdrsp_buf);
+ /* Write the lower 32bits of the cmdrsp buffer physical
+ address */
+ if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_LO,
+ (u32)*cmdrsp_buf_pa)) {
+ dev_err(adapter->dev, "Failed to write download command to boot code.\n");
+ ret = -1;
+ goto done;
+ }
+ /* Write the upper 32bits of the cmdrsp buffer physical
+ address */
+ if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_HI,
+ (u32)((u64)*cmdrsp_buf_pa >> 32))) {
+ dev_err(adapter->dev, "Failed to write download command"
+ " to boot code.\n");
+ ret = -1;
+ goto done;
+ }
+ }
+
+ cmd_buf_pa = MWIFIEX_SKB_PACB(card->cmd_buf);
+ /* Write the lower 32bits of the physical address to REG_CMD_ADDR_LO */
+ if (mwifiex_write_reg(adapter, REG_CMD_ADDR_LO,
+ (u32)*cmd_buf_pa)) {
+ dev_err(adapter->dev, "Failed to write download command "
+ "to boot code.\n");
+ ret = -1;
+ goto done;
+ }
+ /* Write the upper 32bits of the physical address to REG_CMD_ADDR_HI */
+ if (mwifiex_write_reg(adapter, REG_CMD_ADDR_HI,
+ (u32)((u64)*cmd_buf_pa >> 32))) {
+ dev_err(adapter->dev, "Failed to write download command "
+ "to boot code.\n");
+ ret = -1;
+ goto done;
+ }
+
+ /* Write the command length to REG_CMD_SIZE */
+ if (mwifiex_write_reg(adapter, REG_CMD_SIZE,
+ card->cmd_buf->len)) {
+ dev_err(adapter->dev, "Failed to write command length to "
+ "REG_CMD_SIZE\n");
+ ret = -1;
+ goto done;
+ }
+
+ /* Ring the door bell */
+ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+ CPU_INTR_DOOR_BELL)) {
+ dev_err(adapter->dev, "Failed to assert door-bell "
+ "interrupt.\n");
+ ret = -1;
+ goto done;
+ }
+
+done:
+ if (ret)
+ adapter->cmd_sent = false;
+
+ return 0;
+}
+
+/*
+ * This function handles command complete interrupt
+ */
+static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ int count = 0;
+
+ dev_dbg(adapter->dev, "info: Rx CMD Response\n");
+
+ if (!adapter->curr_cmd) {
+ skb_pull(card->cmdrsp_buf, INTF_HEADER_LEN);
+ if (adapter->ps_state == PS_STATE_SLEEP_CFM) {
+ mwifiex_process_sleep_confirm_resp(adapter,
+ card->cmdrsp_buf->data,
+ card->cmdrsp_buf->len);
+ while (mwifiex_pcie_ok_to_access_hw(adapter) &&
+ (count++ < 10))
+ udelay(50);
+ } else {
+ dev_err(adapter->dev, "There is no command but "
+ "got cmdrsp\n");
+ }
+ memcpy(adapter->upld_buf, card->cmdrsp_buf->data,
+ min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER,
+ card->cmdrsp_buf->len));
+ skb_push(card->cmdrsp_buf, INTF_HEADER_LEN);
+ } else if (mwifiex_pcie_ok_to_access_hw(adapter)) {
+ skb_pull(card->cmdrsp_buf, INTF_HEADER_LEN);
+ adapter->curr_cmd->resp_skb = card->cmdrsp_buf;
+ adapter->cmd_resp_received = true;
+ /* Take the pointer and set it to CMD node and will
+ return in the response complete callback */
+ card->cmdrsp_buf = NULL;
+
+ /* Clear the cmd-rsp buffer address in scratch registers. This
+ will prevent firmware from writing to the same response
+ buffer again. */
+ if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_LO, 0)) {
+ dev_err(adapter->dev, "cmd_done: failed to clear "
+ "cmd_rsp address.\n");
+ return -1;
+ }
+ /* Write the upper 32bits of the cmdrsp buffer physical
+ address */
+ if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_HI, 0)) {
+ dev_err(adapter->dev, "cmd_done: failed to clear "
+ "cmd_rsp address.\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Command Response processing complete handler
+ */
+static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ if (skb) {
+ card->cmdrsp_buf = skb;
+ skb_push(card->cmdrsp_buf, INTF_HEADER_LEN);
+ }
+
+ return 0;
+}
+
+/*
+ * This function handles firmware event ready interrupt
+ */
+static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
+ u32 wrptr, event;
+
+ if (adapter->event_received) {
+ dev_dbg(adapter->dev, "info: Event being processed, "\
+ "do not process this interrupt just yet\n");
+ return 0;
+ }
+
+ if (rdptr >= MWIFIEX_MAX_EVT_BD) {
+ dev_dbg(adapter->dev, "info: Invalid read pointer...\n");
+ return -1;
+ }
+
+ /* Read the event ring write pointer set by firmware */
+ if (mwifiex_read_reg(adapter, REG_EVTBD_WRPTR, &wrptr)) {
+ dev_err(adapter->dev, "EventReady: failed to read REG_EVTBD_WRPTR\n");
+ return -1;
+ }
+
+ dev_dbg(adapter->dev, "info: EventReady: Initial <Rd: 0x%x, Wr: 0x%x>",
+ card->evtbd_rdptr, wrptr);
+ if (((wrptr & MWIFIEX_EVTBD_MASK) !=
+ (card->evtbd_rdptr & MWIFIEX_EVTBD_MASK)) ||
+ ((wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) ==
+ (card->evtbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) {
+ struct sk_buff *skb_cmd;
+ __le16 data_len = 0;
+ u16 evt_len;
+
+ dev_dbg(adapter->dev, "info: Read Index: %d\n", rdptr);
+ skb_cmd = card->evt_buf_list[rdptr];
+ /* Take the pointer and set it to event pointer in adapter
+ and will return back after event handling callback */
+ card->evt_buf_list[rdptr] = NULL;
+ card->evtbd_ring[rdptr]->paddr = 0;
+ card->evtbd_ring[rdptr]->len = 0;
+ card->evtbd_ring[rdptr]->flags = 0;
+
+ event = *(u32 *) &skb_cmd->data[INTF_HEADER_LEN];
+ adapter->event_cause = event;
+ /* The first 4bytes will be the event transfer header
+ len is 2 bytes followed by type which is 2 bytes */
+ memcpy(&data_len, skb_cmd->data, sizeof(__le16));
+ evt_len = le16_to_cpu(data_len);
+
+ skb_pull(skb_cmd, INTF_HEADER_LEN);
+ dev_dbg(adapter->dev, "info: Event length: %d\n", evt_len);
+
+ if ((evt_len > 0) && (evt_len < MAX_EVENT_SIZE))
+ memcpy(adapter->event_body, skb_cmd->data +
+ MWIFIEX_EVENT_HEADER_LEN, evt_len -
+ MWIFIEX_EVENT_HEADER_LEN);
+
+ adapter->event_received = true;
+ adapter->event_skb = skb_cmd;
+
+ /* Do not update the event read pointer here, wait till the
+ buffer is released. This is just to make things simpler,
+ we need to find a better method of managing these buffers.
+ */
+ }
+
+ return 0;
+}
+
+/*
+ * Event processing complete handler
+ */
+static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb)
+{
+ struct pcie_service_card *card = adapter->card;
+ int ret = 0;
+ u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
+ u32 wrptr;
+ phys_addr_t *buf_pa;
+
+ if (!skb)
+ return 0;
+
+ if (rdptr >= MWIFIEX_MAX_EVT_BD)
+ dev_err(adapter->dev, "event_complete: Invalid rdptr 0x%x\n",
+ rdptr);
+
+ /* Read the event ring write pointer set by firmware */
+ if (mwifiex_read_reg(adapter, REG_EVTBD_WRPTR, &wrptr)) {
+ dev_err(adapter->dev, "event_complete: failed to read REG_EVTBD_WRPTR\n");
+ ret = -1;
+ goto done;
+ }
+
+ if (!card->evt_buf_list[rdptr]) {
+ skb_push(skb, INTF_HEADER_LEN);
+ card->evt_buf_list[rdptr] = skb;
+ buf_pa = MWIFIEX_SKB_PACB(skb);
+ card->evtbd_ring[rdptr]->paddr = *buf_pa;
+ card->evtbd_ring[rdptr]->len = (u16)skb->len;
+ card->evtbd_ring[rdptr]->flags = 0;
+ skb = NULL;
+ } else {
+ dev_dbg(adapter->dev, "info: ERROR: Buffer is still valid at "
+ "index %d, <%p, %p>\n", rdptr,
+ card->evt_buf_list[rdptr], skb);
+ }
+
+ if ((++card->evtbd_rdptr & MWIFIEX_EVTBD_MASK) == MWIFIEX_MAX_EVT_BD) {
+ card->evtbd_rdptr = ((card->evtbd_rdptr &
+ MWIFIEX_BD_FLAG_ROLLOVER_IND) ^
+ MWIFIEX_BD_FLAG_ROLLOVER_IND);
+ }
+
+ dev_dbg(adapter->dev, "info: Updated <Rd: 0x%x, Wr: 0x%x>",
+ card->evtbd_rdptr, wrptr);
+
+ /* Write the event ring read pointer in to REG_EVTBD_RDPTR */
+ if (mwifiex_write_reg(adapter, REG_EVTBD_RDPTR, card->evtbd_rdptr)) {
+ dev_err(adapter->dev, "event_complete: failed to read REG_EVTBD_RDPTR\n");
+ ret = -1;
+ goto done;
+ }
+
+done:
+ /* Free the buffer for failure case */
+ if (ret && skb)
+ dev_kfree_skb_any(skb);
+
+ dev_dbg(adapter->dev, "info: Check Events Again\n");
+ ret = mwifiex_pcie_process_event_ready(adapter);
+
+ return ret;
+}
+
+/*
+ * This function downloads the firmware to the card.
+ *
+ * Firmware is downloaded to the card in blocks. Every block download
+ * is tested for CRC errors, and retried a number of times before
+ * returning failure.
+ */
+static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
+ struct mwifiex_fw_image *fw)
+{
+ int ret;
+ u8 *firmware = fw->fw_buf;
+ u32 firmware_len = fw->fw_len;
+ u32 offset = 0;
+ struct sk_buff *skb;
+ u32 txlen, tx_blocks = 0, tries, len;
+ u32 block_retry_cnt = 0;
+
+ if (!adapter) {
+ pr_err("adapter structure is not valid\n");
+ return -1;
+ }
+
+ if (!firmware || !firmware_len) {
+ dev_err(adapter->dev, "No firmware image found! "
+ "Terminating download\n");
+ return -1;
+ }
+
+ dev_dbg(adapter->dev, "info: Downloading FW image (%d bytes)\n",
+ firmware_len);
+
+ if (mwifiex_pcie_disable_host_int(adapter)) {
+ dev_err(adapter->dev, "%s: Disabling interrupts"
+ " failed.\n", __func__);
+ return -1;
+ }
+
+ skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ mwifiex_update_sk_buff_pa(skb);
+
+ /* Perform firmware data transfer */
+ do {
+ u32 ireg_intr = 0;
+
+ /* More data? */
+ if (offset >= firmware_len)
+ break;
+
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ ret = mwifiex_read_reg(adapter, PCIE_SCRATCH_2_REG,
+ &len);
+ if (ret) {
+ dev_warn(adapter->dev, "Failed reading length from boot code\n");
+ goto done;
+ }
+ if (len)
+ break;
+ udelay(10);
+ }
+
+ if (!len) {
+ break;
+ } else if (len > MWIFIEX_UPLD_SIZE) {
+ pr_err("FW download failure @ %d, invalid length %d\n",
+ offset, len);
+ ret = -1;
+ goto done;
+ }
+
+ txlen = len;
+
+ if (len & BIT(0)) {
+ block_retry_cnt++;
+ if (block_retry_cnt > MAX_WRITE_IOMEM_RETRY) {
+ pr_err("FW download failure @ %d, over max "
+ "retry count\n", offset);
+ ret = -1;
+ goto done;
+ }
+ dev_err(adapter->dev, "FW CRC error indicated by the "
+ "helper: len = 0x%04X, txlen = "
+ "%d\n", len, txlen);
+ len &= ~BIT(0);
+ /* Setting this to 0 to resend from same offset */
+ txlen = 0;
+ } else {
+ block_retry_cnt = 0;
+ /* Set blocksize to transfer - checking for
+ last block */
+ if (firmware_len - offset < txlen)
+ txlen = firmware_len - offset;
+
+ dev_dbg(adapter->dev, ".");
+
+ tx_blocks =
+ (txlen + MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD - 1) /
+ MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD;
+
+ /* Copy payload to buffer */
+ memmove(skb->data, &firmware[offset], txlen);
+ }
+
+ skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len);
+ skb_trim(skb, tx_blocks * MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD);
+
+ /* Send the boot command to device */
+ if (mwifiex_pcie_send_boot_cmd(adapter, skb)) {
+ dev_err(adapter->dev, "Failed to send firmware download command\n");
+ ret = -1;
+ goto done;
+ }
+ /* Wait for the command done interrupt */
+ do {
+ if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS,
+ &ireg_intr)) {
+ dev_err(adapter->dev, "%s: Failed to read "
+ "interrupt status during "
+ "fw dnld.\n", __func__);
+ ret = -1;
+ goto done;
+ }
+ } while ((ireg_intr & CPU_INTR_DOOR_BELL) ==
+ CPU_INTR_DOOR_BELL);
+ offset += txlen;
+ } while (true);
+
+ dev_dbg(adapter->dev, "info:\nFW download over, size %d bytes\n",
+ offset);
+
+ ret = 0;
+
+done:
+ dev_kfree_skb_any(skb);
+ return ret;
+}
+
+/*
+ * This function checks the firmware status in card.
+ *
+ * The winner interface is also determined by this function.
+ */
+static int
+mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num)
+{
+ int ret = 0;
+ u32 firmware_stat, winner_status;
+ u32 tries;
+
+ /* Mask spurios interrupts */
+ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS_MASK,
+ HOST_INTR_MASK)) {
+ dev_warn(adapter->dev, "Write register failed\n");
+ return -1;
+ }
+
+ dev_dbg(adapter->dev, "Setting driver ready signature\n");
+ if (mwifiex_write_reg(adapter, REG_DRV_READY, FIRMWARE_READY_PCIE)) {
+ dev_err(adapter->dev, "Failed to write driver ready signature\n");
+ return -1;
+ }
+
+ /* Wait for firmware initialization event */
+ for (tries = 0; tries < poll_num; tries++) {
+ if (mwifiex_read_reg(adapter, PCIE_SCRATCH_3_REG,
+ &firmware_stat))
+ ret = -1;
+ else
+ ret = 0;
+ if (ret)
+ continue;
+ if (firmware_stat == FIRMWARE_READY_PCIE) {
+ ret = 0;
+ break;
+ } else {
+ mdelay(100);
+ ret = -1;
+ }
+ }
+
+ if (ret) {
+ if (mwifiex_read_reg(adapter, PCIE_SCRATCH_3_REG,
+ &winner_status))
+ ret = -1;
+ else if (!winner_status) {
+ dev_err(adapter->dev, "PCI-E is the winner\n");
+ adapter->winner = 1;
+ ret = -1;
+ } else {
+ dev_err(adapter->dev, "PCI-E is not the winner <%#x, %d>, exit download\n",
+ ret, adapter->winner);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * This function reads the interrupt status from card.
+ */
+static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
+{
+ u32 pcie_ireg;
+ unsigned long flags;
+
+ if (!mwifiex_pcie_ok_to_access_hw(adapter))
+ return;
+
+ if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, &pcie_ireg)) {
+ dev_warn(adapter->dev, "Read register failed\n");
+ return;
+ }
+
+ if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) {
+
+ mwifiex_pcie_disable_host_int(adapter);
+
+ /* Clear the pending interrupts */
+ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS,
+ ~pcie_ireg)) {
+ dev_warn(adapter->dev, "Write register failed\n");
+ return;
+ }
+ spin_lock_irqsave(&adapter->int_lock, flags);
+ adapter->int_status |= pcie_ireg;
+ spin_unlock_irqrestore(&adapter->int_lock, flags);
+
+ if (pcie_ireg & HOST_INTR_CMD_DONE) {
+ if ((adapter->ps_state == PS_STATE_SLEEP_CFM) ||
+ (adapter->ps_state == PS_STATE_SLEEP)) {
+ mwifiex_pcie_enable_host_int(adapter);
+ if (mwifiex_write_reg(adapter,
+ PCIE_CPU_INT_EVENT,
+ CPU_INTR_SLEEP_CFM_DONE)) {
+ dev_warn(adapter->dev, "Write register"
+ " failed\n");
+ return;
+
+ }
+ }
+ } else if (!adapter->pps_uapsd_mode &&
+ adapter->ps_state == PS_STATE_SLEEP) {
+ /* Potentially for PCIe we could get other
+ * interrupts like shared. Don't change power
+ * state until cookie is set */
+ if (mwifiex_pcie_ok_to_access_hw(adapter))
+ adapter->ps_state = PS_STATE_AWAKE;
+ }
+ }
+}
+
+/*
+ * Interrupt handler for PCIe root port
+ *
+ * This function reads the interrupt status from firmware and assigns
+ * the main process in workqueue which will handle the interrupt.
+ */
+static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context)
+{
+ struct pci_dev *pdev = (struct pci_dev *)context;
+ struct pcie_service_card *card;
+ struct mwifiex_adapter *adapter;
+
+ if (!pdev) {
+ pr_debug("info: %s: pdev is NULL\n", (u8 *)pdev);
+ goto exit;
+ }
+
+ card = (struct pcie_service_card *) pci_get_drvdata(pdev);
+ if (!card || !card->adapter) {
+ pr_debug("info: %s: card=%p adapter=%p\n", __func__, card,
+ card ? card->adapter : NULL);
+ goto exit;
+ }
+ adapter = card->adapter;
+
+ if (adapter->surprise_removed)
+ goto exit;
+
+ mwifiex_interrupt_status(adapter);
+ queue_work(adapter->workqueue, &adapter->main_work);
+
+exit:
+ return IRQ_HANDLED;
+}
+
+/*
+ * This function checks the current interrupt status.
+ *
+ * The following interrupts are checked and handled by this function -
+ * - Data sent
+ * - Command sent
+ * - Command received
+ * - Packets received
+ * - Events received
+ *
+ * In case of Rx packets received, the packets are uploaded from card to
+ * host and processed accordingly.
+ */
+static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
+{
+ int ret;
+ u32 pcie_ireg = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->int_lock, flags);
+ /* Clear out unused interrupts */
+ adapter->int_status &= HOST_INTR_MASK;
+ spin_unlock_irqrestore(&adapter->int_lock, flags);
+
+ while (adapter->int_status & HOST_INTR_MASK) {
+ if (adapter->int_status & HOST_INTR_DNLD_DONE) {
+ adapter->int_status &= ~HOST_INTR_DNLD_DONE;
+ if (adapter->data_sent) {
+ dev_dbg(adapter->dev, "info: DATA sent Interrupt\n");
+ adapter->data_sent = false;
+ }
+ }
+ if (adapter->int_status & HOST_INTR_UPLD_RDY) {
+ adapter->int_status &= ~HOST_INTR_UPLD_RDY;
+ dev_dbg(adapter->dev, "info: Rx DATA\n");
+ ret = mwifiex_pcie_process_recv_data(adapter);
+ if (ret)
+ return ret;
+ }
+ if (adapter->int_status & HOST_INTR_EVENT_RDY) {
+ adapter->int_status &= ~HOST_INTR_EVENT_RDY;
+ dev_dbg(adapter->dev, "info: Rx EVENT\n");
+ ret = mwifiex_pcie_process_event_ready(adapter);
+ if (ret)
+ return ret;
+ }
+
+ if (adapter->int_status & HOST_INTR_CMD_DONE) {
+ adapter->int_status &= ~HOST_INTR_CMD_DONE;
+ if (adapter->cmd_sent) {
+ dev_dbg(adapter->dev, "info: CMD sent Interrupt\n");
+ adapter->cmd_sent = false;
+ }
+ /* Handle command response */
+ ret = mwifiex_pcie_process_cmd_complete(adapter);
+ if (ret)
+ return ret;
+ }
+
+ if (mwifiex_pcie_ok_to_access_hw(adapter)) {
+ if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS,
+ &pcie_ireg)) {
+ dev_warn(adapter->dev, "Read register failed\n");
+ return -1;
+ }
+
+ if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) {
+ if (mwifiex_write_reg(adapter,
+ PCIE_HOST_INT_STATUS, ~pcie_ireg)) {
+ dev_warn(adapter->dev, "Write register"
+ " failed\n");
+ return -1;
+ }
+ adapter->int_status |= pcie_ireg;
+ adapter->int_status &= HOST_INTR_MASK;
+ }
+
+ }
+ }
+ dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n",
+ adapter->cmd_sent, adapter->data_sent);
+ mwifiex_pcie_enable_host_int(adapter);
+
+ return 0;
+}
+
+/*
+ * This function downloads data from driver to card.
+ *
+ * Both commands and data packets are transferred to the card by this
+ * function.
+ *
+ * This function adds the PCIE specific header to the front of the buffer
+ * before transferring. The header contains the length of the packet and
+ * the type. The firmware handles the packets based upon this set type.
+ */
+static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type,
+ struct sk_buff *skb,
+ struct mwifiex_tx_param *tx_param)
+{
+ if (!adapter || !skb) {
+ dev_err(adapter->dev, "Invalid parameter in %s <%p, %p>\n",
+ __func__, adapter, skb);
+ return -1;
+ }
+
+ if (type == MWIFIEX_TYPE_DATA)
+ return mwifiex_pcie_send_data(adapter, skb);
+ else if (type == MWIFIEX_TYPE_CMD)
+ return mwifiex_pcie_send_cmd(adapter, skb);
+
+ return 0;
+}
+
+/*
+ * This function initializes the PCI-E host memory space, WCB rings, etc.
+ *
+ * The following initializations steps are followed -
+ * - Allocate TXBD ring buffers
+ * - Allocate RXBD ring buffers
+ * - Allocate event BD ring buffers
+ * - Allocate command response ring buffer
+ * - Allocate sleep cookie buffer
+ */
+static int mwifiex_pcie_init(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ int ret;
+ struct pci_dev *pdev = card->dev;
+
+ pci_set_drvdata(pdev, card);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto err_enable_dev;
+
+ pci_set_master(pdev);
+
+ dev_dbg(adapter->dev, "try set_consistent_dma_mask(32)\n");
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(adapter->dev, "set_dma_mask(32) failed\n");
+ goto err_set_dma_mask;
+ }
+
+ ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(adapter->dev, "set_consistent_dma_mask(64) failed\n");
+ goto err_set_dma_mask;
+ }
+
+ ret = pci_request_region(pdev, 0, DRV_NAME);
+ if (ret) {
+ dev_err(adapter->dev, "req_reg(0) error\n");
+ goto err_req_region0;
+ }
+ card->pci_mmap = pci_iomap(pdev, 0, 0);
+ if (!card->pci_mmap) {
+ dev_err(adapter->dev, "iomap(0) error\n");
+ goto err_iomap0;
+ }
+ ret = pci_request_region(pdev, 2, DRV_NAME);
+ if (ret) {
+ dev_err(adapter->dev, "req_reg(2) error\n");
+ goto err_req_region2;
+ }
+ card->pci_mmap1 = pci_iomap(pdev, 2, 0);
+ if (!card->pci_mmap1) {
+ dev_err(adapter->dev, "iomap(2) error\n");
+ goto err_iomap2;
+ }
+
+ dev_dbg(adapter->dev, "PCI memory map Virt0: %p PCI memory map Virt2: "
+ "%p\n", card->pci_mmap, card->pci_mmap1);
+
+ card->cmdrsp_buf = NULL;
+ ret = mwifiex_pcie_create_txbd_ring(adapter);
+ if (ret)
+ goto err_cre_txbd;
+ ret = mwifiex_pcie_create_rxbd_ring(adapter);
+ if (ret)
+ goto err_cre_rxbd;
+ ret = mwifiex_pcie_create_evtbd_ring(adapter);
+ if (ret)
+ goto err_cre_evtbd;
+ ret = mwifiex_pcie_alloc_cmdrsp_buf(adapter);
+ if (ret)
+ goto err_alloc_cmdbuf;
+ ret = mwifiex_pcie_alloc_sleep_cookie_buf(adapter);
+ if (ret)
+ goto err_alloc_cookie;
+
+ return ret;
+
+err_alloc_cookie:
+ mwifiex_pcie_delete_cmdrsp_buf(adapter);
+err_alloc_cmdbuf:
+ mwifiex_pcie_delete_evtbd_ring(adapter);
+err_cre_evtbd:
+ mwifiex_pcie_delete_rxbd_ring(adapter);
+err_cre_rxbd:
+ mwifiex_pcie_delete_txbd_ring(adapter);
+err_cre_txbd:
+ pci_iounmap(pdev, card->pci_mmap1);
+err_iomap2:
+ pci_release_region(pdev, 2);
+err_req_region2:
+ pci_iounmap(pdev, card->pci_mmap);
+err_iomap0:
+ pci_release_region(pdev, 0);
+err_req_region0:
+err_set_dma_mask:
+ pci_disable_device(pdev);
+err_enable_dev:
+ pci_set_drvdata(pdev, NULL);
+ return ret;
+}
+
+/*
+ * This function cleans up the allocated card buffers.
+ *
+ * The following are freed by this function -
+ * - TXBD ring buffers
+ * - RXBD ring buffers
+ * - Event BD ring buffers
+ * - Command response ring buffer
+ * - Sleep cookie buffer
+ */
+static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct pci_dev *pdev = card->dev;
+
+ mwifiex_pcie_delete_sleep_cookie_buf(adapter);
+ mwifiex_pcie_delete_cmdrsp_buf(adapter);
+ mwifiex_pcie_delete_evtbd_ring(adapter);
+ mwifiex_pcie_delete_rxbd_ring(adapter);
+ mwifiex_pcie_delete_txbd_ring(adapter);
+ card->cmdrsp_buf = NULL;
+
+ dev_dbg(adapter->dev, "Clearing driver ready signature\n");
+ if (user_rmmod) {
+ if (mwifiex_write_reg(adapter, REG_DRV_READY, 0x00000000))
+ dev_err(adapter->dev, "Failed to write driver not-ready signature\n");
+ }
+
+ if (pdev) {
+ pci_iounmap(pdev, card->pci_mmap);
+ pci_iounmap(pdev, card->pci_mmap1);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ }
+}
+
+/*
+ * This function registers the PCIE device.
+ *
+ * PCIE IRQ is claimed, block size is set and driver data is initialized.
+ */
+static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
+{
+ int ret;
+ struct pcie_service_card *card = adapter->card;
+ struct pci_dev *pdev = card->dev;
+
+ /* save adapter pointer in card */
+ card->adapter = adapter;
+
+ ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED,
+ "MRVL_PCIE", pdev);
+ if (ret) {
+ pr_err("request_irq failed: ret=%d\n", ret);
+ adapter->card = NULL;
+ return -1;
+ }
+
+ adapter->dev = &pdev->dev;
+ strcpy(adapter->fw_name, PCIE8766_DEFAULT_FW_NAME);
+
+ return 0;
+}
+
+/*
+ * This function unregisters the PCIE device.
+ *
+ * The PCIE IRQ is released, the function is disabled and driver
+ * data is set to null.
+ */
+static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ if (card) {
+ dev_dbg(adapter->dev, "%s(): calling free_irq()\n", __func__);
+ free_irq(card->dev->irq, card->dev);
+ }
+}
+
+static struct mwifiex_if_ops pcie_ops = {
+ .init_if = mwifiex_pcie_init,
+ .cleanup_if = mwifiex_pcie_cleanup,
+ .check_fw_status = mwifiex_check_fw_status,
+ .prog_fw = mwifiex_prog_fw_w_helper,
+ .register_dev = mwifiex_register_dev,
+ .unregister_dev = mwifiex_unregister_dev,
+ .enable_int = mwifiex_pcie_enable_host_int,
+ .process_int_status = mwifiex_process_int_status,
+ .host_to_card = mwifiex_pcie_host_to_card,
+ .wakeup = mwifiex_pm_wakeup_card,
+ .wakeup_complete = mwifiex_pm_wakeup_card_complete,
+
+ /* PCIE specific */
+ .cmdrsp_complete = mwifiex_pcie_cmdrsp_complete,
+ .event_complete = mwifiex_pcie_event_complete,
+ .update_mp_end_port = NULL,
+ .cleanup_mpa_buf = NULL,
+};
+
+/*
+ * This function initializes the PCIE driver module.
+ *
+ * This initiates the semaphore and registers the device with
+ * PCIE bus.
+ */
+static int mwifiex_pcie_init_module(void)
+{
+ int ret;
+
+ pr_debug("Marvell 8766 PCIe Driver\n");
+
+ sema_init(&add_remove_card_sem, 1);
+
+ /* Clear the flag in case user removes the card. */
+ user_rmmod = 0;
+
+ ret = pci_register_driver(&mwifiex_pcie);
+ if (ret)
+ pr_err("Driver register failed!\n");
+ else
+ pr_debug("info: Driver registered successfully!\n");
+
+ return ret;
+}
+
+/*
+ * This function cleans up the PCIE driver.
+ *
+ * The following major steps are followed for cleanup -
+ * - Resume the device if its suspended
+ * - Disconnect the device if connected
+ * - Shutdown the firmware
+ * - Unregister the device from PCIE bus.
+ */
+static void mwifiex_pcie_cleanup_module(void)
+{
+ if (!down_interruptible(&add_remove_card_sem))
+ up(&add_remove_card_sem);
+
+ /* Set the flag as user is removing this module. */
+ user_rmmod = 1;
+
+ pci_unregister_driver(&mwifiex_pcie);
+}
+
+module_init(mwifiex_pcie_init_module);
+module_exit(mwifiex_pcie_cleanup_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell WiFi-Ex PCI-Express Driver version " PCIE_VERSION);
+MODULE_VERSION(PCIE_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE("mrvl/pcie8766_uapsta.bin");
diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h
new file mode 100644
index 000000000000..445ff21772e2
--- /dev/null
+++ b/drivers/net/wireless/mwifiex/pcie.h
@@ -0,0 +1,148 @@
+/* @file mwifiex_pcie.h
+ *
+ * @brief This file contains definitions for PCI-E interface.
+ * driver.
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#ifndef _MWIFIEX_PCIE_H
+#define _MWIFIEX_PCIE_H
+
+#include <linux/pci.h>
+#include <linux/pcieport_if.h>
+#include <linux/interrupt.h>
+
+#include "main.h"
+
+#define PCIE8766_DEFAULT_FW_NAME "mrvl/pcie8766_uapsta.bin"
+
+/* Constants for Buffer Descriptor (BD) rings */
+#define MWIFIEX_MAX_TXRX_BD 0x20
+#define MWIFIEX_TXBD_MASK 0x3F
+#define MWIFIEX_RXBD_MASK 0x3F
+
+#define MWIFIEX_MAX_EVT_BD 0x04
+#define MWIFIEX_EVTBD_MASK 0x07
+
+/* PCIE INTERNAL REGISTERS */
+#define PCIE_SCRATCH_0_REG 0xC10
+#define PCIE_SCRATCH_1_REG 0xC14
+#define PCIE_CPU_INT_EVENT 0xC18
+#define PCIE_CPU_INT_STATUS 0xC1C
+#define PCIE_HOST_INT_STATUS 0xC30
+#define PCIE_HOST_INT_MASK 0xC34
+#define PCIE_HOST_INT_STATUS_MASK 0xC3C
+#define PCIE_SCRATCH_2_REG 0xC40
+#define PCIE_SCRATCH_3_REG 0xC44
+#define PCIE_SCRATCH_4_REG 0xCC0
+#define PCIE_SCRATCH_5_REG 0xCC4
+#define PCIE_SCRATCH_6_REG 0xCC8
+#define PCIE_SCRATCH_7_REG 0xCCC
+#define PCIE_SCRATCH_8_REG 0xCD0
+#define PCIE_SCRATCH_9_REG 0xCD4
+#define PCIE_SCRATCH_10_REG 0xCD8
+#define PCIE_SCRATCH_11_REG 0xCDC
+#define PCIE_SCRATCH_12_REG 0xCE0
+
+#define CPU_INTR_DNLD_RDY BIT(0)
+#define CPU_INTR_DOOR_BELL BIT(1)
+#define CPU_INTR_SLEEP_CFM_DONE BIT(2)
+#define CPU_INTR_RESET BIT(3)
+
+#define HOST_INTR_DNLD_DONE BIT(0)
+#define HOST_INTR_UPLD_RDY BIT(1)
+#define HOST_INTR_CMD_DONE BIT(2)
+#define HOST_INTR_EVENT_RDY BIT(3)
+#define HOST_INTR_MASK (HOST_INTR_DNLD_DONE | \
+ HOST_INTR_UPLD_RDY | \
+ HOST_INTR_CMD_DONE | \
+ HOST_INTR_EVENT_RDY)
+
+#define MWIFIEX_BD_FLAG_ROLLOVER_IND BIT(7)
+#define MWIFIEX_BD_FLAG_FIRST_DESC BIT(0)
+#define MWIFIEX_BD_FLAG_LAST_DESC BIT(1)
+#define REG_CMD_ADDR_LO PCIE_SCRATCH_0_REG
+#define REG_CMD_ADDR_HI PCIE_SCRATCH_1_REG
+#define REG_CMD_SIZE PCIE_SCRATCH_2_REG
+
+#define REG_CMDRSP_ADDR_LO PCIE_SCRATCH_4_REG
+#define REG_CMDRSP_ADDR_HI PCIE_SCRATCH_5_REG
+
+/* TX buffer description read pointer */
+#define REG_TXBD_RDPTR PCIE_SCRATCH_6_REG
+/* TX buffer description write pointer */
+#define REG_TXBD_WRPTR PCIE_SCRATCH_7_REG
+/* RX buffer description read pointer */
+#define REG_RXBD_RDPTR PCIE_SCRATCH_8_REG
+/* RX buffer description write pointer */
+#define REG_RXBD_WRPTR PCIE_SCRATCH_9_REG
+/* Event buffer description read pointer */
+#define REG_EVTBD_RDPTR PCIE_SCRATCH_10_REG
+/* Event buffer description write pointer */
+#define REG_EVTBD_WRPTR PCIE_SCRATCH_11_REG
+/* Driver ready signature write pointer */
+#define REG_DRV_READY PCIE_SCRATCH_12_REG
+
+/* Max retry number of command write */
+#define MAX_WRITE_IOMEM_RETRY 2
+/* Define PCIE block size for firmware download */
+#define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256
+/* FW awake cookie after FW ready */
+#define FW_AWAKE_COOKIE (0xAA55AA55)
+
+struct mwifiex_pcie_buf_desc {
+ u64 paddr;
+ u16 len;
+ u16 flags;
+} __packed;
+
+struct pcie_service_card {
+ struct pci_dev *dev;
+ struct mwifiex_adapter *adapter;
+
+ u32 txbd_wrptr;
+ u32 txbd_rdptr;
+ u32 txbd_ring_size;
+ u8 *txbd_ring_vbase;
+ phys_addr_t txbd_ring_pbase;
+ struct mwifiex_pcie_buf_desc *txbd_ring[MWIFIEX_MAX_TXRX_BD];
+ struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD];
+
+ u32 rxbd_wrptr;
+ u32 rxbd_rdptr;
+ u32 rxbd_ring_size;
+ u8 *rxbd_ring_vbase;
+ phys_addr_t rxbd_ring_pbase;
+ struct mwifiex_pcie_buf_desc *rxbd_ring[MWIFIEX_MAX_TXRX_BD];
+ struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD];
+
+ u32 evtbd_wrptr;
+ u32 evtbd_rdptr;
+ u32 evtbd_ring_size;
+ u8 *evtbd_ring_vbase;
+ phys_addr_t evtbd_ring_pbase;
+ struct mwifiex_pcie_buf_desc *evtbd_ring[MWIFIEX_MAX_EVT_BD];
+ struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD];
+
+ struct sk_buff *cmd_buf;
+ struct sk_buff *cmdrsp_buf;
+ struct sk_buff *sleep_cookie;
+ void __iomem *pci_mmap;
+ void __iomem *pci_mmap1;
+};
+
+#endif /* _MWIFIEX_PCIE_H */
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index 5c22860fb40a..8d3ab378662b 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -172,59 +172,6 @@ mwifiex_ssid_cmp(struct mwifiex_802_11_ssid *ssid1,
}
/*
- * Sends IOCTL request to get the best BSS.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int mwifiex_find_best_bss(struct mwifiex_private *priv,
- struct mwifiex_ssid_bssid *ssid_bssid)
-{
- struct mwifiex_ssid_bssid tmp_ssid_bssid;
- u8 *mac;
-
- if (!ssid_bssid)
- return -1;
-
- memcpy(&tmp_ssid_bssid, ssid_bssid,
- sizeof(struct mwifiex_ssid_bssid));
-
- if (!mwifiex_bss_ioctl_find_bss(priv, &tmp_ssid_bssid)) {
- memcpy(ssid_bssid, &tmp_ssid_bssid,
- sizeof(struct mwifiex_ssid_bssid));
- mac = (u8 *) &ssid_bssid->bssid;
- dev_dbg(priv->adapter->dev, "cmd: found network: ssid=%s,"
- " %pM\n", ssid_bssid->ssid.ssid, mac);
- return 0;
- }
-
- return -1;
-}
-
-/*
- * Sends IOCTL request to start a scan with user configurations.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- *
- * Upon completion, it also generates a wireless event to notify
- * applications.
- */
-int mwifiex_set_user_scan_ioctl(struct mwifiex_private *priv,
- struct mwifiex_user_scan_cfg *scan_req)
-{
- int status;
-
- priv->adapter->cmd_wait_q.condition = false;
-
- status = mwifiex_scan_networks(priv, scan_req);
- if (!status)
- status = mwifiex_wait_queue_complete(priv->adapter);
-
- return status;
-}
-
-/*
* This function checks if wapi is enabled in driver and scanned network is
* compatible with it.
*/
@@ -286,8 +233,7 @@ mwifiex_is_network_compatible_for_static_wep(struct mwifiex_private *priv,
*/
static bool
mwifiex_is_network_compatible_for_wpa(struct mwifiex_private *priv,
- struct mwifiex_bssdescriptor *bss_desc,
- int index)
+ struct mwifiex_bssdescriptor *bss_desc)
{
if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED
&& priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled
@@ -298,9 +244,9 @@ mwifiex_is_network_compatible_for_wpa(struct mwifiex_private *priv,
* LinkSys WRT54G && bss_desc->privacy
*/
) {
- dev_dbg(priv->adapter->dev, "info: %s: WPA: index=%d"
+ dev_dbg(priv->adapter->dev, "info: %s: WPA:"
" wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s "
- "EncMode=%#x privacy=%#x\n", __func__, index,
+ "EncMode=%#x privacy=%#x\n", __func__,
(bss_desc->bcn_wpa_ie) ?
(*(bss_desc->bcn_wpa_ie)).
vend_hdr.element_id : 0,
@@ -324,8 +270,7 @@ mwifiex_is_network_compatible_for_wpa(struct mwifiex_private *priv,
*/
static bool
mwifiex_is_network_compatible_for_wpa2(struct mwifiex_private *priv,
- struct mwifiex_bssdescriptor *bss_desc,
- int index)
+ struct mwifiex_bssdescriptor *bss_desc)
{
if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED
&& !priv->sec_info.wpa_enabled && priv->sec_info.wpa2_enabled
@@ -336,9 +281,9 @@ mwifiex_is_network_compatible_for_wpa2(struct mwifiex_private *priv,
* LinkSys WRT54G && bss_desc->privacy
*/
) {
- dev_dbg(priv->adapter->dev, "info: %s: WPA2: index=%d"
+ dev_dbg(priv->adapter->dev, "info: %s: WPA2: "
" wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s "
- "EncMode=%#x privacy=%#x\n", __func__, index,
+ "EncMode=%#x privacy=%#x\n", __func__,
(bss_desc->bcn_wpa_ie) ?
(*(bss_desc->bcn_wpa_ie)).
vend_hdr.element_id : 0,
@@ -383,8 +328,7 @@ mwifiex_is_network_compatible_for_adhoc_aes(struct mwifiex_private *priv,
*/
static bool
mwifiex_is_network_compatible_for_dynamic_wep(struct mwifiex_private *priv,
- struct mwifiex_bssdescriptor *bss_desc,
- int index)
+ struct mwifiex_bssdescriptor *bss_desc)
{
if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED
&& !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled
@@ -395,9 +339,9 @@ mwifiex_is_network_compatible_for_dynamic_wep(struct mwifiex_private *priv,
&& priv->sec_info.encryption_mode
&& bss_desc->privacy) {
dev_dbg(priv->adapter->dev, "info: %s: dynamic "
- "WEP: index=%d wpa_ie=%#x wpa2_ie=%#x "
+ "WEP: wpa_ie=%#x wpa2_ie=%#x "
"EncMode=%#x privacy=%#x\n",
- __func__, index,
+ __func__,
(bss_desc->bcn_wpa_ie) ?
(*(bss_desc->bcn_wpa_ie)).
vend_hdr.element_id : 0,
@@ -430,42 +374,41 @@ mwifiex_is_network_compatible_for_dynamic_wep(struct mwifiex_private *priv,
* Compatibility is not matched while roaming, except for mode.
*/
static s32
-mwifiex_is_network_compatible(struct mwifiex_private *priv, u32 index, u32 mode)
+mwifiex_is_network_compatible(struct mwifiex_private *priv,
+ struct mwifiex_bssdescriptor *bss_desc, u32 mode)
{
struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_bssdescriptor *bss_desc;
- bss_desc = &adapter->scan_table[index];
bss_desc->disable_11n = false;
/* Don't check for compatibility if roaming */
if (priv->media_connected && (priv->bss_mode == NL80211_IFTYPE_STATION)
&& (bss_desc->bss_mode == NL80211_IFTYPE_STATION))
- return index;
+ return 0;
if (priv->wps.session_enable) {
dev_dbg(adapter->dev,
"info: return success directly in WPS period\n");
- return index;
+ return 0;
}
if (mwifiex_is_network_compatible_for_wapi(priv, bss_desc)) {
dev_dbg(adapter->dev, "info: return success for WAPI AP\n");
- return index;
+ return 0;
}
if (bss_desc->bss_mode == mode) {
if (mwifiex_is_network_compatible_for_no_sec(priv, bss_desc)) {
/* No security */
- return index;
+ return 0;
} else if (mwifiex_is_network_compatible_for_static_wep(priv,
bss_desc)) {
/* Static WEP enabled */
dev_dbg(adapter->dev, "info: Disable 11n in WEP mode.\n");
bss_desc->disable_11n = true;
- return index;
- } else if (mwifiex_is_network_compatible_for_wpa(priv, bss_desc,
- index)) {
+ return 0;
+ } else if (mwifiex_is_network_compatible_for_wpa(priv,
+ bss_desc)) {
/* WPA enabled */
if (((priv->adapter->config_bands & BAND_GN
|| priv->adapter->config_bands & BAND_AN)
@@ -483,9 +426,9 @@ mwifiex_is_network_compatible(struct mwifiex_private *priv, u32 index, u32 mode)
return -1;
}
}
- return index;
+ return 0;
} else if (mwifiex_is_network_compatible_for_wpa2(priv,
- bss_desc, index)) {
+ bss_desc)) {
/* WPA2 enabled */
if (((priv->adapter->config_bands & BAND_GN
|| priv->adapter->config_bands & BAND_AN)
@@ -503,22 +446,22 @@ mwifiex_is_network_compatible(struct mwifiex_private *priv, u32 index, u32 mode)
return -1;
}
}
- return index;
+ return 0;
} else if (mwifiex_is_network_compatible_for_adhoc_aes(priv,
bss_desc)) {
/* Ad-hoc AES enabled */
- return index;
+ return 0;
} else if (mwifiex_is_network_compatible_for_dynamic_wep(priv,
- bss_desc, index)) {
+ bss_desc)) {
/* Dynamic WEP enabled */
- return index;
+ return 0;
}
/* Security doesn't match */
- dev_dbg(adapter->dev, "info: %s: failed: index=%d "
+ dev_dbg(adapter->dev, "info: %s: failed: "
"wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode"
"=%#x privacy=%#x\n",
- __func__, index,
+ __func__,
(bss_desc->bcn_wpa_ie) ?
(*(bss_desc->bcn_wpa_ie)).vend_hdr.
element_id : 0,
@@ -538,52 +481,6 @@ mwifiex_is_network_compatible(struct mwifiex_private *priv, u32 index, u32 mode)
}
/*
- * This function finds the best SSID in the scan list.
- *
- * It searches the scan table for the best SSID that also matches the current
- * adapter network preference (mode, security etc.).
- */
-static s32
-mwifiex_find_best_network_in_list(struct mwifiex_private *priv)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- u32 mode = priv->bss_mode;
- s32 best_net = -1;
- s32 best_rssi = 0;
- u32 i;
-
- dev_dbg(adapter->dev, "info: num of BSSIDs = %d\n",
- adapter->num_in_scan_table);
-
- for (i = 0; i < adapter->num_in_scan_table; i++) {
- switch (mode) {
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- if (mwifiex_is_network_compatible(priv, i, mode) >= 0) {
- if (SCAN_RSSI(adapter->scan_table[i].rssi) >
- best_rssi) {
- best_rssi = SCAN_RSSI(adapter->
- scan_table[i].rssi);
- best_net = i;
- }
- }
- break;
- case NL80211_IFTYPE_UNSPECIFIED:
- default:
- if (SCAN_RSSI(adapter->scan_table[i].rssi) >
- best_rssi) {
- best_rssi = SCAN_RSSI(adapter->scan_table[i].
- rssi);
- best_net = i;
- }
- break;
- }
- }
-
- return best_net;
-}
-
-/*
* This function creates a channel list for the driver to scan, based
* on region/band information.
*
@@ -612,7 +509,7 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv,
sband = priv->wdev->wiphy->bands[band];
- for (i = 0; (i < sband->n_channels) ; i++, chan_idx++) {
+ for (i = 0; (i < sband->n_channels) ; i++) {
ch = &sband->channels[i];
if (ch->flags & IEEE80211_CHAN_DISABLED)
continue;
@@ -643,6 +540,7 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv,
scan_chan_list[chan_idx].chan_scan_mode_bitmap
|= MWIFIEX_DISABLE_CHAN_FILT;
}
+ chan_idx++;
}
}
@@ -921,8 +819,10 @@ mwifiex_scan_setup_scan_config(struct mwifiex_private *priv,
wildcard_ssid_tlv->header.len = cpu_to_le16(
(u16) (ssid_len + sizeof(wildcard_ssid_tlv->
max_ssid_length)));
- wildcard_ssid_tlv->max_ssid_length =
- user_scan_in->ssid_list[ssid_idx].max_len;
+
+ /* max_ssid_length = 0 tells firmware to perform
+ specific scan for the SSID filled */
+ wildcard_ssid_tlv->max_ssid_length = 0;
memcpy(wildcard_ssid_tlv->ssid,
user_scan_in->ssid_list[ssid_idx].ssid,
@@ -1161,34 +1061,13 @@ mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter,
}
/*
- * This function interprets a BSS scan response returned from the firmware.
- *
- * The various fixed fields and IEs are parsed and passed back for a BSS
- * probe response or beacon from scan command. Information is recorded as
- * needed in the scan table for that entry.
- *
- * The following IE types are recognized and parsed -
- * - SSID
- * - Supported rates
- * - FH parameters set
- * - DS parameters set
- * - CF parameters set
- * - IBSS parameters set
- * - ERP information
- * - Extended supported rates
- * - Vendor specific (221)
- * - RSN IE
- * - WAPI IE
- * - HT capability
- * - HT operation
- * - BSS Coexistence 20/40
- * - Extended capability
- * - Overlapping BSS scan parameters
+ * This function parses provided beacon buffer and updates
+ * respective fields in bss descriptor structure.
*/
-static int
-mwifiex_interpret_bss_desc_with_ie(struct mwifiex_adapter *adapter,
- struct mwifiex_bssdescriptor *bss_entry,
- u8 **beacon_info, u32 *bytes_left)
+int
+mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
+ struct mwifiex_bssdescriptor *bss_entry,
+ u8 *ie_buf, u32 ie_len)
{
int ret = 0;
u8 element_id;
@@ -1196,135 +1075,43 @@ mwifiex_interpret_bss_desc_with_ie(struct mwifiex_adapter *adapter,
struct ieee_types_ds_param_set *ds_param_set;
struct ieee_types_cf_param_set *cf_param_set;
struct ieee_types_ibss_param_set *ibss_param_set;
- __le16 beacon_interval;
- __le16 capabilities;
u8 *current_ptr;
u8 *rate;
u8 element_len;
u16 total_ie_len;
u8 bytes_to_copy;
u8 rate_size;
- u16 beacon_size;
u8 found_data_rate_ie;
- u32 bytes_left_for_current_beacon;
+ u32 bytes_left;
struct ieee_types_vendor_specific *vendor_ie;
const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 };
const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 };
found_data_rate_ie = false;
rate_size = 0;
- beacon_size = 0;
-
- if (*bytes_left >= sizeof(beacon_size)) {
- /* Extract & convert beacon size from the command buffer */
- memcpy(&beacon_size, *beacon_info, sizeof(beacon_size));
- *bytes_left -= sizeof(beacon_size);
- *beacon_info += sizeof(beacon_size);
- }
-
- if (!beacon_size || beacon_size > *bytes_left) {
- *beacon_info += *bytes_left;
- *bytes_left = 0;
- return -1;
- }
-
- /* Initialize the current working beacon pointer for this BSS
- iteration */
- current_ptr = *beacon_info;
-
- /* Advance the return beacon pointer past the current beacon */
- *beacon_info += beacon_size;
- *bytes_left -= beacon_size;
-
- bytes_left_for_current_beacon = beacon_size;
-
- memcpy(bss_entry->mac_address, current_ptr, ETH_ALEN);
- dev_dbg(adapter->dev, "info: InterpretIE: AP MAC Addr: %pM\n",
- bss_entry->mac_address);
-
- current_ptr += ETH_ALEN;
- bytes_left_for_current_beacon -= ETH_ALEN;
-
- if (bytes_left_for_current_beacon < 12) {
- dev_err(adapter->dev, "InterpretIE: not enough bytes left\n");
- return -1;
- }
-
- /*
- * Next 4 fields are RSSI, time stamp, beacon interval,
- * and capability information
- */
-
- /* RSSI is 1 byte long */
- bss_entry->rssi = (s32) (*current_ptr);
- dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%02X\n", *current_ptr);
- current_ptr += 1;
- bytes_left_for_current_beacon -= 1;
-
- /*
- * The RSSI is not part of the beacon/probe response. After we have
- * advanced current_ptr past the RSSI field, save the remaining
- * data for use at the application layer
- */
- bss_entry->beacon_buf = current_ptr;
- bss_entry->beacon_buf_size = bytes_left_for_current_beacon;
-
- /* Time stamp is 8 bytes long */
- memcpy(bss_entry->time_stamp, current_ptr, 8);
- current_ptr += 8;
- bytes_left_for_current_beacon -= 8;
-
- /* Beacon interval is 2 bytes long */
- memcpy(&beacon_interval, current_ptr, 2);
- bss_entry->beacon_period = le16_to_cpu(beacon_interval);
- current_ptr += 2;
- bytes_left_for_current_beacon -= 2;
-
- /* Capability information is 2 bytes long */
- memcpy(&capabilities, current_ptr, 2);
- dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n",
- capabilities);
- bss_entry->cap_info_bitmap = le16_to_cpu(capabilities);
- current_ptr += 2;
- bytes_left_for_current_beacon -= 2;
-
- /* Rest of the current buffer are IE's */
- dev_dbg(adapter->dev, "info: InterpretIE: IELength for this AP = %d\n",
- bytes_left_for_current_beacon);
-
- if (bss_entry->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
- dev_dbg(adapter->dev, "info: InterpretIE: AP WEP enabled\n");
- bss_entry->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP;
- } else {
- bss_entry->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL;
- }
-
- if (bss_entry->cap_info_bitmap & WLAN_CAPABILITY_IBSS)
- bss_entry->bss_mode = NL80211_IFTYPE_ADHOC;
- else
- bss_entry->bss_mode = NL80211_IFTYPE_STATION;
-
+ current_ptr = ie_buf;
+ bytes_left = ie_len;
+ bss_entry->beacon_buf = ie_buf;
+ bss_entry->beacon_buf_size = ie_len;
/* Process variable IE */
- while (bytes_left_for_current_beacon >= 2) {
+ while (bytes_left >= 2) {
element_id = *current_ptr;
element_len = *(current_ptr + 1);
total_ie_len = element_len + sizeof(struct ieee_types_header);
- if (bytes_left_for_current_beacon < total_ie_len) {
+ if (bytes_left < total_ie_len) {
dev_err(adapter->dev, "err: InterpretIE: in processing"
" IE, bytes left < IE length\n");
- bytes_left_for_current_beacon = 0;
- ret = -1;
- continue;
+ return -1;
}
switch (element_id) {
case WLAN_EID_SSID:
bss_entry->ssid.ssid_len = element_len;
memcpy(bss_entry->ssid.ssid, (current_ptr + 2),
element_len);
- dev_dbg(adapter->dev, "info: InterpretIE: ssid: %-32s\n",
- bss_entry->ssid.ssid);
+ dev_dbg(adapter->dev, "info: InterpretIE: ssid: "
+ "%-32s\n", bss_entry->ssid.ssid);
break;
case WLAN_EID_SUPP_RATES:
@@ -1471,13 +1258,6 @@ mwifiex_interpret_bss_desc_with_ie(struct mwifiex_adapter *adapter,
sizeof(struct ieee_types_header) -
bss_entry->beacon_buf);
break;
- case WLAN_EID_OVERLAP_BSS_SCAN_PARAM:
- bss_entry->bcn_obss_scan =
- (struct ieee_types_obss_scan_param *)
- current_ptr;
- bss_entry->overlap_bss_offset = (u16) (current_ptr -
- bss_entry->beacon_buf);
- break;
default:
break;
}
@@ -1485,577 +1265,13 @@ mwifiex_interpret_bss_desc_with_ie(struct mwifiex_adapter *adapter,
current_ptr += element_len + 2;
/* Need to account for IE ID and IE Len */
- bytes_left_for_current_beacon -= (element_len + 2);
+ bytes_left -= (element_len + 2);
- } /* while (bytes_left_for_current_beacon > 2) */
+ } /* while (bytes_left > 2) */
return ret;
}
/*
- * This function adjusts the pointers used in beacon buffers to reflect
- * shifts.
- *
- * The memory allocated for beacon buffers is of fixed sizes where all the
- * saved beacons must be stored. New beacons are added in the free portion
- * of this memory, space permitting; while duplicate beacon buffers are
- * placed at the same start location. However, since duplicate beacon
- * buffers may not match the size of the old one, all the following buffers
- * in the memory must be shifted to either make space, or to fill up freed
- * up space.
- *
- * This function is used to update the beacon buffer pointers that are past
- * an existing beacon buffer that is updated with a new one of different
- * size. The pointers are shifted by a fixed amount, either forward or
- * backward.
- *
- * the following pointers in every affected beacon buffers are changed, if
- * present -
- * - WPA IE pointer
- * - RSN IE pointer
- * - WAPI IE pointer
- * - HT capability IE pointer
- * - HT information IE pointer
- * - BSS coexistence 20/40 IE pointer
- * - Extended capability IE pointer
- * - Overlapping BSS scan parameter IE pointer
- */
-static void
-mwifiex_adjust_beacon_buffer_ptrs(struct mwifiex_private *priv, u8 advance,
- u8 *bcn_store, u32 rem_bcn_size,
- u32 num_of_ent)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- u32 adj_idx;
- for (adj_idx = 0; adj_idx < num_of_ent; adj_idx++) {
- if (adapter->scan_table[adj_idx].beacon_buf > bcn_store) {
-
- if (advance)
- adapter->scan_table[adj_idx].beacon_buf +=
- rem_bcn_size;
- else
- adapter->scan_table[adj_idx].beacon_buf -=
- rem_bcn_size;
-
- if (adapter->scan_table[adj_idx].bcn_wpa_ie)
- adapter->scan_table[adj_idx].bcn_wpa_ie =
- (struct ieee_types_vendor_specific *)
- (adapter->scan_table[adj_idx].beacon_buf +
- adapter->scan_table[adj_idx].wpa_offset);
- if (adapter->scan_table[adj_idx].bcn_rsn_ie)
- adapter->scan_table[adj_idx].bcn_rsn_ie =
- (struct ieee_types_generic *)
- (adapter->scan_table[adj_idx].beacon_buf +
- adapter->scan_table[adj_idx].rsn_offset);
- if (adapter->scan_table[adj_idx].bcn_wapi_ie)
- adapter->scan_table[adj_idx].bcn_wapi_ie =
- (struct ieee_types_generic *)
- (adapter->scan_table[adj_idx].beacon_buf +
- adapter->scan_table[adj_idx].wapi_offset);
- if (adapter->scan_table[adj_idx].bcn_ht_cap)
- adapter->scan_table[adj_idx].bcn_ht_cap =
- (struct ieee80211_ht_cap *)
- (adapter->scan_table[adj_idx].beacon_buf +
- adapter->scan_table[adj_idx].ht_cap_offset);
-
- if (adapter->scan_table[adj_idx].bcn_ht_info)
- adapter->scan_table[adj_idx].bcn_ht_info =
- (struct ieee80211_ht_info *)
- (adapter->scan_table[adj_idx].beacon_buf +
- adapter->scan_table[adj_idx].ht_info_offset);
- if (adapter->scan_table[adj_idx].bcn_bss_co_2040)
- adapter->scan_table[adj_idx].bcn_bss_co_2040 =
- (u8 *)
- (adapter->scan_table[adj_idx].beacon_buf +
- adapter->scan_table[adj_idx].bss_co_2040_offset);
- if (adapter->scan_table[adj_idx].bcn_ext_cap)
- adapter->scan_table[adj_idx].bcn_ext_cap =
- (u8 *)
- (adapter->scan_table[adj_idx].beacon_buf +
- adapter->scan_table[adj_idx].ext_cap_offset);
- if (adapter->scan_table[adj_idx].bcn_obss_scan)
- adapter->scan_table[adj_idx].bcn_obss_scan =
- (struct ieee_types_obss_scan_param *)
- (adapter->scan_table[adj_idx].beacon_buf +
- adapter->scan_table[adj_idx].overlap_bss_offset);
- }
- }
-}
-
-/*
- * This function updates the pointers used in beacon buffer for given bss
- * descriptor to reflect shifts
- *
- * Following pointers are updated
- * - WPA IE pointer
- * - RSN IE pointer
- * - WAPI IE pointer
- * - HT capability IE pointer
- * - HT information IE pointer
- * - BSS coexistence 20/40 IE pointer
- * - Extended capability IE pointer
- * - Overlapping BSS scan parameter IE pointer
- */
-static void
-mwifiex_update_beacon_buffer_ptrs(struct mwifiex_bssdescriptor *beacon)
-{
- if (beacon->bcn_wpa_ie)
- beacon->bcn_wpa_ie = (struct ieee_types_vendor_specific *)
- (beacon->beacon_buf + beacon->wpa_offset);
- if (beacon->bcn_rsn_ie)
- beacon->bcn_rsn_ie = (struct ieee_types_generic *)
- (beacon->beacon_buf + beacon->rsn_offset);
- if (beacon->bcn_wapi_ie)
- beacon->bcn_wapi_ie = (struct ieee_types_generic *)
- (beacon->beacon_buf + beacon->wapi_offset);
- if (beacon->bcn_ht_cap)
- beacon->bcn_ht_cap = (struct ieee80211_ht_cap *)
- (beacon->beacon_buf + beacon->ht_cap_offset);
- if (beacon->bcn_ht_info)
- beacon->bcn_ht_info = (struct ieee80211_ht_info *)
- (beacon->beacon_buf + beacon->ht_info_offset);
- if (beacon->bcn_bss_co_2040)
- beacon->bcn_bss_co_2040 = (u8 *) (beacon->beacon_buf +
- beacon->bss_co_2040_offset);
- if (beacon->bcn_ext_cap)
- beacon->bcn_ext_cap = (u8 *) (beacon->beacon_buf +
- beacon->ext_cap_offset);
- if (beacon->bcn_obss_scan)
- beacon->bcn_obss_scan = (struct ieee_types_obss_scan_param *)
- (beacon->beacon_buf + beacon->overlap_bss_offset);
-}
-
-/*
- * This function stores a beacon or probe response for a BSS returned
- * in the scan.
- *
- * This stores a new scan response or an update for a previous scan response.
- * New entries need to verify that they do not exceed the total amount of
- * memory allocated for the table.
- *
- * Replacement entries need to take into consideration the amount of space
- * currently allocated for the beacon/probe response and adjust the entry
- * as needed.
- *
- * A small amount of extra pad (SCAN_BEACON_ENTRY_PAD) is generally reserved
- * for an entry in case it is a beacon since a probe response for the
- * network will by larger per the standard. This helps to reduce the
- * amount of memory copying to fit a new probe response into an entry
- * already occupied by a network's previously stored beacon.
- */
-static void
-mwifiex_ret_802_11_scan_store_beacon(struct mwifiex_private *priv,
- u32 beacon_idx, u32 num_of_ent,
- struct mwifiex_bssdescriptor *new_beacon)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- u8 *bcn_store;
- u32 new_bcn_size;
- u32 old_bcn_size;
- u32 bcn_space;
-
- if (adapter->scan_table[beacon_idx].beacon_buf) {
-
- new_bcn_size = new_beacon->beacon_buf_size;
- old_bcn_size = adapter->scan_table[beacon_idx].beacon_buf_size;
- bcn_space = adapter->scan_table[beacon_idx].beacon_buf_size_max;
- bcn_store = adapter->scan_table[beacon_idx].beacon_buf;
-
- /* Set the max to be the same as current entry unless changed
- below */
- new_beacon->beacon_buf_size_max = bcn_space;
- if (new_bcn_size == old_bcn_size) {
- /*
- * Beacon is the same size as the previous entry.
- * Replace the previous contents with the scan result
- */
- memcpy(bcn_store, new_beacon->beacon_buf,
- new_beacon->beacon_buf_size);
-
- } else if (new_bcn_size <= bcn_space) {
- /*
- * New beacon size will fit in the amount of space
- * we have previously allocated for it
- */
-
- /* Copy the new beacon buffer entry over the old one */
- memcpy(bcn_store, new_beacon->beacon_buf, new_bcn_size);
-
- /*
- * If the old beacon size was less than the maximum
- * we had alloted for the entry, and the new entry
- * is even smaller, reset the max size to the old
- * beacon entry and compress the storage space
- * (leaving a new pad space of (old_bcn_size -
- * new_bcn_size).
- */
- if (old_bcn_size < bcn_space
- && new_bcn_size <= old_bcn_size) {
- /*
- * Old Beacon size is smaller than the alloted
- * storage size. Shrink the alloted storage
- * space.
- */
- dev_dbg(adapter->dev, "info: AppControl:"
- " smaller duplicate beacon "
- "(%d), old = %d, new = %d, space = %d,"
- "left = %d\n",
- beacon_idx, old_bcn_size, new_bcn_size,
- bcn_space,
- (int)(sizeof(adapter->bcn_buf) -
- (adapter->bcn_buf_end -
- adapter->bcn_buf)));
-
- /*
- * memmove (since the memory overlaps) the
- * data after the beacon we just stored to the
- * end of the current beacon. This cleans up
- * any unused space the old larger beacon was
- * using in the buffer
- */
- memmove(bcn_store + old_bcn_size,
- bcn_store + bcn_space,
- adapter->bcn_buf_end - (bcn_store +
- bcn_space));
-
- /*
- * Decrement the end pointer by the difference
- * between the old larger size and the new
- * smaller size since we are using less space
- * due to the new beacon being smaller
- */
- adapter->bcn_buf_end -=
- (bcn_space - old_bcn_size);
-
- /* Set the maximum storage size to the old
- beacon size */
- new_beacon->beacon_buf_size_max = old_bcn_size;
-
- /* Adjust beacon buffer pointers that are past
- the current */
- mwifiex_adjust_beacon_buffer_ptrs(priv, 0,
- bcn_store, (bcn_space - old_bcn_size),
- num_of_ent);
- }
- } else if (adapter->bcn_buf_end + (new_bcn_size - bcn_space)
- < (adapter->bcn_buf + sizeof(adapter->bcn_buf))) {
- /*
- * Beacon is larger than space previously allocated
- * (bcn_space) and there is enough space left in the
- * beaconBuffer to store the additional data
- */
- dev_dbg(adapter->dev, "info: AppControl:"
- " larger duplicate beacon (%d), "
- "old = %d, new = %d, space = %d, left = %d\n",
- beacon_idx, old_bcn_size, new_bcn_size,
- bcn_space,
- (int)(sizeof(adapter->bcn_buf) -
- (adapter->bcn_buf_end -
- adapter->bcn_buf)));
-
- /*
- * memmove (since the memory overlaps) the data
- * after the beacon we just stored to the end of
- * the current beacon. This moves the data for
- * the beacons after this further in memory to
- * make space for the new larger beacon we are
- * about to copy in.
- */
- memmove(bcn_store + new_bcn_size,
- bcn_store + bcn_space,
- adapter->bcn_buf_end - (bcn_store + bcn_space));
-
- /* Copy the new beacon buffer entry over the old one */
- memcpy(bcn_store, new_beacon->beacon_buf, new_bcn_size);
-
- /* Move the beacon end pointer by the amount of new
- beacon data we are adding */
- adapter->bcn_buf_end += (new_bcn_size - bcn_space);
-
- /*
- * This entry is bigger than the alloted max space
- * previously reserved. Increase the max space to
- * be equal to the new beacon size
- */
- new_beacon->beacon_buf_size_max = new_bcn_size;
-
- /* Adjust beacon buffer pointers that are past the
- current */
- mwifiex_adjust_beacon_buffer_ptrs(priv, 1, bcn_store,
- (new_bcn_size - bcn_space),
- num_of_ent);
- } else {
- /*
- * Beacon is larger than the previously allocated space,
- * but there is not enough free space to store the
- * additional data.
- */
- dev_err(adapter->dev, "AppControl: larger duplicate "
- " beacon (%d), old = %d new = %d, space = %d,"
- " left = %d\n", beacon_idx, old_bcn_size,
- new_bcn_size, bcn_space,
- (int)(sizeof(adapter->bcn_buf) -
- (adapter->bcn_buf_end - adapter->bcn_buf)));
-
- /* Storage failure, keep old beacon intact */
- new_beacon->beacon_buf_size = old_bcn_size;
- if (new_beacon->bcn_wpa_ie)
- new_beacon->wpa_offset =
- adapter->scan_table[beacon_idx].
- wpa_offset;
- if (new_beacon->bcn_rsn_ie)
- new_beacon->rsn_offset =
- adapter->scan_table[beacon_idx].
- rsn_offset;
- if (new_beacon->bcn_wapi_ie)
- new_beacon->wapi_offset =
- adapter->scan_table[beacon_idx].
- wapi_offset;
- if (new_beacon->bcn_ht_cap)
- new_beacon->ht_cap_offset =
- adapter->scan_table[beacon_idx].
- ht_cap_offset;
- if (new_beacon->bcn_ht_info)
- new_beacon->ht_info_offset =
- adapter->scan_table[beacon_idx].
- ht_info_offset;
- if (new_beacon->bcn_bss_co_2040)
- new_beacon->bss_co_2040_offset =
- adapter->scan_table[beacon_idx].
- bss_co_2040_offset;
- if (new_beacon->bcn_ext_cap)
- new_beacon->ext_cap_offset =
- adapter->scan_table[beacon_idx].
- ext_cap_offset;
- if (new_beacon->bcn_obss_scan)
- new_beacon->overlap_bss_offset =
- adapter->scan_table[beacon_idx].
- overlap_bss_offset;
- }
- /* Point the new entry to its permanent storage space */
- new_beacon->beacon_buf = bcn_store;
- mwifiex_update_beacon_buffer_ptrs(new_beacon);
- } else {
- /*
- * No existing beacon data exists for this entry, check to see
- * if we can fit it in the remaining space
- */
- if (adapter->bcn_buf_end + new_beacon->beacon_buf_size +
- SCAN_BEACON_ENTRY_PAD < (adapter->bcn_buf +
- sizeof(adapter->bcn_buf))) {
-
- /*
- * Copy the beacon buffer data from the local entry to
- * the adapter dev struct buffer space used to store
- * the raw beacon data for each entry in the scan table
- */
- memcpy(adapter->bcn_buf_end, new_beacon->beacon_buf,
- new_beacon->beacon_buf_size);
-
- /* Update the beacon ptr to point to the table save
- area */
- new_beacon->beacon_buf = adapter->bcn_buf_end;
- new_beacon->beacon_buf_size_max =
- (new_beacon->beacon_buf_size +
- SCAN_BEACON_ENTRY_PAD);
-
- mwifiex_update_beacon_buffer_ptrs(new_beacon);
-
- /* Increment the end pointer by the size reserved */
- adapter->bcn_buf_end += new_beacon->beacon_buf_size_max;
-
- dev_dbg(adapter->dev, "info: AppControl: beacon[%02d]"
- " sz=%03d, used = %04d, left = %04d\n",
- beacon_idx,
- new_beacon->beacon_buf_size,
- (int)(adapter->bcn_buf_end - adapter->bcn_buf),
- (int)(sizeof(adapter->bcn_buf) -
- (adapter->bcn_buf_end -
- adapter->bcn_buf)));
- } else {
- /* No space for new beacon */
- dev_dbg(adapter->dev, "info: AppControl: no space for"
- " beacon (%d): %pM sz=%03d, left=%03d\n",
- beacon_idx, new_beacon->mac_address,
- new_beacon->beacon_buf_size,
- (int)(sizeof(adapter->bcn_buf) -
- (adapter->bcn_buf_end -
- adapter->bcn_buf)));
-
- /* Storage failure; clear storage records for this
- bcn */
- new_beacon->beacon_buf = NULL;
- new_beacon->beacon_buf_size = 0;
- new_beacon->beacon_buf_size_max = 0;
- new_beacon->bcn_wpa_ie = NULL;
- new_beacon->wpa_offset = 0;
- new_beacon->bcn_rsn_ie = NULL;
- new_beacon->rsn_offset = 0;
- new_beacon->bcn_wapi_ie = NULL;
- new_beacon->wapi_offset = 0;
- new_beacon->bcn_ht_cap = NULL;
- new_beacon->ht_cap_offset = 0;
- new_beacon->bcn_ht_info = NULL;
- new_beacon->ht_info_offset = 0;
- new_beacon->bcn_bss_co_2040 = NULL;
- new_beacon->bss_co_2040_offset = 0;
- new_beacon->bcn_ext_cap = NULL;
- new_beacon->ext_cap_offset = 0;
- new_beacon->bcn_obss_scan = NULL;
- new_beacon->overlap_bss_offset = 0;
- }
- }
-}
-
-/*
- * This function restores a beacon buffer of the current BSS descriptor.
- */
-static void mwifiex_restore_curr_bcn(struct mwifiex_private *priv)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_bssdescriptor *curr_bss =
- &priv->curr_bss_params.bss_descriptor;
- unsigned long flags;
-
- if (priv->curr_bcn_buf &&
- ((adapter->bcn_buf_end + priv->curr_bcn_size) <
- (adapter->bcn_buf + sizeof(adapter->bcn_buf)))) {
- spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags);
-
- /* restore the current beacon buffer */
- memcpy(adapter->bcn_buf_end, priv->curr_bcn_buf,
- priv->curr_bcn_size);
- curr_bss->beacon_buf = adapter->bcn_buf_end;
- curr_bss->beacon_buf_size = priv->curr_bcn_size;
- adapter->bcn_buf_end += priv->curr_bcn_size;
-
- /* adjust the pointers in the current BSS descriptor */
- if (curr_bss->bcn_wpa_ie)
- curr_bss->bcn_wpa_ie =
- (struct ieee_types_vendor_specific *)
- (curr_bss->beacon_buf +
- curr_bss->wpa_offset);
-
- if (curr_bss->bcn_rsn_ie)
- curr_bss->bcn_rsn_ie = (struct ieee_types_generic *)
- (curr_bss->beacon_buf +
- curr_bss->rsn_offset);
-
- if (curr_bss->bcn_ht_cap)
- curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *)
- (curr_bss->beacon_buf +
- curr_bss->ht_cap_offset);
-
- if (curr_bss->bcn_ht_info)
- curr_bss->bcn_ht_info = (struct ieee80211_ht_info *)
- (curr_bss->beacon_buf +
- curr_bss->ht_info_offset);
-
- if (curr_bss->bcn_bss_co_2040)
- curr_bss->bcn_bss_co_2040 =
- (u8 *) (curr_bss->beacon_buf +
- curr_bss->bss_co_2040_offset);
-
- if (curr_bss->bcn_ext_cap)
- curr_bss->bcn_ext_cap = (u8 *) (curr_bss->beacon_buf +
- curr_bss->ext_cap_offset);
-
- if (curr_bss->bcn_obss_scan)
- curr_bss->bcn_obss_scan =
- (struct ieee_types_obss_scan_param *)
- (curr_bss->beacon_buf +
- curr_bss->overlap_bss_offset);
-
- spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags);
-
- dev_dbg(adapter->dev, "info: current beacon restored %d\n",
- priv->curr_bcn_size);
- } else {
- dev_warn(adapter->dev,
- "curr_bcn_buf not saved or bcn_buf has no space\n");
- }
-}
-
-/*
- * This function post processes the scan table after a new scan command has
- * completed.
- *
- * It inspects each entry of the scan table and tries to find an entry that
- * matches with our current associated/joined network from the scan. If
- * one is found, the stored copy of the BSS descriptor of our current network
- * is updated.
- *
- * It also debug dumps the current scan table contents after processing is over.
- */
-static void
-mwifiex_process_scan_results(struct mwifiex_private *priv)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- s32 j;
- u32 i;
- unsigned long flags;
-
- if (priv->media_connected) {
-
- j = mwifiex_find_ssid_in_list(priv, &priv->curr_bss_params.
- bss_descriptor.ssid,
- priv->curr_bss_params.
- bss_descriptor.mac_address,
- priv->bss_mode);
-
- if (j >= 0) {
- spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags);
- priv->curr_bss_params.bss_descriptor.bcn_wpa_ie = NULL;
- priv->curr_bss_params.bss_descriptor.wpa_offset = 0;
- priv->curr_bss_params.bss_descriptor.bcn_rsn_ie = NULL;
- priv->curr_bss_params.bss_descriptor.rsn_offset = 0;
- priv->curr_bss_params.bss_descriptor.bcn_wapi_ie = NULL;
- priv->curr_bss_params.bss_descriptor.wapi_offset = 0;
- priv->curr_bss_params.bss_descriptor.bcn_ht_cap = NULL;
- priv->curr_bss_params.bss_descriptor.ht_cap_offset =
- 0;
- priv->curr_bss_params.bss_descriptor.bcn_ht_info = NULL;
- priv->curr_bss_params.bss_descriptor.ht_info_offset =
- 0;
- priv->curr_bss_params.bss_descriptor.bcn_bss_co_2040 =
- NULL;
- priv->curr_bss_params.bss_descriptor.
- bss_co_2040_offset = 0;
- priv->curr_bss_params.bss_descriptor.bcn_ext_cap = NULL;
- priv->curr_bss_params.bss_descriptor.ext_cap_offset = 0;
- priv->curr_bss_params.bss_descriptor.
- bcn_obss_scan = NULL;
- priv->curr_bss_params.bss_descriptor.
- overlap_bss_offset = 0;
- priv->curr_bss_params.bss_descriptor.beacon_buf = NULL;
- priv->curr_bss_params.bss_descriptor.beacon_buf_size =
- 0;
- priv->curr_bss_params.bss_descriptor.
- beacon_buf_size_max = 0;
-
- dev_dbg(adapter->dev, "info: Found current ssid/bssid"
- " in list @ index #%d\n", j);
- /* Make a copy of current BSSID descriptor */
- memcpy(&priv->curr_bss_params.bss_descriptor,
- &adapter->scan_table[j],
- sizeof(priv->curr_bss_params.bss_descriptor));
-
- mwifiex_save_curr_bcn(priv);
- spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags);
-
- } else {
- mwifiex_restore_curr_bcn(priv);
- }
- }
-
- for (i = 0; i < adapter->num_in_scan_table; i++)
- dev_dbg(adapter->dev, "info: scan:(%02d) %pM "
- "RSSI[%03d], SSID[%s]\n",
- i, adapter->scan_table[i].mac_address,
- (s32) adapter->scan_table[i].rssi,
- adapter->scan_table[i].ssid.ssid);
-}
-
-/*
* This function converts radio type scan parameter to a band configuration
* to be used in join command.
*/
@@ -2072,175 +1288,6 @@ mwifiex_radio_type_to_band(u8 radio_type)
}
/*
- * This function deletes a specific indexed entry from the scan table.
- *
- * This also compacts the remaining entries and adjusts any buffering
- * of beacon/probe response data if needed.
- */
-static void
-mwifiex_scan_delete_table_entry(struct mwifiex_private *priv, s32 table_idx)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- u32 del_idx;
- u32 beacon_buf_adj;
- u8 *beacon_buf;
-
- /*
- * Shift the saved beacon buffer data for the scan table back over the
- * entry being removed. Update the end of buffer pointer. Save the
- * deleted buffer allocation size for pointer adjustments for entries
- * compacted after the deleted index.
- */
- beacon_buf_adj = adapter->scan_table[table_idx].beacon_buf_size_max;
-
- dev_dbg(adapter->dev, "info: Scan: Delete Entry %d, beacon buffer "
- "removal = %d bytes\n", table_idx, beacon_buf_adj);
-
- /* Check if the table entry had storage allocated for its beacon */
- if (beacon_buf_adj) {
- beacon_buf = adapter->scan_table[table_idx].beacon_buf;
-
- /*
- * Remove the entry's buffer space, decrement the table end
- * pointer by the amount we are removing
- */
- adapter->bcn_buf_end -= beacon_buf_adj;
-
- dev_dbg(adapter->dev, "info: scan: delete entry %d,"
- " compact data: %p <- %p (sz = %d)\n",
- table_idx, beacon_buf,
- beacon_buf + beacon_buf_adj,
- (int)(adapter->bcn_buf_end - beacon_buf));
-
- /*
- * Compact data storage. Copy all data after the deleted
- * entry's end address (beacon_buf + beacon_buf_adj) back
- * to the original start address (beacon_buf).
- *
- * Scan table entries affected by the move will have their
- * entry pointer adjusted below.
- *
- * Use memmove since the dest/src memory regions overlap.
- */
- memmove(beacon_buf, beacon_buf + beacon_buf_adj,
- adapter->bcn_buf_end - beacon_buf);
- }
-
- dev_dbg(adapter->dev,
- "info: Scan: Delete Entry %d, num_in_scan_table = %d\n",
- table_idx, adapter->num_in_scan_table);
-
- /* Shift all of the entries after the table_idx back by one, compacting
- the table and removing the requested entry */
- for (del_idx = table_idx; (del_idx + 1) < adapter->num_in_scan_table;
- del_idx++) {
- /* Copy the next entry over this one */
- memcpy(adapter->scan_table + del_idx,
- adapter->scan_table + del_idx + 1,
- sizeof(struct mwifiex_bssdescriptor));
-
- /*
- * Adjust this entry's pointer to its beacon buffer based on
- * the removed/compacted entry from the deleted index. Don't
- * decrement if the buffer pointer is NULL (no data stored for
- * this entry).
- */
- if (adapter->scan_table[del_idx].beacon_buf) {
- adapter->scan_table[del_idx].beacon_buf -=
- beacon_buf_adj;
- if (adapter->scan_table[del_idx].bcn_wpa_ie)
- adapter->scan_table[del_idx].bcn_wpa_ie =
- (struct ieee_types_vendor_specific *)
- (adapter->scan_table[del_idx].
- beacon_buf +
- adapter->scan_table[del_idx].
- wpa_offset);
- if (adapter->scan_table[del_idx].bcn_rsn_ie)
- adapter->scan_table[del_idx].bcn_rsn_ie =
- (struct ieee_types_generic *)
- (adapter->scan_table[del_idx].
- beacon_buf +
- adapter->scan_table[del_idx].
- rsn_offset);
- if (adapter->scan_table[del_idx].bcn_wapi_ie)
- adapter->scan_table[del_idx].bcn_wapi_ie =
- (struct ieee_types_generic *)
- (adapter->scan_table[del_idx].beacon_buf
- + adapter->scan_table[del_idx].
- wapi_offset);
- if (adapter->scan_table[del_idx].bcn_ht_cap)
- adapter->scan_table[del_idx].bcn_ht_cap =
- (struct ieee80211_ht_cap *)
- (adapter->scan_table[del_idx].beacon_buf
- + adapter->scan_table[del_idx].
- ht_cap_offset);
-
- if (adapter->scan_table[del_idx].bcn_ht_info)
- adapter->scan_table[del_idx].bcn_ht_info =
- (struct ieee80211_ht_info *)
- (adapter->scan_table[del_idx].beacon_buf
- + adapter->scan_table[del_idx].
- ht_info_offset);
- if (adapter->scan_table[del_idx].bcn_bss_co_2040)
- adapter->scan_table[del_idx].bcn_bss_co_2040 =
- (u8 *)
- (adapter->scan_table[del_idx].beacon_buf
- + adapter->scan_table[del_idx].
- bss_co_2040_offset);
- if (adapter->scan_table[del_idx].bcn_ext_cap)
- adapter->scan_table[del_idx].bcn_ext_cap =
- (u8 *)
- (adapter->scan_table[del_idx].beacon_buf
- + adapter->scan_table[del_idx].
- ext_cap_offset);
- if (adapter->scan_table[del_idx].bcn_obss_scan)
- adapter->scan_table[del_idx].
- bcn_obss_scan =
- (struct ieee_types_obss_scan_param *)
- (adapter->scan_table[del_idx].beacon_buf
- + adapter->scan_table[del_idx].
- overlap_bss_offset);
- }
- }
-
- /* The last entry is invalid now that it has been deleted or moved
- back */
- memset(adapter->scan_table + adapter->num_in_scan_table - 1,
- 0x00, sizeof(struct mwifiex_bssdescriptor));
-
- adapter->num_in_scan_table--;
-}
-
-/*
- * This function deletes all occurrences of a given SSID from the scan table.
- *
- * This iterates through the scan table and deletes all entries that match
- * the given SSID. It also compacts the remaining scan table entries.
- */
-static int
-mwifiex_scan_delete_ssid_table_entry(struct mwifiex_private *priv,
- struct mwifiex_802_11_ssid *del_ssid)
-{
- s32 table_idx = -1;
-
- dev_dbg(priv->adapter->dev, "info: scan: delete ssid entry: %-32s\n",
- del_ssid->ssid);
-
- /* If the requested SSID is found in the table, delete it. Then keep
- searching the table for multiple entires for the SSID until no
- more are found */
- while ((table_idx = mwifiex_find_ssid_in_list(priv, del_ssid, NULL,
- NL80211_IFTYPE_UNSPECIFIED)) >= 0) {
- dev_dbg(priv->adapter->dev,
- "info: Scan: Delete SSID Entry: Found Idx = %d\n",
- table_idx);
- mwifiex_scan_delete_table_entry(priv, table_idx);
- }
-
- return table_idx == -1 ? -1 : 0;
-}
-
-/*
* This is an internal function used to start a scan based on an input
* configuration.
*
@@ -2248,8 +1295,8 @@ mwifiex_scan_delete_ssid_table_entry(struct mwifiex_private *priv,
* order to send the appropriate scan commands to firmware to populate or
* update the internal driver scan table.
*/
-int mwifiex_scan_networks(struct mwifiex_private *priv,
- const struct mwifiex_user_scan_cfg *user_scan_in)
+static int mwifiex_scan_networks(struct mwifiex_private *priv,
+ const struct mwifiex_user_scan_cfg *user_scan_in)
{
int ret = 0;
struct mwifiex_adapter *adapter = priv->adapter;
@@ -2258,7 +1305,6 @@ int mwifiex_scan_networks(struct mwifiex_private *priv,
struct mwifiex_ie_types_chan_list_param_set *chan_list_out;
u32 buf_size;
struct mwifiex_chan_scan_param_set *scan_chan_list;
- u8 keep_previous_scan;
u8 filtered_scan;
u8 scan_current_chan_only;
u8 max_chan_per_scan;
@@ -2295,24 +1341,11 @@ int mwifiex_scan_networks(struct mwifiex_private *priv,
return -ENOMEM;
}
- keep_previous_scan = false;
-
mwifiex_scan_setup_scan_config(priv, user_scan_in,
&scan_cfg_out->config, &chan_list_out,
scan_chan_list, &max_chan_per_scan,
&filtered_scan, &scan_current_chan_only);
- if (user_scan_in)
- keep_previous_scan = user_scan_in->keep_previous_scan;
-
-
- if (!keep_previous_scan) {
- memset(adapter->scan_table, 0x00,
- sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP);
- adapter->num_in_scan_table = 0;
- adapter->bcn_buf_end = adapter->bcn_buf;
- }
-
ret = mwifiex_scan_channel_list(priv, max_chan_per_scan, filtered_scan,
&scan_cfg_out->config, chan_list_out,
scan_chan_list);
@@ -2326,6 +1359,7 @@ int mwifiex_scan_networks(struct mwifiex_private *priv,
list_del(&cmd_node->list);
spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
flags);
+ adapter->cmd_queued = cmd_node;
mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
true);
} else {
@@ -2344,6 +1378,29 @@ int mwifiex_scan_networks(struct mwifiex_private *priv,
}
/*
+ * Sends IOCTL request to start a scan with user configurations.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ *
+ * Upon completion, it also generates a wireless event to notify
+ * applications.
+ */
+int mwifiex_set_user_scan_ioctl(struct mwifiex_private *priv,
+ struct mwifiex_user_scan_cfg *scan_req)
+{
+ int status;
+
+ priv->adapter->scan_wait_q_woken = false;
+
+ status = mwifiex_scan_networks(priv, scan_req);
+ if (!status)
+ status = mwifiex_wait_queue_complete(priv->adapter);
+
+ return status;
+}
+
+/*
* This function prepares a scan command to be sent to the firmware.
*
* This uses the scan command configuration sent to the command processing
@@ -2357,12 +1414,10 @@ int mwifiex_scan_networks(struct mwifiex_private *priv,
* - Setting command ID, and proper size
* - Ensuring correct endian-ness
*/
-int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, void *data_buf)
+int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
+ struct mwifiex_scan_cmd_config *scan_cfg)
{
struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan;
- struct mwifiex_scan_cmd_config *scan_cfg;
-
- scan_cfg = (struct mwifiex_scan_cmd_config *) data_buf;
/* Set fixed field variables in scan command */
scan_cmd->bss_mode = scan_cfg->bss_mode;
@@ -2381,6 +1436,113 @@ int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, void *data_buf)
}
/*
+ * This function checks compatibility of requested network with current
+ * driver settings.
+ */
+int mwifiex_check_network_compatibility(struct mwifiex_private *priv,
+ struct mwifiex_bssdescriptor *bss_desc)
+{
+ int ret = -1;
+
+ if (!bss_desc)
+ return -1;
+
+ if ((mwifiex_get_cfp_by_band_and_channel_from_cfg80211(priv,
+ (u8) bss_desc->bss_band, (u16) bss_desc->channel))) {
+ switch (priv->bss_mode) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ ret = mwifiex_is_network_compatible(priv, bss_desc,
+ priv->bss_mode);
+ if (ret)
+ dev_err(priv->adapter->dev, "cannot find ssid "
+ "%s\n", bss_desc->ssid.ssid);
+ break;
+ default:
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+static int
+mwifiex_update_curr_bss_params(struct mwifiex_private *priv, u8 *bssid,
+ s32 rssi, const u8 *ie_buf, size_t ie_len,
+ u16 beacon_period, u16 cap_info_bitmap, u8 band)
+{
+ struct mwifiex_bssdescriptor *bss_desc;
+ int ret;
+ unsigned long flags;
+ u8 *beacon_ie;
+
+ /* Allocate and fill new bss descriptor */
+ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor),
+ GFP_KERNEL);
+ if (!bss_desc) {
+ dev_err(priv->adapter->dev, " failed to alloc bss_desc\n");
+ return -ENOMEM;
+ }
+
+ beacon_ie = kmemdup(ie_buf, ie_len, GFP_KERNEL);
+ if (!beacon_ie) {
+ kfree(bss_desc);
+ dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n");
+ return -ENOMEM;
+ }
+
+ ret = mwifiex_fill_new_bss_desc(priv, bssid, rssi, beacon_ie,
+ ie_len, beacon_period,
+ cap_info_bitmap, band, bss_desc);
+ if (ret)
+ goto done;
+
+ ret = mwifiex_check_network_compatibility(priv, bss_desc);
+ if (ret)
+ goto done;
+
+ /* Update current bss descriptor parameters */
+ spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags);
+ priv->curr_bss_params.bss_descriptor.bcn_wpa_ie = NULL;
+ priv->curr_bss_params.bss_descriptor.wpa_offset = 0;
+ priv->curr_bss_params.bss_descriptor.bcn_rsn_ie = NULL;
+ priv->curr_bss_params.bss_descriptor.rsn_offset = 0;
+ priv->curr_bss_params.bss_descriptor.bcn_wapi_ie = NULL;
+ priv->curr_bss_params.bss_descriptor.wapi_offset = 0;
+ priv->curr_bss_params.bss_descriptor.bcn_ht_cap = NULL;
+ priv->curr_bss_params.bss_descriptor.ht_cap_offset =
+ 0;
+ priv->curr_bss_params.bss_descriptor.bcn_ht_info = NULL;
+ priv->curr_bss_params.bss_descriptor.ht_info_offset =
+ 0;
+ priv->curr_bss_params.bss_descriptor.bcn_bss_co_2040 =
+ NULL;
+ priv->curr_bss_params.bss_descriptor.
+ bss_co_2040_offset = 0;
+ priv->curr_bss_params.bss_descriptor.bcn_ext_cap = NULL;
+ priv->curr_bss_params.bss_descriptor.ext_cap_offset = 0;
+ priv->curr_bss_params.bss_descriptor.beacon_buf = NULL;
+ priv->curr_bss_params.bss_descriptor.beacon_buf_size =
+ 0;
+
+ /* Make a copy of current BSSID descriptor */
+ memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc,
+ sizeof(priv->curr_bss_params.bss_descriptor));
+ mwifiex_save_curr_bcn(priv);
+ spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags);
+
+done:
+ kfree(bss_desc);
+ kfree(beacon_ie);
+ return 0;
+}
+
+static void mwifiex_free_bss_priv(struct cfg80211_bss *bss)
+{
+ kfree(bss->priv);
+}
+
+/*
* This function handles the command response of scan.
*
* The response buffer for the scan command has the following
@@ -2406,23 +1568,19 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
struct mwifiex_adapter *adapter = priv->adapter;
struct cmd_ctrl_node *cmd_node;
struct host_cmd_ds_802_11_scan_rsp *scan_rsp;
- struct mwifiex_bssdescriptor *bss_new_entry = NULL;
struct mwifiex_ie_types_data *tlv_data;
struct mwifiex_ie_types_tsf_timestamp *tsf_tlv;
u8 *bss_info;
u32 scan_resp_size;
u32 bytes_left;
- u32 num_in_table;
- u32 bss_idx;
u32 idx;
u32 tlv_buf_size;
- long long tsf_val;
struct mwifiex_chan_freq_power *cfp;
struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv;
struct chan_band_param_set *chan_band;
- u8 band;
u8 is_bgscan_resp;
unsigned long flags;
+ struct cfg80211_bss *bss;
is_bgscan_resp = (le16_to_cpu(resp->command)
== HostCmd_CMD_802_11_BG_SCAN_QUERY);
@@ -2432,7 +1590,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
scan_rsp = &resp->params.scan_resp;
- if (scan_rsp->number_of_sets > IW_MAX_AP) {
+ if (scan_rsp->number_of_sets > MWIFIEX_MAX_AP) {
dev_err(adapter->dev, "SCAN_RESP: too many AP returned (%d)\n",
scan_rsp->number_of_sets);
ret = -1;
@@ -2449,7 +1607,6 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
"info: SCAN_RESP: returned %d APs before parsing\n",
scan_rsp->number_of_sets);
- num_in_table = adapter->num_in_scan_table;
bss_info = scan_rsp->bss_desc_and_tlv_buffer;
/*
@@ -2481,125 +1638,149 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
(struct mwifiex_ie_types_data **)
&chan_band_tlv);
- /*
- * Process each scan response returned (scan_rsp->number_of_sets).
- * Save the information in the bss_new_entry and then insert into the
- * driver scan table either as an update to an existing entry
- * or as an addition at the end of the table
- */
- bss_new_entry = kzalloc(sizeof(struct mwifiex_bssdescriptor),
- GFP_KERNEL);
- if (!bss_new_entry) {
- dev_err(adapter->dev, " failed to alloc bss_new_entry\n");
- return -ENOMEM;
- }
-
for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) {
- /* Zero out the bss_new_entry we are about to store info in */
- memset(bss_new_entry, 0x00,
- sizeof(struct mwifiex_bssdescriptor));
-
- if (mwifiex_interpret_bss_desc_with_ie(adapter, bss_new_entry,
- &bss_info,
- &bytes_left)) {
- /* Error parsing/interpreting scan response, skipped */
- dev_err(adapter->dev, "SCAN_RESP: "
- "mwifiex_interpret_bss_desc_with_ie "
- "returned ERROR\n");
- continue;
+ u8 bssid[ETH_ALEN];
+ s32 rssi;
+ const u8 *ie_buf;
+ size_t ie_len;
+ int channel = -1;
+ u64 network_tsf = 0;
+ u16 beacon_size = 0;
+ u32 curr_bcn_bytes;
+ u32 freq;
+ u16 beacon_period;
+ u16 cap_info_bitmap;
+ u8 *current_ptr;
+ struct mwifiex_bcn_param *bcn_param;
+
+ if (bytes_left >= sizeof(beacon_size)) {
+ /* Extract & convert beacon size from command buffer */
+ memcpy(&beacon_size, bss_info, sizeof(beacon_size));
+ bytes_left -= sizeof(beacon_size);
+ bss_info += sizeof(beacon_size);
+ }
+
+ if (!beacon_size || beacon_size > bytes_left) {
+ bss_info += bytes_left;
+ bytes_left = 0;
+ return -1;
}
- /* Process the data fields and IEs returned for this BSS */
- dev_dbg(adapter->dev, "info: SCAN_RESP: BSSID = %pM\n",
- bss_new_entry->mac_address);
+ /* Initialize the current working beacon pointer for this BSS
+ * iteration */
+ current_ptr = bss_info;
- /* Search the scan table for the same bssid */
- for (bss_idx = 0; bss_idx < num_in_table; bss_idx++) {
- if (memcmp(bss_new_entry->mac_address,
- adapter->scan_table[bss_idx].mac_address,
- sizeof(bss_new_entry->mac_address))) {
- continue;
+ /* Advance the return beacon pointer past the current beacon */
+ bss_info += beacon_size;
+ bytes_left -= beacon_size;
+
+ curr_bcn_bytes = beacon_size;
+
+ /*
+ * First 5 fields are bssid, RSSI, time stamp, beacon interval,
+ * and capability information
+ */
+ if (curr_bcn_bytes < sizeof(struct mwifiex_bcn_param)) {
+ dev_err(adapter->dev, "InterpretIE: not enough bytes left\n");
+ continue;
+ }
+ bcn_param = (struct mwifiex_bcn_param *)current_ptr;
+ current_ptr += sizeof(*bcn_param);
+ curr_bcn_bytes -= sizeof(*bcn_param);
+
+ memcpy(bssid, bcn_param->bssid, ETH_ALEN);
+
+ rssi = (s32) (bcn_param->rssi);
+ dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%02X\n",
+ rssi);
+
+ beacon_period = le16_to_cpu(bcn_param->beacon_period);
+
+ cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap);
+ dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n",
+ cap_info_bitmap);
+
+ /* Rest of the current buffer are IE's */
+ ie_buf = current_ptr;
+ ie_len = curr_bcn_bytes;
+ dev_dbg(adapter->dev, "info: InterpretIE: IELength for this AP"
+ " = %d\n", curr_bcn_bytes);
+
+ while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) {
+ u8 element_id, element_len;
+
+ element_id = *current_ptr;
+ element_len = *(current_ptr + 1);
+ if (curr_bcn_bytes < element_len +
+ sizeof(struct ieee_types_header)) {
+ dev_err(priv->adapter->dev, "%s: in processing"
+ " IE, bytes left < IE length\n",
+ __func__);
+ goto done;
}
- /*
- * If the SSID matches as well, it is a
- * duplicate of this entry. Keep the bss_idx
- * set to this entry so we replace the old
- * contents in the table
- */
- if ((bss_new_entry->ssid.ssid_len
- == adapter->scan_table[bss_idx]. ssid.ssid_len)
- && (!memcmp(bss_new_entry->ssid.ssid,
- adapter->scan_table[bss_idx].ssid.ssid,
- bss_new_entry->ssid.ssid_len))) {
- dev_dbg(adapter->dev, "info: SCAN_RESP:"
- " duplicate of index: %d\n", bss_idx);
+ if (element_id == WLAN_EID_DS_PARAMS) {
+ channel = *(u8 *) (current_ptr +
+ sizeof(struct ieee_types_header));
break;
}
- }
- /*
- * If the bss_idx is equal to the number of entries in
- * the table, the new entry was not a duplicate; append
- * it to the scan table
- */
- if (bss_idx == num_in_table) {
- /* Range check the bss_idx, keep it limited to
- the last entry */
- if (bss_idx == IW_MAX_AP)
- bss_idx--;
- else
- num_in_table++;
+
+ current_ptr += element_len +
+ sizeof(struct ieee_types_header);
+ curr_bcn_bytes -= element_len +
+ sizeof(struct ieee_types_header);
}
/*
- * Save the beacon/probe response returned for later application
- * retrieval. Duplicate beacon/probe responses are updated if
- * possible
- */
- mwifiex_ret_802_11_scan_store_beacon(priv, bss_idx,
- num_in_table, bss_new_entry);
- /*
* If the TSF TLV was appended to the scan results, save this
* entry's TSF value in the networkTSF field.The networkTSF is
* the firmware's TSF value at the time the beacon or probe
* response was received.
*/
- if (tsf_tlv) {
- memcpy(&tsf_val, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE]
- , sizeof(tsf_val));
- memcpy(&bss_new_entry->network_tsf, &tsf_val,
- sizeof(bss_new_entry->network_tsf));
- }
- band = BAND_G;
- if (chan_band_tlv) {
- chan_band = &chan_band_tlv->chan_band_param[idx];
- band = mwifiex_radio_type_to_band(chan_band->radio_type
- & (BIT(0) | BIT(1)));
- }
+ if (tsf_tlv)
+ memcpy(&network_tsf,
+ &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE],
+ sizeof(network_tsf));
+
+ if (channel != -1) {
+ struct ieee80211_channel *chan;
+ u8 band;
+
+ band = BAND_G;
+ if (chan_band_tlv) {
+ chan_band =
+ &chan_band_tlv->chan_band_param[idx];
+ band = mwifiex_radio_type_to_band(
+ chan_band->radio_type
+ & (BIT(0) | BIT(1)));
+ }
- /* Save the band designation for this entry for use in join */
- bss_new_entry->bss_band = band;
- cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211(priv,
- (u8) bss_new_entry->bss_band,
- (u16)bss_new_entry->channel);
+ cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211(
+ priv, (u8)band, (u16)channel);
- if (cfp)
- bss_new_entry->freq = cfp->freq;
- else
- bss_new_entry->freq = 0;
+ freq = cfp ? cfp->freq : 0;
- /* Copy the locally created bss_new_entry to the scan table */
- memcpy(&adapter->scan_table[bss_idx], bss_new_entry,
- sizeof(adapter->scan_table[bss_idx]));
+ chan = ieee80211_get_channel(priv->wdev->wiphy, freq);
- }
+ if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
+ bss = cfg80211_inform_bss(priv->wdev->wiphy,
+ chan, bssid, network_tsf,
+ cap_info_bitmap, beacon_period,
+ ie_buf, ie_len, rssi, GFP_KERNEL);
+ *(u8 *)bss->priv = band;
+ bss->free_priv = mwifiex_free_bss_priv;
- dev_dbg(adapter->dev,
- "info: SCAN_RESP: Scanned %2d APs, %d valid, %d total\n",
- scan_rsp->number_of_sets,
- num_in_table - adapter->num_in_scan_table, num_in_table);
-
- /* Update the total number of BSSIDs in the scan table */
- adapter->num_in_scan_table = num_in_table;
+ if (priv->media_connected && !memcmp(bssid,
+ priv->curr_bss_params.bss_descriptor
+ .mac_address, ETH_ALEN))
+ mwifiex_update_curr_bss_params(priv,
+ bssid, rssi, ie_buf,
+ ie_len, beacon_period,
+ cap_info_bitmap, band);
+ }
+ } else {
+ dev_dbg(adapter->dev, "missing BSS channel IE\n");
+ }
+ }
spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
if (list_empty(&adapter->scan_pending_q)) {
@@ -2607,17 +1788,11 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
adapter->scan_processing = false;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
- /*
- * Process the resulting scan table:
- * - Remove any bad ssids
- * - Update our current BSS information from scan data
- */
- mwifiex_process_scan_results(priv);
/* Need to indicate IOCTL complete */
if (adapter->curr_cmd->wait_q_enabled) {
adapter->cmd_wait_q.status = 0;
- mwifiex_complete_cmd(adapter);
+ mwifiex_complete_cmd(adapter, adapter->curr_cmd);
}
if (priv->report_scan_result)
priv->report_scan_result = false;
@@ -2638,7 +1813,6 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
}
done:
- kfree((u8 *) bss_new_entry);
return ret;
}
@@ -2665,141 +1839,6 @@ int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd)
}
/*
- * This function finds a SSID in the scan table.
- *
- * A BSSID may optionally be provided to qualify the SSID.
- * For non-Auto mode, further check is made to make sure the
- * BSS found in the scan table is compatible with the current
- * settings of the driver.
- */
-s32
-mwifiex_find_ssid_in_list(struct mwifiex_private *priv,
- struct mwifiex_802_11_ssid *ssid, u8 *bssid,
- u32 mode)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- s32 net = -1, j;
- u8 best_rssi = 0;
- u32 i;
-
- dev_dbg(adapter->dev, "info: num of entries in table = %d\n",
- adapter->num_in_scan_table);
-
- /*
- * Loop through the table until the maximum is reached or until a match
- * is found based on the bssid field comparison
- */
- for (i = 0;
- i < adapter->num_in_scan_table && (!bssid || (bssid && net < 0));
- i++) {
- if (!mwifiex_ssid_cmp(&adapter->scan_table[i].ssid, ssid) &&
- (!bssid
- || !memcmp(adapter->scan_table[i].mac_address, bssid,
- ETH_ALEN))
- &&
- (mwifiex_get_cfp_by_band_and_channel_from_cfg80211
- (priv, (u8) adapter->scan_table[i].bss_band,
- (u16) adapter->scan_table[i].channel))) {
- switch (mode) {
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- j = mwifiex_is_network_compatible(priv, i,
- mode);
-
- if (j >= 0) {
- if (SCAN_RSSI
- (adapter->scan_table[i].rssi) >
- best_rssi) {
- best_rssi = SCAN_RSSI(adapter->
- scan_table
- [i].rssi);
- net = i;
- }
- } else {
- if (net == -1)
- net = j;
- }
- break;
- case NL80211_IFTYPE_UNSPECIFIED:
- default:
- /*
- * Do not check compatibility if the mode
- * requested is Auto/Unknown. Allows generic
- * find to work without verifying against the
- * Adapter security settings
- */
- if (SCAN_RSSI(adapter->scan_table[i].rssi) >
- best_rssi) {
- best_rssi = SCAN_RSSI(adapter->
- scan_table[i].rssi);
- net = i;
- }
- break;
- }
- }
- }
-
- return net;
-}
-
-/*
- * This function finds a specific compatible BSSID in the scan list.
- *
- * This function loops through the scan table looking for a compatible
- * match. If a BSSID matches, but the BSS is found to be not compatible
- * the function ignores it and continues to search through the rest of
- * the entries in case there is an AP with multiple SSIDs assigned to
- * the same BSSID.
- */
-s32
-mwifiex_find_bssid_in_list(struct mwifiex_private *priv, u8 *bssid,
- u32 mode)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- s32 net = -1;
- u32 i;
-
- if (!bssid)
- return -1;
-
- dev_dbg(adapter->dev, "info: FindBSSID: Num of BSSIDs = %d\n",
- adapter->num_in_scan_table);
-
- /*
- * Look through the scan table for a compatible match. The ret return
- * variable will be equal to the index in the scan table (greater
- * than zero) if the network is compatible. The loop will continue
- * past a matched bssid that is not compatible in case there is an
- * AP with multiple SSIDs assigned to the same BSSID
- */
- for (i = 0; net < 0 && i < adapter->num_in_scan_table; i++) {
- if (!memcmp
- (adapter->scan_table[i].mac_address, bssid, ETH_ALEN)
- && mwifiex_get_cfp_by_band_and_channel_from_cfg80211
- (priv,
- (u8) adapter->
- scan_table[i].
- bss_band,
- (u16) adapter->
- scan_table[i].
- channel)) {
- switch (mode) {
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- net = mwifiex_is_network_compatible(priv, i,
- mode);
- break;
- default:
- net = i;
- break;
- }
- }
- }
-
- return net;
-}
-
-/*
* This function inserts scan command node to the scan pending queue.
*/
void
@@ -2810,48 +1849,13 @@ mwifiex_queue_scan_cmd(struct mwifiex_private *priv,
unsigned long flags;
cmd_node->wait_q_enabled = true;
+ cmd_node->condition = &adapter->scan_wait_q_woken;
spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
list_add_tail(&cmd_node->list, &adapter->scan_pending_q);
spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
}
/*
- * This function finds an AP with specific ssid in the scan list.
- */
-int mwifiex_find_best_network(struct mwifiex_private *priv,
- struct mwifiex_ssid_bssid *req_ssid_bssid)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_bssdescriptor *req_bss;
- s32 i;
-
- memset(req_ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid));
-
- i = mwifiex_find_best_network_in_list(priv);
-
- if (i >= 0) {
- req_bss = &adapter->scan_table[i];
- memcpy(&req_ssid_bssid->ssid, &req_bss->ssid,
- sizeof(struct mwifiex_802_11_ssid));
- memcpy((u8 *) &req_ssid_bssid->bssid,
- (u8 *) &req_bss->mac_address, ETH_ALEN);
-
- /* Make sure we are in the right mode */
- if (priv->bss_mode == NL80211_IFTYPE_UNSPECIFIED)
- priv->bss_mode = req_bss->bss_mode;
- }
-
- if (!req_ssid_bssid->ssid.ssid_len)
- return -1;
-
- dev_dbg(adapter->dev, "info: Best network found = [%s], "
- "[%pM]\n", req_ssid_bssid->ssid.ssid,
- req_ssid_bssid->bssid);
-
- return 0;
-}
-
-/*
* This function sends a scan command for all available channels to the
* firmware, filtered on a specific SSID.
*/
@@ -2876,8 +1880,6 @@ static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv,
return ret;
}
- mwifiex_scan_delete_ssid_table_entry(priv, req_ssid);
-
scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL);
if (!scan_cfg) {
dev_err(adapter->dev, "failed to alloc scan_cfg\n");
@@ -2886,7 +1888,6 @@ static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv,
memcpy(scan_cfg->ssid_list[0].ssid, req_ssid->ssid,
req_ssid->ssid_len);
- scan_cfg->keep_previous_scan = true;
ret = mwifiex_scan_networks(priv, scan_cfg);
@@ -2915,7 +1916,7 @@ int mwifiex_request_scan(struct mwifiex_private *priv,
}
priv->scan_pending_on_block = true;
- priv->adapter->cmd_wait_q.condition = false;
+ priv->adapter->scan_wait_q_woken = false;
if (req_ssid && req_ssid->ssid_len != 0)
/* Specific SSID scan */
@@ -2999,7 +2000,7 @@ mwifiex_save_curr_bcn(struct mwifiex_private *priv)
priv->curr_bcn_size = curr_bss->beacon_buf_size;
kfree(priv->curr_bcn_buf);
- priv->curr_bcn_buf = kzalloc(curr_bss->beacon_buf_size,
+ priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size,
GFP_KERNEL);
if (!priv->curr_bcn_buf) {
dev_err(priv->adapter->dev,
@@ -3012,6 +2013,39 @@ mwifiex_save_curr_bcn(struct mwifiex_private *priv)
curr_bss->beacon_buf_size);
dev_dbg(priv->adapter->dev, "info: current beacon saved %d\n",
priv->curr_bcn_size);
+
+ curr_bss->beacon_buf = priv->curr_bcn_buf;
+
+ /* adjust the pointers in the current BSS descriptor */
+ if (curr_bss->bcn_wpa_ie)
+ curr_bss->bcn_wpa_ie =
+ (struct ieee_types_vendor_specific *)
+ (curr_bss->beacon_buf +
+ curr_bss->wpa_offset);
+
+ if (curr_bss->bcn_rsn_ie)
+ curr_bss->bcn_rsn_ie = (struct ieee_types_generic *)
+ (curr_bss->beacon_buf +
+ curr_bss->rsn_offset);
+
+ if (curr_bss->bcn_ht_cap)
+ curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *)
+ (curr_bss->beacon_buf +
+ curr_bss->ht_cap_offset);
+
+ if (curr_bss->bcn_ht_info)
+ curr_bss->bcn_ht_info = (struct ieee80211_ht_info *)
+ (curr_bss->beacon_buf +
+ curr_bss->ht_info_offset);
+
+ if (curr_bss->bcn_bss_co_2040)
+ curr_bss->bcn_bss_co_2040 =
+ (u8 *) (curr_bss->beacon_buf +
+ curr_bss->bss_co_2040_offset);
+
+ if (curr_bss->bcn_ext_cap)
+ curr_bss->bcn_ext_cap = (u8 *) (curr_bss->beacon_buf +
+ curr_bss->ext_cap_offset);
}
/*
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index d425dbd91d19..283171bbcedf 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -31,10 +31,27 @@
#define SDIO_VERSION "1.0"
+/* The mwifiex_sdio_remove() callback function is called when
+ * user removes this module from kernel space or ejects
+ * the card from the slot. The driver handles these 2 cases
+ * differently.
+ * If the user is removing the module, the few commands (FUNC_SHUTDOWN,
+ * HS_CANCEL etc.) are sent to the firmware.
+ * If the card is removed, there is no need to send these command.
+ *
+ * The variable 'user_rmmod' is used to distinguish these two
+ * scenarios. This flag is initialized as FALSE in case the card
+ * is removed, and will be set to TRUE for module removal when
+ * module_exit function is called.
+ */
+static u8 user_rmmod;
+
static struct mwifiex_if_ops sdio_ops;
static struct semaphore add_remove_card_sem;
+static int mwifiex_sdio_resume(struct device *dev);
+
/*
* SDIO probe.
*
@@ -72,7 +89,8 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
return -EIO;
}
- if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops)) {
+ if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops,
+ MWIFIEX_SDIO)) {
pr_err("%s: add card failed\n", __func__);
kfree(card);
sdio_claim_host(func);
@@ -93,17 +111,39 @@ static void
mwifiex_sdio_remove(struct sdio_func *func)
{
struct sdio_mmc_card *card;
+ struct mwifiex_adapter *adapter;
+ int i;
pr_debug("info: SDIO func num=%d\n", func->num);
- if (func) {
- card = sdio_get_drvdata(func);
- if (card) {
- mwifiex_remove_card(card->adapter,
- &add_remove_card_sem);
- kfree(card);
- }
+ card = sdio_get_drvdata(func);
+ if (!card)
+ return;
+
+ adapter = card->adapter;
+ if (!adapter || !adapter->priv_num)
+ return;
+
+ if (user_rmmod) {
+ if (adapter->is_suspended)
+ mwifiex_sdio_resume(adapter->dev);
+
+ for (i = 0; i < adapter->priv_num; i++)
+ if ((GET_BSS_ROLE(adapter->priv[i]) ==
+ MWIFIEX_BSS_ROLE_STA) &&
+ adapter->priv[i]->media_connected)
+ mwifiex_deauthenticate(adapter->priv[i], NULL);
+
+ mwifiex_disable_auto_ds(mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_ANY));
+
+ mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_ANY),
+ MWIFIEX_FUNC_SHUTDOWN);
}
+
+ mwifiex_remove_card(card->adapter, &add_remove_card_sem);
+ kfree(card);
}
/*
@@ -791,7 +831,7 @@ done:
* The winner interface is also determined by this function.
*/
static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
- u32 poll_num, int *winner)
+ u32 poll_num)
{
int ret = 0;
u16 firmware_stat;
@@ -803,7 +843,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat);
if (ret)
continue;
- if (firmware_stat == FIRMWARE_READY) {
+ if (firmware_stat == FIRMWARE_READY_SDIO) {
ret = 0;
break;
} else {
@@ -812,15 +852,15 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
}
}
- if (winner && ret) {
+ if (ret) {
if (mwifiex_read_reg
(adapter, CARD_FW_STATUS0_REG, &winner_status))
winner_status = 0;
if (winner_status)
- *winner = 0;
+ adapter->winner = 0;
else
- *winner = 1;
+ adapter->winner = 1;
}
return ret;
}
@@ -1283,7 +1323,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
if (!(card->mp_wr_bitmap &
(1 << card->curr_wr_port))
|| !MP_TX_AGGR_BUF_HAS_ROOM(
- card, next_pkt_len))
+ card, pkt_len + next_pkt_len))
f_send_aggr_buf = 1;
} else {
/* No room in Aggr buf, send it */
@@ -1374,7 +1414,7 @@ tx_curr_single:
* the type. The firmware handles the packets based upon this set type.
*/
static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
- u8 type, u8 *payload, u32 pkt_len,
+ u8 type, struct sk_buff *skb,
struct mwifiex_tx_param *tx_param)
{
struct sdio_mmc_card *card = adapter->card;
@@ -1382,6 +1422,8 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
u32 buf_block_len;
u32 blk_size;
u8 port = CTRL_PORT;
+ u8 *payload = (u8 *)skb->data;
+ u32 pkt_len = skb->len;
/* Allocate buffer and copy payload */
blk_size = MWIFIEX_SDIO_BLOCK_SIZE;
@@ -1531,6 +1573,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
sdio_set_drvdata(func, card);
adapter->dev = &func->dev;
+ strcpy(adapter->fw_name, SD8787_DEFAULT_FW_NAME);
return 0;
@@ -1552,7 +1595,6 @@ disable_func:
* the first interrupt got from bootloader
* - Disable host interrupt mask register
* - Get SDIO port
- * - Get revision ID
* - Initialize SDIO variables in card
* - Allocate MP registers
* - Allocate MPA Tx and Rx buffers
@@ -1576,10 +1618,6 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
/* Get SDIO ioport */
mwifiex_init_sdio_ioport(adapter);
- /* Get revision ID */
-#define REV_ID_REG 0x5c
- mwifiex_read_reg(adapter, REV_ID_REG, &adapter->revision_id);
-
/* Initialize SDIO variables in card */
card->mp_rd_bitmap = 0;
card->mp_wr_bitmap = 0;
@@ -1687,6 +1725,8 @@ static struct mwifiex_if_ops sdio_ops = {
/* SDIO specific */
.update_mp_end_port = mwifiex_update_mp_end_port,
.cleanup_mpa_buf = mwifiex_cleanup_mpa_buf,
+ .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete,
+ .event_complete = mwifiex_sdio_event_complete,
};
/*
@@ -1700,6 +1740,9 @@ mwifiex_sdio_init_module(void)
{
sema_init(&add_remove_card_sem, 1);
+ /* Clear the flag in case user removes the card. */
+ user_rmmod = 0;
+
return sdio_register_driver(&mwifiex_sdio);
}
@@ -1715,32 +1758,12 @@ mwifiex_sdio_init_module(void)
static void
mwifiex_sdio_cleanup_module(void)
{
- struct mwifiex_adapter *adapter = g_adapter;
- int i;
-
- if (down_interruptible(&add_remove_card_sem))
- goto exit_sem_err;
-
- if (!adapter || !adapter->priv_num)
- goto exit;
-
- if (adapter->is_suspended)
- mwifiex_sdio_resume(adapter->dev);
-
- for (i = 0; i < adapter->priv_num; i++)
- if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) &&
- adapter->priv[i]->media_connected)
- mwifiex_deauthenticate(adapter->priv[i], NULL);
-
- if (!adapter->surprise_removed)
- mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter,
- MWIFIEX_BSS_ROLE_ANY),
- MWIFIEX_FUNC_SHUTDOWN);
+ if (!down_interruptible(&add_remove_card_sem))
+ up(&add_remove_card_sem);
-exit:
- up(&add_remove_card_sem);
+ /* Set the flag as user is removing this module. */
+ user_rmmod = 1;
-exit_sem_err:
sdio_unregister_driver(&mwifiex_sdio);
}
@@ -1751,4 +1774,4 @@ MODULE_AUTHOR("Marvell International Ltd.");
MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION);
MODULE_VERSION(SDIO_VERSION);
MODULE_LICENSE("GPL v2");
-MODULE_FIRMWARE("sd8787.bin");
+MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h
index 4e97e90aa399..3f711801e58a 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/mwifiex/sdio.h
@@ -28,6 +28,8 @@
#include "main.h"
+#define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin"
+
#define BLOCK_MODE 1
#define BYTE_MODE 0
@@ -52,10 +54,10 @@
#define SDIO_MP_AGGR_DEF_PKT_LIMIT 8
-#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (4096) /* 4K */
+#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (8192) /* 8K */
/* Multi port RX aggregation buffer size */
-#define SDIO_MP_RX_AGGR_DEF_BUF_SIZE (4096) /* 4K */
+#define SDIO_MP_RX_AGGR_DEF_BUF_SIZE (16384) /* 16K */
/* Misc. Config Register : Auto Re-enable interrupts */
#define AUTO_RE_ENABLE_INT BIT(4)
@@ -167,9 +169,6 @@
/* Rx unit register */
#define CARD_RX_UNIT_REG 0x63
-/* Event header len w/o 4 bytes of interface header */
-#define MWIFIEX_EVENT_HEADER_LEN 4
-
/* Max retry number of CMD53 write */
#define MAX_WRITE_IOMEM_RETRY 2
@@ -302,4 +301,25 @@ struct sdio_mmc_card {
struct mwifiex_sdio_mpa_tx mpa_tx;
struct mwifiex_sdio_mpa_rx mpa_rx;
};
+
+/*
+ * .cmdrsp_complete handler
+ */
+static inline int mwifiex_sdio_cmdrsp_complete(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb)
+{
+ dev_kfree_skb_any(skb);
+ return 0;
+}
+
+/*
+ * .event_complete handler
+ */
+static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb)
+{
+ dev_kfree_skb_any(skb);
+ return 0;
+}
+
#endif /* _MWIFIEX_SDIO_H */
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index 8af3a78d2723..ea6518d1c9e3 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -67,10 +67,9 @@ mwifiex_cmd_802_11_rssi_info(struct mwifiex_private *priv,
*/
static int mwifiex_cmd_mac_control(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
- u16 cmd_action, void *data_buf)
+ u16 cmd_action, u16 *action)
{
struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl;
- u16 action = *((u16 *) data_buf);
if (cmd_action != HostCmd_ACT_GEN_SET) {
dev_err(priv->adapter->dev,
@@ -81,7 +80,7 @@ static int mwifiex_cmd_mac_control(struct mwifiex_private *priv,
cmd->command = cpu_to_le16(HostCmd_CMD_MAC_CONTROL);
cmd->size =
cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN);
- mac_ctrl->action = cpu_to_le16(action);
+ mac_ctrl->action = cpu_to_le16(*action);
return 0;
}
@@ -104,10 +103,9 @@ static int mwifiex_cmd_mac_control(struct mwifiex_private *priv,
static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
u16 cmd_action, u32 cmd_oid,
- void *data_buf)
+ u32 *ul_temp)
{
struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib;
- u32 ul_temp;
dev_dbg(priv->adapter->dev, "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid);
cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB);
@@ -127,9 +125,8 @@ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv,
if (cmd_action == HostCmd_ACT_GEN_SET) {
snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET);
snmp_mib->buf_size = cpu_to_le16(sizeof(u16));
- ul_temp = *((u32 *) data_buf);
*((__le16 *) (snmp_mib->value)) =
- cpu_to_le16((u16) ul_temp);
+ cpu_to_le16((u16) *ul_temp);
cmd->size = cpu_to_le16(le16_to_cpu(cmd->size)
+ sizeof(u16));
}
@@ -139,9 +136,8 @@ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv,
if (cmd_action == HostCmd_ACT_GEN_SET) {
snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET);
snmp_mib->buf_size = cpu_to_le16(sizeof(u16));
- ul_temp = *((u32 *) data_buf);
*(__le16 *) (snmp_mib->value) =
- cpu_to_le16((u16) ul_temp);
+ cpu_to_le16((u16) *ul_temp);
cmd->size = cpu_to_le16(le16_to_cpu(cmd->size)
+ sizeof(u16));
}
@@ -152,9 +148,8 @@ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv,
if (cmd_action == HostCmd_ACT_GEN_SET) {
snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET);
snmp_mib->buf_size = cpu_to_le16(sizeof(u16));
- ul_temp = (*(u32 *) data_buf);
*((__le16 *) (snmp_mib->value)) =
- cpu_to_le16((u16) ul_temp);
+ cpu_to_le16((u16) *ul_temp);
cmd->size = cpu_to_le16(le16_to_cpu(cmd->size)
+ sizeof(u16));
}
@@ -164,9 +159,8 @@ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv,
if (cmd_action == HostCmd_ACT_GEN_SET) {
snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET);
snmp_mib->buf_size = cpu_to_le16(sizeof(u16));
- ul_temp = *(u32 *) data_buf;
*((__le16 *) (snmp_mib->value)) =
- cpu_to_le16((u16) ul_temp);
+ cpu_to_le16((u16) *ul_temp);
cmd->size = cpu_to_le16(le16_to_cpu(cmd->size)
+ sizeof(u16));
}
@@ -209,13 +203,11 @@ mwifiex_cmd_802_11_get_log(struct host_cmd_ds_command *cmd)
*/
static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
- u16 cmd_action, void *data_buf)
+ u16 cmd_action, u16 *pbitmap_rates)
{
struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg;
struct mwifiex_rate_scope *rate_scope;
struct mwifiex_rate_drop_pattern *rate_drop;
- u16 *pbitmap_rates = (u16 *) data_buf;
-
u32 i;
cmd->command = cpu_to_le16(HostCmd_CMD_TX_RATE_CFG);
@@ -272,10 +264,10 @@ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv,
* - Ensuring correct endian-ness
*/
static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd,
- u16 cmd_action, void *data_buf)
+ u16 cmd_action,
+ struct host_cmd_ds_txpwr_cfg *txp)
{
struct mwifiex_types_power_group *pg_tlv;
- struct host_cmd_ds_txpwr_cfg *txp;
struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg;
cmd->command = cpu_to_le16(HostCmd_CMD_TXPWR_CFG);
@@ -283,12 +275,11 @@ static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd,
cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg));
switch (cmd_action) {
case HostCmd_ACT_GEN_SET:
- txp = (struct host_cmd_ds_txpwr_cfg *) data_buf;
if (txp->mode) {
pg_tlv = (struct mwifiex_types_power_group
- *) ((unsigned long) data_buf +
+ *) ((unsigned long) txp +
sizeof(struct host_cmd_ds_txpwr_cfg));
- memmove(cmd_txp_cfg, data_buf,
+ memmove(cmd_txp_cfg, txp,
sizeof(struct host_cmd_ds_txpwr_cfg) +
sizeof(struct mwifiex_types_power_group) +
pg_tlv->length);
@@ -300,8 +291,7 @@ static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd,
sizeof(struct mwifiex_types_power_group) +
pg_tlv->length);
} else {
- memmove(cmd_txp_cfg, data_buf,
- sizeof(struct host_cmd_ds_txpwr_cfg));
+ memmove(cmd_txp_cfg, txp, sizeof(*txp));
}
cmd_txp_cfg->action = cpu_to_le16(cmd_action);
break;
@@ -322,22 +312,23 @@ static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd,
* (as required)
* - Ensuring correct endian-ness
*/
-static int mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- u16 cmd_action,
- struct mwifiex_hs_config_param *data_buf)
+static int
+mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action,
+ struct mwifiex_hs_config_param *hscfg_param)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg;
u16 hs_activate = false;
- if (data_buf == NULL)
+ if (!hscfg_param)
/* New Activate command */
hs_activate = true;
cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH);
if (!hs_activate &&
- (data_buf->conditions
+ (hscfg_param->conditions
!= cpu_to_le32(HOST_SLEEP_CFG_CANCEL))
&& ((adapter->arp_filter_size > 0)
&& (adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) {
@@ -359,9 +350,9 @@ static int mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv,
hs_cfg->params.hs_activate.resp_ctrl = RESP_NEEDED;
} else {
hs_cfg->action = cpu_to_le16(HS_CONFIGURE);
- hs_cfg->params.hs_config.conditions = data_buf->conditions;
- hs_cfg->params.hs_config.gpio = data_buf->gpio;
- hs_cfg->params.hs_config.gap = data_buf->gap;
+ hs_cfg->params.hs_config.conditions = hscfg_param->conditions;
+ hs_cfg->params.hs_config.gpio = hscfg_param->gpio;
+ hs_cfg->params.hs_config.gap = hscfg_param->gap;
dev_dbg(adapter->dev,
"cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n",
hs_cfg->params.hs_config.conditions,
@@ -405,11 +396,11 @@ static int mwifiex_cmd_802_11_mac_address(struct mwifiex_private *priv,
* - Setting MAC multicast address
* - Ensuring correct endian-ness
*/
-static int mwifiex_cmd_mac_multicast_adr(struct host_cmd_ds_command *cmd,
- u16 cmd_action, void *data_buf)
+static int
+mwifiex_cmd_mac_multicast_adr(struct host_cmd_ds_command *cmd,
+ u16 cmd_action,
+ struct mwifiex_multicast_list *mcast_list)
{
- struct mwifiex_multicast_list *mcast_list =
- (struct mwifiex_multicast_list *) data_buf;
struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr;
cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) +
@@ -435,7 +426,7 @@ static int mwifiex_cmd_mac_multicast_adr(struct host_cmd_ds_command *cmd,
*/
static int mwifiex_cmd_802_11_deauthenticate(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
- void *data_buf)
+ u8 *mac)
{
struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth;
@@ -444,7 +435,7 @@ static int mwifiex_cmd_802_11_deauthenticate(struct mwifiex_private *priv,
+ S_DS_GEN);
/* Set AP MAC address */
- memcpy(deauth->mac_addr, (u8 *) data_buf, ETH_ALEN);
+ memcpy(deauth->mac_addr, mac, ETH_ALEN);
dev_dbg(priv->adapter->dev, "cmd: Deauth: %pM\n", deauth->mac_addr);
@@ -543,15 +534,14 @@ mwifiex_set_keyparamset_wep(struct mwifiex_private *priv,
* encryption (TKIP, AES) (as required)
* - Ensuring correct endian-ness
*/
-static int mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- u16 cmd_action,
- u32 cmd_oid, void *data_buf)
+static int
+mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action, u32 cmd_oid,
+ struct mwifiex_ds_encrypt_key *enc_key)
{
struct host_cmd_ds_802_11_key_material *key_material =
&cmd->params.key_material;
- struct mwifiex_ds_encrypt_key *enc_key =
- (struct mwifiex_ds_encrypt_key *) data_buf;
u16 key_param_len = 0;
int ret = 0;
const u8 bc_mac[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -741,7 +731,7 @@ static int mwifiex_cmd_802_11d_domain_info(struct mwifiex_private *priv,
*/
static int mwifiex_cmd_802_11_rf_channel(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
- u16 cmd_action, void *data_buf)
+ u16 cmd_action, u16 *channel)
{
struct host_cmd_ds_802_11_rf_channel *rf_chan =
&cmd->params.rf_channel;
@@ -759,7 +749,7 @@ static int mwifiex_cmd_802_11_rf_channel(struct mwifiex_private *priv,
rf_type = le16_to_cpu(rf_chan->rf_type);
SET_SECONDARYCHAN(rf_type, priv->adapter->chan_offset);
- rf_chan->current_channel = cpu_to_le16(*((u16 *) data_buf));
+ rf_chan->current_channel = cpu_to_le16(*channel);
}
rf_chan->action = cpu_to_le16(cmd_action);
return 0;
@@ -774,11 +764,10 @@ static int mwifiex_cmd_802_11_rf_channel(struct mwifiex_private *priv,
* - Ensuring correct endian-ness
*/
static int mwifiex_cmd_ibss_coalescing_status(struct host_cmd_ds_command *cmd,
- u16 cmd_action, void *data_buf)
+ u16 cmd_action, u16 *enable)
{
struct host_cmd_ds_802_11_ibss_status *ibss_coal =
&(cmd->params.ibss_coalescing);
- u16 enable = 0;
cmd->command = cpu_to_le16(HostCmd_CMD_802_11_IBSS_COALESCING_STATUS);
cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_ibss_status) +
@@ -788,9 +777,10 @@ static int mwifiex_cmd_ibss_coalescing_status(struct host_cmd_ds_command *cmd,
switch (cmd_action) {
case HostCmd_ACT_GEN_SET:
- if (data_buf != NULL)
- enable = *(u16 *) data_buf;
- ibss_coal->enable = cpu_to_le16(enable);
+ if (enable)
+ ibss_coal->enable = cpu_to_le16(*enable);
+ else
+ ibss_coal->enable = 0;
break;
/* In other case.. Nothing to do */
@@ -822,9 +812,8 @@ static int mwifiex_cmd_ibss_coalescing_status(struct host_cmd_ds_command *cmd,
static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd,
u16 cmd_action, void *data_buf)
{
- struct mwifiex_ds_reg_rw *reg_rw;
+ struct mwifiex_ds_reg_rw *reg_rw = data_buf;
- reg_rw = (struct mwifiex_ds_reg_rw *) data_buf;
switch (le16_to_cpu(cmd->command)) {
case HostCmd_CMD_MAC_REG_ACCESS:
{
@@ -893,8 +882,7 @@ static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd,
}
case HostCmd_CMD_802_11_EEPROM_ACCESS:
{
- struct mwifiex_ds_read_eeprom *rd_eeprom =
- (struct mwifiex_ds_read_eeprom *) data_buf;
+ struct mwifiex_ds_read_eeprom *rd_eeprom = data_buf;
struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom =
(struct host_cmd_ds_802_11_eeprom_access *)
&cmd->params.eeprom;
@@ -914,6 +902,59 @@ static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd,
}
/*
+ * This function prepares command to set PCI-Express
+ * host buffer configuration
+ *
+ * Preparation includes -
+ * - Setting command ID, action and proper size
+ * - Setting host buffer configuration
+ * - Ensuring correct endian-ness
+ */
+static int
+mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd, u16 action)
+{
+ struct host_cmd_ds_pcie_details *host_spec =
+ &cmd->params.pcie_host_spec;
+ struct pcie_service_card *card = priv->adapter->card;
+ phys_addr_t *buf_pa;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_PCIE_DESC_DETAILS);
+ cmd->size = cpu_to_le16(sizeof(struct
+ host_cmd_ds_pcie_details) + S_DS_GEN);
+ cmd->result = 0;
+
+ memset(host_spec, 0, sizeof(struct host_cmd_ds_pcie_details));
+
+ if (action == HostCmd_ACT_GEN_SET) {
+ /* Send the ring base addresses and count to firmware */
+ host_spec->txbd_addr_lo = (u32)(card->txbd_ring_pbase);
+ host_spec->txbd_addr_hi =
+ (u32)(((u64)card->txbd_ring_pbase)>>32);
+ host_spec->txbd_count = MWIFIEX_MAX_TXRX_BD;
+ host_spec->rxbd_addr_lo = (u32)(card->rxbd_ring_pbase);
+ host_spec->rxbd_addr_hi =
+ (u32)(((u64)card->rxbd_ring_pbase)>>32);
+ host_spec->rxbd_count = MWIFIEX_MAX_TXRX_BD;
+ host_spec->evtbd_addr_lo =
+ (u32)(card->evtbd_ring_pbase);
+ host_spec->evtbd_addr_hi =
+ (u32)(((u64)card->evtbd_ring_pbase)>>32);
+ host_spec->evtbd_count = MWIFIEX_MAX_EVT_BD;
+ if (card->sleep_cookie) {
+ buf_pa = MWIFIEX_SKB_PACB(card->sleep_cookie);
+ host_spec->sleep_cookie_addr_lo = (u32) *buf_pa;
+ host_spec->sleep_cookie_addr_hi =
+ (u32) (((u64)*buf_pa) >> 32);
+ dev_dbg(priv->adapter->dev, "sleep_cook_lo phy addr: "
+ "0x%x\n", host_spec->sleep_cookie_addr_lo);
+ }
+ }
+
+ return 0;
+}
+
+/*
* This function prepares the commands before sending them to the firmware.
*
* This is a generic function which calls specific command preparation
@@ -923,8 +964,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
u16 cmd_action, u32 cmd_oid,
void *data_buf, void *cmd_buf)
{
- struct host_cmd_ds_command *cmd_ptr =
- (struct host_cmd_ds_command *) cmd_buf;
+ struct host_cmd_ds_command *cmd_ptr = cmd_buf;
int ret = 0;
/* Prepare command */
@@ -1092,6 +1132,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
host_cmd_ds_set_bss_mode) + S_DS_GEN);
ret = 0;
break;
+ case HostCmd_CMD_PCIE_DESC_DETAILS:
+ ret = mwifiex_cmd_pcie_host_spec(priv, cmd_ptr, cmd_action);
+ break;
default:
dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd- %#x\n", cmd_no);
@@ -1108,6 +1151,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
* working state.
*
* The following commands are issued sequentially -
+ * - Set PCI-Express host buffer configuration (PCIE only)
* - Function init (for first interface only)
* - Read MAC address (for first interface only)
* - Reconfigure Tx buffer size (for first interface only)
@@ -1126,8 +1170,16 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl;
struct mwifiex_ds_auto_ds auto_ds;
enum state_11d_t state_11d;
+ struct mwifiex_ds_11n_tx_cfg tx_cfg;
if (first_sta) {
+ if (priv->adapter->iface_type == MWIFIEX_PCIE) {
+ ret = mwifiex_send_cmd_async(priv,
+ HostCmd_CMD_PCIE_DESC_DETAILS,
+ HostCmd_ACT_GEN_SET, 0, NULL);
+ if (ret)
+ return -1;
+ }
ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_FUNC_INIT,
HostCmd_ACT_GEN_SET, 0, NULL);
@@ -1181,7 +1233,7 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
/* Send request to firmware */
ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_AMSDU_AGGR_CTRL,
HostCmd_ACT_GEN_SET, 0,
- (void *) &amsdu_aggr_ctrl);
+ &amsdu_aggr_ctrl);
if (ret)
return -1;
/* MAC Control must be the last command in init_fw */
@@ -1211,8 +1263,15 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
if (ret)
dev_err(priv->adapter->dev, "11D: failed to enable 11D\n");
+ /* Send cmd to FW to configure 11n specific configuration
+ * (Short GI, Channel BW, Green field support etc.) for transmit
+ */
+ tx_cfg.tx_htcap = MWIFIEX_FW_DEF_HTTXCFG;
+ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_CFG,
+ HostCmd_ACT_GEN_SET, 0, &tx_cfg);
+
/* set last_init_cmd */
- priv->adapter->last_init_cmd = HostCmd_CMD_802_11_SNMP_MIB;
+ priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG;
ret = -EINPROGRESS;
return ret;
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index d08f76429a0a..7a16b0c417af 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -120,11 +120,10 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv,
*/
static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
- void *data_buf)
+ struct mwifiex_ds_get_signal *signal)
{
struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp =
&resp->params.rssi_info_rsp;
- struct mwifiex_ds_get_signal *signal;
priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last);
priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last);
@@ -139,9 +138,8 @@ static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv,
priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg);
/* Need to indicate IOCTL complete */
- if (data_buf) {
- signal = (struct mwifiex_ds_get_signal *) data_buf;
- memset(signal, 0, sizeof(struct mwifiex_ds_get_signal));
+ if (signal) {
+ memset(signal, 0, sizeof(*signal));
signal->selector = ALL_RSSI_INFO_MASK;
@@ -185,7 +183,7 @@ static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv,
*/
static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
- void *data_buf)
+ u32 *data_buf)
{
struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib;
u16 oid = le16_to_cpu(smib->oid);
@@ -198,7 +196,7 @@ static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv,
if (query_type == HostCmd_ACT_GEN_GET) {
ul_temp = le16_to_cpu(*((__le16 *) (smib->value)));
if (data_buf)
- *(u32 *)data_buf = ul_temp;
+ *data_buf = ul_temp;
switch (oid) {
case FRAG_THRESH_I:
dev_dbg(priv->adapter->dev,
@@ -228,14 +226,12 @@ static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv,
*/
static int mwifiex_ret_get_log(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
- void *data_buf)
+ struct mwifiex_ds_get_stats *stats)
{
struct host_cmd_ds_802_11_get_log *get_log =
(struct host_cmd_ds_802_11_get_log *) &resp->params.get_log;
- struct mwifiex_ds_get_stats *stats;
- if (data_buf) {
- stats = (struct mwifiex_ds_get_stats *) data_buf;
+ if (stats) {
stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame);
stats->failed = le32_to_cpu(get_log->failed);
stats->retry = le32_to_cpu(get_log->retry);
@@ -278,9 +274,8 @@ static int mwifiex_ret_get_log(struct mwifiex_private *priv,
*/
static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
- void *data_buf)
+ struct mwifiex_rate_cfg *ds_rate)
{
- struct mwifiex_rate_cfg *ds_rate;
struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg;
struct mwifiex_rate_scope *rate_scope;
struct mwifiex_ie_types_header *head;
@@ -329,8 +324,7 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
HostCmd_CMD_802_11_TX_RATE_QUERY,
HostCmd_ACT_GEN_GET, 0, NULL);
- if (data_buf) {
- ds_rate = (struct mwifiex_rate_cfg *) data_buf;
+ if (ds_rate) {
if (le16_to_cpu(rate_cfg->action) == HostCmd_ACT_GEN_GET) {
if (priv->is_data_rate_auto) {
ds_rate->is_rate_auto = 1;
@@ -413,8 +407,7 @@ static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf)
* and saving the current Tx power level in driver.
*/
static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp,
- void *data_buf)
+ struct host_cmd_ds_command *resp)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg;
@@ -631,7 +624,7 @@ static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv,
*/
static int mwifiex_ret_802_11_rf_channel(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
- void *data_buf)
+ u16 *data_buf)
{
struct host_cmd_ds_802_11_rf_channel *rf_channel =
&resp->params.rf_channel;
@@ -644,8 +637,9 @@ static int mwifiex_ret_802_11_rf_channel(struct mwifiex_private *priv,
/* Update the channel again */
priv->curr_bss_params.bss_descriptor.channel = new_channel;
}
+
if (data_buf)
- *((u16 *)data_buf) = new_channel;
+ *data_buf = new_channel;
return 0;
}
@@ -658,13 +652,11 @@ static int mwifiex_ret_802_11_rf_channel(struct mwifiex_private *priv,
*/
static int mwifiex_ret_ver_ext(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
- void *data_buf)
+ struct host_cmd_ds_version_ext *version_ext)
{
struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext;
- struct host_cmd_ds_version_ext *version_ext;
- if (data_buf) {
- version_ext = (struct host_cmd_ds_version_ext *)data_buf;
+ if (version_ext) {
version_ext->version_str_sel = ver_ext->version_str_sel;
memcpy(version_ext->version_str, ver_ext->version_str,
sizeof(char) * 128);
@@ -686,8 +678,8 @@ static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp,
struct mwifiex_ds_read_eeprom *eeprom;
if (data_buf) {
- reg_rw = (struct mwifiex_ds_reg_rw *) data_buf;
- eeprom = (struct mwifiex_ds_read_eeprom *) data_buf;
+ reg_rw = data_buf;
+ eeprom = data_buf;
switch (type) {
case HostCmd_CMD_MAC_REG_ACCESS:
{
@@ -825,13 +817,11 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv,
* This is a generic function, which calls command specific
* response handlers based on the command ID.
*/
-int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv,
- u16 cmdresp_no, void *cmd_buf)
+int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
+ struct host_cmd_ds_command *resp)
{
int ret = 0;
struct mwifiex_adapter *adapter = priv->adapter;
- struct host_cmd_ds_command *resp =
- (struct host_cmd_ds_command *) cmd_buf;
void *data_buf = adapter->curr_cmd->data_buf;
/* If the command is not successful, cleanup and return failure */
@@ -865,7 +855,7 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv,
"info: CMD_RESP: BG_SCAN result is ready!\n");
break;
case HostCmd_CMD_TXPWR_CFG:
- ret = mwifiex_ret_tx_power_cfg(priv, resp, data_buf);
+ ret = mwifiex_ret_tx_power_cfg(priv, resp);
break;
case HostCmd_CMD_802_11_PS_MODE_ENH:
ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf);
@@ -962,6 +952,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv,
case HostCmd_CMD_11N_CFG:
ret = mwifiex_ret_11n_cfg(resp, data_buf);
break;
+ case HostCmd_CMD_PCIE_DESC_DETAILS:
+ break;
default:
dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
resp->command);
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index fc265cab0907..f204810e8338 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -130,8 +130,8 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv)
if (netif_carrier_ok(priv->netdev))
netif_carrier_off(priv->netdev);
/* Reset wireless stats signal info */
- priv->w_stats.qual.level = 0;
- priv->w_stats.qual.noise = 0;
+ priv->qual_level = 0;
+ priv->qual_noise = 0;
}
/*
@@ -299,11 +299,6 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
case EVENT_BG_SCAN_REPORT:
dev_dbg(adapter->dev, "event: BGS_REPORT\n");
- /* Clear the previous scan result */
- memset(adapter->scan_table, 0x00,
- sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP);
- adapter->num_in_scan_table = 0;
- adapter->bcn_buf_end = adapter->bcn_buf;
ret = mwifiex_send_cmd_async(priv,
HostCmd_CMD_802_11_BG_SCAN_QUERY,
HostCmd_ACT_GEN_GET, 0, NULL);
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index d05907d05039..ea4a29b7e331 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -55,7 +55,9 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)
{
bool cancel_flag = false;
int status = adapter->cmd_wait_q.status;
+ struct cmd_ctrl_node *cmd_queued = adapter->cmd_queued;
+ adapter->cmd_queued = NULL;
dev_dbg(adapter->dev, "cmd pending\n");
atomic_inc(&adapter->cmd_pending);
@@ -64,8 +66,8 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)
/* Wait for completion */
wait_event_interruptible(adapter->cmd_wait_q.wait,
- adapter->cmd_wait_q.condition);
- if (!adapter->cmd_wait_q.condition)
+ *(cmd_queued->condition));
+ if (!*(cmd_queued->condition))
cancel_flag = true;
if (cancel_flag) {
@@ -142,90 +144,146 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
}
/*
+ * This function fills bss descriptor structure using provided
+ * information.
+ */
+int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
+ u8 *bssid, s32 rssi, u8 *ie_buf,
+ size_t ie_len, u16 beacon_period,
+ u16 cap_info_bitmap, u8 band,
+ struct mwifiex_bssdescriptor *bss_desc)
+{
+ int ret;
+
+ memcpy(bss_desc->mac_address, bssid, ETH_ALEN);
+ bss_desc->rssi = rssi;
+ bss_desc->beacon_buf = ie_buf;
+ bss_desc->beacon_buf_size = ie_len;
+ bss_desc->beacon_period = beacon_period;
+ bss_desc->cap_info_bitmap = cap_info_bitmap;
+ bss_desc->bss_band = band;
+ if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
+ dev_dbg(priv->adapter->dev, "info: InterpretIE: AP WEP enabled\n");
+ bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP;
+ } else {
+ bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL;
+ }
+ if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS)
+ bss_desc->bss_mode = NL80211_IFTYPE_ADHOC;
+ else
+ bss_desc->bss_mode = NL80211_IFTYPE_STATION;
+
+ ret = mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc,
+ ie_buf, ie_len);
+
+ return ret;
+}
+
+/*
* In Ad-Hoc mode, the IBSS is created if not found in scan list.
* In both Ad-Hoc and infra mode, an deauthentication is performed
* first.
*/
-int mwifiex_bss_start(struct mwifiex_private *priv,
- struct mwifiex_ssid_bssid *ssid_bssid)
+int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
+ struct mwifiex_802_11_ssid *req_ssid)
{
int ret;
struct mwifiex_adapter *adapter = priv->adapter;
- s32 i = -1;
+ struct mwifiex_bssdescriptor *bss_desc = NULL;
+ u8 *beacon_ie = NULL;
priv->scan_block = false;
- if (!ssid_bssid)
- return -1;
+
+ if (bss) {
+ /* Allocate and fill new bss descriptor */
+ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor),
+ GFP_KERNEL);
+ if (!bss_desc) {
+ dev_err(priv->adapter->dev, " failed to alloc bss_desc\n");
+ return -ENOMEM;
+ }
+
+ beacon_ie = kmemdup(bss->information_elements,
+ bss->len_beacon_ies, GFP_KERNEL);
+ if (!beacon_ie) {
+ kfree(bss_desc);
+ dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n");
+ return -ENOMEM;
+ }
+
+ ret = mwifiex_fill_new_bss_desc(priv, bss->bssid, bss->signal,
+ beacon_ie, bss->len_beacon_ies,
+ bss->beacon_interval,
+ bss->capability,
+ *(u8 *)bss->priv, bss_desc);
+ if (ret)
+ goto done;
+ }
if (priv->bss_mode == NL80211_IFTYPE_STATION) {
/* Infra mode */
ret = mwifiex_deauthenticate(priv, NULL);
if (ret)
- return ret;
+ goto done;
- /* Search for the requested SSID in the scan table */
- if (ssid_bssid->ssid.ssid_len)
- i = mwifiex_find_ssid_in_list(priv, &ssid_bssid->ssid,
- NULL, NL80211_IFTYPE_STATION);
- else
- i = mwifiex_find_bssid_in_list(priv,
- (u8 *) &ssid_bssid->bssid,
- NL80211_IFTYPE_STATION);
- if (i < 0)
- return -1;
+ ret = mwifiex_check_network_compatibility(priv, bss_desc);
+ if (ret)
+ goto done;
- dev_dbg(adapter->dev,
- "info: SSID found in scan list ... associating...\n");
+ dev_dbg(adapter->dev, "info: SSID found in scan list ... "
+ "associating...\n");
+
+ if (!netif_queue_stopped(priv->netdev))
+ netif_stop_queue(priv->netdev);
/* Clear any past association response stored for
* application retrieval */
priv->assoc_rsp_size = 0;
- ret = mwifiex_associate(priv, &adapter->scan_table[i]);
- if (ret)
- return ret;
+ ret = mwifiex_associate(priv, bss_desc);
+ if (bss)
+ cfg80211_put_bss(bss);
} else {
/* Adhoc mode */
/* If the requested SSID matches current SSID, return */
- if (ssid_bssid->ssid.ssid_len &&
+ if (bss_desc && bss_desc->ssid.ssid_len &&
(!mwifiex_ssid_cmp
(&priv->curr_bss_params.bss_descriptor.ssid,
- &ssid_bssid->ssid)))
+ &bss_desc->ssid))) {
+ kfree(bss_desc);
+ kfree(beacon_ie);
return 0;
+ }
/* Exit Adhoc mode first */
dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n");
ret = mwifiex_deauthenticate(priv, NULL);
if (ret)
- return ret;
+ goto done;
priv->adhoc_is_link_sensed = false;
- /* Search for the requested network in the scan table */
- if (ssid_bssid->ssid.ssid_len)
- i = mwifiex_find_ssid_in_list(priv,
- &ssid_bssid->ssid, NULL,
- NL80211_IFTYPE_ADHOC);
- else
- i = mwifiex_find_bssid_in_list(priv,
- (u8 *)&ssid_bssid->bssid,
- NL80211_IFTYPE_ADHOC);
+ ret = mwifiex_check_network_compatibility(priv, bss_desc);
- if (i >= 0) {
+ if (!netif_queue_stopped(priv->netdev))
+ netif_stop_queue(priv->netdev);
+
+ if (!ret) {
dev_dbg(adapter->dev, "info: network found in scan"
" list. Joining...\n");
- ret = mwifiex_adhoc_join(priv, &adapter->scan_table[i]);
- if (ret)
- return ret;
+ ret = mwifiex_adhoc_join(priv, bss_desc);
+ if (bss)
+ cfg80211_put_bss(bss);
} else {
dev_dbg(adapter->dev, "info: Network not found in "
"the list, creating adhoc with ssid = %s\n",
- ssid_bssid->ssid.ssid);
- ret = mwifiex_adhoc_start(priv, &ssid_bssid->ssid);
- if (ret)
- return ret;
+ req_ssid->ssid);
+ ret = mwifiex_adhoc_start(priv, req_ssid);
}
}
+done:
+ kfree(bss_desc);
+ kfree(beacon_ie);
return ret;
}
@@ -235,8 +293,8 @@ int mwifiex_bss_start(struct mwifiex_private *priv,
* This function prepares the correct firmware command and
* issues it.
*/
-int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
- int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
+static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
+ int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
{
struct mwifiex_adapter *adapter = priv->adapter;
@@ -376,7 +434,6 @@ int mwifiex_get_bss_info(struct mwifiex_private *priv,
{
struct mwifiex_adapter *adapter = priv->adapter;
struct mwifiex_bssdescriptor *bss_desc;
- s32 tbl_idx;
if (!info)
return -1;
@@ -394,17 +451,6 @@ int mwifiex_get_bss_info(struct mwifiex_private *priv,
info->region_code = adapter->region_code;
- /* Scan table index if connected */
- info->scan_table_idx = 0;
- if (priv->media_connected) {
- tbl_idx =
- mwifiex_find_ssid_in_list(priv, &bss_desc->ssid,
- bss_desc->mac_address,
- priv->bss_mode);
- if (tbl_idx >= 0)
- info->scan_table_idx = tbl_idx;
- }
-
info->media_connected = priv->media_connected;
info->max_power_level = priv->max_tx_power_level;
@@ -487,6 +533,20 @@ int mwifiex_set_radio_band_cfg(struct mwifiex_private *priv,
}
/*
+ * The function disables auto deep sleep mode.
+ */
+int mwifiex_disable_auto_ds(struct mwifiex_private *priv)
+{
+ struct mwifiex_ds_auto_ds auto_ds;
+
+ auto_ds.auto_ds = DEEP_SLEEP_OFF;
+
+ return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
+ DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds);
+}
+EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds);
+
+/*
* IOCTL request handler to set/get active channel.
*
* This function performs validity checking on channel/frequency
@@ -572,50 +632,6 @@ static int mwifiex_bss_ioctl_ibss_channel(struct mwifiex_private *priv,
}
/*
- * IOCTL request handler to find a particular BSS.
- *
- * The BSS can be searched with either a BSSID or a SSID. If none of
- * these are provided, just the best BSS (best RSSI) is returned.
- */
-int mwifiex_bss_ioctl_find_bss(struct mwifiex_private *priv,
- struct mwifiex_ssid_bssid *ssid_bssid)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_bssdescriptor *bss_desc;
- u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
- u8 mac[ETH_ALEN];
- int i = 0;
-
- if (memcmp(ssid_bssid->bssid, zero_mac, sizeof(zero_mac))) {
- i = mwifiex_find_bssid_in_list(priv,
- (u8 *) ssid_bssid->bssid,
- priv->bss_mode);
- if (i < 0) {
- memcpy(mac, ssid_bssid->bssid, sizeof(mac));
- dev_err(adapter->dev, "cannot find bssid %pM\n", mac);
- return -1;
- }
- bss_desc = &adapter->scan_table[i];
- memcpy(&ssid_bssid->ssid, &bss_desc->ssid,
- sizeof(struct mwifiex_802_11_ssid));
- } else if (ssid_bssid->ssid.ssid_len) {
- i = mwifiex_find_ssid_in_list(priv, &ssid_bssid->ssid, NULL,
- priv->bss_mode);
- if (i < 0) {
- dev_err(adapter->dev, "cannot find ssid %s\n",
- ssid_bssid->ssid.ssid);
- return -1;
- }
- bss_desc = &adapter->scan_table[i];
- memcpy(ssid_bssid->bssid, bss_desc->mac_address, ETH_ALEN);
- } else {
- return mwifiex_find_best_network(priv, ssid_bssid);
- }
-
- return 0;
-}
-
-/*
* IOCTL request handler to change Ad-Hoc channel.
*
* This function allocates the IOCTL request buffer, fills it
@@ -639,6 +655,9 @@ mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, int channel)
struct mwifiex_bss_info bss_info;
struct mwifiex_ssid_bssid ssid_bssid;
u16 curr_chan = 0;
+ struct cfg80211_bss *bss = NULL;
+ struct ieee80211_channel *chan;
+ enum ieee80211_band band;
memset(&bss_info, 0, sizeof(bss_info));
@@ -674,12 +693,20 @@ mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, int channel)
ret = -1;
goto done;
}
- /* Start/Join Adhoc network */
- memset(&ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid));
- memcpy(&ssid_bssid.ssid, &bss_info.ssid,
- sizeof(struct mwifiex_802_11_ssid));
- ret = mwifiex_bss_start(priv, &ssid_bssid);
+ band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+ chan = __ieee80211_get_channel(priv->wdev->wiphy,
+ ieee80211_channel_to_frequency(channel, band));
+
+ /* Find the BSS we want using available scan results */
+ bss = cfg80211_get_bss(priv->wdev->wiphy, chan, bss_info.bssid,
+ bss_info.ssid.ssid, bss_info.ssid.ssid_len,
+ WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+ if (!bss)
+ wiphy_warn(priv->wdev->wiphy, "assoc: bss %pM not in scan results\n",
+ bss_info.bssid);
+
+ ret = mwifiex_bss_start(priv, bss, &bss_info.ssid);
done:
return ret;
}
@@ -695,51 +722,9 @@ done:
static int mwifiex_rate_ioctl_get_rate_value(struct mwifiex_private *priv,
struct mwifiex_rate_cfg *rate_cfg)
{
- struct mwifiex_adapter *adapter = priv->adapter;
-
rate_cfg->is_rate_auto = priv->is_data_rate_auto;
- if (!priv->media_connected) {
- switch (adapter->config_bands) {
- case BAND_B:
- /* Return the lowest supported rate for B band */
- rate_cfg->rate = supported_rates_b[0] & 0x7f;
- break;
- case BAND_G:
- case BAND_G | BAND_GN:
- /* Return the lowest supported rate for G band */
- rate_cfg->rate = supported_rates_g[0] & 0x7f;
- break;
- case BAND_B | BAND_G:
- case BAND_A | BAND_B | BAND_G:
- case BAND_A | BAND_B:
- case BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN:
- case BAND_B | BAND_G | BAND_GN:
- /* Return the lowest supported rate for BG band */
- rate_cfg->rate = supported_rates_bg[0] & 0x7f;
- break;
- case BAND_A:
- case BAND_A | BAND_G:
- case BAND_A | BAND_G | BAND_AN | BAND_GN:
- case BAND_A | BAND_AN:
- /* Return the lowest supported rate for A band */
- rate_cfg->rate = supported_rates_a[0] & 0x7f;
- break;
- case BAND_GN:
- /* Return the lowest supported rate for N band */
- rate_cfg->rate = supported_rates_n[0] & 0x7f;
- break;
- default:
- dev_warn(adapter->dev, "invalid band %#x\n",
- adapter->config_bands);
- break;
- }
- } else {
- return mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_802_11_TX_RATE_QUERY,
- HostCmd_ACT_GEN_GET, 0, NULL);
- }
-
- return 0;
+ return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_TX_RATE_QUERY,
+ HostCmd_ACT_GEN_GET, 0, NULL);
}
/*
@@ -780,7 +765,7 @@ static int mwifiex_rate_ioctl_set_rate_value(struct mwifiex_private *priv,
if ((rate[i] & 0x7f) == (rate_cfg->rate & 0x7f))
break;
}
- if (!rate[i] || (i == MWIFIEX_SUPPORTED_RATES)) {
+ if ((i == MWIFIEX_SUPPORTED_RATES) || !rate[i]) {
dev_err(adapter->dev, "fixed data rate %#x is out "
"of range\n", rate_cfg->rate);
return -1;
@@ -846,10 +831,10 @@ int mwifiex_drv_get_data_rate(struct mwifiex_private *priv,
ret = mwifiex_rate_ioctl_cfg(priv, rate);
if (!ret) {
- if (rate && rate->is_rate_auto)
+ if (rate->is_rate_auto)
rate->rate = mwifiex_index_to_data_rate(priv->tx_rate,
priv->tx_htinfo);
- else if (rate)
+ else
rate->rate = priv->data_rate;
} else {
ret = -1;
@@ -1266,9 +1251,9 @@ int mwifiex_get_signal_info(struct mwifiex_private *priv,
if (!status) {
if (signal->selector & BCN_RSSI_AVG_MASK)
- priv->w_stats.qual.level = signal->bcn_rssi_avg;
+ priv->qual_level = signal->bcn_rssi_avg;
if (signal->selector & BCN_NF_AVG_MASK)
- priv->w_stats.qual.noise = signal->bcn_nf_avg;
+ priv->qual_noise = signal->bcn_nf_avg;
}
return status;
@@ -1327,18 +1312,8 @@ int
mwifiex_get_stats_info(struct mwifiex_private *priv,
struct mwifiex_ds_get_stats *log)
{
- int ret;
-
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_GET_LOG,
+ return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_GET_LOG,
HostCmd_ACT_GEN_GET, 0, log);
-
- if (!ret) {
- priv->w_stats.discard.fragment = log->fcs_error;
- priv->w_stats.discard.retries = log->retry;
- priv->w_stats.discard.misc = log->ack_failure;
- }
-
- return ret;
}
/*
@@ -1580,7 +1555,7 @@ mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len)
{
struct mwifiex_ds_misc_gen_ie gen_ie;
- if (ie_len > IW_CUSTOM_MAX)
+ if (ie_len > IEEE_MAX_IE_SIZE)
return -EFAULT;
gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE;
diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c
index 1fdddece7479..27430512f7cd 100644
--- a/drivers/net/wireless/mwifiex/sta_rx.c
+++ b/drivers/net/wireless/mwifiex/sta_rx.c
@@ -187,7 +187,7 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *adapter,
ret = mwifiex_11n_rx_reorder_pkt(priv, local_rx_pd->seq_num,
local_rx_pd->priority, ta,
(u8) local_rx_pd->rx_pkt_type,
- (void *) skb);
+ skb);
if (ret || (rx_pkt_type == PKT_TYPE_BAR)) {
if (priv && (ret == -1))
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c
index fa6221bc9104..d97facd70e88 100644
--- a/drivers/net/wireless/mwifiex/sta_tx.c
+++ b/drivers/net/wireless/mwifiex/sta_tx.c
@@ -47,6 +47,7 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv,
struct mwifiex_adapter *adapter = priv->adapter;
struct txpd *local_tx_pd;
struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
+ u8 pad;
if (!skb->len) {
dev_err(adapter->dev, "Tx: bad packet length: %d\n",
@@ -55,15 +56,19 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv,
return skb->data;
}
- BUG_ON(skb_headroom(skb) < (sizeof(*local_tx_pd) + INTF_HEADER_LEN));
- skb_push(skb, sizeof(*local_tx_pd));
+ /* If skb->data is not aligned; add padding */
+ pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4;
+
+ BUG_ON(skb_headroom(skb) < (sizeof(*local_tx_pd) + INTF_HEADER_LEN
+ + pad));
+ skb_push(skb, sizeof(*local_tx_pd) + pad);
local_tx_pd = (struct txpd *) skb->data;
memset(local_tx_pd, 0, sizeof(struct txpd));
local_tx_pd->bss_num = priv->bss_num;
local_tx_pd->bss_type = priv->bss_type;
local_tx_pd->tx_pkt_length = cpu_to_le16((u16) (skb->len -
- sizeof(struct txpd)));
+ (sizeof(struct txpd) + pad)));
local_tx_pd->priority = (u8) skb->priority;
local_tx_pd->pkt_delay_2ms =
@@ -88,7 +93,7 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv,
}
/* Offset of actual data */
- local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
+ local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) + pad);
/* make space for INTF_HEADER_LEN */
skb_push(skb, INTF_HEADER_LEN);
@@ -146,7 +151,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags)
skb_push(skb, INTF_HEADER_LEN);
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
- skb->data, skb->len, NULL);
+ skb, NULL);
switch (ret) {
case -EBUSY:
adapter->data_sent = true;
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c
index aaa50c074196..a206f412875f 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/mwifiex/txrx.c
@@ -71,14 +71,14 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
u8 *head_ptr;
struct txpd *local_tx_pd = NULL;
- head_ptr = (u8 *) mwifiex_process_sta_txpd(priv, skb);
+ head_ptr = mwifiex_process_sta_txpd(priv, skb);
if (head_ptr) {
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
local_tx_pd =
(struct txpd *) (head_ptr + INTF_HEADER_LEN);
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
- skb->data, skb->len, tx_param);
+ skb, tx_param);
}
switch (ret) {
@@ -87,7 +87,8 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
(adapter->pps_uapsd_mode) &&
(adapter->tx_lock_flag)) {
priv->adapter->tx_lock_flag = false;
- local_tx_pd->flags = 0;
+ if (local_tx_pd)
+ local_tx_pd->flags = 0;
}
dev_dbg(adapter->dev, "data: -EBUSY is returned\n");
break;
@@ -160,43 +161,3 @@ done:
return 0;
}
-/*
- * Packet receive completion callback handler.
- *
- * This function calls another completion callback handler which
- * updates the statistics, and optionally updates the parent buffer
- * use count before freeing the received packet.
- */
-int mwifiex_recv_packet_complete(struct mwifiex_adapter *adapter,
- struct sk_buff *skb, int status)
-{
- struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
- struct mwifiex_rxinfo *rx_info_parent;
- struct mwifiex_private *priv;
- struct sk_buff *skb_parent;
- unsigned long flags;
-
- priv = adapter->priv[rx_info->bss_index];
-
- if (priv && (status == -1))
- priv->stats.rx_dropped++;
-
- if (rx_info->parent) {
- skb_parent = rx_info->parent;
- rx_info_parent = MWIFIEX_SKB_RXCB(skb_parent);
-
- spin_lock_irqsave(&priv->rx_pkt_lock, flags);
- --rx_info_parent->use_count;
-
- if (!rx_info_parent->use_count) {
- spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
- dev_kfree_skb_any(skb_parent);
- } else {
- spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
- }
- } else {
- dev_kfree_skb_any(skb);
- }
-
- return 0;
-}
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index d41291529bc0..06976f517f66 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -185,13 +185,14 @@ int mwifiex_recv_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb)
* corresponding waiting function. Otherwise, it processes the
* IOCTL response and frees the response buffer.
*/
-int mwifiex_complete_cmd(struct mwifiex_adapter *adapter)
+int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node)
{
atomic_dec(&adapter->cmd_pending);
dev_dbg(adapter->dev, "cmd completed: status=%d\n",
adapter->cmd_wait_q.status);
- adapter->cmd_wait_q.condition = true;
+ *(cmd_node->condition) = true;
if (adapter->cmd_wait_q.status == -ETIMEDOUT)
dev_err(adapter->dev, "cmd timeout\n");
diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h
index 9506afc6c0e4..f6d36b9654a0 100644
--- a/drivers/net/wireless/mwifiex/util.h
+++ b/drivers/net/wireless/mwifiex/util.h
@@ -22,11 +22,16 @@
static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb)
{
- return (struct mwifiex_rxinfo *)skb->cb;
+ return (struct mwifiex_rxinfo *)(skb->cb + sizeof(phys_addr_t));
}
static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb)
{
- return (struct mwifiex_txinfo *)skb->cb;
+ return (struct mwifiex_txinfo *)(skb->cb + sizeof(phys_addr_t));
+}
+
+static inline phys_addr_t *MWIFIEX_SKB_PACB(struct sk_buff *skb)
+{
+ return (phys_addr_t *)skb->cb;
}
#endif /* !_MWIFIEX_UTIL_H_ */
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index 91634daec306..6c239c3c8249 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -633,6 +633,8 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_adapter *adapter,
ra_list = NULL;
} else {
memcpy(ra, skb->data, ETH_ALEN);
+ if (ra[0] & 0x01)
+ memset(ra, 0xff, ETH_ALEN);
ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra);
}
@@ -971,25 +973,25 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
}
/*
- * This function gets the number of packets in the Tx queue of a
- * particular RA list.
+ * This function checks if 11n aggregation is possible.
*/
static int
-mwifiex_num_pkts_in_txq(struct mwifiex_private *priv,
- struct mwifiex_ra_list_tbl *ptr, int max_buf_size)
+mwifiex_is_11n_aggragation_possible(struct mwifiex_private *priv,
+ struct mwifiex_ra_list_tbl *ptr,
+ int max_buf_size)
{
int count = 0, total_size = 0;
struct sk_buff *skb, *tmp;
skb_queue_walk_safe(&ptr->skb_head, skb, tmp) {
total_size += skb->len;
- if (total_size < max_buf_size)
- ++count;
- else
+ if (total_size >= max_buf_size)
break;
+ if (++count >= MIN_NUM_AMSDU)
+ return true;
}
- return count;
+ return false;
}
/*
@@ -1123,8 +1125,8 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv,
tx_param.next_pkt_len =
((skb_next) ? skb_next->len +
sizeof(struct txpd) : 0);
- ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
- skb->data, skb->len, &tx_param);
+ ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, skb,
+ &tx_param);
switch (ret) {
case -EBUSY:
dev_dbg(adapter->dev, "data: -EBUSY is returned\n");
@@ -1229,11 +1231,9 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
mwifiex_send_delba(priv, tid_del, ra, 1);
}
}
-/* Minimum number of AMSDU */
-#define MIN_NUM_AMSDU 2
if (mwifiex_is_amsdu_allowed(priv, tid) &&
- (mwifiex_num_pkts_in_txq(priv, ptr, adapter->tx_buf_size) >=
- MIN_NUM_AMSDU))
+ mwifiex_is_11n_aggragation_possible(priv, ptr,
+ adapter->tx_buf_size))
mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN,
ptr_index, flags);
/* ra_list_spinlock has been freed in
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index aeac3cc4dbe4..995695c28d5c 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -10,6 +10,7 @@
*/
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -1891,9 +1892,9 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
txpriority = index;
- if (ieee80211_is_data_qos(wh->frame_control) &&
- skb->protocol != cpu_to_be16(ETH_P_PAE) &&
- sta->ht_cap.ht_supported && priv->ap_fw) {
+ if (priv->ap_fw && sta && sta->ht_cap.ht_supported
+ && skb->protocol != cpu_to_be16(ETH_P_PAE)
+ && ieee80211_is_data_qos(wh->frame_control)) {
tid = qos & 0xf;
mwl8k_tx_count_packet(sta, tid);
spin_lock(&priv->stream_lock);
@@ -4096,9 +4097,6 @@ static int mwl8k_set_key(struct ieee80211_hw *hw,
if (rc)
goto out;
-
- mwl8k_vif->is_hw_crypto_enabled = false;
-
}
out:
return rc;
@@ -4917,7 +4915,8 @@ static int mwl8k_sta_add(struct ieee80211_hw *hw,
return ret;
}
-static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
+static int mwl8k_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct mwl8k_priv *priv = hw->priv;
@@ -5464,7 +5463,7 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
goto fail;
for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) {
- rc = mwl8k_conf_tx(hw, i, &priv->wmm_params[i]);
+ rc = mwl8k_conf_tx(hw, NULL, i, &priv->wmm_params[i]);
if (rc)
goto fail;
}
@@ -5503,6 +5502,14 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
/* Set rssi values to dBm */
hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_HAS_RATE_CONTROL;
+
+ /*
+ * Ask mac80211 to not to trigger PS mode
+ * based on PM bit of incoming frames.
+ */
+ if (priv->ap_fw)
+ hw->flags |= IEEE80211_HW_AP_LINK_PS;
+
hw->vif_data_size = sizeof(struct mwl8k_vif);
hw->sta_data_size = sizeof(struct mwl8k_sta);
diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/orinoco/airport.c
index 4a0a0e5265c9..0ca8b1455cd9 100644
--- a/drivers/net/wireless/orinoco/airport.c
+++ b/drivers/net/wireless/orinoco/airport.c
@@ -150,7 +150,7 @@ airport_attach(struct macio_dev *mdev, const struct of_device_id *match)
struct orinoco_private *priv;
struct airport *card;
unsigned long phys_addr;
- hermes_t *hw;
+ struct hermes *hw;
if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) {
printk(KERN_ERR PFX "Wrong interrupt/addresses in OF tree\n");
@@ -228,10 +228,9 @@ MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
MODULE_LICENSE("Dual MPL/GPL");
-static struct of_device_id airport_match[] =
-{
+static struct of_device_id airport_match[] = {
{
- .name = "radio",
+ .name = "radio",
},
{},
};
@@ -240,7 +239,7 @@ MODULE_DEVICE_TABLE(of, airport_match);
static struct macio_driver airport_driver = {
.driver = {
- .name = DRIVER_NAME,
+ .name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = airport_match,
},
diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c
index 736bbb9bd1d0..f7b15b8934fa 100644
--- a/drivers/net/wireless/orinoco/cfg.c
+++ b/drivers/net/wireless/orinoco/cfg.c
@@ -59,7 +59,7 @@ int orinoco_wiphy_register(struct wiphy *wiphy)
for (i = 0; i < NUM_CHANNELS; i++) {
if (priv->channel_mask & (1 << i)) {
priv->channels[i].center_freq =
- ieee80211_dsss_chan_to_freq(i+1);
+ ieee80211_dsss_chan_to_freq(i + 1);
channels++;
}
}
@@ -182,7 +182,7 @@ static int orinoco_set_channel(struct wiphy *wiphy,
channel = ieee80211_freq_to_dsss_chan(chan->center_freq);
if ((channel < 1) || (channel > NUM_CHANNELS) ||
- !(priv->channel_mask & (1 << (channel-1))))
+ !(priv->channel_mask & (1 << (channel - 1))))
return -EINVAL;
if (orinoco_lock(priv, &flags) != 0)
@@ -191,7 +191,7 @@ static int orinoco_set_channel(struct wiphy *wiphy,
priv->channel = channel;
if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
/* Fast channel change - no commit if successful */
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
HERMES_TEST_SET_CHANNEL,
channel, NULL);
diff --git a/drivers/net/wireless/orinoco/fw.c b/drivers/net/wireless/orinoco/fw.c
index 259d75853984..4df8cf64b56c 100644
--- a/drivers/net/wireless/orinoco/fw.c
+++ b/drivers/net/wireless/orinoco/fw.c
@@ -6,6 +6,7 @@
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/device.h>
+#include <linux/module.h>
#include "hermes.h"
#include "hermes_dld.h"
@@ -100,7 +101,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
/* Plug Data Area (PDA) */
__le16 *pda;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
const struct firmware *fw_entry;
const struct orinoco_fw_header *hdr;
const unsigned char *first_block;
@@ -205,7 +206,7 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
const unsigned char *image, const void *end,
int secondary)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int ret = 0;
const unsigned char *ptr;
const unsigned char *first_block;
@@ -322,9 +323,8 @@ symbol_dl_firmware(struct orinoco_private *priv,
fw_entry->data + fw_entry->size, 1);
if (!orinoco_cached_fw_get(priv, false))
release_firmware(fw_entry);
- if (ret) {
+ if (ret)
dev_err(dev, "Secondary firmware download failed\n");
- }
return ret;
}
diff --git a/drivers/net/wireless/orinoco/fw.h b/drivers/net/wireless/orinoco/fw.h
index 89fc26d25b06..aca63e3c4b5b 100644
--- a/drivers/net/wireless/orinoco/fw.h
+++ b/drivers/net/wireless/orinoco/fw.h
@@ -14,7 +14,7 @@ int orinoco_download(struct orinoco_private *priv);
void orinoco_cache_fw(struct orinoco_private *priv, int ap);
void orinoco_uncache_fw(struct orinoco_private *priv);
#else
-#define orinoco_cache_fw(priv, ap) do { } while(0)
+#define orinoco_cache_fw(priv, ap) do { } while (0)
#define orinoco_uncache_fw(priv) do { } while (0)
#endif
diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c
index 6c6a23e08df6..75c15bc7b34c 100644
--- a/drivers/net/wireless/orinoco/hermes.c
+++ b/drivers/net/wireless/orinoco/hermes.c
@@ -103,7 +103,7 @@ static const struct hermes_ops hermes_ops_local;
Callable from any context.
*/
-static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0,
+static int hermes_issue_cmd(struct hermes *hw, u16 cmd, u16 param0,
u16 param1, u16 param2)
{
int k = CMD_BUSY_TIMEOUT;
@@ -132,7 +132,7 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0,
*/
/* For doing cmds that wipe the magic constant in SWSUPPORT0 */
-static int hermes_doicmd_wait(hermes_t *hw, u16 cmd,
+static int hermes_doicmd_wait(struct hermes *hw, u16 cmd,
u16 parm0, u16 parm1, u16 parm2,
struct hermes_response *resp)
{
@@ -185,7 +185,8 @@ out:
return err;
}
-void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing)
+void hermes_struct_init(struct hermes *hw, void __iomem *address,
+ int reg_spacing)
{
hw->iobase = address;
hw->reg_spacing = reg_spacing;
@@ -195,7 +196,7 @@ void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing)
}
EXPORT_SYMBOL(hermes_struct_init);
-static int hermes_init(hermes_t *hw)
+static int hermes_init(struct hermes *hw)
{
u16 reg;
int err = 0;
@@ -249,7 +250,7 @@ static int hermes_init(hermes_t *hw)
* > 0 on error returned by the firmware
*
* Callable from any context, but locking is your problem. */
-static int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
+static int hermes_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0,
struct hermes_response *resp)
{
int err;
@@ -313,7 +314,7 @@ static int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
return err;
}
-static int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
+static int hermes_allocate(struct hermes *hw, u16 size, u16 *fid)
{
int err = 0;
int k;
@@ -363,7 +364,7 @@ static int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
* from firmware
*
* Callable from any context */
-static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset)
+static int hermes_bap_seek(struct hermes *hw, int bap, u16 id, u16 offset)
{
int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0;
int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0;
@@ -422,7 +423,7 @@ static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset)
* 0 on success
* > 0 on error from firmware
*/
-static int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
+static int hermes_bap_pread(struct hermes *hw, int bap, void *buf, int len,
u16 id, u16 offset)
{
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
@@ -436,7 +437,7 @@ static int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
goto out;
/* Actually do the transfer */
- hermes_read_words(hw, dreg, buf, len/2);
+ hermes_read_words(hw, dreg, buf, len / 2);
out:
return err;
@@ -450,8 +451,8 @@ static int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
* 0 on success
* > 0 on error from firmware
*/
-static int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
- u16 id, u16 offset)
+static int hermes_bap_pwrite(struct hermes *hw, int bap, const void *buf,
+ int len, u16 id, u16 offset)
{
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0;
@@ -478,8 +479,8 @@ static int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
* practice.
*
* Callable from user or bh context. */
-static int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize,
- u16 *length, void *buf)
+static int hermes_read_ltv(struct hermes *hw, int bap, u16 rid,
+ unsigned bufsize, u16 *length, void *buf)
{
int err = 0;
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
@@ -523,7 +524,7 @@ static int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize,
return 0;
}
-static int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
+static int hermes_write_ltv(struct hermes *hw, int bap, u16 rid,
u16 length, const void *value)
{
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
@@ -553,14 +554,14 @@ static int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
/*** Hermes AUX control ***/
static inline void
-hermes_aux_setaddr(hermes_t *hw, u32 addr)
+hermes_aux_setaddr(struct hermes *hw, u32 addr)
{
hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
}
static inline int
-hermes_aux_control(hermes_t *hw, int enabled)
+hermes_aux_control(struct hermes *hw, int enabled)
{
int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
@@ -594,7 +595,7 @@ hermes_aux_control(hermes_t *hw, int enabled)
* wl_lkm Agere fw does
* Don't know about intersil
*/
-static int hermesi_program_init(hermes_t *hw, u32 offset)
+static int hermesi_program_init(struct hermes *hw, u32 offset)
{
int err;
@@ -643,7 +644,7 @@ static int hermesi_program_init(hermes_t *hw, u32 offset)
* wl_lkm Agere fw does
* Don't know about intersil
*/
-static int hermesi_program_end(hermes_t *hw)
+static int hermesi_program_end(struct hermes *hw)
{
struct hermes_response resp;
int rc = 0;
@@ -684,7 +685,8 @@ static int hermes_program_bytes(struct hermes *hw, const char *data,
}
/* Read PDA from the adapter */
-static int hermes_read_pda(hermes_t *hw, __le16 *pda, u32 pda_addr, u16 pda_len)
+static int hermes_read_pda(struct hermes *hw, __le16 *pda, u32 pda_addr,
+ u16 pda_len)
{
int ret;
u16 pda_size;
diff --git a/drivers/net/wireless/orinoco/hermes.h b/drivers/net/wireless/orinoco/hermes.h
index d9f18c11682a..28a42448d329 100644
--- a/drivers/net/wireless/orinoco/hermes.h
+++ b/drivers/net/wireless/orinoco/hermes.h
@@ -28,7 +28,7 @@
*
* As a module of low level hardware access routines, there is no
* locking. Users of this module should ensure that they serialize
- * access to the hermes_t structure, and to the hardware
+ * access to the hermes structure, and to the hardware
*/
#include <linux/if_ether.h>
@@ -43,7 +43,7 @@
#define HERMES_BAP_DATALEN_MAX (4096)
#define HERMES_BAP_OFFSET_MAX (4096)
#define HERMES_PORTID_MAX (7)
-#define HERMES_NUMPORTS_MAX (HERMES_PORTID_MAX+1)
+#define HERMES_NUMPORTS_MAX (HERMES_PORTID_MAX + 1)
#define HERMES_PDR_LEN_MAX (260) /* in bytes, from EK */
#define HERMES_PDA_RECS_MAX (200) /* a guess */
#define HERMES_PDA_LEN_MAX (1024) /* in bytes, from EK */
@@ -148,7 +148,7 @@
#define HERMES_CMD_WRITEMIF (0x0031)
/*--- Debugging Commands -----------------------------*/
-#define HERMES_CMD_TEST (0x0038)
+#define HERMES_CMD_TEST (0x0038)
/* Test command arguments */
@@ -178,8 +178,8 @@
#define HERMES_DESCRIPTOR_OFFSET 0
#define HERMES_802_11_OFFSET (14)
-#define HERMES_802_3_OFFSET (14+32)
-#define HERMES_802_2_OFFSET (14+32+14)
+#define HERMES_802_3_OFFSET (14 + 32)
+#define HERMES_802_2_OFFSET (14 + 32 + 14)
#define HERMES_TXCNTL2_OFFSET (HERMES_802_3_OFFSET - 2)
#define HERMES_RXSTAT_ERR (0x0003)
@@ -406,7 +406,7 @@ struct hermes_ops {
};
/* Basic control structure */
-typedef struct hermes {
+struct hermes {
void __iomem *iobase;
int reg_spacing;
#define HERMES_16BIT_REGSPACING 0
@@ -415,7 +415,7 @@ typedef struct hermes {
bool eeprom_pda;
const struct hermes_ops *ops;
void *priv;
-} hermes_t;
+};
/* Register access convenience macros */
#define hermes_read_reg(hw, off) \
@@ -427,28 +427,29 @@ typedef struct hermes {
hermes_write_reg((hw), HERMES_##name, (val))
/* Function prototypes */
-void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing);
+void hermes_struct_init(struct hermes *hw, void __iomem *address,
+ int reg_spacing);
/* Inline functions */
-static inline int hermes_present(hermes_t *hw)
+static inline int hermes_present(struct hermes *hw)
{
return hermes_read_regn(hw, SWSUPPORT0) == HERMES_MAGIC;
}
-static inline void hermes_set_irqmask(hermes_t *hw, u16 events)
+static inline void hermes_set_irqmask(struct hermes *hw, u16 events)
{
hw->inten = events;
hermes_write_regn(hw, INTEN, events);
}
-static inline int hermes_enable_port(hermes_t *hw, int port)
+static inline int hermes_enable_port(struct hermes *hw, int port)
{
return hw->ops->cmd_wait(hw, HERMES_CMD_ENABLE | (port << 8),
0, NULL);
}
-static inline int hermes_disable_port(hermes_t *hw, int port)
+static inline int hermes_disable_port(struct hermes *hw, int port)
{
return hw->ops->cmd_wait(hw, HERMES_CMD_DISABLE | (port << 8),
0, NULL);
@@ -456,13 +457,13 @@ static inline int hermes_disable_port(hermes_t *hw, int port)
/* Initiate an INQUIRE command (tallies or scan). The result will come as an
* information frame in __orinoco_ev_info() */
-static inline int hermes_inquire(hermes_t *hw, u16 rid)
+static inline int hermes_inquire(struct hermes *hw, u16 rid)
{
return hw->ops->cmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL);
}
-#define HERMES_BYTES_TO_RECLEN(n) ((((n)+1)/2) + 1)
-#define HERMES_RECLEN_TO_BYTES(n) (((n)-1) * 2)
+#define HERMES_BYTES_TO_RECLEN(n) ((((n) + 1) / 2) + 1)
+#define HERMES_RECLEN_TO_BYTES(n) (((n) - 1) * 2)
/* Note that for the next two, the count is in 16-bit words, not bytes */
static inline void hermes_read_words(struct hermes *hw, int off,
@@ -498,7 +499,8 @@ static inline void hermes_clear_words(struct hermes *hw, int off,
(hw->ops->write_ltv((hw), (bap), (rid), \
HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf)))
-static inline int hermes_read_wordrec(hermes_t *hw, int bap, u16 rid, u16 *word)
+static inline int hermes_read_wordrec(struct hermes *hw, int bap, u16 rid,
+ u16 *word)
{
__le16 rec;
int err;
@@ -508,7 +510,8 @@ static inline int hermes_read_wordrec(hermes_t *hw, int bap, u16 rid, u16 *word)
return err;
}
-static inline int hermes_write_wordrec(hermes_t *hw, int bap, u16 rid, u16 word)
+static inline int hermes_write_wordrec(struct hermes *hw, int bap, u16 rid,
+ u16 word)
{
__le16 rec = cpu_to_le16(word);
return HERMES_WRITE_RECORD(hw, bap, rid, &rec);
diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c
index 2b2b9a1a979c..4a10b7aca043 100644
--- a/drivers/net/wireless/orinoco/hermes_dld.c
+++ b/drivers/net/wireless/orinoco/hermes_dld.c
@@ -193,7 +193,7 @@ hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end)
/* Process one Plug Data Item - find corresponding PDR and plug it */
static int
-hermes_plug_pdi(hermes_t *hw, const struct pdr *first_pdr,
+hermes_plug_pdi(struct hermes *hw, const struct pdr *first_pdr,
const struct pdi *pdi, const void *pdr_end)
{
const struct pdr *pdr;
@@ -220,7 +220,7 @@ hermes_plug_pdi(hermes_t *hw, const struct pdr *first_pdr,
* Attempt to write every records that is in the specified pda
* which also has a valid production data record for the firmware.
*/
-int hermes_apply_pda(hermes_t *hw,
+int hermes_apply_pda(struct hermes *hw,
const char *first_pdr,
const void *pdr_end,
const __le16 *pda,
@@ -274,7 +274,7 @@ hermes_blocks_length(const char *first_block, const void *end)
/*** Hermes programming ***/
/* Program the data blocks */
-int hermes_program(hermes_t *hw, const char *first_block, const void *end)
+int hermes_program(struct hermes *hw, const char *first_block, const void *end)
{
const struct dblock *blk;
u32 blkaddr;
@@ -387,7 +387,7 @@ DEFINE_DEFAULT_PDR(0x0161, 256,
*
* For certain records, use defaults if they are not found in pda.
*/
-int hermes_apply_pda_with_defaults(hermes_t *hw,
+int hermes_apply_pda_with_defaults(struct hermes *hw,
const char *first_pdr,
const void *pdr_end,
const __le16 *pda,
diff --git a/drivers/net/wireless/orinoco/hermes_dld.h b/drivers/net/wireless/orinoco/hermes_dld.h
index 583a5bcf9175..b5377e232c63 100644
--- a/drivers/net/wireless/orinoco/hermes_dld.h
+++ b/drivers/net/wireless/orinoco/hermes_dld.h
@@ -27,21 +27,21 @@
#include "hermes.h"
-int hermesi_program_init(hermes_t *hw, u32 offset);
-int hermesi_program_end(hermes_t *hw);
-int hermes_program(hermes_t *hw, const char *first_block, const void *end);
+int hermesi_program_init(struct hermes *hw, u32 offset);
+int hermesi_program_end(struct hermes *hw);
+int hermes_program(struct hermes *hw, const char *first_block, const void *end);
-int hermes_read_pda(hermes_t *hw,
+int hermes_read_pda(struct hermes *hw,
__le16 *pda,
u32 pda_addr,
u16 pda_len,
int use_eeprom);
-int hermes_apply_pda(hermes_t *hw,
+int hermes_apply_pda(struct hermes *hw,
const char *first_pdr,
const void *pdr_end,
const __le16 *pda,
const void *pda_end);
-int hermes_apply_pda_with_defaults(hermes_t *hw,
+int hermes_apply_pda_with_defaults(struct hermes *hw,
const char *first_pdr,
const void *pdr_end,
const __le16 *pda,
diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c
index 3c7877a7c31c..c09c8437c0b8 100644
--- a/drivers/net/wireless/orinoco/hw.c
+++ b/drivers/net/wireless/orinoco/hw.c
@@ -47,7 +47,7 @@ struct comp_id {
u16 id, variant, major, minor;
} __packed;
-static inline fwtype_t determine_firmware_type(struct comp_id *nic_id)
+static inline enum fwtype determine_firmware_type(struct comp_id *nic_id)
{
if (nic_id->id < 0x8000)
return FIRMWARE_TYPE_AGERE;
@@ -71,11 +71,11 @@ int determine_fw_capabilities(struct orinoco_private *priv,
u32 *hw_ver)
{
struct device *dev = priv->dev;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err;
struct comp_id nic_id, sta_id;
unsigned int firmver;
- char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2)));
+ char tmp[SYMBOL_MAX_VER_LEN + 1] __attribute__((aligned(2)));
/* Get the hardware version */
err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
@@ -280,7 +280,7 @@ int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr)
{
struct device *dev = priv->dev;
struct hermes_idstring nickbuf;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int len;
int err;
u16 reclen;
@@ -458,7 +458,7 @@ int orinoco_hw_program_rids(struct orinoco_private *priv)
{
struct net_device *dev = priv->ndev;
struct wireless_dev *wdev = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err;
struct hermes_idstring idbuf;
@@ -529,7 +529,7 @@ int orinoco_hw_program_rids(struct orinoco_private *priv)
memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
/* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
- HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
+ HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2),
&idbuf);
if (err) {
printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
@@ -537,7 +537,7 @@ int orinoco_hw_program_rids(struct orinoco_private *priv)
return err;
}
err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
- HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
+ HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2),
&idbuf);
if (err) {
printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
@@ -549,7 +549,7 @@ int orinoco_hw_program_rids(struct orinoco_private *priv)
idbuf.len = cpu_to_le16(strlen(priv->nick));
memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
- HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
+ HERMES_BYTES_TO_RECLEN(strlen(priv->nick) + 2),
&idbuf);
if (err) {
printk(KERN_ERR "%s: Error %d setting nickname\n",
@@ -689,7 +689,7 @@ int orinoco_hw_program_rids(struct orinoco_private *priv)
/* Get tsc from the firmware */
int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err = 0;
u8 tsc_arr[4][ORINOCO_SEQ_LEN];
@@ -706,7 +706,7 @@ int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc)
int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int ratemode = priv->bitratemode;
int err = 0;
@@ -737,7 +737,7 @@ int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int i;
int err = 0;
u16 val;
@@ -786,7 +786,7 @@ int __orinoco_hw_set_wap(struct orinoco_private *priv)
{
int roaming_flag;
int err = 0;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
switch (priv->firmware_type) {
case FIRMWARE_TYPE_AGERE:
@@ -818,7 +818,7 @@ int __orinoco_hw_set_wap(struct orinoco_private *priv)
* which is needed for 802.1x implementations. */
int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err = 0;
int i;
@@ -902,7 +902,7 @@ int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
int __orinoco_hw_setup_enc(struct orinoco_private *priv)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err = 0;
int master_wep_flag;
int auth_flag;
@@ -999,7 +999,7 @@ int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
u8 rx_mic[MIC_KEYLEN];
u8 tsc[ORINOCO_SEQ_LEN];
} __packed buf;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int ret;
int err;
int k;
@@ -1052,7 +1052,7 @@ int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err;
err = hermes_write_wordrec(hw, USER_BAP,
@@ -1068,7 +1068,7 @@ int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
struct net_device *dev,
int mc_count, int promisc)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err = 0;
if (promisc != priv->promiscuous) {
@@ -1111,9 +1111,9 @@ int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
/* Return : < 0 -> error code ; >= 0 -> length */
int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
- char buf[IW_ESSID_MAX_SIZE+1])
+ char buf[IW_ESSID_MAX_SIZE + 1])
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err = 0;
struct hermes_idstring essidbuf;
char *p = (char *)(&essidbuf.val);
@@ -1166,7 +1166,7 @@ int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
int orinoco_hw_get_freq(struct orinoco_private *priv)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err = 0;
u16 channel;
int freq = 0;
@@ -1206,7 +1206,7 @@ int orinoco_hw_get_freq(struct orinoco_private *priv)
int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
int *numrates, s32 *rates, int max)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
struct hermes_idstring list;
unsigned char *p = (unsigned char *)&list.val;
int err = 0;
@@ -1238,7 +1238,7 @@ int orinoco_hw_trigger_scan(struct orinoco_private *priv,
const struct cfg80211_ssid *ssid)
{
struct net_device *dev = priv->ndev;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
unsigned long flags;
int err = 0;
@@ -1323,7 +1323,7 @@ int orinoco_hw_trigger_scan(struct orinoco_private *priv,
int orinoco_hw_disassociate(struct orinoco_private *priv,
u8 *addr, u16 reason_code)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err;
struct {
@@ -1346,7 +1346,7 @@ int orinoco_hw_disassociate(struct orinoco_private *priv,
int orinoco_hw_get_current_bssid(struct orinoco_private *priv,
u8 *addr)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err;
err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/orinoco/hw.h
index 97af71e79950..8f6831f4e328 100644
--- a/drivers/net/wireless/orinoco/hw.h
+++ b/drivers/net/wireless/orinoco/hw.h
@@ -45,7 +45,7 @@ int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
struct net_device *dev,
int mc_count, int promisc);
int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
- char buf[IW_ESSID_MAX_SIZE+1]);
+ char buf[IW_ESSID_MAX_SIZE + 1]);
int orinoco_hw_get_freq(struct orinoco_private *priv);
int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
int *numrates, s32 *rates, int max);
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c
index 62c6b2b37dbe..b52acc4b4086 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/orinoco/main.c
@@ -4,7 +4,7 @@
* adaptors, with Lucent/Agere, Intersil or Symbol firmware.
*
* Current maintainers (as of 29 September 2003) are:
- * Pavel Roskin <proski AT gnu.org>
+ * Pavel Roskin <proski AT gnu.org>
* and David Gibson <hermes AT gibson.dropbear.id.au>
*
* (C) Copyright David Gibson, IBM Corporation 2001-2003.
@@ -146,10 +146,10 @@ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
#define ORINOCO_MAX_MTU (IEEE80211_MAX_DATA_LEN - ENCAPS_OVERHEAD)
#define MAX_IRQLOOPS_PER_IRQ 10
-#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of
- * how many events the
- * device could
- * legitimately generate */
+#define MAX_IRQLOOPS_PER_JIFFY (20000 / HZ) /* Based on a guestimate of
+ * how many events the
+ * device could
+ * legitimately generate */
#define DUMMY_FID 0xFFFF
@@ -157,7 +157,7 @@ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
HERMES_MAX_MULTICAST : 0)*/
#define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST)
-#define ORINOCO_INTEN (HERMES_EV_RX | HERMES_EV_ALLOC \
+#define ORINOCO_INTEN (HERMES_EV_RX | HERMES_EV_ALLOC \
| HERMES_EV_TX | HERMES_EV_TXEXC \
| HERMES_EV_WTERR | HERMES_EV_INFO \
| HERMES_EV_INFDROP)
@@ -437,12 +437,12 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct orinoco_private *priv = ndev_priv(dev);
struct net_device_stats *stats = &priv->stats;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err = 0;
u16 txfid = priv->txfid;
int tx_control;
unsigned long flags;
- u8 mic_buf[MICHAEL_MIC_LEN+1];
+ u8 mic_buf[MICHAEL_MIC_LEN + 1];
if (!netif_running(dev)) {
printk(KERN_ERR "%s: Tx on stopped device!\n",
@@ -579,7 +579,7 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_BUSY;
}
-static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
+static void __orinoco_ev_alloc(struct net_device *dev, struct hermes *hw)
{
struct orinoco_private *priv = ndev_priv(dev);
u16 fid = hermes_read_regn(hw, ALLOCFID);
@@ -594,7 +594,7 @@ static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
}
-static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
+static void __orinoco_ev_tx(struct net_device *dev, struct hermes *hw)
{
struct orinoco_private *priv = ndev_priv(dev);
struct net_device_stats *stats = &priv->stats;
@@ -606,7 +606,7 @@ static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
}
-static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
+static void __orinoco_ev_txexc(struct net_device *dev, struct hermes *hw)
{
struct orinoco_private *priv = ndev_priv(dev);
struct net_device_stats *stats = &priv->stats;
@@ -753,7 +753,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
struct sk_buff *skb;
struct orinoco_private *priv = ndev_priv(dev);
struct net_device_stats *stats = &priv->stats;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
len = le16_to_cpu(desc->data_len);
@@ -840,7 +840,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
stats->rx_dropped++;
}
-void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
+void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw)
{
struct orinoco_private *priv = ndev_priv(dev);
struct net_device_stats *stats = &priv->stats;
@@ -918,7 +918,7 @@ void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
32bit boundary, plus 1 byte so we can read in odd length
packets from the card, which has an IO granularity of 16
bits */
- skb = dev_alloc_skb(length+ETH_HLEN+2+1);
+ skb = dev_alloc_skb(length + ETH_HLEN + 2 + 1);
if (!skb) {
printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
dev->name);
@@ -1402,7 +1402,7 @@ static void orinoco_process_scan_results(struct work_struct *work)
spin_unlock_irqrestore(&priv->scan_lock, flags);
}
-void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
+void __orinoco_ev_info(struct net_device *dev, struct hermes *hw)
{
struct orinoco_private *priv = ndev_priv(dev);
u16 infofid;
@@ -1620,7 +1620,7 @@ void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
}
EXPORT_SYMBOL(__orinoco_ev_info);
-static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
+static void __orinoco_ev_infdrop(struct net_device *dev, struct hermes *hw)
{
if (net_ratelimit())
printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name);
@@ -1831,7 +1831,7 @@ static int __orinoco_commit(struct orinoco_private *priv)
int orinoco_commit(struct orinoco_private *priv)
{
struct net_device *dev = priv->ndev;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err;
if (priv->broken_disableport) {
@@ -1874,12 +1874,12 @@ int orinoco_commit(struct orinoco_private *priv)
/* Interrupt handler */
/********************************************************************/
-static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw)
+static void __orinoco_ev_tick(struct net_device *dev, struct hermes *hw)
{
printk(KERN_DEBUG "%s: TICK\n", dev->name);
}
-static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw)
+static void __orinoco_ev_wterr(struct net_device *dev, struct hermes *hw)
{
/* This seems to happen a fair bit under load, but ignoring it
seems to work fine...*/
@@ -1891,7 +1891,7 @@ irqreturn_t orinoco_interrupt(int irq, void *dev_id)
{
struct orinoco_private *priv = dev_id;
struct net_device *dev = priv->ndev;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int count = MAX_IRQLOOPS_PER_IRQ;
u16 evstat, events;
/* These are used to detect a runaway interrupt situation.
@@ -1958,7 +1958,7 @@ irqreturn_t orinoco_interrupt(int irq, void *dev_id)
evstat = hermes_read_regn(hw, EVSTAT);
events = evstat & hw->inten;
- };
+ }
orinoco_unlock(priv, &flags);
return IRQ_HANDLED;
@@ -2017,8 +2017,8 @@ static void orinoco_unregister_pm_notifier(struct orinoco_private *priv)
unregister_pm_notifier(&priv->pm_notifier);
}
#else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */
-#define orinoco_register_pm_notifier(priv) do { } while(0)
-#define orinoco_unregister_pm_notifier(priv) do { } while(0)
+#define orinoco_register_pm_notifier(priv) do { } while (0)
+#define orinoco_unregister_pm_notifier(priv) do { } while (0)
#endif
/********************************************************************/
@@ -2029,7 +2029,7 @@ int orinoco_init(struct orinoco_private *priv)
{
struct device *dev = priv->dev;
struct wiphy *wiphy = priv_to_wiphy(priv);
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err = 0;
/* No need to lock, the hw_unavailable flag is already set in
@@ -2135,7 +2135,7 @@ static const struct net_device_ops orinoco_netdev_ops = {
.ndo_open = orinoco_open,
.ndo_stop = orinoco_stop,
.ndo_start_xmit = orinoco_xmit,
- .ndo_set_multicast_list = orinoco_set_multicast_list,
+ .ndo_set_rx_mode = orinoco_set_multicast_list,
.ndo_change_mtu = orinoco_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/wireless/orinoco/mic.c b/drivers/net/wireless/orinoco/mic.c
index c03e7f54d1b8..fce4a843e656 100644
--- a/drivers/net/wireless/orinoco/mic.c
+++ b/drivers/net/wireless/orinoco/mic.c
@@ -59,10 +59,10 @@ int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key,
/* Copy header into buffer. We need the padding on the end zeroed */
memcpy(&hdr[0], da, ETH_ALEN);
memcpy(&hdr[ETH_ALEN], sa, ETH_ALEN);
- hdr[ETH_ALEN*2] = priority;
- hdr[ETH_ALEN*2+1] = 0;
- hdr[ETH_ALEN*2+2] = 0;
- hdr[ETH_ALEN*2+3] = 0;
+ hdr[ETH_ALEN * 2] = priority;
+ hdr[ETH_ALEN * 2 + 1] = 0;
+ hdr[ETH_ALEN * 2 + 2] = 0;
+ hdr[ETH_ALEN * 2 + 3] = 0;
/* Use scatter gather to MIC header and data in one go */
sg_init_table(sg, 2);
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h
index 255710ef082a..3bb936b9558c 100644
--- a/drivers/net/wireless/orinoco/orinoco.h
+++ b/drivers/net/wireless/orinoco/orinoco.h
@@ -49,11 +49,11 @@ enum orinoco_alg {
ORINOCO_ALG_TKIP
};
-typedef enum {
+enum fwtype {
FIRMWARE_TYPE_AGERE,
FIRMWARE_TYPE_INTERSIL,
FIRMWARE_TYPE_SYMBOL
-} fwtype_t;
+};
struct firmware;
@@ -88,11 +88,11 @@ struct orinoco_private {
struct iw_statistics wstats;
/* Hardware control variables */
- hermes_t hw;
+ struct hermes hw;
u16 txfid;
/* Capabilities of the hardware/firmware */
- fwtype_t firmware_type;
+ enum fwtype firmware_type;
int ibss_port;
int nicbuf_size;
u16 channel_mask;
@@ -122,8 +122,8 @@ struct orinoco_private {
struct key_params keys[ORINOCO_MAX_KEYS];
int bitratemode;
- char nick[IW_ESSID_MAX_SIZE+1];
- char desired_essid[IW_ESSID_MAX_SIZE+1];
+ char nick[IW_ESSID_MAX_SIZE + 1];
+ char desired_essid[IW_ESSID_MAX_SIZE + 1];
char desired_bssid[ETH_ALEN];
int bssid_fixed;
u16 frag_thresh, mwo_robust;
@@ -197,8 +197,8 @@ extern int orinoco_up(struct orinoco_private *priv);
extern void orinoco_down(struct orinoco_private *priv);
extern irqreturn_t orinoco_interrupt(int irq, void *dev_id);
-extern void __orinoco_ev_info(struct net_device *dev, hermes_t *hw);
-extern void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw);
+extern void __orinoco_ev_info(struct net_device *dev, struct hermes *hw);
+extern void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw);
int orinoco_process_xmit_skb(struct sk_buff *skb,
struct net_device *dev,
diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c
index 88e3c0ebcaad..d7dbc00bcfbe 100644
--- a/drivers/net/wireless/orinoco/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco/orinoco_cs.c
@@ -65,7 +65,7 @@ static void orinoco_cs_release(struct pcmcia_device *link);
static void orinoco_cs_detach(struct pcmcia_device *p_dev);
/********************************************************************/
-/* Device methods */
+/* Device methods */
/********************************************************************/
static int
@@ -89,7 +89,7 @@ orinoco_cs_hard_reset(struct orinoco_private *priv)
}
/********************************************************************/
-/* PCMCIA stuff */
+/* PCMCIA stuff */
/********************************************************************/
static int
@@ -134,7 +134,7 @@ static int
orinoco_cs_config(struct pcmcia_device *link)
{
struct orinoco_private *priv = link->priv;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int ret;
void __iomem *mem;
@@ -239,7 +239,6 @@ static int orinoco_cs_resume(struct pcmcia_device *link)
static const struct pcmcia_device_id orinoco_cs_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), /* 3Com AirConnect PCI 777A */
- PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), /* Lucent Orinoco and old Intersil */
PCMCIA_DEVICE_MANF_CARD(0x016b, 0x0001), /* Ericsson WLAN Card C11 */
PCMCIA_DEVICE_MANF_CARD(0x01eb, 0x080a), /* Nortel Networks eMobility 802.11 Wireless Adapter */
PCMCIA_DEVICE_MANF_CARD(0x0261, 0x0002), /* AirWay 802.11 Adapter (PCMCIA) */
@@ -272,6 +271,7 @@ static const struct pcmcia_device_id orinoco_cs_ids[] = {
PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PCI CARD HARMONY 80211B", 0xc6536a5e, 0x9f494e26),
PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "11Mbps WLAN Card", 0x43d74cb4, 0x579bd91b),
PCMCIA_DEVICE_PROD_ID12("Symbol Technologies", "LA4111 Spectrum24 Wireless LAN PC Card", 0x3f02b4d6, 0x3663cb0e),
+ PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.01", 0xd27deb1a), /* Lucent Orinoco */
#ifdef CONFIG_HERMES_PRISM
/* Only entries that certainly identify Prism chipset */
PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), /* SonicWALL Long Range Wireless Card */
@@ -321,6 +321,9 @@ static const struct pcmcia_device_id orinoco_cs_ids[] = {
PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2),
PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b),
PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39),
+
+ /* This may be Agere or Intersil Firmware */
+ PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002),
#endif
PCMCIA_DEVICE_NULL,
};
diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/orinoco/orinoco_nortel.c
index bc3ea0b67a4f..326396b313a6 100644
--- a/drivers/net/wireless/orinoco/orinoco_nortel.c
+++ b/drivers/net/wireless/orinoco/orinoco_nortel.c
@@ -296,8 +296,7 @@ static struct pci_driver orinoco_nortel_driver = {
static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
" (Tobias Hoffmann & Christoph Jungegger <disdos@traum404.de>)";
MODULE_AUTHOR("Christoph Jungegger <disdos@traum404.de>");
-MODULE_DESCRIPTION
- ("Driver for wireless LAN cards using the Nortel PCI bridge");
+MODULE_DESCRIPTION("Driver for wireless LAN cards using the Nortel PCI bridge");
MODULE_LICENSE("Dual MPL/GPL");
static int __init orinoco_nortel_init(void)
diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c
index 468197f86673..6058c66b844e 100644
--- a/drivers/net/wireless/orinoco/orinoco_pci.c
+++ b/drivers/net/wireless/orinoco/orinoco_pci.c
@@ -6,7 +6,7 @@
* hermes registers, as well as the COR register.
*
* Current maintainers are:
- * Pavel Roskin <proski AT gnu.org>
+ * Pavel Roskin <proski AT gnu.org>
* and David Gibson <hermes AT gibson.dropbear.id.au>
*
* Some of this code is borrowed from orinoco_plx.c
@@ -81,7 +81,7 @@
*/
static int orinoco_pci_cor_reset(struct orinoco_private *priv)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
unsigned long timeout;
u16 reg;
diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c
index 9358f4d2307b..2bac8248a991 100644
--- a/drivers/net/wireless/orinoco/orinoco_plx.c
+++ b/drivers/net/wireless/orinoco/orinoco_plx.c
@@ -4,7 +4,7 @@
* but are connected to the PCI bus by a PLX9052.
*
* Current maintainers are:
- * Pavel Roskin <proski AT gnu.org>
+ * Pavel Roskin <proski AT gnu.org>
* and David Gibson <hermes AT gibson.dropbear.id.au>
*
* (C) Copyright David Gibson, IBM Corp. 2001-2003.
@@ -102,14 +102,14 @@
#define PLX_RESET_TIME (500) /* milliseconds */
#define PLX_INTCSR 0x4c /* Interrupt Control & Status Register */
-#define PLX_INTCSR_INTEN (1<<6) /* Interrupt Enable bit */
+#define PLX_INTCSR_INTEN (1 << 6) /* Interrupt Enable bit */
/*
* Do a soft reset of the card using the Configuration Option Register
*/
static int orinoco_plx_cor_reset(struct orinoco_private *priv)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
struct orinoco_pci_card *card = priv->card;
unsigned long timeout;
u16 reg;
diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c
index 784605f0af15..93159d68ec93 100644
--- a/drivers/net/wireless/orinoco/orinoco_tmd.c
+++ b/drivers/net/wireless/orinoco/orinoco_tmd.c
@@ -59,7 +59,7 @@
*/
static int orinoco_tmd_cor_reset(struct orinoco_private *priv)
{
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
struct orinoco_pci_card *card = priv->card;
unsigned long timeout;
u16 reg;
diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c
index b9aedf18a046..0793e4265b43 100644
--- a/drivers/net/wireless/orinoco/orinoco_usb.c
+++ b/drivers/net/wireless/orinoco/orinoco_usb.c
@@ -199,7 +199,7 @@ MODULE_FIRMWARE("orinoco_ezusb_fw");
#define EZUSB_FRAME_DATA 1
#define EZUSB_FRAME_CONTROL 2
-#define DEF_TIMEOUT (3*HZ)
+#define DEF_TIMEOUT (3 * HZ)
#define BULK_BUF_SIZE 2048
@@ -959,7 +959,7 @@ static int ezusb_access_ltv(struct ezusb_priv *upriv,
return retval;
}
-static int ezusb_write_ltv(hermes_t *hw, int bap, u16 rid,
+static int ezusb_write_ltv(struct hermes *hw, int bap, u16 rid,
u16 length, const void *data)
{
struct ezusb_priv *upriv = hw->priv;
@@ -989,7 +989,7 @@ static int ezusb_write_ltv(hermes_t *hw, int bap, u16 rid,
NULL, 0, NULL);
}
-static int ezusb_read_ltv(hermes_t *hw, int bap, u16 rid,
+static int ezusb_read_ltv(struct hermes *hw, int bap, u16 rid,
unsigned bufsize, u16 *length, void *buf)
{
struct ezusb_priv *upriv = hw->priv;
@@ -1006,7 +1006,7 @@ static int ezusb_read_ltv(hermes_t *hw, int bap, u16 rid,
buf, bufsize, length);
}
-static int ezusb_doicmd_wait(hermes_t *hw, u16 cmd, u16 parm0, u16 parm1,
+static int ezusb_doicmd_wait(struct hermes *hw, u16 cmd, u16 parm0, u16 parm1,
u16 parm2, struct hermes_response *resp)
{
struct ezusb_priv *upriv = hw->priv;
@@ -1028,7 +1028,7 @@ static int ezusb_doicmd_wait(hermes_t *hw, u16 cmd, u16 parm0, u16 parm1,
EZUSB_FRAME_CONTROL, NULL, 0, NULL);
}
-static int ezusb_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
+static int ezusb_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0,
struct hermes_response *resp)
{
struct ezusb_priv *upriv = hw->priv;
@@ -1196,7 +1196,7 @@ static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
struct orinoco_private *priv = ndev_priv(dev);
struct net_device_stats *stats = &priv->stats;
struct ezusb_priv *upriv = priv->card;
- u8 mic[MICHAEL_MIC_LEN+1];
+ u8 mic[MICHAEL_MIC_LEN + 1];
int err = 0;
int tx_control;
unsigned long flags;
@@ -1356,7 +1356,7 @@ static int ezusb_hard_reset(struct orinoco_private *priv)
}
-static int ezusb_init(hermes_t *hw)
+static int ezusb_init(struct hermes *hw)
{
struct ezusb_priv *upriv = hw->priv;
int retval;
@@ -1438,7 +1438,7 @@ static void ezusb_bulk_in_callback(struct urb *urb)
} else if (upriv->dev) {
struct net_device *dev = upriv->dev;
struct orinoco_private *priv = ndev_priv(dev);
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
if (hermes_rid == EZUSB_RID_RX) {
__orinoco_ev_rx(dev, hw);
@@ -1562,7 +1562,7 @@ static const struct net_device_ops ezusb_netdev_ops = {
.ndo_open = orinoco_open,
.ndo_stop = orinoco_stop,
.ndo_start_xmit = ezusb_xmit,
- .ndo_set_multicast_list = orinoco_set_multicast_list,
+ .ndo_set_rx_mode = orinoco_set_multicast_list,
.ndo_change_mtu = orinoco_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
@@ -1575,7 +1575,7 @@ static int ezusb_probe(struct usb_interface *interface,
{
struct usb_device *udev = interface_to_usbdev(interface);
struct orinoco_private *priv;
- hermes_t *hw;
+ struct hermes *hw;
struct ezusb_priv *upriv = NULL;
struct usb_interface_descriptor *iface_desc;
struct usb_endpoint_descriptor *ep;
@@ -1757,7 +1757,7 @@ static struct usb_driver orinoco_driver = {
/* Can't be declared "const" or the whole __initdata section will
* become const */
static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
- " (Manuel Estrada Sainz)";
+ " (Manuel Estrada Sainz)";
static int __init ezusb_module_init(void)
{
@@ -1787,6 +1787,5 @@ module_init(ezusb_module_init);
module_exit(ezusb_module_exit);
MODULE_AUTHOR("Manuel Estrada Sainz");
-MODULE_DESCRIPTION
- ("Driver for Orinoco wireless LAN cards using EZUSB bridge");
+MODULE_DESCRIPTION("Driver for Orinoco wireless LAN cards using EZUSB bridge");
MODULE_LICENSE("Dual MPL/GPL");
diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c
index 81f3673d31d4..6e28ee4e9c52 100644
--- a/drivers/net/wireless/orinoco/spectrum_cs.c
+++ b/drivers/net/wireless/orinoco/spectrum_cs.c
@@ -11,9 +11,9 @@
*
* Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
* Portions based on orinoco_cs.c:
- * Copyright (C) David Gibson, Linuxcare Australia
+ * Copyright (C) David Gibson, Linuxcare Australia
* Portions based on Spectrum24tDnld.c from original spectrum24 driver:
- * Copyright (C) Symbol Technologies.
+ * Copyright (C) Symbol Technologies.
*
* See copyright notice in file main.c.
*/
@@ -125,7 +125,7 @@ failed:
}
/********************************************************************/
-/* Device methods */
+/* Device methods */
/********************************************************************/
static int
@@ -150,7 +150,7 @@ spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle)
}
/********************************************************************/
-/* PCMCIA stuff */
+/* PCMCIA stuff */
/********************************************************************/
static int
@@ -197,7 +197,7 @@ static int
spectrum_cs_config(struct pcmcia_device *link)
{
struct orinoco_private *priv = link->priv;
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int ret;
void __iomem *mem;
diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c
index e793679e2e19..33747e131a96 100644
--- a/drivers/net/wireless/orinoco/wext.c
+++ b/drivers/net/wireless/orinoco/wext.c
@@ -9,6 +9,7 @@
#include <linux/ieee80211.h>
#include <net/iw_handler.h>
#include <net/cfg80211.h>
+#include <net/cfg80211-wext.h>
#include "hermes.h"
#include "hermes_rid.h"
@@ -87,7 +88,7 @@ nomem:
static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev)
{
struct orinoco_private *priv = ndev_priv(dev);
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
struct iw_statistics *wstats = &priv->wstats;
int err;
unsigned long flags;
@@ -448,7 +449,7 @@ static int orinoco_ioctl_setfreq(struct net_device *dev,
}
if ((chan < 1) || (chan > NUM_CHANNELS) ||
- !(priv->channel_mask & (1 << (chan-1))))
+ !(priv->channel_mask & (1 << (chan - 1))))
return -EINVAL;
if (orinoco_lock(priv, &flags) != 0)
@@ -457,7 +458,7 @@ static int orinoco_ioctl_setfreq(struct net_device *dev,
priv->channel = chan;
if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
/* Fast channel change - no commit if successful */
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
HERMES_TEST_SET_CHANNEL,
chan, NULL);
@@ -492,7 +493,7 @@ static int orinoco_ioctl_getsens(struct net_device *dev,
char *extra)
{
struct orinoco_private *priv = ndev_priv(dev);
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
u16 val;
int err;
unsigned long flags;
@@ -668,7 +669,7 @@ static int orinoco_ioctl_getpower(struct net_device *dev,
char *extra)
{
struct orinoco_private *priv = ndev_priv(dev);
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int err = 0;
u16 enable, period, timeout, mcast;
unsigned long flags;
@@ -873,7 +874,7 @@ static int orinoco_ioctl_set_auth(struct net_device *dev,
union iwreq_data *wrqu, char *extra)
{
struct orinoco_private *priv = ndev_priv(dev);
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
struct iw_param *param = &wrqu->param;
unsigned long flags;
int ret = -EINPROGRESS;
@@ -1269,7 +1270,7 @@ static int orinoco_ioctl_getrid(struct net_device *dev,
char *extra)
{
struct orinoco_private *priv = ndev_priv(dev);
- hermes_t *hw = &priv->hw;
+ struct hermes *hw = &priv->hw;
int rid = data->flags;
u16 length;
int err;
diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c
index 54cc0bba66b9..fa8ce5104781 100644
--- a/drivers/net/wireless/p54/eeprom.c
+++ b/drivers/net/wireless/p54/eeprom.c
@@ -24,6 +24,7 @@
#include <net/mac80211.h>
#include <linux/crc-ccitt.h>
+#include <linux/export.h>
#include "p54.h"
#include "eeprom.h"
@@ -145,6 +146,7 @@ static int p54_fill_band_bitrates(struct ieee80211_hw *dev,
static int p54_generate_band(struct ieee80211_hw *dev,
struct p54_channel_list *list,
+ unsigned int *chan_num,
enum ieee80211_band band)
{
struct p54_common *priv = dev->priv;
@@ -190,7 +192,14 @@ static int p54_generate_band(struct ieee80211_hw *dev,
tmp->channels[j].band = chan->band;
tmp->channels[j].center_freq = chan->freq;
+ priv->survey[*chan_num].channel = &tmp->channels[j];
+ priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM |
+ SURVEY_INFO_CHANNEL_TIME |
+ SURVEY_INFO_CHANNEL_TIME_BUSY |
+ SURVEY_INFO_CHANNEL_TIME_TX;
+ tmp->channels[j].hw_value = (*chan_num);
j++;
+ (*chan_num)++;
}
if (j == 0) {
@@ -263,7 +272,7 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
struct p54_channel_list *list;
- unsigned int i, j, max_channel_num;
+ unsigned int i, j, k, max_channel_num;
int ret = 0;
u16 freq;
@@ -283,6 +292,13 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev)
ret = -ENOMEM;
goto free;
}
+ priv->chan_num = max_channel_num;
+ priv->survey = kzalloc(sizeof(struct survey_info) * max_channel_num,
+ GFP_KERNEL);
+ if (!priv->survey) {
+ ret = -ENOMEM;
+ goto free;
+ }
list->max_entries = max_channel_num;
list->channels = kzalloc(sizeof(struct p54_channel_entry) *
@@ -321,8 +337,9 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev)
sort(list->channels, list->entries, sizeof(struct p54_channel_entry),
p54_compare_channels, NULL);
+ k = 0;
for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) {
- if (p54_generate_band(dev, list, i) == 0)
+ if (p54_generate_band(dev, list, &k, i) == 0)
j++;
}
if (j == 0) {
@@ -335,6 +352,10 @@ free:
kfree(list->channels);
kfree(list);
}
+ if (ret) {
+ kfree(priv->survey);
+ priv->survey = NULL;
+ }
return ret;
}
@@ -853,10 +874,12 @@ err:
kfree(priv->output_limit);
kfree(priv->curve_data);
kfree(priv->rssi_db);
+ kfree(priv->survey);
priv->iq_autocal = NULL;
priv->output_limit = NULL;
priv->curve_data = NULL;
priv->rssi_db = NULL;
+ priv->survey = NULL;
wiphy_err(dev->wiphy, "eeprom parse failed!\n");
return err;
diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/p54/fwio.c
index b6a061cbbdec..18e82b31afa6 100644
--- a/drivers/net/wireless/p54/fwio.c
+++ b/drivers/net/wireless/p54/fwio.c
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
+#include <linux/export.h>
#include <net/mac80211.h>
@@ -385,6 +386,7 @@ int p54_setup_mac(struct p54_common *priv)
setup->v2.osc_start_delay = cpu_to_le16(65535);
}
p54_tx(priv, skb);
+ priv->phy_idle = mode == P54_FILTER_TYPE_HIBERNATE;
return 0;
}
@@ -626,6 +628,7 @@ int p54_set_ps(struct p54_common *priv)
psm->exclude[0] = WLAN_EID_TIM;
p54_tx(priv, skb);
+ priv->phy_ps = mode != P54_PSM_CAM;
return 0;
}
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
index a5a6d9e647bb..db4d9a02f264 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/p54/main.c
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
+#include <linux/module.h>
#include <net/mac80211.h>
@@ -204,13 +205,11 @@ static void p54_stop(struct ieee80211_hw *dev)
struct p54_common *priv = dev->priv;
int i;
- mutex_lock(&priv->conf_mutex);
priv->mode = NL80211_IFTYPE_UNSPECIFIED;
priv->softled_state = 0;
- p54_set_leds(priv);
-
cancel_delayed_work_sync(&priv->work);
-
+ mutex_lock(&priv->conf_mutex);
+ p54_set_leds(priv);
priv->stop(dev);
skb_queue_purge(&priv->tx_pending);
skb_queue_purge(&priv->tx_queue);
@@ -278,6 +277,42 @@ static void p54_remove_interface(struct ieee80211_hw *dev,
mutex_unlock(&priv->conf_mutex);
}
+static int p54_wait_for_stats(struct ieee80211_hw *dev)
+{
+ struct p54_common *priv = dev->priv;
+ int ret;
+
+ priv->update_stats = true;
+ ret = p54_fetch_statistics(priv);
+ if (ret)
+ return ret;
+
+ ret = wait_for_completion_interruptible_timeout(&priv->stat_comp, HZ);
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static void p54_reset_stats(struct p54_common *priv)
+{
+ struct ieee80211_channel *chan = priv->curchan;
+
+ if (chan) {
+ struct survey_info *info = &priv->survey[chan->hw_value];
+
+ /* only reset channel statistics, don't touch .filled, etc. */
+ info->channel_time = 0;
+ info->channel_time_busy = 0;
+ info->channel_time_tx = 0;
+ }
+
+ priv->update_stats = true;
+ priv->survey_raw.active = 0;
+ priv->survey_raw.cca = 0;
+ priv->survey_raw.tx = 0;
+}
+
static int p54_config(struct ieee80211_hw *dev, u32 changed)
{
int ret = 0;
@@ -288,19 +323,36 @@ static int p54_config(struct ieee80211_hw *dev, u32 changed)
if (changed & IEEE80211_CONF_CHANGE_POWER)
priv->output_power = conf->power_level << 2;
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ struct ieee80211_channel *oldchan;
+ WARN_ON(p54_wait_for_stats(dev));
+ oldchan = priv->curchan;
+ priv->curchan = NULL;
ret = p54_scan(priv, P54_SCAN_EXIT, 0);
- if (ret)
+ if (ret) {
+ priv->curchan = oldchan;
goto out;
+ }
+ /*
+ * TODO: Use the LM_SCAN_TRAP to determine the current
+ * operating channel.
+ */
+ priv->curchan = priv->hw->conf.channel;
+ p54_reset_stats(priv);
+ WARN_ON(p54_fetch_statistics(priv));
}
if (changed & IEEE80211_CONF_CHANGE_PS) {
+ WARN_ON(p54_wait_for_stats(dev));
ret = p54_set_ps(priv);
if (ret)
goto out;
+ WARN_ON(p54_wait_for_stats(dev));
}
if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+ WARN_ON(p54_wait_for_stats(dev));
ret = p54_setup_mac(priv);
if (ret)
goto out;
+ WARN_ON(p54_wait_for_stats(dev));
}
out:
@@ -353,7 +405,8 @@ static void p54_configure_filter(struct ieee80211_hw *dev,
p54_set_groupfilter(priv);
}
-static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue,
+static int p54_conf_tx(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct p54_common *priv = dev->priv;
@@ -384,7 +437,9 @@ static void p54_work(struct work_struct *work)
* 2. cancel stuck frames / reset the device if necessary.
*/
- p54_fetch_statistics(priv);
+ mutex_lock(&priv->conf_mutex);
+ WARN_ON_ONCE(p54_fetch_statistics(priv));
+ mutex_unlock(&priv->conf_mutex);
}
static int p54_get_stats(struct ieee80211_hw *dev,
@@ -541,16 +596,47 @@ static int p54_get_survey(struct ieee80211_hw *dev, int idx,
struct survey_info *survey)
{
struct p54_common *priv = dev->priv;
- struct ieee80211_conf *conf = &dev->conf;
+ struct ieee80211_channel *chan;
+ int err, tries;
+ bool in_use = false;
- if (idx != 0)
+ if (idx >= priv->chan_num)
return -ENOENT;
- survey->channel = conf->channel;
- survey->filled = SURVEY_INFO_NOISE_DBM;
- survey->noise = clamp_t(s8, priv->noise, -128, 127);
+#define MAX_TRIES 1
+ for (tries = 0; tries < MAX_TRIES; tries++) {
+ chan = priv->curchan;
+ if (chan && chan->hw_value == idx) {
+ mutex_lock(&priv->conf_mutex);
+ err = p54_wait_for_stats(dev);
+ mutex_unlock(&priv->conf_mutex);
+ if (err)
+ return err;
+
+ in_use = true;
+ }
- return 0;
+ memcpy(survey, &priv->survey[idx], sizeof(*survey));
+
+ if (in_use) {
+ /* test if the reported statistics are valid. */
+ if (survey->channel_time != 0) {
+ survey->filled |= SURVEY_INFO_IN_USE;
+ } else {
+ /*
+ * hw/fw has not accumulated enough sample sets.
+ * Wait for 100ms, this ought to be enough to
+ * to get at least one non-null set of channel
+ * usage statistics.
+ */
+ msleep(100);
+ continue;
+ }
+ }
+ return 0;
+ }
+ return -ETIMEDOUT;
+#undef MAX_TRIES
}
static unsigned int p54_flush_count(struct p54_common *priv)
@@ -686,11 +772,14 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
mutex_init(&priv->conf_mutex);
mutex_init(&priv->eeprom_mutex);
+ init_completion(&priv->stat_comp);
init_completion(&priv->eeprom_comp);
init_completion(&priv->beacon_comp);
INIT_DELAYED_WORK(&priv->work, p54_work);
memset(&priv->mc_maclist[0], ~0, ETH_ALEN);
+ priv->curchan = NULL;
+ p54_reset_stats(priv);
return dev;
}
EXPORT_SYMBOL_GPL(p54_init_common);
@@ -730,11 +819,13 @@ void p54_free_common(struct ieee80211_hw *dev)
kfree(priv->curve_data);
kfree(priv->rssi_db);
kfree(priv->used_rxkeys);
+ kfree(priv->survey);
priv->iq_autocal = NULL;
priv->output_limit = NULL;
priv->curve_data = NULL;
priv->rssi_db = NULL;
priv->used_rxkeys = NULL;
+ priv->survey = NULL;
ieee80211_free_hw(dev);
}
EXPORT_SYMBOL_GPL(p54_free_common);
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h
index 799d05e12595..452fa3a64aa1 100644
--- a/drivers/net/wireless/p54/p54.h
+++ b/drivers/net/wireless/p54/p54.h
@@ -199,6 +199,22 @@ struct p54_common {
u8 tx_diversity_mask;
unsigned int output_power;
struct p54_rssi_db_entry *cur_rssi;
+ struct ieee80211_channel *curchan;
+ struct survey_info *survey;
+ unsigned int chan_num;
+ struct completion stat_comp;
+ bool update_stats;
+ struct {
+ unsigned int timestamp;
+ unsigned int cached_cca;
+ unsigned int cached_tx;
+ unsigned int cached_rssi;
+ u64 active;
+ u64 cca;
+ u64 tx;
+ u64 rssi;
+ } survey_raw;
+
int noise;
/* calibration, output power limit and rssi<->dBm conversation data */
struct pda_iq_autocal_entry *iq_autocal;
@@ -220,6 +236,8 @@ struct p54_common {
u32 basic_rate_mask;
u16 aid;
u8 coverage_class;
+ bool phy_idle;
+ bool phy_ps;
bool powersave_override;
__le32 beacon_req_id;
struct completion beacon_comp;
diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c
index 1b753173680f..b1f51a215792 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/p54/p54pci.c
@@ -20,6 +20,7 @@
#include <linux/etherdevice.h>
#include <linux/delay.h>
#include <linux/completion.h>
+#include <linux/module.h>
#include <net/mac80211.h>
#include "p54.h"
diff --git a/drivers/net/wireless/p54/p54pci.h b/drivers/net/wireless/p54/p54pci.h
index ee9bc62a4fa2..7aa509f7e387 100644
--- a/drivers/net/wireless/p54/p54pci.h
+++ b/drivers/net/wireless/p54/p54pci.h
@@ -1,5 +1,6 @@
#ifndef P54PCI_H
#define P54PCI_H
+#include <linux/interrupt.h>
/*
* Defines for PCI based mac80211 Prism54 driver
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c
index 6d9204fef90b..f18df82eeb92 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/p54/p54spi.c
@@ -41,7 +41,6 @@
#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */
MODULE_FIRMWARE("3826.arm");
-MODULE_ALIAS("stlc45xx");
/*
* gpios should be handled in board files and provided via platform data,
@@ -738,3 +737,4 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
MODULE_ALIAS("spi:cx3110x");
MODULE_ALIAS("spi:p54spi");
+MODULE_ALIAS("spi:stlc45xx");
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index a8f3bc740dfa..9b6096866427 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -20,6 +20,7 @@
#include <linux/etherdevice.h>
#include <linux/delay.h>
#include <linux/crc32.h>
+#include <linux/module.h>
#include <net/mac80211.h>
#include "p54.h"
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c
index 042842e704de..6ed9c323e3cb 100644
--- a/drivers/net/wireless/p54/txrx.c
+++ b/drivers/net/wireless/p54/txrx.c
@@ -16,9 +16,11 @@
* published by the Free Software Foundation.
*/
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
+#include <asm/div64.h>
#include <net/mac80211.h>
@@ -507,6 +509,8 @@ static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb)
struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
struct p54_statistics *stats = (struct p54_statistics *) hdr->data;
struct sk_buff *tmp;
+ struct ieee80211_channel *chan;
+ unsigned int i, rssi, tx, cca, dtime, dtotal, dcca, dtx, drssi, unit;
u32 tsf32;
if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
@@ -523,8 +527,75 @@ static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb)
priv->noise = p54_rssi_to_dbm(priv, le32_to_cpu(stats->noise));
+ /*
+ * STSW450X LMAC API page 26 - 3.8 Statistics
+ * "The exact measurement period can be derived from the
+ * timestamp member".
+ */
+ dtime = tsf32 - priv->survey_raw.timestamp;
+
+ /*
+ * STSW450X LMAC API page 26 - 3.8.1 Noise histogram
+ * The LMAC samples RSSI, CCA and transmit state at regular
+ * periods (typically 8 times per 1k [as in 1024] usec).
+ */
+ cca = le32_to_cpu(stats->sample_cca);
+ tx = le32_to_cpu(stats->sample_tx);
+ rssi = 0;
+ for (i = 0; i < ARRAY_SIZE(stats->sample_noise); i++)
+ rssi += le32_to_cpu(stats->sample_noise[i]);
+
+ dcca = cca - priv->survey_raw.cached_cca;
+ drssi = rssi - priv->survey_raw.cached_rssi;
+ dtx = tx - priv->survey_raw.cached_tx;
+ dtotal = dcca + drssi + dtx;
+
+ /*
+ * update statistics when more than a second is over since the
+ * last call, or when a update is badly needed.
+ */
+ if (dtotal && (priv->update_stats || dtime >= USEC_PER_SEC) &&
+ dtime >= dtotal) {
+ priv->survey_raw.timestamp = tsf32;
+ priv->update_stats = false;
+ unit = dtime / dtotal;
+
+ if (dcca) {
+ priv->survey_raw.cca += dcca * unit;
+ priv->survey_raw.cached_cca = cca;
+ }
+ if (dtx) {
+ priv->survey_raw.tx += dtx * unit;
+ priv->survey_raw.cached_tx = tx;
+ }
+ if (drssi) {
+ priv->survey_raw.rssi += drssi * unit;
+ priv->survey_raw.cached_rssi = rssi;
+ }
+
+ /* 1024 usec / 8 times = 128 usec / time */
+ if (!(priv->phy_ps || priv->phy_idle))
+ priv->survey_raw.active += dtotal * unit;
+ else
+ priv->survey_raw.active += (dcca + dtx) * unit;
+ }
+
+ chan = priv->curchan;
+ if (chan) {
+ struct survey_info *survey = &priv->survey[chan->hw_value];
+ survey->noise = clamp_t(s8, priv->noise, -128, 127);
+ survey->channel_time = priv->survey_raw.active;
+ survey->channel_time_tx = priv->survey_raw.tx;
+ survey->channel_time_busy = priv->survey_raw.tx +
+ priv->survey_raw.cca;
+ do_div(survey->channel_time, 1024);
+ do_div(survey->channel_time_tx, 1024);
+ do_div(survey->channel_time_busy, 1024);
+ }
+
tmp = p54_find_and_unlink_skb(priv, hdr->req_id);
dev_kfree_skb_any(tmp);
+ complete(&priv->stat_comp);
}
static void p54_rx_trap(struct p54_common *priv, struct sk_buff *skb)
@@ -619,7 +690,7 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb,
if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
*flags |= P54_HDR_FLAG_DATA_OUT_SEQNR;
- if (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)
+ if (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)
*flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL;
if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c
index ec2c75d77cea..5d0f61508a2e 100644
--- a/drivers/net/wireless/prism54/islpci_dev.c
+++ b/drivers/net/wireless/prism54/islpci_dev.c
@@ -18,6 +18,7 @@
*
*/
+#include <linux/hardirq.h>
#include <linux/module.h>
#include <linux/slab.h>
diff --git a/drivers/net/wireless/prism54/islpci_dev.h b/drivers/net/wireless/prism54/islpci_dev.h
index c4d0f19b7cbc..c40403877f97 100644
--- a/drivers/net/wireless/prism54/islpci_dev.h
+++ b/drivers/net/wireless/prism54/islpci_dev.h
@@ -22,6 +22,7 @@
#ifndef _ISLPCI_DEV_H
#define _ISLPCI_DEV_H
+#include <linux/irqreturn.h>
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <net/iw_handler.h>
diff --git a/drivers/net/wireless/prism54/islpci_hotplug.c b/drivers/net/wireless/prism54/islpci_hotplug.c
index b5e64d71b7a6..9e68e0cb718e 100644
--- a/drivers/net/wireless/prism54/islpci_hotplug.c
+++ b/drivers/net/wireless/prism54/islpci_hotplug.c
@@ -17,6 +17,7 @@
*
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 2a06ebcd67c5..0021e4948512 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -273,7 +273,7 @@ static const struct net_device_ops ray_netdev_ops = {
.ndo_start_xmit = ray_dev_start_xmit,
.ndo_set_config = ray_dev_config,
.ndo_get_stats = ray_get_stats,
- .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 29f938930667..0c13840a7de5 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -36,13 +36,11 @@
#include <linux/mii.h>
#include <linux/usb.h>
#include <linux/usb/cdc.h>
-#include <linux/wireless.h>
#include <linux/ieee80211.h>
#include <linux/if_arp.h>
#include <linux/ctype.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
-#include <net/iw_handler.h>
#include <net/cfg80211.h>
#include <linux/usb/usbnet.h>
#include <linux/usb/rndis_host.h>
@@ -3392,7 +3390,7 @@ static const struct net_device_ops rndis_wlan_netdev_ops = {
.ndo_tx_timeout = usbnet_tx_timeout,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = rndis_wlan_set_multicast_list,
+ .ndo_set_rx_mode = rndis_wlan_set_multicast_list,
};
static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf)
diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig
index b2f8b8fd4d2d..a0a7854facc0 100644
--- a/drivers/net/wireless/rt2x00/Kconfig
+++ b/drivers/net/wireless/rt2x00/Kconfig
@@ -83,14 +83,12 @@ config RT2800PCI_RT33XX
config RT2800PCI_RT35XX
bool "rt2800pci - Include support for rt35xx devices (EXPERIMENTAL)"
depends on EXPERIMENTAL
- default n
+ default y
---help---
This adds support for rt35xx wireless chipset family to the
rt2800pci driver.
Supported chips: RT3060, RT3062, RT3562, RT3592
- Support for these devices is non-functional at the moment and is
- intended for testers and developers.
config RT2800PCI_RT53XX
bool "rt2800pci - Include support for rt53xx devices (EXPERIMENTAL)"
@@ -154,15 +152,12 @@ config RT2800USB_RT33XX
config RT2800USB_RT35XX
bool "rt2800usb - Include support for rt35xx devices (EXPERIMENTAL)"
depends on EXPERIMENTAL
- default n
+ default y
---help---
This adds support for rt35xx wireless chipset family to the
rt2800usb driver.
Supported chips: RT3572
- Support for these devices is non-functional at the moment and is
- intended for testers and developers.
-
config RT2800USB_RT53XX
bool "rt2800usb - Include support for rt53xx devices (EXPERIMENTAL)"
depends on EXPERIMENTAL
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index 937f9e8bf05f..3a6b40239bc1 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -645,11 +645,6 @@ static void rt2400pci_start_queue(struct data_queue *queue)
rt2x00pci_register_write(rt2x00dev, RXCSR0, reg);
break;
case QID_BEACON:
- /*
- * Allow the tbtt tasklet to be scheduled.
- */
- tasklet_enable(&rt2x00dev->tbtt_tasklet);
-
rt2x00pci_register_read(rt2x00dev, CSR14, &reg);
rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
rt2x00_set_field32(&reg, CSR14_TBCN, 1);
@@ -715,7 +710,7 @@ static void rt2400pci_stop_queue(struct data_queue *queue)
/*
* Wait for possibly running tbtt tasklets.
*/
- tasklet_disable(&rt2x00dev->tbtt_tasklet);
+ tasklet_kill(&rt2x00dev->tbtt_tasklet);
break;
default:
break;
@@ -982,12 +977,6 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
if (state == STATE_RADIO_IRQ_ON) {
rt2x00pci_register_read(rt2x00dev, CSR7, &reg);
rt2x00pci_register_write(rt2x00dev, CSR7, reg);
-
- /*
- * Enable tasklets.
- */
- tasklet_enable(&rt2x00dev->txstatus_tasklet);
- tasklet_enable(&rt2x00dev->rxdone_tasklet);
}
/*
@@ -1011,8 +1000,9 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
* Ensure that all tasklets are finished before
* disabling the interrupts.
*/
- tasklet_disable(&rt2x00dev->txstatus_tasklet);
- tasklet_disable(&rt2x00dev->rxdone_tasklet);
+ tasklet_kill(&rt2x00dev->txstatus_tasklet);
+ tasklet_kill(&rt2x00dev->rxdone_tasklet);
+ tasklet_kill(&rt2x00dev->tbtt_tasklet);
}
}
@@ -1249,7 +1239,7 @@ static void rt2400pci_fill_rxdone(struct queue_entry *entry,
* call, we must decrease the higher 32bits with 1 to get
* to correct value.
*/
- tsf = rt2x00dev->ops->hw->get_tsf(rt2x00dev->hw);
+ tsf = rt2x00dev->ops->hw->get_tsf(rt2x00dev->hw, NULL);
rx_low = rt2x00_get_field32(word4, RXD_W4_RX_END_TIME);
rx_high = upper_32_bits(tsf);
@@ -1347,22 +1337,25 @@ static void rt2400pci_txstatus_tasklet(unsigned long data)
/*
* Enable all TXDONE interrupts again.
*/
- spin_lock_irq(&rt2x00dev->irqmask_lock);
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) {
+ spin_lock_irq(&rt2x00dev->irqmask_lock);
- rt2x00pci_register_read(rt2x00dev, CSR8, &reg);
- rt2x00_set_field32(&reg, CSR8_TXDONE_TXRING, 0);
- rt2x00_set_field32(&reg, CSR8_TXDONE_ATIMRING, 0);
- rt2x00_set_field32(&reg, CSR8_TXDONE_PRIORING, 0);
- rt2x00pci_register_write(rt2x00dev, CSR8, reg);
+ rt2x00pci_register_read(rt2x00dev, CSR8, &reg);
+ rt2x00_set_field32(&reg, CSR8_TXDONE_TXRING, 0);
+ rt2x00_set_field32(&reg, CSR8_TXDONE_ATIMRING, 0);
+ rt2x00_set_field32(&reg, CSR8_TXDONE_PRIORING, 0);
+ rt2x00pci_register_write(rt2x00dev, CSR8, reg);
- spin_unlock_irq(&rt2x00dev->irqmask_lock);
+ spin_unlock_irq(&rt2x00dev->irqmask_lock);
+ }
}
static void rt2400pci_tbtt_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt2x00lib_beacondone(rt2x00dev);
- rt2400pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE);
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ rt2400pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE);
}
static void rt2400pci_rxdone_tasklet(unsigned long data)
@@ -1370,7 +1363,7 @@ static void rt2400pci_rxdone_tasklet(unsigned long data)
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
if (rt2x00pci_rxdone(rt2x00dev))
tasklet_schedule(&rt2x00dev->rxdone_tasklet);
- else
+ else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
rt2400pci_enable_interrupt(rt2x00dev, CSR8_RXDONE);
}
@@ -1655,7 +1648,8 @@ static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev)
/*
* IEEE80211 stack callback functions.
*/
-static int rt2400pci_conf_tx(struct ieee80211_hw *hw, u16 queue,
+static int rt2400pci_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
@@ -1668,7 +1662,7 @@ static int rt2400pci_conf_tx(struct ieee80211_hw *hw, u16 queue,
if (queue != 0)
return -EINVAL;
- if (rt2x00mac_conf_tx(hw, queue, params))
+ if (rt2x00mac_conf_tx(hw, vif, queue, params))
return -EINVAL;
/*
@@ -1680,7 +1674,8 @@ static int rt2400pci_conf_tx(struct ieee80211_hw *hw, u16 queue,
return 0;
}
-static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw)
+static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
u64 tsf;
@@ -1723,6 +1718,7 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = {
.set_antenna = rt2x00mac_set_antenna,
.get_antenna = rt2x00mac_get_antenna,
.get_ringparam = rt2x00mac_get_ringparam,
+ .tx_frames_pending = rt2x00mac_tx_frames_pending,
};
static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = {
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index d27d7b8ba3b6..dcc0e1fcca77 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -735,11 +735,6 @@ static void rt2500pci_start_queue(struct data_queue *queue)
rt2x00pci_register_write(rt2x00dev, RXCSR0, reg);
break;
case QID_BEACON:
- /*
- * Allow the tbtt tasklet to be scheduled.
- */
- tasklet_enable(&rt2x00dev->tbtt_tasklet);
-
rt2x00pci_register_read(rt2x00dev, CSR14, &reg);
rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
rt2x00_set_field32(&reg, CSR14_TBCN, 1);
@@ -805,7 +800,7 @@ static void rt2500pci_stop_queue(struct data_queue *queue)
/*
* Wait for possibly running tbtt tasklets.
*/
- tasklet_disable(&rt2x00dev->tbtt_tasklet);
+ tasklet_kill(&rt2x00dev->tbtt_tasklet);
break;
default:
break;
@@ -1137,12 +1132,6 @@ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
if (state == STATE_RADIO_IRQ_ON) {
rt2x00pci_register_read(rt2x00dev, CSR7, &reg);
rt2x00pci_register_write(rt2x00dev, CSR7, reg);
-
- /*
- * Enable tasklets.
- */
- tasklet_enable(&rt2x00dev->txstatus_tasklet);
- tasklet_enable(&rt2x00dev->rxdone_tasklet);
}
/*
@@ -1165,8 +1154,9 @@ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
/*
* Ensure that all tasklets are finished.
*/
- tasklet_disable(&rt2x00dev->txstatus_tasklet);
- tasklet_disable(&rt2x00dev->rxdone_tasklet);
+ tasklet_kill(&rt2x00dev->txstatus_tasklet);
+ tasklet_kill(&rt2x00dev->rxdone_tasklet);
+ tasklet_kill(&rt2x00dev->tbtt_tasklet);
}
}
@@ -1479,22 +1469,25 @@ static void rt2500pci_txstatus_tasklet(unsigned long data)
/*
* Enable all TXDONE interrupts again.
*/
- spin_lock_irq(&rt2x00dev->irqmask_lock);
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) {
+ spin_lock_irq(&rt2x00dev->irqmask_lock);
- rt2x00pci_register_read(rt2x00dev, CSR8, &reg);
- rt2x00_set_field32(&reg, CSR8_TXDONE_TXRING, 0);
- rt2x00_set_field32(&reg, CSR8_TXDONE_ATIMRING, 0);
- rt2x00_set_field32(&reg, CSR8_TXDONE_PRIORING, 0);
- rt2x00pci_register_write(rt2x00dev, CSR8, reg);
+ rt2x00pci_register_read(rt2x00dev, CSR8, &reg);
+ rt2x00_set_field32(&reg, CSR8_TXDONE_TXRING, 0);
+ rt2x00_set_field32(&reg, CSR8_TXDONE_ATIMRING, 0);
+ rt2x00_set_field32(&reg, CSR8_TXDONE_PRIORING, 0);
+ rt2x00pci_register_write(rt2x00dev, CSR8, reg);
- spin_unlock_irq(&rt2x00dev->irqmask_lock);
+ spin_unlock_irq(&rt2x00dev->irqmask_lock);
+ }
}
static void rt2500pci_tbtt_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt2x00lib_beacondone(rt2x00dev);
- rt2500pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE);
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ rt2500pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE);
}
static void rt2500pci_rxdone_tasklet(unsigned long data)
@@ -1502,7 +1495,7 @@ static void rt2500pci_rxdone_tasklet(unsigned long data)
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
if (rt2x00pci_rxdone(rt2x00dev))
tasklet_schedule(&rt2x00dev->rxdone_tasklet);
- else
+ else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
rt2500pci_enable_interrupt(rt2x00dev, CSR8_RXDONE);
}
@@ -1973,7 +1966,8 @@ static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev)
/*
* IEEE80211 stack callback functions.
*/
-static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw)
+static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
u64 tsf;
@@ -2016,6 +2010,7 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = {
.set_antenna = rt2x00mac_set_antenna,
.get_antenna = rt2x00mac_get_antenna,
.get_ringparam = rt2x00mac_get_ringparam,
+ .tx_frames_pending = rt2x00mac_tx_frames_pending,
};
static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = {
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 15237c275486..53c5f878f61d 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -1827,6 +1827,7 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = {
.set_antenna = rt2x00mac_set_antenna,
.get_antenna = rt2x00mac_get_antenna,
.get_ringparam = rt2x00mac_get_ringparam,
+ .tx_frames_pending = rt2x00mac_tx_frames_pending,
};
static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index f67bc9b31b28..4778620347c4 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -664,6 +664,9 @@
/*
* LED_CFG: LED control
+ * ON_PERIOD: LED active time (ms) during TX (only used for LED mode 1)
+ * OFF_PERIOD: LED inactive time (ms) during TX (only used for LED mode 1)
+ * SLOW_BLINK_PERIOD: LED blink interval in seconds (only used for LED mode 2)
* color LED's:
* 0: off
* 1: blinking upon TX2
@@ -1740,6 +1743,7 @@ struct mac_iveiv_entry {
/*
* BBP 3: RX Antenna
*/
+#define BBP3_RX_ADC FIELD8(0x03)
#define BBP3_RX_ANTENNA FIELD8(0x18)
#define BBP3_HT40_MINUS FIELD8(0x20)
@@ -1783,6 +1787,8 @@ struct mac_iveiv_entry {
#define RFCSR1_TX0_PD FIELD8(0x08)
#define RFCSR1_RX1_PD FIELD8(0x10)
#define RFCSR1_TX1_PD FIELD8(0x20)
+#define RFCSR1_RX2_PD FIELD8(0x40)
+#define RFCSR1_TX2_PD FIELD8(0x80)
/*
* RFCSR 2:
@@ -1790,15 +1796,25 @@ struct mac_iveiv_entry {
#define RFCSR2_RESCAL_EN FIELD8(0x80)
/*
+ * FRCSR 5:
+ */
+#define RFCSR5_R1 FIELD8(0x0c)
+
+/*
* RFCSR 6:
*/
#define RFCSR6_R1 FIELD8(0x03)
#define RFCSR6_R2 FIELD8(0x40)
+#define RFCSR6_TXDIV FIELD8(0x0c)
/*
* RFCSR 7:
*/
#define RFCSR7_RF_TUNING FIELD8(0x01)
+#define RFCSR7_R02 FIELD8(0x07)
+#define RFCSR7_R3 FIELD8(0x08)
+#define RFCSR7_R45 FIELD8(0x30)
+#define RFCSR7_R67 FIELD8(0xc0)
/*
* RFCSR 11:
@@ -1809,11 +1825,13 @@ struct mac_iveiv_entry {
* RFCSR 12:
*/
#define RFCSR12_TX_POWER FIELD8(0x1f)
+#define RFCSR12_DR0 FIELD8(0xe0)
/*
* RFCSR 13:
*/
#define RFCSR13_TX_POWER FIELD8(0x1f)
+#define RFCSR13_DR0 FIELD8(0xe0)
/*
* RFCSR 15:
@@ -2256,6 +2274,7 @@ struct mac_iveiv_entry {
#define MCU_ANT_SELECT 0X73
#define MCU_BBP_SIGNAL 0x80
#define MCU_POWER_SAVE 0x83
+#define MCU_BAND_SELECT 0x91
/*
* MCU mailbox tokens
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 2a6aa85cc6c9..3f183a15186e 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -401,7 +401,8 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
return -EBUSY;
if (rt2x00_is_pci(rt2x00dev)) {
- if (rt2x00_rt(rt2x00dev, RT5390)) {
+ if (rt2x00_rt(rt2x00dev, RT3572) ||
+ rt2x00_rt(rt2x00dev, RT5390)) {
rt2800_register_read(rt2x00dev, AUX_CTRL, &reg);
rt2x00_set_field32(&reg, AUX_CTRL_FORCE_PCIE_CLK, 1);
rt2x00_set_field32(&reg, AUX_CTRL_WAKE_PCIE_EN, 1);
@@ -492,7 +493,7 @@ void rt2800_write_tx_data(struct queue_entry *entry,
rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->u.ht.ba_size);
rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID,
test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ?
- txdesc->key_idx : 0xff);
+ txdesc->key_idx : txdesc->u.ht.wcid);
rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
txdesc->length);
rt2x00_set_field32(&word, TXWI_W1_PACKETID_QUEUE, entry->queue->qid);
@@ -600,50 +601,7 @@ void rt2800_process_rxwi(struct queue_entry *entry,
}
EXPORT_SYMBOL_GPL(rt2800_process_rxwi);
-static bool rt2800_txdone_entry_check(struct queue_entry *entry, u32 reg)
-{
- __le32 *txwi;
- u32 word;
- int wcid, ack, pid;
- int tx_wcid, tx_ack, tx_pid;
-
- wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
- ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
- pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
-
- /*
- * This frames has returned with an IO error,
- * so the status report is not intended for this
- * frame.
- */
- if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) {
- rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
- return false;
- }
-
- /*
- * Validate if this TX status report is intended for
- * this entry by comparing the WCID/ACK/PID fields.
- */
- txwi = rt2800_drv_get_txwi(entry);
-
- rt2x00_desc_read(txwi, 1, &word);
- tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
- tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK);
- tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID);
-
- if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid)) {
- WARNING(entry->queue->rt2x00dev,
- "TX status report missed for queue %d entry %d\n",
- entry->queue->qid, entry->entry_idx);
- rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
- return false;
- }
-
- return true;
-}
-
-void rt2800_txdone_entry(struct queue_entry *entry, u32 status)
+void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi)
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
@@ -651,13 +609,11 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status)
u32 word;
u16 mcs, real_mcs;
int aggr, ampdu;
- __le32 *txwi;
/*
* Obtain the status about this packet.
*/
txdesc.flags = 0;
- txwi = rt2800_drv_get_txwi(entry);
rt2x00_desc_read(txwi, 0, &word);
mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
@@ -725,45 +681,6 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status)
}
EXPORT_SYMBOL_GPL(rt2800_txdone_entry);
-void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
-{
- struct data_queue *queue;
- struct queue_entry *entry;
- u32 reg;
- u8 qid;
-
- while (kfifo_get(&rt2x00dev->txstatus_fifo, &reg)) {
-
- /* TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus
- * qid is guaranteed to be one of the TX QIDs
- */
- qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE);
- queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
- if (unlikely(!queue)) {
- WARNING(rt2x00dev, "Got TX status for an unavailable "
- "queue %u, dropping\n", qid);
- continue;
- }
-
- /*
- * Inside each queue, we process each entry in a chronological
- * order. We first check that the queue is not empty.
- */
- entry = NULL;
- while (!rt2x00queue_empty(queue)) {
- entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
- if (rt2800_txdone_entry_check(entry, reg))
- break;
- }
-
- if (!entry || rt2x00queue_empty(queue))
- break;
-
- rt2800_txdone_entry(entry, reg);
- }
-}
-EXPORT_SYMBOL_GPL(rt2800_txdone);
-
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
@@ -784,8 +701,7 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
/*
* Add space for the TXWI in front of the skb.
*/
- skb_push(entry->skb, TXWI_DESC_SIZE);
- memset(entry->skb, 0, TXWI_DESC_SIZE);
+ memset(skb_push(entry->skb, TXWI_DESC_SIZE), 0, TXWI_DESC_SIZE);
/*
* Register descriptor details in skb frame descriptor.
@@ -981,28 +897,12 @@ static void rt2800_brightness_set(struct led_classdev *led_cdev,
}
}
-static int rt2800_blink_set(struct led_classdev *led_cdev,
- unsigned long *delay_on, unsigned long *delay_off)
-{
- struct rt2x00_led *led =
- container_of(led_cdev, struct rt2x00_led, led_dev);
- u32 reg;
-
- rt2800_register_read(led->rt2x00dev, LED_CFG, &reg);
- rt2x00_set_field32(&reg, LED_CFG_ON_PERIOD, *delay_on);
- rt2x00_set_field32(&reg, LED_CFG_OFF_PERIOD, *delay_off);
- rt2800_register_write(led->rt2x00dev, LED_CFG, reg);
-
- return 0;
-}
-
static void rt2800_init_led(struct rt2x00_dev *rt2x00dev,
struct rt2x00_led *led, enum led_type type)
{
led->rt2x00dev = rt2x00dev;
led->type = type;
led->led_dev.brightness_set = rt2800_brightness_set;
- led->led_dev.blink_set = rt2800_blink_set;
led->flags = LED_INITIALIZED;
}
#endif /* CONFIG_RT2X00_LIB_LEDS */
@@ -1010,11 +910,51 @@ static void rt2800_init_led(struct rt2x00_dev *rt2x00dev,
/*
* Configuration handlers.
*/
-static void rt2800_config_wcid_attr(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_crypto *crypto,
- struct ieee80211_key_conf *key)
+static void rt2800_config_wcid(struct rt2x00_dev *rt2x00dev,
+ const u8 *address,
+ int wcid)
{
struct mac_wcid_entry wcid_entry;
+ u32 offset;
+
+ offset = MAC_WCID_ENTRY(wcid);
+
+ memset(&wcid_entry, 0xff, sizeof(wcid_entry));
+ if (address)
+ memcpy(wcid_entry.mac, address, ETH_ALEN);
+
+ rt2800_register_multiwrite(rt2x00dev, offset,
+ &wcid_entry, sizeof(wcid_entry));
+}
+
+static void rt2800_delete_wcid_attr(struct rt2x00_dev *rt2x00dev, int wcid)
+{
+ u32 offset;
+ offset = MAC_WCID_ATTR_ENTRY(wcid);
+ rt2800_register_write(rt2x00dev, offset, 0);
+}
+
+static void rt2800_config_wcid_attr_bssidx(struct rt2x00_dev *rt2x00dev,
+ int wcid, u32 bssidx)
+{
+ u32 offset = MAC_WCID_ATTR_ENTRY(wcid);
+ u32 reg;
+
+ /*
+ * The BSS Idx numbers is split in a main value of 3 bits,
+ * and a extended field for adding one additional bit to the value.
+ */
+ rt2800_register_read(rt2x00dev, offset, &reg);
+ rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_BSS_IDX, (bssidx & 0x7));
+ rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_BSS_IDX_EXT,
+ (bssidx & 0x8) >> 3);
+ rt2800_register_write(rt2x00dev, offset, reg);
+}
+
+static void rt2800_config_wcid_attr_cipher(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_crypto *crypto,
+ struct ieee80211_key_conf *key)
+{
struct mac_iveiv_entry iveiv_entry;
u32 offset;
u32 reg;
@@ -1034,14 +974,16 @@ static void rt2800_config_wcid_attr(struct rt2x00_dev *rt2x00dev,
(crypto->cipher & 0x7));
rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_CIPHER_EXT,
(crypto->cipher & 0x8) >> 3);
- rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_BSS_IDX,
- (crypto->bssidx & 0x7));
- rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_BSS_IDX_EXT,
- (crypto->bssidx & 0x8) >> 3);
rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_RX_WIUDF, crypto->cipher);
rt2800_register_write(rt2x00dev, offset, reg);
} else {
- rt2800_register_write(rt2x00dev, offset, 0);
+ /* Delete the cipher without touching the bssidx */
+ rt2800_register_read(rt2x00dev, offset, &reg);
+ rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_KEYTAB, 0);
+ rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_CIPHER, 0);
+ rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_CIPHER_EXT, 0);
+ rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_RX_WIUDF, 0);
+ rt2800_register_write(rt2x00dev, offset, reg);
}
offset = MAC_IVEIV_ENTRY(key->hw_key_idx);
@@ -1054,14 +996,6 @@ static void rt2800_config_wcid_attr(struct rt2x00_dev *rt2x00dev,
iveiv_entry.iv[3] |= key->keyidx << 6;
rt2800_register_multiwrite(rt2x00dev, offset,
&iveiv_entry, sizeof(iveiv_entry));
-
- offset = MAC_WCID_ENTRY(key->hw_key_idx);
-
- memset(&wcid_entry, 0, sizeof(wcid_entry));
- if (crypto->cmd == SET_KEY)
- memcpy(wcid_entry.mac, crypto->address, ETH_ALEN);
- rt2800_register_multiwrite(rt2x00dev, offset,
- &wcid_entry, sizeof(wcid_entry));
}
int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev,
@@ -1108,20 +1042,24 @@ int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev,
/*
* Update WCID information
*/
- rt2800_config_wcid_attr(rt2x00dev, crypto, key);
+ rt2800_config_wcid(rt2x00dev, crypto->address, key->hw_key_idx);
+ rt2800_config_wcid_attr_bssidx(rt2x00dev, key->hw_key_idx,
+ crypto->bssidx);
+ rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key);
return 0;
}
EXPORT_SYMBOL_GPL(rt2800_config_shared_key);
-static inline int rt2800_find_pairwise_keyslot(struct rt2x00_dev *rt2x00dev)
+static inline int rt2800_find_wcid(struct rt2x00_dev *rt2x00dev)
{
+ struct mac_wcid_entry wcid_entry;
int idx;
- u32 offset, reg;
+ u32 offset;
/*
- * Search for the first free pairwise key entry and return the
- * corresponding index.
+ * Search for the first free WCID entry and return the corresponding
+ * index.
*
* Make sure the WCID starts _after_ the last possible shared key
* entry (>32).
@@ -1131,11 +1069,17 @@ static inline int rt2800_find_pairwise_keyslot(struct rt2x00_dev *rt2x00dev)
* first 222 entries.
*/
for (idx = 33; idx <= 222; idx++) {
- offset = MAC_WCID_ATTR_ENTRY(idx);
- rt2800_register_read(rt2x00dev, offset, &reg);
- if (!reg)
+ offset = MAC_WCID_ENTRY(idx);
+ rt2800_register_multiread(rt2x00dev, offset, &wcid_entry,
+ sizeof(wcid_entry));
+ if (is_broadcast_ether_addr(wcid_entry.mac))
return idx;
}
+
+ /*
+ * Use -1 to indicate that we don't have any more space in the WCID
+ * table.
+ */
return -1;
}
@@ -1145,13 +1089,15 @@ int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
{
struct hw_key_entry key_entry;
u32 offset;
- int idx;
if (crypto->cmd == SET_KEY) {
- idx = rt2800_find_pairwise_keyslot(rt2x00dev);
- if (idx < 0)
+ /*
+ * Allow key configuration only for STAs that are
+ * known by the hw.
+ */
+ if (crypto->wcid < 0)
return -ENOSPC;
- key->hw_key_idx = idx;
+ key->hw_key_idx = crypto->wcid;
memcpy(key_entry.key, crypto->key,
sizeof(key_entry.key));
@@ -1168,12 +1114,59 @@ int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
/*
* Update WCID information
*/
- rt2800_config_wcid_attr(rt2x00dev, crypto, key);
+ rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key);
return 0;
}
EXPORT_SYMBOL_GPL(rt2800_config_pairwise_key);
+int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ int wcid;
+ struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta);
+
+ /*
+ * Find next free WCID.
+ */
+ wcid = rt2800_find_wcid(rt2x00dev);
+
+ /*
+ * Store selected wcid even if it is invalid so that we can
+ * later decide if the STA is uploaded into the hw.
+ */
+ sta_priv->wcid = wcid;
+
+ /*
+ * No space left in the device, however, we can still communicate
+ * with the STA -> No error.
+ */
+ if (wcid < 0)
+ return 0;
+
+ /*
+ * Clean up WCID attributes and write STA address to the device.
+ */
+ rt2800_delete_wcid_attr(rt2x00dev, wcid);
+ rt2800_config_wcid(rt2x00dev, sta->addr, wcid);
+ rt2800_config_wcid_attr_bssidx(rt2x00dev, wcid,
+ rt2x00lib_get_bssidx(rt2x00dev, vif));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_sta_add);
+
+int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid)
+{
+ /*
+ * Remove WCID entry, no need to clean the attributes as they will
+ * get renewed when the WCID is reused.
+ */
+ rt2800_config_wcid(rt2x00dev, NULL, wcid);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_sta_remove);
+
void rt2800_config_filter(struct rt2x00_dev *rt2x00dev,
const unsigned int filter_flags)
{
@@ -1355,7 +1348,7 @@ static void rt2800_config_ht_opmode(struct rt2x00_dev *rt2x00dev,
gf20_rate = gf40_rate = 0x0003;
}
break;
- };
+ }
/* check for STAs not supporting greenfield mode */
if (any_sta_nongf)
@@ -1433,6 +1426,40 @@ void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp,
}
EXPORT_SYMBOL_GPL(rt2800_config_erp);
+static void rt2800_config_3572bt_ant(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+ u16 eeprom;
+ u8 led_ctrl, led_g_mode, led_r_mode;
+
+ rt2800_register_read(rt2x00dev, GPIO_SWITCH, &reg);
+ if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
+ rt2x00_set_field32(&reg, GPIO_SWITCH_0, 1);
+ rt2x00_set_field32(&reg, GPIO_SWITCH_1, 1);
+ } else {
+ rt2x00_set_field32(&reg, GPIO_SWITCH_0, 0);
+ rt2x00_set_field32(&reg, GPIO_SWITCH_1, 0);
+ }
+ rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg);
+
+ rt2800_register_read(rt2x00dev, LED_CFG, &reg);
+ led_g_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 3 : 0;
+ led_r_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 0 : 3;
+ if (led_g_mode != rt2x00_get_field32(reg, LED_CFG_G_LED_MODE) ||
+ led_r_mode != rt2x00_get_field32(reg, LED_CFG_R_LED_MODE)) {
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
+ led_ctrl = rt2x00_get_field16(eeprom, EEPROM_FREQ_LED_MODE);
+ if (led_ctrl == 0 || led_ctrl > 0x40) {
+ rt2x00_set_field32(&reg, LED_CFG_G_LED_MODE, led_g_mode);
+ rt2x00_set_field32(&reg, LED_CFG_R_LED_MODE, led_r_mode);
+ rt2800_register_write(rt2x00dev, LED_CFG, reg);
+ } else {
+ rt2800_mcu_request(rt2x00dev, MCU_BAND_SELECT, 0xff,
+ (led_g_mode << 2) | led_r_mode, 1);
+ }
+ }
+}
+
static void rt2800_set_ant_diversity(struct rt2x00_dev *rt2x00dev,
enum antenna ant)
{
@@ -1463,6 +1490,10 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
rt2800_bbp_read(rt2x00dev, 1, &r1);
rt2800_bbp_read(rt2x00dev, 3, &r3);
+ if (rt2x00_rt(rt2x00dev, RT3572) &&
+ test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
+ rt2800_config_3572bt_ant(rt2x00dev);
+
/*
* Configure the TX antenna.
*/
@@ -1471,7 +1502,11 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0);
break;
case 2:
- rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
+ if (rt2x00_rt(rt2x00dev, RT3572) &&
+ test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
+ rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1);
+ else
+ rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
break;
case 3:
rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0);
@@ -1496,7 +1531,15 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0);
break;
case 2:
- rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 1);
+ if (rt2x00_rt(rt2x00dev, RT3572) &&
+ test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+ rt2x00_set_field8(&r3, BBP3_RX_ADC, 1);
+ rt2x00_set_field8(&r3, BBP3_RX_ANTENNA,
+ rt2x00dev->curr_band == IEEE80211_BAND_5GHZ);
+ rt2800_set_ant_diversity(rt2x00dev, ANTENNA_B);
+ } else {
+ rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 1);
+ }
break;
case 3:
rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 2);
@@ -1630,6 +1673,161 @@ static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev,
rt2800_rfcsr_write(rt2x00dev, 7, rfcsr);
}
+static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_conf *conf,
+ struct rf_channel *rf,
+ struct channel_info *info)
+{
+ u8 rfcsr;
+ u32 reg;
+
+ if (rf->channel <= 14) {
+ rt2800_bbp_write(rt2x00dev, 25, 0x15);
+ rt2800_bbp_write(rt2x00dev, 26, 0x85);
+ } else {
+ rt2800_bbp_write(rt2x00dev, 25, 0x09);
+ rt2800_bbp_write(rt2x00dev, 26, 0xff);
+ }
+
+ rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1);
+ rt2800_rfcsr_write(rt2x00dev, 3, rf->rf3);
+
+ rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR6_R1, rf->rf2);
+ if (rf->channel <= 14)
+ rt2x00_set_field8(&rfcsr, RFCSR6_TXDIV, 2);
+ else
+ rt2x00_set_field8(&rfcsr, RFCSR6_TXDIV, 1);
+ rt2800_rfcsr_write(rt2x00dev, 6, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 5, &rfcsr);
+ if (rf->channel <= 14)
+ rt2x00_set_field8(&rfcsr, RFCSR5_R1, 1);
+ else
+ rt2x00_set_field8(&rfcsr, RFCSR5_R1, 2);
+ rt2800_rfcsr_write(rt2x00dev, 5, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr);
+ if (rf->channel <= 14) {
+ rt2x00_set_field8(&rfcsr, RFCSR12_DR0, 3);
+ rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER,
+ (info->default_power1 & 0x3) |
+ ((info->default_power1 & 0xC) << 1));
+ } else {
+ rt2x00_set_field8(&rfcsr, RFCSR12_DR0, 7);
+ rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER,
+ (info->default_power1 & 0x3) |
+ ((info->default_power1 & 0xC) << 1));
+ }
+ rt2800_rfcsr_write(rt2x00dev, 12, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr);
+ if (rf->channel <= 14) {
+ rt2x00_set_field8(&rfcsr, RFCSR13_DR0, 3);
+ rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER,
+ (info->default_power2 & 0x3) |
+ ((info->default_power2 & 0xC) << 1));
+ } else {
+ rt2x00_set_field8(&rfcsr, RFCSR13_DR0, 7);
+ rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER,
+ (info->default_power2 & 0x3) |
+ ((info->default_power2 & 0xC) << 1));
+ }
+ rt2800_rfcsr_write(rt2x00dev, 13, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0);
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0);
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0);
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0);
+ if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+ if (rf->channel <= 14) {
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1);
+ }
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1);
+ } else {
+ switch (rt2x00dev->default_ant.tx_chain_num) {
+ case 1:
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1);
+ case 2:
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1);
+ break;
+ }
+
+ switch (rt2x00dev->default_ant.rx_chain_num) {
+ case 1:
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1);
+ case 2:
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1);
+ break;
+ }
+ }
+ rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset);
+ rt2800_rfcsr_write(rt2x00dev, 23, rfcsr);
+
+ rt2800_rfcsr_write(rt2x00dev, 24,
+ rt2x00dev->calibration[conf_is_ht40(conf)]);
+ rt2800_rfcsr_write(rt2x00dev, 31,
+ rt2x00dev->calibration[conf_is_ht40(conf)]);
+
+ if (rf->channel <= 14) {
+ rt2800_rfcsr_write(rt2x00dev, 7, 0xd8);
+ rt2800_rfcsr_write(rt2x00dev, 9, 0xc3);
+ rt2800_rfcsr_write(rt2x00dev, 10, 0xf1);
+ rt2800_rfcsr_write(rt2x00dev, 11, 0xb9);
+ rt2800_rfcsr_write(rt2x00dev, 15, 0x53);
+ rt2800_rfcsr_write(rt2x00dev, 16, 0x4c);
+ rt2800_rfcsr_write(rt2x00dev, 17, 0x23);
+ rt2800_rfcsr_write(rt2x00dev, 19, 0x93);
+ rt2800_rfcsr_write(rt2x00dev, 20, 0xb3);
+ rt2800_rfcsr_write(rt2x00dev, 25, 0x15);
+ rt2800_rfcsr_write(rt2x00dev, 26, 0x85);
+ rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
+ rt2800_rfcsr_write(rt2x00dev, 29, 0x9b);
+ } else {
+ rt2800_rfcsr_write(rt2x00dev, 7, 0x14);
+ rt2800_rfcsr_write(rt2x00dev, 9, 0xc0);
+ rt2800_rfcsr_write(rt2x00dev, 10, 0xf1);
+ rt2800_rfcsr_write(rt2x00dev, 11, 0x00);
+ rt2800_rfcsr_write(rt2x00dev, 15, 0x43);
+ rt2800_rfcsr_write(rt2x00dev, 16, 0x7a);
+ rt2800_rfcsr_write(rt2x00dev, 17, 0x23);
+ if (rf->channel <= 64) {
+ rt2800_rfcsr_write(rt2x00dev, 19, 0xb7);
+ rt2800_rfcsr_write(rt2x00dev, 20, 0xf6);
+ rt2800_rfcsr_write(rt2x00dev, 25, 0x3d);
+ } else if (rf->channel <= 128) {
+ rt2800_rfcsr_write(rt2x00dev, 19, 0x74);
+ rt2800_rfcsr_write(rt2x00dev, 20, 0xf4);
+ rt2800_rfcsr_write(rt2x00dev, 25, 0x01);
+ } else {
+ rt2800_rfcsr_write(rt2x00dev, 19, 0x72);
+ rt2800_rfcsr_write(rt2x00dev, 20, 0xf3);
+ rt2800_rfcsr_write(rt2x00dev, 25, 0x01);
+ }
+ rt2800_rfcsr_write(rt2x00dev, 26, 0x87);
+ rt2800_rfcsr_write(rt2x00dev, 27, 0x01);
+ rt2800_rfcsr_write(rt2x00dev, 29, 0x9f);
+ }
+
+ rt2800_register_read(rt2x00dev, GPIO_CTRL_CFG, &reg);
+ rt2x00_set_field32(&reg, GPIO_CTRL_CFG_GPIOD_BIT7, 0);
+ if (rf->channel <= 14)
+ rt2x00_set_field32(&reg, GPIO_CTRL_CFG_BIT7, 1);
+ else
+ rt2x00_set_field32(&reg, GPIO_CTRL_CFG_BIT7, 0);
+ rt2800_register_write(rt2x00dev, GPIO_CTRL_CFG, reg);
+
+ rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1);
+ rt2800_rfcsr_write(rt2x00dev, 7, rfcsr);
+}
#define RT5390_POWER_BOUND 0x27
#define RT5390_FREQ_OFFSET_BOUND 0x5f
@@ -1748,9 +1946,10 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
rt2x00_rf(rt2x00dev, RF3020) ||
rt2x00_rf(rt2x00dev, RF3021) ||
rt2x00_rf(rt2x00dev, RF3022) ||
- rt2x00_rf(rt2x00dev, RF3052) ||
rt2x00_rf(rt2x00dev, RF3320))
rt2800_config_channel_rf3xxx(rt2x00dev, conf, rf, info);
+ else if (rt2x00_rf(rt2x00dev, RF3052))
+ rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info);
else if (rt2x00_rf(rt2x00dev, RF5370) ||
rt2x00_rf(rt2x00dev, RF5390))
rt2800_config_channel_rf53xx(rt2x00dev, conf, rf, info);
@@ -1777,7 +1976,10 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
}
}
} else {
- rt2800_bbp_write(rt2x00dev, 82, 0xf2);
+ if (rt2x00_rt(rt2x00dev, RT3572))
+ rt2800_bbp_write(rt2x00dev, 82, 0x94);
+ else
+ rt2800_bbp_write(rt2x00dev, 82, 0xf2);
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
rt2800_bbp_write(rt2x00dev, 75, 0x46);
@@ -1791,12 +1993,17 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field32(&reg, TX_BAND_CFG_BG, rf->channel <= 14);
rt2800_register_write(rt2x00dev, TX_BAND_CFG, reg);
+ if (rt2x00_rt(rt2x00dev, RT3572))
+ rt2800_rfcsr_write(rt2x00dev, 8, 0);
+
tx_pin = 0;
/* Turn on unused PA or LNA when not using 1T or 1R */
if (rt2x00dev->default_ant.tx_chain_num == 2) {
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, 1);
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, 1);
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN,
+ rf->channel > 14);
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN,
+ rf->channel <= 14);
}
/* Turn on unused PA or LNA when not using 1T or 1R */
@@ -1809,11 +2016,18 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1);
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1);
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1);
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, rf->channel <= 14);
+ if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1);
+ else
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN,
+ rf->channel <= 14);
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rf->channel > 14);
rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin);
+ if (rt2x00_rt(rt2x00dev, RT3572))
+ rt2800_rfcsr_write(rt2x00dev, 8, 0x80);
+
rt2800_bbp_read(rt2x00dev, 4, &bbp);
rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf));
rt2800_bbp_write(rt2x00dev, 4, bbp);
@@ -2413,6 +2627,9 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400);
rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000);
rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000030);
+ } else if (rt2x00_rt(rt2x00dev, RT3572)) {
+ rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400);
+ rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
} else if (rt2x00_rt(rt2x00dev, RT5390)) {
rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404);
rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
@@ -2629,11 +2846,8 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
SHARED_KEY_MODE_ENTRY(i), 0);
for (i = 0; i < 256; i++) {
- static const u32 wcid[2] = { 0xffffffff, 0x00ffffff };
- rt2800_register_multiwrite(rt2x00dev, MAC_WCID_ENTRY(i),
- wcid, sizeof(wcid));
-
- rt2800_register_write(rt2x00dev, MAC_WCID_ATTR_ENTRY(i), 0);
+ rt2800_config_wcid(rt2x00dev, NULL, i);
+ rt2800_delete_wcid_attr(rt2x00dev, i);
rt2800_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0);
}
@@ -2799,6 +3013,7 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
}
if (rt2800_is_305x_soc(rt2x00dev) ||
+ rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT5390))
rt2800_bbp_write(rt2x00dev, 31, 0x08);
@@ -2828,6 +3043,7 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
rt2x00_rt(rt2x00dev, RT3071) ||
rt2x00_rt(rt2x00dev, RT3090) ||
rt2x00_rt(rt2x00dev, RT3390) ||
+ rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT5390)) {
rt2800_bbp_write(rt2x00dev, 79, 0x13);
rt2800_bbp_write(rt2x00dev, 80, 0x05);
@@ -2868,6 +3084,7 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) ||
rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) ||
rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E) ||
+ rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2800_is_305x_soc(rt2x00dev))
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
@@ -2895,6 +3112,7 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
if (rt2x00_rt(rt2x00dev, RT3071) ||
rt2x00_rt(rt2x00dev, RT3090) ||
rt2x00_rt(rt2x00dev, RT3390) ||
+ rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT5390)) {
rt2800_bbp_read(rt2x00dev, 138, &value);
@@ -3031,6 +3249,7 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
!rt2x00_rt(rt2x00dev, RT3071) &&
!rt2x00_rt(rt2x00dev, RT3090) &&
!rt2x00_rt(rt2x00dev, RT3390) &&
+ !rt2x00_rt(rt2x00dev, RT3572) &&
!rt2x00_rt(rt2x00dev, RT5390) &&
!rt2800_is_305x_soc(rt2x00dev))
return 0;
@@ -3109,6 +3328,38 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
rt2800_rfcsr_write(rt2x00dev, 29, 0x8f);
rt2800_rfcsr_write(rt2x00dev, 30, 0x20);
rt2800_rfcsr_write(rt2x00dev, 31, 0x0f);
+ } else if (rt2x00_rt(rt2x00dev, RT3572)) {
+ rt2800_rfcsr_write(rt2x00dev, 0, 0x70);
+ rt2800_rfcsr_write(rt2x00dev, 1, 0x81);
+ rt2800_rfcsr_write(rt2x00dev, 2, 0xf1);
+ rt2800_rfcsr_write(rt2x00dev, 3, 0x02);
+ rt2800_rfcsr_write(rt2x00dev, 4, 0x4c);
+ rt2800_rfcsr_write(rt2x00dev, 5, 0x05);
+ rt2800_rfcsr_write(rt2x00dev, 6, 0x4a);
+ rt2800_rfcsr_write(rt2x00dev, 7, 0xd8);
+ rt2800_rfcsr_write(rt2x00dev, 9, 0xc3);
+ rt2800_rfcsr_write(rt2x00dev, 10, 0xf1);
+ rt2800_rfcsr_write(rt2x00dev, 11, 0xb9);
+ rt2800_rfcsr_write(rt2x00dev, 12, 0x70);
+ rt2800_rfcsr_write(rt2x00dev, 13, 0x65);
+ rt2800_rfcsr_write(rt2x00dev, 14, 0xa0);
+ rt2800_rfcsr_write(rt2x00dev, 15, 0x53);
+ rt2800_rfcsr_write(rt2x00dev, 16, 0x4c);
+ rt2800_rfcsr_write(rt2x00dev, 17, 0x23);
+ rt2800_rfcsr_write(rt2x00dev, 18, 0xac);
+ rt2800_rfcsr_write(rt2x00dev, 19, 0x93);
+ rt2800_rfcsr_write(rt2x00dev, 20, 0xb3);
+ rt2800_rfcsr_write(rt2x00dev, 21, 0xd0);
+ rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
+ rt2800_rfcsr_write(rt2x00dev, 23, 0x3c);
+ rt2800_rfcsr_write(rt2x00dev, 24, 0x16);
+ rt2800_rfcsr_write(rt2x00dev, 25, 0x15);
+ rt2800_rfcsr_write(rt2x00dev, 26, 0x85);
+ rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
+ rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
+ rt2800_rfcsr_write(rt2x00dev, 29, 0x9b);
+ rt2800_rfcsr_write(rt2x00dev, 30, 0x09);
+ rt2800_rfcsr_write(rt2x00dev, 31, 0x10);
} else if (rt2800_is_305x_soc(rt2x00dev)) {
rt2800_rfcsr_write(rt2x00dev, 0, 0x50);
rt2800_rfcsr_write(rt2x00dev, 1, 0x01);
@@ -3258,6 +3509,19 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
rt2800_register_read(rt2x00dev, GPIO_SWITCH, &reg);
rt2x00_set_field32(&reg, GPIO_SWITCH_5, 0);
rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg);
+ } else if (rt2x00_rt(rt2x00dev, RT3572)) {
+ rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR6_R2, 1);
+ rt2800_rfcsr_write(rt2x00dev, 6, rfcsr);
+
+ rt2800_register_read(rt2x00dev, LDO_CFG0, &reg);
+ rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL, 3);
+ rt2x00_set_field32(&reg, LDO_CFG0_BGSEL, 1);
+ rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
+ msleep(1);
+ rt2800_register_read(rt2x00dev, LDO_CFG0, &reg);
+ rt2x00_set_field32(&reg, LDO_CFG0_BGSEL, 1);
+ rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
}
/*
@@ -3270,7 +3534,8 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
rt2800_init_rx_filter(rt2x00dev, true, 0x27, 0x19);
} else if (rt2x00_rt(rt2x00dev, RT3071) ||
rt2x00_rt(rt2x00dev, RT3090) ||
- rt2x00_rt(rt2x00dev, RT3390)) {
+ rt2x00_rt(rt2x00dev, RT3390) ||
+ rt2x00_rt(rt2x00dev, RT3572)) {
rt2x00dev->calibration[0] =
rt2800_init_rx_filter(rt2x00dev, false, 0x07, 0x13);
rt2x00dev->calibration[1] =
@@ -3504,14 +3769,15 @@ static void rt2800_efuse_read(struct rt2x00_dev *rt2x00dev, unsigned int i)
rt2800_regbusy_read(rt2x00dev, EFUSE_CTRL, EFUSE_CTRL_KICK, &reg);
/* Apparently the data is read from end to start */
- rt2800_register_read_lock(rt2x00dev, EFUSE_DATA3,
- (u32 *)&rt2x00dev->eeprom[i]);
- rt2800_register_read_lock(rt2x00dev, EFUSE_DATA2,
- (u32 *)&rt2x00dev->eeprom[i + 2]);
- rt2800_register_read_lock(rt2x00dev, EFUSE_DATA1,
- (u32 *)&rt2x00dev->eeprom[i + 4]);
- rt2800_register_read_lock(rt2x00dev, EFUSE_DATA0,
- (u32 *)&rt2x00dev->eeprom[i + 6]);
+ rt2800_register_read_lock(rt2x00dev, EFUSE_DATA3, &reg);
+ /* The returned value is in CPU order, but eeprom is le */
+ rt2x00dev->eeprom[i] = cpu_to_le32(reg);
+ rt2800_register_read_lock(rt2x00dev, EFUSE_DATA2, &reg);
+ *(u32 *)&rt2x00dev->eeprom[i + 2] = cpu_to_le32(reg);
+ rt2800_register_read_lock(rt2x00dev, EFUSE_DATA1, &reg);
+ *(u32 *)&rt2x00dev->eeprom[i + 4] = cpu_to_le32(reg);
+ rt2800_register_read_lock(rt2x00dev, EFUSE_DATA0, &reg);
+ *(u32 *)&rt2x00dev->eeprom[i + 6] = cpu_to_le32(reg);
mutex_unlock(&rt2x00dev->csr_mutex);
}
@@ -3677,19 +3943,23 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
return -ENODEV;
}
- if (!rt2x00_rf(rt2x00dev, RF2820) &&
- !rt2x00_rf(rt2x00dev, RF2850) &&
- !rt2x00_rf(rt2x00dev, RF2720) &&
- !rt2x00_rf(rt2x00dev, RF2750) &&
- !rt2x00_rf(rt2x00dev, RF3020) &&
- !rt2x00_rf(rt2x00dev, RF2020) &&
- !rt2x00_rf(rt2x00dev, RF3021) &&
- !rt2x00_rf(rt2x00dev, RF3022) &&
- !rt2x00_rf(rt2x00dev, RF3052) &&
- !rt2x00_rf(rt2x00dev, RF3320) &&
- !rt2x00_rf(rt2x00dev, RF5370) &&
- !rt2x00_rf(rt2x00dev, RF5390)) {
- ERROR(rt2x00dev, "Invalid RF chipset detected.\n");
+ switch (rt2x00dev->chip.rf) {
+ case RF2820:
+ case RF2850:
+ case RF2720:
+ case RF2750:
+ case RF3020:
+ case RF2020:
+ case RF3021:
+ case RF3022:
+ case RF3052:
+ case RF3320:
+ case RF5370:
+ case RF5390:
+ break;
+ default:
+ ERROR(rt2x00dev, "Invalid RF chipset 0x%x detected.\n",
+ rt2x00dev->chip.rf);
return -ENODEV;
}
@@ -4128,7 +4398,8 @@ int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
}
EXPORT_SYMBOL_GPL(rt2800_set_rts_threshold);
-int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
+int rt2800_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue_idx,
const struct ieee80211_tx_queue_params *params)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
@@ -4144,7 +4415,7 @@ int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
* we are free to update the registers based on the value
* in the queue parameter.
*/
- retval = rt2x00mac_conf_tx(hw, queue_idx, params);
+ retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params);
if (retval)
return retval;
@@ -4196,7 +4467,7 @@ int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
}
EXPORT_SYMBOL_GPL(rt2800_conf_tx);
-u64 rt2800_get_tsf(struct ieee80211_hw *hw)
+u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
u64 tsf;
@@ -4216,8 +4487,19 @@ int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 *ssn,
u8 buf_size)
{
+ struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv;
int ret = 0;
+ /*
+ * Don't allow aggregation for stations the hardware isn't aware
+ * of because tx status reports for frames to an unknown station
+ * always contain wcid=255 and thus we can't distinguish between
+ * multiple stations which leads to unwanted situations when the
+ * hw reorders frames due to aggregation.
+ */
+ if (sta_priv->wcid < 0)
+ return 1;
+
switch (action) {
case IEEE80211_AMPDU_RX_START:
case IEEE80211_AMPDU_RX_STOP:
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h
index f2d15941c71a..8c3c281904fe 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/rt2x00/rt2800lib.h
@@ -152,8 +152,7 @@ void rt2800_write_tx_data(struct queue_entry *entry,
struct txentry_desc *txdesc);
void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc);
-void rt2800_txdone(struct rt2x00_dev *rt2x00dev);
-void rt2800_txdone_entry(struct queue_entry *entry, u32 status);
+void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32* txwi);
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc);
void rt2800_clear_beacon(struct queue_entry *entry);
@@ -167,6 +166,9 @@ int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev,
int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_crypto *crypto,
struct ieee80211_key_conf *key);
+int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid);
void rt2800_config_filter(struct rt2x00_dev *rt2x00dev,
const unsigned int filter_flags);
void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
@@ -195,9 +197,10 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev);
void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32,
u16 *iv16);
int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
-int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
+int rt2800_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue_idx,
const struct ieee80211_tx_queue_params *params);
-u64 rt2800_get_tsf(struct ieee80211_hw *hw);
+u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn,
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index cc4a54f571b8..da48c8ac27bd 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -200,13 +200,6 @@ static void rt2800pci_start_queue(struct data_queue *queue)
rt2x00pci_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
break;
case QID_BEACON:
- /*
- * Allow beacon tasklets to be scheduled for periodic
- * beacon updates.
- */
- tasklet_enable(&rt2x00dev->tbtt_tasklet);
- tasklet_enable(&rt2x00dev->pretbtt_tasklet);
-
rt2x00pci_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
@@ -219,7 +212,7 @@ static void rt2800pci_start_queue(struct data_queue *queue)
break;
default:
break;
- };
+ }
}
static void rt2800pci_kick_queue(struct data_queue *queue)
@@ -269,10 +262,13 @@ static void rt2800pci_stop_queue(struct data_queue *queue)
rt2x00pci_register_write(rt2x00dev, INT_TIMER_EN, reg);
/*
- * Wait for tbtt tasklets to finish.
+ * Wait for current invocation to finish. The tasklet
+ * won't be scheduled anymore afterwards since we disabled
+ * the TBTT and PRE TBTT timer.
*/
- tasklet_disable(&rt2x00dev->tbtt_tasklet);
- tasklet_disable(&rt2x00dev->pretbtt_tasklet);
+ tasklet_kill(&rt2x00dev->tbtt_tasklet);
+ tasklet_kill(&rt2x00dev->pretbtt_tasklet);
+
break;
default:
break;
@@ -437,14 +433,6 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
if (state == STATE_RADIO_IRQ_ON) {
rt2x00pci_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
rt2x00pci_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
-
- /*
- * Enable tasklets. The beacon related tasklets are
- * enabled when the beacon queue is started.
- */
- tasklet_enable(&rt2x00dev->txstatus_tasklet);
- tasklet_enable(&rt2x00dev->rxdone_tasklet);
- tasklet_enable(&rt2x00dev->autowake_tasklet);
}
spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
@@ -472,12 +460,13 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
if (state == STATE_RADIO_IRQ_OFF) {
/*
- * Ensure that all tasklets are finished before
- * disabling the interrupts.
+ * Wait for possibly running tasklets to finish.
*/
- tasklet_disable(&rt2x00dev->txstatus_tasklet);
- tasklet_disable(&rt2x00dev->rxdone_tasklet);
- tasklet_disable(&rt2x00dev->autowake_tasklet);
+ tasklet_kill(&rt2x00dev->txstatus_tasklet);
+ tasklet_kill(&rt2x00dev->rxdone_tasklet);
+ tasklet_kill(&rt2x00dev->autowake_tasklet);
+ tasklet_kill(&rt2x00dev->tbtt_tasklet);
+ tasklet_kill(&rt2x00dev->pretbtt_tasklet);
}
}
@@ -501,7 +490,9 @@ static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev)
rt2x00pci_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f);
rt2x00pci_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
- if (rt2x00_rt(rt2x00dev, RT5390)) {
+ if (rt2x00_is_pcie(rt2x00dev) &&
+ (rt2x00_rt(rt2x00dev, RT3572) ||
+ rt2x00_rt(rt2x00dev, RT5390))) {
rt2x00pci_register_read(rt2x00dev, AUX_CTRL, &reg);
rt2x00_set_field32(&reg, AUX_CTRL_FORCE_PCIE_CLK, 1);
rt2x00_set_field32(&reg, AUX_CTRL_WAKE_PCIE_EN, 1);
@@ -628,11 +619,11 @@ static void rt2800pci_write_tx_desc(struct queue_entry *entry,
/*
* Initialize TX descriptor
*/
- rt2x00_desc_read(txd, 0, &word);
+ word = 0;
rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma);
rt2x00_desc_write(txd, 0, word);
- rt2x00_desc_read(txd, 1, &word);
+ word = 0;
rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len);
rt2x00_set_field32(&word, TXD_W1_LAST_SEC1,
!test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
@@ -643,12 +634,12 @@ static void rt2800pci_write_tx_desc(struct queue_entry *entry,
rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0);
rt2x00_desc_write(txd, 1, word);
- rt2x00_desc_read(txd, 2, &word);
+ word = 0;
rt2x00_set_field32(&word, TXD_W2_SD_PTR1,
skbdesc->skb_dma + TXWI_DESC_SIZE);
rt2x00_desc_write(txd, 2, word);
- rt2x00_desc_read(txd, 3, &word);
+ word = 0;
rt2x00_set_field32(&word, TXD_W3_WIV,
!test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags));
rt2x00_set_field32(&word, TXD_W3_QSEL, 2);
@@ -769,7 +760,7 @@ static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
}
entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
- rt2800_txdone_entry(entry, status);
+ rt2800_txdone_entry(entry, status, rt2800pci_get_txwi(entry));
if (--max_tx_done == 0)
break;
@@ -811,14 +802,16 @@ static void rt2800pci_pretbtt_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt2x00lib_pretbtt(rt2x00dev);
- rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT);
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT);
}
static void rt2800pci_tbtt_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt2x00lib_beacondone(rt2x00dev);
- rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT);
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT);
}
static void rt2800pci_rxdone_tasklet(unsigned long data)
@@ -826,7 +819,7 @@ static void rt2800pci_rxdone_tasklet(unsigned long data)
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
if (rt2x00pci_rxdone(rt2x00dev))
tasklet_schedule(&rt2x00dev->rxdone_tasklet);
- else
+ else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE);
}
@@ -834,7 +827,8 @@ static void rt2800pci_autowake_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt2800pci_wakeup(rt2x00dev);
- rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_AUTO_WAKEUP);
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_AUTO_WAKEUP);
}
static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
@@ -1021,6 +1015,8 @@ static const struct ieee80211_ops rt2800pci_mac80211_ops = {
.get_stats = rt2x00mac_get_stats,
.get_tkip_seq = rt2800_get_tkip_seq,
.set_rts_threshold = rt2800_set_rts_threshold,
+ .sta_add = rt2x00mac_sta_add,
+ .sta_remove = rt2x00mac_sta_remove,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2800_conf_tx,
.get_tsf = rt2800_get_tsf,
@@ -1029,6 +1025,7 @@ static const struct ieee80211_ops rt2800pci_mac80211_ops = {
.flush = rt2x00mac_flush,
.get_survey = rt2800_get_survey,
.get_ringparam = rt2x00mac_get_ringparam,
+ .tx_frames_pending = rt2x00mac_tx_frames_pending,
};
static const struct rt2800_ops rt2800pci_rt2800_ops = {
@@ -1081,6 +1078,8 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
.config_erp = rt2800_config_erp,
.config_ant = rt2800_config_ant,
.config = rt2800_config,
+ .sta_add = rt2800_sta_add,
+ .sta_remove = rt2800_sta_remove,
};
static const struct data_queue_desc rt2800pci_queue_rx = {
@@ -1158,6 +1157,8 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = {
#endif
#ifdef CONFIG_RT2800PCI_RT53XX
{ PCI_DEVICE(0x1814, 0x5390) },
+ { PCI_DEVICE(0x1814, 0x539a) },
+ { PCI_DEVICE(0x1814, 0x539f) },
#endif
{ 0, }
};
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index ba82c972703a..377876315b8d 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -457,6 +457,96 @@ static int rt2800usb_get_tx_data_len(struct queue_entry *entry)
/*
* TX control handlers
*/
+static bool rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg)
+{
+ __le32 *txwi;
+ u32 word;
+ int wcid, ack, pid;
+ int tx_wcid, tx_ack, tx_pid;
+
+ if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) ||
+ !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) {
+ WARNING(entry->queue->rt2x00dev,
+ "Data pending for entry %u in queue %u\n",
+ entry->entry_idx, entry->queue->qid);
+ cond_resched();
+ return false;
+ }
+
+ wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
+ ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
+ pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
+
+ /*
+ * This frames has returned with an IO error,
+ * so the status report is not intended for this
+ * frame.
+ */
+ if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) {
+ rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
+ return false;
+ }
+
+ /*
+ * Validate if this TX status report is intended for
+ * this entry by comparing the WCID/ACK/PID fields.
+ */
+ txwi = rt2800usb_get_txwi(entry);
+
+ rt2x00_desc_read(txwi, 1, &word);
+ tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
+ tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK);
+ tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID);
+
+ if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid)) {
+ WARNING(entry->queue->rt2x00dev,
+ "TX status report missed for queue %d entry %d\n",
+ entry->queue->qid, entry->entry_idx);
+ rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
+ return false;
+ }
+
+ return true;
+}
+
+static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev)
+{
+ struct data_queue *queue;
+ struct queue_entry *entry;
+ u32 reg;
+ u8 qid;
+
+ while (kfifo_get(&rt2x00dev->txstatus_fifo, &reg)) {
+
+ /* TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus
+ * qid is guaranteed to be one of the TX QIDs
+ */
+ qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE);
+ queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
+ if (unlikely(!queue)) {
+ WARNING(rt2x00dev, "Got TX status for an unavailable "
+ "queue %u, dropping\n", qid);
+ continue;
+ }
+
+ /*
+ * Inside each queue, we process each entry in a chronological
+ * order. We first check that the queue is not empty.
+ */
+ entry = NULL;
+ while (!rt2x00queue_empty(queue)) {
+ entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
+ if (rt2800usb_txdone_entry_check(entry, reg))
+ break;
+ entry = NULL;
+ }
+
+ if (entry)
+ rt2800_txdone_entry(entry, reg,
+ rt2800usb_get_txwi(entry));
+ }
+}
+
static void rt2800usb_work_txdone(struct work_struct *work)
{
struct rt2x00_dev *rt2x00dev =
@@ -464,7 +554,7 @@ static void rt2800usb_work_txdone(struct work_struct *work)
struct data_queue *queue;
struct queue_entry *entry;
- rt2800_txdone(rt2x00dev);
+ rt2800usb_txdone(rt2x00dev);
/*
* Process any trailing TX status reports for IO failures,
@@ -477,8 +567,10 @@ static void rt2800usb_work_txdone(struct work_struct *work)
while (!rt2x00queue_empty(queue)) {
entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
- if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
+ if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) ||
+ !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))
break;
+
if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
else if (rt2x00queue_status_timeout(entry))
@@ -668,6 +760,8 @@ static const struct ieee80211_ops rt2800usb_mac80211_ops = {
.get_stats = rt2x00mac_get_stats,
.get_tkip_seq = rt2800_get_tkip_seq,
.set_rts_threshold = rt2800_set_rts_threshold,
+ .sta_add = rt2x00mac_sta_add,
+ .sta_remove = rt2x00mac_sta_remove,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2800_conf_tx,
.get_tsf = rt2800_get_tsf,
@@ -676,6 +770,7 @@ static const struct ieee80211_ops rt2800usb_mac80211_ops = {
.flush = rt2x00mac_flush,
.get_survey = rt2800_get_survey,
.get_ringparam = rt2x00mac_get_ringparam,
+ .tx_frames_pending = rt2x00mac_tx_frames_pending,
};
static const struct rt2800_ops rt2800usb_rt2800_ops = {
@@ -724,6 +819,8 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.config_erp = rt2800_config_erp,
.config_ant = rt2800_config_ant,
.config = rt2800_config,
+ .sta_add = rt2800_sta_add,
+ .sta_remove = rt2800_sta_remove,
};
static const struct data_queue_desc rt2800usb_queue_rx = {
@@ -822,6 +919,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x050d, 0x935b) },
/* Buffalo */
{ USB_DEVICE(0x0411, 0x00e8) },
+ { USB_DEVICE(0x0411, 0x0158) },
{ USB_DEVICE(0x0411, 0x016f) },
{ USB_DEVICE(0x0411, 0x01a2) },
/* Corega */
@@ -839,6 +937,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x07d1, 0x3c16) },
/* Draytek */
{ USB_DEVICE(0x07fa, 0x7712) },
+ /* DVICO */
+ { USB_DEVICE(0x0fe9, 0xb307) },
/* Edimax */
{ USB_DEVICE(0x7392, 0x7711) },
{ USB_DEVICE(0x7392, 0x7717) },
@@ -939,6 +1039,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x0df6, 0x0048) },
{ USB_DEVICE(0x0df6, 0x0051) },
{ USB_DEVICE(0x0df6, 0x005f) },
+ { USB_DEVICE(0x0df6, 0x0060) },
/* SMC */
{ USB_DEVICE(0x083a, 0x6618) },
{ USB_DEVICE(0x083a, 0x7511) },
@@ -971,6 +1072,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x0586, 0x341e) },
{ USB_DEVICE(0x0586, 0x343e) },
#ifdef CONFIG_RT2800USB_RT33XX
+ /* Belkin */
+ { USB_DEVICE(0x050d, 0x945b) },
/* Ralink */
{ USB_DEVICE(0x148f, 0x3370) },
{ USB_DEVICE(0x148f, 0x8070) },
@@ -995,6 +1098,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x148f, 0x3572) },
/* Sitecom */
{ USB_DEVICE(0x0df6, 0x0041) },
+ { USB_DEVICE(0x0df6, 0x0062) },
/* Toshiba */
{ USB_DEVICE(0x0930, 0x0a07) },
/* Zinwell */
@@ -1093,8 +1197,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x0df6, 0x004a) },
{ USB_DEVICE(0x0df6, 0x004d) },
{ USB_DEVICE(0x0df6, 0x0053) },
- { USB_DEVICE(0x0df6, 0x0060) },
- { USB_DEVICE(0x0df6, 0x0062) },
/* SMC */
{ USB_DEVICE(0x083a, 0xa512) },
{ USB_DEVICE(0x083a, 0xc522) },
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index c446db69bd3c..99ff12d0c29d 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -29,6 +29,7 @@
#define RT2X00_H
#include <linux/bitops.h>
+#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
@@ -477,6 +478,8 @@ struct rt2x00lib_crypto {
u8 key[16];
u8 tx_mic[8];
u8 rx_mic[8];
+
+ int wcid;
};
/*
@@ -511,6 +514,19 @@ struct rt2x00intf_conf {
};
/*
+ * Private structure for storing STA details
+ * wcid: Wireless Client ID
+ */
+struct rt2x00_sta {
+ int wcid;
+};
+
+static inline struct rt2x00_sta* sta_to_rt2x00_sta(struct ieee80211_sta *sta)
+{
+ return (struct rt2x00_sta *)sta->drv_priv;
+}
+
+/*
* rt2x00lib callback functions.
*/
struct rt2x00lib_ops {
@@ -619,6 +635,11 @@ struct rt2x00lib_ops {
void (*config) (struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_conf *libconf,
const unsigned int changed_flags);
+ int (*sta_add) (struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ int (*sta_remove) (struct rt2x00_dev *rt2x00dev,
+ int wcid);
};
/*
@@ -922,6 +943,7 @@ struct rt2x00_dev {
* Powersaving work
*/
struct delayed_work autowakeup_work;
+ struct work_struct sleep_work;
/*
* Data queue arrays for RX, TX, Beacon and ATIM.
@@ -1225,6 +1247,12 @@ static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev,
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
/*
+ * Utility functions.
+ */
+u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_vif *vif);
+
+/*
* Interrupt context handlers.
*/
void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev);
@@ -1260,6 +1288,10 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
#else
#define rt2x00mac_set_key NULL
#endif /* CONFIG_RT2X00_LIB_CRYPTO */
+int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw);
void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw);
int rt2x00mac_get_stats(struct ieee80211_hw *hw,
@@ -1268,7 +1300,8 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
u32 changes);
-int rt2x00mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
+int rt2x00mac_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params);
void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw);
void rt2x00mac_flush(struct ieee80211_hw *hw, bool drop);
@@ -1276,6 +1309,7 @@ int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
void rt2x00mac_get_ringparam(struct ieee80211_hw *hw,
u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
+bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw);
/*
* Driver allocation handlers.
diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c
index 1bb9d46077ff..1ca4c7ffc189 100644
--- a/drivers/net/wireless/rt2x00/rt2x00crypto.c
+++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c
@@ -45,11 +45,11 @@ enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key)
}
}
-void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry,
+void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
+ struct sk_buff *skb,
struct txentry_desc *txdesc)
{
- struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_key_conf *hw_key = tx_info->control.hw_key;
if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !hw_key)
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 939821b4af2f..edd317fa7c0a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -33,6 +33,22 @@
#include "rt2x00lib.h"
/*
+ * Utility functions.
+ */
+u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_vif *vif)
+{
+ /*
+ * When in STA mode, bssidx is always 0 otherwise local_address[5]
+ * contains the bss number, see BSS_ID_MASK comments for details.
+ */
+ if (rt2x00dev->intf_sta_count)
+ return 0;
+ return vif->addr[5] & (rt2x00dev->ops->max_ap_intf - 1);
+}
+EXPORT_SYMBOL_GPL(rt2x00lib_get_bssidx);
+
+/*
* Radio control handlers.
*/
int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
@@ -449,6 +465,23 @@ static u8 *rt2x00lib_find_ie(u8 *data, unsigned int len, u8 ie)
return NULL;
}
+static void rt2x00lib_sleep(struct work_struct *work)
+{
+ struct rt2x00_dev *rt2x00dev =
+ container_of(work, struct rt2x00_dev, sleep_work);
+
+ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
+ return;
+
+ /*
+ * Check again is powersaving is enabled, to prevent races from delayed
+ * work execution.
+ */
+ if (!test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags))
+ rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf,
+ IEEE80211_CONF_CHANGE_PS);
+}
+
static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
struct sk_buff *skb,
struct rxdone_entry_desc *rxdesc)
@@ -496,8 +529,7 @@ static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
cam |= (tim_ie->bitmap_ctrl & 0x01);
if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags))
- rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf,
- IEEE80211_CONF_CHANGE_PS);
+ queue_work(rt2x00dev->workqueue, &rt2x00dev->sleep_work);
}
static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,
@@ -583,6 +615,18 @@ void rt2x00lib_rxdone(struct queue_entry *entry)
rt2x00dev->ops->lib->fill_rxdone(entry, &rxdesc);
/*
+ * Check for valid size in case we get corrupted descriptor from
+ * hardware.
+ */
+ if (unlikely(rxdesc.size == 0 ||
+ rxdesc.size > entry->queue->data_size)) {
+ WARNING(rt2x00dev, "Wrong frame size %d max %d.\n",
+ rxdesc.size, entry->queue->data_size);
+ dev_kfree_skb(entry->skb);
+ goto renew_skb;
+ }
+
+ /*
* The data behind the ieee80211 header must be
* aligned on a 4 byte boundary.
*/
@@ -642,6 +686,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry)
ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
+renew_skb:
/*
* Replace the skb with the freshly allocated one.
*/
@@ -902,6 +947,11 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE;
/*
+ * Tell mac80211 about the size of our private STA structure.
+ */
+ rt2x00dev->hw->sta_data_size = sizeof(struct rt2x00_sta);
+
+ /*
* Allocate tx status FIFO for driver use.
*/
if (test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags)) {
@@ -933,7 +983,6 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
tasklet_init(&rt2x00dev->taskletname, \
rt2x00dev->ops->lib->taskletname, \
(unsigned long)rt2x00dev); \
- tasklet_disable(&rt2x00dev->taskletname); \
}
RT2X00_TASKLET_INIT(txstatus_tasklet);
@@ -1108,6 +1157,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled);
INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup);
+ INIT_WORK(&rt2x00dev->sleep_work, rt2x00lib_sleep);
/*
* Let the driver probe the device to detect the capabilities.
@@ -1164,6 +1214,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
*/
cancel_work_sync(&rt2x00dev->intf_work);
cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
+ cancel_work_sync(&rt2x00dev->sleep_work);
if (rt2x00_is_usb(rt2x00dev)) {
del_timer_sync(&rt2x00dev->txstatus_timer);
cancel_work_sync(&rt2x00dev->rxdone_work);
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h
index 322cc4f3de5d..4cdf247a870d 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/rt2x00/rt2x00lib.h
@@ -336,7 +336,8 @@ static inline void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev,
*/
#ifdef CONFIG_RT2X00_LIB_CRYPTO
enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key);
-void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry,
+void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
+ struct sk_buff *skb,
struct txentry_desc *txdesc);
unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev,
struct sk_buff *skb);
@@ -354,7 +355,8 @@ static inline enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *
return CIPHER_NONE;
}
-static inline void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry,
+static inline void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
+ struct sk_buff *skb,
struct txentry_desc *txdesc)
{
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 93bec140e598..bf0acff07807 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -113,7 +113,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
* due to possible race conditions in mac80211.
*/
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
- goto exit_fail;
+ goto exit_free_skb;
/*
* Use the ATIM queue if appropriate and present.
@@ -127,7 +127,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
ERROR(rt2x00dev,
"Attempt to send packet over invalid queue %d.\n"
"Please file bug report to %s.\n", qid, DRV_PROJECT);
- goto exit_fail;
+ goto exit_free_skb;
}
/*
@@ -159,6 +159,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
exit_fail:
rt2x00queue_pause_queue(queue);
+ exit_free_skb:
dev_kfree_skb_any(skb);
}
EXPORT_SYMBOL_GPL(rt2x00mac_tx);
@@ -493,6 +494,7 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct rt2x00lib_crypto crypto;
static const u8 bcast_addr[ETH_ALEN] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, };
+ struct rt2x00_sta *sta_priv = NULL;
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
return 0;
@@ -503,24 +505,18 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
memset(&crypto, 0, sizeof(crypto));
- /*
- * When in STA mode, bssidx is always 0 otherwise local_address[5]
- * contains the bss number, see BSS_ID_MASK comments for details.
- */
- if (rt2x00dev->intf_sta_count)
- crypto.bssidx = 0;
- else
- crypto.bssidx = vif->addr[5] & (rt2x00dev->ops->max_ap_intf - 1);
-
+ crypto.bssidx = rt2x00lib_get_bssidx(rt2x00dev, vif);
crypto.cipher = rt2x00crypto_key_to_cipher(key);
if (crypto.cipher == CIPHER_NONE)
return -EOPNOTSUPP;
crypto.cmd = cmd;
- if (sta)
+ if (sta) {
crypto.address = sta->addr;
- else
+ sta_priv = sta_to_rt2x00_sta(sta);
+ crypto.wcid = sta_priv->wcid;
+ } else
crypto.address = bcast_addr;
if (crypto.cipher == CIPHER_TKIP)
@@ -559,6 +555,39 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
EXPORT_SYMBOL_GPL(rt2x00mac_set_key);
#endif /* CONFIG_RT2X00_LIB_CRYPTO */
+int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+ struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta);
+
+ /*
+ * If there's no space left in the device table store
+ * -1 as wcid but tell mac80211 everything went ok.
+ */
+ if (rt2x00dev->ops->lib->sta_add(rt2x00dev, vif, sta))
+ sta_priv->wcid = -1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2x00mac_sta_add);
+
+int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+ struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta);
+
+ /*
+ * If we never sent the STA to the device no need to clean it up.
+ */
+ if (sta_priv->wcid < 0)
+ return 0;
+
+ return rt2x00dev->ops->lib->sta_remove(rt2x00dev, sta_priv->wcid);
+}
+EXPORT_SYMBOL_GPL(rt2x00mac_sta_remove);
+
void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
@@ -684,7 +713,8 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed);
-int rt2x00mac_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
+int rt2x00mac_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue_idx,
const struct ieee80211_tx_queue_params *params)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
@@ -818,3 +848,17 @@ void rt2x00mac_get_ringparam(struct ieee80211_hw *hw,
*rx_max = rt2x00dev->rx->limit;
}
EXPORT_SYMBOL_GPL(rt2x00mac_get_ringparam);
+
+bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw)
+{
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+ struct data_queue *queue;
+
+ tx_queue_for_each(rt2x00dev, queue) {
+ if (!rt2x00queue_empty(queue))
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(rt2x00mac_tx_frames_pending);
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index ab8c16f8bcaf..5adfb3eab9cd 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -200,20 +200,20 @@ void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
skb_pull(skb, l2pad);
}
-static void rt2x00queue_create_tx_descriptor_seq(struct queue_entry *entry,
+static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev,
+ struct sk_buff *skb,
struct txentry_desc *txdesc)
{
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif);
- unsigned long irqflags;
if (!(tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
return;
__set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
- if (!test_bit(REQUIRE_SW_SEQNO, &entry->queue->rt2x00dev->cap_flags))
+ if (!test_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags))
return;
/*
@@ -227,23 +227,23 @@ static void rt2x00queue_create_tx_descriptor_seq(struct queue_entry *entry,
* sequence counting per-frame, since those will override the
* sequence counter given by mac80211.
*/
- spin_lock_irqsave(&intf->seqlock, irqflags);
+ spin_lock(&intf->seqlock);
if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags))
intf->seqno += 0x10;
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
hdr->seq_ctrl |= cpu_to_le16(intf->seqno);
- spin_unlock_irqrestore(&intf->seqlock, irqflags);
+ spin_unlock(&intf->seqlock);
}
-static void rt2x00queue_create_tx_descriptor_plcp(struct queue_entry *entry,
+static void rt2x00queue_create_tx_descriptor_plcp(struct rt2x00_dev *rt2x00dev,
+ struct sk_buff *skb,
struct txentry_desc *txdesc,
const struct rt2x00_rate *hwrate)
{
- struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0];
unsigned int data_length;
unsigned int duration;
@@ -260,8 +260,8 @@ static void rt2x00queue_create_tx_descriptor_plcp(struct queue_entry *entry,
txdesc->u.plcp.ifs = IFS_SIFS;
/* Data length + CRC + Crypto overhead (IV/EIV/ICV/MIC) */
- data_length = entry->skb->len + 4;
- data_length += rt2x00crypto_tx_overhead(rt2x00dev, entry->skb);
+ data_length = skb->len + 4;
+ data_length += rt2x00crypto_tx_overhead(rt2x00dev, skb);
/*
* PLCP setup
@@ -302,18 +302,24 @@ static void rt2x00queue_create_tx_descriptor_plcp(struct queue_entry *entry,
}
}
-static void rt2x00queue_create_tx_descriptor_ht(struct queue_entry *entry,
+static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev,
+ struct sk_buff *skb,
struct txentry_desc *txdesc,
const struct rt2x00_rate *hwrate)
{
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0];
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct rt2x00_sta *sta_priv = NULL;
- if (tx_info->control.sta)
+ if (tx_info->control.sta) {
txdesc->u.ht.mpdu_density =
tx_info->control.sta->ht_cap.ampdu_density;
+ sta_priv = sta_to_rt2x00_sta(tx_info->control.sta);
+ txdesc->u.ht.wcid = sta_priv->wcid;
+ }
+
txdesc->u.ht.ba_size = 7; /* FIXME: What value is needed? */
/*
@@ -381,12 +387,12 @@ static void rt2x00queue_create_tx_descriptor_ht(struct queue_entry *entry,
txdesc->u.ht.txop = TXOP_HTTXOP;
}
-static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
+static void rt2x00queue_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
+ struct sk_buff *skb,
struct txentry_desc *txdesc)
{
- struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0];
struct ieee80211_rate *rate;
const struct rt2x00_rate *hwrate = NULL;
@@ -396,8 +402,8 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
/*
* Header and frame information.
*/
- txdesc->length = entry->skb->len;
- txdesc->header_length = ieee80211_get_hdrlen_from_skb(entry->skb);
+ txdesc->length = skb->len;
+ txdesc->header_length = ieee80211_get_hdrlen_from_skb(skb);
/*
* Check whether this frame is to be acked.
@@ -472,13 +478,15 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
/*
* Apply TX descriptor handling by components
*/
- rt2x00crypto_create_tx_descriptor(entry, txdesc);
- rt2x00queue_create_tx_descriptor_seq(entry, txdesc);
+ rt2x00crypto_create_tx_descriptor(rt2x00dev, skb, txdesc);
+ rt2x00queue_create_tx_descriptor_seq(rt2x00dev, skb, txdesc);
if (test_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags))
- rt2x00queue_create_tx_descriptor_ht(entry, txdesc, hwrate);
+ rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc,
+ hwrate);
else
- rt2x00queue_create_tx_descriptor_plcp(entry, txdesc, hwrate);
+ rt2x00queue_create_tx_descriptor_plcp(rt2x00dev, skb, txdesc,
+ hwrate);
}
static int rt2x00queue_write_tx_data(struct queue_entry *entry,
@@ -556,33 +564,18 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
bool local)
{
struct ieee80211_tx_info *tx_info;
- struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX);
+ struct queue_entry *entry;
struct txentry_desc txdesc;
struct skb_frame_desc *skbdesc;
u8 rate_idx, rate_flags;
-
- if (unlikely(rt2x00queue_full(queue))) {
- ERROR(queue->rt2x00dev,
- "Dropping frame due to full tx queue %d.\n", queue->qid);
- return -ENOBUFS;
- }
-
- if (unlikely(test_and_set_bit(ENTRY_OWNER_DEVICE_DATA,
- &entry->flags))) {
- ERROR(queue->rt2x00dev,
- "Arrived at non-free entry in the non-full queue %d.\n"
- "Please file bug report to %s.\n",
- queue->qid, DRV_PROJECT);
- return -EINVAL;
- }
+ int ret = 0;
/*
* Copy all TX descriptor information into txdesc,
* after that we are free to use the skb->cb array
* for our information.
*/
- entry->skb = skb;
- rt2x00queue_create_tx_descriptor(entry, &txdesc);
+ rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc);
/*
* All information is retrieved from the skb->cb array,
@@ -594,7 +587,6 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
rate_flags = tx_info->control.rates[0].flags;
skbdesc = get_skb_frame_desc(skb);
memset(skbdesc, 0, sizeof(*skbdesc));
- skbdesc->entry = entry;
skbdesc->tx_rate_idx = rate_idx;
skbdesc->tx_rate_flags = rate_flags;
@@ -623,9 +615,33 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
* for PCI devices.
*/
if (test_bit(REQUIRE_L2PAD, &queue->rt2x00dev->cap_flags))
- rt2x00queue_insert_l2pad(entry->skb, txdesc.header_length);
+ rt2x00queue_insert_l2pad(skb, txdesc.header_length);
else if (test_bit(REQUIRE_DMA, &queue->rt2x00dev->cap_flags))
- rt2x00queue_align_frame(entry->skb);
+ rt2x00queue_align_frame(skb);
+
+ spin_lock(&queue->tx_lock);
+
+ if (unlikely(rt2x00queue_full(queue))) {
+ ERROR(queue->rt2x00dev,
+ "Dropping frame due to full tx queue %d.\n", queue->qid);
+ ret = -ENOBUFS;
+ goto out;
+ }
+
+ entry = rt2x00queue_get_entry(queue, Q_INDEX);
+
+ if (unlikely(test_and_set_bit(ENTRY_OWNER_DEVICE_DATA,
+ &entry->flags))) {
+ ERROR(queue->rt2x00dev,
+ "Arrived at non-free entry in the non-full queue %d.\n"
+ "Please file bug report to %s.\n",
+ queue->qid, DRV_PROJECT);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ skbdesc->entry = entry;
+ entry->skb = skb;
/*
* It could be possible that the queue was corrupted and this
@@ -635,7 +651,8 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
if (unlikely(rt2x00queue_write_tx_data(entry, &txdesc))) {
clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
entry->skb = NULL;
- return -EIO;
+ ret = -EIO;
+ goto out;
}
set_bit(ENTRY_DATA_PENDING, &entry->flags);
@@ -644,7 +661,9 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
rt2x00queue_write_tx_descriptor(entry, &txdesc);
rt2x00queue_kick_tx_queue(queue, &txdesc);
- return 0;
+out:
+ spin_unlock(&queue->tx_lock);
+ return ret;
}
int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
@@ -698,7 +717,7 @@ int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev,
* after that we are free to use the skb->cb array
* for our information.
*/
- rt2x00queue_create_tx_descriptor(intf->beacon, &txdesc);
+ rt2x00queue_create_tx_descriptor(rt2x00dev, intf->beacon->skb, &txdesc);
/*
* Fill in skb descriptor
@@ -1185,6 +1204,7 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
struct data_queue *queue, enum data_queue_qid qid)
{
mutex_init(&queue->status_lock);
+ spin_lock_init(&queue->tx_lock);
spin_lock_init(&queue->index_lock);
queue->rt2x00dev = rt2x00dev;
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index 167d45873dca..349008d1fb28 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -54,7 +54,7 @@
* @QID_RX: RX queue
* @QID_OTHER: None of the above (don't use, only present for completeness)
* @QID_BEACON: Beacon queue (value unspecified, don't send it to device)
- * @QID_ATIM: Atim queue (value unspeficied, don't send it to device)
+ * @QID_ATIM: Atim queue (value unspecified, don't send it to device)
*/
enum data_queue_qid {
QID_AC_VO = 0,
@@ -288,8 +288,8 @@ enum txentry_desc_flags {
* @signal: PLCP signal.
* @service: PLCP service.
* @msc: MCS.
- * @stbc: STBC.
- * @ba_size: BA size.
+ * @stbc: Use Space Time Block Coding (only available for MCS rates < 8).
+ * @ba_size: Size of the recepients RX reorder buffer - 1.
* @rate_mode: Rate mode (See @enum rate_modulation).
* @mpdu_density: MDPU density.
* @retry_limit: Max number of retries.
@@ -321,6 +321,7 @@ struct txentry_desc {
u8 ba_size;
u8 mpdu_density;
enum txop txop;
+ int wcid;
} ht;
} u;
@@ -432,6 +433,7 @@ enum data_queue_flags {
* @flags: Entry flags, see &enum queue_entry_flags.
* @status_lock: The mutex for protecting the start/stop/flush
* handling on this queue.
+ * @tx_lock: Spinlock to serialize tx operations on this queue.
* @index_lock: Spinlock to protect index handling. Whenever @index, @index_done or
* @index_crypt needs to be changed this lock should be grabbed to prevent
* index corruption due to concurrency.
@@ -458,6 +460,7 @@ struct data_queue {
unsigned long flags;
struct mutex status_lock;
+ spinlock_t tx_lock;
spinlock_t index_lock;
unsigned int count;
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index 8f90f6268077..1e31050dafc9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -262,23 +262,20 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
struct queue_entry *entry = (struct queue_entry *)urb->context;
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
- if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
+ if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
return;
-
- if (rt2x00dev->ops->lib->tx_dma_done)
- rt2x00dev->ops->lib->tx_dma_done(entry);
-
- /*
- * Report the frame as DMA done
- */
- rt2x00lib_dmadone(entry);
-
/*
* Check if the frame was correctly uploaded
*/
if (urb->status)
set_bit(ENTRY_DATA_IO_FAILED, &entry->flags);
+ /*
+ * Report the frame as DMA done
+ */
+ rt2x00lib_dmadone(entry);
+ if (rt2x00dev->ops->lib->tx_dma_done)
+ rt2x00dev->ops->lib->tx_dma_done(entry);
/*
* Schedule the delayed work for reading the TX status
* from the device.
@@ -802,6 +799,7 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
int retval;
usb_dev = usb_get_dev(usb_dev);
+ usb_reset_device(usb_dev);
hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
if (!hw) {
@@ -873,18 +871,8 @@ int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state)
{
struct ieee80211_hw *hw = usb_get_intfdata(usb_intf);
struct rt2x00_dev *rt2x00dev = hw->priv;
- int retval;
-
- retval = rt2x00lib_suspend(rt2x00dev, state);
- if (retval)
- return retval;
- /*
- * Decrease usbdev refcount.
- */
- usb_put_dev(interface_to_usbdev(usb_intf));
-
- return 0;
+ return rt2x00lib_suspend(rt2x00dev, state);
}
EXPORT_SYMBOL_GPL(rt2x00usb_suspend);
@@ -893,8 +881,6 @@ int rt2x00usb_resume(struct usb_interface *usb_intf)
struct ieee80211_hw *hw = usb_get_intfdata(usb_intf);
struct rt2x00_dev *rt2x00dev = hw->priv;
- usb_get_dev(interface_to_usbdev(usb_intf));
-
return rt2x00lib_resume(rt2x00dev);
}
EXPORT_SYMBOL_GPL(rt2x00usb_resume);
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index 9d35ec16a3a5..bf55b4a311e3 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -1142,11 +1142,6 @@ static void rt61pci_start_queue(struct data_queue *queue)
rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg);
break;
case QID_BEACON:
- /*
- * Allow the tbtt tasklet to be scheduled.
- */
- tasklet_enable(&rt2x00dev->tbtt_tasklet);
-
rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, &reg);
rt2x00_set_field32(&reg, TXRX_CSR9_TSF_TICKING, 1);
rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 1);
@@ -1230,7 +1225,7 @@ static void rt61pci_stop_queue(struct data_queue *queue)
/*
* Wait for possibly running tbtt tasklets.
*/
- tasklet_disable(&rt2x00dev->tbtt_tasklet);
+ tasklet_kill(&rt2x00dev->tbtt_tasklet);
break;
default:
break;
@@ -1731,13 +1726,6 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
rt2x00pci_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, &reg);
rt2x00pci_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg);
-
- /*
- * Enable tasklets.
- */
- tasklet_enable(&rt2x00dev->txstatus_tasklet);
- tasklet_enable(&rt2x00dev->rxdone_tasklet);
- tasklet_enable(&rt2x00dev->autowake_tasklet);
}
/*
@@ -1772,9 +1760,10 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
/*
* Ensure that all tasklets are finished.
*/
- tasklet_disable(&rt2x00dev->txstatus_tasklet);
- tasklet_disable(&rt2x00dev->rxdone_tasklet);
- tasklet_disable(&rt2x00dev->autowake_tasklet);
+ tasklet_kill(&rt2x00dev->txstatus_tasklet);
+ tasklet_kill(&rt2x00dev->rxdone_tasklet);
+ tasklet_kill(&rt2x00dev->autowake_tasklet);
+ tasklet_kill(&rt2x00dev->tbtt_tasklet);
}
}
@@ -2300,22 +2289,24 @@ static void rt61pci_txstatus_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt61pci_txdone(rt2x00dev);
- rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TXDONE);
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TXDONE);
}
static void rt61pci_tbtt_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt2x00lib_beacondone(rt2x00dev);
- rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_BEACON_DONE);
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_BEACON_DONE);
}
static void rt61pci_rxdone_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
if (rt2x00pci_rxdone(rt2x00dev))
- rt2x00pci_rxdone(rt2x00dev);
- else
+ tasklet_schedule(&rt2x00dev->rxdone_tasklet);
+ else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RXDONE);
}
@@ -2325,7 +2316,8 @@ static void rt61pci_autowake_tasklet(unsigned long data)
rt61pci_wakeup(rt2x00dev);
rt2x00pci_register_write(rt2x00dev,
M2H_CMD_DONE_CSR, 0xffffffff);
- rt61pci_enable_mcu_interrupt(rt2x00dev, MCU_INT_MASK_CSR_TWAKEUP);
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ rt61pci_enable_mcu_interrupt(rt2x00dev, MCU_INT_MASK_CSR_TWAKEUP);
}
static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance)
@@ -2891,7 +2883,8 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev)
/*
* IEEE80211 stack callback functions.
*/
-static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
+static int rt61pci_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue_idx,
const struct ieee80211_tx_queue_params *params)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
@@ -2907,7 +2900,7 @@ static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
* we are free to update the registers based on the value
* in the queue parameter.
*/
- retval = rt2x00mac_conf_tx(hw, queue_idx, params);
+ retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params);
if (retval)
return retval;
@@ -2948,7 +2941,7 @@ static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
return 0;
}
-static u64 rt61pci_get_tsf(struct ieee80211_hw *hw)
+static u64 rt61pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
u64 tsf;
@@ -2982,6 +2975,7 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = {
.set_antenna = rt2x00mac_set_antenna,
.get_antenna = rt2x00mac_get_antenna,
.get_ringparam = rt2x00mac_get_ringparam,
+ .tx_frames_pending = rt2x00mac_tx_frames_pending,
};
static const struct rt2x00lib_ops rt61pci_rt2x00_ops = {
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index ad20953cbf05..cfb19dbb0a67 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -2222,7 +2222,8 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
/*
* IEEE80211 stack callback functions.
*/
-static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
+static int rt73usb_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue_idx,
const struct ieee80211_tx_queue_params *params)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
@@ -2238,7 +2239,7 @@ static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
* we are free to update the registers based on the value
* in the queue parameter.
*/
- retval = rt2x00mac_conf_tx(hw, queue_idx, params);
+ retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params);
if (retval)
return retval;
@@ -2279,7 +2280,7 @@ static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
return 0;
}
-static u64 rt73usb_get_tsf(struct ieee80211_hw *hw)
+static u64 rt73usb_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
u64 tsf;
@@ -2314,6 +2315,7 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = {
.set_antenna = rt2x00mac_set_antenna,
.get_antenna = rt2x00mac_get_antenna,
.get_ringparam = rt2x00mac_get_ringparam,
+ .tx_frames_pending = rt2x00mac_tx_frames_pending,
};
static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
@@ -2419,6 +2421,7 @@ static struct usb_device_id rt73usb_device_table[] = {
/* Buffalo */
{ USB_DEVICE(0x0411, 0x00d8) },
{ USB_DEVICE(0x0411, 0x00d9) },
+ { USB_DEVICE(0x0411, 0x00e6) },
{ USB_DEVICE(0x0411, 0x00f4) },
{ USB_DEVICE(0x0411, 0x0116) },
{ USB_DEVICE(0x0411, 0x0119) },
diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c
index 80db5cabc9b9..2f14a5fb0cbb 100644
--- a/drivers/net/wireless/rtl818x/rtl8180/dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c
@@ -16,11 +16,13 @@
*/
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
#include <linux/eeprom_93cx6.h>
+#include <linux/module.h>
#include <net/mac80211.h>
#include "rtl8180.h"
@@ -668,7 +670,8 @@ static void rtl8180_stop(struct ieee80211_hw *dev)
rtl8180_free_tx_ring(dev, i);
}
-static u64 rtl8180_get_tsf(struct ieee80211_hw *dev)
+static u64 rtl8180_get_tsf(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
{
struct rtl8180_priv *priv = dev->priv;
@@ -700,7 +703,7 @@ static void rtl8180_beacon_work(struct work_struct *work)
* TODO: make hardware update beacon timestamp
*/
mgmt = (struct ieee80211_mgmt *)skb->data;
- mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev));
+ mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev, vif));
/* TODO: use actual beacon queue */
skb_set_queue_mapping(skb, 0);
diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c
index 1e0be14d10d4..4a78f9e39dfa 100644
--- a/drivers/net/wireless/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/etherdevice.h>
#include <linux/eeprom_93cx6.h>
+#include <linux/module.h>
#include <net/mac80211.h>
#include "rtl8187.h"
@@ -1241,7 +1242,8 @@ static void rtl8187_configure_filter(struct ieee80211_hw *dev,
rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf);
}
-static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue,
+static int rtl8187_conf_tx(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct rtl8187_priv *priv = dev->priv;
@@ -1277,7 +1279,7 @@ static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue,
return 0;
}
-static u64 rtl8187_get_tsf(struct ieee80211_hw *dev)
+static u64 rtl8187_get_tsf(struct ieee80211_hw *dev, struct ieee80211_vif *vif)
{
struct rtl8187_priv *priv = dev->priv;
diff --git a/drivers/net/wireless/rtlwifi/Kconfig b/drivers/net/wireless/rtlwifi/Kconfig
index 5aee8b22d74e..d6c42e69bdbd 100644
--- a/drivers/net/wireless/rtlwifi/Kconfig
+++ b/drivers/net/wireless/rtlwifi/Kconfig
@@ -12,7 +12,7 @@ config RTL8192CE
config RTL8192SE
tristate "Realtek RTL8192SE/RTL8191SE PCIe Wireless Network Adapter"
- depends on MAC80211 && EXPERIMENTAL
+ depends on MAC80211 && EXPERIMENTAL && PCI
select FW_LOADER
select RTLWIFI
---help---
@@ -21,6 +21,17 @@ config RTL8192SE
If you choose to build it as a module, it will be called rtl8192se
+config RTL8192DE
+ tristate "Realtek RTL8192DE/RTL8188DE PCIe Wireless Network Adapter"
+ depends on MAC80211 && EXPERIMENTAL && PCI
+ select FW_LOADER
+ select RTLWIFI
+ ---help---
+ This is the driver for Realtek RTL8192DE/RTL8188DE 802.11n PCIe
+ wireless network adapters.
+
+ If you choose to build it as a module, it will be called rtl8192de
+
config RTL8192CU
tristate "Realtek RTL8192CU/RTL8188CU USB Wireless Network Adapter"
depends on MAC80211 && USB && EXPERIMENTAL
@@ -35,10 +46,10 @@ config RTL8192CU
config RTLWIFI
tristate
- depends on RTL8192CE || RTL8192CU || RTL8192SE
+ depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE
default m
config RTL8192C_COMMON
tristate
- depends on RTL8192CE || RTL8192CU || RTL8192SE
+ depends on RTL8192CE || RTL8192CU
default m
diff --git a/drivers/net/wireless/rtlwifi/Makefile b/drivers/net/wireless/rtlwifi/Makefile
index 7acce83c3785..97935c565bab 100644
--- a/drivers/net/wireless/rtlwifi/Makefile
+++ b/drivers/net/wireless/rtlwifi/Makefile
@@ -23,5 +23,6 @@ obj-$(CONFIG_RTL8192C_COMMON) += rtl8192c/
obj-$(CONFIG_RTL8192CE) += rtl8192ce/
obj-$(CONFIG_RTL8192CU) += rtl8192cu/
obj-$(CONFIG_RTL8192SE) += rtl8192se/
+obj-$(CONFIG_RTL8192DE) += rtl8192de/
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c
index ccb6da38fe22..b4ce93436d2e 100644
--- a/drivers/net/wireless/rtlwifi/base.c
+++ b/drivers/net/wireless/rtlwifi/base.c
@@ -27,7 +27,10 @@
*
*****************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/ip.h>
+#include <linux/module.h>
#include "wifi.h"
#include "rc.h"
#include "base.h"
@@ -309,6 +312,8 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw)
IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_BEACON_FILTER |
IEEE80211_HW_AMPDU_AGGREGATION |
+ IEEE80211_HW_CONNECTION_MONITOR |
+ /* IEEE80211_HW_SUPPORTS_CQM_RSSI | */
IEEE80211_HW_REPORTS_TX_ACK_STATUS | 0;
/* swlps or hwlps has been set in diff chip in init_sw_vars */
@@ -397,8 +402,8 @@ void rtl_init_rfkill(struct ieee80211_hw *hw)
radio_state = rtlpriv->cfg->ops->radio_onoff_checking(hw, &valid);
if (valid) {
- printk(KERN_INFO "rtlwifi: wireless switch is %s\n",
- rtlpriv->rfkill.rfkill_state ? "on" : "off");
+ pr_info("wireless switch is %s\n",
+ rtlpriv->rfkill.rfkill_state ? "on" : "off");
rtlpriv->rfkill.rfkill_state = radio_state;
@@ -523,7 +528,7 @@ static void _rtl_query_shortgi(struct ieee80211_hw *hw,
mac->opmode == NL80211_IFTYPE_ADHOC)
bw_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- if ((bw_40 == true) && sgi_40)
+ if (bw_40 && sgi_40)
tcb_desc->use_shortgi = true;
else if ((bw_40 == false) && sgi_20)
tcb_desc->use_shortgi = true;
@@ -662,6 +667,167 @@ static u8 _rtl_get_highest_n_rate(struct ieee80211_hw *hw)
return hw_rate;
}
+/* mac80211's rate_idx is like this:
+ *
+ * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ
+ *
+ * B/G rate:
+ * (rx_status->flag & RX_FLAG_HT) = 0,
+ * DESC92_RATE1M-->DESC92_RATE54M ==> idx is 0-->11,
+ *
+ * N rate:
+ * (rx_status->flag & RX_FLAG_HT) = 1,
+ * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15
+ *
+ * 5G band:rx_status->band == IEEE80211_BAND_5GHZ
+ * A rate:
+ * (rx_status->flag & RX_FLAG_HT) = 0,
+ * DESC92_RATE6M-->DESC92_RATE54M ==> idx is 0-->7,
+ *
+ * N rate:
+ * (rx_status->flag & RX_FLAG_HT) = 1,
+ * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15
+ */
+int rtlwifi_rate_mapping(struct ieee80211_hw *hw,
+ bool isht, u8 desc_rate, bool first_ampdu)
+{
+ int rate_idx;
+
+ if (false == isht) {
+ if (IEEE80211_BAND_2GHZ == hw->conf.channel->band) {
+ switch (desc_rate) {
+ case DESC92_RATE1M:
+ rate_idx = 0;
+ break;
+ case DESC92_RATE2M:
+ rate_idx = 1;
+ break;
+ case DESC92_RATE5_5M:
+ rate_idx = 2;
+ break;
+ case DESC92_RATE11M:
+ rate_idx = 3;
+ break;
+ case DESC92_RATE6M:
+ rate_idx = 4;
+ break;
+ case DESC92_RATE9M:
+ rate_idx = 5;
+ break;
+ case DESC92_RATE12M:
+ rate_idx = 6;
+ break;
+ case DESC92_RATE18M:
+ rate_idx = 7;
+ break;
+ case DESC92_RATE24M:
+ rate_idx = 8;
+ break;
+ case DESC92_RATE36M:
+ rate_idx = 9;
+ break;
+ case DESC92_RATE48M:
+ rate_idx = 10;
+ break;
+ case DESC92_RATE54M:
+ rate_idx = 11;
+ break;
+ default:
+ rate_idx = 0;
+ break;
+ }
+ } else {
+ switch (desc_rate) {
+ case DESC92_RATE6M:
+ rate_idx = 0;
+ break;
+ case DESC92_RATE9M:
+ rate_idx = 1;
+ break;
+ case DESC92_RATE12M:
+ rate_idx = 2;
+ break;
+ case DESC92_RATE18M:
+ rate_idx = 3;
+ break;
+ case DESC92_RATE24M:
+ rate_idx = 4;
+ break;
+ case DESC92_RATE36M:
+ rate_idx = 5;
+ break;
+ case DESC92_RATE48M:
+ rate_idx = 6;
+ break;
+ case DESC92_RATE54M:
+ rate_idx = 7;
+ break;
+ default:
+ rate_idx = 0;
+ break;
+ }
+ }
+
+ } else {
+
+ switch (desc_rate) {
+ case DESC92_RATEMCS0:
+ rate_idx = 0;
+ break;
+ case DESC92_RATEMCS1:
+ rate_idx = 1;
+ break;
+ case DESC92_RATEMCS2:
+ rate_idx = 2;
+ break;
+ case DESC92_RATEMCS3:
+ rate_idx = 3;
+ break;
+ case DESC92_RATEMCS4:
+ rate_idx = 4;
+ break;
+ case DESC92_RATEMCS5:
+ rate_idx = 5;
+ break;
+ case DESC92_RATEMCS6:
+ rate_idx = 6;
+ break;
+ case DESC92_RATEMCS7:
+ rate_idx = 7;
+ break;
+ case DESC92_RATEMCS8:
+ rate_idx = 8;
+ break;
+ case DESC92_RATEMCS9:
+ rate_idx = 9;
+ break;
+ case DESC92_RATEMCS10:
+ rate_idx = 10;
+ break;
+ case DESC92_RATEMCS11:
+ rate_idx = 11;
+ break;
+ case DESC92_RATEMCS12:
+ rate_idx = 12;
+ break;
+ case DESC92_RATEMCS13:
+ rate_idx = 13;
+ break;
+ case DESC92_RATEMCS14:
+ rate_idx = 14;
+ break;
+ case DESC92_RATEMCS15:
+ rate_idx = 15;
+ break;
+ default:
+ rate_idx = 0;
+ break;
+ }
+ }
+ return rate_idx;
+}
+EXPORT_SYMBOL(rtlwifi_rate_mapping);
+
void rtl_get_tcb_desc(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
@@ -687,7 +853,7 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw,
*So tcb_desc->hw_rate is just used for
*special data and mgt frames
*/
- if (info->control.rates[0].idx == 0 &&
+ if (info->control.rates[0].idx == 0 ||
ieee80211_is_nullfunc(fc)) {
tcb_desc->use_driver_rate = true;
tcb_desc->ratr_index = RATR_INX_WIRELESS_MC;
@@ -756,18 +922,17 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
return false;
RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG,
- ("%s ACT_ADDBAREQ From :" MAC_FMT "\n",
- is_tx ? "Tx" : "Rx", MAC_ARG(hdr->addr2)));
+ ("%s ACT_ADDBAREQ From :%pM\n",
+ is_tx ? "Tx" : "Rx", hdr->addr2));
break;
case ACT_ADDBARSP:
RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG,
- ("%s ACT_ADDBARSP From :" MAC_FMT "\n",
- is_tx ? "Tx" : "Rx", MAC_ARG(hdr->addr2)));
+ ("%s ACT_ADDBARSP From :%pM\n",
+ is_tx ? "Tx" : "Rx", hdr->addr2));
break;
case ACT_DELBA:
RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG,
- ("ACT_ADDBADEL From :" MAC_FMT "\n",
- MAC_ARG(hdr->addr2)));
+ ("ACT_ADDBADEL From :%pM\n", hdr->addr2));
break;
}
break;
@@ -888,7 +1053,6 @@ int rtl_tx_agg_stop(struct ieee80211_hw *hw,
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
- struct rtl_tid_data *tid_data;
struct rtl_sta_info *sta_entry = NULL;
if (sta == NULL)
@@ -906,7 +1070,6 @@ int rtl_tx_agg_stop(struct ieee80211_hw *hw,
return -EINVAL;
sta_entry = (struct rtl_sta_info *)sta->drv_priv;
- tid_data = &sta_entry->tids[tid];
sta_entry->tids[tid].agg.agg_state = RTL_AGG_STOP;
ieee80211_stop_tx_ba_cb_irqsafe(mac->vif, sta->addr, tid);
@@ -918,7 +1081,6 @@ int rtl_tx_agg_oper(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, u16 tid)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_tid_data *tid_data;
struct rtl_sta_info *sta_entry = NULL;
if (sta == NULL)
@@ -936,7 +1098,6 @@ int rtl_tx_agg_oper(struct ieee80211_hw *hw,
return -EINVAL;
sta_entry = (struct rtl_sta_info *)sta->drv_priv;
- tid_data = &sta_entry->tids[tid];
sta_entry->tids[tid].agg.agg_state = RTL_AGG_OPERATIONAL;
return 0;
@@ -980,7 +1141,7 @@ void rtl_watchdog_wq_callback(void *data)
}
/*
- *<3> to check if traffic busy, if
+ *<2> to check if traffic busy, if
* busytraffic we don't change channel
*/
if (mac->link_state >= MAC80211_LINKED) {
@@ -1406,8 +1567,7 @@ MODULE_DESCRIPTION("Realtek 802.11n PCI wireless core");
static int __init rtl_core_module_init(void)
{
if (rtl_rate_control_register())
- printk(KERN_ERR "rtlwifi: Unable to register rtl_rc,"
- "use default RC !!\n");
+ pr_err("Unable to register rtl_rc, use default RC !!\n");
return 0;
}
diff --git a/drivers/net/wireless/rtlwifi/base.h b/drivers/net/wireless/rtlwifi/base.h
index a91f3eee59c8..4ae905983d0d 100644
--- a/drivers/net/wireless/rtlwifi/base.h
+++ b/drivers/net/wireless/rtlwifi/base.h
@@ -140,4 +140,6 @@ u8 *rtl_find_ie(u8 *data, unsigned int len, u8 ie);
void rtl_recognize_peer(struct ieee80211_hw *hw, u8 *data, unsigned int len);
u8 rtl_tid_to_ac(struct ieee80211_hw *hw, u8 tid);
extern struct attribute_group rtl_attribute_group;
+int rtlwifi_rate_mapping(struct ieee80211_hw *hw,
+ bool isht, u8 desc_rate, bool first_ampdu);
#endif
diff --git a/drivers/net/wireless/rtlwifi/cam.c b/drivers/net/wireless/rtlwifi/cam.c
index 7295af0536b7..dc36d7461caa 100644
--- a/drivers/net/wireless/rtlwifi/cam.c
+++ b/drivers/net/wireless/rtlwifi/cam.c
@@ -27,6 +27,9 @@
*
*****************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/export.h>
#include "wifi.h"
#include "cam.h"
@@ -131,9 +134,9 @@ u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr,
RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
("EntryNo:%x, ulKeyId=%x, ulEncAlg=%x, "
- "ulUseDK=%x MacAddr" MAC_FMT "\n",
+ "ulUseDK=%x MacAddr %pM\n",
ul_entry_idx, ul_key_id, ul_enc_alg,
- ul_default_key, MAC_ARG(mac_addr)));
+ ul_default_key, mac_addr));
if (ul_key_id == TOTAL_CAM_ENTRY) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
@@ -347,7 +350,7 @@ void rtl_cam_del_entry(struct ieee80211_hw *hw, u8 *sta_addr)
/* Remove from HW Security CAM */
memset(rtlpriv->sec.hwsec_cam_sta_addr[i], 0, ETH_ALEN);
rtlpriv->sec.hwsec_cam_bitmap &= ~(BIT(0) << i);
- printk(KERN_INFO "&&&&&&&&&del entry %d\n", i);
+ pr_info("&&&&&&&&&del entry %d\n", i);
}
}
return;
diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c
index d2ec2535aa3c..3f0f056fae9c 100644
--- a/drivers/net/wireless/rtlwifi/core.c
+++ b/drivers/net/wireless/rtlwifi/core.c
@@ -335,8 +335,8 @@ static int rtl_op_config(struct ieee80211_hw *hw, u32 changed)
* before going offchannel, or dis-association or delete BA will
* happen by AP
*/
- if (rtlpriv->mac80211.offchan_deley) {
- rtlpriv->mac80211.offchan_deley = false;
+ if (rtlpriv->mac80211.offchan_delay) {
+ rtlpriv->mac80211.offchan_delay = false;
mdelay(50);
}
rtlphy->current_channel = wide_chan;
@@ -443,11 +443,11 @@ static int rtl_op_sta_add(struct ieee80211_hw *hw,
sta_entry->wireless_mode = WIRELESS_MODE_G;
if (sta->supp_rates[0] <= 0xf)
sta_entry->wireless_mode = WIRELESS_MODE_B;
- if (sta->ht_cap.ht_supported == true)
+ if (sta->ht_cap.ht_supported)
sta_entry->wireless_mode = WIRELESS_MODE_N_24G;
} else if (rtlhal->current_bandtype == BAND_ON_5G) {
sta_entry->wireless_mode = WIRELESS_MODE_A;
- if (sta->ht_cap.ht_supported == true)
+ if (sta->ht_cap.ht_supported)
sta_entry->wireless_mode = WIRELESS_MODE_N_24G;
}
@@ -456,7 +456,7 @@ static int rtl_op_sta_add(struct ieee80211_hw *hw,
sta_entry->wireless_mode = WIRELESS_MODE_G;
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG,
- ("Add sta addr is "MAC_FMT"\n", MAC_ARG(sta->addr)));
+ ("Add sta addr is %pM\n", sta->addr));
rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0);
}
return 0;
@@ -469,7 +469,7 @@ static int rtl_op_sta_remove(struct ieee80211_hw *hw,
struct rtl_sta_info *sta_entry;
if (sta) {
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG,
- ("Remove sta addr is "MAC_FMT"\n", MAC_ARG(sta->addr)));
+ ("Remove sta addr is %pM\n", sta->addr));
sta_entry = (struct rtl_sta_info *) sta->drv_priv;
sta_entry->wireless_mode = 0;
sta_entry->ratr_index = 0;
@@ -504,7 +504,8 @@ static int _rtl_get_hal_qnum(u16 queue)
*for mac80211 VO=0, VI=1, BE=2, BK=3
*for rtl819x BE=0, BK=1, VI=2, VO=3
*/
-static int rtl_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
+static int rtl_op_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *param)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -610,6 +611,11 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
mac->link_state = MAC80211_NOLINK;
memset(mac->bssid, 0, 6);
+
+ /* reset sec info */
+ rtl_cam_reset_sec_info(hw);
+
+ rtl_cam_reset_all_entry(hw);
mac->vendor = PEER_UNKNOWN;
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG,
@@ -650,7 +656,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE,
("BSS_CHANGED_HT\n"));
rcu_read_lock();
- sta = get_sta(hw, vif, (u8 *)bss_conf->bssid);
+ sta = get_sta(hw, vif, bss_conf->bssid);
if (sta) {
if (sta->ht_cap.ampdu_density >
mac->current_ampdu_density)
@@ -678,14 +684,14 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
(u8 *) bss_conf->bssid);
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG,
- (MAC_FMT "\n", MAC_ARG(bss_conf->bssid)));
+ ("%pM\n", bss_conf->bssid));
mac->vendor = PEER_UNKNOWN;
memcpy(mac->bssid, bss_conf->bssid, 6);
rtlpriv->cfg->ops->set_network_type(hw, vif->type);
rcu_read_lock();
- sta = get_sta(hw, vif, (u8 *)bss_conf->bssid);
+ sta = get_sta(hw, vif, bss_conf->bssid);
if (!sta) {
rcu_read_unlock();
goto out;
@@ -770,7 +776,7 @@ out:
mutex_unlock(&rtlpriv->locks.conf_mutex);
}
-static u64 rtl_op_get_tsf(struct ieee80211_hw *hw)
+static u64 rtl_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
u64 tsf;
@@ -779,7 +785,8 @@ static u64 rtl_op_get_tsf(struct ieee80211_hw *hw)
return tsf;
}
-static void rtl_op_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+static void rtl_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u64 tsf)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
@@ -789,7 +796,8 @@ static void rtl_op_set_tsf(struct ieee80211_hw *hw, u64 tsf)
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_CORRECT_TSF, (u8 *) (&bibss));
}
-static void rtl_op_reset_tsf(struct ieee80211_hw *hw)
+static void rtl_op_reset_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
u8 tmp = 0;
@@ -1063,6 +1071,9 @@ static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
*or clear all entry here.
*/
rtl_cam_delete_one_entry(hw, mac_addr, key_idx);
+
+ rtl_cam_reset_sec_info(hw);
+
break;
default:
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h
index 4b247db2861d..f02824a3b747 100644
--- a/drivers/net/wireless/rtlwifi/core.h
+++ b/drivers/net/wireless/rtlwifi/core.h
@@ -30,6 +30,8 @@
#ifndef __RTL_CORE_H__
#define __RTL_CORE_H__
+#include <net/mac80211.h>
+
#define RTL_SUPPORTED_FILTERS \
(FIF_PROMISC_IN_BSS | \
FIF_ALLMULTI | FIF_CONTROL | \
diff --git a/drivers/net/wireless/rtlwifi/debug.c b/drivers/net/wireless/rtlwifi/debug.c
index 5fa73852cb66..1b5cb7153a52 100644
--- a/drivers/net/wireless/rtlwifi/debug.c
+++ b/drivers/net/wireless/rtlwifi/debug.c
@@ -33,8 +33,6 @@ void rtl_dbgp_flag_init(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
u8 i;
- rtlpriv->dbg.global_debuglevel = DBG_EMERG;
-
rtlpriv->dbg.global_debugcomponents =
COMP_ERR | COMP_FW | COMP_INIT | COMP_RECV | COMP_SEND |
COMP_MLME | COMP_SCAN | COMP_INTR | COMP_LED | COMP_SEC |
diff --git a/drivers/net/wireless/rtlwifi/debug.h b/drivers/net/wireless/rtlwifi/debug.h
index e4aa8687408c..160dd0685213 100644
--- a/drivers/net/wireless/rtlwifi/debug.h
+++ b/drivers/net/wireless/rtlwifi/debug.h
@@ -204,10 +204,5 @@ enum dbgp_flag_e {
} \
} while (0);
-#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
-#define MAC_ARG(x) \
- ((u8 *)(x))[0], ((u8 *)(x))[1], ((u8 *)(x))[2],\
- ((u8 *)(x))[3], ((u8 *)(x))[4], ((u8 *)(x))[5]
-
void rtl_dbgp_flag_init(struct ieee80211_hw *hw);
#endif
diff --git a/drivers/net/wireless/rtlwifi/efuse.c b/drivers/net/wireless/rtlwifi/efuse.c
index 50de6f5d8a56..ed1058b71587 100644
--- a/drivers/net/wireless/rtlwifi/efuse.c
+++ b/drivers/net/wireless/rtlwifi/efuse.c
@@ -27,6 +27,7 @@
*
*****************************************************************************/
+#include <linux/export.h>
#include "wifi.h"
#include "efuse.h"
@@ -382,7 +383,7 @@ bool efuse_shadow_update_chk(struct ieee80211_hw *hw)
}
}
- if (wordchanged == true)
+ if (wordchanged)
hdr_num++;
}
@@ -453,7 +454,7 @@ bool efuse_shadow_update(struct ieee80211_hw *hw)
base = offset * 8;
for (i = 0; i < 8; i++) {
- if (first_pg == true) {
+ if (first_pg) {
word_en &= ~(BIT(i / 2));
@@ -505,7 +506,7 @@ void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
- if (rtlefuse->autoload_failflag == true)
+ if (rtlefuse->autoload_failflag)
memset(&rtlefuse->efuse_map[EFUSE_INIT_MAP][0], 0xFF,
rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE]);
else
@@ -690,7 +691,7 @@ static void efuse_read_data_case1(struct ieee80211_hw *hw, u16 *efuse_addr,
}
}
- if (dataempty == true) {
+ if (dataempty) {
*readstate = PG_STATE_DATA;
} else {
*efuse_addr = *efuse_addr + (word_cnts * 2) + 1;
@@ -925,7 +926,7 @@ static int efuse_pg_packet_write(struct ieee80211_hw *hw,
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct pgpkt_struct target_pkt;
u8 write_state = PG_STATE_HEADER;
- int continual = true, dataempty = true, result = true;
+ int continual = true, result = true;
u16 efuse_addr = 0;
u8 efuse_data;
u8 target_word_cnts = 0;
@@ -953,7 +954,6 @@ static int efuse_pg_packet_write(struct ieee80211_hw *hw,
(EFUSE_MAX_SIZE - EFUSE_OOB_PROTECT_BYTES))) {
if (write_state == PG_STATE_HEADER) {
- dataempty = true;
badworden = 0x0F;
RTPRINT(rtlpriv, FEEPROM, EFUSE_PG,
("efuse PG_STATE_HEADER\n"));
@@ -1176,13 +1176,12 @@ static u16 efuse_get_current_size(struct ieee80211_hw *hw)
{
int continual = true;
u16 efuse_addr = 0;
- u8 hoffset, hworden;
+ u8 hworden;
u8 efuse_data, word_cnts;
while (continual && efuse_one_byte_read(hw, efuse_addr, &efuse_data)
&& (efuse_addr < EFUSE_MAX_SIZE)) {
if (efuse_data != 0xFF) {
- hoffset = (efuse_data >> 4) & 0x0F;
hworden = efuse_data & 0x0F;
word_cnts = efuse_calculate_word_cnts(hworden);
efuse_addr = efuse_addr + (word_cnts * 2) + 1;
diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c
index 254b64ba4bf6..eb61061821e4 100644
--- a/drivers/net/wireless/rtlwifi/pci.c
+++ b/drivers/net/wireless/rtlwifi/pci.c
@@ -27,6 +27,7 @@
*
*****************************************************************************/
+#include <linux/export.h>
#include "core.h"
#include "wifi.h"
#include "pci.h"
@@ -35,10 +36,10 @@
#include "efuse.h"
static const u16 pcibridge_vendors[PCI_BRIDGE_VENDOR_MAX] = {
- INTEL_VENDOR_ID,
- ATI_VENDOR_ID,
- AMD_VENDOR_ID,
- SIS_VENDOR_ID
+ PCI_VENDOR_ID_INTEL,
+ PCI_VENDOR_ID_ATI,
+ PCI_VENDOR_ID_AMD,
+ PCI_VENDOR_ID_SI
};
static const u8 ac_to_hwq[] = {
@@ -218,7 +219,6 @@ static void rtl_pci_disable_aspm(struct ieee80211_hw *hw)
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
u8 pcibridge_vendor = pcipriv->ndis_adapter.pcibridge_vendor;
- u32 pcicfg_addrport = pcipriv->ndis_adapter.pcicfg_addrport;
u8 num4bytes = pcipriv->ndis_adapter.num4bytes;
/*Retrieve original configuration settings. */
u8 linkctrl_reg = pcipriv->ndis_adapter.linkctrl_reg;
@@ -254,9 +254,8 @@ static void rtl_pci_disable_aspm(struct ieee80211_hw *hw)
udelay(50);
/*4 Disable Pci Bridge ASPM */
- rtl_pci_raw_write_port_ulong(PCI_CONF_ADDRESS,
- pcicfg_addrport + (num4bytes << 2));
- rtl_pci_raw_write_port_uchar(PCI_CONF_DATA, pcibridge_linkctrlreg);
+ pci_write_config_byte(rtlpci->pdev, (num4bytes << 2),
+ pcibridge_linkctrlreg);
udelay(50);
}
@@ -277,7 +276,6 @@ static void rtl_pci_enable_aspm(struct ieee80211_hw *hw)
u8 pcibridge_devnum = pcipriv->ndis_adapter.pcibridge_devnum;
u8 pcibridge_funcnum = pcipriv->ndis_adapter.pcibridge_funcnum;
u8 pcibridge_vendor = pcipriv->ndis_adapter.pcibridge_vendor;
- u32 pcicfg_addrport = pcipriv->ndis_adapter.pcicfg_addrport;
u8 num4bytes = pcipriv->ndis_adapter.num4bytes;
u16 aspmlevel;
u8 u_pcibridge_aspmsetting;
@@ -293,8 +291,6 @@ static void rtl_pci_enable_aspm(struct ieee80211_hw *hw)
}
/*4 Enable Pci Bridge ASPM */
- rtl_pci_raw_write_port_ulong(PCI_CONF_ADDRESS,
- pcicfg_addrport + (num4bytes << 2));
u_pcibridge_aspmsetting =
pcipriv->ndis_adapter.pcibridge_linkctrlreg |
@@ -303,7 +299,8 @@ static void rtl_pci_enable_aspm(struct ieee80211_hw *hw)
if (pcibridge_vendor == PCI_BRIDGE_VENDOR_INTEL)
u_pcibridge_aspmsetting &= ~BIT(0);
- rtl_pci_raw_write_port_uchar(PCI_CONF_DATA, u_pcibridge_aspmsetting);
+ pci_write_config_byte(rtlpci->pdev, (num4bytes << 2),
+ u_pcibridge_aspmsetting);
RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
("PlatformEnableASPM():PciBridge busnumber[%x], "
@@ -335,25 +332,18 @@ static void rtl_pci_enable_aspm(struct ieee80211_hw *hw)
static bool rtl_pci_get_amd_l1_patch(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- u32 pcicfg_addrport = pcipriv->ndis_adapter.pcicfg_addrport;
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
bool status = false;
u8 offset_e0;
unsigned offset_e4;
- rtl_pci_raw_write_port_ulong(PCI_CONF_ADDRESS,
- pcicfg_addrport + 0xE0);
- rtl_pci_raw_write_port_uchar(PCI_CONF_DATA, 0xA0);
+ pci_write_config_byte(rtlpci->pdev, 0xe0, 0xa0);
- rtl_pci_raw_write_port_ulong(PCI_CONF_ADDRESS,
- pcicfg_addrport + 0xE0);
- rtl_pci_raw_read_port_uchar(PCI_CONF_DATA, &offset_e0);
+ pci_read_config_byte(rtlpci->pdev, 0xe0, &offset_e0);
if (offset_e0 == 0xA0) {
- rtl_pci_raw_write_port_ulong(PCI_CONF_ADDRESS,
- pcicfg_addrport + 0xE4);
- rtl_pci_raw_read_port_ulong(PCI_CONF_DATA, &offset_e4);
+ pci_read_config_dword(rtlpci->pdev, 0xe4, &offset_e4);
if (offset_e4 & BIT(23))
status = true;
}
@@ -364,17 +354,15 @@ static bool rtl_pci_get_amd_l1_patch(struct ieee80211_hw *hw)
static void rtl_pci_get_linkcontrol_field(struct ieee80211_hw *hw)
{
struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
u8 capabilityoffset = pcipriv->ndis_adapter.pcibridge_pciehdr_offset;
- u32 pcicfg_addrport = pcipriv->ndis_adapter.pcicfg_addrport;
u8 linkctrl_reg;
u8 num4bbytes;
num4bbytes = (capabilityoffset + 0x10) / 4;
/*Read Link Control Register */
- rtl_pci_raw_write_port_ulong(PCI_CONF_ADDRESS,
- pcicfg_addrport + (num4bbytes << 2));
- rtl_pci_raw_read_port_uchar(PCI_CONF_DATA, &linkctrl_reg);
+ pci_read_config_byte(rtlpci->pdev, (num4bbytes << 2), &linkctrl_reg);
pcipriv->ndis_adapter.pcibridge_linkctrlreg = linkctrl_reg;
}
@@ -390,7 +378,7 @@ static void rtl_pci_parse_configuration(struct pci_dev *pdev,
u8 linkctrl_reg;
/*Link Control Register */
- pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ pos = pci_pcie_cap(pdev);
pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &linkctrl_reg);
pcipriv->ndis_adapter.linkctrl_reg = linkctrl_reg;
@@ -488,7 +476,7 @@ static void _rtl_pci_tx_chk_waitq(struct ieee80211_hw *hw)
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct sk_buff *skb = NULL;
struct ieee80211_tx_info *info = NULL;
- int tid; /* should be int */
+ int tid;
if (!rtlpriv->rtlhal.earlymode_enable)
return;
@@ -581,7 +569,7 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio)
fc = rtl_get_fc(skb);
if (ieee80211_is_nullfunc(fc)) {
if (ieee80211_has_pm(fc)) {
- rtlpriv->mac80211.offchan_deley = true;
+ rtlpriv->mac80211.offchan_delay = true;
rtlpriv->psc.state_inap = 1;
} else {
rtlpriv->psc.state_inap = 0;
@@ -622,8 +610,58 @@ tx_status_ok:
if (((rtlpriv->link_info.num_rx_inperiod +
rtlpriv->link_info.num_tx_inperiod) > 8) ||
(rtlpriv->link_info.num_rx_inperiod > 2)) {
- rtl_lps_leave(hw);
+ tasklet_schedule(&rtlpriv->works.ips_leave_tasklet);
+ }
+}
+
+static void _rtl_receive_one(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct ieee80211_rx_status rx_status)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct ieee80211_hdr *hdr = rtl_get_hdr(skb);
+ __le16 fc = rtl_get_fc(skb);
+ bool unicast = false;
+ struct sk_buff *uskb = NULL;
+ u8 *pdata;
+
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+
+ if (is_broadcast_ether_addr(hdr->addr1)) {
+ ;/*TODO*/
+ } else if (is_multicast_ether_addr(hdr->addr1)) {
+ ;/*TODO*/
+ } else {
+ unicast = true;
+ rtlpriv->stats.rxbytesunicast += skb->len;
+ }
+
+ rtl_is_special_data(hw, skb, false);
+
+ if (ieee80211_is_data(fc)) {
+ rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX);
+
+ if (unicast)
+ rtlpriv->link_info.num_rx_inperiod++;
}
+
+ /* for sw lps */
+ rtl_swlps_beacon(hw, (void *)skb->data, skb->len);
+ rtl_recognize_peer(hw, (void *)skb->data, skb->len);
+ if ((rtlpriv->mac80211.opmode == NL80211_IFTYPE_AP) &&
+ (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) &&
+ (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)))
+ return;
+
+ if (unlikely(!rtl_action_proc(hw, skb, false)))
+ return;
+
+ uskb = dev_alloc_skb(skb->len + 128);
+ memcpy(IEEE80211_SKB_RXCB(uskb), &rx_status, sizeof(rx_status));
+ pdata = (u8 *)skb_put(uskb, skb->len);
+ memcpy(pdata, skb->data, skb->len);
+
+ ieee80211_rx_irqsafe(hw, uskb);
}
static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
@@ -637,185 +675,112 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
u8 own;
u8 tmp_one;
u32 bufferaddress;
- bool unicast = false;
struct rtl_stats stats = {
.signal = 0,
.noise = -98,
.rate = 0,
};
+ int index = rtlpci->rx_ring[rx_queue_idx].idx;
/*RX NORMAL PKT */
while (count--) {
/*rx descriptor */
struct rtl_rx_desc *pdesc = &rtlpci->rx_ring[rx_queue_idx].desc[
- rtlpci->rx_ring[rx_queue_idx].idx];
+ index];
/*rx pkt */
struct sk_buff *skb = rtlpci->rx_ring[rx_queue_idx].rx_buf[
- rtlpci->rx_ring[rx_queue_idx].idx];
+ index];
+ struct sk_buff *new_skb = NULL;
own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) pdesc,
false, HW_DESC_OWN);
- if (own) {
- /*wait data to be filled by hardware */
- return;
- } else {
- struct ieee80211_hdr *hdr;
- __le16 fc;
- struct sk_buff *new_skb = NULL;
-
- rtlpriv->cfg->ops->query_rx_desc(hw, &stats,
- &rx_status,
- (u8 *) pdesc, skb);
-
- new_skb = dev_alloc_skb(rtlpci->rxbuffersize);
- if (unlikely(!new_skb)) {
- RT_TRACE(rtlpriv, (COMP_INTR | COMP_RECV),
- DBG_DMESG,
- ("can't alloc skb for rx\n"));
- goto done;
- }
+ /*wait data to be filled by hardware */
+ if (own)
+ break;
- pci_unmap_single(rtlpci->pdev,
- *((dma_addr_t *) skb->cb),
- rtlpci->rxbuffersize,
- PCI_DMA_FROMDEVICE);
+ rtlpriv->cfg->ops->query_rx_desc(hw, &stats,
+ &rx_status,
+ (u8 *) pdesc, skb);
- skb_put(skb, rtlpriv->cfg->ops->get_desc((u8 *) pdesc,
- false,
- HW_DESC_RXPKT_LEN));
- skb_reserve(skb,
- stats.rx_drvinfo_size + stats.rx_bufshift);
+ if (stats.crc || stats.hwerror)
+ goto done;
- /*
- *NOTICE This can not be use for mac80211,
- *this is done in mac80211 code,
- *if you done here sec DHCP will fail
- *skb_trim(skb, skb->len - 4);
- */
+ new_skb = dev_alloc_skb(rtlpci->rxbuffersize);
+ if (unlikely(!new_skb)) {
+ RT_TRACE(rtlpriv, (COMP_INTR | COMP_RECV),
+ DBG_DMESG,
+ ("can't alloc skb for rx\n"));
+ goto done;
+ }
- hdr = rtl_get_hdr(skb);
- fc = rtl_get_fc(skb);
-
- if (!stats.crc && !stats.hwerror) {
- memcpy(IEEE80211_SKB_RXCB(skb), &rx_status,
- sizeof(rx_status));
-
- if (is_broadcast_ether_addr(hdr->addr1)) {
- ;/*TODO*/
- } else if (is_multicast_ether_addr(hdr->addr1)) {
- ;/*TODO*/
- } else {
- unicast = true;
- rtlpriv->stats.rxbytesunicast +=
- skb->len;
- }
-
- rtl_is_special_data(hw, skb, false);
-
- if (ieee80211_is_data(fc)) {
- rtlpriv->cfg->ops->led_control(hw,
- LED_CTL_RX);
-
- if (unicast)
- rtlpriv->link_info.
- num_rx_inperiod++;
- }
-
- /* for sw lps */
- rtl_swlps_beacon(hw, (void *)skb->data,
- skb->len);
- rtl_recognize_peer(hw, (void *)skb->data,
- skb->len);
- if ((rtlpriv->mac80211.opmode ==
- NL80211_IFTYPE_AP) &&
- (rtlpriv->rtlhal.current_bandtype ==
- BAND_ON_2_4G) &&
- (ieee80211_is_beacon(fc) ||
- ieee80211_is_probe_resp(fc))) {
- dev_kfree_skb_any(skb);
- } else {
- if (unlikely(!rtl_action_proc(hw, skb,
- false))) {
- dev_kfree_skb_any(skb);
- } else {
- struct sk_buff *uskb = NULL;
- u8 *pdata;
- uskb = dev_alloc_skb(skb->len
- + 128);
- memcpy(IEEE80211_SKB_RXCB(uskb),
- &rx_status,
- sizeof(rx_status));
- pdata = (u8 *)skb_put(uskb,
- skb->len);
- memcpy(pdata, skb->data,
- skb->len);
- dev_kfree_skb_any(skb);
-
- ieee80211_rx_irqsafe(hw, uskb);
- }
- }
- } else {
- dev_kfree_skb_any(skb);
- }
+ pci_unmap_single(rtlpci->pdev,
+ *((dma_addr_t *) skb->cb),
+ rtlpci->rxbuffersize,
+ PCI_DMA_FROMDEVICE);
- if (((rtlpriv->link_info.num_rx_inperiod +
- rtlpriv->link_info.num_tx_inperiod) > 8) ||
- (rtlpriv->link_info.num_rx_inperiod > 2)) {
- rtl_lps_leave(hw);
- }
+ skb_put(skb, rtlpriv->cfg->ops->get_desc((u8 *) pdesc, false,
+ HW_DESC_RXPKT_LEN));
+ skb_reserve(skb, stats.rx_drvinfo_size + stats.rx_bufshift);
- skb = new_skb;
+ /*
+ * NOTICE This can not be use for mac80211,
+ * this is done in mac80211 code,
+ * if you done here sec DHCP will fail
+ * skb_trim(skb, skb->len - 4);
+ */
- rtlpci->rx_ring[rx_queue_idx].rx_buf[rtlpci->
- rx_ring
- [rx_queue_idx].
- idx] = skb;
- *((dma_addr_t *) skb->cb) =
+ _rtl_receive_one(hw, skb, rx_status);
+
+ if (((rtlpriv->link_info.num_rx_inperiod +
+ rtlpriv->link_info.num_tx_inperiod) > 8) ||
+ (rtlpriv->link_info.num_rx_inperiod > 2)) {
+ tasklet_schedule(&rtlpriv->works.ips_leave_tasklet);
+ }
+
+ dev_kfree_skb_any(skb);
+ skb = new_skb;
+
+ rtlpci->rx_ring[rx_queue_idx].rx_buf[index] = skb;
+ *((dma_addr_t *) skb->cb) =
pci_map_single(rtlpci->pdev, skb_tail_pointer(skb),
rtlpci->rxbuffersize,
PCI_DMA_FROMDEVICE);
- }
done:
bufferaddress = (*((dma_addr_t *)skb->cb));
tmp_one = 1;
rtlpriv->cfg->ops->set_desc((u8 *) pdesc, false,
HW_DESC_RXBUFF_ADDR,
(u8 *)&bufferaddress);
- rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, HW_DESC_RXOWN,
- (u8 *)&tmp_one);
rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false,
HW_DESC_RXPKT_LEN,
(u8 *)&rtlpci->rxbuffersize);
- if (rtlpci->rx_ring[rx_queue_idx].idx ==
- rtlpci->rxringcount - 1)
+ if (index == rtlpci->rxringcount - 1)
rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false,
HW_DESC_RXERO,
(u8 *)&tmp_one);
- rtlpci->rx_ring[rx_queue_idx].idx =
- (rtlpci->rx_ring[rx_queue_idx].idx + 1) %
- rtlpci->rxringcount;
+ rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, HW_DESC_RXOWN,
+ (u8 *)&tmp_one);
+
+ index = (index + 1) % rtlpci->rxringcount;
}
+ rtlpci->rx_ring[rx_queue_idx].idx = index;
}
static irqreturn_t _rtl_pci_interrupt(int irq, void *dev_id)
{
struct ieee80211_hw *hw = dev_id;
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
unsigned long flags;
u32 inta = 0;
u32 intb = 0;
- if (rtlpci->irq_enabled == 0)
- return IRQ_HANDLED;
-
spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
/*read ISR: 4/8bytes */
@@ -938,6 +903,11 @@ static void _rtl_pci_irq_tasklet(struct ieee80211_hw *hw)
_rtl_pci_tx_chk_waitq(hw);
}
+static void _rtl_pci_ips_leave_tasklet(struct ieee80211_hw *hw)
+{
+ rtl_lps_leave(hw);
+}
+
static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -1036,6 +1006,9 @@ static void _rtl_pci_init_struct(struct ieee80211_hw *hw,
tasklet_init(&rtlpriv->works.irq_prepare_bcn_tasklet,
(void (*)(unsigned long))_rtl_pci_prepare_bcn_tasklet,
(unsigned long)hw);
+ tasklet_init(&rtlpriv->works.ips_leave_tasklet,
+ (void (*)(unsigned long))_rtl_pci_ips_leave_tasklet,
+ (unsigned long)hw);
}
static int _rtl_pci_init_tx_ring(struct ieee80211_hw *hw,
@@ -1505,6 +1478,7 @@ static void rtl_pci_deinit(struct ieee80211_hw *hw)
synchronize_irq(rtlpci->pdev->irq);
tasklet_kill(&rtlpriv->works.irq_tasklet);
+ tasklet_kill(&rtlpriv->works.ips_leave_tasklet);
flush_workqueue(rtlpriv->works.rtl_wq);
destroy_workqueue(rtlpriv->works.rtl_wq);
@@ -1552,7 +1526,7 @@ static int rtl_pci_start(struct ieee80211_hw *hw)
rtl_init_rx_config(hw);
- /*should after adapter start and interrupt enable. */
+ /*should be after adapter start and interrupt enable. */
set_hal_start(rtlhal);
RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
@@ -1573,12 +1547,13 @@ static void rtl_pci_stop(struct ieee80211_hw *hw)
u8 RFInProgressTimeOut = 0;
/*
- *should before disable interrrupt&adapter
+ *should be before disable interrupt&adapter
*and will do it immediately.
*/
set_hal_stop(rtlhal);
rtlpriv->cfg->ops->disable_interrupt(hw);
+ tasklet_kill(&rtlpriv->works.ips_leave_tasklet);
spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flags);
while (ppsc->rfchange_inprogress) {
@@ -1709,15 +1684,17 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev,
pcipriv->ndis_adapter.devnumber = PCI_SLOT(pdev->devfn);
pcipriv->ndis_adapter.funcnumber = PCI_FUNC(pdev->devfn);
- /*find bridge info */
- pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor;
- for (tmp = 0; tmp < PCI_BRIDGE_VENDOR_MAX; tmp++) {
- if (bridge_pdev->vendor == pcibridge_vendors[tmp]) {
- pcipriv->ndis_adapter.pcibridge_vendor = tmp;
- RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- ("Pci Bridge Vendor is found index: %d\n",
- tmp));
- break;
+ if (bridge_pdev) {
+ /*find bridge info if available */
+ pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor;
+ for (tmp = 0; tmp < PCI_BRIDGE_VENDOR_MAX; tmp++) {
+ if (bridge_pdev->vendor == pcibridge_vendors[tmp]) {
+ pcipriv->ndis_adapter.pcibridge_vendor = tmp;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+ ("Pci Bridge Vendor is found index:"
+ " %d\n", tmp));
+ break;
+ }
}
}
@@ -1729,10 +1706,6 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev,
PCI_SLOT(bridge_pdev->devfn);
pcipriv->ndis_adapter.pcibridge_funcnum =
PCI_FUNC(bridge_pdev->devfn);
- pcipriv->ndis_adapter.pcicfg_addrport =
- (pcipriv->ndis_adapter.pcibridge_busnum << 16) |
- (pcipriv->ndis_adapter.pcibridge_devnum << 11) |
- (pcipriv->ndis_adapter.pcibridge_funcnum << 8) | (1 << 31);
pcipriv->ndis_adapter.pcibridge_pciehdr_offset =
pci_pcie_cap(bridge_pdev);
pcipriv->ndis_adapter.num4bytes =
@@ -2021,36 +1994,25 @@ call rtl_mac_stop() from the mac80211
suspend function first, So there is
no need to call hw_disable here.
****************************************/
-int rtl_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+int rtl_pci_suspend(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct rtl_priv *rtlpriv = rtl_priv(hw);
rtlpriv->cfg->ops->hw_suspend(hw);
rtl_deinit_rfkill(hw);
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
EXPORT_SYMBOL(rtl_pci_suspend);
-int rtl_pci_resume(struct pci_dev *pdev)
+int rtl_pci_resume(struct device *dev)
{
- int ret;
+ struct pci_dev *pdev = to_pci_dev(dev);
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct rtl_priv *rtlpriv = rtl_priv(hw);
- pci_set_power_state(pdev, PCI_D0);
- ret = pci_enable_device(pdev);
- if (ret) {
- RT_ASSERT(false, ("ERR: <======\n"));
- return ret;
- }
-
- pci_restore_state(pdev);
-
rtlpriv->cfg->ops->hw_resume(hw);
rtl_init_rfkill(hw);
return 0;
diff --git a/drivers/net/wireless/rtlwifi/pci.h b/drivers/net/wireless/rtlwifi/pci.h
index 671b1f5aa0cf..ebe0b42c0518 100644
--- a/drivers/net/wireless/rtlwifi/pci.h
+++ b/drivers/net/wireless/rtlwifi/pci.h
@@ -62,12 +62,6 @@
.subdevice = PCI_ANY_ID,\
.driver_data = (kernel_ulong_t)&(cfg)
-#define INTEL_VENDOR_ID 0x8086
-#define SIS_VENDOR_ID 0x1039
-#define ATI_VENDOR_ID 0x1002
-#define ATI_DEVICE_ID 0x7914
-#define AMD_VENDOR_ID 0x1022
-
#define PCI_MAX_BRIDGE_NUMBER 255
#define PCI_MAX_DEVICES 32
#define PCI_MAX_FUNCTION 8
@@ -75,11 +69,6 @@
#define PCI_CONF_ADDRESS 0x0CF8 /*PCI Configuration Space Address */
#define PCI_CONF_DATA 0x0CFC /*PCI Configuration Space Data */
-#define PCI_CLASS_BRIDGE_DEV 0x06
-#define PCI_SUBCLASS_BR_PCI_TO_PCI 0x04
-#define PCI_CAPABILITY_ID_PCI_EXPRESS 0x10
-#define PCI_CAP_ID_EXP 0x10
-
#define U1DONTCARE 0xFF
#define U2DONTCARE 0xFFFF
#define U4DONTCARE 0xFFFFFFFF
@@ -169,7 +158,6 @@ struct rtl_pci {
bool first_init;
bool being_init_adapter;
bool init_ready;
- bool irq_enabled;
/*Tx */
struct rtl8192_tx_ring tx_ring[RTL_PCI_MAX_TX_QUEUE_COUNT];
@@ -224,7 +212,6 @@ struct mp_adapter {
u16 pcibridge_vendorid;
u16 pcibridge_deviceid;
- u32 pcicfg_addrport;
u8 num4bytes;
u8 pcibridge_pciehdr_offset;
@@ -250,8 +237,8 @@ extern struct rtl_intf_ops rtl_pci_ops;
int __devinit rtl_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id);
void rtl_pci_disconnect(struct pci_dev *pdev);
-int rtl_pci_suspend(struct pci_dev *pdev, pm_message_t state);
-int rtl_pci_resume(struct pci_dev *pdev);
+int rtl_pci_suspend(struct device *dev);
+int rtl_pci_resume(struct device *dev);
static inline u8 pci_read8_sync(struct rtl_priv *rtlpriv, u32 addr)
{
@@ -285,29 +272,4 @@ static inline void pci_write32_async(struct rtl_priv *rtlpriv,
writel(val, (u8 __iomem *) rtlpriv->io.pci_mem_start + addr);
}
-static inline void rtl_pci_raw_write_port_ulong(u32 port, u32 val)
-{
- outl(val, port);
-}
-
-static inline void rtl_pci_raw_write_port_uchar(u32 port, u8 val)
-{
- outb(val, port);
-}
-
-static inline void rtl_pci_raw_read_port_uchar(u32 port, u8 *pval)
-{
- *pval = inb(port);
-}
-
-static inline void rtl_pci_raw_read_port_ushort(u32 port, u16 *pval)
-{
- *pval = inw(port);
-}
-
-static inline void rtl_pci_raw_read_port_ulong(u32 port, u32 *pval)
-{
- *pval = inl(port);
-}
-
#endif
diff --git a/drivers/net/wireless/rtlwifi/ps.c b/drivers/net/wireless/rtlwifi/ps.c
index 39b0297ce925..db5262844543 100644
--- a/drivers/net/wireless/rtlwifi/ps.c
+++ b/drivers/net/wireless/rtlwifi/ps.c
@@ -27,6 +27,7 @@
*
*****************************************************************************/
+#include <linux/export.h>
#include "wifi.h"
#include "base.h"
#include "ps.h"
@@ -68,6 +69,7 @@ bool rtl_ps_disable_nic(struct ieee80211_hw *hw)
/*<2> Disable Interrupt */
rtlpriv->cfg->ops->disable_interrupt(hw);
+ tasklet_kill(&rtlpriv->works.irq_tasklet);
/*<3> Disable Adapter */
rtlpriv->cfg->ops->hw_disable(hw);
@@ -78,65 +80,18 @@ EXPORT_SYMBOL(rtl_ps_disable_nic);
bool rtl_ps_set_rf_state(struct ieee80211_hw *hw,
enum rf_pwrstate state_toset,
- u32 changesource, bool protect_or_not)
+ u32 changesource)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
- enum rf_pwrstate rtstate;
bool actionallowed = false;
- u16 rfwait_cnt = 0;
- unsigned long flag;
-
- /*protect_or_not = true; */
-
- if (protect_or_not)
- goto no_protect;
-
- /*
- *Only one thread can change
- *the RF state at one time, and others
- *should wait to be executed.
- */
- while (true) {
- spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag);
- if (ppsc->rfchange_inprogress) {
- spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock,
- flag);
-
- RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
- ("RF Change in progress!"
- "Wait to set..state_toset(%d).\n",
- state_toset));
-
- /* Set RF after the previous action is done. */
- while (ppsc->rfchange_inprogress) {
- rfwait_cnt++;
- mdelay(1);
-
- /*
- *Wait too long, return false to avoid
- *to be stuck here.
- */
- if (rfwait_cnt > 100)
- return false;
- }
- } else {
- ppsc->rfchange_inprogress = true;
- spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock,
- flag);
- break;
- }
- }
-
-no_protect:
- rtstate = ppsc->rfpwr_state;
switch (state_toset) {
case ERFON:
ppsc->rfoff_reason &= (~changesource);
if ((changesource == RF_CHANGE_BY_HW) &&
- (ppsc->hwradiooff == true)) {
+ (ppsc->hwradiooff)) {
ppsc->hwradiooff = false;
}
@@ -172,12 +127,6 @@ no_protect:
if (actionallowed)
rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset);
- if (!protect_or_not) {
- spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag);
- ppsc->rfchange_inprogress = false;
- spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
- }
-
return actionallowed;
}
EXPORT_SYMBOL(rtl_ps_set_rf_state);
@@ -200,8 +149,7 @@ static void _rtl_ps_inactive_ps(struct ieee80211_hw *hw)
}
}
- rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate,
- RF_CHANGE_BY_IPS, false);
+ rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate, RF_CHANGE_BY_IPS);
if (ppsc->inactive_pwrstate == ERFOFF &&
rtlhal->interface == INTF_PCI) {
@@ -289,12 +237,11 @@ void rtl_ips_nic_on(struct ieee80211_hw *hw)
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
enum rf_pwrstate rtstate;
- unsigned long flags;
if (mac->opmode != NL80211_IFTYPE_STATION)
return;
- spin_lock_irqsave(&rtlpriv->locks.ips_lock, flags);
+ spin_lock(&rtlpriv->locks.ips_lock);
if (ppsc->inactiveps) {
rtstate = ppsc->rfpwr_state;
@@ -310,7 +257,7 @@ void rtl_ips_nic_on(struct ieee80211_hw *hw)
}
}
- spin_unlock_irqrestore(&rtlpriv->locks.ips_lock, flags);
+ spin_unlock(&rtlpriv->locks.ips_lock);
}
/*for FW LPS*/
@@ -428,7 +375,6 @@ void rtl_lps_enter(struct ieee80211_hw *hw)
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
struct rtl_priv *rtlpriv = rtl_priv(hw);
- unsigned long flag;
if (!ppsc->fwctrl_lps)
return;
@@ -449,7 +395,7 @@ void rtl_lps_enter(struct ieee80211_hw *hw)
if (mac->link_state != MAC80211_LINKED)
return;
- spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
+ spin_lock(&rtlpriv->locks.lps_lock);
/* Idle for a while if we connect to AP a while ago. */
if (mac->cnt_after_linked >= 2) {
@@ -461,7 +407,7 @@ void rtl_lps_enter(struct ieee80211_hw *hw)
}
}
- spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
+ spin_unlock(&rtlpriv->locks.lps_lock);
}
/*Leave the leisure power save mode.*/
@@ -470,9 +416,8 @@ void rtl_lps_leave(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
- unsigned long flag;
- spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
+ spin_lock(&rtlpriv->locks.lps_lock);
if (ppsc->fwctrl_lps) {
if (ppsc->dot11_psmode != EACTIVE) {
@@ -493,7 +438,7 @@ void rtl_lps_leave(struct ieee80211_hw *hw)
rtl_lps_set_psmode(hw, EACTIVE);
}
}
- spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
+ spin_unlock(&rtlpriv->locks.lps_lock);
}
/* For sw LPS*/
@@ -582,7 +527,6 @@ void rtl_swlps_rf_awake(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
- unsigned long flag;
if (!rtlpriv->psc.swctrl_lps)
return;
@@ -595,9 +539,9 @@ void rtl_swlps_rf_awake(struct ieee80211_hw *hw)
RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
}
- spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
- rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS, false);
- spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
+ spin_lock(&rtlpriv->locks.lps_lock);
+ rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS);
+ spin_unlock(&rtlpriv->locks.lps_lock);
}
void rtl_swlps_rfon_wq_callback(void *data)
@@ -614,7 +558,6 @@ void rtl_swlps_rf_sleep(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
- unsigned long flag;
u8 sleep_intv;
if (!rtlpriv->psc.sw_ps_enabled)
@@ -631,16 +574,9 @@ void rtl_swlps_rf_sleep(struct ieee80211_hw *hw)
if (rtlpriv->link_info.busytraffic)
return;
- spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag);
- if (rtlpriv->psc.rfchange_inprogress) {
- spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
- return;
- }
- spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
-
- spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
- rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS, false);
- spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
+ spin_lock(&rtlpriv->locks.lps_lock);
+ rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS);
+ spin_unlock(&rtlpriv->locks.lps_lock);
if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
!RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
diff --git a/drivers/net/wireless/rtlwifi/ps.h b/drivers/net/wireless/rtlwifi/ps.h
index e3bf89840370..84628e6041c7 100644
--- a/drivers/net/wireless/rtlwifi/ps.h
+++ b/drivers/net/wireless/rtlwifi/ps.h
@@ -33,8 +33,7 @@
#define MAX_SW_LPS_SLEEP_INTV 5
bool rtl_ps_set_rf_state(struct ieee80211_hw *hw,
- enum rf_pwrstate state_toset, u32 changesource,
- bool protect_or_not);
+ enum rf_pwrstate state_toset, u32 changesource);
bool rtl_ps_enable_nic(struct ieee80211_hw *hw);
bool rtl_ps_disable_nic(struct ieee80211_hw *hw);
void rtl_ips_nic_off(struct ieee80211_hw *hw);
diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c
index 30da68a77786..539df66dce0a 100644
--- a/drivers/net/wireless/rtlwifi/rc.c
+++ b/drivers/net/wireless/rtlwifi/rc.c
@@ -200,7 +200,7 @@ static void rtl_tx_status(void *ppriv,
if (sta) {
/* Check if aggregation has to be enabled for this tid */
sta_entry = (struct rtl_sta_info *) sta->drv_priv;
- if ((sta->ht_cap.ht_supported == true) &&
+ if ((sta->ht_cap.ht_supported) &&
!(skb->protocol == cpu_to_be16(ETH_P_PAE))) {
if (ieee80211_is_data_qos(fc)) {
u8 tid = rtl_get_tid(skb);
diff --git a/drivers/net/wireless/rtlwifi/regd.c b/drivers/net/wireless/rtlwifi/regd.c
index 8f6718f163e5..9fedb1f70919 100644
--- a/drivers/net/wireless/rtlwifi/regd.c
+++ b/drivers/net/wireless/rtlwifi/regd.c
@@ -303,22 +303,6 @@ static void _rtl_reg_apply_world_flags(struct wiphy *wiphy,
return;
}
-static void _rtl_dump_channel_map(struct wiphy *wiphy)
-{
- enum ieee80211_band band;
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *ch;
- unsigned int i;
-
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
- if (!wiphy->bands[band])
- continue;
- sband = wiphy->bands[band];
- for (i = 0; i < sband->n_channels; i++)
- ch = &sband->channels[i];
- }
-}
-
static int _rtl_reg_notifier_apply(struct wiphy *wiphy,
struct regulatory_request *request,
struct rtl_regulatory *reg)
@@ -336,8 +320,6 @@ static int _rtl_reg_notifier_apply(struct wiphy *wiphy,
break;
}
- _rtl_dump_channel_map(wiphy);
-
return 0;
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
index 97183829b9be..72a98cab6f69 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
@@ -27,6 +27,7 @@
*
*****************************************************************************/
+#include <linux/export.h>
#include "dm_common.h"
#include "phy_common.h"
#include "../pci.h"
@@ -474,7 +475,7 @@ static void rtl92c_dm_ctrl_initgain_by_twoport(struct ieee80211_hw *hw)
{
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
- if (mac->act_scanning == true)
+ if (mac->act_scanning)
return;
if (mac->link_state >= MAC80211_LINKED)
@@ -670,7 +671,7 @@ static void rtl92c_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw
u8 ofdm_index[2], cck_index = 0, ofdm_index_old[2], cck_index_old = 0;
int i;
bool is2t = IS_92C_SERIAL(rtlhal->version);
- u8 txpwr_level[2] = {0, 0};
+ s8 txpwr_level[2] = {0, 0};
u8 ofdm_min_index = 6, rf;
rtlpriv->dm.txpower_trackinginit = true;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
index 50303e1adff1..950c65a15b8a 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
@@ -27,7 +27,10 @@
*
*****************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/firmware.h>
+#include <linux/export.h>
#include "../wifi.h"
#include "../pci.h"
#include "../base.h"
@@ -224,8 +227,7 @@ int rtl92c_download_fw(struct ieee80211_hw *hw)
u32 fwsize;
enum version_8192c version = rtlhal->version;
- printk(KERN_INFO "rtl8192c: Loading firmware file %s\n",
- rtlpriv->cfg->fw_name);
+ pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name);
if (!rtlhal->pfirmware)
return 1;
@@ -546,7 +548,6 @@ static bool _rtl92c_cmd_send_packet(struct ieee80211_hw *hw,
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl8192_tx_ring *ring;
struct rtl_tx_desc *pdesc;
- u8 own;
unsigned long flags;
struct sk_buff *pskb = NULL;
@@ -559,7 +560,6 @@ static bool _rtl92c_cmd_send_packet(struct ieee80211_hw *hw,
spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
pdesc = &ring->desc[0];
- own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) pdesc, true, HW_DESC_OWN);
rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *) pdesc, 1, 1, skb);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/main.c b/drivers/net/wireless/rtlwifi/rtl8192c/main.c
index 2f624fc27499..605ff191aeb7 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/main.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/main.c
@@ -27,6 +27,7 @@
*
*****************************************************************************/
+#include <linux/module.h>
#include "../wifi.h"
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c
index d2cc81586a6a..1f07558debf2 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c
@@ -27,6 +27,7 @@
*
*****************************************************************************/
+#include <linux/export.h>
#include "../wifi.h"
#include "../rtl8192ce/reg.h"
#include "../rtl8192ce/def.h"
@@ -1253,10 +1254,9 @@ static void _rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw,
const u32 retrycount = 2;
- u32 bbvalue;
-
if (t == 0) {
- bbvalue = rtl_get_bbreg(hw, 0x800, MASKDWORD);
+ /* dummy read */
+ rtl_get_bbreg(hw, 0x800, MASKDWORD);
_rtl92c_phy_save_adda_registers(hw, adda_reg,
rtlphy->adda_backup, 16);
@@ -1762,8 +1762,7 @@ void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery)
long result[4][8];
u8 i, final_candidate;
bool patha_ok, pathb_ok;
- long reg_e94, reg_e9c, reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4,
- reg_ecc, reg_tmp = 0;
+ long reg_e94, reg_e9c, reg_ea4, reg_eb4, reg_ebc, reg_ec4, reg_tmp = 0;
bool is12simular, is13simular, is23simular;
bool start_conttx = false, singletone = false;
u32 iqk_bb_reg[10] = {
@@ -1841,21 +1840,17 @@ void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery)
reg_e94 = result[i][0];
reg_e9c = result[i][1];
reg_ea4 = result[i][2];
- reg_eac = result[i][3];
reg_eb4 = result[i][4];
reg_ebc = result[i][5];
reg_ec4 = result[i][6];
- reg_ecc = result[i][7];
}
if (final_candidate != 0xff) {
rtlphy->reg_e94 = reg_e94 = result[final_candidate][0];
rtlphy->reg_e9c = reg_e9c = result[final_candidate][1];
reg_ea4 = result[final_candidate][2];
- reg_eac = result[final_candidate][3];
rtlphy->reg_eb4 = reg_eb4 = result[final_candidate][4];
rtlphy->reg_ebc = reg_ebc = result[final_candidate][5];
reg_ec4 = result[final_candidate][6];
- reg_ecc = result[final_candidate][7];
patha_ok = pathb_ok = true;
} else {
rtlphy->reg_e94 = rtlphy->reg_eb4 = 0x100;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
index 35ff7df41a1d..9fc804d89d65 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
@@ -142,8 +142,22 @@ enum version_8192c {
VERSION_UNKNOWN = 0x88,
};
+#define CUT_VERSION_MASK (BIT(6)|BIT(7))
+#define CHIP_VENDOR_UMC BIT(5)
+#define CHIP_VENDOR_UMC_B_CUT BIT(6) /* Chip version for ECO */
+#define IS_VENDOR_UMC_A_CUT(version) ((IS_CHIP_VENDOR_UMC(version)) ? \
+ ((GET_CVID_CUT_VERSION(version)) ? false : true) : false)
#define IS_CHIP_VER_B(version) ((version & CHIP_VER_B) ? true : false)
+#define IS_VENDOR_UMC_A_CUT(version) ((IS_CHIP_VENDOR_UMC(version)) ? \
+ ((GET_CVID_CUT_VERSION(version)) ? false : true) : false)
#define IS_92C_SERIAL(version) ((version & CHIP_92C_BITMASK) ? true : false)
+#define IS_CHIP_VENDOR_UMC(version) \
+ ((version & CHIP_VENDOR_UMC) ? true : false)
+#define GET_CVID_CUT_VERSION(version) ((version) & CUT_VERSION_MASK)
+#define IS_81xxC_VENDOR_UMC_B_CUT(version) \
+ ((IS_CHIP_VENDOR_UMC(version)) ? \
+ ((GET_CVID_CUT_VERSION(version) == CHIP_VENDOR_UMC_B_CUT) ? \
+ true : false) : false)
enum rtl819x_loopback_e {
RTL819X_NO_LOOPBACK = 0,
@@ -220,41 +234,6 @@ enum rtl_desc_qsel {
QSLT_CMD = 0x13,
};
-enum rtl_desc92c_rate {
- DESC92C_RATE1M = 0x00,
- DESC92C_RATE2M = 0x01,
- DESC92C_RATE5_5M = 0x02,
- DESC92C_RATE11M = 0x03,
-
- DESC92C_RATE6M = 0x04,
- DESC92C_RATE9M = 0x05,
- DESC92C_RATE12M = 0x06,
- DESC92C_RATE18M = 0x07,
- DESC92C_RATE24M = 0x08,
- DESC92C_RATE36M = 0x09,
- DESC92C_RATE48M = 0x0a,
- DESC92C_RATE54M = 0x0b,
-
- DESC92C_RATEMCS0 = 0x0c,
- DESC92C_RATEMCS1 = 0x0d,
- DESC92C_RATEMCS2 = 0x0e,
- DESC92C_RATEMCS3 = 0x0f,
- DESC92C_RATEMCS4 = 0x10,
- DESC92C_RATEMCS5 = 0x11,
- DESC92C_RATEMCS6 = 0x12,
- DESC92C_RATEMCS7 = 0x13,
- DESC92C_RATEMCS8 = 0x14,
- DESC92C_RATEMCS9 = 0x15,
- DESC92C_RATEMCS10 = 0x16,
- DESC92C_RATEMCS11 = 0x17,
- DESC92C_RATEMCS12 = 0x18,
- DESC92C_RATEMCS13 = 0x19,
- DESC92C_RATEMCS14 = 0x1a,
- DESC92C_RATEMCS15 = 0x1b,
- DESC92C_RATEMCS15_SG = 0x1c,
- DESC92C_RATEMCS32 = 0x20,
-};
-
struct phy_sts_cck_8192s_t {
u8 adc_pwdb_X[4];
u8 sq_rpt;
@@ -267,108 +246,4 @@ struct h2c_cmd_8192c {
u8 *p_cmdbuffer;
};
-/* NOTE: reference to rtl8192c_rates struct */
-static inline int _rtl92c_rate_mapping(struct ieee80211_hw *hw, bool isHT,
- u8 desc_rate, bool first_ampdu)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- int rate_idx = 0;
-
- if (first_ampdu) {
- if (false == isHT) {
- switch (desc_rate) {
- case DESC92C_RATE1M:
- rate_idx = 0;
- break;
- case DESC92C_RATE2M:
- rate_idx = 1;
- break;
- case DESC92C_RATE5_5M:
- rate_idx = 2;
- break;
- case DESC92C_RATE11M:
- rate_idx = 3;
- break;
- case DESC92C_RATE6M:
- rate_idx = 4;
- break;
- case DESC92C_RATE9M:
- rate_idx = 5;
- break;
- case DESC92C_RATE12M:
- rate_idx = 6;
- break;
- case DESC92C_RATE18M:
- rate_idx = 7;
- break;
- case DESC92C_RATE24M:
- rate_idx = 8;
- break;
- case DESC92C_RATE36M:
- rate_idx = 9;
- break;
- case DESC92C_RATE48M:
- rate_idx = 10;
- break;
- case DESC92C_RATE54M:
- rate_idx = 11;
- break;
- default:
- RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
- ("Rate %d is not support, set to "
- "1M rate.\n", desc_rate));
- rate_idx = 0;
- break;
- }
- } else {
- rate_idx = 11;
- }
- return rate_idx;
- }
- switch (desc_rate) {
- case DESC92C_RATE1M:
- rate_idx = 0;
- break;
- case DESC92C_RATE2M:
- rate_idx = 1;
- break;
- case DESC92C_RATE5_5M:
- rate_idx = 2;
- break;
- case DESC92C_RATE11M:
- rate_idx = 3;
- break;
- case DESC92C_RATE6M:
- rate_idx = 4;
- break;
- case DESC92C_RATE9M:
- rate_idx = 5;
- break;
- case DESC92C_RATE12M:
- rate_idx = 6;
- break;
- case DESC92C_RATE18M:
- rate_idx = 7;
- break;
- case DESC92C_RATE24M:
- rate_idx = 8;
- break;
- case DESC92C_RATE36M:
- rate_idx = 9;
- break;
- case DESC92C_RATE48M:
- rate_idx = 10;
- break;
- case DESC92C_RATE54M:
- rate_idx = 11;
- break;
- /* TODO: How to mapping MCS rate? */
- /* NOTE: referenc to __ieee80211_rx */
- default:
- rate_idx = 11;
- break;
- }
- return rate_idx;
-}
-
#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
index defb4370cf74..a3deaefa788c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
@@ -488,7 +488,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
case HW_VAR_CORRECT_TSF:{
u8 btype_ibss = ((u8 *) (val))[0];
- if (btype_ibss == true)
+ if (btype_ibss)
_rtl92ce_stop_tx_beacon(hw);
_rtl92ce_set_bcn_ctrl_reg(hw, 0, BIT(3));
@@ -500,7 +500,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
_rtl92ce_set_bcn_ctrl_reg(hw, BIT(3), 0);
- if (btype_ibss == true)
+ if (btype_ibss)
_rtl92ce_resume_tx_beacon(hw);
break;
@@ -763,11 +763,9 @@ static void _rtl92ce_hw_configure(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
u8 reg_bw_opmode;
- u32 reg_ratr, reg_prsr;
+ u32 reg_prsr;
reg_bw_opmode = BW_OPMODE_20MHZ;
- reg_ratr = RATE_ALL_CCK | RATE_ALL_OFDM_AG |
- RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS;
reg_prsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, 0x8);
@@ -1123,7 +1121,7 @@ void rtl92ce_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
if (rtlpriv->psc.rfpwr_state != ERFON)
return;
- if (check_bssid == true) {
+ if (check_bssid) {
reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR,
(u8 *) (&reg_rcr));
@@ -1185,7 +1183,6 @@ void rtl92ce_enable_interrupt(struct ieee80211_hw *hw)
rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF);
rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF);
- rtlpci->irq_enabled = true;
}
void rtl92ce_disable_interrupt(struct ieee80211_hw *hw)
@@ -1195,7 +1192,7 @@ void rtl92ce_disable_interrupt(struct ieee80211_hw *hw)
rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED);
rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED);
- rtlpci->irq_enabled = false;
+ synchronize_irq(rtlpci->pdev->irq);
}
static void _rtl92ce_poweroff_adapter(struct ieee80211_hw *hw)
@@ -1586,7 +1583,7 @@ static void _rtl92ce_read_adapter_info(struct ieee80211_hw *hw)
rtlefuse->autoload_failflag = false;
}
- if (rtlefuse->autoload_failflag == true)
+ if (rtlefuse->autoload_failflag)
return;
for (i = 0; i < 6; i += 2) {
@@ -1595,7 +1592,7 @@ static void _rtl92ce_read_adapter_info(struct ieee80211_hw *hw)
}
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- (MAC_FMT "\n", MAC_ARG(rtlefuse->dev_addr)));
+ ("%pM\n", rtlefuse->dev_addr));
_rtl92ce_read_txpower_info_from_hwpg(hw,
rtlefuse->autoload_failflag,
@@ -1969,7 +1966,7 @@ bool rtl92ce_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
- enum rf_pwrstate e_rfpowerstate_toset, cur_rfstate;
+ enum rf_pwrstate e_rfpowerstate_toset;
u8 u1tmp;
bool actuallyset = false;
unsigned long flag;
@@ -1989,15 +1986,13 @@ bool rtl92ce_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
}
- cur_rfstate = ppsc->rfpwr_state;
-
rtl_write_byte(rtlpriv, REG_MAC_PINMUX_CFG, rtl_read_byte(rtlpriv,
REG_MAC_PINMUX_CFG)&~(BIT(3)));
u1tmp = rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL);
e_rfpowerstate_toset = (u1tmp & BIT(3)) ? ERFON : ERFOFF;
- if ((ppsc->hwradiooff == true) && (e_rfpowerstate_toset == ERFON)) {
+ if ((ppsc->hwradiooff) && (e_rfpowerstate_toset == ERFON)) {
RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
("GPIOChangeRF - HW Radio ON, RF ON\n"));
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/led.c b/drivers/net/wireless/rtlwifi/rtl8192ce/led.c
index 9dd1ed7b6422..28a1a707d09c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/led.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/led.c
@@ -84,7 +84,7 @@ void rtl92ce_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
break;
case LED_PIN_LED0:
ledcfg &= 0xf0;
- if (pcipriv->ledctl.led_opendrain == true)
+ if (pcipriv->ledctl.led_opendrain)
rtl_write_byte(rtlpriv, REG_LEDCFG2,
(ledcfg | BIT(1) | BIT(5) | BIT(6)));
else
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c
index abe0fcc75368..592a10ac5929 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c
@@ -46,13 +46,12 @@ u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw,
struct rtl_priv *rtlpriv = rtl_priv(hw);
u32 original_value, readback_value, bitshift;
struct rtl_phy *rtlphy = &(rtlpriv->phy);
- unsigned long flags;
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), "
"rfpath(%#x), bitmask(%#x)\n",
regaddr, rfpath, bitmask));
- spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
+ spin_lock(&rtlpriv->locks.rf_lock);
if (rtlphy->rf_mode != RF_OP_BY_FW) {
original_value = _rtl92c_phy_rf_serial_read(hw,
@@ -65,7 +64,7 @@ u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw,
bitshift = _rtl92c_phy_calculate_bit_shift(bitmask);
readback_value = (original_value & bitmask) >> bitshift;
- spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
+ spin_unlock(&rtlpriv->locks.rf_lock);
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
("regaddr(%#x), rfpath(%#x), "
@@ -120,13 +119,12 @@ void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw,
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_phy *rtlphy = &(rtlpriv->phy);
u32 original_value, bitshift;
- unsigned long flags;
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
("regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n",
regaddr, bitmask, data, rfpath));
- spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
+ spin_lock(&rtlpriv->locks.rf_lock);
if (rtlphy->rf_mode != RF_OP_BY_FW) {
if (bitmask != RFREG_OFFSET_MASK) {
@@ -153,7 +151,7 @@ void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw,
_rtl92c_phy_fw_rf_serial_write(hw, rfpath, regaddr, data);
}
- spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
+ spin_unlock(&rtlpriv->locks.rf_lock);
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), "
"bitmask(%#x), data(%#x), "
@@ -281,7 +279,6 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
{
int i;
- bool rtstatus = true;
u32 *radioa_array_table;
u32 *radiob_array_table;
u16 radioa_arraylen, radiob_arraylen;
@@ -308,7 +305,6 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
("Radio_B:RTL8192CE_RADIOB_1TARRAY\n"));
}
RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, ("Radio No %x\n", rfpath));
- rtstatus = true;
switch (rfpath) {
case RF90_PATH_A:
for (i = 0; i < radioa_arraylen; i = i + 2) {
@@ -521,7 +517,6 @@ static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw,
u8 i, queue_id;
struct rtl8192_tx_ring *ring = NULL;
- ppsc->set_rfpowerstate_inprogress = true;
switch (rfpwr_state) {
case ERFON:{
if ((ppsc->rfpwr_state == ERFOFF) &&
@@ -617,7 +612,6 @@ static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw,
}
if (bresult)
ppsc->rfpwr_state = rfpwr_state;
- ppsc->set_rfpowerstate_inprogress = false;
return bresult;
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
index 598cecc63f41..ba5ff0411f0a 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
@@ -1074,10 +1074,10 @@
#define _SRL(x) (((x) & 0x3F) << 8)
#define _SIFS_CCK_CTX(x) ((x) & 0xFF)
-#define _SIFS_CCK_TRX(x) (((x) & 0xFF) << 8);
+#define _SIFS_CCK_TRX(x) (((x) & 0xFF) << 8)
#define _SIFS_OFDM_CTX(x) ((x) & 0xFF)
-#define _SIFS_OFDM_TRX(x) (((x) & 0xFF) << 8);
+#define _SIFS_OFDM_TRX(x) (((x) & 0xFF) << 8)
#define _TBTT_PROHIBIT_HOLD(x) (((x) & 0xFF) << 8)
@@ -1203,7 +1203,9 @@
#define EPROM_CMD_CONFIG 0x3
#define EPROM_CMD_LOAD 1
+#define HWSET_MAX_SIZE 128
#define HWSET_MAX_SIZE_92S HWSET_MAX_SIZE
+#define EFUSE_MAX_SECTION 16
#define WL_HWPDN_EN BIT(0)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.c b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.c
index 90d0f2cf3b27..d3b01e6023ba 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.c
@@ -76,7 +76,7 @@ void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
if (rtlefuse->eeprom_regulatory != 0)
turbo_scanoff = true;
- if (mac->act_scanning == true) {
+ if (mac->act_scanning) {
tx_agc[RF90_PATH_A] = 0x3f3f3f3f;
tx_agc[RF90_PATH_B] = 0x3f3f3f3f;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
index 373dc78af1dc..f2aa33dc4d78 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
@@ -28,6 +28,7 @@
*****************************************************************************/
#include <linux/vmalloc.h>
+#include <linux/module.h>
#include "../wifi.h"
#include "../core.h"
@@ -92,6 +93,8 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
const struct firmware *firmware;
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ char *fw_name = NULL;
rtl8192ce_bt_reg_init(hw);
@@ -129,10 +132,16 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
rtlpci->irq_mask[1] = (u32) (IMR_CPWM | IMR_C2HCMD | 0);
+ /* for debug level */
+ rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug;
/* for LPS & IPS */
rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ if (!rtlpriv->psc.inactiveps)
+ pr_info("rtl8192ce: Power Save off (module option)\n");
+ if (!rtlpriv->psc.fwctrl_lps)
+ pr_info("rtl8192ce: FW Power Save off (module option)\n");
rtlpriv->psc.reg_fwctrl_lps = 3;
rtlpriv->psc.reg_max_lps_awakeintvl = 5;
/* for ASPM, you can close aspm through
@@ -155,8 +164,14 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
}
/* request fw */
- err = request_firmware(&firmware, rtlpriv->cfg->fw_name,
- rtlpriv->io.dev);
+ if (IS_VENDOR_UMC_A_CUT(rtlhal->version) &&
+ !IS_92C_SERIAL(rtlhal->version))
+ fw_name = "rtlwifi/rtl8192cfwU.bin";
+ else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version))
+ fw_name = "rtlwifi/rtl8192cfwU_B.bin";
+ else
+ fw_name = rtlpriv->cfg->fw_name;
+ err = request_firmware(&firmware, fw_name, rtlpriv->io.dev);
if (err) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
("Failed to request firmware!\n"));
@@ -241,6 +256,7 @@ static struct rtl_mod_params rtl92ce_mod_params = {
.inactiveps = true,
.swctrl_lps = false,
.fwctrl_lps = true,
+ .debug = DBG_EMERG,
};
static struct rtl_hal_cfg rtl92ce_hal_cfg = {
@@ -318,21 +334,21 @@ static struct rtl_hal_cfg rtl92ce_hal_cfg = {
.maps[RTL_IMR_ROK] = IMR_ROK,
.maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER),
- .maps[RTL_RC_CCK_RATE1M] = DESC92C_RATE1M,
- .maps[RTL_RC_CCK_RATE2M] = DESC92C_RATE2M,
- .maps[RTL_RC_CCK_RATE5_5M] = DESC92C_RATE5_5M,
- .maps[RTL_RC_CCK_RATE11M] = DESC92C_RATE11M,
- .maps[RTL_RC_OFDM_RATE6M] = DESC92C_RATE6M,
- .maps[RTL_RC_OFDM_RATE9M] = DESC92C_RATE9M,
- .maps[RTL_RC_OFDM_RATE12M] = DESC92C_RATE12M,
- .maps[RTL_RC_OFDM_RATE18M] = DESC92C_RATE18M,
- .maps[RTL_RC_OFDM_RATE24M] = DESC92C_RATE24M,
- .maps[RTL_RC_OFDM_RATE36M] = DESC92C_RATE36M,
- .maps[RTL_RC_OFDM_RATE48M] = DESC92C_RATE48M,
- .maps[RTL_RC_OFDM_RATE54M] = DESC92C_RATE54M,
-
- .maps[RTL_RC_HT_RATEMCS7] = DESC92C_RATEMCS7,
- .maps[RTL_RC_HT_RATEMCS15] = DESC92C_RATEMCS15,
+ .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M,
+ .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M,
+ .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M,
+ .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M,
+ .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M,
+ .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M,
+ .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M,
+ .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M,
+ .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M,
+ .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M,
+ .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M,
+ .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M,
+
+ .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7,
+ .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15,
};
DEFINE_PCI_DEVICE_TABLE(rtl92ce_pci_ids) = {
@@ -351,27 +367,35 @@ MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Realtek 8192C/8188C 802.11n PCI wireless");
MODULE_FIRMWARE("rtlwifi/rtl8192cfw.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8192cfwU.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8192cfwU_B.bin");
module_param_named(swenc, rtl92ce_mod_params.sw_crypto, bool, 0444);
+module_param_named(debug, rtl92ce_mod_params.debug, int, 0444);
module_param_named(ips, rtl92ce_mod_params.inactiveps, bool, 0444);
module_param_named(swlps, rtl92ce_mod_params.swctrl_lps, bool, 0444);
module_param_named(fwlps, rtl92ce_mod_params.fwctrl_lps, bool, 0444);
-MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n");
-MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n");
-MODULE_PARM_DESC(fwlps, "using linked fw control power save "
- "(default 1 is open)\n");
+MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
+MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
+MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
+MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
+
+static const struct dev_pm_ops rtlwifi_pm_ops = {
+ .suspend = rtl_pci_suspend,
+ .resume = rtl_pci_resume,
+ .freeze = rtl_pci_suspend,
+ .thaw = rtl_pci_resume,
+ .poweroff = rtl_pci_suspend,
+ .restore = rtl_pci_resume,
+};
static struct pci_driver rtl92ce_driver = {
.name = KBUILD_MODNAME,
.id_table = rtl92ce_pci_ids,
.probe = rtl_pci_probe,
.remove = rtl_pci_disconnect,
-
-#ifdef CONFIG_PM
- .suspend = rtl_pci_suspend,
- .resume = rtl_pci_resume,
-#endif
-
+ .driver.pm = &rtlwifi_pm_ops,
};
static int __init rtl92ce_module_init(void)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
index 54b2bd53d36a..4fb5ae24dee0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
@@ -48,104 +48,6 @@ static u8 _rtl92ce_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue)
return skb->priority;
}
-static int _rtl92ce_rate_mapping(bool isht, u8 desc_rate, bool first_ampdu)
-{
- int rate_idx;
-
- if (first_ampdu) {
- if (false == isht) {
- switch (desc_rate) {
- case DESC92C_RATE1M:
- rate_idx = 0;
- break;
- case DESC92C_RATE2M:
- rate_idx = 1;
- break;
- case DESC92C_RATE5_5M:
- rate_idx = 2;
- break;
- case DESC92C_RATE11M:
- rate_idx = 3;
- break;
- case DESC92C_RATE6M:
- rate_idx = 4;
- break;
- case DESC92C_RATE9M:
- rate_idx = 5;
- break;
- case DESC92C_RATE12M:
- rate_idx = 6;
- break;
- case DESC92C_RATE18M:
- rate_idx = 7;
- break;
- case DESC92C_RATE24M:
- rate_idx = 8;
- break;
- case DESC92C_RATE36M:
- rate_idx = 9;
- break;
- case DESC92C_RATE48M:
- rate_idx = 10;
- break;
- case DESC92C_RATE54M:
- rate_idx = 11;
- break;
- default:
- rate_idx = 0;
- break;
- }
- } else {
- rate_idx = 11;
- }
-
- return rate_idx;
- }
-
- switch (desc_rate) {
- case DESC92C_RATE1M:
- rate_idx = 0;
- break;
- case DESC92C_RATE2M:
- rate_idx = 1;
- break;
- case DESC92C_RATE5_5M:
- rate_idx = 2;
- break;
- case DESC92C_RATE11M:
- rate_idx = 3;
- break;
- case DESC92C_RATE6M:
- rate_idx = 4;
- break;
- case DESC92C_RATE9M:
- rate_idx = 5;
- break;
- case DESC92C_RATE12M:
- rate_idx = 6;
- break;
- case DESC92C_RATE18M:
- rate_idx = 7;
- break;
- case DESC92C_RATE24M:
- rate_idx = 8;
- break;
- case DESC92C_RATE36M:
- rate_idx = 9;
- break;
- case DESC92C_RATE48M:
- rate_idx = 10;
- break;
- case DESC92C_RATE54M:
- rate_idx = 11;
- break;
- default:
- rate_idx = 11;
- break;
- }
- return rate_idx;
-}
-
static u8 _rtl92c_query_rxpwrpercentage(char antpower)
{
if ((antpower <= -100) || (antpower >= 20))
@@ -225,7 +127,7 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw,
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct phy_sts_cck_8192s_t *cck_buf;
- s8 rx_pwr_all, rx_pwr[4];
+ s8 rx_pwr_all = 0, rx_pwr[4];
u8 evm, pwdb_all, rf_rx_num = 0;
u8 i, max_spatial_stream;
u32 rssi, total_rssi = 0;
@@ -336,8 +238,8 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw,
pstats->rxpower = rx_pwr_all;
pstats->recvsignalpower = rx_pwr_all;
- if (pdesc->rxht && pdesc->rxmcs >= DESC92C_RATEMCS8 &&
- pdesc->rxmcs <= DESC92C_RATEMCS15)
+ if (pdesc->rxht && pdesc->rxmcs >= DESC92_RATEMCS8 &&
+ pdesc->rxmcs <= DESC92_RATEMCS15)
max_spatial_stream = 2;
else
max_spatial_stream = 1;
@@ -592,7 +494,6 @@ static void _rtl92ce_translate_rx_signal_stuff(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr;
u8 *tmp_buf;
u8 *praddr;
- u8 *psaddr;
__le16 fc;
u16 type, c_fc;
bool packet_matchbssid, packet_toself, packet_beacon;
@@ -604,7 +505,6 @@ static void _rtl92ce_translate_rx_signal_stuff(struct ieee80211_hw *hw,
c_fc = le16_to_cpu(fc);
type = WLAN_FC_GET_TYPE(fc);
praddr = hdr->addr1;
- psaddr = hdr->addr2;
packet_matchbssid =
((IEEE80211_FTYPE_CTL != type) &&
@@ -672,15 +572,13 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
if (stats->decrypted)
rx_status->flag |= RX_FLAG_DECRYPTED;
- rx_status->rate_idx = _rtl92ce_rate_mapping((bool)
- GET_RX_DESC_RXHT(pdesc),
- (u8)
- GET_RX_DESC_RXMCS(pdesc),
- (bool)
- GET_RX_DESC_PAGGR(pdesc));
+ rx_status->rate_idx = rtlwifi_rate_mapping(hw,
+ (bool)GET_RX_DESC_RXHT(pdesc),
+ (u8)GET_RX_DESC_RXMCS(pdesc),
+ (bool)GET_RX_DESC_PAGGR(pdesc));
rx_status->mactime = GET_RX_DESC_TSFL(pdesc);
- if (phystatus == true) {
+ if (phystatus) {
p_drvinfo = (struct rx_fwinfo_92c *)(skb->data +
stats->rx_bufshift);
@@ -770,7 +668,7 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw,
SET_TX_DESC_RTS_BW(pdesc, 0);
SET_TX_DESC_RTS_SC(pdesc, tcb_desc->rts_sc);
SET_TX_DESC_RTS_SHORT(pdesc,
- ((tcb_desc->rts_rate <= DESC92C_RATE54M) ?
+ ((tcb_desc->rts_rate <= DESC92_RATE54M) ?
(tcb_desc->rts_use_shortpreamble ? 1 : 0)
: (tcb_desc->rts_use_shortgi ? 1 : 0)));
@@ -888,7 +786,7 @@ void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw,
if (firstseg)
SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN);
- SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE1M);
+ SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M);
SET_TX_DESC_SEQ(pdesc, 0);
@@ -929,9 +827,10 @@ void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw,
void rtl92ce_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
{
- if (istx == true) {
+ if (istx) {
switch (desc_name) {
case HW_DESC_OWN:
+ wmb();
SET_TX_DESC_OWN(pdesc, 1);
break;
case HW_DESC_TX_NEXTDESC_ADDR:
@@ -945,6 +844,7 @@ void rtl92ce_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
} else {
switch (desc_name) {
case HW_DESC_RXOWN:
+ wmb();
SET_RX_DESC_OWN(pdesc, 1);
break;
case HW_DESC_RXBUFF_ADDR:
@@ -968,7 +868,7 @@ u32 rtl92ce_get_desc(u8 *p_desc, bool istx, u8 desc_name)
{
u32 ret = 0;
- if (istx == true) {
+ if (istx) {
switch (desc_name) {
case HW_DESC_OWN:
ret = GET_TX_DESC_OWN(p_desc);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
index 0f1177137501..c8977a50ca36 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
@@ -537,12 +537,6 @@ do { \
memset(__pdesc, 0, _size); \
} while (0);
-#define RX_HAL_IS_CCK_RATE(_pdesc)\
- (_pdesc->rxmcs == DESC92C_RATE1M || \
- _pdesc->rxmcs == DESC92C_RATE2M || \
- _pdesc->rxmcs == DESC92C_RATE5_5M || \
- _pdesc->rxmcs == DESC92C_RATE11M)
-
struct rx_fwinfo_92c {
u8 gain_trsw[4];
u8 pwdb_all;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/def.h b/drivers/net/wireless/rtlwifi/rtl8192cu/def.h
index c54940ea72fe..d097efb1e717 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/def.h
@@ -50,10 +50,6 @@
#define IS_VENDOR_UMC(version) \
(((version) & CHIP_VENDOR_UMC) ? true : false)
-#define IS_VENDOR_UMC_A_CUT(version) \
- (((version) & CHIP_VENDOR_UMC) ? (((version) & (BIT(6) | BIT(7))) ? \
- false : true) : false)
-
#define IS_VENDOR_8723_A_CUT(version) \
(((version) & CHIP_VENDOR_UMC) ? (((version) & (BIT(6))) ? \
false : true) : false)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
index 52e2af58c1ed..814c05df51e8 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
@@ -27,6 +27,8 @@
*
*****************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "../wifi.h"
#include "../efuse.h"
#include "../base.h"
@@ -337,7 +339,7 @@ static void _rtl92cu_read_board_type(struct ieee80211_hw *hw, u8 *contents)
rtlefuse->board_type = boardType;
if (IS_HIGHT_PA(rtlefuse->board_type))
rtlefuse->external_pa = 1;
- printk(KERN_INFO "rtl8192cu: Board Type %x\n", rtlefuse->board_type);
+ pr_info("Board Type %x\n", rtlefuse->board_type);
#ifdef CONFIG_ANTENNA_DIVERSITY
/* Antenna Diversity setting. */
@@ -346,8 +348,7 @@ static void _rtl92cu_read_board_type(struct ieee80211_hw *hw, u8 *contents)
else
rtl_efuse->antenna_cfg = registry_par->antdiv_cfg; /* 0:OFF, */
- printk(KERN_INFO "rtl8192cu: Antenna Config %x\n",
- rtl_efuse->antenna_cfg);
+ pr_info("Antenna Config %x\n", rtl_efuse->antenna_cfg);
#endif
}
@@ -384,71 +385,57 @@ static void _update_bt_param(_adapter *padapter)
pbtpriv->bBTNonTrafficModeSet = _FALSE;
pbtpriv->CurrentState = 0;
pbtpriv->PreviousState = 0;
- printk(KERN_INFO "rtl8192cu: BT Coexistance = %s\n",
- (pbtpriv->BT_Coexist == _TRUE) ? "enable" : "disable");
+ pr_info("BT Coexistance = %s\n",
+ (pbtpriv->BT_Coexist == _TRUE) ? "enable" : "disable");
if (pbtpriv->BT_Coexist) {
if (pbtpriv->BT_Ant_Num == Ant_x2)
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_"
- "Ant_Num = Antx2\n");
+ pr_info("BlueTooth BT_Ant_Num = Antx2\n");
else if (pbtpriv->BT_Ant_Num == Ant_x1)
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_"
- "Ant_Num = Antx1\n");
+ pr_info("BlueTooth BT_Ant_Num = Antx1\n");
switch (pbtpriv->BT_CoexistType) {
case BT_2Wire:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_"
- "CoexistType = BT_2Wire\n");
+ pr_info("BlueTooth BT_CoexistType = BT_2Wire\n");
break;
case BT_ISSC_3Wire:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_"
- "CoexistType = BT_ISSC_3Wire\n");
+ pr_info("BlueTooth BT_CoexistType = BT_ISSC_3Wire\n");
break;
case BT_Accel:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_"
- "CoexistType = BT_Accel\n");
+ pr_info("BlueTooth BT_CoexistType = BT_Accel\n");
break;
case BT_CSR_BC4:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_"
- "CoexistType = BT_CSR_BC4\n");
+ pr_info("BlueTooth BT_CoexistType = BT_CSR_BC4\n");
break;
case BT_CSR_BC8:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_"
- "CoexistType = BT_CSR_BC8\n");
+ pr_info("BlueTooth BT_CoexistType = BT_CSR_BC8\n");
break;
case BT_RTL8756:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_"
- "CoexistType = BT_RTL8756\n");
+ pr_info("BlueTooth BT_CoexistType = BT_RTL8756\n");
break;
default:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_"
- "CoexistType = Unknown\n");
+ pr_info("BlueTooth BT_CoexistType = Unknown\n");
break;
}
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_Ant_isolation = %d\n",
- pbtpriv->BT_Ant_isolation);
+ pr_info("BlueTooth BT_Ant_isolation = %d\n",
+ pbtpriv->BT_Ant_isolation);
switch (pbtpriv->BT_Service) {
case BT_OtherAction:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_Service = "
- "BT_OtherAction\n");
+ pr_info("BlueTooth BT_Service = BT_OtherAction\n");
break;
case BT_SCO:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_Service = "
- "BT_SCO\n");
+ pr_info("BlueTooth BT_Service = BT_SCO\n");
break;
case BT_Busy:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_Service = "
- "BT_Busy\n");
+ pr_info("BlueTooth BT_Service = BT_Busy\n");
break;
case BT_OtherBusy:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_Service = "
- "BT_OtherBusy\n");
+ pr_info("BlueTooth BT_Service = BT_OtherBusy\n");
break;
default:
- printk(KERN_INFO "rtl8192cu: BlueTooth BT_Service = "
- "BT_Idle\n");
+ pr_info("BlueTooth BT_Service = BT_Idle\n");
break;
}
- printk(KERN_INFO "rtl8192cu: BT_RadioSharedType = 0x%x\n",
- pbtpriv->BT_RadioSharedType);
+ pr_info("BT_RadioSharedType = 0x%x\n",
+ pbtpriv->BT_RadioSharedType);
}
}
@@ -520,13 +507,13 @@ static void _rtl92cu_read_adapter_info(struct ieee80211_hw *hw)
RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("Autoload OK\n"));
rtlefuse->autoload_failflag = false;
}
- if (rtlefuse->autoload_failflag == true)
+ if (rtlefuse->autoload_failflag)
return;
for (i = 0; i < 6; i += 2) {
usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR + i];
*((u16 *) (&rtlefuse->dev_addr[i])) = usvalue;
}
- printk(KERN_INFO "rtl8192cu: MAC address: %pM\n", rtlefuse->dev_addr);
+ pr_info("MAC address: %pM\n", rtlefuse->dev_addr);
_rtl92cu_read_txpower_info_from_hwpg(hw,
rtlefuse->autoload_failflag, hwinfo);
rtlefuse->eeprom_vid = *(u16 *)&hwinfo[EEPROM_VID];
@@ -665,7 +652,7 @@ static int _rtl92cu_init_power_on(struct ieee80211_hw *hw)
rtl_write_word(rtlpriv, REG_APS_FSMCO, value16);
do {
if (!(rtl_read_word(rtlpriv, REG_APS_FSMCO) & APFM_ONMAC)) {
- printk(KERN_INFO "rtl8192cu: MAC auto ON okay!\n");
+ pr_info("MAC auto ON okay!\n");
break;
}
if (pollingCount++ > 100) {
@@ -819,7 +806,7 @@ static void _rtl92cu_init_chipN_one_out_ep_priority(struct ieee80211_hw *hw,
}
_rtl92c_init_chipN_reg_priority(hw, value, value, value, value,
value, value);
- printk(KERN_INFO "rtl8192cu: Tx queue select: 0x%02x\n", queue_sel);
+ pr_info("Tx queue select: 0x%02x\n", queue_sel);
}
static void _rtl92cu_init_chipN_two_out_ep_priority(struct ieee80211_hw *hw,
@@ -863,7 +850,7 @@ static void _rtl92cu_init_chipN_two_out_ep_priority(struct ieee80211_hw *hw,
hiQ = valueHi;
}
_rtl92c_init_chipN_reg_priority(hw, beQ, bkQ, viQ, voQ, mgtQ, hiQ);
- printk(KERN_INFO "rtl8192cu: Tx queue select: 0x%02x\n", queue_sel);
+ pr_info("Tx queue select: 0x%02x\n", queue_sel);
}
static void _rtl92cu_init_chipN_three_out_ep_priority(struct ieee80211_hw *hw,
@@ -1594,7 +1581,7 @@ static void _rtl92cu_set_check_bssid(struct ieee80211_hw *hw,
default:
break;
}
- if (filterout_non_associated_bssid == true) {
+ if (filterout_non_associated_bssid) {
if (IS_NORMAL_CHIP(rtlhal->version)) {
switch (rtlphy->current_io_type) {
case IO_CMD_RESUME_DM_BY_SCAN:
@@ -2155,7 +2142,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
case HW_VAR_CORRECT_TSF:{
u8 btype_ibss = ((u8 *) (val))[0];
- if (btype_ibss == true)
+ if (btype_ibss)
_rtl92cu_stop_tx_beacon(hw);
_rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(3));
rtl_write_dword(rtlpriv, REG_TSFTR, (u32)(mac->tsf &
@@ -2163,7 +2150,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
rtl_write_dword(rtlpriv, REG_TSFTR + 4,
(u32)((mac->tsf >> 32) & 0xffffffff));
_rtl92cu_set_bcn_ctrl_reg(hw, BIT(3), 0);
- if (btype_ibss == true)
+ if (btype_ibss)
_rtl92cu_resume_tx_beacon(hw);
break;
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/led.c b/drivers/net/wireless/rtlwifi/rtl8192cu/led.c
index 332c74348a69..2ff9d8314e7b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/led.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/led.c
@@ -82,7 +82,7 @@ void rtl92cu_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
break;
case LED_PIN_LED0:
ledcfg &= 0xf0;
- if (usbpriv->ledctl.led_opendrain == true)
+ if (usbpriv->ledctl.led_opendrain)
rtl_write_byte(rtlpriv, REG_LEDCFG2,
(ledcfg | BIT(1) | BIT(5) | BIT(6)));
else
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c
index f8514cba17b6..060a06f4a885 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c
@@ -26,6 +26,9 @@
* Larry Finger <Larry.Finger@lwfinger.net>
*
****************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include "../wifi.h"
@@ -213,14 +216,14 @@ bool rtl92c_init_llt_table(struct ieee80211_hw *hw, u32 boundary)
for (i = 0; i < (boundary - 1); i++) {
rst = rtl92c_llt_write(hw, i , i + 1);
if (true != rst) {
- printk(KERN_ERR "===> %s #1 fail\n", __func__);
+ pr_err("===> %s #1 fail\n", __func__);
return rst;
}
}
/* end of list */
rst = rtl92c_llt_write(hw, (boundary - 1), 0xFF);
if (true != rst) {
- printk(KERN_ERR "===> %s #2 fail\n", __func__);
+ pr_err("===> %s #2 fail\n", __func__);
return rst;
}
/* Make the other pages as ring buffer
@@ -231,14 +234,14 @@ bool rtl92c_init_llt_table(struct ieee80211_hw *hw, u32 boundary)
for (i = boundary; i < LLT_LAST_ENTRY_OF_TX_PKT_BUFFER; i++) {
rst = rtl92c_llt_write(hw, i, (i + 1));
if (true != rst) {
- printk(KERN_ERR "===> %s #3 fail\n", __func__);
+ pr_err("===> %s #3 fail\n", __func__);
return rst;
}
}
/* Let last entry point to the start entry of ring buffer */
rst = rtl92c_llt_write(hw, LLT_LAST_ENTRY_OF_TX_PKT_BUFFER, boundary);
if (true != rst) {
- printk(KERN_ERR "===> %s #4 fail\n", __func__);
+ pr_err("===> %s #4 fail\n", __func__);
return rst;
}
return rst;
@@ -380,13 +383,11 @@ void rtl92c_enable_interrupt(struct ieee80211_hw *hw)
0xFFFFFFFF);
rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] &
0xFFFFFFFF);
- rtlpci->irq_enabled = true;
} else {
rtl_write_dword(rtlpriv, REG_HIMR, rtlusb->irq_mask[0] &
0xFFFFFFFF);
rtl_write_dword(rtlpriv, REG_HIMRE, rtlusb->irq_mask[1] &
0xFFFFFFFF);
- rtlusb->irq_enabled = true;
}
}
@@ -398,16 +399,9 @@ void rtl92c_init_interrupt(struct ieee80211_hw *hw)
void rtl92c_disable_interrupt(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
- struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
- struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));
rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED);
rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED);
- if (IS_HARDWARE_TYPE_8192CE(rtlhal))
- rtlpci->irq_enabled = false;
- else if (IS_HARDWARE_TYPE_8192CU(rtlhal))
- rtlusb->irq_enabled = false;
}
void rtl92c_set_qos(struct ieee80211_hw *hw, int aci)
@@ -898,8 +892,8 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw,
pstats->rxpower = rx_pwr_all;
pstats->recvsignalpower = rx_pwr_all;
if (GET_RX_DESC_RX_MCS(pdesc) &&
- GET_RX_DESC_RX_MCS(pdesc) >= DESC92C_RATEMCS8 &&
- GET_RX_DESC_RX_MCS(pdesc) <= DESC92C_RATEMCS15)
+ GET_RX_DESC_RX_MCS(pdesc) >= DESC92_RATEMCS8 &&
+ GET_RX_DESC_RX_MCS(pdesc) <= DESC92_RATEMCS15)
max_spatial_stream = 2;
else
max_spatial_stream = 1;
@@ -1113,7 +1107,6 @@ void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr;
u8 *tmp_buf;
u8 *praddr;
- u8 *psaddr;
__le16 fc;
u16 type, cpu_fc;
bool packet_matchbssid, packet_toself, packet_beacon;
@@ -1124,7 +1117,6 @@ void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw,
cpu_fc = le16_to_cpu(fc);
type = WLAN_FC_GET_TYPE(fc);
praddr = hdr->addr1;
- psaddr = hdr->addr2;
packet_matchbssid =
((IEEE80211_FTYPE_CTL != type) &&
(!compare_ether_addr(mac->bssid,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.h b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.h
index 298fdb724aa5..626d88e88e26 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.h
@@ -87,12 +87,6 @@ void rtl92c_set_data_filter(struct ieee80211_hw *hw, u16 filter);
u32 rtl92c_get_txdma_status(struct ieee80211_hw *hw);
-#define RX_HAL_IS_CCK_RATE(_pdesc)\
- (GET_RX_DESC_RX_MCS(_pdesc) == DESC92C_RATE1M ||\
- GET_RX_DESC_RX_MCS(_pdesc) == DESC92C_RATE2M ||\
- GET_RX_DESC_RX_MCS(_pdesc) == DESC92C_RATE5_5M ||\
- GET_RX_DESC_RX_MCS(_pdesc) == DESC92C_RATE11M)
-
struct rx_fwinfo_92c {
u8 gain_trsw[4];
u8 pwdb_all;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c b/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c
index 9a3d0239e27e..72852900df84 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c
@@ -470,7 +470,6 @@ static bool _rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw,
u8 i, queue_id;
struct rtl8192_tx_ring *ring = NULL;
- ppsc->set_rfpowerstate_inprogress = true;
switch (rfpwr_state) {
case ERFON:
if ((ppsc->rfpwr_state == ERFOFF) &&
@@ -590,7 +589,6 @@ static bool _rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw,
}
if (bresult)
ppsc->rfpwr_state = rfpwr_state;
- ppsc->set_rfpowerstate_inprogress = false;
return bresult;
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c
index c7576ec4744e..1e851aae58db 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c
@@ -82,7 +82,7 @@ void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
(rtlefuse->external_pa))
turbo_scanoff = true;
}
- if (mac->act_scanning == true) {
+ if (mac->act_scanning) {
tx_agc[RF90_PATH_A] = 0x3f3f3f3f;
tx_agc[RF90_PATH_B] = 0x3f3f3f3f;
if (turbo_scanoff) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
index 092e342c19df..c244f2f1b83f 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
@@ -42,6 +42,7 @@
#include "led.h"
#include "hw.h"
#include <linux/vmalloc.h>
+#include <linux/module.h>
MODULE_AUTHOR("Georgia <georgia@realtek.com>");
MODULE_AUTHOR("Ziv Huang <ziv_huang@realtek.com>");
@@ -60,6 +61,7 @@ static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->dm.dm_flag = 0;
rtlpriv->dm.disable_framebursting = 0;
rtlpriv->dm.thermalvalue = 0;
+ rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug;
rtlpriv->rtlhal.pfirmware = vmalloc(0x4000);
if (!rtlpriv->rtlhal.pfirmware) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -149,8 +151,14 @@ static struct rtl_hal_ops rtl8192cu_hal_ops = {
static struct rtl_mod_params rtl92cu_mod_params = {
.sw_crypto = 0,
+ .debug = DBG_EMERG,
};
+module_param_named(swenc, rtl92cu_mod_params.sw_crypto, bool, 0444);
+module_param_named(debug, rtl92cu_mod_params.debug, int, 0444);
+MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
+MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
+
static struct rtl_hal_usbint_cfg rtl92cu_interface_cfg = {
/* rx */
.in_ep_num = RTL92C_USB_BULK_IN_NUM,
@@ -241,20 +249,20 @@ static struct rtl_hal_cfg rtl92cu_hal_cfg = {
.maps[RTL_IMR_ROK] = IMR_ROK,
.maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER),
- .maps[RTL_RC_CCK_RATE1M] = DESC92C_RATE1M,
- .maps[RTL_RC_CCK_RATE2M] = DESC92C_RATE2M,
- .maps[RTL_RC_CCK_RATE5_5M] = DESC92C_RATE5_5M,
- .maps[RTL_RC_CCK_RATE11M] = DESC92C_RATE11M,
- .maps[RTL_RC_OFDM_RATE6M] = DESC92C_RATE6M,
- .maps[RTL_RC_OFDM_RATE9M] = DESC92C_RATE9M,
- .maps[RTL_RC_OFDM_RATE12M] = DESC92C_RATE12M,
- .maps[RTL_RC_OFDM_RATE18M] = DESC92C_RATE18M,
- .maps[RTL_RC_OFDM_RATE24M] = DESC92C_RATE24M,
- .maps[RTL_RC_OFDM_RATE36M] = DESC92C_RATE36M,
- .maps[RTL_RC_OFDM_RATE48M] = DESC92C_RATE48M,
- .maps[RTL_RC_OFDM_RATE54M] = DESC92C_RATE54M,
- .maps[RTL_RC_HT_RATEMCS7] = DESC92C_RATEMCS7,
- .maps[RTL_RC_HT_RATEMCS15] = DESC92C_RATEMCS15,
+ .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M,
+ .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M,
+ .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M,
+ .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M,
+ .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M,
+ .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M,
+ .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M,
+ .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M,
+ .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M,
+ .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M,
+ .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M,
+ .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M,
+ .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7,
+ .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15,
};
#define USB_VENDER_ID_REALTEK 0x0bda
@@ -281,6 +289,8 @@ static struct usb_device_id rtl8192c_usb_ids[] = {
{RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817d, rtl92cu_hal_cfg)},
/* 8188CE-VAU USB minCard (b/g mode only) */
{RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817e, rtl92cu_hal_cfg)},
+ /* 8188RU in Alfa AWUS036NHR */
+ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817f, rtl92cu_hal_cfg)},
/* 8188 Combo for BC4 */
{RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8754, rtl92cu_hal_cfg)},
@@ -298,24 +308,28 @@ static struct usb_device_id rtl8192c_usb_ids[] = {
{RTL_USB_DEVICE(0x06f8, 0xe033, rtl92cu_hal_cfg)}, /*Hercules - Edimax*/
{RTL_USB_DEVICE(0x07b8, 0x8188, rtl92cu_hal_cfg)}, /*Abocom - Abocom*/
{RTL_USB_DEVICE(0x07b8, 0x8189, rtl92cu_hal_cfg)}, /*Funai - Abocom*/
+ {RTL_USB_DEVICE(0x0846, 0x9041, rtl92cu_hal_cfg)}, /*NetGear WNA1000M*/
{RTL_USB_DEVICE(0x0Df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/
{RTL_USB_DEVICE(0x0eb0, 0x9071, rtl92cu_hal_cfg)}, /*NO Brand - Etop*/
/* HP - Lite-On ,8188CUS Slim Combo */
{RTL_USB_DEVICE(0x103c, 0x1629, rtl92cu_hal_cfg)},
+ {RTL_USB_DEVICE(0x13d3, 0x3357, rtl92cu_hal_cfg)}, /* AzureWave */
{RTL_USB_DEVICE(0x2001, 0x3308, rtl92cu_hal_cfg)}, /*D-Link - Alpha*/
{RTL_USB_DEVICE(0x2019, 0xab2a, rtl92cu_hal_cfg)}, /*Planex - Abocom*/
{RTL_USB_DEVICE(0x2019, 0xed17, rtl92cu_hal_cfg)}, /*PCI - Edimax*/
{RTL_USB_DEVICE(0x20f4, 0x648b, rtl92cu_hal_cfg)}, /*TRENDnet - Cameo*/
{RTL_USB_DEVICE(0x7392, 0x7811, rtl92cu_hal_cfg)}, /*Edimax - Edimax*/
- {RTL_USB_DEVICE(0x3358, 0x13d3, rtl92cu_hal_cfg)}, /*Azwave 8188CE-VAU*/
+ {RTL_USB_DEVICE(0x13d3, 0x3358, rtl92cu_hal_cfg)}, /*Azwave 8188CE-VAU*/
/* Russian customer -Azwave (8188CE-VAU b/g mode only) */
- {RTL_USB_DEVICE(0x3359, 0x13d3, rtl92cu_hal_cfg)},
+ {RTL_USB_DEVICE(0x13d3, 0x3359, rtl92cu_hal_cfg)},
+ {RTL_USB_DEVICE(0x4855, 0x0090, rtl92cu_hal_cfg)}, /* Feixun */
+ {RTL_USB_DEVICE(0x4855, 0x0091, rtl92cu_hal_cfg)}, /* NetweeN-Feixun */
+ {RTL_USB_DEVICE(0x9846, 0x9041, rtl92cu_hal_cfg)}, /* Netgear Cameo */
/****** 8192CU ********/
{RTL_USB_DEVICE(0x0586, 0x341f, rtl92cu_hal_cfg)}, /*Zyxel -Abocom*/
{RTL_USB_DEVICE(0x07aa, 0x0056, rtl92cu_hal_cfg)}, /*ATKK-Gemtek*/
{RTL_USB_DEVICE(0x07b8, 0x8178, rtl92cu_hal_cfg)}, /*Funai -Abocom*/
- {RTL_USB_DEVICE(0x07b8, 0x8178, rtl92cu_hal_cfg)}, /*Abocom -Abocom*/
{RTL_USB_DEVICE(0x2001, 0x3307, rtl92cu_hal_cfg)}, /*D-Link-Cameo*/
{RTL_USB_DEVICE(0x2001, 0x3309, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
{RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
index 3a92ba3c4a1e..bc33b147f44f 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
@@ -337,12 +337,12 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw,
rx_status->flag |= RX_FLAG_MACTIME_MPDU;
if (stats->decrypted)
rx_status->flag |= RX_FLAG_DECRYPTED;
- rx_status->rate_idx = _rtl92c_rate_mapping(hw,
- (bool)GET_RX_DESC_RX_HT(pdesc),
- (u8)GET_RX_DESC_RX_MCS(pdesc),
- (bool)GET_RX_DESC_PAGGR(pdesc));
+ rx_status->rate_idx = rtlwifi_rate_mapping(hw,
+ (bool)GET_RX_DESC_RX_HT(pdesc),
+ (u8)GET_RX_DESC_RX_MCS(pdesc),
+ (bool)GET_RX_DESC_PAGGR(pdesc));
rx_status->mactime = GET_RX_DESC_TSFL(pdesc);
- if (phystatus == true) {
+ if (phystatus) {
p_drvinfo = (struct rx_fwinfo_92c *)(pdesc + RTL_RX_DESC_SIZE);
rtl92c_translate_rx_signal_stuff(hw, skb, stats, pdesc,
p_drvinfo);
@@ -406,11 +406,10 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb)
if (GET_RX_DESC_RX_HT(rxdesc))
rx_status->flag |= RX_FLAG_HT;
/* Data rate */
- rx_status->rate_idx = _rtl92c_rate_mapping(hw,
- (bool)GET_RX_DESC_RX_HT(rxdesc),
- (u8)GET_RX_DESC_RX_MCS(rxdesc),
- (bool)GET_RX_DESC_PAGGR(rxdesc)
- );
+ rx_status->rate_idx = rtlwifi_rate_mapping(hw,
+ (bool)GET_RX_DESC_RX_HT(rxdesc),
+ (u8)GET_RX_DESC_RX_MCS(rxdesc),
+ (bool)GET_RX_DESC_PAGGR(rxdesc));
/* There is a phy status after this rx descriptor. */
if (GET_RX_DESC_PHY_STATUS(rxdesc)) {
p_drvinfo = (struct rx_fwinfo_92c *)(rxdesc + RTL_RX_DESC_SIZE);
@@ -545,19 +544,20 @@ void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw,
SET_TX_DESC_RTS_BW(txdesc, 0);
SET_TX_DESC_RTS_SC(txdesc, tcb_desc->rts_sc);
SET_TX_DESC_RTS_SHORT(txdesc,
- ((tcb_desc->rts_rate <= DESC92C_RATE54M) ?
+ ((tcb_desc->rts_rate <= DESC92_RATE54M) ?
(tcb_desc->rts_use_shortpreamble ? 1 : 0)
: (tcb_desc->rts_use_shortgi ? 1 : 0)));
if (mac->bw_40) {
- if (tcb_desc->packet_bw) {
+ if (rate_flag & IEEE80211_TX_RC_DUP_DATA) {
SET_TX_DESC_DATA_BW(txdesc, 1);
SET_TX_DESC_DATA_SC(txdesc, 3);
+ } else if(rate_flag & IEEE80211_TX_RC_40_MHZ_WIDTH){
+ SET_TX_DESC_DATA_BW(txdesc, 1);
+ SET_TX_DESC_DATA_SC(txdesc, mac->cur_40_prime_sc);
} else {
SET_TX_DESC_DATA_BW(txdesc, 0);
- if (rate_flag & IEEE80211_TX_RC_DUP_DATA)
- SET_TX_DESC_DATA_SC(txdesc,
- mac->cur_40_prime_sc);
- }
+ SET_TX_DESC_DATA_SC(txdesc, 0);
+ }
} else {
SET_TX_DESC_DATA_BW(txdesc, 0);
SET_TX_DESC_DATA_SC(txdesc, 0);
@@ -643,7 +643,7 @@ void rtl92cu_fill_fake_txdesc(struct ieee80211_hw *hw, u8 * pDesc,
}
SET_TX_DESC_USE_RATE(pDesc, 1); /* use data rate which is set by Sw */
SET_TX_DESC_OWN(pDesc, 1);
- SET_TX_DESC_TX_RATE(pDesc, DESC92C_RATE1M);
+ SET_TX_DESC_TX_RATE(pDesc, DESC92_RATE1M);
_rtl_tx_desc_checksum(pDesc);
}
@@ -659,7 +659,7 @@ void rtl92cu_tx_fill_cmddesc(struct ieee80211_hw *hw,
memset((void *)pdesc, 0, RTL_TX_HEADER_SIZE);
if (firstseg)
SET_TX_DESC_OFFSET(pdesc, RTL_TX_HEADER_SIZE);
- SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE1M);
+ SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M);
SET_TX_DESC_SEQ(pdesc, 0);
SET_TX_DESC_LINIP(pdesc, 0);
SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/Makefile b/drivers/net/wireless/rtlwifi/rtl8192de/Makefile
new file mode 100644
index 000000000000..e3213c8264b6
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/Makefile
@@ -0,0 +1,14 @@
+rtl8192de-objs := \
+ dm.o \
+ fw.o \
+ hw.o \
+ led.o \
+ phy.o \
+ rf.o \
+ sw.o \
+ table.o \
+ trx.o
+
+obj-$(CONFIG_RTL8192DE) += rtl8192de.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/def.h b/drivers/net/wireless/rtlwifi/rtl8192de/def.h
new file mode 100644
index 000000000000..946304771748
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/def.h
@@ -0,0 +1,273 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92D_DEF_H__
+#define __RTL92D_DEF_H__
+
+/* Min Spacing related settings. */
+#define MAX_MSS_DENSITY_2T 0x13
+#define MAX_MSS_DENSITY_1T 0x0A
+
+#define RF6052_MAX_TX_PWR 0x3F
+#define RF6052_MAX_REG 0x3F
+#define RF6052_MAX_PATH 2
+
+#define HAL_RETRY_LIMIT_INFRA 48
+#define HAL_RETRY_LIMIT_AP_ADHOC 7
+
+#define PHY_RSSI_SLID_WIN_MAX 100
+#define PHY_LINKQUALITY_SLID_WIN_MAX 20
+#define PHY_BEACON_RSSI_SLID_WIN_MAX 10
+
+#define RESET_DELAY_8185 20
+
+#define RT_IBSS_INT_MASKS (IMR_BCNINT | IMR_TBDOK | IMR_TBDER)
+#define RT_AC_INT_MASKS (IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK)
+
+#define NUM_OF_FIRMWARE_QUEUE 10
+#define NUM_OF_PAGES_IN_FW 0x100
+#define NUM_OF_PAGE_IN_FW_QUEUE_BK 0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_BE 0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_VI 0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_VO 0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA 0x0
+#define NUM_OF_PAGE_IN_FW_QUEUE_CMD 0x0
+#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT 0x02
+#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH 0x02
+#define NUM_OF_PAGE_IN_FW_QUEUE_BCN 0x2
+#define NUM_OF_PAGE_IN_FW_QUEUE_PUB 0xA1
+
+#define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM 0x026
+#define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM 0x048
+#define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM 0x048
+#define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM 0x026
+#define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM 0x00
+
+#define MAX_LINES_HWCONFIG_TXT 1000
+#define MAX_BYTES_LINE_HWCONFIG_TXT 256
+
+#define SW_THREE_WIRE 0
+#define HW_THREE_WIRE 2
+
+#define BT_DEMO_BOARD 0
+#define BT_QA_BOARD 1
+#define BT_FPGA 2
+
+#define RX_SMOOTH_FACTOR 20
+
+#define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0
+#define HAL_PRIME_CHNL_OFFSET_LOWER 1
+#define HAL_PRIME_CHNL_OFFSET_UPPER 2
+
+#define MAX_H2C_QUEUE_NUM 10
+
+#define RX_MPDU_QUEUE 0
+#define RX_CMD_QUEUE 1
+#define RX_MAX_QUEUE 2
+
+#define C2H_RX_CMD_HDR_LEN 8
+#define GET_C2H_CMD_CMD_LEN(__prxhdr) \
+ LE_BITS_TO_4BYTE((__prxhdr), 0, 16)
+#define GET_C2H_CMD_ELEMENT_ID(__prxhdr) \
+ LE_BITS_TO_4BYTE((__prxhdr), 16, 8)
+#define GET_C2H_CMD_CMD_SEQ(__prxhdr) \
+ LE_BITS_TO_4BYTE((__prxhdr), 24, 7)
+#define GET_C2H_CMD_CONTINUE(__prxhdr) \
+ LE_BITS_TO_4BYTE((__prxhdr), 31, 1)
+#define GET_C2H_CMD_CONTENT(__prxhdr) \
+ ((u8 *)(__prxhdr) + C2H_RX_CMD_HDR_LEN)
+
+#define GET_C2H_CMD_FEEDBACK_ELEMENT_ID(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE((__pcmdfbhdr), 0, 8)
+#define GET_C2H_CMD_FEEDBACK_CCX_LEN(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE((__pcmdfbhdr), 8, 8)
+#define GET_C2H_CMD_FEEDBACK_CCX_CMD_CNT(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE((__pcmdfbhdr), 16, 16)
+#define GET_C2H_CMD_FEEDBACK_CCX_MAC_ID(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 0, 5)
+#define GET_C2H_CMD_FEEDBACK_CCX_VALID(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 7, 1)
+#define GET_C2H_CMD_FEEDBACK_CCX_RETRY_CNT(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 8, 5)
+#define GET_C2H_CMD_FEEDBACK_CCX_TOK(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 15, 1)
+#define GET_C2H_CMD_FEEDBACK_CCX_QSEL(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 16, 4)
+#define GET_C2H_CMD_FEEDBACK_CCX_SEQ(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 20, 12)
+
+enum version_8192d {
+ VERSION_TEST_CHIP_88C = 0x0000,
+ VERSION_TEST_CHIP_92C = 0x0020,
+ VERSION_TEST_UMC_CHIP_8723 = 0x0081,
+ VERSION_NORMAL_TSMC_CHIP_88C = 0x0008,
+ VERSION_NORMAL_TSMC_CHIP_92C = 0x0028,
+ VERSION_NORMAL_TSMC_CHIP_92C_1T2R = 0x0018,
+ VERSION_NORMAL_UMC_CHIP_88C_A_CUT = 0x0088,
+ VERSION_NORMAL_UMC_CHIP_92C_A_CUT = 0x00a8,
+ VERSION_NORMAL_UMC_CHIP_92C_1T2R_A_CUT = 0x0098,
+ VERSION_NORMAL_UMC_CHIP_8723_1T1R_A_CUT = 0x0089,
+ VERSION_NORMAL_UMC_CHIP_8723_1T1R_B_CUT = 0x1089,
+ VERSION_NORMAL_UMC_CHIP_88C_B_CUT = 0x1088,
+ VERSION_NORMAL_UMC_CHIP_92C_B_CUT = 0x10a8,
+ VERSION_NORMAL_UMC_CHIP_92C_1T2R_B_CUT = 0x1090,
+ VERSION_TEST_CHIP_92D_SINGLEPHY = 0x0022,
+ VERSION_TEST_CHIP_92D_DUALPHY = 0x0002,
+ VERSION_NORMAL_CHIP_92D_SINGLEPHY = 0x002a,
+ VERSION_NORMAL_CHIP_92D_DUALPHY = 0x000a,
+ VERSION_NORMAL_CHIP_92D_C_CUT_SINGLEPHY = 0x202a,
+ VERSION_NORMAL_CHIP_92D_C_CUT_DUALPHY = 0x200a,
+ VERSION_NORMAL_CHIP_92D_D_CUT_SINGLEPHY = 0x302a,
+ VERSION_NORMAL_CHIP_92D_D_CUT_DUALPHY = 0x300a,
+ VERSION_NORMAL_CHIP_92D_E_CUT_SINGLEPHY = 0x402a,
+ VERSION_NORMAL_CHIP_92D_E_CUT_DUALPHY = 0x400a,
+};
+
+/* for 92D */
+#define CHIP_92D_SINGLEPHY BIT(9)
+#define C_CUT_VERSION BIT(13)
+#define D_CUT_VERSION ((BIT(12)|BIT(13)))
+#define E_CUT_VERSION BIT(14)
+
+/* Chip specific */
+#define CHIP_BONDING_IDENTIFIER(_value) (((_value)>>22)&0x3)
+#define CHIP_BONDING_92C_1T2R 0x1
+#define CHIP_BONDING_88C_USB_MCARD 0x2
+#define CHIP_BONDING_88C_USB_HP 0x1
+
+/* [15:12] IC version(CUT): A-cut=0, B-cut=1, C-cut=2, D-cut=3 */
+/* [7] Manufacturer: TSMC=0, UMC=1 */
+/* [6:4] RF type: 1T1R=0, 1T2R=1, 2T2R=2 */
+/* [3] Chip type: TEST=0, NORMAL=1 */
+/* [2:0] IC type: 81xxC=0, 8723=1, 92D=2 */
+#define CHIP_8723 BIT(0)
+#define CHIP_92D BIT(1)
+#define NORMAL_CHIP BIT(3)
+#define RF_TYPE_1T1R (~(BIT(4)|BIT(5)|BIT(6)))
+#define RF_TYPE_1T2R BIT(4)
+#define RF_TYPE_2T2R BIT(5)
+#define CHIP_VENDOR_UMC BIT(7)
+#define B_CUT_VERSION BIT(12)
+
+/* MASK */
+#define IC_TYPE_MASK (BIT(0)|BIT(1)|BIT(2))
+#define CHIP_TYPE_MASK BIT(3)
+#define RF_TYPE_MASK (BIT(4)|BIT(5)|BIT(6))
+#define MANUFACTUER_MASK BIT(7)
+#define ROM_VERSION_MASK (BIT(11)|BIT(10)|BIT(9)|BIT(8))
+#define CUT_VERSION_MASK (BIT(15)|BIT(14)|BIT(13)|BIT(12))
+
+
+/* Get element */
+#define GET_CVID_IC_TYPE(version) ((version) & IC_TYPE_MASK)
+#define GET_CVID_CHIP_TYPE(version) ((version) & CHIP_TYPE_MASK)
+#define GET_CVID_RF_TYPE(version) ((version) & RF_TYPE_MASK)
+#define GET_CVID_MANUFACTUER(version) ((version) & MANUFACTUER_MASK)
+#define GET_CVID_ROM_VERSION(version) ((version) & ROM_VERSION_MASK)
+#define GET_CVID_CUT_VERSION(version) ((version) & CUT_VERSION_MASK)
+
+#define IS_1T1R(version) ((GET_CVID_RF_TYPE(version)) ? \
+ false : true)
+#define IS_1T2R(version) ((GET_CVID_RF_TYPE(version) == \
+ RF_TYPE_1T2R) ? true : false)
+#define IS_2T2R(version) ((GET_CVID_RF_TYPE(version) == \
+ RF_TYPE_2T2R) ? true : false)
+
+#define IS_92D_SINGLEPHY(version) ((IS_92D(version)) ? \
+ (IS_2T2R(version) ? true : false) : false)
+#define IS_92D(version) ((GET_CVID_IC_TYPE(version) == \
+ CHIP_92D) ? true : false)
+#define IS_92D_C_CUT(version) ((IS_92D(version)) ? \
+ ((GET_CVID_CUT_VERSION(version) == \
+ 0x2000) ? true : false) : false)
+#define IS_92D_D_CUT(version) ((IS_92D(version)) ? \
+ ((GET_CVID_CUT_VERSION(version) == \
+ 0x3000) ? true : false) : false)
+#define IS_92D_E_CUT(version) ((IS_92D(version)) ? \
+ ((GET_CVID_CUT_VERSION(version) == \
+ 0x4000) ? true : false) : false)
+#define CHIP_92D_C_CUT BIT(10)
+#define CHIP_92D_D_CUT BIT(11)
+
+enum rf_optype {
+ RF_OP_BY_SW_3WIRE = 0,
+ RF_OP_BY_FW,
+ RF_OP_MAX
+};
+
+enum rtl_desc_qsel {
+ QSLT_BK = 0x2,
+ QSLT_BE = 0x0,
+ QSLT_VI = 0x5,
+ QSLT_VO = 0x7,
+ QSLT_BEACON = 0x10,
+ QSLT_HIGH = 0x11,
+ QSLT_MGNT = 0x12,
+ QSLT_CMD = 0x13,
+};
+
+enum channel_plan {
+ CHPL_FCC = 0,
+ CHPL_IC = 1,
+ CHPL_ETSI = 2,
+ CHPL_SPAIN = 3,
+ CHPL_FRANCE = 4,
+ CHPL_MKK = 5,
+ CHPL_MKK1 = 6,
+ CHPL_ISRAEL = 7,
+ CHPL_TELEC = 8,
+ CHPL_GLOBAL = 9,
+ CHPL_WORLD = 10,
+};
+
+struct phy_sts_cck_8192d {
+ u8 adc_pwdb_X[4];
+ u8 sq_rpt;
+ u8 cck_agc_rpt;
+};
+
+struct h2c_cmd_8192c {
+ u8 element_id;
+ u32 cmd_len;
+ u8 *p_cmdbuffer;
+};
+
+struct txpower_info {
+ u8 cck_index[RF6052_MAX_PATH][CHANNEL_GROUP_MAX];
+ u8 ht40_1sindex[RF6052_MAX_PATH][CHANNEL_GROUP_MAX];
+ u8 ht40_2sindexdiff[RF6052_MAX_PATH][CHANNEL_GROUP_MAX];
+ u8 ht20indexdiff[RF6052_MAX_PATH][CHANNEL_GROUP_MAX];
+ u8 ofdmindexdiff[RF6052_MAX_PATH][CHANNEL_GROUP_MAX];
+ u8 ht40maxoffset[RF6052_MAX_PATH][CHANNEL_GROUP_MAX];
+ u8 ht20maxoffset[RF6052_MAX_PATH][CHANNEL_GROUP_MAX];
+ u8 tssi_a[3]; /* 5GL/5GM/5GH */
+ u8 tssi_b[3];
+};
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
new file mode 100644
index 000000000000..3cd0736fe8e1
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
@@ -0,0 +1,1355 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../base.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "dm.h"
+#include "fw.h"
+
+#define UNDEC_SM_PWDB entry_min_undecoratedsmoothed_pwdb
+
+struct dig_t de_digtable;
+
+static const u32 ofdmswing_table[OFDM_TABLE_SIZE_92D] = {
+ 0x7f8001fe, /* 0, +6.0dB */
+ 0x788001e2, /* 1, +5.5dB */
+ 0x71c001c7, /* 2, +5.0dB */
+ 0x6b8001ae, /* 3, +4.5dB */
+ 0x65400195, /* 4, +4.0dB */
+ 0x5fc0017f, /* 5, +3.5dB */
+ 0x5a400169, /* 6, +3.0dB */
+ 0x55400155, /* 7, +2.5dB */
+ 0x50800142, /* 8, +2.0dB */
+ 0x4c000130, /* 9, +1.5dB */
+ 0x47c0011f, /* 10, +1.0dB */
+ 0x43c0010f, /* 11, +0.5dB */
+ 0x40000100, /* 12, +0dB */
+ 0x3c8000f2, /* 13, -0.5dB */
+ 0x390000e4, /* 14, -1.0dB */
+ 0x35c000d7, /* 15, -1.5dB */
+ 0x32c000cb, /* 16, -2.0dB */
+ 0x300000c0, /* 17, -2.5dB */
+ 0x2d4000b5, /* 18, -3.0dB */
+ 0x2ac000ab, /* 19, -3.5dB */
+ 0x288000a2, /* 20, -4.0dB */
+ 0x26000098, /* 21, -4.5dB */
+ 0x24000090, /* 22, -5.0dB */
+ 0x22000088, /* 23, -5.5dB */
+ 0x20000080, /* 24, -6.0dB */
+ 0x1e400079, /* 25, -6.5dB */
+ 0x1c800072, /* 26, -7.0dB */
+ 0x1b00006c, /* 27. -7.5dB */
+ 0x19800066, /* 28, -8.0dB */
+ 0x18000060, /* 29, -8.5dB */
+ 0x16c0005b, /* 30, -9.0dB */
+ 0x15800056, /* 31, -9.5dB */
+ 0x14400051, /* 32, -10.0dB */
+ 0x1300004c, /* 33, -10.5dB */
+ 0x12000048, /* 34, -11.0dB */
+ 0x11000044, /* 35, -11.5dB */
+ 0x10000040, /* 36, -12.0dB */
+ 0x0f00003c, /* 37, -12.5dB */
+ 0x0e400039, /* 38, -13.0dB */
+ 0x0d800036, /* 39, -13.5dB */
+ 0x0cc00033, /* 40, -14.0dB */
+ 0x0c000030, /* 41, -14.5dB */
+ 0x0b40002d, /* 42, -15.0dB */
+};
+
+static const u8 cckswing_table_ch1ch13[CCK_TABLE_SIZE][8] = {
+ {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04}, /* 0, +0dB */
+ {0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04}, /* 1, -0.5dB */
+ {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, /* 2, -1.0dB */
+ {0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03}, /* 3, -1.5dB */
+ {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, /* 4, -2.0dB */
+ {0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03}, /* 5, -2.5dB */
+ {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, /* 6, -3.0dB */
+ {0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03}, /* 7, -3.5dB */
+ {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, /* 8, -4.0dB */
+ {0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02}, /* 9, -4.5dB */
+ {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, /* 10, -5.0dB */
+ {0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02}, /* 11, -5.5dB */
+ {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, /* 12, -6.0dB */
+ {0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02}, /* 13, -6.5dB */
+ {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, /* 14, -7.0dB */
+ {0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02}, /* 15, -7.5dB */
+ {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, /* 16, -8.0dB */
+ {0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02}, /* 17, -8.5dB */
+ {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, /* 18, -9.0dB */
+ {0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 19, -9.5dB */
+ {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 20, -10.0dB */
+ {0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 21, -10.5dB */
+ {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 22, -11.0dB */
+ {0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01}, /* 23, -11.5dB */
+ {0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01}, /* 24, -12.0dB */
+ {0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01}, /* 25, -12.5dB */
+ {0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01}, /* 26, -13.0dB */
+ {0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 27, -13.5dB */
+ {0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 28, -14.0dB */
+ {0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 29, -14.5dB */
+ {0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 30, -15.0dB */
+ {0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01}, /* 31, -15.5dB */
+ {0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01} /* 32, -16.0dB */
+};
+
+static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = {
+ {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00}, /* 0, +0dB */
+ {0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00}, /* 1, -0.5dB */
+ {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, /* 2, -1.0dB */
+ {0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00}, /* 3, -1.5dB */
+ {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, /* 4, -2.0dB */
+ {0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00}, /* 5, -2.5dB */
+ {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, /* 6, -3.0dB */
+ {0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00}, /* 7, -3.5dB */
+ {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, /* 8, -4.0dB */
+ {0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00}, /* 9, -4.5dB */
+ {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, /* 10, -5.0dB */
+ {0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 11, -5.5dB */
+ {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 12, -6.0dB */
+ {0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00}, /* 13, -6.5dB */
+ {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, /* 14, -7.0dB */
+ {0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 15, -7.5dB */
+ {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 16, -8.0dB */
+ {0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 17, -8.5dB */
+ {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 18, -9.0dB */
+ {0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 19, -9.5dB */
+ {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 20, -10.0dB */
+ {0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 21, -10.5dB */
+ {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 22, -11.0dB */
+ {0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 23, -11.5dB */
+ {0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 24, -12.0dB */
+ {0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 25, -12.5dB */
+ {0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 26, -13.0dB */
+ {0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 27, -13.5dB */
+ {0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 28, -14.0dB */
+ {0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 29, -14.5dB */
+ {0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 30, -15.0dB */
+ {0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 31, -15.5dB */
+ {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} /* 32, -16.0dB */
+};
+
+static void rtl92d_dm_diginit(struct ieee80211_hw *hw)
+{
+ de_digtable.dig_enable_flag = true;
+ de_digtable.dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX;
+ de_digtable.cur_igvalue = 0x20;
+ de_digtable.pre_igvalue = 0x0;
+ de_digtable.cursta_connectctate = DIG_STA_DISCONNECT;
+ de_digtable.presta_connectstate = DIG_STA_DISCONNECT;
+ de_digtable.curmultista_connectstate = DIG_MULTISTA_DISCONNECT;
+ de_digtable.rssi_lowthresh = DM_DIG_THRESH_LOW;
+ de_digtable.rssi_highthresh = DM_DIG_THRESH_HIGH;
+ de_digtable.fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
+ de_digtable.fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
+ de_digtable.rx_gain_range_max = DM_DIG_FA_UPPER;
+ de_digtable.rx_gain_range_min = DM_DIG_FA_LOWER;
+ de_digtable.backoff_val = DM_DIG_BACKOFF_DEFAULT;
+ de_digtable.backoff_val_range_max = DM_DIG_BACKOFF_MAX;
+ de_digtable.backoff_val_range_min = DM_DIG_BACKOFF_MIN;
+ de_digtable.pre_cck_pd_state = CCK_PD_STAGE_LOWRSSI;
+ de_digtable.cur_cck_pd_state = CCK_PD_STAGE_MAX;
+ de_digtable.large_fa_hit = 0;
+ de_digtable.recover_cnt = 0;
+ de_digtable.forbidden_igi = DM_DIG_FA_LOWER;
+}
+
+static void rtl92d_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw)
+{
+ u32 ret_value;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt);
+ unsigned long flag = 0;
+
+ /* hold ofdm counter */
+ rtl_set_bbreg(hw, ROFDM0_LSTF, BIT(31), 1); /* hold page C counter */
+ rtl_set_bbreg(hw, ROFDM1_LSTF, BIT(31), 1); /*hold page D counter */
+
+ ret_value = rtl_get_bbreg(hw, ROFDM0_FRAMESYNC, BMASKDWORD);
+ falsealm_cnt->cnt_fast_fsync_fail = (ret_value & 0xffff);
+ falsealm_cnt->cnt_sb_search_fail = ((ret_value & 0xffff0000) >> 16);
+ ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER1, BMASKDWORD);
+ falsealm_cnt->cnt_parity_fail = ((ret_value & 0xffff0000) >> 16);
+ ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER2, BMASKDWORD);
+ falsealm_cnt->cnt_rate_illegal = (ret_value & 0xffff);
+ falsealm_cnt->cnt_crc8_fail = ((ret_value & 0xffff0000) >> 16);
+ ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, BMASKDWORD);
+ falsealm_cnt->cnt_mcs_fail = (ret_value & 0xffff);
+ falsealm_cnt->cnt_ofdm_fail = falsealm_cnt->cnt_parity_fail +
+ falsealm_cnt->cnt_rate_illegal +
+ falsealm_cnt->cnt_crc8_fail +
+ falsealm_cnt->cnt_mcs_fail +
+ falsealm_cnt->cnt_fast_fsync_fail +
+ falsealm_cnt->cnt_sb_search_fail;
+
+ if (rtlpriv->rtlhal.current_bandtype != BAND_ON_5G) {
+ /* hold cck counter */
+ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
+ ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERLOWER, BMASKBYTE0);
+ falsealm_cnt->cnt_cck_fail = ret_value;
+ ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERUPPER, BMASKBYTE3);
+ falsealm_cnt->cnt_cck_fail += (ret_value & 0xff) << 8;
+ rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
+ } else {
+ falsealm_cnt->cnt_cck_fail = 0;
+ }
+
+ /* reset false alarm counter registers */
+ falsealm_cnt->cnt_all = falsealm_cnt->cnt_fast_fsync_fail +
+ falsealm_cnt->cnt_sb_search_fail +
+ falsealm_cnt->cnt_parity_fail +
+ falsealm_cnt->cnt_rate_illegal +
+ falsealm_cnt->cnt_crc8_fail +
+ falsealm_cnt->cnt_mcs_fail +
+ falsealm_cnt->cnt_cck_fail;
+
+ rtl_set_bbreg(hw, ROFDM1_LSTF, 0x08000000, 1);
+ /* update ofdm counter */
+ rtl_set_bbreg(hw, ROFDM1_LSTF, 0x08000000, 0);
+ /* update page C counter */
+ rtl_set_bbreg(hw, ROFDM0_LSTF, BIT(31), 0);
+ /* update page D counter */
+ rtl_set_bbreg(hw, ROFDM1_LSTF, BIT(31), 0);
+ if (rtlpriv->rtlhal.current_bandtype != BAND_ON_5G) {
+ /* reset cck counter */
+ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
+ rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, 0x0000c000, 0);
+ /* enable cck counter */
+ rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, 0x0000c000, 2);
+ rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
+ }
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("Cnt_Fast_Fsync_fail = %x, "
+ "Cnt_SB_Search_fail = %x\n",
+ falsealm_cnt->cnt_fast_fsync_fail,
+ falsealm_cnt->cnt_sb_search_fail));
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("Cnt_Parity_Fail = %x, "
+ "Cnt_Rate_Illegal = %x, Cnt_Crc8_fail = %x, "
+ "Cnt_Mcs_fail = %x\n",
+ falsealm_cnt->cnt_parity_fail,
+ falsealm_cnt->cnt_rate_illegal,
+ falsealm_cnt->cnt_crc8_fail,
+ falsealm_cnt->cnt_mcs_fail));
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+ ("Cnt_Ofdm_fail = %x, " "Cnt_Cck_fail = %x, "
+ "Cnt_all = %x\n",
+ falsealm_cnt->cnt_ofdm_fail,
+ falsealm_cnt->cnt_cck_fail,
+ falsealm_cnt->cnt_all));
+}
+
+static void rtl92d_dm_find_minimum_rssi(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtlpriv);
+
+ /* Determine the minimum RSSI */
+ if ((mac->link_state < MAC80211_LINKED) &&
+ (rtlpriv->dm.UNDEC_SM_PWDB == 0)) {
+ de_digtable.min_undecorated_pwdb_for_dm = 0;
+ RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD,
+ ("Not connected to any\n"));
+ }
+ if (mac->link_state >= MAC80211_LINKED) {
+ if (mac->opmode == NL80211_IFTYPE_AP ||
+ mac->opmode == NL80211_IFTYPE_ADHOC) {
+ de_digtable.min_undecorated_pwdb_for_dm =
+ rtlpriv->dm.UNDEC_SM_PWDB;
+ RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD,
+ ("AP Client PWDB = 0x%lx\n",
+ rtlpriv->dm.UNDEC_SM_PWDB));
+ } else {
+ de_digtable.min_undecorated_pwdb_for_dm =
+ rtlpriv->dm.undecorated_smoothed_pwdb;
+ RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD,
+ ("STA Default Port PWDB = 0x%x\n",
+ de_digtable.min_undecorated_pwdb_for_dm));
+ }
+ } else {
+ de_digtable.min_undecorated_pwdb_for_dm =
+ rtlpriv->dm.UNDEC_SM_PWDB;
+ RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD,
+ ("AP Ext Port or disconnet PWDB = 0x%x\n",
+ de_digtable.min_undecorated_pwdb_for_dm));
+ }
+
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("MinUndecoratedPWDBForDM =%d\n",
+ de_digtable.min_undecorated_pwdb_for_dm));
+}
+
+static void rtl92d_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ unsigned long flag = 0;
+
+ if (de_digtable.cursta_connectctate == DIG_STA_CONNECT) {
+ if (de_digtable.pre_cck_pd_state == CCK_PD_STAGE_LOWRSSI) {
+ if (de_digtable.min_undecorated_pwdb_for_dm <= 25)
+ de_digtable.cur_cck_pd_state =
+ CCK_PD_STAGE_LOWRSSI;
+ else
+ de_digtable.cur_cck_pd_state =
+ CCK_PD_STAGE_HIGHRSSI;
+ } else {
+ if (de_digtable.min_undecorated_pwdb_for_dm <= 20)
+ de_digtable.cur_cck_pd_state =
+ CCK_PD_STAGE_LOWRSSI;
+ else
+ de_digtable.cur_cck_pd_state =
+ CCK_PD_STAGE_HIGHRSSI;
+ }
+ } else {
+ de_digtable.cur_cck_pd_state = CCK_PD_STAGE_LOWRSSI;
+ }
+ if (de_digtable.pre_cck_pd_state != de_digtable.cur_cck_pd_state) {
+ if (de_digtable.cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI) {
+ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
+ rtl_set_bbreg(hw, RCCK0_CCA, BMASKBYTE2, 0x83);
+ rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
+ } else {
+ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
+ rtl_set_bbreg(hw, RCCK0_CCA, BMASKBYTE2, 0xcd);
+ rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
+ }
+ de_digtable.pre_cck_pd_state = de_digtable.cur_cck_pd_state;
+ }
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("CurSTAConnectState=%s\n",
+ (de_digtable.cursta_connectctate == DIG_STA_CONNECT ?
+ "DIG_STA_CONNECT " : "DIG_STA_DISCONNECT")));
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("CCKPDStage=%s\n",
+ (de_digtable.cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI ?
+ "Low RSSI " : "High RSSI ")));
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("is92d single phy =%x\n",
+ IS_92D_SINGLEPHY(rtlpriv->rtlhal.version)));
+
+}
+
+void rtl92d_dm_write_dig(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("cur_igvalue = 0x%x, "
+ "pre_igvalue = 0x%x, backoff_val = %d\n",
+ de_digtable.cur_igvalue, de_digtable.pre_igvalue,
+ de_digtable.backoff_val));
+ if (de_digtable.dig_enable_flag == false) {
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("DIG is disabled\n"));
+ de_digtable.pre_igvalue = 0x17;
+ return;
+ }
+ if (de_digtable.pre_igvalue != de_digtable.cur_igvalue) {
+ rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f,
+ de_digtable.cur_igvalue);
+ rtl_set_bbreg(hw, ROFDM0_XBAGCCORE1, 0x7f,
+ de_digtable.cur_igvalue);
+ de_digtable.pre_igvalue = de_digtable.cur_igvalue;
+ }
+}
+
+static void rtl92d_early_mode_enabled(struct rtl_priv *rtlpriv)
+{
+ if ((rtlpriv->mac80211.link_state >= MAC80211_LINKED) &&
+ (rtlpriv->mac80211.vendor == PEER_CISCO)) {
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+ ("IOT_PEER = CISCO\n"));
+ if (de_digtable.last_min_undecorated_pwdb_for_dm >= 50
+ && de_digtable.min_undecorated_pwdb_for_dm < 50) {
+ rtl_write_byte(rtlpriv, REG_EARLY_MODE_CONTROL, 0x00);
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+ ("Early Mode Off\n"));
+ } else if (de_digtable.last_min_undecorated_pwdb_for_dm <= 55 &&
+ de_digtable.min_undecorated_pwdb_for_dm > 55) {
+ rtl_write_byte(rtlpriv, REG_EARLY_MODE_CONTROL, 0x0f);
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+ ("Early Mode On\n"));
+ }
+ } else if (!(rtl_read_byte(rtlpriv, REG_EARLY_MODE_CONTROL) & 0xf)) {
+ rtl_write_byte(rtlpriv, REG_EARLY_MODE_CONTROL, 0x0f);
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("Early Mode On\n"));
+ }
+}
+
+static void rtl92d_dm_dig(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 value_igi = de_digtable.cur_igvalue;
+ struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt);
+
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("==>\n"));
+ if (rtlpriv->rtlhal.earlymode_enable) {
+ rtl92d_early_mode_enabled(rtlpriv);
+ de_digtable.last_min_undecorated_pwdb_for_dm =
+ de_digtable.min_undecorated_pwdb_for_dm;
+ }
+ if (rtlpriv->dm.dm_initialgain_enable == false)
+ return;
+
+ /* because we will send data pkt when scanning
+ * this will cause some ap like gear-3700 wep TP
+ * lower if we retrun here, this is the diff of
+ * mac80211 driver vs ieee80211 driver */
+ /* if (rtlpriv->mac80211.act_scanning)
+ * return; */
+
+ /* Not STA mode return tmp */
+ if (rtlpriv->mac80211.opmode != NL80211_IFTYPE_STATION)
+ return;
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("progress\n"));
+ /* Decide the current status and if modify initial gain or not */
+ if (rtlpriv->mac80211.link_state >= MAC80211_LINKED)
+ de_digtable.cursta_connectctate = DIG_STA_CONNECT;
+ else
+ de_digtable.cursta_connectctate = DIG_STA_DISCONNECT;
+
+ /* adjust initial gain according to false alarm counter */
+ if (falsealm_cnt->cnt_all < DM_DIG_FA_TH0)
+ value_igi--;
+ else if (falsealm_cnt->cnt_all < DM_DIG_FA_TH1)
+ value_igi += 0;
+ else if (falsealm_cnt->cnt_all < DM_DIG_FA_TH2)
+ value_igi++;
+ else if (falsealm_cnt->cnt_all >= DM_DIG_FA_TH2)
+ value_igi += 2;
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+ ("dm_DIG() Before: large_fa_hit=%d, forbidden_igi=%x\n",
+ de_digtable.large_fa_hit, de_digtable.forbidden_igi));
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+ ("dm_DIG() Before: Recover_cnt=%d, rx_gain_range_min=%x\n",
+ de_digtable.recover_cnt, de_digtable.rx_gain_range_min));
+
+ /* deal with abnorally large false alarm */
+ if (falsealm_cnt->cnt_all > 10000) {
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+ ("dm_DIG(): Abnornally false alarm case.\n"));
+
+ de_digtable.large_fa_hit++;
+ if (de_digtable.forbidden_igi < de_digtable.cur_igvalue) {
+ de_digtable.forbidden_igi = de_digtable.cur_igvalue;
+ de_digtable.large_fa_hit = 1;
+ }
+ if (de_digtable.large_fa_hit >= 3) {
+ if ((de_digtable.forbidden_igi + 1) > DM_DIG_MAX)
+ de_digtable.rx_gain_range_min = DM_DIG_MAX;
+ else
+ de_digtable.rx_gain_range_min =
+ (de_digtable.forbidden_igi + 1);
+ de_digtable.recover_cnt = 3600; /* 3600=2hr */
+ }
+ } else {
+ /* Recovery mechanism for IGI lower bound */
+ if (de_digtable.recover_cnt != 0) {
+ de_digtable.recover_cnt--;
+ } else {
+ if (de_digtable.large_fa_hit == 0) {
+ if ((de_digtable.forbidden_igi - 1) <
+ DM_DIG_FA_LOWER) {
+ de_digtable.forbidden_igi =
+ DM_DIG_FA_LOWER;
+ de_digtable.rx_gain_range_min =
+ DM_DIG_FA_LOWER;
+
+ } else {
+ de_digtable.forbidden_igi--;
+ de_digtable.rx_gain_range_min =
+ (de_digtable.forbidden_igi + 1);
+ }
+ } else if (de_digtable.large_fa_hit == 3) {
+ de_digtable.large_fa_hit = 0;
+ }
+ }
+ }
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+ ("dm_DIG() After: large_fa_hit=%d, forbidden_igi=%x\n",
+ de_digtable.large_fa_hit, de_digtable.forbidden_igi));
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+ ("dm_DIG() After: recover_cnt=%d, rx_gain_range_min=%x\n",
+ de_digtable.recover_cnt, de_digtable.rx_gain_range_min));
+
+ if (value_igi > DM_DIG_MAX)
+ value_igi = DM_DIG_MAX;
+ else if (value_igi < de_digtable.rx_gain_range_min)
+ value_igi = de_digtable.rx_gain_range_min;
+ de_digtable.cur_igvalue = value_igi;
+ rtl92d_dm_write_dig(hw);
+ if (rtlpriv->rtlhal.current_bandtype != BAND_ON_5G)
+ rtl92d_dm_cck_packet_detection_thresh(hw);
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, ("<<==\n"));
+}
+
+static void rtl92d_dm_init_dynamic_txpower(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->dm.dynamic_txpower_enable = true;
+ rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL;
+ rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL;
+}
+
+static void rtl92d_dm_dynamic_txpower(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ long undecorated_smoothed_pwdb;
+
+ if ((!rtlpriv->dm.dynamic_txpower_enable)
+ || rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) {
+ rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL;
+ return;
+ }
+ if ((mac->link_state < MAC80211_LINKED) &&
+ (rtlpriv->dm.UNDEC_SM_PWDB == 0)) {
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE,
+ ("Not connected to any\n"));
+ rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL;
+ rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL;
+ return;
+ }
+ if (mac->link_state >= MAC80211_LINKED) {
+ if (mac->opmode == NL80211_IFTYPE_ADHOC) {
+ undecorated_smoothed_pwdb =
+ rtlpriv->dm.UNDEC_SM_PWDB;
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ ("IBSS Client PWDB = 0x%lx\n",
+ undecorated_smoothed_pwdb));
+ } else {
+ undecorated_smoothed_pwdb =
+ rtlpriv->dm.undecorated_smoothed_pwdb;
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ ("STA Default Port PWDB = 0x%lx\n",
+ undecorated_smoothed_pwdb));
+ }
+ } else {
+ undecorated_smoothed_pwdb =
+ rtlpriv->dm.UNDEC_SM_PWDB;
+
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ ("AP Ext Port PWDB = 0x%lx\n",
+ undecorated_smoothed_pwdb));
+ }
+ if (rtlhal->current_bandtype == BAND_ON_5G) {
+ if (undecorated_smoothed_pwdb >= 0x33) {
+ rtlpriv->dm.dynamic_txhighpower_lvl =
+ TXHIGHPWRLEVEL_LEVEL2;
+ RT_TRACE(rtlpriv, COMP_HIPWR, DBG_LOUD,
+ ("5G:TxHighPwrLevel_Level2 (TxPwr=0x0)\n"));
+ } else if ((undecorated_smoothed_pwdb < 0x33)
+ && (undecorated_smoothed_pwdb >= 0x2b)) {
+ rtlpriv->dm.dynamic_txhighpower_lvl =
+ TXHIGHPWRLEVEL_LEVEL1;
+ RT_TRACE(rtlpriv, COMP_HIPWR, DBG_LOUD,
+ ("5G:TxHighPwrLevel_Level1 (TxPwr=0x10)\n"));
+ } else if (undecorated_smoothed_pwdb < 0x2b) {
+ rtlpriv->dm.dynamic_txhighpower_lvl =
+ TXHIGHPWRLEVEL_NORMAL;
+ RT_TRACE(rtlpriv, COMP_HIPWR, DBG_LOUD,
+ ("5G:TxHighPwrLevel_Normal\n"));
+ }
+ } else {
+ if (undecorated_smoothed_pwdb >=
+ TX_POWER_NEAR_FIELD_THRESH_LVL2) {
+ rtlpriv->dm.dynamic_txhighpower_lvl =
+ TXHIGHPWRLEVEL_LEVEL2;
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ ("TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x0)\n"));
+ } else
+ if ((undecorated_smoothed_pwdb <
+ (TX_POWER_NEAR_FIELD_THRESH_LVL2 - 3))
+ && (undecorated_smoothed_pwdb >=
+ TX_POWER_NEAR_FIELD_THRESH_LVL1)) {
+
+ rtlpriv->dm.dynamic_txhighpower_lvl =
+ TXHIGHPWRLEVEL_LEVEL1;
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ ("TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x10)\n"));
+ } else if (undecorated_smoothed_pwdb <
+ (TX_POWER_NEAR_FIELD_THRESH_LVL1 - 5)) {
+ rtlpriv->dm.dynamic_txhighpower_lvl =
+ TXHIGHPWRLEVEL_NORMAL;
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ ("TXHIGHPWRLEVEL_NORMAL\n"));
+ }
+ }
+ if ((rtlpriv->dm.dynamic_txhighpower_lvl != rtlpriv->dm.last_dtp_lvl)) {
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ ("PHY_SetTxPowerLevel8192S() Channel = %d\n",
+ rtlphy->current_channel));
+ rtl92d_phy_set_txpower_level(hw, rtlphy->current_channel);
+ }
+ rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl;
+}
+
+static void rtl92d_dm_pwdb_monitor(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ /* AP & ADHOC & MESH will return tmp */
+ if (rtlpriv->mac80211.opmode != NL80211_IFTYPE_STATION)
+ return;
+ /* Indicate Rx signal strength to FW. */
+ if (rtlpriv->dm.useramask) {
+ u32 temp = rtlpriv->dm.undecorated_smoothed_pwdb;
+
+ temp <<= 16;
+ temp |= 0x100;
+ /* fw v12 cmdid 5:use max macid ,for nic ,
+ * default macid is 0 ,max macid is 1 */
+ rtl92d_fill_h2c_cmd(hw, H2C_RSSI_REPORT, 3, (u8 *) (&temp));
+ } else {
+ rtl_write_byte(rtlpriv, 0x4fe,
+ (u8) rtlpriv->dm.undecorated_smoothed_pwdb);
+ }
+}
+
+void rtl92d_dm_init_edca_turbo(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->dm.current_turbo_edca = false;
+ rtlpriv->dm.is_any_nonbepkts = false;
+ rtlpriv->dm.is_cur_rdlstate = false;
+}
+
+static void rtl92d_dm_check_edca_turbo(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ static u64 last_txok_cnt;
+ static u64 last_rxok_cnt;
+ u64 cur_txok_cnt;
+ u64 cur_rxok_cnt;
+ u32 edca_be_ul = 0x5ea42b;
+ u32 edca_be_dl = 0x5ea42b;
+
+ if (mac->link_state != MAC80211_LINKED) {
+ rtlpriv->dm.current_turbo_edca = false;
+ goto exit;
+ }
+
+ /* Enable BEQ TxOP limit configuration in wireless G-mode. */
+ /* To check whether we shall force turn on TXOP configuration. */
+ if ((!rtlpriv->dm.disable_framebursting) &&
+ (rtlpriv->sec.pairwise_enc_algorithm == WEP40_ENCRYPTION ||
+ rtlpriv->sec.pairwise_enc_algorithm == WEP104_ENCRYPTION ||
+ rtlpriv->sec.pairwise_enc_algorithm == TKIP_ENCRYPTION)) {
+ /* Force TxOP limit to 0x005e for UL. */
+ if (!(edca_be_ul & 0xffff0000))
+ edca_be_ul |= 0x005e0000;
+ /* Force TxOP limit to 0x005e for DL. */
+ if (!(edca_be_dl & 0xffff0000))
+ edca_be_dl |= 0x005e0000;
+ }
+
+ if ((!rtlpriv->dm.is_any_nonbepkts) &&
+ (!rtlpriv->dm.disable_framebursting)) {
+ cur_txok_cnt = rtlpriv->stats.txbytesunicast - last_txok_cnt;
+ cur_rxok_cnt = rtlpriv->stats.rxbytesunicast - last_rxok_cnt;
+ if (cur_rxok_cnt > 4 * cur_txok_cnt) {
+ if (!rtlpriv->dm.is_cur_rdlstate ||
+ !rtlpriv->dm.current_turbo_edca) {
+ rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM,
+ edca_be_dl);
+ rtlpriv->dm.is_cur_rdlstate = true;
+ }
+ } else {
+ if (rtlpriv->dm.is_cur_rdlstate ||
+ !rtlpriv->dm.current_turbo_edca) {
+ rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM,
+ edca_be_ul);
+ rtlpriv->dm.is_cur_rdlstate = false;
+ }
+ }
+ rtlpriv->dm.current_turbo_edca = true;
+ } else {
+ if (rtlpriv->dm.current_turbo_edca) {
+ u8 tmp = AC0_BE;
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM,
+ (u8 *) (&tmp));
+ rtlpriv->dm.current_turbo_edca = false;
+ }
+ }
+
+exit:
+ rtlpriv->dm.is_any_nonbepkts = false;
+ last_txok_cnt = rtlpriv->stats.txbytesunicast;
+ last_rxok_cnt = rtlpriv->stats.rxbytesunicast;
+}
+
+static void rtl92d_dm_rxgain_tracking_thermalmeter(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 index_mapping[RX_INDEX_MAPPING_NUM] = {
+ 0x0f, 0x0f, 0x0d, 0x0c, 0x0b,
+ 0x0a, 0x09, 0x08, 0x07, 0x06,
+ 0x05, 0x04, 0x04, 0x03, 0x02
+ };
+ int i;
+ u32 u4tmp;
+
+ u4tmp = (index_mapping[(rtlpriv->efuse.eeprom_thermalmeter -
+ rtlpriv->dm.thermalvalue_rxgain)]) << 12;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("===> Rx Gain %x\n", u4tmp));
+ for (i = RF90_PATH_A; i < rtlpriv->phy.num_total_rfpath; i++)
+ rtl_set_rfreg(hw, i, 0x3C, BRFREGOFFSETMASK,
+ (rtlpriv->phy.reg_rf3c[i] & (~(0xF000))) | u4tmp);
+}
+
+static void rtl92d_bandtype_2_4G(struct ieee80211_hw *hw, long *temp_cckg,
+ u8 *cck_index_old)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ int i;
+ unsigned long flag = 0;
+ long temp_cck;
+
+ /* Query CCK default setting From 0xa24 */
+ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
+ temp_cck = rtl_get_bbreg(hw, RCCK0_TXFILTER2,
+ BMASKDWORD) & BMASKCCK;
+ rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
+ for (i = 0; i < CCK_TABLE_LENGTH; i++) {
+ if (rtlpriv->dm.cck_inch14) {
+ if (!memcmp((void *)&temp_cck,
+ (void *)&cckswing_table_ch14[i][2], 4)) {
+ *cck_index_old = (u8) i;
+ RT_TRACE(rtlpriv,
+ COMP_POWER_TRACKING,
+ DBG_LOUD,
+ ("Initial reg0x%x = 0x%lx, "
+ "cck_index=0x%x, ch 14 %d\n",
+ RCCK0_TXFILTER2,
+ temp_cck, *cck_index_old,
+ rtlpriv->dm.cck_inch14));
+ break;
+ }
+ } else {
+ if (!memcmp((void *) &temp_cck,
+ &cckswing_table_ch1ch13[i][2], 4)) {
+ *cck_index_old = (u8) i;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING,
+ DBG_LOUD,
+ ("Initial reg0x%x = 0x%lx, "
+ "cck_index = 0x%x, ch14 %d\n",
+ RCCK0_TXFILTER2,
+ temp_cck, *cck_index_old,
+ rtlpriv->dm.cck_inch14));
+ break;
+ }
+ }
+ }
+ *temp_cckg = temp_cck;
+}
+
+static void rtl92d_bandtype_5G(struct rtl_hal *rtlhal, u8 *ofdm_index,
+ bool *internal_pa, u8 thermalvalue, u8 delta,
+ u8 rf, struct rtl_efuse *rtlefuse,
+ struct rtl_priv *rtlpriv, struct rtl_phy *rtlphy,
+ u8 index_mapping[5][INDEX_MAPPING_NUM],
+ u8 index_mapping_pa[8][INDEX_MAPPING_NUM])
+{
+ int i;
+ u8 index;
+ u8 offset = 0;
+
+ for (i = 0; i < rf; i++) {
+ if (rtlhal->macphymode == DUALMAC_DUALPHY &&
+ rtlhal->interfaceindex == 1) /* MAC 1 5G */
+ *internal_pa = rtlefuse->internal_pa_5g[1];
+ else
+ *internal_pa = rtlefuse->internal_pa_5g[i];
+ if (*internal_pa) {
+ if (rtlhal->interfaceindex == 1 || i == rf)
+ offset = 4;
+ else
+ offset = 0;
+ if (rtlphy->current_channel >= 100 &&
+ rtlphy->current_channel <= 165)
+ offset += 2;
+ } else {
+ if (rtlhal->interfaceindex == 1 || i == rf)
+ offset = 2;
+ else
+ offset = 0;
+ }
+ if (thermalvalue > rtlefuse->eeprom_thermalmeter)
+ offset++;
+ if (*internal_pa) {
+ if (delta > INDEX_MAPPING_NUM - 1)
+ index = index_mapping_pa[offset]
+ [INDEX_MAPPING_NUM - 1];
+ else
+ index =
+ index_mapping_pa[offset][delta];
+ } else {
+ if (delta > INDEX_MAPPING_NUM - 1)
+ index =
+ index_mapping[offset][INDEX_MAPPING_NUM - 1];
+ else
+ index = index_mapping[offset][delta];
+ }
+ if (thermalvalue > rtlefuse->eeprom_thermalmeter) {
+ if (*internal_pa && thermalvalue > 0x12) {
+ ofdm_index[i] = rtlpriv->dm.ofdm_index[i] -
+ ((delta / 2) * 3 + (delta % 2));
+ } else {
+ ofdm_index[i] -= index;
+ }
+ } else {
+ ofdm_index[i] += index;
+ }
+ }
+}
+
+static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
+ struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u8 thermalvalue, delta, delta_lck, delta_iqk, delta_rxgain;
+ u8 offset, thermalvalue_avg_count = 0;
+ u32 thermalvalue_avg = 0;
+ bool internal_pa = false;
+ long ele_a = 0, ele_d, temp_cck, val_x, value32;
+ long val_y, ele_c = 0;
+ u8 ofdm_index[2];
+ u8 cck_index = 0;
+ u8 ofdm_index_old[2];
+ u8 cck_index_old = 0;
+ u8 index;
+ int i;
+ bool is2t = IS_92D_SINGLEPHY(rtlhal->version);
+ u8 ofdm_min_index = 6, ofdm_min_index_internal_pa = 3, rf;
+ u8 indexforchannel =
+ rtl92d_get_rightchnlplace_for_iqk(rtlphy->current_channel);
+ u8 index_mapping[5][INDEX_MAPPING_NUM] = {
+ /* 5G, path A/MAC 0, decrease power */
+ {0, 1, 3, 6, 8, 9, 11, 13, 14, 16, 17, 18, 18},
+ /* 5G, path A/MAC 0, increase power */
+ {0, 2, 4, 5, 7, 10, 12, 14, 16, 18, 18, 18, 18},
+ /* 5G, path B/MAC 1, decrease power */
+ {0, 2, 3, 6, 8, 9, 11, 13, 14, 16, 17, 18, 18},
+ /* 5G, path B/MAC 1, increase power */
+ {0, 2, 4, 5, 7, 10, 13, 16, 16, 18, 18, 18, 18},
+ /* 2.4G, for decreas power */
+ {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10},
+ };
+ u8 index_mapping_internal_pa[8][INDEX_MAPPING_NUM] = {
+ /* 5G, path A/MAC 0, ch36-64, decrease power */
+ {0, 1, 2, 4, 6, 7, 9, 11, 12, 14, 15, 16, 16},
+ /* 5G, path A/MAC 0, ch36-64, increase power */
+ {0, 2, 4, 5, 7, 10, 12, 14, 16, 18, 18, 18, 18},
+ /* 5G, path A/MAC 0, ch100-165, decrease power */
+ {0, 1, 2, 3, 5, 6, 8, 10, 11, 13, 14, 15, 15},
+ /* 5G, path A/MAC 0, ch100-165, increase power */
+ {0, 2, 4, 5, 7, 10, 12, 14, 16, 18, 18, 18, 18},
+ /* 5G, path B/MAC 1, ch36-64, decrease power */
+ {0, 1, 2, 4, 6, 7, 9, 11, 12, 14, 15, 16, 16},
+ /* 5G, path B/MAC 1, ch36-64, increase power */
+ {0, 2, 4, 5, 7, 10, 13, 16, 16, 18, 18, 18, 18},
+ /* 5G, path B/MAC 1, ch100-165, decrease power */
+ {0, 1, 2, 3, 5, 6, 8, 9, 10, 12, 13, 14, 14},
+ /* 5G, path B/MAC 1, ch100-165, increase power */
+ {0, 2, 4, 5, 7, 10, 13, 16, 16, 18, 18, 18, 18},
+ };
+
+ rtlpriv->dm.txpower_trackinginit = true;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, ("\n"));
+ thermalvalue = (u8) rtl_get_rfreg(hw, RF90_PATH_A, RF_T_METER, 0xf800);
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("Readback Thermal Meter = 0x%x pre thermal meter 0x%x "
+ "eeprom_thermalmeter 0x%x\n", thermalvalue,
+ rtlpriv->dm.thermalvalue, rtlefuse->eeprom_thermalmeter));
+ rtl92d_phy_ap_calibrate(hw, (thermalvalue -
+ rtlefuse->eeprom_thermalmeter));
+ if (is2t)
+ rf = 2;
+ else
+ rf = 1;
+ if (thermalvalue) {
+ ele_d = rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE,
+ BMASKDWORD) & BMASKOFDM_D;
+ for (i = 0; i < OFDM_TABLE_SIZE_92D; i++) {
+ if (ele_d == (ofdmswing_table[i] & BMASKOFDM_D)) {
+ ofdm_index_old[0] = (u8) i;
+
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("Initial pathA ele_d reg0x%x = 0x%lx,"
+ " ofdm_index=0x%x\n",
+ ROFDM0_XATxIQIMBALANCE,
+ ele_d, ofdm_index_old[0]));
+ break;
+ }
+ }
+ if (is2t) {
+ ele_d = rtl_get_bbreg(hw, ROFDM0_XBTxIQIMBALANCE,
+ BMASKDWORD) & BMASKOFDM_D;
+ for (i = 0; i < OFDM_TABLE_SIZE_92D; i++) {
+ if (ele_d ==
+ (ofdmswing_table[i] & BMASKOFDM_D)) {
+ ofdm_index_old[1] = (u8) i;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING,
+ DBG_LOUD,
+ ("Initial pathB ele_d reg "
+ "0x%x = 0x%lx, ofdm_index "
+ "= 0x%x\n",
+ ROFDM0_XBTxIQIMBALANCE, ele_d,
+ ofdm_index_old[1]));
+ break;
+ }
+ }
+ }
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ rtl92d_bandtype_2_4G(hw, &temp_cck, &cck_index_old);
+ } else {
+ temp_cck = 0x090e1317;
+ cck_index_old = 12;
+ }
+
+ if (!rtlpriv->dm.thermalvalue) {
+ rtlpriv->dm.thermalvalue =
+ rtlefuse->eeprom_thermalmeter;
+ rtlpriv->dm.thermalvalue_lck = thermalvalue;
+ rtlpriv->dm.thermalvalue_iqk = thermalvalue;
+ rtlpriv->dm.thermalvalue_rxgain =
+ rtlefuse->eeprom_thermalmeter;
+ for (i = 0; i < rf; i++)
+ rtlpriv->dm.ofdm_index[i] = ofdm_index_old[i];
+ rtlpriv->dm.cck_index = cck_index_old;
+ }
+ if (rtlhal->reloadtxpowerindex) {
+ for (i = 0; i < rf; i++)
+ rtlpriv->dm.ofdm_index[i] = ofdm_index_old[i];
+ rtlpriv->dm.cck_index = cck_index_old;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("reload ofdm index for band switch\n"));
+ }
+ rtlpriv->dm.thermalvalue_avg
+ [rtlpriv->dm.thermalvalue_avg_index] = thermalvalue;
+ rtlpriv->dm.thermalvalue_avg_index++;
+ if (rtlpriv->dm.thermalvalue_avg_index == AVG_THERMAL_NUM)
+ rtlpriv->dm.thermalvalue_avg_index = 0;
+ for (i = 0; i < AVG_THERMAL_NUM; i++) {
+ if (rtlpriv->dm.thermalvalue_avg[i]) {
+ thermalvalue_avg +=
+ rtlpriv->dm.thermalvalue_avg[i];
+ thermalvalue_avg_count++;
+ }
+ }
+ if (thermalvalue_avg_count)
+ thermalvalue = (u8) (thermalvalue_avg /
+ thermalvalue_avg_count);
+ if (rtlhal->reloadtxpowerindex) {
+ delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ?
+ (thermalvalue - rtlefuse->eeprom_thermalmeter) :
+ (rtlefuse->eeprom_thermalmeter - thermalvalue);
+ rtlhal->reloadtxpowerindex = false;
+ rtlpriv->dm.done_txpower = false;
+ } else if (rtlpriv->dm.done_txpower) {
+ delta = (thermalvalue > rtlpriv->dm.thermalvalue) ?
+ (thermalvalue - rtlpriv->dm.thermalvalue) :
+ (rtlpriv->dm.thermalvalue - thermalvalue);
+ } else {
+ delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ?
+ (thermalvalue - rtlefuse->eeprom_thermalmeter) :
+ (rtlefuse->eeprom_thermalmeter - thermalvalue);
+ }
+ delta_lck = (thermalvalue > rtlpriv->dm.thermalvalue_lck) ?
+ (thermalvalue - rtlpriv->dm.thermalvalue_lck) :
+ (rtlpriv->dm.thermalvalue_lck - thermalvalue);
+ delta_iqk = (thermalvalue > rtlpriv->dm.thermalvalue_iqk) ?
+ (thermalvalue - rtlpriv->dm.thermalvalue_iqk) :
+ (rtlpriv->dm.thermalvalue_iqk - thermalvalue);
+ delta_rxgain =
+ (thermalvalue > rtlpriv->dm.thermalvalue_rxgain) ?
+ (thermalvalue - rtlpriv->dm.thermalvalue_rxgain) :
+ (rtlpriv->dm.thermalvalue_rxgain - thermalvalue);
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("Readback Thermal Meter = 0x%x pre thermal meter 0x%x"
+ " eeprom_thermalmeter 0x%x delta 0x%x "
+ "delta_lck 0x%x delta_iqk 0x%x\n",
+ thermalvalue, rtlpriv->dm.thermalvalue,
+ rtlefuse->eeprom_thermalmeter, delta, delta_lck,
+ delta_iqk));
+ if ((delta_lck > rtlefuse->delta_lck) &&
+ (rtlefuse->delta_lck != 0)) {
+ rtlpriv->dm.thermalvalue_lck = thermalvalue;
+ rtl92d_phy_lc_calibrate(hw);
+ }
+ if (delta > 0 && rtlpriv->dm.txpower_track_control) {
+ rtlpriv->dm.done_txpower = true;
+ delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ?
+ (thermalvalue - rtlefuse->eeprom_thermalmeter) :
+ (rtlefuse->eeprom_thermalmeter - thermalvalue);
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ offset = 4;
+ if (delta > INDEX_MAPPING_NUM - 1)
+ index = index_mapping[offset]
+ [INDEX_MAPPING_NUM - 1];
+ else
+ index = index_mapping[offset][delta];
+ if (thermalvalue > rtlpriv->dm.thermalvalue) {
+ for (i = 0; i < rf; i++)
+ ofdm_index[i] -= delta;
+ cck_index -= delta;
+ } else {
+ for (i = 0; i < rf; i++)
+ ofdm_index[i] += index;
+ cck_index += index;
+ }
+ } else if (rtlhal->current_bandtype == BAND_ON_5G) {
+ rtl92d_bandtype_5G(rtlhal, ofdm_index,
+ &internal_pa, thermalvalue,
+ delta, rf, rtlefuse, rtlpriv,
+ rtlphy, index_mapping,
+ index_mapping_internal_pa);
+ }
+ if (is2t) {
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("temp OFDM_A_index=0x%x, OFDM_B_index"
+ " = 0x%x,cck_index=0x%x\n",
+ rtlpriv->dm.ofdm_index[0],
+ rtlpriv->dm.ofdm_index[1],
+ rtlpriv->dm.cck_index));
+ } else {
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("temp OFDM_A_index=0x%x,cck_index = "
+ "0x%x\n",
+ rtlpriv->dm.ofdm_index[0],
+ rtlpriv->dm.cck_index));
+ }
+ for (i = 0; i < rf; i++) {
+ if (ofdm_index[i] > OFDM_TABLE_SIZE_92D - 1)
+ ofdm_index[i] = OFDM_TABLE_SIZE_92D - 1;
+ else if (ofdm_index[i] < ofdm_min_index)
+ ofdm_index[i] = ofdm_min_index;
+ }
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ if (cck_index > CCK_TABLE_SIZE - 1) {
+ cck_index = CCK_TABLE_SIZE - 1;
+ } else if (internal_pa ||
+ rtlhal->current_bandtype ==
+ BAND_ON_2_4G) {
+ if (ofdm_index[i] <
+ ofdm_min_index_internal_pa)
+ ofdm_index[i] =
+ ofdm_min_index_internal_pa;
+ } else if (cck_index < 0) {
+ cck_index = 0;
+ }
+ }
+ if (is2t) {
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("new OFDM_A_index=0x%x, OFDM_B_index "
+ "= 0x%x, cck_index=0x%x\n",
+ ofdm_index[0], ofdm_index[1],
+ cck_index));
+ } else {
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("new OFDM_A_index=0x%x,cck_index = "
+ "0x%x\n",
+ ofdm_index[0], cck_index));
+ }
+ ele_d = (ofdmswing_table[(u8) ofdm_index[0]] &
+ 0xFFC00000) >> 22;
+ val_x = rtlphy->iqk_matrix_regsetting
+ [indexforchannel].value[0][0];
+ val_y = rtlphy->iqk_matrix_regsetting
+ [indexforchannel].value[0][1];
+ if (val_x != 0) {
+ if ((val_x & 0x00000200) != 0)
+ val_x = val_x | 0xFFFFFC00;
+ ele_a =
+ ((val_x * ele_d) >> 8) & 0x000003FF;
+
+ /* new element C = element D x Y */
+ if ((val_y & 0x00000200) != 0)
+ val_y = val_y | 0xFFFFFC00;
+ ele_c = ((val_y * ele_d) >> 8) & 0x000003FF;
+
+ /* wirte new elements A, C, D to regC80 and
+ * regC94, element B is always 0 */
+ value32 = (ele_d << 22) | ((ele_c & 0x3F) <<
+ 16) | ele_a;
+ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE,
+ BMASKDWORD, value32);
+
+ value32 = (ele_c & 0x000003C0) >> 6;
+ rtl_set_bbreg(hw, ROFDM0_XCTxAFE, BMASKH4BITS,
+ value32);
+
+ value32 = ((val_x * ele_d) >> 7) & 0x01;
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(24),
+ value32);
+
+ } else {
+ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE,
+ BMASKDWORD,
+ ofdmswing_table
+ [(u8)ofdm_index[0]]);
+ rtl_set_bbreg(hw, ROFDM0_XCTxAFE, BMASKH4BITS,
+ 0x00);
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD,
+ BIT(24), 0x00);
+ }
+
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("TxPwrTracking for interface %d path A: X ="
+ " 0x%lx, Y = 0x%lx ele_A = 0x%lx ele_C = "
+ "0x%lx ele_D = 0x%lx 0xe94 = 0x%lx 0xe9c = "
+ "0x%lx\n", rtlhal->interfaceindex,
+ val_x, val_y, ele_a, ele_c, ele_d,
+ val_x, val_y));
+
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ /* Adjust CCK according to IQK result */
+ if (!rtlpriv->dm.cck_inch14) {
+ rtl_write_byte(rtlpriv, 0xa22,
+ cckswing_table_ch1ch13
+ [(u8)cck_index][0]);
+ rtl_write_byte(rtlpriv, 0xa23,
+ cckswing_table_ch1ch13
+ [(u8)cck_index][1]);
+ rtl_write_byte(rtlpriv, 0xa24,
+ cckswing_table_ch1ch13
+ [(u8)cck_index][2]);
+ rtl_write_byte(rtlpriv, 0xa25,
+ cckswing_table_ch1ch13
+ [(u8)cck_index][3]);
+ rtl_write_byte(rtlpriv, 0xa26,
+ cckswing_table_ch1ch13
+ [(u8)cck_index][4]);
+ rtl_write_byte(rtlpriv, 0xa27,
+ cckswing_table_ch1ch13
+ [(u8)cck_index][5]);
+ rtl_write_byte(rtlpriv, 0xa28,
+ cckswing_table_ch1ch13
+ [(u8)cck_index][6]);
+ rtl_write_byte(rtlpriv, 0xa29,
+ cckswing_table_ch1ch13
+ [(u8)cck_index][7]);
+ } else {
+ rtl_write_byte(rtlpriv, 0xa22,
+ cckswing_table_ch14
+ [(u8)cck_index][0]);
+ rtl_write_byte(rtlpriv, 0xa23,
+ cckswing_table_ch14
+ [(u8)cck_index][1]);
+ rtl_write_byte(rtlpriv, 0xa24,
+ cckswing_table_ch14
+ [(u8)cck_index][2]);
+ rtl_write_byte(rtlpriv, 0xa25,
+ cckswing_table_ch14
+ [(u8)cck_index][3]);
+ rtl_write_byte(rtlpriv, 0xa26,
+ cckswing_table_ch14
+ [(u8)cck_index][4]);
+ rtl_write_byte(rtlpriv, 0xa27,
+ cckswing_table_ch14
+ [(u8)cck_index][5]);
+ rtl_write_byte(rtlpriv, 0xa28,
+ cckswing_table_ch14
+ [(u8)cck_index][6]);
+ rtl_write_byte(rtlpriv, 0xa29,
+ cckswing_table_ch14
+ [(u8)cck_index][7]);
+ }
+ }
+ if (is2t) {
+ ele_d = (ofdmswing_table[(u8) ofdm_index[1]] &
+ 0xFFC00000) >> 22;
+ val_x = rtlphy->iqk_matrix_regsetting
+ [indexforchannel].value[0][4];
+ val_y = rtlphy->iqk_matrix_regsetting
+ [indexforchannel].value[0][5];
+ if (val_x != 0) {
+ if ((val_x & 0x00000200) != 0)
+ /* consider minus */
+ val_x = val_x | 0xFFFFFC00;
+ ele_a = ((val_x * ele_d) >> 8) &
+ 0x000003FF;
+ /* new element C = element D x Y */
+ if ((val_y & 0x00000200) != 0)
+ val_y =
+ val_y | 0xFFFFFC00;
+ ele_c =
+ ((val_y *
+ ele_d) >> 8) & 0x00003FF;
+ /* write new elements A, C, D to regC88
+ * and regC9C, element B is always 0
+ */
+ value32 = (ele_d << 22) |
+ ((ele_c & 0x3F) << 16) |
+ ele_a;
+ rtl_set_bbreg(hw,
+ ROFDM0_XBTxIQIMBALANCE,
+ BMASKDWORD, value32);
+ value32 = (ele_c & 0x000003C0) >> 6;
+ rtl_set_bbreg(hw, ROFDM0_XDTxAFE,
+ BMASKH4BITS, value32);
+ value32 = ((val_x * ele_d) >> 7) & 0x01;
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD,
+ BIT(28), value32);
+ } else {
+ rtl_set_bbreg(hw,
+ ROFDM0_XBTxIQIMBALANCE,
+ BMASKDWORD,
+ ofdmswing_table
+ [(u8) ofdm_index[1]]);
+ rtl_set_bbreg(hw, ROFDM0_XDTxAFE,
+ BMASKH4BITS, 0x00);
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD,
+ BIT(28), 0x00);
+ }
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("TxPwrTracking path B: X = 0x%lx, "
+ "Y = 0x%lx ele_A = 0x%lx ele_C = 0x"
+ "%lx ele_D = 0x%lx 0xeb4 = 0x%lx "
+ "0xebc = 0x%lx\n",
+ val_x, val_y, ele_a, ele_c,
+ ele_d, val_x, val_y));
+ }
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("TxPwrTracking 0xc80 = 0x%x, 0xc94 = "
+ "0x%x RF 0x24 = 0x%x\n",
+ rtl_get_bbreg(hw, 0xc80, BMASKDWORD),
+ rtl_get_bbreg(hw, 0xc94, BMASKDWORD),
+ rtl_get_rfreg(hw, RF90_PATH_A, 0x24,
+ BRFREGOFFSETMASK)));
+ }
+ if ((delta_iqk > rtlefuse->delta_iqk) &&
+ (rtlefuse->delta_iqk != 0)) {
+ rtl92d_phy_reset_iqk_result(hw);
+ rtlpriv->dm.thermalvalue_iqk = thermalvalue;
+ rtl92d_phy_iq_calibrate(hw);
+ }
+ if (delta_rxgain > 0 && rtlhal->current_bandtype == BAND_ON_5G
+ && thermalvalue <= rtlefuse->eeprom_thermalmeter) {
+ rtlpriv->dm.thermalvalue_rxgain = thermalvalue;
+ rtl92d_dm_rxgain_tracking_thermalmeter(hw);
+ }
+ if (rtlpriv->dm.txpower_track_control)
+ rtlpriv->dm.thermalvalue = thermalvalue;
+ }
+
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, ("<===\n"));
+}
+
+static void rtl92d_dm_initialize_txpower_tracking(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->dm.txpower_tracking = true;
+ rtlpriv->dm.txpower_trackinginit = false;
+ rtlpriv->dm.txpower_track_control = true;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("pMgntInfo->txpower_tracking = %d\n",
+ rtlpriv->dm.txpower_tracking));
+}
+
+void rtl92d_dm_check_txpower_tracking_thermal_meter(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ static u8 tm_trigger;
+
+ if (!rtlpriv->dm.txpower_tracking)
+ return;
+
+ if (!tm_trigger) {
+ rtl_set_rfreg(hw, RF90_PATH_A, RF_T_METER, BIT(17) |
+ BIT(16), 0x03);
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("Trigger 92S Thermal Meter!!\n"));
+ tm_trigger = 1;
+ return;
+ } else {
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ ("Schedule TxPowerTracking direct call!!\n"));
+ rtl92d_dm_txpower_tracking_callback_thermalmeter(hw);
+ tm_trigger = 0;
+ }
+}
+
+void rtl92d_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rate_adaptive *ra = &(rtlpriv->ra);
+
+ ra->ratr_state = DM_RATR_STA_INIT;
+ ra->pre_ratr_state = DM_RATR_STA_INIT;
+ if (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER)
+ rtlpriv->dm.useramask = true;
+ else
+ rtlpriv->dm.useramask = false;
+}
+
+void rtl92d_dm_init(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER;
+ rtl92d_dm_diginit(hw);
+ rtl92d_dm_init_dynamic_txpower(hw);
+ rtl92d_dm_init_edca_turbo(hw);
+ rtl92d_dm_init_rate_adaptive_mask(hw);
+ rtl92d_dm_initialize_txpower_tracking(hw);
+}
+
+void rtl92d_dm_watchdog(struct ieee80211_hw *hw)
+{
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ bool fw_current_inpsmode = false;
+ bool fwps_awake = true;
+
+ /* 1. RF is OFF. (No need to do DM.)
+ * 2. Fw is under power saving mode for FwLPS.
+ * (Prevent from SW/FW I/O racing.)
+ * 3. IPS workitem is scheduled. (Prevent from IPS sequence
+ * to be swapped with DM.
+ * 4. RFChangeInProgress is TRUE.
+ * (Prevent from broken by IPS/HW/SW Rf off.) */
+
+ if ((ppsc->rfpwr_state == ERFON) && ((!fw_current_inpsmode) &&
+ fwps_awake) && (!ppsc->rfchange_inprogress)) {
+ rtl92d_dm_pwdb_monitor(hw);
+ rtl92d_dm_false_alarm_counter_statistics(hw);
+ rtl92d_dm_find_minimum_rssi(hw);
+ rtl92d_dm_dig(hw);
+ /* rtl92d_dm_dynamic_bb_powersaving(hw); */
+ rtl92d_dm_dynamic_txpower(hw);
+ /* rtl92d_dm_check_txpower_tracking_thermal_meter(hw); */
+ /* rtl92d_dm_refresh_rate_adaptive_mask(hw); */
+ /* rtl92d_dm_interrupt_migration(hw); */
+ rtl92d_dm_check_edca_turbo(hw);
+ }
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.h b/drivers/net/wireless/rtlwifi/rtl8192de/dm.h
new file mode 100644
index 000000000000..69354657f0f5
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.h
@@ -0,0 +1,212 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92C_DM_H__
+#define __RTL92C_DM_H__
+
+#define HAL_DM_DIG_DISABLE BIT(0)
+#define HAL_DM_HIPWR_DISABLE BIT(1)
+
+#define OFDM_TABLE_LENGTH 37
+#define OFDM_TABLE_SIZE_92D 43
+#define CCK_TABLE_LENGTH 33
+
+#define CCK_TABLE_SIZE 33
+
+#define BW_AUTO_SWITCH_HIGH_LOW 25
+#define BW_AUTO_SWITCH_LOW_HIGH 30
+
+#define DM_DIG_THRESH_HIGH 40
+#define DM_DIG_THRESH_LOW 35
+
+#define DM_FALSEALARM_THRESH_LOW 400
+#define DM_FALSEALARM_THRESH_HIGH 1000
+
+#define DM_DIG_MAX 0x3e
+#define DM_DIG_MIN 0x1c
+
+#define DM_DIG_FA_UPPER 0x32
+#define DM_DIG_FA_LOWER 0x20
+#define DM_DIG_FA_TH0 0x100
+#define DM_DIG_FA_TH1 0x400
+#define DM_DIG_FA_TH2 0x600
+
+#define DM_DIG_BACKOFF_MAX 12
+#define DM_DIG_BACKOFF_MIN -4
+#define DM_DIG_BACKOFF_DEFAULT 10
+
+#define RXPATHSELECTION_SS_TH_lOW 30
+#define RXPATHSELECTION_DIFF_TH 18
+
+#define DM_RATR_STA_INIT 0
+#define DM_RATR_STA_HIGH 1
+#define DM_RATR_STA_MIDDLE 2
+#define DM_RATR_STA_LOW 3
+
+#define CTS2SELF_THVAL 30
+#define REGC38_TH 20
+
+#define WAIOTTHVAL 25
+
+#define TXHIGHPWRLEVEL_NORMAL 0
+#define TXHIGHPWRLEVEL_LEVEL1 1
+#define TXHIGHPWRLEVEL_LEVEL2 2
+#define TXHIGHPWRLEVEL_BT1 3
+#define TXHIGHPWRLEVEL_BT2 4
+
+#define DM_TYPE_BYFW 0
+#define DM_TYPE_BYDRIVER 1
+
+#define TX_POWER_NEAR_FIELD_THRESH_LVL2 74
+#define TX_POWER_NEAR_FIELD_THRESH_LVL1 67
+#define INDEX_MAPPING_NUM 13
+
+struct ps_t {
+ u8 pre_ccastate;
+ u8 cur_ccasate;
+
+ u8 pre_rfstate;
+ u8 cur_rfstate;
+
+ long rssi_val_min;
+};
+
+struct dig_t {
+ u8 dig_enable_flag;
+ u8 dig_ext_port_stage;
+
+ u32 rssi_lowthresh;
+ u32 rssi_highthresh;
+
+ u32 fa_lowthresh;
+ u32 fa_highthresh;
+
+ u8 cursta_connectctate;
+ u8 presta_connectstate;
+ u8 curmultista_connectstate;
+
+ u8 pre_igvalue;
+ u8 cur_igvalue;
+
+ char backoff_val;
+ char backoff_val_range_max;
+ char backoff_val_range_min;
+ u8 rx_gain_range_max;
+ u8 rx_gain_range_min;
+ u8 min_undecorated_pwdb_for_dm;
+ long last_min_undecorated_pwdb_for_dm;
+
+ u8 pre_cck_pd_state;
+ u8 cur_cck_pd_state;
+
+ u8 pre_cck_fa_state;
+ u8 cur_cck_fa_state;
+
+ u8 pre_ccastate;
+ u8 cur_ccasate;
+
+ u8 large_fa_hit;
+ u8 forbidden_igi;
+ u32 recover_cnt;
+};
+
+struct swat {
+ u8 failure_cnt;
+ u8 try_flag;
+ u8 stop_trying;
+ long pre_rssi;
+ long trying_threshold;
+ u8 cur_antenna;
+ u8 pre_antenna;
+};
+
+enum tag_dynamic_init_gain_operation_type_definition {
+ DIG_TYPE_THRESH_HIGH = 0,
+ DIG_TYPE_THRESH_LOW = 1,
+ DIG_TYPE_BACKOFF = 2,
+ DIG_TYPE_RX_GAIN_MIN = 3,
+ DIG_TYPE_RX_GAIN_MAX = 4,
+ DIG_TYPE_ENABLE = 5,
+ DIG_TYPE_DISABLE = 6,
+ DIG_OP_TYPE_MAX
+};
+
+enum tag_cck_packet_detection_threshold_type_definition {
+ CCK_PD_STAGE_LOWRSSI = 0,
+ CCK_PD_STAGE_HIGHRSSI = 1,
+ CCK_FA_STAGE_LOW = 2,
+ CCK_FA_STAGE_HIGH = 3,
+ CCK_PD_STAGE_MAX = 4,
+};
+
+enum dm_1r_cca {
+ CCA_1R = 0,
+ CCA_2R = 1,
+ CCA_MAX = 2,
+};
+
+enum dm_rf {
+ RF_SAVE = 0,
+ RF_NORMAL = 1,
+ RF_MAX = 2,
+};
+
+enum dm_sw_ant_switch {
+ ANS_ANTENNA_B = 1,
+ ANS_ANTENNA_A = 2,
+ ANS_ANTENNA_MAX = 3,
+};
+
+enum dm_dig_ext_port_alg {
+ DIG_EXT_PORT_STAGE_0 = 0,
+ DIG_EXT_PORT_STAGE_1 = 1,
+ DIG_EXT_PORT_STAGE_2 = 2,
+ DIG_EXT_PORT_STAGE_3 = 3,
+ DIG_EXT_PORT_STAGE_MAX = 4,
+};
+
+enum dm_dig_connect {
+ DIG_STA_DISCONNECT = 0,
+ DIG_STA_CONNECT = 1,
+ DIG_STA_BEFORE_CONNECT = 2,
+ DIG_MULTISTA_DISCONNECT = 3,
+ DIG_MULTISTA_CONNECT = 4,
+ DIG_CONNECT_MAX
+};
+
+extern struct dig_t de_digtable;
+
+void rtl92d_dm_init(struct ieee80211_hw *hw);
+void rtl92d_dm_watchdog(struct ieee80211_hw *hw);
+void rtl92d_dm_init_edca_turbo(struct ieee80211_hw *hw);
+void rtl92d_dm_write_dig(struct ieee80211_hw *hw);
+void rtl92d_dm_check_txpower_tracking_thermal_meter(struct ieee80211_hw *hw);
+void rtl92d_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
new file mode 100644
index 000000000000..82f060bdbc0b
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
@@ -0,0 +1,790 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "../base.h"
+#include "reg.h"
+#include "def.h"
+#include "fw.h"
+#include "sw.h"
+
+static bool _rtl92d_is_fw_downloaded(struct rtl_priv *rtlpriv)
+{
+ return (rtl_read_dword(rtlpriv, REG_MCUFWDL) & MCUFWDL_RDY) ?
+ true : false;
+}
+
+static void _rtl92d_enable_fw_download(struct ieee80211_hw *hw, bool enable)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 tmp;
+
+ if (enable) {
+ tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp | 0x04);
+ tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
+ rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01);
+ tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2);
+ rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7);
+ } else {
+ tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
+ rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe);
+ /* Reserved for fw extension.
+ * 0x81[7] is used for mac0 status ,
+ * so don't write this reg here
+ * rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00);*/
+ }
+}
+
+static void _rtl92d_fw_block_write(struct ieee80211_hw *hw,
+ const u8 *buffer, u32 size)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 blocksize = sizeof(u32);
+ u8 *bufferptr = (u8 *) buffer;
+ u32 *pu4BytePtr = (u32 *) buffer;
+ u32 i, offset, blockCount, remainSize;
+
+ blockCount = size / blocksize;
+ remainSize = size % blocksize;
+ for (i = 0; i < blockCount; i++) {
+ offset = i * blocksize;
+ rtl_write_dword(rtlpriv, (FW_8192D_START_ADDRESS + offset),
+ *(pu4BytePtr + i));
+ }
+ if (remainSize) {
+ offset = blockCount * blocksize;
+ bufferptr += offset;
+ for (i = 0; i < remainSize; i++) {
+ rtl_write_byte(rtlpriv, (FW_8192D_START_ADDRESS +
+ offset + i), *(bufferptr + i));
+ }
+ }
+}
+
+static void _rtl92d_fw_page_write(struct ieee80211_hw *hw,
+ u32 page, const u8 *buffer, u32 size)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 value8;
+ u8 u8page = (u8) (page & 0x07);
+
+ value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;
+ rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
+ _rtl92d_fw_block_write(hw, buffer, size);
+}
+
+static void _rtl92d_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
+{
+ u32 fwlen = *pfwlen;
+ u8 remain = (u8) (fwlen % 4);
+
+ remain = (remain == 0) ? 0 : (4 - remain);
+ while (remain > 0) {
+ pfwbuf[fwlen] = 0;
+ fwlen++;
+ remain--;
+ }
+ *pfwlen = fwlen;
+}
+
+static void _rtl92d_write_fw(struct ieee80211_hw *hw,
+ enum version_8192d version, u8 *buffer, u32 size)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 *bufferPtr = (u8 *) buffer;
+ u32 pagenums, remainSize;
+ u32 page, offset;
+
+ RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, ("FW size is %d bytes,\n", size));
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192DE)
+ _rtl92d_fill_dummy(bufferPtr, &size);
+ pagenums = size / FW_8192D_PAGE_SIZE;
+ remainSize = size % FW_8192D_PAGE_SIZE;
+ if (pagenums > 8) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Page numbers should not greater then 8\n"));
+ }
+ for (page = 0; page < pagenums; page++) {
+ offset = page * FW_8192D_PAGE_SIZE;
+ _rtl92d_fw_page_write(hw, page, (bufferPtr + offset),
+ FW_8192D_PAGE_SIZE);
+ }
+ if (remainSize) {
+ offset = pagenums * FW_8192D_PAGE_SIZE;
+ page = pagenums;
+ _rtl92d_fw_page_write(hw, page, (bufferPtr + offset),
+ remainSize);
+ }
+}
+
+static int _rtl92d_fw_free_to_go(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 counter = 0;
+ u32 value32;
+
+ do {
+ value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
+ } while ((counter++ < FW_8192D_POLLING_TIMEOUT_COUNT) &&
+ (!(value32 & FWDL_ChkSum_rpt)));
+ if (counter >= FW_8192D_POLLING_TIMEOUT_COUNT) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("chksum report faill ! REG_MCUFWDL:0x%08x .\n",
+ value32));
+ return -EIO;
+ }
+ RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
+ ("Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32));
+ value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
+ value32 |= MCUFWDL_RDY;
+ rtl_write_dword(rtlpriv, REG_MCUFWDL, value32);
+ return 0;
+}
+
+void rtl92d_firmware_selfreset(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 u1b_tmp;
+ u8 delay = 100;
+
+ /* Set (REG_HMETFR + 3) to 0x20 is reset 8051 */
+ rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20);
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+ while (u1b_tmp & BIT(2)) {
+ delay--;
+ if (delay == 0)
+ break;
+ udelay(50);
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+ }
+ RT_ASSERT((delay > 0), ("8051 reset failed!\n"));
+ RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+ ("=====> 8051 reset success (%d) .\n", delay));
+}
+
+static int _rtl92d_fw_init(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u32 counter;
+
+ RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, ("FW already have download\n"));
+ /* polling for FW ready */
+ counter = 0;
+ do {
+ if (rtlhal->interfaceindex == 0) {
+ if (rtl_read_byte(rtlpriv, FW_MAC0_READY) &
+ MAC0_READY) {
+ RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+ ("Polling FW ready success!! "
+ "REG_MCUFWDL: 0x%x .\n",
+ rtl_read_byte(rtlpriv,
+ FW_MAC0_READY)));
+ return 0;
+ }
+ udelay(5);
+ } else {
+ if (rtl_read_byte(rtlpriv, FW_MAC1_READY) &
+ MAC1_READY) {
+ RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+ ("Polling FW ready success!! "
+ "REG_MCUFWDL: 0x%x .\n",
+ rtl_read_byte(rtlpriv,
+ FW_MAC1_READY)));
+ return 0;
+ }
+ udelay(5);
+ }
+ } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
+
+ if (rtlhal->interfaceindex == 0) {
+ RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+ ("Polling FW ready fail!! MAC0 FW init not ready: "
+ "0x%x .\n",
+ rtl_read_byte(rtlpriv, FW_MAC0_READY)));
+ } else {
+ RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+ ("Polling FW ready fail!! MAC1 FW init not ready: "
+ "0x%x .\n",
+ rtl_read_byte(rtlpriv, FW_MAC1_READY)));
+ }
+ RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+ ("Polling FW ready fail!! REG_MCUFWDL:0x%08ul .\n",
+ rtl_read_dword(rtlpriv, REG_MCUFWDL)));
+ return -1;
+}
+
+int rtl92d_download_fw(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 *pfwheader;
+ u8 *pfwdata;
+ u32 fwsize;
+ int err;
+ enum version_8192d version = rtlhal->version;
+ u8 value;
+ u32 count;
+ bool fw_downloaded = false, fwdl_in_process = false;
+ unsigned long flags;
+
+ if (!rtlhal->pfirmware)
+ return 1;
+ fwsize = rtlhal->fwsize;
+ pfwheader = (u8 *) rtlhal->pfirmware;
+ pfwdata = (u8 *) rtlhal->pfirmware;
+ rtlhal->fw_version = (u16) GET_FIRMWARE_HDR_VERSION(pfwheader);
+ rtlhal->fw_subversion = (u16) GET_FIRMWARE_HDR_SUB_VER(pfwheader);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, (" FirmwareVersion(%d),"
+ "FirmwareSubVersion(%d), Signature(%#x)\n",
+ rtlhal->fw_version, rtlhal->fw_subversion,
+ GET_FIRMWARE_HDR_SIGNATURE(pfwheader)));
+ if (IS_FW_HEADER_EXIST(pfwheader)) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("Shift 32 bytes for FW header!!\n"));
+ pfwdata = pfwdata + 32;
+ fwsize = fwsize - 32;
+ }
+
+ spin_lock_irqsave(&globalmutex_for_fwdownload, flags);
+ fw_downloaded = _rtl92d_is_fw_downloaded(rtlpriv);
+ if ((rtl_read_byte(rtlpriv, 0x1f) & BIT(5)) == BIT(5))
+ fwdl_in_process = true;
+ else
+ fwdl_in_process = false;
+ if (fw_downloaded) {
+ spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags);
+ goto exit;
+ } else if (fwdl_in_process) {
+ spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags);
+ for (count = 0; count < 5000; count++) {
+ udelay(500);
+ spin_lock_irqsave(&globalmutex_for_fwdownload, flags);
+ fw_downloaded = _rtl92d_is_fw_downloaded(rtlpriv);
+ if ((rtl_read_byte(rtlpriv, 0x1f) & BIT(5)) == BIT(5))
+ fwdl_in_process = true;
+ else
+ fwdl_in_process = false;
+ spin_unlock_irqrestore(&globalmutex_for_fwdownload,
+ flags);
+ if (fw_downloaded)
+ goto exit;
+ else if (!fwdl_in_process)
+ break;
+ else
+ RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+ ("Wait for another mac "
+ "download fw\n"));
+ }
+ spin_lock_irqsave(&globalmutex_for_fwdownload, flags);
+ value = rtl_read_byte(rtlpriv, 0x1f);
+ value |= BIT(5);
+ rtl_write_byte(rtlpriv, 0x1f, value);
+ spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags);
+ } else {
+ value = rtl_read_byte(rtlpriv, 0x1f);
+ value |= BIT(5);
+ rtl_write_byte(rtlpriv, 0x1f, value);
+ spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags);
+ }
+
+ /* If 8051 is running in RAM code, driver should
+ * inform Fw to reset by itself, or it will cause
+ * download Fw fail.*/
+ /* 8051 RAM code */
+ if (rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) {
+ rtl92d_firmware_selfreset(hw);
+ rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00);
+ }
+ _rtl92d_enable_fw_download(hw, true);
+ _rtl92d_write_fw(hw, version, pfwdata, fwsize);
+ _rtl92d_enable_fw_download(hw, false);
+ spin_lock_irqsave(&globalmutex_for_fwdownload, flags);
+ err = _rtl92d_fw_free_to_go(hw);
+ /* download fw over,clear 0x1f[5] */
+ value = rtl_read_byte(rtlpriv, 0x1f);
+ value &= (~BIT(5));
+ rtl_write_byte(rtlpriv, 0x1f, value);
+ spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("fw is not ready to run!\n"));
+ goto exit;
+ } else {
+ RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
+ ("fw is ready to run!\n"));
+ }
+exit:
+ err = _rtl92d_fw_init(hw);
+ return err;
+}
+
+static bool _rtl92d_check_fw_read_last_h2c(struct ieee80211_hw *hw, u8 boxnum)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 val_hmetfr;
+ bool result = false;
+
+ val_hmetfr = rtl_read_byte(rtlpriv, REG_HMETFR);
+ if (((val_hmetfr >> boxnum) & BIT(0)) == 0)
+ result = true;
+ return result;
+}
+
+static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw,
+ u8 element_id, u32 cmd_len, u8 *cmdbuffer)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ u8 boxnum;
+ u16 box_reg = 0, box_extreg = 0;
+ u8 u1b_tmp;
+ bool isfw_read = false;
+ u8 buf_index = 0;
+ bool bwrite_sucess = false;
+ u8 wait_h2c_limmit = 100;
+ u8 wait_writeh2c_limmit = 100;
+ u8 boxcontent[4], boxextcontent[2];
+ u32 h2c_waitcounter = 0;
+ unsigned long flag;
+ u8 idx;
+
+ if (ppsc->rfpwr_state == ERFOFF || ppsc->inactive_pwrstate == ERFOFF) {
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ ("Return as RF is off!!!\n"));
+ return;
+ }
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("come in\n"));
+ while (true) {
+ spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag);
+ if (rtlhal->h2c_setinprogress) {
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ ("H2C set in progress! Wait to set.."
+ "element_id(%d).\n", element_id));
+
+ while (rtlhal->h2c_setinprogress) {
+ spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock,
+ flag);
+ h2c_waitcounter++;
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ ("Wait 100 us (%d times)...\n",
+ h2c_waitcounter));
+ udelay(100);
+
+ if (h2c_waitcounter > 1000)
+ return;
+
+ spin_lock_irqsave(&rtlpriv->locks.h2c_lock,
+ flag);
+ }
+ spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
+ } else {
+ rtlhal->h2c_setinprogress = true;
+ spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
+ break;
+ }
+ }
+ while (!bwrite_sucess) {
+ wait_writeh2c_limmit--;
+ if (wait_writeh2c_limmit == 0) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Write H2C fail because no trigger "
+ "for FW INT!\n"));
+ break;
+ }
+ boxnum = rtlhal->last_hmeboxnum;
+ switch (boxnum) {
+ case 0:
+ box_reg = REG_HMEBOX_0;
+ box_extreg = REG_HMEBOX_EXT_0;
+ break;
+ case 1:
+ box_reg = REG_HMEBOX_1;
+ box_extreg = REG_HMEBOX_EXT_1;
+ break;
+ case 2:
+ box_reg = REG_HMEBOX_2;
+ box_extreg = REG_HMEBOX_EXT_2;
+ break;
+ case 3:
+ box_reg = REG_HMEBOX_3;
+ box_extreg = REG_HMEBOX_EXT_3;
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+ isfw_read = _rtl92d_check_fw_read_last_h2c(hw, boxnum);
+ while (!isfw_read) {
+ wait_h2c_limmit--;
+ if (wait_h2c_limmit == 0) {
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ ("Wating too long for FW read "
+ "clear HMEBox(%d)!\n", boxnum));
+ break;
+ }
+ udelay(10);
+ isfw_read = _rtl92d_check_fw_read_last_h2c(hw, boxnum);
+ u1b_tmp = rtl_read_byte(rtlpriv, 0x1BF);
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ ("Wating for FW read clear HMEBox(%d)!!! "
+ "0x1BF = %2x\n", boxnum, u1b_tmp));
+ }
+ if (!isfw_read) {
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ ("Write H2C register BOX[%d] fail!!!!! "
+ "Fw do not read.\n", boxnum));
+ break;
+ }
+ memset(boxcontent, 0, sizeof(boxcontent));
+ memset(boxextcontent, 0, sizeof(boxextcontent));
+ boxcontent[0] = element_id;
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ ("Write element_id box_reg(%4x) = %2x\n",
+ box_reg, element_id));
+ switch (cmd_len) {
+ case 1:
+ boxcontent[0] &= ~(BIT(7));
+ memcpy(boxcontent + 1, cmdbuffer + buf_index, 1);
+ for (idx = 0; idx < 4; idx++)
+ rtl_write_byte(rtlpriv, box_reg + idx,
+ boxcontent[idx]);
+ break;
+ case 2:
+ boxcontent[0] &= ~(BIT(7));
+ memcpy(boxcontent + 1, cmdbuffer + buf_index, 2);
+ for (idx = 0; idx < 4; idx++)
+ rtl_write_byte(rtlpriv, box_reg + idx,
+ boxcontent[idx]);
+ break;
+ case 3:
+ boxcontent[0] &= ~(BIT(7));
+ memcpy(boxcontent + 1, cmdbuffer + buf_index, 3);
+ for (idx = 0; idx < 4; idx++)
+ rtl_write_byte(rtlpriv, box_reg + idx,
+ boxcontent[idx]);
+ break;
+ case 4:
+ boxcontent[0] |= (BIT(7));
+ memcpy(boxextcontent, cmdbuffer + buf_index, 2);
+ memcpy(boxcontent + 1, cmdbuffer + buf_index + 2, 2);
+ for (idx = 0; idx < 2; idx++)
+ rtl_write_byte(rtlpriv, box_extreg + idx,
+ boxextcontent[idx]);
+ for (idx = 0; idx < 4; idx++)
+ rtl_write_byte(rtlpriv, box_reg + idx,
+ boxcontent[idx]);
+ break;
+ case 5:
+ boxcontent[0] |= (BIT(7));
+ memcpy(boxextcontent, cmdbuffer + buf_index, 2);
+ memcpy(boxcontent + 1, cmdbuffer + buf_index + 2, 3);
+ for (idx = 0; idx < 2; idx++)
+ rtl_write_byte(rtlpriv, box_extreg + idx,
+ boxextcontent[idx]);
+ for (idx = 0; idx < 4; idx++)
+ rtl_write_byte(rtlpriv, box_reg + idx,
+ boxcontent[idx]);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+ bwrite_sucess = true;
+ rtlhal->last_hmeboxnum = boxnum + 1;
+ if (rtlhal->last_hmeboxnum == 4)
+ rtlhal->last_hmeboxnum = 0;
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ ("pHalData->last_hmeboxnum = %d\n",
+ rtlhal->last_hmeboxnum));
+ }
+ spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag);
+ rtlhal->h2c_setinprogress = false;
+ spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("go out\n"));
+}
+
+void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw,
+ u8 element_id, u32 cmd_len, u8 *cmdbuffer)
+{
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u32 tmp_cmdbuf[2];
+
+ if (rtlhal->fw_ready == false) {
+ RT_ASSERT(false, ("return H2C cmd because of Fw "
+ "download fail!!!\n"));
+ return;
+ }
+ memset(tmp_cmdbuf, 0, 8);
+ memcpy(tmp_cmdbuf, cmdbuffer, cmd_len);
+ _rtl92d_fill_h2c_command(hw, element_id, cmd_len, (u8 *)&tmp_cmdbuf);
+ return;
+}
+
+void rtl92d_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 u1_h2c_set_pwrmode[3] = { 0 };
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, ("FW LPS mode = %d\n", mode));
+ SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode);
+ SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, 1);
+ SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode,
+ ppsc->reg_max_lps_awakeintvl);
+ RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
+ "rtl92d_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode\n",
+ u1_h2c_set_pwrmode, 3);
+ rtl92d_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode);
+}
+
+static bool _rtl92d_cmd_send_packet(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl8192_tx_ring *ring;
+ struct rtl_tx_desc *pdesc;
+ u8 idx = 0;
+ unsigned long flags;
+ struct sk_buff *pskb;
+
+ ring = &rtlpci->tx_ring[BEACON_QUEUE];
+ pskb = __skb_dequeue(&ring->queue);
+ if (pskb)
+ kfree_skb(pskb);
+ spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
+ pdesc = &ring->desc[idx];
+ /* discard output from call below */
+ rtlpriv->cfg->ops->get_desc((u8 *) pdesc, true, HW_DESC_OWN);
+ rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *) pdesc, 1, 1, skb);
+ __skb_queue_tail(&ring->queue, skb);
+ spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags);
+ rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE);
+ return true;
+}
+
+#define BEACON_PG 0 /*->1 */
+#define PSPOLL_PG 2
+#define NULL_PG 3
+#define PROBERSP_PG 4 /*->5 */
+#define TOTAL_RESERVED_PKT_LEN 768
+
+static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = {
+ /* page 0 beacon */
+ 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
+ 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x50, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69,
+ 0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C,
+ 0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96,
+ 0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A,
+ 0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C,
+ 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02,
+ 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* page 1 beacon */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* page 2 ps-poll */
+ 0xA4, 0x10, 0x01, 0xC0, 0x00, 0x40, 0x10, 0x10,
+ 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* page 3 null */
+ 0x48, 0x01, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10,
+ 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
+ 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x72, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* page 4 probe_resp */
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10,
+ 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
+ 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00,
+ 0x9E, 0x46, 0x15, 0x32, 0x27, 0xF2, 0x2D, 0x00,
+ 0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69,
+ 0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C,
+ 0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96,
+ 0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A,
+ 0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C,
+ 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02,
+ 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* page 5 probe_resp */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+void rtl92d_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool dl_finished)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct sk_buff *skb = NULL;
+ u32 totalpacketlen;
+ bool rtstatus;
+ u8 u1RsvdPageLoc[3] = { 0 };
+ bool dlok = false;
+ u8 *beacon;
+ u8 *p_pspoll;
+ u8 *nullfunc;
+ u8 *p_probersp;
+ /*---------------------------------------------------------
+ (1) beacon
+ ---------------------------------------------------------*/
+ beacon = &reserved_page_packet[BEACON_PG * 128];
+ SET_80211_HDR_ADDRESS2(beacon, mac->mac_addr);
+ SET_80211_HDR_ADDRESS3(beacon, mac->bssid);
+ /*-------------------------------------------------------
+ (2) ps-poll
+ --------------------------------------------------------*/
+ p_pspoll = &reserved_page_packet[PSPOLL_PG * 128];
+ SET_80211_PS_POLL_AID(p_pspoll, (mac->assoc_id | 0xc000));
+ SET_80211_PS_POLL_BSSID(p_pspoll, mac->bssid);
+ SET_80211_PS_POLL_TA(p_pspoll, mac->mac_addr);
+ SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(u1RsvdPageLoc, PSPOLL_PG);
+ /*--------------------------------------------------------
+ (3) null data
+ ---------------------------------------------------------*/
+ nullfunc = &reserved_page_packet[NULL_PG * 128];
+ SET_80211_HDR_ADDRESS1(nullfunc, mac->bssid);
+ SET_80211_HDR_ADDRESS2(nullfunc, mac->mac_addr);
+ SET_80211_HDR_ADDRESS3(nullfunc, mac->bssid);
+ SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(u1RsvdPageLoc, NULL_PG);
+ /*---------------------------------------------------------
+ (4) probe response
+ ----------------------------------------------------------*/
+ p_probersp = &reserved_page_packet[PROBERSP_PG * 128];
+ SET_80211_HDR_ADDRESS1(p_probersp, mac->bssid);
+ SET_80211_HDR_ADDRESS2(p_probersp, mac->mac_addr);
+ SET_80211_HDR_ADDRESS3(p_probersp, mac->bssid);
+ SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1RsvdPageLoc, PROBERSP_PG);
+ totalpacketlen = TOTAL_RESERVED_PKT_LEN;
+ RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD,
+ "rtl92d_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n",
+ &reserved_page_packet[0], totalpacketlen);
+ RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
+ "rtl92d_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n",
+ u1RsvdPageLoc, 3);
+ skb = dev_alloc_skb(totalpacketlen);
+ memcpy((u8 *) skb_put(skb, totalpacketlen), &reserved_page_packet,
+ totalpacketlen);
+ rtstatus = _rtl92d_cmd_send_packet(hw, skb);
+
+ if (rtstatus)
+ dlok = true;
+ if (dlok) {
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ ("Set RSVD page location to Fw.\n"));
+ RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
+ "H2C_RSVDPAGE:\n", u1RsvdPageLoc, 3);
+ rtl92d_fill_h2c_cmd(hw, H2C_RSVDPAGE,
+ sizeof(u1RsvdPageLoc), u1RsvdPageLoc);
+ } else
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("Set RSVD page location to Fw FAIL!!!!!!.\n"));
+}
+
+void rtl92d_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus)
+{
+ u8 u1_joinbssrpt_parm[1] = {0};
+
+ SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(u1_joinbssrpt_parm, mstatus);
+ rtl92d_fill_h2c_cmd(hw, H2C_JOINBSSRPT, 1, u1_joinbssrpt_parm);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.h b/drivers/net/wireless/rtlwifi/rtl8192de/fw.h
new file mode 100644
index 000000000000..0c4d489eaa48
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.h
@@ -0,0 +1,155 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92D__FW__H__
+#define __RTL92D__FW__H__
+
+#define FW_8192D_START_ADDRESS 0x1000
+#define FW_8192D_PAGE_SIZE 4096
+#define FW_8192D_POLLING_TIMEOUT_COUNT 1000
+
+#define IS_FW_HEADER_EXIST(_pfwhdr) \
+ ((GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFF0) == 0x92C0 || \
+ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFF0) == 0x88C0 || \
+ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFFF) == 0x92D0 || \
+ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFFF) == 0x92D1 || \
+ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFFF) == 0x92D2 || \
+ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFFF) == 0x92D3)
+
+/* Define a macro that takes an le32 word, converts it to host ordering,
+ * right shifts by a specified count, creates a mask of the specified
+ * bit count, and extracts that number of bits.
+ */
+
+#define SHIFT_AND_MASK_LE(__pdesc, __shift, __mask) \
+ ((le32_to_cpu(*(((__le32 *)(__pdesc)))) >> (__shift)) & \
+ BIT_LEN_MASK_32(__mask))
+
+/* Firmware Header(8-byte alinment required) */
+/* --- LONG WORD 0 ---- */
+#define GET_FIRMWARE_HDR_SIGNATURE(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr, 0, 16)
+#define GET_FIRMWARE_HDR_CATEGORY(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr, 16, 8)
+#define GET_FIRMWARE_HDR_FUNCTION(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr, 24, 8)
+#define GET_FIRMWARE_HDR_VERSION(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 4, 0, 16)
+#define GET_FIRMWARE_HDR_SUB_VER(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 4, 16, 8)
+#define GET_FIRMWARE_HDR_RSVD1(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 4, 24, 8)
+
+/* --- LONG WORD 1 ---- */
+#define GET_FIRMWARE_HDR_MONTH(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 8, 0, 8)
+#define GET_FIRMWARE_HDR_DATE(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 8, 8, 8)
+#define GET_FIRMWARE_HDR_HOUR(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 8, 16, 8)
+#define GET_FIRMWARE_HDR_MINUTE(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 8, 24, 8)
+#define GET_FIRMWARE_HDR_ROMCODE_SIZE(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 12, 0, 16)
+#define GET_FIRMWARE_HDR_RSVD2(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 12, 16, 16)
+
+/* --- LONG WORD 2 ---- */
+#define GET_FIRMWARE_HDR_SVN_IDX(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 16, 0, 32)
+#define GET_FIRMWARE_HDR_RSVD3(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 20, 0, 32)
+
+/* --- LONG WORD 3 ---- */
+#define GET_FIRMWARE_HDR_RSVD4(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 24, 0, 32)
+#define GET_FIRMWARE_HDR_RSVD5(__fwhdr) \
+ SHIFT_AND_MASK_LE(__fwhdr + 28, 0, 32)
+
+#define pagenum_128(_len) \
+ (u32)(((_len) >> 7) + ((_len) & 0x7F ? 1 : 0))
+
+#define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 1, 0, 8, __val)
+#define SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 2, 0, 8, __val)
+#define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 1, 0, 8, __val)
+#define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 2, 0, 8, __val)
+
+struct rtl92d_firmware_header {
+ u16 signature;
+ u8 category;
+ u8 function;
+ u16 version;
+ u8 subversion;
+ u8 rsvd1;
+
+ u8 month;
+ u8 date;
+ u8 hour;
+ u8 minute;
+ u16 ramcodeSize;
+ u16 rsvd2;
+
+ u32 svnindex;
+ u32 rsvd3;
+
+ u32 rsvd4;
+ u32 rsvd5;
+};
+
+enum rtl8192d_h2c_cmd {
+ H2C_AP_OFFLOAD = 0,
+ H2C_SETPWRMODE = 1,
+ H2C_JOINBSSRPT = 2,
+ H2C_RSVDPAGE = 3,
+ H2C_RSSI_REPORT = 5,
+ H2C_RA_MASK = 6,
+ H2C_MAC_MODE_SEL = 9,
+ H2C_PWRM = 15,
+ MAX_H2CCMD
+};
+
+int rtl92d_download_fw(struct ieee80211_hw *hw);
+void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id,
+ u32 cmd_len, u8 *p_cmdbuffer);
+void rtl92d_firmware_selfreset(struct ieee80211_hw *hw);
+void rtl92d_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
+void rtl92d_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished);
+void rtl92d_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
new file mode 100644
index 000000000000..f5bd3a3cd34a
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
@@ -0,0 +1,2328 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../efuse.h"
+#include "../base.h"
+#include "../regd.h"
+#include "../cam.h"
+#include "../ps.h"
+#include "../pci.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "dm.h"
+#include "fw.h"
+#include "led.h"
+#include "sw.h"
+#include "hw.h"
+
+u32 rtl92de_read_dword_dbi(struct ieee80211_hw *hw, u16 offset, u8 direct)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 value;
+
+ rtl_write_word(rtlpriv, REG_DBI_CTRL, (offset & 0xFFC));
+ rtl_write_byte(rtlpriv, REG_DBI_FLAG, BIT(1) | direct);
+ udelay(10);
+ value = rtl_read_dword(rtlpriv, REG_DBI_RDATA);
+ return value;
+}
+
+void rtl92de_write_dword_dbi(struct ieee80211_hw *hw,
+ u16 offset, u32 value, u8 direct)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtl_write_word(rtlpriv, REG_DBI_CTRL, ((offset & 0xFFC) | 0xF000));
+ rtl_write_dword(rtlpriv, REG_DBI_WDATA, value);
+ rtl_write_byte(rtlpriv, REG_DBI_FLAG, BIT(0) | direct);
+}
+
+static void _rtl92de_set_bcn_ctrl_reg(struct ieee80211_hw *hw,
+ u8 set_bits, u8 clear_bits)
+{
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpci->reg_bcn_ctrl_val |= set_bits;
+ rtlpci->reg_bcn_ctrl_val &= ~clear_bits;
+ rtl_write_byte(rtlpriv, REG_BCN_CTRL, (u8) rtlpci->reg_bcn_ctrl_val);
+}
+
+static void _rtl92de_stop_tx_beacon(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 tmp1byte;
+
+ tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2);
+ rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte & (~BIT(6)));
+ rtl_write_byte(rtlpriv, REG_BCN_MAX_ERR, 0xff);
+ rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0x64);
+ tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2);
+ tmp1byte &= ~(BIT(0));
+ rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte);
+}
+
+static void _rtl92de_resume_tx_beacon(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 tmp1byte;
+
+ tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2);
+ rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte | BIT(6));
+ rtl_write_byte(rtlpriv, REG_BCN_MAX_ERR, 0x0a);
+ rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff);
+ tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2);
+ tmp1byte |= BIT(0);
+ rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte);
+}
+
+static void _rtl92de_enable_bcn_sub_func(struct ieee80211_hw *hw)
+{
+ _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(1));
+}
+
+static void _rtl92de_disable_bcn_sub_func(struct ieee80211_hw *hw)
+{
+ _rtl92de_set_bcn_ctrl_reg(hw, BIT(1), 0);
+}
+
+void rtl92de_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ switch (variable) {
+ case HW_VAR_RCR:
+ *((u32 *) (val)) = rtlpci->receive_config;
+ break;
+ case HW_VAR_RF_STATE:
+ *((enum rf_pwrstate *)(val)) = ppsc->rfpwr_state;
+ break;
+ case HW_VAR_FWLPS_RF_ON:{
+ enum rf_pwrstate rfState;
+ u32 val_rcr;
+
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RF_STATE,
+ (u8 *) (&rfState));
+ if (rfState == ERFOFF) {
+ *((bool *) (val)) = true;
+ } else {
+ val_rcr = rtl_read_dword(rtlpriv, REG_RCR);
+ val_rcr &= 0x00070000;
+ if (val_rcr)
+ *((bool *) (val)) = false;
+ else
+ *((bool *) (val)) = true;
+ }
+ break;
+ }
+ case HW_VAR_FW_PSMODE_STATUS:
+ *((bool *) (val)) = ppsc->fw_current_inpsmode;
+ break;
+ case HW_VAR_CORRECT_TSF:{
+ u64 tsf;
+ u32 *ptsf_low = (u32 *)&tsf;
+ u32 *ptsf_high = ((u32 *)&tsf) + 1;
+
+ *ptsf_high = rtl_read_dword(rtlpriv, (REG_TSFTR + 4));
+ *ptsf_low = rtl_read_dword(rtlpriv, REG_TSFTR);
+ *((u64 *) (val)) = tsf;
+ break;
+ }
+ case HW_VAR_INT_MIGRATION:
+ *((bool *)(val)) = rtlpriv->dm.interrupt_migration;
+ break;
+ case HW_VAR_INT_AC:
+ *((bool *)(val)) = rtlpriv->dm.disable_tx_int;
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+}
+
+void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ u8 idx;
+
+ switch (variable) {
+ case HW_VAR_ETHER_ADDR:
+ for (idx = 0; idx < ETH_ALEN; idx++) {
+ rtl_write_byte(rtlpriv, (REG_MACID + idx),
+ val[idx]);
+ }
+ break;
+ case HW_VAR_BASIC_RATE: {
+ u16 rate_cfg = ((u16 *) val)[0];
+ u8 rate_index = 0;
+
+ rate_cfg = rate_cfg & 0x15f;
+ if (mac->vendor == PEER_CISCO &&
+ ((rate_cfg & 0x150) == 0))
+ rate_cfg |= 0x01;
+ rtl_write_byte(rtlpriv, REG_RRSR, rate_cfg & 0xff);
+ rtl_write_byte(rtlpriv, REG_RRSR + 1,
+ (rate_cfg >> 8) & 0xff);
+ while (rate_cfg > 0x1) {
+ rate_cfg = (rate_cfg >> 1);
+ rate_index++;
+ }
+ if (rtlhal->fw_version > 0xe)
+ rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL,
+ rate_index);
+ break;
+ }
+ case HW_VAR_BSSID:
+ for (idx = 0; idx < ETH_ALEN; idx++) {
+ rtl_write_byte(rtlpriv, (REG_BSSID + idx),
+ val[idx]);
+ }
+ break;
+ case HW_VAR_SIFS:
+ rtl_write_byte(rtlpriv, REG_SIFS_CTX + 1, val[0]);
+ rtl_write_byte(rtlpriv, REG_SIFS_TRX + 1, val[1]);
+ rtl_write_byte(rtlpriv, REG_SPEC_SIFS + 1, val[0]);
+ rtl_write_byte(rtlpriv, REG_MAC_SPEC_SIFS + 1, val[0]);
+ if (!mac->ht_enable)
+ rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM,
+ 0x0e0e);
+ else
+ rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM,
+ *((u16 *) val));
+ break;
+ case HW_VAR_SLOT_TIME: {
+ u8 e_aci;
+
+ RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+ ("HW_VAR_SLOT_TIME %x\n", val[0]));
+ rtl_write_byte(rtlpriv, REG_SLOT, val[0]);
+ for (e_aci = 0; e_aci < AC_MAX; e_aci++)
+ rtlpriv->cfg->ops->set_hw_reg(hw,
+ HW_VAR_AC_PARAM,
+ (u8 *) (&e_aci));
+ break;
+ }
+ case HW_VAR_ACK_PREAMBLE: {
+ u8 reg_tmp;
+ u8 short_preamble = (bool) (*(u8 *) val);
+
+ reg_tmp = (mac->cur_40_prime_sc) << 5;
+ if (short_preamble)
+ reg_tmp |= 0x80;
+ rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_tmp);
+ break;
+ }
+ case HW_VAR_AMPDU_MIN_SPACE: {
+ u8 min_spacing_to_set;
+ u8 sec_min_space;
+
+ min_spacing_to_set = *((u8 *) val);
+ if (min_spacing_to_set <= 7) {
+ sec_min_space = 0;
+ if (min_spacing_to_set < sec_min_space)
+ min_spacing_to_set = sec_min_space;
+ mac->min_space_cfg = ((mac->min_space_cfg & 0xf8) |
+ min_spacing_to_set);
+ *val = min_spacing_to_set;
+ RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+ ("Set HW_VAR_AMPDU_MIN_SPACE: %#x\n",
+ mac->min_space_cfg));
+ rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE,
+ mac->min_space_cfg);
+ }
+ break;
+ }
+ case HW_VAR_SHORTGI_DENSITY: {
+ u8 density_to_set;
+
+ density_to_set = *((u8 *) val);
+ mac->min_space_cfg = rtlpriv->rtlhal.minspace_cfg;
+ mac->min_space_cfg |= (density_to_set << 3);
+ RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+ ("Set HW_VAR_SHORTGI_DENSITY: %#x\n",
+ mac->min_space_cfg));
+ rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE,
+ mac->min_space_cfg);
+ break;
+ }
+ case HW_VAR_AMPDU_FACTOR: {
+ u8 factor_toset;
+ u32 regtoSet;
+ u8 *ptmp_byte = NULL;
+ u8 index;
+
+ if (rtlhal->macphymode == DUALMAC_DUALPHY)
+ regtoSet = 0xb9726641;
+ else if (rtlhal->macphymode == DUALMAC_SINGLEPHY)
+ regtoSet = 0x66626641;
+ else
+ regtoSet = 0xb972a841;
+ factor_toset = *((u8 *) val);
+ if (factor_toset <= 3) {
+ factor_toset = (1 << (factor_toset + 2));
+ if (factor_toset > 0xf)
+ factor_toset = 0xf;
+ for (index = 0; index < 4; index++) {
+ ptmp_byte = (u8 *) (&regtoSet) + index;
+ if ((*ptmp_byte & 0xf0) >
+ (factor_toset << 4))
+ *ptmp_byte = (*ptmp_byte & 0x0f)
+ | (factor_toset << 4);
+ if ((*ptmp_byte & 0x0f) > factor_toset)
+ *ptmp_byte = (*ptmp_byte & 0xf0)
+ | (factor_toset);
+ }
+ rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, regtoSet);
+ RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+ ("Set HW_VAR_AMPDU_FACTOR: %#x\n",
+ factor_toset));
+ }
+ break;
+ }
+ case HW_VAR_AC_PARAM: {
+ u8 e_aci = *((u8 *) val);
+ rtl92d_dm_init_edca_turbo(hw);
+ if (rtlpci->acm_method != eAcmWay2_SW)
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL,
+ (u8 *) (&e_aci));
+ break;
+ }
+ case HW_VAR_ACM_CTRL: {
+ u8 e_aci = *((u8 *) val);
+ union aci_aifsn *p_aci_aifsn =
+ (union aci_aifsn *)(&(mac->ac[0].aifs));
+ u8 acm = p_aci_aifsn->f.acm;
+ u8 acm_ctrl = rtl_read_byte(rtlpriv, REG_ACMHWCTRL);
+
+ acm_ctrl = acm_ctrl | ((rtlpci->acm_method == 2) ? 0x0 : 0x1);
+ if (acm) {
+ switch (e_aci) {
+ case AC0_BE:
+ acm_ctrl |= ACMHW_BEQEN;
+ break;
+ case AC2_VI:
+ acm_ctrl |= ACMHW_VIQEN;
+ break;
+ case AC3_VO:
+ acm_ctrl |= ACMHW_VOQEN;
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("HW_VAR_ACM_CTRL acm set "
+ "failed: eACI is %d\n", acm));
+ break;
+ }
+ } else {
+ switch (e_aci) {
+ case AC0_BE:
+ acm_ctrl &= (~ACMHW_BEQEN);
+ break;
+ case AC2_VI:
+ acm_ctrl &= (~ACMHW_VIQEN);
+ break;
+ case AC3_VO:
+ acm_ctrl &= (~ACMHW_VOQEN);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+ }
+ RT_TRACE(rtlpriv, COMP_QOS, DBG_TRACE,
+ ("SetHwReg8190pci(): [HW_VAR_ACM_CTRL] "
+ "Write 0x%X\n", acm_ctrl));
+ rtl_write_byte(rtlpriv, REG_ACMHWCTRL, acm_ctrl);
+ break;
+ }
+ case HW_VAR_RCR:
+ rtl_write_dword(rtlpriv, REG_RCR, ((u32 *) (val))[0]);
+ rtlpci->receive_config = ((u32 *) (val))[0];
+ break;
+ case HW_VAR_RETRY_LIMIT: {
+ u8 retry_limit = ((u8 *) (val))[0];
+
+ rtl_write_word(rtlpriv, REG_RL,
+ retry_limit << RETRY_LIMIT_SHORT_SHIFT |
+ retry_limit << RETRY_LIMIT_LONG_SHIFT);
+ break;
+ }
+ case HW_VAR_DUAL_TSF_RST:
+ rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, (BIT(0) | BIT(1)));
+ break;
+ case HW_VAR_EFUSE_BYTES:
+ rtlefuse->efuse_usedbytes = *((u16 *) val);
+ break;
+ case HW_VAR_EFUSE_USAGE:
+ rtlefuse->efuse_usedpercentage = *((u8 *) val);
+ break;
+ case HW_VAR_IO_CMD:
+ rtl92d_phy_set_io_cmd(hw, (*(enum io_type *)val));
+ break;
+ case HW_VAR_WPA_CONFIG:
+ rtl_write_byte(rtlpriv, REG_SECCFG, *((u8 *) val));
+ break;
+ case HW_VAR_SET_RPWM:
+ rtl92d_fill_h2c_cmd(hw, H2C_PWRM, 1, (u8 *) (val));
+ break;
+ case HW_VAR_H2C_FW_PWRMODE:
+ break;
+ case HW_VAR_FW_PSMODE_STATUS:
+ ppsc->fw_current_inpsmode = *((bool *) val);
+ break;
+ case HW_VAR_H2C_FW_JOINBSSRPT: {
+ u8 mstatus = (*(u8 *) val);
+ u8 tmp_regcr, tmp_reg422;
+ bool recover = false;
+
+ if (mstatus == RT_MEDIA_CONNECT) {
+ rtlpriv->cfg->ops->set_hw_reg(hw,
+ HW_VAR_AID, NULL);
+ tmp_regcr = rtl_read_byte(rtlpriv, REG_CR + 1);
+ rtl_write_byte(rtlpriv, REG_CR + 1,
+ (tmp_regcr | BIT(0)));
+ _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(3));
+ _rtl92de_set_bcn_ctrl_reg(hw, BIT(4), 0);
+ tmp_reg422 = rtl_read_byte(rtlpriv,
+ REG_FWHW_TXQ_CTRL + 2);
+ if (tmp_reg422 & BIT(6))
+ recover = true;
+ rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
+ tmp_reg422 & (~BIT(6)));
+ rtl92d_set_fw_rsvdpagepkt(hw, 0);
+ _rtl92de_set_bcn_ctrl_reg(hw, BIT(3), 0);
+ _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(4));
+ if (recover)
+ rtl_write_byte(rtlpriv,
+ REG_FWHW_TXQ_CTRL + 2,
+ tmp_reg422);
+ rtl_write_byte(rtlpriv, REG_CR + 1,
+ (tmp_regcr & ~(BIT(0))));
+ }
+ rtl92d_set_fw_joinbss_report_cmd(hw, (*(u8 *) val));
+ break;
+ }
+ case HW_VAR_AID: {
+ u16 u2btmp;
+ u2btmp = rtl_read_word(rtlpriv, REG_BCN_PSR_RPT);
+ u2btmp &= 0xC000;
+ rtl_write_word(rtlpriv, REG_BCN_PSR_RPT, (u2btmp |
+ mac->assoc_id));
+ break;
+ }
+ case HW_VAR_CORRECT_TSF: {
+ u8 btype_ibss = ((u8 *) (val))[0];
+
+ if (btype_ibss)
+ _rtl92de_stop_tx_beacon(hw);
+ _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(3));
+ rtl_write_dword(rtlpriv, REG_TSFTR,
+ (u32) (mac->tsf & 0xffffffff));
+ rtl_write_dword(rtlpriv, REG_TSFTR + 4,
+ (u32) ((mac->tsf >> 32) & 0xffffffff));
+ _rtl92de_set_bcn_ctrl_reg(hw, BIT(3), 0);
+ if (btype_ibss)
+ _rtl92de_resume_tx_beacon(hw);
+
+ break;
+ }
+ case HW_VAR_INT_MIGRATION: {
+ bool int_migration = *(bool *) (val);
+
+ if (int_migration) {
+ /* Set interrrupt migration timer and
+ * corresponging Tx/Rx counter.
+ * timer 25ns*0xfa0=100us for 0xf packets.
+ * 0x306:Rx, 0x307:Tx */
+ rtl_write_dword(rtlpriv, REG_INT_MIG, 0xfe000fa0);
+ rtlpriv->dm.interrupt_migration = int_migration;
+ } else {
+ /* Reset all interrupt migration settings. */
+ rtl_write_dword(rtlpriv, REG_INT_MIG, 0);
+ rtlpriv->dm.interrupt_migration = int_migration;
+ }
+ break;
+ }
+ case HW_VAR_INT_AC: {
+ bool disable_ac_int = *((bool *) val);
+
+ /* Disable four ACs interrupts. */
+ if (disable_ac_int) {
+ /* Disable VO, VI, BE and BK four AC interrupts
+ * to gain more efficient CPU utilization.
+ * When extremely highly Rx OK occurs,
+ * we will disable Tx interrupts.
+ */
+ rtlpriv->cfg->ops->update_interrupt_mask(hw, 0,
+ RT_AC_INT_MASKS);
+ rtlpriv->dm.disable_tx_int = disable_ac_int;
+ /* Enable four ACs interrupts. */
+ } else {
+ rtlpriv->cfg->ops->update_interrupt_mask(hw,
+ RT_AC_INT_MASKS, 0);
+ rtlpriv->dm.disable_tx_int = disable_ac_int;
+ }
+ break;
+ }
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+}
+
+static bool _rtl92de_llt_write(struct ieee80211_hw *hw, u32 address, u32 data)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ bool status = true;
+ long count = 0;
+ u32 value = _LLT_INIT_ADDR(address) |
+ _LLT_INIT_DATA(data) | _LLT_OP(_LLT_WRITE_ACCESS);
+
+ rtl_write_dword(rtlpriv, REG_LLT_INIT, value);
+ do {
+ value = rtl_read_dword(rtlpriv, REG_LLT_INIT);
+ if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value))
+ break;
+ if (count > POLLING_LLT_THRESHOLD) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Failed to polling write LLT done at "
+ "address %d!\n", address));
+ status = false;
+ break;
+ }
+ } while (++count);
+ return status;
+}
+
+static bool _rtl92de_llt_table_init(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ unsigned short i;
+ u8 txpktbuf_bndy;
+ u8 maxPage;
+ bool status;
+ u32 value32; /* High+low page number */
+ u8 value8; /* normal page number */
+
+ if (rtlpriv->rtlhal.macphymode == SINGLEMAC_SINGLEPHY) {
+ maxPage = 255;
+ txpktbuf_bndy = 246;
+ value8 = 0;
+ value32 = 0x80bf0d29;
+ } else if (rtlpriv->rtlhal.macphymode != SINGLEMAC_SINGLEPHY) {
+ maxPage = 127;
+ txpktbuf_bndy = 123;
+ value8 = 0;
+ value32 = 0x80750005;
+ }
+
+ /* Set reserved page for each queue */
+ /* 11. RQPN 0x200[31:0] = 0x80BD1C1C */
+ /* load RQPN */
+ rtl_write_byte(rtlpriv, REG_RQPN_NPQ, value8);
+ rtl_write_dword(rtlpriv, REG_RQPN, value32);
+
+ /* 12. TXRKTBUG_PG_BNDY 0x114[31:0] = 0x27FF00F6 */
+ /* TXRKTBUG_PG_BNDY */
+ rtl_write_dword(rtlpriv, REG_TRXFF_BNDY,
+ (rtl_read_word(rtlpriv, REG_TRXFF_BNDY + 2) << 16 |
+ txpktbuf_bndy));
+
+ /* 13. TDECTRL[15:8] 0x209[7:0] = 0xF6 */
+ /* Beacon Head for TXDMA */
+ rtl_write_byte(rtlpriv, REG_TDECTRL + 1, txpktbuf_bndy);
+
+ /* 14. BCNQ_PGBNDY 0x424[7:0] = 0xF6 */
+ /* BCNQ_PGBNDY */
+ rtl_write_byte(rtlpriv, REG_TXPKTBUF_BCNQ_BDNY, txpktbuf_bndy);
+ rtl_write_byte(rtlpriv, REG_TXPKTBUF_MGQ_BDNY, txpktbuf_bndy);
+
+ /* 15. WMAC_LBK_BF_HD 0x45D[7:0] = 0xF6 */
+ /* WMAC_LBK_BF_HD */
+ rtl_write_byte(rtlpriv, 0x45D, txpktbuf_bndy);
+
+ /* Set Tx/Rx page size (Tx must be 128 Bytes, */
+ /* Rx can be 64,128,256,512,1024 bytes) */
+ /* 16. PBP [7:0] = 0x11 */
+ /* TRX page size */
+ rtl_write_byte(rtlpriv, REG_PBP, 0x11);
+
+ /* 17. DRV_INFO_SZ = 0x04 */
+ rtl_write_byte(rtlpriv, REG_RX_DRVINFO_SZ, 0x4);
+
+ /* 18. LLT_table_init(Adapter); */
+ for (i = 0; i < (txpktbuf_bndy - 1); i++) {
+ status = _rtl92de_llt_write(hw, i, i + 1);
+ if (true != status)
+ return status;
+ }
+
+ /* end of list */
+ status = _rtl92de_llt_write(hw, (txpktbuf_bndy - 1), 0xFF);
+ if (true != status)
+ return status;
+
+ /* Make the other pages as ring buffer */
+ /* This ring buffer is used as beacon buffer if we */
+ /* config this MAC as two MAC transfer. */
+ /* Otherwise used as local loopback buffer. */
+ for (i = txpktbuf_bndy; i < maxPage; i++) {
+ status = _rtl92de_llt_write(hw, i, (i + 1));
+ if (true != status)
+ return status;
+ }
+
+ /* Let last entry point to the start entry of ring buffer */
+ status = _rtl92de_llt_write(hw, maxPage, txpktbuf_bndy);
+ if (true != status)
+ return status;
+
+ return true;
+}
+
+static void _rtl92de_gen_refresh_led_state(struct ieee80211_hw *hw)
+{
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+
+ if (rtlpci->up_first_time)
+ return;
+ if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS)
+ rtl92de_sw_led_on(hw, pLed0);
+ else if (ppsc->rfoff_reason == RF_CHANGE_BY_INIT)
+ rtl92de_sw_led_on(hw, pLed0);
+ else
+ rtl92de_sw_led_off(hw, pLed0);
+}
+
+static bool _rtl92de_init_mac(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ unsigned char bytetmp;
+ unsigned short wordtmp;
+ u16 retry;
+
+ rtl92d_phy_set_poweron(hw);
+ /* Add for resume sequence of power domain according
+ * to power document V11. Chapter V.11.... */
+ /* 0. RSV_CTRL 0x1C[7:0] = 0x00 */
+ /* unlock ISO/CLK/Power control register */
+ rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x00);
+ rtl_write_byte(rtlpriv, REG_LDOA15_CTRL, 0x05);
+
+ /* 1. AFE_XTAL_CTRL [7:0] = 0x0F enable XTAL */
+ /* 2. SPS0_CTRL 0x11[7:0] = 0x2b enable SPS into PWM mode */
+ /* 3. delay (1ms) this is not necessary when initially power on */
+
+ /* C. Resume Sequence */
+ /* a. SPS0_CTRL 0x11[7:0] = 0x2b */
+ rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b);
+
+ /* b. AFE_XTAL_CTRL [7:0] = 0x0F */
+ rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL, 0x0F);
+
+ /* c. DRV runs power on init flow */
+
+ /* auto enable WLAN */
+ /* 4. APS_FSMCO 0x04[8] = 1; wait till 0x04[8] = 0 */
+ /* Power On Reset for MAC Block */
+ bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1) | BIT(0);
+ udelay(2);
+ rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, bytetmp);
+ udelay(2);
+
+ /* 5. Wait while 0x04[8] == 0 goto 2, otherwise goto 1 */
+ bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1);
+ udelay(50);
+ retry = 0;
+ while ((bytetmp & BIT(0)) && retry < 1000) {
+ retry++;
+ bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1);
+ udelay(50);
+ }
+
+ /* Enable Radio off, GPIO, and LED function */
+ /* 6. APS_FSMCO 0x04[15:0] = 0x0012 when enable HWPDN */
+ rtl_write_word(rtlpriv, REG_APS_FSMCO, 0x1012);
+
+ /* release RF digital isolation */
+ /* 7. SYS_ISO_CTRL 0x01[1] = 0x0; */
+ /*Set REG_SYS_ISO_CTRL 0x1=0x82 to prevent wake# problem. */
+ rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL + 1, 0x82);
+ udelay(2);
+
+ /* make sure that BB reset OK. */
+ /* rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); */
+
+ /* Disable REG_CR before enable it to assure reset */
+ rtl_write_word(rtlpriv, REG_CR, 0x0);
+
+ /* Release MAC IO register reset */
+ rtl_write_word(rtlpriv, REG_CR, 0x2ff);
+
+ /* clear stopping tx/rx dma */
+ rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, 0x0);
+
+ /* rtl_write_word(rtlpriv,REG_CR+2, 0x2); */
+
+ /* System init */
+ /* 18. LLT_table_init(Adapter); */
+ if (_rtl92de_llt_table_init(hw) == false)
+ return false;
+
+ /* Clear interrupt and enable interrupt */
+ /* 19. HISR 0x124[31:0] = 0xffffffff; */
+ /* HISRE 0x12C[7:0] = 0xFF */
+ rtl_write_dword(rtlpriv, REG_HISR, 0xffffffff);
+ rtl_write_byte(rtlpriv, REG_HISRE, 0xff);
+
+ /* 20. HIMR 0x120[31:0] |= [enable INT mask bit map]; */
+ /* 21. HIMRE 0x128[7:0] = [enable INT mask bit map] */
+ /* The IMR should be enabled later after all init sequence
+ * is finished. */
+
+ /* 22. PCIE configuration space configuration */
+ /* 23. Ensure PCIe Device 0x80[15:0] = 0x0143 (ASPM+CLKREQ), */
+ /* and PCIe gated clock function is enabled. */
+ /* PCIE configuration space will be written after
+ * all init sequence.(Or by BIOS) */
+
+ rtl92d_phy_config_maccoexist_rfpage(hw);
+
+ /* THe below section is not related to power document Vxx . */
+ /* This is only useful for driver and OS setting. */
+ /* -------------------Software Relative Setting---------------------- */
+ wordtmp = rtl_read_word(rtlpriv, REG_TRXDMA_CTRL);
+ wordtmp &= 0xf;
+ wordtmp |= 0xF771;
+ rtl_write_word(rtlpriv, REG_TRXDMA_CTRL, wordtmp);
+
+ /* Reported Tx status from HW for rate adaptive. */
+ /* This should be realtive to power on step 14. But in document V11 */
+ /* still not contain the description.!!! */
+ rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 1, 0x1F);
+
+ /* Set Tx/Rx page size (Tx must be 128 Bytes,
+ * Rx can be 64,128,256,512,1024 bytes) */
+ /* rtl_write_byte(rtlpriv,REG_PBP, 0x11); */
+
+ /* Set RCR register */
+ rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
+ /* rtl_write_byte(rtlpriv,REG_RX_DRVINFO_SZ, 4); */
+
+ /* Set TCR register */
+ rtl_write_dword(rtlpriv, REG_TCR, rtlpci->transmit_config);
+
+ /* disable earlymode */
+ rtl_write_byte(rtlpriv, 0x4d0, 0x0);
+
+ /* Set TX/RX descriptor physical address(from OS API). */
+ rtl_write_dword(rtlpriv, REG_BCNQ_DESA,
+ rtlpci->tx_ring[BEACON_QUEUE].dma);
+ rtl_write_dword(rtlpriv, REG_MGQ_DESA, rtlpci->tx_ring[MGNT_QUEUE].dma);
+ rtl_write_dword(rtlpriv, REG_VOQ_DESA, rtlpci->tx_ring[VO_QUEUE].dma);
+ rtl_write_dword(rtlpriv, REG_VIQ_DESA, rtlpci->tx_ring[VI_QUEUE].dma);
+ rtl_write_dword(rtlpriv, REG_BEQ_DESA, rtlpci->tx_ring[BE_QUEUE].dma);
+ rtl_write_dword(rtlpriv, REG_BKQ_DESA, rtlpci->tx_ring[BK_QUEUE].dma);
+ rtl_write_dword(rtlpriv, REG_HQ_DESA, rtlpci->tx_ring[HIGH_QUEUE].dma);
+ /* Set RX Desc Address */
+ rtl_write_dword(rtlpriv, REG_RX_DESA,
+ rtlpci->rx_ring[RX_MPDU_QUEUE].dma);
+
+ /* if we want to support 64 bit DMA, we should set it here,
+ * but now we do not support 64 bit DMA*/
+
+ rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 3, 0x33);
+
+ /* Reset interrupt migration setting when initialization */
+ rtl_write_dword(rtlpriv, REG_INT_MIG, 0);
+
+ /* Reconsider when to do this operation after asking HWSD. */
+ bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL);
+ rtl_write_byte(rtlpriv, REG_APSD_CTRL, bytetmp & ~BIT(6));
+ do {
+ retry++;
+ bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL);
+ } while ((retry < 200) && !(bytetmp & BIT(7)));
+
+ /* After MACIO reset,we must refresh LED state. */
+ _rtl92de_gen_refresh_led_state(hw);
+
+ /* Reset H2C protection register */
+ rtl_write_dword(rtlpriv, REG_MCUTST_1, 0x0);
+
+ return true;
+}
+
+static void _rtl92de_hw_configure(struct ieee80211_hw *hw)
+{
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 reg_bw_opmode = BW_OPMODE_20MHZ;
+ u32 reg_rrsr;
+
+ reg_rrsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, 0x8);
+ rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode);
+ rtl_write_dword(rtlpriv, REG_RRSR, reg_rrsr);
+ rtl_write_byte(rtlpriv, REG_SLOT, 0x09);
+ rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, 0x0);
+ rtl_write_word(rtlpriv, REG_FWHW_TXQ_CTRL, 0x1F80);
+ rtl_write_word(rtlpriv, REG_RL, 0x0707);
+ rtl_write_dword(rtlpriv, REG_BAR_MODE_CTRL, 0x02012802);
+ rtl_write_byte(rtlpriv, REG_HWSEQ_CTRL, 0xFF);
+ rtl_write_dword(rtlpriv, REG_DARFRC, 0x01000000);
+ rtl_write_dword(rtlpriv, REG_DARFRC + 4, 0x07060504);
+ rtl_write_dword(rtlpriv, REG_RARFRC, 0x01000000);
+ rtl_write_dword(rtlpriv, REG_RARFRC + 4, 0x07060504);
+ /* Aggregation threshold */
+ if (rtlhal->macphymode == DUALMAC_DUALPHY)
+ rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0xb9726641);
+ else if (rtlhal->macphymode == DUALMAC_SINGLEPHY)
+ rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0x66626641);
+ else
+ rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0xb972a841);
+ rtl_write_byte(rtlpriv, REG_ATIMWND, 0x2);
+ rtl_write_byte(rtlpriv, REG_BCN_MAX_ERR, 0x0a);
+ rtlpci->reg_bcn_ctrl_val = 0x1f;
+ rtl_write_byte(rtlpriv, REG_BCN_CTRL, rtlpci->reg_bcn_ctrl_val);
+ rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff);
+ rtl_write_byte(rtlpriv, REG_PIFS, 0x1C);
+ rtl_write_byte(rtlpriv, REG_AGGR_BREAK_TIME, 0x16);
+ rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0020);
+ /* For throughput */
+ rtl_write_word(rtlpriv, REG_FAST_EDCA_CTRL, 0x6666);
+ /* ACKTO for IOT issue. */
+ rtl_write_byte(rtlpriv, REG_ACKTO, 0x40);
+ /* Set Spec SIFS (used in NAV) */
+ rtl_write_word(rtlpriv, REG_SPEC_SIFS, 0x1010);
+ rtl_write_word(rtlpriv, REG_MAC_SPEC_SIFS, 0x1010);
+ /* Set SIFS for CCK */
+ rtl_write_word(rtlpriv, REG_SIFS_CTX, 0x1010);
+ /* Set SIFS for OFDM */
+ rtl_write_word(rtlpriv, REG_SIFS_TRX, 0x1010);
+ /* Set Multicast Address. */
+ rtl_write_dword(rtlpriv, REG_MAR, 0xffffffff);
+ rtl_write_dword(rtlpriv, REG_MAR + 4, 0xffffffff);
+ switch (rtlpriv->phy.rf_type) {
+ case RF_1T2R:
+ case RF_1T1R:
+ rtlhal->minspace_cfg = (MAX_MSS_DENSITY_1T << 3);
+ break;
+ case RF_2T2R:
+ case RF_2T2R_GREEN:
+ rtlhal->minspace_cfg = (MAX_MSS_DENSITY_2T << 3);
+ break;
+ }
+}
+
+static void _rtl92de_enable_aspm_back_door(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+
+ rtl_write_byte(rtlpriv, 0x34b, 0x93);
+ rtl_write_word(rtlpriv, 0x350, 0x870c);
+ rtl_write_byte(rtlpriv, 0x352, 0x1);
+ if (ppsc->support_backdoor)
+ rtl_write_byte(rtlpriv, 0x349, 0x1b);
+ else
+ rtl_write_byte(rtlpriv, 0x349, 0x03);
+ rtl_write_word(rtlpriv, 0x350, 0x2718);
+ rtl_write_byte(rtlpriv, 0x352, 0x1);
+}
+
+void rtl92de_enable_hw_security_config(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 sec_reg_value;
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("PairwiseEncAlgorithm = %d GroupEncAlgorithm = %d\n",
+ rtlpriv->sec.pairwise_enc_algorithm,
+ rtlpriv->sec.group_enc_algorithm));
+ if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) {
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+ ("not open hw encryption\n"));
+ return;
+ }
+ sec_reg_value = SCR_TXENCENABLE | SCR_RXENCENABLE;
+ if (rtlpriv->sec.use_defaultkey) {
+ sec_reg_value |= SCR_TXUSEDK;
+ sec_reg_value |= SCR_RXUSEDK;
+ }
+ sec_reg_value |= (SCR_RXBCUSEDK | SCR_TXBCUSEDK);
+ rtl_write_byte(rtlpriv, REG_CR + 1, 0x02);
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
+ ("The SECR-value %x\n", sec_reg_value));
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value);
+}
+
+int rtl92de_hw_init(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ bool rtstatus = true;
+ u8 tmp_u1b;
+ int i;
+ int err;
+ unsigned long flags;
+
+ rtlpci->being_init_adapter = true;
+ rtlpci->init_ready = false;
+ spin_lock_irqsave(&globalmutex_for_power_and_efuse, flags);
+ /* we should do iqk after disable/enable */
+ rtl92d_phy_reset_iqk_result(hw);
+ /* rtlpriv->intf_ops->disable_aspm(hw); */
+ rtstatus = _rtl92de_init_mac(hw);
+ if (rtstatus != true) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("Init MAC failed\n"));
+ err = 1;
+ spin_unlock_irqrestore(&globalmutex_for_power_and_efuse, flags);
+ return err;
+ }
+ err = rtl92d_download_fw(hw);
+ spin_unlock_irqrestore(&globalmutex_for_power_and_efuse, flags);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("Failed to download FW. Init HW "
+ "without FW..\n"));
+ rtlhal->fw_ready = false;
+ return 1;
+ } else {
+ rtlhal->fw_ready = true;
+ }
+ rtlhal->last_hmeboxnum = 0;
+ rtlpriv->psc.fw_current_inpsmode = false;
+
+ tmp_u1b = rtl_read_byte(rtlpriv, 0x605);
+ tmp_u1b = tmp_u1b | 0x30;
+ rtl_write_byte(rtlpriv, 0x605, tmp_u1b);
+
+ if (rtlhal->earlymode_enable) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("EarlyMode Enabled!!!\n"));
+
+ tmp_u1b = rtl_read_byte(rtlpriv, 0x4d0);
+ tmp_u1b = tmp_u1b | 0x1f;
+ rtl_write_byte(rtlpriv, 0x4d0, tmp_u1b);
+
+ rtl_write_byte(rtlpriv, 0x4d3, 0x80);
+
+ tmp_u1b = rtl_read_byte(rtlpriv, 0x605);
+ tmp_u1b = tmp_u1b | 0x40;
+ rtl_write_byte(rtlpriv, 0x605, tmp_u1b);
+ }
+
+ if (mac->rdg_en) {
+ rtl_write_byte(rtlpriv, REG_RD_CTRL, 0xff);
+ rtl_write_word(rtlpriv, REG_RD_NAV_NXT, 0x200);
+ rtl_write_byte(rtlpriv, REG_RD_RESP_PKT_TH, 0x05);
+ }
+
+ rtl92d_phy_mac_config(hw);
+ /* because last function modify RCR, so we update
+ * rcr var here, or TP will unstable for receive_config
+ * is wrong, RX RCR_ACRC32 will cause TP unstabel & Rx
+ * RCR_APP_ICV will cause mac80211 unassoc for cisco 1252*/
+ rtlpci->receive_config = rtl_read_dword(rtlpriv, REG_RCR);
+ rtlpci->receive_config &= ~(RCR_ACRC32 | RCR_AICV);
+
+ rtl92d_phy_bb_config(hw);
+
+ rtlphy->rf_mode = RF_OP_BY_SW_3WIRE;
+ /* set before initialize RF */
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0xf);
+
+ /* config RF */
+ rtl92d_phy_rf_config(hw);
+
+ /* After read predefined TXT, we must set BB/MAC/RF
+ * register as our requirement */
+ /* After load BB,RF params,we need do more for 92D. */
+ rtl92d_update_bbrf_configuration(hw);
+ /* set default value after initialize RF, */
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0);
+ rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0,
+ RF_CHNLBW, BRFREGOFFSETMASK);
+ rtlphy->rfreg_chnlval[1] = rtl_get_rfreg(hw, (enum radio_path)1,
+ RF_CHNLBW, BRFREGOFFSETMASK);
+
+ /*---- Set CCK and OFDM Block "ON"----*/
+ if (rtlhal->current_bandtype == BAND_ON_2_4G)
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1);
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1);
+ if (rtlhal->interfaceindex == 0) {
+ /* RFPGA0_ANALOGPARAMETER2: cck clock select,
+ * set to 20MHz by default */
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10) |
+ BIT(11), 3);
+ } else {
+ /* Mac1 */
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(11) |
+ BIT(10), 3);
+ }
+
+ _rtl92de_hw_configure(hw);
+
+ /* reset hw sec */
+ rtl_cam_reset_all_entry(hw);
+ rtl92de_enable_hw_security_config(hw);
+
+ /* Read EEPROM TX power index and PHY_REG_PG.txt to capture correct */
+ /* TX power index for different rate set. */
+ rtl92d_phy_get_hw_reg_originalvalue(hw);
+ rtl92d_phy_set_txpower_level(hw, rtlphy->current_channel);
+
+ ppsc->rfpwr_state = ERFON;
+
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, mac->mac_addr);
+
+ _rtl92de_enable_aspm_back_door(hw);
+ /* rtlpriv->intf_ops->enable_aspm(hw); */
+
+ rtl92d_dm_init(hw);
+ rtlpci->being_init_adapter = false;
+
+ if (ppsc->rfpwr_state == ERFON) {
+ rtl92d_phy_lc_calibrate(hw);
+ /* 5G and 2.4G must wait sometime to let RF LO ready */
+ if (rtlhal->macphymode == DUALMAC_DUALPHY) {
+ u32 tmp_rega;
+ for (i = 0; i < 10000; i++) {
+ udelay(MAX_STALL_TIME);
+
+ tmp_rega = rtl_get_rfreg(hw,
+ (enum radio_path)RF90_PATH_A,
+ 0x2a, BMASKDWORD);
+
+ if (((tmp_rega & BIT(11)) == BIT(11)))
+ break;
+ }
+ /* check that loop was successful. If not, exit now */
+ if (i == 10000) {
+ rtlpci->init_ready = false;
+ return 1;
+ }
+ }
+ }
+ rtlpci->init_ready = true;
+ return err;
+}
+
+static enum version_8192d _rtl92de_read_chip_version(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ enum version_8192d version = VERSION_NORMAL_CHIP_92D_SINGLEPHY;
+ u32 value32;
+
+ value32 = rtl_read_dword(rtlpriv, REG_SYS_CFG);
+ if (!(value32 & 0x000f0000)) {
+ version = VERSION_TEST_CHIP_92D_SINGLEPHY;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("TEST CHIP!!!\n"));
+ } else {
+ version = VERSION_NORMAL_CHIP_92D_SINGLEPHY;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("Normal CHIP!!!\n"));
+ }
+ return version;
+}
+
+static int _rtl92de_set_media_status(struct ieee80211_hw *hw,
+ enum nl80211_iftype type)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 bt_msr = rtl_read_byte(rtlpriv, MSR);
+ enum led_ctl_mode ledaction = LED_CTL_NO_LINK;
+ u8 bcnfunc_enable;
+
+ bt_msr &= 0xfc;
+
+ if (type == NL80211_IFTYPE_UNSPECIFIED ||
+ type == NL80211_IFTYPE_STATION) {
+ _rtl92de_stop_tx_beacon(hw);
+ _rtl92de_enable_bcn_sub_func(hw);
+ } else if (type == NL80211_IFTYPE_ADHOC ||
+ type == NL80211_IFTYPE_AP) {
+ _rtl92de_resume_tx_beacon(hw);
+ _rtl92de_disable_bcn_sub_func(hw);
+ } else {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("Set HW_VAR_MEDIA_STATUS: No such media "
+ "status(%x).\n", type));
+ }
+ bcnfunc_enable = rtl_read_byte(rtlpriv, REG_BCN_CTRL);
+ switch (type) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ bt_msr |= MSR_NOLINK;
+ ledaction = LED_CTL_LINK;
+ bcnfunc_enable &= 0xF7;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("Set Network type to NO LINK!\n"));
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ bt_msr |= MSR_ADHOC;
+ bcnfunc_enable |= 0x08;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("Set Network type to Ad Hoc!\n"));
+ break;
+ case NL80211_IFTYPE_STATION:
+ bt_msr |= MSR_INFRA;
+ ledaction = LED_CTL_LINK;
+ bcnfunc_enable &= 0xF7;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("Set Network type to STA!\n"));
+ break;
+ case NL80211_IFTYPE_AP:
+ bt_msr |= MSR_AP;
+ bcnfunc_enable |= 0x08;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("Set Network type to AP!\n"));
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Network type %d not support!\n", type));
+ return 1;
+ break;
+
+ }
+ rtl_write_byte(rtlpriv, REG_CR + 2, bt_msr);
+ rtlpriv->cfg->ops->led_control(hw, ledaction);
+ if ((bt_msr & 0xfc) == MSR_AP)
+ rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
+ else
+ rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
+ return 0;
+}
+
+void rtl92de_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ u32 reg_rcr = rtlpci->receive_config;
+
+ if (rtlpriv->psc.rfpwr_state != ERFON)
+ return;
+ if (check_bssid) {
+ reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
+ _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(4));
+ } else if (check_bssid == false) {
+ reg_rcr &= (~(RCR_CBSSID_DATA | RCR_CBSSID_BCN));
+ _rtl92de_set_bcn_ctrl_reg(hw, BIT(4), 0);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
+ }
+}
+
+int rtl92de_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (_rtl92de_set_media_status(hw, type))
+ return -EOPNOTSUPP;
+
+ /* check bssid */
+ if (rtlpriv->mac80211.link_state == MAC80211_LINKED) {
+ if (type != NL80211_IFTYPE_AP)
+ rtl92de_set_check_bssid(hw, true);
+ } else {
+ rtl92de_set_check_bssid(hw, false);
+ }
+ return 0;
+}
+
+/* do iqk or reload iqk */
+/* windows just rtl92d_phy_reload_iqk_setting in set channel,
+ * but it's very strict for time sequence so we add
+ * rtl92d_phy_reload_iqk_setting here */
+void rtl92d_linked_set_reg(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u8 indexforchannel;
+ u8 channel = rtlphy->current_channel;
+
+ indexforchannel = rtl92d_get_rightchnlplace_for_iqk(channel);
+ if (!rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done) {
+ RT_TRACE(rtlpriv, COMP_SCAN | COMP_INIT, DBG_DMESG,
+ ("Do IQK for channel:%d.\n", channel));
+ rtl92d_phy_iq_calibrate(hw);
+ }
+}
+
+/* don't set REG_EDCA_BE_PARAM here because
+ * mac80211 will send pkt when scan */
+void rtl92de_set_qos(struct ieee80211_hw *hw, int aci)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ rtl92d_dm_init_edca_turbo(hw);
+ return;
+ switch (aci) {
+ case AC1_BK:
+ rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f);
+ break;
+ case AC0_BE:
+ break;
+ case AC2_VI:
+ rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x5e4322);
+ break;
+ case AC3_VO:
+ rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x2f3222);
+ break;
+ default:
+ RT_ASSERT(false, ("invalid aci: %d !\n", aci));
+ break;
+ }
+}
+
+void rtl92de_enable_interrupt(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF);
+ rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF);
+}
+
+void rtl92de_disable_interrupt(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED);
+ rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED);
+ synchronize_irq(rtlpci->pdev->irq);
+}
+
+static void _rtl92de_poweroff_adapter(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 u1b_tmp;
+ unsigned long flags;
+
+ rtlpriv->intf_ops->enable_aspm(hw);
+ rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x00);
+ rtl_set_bbreg(hw, RFPGA0_XCD_RFPARAMETER, BIT(3), 0);
+ rtl_set_bbreg(hw, RFPGA0_XCD_RFPARAMETER, BIT(15), 0);
+
+ /* 0x20:value 05-->04 */
+ rtl_write_byte(rtlpriv, REG_LDOA15_CTRL, 0x04);
+
+ /* ==== Reset digital sequence ====== */
+ rtl92d_firmware_selfreset(hw);
+
+ /* f. SYS_FUNC_EN 0x03[7:0]=0x51 reset MCU, MAC register, DCORE */
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, 0x51);
+
+ /* g. MCUFWDL 0x80[1:0]=0 reset MCU ready status */
+ rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00);
+
+ /* ==== Pull GPIO PIN to balance level and LED control ====== */
+
+ /* h. GPIO_PIN_CTRL 0x44[31:0]=0x000 */
+ rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x00000000);
+
+ /* i. Value = GPIO_PIN_CTRL[7:0] */
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_GPIO_PIN_CTRL);
+
+ /* j. GPIO_PIN_CTRL 0x44[31:0] = 0x00FF0000 | (value <<8); */
+ /* write external PIN level */
+ rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL,
+ 0x00FF0000 | (u1b_tmp << 8));
+
+ /* k. GPIO_MUXCFG 0x42 [15:0] = 0x0780 */
+ rtl_write_word(rtlpriv, REG_GPIO_IO_SEL, 0x0790);
+
+ /* l. LEDCFG 0x4C[15:0] = 0x8080 */
+ rtl_write_word(rtlpriv, REG_LEDCFG0, 0x8080);
+
+ /* ==== Disable analog sequence === */
+
+ /* m. AFE_PLL_CTRL[7:0] = 0x80 disable PLL */
+ rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL, 0x80);
+
+ /* n. SPS0_CTRL 0x11[7:0] = 0x22 enter PFM mode */
+ rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x23);
+
+ /* o. AFE_XTAL_CTRL 0x24[7:0] = 0x0E disable XTAL, if No BT COEX */
+ rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL, 0x0e);
+
+ /* p. RSV_CTRL 0x1C[7:0] = 0x0E lock ISO/CLK/Power control register */
+ rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x0e);
+
+ /* ==== interface into suspend === */
+
+ /* q. APS_FSMCO[15:8] = 0x58 PCIe suspend mode */
+ /* According to power document V11, we need to set this */
+ /* value as 0x18. Otherwise, we may not L0s sometimes. */
+ /* This indluences power consumption. Bases on SD1's test, */
+ /* set as 0x00 do not affect power current. And if it */
+ /* is set as 0x18, they had ever met auto load fail problem. */
+ rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, 0x10);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("In PowerOff,reg0x%x=%X\n", REG_SPS0_CTRL,
+ rtl_read_byte(rtlpriv, REG_SPS0_CTRL)));
+ /* r. Note: for PCIe interface, PON will not turn */
+ /* off m-bias and BandGap in PCIe suspend mode. */
+
+ /* 0x17[7] 1b': power off in process 0b' : power off over */
+ if (rtlpriv->rtlhal.macphymode != SINGLEMAC_SINGLEPHY) {
+ spin_lock_irqsave(&globalmutex_power, flags);
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS);
+ u1b_tmp &= (~BIT(7));
+ rtl_write_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS, u1b_tmp);
+ spin_unlock_irqrestore(&globalmutex_power, flags);
+ }
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("<=======\n"));
+}
+
+void rtl92de_card_disable(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ enum nl80211_iftype opmode;
+
+ mac->link_state = MAC80211_NOLINK;
+ opmode = NL80211_IFTYPE_UNSPECIFIED;
+ _rtl92de_set_media_status(hw, opmode);
+
+ if (rtlpci->driver_is_goingto_unload ||
+ ppsc->rfoff_reason > RF_CHANGE_BY_PS)
+ rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF);
+ RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
+ /* Power sequence for each MAC. */
+ /* a. stop tx DMA */
+ /* b. close RF */
+ /* c. clear rx buf */
+ /* d. stop rx DMA */
+ /* e. reset MAC */
+
+ /* a. stop tx DMA */
+ rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, 0xFE);
+ udelay(50);
+
+ /* b. TXPAUSE 0x522[7:0] = 0xFF Pause MAC TX queue */
+
+ /* c. ========RF OFF sequence========== */
+ /* 0x88c[23:20] = 0xf. */
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0xf);
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00);
+
+ /* APSD_CTRL 0x600[7:0] = 0x40 */
+ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40);
+
+ /* Close antenna 0,0xc04,0xd04 */
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0);
+ rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0);
+
+ /* SYS_FUNC_EN 0x02[7:0] = 0xE2 reset BB state machine */
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2);
+
+ /* Mac0 can not do Global reset. Mac1 can do. */
+ /* SYS_FUNC_EN 0x02[7:0] = 0xE0 reset BB state machine */
+ if (rtlpriv->rtlhal.interfaceindex == 1)
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE0);
+ udelay(50);
+
+ /* d. stop tx/rx dma before disable REG_CR (0x100) to fix */
+ /* dma hang issue when disable/enable device. */
+ rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, 0xff);
+ udelay(50);
+ rtl_write_byte(rtlpriv, REG_CR, 0x0);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("==> Do power off.......\n"));
+ if (rtl92d_phy_check_poweroff(hw))
+ _rtl92de_poweroff_adapter(hw);
+ return;
+}
+
+void rtl92de_interrupt_recognized(struct ieee80211_hw *hw,
+ u32 *p_inta, u32 *p_intb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ *p_inta = rtl_read_dword(rtlpriv, ISR) & rtlpci->irq_mask[0];
+ rtl_write_dword(rtlpriv, ISR, *p_inta);
+
+ /*
+ * *p_intb = rtl_read_dword(rtlpriv, REG_HISRE) & rtlpci->irq_mask[1];
+ * rtl_write_dword(rtlpriv, ISR + 4, *p_intb);
+ */
+}
+
+void rtl92de_set_beacon_related_registers(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ u16 bcn_interval, atim_window;
+
+ bcn_interval = mac->beacon_interval;
+ atim_window = 2;
+ /*rtl92de_disable_interrupt(hw); */
+ rtl_write_word(rtlpriv, REG_ATIMWND, atim_window);
+ rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval);
+ rtl_write_word(rtlpriv, REG_BCNTCFG, 0x660f);
+ rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x20);
+ if (rtlpriv->rtlhal.current_bandtype == BAND_ON_5G)
+ rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x30);
+ else
+ rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x20);
+ rtl_write_byte(rtlpriv, 0x606, 0x30);
+}
+
+void rtl92de_set_beacon_interval(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ u16 bcn_interval = mac->beacon_interval;
+
+ RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
+ ("beacon_interval:%d\n", bcn_interval));
+ /* rtl92de_disable_interrupt(hw); */
+ rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval);
+ /* rtl92de_enable_interrupt(hw); */
+}
+
+void rtl92de_update_interrupt_mask(struct ieee80211_hw *hw,
+ u32 add_msr, u32 rm_msr)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD,
+ ("add_msr:%x, rm_msr:%x\n", add_msr, rm_msr));
+ if (add_msr)
+ rtlpci->irq_mask[0] |= add_msr;
+ if (rm_msr)
+ rtlpci->irq_mask[0] &= (~rm_msr);
+ rtl92de_disable_interrupt(hw);
+ rtl92de_enable_interrupt(hw);
+}
+
+static void _rtl92de_readpowervalue_fromprom(struct txpower_info *pwrinfo,
+ u8 *rom_content, bool autoLoadfail)
+{
+ u32 rfpath, eeaddr, group, offset1, offset2;
+ u8 i;
+
+ memset(pwrinfo, 0, sizeof(struct txpower_info));
+ if (autoLoadfail) {
+ for (group = 0; group < CHANNEL_GROUP_MAX; group++) {
+ for (rfpath = 0; rfpath < RF6052_MAX_PATH; rfpath++) {
+ if (group < CHANNEL_GROUP_MAX_2G) {
+ pwrinfo->cck_index[rfpath][group] =
+ EEPROM_DEFAULT_TXPOWERLEVEL_2G;
+ pwrinfo->ht40_1sindex[rfpath][group] =
+ EEPROM_DEFAULT_TXPOWERLEVEL_2G;
+ } else {
+ pwrinfo->ht40_1sindex[rfpath][group] =
+ EEPROM_DEFAULT_TXPOWERLEVEL_5G;
+ }
+ pwrinfo->ht40_2sindexdiff[rfpath][group] =
+ EEPROM_DEFAULT_HT40_2SDIFF;
+ pwrinfo->ht20indexdiff[rfpath][group] =
+ EEPROM_DEFAULT_HT20_DIFF;
+ pwrinfo->ofdmindexdiff[rfpath][group] =
+ EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF;
+ pwrinfo->ht40maxoffset[rfpath][group] =
+ EEPROM_DEFAULT_HT40_PWRMAXOFFSET;
+ pwrinfo->ht20maxoffset[rfpath][group] =
+ EEPROM_DEFAULT_HT20_PWRMAXOFFSET;
+ }
+ }
+ for (i = 0; i < 3; i++) {
+ pwrinfo->tssi_a[i] = EEPROM_DEFAULT_TSSI;
+ pwrinfo->tssi_b[i] = EEPROM_DEFAULT_TSSI;
+ }
+ return;
+ }
+
+ /* Maybe autoload OK,buf the tx power index value is not filled.
+ * If we find it, we set it to default value. */
+ for (rfpath = 0; rfpath < RF6052_MAX_PATH; rfpath++) {
+ for (group = 0; group < CHANNEL_GROUP_MAX_2G; group++) {
+ eeaddr = EEPROM_CCK_TX_PWR_INX_2G + (rfpath * 3)
+ + group;
+ pwrinfo->cck_index[rfpath][group] =
+ (rom_content[eeaddr] == 0xFF) ?
+ (eeaddr > 0x7B ?
+ EEPROM_DEFAULT_TXPOWERLEVEL_5G :
+ EEPROM_DEFAULT_TXPOWERLEVEL_2G) :
+ rom_content[eeaddr];
+ }
+ }
+ for (rfpath = 0; rfpath < RF6052_MAX_PATH; rfpath++) {
+ for (group = 0; group < CHANNEL_GROUP_MAX; group++) {
+ offset1 = group / 3;
+ offset2 = group % 3;
+ eeaddr = EEPROM_HT40_1S_TX_PWR_INX_2G + (rfpath * 3) +
+ offset2 + offset1 * 21;
+ pwrinfo->ht40_1sindex[rfpath][group] =
+ (rom_content[eeaddr] == 0xFF) ? (eeaddr > 0x7B ?
+ EEPROM_DEFAULT_TXPOWERLEVEL_5G :
+ EEPROM_DEFAULT_TXPOWERLEVEL_2G) :
+ rom_content[eeaddr];
+ }
+ }
+ /* These just for 92D efuse offset. */
+ for (group = 0; group < CHANNEL_GROUP_MAX; group++) {
+ for (rfpath = 0; rfpath < RF6052_MAX_PATH; rfpath++) {
+ int base1 = EEPROM_HT40_2S_TX_PWR_INX_DIFF_2G;
+
+ offset1 = group / 3;
+ offset2 = group % 3;
+
+ if (rom_content[base1 + offset2 + offset1 * 21] != 0xFF)
+ pwrinfo->ht40_2sindexdiff[rfpath][group] =
+ (rom_content[base1 +
+ offset2 + offset1 * 21] >> (rfpath * 4))
+ & 0xF;
+ else
+ pwrinfo->ht40_2sindexdiff[rfpath][group] =
+ EEPROM_DEFAULT_HT40_2SDIFF;
+ if (rom_content[EEPROM_HT20_TX_PWR_INX_DIFF_2G + offset2
+ + offset1 * 21] != 0xFF)
+ pwrinfo->ht20indexdiff[rfpath][group] =
+ (rom_content[EEPROM_HT20_TX_PWR_INX_DIFF_2G
+ + offset2 + offset1 * 21] >> (rfpath * 4))
+ & 0xF;
+ else
+ pwrinfo->ht20indexdiff[rfpath][group] =
+ EEPROM_DEFAULT_HT20_DIFF;
+ if (rom_content[EEPROM_OFDM_TX_PWR_INX_DIFF_2G + offset2
+ + offset1 * 21] != 0xFF)
+ pwrinfo->ofdmindexdiff[rfpath][group] =
+ (rom_content[EEPROM_OFDM_TX_PWR_INX_DIFF_2G
+ + offset2 + offset1 * 21] >> (rfpath * 4))
+ & 0xF;
+ else
+ pwrinfo->ofdmindexdiff[rfpath][group] =
+ EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF;
+ if (rom_content[EEPROM_HT40_MAX_PWR_OFFSET_2G + offset2
+ + offset1 * 21] != 0xFF)
+ pwrinfo->ht40maxoffset[rfpath][group] =
+ (rom_content[EEPROM_HT40_MAX_PWR_OFFSET_2G
+ + offset2 + offset1 * 21] >> (rfpath * 4))
+ & 0xF;
+ else
+ pwrinfo->ht40maxoffset[rfpath][group] =
+ EEPROM_DEFAULT_HT40_PWRMAXOFFSET;
+ if (rom_content[EEPROM_HT20_MAX_PWR_OFFSET_2G + offset2
+ + offset1 * 21] != 0xFF)
+ pwrinfo->ht20maxoffset[rfpath][group] =
+ (rom_content[EEPROM_HT20_MAX_PWR_OFFSET_2G +
+ offset2 + offset1 * 21] >> (rfpath * 4)) &
+ 0xF;
+ else
+ pwrinfo->ht20maxoffset[rfpath][group] =
+ EEPROM_DEFAULT_HT20_PWRMAXOFFSET;
+ }
+ }
+ if (rom_content[EEPROM_TSSI_A_5G] != 0xFF) {
+ /* 5GL */
+ pwrinfo->tssi_a[0] = rom_content[EEPROM_TSSI_A_5G] & 0x3F;
+ pwrinfo->tssi_b[0] = rom_content[EEPROM_TSSI_B_5G] & 0x3F;
+ /* 5GM */
+ pwrinfo->tssi_a[1] = rom_content[EEPROM_TSSI_AB_5G] & 0x3F;
+ pwrinfo->tssi_b[1] =
+ (rom_content[EEPROM_TSSI_AB_5G] & 0xC0) >> 6 |
+ (rom_content[EEPROM_TSSI_AB_5G + 1] & 0x0F) << 2;
+ /* 5GH */
+ pwrinfo->tssi_a[2] = (rom_content[EEPROM_TSSI_AB_5G + 1] &
+ 0xF0) >> 4 |
+ (rom_content[EEPROM_TSSI_AB_5G + 2] & 0x03) << 4;
+ pwrinfo->tssi_b[2] = (rom_content[EEPROM_TSSI_AB_5G + 2] &
+ 0xFC) >> 2;
+ } else {
+ for (i = 0; i < 3; i++) {
+ pwrinfo->tssi_a[i] = EEPROM_DEFAULT_TSSI;
+ pwrinfo->tssi_b[i] = EEPROM_DEFAULT_TSSI;
+ }
+ }
+}
+
+static void _rtl92de_read_txpower_info(struct ieee80211_hw *hw,
+ bool autoload_fail, u8 *hwinfo)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct txpower_info pwrinfo;
+ u8 tempval[2], i, pwr, diff;
+ u32 ch, rfPath, group;
+
+ _rtl92de_readpowervalue_fromprom(&pwrinfo, hwinfo, autoload_fail);
+ if (!autoload_fail) {
+ /* bit0~2 */
+ rtlefuse->eeprom_regulatory = (hwinfo[EEPROM_RF_OPT1] & 0x7);
+ rtlefuse->eeprom_thermalmeter =
+ hwinfo[EEPROM_THERMAL_METER] & 0x1f;
+ rtlefuse->crystalcap = hwinfo[EEPROM_XTAL_K];
+ tempval[0] = hwinfo[EEPROM_IQK_DELTA] & 0x03;
+ tempval[1] = (hwinfo[EEPROM_LCK_DELTA] & 0x0C) >> 2;
+ rtlefuse->txpwr_fromeprom = true;
+ if (IS_92D_D_CUT(rtlpriv->rtlhal.version) ||
+ IS_92D_E_CUT(rtlpriv->rtlhal.version)) {
+ rtlefuse->internal_pa_5g[0] =
+ !((hwinfo[EEPROM_TSSI_A_5G] & BIT(6)) >> 6);
+ rtlefuse->internal_pa_5g[1] =
+ !((hwinfo[EEPROM_TSSI_B_5G] & BIT(6)) >> 6);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+ ("Is D cut,Internal PA0 %d Internal PA1 %d\n",
+ rtlefuse->internal_pa_5g[0],
+ rtlefuse->internal_pa_5g[1]))
+ }
+ rtlefuse->eeprom_c9 = hwinfo[EEPROM_RF_OPT6];
+ rtlefuse->eeprom_cc = hwinfo[EEPROM_RF_OPT7];
+ } else {
+ rtlefuse->eeprom_regulatory = 0;
+ rtlefuse->eeprom_thermalmeter = EEPROM_DEFAULT_THERMALMETER;
+ rtlefuse->crystalcap = EEPROM_DEFAULT_CRYSTALCAP;
+ tempval[0] = tempval[1] = 3;
+ }
+
+ /* Use default value to fill parameters if
+ * efuse is not filled on some place. */
+
+ /* ThermalMeter from EEPROM */
+ if (rtlefuse->eeprom_thermalmeter < 0x06 ||
+ rtlefuse->eeprom_thermalmeter > 0x1c)
+ rtlefuse->eeprom_thermalmeter = 0x12;
+ rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter;
+
+ /* check XTAL_K */
+ if (rtlefuse->crystalcap == 0xFF)
+ rtlefuse->crystalcap = 0;
+ if (rtlefuse->eeprom_regulatory > 3)
+ rtlefuse->eeprom_regulatory = 0;
+
+ for (i = 0; i < 2; i++) {
+ switch (tempval[i]) {
+ case 0:
+ tempval[i] = 5;
+ break;
+ case 1:
+ tempval[i] = 4;
+ break;
+ case 2:
+ tempval[i] = 3;
+ break;
+ case 3:
+ default:
+ tempval[i] = 0;
+ break;
+ }
+ }
+
+ rtlefuse->delta_iqk = tempval[0];
+ if (tempval[1] > 0)
+ rtlefuse->delta_lck = tempval[1] - 1;
+ if (rtlefuse->eeprom_c9 == 0xFF)
+ rtlefuse->eeprom_c9 = 0x00;
+ RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD,
+ ("EEPROMRegulatory = 0x%x\n", rtlefuse->eeprom_regulatory));
+ RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD,
+ ("ThermalMeter = 0x%x\n", rtlefuse->eeprom_thermalmeter));
+ RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD,
+ ("CrystalCap = 0x%x\n", rtlefuse->crystalcap));
+ RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD,
+ ("Delta_IQK = 0x%x Delta_LCK = 0x%x\n", rtlefuse->delta_iqk,
+ rtlefuse->delta_lck));
+
+ for (rfPath = 0; rfPath < RF6052_MAX_PATH; rfPath++) {
+ for (ch = 0; ch < CHANNEL_MAX_NUMBER; ch++) {
+ group = rtl92d_get_chnlgroup_fromarray((u8) ch);
+ if (ch < CHANNEL_MAX_NUMBER_2G)
+ rtlefuse->txpwrlevel_cck[rfPath][ch] =
+ pwrinfo.cck_index[rfPath][group];
+ rtlefuse->txpwrlevel_ht40_1s[rfPath][ch] =
+ pwrinfo.ht40_1sindex[rfPath][group];
+ rtlefuse->txpwr_ht20diff[rfPath][ch] =
+ pwrinfo.ht20indexdiff[rfPath][group];
+ rtlefuse->txpwr_legacyhtdiff[rfPath][ch] =
+ pwrinfo.ofdmindexdiff[rfPath][group];
+ rtlefuse->pwrgroup_ht20[rfPath][ch] =
+ pwrinfo.ht20maxoffset[rfPath][group];
+ rtlefuse->pwrgroup_ht40[rfPath][ch] =
+ pwrinfo.ht40maxoffset[rfPath][group];
+ pwr = pwrinfo.ht40_1sindex[rfPath][group];
+ diff = pwrinfo.ht40_2sindexdiff[rfPath][group];
+ rtlefuse->txpwrlevel_ht40_2s[rfPath][ch] =
+ (pwr > diff) ? (pwr - diff) : 0;
+ }
+ }
+}
+
+static void _rtl92de_read_macphymode_from_prom(struct ieee80211_hw *hw,
+ u8 *content)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 macphy_crvalue = content[EEPROM_MAC_FUNCTION];
+
+ if (macphy_crvalue & BIT(3)) {
+ rtlhal->macphymode = SINGLEMAC_SINGLEPHY;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("MacPhyMode SINGLEMAC_SINGLEPHY\n"));
+ } else {
+ rtlhal->macphymode = DUALMAC_DUALPHY;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("MacPhyMode DUALMAC_DUALPHY\n"));
+ }
+}
+
+static void _rtl92de_read_macphymode_and_bandtype(struct ieee80211_hw *hw,
+ u8 *content)
+{
+ _rtl92de_read_macphymode_from_prom(hw, content);
+ rtl92d_phy_config_macphymode(hw);
+ rtl92d_phy_config_macphymode_info(hw);
+}
+
+static void _rtl92de_efuse_update_chip_version(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ enum version_8192d chipver = rtlpriv->rtlhal.version;
+ u8 cutvalue[2];
+ u16 chipvalue;
+
+ rtlpriv->intf_ops->read_efuse_byte(hw, EEPROME_CHIP_VERSION_H,
+ &cutvalue[1]);
+ rtlpriv->intf_ops->read_efuse_byte(hw, EEPROME_CHIP_VERSION_L,
+ &cutvalue[0]);
+ chipvalue = (cutvalue[1] << 8) | cutvalue[0];
+ switch (chipvalue) {
+ case 0xAA55:
+ chipver |= CHIP_92D_C_CUT;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("C-CUT!!!\n"));
+ break;
+ case 0x9966:
+ chipver |= CHIP_92D_D_CUT;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("D-CUT!!!\n"));
+ break;
+ default:
+ chipver |= CHIP_92D_D_CUT;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, ("Unkown CUT!\n"));
+ break;
+ }
+ rtlpriv->rtlhal.version = chipver;
+}
+
+static void _rtl92de_read_adapter_info(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u16 i, usvalue;
+ u8 hwinfo[HWSET_MAX_SIZE];
+ u16 eeprom_id;
+ unsigned long flags;
+
+ if (rtlefuse->epromtype == EEPROM_BOOT_EFUSE) {
+ spin_lock_irqsave(&globalmutex_for_power_and_efuse, flags);
+ rtl_efuse_shadow_map_update(hw);
+ _rtl92de_efuse_update_chip_version(hw);
+ spin_unlock_irqrestore(&globalmutex_for_power_and_efuse, flags);
+ memcpy((void *)hwinfo, (void *)&rtlefuse->efuse_map
+ [EFUSE_INIT_MAP][0],
+ HWSET_MAX_SIZE);
+ } else if (rtlefuse->epromtype == EEPROM_93C46) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("RTL819X Not boot from eeprom, check it !!"));
+ }
+ RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, ("MAP\n"),
+ hwinfo, HWSET_MAX_SIZE);
+
+ eeprom_id = *((u16 *)&hwinfo[0]);
+ if (eeprom_id != RTL8190_EEPROM_ID) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("EEPROM ID(%#x) is invalid!!\n", eeprom_id));
+ rtlefuse->autoload_failflag = true;
+ } else {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("Autoload OK\n"));
+ rtlefuse->autoload_failflag = false;
+ }
+ if (rtlefuse->autoload_failflag) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("RTL819X Not boot from eeprom, check it !!"));
+ return;
+ }
+ rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMER_ID];
+ _rtl92de_read_macphymode_and_bandtype(hw, hwinfo);
+
+ /* VID, DID SE 0xA-D */
+ rtlefuse->eeprom_vid = *(u16 *)&hwinfo[EEPROM_VID];
+ rtlefuse->eeprom_did = *(u16 *)&hwinfo[EEPROM_DID];
+ rtlefuse->eeprom_svid = *(u16 *)&hwinfo[EEPROM_SVID];
+ rtlefuse->eeprom_smid = *(u16 *)&hwinfo[EEPROM_SMID];
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("EEPROMId = 0x%4x\n", eeprom_id));
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("EEPROM VID = 0x%4x\n", rtlefuse->eeprom_vid));
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("EEPROM DID = 0x%4x\n", rtlefuse->eeprom_did));
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("EEPROM SVID = 0x%4x\n", rtlefuse->eeprom_svid));
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("EEPROM SMID = 0x%4x\n", rtlefuse->eeprom_smid));
+
+ /* Read Permanent MAC address */
+ if (rtlhal->interfaceindex == 0) {
+ for (i = 0; i < 6; i += 2) {
+ usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR_MAC0_92D + i];
+ *((u16 *) (&rtlefuse->dev_addr[i])) = usvalue;
+ }
+ } else {
+ for (i = 0; i < 6; i += 2) {
+ usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR_MAC1_92D + i];
+ *((u16 *) (&rtlefuse->dev_addr[i])) = usvalue;
+ }
+ }
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR,
+ rtlefuse->dev_addr);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+ ("%pM\n", rtlefuse->dev_addr));
+ _rtl92de_read_txpower_info(hw, rtlefuse->autoload_failflag, hwinfo);
+
+ /* Read Channel Plan */
+ switch (rtlhal->bandset) {
+ case BAND_ON_2_4G:
+ rtlefuse->channel_plan = COUNTRY_CODE_TELEC;
+ break;
+ case BAND_ON_5G:
+ rtlefuse->channel_plan = COUNTRY_CODE_FCC;
+ break;
+ case BAND_ON_BOTH:
+ rtlefuse->channel_plan = COUNTRY_CODE_FCC;
+ break;
+ default:
+ rtlefuse->channel_plan = COUNTRY_CODE_FCC;
+ break;
+ }
+ rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION];
+ rtlefuse->txpwr_fromeprom = true;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid));
+}
+
+void rtl92de_read_eeprom_info(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 tmp_u1b;
+
+ rtlhal->version = _rtl92de_read_chip_version(hw);
+ tmp_u1b = rtl_read_byte(rtlpriv, REG_9346CR);
+ rtlefuse->autoload_status = tmp_u1b;
+ if (tmp_u1b & BIT(4)) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, ("Boot from EEPROM\n"));
+ rtlefuse->epromtype = EEPROM_93C46;
+ } else {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, ("Boot from EFUSE\n"));
+ rtlefuse->epromtype = EEPROM_BOOT_EFUSE;
+ }
+ if (tmp_u1b & BIT(5)) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("Autoload OK\n"));
+
+ rtlefuse->autoload_failflag = false;
+ _rtl92de_read_adapter_info(hw);
+ } else {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("Autoload ERR!!\n"));
+ }
+ return;
+}
+
+static void rtl92de_update_hal_rate_table(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u32 ratr_value;
+ u8 ratr_index = 0;
+ u8 nmode = mac->ht_enable;
+ u8 mimo_ps = IEEE80211_SMPS_OFF;
+ u16 shortgi_rate;
+ u32 tmp_ratr_value;
+ u8 curtxbw_40mhz = mac->bw_40;
+ u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ?
+ 1 : 0;
+ u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ?
+ 1 : 0;
+ enum wireless_mode wirelessmode = mac->mode;
+
+ if (rtlhal->current_bandtype == BAND_ON_5G)
+ ratr_value = sta->supp_rates[1] << 4;
+ else
+ ratr_value = sta->supp_rates[0];
+ ratr_value |= (sta->ht_cap.mcs.rx_mask[1] << 20 |
+ sta->ht_cap.mcs.rx_mask[0] << 12);
+ switch (wirelessmode) {
+ case WIRELESS_MODE_A:
+ ratr_value &= 0x00000FF0;
+ break;
+ case WIRELESS_MODE_B:
+ if (ratr_value & 0x0000000c)
+ ratr_value &= 0x0000000d;
+ else
+ ratr_value &= 0x0000000f;
+ break;
+ case WIRELESS_MODE_G:
+ ratr_value &= 0x00000FF5;
+ break;
+ case WIRELESS_MODE_N_24G:
+ case WIRELESS_MODE_N_5G:
+ nmode = 1;
+ if (mimo_ps == IEEE80211_SMPS_STATIC) {
+ ratr_value &= 0x0007F005;
+ } else {
+ u32 ratr_mask;
+
+ if (get_rf_type(rtlphy) == RF_1T2R ||
+ get_rf_type(rtlphy) == RF_1T1R) {
+ ratr_mask = 0x000ff005;
+ } else {
+ ratr_mask = 0x0f0ff005;
+ }
+
+ ratr_value &= ratr_mask;
+ }
+ break;
+ default:
+ if (rtlphy->rf_type == RF_1T2R)
+ ratr_value &= 0x000ff0ff;
+ else
+ ratr_value &= 0x0f0ff0ff;
+
+ break;
+ }
+ ratr_value &= 0x0FFFFFFF;
+ if (nmode && ((curtxbw_40mhz && curshortgi_40mhz) ||
+ (!curtxbw_40mhz && curshortgi_20mhz))) {
+ ratr_value |= 0x10000000;
+ tmp_ratr_value = (ratr_value >> 12);
+ for (shortgi_rate = 15; shortgi_rate > 0; shortgi_rate--) {
+ if ((1 << shortgi_rate) & tmp_ratr_value)
+ break;
+ }
+ shortgi_rate = (shortgi_rate << 12) | (shortgi_rate << 8) |
+ (shortgi_rate << 4) | (shortgi_rate);
+ }
+ rtl_write_dword(rtlpriv, REG_ARFR0 + ratr_index * 4, ratr_value);
+ RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG,
+ ("%x\n", rtl_read_dword(rtlpriv, REG_ARFR0)));
+}
+
+static void rtl92de_update_hal_rate_mask(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, u8 rssi_level)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_sta_info *sta_entry = NULL;
+ u32 ratr_bitmap;
+ u8 ratr_index;
+ u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+ ? 1 : 0;
+ u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ?
+ 1 : 0;
+ u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ?
+ 1 : 0;
+ enum wireless_mode wirelessmode = 0;
+ bool shortgi = false;
+ u32 value[2];
+ u8 macid = 0;
+ u8 mimo_ps = IEEE80211_SMPS_OFF;
+
+ sta_entry = (struct rtl_sta_info *) sta->drv_priv;
+ mimo_ps = sta_entry->mimo_ps;
+ wirelessmode = sta_entry->wireless_mode;
+ if (mac->opmode == NL80211_IFTYPE_STATION)
+ curtxbw_40mhz = mac->bw_40;
+ else if (mac->opmode == NL80211_IFTYPE_AP ||
+ mac->opmode == NL80211_IFTYPE_ADHOC)
+ macid = sta->aid + 1;
+
+ if (rtlhal->current_bandtype == BAND_ON_5G)
+ ratr_bitmap = sta->supp_rates[1] << 4;
+ else
+ ratr_bitmap = sta->supp_rates[0];
+ ratr_bitmap |= (sta->ht_cap.mcs.rx_mask[1] << 20 |
+ sta->ht_cap.mcs.rx_mask[0] << 12);
+ switch (wirelessmode) {
+ case WIRELESS_MODE_B:
+ ratr_index = RATR_INX_WIRELESS_B;
+ if (ratr_bitmap & 0x0000000c)
+ ratr_bitmap &= 0x0000000d;
+ else
+ ratr_bitmap &= 0x0000000f;
+ break;
+ case WIRELESS_MODE_G:
+ ratr_index = RATR_INX_WIRELESS_GB;
+
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x00000f00;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x00000ff0;
+ else
+ ratr_bitmap &= 0x00000ff5;
+ break;
+ case WIRELESS_MODE_A:
+ ratr_index = RATR_INX_WIRELESS_G;
+ ratr_bitmap &= 0x00000ff0;
+ break;
+ case WIRELESS_MODE_N_24G:
+ case WIRELESS_MODE_N_5G:
+ if (wirelessmode == WIRELESS_MODE_N_24G)
+ ratr_index = RATR_INX_WIRELESS_NGB;
+ else
+ ratr_index = RATR_INX_WIRELESS_NG;
+ if (mimo_ps == IEEE80211_SMPS_STATIC) {
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x00070000;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x0007f000;
+ else
+ ratr_bitmap &= 0x0007f005;
+ } else {
+ if (rtlphy->rf_type == RF_1T2R ||
+ rtlphy->rf_type == RF_1T1R) {
+ if (curtxbw_40mhz) {
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x000f0000;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x000ff000;
+ else
+ ratr_bitmap &= 0x000ff015;
+ } else {
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x000f0000;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x000ff000;
+ else
+ ratr_bitmap &= 0x000ff005;
+ }
+ } else {
+ if (curtxbw_40mhz) {
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x0f0f0000;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x0f0ff000;
+ else
+ ratr_bitmap &= 0x0f0ff015;
+ } else {
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x0f0f0000;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x0f0ff000;
+ else
+ ratr_bitmap &= 0x0f0ff005;
+ }
+ }
+ }
+ if ((curtxbw_40mhz && curshortgi_40mhz) ||
+ (!curtxbw_40mhz && curshortgi_20mhz)) {
+
+ if (macid == 0)
+ shortgi = true;
+ else if (macid == 1)
+ shortgi = false;
+ }
+ break;
+ default:
+ ratr_index = RATR_INX_WIRELESS_NGB;
+
+ if (rtlphy->rf_type == RF_1T2R)
+ ratr_bitmap &= 0x000ff0ff;
+ else
+ ratr_bitmap &= 0x0f0ff0ff;
+ break;
+ }
+
+ value[0] = (ratr_bitmap & 0x0fffffff) | (ratr_index << 28);
+ value[1] = macid | (shortgi ? 0x20 : 0x00) | 0x80;
+ RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG,
+ ("ratr_bitmap :%x value0:%x value1:%x\n",
+ ratr_bitmap, value[0], value[1]));
+ rtl92d_fill_h2c_cmd(hw, H2C_RA_MASK, 5, (u8 *) value);
+ if (macid != 0)
+ sta_entry->ratr_index = ratr_index;
+}
+
+void rtl92de_update_hal_rate_tbl(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, u8 rssi_level)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (rtlpriv->dm.useramask)
+ rtl92de_update_hal_rate_mask(hw, sta, rssi_level);
+ else
+ rtl92de_update_hal_rate_table(hw, sta);
+}
+
+void rtl92de_update_channel_access_setting(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ u16 sifs_timer;
+
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME,
+ (u8 *)&mac->slot_time);
+ if (!mac->ht_enable)
+ sifs_timer = 0x0a0a;
+ else
+ sifs_timer = 0x1010;
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SIFS, (u8 *)&sifs_timer);
+}
+
+bool rtl92de_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ enum rf_pwrstate e_rfpowerstate_toset;
+ u8 u1tmp;
+ bool actuallyset = false;
+ unsigned long flag;
+
+ if (rtlpci->being_init_adapter)
+ return false;
+ if (ppsc->swrf_processing)
+ return false;
+ spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag);
+ if (ppsc->rfchange_inprogress) {
+ spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
+ return false;
+ } else {
+ ppsc->rfchange_inprogress = true;
+ spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
+ }
+ rtl_write_byte(rtlpriv, REG_MAC_PINMUX_CFG, rtl_read_byte(rtlpriv,
+ REG_MAC_PINMUX_CFG) & ~(BIT(3)));
+ u1tmp = rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL);
+ e_rfpowerstate_toset = (u1tmp & BIT(3)) ? ERFON : ERFOFF;
+ if (ppsc->hwradiooff && (e_rfpowerstate_toset == ERFON)) {
+ RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+ ("GPIOChangeRF - HW Radio ON, RF ON\n"));
+ e_rfpowerstate_toset = ERFON;
+ ppsc->hwradiooff = false;
+ actuallyset = true;
+ } else if ((ppsc->hwradiooff == false)
+ && (e_rfpowerstate_toset == ERFOFF)) {
+ RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+ ("GPIOChangeRF - HW Radio OFF, RF OFF\n"));
+ e_rfpowerstate_toset = ERFOFF;
+ ppsc->hwradiooff = true;
+ actuallyset = true;
+ }
+ if (actuallyset) {
+ spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag);
+ ppsc->rfchange_inprogress = false;
+ spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
+ } else {
+ if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC)
+ RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
+ spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag);
+ ppsc->rfchange_inprogress = false;
+ spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
+ }
+ *valid = 1;
+ return !ppsc->hwradiooff;
+}
+
+void rtl92de_set_key(struct ieee80211_hw *hw, u32 key_index,
+ u8 *p_macaddr, bool is_group, u8 enc_algo,
+ bool is_wepkey, bool clear_all)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u8 *macaddr = p_macaddr;
+ u32 entry_id;
+ bool is_pairwise = false;
+ static u8 cam_const_addr[4][6] = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}
+ };
+ static u8 cam_const_broad[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+ if (clear_all) {
+ u8 idx;
+ u8 cam_offset = 0;
+ u8 clear_number = 5;
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, ("clear_all\n"));
+ for (idx = 0; idx < clear_number; idx++) {
+ rtl_cam_mark_invalid(hw, cam_offset + idx);
+ rtl_cam_empty_entry(hw, cam_offset + idx);
+
+ if (idx < 5) {
+ memset(rtlpriv->sec.key_buf[idx], 0,
+ MAX_KEY_LEN);
+ rtlpriv->sec.key_len[idx] = 0;
+ }
+ }
+ } else {
+ switch (enc_algo) {
+ case WEP40_ENCRYPTION:
+ enc_algo = CAM_WEP40;
+ break;
+ case WEP104_ENCRYPTION:
+ enc_algo = CAM_WEP104;
+ break;
+ case TKIP_ENCRYPTION:
+ enc_algo = CAM_TKIP;
+ break;
+ case AESCCMP_ENCRYPTION:
+ enc_algo = CAM_AES;
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("switch case "
+ "not process\n"));
+ enc_algo = CAM_TKIP;
+ break;
+ }
+ if (is_wepkey || rtlpriv->sec.use_defaultkey) {
+ macaddr = cam_const_addr[key_index];
+ entry_id = key_index;
+ } else {
+ if (is_group) {
+ macaddr = cam_const_broad;
+ entry_id = key_index;
+ } else {
+ if (mac->opmode == NL80211_IFTYPE_AP) {
+ entry_id = rtl_cam_get_free_entry(hw,
+ p_macaddr);
+ if (entry_id >= TOTAL_CAM_ENTRY) {
+ RT_TRACE(rtlpriv, COMP_SEC,
+ DBG_EMERG, ("Can not "
+ "find free hw security"
+ " cam entry\n"));
+ return;
+ }
+ } else {
+ entry_id = CAM_PAIRWISE_KEY_POSITION;
+ }
+ key_index = PAIRWISE_KEYIDX;
+ is_pairwise = true;
+ }
+ }
+ if (rtlpriv->sec.key_len[key_index] == 0) {
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+ ("delete one entry, entry_id is %d\n",
+ entry_id));
+ if (mac->opmode == NL80211_IFTYPE_AP)
+ rtl_cam_del_entry(hw, p_macaddr);
+ rtl_cam_delete_one_entry(hw, p_macaddr, entry_id);
+ } else {
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
+ ("The insert KEY length is %d\n",
+ rtlpriv->sec.key_len[PAIRWISE_KEYIDX]));
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
+ ("The insert KEY is %x %x\n",
+ rtlpriv->sec.key_buf[0][0],
+ rtlpriv->sec.key_buf[0][1]));
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+ ("add one entry\n"));
+ if (is_pairwise) {
+ RT_PRINT_DATA(rtlpriv, COMP_SEC, DBG_LOUD,
+ "Pairwiase Key content :",
+ rtlpriv->sec.pairwise_key,
+ rtlpriv->
+ sec.key_len[PAIRWISE_KEYIDX]);
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+ ("set Pairwiase key\n"));
+ rtl_cam_add_one_entry(hw, macaddr, key_index,
+ entry_id, enc_algo,
+ CAM_CONFIG_NO_USEDK,
+ rtlpriv->
+ sec.key_buf[key_index]);
+ } else {
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+ ("set group key\n"));
+ if (mac->opmode == NL80211_IFTYPE_ADHOC) {
+ rtl_cam_add_one_entry(hw,
+ rtlefuse->dev_addr,
+ PAIRWISE_KEYIDX,
+ CAM_PAIRWISE_KEY_POSITION,
+ enc_algo, CAM_CONFIG_NO_USEDK,
+ rtlpriv->sec.key_buf[entry_id]);
+ }
+ rtl_cam_add_one_entry(hw, macaddr, key_index,
+ entry_id, enc_algo,
+ CAM_CONFIG_NO_USEDK,
+ rtlpriv->sec.key_buf
+ [entry_id]);
+ }
+ }
+ }
+}
+
+void rtl92de_suspend(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->rtlhal.macphyctl_reg = rtl_read_byte(rtlpriv,
+ REG_MAC_PHY_CTRL_NORMAL);
+}
+
+void rtl92de_resume(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtl_write_byte(rtlpriv, REG_MAC_PHY_CTRL_NORMAL,
+ rtlpriv->rtlhal.macphyctl_reg);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.h b/drivers/net/wireless/rtlwifi/rtl8192de/hw.h
new file mode 100644
index 000000000000..ad44ffa520e6
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.h
@@ -0,0 +1,66 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92DE_HW_H__
+#define __RTL92DE_HW_H__
+
+void rtl92de_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val);
+void rtl92de_read_eeprom_info(struct ieee80211_hw *hw);
+void rtl92de_interrupt_recognized(struct ieee80211_hw *hw,
+ u32 *p_inta, u32 *p_intb);
+int rtl92de_hw_init(struct ieee80211_hw *hw);
+void rtl92de_card_disable(struct ieee80211_hw *hw);
+void rtl92de_enable_interrupt(struct ieee80211_hw *hw);
+void rtl92de_disable_interrupt(struct ieee80211_hw *hw);
+int rtl92de_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type);
+void rtl92de_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid);
+void rtl92de_set_qos(struct ieee80211_hw *hw, int aci);
+void rtl92de_set_beacon_related_registers(struct ieee80211_hw *hw);
+void rtl92de_set_beacon_interval(struct ieee80211_hw *hw);
+void rtl92de_update_interrupt_mask(struct ieee80211_hw *hw,
+ u32 add_msr, u32 rm_msr);
+void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val);
+void rtl92de_update_hal_rate_tbl(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, u8 rssi_level);
+void rtl92de_update_channel_access_setting(struct ieee80211_hw *hw);
+bool rtl92de_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid);
+void rtl92de_enable_hw_security_config(struct ieee80211_hw *hw);
+void rtl92de_set_key(struct ieee80211_hw *hw, u32 key_index,
+ u8 *p_macaddr, bool is_group, u8 enc_algo,
+ bool is_wepkey, bool clear_all);
+
+extern void rtl92de_write_dword_dbi(struct ieee80211_hw *hw, u16 offset,
+ u32 value, u8 direct);
+extern u32 rtl92de_read_dword_dbi(struct ieee80211_hw *hw, u16 offset,
+ u8 direct);
+void rtl92de_suspend(struct ieee80211_hw *hw);
+void rtl92de_resume(struct ieee80211_hw *hw);
+void rtl92d_linked_set_reg(struct ieee80211_hw *hw);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/led.c b/drivers/net/wireless/rtlwifi/rtl8192de/led.c
new file mode 100644
index 000000000000..f1552f4df658
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/led.c
@@ -0,0 +1,159 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "reg.h"
+#include "led.h"
+
+static void _rtl92ce_init_led(struct ieee80211_hw *hw,
+ struct rtl_led *pled, enum rtl_led_pin ledpin)
+{
+ pled->hw = hw;
+ pled->ledpin = ledpin;
+ pled->ledon = false;
+}
+
+void rtl92de_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
+{
+ u8 ledcfg;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
+ ("LedAddr:%X ledpin=%d\n", REG_LEDCFG2, pled->ledpin));
+
+ switch (pled->ledpin) {
+ case LED_PIN_GPIO0:
+ break;
+ case LED_PIN_LED0:
+ ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2);
+
+ if ((rtlpriv->efuse.eeprom_did == 0x8176) ||
+ (rtlpriv->efuse.eeprom_did == 0x8193))
+ /* BIT7 of REG_LEDCFG2 should be set to
+ * make sure we could emit the led2. */
+ rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0xf0) |
+ BIT(7) | BIT(5) | BIT(6));
+ else
+ rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0xf0) |
+ BIT(7) | BIT(5));
+ break;
+ case LED_PIN_LED1:
+ ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG1);
+
+ rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0x0f) | BIT(5));
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+ pled->ledon = true;
+}
+
+void rtl92de_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ u8 ledcfg;
+
+ RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
+ ("LedAddr:%X ledpin=%d\n", REG_LEDCFG2, pled->ledpin));
+
+ ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2);
+
+ switch (pled->ledpin) {
+ case LED_PIN_GPIO0:
+ break;
+ case LED_PIN_LED0:
+ ledcfg &= 0xf0;
+ if (pcipriv->ledctl.led_opendrain)
+ rtl_write_byte(rtlpriv, REG_LEDCFG2,
+ (ledcfg | BIT(1) | BIT(5) | BIT(6)));
+ else
+ rtl_write_byte(rtlpriv, REG_LEDCFG2,
+ (ledcfg | BIT(3) | BIT(5) | BIT(6)));
+ break;
+ case LED_PIN_LED1:
+ ledcfg &= 0x0f;
+ rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(3)));
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+ pled->ledon = false;
+}
+
+void rtl92de_init_sw_leds(struct ieee80211_hw *hw)
+{
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ _rtl92ce_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0);
+ _rtl92ce_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1);
+}
+
+static void _rtl92ce_sw_led_control(struct ieee80211_hw *hw,
+ enum led_ctl_mode ledaction)
+{
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ switch (ledaction) {
+ case LED_CTL_POWER_ON:
+ case LED_CTL_LINK:
+ case LED_CTL_NO_LINK:
+ rtl92de_sw_led_on(hw, pLed0);
+ break;
+ case LED_CTL_POWER_OFF:
+ rtl92de_sw_led_off(hw, pLed0);
+ break;
+ default:
+ break;
+ }
+}
+
+void rtl92de_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+
+ if ((ppsc->rfoff_reason > RF_CHANGE_BY_PS) &&
+ (ledaction == LED_CTL_TX ||
+ ledaction == LED_CTL_RX ||
+ ledaction == LED_CTL_SITE_SURVEY ||
+ ledaction == LED_CTL_LINK ||
+ ledaction == LED_CTL_NO_LINK ||
+ ledaction == LED_CTL_START_TO_LINK ||
+ ledaction == LED_CTL_POWER_ON)) {
+ return;
+ }
+ RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, ("ledaction %d,\n", ledaction));
+
+ _rtl92ce_sw_led_control(hw, ledaction);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/led.h b/drivers/net/wireless/rtlwifi/rtl8192de/led.h
new file mode 100644
index 000000000000..57f4a3c583d4
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/led.h
@@ -0,0 +1,38 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92CE_LED_H__
+#define __RTL92CE_LED_H__
+
+void rtl92de_init_sw_leds(struct ieee80211_hw *hw);
+void rtl92de_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled);
+void rtl92de_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled);
+void rtl92de_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
new file mode 100644
index 000000000000..3ac7af1c5509
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
@@ -0,0 +1,3831 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "../ps.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "rf.h"
+#include "dm.h"
+#include "table.h"
+#include "sw.h"
+#include "hw.h"
+
+#define MAX_RF_IMR_INDEX 12
+#define MAX_RF_IMR_INDEX_NORMAL 13
+#define RF_REG_NUM_FOR_C_CUT_5G 6
+#define RF_REG_NUM_FOR_C_CUT_5G_INTERNALPA 7
+#define RF_REG_NUM_FOR_C_CUT_2G 5
+#define RF_CHNL_NUM_5G 19
+#define RF_CHNL_NUM_5G_40M 17
+#define TARGET_CHNL_NUM_5G 221
+#define TARGET_CHNL_NUM_2G 14
+#define CV_CURVE_CNT 64
+
+static u32 rf_reg_for_5g_swchnl_normal[MAX_RF_IMR_INDEX_NORMAL] = {
+ 0, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x0
+};
+
+static u8 rf_reg_for_c_cut_5g[RF_REG_NUM_FOR_C_CUT_5G] = {
+ RF_SYN_G1, RF_SYN_G2, RF_SYN_G3, RF_SYN_G4, RF_SYN_G5, RF_SYN_G6
+};
+
+static u8 rf_reg_for_c_cut_2g[RF_REG_NUM_FOR_C_CUT_2G] = {
+ RF_SYN_G1, RF_SYN_G2, RF_SYN_G3, RF_SYN_G7, RF_SYN_G8
+};
+
+static u8 rf_for_c_cut_5g_internal_pa[RF_REG_NUM_FOR_C_CUT_5G_INTERNALPA] = {
+ 0x0B, 0x48, 0x49, 0x4B, 0x03, 0x04, 0x0E
+};
+
+static u32 rf_reg_mask_for_c_cut_2g[RF_REG_NUM_FOR_C_CUT_2G] = {
+ BIT(19) | BIT(18) | BIT(17) | BIT(14) | BIT(1),
+ BIT(10) | BIT(9),
+ BIT(18) | BIT(17) | BIT(16) | BIT(1),
+ BIT(2) | BIT(1),
+ BIT(15) | BIT(14) | BIT(13) | BIT(12) | BIT(11)
+};
+
+static u8 rf_chnl_5g[RF_CHNL_NUM_5G] = {
+ 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108,
+ 112, 116, 120, 124, 128, 132, 136, 140
+};
+
+static u8 rf_chnl_5g_40m[RF_CHNL_NUM_5G_40M] = {
+ 38, 42, 46, 50, 54, 58, 62, 102, 106, 110, 114,
+ 118, 122, 126, 130, 134, 138
+};
+static u32 rf_reg_pram_c_5g[5][RF_REG_NUM_FOR_C_CUT_5G] = {
+ {0xE43BE, 0xFC638, 0x77C0A, 0xDE471, 0xd7110, 0x8EB04},
+ {0xE43BE, 0xFC078, 0xF7C1A, 0xE0C71, 0xD7550, 0xAEB04},
+ {0xE43BF, 0xFF038, 0xF7C0A, 0xDE471, 0xE5550, 0xAEB04},
+ {0xE43BF, 0xFF079, 0xF7C1A, 0xDE471, 0xE5550, 0xAEB04},
+ {0xE43BF, 0xFF038, 0xF7C1A, 0xDE471, 0xd7550, 0xAEB04}
+};
+
+static u32 rf_reg_param_for_c_cut_2g[3][RF_REG_NUM_FOR_C_CUT_2G] = {
+ {0x643BC, 0xFC038, 0x77C1A, 0x41289, 0x01840},
+ {0x643BC, 0xFC038, 0x07C1A, 0x41289, 0x01840},
+ {0x243BC, 0xFC438, 0x07C1A, 0x4128B, 0x0FC41}
+};
+
+static u32 rf_syn_g4_for_c_cut_2g = 0xD1C31 & 0x7FF;
+
+static u32 rf_pram_c_5g_int_pa[3][RF_REG_NUM_FOR_C_CUT_5G_INTERNALPA] = {
+ {0x01a00, 0x40443, 0x00eb5, 0x89bec, 0x94a12, 0x94a12, 0x94a12},
+ {0x01800, 0xc0443, 0x00730, 0x896ee, 0x94a52, 0x94a52, 0x94a52},
+ {0x01800, 0xc0443, 0x00730, 0x896ee, 0x94a12, 0x94a12, 0x94a12}
+};
+
+/* [mode][patha+b][reg] */
+static u32 rf_imr_param_normal[1][3][MAX_RF_IMR_INDEX_NORMAL] = {
+ {
+ /* channel 1-14. */
+ {
+ 0x70000, 0x00ff0, 0x4400f, 0x00ff0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x64888, 0xe266c, 0x00090, 0x22fff
+ },
+ /* path 36-64 */
+ {
+ 0x70000, 0x22880, 0x4470f, 0x55880, 0x00070, 0x88000,
+ 0x0, 0x88080, 0x70000, 0x64a82, 0xe466c, 0x00090,
+ 0x32c9a
+ },
+ /* 100 -165 */
+ {
+ 0x70000, 0x44880, 0x4477f, 0x77880, 0x00070, 0x88000,
+ 0x0, 0x880b0, 0x0, 0x64b82, 0xe466c, 0x00090, 0x32c9a
+ }
+ }
+};
+
+static u32 curveindex_5g[TARGET_CHNL_NUM_5G] = {0};
+
+static u32 curveindex_2g[TARGET_CHNL_NUM_2G] = {0};
+
+static u32 targetchnl_5g[TARGET_CHNL_NUM_5G] = {
+ 25141, 25116, 25091, 25066, 25041,
+ 25016, 24991, 24966, 24941, 24917,
+ 24892, 24867, 24843, 24818, 24794,
+ 24770, 24765, 24721, 24697, 24672,
+ 24648, 24624, 24600, 24576, 24552,
+ 24528, 24504, 24480, 24457, 24433,
+ 24409, 24385, 24362, 24338, 24315,
+ 24291, 24268, 24245, 24221, 24198,
+ 24175, 24151, 24128, 24105, 24082,
+ 24059, 24036, 24013, 23990, 23967,
+ 23945, 23922, 23899, 23876, 23854,
+ 23831, 23809, 23786, 23764, 23741,
+ 23719, 23697, 23674, 23652, 23630,
+ 23608, 23586, 23564, 23541, 23519,
+ 23498, 23476, 23454, 23432, 23410,
+ 23388, 23367, 23345, 23323, 23302,
+ 23280, 23259, 23237, 23216, 23194,
+ 23173, 23152, 23130, 23109, 23088,
+ 23067, 23046, 23025, 23003, 22982,
+ 22962, 22941, 22920, 22899, 22878,
+ 22857, 22837, 22816, 22795, 22775,
+ 22754, 22733, 22713, 22692, 22672,
+ 22652, 22631, 22611, 22591, 22570,
+ 22550, 22530, 22510, 22490, 22469,
+ 22449, 22429, 22409, 22390, 22370,
+ 22350, 22336, 22310, 22290, 22271,
+ 22251, 22231, 22212, 22192, 22173,
+ 22153, 22134, 22114, 22095, 22075,
+ 22056, 22037, 22017, 21998, 21979,
+ 21960, 21941, 21921, 21902, 21883,
+ 21864, 21845, 21826, 21807, 21789,
+ 21770, 21751, 21732, 21713, 21695,
+ 21676, 21657, 21639, 21620, 21602,
+ 21583, 21565, 21546, 21528, 21509,
+ 21491, 21473, 21454, 21436, 21418,
+ 21400, 21381, 21363, 21345, 21327,
+ 21309, 21291, 21273, 21255, 21237,
+ 21219, 21201, 21183, 21166, 21148,
+ 21130, 21112, 21095, 21077, 21059,
+ 21042, 21024, 21007, 20989, 20972,
+ 25679, 25653, 25627, 25601, 25575,
+ 25549, 25523, 25497, 25471, 25446,
+ 25420, 25394, 25369, 25343, 25318,
+ 25292, 25267, 25242, 25216, 25191,
+ 25166
+};
+
+/* channel 1~14 */
+static u32 targetchnl_2g[TARGET_CHNL_NUM_2G] = {
+ 26084, 26030, 25976, 25923, 25869, 25816, 25764,
+ 25711, 25658, 25606, 25554, 25502, 25451, 25328
+};
+
+static u32 _rtl92d_phy_calculate_bit_shift(u32 bitmask)
+{
+ u32 i;
+
+ for (i = 0; i <= 31; i++) {
+ if (((bitmask >> i) & 0x1) == 1)
+ break;
+ }
+
+ return i;
+}
+
+u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u32 returnvalue, originalvalue, bitshift;
+ u8 dbi_direct;
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), "
+ "bitmask(%#x)\n", regaddr, bitmask));
+ if (rtlhal->during_mac1init_radioa || rtlhal->during_mac0init_radiob) {
+ /* mac1 use phy0 read radio_b. */
+ /* mac0 use phy1 read radio_b. */
+ if (rtlhal->during_mac1init_radioa)
+ dbi_direct = BIT(3);
+ else if (rtlhal->during_mac0init_radiob)
+ dbi_direct = BIT(3) | BIT(2);
+ originalvalue = rtl92de_read_dword_dbi(hw, (u16)regaddr,
+ dbi_direct);
+ } else {
+ originalvalue = rtl_read_dword(rtlpriv, regaddr);
+ }
+ bitshift = _rtl92d_phy_calculate_bit_shift(bitmask);
+ returnvalue = (originalvalue & bitmask) >> bitshift;
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("BBR MASK=0x%x "
+ "Addr[0x%x]=0x%x\n", bitmask, regaddr, originalvalue));
+ return returnvalue;
+}
+
+void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw,
+ u32 regaddr, u32 bitmask, u32 data)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u8 dbi_direct = 0;
+ u32 originalvalue, bitshift;
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), bitmask(%#x),"
+ " data(%#x)\n", regaddr, bitmask, data));
+ if (rtlhal->during_mac1init_radioa)
+ dbi_direct = BIT(3);
+ else if (rtlhal->during_mac0init_radiob)
+ /* mac0 use phy1 write radio_b. */
+ dbi_direct = BIT(3) | BIT(2);
+ if (bitmask != BMASKDWORD) {
+ if (rtlhal->during_mac1init_radioa ||
+ rtlhal->during_mac0init_radiob)
+ originalvalue = rtl92de_read_dword_dbi(hw,
+ (u16) regaddr,
+ dbi_direct);
+ else
+ originalvalue = rtl_read_dword(rtlpriv, regaddr);
+ bitshift = _rtl92d_phy_calculate_bit_shift(bitmask);
+ data = ((originalvalue & (~bitmask)) | (data << bitshift));
+ }
+ if (rtlhal->during_mac1init_radioa || rtlhal->during_mac0init_radiob)
+ rtl92de_write_dword_dbi(hw, (u16) regaddr, data, dbi_direct);
+ else
+ rtl_write_dword(rtlpriv, regaddr, data);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), bitmask(%#x),"
+ " data(%#x)\n", regaddr, bitmask, data));
+}
+
+static u32 _rtl92d_phy_rf_serial_read(struct ieee80211_hw *hw,
+ enum radio_path rfpath, u32 offset)
+{
+
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath];
+ u32 newoffset;
+ u32 tmplong, tmplong2;
+ u8 rfpi_enable = 0;
+ u32 retvalue;
+
+ newoffset = offset;
+ tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD);
+ if (rfpath == RF90_PATH_A)
+ tmplong2 = tmplong;
+ else
+ tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, BMASKDWORD);
+ tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) |
+ (newoffset << 23) | BLSSIREADEDGE;
+ rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD,
+ tmplong & (~BLSSIREADEDGE));
+ udelay(10);
+ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, BMASKDWORD, tmplong2);
+ udelay(50);
+ udelay(50);
+ rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD,
+ tmplong | BLSSIREADEDGE);
+ udelay(10);
+ if (rfpath == RF90_PATH_A)
+ rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1,
+ BIT(8));
+ else if (rfpath == RF90_PATH_B)
+ rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XB_HSSIPARAMETER1,
+ BIT(8));
+ if (rfpi_enable)
+ retvalue = rtl_get_bbreg(hw, pphyreg->rflssi_readbackpi,
+ BLSSIREADBACKDATA);
+ else
+ retvalue = rtl_get_bbreg(hw, pphyreg->rflssi_readback,
+ BLSSIREADBACKDATA);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("RFR-%d Addr[0x%x] = 0x%x\n",
+ rfpath, pphyreg->rflssi_readback, retvalue));
+ return retvalue;
+}
+
+static void _rtl92d_phy_rf_serial_write(struct ieee80211_hw *hw,
+ enum radio_path rfpath,
+ u32 offset, u32 data)
+{
+ u32 data_and_addr;
+ u32 newoffset;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath];
+
+ newoffset = offset;
+ /* T65 RF */
+ data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff;
+ rtl_set_bbreg(hw, pphyreg->rf3wire_offset, BMASKDWORD, data_and_addr);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("RFW-%d Addr[0x%x]=0x%x\n",
+ rfpath, pphyreg->rf3wire_offset, data_and_addr));
+}
+
+u32 rtl92d_phy_query_rf_reg(struct ieee80211_hw *hw,
+ enum radio_path rfpath, u32 regaddr, u32 bitmask)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 original_value, readback_value, bitshift;
+ unsigned long flags;
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), "
+ "rfpath(%#x), bitmask(%#x)\n",
+ regaddr, rfpath, bitmask));
+ spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
+ original_value = _rtl92d_phy_rf_serial_read(hw, rfpath, regaddr);
+ bitshift = _rtl92d_phy_calculate_bit_shift(bitmask);
+ readback_value = (original_value & bitmask) >> bitshift;
+ spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), rfpath(%#x), "
+ "bitmask(%#x), original_value(%#x)\n",
+ regaddr, rfpath, bitmask, original_value));
+ return readback_value;
+}
+
+void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath,
+ u32 regaddr, u32 bitmask, u32 data)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u32 original_value, bitshift;
+ unsigned long flags;
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ ("regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n",
+ regaddr, bitmask, data, rfpath));
+ if (bitmask == 0)
+ return;
+ spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
+ if (rtlphy->rf_mode != RF_OP_BY_FW) {
+ if (bitmask != BRFREGOFFSETMASK) {
+ original_value = _rtl92d_phy_rf_serial_read(hw,
+ rfpath, regaddr);
+ bitshift = _rtl92d_phy_calculate_bit_shift(bitmask);
+ data = ((original_value & (~bitmask)) |
+ (data << bitshift));
+ }
+ _rtl92d_phy_rf_serial_write(hw, rfpath, regaddr, data);
+ }
+ spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), "
+ "bitmask(%#x), data(%#x), rfpath(%#x)\n",
+ regaddr, bitmask, data, rfpath));
+}
+
+bool rtl92d_phy_mac_config(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 i;
+ u32 arraylength;
+ u32 *ptrarray;
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, ("Read Rtl819XMACPHY_Array\n"));
+ arraylength = MAC_2T_ARRAYLENGTH;
+ ptrarray = rtl8192de_mac_2tarray;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, ("Img:Rtl819XMAC_Array\n"));
+ for (i = 0; i < arraylength; i = i + 2)
+ rtl_write_byte(rtlpriv, ptrarray[i], (u8) ptrarray[i + 1]);
+ if (rtlpriv->rtlhal.macphymode == SINGLEMAC_SINGLEPHY) {
+ /* improve 2-stream TX EVM */
+ /* rtl_write_byte(rtlpriv, 0x14,0x71); */
+ /* AMPDU aggregation number 9 */
+ /* rtl_write_word(rtlpriv, REG_MAX_AGGR_NUM, MAX_AGGR_NUM); */
+ rtl_write_byte(rtlpriv, REG_MAX_AGGR_NUM, 0x0B);
+ } else {
+ /* 92D need to test to decide the num. */
+ rtl_write_byte(rtlpriv, REG_MAX_AGGR_NUM, 0x07);
+ }
+ return true;
+}
+
+static void _rtl92d_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ /* RF Interface Sowrtware Control */
+ /* 16 LSBs if read 32-bit from 0x870 */
+ rtlphy->phyreg_def[RF90_PATH_A].rfintfs = RFPGA0_XAB_RFINTERFACESW;
+ /* 16 MSBs if read 32-bit from 0x870 (16-bit for 0x872) */
+ rtlphy->phyreg_def[RF90_PATH_B].rfintfs = RFPGA0_XAB_RFINTERFACESW;
+ /* 16 LSBs if read 32-bit from 0x874 */
+ rtlphy->phyreg_def[RF90_PATH_C].rfintfs = RFPGA0_XCD_RFINTERFACESW;
+ /* 16 MSBs if read 32-bit from 0x874 (16-bit for 0x876) */
+
+ rtlphy->phyreg_def[RF90_PATH_D].rfintfs = RFPGA0_XCD_RFINTERFACESW;
+ /* RF Interface Readback Value */
+ /* 16 LSBs if read 32-bit from 0x8E0 */
+ rtlphy->phyreg_def[RF90_PATH_A].rfintfi = RFPGA0_XAB_RFINTERFACERB;
+ /* 16 MSBs if read 32-bit from 0x8E0 (16-bit for 0x8E2) */
+ rtlphy->phyreg_def[RF90_PATH_B].rfintfi = RFPGA0_XAB_RFINTERFACERB;
+ /* 16 LSBs if read 32-bit from 0x8E4 */
+ rtlphy->phyreg_def[RF90_PATH_C].rfintfi = RFPGA0_XCD_RFINTERFACERB;
+ /* 16 MSBs if read 32-bit from 0x8E4 (16-bit for 0x8E6) */
+ rtlphy->phyreg_def[RF90_PATH_D].rfintfi = RFPGA0_XCD_RFINTERFACERB;
+
+ /* RF Interface Output (and Enable) */
+ /* 16 LSBs if read 32-bit from 0x860 */
+ rtlphy->phyreg_def[RF90_PATH_A].rfintfo = RFPGA0_XA_RFINTERFACEOE;
+ /* 16 LSBs if read 32-bit from 0x864 */
+ rtlphy->phyreg_def[RF90_PATH_B].rfintfo = RFPGA0_XB_RFINTERFACEOE;
+
+ /* RF Interface (Output and) Enable */
+ /* 16 MSBs if read 32-bit from 0x860 (16-bit for 0x862) */
+ rtlphy->phyreg_def[RF90_PATH_A].rfintfe = RFPGA0_XA_RFINTERFACEOE;
+ /* 16 MSBs if read 32-bit from 0x864 (16-bit for 0x866) */
+ rtlphy->phyreg_def[RF90_PATH_B].rfintfe = RFPGA0_XB_RFINTERFACEOE;
+
+ /* Addr of LSSI. Wirte RF register by driver */
+ /* LSSI Parameter */
+ rtlphy->phyreg_def[RF90_PATH_A].rf3wire_offset =
+ RFPGA0_XA_LSSIPARAMETER;
+ rtlphy->phyreg_def[RF90_PATH_B].rf3wire_offset =
+ RFPGA0_XB_LSSIPARAMETER;
+
+ /* RF parameter */
+ /* BB Band Select */
+ rtlphy->phyreg_def[RF90_PATH_A].rflssi_select = RFPGA0_XAB_RFPARAMETER;
+ rtlphy->phyreg_def[RF90_PATH_B].rflssi_select = RFPGA0_XAB_RFPARAMETER;
+ rtlphy->phyreg_def[RF90_PATH_C].rflssi_select = RFPGA0_XCD_RFPARAMETER;
+ rtlphy->phyreg_def[RF90_PATH_D].rflssi_select = RFPGA0_XCD_RFPARAMETER;
+
+ /* Tx AGC Gain Stage (same for all path. Should we remove this?) */
+ /* Tx gain stage */
+ rtlphy->phyreg_def[RF90_PATH_A].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+ /* Tx gain stage */
+ rtlphy->phyreg_def[RF90_PATH_B].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+ /* Tx gain stage */
+ rtlphy->phyreg_def[RF90_PATH_C].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+ /* Tx gain stage */
+ rtlphy->phyreg_def[RF90_PATH_D].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+
+ /* Tranceiver A~D HSSI Parameter-1 */
+ /* wire control parameter1 */
+ rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para1 = RFPGA0_XA_HSSIPARAMETER1;
+ /* wire control parameter1 */
+ rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para1 = RFPGA0_XB_HSSIPARAMETER1;
+
+ /* Tranceiver A~D HSSI Parameter-2 */
+ /* wire control parameter2 */
+ rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para2 = RFPGA0_XA_HSSIPARAMETER2;
+ /* wire control parameter2 */
+ rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para2 = RFPGA0_XB_HSSIPARAMETER2;
+
+ /* RF switch Control */
+ /* TR/Ant switch control */
+ rtlphy->phyreg_def[RF90_PATH_A].rfswitch_control =
+ RFPGA0_XAB_SWITCHCONTROL;
+ rtlphy->phyreg_def[RF90_PATH_B].rfswitch_control =
+ RFPGA0_XAB_SWITCHCONTROL;
+ rtlphy->phyreg_def[RF90_PATH_C].rfswitch_control =
+ RFPGA0_XCD_SWITCHCONTROL;
+ rtlphy->phyreg_def[RF90_PATH_D].rfswitch_control =
+ RFPGA0_XCD_SWITCHCONTROL;
+
+ /* AGC control 1 */
+ rtlphy->phyreg_def[RF90_PATH_A].rfagc_control1 = ROFDM0_XAAGCCORE1;
+ rtlphy->phyreg_def[RF90_PATH_B].rfagc_control1 = ROFDM0_XBAGCCORE1;
+ rtlphy->phyreg_def[RF90_PATH_C].rfagc_control1 = ROFDM0_XCAGCCORE1;
+ rtlphy->phyreg_def[RF90_PATH_D].rfagc_control1 = ROFDM0_XDAGCCORE1;
+
+ /* AGC control 2 */
+ rtlphy->phyreg_def[RF90_PATH_A].rfagc_control2 = ROFDM0_XAAGCCORE2;
+ rtlphy->phyreg_def[RF90_PATH_B].rfagc_control2 = ROFDM0_XBAGCCORE2;
+ rtlphy->phyreg_def[RF90_PATH_C].rfagc_control2 = ROFDM0_XCAGCCORE2;
+ rtlphy->phyreg_def[RF90_PATH_D].rfagc_control2 = ROFDM0_XDAGCCORE2;
+
+ /* RX AFE control 1 */
+ rtlphy->phyreg_def[RF90_PATH_A].rfrxiq_imbalance =
+ ROFDM0_XARXIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_B].rfrxiq_imbalance =
+ ROFDM0_XBRXIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_C].rfrxiq_imbalance =
+ ROFDM0_XCRXIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_D].rfrxiq_imbalance =
+ ROFDM0_XDRXIQIMBALANCE;
+
+ /*RX AFE control 1 */
+ rtlphy->phyreg_def[RF90_PATH_A].rfrx_afe = ROFDM0_XARXAFE;
+ rtlphy->phyreg_def[RF90_PATH_B].rfrx_afe = ROFDM0_XBRXAFE;
+ rtlphy->phyreg_def[RF90_PATH_C].rfrx_afe = ROFDM0_XCRXAFE;
+ rtlphy->phyreg_def[RF90_PATH_D].rfrx_afe = ROFDM0_XDRXAFE;
+
+ /* Tx AFE control 1 */
+ rtlphy->phyreg_def[RF90_PATH_A].rftxiq_imbalance =
+ ROFDM0_XATxIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_B].rftxiq_imbalance =
+ ROFDM0_XBTxIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_C].rftxiq_imbalance =
+ ROFDM0_XCTxIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_D].rftxiq_imbalance =
+ ROFDM0_XDTxIQIMBALANCE;
+
+ /* Tx AFE control 2 */
+ rtlphy->phyreg_def[RF90_PATH_A].rftx_afe = ROFDM0_XATxAFE;
+ rtlphy->phyreg_def[RF90_PATH_B].rftx_afe = ROFDM0_XBTxAFE;
+ rtlphy->phyreg_def[RF90_PATH_C].rftx_afe = ROFDM0_XCTxAFE;
+ rtlphy->phyreg_def[RF90_PATH_D].rftx_afe = ROFDM0_XDTxAFE;
+
+ /* Tranceiver LSSI Readback SI mode */
+ rtlphy->phyreg_def[RF90_PATH_A].rflssi_readback =
+ RFPGA0_XA_LSSIREADBACK;
+ rtlphy->phyreg_def[RF90_PATH_B].rflssi_readback =
+ RFPGA0_XB_LSSIREADBACK;
+ rtlphy->phyreg_def[RF90_PATH_C].rflssi_readback =
+ RFPGA0_XC_LSSIREADBACK;
+ rtlphy->phyreg_def[RF90_PATH_D].rflssi_readback =
+ RFPGA0_XD_LSSIREADBACK;
+
+ /* Tranceiver LSSI Readback PI mode */
+ rtlphy->phyreg_def[RF90_PATH_A].rflssi_readbackpi =
+ TRANSCEIVERA_HSPI_READBACK;
+ rtlphy->phyreg_def[RF90_PATH_B].rflssi_readbackpi =
+ TRANSCEIVERB_HSPI_READBACK;
+}
+
+static bool _rtl92d_phy_config_bb_with_headerfile(struct ieee80211_hw *hw,
+ u8 configtype)
+{
+ int i;
+ u32 *phy_regarray_table;
+ u32 *agctab_array_table = NULL;
+ u32 *agctab_5garray_table;
+ u16 phy_reg_arraylen, agctab_arraylen = 0, agctab_5garraylen;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+
+ /* Normal chip,Mac0 use AGC_TAB.txt for 2G and 5G band. */
+ if (rtlhal->interfaceindex == 0) {
+ agctab_arraylen = AGCTAB_ARRAYLENGTH;
+ agctab_array_table = rtl8192de_agctab_array;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ (" ===> phy:MAC0, Rtl819XAGCTAB_Array\n"));
+ } else {
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ agctab_arraylen = AGCTAB_2G_ARRAYLENGTH;
+ agctab_array_table = rtl8192de_agctab_2garray;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ (" ===> phy:MAC1, Rtl819XAGCTAB_2GArray\n"));
+ } else {
+ agctab_5garraylen = AGCTAB_5G_ARRAYLENGTH;
+ agctab_5garray_table = rtl8192de_agctab_5garray;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ (" ===> phy:MAC1, Rtl819XAGCTAB_5GArray\n"));
+
+ }
+ }
+ phy_reg_arraylen = PHY_REG_2T_ARRAYLENGTH;
+ phy_regarray_table = rtl8192de_phy_reg_2tarray;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ (" ===> phy:Rtl819XPHY_REG_Array_PG\n"));
+ if (configtype == BASEBAND_CONFIG_PHY_REG) {
+ for (i = 0; i < phy_reg_arraylen; i = i + 2) {
+ if (phy_regarray_table[i] == 0xfe)
+ mdelay(50);
+ else if (phy_regarray_table[i] == 0xfd)
+ mdelay(5);
+ else if (phy_regarray_table[i] == 0xfc)
+ mdelay(1);
+ else if (phy_regarray_table[i] == 0xfb)
+ udelay(50);
+ else if (phy_regarray_table[i] == 0xfa)
+ udelay(5);
+ else if (phy_regarray_table[i] == 0xf9)
+ udelay(1);
+ rtl_set_bbreg(hw, phy_regarray_table[i], BMASKDWORD,
+ phy_regarray_table[i + 1]);
+ udelay(1);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("The phy_regarray_table[0] is %x"
+ " Rtl819XPHY_REGArray[1] is %x\n",
+ phy_regarray_table[i],
+ phy_regarray_table[i + 1]));
+ }
+ } else if (configtype == BASEBAND_CONFIG_AGC_TAB) {
+ if (rtlhal->interfaceindex == 0) {
+ for (i = 0; i < agctab_arraylen; i = i + 2) {
+ rtl_set_bbreg(hw, agctab_array_table[i],
+ BMASKDWORD,
+ agctab_array_table[i + 1]);
+ /* Add 1us delay between BB/RF register
+ * setting. */
+ udelay(1);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("The Rtl819XAGCTAB_Array_"
+ "Table[0] is %ul "
+ "Rtl819XPHY_REGArray[1] is %ul\n",
+ agctab_array_table[i],
+ agctab_array_table[i + 1]));
+ }
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("Normal Chip, MAC0, load "
+ "Rtl819XAGCTAB_Array\n"));
+ } else {
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ for (i = 0; i < agctab_arraylen; i = i + 2) {
+ rtl_set_bbreg(hw, agctab_array_table[i],
+ BMASKDWORD,
+ agctab_array_table[i + 1]);
+ /* Add 1us delay between BB/RF register
+ * setting. */
+ udelay(1);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("The Rtl819XAGCTAB_Array_"
+ "Table[0] is %ul Rtl819XPHY_"
+ "REGArray[1] is %ul\n",
+ agctab_array_table[i],
+ agctab_array_table[i + 1]));
+ }
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("Load Rtl819XAGCTAB_2GArray\n"));
+ } else {
+ for (i = 0; i < agctab_5garraylen; i = i + 2) {
+ rtl_set_bbreg(hw,
+ agctab_5garray_table[i],
+ BMASKDWORD,
+ agctab_5garray_table[i + 1]);
+ /* Add 1us delay between BB/RF registeri
+ * setting. */
+ udelay(1);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("The Rtl819XAGCTAB_5GArray_"
+ "Table[0] is %ul Rtl819XPHY_"
+ "REGArray[1] is %ul\n",
+ agctab_5garray_table[i],
+ agctab_5garray_table[i + 1]));
+ }
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("Load Rtl819XAGCTAB_5GArray\n"));
+ }
+ }
+ }
+ return true;
+}
+
+static void _rtl92d_store_pwrindex_diffrate_offset(struct ieee80211_hw *hw,
+ u32 regaddr, u32 bitmask,
+ u32 data)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ if (regaddr == RTXAGC_A_RATE18_06) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][0] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][0] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][0]));
+ }
+ if (regaddr == RTXAGC_A_RATE54_24) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][1] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][1] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][1]));
+ }
+ if (regaddr == RTXAGC_A_CCK1_MCS32) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][6] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][6] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][6]));
+ }
+ if (regaddr == RTXAGC_B_CCK11_A_CCK2_11 && bitmask == 0xffffff00) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][7] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][7] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][7]));
+ }
+ if (regaddr == RTXAGC_A_MCS03_MCS00) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][2] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][2] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][2]));
+ }
+ if (regaddr == RTXAGC_A_MCS07_MCS04) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][3] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][3] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][3]));
+ }
+ if (regaddr == RTXAGC_A_MCS11_MCS08) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][4] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][4] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][4]));
+ }
+ if (regaddr == RTXAGC_A_MCS15_MCS12) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][5] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][5] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][5]));
+ }
+ if (regaddr == RTXAGC_B_RATE18_06) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][8] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][8] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][8]));
+ }
+ if (regaddr == RTXAGC_B_RATE54_24) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][9] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][9] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][9]));
+ }
+ if (regaddr == RTXAGC_B_CCK1_55_MCS32) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][14] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][14] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][14]));
+ }
+ if (regaddr == RTXAGC_B_CCK11_A_CCK2_11 && bitmask == 0x000000ff) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][15] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][15] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][15]));
+ }
+ if (regaddr == RTXAGC_B_MCS03_MCS00) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][10] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][10] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][10]));
+ }
+ if (regaddr == RTXAGC_B_MCS07_MCS04) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][11] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][11] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][11]));
+ }
+ if (regaddr == RTXAGC_B_MCS11_MCS08) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][12] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][12] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][12]));
+ }
+ if (regaddr == RTXAGC_B_MCS15_MCS12) {
+ rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][13] =
+ data;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("MCSTxPowerLevelOriginalOffset[%d][13] = 0x%ulx\n",
+ rtlphy->pwrgroup_cnt,
+ rtlphy->mcs_txpwrlevel_origoffset
+ [rtlphy->pwrgroup_cnt][13]));
+ rtlphy->pwrgroup_cnt++;
+ }
+}
+
+static bool _rtl92d_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw,
+ u8 configtype)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ int i;
+ u32 *phy_regarray_table_pg;
+ u16 phy_regarray_pg_len;
+
+ phy_regarray_pg_len = PHY_REG_ARRAY_PG_LENGTH;
+ phy_regarray_table_pg = rtl8192de_phy_reg_array_pg;
+ if (configtype == BASEBAND_CONFIG_PHY_REG) {
+ for (i = 0; i < phy_regarray_pg_len; i = i + 3) {
+ if (phy_regarray_table_pg[i] == 0xfe)
+ mdelay(50);
+ else if (phy_regarray_table_pg[i] == 0xfd)
+ mdelay(5);
+ else if (phy_regarray_table_pg[i] == 0xfc)
+ mdelay(1);
+ else if (phy_regarray_table_pg[i] == 0xfb)
+ udelay(50);
+ else if (phy_regarray_table_pg[i] == 0xfa)
+ udelay(5);
+ else if (phy_regarray_table_pg[i] == 0xf9)
+ udelay(1);
+ _rtl92d_store_pwrindex_diffrate_offset(hw,
+ phy_regarray_table_pg[i],
+ phy_regarray_table_pg[i + 1],
+ phy_regarray_table_pg[i + 2]);
+ }
+ } else {
+ RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+ ("configtype != BaseBand_Config_PHY_REG\n"));
+ }
+ return true;
+}
+
+static bool _rtl92d_phy_bb_config(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ bool rtstatus = true;
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, ("==>\n"));
+ rtstatus = _rtl92d_phy_config_bb_with_headerfile(hw,
+ BASEBAND_CONFIG_PHY_REG);
+ if (rtstatus != true) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("Write BB Reg Fail!!"));
+ return false;
+ }
+
+ /* if (rtlphy->rf_type == RF_1T2R) {
+ * _rtl92c_phy_bb_config_1t(hw);
+ * RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, ("Config to 1T!!\n"));
+ *} */
+
+ if (rtlefuse->autoload_failflag == false) {
+ rtlphy->pwrgroup_cnt = 0;
+ rtstatus = _rtl92d_phy_config_bb_with_pgheaderfile(hw,
+ BASEBAND_CONFIG_PHY_REG);
+ }
+ if (rtstatus != true) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("BB_PG Reg Fail!!"));
+ return false;
+ }
+ rtstatus = _rtl92d_phy_config_bb_with_headerfile(hw,
+ BASEBAND_CONFIG_AGC_TAB);
+ if (rtstatus != true) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("AGC Table Fail\n"));
+ return false;
+ }
+ rtlphy->cck_high_power = (bool) (rtl_get_bbreg(hw,
+ RFPGA0_XA_HSSIPARAMETER2, 0x200));
+
+ return true;
+}
+
+bool rtl92d_phy_bb_config(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u16 regval;
+ u32 regvaldw;
+ u8 value;
+
+ _rtl92d_phy_init_bb_rf_register_definition(hw);
+ regval = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN);
+ rtl_write_word(rtlpriv, REG_SYS_FUNC_EN,
+ regval | BIT(13) | BIT(0) | BIT(1));
+ rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL, 0x83);
+ rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL + 1, 0xdb);
+ /* 0x1f bit7 bit6 represent for mac0/mac1 driver ready */
+ value = rtl_read_byte(rtlpriv, REG_RF_CTRL);
+ rtl_write_byte(rtlpriv, REG_RF_CTRL, value | RF_EN | RF_RSTB |
+ RF_SDMRSTB);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, FEN_PPLL | FEN_PCIEA |
+ FEN_DIO_PCIE | FEN_BB_GLB_RSTn | FEN_BBRSTB);
+ rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL + 1, 0x80);
+ if (!(IS_92D_SINGLEPHY(rtlpriv->rtlhal.version))) {
+ regvaldw = rtl_read_dword(rtlpriv, REG_LEDCFG0);
+ rtl_write_dword(rtlpriv, REG_LEDCFG0, regvaldw | BIT(23));
+ }
+
+ return _rtl92d_phy_bb_config(hw);
+}
+
+bool rtl92d_phy_rf_config(struct ieee80211_hw *hw)
+{
+ return rtl92d_phy_rf6052_config(hw);
+}
+
+bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
+ enum rf_content content,
+ enum radio_path rfpath)
+{
+ int i;
+ u32 *radioa_array_table;
+ u32 *radiob_array_table;
+ u16 radioa_arraylen, radiob_arraylen;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ radioa_arraylen = RADIOA_2T_ARRAYLENGTH;
+ radioa_array_table = rtl8192de_radioa_2tarray;
+ radiob_arraylen = RADIOB_2T_ARRAYLENGTH;
+ radiob_array_table = rtl8192de_radiob_2tarray;
+ if (rtlpriv->efuse.internal_pa_5g[0]) {
+ radioa_arraylen = RADIOA_2T_INT_PA_ARRAYLENGTH;
+ radioa_array_table = rtl8192de_radioa_2t_int_paarray;
+ }
+ if (rtlpriv->efuse.internal_pa_5g[1]) {
+ radiob_arraylen = RADIOB_2T_INT_PA_ARRAYLENGTH;
+ radiob_array_table = rtl8192de_radiob_2t_int_paarray;
+ }
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("PHY_ConfigRFWithHeaderFile() "
+ "Radio_A:Rtl819XRadioA_1TArray\n"));
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("PHY_ConfigRFWithHeaderFile() "
+ "Radio_B:Rtl819XRadioB_1TArray\n"));
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, ("Radio No %x\n", rfpath));
+
+ /* this only happens when DMDP, mac0 start on 2.4G,
+ * mac1 start on 5G, mac 0 has to set phy0&phy1
+ * pathA or mac1 has to set phy0&phy1 pathA */
+ if ((content == radiob_txt) && (rfpath == RF90_PATH_A)) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ (" ===> althougth Path A, we load radiob.txt\n"));
+ radioa_arraylen = radiob_arraylen;
+ radioa_array_table = radiob_array_table;
+ }
+ switch (rfpath) {
+ case RF90_PATH_A:
+ for (i = 0; i < radioa_arraylen; i = i + 2) {
+ if (radioa_array_table[i] == 0xfe) {
+ mdelay(50);
+ } else if (radioa_array_table[i] == 0xfd) {
+ /* delay_ms(5); */
+ mdelay(5);
+ } else if (radioa_array_table[i] == 0xfc) {
+ /* delay_ms(1); */
+ mdelay(1);
+ } else if (radioa_array_table[i] == 0xfb) {
+ udelay(50);
+ } else if (radioa_array_table[i] == 0xfa) {
+ udelay(5);
+ } else if (radioa_array_table[i] == 0xf9) {
+ udelay(1);
+ } else {
+ rtl_set_rfreg(hw, rfpath, radioa_array_table[i],
+ BRFREGOFFSETMASK,
+ radioa_array_table[i + 1]);
+ /* Add 1us delay between BB/RF register set. */
+ udelay(1);
+ }
+ }
+ break;
+ case RF90_PATH_B:
+ for (i = 0; i < radiob_arraylen; i = i + 2) {
+ if (radiob_array_table[i] == 0xfe) {
+ /* Delay specific ms. Only RF configuration
+ * requires delay. */
+ mdelay(50);
+ } else if (radiob_array_table[i] == 0xfd) {
+ /* delay_ms(5); */
+ mdelay(5);
+ } else if (radiob_array_table[i] == 0xfc) {
+ /* delay_ms(1); */
+ mdelay(1);
+ } else if (radiob_array_table[i] == 0xfb) {
+ udelay(50);
+ } else if (radiob_array_table[i] == 0xfa) {
+ udelay(5);
+ } else if (radiob_array_table[i] == 0xf9) {
+ udelay(1);
+ } else {
+ rtl_set_rfreg(hw, rfpath, radiob_array_table[i],
+ BRFREGOFFSETMASK,
+ radiob_array_table[i + 1]);
+ /* Add 1us delay between BB/RF register set. */
+ udelay(1);
+ }
+ }
+ break;
+ case RF90_PATH_C:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ case RF90_PATH_D:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+ return true;
+}
+
+void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ rtlphy->default_initialgain[0] =
+ (u8) rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, BMASKBYTE0);
+ rtlphy->default_initialgain[1] =
+ (u8) rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, BMASKBYTE0);
+ rtlphy->default_initialgain[2] =
+ (u8) rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, BMASKBYTE0);
+ rtlphy->default_initialgain[3] =
+ (u8) rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, BMASKBYTE0);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("Default initial gain (c50=0x%x, "
+ "c58=0x%x, c60=0x%x, c68=0x%x\n",
+ rtlphy->default_initialgain[0],
+ rtlphy->default_initialgain[1],
+ rtlphy->default_initialgain[2],
+ rtlphy->default_initialgain[3]));
+ rtlphy->framesync = (u8)rtl_get_bbreg(hw, ROFDM0_RXDETECTOR3,
+ BMASKBYTE0);
+ rtlphy->framesync_c34 = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR2,
+ BMASKDWORD);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("Default framesync (0x%x) = 0x%x\n",
+ ROFDM0_RXDETECTOR3, rtlphy->framesync));
+}
+
+static void _rtl92d_get_txpower_index(struct ieee80211_hw *hw, u8 channel,
+ u8 *cckpowerlevel, u8 *ofdmpowerlevel)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u8 index = (channel - 1);
+
+ /* 1. CCK */
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ /* RF-A */
+ cckpowerlevel[RF90_PATH_A] =
+ rtlefuse->txpwrlevel_cck[RF90_PATH_A][index];
+ /* RF-B */
+ cckpowerlevel[RF90_PATH_B] =
+ rtlefuse->txpwrlevel_cck[RF90_PATH_B][index];
+ } else {
+ cckpowerlevel[RF90_PATH_A] = 0;
+ cckpowerlevel[RF90_PATH_B] = 0;
+ }
+ /* 2. OFDM for 1S or 2S */
+ if (rtlphy->rf_type == RF_1T2R || rtlphy->rf_type == RF_1T1R) {
+ /* Read HT 40 OFDM TX power */
+ ofdmpowerlevel[RF90_PATH_A] =
+ rtlefuse->txpwrlevel_ht40_1s[RF90_PATH_A][index];
+ ofdmpowerlevel[RF90_PATH_B] =
+ rtlefuse->txpwrlevel_ht40_1s[RF90_PATH_B][index];
+ } else if (rtlphy->rf_type == RF_2T2R) {
+ /* Read HT 40 OFDM TX power */
+ ofdmpowerlevel[RF90_PATH_A] =
+ rtlefuse->txpwrlevel_ht40_2s[RF90_PATH_A][index];
+ ofdmpowerlevel[RF90_PATH_B] =
+ rtlefuse->txpwrlevel_ht40_2s[RF90_PATH_B][index];
+ }
+}
+
+static void _rtl92d_ccxpower_index_check(struct ieee80211_hw *hw,
+ u8 channel, u8 *cckpowerlevel, u8 *ofdmpowerlevel)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ rtlphy->cur_cck_txpwridx = cckpowerlevel[0];
+ rtlphy->cur_ofdm24g_txpwridx = ofdmpowerlevel[0];
+}
+
+static u8 _rtl92c_phy_get_rightchnlplace(u8 chnl)
+{
+ u8 channel_5g[59] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,
+ 60, 62, 64, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128,
+ 130, 132, 134, 136, 138, 140, 149, 151,
+ 153, 155, 157, 159, 161, 163, 165
+ };
+ u8 place = chnl;
+
+ if (chnl > 14) {
+ for (place = 14; place < sizeof(channel_5g); place++) {
+ if (channel_5g[place] == chnl) {
+ place++;
+ break;
+ }
+ }
+ }
+ return place;
+}
+
+void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel)
+{
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 cckpowerlevel[2], ofdmpowerlevel[2];
+
+ if (rtlefuse->txpwr_fromeprom == false)
+ return;
+ channel = _rtl92c_phy_get_rightchnlplace(channel);
+ _rtl92d_get_txpower_index(hw, channel, &cckpowerlevel[0],
+ &ofdmpowerlevel[0]);
+ if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G)
+ _rtl92d_ccxpower_index_check(hw, channel, &cckpowerlevel[0],
+ &ofdmpowerlevel[0]);
+ if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G)
+ rtl92d_phy_rf6052_set_cck_txpower(hw, &cckpowerlevel[0]);
+ rtl92d_phy_rf6052_set_ofdm_txpower(hw, &ofdmpowerlevel[0], channel);
+}
+
+void rtl92d_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ enum io_type iotype;
+
+ if (!is_hal_stop(rtlhal)) {
+ switch (operation) {
+ case SCAN_OPT_BACKUP:
+ rtlhal->current_bandtypebackup =
+ rtlhal->current_bandtype;
+ iotype = IO_CMD_PAUSE_DM_BY_SCAN;
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD,
+ (u8 *)&iotype);
+ break;
+ case SCAN_OPT_RESTORE:
+ iotype = IO_CMD_RESUME_DM_BY_SCAN;
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD,
+ (u8 *)&iotype);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Unknown Scan Backup operation.\n"));
+ break;
+ }
+ }
+}
+
+void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw,
+ enum nl80211_channel_type ch_type)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ unsigned long flag = 0;
+ u8 reg_prsr_rsc;
+ u8 reg_bw_opmode;
+
+ if (rtlphy->set_bwmode_inprogress)
+ return;
+ if ((is_hal_stop(rtlhal)) || (RT_CANNOT_IO(hw))) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("FALSE driver sleep or unload\n"));
+ return;
+ }
+ rtlphy->set_bwmode_inprogress = true;
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE,
+ ("Switch to %s bandwidth\n",
+ rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20 ?
+ "20MHz" : "40MHz"));
+ reg_bw_opmode = rtl_read_byte(rtlpriv, REG_BWOPMODE);
+ reg_prsr_rsc = rtl_read_byte(rtlpriv, REG_RRSR + 2);
+ switch (rtlphy->current_chan_bw) {
+ case HT_CHANNEL_WIDTH_20:
+ reg_bw_opmode |= BW_OPMODE_20MHZ;
+ rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode);
+ break;
+ case HT_CHANNEL_WIDTH_20_40:
+ reg_bw_opmode &= ~BW_OPMODE_20MHZ;
+ rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode);
+
+ reg_prsr_rsc = (reg_prsr_rsc & 0x90) |
+ (mac->cur_40_prime_sc << 5);
+ rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_prsr_rsc);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("unknown bandwidth: %#X\n", rtlphy->current_chan_bw));
+ break;
+ }
+ switch (rtlphy->current_chan_bw) {
+ case HT_CHANNEL_WIDTH_20:
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x0);
+ rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x0);
+ /* SET BIT10 BIT11 for receive cck */
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10) |
+ BIT(11), 3);
+ break;
+ case HT_CHANNEL_WIDTH_20_40:
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x1);
+ rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x1);
+ /* Set Control channel to upper or lower.
+ * These settings are required only for 40MHz */
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
+ rtl_set_bbreg(hw, RCCK0_SYSTEM, BCCKSIDEBAND,
+ (mac->cur_40_prime_sc >> 1));
+ rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
+ }
+ rtl_set_bbreg(hw, ROFDM1_LSTF, 0xC00, mac->cur_40_prime_sc);
+ /* SET BIT10 BIT11 for receive cck */
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10) |
+ BIT(11), 0);
+ rtl_set_bbreg(hw, 0x818, (BIT(26) | BIT(27)),
+ (mac->cur_40_prime_sc ==
+ HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("unknown bandwidth: %#X\n", rtlphy->current_chan_bw));
+ break;
+
+ }
+ rtl92d_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw);
+ rtlphy->set_bwmode_inprogress = false;
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, ("<==\n"));
+}
+
+static void _rtl92d_phy_stop_trx_before_changeband(struct ieee80211_hw *hw)
+{
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0);
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0);
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x00);
+ rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x0);
+}
+
+static void rtl92d_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 value8;
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("==>\n"));
+ rtlhal->bandset = band;
+ rtlhal->current_bandtype = band;
+ if (IS_92D_SINGLEPHY(rtlhal->version))
+ rtlhal->bandset = BAND_ON_BOTH;
+ /* stop RX/Tx */
+ _rtl92d_phy_stop_trx_before_changeband(hw);
+ /* reconfig BB/RF according to wireless mode */
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ /* BB & RF Config */
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, ("====>2.4G\n"));
+ if (rtlhal->interfaceindex == 1)
+ _rtl92d_phy_config_bb_with_headerfile(hw,
+ BASEBAND_CONFIG_AGC_TAB);
+ } else {
+ /* 5G band */
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, ("====>5G\n"));
+ if (rtlhal->interfaceindex == 1)
+ _rtl92d_phy_config_bb_with_headerfile(hw,
+ BASEBAND_CONFIG_AGC_TAB);
+ }
+ rtl92d_update_bbrf_configuration(hw);
+ if (rtlhal->current_bandtype == BAND_ON_2_4G)
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1);
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1);
+
+ /* 20M BW. */
+ /* rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 1); */
+ rtlhal->reloadtxpowerindex = true;
+ /* notice fw know band status 0x81[1]/0x53[1] = 0: 5G, 1: 2G */
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ value8 = rtl_read_byte(rtlpriv, (rtlhal->interfaceindex ==
+ 0 ? REG_MAC0 : REG_MAC1));
+ value8 |= BIT(1);
+ rtl_write_byte(rtlpriv, (rtlhal->interfaceindex ==
+ 0 ? REG_MAC0 : REG_MAC1), value8);
+ } else {
+ value8 = rtl_read_byte(rtlpriv, (rtlhal->interfaceindex ==
+ 0 ? REG_MAC0 : REG_MAC1));
+ value8 &= (~BIT(1));
+ rtl_write_byte(rtlpriv, (rtlhal->interfaceindex ==
+ 0 ? REG_MAC0 : REG_MAC1), value8);
+ }
+ mdelay(1);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("<==Switch Band OK.\n"));
+}
+
+static void _rtl92d_phy_reload_imr_setting(struct ieee80211_hw *hw,
+ u8 channel, u8 rfpath)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 imr_num = MAX_RF_IMR_INDEX;
+ u32 rfmask = BRFREGOFFSETMASK;
+ u8 group, i;
+ unsigned long flag = 0;
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("====>path %d\n", rfpath));
+ if (rtlpriv->rtlhal.current_bandtype == BAND_ON_5G) {
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("====>5G\n"));
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(25) | BIT(24), 0);
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0xf);
+ /* fc area 0xd2c */
+ if (channel > 99)
+ rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(13) |
+ BIT(14), 2);
+ else
+ rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(13) |
+ BIT(14), 1);
+ /* leave 0 for channel1-14. */
+ group = channel <= 64 ? 1 : 2;
+ imr_num = MAX_RF_IMR_INDEX_NORMAL;
+ for (i = 0; i < imr_num; i++)
+ rtl_set_rfreg(hw, (enum radio_path)rfpath,
+ rf_reg_for_5g_swchnl_normal[i], rfmask,
+ rf_imr_param_normal[0][group][i]);
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0);
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 1);
+ } else {
+ /* G band. */
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD,
+ ("Load RF IMR parameters for G band. IMR already "
+ "setting %d\n",
+ rtlpriv->rtlhal.load_imrandiqk_setting_for2g));
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("====>2.4G\n"));
+ if (!rtlpriv->rtlhal.load_imrandiqk_setting_for2g) {
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD,
+ ("Load RF IMR parameters "
+ "for G band. %d\n", rfpath));
+ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(25) | BIT(24), 0);
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4,
+ 0x00f00000, 0xf);
+ imr_num = MAX_RF_IMR_INDEX_NORMAL;
+ for (i = 0; i < imr_num; i++) {
+ rtl_set_rfreg(hw, (enum radio_path)rfpath,
+ rf_reg_for_5g_swchnl_normal[i],
+ BRFREGOFFSETMASK,
+ rf_imr_param_normal[0][0][i]);
+ }
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4,
+ 0x00f00000, 0);
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN | BCCKEN, 3);
+ rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
+ }
+ }
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("<====\n"));
+}
+
+static void _rtl92d_phy_enable_rf_env(struct ieee80211_hw *hw,
+ u8 rfpath, u32 *pu4_regval)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath];
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, ("====>\n"));
+ /*----Store original RFENV control type----*/
+ switch (rfpath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ *pu4_regval = rtl_get_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ *pu4_regval =
+ rtl_get_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16);
+ break;
+ }
+ /*----Set RF_ENV enable----*/
+ rtl_set_bbreg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1);
+ udelay(1);
+ /*----Set RF_ENV output high----*/
+ rtl_set_bbreg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1);
+ udelay(1);
+ /* Set bit number of Address and Data for RF register */
+ /* Set 1 to 4 bits for 8255 */
+ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREADDRESSLENGTH, 0x0);
+ udelay(1);
+ /*Set 0 to 12 bits for 8255 */
+ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREDATALENGTH, 0x0);
+ udelay(1);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, ("<====\n"));
+}
+
+static void _rtl92d_phy_restore_rf_env(struct ieee80211_hw *hw, u8 rfpath,
+ u32 *pu4_regval)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath];
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, ("=====>\n"));
+ /*----Restore RFENV control type----*/ ;
+ switch (rfpath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV, *pu4_regval);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16,
+ *pu4_regval);
+ break;
+ }
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, ("<=====\n"));
+}
+
+static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u8 path = rtlhal->current_bandtype ==
+ BAND_ON_5G ? RF90_PATH_A : RF90_PATH_B;
+ u8 index = 0, i = 0, rfpath = RF90_PATH_A;
+ bool need_pwr_down = false, internal_pa = false;
+ u32 u4regvalue, mask = 0x1C000, value = 0, u4tmp, u4tmp2;
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("====>\n"));
+ /* config path A for 5G */
+ if (rtlhal->current_bandtype == BAND_ON_5G) {
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("====>5G\n"));
+ u4tmp = curveindex_5g[channel - 1];
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("ver 1 set RF-A, 5G, "
+ "0x28 = 0x%x !!\n", u4tmp));
+ for (i = 0; i < RF_CHNL_NUM_5G; i++) {
+ if (channel == rf_chnl_5g[i] && channel <= 140)
+ index = 0;
+ }
+ for (i = 0; i < RF_CHNL_NUM_5G_40M; i++) {
+ if (channel == rf_chnl_5g_40m[i] && channel <= 140)
+ index = 1;
+ }
+ if (channel == 149 || channel == 155 || channel == 161)
+ index = 2;
+ else if (channel == 151 || channel == 153 || channel == 163
+ || channel == 165)
+ index = 3;
+ else if (channel == 157 || channel == 159)
+ index = 4;
+
+ if (rtlhal->macphymode == DUALMAC_DUALPHY
+ && rtlhal->interfaceindex == 1) {
+ need_pwr_down = rtl92d_phy_enable_anotherphy(hw, false);
+ rtlhal->during_mac1init_radioa = true;
+ /* asume no this case */
+ if (need_pwr_down)
+ _rtl92d_phy_enable_rf_env(hw, path,
+ &u4regvalue);
+ }
+ for (i = 0; i < RF_REG_NUM_FOR_C_CUT_5G; i++) {
+ if (i == 0 && (rtlhal->macphymode == DUALMAC_DUALPHY)) {
+ rtl_set_rfreg(hw, (enum radio_path)path,
+ rf_reg_for_c_cut_5g[i],
+ BRFREGOFFSETMASK, 0xE439D);
+ } else if (rf_reg_for_c_cut_5g[i] == RF_SYN_G4) {
+ u4tmp2 = (rf_reg_pram_c_5g[index][i] &
+ 0x7FF) | (u4tmp << 11);
+ if (channel == 36)
+ u4tmp2 &= ~(BIT(7) | BIT(6));
+ rtl_set_rfreg(hw, (enum radio_path)path,
+ rf_reg_for_c_cut_5g[i],
+ BRFREGOFFSETMASK, u4tmp2);
+ } else {
+ rtl_set_rfreg(hw, (enum radio_path)path,
+ rf_reg_for_c_cut_5g[i],
+ BRFREGOFFSETMASK,
+ rf_reg_pram_c_5g[index][i]);
+ }
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ ("offset 0x%x value 0x%x "
+ "path %d index %d readback 0x%x\n",
+ rf_reg_for_c_cut_5g[i],
+ rf_reg_pram_c_5g[index][i], path,
+ index, rtl_get_rfreg(hw, (enum radio_path)path,
+ rf_reg_for_c_cut_5g[i], BRFREGOFFSETMASK)));
+ }
+ if (need_pwr_down)
+ _rtl92d_phy_restore_rf_env(hw, path, &u4regvalue);
+ if (rtlhal->during_mac1init_radioa)
+ rtl92d_phy_powerdown_anotherphy(hw, false);
+ if (channel < 149)
+ value = 0x07;
+ else if (channel >= 149)
+ value = 0x02;
+ if (channel >= 36 && channel <= 64)
+ index = 0;
+ else if (channel >= 100 && channel <= 140)
+ index = 1;
+ else
+ index = 2;
+ for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath;
+ rfpath++) {
+ if (rtlhal->macphymode == DUALMAC_DUALPHY &&
+ rtlhal->interfaceindex == 1) /* MAC 1 5G */
+ internal_pa = rtlpriv->efuse.internal_pa_5g[1];
+ else
+ internal_pa =
+ rtlpriv->efuse.internal_pa_5g[rfpath];
+ if (internal_pa) {
+ for (i = 0;
+ i < RF_REG_NUM_FOR_C_CUT_5G_INTERNALPA;
+ i++) {
+ rtl_set_rfreg(hw, rfpath,
+ rf_for_c_cut_5g_internal_pa[i],
+ BRFREGOFFSETMASK,
+ rf_pram_c_5g_int_pa[index][i]);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD,
+ ("offset 0x%x value 0x%x "
+ "path %d index %d\n",
+ rf_for_c_cut_5g_internal_pa[i],
+ rf_pram_c_5g_int_pa[index][i],
+ rfpath, index));
+ }
+ } else {
+ rtl_set_rfreg(hw, (enum radio_path)rfpath, 0x0B,
+ mask, value);
+ }
+ }
+ } else if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("====>2.4G\n"));
+ u4tmp = curveindex_2g[channel - 1];
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("ver 3 set RF-B, 2G, "
+ "0x28 = 0x%x !!\n", u4tmp));
+ if (channel == 1 || channel == 2 || channel == 4 || channel == 9
+ || channel == 10 || channel == 11 || channel == 12)
+ index = 0;
+ else if (channel == 3 || channel == 13 || channel == 14)
+ index = 1;
+ else if (channel >= 5 && channel <= 8)
+ index = 2;
+ if (rtlhal->macphymode == DUALMAC_DUALPHY) {
+ path = RF90_PATH_A;
+ if (rtlhal->interfaceindex == 0) {
+ need_pwr_down =
+ rtl92d_phy_enable_anotherphy(hw, true);
+ rtlhal->during_mac0init_radiob = true;
+
+ if (need_pwr_down)
+ _rtl92d_phy_enable_rf_env(hw, path,
+ &u4regvalue);
+ }
+ }
+ for (i = 0; i < RF_REG_NUM_FOR_C_CUT_2G; i++) {
+ if (rf_reg_for_c_cut_2g[i] == RF_SYN_G7)
+ rtl_set_rfreg(hw, (enum radio_path)path,
+ rf_reg_for_c_cut_2g[i],
+ BRFREGOFFSETMASK,
+ (rf_reg_param_for_c_cut_2g[index][i] |
+ BIT(17)));
+ else
+ rtl_set_rfreg(hw, (enum radio_path)path,
+ rf_reg_for_c_cut_2g[i],
+ BRFREGOFFSETMASK,
+ rf_reg_param_for_c_cut_2g
+ [index][i]);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ ("offset 0x%x value 0x%x mak 0x%x path %d "
+ "index %d readback 0x%x\n",
+ rf_reg_for_c_cut_2g[i],
+ rf_reg_param_for_c_cut_2g[index][i],
+ rf_reg_mask_for_c_cut_2g[i], path, index,
+ rtl_get_rfreg(hw, (enum radio_path)path,
+ rf_reg_for_c_cut_2g[i],
+ BRFREGOFFSETMASK)));
+ }
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("cosa ver 3 set RF-B, 2G, 0x28 = 0x%x !!\n",
+ rf_syn_g4_for_c_cut_2g | (u4tmp << 11)));
+
+ rtl_set_rfreg(hw, (enum radio_path)path, RF_SYN_G4,
+ BRFREGOFFSETMASK,
+ rf_syn_g4_for_c_cut_2g | (u4tmp << 11));
+ if (need_pwr_down)
+ _rtl92d_phy_restore_rf_env(hw, path, &u4regvalue);
+ if (rtlhal->during_mac0init_radiob)
+ rtl92d_phy_powerdown_anotherphy(hw, true);
+ }
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("<====\n"));
+}
+
+u8 rtl92d_get_rightchnlplace_for_iqk(u8 chnl)
+{
+ u8 channel_all[59] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,
+ 60, 62, 64, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128, 130,
+ 132, 134, 136, 138, 140, 149, 151, 153, 155,
+ 157, 159, 161, 163, 165
+ };
+ u8 place = chnl;
+
+ if (chnl > 14) {
+ for (place = 14; place < sizeof(channel_all); place++) {
+ if (channel_all[place] == chnl)
+ return place - 13;
+ }
+ }
+
+ return 0;
+}
+
+#define MAX_TOLERANCE 5
+#define IQK_DELAY_TIME 1 /* ms */
+#define MAX_TOLERANCE_92D 3
+
+/* bit0 = 1 => Tx OK, bit1 = 1 => Rx OK */
+static u8 _rtl92d_phy_patha_iqk(struct ieee80211_hw *hw, bool configpathb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u32 regeac, rege94, rege9c, regea4;
+ u8 result = 0;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path A IQK!\n"));
+ /* path-A IQK setting */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path-A IQK setting!\n"));
+ if (rtlhal->interfaceindex == 0) {
+ rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x10008c1f);
+ rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x10008c1f);
+ } else {
+ rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x10008c22);
+ rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x10008c22);
+ }
+ rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82140102);
+ rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x28160206);
+ /* path-B IQK setting */
+ if (configpathb) {
+ rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x10008c22);
+ rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x10008c22);
+ rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82140102);
+ rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x28160206);
+ }
+ /* LO calibration setting */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("LO calibration setting!\n"));
+ rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911);
+ /* One shot, path A LOK & IQK */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("One shot, path A LOK & IQK!\n"));
+ rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf9000000);
+ rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000);
+ /* delay x ms */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Delay %d ms for One shot, path A LOK & IQK.\n",
+ IQK_DELAY_TIME));
+ mdelay(IQK_DELAY_TIME);
+ /* Check failed */
+ regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xeac = 0x%x\n", regeac));
+ rege94 = rtl_get_bbreg(hw, 0xe94, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xe94 = 0x%x\n", rege94));
+ rege9c = rtl_get_bbreg(hw, 0xe9c, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xe9c = 0x%x\n", rege9c));
+ regea4 = rtl_get_bbreg(hw, 0xea4, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xea4 = 0x%x\n", regea4));
+ if (!(regeac & BIT(28)) && (((rege94 & 0x03FF0000) >> 16) != 0x142) &&
+ (((rege9c & 0x03FF0000) >> 16) != 0x42))
+ result |= 0x01;
+ else /* if Tx not OK, ignore Rx */
+ return result;
+ /* if Tx is OK, check whether Rx is OK */
+ if (!(regeac & BIT(27)) && (((regea4 & 0x03FF0000) >> 16) != 0x132) &&
+ (((regeac & 0x03FF0000) >> 16) != 0x36))
+ result |= 0x02;
+ else
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path A Rx IQK fail!!\n"));
+ return result;
+}
+
+/* bit0 = 1 => Tx OK, bit1 = 1 => Rx OK */
+static u8 _rtl92d_phy_patha_iqk_5g_normal(struct ieee80211_hw *hw,
+ bool configpathb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u32 regeac, rege94, rege9c, regea4;
+ u8 result = 0;
+ u8 i;
+ u8 retrycount = 2;
+ u32 TxOKBit = BIT(28), RxOKBit = BIT(27);
+
+ if (rtlhal->interfaceindex == 1) { /* PHY1 */
+ TxOKBit = BIT(31);
+ RxOKBit = BIT(30);
+ }
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path A IQK!\n"));
+ /* path-A IQK setting */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path-A IQK setting!\n"));
+ rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x18008c1f);
+ rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x18008c1f);
+ rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82140307);
+ rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x68160960);
+ /* path-B IQK setting */
+ if (configpathb) {
+ rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x18008c2f);
+ rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x18008c2f);
+ rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82110000);
+ rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x68110000);
+ }
+ /* LO calibration setting */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("LO calibration setting!\n"));
+ rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911);
+ /* path-A PA on */
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, 0x07000f60);
+ rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BMASKDWORD, 0x66e60e30);
+ for (i = 0; i < retrycount; i++) {
+ /* One shot, path A LOK & IQK */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("One shot, path A LOK & IQK!\n"));
+ rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf9000000);
+ rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000);
+ /* delay x ms */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Delay %d ms for One shot, path A LOK & IQK.\n",
+ IQK_DELAY_TIME));
+ mdelay(IQK_DELAY_TIME * 10);
+ /* Check failed */
+ regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xeac = 0x%x\n", regeac));
+ rege94 = rtl_get_bbreg(hw, 0xe94, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xe94 = 0x%x\n", rege94));
+ rege9c = rtl_get_bbreg(hw, 0xe9c, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xe9c = 0x%x\n", rege9c));
+ regea4 = rtl_get_bbreg(hw, 0xea4, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xea4 = 0x%x\n", regea4));
+ if (!(regeac & TxOKBit) &&
+ (((rege94 & 0x03FF0000) >> 16) != 0x142)) {
+ result |= 0x01;
+ } else { /* if Tx not OK, ignore Rx */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path A Tx IQK fail!!\n"));
+ continue;
+ }
+
+ /* if Tx is OK, check whether Rx is OK */
+ if (!(regeac & RxOKBit) &&
+ (((regea4 & 0x03FF0000) >> 16) != 0x132)) {
+ result |= 0x02;
+ break;
+ } else {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path A Rx IQK fail!!\n"));
+ }
+ }
+ /* path A PA off */
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD,
+ rtlphy->iqk_bb_backup[0]);
+ rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BMASKDWORD,
+ rtlphy->iqk_bb_backup[1]);
+ return result;
+}
+
+/* bit0 = 1 => Tx OK, bit1 = 1 => Rx OK */
+static u8 _rtl92d_phy_pathb_iqk(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 regeac, regeb4, regebc, regec4, regecc;
+ u8 result = 0;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path B IQK!\n"));
+ /* One shot, path B LOK & IQK */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("One shot, path A LOK & IQK!\n"));
+ rtl_set_bbreg(hw, 0xe60, BMASKDWORD, 0x00000002);
+ rtl_set_bbreg(hw, 0xe60, BMASKDWORD, 0x00000000);
+ /* delay x ms */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Delay %d ms for One shot, path B LOK & IQK.\n",
+ IQK_DELAY_TIME));
+ mdelay(IQK_DELAY_TIME);
+ /* Check failed */
+ regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xeac = 0x%x\n", regeac));
+ regeb4 = rtl_get_bbreg(hw, 0xeb4, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xeb4 = 0x%x\n", regeb4));
+ regebc = rtl_get_bbreg(hw, 0xebc, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xebc = 0x%x\n", regebc));
+ regec4 = rtl_get_bbreg(hw, 0xec4, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xec4 = 0x%x\n", regec4));
+ regecc = rtl_get_bbreg(hw, 0xecc, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xecc = 0x%x\n", regecc));
+ if (!(regeac & BIT(31)) && (((regeb4 & 0x03FF0000) >> 16) != 0x142) &&
+ (((regebc & 0x03FF0000) >> 16) != 0x42))
+ result |= 0x01;
+ else
+ return result;
+ if (!(regeac & BIT(30)) && (((regec4 & 0x03FF0000) >> 16) != 0x132) &&
+ (((regecc & 0x03FF0000) >> 16) != 0x36))
+ result |= 0x02;
+ else
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path B Rx IQK fail!!\n"));
+ return result;
+}
+
+/* bit0 = 1 => Tx OK, bit1 = 1 => Rx OK */
+static u8 _rtl92d_phy_pathb_iqk_5g_normal(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u32 regeac, regeb4, regebc, regec4, regecc;
+ u8 result = 0;
+ u8 i;
+ u8 retrycount = 2;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path B IQK!\n"));
+ /* path-A IQK setting */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path-A IQK setting!\n"));
+ rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x18008c1f);
+ rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x18008c1f);
+ rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82110000);
+ rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x68110000);
+
+ /* path-B IQK setting */
+ rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x18008c2f);
+ rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x18008c2f);
+ rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82140307);
+ rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x68160960);
+
+ /* LO calibration setting */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("LO calibration setting!\n"));
+ rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911);
+
+ /* path-B PA on */
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, 0x0f600700);
+ rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BMASKDWORD, 0x061f0d30);
+
+ for (i = 0; i < retrycount; i++) {
+ /* One shot, path B LOK & IQK */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("One shot, path A LOK & IQK!\n"));
+ rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xfa000000);
+ rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000);
+
+ /* delay x ms */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Delay %d ms for One shot, path B LOK & IQK.\n", 10));
+ mdelay(IQK_DELAY_TIME * 10);
+
+ /* Check failed */
+ regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xeac = 0x%x\n", regeac));
+ regeb4 = rtl_get_bbreg(hw, 0xeb4, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xeb4 = 0x%x\n", regeb4));
+ regebc = rtl_get_bbreg(hw, 0xebc, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xebc = 0x%x\n", regebc));
+ regec4 = rtl_get_bbreg(hw, 0xec4, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xec4 = 0x%x\n", regec4));
+ regecc = rtl_get_bbreg(hw, 0xecc, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xecc = 0x%x\n", regecc));
+ if (!(regeac & BIT(31)) &&
+ (((regeb4 & 0x03FF0000) >> 16) != 0x142))
+ result |= 0x01;
+ else
+ continue;
+ if (!(regeac & BIT(30)) &&
+ (((regec4 & 0x03FF0000) >> 16) != 0x132)) {
+ result |= 0x02;
+ break;
+ } else {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path B Rx IQK fail!!\n"));
+ }
+ }
+
+ /* path B PA off */
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD,
+ rtlphy->iqk_bb_backup[0]);
+ rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BMASKDWORD,
+ rtlphy->iqk_bb_backup[2]);
+ return result;
+}
+
+static void _rtl92d_phy_save_adda_registers(struct ieee80211_hw *hw,
+ u32 *adda_reg, u32 *adda_backup,
+ u32 regnum)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 i;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Save ADDA parameters.\n"));
+ for (i = 0; i < regnum; i++)
+ adda_backup[i] = rtl_get_bbreg(hw, adda_reg[i], BMASKDWORD);
+}
+
+static void _rtl92d_phy_save_mac_registers(struct ieee80211_hw *hw,
+ u32 *macreg, u32 *macbackup)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 i;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Save MAC parameters.\n"));
+ for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++)
+ macbackup[i] = rtl_read_byte(rtlpriv, macreg[i]);
+ macbackup[i] = rtl_read_dword(rtlpriv, macreg[i]);
+}
+
+static void _rtl92d_phy_reload_adda_registers(struct ieee80211_hw *hw,
+ u32 *adda_reg, u32 *adda_backup,
+ u32 regnum)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 i;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Reload ADDA power saving parameters !\n"));
+ for (i = 0; i < regnum; i++)
+ rtl_set_bbreg(hw, adda_reg[i], BMASKDWORD, adda_backup[i]);
+}
+
+static void _rtl92d_phy_reload_mac_registers(struct ieee80211_hw *hw,
+ u32 *macreg, u32 *macbackup)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 i;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Reload MAC parameters !\n"));
+ for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++)
+ rtl_write_byte(rtlpriv, macreg[i], (u8) macbackup[i]);
+ rtl_write_byte(rtlpriv, macreg[i], macbackup[i]);
+}
+
+static void _rtl92d_phy_path_adda_on(struct ieee80211_hw *hw,
+ u32 *adda_reg, bool patha_on, bool is2t)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 pathon;
+ u32 i;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("ADDA ON.\n"));
+ pathon = patha_on ? 0x04db25a4 : 0x0b1b25a4;
+ if (patha_on)
+ pathon = rtlpriv->rtlhal.interfaceindex == 0 ?
+ 0x04db25a4 : 0x0b1b25a4;
+ for (i = 0; i < IQK_ADDA_REG_NUM; i++)
+ rtl_set_bbreg(hw, adda_reg[i], BMASKDWORD, pathon);
+}
+
+static void _rtl92d_phy_mac_setting_calibration(struct ieee80211_hw *hw,
+ u32 *macreg, u32 *macbackup)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 i;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("MAC settings for Calibration.\n"));
+ rtl_write_byte(rtlpriv, macreg[0], 0x3F);
+
+ for (i = 1; i < (IQK_MAC_REG_NUM - 1); i++)
+ rtl_write_byte(rtlpriv, macreg[i], (u8)(macbackup[i] &
+ (~BIT(3))));
+ rtl_write_byte(rtlpriv, macreg[i], (u8) (macbackup[i] & (~BIT(5))));
+}
+
+static void _rtl92d_phy_patha_standby(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path-A standby mode!\n"));
+
+ rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x0);
+ rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, BMASKDWORD, 0x00010000);
+ rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000);
+}
+
+static void _rtl92d_phy_pimode_switch(struct ieee80211_hw *hw, bool pi_mode)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 mode;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("BB Switch to %s mode!\n", (pi_mode ? "PI" : "SI")));
+ mode = pi_mode ? 0x01000100 : 0x01000000;
+ rtl_set_bbreg(hw, 0x820, BMASKDWORD, mode);
+ rtl_set_bbreg(hw, 0x828, BMASKDWORD, mode);
+}
+
+static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8],
+ u8 t, bool is2t)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u32 i;
+ u8 patha_ok, pathb_ok;
+ static u32 adda_reg[IQK_ADDA_REG_NUM] = {
+ RFPGA0_XCD_SWITCHCONTROL, 0xe6c, 0xe70, 0xe74,
+ 0xe78, 0xe7c, 0xe80, 0xe84,
+ 0xe88, 0xe8c, 0xed0, 0xed4,
+ 0xed8, 0xedc, 0xee0, 0xeec
+ };
+ static u32 iqk_mac_reg[IQK_MAC_REG_NUM] = {
+ 0x522, 0x550, 0x551, 0x040
+ };
+ static u32 iqk_bb_reg[IQK_BB_REG_NUM] = {
+ RFPGA0_XAB_RFINTERFACESW, RFPGA0_XA_RFINTERFACEOE,
+ RFPGA0_XB_RFINTERFACEOE, ROFDM0_TRMUXPAR,
+ RFPGA0_XCD_RFINTERFACESW, ROFDM0_TRXPATHENABLE,
+ RFPGA0_RFMOD, RFPGA0_ANALOGPARAMETER4,
+ ROFDM0_XAAGCCORE1, ROFDM0_XBAGCCORE1
+ };
+ const u32 retrycount = 2;
+ u32 bbvalue;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("IQK for 2.4G :Start!!!\n"));
+ if (t == 0) {
+ bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("==>0x%08x\n", bbvalue));
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("IQ Calibration for %s\n",
+ (is2t ? "2T2R" : "1T1R")));
+
+ /* Save ADDA parameters, turn Path A ADDA on */
+ _rtl92d_phy_save_adda_registers(hw, adda_reg,
+ rtlphy->adda_backup, IQK_ADDA_REG_NUM);
+ _rtl92d_phy_save_mac_registers(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
+ _rtl92d_phy_save_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup, IQK_BB_REG_NUM);
+ }
+ _rtl92d_phy_path_adda_on(hw, adda_reg, true, is2t);
+ if (t == 0)
+ rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw,
+ RFPGA0_XA_HSSIPARAMETER1, BIT(8));
+
+ /* Switch BB to PI mode to do IQ Calibration. */
+ if (!rtlphy->rfpi_enable)
+ _rtl92d_phy_pimode_switch(hw, true);
+
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(24), 0x00);
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKDWORD, 0x03a05600);
+ rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, BMASKDWORD, 0x000800e4);
+ rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, BMASKDWORD, 0x22204000);
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xf00000, 0x0f);
+ if (is2t) {
+ rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, BMASKDWORD,
+ 0x00010000);
+ rtl_set_bbreg(hw, RFPGA0_XB_LSSIPARAMETER, BMASKDWORD,
+ 0x00010000);
+ }
+ /* MAC settings */
+ _rtl92d_phy_mac_setting_calibration(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
+ /* Page B init */
+ rtl_set_bbreg(hw, 0xb68, BMASKDWORD, 0x0f600000);
+ if (is2t)
+ rtl_set_bbreg(hw, 0xb6c, BMASKDWORD, 0x0f600000);
+ /* IQ calibration setting */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("IQK setting!\n"));
+ rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000);
+ rtl_set_bbreg(hw, 0xe40, BMASKDWORD, 0x01007c00);
+ rtl_set_bbreg(hw, 0xe44, BMASKDWORD, 0x01004800);
+ for (i = 0; i < retrycount; i++) {
+ patha_ok = _rtl92d_phy_patha_iqk(hw, is2t);
+ if (patha_ok == 0x03) {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path A IQK Success!!\n"));
+ result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][2] = (rtl_get_bbreg(hw, 0xea4, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][3] = (rtl_get_bbreg(hw, 0xeac, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ break;
+ } else if (i == (retrycount - 1) && patha_ok == 0x01) {
+ /* Tx IQK OK */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path A IQK Only Tx Success!!\n"));
+
+ result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ }
+ }
+ if (0x00 == patha_ok)
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path A IQK failed!!\n"));
+ if (is2t) {
+ _rtl92d_phy_patha_standby(hw);
+ /* Turn Path B ADDA on */
+ _rtl92d_phy_path_adda_on(hw, adda_reg, false, is2t);
+ for (i = 0; i < retrycount; i++) {
+ pathb_ok = _rtl92d_phy_pathb_iqk(hw);
+ if (pathb_ok == 0x03) {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path B IQK Success!!\n"));
+ result[t][4] = (rtl_get_bbreg(hw, 0xeb4,
+ BMASKDWORD) & 0x3FF0000) >> 16;
+ result[t][5] = (rtl_get_bbreg(hw, 0xebc,
+ BMASKDWORD) & 0x3FF0000) >> 16;
+ result[t][6] = (rtl_get_bbreg(hw, 0xec4,
+ BMASKDWORD) & 0x3FF0000) >> 16;
+ result[t][7] = (rtl_get_bbreg(hw, 0xecc,
+ BMASKDWORD) & 0x3FF0000) >> 16;
+ break;
+ } else if (i == (retrycount - 1) && pathb_ok == 0x01) {
+ /* Tx IQK OK */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path B Only Tx IQK Success!!\n"));
+ result[t][4] = (rtl_get_bbreg(hw, 0xeb4,
+ BMASKDWORD) & 0x3FF0000) >> 16;
+ result[t][5] = (rtl_get_bbreg(hw, 0xebc,
+ BMASKDWORD) & 0x3FF0000) >> 16;
+ }
+ }
+ if (0x00 == pathb_ok)
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path B IQK failed!!\n"));
+ }
+
+ /* Back to BB mode, load original value */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("IQK:Back to BB mode, load original value!\n"));
+
+ rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0);
+ if (t != 0) {
+ /* Switch back BB to SI mode after finish IQ Calibration. */
+ if (!rtlphy->rfpi_enable)
+ _rtl92d_phy_pimode_switch(hw, false);
+ /* Reload ADDA power saving parameters */
+ _rtl92d_phy_reload_adda_registers(hw, adda_reg,
+ rtlphy->adda_backup, IQK_ADDA_REG_NUM);
+ /* Reload MAC parameters */
+ _rtl92d_phy_reload_mac_registers(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
+ if (is2t)
+ _rtl92d_phy_reload_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup,
+ IQK_BB_REG_NUM);
+ else
+ _rtl92d_phy_reload_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup,
+ IQK_BB_REG_NUM - 1);
+ /* load 0xe30 IQC default value */
+ rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x01008c00);
+ rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x01008c00);
+ }
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("<==\n"));
+}
+
+static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw,
+ long result[][8], u8 t)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u8 patha_ok, pathb_ok;
+ static u32 adda_reg[IQK_ADDA_REG_NUM] = {
+ RFPGA0_XCD_SWITCHCONTROL, 0xe6c, 0xe70, 0xe74,
+ 0xe78, 0xe7c, 0xe80, 0xe84,
+ 0xe88, 0xe8c, 0xed0, 0xed4,
+ 0xed8, 0xedc, 0xee0, 0xeec
+ };
+ static u32 iqk_mac_reg[IQK_MAC_REG_NUM] = {
+ 0x522, 0x550, 0x551, 0x040
+ };
+ static u32 iqk_bb_reg[IQK_BB_REG_NUM] = {
+ RFPGA0_XAB_RFINTERFACESW, RFPGA0_XA_RFINTERFACEOE,
+ RFPGA0_XB_RFINTERFACEOE, ROFDM0_TRMUXPAR,
+ RFPGA0_XCD_RFINTERFACESW, ROFDM0_TRXPATHENABLE,
+ RFPGA0_RFMOD, RFPGA0_ANALOGPARAMETER4,
+ ROFDM0_XAAGCCORE1, ROFDM0_XBAGCCORE1
+ };
+ u32 bbvalue;
+ bool is2t = IS_92D_SINGLEPHY(rtlhal->version);
+
+ /* Note: IQ calibration must be performed after loading
+ * PHY_REG.txt , and radio_a, radio_b.txt */
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("IQK for 5G NORMAL:Start!!!\n"));
+ mdelay(IQK_DELAY_TIME * 20);
+ if (t == 0) {
+ bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, BMASKDWORD);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("==>0x%08x\n", bbvalue));
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("IQ Calibration for %s\n",
+ (is2t ? "2T2R" : "1T1R")));
+ /* Save ADDA parameters, turn Path A ADDA on */
+ _rtl92d_phy_save_adda_registers(hw, adda_reg,
+ rtlphy->adda_backup,
+ IQK_ADDA_REG_NUM);
+ _rtl92d_phy_save_mac_registers(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
+ if (is2t)
+ _rtl92d_phy_save_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup,
+ IQK_BB_REG_NUM);
+ else
+ _rtl92d_phy_save_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup,
+ IQK_BB_REG_NUM - 1);
+ }
+ _rtl92d_phy_path_adda_on(hw, adda_reg, true, is2t);
+ /* MAC settings */
+ _rtl92d_phy_mac_setting_calibration(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
+ if (t == 0)
+ rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw,
+ RFPGA0_XA_HSSIPARAMETER1, BIT(8));
+ /* Switch BB to PI mode to do IQ Calibration. */
+ if (!rtlphy->rfpi_enable)
+ _rtl92d_phy_pimode_switch(hw, true);
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(24), 0x00);
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKDWORD, 0x03a05600);
+ rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, BMASKDWORD, 0x000800e4);
+ rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, BMASKDWORD, 0x22208000);
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xf00000, 0x0f);
+
+ /* Page B init */
+ rtl_set_bbreg(hw, 0xb68, BMASKDWORD, 0x0f600000);
+ if (is2t)
+ rtl_set_bbreg(hw, 0xb6c, BMASKDWORD, 0x0f600000);
+ /* IQ calibration setting */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("IQK setting!\n"));
+ rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000);
+ rtl_set_bbreg(hw, 0xe40, BMASKDWORD, 0x10007c00);
+ rtl_set_bbreg(hw, 0xe44, BMASKDWORD, 0x01004800);
+ patha_ok = _rtl92d_phy_patha_iqk_5g_normal(hw, is2t);
+ if (patha_ok == 0x03) {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path A IQK Success!!\n"));
+ result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][2] = (rtl_get_bbreg(hw, 0xea4, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][3] = (rtl_get_bbreg(hw, 0xeac, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ } else if (patha_ok == 0x01) { /* Tx IQK OK */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path A IQK Only Tx Success!!\n"));
+
+ result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ } else {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path A IQK Fail!!\n"));
+ }
+ if (is2t) {
+ /* _rtl92d_phy_patha_standby(hw); */
+ /* Turn Path B ADDA on */
+ _rtl92d_phy_path_adda_on(hw, adda_reg, false, is2t);
+ pathb_ok = _rtl92d_phy_pathb_iqk_5g_normal(hw);
+ if (pathb_ok == 0x03) {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path B IQK Success!!\n"));
+ result[t][4] = (rtl_get_bbreg(hw, 0xeb4, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][5] = (rtl_get_bbreg(hw, 0xebc, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][6] = (rtl_get_bbreg(hw, 0xec4, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][7] = (rtl_get_bbreg(hw, 0xecc, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ } else if (pathb_ok == 0x01) { /* Tx IQK OK */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path B Only Tx IQK Success!!\n"));
+ result[t][4] = (rtl_get_bbreg(hw, 0xeb4, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][5] = (rtl_get_bbreg(hw, 0xebc, BMASKDWORD) &
+ 0x3FF0000) >> 16;
+ } else {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path B IQK failed!!\n"));
+ }
+ }
+
+ /* Back to BB mode, load original value */
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("IQK:Back to BB mode, load original value!\n"));
+ rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0);
+ if (t != 0) {
+ if (is2t)
+ _rtl92d_phy_reload_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup,
+ IQK_BB_REG_NUM);
+ else
+ _rtl92d_phy_reload_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup,
+ IQK_BB_REG_NUM - 1);
+ /* Reload MAC parameters */
+ _rtl92d_phy_reload_mac_registers(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
+ /* Switch back BB to SI mode after finish IQ Calibration. */
+ if (!rtlphy->rfpi_enable)
+ _rtl92d_phy_pimode_switch(hw, false);
+ /* Reload ADDA power saving parameters */
+ _rtl92d_phy_reload_adda_registers(hw, adda_reg,
+ rtlphy->adda_backup,
+ IQK_ADDA_REG_NUM);
+ }
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("<==\n"));
+}
+
+static bool _rtl92d_phy_simularity_compare(struct ieee80211_hw *hw,
+ long result[][8], u8 c1, u8 c2)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u32 i, j, diff, sim_bitmap, bound;
+ u8 final_candidate[2] = {0xFF, 0xFF}; /* for path A and path B */
+ bool bresult = true;
+ bool is2t = IS_92D_SINGLEPHY(rtlhal->version);
+
+ if (is2t)
+ bound = 8;
+ else
+ bound = 4;
+ sim_bitmap = 0;
+ for (i = 0; i < bound; i++) {
+ diff = (result[c1][i] > result[c2][i]) ? (result[c1][i] -
+ result[c2][i]) : (result[c2][i] - result[c1][i]);
+ if (diff > MAX_TOLERANCE_92D) {
+ if ((i == 2 || i == 6) && !sim_bitmap) {
+ if (result[c1][i] + result[c1][i + 1] == 0)
+ final_candidate[(i / 4)] = c2;
+ else if (result[c2][i] + result[c2][i + 1] == 0)
+ final_candidate[(i / 4)] = c1;
+ else
+ sim_bitmap = sim_bitmap | (1 << i);
+ } else {
+ sim_bitmap = sim_bitmap | (1 << i);
+ }
+ }
+ }
+ if (sim_bitmap == 0) {
+ for (i = 0; i < (bound / 4); i++) {
+ if (final_candidate[i] != 0xFF) {
+ for (j = i * 4; j < (i + 1) * 4 - 2; j++)
+ result[3][j] =
+ result[final_candidate[i]][j];
+ bresult = false;
+ }
+ }
+ return bresult;
+ }
+ if (!(sim_bitmap & 0x0F)) { /* path A OK */
+ for (i = 0; i < 4; i++)
+ result[3][i] = result[c1][i];
+ } else if (!(sim_bitmap & 0x03)) { /* path A, Tx OK */
+ for (i = 0; i < 2; i++)
+ result[3][i] = result[c1][i];
+ }
+ if (!(sim_bitmap & 0xF0) && is2t) { /* path B OK */
+ for (i = 4; i < 8; i++)
+ result[3][i] = result[c1][i];
+ } else if (!(sim_bitmap & 0x30)) { /* path B, Tx OK */
+ for (i = 4; i < 6; i++)
+ result[3][i] = result[c1][i];
+ }
+ return false;
+}
+
+static void _rtl92d_phy_patha_fill_iqk_matrix(struct ieee80211_hw *hw,
+ bool iqk_ok, long result[][8],
+ u8 final_candidate, bool txonly)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u32 oldval_0, val_x, tx0_a, reg;
+ long val_y, tx0_c;
+ bool is2t = IS_92D_SINGLEPHY(rtlhal->version) ||
+ rtlhal->macphymode == DUALMAC_DUALPHY;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("Path A IQ Calibration %s !\n",
+ (iqk_ok) ? "Success" : "Failed"));
+ if (final_candidate == 0xFF) {
+ return;
+ } else if (iqk_ok) {
+ oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE,
+ BMASKDWORD) >> 22) & 0x3FF; /* OFDM0_D */
+ val_x = result[final_candidate][0];
+ if ((val_x & 0x00000200) != 0)
+ val_x = val_x | 0xFFFFFC00;
+ tx0_a = (val_x * oldval_0) >> 8;
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("X = 0x%x, tx0_a = 0x%x,"
+ " oldval_0 0x%x\n", val_x, tx0_a, oldval_0));
+ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, 0x3FF, tx0_a);
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(24),
+ ((val_x * oldval_0 >> 7) & 0x1));
+ val_y = result[final_candidate][1];
+ if ((val_y & 0x00000200) != 0)
+ val_y = val_y | 0xFFFFFC00;
+ /* path B IQK result + 3 */
+ if (rtlhal->interfaceindex == 1 &&
+ rtlhal->current_bandtype == BAND_ON_5G)
+ val_y += 3;
+ tx0_c = (val_y * oldval_0) >> 8;
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Y = 0x%lx, tx0_c = 0x%lx\n",
+ val_y, tx0_c));
+ rtl_set_bbreg(hw, ROFDM0_XCTxAFE, 0xF0000000,
+ ((tx0_c & 0x3C0) >> 6));
+ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, 0x003F0000,
+ (tx0_c & 0x3F));
+ if (is2t)
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(26),
+ ((val_y * oldval_0 >> 7) & 0x1));
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xC80 = 0x%x\n",
+ rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE,
+ BMASKDWORD)));
+ if (txonly) {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("only Tx OK\n"));
+ return;
+ }
+ reg = result[final_candidate][2];
+ rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0x3FF, reg);
+ reg = result[final_candidate][3] & 0x3F;
+ rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0xFC00, reg);
+ reg = (result[final_candidate][3] >> 6) & 0xF;
+ rtl_set_bbreg(hw, 0xca0, 0xF0000000, reg);
+ }
+}
+
+static void _rtl92d_phy_pathb_fill_iqk_matrix(struct ieee80211_hw *hw,
+ bool iqk_ok, long result[][8], u8 final_candidate, bool txonly)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u32 oldval_1, val_x, tx1_a, reg;
+ long val_y, tx1_c;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Path B IQ Calibration %s !\n",
+ (iqk_ok) ? "Success" : "Failed"));
+ if (final_candidate == 0xFF) {
+ return;
+ } else if (iqk_ok) {
+ oldval_1 = (rtl_get_bbreg(hw, ROFDM0_XBTxIQIMBALANCE,
+ BMASKDWORD) >> 22) & 0x3FF;
+ val_x = result[final_candidate][4];
+ if ((val_x & 0x00000200) != 0)
+ val_x = val_x | 0xFFFFFC00;
+ tx1_a = (val_x * oldval_1) >> 8;
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("X = 0x%x, tx1_a = 0x%x\n",
+ val_x, tx1_a));
+ rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, 0x3FF, tx1_a);
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(28),
+ ((val_x * oldval_1 >> 7) & 0x1));
+ val_y = result[final_candidate][5];
+ if ((val_y & 0x00000200) != 0)
+ val_y = val_y | 0xFFFFFC00;
+ if (rtlhal->current_bandtype == BAND_ON_5G)
+ val_y += 3;
+ tx1_c = (val_y * oldval_1) >> 8;
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("Y = 0x%lx, tx1_c = 0x%lx\n",
+ val_y, tx1_c));
+ rtl_set_bbreg(hw, ROFDM0_XDTxAFE, 0xF0000000,
+ ((tx1_c & 0x3C0) >> 6));
+ rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, 0x003F0000,
+ (tx1_c & 0x3F));
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(30),
+ ((val_y * oldval_1 >> 7) & 0x1));
+ if (txonly)
+ return;
+ reg = result[final_candidate][6];
+ rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, 0x3FF, reg);
+ reg = result[final_candidate][7] & 0x3F;
+ rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, 0xFC00, reg);
+ reg = (result[final_candidate][7] >> 6) & 0xF;
+ rtl_set_bbreg(hw, ROFDM0_AGCRSSITABLE, 0x0000F000, reg);
+ }
+}
+
+void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ long result[4][8];
+ u8 i, final_candidate, indexforchannel;
+ bool patha_ok, pathb_ok;
+ long rege94, rege9c, regea4, regeac, regeb4;
+ long regebc, regec4, regecc, regtmp = 0;
+ bool is12simular, is13simular, is23simular;
+ unsigned long flag = 0;
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("IQK:Start!!!channel %d\n", rtlphy->current_channel));
+ for (i = 0; i < 8; i++) {
+ result[0][i] = 0;
+ result[1][i] = 0;
+ result[2][i] = 0;
+ result[3][i] = 0;
+ }
+ final_candidate = 0xff;
+ patha_ok = false;
+ pathb_ok = false;
+ is12simular = false;
+ is23simular = false;
+ is13simular = false;
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("IQK !!!currentband %d\n", rtlhal->current_bandtype));
+ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
+ for (i = 0; i < 3; i++) {
+ if (rtlhal->current_bandtype == BAND_ON_5G) {
+ _rtl92d_phy_iq_calibrate_5g_normal(hw, result, i);
+ } else if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ if (IS_92D_SINGLEPHY(rtlhal->version))
+ _rtl92d_phy_iq_calibrate(hw, result, i, true);
+ else
+ _rtl92d_phy_iq_calibrate(hw, result, i, false);
+ }
+ if (i == 1) {
+ is12simular = _rtl92d_phy_simularity_compare(hw, result,
+ 0, 1);
+ if (is12simular) {
+ final_candidate = 0;
+ break;
+ }
+ }
+ if (i == 2) {
+ is13simular = _rtl92d_phy_simularity_compare(hw, result,
+ 0, 2);
+ if (is13simular) {
+ final_candidate = 0;
+ break;
+ }
+ is23simular = _rtl92d_phy_simularity_compare(hw, result,
+ 1, 2);
+ if (is23simular) {
+ final_candidate = 1;
+ } else {
+ for (i = 0; i < 8; i++)
+ regtmp += result[3][i];
+
+ if (regtmp != 0)
+ final_candidate = 3;
+ else
+ final_candidate = 0xFF;
+ }
+ }
+ }
+ rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
+ for (i = 0; i < 4; i++) {
+ rege94 = result[i][0];
+ rege9c = result[i][1];
+ regea4 = result[i][2];
+ regeac = result[i][3];
+ regeb4 = result[i][4];
+ regebc = result[i][5];
+ regec4 = result[i][6];
+ regecc = result[i][7];
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("IQK: rege94=%lx rege9c=%lx regea4=%lx regeac=%lx "
+ "regeb4=%lx regebc=%lx regec4=%lx regecc=%lx\n ",
+ rege94, rege9c, regea4, regeac, regeb4, regebc, regec4,
+ regecc));
+ }
+ if (final_candidate != 0xff) {
+ rtlphy->reg_e94 = rege94 = result[final_candidate][0];
+ rtlphy->reg_e9c = rege9c = result[final_candidate][1];
+ regea4 = result[final_candidate][2];
+ regeac = result[final_candidate][3];
+ rtlphy->reg_eb4 = regeb4 = result[final_candidate][4];
+ rtlphy->reg_ebc = regebc = result[final_candidate][5];
+ regec4 = result[final_candidate][6];
+ regecc = result[final_candidate][7];
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("IQK: final_candidate is %x\n", final_candidate));
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("IQK: rege94=%lx rege9c=%lx regea4=%lx regeac=%lx "
+ "regeb4=%lx regebc=%lx regec4=%lx regecc=%lx\n ",
+ rege94, rege9c, regea4, regeac, regeb4, regebc, regec4,
+ regecc));
+ patha_ok = pathb_ok = true;
+ } else {
+ rtlphy->reg_e94 = rtlphy->reg_eb4 = 0x100; /* X default value */
+ rtlphy->reg_e9c = rtlphy->reg_ebc = 0x0; /* Y default value */
+ }
+ if ((rege94 != 0) /*&&(regea4 != 0) */)
+ _rtl92d_phy_patha_fill_iqk_matrix(hw, patha_ok, result,
+ final_candidate, (regea4 == 0));
+ if (IS_92D_SINGLEPHY(rtlhal->version)) {
+ if ((regeb4 != 0) /*&&(regec4 != 0) */)
+ _rtl92d_phy_pathb_fill_iqk_matrix(hw, pathb_ok, result,
+ final_candidate, (regec4 == 0));
+ }
+ if (final_candidate != 0xFF) {
+ indexforchannel = rtl92d_get_rightchnlplace_for_iqk(
+ rtlphy->current_channel);
+
+ for (i = 0; i < IQK_MATRIX_REG_NUM; i++)
+ rtlphy->iqk_matrix_regsetting[indexforchannel].
+ value[0][i] = result[final_candidate][i];
+ rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done =
+ true;
+
+ RT_TRACE(rtlpriv, COMP_SCAN | COMP_MLME, DBG_LOUD,
+ ("\nIQK OK indexforchannel %d.\n", indexforchannel));
+ }
+}
+
+void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u8 indexforchannel;
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("channel %d\n", channel));
+ /*------Do IQK for normal chip and test chip 5G band------- */
+ indexforchannel = rtl92d_get_rightchnlplace_for_iqk(channel);
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ ("indexforchannel %d done %d\n", indexforchannel,
+ rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done));
+ if (0 && !rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done &&
+ rtlphy->need_iqk) {
+ /* Re Do IQK. */
+ RT_TRACE(rtlpriv, COMP_SCAN | COMP_INIT, DBG_LOUD,
+ ("Do IQK Matrix reg for channel:%d....\n", channel));
+ rtl92d_phy_iq_calibrate(hw);
+ } else {
+ /* Just load the value. */
+ /* 2G band just load once. */
+ if (((!rtlhal->load_imrandiqk_setting_for2g) &&
+ indexforchannel == 0) || indexforchannel > 0) {
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD,
+ ("Just Read IQK Matrix reg for channel:%d"
+ "....\n", channel));
+ if ((rtlphy->iqk_matrix_regsetting[indexforchannel].
+ value[0] != NULL)
+ /*&&(regea4 != 0) */)
+ _rtl92d_phy_patha_fill_iqk_matrix(hw, true,
+ rtlphy->iqk_matrix_regsetting[
+ indexforchannel].value, 0,
+ (rtlphy->iqk_matrix_regsetting[
+ indexforchannel].value[0][2] == 0));
+ if (IS_92D_SINGLEPHY(rtlhal->version)) {
+ if ((rtlphy->iqk_matrix_regsetting[
+ indexforchannel].value[0][4] != 0)
+ /*&&(regec4 != 0) */)
+ _rtl92d_phy_pathb_fill_iqk_matrix(hw,
+ true,
+ rtlphy->iqk_matrix_regsetting[
+ indexforchannel].value, 0,
+ (rtlphy->iqk_matrix_regsetting[
+ indexforchannel].value[0][6]
+ == 0));
+ }
+ }
+ }
+ rtlphy->need_iqk = false;
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("<====\n"));
+}
+
+static u32 _rtl92d_phy_get_abs(u32 val1, u32 val2)
+{
+ u32 ret;
+
+ if (val1 >= val2)
+ ret = val1 - val2;
+ else
+ ret = val2 - val1;
+ return ret;
+}
+
+static bool _rtl92d_is_legal_5g_channel(struct ieee80211_hw *hw, u8 channel)
+{
+
+ int i;
+ u8 channel_5g[45] = {
+ 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,
+ 60, 62, 64, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128, 130, 132,
+ 134, 136, 138, 140, 149, 151, 153, 155, 157, 159,
+ 161, 163, 165
+ };
+
+ for (i = 0; i < sizeof(channel_5g); i++)
+ if (channel == channel_5g[i])
+ return true;
+ return false;
+}
+
+static void _rtl92d_phy_calc_curvindex(struct ieee80211_hw *hw,
+ u32 *targetchnl, u32 * curvecount_val,
+ bool is5g, u32 *curveindex)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 smallest_abs_val = 0xffffffff, u4tmp;
+ u8 i, j;
+ u8 chnl_num = is5g ? TARGET_CHNL_NUM_5G : TARGET_CHNL_NUM_2G;
+
+ for (i = 0; i < chnl_num; i++) {
+ if (is5g && !_rtl92d_is_legal_5g_channel(hw, i + 1))
+ continue;
+ curveindex[i] = 0;
+ for (j = 0; j < (CV_CURVE_CNT * 2); j++) {
+ u4tmp = _rtl92d_phy_get_abs(targetchnl[i],
+ curvecount_val[j]);
+
+ if (u4tmp < smallest_abs_val) {
+ curveindex[i] = j;
+ smallest_abs_val = u4tmp;
+ }
+ }
+ smallest_abs_val = 0xffffffff;
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("curveindex[%d] = %x\n", i,
+ curveindex[i]));
+ }
+}
+
+static void _rtl92d_phy_reload_lck_setting(struct ieee80211_hw *hw,
+ u8 channel)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 erfpath = rtlpriv->rtlhal.current_bandtype ==
+ BAND_ON_5G ? RF90_PATH_A :
+ IS_92D_SINGLEPHY(rtlpriv->rtlhal.version) ?
+ RF90_PATH_B : RF90_PATH_A;
+ u32 u4tmp = 0, u4regvalue = 0;
+ bool bneed_powerdown_radio = false;
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("path %d\n", erfpath));
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("band type = %d\n",
+ rtlpriv->rtlhal.current_bandtype));
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("channel = %d\n", channel));
+ if (rtlpriv->rtlhal.current_bandtype == BAND_ON_5G) {/* Path-A for 5G */
+ u4tmp = curveindex_5g[channel-1];
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("ver 1 set RF-A, 5G, 0x28 = 0x%ulx !!\n", u4tmp));
+ if (rtlpriv->rtlhal.macphymode == DUALMAC_DUALPHY &&
+ rtlpriv->rtlhal.interfaceindex == 1) {
+ bneed_powerdown_radio =
+ rtl92d_phy_enable_anotherphy(hw, false);
+ rtlpriv->rtlhal.during_mac1init_radioa = true;
+ /* asume no this case */
+ if (bneed_powerdown_radio)
+ _rtl92d_phy_enable_rf_env(hw, erfpath,
+ &u4regvalue);
+ }
+ rtl_set_rfreg(hw, erfpath, RF_SYN_G4, 0x3f800, u4tmp);
+ if (bneed_powerdown_radio)
+ _rtl92d_phy_restore_rf_env(hw, erfpath, &u4regvalue);
+ if (rtlpriv->rtlhal.during_mac1init_radioa)
+ rtl92d_phy_powerdown_anotherphy(hw, false);
+ } else if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) {
+ u4tmp = curveindex_2g[channel-1];
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("ver 3 set RF-B, 2G, 0x28 = 0x%ulx !!\n", u4tmp));
+ if (rtlpriv->rtlhal.macphymode == DUALMAC_DUALPHY &&
+ rtlpriv->rtlhal.interfaceindex == 0) {
+ bneed_powerdown_radio =
+ rtl92d_phy_enable_anotherphy(hw, true);
+ rtlpriv->rtlhal.during_mac0init_radiob = true;
+ if (bneed_powerdown_radio)
+ _rtl92d_phy_enable_rf_env(hw, erfpath,
+ &u4regvalue);
+ }
+ rtl_set_rfreg(hw, erfpath, RF_SYN_G4, 0x3f800, u4tmp);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("ver 3 set RF-B, 2G, 0x28 = 0x%ulx !!\n",
+ rtl_get_rfreg(hw, erfpath, RF_SYN_G4, 0x3f800)));
+ if (bneed_powerdown_radio)
+ _rtl92d_phy_restore_rf_env(hw, erfpath, &u4regvalue);
+ if (rtlpriv->rtlhal.during_mac0init_radiob)
+ rtl92d_phy_powerdown_anotherphy(hw, true);
+ }
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, ("<====\n"));
+}
+
+static void _rtl92d_phy_lc_calibrate_sw(struct ieee80211_hw *hw, bool is2t)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ u8 tmpreg, index, rf_mode[2];
+ u8 path = is2t ? 2 : 1;
+ u8 i;
+ u32 u4tmp, offset;
+ u32 curvecount_val[CV_CURVE_CNT * 2] = {0};
+ u16 timeout = 800, timecount = 0;
+
+ /* Check continuous TX and Packet TX */
+ tmpreg = rtl_read_byte(rtlpriv, 0xd03);
+ /* if Deal with contisuous TX case, disable all continuous TX */
+ /* if Deal with Packet TX case, block all queues */
+ if ((tmpreg & 0x70) != 0)
+ rtl_write_byte(rtlpriv, 0xd03, tmpreg & 0x8F);
+ else
+ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF);
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xF00000, 0x0F);
+ for (index = 0; index < path; index++) {
+ /* 1. Read original RF mode */
+ offset = index == 0 ? ROFDM0_XAAGCCORE1 : ROFDM0_XBAGCCORE1;
+ rf_mode[index] = rtl_read_byte(rtlpriv, offset);
+ /* 2. Set RF mode = standby mode */
+ rtl_set_rfreg(hw, (enum radio_path)index, RF_AC,
+ BRFREGOFFSETMASK, 0x010000);
+ if (rtlpci->init_ready) {
+ /* switch CV-curve control by LC-calibration */
+ rtl_set_rfreg(hw, (enum radio_path)index, RF_SYN_G7,
+ BIT(17), 0x0);
+ /* 4. Set LC calibration begin */
+ rtl_set_rfreg(hw, (enum radio_path)index, RF_CHNLBW,
+ 0x08000, 0x01);
+ }
+ u4tmp = rtl_get_rfreg(hw, (enum radio_path)index, RF_SYN_G6,
+ BRFREGOFFSETMASK);
+ while ((!(u4tmp & BIT(11))) && timecount <= timeout) {
+ mdelay(50);
+ timecount += 50;
+ u4tmp = rtl_get_rfreg(hw, (enum radio_path)index,
+ RF_SYN_G6, BRFREGOFFSETMASK);
+ }
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("PHY_LCK finish delay for %d ms=2\n", timecount));
+ u4tmp = rtl_get_rfreg(hw, index, RF_SYN_G4, BRFREGOFFSETMASK);
+ if (index == 0 && rtlhal->interfaceindex == 0) {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("path-A / 5G LCK\n"));
+ } else {
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("path-B / 2.4G LCK\n"));
+ }
+ memset(&curvecount_val[0], 0, CV_CURVE_CNT * 2);
+ /* Set LC calibration off */
+ rtl_set_rfreg(hw, (enum radio_path)index, RF_CHNLBW,
+ 0x08000, 0x0);
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("set RF 0x18[15] = 0\n"));
+ /* save Curve-counting number */
+ for (i = 0; i < CV_CURVE_CNT; i++) {
+ u32 readval = 0, readval2 = 0;
+ rtl_set_rfreg(hw, (enum radio_path)index, 0x3F,
+ 0x7f, i);
+
+ rtl_set_rfreg(hw, (enum radio_path)index, 0x4D,
+ BRFREGOFFSETMASK, 0x0);
+ readval = rtl_get_rfreg(hw, (enum radio_path)index,
+ 0x4F, BRFREGOFFSETMASK);
+ curvecount_val[2 * i + 1] = (readval & 0xfffe0) >> 5;
+ /* reg 0x4f [4:0] */
+ /* reg 0x50 [19:10] */
+ readval2 = rtl_get_rfreg(hw, (enum radio_path)index,
+ 0x50, 0xffc00);
+ curvecount_val[2 * i] = (((readval & 0x1F) << 10) |
+ readval2);
+ }
+ if (index == 0 && rtlhal->interfaceindex == 0)
+ _rtl92d_phy_calc_curvindex(hw, targetchnl_5g,
+ curvecount_val,
+ true, curveindex_5g);
+ else
+ _rtl92d_phy_calc_curvindex(hw, targetchnl_2g,
+ curvecount_val,
+ false, curveindex_2g);
+ /* switch CV-curve control mode */
+ rtl_set_rfreg(hw, (enum radio_path)index, RF_SYN_G7,
+ BIT(17), 0x1);
+ }
+
+ /* Restore original situation */
+ for (index = 0; index < path; index++) {
+ offset = index == 0 ? ROFDM0_XAAGCCORE1 : ROFDM0_XBAGCCORE1;
+ rtl_write_byte(rtlpriv, offset, 0x50);
+ rtl_write_byte(rtlpriv, offset, rf_mode[index]);
+ }
+ if ((tmpreg & 0x70) != 0)
+ rtl_write_byte(rtlpriv, 0xd03, tmpreg);
+ else /*Deal with Packet TX case */
+ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00);
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xF00000, 0x00);
+ _rtl92d_phy_reload_lck_setting(hw, rtlpriv->phy.current_channel);
+}
+
+static void _rtl92d_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("cosa PHY_LCK ver=2\n"));
+ _rtl92d_phy_lc_calibrate_sw(hw, is2t);
+}
+
+void rtl92d_phy_lc_calibrate(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u32 timeout = 2000, timecount = 0;
+
+ while (rtlpriv->mac80211.act_scanning && timecount < timeout) {
+ udelay(50);
+ timecount += 50;
+ }
+
+ rtlphy->lck_inprogress = true;
+ RTPRINT(rtlpriv, FINIT, INIT_IQK,
+ ("LCK:Start!!! currentband %x delay %d ms\n",
+ rtlhal->current_bandtype, timecount));
+ if (IS_92D_SINGLEPHY(rtlhal->version)) {
+ _rtl92d_phy_lc_calibrate(hw, true);
+ } else {
+ /* For 1T1R */
+ _rtl92d_phy_lc_calibrate(hw, false);
+ }
+ rtlphy->lck_inprogress = false;
+ RTPRINT(rtlpriv, FINIT, INIT_IQK, ("LCK:Finish!!!\n"));
+}
+
+void rtl92d_phy_ap_calibrate(struct ieee80211_hw *hw, char delta)
+{
+ return;
+}
+
+static bool _rtl92d_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable,
+ u32 cmdtableidx, u32 cmdtablesz, enum swchnlcmd_id cmdid,
+ u32 para1, u32 para2, u32 msdelay)
+{
+ struct swchnlcmd *pcmd;
+
+ if (cmdtable == NULL) {
+ RT_ASSERT(false, ("cmdtable cannot be NULL.\n"));
+ return false;
+ }
+ if (cmdtableidx >= cmdtablesz)
+ return false;
+
+ pcmd = cmdtable + cmdtableidx;
+ pcmd->cmdid = cmdid;
+ pcmd->para1 = para1;
+ pcmd->para2 = para2;
+ pcmd->msdelay = msdelay;
+ return true;
+}
+
+void rtl92d_phy_reset_iqk_result(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u8 i;
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("settings regs %d default regs %d\n",
+ (int)(sizeof(rtlphy->iqk_matrix_regsetting) /
+ sizeof(struct iqk_matrix_regs)),
+ IQK_MATRIX_REG_NUM));
+ /* 0xe94, 0xe9c, 0xea4, 0xeac, 0xeb4, 0xebc, 0xec4, 0xecc */
+ for (i = 0; i < IQK_MATRIX_SETTINGS_NUM; i++) {
+ rtlphy->iqk_matrix_regsetting[i].value[0][0] = 0x100;
+ rtlphy->iqk_matrix_regsetting[i].value[0][2] = 0x100;
+ rtlphy->iqk_matrix_regsetting[i].value[0][4] = 0x100;
+ rtlphy->iqk_matrix_regsetting[i].value[0][6] = 0x100;
+ rtlphy->iqk_matrix_regsetting[i].value[0][1] = 0x0;
+ rtlphy->iqk_matrix_regsetting[i].value[0][3] = 0x0;
+ rtlphy->iqk_matrix_regsetting[i].value[0][5] = 0x0;
+ rtlphy->iqk_matrix_regsetting[i].value[0][7] = 0x0;
+ rtlphy->iqk_matrix_regsetting[i].iqk_done = false;
+ }
+}
+
+static bool _rtl92d_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw,
+ u8 channel, u8 *stage, u8 *step,
+ u32 *delay)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct swchnlcmd precommoncmd[MAX_PRECMD_CNT];
+ u32 precommoncmdcnt;
+ struct swchnlcmd postcommoncmd[MAX_POSTCMD_CNT];
+ u32 postcommoncmdcnt;
+ struct swchnlcmd rfdependcmd[MAX_RFDEPENDCMD_CNT];
+ u32 rfdependcmdcnt;
+ struct swchnlcmd *currentcmd = NULL;
+ u8 rfpath;
+ u8 num_total_rfpath = rtlphy->num_total_rfpath;
+
+ precommoncmdcnt = 0;
+ _rtl92d_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++,
+ MAX_PRECMD_CNT,
+ CMDID_SET_TXPOWEROWER_LEVEL, 0, 0, 0);
+ _rtl92d_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++,
+ MAX_PRECMD_CNT, CMDID_END, 0, 0, 0);
+ postcommoncmdcnt = 0;
+ _rtl92d_phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++,
+ MAX_POSTCMD_CNT, CMDID_END, 0, 0, 0);
+ rfdependcmdcnt = 0;
+ _rtl92d_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++,
+ MAX_RFDEPENDCMD_CNT, CMDID_RF_WRITEREG,
+ RF_CHNLBW, channel, 0);
+ _rtl92d_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++,
+ MAX_RFDEPENDCMD_CNT, CMDID_END,
+ 0, 0, 0);
+
+ do {
+ switch (*stage) {
+ case 0:
+ currentcmd = &precommoncmd[*step];
+ break;
+ case 1:
+ currentcmd = &rfdependcmd[*step];
+ break;
+ case 2:
+ currentcmd = &postcommoncmd[*step];
+ break;
+ }
+ if (currentcmd->cmdid == CMDID_END) {
+ if ((*stage) == 2) {
+ return true;
+ } else {
+ (*stage)++;
+ (*step) = 0;
+ continue;
+ }
+ }
+ switch (currentcmd->cmdid) {
+ case CMDID_SET_TXPOWEROWER_LEVEL:
+ rtl92d_phy_set_txpower_level(hw, channel);
+ break;
+ case CMDID_WRITEPORT_ULONG:
+ rtl_write_dword(rtlpriv, currentcmd->para1,
+ currentcmd->para2);
+ break;
+ case CMDID_WRITEPORT_USHORT:
+ rtl_write_word(rtlpriv, currentcmd->para1,
+ (u16)currentcmd->para2);
+ break;
+ case CMDID_WRITEPORT_UCHAR:
+ rtl_write_byte(rtlpriv, currentcmd->para1,
+ (u8)currentcmd->para2);
+ break;
+ case CMDID_RF_WRITEREG:
+ for (rfpath = 0; rfpath < num_total_rfpath; rfpath++) {
+ rtlphy->rfreg_chnlval[rfpath] =
+ ((rtlphy->rfreg_chnlval[rfpath] &
+ 0xffffff00) | currentcmd->para2);
+ if (rtlpriv->rtlhal.current_bandtype ==
+ BAND_ON_5G) {
+ if (currentcmd->para2 > 99)
+ rtlphy->rfreg_chnlval[rfpath] =
+ rtlphy->rfreg_chnlval
+ [rfpath] | (BIT(18));
+ else
+ rtlphy->rfreg_chnlval[rfpath] =
+ rtlphy->rfreg_chnlval
+ [rfpath] & (~BIT(18));
+ rtlphy->rfreg_chnlval[rfpath] |=
+ (BIT(16) | BIT(8));
+ } else {
+ rtlphy->rfreg_chnlval[rfpath] &=
+ ~(BIT(8) | BIT(16) | BIT(18));
+ }
+ rtl_set_rfreg(hw, (enum radio_path)rfpath,
+ currentcmd->para1,
+ BRFREGOFFSETMASK,
+ rtlphy->rfreg_chnlval[rfpath]);
+ _rtl92d_phy_reload_imr_setting(hw, channel,
+ rfpath);
+ }
+ _rtl92d_phy_switch_rf_setting(hw, channel);
+ /* do IQK when all parameters are ready */
+ rtl92d_phy_reload_iqk_setting(hw, channel);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+ break;
+ } while (true);
+ (*delay) = currentcmd->msdelay;
+ (*step)++;
+ return false;
+}
+
+u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u32 delay;
+ u32 timeout = 1000, timecount = 0;
+ u8 channel = rtlphy->current_channel;
+ u32 ret_value;
+
+ if (rtlphy->sw_chnl_inprogress)
+ return 0;
+ if (rtlphy->set_bwmode_inprogress)
+ return 0;
+
+ if ((is_hal_stop(rtlhal)) || (RT_CANNOT_IO(hw))) {
+ RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
+ ("sw_chnl_inprogress false driver sleep or unload\n"));
+ return 0;
+ }
+ while (rtlphy->lck_inprogress && timecount < timeout) {
+ mdelay(50);
+ timecount += 50;
+ }
+ if (rtlhal->macphymode == SINGLEMAC_SINGLEPHY &&
+ rtlhal->bandset == BAND_ON_BOTH) {
+ ret_value = rtl_get_bbreg(hw, RFPGA0_XAB_RFPARAMETER,
+ BMASKDWORD);
+ if (rtlphy->current_channel > 14 && !(ret_value & BIT(0)))
+ rtl92d_phy_switch_wirelessband(hw, BAND_ON_5G);
+ else if (rtlphy->current_channel <= 14 && (ret_value & BIT(0)))
+ rtl92d_phy_switch_wirelessband(hw, BAND_ON_2_4G);
+ }
+ switch (rtlhal->current_bandtype) {
+ case BAND_ON_5G:
+ /* Get first channel error when change between
+ * 5G and 2.4G band. */
+ if (channel <= 14)
+ return 0;
+ RT_ASSERT((channel > 14), ("5G but channel<=14"));
+ break;
+ case BAND_ON_2_4G:
+ /* Get first channel error when change between
+ * 5G and 2.4G band. */
+ if (channel > 14)
+ return 0;
+ RT_ASSERT((channel <= 14), ("2G but channel>14"));
+ break;
+ default:
+ RT_ASSERT(false,
+ ("Invalid WirelessMode(%#x)!!\n",
+ rtlpriv->mac80211.mode));
+ break;
+ }
+ rtlphy->sw_chnl_inprogress = true;
+ if (channel == 0)
+ channel = 1;
+ rtlphy->sw_chnl_stage = 0;
+ rtlphy->sw_chnl_step = 0;
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE,
+ ("switch to channel%d\n", rtlphy->current_channel));
+
+ do {
+ if (!rtlphy->sw_chnl_inprogress)
+ break;
+ if (!_rtl92d_phy_sw_chnl_step_by_step(hw,
+ rtlphy->current_channel,
+ &rtlphy->sw_chnl_stage, &rtlphy->sw_chnl_step, &delay)) {
+ if (delay > 0)
+ mdelay(delay);
+ else
+ continue;
+ } else {
+ rtlphy->sw_chnl_inprogress = false;
+ }
+ break;
+ } while (true);
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, ("<==\n"));
+ rtlphy->sw_chnl_inprogress = false;
+ return 1;
+}
+
+static void rtl92d_phy_set_io(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+ ("--->Cmd(%#x), set_io_inprogress(%d)\n",
+ rtlphy->current_io_type, rtlphy->set_io_inprogress));
+ switch (rtlphy->current_io_type) {
+ case IO_CMD_RESUME_DM_BY_SCAN:
+ de_digtable.cur_igvalue = rtlphy->initgain_backup.xaagccore1;
+ rtl92d_dm_write_dig(hw);
+ rtl92d_phy_set_txpower_level(hw, rtlphy->current_channel);
+ break;
+ case IO_CMD_PAUSE_DM_BY_SCAN:
+ rtlphy->initgain_backup.xaagccore1 = de_digtable.cur_igvalue;
+ de_digtable.cur_igvalue = 0x17;
+ rtl92d_dm_write_dig(hw);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+ rtlphy->set_io_inprogress = false;
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+ ("<---(%#x)\n", rtlphy->current_io_type));
+}
+
+bool rtl92d_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ bool postprocessing = false;
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+ ("-->IO Cmd(%#x), set_io_inprogress(%d)\n",
+ iotype, rtlphy->set_io_inprogress));
+ do {
+ switch (iotype) {
+ case IO_CMD_RESUME_DM_BY_SCAN:
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+ ("[IO CMD] Resume DM after scan.\n"));
+ postprocessing = true;
+ break;
+ case IO_CMD_PAUSE_DM_BY_SCAN:
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+ ("[IO CMD] Pause DM before scan.\n"));
+ postprocessing = true;
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ break;
+ }
+ } while (false);
+ if (postprocessing && !rtlphy->set_io_inprogress) {
+ rtlphy->set_io_inprogress = true;
+ rtlphy->current_io_type = iotype;
+ } else {
+ return false;
+ }
+ rtl92d_phy_set_io(hw);
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, ("<--IO Type(%#x)\n", iotype));
+ return true;
+}
+
+static void _rtl92d_phy_set_rfon(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ /* a. SYS_CLKR 0x08[11] = 1 restore MAC clock */
+ /* b. SPS_CTRL 0x11[7:0] = 0x2b */
+ if (rtlpriv->rtlhal.macphymode == SINGLEMAC_SINGLEPHY)
+ rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b);
+ /* c. For PCIE: SYS_FUNC_EN 0x02[7:0] = 0xE3 enable BB TRX function */
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3);
+ /* RF_ON_EXCEP(d~g): */
+ /* d. APSD_CTRL 0x600[7:0] = 0x00 */
+ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00);
+ /* e. SYS_FUNC_EN 0x02[7:0] = 0xE2 reset BB TRX function again */
+ /* f. SYS_FUNC_EN 0x02[7:0] = 0xE3 enable BB TRX function*/
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3);
+ /* g. txpause 0x522[7:0] = 0x00 enable mac tx queue */
+ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00);
+}
+
+static void _rtl92d_phy_set_rfsleep(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 u4btmp;
+ u8 delay = 5;
+
+ /* a. TXPAUSE 0x522[7:0] = 0xFF Pause MAC TX queue */
+ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF);
+ /* b. RF path 0 offset 0x00 = 0x00 disable RF */
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00);
+ /* c. APSD_CTRL 0x600[7:0] = 0x40 */
+ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40);
+ /* d. APSD_CTRL 0x600[7:0] = 0x00
+ * APSD_CTRL 0x600[7:0] = 0x00
+ * RF path 0 offset 0x00 = 0x00
+ * APSD_CTRL 0x600[7:0] = 0x40
+ * */
+ u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, BRFREGOFFSETMASK);
+ while (u4btmp != 0 && delay > 0) {
+ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x0);
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00);
+ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40);
+ u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, BRFREGOFFSETMASK);
+ delay--;
+ }
+ if (delay == 0) {
+ /* Jump out the LPS turn off sequence to RF_ON_EXCEP */
+ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00);
+
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3);
+ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00);
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ ("Fail !!! Switch RF timeout.\n"));
+ return;
+ }
+ /* e. For PCIE: SYS_FUNC_EN 0x02[7:0] = 0xE2 reset BB TRX function */
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2);
+ /* f. SPS_CTRL 0x11[7:0] = 0x22 */
+ if (rtlpriv->rtlhal.macphymode == SINGLEMAC_SINGLEPHY)
+ rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x22);
+ /* g. SYS_CLKR 0x08[11] = 0 gated MAC clock */
+}
+
+bool rtl92d_phy_set_rf_power_state(struct ieee80211_hw *hw,
+ enum rf_pwrstate rfpwr_state)
+{
+
+ bool bresult = true;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ u8 i, queue_id;
+ struct rtl8192_tx_ring *ring = NULL;
+
+ if (rfpwr_state == ppsc->rfpwr_state)
+ return false;
+ switch (rfpwr_state) {
+ case ERFON:
+ if ((ppsc->rfpwr_state == ERFOFF) &&
+ RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC)) {
+ bool rtstatus;
+ u32 InitializeCount = 0;
+ do {
+ InitializeCount++;
+ RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+ ("IPS Set eRf nic enable\n"));
+ rtstatus = rtl_ps_enable_nic(hw);
+ } while ((rtstatus != true) &&
+ (InitializeCount < 10));
+
+ RT_CLEAR_PS_LEVEL(ppsc,
+ RT_RF_OFF_LEVL_HALT_NIC);
+ } else {
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
+ ("awake, sleeped:%d ms state_"
+ "inap:%x\n",
+ jiffies_to_msecs(jiffies -
+ ppsc->last_sleep_jiffies),
+ rtlpriv->psc.state_inap));
+ ppsc->last_awake_jiffies = jiffies;
+ _rtl92d_phy_set_rfon(hw);
+ }
+
+ if (mac->link_state == MAC80211_LINKED)
+ rtlpriv->cfg->ops->led_control(hw,
+ LED_CTL_LINK);
+ else
+ rtlpriv->cfg->ops->led_control(hw,
+ LED_CTL_NO_LINK);
+ break;
+ case ERFOFF:
+ if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) {
+ RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+ ("IPS Set eRf nic disable\n"));
+ rtl_ps_disable_nic(hw);
+ RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
+ } else {
+ if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS)
+ rtlpriv->cfg->ops->led_control(hw,
+ LED_CTL_NO_LINK);
+ else
+ rtlpriv->cfg->ops->led_control(hw,
+ LED_CTL_POWER_OFF);
+ }
+ break;
+ case ERFSLEEP:
+ if (ppsc->rfpwr_state == ERFOFF)
+ break;
+
+ for (queue_id = 0, i = 0;
+ queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) {
+ ring = &pcipriv->dev.tx_ring[queue_id];
+ if (skb_queue_len(&ring->queue) == 0 ||
+ queue_id == BEACON_QUEUE) {
+ queue_id++;
+ continue;
+ } else if (rtlpci->pdev->current_state != PCI_D0) {
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ ("eRf Off/Sleep: %d times TcbBusyQueu"
+ "e[%d] !=0 but lower power state!\n",
+ (i + 1), queue_id));
+ break;
+ } else {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("eRf Off/Sleep: %d times TcbBusyQueu"
+ "e[%d] =%d "
+ "before doze!\n", (i + 1), queue_id,
+ skb_queue_len(&ring->queue)));
+ udelay(10);
+ i++;
+ }
+
+ if (i >= MAX_DOZE_WAITING_TIMES_9x) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("\nERFOFF: %d times TcbBusyQueue[%d] "
+ "= %d !\n",
+ MAX_DOZE_WAITING_TIMES_9x, queue_id,
+ skb_queue_len(&ring->queue)));
+ break;
+ }
+ }
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
+ ("Set rfsleep awaked:%d ms\n",
+ jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies)));
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, ("sleep awaked:%d ms "
+ "state_inap:%x\n", jiffies_to_msecs(jiffies -
+ ppsc->last_awake_jiffies), rtlpriv->psc.state_inap));
+ ppsc->last_sleep_jiffies = jiffies;
+ _rtl92d_phy_set_rfsleep(hw);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("switch case not process\n"));
+ bresult = false;
+ break;
+ }
+ if (bresult)
+ ppsc->rfpwr_state = rfpwr_state;
+ return bresult;
+}
+
+void rtl92d_phy_config_macphymode(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 offset = REG_MAC_PHY_CTRL_NORMAL;
+
+ switch (rtlhal->macphymode) {
+ case DUALMAC_DUALPHY:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("MacPhyMode: DUALMAC_DUALPHY\n"));
+ rtl_write_byte(rtlpriv, offset, 0xF3);
+ break;
+ case SINGLEMAC_SINGLEPHY:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("MacPhyMode: SINGLEMAC_SINGLEPHY\n"));
+ rtl_write_byte(rtlpriv, offset, 0xF4);
+ break;
+ case DUALMAC_SINGLEPHY:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("MacPhyMode: DUALMAC_SINGLEPHY\n"));
+ rtl_write_byte(rtlpriv, offset, 0xF1);
+ break;
+ }
+}
+
+void rtl92d_phy_config_macphymode_info(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ switch (rtlhal->macphymode) {
+ case DUALMAC_SINGLEPHY:
+ rtlphy->rf_type = RF_2T2R;
+ rtlhal->version |= CHIP_92D_SINGLEPHY;
+ rtlhal->bandset = BAND_ON_BOTH;
+ rtlhal->current_bandtype = BAND_ON_2_4G;
+ break;
+
+ case SINGLEMAC_SINGLEPHY:
+ rtlphy->rf_type = RF_2T2R;
+ rtlhal->version |= CHIP_92D_SINGLEPHY;
+ rtlhal->bandset = BAND_ON_BOTH;
+ rtlhal->current_bandtype = BAND_ON_2_4G;
+ break;
+
+ case DUALMAC_DUALPHY:
+ rtlphy->rf_type = RF_1T1R;
+ rtlhal->version &= (~CHIP_92D_SINGLEPHY);
+ /* Now we let MAC0 run on 5G band. */
+ if (rtlhal->interfaceindex == 0) {
+ rtlhal->bandset = BAND_ON_5G;
+ rtlhal->current_bandtype = BAND_ON_5G;
+ } else {
+ rtlhal->bandset = BAND_ON_2_4G;
+ rtlhal->current_bandtype = BAND_ON_2_4G;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+u8 rtl92d_get_chnlgroup_fromarray(u8 chnl)
+{
+ u8 group;
+ u8 channel_info[59] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56,
+ 58, 60, 62, 64, 100, 102, 104, 106, 108,
+ 110, 112, 114, 116, 118, 120, 122, 124,
+ 126, 128, 130, 132, 134, 136, 138, 140,
+ 149, 151, 153, 155, 157, 159, 161, 163,
+ 165
+ };
+
+ if (channel_info[chnl] <= 3)
+ group = 0;
+ else if (channel_info[chnl] <= 9)
+ group = 1;
+ else if (channel_info[chnl] <= 14)
+ group = 2;
+ else if (channel_info[chnl] <= 44)
+ group = 3;
+ else if (channel_info[chnl] <= 54)
+ group = 4;
+ else if (channel_info[chnl] <= 64)
+ group = 5;
+ else if (channel_info[chnl] <= 112)
+ group = 6;
+ else if (channel_info[chnl] <= 126)
+ group = 7;
+ else if (channel_info[chnl] <= 140)
+ group = 8;
+ else if (channel_info[chnl] <= 153)
+ group = 9;
+ else if (channel_info[chnl] <= 159)
+ group = 10;
+ else
+ group = 11;
+ return group;
+}
+
+void rtl92d_phy_set_poweron(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ unsigned long flags;
+ u8 value8;
+ u16 i;
+ u32 mac_reg = (rtlhal->interfaceindex == 0 ? REG_MAC0 : REG_MAC1);
+
+ /* notice fw know band status 0x81[1]/0x53[1] = 0: 5G, 1: 2G */
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ value8 = rtl_read_byte(rtlpriv, mac_reg);
+ value8 |= BIT(1);
+ rtl_write_byte(rtlpriv, mac_reg, value8);
+ } else {
+ value8 = rtl_read_byte(rtlpriv, mac_reg);
+ value8 &= (~BIT(1));
+ rtl_write_byte(rtlpriv, mac_reg, value8);
+ }
+
+ if (rtlhal->macphymode == SINGLEMAC_SINGLEPHY) {
+ value8 = rtl_read_byte(rtlpriv, REG_MAC0);
+ rtl_write_byte(rtlpriv, REG_MAC0, value8 | MAC0_ON);
+ } else {
+ spin_lock_irqsave(&globalmutex_power, flags);
+ if (rtlhal->interfaceindex == 0) {
+ value8 = rtl_read_byte(rtlpriv, REG_MAC0);
+ rtl_write_byte(rtlpriv, REG_MAC0, value8 | MAC0_ON);
+ } else {
+ value8 = rtl_read_byte(rtlpriv, REG_MAC1);
+ rtl_write_byte(rtlpriv, REG_MAC1, value8 | MAC1_ON);
+ }
+ value8 = rtl_read_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS);
+ spin_unlock_irqrestore(&globalmutex_power, flags);
+ for (i = 0; i < 200; i++) {
+ if ((value8 & BIT(7)) == 0) {
+ break;
+ } else {
+ udelay(500);
+ spin_lock_irqsave(&globalmutex_power, flags);
+ value8 = rtl_read_byte(rtlpriv,
+ REG_POWER_OFF_IN_PROCESS);
+ spin_unlock_irqrestore(&globalmutex_power,
+ flags);
+ }
+ }
+ if (i == 200)
+ RT_ASSERT(false, ("Another mac power off over time\n"));
+ }
+}
+
+void rtl92d_phy_config_maccoexist_rfpage(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ switch (rtlpriv->rtlhal.macphymode) {
+ case DUALMAC_DUALPHY:
+ rtl_write_byte(rtlpriv, REG_DMC, 0x0);
+ rtl_write_byte(rtlpriv, REG_RX_PKT_LIMIT, 0x08);
+ rtl_write_word(rtlpriv, REG_TRXFF_BNDY + 2, 0x13ff);
+ break;
+ case DUALMAC_SINGLEPHY:
+ rtl_write_byte(rtlpriv, REG_DMC, 0xf8);
+ rtl_write_byte(rtlpriv, REG_RX_PKT_LIMIT, 0x08);
+ rtl_write_word(rtlpriv, REG_TRXFF_BNDY + 2, 0x13ff);
+ break;
+ case SINGLEMAC_SINGLEPHY:
+ rtl_write_byte(rtlpriv, REG_DMC, 0x0);
+ rtl_write_byte(rtlpriv, REG_RX_PKT_LIMIT, 0x10);
+ rtl_write_word(rtlpriv, (REG_TRXFF_BNDY + 2), 0x27FF);
+ break;
+ default:
+ break;
+ }
+}
+
+void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u8 rfpath, i;
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("==>\n"));
+ /* r_select_5G for path_A/B 0 for 2.4G, 1 for 5G */
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ /* r_select_5G for path_A/B,0x878 */
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(0), 0x0);
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(15), 0x0);
+ if (rtlhal->macphymode != DUALMAC_DUALPHY) {
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(16), 0x0);
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(31), 0x0);
+ }
+ /* rssi_table_select:index 0 for 2.4G.1~3 for 5G,0xc78 */
+ rtl_set_bbreg(hw, ROFDM0_AGCRSSITABLE, BIT(6) | BIT(7), 0x0);
+ /* fc_area 0xd2c */
+ rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(14) | BIT(13), 0x0);
+ /* 5G LAN ON */
+ rtl_set_bbreg(hw, 0xB30, 0x00F00000, 0xa);
+ /* TX BB gain shift*1,Just for testchip,0xc80,0xc88 */
+ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD,
+ 0x40000100);
+ rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD,
+ 0x40000100);
+ if (rtlhal->macphymode == DUALMAC_DUALPHY) {
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW,
+ BIT(10) | BIT(6) | BIT(5),
+ ((rtlefuse->eeprom_c9 & BIT(3)) >> 3) |
+ (rtlefuse->eeprom_c9 & BIT(1)) |
+ ((rtlefuse->eeprom_cc & BIT(1)) << 4));
+ rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE,
+ BIT(10) | BIT(6) | BIT(5),
+ ((rtlefuse->eeprom_c9 & BIT(2)) >> 2) |
+ ((rtlefuse->eeprom_c9 & BIT(0)) << 1) |
+ ((rtlefuse->eeprom_cc & BIT(0)) << 5));
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(15), 0);
+ } else {
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW,
+ BIT(26) | BIT(22) | BIT(21) | BIT(10) |
+ BIT(6) | BIT(5),
+ ((rtlefuse->eeprom_c9 & BIT(3)) >> 3) |
+ (rtlefuse->eeprom_c9 & BIT(1)) |
+ ((rtlefuse->eeprom_cc & BIT(1)) << 4) |
+ ((rtlefuse->eeprom_c9 & BIT(7)) << 9) |
+ ((rtlefuse->eeprom_c9 & BIT(5)) << 12) |
+ ((rtlefuse->eeprom_cc & BIT(3)) << 18));
+ rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE,
+ BIT(10) | BIT(6) | BIT(5),
+ ((rtlefuse->eeprom_c9 & BIT(2)) >> 2) |
+ ((rtlefuse->eeprom_c9 & BIT(0)) << 1) |
+ ((rtlefuse->eeprom_cc & BIT(0)) << 5));
+ rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE,
+ BIT(10) | BIT(6) | BIT(5),
+ ((rtlefuse->eeprom_c9 & BIT(6)) >> 6) |
+ ((rtlefuse->eeprom_c9 & BIT(4)) >> 3) |
+ ((rtlefuse->eeprom_cc & BIT(2)) << 3));
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER,
+ BIT(31) | BIT(15), 0);
+ }
+ /* 1.5V_LDO */
+ } else {
+ /* r_select_5G for path_A/B */
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(0), 0x1);
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(15), 0x1);
+ if (rtlhal->macphymode != DUALMAC_DUALPHY) {
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(16), 0x1);
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(31), 0x1);
+ }
+ /* rssi_table_select:index 0 for 2.4G.1~3 for 5G */
+ rtl_set_bbreg(hw, ROFDM0_AGCRSSITABLE, BIT(6) | BIT(7), 0x1);
+ /* fc_area */
+ rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(14) | BIT(13), 0x1);
+ /* 5G LAN ON */
+ rtl_set_bbreg(hw, 0xB30, 0x00F00000, 0x0);
+ /* TX BB gain shift,Just for testchip,0xc80,0xc88 */
+ if (rtlefuse->internal_pa_5g[0])
+ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD,
+ 0x2d4000b5);
+ else
+ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD,
+ 0x20000080);
+ if (rtlefuse->internal_pa_5g[1])
+ rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD,
+ 0x2d4000b5);
+ else
+ rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD,
+ 0x20000080);
+ if (rtlhal->macphymode == DUALMAC_DUALPHY) {
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW,
+ BIT(10) | BIT(6) | BIT(5),
+ (rtlefuse->eeprom_cc & BIT(5)));
+ rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BIT(10),
+ ((rtlefuse->eeprom_cc & BIT(4)) >> 4));
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(15),
+ (rtlefuse->eeprom_cc & BIT(4)) >> 4);
+ } else {
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW,
+ BIT(26) | BIT(22) | BIT(21) | BIT(10) |
+ BIT(6) | BIT(5),
+ (rtlefuse->eeprom_cc & BIT(5)) |
+ ((rtlefuse->eeprom_cc & BIT(7)) << 14));
+ rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BIT(10),
+ ((rtlefuse->eeprom_cc & BIT(4)) >> 4));
+ rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BIT(10),
+ ((rtlefuse->eeprom_cc & BIT(6)) >> 6));
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER,
+ BIT(31) | BIT(15),
+ ((rtlefuse->eeprom_cc & BIT(4)) >> 4) |
+ ((rtlefuse->eeprom_cc & BIT(6)) << 10));
+ }
+ }
+ /* update IQK related settings */
+ rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, BMASKDWORD, 0x40000100);
+ rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, BMASKDWORD, 0x40000100);
+ rtl_set_bbreg(hw, ROFDM0_XCTxAFE, 0xF0000000, 0x00);
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(30) | BIT(28) |
+ BIT(26) | BIT(24), 0x00);
+ rtl_set_bbreg(hw, ROFDM0_XDTxAFE, 0xF0000000, 0x00);
+ rtl_set_bbreg(hw, 0xca0, 0xF0000000, 0x00);
+ rtl_set_bbreg(hw, ROFDM0_AGCRSSITABLE, 0x0000F000, 0x00);
+
+ /* Update RF */
+ for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath;
+ rfpath++) {
+ if (rtlhal->current_bandtype == BAND_ON_2_4G) {
+ /* MOD_AG for RF paht_A 0x18 BIT8,BIT16 */
+ rtl_set_rfreg(hw, rfpath, RF_CHNLBW, BIT(8) | BIT(16) |
+ BIT(18), 0);
+ /* RF0x0b[16:14] =3b'111 */
+ rtl_set_rfreg(hw, (enum radio_path)rfpath, 0x0B,
+ 0x1c000, 0x07);
+ } else {
+ /* MOD_AG for RF paht_A 0x18 BIT8,BIT16 */
+ rtl_set_rfreg(hw, rfpath, RF_CHNLBW, BIT(8) |
+ BIT(16) | BIT(18),
+ (BIT(16) | BIT(8)) >> 8);
+ }
+ }
+ /* Update for all band. */
+ /* DMDP */
+ if (rtlphy->rf_type == RF_1T1R) {
+ /* Use antenna 0,0xc04,0xd04 */
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x11);
+ rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x1);
+
+ /* enable ad/da clock1 for dual-phy reg0x888 */
+ if (rtlhal->interfaceindex == 0) {
+ rtl_set_bbreg(hw, RFPGA0_ADDALLOCKEN, BIT(12) |
+ BIT(13), 0x3);
+ } else {
+ rtl92d_phy_enable_anotherphy(hw, false);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ ("MAC1 use DBI to update 0x888"));
+ /* 0x888 */
+ rtl92de_write_dword_dbi(hw, RFPGA0_ADDALLOCKEN,
+ rtl92de_read_dword_dbi(hw,
+ RFPGA0_ADDALLOCKEN,
+ BIT(3)) | BIT(12) | BIT(13),
+ BIT(3));
+ rtl92d_phy_powerdown_anotherphy(hw, false);
+ }
+ } else {
+ /* Single PHY */
+ /* Use antenna 0 & 1,0xc04,0xd04 */
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x33);
+ rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x3);
+ /* disable ad/da clock1,0x888 */
+ rtl_set_bbreg(hw, RFPGA0_ADDALLOCKEN, BIT(12) | BIT(13), 0);
+ }
+ for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath;
+ rfpath++) {
+ rtlphy->rfreg_chnlval[rfpath] = rtl_get_rfreg(hw, rfpath,
+ RF_CHNLBW, BRFREGOFFSETMASK);
+ rtlphy->reg_rf3c[rfpath] = rtl_get_rfreg(hw, rfpath, 0x3C,
+ BRFREGOFFSETMASK);
+ }
+ for (i = 0; i < 2; i++)
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, ("RF 0x18 = 0x%x\n",
+ rtlphy->rfreg_chnlval[i]));
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("<==\n"));
+
+}
+
+bool rtl92d_phy_check_poweroff(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 u1btmp;
+ unsigned long flags;
+
+ if (rtlhal->macphymode == SINGLEMAC_SINGLEPHY) {
+ u1btmp = rtl_read_byte(rtlpriv, REG_MAC0);
+ rtl_write_byte(rtlpriv, REG_MAC0, u1btmp & (~MAC0_ON));
+ return true;
+ }
+ spin_lock_irqsave(&globalmutex_power, flags);
+ if (rtlhal->interfaceindex == 0) {
+ u1btmp = rtl_read_byte(rtlpriv, REG_MAC0);
+ rtl_write_byte(rtlpriv, REG_MAC0, u1btmp & (~MAC0_ON));
+ u1btmp = rtl_read_byte(rtlpriv, REG_MAC1);
+ u1btmp &= MAC1_ON;
+ } else {
+ u1btmp = rtl_read_byte(rtlpriv, REG_MAC1);
+ rtl_write_byte(rtlpriv, REG_MAC1, u1btmp & (~MAC1_ON));
+ u1btmp = rtl_read_byte(rtlpriv, REG_MAC0);
+ u1btmp &= MAC0_ON;
+ }
+ if (u1btmp) {
+ spin_unlock_irqrestore(&globalmutex_power, flags);
+ return false;
+ }
+ u1btmp = rtl_read_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS);
+ u1btmp |= BIT(7);
+ rtl_write_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS, u1btmp);
+ spin_unlock_irqrestore(&globalmutex_power, flags);
+ return true;
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.h b/drivers/net/wireless/rtlwifi/rtl8192de/phy.h
new file mode 100644
index 000000000000..a52c824b41e3
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.h
@@ -0,0 +1,178 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92D_PHY_H__
+#define __RTL92D_PHY_H__
+
+#define MAX_PRECMD_CNT 16
+#define MAX_RFDEPENDCMD_CNT 16
+#define MAX_POSTCMD_CNT 16
+
+#define MAX_DOZE_WAITING_TIMES_9x 64
+
+#define RT_CANNOT_IO(hw) false
+#define HIGHPOWER_RADIOA_ARRAYLEN 22
+
+#define IQK_ADDA_REG_NUM 16
+#define MAX_TOLERANCE 5
+#define IQK_DELAY_TIME 1
+
+#define APK_BB_REG_NUM 5
+#define APK_AFE_REG_NUM 16
+#define APK_CURVE_REG_NUM 4
+#define PATH_NUM 2
+
+#define LOOP_LIMIT 5
+#define MAX_STALL_TIME 50
+#define ANTENNA_DIVERSITY_VALUE 0x80
+#define MAX_TXPWR_IDX_NMODE_92S 63
+#define RESET_CNT_LIMIT 3
+
+#define IQK_ADDA_REG_NUM 16
+#define IQK_BB_REG_NUM 10
+#define IQK_BB_REG_NUM_test 6
+#define IQK_MAC_REG_NUM 4
+#define RX_INDEX_MAPPING_NUM 15
+
+#define IQK_DELAY_TIME 1
+
+#define CT_OFFSET_MAC_ADDR 0X16
+
+#define CT_OFFSET_CCK_TX_PWR_IDX 0x5A
+#define CT_OFFSET_HT401S_TX_PWR_IDX 0x60
+#define CT_OFFSET_HT402S_TX_PWR_IDX_DIFF 0x66
+#define CT_OFFSET_HT20_TX_PWR_IDX_DIFF 0x69
+#define CT_OFFSET_OFDM_TX_PWR_IDX_DIFF 0x6C
+
+#define CT_OFFSET_HT40_MAX_PWR_OFFSET 0x6F
+#define CT_OFFSET_HT20_MAX_PWR_OFFSET 0x72
+
+#define CT_OFFSET_CHANNEL_PLAH 0x75
+#define CT_OFFSET_THERMAL_METER 0x78
+#define CT_OFFSET_RF_OPTION 0x79
+#define CT_OFFSET_VERSION 0x7E
+#define CT_OFFSET_CUSTOMER_ID 0x7F
+
+enum swchnlcmd_id {
+ CMDID_END,
+ CMDID_SET_TXPOWEROWER_LEVEL,
+ CMDID_BBREGWRITE10,
+ CMDID_WRITEPORT_ULONG,
+ CMDID_WRITEPORT_USHORT,
+ CMDID_WRITEPORT_UCHAR,
+ CMDID_RF_WRITEREG,
+};
+
+struct swchnlcmd {
+ enum swchnlcmd_id cmdid;
+ u32 para1;
+ u32 para2;
+ u32 msdelay;
+};
+
+enum baseband_config_type {
+ BASEBAND_CONFIG_PHY_REG = 0,
+ BASEBAND_CONFIG_AGC_TAB = 1,
+};
+
+enum rf_content {
+ radioa_txt = 0,
+ radiob_txt = 1,
+ radioc_txt = 2,
+ radiod_txt = 3
+};
+
+static inline void rtl92d_acquire_cckandrw_pagea_ctl(struct ieee80211_hw *hw,
+ unsigned long *flag)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (rtlpriv->rtlhal.interfaceindex == 1)
+ spin_lock_irqsave(&rtlpriv->locks.cck_and_rw_pagea_lock, *flag);
+}
+
+static inline void rtl92d_release_cckandrw_pagea_ctl(struct ieee80211_hw *hw,
+ unsigned long *flag)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (rtlpriv->rtlhal.interfaceindex == 1)
+ spin_unlock_irqrestore(&rtlpriv->locks.cck_and_rw_pagea_lock,
+ *flag);
+}
+
+extern u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw,
+ u32 regaddr, u32 bitmask);
+extern void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw,
+ u32 regaddr, u32 bitmask, u32 data);
+extern u32 rtl92d_phy_query_rf_reg(struct ieee80211_hw *hw,
+ enum radio_path rfpath, u32 regaddr,
+ u32 bitmask);
+extern void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw,
+ enum radio_path rfpath, u32 regaddr,
+ u32 bitmask, u32 data);
+extern bool rtl92d_phy_mac_config(struct ieee80211_hw *hw);
+extern bool rtl92d_phy_bb_config(struct ieee80211_hw *hw);
+extern bool rtl92d_phy_rf_config(struct ieee80211_hw *hw);
+extern bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw,
+ enum radio_path rfpath);
+extern void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
+extern void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel);
+extern void rtl92d_phy_scan_operation_backup(struct ieee80211_hw *hw,
+ u8 operation);
+extern void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw,
+ enum nl80211_channel_type ch_type);
+extern u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw);
+bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
+ enum rf_content content,
+ enum radio_path rfpath);
+bool rtl92d_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype);
+extern bool rtl92d_phy_set_rf_power_state(struct ieee80211_hw *hw,
+ enum rf_pwrstate rfpwr_state);
+
+void rtl92d_phy_config_macphymode(struct ieee80211_hw *hw);
+void rtl92d_phy_config_macphymode_info(struct ieee80211_hw *hw);
+u8 rtl92d_get_chnlgroup_fromarray(u8 chnl);
+void rtl92d_phy_set_poweron(struct ieee80211_hw *hw);
+void rtl92d_phy_config_maccoexist_rfpage(struct ieee80211_hw *hw);
+bool rtl92d_phy_check_poweroff(struct ieee80211_hw *hw);
+void rtl92d_phy_lc_calibrate(struct ieee80211_hw *hw);
+void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw);
+void rtl92d_phy_ap_calibrate(struct ieee80211_hw *hw, char delta);
+void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw);
+void rtl92d_phy_reset_iqk_result(struct ieee80211_hw *hw);
+void rtl92d_release_cckandrw_pagea_ctl(struct ieee80211_hw *hw,
+ unsigned long *flag);
+void rtl92d_acquire_cckandrw_pagea_ctl(struct ieee80211_hw *hw,
+ unsigned long *flag);
+u8 rtl92d_get_rightchnlplace_for_iqk(u8 chnl);
+void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel);
+void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
new file mode 100644
index 000000000000..131acc306fcc
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
@@ -0,0 +1,1313 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92D_REG_H__
+#define __RTL92D_REG_H__
+
+/* ----------------------------------------------------- */
+/* 0x0000h ~ 0x00FFh System Configuration */
+/* ----------------------------------------------------- */
+#define REG_SYS_ISO_CTRL 0x0000
+#define REG_SYS_FUNC_EN 0x0002
+#define REG_APS_FSMCO 0x0004
+#define REG_SYS_CLKR 0x0008
+#define REG_9346CR 0x000A
+#define REG_EE_VPD 0x000C
+#define REG_AFE_MISC 0x0010
+#define REG_SPS0_CTRL 0x0011
+#define REG_POWER_OFF_IN_PROCESS 0x0017
+#define REG_SPS_OCP_CFG 0x0018
+#define REG_RSV_CTRL 0x001C
+#define REG_RF_CTRL 0x001F
+#define REG_LDOA15_CTRL 0x0020
+#define REG_LDOV12D_CTRL 0x0021
+#define REG_LDOHCI12_CTRL 0x0022
+#define REG_LPLDO_CTRL 0x0023
+#define REG_AFE_XTAL_CTRL 0x0024
+#define REG_AFE_PLL_CTRL 0x0028
+/* for 92d, DMDP,SMSP,DMSP contrl */
+#define REG_MAC_PHY_CTRL 0x002c
+#define REG_EFUSE_CTRL 0x0030
+#define REG_EFUSE_TEST 0x0034
+#define REG_PWR_DATA 0x0038
+#define REG_CAL_TIMER 0x003C
+#define REG_ACLK_MON 0x003E
+#define REG_GPIO_MUXCFG 0x0040
+#define REG_GPIO_IO_SEL 0x0042
+#define REG_MAC_PINMUX_CFG 0x0043
+#define REG_GPIO_PIN_CTRL 0x0044
+#define REG_GPIO_INTM 0x0048
+#define REG_LEDCFG0 0x004C
+#define REG_LEDCFG1 0x004D
+#define REG_LEDCFG2 0x004E
+#define REG_LEDCFG3 0x004F
+#define REG_FSIMR 0x0050
+#define REG_FSISR 0x0054
+
+#define REG_MCUFWDL 0x0080
+
+#define REG_HMEBOX_EXT_0 0x0088
+#define REG_HMEBOX_EXT_1 0x008A
+#define REG_HMEBOX_EXT_2 0x008C
+#define REG_HMEBOX_EXT_3 0x008E
+
+#define REG_BIST_SCAN 0x00D0
+#define REG_BIST_RPT 0x00D4
+#define REG_BIST_ROM_RPT 0x00D8
+#define REG_USB_SIE_INTF 0x00E0
+#define REG_PCIE_MIO_INTF 0x00E4
+#define REG_PCIE_MIO_INTD 0x00E8
+#define REG_HPON_FSM 0x00EC
+#define REG_SYS_CFG 0x00F0
+#define REG_MAC_PHY_CTRL_NORMAL 0x00f8
+
+#define REG_MAC0 0x0081
+#define REG_MAC1 0x0053
+#define FW_MAC0_READY 0x18
+#define FW_MAC1_READY 0x1A
+#define MAC0_ON BIT(7)
+#define MAC1_ON BIT(0)
+#define MAC0_READY BIT(0)
+#define MAC1_READY BIT(0)
+
+/* ----------------------------------------------------- */
+/* 0x0100h ~ 0x01FFh MACTOP General Configuration */
+/* ----------------------------------------------------- */
+#define REG_CR 0x0100
+#define REG_PBP 0x0104
+#define REG_TRXDMA_CTRL 0x010C
+#define REG_TRXFF_BNDY 0x0114
+#define REG_TRXFF_STATUS 0x0118
+#define REG_RXFF_PTR 0x011C
+#define REG_HIMR 0x0120
+#define REG_HISR 0x0124
+#define REG_HIMRE 0x0128
+#define REG_HISRE 0x012C
+#define REG_CPWM 0x012F
+#define REG_FWIMR 0x0130
+#define REG_FWISR 0x0134
+#define REG_PKTBUF_DBG_CTRL 0x0140
+#define REG_PKTBUF_DBG_DATA_L 0x0144
+#define REG_PKTBUF_DBG_DATA_H 0x0148
+
+#define REG_TC0_CTRL 0x0150
+#define REG_TC1_CTRL 0x0154
+#define REG_TC2_CTRL 0x0158
+#define REG_TC3_CTRL 0x015C
+#define REG_TC4_CTRL 0x0160
+#define REG_TCUNIT_BASE 0x0164
+#define REG_MBIST_START 0x0174
+#define REG_MBIST_DONE 0x0178
+#define REG_MBIST_FAIL 0x017C
+#define REG_C2HEVT_MSG_NORMAL 0x01A0
+#define REG_C2HEVT_MSG_TEST 0x01B8
+#define REG_C2HEVT_CLEAR 0x01BF
+#define REG_MCUTST_1 0x01c0
+#define REG_FMETHR 0x01C8
+#define REG_HMETFR 0x01CC
+#define REG_HMEBOX_0 0x01D0
+#define REG_HMEBOX_1 0x01D4
+#define REG_HMEBOX_2 0x01D8
+#define REG_HMEBOX_3 0x01DC
+
+#define REG_LLT_INIT 0x01E0
+#define REG_BB_ACCEESS_CTRL 0x01E8
+#define REG_BB_ACCESS_DATA 0x01EC
+
+
+/* ----------------------------------------------------- */
+/* 0x0200h ~ 0x027Fh TXDMA Configuration */
+/* ----------------------------------------------------- */
+#define REG_RQPN 0x0200
+#define REG_FIFOPAGE 0x0204
+#define REG_TDECTRL 0x0208
+#define REG_TXDMA_OFFSET_CHK 0x020C
+#define REG_TXDMA_STATUS 0x0210
+#define REG_RQPN_NPQ 0x0214
+
+/* ----------------------------------------------------- */
+/* 0x0280h ~ 0x02FFh RXDMA Configuration */
+/* ----------------------------------------------------- */
+#define REG_RXDMA_AGG_PG_TH 0x0280
+#define REG_RXPKT_NUM 0x0284
+#define REG_RXDMA_STATUS 0x0288
+
+/* ----------------------------------------------------- */
+/* 0x0300h ~ 0x03FFh PCIe */
+/* ----------------------------------------------------- */
+#define REG_PCIE_CTRL_REG 0x0300
+#define REG_INT_MIG 0x0304
+#define REG_BCNQ_DESA 0x0308
+#define REG_HQ_DESA 0x0310
+#define REG_MGQ_DESA 0x0318
+#define REG_VOQ_DESA 0x0320
+#define REG_VIQ_DESA 0x0328
+#define REG_BEQ_DESA 0x0330
+#define REG_BKQ_DESA 0x0338
+#define REG_RX_DESA 0x0340
+#define REG_DBI 0x0348
+#define REG_DBI_WDATA 0x0348
+#define REG_DBI_RDATA 0x034C
+#define REG_DBI_CTRL 0x0350
+#define REG_DBI_FLAG 0x0352
+#define REG_MDIO 0x0354
+#define REG_DBG_SEL 0x0360
+#define REG_PCIE_HRPWM 0x0361
+#define REG_PCIE_HCPWM 0x0363
+#define REG_UART_CTRL 0x0364
+#define REG_UART_TX_DESA 0x0370
+#define REG_UART_RX_DESA 0x0378
+
+/* ----------------------------------------------------- */
+/* 0x0400h ~ 0x047Fh Protocol Configuration */
+/* ----------------------------------------------------- */
+#define REG_VOQ_INFORMATION 0x0400
+#define REG_VIQ_INFORMATION 0x0404
+#define REG_BEQ_INFORMATION 0x0408
+#define REG_BKQ_INFORMATION 0x040C
+#define REG_MGQ_INFORMATION 0x0410
+#define REG_HGQ_INFORMATION 0x0414
+#define REG_BCNQ_INFORMATION 0x0418
+
+
+#define REG_CPU_MGQ_INFORMATION 0x041C
+#define REG_FWHW_TXQ_CTRL 0x0420
+#define REG_HWSEQ_CTRL 0x0423
+#define REG_TXPKTBUF_BCNQ_BDNY 0x0424
+#define REG_TXPKTBUF_MGQ_BDNY 0x0425
+#define REG_MULTI_BCNQ_EN 0x0426
+#define REG_MULTI_BCNQ_OFFSET 0x0427
+#define REG_SPEC_SIFS 0x0428
+#define REG_RL 0x042A
+#define REG_DARFRC 0x0430
+#define REG_RARFRC 0x0438
+#define REG_RRSR 0x0440
+#define REG_ARFR0 0x0444
+#define REG_ARFR1 0x0448
+#define REG_ARFR2 0x044C
+#define REG_ARFR3 0x0450
+#define REG_AGGLEN_LMT 0x0458
+#define REG_AMPDU_MIN_SPACE 0x045C
+#define REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045D
+#define REG_FAST_EDCA_CTRL 0x0460
+#define REG_RD_RESP_PKT_TH 0x0463
+#define REG_INIRTS_RATE_SEL 0x0480
+#define REG_INIDATA_RATE_SEL 0x0484
+#define REG_POWER_STATUS 0x04A4
+#define REG_POWER_STAGE1 0x04B4
+#define REG_POWER_STAGE2 0x04B8
+#define REG_PKT_LIFE_TIME 0x04C0
+#define REG_STBC_SETTING 0x04C4
+#define REG_PROT_MODE_CTRL 0x04C8
+#define REG_MAX_AGGR_NUM 0x04CA
+#define REG_RTS_MAX_AGGR_NUM 0x04CB
+#define REG_BAR_MODE_CTRL 0x04CC
+#define REG_RA_TRY_RATE_AGG_LMT 0x04CF
+#define REG_EARLY_MODE_CONTROL 0x4D0
+#define REG_NQOS_SEQ 0x04DC
+#define REG_QOS_SEQ 0x04DE
+#define REG_NEED_CPU_HANDLE 0x04E0
+#define REG_PKT_LOSE_RPT 0x04E1
+#define REG_PTCL_ERR_STATUS 0x04E2
+#define REG_DUMMY 0x04FC
+
+/* ----------------------------------------------------- */
+/* 0x0500h ~ 0x05FFh EDCA Configuration */
+/* ----------------------------------------------------- */
+#define REG_EDCA_VO_PARAM 0x0500
+#define REG_EDCA_VI_PARAM 0x0504
+#define REG_EDCA_BE_PARAM 0x0508
+#define REG_EDCA_BK_PARAM 0x050C
+#define REG_BCNTCFG 0x0510
+#define REG_PIFS 0x0512
+#define REG_RDG_PIFS 0x0513
+#define REG_SIFS_CTX 0x0514
+#define REG_SIFS_TRX 0x0516
+#define REG_AGGR_BREAK_TIME 0x051A
+#define REG_SLOT 0x051B
+#define REG_TX_PTCL_CTRL 0x0520
+#define REG_TXPAUSE 0x0522
+#define REG_DIS_TXREQ_CLR 0x0523
+#define REG_RD_CTRL 0x0524
+#define REG_TBTT_PROHIBIT 0x0540
+#define REG_RD_NAV_NXT 0x0544
+#define REG_NAV_PROT_LEN 0x0546
+#define REG_BCN_CTRL 0x0550
+#define REG_USTIME_TSF 0x0551
+#define REG_MBID_NUM 0x0552
+#define REG_DUAL_TSF_RST 0x0553
+#define REG_BCN_INTERVAL 0x0554
+#define REG_MBSSID_BCN_SPACE 0x0554
+#define REG_DRVERLYINT 0x0558
+#define REG_BCNDMATIM 0x0559
+#define REG_ATIMWND 0x055A
+#define REG_BCN_MAX_ERR 0x055D
+#define REG_RXTSF_OFFSET_CCK 0x055E
+#define REG_RXTSF_OFFSET_OFDM 0x055F
+#define REG_TSFTR 0x0560
+#define REG_INIT_TSFTR 0x0564
+#define REG_PSTIMER 0x0580
+#define REG_TIMER0 0x0584
+#define REG_TIMER1 0x0588
+#define REG_ACMHWCTRL 0x05C0
+#define REG_ACMRSTCTRL 0x05C1
+#define REG_ACMAVG 0x05C2
+#define REG_VO_ADMTIME 0x05C4
+#define REG_VI_ADMTIME 0x05C6
+#define REG_BE_ADMTIME 0x05C8
+#define REG_EDCA_RANDOM_GEN 0x05CC
+#define REG_SCH_TXCMD 0x05D0
+
+/* Dual MAC Co-Existence Register */
+#define REG_DMC 0x05F0
+
+/* ----------------------------------------------------- */
+/* 0x0600h ~ 0x07FFh WMAC Configuration */
+/* ----------------------------------------------------- */
+#define REG_APSD_CTRL 0x0600
+#define REG_BWOPMODE 0x0603
+#define REG_TCR 0x0604
+#define REG_RCR 0x0608
+#define REG_RX_PKT_LIMIT 0x060C
+#define REG_RX_DLK_TIME 0x060D
+#define REG_RX_DRVINFO_SZ 0x060F
+
+#define REG_MACID 0x0610
+#define REG_BSSID 0x0618
+#define REG_MAR 0x0620
+#define REG_MBIDCAMCFG 0x0628
+
+#define REG_USTIME_EDCA 0x0638
+#define REG_MAC_SPEC_SIFS 0x063A
+#define REG_RESP_SIFS_CCK 0x063C
+#define REG_RESP_SIFS_OFDM 0x063E
+#define REG_ACKTO 0x0640
+#define REG_CTS2TO 0x0641
+#define REG_EIFS 0x0642
+
+
+/* WMA, BA, CCX */
+#define REG_NAV_CTRL 0x0650
+#define REG_BACAMCMD 0x0654
+#define REG_BACAMCONTENT 0x0658
+#define REG_LBDLY 0x0660
+#define REG_FWDLY 0x0661
+#define REG_RXERR_RPT 0x0664
+#define REG_WMAC_TRXPTCL_CTL 0x0668
+
+
+/* Security */
+#define REG_CAMCMD 0x0670
+#define REG_CAMWRITE 0x0674
+#define REG_CAMREAD 0x0678
+#define REG_CAMDBG 0x067C
+#define REG_SECCFG 0x0680
+
+/* Power */
+#define REG_WOW_CTRL 0x0690
+#define REG_PSSTATUS 0x0691
+#define REG_PS_RX_INFO 0x0692
+#define REG_LPNAV_CTRL 0x0694
+#define REG_WKFMCAM_CMD 0x0698
+#define REG_WKFMCAM_RWD 0x069C
+#define REG_RXFLTMAP0 0x06A0
+#define REG_RXFLTMAP1 0x06A2
+#define REG_RXFLTMAP2 0x06A4
+#define REG_BCN_PSR_RPT 0x06A8
+#define REG_CALB32K_CTRL 0x06AC
+#define REG_PKT_MON_CTRL 0x06B4
+#define REG_BT_COEX_TABLE 0x06C0
+#define REG_WMAC_RESP_TXINFO 0x06D8
+
+
+/* ----------------------------------------------------- */
+/* Redifine 8192C register definition for compatibility */
+/* ----------------------------------------------------- */
+#define CR9346 REG_9346CR
+#define MSR (REG_CR + 2)
+#define ISR REG_HISR
+#define TSFR REG_TSFTR
+
+#define MACIDR0 REG_MACID
+#define MACIDR4 (REG_MACID + 4)
+
+#define PBP REG_PBP
+
+#define IDR0 MACIDR0
+#define IDR4 MACIDR4
+
+/* ----------------------------------------------------- */
+/* 8192C (MSR) Media Status Register(Offset 0x4C, 8 bits)*/
+/* ----------------------------------------------------- */
+#define MSR_NOLINK 0x00
+#define MSR_ADHOC 0x01
+#define MSR_INFRA 0x02
+#define MSR_AP 0x03
+
+/* 6. Adaptive Control Registers (Offset: 0x0160 - 0x01CF) */
+/* ----------------------------------------------------- */
+/* 8192C Response Rate Set Register(offset 0x181, 24bits)*/
+/* ----------------------------------------------------- */
+#define RRSR_RSC_OFFSET 21
+#define RRSR_SHORT_OFFSET 23
+#define RRSR_RSC_BW_40M 0x600000
+#define RRSR_RSC_UPSUBCHNL 0x400000
+#define RRSR_RSC_LOWSUBCHNL 0x200000
+#define RRSR_SHORT 0x800000
+#define RRSR_1M BIT0
+#define RRSR_2M BIT1
+#define RRSR_5_5M BIT2
+#define RRSR_11M BIT3
+#define RRSR_6M BIT4
+#define RRSR_9M BIT5
+#define RRSR_12M BIT6
+#define RRSR_18M BIT7
+#define RRSR_24M BIT8
+#define RRSR_36M BIT9
+#define RRSR_48M BIT10
+#define RRSR_54M BIT11
+#define RRSR_MCS0 BIT12
+#define RRSR_MCS1 BIT13
+#define RRSR_MCS2 BIT14
+#define RRSR_MCS3 BIT15
+#define RRSR_MCS4 BIT16
+#define RRSR_MCS5 BIT17
+#define RRSR_MCS6 BIT18
+#define RRSR_MCS7 BIT19
+#define BRSR_ACKSHORTPMB BIT23
+
+/* ----------------------------------------------------- */
+/* 8192C Rate Definition */
+/* ----------------------------------------------------- */
+/* CCK */
+#define RATR_1M 0x00000001
+#define RATR_2M 0x00000002
+#define RATR_55M 0x00000004
+#define RATR_11M 0x00000008
+/* OFDM */
+#define RATR_6M 0x00000010
+#define RATR_9M 0x00000020
+#define RATR_12M 0x00000040
+#define RATR_18M 0x00000080
+#define RATR_24M 0x00000100
+#define RATR_36M 0x00000200
+#define RATR_48M 0x00000400
+#define RATR_54M 0x00000800
+/* MCS 1 Spatial Stream */
+#define RATR_MCS0 0x00001000
+#define RATR_MCS1 0x00002000
+#define RATR_MCS2 0x00004000
+#define RATR_MCS3 0x00008000
+#define RATR_MCS4 0x00010000
+#define RATR_MCS5 0x00020000
+#define RATR_MCS6 0x00040000
+#define RATR_MCS7 0x00080000
+/* MCS 2 Spatial Stream */
+#define RATR_MCS8 0x00100000
+#define RATR_MCS9 0x00200000
+#define RATR_MCS10 0x00400000
+#define RATR_MCS11 0x00800000
+#define RATR_MCS12 0x01000000
+#define RATR_MCS13 0x02000000
+#define RATR_MCS14 0x04000000
+#define RATR_MCS15 0x08000000
+
+/* CCK */
+#define RATE_1M BIT(0)
+#define RATE_2M BIT(1)
+#define RATE_5_5M BIT(2)
+#define RATE_11M BIT(3)
+/* OFDM */
+#define RATE_6M BIT(4)
+#define RATE_9M BIT(5)
+#define RATE_12M BIT(6)
+#define RATE_18M BIT(7)
+#define RATE_24M BIT(8)
+#define RATE_36M BIT(9)
+#define RATE_48M BIT(10)
+#define RATE_54M BIT(11)
+/* MCS 1 Spatial Stream */
+#define RATE_MCS0 BIT(12)
+#define RATE_MCS1 BIT(13)
+#define RATE_MCS2 BIT(14)
+#define RATE_MCS3 BIT(15)
+#define RATE_MCS4 BIT(16)
+#define RATE_MCS5 BIT(17)
+#define RATE_MCS6 BIT(18)
+#define RATE_MCS7 BIT(19)
+/* MCS 2 Spatial Stream */
+#define RATE_MCS8 BIT(20)
+#define RATE_MCS9 BIT(21)
+#define RATE_MCS10 BIT(22)
+#define RATE_MCS11 BIT(23)
+#define RATE_MCS12 BIT(24)
+#define RATE_MCS13 BIT(25)
+#define RATE_MCS14 BIT(26)
+#define RATE_MCS15 BIT(27)
+
+/* ALL CCK Rate */
+#define RATE_ALL_CCK (RATR_1M | RATR_2M | RATR_55M | \
+ RATR_11M)
+#define RATE_ALL_OFDM_AG (RATR_6M | RATR_9M | RATR_12M | \
+ RATR_18M | RATR_24M | \
+ RATR_36M | RATR_48M | RATR_54M)
+#define RATE_ALL_OFDM_1SS (RATR_MCS0 | RATR_MCS1 | RATR_MCS2 | \
+ RATR_MCS3 | RATR_MCS4 | RATR_MCS5 | \
+ RATR_MCS6 | RATR_MCS7)
+#define RATE_ALL_OFDM_2SS (RATR_MCS8 | RATR_MCS9 | RATR_MCS10 | \
+ RATR_MCS11 | RATR_MCS12 | RATR_MCS13 | \
+ RATR_MCS14 | RATR_MCS15)
+
+/* ----------------------------------------------------- */
+/* 8192C BW_OPMODE bits (Offset 0x203, 8bit) */
+/* ----------------------------------------------------- */
+#define BW_OPMODE_20MHZ BIT(2)
+#define BW_OPMODE_5G BIT(1)
+#define BW_OPMODE_11J BIT(0)
+
+
+/* ----------------------------------------------------- */
+/* 8192C CAM Config Setting (offset 0x250, 1 byte) */
+/* ----------------------------------------------------- */
+#define CAM_VALID BIT(15)
+#define CAM_NOTVALID 0x0000
+#define CAM_USEDK BIT(5)
+
+#define CAM_NONE 0x0
+#define CAM_WEP40 0x01
+#define CAM_TKIP 0x02
+#define CAM_AES 0x04
+#define CAM_WEP104 0x05
+#define CAM_SMS4 0x6
+
+
+#define TOTAL_CAM_ENTRY 32
+#define HALF_CAM_ENTRY 16
+
+#define CAM_WRITE BIT(16)
+#define CAM_READ 0x00000000
+#define CAM_POLLINIG BIT(31)
+
+/* 10. Power Save Control Registers (Offset: 0x0260 - 0x02DF) */
+#define WOW_PMEN BIT0 /* Power management Enable. */
+#define WOW_WOMEN BIT1 /* WoW function on or off. */
+#define WOW_MAGIC BIT2 /* Magic packet */
+#define WOW_UWF BIT3 /* Unicast Wakeup frame. */
+
+/* 12. Host Interrupt Status Registers (Offset: 0x0300 - 0x030F) */
+/* ----------------------------------------------------- */
+/* 8190 IMR/ISR bits (offset 0xfd, 8bits) */
+/* ----------------------------------------------------- */
+#define IMR8190_DISABLED 0x0
+#define IMR_BCNDMAINT6 BIT(31)
+#define IMR_BCNDMAINT5 BIT(30)
+#define IMR_BCNDMAINT4 BIT(29)
+#define IMR_BCNDMAINT3 BIT(28)
+#define IMR_BCNDMAINT2 BIT(27)
+#define IMR_BCNDMAINT1 BIT(26)
+#define IMR_BCNDOK8 BIT(25)
+#define IMR_BCNDOK7 BIT(24)
+#define IMR_BCNDOK6 BIT(23)
+#define IMR_BCNDOK5 BIT(22)
+#define IMR_BCNDOK4 BIT(21)
+#define IMR_BCNDOK3 BIT(20)
+#define IMR_BCNDOK2 BIT(19)
+#define IMR_BCNDOK1 BIT(18)
+#define IMR_TIMEOUT2 BIT(17)
+#define IMR_TIMEOUT1 BIT(16)
+#define IMR_TXFOVW BIT(15)
+#define IMR_PSTIMEOUT BIT(14)
+#define IMR_BcnInt BIT(13)
+#define IMR_RXFOVW BIT(12)
+#define IMR_RDU BIT(11)
+#define IMR_ATIMEND BIT(10)
+#define IMR_BDOK BIT(9)
+#define IMR_HIGHDOK BIT(8)
+#define IMR_TBDOK BIT(7)
+#define IMR_MGNTDOK BIT(6)
+#define IMR_TBDER BIT(5)
+#define IMR_BKDOK BIT(4)
+#define IMR_BEDOK BIT(3)
+#define IMR_VIDOK BIT(2)
+#define IMR_VODOK BIT(1)
+#define IMR_ROK BIT(0)
+
+#define IMR_TXERR BIT(11)
+#define IMR_RXERR BIT(10)
+#define IMR_C2HCMD BIT(9)
+#define IMR_CPWM BIT(8)
+#define IMR_OCPINT BIT(1)
+#define IMR_WLANOFF BIT(0)
+
+/* ----------------------------------------------------- */
+/* 8192C EFUSE */
+/* ----------------------------------------------------- */
+#define HWSET_MAX_SIZE 256
+#define EFUSE_MAX_SECTION 32
+#define EFUSE_REAL_CONTENT_LEN 512
+
+/* ----------------------------------------------------- */
+/* 8192C EEPROM/EFUSE share register definition. */
+/* ----------------------------------------------------- */
+#define EEPROM_DEFAULT_TSSI 0x0
+#define EEPROM_DEFAULT_CRYSTALCAP 0x0
+#define EEPROM_DEFAULT_THERMALMETER 0x12
+
+#define EEPROM_DEFAULT_TXPOWERLEVEL_2G 0x2C
+#define EEPROM_DEFAULT_TXPOWERLEVEL_5G 0x22
+
+#define EEPROM_DEFAULT_HT40_2SDIFF 0x0
+/* HT20<->40 default Tx Power Index Difference */
+#define EEPROM_DEFAULT_HT20_DIFF 2
+/* OFDM Tx Power index diff */
+#define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x4
+#define EEPROM_DEFAULT_HT40_PWRMAXOFFSET 0
+#define EEPROM_DEFAULT_HT20_PWRMAXOFFSET 0
+
+#define EEPROM_CHANNEL_PLAN_FCC 0x0
+#define EEPROM_CHANNEL_PLAN_IC 0x1
+#define EEPROM_CHANNEL_PLAN_ETSI 0x2
+#define EEPROM_CHANNEL_PLAN_SPAIN 0x3
+#define EEPROM_CHANNEL_PLAN_FRANCE 0x4
+#define EEPROM_CHANNEL_PLAN_MKK 0x5
+#define EEPROM_CHANNEL_PLAN_MKK1 0x6
+#define EEPROM_CHANNEL_PLAN_ISRAEL 0x7
+#define EEPROM_CHANNEL_PLAN_TELEC 0x8
+#define EEPROM_CHANNEL_PLAN_GLOBAL_DOMAIN 0x9
+#define EEPROM_CHANNEL_PLAN_WORLD_WIDE_13 0xA
+#define EEPROM_CHANNEL_PLAN_NCC 0xB
+#define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80
+
+#define EEPROM_CID_DEFAULT 0x0
+#define EEPROM_CID_TOSHIBA 0x4
+#define EEPROM_CID_CCX 0x10
+#define EEPROM_CID_QMI 0x0D
+#define EEPROM_CID_WHQL 0xFE
+
+
+#define RTL8192_EEPROM_ID 0x8129
+#define EEPROM_WAPI_SUPPORT 0x78
+
+
+#define RTL8190_EEPROM_ID 0x8129 /* 0-1 */
+#define EEPROM_HPON 0x02 /* LDO settings.2-5 */
+#define EEPROM_CLK 0x06 /* Clock settings.6-7 */
+#define EEPROM_MAC_FUNCTION 0x08 /* SE Test mode.8 */
+
+#define EEPROM_VID 0x28 /* SE Vendor ID.A-B */
+#define EEPROM_DID 0x2A /* SE Device ID. C-D */
+#define EEPROM_SVID 0x2C /* SE Vendor ID.E-F */
+#define EEPROM_SMID 0x2E /* SE PCI Subsystem ID. 10-11 */
+
+#define EEPROM_MAC_ADDR 0x16 /* SEMAC Address. 12-17 */
+#define EEPROM_MAC_ADDR_MAC0_92D 0x55
+#define EEPROM_MAC_ADDR_MAC1_92D 0x5B
+
+/* 2.4G band Tx power index setting */
+#define EEPROM_CCK_TX_PWR_INX_2G 0x61
+#define EEPROM_HT40_1S_TX_PWR_INX_2G 0x67
+#define EEPROM_HT40_2S_TX_PWR_INX_DIFF_2G 0x6D
+#define EEPROM_HT20_TX_PWR_INX_DIFF_2G 0x70
+#define EEPROM_OFDM_TX_PWR_INX_DIFF_2G 0x73
+#define EEPROM_HT40_MAX_PWR_OFFSET_2G 0x76
+#define EEPROM_HT20_MAX_PWR_OFFSET_2G 0x79
+
+/*5GL channel 32-64 */
+#define EEPROM_HT40_1S_TX_PWR_INX_5GL 0x7C
+#define EEPROM_HT40_2S_TX_PWR_INX_DIFF_5GL 0x82
+#define EEPROM_HT20_TX_PWR_INX_DIFF_5GL 0x85
+#define EEPROM_OFDM_TX_PWR_INX_DIFF_5GL 0x88
+#define EEPROM_HT40_MAX_PWR_OFFSET_5GL 0x8B
+#define EEPROM_HT20_MAX_PWR_OFFSET_5GL 0x8E
+
+/* 5GM channel 100-140 */
+#define EEPROM_HT40_1S_TX_PWR_INX_5GM 0x91
+#define EEPROM_HT40_2S_TX_PWR_INX_DIFF_5GM 0x97
+#define EEPROM_HT20_TX_PWR_INX_DIFF_5GM 0x9A
+#define EEPROM_OFDM_TX_PWR_INX_DIFF_5GM 0x9D
+#define EEPROM_HT40_MAX_PWR_OFFSET_5GM 0xA0
+#define EEPROM_HT20_MAX_PWR_OFFSET_5GM 0xA3
+
+/* 5GH channel 149-165 */
+#define EEPROM_HT40_1S_TX_PWR_INX_5GH 0xA6
+#define EEPROM_HT40_2S_TX_PWR_INX_DIFF_5GH 0xAC
+#define EEPROM_HT20_TX_PWR_INX_DIFF_5GH 0xAF
+#define EEPROM_OFDM_TX_PWR_INX_DIFF_5GH 0xB2
+#define EEPROM_HT40_MAX_PWR_OFFSET_5GH 0xB5
+#define EEPROM_HT20_MAX_PWR_OFFSET_5GH 0xB8
+
+/* Map of supported channels. */
+#define EEPROM_CHANNEL_PLAN 0xBB
+#define EEPROM_IQK_DELTA 0xBC
+#define EEPROM_LCK_DELTA 0xBC
+#define EEPROM_XTAL_K 0xBD /* [7:5] */
+#define EEPROM_TSSI_A_5G 0xBE
+#define EEPROM_TSSI_B_5G 0xBF
+#define EEPROM_TSSI_AB_5G 0xC0
+#define EEPROM_THERMAL_METER 0xC3 /* [4:0] */
+#define EEPROM_RF_OPT1 0xC4
+#define EEPROM_RF_OPT2 0xC5
+#define EEPROM_RF_OPT3 0xC6
+#define EEPROM_RF_OPT4 0xC7
+#define EEPROM_RF_OPT5 0xC8
+#define EEPROM_RF_OPT6 0xC9
+#define EEPROM_VERSION 0xCA
+#define EEPROM_CUSTOMER_ID 0xCB
+#define EEPROM_RF_OPT7 0xCC
+
+#define EEPROM_DEF_PART_NO 0x3FD /* Byte */
+#define EEPROME_CHIP_VERSION_L 0x3FF
+#define EEPROME_CHIP_VERSION_H 0x3FE
+
+/*
+ * Current IOREG MAP
+ * 0x0000h ~ 0x00FFh System Configuration (256 Bytes)
+ * 0x0100h ~ 0x01FFh MACTOP General Configuration (256 Bytes)
+ * 0x0200h ~ 0x027Fh TXDMA Configuration (128 Bytes)
+ * 0x0280h ~ 0x02FFh RXDMA Configuration (128 Bytes)
+ * 0x0300h ~ 0x03FFh PCIE EMAC Reserved Region (256 Bytes)
+ * 0x0400h ~ 0x04FFh Protocol Configuration (256 Bytes)
+ * 0x0500h ~ 0x05FFh EDCA Configuration (256 Bytes)
+ * 0x0600h ~ 0x07FFh WMAC Configuration (512 Bytes)
+ * 0x2000h ~ 0x3FFFh 8051 FW Download Region (8196 Bytes)
+ */
+
+/* ----------------------------------------------------- */
+/* 8192C (RCR) (Offset 0x608, 32 bits) */
+/* ----------------------------------------------------- */
+#define RCR_APPFCS BIT(31)
+#define RCR_APP_MIC BIT(30)
+#define RCR_APP_ICV BIT(29)
+#define RCR_APP_PHYST_RXFF BIT(28)
+#define RCR_APP_BA_SSN BIT(27)
+#define RCR_ENMBID BIT(24)
+#define RCR_LSIGEN BIT(23)
+#define RCR_MFBEN BIT(22)
+#define RCR_HTC_LOC_CTRL BIT(14)
+#define RCR_AMF BIT(13)
+#define RCR_ACF BIT(12)
+#define RCR_ADF BIT(11)
+#define RCR_AICV BIT(9)
+#define RCR_ACRC32 BIT(8)
+#define RCR_CBSSID_BCN BIT(7)
+#define RCR_CBSSID_DATA BIT(6)
+#define RCR_APWRMGT BIT(5)
+#define RCR_ADD3 BIT(4)
+#define RCR_AB BIT(3)
+#define RCR_AM BIT(2)
+#define RCR_APM BIT(1)
+#define RCR_AAP BIT(0)
+#define RCR_MXDMA_OFFSET 8
+#define RCR_FIFO_OFFSET 13
+
+/* ----------------------------------------------------- */
+/* 8192C Regsiter Bit and Content definition */
+/* ----------------------------------------------------- */
+/* ----------------------------------------------------- */
+/* 0x0000h ~ 0x00FFh System Configuration */
+/* ----------------------------------------------------- */
+
+/* SPS0_CTRL */
+#define SW18_FPWM BIT(3)
+
+
+/* SYS_ISO_CTRL */
+#define ISO_MD2PP BIT(0)
+#define ISO_UA2USB BIT(1)
+#define ISO_UD2CORE BIT(2)
+#define ISO_PA2PCIE BIT(3)
+#define ISO_PD2CORE BIT(4)
+#define ISO_IP2MAC BIT(5)
+#define ISO_DIOP BIT(6)
+#define ISO_DIOE BIT(7)
+#define ISO_EB2CORE BIT(8)
+#define ISO_DIOR BIT(9)
+
+#define PWC_EV25V BIT(14)
+#define PWC_EV12V BIT(15)
+
+
+/* SYS_FUNC_EN */
+#define FEN_BBRSTB BIT(0)
+#define FEN_BB_GLB_RSTn BIT(1)
+#define FEN_USBA BIT(2)
+#define FEN_UPLL BIT(3)
+#define FEN_USBD BIT(4)
+#define FEN_DIO_PCIE BIT(5)
+#define FEN_PCIEA BIT(6)
+#define FEN_PPLL BIT(7)
+#define FEN_PCIED BIT(8)
+#define FEN_DIOE BIT(9)
+#define FEN_CPUEN BIT(10)
+#define FEN_DCORE BIT(11)
+#define FEN_ELDR BIT(12)
+#define FEN_DIO_RF BIT(13)
+#define FEN_HWPDN BIT(14)
+#define FEN_MREGEN BIT(15)
+
+/* APS_FSMCO */
+#define PFM_LDALL BIT(0)
+#define PFM_ALDN BIT(1)
+#define PFM_LDKP BIT(2)
+#define PFM_WOWL BIT(3)
+#define EnPDN BIT(4)
+#define PDN_PL BIT(5)
+#define APFM_ONMAC BIT(8)
+#define APFM_OFF BIT(9)
+#define APFM_RSM BIT(10)
+#define AFSM_HSUS BIT(11)
+#define AFSM_PCIE BIT(12)
+#define APDM_MAC BIT(13)
+#define APDM_HOST BIT(14)
+#define APDM_HPDN BIT(15)
+#define RDY_MACON BIT(16)
+#define SUS_HOST BIT(17)
+#define ROP_ALD BIT(20)
+#define ROP_PWR BIT(21)
+#define ROP_SPS BIT(22)
+#define SOP_MRST BIT(25)
+#define SOP_FUSE BIT(26)
+#define SOP_ABG BIT(27)
+#define SOP_AMB BIT(28)
+#define SOP_RCK BIT(29)
+#define SOP_A8M BIT(30)
+#define XOP_BTCK BIT(31)
+
+/* SYS_CLKR */
+#define ANAD16V_EN BIT(0)
+#define ANA8M BIT(1)
+#define MACSLP BIT(4)
+#define LOADER_CLK_EN BIT(5)
+#define _80M_SSC_DIS BIT(7)
+#define _80M_SSC_EN_HO BIT(8)
+#define PHY_SSC_RSTB BIT(9)
+#define SEC_CLK_EN BIT(10)
+#define MAC_CLK_EN BIT(11)
+#define SYS_CLK_EN BIT(12)
+#define RING_CLK_EN BIT(13)
+
+
+/* 9346CR */
+#define BOOT_FROM_EEPROM BIT(4)
+#define EEPROM_EN BIT(5)
+
+/* AFE_MISC */
+#define AFE_BGEN BIT(0)
+#define AFE_MBEN BIT(1)
+#define MAC_ID_EN BIT(7)
+
+/* RSV_CTRL */
+#define WLOCK_ALL BIT(0)
+#define WLOCK_00 BIT(1)
+#define WLOCK_04 BIT(2)
+#define WLOCK_08 BIT(3)
+#define WLOCK_40 BIT(4)
+#define R_DIS_PRST_0 BIT(5)
+#define R_DIS_PRST_1 BIT(6)
+#define LOCK_ALL_EN BIT(7)
+
+/* RF_CTRL */
+#define RF_EN BIT(0)
+#define RF_RSTB BIT(1)
+#define RF_SDMRSTB BIT(2)
+
+
+
+/* LDOA15_CTRL */
+#define LDA15_EN BIT(0)
+#define LDA15_STBY BIT(1)
+#define LDA15_OBUF BIT(2)
+#define LDA15_REG_VOS BIT(3)
+#define _LDA15_VOADJ(x) (((x) & 0x7) << 4)
+
+
+
+/* LDOV12D_CTRL */
+#define LDV12_EN BIT(0)
+#define LDV12_SDBY BIT(1)
+#define LPLDO_HSM BIT(2)
+#define LPLDO_LSM_DIS BIT(3)
+#define _LDV12_VADJ(x) (((x) & 0xF) << 4)
+
+
+/* AFE_XTAL_CTRL */
+#define XTAL_EN BIT(0)
+#define XTAL_BSEL BIT(1)
+#define _XTAL_BOSC(x) (((x) & 0x3) << 2)
+#define _XTAL_CADJ(x) (((x) & 0xF) << 4)
+#define XTAL_GATE_USB BIT(8)
+#define _XTAL_USB_DRV(x) (((x) & 0x3) << 9)
+#define XTAL_GATE_AFE BIT(11)
+#define _XTAL_AFE_DRV(x) (((x) & 0x3) << 12)
+#define XTAL_RF_GATE BIT(14)
+#define _XTAL_RF_DRV(x) (((x) & 0x3) << 15)
+#define XTAL_GATE_DIG BIT(17)
+#define _XTAL_DIG_DRV(x) (((x) & 0x3) << 18)
+#define XTAL_BT_GATE BIT(20)
+#define _XTAL_BT_DRV(x) (((x) & 0x3) << 21)
+#define _XTAL_GPIO(x) (((x) & 0x7) << 23)
+
+
+#define CKDLY_AFE BIT(26)
+#define CKDLY_USB BIT(27)
+#define CKDLY_DIG BIT(28)
+#define CKDLY_BT BIT(29)
+
+
+/* AFE_PLL_CTRL */
+#define APLL_EN BIT(0)
+#define APLL_320_EN BIT(1)
+#define APLL_FREF_SEL BIT(2)
+#define APLL_EDGE_SEL BIT(3)
+#define APLL_WDOGB BIT(4)
+#define APLL_LPFEN BIT(5)
+
+#define APLL_REF_CLK_13MHZ 0x1
+#define APLL_REF_CLK_19_2MHZ 0x2
+#define APLL_REF_CLK_20MHZ 0x3
+#define APLL_REF_CLK_25MHZ 0x4
+#define APLL_REF_CLK_26MHZ 0x5
+#define APLL_REF_CLK_38_4MHZ 0x6
+#define APLL_REF_CLK_40MHZ 0x7
+
+#define APLL_320EN BIT(14)
+#define APLL_80EN BIT(15)
+#define APLL_1MEN BIT(24)
+
+
+/* EFUSE_CTRL */
+#define ALD_EN BIT(18)
+#define EF_PD BIT(19)
+#define EF_FLAG BIT(31)
+
+/* EFUSE_TEST */
+#define EF_TRPT BIT(7)
+#define LDOE25_EN BIT(31)
+
+/* MCUFWDL */
+#define MCUFWDL_EN BIT(0)
+#define MCUFWDL_RDY BIT(1)
+#define FWDL_ChkSum_rpt BIT(2)
+#define MACINI_RDY BIT(3)
+#define BBINI_RDY BIT(4)
+#define RFINI_RDY BIT(5)
+#define WINTINI_RDY BIT(6)
+#define MAC1_WINTINI_RDY BIT(11)
+#define CPRST BIT(23)
+
+/* REG_SYS_CFG */
+#define XCLK_VLD BIT(0)
+#define ACLK_VLD BIT(1)
+#define UCLK_VLD BIT(2)
+#define PCLK_VLD BIT(3)
+#define PCIRSTB BIT(4)
+#define V15_VLD BIT(5)
+#define TRP_B15V_EN BIT(7)
+#define SIC_IDLE BIT(8)
+#define BD_MAC2 BIT(9)
+#define BD_MAC1 BIT(10)
+#define IC_MACPHY_MODE BIT(11)
+#define PAD_HWPD_IDN BIT(22)
+#define TRP_VAUX_EN BIT(23)
+#define TRP_BT_EN BIT(24)
+#define BD_PKG_SEL BIT(25)
+#define BD_HCI_SEL BIT(26)
+#define TYPE_ID BIT(27)
+
+/* LLT_INIT */
+#define _LLT_NO_ACTIVE 0x0
+#define _LLT_WRITE_ACCESS 0x1
+#define _LLT_READ_ACCESS 0x2
+
+#define _LLT_INIT_DATA(x) ((x) & 0xFF)
+#define _LLT_INIT_ADDR(x) (((x) & 0xFF) << 8)
+#define _LLT_OP(x) (((x) & 0x3) << 30)
+#define _LLT_OP_VALUE(x) (((x) >> 30) & 0x3)
+
+
+/* ----------------------------------------------------- */
+/* 0x0400h ~ 0x047Fh Protocol Configuration */
+/* ----------------------------------------------------- */
+#define RETRY_LIMIT_SHORT_SHIFT 8
+#define RETRY_LIMIT_LONG_SHIFT 0
+
+
+/* ----------------------------------------------------- */
+/* 0x0500h ~ 0x05FFh EDCA Configuration */
+/* ----------------------------------------------------- */
+/* EDCA setting */
+#define AC_PARAM_TXOP_LIMIT_OFFSET 16
+#define AC_PARAM_ECW_MAX_OFFSET 12
+#define AC_PARAM_ECW_MIN_OFFSET 8
+#define AC_PARAM_AIFS_OFFSET 0
+
+/* ACMHWCTRL */
+#define ACMHW_HWEN BIT(0)
+#define ACMHW_BEQEN BIT(1)
+#define ACMHW_VIQEN BIT(2)
+#define ACMHW_VOQEN BIT(3)
+
+/* ----------------------------------------------------- */
+/* 0x0600h ~ 0x07FFh WMAC Configuration */
+/* ----------------------------------------------------- */
+
+/* TCR */
+#define TSFRST BIT(0)
+#define DIS_GCLK BIT(1)
+#define PAD_SEL BIT(2)
+#define PWR_ST BIT(6)
+#define PWRBIT_OW_EN BIT(7)
+#define ACRC BIT(8)
+#define CFENDFORM BIT(9)
+#define ICV BIT(10)
+
+/* SECCFG */
+#define SCR_TXUSEDK BIT(0)
+#define SCR_RXUSEDK BIT(1)
+#define SCR_TXENCENABLE BIT(2)
+#define SCR_RXENCENABLE BIT(3)
+#define SCR_SKBYA2 BIT(4)
+#define SCR_NOSKMC BIT(5)
+#define SCR_TXBCUSEDK BIT(6)
+#define SCR_RXBCUSEDK BIT(7)
+
+/* General definitions */
+#define MAC_ADDR_LEN 6
+#define LAST_ENTRY_OF_TX_PKT_BUFFER 255
+#define LAST_ENTRY_OF_TX_PKT_BUFFER_DUAL_MAC 127
+
+#define POLLING_LLT_THRESHOLD 20
+#define POLLING_READY_TIMEOUT_COUNT 1000
+
+/* Min Spacing related settings. */
+#define MAX_MSS_DENSITY_2T 0x13
+#define MAX_MSS_DENSITY_1T 0x0A
+
+
+/* BB-PHY register PMAC 0x100 PHY 0x800 - 0xEFF */
+/* 1. PMAC duplicate register due to connection: */
+/* RF_Mode, TRxRN, NumOf L-STF */
+/* 2. 0x800/0x900/0xA00/0xC00/0xD00/0xE00 */
+/* 3. RF register 0x00-2E */
+/* 4. Bit Mask for BB/RF register */
+/* 5. Other defintion for BB/RF R/W */
+
+/* 3. Page8(0x800) */
+#define RFPGA0_RFMOD 0x800
+
+#define RFPGA0_TXINFO 0x804
+#define RFPGA0_PSDFUNCTION 0x808
+
+#define RFPGA0_TXGAINSTAGE 0x80c
+
+#define RFPGA0_RFTIMING1 0x810
+#define RFPGA0_RFTIMING2 0x814
+
+#define RFPGA0_XA_HSSIPARAMETER1 0x820
+#define RFPGA0_XA_HSSIPARAMETER2 0x824
+#define RFPGA0_XB_HSSIPARAMETER1 0x828
+#define RFPGA0_XB_HSSIPARAMETER2 0x82c
+
+#define RFPGA0_XA_LSSIPARAMETER 0x840
+#define RFPGA0_XB_LSSIPARAMETER 0x844
+
+#define RFPGA0_RFWAkEUPPARAMETER 0x850
+#define RFPGA0_RFSLEEPUPPARAMETER 0x854
+
+#define RFPGA0_XAB_SWITCHCONTROL 0x858
+#define RFPGA0_XCD_SWITCHCONTROL 0x85c
+
+#define RFPGA0_XA_RFINTERFACEOE 0x860
+#define RFPGA0_XB_RFINTERFACEOE 0x864
+
+#define RFPGA0_XAB_RFINTERFACESW 0x870
+#define RFPGA0_XCD_RFINTERFACESW 0x874
+
+#define RFPGA0_XAB_RFPARAMETER 0x878
+#define RFPGA0_XCD_RFPARAMETER 0x87c
+
+#define RFPGA0_ANALOGPARAMETER1 0x880
+#define RFPGA0_ANALOGPARAMETER2 0x884
+#define RFPGA0_ANALOGPARAMETER3 0x888
+#define RFPGA0_ADDALLOCKEN 0x888
+#define RFPGA0_ANALOGPARAMETER4 0x88c
+
+#define RFPGA0_XA_LSSIREADBACK 0x8a0
+#define RFPGA0_XB_LSSIREADBACK 0x8a4
+#define RFPGA0_XC_LSSIREADBACK 0x8a8
+#define RFPGA0_XD_LSSIREADBACK 0x8ac
+
+#define RFPGA0_PSDREPORT 0x8b4
+#define TRANSCEIVERA_HSPI_READBACK 0x8b8
+#define TRANSCEIVERB_HSPI_READBACK 0x8bc
+#define RFPGA0_XAB_RFINTERFACERB 0x8e0
+#define RFPGA0_XCD_RFINTERFACERB 0x8e4
+
+/* 4. Page9(0x900) */
+#define RFPGA1_RFMOD 0x900
+
+#define RFPGA1_TXBLOCK 0x904
+#define RFPGA1_DEBUGSELECT 0x908
+#define RFPGA1_TXINFO 0x90c
+
+/* 5. PageA(0xA00) */
+#define RCCK0_SYSTEM 0xa00
+
+#define RCCK0_AFESSTTING 0xa04
+#define RCCK0_CCA 0xa08
+
+#define RCCK0_RXAGC1 0xa0c
+#define RCCK0_RXAGC2 0xa10
+
+#define RCCK0_RXHP 0xa14
+
+#define RCCK0_DSPPARAMETER1 0xa18
+#define RCCK0_DSPPARAMETER2 0xa1c
+
+#define RCCK0_TXFILTER1 0xa20
+#define RCCK0_TXFILTER2 0xa24
+#define RCCK0_DEBUGPORT 0xa28
+#define RCCK0_FALSEALARMREPORT 0xa2c
+#define RCCK0_TRSSIREPORT 0xa50
+#define RCCK0_RXREPORT 0xa54
+#define RCCK0_FACOUNTERLOWER 0xa5c
+#define RCCK0_FACOUNTERUPPER 0xa58
+
+/* 6. PageC(0xC00) */
+#define ROFDM0_LSTF 0xc00
+
+#define ROFDM0_TRXPATHENABLE 0xc04
+#define ROFDM0_TRMUXPAR 0xc08
+#define ROFDM0_TRSWISOLATION 0xc0c
+
+#define ROFDM0_XARXAFE 0xc10
+#define ROFDM0_XARXIQIMBALANCE 0xc14
+#define ROFDM0_XBRXAFE 0xc18
+#define ROFDM0_XBRXIQIMBALANCE 0xc1c
+#define ROFDM0_XCRXAFE 0xc20
+#define ROFDM0_XCRXIQIMBALANCE 0xc24
+#define ROFDM0_XDRXAFE 0xc28
+#define ROFDM0_XDRXIQIMBALANCE 0xc2c
+
+#define ROFDM0_RXDETECTOR1 0xc30
+#define ROFDM0_RXDETECTOR2 0xc34
+#define ROFDM0_RXDETECTOR3 0xc38
+#define ROFDM0_RXDETECTOR4 0xc3c
+
+#define ROFDM0_RXDSP 0xc40
+#define ROFDM0_CFOANDDAGC 0xc44
+#define ROFDM0_CCADROPTHRESHOLD 0xc48
+#define ROFDM0_ECCATHRESHOLD 0xc4c
+
+#define ROFDM0_XAAGCCORE1 0xc50
+#define ROFDM0_XAAGCCORE2 0xc54
+#define ROFDM0_XBAGCCORE1 0xc58
+#define ROFDM0_XBAGCCORE2 0xc5c
+#define ROFDM0_XCAGCCORE1 0xc60
+#define ROFDM0_XCAGCCORE2 0xc64
+#define ROFDM0_XDAGCCORE1 0xc68
+#define ROFDM0_XDAGCCORE2 0xc6c
+
+#define ROFDM0_AGCPARAMETER1 0xc70
+#define ROFDM0_AGCPARAMETER2 0xc74
+#define ROFDM0_AGCRSSITABLE 0xc78
+#define ROFDM0_HTSTFAGC 0xc7c
+
+#define ROFDM0_XATxIQIMBALANCE 0xc80
+#define ROFDM0_XATxAFE 0xc84
+#define ROFDM0_XBTxIQIMBALANCE 0xc88
+#define ROFDM0_XBTxAFE 0xc8c
+#define ROFDM0_XCTxIQIMBALANCE 0xc90
+#define ROFDM0_XCTxAFE 0xc94
+#define ROFDM0_XDTxIQIMBALANCE 0xc98
+#define ROFDM0_XDTxAFE 0xc9c
+
+#define ROFDM0_RXHPPARAMETER 0xce0
+#define ROFDM0_TXPSEUDONOISEWGT 0xce4
+#define ROFDM0_FRAMESYNC 0xcf0
+#define ROFDM0_DFSREPORT 0xcf4
+#define ROFDM0_TXCOEFF1 0xca4
+#define ROFDM0_TXCOEFF2 0xca8
+#define ROFDM0_TXCOEFF3 0xcac
+#define ROFDM0_TXCOEFF4 0xcb0
+#define ROFDM0_TXCOEFF5 0xcb4
+#define ROFDM0_TXCOEFF6 0xcb8
+
+/* 7. PageD(0xD00) */
+#define ROFDM1_LSTF 0xd00
+#define ROFDM1_TRXPATHENABLE 0xd04
+
+#define ROFDM1_CFO 0xd08
+#define ROFDM1_CSI1 0xd10
+#define ROFDM1_SBD 0xd14
+#define ROFDM1_CSI2 0xd18
+#define ROFDM1_CFOTRACKING 0xd2c
+#define ROFDM1_TRXMESAURE1 0xd34
+#define ROFDM1_INTFDET 0xd3c
+#define ROFDM1_PSEUDONOISESTATEAB 0xd50
+#define ROFDM1_PSEUDONOISESTATECD 0xd54
+#define ROFDM1_RXPSEUDONOISEWGT 0xd58
+
+#define ROFDM_PHYCOUNTER1 0xda0
+#define ROFDM_PHYCOUNTER2 0xda4
+#define ROFDM_PHYCOUNTER3 0xda8
+
+#define ROFDM_SHORTCFOAB 0xdac
+#define ROFDM_SHORTCFOCD 0xdb0
+#define ROFDM_LONGCFOAB 0xdb4
+#define ROFDM_LONGCFOCD 0xdb8
+#define ROFDM_TAILCFOAB 0xdbc
+#define ROFDM_TAILCFOCD 0xdc0
+#define ROFDM_PWMEASURE1 0xdc4
+#define ROFDM_PWMEASURE2 0xdc8
+#define ROFDM_BWREPORT 0xdcc
+#define ROFDM_AGCREPORT 0xdd0
+#define ROFDM_RXSNR 0xdd4
+#define ROFDM_RXEVMCSI 0xdd8
+#define ROFDM_SIGReport 0xddc
+
+/* 8. PageE(0xE00) */
+#define RTXAGC_A_RATE18_06 0xe00
+#define RTXAGC_A_RATE54_24 0xe04
+#define RTXAGC_A_CCK1_MCS32 0xe08
+#define RTXAGC_A_MCS03_MCS00 0xe10
+#define RTXAGC_A_MCS07_MCS04 0xe14
+#define RTXAGC_A_MCS11_MCS08 0xe18
+#define RTXAGC_A_MCS15_MCS12 0xe1c
+
+#define RTXAGC_B_RATE18_06 0x830
+#define RTXAGC_B_RATE54_24 0x834
+#define RTXAGC_B_CCK1_55_MCS32 0x838
+#define RTXAGC_B_MCS03_MCS00 0x83c
+#define RTXAGC_B_MCS07_MCS04 0x848
+#define RTXAGC_B_MCS11_MCS08 0x84c
+#define RTXAGC_B_MCS15_MCS12 0x868
+#define RTXAGC_B_CCK11_A_CCK2_11 0x86c
+
+/* RL6052 Register definition */
+#define RF_AC 0x00
+
+#define RF_IQADJ_G1 0x01
+#define RF_IQADJ_G2 0x02
+#define RF_POW_TRSW 0x05
+
+#define RF_GAIN_RX 0x06
+#define RF_GAIN_TX 0x07
+
+#define RF_TXM_IDAC 0x08
+#define RF_BS_IQGEN 0x0F
+
+#define RF_MODE1 0x10
+#define RF_MODE2 0x11
+
+#define RF_RX_AGC_HP 0x12
+#define RF_TX_AGC 0x13
+#define RF_BIAS 0x14
+#define RF_IPA 0x15
+#define RF_POW_ABILITY 0x17
+#define RF_MODE_AG 0x18
+#define rRfChannel 0x18
+#define RF_CHNLBW 0x18
+#define RF_TOP 0x19
+
+#define RF_RX_G1 0x1A
+#define RF_RX_G2 0x1B
+
+#define RF_RX_BB2 0x1C
+#define RF_RX_BB1 0x1D
+
+#define RF_RCK1 0x1E
+#define RF_RCK2 0x1F
+
+#define RF_TX_G1 0x20
+#define RF_TX_G2 0x21
+#define RF_TX_G3 0x22
+
+#define RF_TX_BB1 0x23
+
+#define RF_T_METER 0x42
+
+#define RF_SYN_G1 0x25
+#define RF_SYN_G2 0x26
+#define RF_SYN_G3 0x27
+#define RF_SYN_G4 0x28
+#define RF_SYN_G5 0x29
+#define RF_SYN_G6 0x2A
+#define RF_SYN_G7 0x2B
+#define RF_SYN_G8 0x2C
+
+#define RF_RCK_OS 0x30
+
+#define RF_TXPA_G1 0x31
+#define RF_TXPA_G2 0x32
+#define RF_TXPA_G3 0x33
+
+/* Bit Mask */
+
+/* 2. Page8(0x800) */
+#define BRFMOD 0x1
+#define BCCKTXSC 0x30
+#define BCCKEN 0x1000000
+#define BOFDMEN 0x2000000
+
+#define B3WIREDATALENGTH 0x800
+#define B3WIREADDRESSLENGTH 0x400
+
+#define BRFSI_RFENV 0x10
+
+#define BLSSIREADADDRESS 0x7f800000
+#define BLSSIREADEDGE 0x80000000
+#define BLSSIREADBACKDATA 0xfffff
+/* 4. PageA(0xA00) */
+#define BCCKSIDEBAND 0x10
+
+/* Other Definition */
+#define BBYTE0 0x1
+#define BBYTE1 0x2
+#define BBYTE2 0x4
+#define BBYTE3 0x8
+#define BWORD0 0x3
+#define BWORD1 0xc
+#define BDWORD 0xf
+
+#define BMASKBYTE0 0xff
+#define BMASKBYTE1 0xff00
+#define BMASKBYTE2 0xff0000
+#define BMASKBYTE3 0xff000000
+#define BMASKHWORD 0xffff0000
+#define BMASKLWORD 0x0000ffff
+#define BMASKDWORD 0xffffffff
+#define BMASK12BITS 0xfff
+#define BMASKH4BITS 0xf0000000
+#define BMASKOFDM_D 0xffc00000
+#define BMASKCCK 0x3f3f3f3f
+
+#define BRFREGOFFSETMASK 0xfffff
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/rf.c b/drivers/net/wireless/rtlwifi/rtl8192de/rf.c
new file mode 100644
index 000000000000..db27cebaac2c
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/rf.c
@@ -0,0 +1,628 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "rf.h"
+#include "dm.h"
+#include "hw.h"
+
+void rtl92d_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u8 rfpath;
+
+ switch (bandwidth) {
+ case HT_CHANNEL_WIDTH_20:
+ for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) {
+ rtlphy->rfreg_chnlval[rfpath] = ((rtlphy->rfreg_chnlval
+ [rfpath] & 0xfffff3ff) | 0x0400);
+ rtl_set_rfreg(hw, rfpath, RF_CHNLBW, BIT(10) |
+ BIT(11), 0x01);
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD,
+ ("20M RF 0x18 = 0x%x\n",
+ rtlphy->rfreg_chnlval[rfpath]));
+ }
+
+ break;
+ case HT_CHANNEL_WIDTH_20_40:
+ for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) {
+ rtlphy->rfreg_chnlval[rfpath] =
+ ((rtlphy->rfreg_chnlval[rfpath] & 0xfffff3ff));
+ rtl_set_rfreg(hw, rfpath, RF_CHNLBW, BIT(10) | BIT(11),
+ 0x00);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD,
+ ("40M RF 0x18 = 0x%x\n",
+ rtlphy->rfreg_chnlval[rfpath]));
+ }
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("unknown bandwidth: %#X\n", bandwidth));
+ break;
+ }
+}
+
+void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
+ u8 *ppowerlevel)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u32 tx_agc[2] = {0, 0}, tmpval;
+ bool turbo_scanoff = false;
+ u8 idx1, idx2;
+ u8 *ptr;
+
+ if (rtlefuse->eeprom_regulatory != 0)
+ turbo_scanoff = true;
+ if (mac->act_scanning) {
+ tx_agc[RF90_PATH_A] = 0x3f3f3f3f;
+ tx_agc[RF90_PATH_B] = 0x3f3f3f3f;
+ if (turbo_scanoff) {
+ for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
+ tx_agc[idx1] = ppowerlevel[idx1] |
+ (ppowerlevel[idx1] << 8) |
+ (ppowerlevel[idx1] << 16) |
+ (ppowerlevel[idx1] << 24);
+ }
+ }
+ } else {
+ for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
+ tx_agc[idx1] = ppowerlevel[idx1] |
+ (ppowerlevel[idx1] << 8) |
+ (ppowerlevel[idx1] << 16) |
+ (ppowerlevel[idx1] << 24);
+ }
+ if (rtlefuse->eeprom_regulatory == 0) {
+ tmpval = (rtlphy->mcs_txpwrlevel_origoffset[0][6]) +
+ (rtlphy->mcs_txpwrlevel_origoffset[0][7] << 8);
+ tx_agc[RF90_PATH_A] += tmpval;
+ tmpval = (rtlphy->mcs_txpwrlevel_origoffset[0][14]) +
+ (rtlphy->mcs_txpwrlevel_origoffset[0][15] << 24);
+ tx_agc[RF90_PATH_B] += tmpval;
+ }
+ }
+
+ for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
+ ptr = (u8 *) (&(tx_agc[idx1]));
+ for (idx2 = 0; idx2 < 4; idx2++) {
+ if (*ptr > RF6052_MAX_TX_PWR)
+ *ptr = RF6052_MAX_TX_PWR;
+ ptr++;
+ }
+ }
+
+ tmpval = tx_agc[RF90_PATH_A] & 0xff;
+ rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, BMASKBYTE1, tmpval);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("CCK PWR 1M (rf-A) = 0x%x (reg 0x%x)\n", tmpval,
+ RTXAGC_A_CCK1_MCS32));
+ tmpval = tx_agc[RF90_PATH_A] >> 8;
+ rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("CCK PWR 2~11M (rf-A) = 0x%x (reg 0x%x)\n", tmpval,
+ RTXAGC_B_CCK11_A_CCK2_11));
+ tmpval = tx_agc[RF90_PATH_B] >> 24;
+ rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, BMASKBYTE0, tmpval);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n", tmpval,
+ RTXAGC_B_CCK11_A_CCK2_11));
+ tmpval = tx_agc[RF90_PATH_B] & 0x00ffffff;
+ rtl_set_bbreg(hw, RTXAGC_B_CCK1_55_MCS32, 0xffffff00, tmpval);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("CCK PWR 1~5.5M (rf-B) = 0x%x (reg 0x%x)\n", tmpval,
+ RTXAGC_B_CCK1_55_MCS32));
+}
+
+static void _rtl92d_phy_get_power_base(struct ieee80211_hw *hw,
+ u8 *ppowerlevel, u8 channel,
+ u32 *ofdmbase, u32 *mcsbase)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u32 powerbase0, powerbase1;
+ u8 legacy_pwrdiff, ht20_pwrdiff;
+ u8 i, powerlevel[2];
+
+ for (i = 0; i < 2; i++) {
+ powerlevel[i] = ppowerlevel[i];
+ legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff[i][channel - 1];
+ powerbase0 = powerlevel[i] + legacy_pwrdiff;
+ powerbase0 = (powerbase0 << 24) | (powerbase0 << 16) |
+ (powerbase0 << 8) | powerbase0;
+ *(ofdmbase + i) = powerbase0;
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ (" [OFDM power base index rf(%c) = 0x%x]\n",
+ ((i == 0) ? 'A' : 'B'), *(ofdmbase + i)));
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) {
+ ht20_pwrdiff = rtlefuse->txpwr_ht20diff[i][channel - 1];
+ powerlevel[i] += ht20_pwrdiff;
+ }
+ powerbase1 = powerlevel[i];
+ powerbase1 = (powerbase1 << 24) | (powerbase1 << 16) |
+ (powerbase1 << 8) | powerbase1;
+ *(mcsbase + i) = powerbase1;
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ (" [MCS power base index rf(%c) = 0x%x]\n",
+ ((i == 0) ? 'A' : 'B'), *(mcsbase + i)));
+ }
+}
+
+static u8 _rtl92d_phy_get_chnlgroup_bypg(u8 chnlindex)
+{
+ u8 group;
+ u8 channel_info[59] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,
+ 60, 62, 64, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128, 130, 132,
+ 134, 136, 138, 140, 149, 151, 153, 155, 157, 159,
+ 161, 163, 165
+ };
+
+ if (channel_info[chnlindex] <= 3) /* Chanel 1-3 */
+ group = 0;
+ else if (channel_info[chnlindex] <= 9) /* Channel 4-9 */
+ group = 1;
+ else if (channel_info[chnlindex] <= 14) /* Channel 10-14 */
+ group = 2;
+ else if (channel_info[chnlindex] <= 64)
+ group = 6;
+ else if (channel_info[chnlindex] <= 140)
+ group = 7;
+ else
+ group = 8;
+ return group;
+}
+
+static void _rtl92d_get_txpower_writeval_by_regulatory(struct ieee80211_hw *hw,
+ u8 channel, u8 index,
+ u32 *powerbase0,
+ u32 *powerbase1,
+ u32 *p_outwriteval)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u8 i, chnlgroup = 0, pwr_diff_limit[4];
+ u32 writeval = 0, customer_limit, rf;
+
+ for (rf = 0; rf < 2; rf++) {
+ switch (rtlefuse->eeprom_regulatory) {
+ case 0:
+ chnlgroup = 0;
+ writeval = rtlphy->mcs_txpwrlevel_origoffset
+ [chnlgroup][index +
+ (rf ? 8 : 0)] + ((index < 2) ?
+ powerbase0[rf] :
+ powerbase1[rf]);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR, ("RTK better "
+ "performance, writeval(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), writeval));
+ break;
+ case 1:
+ if (rtlphy->pwrgroup_cnt == 1)
+ chnlgroup = 0;
+ if (rtlphy->pwrgroup_cnt >= MAX_PG_GROUP) {
+ chnlgroup = _rtl92d_phy_get_chnlgroup_bypg(
+ channel - 1);
+ if (rtlphy->current_chan_bw ==
+ HT_CHANNEL_WIDTH_20)
+ chnlgroup++;
+ else
+ chnlgroup += 4;
+ writeval = rtlphy->mcs_txpwrlevel_origoffset
+ [chnlgroup][index +
+ (rf ? 8 : 0)] + ((index < 2) ?
+ powerbase0[rf] :
+ powerbase1[rf]);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("Realtek regulatory, "
+ "20MHz, writeval(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'),
+ writeval));
+ }
+ break;
+ case 2:
+ writeval = ((index < 2) ? powerbase0[rf] :
+ powerbase1[rf]);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR, ("Better regulatory, "
+ "writeval(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), writeval));
+ break;
+ case 3:
+ chnlgroup = 0;
+ if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("customer's limit, 40MHz rf(%c) = "
+ "0x%x\n", ((rf == 0) ? 'A' : 'B'),
+ rtlefuse->pwrgroup_ht40[rf]
+ [channel - 1]));
+ } else {
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("customer's limit, 20MHz rf(%c) = "
+ "0x%x\n", ((rf == 0) ? 'A' : 'B'),
+ rtlefuse->pwrgroup_ht20[rf]
+ [channel - 1]));
+ }
+ for (i = 0; i < 4; i++) {
+ pwr_diff_limit[i] =
+ (u8)((rtlphy->mcs_txpwrlevel_origoffset
+ [chnlgroup][index + (rf ? 8 : 0)] &
+ (0x7f << (i * 8))) >> (i * 8));
+ if (rtlphy->current_chan_bw ==
+ HT_CHANNEL_WIDTH_20_40) {
+ if (pwr_diff_limit[i] >
+ rtlefuse->pwrgroup_ht40[rf]
+ [channel - 1])
+ pwr_diff_limit[i] =
+ rtlefuse->pwrgroup_ht40
+ [rf][channel - 1];
+ } else {
+ if (pwr_diff_limit[i] >
+ rtlefuse->pwrgroup_ht20[rf][
+ channel - 1])
+ pwr_diff_limit[i] =
+ rtlefuse->pwrgroup_ht20[rf]
+ [channel - 1];
+ }
+ }
+ customer_limit = (pwr_diff_limit[3] << 24) |
+ (pwr_diff_limit[2] << 16) |
+ (pwr_diff_limit[1] << 8) |
+ (pwr_diff_limit[0]);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("Customer's limit rf(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), customer_limit));
+ writeval = customer_limit + ((index < 2) ?
+ powerbase0[rf] : powerbase1[rf]);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("Customer, writeval rf(%c)= 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), writeval));
+ break;
+ default:
+ chnlgroup = 0;
+ writeval = rtlphy->mcs_txpwrlevel_origoffset
+ [chnlgroup][index +
+ (rf ? 8 : 0)] + ((index < 2) ?
+ powerbase0[rf] : powerbase1[rf]);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("RTK better performance, writeval "
+ "rf(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), writeval));
+ break;
+ }
+ *(p_outwriteval + rf) = writeval;
+ }
+}
+
+static void _rtl92d_write_ofdm_power_reg(struct ieee80211_hw *hw,
+ u8 index, u32 *pvalue)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ static u16 regoffset_a[6] = {
+ RTXAGC_A_RATE18_06, RTXAGC_A_RATE54_24,
+ RTXAGC_A_MCS03_MCS00, RTXAGC_A_MCS07_MCS04,
+ RTXAGC_A_MCS11_MCS08, RTXAGC_A_MCS15_MCS12
+ };
+ static u16 regoffset_b[6] = {
+ RTXAGC_B_RATE18_06, RTXAGC_B_RATE54_24,
+ RTXAGC_B_MCS03_MCS00, RTXAGC_B_MCS07_MCS04,
+ RTXAGC_B_MCS11_MCS08, RTXAGC_B_MCS15_MCS12
+ };
+ u8 i, rf, pwr_val[4];
+ u32 writeval;
+ u16 regoffset;
+
+ for (rf = 0; rf < 2; rf++) {
+ writeval = pvalue[rf];
+ for (i = 0; i < 4; i++) {
+ pwr_val[i] = (u8) ((writeval & (0x7f <<
+ (i * 8))) >> (i * 8));
+ if (pwr_val[i] > RF6052_MAX_TX_PWR)
+ pwr_val[i] = RF6052_MAX_TX_PWR;
+ }
+ writeval = (pwr_val[3] << 24) | (pwr_val[2] << 16) |
+ (pwr_val[1] << 8) | pwr_val[0];
+ if (rf == 0)
+ regoffset = regoffset_a[index];
+ else
+ regoffset = regoffset_b[index];
+ rtl_set_bbreg(hw, regoffset, BMASKDWORD, writeval);
+ RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+ ("Set 0x%x = %08x\n", regoffset, writeval));
+ if (((get_rf_type(rtlphy) == RF_2T2R) &&
+ (regoffset == RTXAGC_A_MCS15_MCS12 ||
+ regoffset == RTXAGC_B_MCS15_MCS12)) ||
+ ((get_rf_type(rtlphy) != RF_2T2R) &&
+ (regoffset == RTXAGC_A_MCS07_MCS04 ||
+ regoffset == RTXAGC_B_MCS07_MCS04))) {
+ writeval = pwr_val[3];
+ if (regoffset == RTXAGC_A_MCS15_MCS12 ||
+ regoffset == RTXAGC_A_MCS07_MCS04)
+ regoffset = 0xc90;
+ if (regoffset == RTXAGC_B_MCS15_MCS12 ||
+ regoffset == RTXAGC_B_MCS07_MCS04)
+ regoffset = 0xc98;
+ for (i = 0; i < 3; i++) {
+ if (i != 2)
+ writeval = (writeval > 8) ?
+ (writeval - 8) : 0;
+ else
+ writeval = (writeval > 6) ?
+ (writeval - 6) : 0;
+ rtl_write_byte(rtlpriv, (u32) (regoffset + i),
+ (u8) writeval);
+ }
+ }
+ }
+}
+
+void rtl92d_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
+ u8 *ppowerlevel, u8 channel)
+{
+ u32 writeval[2], powerbase0[2], powerbase1[2];
+ u8 index;
+
+ _rtl92d_phy_get_power_base(hw, ppowerlevel, channel,
+ &powerbase0[0], &powerbase1[0]);
+ for (index = 0; index < 6; index++) {
+ _rtl92d_get_txpower_writeval_by_regulatory(hw,
+ channel, index, &powerbase0[0],
+ &powerbase1[0], &writeval[0]);
+ _rtl92d_write_ofdm_power_reg(hw, index, &writeval[0]);
+ }
+}
+
+bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u8 u1btmp;
+ u8 direct = bmac0 ? BIT(3) | BIT(2) : BIT(3);
+ u8 mac_reg = bmac0 ? REG_MAC1 : REG_MAC0;
+ u8 mac_on_bit = bmac0 ? MAC1_ON : MAC0_ON;
+ bool bresult = true; /* true: need to enable BB/RF power */
+
+ rtlhal->during_mac0init_radiob = false;
+ rtlhal->during_mac1init_radioa = false;
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, ("===>\n"));
+ /* MAC0 Need PHY1 load radio_b.txt . Driver use DBI to write. */
+ u1btmp = rtl_read_byte(rtlpriv, mac_reg);
+ if (!(u1btmp & mac_on_bit)) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("enable BB & RF\n"));
+ /* Enable BB and RF power */
+ rtl92de_write_dword_dbi(hw, REG_SYS_ISO_CTRL,
+ rtl92de_read_dword_dbi(hw, REG_SYS_ISO_CTRL, direct) |
+ BIT(29) | BIT(16) | BIT(17), direct);
+ } else {
+ /* We think if MAC1 is ON,then radio_a.txt
+ * and radio_b.txt has been load. */
+ bresult = false;
+ }
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, ("<===\n"));
+ return bresult;
+
+}
+
+void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw, bool bmac0)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u8 u1btmp;
+ u8 direct = bmac0 ? BIT(3) | BIT(2) : BIT(3);
+ u8 mac_reg = bmac0 ? REG_MAC1 : REG_MAC0;
+ u8 mac_on_bit = bmac0 ? MAC1_ON : MAC0_ON;
+
+ rtlhal->during_mac0init_radiob = false;
+ rtlhal->during_mac1init_radioa = false;
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, ("====>\n"));
+ /* check MAC0 enable or not again now, if
+ * enabled, not power down radio A. */
+ u1btmp = rtl_read_byte(rtlpriv, mac_reg);
+ if (!(u1btmp & mac_on_bit)) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("power down\n"));
+ /* power down RF radio A according to YuNan's advice. */
+ rtl92de_write_dword_dbi(hw, RFPGA0_XA_LSSIPARAMETER,
+ 0x00000000, direct);
+ }
+ RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, ("<====\n"));
+}
+
+bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ bool rtstatus = true;
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u32 u4_regvalue = 0;
+ u8 rfpath;
+ struct bb_reg_def *pphyreg;
+ bool mac1_initradioa_first = false, mac0_initradiob_first = false;
+ bool need_pwrdown_radioa = false, need_pwrdown_radiob = false;
+ bool true_bpath = false;
+
+ if (rtlphy->rf_type == RF_1T1R)
+ rtlphy->num_total_rfpath = 1;
+ else
+ rtlphy->num_total_rfpath = 2;
+
+ /* Single phy mode: use radio_a radio_b config path_A path_B */
+ /* seperately by MAC0, and MAC1 needn't configure RF; */
+ /* Dual PHY mode:MAC0 use radio_a config 1st phy path_A, */
+ /* MAC1 use radio_b config 2nd PHY path_A. */
+ /* DMDP,MAC0 on G band,MAC1 on A band. */
+ if (rtlhal->macphymode == DUALMAC_DUALPHY) {
+ if (rtlhal->current_bandtype == BAND_ON_2_4G &&
+ rtlhal->interfaceindex == 0) {
+ /* MAC0 needs PHY1 load radio_b.txt.
+ * Driver use DBI to write. */
+ if (rtl92d_phy_enable_anotherphy(hw, true)) {
+ rtlphy->num_total_rfpath = 2;
+ mac0_initradiob_first = true;
+ } else {
+ /* We think if MAC1 is ON,then radio_a.txt and
+ * radio_b.txt has been load. */
+ return rtstatus;
+ }
+ } else if (rtlhal->current_bandtype == BAND_ON_5G &&
+ rtlhal->interfaceindex == 1) {
+ /* MAC1 needs PHY0 load radio_a.txt.
+ * Driver use DBI to write. */
+ if (rtl92d_phy_enable_anotherphy(hw, false)) {
+ rtlphy->num_total_rfpath = 2;
+ mac1_initradioa_first = true;
+ } else {
+ /* We think if MAC0 is ON,then radio_a.txt and
+ * radio_b.txt has been load. */
+ return rtstatus;
+ }
+ } else if (rtlhal->interfaceindex == 1) {
+ /* MAC0 enabled, only init radia B. */
+ true_bpath = true;
+ }
+ }
+
+ for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) {
+ /* Mac1 use PHY0 write */
+ if (mac1_initradioa_first) {
+ if (rfpath == RF90_PATH_A) {
+ rtlhal->during_mac1init_radioa = true;
+ need_pwrdown_radioa = true;
+ } else if (rfpath == RF90_PATH_B) {
+ rtlhal->during_mac1init_radioa = false;
+ mac1_initradioa_first = false;
+ rfpath = RF90_PATH_A;
+ true_bpath = true;
+ rtlphy->num_total_rfpath = 1;
+ }
+ } else if (mac0_initradiob_first) {
+ /* Mac0 use PHY1 write */
+ if (rfpath == RF90_PATH_A)
+ rtlhal->during_mac0init_radiob = false;
+ if (rfpath == RF90_PATH_B) {
+ rtlhal->during_mac0init_radiob = true;
+ mac0_initradiob_first = false;
+ need_pwrdown_radiob = true;
+ rfpath = RF90_PATH_A;
+ true_bpath = true;
+ rtlphy->num_total_rfpath = 1;
+ }
+ }
+ pphyreg = &rtlphy->phyreg_def[rfpath];
+ switch (rfpath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs,
+ BRFSI_RFENV);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs,
+ BRFSI_RFENV << 16);
+ break;
+ }
+ rtl_set_bbreg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1);
+ udelay(1);
+ rtl_set_bbreg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1);
+ udelay(1);
+ /* Set bit number of Address and Data for RF register */
+ /* Set 1 to 4 bits for 8255 */
+ rtl_set_bbreg(hw, pphyreg->rfhssi_para2,
+ B3WIREADDRESSLENGTH, 0x0);
+ udelay(1);
+ /* Set 0 to 12 bits for 8255 */
+ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREDATALENGTH, 0x0);
+ udelay(1);
+ switch (rfpath) {
+ case RF90_PATH_A:
+ if (true_bpath)
+ rtstatus = rtl92d_phy_config_rf_with_headerfile(
+ hw, radiob_txt,
+ (enum radio_path)rfpath);
+ else
+ rtstatus = rtl92d_phy_config_rf_with_headerfile(
+ hw, radioa_txt,
+ (enum radio_path)rfpath);
+ break;
+ case RF90_PATH_B:
+ rtstatus =
+ rtl92d_phy_config_rf_with_headerfile(hw, radiob_txt,
+ (enum radio_path) rfpath);
+ break;
+ case RF90_PATH_C:
+ break;
+ case RF90_PATH_D:
+ break;
+ }
+ switch (rfpath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV,
+ u4_regvalue);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16,
+ u4_regvalue);
+ break;
+ }
+ if (rtstatus != true) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ ("Radio[%d] Fail!!", rfpath));
+ goto phy_rf_cfg_fail;
+ }
+
+ }
+
+ /* check MAC0 enable or not again, if enabled,
+ * not power down radio A. */
+ /* check MAC1 enable or not again, if enabled,
+ * not power down radio B. */
+ if (need_pwrdown_radioa)
+ rtl92d_phy_powerdown_anotherphy(hw, false);
+ else if (need_pwrdown_radiob)
+ rtl92d_phy_powerdown_anotherphy(hw, true);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, ("<---\n"));
+ return rtstatus;
+
+phy_rf_cfg_fail:
+ return rtstatus;
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/rf.h b/drivers/net/wireless/rtlwifi/rtl8192de/rf.h
new file mode 100644
index 000000000000..74b9cfc39a83
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/rf.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92D_RF_H__
+#define __RTL92D_RF_H__
+
+extern void rtl92d_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
+ u8 bandwidth);
+extern void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
+ u8 *ppowerlevel);
+extern void rtl92d_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
+ u8 *ppowerlevel, u8 channel);
+extern bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw);
+extern bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0);
+extern void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw,
+ bool bmac0);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
new file mode 100644
index 000000000000..149493f4c25c
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
@@ -0,0 +1,437 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+
+#include "../wifi.h"
+#include "../core.h"
+#include "../pci.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "dm.h"
+#include "hw.h"
+#include "sw.h"
+#include "trx.h"
+#include "led.h"
+
+static void rtl92d_init_aspm_vars(struct ieee80211_hw *hw)
+{
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ /*close ASPM for AMD defaultly */
+ rtlpci->const_amdpci_aspm = 0;
+
+ /*
+ * ASPM PS mode.
+ * 0 - Disable ASPM,
+ * 1 - Enable ASPM without Clock Req,
+ * 2 - Enable ASPM with Clock Req,
+ * 3 - Alwyas Enable ASPM with Clock Req,
+ * 4 - Always Enable ASPM without Clock Req.
+ * set defult to RTL8192CE:3 RTL8192E:2
+ * */
+ rtlpci->const_pci_aspm = 3;
+
+ /*Setting for PCI-E device */
+ rtlpci->const_devicepci_aspm_setting = 0x03;
+
+ /*Setting for PCI-E bridge */
+ rtlpci->const_hostpci_aspm_setting = 0x02;
+
+ /*
+ * In Hw/Sw Radio Off situation.
+ * 0 - Default,
+ * 1 - From ASPM setting without low Mac Pwr,
+ * 2 - From ASPM setting with low Mac Pwr,
+ * 3 - Bus D3
+ * set default to RTL8192CE:0 RTL8192SE:2
+ */
+ rtlpci->const_hwsw_rfoff_d3 = 0;
+
+ /*
+ * This setting works for those device with
+ * backdoor ASPM setting such as EPHY setting.
+ * 0 - Not support ASPM,
+ * 1 - Support ASPM,
+ * 2 - According to chipset.
+ */
+ rtlpci->const_support_pciaspm = 1;
+}
+
+static int rtl92d_init_sw_vars(struct ieee80211_hw *hw)
+{
+ int err;
+ u8 tid;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ const struct firmware *firmware;
+ static int header_print;
+
+ rtlpriv->dm.dm_initialgain_enable = true;
+ rtlpriv->dm.dm_flag = 0;
+ rtlpriv->dm.disable_framebursting = 0;
+ rtlpriv->dm.thermalvalue = 0;
+ rtlpriv->dm.useramask = 1;
+
+ /* dual mac */
+ if (rtlpriv->rtlhal.current_bandtype == BAND_ON_5G)
+ rtlpriv->phy.current_channel = 36;
+ else
+ rtlpriv->phy.current_channel = 1;
+
+ if (rtlpriv->rtlhal.macphymode != SINGLEMAC_SINGLEPHY) {
+ rtlpriv->rtlhal.disable_amsdu_8k = true;
+ /* No long RX - reduce fragmentation */
+ rtlpci->rxbuffersize = 4096;
+ }
+
+ rtlpci->transmit_config = CFENDFORM | BIT(12) | BIT(13);
+
+ rtlpci->receive_config = (
+ RCR_APPFCS
+ | RCR_AMF
+ | RCR_ADF
+ | RCR_APP_MIC
+ | RCR_APP_ICV
+ | RCR_AICV
+ | RCR_ACRC32
+ | RCR_AB
+ | RCR_AM
+ | RCR_APM
+ | RCR_APP_PHYST_RXFF
+ | RCR_HTC_LOC_CTRL
+ );
+
+ rtlpci->irq_mask[0] = (u32) (
+ IMR_ROK
+ | IMR_VODOK
+ | IMR_VIDOK
+ | IMR_BEDOK
+ | IMR_BKDOK
+ | IMR_MGNTDOK
+ | IMR_HIGHDOK
+ | IMR_BDOK
+ | IMR_RDU
+ | IMR_RXFOVW
+ );
+
+ rtlpci->irq_mask[1] = (u32) (IMR_CPWM | IMR_C2HCMD);
+
+ /* for debug level */
+ rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug;
+ /* for LPS & IPS */
+ rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
+ rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
+ rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ if (!rtlpriv->psc.inactiveps)
+ pr_info("rtl8192ce: Power Save off (module option)\n");
+ if (!rtlpriv->psc.fwctrl_lps)
+ pr_info("rtl8192ce: FW Power Save off (module option)\n");
+ rtlpriv->psc.reg_fwctrl_lps = 3;
+ rtlpriv->psc.reg_max_lps_awakeintvl = 5;
+ /* for ASPM, you can close aspm through
+ * set const_support_pciaspm = 0 */
+ rtl92d_init_aspm_vars(hw);
+
+ if (rtlpriv->psc.reg_fwctrl_lps == 1)
+ rtlpriv->psc.fwctrl_psmode = FW_PS_MIN_MODE;
+ else if (rtlpriv->psc.reg_fwctrl_lps == 2)
+ rtlpriv->psc.fwctrl_psmode = FW_PS_MAX_MODE;
+ else if (rtlpriv->psc.reg_fwctrl_lps == 3)
+ rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE;
+
+ /* for firmware buf */
+ rtlpriv->rtlhal.pfirmware = vzalloc(0x8000);
+ if (!rtlpriv->rtlhal.pfirmware) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Can't alloc buffer for fw.\n"));
+ return 1;
+ }
+
+ if (!header_print) {
+ pr_info("Driver for Realtek RTL8192DE WLAN interface\n");
+ pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name);
+ header_print++;
+ }
+ /* request fw */
+ err = request_firmware(&firmware, rtlpriv->cfg->fw_name,
+ rtlpriv->io.dev);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Failed to request firmware!\n"));
+ return 1;
+ }
+ if (firmware->size > 0x8000) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Firmware is too big!\n"));
+ release_firmware(firmware);
+ return 1;
+ }
+ memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size);
+ rtlpriv->rtlhal.fwsize = firmware->size;
+ release_firmware(firmware);
+
+ /* for early mode */
+ rtlpriv->rtlhal.earlymode_enable = true;
+ for (tid = 0; tid < 8; tid++)
+ skb_queue_head_init(&rtlpriv->mac80211.skb_waitq[tid]);
+ return 0;
+}
+
+static void rtl92d_deinit_sw_vars(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 tid;
+
+ if (rtlpriv->rtlhal.pfirmware) {
+ vfree(rtlpriv->rtlhal.pfirmware);
+ rtlpriv->rtlhal.pfirmware = NULL;
+ }
+ for (tid = 0; tid < 8; tid++)
+ skb_queue_purge(&rtlpriv->mac80211.skb_waitq[tid]);
+}
+
+static struct rtl_hal_ops rtl8192de_hal_ops = {
+ .init_sw_vars = rtl92d_init_sw_vars,
+ .deinit_sw_vars = rtl92d_deinit_sw_vars,
+ .read_eeprom_info = rtl92de_read_eeprom_info,
+ .interrupt_recognized = rtl92de_interrupt_recognized,
+ .hw_init = rtl92de_hw_init,
+ .hw_disable = rtl92de_card_disable,
+ .hw_suspend = rtl92de_suspend,
+ .hw_resume = rtl92de_resume,
+ .enable_interrupt = rtl92de_enable_interrupt,
+ .disable_interrupt = rtl92de_disable_interrupt,
+ .set_network_type = rtl92de_set_network_type,
+ .set_chk_bssid = rtl92de_set_check_bssid,
+ .set_qos = rtl92de_set_qos,
+ .set_bcn_reg = rtl92de_set_beacon_related_registers,
+ .set_bcn_intv = rtl92de_set_beacon_interval,
+ .update_interrupt_mask = rtl92de_update_interrupt_mask,
+ .get_hw_reg = rtl92de_get_hw_reg,
+ .set_hw_reg = rtl92de_set_hw_reg,
+ .update_rate_tbl = rtl92de_update_hal_rate_tbl,
+ .fill_tx_desc = rtl92de_tx_fill_desc,
+ .fill_tx_cmddesc = rtl92de_tx_fill_cmddesc,
+ .query_rx_desc = rtl92de_rx_query_desc,
+ .set_channel_access = rtl92de_update_channel_access_setting,
+ .radio_onoff_checking = rtl92de_gpio_radio_on_off_checking,
+ .set_bw_mode = rtl92d_phy_set_bw_mode,
+ .switch_channel = rtl92d_phy_sw_chnl,
+ .dm_watchdog = rtl92d_dm_watchdog,
+ .scan_operation_backup = rtl92d_phy_scan_operation_backup,
+ .set_rf_power_state = rtl92d_phy_set_rf_power_state,
+ .led_control = rtl92de_led_control,
+ .set_desc = rtl92de_set_desc,
+ .get_desc = rtl92de_get_desc,
+ .tx_polling = rtl92de_tx_polling,
+ .enable_hw_sec = rtl92de_enable_hw_security_config,
+ .set_key = rtl92de_set_key,
+ .init_sw_leds = rtl92de_init_sw_leds,
+ .get_bbreg = rtl92d_phy_query_bb_reg,
+ .set_bbreg = rtl92d_phy_set_bb_reg,
+ .get_rfreg = rtl92d_phy_query_rf_reg,
+ .set_rfreg = rtl92d_phy_set_rf_reg,
+ .linked_set_reg = rtl92d_linked_set_reg,
+};
+
+static struct rtl_mod_params rtl92de_mod_params = {
+ .sw_crypto = false,
+ .inactiveps = true,
+ .swctrl_lps = true,
+ .fwctrl_lps = false,
+ .debug = DBG_EMERG,
+};
+
+static struct rtl_hal_cfg rtl92de_hal_cfg = {
+ .bar_id = 2,
+ .write_readback = true,
+ .name = "rtl8192de",
+ .fw_name = "rtlwifi/rtl8192defw.bin",
+ .ops = &rtl8192de_hal_ops,
+ .mod_params = &rtl92de_mod_params,
+
+ .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL,
+ .maps[SYS_FUNC_EN] = REG_SYS_FUNC_EN,
+ .maps[SYS_CLK] = REG_SYS_CLKR,
+ .maps[MAC_RCR_AM] = RCR_AM,
+ .maps[MAC_RCR_AB] = RCR_AB,
+ .maps[MAC_RCR_ACRC32] = RCR_ACRC32,
+ .maps[MAC_RCR_ACF] = RCR_ACF,
+ .maps[MAC_RCR_AAP] = RCR_AAP,
+
+ .maps[EFUSE_TEST] = REG_EFUSE_TEST,
+ .maps[EFUSE_CTRL] = REG_EFUSE_CTRL,
+ .maps[EFUSE_CLK] = 0, /* just for 92se */
+ .maps[EFUSE_CLK_CTRL] = REG_EFUSE_CTRL,
+ .maps[EFUSE_PWC_EV12V] = PWC_EV12V,
+ .maps[EFUSE_FEN_ELDR] = FEN_ELDR,
+ .maps[EFUSE_LOADER_CLK_EN] = LOADER_CLK_EN,
+ .maps[EFUSE_ANA8M] = 0, /* just for 92se */
+ .maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE,
+ .maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION,
+ .maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN,
+
+ .maps[RWCAM] = REG_CAMCMD,
+ .maps[WCAMI] = REG_CAMWRITE,
+ .maps[RCAMO] = REG_CAMREAD,
+ .maps[CAMDBG] = REG_CAMDBG,
+ .maps[SECR] = REG_SECCFG,
+ .maps[SEC_CAM_NONE] = CAM_NONE,
+ .maps[SEC_CAM_WEP40] = CAM_WEP40,
+ .maps[SEC_CAM_TKIP] = CAM_TKIP,
+ .maps[SEC_CAM_AES] = CAM_AES,
+ .maps[SEC_CAM_WEP104] = CAM_WEP104,
+
+ .maps[RTL_IMR_BCNDMAINT6] = IMR_BCNDMAINT6,
+ .maps[RTL_IMR_BCNDMAINT5] = IMR_BCNDMAINT5,
+ .maps[RTL_IMR_BCNDMAINT4] = IMR_BCNDMAINT4,
+ .maps[RTL_IMR_BCNDMAINT3] = IMR_BCNDMAINT3,
+ .maps[RTL_IMR_BCNDMAINT2] = IMR_BCNDMAINT2,
+ .maps[RTL_IMR_BCNDMAINT1] = IMR_BCNDMAINT1,
+ .maps[RTL_IMR_BCNDOK8] = IMR_BCNDOK8,
+ .maps[RTL_IMR_BCNDOK7] = IMR_BCNDOK7,
+ .maps[RTL_IMR_BCNDOK6] = IMR_BCNDOK6,
+ .maps[RTL_IMR_BCNDOK5] = IMR_BCNDOK5,
+ .maps[RTL_IMR_BCNDOK4] = IMR_BCNDOK4,
+ .maps[RTL_IMR_BCNDOK3] = IMR_BCNDOK3,
+ .maps[RTL_IMR_BCNDOK2] = IMR_BCNDOK2,
+ .maps[RTL_IMR_BCNDOK1] = IMR_BCNDOK1,
+ .maps[RTL_IMR_TIMEOUT2] = IMR_TIMEOUT2,
+ .maps[RTL_IMR_TIMEOUT1] = IMR_TIMEOUT1,
+
+ .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW,
+ .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT,
+ .maps[RTL_IMR_BcnInt] = IMR_BcnInt,
+ .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW,
+ .maps[RTL_IMR_RDU] = IMR_RDU,
+ .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND,
+ .maps[RTL_IMR_BDOK] = IMR_BDOK,
+ .maps[RTL_IMR_MGNTDOK] = IMR_MGNTDOK,
+ .maps[RTL_IMR_TBDER] = IMR_TBDER,
+ .maps[RTL_IMR_HIGHDOK] = IMR_HIGHDOK,
+ .maps[RTL_IMR_TBDOK] = IMR_TBDOK,
+ .maps[RTL_IMR_BKDOK] = IMR_BKDOK,
+ .maps[RTL_IMR_BEDOK] = IMR_BEDOK,
+ .maps[RTL_IMR_VIDOK] = IMR_VIDOK,
+ .maps[RTL_IMR_VODOK] = IMR_VODOK,
+ .maps[RTL_IMR_ROK] = IMR_ROK,
+ .maps[RTL_IBSS_INT_MASKS] = (IMR_BcnInt | IMR_TBDOK | IMR_TBDER),
+
+ .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M,
+ .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M,
+ .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M,
+ .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M,
+ .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M,
+ .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M,
+ .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M,
+ .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M,
+ .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M,
+ .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M,
+ .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M,
+ .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M,
+
+ .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7,
+ .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15,
+};
+
+static struct pci_device_id rtl92de_pci_ids[] __devinitdata = {
+ {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8193, rtl92de_hal_cfg)},
+ {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x002B, rtl92de_hal_cfg)},
+ {},
+};
+
+MODULE_DEVICE_TABLE(pci, rtl92de_pci_ids);
+
+MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>");
+MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>");
+MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek 8192DE 802.11n Dual Mac PCI wireless");
+MODULE_FIRMWARE("rtlwifi/rtl8192defw.bin");
+
+module_param_named(swenc, rtl92de_mod_params.sw_crypto, bool, 0444);
+module_param_named(debug, rtl92de_mod_params.debug, int, 0444);
+module_param_named(ips, rtl92de_mod_params.inactiveps, bool, 0444);
+module_param_named(swlps, rtl92de_mod_params.swctrl_lps, bool, 0444);
+module_param_named(fwlps, rtl92de_mod_params.fwctrl_lps, bool, 0444);
+MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
+MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
+MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
+MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
+
+static const struct dev_pm_ops rtlwifi_pm_ops = {
+ .suspend = rtl_pci_suspend,
+ .resume = rtl_pci_resume,
+ .freeze = rtl_pci_suspend,
+ .thaw = rtl_pci_resume,
+ .poweroff = rtl_pci_suspend,
+ .restore = rtl_pci_resume,
+};
+
+static struct pci_driver rtl92de_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rtl92de_pci_ids,
+ .probe = rtl_pci_probe,
+ .remove = rtl_pci_disconnect,
+ .driver.pm = &rtlwifi_pm_ops,
+};
+
+/* add global spin lock to solve the problem that
+ * Dul mac register operation on the same time */
+spinlock_t globalmutex_power;
+spinlock_t globalmutex_for_fwdownload;
+spinlock_t globalmutex_for_power_and_efuse;
+
+static int __init rtl92de_module_init(void)
+{
+ int ret = 0;
+
+ spin_lock_init(&globalmutex_power);
+ spin_lock_init(&globalmutex_for_fwdownload);
+ spin_lock_init(&globalmutex_for_power_and_efuse);
+
+ ret = pci_register_driver(&rtl92de_driver);
+ if (ret)
+ RT_ASSERT(false, (": No device found\n"));
+ return ret;
+}
+
+static void __exit rtl92de_module_exit(void)
+{
+ pci_unregister_driver(&rtl92de_driver);
+}
+
+module_init(rtl92de_module_init);
+module_exit(rtl92de_module_exit);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.h b/drivers/net/wireless/rtlwifi/rtl8192de/sw.h
new file mode 100644
index 000000000000..c95e47de1346
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.h
@@ -0,0 +1,37 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92DE_SW_H__
+#define __RTL92DE_SW_H__
+
+extern spinlock_t globalmutex_power;
+extern spinlock_t globalmutex_for_fwdownload;
+extern spinlock_t globalmutex_for_power_and_efuse;
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/table.c b/drivers/net/wireless/rtlwifi/rtl8192de/table.c
new file mode 100644
index 000000000000..bad7f9449ecf
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/table.c
@@ -0,0 +1,1690 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ * Created on 2010/12/23, 6:38
+ *****************************************************************************/
+
+#include <linux/types.h>
+
+#include "table.h"
+
+u32 rtl8192de_phy_reg_2tarray[PHY_REG_2T_ARRAYLENGTH] = {
+ 0x024, 0x0011800d,
+ 0x028, 0x00ffdb83,
+ 0x014, 0x088ba955,
+ 0x010, 0x49022b03,
+ 0x800, 0x80040002,
+ 0x804, 0x00000003,
+ 0x808, 0x0000fc00,
+ 0x80c, 0x0000000a,
+ 0x810, 0x80706388,
+ 0x814, 0x020c3d10,
+ 0x818, 0x02200385,
+ 0x81c, 0x00000000,
+ 0x820, 0x01000100,
+ 0x824, 0x00390004,
+ 0x828, 0x01000100,
+ 0x82c, 0x00390004,
+ 0x830, 0x27272727,
+ 0x834, 0x27272727,
+ 0x838, 0x27272727,
+ 0x83c, 0x27272727,
+ 0x840, 0x00010000,
+ 0x844, 0x00010000,
+ 0x848, 0x27272727,
+ 0x84c, 0x27272727,
+ 0x850, 0x00000000,
+ 0x854, 0x00000000,
+ 0x858, 0x569a569a,
+ 0x85c, 0x0c1b25a4,
+ 0x860, 0x66e60230,
+ 0x864, 0x061f0130,
+ 0x868, 0x27272727,
+ 0x86c, 0x272b2b2b,
+ 0x870, 0x07000700,
+ 0x874, 0x22188000,
+ 0x878, 0x08080808,
+ 0x87c, 0x00007ff8,
+ 0x880, 0xc0083070,
+ 0x884, 0x00000cd5,
+ 0x888, 0x00000000,
+ 0x88c, 0xcc0000c0,
+ 0x890, 0x00000800,
+ 0x894, 0xfffffffe,
+ 0x898, 0x40302010,
+ 0x89c, 0x00706050,
+ 0x900, 0x00000000,
+ 0x904, 0x00000023,
+ 0x908, 0x00000000,
+ 0x90c, 0x81121313,
+ 0xa00, 0x00d047c8,
+ 0xa04, 0x80ff000c,
+ 0xa08, 0x8c838300,
+ 0xa0c, 0x2e68120f,
+ 0xa10, 0x9500bb78,
+ 0xa14, 0x11144028,
+ 0xa18, 0x00881117,
+ 0xa1c, 0x89140f00,
+ 0xa20, 0x1a1b0000,
+ 0xa24, 0x090e1317,
+ 0xa28, 0x00000204,
+ 0xa2c, 0x00d30000,
+ 0xa70, 0x101fbf00,
+ 0xa74, 0x00000007,
+ 0xc00, 0x40071d40,
+ 0xc04, 0x03a05633,
+ 0xc08, 0x001000e4,
+ 0xc0c, 0x6c6c6c6c,
+ 0xc10, 0x08800000,
+ 0xc14, 0x40000100,
+ 0xc18, 0x08800000,
+ 0xc1c, 0x40000100,
+ 0xc20, 0x00000000,
+ 0xc24, 0x00000000,
+ 0xc28, 0x00000000,
+ 0xc2c, 0x00000000,
+ 0xc30, 0x69e9ac44,
+ 0xc34, 0x469652cf,
+ 0xc38, 0x49795994,
+ 0xc3c, 0x0a979718,
+ 0xc40, 0x1f7c403f,
+ 0xc44, 0x000100b7,
+ 0xc48, 0xec020107,
+ 0xc4c, 0x007f037f,
+ 0xc50, 0x69543420,
+ 0xc54, 0x43bc009e,
+ 0xc58, 0x69543420,
+ 0xc5c, 0x433c00a8,
+ 0xc60, 0x00000000,
+ 0xc64, 0x5116848b,
+ 0xc68, 0x47c00bff,
+ 0xc6c, 0x00000036,
+ 0xc70, 0x2c7f000d,
+ 0xc74, 0x058610db,
+ 0xc78, 0x0000001f,
+ 0xc7c, 0x40b95612,
+ 0xc80, 0x40000100,
+ 0xc84, 0x20f60000,
+ 0xc88, 0x40000100,
+ 0xc8c, 0x20e00000,
+ 0xc90, 0x00121820,
+ 0xc94, 0x00000007,
+ 0xc98, 0x00121820,
+ 0xc9c, 0x00007f7f,
+ 0xca0, 0x00000000,
+ 0xca4, 0x00000080,
+ 0xca8, 0x00000000,
+ 0xcac, 0x00000000,
+ 0xcb0, 0x00000000,
+ 0xcb4, 0x00000000,
+ 0xcb8, 0x00000000,
+ 0xcbc, 0x28000000,
+ 0xcc0, 0x00000000,
+ 0xcc4, 0x00000000,
+ 0xcc8, 0x00000000,
+ 0xccc, 0x00000000,
+ 0xcd0, 0x00000000,
+ 0xcd4, 0x00000000,
+ 0xcd8, 0x64b11e20,
+ 0xcdc, 0xe8767533,
+ 0xce0, 0x00222222,
+ 0xce4, 0x00000000,
+ 0xce8, 0x37644302,
+ 0xcec, 0x2f97d40c,
+ 0xd00, 0x00080740,
+ 0xd04, 0x00020403,
+ 0xd08, 0x0000907f,
+ 0xd0c, 0x20010201,
+ 0xd10, 0xa0633333,
+ 0xd14, 0x3333bc43,
+ 0xd18, 0x7a8f5b6b,
+ 0xd2c, 0xcc979975,
+ 0xd30, 0x00000000,
+ 0xd34, 0x80608404,
+ 0xd38, 0x00000000,
+ 0xd3c, 0x00027293,
+ 0xd40, 0x00000000,
+ 0xd44, 0x00000000,
+ 0xd48, 0x00000000,
+ 0xd4c, 0x00000000,
+ 0xd50, 0x6437140a,
+ 0xd54, 0x00000000,
+ 0xd58, 0x00000000,
+ 0xd5c, 0x30032064,
+ 0xd60, 0x4653de68,
+ 0xd64, 0x04518a3c,
+ 0xd68, 0x00002101,
+ 0xd6c, 0x2a201c16,
+ 0xd70, 0x1812362e,
+ 0xd74, 0x322c2220,
+ 0xd78, 0x000e3c24,
+ 0xe00, 0x2a2a2a2a,
+ 0xe04, 0x2a2a2a2a,
+ 0xe08, 0x03902a2a,
+ 0xe10, 0x2a2a2a2a,
+ 0xe14, 0x2a2a2a2a,
+ 0xe18, 0x2a2a2a2a,
+ 0xe1c, 0x2a2a2a2a,
+ 0xe28, 0x00000000,
+ 0xe30, 0x1000dc1f,
+ 0xe34, 0x10008c1f,
+ 0xe38, 0x02140102,
+ 0xe3c, 0x681604c2,
+ 0xe40, 0x01007c00,
+ 0xe44, 0x01004800,
+ 0xe48, 0xfb000000,
+ 0xe4c, 0x000028d1,
+ 0xe50, 0x1000dc1f,
+ 0xe54, 0x10008c1f,
+ 0xe58, 0x02140102,
+ 0xe5c, 0x28160d05,
+ 0xe60, 0x00000010,
+ 0xe68, 0x001b25a4,
+ 0xe6c, 0x63db25a4,
+ 0xe70, 0x63db25a4,
+ 0xe74, 0x0c126da4,
+ 0xe78, 0x0c126da4,
+ 0xe7c, 0x0c126da4,
+ 0xe80, 0x0c126da4,
+ 0xe84, 0x63db25a4,
+ 0xe88, 0x0c126da4,
+ 0xe8c, 0x63db25a4,
+ 0xed0, 0x63db25a4,
+ 0xed4, 0x63db25a4,
+ 0xed8, 0x63db25a4,
+ 0xedc, 0x001b25a4,
+ 0xee0, 0x001b25a4,
+ 0xeec, 0x6fdb25a4,
+ 0xf14, 0x00000003,
+ 0xf1c, 0x00000064,
+ 0xf4c, 0x00000004,
+ 0xf00, 0x00000300,
+};
+
+u32 rtl8192de_phy_reg_array_pg[PHY_REG_ARRAY_PG_LENGTH] = {
+ 0xe00, 0xffffffff, 0x07090c0c,
+ 0xe04, 0xffffffff, 0x01020405,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x0b0c0c0e,
+ 0xe14, 0xffffffff, 0x01030506,
+ 0xe18, 0xffffffff, 0x0b0c0d0e,
+ 0xe1c, 0xffffffff, 0x01030509,
+ 0x830, 0xffffffff, 0x07090c0c,
+ 0x834, 0xffffffff, 0x01020405,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x0b0c0c0e,
+ 0x848, 0xffffffff, 0x01030506,
+ 0x84c, 0xffffffff, 0x0b0c0d0e,
+ 0x868, 0xffffffff, 0x01030509,
+ 0xe00, 0xffffffff, 0x00000000,
+ 0xe04, 0xffffffff, 0x00000000,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x00000000,
+ 0xe14, 0xffffffff, 0x00000000,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x00000000,
+ 0x834, 0xffffffff, 0x00000000,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x00000000,
+ 0x848, 0xffffffff, 0x00000000,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x04040404,
+ 0xe04, 0xffffffff, 0x00020204,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x06060606,
+ 0xe14, 0xffffffff, 0x00020406,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x04040404,
+ 0x834, 0xffffffff, 0x00020204,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x06060606,
+ 0x848, 0xffffffff, 0x00020406,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x00000000,
+ 0xe04, 0xffffffff, 0x00000000,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x00000000,
+ 0xe14, 0xffffffff, 0x00000000,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x00000000,
+ 0x834, 0xffffffff, 0x00000000,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x00000000,
+ 0x848, 0xffffffff, 0x00000000,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x00000000,
+ 0xe04, 0xffffffff, 0x00000000,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x00000000,
+ 0xe14, 0xffffffff, 0x00000000,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x00000000,
+ 0x834, 0xffffffff, 0x00000000,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x00000000,
+ 0x848, 0xffffffff, 0x00000000,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x04040404,
+ 0xe04, 0xffffffff, 0x00020204,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x00000000,
+ 0xe14, 0xffffffff, 0x00000000,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x04040404,
+ 0x834, 0xffffffff, 0x00020204,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x00000000,
+ 0x848, 0xffffffff, 0x00000000,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x00000000,
+ 0xe04, 0xffffffff, 0x00000000,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x00000000,
+ 0xe14, 0xffffffff, 0x00000000,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x00000000,
+ 0x834, 0xffffffff, 0x00000000,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x00000000,
+ 0x848, 0xffffffff, 0x00000000,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x04040404,
+ 0xe04, 0xffffffff, 0x00020204,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x08080808,
+ 0xe14, 0xffffffff, 0x00040408,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x04040404,
+ 0x834, 0xffffffff, 0x00020204,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x08080808,
+ 0x848, 0xffffffff, 0x00040408,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x04040404,
+ 0xe04, 0xffffffff, 0x00020204,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x08080808,
+ 0xe14, 0xffffffff, 0x00040408,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x04040404,
+ 0x834, 0xffffffff, 0x00020204,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x08080808,
+ 0x848, 0xffffffff, 0x00040408,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x04040404,
+ 0xe04, 0xffffffff, 0x00020204,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x08080808,
+ 0xe14, 0xffffffff, 0x00040408,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x04040404,
+ 0x834, 0xffffffff, 0x00020204,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x08080808,
+ 0x848, 0xffffffff, 0x00040408,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x04040404,
+ 0xe04, 0xffffffff, 0x00020204,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x08080808,
+ 0xe14, 0xffffffff, 0x00040408,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x04040404,
+ 0x834, 0xffffffff, 0x00020204,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x08080808,
+ 0x848, 0xffffffff, 0x00040408,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x04040404,
+ 0xe04, 0xffffffff, 0x00020204,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x08080808,
+ 0xe14, 0xffffffff, 0x00040408,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x04040404,
+ 0x834, 0xffffffff, 0x00020204,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x08080808,
+ 0x848, 0xffffffff, 0x00040408,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x04040404,
+ 0xe04, 0xffffffff, 0x00020204,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x08080808,
+ 0xe14, 0xffffffff, 0x00040408,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x04040404,
+ 0x834, 0xffffffff, 0x00020204,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x08080808,
+ 0x848, 0xffffffff, 0x00040408,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+};
+
+u32 rtl8192de_radioa_2tarray[RADIOA_2T_ARRAYLENGTH] = {
+ 0x000, 0x00030000,
+ 0x001, 0x00030000,
+ 0x002, 0x00000000,
+ 0x003, 0x00018c63,
+ 0x004, 0x00018c63,
+ 0x008, 0x00084000,
+ 0x00b, 0x0001c000,
+ 0x00e, 0x00018c67,
+ 0x00f, 0x00000851,
+ 0x014, 0x00021440,
+ 0x018, 0x00017524,
+ 0x019, 0x00000000,
+ 0x01d, 0x000a1290,
+ 0x023, 0x00001558,
+ 0x01a, 0x00030a99,
+ 0x01b, 0x00040b00,
+ 0x01c, 0x000fc339,
+ 0x03a, 0x000a57eb,
+ 0x03b, 0x00020000,
+ 0x03c, 0x000ff454,
+ 0x020, 0x0000aa52,
+ 0x021, 0x00054000,
+ 0x040, 0x0000aa52,
+ 0x041, 0x00014000,
+ 0x025, 0x000803be,
+ 0x026, 0x000fc638,
+ 0x027, 0x00077c18,
+ 0x028, 0x000de471,
+ 0x029, 0x000d7110,
+ 0x02a, 0x0008cb04,
+ 0x02b, 0x0004128b,
+ 0x02c, 0x00001840,
+ 0x043, 0x0002444f,
+ 0x044, 0x0001adb0,
+ 0x045, 0x00056467,
+ 0x046, 0x0008992c,
+ 0x047, 0x0000452c,
+ 0x048, 0x000f9c43,
+ 0x049, 0x00002e0c,
+ 0x04a, 0x000546eb,
+ 0x04b, 0x0008966c,
+ 0x04c, 0x0000dde9,
+ 0x018, 0x00007401,
+ 0x000, 0x00070000,
+ 0x012, 0x000dc000,
+ 0x012, 0x00090000,
+ 0x012, 0x00051000,
+ 0x012, 0x00012000,
+ 0x013, 0x000287b7,
+ 0x013, 0x000247ab,
+ 0x013, 0x0002079f,
+ 0x013, 0x0001c793,
+ 0x013, 0x0001839b,
+ 0x013, 0x00014392,
+ 0x013, 0x0001019a,
+ 0x013, 0x0000c191,
+ 0x013, 0x00008194,
+ 0x013, 0x000040a0,
+ 0x013, 0x00000018,
+ 0x015, 0x0000f424,
+ 0x015, 0x0004f424,
+ 0x015, 0x0008f424,
+ 0x016, 0x000e1330,
+ 0x016, 0x000a1330,
+ 0x016, 0x00061330,
+ 0x016, 0x00021330,
+ 0x018, 0x00017524,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bc,
+ 0x013, 0x000247b0,
+ 0x013, 0x000203b4,
+ 0x013, 0x0001c3a8,
+ 0x013, 0x000181b4,
+ 0x013, 0x000141a8,
+ 0x013, 0x000100b0,
+ 0x013, 0x0000c0a4,
+ 0x013, 0x0000b02c,
+ 0x013, 0x00004020,
+ 0x013, 0x00000014,
+ 0x015, 0x0000f4c3,
+ 0x015, 0x0004f4c3,
+ 0x015, 0x0008f4c3,
+ 0x016, 0x000e085f,
+ 0x016, 0x000a085f,
+ 0x016, 0x0006085f,
+ 0x016, 0x0002085f,
+ 0x018, 0x00037524,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bc,
+ 0x013, 0x000247b0,
+ 0x013, 0x000203b4,
+ 0x013, 0x0001c3a8,
+ 0x013, 0x000181b4,
+ 0x013, 0x000141a8,
+ 0x013, 0x000100b0,
+ 0x013, 0x0000c0a4,
+ 0x013, 0x0000b02c,
+ 0x013, 0x00004020,
+ 0x013, 0x00000014,
+ 0x015, 0x0000f4c3,
+ 0x015, 0x0004f4c3,
+ 0x015, 0x0008f4c3,
+ 0x016, 0x000e085f,
+ 0x016, 0x000a085f,
+ 0x016, 0x0006085f,
+ 0x016, 0x0002085f,
+ 0x018, 0x00057568,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bc,
+ 0x013, 0x000247b0,
+ 0x013, 0x000203b4,
+ 0x013, 0x0001c3a8,
+ 0x013, 0x000181b4,
+ 0x013, 0x000141a8,
+ 0x013, 0x000100b0,
+ 0x013, 0x0000c0a4,
+ 0x013, 0x0000b02c,
+ 0x013, 0x00004020,
+ 0x013, 0x00000014,
+ 0x015, 0x0000f4c3,
+ 0x015, 0x0004f4c3,
+ 0x015, 0x0008f4c3,
+ 0x016, 0x000e085f,
+ 0x016, 0x000a085f,
+ 0x016, 0x0006085f,
+ 0x016, 0x0002085f,
+ 0x030, 0x0004470f,
+ 0x031, 0x00044ff0,
+ 0x032, 0x00000070,
+ 0x033, 0x000dd480,
+ 0x034, 0x000ffac0,
+ 0x035, 0x000b80c0,
+ 0x036, 0x00077000,
+ 0x037, 0x00064ff2,
+ 0x038, 0x000e7661,
+ 0x039, 0x00000e90,
+ 0x000, 0x00030000,
+ 0x018, 0x0000f401,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x01e, 0x00088009,
+ 0x01f, 0x00080003,
+ 0x0fe, 0x00000000,
+ 0x01e, 0x00088001,
+ 0x01f, 0x00080000,
+ 0x0fe, 0x00000000,
+ 0x018, 0x00097524,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x02b, 0x00041289,
+ 0x0fe, 0x00000000,
+ 0x02d, 0x0006aaaa,
+ 0x02e, 0x000b4d01,
+ 0x02d, 0x00080000,
+ 0x02e, 0x00004d02,
+ 0x02d, 0x00095555,
+ 0x02e, 0x00054d03,
+ 0x02d, 0x000aaaaa,
+ 0x02e, 0x000b4d04,
+ 0x02d, 0x000c0000,
+ 0x02e, 0x00004d05,
+ 0x02d, 0x000d5555,
+ 0x02e, 0x00054d06,
+ 0x02d, 0x000eaaaa,
+ 0x02e, 0x000b4d07,
+ 0x02d, 0x00000000,
+ 0x02e, 0x00005108,
+ 0x02d, 0x00015555,
+ 0x02e, 0x00055109,
+ 0x02d, 0x0002aaaa,
+ 0x02e, 0x000b510a,
+ 0x02d, 0x00040000,
+ 0x02e, 0x0000510b,
+ 0x02d, 0x00055555,
+ 0x02e, 0x0005510c,
+};
+
+u32 rtl8192de_radiob_2tarray[RADIOB_2T_ARRAYLENGTH] = {
+ 0x000, 0x00030000,
+ 0x001, 0x00030000,
+ 0x002, 0x00000000,
+ 0x003, 0x00018c63,
+ 0x004, 0x00018c63,
+ 0x008, 0x00084000,
+ 0x00b, 0x0001c000,
+ 0x00e, 0x00018c67,
+ 0x00f, 0x00000851,
+ 0x014, 0x00021440,
+ 0x018, 0x00007401,
+ 0x019, 0x00000060,
+ 0x01d, 0x000a1290,
+ 0x023, 0x00001558,
+ 0x01a, 0x00030a99,
+ 0x01b, 0x00040b00,
+ 0x01c, 0x000fc339,
+ 0x03a, 0x000a57eb,
+ 0x03b, 0x00020000,
+ 0x03c, 0x000ff454,
+ 0x020, 0x0000aa52,
+ 0x021, 0x00054000,
+ 0x040, 0x0000aa52,
+ 0x041, 0x00014000,
+ 0x025, 0x000803be,
+ 0x026, 0x000fc638,
+ 0x027, 0x00077c18,
+ 0x028, 0x000d1c31,
+ 0x029, 0x000d7110,
+ 0x02a, 0x000aeb04,
+ 0x02b, 0x0004128b,
+ 0x02c, 0x00001840,
+ 0x043, 0x0002444f,
+ 0x044, 0x0001adb0,
+ 0x045, 0x00056467,
+ 0x046, 0x0008992c,
+ 0x047, 0x0000452c,
+ 0x048, 0x000f9c43,
+ 0x049, 0x00002e0c,
+ 0x04a, 0x000546eb,
+ 0x04b, 0x0008966c,
+ 0x04c, 0x0000dde9,
+ 0x018, 0x00007401,
+ 0x000, 0x00070000,
+ 0x012, 0x000dc000,
+ 0x012, 0x00090000,
+ 0x012, 0x00051000,
+ 0x012, 0x00012000,
+ 0x013, 0x000287b7,
+ 0x013, 0x000247ab,
+ 0x013, 0x0002079f,
+ 0x013, 0x0001c793,
+ 0x013, 0x0001839b,
+ 0x013, 0x00014392,
+ 0x013, 0x0001019a,
+ 0x013, 0x0000c191,
+ 0x013, 0x00008194,
+ 0x013, 0x000040a0,
+ 0x013, 0x00000018,
+ 0x015, 0x0000f424,
+ 0x015, 0x0004f424,
+ 0x015, 0x0008f424,
+ 0x016, 0x000e1330,
+ 0x016, 0x000a1330,
+ 0x016, 0x00061330,
+ 0x016, 0x00021330,
+ 0x018, 0x00017524,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bc,
+ 0x013, 0x000247b0,
+ 0x013, 0x000203b4,
+ 0x013, 0x0001c3a8,
+ 0x013, 0x000181b4,
+ 0x013, 0x000141a8,
+ 0x013, 0x000100b0,
+ 0x013, 0x0000c0a4,
+ 0x013, 0x0000b02c,
+ 0x013, 0x00004020,
+ 0x013, 0x00000014,
+ 0x015, 0x0000f4c3,
+ 0x015, 0x0004f4c3,
+ 0x015, 0x0008f4c3,
+ 0x016, 0x000e085f,
+ 0x016, 0x000a085f,
+ 0x016, 0x0006085f,
+ 0x016, 0x0002085f,
+ 0x018, 0x00037524,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bc,
+ 0x013, 0x000247b0,
+ 0x013, 0x000203b4,
+ 0x013, 0x0001c3a8,
+ 0x013, 0x000181b4,
+ 0x013, 0x000141a8,
+ 0x013, 0x000100b0,
+ 0x013, 0x0000c0a4,
+ 0x013, 0x0000b02c,
+ 0x013, 0x00004020,
+ 0x013, 0x00000014,
+ 0x015, 0x0000f4c3,
+ 0x015, 0x0004f4c3,
+ 0x015, 0x0008f4c3,
+ 0x016, 0x000e085f,
+ 0x016, 0x000a085f,
+ 0x016, 0x0006085f,
+ 0x016, 0x0002085f,
+ 0x018, 0x00057524,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bc,
+ 0x013, 0x000247b0,
+ 0x013, 0x000203b4,
+ 0x013, 0x0001c3a8,
+ 0x013, 0x000181b4,
+ 0x013, 0x000141a8,
+ 0x013, 0x000100b0,
+ 0x013, 0x0000c0a4,
+ 0x013, 0x0000b02c,
+ 0x013, 0x00004020,
+ 0x013, 0x00000014,
+ 0x015, 0x0000f4c3,
+ 0x015, 0x0004f4c3,
+ 0x015, 0x0008f4c3,
+ 0x016, 0x000e085f,
+ 0x016, 0x000a085f,
+ 0x016, 0x0006085f,
+ 0x016, 0x0002085f,
+ 0x030, 0x0004470f,
+ 0x031, 0x00044ff0,
+ 0x032, 0x00000070,
+ 0x033, 0x000dd480,
+ 0x034, 0x000ffac0,
+ 0x035, 0x000b80c0,
+ 0x036, 0x00077000,
+ 0x037, 0x00064ff2,
+ 0x038, 0x000e7661,
+ 0x039, 0x00000e90,
+ 0x000, 0x00030000,
+ 0x018, 0x0000f401,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x01e, 0x00088009,
+ 0x01f, 0x00080003,
+ 0x0fe, 0x00000000,
+ 0x01e, 0x00088001,
+ 0x01f, 0x00080000,
+ 0x0fe, 0x00000000,
+ 0x018, 0x00087401,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x02b, 0x00041289,
+ 0x0fe, 0x00000000,
+ 0x02d, 0x00066666,
+ 0x02e, 0x00064001,
+ 0x02d, 0x00091111,
+ 0x02e, 0x00014002,
+ 0x02d, 0x000bbbbb,
+ 0x02e, 0x000b4003,
+ 0x02d, 0x000e6666,
+ 0x02e, 0x00064004,
+ 0x02d, 0x00088888,
+ 0x02e, 0x00084005,
+ 0x02d, 0x0009dddd,
+ 0x02e, 0x000d4006,
+ 0x02d, 0x000b3333,
+ 0x02e, 0x00034007,
+ 0x02d, 0x00048888,
+ 0x02e, 0x00084408,
+ 0x02d, 0x000bbbbb,
+ 0x02e, 0x000b4409,
+ 0x02d, 0x000e6666,
+ 0x02e, 0x0006440a,
+ 0x02d, 0x00011111,
+ 0x02e, 0x0001480b,
+ 0x02d, 0x0003bbbb,
+ 0x02e, 0x000b480c,
+ 0x02d, 0x00066666,
+ 0x02e, 0x0006480d,
+ 0x02d, 0x000ccccc,
+ 0x02e, 0x000c480e,
+};
+
+u32 rtl8192de_radioa_2t_int_paarray[RADIOA_2T_INT_PA_ARRAYLENGTH] = {
+ 0x000, 0x00030000,
+ 0x001, 0x00030000,
+ 0x002, 0x00000000,
+ 0x003, 0x00018c63,
+ 0x004, 0x00018c63,
+ 0x008, 0x00084000,
+ 0x00b, 0x0001c000,
+ 0x00e, 0x00018c67,
+ 0x00f, 0x00000851,
+ 0x014, 0x00021440,
+ 0x018, 0x00017524,
+ 0x019, 0x00000000,
+ 0x01d, 0x000a1290,
+ 0x023, 0x00001558,
+ 0x01a, 0x00030a99,
+ 0x01b, 0x00040b00,
+ 0x01c, 0x000fc339,
+ 0x03a, 0x000a57eb,
+ 0x03b, 0x00020000,
+ 0x03c, 0x000ff454,
+ 0x020, 0x0000aa52,
+ 0x021, 0x00054000,
+ 0x040, 0x0000aa52,
+ 0x041, 0x00014000,
+ 0x025, 0x000803be,
+ 0x026, 0x000fc638,
+ 0x027, 0x00077c18,
+ 0x028, 0x000de471,
+ 0x029, 0x000d7110,
+ 0x02a, 0x0008eb04,
+ 0x02b, 0x0004128b,
+ 0x02c, 0x00001840,
+ 0x043, 0x0002444f,
+ 0x044, 0x0001adb0,
+ 0x045, 0x00056467,
+ 0x046, 0x0008992c,
+ 0x047, 0x0000452c,
+ 0x048, 0x000c0443,
+ 0x049, 0x00000730,
+ 0x04a, 0x00050f0f,
+ 0x04b, 0x000896ee,
+ 0x04c, 0x0000ddee,
+ 0x018, 0x00007401,
+ 0x000, 0x00070000,
+ 0x012, 0x000dc000,
+ 0x012, 0x00090000,
+ 0x012, 0x00051000,
+ 0x012, 0x00012000,
+ 0x013, 0x000287b7,
+ 0x013, 0x000247ab,
+ 0x013, 0x0002079f,
+ 0x013, 0x0001c793,
+ 0x013, 0x0001839b,
+ 0x013, 0x00014392,
+ 0x013, 0x0001019a,
+ 0x013, 0x0000c191,
+ 0x013, 0x00008194,
+ 0x013, 0x000040a0,
+ 0x013, 0x00000018,
+ 0x015, 0x0000f424,
+ 0x015, 0x0004f424,
+ 0x015, 0x0008f424,
+ 0x016, 0x000e1330,
+ 0x016, 0x000a1330,
+ 0x016, 0x00061330,
+ 0x016, 0x00021330,
+ 0x018, 0x00017524,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bf,
+ 0x013, 0x000247b3,
+ 0x013, 0x000207a7,
+ 0x013, 0x0001c79b,
+ 0x013, 0x0001839f,
+ 0x013, 0x00014393,
+ 0x013, 0x00010399,
+ 0x013, 0x0000c38d,
+ 0x013, 0x00008199,
+ 0x013, 0x0000418d,
+ 0x013, 0x00000099,
+ 0x015, 0x0000f495,
+ 0x015, 0x0004f495,
+ 0x015, 0x0008f495,
+ 0x016, 0x000e1874,
+ 0x016, 0x000a1874,
+ 0x016, 0x00061874,
+ 0x016, 0x00021874,
+ 0x018, 0x00037564,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bf,
+ 0x013, 0x000247b3,
+ 0x013, 0x000207a7,
+ 0x013, 0x0001c79b,
+ 0x013, 0x0001839f,
+ 0x013, 0x00014393,
+ 0x013, 0x00010399,
+ 0x013, 0x0000c38d,
+ 0x013, 0x00008199,
+ 0x013, 0x0000418d,
+ 0x013, 0x00000099,
+ 0x015, 0x0000f495,
+ 0x015, 0x0004f495,
+ 0x015, 0x0008f495,
+ 0x016, 0x000e1874,
+ 0x016, 0x000a1874,
+ 0x016, 0x00061874,
+ 0x016, 0x00021874,
+ 0x018, 0x00057595,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bf,
+ 0x013, 0x000247b3,
+ 0x013, 0x000207a7,
+ 0x013, 0x0001c79b,
+ 0x013, 0x0001839f,
+ 0x013, 0x00014393,
+ 0x013, 0x00010399,
+ 0x013, 0x0000c38d,
+ 0x013, 0x00008199,
+ 0x013, 0x0000418d,
+ 0x013, 0x00000099,
+ 0x015, 0x0000f495,
+ 0x015, 0x0004f495,
+ 0x015, 0x0008f495,
+ 0x016, 0x000e1874,
+ 0x016, 0x000a1874,
+ 0x016, 0x00061874,
+ 0x016, 0x00021874,
+ 0x030, 0x0004470f,
+ 0x031, 0x00044ff0,
+ 0x032, 0x00000070,
+ 0x033, 0x000dd480,
+ 0x034, 0x000ffac0,
+ 0x035, 0x000b80c0,
+ 0x036, 0x00077000,
+ 0x037, 0x00064ff2,
+ 0x038, 0x000e7661,
+ 0x039, 0x00000e90,
+ 0x000, 0x00030000,
+ 0x018, 0x0000f401,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x01e, 0x00088009,
+ 0x01f, 0x00080003,
+ 0x0fe, 0x00000000,
+ 0x01e, 0x00088001,
+ 0x01f, 0x00080000,
+ 0x0fe, 0x00000000,
+ 0x018, 0x00097524,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x02b, 0x00041289,
+ 0x0fe, 0x00000000,
+ 0x02d, 0x0006aaaa,
+ 0x02e, 0x000b4d01,
+ 0x02d, 0x00080000,
+ 0x02e, 0x00004d02,
+ 0x02d, 0x00095555,
+ 0x02e, 0x00054d03,
+ 0x02d, 0x000aaaaa,
+ 0x02e, 0x000b4d04,
+ 0x02d, 0x000c0000,
+ 0x02e, 0x00004d05,
+ 0x02d, 0x000d5555,
+ 0x02e, 0x00054d06,
+ 0x02d, 0x000eaaaa,
+ 0x02e, 0x000b4d07,
+ 0x02d, 0x00000000,
+ 0x02e, 0x00005108,
+ 0x02d, 0x00015555,
+ 0x02e, 0x00055109,
+ 0x02d, 0x0002aaaa,
+ 0x02e, 0x000b510a,
+ 0x02d, 0x00040000,
+ 0x02e, 0x0000510b,
+ 0x02d, 0x00055555,
+ 0x02e, 0x0005510c,
+};
+
+u32 rtl8192de_radiob_2t_int_paarray[RADIOB_2T_INT_PA_ARRAYLENGTH] = {
+ 0x000, 0x00030000,
+ 0x001, 0x00030000,
+ 0x002, 0x00000000,
+ 0x003, 0x00018c63,
+ 0x004, 0x00018c63,
+ 0x008, 0x00084000,
+ 0x00b, 0x0001c000,
+ 0x00e, 0x00018c67,
+ 0x00f, 0x00000851,
+ 0x014, 0x00021440,
+ 0x018, 0x00007401,
+ 0x019, 0x00000060,
+ 0x01d, 0x000a1290,
+ 0x023, 0x00001558,
+ 0x01a, 0x00030a99,
+ 0x01b, 0x00040b00,
+ 0x01c, 0x000fc339,
+ 0x03a, 0x000a57eb,
+ 0x03b, 0x00020000,
+ 0x03c, 0x000ff454,
+ 0x020, 0x0000aa52,
+ 0x021, 0x00054000,
+ 0x040, 0x0000aa52,
+ 0x041, 0x00014000,
+ 0x025, 0x000803be,
+ 0x026, 0x000fc638,
+ 0x027, 0x00077c18,
+ 0x028, 0x000d1c31,
+ 0x029, 0x000d7110,
+ 0x02a, 0x000aeb04,
+ 0x02b, 0x0004128b,
+ 0x02c, 0x00001840,
+ 0x043, 0x0002444f,
+ 0x044, 0x0001adb0,
+ 0x045, 0x00056467,
+ 0x046, 0x0008992c,
+ 0x047, 0x0000452c,
+ 0x048, 0x000c0443,
+ 0x049, 0x00000730,
+ 0x04a, 0x00050f0f,
+ 0x04b, 0x000896ee,
+ 0x04c, 0x0000ddee,
+ 0x018, 0x00007401,
+ 0x000, 0x00070000,
+ 0x012, 0x000dc000,
+ 0x012, 0x00090000,
+ 0x012, 0x00051000,
+ 0x012, 0x00012000,
+ 0x013, 0x000287b7,
+ 0x013, 0x000247ab,
+ 0x013, 0x0002079f,
+ 0x013, 0x0001c793,
+ 0x013, 0x0001839b,
+ 0x013, 0x00014392,
+ 0x013, 0x0001019a,
+ 0x013, 0x0000c191,
+ 0x013, 0x00008194,
+ 0x013, 0x000040a0,
+ 0x013, 0x00000018,
+ 0x015, 0x0000f424,
+ 0x015, 0x0004f424,
+ 0x015, 0x0008f424,
+ 0x016, 0x000e1330,
+ 0x016, 0x000a1330,
+ 0x016, 0x00061330,
+ 0x016, 0x00021330,
+ 0x018, 0x00017524,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bf,
+ 0x013, 0x000247b3,
+ 0x013, 0x000207a7,
+ 0x013, 0x0001c79b,
+ 0x013, 0x0001839f,
+ 0x013, 0x00014393,
+ 0x013, 0x00010399,
+ 0x013, 0x0000c38d,
+ 0x013, 0x00008199,
+ 0x013, 0x0000418d,
+ 0x013, 0x00000099,
+ 0x015, 0x0000f495,
+ 0x015, 0x0004f495,
+ 0x015, 0x0008f495,
+ 0x016, 0x000e1874,
+ 0x016, 0x000a1874,
+ 0x016, 0x00061874,
+ 0x016, 0x00021874,
+ 0x018, 0x00037564,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bf,
+ 0x013, 0x000247b3,
+ 0x013, 0x000207a7,
+ 0x013, 0x0001c79b,
+ 0x013, 0x0001839f,
+ 0x013, 0x00014393,
+ 0x013, 0x00010399,
+ 0x013, 0x0000c38d,
+ 0x013, 0x00008199,
+ 0x013, 0x0000418d,
+ 0x013, 0x00000099,
+ 0x015, 0x0000f495,
+ 0x015, 0x0004f495,
+ 0x015, 0x0008f495,
+ 0x016, 0x000e1874,
+ 0x016, 0x000a1874,
+ 0x016, 0x00061874,
+ 0x016, 0x00021874,
+ 0x018, 0x00057595,
+ 0x000, 0x00070000,
+ 0x012, 0x000cf000,
+ 0x012, 0x000bc000,
+ 0x012, 0x00078000,
+ 0x012, 0x00000000,
+ 0x013, 0x000287bf,
+ 0x013, 0x000247b3,
+ 0x013, 0x000207a7,
+ 0x013, 0x0001c79b,
+ 0x013, 0x0001839f,
+ 0x013, 0x00014393,
+ 0x013, 0x00010399,
+ 0x013, 0x0000c38d,
+ 0x013, 0x00008199,
+ 0x013, 0x0000418d,
+ 0x013, 0x00000099,
+ 0x015, 0x0000f495,
+ 0x015, 0x0004f495,
+ 0x015, 0x0008f495,
+ 0x016, 0x000e1874,
+ 0x016, 0x000a1874,
+ 0x016, 0x00061874,
+ 0x016, 0x00021874,
+ 0x030, 0x0004470f,
+ 0x031, 0x00044ff0,
+ 0x032, 0x00000070,
+ 0x033, 0x000dd480,
+ 0x034, 0x000ffac0,
+ 0x035, 0x000b80c0,
+ 0x036, 0x00077000,
+ 0x037, 0x00064ff2,
+ 0x038, 0x000e7661,
+ 0x039, 0x00000e90,
+ 0x000, 0x00030000,
+ 0x018, 0x0000f401,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x01e, 0x00088009,
+ 0x01f, 0x00080003,
+ 0x0fe, 0x00000000,
+ 0x01e, 0x00088001,
+ 0x01f, 0x00080000,
+ 0x0fe, 0x00000000,
+ 0x018, 0x00087401,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x02b, 0x00041289,
+ 0x0fe, 0x00000000,
+ 0x02d, 0x00066666,
+ 0x02e, 0x00064001,
+ 0x02d, 0x00091111,
+ 0x02e, 0x00014002,
+ 0x02d, 0x000bbbbb,
+ 0x02e, 0x000b4003,
+ 0x02d, 0x000e6666,
+ 0x02e, 0x00064004,
+ 0x02d, 0x00088888,
+ 0x02e, 0x00084005,
+ 0x02d, 0x0009dddd,
+ 0x02e, 0x000d4006,
+ 0x02d, 0x000b3333,
+ 0x02e, 0x00034007,
+ 0x02d, 0x00048888,
+ 0x02e, 0x00084408,
+ 0x02d, 0x000bbbbb,
+ 0x02e, 0x000b4409,
+ 0x02d, 0x000e6666,
+ 0x02e, 0x0006440a,
+ 0x02d, 0x00011111,
+ 0x02e, 0x0001480b,
+ 0x02d, 0x0003bbbb,
+ 0x02e, 0x000b480c,
+ 0x02d, 0x00066666,
+ 0x02e, 0x0006480d,
+ 0x02d, 0x000ccccc,
+ 0x02e, 0x000c480e,
+};
+
+u32 rtl8192de_mac_2tarray[MAC_2T_ARRAYLENGTH] = {
+ 0x420, 0x00000080,
+ 0x423, 0x00000000,
+ 0x430, 0x00000000,
+ 0x431, 0x00000000,
+ 0x432, 0x00000000,
+ 0x433, 0x00000001,
+ 0x434, 0x00000004,
+ 0x435, 0x00000005,
+ 0x436, 0x00000006,
+ 0x437, 0x00000007,
+ 0x438, 0x00000000,
+ 0x439, 0x00000000,
+ 0x43a, 0x00000000,
+ 0x43b, 0x00000001,
+ 0x43c, 0x00000004,
+ 0x43d, 0x00000005,
+ 0x43e, 0x00000006,
+ 0x43f, 0x00000007,
+ 0x440, 0x00000050,
+ 0x441, 0x00000001,
+ 0x442, 0x00000000,
+ 0x444, 0x00000015,
+ 0x445, 0x000000f0,
+ 0x446, 0x0000000f,
+ 0x447, 0x00000000,
+ 0x462, 0x00000008,
+ 0x463, 0x00000003,
+ 0x4c8, 0x000000ff,
+ 0x4c9, 0x00000008,
+ 0x4cc, 0x000000ff,
+ 0x4cd, 0x000000ff,
+ 0x4ce, 0x00000001,
+ 0x500, 0x00000026,
+ 0x501, 0x000000a2,
+ 0x502, 0x0000002f,
+ 0x503, 0x00000000,
+ 0x504, 0x00000028,
+ 0x505, 0x000000a3,
+ 0x506, 0x0000005e,
+ 0x507, 0x00000000,
+ 0x508, 0x0000002b,
+ 0x509, 0x000000a4,
+ 0x50a, 0x0000005e,
+ 0x50b, 0x00000000,
+ 0x50c, 0x0000004f,
+ 0x50d, 0x000000a4,
+ 0x50e, 0x00000000,
+ 0x50f, 0x00000000,
+ 0x512, 0x0000001c,
+ 0x514, 0x0000000a,
+ 0x515, 0x00000010,
+ 0x516, 0x0000000a,
+ 0x517, 0x00000010,
+ 0x51a, 0x00000016,
+ 0x524, 0x0000000f,
+ 0x525, 0x0000004f,
+ 0x546, 0x00000040,
+ 0x547, 0x00000000,
+ 0x550, 0x00000010,
+ 0x551, 0x00000010,
+ 0x559, 0x00000002,
+ 0x55a, 0x00000002,
+ 0x55d, 0x000000ff,
+ 0x605, 0x00000030,
+ 0x608, 0x0000000e,
+ 0x609, 0x0000002a,
+ 0x652, 0x00000020,
+ 0x63c, 0x0000000a,
+ 0x63d, 0x0000000a,
+ 0x63e, 0x0000000e,
+ 0x63f, 0x0000000e,
+ 0x66e, 0x00000005,
+ 0x700, 0x00000021,
+ 0x701, 0x00000043,
+ 0x702, 0x00000065,
+ 0x703, 0x00000087,
+ 0x708, 0x00000021,
+ 0x709, 0x00000043,
+ 0x70a, 0x00000065,
+ 0x70b, 0x00000087,
+};
+
+u32 rtl8192de_agctab_array[AGCTAB_ARRAYLENGTH] = {
+ 0xc78, 0x7b000001,
+ 0xc78, 0x7b010001,
+ 0xc78, 0x7b020001,
+ 0xc78, 0x7b030001,
+ 0xc78, 0x7b040001,
+ 0xc78, 0x7b050001,
+ 0xc78, 0x7b060001,
+ 0xc78, 0x7a070001,
+ 0xc78, 0x79080001,
+ 0xc78, 0x78090001,
+ 0xc78, 0x770a0001,
+ 0xc78, 0x760b0001,
+ 0xc78, 0x750c0001,
+ 0xc78, 0x740d0001,
+ 0xc78, 0x730e0001,
+ 0xc78, 0x720f0001,
+ 0xc78, 0x71100001,
+ 0xc78, 0x70110001,
+ 0xc78, 0x6f120001,
+ 0xc78, 0x6e130001,
+ 0xc78, 0x6d140001,
+ 0xc78, 0x6c150001,
+ 0xc78, 0x6b160001,
+ 0xc78, 0x6a170001,
+ 0xc78, 0x69180001,
+ 0xc78, 0x68190001,
+ 0xc78, 0x671a0001,
+ 0xc78, 0x661b0001,
+ 0xc78, 0x651c0001,
+ 0xc78, 0x641d0001,
+ 0xc78, 0x631e0001,
+ 0xc78, 0x621f0001,
+ 0xc78, 0x61200001,
+ 0xc78, 0x60210001,
+ 0xc78, 0x49220001,
+ 0xc78, 0x48230001,
+ 0xc78, 0x47240001,
+ 0xc78, 0x46250001,
+ 0xc78, 0x45260001,
+ 0xc78, 0x44270001,
+ 0xc78, 0x43280001,
+ 0xc78, 0x42290001,
+ 0xc78, 0x412a0001,
+ 0xc78, 0x402b0001,
+ 0xc78, 0x262c0001,
+ 0xc78, 0x252d0001,
+ 0xc78, 0x242e0001,
+ 0xc78, 0x232f0001,
+ 0xc78, 0x22300001,
+ 0xc78, 0x21310001,
+ 0xc78, 0x20320001,
+ 0xc78, 0x06330001,
+ 0xc78, 0x05340001,
+ 0xc78, 0x04350001,
+ 0xc78, 0x03360001,
+ 0xc78, 0x02370001,
+ 0xc78, 0x01380001,
+ 0xc78, 0x00390001,
+ 0xc78, 0x003a0001,
+ 0xc78, 0x003b0001,
+ 0xc78, 0x003c0001,
+ 0xc78, 0x003d0001,
+ 0xc78, 0x003e0001,
+ 0xc78, 0x003f0001,
+ 0xc78, 0x7b400001,
+ 0xc78, 0x7b410001,
+ 0xc78, 0x7a420001,
+ 0xc78, 0x79430001,
+ 0xc78, 0x78440001,
+ 0xc78, 0x77450001,
+ 0xc78, 0x76460001,
+ 0xc78, 0x75470001,
+ 0xc78, 0x74480001,
+ 0xc78, 0x73490001,
+ 0xc78, 0x724a0001,
+ 0xc78, 0x714b0001,
+ 0xc78, 0x704c0001,
+ 0xc78, 0x6f4d0001,
+ 0xc78, 0x6e4e0001,
+ 0xc78, 0x6d4f0001,
+ 0xc78, 0x6c500001,
+ 0xc78, 0x6b510001,
+ 0xc78, 0x6a520001,
+ 0xc78, 0x69530001,
+ 0xc78, 0x68540001,
+ 0xc78, 0x67550001,
+ 0xc78, 0x66560001,
+ 0xc78, 0x65570001,
+ 0xc78, 0x64580001,
+ 0xc78, 0x63590001,
+ 0xc78, 0x625a0001,
+ 0xc78, 0x615b0001,
+ 0xc78, 0x605c0001,
+ 0xc78, 0x485d0001,
+ 0xc78, 0x475e0001,
+ 0xc78, 0x465f0001,
+ 0xc78, 0x45600001,
+ 0xc78, 0x44610001,
+ 0xc78, 0x43620001,
+ 0xc78, 0x42630001,
+ 0xc78, 0x41640001,
+ 0xc78, 0x40650001,
+ 0xc78, 0x27660001,
+ 0xc78, 0x26670001,
+ 0xc78, 0x25680001,
+ 0xc78, 0x24690001,
+ 0xc78, 0x236a0001,
+ 0xc78, 0x226b0001,
+ 0xc78, 0x216c0001,
+ 0xc78, 0x206d0001,
+ 0xc78, 0x206e0001,
+ 0xc78, 0x206f0001,
+ 0xc78, 0x20700001,
+ 0xc78, 0x20710001,
+ 0xc78, 0x20720001,
+ 0xc78, 0x20730001,
+ 0xc78, 0x20740001,
+ 0xc78, 0x20750001,
+ 0xc78, 0x20760001,
+ 0xc78, 0x20770001,
+ 0xc78, 0x20780001,
+ 0xc78, 0x20790001,
+ 0xc78, 0x207a0001,
+ 0xc78, 0x207b0001,
+ 0xc78, 0x207c0001,
+ 0xc78, 0x207d0001,
+ 0xc78, 0x207e0001,
+ 0xc78, 0x207f0001,
+ 0xc78, 0x38000002,
+ 0xc78, 0x38010002,
+ 0xc78, 0x38020002,
+ 0xc78, 0x38030002,
+ 0xc78, 0x38040002,
+ 0xc78, 0x38050002,
+ 0xc78, 0x38060002,
+ 0xc78, 0x38070002,
+ 0xc78, 0x38080002,
+ 0xc78, 0x3c090002,
+ 0xc78, 0x3e0a0002,
+ 0xc78, 0x400b0002,
+ 0xc78, 0x440c0002,
+ 0xc78, 0x480d0002,
+ 0xc78, 0x4c0e0002,
+ 0xc78, 0x500f0002,
+ 0xc78, 0x52100002,
+ 0xc78, 0x56110002,
+ 0xc78, 0x5a120002,
+ 0xc78, 0x5e130002,
+ 0xc78, 0x60140002,
+ 0xc78, 0x60150002,
+ 0xc78, 0x60160002,
+ 0xc78, 0x62170002,
+ 0xc78, 0x62180002,
+ 0xc78, 0x62190002,
+ 0xc78, 0x621a0002,
+ 0xc78, 0x621b0002,
+ 0xc78, 0x621c0002,
+ 0xc78, 0x621d0002,
+ 0xc78, 0x621e0002,
+ 0xc78, 0x621f0002,
+ 0xc78, 0x32000044,
+ 0xc78, 0x32010044,
+ 0xc78, 0x32020044,
+ 0xc78, 0x32030044,
+ 0xc78, 0x32040044,
+ 0xc78, 0x32050044,
+ 0xc78, 0x32060044,
+ 0xc78, 0x32070044,
+ 0xc78, 0x32080044,
+ 0xc78, 0x34090044,
+ 0xc78, 0x350a0044,
+ 0xc78, 0x360b0044,
+ 0xc78, 0x370c0044,
+ 0xc78, 0x380d0044,
+ 0xc78, 0x390e0044,
+ 0xc78, 0x3a0f0044,
+ 0xc78, 0x3e100044,
+ 0xc78, 0x42110044,
+ 0xc78, 0x44120044,
+ 0xc78, 0x46130044,
+ 0xc78, 0x4a140044,
+ 0xc78, 0x4e150044,
+ 0xc78, 0x50160044,
+ 0xc78, 0x55170044,
+ 0xc78, 0x5a180044,
+ 0xc78, 0x5e190044,
+ 0xc78, 0x641a0044,
+ 0xc78, 0x6e1b0044,
+ 0xc78, 0x6e1c0044,
+ 0xc78, 0x6e1d0044,
+ 0xc78, 0x6e1e0044,
+ 0xc78, 0x6e1f0044,
+ 0xc78, 0x6e1f0000,
+};
+
+u32 rtl8192de_agctab_5garray[AGCTAB_5G_ARRAYLENGTH] = {
+ 0xc78, 0x7b000001,
+ 0xc78, 0x7b010001,
+ 0xc78, 0x7a020001,
+ 0xc78, 0x79030001,
+ 0xc78, 0x78040001,
+ 0xc78, 0x77050001,
+ 0xc78, 0x76060001,
+ 0xc78, 0x75070001,
+ 0xc78, 0x74080001,
+ 0xc78, 0x73090001,
+ 0xc78, 0x720a0001,
+ 0xc78, 0x710b0001,
+ 0xc78, 0x700c0001,
+ 0xc78, 0x6f0d0001,
+ 0xc78, 0x6e0e0001,
+ 0xc78, 0x6d0f0001,
+ 0xc78, 0x6c100001,
+ 0xc78, 0x6b110001,
+ 0xc78, 0x6a120001,
+ 0xc78, 0x69130001,
+ 0xc78, 0x68140001,
+ 0xc78, 0x67150001,
+ 0xc78, 0x66160001,
+ 0xc78, 0x65170001,
+ 0xc78, 0x64180001,
+ 0xc78, 0x63190001,
+ 0xc78, 0x621a0001,
+ 0xc78, 0x611b0001,
+ 0xc78, 0x601c0001,
+ 0xc78, 0x481d0001,
+ 0xc78, 0x471e0001,
+ 0xc78, 0x461f0001,
+ 0xc78, 0x45200001,
+ 0xc78, 0x44210001,
+ 0xc78, 0x43220001,
+ 0xc78, 0x42230001,
+ 0xc78, 0x41240001,
+ 0xc78, 0x40250001,
+ 0xc78, 0x27260001,
+ 0xc78, 0x26270001,
+ 0xc78, 0x25280001,
+ 0xc78, 0x24290001,
+ 0xc78, 0x232a0001,
+ 0xc78, 0x222b0001,
+ 0xc78, 0x212c0001,
+ 0xc78, 0x202d0001,
+ 0xc78, 0x202e0001,
+ 0xc78, 0x202f0001,
+ 0xc78, 0x20300001,
+ 0xc78, 0x20310001,
+ 0xc78, 0x20320001,
+ 0xc78, 0x20330001,
+ 0xc78, 0x20340001,
+ 0xc78, 0x20350001,
+ 0xc78, 0x20360001,
+ 0xc78, 0x20370001,
+ 0xc78, 0x20380001,
+ 0xc78, 0x20390001,
+ 0xc78, 0x203a0001,
+ 0xc78, 0x203b0001,
+ 0xc78, 0x203c0001,
+ 0xc78, 0x203d0001,
+ 0xc78, 0x203e0001,
+ 0xc78, 0x203f0001,
+ 0xc78, 0x32000044,
+ 0xc78, 0x32010044,
+ 0xc78, 0x32020044,
+ 0xc78, 0x32030044,
+ 0xc78, 0x32040044,
+ 0xc78, 0x32050044,
+ 0xc78, 0x32060044,
+ 0xc78, 0x32070044,
+ 0xc78, 0x32080044,
+ 0xc78, 0x34090044,
+ 0xc78, 0x350a0044,
+ 0xc78, 0x360b0044,
+ 0xc78, 0x370c0044,
+ 0xc78, 0x380d0044,
+ 0xc78, 0x390e0044,
+ 0xc78, 0x3a0f0044,
+ 0xc78, 0x3e100044,
+ 0xc78, 0x42110044,
+ 0xc78, 0x44120044,
+ 0xc78, 0x46130044,
+ 0xc78, 0x4a140044,
+ 0xc78, 0x4e150044,
+ 0xc78, 0x50160044,
+ 0xc78, 0x55170044,
+ 0xc78, 0x5a180044,
+ 0xc78, 0x5e190044,
+ 0xc78, 0x641a0044,
+ 0xc78, 0x6e1b0044,
+ 0xc78, 0x6e1c0044,
+ 0xc78, 0x6e1d0044,
+ 0xc78, 0x6e1e0044,
+ 0xc78, 0x6e1f0044,
+ 0xc78, 0x6e1f0000,
+};
+
+u32 rtl8192de_agctab_2garray[AGCTAB_2G_ARRAYLENGTH] = {
+ 0xc78, 0x7b000001,
+ 0xc78, 0x7b010001,
+ 0xc78, 0x7b020001,
+ 0xc78, 0x7b030001,
+ 0xc78, 0x7b040001,
+ 0xc78, 0x7b050001,
+ 0xc78, 0x7b060001,
+ 0xc78, 0x7a070001,
+ 0xc78, 0x79080001,
+ 0xc78, 0x78090001,
+ 0xc78, 0x770a0001,
+ 0xc78, 0x760b0001,
+ 0xc78, 0x750c0001,
+ 0xc78, 0x740d0001,
+ 0xc78, 0x730e0001,
+ 0xc78, 0x720f0001,
+ 0xc78, 0x71100001,
+ 0xc78, 0x70110001,
+ 0xc78, 0x6f120001,
+ 0xc78, 0x6e130001,
+ 0xc78, 0x6d140001,
+ 0xc78, 0x6c150001,
+ 0xc78, 0x6b160001,
+ 0xc78, 0x6a170001,
+ 0xc78, 0x69180001,
+ 0xc78, 0x68190001,
+ 0xc78, 0x671a0001,
+ 0xc78, 0x661b0001,
+ 0xc78, 0x651c0001,
+ 0xc78, 0x641d0001,
+ 0xc78, 0x631e0001,
+ 0xc78, 0x621f0001,
+ 0xc78, 0x61200001,
+ 0xc78, 0x60210001,
+ 0xc78, 0x49220001,
+ 0xc78, 0x48230001,
+ 0xc78, 0x47240001,
+ 0xc78, 0x46250001,
+ 0xc78, 0x45260001,
+ 0xc78, 0x44270001,
+ 0xc78, 0x43280001,
+ 0xc78, 0x42290001,
+ 0xc78, 0x412a0001,
+ 0xc78, 0x402b0001,
+ 0xc78, 0x262c0001,
+ 0xc78, 0x252d0001,
+ 0xc78, 0x242e0001,
+ 0xc78, 0x232f0001,
+ 0xc78, 0x22300001,
+ 0xc78, 0x21310001,
+ 0xc78, 0x20320001,
+ 0xc78, 0x06330001,
+ 0xc78, 0x05340001,
+ 0xc78, 0x04350001,
+ 0xc78, 0x03360001,
+ 0xc78, 0x02370001,
+ 0xc78, 0x01380001,
+ 0xc78, 0x00390001,
+ 0xc78, 0x003a0001,
+ 0xc78, 0x003b0001,
+ 0xc78, 0x003c0001,
+ 0xc78, 0x003d0001,
+ 0xc78, 0x003e0001,
+ 0xc78, 0x003f0001,
+ 0xc78, 0x38000002,
+ 0xc78, 0x38010002,
+ 0xc78, 0x38020002,
+ 0xc78, 0x38030002,
+ 0xc78, 0x38040002,
+ 0xc78, 0x38050002,
+ 0xc78, 0x38060002,
+ 0xc78, 0x38070002,
+ 0xc78, 0x38080002,
+ 0xc78, 0x3c090002,
+ 0xc78, 0x3e0a0002,
+ 0xc78, 0x400b0002,
+ 0xc78, 0x440c0002,
+ 0xc78, 0x480d0002,
+ 0xc78, 0x4c0e0002,
+ 0xc78, 0x500f0002,
+ 0xc78, 0x52100002,
+ 0xc78, 0x56110002,
+ 0xc78, 0x5a120002,
+ 0xc78, 0x5e130002,
+ 0xc78, 0x60140002,
+ 0xc78, 0x60150002,
+ 0xc78, 0x60160002,
+ 0xc78, 0x62170002,
+ 0xc78, 0x62180002,
+ 0xc78, 0x62190002,
+ 0xc78, 0x621a0002,
+ 0xc78, 0x621b0002,
+ 0xc78, 0x621c0002,
+ 0xc78, 0x621d0002,
+ 0xc78, 0x621e0002,
+ 0xc78, 0x621f0002,
+ 0xc78, 0x6e1f0000,
+};
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/table.h b/drivers/net/wireless/rtlwifi/rtl8192de/table.h
new file mode 100644
index 000000000000..93f30ca62d8f
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/table.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ * Created on 2010/ 5/18, 1:41
+ *****************************************************************************/
+
+#ifndef __RTL92DE_TABLE__H_
+#define __RTL92DE_TABLE__H_
+
+/*Created on 2011/ 1/14, 1:35*/
+
+#define PHY_REG_2T_ARRAYLENGTH 380
+extern u32 rtl8192de_phy_reg_2tarray[PHY_REG_2T_ARRAYLENGTH];
+#define PHY_REG_ARRAY_PG_LENGTH 624
+extern u32 rtl8192de_phy_reg_array_pg[PHY_REG_ARRAY_PG_LENGTH];
+#define RADIOA_2T_ARRAYLENGTH 378
+extern u32 rtl8192de_radioa_2tarray[RADIOA_2T_ARRAYLENGTH];
+#define RADIOB_2T_ARRAYLENGTH 384
+extern u32 rtl8192de_radiob_2tarray[RADIOB_2T_ARRAYLENGTH];
+#define RADIOA_2T_INT_PA_ARRAYLENGTH 378
+extern u32 rtl8192de_radioa_2t_int_paarray[RADIOA_2T_INT_PA_ARRAYLENGTH];
+#define RADIOB_2T_INT_PA_ARRAYLENGTH 384
+extern u32 rtl8192de_radiob_2t_int_paarray[RADIOB_2T_INT_PA_ARRAYLENGTH];
+#define MAC_2T_ARRAYLENGTH 160
+extern u32 rtl8192de_mac_2tarray[MAC_2T_ARRAYLENGTH];
+#define AGCTAB_ARRAYLENGTH 386
+extern u32 rtl8192de_agctab_array[AGCTAB_ARRAYLENGTH];
+#define AGCTAB_5G_ARRAYLENGTH 194
+extern u32 rtl8192de_agctab_5garray[AGCTAB_5G_ARRAYLENGTH];
+#define AGCTAB_2G_ARRAYLENGTH 194
+extern u32 rtl8192de_agctab_2garray[AGCTAB_2G_ARRAYLENGTH];
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c
new file mode 100644
index 000000000000..3637c0c33525
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c
@@ -0,0 +1,866 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "../base.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "trx.h"
+#include "led.h"
+
+static u8 _rtl92de_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue)
+{
+ __le16 fc = rtl_get_fc(skb);
+
+ if (unlikely(ieee80211_is_beacon(fc)))
+ return QSLT_BEACON;
+ if (ieee80211_is_mgmt(fc))
+ return QSLT_MGNT;
+
+ return skb->priority;
+}
+
+static u8 _rtl92d_query_rxpwrpercentage(char antpower)
+{
+ if ((antpower <= -100) || (antpower >= 20))
+ return 0;
+ else if (antpower >= 0)
+ return 100;
+ else
+ return 100 + antpower;
+}
+
+static u8 _rtl92d_evm_db_to_percentage(char value)
+{
+ char ret_val = value;
+
+ if (ret_val >= 0)
+ ret_val = 0;
+ if (ret_val <= -33)
+ ret_val = -33;
+ ret_val = 0 - ret_val;
+ ret_val *= 3;
+ if (ret_val == 99)
+ ret_val = 100;
+ return ret_val;
+}
+
+static long _rtl92de_translate_todbm(struct ieee80211_hw *hw,
+ u8 signal_strength_index)
+{
+ long signal_power;
+
+ signal_power = (long)((signal_strength_index + 1) >> 1);
+ signal_power -= 95;
+ return signal_power;
+}
+
+static long _rtl92de_signal_scale_mapping(struct ieee80211_hw *hw, long currsig)
+{
+ long retsig;
+
+ if (currsig >= 61 && currsig <= 100)
+ retsig = 90 + ((currsig - 60) / 4);
+ else if (currsig >= 41 && currsig <= 60)
+ retsig = 78 + ((currsig - 40) / 2);
+ else if (currsig >= 31 && currsig <= 40)
+ retsig = 66 + (currsig - 30);
+ else if (currsig >= 21 && currsig <= 30)
+ retsig = 54 + (currsig - 20);
+ else if (currsig >= 5 && currsig <= 20)
+ retsig = 42 + (((currsig - 5) * 2) / 3);
+ else if (currsig == 4)
+ retsig = 36;
+ else if (currsig == 3)
+ retsig = 27;
+ else if (currsig == 2)
+ retsig = 18;
+ else if (currsig == 1)
+ retsig = 9;
+ else
+ retsig = currsig;
+ return retsig;
+}
+
+static void _rtl92de_query_rxphystatus(struct ieee80211_hw *hw,
+ struct rtl_stats *pstats,
+ struct rx_desc_92d *pdesc,
+ struct rx_fwinfo_92d *p_drvinfo,
+ bool packet_match_bssid,
+ bool packet_toself,
+ bool packet_beacon)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtlpriv);
+ struct phy_sts_cck_8192d *cck_buf;
+ s8 rx_pwr_all, rx_pwr[4];
+ u8 rf_rx_num = 0, evm, pwdb_all;
+ u8 i, max_spatial_stream;
+ u32 rssi, total_rssi = 0;
+ bool is_cck_rate;
+
+ is_cck_rate = RX_HAL_IS_CCK_RATE(pdesc);
+ pstats->packet_matchbssid = packet_match_bssid;
+ pstats->packet_toself = packet_toself;
+ pstats->packet_beacon = packet_beacon;
+ pstats->is_cck = is_cck_rate;
+ pstats->rx_mimo_signalquality[0] = -1;
+ pstats->rx_mimo_signalquality[1] = -1;
+
+ if (is_cck_rate) {
+ u8 report, cck_highpwr;
+ cck_buf = (struct phy_sts_cck_8192d *)p_drvinfo;
+ if (ppsc->rfpwr_state == ERFON)
+ cck_highpwr = (u8) rtl_get_bbreg(hw,
+ RFPGA0_XA_HSSIPARAMETER2,
+ BIT(9));
+ else
+ cck_highpwr = false;
+ if (!cck_highpwr) {
+ u8 cck_agc_rpt = cck_buf->cck_agc_rpt;
+ report = cck_buf->cck_agc_rpt & 0xc0;
+ report = report >> 6;
+ switch (report) {
+ case 0x3:
+ rx_pwr_all = -46 - (cck_agc_rpt & 0x3e);
+ break;
+ case 0x2:
+ rx_pwr_all = -26 - (cck_agc_rpt & 0x3e);
+ break;
+ case 0x1:
+ rx_pwr_all = -12 - (cck_agc_rpt & 0x3e);
+ break;
+ case 0x0:
+ rx_pwr_all = 16 - (cck_agc_rpt & 0x3e);
+ break;
+ }
+ } else {
+ u8 cck_agc_rpt = cck_buf->cck_agc_rpt;
+ report = p_drvinfo->cfosho[0] & 0x60;
+ report = report >> 5;
+ switch (report) {
+ case 0x3:
+ rx_pwr_all = -46 - ((cck_agc_rpt & 0x1f) << 1);
+ break;
+ case 0x2:
+ rx_pwr_all = -26 - ((cck_agc_rpt & 0x1f) << 1);
+ break;
+ case 0x1:
+ rx_pwr_all = -12 - ((cck_agc_rpt & 0x1f) << 1);
+ break;
+ case 0x0:
+ rx_pwr_all = 16 - ((cck_agc_rpt & 0x1f) << 1);
+ break;
+ }
+ }
+ pwdb_all = _rtl92d_query_rxpwrpercentage(rx_pwr_all);
+ /* CCK gain is smaller than OFDM/MCS gain, */
+ /* so we add gain diff by experiences, the val is 6 */
+ pwdb_all += 6;
+ if (pwdb_all > 100)
+ pwdb_all = 100;
+ /* modify the offset to make the same gain index with OFDM. */
+ if (pwdb_all > 34 && pwdb_all <= 42)
+ pwdb_all -= 2;
+ else if (pwdb_all > 26 && pwdb_all <= 34)
+ pwdb_all -= 6;
+ else if (pwdb_all > 14 && pwdb_all <= 26)
+ pwdb_all -= 8;
+ else if (pwdb_all > 4 && pwdb_all <= 14)
+ pwdb_all -= 4;
+ pstats->rx_pwdb_all = pwdb_all;
+ pstats->recvsignalpower = rx_pwr_all;
+ if (packet_match_bssid) {
+ u8 sq;
+ if (pstats->rx_pwdb_all > 40) {
+ sq = 100;
+ } else {
+ sq = cck_buf->sq_rpt;
+ if (sq > 64)
+ sq = 0;
+ else if (sq < 20)
+ sq = 100;
+ else
+ sq = ((64 - sq) * 100) / 44;
+ }
+ pstats->signalquality = sq;
+ pstats->rx_mimo_signalquality[0] = sq;
+ pstats->rx_mimo_signalquality[1] = -1;
+ }
+ } else {
+ rtlpriv->dm.rfpath_rxenable[0] = true;
+ rtlpriv->dm.rfpath_rxenable[1] = true;
+ for (i = RF90_PATH_A; i < RF6052_MAX_PATH; i++) {
+ if (rtlpriv->dm.rfpath_rxenable[i])
+ rf_rx_num++;
+ rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f) * 2)
+ - 110;
+ rssi = _rtl92d_query_rxpwrpercentage(rx_pwr[i]);
+ total_rssi += rssi;
+ rtlpriv->stats.rx_snr_db[i] =
+ (long)(p_drvinfo->rxsnr[i] / 2);
+ if (packet_match_bssid)
+ pstats->rx_mimo_signalstrength[i] = (u8) rssi;
+ }
+ rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 106;
+ pwdb_all = _rtl92d_query_rxpwrpercentage(rx_pwr_all);
+ pstats->rx_pwdb_all = pwdb_all;
+ pstats->rxpower = rx_pwr_all;
+ pstats->recvsignalpower = rx_pwr_all;
+ if (pdesc->rxht && pdesc->rxmcs >= DESC92_RATEMCS8 &&
+ pdesc->rxmcs <= DESC92_RATEMCS15)
+ max_spatial_stream = 2;
+ else
+ max_spatial_stream = 1;
+ for (i = 0; i < max_spatial_stream; i++) {
+ evm = _rtl92d_evm_db_to_percentage(p_drvinfo->rxevm[i]);
+ if (packet_match_bssid) {
+ if (i == 0)
+ pstats->signalquality =
+ (u8)(evm & 0xff);
+ pstats->rx_mimo_signalquality[i] =
+ (u8)(evm & 0xff);
+ }
+ }
+ }
+ if (is_cck_rate)
+ pstats->signalstrength = (u8)(_rtl92de_signal_scale_mapping(hw,
+ pwdb_all));
+ else if (rf_rx_num != 0)
+ pstats->signalstrength = (u8)(_rtl92de_signal_scale_mapping(hw,
+ total_rssi /= rf_rx_num));
+}
+
+static void rtl92d_loop_over_paths(struct ieee80211_hw *hw,
+ struct rtl_stats *pstats)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u8 rfpath;
+
+ for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath;
+ rfpath++) {
+ if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) {
+ rtlpriv->stats.rx_rssi_percentage[rfpath] =
+ pstats->rx_mimo_signalstrength[rfpath];
+
+ }
+ if (pstats->rx_mimo_signalstrength[rfpath] >
+ rtlpriv->stats.rx_rssi_percentage[rfpath]) {
+ rtlpriv->stats.rx_rssi_percentage[rfpath] =
+ ((rtlpriv->stats.rx_rssi_percentage[rfpath] *
+ (RX_SMOOTH_FACTOR - 1)) +
+ (pstats->rx_mimo_signalstrength[rfpath])) /
+ (RX_SMOOTH_FACTOR);
+ rtlpriv->stats.rx_rssi_percentage[rfpath] =
+ rtlpriv->stats.rx_rssi_percentage[rfpath] + 1;
+ } else {
+ rtlpriv->stats.rx_rssi_percentage[rfpath] =
+ ((rtlpriv->stats.rx_rssi_percentage[rfpath] *
+ (RX_SMOOTH_FACTOR - 1)) +
+ (pstats->rx_mimo_signalstrength[rfpath])) /
+ (RX_SMOOTH_FACTOR);
+ }
+ }
+}
+
+static void _rtl92de_process_ui_rssi(struct ieee80211_hw *hw,
+ struct rtl_stats *pstats)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 last_rssi, tmpval;
+
+ if (pstats->packet_toself || pstats->packet_beacon) {
+ rtlpriv->stats.rssi_calculate_cnt++;
+ if (rtlpriv->stats.ui_rssi.total_num++ >=
+ PHY_RSSI_SLID_WIN_MAX) {
+ rtlpriv->stats.ui_rssi.total_num =
+ PHY_RSSI_SLID_WIN_MAX;
+ last_rssi = rtlpriv->stats.ui_rssi.elements[
+ rtlpriv->stats.ui_rssi.index];
+ rtlpriv->stats.ui_rssi.total_val -= last_rssi;
+ }
+ rtlpriv->stats.ui_rssi.total_val += pstats->signalstrength;
+ rtlpriv->stats.ui_rssi.elements
+ [rtlpriv->stats.ui_rssi.index++] =
+ pstats->signalstrength;
+ if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX)
+ rtlpriv->stats.ui_rssi.index = 0;
+ tmpval = rtlpriv->stats.ui_rssi.total_val /
+ rtlpriv->stats.ui_rssi.total_num;
+ rtlpriv->stats.signal_strength = _rtl92de_translate_todbm(hw,
+ (u8) tmpval);
+ pstats->rssi = rtlpriv->stats.signal_strength;
+ }
+ if (!pstats->is_cck && pstats->packet_toself)
+ rtl92d_loop_over_paths(hw, pstats);
+}
+
+static void _rtl92de_update_rxsignalstatistics(struct ieee80211_hw *hw,
+ struct rtl_stats *pstats)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ int weighting = 0;
+
+ if (rtlpriv->stats.recv_signal_power == 0)
+ rtlpriv->stats.recv_signal_power = pstats->recvsignalpower;
+ if (pstats->recvsignalpower > rtlpriv->stats.recv_signal_power)
+ weighting = 5;
+ else if (pstats->recvsignalpower < rtlpriv->stats.recv_signal_power)
+ weighting = (-5);
+ rtlpriv->stats.recv_signal_power = (rtlpriv->stats.recv_signal_power *
+ 5 + pstats->recvsignalpower + weighting) / 6;
+}
+
+static void _rtl92de_process_pwdb(struct ieee80211_hw *hw,
+ struct rtl_stats *pstats)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ long undecorated_smoothed_pwdb;
+
+ if (mac->opmode == NL80211_IFTYPE_ADHOC ||
+ mac->opmode == NL80211_IFTYPE_AP)
+ return;
+ else
+ undecorated_smoothed_pwdb =
+ rtlpriv->dm.undecorated_smoothed_pwdb;
+
+ if (pstats->packet_toself || pstats->packet_beacon) {
+ if (undecorated_smoothed_pwdb < 0)
+ undecorated_smoothed_pwdb = pstats->rx_pwdb_all;
+ if (pstats->rx_pwdb_all > (u32) undecorated_smoothed_pwdb) {
+ undecorated_smoothed_pwdb =
+ (((undecorated_smoothed_pwdb) *
+ (RX_SMOOTH_FACTOR - 1)) +
+ (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR);
+ undecorated_smoothed_pwdb =
+ undecorated_smoothed_pwdb + 1;
+ } else {
+ undecorated_smoothed_pwdb =
+ (((undecorated_smoothed_pwdb) *
+ (RX_SMOOTH_FACTOR - 1)) +
+ (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR);
+ }
+ rtlpriv->dm.undecorated_smoothed_pwdb =
+ undecorated_smoothed_pwdb;
+ _rtl92de_update_rxsignalstatistics(hw, pstats);
+ }
+}
+
+static void rtl92d_loop_over_streams(struct ieee80211_hw *hw,
+ struct rtl_stats *pstats)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ if (pstats->rx_mimo_signalquality[stream] != -1) {
+ if (rtlpriv->stats.rx_evm_percentage[stream] == 0) {
+ rtlpriv->stats.rx_evm_percentage[stream] =
+ pstats->rx_mimo_signalquality[stream];
+ }
+ rtlpriv->stats.rx_evm_percentage[stream] =
+ ((rtlpriv->stats.rx_evm_percentage[stream]
+ * (RX_SMOOTH_FACTOR - 1)) +
+ (pstats->rx_mimo_signalquality[stream] * 1)) /
+ (RX_SMOOTH_FACTOR);
+ }
+ }
+}
+
+static void _rtl92de_process_ui_link_quality(struct ieee80211_hw *hw,
+ struct rtl_stats *pstats)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 last_evm, tmpval;
+
+ if (pstats->signalquality == 0)
+ return;
+ if (pstats->packet_toself || pstats->packet_beacon) {
+ if (rtlpriv->stats.ui_link_quality.total_num++ >=
+ PHY_LINKQUALITY_SLID_WIN_MAX) {
+ rtlpriv->stats.ui_link_quality.total_num =
+ PHY_LINKQUALITY_SLID_WIN_MAX;
+ last_evm = rtlpriv->stats.ui_link_quality.elements[
+ rtlpriv->stats.ui_link_quality.index];
+ rtlpriv->stats.ui_link_quality.total_val -= last_evm;
+ }
+ rtlpriv->stats.ui_link_quality.total_val +=
+ pstats->signalquality;
+ rtlpriv->stats.ui_link_quality.elements[
+ rtlpriv->stats.ui_link_quality.index++] =
+ pstats->signalquality;
+ if (rtlpriv->stats.ui_link_quality.index >=
+ PHY_LINKQUALITY_SLID_WIN_MAX)
+ rtlpriv->stats.ui_link_quality.index = 0;
+ tmpval = rtlpriv->stats.ui_link_quality.total_val /
+ rtlpriv->stats.ui_link_quality.total_num;
+ rtlpriv->stats.signal_quality = tmpval;
+ rtlpriv->stats.last_sigstrength_inpercent = tmpval;
+ rtl92d_loop_over_streams(hw, pstats);
+ }
+}
+
+static void _rtl92de_process_phyinfo(struct ieee80211_hw *hw,
+ u8 *buffer,
+ struct rtl_stats *pcurrent_stats)
+{
+
+ if (!pcurrent_stats->packet_matchbssid &&
+ !pcurrent_stats->packet_beacon)
+ return;
+
+ _rtl92de_process_ui_rssi(hw, pcurrent_stats);
+ _rtl92de_process_pwdb(hw, pcurrent_stats);
+ _rtl92de_process_ui_link_quality(hw, pcurrent_stats);
+}
+
+static void _rtl92de_translate_rx_signal_stuff(struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct rtl_stats *pstats,
+ struct rx_desc_92d *pdesc,
+ struct rx_fwinfo_92d *p_drvinfo)
+{
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct ieee80211_hdr *hdr;
+ u8 *tmp_buf;
+ u8 *praddr;
+ u16 type, cfc;
+ __le16 fc;
+ bool packet_matchbssid, packet_toself, packet_beacon;
+
+ tmp_buf = skb->data + pstats->rx_drvinfo_size + pstats->rx_bufshift;
+ hdr = (struct ieee80211_hdr *)tmp_buf;
+ fc = hdr->frame_control;
+ cfc = le16_to_cpu(fc);
+ type = WLAN_FC_GET_TYPE(fc);
+ praddr = hdr->addr1;
+ packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) &&
+ (!compare_ether_addr(mac->bssid, (cfc & IEEE80211_FCTL_TODS) ?
+ hdr->addr1 : (cfc & IEEE80211_FCTL_FROMDS) ?
+ hdr->addr2 : hdr->addr3)) && (!pstats->hwerror) &&
+ (!pstats->crc) && (!pstats->icv));
+ packet_toself = packet_matchbssid &&
+ (!compare_ether_addr(praddr, rtlefuse->dev_addr));
+ if (ieee80211_is_beacon(fc))
+ packet_beacon = true;
+ _rtl92de_query_rxphystatus(hw, pstats, pdesc, p_drvinfo,
+ packet_matchbssid, packet_toself,
+ packet_beacon);
+ _rtl92de_process_phyinfo(hw, tmp_buf, pstats);
+}
+
+bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
+ struct ieee80211_rx_status *rx_status,
+ u8 *p_desc, struct sk_buff *skb)
+{
+ struct rx_fwinfo_92d *p_drvinfo;
+ struct rx_desc_92d *pdesc = (struct rx_desc_92d *)p_desc;
+ u32 phystatus = GET_RX_DESC_PHYST(pdesc);
+
+ stats->length = (u16) GET_RX_DESC_PKT_LEN(pdesc);
+ stats->rx_drvinfo_size = (u8) GET_RX_DESC_DRV_INFO_SIZE(pdesc) *
+ RX_DRV_INFO_SIZE_UNIT;
+ stats->rx_bufshift = (u8) (GET_RX_DESC_SHIFT(pdesc) & 0x03);
+ stats->icv = (u16) GET_RX_DESC_ICV(pdesc);
+ stats->crc = (u16) GET_RX_DESC_CRC32(pdesc);
+ stats->hwerror = (stats->crc | stats->icv);
+ stats->decrypted = !GET_RX_DESC_SWDEC(pdesc);
+ stats->rate = (u8) GET_RX_DESC_RXMCS(pdesc);
+ stats->shortpreamble = (u16) GET_RX_DESC_SPLCP(pdesc);
+ stats->isampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1);
+ stats->isfirst_ampdu = (bool) ((GET_RX_DESC_PAGGR(pdesc) == 1)
+ && (GET_RX_DESC_FAGGR(pdesc) == 1));
+ stats->timestamp_low = GET_RX_DESC_TSFL(pdesc);
+ stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc);
+ rx_status->freq = hw->conf.channel->center_freq;
+ rx_status->band = hw->conf.channel->band;
+ if (GET_RX_DESC_CRC32(pdesc))
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ if (!GET_RX_DESC_SWDEC(pdesc))
+ rx_status->flag |= RX_FLAG_DECRYPTED;
+ if (GET_RX_DESC_BW(pdesc))
+ rx_status->flag |= RX_FLAG_40MHZ;
+ if (GET_RX_DESC_RXHT(pdesc))
+ rx_status->flag |= RX_FLAG_HT;
+ rx_status->flag |= RX_FLAG_MACTIME_MPDU;
+ if (stats->decrypted)
+ rx_status->flag |= RX_FLAG_DECRYPTED;
+ rx_status->rate_idx = rtlwifi_rate_mapping(hw,
+ (bool)GET_RX_DESC_RXHT(pdesc),
+ (u8)GET_RX_DESC_RXMCS(pdesc),
+ (bool)GET_RX_DESC_PAGGR(pdesc));
+ rx_status->mactime = GET_RX_DESC_TSFL(pdesc);
+ if (phystatus) {
+ p_drvinfo = (struct rx_fwinfo_92d *)(skb->data +
+ stats->rx_bufshift);
+ _rtl92de_translate_rx_signal_stuff(hw,
+ skb, stats, pdesc,
+ p_drvinfo);
+ }
+ /*rx_status->qual = stats->signal; */
+ rx_status->signal = stats->rssi + 10;
+ /*rx_status->noise = -stats->noise; */
+ return true;
+}
+
+static void _rtl92de_insert_emcontent(struct rtl_tcb_desc *ptcb_desc,
+ u8 *virtualaddress)
+{
+ memset(virtualaddress, 0, 8);
+
+ SET_EARLYMODE_PKTNUM(virtualaddress, ptcb_desc->empkt_num);
+ SET_EARLYMODE_LEN0(virtualaddress, ptcb_desc->empkt_len[0]);
+ SET_EARLYMODE_LEN1(virtualaddress, ptcb_desc->empkt_len[1]);
+ SET_EARLYMODE_LEN2_1(virtualaddress, ptcb_desc->empkt_len[2] & 0xF);
+ SET_EARLYMODE_LEN2_2(virtualaddress, ptcb_desc->empkt_len[2] >> 4);
+ SET_EARLYMODE_LEN3(virtualaddress, ptcb_desc->empkt_len[3]);
+ SET_EARLYMODE_LEN4(virtualaddress, ptcb_desc->empkt_len[4]);
+}
+
+void rtl92de_tx_fill_desc(struct ieee80211_hw *hw,
+ struct ieee80211_hdr *hdr, u8 *pdesc_tx,
+ struct ieee80211_tx_info *info, struct sk_buff *skb,
+ u8 hw_queue, struct rtl_tcb_desc *ptcb_desc)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct ieee80211_sta *sta = info->control.sta;
+ u8 *pdesc = (u8 *) pdesc_tx;
+ u16 seq_number;
+ __le16 fc = hdr->frame_control;
+ unsigned int buf_len = 0;
+ unsigned int skb_len = skb->len;
+ u8 fw_qsel = _rtl92de_map_hwqueue_to_fwqueue(skb, hw_queue);
+ bool firstseg = ((hdr->seq_ctrl &
+ cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0);
+ bool lastseg = ((hdr->frame_control &
+ cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) == 0);
+ dma_addr_t mapping;
+ u8 bw_40 = 0;
+
+ if (mac->opmode == NL80211_IFTYPE_STATION) {
+ bw_40 = mac->bw_40;
+ } else if (mac->opmode == NL80211_IFTYPE_AP ||
+ mac->opmode == NL80211_IFTYPE_ADHOC) {
+ if (sta)
+ bw_40 = sta->ht_cap.cap &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ }
+ seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
+ rtl_get_tcb_desc(hw, info, sta, skb, ptcb_desc);
+ /* reserve 8 byte for AMPDU early mode */
+ if (rtlhal->earlymode_enable) {
+ skb_push(skb, EM_HDR_LEN);
+ memset(skb->data, 0, EM_HDR_LEN);
+ }
+ buf_len = skb->len;
+ mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ CLEAR_PCI_TX_DESC_CONTENT(pdesc, sizeof(struct tx_desc_92d));
+ if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) {
+ firstseg = true;
+ lastseg = true;
+ }
+ if (firstseg) {
+ if (rtlhal->earlymode_enable) {
+ SET_TX_DESC_PKT_OFFSET(pdesc, 1);
+ SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN +
+ EM_HDR_LEN);
+ if (ptcb_desc->empkt_num) {
+ RT_TRACE(rtlpriv, COMP_SEND, DBG_LOUD,
+ ("Insert 8 byte.pTcb->EMPktNum:%d\n",
+ ptcb_desc->empkt_num));
+ _rtl92de_insert_emcontent(ptcb_desc,
+ (u8 *)(skb->data));
+ }
+ } else {
+ SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN);
+ }
+ /* 5G have no CCK rate */
+ if (rtlhal->current_bandtype == BAND_ON_5G)
+ if (ptcb_desc->hw_rate < DESC92_RATE6M)
+ ptcb_desc->hw_rate = DESC92_RATE6M;
+ SET_TX_DESC_TX_RATE(pdesc, ptcb_desc->hw_rate);
+ if (ptcb_desc->use_shortgi || ptcb_desc->use_shortpreamble)
+ SET_TX_DESC_DATA_SHORTGI(pdesc, 1);
+
+ if (rtlhal->macphymode == DUALMAC_DUALPHY &&
+ ptcb_desc->hw_rate == DESC92_RATEMCS7)
+ SET_TX_DESC_DATA_SHORTGI(pdesc, 1);
+
+ if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+ SET_TX_DESC_AGG_ENABLE(pdesc, 1);
+ SET_TX_DESC_MAX_AGG_NUM(pdesc, 0x14);
+ }
+ SET_TX_DESC_SEQ(pdesc, seq_number);
+ SET_TX_DESC_RTS_ENABLE(pdesc, ((ptcb_desc->rts_enable &&
+ !ptcb_desc->cts_enable) ? 1 : 0));
+ SET_TX_DESC_HW_RTS_ENABLE(pdesc, ((ptcb_desc->rts_enable
+ || ptcb_desc->cts_enable) ? 1 : 0));
+ SET_TX_DESC_CTS2SELF(pdesc, ((ptcb_desc->cts_enable) ? 1 : 0));
+ SET_TX_DESC_RTS_STBC(pdesc, ((ptcb_desc->rts_stbc) ? 1 : 0));
+ /* 5G have no CCK rate */
+ if (rtlhal->current_bandtype == BAND_ON_5G)
+ if (ptcb_desc->rts_rate < DESC92_RATE6M)
+ ptcb_desc->rts_rate = DESC92_RATE6M;
+ SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate);
+ SET_TX_DESC_RTS_BW(pdesc, 0);
+ SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc);
+ SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <=
+ DESC92_RATE54M) ?
+ (ptcb_desc->rts_use_shortpreamble ? 1 : 0) :
+ (ptcb_desc->rts_use_shortgi ? 1 : 0)));
+ if (bw_40) {
+ if (ptcb_desc->packet_bw) {
+ SET_TX_DESC_DATA_BW(pdesc, 1);
+ SET_TX_DESC_TX_SUB_CARRIER(pdesc, 3);
+ } else {
+ SET_TX_DESC_DATA_BW(pdesc, 0);
+ SET_TX_DESC_TX_SUB_CARRIER(pdesc,
+ mac->cur_40_prime_sc);
+ }
+ } else {
+ SET_TX_DESC_DATA_BW(pdesc, 0);
+ SET_TX_DESC_TX_SUB_CARRIER(pdesc, 0);
+ }
+ SET_TX_DESC_LINIP(pdesc, 0);
+ SET_TX_DESC_PKT_SIZE(pdesc, (u16) skb_len);
+ if (sta) {
+ u8 ampdu_density = sta->ht_cap.ampdu_density;
+ SET_TX_DESC_AMPDU_DENSITY(pdesc, ampdu_density);
+ }
+ if (info->control.hw_key) {
+ struct ieee80211_key_conf *keyconf;
+
+ keyconf = info->control.hw_key;
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ case WLAN_CIPHER_SUITE_TKIP:
+ SET_TX_DESC_SEC_TYPE(pdesc, 0x1);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ SET_TX_DESC_SEC_TYPE(pdesc, 0x3);
+ break;
+ default:
+ SET_TX_DESC_SEC_TYPE(pdesc, 0x0);
+ break;
+
+ }
+ }
+ SET_TX_DESC_PKT_ID(pdesc, 0);
+ SET_TX_DESC_QUEUE_SEL(pdesc, fw_qsel);
+ SET_TX_DESC_DATA_RATE_FB_LIMIT(pdesc, 0x1F);
+ SET_TX_DESC_RTS_RATE_FB_LIMIT(pdesc, 0xF);
+ SET_TX_DESC_DISABLE_FB(pdesc, ptcb_desc->disable_ratefallback ?
+ 1 : 0);
+ SET_TX_DESC_USE_RATE(pdesc, ptcb_desc->use_driver_rate ? 1 : 0);
+
+ /* Set TxRate and RTSRate in TxDesc */
+ /* This prevent Tx initial rate of new-coming packets */
+ /* from being overwritten by retried packet rate.*/
+ if (!ptcb_desc->use_driver_rate) {
+ SET_TX_DESC_RTS_RATE(pdesc, 0x08);
+ /* SET_TX_DESC_TX_RATE(pdesc, 0x0b); */
+ }
+ if (ieee80211_is_data_qos(fc)) {
+ if (mac->rdg_en) {
+ RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+ ("Enable RDG function.\n"));
+ SET_TX_DESC_RDG_ENABLE(pdesc, 1);
+ SET_TX_DESC_HTC(pdesc, 1);
+ }
+ }
+ }
+
+ SET_TX_DESC_FIRST_SEG(pdesc, (firstseg ? 1 : 0));
+ SET_TX_DESC_LAST_SEG(pdesc, (lastseg ? 1 : 0));
+ SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) buf_len);
+ SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping);
+ if (rtlpriv->dm.useramask) {
+ SET_TX_DESC_RATE_ID(pdesc, ptcb_desc->ratr_index);
+ SET_TX_DESC_MACID(pdesc, ptcb_desc->mac_id);
+ } else {
+ SET_TX_DESC_RATE_ID(pdesc, 0xC + ptcb_desc->ratr_index);
+ SET_TX_DESC_MACID(pdesc, ptcb_desc->ratr_index);
+ }
+ if (ieee80211_is_data_qos(fc))
+ SET_TX_DESC_QOS(pdesc, 1);
+
+ if ((!ieee80211_is_data_qos(fc)) && ppsc->fwctrl_lps) {
+ SET_TX_DESC_HWSEQ_EN(pdesc, 1);
+ SET_TX_DESC_PKT_ID(pdesc, 8);
+ }
+ SET_TX_DESC_MORE_FRAG(pdesc, (lastseg ? 0 : 1));
+ RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, ("\n"));
+}
+
+void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw,
+ u8 *pdesc, bool firstseg,
+ bool lastseg, struct sk_buff *skb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtlpriv);
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u8 fw_queue = QSLT_BEACON;
+ dma_addr_t mapping = pci_map_single(rtlpci->pdev,
+ skb->data, skb->len, PCI_DMA_TODEVICE);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data);
+ __le16 fc = hdr->frame_control;
+
+ CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE);
+ if (firstseg)
+ SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN);
+ /* 5G have no CCK rate
+ * Caution: The macros below are multi-line expansions.
+ * The braces are needed no matter what checkpatch says
+ */
+ if (rtlhal->current_bandtype == BAND_ON_5G) {
+ SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE6M);
+ } else {
+ SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M);
+ }
+ SET_TX_DESC_SEQ(pdesc, 0);
+ SET_TX_DESC_LINIP(pdesc, 0);
+ SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue);
+ SET_TX_DESC_FIRST_SEG(pdesc, 1);
+ SET_TX_DESC_LAST_SEG(pdesc, 1);
+ SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) (skb->len));
+ SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping);
+ SET_TX_DESC_RATE_ID(pdesc, 7);
+ SET_TX_DESC_MACID(pdesc, 0);
+ SET_TX_DESC_PKT_SIZE((u8 *) pdesc, (u16) (skb->len));
+ SET_TX_DESC_FIRST_SEG(pdesc, 1);
+ SET_TX_DESC_LAST_SEG(pdesc, 1);
+ SET_TX_DESC_OFFSET(pdesc, 0x20);
+ SET_TX_DESC_USE_RATE(pdesc, 1);
+
+ if (!ieee80211_is_data_qos(fc) && ppsc->fwctrl_lps) {
+ SET_TX_DESC_HWSEQ_EN(pdesc, 1);
+ SET_TX_DESC_PKT_ID(pdesc, 8);
+ }
+
+ RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD,
+ "H2C Tx Cmd Content\n", pdesc, TX_DESC_SIZE);
+ wmb();
+ SET_TX_DESC_OWN(pdesc, 1);
+}
+
+void rtl92de_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
+{
+ if (istx) {
+ switch (desc_name) {
+ case HW_DESC_OWN:
+ wmb();
+ SET_TX_DESC_OWN(pdesc, 1);
+ break;
+ case HW_DESC_TX_NEXTDESC_ADDR:
+ SET_TX_DESC_NEXT_DESC_ADDRESS(pdesc, *(u32 *) val);
+ break;
+ default:
+ RT_ASSERT(false, ("ERR txdesc :%d"
+ " not process\n", desc_name));
+ break;
+ }
+ } else {
+ switch (desc_name) {
+ case HW_DESC_RXOWN:
+ wmb();
+ SET_RX_DESC_OWN(pdesc, 1);
+ break;
+ case HW_DESC_RXBUFF_ADDR:
+ SET_RX_DESC_BUFF_ADDR(pdesc, *(u32 *) val);
+ break;
+ case HW_DESC_RXPKT_LEN:
+ SET_RX_DESC_PKT_LEN(pdesc, *(u32 *) val);
+ break;
+ case HW_DESC_RXERO:
+ SET_RX_DESC_EOR(pdesc, 1);
+ break;
+ default:
+ RT_ASSERT(false, ("ERR rxdesc :%d "
+ "not process\n", desc_name));
+ break;
+ }
+ }
+}
+
+u32 rtl92de_get_desc(u8 *p_desc, bool istx, u8 desc_name)
+{
+ u32 ret = 0;
+
+ if (istx) {
+ switch (desc_name) {
+ case HW_DESC_OWN:
+ ret = GET_TX_DESC_OWN(p_desc);
+ break;
+ case HW_DESC_TXBUFF_ADDR:
+ ret = GET_TX_DESC_TX_BUFFER_ADDRESS(p_desc);
+ break;
+ default:
+ RT_ASSERT(false, ("ERR txdesc :%d "
+ "not process\n", desc_name));
+ break;
+ }
+ } else {
+ struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc;
+ switch (desc_name) {
+ case HW_DESC_OWN:
+ ret = GET_RX_DESC_OWN(pdesc);
+ break;
+ case HW_DESC_RXPKT_LEN:
+ ret = GET_RX_DESC_PKT_LEN(pdesc);
+ break;
+ default:
+ RT_ASSERT(false, ("ERR rxdesc :%d "
+ "not process\n", desc_name));
+ break;
+ }
+ }
+ return ret;
+}
+
+void rtl92de_tx_polling(struct ieee80211_hw *hw, u8 hw_queue)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ if (hw_queue == BEACON_QUEUE)
+ rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, BIT(4));
+ else
+ rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG,
+ BIT(0) << (hw_queue));
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h
new file mode 100644
index 000000000000..4d55d0b6816d
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h
@@ -0,0 +1,750 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92DE_TRX_H__
+#define __RTL92DE_TRX_H__
+
+#define TX_DESC_SIZE 64
+#define TX_DESC_AGGR_SUBFRAME_SIZE 32
+
+#define RX_DESC_SIZE 32
+#define RX_DRV_INFO_SIZE_UNIT 8
+
+#define TX_DESC_NEXT_DESC_OFFSET 40
+#define USB_HWDESC_HEADER_LEN 32
+#define CRCLENGTH 4
+
+/* Define a macro that takes a le32 word, converts it to host ordering,
+ * right shifts by a specified count, creates a mask of the specified
+ * bit count, and extracts that number of bits.
+ */
+
+#define SHIFT_AND_MASK_LE(__pdesc, __shift, __mask) \
+ ((le32_to_cpu(*(((__le32 *)(__pdesc)))) >> (__shift)) & \
+ BIT_LEN_MASK_32(__mask))
+
+/* Define a macro that clears a bit field in an le32 word and
+ * sets the specified value into that bit field. The resulting
+ * value remains in le32 ordering; however, it is properly converted
+ * to host ordering for the clear and set operations before conversion
+ * back to le32.
+ */
+
+#define SET_BITS_OFFSET_LE(__pdesc, __shift, __len, __val) \
+ (*(__le32 *)(__pdesc) = \
+ (cpu_to_le32((le32_to_cpu(*((__le32 *)(__pdesc))) & \
+ (~(BIT_OFFSET_LEN_MASK_32((__shift), __len)))) | \
+ (((u32)(__val) & BIT_LEN_MASK_32(__len)) << (__shift)))));
+
+/* macros to read/write various fields in RX or TX descriptors */
+
+#define SET_TX_DESC_PKT_SIZE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 0, 16, __val)
+#define SET_TX_DESC_OFFSET(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 16, 8, __val)
+#define SET_TX_DESC_BMC(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 24, 1, __val)
+#define SET_TX_DESC_HTC(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 25, 1, __val)
+#define SET_TX_DESC_LAST_SEG(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 26, 1, __val)
+#define SET_TX_DESC_FIRST_SEG(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 27, 1, __val)
+#define SET_TX_DESC_LINIP(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 28, 1, __val)
+#define SET_TX_DESC_NO_ACM(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 29, 1, __val)
+#define SET_TX_DESC_GF(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 30, 1, __val)
+#define SET_TX_DESC_OWN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val)
+
+#define GET_TX_DESC_PKT_SIZE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 0, 16)
+#define GET_TX_DESC_OFFSET(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 16, 8)
+#define GET_TX_DESC_BMC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 24, 1)
+#define GET_TX_DESC_HTC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 25, 1)
+#define GET_TX_DESC_LAST_SEG(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 26, 1)
+#define GET_TX_DESC_FIRST_SEG(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 27, 1)
+#define GET_TX_DESC_LINIP(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 28, 1)
+#define GET_TX_DESC_NO_ACM(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 29, 1)
+#define GET_TX_DESC_GF(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 30, 1)
+#define GET_TX_DESC_OWN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 31, 1)
+
+#define SET_TX_DESC_MACID(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 0, 5, __val)
+#define SET_TX_DESC_AGG_ENABLE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 5, 1, __val)
+#define SET_TX_DESC_BK(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 6, 1, __val)
+#define SET_TX_DESC_RDG_ENABLE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 7, 1, __val)
+#define SET_TX_DESC_QUEUE_SEL(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 8, 5, __val)
+#define SET_TX_DESC_RDG_NAV_EXT(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 13, 1, __val)
+#define SET_TX_DESC_LSIG_TXOP_EN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 14, 1, __val)
+#define SET_TX_DESC_PIFS(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 15, 1, __val)
+#define SET_TX_DESC_RATE_ID(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 16, 4, __val)
+#define SET_TX_DESC_NAV_USE_HDR(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 20, 1, __val)
+#define SET_TX_DESC_EN_DESC_ID(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 21, 1, __val)
+#define SET_TX_DESC_SEC_TYPE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 22, 2, __val)
+#define SET_TX_DESC_PKT_OFFSET(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+4, 26, 8, __val)
+
+#define GET_TX_DESC_MACID(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 0, 5)
+#define GET_TX_DESC_AGG_ENABLE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 5, 1)
+#define GET_TX_DESC_AGG_BREAK(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 6, 1)
+#define GET_TX_DESC_RDG_ENABLE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 7, 1)
+#define GET_TX_DESC_QUEUE_SEL(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 8, 5)
+#define GET_TX_DESC_RDG_NAV_EXT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 13, 1)
+#define GET_TX_DESC_LSIG_TXOP_EN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 14, 1)
+#define GET_TX_DESC_PIFS(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 15, 1)
+#define GET_TX_DESC_RATE_ID(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 16, 4)
+#define GET_TX_DESC_NAV_USE_HDR(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 20, 1)
+#define GET_TX_DESC_EN_DESC_ID(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 21, 1)
+#define GET_TX_DESC_SEC_TYPE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 22, 2)
+#define GET_TX_DESC_PKT_OFFSET(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 24, 8)
+
+#define SET_TX_DESC_RTS_RC(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 0, 6, __val)
+#define SET_TX_DESC_DATA_RC(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 6, 6, __val)
+#define SET_TX_DESC_BAR_RTY_TH(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 14, 2, __val)
+#define SET_TX_DESC_MORE_FRAG(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 17, 1, __val)
+#define SET_TX_DESC_RAW(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 18, 1, __val)
+#define SET_TX_DESC_CCX(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 19, 1, __val)
+#define SET_TX_DESC_AMPDU_DENSITY(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 20, 3, __val)
+#define SET_TX_DESC_ANTSEL_A(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 24, 1, __val)
+#define SET_TX_DESC_ANTSEL_B(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 25, 1, __val)
+#define SET_TX_DESC_TX_ANT_CCK(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 26, 2, __val)
+#define SET_TX_DESC_TX_ANTL(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 28, 2, __val)
+#define SET_TX_DESC_TX_ANT_HT(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+8, 30, 2, __val)
+
+#define GET_TX_DESC_RTS_RC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 0, 6)
+#define GET_TX_DESC_DATA_RC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 6, 6)
+#define GET_TX_DESC_BAR_RTY_TH(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 14, 2)
+#define GET_TX_DESC_MORE_FRAG(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 17, 1)
+#define GET_TX_DESC_RAW(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 18, 1)
+#define GET_TX_DESC_CCX(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 19, 1)
+#define GET_TX_DESC_AMPDU_DENSITY(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 20, 3)
+#define GET_TX_DESC_ANTSEL_A(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 24, 1)
+#define GET_TX_DESC_ANTSEL_B(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 25, 1)
+#define GET_TX_DESC_TX_ANT_CCK(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 26, 2)
+#define GET_TX_DESC_TX_ANTL(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 28, 2)
+#define GET_TX_DESC_TX_ANT_HT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 30, 2)
+
+#define SET_TX_DESC_NEXT_HEAP_PAGE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+12, 0, 8, __val)
+#define SET_TX_DESC_TAIL_PAGE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+12, 8, 8, __val)
+#define SET_TX_DESC_SEQ(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+12, 16, 12, __val)
+#define SET_TX_DESC_PKT_ID(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+12, 28, 4, __val)
+
+#define GET_TX_DESC_NEXT_HEAP_PAGE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 0, 8)
+#define GET_TX_DESC_TAIL_PAGE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 8, 8)
+#define GET_TX_DESC_SEQ(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 16, 12)
+#define GET_TX_DESC_PKT_ID(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 28, 4)
+
+#define SET_TX_DESC_RTS_RATE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 0, 5, __val)
+#define SET_TX_DESC_AP_DCFE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 5, 1, __val)
+#define SET_TX_DESC_QOS(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 6, 1, __val)
+#define SET_TX_DESC_HWSEQ_EN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 7, 1, __val)
+#define SET_TX_DESC_USE_RATE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 8, 1, __val)
+#define SET_TX_DESC_DISABLE_RTS_FB(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 9, 1, __val)
+#define SET_TX_DESC_DISABLE_FB(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 10, 1, __val)
+#define SET_TX_DESC_CTS2SELF(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 11, 1, __val)
+#define SET_TX_DESC_RTS_ENABLE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 12, 1, __val)
+#define SET_TX_DESC_HW_RTS_ENABLE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 13, 1, __val)
+#define SET_TX_DESC_PORT_ID(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 14, 1, __val)
+#define SET_TX_DESC_WAIT_DCTS(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 18, 1, __val)
+#define SET_TX_DESC_CTS2AP_EN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 19, 1, __val)
+#define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 20, 2, __val)
+#define SET_TX_DESC_TX_STBC(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 22, 2, __val)
+#define SET_TX_DESC_DATA_SHORT(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 24, 1, __val)
+#define SET_TX_DESC_DATA_BW(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 25, 1, __val)
+#define SET_TX_DESC_RTS_SHORT(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 26, 1, __val)
+#define SET_TX_DESC_RTS_BW(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 27, 1, __val)
+#define SET_TX_DESC_RTS_SC(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 28, 2, __val)
+#define SET_TX_DESC_RTS_STBC(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+16, 30, 2, __val)
+
+#define GET_TX_DESC_RTS_RATE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 0, 5)
+#define GET_TX_DESC_AP_DCFE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 5, 1)
+#define GET_TX_DESC_QOS(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 6, 1)
+#define GET_TX_DESC_HWSEQ_EN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 7, 1)
+#define GET_TX_DESC_USE_RATE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 8, 1)
+#define GET_TX_DESC_DISABLE_RTS_FB(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 9, 1)
+#define GET_TX_DESC_DISABLE_FB(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 10, 1)
+#define GET_TX_DESC_CTS2SELF(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 11, 1)
+#define GET_TX_DESC_RTS_ENABLE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 12, 1)
+#define GET_TX_DESC_HW_RTS_ENABLE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 13, 1)
+#define GET_TX_DESC_PORT_ID(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 14, 1)
+#define GET_TX_DESC_WAIT_DCTS(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 18, 1)
+#define GET_TX_DESC_CTS2AP_EN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 19, 1)
+#define GET_TX_DESC_TX_SUB_CARRIER(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 20, 2)
+#define GET_TX_DESC_TX_STBC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 22, 2)
+#define GET_TX_DESC_DATA_SHORT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 24, 1)
+#define GET_TX_DESC_DATA_BW(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 25, 1)
+#define GET_TX_DESC_RTS_SHORT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 26, 1)
+#define GET_TX_DESC_RTS_BW(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 27, 1)
+#define GET_TX_DESC_RTS_SC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 28, 2)
+#define GET_TX_DESC_RTS_STBC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 30, 2)
+
+#define SET_TX_DESC_TX_RATE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+20, 0, 6, __val)
+#define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+20, 6, 1, __val)
+#define SET_TX_DESC_CCX_TAG(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+20, 7, 1, __val)
+#define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+20, 8, 5, __val)
+#define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+20, 13, 4, __val)
+#define SET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+20, 17, 1, __val)
+#define SET_TX_DESC_DATA_RETRY_LIMIT(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+20, 18, 6, __val)
+#define SET_TX_DESC_USB_TXAGG_NUM(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+20, 24, 8, __val)
+
+#define GET_TX_DESC_TX_RATE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+20, 0, 6)
+#define GET_TX_DESC_DATA_SHORTGI(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+20, 6, 1)
+#define GET_TX_DESC_CCX_TAG(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+20, 7, 1)
+#define GET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+20, 8, 5)
+#define GET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+20, 13, 4)
+#define GET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+20, 17, 1)
+#define GET_TX_DESC_DATA_RETRY_LIMIT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+20, 18, 6)
+#define GET_TX_DESC_USB_TXAGG_NUM(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+20, 24, 8)
+
+#define SET_TX_DESC_TXAGC_A(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+24, 0, 5, __val)
+#define SET_TX_DESC_TXAGC_B(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+24, 5, 5, __val)
+#define SET_TX_DESC_USE_MAX_LEN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+24, 10, 1, __val)
+#define SET_TX_DESC_MAX_AGG_NUM(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+24, 11, 5, __val)
+#define SET_TX_DESC_MCSG1_MAX_LEN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+24, 16, 4, __val)
+#define SET_TX_DESC_MCSG2_MAX_LEN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+24, 20, 4, __val)
+#define SET_TX_DESC_MCSG3_MAX_LEN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+24, 24, 4, __val)
+#define SET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+24, 28, 4, __val)
+
+#define GET_TX_DESC_TXAGC_A(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+24, 0, 5)
+#define GET_TX_DESC_TXAGC_B(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+24, 5, 5)
+#define GET_TX_DESC_USE_MAX_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+24, 10, 1)
+#define GET_TX_DESC_MAX_AGG_NUM(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+24, 11, 5)
+#define GET_TX_DESC_MCSG1_MAX_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+24, 16, 4)
+#define GET_TX_DESC_MCSG2_MAX_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+24, 20, 4)
+#define GET_TX_DESC_MCSG3_MAX_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+24, 24, 4)
+#define GET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+24, 28, 4)
+
+#define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+28, 0, 16, __val)
+#define SET_TX_DESC_MCSG4_MAX_LEN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+28, 16, 4, __val)
+#define SET_TX_DESC_MCSG5_MAX_LEN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+28, 20, 4, __val)
+#define SET_TX_DESC_MCSG6_MAX_LEN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+28, 24, 4, __val)
+#define SET_TX_DESC_MCS15_SGI_MAX_LEN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+28, 28, 4, __val)
+
+#define GET_TX_DESC_TX_BUFFER_SIZE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+28, 0, 16)
+#define GET_TX_DESC_MCSG4_MAX_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+28, 16, 4)
+#define GET_TX_DESC_MCSG5_MAX_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+28, 20, 4)
+#define GET_TX_DESC_MCSG6_MAX_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+28, 24, 4)
+#define GET_TX_DESC_MCS15_SGI_MAX_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+28, 28, 4)
+
+#define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+32, 0, 32, __val)
+#define SET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+36, 0, 32, __val)
+
+#define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+32, 0, 32)
+#define GET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+36, 0, 32)
+
+#define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+40, 0, 32, __val)
+#define SET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+44, 0, 32, __val)
+
+#define GET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+40, 0, 32)
+#define GET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+44, 0, 32)
+
+#define GET_RX_DESC_PKT_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 0, 14)
+#define GET_RX_DESC_CRC32(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 14, 1)
+#define GET_RX_DESC_ICV(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 15, 1)
+#define GET_RX_DESC_DRV_INFO_SIZE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 16, 4)
+#define GET_RX_DESC_SECURITY(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 20, 3)
+#define GET_RX_DESC_QOS(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 23, 1)
+#define GET_RX_DESC_SHIFT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 24, 2)
+#define GET_RX_DESC_PHYST(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 26, 1)
+#define GET_RX_DESC_SWDEC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 27, 1)
+#define GET_RX_DESC_LS(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 28, 1)
+#define GET_RX_DESC_FS(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 29, 1)
+#define GET_RX_DESC_EOR(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 30, 1)
+#define GET_RX_DESC_OWN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc, 31, 1)
+
+#define SET_RX_DESC_PKT_LEN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 0, 14, __val)
+#define SET_RX_DESC_EOR(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 30, 1, __val)
+#define SET_RX_DESC_OWN(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val)
+
+#define GET_RX_DESC_MACID(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 0, 5)
+#define GET_RX_DESC_TID(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 5, 4)
+#define GET_RX_DESC_HWRSVD(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 9, 5)
+#define GET_RX_DESC_PAGGR(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 14, 1)
+#define GET_RX_DESC_FAGGR(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 15, 1)
+#define GET_RX_DESC_A1_FIT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 16, 4)
+#define GET_RX_DESC_A2_FIT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 20, 4)
+#define GET_RX_DESC_PAM(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 24, 1)
+#define GET_RX_DESC_PWR(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 25, 1)
+#define GET_RX_DESC_MD(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 26, 1)
+#define GET_RX_DESC_MF(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 27, 1)
+#define GET_RX_DESC_TYPE(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 28, 2)
+#define GET_RX_DESC_MC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 30, 1)
+#define GET_RX_DESC_BC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+4, 31, 1)
+#define GET_RX_DESC_SEQ(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 0, 12)
+#define GET_RX_DESC_FRAG(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 12, 4)
+#define GET_RX_DESC_NEXT_PKT_LEN(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 16, 14)
+#define GET_RX_DESC_NEXT_IND(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 30, 1)
+#define GET_RX_DESC_RSVD(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+8, 31, 1)
+
+#define GET_RX_DESC_RXMCS(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 0, 6)
+#define GET_RX_DESC_RXHT(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 6, 1)
+#define GET_RX_DESC_SPLCP(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 8, 1)
+#define GET_RX_DESC_BW(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 9, 1)
+#define GET_RX_DESC_HTC(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 10, 1)
+#define GET_RX_DESC_HWPC_ERR(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 14, 1)
+#define GET_RX_DESC_HWPC_IND(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 15, 1)
+#define GET_RX_DESC_IV0(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+12, 16, 16)
+
+#define GET_RX_DESC_IV1(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+16, 0, 32)
+#define GET_RX_DESC_TSFL(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+20, 0, 32)
+
+#define GET_RX_DESC_BUFF_ADDR(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+24, 0, 32)
+#define GET_RX_DESC_BUFF_ADDR64(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc+28, 0, 32)
+
+#define SET_RX_DESC_BUFF_ADDR(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+24, 0, 32, __val)
+#define SET_RX_DESC_BUFF_ADDR64(__pdesc, __val) \
+ SET_BITS_OFFSET_LE(__pdesc+28, 0, 32, __val)
+
+#define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \
+do { \
+ if (_size > TX_DESC_NEXT_DESC_OFFSET) \
+ memset((void *)__pdesc, 0, TX_DESC_NEXT_DESC_OFFSET); \
+ else \
+ memset((void *)__pdesc, 0, _size); \
+} while (0);
+
+/* For 92D early mode */
+#define SET_EARLYMODE_PKTNUM(__paddr, __value) \
+ SET_BITS_OFFSET_LE(__paddr, 0, 3, __value)
+#define SET_EARLYMODE_LEN0(__paddr, __value) \
+ SET_BITS_OFFSET_LE(__paddr, 4, 12, __value)
+#define SET_EARLYMODE_LEN1(__paddr, __value) \
+ SET_BITS_OFFSET_LE(__paddr, 16, 12, __value)
+#define SET_EARLYMODE_LEN2_1(__paddr, __value) \
+ SET_BITS_OFFSET_LE(__paddr, 28, 4, __value)
+#define SET_EARLYMODE_LEN2_2(__paddr, __value) \
+ SET_BITS_OFFSET_LE(__paddr+4, 0, 8, __value)
+#define SET_EARLYMODE_LEN3(__paddr, __value) \
+ SET_BITS_OFFSET_LE(__paddr+4, 8, 12, __value)
+#define SET_EARLYMODE_LEN4(__paddr, __value) \
+ SET_BITS_OFFSET_LE(__paddr+4, 20, 12, __value)
+
+struct rx_fwinfo_92d {
+ u8 gain_trsw[4];
+ u8 pwdb_all;
+ u8 cfosho[4];
+ u8 cfotail[4];
+ char rxevm[2];
+ char rxsnr[4];
+ u8 pdsnr[2];
+ u8 csi_current[2];
+ u8 csi_target[2];
+ u8 sigevm;
+ u8 max_ex_pwr;
+ u8 ex_intf_flag:1;
+ u8 sgi_en:1;
+ u8 rxsc:2;
+ u8 reserve:4;
+} __packed;
+
+struct tx_desc_92d {
+ u32 pktsize:16;
+ u32 offset:8;
+ u32 bmc:1;
+ u32 htc:1;
+ u32 lastseg:1;
+ u32 firstseg:1;
+ u32 linip:1;
+ u32 noacm:1;
+ u32 gf:1;
+ u32 own:1;
+
+ u32 macid:5;
+ u32 agg_en:1;
+ u32 bk:1;
+ u32 rdg_en:1;
+ u32 queuesel:5;
+ u32 rd_nav_ext:1;
+ u32 lsig_txop_en:1;
+ u32 pifs:1;
+ u32 rateid:4;
+ u32 nav_usehdr:1;
+ u32 en_descid:1;
+ u32 sectype:2;
+ u32 pktoffset:8;
+
+ u32 rts_rc:6;
+ u32 data_rc:6;
+ u32 rsvd0:2;
+ u32 bar_retryht:2;
+ u32 rsvd1:1;
+ u32 morefrag:1;
+ u32 raw:1;
+ u32 ccx:1;
+ u32 ampdudensity:3;
+ u32 rsvd2:1;
+ u32 ant_sela:1;
+ u32 ant_selb:1;
+ u32 txant_cck:2;
+ u32 txant_l:2;
+ u32 txant_ht:2;
+
+ u32 nextheadpage:8;
+ u32 tailpage:8;
+ u32 seq:12;
+ u32 pktid:4;
+
+ u32 rtsrate:5;
+ u32 apdcfe:1;
+ u32 qos:1;
+ u32 hwseq_enable:1;
+ u32 userrate:1;
+ u32 dis_rtsfb:1;
+ u32 dis_datafb:1;
+ u32 cts2self:1;
+ u32 rts_en:1;
+ u32 hwrts_en:1;
+ u32 portid:1;
+ u32 rsvd3:3;
+ u32 waitdcts:1;
+ u32 cts2ap_en:1;
+ u32 txsc:2;
+ u32 stbc:2;
+ u32 txshort:1;
+ u32 txbw:1;
+ u32 rtsshort:1;
+ u32 rtsbw:1;
+ u32 rtssc:2;
+ u32 rtsstbc:2;
+
+ u32 txrate:6;
+ u32 shortgi:1;
+ u32 ccxt:1;
+ u32 txrate_fb_lmt:5;
+ u32 rtsrate_fb_lmt:4;
+ u32 retrylmt_en:1;
+ u32 txretrylmt:6;
+ u32 usb_txaggnum:8;
+
+ u32 txagca:5;
+ u32 txagcb:5;
+ u32 usemaxlen:1;
+ u32 maxaggnum:5;
+ u32 mcsg1maxlen:4;
+ u32 mcsg2maxlen:4;
+ u32 mcsg3maxlen:4;
+ u32 mcs7sgimaxlen:4;
+
+ u32 txbuffersize:16;
+ u32 mcsg4maxlen:4;
+ u32 mcsg5maxlen:4;
+ u32 mcsg6maxlen:4;
+ u32 mcsg15sgimaxlen:4;
+
+ u32 txbuffaddr;
+ u32 txbufferaddr64;
+ u32 nextdescaddress;
+ u32 nextdescaddress64;
+
+ u32 reserve_pass_pcie_mm_limit[4];
+} __packed;
+
+struct rx_desc_92d {
+ u32 length:14;
+ u32 crc32:1;
+ u32 icverror:1;
+ u32 drv_infosize:4;
+ u32 security:3;
+ u32 qos:1;
+ u32 shift:2;
+ u32 phystatus:1;
+ u32 swdec:1;
+ u32 lastseg:1;
+ u32 firstseg:1;
+ u32 eor:1;
+ u32 own:1;
+
+ u32 macid:5;
+ u32 tid:4;
+ u32 hwrsvd:5;
+ u32 paggr:1;
+ u32 faggr:1;
+ u32 a1_fit:4;
+ u32 a2_fit:4;
+ u32 pam:1;
+ u32 pwr:1;
+ u32 moredata:1;
+ u32 morefrag:1;
+ u32 type:2;
+ u32 mc:1;
+ u32 bc:1;
+
+ u32 seq:12;
+ u32 frag:4;
+ u32 nextpktlen:14;
+ u32 nextind:1;
+ u32 rsvd:1;
+
+ u32 rxmcs:6;
+ u32 rxht:1;
+ u32 amsdu:1;
+ u32 splcp:1;
+ u32 bandwidth:1;
+ u32 htc:1;
+ u32 tcpchk_rpt:1;
+ u32 ipcchk_rpt:1;
+ u32 tcpchk_valid:1;
+ u32 hwpcerr:1;
+ u32 hwpcind:1;
+ u32 iv0:16;
+
+ u32 iv1;
+
+ u32 tsfl;
+
+ u32 bufferaddress;
+ u32 bufferaddress64;
+
+} __packed;
+
+void rtl92de_tx_fill_desc(struct ieee80211_hw *hw,
+ struct ieee80211_hdr *hdr,
+ u8 *pdesc, struct ieee80211_tx_info *info,
+ struct sk_buff *skb, u8 hw_queue,
+ struct rtl_tcb_desc *ptcb_desc);
+bool rtl92de_rx_query_desc(struct ieee80211_hw *hw,
+ struct rtl_stats *stats,
+ struct ieee80211_rx_status *rx_status,
+ u8 *pdesc, struct sk_buff *skb);
+void rtl92de_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val);
+u32 rtl92de_get_desc(u8 *pdesc, bool istx, u8 desc_name);
+void rtl92de_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
+void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
+ bool b_firstseg, bool b_lastseg,
+ struct sk_buff *skb);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/def.h b/drivers/net/wireless/rtlwifi/rtl8192se/def.h
index 69828f2b3fab..c6c044816d39 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/def.h
@@ -33,37 +33,6 @@
#define RX_CMD_QUEUE 1
#define RX_MAX_QUEUE 2
-#define DESC92S_RATE1M 0x00
-#define DESC92S_RATE2M 0x01
-#define DESC92S_RATE5_5M 0x02
-#define DESC92S_RATE11M 0x03
-#define DESC92S_RATE6M 0x04
-#define DESC92S_RATE9M 0x05
-#define DESC92S_RATE12M 0x06
-#define DESC92S_RATE18M 0x07
-#define DESC92S_RATE24M 0x08
-#define DESC92S_RATE36M 0x09
-#define DESC92S_RATE48M 0x0a
-#define DESC92S_RATE54M 0x0b
-#define DESC92S_RATEMCS0 0x0c
-#define DESC92S_RATEMCS1 0x0d
-#define DESC92S_RATEMCS2 0x0e
-#define DESC92S_RATEMCS3 0x0f
-#define DESC92S_RATEMCS4 0x10
-#define DESC92S_RATEMCS5 0x11
-#define DESC92S_RATEMCS6 0x12
-#define DESC92S_RATEMCS7 0x13
-#define DESC92S_RATEMCS8 0x14
-#define DESC92S_RATEMCS9 0x15
-#define DESC92S_RATEMCS10 0x16
-#define DESC92S_RATEMCS11 0x17
-#define DESC92S_RATEMCS12 0x18
-#define DESC92S_RATEMCS13 0x19
-#define DESC92S_RATEMCS14 0x1a
-#define DESC92S_RATEMCS15 0x1b
-#define DESC92S_RATEMCS15_SG 0x1c
-#define DESC92S_RATEMCS32 0x20
-
#define SHORT_SLOT_TIME 9
#define NON_SHORT_SLOT_TIME 20
@@ -490,11 +459,11 @@ do { \
#define SET_RX_STATUS__DESC_BUFF_ADDR(__pdesc, __val) \
SET_BITS_OFFSET_LE(__pdesc + 24, 0, 32, __val)
-#define RX_HAL_IS_CCK_RATE(_pdesc)\
- (GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92S_RATE1M || \
- GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92S_RATE2M || \
- GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92S_RATE5_5M ||\
- GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92S_RATE11M)
+#define SE_RX_HAL_IS_CCK_RATE(_pdesc)\
+ (GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE1M || \
+ GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE2M || \
+ GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE5_5M ||\
+ GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE11M)
enum rf_optype {
RF_OP_BY_SW_3WIRE = 0,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c
index da86db86fa4a..4203a8531ca0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c
@@ -222,7 +222,6 @@ static void _rtl92s_dm_refresh_rateadaptive_mask(struct ieee80211_hw *hw)
u32 low_rssi_thresh = 0;
u32 middle_rssi_thresh = 0;
u32 high_rssi_thresh = 0;
- u8 rssi_level;
struct ieee80211_sta *sta = NULL;
if (is_hal_stop(rtlhal))
@@ -272,18 +271,14 @@ static void _rtl92s_dm_refresh_rateadaptive_mask(struct ieee80211_hw *hw)
if (rtlpriv->dm.undecorated_smoothed_pwdb >
(long)high_rssi_thresh) {
ra->ratr_state = DM_RATR_STA_HIGH;
- rssi_level = 1;
} else if (rtlpriv->dm.undecorated_smoothed_pwdb >
(long)middle_rssi_thresh) {
ra->ratr_state = DM_RATR_STA_LOW;
- rssi_level = 3;
} else if (rtlpriv->dm.undecorated_smoothed_pwdb >
(long)low_rssi_thresh) {
ra->ratr_state = DM_RATR_STA_LOW;
- rssi_level = 5;
} else {
ra->ratr_state = DM_RATR_STA_ULTRALOW;
- rssi_level = 6;
}
if (ra->pre_ratr_state != ra->ratr_state) {
@@ -457,7 +452,7 @@ static void _rtl92s_dm_initial_gain_sta_beforeconnect(struct ieee80211_hw *hw)
if (rtlpriv->psc.rfpwr_state != ERFON)
return;
- if (digtable.backoff_enable_flag == true)
+ if (digtable.backoff_enable_flag)
rtl92s_backoff_enable_flag(hw);
else
digtable.backoff_val = DM_DIG_BACKOFF;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/fw.c b/drivers/net/wireless/rtlwifi/rtl8192se/fw.c
index 3b5af0113d7f..6f91a148c222 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/fw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/fw.c
@@ -358,7 +358,6 @@ int rtl92s_download_fw(struct ieee80211_hw *hw)
struct fw_priv *pfw_priv = NULL;
u8 *puc_mappedfile = NULL;
u32 ul_filelength = 0;
- u32 file_length = 0;
u8 fwhdr_size = RT_8192S_FIRMWARE_HDR_SIZE;
u8 fwstatus = FW_STATUS_INIT;
bool rtstatus = true;
@@ -370,7 +369,6 @@ int rtl92s_download_fw(struct ieee80211_hw *hw)
firmware->fwstatus = FW_STATUS_INIT;
puc_mappedfile = firmware->sz_fw_tmpbuffer;
- file_length = firmware->sz_fw_tmpbufferlen;
/* 1. Retrieve FW header. */
firmware->pfwheader = (struct fw_hdr *) puc_mappedfile;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
index 2e9005d0454b..c474486e3911 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
@@ -27,6 +27,8 @@
*
*****************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "../wifi.h"
#include "../efuse.h"
#include "../base.h"
@@ -465,8 +467,7 @@ static u8 _rtl92ce_halset_sysclk(struct ieee80211_hw *hw, u8 data)
if ((tmpvalue & BIT(6)))
break;
- printk(KERN_ERR "wait for BIT(6) return value %x\n",
- tmpvalue);
+ pr_err("wait for BIT(6) return value %x\n", tmpvalue);
if (waitcount == 0)
break;
@@ -516,7 +517,7 @@ static u8 _rtl92se_rf_onoff_detect(struct ieee80211_hw *hw)
mdelay(10);
/* check GPIO3 */
- u1tmp = rtl_read_byte(rtlpriv, GPIO_IN);
+ u1tmp = rtl_read_byte(rtlpriv, GPIO_IN_SE);
retval = (u1tmp & HAL_8192S_HW_GPIO_OFF_BIT) ? ERFON : ERFOFF;
return retval;
@@ -884,12 +885,10 @@ static void _rtl92se_hw_configure(struct ieee80211_hw *hw)
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
u8 reg_bw_opmode = 0;
- u32 reg_ratr = 0, reg_rrsr = 0;
+ u32 reg_rrsr = 0;
u8 regtmp = 0;
reg_bw_opmode = BW_OPMODE_20MHZ;
- reg_ratr = RATE_ALL_CCK | RATE_ALL_OFDM_AG | RATE_ALL_OFDM_1SS |
- RATE_ALL_OFDM_2SS;
reg_rrsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
regtmp = rtl_read_byte(rtlpriv, INIRTSMCS_SEL);
@@ -996,7 +995,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
rtlpriv->psc.rfoff_reason = RF_CHANGE_BY_INIT;
rtlpriv->psc.rfpwr_state = ERFON;
- rtl_ps_set_rf_state(hw, ERFOFF, rfoffreason, true);
+ /* FIXME: check spinlocks if this block is uncommented */
+ rtl_ps_set_rf_state(hw, ERFOFF, rfoffreason);
} else {
/* gpio radio on/off is out of adapter start */
if (rtlpriv->psc.hwradiooff == false) {
@@ -1107,7 +1107,7 @@ void rtl92se_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
if (rtlpriv->psc.rfpwr_state != ERFON)
return;
- if (check_bssid == true) {
+ if (check_bssid) {
reg_rcr |= (RCR_CBSSID);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
} else if (check_bssid == false) {
@@ -1122,14 +1122,12 @@ static int _rtl92se_set_media_status(struct ieee80211_hw *hw,
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
u8 bt_msr = rtl_read_byte(rtlpriv, MSR);
- enum led_ctl_mode ledaction = LED_CTL_NO_LINK;
u32 temp;
bt_msr &= ~MSR_LINK_MASK;
switch (type) {
case NL80211_IFTYPE_UNSPECIFIED:
bt_msr |= (MSR_LINK_NONE << MSR_LINK_SHIFT);
- ledaction = LED_CTL_LINK;
RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
("Set Network type to NO LINK!\n"));
break;
@@ -1140,7 +1138,6 @@ static int _rtl92se_set_media_status(struct ieee80211_hw *hw,
break;
case NL80211_IFTYPE_STATION:
bt_msr |= (MSR_LINK_MANAGED << MSR_LINK_SHIFT);
- ledaction = LED_CTL_LINK;
RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
("Set Network type to STA!\n"));
break;
@@ -1218,8 +1215,6 @@ void rtl92se_enable_interrupt(struct ieee80211_hw *hw)
rtl_write_dword(rtlpriv, INTA_MASK, rtlpci->irq_mask[0]);
/* Support Bit 32-37(Assign as Bit 0-5) interrupt setting now */
rtl_write_dword(rtlpriv, INTA_MASK + 4, rtlpci->irq_mask[1] & 0x3F);
-
- rtlpci->irq_enabled = true;
}
void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
@@ -1230,7 +1225,7 @@ void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
rtl_write_dword(rtlpriv, INTA_MASK, 0);
rtl_write_dword(rtlpriv, INTA_MASK + 4, 0);
- rtlpci->irq_enabled = false;
+ synchronize_irq(rtlpci->pdev->irq);
}
@@ -1261,8 +1256,7 @@ static u8 _rtl92s_set_sysclk(struct ieee80211_hw *hw, u8 data)
if ((tmp & BIT(6)))
break;
- printk(KERN_ERR "wait for BIT(6) return value %x\n",
- tmp);
+ pr_err("wait for BIT(6) return value %x\n", tmp);
if (waitcnt == 0)
break;
@@ -1321,7 +1315,7 @@ static void _rtl92s_phy_set_rfhalt(struct ieee80211_hw *hw)
if (u1btmp & BIT(7)) {
u1btmp &= ~(BIT(6) | BIT(7));
if (!_rtl92s_set_sysclk(hw, u1btmp)) {
- printk(KERN_ERR "Switch ctrl path fail\n");
+ pr_err("Switch ctrl path fail\n");
return;
}
}
@@ -1388,7 +1382,7 @@ static void _rtl92se_power_domain_init(struct ieee80211_hw *hw)
rtl_write_byte(rtlpriv, LDOA15_CTRL, 0x34);
/* Reset MAC-IO and CPU and Core Digital BIT10/11/15 */
- tmpu1b = rtl_read_byte(rtlpriv, SYS_FUNC_EN + 1);
+ tmpu1b = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
/* If IPS we need to turn LED on. So we not
* not disable BIT 3/7 of reg3. */
@@ -1397,7 +1391,7 @@ static void _rtl92se_power_domain_init(struct ieee80211_hw *hw)
else
tmpu1b &= 0x73;
- rtl_write_byte(rtlpriv, SYS_FUNC_EN + 1, tmpu1b);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmpu1b);
/* wait for BIT 10/11/15 to pull high automatically!! */
mdelay(1);
@@ -1434,15 +1428,15 @@ static void _rtl92se_power_domain_init(struct ieee80211_hw *hw)
rtl_write_byte(rtlpriv, LDOA15_CTRL, (tmpu1b | BIT(0)));
/* Set Digital Vdd to Retention isolation Path. */
- tmpu2b = rtl_read_word(rtlpriv, SYS_ISO_CTRL);
- rtl_write_word(rtlpriv, SYS_ISO_CTRL, (tmpu2b | BIT(11)));
+ tmpu2b = rtl_read_word(rtlpriv, REG_SYS_ISO_CTRL);
+ rtl_write_word(rtlpriv, REG_SYS_ISO_CTRL, (tmpu2b | BIT(11)));
/* For warm reboot NIC disappera bug. */
- tmpu2b = rtl_read_word(rtlpriv, SYS_FUNC_EN);
- rtl_write_word(rtlpriv, SYS_FUNC_EN, (tmpu2b | BIT(13)));
+ tmpu2b = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN);
+ rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | BIT(13)));
- rtl_write_byte(rtlpriv, SYS_ISO_CTRL + 1, 0x68);
+ rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL + 1, 0x68);
/* Enable AFE PLL Macro Block */
tmpu1b = rtl_read_byte(rtlpriv, AFE_PLL_CTRL);
@@ -1453,17 +1447,17 @@ static void _rtl92se_power_domain_init(struct ieee80211_hw *hw)
mdelay(1);
/* Release isolation AFE PLL & MD */
- rtl_write_byte(rtlpriv, SYS_ISO_CTRL, 0xA6);
+ rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL, 0xA6);
/* Enable MAC clock */
tmpu2b = rtl_read_word(rtlpriv, SYS_CLKR);
rtl_write_word(rtlpriv, SYS_CLKR, (tmpu2b | BIT(12) | BIT(11)));
/* Enable Core digital and enable IOREG R/W */
- tmpu2b = rtl_read_word(rtlpriv, SYS_FUNC_EN);
- rtl_write_word(rtlpriv, SYS_FUNC_EN, (tmpu2b | BIT(11)));
+ tmpu2b = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN);
+ rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | BIT(11)));
/* enable REG_EN */
- rtl_write_word(rtlpriv, SYS_FUNC_EN, (tmpu2b | BIT(11) | BIT(15)));
+ rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | BIT(11) | BIT(15)));
/* Switch the control path. */
tmpu2b = rtl_read_word(rtlpriv, SYS_CLKR);
@@ -1655,7 +1649,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw)
rtlefuse->autoload_failflag = false;
}
- if (rtlefuse->autoload_failflag == true)
+ if (rtlefuse->autoload_failflag)
return;
_rtl8192se_get_IC_Inferiority(hw);
@@ -1688,7 +1682,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw)
rtl_write_byte(rtlpriv, MACIDR0 + i, rtlefuse->dev_addr[i]);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- (MAC_FMT "\n", MAC_ARG(rtlefuse->dev_addr)));
+ ("%pM\n", rtlefuse->dev_addr));
/* Get Tx Power Level by Channel */
/* Read Tx power of Channel 1 ~ 14 from EEPROM. */
@@ -2271,7 +2265,7 @@ bool rtl92se_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
- enum rf_pwrstate rfpwr_toset, cur_rfstate;
+ enum rf_pwrstate rfpwr_toset /*, cur_rfstate */;
unsigned long flag = 0;
bool actuallyset = false;
bool turnonbypowerdomain = false;
@@ -2292,7 +2286,7 @@ bool rtl92se_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
}
- cur_rfstate = ppsc->rfpwr_state;
+ /* cur_rfstate = ppsc->rfpwr_state;*/
/* because after _rtl92s_phy_set_rfhalt, all power
* closed, so we must open some power for GPIO check,
@@ -2305,7 +2299,7 @@ bool rtl92se_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
rfpwr_toset = _rtl92se_rf_onoff_detect(hw);
- if ((ppsc->hwradiooff == true) && (rfpwr_toset == ERFON)) {
+ if ((ppsc->hwradiooff) && (rfpwr_toset == ERFON)) {
RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
("RFKILL-HW Radio ON, RF ON\n"));
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/led.c b/drivers/net/wireless/rtlwifi/rtl8192se/led.c
index 6d4f66616680..e3fe7c90ebf4 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/led.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/led.c
@@ -90,7 +90,7 @@ void rtl92se_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
break;
case LED_PIN_LED0:
ledcfg &= 0xf0;
- if (pcipriv->ledctl.led_opendrain == true)
+ if (pcipriv->ledctl.led_opendrain)
rtl_write_byte(rtlpriv, LEDCFG, (ledcfg | BIT(1)));
else
rtl_write_byte(rtlpriv, LEDCFG, (ledcfg | BIT(3)));
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
index 63b45e60a95e..f27171af979c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
@@ -27,6 +27,8 @@
*
*****************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "../wifi.h"
#include "../pci.h"
#include "../ps.h"
@@ -180,19 +182,18 @@ u32 rtl92s_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath,
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
u32 original_value, readback_value, bitshift;
- unsigned long flags;
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), rfpath(%#x), "
"bitmask(%#x)\n", regaddr, rfpath, bitmask));
- spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
+ spin_lock(&rtlpriv->locks.rf_lock);
original_value = _rtl92s_phy_rf_serial_read(hw, rfpath, regaddr);
bitshift = _rtl92s_phy_calculate_bit_shift(bitmask);
readback_value = (original_value & bitmask) >> bitshift;
- spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
+ spin_unlock(&rtlpriv->locks.rf_lock);
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), rfpath(%#x), "
"bitmask(%#x), original_value(%#x)\n", regaddr, rfpath,
@@ -207,7 +208,6 @@ void rtl92s_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath,
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_phy *rtlphy = &(rtlpriv->phy);
u32 original_value, bitshift;
- unsigned long flags;
if (!((rtlphy->rf_pathmap >> rfpath) & 0x1))
return;
@@ -215,7 +215,7 @@ void rtl92s_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath,
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), bitmask(%#x),"
" data(%#x), rfpath(%#x)\n", regaddr, bitmask, data, rfpath));
- spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
+ spin_lock(&rtlpriv->locks.rf_lock);
if (bitmask != RFREG_OFFSET_MASK) {
original_value = _rtl92s_phy_rf_serial_read(hw, rfpath,
@@ -226,7 +226,7 @@ void rtl92s_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath,
_rtl92s_phy_rf_serial_write(hw, rfpath, regaddr, data);
- spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
+ spin_unlock(&rtlpriv->locks.rf_lock);
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), bitmask(%#x), "
"data(%#x), rfpath(%#x)\n", regaddr, bitmask, data, rfpath));
@@ -263,7 +263,6 @@ void rtl92s_phy_set_bw_mode(struct ieee80211_hw *hw,
struct rtl_phy *rtlphy = &(rtlpriv->phy);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
u8 reg_bw_opmode;
- u8 reg_prsr_rsc;
RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, ("Switch to %s bandwidth\n",
rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20 ?
@@ -277,7 +276,8 @@ void rtl92s_phy_set_bw_mode(struct ieee80211_hw *hw,
rtlphy->set_bwmode_inprogress = true;
reg_bw_opmode = rtl_read_byte(rtlpriv, BW_OPMODE);
- reg_prsr_rsc = rtl_read_byte(rtlpriv, RRSR + 2);
+ /* dummy read */
+ rtl_read_byte(rtlpriv, RRSR + 2);
switch (rtlphy->current_chan_bw) {
case HT_CHANNEL_WIDTH_20:
@@ -546,8 +546,6 @@ bool rtl92s_phy_set_rf_power_state(struct ieee80211_hw *hw,
if (rfpwr_state == ppsc->rfpwr_state)
return false;
- ppsc->set_rfpowerstate_inprogress = true;
-
switch (rfpwr_state) {
case ERFON:{
if ((ppsc->rfpwr_state == ERFOFF) &&
@@ -659,8 +657,6 @@ bool rtl92s_phy_set_rf_power_state(struct ieee80211_hw *hw,
if (bresult)
ppsc->rfpwr_state = rfpwr_state;
- ppsc->set_rfpowerstate_inprogress = false;
-
return bresult;
}
@@ -1022,8 +1018,7 @@ static bool _rtl92s_phy_bb_config_parafile(struct ieee80211_hw *hw)
rtstatus = _rtl92s_phy_config_bb(hw, BASEBAND_CONFIG_AGC_TAB);
if (rtstatus != true) {
- printk(KERN_ERR "_rtl92s_phy_bb_config_parafile(): "
- "AGC Table Fail\n");
+ pr_err("%s(): AGC Table Fail\n", __func__);
goto phy_BB8190_Config_ParaFile_Fail;
}
@@ -1422,7 +1417,7 @@ static void _rtl92s_phy_set_fwcmd_io(struct ieee80211_hw *hw)
break;
case FW_CMD_HIGH_PWR_ENABLE:
if ((rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) ||
- (rtlpriv->dm.dynamic_txpower_enable == true))
+ rtlpriv->dm.dynamic_txpower_enable)
break;
/* CCA threshold */
@@ -1614,7 +1609,7 @@ bool rtl92s_phy_set_fw_cmd(struct ieee80211_hw *hw, enum fwcmd_iotype fw_cmdio)
fw_cmdmap &= ~FW_DIG_ENABLE_CTL;
if ((rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) ||
- (rtlpriv->dm.dynamic_txpower_enable == true))
+ rtlpriv->dm.dynamic_txpower_enable)
fw_cmdmap &= ~FW_HIGH_PWR_ENABLE_CTL;
if ((digtable.dig_ext_port_stage ==
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/reg.h b/drivers/net/wireless/rtlwifi/rtl8192se/reg.h
index 0116eaddbfac..11f125c030ce 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/reg.h
@@ -248,12 +248,8 @@
#define PSTIME 0x02E0
#define TIMER0 0x02E4
#define TIMER1 0x02E8
-#define GPIO_CTRL 0x02EC
-#define GPIO_IN 0x02EC
-#define GPIO_OUT 0x02ED
+#define GPIO_IN_SE 0x02EC
#define GPIO_IO_SEL 0x02EE
-#define GPIO_MOD 0x02EF
-#define GPIO_INTCTRL 0x02F0
#define MAC_PINMUX_CFG 0x02F1
#define LEDCFG 0x02F2
#define PHY_REG 0x02F3
@@ -739,6 +735,7 @@
#define HWSET_MAX_SIZE_92S 128
#define EFUSE_MAX_SECTION 16
#define EFUSE_REAL_CONTENT_LEN 512
+#define EFUSE_OOB_PROTECT_BYTES 15
#define RTL8190_EEPROM_ID 0x8129
#define EEPROM_HPON 0x02
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/rf.c b/drivers/net/wireless/rtlwifi/rtl8192se/rf.c
index 1d3a48330399..0ad50fe44aa2 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/rf.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/rf.c
@@ -27,6 +27,8 @@
*
*****************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "../wifi.h"
#include "reg.h"
#include "def.h"
@@ -410,7 +412,7 @@ void rtl92s_phy_rf6052_set_ccktxpower(struct ieee80211_hw *hw, u8 pwrlevel)
(rtlefuse->eeprom_regulatory != 0)))
dont_inc_cck_or_turboscanoff = true;
- if (mac->act_scanning == true) {
+ if (mac->act_scanning) {
txagc = 0x3f;
if (dont_inc_cck_or_turboscanoff)
txagc = pwrlevel;
@@ -507,7 +509,7 @@ bool rtl92s_phy_rf6052_config(struct ieee80211_hw *hw)
}
if (rtstatus != true) {
- printk(KERN_ERR "Radio[%d] Fail!!", rfpath);
+ pr_err("Radio[%d] Fail!!\n", rfpath);
goto fail;
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
index 1c6cb1d7d660..92f49d522c56 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
@@ -27,7 +27,10 @@
*
*****************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/vmalloc.h>
+#include <linux/module.h>
#include "../wifi.h"
#include "../core.h"
@@ -158,10 +161,16 @@ static int rtl92s_init_sw_vars(struct ieee80211_hw *hw)
rtlpci->first_init = true;
+ /* for debug level */
+ rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug;
/* for LPS & IPS */
rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ if (!rtlpriv->psc.inactiveps)
+ pr_info("rtl8192ce: Power Save off (module option)\n");
+ if (!rtlpriv->psc.fwctrl_lps)
+ pr_info("rtl8192ce: FW Power Save off (module option)\n");
rtlpriv->psc.reg_fwctrl_lps = 3;
rtlpriv->psc.reg_max_lps_awakeintvl = 5;
/* for ASPM, you can close aspm through
@@ -183,8 +192,8 @@ static int rtl92s_init_sw_vars(struct ieee80211_hw *hw)
return 1;
}
- printk(KERN_INFO "rtl8192se: Driver for Realtek RTL8192SE/RTL8191SE\n"
- " Loading firmware %s\n", rtlpriv->cfg->fw_name);
+ pr_info("Driver for Realtek RTL8192SE/RTL8191SE\n"
+ "Loading firmware %s\n", rtlpriv->cfg->fw_name);
/* request fw */
err = request_firmware(&firmware, rtlpriv->cfg->fw_name,
rtlpriv->io.dev);
@@ -266,6 +275,7 @@ static struct rtl_mod_params rtl92se_mod_params = {
.inactiveps = true,
.swctrl_lps = true,
.fwctrl_lps = false,
+ .debug = DBG_EMERG,
};
/* Because memory R/W bursting will cause system hang/crash
@@ -298,6 +308,7 @@ static struct rtl_hal_cfg rtl92se_hal_cfg = {
.maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE_92S,
.maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION,
.maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN,
+ .maps[EFUSE_OOB_PROTECT_BYTES_LEN] = EFUSE_OOB_PROTECT_BYTES,
.maps[RWCAM] = REG_RWCAM,
.maps[WCAMI] = REG_WCAMI,
@@ -346,21 +357,21 @@ static struct rtl_hal_cfg rtl92se_hal_cfg = {
.maps[RTL_IMR_ROK] = IMR_ROK,
.maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER),
- .maps[RTL_RC_CCK_RATE1M] = DESC92S_RATE1M,
- .maps[RTL_RC_CCK_RATE2M] = DESC92S_RATE2M,
- .maps[RTL_RC_CCK_RATE5_5M] = DESC92S_RATE5_5M,
- .maps[RTL_RC_CCK_RATE11M] = DESC92S_RATE11M,
- .maps[RTL_RC_OFDM_RATE6M] = DESC92S_RATE6M,
- .maps[RTL_RC_OFDM_RATE9M] = DESC92S_RATE9M,
- .maps[RTL_RC_OFDM_RATE12M] = DESC92S_RATE12M,
- .maps[RTL_RC_OFDM_RATE18M] = DESC92S_RATE18M,
- .maps[RTL_RC_OFDM_RATE24M] = DESC92S_RATE24M,
- .maps[RTL_RC_OFDM_RATE36M] = DESC92S_RATE36M,
- .maps[RTL_RC_OFDM_RATE48M] = DESC92S_RATE48M,
- .maps[RTL_RC_OFDM_RATE54M] = DESC92S_RATE54M,
-
- .maps[RTL_RC_HT_RATEMCS7] = DESC92S_RATEMCS7,
- .maps[RTL_RC_HT_RATEMCS15] = DESC92S_RATEMCS15,
+ .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M,
+ .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M,
+ .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M,
+ .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M,
+ .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M,
+ .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M,
+ .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M,
+ .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M,
+ .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M,
+ .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M,
+ .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M,
+ .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M,
+
+ .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7,
+ .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15,
};
static struct pci_device_id rtl92se_pci_ids[] __devinitdata = {
@@ -376,31 +387,37 @@ MODULE_DEVICE_TABLE(pci, rtl92se_pci_ids);
MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>");
MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>");
+MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Realtek 8192S/8191S 802.11n PCI wireless");
MODULE_FIRMWARE("rtlwifi/rtl8192sefw.bin");
module_param_named(swenc, rtl92se_mod_params.sw_crypto, bool, 0444);
+module_param_named(debug, rtl92se_mod_params.debug, int, 0444);
module_param_named(ips, rtl92se_mod_params.inactiveps, bool, 0444);
module_param_named(swlps, rtl92se_mod_params.swctrl_lps, bool, 0444);
module_param_named(fwlps, rtl92se_mod_params.fwctrl_lps, bool, 0444);
-MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n");
-MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n");
-MODULE_PARM_DESC(swlps, "using linked sw control power save (default 1 is "
- "open)\n");
+MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
+MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
+MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
+MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
+static const struct dev_pm_ops rtlwifi_pm_ops = {
+ .suspend = rtl_pci_suspend,
+ .resume = rtl_pci_resume,
+ .freeze = rtl_pci_suspend,
+ .thaw = rtl_pci_resume,
+ .poweroff = rtl_pci_suspend,
+ .restore = rtl_pci_resume,
+};
static struct pci_driver rtl92se_driver = {
.name = KBUILD_MODNAME,
.id_table = rtl92se_pci_ids,
.probe = rtl_pci_probe,
.remove = rtl_pci_disconnect,
-
-#ifdef CONFIG_PM
- .suspend = rtl_pci_suspend,
- .resume = rtl_pci_resume,
-#endif
-
+ .driver.pm = &rtlwifi_pm_ops,
};
static int __init rtl92se_module_init(void)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
index 5cf442373d46..fbebe3ea0a22 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
@@ -51,104 +51,6 @@ static u8 _rtl92se_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 skb_queue)
return skb->priority;
}
-static int _rtl92se_rate_mapping(bool isht, u8 desc_rate, bool first_ampdu)
-{
- int rate_idx = 0;
-
- if (first_ampdu) {
- if (false == isht) {
- switch (desc_rate) {
- case DESC92S_RATE1M:
- rate_idx = 0;
- break;
- case DESC92S_RATE2M:
- rate_idx = 1;
- break;
- case DESC92S_RATE5_5M:
- rate_idx = 2;
- break;
- case DESC92S_RATE11M:
- rate_idx = 3;
- break;
- case DESC92S_RATE6M:
- rate_idx = 4;
- break;
- case DESC92S_RATE9M:
- rate_idx = 5;
- break;
- case DESC92S_RATE12M:
- rate_idx = 6;
- break;
- case DESC92S_RATE18M:
- rate_idx = 7;
- break;
- case DESC92S_RATE24M:
- rate_idx = 8;
- break;
- case DESC92S_RATE36M:
- rate_idx = 9;
- break;
- case DESC92S_RATE48M:
- rate_idx = 10;
- break;
- case DESC92S_RATE54M:
- rate_idx = 11;
- break;
- default:
- rate_idx = 0;
- break;
- }
- } else {
- rate_idx = 11;
- }
-
- return rate_idx;
- }
-
- switch (desc_rate) {
- case DESC92S_RATE1M:
- rate_idx = 0;
- break;
- case DESC92S_RATE2M:
- rate_idx = 1;
- break;
- case DESC92S_RATE5_5M:
- rate_idx = 2;
- break;
- case DESC92S_RATE11M:
- rate_idx = 3;
- break;
- case DESC92S_RATE6M:
- rate_idx = 4;
- break;
- case DESC92S_RATE9M:
- rate_idx = 5;
- break;
- case DESC92S_RATE12M:
- rate_idx = 6;
- break;
- case DESC92S_RATE18M:
- rate_idx = 7;
- break;
- case DESC92S_RATE24M:
- rate_idx = 8;
- break;
- case DESC92S_RATE36M:
- rate_idx = 9;
- break;
- case DESC92S_RATE48M:
- rate_idx = 10;
- break;
- case DESC92S_RATE54M:
- rate_idx = 11;
- break;
- default:
- rate_idx = 11;
- break;
- }
- return rate_idx;
-}
-
static u8 _rtl92s_query_rxpwrpercentage(char antpower)
{
if ((antpower <= -100) || (antpower >= 20))
@@ -222,18 +124,15 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
u8 i, max_spatial_stream;
u32 rssi, total_rssi = 0;
bool in_powersavemode = false;
- bool is_cck_rate;
+ bool is_cck = pstats->is_cck;
- is_cck_rate = RX_HAL_IS_CCK_RATE(pdesc);
pstats->packet_matchbssid = packet_match_bssid;
pstats->packet_toself = packet_toself;
- pstats->is_cck = is_cck_rate;
pstats->packet_beacon = packet_beacon;
- pstats->is_cck = is_cck_rate;
pstats->rx_mimo_signalquality[0] = -1;
pstats->rx_mimo_signalquality[1] = -1;
- if (is_cck_rate) {
+ if (is_cck) {
u8 report, cck_highpwr;
cck_buf = (struct phy_sts_cck_8192s_t *)p_drvinfo;
@@ -344,9 +243,8 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
pstats->rxpower = rx_pwr_all;
pstats->recvsignalpower = rx_pwr_all;
- if (GET_RX_STATUS_DESC_RX_HT(pdesc) &&
- GET_RX_STATUS_DESC_RX_MCS(pdesc) >= DESC92S_RATEMCS8 &&
- GET_RX_STATUS_DESC_RX_MCS(pdesc) <= DESC92S_RATEMCS15)
+ if (pstats->is_ht && pstats->rate >= DESC92_RATEMCS8 &&
+ pstats->rate <= DESC92_RATEMCS15)
max_spatial_stream = 2;
else
max_spatial_stream = 1;
@@ -364,7 +262,7 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
}
}
- if (is_cck_rate)
+ if (is_cck)
pstats->signalstrength = (u8)(_rtl92se_signal_scale_mapping(hw,
pwdb_all));
else if (rf_rx_num != 0)
@@ -581,7 +479,6 @@ static void _rtl92se_translate_rx_signal_stuff(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr;
u8 *tmp_buf;
u8 *praddr;
- u8 *psaddr;
__le16 fc;
u16 type, cfc;
bool packet_matchbssid, packet_toself, packet_beacon;
@@ -593,7 +490,6 @@ static void _rtl92se_translate_rx_signal_stuff(struct ieee80211_hw *hw,
cfc = le16_to_cpu(fc);
type = WLAN_FC_GET_TYPE(fc);
praddr = hdr->addr1;
- psaddr = hdr->addr2;
packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) &&
(!compare_ether_addr(mac->bssid, (cfc & IEEE80211_FCTL_TODS) ?
@@ -618,6 +514,8 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
{
struct rx_fwinfo *p_drvinfo;
u32 phystatus = (u32)GET_RX_STATUS_DESC_PHY_STATUS(pdesc);
+ struct ieee80211_hdr *hdr;
+ bool first_ampdu = false;
stats->length = (u16)GET_RX_STATUS_DESC_PKT_LEN(pdesc);
stats->rx_drvinfo_size = (u8)GET_RX_STATUS_DESC_DRVINFO_SIZE(pdesc) * 8;
@@ -630,8 +528,12 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
stats->rate = (u8)GET_RX_STATUS_DESC_RX_MCS(pdesc);
stats->shortpreamble = (u16)GET_RX_STATUS_DESC_SPLCP(pdesc);
stats->isampdu = (bool)(GET_RX_STATUS_DESC_PAGGR(pdesc) == 1);
+ stats->isfirst_ampdu = (bool) ((GET_RX_STATUS_DESC_PAGGR(pdesc) == 1)
+ && (GET_RX_STATUS_DESC_FAGGR(pdesc) == 1));
stats->timestamp_low = GET_RX_STATUS_DESC_TSFL(pdesc);
stats->rx_is40Mhzpacket = (bool)GET_RX_STATUS_DESC_BW(pdesc);
+ stats->is_ht = (bool)GET_RX_STATUS_DESC_RX_HT(pdesc);
+ stats->is_cck = SE_RX_HAL_IS_CCK_RATE(pdesc);
if (stats->hwerror)
return false;
@@ -639,31 +541,40 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
rx_status->freq = hw->conf.channel->center_freq;
rx_status->band = hw->conf.channel->band;
- if (GET_RX_STATUS_DESC_CRC32(pdesc))
- rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ hdr = (struct ieee80211_hdr *)(skb->data + stats->rx_drvinfo_size
+ + stats->rx_bufshift);
- if (!GET_RX_STATUS_DESC_SWDEC(pdesc))
- rx_status->flag |= RX_FLAG_DECRYPTED;
+ if (stats->crc)
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
- if (GET_RX_STATUS_DESC_BW(pdesc))
+ if (stats->rx_is40Mhzpacket)
rx_status->flag |= RX_FLAG_40MHZ;
- if (GET_RX_STATUS_DESC_RX_HT(pdesc))
+ if (stats->is_ht)
rx_status->flag |= RX_FLAG_HT;
rx_status->flag |= RX_FLAG_MACTIME_MPDU;
- if (stats->decrypted)
- rx_status->flag |= RX_FLAG_DECRYPTED;
-
- rx_status->rate_idx = _rtl92se_rate_mapping((bool)
- GET_RX_STATUS_DESC_RX_HT(pdesc),
- (u8)GET_RX_STATUS_DESC_RX_MCS(pdesc),
- (bool)GET_RX_STATUS_DESC_PAGGR(pdesc));
+ /* hw will set stats->decrypted true, if it finds the
+ * frame is open data frame or mgmt frame,
+ * hw will not decrypt robust managment frame
+ * for IEEE80211w but still set stats->decrypted
+ * true, so here we should set it back to undecrypted
+ * for IEEE80211w frame, and mac80211 sw will help
+ * to decrypt it */
+ if (stats->decrypted) {
+ if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+ (ieee80211_has_protected(hdr->frame_control)))
+ rx_status->flag &= ~RX_FLAG_DECRYPTED;
+ else
+ rx_status->flag |= RX_FLAG_DECRYPTED;
+ }
+ rx_status->rate_idx = rtlwifi_rate_mapping(hw,
+ stats->is_ht, stats->rate, first_ampdu);
- rx_status->mactime = GET_RX_STATUS_DESC_TSFL(pdesc);
- if (phystatus == true) {
+ rx_status->mactime = stats->timestamp_low;
+ if (phystatus) {
p_drvinfo = (struct rx_fwinfo *)(skb->data +
stats->rx_bufshift);
_rtl92se_translate_rx_signal_stuff(hw, skb, stats, pdesc,
@@ -725,14 +636,14 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw,
SET_TX_DESC_RSVD_MACID(pdesc, reserved_macid);
SET_TX_DESC_TXHT(pdesc, ((ptcb_desc->hw_rate >=
- DESC92S_RATEMCS0) ? 1 : 0));
+ DESC92_RATEMCS0) ? 1 : 0));
if (rtlhal->version == VERSION_8192S_ACUT) {
- if (ptcb_desc->hw_rate == DESC92S_RATE1M ||
- ptcb_desc->hw_rate == DESC92S_RATE2M ||
- ptcb_desc->hw_rate == DESC92S_RATE5_5M ||
- ptcb_desc->hw_rate == DESC92S_RATE11M) {
- ptcb_desc->hw_rate = DESC92S_RATE12M;
+ if (ptcb_desc->hw_rate == DESC92_RATE1M ||
+ ptcb_desc->hw_rate == DESC92_RATE2M ||
+ ptcb_desc->hw_rate == DESC92_RATE5_5M ||
+ ptcb_desc->hw_rate == DESC92_RATE11M) {
+ ptcb_desc->hw_rate = DESC92_RATE12M;
}
}
@@ -761,7 +672,7 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw,
SET_TX_DESC_RTS_BANDWIDTH(pdesc, 0);
SET_TX_DESC_RTS_SUB_CARRIER(pdesc, ptcb_desc->rts_sc);
SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <=
- DESC92S_RATE54M) ?
+ DESC92_RATE54M) ?
(ptcb_desc->rts_use_shortpreamble ? 1 : 0)
: (ptcb_desc->rts_use_shortgi ? 1 : 0)));
@@ -875,6 +786,7 @@ void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16)(skb->len));
SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, cpu_to_le32(mapping));
+ wmb();
SET_TX_DESC_OWN(pdesc, 1);
} else { /* H2C Command Desc format (Host TXCMD) */
/* 92SE must set as 1 for firmware download HW DMA error */
@@ -893,6 +805,7 @@ void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16)(skb->len));
SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, cpu_to_le32(mapping));
+ wmb();
SET_TX_DESC_OWN(pdesc, 1);
}
@@ -900,9 +813,10 @@ void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
void rtl92se_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
{
- if (istx == true) {
+ if (istx) {
switch (desc_name) {
case HW_DESC_OWN:
+ wmb();
SET_TX_DESC_OWN(pdesc, 1);
break;
case HW_DESC_TX_NEXTDESC_ADDR:
@@ -916,6 +830,7 @@ void rtl92se_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
} else {
switch (desc_name) {
case HW_DESC_RXOWN:
+ wmb();
SET_RX_STATUS_DESC_OWN(pdesc, 1);
break;
case HW_DESC_RXBUFF_ADDR:
@@ -939,7 +854,7 @@ u32 rtl92se_get_desc(u8 *desc, bool istx, u8 desc_name)
{
u32 ret = 0;
- if (istx == true) {
+ if (istx) {
switch (desc_name) {
case HW_DESC_OWN:
ret = GET_TX_DESC_OWN(desc);
diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c
index a9367eba1ea7..54cb8a60514d 100644
--- a/drivers/net/wireless/rtlwifi/usb.c
+++ b/drivers/net/wireless/rtlwifi/usb.c
@@ -24,7 +24,11 @@
* Hsinchu 300, Taiwan.
*
*****************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/usb.h>
+#include <linux/export.h>
#include "core.h"
#include "wifi.h"
#include "usb.h"
@@ -104,9 +108,8 @@ static int _usbctrl_vendorreq_sync_read(struct usb_device *udev, u8 request,
pdata, len, 0); /* max. timeout */
if (status < 0)
- printk(KERN_ERR "reg 0x%x, usbctrl_vendorreq TimeOut! "
- "status:0x%x value=0x%x\n", value, status,
- *(u32 *)pdata);
+ pr_err("reg 0x%x, usbctrl_vendorreq TimeOut! status:0x%x value=0x%x\n",
+ value, status, *(u32 *)pdata);
return status;
}
@@ -189,44 +192,6 @@ static void _usb_write32_async(struct rtl_priv *rtlpriv, u32 addr, u32 val)
_usb_write_async(to_usb_device(dev), addr, val, 4);
}
-static int _usb_nbytes_read_write(struct usb_device *udev, bool read, u32 addr,
- u16 len, u8 *pdata)
-{
- int status;
- u8 request;
- u16 wvalue;
- u16 index;
-
- request = REALTEK_USB_VENQT_CMD_REQ;
- index = REALTEK_USB_VENQT_CMD_IDX; /* n/a */
- wvalue = (u16)addr;
- if (read)
- status = _usbctrl_vendorreq_sync_read(udev, request, wvalue,
- index, pdata, len);
- else
- status = _usbctrl_vendorreq_async_write(udev, request, wvalue,
- index, pdata, len);
- return status;
-}
-
-static int _usb_readN_sync(struct rtl_priv *rtlpriv, u32 addr, u16 len,
- u8 *pdata)
-{
- struct device *dev = rtlpriv->io.dev;
-
- return _usb_nbytes_read_write(to_usb_device(dev), true, addr, len,
- pdata);
-}
-
-static int _usb_writeN_async(struct rtl_priv *rtlpriv, u32 addr, u16 len,
- u8 *pdata)
-{
- struct device *dev = rtlpriv->io.dev;
-
- return _usb_nbytes_read_write(to_usb_device(dev), false, addr, len,
- pdata);
-}
-
static void _rtl_usb_io_handler_init(struct device *dev,
struct ieee80211_hw *hw)
{
@@ -237,11 +202,9 @@ static void _rtl_usb_io_handler_init(struct device *dev,
rtlpriv->io.write8_async = _usb_write8_async;
rtlpriv->io.write16_async = _usb_write16_async;
rtlpriv->io.write32_async = _usb_write32_async;
- rtlpriv->io.writeN_async = _usb_writeN_async;
rtlpriv->io.read8_sync = _usb_read8_sync;
rtlpriv->io.read16_sync = _usb_read16_sync;
rtlpriv->io.read32_sync = _usb_read32_sync;
- rtlpriv->io.readN_sync = _usb_readN_sync;
}
static void _rtl_usb_io_handler_release(struct ieee80211_hw *hw)
@@ -316,7 +279,7 @@ static int _rtl_usb_init_rx(struct ieee80211_hw *hw)
rtlusb->usb_rx_segregate_hdl =
rtlpriv->cfg->usb_interface_cfg->usb_rx_segregate_hdl;
- printk(KERN_INFO "rtl8192cu: rx_max_size %d, rx_urb_num %d, in_ep %d\n",
+ pr_info("rx_max_size %d, rx_urb_num %d, in_ep %d\n",
rtlusb->rx_max_size, rtlusb->rx_urb_num, rtlusb->in_ep);
init_usb_anchor(&rtlusb->rx_submitted);
return 0;
@@ -580,7 +543,7 @@ static void _rtl_rx_completed(struct urb *_urb)
} else{
/* TO DO */
_rtl_rx_pre_process(hw, skb);
- printk(KERN_ERR "rtlwifi: rx agg not supported\n");
+ pr_err("rx agg not supported\n");
}
goto resubmit;
}
@@ -861,6 +824,7 @@ static void _rtl_usb_tx_preprocess(struct ieee80211_hw *hw, struct sk_buff *skb,
u8 tid = 0;
u16 seq_number = 0;
+ memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc));
if (ieee80211_is_auth(fc)) {
RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, ("MAC80211_LINKING\n"));
rtl_ips_nic_on(hw);
diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h
index 693395ee98f9..713c7ddba8eb 100644
--- a/drivers/net/wireless/rtlwifi/wifi.h
+++ b/drivers/net/wireless/rtlwifi/wifi.h
@@ -32,7 +32,6 @@
#include <linux/sched.h>
#include <linux/firmware.h>
-#include <linux/version.h>
#include <linux/etherdevice.h>
#include <linux/vmalloc.h>
#include <linux/usb.h>
@@ -166,6 +165,12 @@ enum hardware_type {
#define IS_HARDWARE_TYPE_8723U(rtlhal) \
(rtlhal->hw_type == HARDWARE_TYPE_RTL8723U)
+#define RX_HAL_IS_CCK_RATE(_pdesc)\
+ (_pdesc->rxmcs == DESC92_RATE1M || \
+ _pdesc->rxmcs == DESC92_RATE2M || \
+ _pdesc->rxmcs == DESC92_RATE5_5M || \
+ _pdesc->rxmcs == DESC92_RATE11M)
+
enum scan_operation_backup_opt {
SCAN_OPT_BACKUP = 0,
SCAN_OPT_RESTORE,
@@ -303,9 +308,6 @@ enum hw_variables {
HW_VAR_DATA_FILTER,
};
-#define HWSET_MAX_SIZE 128
-#define EFUSE_MAX_SECTION 16
-
enum _RT_MEDIA_STATUS {
RT_MEDIA_DISCONNECT = 0,
RT_MEDIA_CONNECT = 1
@@ -390,6 +392,41 @@ enum rtl_hal_state {
_HAL_STATE_START = 1,
};
+enum rtl_desc92_rate {
+ DESC92_RATE1M = 0x00,
+ DESC92_RATE2M = 0x01,
+ DESC92_RATE5_5M = 0x02,
+ DESC92_RATE11M = 0x03,
+
+ DESC92_RATE6M = 0x04,
+ DESC92_RATE9M = 0x05,
+ DESC92_RATE12M = 0x06,
+ DESC92_RATE18M = 0x07,
+ DESC92_RATE24M = 0x08,
+ DESC92_RATE36M = 0x09,
+ DESC92_RATE48M = 0x0a,
+ DESC92_RATE54M = 0x0b,
+
+ DESC92_RATEMCS0 = 0x0c,
+ DESC92_RATEMCS1 = 0x0d,
+ DESC92_RATEMCS2 = 0x0e,
+ DESC92_RATEMCS3 = 0x0f,
+ DESC92_RATEMCS4 = 0x10,
+ DESC92_RATEMCS5 = 0x11,
+ DESC92_RATEMCS6 = 0x12,
+ DESC92_RATEMCS7 = 0x13,
+ DESC92_RATEMCS8 = 0x14,
+ DESC92_RATEMCS9 = 0x15,
+ DESC92_RATEMCS10 = 0x16,
+ DESC92_RATEMCS11 = 0x17,
+ DESC92_RATEMCS12 = 0x18,
+ DESC92_RATEMCS13 = 0x19,
+ DESC92_RATEMCS14 = 0x1a,
+ DESC92_RATEMCS15 = 0x1b,
+ DESC92_RATEMCS15_SG = 0x1c,
+ DESC92_RATEMCS32 = 0x20,
+};
+
enum rtl_var_map {
/*reg map */
SYS_ISO_CTRL = 0,
@@ -413,6 +450,7 @@ enum rtl_var_map {
EFUSE_HWSET_MAX_SIZE,
EFUSE_MAX_SECTION_MAP,
EFUSE_REAL_CONTENT_SIZE,
+ EFUSE_OOB_PROTECT_BYTES_LEN,
/*CAM map */
RWCAM,
@@ -905,16 +943,12 @@ struct rtl_io {
unsigned long pci_base_addr; /*device I/O address */
void (*write8_async) (struct rtl_priv *rtlpriv, u32 addr, u8 val);
- void (*write16_async) (struct rtl_priv *rtlpriv, u32 addr, u16 val);
- void (*write32_async) (struct rtl_priv *rtlpriv, u32 addr, u32 val);
- int (*writeN_async) (struct rtl_priv *rtlpriv, u32 addr, u16 len,
- u8 *pdata);
+ void (*write16_async) (struct rtl_priv *rtlpriv, u32 addr, __le16 val);
+ void (*write32_async) (struct rtl_priv *rtlpriv, u32 addr, __le32 val);
u8(*read8_sync) (struct rtl_priv *rtlpriv, u32 addr);
u16(*read16_sync) (struct rtl_priv *rtlpriv, u32 addr);
u32(*read32_sync) (struct rtl_priv *rtlpriv, u32 addr);
- int (*readN_sync) (struct rtl_priv *rtlpriv, u32 addr, u16 len,
- u8 *pdata);
};
@@ -938,7 +972,7 @@ struct rtl_mac {
int n_channels;
int n_bitrates;
- bool offchan_deley;
+ bool offchan_delay;
/*filters */
u32 rx_conf;
@@ -1188,7 +1222,6 @@ struct rtl_efuse {
struct rtl_ps_ctl {
bool pwrdomain_protect;
- bool set_rfpowerstate_inprogress;
bool in_powersavemode;
bool rfchange_inprogress;
bool swrf_processing;
@@ -1292,6 +1325,7 @@ struct rtl_stats {
s8 rx_mimo_signalquality[2];
bool packet_matchbssid;
bool is_cck;
+ bool is_ht;
bool packet_toself;
bool packet_beacon; /*for rssi */
char cck_adc_pwdb[4]; /*for rx path selection */
@@ -1453,6 +1487,9 @@ struct rtl_mod_params {
/* default: 0 = using hardware encryption */
int sw_crypto;
+ /* default: 0 = DBG_EMERG (0)*/
+ int debug;
+
/* default: 1 = using no linked power save */
bool inactiveps;
@@ -1536,6 +1573,7 @@ struct rtl_works {
/* For SW LPS */
struct delayed_work ps_work;
struct delayed_work ps_rfon_wq;
+ struct tasklet_struct ips_leave_tasklet;
};
struct rtl_debug {
@@ -1983,7 +2021,7 @@ static inline u16 rtl_get_tid(struct sk_buff *skb)
static inline struct ieee80211_sta *get_sta(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- u8 *bssid)
+ const u8 *bssid)
{
return ieee80211_find_sta(vif, bssid);
}
diff --git a/drivers/net/wireless/wl1251/acx.c b/drivers/net/wireless/wl1251/acx.c
index ef8370edace7..ad87a1ac6462 100644
--- a/drivers/net/wireless/wl1251/acx.c
+++ b/drivers/net/wireless/wl1251/acx.c
@@ -140,8 +140,6 @@ int wl1251_acx_sleep_auth(struct wl1251 *wl, u8 sleep_auth)
auth->sleep_auth = sleep_auth;
ret = wl1251_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth));
- if (ret < 0)
- return ret;
out:
kfree(auth);
@@ -681,10 +679,8 @@ int wl1251_acx_cca_threshold(struct wl1251 *wl)
ret = wl1251_cmd_configure(wl, ACX_CCA_THRESHOLD,
detection, sizeof(*detection));
- if (ret < 0) {
+ if (ret < 0)
wl1251_warning("failed to set cca threshold: %d", ret);
- return ret;
- }
out:
kfree(detection);
diff --git a/drivers/net/wireless/wl1251/cmd.c b/drivers/net/wireless/wl1251/cmd.c
index 81f164bc4888..d14d69d733a0 100644
--- a/drivers/net/wireless/wl1251/cmd.c
+++ b/drivers/net/wireless/wl1251/cmd.c
@@ -241,7 +241,7 @@ int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable)
if (ret < 0) {
wl1251_error("tx %s cmd for channel %d failed",
enable ? "start" : "stop", channel);
- return ret;
+ goto out;
}
wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d",
diff --git a/drivers/net/wireless/wl1251/cmd.h b/drivers/net/wireless/wl1251/cmd.h
index 79ca5273c9e9..ee4f2b391822 100644
--- a/drivers/net/wireless/wl1251/cmd.h
+++ b/drivers/net/wireless/wl1251/cmd.h
@@ -269,7 +269,7 @@ struct cmd_join {
u8 bss_type;
u8 channel;
u8 ssid_len;
- u8 ssid[IW_ESSID_MAX_SIZE];
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ctrl; /* JOIN_CMD_CTRL_* */
u8 tx_mgt_frame_rate; /* OBSOLETE */
u8 tx_mgt_frame_mod; /* OBSOLETE */
diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c
index a14a48c99cdc..ba3268ea81fe 100644
--- a/drivers/net/wireless/wl1251/main.c
+++ b/drivers/net/wireless/wl1251/main.c
@@ -1158,7 +1158,8 @@ static struct ieee80211_channel wl1251_channels[] = {
{ .hw_value = 13, .center_freq = 2472},
};
-static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
+static int wl1251_op_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
enum wl1251_acx_ps_scheme ps_scheme;
diff --git a/drivers/net/wireless/wl1251/sdio.c b/drivers/net/wireless/wl1251/sdio.c
index f51a0241a440..f78694295c39 100644
--- a/drivers/net/wireless/wl1251/sdio.c
+++ b/drivers/net/wireless/wl1251/sdio.c
@@ -19,6 +19,7 @@
* Copyright (C) 2008 Google Inc
* Copyright (C) 2009 Bob Copeland (me@bobcopeland.com)
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mmc/sdio_func.h>
diff --git a/drivers/net/wireless/wl1251/spi.c b/drivers/net/wireless/wl1251/spi.c
index af6448c4d3e2..eaa5f9556200 100644
--- a/drivers/net/wireless/wl1251/spi.c
+++ b/drivers/net/wireless/wl1251/spi.c
@@ -19,6 +19,7 @@
*
*/
+#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/slab.h>
diff --git a/drivers/net/wireless/wl1251/wl12xx_80211.h b/drivers/net/wireless/wl1251/wl12xx_80211.h
index 1417b1445c3d..04ed51495772 100644
--- a/drivers/net/wireless/wl1251/wl12xx_80211.h
+++ b/drivers/net/wireless/wl1251/wl12xx_80211.h
@@ -76,7 +76,7 @@ struct wl12xx_ie_header {
struct wl12xx_ie_ssid {
struct wl12xx_ie_header header;
- char ssid[IW_ESSID_MAX_SIZE];
+ char ssid[IEEE80211_MAX_SSID_LEN];
} __packed;
struct wl12xx_ie_rates {
diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig
index 35ce7b0f4a60..3fe388b87c2e 100644
--- a/drivers/net/wireless/wl12xx/Kconfig
+++ b/drivers/net/wireless/wl12xx/Kconfig
@@ -11,7 +11,6 @@ config WL12XX
depends on WL12XX_MENU && GENERIC_HARDIRQS
depends on INET
select FW_LOADER
- select CRC7
---help---
This module adds support for wireless adapters based on TI wl1271 and
TI wl1273 chipsets. This module does *not* include support for wl1251.
@@ -20,19 +19,10 @@ config WL12XX
If you choose to build a module, it will be called wl12xx. Say N if
unsure.
-config WL12XX_HT
- bool "TI wl12xx 802.11 HT support (EXPERIMENTAL)"
- depends on WL12XX && EXPERIMENTAL
- default n
- ---help---
- This will enable 802.11 HT support in the wl12xx module.
-
- That configuration is temporary due to the code incomplete and
- still in testing process.
-
config WL12XX_SPI
tristate "TI wl12xx SPI support"
depends on WL12XX && SPI_MASTER
+ select CRC7
---help---
This module adds support for the SPI interface of adapters using
TI wl12xx chipsets. Select this if your platform is using
diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile
index 521c0414e52e..621b3483ca2c 100644
--- a/drivers/net/wireless/wl12xx/Makefile
+++ b/drivers/net/wireless/wl12xx/Makefile
@@ -1,16 +1,16 @@
wl12xx-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
boot.o init.o debugfs.o scan.o
-wl12xx_spi-objs = spi.o
+wl12xx_spi-objs = spi.o
wl12xx_sdio-objs = sdio.o
-wl12xx_sdio_test-objs = sdio_test.o
+wl12xx_sdio_test-objs = sdio_test.o
wl12xx-$(CONFIG_NL80211_TESTMODE) += testmode.o
obj-$(CONFIG_WL12XX) += wl12xx.o
obj-$(CONFIG_WL12XX_SPI) += wl12xx_spi.o
obj-$(CONFIG_WL12XX_SDIO) += wl12xx_sdio.o
-obj-$(CONFIG_WL12XX_SDIO_TEST) += wl12xx_sdio_test.o
+obj-$(CONFIG_WL12XX_SDIO_TEST) += wl12xx_sdio_test.o
# small builtin driver bit
obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o
diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c
index c6ee530e5bf7..ca044a743191 100644
--- a/drivers/net/wireless/wl12xx/acx.c
+++ b/drivers/net/wireless/wl12xx/acx.c
@@ -25,7 +25,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/crc7.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
@@ -47,6 +46,7 @@ int wl1271_acx_wake_up_conditions(struct wl1271 *wl)
goto out;
}
+ wake_up->role_id = wl->role_id;
wake_up->wake_up_event = wl->conf.conn.wake_up_event;
wake_up->listen_interval = wl->conf.conn.listen_interval;
@@ -78,8 +78,6 @@ int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth)
auth->sleep_auth = sleep_auth;
ret = wl1271_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth));
- if (ret < 0)
- return ret;
out:
kfree(auth);
@@ -91,7 +89,7 @@ int wl1271_acx_tx_power(struct wl1271 *wl, int power)
struct acx_current_tx_power *acx;
int ret;
- wl1271_debug(DEBUG_ACX, "acx dot11_cur_tx_pwr");
+ wl1271_debug(DEBUG_ACX, "acx dot11_cur_tx_pwr %d", power);
if (power < 0 || power > 25)
return -EINVAL;
@@ -102,6 +100,7 @@ int wl1271_acx_tx_power(struct wl1271 *wl, int power)
goto out;
}
+ acx->role_id = wl->role_id;
acx->current_tx_power = power * 10;
ret = wl1271_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx));
@@ -129,6 +128,7 @@ int wl1271_acx_feature_cfg(struct wl1271 *wl)
}
/* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */
+ feature->role_id = wl->role_id;
feature->data_flow_options = 0;
feature->options = 0;
@@ -184,34 +184,6 @@ out:
return ret;
}
-int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter)
-{
- struct acx_rx_config *rx_config;
- int ret;
-
- wl1271_debug(DEBUG_ACX, "acx rx config");
-
- rx_config = kzalloc(sizeof(*rx_config), GFP_KERNEL);
- if (!rx_config) {
- ret = -ENOMEM;
- goto out;
- }
-
- rx_config->config_options = cpu_to_le32(config);
- rx_config->filter_options = cpu_to_le32(filter);
-
- ret = wl1271_cmd_configure(wl, ACX_RX_CFG,
- rx_config, sizeof(*rx_config));
- if (ret < 0) {
- wl1271_warning("failed to set rx config: %d", ret);
- goto out;
- }
-
-out:
- kfree(rx_config);
- return ret;
-}
-
int wl1271_acx_pd_threshold(struct wl1271 *wl)
{
struct acx_packet_detection *pd;
@@ -251,6 +223,7 @@ int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time)
goto out;
}
+ slot->role_id = wl->role_id;
slot->wone_index = STATION_WONE_INDEX;
slot->slot_time = slot_time;
@@ -280,6 +253,7 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
}
/* MAC filtering */
+ acx->role_id = wl->role_id;
acx->enabled = enable;
acx->num_groups = mc_list_len;
memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN);
@@ -309,6 +283,7 @@ int wl1271_acx_service_period_timeout(struct wl1271 *wl)
wl1271_debug(DEBUG_ACX, "acx service period timeout");
+ rx_timeout->role_id = wl->role_id;
rx_timeout->ps_poll_timeout = cpu_to_le16(wl->conf.rx.ps_poll_timeout);
rx_timeout->upsd_timeout = cpu_to_le16(wl->conf.rx.upsd_timeout);
@@ -345,6 +320,7 @@ int wl1271_acx_rts_threshold(struct wl1271 *wl, u32 rts_threshold)
goto out;
}
+ rts->role_id = wl->role_id;
rts->threshold = cpu_to_le16((u16)rts_threshold);
ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts));
@@ -404,6 +380,7 @@ int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter)
goto out;
}
+ beacon_filter->role_id = wl->role_id;
beacon_filter->enable = enable_filter;
/*
@@ -440,6 +417,7 @@ int wl1271_acx_beacon_filter_table(struct wl1271 *wl)
}
/* configure default beacon pass-through rules */
+ ie_table->role_id = wl->role_id;
ie_table->num_ie = 0;
for (i = 0; i < wl->conf.conn.bcn_filt_ie_count; i++) {
struct conf_bcn_filt_rule *r = &(wl->conf.conn.bcn_filt_ie[i]);
@@ -501,6 +479,7 @@ int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable)
timeout = wl->conf.conn.bss_lose_timeout;
}
+ acx->role_id = wl->role_id;
acx->synch_fail_thold = cpu_to_le32(threshold);
acx->bss_lose_timeout = cpu_to_le32(timeout);
@@ -547,43 +526,13 @@ out:
return ret;
}
-int wl1271_acx_sta_sg_cfg(struct wl1271 *wl)
-{
- struct acx_sta_bt_wlan_coex_param *param;
- struct conf_sg_settings *c = &wl->conf.sg;
- int i, ret;
-
- wl1271_debug(DEBUG_ACX, "acx sg sta cfg");
-
- param = kzalloc(sizeof(*param), GFP_KERNEL);
- if (!param) {
- ret = -ENOMEM;
- goto out;
- }
-
- /* BT-WLAN coext parameters */
- for (i = 0; i < CONF_SG_STA_PARAMS_MAX; i++)
- param->params[i] = cpu_to_le32(c->sta_params[i]);
- param->param_idx = CONF_SG_PARAMS_ALL;
-
- ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param));
- if (ret < 0) {
- wl1271_warning("failed to set sg config: %d", ret);
- goto out;
- }
-
-out:
- kfree(param);
- return ret;
-}
-
-int wl1271_acx_ap_sg_cfg(struct wl1271 *wl)
+int wl12xx_acx_sg_cfg(struct wl1271 *wl)
{
- struct acx_ap_bt_wlan_coex_param *param;
+ struct acx_bt_wlan_coex_param *param;
struct conf_sg_settings *c = &wl->conf.sg;
int i, ret;
- wl1271_debug(DEBUG_ACX, "acx sg ap cfg");
+ wl1271_debug(DEBUG_ACX, "acx sg cfg");
param = kzalloc(sizeof(*param), GFP_KERNEL);
if (!param) {
@@ -592,8 +541,8 @@ int wl1271_acx_ap_sg_cfg(struct wl1271 *wl)
}
/* BT-WLAN coext parameters */
- for (i = 0; i < CONF_SG_AP_PARAMS_MAX; i++)
- param->params[i] = cpu_to_le32(c->ap_params[i]);
+ for (i = 0; i < CONF_SG_PARAMS_MAX; i++)
+ param->params[i] = cpu_to_le32(c->params[i]);
param->param_idx = CONF_SG_PARAMS_ALL;
ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param));
@@ -625,10 +574,8 @@ int wl1271_acx_cca_threshold(struct wl1271 *wl)
ret = wl1271_cmd_configure(wl, ACX_CCA_THRESHOLD,
detection, sizeof(*detection));
- if (ret < 0) {
+ if (ret < 0)
wl1271_warning("failed to set cca threshold: %d", ret);
- return ret;
- }
out:
kfree(detection);
@@ -648,6 +595,7 @@ int wl1271_acx_bcn_dtim_options(struct wl1271 *wl)
goto out;
}
+ bb->role_id = wl->role_id;
bb->beacon_rx_timeout = cpu_to_le16(wl->conf.conn.beacon_rx_timeout);
bb->broadcast_timeout = cpu_to_le16(wl->conf.conn.broadcast_timeout);
bb->rx_broadcast_in_ps = wl->conf.conn.rx_broadcast_in_ps;
@@ -677,6 +625,7 @@ int wl1271_acx_aid(struct wl1271 *wl, u16 aid)
goto out;
}
+ acx_aid->role_id = wl->role_id;
acx_aid->aid = cpu_to_le16(aid);
ret = wl1271_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid));
@@ -732,6 +681,7 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble)
goto out;
}
+ acx->role_id = wl->role_id;
acx->preamble = preamble;
ret = wl1271_cmd_configure(wl, ACX_PREAMBLE_TYPE, acx, sizeof(*acx));
@@ -759,6 +709,7 @@ int wl1271_acx_cts_protect(struct wl1271 *wl,
goto out;
}
+ acx->role_id = wl->role_id;
acx->ctsprotect = ctsprotect;
ret = wl1271_cmd_configure(wl, ACX_CTS_PROTECTION, acx, sizeof(*acx));
@@ -790,9 +741,8 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
{
- struct acx_sta_rate_policy *acx;
+ struct acx_rate_policy *acx;
struct conf_tx_rate_class *c = &wl->conf.tx.sta_rc_conf;
- int idx = 0;
int ret = 0;
wl1271_debug(DEBUG_ACX, "acx rate policies");
@@ -804,25 +754,46 @@ int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
goto out;
}
+ wl1271_debug(DEBUG_ACX, "basic_rate: 0x%x, full_rate: 0x%x",
+ wl->basic_rate, wl->rate_set);
+
/* configure one basic rate class */
- idx = ACX_TX_BASIC_RATE;
- acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->basic_rate);
- acx->rate_class[idx].short_retry_limit = c->short_retry_limit;
- acx->rate_class[idx].long_retry_limit = c->long_retry_limit;
- acx->rate_class[idx].aflags = c->aflags;
+ acx->rate_policy_idx = cpu_to_le32(ACX_TX_BASIC_RATE);
+ acx->rate_policy.enabled_rates = cpu_to_le32(wl->basic_rate);
+ acx->rate_policy.short_retry_limit = c->short_retry_limit;
+ acx->rate_policy.long_retry_limit = c->long_retry_limit;
+ acx->rate_policy.aflags = c->aflags;
+
+ ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("Setting of rate policies failed: %d", ret);
+ goto out;
+ }
/* configure one AP supported rate class */
- idx = ACX_TX_AP_FULL_RATE;
- acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->rate_set);
- acx->rate_class[idx].short_retry_limit = c->short_retry_limit;
- acx->rate_class[idx].long_retry_limit = c->long_retry_limit;
- acx->rate_class[idx].aflags = c->aflags;
+ acx->rate_policy_idx = cpu_to_le32(ACX_TX_AP_FULL_RATE);
+ acx->rate_policy.enabled_rates = cpu_to_le32(wl->rate_set);
+ acx->rate_policy.short_retry_limit = c->short_retry_limit;
+ acx->rate_policy.long_retry_limit = c->long_retry_limit;
+ acx->rate_policy.aflags = c->aflags;
- acx->rate_class_cnt = cpu_to_le32(ACX_TX_RATE_POLICY_CNT);
+ ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("Setting of rate policies failed: %d", ret);
+ goto out;
+ }
- wl1271_debug(DEBUG_ACX, "basic_rate: 0x%x, full_rate: 0x%x",
- acx->rate_class[ACX_TX_BASIC_RATE].enabled_rates,
- acx->rate_class[ACX_TX_AP_FULL_RATE].enabled_rates);
+ /*
+ * configure one rate class for basic p2p operations.
+ * (p2p packets should always go out with OFDM rates, even
+ * if we are currently connected to 11b AP)
+ */
+ acx->rate_policy_idx = cpu_to_le32(ACX_TX_BASIC_RATE_P2P);
+ acx->rate_policy.enabled_rates =
+ cpu_to_le32(CONF_TX_RATE_MASK_BASIC_P2P);
+ acx->rate_policy.short_retry_limit = c->short_retry_limit;
+ acx->rate_policy.long_retry_limit = c->long_retry_limit;
+ acx->rate_policy.aflags = c->aflags;
ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx));
if (ret < 0) {
@@ -838,7 +809,7 @@ out:
int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
u8 idx)
{
- struct acx_ap_rate_policy *acx;
+ struct acx_rate_policy *acx;
int ret = 0;
wl1271_debug(DEBUG_ACX, "acx ap rate policy %d rates 0x%x",
@@ -884,6 +855,7 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
goto out;
}
+ acx->role_id = wl->role_id;
acx->ac = ac;
acx->cw_min = cw_min;
acx->cw_max = cpu_to_le16(cw_max);
@@ -917,6 +889,7 @@ int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
goto out;
}
+ acx->role_id = wl->role_id;
acx->queue_id = queue_id;
acx->channel_type = channel_type;
acx->tsid = tsid;
@@ -996,52 +969,9 @@ out:
return ret;
}
-int wl1271_acx_ap_mem_cfg(struct wl1271 *wl)
+int wl12xx_acx_mem_cfg(struct wl1271 *wl)
{
- struct wl1271_acx_ap_config_memory *mem_conf;
- struct conf_memory_settings *mem;
- int ret;
-
- wl1271_debug(DEBUG_ACX, "wl1271 mem cfg");
-
- mem_conf = kzalloc(sizeof(*mem_conf), GFP_KERNEL);
- if (!mem_conf) {
- ret = -ENOMEM;
- goto out;
- }
-
- if (wl->chip.id == CHIP_ID_1283_PG20)
- /*
- * FIXME: The 128x AP FW does not yet support dynamic memory.
- * Use the base memory configuration for 128x for now. This
- * should be fine tuned in the future.
- */
- mem = &wl->conf.mem_wl128x;
- else
- mem = &wl->conf.mem_wl127x;
-
- /* memory config */
- mem_conf->num_stations = mem->num_stations;
- mem_conf->rx_mem_block_num = mem->rx_block_num;
- mem_conf->tx_min_mem_block_num = mem->tx_min_block_num;
- mem_conf->num_ssid_profiles = mem->ssid_profiles;
- mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS);
-
- ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf,
- sizeof(*mem_conf));
- if (ret < 0) {
- wl1271_warning("wl1271 mem config failed: %d", ret);
- goto out;
- }
-
-out:
- kfree(mem_conf);
- return ret;
-}
-
-int wl1271_acx_sta_mem_cfg(struct wl1271 *wl)
-{
- struct wl1271_acx_sta_config_memory *mem_conf;
+ struct wl12xx_acx_config_memory *mem_conf;
struct conf_memory_settings *mem;
int ret;
@@ -1068,6 +998,7 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl)
mem_conf->tx_free_req = mem->min_req_tx_blocks;
mem_conf->rx_free_req = mem->min_req_rx_blocks;
mem_conf->tx_min = mem->tx_min;
+ mem_conf->fwlog_blocks = wl->conf.fwlog.mem_blocks;
ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf,
sizeof(*mem_conf));
@@ -1183,6 +1114,7 @@ int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable)
goto out;
}
+ acx->role_id = wl->role_id;
acx->enable = enable ? CONF_BET_MODE_ENABLE : CONF_BET_MODE_DISABLE;
acx->max_consecutive = wl->conf.conn.bet_max_consecutive;
@@ -1210,6 +1142,7 @@ int wl1271_acx_arp_ip_filter(struct wl1271 *wl, u8 enable, __be32 address)
goto out;
}
+ acx->role_id = wl->role_id;
acx->version = ACX_IPV4_VERSION;
acx->enable = enable;
@@ -1269,6 +1202,7 @@ int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable)
goto out;
}
+ acx->role_id = wl->role_id;
acx->enabled = enable;
ret = wl1271_cmd_configure(wl, ACX_KEEP_ALIVE_MODE, acx, sizeof(*acx));
@@ -1295,6 +1229,7 @@ int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid)
goto out;
}
+ acx->role_id = wl->role_id;
acx->period = cpu_to_le32(wl->conf.conn.keep_alive_interval);
acx->index = index;
acx->tpl_validation = tpl_valid;
@@ -1328,6 +1263,7 @@ int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
wl->last_rssi_event = -1;
+ acx->role_id = wl->role_id;
acx->pacing = cpu_to_le16(wl->conf.roam_trigger.trigger_pacing);
acx->metric = WL1271_ACX_TRIG_METRIC_RSSI_BEACON;
acx->type = WL1271_ACX_TRIG_TYPE_EDGE;
@@ -1366,6 +1302,7 @@ int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl)
goto out;
}
+ acx->role_id = wl->role_id;
acx->rssi_beacon = c->avg_weight_rssi_beacon;
acx->rssi_data = c->avg_weight_rssi_data;
acx->snr_beacon = c->avg_weight_snr_beacon;
@@ -1384,14 +1321,15 @@ out:
int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap,
- bool allow_ht_operation)
+ bool allow_ht_operation, u8 hlid)
{
struct wl1271_acx_ht_capabilities *acx;
- u8 mac_address[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
int ret = 0;
u32 ht_capabilites = 0;
- wl1271_debug(DEBUG_ACX, "acx ht capabilities setting");
+ wl1271_debug(DEBUG_ACX, "acx ht capabilities setting "
+ "sta supp: %d sta cap: %d", ht_cap->ht_supported,
+ ht_cap->cap);
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
@@ -1399,26 +1337,22 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
goto out;
}
- /* Allow HT Operation ? */
- if (allow_ht_operation) {
- ht_capabilites =
- WL1271_ACX_FW_CAP_HT_OPERATION;
- if (ht_cap->cap & IEEE80211_HT_CAP_GRN_FLD)
- ht_capabilites |=
- WL1271_ACX_FW_CAP_GREENFIELD_FRAME_FORMAT;
- if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
- ht_capabilites |=
- WL1271_ACX_FW_CAP_SHORT_GI_FOR_20MHZ_PACKETS;
- if (ht_cap->cap & IEEE80211_HT_CAP_LSIG_TXOP_PROT)
- ht_capabilites |=
- WL1271_ACX_FW_CAP_LSIG_TXOP_PROTECTION;
+ if (allow_ht_operation && ht_cap->ht_supported) {
+ /* no need to translate capabilities - use the spec values */
+ ht_capabilites = ht_cap->cap;
+
+ /*
+ * this bit is not employed by the spec but only by FW to
+ * indicate peer HT support
+ */
+ ht_capabilites |= WL12XX_HT_CAP_HT_OPERATION;
/* get data from A-MPDU parameters field */
acx->ampdu_max_length = ht_cap->ampdu_factor;
acx->ampdu_min_spacing = ht_cap->ampdu_density;
}
- memcpy(acx->mac_address, mac_address, ETH_ALEN);
+ acx->hlid = hlid;
acx->ht_capabilites = cpu_to_le32(ht_capabilites);
ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx));
@@ -1446,6 +1380,7 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl,
goto out;
}
+ acx->role_id = wl->role_id;
acx->ht_protection =
(u8)(ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION);
acx->rifs_mode = 0;
@@ -1467,14 +1402,12 @@ out:
}
/* Configure BA session initiator/receiver parameters setting in the FW. */
-int wl1271_acx_set_ba_session(struct wl1271 *wl,
- enum ieee80211_back_parties direction,
- u8 tid_index, u8 policy)
+int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl)
{
- struct wl1271_acx_ba_session_policy *acx;
+ struct wl1271_acx_ba_initiator_policy *acx;
int ret;
- wl1271_debug(DEBUG_ACX, "acx ba session setting");
+ wl1271_debug(DEBUG_ACX, "acx ba initiator policy");
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
@@ -1482,33 +1415,18 @@ int wl1271_acx_set_ba_session(struct wl1271 *wl,
goto out;
}
- /* ANY role */
- acx->role_id = 0xff;
- acx->tid = tid_index;
- acx->enable = policy;
- acx->ba_direction = direction;
-
- switch (direction) {
- case WLAN_BACK_INITIATOR:
- acx->win_size = wl->conf.ht.tx_ba_win_size;
- acx->inactivity_timeout = wl->conf.ht.inactivity_timeout;
- break;
- case WLAN_BACK_RECIPIENT:
- acx->win_size = RX_BA_WIN_SIZE;
- acx->inactivity_timeout = 0;
- break;
- default:
- wl1271_error("Incorrect acx command id=%x\n", direction);
- ret = -EINVAL;
- goto out;
- }
+ /* set for the current role */
+ acx->role_id = wl->role_id;
+ acx->tid_bitmap = wl->conf.ht.tx_ba_tid_bitmap;
+ acx->win_size = wl->conf.ht.tx_ba_win_size;
+ acx->inactivity_timeout = wl->conf.ht.inactivity_timeout;
ret = wl1271_cmd_configure(wl,
- ACX_BA_SESSION_POLICY_CFG,
+ ACX_BA_SESSION_INIT_POLICY,
acx,
sizeof(*acx));
if (ret < 0) {
- wl1271_warning("acx ba session setting failed: %d", ret);
+ wl1271_warning("acx ba initiator policy failed: %d", ret);
goto out;
}
@@ -1518,8 +1436,8 @@ out:
}
/* setup BA session receiver setting in the FW. */
-int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
- bool enable)
+int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
+ u16 ssn, bool enable, u8 peer_hlid)
{
struct wl1271_acx_ba_receiver_setup *acx;
int ret;
@@ -1532,11 +1450,10 @@ int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
goto out;
}
- /* Single link for now */
- acx->link_id = 1;
+ acx->hlid = peer_hlid;
acx->tid = tid_index;
acx->enable = enable;
- acx->win_size = 0;
+ acx->win_size = wl->conf.ht.rx_ba_win_size;
acx->ssn = ssn;
ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx,
@@ -1577,22 +1494,71 @@ out:
return ret;
}
-int wl1271_acx_max_tx_retry(struct wl1271 *wl)
+int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable)
{
- struct wl1271_acx_max_tx_retry *acx = NULL;
+ struct wl1271_acx_ps_rx_streaming *rx_streaming;
+ u32 conf_queues, enable_queues;
+ int i, ret = 0;
+
+ wl1271_debug(DEBUG_ACX, "acx ps rx streaming");
+
+ rx_streaming = kzalloc(sizeof(*rx_streaming), GFP_KERNEL);
+ if (!rx_streaming) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ conf_queues = wl->conf.rx_streaming.queues;
+ if (enable)
+ enable_queues = conf_queues;
+ else
+ enable_queues = 0;
+
+ for (i = 0; i < 8; i++) {
+ /*
+ * Skip non-changed queues, to avoid redundant acxs.
+ * this check assumes conf.rx_streaming.queues can't
+ * be changed while rx_streaming is enabled.
+ */
+ if (!(conf_queues & BIT(i)))
+ continue;
+
+ rx_streaming->role_id = wl->role_id;
+ rx_streaming->tid = i;
+ rx_streaming->enable = enable_queues & BIT(i);
+ rx_streaming->period = wl->conf.rx_streaming.interval;
+ rx_streaming->timeout = wl->conf.rx_streaming.interval;
+
+ ret = wl1271_cmd_configure(wl, ACX_PS_RX_STREAMING,
+ rx_streaming,
+ sizeof(*rx_streaming));
+ if (ret < 0) {
+ wl1271_warning("acx ps rx streaming failed: %d", ret);
+ goto out;
+ }
+ }
+out:
+ kfree(rx_streaming);
+ return ret;
+}
+
+int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl)
+{
+ struct wl1271_acx_ap_max_tx_retry *acx = NULL;
int ret;
- wl1271_debug(DEBUG_ACX, "acx max tx retry");
+ wl1271_debug(DEBUG_ACX, "acx ap max tx retry");
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx)
return -ENOMEM;
- acx->max_tx_retry = cpu_to_le16(wl->conf.tx.ap_max_tx_retries);
+ acx->role_id = wl->role_id;
+ acx->max_tx_retry = cpu_to_le16(wl->conf.tx.max_tx_retries);
ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx));
if (ret < 0) {
- wl1271_warning("acx max tx retry failed: %d", ret);
+ wl1271_warning("acx ap max tx retry failed: %d", ret);
goto out;
}
@@ -1656,31 +1622,6 @@ out:
return ret;
}
-int wl1271_acx_set_ap_beacon_filter(struct wl1271 *wl, bool enable)
-{
- struct acx_ap_beacon_filter *acx = NULL;
- int ret;
-
- wl1271_debug(DEBUG_ACX, "acx set ap beacon filter: %d", enable);
-
- acx = kzalloc(sizeof(*acx), GFP_KERNEL);
- if (!acx)
- return -ENOMEM;
-
- acx->enable = enable ? 1 : 0;
-
- ret = wl1271_cmd_configure(wl, ACX_AP_BEACON_FILTER_OPT,
- acx, sizeof(*acx));
- if (ret < 0) {
- wl1271_warning("acx set ap beacon filter failed: %d", ret);
- goto out;
- }
-
-out:
- kfree(acx);
- return ret;
-}
-
int wl1271_acx_fm_coex(struct wl1271 *wl)
{
struct wl1271_acx_fm_coex *acx;
@@ -1720,3 +1661,85 @@ out:
kfree(acx);
return ret;
}
+
+int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl)
+{
+ struct wl12xx_acx_set_rate_mgmt_params *acx = NULL;
+ struct conf_rate_policy_settings *conf = &wl->conf.rate;
+ int ret;
+
+ wl1271_debug(DEBUG_ACX, "acx set rate mgmt params");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx)
+ return -ENOMEM;
+
+ acx->index = ACX_RATE_MGMT_ALL_PARAMS;
+ acx->rate_retry_score = cpu_to_le16(conf->rate_retry_score);
+ acx->per_add = cpu_to_le16(conf->per_add);
+ acx->per_th1 = cpu_to_le16(conf->per_th1);
+ acx->per_th2 = cpu_to_le16(conf->per_th2);
+ acx->max_per = cpu_to_le16(conf->max_per);
+ acx->inverse_curiosity_factor = conf->inverse_curiosity_factor;
+ acx->tx_fail_low_th = conf->tx_fail_low_th;
+ acx->tx_fail_high_th = conf->tx_fail_high_th;
+ acx->per_alpha_shift = conf->per_alpha_shift;
+ acx->per_add_shift = conf->per_add_shift;
+ acx->per_beta1_shift = conf->per_beta1_shift;
+ acx->per_beta2_shift = conf->per_beta2_shift;
+ acx->rate_check_up = conf->rate_check_up;
+ acx->rate_check_down = conf->rate_check_down;
+ memcpy(acx->rate_retry_policy, conf->rate_retry_policy,
+ sizeof(acx->rate_retry_policy));
+
+ ret = wl1271_cmd_configure(wl, ACX_SET_RATE_MGMT_PARAMS,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("acx set rate mgmt params failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+int wl12xx_acx_config_hangover(struct wl1271 *wl)
+{
+ struct wl12xx_acx_config_hangover *acx;
+ struct conf_hangover_settings *conf = &wl->conf.hangover;
+ int ret;
+
+ wl1271_debug(DEBUG_ACX, "acx config hangover");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->recover_time = cpu_to_le32(conf->recover_time);
+ acx->hangover_period = conf->hangover_period;
+ acx->dynamic_mode = conf->dynamic_mode;
+ acx->early_termination_mode = conf->early_termination_mode;
+ acx->max_period = conf->max_period;
+ acx->min_period = conf->min_period;
+ acx->increase_delta = conf->increase_delta;
+ acx->decrease_delta = conf->decrease_delta;
+ acx->quiet_time = conf->quiet_time;
+ acx->increase_time = conf->increase_time;
+ acx->window_size = acx->window_size;
+
+ ret = wl1271_cmd_configure(wl, ACX_CONFIG_HANGOVER, acx,
+ sizeof(*acx));
+
+ if (ret < 0) {
+ wl1271_warning("acx config hangover failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+
+}
diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h
index 9a895e3cc613..e3f93b4b3429 100644
--- a/drivers/net/wireless/wl12xx/acx.h
+++ b/drivers/net/wireless/wl12xx/acx.h
@@ -101,6 +101,17 @@ struct acx_error_counter {
__le32 seq_num_miss;
} __packed;
+enum wl12xx_role {
+ WL1271_ROLE_STA = 0,
+ WL1271_ROLE_IBSS,
+ WL1271_ROLE_AP,
+ WL1271_ROLE_DEVICE,
+ WL1271_ROLE_P2P_CL,
+ WL1271_ROLE_P2P_GO,
+
+ WL12XX_INVALID_ROLE_TYPE = 0xff
+};
+
enum wl1271_psm_mode {
/* Active mode */
WL1271_PSM_CAM = 0,
@@ -160,94 +171,6 @@ struct acx_rx_msdu_lifetime {
__le32 lifetime;
} __packed;
-/*
- * RX Config Options Table
- * Bit Definition
- * === ==========
- * 31:14 Reserved
- * 13 Copy RX Status - when set, write three receive status words
- * to top of rx'd MPDUs.
- * When cleared, do not write three status words (added rev 1.5)
- * 12 Reserved
- * 11 RX Complete upon FCS error - when set, give rx complete
- * interrupt for FCS errors, after the rx filtering, e.g. unicast
- * frames not to us with FCS error will not generate an interrupt.
- * 10 SSID Filter Enable - When set, the WiLink discards all beacon,
- * probe request, and probe response frames with an SSID that does
- * not match the SSID specified by the host in the START/JOIN
- * command.
- * When clear, the WiLink receives frames with any SSID.
- * 9 Broadcast Filter Enable - When set, the WiLink discards all
- * broadcast frames. When clear, the WiLink receives all received
- * broadcast frames.
- * 8:6 Reserved
- * 5 BSSID Filter Enable - When set, the WiLink discards any frames
- * with a BSSID that does not match the BSSID specified by the
- * host.
- * When clear, the WiLink receives frames from any BSSID.
- * 4 MAC Addr Filter - When set, the WiLink discards any frames
- * with a destination address that does not match the MAC address
- * of the adaptor.
- * When clear, the WiLink receives frames destined to any MAC
- * address.
- * 3 Promiscuous - When set, the WiLink receives all valid frames
- * (i.e., all frames that pass the FCS check).
- * When clear, only frames that pass the other filters specified
- * are received.
- * 2 FCS - When set, the WiLink includes the FCS with the received
- * frame.
- * When cleared, the FCS is discarded.
- * 1 PLCP header - When set, write all data from baseband to frame
- * buffer including PHY header.
- * 0 Reserved - Always equal to 0.
- *
- * RX Filter Options Table
- * Bit Definition
- * === ==========
- * 31:12 Reserved - Always equal to 0.
- * 11 Association - When set, the WiLink receives all association
- * related frames (association request/response, reassocation
- * request/response, and disassociation). When clear, these frames
- * are discarded.
- * 10 Auth/De auth - When set, the WiLink receives all authentication
- * and de-authentication frames. When clear, these frames are
- * discarded.
- * 9 Beacon - When set, the WiLink receives all beacon frames.
- * When clear, these frames are discarded.
- * 8 Contention Free - When set, the WiLink receives all contention
- * free frames.
- * When clear, these frames are discarded.
- * 7 Control - When set, the WiLink receives all control frames.
- * When clear, these frames are discarded.
- * 6 Data - When set, the WiLink receives all data frames.
- * When clear, these frames are discarded.
- * 5 FCS Error - When set, the WiLink receives frames that have FCS
- * errors.
- * When clear, these frames are discarded.
- * 4 Management - When set, the WiLink receives all management
- * frames.
- * When clear, these frames are discarded.
- * 3 Probe Request - When set, the WiLink receives all probe request
- * frames.
- * When clear, these frames are discarded.
- * 2 Probe Response - When set, the WiLink receives all probe
- * response frames.
- * When clear, these frames are discarded.
- * 1 RTS/CTS/ACK - When set, the WiLink receives all RTS, CTS and ACK
- * frames.
- * When clear, these frames are discarded.
- * 0 Rsvd Type/Sub Type - When set, the WiLink receives all frames
- * that have reserved frame types and sub types as defined by the
- * 802.11 specification.
- * When clear, these frames are discarded.
- */
-struct acx_rx_config {
- struct acx_header header;
-
- __le32 config_options;
- __le32 filter_options;
-} __packed;
-
struct acx_packet_detection {
struct acx_header header;
@@ -267,9 +190,10 @@ enum acx_slot_type {
struct acx_slot {
struct acx_header header;
+ u8 role_id;
u8 wone_index; /* Reserved */
u8 slot_time;
- u8 reserved[6];
+ u8 reserved[5];
} __packed;
@@ -279,29 +203,35 @@ struct acx_slot {
struct acx_dot11_grp_addr_tbl {
struct acx_header header;
+ u8 role_id;
u8 enabled;
u8 num_groups;
- u8 pad[2];
+ u8 pad[1];
u8 mac_table[ADDRESS_GROUP_MAX_LEN];
} __packed;
struct acx_rx_timeout {
struct acx_header header;
+ u8 role_id;
+ u8 reserved;
__le16 ps_poll_timeout;
__le16 upsd_timeout;
+ u8 padding[2];
} __packed;
struct acx_rts_threshold {
struct acx_header header;
+ u8 role_id;
+ u8 reserved;
__le16 threshold;
- u8 pad[2];
} __packed;
struct acx_beacon_filter_option {
struct acx_header header;
+ u8 role_id;
u8 enable;
/*
* The number of beacons without the unicast TIM
@@ -311,7 +241,7 @@ struct acx_beacon_filter_option {
* without the unicast TIM bit set are dropped.
*/
u8 max_num_beacons;
- u8 pad[2];
+ u8 pad[1];
} __packed;
/*
@@ -350,14 +280,17 @@ struct acx_beacon_filter_option {
struct acx_beacon_filter_ie_table {
struct acx_header header;
+ u8 role_id;
u8 num_ie;
- u8 pad[3];
+ u8 pad[2];
u8 table[BEACON_FILTER_TABLE_MAX_SIZE];
} __packed;
struct acx_conn_monit_params {
struct acx_header header;
+ u8 role_id;
+ u8 padding[3];
__le32 synch_fail_thold; /* number of beacons missed */
__le32 bss_lose_timeout; /* number of TU's from synch fail */
} __packed;
@@ -369,23 +302,14 @@ struct acx_bt_wlan_coex {
u8 pad[3];
} __packed;
-struct acx_sta_bt_wlan_coex_param {
- struct acx_header header;
-
- __le32 params[CONF_SG_STA_PARAMS_MAX];
- u8 param_idx;
- u8 padding[3];
-} __packed;
-
-struct acx_ap_bt_wlan_coex_param {
+struct acx_bt_wlan_coex_param {
struct acx_header header;
- __le32 params[CONF_SG_AP_PARAMS_MAX];
+ __le32 params[CONF_SG_PARAMS_MAX];
u8 param_idx;
u8 padding[3];
} __packed;
-
struct acx_dco_itrim_params {
struct acx_header header;
@@ -406,15 +330,16 @@ struct acx_energy_detection {
struct acx_beacon_broadcast {
struct acx_header header;
- __le16 beacon_rx_timeout;
- __le16 broadcast_timeout;
-
+ u8 role_id;
/* Enables receiving of broadcast packets in PS mode */
u8 rx_broadcast_in_ps;
+ __le16 beacon_rx_timeout;
+ __le16 broadcast_timeout;
+
/* Consecutive PS Poll failures before updating the host */
u8 ps_poll_threshold;
- u8 pad[2];
+ u8 pad[1];
} __packed;
struct acx_event_mask {
@@ -424,35 +349,6 @@ struct acx_event_mask {
__le32 high_event_mask; /* Unused */
} __packed;
-#define CFG_RX_FCS BIT(2)
-#define CFG_RX_ALL_GOOD BIT(3)
-#define CFG_UNI_FILTER_EN BIT(4)
-#define CFG_BSSID_FILTER_EN BIT(5)
-#define CFG_MC_FILTER_EN BIT(6)
-#define CFG_MC_ADDR0_EN BIT(7)
-#define CFG_MC_ADDR1_EN BIT(8)
-#define CFG_BC_REJECT_EN BIT(9)
-#define CFG_SSID_FILTER_EN BIT(10)
-#define CFG_RX_INT_FCS_ERROR BIT(11)
-#define CFG_RX_INT_ENCRYPTED BIT(12)
-#define CFG_RX_WR_RX_STATUS BIT(13)
-#define CFG_RX_FILTER_NULTI BIT(14)
-#define CFG_RX_RESERVE BIT(15)
-#define CFG_RX_TIMESTAMP_TSF BIT(16)
-
-#define CFG_RX_RSV_EN BIT(0)
-#define CFG_RX_RCTS_ACK BIT(1)
-#define CFG_RX_PRSP_EN BIT(2)
-#define CFG_RX_PREQ_EN BIT(3)
-#define CFG_RX_MGMT_EN BIT(4)
-#define CFG_RX_FCS_ERROR BIT(5)
-#define CFG_RX_DATA_EN BIT(6)
-#define CFG_RX_CTL_EN BIT(7)
-#define CFG_RX_CF_EN BIT(8)
-#define CFG_RX_BCN_EN BIT(9)
-#define CFG_RX_AUTH_EN BIT(10)
-#define CFG_RX_ASSOC_EN BIT(11)
-
#define SCAN_PASSIVE BIT(0)
#define SCAN_5GHZ_BAND BIT(1)
#define SCAN_TRIGGERED BIT(2)
@@ -465,6 +361,8 @@ struct acx_event_mask {
struct acx_feature_config {
struct acx_header header;
+ u8 role_id;
+ u8 padding[3];
__le32 options;
__le32 data_flow_options;
} __packed;
@@ -472,16 +370,18 @@ struct acx_feature_config {
struct acx_current_tx_power {
struct acx_header header;
+ u8 role_id;
u8 current_tx_power;
- u8 padding[3];
+ u8 padding[2];
} __packed;
struct acx_wake_up_condition {
struct acx_header header;
+ u8 role_id;
u8 wake_up_event; /* Only one bit can be set */
u8 listen_interval;
- u8 pad[2];
+ u8 pad[1];
} __packed;
struct acx_aid {
@@ -490,8 +390,9 @@ struct acx_aid {
/*
* To be set when associated with an AP.
*/
+ u8 role_id;
+ u8 reserved;
__le16 aid;
- u8 pad[2];
} __packed;
enum acx_preamble_type {
@@ -506,8 +407,9 @@ struct acx_preamble {
* When set, the WiLink transmits the frames with a short preamble and
* when cleared, the WiLink transmits the frames with a long preamble.
*/
+ u8 role_id;
u8 preamble;
- u8 padding[3];
+ u8 padding[2];
} __packed;
enum acx_ctsprotect_type {
@@ -517,8 +419,9 @@ enum acx_ctsprotect_type {
struct acx_ctsprotect {
struct acx_header header;
+ u8 role_id;
u8 ctsprotect;
- u8 padding[3];
+ u8 padding[2];
} __packed;
struct acx_tx_statistics {
@@ -753,18 +656,10 @@ struct acx_rate_class {
#define ACX_TX_BASIC_RATE 0
#define ACX_TX_AP_FULL_RATE 1
-#define ACX_TX_RATE_POLICY_CNT 2
-struct acx_sta_rate_policy {
- struct acx_header header;
-
- __le32 rate_class_cnt;
- struct acx_rate_class rate_class[CONF_TX_MAX_RATE_CLASSES];
-} __packed;
-
-
+#define ACX_TX_BASIC_RATE_P2P 2
#define ACX_TX_AP_MODE_MGMT_RATE 4
#define ACX_TX_AP_MODE_BCST_RATE 5
-struct acx_ap_rate_policy {
+struct acx_rate_policy {
struct acx_header header;
__le32 rate_policy_idx;
@@ -773,22 +668,23 @@ struct acx_ap_rate_policy {
struct acx_ac_cfg {
struct acx_header header;
+ u8 role_id;
u8 ac;
+ u8 aifsn;
u8 cw_min;
__le16 cw_max;
- u8 aifsn;
- u8 reserved;
__le16 tx_op_limit;
} __packed;
struct acx_tid_config {
struct acx_header header;
+ u8 role_id;
u8 queue_id;
u8 channel_type;
u8 tsid;
u8 ps_scheme;
u8 ack_policy;
- u8 padding[3];
+ u8 padding[2];
__le32 apsd_conf[2];
} __packed;
@@ -804,19 +700,7 @@ struct acx_tx_config_options {
__le16 tx_compl_threshold; /* number of packets */
} __packed;
-#define ACX_TX_DESCRIPTORS 32
-
-struct wl1271_acx_ap_config_memory {
- struct acx_header header;
-
- u8 rx_mem_block_num;
- u8 tx_min_mem_block_num;
- u8 num_stations;
- u8 num_ssid_profiles;
- __le32 total_tx_descriptors;
-} __packed;
-
-struct wl1271_acx_sta_config_memory {
+struct wl12xx_acx_config_memory {
struct acx_header header;
u8 rx_mem_block_num;
@@ -828,6 +712,8 @@ struct wl1271_acx_sta_config_memory {
u8 tx_free_req;
u8 rx_free_req;
u8 tx_min;
+ u8 fwlog_blocks;
+ u8 padding[3];
} __packed;
struct wl1271_acx_mem_map {
@@ -888,9 +774,10 @@ struct wl1271_acx_rx_config_opt {
struct wl1271_acx_bet_enable {
struct acx_header header;
+ u8 role_id;
u8 enable;
u8 max_consecutive;
- u8 padding[2];
+ u8 padding[1];
} __packed;
#define ACX_IPV4_VERSION 4
@@ -903,9 +790,10 @@ struct wl1271_acx_bet_enable {
struct wl1271_acx_arp_filter {
struct acx_header header;
+ u8 role_id;
u8 version; /* ACX_IPV4_VERSION, ACX_IPV6_VERSION */
u8 enable; /* bitmap of enabled ARP filtering features */
- u8 padding[2];
+ u8 padding[1];
u8 address[16]; /* The configured device IP address - all ARP
requests directed to this IP address will pass
through. For IPv4, the first four bytes are
@@ -923,8 +811,9 @@ struct wl1271_acx_pm_config {
struct wl1271_acx_keep_alive_mode {
struct acx_header header;
+ u8 role_id;
u8 enabled;
- u8 padding[3];
+ u8 padding[2];
} __packed;
enum {
@@ -940,11 +829,11 @@ enum {
struct wl1271_acx_keep_alive_config {
struct acx_header header;
- __le32 period;
+ u8 role_id;
u8 index;
u8 tpl_validation;
u8 trigger;
- u8 padding;
+ __le32 period;
} __packed;
#define HOST_IF_CFG_RX_FIFO_ENABLE BIT(0)
@@ -988,26 +877,33 @@ enum {
struct wl1271_acx_rssi_snr_trigger {
struct acx_header header;
- __le16 threshold;
- __le16 pacing; /* 0 - 60000 ms */
+ u8 role_id;
u8 metric;
u8 type;
u8 dir;
+ __le16 threshold;
+ __le16 pacing; /* 0 - 60000 ms */
u8 hysteresis;
u8 index;
u8 enable;
- u8 padding[2];
+ u8 padding[1];
};
struct wl1271_acx_rssi_snr_avg_weights {
struct acx_header header;
+ u8 role_id;
+ u8 padding[3];
u8 rssi_beacon;
u8 rssi_data;
u8 snr_beacon;
u8 snr_data;
};
+
+/* special capability bit (not employed by the 802.11n spec) */
+#define WL12XX_HT_CAP_HT_OPERATION BIT(16)
+
/*
* ACX_PEER_HT_CAP
* Configure HT capabilities - declare the capabilities of the peer
@@ -1016,28 +912,11 @@ struct wl1271_acx_rssi_snr_avg_weights {
struct wl1271_acx_ht_capabilities {
struct acx_header header;
- /*
- * bit 0 - Allow HT Operation
- * bit 1 - Allow Greenfield format in TX
- * bit 2 - Allow Short GI in TX
- * bit 3 - Allow L-SIG TXOP Protection in TX
- * bit 4 - Allow HT Control fields in TX.
- * Note, driver will still leave space for HT control in packets
- * regardless of the value of this field. FW will be responsible
- * to drop the HT field from any frame when this Bit set to 0.
- * bit 5 - Allow RD initiation in TXOP. FW is allowed to initate RD.
- * Exact policy setting for this feature is TBD.
- * Note, this bit can only be set to 1 if bit 3 is set to 1.
- */
+ /* bitmask of capability bits supported by the peer */
__le32 ht_capabilites;
- /*
- * Indicates to which peer these capabilities apply.
- * For infrastructure use ff:ff:ff:ff:ff:ff that indicates relevance
- * for all peers.
- * Only valid for IBSS/DLS operation.
- */
- u8 mac_address[ETH_ALEN];
+ /* Indicates to which link these capabilities apply. */
+ u8 hlid;
/*
* This the maximum A-MPDU length supported by the AP. The FW may not
@@ -1047,16 +926,9 @@ struct wl1271_acx_ht_capabilities {
/* This is the minimal spacing required when sending A-MPDUs to the AP*/
u8 ampdu_min_spacing;
-} __packed;
-
-/* HT Capabilites Fw Bit Mask Mapping */
-#define WL1271_ACX_FW_CAP_HT_OPERATION BIT(0)
-#define WL1271_ACX_FW_CAP_GREENFIELD_FRAME_FORMAT BIT(1)
-#define WL1271_ACX_FW_CAP_SHORT_GI_FOR_20MHZ_PACKETS BIT(2)
-#define WL1271_ACX_FW_CAP_LSIG_TXOP_PROTECTION BIT(3)
-#define WL1271_ACX_FW_CAP_HT_CONTROL_FIELDS BIT(4)
-#define WL1271_ACX_FW_CAP_RD_INITIATION BIT(5)
+ u8 padding;
+} __packed;
/*
* ACX_HT_BSS_OPERATION
@@ -1065,6 +937,8 @@ struct wl1271_acx_ht_capabilities {
struct wl1271_acx_ht_information {
struct acx_header header;
+ u8 role_id;
+
/* Values: 0 - RIFS not allowed, 1 - RIFS allowed */
u8 rifs_mode;
@@ -1086,60 +960,51 @@ struct wl1271_acx_ht_information {
*/
u8 dual_cts_protection;
- u8 padding[3];
+ u8 padding[2];
} __packed;
-#define RX_BA_WIN_SIZE 8
+#define RX_BA_MAX_SESSIONS 2
-struct wl1271_acx_ba_session_policy {
+struct wl1271_acx_ba_initiator_policy {
struct acx_header header;
- /*
- * Specifies role Id, Range 0-7, 0xFF means ANY role.
- * Future use. For now this field is irrelevant
- */
+
+ /* Specifies role Id, Range 0-7, 0xFF means ANY role. */
u8 role_id;
+
/*
- * Specifies Link Id, Range 0-31, 0xFF means ANY Link Id.
- * Not applicable if Role Id is set to ANY.
+ * Per TID setting for allowing TX BA. Set a bit to 1 to allow
+ * TX BA sessions for the corresponding TID.
*/
- u8 link_id;
-
- u8 tid;
-
- u8 enable;
+ u8 tid_bitmap;
/* Windows size in number of packets */
- u16 win_size;
+ u8 win_size;
- /*
- * As initiator inactivity timeout in time units(TU) of 1024us.
- * As receiver reserved
- */
- u16 inactivity_timeout;
+ u8 padding1[1];
- /* Initiator = 1/Receiver = 0 */
- u8 ba_direction;
+ /* As initiator inactivity timeout in time units(TU) of 1024us */
+ u16 inactivity_timeout;
- u8 padding[3];
+ u8 padding[2];
} __packed;
struct wl1271_acx_ba_receiver_setup {
struct acx_header header;
- /* Specifies Link Id, Range 0-31, 0xFF means ANY Link Id */
- u8 link_id;
+ /* Specifies link id, range 0-31 */
+ u8 hlid;
u8 tid;
u8 enable;
- u8 padding[1];
-
/* Windows size in number of packets */
- u16 win_size;
+ u8 win_size;
/* BA session starting sequence number. RANGE 0-FFF */
u16 ssn;
+
+ u8 padding[2];
} __packed;
struct wl1271_acx_fw_tsf_information {
@@ -1153,15 +1018,32 @@ struct wl1271_acx_fw_tsf_information {
u8 padding[3];
} __packed;
-struct wl1271_acx_max_tx_retry {
+struct wl1271_acx_ps_rx_streaming {
struct acx_header header;
+ u8 role_id;
+ u8 tid;
+ u8 enable;
+
+ /* interval between triggers (10-100 msec) */
+ u8 period;
+
+ /* timeout before first trigger (0-200 msec) */
+ u8 timeout;
+ u8 padding[3];
+} __packed;
+
+struct wl1271_acx_ap_max_tx_retry {
+ struct acx_header header;
+
+ u8 role_id;
+ u8 padding_1;
+
/*
* the number of frames transmission failures before
* issuing the aging event.
*/
__le16 max_tx_retry;
- u8 padding_1[2];
} __packed;
struct wl1271_acx_config_ps {
@@ -1180,13 +1062,6 @@ struct wl1271_acx_inconnection_sta {
u8 padding1[2];
} __packed;
-struct acx_ap_beacon_filter {
- struct acx_header header;
-
- u8 enable;
- u8 pad[3];
-} __packed;
-
/*
* ACX_FM_COEX_CFG
* set the FM co-existence parameters.
@@ -1246,6 +1121,47 @@ struct wl1271_acx_fm_coex {
u8 swallow_clk_diff;
} __packed;
+#define ACX_RATE_MGMT_ALL_PARAMS 0xff
+struct wl12xx_acx_set_rate_mgmt_params {
+ struct acx_header header;
+
+ u8 index; /* 0xff to configure all params */
+ u8 padding1;
+ __le16 rate_retry_score;
+ __le16 per_add;
+ __le16 per_th1;
+ __le16 per_th2;
+ __le16 max_per;
+ u8 inverse_curiosity_factor;
+ u8 tx_fail_low_th;
+ u8 tx_fail_high_th;
+ u8 per_alpha_shift;
+ u8 per_add_shift;
+ u8 per_beta1_shift;
+ u8 per_beta2_shift;
+ u8 rate_check_up;
+ u8 rate_check_down;
+ u8 rate_retry_policy[ACX_RATE_MGMT_NUM_OF_RATES];
+ u8 padding2[2];
+} __packed;
+
+struct wl12xx_acx_config_hangover {
+ struct acx_header header;
+
+ __le32 recover_time;
+ u8 hangover_period;
+ u8 dynamic_mode;
+ u8 early_termination_mode;
+ u8 max_period;
+ u8 min_period;
+ u8 increase_delta;
+ u8 decrease_delta;
+ u8 quiet_time;
+ u8 increase_time;
+ u8 window_size;
+ u8 padding[2];
+} __packed;
+
enum {
ACX_WAKE_UP_CONDITIONS = 0x0002,
ACX_MEM_CFG = 0x0003,
@@ -1253,10 +1169,7 @@ enum {
ACX_AC_CFG = 0x0007,
ACX_MEM_MAP = 0x0008,
ACX_AID = 0x000A,
- /* ACX_FW_REV is missing in the ref driver, but seems to work */
- ACX_FW_REV = 0x000D,
ACX_MEDIUM_USAGE = 0x000F,
- ACX_RX_CFG = 0x0010,
ACX_TX_QUEUE_CFG = 0x0011, /* FIXME: only used by wl1251 */
ACX_STATISTICS = 0x0013, /* Debug API */
ACX_PWR_CONSUMPTION_STATISTICS = 0x0014,
@@ -1264,7 +1177,6 @@ enum {
ACX_TID_CFG = 0x001A,
ACX_PS_RX_STREAMING = 0x001B,
ACX_BEACON_FILTER_OPT = 0x001F,
- ACX_AP_BEACON_FILTER_OPT = 0x0020,
ACX_NOISE_HIST = 0x0021,
ACX_HDK_VERSION = 0x0022, /* ??? */
ACX_PD_THRESHOLD = 0x0023,
@@ -1272,7 +1184,6 @@ enum {
ACX_CCA_THRESHOLD = 0x0025,
ACX_EVENT_MBOX_MASK = 0x0026,
ACX_CONN_MONIT_PARAMS = 0x002D,
- ACX_CONS_TX_FAILURE = 0x002F,
ACX_BCN_DTIM_OPTIONS = 0x0031,
ACX_SG_ENABLE = 0x0032,
ACX_SG_CFG = 0x0033,
@@ -1299,11 +1210,14 @@ enum {
ACX_RSSI_SNR_WEIGHTS = 0x0052,
ACX_KEEP_ALIVE_MODE = 0x0053,
ACX_SET_KEEP_ALIVE_CONFIG = 0x0054,
- ACX_BA_SESSION_POLICY_CFG = 0x0055,
+ ACX_BA_SESSION_INIT_POLICY = 0x0055,
ACX_BA_SESSION_RX_SETUP = 0x0056,
ACX_PEER_HT_CAP = 0x0057,
ACX_HT_BSS_OPERATION = 0x0058,
ACX_COEX_ACTIVITY = 0x0059,
+ ACX_BURST_MODE = 0x005C,
+ ACX_SET_RATE_MGMT_PARAMS = 0x005D,
+ ACX_SET_RATE_ADAPT_PARAMS = 0x0060,
ACX_SET_DCO_ITRIM_PARAMS = 0x0061,
ACX_GEN_FW_CMD = 0x0070,
ACX_HOST_IF_CFG_BITMAP = 0x0071,
@@ -1327,7 +1241,6 @@ int wl1271_acx_feature_cfg(struct wl1271 *wl);
int wl1271_acx_mem_map(struct wl1271 *wl,
struct acx_header *mem_map, size_t len);
int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl);
-int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter);
int wl1271_acx_pd_threshold(struct wl1271 *wl);
int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time);
int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
@@ -1339,8 +1252,7 @@ int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter);
int wl1271_acx_beacon_filter_table(struct wl1271 *wl);
int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable);
int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable);
-int wl1271_acx_sta_sg_cfg(struct wl1271 *wl);
-int wl1271_acx_ap_sg_cfg(struct wl1271 *wl);
+int wl12xx_acx_sg_cfg(struct wl1271 *wl);
int wl1271_acx_cca_threshold(struct wl1271 *wl);
int wl1271_acx_bcn_dtim_options(struct wl1271 *wl);
int wl1271_acx_aid(struct wl1271 *wl, u16 aid);
@@ -1359,8 +1271,7 @@ int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
u32 apsd_conf0, u32 apsd_conf1);
int wl1271_acx_frag_threshold(struct wl1271 *wl, u32 frag_threshold);
int wl1271_acx_tx_config_options(struct wl1271 *wl);
-int wl1271_acx_ap_mem_cfg(struct wl1271 *wl);
-int wl1271_acx_sta_mem_cfg(struct wl1271 *wl);
+int wl12xx_acx_mem_cfg(struct wl1271 *wl);
int wl1271_acx_init_mem_config(struct wl1271 *wl);
int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap);
int wl1271_acx_init_rx_interrupt(struct wl1271 *wl);
@@ -1375,19 +1286,19 @@ int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl);
int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap,
- bool allow_ht_operation);
+ bool allow_ht_operation, u8 hlid);
int wl1271_acx_set_ht_information(struct wl1271 *wl,
u16 ht_operation_mode);
-int wl1271_acx_set_ba_session(struct wl1271 *wl,
- enum ieee80211_back_parties direction,
- u8 tid_index, u8 policy);
-int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
- bool enable);
+int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl);
+int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
+ u16 ssn, bool enable, u8 peer_hlid);
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
-int wl1271_acx_max_tx_retry(struct wl1271 *wl);
+int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable);
+int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl);
int wl1271_acx_config_ps(struct wl1271 *wl);
int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
-int wl1271_acx_set_ap_beacon_filter(struct wl1271 *wl, bool enable);
int wl1271_acx_fm_coex(struct wl1271 *wl);
+int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);
+int wl12xx_acx_config_hangover(struct wl1271 *wl);
#endif /* __WL1271_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c
index b07f8b7e5f11..681337914976 100644
--- a/drivers/net/wireless/wl12xx/boot.c
+++ b/drivers/net/wireless/wl12xx/boot.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/wl12xx.h>
+#include <linux/export.h>
#include "acx.h"
#include "reg.h"
@@ -102,6 +103,23 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl);
}
+static unsigned int wl12xx_get_fw_ver_quirks(struct wl1271 *wl)
+{
+ unsigned int quirks = 0;
+ unsigned int *fw_ver = wl->chip.fw_ver;
+
+ /* Only new station firmwares support routing fw logs to the host */
+ if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
+ (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN))
+ quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED;
+
+ /* This feature is not yet supported for AP mode */
+ if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP)
+ quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED;
+
+ return quirks;
+}
+
static void wl1271_parse_fw_ver(struct wl1271 *wl)
{
int ret;
@@ -116,6 +134,9 @@ static void wl1271_parse_fw_ver(struct wl1271 *wl)
memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
return;
}
+
+ /* Check if any quirks are needed with older fw versions */
+ wl->quirks |= wl12xx_get_fw_ver_quirks(wl);
}
static void wl1271_boot_fw_version(struct wl1271 *wl)
@@ -274,9 +295,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
*/
if (wl->nvs_len == sizeof(struct wl1271_nvs_file) ||
wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) {
- /* for now 11a is unsupported in AP mode */
- if (wl->bss_type != BSS_TYPE_AP_BSS &&
- nvs->general_params.dual_mode_select)
+ if (nvs->general_params.dual_mode_select)
wl->enable_11a = true;
}
@@ -474,18 +493,19 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
wl->event_mask = BSS_LOSE_EVENT_ID |
SCAN_COMPLETE_EVENT_ID |
PS_REPORT_EVENT_ID |
- JOIN_EVENT_COMPLETE_ID |
DISCONNECT_EVENT_COMPLETE_ID |
RSSI_SNR_TRIGGER_0_EVENT_ID |
PSPOLL_DELIVERY_FAILURE_EVENT_ID |
SOFT_GEMINI_SENSE_EVENT_ID |
PERIODIC_SCAN_REPORT_EVENT_ID |
- PERIODIC_SCAN_COMPLETE_EVENT_ID;
-
- if (wl->bss_type == BSS_TYPE_AP_BSS)
- wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID;
- else
- wl->event_mask |= DUMMY_PACKET_EVENT_ID;
+ PERIODIC_SCAN_COMPLETE_EVENT_ID |
+ DUMMY_PACKET_EVENT_ID |
+ PEER_REMOVE_COMPLETE_EVENT_ID |
+ BA_SESSION_RX_CONSTRAINT_EVENT_ID |
+ REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
+ INACTIVE_STA_EVENT_ID |
+ MAX_TX_RETRY_EVENT_ID |
+ CHANNEL_SWITCH_COMPLETE_EVENT_ID;
ret = wl1271_event_unmask(wl);
if (ret < 0) {
@@ -516,13 +536,13 @@ static void wl1271_boot_hw_version(struct wl1271 *wl)
{
u32 fuse;
- fuse = wl1271_top_reg_read(wl, REG_FUSE_DATA_2_1);
+ if (wl->chip.id == CHIP_ID_1283_PG20)
+ fuse = wl1271_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1);
+ else
+ fuse = wl1271_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1);
fuse = (fuse & PG_VER_MASK) >> PG_VER_OFFSET;
wl->hw_pg_ver = (s8)fuse;
-
- if (((wl->hw_pg_ver & PG_MAJOR_VER_MASK) >> PG_MAJOR_VER_OFFSET) < 3)
- wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION;
}
static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl)
@@ -663,7 +683,8 @@ static int wl127x_boot_clk(struct wl1271 *wl)
u32 pause;
u32 clk;
- wl1271_boot_hw_version(wl);
+ if (((wl->hw_pg_ver & PG_MAJOR_VER_MASK) >> PG_MAJOR_VER_OFFSET) < 3)
+ wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION;
if (wl->ref_clock == CONF_REF_CLK_19_2_E ||
wl->ref_clock == CONF_REF_CLK_38_4_E ||
@@ -717,6 +738,8 @@ int wl1271_load_firmware(struct wl1271 *wl)
u32 tmp, clk;
int selected_clock = -1;
+ wl1271_boot_hw_version(wl);
+
if (wl->chip.id == CHIP_ID_1283_PG20) {
ret = wl128x_boot_clk(wl, &selected_clock);
if (ret < 0)
@@ -816,9 +839,6 @@ int wl1271_boot(struct wl1271 *wl)
/* Enable firmware interrupts now */
wl1271_boot_enable_interrupts(wl);
- /* set the wl1271 default filters */
- wl1271_set_default_filters(wl);
-
wl1271_event_mbox_config(wl);
out:
diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h
index e8f8255bbabe..06dad9380fa7 100644
--- a/drivers/net/wireless/wl12xx/boot.h
+++ b/drivers/net/wireless/wl12xx/boot.h
@@ -55,7 +55,8 @@ struct wl1271_static_data {
#define OCP_REG_CLK_POLARITY 0x0cb2
#define OCP_REG_CLK_PULL 0x0cb4
-#define REG_FUSE_DATA_2_1 0x050a
+#define WL127X_REG_FUSE_DATA_2_1 0x050a
+#define WL128X_REG_FUSE_DATA_2_1 0x2152
#define PG_VER_MASK 0x3c
#define PG_VER_OFFSET 2
diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c
index 42935ac72663..a52299e548fa 100644
--- a/